diff --git a/.cirrus.yml b/.cirrus.yml new file mode 100644 index 0000000000..47ef5bc604 --- /dev/null +++ b/.cirrus.yml @@ -0,0 +1,27 @@ +env: + CIRRUS_CLONE_DEPTH: 1 + +freebsd_12_task: + freebsd_instance: + image: freebsd-12-0-release-amd64 + cpu: 8 + memory: 8G + install_script: pkg install -y + bison curl cyrus-sasl git glib gmake gnutls + nettle perl5 pixman pkgconf png usbredir + script: + - mkdir build + - cd build + - ../configure || { cat config.log; exit 1; } + - gmake -j8 + - gmake -j8 V=1 check + +macos_task: + osx_instance: + image: mojave-base + install_script: + - brew install pkg-config python glib pixman make sdl2 + script: + - ./configure --python=/usr/local/bin/python3 || { cat config.log; exit 1; } + - gmake -j$(sysctl -n hw.ncpu) + - gmake check -j$(sysctl -n hw.ncpu) diff --git a/.editorconfig b/.editorconfig index b2022e391a..1582883393 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,10 @@ -# http://editorconfig.org +# EditorConfig is a file format and collection of text editor plugins +# for maintaining consistent coding styles between different editors +# and IDEs. Most popular editors support this either natively or via +# plugin. +# +# Check https://editorconfig.org for details. + root = true [*] @@ -6,10 +12,23 @@ end_of_line = lf insert_final_newline = true charset = utf-8 +[*.mak] +indent_style = tab +indent_size = 8 +file_type_emacs = makefile + [Makefile*] indent_style = tab indent_size = 8 +file_type_emacs = makefile [*.{c,h}] indent_style = space indent_size = 4 + +[*.{vert,frag}] +file_type_emacs = glsl + +[*.json] +indent_style = space +file_type_emacs = python diff --git a/.gitignore b/.gitignore index 5668d02782..8f78221804 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +/.doctrees /config-devices.* /config-all-devices.* /config-all-disas.* @@ -5,6 +6,7 @@ /config-target.* /config.status /config-temp +/elf2dmp /trace-events-all /trace/generated-events.h /trace/generated-events.c @@ -30,83 +32,21 @@ /qapi-gen-timestamp /qapi/qapi-builtin-types.[ch] /qapi/qapi-builtin-visit.[ch] -/qapi/qapi-commands-block-core.[ch] -/qapi/qapi-commands-block.[ch] -/qapi/qapi-commands-char.[ch] -/qapi/qapi-commands-common.[ch] -/qapi/qapi-commands-crypto.[ch] -/qapi/qapi-commands-introspect.[ch] -/qapi/qapi-commands-job.[ch] -/qapi/qapi-commands-migration.[ch] -/qapi/qapi-commands-misc.[ch] -/qapi/qapi-commands-net.[ch] -/qapi/qapi-commands-rocker.[ch] -/qapi/qapi-commands-run-state.[ch] -/qapi/qapi-commands-sockets.[ch] -/qapi/qapi-commands-tpm.[ch] -/qapi/qapi-commands-trace.[ch] -/qapi/qapi-commands-transaction.[ch] -/qapi/qapi-commands-ui.[ch] +/qapi/qapi-commands-*.[ch] /qapi/qapi-commands.[ch] -/qapi/qapi-events-block-core.[ch] -/qapi/qapi-events-block.[ch] -/qapi/qapi-events-char.[ch] -/qapi/qapi-events-common.[ch] -/qapi/qapi-events-crypto.[ch] -/qapi/qapi-events-introspect.[ch] -/qapi/qapi-events-job.[ch] -/qapi/qapi-events-migration.[ch] -/qapi/qapi-events-misc.[ch] -/qapi/qapi-events-net.[ch] -/qapi/qapi-events-rocker.[ch] -/qapi/qapi-events-run-state.[ch] -/qapi/qapi-events-sockets.[ch] -/qapi/qapi-events-tpm.[ch] -/qapi/qapi-events-trace.[ch] -/qapi/qapi-events-transaction.[ch] -/qapi/qapi-events-ui.[ch] +/qapi/qapi-emit-events.[ch] +/qapi/qapi-events-*.[ch] /qapi/qapi-events.[ch] /qapi/qapi-introspect.[ch] -/qapi/qapi-types-block-core.[ch] -/qapi/qapi-types-block.[ch] -/qapi/qapi-types-char.[ch] -/qapi/qapi-types-common.[ch] -/qapi/qapi-types-crypto.[ch] -/qapi/qapi-types-introspect.[ch] -/qapi/qapi-types-job.[ch] -/qapi/qapi-types-migration.[ch] -/qapi/qapi-types-misc.[ch] -/qapi/qapi-types-net.[ch] -/qapi/qapi-types-rocker.[ch] -/qapi/qapi-types-run-state.[ch] -/qapi/qapi-types-sockets.[ch] -/qapi/qapi-types-tpm.[ch] -/qapi/qapi-types-trace.[ch] -/qapi/qapi-types-transaction.[ch] -/qapi/qapi-types-ui.[ch] +/qapi/qapi-types-*.[ch] /qapi/qapi-types.[ch] -/qapi/qapi-visit-block-core.[ch] -/qapi/qapi-visit-block.[ch] -/qapi/qapi-visit-char.[ch] -/qapi/qapi-visit-common.[ch] -/qapi/qapi-visit-crypto.[ch] -/qapi/qapi-visit-introspect.[ch] -/qapi/qapi-visit-job.[ch] -/qapi/qapi-visit-migration.[ch] -/qapi/qapi-visit-misc.[ch] -/qapi/qapi-visit-net.[ch] -/qapi/qapi-visit-rocker.[ch] -/qapi/qapi-visit-run-state.[ch] -/qapi/qapi-visit-sockets.[ch] -/qapi/qapi-visit-tpm.[ch] -/qapi/qapi-visit-trace.[ch] -/qapi/qapi-visit-transaction.[ch] -/qapi/qapi-visit-ui.[ch] +/qapi/qapi-visit-*.[ch] /qapi/qapi-visit.[ch] /qapi/qapi-doc.texi /qemu-doc.html /qemu-doc.info /qemu-doc.txt +/qemu-edid /qemu-img /qemu-nbd /qemu-options.def @@ -166,6 +106,10 @@ /pc-bios/optionrom/linuxboot_dma.bin /pc-bios/optionrom/linuxboot_dma.raw /pc-bios/optionrom/linuxboot_dma.img +/pc-bios/optionrom/pvh.asm +/pc-bios/optionrom/pvh.bin +/pc-bios/optionrom/pvh.raw +/pc-bios/optionrom/pvh.img /pc-bios/optionrom/multiboot.asm /pc-bios/optionrom/multiboot.bin /pc-bios/optionrom/multiboot.raw @@ -176,6 +120,7 @@ /pc-bios/optionrom/kvmvapic.img /pc-bios/s390-ccw/s390-ccw.elf /pc-bios/s390-ccw/s390-ccw.img +/docs/built /docs/interop/qemu-ga-qapi.texi /docs/interop/qemu-ga-ref.html /docs/interop/qemu-ga-ref.info* diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000..79d02cf740 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,73 @@ +before_script: + - apt-get update -qq + - apt-get install -y -qq flex bison libglib2.0-dev libpixman-1-dev genisoimage + +build-system1: + script: + - apt-get install -y -qq libgtk-3-dev libvte-dev nettle-dev libcacard-dev + libusb-dev libvde-dev libspice-protocol-dev libgl1-mesa-dev + - ./configure --enable-werror --target-list="aarch64-softmmu alpha-softmmu + cris-softmmu hppa-softmmu lm32-softmmu moxie-softmmu microblazeel-softmmu + mips64el-softmmu m68k-softmmu ppc-softmmu riscv64-softmmu sparc-softmmu" + - make -j2 + - make -j2 check + +build-system2: + script: + - apt-get install -y -qq libsdl2-dev libgcrypt-dev libbrlapi-dev libaio-dev + libfdt-dev liblzo2-dev librdmacm-dev libibverbs-dev libibumad-dev + - ./configure --enable-werror --target-list="tricore-softmmu unicore32-softmmu + microblaze-softmmu mips-softmmu riscv32-softmmu s390x-softmmu sh4-softmmu + sparc64-softmmu x86_64-softmmu xtensa-softmmu nios2-softmmu or1k-softmmu" + - make -j2 + - make -j2 check + +build-disabled: + script: + - ./configure --enable-werror --disable-rdma --disable-slirp --disable-curl + --disable-capstone --disable-live-block-migration --disable-glusterfs + --disable-replication --disable-coroutine-pool --disable-smartcard + --disable-guest-agent --disable-curses --disable-libxml2 --disable-tpm + --disable-qom-cast-debug --disable-spice --disable-vhost-vsock + --disable-vhost-net --disable-vhost-crypto --disable-vhost-user + --target-list="i386-softmmu ppc64-softmmu mips64-softmmu i386-linux-user" + - make -j2 + - make -j2 check-qtest SPEED=slow + +build-tcg-disabled: + script: + - apt-get install -y -qq clang libgtk-3-dev libbluetooth-dev libusb-dev + - ./configure --cc=clang --enable-werror --disable-tcg --audio-drv-list="" + - make -j2 + - make check-unit + - make check-qapi-schema + - cd tests/qemu-iotests/ + - ./check -raw 001 002 003 004 005 008 009 010 011 012 021 025 032 033 048 + 052 063 077 086 101 104 106 113 147 148 150 151 152 157 159 160 + 163 170 171 183 184 192 194 197 205 208 215 221 222 226 227 236 + - ./check -qcow2 001 002 003 004 005 007 008 009 010 011 012 013 017 018 019 + 020 021 022 024 025 027 028 029 031 032 033 034 035 036 037 038 + 039 040 042 043 046 047 048 049 050 051 052 053 054 056 057 058 + 060 061 062 063 065 066 067 068 069 071 072 073 074 079 080 082 + 085 086 089 090 091 095 096 097 098 099 102 103 104 105 107 108 + 110 111 114 117 120 122 124 126 127 129 130 132 133 134 137 138 + 139 140 141 142 143 144 145 147 150 151 152 154 155 156 157 158 + 161 165 170 172 174 176 177 179 184 186 187 190 192 194 195 196 + 197 200 202 203 205 208 209 214 215 216 217 218 222 226 227 229 234 + +build-user: + script: + - ./configure --enable-werror --disable-system --disable-guest-agent + --disable-capstone --disable-slirp --disable-fdt + - make -j2 + - make run-tcg-tests-i386-linux-user run-tcg-tests-x86_64-linux-user + +build-clang: + script: + - apt-get install -y -qq clang libsdl2-dev + xfslibs-dev libiscsi-dev libnfs-dev libseccomp-dev gnutls-dev librbd-dev + - ./configure --cc=clang --cxx=clang++ --enable-werror + --target-list="alpha-softmmu arm-softmmu m68k-softmmu mips64-softmmu + ppc-softmmu s390x-softmmu x86_64-softmmu arm-linux-user" + - make -j2 + - make -j2 check diff --git a/.gitmodules b/.gitmodules index d108478e0a..ceafb0ee29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,45 +1,54 @@ [submodule "roms/seabios"] path = roms/seabios - url = git://git.qemu-project.org/seabios.git/ + url = https://git.qemu.org/git/seabios.git/ [submodule "roms/SLOF"] path = roms/SLOF - url = git://git.qemu-project.org/SLOF.git + url = https://git.qemu.org/git/SLOF.git [submodule "roms/ipxe"] path = roms/ipxe - url = git://git.qemu-project.org/ipxe.git + url = https://git.qemu.org/git/ipxe.git [submodule "roms/openbios"] path = roms/openbios - url = git://git.qemu-project.org/openbios.git + url = https://git.qemu.org/git/openbios.git [submodule "roms/openhackware"] path = roms/openhackware - url = git://git.qemu-project.org/openhackware.git + url = https://git.qemu.org/git/openhackware.git [submodule "roms/qemu-palcode"] path = roms/qemu-palcode - url = git://git.qemu.org/qemu-palcode.git + url = https://git.qemu.org/git/qemu-palcode.git [submodule "roms/sgabios"] path = roms/sgabios - url = git://git.qemu-project.org/sgabios.git + url = https://git.qemu.org/git/sgabios.git [submodule "dtc"] path = dtc - url = git://git.qemu-project.org/dtc.git + url = https://git.qemu.org/git/dtc.git [submodule "roms/u-boot"] path = roms/u-boot - url = git://git.qemu-project.org/u-boot.git + url = https://git.qemu.org/git/u-boot.git [submodule "roms/skiboot"] path = roms/skiboot - url = git://git.qemu.org/skiboot.git + url = https://git.qemu.org/git/skiboot.git [submodule "roms/QemuMacDrivers"] path = roms/QemuMacDrivers - url = git://git.qemu.org/QemuMacDrivers.git + url = https://git.qemu.org/git/QemuMacDrivers.git [submodule "ui/keycodemapdb"] path = ui/keycodemapdb - url = git://git.qemu.org/keycodemapdb.git + url = https://git.qemu.org/git/keycodemapdb.git [submodule "capstone"] path = capstone - url = git://git.qemu.org/capstone.git + url = https://git.qemu.org/git/capstone.git [submodule "roms/seabios-hppa"] path = roms/seabios-hppa - url = git://github.com/hdeller/seabios-hppa.git + url = https://github.com/hdeller/seabios-hppa.git [submodule "roms/u-boot-sam460ex"] path = roms/u-boot-sam460ex - url = git://git.qemu.org/u-boot-sam460ex.git + url = https://git.qemu.org/git/u-boot-sam460ex.git +[submodule "tests/fp/berkeley-testfloat-3"] + path = tests/fp/berkeley-testfloat-3 + url = https://github.com/cota/berkeley-testfloat-3 +[submodule "tests/fp/berkeley-softfloat-3"] + path = tests/fp/berkeley-softfloat-3 + url = https://github.com/cota/berkeley-softfloat-3 +[submodule "roms/edk2"] + path = roms/edk2 + url = https://github.com/tianocore/edk2.git diff --git a/.mailmap b/.mailmap index 778a4d4e2c..b8e08297c9 100644 --- a/.mailmap +++ b/.mailmap @@ -12,8 +12,12 @@ Fabrice Bellard bellard Jocelyn Mayer j_mayer Paul Brook pbrook -Paul Burton -Paul Burton +Yongbok Kim +Aleksandar Markovic +Aleksandar Markovic +Paul Burton +Paul Burton +Paul Burton Thiemo Seufer ths malc malc @@ -30,5 +34,6 @@ Justin Terry (VM) Justin Terry (VM) via Qemu-devel +Reimar Döffinger diff --git a/.shippable.yml b/.shippable.yml index f74a3de3ff..f2ffef21d1 100644 --- a/.shippable.yml +++ b/.shippable.yml @@ -7,10 +7,11 @@ env: matrix: - IMAGE=debian-amd64 TARGET_LIST=x86_64-softmmu,x86_64-linux-user - - IMAGE=debian-win32-cross - TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu - - IMAGE=debian-win64-cross - TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu + # currently disabled as the mxe.cc repos are down + # - IMAGE=debian-win32-cross + # TARGET_LIST=arm-softmmu,i386-softmmu,lm32-softmmu + # - IMAGE=debian-win64-cross + # TARGET_LIST=aarch64-softmmu,sparc64-softmmu,x86_64-softmmu - IMAGE=debian-armel-cross TARGET_LIST=arm-softmmu,arm-linux-user,armeb-linux-user - IMAGE=debian-armhf-cross diff --git a/COPYING.LIB b/COPYING.LIB index 48afc2efd7..4362b49151 100644 --- a/COPYING.LIB +++ b/COPYING.LIB @@ -1,8 +1,8 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + 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. @@ -10,7 +10,7 @@ as the successor of the GNU Library Public License, version 2, hence the version number 2.1.] - Preamble + Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public @@ -112,7 +112,7 @@ modification follow. Pay close attention to the difference between a former contains code derived from the library, whereas the latter must be combined with the library in order to run. - GNU LESSER GENERAL PUBLIC LICENSE + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License Agreement applies to any software library or other @@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - + 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an @@ -432,7 +432,7 @@ 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 + NO WARRANTY 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. @@ -455,7 +455,7 @@ FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - END OF TERMS AND CONDITIONS + END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries @@ -476,7 +476,7 @@ convey the exclusion of warranty; and each file should have at least the 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. + 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 @@ -485,7 +485,7 @@ convey the exclusion of warranty; and each file should have at least the 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 + 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. @@ -500,5 +500,3 @@ necessary. Here is a sample; alter the names: Ty Coon, President of Vice That's all there is to it! - - diff --git a/Kconfig.host b/Kconfig.host new file mode 100644 index 0000000000..aec95365ff --- /dev/null +++ b/Kconfig.host @@ -0,0 +1,36 @@ +# These are "proxy" symbols used to pass config-host.mak values +# down to Kconfig. See also MINIKCONF_ARGS in the Makefile: +# these two need to be kept in sync. + +config KVM + bool + +config LINUX + bool + +config OPENGL + bool + +config X11 + bool + +config SPICE + bool + +config IVSHMEM + bool + +config TPM + bool + +config VHOST_USER + bool + +config XEN + bool + +config VIRTFS + bool + +config PVRDMA + bool diff --git a/MAINTAINERS b/MAINTAINERS index 666e936812..56139ac8ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -12,9 +12,14 @@ consult qemu-devel and not any specific individual privately. Descriptions of section entries: M: Mail patches to: FullName + Maintainers are looking after a certain area and must be CCed on + patches. They are considered the main contact point. R: Designated reviewer: FullName These reviewers should be CCed on patches. + Reviewers are familiar with the subject matter and provide feedback + even though they are not maintainers. L: Mailing list that is relevant to this area + These lists should be CCed on patches. W: Web-page with status/info Q: Patchwork web based patch tracking system site T: SCM tree type and location. Type is one of: git, hg, quilt, stgit. @@ -74,7 +79,7 @@ S: Maintained L: qemu-trivial@nongnu.org K: ^Subject:.*(?i)trivial T: git git://git.corpit.ru/qemu.git trivial-patches -T: git git://github.com/vivier/qemu.git trivial-patches +T: git https://github.com/vivier/qemu.git trivial-patches Architecture support -------------------- @@ -88,6 +93,7 @@ F: hw/char/terminal3270.c F: hw/intc/s390_flic.c F: hw/intc/s390_flic_kvm.c F: hw/s390x/ +F: hw/vfio/ap.c F: hw/vfio/ccw.c F: hw/watchdog/wdt_diag288.c F: include/hw/s390x/ @@ -95,21 +101,24 @@ F: include/hw/watchdog/wdt_diag288.h F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img F: target/s390x/ +F: docs/vfio-ap.txt K: ^Subject:.*(?i)s390x? -T: git git://github.com/cohuck/qemu.git s390-next +T: git https://github.com/cohuck/qemu.git s390-next L: qemu-s390x@nongnu.org Guest CPU cores (TCG): ---------------------- Overall L: qemu-devel@nongnu.org -M: Paolo Bonzini -M: Peter Crosthwaite M: Richard Henderson +R: Paolo Bonzini S: Maintained F: cpus.c F: exec.c F: accel/tcg/ +F: accel/stubs/tcg-stub.c +F: scripts/decodetree.py +F: docs/devel/decodetree.rst F: include/exec/cpu*.h F: include/exec/exec-all.h F: include/exec/helper*.h @@ -119,9 +128,11 @@ F: include/sysemu/cpus.h FPU emulation M: Aurelien Jarno M: Peter Maydell -S: Odd Fixes +M: Alex Bennée +S: Maintained F: fpu/ F: include/fpu/ +F: tests/fp/ Alpha M: Richard Henderson @@ -144,6 +155,13 @@ F: disas/arm.c F: disas/arm-a64.cc F: disas/libvixl/ +ARM SMMU +M: Eric Auger +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/smmu* +F: include/hw/arm/smmu* + CRIS M: Edgar E. Iglesias S: Maintained @@ -168,6 +186,7 @@ F: disas/lm32.c F: hw/lm32/ F: hw/*/lm32_* F: hw/*/milkymist-* +F: include/hw/display/milkymist_tmu2.h F: include/hw/char/lm32_juart.h F: include/hw/lm32/ F: tests/tcg/lm32/ @@ -187,19 +206,24 @@ F: disas/microblaze.c MIPS M: Aurelien Jarno -M: Aleksandar Markovic +M: Aleksandar Markovic +R: Aleksandar Rikalo S: Maintained F: target/mips/ +F: default-configs/*mips* +F: disas/mips.c +F: disas/nanomips.cpp +F: disas/nanomips.h +F: hw/intc/mips_gic.c F: hw/mips/ F: hw/misc/mips_* -F: hw/intc/mips_gic.c F: hw/timer/mips_gictimer.c +F: include/hw/intc/mips_gic.h F: include/hw/mips/ F: include/hw/misc/mips_* -F: include/hw/intc/mips_gic.h F: include/hw/timer/mips_gictimer.h F: tests/tcg/mips/ -F: disas/mips.c +K: ^Subject:.*(?i)mips Moxie M: Anthony Green @@ -215,7 +239,9 @@ M: Marek Vasut S: Maintained F: target/nios2/ F: hw/nios2/ +F: hw/intc/nios2_iic.c F: disas/nios2.c +F: default-configs/nios2-softmmu.mak OpenRISC M: Stafford Horne @@ -226,7 +252,6 @@ F: tests/tcg/openrisc/ PowerPC M: David Gibson -M: Alexander Graf L: qemu-ppc@nongnu.org S: Maintained F: target/ppc/ @@ -235,24 +260,26 @@ F: include/hw/ppc/ F: disas/ppc.c RISC-V -M: Michael Clark M: Palmer Dabbelt +M: Alistair Francis M: Sagar Karandikar M: Bastian Koppelmann -S: Maintained +L: qemu-riscv@nongnu.org +S: Supported F: target/riscv/ F: hw/riscv/ F: include/hw/riscv/ -F: disas/riscv.c +F: linux-user/host/riscv32/ +F: linux-user/host/riscv64/ S390 M: Richard Henderson -M: Alexander Graf M: David Hildenbrand S: Maintained F: target/s390x/ F: hw/s390x/ F: disas/s390.c +F: tests/tcg/s390x/ L: qemu-s390x@nongnu.org SH4 @@ -270,6 +297,7 @@ S: Maintained F: target/sparc/ F: hw/sparc/ F: hw/sparc64/ +F: include/hw/sparc/sparc64.h F: disas/sparc.c UniCore32 @@ -289,7 +317,8 @@ F: tests/tcg/i386/ F: tests/tcg/x86_64/ F: hw/i386/ F: disas/i386.c -T: git git://github.com/ehabkost/qemu.git x86-next +F: docs/qemu-cpu-models.texi +T: git https://github.com/ehabkost/qemu.git x86-next Xtensa M: Max Filippov @@ -299,6 +328,8 @@ F: target/xtensa/ F: hw/xtensa/ F: tests/tcg/xtensa/ F: disas/xtensa.c +F: include/hw/xtensa/xtensa-isa.h +F: default-configs/xtensa*.mak TriCore M: Bastian Koppelmann @@ -309,6 +340,7 @@ F: include/hw/tricore/ Multiarch Linux User Tests M: Alex Bennée +S: Maintained F: tests/tcg/multiarch/ Guest CPU Cores (KVM): @@ -320,7 +352,10 @@ L: kvm@vger.kernel.org S: Supported F: */kvm.* F: accel/kvm/ +F: accel/stubs/kvm-stub.c +F: include/hw/kvm/ F: include/sysemu/kvm*.h +F: scripts/kvm/kvm_flightrecorder ARM M: Peter Maydell @@ -330,19 +365,20 @@ F: target/arm/kvm.c MIPS M: James Hogan +R: Aleksandar Rikalo S: Maintained F: target/mips/kvm.c PPC -M: Alexander Graf +M: David Gibson S: Maintained F: target/ppc/kvm.c S390 -M: Christian Borntraeger +M: Halil Pasic M: Cornelia Huck -M: Alexander Graf -S: Maintained +M: Christian Borntraeger +S: Supported F: target/s390x/kvm.c F: target/s390x/kvm_s390x.h F: target/s390x/kvm-stub.c @@ -353,8 +389,8 @@ 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 +T: git https://github.com/cohuck/qemu.git s390-next +T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org X86 @@ -363,6 +399,7 @@ M: Marcelo Tosatti L: kvm@vger.kernel.org S: Supported F: target/i386/kvm.c +F: scripts/kvm/vmxcap Guest CPU Cores (Xen): ---------------------- @@ -370,6 +407,7 @@ Guest CPU Cores (Xen): X86 M: Stefano Stabellini M: Anthony Perard +M: Paul Durrant L: xen-devel@lists.xenproject.org S: Supported F: */xen* @@ -377,10 +415,12 @@ F: hw/9pfs/xen-9p-backend.c F: hw/char/xen_console.c F: hw/display/xenfb.c F: hw/net/xen_nic.c -F: hw/block/xen_* +F: hw/block/xen* +F: hw/block/dataplane/xen* F: hw/xen/ F: hw/xenpv/ F: hw/i386/xen/ +F: include/hw/block/dataplane/xen* F: include/hw/xen/ F: include/sysemu/xen-mapcache.h @@ -388,15 +428,20 @@ Hosts: ------ LINUX -L: qemu-devel@nongnu.org +M: Michael S. Tsirkin +M: Cornelia Huck +M: Paolo Bonzini S: Maintained -F: linux-* F: linux-headers/ +F: scripts/update-linux-headers.sh POSIX -L: qemu-devel@nongnu.org +M: Paolo Bonzini S: Maintained -F: *posix* +F: os-posix.c +F: include/sysemu/os-posix.h +F: util/*posix*.c +F: include/qemu/*posix*.h NETBSD L: qemu-devel@nongnu.org @@ -430,8 +475,9 @@ ARM Machines ------------ Allwinner-a10 M: Beniamino Galvani +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/allwinner* F: include/hw/*/allwinner* F: hw/arm/cubieboard.c @@ -444,21 +490,31 @@ F: hw/char/pl011.c F: include/hw/char/pl011.h F: hw/display/pl110* F: hw/dma/pl080.c +F: include/hw/dma/pl080.h 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/ssi/pl022.c +F: include/hw/ssi/pl022.h F: hw/timer/pl031.c +F: include/hw/timer/pl031.h F: include/hw/arm/primecell.h F: hw/timer/cmsdk-apb-timer.c F: include/hw/timer/cmsdk-apb-timer.h +F: hw/timer/cmsdk-apb-dualtimer.c +F: include/hw/timer/cmsdk-apb-dualtimer.h F: hw/char/cmsdk-apb-uart.c F: include/hw/char/cmsdk-apb-uart.h +F: hw/watchdog/cmsdk-apb-watchdog.c +F: include/hw/watchdog/cmsdk-apb-watchdog.h F: hw/misc/tz-ppc.c F: include/hw/misc/tz-ppc.h F: hw/misc/tz-mpc.c F: include/hw/misc/tz-mpc.h +F: hw/misc/tz-msc.c +F: include/hw/misc/tz-msc.h ARM cores M: Peter Maydell @@ -468,6 +524,7 @@ F: hw/intc/arm* F: hw/intc/gic_internal.h F: hw/misc/a9scu.c F: hw/misc/arm11scu.c +F: hw/misc/arm_l2x0.c F: hw/timer/a9gtimer* F: hw/timer/arm* F: include/hw/arm/arm*.h @@ -481,40 +538,58 @@ F: tests/test-arm-mptimer.c Exynos M: Igor Mitsyanko +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/*/exynos* F: include/hw/arm/exynos4210.h Calxeda Highbank M: Rob Herring +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/highbank.c F: hw/net/xgmac.c Canon DIGIC M: Antony Pavlov +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: include/hw/arm/digic.h F: hw/*/digic* +F: include/hw/*/digic* Gumstix -M: Philippe Mathieu-Daudé +M: Peter Maydell +R: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org L: qemu-arm@nongnu.org S: Odd Fixes F: hw/arm/gumstix.c -i.MX31 -M: Peter Chubb +i.MX25 PDK +M: Peter Maydell +R: Jean-Christophe Dubois L: qemu-arm@nongnu.org -S: Odd fixes -F: hw/*/imx* -F: include/hw/*/imx* +S: Odd Fixes +F: hw/arm/fsl-imx25.c +F: hw/arm/imx25_pdk.c +F: hw/misc/imx25_ccm.c +F: include/hw/arm/fsl-imx25.h +F: include/hw/misc/imx25_ccm.h + +i.MX31 (kzm) +M: Peter Chubb +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Odd Fixes F: hw/arm/kzm.c -F: include/hw/arm/fsl-imx31.h +F: hw/*/imx_* +F: hw/*/*imx31* +F: include/hw/*/imx_* +F: include/hw/*/*imx31* Integrator CP M: Peter Maydell @@ -522,6 +597,31 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/integratorcp.c F: hw/misc/arm_integrator_debug.c +F: include/hw/misc/arm_integrator_debug.h + +MCIMX6UL EVK / i.MX6ul +M: Peter Maydell +R: Jean-Christophe Dubois +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/mcimx6ul-evk.c +F: hw/arm/fsl-imx6ul.c +F: hw/misc/imx6ul_ccm.c +F: include/hw/arm/fsl-imx6ul.h +F: include/hw/misc/imx6ul_ccm.h + +MCIMX7D SABRE / i.MX7 +M: Peter Maydell +R: Andrey Smirnov +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/mcimx7d-sabre.c +F: hw/arm/fsl-imx7.c +F: hw/misc/imx7_*.c +F: include/hw/arm/fsl-imx7.h +F: include/hw/misc/imx7_*.h +F: hw/pci-host/designware.c +F: include/hw/pci-host/designware.h MPS2 M: Peter Maydell @@ -531,26 +631,62 @@ F: hw/arm/mps2.c F: hw/arm/mps2-tz.c F: hw/misc/mps2-*.c F: include/hw/misc/mps2-*.h -F: hw/arm/iotkit.c -F: include/hw/arm/iotkit.h +F: hw/arm/armsse.c +F: include/hw/arm/armsse.h +F: hw/misc/iotkit-secctl.c +F: include/hw/misc/iotkit-secctl.h +F: hw/misc/iotkit-sysctl.c +F: include/hw/misc/iotkit-sysctl.h +F: hw/misc/iotkit-sysinfo.c +F: include/hw/misc/iotkit-sysinfo.h +F: hw/misc/armsse-cpuid.c +F: include/hw/misc/armsse-cpuid.h +F: hw/misc/armsse-mhu.c +F: include/hw/misc/armsse-mhu.h + +Musca +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/musca.c Musicpal M: Jan Kiszka +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/musicpal.c nSeries M: Andrzej Zaborowski +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/nseries.c +F: hw/input/lm832x.c +F: hw/input/tsc2005.c +F: hw/misc/cbus.c +F: hw/timer/twl92230.c Palm M: Andrzej Zaborowski +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/palm.c +F: hw/input/tsc210x.c + +Raspberry Pi +M: Peter Maydell +R: Andrew Baumann +R: Philippe Mathieu-Daudé +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/raspi.c +F: hw/arm/raspi_platform.h +F: hw/*/bcm283* +F: include/hw/arm/raspi* +F: include/hw/*/bcm283* Real View M: Peter Maydell @@ -563,15 +699,41 @@ F: include/hw/intc/realview_gic.h PXA2XX M: Andrzej Zaborowski +M: Peter Maydell L: qemu-arm@nongnu.org -S: Maintained +S: Odd Fixes F: hw/arm/mainstone.c F: hw/arm/spitz.c F: hw/arm/tosa.c F: hw/arm/z2.c F: hw/*/pxa2xx* +F: hw/display/tc6393xb.c +F: hw/gpio/max7310.c +F: hw/gpio/zaurus.c F: hw/misc/mst_fpga.c +F: hw/misc/max111x.c F: include/hw/arm/pxa.h +F: include/hw/arm/sharpsl.h + +SABRELITE / i.MX6 +M: Peter Maydell +R: Jean-Christophe Dubois +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/sabrelite.c +F: hw/arm/fsl-imx6.c +F: hw/misc/imx6_*.c +F: hw/ssi/imx_spi.c +F: include/hw/arm/fsl-imx6.h +F: include/hw/misc/imx6_*.h +F: include/hw/ssi/imx_spi.h + +Sharp SL-5500 (Collie) PDA +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Odd Fixes +F: hw/arm/collie.c +F: hw/arm/strongarm* Stellaris M: Peter Maydell @@ -579,6 +741,12 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/*/stellaris* +Versatile Express +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/vexpress.c + Versatile PB M: Peter Maydell L: qemu-arm@nongnu.org @@ -586,9 +754,17 @@ S: Maintained F: hw/*/versatile* F: hw/misc/arm_sysctl.c +Virt +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/arm/virt* +F: include/hw/arm/virt.h + Xilinx Zynq M: Edgar E. Iglesias M: Alistair Francis +M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/xilinx_* @@ -600,13 +776,16 @@ X: hw/ssi/xilinx_* Xilinx ZynqMP M: Alistair Francis M: Edgar E. Iglesias +M: Peter Maydell L: qemu-arm@nongnu.org S: Maintained F: hw/*/xlnx*.c F: include/hw/*/xlnx*.h +F: include/hw/ssi/xilinx_spips.h +F: hw/display/dpcd.c +F: include/hw/display/dpcd.h ARM ACPI Subsystem -M: Shannon Zhao M: Shannon Zhao L: qemu-arm@nongnu.org S: Maintained @@ -614,6 +793,7 @@ F: hw/arm/virt-acpi-build.c STM32F205 M: Alistair Francis +M: Peter Maydell S: Maintained F: hw/arm/stm32f205_soc.c F: hw/misc/stm32f2xx_syscfg.c @@ -625,11 +805,13 @@ F: include/hw/*/stm32*.h Netduino 2 M: Alistair Francis +M: Peter Maydell S: Maintained F: hw/arm/netduino2.c SmartFusion2 M: Subbaraya Sundeep +M: Peter Maydell S: Maintained F: hw/arm/msf2-soc.c F: hw/misc/msf2-sysreg.c @@ -642,20 +824,35 @@ F: include/hw/ssi/mss-spi.h Emcraft M2S-FG484 M: Subbaraya Sundeep +M: Peter Maydell S: Maintained F: hw/arm/msf2-som.c ASPEED BMCs M: Cédric Le Goater +M: Peter Maydell R: Andrew Jeffery R: Joel Stanley L: qemu-arm@nongnu.org S: Maintained F: hw/*/*aspeed* +F: hw/misc/pca9552.c F: include/hw/*/*aspeed* +F: include/hw/misc/pca9552*.h F: hw/net/ftgmac100.c F: include/hw/net/ftgmac100.h +NRF51 +M: Joel Stanley +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Maintained +F: hw/*/nrf51*.c +F: hw/*/microbit*.c +F: include/hw/*/nrf51*.h +F: include/hw/*/microbit*.h +F: tests/microbit-test.c + CRIS Machines ------------- Axis Dev88 @@ -664,6 +861,15 @@ S: Maintained F: hw/cris/axis_dev88.c F: hw/*/etraxfs_*.c +HP-PARISC Machines +------------------ +Dino +M: Richard Henderson +R: Helge Deller +S: Odd Fixes +F: hw/hppa/ +F: pc-bios/hppa-firmware.img + LM32 Machines ------------- EVR32 and uclinux BSP @@ -699,6 +905,7 @@ petalogix_s3adsp1800 M: Edgar E. Iglesias S: Maintained F: hw/microblaze/petalogix_s3adsp1800_mmu.c +F: include/hw/char/xilinx_uartlite.h petalogix_ml605 M: Edgar E. Iglesias @@ -709,38 +916,48 @@ MIPS Machines ------------- Jazz M: Hervé Poussineau +R: Aleksandar Rikalo S: Maintained F: hw/mips/mips_jazz.c +F: hw/display/jazz_led.c +F: hw/dma/rc4030.c Malta M: Aurelien Jarno +R: Aleksandar Rikalo S: Maintained F: hw/mips/mips_malta.c Mipssim -M: Aleksandar Markovic +M: Aleksandar Markovic +R: Aleksandar Rikalo S: Odd Fixes F: hw/mips/mips_mipssim.c F: hw/net/mipsnet.c R4000 M: Aurelien Jarno +R: Aleksandar Rikalo S: Maintained F: hw/mips/mips_r4k.c Fulong 2E -M: Aleksandar Markovic +M: Aleksandar Markovic +R: Aleksandar Rikalo S: Odd Fixes F: hw/mips/mips_fulong2e.c F: hw/isa/vt82c686.c +F: hw/pci-host/bonito.c F: include/hw/isa/vt82c686.h Boston -M: Paul Burton +M: Paul Burton +R: Aleksandar Rikalo S: Maintained F: hw/core/loader-fit.c F: hw/mips/boston.c F: hw/pci-host/xilinx-pcie.c +F: include/hw/pci-host/xilinx-pcie.h OpenRISC Machines ----------------- @@ -752,56 +969,69 @@ F: hw/openrisc/openrisc_sim.c PowerPC Machines ---------------- 405 -M: Alexander Graf +M: David Gibson L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/ppc405_boards.c Bamboo -M: Alexander Graf +M: David Gibson L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/ppc440_bamboo.c e500 -M: Alexander Graf +M: David Gibson L: qemu-ppc@nongnu.org -S: Supported -F: hw/ppc/e500.[hc] -F: hw/ppc/e500plat.c +S: Odd Fixes +F: hw/ppc/e500* +F: hw/gpio/mpc8xxx.c +F: hw/i2c/mpc_i2c.c +F: hw/net/fsl_etsec/ +F: hw/pci-host/ppce500.c F: include/hw/ppc/ppc_e500.h F: include/hw/pci-host/ppce500.h F: pc-bios/u-boot.e500 mpc8544ds -M: Alexander Graf +M: David Gibson L: qemu-ppc@nongnu.org -S: Supported +S: Odd Fixes F: hw/ppc/mpc8544ds.c F: hw/ppc/mpc8544_guts.c -New World -M: Alexander Graf +New World (mac99) +M: Mark Cave-Ayland +R: David Gibson L: qemu-ppc@nongnu.org -S: Maintained +S: Odd Fixes F: hw/ppc/mac_newworld.c F: hw/pci-host/uninorth.c F: hw/pci-bridge/dec.[hc] F: hw/misc/macio/ F: hw/misc/mos6522.c F: hw/nvram/mac_nvram.c +F: hw/input/adb* F: include/hw/misc/macio/ F: include/hw/misc/mos6522.h F: include/hw/ppc/mac_dbdma.h +F: include/hw/pci-host/uninorth.h +F: include/hw/input/adb* +F: pc-bios/qemu_vga.ndrv -Old World -M: Alexander Graf +Old World (g3beige) +M: Mark Cave-Ayland +R: David Gibson L: qemu-ppc@nongnu.org -S: Maintained +S: Odd Fixes F: hw/ppc/mac_oldworld.c F: hw/pci-host/grackle.c F: hw/misc/macio/ F: hw/intc/heathrow_pic.c +F: hw/input/adb* +F: include/hw/intc/heathrow_pic.h +F: include/hw/input/adb* +F: pc-bios/qemu_vga.ndrv PReP M: Hervé Poussineau @@ -822,7 +1052,6 @@ F: pc-bios/ppc_rom.bin sPAPR M: David Gibson -M: Alexander Graf L: qemu-ppc@nongnu.org S: Supported F: hw/*/spapr* @@ -832,7 +1061,6 @@ F: include/hw/*/xics* F: pc-bios/spapr-rtas/* F: pc-bios/spapr-rtas.bin F: pc-bios/slof.bin -F: pc-bios/skiboot.lid F: docs/specs/ppc-spapr-hcalls.txt F: docs/specs/ppc-spapr-hotplug.txt F: tests/spapr* @@ -840,6 +1068,18 @@ F: tests/libqos/*spapr* F: tests/rtas* F: tests/libqos/rtas* +PowerNV (Non-Virtualized) +M: Cédric Le Goater +M: David Gibson +L: qemu-ppc@nongnu.org +S: Maintained +F: hw/ppc/pnv* +F: hw/intc/pnv* +F: hw/intc/xics_pnv.c +F: include/hw/ppc/pnv* +F: pc-bios/skiboot.lid +F: tests/pnv* + virtex_ml507 M: Edgar E. Iglesias L: qemu-ppc@nongnu.org @@ -850,8 +1090,14 @@ sam460ex M: BALATON Zoltan L: qemu-ppc@nongnu.org S: Maintained +F: hw/ppc/sam460ex.c +F: hw/ppc/ppc440_pcix.c +F: hw/display/sm501* F: hw/ide/sii3112.c F: hw/timer/m41t80.c +F: pc-bios/canyonlands.dt[sb] +F: pc-bios/u-boot-sam460ex-20100605.bin +F: roms/u-boot-sam460ex SH4 Machines ------------ @@ -873,18 +1119,27 @@ Sun4m M: Mark Cave-Ayland S: Maintained F: hw/sparc/sun4m.c +F: hw/sparc/sun4m_iommu.c +F: hw/display/cg3.c +F: hw/display/tcx.c F: hw/dma/sparc32_dma.c -F: hw/dma/sun4m_iommu.c F: hw/misc/eccmemctl.c -F: hw/misc/slavio_misc.c +F: hw/*/slavio_*.c +F: include/hw/nvram/sun_nvram.h F: include/hw/sparc/sparc32_dma.h -F: include/hw/sparc/sun4m.h +F: include/hw/sparc/sun4m_iommu.h F: pc-bios/openbios-sparc32 Sun4u M: Mark Cave-Ayland S: Maintained F: hw/sparc64/sun4u.c +F: hw/sparc64/sun4u_iommu.c +F: include/hw/sparc/sun4u_iommu.h +F: hw/pci-host/sabre.c +F: include/hw/pci-host/sabre.h +F: hw/pci-bridge/simba.c +F: include/hw/pci-bridge/simba.h F: pc-bios/openbios-sparc64 Sun4v @@ -905,8 +1160,8 @@ S390 Machines ------------- S390 Virtio-ccw M: Cornelia Huck +M: Halil Pasic M: Christian Borntraeger -M: Alexander Graf S: Supported F: hw/char/sclp*.[hc] F: hw/char/terminal3270.c @@ -915,17 +1170,24 @@ F: include/hw/s390x/ F: hw/watchdog/wdt_diag288.c F: include/hw/watchdog/wdt_diag288.h F: default-configs/s390x-softmmu.mak -T: git git://github.com/cohuck/qemu.git s390-next -T: git git://github.com/borntraeger/qemu.git s390-next +T: git https://github.com/cohuck/qemu.git s390-next +T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org -S390-ccw Bios +S390-ccw boot M: Christian Borntraeger M: Thomas Huth S: Supported +F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img -T: git git://github.com/borntraeger/qemu.git s390-next +T: git https://github.com/borntraeger/qemu.git s390-next +L: qemu-s390x@nongnu.org + +S390 PCI +M: Collin Walling +S: Supported +F: hw/s390x/s390-pci* L: qemu-s390x@nongnu.org UniCore32 Machines @@ -957,6 +1219,10 @@ F: hw/acpi/ich9.c F: include/hw/acpi/ich9.h F: include/hw/acpi/piix4.h F: hw/misc/sga.c +F: hw/isa/apm.c +F: include/hw/isa/apm.h +F: tests/test-x86-cpuid.c +F: tests/test-x86-cpuid-compat.c PC Chipset M: Michael S. Tsirkin @@ -978,11 +1244,13 @@ F: hw/timer/hpet* F: hw/timer/i8254* F: hw/timer/mc146818rtc* F: hw/watchdog/wdt_ib700.c +F: hw/watchdog/wdt_i6300esb.c F: include/hw/display/vga.h F: include/hw/char/parallel.h F: include/hw/dma/i8257.h F: include/hw/i2c/pm_smbus.h F: include/hw/input/i8042.h +F: include/hw/isa/i8259_internal.h F: include/hw/isa/superio.h F: include/hw/timer/hpet.h F: include/hw/timer/i8254* @@ -994,8 +1262,10 @@ M: Marcel Apfelbaum S: Supported F: hw/core/machine.c F: hw/core/null-machine.c +F: hw/cpu/cluster.c F: include/hw/boards.h -T: git git://github.com/ehabkost/qemu.git machine-next +F: include/hw/cpu/cluster.h +T: git https://github.com/ehabkost/qemu.git machine-next Xtensa Machines --------------- @@ -1031,7 +1301,7 @@ F: tests/ide-test.c F: tests/ahci-test.c F: tests/cdrom-test.c F: tests/libqos/ahci* -T: git git://github.com/jnsnow/qemu.git ide +T: git https://github.com/jnsnow/qemu.git ide IPMI M: Corey Minyard @@ -1040,7 +1310,7 @@ F: include/hw/ipmi/* F: hw/ipmi/* F: hw/smbios/smbios_type_38.c F: tests/ipmi* -T: git git://github.com/cminyard/qemu.git master-ipmi-rebase +T: git https://github.com/cminyard/qemu.git master-ipmi-rebase Floppy M: John Snow @@ -1049,7 +1319,7 @@ 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 +T: git https://github.com/jnsnow/qemu.git ide OMAP M: Peter Maydell @@ -1080,7 +1350,7 @@ M: Michael S. Tsirkin M: Igor Mammedov S: Supported F: include/hw/acpi/* -F: include/hw/smbios/* +F: include/hw/firmware/smbios.h F: hw/mem/* F: hw/acpi/* F: hw/smbios/* @@ -1088,9 +1358,10 @@ F: hw/i386/acpi-build.[hc] F: hw/arm/virt-acpi-build.c F: tests/bios-tables-test.c F: tests/acpi-utils.[hc] +F: tests/data/acpi/ ppc4xx -M: Alexander Graf +M: David Gibson L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/ppc/ppc4*.c @@ -1098,16 +1369,9 @@ F: hw/i2c/ppc4xx_i2c.c F: include/hw/ppc/ppc4xx.h F: include/hw/i2c/ppc4xx_i2c.h -ppce500 -M: Alexander Graf -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 +M: Marc-André Lureau +R: Paolo Bonzini S: Odd Fixes F: hw/char/ @@ -1117,19 +1381,18 @@ S: Odd Fixes F: hw/net/ F: include/hw/net/ F: tests/virtio-net-test.c -T: git git://github.com/jasowang/qemu.git net +T: git https://github.com/jasowang/qemu.git net SCSI M: Paolo Bonzini -R: Fam Zheng +R: Fam Zheng S: Supported F: include/hw/scsi/* F: hw/scsi/* F: tests/virtio-scsi-test.c -T: git git://github.com/bonzini/qemu.git scsi-next +T: git https://github.com/bonzini/qemu.git scsi-next SSI -M: Peter Crosthwaite M: Alistair Francis S: Maintained F: hw/ssi/* @@ -1140,7 +1403,6 @@ F: tests/m25p80-test.c Xilinx SPI M: Alistair Francis -M: Peter Crosthwaite S: Maintained F: hw/ssi/xilinx_* @@ -1177,18 +1439,36 @@ F: include/hw/vfio/ vfio-ccw M: Cornelia Huck +M: Eric Farman +M: Farhan Ali S: Supported F: hw/vfio/ccw.c F: hw/s390x/s390-ccw.c F: include/hw/s390x/s390-ccw.h -T: git git://github.com/cohuck/qemu.git s390-next +T: git https://github.com/cohuck/qemu.git s390-next +L: qemu-s390x@nongnu.org + +vfio-ap +M: Christian Borntraeger +M: Tony Krowiak +M: Halil Pasic +M: Pierre Morel +S: Supported +F: hw/s390x/ap-device.c +F: hw/s390x/ap-bridge.c +F: include/hw/s390x/ap-device.h +F: include/hw/s390x/ap-bridge.h +F: hw/vfio/ap.c +F: docs/vfio-ap.txt L: qemu-s390x@nongnu.org vhost M: Michael S. Tsirkin S: Supported F: hw/*/*vhost* +F: docs/interop/vhost-user.json F: docs/interop/vhost-user.txt +F: contrib/vhost-user-*/ virtio M: Michael S. Tsirkin @@ -1206,7 +1486,7 @@ S: Supported F: hw/9pfs/ F: fsdev/ F: tests/virtio-9p-test.c -T: git git://github.com/gkurz/qemu.git 9p-next +T: git https://github.com/gkurz/qemu.git 9p-next virtio-blk M: Stefan Hajnoczi @@ -1215,15 +1495,16 @@ S: Supported F: hw/block/virtio-blk.c F: hw/block/dataplane/* F: tests/virtio-blk-test.c -T: git git://github.com/stefanha/qemu.git block +T: git https://github.com/stefanha/qemu.git block virtio-ccw M: Cornelia Huck -M: Christian Borntraeger +M: Halil Pasic S: Supported -F: hw/s390x/virtio-ccw.[hc] -T: git git://github.com/cohuck/qemu.git s390-next -T: git git://github.com/borntraeger/qemu.git s390-next +F: hw/s390x/virtio-ccw*.[hc] +F: hw/s390x/vhost-vsock-ccw.c +T: git https://github.com/cohuck/qemu.git s390-next +T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org virtio-input @@ -1300,6 +1581,7 @@ S: Maintained F: hw/acpi/nvdimm.c F: hw/mem/nvdimm.c F: include/hw/mem/nvdimm.h +F: docs/nvdimm.txt e1000x M: Dmitry Fleytman @@ -1323,6 +1605,12 @@ F: hw/core/generic-loader.c F: include/hw/core/generic-loader.h F: docs/generic-loader.txt +Intel Hexadecimal Object File Loader +M: Su Hang +S: Maintained +F: tests/hexloader-test.c +F: tests/data/hex-loader/test.hex + CHRP NVRAM M: Thomas Huth S: Maintained @@ -1373,6 +1661,34 @@ S: Odd Fixes W: https://www.kraxel.org/blog/2014/10/qemu-using-cirrus-considered-harmful/ F: hw/display/cirrus* +EDID Generator +M: Gerd Hoffmann +S: Maintained +F: hw/display/edid* +F: include/hw/display/edid.h +F: qemu-edid.c + +Firmware configuration (fw_cfg) +M: Philippe Mathieu-Daudé +R: Laszlo Ersek +R: Gerd Hoffmann +S: Supported +F: docs/specs/fw_cfg.txt +F: hw/nvram/fw_cfg.c +F: include/hw/nvram/fw_cfg.h +F: include/standard-headers/linux/qemu_fw_cfg.h +F: tests/libqos/fw_cfg.c +F: tests/fw_cfg-test.c +T: git https://github.com/philmd/qemu.git fw_cfg-next + +XIVE +M: David Gibson +M: Cédric Le Goater +L: qemu-ppc@nongnu.org +S: Supported +F: hw/*/*xive* +F: include/hw/*/*xive* + Subsystems ---------- Audio @@ -1399,12 +1715,12 @@ F: qemu-io* F: tests/qemu-iotests/ F: util/qemu-progress.c F: qobject/block-qdict.c -F: test/check-block-qdict.c -T: git git://repo.or.cz/qemu/kevin.git block +F: tests/check-block-qdict.c +T: git https://repo.or.cz/qemu/kevin.git block Block I/O path M: Stefan Hajnoczi -M: Fam Zheng +M: Fam Zheng L: qemu-block@nongnu.org S: Supported F: util/async.c @@ -1414,31 +1730,31 @@ F: migration/block* F: include/block/aio.h F: include/block/aio-wait.h F: scripts/qemugdb/aio.py -T: git git://github.com/stefanha/qemu.git block +T: git https://github.com/stefanha/qemu.git block Block SCSI subsystem M: Paolo Bonzini -R: Fam Zheng +R: Fam Zheng L: qemu-block@nongnu.org S: Supported F: include/scsi/* F: scsi/* Block Jobs -M: Jeff Cody +M: John Snow L: qemu-block@nongnu.org S: Supported F: blockjob.c F: include/block/blockjob.h F: job.c F: job-qmp.c -F: include/block/job.h +F: include/qemu/job.h F: block/backup.c F: block/commit.c F: block/stream.c F: block/mirror.c F: qapi/job.json -T: git git://github.com/codyprime/qemu-kvm-jtc.git block +T: git https://github.com/jnsnow/qemu.git jobs Block QAPI, monitor, command line M: Markus Armbruster @@ -1447,10 +1763,10 @@ F: blockdev.c F: block/qapi.c F: qapi/block*.json F: qapi/transaction.json -T: git git://repo.or.cz/qemu/armbru.git block-next +T: git https://repo.or.cz/qemu/armbru.git block-next Dirty Bitmaps -M: Fam Zheng +M: Fam Zheng M: John Snow L: qemu-block@nongnu.org S: Supported @@ -1460,12 +1776,12 @@ F: include/qemu/hbitmap.h F: include/block/dirty-bitmap.h F: tests/test-hbitmap.c F: docs/interop/bitmaps.rst -T: git git://github.com/famz/qemu.git bitmaps -T: git git://github.com/jnsnow/qemu.git bitmaps +T: git https://github.com/famz/qemu.git bitmaps +T: git https://github.com/jnsnow/qemu.git bitmaps Character device backends -M: Paolo Bonzini M: Marc-André Lureau +R: Paolo Bonzini S: Maintained F: chardev/ F: include/chardev/ @@ -1497,8 +1813,8 @@ F: qom/cpu.c F: include/qom/cpu.h Device Tree -M: Peter Crosthwaite -M: Alexander Graf +M: Alistair Francis +R: David Gibson S: Maintained F: device_tree.c F: include/sysemu/device_tree.h @@ -1523,8 +1839,7 @@ F: util/error.c F: util/qemu-error.c GDB stub -L: qemu-devel@nongnu.org -S: Odd Fixes +S: Orphan F: gdbstub* F: gdb-xml/ @@ -1548,6 +1863,7 @@ F: ui/spice-*.c F: audio/spiceaudio.c F: hw/display/qxl* F: qapi/ui.json +F: docs/spice-port-fqdn.txt Graphics M: Gerd Hoffmann @@ -1555,6 +1871,7 @@ S: Odd Fixes F: ui/ F: include/ui/ F: qapi/ui.json +F: util/drm.c Cocoa graphics M: Peter Maydell @@ -1584,7 +1901,7 @@ M: Jason Wang S: Maintained F: net/ F: include/net/ -T: git git://github.com/jasowang/qemu.git net +T: git https://github.com/jasowang/qemu.git net F: qapi/net.json Netmap network backend @@ -1600,7 +1917,7 @@ M: Eduardo Habkost S: Maintained F: numa.c F: include/sysemu/numa.h -T: git git://github.com/ehabkost/qemu.git machine-next +T: git https://github.com/ehabkost/qemu.git machine-next Host Memory Backends M: Eduardo Habkost @@ -1608,7 +1925,7 @@ M: Igor Mammedov S: Maintained F: backends/hostmem*.c F: include/sysemu/hostmem.h -T: git git://github.com/ehabkost/qemu.git machine-next +T: git https://github.com/ehabkost/qemu.git machine-next Cryptodev Backends M: Gonglei @@ -1641,15 +1958,14 @@ F: tests/test-visitor-serialization.c F: scripts/qapi-gen.py F: scripts/qapi/* F: docs/devel/qapi* -T: git git://repo.or.cz/qemu/armbru.git qapi-next +T: git https://repo.or.cz/qemu/armbru.git qapi-next QAPI Schema M: Eric Blake M: Markus Armbruster S: Supported -F: qapi-schema.json F: qapi/*.json -T: git git://repo.or.cz/qemu/armbru.git qapi-next +T: git https://repo.or.cz/qemu/armbru.git qapi-next QObject M: Markus Armbruster @@ -1659,11 +1975,15 @@ F: include/qapi/qmp/ X: include/qapi/qmp/dispatch.h F: scripts/coccinelle/qobject.cocci F: tests/check-qdict.c -F: tests/check-qnum.c F: tests/check-qjson.c F: tests/check-qlist.c +F: tests/check-qlit.c +F: tests/check-qnull.c +F: tests/check-qnum.c +F: tests/check-qobject.c F: tests/check-qstring.c -T: git git://repo.or.cz/qemu/armbru.git qapi-next +F: tests/data/qobject/qdict.txt +T: git https://repo.or.cz/qemu/armbru.git qapi-next QEMU Guest Agent M: Michael Roth @@ -1673,19 +1993,18 @@ F: qemu-ga.texi F: scripts/qemu-guest-agent/ F: tests/test-qga.c F: docs/interop/qemu-ga-ref.texi -T: git git://github.com/mdroth/qemu.git qga +T: git https://github.com/mdroth/qemu.git qga QOM M: Andreas Färber S: Supported -T: git git://github.com/afaerber/qemu-cpu.git qom-next +T: git https://github.com/afaerber/qemu-cpu.git qom-next 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 M: Markus Armbruster @@ -1693,9 +2012,21 @@ S: Supported F: qmp.c F: monitor.c F: docs/devel/*qmp-* +F: docs/interop/*qmp-* F: scripts/qmp/ F: tests/qmp-test.c -T: git git://repo.or.cz/qemu/armbru.git qapi-next +F: tests/qmp-cmd-test.c +T: git https://repo.or.cz/qemu/armbru.git qapi-next + +qtest +M: Thomas Huth +M: Laurent Vivier +R: Paolo Bonzini +S: Maintained +F: qtest.c +F: tests/libqtest.* +F: tests/libqos/ +F: tests/*-test.c Register API M: Alistair Francis @@ -1727,11 +2058,12 @@ F: trace-events F: qemu-option-trace.texi F: scripts/tracetool.py F: scripts/tracetool/ +F: scripts/qemu-trace-stap* F: docs/devel/tracing.txt -T: git git://github.com/stefanha/qemu.git tracing +T: git https://github.com/stefanha/qemu.git tracing TPM -M: Stefan Berger +M: Stefan Berger S: Maintained F: tpm.c F: stubs/tpm.c @@ -1741,7 +2073,7 @@ F: include/sysemu/tpm* F: qapi/tpm.json F: backends/tpm.c F: tests/*tpm* -T: git git://github.com/stefanberger/qemu-tpm.git tpm-next +T: git https://github.com/stefanberger/qemu-tpm.git tpm-next Checkpatch S: Odd Fixes @@ -1756,7 +2088,7 @@ F: migration/ F: scripts/vmstate-static-checker.py F: tests/vmstate-static-checker-data/ F: tests/migration-test.c -F: docs/devel/migration.txt +F: docs/devel/migration.rst F: qapi/migration.json Seccomp @@ -1772,11 +2104,14 @@ F: crypto/ F: include/crypto/ F: tests/test-crypto-* F: tests/benchmark-crypto-* +F: tests/crypto-tls-* +F: tests/pkix_asn1_tab.c F: qemu.sasl Coroutines M: Stefan Hajnoczi M: Kevin Wolf +S: Maintained F: util/*coroutine* F: include/qemu/coroutine* F: tests/test-coroutine.c @@ -1794,15 +2129,29 @@ F: io/ F: include/io/ F: tests/test-io-* +User authorization +M: Daniel P. Berrange +S: Maintained +F: authz/ +F: qapi/authz.json +F: include/authz/ +F: tests/test-authz-* + Sockets M: Daniel P. Berrange M: Gerd Hoffmann -M: Paolo Bonzini S: Maintained F: include/qemu/sockets.h F: util/qemu-sockets.c F: qapi/sockets.json +File monitor +M: Daniel P. Berrange +S: Odd fixes +F: util/filemonitor*.c +F: include/qemu/filemonitor.h +F: tests/test-util-filemonitor.c + Throttling infrastructure M: Alberto Garcia S: Supported @@ -1815,7 +2164,7 @@ F: tests/test-throttle.c L: qemu-block@nongnu.org UUID -M: Fam Zheng +M: Fam Zheng S: Supported F: util/uuid.c F: include/qemu/uuid.h @@ -1830,7 +2179,7 @@ F: include/migration/failover.h F: docs/COLO-FT.txt COLO Proxy -M: Zhang Chen +M: Zhang Chen M: Li Zhijian S: Supported F: docs/colo-proxy.txt @@ -1856,6 +2205,23 @@ S: Maintained F: include/qemu/iova-tree.h F: util/iova-tree.c +elf2dmp +M: Viktor Prutyanov +S: Maintained +F: contrib/elf2dmp/ + +I2C and SMBus +M: Corey Minyard +S: Maintained +F: hw/i2c/core.c +F: hw/i2c/smbus_slave.c +F: hw/i2c/smbus_master.c +F: hw/i2c/smbus_eeprom.c +F: include/hw/i2c/i2c.h +F: include/hw/i2c/smbus_master.h +F: include/hw/i2c/smbus_slave.h +F: include/hw/i2c/smbus_eeprom.h + Usermode Emulation ------------------ Overall @@ -1901,13 +2267,14 @@ F: tcg/arm/ F: disas/arm.c i386 target -L: qemu-devel@nongnu.org +M: Richard Henderson S: Maintained F: tcg/i386/ F: disas/i386.c MIPS target M: Aurelien Jarno +R: Aleksandar Rikalo S: Maintained F: tcg/mips/ F: disas/mips.c @@ -1918,8 +2285,15 @@ S: Odd Fixes F: tcg/ppc/ F: disas/ppc.c +RISC-V +M: Palmer Dabbelt +M: Alistair Francis +L: qemu-riscv@nongnu.org +S: Maintained +F: tcg/riscv/ +F: disas/riscv.c + S390 target -M: Alexander Graf M: Richard Henderson S: Maintained F: tcg/s390/ @@ -1941,35 +2315,29 @@ F: disas/tci.c Block drivers ------------- VMDK -M: Fam Zheng +M: Fam Zheng L: qemu-block@nongnu.org S: Supported F: block/vmdk.c RBD M: Josh Durgin -M: Jeff Cody L: qemu-block@nongnu.org S: Supported F: block/rbd.c -T: git git://github.com/codyprime/qemu-kvm-jtc.git block Sheepdog -M: Hitoshi Mitake M: Liu Yuan -M: Jeff Cody L: qemu-block@nongnu.org L: sheepdog@lists.wpkg.org -S: Supported +S: Odd Fixes F: block/sheepdog.c -T: git git://github.com/codyprime/qemu-kvm-jtc.git block VHDX -M: Jeff Cody +M: Jeff Cody L: qemu-block@nongnu.org S: Supported F: block/vhdx* -T: git git://github.com/codyprime/qemu-kvm-jtc.git block VDI M: Stefan Weil @@ -1982,13 +2350,12 @@ M: Ronnie Sahlberg M: Paolo Bonzini M: Peter Lieven L: qemu-block@nongnu.org -S: Supported +S: Odd Fixes F: block/iscsi.c F: block/iscsi-opts.c Network Block Device (NBD) M: Eric Blake -M: Paolo Bonzini L: qemu-block@nongnu.org S: Maintained F: block/nbd* @@ -1997,46 +2364,38 @@ F: include/block/nbd* F: qemu-nbd.* F: blockdev-nbd.c F: docs/interop/nbd.txt -T: git git://repo.or.cz/qemu/ericb.git nbd +T: git https://repo.or.cz/qemu/ericb.git nbd NFS -M: Jeff Cody M: Peter Lieven L: qemu-block@nongnu.org S: Maintained F: block/nfs.c -T: git git://github.com/codyprime/qemu-kvm-jtc.git block SSH M: Richard W.M. Jones -M: Jeff Cody L: qemu-block@nongnu.org S: Supported F: block/ssh.c -T: git git://github.com/codyprime/qemu-kvm-jtc.git block CURL -M: Jeff Cody L: qemu-block@nongnu.org S: Supported F: block/curl.c -T: git git://github.com/codyprime/qemu-kvm-jtc.git block GLUSTER -M: Jeff Cody L: qemu-block@nongnu.org S: Supported F: block/gluster.c -T: git git://github.com/codyprime/qemu-kvm-jtc.git block Null Block Driver -M: Fam Zheng +M: Fam Zheng L: qemu-block@nongnu.org S: Supported F: block/null.c NVMe Block Driver -M: Fam Zheng +M: Fam Zheng L: qemu-block@nongnu.org S: Supported F: block/nvme* @@ -2162,13 +2521,15 @@ S: Maintained F: hw/rdma/* F: hw/rdma/vmw/* F: docs/pvrdma.txt +F: contrib/rdmacm-mux/* +F: qapi/rdma.json Build and test automation ------------------------- Build and test automation M: Alex Bennée -M: Fam Zheng -R: Philippe Mathieu-Daudé +M: Fam Zheng +R: Philippe Mathieu-Daudé L: qemu-devel@nongnu.org S: Maintained F: .travis.yml @@ -2176,13 +2537,28 @@ F: scripts/travis/ F: .shippable.yml F: tests/docker/ F: tests/vm/ +F: scripts/archive-source.sh W: https://travis-ci.org/qemu/qemu W: https://app.shippable.com/github/qemu/qemu W: http://patchew.org/QEMU/ +FreeBSD Hosted Continuous Integration +M: Ed Maste +M: Li-Wen Hsu +L: qemu-devel@nongnu.org +S: Maintained +F: .cirrus.yml +W: https://cirrus-ci.com/github/qemu/qemu + +GitLab Continuous Integration +M: Thomas Huth +S: Maintained +F: .gitlab-ci.yml + Guest Test Compilation Support M: Alex Bennée R: Philippe Mathieu-Daudé +S: Maintained F: tests/tcg/Makefile F: tests/tcg/Makefile.include L: qemu-devel@nongnu.org @@ -2194,6 +2570,12 @@ M: Daniel P. Berrange S: Odd Fixes F: docs/devel/build-system.txt +GIT Data Mining Config +M: Alex Bennée +S: Odd Fixes +F: gitdm.config +F: contrib/gitdm/* + Incompatible changes R: libvir-list@redhat.com F: qemu-deprecated.texi @@ -2204,3 +2586,9 @@ GIT submodules M: Daniel P. Berrange S: Odd Fixes F: scripts/git-submodule.sh + +Sphinx documentation configuration and build machinery +M: Peter Maydell +S: Maintained +F: docs/conf.py +F: docs/*/conf.py diff --git a/Makefile b/Makefile index 16b27f79dd..04a0d45050 100644 --- a/Makefile +++ b/Makefile @@ -67,7 +67,7 @@ CONFIG_ALL=y -include config-all-devices.mak -include config-all-disas.mak -config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios +config-host.mak: $(SRC_PATH)/configure $(SRC_PATH)/pc-bios $(SRC_PATH)/VERSION @echo $@ is out-of-date, running configure @# TODO: The next lines include code which supports a smooth @# transition from old configurations without config.status. @@ -87,83 +87,41 @@ endif include $(SRC_PATH)/rules.mak +# Create QEMU_PKGVERSION and FULL_VERSION strings +# If PKGVERSION is set, use that; otherwise get version and -dirty status from git +QEMU_PKGVERSION := $(if $(PKGVERSION),$(PKGVERSION),$(shell \ + cd $(SRC_PATH); \ + if test -e .git; then \ + git describe --match 'v*' 2>/dev/null | tr -d '\n'; \ + if ! git diff-index --quiet HEAD &>/dev/null; then \ + echo "-dirty"; \ + fi; \ + fi)) + +# Either "version (pkgversion)", or just "version" if pkgversion not set +FULL_VERSION := $(if $(QEMU_PKGVERSION),$(VERSION) ($(QEMU_PKGVERSION)),$(VERSION)) + GENERATED_FILES = qemu-version.h config-host.h qemu-options.def -GENERATED_FILES += qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c -GENERATED_FILES += qapi/qapi-types.h qapi/qapi-types.c -GENERATED_FILES += qapi/qapi-types-block-core.h qapi/qapi-types-block-core.c -GENERATED_FILES += qapi/qapi-types-block.h qapi/qapi-types-block.c -GENERATED_FILES += qapi/qapi-types-char.h qapi/qapi-types-char.c -GENERATED_FILES += qapi/qapi-types-common.h qapi/qapi-types-common.c -GENERATED_FILES += qapi/qapi-types-crypto.h qapi/qapi-types-crypto.c -GENERATED_FILES += qapi/qapi-types-introspect.h qapi/qapi-types-introspect.c -GENERATED_FILES += qapi/qapi-types-job.h qapi/qapi-types-job.c -GENERATED_FILES += qapi/qapi-types-migration.h qapi/qapi-types-migration.c -GENERATED_FILES += qapi/qapi-types-misc.h qapi/qapi-types-misc.c -GENERATED_FILES += qapi/qapi-types-net.h qapi/qapi-types-net.c -GENERATED_FILES += qapi/qapi-types-rocker.h qapi/qapi-types-rocker.c -GENERATED_FILES += qapi/qapi-types-run-state.h qapi/qapi-types-run-state.c -GENERATED_FILES += qapi/qapi-types-sockets.h qapi/qapi-types-sockets.c -GENERATED_FILES += qapi/qapi-types-tpm.h qapi/qapi-types-tpm.c -GENERATED_FILES += qapi/qapi-types-trace.h qapi/qapi-types-trace.c -GENERATED_FILES += qapi/qapi-types-transaction.h qapi/qapi-types-transaction.c -GENERATED_FILES += qapi/qapi-types-ui.h qapi/qapi-types-ui.c -GENERATED_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c -GENERATED_FILES += qapi/qapi-visit.h qapi/qapi-visit.c -GENERATED_FILES += qapi/qapi-visit-block-core.h qapi/qapi-visit-block-core.c -GENERATED_FILES += qapi/qapi-visit-block.h qapi/qapi-visit-block.c -GENERATED_FILES += qapi/qapi-visit-char.h qapi/qapi-visit-char.c -GENERATED_FILES += qapi/qapi-visit-common.h qapi/qapi-visit-common.c -GENERATED_FILES += qapi/qapi-visit-crypto.h qapi/qapi-visit-crypto.c -GENERATED_FILES += qapi/qapi-visit-introspect.h qapi/qapi-visit-introspect.c -GENERATED_FILES += qapi/qapi-visit-job.h qapi/qapi-visit-job.c -GENERATED_FILES += qapi/qapi-visit-migration.h qapi/qapi-visit-migration.c -GENERATED_FILES += qapi/qapi-visit-misc.h qapi/qapi-visit-misc.c -GENERATED_FILES += qapi/qapi-visit-net.h qapi/qapi-visit-net.c -GENERATED_FILES += qapi/qapi-visit-rocker.h qapi/qapi-visit-rocker.c -GENERATED_FILES += qapi/qapi-visit-run-state.h qapi/qapi-visit-run-state.c -GENERATED_FILES += qapi/qapi-visit-sockets.h qapi/qapi-visit-sockets.c -GENERATED_FILES += qapi/qapi-visit-tpm.h qapi/qapi-visit-tpm.c -GENERATED_FILES += qapi/qapi-visit-trace.h qapi/qapi-visit-trace.c -GENERATED_FILES += qapi/qapi-visit-transaction.h qapi/qapi-visit-transaction.c -GENERATED_FILES += qapi/qapi-visit-ui.h qapi/qapi-visit-ui.c -GENERATED_FILES += qapi/qapi-commands.h qapi/qapi-commands.c -GENERATED_FILES += qapi/qapi-commands-block-core.h qapi/qapi-commands-block-core.c -GENERATED_FILES += qapi/qapi-commands-block.h qapi/qapi-commands-block.c -GENERATED_FILES += qapi/qapi-commands-char.h qapi/qapi-commands-char.c -GENERATED_FILES += qapi/qapi-commands-common.h qapi/qapi-commands-common.c -GENERATED_FILES += qapi/qapi-commands-crypto.h qapi/qapi-commands-crypto.c -GENERATED_FILES += qapi/qapi-commands-introspect.h qapi/qapi-commands-introspect.c -GENERATED_FILES += qapi/qapi-commands-job.h qapi/qapi-commands-job.c -GENERATED_FILES += qapi/qapi-commands-migration.h qapi/qapi-commands-migration.c -GENERATED_FILES += qapi/qapi-commands-misc.h qapi/qapi-commands-misc.c -GENERATED_FILES += qapi/qapi-commands-net.h qapi/qapi-commands-net.c -GENERATED_FILES += qapi/qapi-commands-rocker.h qapi/qapi-commands-rocker.c -GENERATED_FILES += qapi/qapi-commands-run-state.h qapi/qapi-commands-run-state.c -GENERATED_FILES += qapi/qapi-commands-sockets.h qapi/qapi-commands-sockets.c -GENERATED_FILES += qapi/qapi-commands-tpm.h qapi/qapi-commands-tpm.c -GENERATED_FILES += qapi/qapi-commands-trace.h qapi/qapi-commands-trace.c -GENERATED_FILES += qapi/qapi-commands-transaction.h qapi/qapi-commands-transaction.c -GENERATED_FILES += qapi/qapi-commands-ui.h qapi/qapi-commands-ui.c -GENERATED_FILES += qapi/qapi-events.h qapi/qapi-events.c -GENERATED_FILES += qapi/qapi-events-block-core.h qapi/qapi-events-block-core.c -GENERATED_FILES += qapi/qapi-events-block.h qapi/qapi-events-block.c -GENERATED_FILES += qapi/qapi-events-char.h qapi/qapi-events-char.c -GENERATED_FILES += qapi/qapi-events-common.h qapi/qapi-events-common.c -GENERATED_FILES += qapi/qapi-events-crypto.h qapi/qapi-events-crypto.c -GENERATED_FILES += qapi/qapi-events-introspect.h qapi/qapi-events-introspect.c -GENERATED_FILES += qapi/qapi-events-job.h qapi/qapi-events-job.c -GENERATED_FILES += qapi/qapi-events-migration.h qapi/qapi-events-migration.c -GENERATED_FILES += qapi/qapi-events-misc.h qapi/qapi-events-misc.c -GENERATED_FILES += qapi/qapi-events-net.h qapi/qapi-events-net.c -GENERATED_FILES += qapi/qapi-events-rocker.h qapi/qapi-events-rocker.c -GENERATED_FILES += qapi/qapi-events-run-state.h qapi/qapi-events-run-state.c -GENERATED_FILES += qapi/qapi-events-sockets.h qapi/qapi-events-sockets.c -GENERATED_FILES += qapi/qapi-events-tpm.h qapi/qapi-events-tpm.c -GENERATED_FILES += qapi/qapi-events-trace.h qapi/qapi-events-trace.c -GENERATED_FILES += qapi/qapi-events-transaction.h qapi/qapi-events-transaction.c -GENERATED_FILES += qapi/qapi-events-ui.h qapi/qapi-events-ui.c -GENERATED_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h -GENERATED_FILES += qapi/qapi-doc.texi + +GENERATED_QAPI_FILES = qapi/qapi-builtin-types.h qapi/qapi-builtin-types.c +GENERATED_QAPI_FILES += qapi/qapi-types.h qapi/qapi-types.c +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.h) +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-types-%.c) +GENERATED_QAPI_FILES += qapi/qapi-builtin-visit.h qapi/qapi-builtin-visit.c +GENERATED_QAPI_FILES += qapi/qapi-visit.h qapi/qapi-visit.c +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.h) +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-visit-%.c) +GENERATED_QAPI_FILES += qapi/qapi-commands.h qapi/qapi-commands.c +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.h) +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-commands-%.c) +GENERATED_QAPI_FILES += qapi/qapi-emit-events.h qapi/qapi-emit-events.c +GENERATED_QAPI_FILES += qapi/qapi-events.h qapi/qapi-events.c +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.h) +GENERATED_QAPI_FILES += $(QAPI_MODULES:%=qapi/qapi-events-%.c) +GENERATED_QAPI_FILES += qapi/qapi-introspect.c qapi/qapi-introspect.h +GENERATED_QAPI_FILES += qapi/qapi-doc.texi + +GENERATED_FILES += $(GENERATED_QAPI_FILES) GENERATED_FILES += trace/generated-tcg-tracers.h @@ -201,7 +159,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") %/trace.h: %/trace.h-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) +%/trace.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=$(call trace-group-name,$@) \ --format=h \ @@ -210,7 +168,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") %/trace.c: %/trace.c-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) +%/trace.c-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=$(call trace-group-name,$@) \ --format=c \ @@ -219,7 +177,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") %/trace-ust.h: %/trace-ust.h-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) +%/trace-ust.h-timestamp: $(SRC_PATH)/%/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=$(call trace-group-name,$@) \ --format=ust-events-h \ @@ -243,7 +201,7 @@ tracetool-y += $(shell find $(SRC_PATH)/scripts/tracetool -name "*.py") trace-root.h: trace-root.h-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) +trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=root \ --format=h \ @@ -252,7 +210,7 @@ trace-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) trace-root.c: trace-root.c-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) +trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=root \ --format=c \ @@ -261,7 +219,7 @@ trace-root.c-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) trace-ust-root.h: trace-ust-root.h-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) +trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=root \ --format=ust-events-h \ @@ -270,7 +228,7 @@ trace-ust-root.h-timestamp: $(SRC_PATH)/trace-events $(tracetool-y) trace-ust-all.h: trace-ust-all.h-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) +trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=all \ --format=ust-events-h \ @@ -279,7 +237,7 @@ trace-ust-all.h-timestamp: $(trace-events-files) $(tracetool-y) trace-ust-all.c: trace-ust-all.c-timestamp @cmp $< $@ >/dev/null 2>&1 || cp $< $@ -trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) +trace-ust-all.c-timestamp: $(trace-events-files) $(tracetool-y) $(BUILD_DIR)/config-host.mak $(call quiet-command,$(TRACETOOL) \ --group=all \ --format=ust-events-c \ @@ -357,22 +315,26 @@ DOCS=qemu-doc.html qemu-doc.txt qemu.1 qemu-img.1 qemu-nbd.8 qemu-ga.8 DOCS+=docs/interop/qemu-qmp-ref.html docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7 DOCS+=docs/interop/qemu-ga-ref.html docs/interop/qemu-ga-ref.txt docs/interop/qemu-ga-ref.7 DOCS+=docs/qemu-block-drivers.7 +DOCS+=docs/qemu-cpu-models.7 ifdef CONFIG_VIRTFS DOCS+=fsdev/virtfs-proxy-helper.1 endif +ifdef CONFIG_TRACE_SYSTEMTAP +DOCS+=scripts/qemu-trace-stap.1 +endif else DOCS= endif SUBDIR_MAKEFLAGS=$(if $(V),,--no-print-directory --quiet) BUILD_DIR=$(BUILD_DIR) -SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(TARGET_DIRS)) -SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %-config-devices.mak.d, $(TARGET_DIRS)) +SUBDIR_DEVICES_MAK=$(patsubst %, %/config-devices.mak, $(filter %-softmmu, $(TARGET_DIRS))) +SUBDIR_DEVICES_MAK_DEP=$(patsubst %, %.d, $(SUBDIR_DEVICES_MAK)) ifeq ($(SUBDIR_DEVICES_MAK),) -config-all-devices.mak: +config-all-devices.mak: config-host.mak $(call quiet-command,echo '# no devices' > $@,"GEN","$@") else -config-all-devices.mak: $(SUBDIR_DEVICES_MAK) +config-all-devices.mak: $(SUBDIR_DEVICES_MAK) config-host.mak $(call quiet-command, sed -n \ 's|^\([^=]*\)=\(.*\)$$|\1:=$$(findstring y,$$(\1)\2)|p' \ $(SUBDIR_DEVICES_MAK) | sort -u > $@, \ @@ -381,9 +343,27 @@ endif -include $(SUBDIR_DEVICES_MAK_DEP) -%/config-devices.mak: default-configs/%.mak $(SRC_PATH)/scripts/make_device_config.sh - $(call quiet-command, \ - $(SHELL) $(SRC_PATH)/scripts/make_device_config.sh $< $*-config-devices.mak.d $@ > $@.tmp,"GEN","$@.tmp") +# This has to be kept in sync with Kconfig.host. +MINIKCONF_ARGS = \ + $(CONFIG_MINIKCONF_MODE) \ + $@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \ + CONFIG_KVM=$(CONFIG_KVM) \ + CONFIG_SPICE=$(CONFIG_SPICE) \ + CONFIG_IVSHMEM=$(CONFIG_IVSHMEM) \ + CONFIG_TPM=$(CONFIG_TPM) \ + CONFIG_XEN=$(CONFIG_XEN) \ + CONFIG_OPENGL=$(CONFIG_OPENGL) \ + CONFIG_X11=$(CONFIG_X11) \ + CONFIG_VHOST_USER=$(CONFIG_VHOST_USER) \ + CONFIG_VIRTFS=$(CONFIG_VIRTFS) \ + CONFIG_LINUX=$(CONFIG_LINUX) \ + CONFIG_PVRDMA=$(CONFIG_PVRDMA) + +MINIKCONF_INPUTS = $(SRC_PATH)/Kconfig.host $(SRC_PATH)/hw/Kconfig +MINIKCONF = $(PYTHON) $(SRC_PATH)/scripts/minikconf.py \ + +$(SUBDIR_DEVICES_MAK): %/config-devices.mak: default-configs/%.mak $(MINIKCONF_INPUTS) $(BUILD_DIR)/config-host.mak + $(call quiet-command, $(MINIKCONF) $(MINIKCONF_ARGS) > $@.tmp, "GEN", "$@.tmp") $(call quiet-command, if test -f $@; then \ if cmp -s $@.old $@; then \ mv $@.tmp $@; \ @@ -411,11 +391,14 @@ endif dummy := $(call unnest-vars,, \ stub-obj-y \ + authz-obj-y \ chardev-obj-y \ util-obj-y \ qga-obj-y \ + elf2dmp-obj-y \ ivshmem-client-obj-y \ ivshmem-server-obj-y \ + rdmacm-mux-obj-y \ libvhost-user-obj-y \ vhost-user-scsi-obj-y \ vhost-user-blk-obj-y \ @@ -436,27 +419,12 @@ dummy := $(call unnest-vars,, \ include $(SRC_PATH)/tests/Makefile.include -all: $(DOCS) $(TOOLS) $(HELPERS-y) recurse-all modules +all: $(DOCS) $(if $(BUILD_DOCS),sphinxdocs) $(TOOLS) $(HELPERS-y) recurse-all modules qemu-version.h: FORCE $(call quiet-command, \ - (cd $(SRC_PATH); \ - if test -n "$(PKGVERSION)"; then \ - pkgvers="$(PKGVERSION)"; \ - else \ - if test -d .git; then \ - pkgvers=$$(git rev-parse --short HEAD 2>/dev/null | tr -d '\n');\ - if ! git diff --quiet HEAD >/dev/null 2>&1; then \ - pkgvers="$${pkgvers}-dirty"; \ - fi; \ - fi; \ - fi; \ - printf "#define QEMU_PKGVERSION \"$${pkgvers}\"\n"; \ - if test -n "$${pkgvers}"; then \ - printf '#define QEMU_FULL_VERSION QEMU_VERSION " (" QEMU_PKGVERSION ")"\n'; \ - else \ - printf '#define QEMU_FULL_VERSION QEMU_VERSION\n'; \ - fi; \ + (printf '#define QEMU_PKGVERSION "$(QEMU_PKGVERSION)"\n'; \ + printf '#define QEMU_FULL_VERSION "$(FULL_VERSION)"\n'; \ ) > $@.tmp) $(call quiet-command, if ! cmp -s $@ $@.tmp; then \ mv $@.tmp $@; \ @@ -472,6 +440,7 @@ qemu-options.def: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool SUBDIR_RULES=$(patsubst %,subdir-%, $(TARGET_DIRS)) SOFTMMU_SUBDIR_RULES=$(filter %-softmmu,$(SUBDIR_RULES)) +$(SOFTMMU_SUBDIR_RULES): $(authz-obj-y) $(SOFTMMU_SUBDIR_RULES): $(block-obj-y) $(SOFTMMU_SUBDIR_RULES): $(crypto-obj-y) $(SOFTMMU_SUBDIR_RULES): $(io-obj-y) @@ -505,6 +474,9 @@ CAP_CFLAGS += -DCAPSTONE_HAS_X86 subdir-capstone: .git-submodule-status $(call quiet-command,$(MAKE) -C $(SRC_PATH)/capstone CAPSTONE_SHARED=no BUILDDIR="$(BUILD_DIR)/capstone" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(CAP_CFLAGS)" $(SUBDIR_MAKEFLAGS) $(BUILD_DIR)/capstone/$(LIBCAPSTONE)) +subdir-slirp: .git-submodule-status + $(call quiet-command,$(MAKE) -C $(SRC_PATH)/slirp BUILD_DIR="$(BUILD_DIR)/slirp" CC="$(CC)" AR="$(AR)" LD="$(LD)" RANLIB="$(RANLIB)" CFLAGS="$(QEMU_CFLAGS)") + $(SUBDIR_RULES): libqemuutil.a $(common-obj-y) $(chardev-obj-y) \ $(qom-obj-y) $(crypto-aes-obj-$(CONFIG_USER_ONLY)) @@ -526,7 +498,7 @@ Makefile: $(version-obj-y) # Build libraries libqemuutil.a: $(util-obj-y) $(trace-obj-y) $(stub-obj-y) -libvhost-user.a: $(libvhost-user-obj-y) +libvhost-user.a: $(libvhost-user-obj-y) $(util-obj-y) $(stub-obj-y) ###################################################################### @@ -534,18 +506,20 @@ COMMON_LDADDS = libqemuutil.a qemu-img.o: qemu-img-cmds.h -qemu-img$(EXESUF): qemu-img.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) -qemu-nbd$(EXESUF): qemu-nbd.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) -qemu-io$(EXESUF): qemu-io.o $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +qemu-img$(EXESUF): qemu-img.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +qemu-nbd$(EXESUF): qemu-nbd.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +qemu-io$(EXESUF): qemu-io.o $(authz-obj-y) $(block-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) qemu-bridge-helper$(EXESUF): qemu-bridge-helper.o $(COMMON_LDADDS) qemu-keymap$(EXESUF): qemu-keymap.o ui/input-keymap.o $(COMMON_LDADDS) +qemu-edid$(EXESUF): qemu-edid.o hw/display/edid-generate.o $(COMMON_LDADDS) + fsdev/virtfs-proxy-helper$(EXESUF): fsdev/virtfs-proxy-helper.o fsdev/9p-marshal.o fsdev/9p-iov-marshal.o $(COMMON_LDADDS) fsdev/virtfs-proxy-helper$(EXESUF): LIBS += -lcap -scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) +scsi/qemu-pr-helper$(EXESUF): scsi/qemu-pr-helper.o scsi/utils.o $(authz-obj-y) $(crypto-obj-y) $(io-obj-y) $(qom-obj-y) $(COMMON_LDADDS) ifdef CONFIG_MPATH scsi/qemu-pr-helper$(EXESUF): LIBS += -ludev -lmultipath -lmpathpersist endif @@ -579,100 +553,10 @@ qga/qapi-generated/qapi-gen-timestamp: $(SRC_PATH)/qga/qapi-schema.json $(qapi-p "GEN","$(@:%-timestamp=%)") @>$@ -qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json $(SRC_PATH)/qapi/common.json \ - $(SRC_PATH)/qapi/block.json $(SRC_PATH)/qapi/block-core.json \ - $(SRC_PATH)/qapi/char.json \ - $(SRC_PATH)/qapi/crypto.json \ - $(SRC_PATH)/qapi/introspect.json \ - $(SRC_PATH)/qapi/job.json \ - $(SRC_PATH)/qapi/migration.json \ - $(SRC_PATH)/qapi/misc.json \ - $(SRC_PATH)/qapi/net.json \ - $(SRC_PATH)/qapi/rocker.json \ - $(SRC_PATH)/qapi/run-state.json \ - $(SRC_PATH)/qapi/sockets.json \ - $(SRC_PATH)/qapi/tpm.json \ - $(SRC_PATH)/qapi/trace.json \ - $(SRC_PATH)/qapi/transaction.json \ - $(SRC_PATH)/qapi/ui.json +qapi-modules = $(SRC_PATH)/qapi/qapi-schema.json \ + $(QAPI_MODULES:%=$(SRC_PATH)/qapi/%.json) -qapi/qapi-builtin-types.c qapi/qapi-builtin-types.h \ -qapi/qapi-types.c qapi/qapi-types.h \ -qapi/qapi-types-block-core.c qapi/qapi-types-block-core.h \ -qapi/qapi-types-block.c qapi/qapi-types-block.h \ -qapi/qapi-types-char.c qapi/qapi-types-char.h \ -qapi/qapi-types-common.c qapi/qapi-types-common.h \ -qapi/qapi-types-crypto.c qapi/qapi-types-crypto.h \ -qapi/qapi-types-introspect.c qapi/qapi-types-introspect.h \ -qapi/qapi-types-job.c qapi/qapi-types-job.h \ -qapi/qapi-types-migration.c qapi/qapi-types-migration.h \ -qapi/qapi-types-misc.c qapi/qapi-types-misc.h \ -qapi/qapi-types-net.c qapi/qapi-types-net.h \ -qapi/qapi-types-rocker.c qapi/qapi-types-rocker.h \ -qapi/qapi-types-run-state.c qapi/qapi-types-run-state.h \ -qapi/qapi-types-sockets.c qapi/qapi-types-sockets.h \ -qapi/qapi-types-tpm.c qapi/qapi-types-tpm.h \ -qapi/qapi-types-trace.c qapi/qapi-types-trace.h \ -qapi/qapi-types-transaction.c qapi/qapi-types-transaction.h \ -qapi/qapi-types-ui.c qapi/qapi-types-ui.h \ -qapi/qapi-builtin-visit.c qapi/qapi-builtin-visit.h \ -qapi/qapi-visit.c qapi/qapi-visit.h \ -qapi/qapi-visit-block-core.c qapi/qapi-visit-block-core.h \ -qapi/qapi-visit-block.c qapi/qapi-visit-block.h \ -qapi/qapi-visit-char.c qapi/qapi-visit-char.h \ -qapi/qapi-visit-common.c qapi/qapi-visit-common.h \ -qapi/qapi-visit-crypto.c qapi/qapi-visit-crypto.h \ -qapi/qapi-visit-introspect.c qapi/qapi-visit-introspect.h \ -qapi/qapi-visit-job.c qapi/qapi-visit-job.h \ -qapi/qapi-visit-migration.c qapi/qapi-visit-migration.h \ -qapi/qapi-visit-misc.c qapi/qapi-visit-misc.h \ -qapi/qapi-visit-net.c qapi/qapi-visit-net.h \ -qapi/qapi-visit-rocker.c qapi/qapi-visit-rocker.h \ -qapi/qapi-visit-run-state.c qapi/qapi-visit-run-state.h \ -qapi/qapi-visit-sockets.c qapi/qapi-visit-sockets.h \ -qapi/qapi-visit-tpm.c qapi/qapi-visit-tpm.h \ -qapi/qapi-visit-trace.c qapi/qapi-visit-trace.h \ -qapi/qapi-visit-transaction.c qapi/qapi-visit-transaction.h \ -qapi/qapi-visit-ui.c qapi/qapi-visit-ui.h \ -qapi/qapi-commands.h qapi/qapi-commands.c \ -qapi/qapi-commands-block-core.c qapi/qapi-commands-block-core.h \ -qapi/qapi-commands-block.c qapi/qapi-commands-block.h \ -qapi/qapi-commands-char.c qapi/qapi-commands-char.h \ -qapi/qapi-commands-common.c qapi/qapi-commands-common.h \ -qapi/qapi-commands-crypto.c qapi/qapi-commands-crypto.h \ -qapi/qapi-commands-introspect.c qapi/qapi-commands-introspect.h \ -qapi/qapi-commands-job.c qapi/qapi-commands-job.h \ -qapi/qapi-commands-migration.c qapi/qapi-commands-migration.h \ -qapi/qapi-commands-misc.c qapi/qapi-commands-misc.h \ -qapi/qapi-commands-net.c qapi/qapi-commands-net.h \ -qapi/qapi-commands-rocker.c qapi/qapi-commands-rocker.h \ -qapi/qapi-commands-run-state.c qapi/qapi-commands-run-state.h \ -qapi/qapi-commands-sockets.c qapi/qapi-commands-sockets.h \ -qapi/qapi-commands-tpm.c qapi/qapi-commands-tpm.h \ -qapi/qapi-commands-trace.c qapi/qapi-commands-trace.h \ -qapi/qapi-commands-transaction.c qapi/qapi-commands-transaction.h \ -qapi/qapi-commands-ui.c qapi/qapi-commands-ui.h \ -qapi/qapi-events.c qapi/qapi-events.h \ -qapi/qapi-events-block-core.c qapi/qapi-events-block-core.h \ -qapi/qapi-events-block.c qapi/qapi-events-block.h \ -qapi/qapi-events-char.c qapi/qapi-events-char.h \ -qapi/qapi-events-common.c qapi/qapi-events-common.h \ -qapi/qapi-events-crypto.c qapi/qapi-events-crypto.h \ -qapi/qapi-events-introspect.c qapi/qapi-events-introspect.h \ -qapi/qapi-events-job.c qapi/qapi-events-job.h \ -qapi/qapi-events-migration.c qapi/qapi-events-migration.h \ -qapi/qapi-events-misc.c qapi/qapi-events-misc.h \ -qapi/qapi-events-net.c qapi/qapi-events-net.h \ -qapi/qapi-events-rocker.c qapi/qapi-events-rocker.h \ -qapi/qapi-events-run-state.c qapi/qapi-events-run-state.h \ -qapi/qapi-events-sockets.c qapi/qapi-events-sockets.h \ -qapi/qapi-events-tpm.c qapi/qapi-events-tpm.h \ -qapi/qapi-events-trace.c qapi/qapi-events-trace.h \ -qapi/qapi-events-transaction.c qapi/qapi-events-transaction.h \ -qapi/qapi-events-ui.c qapi/qapi-events-ui.h \ -qapi/qapi-introspect.h qapi/qapi-introspect.c \ -qapi/qapi-doc.texi: \ -qapi-gen-timestamp ; +$(GENERATED_QAPI_FILES): qapi-gen-timestamp ; qapi-gen-timestamp: $(qapi-modules) $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o "qapi" -b $<, \ @@ -707,6 +591,10 @@ ifneq ($(EXESUF),) qemu-ga: qemu-ga$(EXESUF) $(QGA_VSS_PROVIDER) $(QEMU_GA_MSI) endif +elf2dmp$(EXESUF): LIBS += $(CURL_LIBS) +elf2dmp$(EXESUF): $(elf2dmp-obj-y) + $(call LINK, $^) + ifdef CONFIG_IVSHMEM ivshmem-client$(EXESUF): $(ivshmem-client-obj-y) $(COMMON_LDADDS) $(call LINK, $^) @@ -718,6 +606,10 @@ vhost-user-scsi$(EXESUF): $(vhost-user-scsi-obj-y) libvhost-user.a vhost-user-blk$(EXESUF): $(vhost-user-blk-obj-y) libvhost-user.a $(call LINK, $^) +rdmacm-mux$(EXESUF): LIBS += "-libumad" +rdmacm-mux$(EXESUF): $(rdmacm-mux-obj-y) $(COMMON_LDADDS) + $(call LINK, $^) + module_block.h: $(SRC_PATH)/scripts/modules/module_block.py config-host.mak $(call quiet-command,$(PYTHON) $< $@ \ $(addprefix $(SRC_PATH)/,$(patsubst %.mo,%.c,$(block-obj-m))), \ @@ -736,7 +628,11 @@ clean: rm -f config.mak op-i386.h opc-i386.h gen-op-i386.h op-arm.h opc-arm.h gen-op-arm.h rm -f qemu-options.def rm -f *.msi - find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f -exec rm {} + + find . \( -name '*.so' -o -name '*.dll' -o -name '*.mo' -o -name '*.[oda]' \) -type f \ + ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-aarch64.a \ + ! -path ./roms/edk2/ArmPkg/Library/GccLto/liblto-arm.a \ + ! -path ./roms/edk2/BaseTools/Source/Python/UPT/Dll/sqlite3.dll \ + -exec rm {} + rm -f $(filter-out %.tlb,$(TOOLS)) $(HELPERS-y) qemu-ga TAGS cscope.* *.pod *~ */*~ rm -f fsdev/*.pod scsi/*.pod rm -f qemu-img-cmds.h @@ -751,7 +647,7 @@ clean: if test -d $$d; then $(MAKE) -C $$d $@ || exit 1; fi; \ rm -f $$d/qemu-options.def; \ done - rm -f $(SUBDIR_DEVICES_MAK) config-all-devices.mak + rm -f config-all-devices.mak VERSION ?= $(shell cat VERSION) @@ -760,9 +656,26 @@ dist: qemu-$(VERSION).tar.bz2 qemu-%.tar.bz2: $(SRC_PATH)/scripts/make-release "$(SRC_PATH)" "$(patsubst qemu-%.tar.bz2,%,$@)" +# Sphinx does not allow building manuals into the same directory as +# the source files, so if we're doing an in-tree QEMU build we must +# build the manuals into a subdirectory (and then install them from +# there for 'make install'). For an out-of-tree build we can just +# use the docs/ subdirectory in the build tree as normal. +ifeq ($(realpath $(SRC_PATH)),$(realpath .)) +MANUAL_BUILDDIR := docs/built +else +MANUAL_BUILDDIR := docs +endif + +define clean-manual = +rm -rf $(MANUAL_BUILDDIR)/$1/_static +rm -f $(MANUAL_BUILDDIR)/$1/objects.inv $(MANUAL_BUILDDIR)/$1/searchindex.js $(MANUAL_BUILDDIR)/$1/*.html +endef + distclean: clean 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 $(SUBDIR_DEVICES_MAK) rm -f po/*.mo tests/qemu-iotests/common.env rm -f roms/seabios/config.mak roms/vgabios/config.mak rm -f qemu-doc.info qemu-doc.aux qemu-doc.cp qemu-doc.cps @@ -778,29 +691,33 @@ distclean: clean rm -f docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf rm -f docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html rm -f docs/qemu-block-drivers.7 + rm -f docs/qemu-cpu-models.7 + rm -rf .doctrees + $(call clean-manual,devel) + $(call clean-manual,interop) for d in $(TARGET_DIRS); do \ rm -rf $$d || exit 1 ; \ done rm -Rf .sdk if test -f dtc/version_gen.h; then $(MAKE) $(DTC_MAKE_ARGS) clean; fi -KEYMAPS=da en-gb et fr fr-ch is lt modifiers no pt-br sv \ +KEYMAPS=da en-gb et fr fr-ch is lt no pt-br sv \ ar de en-us fi fr-be hr it lv nl pl ru th \ -common de-ch es fo fr-ca hu ja mk nl-be pt sl tr \ +de-ch es fo fr-ca hu ja mk pt sl tr \ 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 \ +vgabios-ramfb.bin vgabios-bochs-display.bin \ 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 \ efi-e1000.rom efi-eepro100.rom efi-ne2k_pci.rom \ efi-pcnet.rom efi-rtl8139.rom efi-virtio.rom \ efi-e1000e.rom efi-vmxnet3.rom \ -qemu-icon.bmp qemu_logo_no_text.svg \ bamboo.dtb canyonlands.dtb petalogix-s3adsp1800.dtb petalogix-ml605.dtb \ -multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin \ +multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin \ s390-ccw.img s390-netboot.img \ spapr-rtas.bin slof.bin skiboot.lid \ palcode-clipper \ @@ -811,7 +728,20 @@ else BLOBS= endif -install-doc: $(DOCS) +# Note that we manually filter-out the non-Sphinx documentation which +# is currently built into the docs/interop directory in the build tree. +define install-manual = +for d in $$(cd $(MANUAL_BUILDDIR) && find $1 -type d); do $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)/$$d"; done +for f in $$(cd $(MANUAL_BUILDDIR) && find $1 -type f -a '!' '(' -name 'qemu-*-qapi.*' -o -name 'qemu-*-ref.*' ')' ); do $(INSTALL_DATA) "$(MANUAL_BUILDDIR)/$$f" "$(DESTDIR)$(qemu_docdir)/$$f"; done +endef + +# Note that we deliberately do not install the "devel" manual: it is +# for QEMU developers, and not interesting to our users. +.PHONY: install-sphinxdocs +install-sphinxdocs: sphinxdocs + $(call install-manual,interop) + +install-doc: $(DOCS) install-sphinxdocs $(INSTALL_DIR) "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) qemu-doc.html "$(DESTDIR)$(qemu_docdir)" $(INSTALL_DATA) qemu-doc.txt "$(DESTDIR)$(qemu_docdir)" @@ -823,11 +753,15 @@ ifdef CONFIG_POSIX $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man7" $(INSTALL_DATA) docs/interop/qemu-qmp-ref.7 "$(DESTDIR)$(mandir)/man7" $(INSTALL_DATA) docs/qemu-block-drivers.7 "$(DESTDIR)$(mandir)/man7" + $(INSTALL_DATA) docs/qemu-cpu-models.7 "$(DESTDIR)$(mandir)/man7" ifneq ($(TOOLS),) $(INSTALL_DATA) qemu-img.1 "$(DESTDIR)$(mandir)/man1" $(INSTALL_DIR) "$(DESTDIR)$(mandir)/man8" $(INSTALL_DATA) qemu-nbd.8 "$(DESTDIR)$(mandir)/man8" endif +ifdef CONFIG_TRACE_SYSTEMTAP + $(INSTALL_DATA) scripts/qemu-trace-stap.1 "$(DESTDIR)$(mandir)/man1" +endif ifneq (,$(findstring qemu-ga,$(TOOLS))) $(INSTALL_DATA) qemu-ga.8 "$(DESTDIR)$(mandir)/man8" $(INSTALL_DATA) docs/interop/qemu-ga-ref.html "$(DESTDIR)$(qemu_docdir)" @@ -850,6 +784,7 @@ ifneq (,$(findstring qemu-ga,$(TOOLS))) endif endif +ICON_SIZES=16x16 24x24 32x32 48x48 64x64 128x128 256x256 512x512 install: all $(if $(BUILD_DOCS),install-doc) install-datadir install-localstatedir ifneq ($(TOOLS),) @@ -866,11 +801,28 @@ endif ifneq ($(HELPERS-y),) $(call install-prog,$(HELPERS-y),$(DESTDIR)$(libexecdir)) endif +ifdef CONFIG_TRACE_SYSTEMTAP + $(INSTALL_PROG) "scripts/qemu-trace-stap" $(DESTDIR)$(bindir) +endif ifneq ($(BLOBS),) set -e; for x in $(BLOBS); do \ $(INSTALL_DATA) $(SRC_PATH)/pc-bios/$$x "$(DESTDIR)$(qemu_datadir)"; \ done endif + for s in $(ICON_SIZES); do \ + mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps"; \ + $(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_$${s}.png \ + "$(DESTDIR)/$(qemu_icondir)/hicolor/$${s}/apps/qemu.png"; \ + done; \ + mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps"; \ + $(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu_32x32.bmp \ + "$(DESTDIR)/$(qemu_icondir)/hicolor/32x32/apps/qemu.bmp"; \ + mkdir -p "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps"; \ + $(INSTALL_DATA) $(SRC_PATH)/ui/icons/qemu.svg \ + "$(DESTDIR)/$(qemu_icondir)/hicolor/scalable/apps/qemu.svg" + mkdir -p "$(DESTDIR)/$(qemu_desktopdir)" + $(INSTALL_DATA) $(SRC_PATH)/ui/qemu.desktop \ + "$(DESTDIR)/$(qemu_desktopdir)/qemu.desktop" ifdef CONFIG_GTK $(MAKE) -C po $@ endif @@ -940,6 +892,23 @@ docs/version.texi: $(SRC_PATH)/VERSION %.pdf: %.texi docs/version.texi $(call quiet-command,texi2pdf $(TEXI2PDFFLAGS) $< -o $@,"GEN","$@") +# Sphinx builds all its documentation at once in one invocation +# and handles "don't rebuild things unless necessary" itself. +# The '.doctrees' files are cached information to speed this up. +.PHONY: sphinxdocs +sphinxdocs: $(MANUAL_BUILDDIR)/devel/index.html $(MANUAL_BUILDDIR)/interop/index.html + +# Canned command to build a single manual +build-manual = $(call quiet-command,sphinx-build $(if $(V),,-q) -b html -D version=$(VERSION) -D release="$(FULL_VERSION)" -d .doctrees/$1 $(SRC_PATH)/docs/$1 $(MANUAL_BUILDDIR)/$1 ,"SPHINX","$(MANUAL_BUILDDIR)/$1") +# We assume all RST files in the manual's directory are used in it +manual-deps = $(wildcard $(SRC_PATH)/docs/$1/*.rst) $(SRC_PATH)/docs/$1/conf.py $(SRC_PATH)/docs/conf.py + +$(MANUAL_BUILDDIR)/devel/index.html: $(call manual-deps,devel) + $(call build-manual,devel) + +$(MANUAL_BUILDDIR)/interop/index.html: $(call manual-deps,interop) + $(call build-manual,interop) + qemu-options.texi: $(SRC_PATH)/qemu-options.hx $(SRC_PATH)/scripts/hxtool $(call quiet-command,sh $(SRC_PATH)/scripts/hxtool -t < $< > $@,"GEN","$@") @@ -965,16 +934,19 @@ fsdev/virtfs-proxy-helper.1: fsdev/virtfs-proxy-helper.texi qemu-nbd.8: qemu-nbd.texi qemu-option-trace.texi qemu-ga.8: qemu-ga.texi docs/qemu-block-drivers.7: docs/qemu-block-drivers.texi +docs/qemu-cpu-models.7: docs/qemu-cpu-models.texi +scripts/qemu-trace-stap.1: scripts/qemu-trace-stap.texi -html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html +html: qemu-doc.html docs/interop/qemu-qmp-ref.html docs/interop/qemu-ga-ref.html sphinxdocs info: qemu-doc.info docs/interop/qemu-qmp-ref.info docs/interop/qemu-ga-ref.info pdf: qemu-doc.pdf docs/interop/qemu-qmp-ref.pdf docs/interop/qemu-ga-ref.pdf txt: qemu-doc.txt docs/interop/qemu-qmp-ref.txt docs/interop/qemu-ga-ref.txt qemu-doc.html qemu-doc.info qemu-doc.pdf qemu-doc.txt: \ qemu-img.texi qemu-nbd.texi qemu-options.texi qemu-option-trace.texi \ - qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ - qemu-monitor-info.texi docs/qemu-block-drivers.texi + qemu-deprecated.texi qemu-monitor.texi qemu-img-cmds.texi qemu-ga.texi \ + qemu-monitor-info.texi docs/qemu-block-drivers.texi \ + docs/qemu-cpu-models.texi docs/interop/qemu-ga-ref.dvi docs/interop/qemu-ga-ref.html \ docs/interop/qemu-ga-ref.info docs/interop/qemu-ga-ref.pdf \ @@ -986,12 +958,15 @@ docs/interop/qemu-qmp-ref.dvi docs/interop/qemu-qmp-ref.html \ docs/interop/qemu-qmp-ref.txt docs/interop/qemu-qmp-ref.7: \ docs/interop/qemu-qmp-ref.texi docs/interop/qemu-qmp-qapi.texi +$(filter %.1 %.7 %.8,$(DOCS)): scripts/texi2pod.pl + # Reports/Analysis %/coverage-report.html: @mkdir -p $* $(call quiet-command,\ - gcovr -p --html --html-details -o $@, \ + gcovr -r $(SRC_PATH) --object-directory $(BUILD_PATH) \ + -p --html --html-details -o $@, \ "GEN", "coverage-report.html") .PHONY: coverage-report diff --git a/Makefile.objs b/Makefile.objs index 7a9828da28..cf065de5ed 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -1,71 +1,19 @@ ####################################################################### # Common libraries for tools and emulators -stub-obj-y = stubs/ crypto/ +stub-obj-y = stubs/ util/ crypto/ util-obj-y = util/ qobject/ qapi/ -util-obj-y += qapi/qapi-builtin-types.o -util-obj-y += qapi/qapi-types.o -util-obj-y += qapi/qapi-types-block-core.o -util-obj-y += qapi/qapi-types-block.o -util-obj-y += qapi/qapi-types-char.o -util-obj-y += qapi/qapi-types-common.o -util-obj-y += qapi/qapi-types-crypto.o -util-obj-y += qapi/qapi-types-introspect.o -util-obj-y += qapi/qapi-types-job.o -util-obj-y += qapi/qapi-types-migration.o -util-obj-y += qapi/qapi-types-misc.o -util-obj-y += qapi/qapi-types-net.o -util-obj-y += qapi/qapi-types-rocker.o -util-obj-y += qapi/qapi-types-run-state.o -util-obj-y += qapi/qapi-types-sockets.o -util-obj-y += qapi/qapi-types-tpm.o -util-obj-y += qapi/qapi-types-trace.o -util-obj-y += qapi/qapi-types-transaction.o -util-obj-y += qapi/qapi-types-ui.o -util-obj-y += qapi/qapi-builtin-visit.o -util-obj-y += qapi/qapi-visit.o -util-obj-y += qapi/qapi-visit-block-core.o -util-obj-y += qapi/qapi-visit-block.o -util-obj-y += qapi/qapi-visit-char.o -util-obj-y += qapi/qapi-visit-common.o -util-obj-y += qapi/qapi-visit-crypto.o -util-obj-y += qapi/qapi-visit-introspect.o -util-obj-y += qapi/qapi-visit-job.o -util-obj-y += qapi/qapi-visit-migration.o -util-obj-y += qapi/qapi-visit-misc.o -util-obj-y += qapi/qapi-visit-net.o -util-obj-y += qapi/qapi-visit-rocker.o -util-obj-y += qapi/qapi-visit-run-state.o -util-obj-y += qapi/qapi-visit-sockets.o -util-obj-y += qapi/qapi-visit-tpm.o -util-obj-y += qapi/qapi-visit-trace.o -util-obj-y += qapi/qapi-visit-transaction.o -util-obj-y += qapi/qapi-visit-ui.o -util-obj-y += qapi/qapi-events.o -util-obj-y += qapi/qapi-events-block-core.o -util-obj-y += qapi/qapi-events-block.o -util-obj-y += qapi/qapi-events-char.o -util-obj-y += qapi/qapi-events-common.o -util-obj-y += qapi/qapi-events-crypto.o -util-obj-y += qapi/qapi-events-introspect.o -util-obj-y += qapi/qapi-events-job.o -util-obj-y += qapi/qapi-events-migration.o -util-obj-y += qapi/qapi-events-misc.o -util-obj-y += qapi/qapi-events-net.o -util-obj-y += qapi/qapi-events-rocker.o -util-obj-y += qapi/qapi-events-run-state.o -util-obj-y += qapi/qapi-events-sockets.o -util-obj-y += qapi/qapi-events-tpm.o -util-obj-y += qapi/qapi-events-trace.o -util-obj-y += qapi/qapi-events-transaction.o -util-obj-y += qapi/qapi-events-ui.o -util-obj-y += qapi/qapi-introspect.o chardev-obj-y = chardev/ +####################################################################### +# authz-obj-y is code used by both qemu system emulation and qemu-img + +authz-obj-y = authz/ + ####################################################################### # block-obj-y is code used by both qemu system emulation and qemu-img -block-obj-y += nbd/ +block-obj-y = nbd/ block-obj-y += block.o blockjob.o job.o block-obj-y += block/ scsi/ block-obj-y += qemu-io-cmds.o @@ -123,8 +71,6 @@ common-obj-y += vl.o vl.o-cflags := $(GPROF_CFLAGS) $(SDL_CFLAGS) common-obj-$(CONFIG_TPM) += tpm.o -common-obj-$(CONFIG_SLIRP) += slirp/ - common-obj-y += backends/ common-obj-y += chardev/ @@ -137,26 +83,8 @@ common-obj-$(CONFIG_FDT) += device_tree.o ###################################################################### # qapi -common-obj-y += qapi/qapi-commands.o -common-obj-y += qapi/qapi-commands-block-core.o -common-obj-y += qapi/qapi-commands-block.o -common-obj-y += qapi/qapi-commands-char.o -common-obj-y += qapi/qapi-commands-common.o -common-obj-y += qapi/qapi-commands-crypto.o -common-obj-y += qapi/qapi-commands-introspect.o -common-obj-y += qapi/qapi-commands-job.o -common-obj-y += qapi/qapi-commands-migration.o -common-obj-y += qapi/qapi-commands-misc.o -common-obj-y += qapi/qapi-commands-net.o -common-obj-y += qapi/qapi-commands-rocker.o -common-obj-y += qapi/qapi-commands-run-state.o -common-obj-y += qapi/qapi-commands-sockets.o -common-obj-y += qapi/qapi-commands-tpm.o -common-obj-y += qapi/qapi-commands-trace.o -common-obj-y += qapi/qapi-commands-transaction.o -common-obj-y += qapi/qapi-commands-ui.o -common-obj-y += qapi/qapi-introspect.o common-obj-y += qmp.o hmp.o +common-obj-y += qapi/ endif ####################################################################### @@ -173,7 +101,6 @@ version-obj-$(CONFIG_WIN32) += $(BUILD_DIR)/version.o ###################################################################### # tracing util-obj-y += trace/ -target-obj-y += trace/ ###################################################################### # guest agent @@ -186,6 +113,7 @@ qga-vss-dll-obj-y = qga/ ###################################################################### # contrib +elf2dmp-obj-y = contrib/elf2dmp/ ivshmem-client-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-client/ ivshmem-server-obj-$(CONFIG_IVSHMEM) = contrib/ivshmem-server/ libvhost-user-obj-y = contrib/libvhost-user/ @@ -193,12 +121,14 @@ vhost-user-scsi.o-cflags := $(LIBISCSI_CFLAGS) vhost-user-scsi.o-libs := $(LIBISCSI_LIBS) vhost-user-scsi-obj-y = contrib/vhost-user-scsi/ vhost-user-blk-obj-y = contrib/vhost-user-blk/ +rdmacm-mux-obj-y = contrib/rdmacm-mux/ ###################################################################### trace-events-subdirs = trace-events-subdirs += accel/kvm trace-events-subdirs += accel/tcg trace-events-subdirs += audio +trace-events-subdirs += authz trace-events-subdirs += block trace-events-subdirs += chardev trace-events-subdirs += crypto @@ -240,7 +170,9 @@ trace-events-subdirs += hw/tpm trace-events-subdirs += hw/usb trace-events-subdirs += hw/vfio trace-events-subdirs += hw/virtio +trace-events-subdirs += hw/watchdog trace-events-subdirs += hw/xen +trace-events-subdirs += hw/gpio trace-events-subdirs += io trace-events-subdirs += linux-user trace-events-subdirs += migration @@ -250,9 +182,11 @@ trace-events-subdirs += qapi trace-events-subdirs += qom trace-events-subdirs += scsi trace-events-subdirs += target/arm +trace-events-subdirs += target/hppa trace-events-subdirs += target/i386 trace-events-subdirs += target/mips trace-events-subdirs += target/ppc +trace-events-subdirs += target/riscv trace-events-subdirs += target/s390x trace-events-subdirs += target/sparc trace-events-subdirs += ui diff --git a/Makefile.target b/Makefile.target index 4d56298bbf..ae02495951 100644 --- a/Makefile.target +++ b/Makefile.target @@ -4,9 +4,12 @@ BUILD_DIR?=$(CURDIR)/.. include ../config-host.mak include config-target.mak -include config-devices.mak include $(SRC_PATH)/rules.mak +ifdef CONFIG_SOFTMMU +include config-devices.mak +endif + $(call set-vpath, $(SRC_PATH):$(BUILD_DIR)) ifdef CONFIG_LINUX QEMU_CFLAGS += -I../linux-headers @@ -37,15 +40,13 @@ PROGS=$(QEMU_PROG) $(QEMU_PROGW) STPFILES= # Makefile Tests -ifdef CONFIG_USER_ONLY include $(SRC_PATH)/tests/tcg/Makefile.include -endif config-target.h: config-target.h-timestamp config-target.h-timestamp: config-target.mak ifdef CONFIG_TRACE_SYSTEMTAP -stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp +stap: $(QEMU_PROG).stp-installed $(QEMU_PROG).stp $(QEMU_PROG)-simpletrace.stp $(QEMU_PROG)-log.stp ifdef CONFIG_USER_ONLY TARGET_TYPE=user @@ -84,6 +85,14 @@ $(QEMU_PROG)-simpletrace.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-simpletrace.stp") +$(QEMU_PROG)-log.stp: $(BUILD_DIR)/trace-events-all $(tracetool-y) + $(call quiet-command,$(TRACETOOL) \ + --group=all \ + --format=log-stap \ + --backends=$(TRACE_BACKENDS) \ + --probe-prefix=qemu.$(TARGET_TYPE).$(TARGET_NAME) \ + $< > $@,"GEN","$(TARGET_DIR)$(QEMU_PROG)-log.stp") + else stap: endif @@ -94,6 +103,8 @@ all: $(PROGS) stap # Dummy command so that make thinks it has done something @true +obj-y += trace/ + ######################################################### # cpu emulator library obj-y += exec.o @@ -140,6 +151,7 @@ ifdef CONFIG_SOFTMMU obj-y += arch_init.o cpus.o monitor.o gdbstub.o balloon.o ioport.o numa.o obj-y += qtest.o obj-y += hw/ +obj-y += qapi/ obj-y += memory.o obj-y += memory_mapping.o obj-y += dump.o @@ -158,20 +170,12 @@ GENERATED_FILES += hmp-commands.h hmp-commands-info.h endif # CONFIG_SOFTMMU -# Workaround for http://gcc.gnu.org/PR55489, see configure. -%/translate.o: QEMU_CFLAGS += $(TRANSLATE_OPT_CFLAGS) - dummy := $(call unnest-vars,,obj-y) all-obj-y := $(obj-y) -target-obj-y := -block-obj-y := -common-obj-y := -chardev-obj-y := include $(SRC_PATH)/Makefile.objs -dummy := $(call unnest-vars,,target-obj-y) -target-obj-y-save := $(target-obj-y) dummy := $(call unnest-vars,.., \ + authz-obj-y \ block-obj-y \ block-obj-m \ chardev-obj-y \ @@ -181,16 +185,17 @@ dummy := $(call unnest-vars,.., \ 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) += $(authz-obj-y) all-obj-$(CONFIG_SOFTMMU) += $(block-obj-y) $(chardev-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) +ifdef CONFIG_SOFTMMU $(QEMU_PROG_BUILD): config-devices.mak +endif COMMON_LDADDS = ../libqemuutil.a @@ -215,6 +220,7 @@ clean: clean-target rm -f *.a *~ $(PROGS) rm -f $(shell find . -name '*.[od]') rm -f hmp-commands.h gdbstub-xml.c + rm -f trace/generated-helpers.c trace/generated-helpers.c-timestamp ifdef CONFIG_TRACE_SYSTEMTAP rm -f *.stp endif @@ -227,6 +233,7 @@ ifdef CONFIG_TRACE_SYSTEMTAP $(INSTALL_DIR) "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset" $(INSTALL_DATA) $(QEMU_PROG).stp-installed "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG).stp" $(INSTALL_DATA) $(QEMU_PROG)-simpletrace.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-simpletrace.stp" + $(INSTALL_DATA) $(QEMU_PROG)-log.stp "$(DESTDIR)$(qemu_datadir)/../systemtap/tapset/$(QEMU_PROG)-log.stp" endif GENERATED_FILES += config-target.h diff --git a/README b/README index 49a9fd09cd..441c33eb2f 100644 --- a/README +++ b/README @@ -54,7 +54,7 @@ Submitting patches The QEMU source code is maintained under the GIT version control system. - git clone git://git.qemu.org/qemu.git + git clone https://git.qemu.org/git/qemu.git When submitting patches, one common approach is to use 'git format-patch' and/or 'git send-email' to format & send the mail to the @@ -70,7 +70,7 @@ the QEMU website The QEMU website is also maintained under source control. - git clone git://git.qemu.org/qemu-web.git + git clone https://git.qemu.org/git/qemu-web.git https://www.qemu.org/2017/02/04/the-new-qemu-website-is-up/ A 'git-publish' utility was created to make above process less diff --git a/VERSION b/VERSION index 4a36342fca..fcdb2e109f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -3.0.0 +4.0.0 diff --git a/accel/accel.c b/accel/accel.c index 966b2d8f53..454fef9d92 100644 --- a/accel/accel.c +++ b/accel/accel.c @@ -34,6 +34,7 @@ #include "qom/object.h" #include "qemu/error-report.h" #include "qemu/option.h" +#include "qapi/error.h" static const TypeInfo accel_type = { .name = TYPE_ACCEL, @@ -64,11 +65,13 @@ static int accel_init_machine(AccelClass *acc, MachineState *ms) ms->accelerator = NULL; *(acc->allowed) = false; object_unref(OBJECT(accel)); + } else { + object_set_accelerator_compat_props(acc->compat_props); } return ret; } -void configure_accelerator(MachineState *ms) +void configure_accelerator(MachineState *ms, const char *progname) { const char *accel; char **accel_list, **tmp; @@ -79,8 +82,22 @@ void configure_accelerator(MachineState *ms) accel = qemu_opt_get(qemu_get_machine_opts(), "accel"); if (accel == NULL) { - /* Use the default "accelerator", tcg */ - accel = "tcg"; + /* Select the default accelerator */ + int pnlen = strlen(progname); + if (pnlen >= 3 && g_str_equal(&progname[pnlen - 3], "kvm")) { + /* If the program name ends with "kvm", we prefer KVM */ + accel = "kvm:tcg"; + } else { +#if defined(CONFIG_TCG) + accel = "tcg"; +#elif defined(CONFIG_KVM) + accel = "kvm"; +#else + error_report("No accelerator selected and" + " no default accelerator available"); + exit(1); +#endif + } } accel_list = g_strsplit(accel, ":", 0); @@ -118,12 +135,6 @@ void configure_accelerator(MachineState *ms) } } -void accel_register_compat_props(AccelState *accel) -{ - AccelClass *class = ACCEL_GET_CLASS(accel); - register_compat_props_array(class->global_props); -} - void accel_setup_post(MachineState *ms) { AccelState *accel = ms->accelerator; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index eb7db92a5e..241db496c3 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -39,6 +39,7 @@ #include "trace.h" #include "hw/irq.h" #include "sysemu/sev.h" +#include "sysemu/balloon.h" #include "hw/boards.h" @@ -78,13 +79,14 @@ struct KVMState int fd; int vmfd; int coalesced_mmio; + int coalesced_pio; struct kvm_coalesced_mmio_ring *coalesced_mmio_ring; bool coalesced_flush_in_progress; int vcpu_events; int robust_singlestep; int debugregs; #ifdef KVM_CAP_SET_GUEST_DEBUG - struct kvm_sw_breakpoint_head kvm_sw_breakpoints; + QTAILQ_HEAD(, kvm_sw_breakpoint) kvm_sw_breakpoints; #endif int many_ioeventfds; int intx_set_mask; @@ -100,7 +102,7 @@ struct KVMState int nr_allocated_irq_routes; unsigned long *used_gsi_bitmap; unsigned int gsi_count; - QTAILQ_HEAD(msi_hashtab, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; + QTAILQ_HEAD(, KVMMSIRoute) msi_hashtab[KVM_MSI_HASHTAB_SIZE]; #endif KVMMemoryListener memory_listener; QLIST_HEAD(, KVMParkedVcpu) kvm_parked_vcpus; @@ -559,6 +561,45 @@ static void kvm_uncoalesce_mmio_region(MemoryListener *listener, } } +static void kvm_coalesce_pio_add(MemoryListener *listener, + MemoryRegionSection *section, + hwaddr start, hwaddr size) +{ + KVMState *s = kvm_state; + + if (s->coalesced_pio) { + struct kvm_coalesced_mmio_zone zone; + + zone.addr = start; + zone.size = size; + zone.pio = 1; + + (void)kvm_vm_ioctl(s, KVM_REGISTER_COALESCED_MMIO, &zone); + } +} + +static void kvm_coalesce_pio_del(MemoryListener *listener, + MemoryRegionSection *section, + hwaddr start, hwaddr size) +{ + KVMState *s = kvm_state; + + if (s->coalesced_pio) { + struct kvm_coalesced_mmio_zone zone; + + zone.addr = start; + zone.size = size; + zone.pio = 1; + + (void)kvm_vm_ioctl(s, KVM_UNREGISTER_COALESCED_MMIO, &zone); + } +} + +static MemoryListener kvm_coalesced_pio_listener = { + .coalesced_io_add = kvm_coalesce_pio_add, + .coalesced_io_del = kvm_coalesce_pio_del, +}; + int kvm_check_extension(KVMState *s, unsigned int extension) { int ret; @@ -616,6 +657,8 @@ static int kvm_set_ioeventfd_mmio(int fd, hwaddr addr, uint32_t val, .fd = fd, }; + trace_kvm_set_ioeventfd_mmio(fd, (uint64_t)addr, val, assign, size, + datamatch); if (!kvm_enabled()) { return -ENOSYS; } @@ -647,6 +690,7 @@ static int kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint16_t val, .fd = fd, }; int r; + trace_kvm_set_ioeventfd_pio(fd, addr, val, assign, size, datamatch); if (!kvm_enabled()) { return -ENOSYS; } @@ -1549,7 +1593,7 @@ static int kvm_init(MachineState *ms) kvm_type = qemu_opt_get(qemu_get_machine_opts(), "kvm-type"); if (mc->kvm_type) { - type = mc->kvm_type(kvm_type); + type = mc->kvm_type(ms, kvm_type); } else if (kvm_type) { ret = -EINVAL; fprintf(stderr, "Invalid argument kvm-type=%s\n", kvm_type); @@ -1615,6 +1659,8 @@ static int kvm_init(MachineState *ms) } s->coalesced_mmio = kvm_check_extension(s, KVM_CAP_COALESCED_MMIO); + s->coalesced_pio = s->coalesced_mmio && + kvm_check_extension(s, KVM_CAP_COALESCED_PIO); #ifdef KVM_CAP_VCPU_EVENTS s->vcpu_events = kvm_check_extension(s, KVM_CAP_VCPU_EVENTS); @@ -1638,10 +1684,8 @@ static int kvm_init(MachineState *ms) s->irq_set_ioctl = KVM_IRQ_LINE_STATUS; } -#ifdef KVM_CAP_READONLY_MEM kvm_readonly_mem_allowed = (kvm_check_extension(s, KVM_CAP_READONLY_MEM) > 0); -#endif kvm_eventfds_allowed = (kvm_check_extension(s, KVM_CAP_IOEVENTFD) > 0); @@ -1687,17 +1731,22 @@ static int kvm_init(MachineState *ms) 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; + s->memory_listener.listener.coalesced_io_add = kvm_coalesce_mmio_region; + s->memory_listener.listener.coalesced_io_del = kvm_uncoalesce_mmio_region; kvm_memory_listener_register(s, &s->memory_listener, &address_space_memory, 0); memory_listener_register(&kvm_io_listener, &address_space_io); + memory_listener_register(&kvm_coalesced_pio_listener, + &address_space_io); s->many_ioeventfds = kvm_check_many_ioeventfds(); s->sync_mmu = !!kvm_vm_check_extension(kvm_state, KVM_CAP_SYNC_MMU); + if (!s->sync_mmu) { + qemu_balloon_inhibit(true); + } return 0; @@ -1776,7 +1825,13 @@ void kvm_flush_coalesced_mmio_buffer(void) ent = &ring->coalesced_mmio[ring->first]; - cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); + if (ent->pio == 1) { + address_space_rw(&address_space_io, ent->phys_addr, + MEMTXATTRS_UNSPECIFIED, ent->data, + ent->len, true); + } else { + cpu_physical_memory_write(ent->phys_addr, ent->data, ent->len); + } smp_wmb(); ring->first = (ring->first + 1) % KVM_COALESCED_MMIO_MAX; } diff --git a/accel/kvm/trace-events b/accel/kvm/trace-events index 58e98efe5d..33c5b1b3af 100644 --- a/accel/kvm/trace-events +++ b/accel/kvm/trace-events @@ -1,4 +1,4 @@ -# Trace events for debugging and performance instrumentation +# See docs/devel/tracing.txt for syntax documentation. # kvm-all.c kvm_ioctl(int type, void *arg) "type 0x%x, arg %p" @@ -12,5 +12,7 @@ kvm_irqchip_commit_routes(void) "" kvm_irqchip_add_msi_route(char *name, int vector, int virq) "dev %s vector %d virq %d" kvm_irqchip_update_msi_route(int virq) "Updating MSI route virq=%d" kvm_irqchip_release_virq(int virq) "virq %d" +kvm_set_ioeventfd_mmio(int fd, uint64_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%" PRIx64 " val=0x%x assign: %d size: %d match: %d" +kvm_set_ioeventfd_pio(int fd, uint16_t addr, uint32_t val, bool assign, uint32_t size, bool datamatch) "fd: %d @0x%x val=0x%x assign: %d size: %d match: %d" kvm_set_user_memory(uint32_t slot, uint32_t flags, uint64_t guest_phys_addr, uint64_t memory_size, uint64_t userspace_addr, int ret) "Slot#%d flags=0x%x gpa=0x%"PRIx64 " size=0x%"PRIx64 " ua=0x%"PRIx64 " ret=%d" diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index d751bcba48..685602b076 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -7,7 +7,7 @@ * 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. + * 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 @@ -100,19 +100,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, DATA_TYPE ret; ATOMIC_TRACE_RMW; +#if DATA_SIZE == 16 + ret = atomic16_cmpxchg(haddr, cmpv, newv); +#else ret = atomic_cmpxchg__nocheck(haddr, cmpv, newv); +#endif ATOMIC_MMU_CLEANUP; return ret; } #if DATA_SIZE >= 16 +#if HAVE_ATOMIC128 ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; ATOMIC_TRACE_LD; - __atomic_load(haddr, &val, __ATOMIC_RELAXED); + val = atomic16_read(haddr); ATOMIC_MMU_CLEANUP; return val; } @@ -124,9 +129,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, DATA_TYPE *haddr = ATOMIC_MMU_LOOKUP; ATOMIC_TRACE_ST; - __atomic_store(haddr, &val, __ATOMIC_RELAXED); + atomic16_set(haddr, val); ATOMIC_MMU_CLEANUP; } +#endif #else ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val EXTRA_ARGS) @@ -228,19 +234,24 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, target_ulong addr, DATA_TYPE ret; ATOMIC_TRACE_RMW; +#if DATA_SIZE == 16 + ret = atomic16_cmpxchg(haddr, BSWAP(cmpv), BSWAP(newv)); +#else ret = atomic_cmpxchg__nocheck(haddr, BSWAP(cmpv), BSWAP(newv)); +#endif ATOMIC_MMU_CLEANUP; return BSWAP(ret); } #if DATA_SIZE >= 16 +#if HAVE_ATOMIC128 ABI_TYPE ATOMIC_NAME(ld)(CPUArchState *env, target_ulong addr EXTRA_ARGS) { ATOMIC_MMU_DECLS; DATA_TYPE val, *haddr = ATOMIC_MMU_LOOKUP; ATOMIC_TRACE_LD; - __atomic_load(haddr, &val, __ATOMIC_RELAXED); + val = atomic16_read(haddr); ATOMIC_MMU_CLEANUP; return BSWAP(val); } @@ -253,9 +264,10 @@ void ATOMIC_NAME(st)(CPUArchState *env, target_ulong addr, ATOMIC_TRACE_ST; val = BSWAP(val); - __atomic_store(haddr, &val, __ATOMIC_RELAXED); + atomic16_set(haddr, val); ATOMIC_MMU_CLEANUP; } +#endif #else ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, target_ulong addr, ABI_TYPE val EXTRA_ARGS) diff --git a/accel/tcg/cpu-exec-common.c b/accel/tcg/cpu-exec-common.c index 2988fde650..462a1f1865 100644 --- a/accel/tcg/cpu-exec-common.c +++ b/accel/tcg/cpu-exec-common.c @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c738b7f7d6..45ef41ebb2 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -266,6 +266,9 @@ void cpu_exec_step_atomic(CPUState *cpu) #ifndef CONFIG_SOFTMMU tcg_debug_assert(!have_mmap_lock()); #endif + if (qemu_mutex_iothread_locked()) { + qemu_mutex_unlock_iothread(); + } assert_no_pages_locked(); } @@ -332,6 +335,9 @@ TranslationBlock *tb_htable_lookup(CPUState *cpu, target_ulong pc, desc.trace_vcpu_dstate = *cpu->trace_dstate; desc.pc = pc; phys_pc = get_page_addr_code(desc.env, pc); + if (phys_pc == -1) { + return NULL; + } desc.phys_page1 = phys_pc & TARGET_PAGE_MASK; h = tb_hash_func(phys_pc, pc, flags, cf_mask, *cpu->trace_dstate); return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); @@ -413,7 +419,7 @@ static inline TranslationBlock *tb_find(CPUState *cpu, } #endif /* See if we can patch the calling TB. */ - if (last_tb && !qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { + if (last_tb) { tb_add_jump(last_tb, tb_exit, tb); } return tb; @@ -696,6 +702,7 @@ int cpu_exec(CPUState *cpu) if (qemu_mutex_iothread_locked()) { qemu_mutex_unlock_iothread(); } + assert_no_pages_locked(); } /* if an exception is pending, we execute it here */ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 563fa30117..88cc8389e9 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -32,6 +32,7 @@ #include "exec/log.h" #include "exec/helper-proto.h" #include "qemu/atomic.h" +#include "qemu/atomic128.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ @@ -58,9 +59,9 @@ } \ } while (0) -#define assert_cpu_is_self(this_cpu) do { \ +#define assert_cpu_is_self(cpu) do { \ if (DEBUG_TLB_GATE) { \ - g_assert(!cpu->created || qemu_cpu_is_self(cpu)); \ + g_assert(!(cpu)->created || qemu_cpu_is_self(cpu)); \ } \ } while (0) @@ -73,6 +74,178 @@ QEMU_BUILD_BUG_ON(sizeof(target_ulong) > sizeof(run_on_cpu_data)); QEMU_BUILD_BUG_ON(NB_MMU_MODES > 16); #define ALL_MMUIDX_BITS ((1 << NB_MMU_MODES) - 1) +static inline size_t sizeof_tlb(CPUArchState *env, uintptr_t mmu_idx) +{ + return env->tlb_mask[mmu_idx] + (1 << CPU_TLB_ENTRY_BITS); +} + +static void tlb_window_reset(CPUTLBWindow *window, int64_t ns, + size_t max_entries) +{ + window->begin_ns = ns; + window->max_entries = max_entries; +} + +static void tlb_dyn_init(CPUArchState *env) +{ + int i; + + for (i = 0; i < NB_MMU_MODES; i++) { + CPUTLBDesc *desc = &env->tlb_d[i]; + size_t n_entries = 1 << CPU_TLB_DYN_DEFAULT_BITS; + + tlb_window_reset(&desc->window, get_clock_realtime(), 0); + desc->n_used_entries = 0; + env->tlb_mask[i] = (n_entries - 1) << CPU_TLB_ENTRY_BITS; + env->tlb_table[i] = g_new(CPUTLBEntry, n_entries); + env->iotlb[i] = g_new(CPUIOTLBEntry, n_entries); + } +} + +/** + * tlb_mmu_resize_locked() - perform TLB resize bookkeeping; resize if necessary + * @env: CPU that owns the TLB + * @mmu_idx: MMU index of the TLB + * + * Called with tlb_lock_held. + * + * We have two main constraints when resizing a TLB: (1) we only resize it + * on a TLB flush (otherwise we'd have to take a perf hit by either rehashing + * the array or unnecessarily flushing it), which means we do not control how + * frequently the resizing can occur; (2) we don't have access to the guest's + * future scheduling decisions, and therefore have to decide the magnitude of + * the resize based on past observations. + * + * In general, a memory-hungry process can benefit greatly from an appropriately + * sized TLB, since a guest TLB miss is very expensive. This doesn't mean that + * we just have to make the TLB as large as possible; while an oversized TLB + * results in minimal TLB miss rates, it also takes longer to be flushed + * (flushes can be _very_ frequent), and the reduced locality can also hurt + * performance. + * + * To achieve near-optimal performance for all kinds of workloads, we: + * + * 1. Aggressively increase the size of the TLB when the use rate of the + * TLB being flushed is high, since it is likely that in the near future this + * memory-hungry process will execute again, and its memory hungriness will + * probably be similar. + * + * 2. Slowly reduce the size of the TLB as the use rate declines over a + * reasonably large time window. The rationale is that if in such a time window + * we have not observed a high TLB use rate, it is likely that we won't observe + * it in the near future. In that case, once a time window expires we downsize + * the TLB to match the maximum use rate observed in the window. + * + * 3. Try to keep the maximum use rate in a time window in the 30-70% range, + * since in that range performance is likely near-optimal. Recall that the TLB + * is direct mapped, so we want the use rate to be low (or at least not too + * high), since otherwise we are likely to have a significant amount of + * conflict misses. + */ +static void tlb_mmu_resize_locked(CPUArchState *env, int mmu_idx) +{ + CPUTLBDesc *desc = &env->tlb_d[mmu_idx]; + size_t old_size = tlb_n_entries(env, mmu_idx); + size_t rate; + size_t new_size = old_size; + int64_t now = get_clock_realtime(); + int64_t window_len_ms = 100; + int64_t window_len_ns = window_len_ms * 1000 * 1000; + bool window_expired = now > desc->window.begin_ns + window_len_ns; + + if (desc->n_used_entries > desc->window.max_entries) { + desc->window.max_entries = desc->n_used_entries; + } + rate = desc->window.max_entries * 100 / old_size; + + if (rate > 70) { + new_size = MIN(old_size << 1, 1 << CPU_TLB_DYN_MAX_BITS); + } else if (rate < 30 && window_expired) { + size_t ceil = pow2ceil(desc->window.max_entries); + size_t expected_rate = desc->window.max_entries * 100 / ceil; + + /* + * Avoid undersizing when the max number of entries seen is just below + * a pow2. For instance, if max_entries == 1025, the expected use rate + * would be 1025/2048==50%. However, if max_entries == 1023, we'd get + * 1023/1024==99.9% use rate, so we'd likely end up doubling the size + * later. Thus, make sure that the expected use rate remains below 70%. + * (and since we double the size, that means the lowest rate we'd + * expect to get is 35%, which is still in the 30-70% range where + * we consider that the size is appropriate.) + */ + if (expected_rate > 70) { + ceil *= 2; + } + new_size = MAX(ceil, 1 << CPU_TLB_DYN_MIN_BITS); + } + + if (new_size == old_size) { + if (window_expired) { + tlb_window_reset(&desc->window, now, desc->n_used_entries); + } + return; + } + + g_free(env->tlb_table[mmu_idx]); + g_free(env->iotlb[mmu_idx]); + + tlb_window_reset(&desc->window, now, 0); + /* desc->n_used_entries is cleared by the caller */ + env->tlb_mask[mmu_idx] = (new_size - 1) << CPU_TLB_ENTRY_BITS; + env->tlb_table[mmu_idx] = g_try_new(CPUTLBEntry, new_size); + env->iotlb[mmu_idx] = g_try_new(CPUIOTLBEntry, new_size); + /* + * If the allocations fail, try smaller sizes. We just freed some + * memory, so going back to half of new_size has a good chance of working. + * Increased memory pressure elsewhere in the system might cause the + * allocations to fail though, so we progressively reduce the allocation + * size, aborting if we cannot even allocate the smallest TLB we support. + */ + while (env->tlb_table[mmu_idx] == NULL || env->iotlb[mmu_idx] == NULL) { + if (new_size == (1 << CPU_TLB_DYN_MIN_BITS)) { + error_report("%s: %s", __func__, strerror(errno)); + abort(); + } + new_size = MAX(new_size >> 1, 1 << CPU_TLB_DYN_MIN_BITS); + env->tlb_mask[mmu_idx] = (new_size - 1) << CPU_TLB_ENTRY_BITS; + + g_free(env->tlb_table[mmu_idx]); + g_free(env->iotlb[mmu_idx]); + env->tlb_table[mmu_idx] = g_try_new(CPUTLBEntry, new_size); + env->iotlb[mmu_idx] = g_try_new(CPUIOTLBEntry, new_size); + } +} + +static inline void tlb_table_flush_by_mmuidx(CPUArchState *env, int mmu_idx) +{ + tlb_mmu_resize_locked(env, mmu_idx); + memset(env->tlb_table[mmu_idx], -1, sizeof_tlb(env, mmu_idx)); + env->tlb_d[mmu_idx].n_used_entries = 0; +} + +static inline void tlb_n_used_entries_inc(CPUArchState *env, uintptr_t mmu_idx) +{ + env->tlb_d[mmu_idx].n_used_entries++; +} + +static inline void tlb_n_used_entries_dec(CPUArchState *env, uintptr_t mmu_idx) +{ + env->tlb_d[mmu_idx].n_used_entries--; +} + +void tlb_init(CPUState *cpu) +{ + CPUArchState *env = cpu->env_ptr; + + qemu_spin_init(&env->tlb_c.lock); + + /* Ensure that cpu_reset performs a full flush. */ + env->tlb_c.dirty = ALL_MMUIDX_BITS; + + tlb_dyn_init(env); +} + /* flush_all_helper: run fn across all cpus * * If the wait flag is set then the src cpu's helper will be queued as @@ -92,128 +265,89 @@ static void flush_all_helper(CPUState *src, run_on_cpu_func fn, } } -size_t tlb_flush_count(void) +void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) { CPUState *cpu; - size_t count = 0; + size_t full = 0, part = 0, elide = 0; CPU_FOREACH(cpu) { CPUArchState *env = cpu->env_ptr; - count += atomic_read(&env->tlb_flush_count); + full += atomic_read(&env->tlb_c.full_flush_count); + part += atomic_read(&env->tlb_c.part_flush_count); + elide += atomic_read(&env->tlb_c.elide_flush_count); } - return count; + *pfull = full; + *ppart = part; + *pelide = elide; } -/* This is OK because CPU architectures generally permit an - * implementation to drop entries from the TLB at any time, so - * flushing more entries than required is only an efficiency issue, - * not a correctness issue. - */ -static void tlb_flush_nocheck(CPUState *cpu) +static void tlb_flush_one_mmuidx_locked(CPUArchState *env, int mmu_idx) { - CPUArchState *env = cpu->env_ptr; - - /* The QOM tests will trigger tlb_flushes without setting up TCG - * so we bug out here in that case. - */ - if (!tcg_enabled()) { - return; - } - - assert_cpu_is_self(cpu); - atomic_set(&env->tlb_flush_count, env->tlb_flush_count + 1); - tlb_debug("(count: %zu)\n", tlb_flush_count()); - - memset(env->tlb_table, -1, sizeof(env->tlb_table)); - memset(env->tlb_v_table, -1, sizeof(env->tlb_v_table)); - cpu_tb_jmp_cache_clear(cpu); - - env->vtlb_index = 0; - env->tlb_flush_addr = -1; - env->tlb_flush_mask = 0; - - atomic_mb_set(&cpu->pending_tlb_flush, 0); -} - -static void tlb_flush_global_async_work(CPUState *cpu, run_on_cpu_data data) -{ - tlb_flush_nocheck(cpu); -} - -void tlb_flush(CPUState *cpu) -{ - if (cpu->created && !qemu_cpu_is_self(cpu)) { - if (atomic_mb_read(&cpu->pending_tlb_flush) != ALL_MMUIDX_BITS) { - atomic_mb_set(&cpu->pending_tlb_flush, ALL_MMUIDX_BITS); - async_run_on_cpu(cpu, tlb_flush_global_async_work, - RUN_ON_CPU_NULL); - } - } else { - tlb_flush_nocheck(cpu); - } -} - -void tlb_flush_all_cpus(CPUState *src_cpu) -{ - const run_on_cpu_func fn = tlb_flush_global_async_work; - flush_all_helper(src_cpu, fn, RUN_ON_CPU_NULL); - fn(src_cpu, RUN_ON_CPU_NULL); -} - -void tlb_flush_all_cpus_synced(CPUState *src_cpu) -{ - const run_on_cpu_func fn = tlb_flush_global_async_work; - flush_all_helper(src_cpu, fn, RUN_ON_CPU_NULL); - async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_NULL); + tlb_table_flush_by_mmuidx(env, mmu_idx); + memset(env->tlb_v_table[mmu_idx], -1, sizeof(env->tlb_v_table[0])); + env->tlb_d[mmu_idx].large_page_addr = -1; + env->tlb_d[mmu_idx].large_page_mask = -1; + env->tlb_d[mmu_idx].vindex = 0; } static void tlb_flush_by_mmuidx_async_work(CPUState *cpu, run_on_cpu_data data) { CPUArchState *env = cpu->env_ptr; - unsigned long mmu_idx_bitmask = data.host_int; - int mmu_idx; + uint16_t asked = data.host_int; + uint16_t all_dirty, work, to_clean; assert_cpu_is_self(cpu); - tlb_debug("start: mmu_idx:0x%04lx\n", mmu_idx_bitmask); + tlb_debug("mmu_idx:0x%04" PRIx16 "\n", asked); - for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + qemu_spin_lock(&env->tlb_c.lock); - if (test_bit(mmu_idx, &mmu_idx_bitmask)) { - tlb_debug("%d\n", mmu_idx); + all_dirty = env->tlb_c.dirty; + to_clean = asked & all_dirty; + all_dirty &= ~to_clean; + env->tlb_c.dirty = all_dirty; - 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])); - } + for (work = to_clean; work != 0; work &= work - 1) { + int mmu_idx = ctz32(work); + tlb_flush_one_mmuidx_locked(env, mmu_idx); } + qemu_spin_unlock(&env->tlb_c.lock); + cpu_tb_jmp_cache_clear(cpu); - tlb_debug("done\n"); + if (to_clean == ALL_MMUIDX_BITS) { + atomic_set(&env->tlb_c.full_flush_count, + env->tlb_c.full_flush_count + 1); + } else { + atomic_set(&env->tlb_c.part_flush_count, + env->tlb_c.part_flush_count + ctpop16(to_clean)); + if (to_clean != asked) { + atomic_set(&env->tlb_c.elide_flush_count, + env->tlb_c.elide_flush_count + + ctpop16(asked & ~to_clean)); + } + } } void tlb_flush_by_mmuidx(CPUState *cpu, uint16_t idxmap) { tlb_debug("mmu_idx: 0x%" PRIx16 "\n", idxmap); - if (!qemu_cpu_is_self(cpu)) { - uint16_t pending_flushes = idxmap; - pending_flushes &= ~atomic_mb_read(&cpu->pending_tlb_flush); - - if (pending_flushes) { - tlb_debug("reduced mmu_idx: 0x%" PRIx16 "\n", pending_flushes); - - atomic_or(&cpu->pending_tlb_flush, pending_flushes); - async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work, - RUN_ON_CPU_HOST_INT(pending_flushes)); - } + if (cpu->created && !qemu_cpu_is_self(cpu)) { + async_run_on_cpu(cpu, tlb_flush_by_mmuidx_async_work, + RUN_ON_CPU_HOST_INT(idxmap)); } else { - tlb_flush_by_mmuidx_async_work(cpu, - RUN_ON_CPU_HOST_INT(idxmap)); + tlb_flush_by_mmuidx_async_work(cpu, RUN_ON_CPU_HOST_INT(idxmap)); } } +void tlb_flush(CPUState *cpu) +{ + tlb_flush_by_mmuidx(cpu, ALL_MMUIDX_BITS); +} + void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap) { const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; @@ -224,8 +358,12 @@ void tlb_flush_by_mmuidx_all_cpus(CPUState *src_cpu, uint16_t idxmap) fn(src_cpu, RUN_ON_CPU_HOST_INT(idxmap)); } -void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, - uint16_t idxmap) +void tlb_flush_all_cpus(CPUState *src_cpu) +{ + tlb_flush_by_mmuidx_all_cpus(src_cpu, ALL_MMUIDX_BITS); +} + +void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, uint16_t idxmap) { const run_on_cpu_func fn = tlb_flush_by_mmuidx_async_work; @@ -235,70 +373,70 @@ void tlb_flush_by_mmuidx_all_cpus_synced(CPUState *src_cpu, async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_HOST_INT(idxmap)); } +void tlb_flush_all_cpus_synced(CPUState *src_cpu) +{ + tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, ALL_MMUIDX_BITS); +} + static inline bool tlb_hit_page_anyprot(CPUTLBEntry *tlb_entry, target_ulong page) { return tlb_hit_page(tlb_entry->addr_read, page) || - tlb_hit_page(tlb_entry->addr_write, page) || + tlb_hit_page(tlb_addr_write(tlb_entry), page) || tlb_hit_page(tlb_entry->addr_code, page); } -static inline void tlb_flush_entry(CPUTLBEntry *tlb_entry, target_ulong page) +/** + * tlb_entry_is_empty - return true if the entry is not in use + * @te: pointer to CPUTLBEntry + */ +static inline bool tlb_entry_is_empty(const CPUTLBEntry *te) +{ + return te->addr_read == -1 && te->addr_write == -1 && te->addr_code == -1; +} + +/* Called with tlb_c.lock held */ +static inline bool tlb_flush_entry_locked(CPUTLBEntry *tlb_entry, + target_ulong page) { if (tlb_hit_page_anyprot(tlb_entry, page)) { memset(tlb_entry, -1, sizeof(*tlb_entry)); + return true; } + return false; } -static inline void tlb_flush_vtlb_page(CPUArchState *env, int mmu_idx, - target_ulong page) +/* Called with tlb_c.lock held */ +static inline void tlb_flush_vtlb_page_locked(CPUArchState *env, int mmu_idx, + target_ulong page) { int k; + + assert_cpu_is_self(ENV_GET_CPU(env)); for (k = 0; k < CPU_VTLB_SIZE; k++) { - tlb_flush_entry(&env->tlb_v_table[mmu_idx][k], page); + if (tlb_flush_entry_locked(&env->tlb_v_table[mmu_idx][k], page)) { + tlb_n_used_entries_dec(env, mmu_idx); + } } } -static void tlb_flush_page_async_work(CPUState *cpu, run_on_cpu_data data) +static void tlb_flush_page_locked(CPUArchState *env, int midx, + target_ulong page) { - CPUArchState *env = cpu->env_ptr; - target_ulong addr = (target_ulong) data.target_ptr; - int i; - int mmu_idx; - - assert_cpu_is_self(cpu); - - tlb_debug("page :" TARGET_FMT_lx "\n", addr); + target_ulong lp_addr = env->tlb_d[midx].large_page_addr; + target_ulong lp_mask = env->tlb_d[midx].large_page_mask; /* Check if we need to flush due to large pages. */ - if ((addr & env->tlb_flush_mask) == env->tlb_flush_addr) { - tlb_debug("forcing full flush (" + if ((page & lp_mask) == lp_addr) { + tlb_debug("forcing full flush midx %d (" TARGET_FMT_lx "/" TARGET_FMT_lx ")\n", - env->tlb_flush_addr, env->tlb_flush_mask); - - tlb_flush(cpu); - return; - } - - addr &= TARGET_PAGE_MASK; - i = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - tlb_flush_entry(&env->tlb_table[mmu_idx][i], addr); - tlb_flush_vtlb_page(env, mmu_idx, addr); - } - - tb_flush_jmp_cache(cpu, addr); -} - -void tlb_flush_page(CPUState *cpu, target_ulong addr) -{ - tlb_debug("page :" TARGET_FMT_lx "\n", addr); - - if (!qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_flush_page_async_work, - RUN_ON_CPU_TARGET_PTR(addr)); + midx, lp_addr, lp_mask); + tlb_flush_one_mmuidx_locked(env, midx); } else { - tlb_flush_page_async_work(cpu, RUN_ON_CPU_TARGET_PTR(addr)); + if (tlb_flush_entry_locked(tlb_entry(env, midx, page), page)) { + tlb_n_used_entries_dec(env, midx); + } + tlb_flush_vtlb_page_locked(env, midx, page); } } @@ -314,47 +452,24 @@ static void tlb_flush_page_by_mmuidx_async_work(CPUState *cpu, target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr; target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK; unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; - int page = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); int mmu_idx; assert_cpu_is_self(cpu); - tlb_debug("page:%d addr:"TARGET_FMT_lx" mmu_idx:0x%lx\n", - page, addr, mmu_idx_bitmap); + tlb_debug("page addr:" TARGET_FMT_lx " mmu_map:0x%lx\n", + addr, mmu_idx_bitmap); + qemu_spin_lock(&env->tlb_c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { if (test_bit(mmu_idx, &mmu_idx_bitmap)) { - tlb_flush_entry(&env->tlb_table[mmu_idx][page], addr); - tlb_flush_vtlb_page(env, mmu_idx, addr); + tlb_flush_page_locked(env, mmu_idx, addr); } } + qemu_spin_unlock(&env->tlb_c.lock); tb_flush_jmp_cache(cpu, addr); } -static void tlb_check_page_and_flush_by_mmuidx_async_work(CPUState *cpu, - run_on_cpu_data data) -{ - CPUArchState *env = cpu->env_ptr; - target_ulong addr_and_mmuidx = (target_ulong) data.target_ptr; - target_ulong addr = addr_and_mmuidx & TARGET_PAGE_MASK; - unsigned long mmu_idx_bitmap = addr_and_mmuidx & ALL_MMUIDX_BITS; - - tlb_debug("addr:"TARGET_FMT_lx" mmu_idx: %04lx\n", addr, mmu_idx_bitmap); - - /* 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); - - tlb_flush_by_mmuidx_async_work(cpu, - RUN_ON_CPU_HOST_INT(mmu_idx_bitmap)); - } else { - tlb_flush_page_by_mmuidx_async_work(cpu, data); - } -} - void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) { target_ulong addr_and_mmu_idx; @@ -366,18 +481,23 @@ void tlb_flush_page_by_mmuidx(CPUState *cpu, target_ulong addr, uint16_t idxmap) addr_and_mmu_idx |= idxmap; if (!qemu_cpu_is_self(cpu)) { - async_run_on_cpu(cpu, tlb_check_page_and_flush_by_mmuidx_async_work, + async_run_on_cpu(cpu, tlb_flush_page_by_mmuidx_async_work, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); } else { - tlb_check_page_and_flush_by_mmuidx_async_work( + tlb_flush_page_by_mmuidx_async_work( cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); } } +void tlb_flush_page(CPUState *cpu, target_ulong addr) +{ + tlb_flush_page_by_mmuidx(cpu, addr, ALL_MMUIDX_BITS); +} + void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, uint16_t idxmap) { - const run_on_cpu_func fn = tlb_check_page_and_flush_by_mmuidx_async_work; + const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work; target_ulong addr_and_mmu_idx; tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); @@ -390,11 +510,16 @@ void tlb_flush_page_by_mmuidx_all_cpus(CPUState *src_cpu, target_ulong addr, fn(src_cpu, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); } -void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, - target_ulong addr, - uint16_t idxmap) +void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) { - const run_on_cpu_func fn = tlb_check_page_and_flush_by_mmuidx_async_work; + tlb_flush_page_by_mmuidx_all_cpus(src, addr, ALL_MMUIDX_BITS); +} + +void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, + target_ulong addr, + uint16_t idxmap) +{ + const run_on_cpu_func fn = tlb_flush_page_by_mmuidx_async_work; target_ulong addr_and_mmu_idx; tlb_debug("addr: "TARGET_FMT_lx" mmu_idx:%"PRIx16"\n", addr, idxmap); @@ -407,21 +532,9 @@ void tlb_flush_page_by_mmuidx_all_cpus_synced(CPUState *src_cpu, async_safe_run_on_cpu(src_cpu, fn, RUN_ON_CPU_TARGET_PTR(addr_and_mmu_idx)); } -void tlb_flush_page_all_cpus(CPUState *src, target_ulong addr) +void tlb_flush_page_all_cpus_synced(CPUState *src, target_ulong addr) { - const run_on_cpu_func fn = tlb_flush_page_async_work; - - flush_all_helper(src, fn, RUN_ON_CPU_TARGET_PTR(addr)); - fn(src, RUN_ON_CPU_TARGET_PTR(addr)); -} - -void tlb_flush_page_all_cpus_synced(CPUState *src, - target_ulong addr) -{ - const run_on_cpu_func fn = tlb_flush_page_async_work; - - flush_all_helper(src, fn, RUN_ON_CPU_TARGET_PTR(addr)); - async_safe_run_on_cpu(src, fn, RUN_ON_CPU_TARGET_PTR(addr)); + tlb_flush_page_by_mmuidx_all_cpus_synced(src, addr, ALL_MMUIDX_BITS); } /* update the TLBs so that writes to code in the virtual page 'addr' @@ -450,72 +563,44 @@ void tlb_unprotect_code(ram_addr_t ram_addr) * most usual is detecting writes to code regions which may invalidate * generated code. * - * Because we want other vCPUs to respond to changes straight away we - * update the te->addr_write field atomically. If the TLB entry has - * been changed by the vCPU in the mean time we skip the update. + * Other vCPUs might be reading their TLBs during guest execution, so we update + * te->addr_write with atomic_set. We don't need to worry about this for + * oversized guests as MTTCG is disabled for them. * - * As this function uses atomic accesses we also need to ensure - * updates to tlb_entries follow the same access rules. We don't need - * to worry about this for oversized guests as MTTCG is disabled for - * them. + * Called with tlb_c.lock held. */ - -static void tlb_reset_dirty_range(CPUTLBEntry *tlb_entry, uintptr_t start, - uintptr_t length) +static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, + uintptr_t start, uintptr_t length) { -#if TCG_OVERSIZED_GUEST uintptr_t addr = tlb_entry->addr_write; if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) { addr &= TARGET_PAGE_MASK; addr += tlb_entry->addend; if ((addr - start) < length) { +#if TCG_OVERSIZED_GUEST tlb_entry->addr_write |= TLB_NOTDIRTY; - } - } #else - /* paired with atomic_mb_set in tlb_set_page_with_attrs */ - uintptr_t orig_addr = atomic_mb_read(&tlb_entry->addr_write); - uintptr_t addr = orig_addr; - - if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_NOTDIRTY)) == 0) { - addr &= TARGET_PAGE_MASK; - addr += atomic_read(&tlb_entry->addend); - if ((addr - start) < length) { - uintptr_t notdirty_addr = orig_addr | TLB_NOTDIRTY; - atomic_cmpxchg(&tlb_entry->addr_write, orig_addr, notdirty_addr); + atomic_set(&tlb_entry->addr_write, + tlb_entry->addr_write | TLB_NOTDIRTY); +#endif } } -#endif } -/* For atomic correctness when running MTTCG we need to use the right - * primitives when copying entries */ -static inline void copy_tlb_helper(CPUTLBEntry *d, CPUTLBEntry *s, - bool atomic_set) +/* + * Called with tlb_c.lock held. + * Called only from the vCPU context, i.e. the TLB's owner thread. + */ +static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s) { -#if TCG_OVERSIZED_GUEST *d = *s; -#else - if (atomic_set) { - d->addr_read = s->addr_read; - d->addr_code = s->addr_code; - atomic_set(&d->addend, atomic_read(&s->addend)); - /* Pairs with flag setting in tlb_reset_dirty_range */ - atomic_mb_set(&d->addr_write, atomic_read(&s->addr_write)); - } else { - d->addr_read = s->addr_read; - d->addr_write = atomic_read(&s->addr_write); - d->addr_code = s->addr_code; - d->addend = atomic_read(&s->addend); - } -#endif } /* This is a cross vCPU call (i.e. another vCPU resetting the flags of - * the target vCPU). As such care needs to be taken that we don't - * dangerously race with another vCPU update. The only thing actually - * updated is the target TLB entry ->addr_write flags. + * the target vCPU). + * We must take tlb_c.lock to avoid racing with another vCPU update. The only + * thing actually updated is the target TLB entry ->addr_write flags. */ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) { @@ -524,22 +609,27 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) int mmu_idx; env = cpu->env_ptr; + qemu_spin_lock(&env->tlb_c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { unsigned int i; + unsigned int n = tlb_n_entries(env, mmu_idx); - for (i = 0; i < CPU_TLB_SIZE; i++) { - tlb_reset_dirty_range(&env->tlb_table[mmu_idx][i], - start1, length); + for (i = 0; i < n; i++) { + tlb_reset_dirty_range_locked(&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); + tlb_reset_dirty_range_locked(&env->tlb_v_table[mmu_idx][i], start1, + length); } } + qemu_spin_unlock(&env->tlb_c.lock); } -static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr) +/* Called with tlb_c.lock held */ +static inline void tlb_set_dirty1_locked(CPUTLBEntry *tlb_entry, + target_ulong vaddr) { if (tlb_entry->addr_write == (vaddr | TLB_NOTDIRTY)) { tlb_entry->addr_write = vaddr; @@ -551,46 +641,47 @@ static inline void tlb_set_dirty1(CPUTLBEntry *tlb_entry, target_ulong vaddr) void tlb_set_dirty(CPUState *cpu, target_ulong vaddr) { CPUArchState *env = cpu->env_ptr; - int i; int mmu_idx; assert_cpu_is_self(cpu); vaddr &= TARGET_PAGE_MASK; - i = (vaddr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); + qemu_spin_lock(&env->tlb_c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { - tlb_set_dirty1(&env->tlb_table[mmu_idx][i], vaddr); + tlb_set_dirty1_locked(tlb_entry(env, mmu_idx, vaddr), vaddr); } for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { int k; for (k = 0; k < CPU_VTLB_SIZE; k++) { - tlb_set_dirty1(&env->tlb_v_table[mmu_idx][k], vaddr); + tlb_set_dirty1_locked(&env->tlb_v_table[mmu_idx][k], vaddr); } } + qemu_spin_unlock(&env->tlb_c.lock); } /* Our TLB does not support large pages, so remember the area covered by large pages and trigger a full TLB flush if these are invalidated. */ -static void tlb_add_large_page(CPUArchState *env, target_ulong vaddr, - target_ulong size) +static void tlb_add_large_page(CPUArchState *env, int mmu_idx, + target_ulong vaddr, target_ulong size) { - target_ulong mask = ~(size - 1); + target_ulong lp_addr = env->tlb_d[mmu_idx].large_page_addr; + target_ulong lp_mask = ~(size - 1); - if (env->tlb_flush_addr == (target_ulong)-1) { - env->tlb_flush_addr = vaddr & mask; - env->tlb_flush_mask = mask; - return; + if (lp_addr == (target_ulong)-1) { + /* No previous large page. */ + lp_addr = vaddr; + } else { + /* Extend the existing region to include the new page. + This is a compromise between unnecessary flushes and + the cost of maintaining a full variable size TLB. */ + lp_mask &= env->tlb_d[mmu_idx].large_page_mask; + while (((lp_addr ^ vaddr) & lp_mask) != 0) { + lp_mask <<= 1; + } } - /* Extend the existing region to include the new page. - This is a compromise between unnecessary flushes and the cost - of maintaining a full variable size TLB. */ - mask &= env->tlb_flush_mask; - while (((env->tlb_flush_addr ^ vaddr) & mask) != 0) { - mask <<= 1; - } - env->tlb_flush_addr &= mask; - env->tlb_flush_mask = mask; + env->tlb_d[mmu_idx].large_page_addr = lp_addr & lp_mask; + env->tlb_d[mmu_idx].large_page_mask = lp_mask; } /* Add a new TLB entry. At most one entry for a given virtual address @@ -617,12 +708,10 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, assert_cpu_is_self(cpu); - if (size < TARGET_PAGE_SIZE) { + if (size <= TARGET_PAGE_SIZE) { sz = TARGET_PAGE_SIZE; } else { - if (size > TARGET_PAGE_SIZE) { - tlb_add_large_page(env, vaddr, size); - } + tlb_add_large_page(env, mmu_idx, vaddr, size); sz = size; } vaddr_page = vaddr & TARGET_PAGE_MASK; @@ -654,27 +743,40 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, addend = (uintptr_t)memory_region_get_ram_ptr(section->mr) + xlat; } - /* Make sure there's no cached translation for the new page. */ - tlb_flush_vtlb_page(env, mmu_idx, vaddr_page); - code_address = address; iotlb = memory_region_section_get_iotlb(cpu, section, vaddr_page, paddr_page, xlat, prot, &address); - index = (vaddr_page >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - te = &env->tlb_table[mmu_idx][index]; + index = tlb_index(env, mmu_idx, vaddr_page); + te = tlb_entry(env, mmu_idx, vaddr_page); + + /* + * Hold the TLB lock for the rest of the function. We could acquire/release + * the lock several times in the function, but it is faster to amortize the + * acquisition cost by acquiring it just once. Note that this leads to + * a longer critical section, but this is not a concern since the TLB lock + * is unlikely to be contended. + */ + qemu_spin_lock(&env->tlb_c.lock); + + /* Note that the tlb is no longer clean. */ + env->tlb_c.dirty |= 1 << mmu_idx; + + /* Make sure there's no cached translation for the new page. */ + tlb_flush_vtlb_page_locked(env, mmu_idx, vaddr_page); /* * Only evict the old entry to the victim tlb if it's for a * different page; otherwise just overwrite the stale data. */ - if (!tlb_hit_page_anyprot(te, vaddr_page)) { - unsigned vidx = env->vtlb_index++ % CPU_VTLB_SIZE; + if (!tlb_hit_page_anyprot(te, vaddr_page) && !tlb_entry_is_empty(te)) { + unsigned vidx = env->tlb_d[mmu_idx].vindex++ % CPU_VTLB_SIZE; CPUTLBEntry *tv = &env->tlb_v_table[mmu_idx][vidx]; /* Evict the old entry into the victim tlb. */ - copy_tlb_helper(tv, te, true); + copy_tlb_helper_locked(tv, te); env->iotlb_v[mmu_idx][vidx] = env->iotlb[mmu_idx][index]; + tlb_n_used_entries_dec(env, mmu_idx); } /* refill the tlb */ @@ -725,9 +827,9 @@ void tlb_set_page_with_attrs(CPUState *cpu, target_ulong vaddr, } } - /* Pairs with flag setting in tlb_reset_dirty_range */ - copy_tlb_helper(te, &tn, true); - /* atomic_mb_set(&te->addr_write, write_address); */ + copy_tlb_helper_locked(te, &tn); + tlb_n_used_entries_inc(env, mmu_idx); + qemu_spin_unlock(&env->tlb_c.lock); } /* Add a new TLB entry, but without specifying the memory @@ -741,39 +843,6 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr, prot, mmu_idx, size); } -static void report_bad_exec(CPUState *cpu, target_ulong addr) -{ - /* Accidentally executing outside RAM or ROM is quite common for - * several user-error situations, so report it in a way that - * makes it clear that this isn't a QEMU bug and provide suggestions - * about what a user could do to fix things. - */ - error_report("Trying to execute code outside RAM or ROM at 0x" - TARGET_FMT_lx, addr); - error_printf("This usually means one of the following happened:\n\n" - "(1) You told QEMU to execute a kernel for the wrong machine " - "type, and it crashed on startup (eg trying to run a " - "raspberry pi kernel on a versatilepb QEMU machine)\n" - "(2) You didn't give QEMU a kernel or BIOS filename at all, " - "and QEMU executed a ROM full of no-op instructions until " - "it fell off the end\n" - "(3) Your guest kernel has a bug and crashed by jumping " - "off into nowhere\n\n" - "This is almost always one of the first two, so check your " - "command line and that you are using the right type of kernel " - "for this machine.\n" - "If you think option (3) is likely then you can try debugging " - "your guest with the -d debug options; in particular " - "-d guest_errors will cause the log to include a dump of the " - "guest register state at this point.\n\n" - "Execution cannot continue; stopping here.\n\n"); - - /* Report also to the logs, with more detail including register dump */ - qemu_log_mask(LOG_GUEST_ERROR, "qemu: fatal: Trying to execute code " - "outside RAM or ROM at 0x" TARGET_FMT_lx "\n", addr); - log_cpu_state_mask(LOG_GUEST_ERROR, cpu, CPU_DUMP_FPU | CPU_DUMP_CCOP); -} - static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) { ram_addr_t ram_addr; @@ -789,7 +858,7 @@ static inline ram_addr_t qemu_ram_addr_from_host_nofail(void *ptr) static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, int mmu_idx, target_ulong addr, uintptr_t retaddr, - bool recheck, int size) + bool recheck, MMUAccessType access_type, int size) { CPUState *cpu = ENV_GET_CPU(env); hwaddr mr_offset; @@ -806,16 +875,16 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, * repeat the MMU check here. This tlb_fill() call might * longjump out if this access should cause a guest exception. */ - int index; + CPUTLBEntry *entry; target_ulong tlb_addr; tlb_fill(cpu, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); - index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addr = env->tlb_table[mmu_idx][index].addr_read; + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = entry->addr_read; if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { /* RAM access */ - uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; + uintptr_t haddr = addr + entry->addend; return ldn_p((void *)haddr, size); } @@ -831,6 +900,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, } cpu->mem_io_vaddr = addr; + cpu->mem_io_access_type = access_type; if (mr->global_locking && !qemu_mutex_iothread_locked()) { qemu_mutex_lock_iothread(); @@ -843,7 +913,7 @@ static uint64_t io_readx(CPUArchState *env, CPUIOTLBEntry *iotlbentry, section->offset_within_address_space - section->offset_within_region; - cpu_transaction_failed(cpu, physaddr, addr, size, MMU_DATA_LOAD, + cpu_transaction_failed(cpu, physaddr, addr, size, access_type, mmu_idx, iotlbentry->attrs, r, retaddr); } if (locked) { @@ -872,16 +942,16 @@ static void io_writex(CPUArchState *env, CPUIOTLBEntry *iotlbentry, * repeat the MMU check here. This tlb_fill() call might * longjump out if this access should cause a guest exception. */ - int index; + CPUTLBEntry *entry; target_ulong tlb_addr; tlb_fill(cpu, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); - index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + entry = tlb_entry(env, mmu_idx, addr); + tlb_addr = tlb_addr_write(entry); if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { /* RAM access */ - uintptr_t haddr = addr + env->tlb_table[mmu_idx][index].addend; + uintptr_t haddr = addr + entry->addend; stn_p((void *)haddr, size, val); return; @@ -923,17 +993,28 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, size_t elt_ofs, target_ulong page) { size_t vidx; + + assert_cpu_is_self(ENV_GET_CPU(env)); for (vidx = 0; vidx < CPU_VTLB_SIZE; ++vidx) { CPUTLBEntry *vtlb = &env->tlb_v_table[mmu_idx][vidx]; - target_ulong cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs); + target_ulong cmp; + + /* elt_ofs might correspond to .addr_write, so use atomic_read */ +#if TCG_OVERSIZED_GUEST + cmp = *(target_ulong *)((uintptr_t)vtlb + elt_ofs); +#else + cmp = atomic_read((target_ulong *)((uintptr_t)vtlb + elt_ofs)); +#endif if (cmp == page) { /* Found entry in victim tlb, swap tlb and iotlb. */ CPUTLBEntry tmptlb, *tlb = &env->tlb_table[mmu_idx][index]; - copy_tlb_helper(&tmptlb, tlb, false); - copy_tlb_helper(tlb, vtlb, true); - copy_tlb_helper(vtlb, &tmptlb, true); + qemu_spin_lock(&env->tlb_c.lock); + copy_tlb_helper_locked(&tmptlb, tlb); + copy_tlb_helper_locked(tlb, vtlb); + copy_tlb_helper_locked(vtlb, &tmptlb); + qemu_spin_unlock(&env->tlb_c.lock); CPUIOTLBEntry tmpio, *io = &env->iotlb[mmu_idx][index]; CPUIOTLBEntry *vio = &env->iotlb_v[mmu_idx][vidx]; @@ -956,87 +1037,33 @@ static bool victim_tlb_hit(CPUArchState *env, size_t mmu_idx, size_t index, */ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) { - int mmu_idx, index; + uintptr_t mmu_idx = cpu_mmu_index(env, true); + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); void *p; - MemoryRegion *mr; - MemoryRegionSection *section; - CPUState *cpu = ENV_GET_CPU(env); - CPUIOTLBEntry *iotlbentry; - hwaddr physaddr, mr_offset; - index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - mmu_idx = cpu_mmu_index(env, true); - if (unlikely(!tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr))) { + if (unlikely(!tlb_hit(entry->addr_code, addr))) { if (!VICTIM_TLB_HIT(addr_code, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 0, MMU_INST_FETCH, mmu_idx, 0); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); } - assert(tlb_hit(env->tlb_table[mmu_idx][index].addr_code, addr)); + assert(tlb_hit(entry->addr_code, addr)); } - if (unlikely(env->tlb_table[mmu_idx][index].addr_code & TLB_RECHECK)) { + if (unlikely(entry->addr_code & (TLB_RECHECK | TLB_MMIO))) { /* - * This is a TLB_RECHECK access, where the MMU protection - * covers a smaller range than a target page, and we must - * repeat the MMU check here. This tlb_fill() call might - * longjump out if this access should cause a guest exception. - */ - int index; - target_ulong tlb_addr; - - tlb_fill(cpu, addr, 0, MMU_INST_FETCH, mmu_idx, 0); - - index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addr = env->tlb_table[mmu_idx][index].addr_code; - if (!(tlb_addr & ~(TARGET_PAGE_MASK | TLB_RECHECK))) { - /* RAM access. We can't handle this, so for now just stop */ - cpu_abort(cpu, "Unable to handle guest executing from RAM within " - "a small MPU region at 0x" TARGET_FMT_lx, addr); - } - /* - * Fall through to handle IO accesses (which will almost certainly - * also result in failure) + * Return -1 if we can't translate and execute from an entire + * page of RAM here, which will cause us to execute by loading + * and translating one insn at a time, without caching: + * - TLB_RECHECK: means the MMU protection covers a smaller range + * than a target page, so we must redo the MMU check every insn + * - TLB_MMIO: region is not backed by RAM */ + return -1; } - iotlbentry = &env->iotlb[mmu_idx][index]; - section = iotlb_to_section(cpu, iotlbentry->addr, iotlbentry->attrs); - mr = section->mr; - if (memory_region_is_unassigned(mr)) { - qemu_mutex_lock_iothread(); - if (memory_region_request_mmio_ptr(mr, addr)) { - qemu_mutex_unlock_iothread(); - /* A MemoryRegion is potentially added so re-run the - * get_page_addr_code. - */ - return get_page_addr_code(env, addr); - } - qemu_mutex_unlock_iothread(); - - /* Give the new-style cpu_transaction_failed() hook first chance - * to handle this. - * This is not the ideal place to detect and generate CPU - * exceptions for instruction fetch failure (for instance - * we don't know the length of the access that the CPU would - * use, and it would be better to go ahead and try the access - * and use the MemTXResult it produced). However it is the - * simplest place we have currently available for the check. - */ - mr_offset = (iotlbentry->addr & TARGET_PAGE_MASK) + addr; - physaddr = mr_offset + - section->offset_within_address_space - - section->offset_within_region; - cpu_transaction_failed(cpu, physaddr, addr, 0, MMU_INST_FETCH, mmu_idx, - iotlbentry->attrs, MEMTX_DECODE_ERROR, 0); - - cpu_unassigned_access(cpu, addr, false, true, 0, 4); - /* The CPU's unassigned access hook might have longjumped out - * with an exception. If it didn't (or there was no hook) then - * we can't proceed further. - */ - report_bad_exec(cpu, addr); - exit(1); - } - p = (void *)((uintptr_t)addr + env->tlb_table[mmu_idx][index].addend); + p = (void *)((uintptr_t)addr + entry->addend); return qemu_ram_addr_from_host_nofail(p); } @@ -1049,10 +1076,10 @@ tb_page_addr_t get_page_addr_code(CPUArchState *env, target_ulong addr) void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr) { - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); - if (!tlb_hit(tlb_addr, addr)) { + if (!tlb_hit(tlb_addr_write(entry), addr)) { /* TLB entry is for a different page */ if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, size, MMU_DATA_STORE, @@ -1068,9 +1095,9 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, NotDirtyInfo *ndi) { size_t mmu_idx = get_mmuidx(oi); - size_t index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - CPUTLBEntry *tlbe = &env->tlb_table[mmu_idx][index]; - target_ulong tlb_addr = tlbe->addr_write; + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *tlbe = tlb_entry(env, mmu_idx, addr); + target_ulong tlb_addr = tlb_addr_write(tlbe); TCGMemOp mop = get_memop(oi); int a_bits = get_alignment_bits(mop); int s_bits = mop & MO_SIZE; @@ -1100,8 +1127,10 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, 1 << s_bits, MMU_DATA_STORE, mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + tlbe = tlb_entry(env, mmu_idx, addr); } - tlb_addr = tlbe->addr_write & ~TLB_INVALID_MASK; + tlb_addr = tlb_addr_write(tlbe) & ~TLB_INVALID_MASK; } /* Notice an IO access or a needs-MMU-lookup access */ @@ -1188,7 +1217,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, #include "atomic_template.h" #endif -#ifdef CONFIG_ATOMIC128 +#if HAVE_CMPXCHG128 || HAVE_ATOMIC128 #define DATA_SIZE 16 #include "atomic_template.h" #endif diff --git a/accel/tcg/softmmu_template.h b/accel/tcg/softmmu_template.h index badbf14880..e970a8b378 100644 --- a/accel/tcg/softmmu_template.h +++ b/accel/tcg/softmmu_template.h @@ -11,7 +11,7 @@ * 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. + * 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 @@ -99,20 +99,22 @@ static inline DATA_TYPE glue(io_read, SUFFIX)(CPUArchState *env, size_t mmu_idx, size_t index, target_ulong addr, uintptr_t retaddr, - bool recheck) + bool recheck, + MMUAccessType access_type) { CPUIOTLBEntry *iotlbentry = &env->iotlb[mmu_idx][index]; return io_readx(env, iotlbentry, mmu_idx, addr, retaddr, recheck, - DATA_SIZE); + access_type, DATA_SIZE); } #endif WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, uintptr_t retaddr) { - unsigned mmu_idx = get_mmuidx(oi); - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; + uintptr_t mmu_idx = get_mmuidx(oi); + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + target_ulong tlb_addr = entry->ADDR_READ; unsigned a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; DATA_TYPE res; @@ -127,8 +129,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); } - tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; + tlb_addr = entry->ADDR_READ; } /* Handle an IO access. */ @@ -140,7 +144,8 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, - tlb_addr & TLB_RECHECK); + tlb_addr & TLB_RECHECK, + READ_ACCESS_TYPE); res = TGT_LE(res); return res; } @@ -164,7 +169,7 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, return res; } - haddr = addr + env->tlb_table[mmu_idx][index].addend; + haddr = addr + entry->addend; #if DATA_SIZE == 1 res = glue(glue(ld, LSUFFIX), _p)((uint8_t *)haddr); #else @@ -177,9 +182,10 @@ WORD_TYPE helper_le_ld_name(CPUArchState *env, target_ulong addr, WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, TCGMemOpIdx oi, uintptr_t retaddr) { - unsigned mmu_idx = get_mmuidx(oi); - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - target_ulong tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; + uintptr_t mmu_idx = get_mmuidx(oi); + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + target_ulong tlb_addr = entry->ADDR_READ; unsigned a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; DATA_TYPE res; @@ -194,8 +200,10 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, if (!VICTIM_TLB_HIT(ADDR_READ, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, READ_ACCESS_TYPE, mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); } - tlb_addr = env->tlb_table[mmu_idx][index].ADDR_READ; + tlb_addr = entry->ADDR_READ; } /* Handle an IO access. */ @@ -207,7 +215,8 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, /* ??? Note that the io helpers always read data in the target byte ordering. We should push the LE/BE request down into io. */ res = glue(io_read, SUFFIX)(env, mmu_idx, index, addr, retaddr, - tlb_addr & TLB_RECHECK); + tlb_addr & TLB_RECHECK, + READ_ACCESS_TYPE); res = TGT_BE(res); return res; } @@ -231,7 +240,7 @@ WORD_TYPE helper_be_ld_name(CPUArchState *env, target_ulong addr, return res; } - haddr = addr + env->tlb_table[mmu_idx][index].addend; + haddr = addr + entry->addend; res = glue(glue(ld, LSUFFIX), _be_p)((uint8_t *)haddr); return res; } @@ -272,9 +281,10 @@ static inline void glue(io_write, SUFFIX)(CPUArchState *env, void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) { - unsigned mmu_idx = get_mmuidx(oi); - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + uintptr_t mmu_idx = get_mmuidx(oi); + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + target_ulong tlb_addr = tlb_addr_write(entry); unsigned a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; @@ -288,8 +298,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); } - tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; + tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; } /* Handle an IO access. */ @@ -310,16 +322,16 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (DATA_SIZE > 1 && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 >= TARGET_PAGE_SIZE)) { - int i, index2; - target_ulong page2, tlb_addr2; + int i; + target_ulong page2; + CPUTLBEntry *entry2; do_unaligned_access: /* Ensure the second page is in the TLB. Note that the first page is already guaranteed to be filled, and that the second page cannot evict the first. */ page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; - index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; - if (!tlb_hit_page(tlb_addr2, page2) + entry2 = tlb_entry(env, mmu_idx, page2); + if (!tlb_hit_page(tlb_addr_write(entry2), page2) && !VICTIM_TLB_HIT(addr_write, page2)) { tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -337,7 +349,7 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } - haddr = addr + env->tlb_table[mmu_idx][index].addend; + haddr = addr + entry->addend; #if DATA_SIZE == 1 glue(glue(st, SUFFIX), _p)((uint8_t *)haddr, val); #else @@ -349,9 +361,10 @@ void helper_le_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, TCGMemOpIdx oi, uintptr_t retaddr) { - unsigned mmu_idx = get_mmuidx(oi); - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - target_ulong tlb_addr = env->tlb_table[mmu_idx][index].addr_write; + uintptr_t mmu_idx = get_mmuidx(oi); + uintptr_t index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + target_ulong tlb_addr = tlb_addr_write(entry); unsigned a_bits = get_alignment_bits(get_memop(oi)); uintptr_t haddr; @@ -365,8 +378,10 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (!VICTIM_TLB_HIT(addr_write, addr)) { tlb_fill(ENV_GET_CPU(env), addr, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); + index = tlb_index(env, mmu_idx, addr); + entry = tlb_entry(env, mmu_idx, addr); } - tlb_addr = env->tlb_table[mmu_idx][index].addr_write & ~TLB_INVALID_MASK; + tlb_addr = tlb_addr_write(entry) & ~TLB_INVALID_MASK; } /* Handle an IO access. */ @@ -387,16 +402,16 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, if (DATA_SIZE > 1 && unlikely((addr & ~TARGET_PAGE_MASK) + DATA_SIZE - 1 >= TARGET_PAGE_SIZE)) { - int i, index2; - target_ulong page2, tlb_addr2; + int i; + target_ulong page2; + CPUTLBEntry *entry2; do_unaligned_access: /* Ensure the second page is in the TLB. Note that the first page is already guaranteed to be filled, and that the second page cannot evict the first. */ page2 = (addr + DATA_SIZE) & TARGET_PAGE_MASK; - index2 = (page2 >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - tlb_addr2 = env->tlb_table[mmu_idx][index2].addr_write; - if (!tlb_hit_page(tlb_addr2, page2) + entry2 = tlb_entry(env, mmu_idx, page2); + if (!tlb_hit_page(tlb_addr_write(entry2), page2) && !VICTIM_TLB_HIT(addr_write, page2)) { tlb_fill(ENV_GET_CPU(env), page2, DATA_SIZE, MMU_DATA_STORE, mmu_idx, retaddr); @@ -414,7 +429,7 @@ void helper_be_st_name(CPUArchState *env, target_ulong addr, DATA_TYPE val, return; } - haddr = addr + env->tlb_table[mmu_idx][index].addend; + haddr = addr + entry->addend; glue(glue(st, SUFFIX), _be_p)((uint8_t *)haddr, val); } #endif /* DATA_SIZE > 1 */ diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 56dbb56a16..3d25bdcc17 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -51,7 +51,7 @@ static void tcg_handle_interrupt(CPUState *cpu, int mask) if (!qemu_cpu_is_self(cpu)) { qemu_cpu_kick(cpu); } else { - cpu->icount_decr.u16.high = -1; + atomic_set(&cpu->icount_decr.u16.high, -1); if (use_icount && !cpu->can_do_io && (mask & ~old_mask) != 0) { diff --git a/accel/tcg/tcg-runtime-gvec.c b/accel/tcg/tcg-runtime-gvec.c index 90340e56e0..e2c6f24262 100644 --- a/accel/tcg/tcg-runtime-gvec.c +++ b/accel/tcg/tcg-runtime-gvec.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -512,6 +512,39 @@ void HELPER(gvec_orc)(void *d, void *a, void *b, uint32_t desc) clear_high(d, oprsz, desc); } +void HELPER(gvec_nand)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(vec64)) { + *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) & *(vec64 *)(b + i)); + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_nor)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(vec64)) { + *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) | *(vec64 *)(b + i)); + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_eqv)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(vec64)) { + *(vec64 *)(d + i) = ~(*(vec64 *)(a + i) ^ *(vec64 *)(b + i)); + } + clear_high(d, oprsz, desc); +} + void HELPER(gvec_ands)(void *d, void *a, uint64_t b, uint32_t desc) { intptr_t oprsz = simd_oprsz(desc); @@ -995,3 +1028,227 @@ void HELPER(gvec_ussub64)(void *d, void *a, void *b, uint32_t desc) } clear_high(d, oprsz, desc); } + +void HELPER(gvec_smin8)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int8_t)) { + int8_t aa = *(int8_t *)(a + i); + int8_t bb = *(int8_t *)(b + i); + int8_t dd = aa < bb ? aa : bb; + *(int8_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smin16)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int16_t)) { + int16_t aa = *(int16_t *)(a + i); + int16_t bb = *(int16_t *)(b + i); + int16_t dd = aa < bb ? aa : bb; + *(int16_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smin32)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int32_t)) { + int32_t aa = *(int32_t *)(a + i); + int32_t bb = *(int32_t *)(b + i); + int32_t dd = aa < bb ? aa : bb; + *(int32_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smin64)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int64_t)) { + int64_t aa = *(int64_t *)(a + i); + int64_t bb = *(int64_t *)(b + i); + int64_t dd = aa < bb ? aa : bb; + *(int64_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smax8)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int8_t)) { + int8_t aa = *(int8_t *)(a + i); + int8_t bb = *(int8_t *)(b + i); + int8_t dd = aa > bb ? aa : bb; + *(int8_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smax16)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int16_t)) { + int16_t aa = *(int16_t *)(a + i); + int16_t bb = *(int16_t *)(b + i); + int16_t dd = aa > bb ? aa : bb; + *(int16_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smax32)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int32_t)) { + int32_t aa = *(int32_t *)(a + i); + int32_t bb = *(int32_t *)(b + i); + int32_t dd = aa > bb ? aa : bb; + *(int32_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_smax64)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(int64_t)) { + int64_t aa = *(int64_t *)(a + i); + int64_t bb = *(int64_t *)(b + i); + int64_t dd = aa > bb ? aa : bb; + *(int64_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umin8)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + uint8_t aa = *(uint8_t *)(a + i); + uint8_t bb = *(uint8_t *)(b + i); + uint8_t dd = aa < bb ? aa : bb; + *(uint8_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umin16)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + uint16_t aa = *(uint16_t *)(a + i); + uint16_t bb = *(uint16_t *)(b + i); + uint16_t dd = aa < bb ? aa : bb; + *(uint16_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umin32)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + uint32_t aa = *(uint32_t *)(a + i); + uint32_t bb = *(uint32_t *)(b + i); + uint32_t dd = aa < bb ? aa : bb; + *(uint32_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umin64)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t aa = *(uint64_t *)(a + i); + uint64_t bb = *(uint64_t *)(b + i); + uint64_t dd = aa < bb ? aa : bb; + *(uint64_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umax8)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint8_t)) { + uint8_t aa = *(uint8_t *)(a + i); + uint8_t bb = *(uint8_t *)(b + i); + uint8_t dd = aa > bb ? aa : bb; + *(uint8_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umax16)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint16_t)) { + uint16_t aa = *(uint16_t *)(a + i); + uint16_t bb = *(uint16_t *)(b + i); + uint16_t dd = aa > bb ? aa : bb; + *(uint16_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umax32)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint32_t)) { + uint32_t aa = *(uint32_t *)(a + i); + uint32_t bb = *(uint32_t *)(b + i); + uint32_t dd = aa > bb ? aa : bb; + *(uint32_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} + +void HELPER(gvec_umax64)(void *d, void *a, void *b, uint32_t desc) +{ + intptr_t oprsz = simd_oprsz(desc); + intptr_t i; + + for (i = 0; i < oprsz; i += sizeof(uint64_t)) { + uint64_t aa = *(uint64_t *)(a + i); + uint64_t bb = *(uint64_t *)(b + i); + uint64_t dd = aa > bb ? aa : bb; + *(uint64_t *)(d + i) = dd; + } + clear_high(d, oprsz, desc); +} diff --git a/accel/tcg/tcg-runtime.h b/accel/tcg/tcg-runtime.h index 1bd39d136d..dfe325625c 100644 --- a/accel/tcg/tcg-runtime.h +++ b/accel/tcg/tcg-runtime.h @@ -200,6 +200,26 @@ DEF_HELPER_FLAGS_4(gvec_ussub16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ussub32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ussub64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_smax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umin8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umin16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umin32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umin64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umax8, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umax16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umax32, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umax64, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(gvec_neg8, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg16, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_neg32, TCG_CALL_NO_RWG, void, ptr, ptr, i32) @@ -211,6 +231,9 @@ DEF_HELPER_FLAGS_4(gvec_or, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_xor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_andc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_orc, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_nand, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_nor, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_eqv, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_ands, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(gvec_xors, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) diff --git a/accel/tcg/trace-events b/accel/tcg/trace-events index c22ad60af7..01852217a6 100644 --- a/accel/tcg/trace-events +++ b/accel/tcg/trace-events @@ -1,4 +1,4 @@ -# Trace events for debugging and performance instrumentation +# See docs/devel/tracing.txt for syntax documentation. # TCG related tracing (mostly disabled by default) # cpu-exec.c diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 1571987113..8f593b926f 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -16,12 +16,8 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, see . */ -#ifdef _WIN32 -#include -#endif #include "qemu/osdep.h" - #include "qemu-common.h" #define NO_CPU_IO_DEFS #include "cpu.h" @@ -1282,8 +1278,7 @@ void tb_flush(CPUState *cpu) */ #ifdef CONFIG_USER_ONLY -static void -do_tb_invalidate_check(struct qht *ht, void *p, uint32_t hash, void *userp) +static void do_tb_invalidate_check(void *p, uint32_t hash, void *userp) { TranslationBlock *tb = p; target_ulong addr = *(target_ulong *)userp; @@ -1304,8 +1299,7 @@ static void tb_invalidate_check(target_ulong address) qht_iter(&tb_ctx.htable, do_tb_invalidate_check, &address); } -static void -do_tb_page_check(struct qht *ht, void *p, uint32_t hash, void *userp) +static void do_tb_page_check(void *p, uint32_t hash, void *userp) { TranslationBlock *tb = p; int flags1, flags2; @@ -1493,7 +1487,7 @@ static void tb_phys_invalidate__locked(TranslationBlock *tb) */ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr) { - if (page_addr == -1) { + if (page_addr == -1 && tb->page_addr[0] != -1) { page_lock_tb(tb); do_tb_phys_invalidate(tb, true); page_unlock_tb(tb); @@ -1608,6 +1602,17 @@ tb_link_page(TranslationBlock *tb, tb_page_addr_t phys_pc, assert_memory_lock(); + if (phys_pc == -1) { + /* + * If the TB is not associated with a physical RAM page then + * it must be a temporary one-insn TB, and we have nothing to do + * except fill in the page_addr[] fields. + */ + assert(tb->cflags & CF_NOCACHE); + tb->page_addr[0] = tb->page_addr[1] = -1; + return tb; + } + /* * Add the TB to the page list, acquiring first the pages's locks. * We keep the locks held until after inserting the TB in the hash table, @@ -1677,6 +1682,15 @@ TranslationBlock *tb_gen_code(CPUState *cpu, phys_pc = get_page_addr_code(env, pc); + if (phys_pc == -1) { + /* Generate a temporary TB with 1 insn in it */ + cflags &= ~CF_COUNT_MASK; + cflags |= CF_NOCACHE | 1; + } + + cflags &= ~CF_CLUSTER_MASK; + cflags |= cpu->cluster_index << CF_CLUSTER_SHIFT; + buffer_overflow: tb = tb_alloc(pc); if (unlikely(!tb)) { @@ -1994,15 +2008,6 @@ void tb_invalidate_phys_page_fast(struct page_collection *pages, { PageDesc *p; -#if 0 - if (1) { - qemu_log("modifying code at 0x%x size=%d EIP=%x PC=%08x\n", - cpu_single_env->mem_io_vaddr, len, - cpu_single_env->eip, - cpu_single_env->eip + - (intptr_t)cpu_single_env->segs[R_CS].base); - } -#endif assert_memory_lock(); p = page_find(start >> TARGET_PAGE_BITS); @@ -2121,7 +2126,9 @@ void tb_check_watchpoint(CPUState *cpu) cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); addr = get_page_addr_code(env, pc); - tb_invalidate_phys_range(addr, addr + 1); + if (addr != -1) { + tb_invalidate_phys_range(addr, addr + 1); + } } } @@ -2282,7 +2289,7 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) { struct tb_tree_stats tst = {}; struct qht_stats hst; - size_t nb_tbs; + size_t nb_tbs, flush_full, flush_part, flush_elide; tcg_tb_foreach(tb_tree_stats_iter, &tst); nb_tbs = tst.nb_tbs; @@ -2318,7 +2325,11 @@ void dump_exec_info(FILE *f, fprintf_function cpu_fprintf) cpu_fprintf(f, "TB flush count %u\n", atomic_read(&tb_ctx.tb_flush_count)); cpu_fprintf(f, "TB invalidate count %zu\n", tcg_tb_phys_invalidate_count()); - cpu_fprintf(f, "TLB flush count %zu\n", tlb_flush_count()); + + tlb_flush_counts(&flush_full, &flush_part, &flush_elide); + cpu_fprintf(f, "TLB full flushes %zu\n", flush_full); + cpu_fprintf(f, "TLB partial flushes %zu\n", flush_part); + cpu_fprintf(f, "TLB elided flushes %zu\n", flush_elide); tcg_dump_info(f, cpu_fprintf); } @@ -2333,7 +2344,7 @@ void cpu_interrupt(CPUState *cpu, int mask) { g_assert(qemu_mutex_iothread_locked()); cpu->interrupt_request |= mask; - cpu->icount_decr.u16.high = -1; + atomic_set(&cpu->icount_decr.u16.high, -1); } /* diff --git a/accel/tcg/translate-all.h b/accel/tcg/translate-all.h index 08e2f23a46..64f5fd9a05 100644 --- a/accel/tcg/translate-all.h +++ b/accel/tcg/translate-all.h @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 0f9dca9113..afd0a49ea6 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -34,6 +34,8 @@ void translator_loop_temp_check(DisasContextBase *db) void translator_loop(const TranslatorOps *ops, DisasContextBase *db, CPUState *cpu, TranslationBlock *tb) { + int bp_insn = 0; + /* Initialize DisasContext */ db->tb = tb; db->pc_first = tb->pc; @@ -71,11 +73,13 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ /* Pass breakpoint hits to target for further processing */ - if (unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { + if (!db->singlestep_enabled + && unlikely(!QTAILQ_EMPTY(&cpu->breakpoints))) { CPUBreakpoint *bp; QTAILQ_FOREACH(bp, &cpu->breakpoints, entry) { if (bp->pc == db->pc_next) { if (ops->breakpoint_check(db, cpu, bp)) { + bp_insn = 1; break; } } @@ -118,7 +122,7 @@ void translator_loop(const TranslatorOps *ops, DisasContextBase *db, /* Emit code to exit the TB, as indicated by db->is_jmp. */ ops->tb_stop(db, cpu); - gen_tb_end(db->tb, db->num_insns); + gen_tb_end(db->tb, db->num_insns - bp_insn); /* The disas_log hook may use these values rather than recompute. */ db->tb->size = db->pc_next - db->pc_first; diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 26a3ffbba1..0789984fe6 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -25,6 +25,7 @@ #include "exec/cpu_ldst.h" #include "translate-all.h" #include "exec/helper-proto.h" +#include "qemu/atomic128.h" #undef EAX #undef ECX @@ -478,28 +479,66 @@ int cpu_signal_handler(int host_signum, void *pinfo, #elif defined(__aarch64__) +#ifndef ESR_MAGIC +/* Pre-3.16 kernel headers don't have these, so provide fallback definitions */ +#define ESR_MAGIC 0x45535201 +struct esr_context { + struct _aarch64_ctx head; + uint64_t esr; +}; +#endif + +static inline struct _aarch64_ctx *first_ctx(ucontext_t *uc) +{ + return (struct _aarch64_ctx *)&uc->uc_mcontext.__reserved; +} + +static inline struct _aarch64_ctx *next_ctx(struct _aarch64_ctx *hdr) +{ + return (struct _aarch64_ctx *)((char *)hdr + hdr->size); +} + int cpu_signal_handler(int host_signum, void *pinfo, void *puc) { siginfo_t *info = pinfo; ucontext_t *uc = puc; uintptr_t pc = uc->uc_mcontext.pc; - uint32_t insn = *(uint32_t *)pc; bool is_write; + struct _aarch64_ctx *hdr; + struct esr_context const *esrctx = NULL; - /* XXX: need kernel patch to get write flag faster. */ - is_write = ( (insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */ - || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */ - || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */ - || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */ - || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */ - || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */ - || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */ - /* Ingore bits 10, 11 & 21, controlling indexing. */ - || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */ - || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */ - /* Ignore bits 23 & 24, controlling indexing. */ - || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ + /* Find the esr_context, which has the WnR bit in it */ + for (hdr = first_ctx(uc); hdr->magic; hdr = next_ctx(hdr)) { + if (hdr->magic == ESR_MAGIC) { + esrctx = (struct esr_context const *)hdr; + break; + } + } + if (esrctx) { + /* For data aborts ESR.EC is 0b10010x: then bit 6 is the WnR bit */ + uint64_t esr = esrctx->esr; + is_write = extract32(esr, 27, 5) == 0x12 && extract32(esr, 6, 1) == 1; + } else { + /* + * Fall back to parsing instructions; will only be needed + * for really ancient (pre-3.16) kernels. + */ + uint32_t insn = *(uint32_t *)pc; + + is_write = ((insn & 0xbfff0000) == 0x0c000000 /* C3.3.1 */ + || (insn & 0xbfe00000) == 0x0c800000 /* C3.3.2 */ + || (insn & 0xbfdf0000) == 0x0d000000 /* C3.3.3 */ + || (insn & 0xbfc00000) == 0x0d800000 /* C3.3.4 */ + || (insn & 0x3f400000) == 0x08000000 /* C3.3.6 */ + || (insn & 0x3bc00000) == 0x39000000 /* C3.3.13 */ + || (insn & 0x3fc00000) == 0x3d800000 /* ... 128bit */ + /* Ignore bits 10, 11 & 21, controlling indexing. */ + || (insn & 0x3bc00000) == 0x38000000 /* C3.3.8-12 */ + || (insn & 0x3fe00000) == 0x3c800000 /* ... 128bit */ + /* Ignore bits 23 & 24, controlling indexing. */ + || (insn & 0x3a400000) == 0x28000000); /* C3.3.7,14-16 */ + } return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); } @@ -570,6 +609,81 @@ int cpu_signal_handler(int host_signum, void *pinfo, return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); } +#elif defined(__riscv) + +int cpu_signal_handler(int host_signum, void *pinfo, + void *puc) +{ + siginfo_t *info = pinfo; + ucontext_t *uc = puc; + greg_t pc = uc->uc_mcontext.__gregs[REG_PC]; + uint32_t insn = *(uint32_t *)pc; + int is_write = 0; + + /* Detect store by reading the instruction at the program + counter. Note: we currently only generate 32-bit + instructions so we thus only detect 32-bit stores */ + switch (((insn >> 0) & 0b11)) { + case 3: + switch (((insn >> 2) & 0b11111)) { + case 8: + switch (((insn >> 12) & 0b111)) { + case 0: /* sb */ + case 1: /* sh */ + case 2: /* sw */ + case 3: /* sd */ + case 4: /* sq */ + is_write = 1; + break; + default: + break; + } + break; + case 9: + switch (((insn >> 12) & 0b111)) { + case 2: /* fsw */ + case 3: /* fsd */ + case 4: /* fsq */ + is_write = 1; + break; + default: + break; + } + break; + default: + break; + } + } + + /* Check for compressed instructions */ + switch (((insn >> 13) & 0b111)) { + case 7: + switch (insn & 0b11) { + case 0: /*c.sd */ + case 2: /* c.sdsp */ + is_write = 1; + break; + default: + break; + } + break; + case 6: + switch (insn & 0b11) { + case 0: /* c.sw */ + case 3: /* c.swsp */ + is_write = 1; + break; + default: + break; + } + break; + default: + break; + } + + return handle_cpu_signal(pc, info, is_write, &uc->uc_sigmask); +} + #else #error host CPU specific signal handler needed @@ -615,7 +729,7 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, /* The following is only callable from other helpers, and matches up with the softmmu version. */ -#ifdef CONFIG_ATOMIC128 +#if HAVE_ATOMIC128 || HAVE_CMPXCHG128 #undef EXTRA_ARGS #undef ATOMIC_NAME @@ -628,4 +742,4 @@ static void *atomic_mmu_lookup(CPUArchState *env, target_ulong addr, #define DATA_SIZE 16 #include "atomic_template.h" -#endif /* CONFIG_ATOMIC128 */ +#endif diff --git a/audio/Makefile.objs b/audio/Makefile.objs index db4fa7f18f..dca87f6347 100644 --- a/audio/Makefile.objs +++ b/audio/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y = audio.o noaudio.o wavaudio.o mixeng.o +common-obj-y = audio.o audio_legacy.o noaudio.o wavaudio.o mixeng.o common-obj-$(CONFIG_SPICE) += spiceaudio.o common-obj-$(CONFIG_AUDIO_COREAUDIO) += coreaudio.o common-obj-$(CONFIG_AUDIO_DSOUND) += dsoundaudio.o diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 362a2276fd..49e6884309 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -28,35 +28,14 @@ #include "audio.h" #include "trace.h" -#if QEMU_GNUC_PREREQ(4, 3) #pragma GCC diagnostic ignored "-Waddress" -#endif #define AUDIO_CAP "alsa" #include "audio_int.h" -typedef struct ALSAConf { - int size_in_usec_in; - int size_in_usec_out; - const char *pcm_name_in; - const char *pcm_name_out; - unsigned int buffer_size_in; - unsigned int period_size_in; - unsigned int buffer_size_out; - unsigned int period_size_out; - unsigned int threshold; - - int buffer_size_in_overridden; - int period_size_in_overridden; - - int buffer_size_out_overridden; - int period_size_out_overridden; -} ALSAConf; - struct pollhlp { snd_pcm_t *handle; struct pollfd *pfds; - ALSAConf *conf; int count; int mask; }; @@ -68,6 +47,7 @@ typedef struct ALSAVoiceOut { void *pcm_buf; snd_pcm_t *handle; struct pollhlp pollhlp; + Audiodev *dev; } ALSAVoiceOut; typedef struct ALSAVoiceIn { @@ -75,21 +55,18 @@ typedef struct ALSAVoiceIn { snd_pcm_t *handle; void *pcm_buf; struct pollhlp pollhlp; + Audiodev *dev; } ALSAVoiceIn; struct alsa_params_req { int freq; snd_pcm_format_t fmt; int nchannels; - int size_in_usec; - int override_mask; - unsigned int buffer_size; - unsigned int period_size; }; struct alsa_params_obt { int freq; - audfmt_e fmt; + AudioFormat fmt; int endianness; int nchannels; snd_pcm_uframes_t samples; @@ -296,16 +273,16 @@ static int alsa_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) +static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness) { switch (fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: return SND_PCM_FORMAT_S8; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: return SND_PCM_FORMAT_U8; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: if (endianness) { return SND_PCM_FORMAT_S16_BE; } @@ -313,7 +290,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) return SND_PCM_FORMAT_S16_LE; } - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: if (endianness) { return SND_PCM_FORMAT_U16_BE; } @@ -321,7 +298,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) return SND_PCM_FORMAT_U16_LE; } - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: if (endianness) { return SND_PCM_FORMAT_S32_BE; } @@ -329,7 +306,7 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) return SND_PCM_FORMAT_S32_LE; } - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: if (endianness) { return SND_PCM_FORMAT_U32_BE; } @@ -346,58 +323,58 @@ static snd_pcm_format_t aud_to_alsafmt (audfmt_e fmt, int endianness) } } -static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, +static int alsa_to_audfmt (snd_pcm_format_t alsafmt, AudioFormat *fmt, int *endianness) { switch (alsafmt) { case SND_PCM_FORMAT_S8: *endianness = 0; - *fmt = AUD_FMT_S8; + *fmt = AUDIO_FORMAT_S8; break; case SND_PCM_FORMAT_U8: *endianness = 0; - *fmt = AUD_FMT_U8; + *fmt = AUDIO_FORMAT_U8; break; case SND_PCM_FORMAT_S16_LE: *endianness = 0; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case SND_PCM_FORMAT_U16_LE: *endianness = 0; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case SND_PCM_FORMAT_S16_BE: *endianness = 1; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case SND_PCM_FORMAT_U16_BE: *endianness = 1; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case SND_PCM_FORMAT_S32_LE: *endianness = 0; - *fmt = AUD_FMT_S32; + *fmt = AUDIO_FORMAT_S32; break; case SND_PCM_FORMAT_U32_LE: *endianness = 0; - *fmt = AUD_FMT_U32; + *fmt = AUDIO_FORMAT_U32; break; case SND_PCM_FORMAT_S32_BE: *endianness = 1; - *fmt = AUD_FMT_S32; + *fmt = AUDIO_FORMAT_S32; break; case SND_PCM_FORMAT_U32_BE: *endianness = 1; - *fmt = AUD_FMT_U32; + *fmt = AUDIO_FORMAT_U32; break; default: @@ -410,17 +387,18 @@ static int alsa_to_audfmt (snd_pcm_format_t alsafmt, audfmt_e *fmt, static void alsa_dump_info (struct alsa_params_req *req, struct alsa_params_obt *obt, - snd_pcm_format_t obtfmt) + snd_pcm_format_t obtfmt, + AudiodevAlsaPerDirectionOptions *apdo) { - dolog ("parameter | requested value | obtained value\n"); - dolog ("format | %10d | %10d\n", req->fmt, obtfmt); - dolog ("channels | %10d | %10d\n", - req->nchannels, obt->nchannels); - dolog ("frequency | %10d | %10d\n", req->freq, obt->freq); - dolog ("============================================\n"); - dolog ("requested: buffer size %d period size %d\n", - req->buffer_size, req->period_size); - dolog ("obtained: samples %ld\n", obt->samples); + dolog("parameter | requested value | obtained value\n"); + dolog("format | %10d | %10d\n", req->fmt, obtfmt); + dolog("channels | %10d | %10d\n", + req->nchannels, obt->nchannels); + dolog("frequency | %10d | %10d\n", req->freq, obt->freq); + dolog("============================================\n"); + dolog("requested: buffer len %" PRId32 " period len %" PRId32 "\n", + apdo->buffer_length, apdo->period_length); + dolog("obtained: samples %ld\n", obt->samples); } static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) @@ -453,23 +431,23 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold) } } -static int alsa_open (int in, struct alsa_params_req *req, - struct alsa_params_obt *obt, snd_pcm_t **handlep, - ALSAConf *conf) +static int alsa_open(bool in, struct alsa_params_req *req, + struct alsa_params_obt *obt, snd_pcm_t **handlep, + Audiodev *dev) { + AudiodevAlsaOptions *aopts = &dev->u.alsa; + AudiodevAlsaPerDirectionOptions *apdo = in ? aopts->in : aopts->out; snd_pcm_t *handle; snd_pcm_hw_params_t *hw_params; int err; - int size_in_usec; unsigned int freq, nchannels; - const char *pcm_name = in ? conf->pcm_name_in : conf->pcm_name_out; + const char *pcm_name = apdo->has_dev ? apdo->dev : "default"; snd_pcm_uframes_t obt_buffer_size; const char *typ = in ? "ADC" : "DAC"; snd_pcm_format_t obtfmt; freq = req->freq; nchannels = req->nchannels; - size_in_usec = req->size_in_usec; snd_pcm_hw_params_alloca (&hw_params); @@ -529,79 +507,42 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - if (req->buffer_size) { - unsigned long obt; + if (apdo->buffer_length) { + int dir = 0; + unsigned int btime = apdo->buffer_length; - if (size_in_usec) { - int dir = 0; - unsigned int btime = req->buffer_size; + err = snd_pcm_hw_params_set_buffer_time_near( + handle, hw_params, &btime, &dir); - err = snd_pcm_hw_params_set_buffer_time_near ( - handle, - hw_params, - &btime, - &dir - ); - obt = btime; - } - else { - snd_pcm_uframes_t bsize = req->buffer_size; - - err = snd_pcm_hw_params_set_buffer_size_near ( - handle, - hw_params, - &bsize - ); - obt = bsize; - } if (err < 0) { - alsa_logerr2 (err, typ, "Failed to set buffer %s to %d\n", - size_in_usec ? "time" : "size", req->buffer_size); + alsa_logerr2(err, typ, "Failed to set buffer time to %" PRId32 "\n", + apdo->buffer_length); goto err; } - if ((req->override_mask & 2) && (obt - req->buffer_size)) - dolog ("Requested buffer %s %u was rejected, using %lu\n", - size_in_usec ? "time" : "size", req->buffer_size, obt); + if (apdo->has_buffer_length && btime != apdo->buffer_length) { + dolog("Requested buffer time %" PRId32 + " was rejected, using %u\n", apdo->buffer_length, btime); + } } - if (req->period_size) { - unsigned long obt; + if (apdo->period_length) { + int dir = 0; + unsigned int ptime = apdo->period_length; - if (size_in_usec) { - int dir = 0; - unsigned int ptime = req->period_size; - - err = snd_pcm_hw_params_set_period_time_near ( - handle, - hw_params, - &ptime, - &dir - ); - obt = ptime; - } - else { - int dir = 0; - snd_pcm_uframes_t psize = req->period_size; - - err = snd_pcm_hw_params_set_period_size_near ( - handle, - hw_params, - &psize, - &dir - ); - obt = psize; - } + err = snd_pcm_hw_params_set_period_time_near(handle, hw_params, &ptime, + &dir); if (err < 0) { - alsa_logerr2 (err, typ, "Failed to set period %s to %d\n", - size_in_usec ? "time" : "size", req->period_size); + alsa_logerr2(err, typ, "Failed to set period time to %" PRId32 "\n", + apdo->period_length); goto err; } - if (((req->override_mask & 1) && (obt - req->period_size))) - dolog ("Requested period %s %u was rejected, using %lu\n", - size_in_usec ? "time" : "size", req->period_size, obt); + if (apdo->has_period_length && ptime != apdo->period_length) { + dolog("Requested period time %" PRId32 " was rejected, using %d\n", + apdo->period_length, ptime); + } } err = snd_pcm_hw_params (handle, hw_params); @@ -633,30 +574,12 @@ static int alsa_open (int in, struct alsa_params_req *req, goto err; } - if (!in && conf->threshold) { - snd_pcm_uframes_t threshold; - int bytes_per_sec; - - bytes_per_sec = freq << (nchannels == 2); - - switch (obt->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - break; - - case AUD_FMT_S16: - case AUD_FMT_U16: - bytes_per_sec <<= 1; - break; - - case AUD_FMT_S32: - case AUD_FMT_U32: - bytes_per_sec <<= 2; - break; - } - - threshold = (conf->threshold * bytes_per_sec) / 1000; - alsa_set_threshold (handle, threshold); + if (!in && aopts->has_threshold && aopts->threshold) { + struct audsettings as = { .freq = freq }; + alsa_set_threshold( + handle, + audio_buffer_frames(qapi_AudiodevAlsaPerDirectionOptions_base(apdo), + &as, aopts->threshold)); } obt->nchannels = nchannels; @@ -669,11 +592,11 @@ static int alsa_open (int in, struct alsa_params_req *req, obt->nchannels != req->nchannels || obt->freq != req->freq) { dolog ("Audio parameters for %s\n", typ); - alsa_dump_info (req, obt, obtfmt); + alsa_dump_info(req, obt, obtfmt, apdo); } #ifdef DEBUG - alsa_dump_info (req, obt, obtfmt); + alsa_dump_info(req, obt, obtfmt, pdo); #endif return 0; @@ -799,19 +722,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, struct alsa_params_obt obt; snd_pcm_t *handle; struct audsettings obt_as; - ALSAConf *conf = drv_opaque; + Audiodev *dev = drv_opaque; req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.period_size = conf->period_size_out; - req.buffer_size = conf->buffer_size_out; - req.size_in_usec = conf->size_in_usec_out; - req.override_mask = - (conf->period_size_out_overridden ? 1 : 0) | - (conf->buffer_size_out_overridden ? 2 : 0); - if (alsa_open (0, &req, &obt, &handle, conf)) { + if (alsa_open(0, &req, &obt, &handle, dev)) { return -1; } @@ -832,7 +749,7 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as, } alsa->handle = handle; - alsa->pollhlp.conf = conf; + alsa->dev = dev; return 0; } @@ -872,16 +789,12 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int ctl) static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) { ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; + AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.out; switch (cmd) { case VOICE_ENABLE: { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); + bool poll_mode = apdo->try_poll; ldebug ("enabling voice\n"); if (poll_mode && alsa_poll_out (hw)) { @@ -910,19 +823,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) struct alsa_params_obt obt; snd_pcm_t *handle; struct audsettings obt_as; - ALSAConf *conf = drv_opaque; + Audiodev *dev = drv_opaque; req.fmt = aud_to_alsafmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.period_size = conf->period_size_in; - req.buffer_size = conf->buffer_size_in; - req.size_in_usec = conf->size_in_usec_in; - req.override_mask = - (conf->period_size_in_overridden ? 1 : 0) | - (conf->buffer_size_in_overridden ? 2 : 0); - if (alsa_open (1, &req, &obt, &handle, conf)) { + if (alsa_open(1, &req, &obt, &handle, dev)) { return -1; } @@ -943,7 +850,7 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } alsa->handle = handle; - alsa->pollhlp.conf = conf; + alsa->dev = dev; return 0; } @@ -1085,16 +992,12 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size) static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) { ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw; + AudiodevAlsaPerDirectionOptions *apdo = alsa->dev->u.alsa.in; switch (cmd) { case VOICE_ENABLE: { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); + bool poll_mode = apdo->try_poll; ldebug ("enabling voice\n"); if (poll_mode && alsa_poll_in (hw)) { @@ -1117,88 +1020,54 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) return -1; } -static ALSAConf glob_conf = { - .buffer_size_out = 4096, - .period_size_out = 1024, - .pcm_name_out = "default", - .pcm_name_in = "default", -}; - -static void *alsa_audio_init (void) +static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) { - ALSAConf *conf = g_malloc(sizeof(ALSAConf)); - *conf = glob_conf; - return conf; + if (!apdo->has_try_poll) { + apdo->try_poll = true; + apdo->has_try_poll = true; + } +} + +static void *alsa_audio_init(Audiodev *dev) +{ + AudiodevAlsaOptions *aopts; + assert(dev->driver == AUDIODEV_DRIVER_ALSA); + + aopts = &dev->u.alsa; + alsa_init_per_direction(aopts->in); + alsa_init_per_direction(aopts->out); + + /* + * need to define them, as otherwise alsa produces no sound + * doesn't set has_* so alsa_open can identify it wasn't set by the user + */ + if (!dev->u.alsa.out->has_period_length) { + /* 1024 frames assuming 44100Hz */ + dev->u.alsa.out->period_length = 1024 * 1000000 / 44100; + } + if (!dev->u.alsa.out->has_buffer_length) { + /* 4096 frames assuming 44100Hz */ + dev->u.alsa.out->buffer_length = 4096ll * 1000000 / 44100; + } + + /* + * OptsVisitor sets unspecified optional fields to zero, but do not depend + * on it... + */ + if (!dev->u.alsa.in->has_period_length) { + dev->u.alsa.in->period_length = 0; + } + if (!dev->u.alsa.in->has_buffer_length) { + dev->u.alsa.in->buffer_length = 0; + } + + return dev; } static void alsa_audio_fini (void *opaque) { - g_free(opaque); } -static struct audio_option alsa_options[] = { - { - .name = "DAC_SIZE_IN_USEC", - .tag = AUD_OPT_BOOL, - .valp = &glob_conf.size_in_usec_out, - .descr = "DAC period/buffer size in microseconds (otherwise in frames)" - }, - { - .name = "DAC_PERIOD_SIZE", - .tag = AUD_OPT_INT, - .valp = &glob_conf.period_size_out, - .descr = "DAC period size (0 to go with system default)", - .overriddenp = &glob_conf.period_size_out_overridden - }, - { - .name = "DAC_BUFFER_SIZE", - .tag = AUD_OPT_INT, - .valp = &glob_conf.buffer_size_out, - .descr = "DAC buffer size (0 to go with system default)", - .overriddenp = &glob_conf.buffer_size_out_overridden - }, - { - .name = "ADC_SIZE_IN_USEC", - .tag = AUD_OPT_BOOL, - .valp = &glob_conf.size_in_usec_in, - .descr = - "ADC period/buffer size in microseconds (otherwise in frames)" - }, - { - .name = "ADC_PERIOD_SIZE", - .tag = AUD_OPT_INT, - .valp = &glob_conf.period_size_in, - .descr = "ADC period size (0 to go with system default)", - .overriddenp = &glob_conf.period_size_in_overridden - }, - { - .name = "ADC_BUFFER_SIZE", - .tag = AUD_OPT_INT, - .valp = &glob_conf.buffer_size_in, - .descr = "ADC buffer size (0 to go with system default)", - .overriddenp = &glob_conf.buffer_size_in_overridden - }, - { - .name = "THRESHOLD", - .tag = AUD_OPT_INT, - .valp = &glob_conf.threshold, - .descr = "(undocumented)" - }, - { - .name = "DAC_DEV", - .tag = AUD_OPT_STR, - .valp = &glob_conf.pcm_name_out, - .descr = "DAC device name (for instance dmix)" - }, - { - .name = "ADC_DEV", - .tag = AUD_OPT_STR, - .valp = &glob_conf.pcm_name_in, - .descr = "ADC device name" - }, - { /* End of list */ } -}; - static struct audio_pcm_ops alsa_pcm_ops = { .init_out = alsa_init_out, .fini_out = alsa_fini_out, @@ -1216,7 +1085,6 @@ static struct audio_pcm_ops alsa_pcm_ops = { static struct audio_driver alsa_audio_driver = { .name = "alsa", .descr = "ALSA http://www.alsa-project.org", - .options = alsa_options, .init = alsa_audio_init, .fini = alsa_audio_fini, .pcm_ops = &alsa_pcm_ops, diff --git a/audio/audio.c b/audio/audio.c index 1ace47f510..2040762fef 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -26,6 +26,9 @@ #include "audio.h" #include "monitor/monitor.h" #include "qemu/timer.h" +#include "qapi/error.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-audio.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "sysemu/replay.h" @@ -46,14 +49,16 @@ The 1st one is the one used by default, that is the reason that we generate the list. */ -static const char *audio_prio_list[] = { +const char *audio_prio_list[] = { "spice", CONFIG_AUDIO_DRIVERS "none", "wav", + NULL }; static QLIST_HEAD(, audio_driver) audio_drivers; +static AudiodevListHead audiodevs = QSIMPLEQ_HEAD_INITIALIZER(audiodevs); void audio_driver_register(audio_driver *drv) { @@ -80,61 +85,6 @@ audio_driver *audio_driver_lookup(const char *name) return NULL; } -static void audio_module_load_all(void) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(audio_prio_list); i++) { - audio_driver_lookup(audio_prio_list[i]); - } -} - -struct fixed_settings { - int enabled; - int nb_voices; - int greedy; - struct audsettings settings; -}; - -static struct { - struct fixed_settings fixed_out; - struct fixed_settings fixed_in; - union { - int hertz; - int64_t ticks; - } period; - int try_poll_in; - int try_poll_out; -} conf = { - .fixed_out = { /* DAC fixed settings */ - .enabled = 1, - .nb_voices = 1, - .greedy = 1, - .settings = { - .freq = 44100, - .nchannels = 2, - .fmt = AUD_FMT_S16, - .endianness = AUDIO_HOST_ENDIANNESS, - } - }, - - .fixed_in = { /* ADC fixed settings */ - .enabled = 1, - .nb_voices = 1, - .greedy = 1, - .settings = { - .freq = 44100, - .nchannels = 2, - .fmt = AUD_FMT_S16, - .endianness = AUDIO_HOST_ENDIANNESS, - } - }, - - .period = { .hertz = 100 }, - .try_poll_in = 1, - .try_poll_out = 1, -}; - static AudioState glob_audio_state; const struct mixeng_volume nominal_volume = { @@ -151,9 +101,6 @@ const struct mixeng_volume nominal_volume = { #ifdef AUDIO_IS_FLAWLESS_AND_NO_CHECKS_ARE_REQURIED #error No its not #else -static void audio_print_options (const char *prefix, - struct audio_option *opt); - int audio_bug (const char *funcname, int cond) { if (cond) { @@ -161,16 +108,9 @@ int audio_bug (const char *funcname, int cond) AUD_log (NULL, "A bug was just triggered in %s\n", funcname); if (!shown) { - struct audio_driver *d; - shown = 1; AUD_log (NULL, "Save all your work and restart without audio\n"); - AUD_log (NULL, "Please send bug report to av1474@comtv.ru\n"); AUD_log (NULL, "I am sorry\n"); - d = glob_audio_state.drv; - if (d) { - audio_print_options (d->name, d->options); - } } AUD_log (NULL, "Context:\n"); @@ -232,135 +172,6 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size) return g_malloc0 (len); } -static char *audio_alloc_prefix (const char *s) -{ - const char qemu_prefix[] = "QEMU_"; - size_t len, i; - char *r, *u; - - if (!s) { - return NULL; - } - - len = strlen (s); - r = g_malloc (len + sizeof (qemu_prefix)); - - u = r + sizeof (qemu_prefix) - 1; - - pstrcpy (r, len + sizeof (qemu_prefix), qemu_prefix); - pstrcat (r, len + sizeof (qemu_prefix), s); - - for (i = 0; i < len; ++i) { - u[i] = qemu_toupper(u[i]); - } - - return r; -} - -static const char *audio_audfmt_to_string (audfmt_e fmt) -{ - switch (fmt) { - case AUD_FMT_U8: - return "U8"; - - case AUD_FMT_U16: - return "U16"; - - case AUD_FMT_S8: - return "S8"; - - case AUD_FMT_S16: - return "S16"; - - case AUD_FMT_U32: - return "U32"; - - case AUD_FMT_S32: - return "S32"; - } - - dolog ("Bogus audfmt %d returning S16\n", fmt); - return "S16"; -} - -static audfmt_e audio_string_to_audfmt (const char *s, audfmt_e defval, - int *defaultp) -{ - if (!strcasecmp (s, "u8")) { - *defaultp = 0; - return AUD_FMT_U8; - } - else if (!strcasecmp (s, "u16")) { - *defaultp = 0; - return AUD_FMT_U16; - } - else if (!strcasecmp (s, "u32")) { - *defaultp = 0; - return AUD_FMT_U32; - } - else if (!strcasecmp (s, "s8")) { - *defaultp = 0; - return AUD_FMT_S8; - } - else if (!strcasecmp (s, "s16")) { - *defaultp = 0; - return AUD_FMT_S16; - } - else if (!strcasecmp (s, "s32")) { - *defaultp = 0; - return AUD_FMT_S32; - } - else { - dolog ("Bogus audio format `%s' using %s\n", - s, audio_audfmt_to_string (defval)); - *defaultp = 1; - return defval; - } -} - -static audfmt_e audio_get_conf_fmt (const char *envname, - audfmt_e defval, - int *defaultp) -{ - const char *var = getenv (envname); - if (!var) { - *defaultp = 1; - return defval; - } - return audio_string_to_audfmt (var, defval, defaultp); -} - -static int audio_get_conf_int (const char *key, int defval, int *defaultp) -{ - int val; - char *strval; - - strval = getenv (key); - if (strval && !qemu_strtoi(strval, NULL, 10, &val)) { - *defaultp = 0; - return val; - } - else { - *defaultp = 1; - return defval; - } -} - -static const char *audio_get_conf_str (const char *key, - const char *defval, - int *defaultp) -{ - const char *val = getenv (key); - if (!val) { - *defaultp = 1; - return defval; - } - else { - *defaultp = 0; - return val; - } -} - void AUD_vlog (const char *cap, const char *fmt, va_list ap) { if (cap) { @@ -379,182 +190,27 @@ void AUD_log (const char *cap, const char *fmt, ...) va_end (ap); } -static void audio_print_options (const char *prefix, - struct audio_option *opt) -{ - char *uprefix; - - if (!prefix) { - dolog ("No prefix specified\n"); - return; - } - - if (!opt) { - dolog ("No options\n"); - return; - } - - uprefix = audio_alloc_prefix (prefix); - - for (; opt->name; opt++) { - const char *state = "default"; - printf (" %s_%s: ", uprefix, opt->name); - - if (opt->overriddenp && *opt->overriddenp) { - state = "current"; - } - - switch (opt->tag) { - case AUD_OPT_BOOL: - { - int *intp = opt->valp; - printf ("boolean, %s = %d\n", state, *intp ? 1 : 0); - } - break; - - case AUD_OPT_INT: - { - int *intp = opt->valp; - printf ("integer, %s = %d\n", state, *intp); - } - break; - - case AUD_OPT_FMT: - { - audfmt_e *fmtp = opt->valp; - printf ( - "format, %s = %s, (one of: U8 S8 U16 S16 U32 S32)\n", - state, - audio_audfmt_to_string (*fmtp) - ); - } - break; - - case AUD_OPT_STR: - { - const char **strp = opt->valp; - printf ("string, %s = %s\n", - state, - *strp ? *strp : "(not set)"); - } - break; - - default: - printf ("???\n"); - dolog ("Bad value tag for option %s_%s %d\n", - uprefix, opt->name, opt->tag); - break; - } - printf (" %s\n", opt->descr); - } - - g_free (uprefix); -} - -static void audio_process_options (const char *prefix, - struct audio_option *opt) -{ - char *optname; - const char qemu_prefix[] = "QEMU_"; - size_t preflen, optlen; - - if (audio_bug(__func__, !prefix)) { - dolog ("prefix = NULL\n"); - return; - } - - if (audio_bug(__func__, !opt)) { - dolog ("opt = NULL\n"); - return; - } - - preflen = strlen (prefix); - - for (; opt->name; opt++) { - size_t len, i; - int def; - - if (!opt->valp) { - dolog ("Option value pointer for `%s' is not set\n", - opt->name); - continue; - } - - len = strlen (opt->name); - /* len of opt->name + len of prefix + size of qemu_prefix - * (includes trailing zero) + zero + underscore (on behalf of - * sizeof) */ - optlen = len + preflen + sizeof (qemu_prefix) + 1; - optname = g_malloc (optlen); - - pstrcpy (optname, optlen, qemu_prefix); - - /* copy while upper-casing, including trailing zero */ - for (i = 0; i <= preflen; ++i) { - optname[i + sizeof (qemu_prefix) - 1] = qemu_toupper(prefix[i]); - } - pstrcat (optname, optlen, "_"); - pstrcat (optname, optlen, opt->name); - - def = 1; - switch (opt->tag) { - case AUD_OPT_BOOL: - case AUD_OPT_INT: - { - int *intp = opt->valp; - *intp = audio_get_conf_int (optname, *intp, &def); - } - break; - - case AUD_OPT_FMT: - { - audfmt_e *fmtp = opt->valp; - *fmtp = audio_get_conf_fmt (optname, *fmtp, &def); - } - break; - - case AUD_OPT_STR: - { - const char **strp = opt->valp; - *strp = audio_get_conf_str (optname, *strp, &def); - } - break; - - default: - dolog ("Bad value tag for option `%s' - %d\n", - optname, opt->tag); - break; - } - - if (!opt->overriddenp) { - opt->overriddenp = &opt->overridden; - } - *opt->overriddenp = !def; - g_free (optname); - } -} - static void audio_print_settings (struct audsettings *as) { dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels); switch (as->fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: AUD_log (NULL, "S8"); break; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: AUD_log (NULL, "U8"); break; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: AUD_log (NULL, "S16"); break; - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: AUD_log (NULL, "U16"); break; - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: AUD_log (NULL, "S32"); break; - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: AUD_log (NULL, "U32"); break; default: @@ -585,12 +241,12 @@ static int audio_validate_settings (struct audsettings *as) invalid |= as->endianness != 0 && as->endianness != 1; switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - case AUD_FMT_S16: - case AUD_FMT_U16: - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: break; default: invalid = 1; @@ -606,25 +262,28 @@ static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *a int bits = 8, sign = 0; switch (as->fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: sign = 1; /* fall through */ - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: break; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: sign = 1; /* fall through */ - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: bits = 16; break; - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: sign = 1; /* fall through */ - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: bits = 32; break; + + default: + abort(); } return info->freq == as->freq && info->nchannels == as->nchannels @@ -638,24 +297,27 @@ void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as) int bits = 8, sign = 0, shift = 0; switch (as->fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: sign = 1; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: break; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: sign = 1; - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: bits = 16; shift = 1; break; - case AUD_FMT_S32: + case AUDIO_FORMAT_S32: sign = 1; - case AUD_FMT_U32: + case AUDIO_FORMAT_U32: bits = 32; shift = 2; break; + + default: + abort(); } info->freq = as->freq; @@ -826,12 +488,7 @@ static int audio_attach_capture (HWVoiceOut *hw) SWVoiceOut *sw; HWVoiceOut *hw_cap = &cap->hw; - sc = audio_calloc(__func__, 1, sizeof(*sc)); - if (!sc) { - dolog ("Could not allocate soft capture voice (%zu bytes)\n", - sizeof (*sc)); - return -1; - } + sc = g_malloc0(sizeof(*sc)); sc->cap = cap; sw = &sc->sw; @@ -1152,11 +809,11 @@ static void audio_reset_timer (AudioState *s) { if (audio_is_timer_needed ()) { timer_mod_anticipate_ns(s->ts, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks); + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks); if (!audio_timer_running) { audio_timer_running = true; audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - trace_audio_timer_start(conf.period.ticks / SCALE_MS); + trace_audio_timer_start(s->period_ticks / SCALE_MS); } } else { timer_del(s->ts); @@ -1170,16 +827,17 @@ static void audio_reset_timer (AudioState *s) static void audio_timer (void *opaque) { int64_t now, diff; + AudioState *s = opaque; now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); diff = now - audio_timer_last; - if (diff > conf.period.ticks * 3 / 2) { + if (diff > s->period_ticks * 3 / 2) { trace_audio_timer_delayed(diff / SCALE_MS); } audio_timer_last = now; - audio_run ("timer"); - audio_reset_timer (opaque); + audio_run("timer"); + audio_reset_timer(s); } /* @@ -1239,7 +897,7 @@ void AUD_set_active_out (SWVoiceOut *sw, int on) if (!hw->enabled) { hw->enabled = 1; if (s->vm_running) { - hw->pcm_ops->ctl_out (hw, VOICE_ENABLE, conf.try_poll_out); + hw->pcm_ops->ctl_out(hw, VOICE_ENABLE); audio_reset_timer (s); } } @@ -1284,7 +942,7 @@ void AUD_set_active_in (SWVoiceIn *sw, int on) if (!hw->enabled) { hw->enabled = 1; if (s->vm_running) { - hw->pcm_ops->ctl_in (hw, VOICE_ENABLE, conf.try_poll_in); + hw->pcm_ops->ctl_in(hw, VOICE_ENABLE); audio_reset_timer (s); } } @@ -1605,169 +1263,10 @@ void audio_run (const char *msg) #endif } -static struct audio_option audio_options[] = { - /* DAC */ - { - .name = "DAC_FIXED_SETTINGS", - .tag = AUD_OPT_BOOL, - .valp = &conf.fixed_out.enabled, - .descr = "Use fixed settings for host DAC" - }, - { - .name = "DAC_FIXED_FREQ", - .tag = AUD_OPT_INT, - .valp = &conf.fixed_out.settings.freq, - .descr = "Frequency for fixed host DAC" - }, - { - .name = "DAC_FIXED_FMT", - .tag = AUD_OPT_FMT, - .valp = &conf.fixed_out.settings.fmt, - .descr = "Format for fixed host DAC" - }, - { - .name = "DAC_FIXED_CHANNELS", - .tag = AUD_OPT_INT, - .valp = &conf.fixed_out.settings.nchannels, - .descr = "Number of channels for fixed DAC (1 - mono, 2 - stereo)" - }, - { - .name = "DAC_VOICES", - .tag = AUD_OPT_INT, - .valp = &conf.fixed_out.nb_voices, - .descr = "Number of voices for DAC" - }, - { - .name = "DAC_TRY_POLL", - .tag = AUD_OPT_BOOL, - .valp = &conf.try_poll_out, - .descr = "Attempt using poll mode for DAC" - }, - /* ADC */ - { - .name = "ADC_FIXED_SETTINGS", - .tag = AUD_OPT_BOOL, - .valp = &conf.fixed_in.enabled, - .descr = "Use fixed settings for host ADC" - }, - { - .name = "ADC_FIXED_FREQ", - .tag = AUD_OPT_INT, - .valp = &conf.fixed_in.settings.freq, - .descr = "Frequency for fixed host ADC" - }, - { - .name = "ADC_FIXED_FMT", - .tag = AUD_OPT_FMT, - .valp = &conf.fixed_in.settings.fmt, - .descr = "Format for fixed host ADC" - }, - { - .name = "ADC_FIXED_CHANNELS", - .tag = AUD_OPT_INT, - .valp = &conf.fixed_in.settings.nchannels, - .descr = "Number of channels for fixed ADC (1 - mono, 2 - stereo)" - }, - { - .name = "ADC_VOICES", - .tag = AUD_OPT_INT, - .valp = &conf.fixed_in.nb_voices, - .descr = "Number of voices for ADC" - }, - { - .name = "ADC_TRY_POLL", - .tag = AUD_OPT_BOOL, - .valp = &conf.try_poll_in, - .descr = "Attempt using poll mode for ADC" - }, - /* Misc */ - { - .name = "TIMER_PERIOD", - .tag = AUD_OPT_INT, - .valp = &conf.period.hertz, - .descr = "Timer period in HZ (0 - use lowest possible)" - }, - { /* End of list */ } -}; - -static void audio_pp_nb_voices (const char *typ, int nb) +static int audio_driver_init(AudioState *s, struct audio_driver *drv, + bool msg, Audiodev *dev) { - switch (nb) { - case 0: - printf ("Does not support %s\n", typ); - break; - case 1: - printf ("One %s voice\n", typ); - break; - case INT_MAX: - printf ("Theoretically supports many %s voices\n", typ); - break; - default: - printf ("Theoretically supports up to %d %s voices\n", nb, typ); - break; - } - -} - -void AUD_help (void) -{ - struct audio_driver *d; - - /* make sure we print the help text for modular drivers too */ - audio_module_load_all(); - - audio_process_options ("AUDIO", audio_options); - QLIST_FOREACH(d, &audio_drivers, next) { - if (d->options) { - audio_process_options (d->name, d->options); - } - } - - printf ("Audio options:\n"); - audio_print_options ("AUDIO", audio_options); - printf ("\n"); - - printf ("Available drivers:\n"); - - QLIST_FOREACH(d, &audio_drivers, next) { - - printf ("Name: %s\n", d->name); - printf ("Description: %s\n", d->descr); - - audio_pp_nb_voices ("playback", d->max_voices_out); - audio_pp_nb_voices ("capture", d->max_voices_in); - - if (d->options) { - printf ("Options:\n"); - audio_print_options (d->name, d->options); - } - else { - printf ("No options\n"); - } - printf ("\n"); - } - - printf ( - "Options are settable through environment variables.\n" - "Example:\n" -#ifdef _WIN32 - " set QEMU_AUDIO_DRV=wav\n" - " set QEMU_WAV_PATH=c:\\tune.wav\n" -#else - " export QEMU_AUDIO_DRV=wav\n" - " export QEMU_WAV_PATH=$HOME/tune.wav\n" - "(for csh replace export with setenv in the above)\n" -#endif - " qemu ...\n\n" - ); -} - -static int audio_driver_init (AudioState *s, struct audio_driver *drv) -{ - if (drv->options) { - audio_process_options (drv->name, drv->options); - } - s->drv_opaque = drv->init (); + s->drv_opaque = drv->init(dev); if (s->drv_opaque) { audio_init_nb_voices_out (drv); @@ -1776,7 +1275,9 @@ static int audio_driver_init (AudioState *s, struct audio_driver *drv) return 0; } else { - dolog ("Could not init `%s' audio driver\n", drv->name); + if (msg) { + dolog("Could not init `%s' audio driver\n", drv->name); + } return -1; } } @@ -1791,11 +1292,11 @@ static void audio_vm_change_state_handler (void *opaque, int running, s->vm_running = running; while ((hwo = audio_pcm_hw_find_any_enabled_out (hwo))) { - hwo->pcm_ops->ctl_out (hwo, op, conf.try_poll_out); + hwo->pcm_ops->ctl_out(hwo, op); } while ((hwi = audio_pcm_hw_find_any_enabled_in (hwi))) { - hwi->pcm_ops->ctl_in (hwi, op, conf.try_poll_in); + hwi->pcm_ops->ctl_in(hwi, op); } audio_reset_timer (s); } @@ -1845,6 +1346,11 @@ void audio_cleanup(void) s->drv->fini (s->drv_opaque); s->drv = NULL; } + + if (s->dev) { + qapi_free_Audiodev(s->dev); + s->dev = NULL; + } } static const VMStateDescription vmstate_audio = { @@ -1856,19 +1362,58 @@ static const VMStateDescription vmstate_audio = { } }; -static void audio_init (void) +static void audio_validate_opts(Audiodev *dev, Error **errp); + +static AudiodevListEntry *audiodev_find( + AudiodevListHead *head, const char *drvname) +{ + AudiodevListEntry *e; + QSIMPLEQ_FOREACH(e, head, next) { + if (strcmp(AudiodevDriver_str(e->dev->driver), drvname) == 0) { + return e; + } + } + + return NULL; +} + +static int audio_init(Audiodev *dev) { size_t i; int done = 0; - const char *drvname; + const char *drvname = NULL; VMChangeStateEntry *e; AudioState *s = &glob_audio_state; struct audio_driver *driver; + /* silence gcc warning about uninitialized variable */ + AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); if (s->drv) { - return; + if (dev) { + dolog("Cannot create more than one audio backend, sorry\n"); + qapi_free_Audiodev(dev); + } + return -1; } + if (dev) { + /* -audiodev option */ + drvname = AudiodevDriver_str(dev->driver); + } else { + /* legacy implicit initialization */ + head = audio_handle_legacy_opts(); + /* + * In case of legacy initialization, all Audiodevs in the list will have + * the same configuration (except the driver), so it does't matter which + * one we chose. We need an Audiodev to set up AudioState before we can + * init a driver. Also note that dev at this point is still in the + * list. + */ + dev = QSIMPLEQ_FIRST(&head)->dev; + audio_validate_opts(dev, &error_abort); + } + s->dev = dev; + QLIST_INIT (&s->hw_head_out); QLIST_INIT (&s->hw_head_in); QLIST_INIT (&s->cap_head); @@ -1876,10 +1421,8 @@ static void audio_init (void) s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s); - audio_process_options ("AUDIO", audio_options); - - s->nb_hw_voices_out = conf.fixed_out.nb_voices; - s->nb_hw_voices_in = conf.fixed_in.nb_voices; + s->nb_hw_voices_out = audio_get_pdo_out(dev)->voices; + s->nb_hw_voices_in = audio_get_pdo_in(dev)->voices; if (s->nb_hw_voices_out <= 0) { dolog ("Bogus number of playback voices %d, setting to 1\n", @@ -1893,46 +1436,42 @@ static void audio_init (void) s->nb_hw_voices_in = 0; } - { - int def; - drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); - } - if (drvname) { driver = audio_driver_lookup(drvname); if (driver) { - done = !audio_driver_init(s, driver); + done = !audio_driver_init(s, driver, true, dev); } else { dolog ("Unknown audio driver `%s'\n", drvname); - dolog ("Run with -audio-help to list available drivers\n"); } - } - - if (!done) { - for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) { + } else { + for (i = 0; audio_prio_list[i]; i++) { + AudiodevListEntry *e = audiodev_find(&head, audio_prio_list[i]); driver = audio_driver_lookup(audio_prio_list[i]); - if (driver && driver->can_be_default) { - done = !audio_driver_init(s, driver); + + if (e && driver) { + s->dev = dev = e->dev; + audio_validate_opts(dev, &error_abort); + done = !audio_driver_init(s, driver, false, dev); + if (done) { + e->dev = NULL; + break; + } } } } + audio_free_audiodev_list(&head); if (!done) { driver = audio_driver_lookup("none"); - done = !audio_driver_init(s, driver); + done = !audio_driver_init(s, driver, false, dev); assert(done); dolog("warning: Using timer based audio emulation\n"); } - if (conf.period.hertz <= 0) { - if (conf.period.hertz < 0) { - dolog ("warning: Timer period is negative - %d " - "treating as zero\n", - conf.period.hertz); - } - conf.period.ticks = 1; + if (dev->timer_period <= 0) { + s->period_ticks = 1; } else { - conf.period.ticks = NANOSECONDS_PER_SECOND / conf.period.hertz; + s->period_ticks = dev->timer_period * SCALE_US; } e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); @@ -1943,11 +1482,22 @@ static void audio_init (void) QLIST_INIT (&s->card_head); vmstate_register (NULL, 0, &vmstate_audio, s); + return 0; +} + +void audio_free_audiodev_list(AudiodevListHead *head) +{ + AudiodevListEntry *e; + while ((e = QSIMPLEQ_FIRST(head))) { + QSIMPLEQ_REMOVE_HEAD(head, next); + qapi_free_Audiodev(e->dev); + g_free(e); + } } void AUD_register_card (const char *name, QEMUSoundCard *card) { - audio_init (); + audio_init(NULL); card->name = g_strdup (name); memset (&card->entries, 0, sizeof (card->entries)); QLIST_INSERT_HEAD (&glob_audio_state.card_head, card, entries); @@ -1973,15 +1523,10 @@ CaptureVoiceOut *AUD_add_capture ( if (audio_validate_settings (as)) { dolog ("Invalid settings were passed when trying to add capture\n"); audio_print_settings (as); - goto err0; + return NULL; } - cb = audio_calloc(__func__, 1, sizeof(*cb)); - if (!cb) { - dolog ("Could not allocate capture callback information, size %zu\n", - sizeof (*cb)); - goto err0; - } + cb = g_malloc0(sizeof(*cb)); cb->ops = *ops; cb->opaque = cb_opaque; @@ -1994,12 +1539,7 @@ CaptureVoiceOut *AUD_add_capture ( HWVoiceOut *hw; CaptureVoiceOut *cap; - cap = audio_calloc(__func__, 1, sizeof(*cap)); - if (!cap) { - dolog ("Could not allocate capture voice, size %zu\n", - sizeof (*cap)); - goto err1; - } + cap = g_malloc0(sizeof(*cap)); hw = &cap->hw; QLIST_INIT (&hw->sw_head); @@ -2007,23 +1547,11 @@ CaptureVoiceOut *AUD_add_capture ( /* XXX find a more elegant way */ hw->samples = 4096 * 4; - hw->mix_buf = audio_calloc(__func__, hw->samples, - sizeof(struct st_sample)); - if (!hw->mix_buf) { - dolog ("Could not allocate capture mix buffer (%d samples)\n", - hw->samples); - goto err2; - } + hw->mix_buf = g_new0(struct st_sample, hw->samples); audio_pcm_init_info (&hw->info, as); - cap->buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); - if (!cap->buf) { - dolog ("Could not allocate capture buffer " - "(%d samples, each %d bytes)\n", - hw->samples, 1 << hw->info.shift); - goto err3; - } + cap->buf = g_malloc0_n(hw->samples, 1 << hw->info.shift); hw->clip = mixeng_clip [hw->info.nchannels == 2] @@ -2038,15 +1566,6 @@ CaptureVoiceOut *AUD_add_capture ( audio_attach_capture (hw); } return cap; - - err3: - g_free (cap->hw.mix_buf); - err2: - g_free (cap); - err1: - g_free (cb); - err0: - return NULL; } } @@ -2118,3 +1637,174 @@ void AUD_set_volume_in (SWVoiceIn *sw, int mute, uint8_t lvol, uint8_t rvol) } } } + +void audio_create_pdos(Audiodev *dev) +{ + switch (dev->driver) { +#define CASE(DRIVER, driver, pdo_name) \ + case AUDIODEV_DRIVER_##DRIVER: \ + if (!dev->u.driver.has_in) { \ + dev->u.driver.in = g_malloc0( \ + sizeof(Audiodev##pdo_name##PerDirectionOptions)); \ + dev->u.driver.has_in = true; \ + } \ + if (!dev->u.driver.has_out) { \ + dev->u.driver.out = g_malloc0( \ + sizeof(AudiodevAlsaPerDirectionOptions)); \ + dev->u.driver.has_out = true; \ + } \ + break + + CASE(NONE, none, ); + CASE(ALSA, alsa, Alsa); + CASE(COREAUDIO, coreaudio, Coreaudio); + CASE(DSOUND, dsound, ); + CASE(OSS, oss, Oss); + CASE(PA, pa, Pa); + CASE(SDL, sdl, ); + CASE(SPICE, spice, ); + CASE(WAV, wav, ); + + case AUDIODEV_DRIVER__MAX: + abort(); + }; +} + +static void audio_validate_per_direction_opts( + AudiodevPerDirectionOptions *pdo, Error **errp) +{ + if (!pdo->has_fixed_settings) { + pdo->has_fixed_settings = true; + pdo->fixed_settings = true; + } + if (!pdo->fixed_settings && + (pdo->has_frequency || pdo->has_channels || pdo->has_format)) { + error_setg(errp, + "You can't use frequency, channels or format with fixed-settings=off"); + return; + } + + if (!pdo->has_frequency) { + pdo->has_frequency = true; + pdo->frequency = 44100; + } + if (!pdo->has_channels) { + pdo->has_channels = true; + pdo->channels = 2; + } + if (!pdo->has_voices) { + pdo->has_voices = true; + pdo->voices = 1; + } + if (!pdo->has_format) { + pdo->has_format = true; + pdo->format = AUDIO_FORMAT_S16; + } +} + +static void audio_validate_opts(Audiodev *dev, Error **errp) +{ + Error *err = NULL; + + audio_create_pdos(dev); + + audio_validate_per_direction_opts(audio_get_pdo_in(dev), &err); + if (err) { + error_propagate(errp, err); + return; + } + + audio_validate_per_direction_opts(audio_get_pdo_out(dev), &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (!dev->has_timer_period) { + dev->has_timer_period = true; + dev->timer_period = 10000; /* 100Hz -> 10ms */ + } +} + +void audio_parse_option(const char *opt) +{ + AudiodevListEntry *e; + Audiodev *dev = NULL; + + Visitor *v = qobject_input_visitor_new_str(opt, "driver", &error_fatal); + visit_type_Audiodev(v, NULL, &dev, &error_fatal); + visit_free(v); + + audio_validate_opts(dev, &error_fatal); + + e = g_malloc0(sizeof(AudiodevListEntry)); + e->dev = dev; + QSIMPLEQ_INSERT_TAIL(&audiodevs, e, next); +} + +void audio_init_audiodevs(void) +{ + AudiodevListEntry *e; + + QSIMPLEQ_FOREACH(e, &audiodevs, next) { + audio_init(e->dev); + } +} + +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo) +{ + return (audsettings) { + .freq = pdo->frequency, + .nchannels = pdo->channels, + .fmt = pdo->format, + .endianness = AUDIO_HOST_ENDIANNESS, + }; +} + +int audioformat_bytes_per_sample(AudioFormat fmt) +{ + switch (fmt) { + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S8: + return 1; + + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S16: + return 2; + + case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_S32: + return 4; + + case AUDIO_FORMAT__MAX: + ; + } + abort(); +} + + +/* frames = freq * usec / 1e6 */ +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + uint64_t usecs = pdo->has_buffer_length ? pdo->buffer_length : def_usecs; + return (as->freq * usecs + 500000) / 1000000; +} + +/* samples = channels * frames = channels * freq * usec / 1e6 */ +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + return as->nchannels * audio_buffer_frames(pdo, as, def_usecs); +} + +/* + * bytes = bytes_per_sample * samples = + * bytes_per_sample * channels * freq * usec / 1e6 + */ +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs) +{ + return audio_buffer_samples(pdo, as, def_usecs) * + audioformat_bytes_per_sample(as->fmt); +} diff --git a/audio/audio.h b/audio/audio.h index f4339a185e..64b0f761bc 100644 --- a/audio/audio.h +++ b/audio/audio.h @@ -26,30 +26,31 @@ #define QEMU_AUDIO_H #include "qemu/queue.h" +#include "qapi/qapi-types-audio.h" typedef void (*audio_callback_fn) (void *opaque, int avail); -typedef enum { - AUD_FMT_U8, - AUD_FMT_S8, - AUD_FMT_U16, - AUD_FMT_S16, - AUD_FMT_U32, - AUD_FMT_S32 -} audfmt_e; - #ifdef HOST_WORDS_BIGENDIAN #define AUDIO_HOST_ENDIANNESS 1 #else #define AUDIO_HOST_ENDIANNESS 0 #endif -struct audsettings { +typedef struct audsettings { int freq; int nchannels; - audfmt_e fmt; + AudioFormat fmt; int endianness; -}; +} audsettings; + +audsettings audiodev_to_audsettings(AudiodevPerDirectionOptions *pdo); +int audioformat_bytes_per_sample(AudioFormat fmt); +int audio_buffer_frames(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs); +int audio_buffer_samples(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs); +int audio_buffer_bytes(AudiodevPerDirectionOptions *pdo, + audsettings *as, int def_usecs); typedef enum { AUD_CNOTIFY_ENABLE, @@ -89,7 +90,6 @@ typedef struct QEMUAudioTimeStamp { void AUD_vlog (const char *cap, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3); -void AUD_help (void); void AUD_register_card (const char *name, QEMUSoundCard *card); void AUD_remove_card (QEMUSoundCard *card); CaptureVoiceOut *AUD_add_capture ( @@ -171,4 +171,8 @@ void audio_sample_to_uint64(void *samples, int pos, void audio_sample_from_uint64(void *samples, int pos, uint64_t left, uint64_t right); +void audio_parse_option(const char *opt); +void audio_init_audiodevs(void); +void audio_legacy_help(void); + #endif /* QEMU_AUDIO_H */ diff --git a/audio/audio_int.h b/audio/audio_int.h index 244b454012..3f14842709 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -33,22 +33,6 @@ struct audio_pcm_ops; -typedef enum { - AUD_OPT_INT, - AUD_OPT_FMT, - AUD_OPT_STR, - AUD_OPT_BOOL -} audio_option_tag_e; - -struct audio_option { - const char *name; - audio_option_tag_e tag; - void *valp; - const char *descr; - int *overriddenp; - int overridden; -}; - struct audio_callback { void *opaque; audio_callback_fn fn; @@ -145,8 +129,7 @@ typedef struct audio_driver audio_driver; struct audio_driver { const char *name; const char *descr; - struct audio_option *options; - void *(*init) (void); + void *(*init) (Audiodev *); void (*fini) (void *); struct audio_pcm_ops *pcm_ops; int can_be_default; @@ -191,8 +174,9 @@ struct SWVoiceCap { QLIST_ENTRY (SWVoiceCap) entries; }; -struct AudioState { +typedef struct AudioState { struct audio_driver *drv; + Audiodev *dev; void *drv_opaque; QEMUTimer *ts; @@ -203,10 +187,13 @@ struct AudioState { int nb_hw_voices_out; int nb_hw_voices_in; int vm_running; -}; + int64_t period_ticks; +} AudioState; extern const struct mixeng_volume nominal_volume; +extern const char *audio_prio_list[]; + void audio_driver_register(audio_driver *drv); audio_driver *audio_driver_lookup(const char *name); @@ -248,4 +235,18 @@ static inline int audio_ring_dist (int dst, int src, int len) #define AUDIO_STRINGIFY_(n) #n #define AUDIO_STRINGIFY(n) AUDIO_STRINGIFY_(n) +typedef struct AudiodevListEntry { + Audiodev *dev; + QSIMPLEQ_ENTRY(AudiodevListEntry) next; +} AudiodevListEntry; + +typedef QSIMPLEQ_HEAD(, AudiodevListEntry) AudiodevListHead; +AudiodevListHead audio_handle_legacy_opts(void); + +void audio_free_audiodev_list(AudiodevListHead *head); + +void audio_create_pdos(Audiodev *dev); +AudiodevPerDirectionOptions *audio_get_pdo_in(Audiodev *dev); +AudiodevPerDirectionOptions *audio_get_pdo_out(Audiodev *dev); + #endif /* QEMU_AUDIO_INT_H */ diff --git a/audio/audio_legacy.c b/audio/audio_legacy.c new file mode 100644 index 0000000000..2fd58cb8ef --- /dev/null +++ b/audio/audio_legacy.c @@ -0,0 +1,550 @@ +/* + * QEMU Audio subsystem: legacy configuration handling + * + * Copyright (c) 2015-2019 Zoltán Kővágó + * + * 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 "audio.h" +#include "audio_int.h" +#include "qemu-common.h" +#include "qemu/cutils.h" +#include "qemu/timer.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-audio.h" +#include "qapi/visitor-impl.h" + +#define AUDIO_CAP "audio-legacy" +#include "audio_int.h" + +static uint32_t toui32(const char *str) +{ + unsigned long long ret; + if (parse_uint_full(str, &ret, 10) || ret > UINT32_MAX) { + dolog("Invalid integer value `%s'\n", str); + exit(1); + } + return ret; +} + +/* helper functions to convert env variables */ +static void get_bool(const char *env, bool *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + *dst = toui32(val) != 0; + *has_dst = true; + } +} + +static void get_int(const char *env, uint32_t *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + *dst = toui32(val); + *has_dst = true; + } +} + +static void get_str(const char *env, char **dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + if (*has_dst) { + g_free(*dst); + } + *dst = g_strdup(val); + *has_dst = true; + } +} + +static void get_fmt(const char *env, AudioFormat *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + size_t i; + for (i = 0; AudioFormat_lookup.size; ++i) { + if (strcasecmp(val, AudioFormat_lookup.array[i]) == 0) { + *dst = i; + *has_dst = true; + return; + } + } + + dolog("Invalid audio format `%s'\n", val); + exit(1); + } +} + + +static void get_millis_to_usecs(const char *env, uint32_t *dst, bool *has_dst) +{ + const char *val = getenv(env); + if (val) { + *dst = toui32(val) * 1000; + *has_dst = true; + } +} + +static uint32_t frames_to_usecs(uint32_t frames, + AudiodevPerDirectionOptions *pdo) +{ + uint32_t freq = pdo->has_frequency ? pdo->frequency : 44100; + return (frames * 1000000 + freq / 2) / freq; +} + + +static void get_frames_to_usecs(const char *env, uint32_t *dst, bool *has_dst, + AudiodevPerDirectionOptions *pdo) +{ + const char *val = getenv(env); + if (val) { + *dst = frames_to_usecs(toui32(val), pdo); + *has_dst = true; + } +} + +static uint32_t samples_to_usecs(uint32_t samples, + AudiodevPerDirectionOptions *pdo) +{ + uint32_t channels = pdo->has_channels ? pdo->channels : 2; + return frames_to_usecs(samples / channels, pdo); +} + +static void get_samples_to_usecs(const char *env, uint32_t *dst, bool *has_dst, + AudiodevPerDirectionOptions *pdo) +{ + const char *val = getenv(env); + if (val) { + *dst = samples_to_usecs(toui32(val), pdo); + *has_dst = true; + } +} + +static uint32_t bytes_to_usecs(uint32_t bytes, AudiodevPerDirectionOptions *pdo) +{ + AudioFormat fmt = pdo->has_format ? pdo->format : AUDIO_FORMAT_S16; + uint32_t bytes_per_sample = audioformat_bytes_per_sample(fmt); + return samples_to_usecs(bytes / bytes_per_sample, pdo); +} + +static void get_bytes_to_usecs(const char *env, uint32_t *dst, bool *has_dst, + AudiodevPerDirectionOptions *pdo) +{ + const char *val = getenv(env); + if (val) { + *dst = bytes_to_usecs(toui32(val), pdo); + *has_dst = true; + } +} + +/* backend specific functions */ +/* ALSA */ +static void handle_alsa_per_direction( + AudiodevAlsaPerDirectionOptions *apdo, const char *prefix) +{ + char buf[64]; + size_t len = strlen(prefix); + bool size_in_usecs = false; + bool dummy; + + memcpy(buf, prefix, len); + strcpy(buf + len, "TRY_POLL"); + get_bool(buf, &apdo->try_poll, &apdo->has_try_poll); + + strcpy(buf + len, "DEV"); + get_str(buf, &apdo->dev, &apdo->has_dev); + + strcpy(buf + len, "SIZE_IN_USEC"); + get_bool(buf, &size_in_usecs, &dummy); + + strcpy(buf + len, "PERIOD_SIZE"); + get_int(buf, &apdo->period_length, &apdo->has_period_length); + if (apdo->has_period_length && !size_in_usecs) { + apdo->period_length = frames_to_usecs( + apdo->period_length, + qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); + } + + strcpy(buf + len, "BUFFER_SIZE"); + get_int(buf, &apdo->buffer_length, &apdo->has_buffer_length); + if (apdo->has_buffer_length && !size_in_usecs) { + apdo->buffer_length = frames_to_usecs( + apdo->buffer_length, + qapi_AudiodevAlsaPerDirectionOptions_base(apdo)); + } +} + +static void handle_alsa(Audiodev *dev) +{ + AudiodevAlsaOptions *aopt = &dev->u.alsa; + handle_alsa_per_direction(aopt->in, "QEMU_ALSA_ADC_"); + handle_alsa_per_direction(aopt->out, "QEMU_ALSA_DAC_"); + + get_millis_to_usecs("QEMU_ALSA_THRESHOLD", + &aopt->threshold, &aopt->has_threshold); +} + +/* coreaudio */ +static void handle_coreaudio(Audiodev *dev) +{ + get_frames_to_usecs( + "QEMU_COREAUDIO_BUFFER_SIZE", + &dev->u.coreaudio.out->buffer_length, + &dev->u.coreaudio.out->has_buffer_length, + qapi_AudiodevCoreaudioPerDirectionOptions_base(dev->u.coreaudio.out)); + get_int("QEMU_COREAUDIO_BUFFER_COUNT", + &dev->u.coreaudio.out->buffer_count, + &dev->u.coreaudio.out->has_buffer_count); +} + +/* dsound */ +static void handle_dsound(Audiodev *dev) +{ + get_millis_to_usecs("QEMU_DSOUND_LATENCY_MILLIS", + &dev->u.dsound.latency, &dev->u.dsound.has_latency); + get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_OUT", + &dev->u.dsound.out->buffer_length, + &dev->u.dsound.out->has_buffer_length, + dev->u.dsound.out); + get_bytes_to_usecs("QEMU_DSOUND_BUFSIZE_IN", + &dev->u.dsound.in->buffer_length, + &dev->u.dsound.in->has_buffer_length, + dev->u.dsound.in); +} + +/* OSS */ +static void handle_oss_per_direction( + AudiodevOssPerDirectionOptions *opdo, const char *try_poll_env, + const char *dev_env) +{ + get_bool(try_poll_env, &opdo->try_poll, &opdo->has_try_poll); + get_str(dev_env, &opdo->dev, &opdo->has_dev); + + get_bytes_to_usecs("QEMU_OSS_FRAGSIZE", + &opdo->buffer_length, &opdo->has_buffer_length, + qapi_AudiodevOssPerDirectionOptions_base(opdo)); + get_int("QEMU_OSS_NFRAGS", &opdo->buffer_count, + &opdo->has_buffer_count); +} + +static void handle_oss(Audiodev *dev) +{ + AudiodevOssOptions *oopt = &dev->u.oss; + handle_oss_per_direction(oopt->in, "QEMU_AUDIO_ADC_TRY_POLL", + "QEMU_OSS_ADC_DEV"); + handle_oss_per_direction(oopt->out, "QEMU_AUDIO_DAC_TRY_POLL", + "QEMU_OSS_DAC_DEV"); + + get_bool("QEMU_OSS_MMAP", &oopt->try_mmap, &oopt->has_try_mmap); + get_bool("QEMU_OSS_EXCLUSIVE", &oopt->exclusive, &oopt->has_exclusive); + get_int("QEMU_OSS_POLICY", &oopt->dsp_policy, &oopt->has_dsp_policy); +} + +/* pulseaudio */ +static void handle_pa_per_direction( + AudiodevPaPerDirectionOptions *ppdo, const char *env) +{ + get_str(env, &ppdo->name, &ppdo->has_name); +} + +static void handle_pa(Audiodev *dev) +{ + handle_pa_per_direction(dev->u.pa.in, "QEMU_PA_SOURCE"); + handle_pa_per_direction(dev->u.pa.out, "QEMU_PA_SINK"); + + get_samples_to_usecs( + "QEMU_PA_SAMPLES", &dev->u.pa.in->buffer_length, + &dev->u.pa.in->has_buffer_length, + qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.in)); + get_samples_to_usecs( + "QEMU_PA_SAMPLES", &dev->u.pa.out->buffer_length, + &dev->u.pa.out->has_buffer_length, + qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.out)); + + get_str("QEMU_PA_SERVER", &dev->u.pa.server, &dev->u.pa.has_server); +} + +/* SDL */ +static void handle_sdl(Audiodev *dev) +{ + /* SDL is output only */ + get_samples_to_usecs("QEMU_SDL_SAMPLES", &dev->u.sdl.out->buffer_length, + &dev->u.sdl.out->has_buffer_length, dev->u.sdl.out); +} + +/* wav */ +static void handle_wav(Audiodev *dev) +{ + get_int("QEMU_WAV_FREQUENCY", + &dev->u.wav.out->frequency, &dev->u.wav.out->has_frequency); + get_fmt("QEMU_WAV_FORMAT", &dev->u.wav.out->format, + &dev->u.wav.out->has_format); + get_int("QEMU_WAV_DAC_FIXED_CHANNELS", + &dev->u.wav.out->channels, &dev->u.wav.out->has_channels); + get_str("QEMU_WAV_PATH", &dev->u.wav.path, &dev->u.wav.has_path); +} + +/* general */ +static void handle_per_direction( + AudiodevPerDirectionOptions *pdo, const char *prefix) +{ + char buf[64]; + size_t len = strlen(prefix); + + memcpy(buf, prefix, len); + strcpy(buf + len, "FIXED_SETTINGS"); + get_bool(buf, &pdo->fixed_settings, &pdo->has_fixed_settings); + + strcpy(buf + len, "FIXED_FREQ"); + get_int(buf, &pdo->frequency, &pdo->has_frequency); + + strcpy(buf + len, "FIXED_FMT"); + get_fmt(buf, &pdo->format, &pdo->has_format); + + strcpy(buf + len, "FIXED_CHANNELS"); + get_int(buf, &pdo->channels, &pdo->has_channels); + + strcpy(buf + len, "VOICES"); + get_int(buf, &pdo->voices, &pdo->has_voices); +} + +static AudiodevListEntry *legacy_opt(const char *drvname) +{ + AudiodevListEntry *e = g_malloc0(sizeof(AudiodevListEntry)); + e->dev = g_malloc0(sizeof(Audiodev)); + e->dev->id = g_strdup(drvname); + e->dev->driver = qapi_enum_parse( + &AudiodevDriver_lookup, drvname, -1, &error_abort); + + audio_create_pdos(e->dev); + + handle_per_direction(audio_get_pdo_in(e->dev), "QEMU_AUDIO_ADC_"); + handle_per_direction(audio_get_pdo_out(e->dev), "QEMU_AUDIO_DAC_"); + + /* Original description: Timer period in HZ (0 - use lowest possible) */ + get_int("QEMU_AUDIO_TIMER_PERIOD", + &e->dev->timer_period, &e->dev->has_timer_period); + if (e->dev->has_timer_period && e->dev->timer_period) { + e->dev->timer_period = NANOSECONDS_PER_SECOND / 1000 / + e->dev->timer_period; + } + + switch (e->dev->driver) { + case AUDIODEV_DRIVER_ALSA: + handle_alsa(e->dev); + break; + + case AUDIODEV_DRIVER_COREAUDIO: + handle_coreaudio(e->dev); + break; + + case AUDIODEV_DRIVER_DSOUND: + handle_dsound(e->dev); + break; + + case AUDIODEV_DRIVER_OSS: + handle_oss(e->dev); + break; + + case AUDIODEV_DRIVER_PA: + handle_pa(e->dev); + break; + + case AUDIODEV_DRIVER_SDL: + handle_sdl(e->dev); + break; + + case AUDIODEV_DRIVER_WAV: + handle_wav(e->dev); + break; + + default: + break; + } + + return e; +} + +AudiodevListHead audio_handle_legacy_opts(void) +{ + const char *drvname = getenv("QEMU_AUDIO_DRV"); + AudiodevListHead head = QSIMPLEQ_HEAD_INITIALIZER(head); + + if (drvname) { + AudiodevListEntry *e; + audio_driver *driver = audio_driver_lookup(drvname); + if (!driver) { + dolog("Unknown audio driver `%s'\n", drvname); + exit(1); + } + e = legacy_opt(drvname); + QSIMPLEQ_INSERT_TAIL(&head, e, next); + } else { + for (int i = 0; audio_prio_list[i]; i++) { + audio_driver *driver = audio_driver_lookup(audio_prio_list[i]); + if (driver && driver->can_be_default) { + AudiodevListEntry *e = legacy_opt(driver->name); + QSIMPLEQ_INSERT_TAIL(&head, e, next); + } + } + if (QSIMPLEQ_EMPTY(&head)) { + dolog("Internal error: no default audio driver available\n"); + exit(1); + } + } + + return head; +} + +/* visitor to print -audiodev option */ +typedef struct { + Visitor visitor; + + bool comma; + GList *path; +} LegacyPrintVisitor; + +static void lv_start_struct(Visitor *v, const char *name, void **obj, + size_t size, Error **errp) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + lv->path = g_list_append(lv->path, g_strdup(name)); +} + +static void lv_end_struct(Visitor *v, void **obj) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + lv->path = g_list_delete_link(lv->path, g_list_last(lv->path)); +} + +static void lv_print_key(Visitor *v, const char *name) +{ + GList *e; + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + if (lv->comma) { + putchar(','); + } else { + lv->comma = true; + } + + for (e = lv->path; e; e = e->next) { + if (e->data) { + printf("%s.", (const char *) e->data); + } + } + + printf("%s=", name); +} + +static void lv_type_int64(Visitor *v, const char *name, int64_t *obj, + Error **errp) +{ + lv_print_key(v, name); + printf("%" PRIi64, *obj); +} + +static void lv_type_uint64(Visitor *v, const char *name, uint64_t *obj, + Error **errp) +{ + lv_print_key(v, name); + printf("%" PRIu64, *obj); +} + +static void lv_type_bool(Visitor *v, const char *name, bool *obj, Error **errp) +{ + lv_print_key(v, name); + printf("%s", *obj ? "on" : "off"); +} + +static void lv_type_str(Visitor *v, const char *name, char **obj, Error **errp) +{ + const char *str = *obj; + lv_print_key(v, name); + + while (*str) { + if (*str == ',') { + putchar(','); + } + putchar(*str++); + } +} + +static void lv_complete(Visitor *v, void *opaque) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + assert(lv->path == NULL); +} + +static void lv_free(Visitor *v) +{ + LegacyPrintVisitor *lv = (LegacyPrintVisitor *) v; + + g_list_free_full(lv->path, g_free); + g_free(lv); +} + +static Visitor *legacy_visitor_new(void) +{ + LegacyPrintVisitor *lv = g_malloc0(sizeof(LegacyPrintVisitor)); + + lv->visitor.start_struct = lv_start_struct; + lv->visitor.end_struct = lv_end_struct; + /* lists not supported */ + lv->visitor.type_int64 = lv_type_int64; + lv->visitor.type_uint64 = lv_type_uint64; + lv->visitor.type_bool = lv_type_bool; + lv->visitor.type_str = lv_type_str; + + lv->visitor.type = VISITOR_OUTPUT; + lv->visitor.complete = lv_complete; + lv->visitor.free = lv_free; + + return &lv->visitor; +} + +void audio_legacy_help(void) +{ + AudiodevListHead head; + AudiodevListEntry *e; + + printf("Environment variable based configuration deprecated.\n"); + printf("Please use the new -audiodev option.\n"); + + head = audio_handle_legacy_opts(); + printf("\nEquivalent -audiodev to your current environment variables:\n"); + if (!getenv("QEMU_AUDIO_DRV")) { + printf("(Since you didn't specify QEMU_AUDIO_DRV, I'll list all " + "possibilities)\n"); + } + + QSIMPLEQ_FOREACH(e, &head, next) { + Visitor *v; + Audiodev *dev = e->dev; + printf("-audiodev "); + + v = legacy_visitor_new(); + visit_type_Audiodev(v, NULL, &dev, &error_abort); + visit_free(v); + + printf("\n"); + } + audio_free_audiodev_list(&head); +} diff --git a/audio/audio_template.h b/audio/audio_template.h index 7de227d2d1..1232bb54db 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -299,11 +299,42 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as) return NULL; } +AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev) +{ + switch (dev->driver) { + case AUDIODEV_DRIVER_NONE: + return dev->u.none.TYPE; + case AUDIODEV_DRIVER_ALSA: + return qapi_AudiodevAlsaPerDirectionOptions_base(dev->u.alsa.TYPE); + case AUDIODEV_DRIVER_COREAUDIO: + return qapi_AudiodevCoreaudioPerDirectionOptions_base( + dev->u.coreaudio.TYPE); + case AUDIODEV_DRIVER_DSOUND: + return dev->u.dsound.TYPE; + case AUDIODEV_DRIVER_OSS: + return qapi_AudiodevOssPerDirectionOptions_base(dev->u.oss.TYPE); + case AUDIODEV_DRIVER_PA: + return qapi_AudiodevPaPerDirectionOptions_base(dev->u.pa.TYPE); + case AUDIODEV_DRIVER_SDL: + return dev->u.sdl.TYPE; + case AUDIODEV_DRIVER_SPICE: + return dev->u.spice.TYPE; + case AUDIODEV_DRIVER_WAV: + return dev->u.wav.TYPE; + + case AUDIODEV_DRIVER__MAX: + break; + } + abort(); +} + static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as) { HW *hw; + AudioState *s = &glob_audio_state; + AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); - if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) { + if (pdo->fixed_settings) { hw = glue (audio_pcm_hw_add_new_, TYPE) (as); if (hw) { return hw; @@ -331,9 +362,11 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( SW *sw; HW *hw; struct audsettings hw_as; + AudioState *s = &glob_audio_state; + AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); - if (glue (conf.fixed_, TYPE).enabled) { - hw_as = glue (conf.fixed_, TYPE).settings; + if (pdo->fixed_settings) { + hw_as = audiodev_to_audsettings(pdo); } else { hw_as = *as; @@ -398,6 +431,7 @@ SW *glue (AUD_open_, TYPE) ( ) { AudioState *s = &glob_audio_state; + AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev); if (audio_bug(__func__, !card || !name || !callback_fn || !as)) { dolog ("card=%p name=%p callback_fn=%p as=%p\n", @@ -422,7 +456,7 @@ SW *glue (AUD_open_, TYPE) ( return sw; } - if (!glue (conf.fixed_, TYPE).enabled && sw) { + if (!pdo->fixed_settings && sw) { glue (AUD_close_, TYPE) (card, sw); sw = NULL; } diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c index 6900008d0c..b938fd667b 100644 --- a/audio/audio_win_int.c +++ b/audio/audio_win_int.c @@ -24,20 +24,20 @@ int waveformat_from_audio_settings (WAVEFORMATEX *wfx, wfx->cbSize = 0; switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: wfx->wBitsPerSample = 8; break; - case AUD_FMT_S16: - case AUD_FMT_U16: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: wfx->wBitsPerSample = 16; wfx->nAvgBytesPerSec <<= 1; wfx->nBlockAlign <<= 1; break; - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: wfx->wBitsPerSample = 32; wfx->nAvgBytesPerSec <<= 2; wfx->nBlockAlign <<= 2; @@ -85,15 +85,15 @@ int waveformat_to_audio_settings (WAVEFORMATEX *wfx, switch (wfx->wBitsPerSample) { case 8: - as->fmt = AUD_FMT_U8; + as->fmt = AUDIO_FORMAT_U8; break; case 16: - as->fmt = AUD_FMT_S16; + as->fmt = AUDIO_FORMAT_S16; break; case 32: - as->fmt = AUD_FMT_S32; + as->fmt = AUDIO_FORMAT_S32; break; default: diff --git a/audio/coreaudio.c b/audio/coreaudio.c index 638c60b300..1ee43b7d5f 100644 --- a/audio/coreaudio.c +++ b/audio/coreaudio.c @@ -36,11 +36,6 @@ #define MAC_OS_X_VERSION_10_6 1060 #endif -typedef struct { - int buffer_frames; - int nbuffers; -} CoreaudioConf; - typedef struct coreaudioVoiceOut { HWVoiceOut hw; pthread_mutex_t mutex; @@ -507,7 +502,9 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, int err; const char *typ = "playback"; AudioValueRange frameRange; - CoreaudioConf *conf = drv_opaque; + Audiodev *dev = drv_opaque; + AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; + int frames; /* create mutex */ err = pthread_mutex_init(&core->mutex, NULL); @@ -538,16 +535,17 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, return -1; } - if (frameRange.mMinimum > conf->buffer_frames) { + frames = audio_buffer_frames( + qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); + if (frameRange.mMinimum > frames) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); - } - else if (frameRange.mMaximum < conf->buffer_frames) { + } else if (frameRange.mMaximum < frames) { core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); } else { - core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; + core->audioDevicePropertyBufferFrameSize = frames; } /* set Buffer Frame Size */ @@ -568,7 +566,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, "Could not get device buffer frame size\n"); return -1; } - hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; + hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * + core->audioDevicePropertyBufferFrameSize; /* get StreamFormat */ status = coreaudio_get_streamformat(core->outputDeviceID, @@ -680,40 +679,15 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } -static CoreaudioConf glob_conf = { - .buffer_frames = 512, - .nbuffers = 4, -}; - -static void *coreaudio_audio_init (void) +static void *coreaudio_audio_init(Audiodev *dev) { - CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf)); - *conf = glob_conf; - - return conf; + return dev; } static void coreaudio_audio_fini (void *opaque) { - g_free(opaque); } -static struct audio_option coreaudio_options[] = { - { - .name = "BUFFER_SIZE", - .tag = AUD_OPT_INT, - .valp = &glob_conf.buffer_frames, - .descr = "Size of the buffer in frames" - }, - { - .name = "BUFFER_COUNT", - .tag = AUD_OPT_INT, - .valp = &glob_conf.nbuffers, - .descr = "Number of buffers" - }, - { /* End of list */ } -}; - static struct audio_pcm_ops coreaudio_pcm_ops = { .init_out = coreaudio_init_out, .fini_out = coreaudio_fini_out, @@ -725,7 +699,6 @@ static struct audio_pcm_ops coreaudio_pcm_ops = { static struct audio_driver coreaudio_audio_driver = { .name = "coreaudio", .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", - .options = coreaudio_options, .init = coreaudio_audio_init, .fini = coreaudio_audio_fini, .pcm_ops = &coreaudio_pcm_ops, diff --git a/audio/dsound_template.h b/audio/dsound_template.h index b439f33f58..8ece870c9e 100644 --- a/audio/dsound_template.h +++ b/audio/dsound_template.h @@ -167,17 +167,18 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, dsound *s = drv_opaque; WAVEFORMATEX wfx; struct audsettings obt_as; - DSoundConf *conf = &s->conf; #ifdef DSBTYPE_IN const char *typ = "ADC"; DSoundVoiceIn *ds = (DSoundVoiceIn *) hw; DSCBUFFERDESC bd; DSCBCAPS bc; + AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.in; #else const char *typ = "DAC"; DSoundVoiceOut *ds = (DSoundVoiceOut *) hw; DSBUFFERDESC bd; DSBCAPS bc; + AudiodevPerDirectionOptions *pdo = s->dev->u.dsound.out; #endif if (!s->FIELD2) { @@ -193,8 +194,8 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, memset (&bd, 0, sizeof (bd)); bd.dwSize = sizeof (bd); bd.lpwfxFormat = &wfx; + bd.dwBufferBytes = audio_buffer_bytes(pdo, as, 92880); #ifdef DSBTYPE_IN - bd.dwBufferBytes = conf->bufsize_in; hr = IDirectSoundCapture_CreateCaptureBuffer ( s->dsound_capture, &bd, @@ -203,7 +204,6 @@ static int dsound_init_out(HWVoiceOut *hw, struct audsettings *as, ); #else bd.dwFlags = DSBCAPS_STICKYFOCUS | DSBCAPS_GETCURRENTPOSITION2; - bd.dwBufferBytes = conf->bufsize_out; hr = IDirectSound_CreateSoundBuffer ( s->dsound, &bd, diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 3ed73a30d1..a7d04b5033 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -32,6 +32,7 @@ #define AUDIO_CAP "dsound" #include "audio_int.h" +#include "qemu/host-utils.h" #include #include @@ -42,17 +43,11 @@ /* #define DEBUG_DSOUND */ -typedef struct { - int bufsize_in; - int bufsize_out; - int latency_millis; -} DSoundConf; - typedef struct { LPDIRECTSOUND dsound; LPDIRECTSOUNDCAPTURE dsound_capture; struct audsettings settings; - DSoundConf conf; + Audiodev *dev; } dsound; typedef struct { @@ -248,9 +243,9 @@ static void GCC_FMT_ATTR (3, 4) dsound_logerr2 ( dsound_log_hresult (hr); } -static DWORD millis_to_bytes (struct audio_pcm_info *info, DWORD millis) +static uint64_t usecs_to_bytes(struct audio_pcm_info *info, uint32_t usecs) { - return (millis * info->bytes_per_second) / 1000; + return muldiv64(usecs, info->bytes_per_second, 1000000); } #ifdef DEBUG_DSOUND @@ -478,7 +473,7 @@ static int dsound_run_out (HWVoiceOut *hw, int live) LPVOID p1, p2; int bufsize; dsound *s = ds->s; - DSoundConf *conf = &s->conf; + AudiodevDsoundOptions *dso = &s->dev->u.dsound; if (!dsb) { dolog ("Attempt to run empty with playback buffer\n"); @@ -501,14 +496,14 @@ static int dsound_run_out (HWVoiceOut *hw, int live) len = live << hwshift; if (ds->first_time) { - if (conf->latency_millis) { + if (dso->latency) { DWORD cur_blat; cur_blat = audio_ring_dist (wpos, ppos, bufsize); ds->first_time = 0; old_pos = wpos; old_pos += - millis_to_bytes (&hw->info, conf->latency_millis) - cur_blat; + usecs_to_bytes(&hw->info, dso->latency) - cur_blat; old_pos %= bufsize; old_pos &= ~hw->info.align; } @@ -747,12 +742,6 @@ static int dsound_run_in (HWVoiceIn *hw) return decr; } -static DSoundConf glob_conf = { - .bufsize_in = 16384, - .bufsize_out = 16384, - .latency_millis = 10 -}; - static void dsound_audio_fini (void *opaque) { HRESULT hr; @@ -783,13 +772,22 @@ static void dsound_audio_fini (void *opaque) g_free(s); } -static void *dsound_audio_init (void) +static void *dsound_audio_init(Audiodev *dev) { int err; HRESULT hr; dsound *s = g_malloc0(sizeof(dsound)); + AudiodevDsoundOptions *dso; + + assert(dev->driver == AUDIODEV_DRIVER_DSOUND); + s->dev = dev; + dso = &dev->u.dsound; + + if (!dso->has_latency) { + dso->has_latency = true; + dso->latency = 10000; /* 10 ms */ + } - s->conf = glob_conf; hr = CoInitialize (NULL); if (FAILED (hr)) { dsound_logerr (hr, "Could not initialize COM\n"); @@ -854,28 +852,6 @@ static void *dsound_audio_init (void) return s; } -static struct audio_option dsound_options[] = { - { - .name = "LATENCY_MILLIS", - .tag = AUD_OPT_INT, - .valp = &glob_conf.latency_millis, - .descr = "(undocumented)" - }, - { - .name = "BUFSIZE_OUT", - .tag = AUD_OPT_INT, - .valp = &glob_conf.bufsize_out, - .descr = "(undocumented)" - }, - { - .name = "BUFSIZE_IN", - .tag = AUD_OPT_INT, - .valp = &glob_conf.bufsize_in, - .descr = "(undocumented)" - }, - { /* End of list */ } -}; - static struct audio_pcm_ops dsound_pcm_ops = { .init_out = dsound_init_out, .fini_out = dsound_fini_out, @@ -893,7 +869,6 @@ static struct audio_pcm_ops dsound_pcm_ops = { static struct audio_driver dsound_audio_driver = { .name = "dsound", .descr = "DirectSound http://wikipedia.org/wiki/DirectSound", - .options = dsound_options, .init = dsound_audio_init, .fini = dsound_audio_fini, .pcm_ops = &dsound_pcm_ops, diff --git a/audio/noaudio.c b/audio/noaudio.c index 1bfebeca7d..ccc611fc84 100644 --- a/audio/noaudio.c +++ b/audio/noaudio.c @@ -136,7 +136,7 @@ static int no_ctl_in (HWVoiceIn *hw, int cmd, ...) return 0; } -static void *no_audio_init (void) +static void *no_audio_init(Audiodev *dev) { return &no_audio_init; } @@ -163,7 +163,6 @@ static struct audio_pcm_ops no_pcm_ops = { static struct audio_driver no_audio_driver = { .name = "none", .descr = "Timer based audio emulation", - .options = NULL, .init = no_audio_init, .fini = no_audio_fini, .pcm_ops = &no_pcm_ops, diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 6c69622b4c..fc28981a39 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -37,16 +37,6 @@ #define USE_DSP_POLICY #endif -typedef struct OSSConf { - int try_mmap; - int nfrags; - int fragsize; - const char *devpath_out; - const char *devpath_in; - int exclusive; - int policy; -} OSSConf; - typedef struct OSSVoiceOut { HWVoiceOut hw; void *pcm_buf; @@ -56,7 +46,7 @@ typedef struct OSSVoiceOut { int fragsize; int mmapped; int pending; - OSSConf *conf; + Audiodev *dev; } OSSVoiceOut; typedef struct OSSVoiceIn { @@ -65,12 +55,12 @@ typedef struct OSSVoiceIn { int fd; int nfrags; int fragsize; - OSSConf *conf; + Audiodev *dev; } OSSVoiceIn; struct oss_params { int freq; - audfmt_e fmt; + int fmt; int nchannels; int nfrags; int fragsize; @@ -148,16 +138,16 @@ static int oss_write (SWVoiceOut *sw, void *buf, int len) return audio_pcm_sw_write (sw, buf, len); } -static int aud_to_ossfmt (audfmt_e fmt, int endianness) +static int aud_to_ossfmt (AudioFormat fmt, int endianness) { switch (fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: return AFMT_S8; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: return AFMT_U8; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: if (endianness) { return AFMT_S16_BE; } @@ -165,7 +155,7 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) return AFMT_S16_LE; } - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: if (endianness) { return AFMT_U16_BE; } @@ -182,37 +172,37 @@ static int aud_to_ossfmt (audfmt_e fmt, int endianness) } } -static int oss_to_audfmt (int ossfmt, audfmt_e *fmt, int *endianness) +static int oss_to_audfmt (int ossfmt, AudioFormat *fmt, int *endianness) { switch (ossfmt) { case AFMT_S8: *endianness = 0; - *fmt = AUD_FMT_S8; + *fmt = AUDIO_FORMAT_S8; break; case AFMT_U8: *endianness = 0; - *fmt = AUD_FMT_U8; + *fmt = AUDIO_FORMAT_U8; break; case AFMT_S16_LE: *endianness = 0; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AFMT_U16_LE: *endianness = 0; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case AFMT_S16_BE: *endianness = 1; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AFMT_U16_BE: *endianness = 1; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; default: @@ -262,19 +252,25 @@ static int oss_get_version (int fd, int *version, const char *typ) } #endif -static int oss_open (int in, struct oss_params *req, - struct oss_params *obt, int *pfd, OSSConf* conf) +static int oss_open(int in, struct oss_params *req, audsettings *as, + struct oss_params *obt, int *pfd, Audiodev *dev) { + AudiodevOssOptions *oopts = &dev->u.oss; + AudiodevOssPerDirectionOptions *opdo = in ? oopts->in : oopts->out; int fd; - int oflags = conf->exclusive ? O_EXCL : 0; + int oflags = (oopts->has_exclusive && oopts->exclusive) ? O_EXCL : 0; audio_buf_info abinfo; int fmt, freq, nchannels; int setfragment = 1; - const char *dspname = in ? conf->devpath_in : conf->devpath_out; + const char *dspname = opdo->has_dev ? opdo->dev : "/dev/dsp"; const char *typ = in ? "ADC" : "DAC"; +#ifdef USE_DSP_POLICY + int policy = oopts->has_dsp_policy ? oopts->dsp_policy : 5; +#endif /* Kludge needed to have working mmap on Linux */ - oflags |= conf->try_mmap ? O_RDWR : (in ? O_RDONLY : O_WRONLY); + oflags |= (oopts->has_try_mmap && oopts->try_mmap) ? + O_RDWR : (in ? O_RDONLY : O_WRONLY); fd = open (dspname, oflags | O_NONBLOCK); if (-1 == fd) { @@ -285,6 +281,9 @@ static int oss_open (int in, struct oss_params *req, freq = req->freq; nchannels = req->nchannels; fmt = req->fmt; + req->nfrags = opdo->has_buffer_count ? opdo->buffer_count : 4; + req->fragsize = audio_buffer_bytes( + qapi_AudiodevOssPerDirectionOptions_base(opdo), as, 23220); if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) { oss_logerr2 (errno, typ, "Failed to set sample size %d\n", req->fmt); @@ -308,18 +307,18 @@ static int oss_open (int in, struct oss_params *req, } #ifdef USE_DSP_POLICY - if (conf->policy >= 0) { + if (policy >= 0) { int version; if (!oss_get_version (fd, &version, typ)) { trace_oss_version(version); if (version >= 0x040000) { - int policy = conf->policy; - if (ioctl (fd, SNDCTL_DSP_POLICY, &policy)) { + int policy2 = policy; + if (ioctl(fd, SNDCTL_DSP_POLICY, &policy2)) { oss_logerr2 (errno, typ, "Failed to set timing policy to %d\n", - conf->policy); + policy); goto err; } setfragment = 0; @@ -500,19 +499,18 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, int endianness; int err; int fd; - audfmt_e effective_fmt; + AudioFormat effective_fmt; struct audsettings obt_as; - OSSConf *conf = drv_opaque; + Audiodev *dev = drv_opaque; + AudiodevOssOptions *oopts = &dev->u.oss; oss->fd = -1; req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.fragsize = conf->fragsize; - req.nfrags = conf->nfrags; - if (oss_open (0, &req, &obt, &fd, conf)) { + if (oss_open(0, &req, as, &obt, &fd, dev)) { return -1; } @@ -539,7 +537,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift; oss->mmapped = 0; - if (conf->try_mmap) { + if (oopts->has_try_mmap && oopts->try_mmap) { oss->pcm_buf = mmap ( NULL, hw->samples << hw->info.shift, @@ -597,7 +595,7 @@ static int oss_init_out(HWVoiceOut *hw, struct audsettings *as, } oss->fd = fd; - oss->conf = conf; + oss->dev = dev; return 0; } @@ -605,16 +603,12 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) { int trig; OSSVoiceOut *oss = (OSSVoiceOut *) hw; + AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; switch (cmd) { case VOICE_ENABLE: { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); + bool poll_mode = opdo->try_poll; ldebug ("enabling voice\n"); if (poll_mode) { @@ -667,18 +661,16 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) int endianness; int err; int fd; - audfmt_e effective_fmt; + AudioFormat effective_fmt; struct audsettings obt_as; - OSSConf *conf = drv_opaque; + Audiodev *dev = drv_opaque; oss->fd = -1; req.fmt = aud_to_ossfmt (as->fmt, as->endianness); req.freq = as->freq; req.nchannels = as->nchannels; - req.fragsize = conf->fragsize; - req.nfrags = conf->nfrags; - if (oss_open (1, &req, &obt, &fd, conf)) { + if (oss_open(1, &req, as, &obt, &fd, dev)) { return -1; } @@ -712,7 +704,7 @@ static int oss_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } oss->fd = fd; - oss->conf = conf; + oss->dev = dev; return 0; } @@ -803,16 +795,12 @@ static int oss_read (SWVoiceIn *sw, void *buf, int size) static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) { OSSVoiceIn *oss = (OSSVoiceIn *) hw; + AudiodevOssPerDirectionOptions *opdo = oss->dev->u.oss.out; switch (cmd) { case VOICE_ENABLE: { - va_list ap; - int poll_mode; - - va_start (ap, cmd); - poll_mode = va_arg (ap, int); - va_end (ap); + bool poll_mode = opdo->try_poll; if (poll_mode) { oss_poll_in (hw); @@ -832,82 +820,36 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) return 0; } -static OSSConf glob_conf = { - .try_mmap = 0, - .nfrags = 4, - .fragsize = 4096, - .devpath_out = "/dev/dsp", - .devpath_in = "/dev/dsp", - .exclusive = 0, - .policy = 5 -}; - -static void *oss_audio_init (void) +static void oss_init_per_direction(AudiodevOssPerDirectionOptions *opdo) { - OSSConf *conf = g_malloc(sizeof(OSSConf)); - *conf = glob_conf; + if (!opdo->has_try_poll) { + opdo->try_poll = true; + opdo->has_try_poll = true; + } +} - if (access(conf->devpath_in, R_OK | W_OK) < 0 || - access(conf->devpath_out, R_OK | W_OK) < 0) { - g_free(conf); +static void *oss_audio_init(Audiodev *dev) +{ + AudiodevOssOptions *oopts; + assert(dev->driver == AUDIODEV_DRIVER_OSS); + + oopts = &dev->u.oss; + oss_init_per_direction(oopts->in); + oss_init_per_direction(oopts->out); + + if (access(oopts->in->has_dev ? oopts->in->dev : "/dev/dsp", + R_OK | W_OK) < 0 || + access(oopts->out->has_dev ? oopts->out->dev : "/dev/dsp", + R_OK | W_OK) < 0) { return NULL; } - return conf; + return dev; } static void oss_audio_fini (void *opaque) { - g_free(opaque); } -static struct audio_option oss_options[] = { - { - .name = "FRAGSIZE", - .tag = AUD_OPT_INT, - .valp = &glob_conf.fragsize, - .descr = "Fragment size in bytes" - }, - { - .name = "NFRAGS", - .tag = AUD_OPT_INT, - .valp = &glob_conf.nfrags, - .descr = "Number of fragments" - }, - { - .name = "MMAP", - .tag = AUD_OPT_BOOL, - .valp = &glob_conf.try_mmap, - .descr = "Try using memory mapped access" - }, - { - .name = "DAC_DEV", - .tag = AUD_OPT_STR, - .valp = &glob_conf.devpath_out, - .descr = "Path to DAC device" - }, - { - .name = "ADC_DEV", - .tag = AUD_OPT_STR, - .valp = &glob_conf.devpath_in, - .descr = "Path to ADC device" - }, - { - .name = "EXCLUSIVE", - .tag = AUD_OPT_BOOL, - .valp = &glob_conf.exclusive, - .descr = "Open device in exclusive mode (vmix won't work)" - }, -#ifdef USE_DSP_POLICY - { - .name = "POLICY", - .tag = AUD_OPT_INT, - .valp = &glob_conf.policy, - .descr = "Set the timing policy of the device, -1 to use fragment mode", - }, -#endif - { /* End of list */ } -}; - static struct audio_pcm_ops oss_pcm_ops = { .init_out = oss_init_out, .fini_out = oss_fini_out, @@ -925,7 +867,6 @@ static struct audio_pcm_ops oss_pcm_ops = { static struct audio_driver oss_audio_driver = { .name = "oss", .descr = "OSS http://www.opensound.com", - .options = oss_options, .init = oss_audio_init, .fini = oss_audio_fini, .pcm_ops = &oss_pcm_ops, diff --git a/audio/paaudio.c b/audio/paaudio.c index 949769774d..45295b4e5e 100644 --- a/audio/paaudio.c +++ b/audio/paaudio.c @@ -2,6 +2,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" #include "audio.h" +#include "qapi/opts-visitor.h" #include @@ -10,14 +11,7 @@ #include "audio_pt_int.h" typedef struct { - int samples; - char *server; - char *sink; - char *source; -} PAConf; - -typedef struct { - PAConf conf; + Audiodev *dev; pa_threaded_mainloop *mainloop; pa_context *context; } paaudio; @@ -32,6 +26,7 @@ typedef struct { void *pcm_buf; struct audio_pt pt; paaudio *g; + int samples; } PAVoiceOut; typedef struct { @@ -46,6 +41,7 @@ typedef struct { const void *read_data; size_t read_index, read_length; paaudio *g; + int samples; } PAVoiceIn; static void qpa_audio_fini(void *opaque); @@ -227,7 +223,7 @@ static void *qpa_thread_out (void *arg) } } - decr = to_mix = audio_MIN (pa->live, pa->g->conf.samples >> 2); + decr = to_mix = audio_MIN(pa->live, pa->samples >> 5); rpos = pa->rpos; if (audio_pt_unlock(&pa->pt, __func__)) { @@ -319,7 +315,7 @@ static void *qpa_thread_in (void *arg) } } - incr = to_grab = audio_MIN (pa->dead, pa->g->conf.samples >> 2); + incr = to_grab = audio_MIN(pa->dead, pa->samples >> 5); wpos = pa->wpos; if (audio_pt_unlock(&pa->pt, __func__)) { @@ -385,21 +381,21 @@ static int qpa_read (SWVoiceIn *sw, void *buf, int len) return audio_pcm_sw_read (sw, buf, len); } -static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) +static pa_sample_format_t audfmt_to_pa (AudioFormat afmt, int endianness) { int format; switch (afmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: format = PA_SAMPLE_U8; break; - case AUD_FMT_S16: - case AUD_FMT_U16: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: format = endianness ? PA_SAMPLE_S16BE : PA_SAMPLE_S16LE; break; - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: format = endianness ? PA_SAMPLE_S32BE : PA_SAMPLE_S32LE; break; default: @@ -410,26 +406,26 @@ static pa_sample_format_t audfmt_to_pa (audfmt_e afmt, int endianness) return format; } -static audfmt_e pa_to_audfmt (pa_sample_format_t fmt, int *endianness) +static AudioFormat pa_to_audfmt (pa_sample_format_t fmt, int *endianness) { switch (fmt) { case PA_SAMPLE_U8: - return AUD_FMT_U8; + return AUDIO_FORMAT_U8; case PA_SAMPLE_S16BE: *endianness = 1; - return AUD_FMT_S16; + return AUDIO_FORMAT_S16; case PA_SAMPLE_S16LE: *endianness = 0; - return AUD_FMT_S16; + return AUDIO_FORMAT_S16; case PA_SAMPLE_S32BE: *endianness = 1; - return AUD_FMT_S32; + return AUDIO_FORMAT_S32; case PA_SAMPLE_S32LE: *endianness = 0; - return AUD_FMT_S32; + return AUDIO_FORMAT_S32; default: dolog ("Internal logic error: Bad pa_sample_format %d\n", fmt); - return AUD_FMT_U8; + return AUDIO_FORMAT_U8; } } @@ -546,17 +542,15 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, struct audsettings obt_as = *as; PAVoiceOut *pa = (PAVoiceOut *) hw; paaudio *g = pa->g = drv_opaque; + AudiodevPaOptions *popts = &g->dev->u.pa; + AudiodevPaPerDirectionOptions *ppdo = popts->out; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; ss.rate = as->freq; - /* - * qemu audio tick runs at 100 Hz (by default), so processing - * data chunks worth 10 ms of sound should be a good fit. - */ - ba.tlength = pa_usec_to_bytes (10 * 1000, &ss); - ba.minreq = pa_usec_to_bytes (5 * 1000, &ss); + ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss); + ba.minreq = -1; ba.maxlength = -1; ba.prebuf = -1; @@ -566,7 +560,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, g, "qemu", PA_STREAM_PLAYBACK, - g->conf.sink, + ppdo->has_name ? ppdo->name : NULL, &ss, NULL, /* channel map */ &ba, /* buffering attributes */ @@ -578,7 +572,9 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as, } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = g->conf.samples; + hw->samples = pa->samples = audio_buffer_samples( + qapi_AudiodevPaPerDirectionOptions_base(ppdo), + &obt_as, ppdo->buffer_length); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->rpos = hw->rpos; if (!pa->pcm_buf) { @@ -609,24 +605,32 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) { int error; pa_sample_spec ss; + pa_buffer_attr ba; struct audsettings obt_as = *as; PAVoiceIn *pa = (PAVoiceIn *) hw; paaudio *g = pa->g = drv_opaque; + AudiodevPaOptions *popts = &g->dev->u.pa; + AudiodevPaPerDirectionOptions *ppdo = popts->in; ss.format = audfmt_to_pa (as->fmt, as->endianness); ss.channels = as->nchannels; ss.rate = as->freq; + ba.fragsize = pa_usec_to_bytes(ppdo->latency, &ss); + ba.maxlength = -1; + ba.minreq = -1; + ba.prebuf = -1; + obt_as.fmt = pa_to_audfmt (ss.format, &obt_as.endianness); pa->stream = qpa_simple_new ( g, "qemu", PA_STREAM_RECORD, - g->conf.source, + ppdo->has_name ? ppdo->name : NULL, &ss, NULL, /* channel map */ - NULL, /* buffering attributes */ + &ba, /* buffering attributes */ &error ); if (!pa->stream) { @@ -635,7 +639,9 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) } audio_pcm_init_info (&hw->info, &obt_as); - hw->samples = g->conf.samples; + hw->samples = pa->samples = audio_buffer_samples( + qapi_AudiodevPaPerDirectionOptions_base(ppdo), + &obt_as, ppdo->buffer_length); pa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift); pa->wpos = hw->wpos; if (!pa->pcm_buf) { @@ -807,15 +813,54 @@ static int qpa_ctl_in (HWVoiceIn *hw, int cmd, ...) return 0; } -/* common */ -static PAConf glob_conf = { - .samples = 4096, -}; - -static void *qpa_audio_init (void) +static int qpa_validate_per_direction_opts(Audiodev *dev, + AudiodevPaPerDirectionOptions *pdo) { - paaudio *g = g_malloc(sizeof(paaudio)); - g->conf = glob_conf; + if (!pdo->has_buffer_length) { + pdo->has_buffer_length = true; + pdo->buffer_length = 46440; + } + if (!pdo->has_latency) { + pdo->has_latency = true; + pdo->latency = 15000; + } + return 1; +} + +static void *qpa_audio_init(Audiodev *dev) +{ + paaudio *g; + AudiodevPaOptions *popts = &dev->u.pa; + const char *server; + + if (!popts->has_server) { + char pidfile[64]; + char *runtime; + struct stat st; + + runtime = getenv("XDG_RUNTIME_DIR"); + if (!runtime) { + return NULL; + } + snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime); + if (stat(pidfile, &st) != 0) { + return NULL; + } + } + + assert(dev->driver == AUDIODEV_DRIVER_PA); + + g = g_malloc(sizeof(paaudio)); + server = popts->has_server ? popts->server : NULL; + + if (!qpa_validate_per_direction_opts(dev, popts->in)) { + goto fail; + } + if (!qpa_validate_per_direction_opts(dev, popts->out)) { + goto fail; + } + + g->dev = dev; g->mainloop = NULL; g->context = NULL; @@ -825,14 +870,14 @@ static void *qpa_audio_init (void) } g->context = pa_context_new (pa_threaded_mainloop_get_api (g->mainloop), - g->conf.server); + server); if (!g->context) { goto fail; } pa_context_set_state_callback (g->context, context_state_cb, g); - if (pa_context_connect (g->context, g->conf.server, 0, NULL) < 0) { + if (pa_context_connect(g->context, server, 0, NULL) < 0) { qpa_logerr (pa_context_errno (g->context), "pa_context_connect() failed\n"); goto fail; @@ -895,34 +940,6 @@ static void qpa_audio_fini (void *opaque) g_free(g); } -struct audio_option qpa_options[] = { - { - .name = "SAMPLES", - .tag = AUD_OPT_INT, - .valp = &glob_conf.samples, - .descr = "buffer size in samples" - }, - { - .name = "SERVER", - .tag = AUD_OPT_STR, - .valp = &glob_conf.server, - .descr = "server address" - }, - { - .name = "SINK", - .tag = AUD_OPT_STR, - .valp = &glob_conf.sink, - .descr = "sink device name" - }, - { - .name = "SOURCE", - .tag = AUD_OPT_STR, - .valp = &glob_conf.source, - .descr = "source device name" - }, - { /* End of list */ } -}; - static struct audio_pcm_ops qpa_pcm_ops = { .init_out = qpa_init_out, .fini_out = qpa_fini_out, @@ -940,7 +957,6 @@ static struct audio_pcm_ops qpa_pcm_ops = { static struct audio_driver pa_audio_driver = { .name = "pa", .descr = "http://www.pulseaudio.org/", - .options = qpa_options, .init = qpa_audio_init, .fini = qpa_audio_fini, .pcm_ops = &qpa_pcm_ops, diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c index 9db5ac92bc..ff9248ba68 100644 --- a/audio/sdlaudio.c +++ b/audio/sdlaudio.c @@ -38,31 +38,17 @@ #define AUDIO_CAP "sdl" #include "audio_int.h" -#define USE_SEMAPHORE (SDL_MAJOR_VERSION < 2) - typedef struct SDLVoiceOut { HWVoiceOut hw; int live; -#if USE_SEMAPHORE - int rpos; -#endif int decr; } SDLVoiceOut; -static struct { - int nb_samples; -} conf = { - .nb_samples = 1024 -}; - static struct SDLAudioState { int exit; -#if USE_SEMAPHORE - SDL_mutex *mutex; - SDL_sem *sem; -#endif int initialized; bool driver_created; + Audiodev *dev; } glob_sdl; typedef struct SDLAudioState SDLAudioState; @@ -77,79 +63,19 @@ static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...) AUD_log (AUDIO_CAP, "Reason: %s\n", SDL_GetError ()); } -static int sdl_lock (SDLAudioState *s, const char *forfn) -{ -#if USE_SEMAPHORE - if (SDL_LockMutex (s->mutex)) { - sdl_logerr ("SDL_LockMutex for %s failed\n", forfn); - return -1; - } -#else - SDL_LockAudio(); -#endif - - return 0; -} - -static int sdl_unlock (SDLAudioState *s, const char *forfn) -{ -#if USE_SEMAPHORE - if (SDL_UnlockMutex (s->mutex)) { - sdl_logerr ("SDL_UnlockMutex for %s failed\n", forfn); - return -1; - } -#else - SDL_UnlockAudio(); -#endif - - return 0; -} - -static int sdl_post (SDLAudioState *s, const char *forfn) -{ -#if USE_SEMAPHORE - if (SDL_SemPost (s->sem)) { - sdl_logerr ("SDL_SemPost for %s failed\n", forfn); - return -1; - } -#endif - - return 0; -} - -#if USE_SEMAPHORE -static int sdl_wait (SDLAudioState *s, const char *forfn) -{ - if (SDL_SemWait (s->sem)) { - sdl_logerr ("SDL_SemWait for %s failed\n", forfn); - return -1; - } - return 0; -} -#endif - -static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn) -{ - if (sdl_unlock (s, forfn)) { - return -1; - } - - return sdl_post (s, forfn); -} - -static int aud_to_sdlfmt (audfmt_e fmt) +static int aud_to_sdlfmt (AudioFormat fmt) { switch (fmt) { - case AUD_FMT_S8: + case AUDIO_FORMAT_S8: return AUDIO_S8; - case AUD_FMT_U8: + case AUDIO_FORMAT_U8: return AUDIO_U8; - case AUD_FMT_S16: + case AUDIO_FORMAT_S16: return AUDIO_S16LSB; - case AUD_FMT_U16: + case AUDIO_FORMAT_U16: return AUDIO_U16LSB; default: @@ -161,37 +87,37 @@ static int aud_to_sdlfmt (audfmt_e fmt) } } -static int sdl_to_audfmt(int sdlfmt, audfmt_e *fmt, int *endianness) +static int sdl_to_audfmt(int sdlfmt, AudioFormat *fmt, int *endianness) { switch (sdlfmt) { case AUDIO_S8: *endianness = 0; - *fmt = AUD_FMT_S8; + *fmt = AUDIO_FORMAT_S8; break; case AUDIO_U8: *endianness = 0; - *fmt = AUD_FMT_U8; + *fmt = AUDIO_FORMAT_U8; break; case AUDIO_S16LSB: *endianness = 0; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AUDIO_U16LSB: *endianness = 0; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; case AUDIO_S16MSB: *endianness = 1; - *fmt = AUD_FMT_S16; + *fmt = AUDIO_FORMAT_S16; break; case AUDIO_U16MSB: *endianness = 1; - *fmt = AUD_FMT_U16; + *fmt = AUDIO_FORMAT_U16; break; default: @@ -243,9 +169,9 @@ static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt) static void sdl_close (SDLAudioState *s) { if (s->initialized) { - sdl_lock (s, "sdl_close"); + SDL_LockAudio(); s->exit = 1; - sdl_unlock_and_post (s, "sdl_close"); + SDL_UnlockAudio(); SDL_PauseAudio (1); SDL_CloseAudio (); s->initialized = 0; @@ -258,76 +184,36 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len) SDLAudioState *s = &glob_sdl; HWVoiceOut *hw = &sdl->hw; int samples = len >> hw->info.shift; + int to_mix, decr; - if (s->exit) { + if (s->exit || !sdl->live) { return; } - while (samples) { - int to_mix, decr; + /* dolog ("in callback samples=%d live=%d\n", samples, sdl->live); */ - /* dolog ("in callback samples=%d\n", samples); */ -#if USE_SEMAPHORE - sdl_wait (s, "sdl_callback"); - if (s->exit) { - return; - } + to_mix = audio_MIN(samples, sdl->live); + decr = to_mix; + while (to_mix) { + int chunk = audio_MIN(to_mix, hw->samples - hw->rpos); + struct st_sample *src = hw->mix_buf + hw->rpos; - if (sdl_lock (s, "sdl_callback")) { - return; - } - - if (audio_bug(__func__, sdl->live < 0 || sdl->live > hw->samples)) { - dolog ("sdl->live=%d hw->samples=%d\n", - sdl->live, hw->samples); - return; - } - - if (!sdl->live) { - goto again; - } -#else - if (s->exit || !sdl->live) { - break; - } -#endif - - /* dolog ("in callback live=%d\n", live); */ - to_mix = audio_MIN (samples, sdl->live); - decr = to_mix; - while (to_mix) { - int chunk = audio_MIN (to_mix, hw->samples - hw->rpos); - struct st_sample *src = hw->mix_buf + hw->rpos; - - /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ - hw->clip (buf, src, chunk); -#if USE_SEMAPHORE - sdl->rpos = (sdl->rpos + chunk) % hw->samples; -#else - hw->rpos = (hw->rpos + chunk) % hw->samples; -#endif - to_mix -= chunk; - buf += chunk << hw->info.shift; - } - samples -= decr; - sdl->live -= decr; - sdl->decr += decr; - -#if USE_SEMAPHORE - again: - if (sdl_unlock (s, "sdl_callback")) { - return; - } -#endif + /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */ + hw->clip(buf, src, chunk); + hw->rpos = (hw->rpos + chunk) % hw->samples; + to_mix -= chunk; + buf += chunk << hw->info.shift; } + samples -= decr; + sdl->live -= decr; + sdl->decr += decr; + /* dolog ("done len=%d\n", len); */ -#if (SDL_MAJOR_VERSION >= 2) /* SDL2 does not clear the remaining buffer for us, so do it on our own */ if (samples) { memset(buf, 0, samples << hw->info.shift); } -#endif } static int sdl_write_out (SWVoiceOut *sw, void *buf, int len) @@ -339,11 +225,8 @@ static int sdl_run_out (HWVoiceOut *hw, int live) { int decr; SDLVoiceOut *sdl = (SDLVoiceOut *) hw; - SDLAudioState *s = &glob_sdl; - if (sdl_lock (s, "sdl_run_out")) { - return 0; - } + SDL_LockAudio(); if (sdl->decr > live) { ldebug ("sdl->decr %d live %d sdl->live %d\n", @@ -355,19 +238,10 @@ static int sdl_run_out (HWVoiceOut *hw, int live) decr = audio_MIN (sdl->decr, live); sdl->decr -= decr; -#if USE_SEMAPHORE - sdl->live = live - decr; - hw->rpos = sdl->rpos; -#else sdl->live = live; -#endif - if (sdl->live > 0) { - sdl_unlock_and_post (s, "sdl_run_out"); - } - else { - sdl_unlock (s, "sdl_run_out"); - } + SDL_UnlockAudio(); + return decr; } @@ -386,13 +260,13 @@ static int sdl_init_out(HWVoiceOut *hw, struct audsettings *as, SDL_AudioSpec req, obt; int endianness; int err; - audfmt_e effective_fmt; + AudioFormat effective_fmt; struct audsettings obt_as; req.freq = as->freq; req.format = aud_to_sdlfmt (as->fmt); req.channels = as->nchannels; - req.samples = conf.nb_samples; + req.samples = audio_buffer_samples(s->dev->u.sdl.out, as, 11610); req.callback = sdl_callback; req.userdata = sdl; @@ -436,7 +310,7 @@ static int sdl_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } -static void *sdl_audio_init (void) +static void *sdl_audio_init(Audiodev *dev) { SDLAudioState *s = &glob_sdl; if (s->driver_created) { @@ -449,24 +323,8 @@ static void *sdl_audio_init (void) return NULL; } -#if USE_SEMAPHORE - s->mutex = SDL_CreateMutex (); - if (!s->mutex) { - sdl_logerr ("Failed to create SDL mutex\n"); - SDL_QuitSubSystem (SDL_INIT_AUDIO); - return NULL; - } - - s->sem = SDL_CreateSemaphore (0); - if (!s->sem) { - sdl_logerr ("Failed to create SDL semaphore\n"); - SDL_DestroyMutex (s->mutex); - SDL_QuitSubSystem (SDL_INIT_AUDIO); - return NULL; - } -#endif - s->driver_created = true; + s->dev = dev; return s; } @@ -474,24 +332,11 @@ static void sdl_audio_fini (void *opaque) { SDLAudioState *s = opaque; sdl_close (s); -#if USE_SEMAPHORE - SDL_DestroySemaphore (s->sem); - SDL_DestroyMutex (s->mutex); -#endif SDL_QuitSubSystem (SDL_INIT_AUDIO); s->driver_created = false; + s->dev = NULL; } -static struct audio_option sdl_options[] = { - { - .name = "SAMPLES", - .tag = AUD_OPT_INT, - .valp = &conf.nb_samples, - .descr = "Size of SDL buffer in samples" - }, - { /* End of list */ } -}; - static struct audio_pcm_ops sdl_pcm_ops = { .init_out = sdl_init_out, .fini_out = sdl_fini_out, @@ -503,7 +348,6 @@ static struct audio_pcm_ops sdl_pcm_ops = { static struct audio_driver sdl_audio_driver = { .name = "sdl", .descr = "SDL http://www.libsdl.org", - .options = sdl_options, .init = sdl_audio_init, .fini = sdl_audio_fini, .pcm_ops = &sdl_pcm_ops, diff --git a/audio/spiceaudio.c b/audio/spiceaudio.c index 6ad0eafbc6..4f7873af5a 100644 --- a/audio/spiceaudio.c +++ b/audio/spiceaudio.c @@ -77,7 +77,7 @@ static const SpiceRecordInterface record_sif = { .base.minor_version = SPICE_INTERFACE_RECORD_MINOR, }; -static void *spice_audio_init (void) +static void *spice_audio_init(Audiodev *dev) { if (!using_spice) { return NULL; @@ -130,7 +130,7 @@ static int line_out_init(HWVoiceOut *hw, struct audsettings *as, settings.freq = SPICE_INTERFACE_PLAYBACK_FREQ; #endif settings.nchannels = SPICE_INTERFACE_PLAYBACK_CHAN; - settings.fmt = AUD_FMT_S16; + settings.fmt = AUDIO_FORMAT_S16; settings.endianness = AUDIO_HOST_ENDIANNESS; audio_pcm_init_info (&hw->info, &settings); @@ -258,7 +258,7 @@ static int line_in_init(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) settings.freq = SPICE_INTERFACE_RECORD_FREQ; #endif settings.nchannels = SPICE_INTERFACE_RECORD_CHAN; - settings.fmt = AUD_FMT_S16; + settings.fmt = AUDIO_FORMAT_S16; settings.endianness = AUDIO_HOST_ENDIANNESS; audio_pcm_init_info (&hw->info, &settings); @@ -373,10 +373,6 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...) return 0; } -static struct audio_option audio_options[] = { - { /* end of list */ }, -}; - static struct audio_pcm_ops audio_callbacks = { .init_out = line_out_init, .fini_out = line_out_fini, @@ -394,7 +390,6 @@ static struct audio_pcm_ops audio_callbacks = { static struct audio_driver spice_audio_driver = { .name = "spice", .descr = "spice audio driver", - .options = audio_options, .init = spice_audio_init, .fini = spice_audio_fini, .pcm_ops = &audio_callbacks, diff --git a/audio/trace-events b/audio/trace-events index c986469319..a1d1eccb8a 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# audio/alsaaudio.c +# alsaaudio.c alsa_revents(int revents) "revents = %d" alsa_pollout(int i, int fd) "i = %d fd = %d" alsa_set_handler(int events, int index, int fd, int err) "events=0x%x index=%d fd=%d err=%d" @@ -12,11 +12,11 @@ alsa_resume_out(void) "Resuming suspended output stream" alsa_resume_in(void) "Resuming suspended input stream" alsa_no_frames(int state) "No frames available and ALSA state is %d" -# audio/ossaudio.c +# ossaudio.c oss_version(int version) "OSS version = 0x%x" oss_invalid_available_size(int size, int bufsize) "Invalid available size, size=%d bufsize=%d" -# audio/audio.c +# audio.c audio_timer_start(int interval) "interval %d ms" audio_timer_stop(void) "" audio_timer_delayed(int interval) "interval %d ms" diff --git a/audio/wavaudio.c b/audio/wavaudio.c index 40adfa30c3..8d30f57296 100644 --- a/audio/wavaudio.c +++ b/audio/wavaudio.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" #include "qemu/timer.h" +#include "qapi/opts-visitor.h" #include "audio.h" #define AUDIO_CAP "wav" @@ -37,11 +38,6 @@ typedef struct WAVVoiceOut { int total_samples; } WAVVoiceOut; -typedef struct { - struct audsettings settings; - const char *wav_path; -} WAVConf; - static int wav_run_out (HWVoiceOut *hw, int live) { WAVVoiceOut *wav = (WAVVoiceOut *) hw; @@ -112,25 +108,30 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, 0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04, 0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00 }; - WAVConf *conf = drv_opaque; - struct audsettings wav_as = conf->settings; + Audiodev *dev = drv_opaque; + AudiodevWavOptions *wopts = &dev->u.wav; + struct audsettings wav_as = audiodev_to_audsettings(dev->u.wav.out); + const char *wav_path = wopts->has_path ? wopts->path : "qemu.wav"; stereo = wav_as.nchannels == 2; switch (wav_as.fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: + case AUDIO_FORMAT_S8: + case AUDIO_FORMAT_U8: bits16 = 0; break; - case AUD_FMT_S16: - case AUD_FMT_U16: + case AUDIO_FORMAT_S16: + case AUDIO_FORMAT_U16: bits16 = 1; break; - case AUD_FMT_S32: - case AUD_FMT_U32: + case AUDIO_FORMAT_S32: + case AUDIO_FORMAT_U32: dolog ("WAVE files can not handle 32bit formats\n"); return -1; + + default: + abort(); } hdr[34] = bits16 ? 0x10 : 0x08; @@ -151,10 +152,10 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as, le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4); le_store (hdr + 32, 1 << (bits16 + stereo), 2); - wav->f = fopen (conf->wav_path, "wb"); + wav->f = fopen(wav_path, "wb"); if (!wav->f) { dolog ("Failed to open wave file `%s'\nReason: %s\n", - conf->wav_path, strerror (errno)); + wav_path, strerror(errno)); g_free (wav->pcm_buf); wav->pcm_buf = NULL; return -1; @@ -222,54 +223,17 @@ static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...) return 0; } -static WAVConf glob_conf = { - .settings.freq = 44100, - .settings.nchannels = 2, - .settings.fmt = AUD_FMT_S16, - .wav_path = "qemu.wav" -}; - -static void *wav_audio_init (void) +static void *wav_audio_init(Audiodev *dev) { - WAVConf *conf = g_malloc(sizeof(WAVConf)); - *conf = glob_conf; - return conf; + assert(dev->driver == AUDIODEV_DRIVER_WAV); + return dev; } static void wav_audio_fini (void *opaque) { ldebug ("wav_fini"); - g_free(opaque); } -static struct audio_option wav_options[] = { - { - .name = "FREQUENCY", - .tag = AUD_OPT_INT, - .valp = &glob_conf.settings.freq, - .descr = "Frequency" - }, - { - .name = "FORMAT", - .tag = AUD_OPT_FMT, - .valp = &glob_conf.settings.fmt, - .descr = "Format" - }, - { - .name = "DAC_FIXED_CHANNELS", - .tag = AUD_OPT_INT, - .valp = &glob_conf.settings.nchannels, - .descr = "Number of channels (1 - mono, 2 - stereo)" - }, - { - .name = "PATH", - .tag = AUD_OPT_STR, - .valp = &glob_conf.wav_path, - .descr = "Path to wave file" - }, - { /* End of list */ } -}; - static struct audio_pcm_ops wav_pcm_ops = { .init_out = wav_init_out, .fini_out = wav_fini_out, @@ -281,7 +245,6 @@ static struct audio_pcm_ops wav_pcm_ops = { static struct audio_driver wav_audio_driver = { .name = "wav", .descr = "WAV renderer http://wikipedia.org/wiki/WAV", - .options = wav_options, .init = wav_audio_init, .fini = wav_audio_fini, .pcm_ops = &wav_pcm_ops, diff --git a/audio/wavcapture.c b/audio/wavcapture.c index cf31ed652c..74320dfecc 100644 --- a/audio/wavcapture.c +++ b/audio/wavcapture.c @@ -38,30 +38,29 @@ static void wav_destroy (void *opaque) uint8_t dlen[4]; uint32_t datalen = wav->bytes; uint32_t rifflen = datalen + 36; - Monitor *mon = cur_mon; if (wav->f) { le_store (rlen, rifflen, 4); le_store (dlen, datalen, 4); if (fseek (wav->f, 4, SEEK_SET)) { - monitor_printf (mon, "wav_destroy: rlen fseek failed\nReason: %s\n", - strerror (errno)); + error_report("wav_destroy: rlen fseek failed: %s", + strerror(errno)); goto doclose; } if (fwrite (rlen, 4, 1, wav->f) != 1) { - monitor_printf (mon, "wav_destroy: rlen fwrite failed\nReason %s\n", - strerror (errno)); + error_report("wav_destroy: rlen fwrite failed: %s", + strerror(errno)); goto doclose; } if (fseek (wav->f, 32, SEEK_CUR)) { - monitor_printf (mon, "wav_destroy: dlen fseek failed\nReason %s\n", - strerror (errno)); + error_report("wav_destroy: dlen fseek failed: %s", + strerror(errno)); goto doclose; } if (fwrite (dlen, 1, 4, wav->f) != 4) { - monitor_printf (mon, "wav_destroy: dlen fwrite failed\nReason %s\n", - strerror (errno)); + error_report("wav_destroy: dlen fwrite failed: %s", + strerror(errno)); goto doclose; } doclose: @@ -78,8 +77,7 @@ static void wav_capture (void *opaque, void *buf, int size) WAVState *wav = opaque; if (fwrite (buf, size, 1, wav->f) != 1) { - monitor_printf (cur_mon, "wav_capture: fwrite error\nReason: %s", - strerror (errno)); + error_report("wav_capture: fwrite error: %s", strerror(errno)); } wav->bytes += size; } @@ -110,7 +108,6 @@ static struct capture_ops wav_capture_ops = { int wav_start_capture (CaptureState *s, const char *path, int freq, int bits, int nchannels) { - Monitor *mon = cur_mon; WAVState *wav; uint8_t hdr[] = { 0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56, @@ -124,13 +121,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, CaptureVoiceOut *cap; if (bits != 8 && bits != 16) { - monitor_printf (mon, "incorrect bit count %d, must be 8 or 16\n", bits); + error_report("incorrect bit count %d, must be 8 or 16", bits); return -1; } if (nchannels != 1 && nchannels != 2) { - monitor_printf (mon, "incorrect channel count %d, must be 1 or 2\n", - nchannels); + error_report("incorrect channel count %d, must be 1 or 2", + nchannels); return -1; } @@ -139,7 +136,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, as.freq = freq; as.nchannels = 1 << stereo; - as.fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8; + as.fmt = bits16 ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; as.endianness = 0; ops.notify = wav_notify; @@ -158,8 +155,8 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, wav->f = fopen (path, "wb"); if (!wav->f) { - monitor_printf (mon, "Failed to open wave file `%s'\nReason: %s\n", - path, strerror (errno)); + error_report("Failed to open wave file `%s': %s", + path, strerror(errno)); g_free (wav); return -1; } @@ -170,14 +167,13 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, wav->freq = freq; if (fwrite (hdr, sizeof (hdr), 1, wav->f) != 1) { - monitor_printf (mon, "Failed to write header\nReason: %s\n", - strerror (errno)); + error_report("Failed to write header: %s", strerror(errno)); goto error_free; } cap = AUD_add_capture (&as, &ops, wav); if (!cap) { - monitor_printf (mon, "Failed to add audio capture\n"); + error_report("Failed to add audio capture"); goto error_free; } @@ -189,8 +185,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq, error_free: g_free (wav->path); if (fclose (wav->f)) { - monitor_printf (mon, "Failed to close wave file\nReason: %s\n", - strerror (errno)); + error_report("Failed to close wave file: %s", strerror(errno)); } g_free (wav); return -1; diff --git a/authz/Makefile.objs b/authz/Makefile.objs new file mode 100644 index 0000000000..ed7b273596 --- /dev/null +++ b/authz/Makefile.objs @@ -0,0 +1,7 @@ +authz-obj-y += base.o +authz-obj-y += simple.o +authz-obj-y += list.o +authz-obj-y += listfile.o +authz-obj-$(CONFIG_AUTH_PAM) += pamacct.o + +pamacct.o-libs = -lpam diff --git a/authz/base.c b/authz/base.c new file mode 100644 index 0000000000..110dfa4195 --- /dev/null +++ b/authz/base.c @@ -0,0 +1,82 @@ +/* + * QEMU authorization framework base class + * + * Copyright (c) 2018 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 "authz/base.h" +#include "authz/trace.h" + +bool qauthz_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZClass *cls = QAUTHZ_GET_CLASS(authz); + bool allowed; + + allowed = cls->is_allowed(authz, identity, errp); + trace_qauthz_is_allowed(authz, identity, allowed); + + return allowed; +} + + +bool qauthz_is_allowed_by_id(const char *authzid, + const char *identity, + Error **errp) +{ + QAuthZ *authz; + Object *obj; + Object *container; + + container = object_get_objects_root(); + obj = object_resolve_path_component(container, + authzid); + if (!obj) { + error_setg(errp, "Cannot find QAuthZ object ID %s", + authzid); + return false; + } + + if (!object_dynamic_cast(obj, TYPE_QAUTHZ)) { + error_setg(errp, "Object '%s' is not a QAuthZ subclass", + authzid); + return false; + } + + authz = QAUTHZ(obj); + + return qauthz_is_allowed(authz, identity, errp); +} + + +static const TypeInfo authz_info = { + .parent = TYPE_OBJECT, + .name = TYPE_QAUTHZ, + .instance_size = sizeof(QAuthZ), + .class_size = sizeof(QAuthZClass), + .abstract = true, +}; + +static void qauthz_register_types(void) +{ + type_register_static(&authz_info); +} + +type_init(qauthz_register_types) + diff --git a/authz/list.c b/authz/list.c new file mode 100644 index 0000000000..dc6b0fec13 --- /dev/null +++ b/authz/list.c @@ -0,0 +1,271 @@ +/* + * QEMU access control list authorization driver + * + * Copyright (c) 2018 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 "authz/list.h" +#include "authz/trace.h" +#include "qom/object_interfaces.h" +#include "qapi/qapi-visit-authz.h" + +static bool qauthz_list_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZList *lauthz = QAUTHZ_LIST(authz); + QAuthZListRuleList *rules = lauthz->rules; + + while (rules) { + QAuthZListRule *rule = rules->value; + QAuthZListFormat format = rule->has_format ? rule->format : + QAUTHZ_LIST_FORMAT_EXACT; + + trace_qauthz_list_check_rule(authz, rule->match, identity, + format, rule->policy); + switch (format) { + case QAUTHZ_LIST_FORMAT_EXACT: + if (g_str_equal(rule->match, identity)) { + return rule->policy == QAUTHZ_LIST_POLICY_ALLOW; + } + break; + case QAUTHZ_LIST_FORMAT_GLOB: + if (g_pattern_match_simple(rule->match, identity)) { + return rule->policy == QAUTHZ_LIST_POLICY_ALLOW; + } + break; + default: + g_warn_if_reached(); + return false; + } + rules = rules->next; + } + + trace_qauthz_list_default_policy(authz, identity, lauthz->policy); + return lauthz->policy == QAUTHZ_LIST_POLICY_ALLOW; +} + + +static void +qauthz_list_prop_set_policy(Object *obj, + int value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZList *lauthz = QAUTHZ_LIST(obj); + + lauthz->policy = value; +} + + +static int +qauthz_list_prop_get_policy(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZList *lauthz = QAUTHZ_LIST(obj); + + return lauthz->policy; +} + + +static void +qauthz_list_prop_get_rules(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QAuthZList *lauthz = QAUTHZ_LIST(obj); + + visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp); +} + +static void +qauthz_list_prop_set_rules(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + QAuthZList *lauthz = QAUTHZ_LIST(obj); + QAuthZListRuleList *oldrules; + + oldrules = lauthz->rules; + visit_type_QAuthZListRuleList(v, name, &lauthz->rules, errp); + + qapi_free_QAuthZListRuleList(oldrules); +} + + +static void +qauthz_list_finalize(Object *obj) +{ + QAuthZList *lauthz = QAUTHZ_LIST(obj); + + qapi_free_QAuthZListRuleList(lauthz->rules); +} + + +static void +qauthz_list_class_init(ObjectClass *oc, void *data) +{ + QAuthZClass *authz = QAUTHZ_CLASS(oc); + + object_class_property_add_enum(oc, "policy", + "QAuthZListPolicy", + &QAuthZListPolicy_lookup, + qauthz_list_prop_get_policy, + qauthz_list_prop_set_policy, + NULL); + + object_class_property_add(oc, "rules", "QAuthZListRule", + qauthz_list_prop_get_rules, + qauthz_list_prop_set_rules, + NULL, NULL, NULL); + + authz->is_allowed = qauthz_list_is_allowed; +} + + +QAuthZList *qauthz_list_new(const char *id, + QAuthZListPolicy policy, + Error **errp) +{ + return QAUTHZ_LIST( + object_new_with_props(TYPE_QAUTHZ_LIST, + object_get_objects_root(), + id, errp, + "policy", QAuthZListPolicy_str(policy), + NULL)); +} + +ssize_t qauthz_list_append_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + Error **errp) +{ + QAuthZListRule *rule; + QAuthZListRuleList *rules, *tmp; + size_t i = 0; + + rule = g_new0(QAuthZListRule, 1); + rule->policy = policy; + rule->match = g_strdup(match); + rule->format = format; + rule->has_format = true; + + tmp = g_new0(QAuthZListRuleList, 1); + tmp->value = rule; + + rules = auth->rules; + if (rules) { + while (rules->next) { + i++; + rules = rules->next; + } + rules->next = tmp; + return i + 1; + } else { + auth->rules = tmp; + return 0; + } +} + + +ssize_t qauthz_list_insert_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + size_t index, + Error **errp) +{ + QAuthZListRule *rule; + QAuthZListRuleList *rules, *tmp; + size_t i = 0; + + rule = g_new0(QAuthZListRule, 1); + rule->policy = policy; + rule->match = g_strdup(match); + rule->format = format; + rule->has_format = true; + + tmp = g_new0(QAuthZListRuleList, 1); + tmp->value = rule; + + rules = auth->rules; + if (rules && index > 0) { + while (rules->next && i < (index - 1)) { + i++; + rules = rules->next; + } + tmp->next = rules->next; + rules->next = tmp; + return i + 1; + } else { + tmp->next = auth->rules; + auth->rules = tmp; + return 0; + } +} + + +ssize_t qauthz_list_delete_rule(QAuthZList *auth, const char *match) +{ + QAuthZListRule *rule; + QAuthZListRuleList *rules, *prev; + size_t i = 0; + + prev = NULL; + rules = auth->rules; + while (rules) { + rule = rules->value; + if (g_str_equal(rule->match, match)) { + if (prev) { + prev->next = rules->next; + } else { + auth->rules = rules->next; + } + rules->next = NULL; + qapi_free_QAuthZListRuleList(rules); + return i; + } + prev = rules; + rules = rules->next; + i++; + } + + return -1; +} + + +static const TypeInfo qauthz_list_info = { + .parent = TYPE_QAUTHZ, + .name = TYPE_QAUTHZ_LIST, + .instance_size = sizeof(QAuthZList), + .instance_finalize = qauthz_list_finalize, + .class_size = sizeof(QAuthZListClass), + .class_init = qauthz_list_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_list_register_types(void) +{ + type_register_static(&qauthz_list_info); +} + + +type_init(qauthz_list_register_types); diff --git a/authz/listfile.c b/authz/listfile.c new file mode 100644 index 0000000000..bc2b58ef6d --- /dev/null +++ b/authz/listfile.c @@ -0,0 +1,283 @@ +/* + * QEMU access control list file authorization driver + * + * Copyright (c) 2018 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 "authz/listfile.h" +#include "authz/trace.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qemu/filemonitor.h" +#include "qom/object_interfaces.h" +#include "qapi/qapi-visit-authz.h" +#include "qapi/qmp/qjson.h" +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qerror.h" +#include "qapi/qobject-input-visitor.h" + + +static bool +qauthz_list_file_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(authz); + if (fauthz->list) { + return qauthz_is_allowed(fauthz->list, identity, errp); + } + + return false; +} + + +static QAuthZ * +qauthz_list_file_load(QAuthZListFile *fauthz, Error **errp) +{ + GError *err = NULL; + gchar *content = NULL; + gsize len; + QObject *obj = NULL; + QDict *pdict; + Visitor *v = NULL; + QAuthZ *ret = NULL; + + trace_qauthz_list_file_load(fauthz, fauthz->filename); + if (!g_file_get_contents(fauthz->filename, &content, &len, &err)) { + error_setg(errp, "Unable to read '%s': %s", + fauthz->filename, err->message); + goto cleanup; + } + + obj = qobject_from_json(content, errp); + if (!obj) { + goto cleanup; + } + + pdict = qobject_to(QDict, obj); + if (!pdict) { + error_setg(errp, QERR_INVALID_PARAMETER_TYPE, "obj", "dict"); + goto cleanup; + } + + v = qobject_input_visitor_new(obj); + + ret = (QAuthZ *)user_creatable_add_type(TYPE_QAUTHZ_LIST, + NULL, pdict, v, errp); + + cleanup: + visit_free(v); + qobject_unref(obj); + if (err) { + g_error_free(err); + } + g_free(content); + return ret; +} + + +static void +qauthz_list_file_event(int64_t wd G_GNUC_UNUSED, + QFileMonitorEvent ev G_GNUC_UNUSED, + const char *name G_GNUC_UNUSED, + void *opaque) +{ + QAuthZListFile *fauthz = opaque; + Error *err = NULL; + + if (ev != QFILE_MONITOR_EVENT_MODIFIED && + ev != QFILE_MONITOR_EVENT_CREATED) { + return; + } + + object_unref(OBJECT(fauthz->list)); + fauthz->list = qauthz_list_file_load(fauthz, &err); + trace_qauthz_list_file_refresh(fauthz, + fauthz->filename, fauthz->list ? 1 : 0); + if (!fauthz->list) { + error_report_err(err); + } +} + +static void +qauthz_list_file_complete(UserCreatable *uc, Error **errp) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(uc); + gchar *dir = NULL, *file = NULL; + + fauthz->list = qauthz_list_file_load(fauthz, errp); + + if (!fauthz->refresh) { + return; + } + + fauthz->file_monitor = qemu_file_monitor_new(errp); + if (!fauthz->file_monitor) { + return; + } + + dir = g_path_get_dirname(fauthz->filename); + if (g_str_equal(dir, ".")) { + error_setg(errp, "Filename must be an absolute path"); + goto cleanup; + } + file = g_path_get_basename(fauthz->filename); + if (g_str_equal(file, ".")) { + error_setg(errp, "Path has no trailing filename component"); + goto cleanup; + } + + fauthz->file_watch = qemu_file_monitor_add_watch( + fauthz->file_monitor, dir, file, + qauthz_list_file_event, fauthz, errp); + if (fauthz->file_watch < 0) { + goto cleanup; + } + + cleanup: + g_free(file); + g_free(dir); +} + + +static void +qauthz_list_file_prop_set_filename(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); + + g_free(fauthz->filename); + fauthz->filename = g_strdup(value); +} + + +static char * +qauthz_list_file_prop_get_filename(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); + + return g_strdup(fauthz->filename); +} + + +static void +qauthz_list_file_prop_set_refresh(Object *obj, + bool value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); + + fauthz->refresh = value; +} + + +static bool +qauthz_list_file_prop_get_refresh(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); + + return fauthz->refresh; +} + + +static void +qauthz_list_file_finalize(Object *obj) +{ + QAuthZListFile *fauthz = QAUTHZ_LIST_FILE(obj); + + object_unref(OBJECT(fauthz->list)); + g_free(fauthz->filename); + qemu_file_monitor_free(fauthz->file_monitor); +} + + +static void +qauthz_list_file_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + QAuthZClass *authz = QAUTHZ_CLASS(oc); + + ucc->complete = qauthz_list_file_complete; + + object_class_property_add_str(oc, "filename", + qauthz_list_file_prop_get_filename, + qauthz_list_file_prop_set_filename, + NULL); + object_class_property_add_bool(oc, "refresh", + qauthz_list_file_prop_get_refresh, + qauthz_list_file_prop_set_refresh, + NULL); + + authz->is_allowed = qauthz_list_file_is_allowed; +} + + +static void +qauthz_list_file_init(Object *obj) +{ + QAuthZListFile *authz = QAUTHZ_LIST_FILE(obj); + + authz->file_watch = -1; +#ifdef CONFIG_INOTIFY1 + authz->refresh = TRUE; +#endif +} + + +QAuthZListFile *qauthz_list_file_new(const char *id, + const char *filename, + bool refresh, + Error **errp) +{ + return QAUTHZ_LIST_FILE( + object_new_with_props(TYPE_QAUTHZ_LIST_FILE, + object_get_objects_root(), + id, errp, + "filename", filename, + "refresh", refresh ? "yes" : "no", + NULL)); +} + + +static const TypeInfo qauthz_list_file_info = { + .parent = TYPE_QAUTHZ, + .name = TYPE_QAUTHZ_LIST_FILE, + .instance_init = qauthz_list_file_init, + .instance_size = sizeof(QAuthZListFile), + .instance_finalize = qauthz_list_file_finalize, + .class_size = sizeof(QAuthZListFileClass), + .class_init = qauthz_list_file_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_list_file_register_types(void) +{ + type_register_static(&qauthz_list_file_info); +} + + +type_init(qauthz_list_file_register_types); diff --git a/authz/pamacct.c b/authz/pamacct.c new file mode 100644 index 0000000000..5038358cdc --- /dev/null +++ b/authz/pamacct.c @@ -0,0 +1,148 @@ +/* + * QEMU PAM authorization driver + * + * Copyright (c) 2018 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 "authz/pamacct.h" +#include "authz/trace.h" +#include "qom/object_interfaces.h" + +#include + + +static bool qauthz_pam_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZPAM *pauthz = QAUTHZ_PAM(authz); + const struct pam_conv pam_conversation = { 0 }; + pam_handle_t *pamh = NULL; + int ret; + + trace_qauthz_pam_check(authz, identity, pauthz->service); + ret = pam_start(pauthz->service, + identity, + &pam_conversation, + &pamh); + if (ret != PAM_SUCCESS) { + error_setg(errp, "Unable to start PAM transaction: %s", + pam_strerror(NULL, ret)); + return false; + } + + ret = pam_acct_mgmt(pamh, PAM_SILENT); + pam_end(pamh, ret); + if (ret != PAM_SUCCESS) { + error_setg(errp, "Unable to authorize user '%s': %s", + identity, pam_strerror(pamh, ret)); + return false; + } + + return true; +} + + +static void +qauthz_pam_prop_set_service(Object *obj, + const char *service, + Error **errp G_GNUC_UNUSED) +{ + QAuthZPAM *pauthz = QAUTHZ_PAM(obj); + + g_free(pauthz->service); + pauthz->service = g_strdup(service); +} + + +static char * +qauthz_pam_prop_get_service(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZPAM *pauthz = QAUTHZ_PAM(obj); + + return g_strdup(pauthz->service); +} + + +static void +qauthz_pam_complete(UserCreatable *uc, Error **errp) +{ +} + + +static void +qauthz_pam_finalize(Object *obj) +{ + QAuthZPAM *pauthz = QAUTHZ_PAM(obj); + + g_free(pauthz->service); +} + + +static void +qauthz_pam_class_init(ObjectClass *oc, void *data) +{ + UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + QAuthZClass *authz = QAUTHZ_CLASS(oc); + + ucc->complete = qauthz_pam_complete; + authz->is_allowed = qauthz_pam_is_allowed; + + object_class_property_add_str(oc, "service", + qauthz_pam_prop_get_service, + qauthz_pam_prop_set_service, + NULL); +} + + +QAuthZPAM *qauthz_pam_new(const char *id, + const char *service, + Error **errp) +{ + return QAUTHZ_PAM( + object_new_with_props(TYPE_QAUTHZ_PAM, + object_get_objects_root(), + id, errp, + "service", service, + NULL)); +} + + +static const TypeInfo qauthz_pam_info = { + .parent = TYPE_QAUTHZ, + .name = TYPE_QAUTHZ_PAM, + .instance_size = sizeof(QAuthZPAM), + .instance_finalize = qauthz_pam_finalize, + .class_size = sizeof(QAuthZPAMClass), + .class_init = qauthz_pam_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_pam_register_types(void) +{ + type_register_static(&qauthz_pam_info); +} + + +type_init(qauthz_pam_register_types); diff --git a/authz/simple.c b/authz/simple.c new file mode 100644 index 0000000000..8ab718803e --- /dev/null +++ b/authz/simple.c @@ -0,0 +1,115 @@ +/* + * QEMU simple authorization driver + * + * Copyright (c) 2018 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 "authz/simple.h" +#include "authz/trace.h" +#include "qom/object_interfaces.h" + +static bool qauthz_simple_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp) +{ + QAuthZSimple *sauthz = QAUTHZ_SIMPLE(authz); + + trace_qauthz_simple_is_allowed(authz, sauthz->identity, identity); + return g_str_equal(identity, sauthz->identity); +} + +static void +qauthz_simple_prop_set_identity(Object *obj, + const char *value, + Error **errp G_GNUC_UNUSED) +{ + QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); + + g_free(sauthz->identity); + sauthz->identity = g_strdup(value); +} + + +static char * +qauthz_simple_prop_get_identity(Object *obj, + Error **errp G_GNUC_UNUSED) +{ + QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); + + return g_strdup(sauthz->identity); +} + + +static void +qauthz_simple_finalize(Object *obj) +{ + QAuthZSimple *sauthz = QAUTHZ_SIMPLE(obj); + + g_free(sauthz->identity); +} + + +static void +qauthz_simple_class_init(ObjectClass *oc, void *data) +{ + QAuthZClass *authz = QAUTHZ_CLASS(oc); + + authz->is_allowed = qauthz_simple_is_allowed; + + object_class_property_add_str(oc, "identity", + qauthz_simple_prop_get_identity, + qauthz_simple_prop_set_identity, + NULL); +} + + +QAuthZSimple *qauthz_simple_new(const char *id, + const char *identity, + Error **errp) +{ + return QAUTHZ_SIMPLE( + object_new_with_props(TYPE_QAUTHZ_SIMPLE, + object_get_objects_root(), + id, errp, + "identity", identity, + NULL)); +} + + +static const TypeInfo qauthz_simple_info = { + .parent = TYPE_QAUTHZ, + .name = TYPE_QAUTHZ_SIMPLE, + .instance_size = sizeof(QAuthZSimple), + .instance_finalize = qauthz_simple_finalize, + .class_size = sizeof(QAuthZSimpleClass), + .class_init = qauthz_simple_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_USER_CREATABLE }, + { } + } +}; + + +static void +qauthz_simple_register_types(void) +{ + type_register_static(&qauthz_simple_info); +} + + +type_init(qauthz_simple_register_types); diff --git a/authz/trace-events b/authz/trace-events new file mode 100644 index 0000000000..e62ebb36b7 --- /dev/null +++ b/authz/trace-events @@ -0,0 +1,18 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# base.c +qauthz_is_allowed(void *authz, const char *identity, bool allowed) "AuthZ %p check identity=%s allowed=%d" + +# simple.c +qauthz_simple_is_allowed(void *authz, const char *wantidentity, const char *gotidentity) "AuthZ simple %p check want identity=%s got identity=%s" + +# list.c +qauthz_list_check_rule(void *authz, const char *identity, const char *rule, int format, int policy) "AuthZ list %p check rule=%s identity=%s format=%d policy=%d" +qauthz_list_default_policy(void *authz, const char *identity, int policy) "AuthZ list %p default identity=%s policy=%d" + +# listfile.c +qauthz_list_file_load(void *authz, const char *filename) "AuthZ file %p load filename=%s" +qauthz_list_file_refresh(void *authz, const char *filename, int success) "AuthZ file %p load filename=%s success=%d" + +# pamacct.c +qauthz_pam_check(void *authz, const char *identity, const char *service) "AuthZ PAM %p identity=%s service=%s" diff --git a/backends/Makefile.objs b/backends/Makefile.objs index ad7c0325ed..ff619d31b4 100644 --- a/backends/Makefile.objs +++ b/backends/Makefile.objs @@ -4,15 +4,14 @@ common-obj-$(CONFIG_POSIX) += rng-random.o common-obj-$(CONFIG_TPM) += tpm.o common-obj-y += hostmem.o hostmem-ram.o -common-obj-$(CONFIG_LINUX) += hostmem-file.o +common-obj-$(CONFIG_POSIX) += hostmem-file.o common-obj-y += cryptodev.o common-obj-y += cryptodev-builtin.o -ifeq ($(CONFIG_VIRTIO),y) +ifeq ($(CONFIG_VIRTIO_CRYPTO),y) common-obj-y += cryptodev-vhost.o -common-obj-$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) += \ - cryptodev-vhost-user.o +common-obj-$(CONFIG_VHOST_CRYPTO) += cryptodev-vhost-user.o endif common-obj-$(CONFIG_LINUX) += hostmem-memfd.o diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index d539f14d59..1052a5d0e9 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -47,7 +47,7 @@ typedef struct CryptoDevBackendVhostUser { CryptoDevBackend parent_obj; - VhostUserState *vhost_user; + VhostUserState vhost_user; CharBackend chr; char *chr_name; bool opened; @@ -104,7 +104,7 @@ cryptodev_vhost_user_start(int queues, continue; } - options.opaque = s->vhost_user; + options.opaque = &s->vhost_user; options.backend_type = VHOST_BACKEND_TYPE_USER; options.cc = b->conf.peers.ccs[i]; s->vhost_crypto[i] = cryptodev_vhost_init(&options); @@ -182,7 +182,6 @@ static void cryptodev_vhost_user_init( size_t i; Error *local_err = NULL; Chardev *chr; - VhostUserState *user; CryptoDevBackendClient *cc; CryptoDevBackendVhostUser *s = CRYPTODEV_BACKEND_VHOST_USER(backend); @@ -213,15 +212,10 @@ static void cryptodev_vhost_user_init( } } - user = vhost_user_init(); - if (!user) { - error_setg(errp, "Failed to init vhost_user"); + if (!vhost_user_init(&s->vhost_user, &s->chr, errp)) { return; } - user->chr = &s->chr; - s->vhost_user = user; - qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, cryptodev_vhost_user_event, NULL, s, NULL, true); @@ -307,11 +301,7 @@ static void cryptodev_vhost_user_cleanup( } } - if (s->vhost_user) { - vhost_user_cleanup(s->vhost_user); - g_free(s->vhost_user); - s->vhost_user = NULL; - } + vhost_user_cleanup(&s->vhost_user); } static void cryptodev_vhost_user_set_chardev(Object *obj, diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 134b08d63a..37ac6445d2 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu-common.h" +#include "qemu/error-report.h" #include "sysemu/hostmem.h" #include "sysemu/sysemu.h" #include "qom/object_interfaces.h" @@ -31,15 +32,21 @@ typedef struct HostMemoryBackendFile HostMemoryBackendFile; struct HostMemoryBackendFile { HostMemoryBackend parent_obj; - bool discard_data; char *mem_path; uint64_t align; + bool discard_data; + bool is_pmem; }; static void file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { +#ifndef CONFIG_POSIX + error_setg(errp, "backend '%s' not supported on this host", + object_get_typename(OBJECT(backend))); +#else HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(backend); + gchar *name; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); @@ -49,19 +56,38 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) error_setg(errp, "mem-path property not set"); return; } -#ifndef CONFIG_LINUX - error_setg(errp, "-mem-path not supported on this host"); -#else - if (!host_memory_backend_mr_inited(backend)) { - gchar *path; - backend->force_prealloc = mem_prealloc; - path = object_get_canonical_path(OBJECT(backend)); - memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), - path, - backend->size, fb->align, backend->share, - fb->mem_path, errp); - g_free(path); + + /* + * Verify pmem file size since starting a guest with an incorrect size + * leads to confusing failures inside the guest. + */ + if (fb->is_pmem) { + Error *local_err = NULL; + uint64_t size; + + size = qemu_get_pmem_size(fb->mem_path, &local_err); + if (!size) { + error_propagate(errp, local_err); + return; + } + + if (backend->size > size) { + error_setg(errp, "size property %" PRIu64 " is larger than " + "pmem file \"%s\" size %" PRIu64, backend->size, + fb->mem_path, size); + return; + } } + + backend->force_prealloc = mem_prealloc; + name = host_memory_backend_get_name(backend); + memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), + name, + backend->size, fb->align, + (backend->share ? RAM_SHARED : 0) | + (fb->is_pmem ? RAM_PMEM : 0), + fb->mem_path, errp); + g_free(name); #endif } @@ -78,7 +104,8 @@ static void set_mem_path(Object *o, const char *str, Error **errp) HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); if (host_memory_backend_mr_inited(backend)) { - error_setg(errp, "cannot change property value"); + error_setg(errp, "cannot change property 'mem-path' of %s", + object_get_typename(o)); return; } g_free(fb->mem_path); @@ -116,7 +143,8 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, uint64_t val; if (host_memory_backend_mr_inited(backend)) { - error_setg(&local_err, "cannot change property value"); + error_setg(&local_err, "cannot change property '%s' of %s", + name, object_get_typename(o)); goto out; } @@ -130,6 +158,39 @@ static void file_memory_backend_set_align(Object *o, Visitor *v, error_propagate(errp, local_err); } +static bool file_memory_backend_get_pmem(Object *o, Error **errp) +{ + return MEMORY_BACKEND_FILE(o)->is_pmem; +} + +static void file_memory_backend_set_pmem(Object *o, bool value, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(o); + HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o); + + if (host_memory_backend_mr_inited(backend)) { + + error_setg(errp, "cannot change property 'pmem' of %s.", + object_get_typename(o)); + return; + } + +#ifndef CONFIG_LIBPMEM + if (value) { + Error *local_err = NULL; + + error_setg(&local_err, + "Lack of libpmem support while setting the 'pmem=on'" + " of %s. We can't ensure data persistence.", + object_get_typename(o)); + error_propagate(errp, local_err); + return; + } +#endif + + fb->is_pmem = value; +} + static void file_backend_unparent(Object *obj) { HostMemoryBackend *backend = MEMORY_BACKEND(obj); @@ -161,6 +222,9 @@ file_backend_class_init(ObjectClass *oc, void *data) file_memory_backend_get_align, file_memory_backend_set_align, NULL, NULL, &error_abort); + object_class_property_add_bool(oc, "pmem", + file_memory_backend_get_pmem, file_memory_backend_set_pmem, + &error_abort); } static void file_backend_instance_finalize(Object *o) diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 1e20fe0ba8..46b15b916a 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -44,10 +44,6 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return; } - if (host_memory_backend_mr_inited(backend)) { - return; - } - backend->force_prealloc = mem_prealloc; fd = qemu_memfd_create(TYPE_MEMORY_BACKEND_MEMFD, backend->size, m->hugetlb, m->hugetlbsize, m->seal ? @@ -57,9 +53,10 @@ memfd_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) return; } - name = object_get_canonical_path(OBJECT(backend)); + name = host_memory_backend_get_name(backend); memory_region_init_ram_from_fd(&backend->mr, OBJECT(backend), - name, backend->size, true, fd, errp); + name, backend->size, + backend->share, fd, errp); g_free(name); } @@ -131,6 +128,7 @@ memfd_backend_instance_init(Object *obj) /* default to sealed file */ m->seal = true; + MEMORY_BACKEND(m)->share = true; } static void @@ -140,18 +138,29 @@ memfd_backend_class_init(ObjectClass *oc, void *data) bc->alloc = memfd_backend_memory_alloc; - object_class_property_add_bool(oc, "hugetlb", - memfd_backend_get_hugetlb, - memfd_backend_set_hugetlb, - &error_abort); - object_class_property_add(oc, "hugetlbsize", "int", - memfd_backend_get_hugetlbsize, - memfd_backend_set_hugetlbsize, - NULL, NULL, &error_abort); + if (qemu_memfd_check(MFD_HUGETLB)) { + object_class_property_add_bool(oc, "hugetlb", + memfd_backend_get_hugetlb, + memfd_backend_set_hugetlb, + &error_abort); + object_class_property_set_description(oc, "hugetlb", + "Use huge pages", + &error_abort); + object_class_property_add(oc, "hugetlbsize", "int", + memfd_backend_get_hugetlbsize, + memfd_backend_set_hugetlbsize, + NULL, NULL, &error_abort); + object_class_property_set_description(oc, "hugetlbsize", + "Huge pages size (ex: 2M, 1G)", + &error_abort); + } object_class_property_add_bool(oc, "seal", memfd_backend_get_seal, memfd_backend_set_seal, &error_abort); + object_class_property_set_description(oc, "seal", + "Seal growing & shrinking", + &error_abort); } static const TypeInfo memfd_backend_info = { @@ -164,7 +173,9 @@ static const TypeInfo memfd_backend_info = { static void register_types(void) { - type_register_static(&memfd_backend_info); + if (qemu_memfd_check(MFD_ALLOW_SEALING)) { + type_register_static(&memfd_backend_info); + } } type_init(register_types); diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index 7ddd08d370..24b65d9ae3 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -16,21 +16,20 @@ #define TYPE_MEMORY_BACKEND_RAM "memory-backend-ram" - static void ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) { - char *path; + char *name; if (!backend->size) { error_setg(errp, "can't create backend with size 0"); return; } - path = object_get_canonical_path_component(OBJECT(backend)); - memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), path, + name = host_memory_backend_get_name(backend); + memory_region_init_ram_shared_nomigrate(&backend->mr, OBJECT(backend), name, backend->size, backend->share, errp); - g_free(path); + g_free(name); } static void diff --git a/backends/hostmem.c b/backends/hostmem.c index 4908946cd3..04baf479a1 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -28,6 +28,16 @@ QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_BIND != MPOL_BIND); QEMU_BUILD_BUG_ON(HOST_MEM_POLICY_INTERLEAVE != MPOL_INTERLEAVE); #endif +char * +host_memory_backend_get_name(HostMemoryBackend *backend) +{ + if (!backend->use_canonical_path) { + return object_get_canonical_path_component(OBJECT(backend)); + } + + return object_get_canonical_path(OBJECT(backend)); +} + static void host_memory_backend_get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -47,7 +57,8 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, uint64_t value; if (host_memory_backend_mr_inited(backend)) { - error_setg(&local_err, "cannot change property value"); + error_setg(&local_err, "cannot change property %s of %s ", + name, object_get_typename(obj)); goto out; } @@ -56,8 +67,9 @@ host_memory_backend_set_size(Object *obj, Visitor *v, const char *name, goto out; } if (!value) { - error_setg(&local_err, "Property '%s.%s' doesn't take value '%" - PRIu64 "'", object_get_typename(obj), name, value); + error_setg(&local_err, + "property '%s' of %s doesn't take value '%" PRIu64 "'", + name, object_get_typename(obj), value); goto out; } backend->size = value; @@ -76,7 +88,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, value = find_first_bit(backend->host_nodes, MAX_NODES); if (value == MAX_NODES) { - return; + goto ret; } *node = g_malloc0(sizeof(**node)); @@ -94,6 +106,7 @@ host_memory_backend_get_host_nodes(Object *obj, Visitor *v, const char *name, node = &(*node)->next; } while (true); +ret: visit_type_uint16List(v, name, &host_nodes, errp); } @@ -103,14 +116,23 @@ host_memory_backend_set_host_nodes(Object *obj, Visitor *v, const char *name, { #ifdef CONFIG_NUMA HostMemoryBackend *backend = MEMORY_BACKEND(obj); - uint16List *l = NULL; + uint16List *l, *host_nodes = NULL; - visit_type_uint16List(v, name, &l, errp); + visit_type_uint16List(v, name, &host_nodes, errp); - while (l) { - bitmap_set(backend->host_nodes, l->value, 1); - l = l->next; + for (l = host_nodes; l; l = l->next) { + if (l->value >= MAX_NODES) { + error_setg(errp, "Invalid host-nodes value: %d", l->value); + goto out; + } } + + for (l = host_nodes; l; l = l->next) { + bitmap_set(backend->host_nodes, l->value, 1); + } + +out: + qapi_free_uint16List(host_nodes); #else error_setg(errp, "NUMA node binding are not supported by this QEMU"); #endif @@ -238,6 +260,11 @@ static void host_memory_backend_init(Object *obj) backend->prealloc = mem_prealloc; } +static void host_memory_backend_post_init(Object *obj) +{ + object_apply_compat_props(obj); +} + bool host_memory_backend_mr_inited(HostMemoryBackend *backend) { /* @@ -386,6 +413,23 @@ static void host_memory_backend_set_share(Object *o, bool value, Error **errp) backend->share = value; } +static bool +host_memory_backend_get_use_canonical_path(Object *obj, Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + return backend->use_canonical_path; +} + +static void +host_memory_backend_set_use_canonical_path(Object *obj, bool value, + Error **errp) +{ + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + + backend->use_canonical_path = value; +} + static void host_memory_backend_class_init(ObjectClass *oc, void *data) { @@ -397,27 +441,44 @@ host_memory_backend_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, "merge", host_memory_backend_get_merge, host_memory_backend_set_merge, &error_abort); + object_class_property_set_description(oc, "merge", + "Mark memory as mergeable", &error_abort); object_class_property_add_bool(oc, "dump", host_memory_backend_get_dump, host_memory_backend_set_dump, &error_abort); + object_class_property_set_description(oc, "dump", + "Set to 'off' to exclude from core dump", &error_abort); object_class_property_add_bool(oc, "prealloc", host_memory_backend_get_prealloc, host_memory_backend_set_prealloc, &error_abort); + object_class_property_set_description(oc, "prealloc", + "Preallocate memory", &error_abort); object_class_property_add(oc, "size", "int", host_memory_backend_get_size, host_memory_backend_set_size, NULL, NULL, &error_abort); + object_class_property_set_description(oc, "size", + "Size of the memory region (ex: 500M)", &error_abort); object_class_property_add(oc, "host-nodes", "int", host_memory_backend_get_host_nodes, host_memory_backend_set_host_nodes, NULL, NULL, &error_abort); + object_class_property_set_description(oc, "host-nodes", + "Binds memory to the list of NUMA host nodes", &error_abort); object_class_property_add_enum(oc, "policy", "HostMemPolicy", &HostMemPolicy_lookup, host_memory_backend_get_policy, host_memory_backend_set_policy, &error_abort); + object_class_property_set_description(oc, "policy", + "Set the NUMA policy", &error_abort); object_class_property_add_bool(oc, "share", host_memory_backend_get_share, host_memory_backend_set_share, &error_abort); + object_class_property_set_description(oc, "share", + "Mark the memory as private to QEMU or shared", &error_abort); + object_class_property_add_bool(oc, "x-use-canonical-path-for-ramblock-id", + host_memory_backend_get_use_canonical_path, + host_memory_backend_set_use_canonical_path, &error_abort); } static const TypeInfo host_memory_backend_info = { @@ -428,6 +489,7 @@ static const TypeInfo host_memory_backend_info = { .class_init = host_memory_backend_class_init, .instance_size = sizeof(HostMemoryBackend), .instance_init = host_memory_backend_init, + .instance_post_init = host_memory_backend_post_init, .interfaces = (InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } diff --git a/balloon.c b/balloon.c index 6bf0a96813..9319879838 100644 --- a/balloon.c +++ b/balloon.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu-common.h" +#include "qemu/atomic.h" #include "exec/cpu-common.h" #include "sysemu/kvm.h" #include "sysemu/balloon.h" @@ -37,16 +38,22 @@ static QEMUBalloonEvent *balloon_event_fn; static QEMUBalloonStatus *balloon_stat_fn; static void *balloon_opaque; -static bool balloon_inhibited; +static int balloon_inhibit_count; bool qemu_balloon_is_inhibited(void) { - return balloon_inhibited; + return atomic_read(&balloon_inhibit_count) > 0; } void qemu_balloon_inhibit(bool state) { - balloon_inhibited = state; + if (state) { + atomic_inc(&balloon_inhibit_count); + } else { + atomic_dec(&balloon_inhibit_count); + } + + assert(atomic_read(&balloon_inhibit_count) >= 0); } static bool have_balloon(Error **errp) diff --git a/block.c b/block.c index 39f373e035..16615bc876 100644 --- a/block.c +++ b/block.c @@ -152,53 +152,53 @@ int path_is_absolute(const char *path) #endif } -/* if filename is absolute, just copy it to dest. Otherwise, build a +/* if filename is absolute, just return its duplicate. Otherwise, build a path to it by considering it is relative to base_path. URL are supported. */ -void path_combine(char *dest, int dest_size, - const char *base_path, - const char *filename) +char *path_combine(const char *base_path, const char *filename) { + const char *protocol_stripped = NULL; const char *p, *p1; + char *result; int len; - if (dest_size <= 0) - return; if (path_is_absolute(filename)) { - pstrcpy(dest, dest_size, filename); - } else { - const char *protocol_stripped = NULL; - - if (path_has_protocol(base_path)) { - protocol_stripped = strchr(base_path, ':'); - if (protocol_stripped) { - protocol_stripped++; - } - } - p = protocol_stripped ?: base_path; - - p1 = strrchr(base_path, '/'); -#ifdef _WIN32 - { - const char *p2; - p2 = strrchr(base_path, '\\'); - if (!p1 || p2 > p1) - p1 = p2; - } -#endif - if (p1) - p1++; - else - p1 = base_path; - if (p1 > p) - p = p1; - len = p - base_path; - if (len > dest_size - 1) - len = dest_size - 1; - memcpy(dest, base_path, len); - dest[len] = '\0'; - pstrcat(dest, dest_size, filename); + return g_strdup(filename); } + + if (path_has_protocol(base_path)) { + protocol_stripped = strchr(base_path, ':'); + if (protocol_stripped) { + protocol_stripped++; + } + } + p = protocol_stripped ?: base_path; + + p1 = strrchr(base_path, '/'); +#ifdef _WIN32 + { + const char *p2; + p2 = strrchr(base_path, '\\'); + if (!p1 || p2 > p1) { + p1 = p2; + } + } +#endif + if (p1) { + p1++; + } else { + p1 = base_path; + } + if (p1 > p) { + p = p1; + } + len = p - base_path; + + result = g_malloc(len + strlen(filename) + 1); + memcpy(result, base_path, len); + strcpy(result + len, filename); + + return result; } /* @@ -266,48 +266,98 @@ int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, return 0; } -/* TODO Remove (deprecated since 2.11) - * Block drivers are not supposed to automatically change bs->read_only. - * Instead, they should just check whether they can provide what the user - * explicitly requested and error out if read-write is requested, but they can - * only provide read-only access. */ -int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp) +/* + * Called by a driver that can only provide a read-only image. + * + * Returns 0 if the node is already read-only or it could switch the node to + * read-only because BDRV_O_AUTO_RDONLY is set. + * + * Returns -EACCES if the node is read-write and BDRV_O_AUTO_RDONLY is not set + * or bdrv_can_set_read_only() forbids making the node read-only. If @errmsg + * is not NULL, it is used as the error message for the Error object. + */ +int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, + Error **errp) { int ret = 0; - ret = bdrv_can_set_read_only(bs, read_only, false, errp); - if (ret < 0) { - return ret; + if (!(bs->open_flags & BDRV_O_RDWR)) { + return 0; + } + if (!(bs->open_flags & BDRV_O_AUTO_RDONLY)) { + goto fail; } - bs->read_only = read_only; + ret = bdrv_can_set_read_only(bs, true, false, NULL); + if (ret < 0) { + goto fail; + } + + bs->read_only = true; + bs->open_flags &= ~BDRV_O_RDWR; + return 0; + +fail: + error_setg(errp, "%s", errmsg ?: "Image is read-only"); + return -EACCES; } -void bdrv_get_full_backing_filename_from_filename(const char *backed, - const char *backing, - char *dest, size_t sz, - Error **errp) +/* + * If @backing is empty, this function returns NULL without setting + * @errp. In all other cases, NULL will only be returned with @errp + * set. + * + * Therefore, a return value of NULL without @errp set means that + * there is no backing file; if @errp is set, there is one but its + * absolute filename cannot be generated. + */ +char *bdrv_get_full_backing_filename_from_filename(const char *backed, + const char *backing, + Error **errp) { - if (backing[0] == '\0' || path_has_protocol(backing) || - path_is_absolute(backing)) - { - pstrcpy(dest, sz, backing); + if (backing[0] == '\0') { + return NULL; + } else if (path_has_protocol(backing) || path_is_absolute(backing)) { + return g_strdup(backing); } else if (backed[0] == '\0' || strstart(backed, "json:", NULL)) { error_setg(errp, "Cannot use relative backing file names for '%s'", backed); + return NULL; } else { - path_combine(dest, sz, backed, backing); + return path_combine(backed, backing); } } -void bdrv_get_full_backing_filename(BlockDriverState *bs, char *dest, size_t sz, - Error **errp) +/* + * If @filename is empty or NULL, this function returns NULL without + * setting @errp. In all other cases, NULL will only be returned with + * @errp set. + */ +static char *bdrv_make_absolute_filename(BlockDriverState *relative_to, + const char *filename, Error **errp) { - char *backed = bs->exact_filename[0] ? bs->exact_filename : bs->filename; + char *dir, *full_name; - bdrv_get_full_backing_filename_from_filename(backed, bs->backing_file, - dest, sz, errp); + if (!filename || filename[0] == '\0') { + return NULL; + } else if (path_has_protocol(filename) || path_is_absolute(filename)) { + return g_strdup(filename); + } + + dir = bdrv_dirname(relative_to, errp); + if (!dir) { + return NULL; + } + + full_name = g_strconcat(dir, filename, NULL); + g_free(dir); + return full_name; +} + +char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp) +{ + return bdrv_make_absolute_filename(bs, bs->backing_file, errp); } void bdrv_register(BlockDriver *bdrv) @@ -376,7 +426,7 @@ BlockDriver *bdrv_find_format(const char *format_name) return bdrv_do_find_format(format_name); } -int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) +static int bdrv_format_is_whitelisted(const char *format_name, bool read_only) { static const char *whitelist_rw[] = { CONFIG_BDRV_RW_WHITELIST @@ -391,13 +441,13 @@ int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) } for (p = whitelist_rw; *p; p++) { - if (!strcmp(drv->format_name, *p)) { + if (!strcmp(format_name, *p)) { return 1; } } if (read_only) { for (p = whitelist_ro; *p; p++) { - if (!strcmp(drv->format_name, *p)) { + if (!strcmp(format_name, *p)) { return 1; } } @@ -405,6 +455,11 @@ int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) return 0; } +int bdrv_is_whitelisted(BlockDriver *drv, bool read_only) +{ + return bdrv_format_is_whitelisted(drv->format_name, read_only); +} + bool bdrv_uses_whitelist(void) { return use_bdrv_whitelist; @@ -764,6 +819,31 @@ static void bdrv_join_options(BlockDriverState *bs, QDict *options, } } +static BlockdevDetectZeroesOptions bdrv_parse_detect_zeroes(QemuOpts *opts, + int open_flags, + Error **errp) +{ + Error *local_err = NULL; + char *value = qemu_opt_get_del(opts, "detect-zeroes"); + BlockdevDetectZeroesOptions detect_zeroes = + qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, value, + BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, &local_err); + g_free(value); + if (local_err) { + error_propagate(errp, local_err); + return detect_zeroes; + } + + if (detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + !(open_flags & BDRV_O_UNMAP)) + { + error_setg(errp, "setting detect-zeroes to unmap is not allowed " + "without setting discard operation to unmap"); + } + + return detect_zeroes; +} + /** * Set open flags for a given discard mode * @@ -870,8 +950,9 @@ static void bdrv_temp_snapshot_options(int *child_flags, QDict *child_options, qdict_set_default_str(child_options, BDRV_OPT_CACHE_DIRECT, "off"); qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); - /* Copy the read-only option from the parent */ + /* Copy the read-only and discard options from the parent */ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); + qdict_copy_default(child_options, parent_options, BDRV_OPT_DISCARD); /* aio=native doesn't work for cache.direct=off, so disable it for the * temporary snapshot */ @@ -898,6 +979,7 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, /* Inherit the read-only option from the parent if it's not set */ qdict_copy_default(child_options, parent_options, BDRV_OPT_READ_ONLY); + qdict_copy_default(child_options, parent_options, BDRV_OPT_AUTO_READ_ONLY); /* Our block drivers take care to send flushes and respect unmap policy, * so we can default to enable both on lower layers regardless of the @@ -959,6 +1041,8 @@ static void bdrv_backing_attach(BdrvChild *c) "node is used as backing hd of '%s'", bdrv_get_device_or_node_name(parent)); + bdrv_refresh_filename(backing_hd); + parent->open_flags &= ~BDRV_O_NO_BACKING; pstrcpy(parent->backing_file, sizeof(parent->backing_file), backing_hd->filename); @@ -1021,6 +1105,7 @@ static void bdrv_backing_options(int *child_flags, QDict *child_options, /* backing files always opened read-only */ qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "on"); + qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); flags &= ~BDRV_O_COPY_ON_READ; /* snapshot=on is handled on the top layer */ @@ -1033,11 +1118,11 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, const char *filename, Error **errp) { BlockDriverState *parent = c->opaque; - int orig_flags = bdrv_get_flags(parent); + bool read_only = bdrv_is_read_only(parent); int ret; - if (!(orig_flags & BDRV_O_RDWR)) { - ret = bdrv_reopen(parent, orig_flags | BDRV_O_RDWR, errp); + if (read_only) { + ret = bdrv_reopen_set_read_only(parent, false, errp); if (ret < 0) { return ret; } @@ -1049,8 +1134,8 @@ static int bdrv_backing_update_filename(BdrvChild *c, BlockDriverState *base, error_setg_errno(errp, -ret, "Could not update backing file link"); } - if (!(orig_flags & BDRV_O_RDWR)) { - bdrv_reopen(parent, orig_flags, NULL); + if (read_only) { + bdrv_reopen_set_read_only(parent, true, NULL); } return ret; @@ -1079,37 +1164,28 @@ static int bdrv_open_flags(BlockDriverState *bs, int flags) */ open_flags &= ~(BDRV_O_SNAPSHOT | BDRV_O_NO_BACKING | BDRV_O_PROTOCOL); - /* - * Snapshots should be writable. - */ - if (flags & BDRV_O_TEMPORARY) { - open_flags |= BDRV_O_RDWR; - } - return open_flags; } static void update_flags_from_options(int *flags, QemuOpts *opts) { - *flags &= ~BDRV_O_CACHE_MASK; + *flags &= ~(BDRV_O_CACHE_MASK | BDRV_O_RDWR | BDRV_O_AUTO_RDONLY); - assert(qemu_opt_find(opts, BDRV_OPT_CACHE_NO_FLUSH)); - if (qemu_opt_get_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, false)) { + if (qemu_opt_get_bool_del(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)) { + if (qemu_opt_get_bool_del(opts, BDRV_OPT_CACHE_DIRECT, false)) { *flags |= BDRV_O_NOCACHE; } - *flags &= ~BDRV_O_RDWR; - - assert(qemu_opt_find(opts, BDRV_OPT_READ_ONLY)); - if (!qemu_opt_get_bool(opts, BDRV_OPT_READ_ONLY, false)) { + if (!qemu_opt_get_bool_del(opts, BDRV_OPT_READ_ONLY, false)) { *flags |= BDRV_O_RDWR; } + if (qemu_opt_get_bool_del(opts, BDRV_OPT_AUTO_READ_ONLY, false)) { + *flags |= BDRV_O_AUTO_RDONLY; + } } static void update_options_from_flags(QDict *options, int flags) @@ -1124,6 +1200,10 @@ static void update_options_from_flags(QDict *options, int flags) if (!qdict_haskey(options, BDRV_OPT_READ_ONLY)) { qdict_put_bool(options, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR)); } + if (!qdict_haskey(options, BDRV_OPT_AUTO_READ_ONLY)) { + qdict_put_bool(options, BDRV_OPT_AUTO_READ_ONLY, + flags & BDRV_O_AUTO_RDONLY); + } } static void bdrv_assign_node_name(BlockDriverState *bs, @@ -1296,13 +1376,18 @@ QemuOptsList bdrv_runtime_opts = { .type = QEMU_OPT_BOOL, .help = "Node is opened in read-only mode", }, + { + .name = BDRV_OPT_AUTO_READ_ONLY, + .type = QEMU_OPT_BOOL, + .help = "Node can become read-only if opening read-write fails", + }, { .name = "detect-zeroes", .type = QEMU_OPT_STRING, .help = "try to optimize zero writes (off, on, unmap)", }, { - .name = "discard", + .name = BDRV_OPT_DISCARD, .type = QEMU_OPT_STRING, .help = "discard operation (ignore/off, unmap/on)", }, @@ -1328,7 +1413,6 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, const char *driver_name = NULL; const char *node_name = NULL; const char *discard; - const char *detect_zeroes; QemuOpts *opts; BlockDriver *drv; Error *local_err = NULL; @@ -1361,6 +1445,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, } if (file != NULL) { + bdrv_refresh_filename(blk_bs(file)); filename = blk_bs(file)->filename; } else { /* @@ -1386,13 +1471,19 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, bs->read_only = !(bs->open_flags & BDRV_O_RDWR); if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, bs->read_only)) { - error_setg(errp, - !bs->read_only && bdrv_is_whitelisted(drv, true) - ? "Driver '%s' can only be used for read-only devices" - : "Driver '%s' is not whitelisted", - drv->format_name); - ret = -ENOTSUP; - goto fail_opts; + if (!bs->read_only && bdrv_is_whitelisted(drv, true)) { + ret = bdrv_apply_auto_read_only(bs, NULL, NULL); + } else { + ret = -ENOTSUP; + } + if (ret < 0) { + error_setg(errp, + !bs->read_only && bdrv_is_whitelisted(drv, true) + ? "Driver '%s' can only be used for read-only devices" + : "Driver '%s' is not whitelisted", + drv->format_name); + goto fail_opts; + } } /* bdrv_new() and bdrv_close() make it so */ @@ -1408,7 +1499,7 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, } } - discard = qemu_opt_get(opts, "discard"); + discard = qemu_opt_get(opts, BDRV_OPT_DISCARD); if (discard != NULL) { if (bdrv_parse_discard_flags(discard, &bs->open_flags) != 0) { error_setg(errp, "Invalid discard option"); @@ -1417,29 +1508,12 @@ static int bdrv_open_common(BlockDriverState *bs, BlockBackend *file, } } - detect_zeroes = qemu_opt_get(opts, "detect-zeroes"); - if (detect_zeroes) { - BlockdevDetectZeroesOptions value = - qapi_enum_parse(&BlockdevDetectZeroesOptions_lookup, - detect_zeroes, - BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF, - &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto fail_opts; - } - - if (value == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && - !(bs->open_flags & BDRV_O_UNMAP)) - { - error_setg(errp, "setting detect-zeroes to unmap is not allowed " - "without setting discard operation to unmap"); - ret = -EINVAL; - goto fail_opts; - } - - bs->detect_zeroes = value; + bs->detect_zeroes = + bdrv_parse_detect_zeroes(opts, bs->open_flags, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail_opts; } if (filename != NULL) { @@ -1478,11 +1552,6 @@ static QDict *parse_json_filename(const char *filename, Error **errp) options_obj = qobject_from_json(filename, errp); if (!options_obj) { - /* Work around qobject_from_json() lossage TODO fix that */ - if (errp && !*errp) { - error_setg(errp, "Could not parse the JSON options"); - return NULL; - } error_prepend(errp, "Could not parse the JSON options: "); return NULL; } @@ -1623,6 +1692,7 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared); typedef struct BlockReopenQueueEntry { bool prepared; + bool perms_checked; BDRVReopenState state; QSIMPLEQ_ENTRY(BlockReopenQueueEntry) entry; } BlockReopenQueueEntry; @@ -1918,13 +1988,32 @@ static int bdrv_child_check_perm(BdrvChild *c, BlockReopenQueue *q, ret = bdrv_check_update_perm(c->bs, q, perm, shared, ignore_children, errp); g_slist_free(ignore_children); - return ret; + if (ret < 0) { + return ret; + } + + if (!c->has_backup_perm) { + c->has_backup_perm = true; + c->backup_perm = c->perm; + c->backup_shared_perm = c->shared_perm; + } + /* + * Note: it's OK if c->has_backup_perm was already set, as we can find the + * same child twice during check_perm procedure + */ + + c->perm = perm; + c->shared_perm = shared; + + return 0; } static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared) { uint64_t cumulative_perms, cumulative_shared_perms; + c->has_backup_perm = false; + c->perm = perm; c->shared_perm = shared; @@ -1935,6 +2024,12 @@ static void bdrv_child_set_perm(BdrvChild *c, uint64_t perm, uint64_t shared) static void bdrv_child_abort_perm_update(BdrvChild *c) { + if (c->has_backup_perm) { + c->perm = c->backup_perm; + c->shared_perm = c->backup_shared_perm; + c->has_backup_perm = false; + } + bdrv_abort_perm_update(c->bs); } @@ -2032,6 +2127,8 @@ static void bdrv_replace_child_noperm(BdrvChild *child, BlockDriverState *old_bs = child->bs; int i; + assert(!child->frozen); + if (old_bs && new_bs) { assert(bdrv_get_aio_context(old_bs) == bdrv_get_aio_context(new_bs)); } @@ -2226,6 +2323,18 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) } } +/* Return true if you can reach parent going through child->inherits_from + * recursively. If parent or child are NULL, return false */ +static bool bdrv_inherits_from_recursive(BlockDriverState *child, + BlockDriverState *parent) +{ + while (child && child != parent) { + child = child->inherits_from; + } + + return child != NULL; +} + /* * 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(). @@ -2233,6 +2342,13 @@ static void bdrv_parent_cb_change_media(BlockDriverState *bs, bool load) void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp) { + bool update_inherits_from = bdrv_chain_contains(bs, backing_hd) && + bdrv_inherits_from_recursive(backing_hd, bs); + + if (bdrv_is_backing_chain_frozen(bs, backing_bs(bs), errp)) { + return; + } + if (backing_hd) { bdrv_ref(backing_hd); } @@ -2248,12 +2364,16 @@ void bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, bs->backing = bdrv_attach_child(bs, backing_hd, "backing", &child_backing, errp); + /* If backing_hd was already part of bs's backing chain, and + * inherits_from pointed recursively to bs then let's update it to + * point directly to bs (else it will become NULL). */ + if (update_inherits_from) { + backing_hd->inherits_from = bs; + } if (!bs->backing) { bdrv_unref(backing_hd); } - bdrv_refresh_filename(bs); - out: bdrv_refresh_limits(bs, NULL); } @@ -2271,10 +2391,11 @@ out: int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp) { - char *backing_filename = g_malloc0(PATH_MAX); + char *backing_filename = NULL; char *bdref_key_dot; const char *reference = NULL; int ret = 0; + bool implicit_backing = false; BlockDriverState *backing_hd; QDict *options; QDict *tmp_parent_options = NULL; @@ -2305,13 +2426,22 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, */ reference = qdict_get_try_str(parent_options, bdref_key); if (reference || qdict_haskey(options, "file.filename")) { - backing_filename[0] = '\0'; + /* keep backing_filename NULL */ } else if (bs->backing_file[0] == '\0' && qdict_size(options) == 0) { qobject_unref(options); goto free_exit; } else { - bdrv_get_full_backing_filename(bs, backing_filename, PATH_MAX, - &local_err); + if (qdict_size(options) == 0) { + /* If the user specifies options that do not modify the + * backing file's behavior, we might still consider it the + * implicit backing file. But it's easier this way, and + * just specifying some of the backing BDS's options is + * only possible with -drive anyway (otherwise the QAPI + * schema forces the user to specify everything). */ + implicit_backing = !strcmp(bs->auto_backing_file, bs->backing_file); + } + + backing_filename = bdrv_get_full_backing_filename(bs, &local_err); if (local_err) { ret = -EINVAL; error_propagate(errp, local_err); @@ -2332,9 +2462,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, qdict_put_str(options, "driver", bs->backing_format); } - backing_hd = bdrv_open_inherit(*backing_filename ? backing_filename : NULL, - reference, options, 0, bs, &child_backing, - errp); + backing_hd = bdrv_open_inherit(backing_filename, reference, options, 0, bs, + &child_backing, errp); if (!backing_hd) { bs->open_flags |= BDRV_O_NO_BACKING; error_prepend(errp, "Could not open backing file: "); @@ -2343,6 +2472,12 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, } bdrv_set_aio_context(backing_hd, bdrv_get_aio_context(bs)); + if (implicit_backing) { + bdrv_refresh_filename(backing_hd); + pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), + backing_hd->filename); + } + /* Hook up the backing file link; drop our reference, bs owns the * backing_hd reference now */ bdrv_set_backing_hd(bs, backing_hd, &local_err); @@ -2477,6 +2612,8 @@ BlockDriverState *bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp) qdict_set_default_str(qdict, BDRV_OPT_CACHE_DIRECT, "off"); qdict_set_default_str(qdict, BDRV_OPT_CACHE_NO_FLUSH, "off"); qdict_set_default_str(qdict, BDRV_OPT_READ_ONLY, "off"); + qdict_set_default_str(qdict, BDRV_OPT_AUTO_READ_ONLY, "off"); + } bs = bdrv_open_inherit(NULL, reference, qdict, 0, NULL, NULL, errp); @@ -2584,6 +2721,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, BlockBackend *file = NULL; BlockDriverState *bs; BlockDriver *drv = NULL; + BdrvChild *child; const char *drvname; const char *backing; Error *local_err = NULL; @@ -2767,7 +2905,17 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, } } - bdrv_refresh_filename(bs); + /* Remove all children options and references + * from bs->options and bs->explicit_options */ + QLIST_FOREACH(child, &bs->children, next) { + char *child_key_dot; + child_key_dot = g_strdup_printf("%s.", child->name); + qdict_extract_subqdict(bs->explicit_options, NULL, child_key_dot); + qdict_extract_subqdict(bs->options, NULL, child_key_dot); + qdict_del(bs->explicit_options, child->name); + qdict_del(bs->options, child->name); + g_free(child_key_dot); + } /* Check if any unknown options were used */ if (qdict_size(options) != 0) { @@ -2787,6 +2935,7 @@ static BlockDriverState *bdrv_open_inherit(const char *filename, bdrv_parent_cb_change_media(bs, true); qobject_unref(options); + options = NULL; /* For snapshot=on, create a temporary qcow2 overlay. bs points to the * temporary snapshot afterwards. */ @@ -2835,6 +2984,74 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, NULL, errp); } +/* Return true if the NULL-terminated @list contains @str */ +static bool is_str_in_list(const char *str, const char *const *list) +{ + if (str && list) { + int i; + for (i = 0; list[i] != NULL; i++) { + if (!strcmp(str, list[i])) { + return true; + } + } + } + return false; +} + +/* + * Check that every option set in @bs->options is also set in + * @new_opts. + * + * Options listed in the common_options list and in + * @bs->drv->mutable_opts are skipped. + * + * Return 0 on success, otherwise return -EINVAL and set @errp. + */ +static int bdrv_reset_options_allowed(BlockDriverState *bs, + const QDict *new_opts, Error **errp) +{ + const QDictEntry *e; + /* These options are common to all block drivers and are handled + * in bdrv_reopen_prepare() so they can be left out of @new_opts */ + const char *const common_options[] = { + "node-name", "discard", "cache.direct", "cache.no-flush", + "read-only", "auto-read-only", "detect-zeroes", NULL + }; + + for (e = qdict_first(bs->options); e; e = qdict_next(bs->options, e)) { + if (!qdict_haskey(new_opts, e->key) && + !is_str_in_list(e->key, common_options) && + !is_str_in_list(e->key, bs->drv->mutable_opts)) { + error_setg(errp, "Option '%s' cannot be reset " + "to its default value", e->key); + return -EINVAL; + } + } + + return 0; +} + +/* + * Returns true if @child can be reached recursively from @bs + */ +static bool bdrv_recurse_has_child(BlockDriverState *bs, + BlockDriverState *child) +{ + BdrvChild *c; + + if (bs == child) { + return true; + } + + QLIST_FOREACH(c, &bs->children, next) { + if (bdrv_recurse_has_child(c->bs, child)) { + return true; + } + } + + return false; +} + /* * Adds a BlockDriverState to a simple queue for an atomic, transactional * reopen of multiple devices. @@ -2860,16 +3077,18 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, - int flags, const BdrvChildRole *role, QDict *parent_options, - int parent_flags) + int parent_flags, + bool keep_old_opts) { assert(bs != NULL); BlockReopenQueueEntry *bs_entry; BdrvChild *child; - QDict *old_options, *explicit_options; + QDict *old_options, *explicit_options, *options_copy; + int flags; + QemuOpts *opts; /* Make sure that the caller remembered to use a drained section. This is * important to avoid graph changes between the recursive queuing here and @@ -2895,51 +3114,44 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, /* * 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 + * 2. Retained from explicitly set options of bs + * 3. Inherited from parent node + * 4. 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) */ - if (bs_entry) { - old_options = qdict_clone_shallow(bs_entry->state.explicit_options); - } else { - old_options = qdict_clone_shallow(bs->explicit_options); + if (bs_entry || keep_old_opts) { + old_options = qdict_clone_shallow(bs_entry ? + bs_entry->state.explicit_options : + bs->explicit_options); + bdrv_join_options(bs, options, old_options); + qobject_unref(old_options); } - bdrv_join_options(bs, options, old_options); - qobject_unref(old_options); explicit_options = qdict_clone_shallow(options); /* Inherit from parent node */ if (parent_options) { - QemuOpts *opts; - QDict *options_copy; - assert(!flags); + flags = 0; role->inherit_options(&flags, options, parent_flags, parent_options); - options_copy = qdict_clone_shallow(options); - opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, options_copy, NULL); - update_flags_from_options(&flags, opts); - qemu_opts_del(opts); - qobject_unref(options_copy); + } else { + flags = bdrv_get_flags(bs); } - /* 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); - qobject_unref(old_options); + if (keep_old_opts) { + /* 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); + qobject_unref(old_options); + } + + /* We have the final set of options so let's update the flags */ + options_copy = qdict_clone_shallow(options); + opts = qemu_opts_create(&bdrv_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, options_copy, NULL); + update_flags_from_options(&flags, opts); + qemu_opts_del(opts); + qobject_unref(options_copy); /* bdrv_open_inherit() sets and clears some additional flags internally */ flags &= ~BDRV_O_PROTOCOL; @@ -2964,9 +3176,21 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, bs_entry->state.perm = UINT64_MAX; bs_entry->state.shared_perm = 0; + /* + * If keep_old_opts is false then it means that unspecified + * options must be reset to their original value. We don't allow + * resetting 'backing' but we need to know if the option is + * missing in order to decide if we have to return an error. + */ + if (!keep_old_opts) { + bs_entry->state.backing_missing = + !qdict_haskey(options, "backing") && + !qdict_haskey(options, "backing.driver"); + } + QLIST_FOREACH(child, &bs->children, next) { - QDict *new_child_options; - char *child_key_dot; + QDict *new_child_options = NULL; + bool child_keep_old = keep_old_opts; /* reopen can only change the options of block devices that were * implicitly created and inherited options. For other (referenced) @@ -2975,12 +3199,32 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, continue; } - child_key_dot = g_strdup_printf("%s.", child->name); - qdict_extract_subqdict(options, &new_child_options, child_key_dot); - g_free(child_key_dot); + /* Check if the options contain a child reference */ + if (qdict_haskey(options, child->name)) { + const char *childref = qdict_get_try_str(options, child->name); + /* + * The current child must not be reopened if the child + * reference is null or points to a different node. + */ + if (g_strcmp0(childref, child->bs->node_name)) { + continue; + } + /* + * If the child reference points to the current child then + * reopen it with its existing set of options (note that + * it can still inherit new options from the parent). + */ + child_keep_old = true; + } else { + /* Extract child options ("child-name.*") */ + char *child_key_dot = g_strdup_printf("%s.", child->name); + qdict_extract_subqdict(explicit_options, NULL, child_key_dot); + 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); + bdrv_reopen_queue_child(bs_queue, child->bs, new_child_options, + child->role, options, flags, child_keep_old); } return bs_queue; @@ -2988,10 +3232,10 @@ static BlockReopenQueue *bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, - QDict *options, int flags) + QDict *options, bool keep_old_opts) { - return bdrv_reopen_queue_child(bs_queue, bs, options, flags, - NULL, NULL, 0); + return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, NULL, 0, + keep_old_opts); } /* @@ -3011,23 +3255,44 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, * All affected nodes must be drained between bdrv_reopen_queue() and * bdrv_reopen_multiple(). */ -int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp) +int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp) { int ret = -1; BlockReopenQueueEntry *bs_entry, *next; - Error *local_err = NULL; assert(bs_queue != NULL); QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { assert(bs_entry->state.bs->quiesce_counter > 0); - if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, &local_err)) { - error_propagate(errp, local_err); + if (bdrv_reopen_prepare(&bs_entry->state, bs_queue, errp)) { goto cleanup; } bs_entry->prepared = true; } + QSIMPLEQ_FOREACH(bs_entry, bs_queue, entry) { + BDRVReopenState *state = &bs_entry->state; + ret = bdrv_check_perm(state->bs, bs_queue, state->perm, + state->shared_perm, NULL, errp); + if (ret < 0) { + goto cleanup_perm; + } + /* Check if new_backing_bs would accept the new permissions */ + if (state->replace_backing_bs && state->new_backing_bs) { + uint64_t nperm, nshared; + bdrv_child_perm(state->bs, state->new_backing_bs, + NULL, &child_backing, bs_queue, + state->perm, state->shared_perm, + &nperm, &nshared); + ret = bdrv_check_update_perm(state->new_backing_bs, NULL, + nperm, nshared, NULL, errp); + if (ret < 0) { + goto cleanup_perm; + } + } + bs_entry->perms_checked = true; + } + /* If we reach this point, we have success and just need to apply the * changes */ @@ -3036,15 +3301,35 @@ int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **er } ret = 0; +cleanup_perm: + QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { + BDRVReopenState *state = &bs_entry->state; + if (!bs_entry->perms_checked) { + continue; + } + + if (ret == 0) { + bdrv_set_perm(state->bs, state->perm, state->shared_perm); + } else { + bdrv_abort_perm_update(state->bs); + if (state->replace_backing_bs && state->new_backing_bs) { + bdrv_abort_perm_update(state->new_backing_bs); + } + } + } cleanup: QSIMPLEQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { - if (ret && bs_entry->prepared) { - bdrv_reopen_abort(&bs_entry->state); - } else if (ret) { + if (ret) { + if (bs_entry->prepared) { + bdrv_reopen_abort(&bs_entry->state); + } qobject_unref(bs_entry->state.explicit_options); + qobject_unref(bs_entry->state.options); + } + if (bs_entry->state.new_backing_bs) { + bdrv_unref(bs_entry->state.new_backing_bs); } - qobject_unref(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); @@ -3052,22 +3337,18 @@ cleanup: return ret; } - -/* Reopen a single BlockDriverState with the specified flags. */ -int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp) +int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, + Error **errp) { - int ret = -1; - Error *local_err = NULL; + int ret; BlockReopenQueue *queue; + QDict *opts = qdict_new(); + + qdict_put_bool(opts, BDRV_OPT_READ_ONLY, read_only); bdrv_subtree_drained_begin(bs); - - queue = bdrv_reopen_queue(NULL, bs, NULL, bdrv_flags); - ret = bdrv_reopen_multiple(bdrv_get_aio_context(bs), queue, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); - } - + queue = bdrv_reopen_queue(NULL, bs, opts, true); + ret = bdrv_reopen_multiple(queue, errp); bdrv_subtree_drained_end(bs); return ret; @@ -3120,6 +3401,101 @@ static void bdrv_reopen_perm(BlockReopenQueue *q, BlockDriverState *bs, *shared = cumulative_shared_perms; } +/* + * Take a BDRVReopenState and check if the value of 'backing' in the + * reopen_state->options QDict is valid or not. + * + * If 'backing' is missing from the QDict then return 0. + * + * If 'backing' contains the node name of the backing file of + * reopen_state->bs then return 0. + * + * If 'backing' contains a different node name (or is null) then check + * whether the current backing file can be replaced with the new one. + * If that's the case then reopen_state->replace_backing_bs is set to + * true and reopen_state->new_backing_bs contains a pointer to the new + * backing BlockDriverState (or NULL). + * + * Return 0 on success, otherwise return < 0 and set @errp. + */ +static int bdrv_reopen_parse_backing(BDRVReopenState *reopen_state, + Error **errp) +{ + BlockDriverState *bs = reopen_state->bs; + BlockDriverState *overlay_bs, *new_backing_bs; + QObject *value; + const char *str; + + value = qdict_get(reopen_state->options, "backing"); + if (value == NULL) { + return 0; + } + + switch (qobject_type(value)) { + case QTYPE_QNULL: + new_backing_bs = NULL; + break; + case QTYPE_QSTRING: + str = qobject_get_try_str(value); + new_backing_bs = bdrv_lookup_bs(NULL, str, errp); + if (new_backing_bs == NULL) { + return -EINVAL; + } else if (bdrv_recurse_has_child(new_backing_bs, bs)) { + error_setg(errp, "Making '%s' a backing file of '%s' " + "would create a cycle", str, bs->node_name); + return -EINVAL; + } + break; + default: + /* 'backing' does not allow any other data type */ + g_assert_not_reached(); + } + + /* + * TODO: before removing the x- prefix from x-blockdev-reopen we + * should move the new backing file into the right AioContext + * instead of returning an error. + */ + if (new_backing_bs) { + if (bdrv_get_aio_context(new_backing_bs) != bdrv_get_aio_context(bs)) { + error_setg(errp, "Cannot use a new backing file " + "with a different AioContext"); + return -EINVAL; + } + } + + /* + * Find the "actual" backing file by skipping all links that point + * to an implicit node, if any (e.g. a commit filter node). + */ + overlay_bs = bs; + while (backing_bs(overlay_bs) && backing_bs(overlay_bs)->implicit) { + overlay_bs = backing_bs(overlay_bs); + } + + /* If we want to replace the backing file we need some extra checks */ + if (new_backing_bs != backing_bs(overlay_bs)) { + /* Check for implicit nodes between bs and its backing file */ + if (bs != overlay_bs) { + error_setg(errp, "Cannot change backing link if '%s' has " + "an implicit backing file", bs->node_name); + return -EPERM; + } + /* Check if the backing link that we want to replace is frozen */ + if (bdrv_is_backing_chain_frozen(overlay_bs, backing_bs(overlay_bs), + errp)) { + return -EPERM; + } + reopen_state->replace_backing_bs = true; + if (new_backing_bs) { + bdrv_ref(new_backing_bs); + reopen_state->new_backing_bs = new_backing_bs; + } + } + + return 0; +} + /* * Prepares a BlockDriverState for reopen. All changes are staged in the * 'opaque' field of the BDRVReopenState, which is used and allocated by @@ -3141,16 +3517,24 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp) { int ret = -1; + int old_flags; Error *local_err = NULL; BlockDriver *drv; QemuOpts *opts; - const char *value; + QDict *orig_reopen_opts; + char *discard = NULL; bool read_only; + bool drv_prepared = false; assert(reopen_state != NULL); assert(reopen_state->bs->drv != NULL); drv = reopen_state->bs->drv; + /* This function and each driver's bdrv_reopen_prepare() remove + * entries from reopen_state->options as they are processed, so + * we need to make a copy of the original QDict. */ + orig_reopen_opts = qdict_clone_shallow(reopen_state->options); + /* 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); @@ -3160,20 +3544,35 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, goto error; } + /* This was already called in bdrv_reopen_queue_child() so the flags + * are up-to-date. This time we simply want to remove the options from + * QemuOpts in order to indicate that they have been processed. */ + old_flags = reopen_state->flags; update_flags_from_options(&reopen_state->flags, opts); + assert(old_flags == reopen_state->flags); - /* 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_str(reopen_state->options, "node-name", value); + discard = qemu_opt_get_del(opts, BDRV_OPT_DISCARD); + if (discard != NULL) { + if (bdrv_parse_discard_flags(discard, &reopen_state->flags) != 0) { + error_setg(errp, "Invalid discard option"); + ret = -EINVAL; + goto error; + } } - value = qemu_opt_get(opts, "driver"); - if (value) { - qdict_put_str(reopen_state->options, "driver", value); + reopen_state->detect_zeroes = + bdrv_parse_detect_zeroes(opts, reopen_state->flags, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto error; } + /* All other options (including node-name and driver) must be unchanged. + * Put them back into the QDict, so that they are checked at the end + * of this function. */ + qemu_opts_to_qdict(opts, reopen_state->options); + /* If we are to stay read-only, do not allow permission change * to r/w. Attempting to set to r/w may fail if either BDRV_O_ALLOW_RDWR is * not set, or if the BDS still has copy_on_read enabled */ @@ -3195,11 +3594,23 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, } if (drv->bdrv_reopen_prepare) { + /* + * If a driver-specific option is missing, it means that we + * should reset it to its default value. + * But not all options allow that, so we need to check it first. + */ + ret = bdrv_reset_options_allowed(reopen_state->bs, + reopen_state->options, errp); + if (ret) { + goto error; + } + ret = drv->bdrv_reopen_prepare(reopen_state, queue, &local_err); if (ret) { if (local_err != NULL) { error_propagate(errp, local_err); } else { + bdrv_refresh_filename(reopen_state->bs); error_setg(errp, "failed while preparing to reopen image '%s'", reopen_state->bs->filename); } @@ -3215,6 +3626,32 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, goto error; } + drv_prepared = true; + + /* + * We must provide the 'backing' option if the BDS has a backing + * file or if the image file has a backing file name as part of + * its metadata. Otherwise the 'backing' option can be omitted. + */ + if (drv->supports_backing && reopen_state->backing_missing && + (backing_bs(reopen_state->bs) || reopen_state->bs->backing_file[0])) { + error_setg(errp, "backing is missing for '%s'", + reopen_state->bs->node_name); + ret = -EINVAL; + goto error; + } + + /* + * Allow changing the 'backing' option. The new value can be + * either a reference to an existing node (using its node name) + * or NULL to simply detach the current backing file. + */ + ret = bdrv_reopen_parse_backing(reopen_state, errp); + if (ret < 0) { + goto error; + } + qdict_del(reopen_state->options, "backing"); + /* 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) */ @@ -3225,6 +3662,24 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, QObject *new = entry->value; QObject *old = qdict_get(reopen_state->bs->options, entry->key); + /* Allow child references (child_name=node_name) as long as they + * point to the current child (i.e. everything stays the same). */ + if (qobject_type(new) == QTYPE_QSTRING) { + BdrvChild *child; + QLIST_FOREACH(child, &reopen_state->bs->children, next) { + if (!strcmp(child->name, entry->key)) { + break; + } + } + + if (child) { + const char *str = qobject_get_try_str(new); + if (!strcmp(child->bs->node_name, str)) { + continue; /* Found child with this name, skip option */ + } + } + } + /* * TODO: When using -drive to specify blockdev options, all values * will be strings; however, when using -blockdev, blockdev-add or @@ -3249,16 +3704,25 @@ int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, } while ((entry = qdict_next(reopen_state->options, entry))); } - ret = bdrv_check_perm(reopen_state->bs, queue, reopen_state->perm, - reopen_state->shared_perm, NULL, errp); - if (ret < 0) { - goto error; - } - ret = 0; + /* Restore the original reopen_state->options QDict */ + qobject_unref(reopen_state->options); + reopen_state->options = qobject_ref(orig_reopen_opts); + error: + if (ret < 0 && drv_prepared) { + /* drv->bdrv_reopen_prepare() has succeeded, so we need to + * call drv->bdrv_reopen_abort() before signaling an error + * (bdrv_reopen_multiple() will not call bdrv_reopen_abort() + * when the respective bdrv_reopen_prepare() has failed) */ + if (drv->bdrv_reopen_abort) { + drv->bdrv_reopen_abort(reopen_state); + } + } qemu_opts_del(opts); + qobject_unref(orig_reopen_opts); + g_free(discard); return ret; } @@ -3271,6 +3735,7 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) { BlockDriver *drv; BlockDriverState *bs; + BdrvChild *child; bool old_can_write, new_can_write; assert(reopen_state != NULL); @@ -3288,16 +3753,43 @@ void bdrv_reopen_commit(BDRVReopenState *reopen_state) /* set BDS specific flags now */ qobject_unref(bs->explicit_options); + qobject_unref(bs->options); bs->explicit_options = reopen_state->explicit_options; + bs->options = reopen_state->options; bs->open_flags = reopen_state->flags; bs->read_only = !(reopen_state->flags & BDRV_O_RDWR); + bs->detect_zeroes = reopen_state->detect_zeroes; + + if (reopen_state->replace_backing_bs) { + qdict_del(bs->explicit_options, "backing"); + qdict_del(bs->options, "backing"); + } + + /* Remove child references from bs->options and bs->explicit_options. + * Child options were already removed in bdrv_reopen_queue_child() */ + QLIST_FOREACH(child, &bs->children, next) { + qdict_del(bs->explicit_options, child->name); + qdict_del(bs->options, child->name); + } + + /* + * Change the backing file if a new one was specified. We do this + * after updating bs->options, so bdrv_refresh_filename() (called + * from bdrv_set_backing_hd()) has the new values. + */ + if (reopen_state->replace_backing_bs) { + BlockDriverState *old_backing_bs = backing_bs(bs); + assert(!old_backing_bs || !old_backing_bs->implicit); + /* Abort the permission update on the backing bs we're detaching */ + if (old_backing_bs) { + bdrv_abort_perm_update(old_backing_bs); + } + bdrv_set_backing_hd(bs, reopen_state->new_backing_bs, &error_abort); + } bdrv_refresh_limits(bs, NULL); - bdrv_set_perm(reopen_state->bs, reopen_state->perm, - reopen_state->shared_perm); - new_can_write = !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE); if (!old_can_write && new_can_write && drv->bdrv_reopen_bitmaps_rw) { @@ -3329,10 +3821,6 @@ void bdrv_reopen_abort(BDRVReopenState *reopen_state) if (drv->bdrv_reopen_abort) { drv->bdrv_reopen_abort(reopen_state); } - - qobject_unref(reopen_state->explicit_options); - - bdrv_abort_perm_update(reopen_state->bs); } @@ -3349,7 +3837,9 @@ static void bdrv_close(BlockDriverState *bs) bdrv_drain(bs); /* in case flush left pending I/O */ if (bs->drv) { - bs->drv->bdrv_close(bs); + if (bs->drv->bdrv_close) { + bs->drv->bdrv_close(bs); + } bs->drv = NULL; } @@ -3411,7 +3901,9 @@ void bdrv_close_all(void) static bool should_update_child(BdrvChild *c, BlockDriverState *to) { - BdrvChild *to_c; + GQueue *queue; + GHashTable *found; + bool ret; if (c->role->stay_at_node) { return false; @@ -3447,14 +3939,43 @@ static bool should_update_child(BdrvChild *c, BlockDriverState *to) * if A is a child of B, that means we cannot replace A by B there * because that would create a loop. Silently detaching A from B * is also not really an option. So overall just leaving A in - * place there is the most sensible choice. */ - QLIST_FOREACH(to_c, &to->children, next) { - if (to_c == c) { - return false; + * place there is the most sensible choice. + * + * We would also create a loop in any cases where @c is only + * indirectly referenced by @to. Prevent this by returning false + * if @c is found (by breadth-first search) anywhere in the whole + * subtree of @to. + */ + + ret = true; + found = g_hash_table_new(NULL, NULL); + g_hash_table_add(found, to); + queue = g_queue_new(); + g_queue_push_tail(queue, to); + + while (!g_queue_is_empty(queue)) { + BlockDriverState *v = g_queue_pop_head(queue); + BdrvChild *c2; + + QLIST_FOREACH(c2, &v->children, next) { + if (c2 == c) { + ret = false; + break; + } + + if (g_hash_table_contains(found, c2->bs)) { + continue; + } + + g_queue_push_tail(queue, c2->bs); + g_hash_table_add(found, c2->bs); } } - return true; + g_queue_free(queue); + g_hash_table_destroy(found); + + return ret; } void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, @@ -3479,6 +4000,11 @@ void bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, if (!should_update_child(c, to)) { continue; } + if (c->frozen) { + error_setg(errp, "Cannot change '%s' link to '%s'", + c->name, from->node_name); + goto out; + } list = g_slist_prepend(list, c); perm |= c->perm; shared &= c->shared_perm; @@ -3600,6 +4126,7 @@ static void bdrv_check_co_entry(void *opaque) { CheckCo *cco = opaque; cco->ret = bdrv_co_check(cco->bs, cco->res, cco->fix); + aio_wait_kick(); } int bdrv_check(BlockDriverState *bs, @@ -3618,7 +4145,7 @@ int bdrv_check(BlockDriverState *bs, bdrv_check_co_entry(&cco); } else { co = qemu_coroutine_create(bdrv_check_co_entry, &cco); - qemu_coroutine_enter(co); + bdrv_coroutine_enter(bs, co); BDRV_POLL_WHILE(bs, cco.ret == -EINPROGRESS); } @@ -3657,6 +4184,8 @@ int bdrv_change_backing_file(BlockDriverState *bs, if (ret == 0) { pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); + pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), + backing_file ?: ""); } return ret; } @@ -3687,6 +4216,70 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) return bdrv_find_overlay(bs, NULL); } +/* + * Return true if at least one of the backing links between @bs and + * @base is frozen. @errp is set if that's the case. + * @base must be reachable from @bs, or NULL. + */ +bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, + Error **errp) +{ + BlockDriverState *i; + + for (i = bs; i != base; i = backing_bs(i)) { + if (i->backing && i->backing->frozen) { + error_setg(errp, "Cannot change '%s' link from '%s' to '%s'", + i->backing->name, i->node_name, + backing_bs(i)->node_name); + return true; + } + } + + return false; +} + +/* + * Freeze all backing links between @bs and @base. + * If any of the links is already frozen the operation is aborted and + * none of the links are modified. + * @base must be reachable from @bs, or NULL. + * Returns 0 on success. On failure returns < 0 and sets @errp. + */ +int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, + Error **errp) +{ + BlockDriverState *i; + + if (bdrv_is_backing_chain_frozen(bs, base, errp)) { + return -EPERM; + } + + for (i = bs; i != base; i = backing_bs(i)) { + if (i->backing) { + i->backing->frozen = true; + } + } + + return 0; +} + +/* + * Unfreeze all backing links between @bs and @base. The caller must + * ensure that all links are frozen before using this function. + * @base must be reachable from @bs, or NULL. + */ +void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base) +{ + BlockDriverState *i; + + for (i = bs; i != base; i = backing_bs(i)) { + if (i->backing) { + assert(i->backing->frozen); + i->backing->frozen = false; + } + } +} + /* * Drops images above 'base' up to and including 'top', and sets the image * above 'top' to have base as its backing file. @@ -3719,6 +4312,8 @@ BlockDriverState *bdrv_find_base(BlockDriverState *bs) int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, const char *backing_file_str) { + BlockDriverState *explicit_top = top; + bool update_inherits_from; BdrvChild *c, *next; Error *local_err = NULL; int ret = -EIO; @@ -3734,19 +4329,39 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, goto exit; } + /* This function changes all links that point to top and makes + * them point to base. Check that none of them is frozen. */ + QLIST_FOREACH(c, &top->parents, next_parent) { + if (c->frozen) { + goto exit; + } + } + + /* If 'base' recursively inherits from 'top' then we should set + * base->inherits_from to top->inherits_from after 'top' and all + * other intermediate nodes have been dropped. + * If 'top' is an implicit node (e.g. "commit_top") we should skip + * it because no one inherits from it. We use explicit_top for that. */ + while (explicit_top && explicit_top->implicit) { + explicit_top = backing_bs(explicit_top); + } + update_inherits_from = bdrv_inherits_from_recursive(base, explicit_top); + /* success - we can delete the intermediate states, and link top->base */ /* TODO Check graph modification op blockers (BLK_PERM_GRAPH_MOD) once * we've figured out how they should work. */ - backing_file_str = backing_file_str ? backing_file_str : base->filename; + if (!backing_file_str) { + bdrv_refresh_filename(base); + backing_file_str = base->filename; + } QLIST_FOREACH_SAFE(c, &top->parents, next_parent, next) { /* Check whether we are allowed to switch c from top to base */ GSList *ignore_children = g_slist_prepend(NULL, c); - bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, - ignore_children, &local_err); + ret = bdrv_check_update_perm(base, NULL, c->perm, c->shared_perm, + ignore_children, &local_err); g_slist_free(ignore_children); - if (local_err) { - ret = -EPERM; + if (ret < 0) { error_report_err(local_err); goto exit; } @@ -3769,6 +4384,10 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, bdrv_unref(top); } + if (update_inherits_from) { + base->inherits_from = explicit_top->inherits_from; + } + ret = 0; exit: bdrv_unref(top); @@ -3892,7 +4511,7 @@ static int qsort_strcmp(const void *a, const void *b) } void bdrv_iterate_format(void (*it)(void *opaque, const char *name), - void *opaque) + void *opaque, bool read_only) { BlockDriver *drv; int count = 0; @@ -3903,6 +4522,11 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), if (drv->format_name) { bool found = false; int i = count; + + if (use_bdrv_whitelist && !bdrv_is_whitelisted(drv, read_only)) { + continue; + } + while (formats && i && !found) { found = !strcmp(formats[--i], drv->format_name); } @@ -3921,6 +4545,11 @@ void bdrv_iterate_format(void (*it)(void *opaque, const char *name), bool found = false; int j = count; + if (use_bdrv_whitelist && + !bdrv_format_is_whitelisted(format_name, read_only)) { + continue; + } + while (formats && j && !found) { found = !strcmp(formats[--j], format_name); } @@ -3978,6 +4607,154 @@ BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp) return list; } +#define QAPI_LIST_ADD(list, element) do { \ + typeof(list) _tmp = g_new(typeof(*(list)), 1); \ + _tmp->value = (element); \ + _tmp->next = (list); \ + (list) = _tmp; \ +} while (0) + +typedef struct XDbgBlockGraphConstructor { + XDbgBlockGraph *graph; + GHashTable *graph_nodes; +} XDbgBlockGraphConstructor; + +static XDbgBlockGraphConstructor *xdbg_graph_new(void) +{ + XDbgBlockGraphConstructor *gr = g_new(XDbgBlockGraphConstructor, 1); + + gr->graph = g_new0(XDbgBlockGraph, 1); + gr->graph_nodes = g_hash_table_new(NULL, NULL); + + return gr; +} + +static XDbgBlockGraph *xdbg_graph_finalize(XDbgBlockGraphConstructor *gr) +{ + XDbgBlockGraph *graph = gr->graph; + + g_hash_table_destroy(gr->graph_nodes); + g_free(gr); + + return graph; +} + +static uintptr_t xdbg_graph_node_num(XDbgBlockGraphConstructor *gr, void *node) +{ + uintptr_t ret = (uintptr_t)g_hash_table_lookup(gr->graph_nodes, node); + + if (ret != 0) { + return ret; + } + + /* + * Start counting from 1, not 0, because 0 interferes with not-found (NULL) + * answer of g_hash_table_lookup. + */ + ret = g_hash_table_size(gr->graph_nodes) + 1; + g_hash_table_insert(gr->graph_nodes, node, (void *)ret); + + return ret; +} + +static void xdbg_graph_add_node(XDbgBlockGraphConstructor *gr, void *node, + XDbgBlockGraphNodeType type, const char *name) +{ + XDbgBlockGraphNode *n; + + n = g_new0(XDbgBlockGraphNode, 1); + + n->id = xdbg_graph_node_num(gr, node); + n->type = type; + n->name = g_strdup(name); + + QAPI_LIST_ADD(gr->graph->nodes, n); +} + +static void xdbg_graph_add_edge(XDbgBlockGraphConstructor *gr, void *parent, + const BdrvChild *child) +{ + typedef struct { + unsigned int flag; + BlockPermission num; + } PermissionMap; + + static const PermissionMap permissions[] = { + { BLK_PERM_CONSISTENT_READ, BLOCK_PERMISSION_CONSISTENT_READ }, + { BLK_PERM_WRITE, BLOCK_PERMISSION_WRITE }, + { BLK_PERM_WRITE_UNCHANGED, BLOCK_PERMISSION_WRITE_UNCHANGED }, + { BLK_PERM_RESIZE, BLOCK_PERMISSION_RESIZE }, + { BLK_PERM_GRAPH_MOD, BLOCK_PERMISSION_GRAPH_MOD }, + { 0, 0 } + }; + const PermissionMap *p; + XDbgBlockGraphEdge *edge; + + QEMU_BUILD_BUG_ON(1UL << (ARRAY_SIZE(permissions) - 1) != BLK_PERM_ALL + 1); + + edge = g_new0(XDbgBlockGraphEdge, 1); + + edge->parent = xdbg_graph_node_num(gr, parent); + edge->child = xdbg_graph_node_num(gr, child->bs); + edge->name = g_strdup(child->name); + + for (p = permissions; p->flag; p++) { + if (p->flag & child->perm) { + QAPI_LIST_ADD(edge->perm, p->num); + } + if (p->flag & child->shared_perm) { + QAPI_LIST_ADD(edge->shared_perm, p->num); + } + } + + QAPI_LIST_ADD(gr->graph->edges, edge); +} + + +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp) +{ + BlockBackend *blk; + BlockJob *job; + BlockDriverState *bs; + BdrvChild *child; + XDbgBlockGraphConstructor *gr = xdbg_graph_new(); + + for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { + char *allocated_name = NULL; + const char *name = blk_name(blk); + + if (!*name) { + name = allocated_name = blk_get_attached_dev_id(blk); + } + xdbg_graph_add_node(gr, blk, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_BACKEND, + name); + g_free(allocated_name); + if (blk_root(blk)) { + xdbg_graph_add_edge(gr, blk, blk_root(blk)); + } + } + + for (job = block_job_next(NULL); job; job = block_job_next(job)) { + GSList *el; + + xdbg_graph_add_node(gr, job, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_JOB, + job->job.id); + for (el = job->nodes; el; el = el->next) { + xdbg_graph_add_edge(gr, job, (BdrvChild *)el->data); + } + } + + QTAILQ_FOREACH(bs, &graph_bdrv_states, node_list) { + xdbg_graph_add_node(gr, bs, X_DBG_BLOCK_GRAPH_NODE_TYPE_BLOCK_DRIVER, + bs->node_name); + QLIST_FOREACH(child, &bs->children, next) { + xdbg_graph_add_edge(gr, bs, child); + } + } + + return xdbg_graph_finalize(gr); +} + BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp) @@ -4133,16 +4910,6 @@ bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs) return bs->supported_zero_flags & BDRV_REQ_MAY_UNMAP; } -const char *bdrv_get_encrypted_filename(BlockDriverState *bs) -{ - if (bs->backing && bs->backing->bs->encrypted) - return bs->backing_file; - else if (bs->encrypted) - return bs->filename; - else - return NULL; -} - void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size) { @@ -4166,11 +4933,12 @@ int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return drv->bdrv_get_info(bs, bdi); } -ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs) +ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, + Error **errp) { BlockDriver *drv = bs->drv; if (drv && drv->bdrv_get_specific_info) { - return drv->bdrv_get_specific_info(bs); + return drv->bdrv_get_specific_info(bs, errp); } return NULL; } @@ -4250,7 +5018,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, int is_protocol = 0; BlockDriverState *curr_bs = NULL; BlockDriverState *retval = NULL; - Error *local_error = NULL; if (!bs || !bs->drv || !backing_file) { return NULL; @@ -4258,7 +5025,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, filename_full = g_malloc(PATH_MAX); backing_file_full = g_malloc(PATH_MAX); - filename_tmp = g_malloc(PATH_MAX); is_protocol = path_has_protocol(backing_file); @@ -4267,41 +5033,43 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *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)) { + char *backing_file_full_ret; + if (strcmp(backing_file, curr_bs->backing_file) == 0) { retval = curr_bs->backing->bs; break; } /* Also check against the full backing filename for the image */ - bdrv_get_full_backing_filename(curr_bs, backing_file_full, PATH_MAX, - &local_error); - if (local_error == NULL) { - if (strcmp(backing_file, backing_file_full) == 0) { + backing_file_full_ret = bdrv_get_full_backing_filename(curr_bs, + NULL); + if (backing_file_full_ret) { + bool equal = strcmp(backing_file, backing_file_full_ret) == 0; + g_free(backing_file_full_ret); + if (equal) { retval = curr_bs->backing->bs; break; } - } else { - error_free(local_error); - local_error = NULL; } } else { /* If not an absolute filename path, make it relative to the current * image's filename path */ - path_combine(filename_tmp, PATH_MAX, curr_bs->filename, - backing_file); - - /* We are going to compare absolute pathnames */ - if (!realpath(filename_tmp, filename_full)) { + filename_tmp = bdrv_make_absolute_filename(curr_bs, backing_file, + NULL); + /* We are going to compare canonicalized absolute pathnames */ + if (!filename_tmp || !realpath(filename_tmp, filename_full)) { + g_free(filename_tmp); continue; } + g_free(filename_tmp); /* We need to make sure the backing filename we are comparing against * is relative to the current image filename (or absolute) */ - path_combine(filename_tmp, PATH_MAX, curr_bs->filename, - curr_bs->backing_file); - - if (!realpath(filename_tmp, backing_file_full)) { + filename_tmp = bdrv_get_full_backing_filename(curr_bs, NULL); + if (!filename_tmp || !realpath(filename_tmp, backing_file_full)) { + g_free(filename_tmp); continue; } + g_free(filename_tmp); if (strcmp(backing_file_full, filename_full) == 0) { retval = curr_bs->backing->bs; @@ -4312,7 +5080,6 @@ BlockDriverState *bdrv_find_backing_image(BlockDriverState *bs, g_free(filename_full); g_free(backing_file_full); - g_free(filename_tmp); return retval; } @@ -4334,6 +5101,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, uint64_t perm, shared_perm; Error *local_err = NULL; int ret; + BdrvDirtyBitmap *bm; if (!bs->drv) { return; @@ -4383,6 +5151,12 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, } } + for (bm = bdrv_dirty_bitmap_next(bs, NULL); bm; + bm = bdrv_dirty_bitmap_next(bs, bm)) + { + bdrv_dirty_bitmap_set_migration(bm, false); + } + ret = refresh_total_sectors(bs, bs->total_sectors); if (ret < 0) { bs->open_flags |= BDRV_O_INACTIVE; @@ -4394,6 +5168,7 @@ static void coroutine_fn bdrv_co_invalidate_cache(BlockDriverState *bs, if (parent->role->activate) { parent->role->activate(parent, &local_err); if (local_err) { + bs->open_flags |= BDRV_O_INACTIVE; error_propagate(errp, local_err); return; } @@ -4412,6 +5187,7 @@ static void coroutine_fn bdrv_invalidate_cache_co_entry(void *opaque) InvalidateCacheCo *ico = opaque; bdrv_co_invalidate_cache(ico->bs, ico->errp); ico->done = true; + aio_wait_kick(); } void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) @@ -4428,7 +5204,7 @@ void bdrv_invalidate_cache(BlockDriverState *bs, Error **errp) bdrv_invalidate_cache_co_entry(&ico); } else { co = qemu_coroutine_create(bdrv_invalidate_cache_co_entry, &ico); - qemu_coroutine_enter(co); + bdrv_coroutine_enter(bs, co); BDRV_POLL_WHILE(bs, !ico.done); } } @@ -4453,54 +5229,73 @@ void bdrv_invalidate_cache_all(Error **errp) } } -static int bdrv_inactivate_recurse(BlockDriverState *bs, - bool setting_flag) +static bool bdrv_has_bds_parent(BlockDriverState *bs, bool only_active) +{ + BdrvChild *parent; + + QLIST_FOREACH(parent, &bs->parents, next_parent) { + if (parent->role->parent_is_bds) { + BlockDriverState *parent_bs = parent->opaque; + if (!only_active || !(parent_bs->open_flags & BDRV_O_INACTIVE)) { + return true; + } + } + } + + return false; +} + +static int bdrv_inactivate_recurse(BlockDriverState *bs) { BdrvChild *child, *parent; + uint64_t perm, shared_perm; int ret; if (!bs->drv) { return -ENOMEDIUM; } - if (!setting_flag && bs->drv->bdrv_inactivate) { + /* Make sure that we don't inactivate a child before its parent. + * It will be covered by recursion from the yet active parent. */ + if (bdrv_has_bds_parent(bs, true)) { + return 0; + } + + assert(!(bs->open_flags & BDRV_O_INACTIVE)); + + /* Inactivate this node */ + if (bs->drv->bdrv_inactivate) { ret = bs->drv->bdrv_inactivate(bs); if (ret < 0) { return ret; } } - if (setting_flag && !(bs->open_flags & BDRV_O_INACTIVE)) { - uint64_t perm, shared_perm; - - QLIST_FOREACH(parent, &bs->parents, next_parent) { - if (parent->role->inactivate) { - ret = parent->role->inactivate(parent); - if (ret < 0) { - return ret; - } + QLIST_FOREACH(parent, &bs->parents, next_parent) { + if (parent->role->inactivate) { + ret = parent->role->inactivate(parent); + if (ret < 0) { + return ret; } } - - bs->open_flags |= BDRV_O_INACTIVE; - - /* Update permissions, they may differ for inactive nodes */ - bdrv_get_cumulative_perm(bs, &perm, &shared_perm); - bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort); - bdrv_set_perm(bs, perm, shared_perm); } + bs->open_flags |= BDRV_O_INACTIVE; + + /* Update permissions, they may differ for inactive nodes */ + bdrv_get_cumulative_perm(bs, &perm, &shared_perm); + bdrv_check_perm(bs, NULL, perm, shared_perm, NULL, &error_abort); + bdrv_set_perm(bs, perm, shared_perm); + + + /* Recursively inactivate children */ QLIST_FOREACH(child, &bs->children, next) { - ret = bdrv_inactivate_recurse(child->bs, setting_flag); + ret = bdrv_inactivate_recurse(child->bs); if (ret < 0) { return ret; } } - /* At this point persistent bitmaps should be already stored by the format - * driver */ - bdrv_release_persistent_dirty_bitmaps(bs); - return 0; } @@ -4509,7 +5304,6 @@ int bdrv_inactivate_all(void) BlockDriverState *bs = NULL; BdrvNextIterator it; int ret = 0; - int pass; GSList *aio_ctxs = NULL, *ctx; for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { @@ -4521,17 +5315,17 @@ int bdrv_inactivate_all(void) } } - /* We do two passes of inactivation. The first pass calls to drivers' - * .bdrv_inactivate callbacks recursively so all cache is flushed to disk; - * the second pass sets the BDRV_O_INACTIVE flag so that no further write - * is allowed. */ - for (pass = 0; pass < 2; pass++) { - for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { - ret = bdrv_inactivate_recurse(bs, pass); - if (ret < 0) { - bdrv_next_cleanup(&it); - goto out; - } + for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { + /* Nodes with BDS parents are covered by recursion from the last + * parent that gets inactivated. Don't inactivate them a second + * time if that has already happened. */ + if (bdrv_has_bds_parent(bs, false)) { + continue; + } + ret = bdrv_inactivate_recurse(bs); + if (ret < 0) { + bdrv_next_cleanup(&it); + goto out; } } @@ -4628,9 +5422,9 @@ bool bdrv_op_is_blocked(BlockDriverState *bs, BlockOpType op, Error **errp) assert((int) op >= 0 && op < BLOCK_OP_TYPE_MAX); if (!QLIST_EMPTY(&bs->op_blockers[op])) { blocker = QLIST_FIRST(&bs->op_blockers[op]); - error_propagate(errp, error_copy(blocker->reason)); - error_prepend(errp, "Node '%s' is busy: ", - bdrv_get_device_or_node_name(bs)); + error_propagate_prepend(errp, error_copy(blocker->reason), + "Node '%s' is busy: ", + bdrv_get_device_or_node_name(bs)); return true; } return false; @@ -4734,9 +5528,6 @@ void bdrv_img_create(const char *filename, const char *fmt, if (options) { qemu_opts_do_parse(opts, options, NULL, &local_err); if (local_err) { - error_report_err(local_err); - local_err = NULL; - error_setg(errp, "Invalid options for file format '%s'", fmt); goto out; } } @@ -4775,17 +5566,17 @@ void bdrv_img_create(const char *filename, const char *fmt, size = qemu_opt_get_size(opts, BLOCK_OPT_SIZE, img_size); if (backing_file && !(flags & BDRV_O_NO_BACKING)) { BlockDriverState *bs; - char *full_backing = g_new0(char, PATH_MAX); + char *full_backing; int back_flags; QDict *backing_options = NULL; - bdrv_get_full_backing_filename_from_filename(filename, backing_file, - full_backing, PATH_MAX, - &local_err); + full_backing = + bdrv_get_full_backing_filename_from_filename(filename, backing_file, + &local_err); if (local_err) { - g_free(full_backing); goto out; } + assert(full_backing); /* backing files always opened read-only */ back_flags = flags; @@ -4865,11 +5656,6 @@ AioContext *bdrv_get_aio_context(BlockDriverState *bs) return bs ? bs->aio_context : qemu_get_aio_context(); } -AioWait *bdrv_get_aio_wait(BlockDriverState *bs) -{ - return bs ? &bs->wait : NULL; -} - void bdrv_coroutine_enter(BlockDriverState *bs, Coroutine *co) { aio_co_enter(bdrv_get_aio_context(bs), co); @@ -4911,6 +5697,9 @@ void bdrv_detach_aio_context(BlockDriverState *bs) bdrv_detach_aio_context(child->bs); } + if (bs->quiesce_counter) { + aio_enable_external(bs->aio_context); + } bs->aio_context = NULL; } @@ -4924,6 +5713,10 @@ void bdrv_attach_aio_context(BlockDriverState *bs, return; } + if (bs->quiesce_counter) { + aio_disable_external(new_context); + } + bs->aio_context = new_context; QLIST_FOREACH(child, &bs->children, next) { @@ -4945,18 +5738,16 @@ void bdrv_attach_aio_context(BlockDriverState *bs, bs->walking_aio_notifiers = false; } +/* The caller must own the AioContext lock for the old AioContext of bs, but it + * must not own the AioContext lock for new_context (unless new_context is + * the same as the current context of bs). */ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) { - AioContext *ctx = bdrv_get_aio_context(bs); - - aio_disable_external(ctx); - bdrv_parent_drained_begin(bs, NULL, false); - bdrv_drain(bs); /* ensure there are no in-flight requests */ - - while (aio_poll(ctx, false)) { - /* wait for all bottom halves to execute */ + if (bdrv_get_aio_context(bs) == new_context) { + return; } + bdrv_drained_begin(bs); bdrv_detach_aio_context(bs); /* This function executes in the old AioContext so acquire the new one in @@ -4964,8 +5755,7 @@ void bdrv_set_aio_context(BlockDriverState *bs, AioContext *new_context) */ aio_context_acquire(new_context); bdrv_attach_aio_context(bs, new_context); - bdrv_parent_drained_end(bs, NULL, false); - aio_enable_external(ctx); + bdrv_drained_end(bs); aio_context_release(new_context); } @@ -5119,47 +5909,113 @@ out: return to_replace_bs; } -static bool append_open_options(QDict *d, BlockDriverState *bs) +/** + * Iterates through the list of runtime option keys that are said to + * be "strong" for a BDS. An option is called "strong" if it changes + * a BDS's data. For example, the null block driver's "size" and + * "read-zeroes" options are strong, but its "latency-ns" option is + * not. + * + * If a key returned by this function ends with a dot, all options + * starting with that prefix are strong. + */ +static const char *const *strong_options(BlockDriverState *bs, + const char *const *curopt) +{ + static const char *const global_options[] = { + "driver", "filename", NULL + }; + + if (!curopt) { + return &global_options[0]; + } + + curopt++; + if (curopt == &global_options[ARRAY_SIZE(global_options) - 1] && bs->drv) { + curopt = bs->drv->strong_runtime_opts; + } + + return (curopt && *curopt) ? curopt : NULL; +} + +/** + * Copies all strong runtime options from bs->options to the given + * QDict. The set of strong option keys is determined by invoking + * strong_options(). + * + * Returns true iff any strong option was present in bs->options (and + * thus copied to the target QDict) with the exception of "filename" + * and "driver". The caller is expected to use this value to decide + * whether the existence of strong options prevents the generation of + * a plain filename. + */ +static bool append_strong_runtime_options(QDict *d, BlockDriverState *bs) { - const QDictEntry *entry; - QemuOptDesc *desc; - BdrvChild *child; bool found_any = false; - const char *p; + const char *const *option_name = NULL; - for (entry = qdict_first(bs->options); entry; - entry = qdict_next(bs->options, entry)) - { - /* Exclude options for children */ - QLIST_FOREACH(child, &bs->children, next) { - if (strstart(qdict_entry_key(entry), child->name, &p) - && (!*p || *p == '.')) + if (!bs->drv) { + return false; + } + + while ((option_name = strong_options(bs, option_name))) { + bool option_given = false; + + assert(strlen(*option_name) > 0); + if ((*option_name)[strlen(*option_name) - 1] != '.') { + QObject *entry = qdict_get(bs->options, *option_name); + if (!entry) { + continue; + } + + qdict_put_obj(d, *option_name, qobject_ref(entry)); + option_given = true; + } else { + const QDictEntry *entry; + for (entry = qdict_first(bs->options); entry; + entry = qdict_next(bs->options, entry)) { - break; + if (strstart(qdict_entry_key(entry), *option_name, NULL)) { + qdict_put_obj(d, qdict_entry_key(entry), + qobject_ref(qdict_entry_value(entry))); + option_given = true; + } } } - 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; + /* While "driver" and "filename" need to be included in a JSON filename, + * their existence does not prohibit generation of a plain filename. */ + if (!found_any && option_given && + strcmp(*option_name, "driver") && strcmp(*option_name, "filename")) + { + found_any = true; } + } - qdict_put_obj(d, qdict_entry_key(entry), - qobject_ref(qdict_entry_value(entry))); - found_any = true; + if (!qdict_haskey(d, "driver")) { + /* Drivers created with bdrv_new_open_driver() may not have a + * @driver option. Add it here. */ + qdict_put_str(d, "driver", bs->drv->format_name); } return found_any; } +/* Note: This function may return false positives; it may return true + * even if opening the backing file specified by bs's image header + * would result in exactly bs->backing. */ +static bool bdrv_backing_overridden(BlockDriverState *bs) +{ + if (bs->backing) { + return strcmp(bs->auto_backing_file, + bs->backing->bs->filename); + } else { + /* No backing BDS, so if the image header reports any backing + * file, it must have been suppressed */ + return bs->auto_backing_file[0] != '\0'; + } +} + /* Updates the following BDS fields: * - exact_filename: A filename which may be used for opening a block device * which (mostly) equals the given BDS (even without any @@ -5175,92 +6031,108 @@ static bool append_open_options(QDict *d, BlockDriverState *bs) void bdrv_refresh_filename(BlockDriverState *bs) { BlockDriver *drv = bs->drv; + BdrvChild *child; QDict *opts; + bool backing_overridden; + bool generate_json_filename; /* Whether our default implementation should + fill exact_filename (false) or not (true) */ if (!drv) { return; } - /* 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->bs); + /* This BDS's file name may depend on any of its children's file names, so + * refresh those first */ + QLIST_FOREACH(child, &bs->children, next) { + bdrv_refresh_filename(child->bs); } + if (bs->implicit) { + /* For implicit nodes, just copy everything from the single child */ + child = QLIST_FIRST(&bs->children); + assert(QLIST_NEXT(child, next) == NULL); + + pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), + child->bs->exact_filename); + pstrcpy(bs->filename, sizeof(bs->filename), child->bs->filename); + + bs->full_open_options = qobject_ref(child->bs->full_open_options); + + return; + } + + backing_overridden = bdrv_backing_overridden(bs); + + if (bs->open_flags & BDRV_O_NO_IO) { + /* Without I/O, the backing file does not change anything. + * Therefore, in such a case (primarily qemu-img), we can + * pretend the backing file has not been overridden even if + * it technically has been. */ + backing_overridden = false; + } + + /* Gather the options QDict */ + opts = qdict_new(); + generate_json_filename = append_strong_runtime_options(opts, bs); + generate_json_filename |= backing_overridden; + + if (drv->bdrv_gather_child_options) { + /* Some block drivers may not want to present all of their children's + * options, or name them differently from BdrvChild.name */ + drv->bdrv_gather_child_options(bs, opts, backing_overridden); + } else { + QLIST_FOREACH(child, &bs->children, next) { + if (child->role == &child_backing && !backing_overridden) { + /* We can skip the backing BDS if it has not been overridden */ + continue; + } + + qdict_put(opts, child->name, + qobject_ref(child->bs->full_open_options)); + } + + if (backing_overridden && !bs->backing) { + /* Force no backing file */ + qdict_put_null(opts, "backing"); + } + } + + qobject_unref(bs->full_open_options); + bs->full_open_options = opts; + if (drv->bdrv_refresh_filename) { /* Obsolete information is of no use here, so drop the old file name * information before refreshing it */ bs->exact_filename[0] = '\0'; - if (bs->full_open_options) { - qobject_unref(bs->full_open_options); - bs->full_open_options = NULL; - } - opts = qdict_new(); - append_open_options(opts, bs); - drv->bdrv_refresh_filename(bs, opts); - qobject_unref(opts); + drv->bdrv_refresh_filename(bs); } else if (bs->file) { /* Try to reconstruct valid information from the underlying file */ - bool has_open_options; bs->exact_filename[0] = '\0'; - if (bs->full_open_options) { - qobject_unref(bs->full_open_options); - bs->full_open_options = NULL; - } - opts = qdict_new(); - has_open_options = append_open_options(opts, 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->bs->exact_filename[0] && !has_open_options) { + /* + * We can use the underlying file's filename if: + * - it has a filename, + * - the file is a protocol BDS, and + * - opening that file (as this BDS's format) will automatically create + * the BDS tree we have right now, that is: + * - the user did not significantly change this BDS's behavior with + * some explicit (strong) options + * - no non-file child of this BDS has been overridden by the user + * Both of these conditions are represented by generate_json_filename. + */ + if (bs->file->bs->exact_filename[0] && + bs->file->bs->drv->bdrv_file_open && + !generate_json_filename) + { 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->bs->full_open_options) { - qdict_put_str(opts, "driver", drv->format_name); - qdict_put(opts, "file", - qobject_ref(bs->file->bs->full_open_options)); - - bs->full_open_options = opts; - } else { - qobject_unref(opts); - } - } else if (!bs->full_open_options && qdict_size(bs->options)) { - /* There is no underlying file BDS (at least referenced by BDS.file), - * so the full options QDict should be equal to the options given - * specifically for this block device when it was opened (plus the - * driver specification). - * Because those options don't change, there is no need to update - * full_open_options when it's already set. */ - - opts = qdict_new(); - append_open_options(opts, bs); - qdict_put_str(opts, "driver", drv->format_name); - - if (bs->exact_filename[0]) { - /* This may not work for all block protocol drivers (some may - * require this filename to be parsed), but we have to find some - * default solution here, so just include it. If some block driver - * does not support pure options without any filename at all or - * needs some special format of the options QDict, it needs to - * implement the driver-specific bdrv_refresh_filename() function. - */ - qdict_put_str(opts, "filename", bs->exact_filename); - } - - bs->full_open_options = opts; } if (bs->exact_filename[0]) { pstrcpy(bs->filename, sizeof(bs->filename), bs->exact_filename); - } else if (bs->full_open_options) { + } else { QString *json = qobject_to_json(QOBJECT(bs->full_open_options)); snprintf(bs->filename, sizeof(bs->filename), "json:%s", qstring_get_str(json)); @@ -5268,6 +6140,33 @@ void bdrv_refresh_filename(BlockDriverState *bs) } } +char *bdrv_dirname(BlockDriverState *bs, Error **errp) +{ + BlockDriver *drv = bs->drv; + + if (!drv) { + error_setg(errp, "Node '%s' is ejected", bs->node_name); + return NULL; + } + + if (drv->bdrv_dirname) { + return drv->bdrv_dirname(bs, errp); + } + + if (bs->file) { + return bdrv_dirname(bs->file->bs, errp); + } + + bdrv_refresh_filename(bs); + if (bs->exact_filename[0] != '\0') { + return path_combine(bs->exact_filename, ""); + } + + error_setg(errp, "Cannot generate a base directory for %s nodes", + drv->format_name); + return NULL; +} + /* * Hot add/remove a BDS's child. So the user can take a child offline when * it is broken and take a new child online diff --git a/block/Makefile.objs b/block/Makefile.objs index c8337bf186..7a81892a52 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -1,10 +1,18 @@ -block-obj-y += raw-format.o qcow.o vdi.o vmdk.o cloop.o bochs.o vpc.o vvfat.o dmg.o +block-obj-y += raw-format.o vmdk.o vpc.o +block-obj-$(CONFIG_QCOW1) += qcow.o +block-obj-$(CONFIG_VDI) += vdi.o +block-obj-$(CONFIG_CLOOP) += cloop.o +block-obj-$(CONFIG_BOCHS) += bochs.o +block-obj-$(CONFIG_VVFAT) += vvfat.o +block-obj-$(CONFIG_DMG) += dmg.o + block-obj-y += qcow2.o qcow2-refcount.o qcow2-cluster.o qcow2-snapshot.o qcow2-cache.o qcow2-bitmap.o -block-obj-y += qed.o qed-l2-cache.o qed-table.o qed-cluster.o -block-obj-y += qed-check.o +block-obj-$(CONFIG_QED) += qed.o qed-l2-cache.o qed-table.o qed-cluster.o +block-obj-$(CONFIG_QED) += qed-check.o block-obj-y += vhdx.o vhdx-endian.o vhdx-log.o block-obj-y += quorum.o -block-obj-y += parallels.o blkdebug.o blkverify.o blkreplay.o +block-obj-y += blkdebug.o blkverify.o blkreplay.o +block-obj-$(CONFIG_PARALLELS) += parallels.o block-obj-y += blklogwrites.o block-obj-y += block-backend.o snapshot.o qapi.o block-obj-$(CONFIG_WIN32) += file-win32.o win32-aio.o @@ -14,7 +22,8 @@ block-obj-y += null.o mirror.o commit.o io.o create.o block-obj-y += throttle-groups.o block-obj-$(CONFIG_LINUX) += nvme.o -block-obj-y += nbd.o nbd-client.o sheepdog.o +block-obj-y += nbd.o nbd-client.o +block-obj-$(CONFIG_SHEEPDOG) += sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o block-obj-$(if $(CONFIG_LIBISCSI),y,n) += iscsi-opts.o block-obj-$(CONFIG_LIBNFS) += nfs.o @@ -45,8 +54,11 @@ gluster.o-libs := $(GLUSTERFS_LIBS) vxhs.o-libs := $(VXHS_LIBS) ssh.o-cflags := $(LIBSSH2_CFLAGS) ssh.o-libs := $(LIBSSH2_LIBS) -block-obj-$(if $(CONFIG_BZIP2),m,n) += dmg-bz2.o +block-obj-dmg-bz2-$(CONFIG_BZIP2) += dmg-bz2.o +block-obj-$(if $(CONFIG_DMG),m,n) += $(block-obj-dmg-bz2-y) dmg-bz2.o-libs := $(BZIP2_LIBS) +block-obj-$(if $(CONFIG_LZFSE),m,n) += dmg-lzfse.o +dmg-lzfse.o-libs := $(LZFSE_LIBS) qcow.o-libs := -lz linux-aio.o-libs := -laio parallels.o-cflags := $(LIBXML2_CFLAGS) diff --git a/block/backup.c b/block/backup.c index 8630d32926..9988753249 100644 --- a/block/backup.c +++ b/block/backup.c @@ -28,6 +28,13 @@ #define BACKUP_CLUSTER_SIZE_DEFAULT (1 << 16) +typedef struct CowRequest { + int64_t start_byte; + int64_t end_byte; + QLIST_ENTRY(CowRequest) list; + CoQueue wait_queue; /* coroutines blocked on this request */ +} CowRequest; + typedef struct BackupBlockJob { BlockJob common; BlockBackend *target; @@ -100,7 +107,6 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, void **bounce_buffer) { int ret; - struct iovec iov; QEMUIOVector qiov; BlockBackend *blk = job->common.blk; int nbytes; @@ -112,9 +118,7 @@ static int coroutine_fn backup_cow_with_bounce_buffer(BackupBlockJob *job, if (!*bounce_buffer) { *bounce_buffer = blk_blockalign(blk, job->cluster_size); } - iov.iov_base = *bounce_buffer; - iov.iov_len = nbytes; - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, *bounce_buffer, nbytes); ret = blk_co_preadv(blk, start, qiov.size, &qiov, read_flags); if (ret < 0) { @@ -322,37 +326,6 @@ void backup_do_checkpoint(BlockJob *job, Error **errp) hbitmap_set(backup_job->copy_bitmap, 0, len); } -void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, - uint64_t bytes) -{ - BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); - int64_t start, end; - - assert(block_job_driver(job) == &backup_job_driver); - - start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); - end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); - wait_for_overlapping_requests(backup_job, start, end); -} - -void backup_cow_request_begin(CowRequest *req, BlockJob *job, - int64_t offset, uint64_t bytes) -{ - BackupBlockJob *backup_job = container_of(job, BackupBlockJob, common); - int64_t start, end; - - assert(block_job_driver(job) == &backup_job_driver); - - start = QEMU_ALIGN_DOWN(offset, backup_job->cluster_size); - end = QEMU_ALIGN_UP(offset + bytes, backup_job->cluster_size); - cow_request_begin(req, backup_job, start, end); -} - -void backup_cow_request_end(CowRequest *req) -{ - cow_request_end(req); -} - static void backup_drain(BlockJob *job) { BackupBlockJob *s = container_of(job, BackupBlockJob, common); @@ -380,18 +353,6 @@ static BlockErrorAction backup_error_action(BackupBlockJob *job, } } -typedef struct { - int ret; -} BackupCompleteData; - -static void backup_complete(Job *job, void *opaque) -{ - BackupCompleteData *data = opaque; - - job_completed(job, data->ret, NULL); - g_free(data); -} - static bool coroutine_fn yield_and_check(BackupBlockJob *job) { uint64_t delay_ns; @@ -421,7 +382,7 @@ static int coroutine_fn backup_run_incremental(BackupBlockJob *job) HBitmapIter hbi; hbitmap_iter_init(&hbi, job->copy_bitmap, 0); - while ((cluster = hbitmap_iter_next(&hbi, true)) != -1) { + while ((cluster = hbitmap_iter_next(&hbi)) != -1) { do { if (yield_and_check(job)) { return 0; @@ -458,7 +419,8 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) break; } - offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset); + offset = bdrv_dirty_bitmap_next_zero(job->sync_bitmap, offset, + UINT64_MAX); if (offset == -1) { hbitmap_set(job->copy_bitmap, cluster, end - cluster); break; @@ -480,60 +442,59 @@ static void backup_incremental_init_copy_bitmap(BackupBlockJob *job) bdrv_dirty_iter_free(dbi); } -static void coroutine_fn backup_run(void *opaque) +static int coroutine_fn backup_run(Job *job, Error **errp) { - BackupBlockJob *job = opaque; - BackupCompleteData *data; - BlockDriverState *bs = blk_bs(job->common.blk); + BackupBlockJob *s = container_of(job, BackupBlockJob, common.job); + BlockDriverState *bs = blk_bs(s->common.blk); int64_t offset, nb_clusters; int ret = 0; - QLIST_INIT(&job->inflight_reqs); - qemu_co_rwlock_init(&job->flush_rwlock); + QLIST_INIT(&s->inflight_reqs); + qemu_co_rwlock_init(&s->flush_rwlock); - nb_clusters = DIV_ROUND_UP(job->len, job->cluster_size); - job_progress_set_remaining(&job->common.job, job->len); + nb_clusters = DIV_ROUND_UP(s->len, s->cluster_size); + job_progress_set_remaining(job, s->len); - job->copy_bitmap = hbitmap_alloc(nb_clusters, 0); - if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { - backup_incremental_init_copy_bitmap(job); + s->copy_bitmap = hbitmap_alloc(nb_clusters, 0); + if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { + backup_incremental_init_copy_bitmap(s); } else { - hbitmap_set(job->copy_bitmap, 0, nb_clusters); + hbitmap_set(s->copy_bitmap, 0, nb_clusters); } - job->before_write.notify = backup_before_write_notify; - bdrv_add_before_write_notifier(bs, &job->before_write); + s->before_write.notify = backup_before_write_notify; + bdrv_add_before_write_notifier(bs, &s->before_write); - if (job->sync_mode == MIRROR_SYNC_MODE_NONE) { + if (s->sync_mode == MIRROR_SYNC_MODE_NONE) { /* All bits are set in copy_bitmap to allow any cluster to be copied. * This does not actually require them to be copied. */ - while (!job_is_cancelled(&job->common.job)) { + while (!job_is_cancelled(job)) { /* Yield until the job is cancelled. We just let our before_write * notify callback service CoW requests. */ - job_yield(&job->common.job); + job_yield(job); } - } else if (job->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { - ret = backup_run_incremental(job); + } else if (s->sync_mode == MIRROR_SYNC_MODE_INCREMENTAL) { + ret = backup_run_incremental(s); } else { /* Both FULL and TOP SYNC_MODE's require copying.. */ - for (offset = 0; offset < job->len; - offset += job->cluster_size) { + for (offset = 0; offset < s->len; + offset += s->cluster_size) { bool error_is_read; int alloced = 0; - if (yield_and_check(job)) { + if (yield_and_check(s)) { break; } - if (job->sync_mode == MIRROR_SYNC_MODE_TOP) { + if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { int i; int64_t n; /* Check to see if these blocks are already in the * backing file. */ - for (i = 0; i < job->cluster_size;) { + for (i = 0; i < s->cluster_size;) { /* 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. @@ -542,7 +503,7 @@ static void coroutine_fn backup_run(void *opaque) * needed but at some point that is always the case. */ alloced = bdrv_is_allocated(bs, offset + i, - job->cluster_size - i, &n); + s->cluster_size - i, &n); i += n; if (alloced || n == 0) { @@ -560,33 +521,31 @@ static void coroutine_fn backup_run(void *opaque) if (alloced < 0) { ret = alloced; } else { - ret = backup_do_cow(job, offset, job->cluster_size, + ret = backup_do_cow(s, offset, s->cluster_size, &error_is_read, false); } if (ret < 0) { /* Depending on error action, fail now or retry cluster */ BlockErrorAction action = - backup_error_action(job, error_is_read, -ret); + backup_error_action(s, error_is_read, -ret); if (action == BLOCK_ERROR_ACTION_REPORT) { break; } else { - offset -= job->cluster_size; + offset -= s->cluster_size; continue; } } } } - notifier_with_return_remove(&job->before_write); + notifier_with_return_remove(&s->before_write); /* wait until pending backup_do_cow() calls have completed */ - qemu_co_rwlock_wrlock(&job->flush_rwlock); - qemu_co_rwlock_unlock(&job->flush_rwlock); - hbitmap_free(job->copy_bitmap); + qemu_co_rwlock_wrlock(&s->flush_rwlock); + qemu_co_rwlock_unlock(&s->flush_rwlock); + hbitmap_free(s->copy_bitmap); - data = g_malloc(sizeof(*data)); - data->ret = ret; - job_defer_to_main_loop(&job->common.job, backup_complete, data); + return ret; } static const BlockJobDriver backup_job_driver = { @@ -596,7 +555,7 @@ static const BlockJobDriver backup_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = backup_run, + .run = backup_run, .commit = backup_commit, .abort = backup_abort, .clean = backup_clean, diff --git a/block/blkdebug.c b/block/blkdebug.c index 0759452925..efd9441625 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -401,7 +401,7 @@ static int blkdebug_open(BlockDriverState *bs, QDict *options, int flags, bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | - ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); ret = -EINVAL; @@ -811,51 +811,37 @@ static int64_t blkdebug_getlength(BlockDriverState *bs) return bdrv_getlength(bs->file->bs); } -static void blkdebug_refresh_filename(BlockDriverState *bs, QDict *options) +static void blkdebug_refresh_filename(BlockDriverState *bs) { BDRVBlkdebugState *s = bs->opaque; - QDict *opts; const QDictEntry *e; - bool force_json = false; + int ret; - 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")) - { - force_json = true; - break; - } - } - - if (force_json && !bs->file->bs->full_open_options) { - /* The config file cannot be recreated, so creating a plain filename - * is impossible */ + if (!bs->file->bs->exact_filename[0]) { return; } - if (!force_json && bs->file->bs->exact_filename[0]) { - int ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), - "blkdebug:%s:%s", s->config_file ?: "", - bs->file->bs->exact_filename); - if (ret >= sizeof(bs->exact_filename)) { - /* An overflow makes the filename unusable, so do not report any */ - bs->exact_filename[0] = 0; + for (e = qdict_first(bs->full_open_options); e; + e = qdict_next(bs->full_open_options, e)) + { + /* Real child options are under "image", but "x-image" may + * contain a filename */ + if (strcmp(qdict_entry_key(e), "config") && + strcmp(qdict_entry_key(e), "image") && + strcmp(qdict_entry_key(e), "x-image") && + strcmp(qdict_entry_key(e), "driver")) + { + return; } } - opts = qdict_new(); - qdict_put_str(opts, "driver", "blkdebug"); - - qdict_put(opts, "image", qobject_ref(bs->file->bs->full_open_options)); - - for (e = qdict_first(options); e; e = qdict_next(options, e)) { - if (strcmp(qdict_entry_key(e), "x-image")) { - qdict_put_obj(opts, qdict_entry_key(e), - qobject_ref(qdict_entry_value(e))); - } + ret = snprintf(bs->exact_filename, sizeof(bs->exact_filename), + "blkdebug:%s:%s", + s->config_file ?: "", bs->file->bs->exact_filename); + if (ret >= sizeof(bs->exact_filename)) { + /* An overflow makes the filename unusable, so do not report any */ + bs->exact_filename[0] = 0; } - - bs->full_open_options = opts; } static void blkdebug_refresh_limits(BlockDriverState *bs, Error **errp) @@ -888,6 +874,20 @@ static int blkdebug_reopen_prepare(BDRVReopenState *reopen_state, return 0; } +static const char *const blkdebug_strong_runtime_opts[] = { + "config", + "inject-error.", + "set-state.", + "align", + "max-transfer", + "opt-write-zero", + "max-write-zero", + "opt-discard", + "max-discard", + + NULL +}; + static BlockDriver bdrv_blkdebug = { .format_name = "blkdebug", .protocol_name = "blkdebug", @@ -917,6 +917,8 @@ static BlockDriver bdrv_blkdebug = { = blkdebug_debug_remove_breakpoint, .bdrv_debug_resume = blkdebug_debug_resume, .bdrv_debug_is_suspended = blkdebug_debug_is_suspended, + + .strong_runtime_opts = blkdebug_strong_runtime_opts, }; static void bdrv_blkdebug_init(void) diff --git a/block/blklogwrites.c b/block/blklogwrites.c index ff98cd5533..eb2b4901a5 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -280,31 +280,6 @@ static int64_t blk_log_writes_getlength(BlockDriverState *bs) return bdrv_getlength(bs->file->bs); } -static void blk_log_writes_refresh_filename(BlockDriverState *bs, - QDict *options) -{ - BDRVBlkLogWritesState *s = bs->opaque; - - /* bs->file->bs has already been refreshed */ - bdrv_refresh_filename(s->log_file->bs); - - if (bs->file->bs->full_open_options - && s->log_file->bs->full_open_options) - { - QDict *opts = qdict_new(); - qdict_put_str(opts, "driver", "blklogwrites"); - - qobject_ref(bs->file->bs->full_open_options); - qdict_put_obj(opts, "file", QOBJECT(bs->file->bs->full_open_options)); - qobject_ref(s->log_file->bs->full_open_options); - qdict_put_obj(opts, "log", - QOBJECT(s->log_file->bs->full_open_options)); - qdict_put_int(opts, "log-sector-size", s->sectorsize); - - bs->full_open_options = opts; - } -} - static void blk_log_writes_child_perm(BlockDriverState *bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *ro_q, @@ -521,6 +496,13 @@ blk_log_writes_co_pdiscard(BlockDriverState *bs, int64_t offset, int count) LOG_DISCARD_FLAG, false); } +static const char *const blk_log_writes_strong_runtime_opts[] = { + "log-append", + "log-sector-size", + + NULL +}; + static BlockDriver bdrv_blk_log_writes = { .format_name = "blklogwrites", .instance_size = sizeof(BDRVBlkLogWritesState), @@ -528,7 +510,6 @@ static BlockDriver bdrv_blk_log_writes = { .bdrv_open = blk_log_writes_open, .bdrv_close = blk_log_writes_close, .bdrv_getlength = blk_log_writes_getlength, - .bdrv_refresh_filename = blk_log_writes_refresh_filename, .bdrv_child_perm = blk_log_writes_child_perm, .bdrv_refresh_limits = blk_log_writes_refresh_limits, @@ -540,6 +521,7 @@ static BlockDriver bdrv_blk_log_writes = { .bdrv_co_block_status = bdrv_co_block_status_from_file, .is_filter = true, + .strong_runtime_opts = blk_log_writes_strong_runtime_opts, }; static void bdrv_blk_log_writes_init(void) diff --git a/block/blkreplay.c b/block/blkreplay.c old mode 100755 new mode 100644 index 766150ade6..b5d9efdeca --- a/block/blkreplay.c +++ b/block/blkreplay.c @@ -43,10 +43,6 @@ fail: return ret; } -static void blkreplay_close(BlockDriverState *bs) -{ -} - static int64_t blkreplay_getlength(BlockDriverState *bs) { return bdrv_getlength(bs->file->bs); @@ -135,7 +131,6 @@ static BlockDriver bdrv_blkreplay = { .instance_size = 0, .bdrv_open = blkreplay_open, - .bdrv_close = blkreplay_close, .bdrv_child_perm = bdrv_filter_default_perms, .bdrv_getlength = blkreplay_getlength, diff --git a/block/blkverify.c b/block/blkverify.c index 89bf4386e3..3ff77ff49a 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -281,27 +281,10 @@ static bool blkverify_recurse_is_first_non_filter(BlockDriverState *bs, return bdrv_recurse_is_first_non_filter(s->test_file->bs, candidate); } -static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) +static void blkverify_refresh_filename(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - /* bs->file->bs has already been refreshed */ - bdrv_refresh_filename(s->test_file->bs); - - if (bs->file->bs->full_open_options - && s->test_file->bs->full_open_options) - { - QDict *opts = qdict_new(); - qdict_put_str(opts, "driver", "blkverify"); - - qdict_put(opts, "raw", - qobject_ref(bs->file->bs->full_open_options)); - qdict_put(opts, "test", - qobject_ref(s->test_file->bs->full_open_options)); - - bs->full_open_options = opts; - } - if (bs->file->bs->exact_filename[0] && s->test_file->bs->exact_filename[0]) { @@ -316,6 +299,15 @@ static void blkverify_refresh_filename(BlockDriverState *bs, QDict *options) } } +static char *blkverify_dirname(BlockDriverState *bs, Error **errp) +{ + /* In general, there are two BDSs with different dirnames below this one; + * so there is no unique dirname we could return (unless both are equal by + * chance). Therefore, to be consistent, just always return NULL. */ + error_setg(errp, "Cannot generate a base directory for blkverify nodes"); + return NULL; +} + static BlockDriver bdrv_blkverify = { .format_name = "blkverify", .protocol_name = "blkverify", @@ -327,6 +319,7 @@ static BlockDriver bdrv_blkverify = { .bdrv_child_perm = bdrv_filter_default_perms, .bdrv_getlength = blkverify_getlength, .bdrv_refresh_filename = blkverify_refresh_filename, + .bdrv_dirname = blkverify_dirname, .bdrv_co_preadv = blkverify_co_preadv, .bdrv_co_pwritev = blkverify_co_pwritev, diff --git a/block/block-backend.c b/block/block-backend.c index f2f75a977d..f78e82a707 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -47,9 +47,7 @@ struct BlockBackend { QTAILQ_ENTRY(BlockBackend) monitor_link; /* for monitor_block_backends */ BlockBackendPublic public; - void *dev; /* attached device model, if any */ - bool legacy_dev; /* true if dev is not a DeviceState */ - /* TODO change to DeviceState when all users are qdevified */ + DeviceState *dev; /* attached device model, if any */ const BlockDevOps *dev_ops; void *dev_opaque; @@ -88,7 +86,6 @@ struct BlockBackend { * Accessed with atomic ops. */ unsigned int in_flight; - AioWait wait; }; typedef struct BlockBackendAIOCB { @@ -121,6 +118,7 @@ static void blk_root_inherit_options(int *child_flags, QDict *child_options, abort(); } static void blk_root_drained_begin(BdrvChild *child); +static bool blk_root_drained_poll(BdrvChild *child); static void blk_root_drained_end(BdrvChild *child); static void blk_root_change_media(BdrvChild *child, bool load); @@ -294,6 +292,7 @@ static const BdrvChildRole child_root = { .get_parent_desc = blk_root_get_parent_desc, .drained_begin = blk_root_drained_begin, + .drained_poll = blk_root_drained_poll, .drained_end = blk_root_drained_end, .activate = blk_root_activate, @@ -324,6 +323,9 @@ BlockBackend *blk_new(uint64_t perm, uint64_t shared_perm) blk->shared_perm = shared_perm; blk_set_enable_write_cache(blk, true); + blk->on_read_error = BLOCKDEV_ON_ERROR_REPORT; + blk->on_write_error = BLOCKDEV_ON_ERROR_ENOSPC; + block_acct_init(&blk->stats); notifier_list_init(&blk->remove_bs_notifiers); @@ -419,7 +421,6 @@ static void drive_info_del(DriveInfo *dinfo) return; } qemu_opts_del(dinfo->opts); - g_free(dinfo->serial); g_free(dinfo); } @@ -434,6 +435,7 @@ int blk_get_refcnt(BlockBackend *blk) */ void blk_ref(BlockBackend *blk) { + assert(blk->refcnt > 0); blk->refcnt++; } @@ -446,7 +448,13 @@ void blk_unref(BlockBackend *blk) { if (blk) { assert(blk->refcnt > 0); - if (!--blk->refcnt) { + if (blk->refcnt > 1) { + blk->refcnt--; + } else { + blk_drain(blk); + /* blk_drain() cannot resurrect blk, nobody held a reference */ + assert(blk->refcnt == 1); + blk->refcnt = 0; blk_delete(blk); } } @@ -826,7 +834,11 @@ void blk_get_perm(BlockBackend *blk, uint64_t *perm, uint64_t *shared_perm) *shared_perm = blk->shared_perm; } -static int blk_do_attach_dev(BlockBackend *blk, void *dev) +/* + * Attach device model @dev to @blk. + * Return 0 on success, -EBUSY when a device model is attached already. + */ +int blk_attach_dev(BlockBackend *blk, DeviceState *dev) { if (blk->dev) { return -EBUSY; @@ -841,40 +853,16 @@ static int blk_do_attach_dev(BlockBackend *blk, void *dev) blk_ref(blk); blk->dev = dev; - blk->legacy_dev = false; blk_iostatus_reset(blk); return 0; } -/* - * Attach device model @dev to @blk. - * Return 0 on success, -EBUSY when a device model is attached already. - */ -int blk_attach_dev(BlockBackend *blk, DeviceState *dev) -{ - return blk_do_attach_dev(blk, dev); -} - -/* - * Attach device model @dev to @blk. - * @blk must not have a device model attached already. - * TODO qdevified devices don't use this, remove when devices are qdevified - */ -void blk_attach_dev_legacy(BlockBackend *blk, void *dev) -{ - if (blk_do_attach_dev(blk, dev) < 0) { - abort(); - } - blk->legacy_dev = true; -} - /* * Detach device model @dev from @blk. * @dev must be currently attached to @blk. */ -void blk_detach_dev(BlockBackend *blk, void *dev) -/* TODO change to DeviceState *dev when all users are qdevified */ +void blk_detach_dev(BlockBackend *blk, DeviceState *dev) { assert(blk->dev == dev); blk->dev = NULL; @@ -888,8 +876,7 @@ void blk_detach_dev(BlockBackend *blk, void *dev) /* * Return the device model attached to @blk if any, else null. */ -void *blk_get_attached_dev(BlockBackend *blk) -/* TODO change to return DeviceState * when all users are qdevified */ +DeviceState *blk_get_attached_dev(BlockBackend *blk) { return blk->dev; } @@ -898,17 +885,15 @@ void *blk_get_attached_dev(BlockBackend *blk) * device attached to the BlockBackend. */ char *blk_get_attached_dev_id(BlockBackend *blk) { - DeviceState *dev; - - assert(!blk->legacy_dev); - dev = blk->dev; + DeviceState *dev = blk->dev; if (!dev) { return g_strdup(""); } else if (dev->id) { return g_strdup(dev->id); } - return object_get_canonical_path(OBJECT(dev)); + + return object_get_canonical_path(OBJECT(dev)) ?: g_strdup(""); } /* @@ -938,11 +923,6 @@ BlockBackend *blk_by_dev(void *dev) void blk_set_dev_ops(BlockBackend *blk, const BlockDevOps *ops, void *opaque) { - /* All drivers that use blk_set_dev_ops() are qdevified and we want to keep - * it that way, so we can assume blk->dev, if present, is a DeviceState if - * blk->dev_ops is set. Non-device users may use dev_ops without device. */ - assert(!blk->legacy_dev); - blk->dev_ops = ops; blk->dev_opaque = opaque; @@ -968,8 +948,6 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) bool tray_was_open, tray_is_open; Error *local_err = NULL; - assert(!blk->legacy_dev); - tray_was_open = blk_dev_is_tray_open(blk); blk->dev_ops->change_media_cb(blk->dev_opaque, load, &local_err); if (local_err) { @@ -981,8 +959,7 @@ void blk_dev_change_media_cb(BlockBackend *blk, bool load, Error **errp) if (tray_was_open != tray_is_open) { char *id = blk_get_attached_dev_id(blk); - qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open, - &error_abort); + qapi_event_send_device_tray_moved(blk_name(blk), id, tray_is_open); g_free(id); } } @@ -1210,6 +1187,7 @@ static void blk_read_entry(void *opaque) rwco->ret = blk_co_preadv(rwco->blk, rwco->offset, qiov->size, qiov, rwco->flags); + aio_wait_kick(); } static void blk_write_entry(void *opaque) @@ -1219,23 +1197,15 @@ static void blk_write_entry(void *opaque) rwco->ret = blk_co_pwritev(rwco->blk, rwco->offset, qiov->size, qiov, rwco->flags); + aio_wait_kick(); } static int blk_prw(BlockBackend *blk, int64_t offset, uint8_t *buf, int64_t bytes, CoroutineEntry co_entry, BdrvRequestFlags flags) { - QEMUIOVector qiov; - struct iovec iov; - BlkRwCo rwco; - - iov = (struct iovec) { - .iov_base = buf, - .iov_len = bytes, - }; - qemu_iovec_init_external(&qiov, &iov, 1); - - rwco = (BlkRwCo) { + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); + BlkRwCo rwco = { .blk = blk, .offset = offset, .iobuf = &qiov, @@ -1283,15 +1253,15 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags) return bdrv_make_zero(blk->root, flags); } -static void blk_inc_in_flight(BlockBackend *blk) +void blk_inc_in_flight(BlockBackend *blk) { atomic_inc(&blk->in_flight); } -static void blk_dec_in_flight(BlockBackend *blk) +void blk_dec_in_flight(BlockBackend *blk) { atomic_dec(&blk->in_flight); - aio_wait_kick(&blk->wait); + aio_wait_kick(); } static void error_callback_bh(void *opaque) @@ -1332,8 +1302,8 @@ static const AIOCBInfo blk_aio_em_aiocb_info = { static void blk_aio_complete(BlkAioEmAIOCB *acb) { if (acb->has_returned) { - blk_dec_in_flight(acb->rwco.blk); acb->common.cb(acb->common.opaque, acb->rwco.ret); + blk_dec_in_flight(acb->rwco.blk); qemu_aio_unref(acb); } } @@ -1530,6 +1500,7 @@ static void blk_ioctl_entry(void *opaque) rwco->ret = blk_co_ioctl(rwco->blk, rwco->offset, qiov->iov[0].iov_base); + aio_wait_kick(); } int blk_ioctl(BlockBackend *blk, unsigned long int req, void *buf) @@ -1576,6 +1547,7 @@ static void blk_flush_entry(void *opaque) { BlkRwCo *rwco = opaque; rwco->ret = blk_co_flush(rwco->blk); + aio_wait_kick(); } int blk_flush(BlockBackend *blk) @@ -1592,9 +1564,8 @@ void blk_drain(BlockBackend *blk) } /* We may have -ENOMEDIUM completions in flight */ - AIO_WAIT_WHILE(&blk->wait, - blk_get_aio_context(blk), - atomic_mb_read(&blk->in_flight) > 0); + AIO_WAIT_WHILE(blk_get_aio_context(blk), + atomic_mb_read(&blk->in_flight) > 0); if (bs) { bdrv_drained_end(bs); @@ -1613,8 +1584,7 @@ void blk_drain_all(void) aio_context_acquire(ctx); /* We may have -ENOMEDIUM completions in flight */ - AIO_WAIT_WHILE(&blk->wait, ctx, - atomic_mb_read(&blk->in_flight) > 0); + AIO_WAIT_WHILE(ctx, atomic_mb_read(&blk->in_flight) > 0); aio_context_release(ctx); } @@ -1666,8 +1636,7 @@ static void send_qmp_error_event(BlockBackend *blk, qapi_event_send_block_io_error(blk_name(blk), !!bs, bs ? bdrv_get_node_name(bs) : NULL, optype, action, blk_iostatus_is_enabled(blk), - error == ENOSPC, strerror(error), - &error_abort); + error == ENOSPC, strerror(error)); } /* This is done by device models because, while the block layer knows @@ -1702,7 +1671,7 @@ void blk_error_action(BlockBackend *blk, BlockErrorAction action, } } -int blk_is_read_only(BlockBackend *blk) +bool blk_is_read_only(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); @@ -1713,18 +1682,18 @@ int blk_is_read_only(BlockBackend *blk) } } -int blk_is_sg(BlockBackend *blk) +bool blk_is_sg(BlockBackend *blk) { BlockDriverState *bs = blk_bs(blk); if (!bs) { - return 0; + return false; } return bdrv_is_sg(bs); } -int blk_enable_write_cache(BlockBackend *blk) +bool blk_enable_write_cache(BlockBackend *blk) { return blk->enable_write_cache; } @@ -1772,9 +1741,6 @@ void blk_eject(BlockBackend *blk, bool eject_flag) BlockDriverState *bs = blk_bs(blk); char *id; - /* blk_eject is only called by qdevified devices */ - assert(!blk->legacy_dev); - if (bs) { bdrv_eject(bs, eject_flag); } @@ -1783,7 +1749,7 @@ void blk_eject(BlockBackend *blk, bool eject_flag) * the frontend experienced a tray event. */ id = blk_get_attached_dev_id(blk); qapi_event_send_device_tray_moved(blk_name(blk), id, - eject_flag, &error_abort); + eject_flag); g_free(id); } @@ -1798,6 +1764,13 @@ int blk_get_flags(BlockBackend *blk) } } +/* Returns the minimum request alignment, in bytes; guaranteed nonzero */ +uint32_t blk_get_request_alignment(BlockBackend *blk) +{ + BlockDriverState *bs = blk_bs(blk); + return bs ? bs->bl.request_alignment : BDRV_SECTOR_SIZE; +} + /* Returns the maximum transfer length, in bytes; guaranteed nonzero */ uint32_t blk_get_max_transfer(BlockBackend *blk) { @@ -2011,6 +1984,7 @@ static void blk_pdiscard_entry(void *opaque) QEMUIOVector *qiov = rwco->iobuf; rwco->ret = blk_co_pdiscard(rwco->blk, rwco->offset, qiov->size); + aio_wait_kick(); } int blk_pdiscard(BlockBackend *blk, int64_t offset, int bytes) @@ -2192,6 +2166,13 @@ static void blk_root_drained_begin(BdrvChild *child) } } +static bool blk_root_drained_poll(BdrvChild *child) +{ + BlockBackend *blk = child->opaque; + assert(blk->quiesce_counter); + return !!blk->in_flight; +} + static void blk_root_drained_end(BdrvChild *child) { BlockBackend *blk = child->opaque; @@ -2235,3 +2216,8 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, blk_out->root, off_out, bytes, read_flags, write_flags); } + +const BdrvChild *blk_root(BlockBackend *blk) +{ + return blk->root; +} diff --git a/block/bochs.c b/block/bochs.c index 50c630047b..79f95d3b50 100644 --- a/block/bochs.c +++ b/block/bochs.c @@ -85,14 +85,14 @@ static int bochs_probe(const uint8_t *buf, int buf_size, const char *filename) const struct bochs_header *bochs = (const void *)buf; if (buf_size < HEADER_SIZE) - return 0; + return 0; if (!strcmp(bochs->magic, HEADER_MAGIC) && - !strcmp(bochs->type, REDOLOG_TYPE) && - !strcmp(bochs->subtype, GROWING_TYPE) && - ((le32_to_cpu(bochs->version) == HEADER_VERSION) || - (le32_to_cpu(bochs->version) == HEADER_V1))) - return 100; + !strcmp(bochs->type, REDOLOG_TYPE) && + !strcmp(bochs->subtype, GROWING_TYPE) && + ((le32_to_cpu(bochs->version) == HEADER_VERSION) || + (le32_to_cpu(bochs->version) == HEADER_V1))) + return 100; return 0; } @@ -105,23 +105,18 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, struct bochs_header bochs; int ret; + /* No write support yet */ + ret = bdrv_apply_auto_read_only(bs, NULL, errp); + if (ret < 0) { + return ret; + } + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); if (!bs->file) { return -EINVAL; } - if (!bdrv_is_read_only(bs)) { - error_report("Opening bochs images without an explicit read-only=on " - "option is deprecated. Future versions will refuse to " - "open the image instead of automatically marking the " - "image read-only."); - ret = bdrv_set_read_only(bs, true, errp); /* no write support yet */ - if (ret < 0) { - return ret; - } - } - ret = bdrv_pread(bs->file, 0, &bochs, sizeof(bochs)); if (ret < 0) { return ret; @@ -130,8 +125,8 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, if (strcmp(bochs.magic, HEADER_MAGIC) || strcmp(bochs.type, REDOLOG_TYPE) || strcmp(bochs.subtype, GROWING_TYPE) || - ((le32_to_cpu(bochs.version) != HEADER_VERSION) && - (le32_to_cpu(bochs.version) != HEADER_V1))) { + ((le32_to_cpu(bochs.version) != HEADER_VERSION) && + (le32_to_cpu(bochs.version) != HEADER_V1))) { error_setg(errp, "Image not in Bochs format"); return -EINVAL; } @@ -163,7 +158,7 @@ static int bochs_open(BlockDriverState *bs, QDict *options, int flags, } for (i = 0; i < s->catalog_size; i++) - le32_to_cpus(&s->catalog_bitmap[i]); + le32_to_cpus(&s->catalog_bitmap[i]); s->data_offset = le32_to_cpu(bochs.header) + (s->catalog_size * 4); @@ -222,7 +217,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) extent_offset = (offset % s->extent_size) / 512; if (s->catalog_bitmap[extent_index] == 0xffffffff) { - return 0; /* not allocated */ + return 0; /* not allocated */ } bitmap_offset = s->data_offset + @@ -237,7 +232,7 @@ static int64_t seek_to_sector(BlockDriverState *bs, int64_t sector_num) } if (!((bitmap_entry >> (extent_offset % 8)) & 1)) { - return 0; /* not allocated */ + return 0; /* not allocated */ } return bitmap_offset + (512 * (s->bitmap_blocks + extent_offset)); diff --git a/block/cloop.c b/block/cloop.c index 2be68987bd..df2b85f723 100644 --- a/block/cloop.c +++ b/block/cloop.c @@ -67,23 +67,17 @@ static int cloop_open(BlockDriverState *bs, QDict *options, int flags, uint32_t offsets_size, max_compressed_block_size = 1, i; int ret; + ret = bdrv_apply_auto_read_only(bs, NULL, errp); + if (ret < 0) { + return ret; + } + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); if (!bs->file) { return -EINVAL; } - if (!bdrv_is_read_only(bs)) { - error_report("Opening cloop images without an explicit read-only=on " - "option is deprecated. Future versions will refuse to " - "open the image instead of automatically marking the " - "image read-only."); - ret = bdrv_set_read_only(bs, true, errp); - if (ret < 0) { - return ret; - } - } - /* read header */ ret = bdrv_pread(bs->file, 128, &s->block_size, 4); if (ret < 0) { diff --git a/block/commit.c b/block/commit.c index e1814d9693..ba60fef58a 100644 --- a/block/commit.c +++ b/block/commit.c @@ -36,8 +36,10 @@ typedef struct CommitBlockJob { BlockDriverState *commit_top_bs; BlockBackend *top; BlockBackend *base; + BlockDriverState *base_bs; BlockdevOnError on_error; - int base_flags; + bool base_read_only; + bool chain_frozen; char *backing_file_str; } CommitBlockJob; @@ -46,14 +48,9 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, void *buf) { int ret = 0; - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = buf, - .iov_len = bytes, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); assert(bytes < SIZE_MAX); - qemu_iovec_init_external(&qiov, &iov, 1); ret = blk_co_preadv(bs, offset, qiov.size, &qiov, 0); if (ret < 0) { @@ -68,76 +65,79 @@ static int coroutine_fn commit_populate(BlockBackend *bs, BlockBackend *base, return 0; } -typedef struct { - int ret; -} CommitCompleteData; - -static void commit_complete(Job *job, void *opaque) +static int commit_prepare(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); - BlockJob *bjob = &s->common; - CommitCompleteData *data = opaque; - BlockDriverState *top = blk_bs(s->top); - BlockDriverState *base = blk_bs(s->base); - BlockDriverState *commit_top_bs = s->commit_top_bs; - int ret = data->ret; - bool remove_commit_top_bs = false; - /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ - bdrv_ref(top); - bdrv_ref(commit_top_bs); + bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); + s->chain_frozen = false; /* Remove base node parent that still uses BLK_PERM_WRITE/RESIZE before * the normal backing chain can be restored. */ blk_unref(s->base); + s->base = NULL; - if (!job_is_cancelled(job) && ret == 0) { - /* success */ - ret = bdrv_drop_intermediate(s->commit_top_bs, base, - s->backing_file_str); - } else { - /* XXX Can (or should) we somehow keep 'consistent read' blocked even - * after the failed/cancelled commit job is gone? If we already wrote - * something to base, the intermediate images aren't valid any more. */ - remove_commit_top_bs = true; + /* FIXME: bdrv_drop_intermediate treats total failures and partial failures + * identically. Further work is needed to disambiguate these cases. */ + return bdrv_drop_intermediate(s->commit_top_bs, s->base_bs, + s->backing_file_str); +} + +static void commit_abort(Job *job) +{ + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + BlockDriverState *top_bs = blk_bs(s->top); + + if (s->chain_frozen) { + bdrv_unfreeze_backing_chain(s->commit_top_bs, s->base_bs); } + /* Make sure commit_top_bs and top stay around until bdrv_replace_node() */ + bdrv_ref(top_bs); + bdrv_ref(s->commit_top_bs); + + if (s->base) { + blk_unref(s->base); + } + + /* free the blockers on the intermediate nodes so that bdrv_replace_nodes + * can succeed */ + block_job_remove_all_bdrv(&s->common); + + /* If bdrv_drop_intermediate() failed (or was not invoked), remove the + * commit filter driver from the backing chain now. Do this as the final + * step so that the 'consistent read' permission can be granted. + * + * XXX Can (or should) we somehow keep 'consistent read' blocked even + * after the failed/cancelled commit job is gone? If we already wrote + * something to base, the intermediate images aren't valid any more. */ + bdrv_child_try_set_perm(s->commit_top_bs->backing, 0, BLK_PERM_ALL, + &error_abort); + bdrv_replace_node(s->commit_top_bs, backing_bs(s->commit_top_bs), + &error_abort); + + bdrv_unref(s->commit_top_bs); + bdrv_unref(top_bs); +} + +static void commit_clean(Job *job) +{ + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); + /* restore base open flags here if appropriate (e.g., change the base back * to r/o). These reopens do not need to be atomic, since we won't abort * even on failure here */ - if (s->base_flags != bdrv_get_flags(base)) { - bdrv_reopen(base, s->base_flags, NULL); + if (s->base_read_only) { + bdrv_reopen_set_read_only(s->base_bs, true, NULL); } + g_free(s->backing_file_str); blk_unref(s->top); - - /* If there is more than one reference to the job (e.g. if called from - * job_finish_sync()), job_completed() won't free it and therefore the - * blockers on the intermediate nodes remain. This would cause - * bdrv_set_backing_hd() to fail. */ - block_job_remove_all_bdrv(bjob); - - job_completed(job, ret, NULL); - g_free(data); - - /* If bdrv_drop_intermediate() didn't already do that, remove the commit - * filter driver from the backing chain. Do this as the final step so that - * the 'consistent read' permission can be granted. */ - if (remove_commit_top_bs) { - bdrv_child_try_set_perm(commit_top_bs->backing, 0, BLK_PERM_ALL, - &error_abort); - bdrv_replace_node(commit_top_bs, backing_bs(commit_top_bs), - &error_abort); - } - - bdrv_unref(commit_top_bs); - bdrv_unref(top); } -static void coroutine_fn commit_run(void *opaque) +static int coroutine_fn commit_run(Job *job, Error **errp) { - CommitBlockJob *s = opaque; - CommitCompleteData *data; + CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); int64_t offset; uint64_t delay_ns = 0; int ret = 0; @@ -210,9 +210,7 @@ static void coroutine_fn commit_run(void *opaque) out: qemu_vfree(buf); - data = g_malloc(sizeof(*data)); - data->ret = ret; - job_defer_to_main_loop(&s->common.job, commit_complete, data); + return ret; } static const BlockJobDriver commit_job_driver = { @@ -222,7 +220,10 @@ static const BlockJobDriver commit_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = commit_run, + .run = commit_run, + .prepare = commit_prepare, + .abort = commit_abort, + .clean = commit_clean }, }; @@ -232,17 +233,12 @@ static int coroutine_fn bdrv_commit_top_preadv(BlockDriverState *bs, return bdrv_co_preadv(bs->backing, offset, bytes, qiov, flags); } -static void bdrv_commit_top_refresh_filename(BlockDriverState *bs, QDict *opts) +static void bdrv_commit_top_refresh_filename(BlockDriverState *bs) { - bdrv_refresh_filename(bs->backing->bs); pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->backing->bs->filename); } -static void bdrv_commit_top_close(BlockDriverState *bs) -{ -} - static void bdrv_commit_top_child_perm(BlockDriverState *bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, @@ -260,17 +256,16 @@ static BlockDriver bdrv_commit_top = { .bdrv_co_preadv = bdrv_commit_top_preadv, .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_commit_top_refresh_filename, - .bdrv_close = bdrv_commit_top_close, .bdrv_child_perm = bdrv_commit_top_child_perm, }; void commit_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, BlockDriverState *top, int64_t speed, + BlockDriverState *base, BlockDriverState *top, + int creation_flags, int64_t speed, BlockdevOnError on_error, const char *backing_file_str, const char *filter_node_name, Error **errp) { CommitBlockJob *s; - int orig_base_flags; BlockDriverState *iter; BlockDriverState *commit_top_bs = NULL; Error *local_err = NULL; @@ -283,17 +278,15 @@ void commit_start(const char *job_id, BlockDriverState *bs, } s = block_job_create(job_id, &commit_job_driver, NULL, bs, 0, BLK_PERM_ALL, - speed, JOB_DEFAULT, NULL, NULL, errp); + speed, creation_flags, NULL, NULL, errp); if (!s) { return; } /* convert base to r/w, if necessary */ - orig_base_flags = bdrv_get_flags(base); - if (!(orig_base_flags & BDRV_O_RDWR)) { - bdrv_reopen(base, orig_base_flags | BDRV_O_RDWR, &local_err); - if (local_err != NULL) { - error_propagate(errp, local_err); + s->base_read_only = bdrv_is_read_only(base); + if (s->base_read_only) { + if (bdrv_reopen_set_read_only(base, false, errp) != 0) { goto fail; } } @@ -345,6 +338,11 @@ void commit_start(const char *job_id, BlockDriverState *bs, } } + if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { + goto fail; + } + s->chain_frozen = true; + ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); if (ret < 0) { goto fail; @@ -360,6 +358,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, if (ret < 0) { goto fail; } + s->base_bs = base; /* Required permissions are already taken with block_job_add_bdrv() */ s->top = blk_new(0, BLK_PERM_ALL); @@ -368,7 +367,6 @@ void commit_start(const char *job_id, BlockDriverState *bs, goto fail; } - s->base_flags = orig_base_flags; s->backing_file_str = g_strdup(backing_file_str); s->on_error = on_error; @@ -377,16 +375,21 @@ void commit_start(const char *job_id, BlockDriverState *bs, return; fail: + if (s->chain_frozen) { + bdrv_unfreeze_backing_chain(commit_top_bs, base); + } if (s->base) { blk_unref(s->base); } if (s->top) { blk_unref(s->top); } + job_early_fail(&s->common.job); + /* commit_top_bs has to be replaced after deleting the block job, + * otherwise this would fail because of lack of permissions. */ if (commit_top_bs) { bdrv_replace_node(commit_top_bs, top, &error_abort); } - job_early_fail(&s->common.job); } @@ -400,7 +403,7 @@ int bdrv_commit(BlockDriverState *bs) BlockDriverState *commit_top_bs = NULL; BlockDriver *drv = bs->drv; int64_t offset, length, backing_length; - int ro, open_flags; + int ro; int64_t n; int ret = 0; uint8_t *buf = NULL; @@ -419,10 +422,9 @@ int bdrv_commit(BlockDriverState *bs) } ro = bs->backing->bs->read_only; - open_flags = bs->backing->bs->open_flags; if (ro) { - if (bdrv_reopen(bs->backing->bs, open_flags | BDRV_O_RDWR, NULL)) { + if (bdrv_reopen_set_read_only(bs->backing->bs, false, NULL)) { return -EACCES; } } @@ -532,7 +534,7 @@ ro_cleanup: if (ro) { /* ignoring error return here */ - bdrv_reopen(bs->backing->bs, open_flags & ~BDRV_O_RDWR, NULL); + bdrv_reopen_set_read_only(bs->backing->bs, true, NULL); } return ret; diff --git a/block/copy-on-read.c b/block/copy-on-read.c index a19164f9eb..53972b1da3 100644 --- a/block/copy-on-read.c +++ b/block/copy-on-read.c @@ -34,22 +34,16 @@ static int cor_open(BlockDriverState *bs, QDict *options, int flags, } bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | - (BDRV_REQ_FUA & - bs->file->bs->supported_write_flags); + (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | - ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & - bs->file->bs->supported_zero_flags); + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & + bs->file->bs->supported_zero_flags); return 0; } -static void cor_close(BlockDriverState *bs) -{ -} - - #define PERM_PASSTHROUGH (BLK_PERM_CONSISTENT_READ \ | BLK_PERM_WRITE \ | BLK_PERM_RESIZE) @@ -139,11 +133,10 @@ static bool cor_recurse_is_first_non_filter(BlockDriverState *bs, } -BlockDriver bdrv_copy_on_read = { +static BlockDriver bdrv_copy_on_read = { .format_name = "copy-on-read", .bdrv_open = cor_open, - .bdrv_close = cor_close, .bdrv_child_perm = cor_child_perm, .bdrv_getlength = cor_getlength, diff --git a/block/create.c b/block/create.c index 915cd41bcc..95341219ef 100644 --- a/block/create.c +++ b/block/create.c @@ -34,33 +34,26 @@ typedef struct BlockdevCreateJob { Job common; BlockDriver *drv; BlockdevCreateOptions *opts; - int ret; - Error *err; } BlockdevCreateJob; -static void blockdev_create_complete(Job *job, void *opaque) +static int coroutine_fn blockdev_create_run(Job *job, Error **errp) { BlockdevCreateJob *s = container_of(job, BlockdevCreateJob, common); - - job_completed(job, s->ret, s->err); -} - -static void coroutine_fn blockdev_create_run(void *opaque) -{ - BlockdevCreateJob *s = opaque; + int ret; job_progress_set_remaining(&s->common, 1); - s->ret = s->drv->bdrv_co_create(s->opts, &s->err); + ret = s->drv->bdrv_co_create(s->opts, errp); job_progress_update(&s->common, 1); qapi_free_BlockdevCreateOptions(s->opts); - job_defer_to_main_loop(&s->common, blockdev_create_complete, NULL); + + return ret; } static const JobDriver blockdev_create_job_driver = { .instance_size = sizeof(BlockdevCreateJob), .job_type = JOB_TYPE_CREATE, - .start = blockdev_create_run, + .run = blockdev_create_run, }; void qmp_blockdev_create(const char *job_id, BlockdevCreateOptions *options, diff --git a/block/crypto.c b/block/crypto.c index 146d81c90a..3af46b805f 100644 --- a/block/crypto.c +++ b/block/crypto.c @@ -229,6 +229,7 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, block_crypto_read_func, bs, cflags, + 1, errp); if (!crypto->block) { @@ -593,20 +594,17 @@ static int block_crypto_get_info_luks(BlockDriverState *bs, } static ImageInfoSpecific * -block_crypto_get_specific_info_luks(BlockDriverState *bs) +block_crypto_get_specific_info_luks(BlockDriverState *bs, Error **errp) { BlockCrypto *crypto = bs->opaque; ImageInfoSpecific *spec_info; QCryptoBlockInfo *info; - info = qcrypto_block_get_info(crypto->block, NULL); + info = qcrypto_block_get_info(crypto->block, errp); if (!info) { return NULL; } - if (info->format != Q_CRYPTO_BLOCK_FORMAT_LUKS) { - qapi_free_QCryptoBlockInfo(info); - return NULL; - } + assert(info->format == Q_CRYPTO_BLOCK_FORMAT_LUKS); spec_info = g_new(ImageInfoSpecific, 1); spec_info->type = IMAGE_INFO_SPECIFIC_KIND_LUKS; @@ -621,13 +619,21 @@ block_crypto_get_specific_info_luks(BlockDriverState *bs) return spec_info; } -BlockDriver bdrv_crypto_luks = { +static const char *const block_crypto_strong_runtime_opts[] = { + BLOCK_CRYPTO_OPT_LUKS_KEY_SECRET, + + NULL +}; + +static 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_child_perm = bdrv_format_default_perms, + /* This driver doesn't modify LUKS metadata except when creating image. + * Allow share-rw=on as a special case. */ + .bdrv_child_perm = bdrv_filter_default_perms, .bdrv_co_create = block_crypto_co_create_luks, .bdrv_co_create_opts = block_crypto_co_create_opts_luks, .bdrv_co_truncate = block_crypto_co_truncate, @@ -640,6 +646,8 @@ BlockDriver bdrv_crypto_luks = { .bdrv_getlength = block_crypto_getlength, .bdrv_get_info = block_crypto_get_info_luks, .bdrv_get_specific_info = block_crypto_get_specific_info_luks, + + .strong_runtime_opts = block_crypto_strong_runtime_opts, }; static void block_crypto_init(void) diff --git a/block/curl.c b/block/curl.c index 229bb84a27..606709fea4 100644 --- a/block/curl.c +++ b/block/curl.c @@ -32,22 +32,10 @@ #include "crypto/secret.h" #include #include "qemu/cutils.h" +#include "trace.h" -// #define DEBUG_CURL // #define DEBUG_VERBOSE -#ifdef DEBUG_CURL -#define DEBUG_CURL_PRINT 1 -#else -#define DEBUG_CURL_PRINT 0 -#endif -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_CURL_PRINT) { \ - fprintf(stderr, fmt, ## __VA_ARGS__); \ - } \ - } while (0) - #if LIBCURL_VERSION_NUM >= 0x071000 /* The multi interface timer callback was introduced in 7.16.0 */ #define NEED_CURL_TIMER_CALLBACK @@ -73,8 +61,6 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, #define CURL_NUM_STATES 8 #define CURL_NUM_ACB 8 -#define READ_AHEAD_DEFAULT (256 * 1024) -#define CURL_TIMEOUT_DEFAULT 5 #define CURL_TIMEOUT_MAX 10000 #define CURL_BLOCK_OPT_URL "url" @@ -88,6 +74,10 @@ static CURLMcode __curl_multi_socket_action(CURLM *multi_handle, #define CURL_BLOCK_OPT_PROXY_USERNAME "proxy-username" #define CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET "proxy-password-secret" +#define CURL_BLOCK_OPT_READAHEAD_DEFAULT (256 * 1024) +#define CURL_BLOCK_OPT_SSLVERIFY_DEFAULT true +#define CURL_BLOCK_OPT_TIMEOUT_DEFAULT 5 + struct BDRVCURLState; static bool libcurl_initialized; @@ -154,7 +144,7 @@ static int curl_timer_cb(CURLM *multi, long timeout_ms, void *opaque) { BDRVCURLState *s = opaque; - DPRINTF("CURL: timer callback timeout_ms %ld\n", timeout_ms); + trace_curl_timer_cb(timeout_ms); if (timeout_ms == -1) { timer_del(&s->timer); } else { @@ -193,7 +183,7 @@ static int curl_sock_cb(CURL *curl, curl_socket_t fd, int action, } socket = NULL; - DPRINTF("CURL (AIO): Sock action %d on fd %d\n", action, (int)fd); + trace_curl_sock_cb(action, (int)fd); switch (action) { case CURL_POLL_IN: aio_set_fd_handler(s->aio_context, fd, false, @@ -238,7 +228,7 @@ static size_t curl_read_cb(void *ptr, size_t size, size_t nmemb, void *opaque) size_t realsize = size * nmemb; int i; - DPRINTF("CURL: Just reading %zd bytes\n", realsize); + trace_curl_read_cb(realsize); if (!s || !s->orig_buf) { goto read_end; @@ -483,6 +473,8 @@ static int curl_init_state(BDRVCURLState *s, CURLState *state) curl_easy_setopt(state->curl, CURLOPT_URL, s->url); curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYPEER, (long) s->sslverify); + curl_easy_setopt(state->curl, CURLOPT_SSL_VERIFYHOST, + s->sslverify ? 2L : 0L); if (s->cookie) { curl_easy_setopt(state->curl, CURLOPT_COOKIE, s->cookie); } @@ -682,10 +674,10 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, const char *protocol_delimiter; int ret; - - if (flags & BDRV_O_RDWR) { - error_setg(errp, "curl block device does not support writes"); - return -EROFS; + ret = bdrv_apply_auto_read_only(bs, "curl driver does not support writes", + errp); + if (ret < 0) { + return ret; } if (!libcurl_initialized) { @@ -706,7 +698,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } s->readahead_size = qemu_opt_get_size(opts, CURL_BLOCK_OPT_READAHEAD, - READ_AHEAD_DEFAULT); + CURL_BLOCK_OPT_READAHEAD_DEFAULT); if ((s->readahead_size & 0x1ff) != 0) { error_setg(errp, "HTTP_READAHEAD_SIZE %zd is not a multiple of 512", s->readahead_size); @@ -714,13 +706,14 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } s->timeout = qemu_opt_get_number(opts, CURL_BLOCK_OPT_TIMEOUT, - CURL_TIMEOUT_DEFAULT); + CURL_BLOCK_OPT_TIMEOUT_DEFAULT); if (s->timeout > CURL_TIMEOUT_MAX) { error_setg(errp, "timeout parameter is too large or negative"); goto out_noclean; } - s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, true); + s->sslverify = qemu_opt_get_bool(opts, CURL_BLOCK_OPT_SSLVERIFY, + CURL_BLOCK_OPT_SSLVERIFY_DEFAULT); cookie = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE); cookie_secret = qemu_opt_get(opts, CURL_BLOCK_OPT_COOKIE_SECRET); @@ -775,7 +768,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, } } - DPRINTF("CURL: Opening %s\n", file); + trace_curl_open(file); qemu_co_queue_init(&s->free_state_waitq); s->aio_context = bdrv_get_aio_context(bs); s->url = g_strdup(file); @@ -828,7 +821,7 @@ static int curl_open(BlockDriverState *bs, QDict *options, int flags, "Server does not support 'range' (byte ranges)."); goto out; } - DPRINTF("CURL: Size = %" PRIu64 "\n", s->len); + trace_curl_open_size(s->len); qemu_mutex_lock(&s->mutex); curl_clean_state(state); @@ -906,8 +899,7 @@ static void curl_setup_preadv(BlockDriverState *bs, CURLAIOCB *acb) state->acb[0] = acb; snprintf(state->range, 127, "%" PRIu64 "-%" PRIu64, start, end); - DPRINTF("CURL (AIO): Reading %" PRIu64 " at %" PRIu64 " (%s)\n", - acb->bytes, start, state->range); + trace_curl_setup_preadv(acb->bytes, start, state->range); curl_easy_setopt(state->curl, CURLOPT_RANGE, state->range); curl_multi_add_handle(s->multi, state->curl); @@ -941,7 +933,7 @@ static void curl_close(BlockDriverState *bs) { BDRVCURLState *s = bs->opaque; - DPRINTF("CURL: Close\n"); + trace_curl_close(); curl_detach_aio_context(bs); qemu_mutex_destroy(&s->mutex); @@ -958,6 +950,36 @@ static int64_t curl_getlength(BlockDriverState *bs) return s->len; } +static void curl_refresh_filename(BlockDriverState *bs) +{ + BDRVCURLState *s = bs->opaque; + + /* "readahead" and "timeout" do not change the guest-visible data, + * so ignore them */ + if (s->sslverify != CURL_BLOCK_OPT_SSLVERIFY_DEFAULT || + s->cookie || s->username || s->password || s->proxyusername || + s->proxypassword) + { + return; + } + + pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), s->url); +} + + +static const char *const curl_strong_runtime_opts[] = { + CURL_BLOCK_OPT_URL, + CURL_BLOCK_OPT_SSLVERIFY, + CURL_BLOCK_OPT_COOKIE, + CURL_BLOCK_OPT_COOKIE_SECRET, + CURL_BLOCK_OPT_USERNAME, + CURL_BLOCK_OPT_PASSWORD_SECRET, + CURL_BLOCK_OPT_PROXY_USERNAME, + CURL_BLOCK_OPT_PROXY_PASSWORD_SECRET, + + NULL +}; + static BlockDriver bdrv_http = { .format_name = "http", .protocol_name = "http", @@ -972,6 +994,9 @@ static BlockDriver bdrv_http = { .bdrv_detach_aio_context = curl_detach_aio_context, .bdrv_attach_aio_context = curl_attach_aio_context, + + .bdrv_refresh_filename = curl_refresh_filename, + .strong_runtime_opts = curl_strong_runtime_opts, }; static BlockDriver bdrv_https = { @@ -988,6 +1013,9 @@ static BlockDriver bdrv_https = { .bdrv_detach_aio_context = curl_detach_aio_context, .bdrv_attach_aio_context = curl_attach_aio_context, + + .bdrv_refresh_filename = curl_refresh_filename, + .strong_runtime_opts = curl_strong_runtime_opts, }; static BlockDriver bdrv_ftp = { @@ -1004,6 +1032,9 @@ static BlockDriver bdrv_ftp = { .bdrv_detach_aio_context = curl_detach_aio_context, .bdrv_attach_aio_context = curl_attach_aio_context, + + .bdrv_refresh_filename = curl_refresh_filename, + .strong_runtime_opts = curl_strong_runtime_opts, }; static BlockDriver bdrv_ftps = { @@ -1020,6 +1051,9 @@ static BlockDriver bdrv_ftps = { .bdrv_detach_aio_context = curl_detach_aio_context, .bdrv_attach_aio_context = curl_attach_aio_context, + + .bdrv_refresh_filename = curl_refresh_filename, + .strong_runtime_opts = curl_strong_runtime_opts, }; static void curl_block_init(void) diff --git a/block/dirty-bitmap.c b/block/dirty-bitmap.c index c9b8a6fd52..59e6ebb861 100644 --- a/block/dirty-bitmap.c +++ b/block/dirty-bitmap.c @@ -28,21 +28,12 @@ #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 { QemuMutex *mutex; HBitmap *bitmap; /* Dirty bitmap implementation */ HBitmap *meta; /* Meta dirty bitmap */ - bool qmp_locked; /* Bitmap is locked, it can't be modified - through QMP */ - BdrvDirtyBitmap *successor; /* Anonymous child; implies frozen status */ + bool busy; /* Bitmap is busy, it can't be used via QMP */ + BdrvDirtyBitmap *successor; /* Anonymous child, if any. */ char *name; /* Optional non-empty unique ID */ int64_t size; /* Size of the bitmap, in bytes */ bool disabled; /* Bitmap is disabled. It ignores all writes to @@ -55,6 +46,13 @@ struct BdrvDirtyBitmap { and this bitmap must remain unchanged while this flag is set. */ bool persistent; /* bitmap must be saved to owner disk image */ + bool inconsistent; /* bitmap is persistent, but inconsistent. + It cannot be used at all in any way, except + a QMP user can remove it. */ + bool migration; /* Bitmap is selected for migration, it should + not be stored on the next inactivation + (persistent flag doesn't matter until next + invalidation).*/ QLIST_ENTRY(BdrvDirtyBitmap) list; }; @@ -171,35 +169,58 @@ const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap) } /* Called with BQL taken. */ -bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap) +bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap) { return bitmap->successor; } -void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked) +static bool bdrv_dirty_bitmap_busy(const BdrvDirtyBitmap *bitmap) { - qemu_mutex_lock(bitmap->mutex); - bitmap->qmp_locked = qmp_locked; - qemu_mutex_unlock(bitmap->mutex); + return bitmap->busy; } -bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap) +void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy) { - return bitmap->qmp_locked; + qemu_mutex_lock(bitmap->mutex); + bitmap->busy = busy; + qemu_mutex_unlock(bitmap->mutex); } /* Called with BQL taken. */ bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap) { - return !(bitmap->disabled || bitmap->successor); + return !bitmap->disabled; } -/* Called with BQL taken. */ +/** + * bdrv_dirty_bitmap_status: This API is now deprecated. + * Called with BQL taken. + * + * A BdrvDirtyBitmap can be in four possible user-visible states: + * (1) Active: successor is NULL, and disabled is false: full r/w mode + * (2) Disabled: successor is NULL, and disabled is true: qualified r/w mode, + * guest writes are dropped, but monitor writes are possible, + * through commands like merge and clear. + * (3) Frozen: successor is not NULL. + * A frozen bitmap cannot be renamed, deleted, cleared, set, + * enabled, merged to, etc. A frozen bitmap can only abdicate() + * or reclaim(). + * In this state, the anonymous successor bitmap may be either + * Active and recording writes from the guest (e.g. backup jobs), + * or it can be Disabled and not recording writes. + * (4) Locked: Whether Active or Disabled, the user cannot modify this bitmap + * in any way from the monitor. + * (5) Inconsistent: This is a persistent bitmap whose "in use" bit is set, and + * is unusable by QEMU. It can be deleted to remove it from + * the qcow2. + */ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) { - if (bdrv_dirty_bitmap_frozen(bitmap)) { + if (bdrv_dirty_bitmap_inconsistent(bitmap)) { + return DIRTY_BITMAP_STATUS_INCONSISTENT; + } else if (bdrv_dirty_bitmap_has_successor(bitmap)) { return DIRTY_BITMAP_STATUS_FROZEN; - } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { + } else if (bdrv_dirty_bitmap_busy(bitmap)) { return DIRTY_BITMAP_STATUS_LOCKED; } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { return DIRTY_BITMAP_STATUS_DISABLED; @@ -208,9 +229,44 @@ DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap) } } +/* Called with BQL taken. */ +static bool bdrv_dirty_bitmap_recording(BdrvDirtyBitmap *bitmap) +{ + return !bitmap->disabled || (bitmap->successor && + !bitmap->successor->disabled); +} + +int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, + Error **errp) +{ + if ((flags & BDRV_BITMAP_BUSY) && bdrv_dirty_bitmap_busy(bitmap)) { + error_setg(errp, "Bitmap '%s' is currently in use by another" + " operation and cannot be used", bitmap->name); + return -1; + } + + if ((flags & BDRV_BITMAP_RO) && bdrv_dirty_bitmap_readonly(bitmap)) { + error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", + bitmap->name); + return -1; + } + + if ((flags & BDRV_BITMAP_INCONSISTENT) && + bdrv_dirty_bitmap_inconsistent(bitmap)) { + error_setg(errp, "Bitmap '%s' is inconsistent and cannot be used", + bitmap->name); + error_append_hint(errp, "Try block-dirty-bitmap-remove to delete" + " this bitmap from disk"); + return -1; + } + + return 0; +} + /** * Create a successor bitmap destined to replace this bitmap after an operation. - * Requires that the bitmap is not frozen and has no successor. + * Requires that the bitmap is not marked busy and has no successor. + * The successor will be enabled if the parent bitmap was. * Called with BQL taken. */ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, @@ -219,12 +275,14 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, 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"); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY, errp)) { + return -1; + } + if (bdrv_dirty_bitmap_has_successor(bitmap)) { + error_setg(errp, "Cannot create a successor for a bitmap that already " + "has one"); return -1; } - assert(!bitmap->successor); /* Create an anonymous successor */ granularity = bdrv_dirty_bitmap_granularity(bitmap); @@ -235,15 +293,16 @@ int bdrv_dirty_bitmap_create_successor(BlockDriverState *bs, /* Successor will be on or off based on our current state. */ child->disabled = bitmap->disabled; + bitmap->disabled = true; - /* Install the successor and freeze the parent */ + /* Install the successor and mark the parent as busy */ bitmap->successor = child; + bitmap->busy = true; return 0; } void bdrv_enable_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) { - assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = false; } @@ -260,7 +319,8 @@ void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap) static void bdrv_release_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap) { assert(!bitmap->active_iterators); - assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bdrv_dirty_bitmap_busy(bitmap)); + assert(!bdrv_dirty_bitmap_has_successor(bitmap)); assert(!bitmap->meta); QLIST_REMOVE(bitmap, list); hbitmap_free(bitmap->bitmap); @@ -292,6 +352,7 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, bitmap->successor = NULL; successor->persistent = bitmap->persistent; bitmap->persistent = false; + bitmap->busy = false; bdrv_release_dirty_bitmap(bs, bitmap); return successor; @@ -300,7 +361,8 @@ BdrvDirtyBitmap *bdrv_dirty_bitmap_abdicate(BlockDriverState *bs, /** * In cases of failure where we can no longer safely delete the parent, * we may wish to re-join the parent and child/successor. - * The merged parent will be un-frozen, but not explicitly re-enabled. + * The merged parent will be marked as not busy. + * The marged parent will be enabled if and only if the successor was enabled. * Called within bdrv_dirty_bitmap_lock..unlock and with BQL taken. */ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, @@ -314,10 +376,13 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, return NULL; } - if (!hbitmap_merge(parent->bitmap, successor->bitmap)) { + if (!hbitmap_merge(parent->bitmap, successor->bitmap, parent->bitmap)) { error_setg(errp, "Merging of parent and successor bitmap failed"); return NULL; } + + parent->disabled = successor->disabled; + parent->busy = false; bdrv_release_dirty_bitmap_locked(successor); parent->successor = NULL; @@ -348,7 +413,8 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes) bdrv_dirty_bitmaps_lock(bs); QLIST_FOREACH(bitmap, &bs->dirty_bitmaps, list) { - assert(!bdrv_dirty_bitmap_frozen(bitmap)); + assert(!bdrv_dirty_bitmap_busy(bitmap)); + assert(!bdrv_dirty_bitmap_has_successor(bitmap)); assert(!bitmap->active_iterators); hbitmap_truncate(bitmap->bitmap, bytes); bitmap->size = bytes; @@ -366,7 +432,7 @@ void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap) /** * Release all named dirty bitmaps attached to a BDS (for use in bdrv_close()). - * There must not be any frozen bitmaps attached. + * There must not be any busy bitmaps attached. * This function does not remove persistent bitmaps from the storage. * Called with BQL taken. */ @@ -383,26 +449,6 @@ void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs) bdrv_dirty_bitmaps_unlock(bs); } -/** - * Release all persistent dirty bitmaps attached to a BDS (for use in - * bdrv_inactivate_recurse()). - * There must not be any frozen bitmaps attached. - * This function does not remove persistent bitmaps from the storage. - * Called with BQL taken. - */ -void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs) -{ - BdrvDirtyBitmap *bm, *next; - - bdrv_dirty_bitmaps_lock(bs); - QLIST_FOREACH_SAFE(bm, &bs->dirty_bitmaps, list, next) { - if (bdrv_dirty_bitmap_get_persistance(bm)) { - bdrv_release_dirty_bitmap_locked(bm); - } - } - bdrv_dirty_bitmaps_unlock(bs); -} - /** * Remove persistent dirty bitmap from the storage if it exists. * Absence of bitmap is not an error, because we have the following scenario: @@ -423,7 +469,6 @@ void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, void bdrv_disable_dirty_bitmap(BdrvDirtyBitmap *bitmap) { bdrv_dirty_bitmap_lock(bitmap); - assert(!bdrv_dirty_bitmap_frozen(bitmap)); bitmap->disabled = true; bdrv_dirty_bitmap_unlock(bitmap); } @@ -450,6 +495,11 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs) info->has_name = !!bm->name; info->name = g_strdup(bm->name); info->status = bdrv_dirty_bitmap_status(bm); + info->recording = bdrv_dirty_bitmap_recording(bm); + info->busy = bdrv_dirty_bitmap_busy(bm); + info->persistent = bm->persistent; + info->has_inconsistent = bm->inconsistent; + info->inconsistent = bm->inconsistent; entry->value = info; *plist = entry; plist = &entry->next; @@ -525,69 +575,13 @@ void bdrv_dirty_iter_free(BdrvDirtyBitmapIter *iter) int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter) { - return hbitmap_iter_next(&iter->hbi, true); -} - -/** - * Return the next consecutively dirty area in the dirty bitmap - * belonging to the given iterator @iter. - * - * @max_offset: Maximum value that may be returned for - * *offset + *bytes - * @offset: Will contain the start offset of the next dirty area - * @bytes: Will contain the length of the next dirty area - * - * Returns: True if a dirty area could be found before max_offset - * (which means that *offset and *bytes then contain valid - * values), false otherwise. - * - * Note that @iter is never advanced if false is returned. If an area - * is found (which means that true is returned), it will be advanced - * past that area. - */ -bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, - uint64_t *offset, int *bytes) -{ - uint32_t granularity = bdrv_dirty_bitmap_granularity(iter->bitmap); - uint64_t gran_max_offset; - int64_t ret; - int size; - - if (max_offset == iter->bitmap->size) { - /* If max_offset points to the image end, round it up by the - * bitmap granularity */ - gran_max_offset = ROUND_UP(max_offset, granularity); - } else { - gran_max_offset = max_offset; - } - - ret = hbitmap_iter_next(&iter->hbi, false); - if (ret < 0 || ret + granularity > gran_max_offset) { - return false; - } - - *offset = ret; - size = 0; - - assert(granularity <= INT_MAX); - - do { - /* Advance iterator */ - ret = hbitmap_iter_next(&iter->hbi, true); - size += granularity; - } while (ret + granularity <= gran_max_offset && - hbitmap_iter_next(&iter->hbi, false) == ret + granularity && - size <= INT_MAX - granularity); - - *bytes = MIN(size, max_offset - *offset); - return true; + return hbitmap_iter_next(&iter->hbi); } /* Called within bdrv_dirty_bitmap_lock..unlock */ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes) { - assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); hbitmap_set(bitmap->bitmap, offset, bytes); } @@ -604,7 +598,6 @@ void bdrv_set_dirty_bitmap(BdrvDirtyBitmap *bitmap, void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes) { - assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); hbitmap_reset(bitmap->bitmap, offset, bytes); } @@ -619,7 +612,6 @@ void bdrv_reset_dirty_bitmap(BdrvDirtyBitmap *bitmap, void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) { - assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); bdrv_dirty_bitmap_lock(bitmap); if (!out) { @@ -633,12 +625,11 @@ void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out) bdrv_dirty_bitmap_unlock(bitmap); } -void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in) +void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup) { HBitmap *tmp = bitmap->bitmap; - assert(bdrv_dirty_bitmap_enabled(bitmap)); assert(!bdrv_dirty_bitmap_readonly(bitmap)); - bitmap->bitmap = in; + bitmap->bitmap = backup; hbitmap_free(tmp); } @@ -749,23 +740,46 @@ bool bdrv_has_readonly_bitmaps(BlockDriverState *bs) } /* Called with BQL taken. */ -void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, bool persistent) +void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent) { qemu_mutex_lock(bitmap->mutex); bitmap->persistent = persistent; qemu_mutex_unlock(bitmap->mutex); } -bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap) +/* Called with BQL taken. */ +void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap) { - return bitmap->persistent; + qemu_mutex_lock(bitmap->mutex); + assert(bitmap->persistent == true); + bitmap->inconsistent = true; + bitmap->disabled = true; + qemu_mutex_unlock(bitmap->mutex); +} + +/* Called with BQL taken. */ +void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration) +{ + qemu_mutex_lock(bitmap->mutex); + bitmap->migration = migration; + qemu_mutex_unlock(bitmap->mutex); +} + +bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap) +{ + return bitmap->persistent && !bitmap->migration; +} + +bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap) +{ + return bitmap->inconsistent; } bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs) { BdrvDirtyBitmap *bm; QLIST_FOREACH(bm, &bs->dirty_bitmaps, list) { - if (bm->persistent && !bm->readonly) { + if (bm->persistent && !bm->readonly && !bm->migration) { return true; } } @@ -785,25 +799,50 @@ char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp) return hbitmap_sha256(bitmap->bitmap, errp); } -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset) +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, + uint64_t bytes) { - return hbitmap_next_zero(bitmap->bitmap, offset); + return hbitmap_next_zero(bitmap->bitmap, offset, bytes); +} + +bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, + uint64_t *offset, uint64_t *bytes) +{ + return hbitmap_next_dirty_area(bitmap->bitmap, offset, bytes); } void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, - Error **errp) + HBitmap **backup, Error **errp) { + bool ret; + /* only bitmaps from one bds are supported */ assert(dest->mutex == src->mutex); qemu_mutex_lock(dest->mutex); - assert(bdrv_dirty_bitmap_enabled(dest)); - assert(!bdrv_dirty_bitmap_readonly(dest)); - - if (!hbitmap_merge(dest->bitmap, src->bitmap)) { - error_setg(errp, "Bitmaps are incompatible and can't be merged"); + if (bdrv_dirty_bitmap_check(dest, BDRV_BITMAP_DEFAULT, errp)) { + goto out; } + if (bdrv_dirty_bitmap_check(src, BDRV_BITMAP_ALLOW_RO, errp)) { + goto out; + } + + if (!hbitmap_can_merge(dest->bitmap, src->bitmap)) { + error_setg(errp, "Bitmaps are incompatible and can't be merged"); + goto out; + } + + if (backup) { + *backup = dest->bitmap; + dest->bitmap = hbitmap_alloc(dest->size, hbitmap_granularity(*backup)); + ret = hbitmap_merge(*backup, src->bitmap, dest->bitmap); + } else { + ret = hbitmap_merge(dest->bitmap, src->bitmap, dest->bitmap); + } + assert(ret); + +out: qemu_mutex_unlock(dest->mutex); } diff --git a/block/dmg-lzfse.c b/block/dmg-lzfse.c new file mode 100644 index 0000000000..19d25bc646 --- /dev/null +++ b/block/dmg-lzfse.c @@ -0,0 +1,49 @@ +/* + * DMG lzfse uncompression + * + * Copyright (c) 2018 Julio Cesar Faracco + * + * 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 "dmg.h" +#include + +static int dmg_uncompress_lzfse_do(char *next_in, unsigned int avail_in, + char *next_out, unsigned int avail_out) +{ + size_t out_size = lzfse_decode_buffer((uint8_t *) next_out, avail_out, + (uint8_t *) next_in, avail_in, + NULL); + + /* We need to decode the single chunk only. */ + /* So, out_size == avail_out is not an error here. */ + if (out_size > 0) { + return out_size; + } + return -1; +} + +__attribute__((constructor)) +static void dmg_lzfse_init(void) +{ + assert(!dmg_uncompress_lzfse); + dmg_uncompress_lzfse = dmg_uncompress_lzfse_do; +} diff --git a/block/dmg.c b/block/dmg.c index c9b3c519c4..43497bf343 100644 --- a/block/dmg.c +++ b/block/dmg.c @@ -33,6 +33,9 @@ int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, char *next_out, unsigned int avail_out); +int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, + char *next_out, unsigned int avail_out); + enum { /* Limit chunk sizes to prevent unreasonable amounts of memory being used * or truncating when converting to 32-bit types @@ -41,6 +44,19 @@ enum { DMG_SECTORCOUNTS_MAX = DMG_LENGTHS_MAX / 512, }; +enum { + /* DMG Block Type */ + UDZE = 0, /* Zeroes */ + UDRW, /* RAW type */ + UDIG, /* Ignore */ + UDCO = 0x80000004, + UDZO, + UDBZ, + ULFO, + UDCM = 0x7ffffffe, /* Comments */ + UDLE = 0xffffffff /* Last Entry */ +}; + static int dmg_probe(const uint8_t *buf, int buf_size, const char *filename) { int len; @@ -105,15 +121,17 @@ static void update_max_chunk_size(BDRVDMGState *s, uint32_t chunk, uint32_t uncompressed_sectors = 0; switch (s->types[chunk]) { - case 0x80000005: /* zlib compressed */ - case 0x80000006: /* bzip2 compressed */ + case UDZO: /* zlib compressed */ + case UDBZ: /* bzip2 compressed */ + case ULFO: /* lzfse compressed */ compressed_size = s->lengths[chunk]; uncompressed_sectors = s->sectorcounts[chunk]; break; - case 1: /* copy */ + case UDRW: /* copy */ uncompressed_sectors = DIV_ROUND_UP(s->lengths[chunk], 512); break; - case 2: /* zero */ + case UDZE: /* zero */ + case UDIG: /* ignore */ /* as the all-zeroes block may be large, it is treated specially: the * sector is not copied from a large buffer, a simple memset is used * instead. Therefore uncompressed_sectors does not need to be set. */ @@ -182,12 +200,15 @@ typedef struct DmgHeaderState { static bool dmg_is_known_block_type(uint32_t entry_type) { switch (entry_type) { - case 0x00000001: /* uncompressed */ - case 0x00000002: /* zeroes */ - case 0x80000005: /* zlib */ + case UDZE: /* zeros */ + case UDRW: /* uncompressed */ + case UDIG: /* ignore */ + case UDZO: /* zlib */ return true; - case 0x80000006: /* bzip2 */ + case UDBZ: /* bzip2 */ return !!dmg_uncompress_bz2; + case ULFO: /* lzfse */ + return !!dmg_uncompress_lzfse; default: return false; } @@ -246,9 +267,10 @@ static int dmg_read_mish_block(BDRVDMGState *s, DmgHeaderState *ds, /* sector count */ s->sectorcounts[i] = buff_read_uint64(buffer, offset + 0x10); - /* all-zeroes sector (type 2) does not need to be "uncompressed" and can - * therefore be unbounded. */ - if (s->types[i] != 2 && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { + /* all-zeroes sector (type UDZE and UDIG) does not need to be + * "uncompressed" and can therefore be unbounded. */ + if (s->types[i] != UDZE && s->types[i] != UDIG + && s->sectorcounts[i] > DMG_SECTORCOUNTS_MAX) { error_report("sector count %" PRIu64 " for chunk %" PRIu32 " is larger than max (%u)", s->sectorcounts[i], i, DMG_SECTORCOUNTS_MAX); @@ -413,24 +435,19 @@ static int dmg_open(BlockDriverState *bs, QDict *options, int flags, int64_t offset; int ret; + ret = bdrv_apply_auto_read_only(bs, NULL, errp); + if (ret < 0) { + return ret; + } + bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); if (!bs->file) { return -EINVAL; } - if (!bdrv_is_read_only(bs)) { - error_report("Opening dmg images without an explicit read-only=on " - "option is deprecated. Future versions will refuse to " - "open the image instead of automatically marking the " - "image read-only."); - ret = bdrv_set_read_only(bs, true, errp); - if (ret < 0) { - return ret; - } - } - block_module_load_one("dmg-bz2"); + block_module_load_one("dmg-lzfse"); s->n_chunks = 0; s->offsets = s->lengths = s->sectors = s->sectorcounts = NULL; @@ -558,16 +575,20 @@ static inline uint32_t search_chunk(BDRVDMGState *s, uint64_t sector_num) { /* binary search */ uint32_t chunk1 = 0, chunk2 = s->n_chunks, chunk3; - while (chunk1 != chunk2) { + while (chunk1 <= chunk2) { chunk3 = (chunk1 + chunk2) / 2; if (s->sectors[chunk3] > sector_num) { - chunk2 = chunk3; + if (chunk3 == 0) { + goto err; + } + chunk2 = chunk3 - 1; } else if (s->sectors[chunk3] + s->sectorcounts[chunk3] > sector_num) { return chunk3; } else { - chunk1 = chunk3; + chunk1 = chunk3 + 1; } } +err: return s->n_chunks; /* error */ } @@ -585,7 +606,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) s->current_chunk = s->n_chunks; switch (s->types[chunk]) { /* block entry type */ - case 0x80000005: { /* zlib compressed */ + case UDZO: { /* zlib compressed */ /* we need to buffer, because only the chunk as whole can be * inflated. */ ret = bdrv_pread(bs->file, s->offsets[chunk], @@ -608,7 +629,7 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) return -1; } break; } - case 0x80000006: /* bzip2 compressed */ + case UDBZ: /* bzip2 compressed */ if (!dmg_uncompress_bz2) { break; } @@ -629,14 +650,36 @@ static inline int dmg_read_chunk(BlockDriverState *bs, uint64_t sector_num) return ret; } break; - case 1: /* copy */ + case ULFO: + if (!dmg_uncompress_lzfse) { + break; + } + /* we need to buffer, because only the chunk as whole can be + * inflated. */ + ret = bdrv_pread(bs->file, s->offsets[chunk], + s->compressed_chunk, s->lengths[chunk]); + if (ret != s->lengths[chunk]) { + return -1; + } + + ret = dmg_uncompress_lzfse((char *)s->compressed_chunk, + (unsigned int) s->lengths[chunk], + (char *)s->uncompressed_chunk, + (unsigned int) + (512 * s->sectorcounts[chunk])); + if (ret < 0) { + return ret; + } + break; + case UDRW: /* copy */ ret = bdrv_pread(bs->file, s->offsets[chunk], s->uncompressed_chunk, s->lengths[chunk]); if (ret != s->lengths[chunk]) { return -1; } break; - case 2: /* zero */ + case UDZE: /* zeros */ + case UDIG: /* ignore */ /* see dmg_read, it is treated specially. No buffer needs to be * pre-filled, the zeroes can be set directly. */ break; @@ -671,7 +714,8 @@ dmg_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, /* Special case: current chunk is all zeroes. Do not perform a memcpy as * s->uncompressed_chunk may be too small to cover the large all-zeroes * section. dmg_read_chunk is called to find s->current_chunk */ - if (s->types[s->current_chunk] == 2) { /* all zeroes block entry */ + if (s->types[s->current_chunk] == UDZE + || s->types[s->current_chunk] == UDIG) { /* all zeroes block entry */ qemu_iovec_memset(qiov, i * 512, 0, 512); continue; } diff --git a/block/dmg.h b/block/dmg.h index 2ecf239ba5..f28929998f 100644 --- a/block/dmg.h +++ b/block/dmg.h @@ -55,4 +55,7 @@ typedef struct BDRVDMGState { extern int (*dmg_uncompress_bz2)(char *next_in, unsigned int avail_in, char *next_out, unsigned int avail_out); +extern int (*dmg_uncompress_lzfse)(char *next_in, unsigned int avail_in, + char *next_out, unsigned int avail_out); + #endif diff --git a/block/file-posix.c b/block/file-posix.c index fe83cbf0eb..1cf4ee49eb 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -102,19 +102,7 @@ #include #endif -//#define DEBUG_BLOCK - -#ifdef DEBUG_BLOCK -# define DEBUG_BLOCK_PRINT 1 -#else -# define DEBUG_BLOCK_PRINT 0 -#endif -#define DPRINTF(fmt, ...) \ -do { \ - if (DEBUG_BLOCK_PRINT) { \ - printf(fmt, ## __VA_ARGS__); \ - } \ -} while (0) +#include "trace.h" /* OS X does not have O_DSYNC */ #ifndef O_DSYNC @@ -142,7 +130,6 @@ do { \ typedef struct BDRVRawState { int fd; - int lock_fd; bool use_lock; int type; int open_flags; @@ -152,6 +139,14 @@ typedef struct BDRVRawState { uint64_t perm; uint64_t shared_perm; + /* The perms bits whose corresponding bytes are already locked in + * s->fd. */ + uint64_t locked_perm; + uint64_t locked_shared_perm; + + int perm_change_fd; + BDRVReopenState *reopen_state; + #ifdef CONFIG_XFS bool is_xfs:1; #endif @@ -162,6 +157,7 @@ typedef struct BDRVRawState { bool page_cache_inconsistent:1; bool has_fallocate; bool needs_alignment; + bool drop_cache; bool check_cache_dropped; PRManager *pr_mgr; @@ -170,6 +166,7 @@ typedef struct BDRVRawState { typedef struct BDRVRawReopenState { int fd; int open_flags; + bool drop_cache; bool check_cache_dropped; } BDRVRawReopenState; @@ -178,25 +175,29 @@ static int64_t raw_getlength(BlockDriverState *bs); typedef struct RawPosixAIOData { BlockDriverState *bs; - int aio_fildes; - union { - struct iovec *aio_iov; - void *aio_ioctl_buf; - }; - int aio_niov; - uint64_t aio_nbytes; -#define aio_ioctl_cmd aio_nbytes /* for QEMU_AIO_IOCTL */ - off_t aio_offset; int aio_type; + int aio_fildes; + + off_t aio_offset; + uint64_t aio_nbytes; + union { + struct { + struct iovec *iov; + int niov; + } io; + struct { + uint64_t cmd; + void *buf; + } ioctl; struct { int aio_fd2; off_t aio_offset2; - }; + } copy_range; struct { PreallocMode prealloc; Error **errp; - }; + } truncate; }; } RawPosixAIOData; @@ -205,7 +206,7 @@ static int cdrom_reopen(BlockDriverState *bs); #endif #if defined(__NetBSD__) -static int raw_normalize_devicepath(const char **filename) +static int raw_normalize_devicepath(const char **filename, Error **errp) { static char namebuf[PATH_MAX]; const char *dp, *fname; @@ -214,8 +215,7 @@ static int raw_normalize_devicepath(const char **filename) fname = *filename; dp = strrchr(fname, '/'); if (lstat(fname, &sb) < 0) { - fprintf(stderr, "%s: stat failed: %s\n", - fname, strerror(errno)); + error_setg_errno(errp, errno, "%s: stat failed", fname); return -errno; } @@ -229,14 +229,13 @@ static int raw_normalize_devicepath(const char **filename) snprintf(namebuf, PATH_MAX, "%.*s/r%s", (int)(dp - fname), fname, dp + 1); } - fprintf(stderr, "%s is a block device", fname); *filename = namebuf; - fprintf(stderr, ", using %s\n", *filename); + warn_report("%s is a block device, using %s", fname, *filename); return 0; } #else -static int raw_normalize_devicepath(const char **filename) +static int raw_normalize_devicepath(const char **filename, Error **errp) { return 0; } @@ -379,13 +378,21 @@ static void raw_probe_alignment(BlockDriverState *bs, int fd, Error **errp) } } -static void raw_parse_flags(int bdrv_flags, int *open_flags) +static void raw_parse_flags(int bdrv_flags, int *open_flags, bool has_writers) { + bool read_write = false; assert(open_flags != NULL); *open_flags |= O_BINARY; *open_flags &= ~O_ACCMODE; - if (bdrv_flags & BDRV_O_RDWR) { + + if (bdrv_flags & BDRV_O_AUTO_RDONLY) { + read_write = has_writers; + } else if (bdrv_flags & BDRV_O_RDWR) { + read_write = true; + } + + if (read_write) { *open_flags |= O_RDWR; } else { *open_flags |= O_RDONLY; @@ -428,6 +435,13 @@ static QemuOptsList raw_runtime_opts = { .type = QEMU_OPT_STRING, .help = "id of persistent reservation manager object (default: none)", }, +#if defined(__linux__) + { + .name = "drop-cache", + .type = QEMU_OPT_BOOL, + .help = "invalidate page cache during live migration (default: on)", + }, +#endif { .name = "x-check-cache-dropped", .type = QEMU_OPT_BOOL, @@ -437,6 +451,8 @@ static QemuOptsList raw_runtime_opts = { }, }; +static const char *const mutable_opts[] = { "x-check-cache-dropped", NULL }; + static int raw_open_common(BlockDriverState *bs, QDict *options, int bdrv_flags, int open_flags, bool device, Error **errp) @@ -461,9 +477,8 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, filename = qemu_opt_get(opts, "filename"); - ret = raw_normalize_devicepath(&filename); + ret = raw_normalize_devicepath(&filename, errp); if (ret != 0) { - error_setg_errno(errp, -ret, "Could not normalize device path"); goto fail; } @@ -492,11 +507,10 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, case ON_OFF_AUTO_ON: s->use_lock = true; if (!qemu_has_ofd_lock()) { - fprintf(stderr, - "File lock requested but OFD locking syscall is " - "unavailable, falling back to POSIX file locks.\n" - "Due to the implementation, locks can be lost " - "unexpectedly.\n"); + warn_report("File lock requested but OFD locking syscall is " + "unavailable, falling back to POSIX file locks"); + error_printf("Due to the implementation, locks can be lost " + "unexpectedly.\n"); } break; case ON_OFF_AUTO_OFF: @@ -519,17 +533,19 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } } + s->drop_cache = qemu_opt_get_bool(opts, "drop-cache", true); s->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", false); s->open_flags = open_flags; - raw_parse_flags(bdrv_flags, &s->open_flags); + raw_parse_flags(bdrv_flags, &s->open_flags, false); s->fd = -1; fd = qemu_open(filename, s->open_flags, 0644); - if (fd < 0) { - ret = -errno; - error_setg_errno(errp, errno, "Could not open '%s'", filename); + ret = fd < 0 ? -errno : 0; + + if (ret < 0) { + error_setg_errno(errp, -ret, "Could not open '%s'", filename); if (ret == -EROFS) { ret = -EACCES; } @@ -537,18 +553,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } s->fd = fd; - s->lock_fd = -1; - if (s->use_lock) { - fd = qemu_open(filename, s->open_flags); - if (fd < 0) { - ret = -errno; - error_setg_errno(errp, errno, "Could not open '%s' for locking", - filename); - qemu_close(s->fd); - goto fail; - } - s->lock_fd = fd; - } s->perm = 0; s->shared_perm = BLK_PERM_ALL; @@ -648,7 +652,7 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP; + bs->supported_zero_flags = BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK; ret = 0; fail: if (filename && (bdrv_flags & BDRV_O_TEMPORARY)) { @@ -680,43 +684,72 @@ typedef enum { * file; if @unlock == true, also unlock the unneeded bytes. * @shared_perm_lock_bits is the mask of all permissions that are NOT shared. */ -static int raw_apply_lock_bytes(int fd, +static int raw_apply_lock_bytes(BDRVRawState *s, int fd, uint64_t perm_lock_bits, uint64_t shared_perm_lock_bits, bool unlock, Error **errp) { int ret; int i; + uint64_t locked_perm, locked_shared_perm; + + if (s) { + locked_perm = s->locked_perm; + locked_shared_perm = s->locked_shared_perm; + } else { + /* + * We don't have the previous bits, just lock/unlock for each of the + * requested bits. + */ + if (unlock) { + locked_perm = BLK_PERM_ALL; + locked_shared_perm = BLK_PERM_ALL; + } else { + locked_perm = 0; + locked_shared_perm = 0; + } + } PERM_FOREACH(i) { int off = RAW_LOCK_PERM_BASE + i; - if (perm_lock_bits & (1ULL << i)) { + uint64_t bit = (1ULL << i); + if ((perm_lock_bits & bit) && !(locked_perm & bit)) { ret = qemu_lock_fd(fd, off, 1, false); if (ret) { error_setg(errp, "Failed to lock byte %d", off); return ret; + } else if (s) { + s->locked_perm |= bit; } - } else if (unlock) { + } else if (unlock && (locked_perm & bit) && !(perm_lock_bits & bit)) { ret = qemu_unlock_fd(fd, off, 1); if (ret) { error_setg(errp, "Failed to unlock byte %d", off); return ret; + } else if (s) { + s->locked_perm &= ~bit; } } } PERM_FOREACH(i) { int off = RAW_LOCK_SHARED_BASE + i; - if (shared_perm_lock_bits & (1ULL << i)) { + uint64_t bit = (1ULL << i); + if ((shared_perm_lock_bits & bit) && !(locked_shared_perm & bit)) { ret = qemu_lock_fd(fd, off, 1, false); if (ret) { error_setg(errp, "Failed to lock byte %d", off); return ret; + } else if (s) { + s->locked_shared_perm |= bit; } - } else if (unlock) { + } else if (unlock && (locked_shared_perm & bit) && + !(shared_perm_lock_bits & bit)) { ret = qemu_unlock_fd(fd, off, 1); if (ret) { error_setg(errp, "Failed to unlock byte %d", off); return ret; + } else if (s) { + s->locked_shared_perm &= ~bit; } } } @@ -741,8 +774,6 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, "Failed to get \"%s\" lock", perm_name); g_free(perm_name); - error_append_hint(errp, - "Is another process using the image?\n"); return ret; } } @@ -758,8 +789,6 @@ static int raw_check_lock_bytes(int fd, uint64_t perm, uint64_t shared_perm, "Failed to get shared \"%s\" lock", perm_name); g_free(perm_name); - error_append_hint(errp, - "Is another process using the image?\n"); return ret; } } @@ -784,86 +813,79 @@ static int raw_handle_perm_lock(BlockDriverState *bs, return 0; } - assert(s->lock_fd > 0); - switch (op) { case RAW_PL_PREPARE: - ret = raw_apply_lock_bytes(s->lock_fd, s->perm | new_perm, + if ((s->perm | new_perm) == s->perm && + (s->shared_perm & new_shared) == s->shared_perm) + { + /* + * We are going to unlock bytes, it should not fail. If it fail due + * to some fs-dependent permission-unrelated reasons (which occurs + * sometimes on NFS and leads to abort in bdrv_replace_child) we + * can't prevent such errors by any check here. And we ignore them + * anyway in ABORT and COMMIT. + */ + return 0; + } + ret = raw_apply_lock_bytes(s, s->fd, s->perm | new_perm, ~s->shared_perm | ~new_shared, false, errp); if (!ret) { - ret = raw_check_lock_bytes(s->lock_fd, new_perm, new_shared, errp); + ret = raw_check_lock_bytes(s->fd, new_perm, new_shared, errp); if (!ret) { return 0; } + error_append_hint(errp, + "Is another process using the image [%s]?\n", + bs->filename); } op = RAW_PL_ABORT; /* fall through to unlock bytes. */ case RAW_PL_ABORT: - raw_apply_lock_bytes(s->lock_fd, s->perm, ~s->shared_perm, + raw_apply_lock_bytes(s, s->fd, s->perm, ~s->shared_perm, true, &local_err); if (local_err) { /* Theoretically the above call only unlocks bytes and it cannot * fail. Something weird happened, report it. */ - error_report_err(local_err); + warn_report_err(local_err); } break; case RAW_PL_COMMIT: - raw_apply_lock_bytes(s->lock_fd, new_perm, ~new_shared, + raw_apply_lock_bytes(s, s->fd, new_perm, ~new_shared, true, &local_err); if (local_err) { /* Theoretically the above call only unlocks bytes and it cannot * fail. Something weird happened, report it. */ - error_report_err(local_err); + warn_report_err(local_err); } break; } return ret; } -static int raw_reopen_prepare(BDRVReopenState *state, - BlockReopenQueue *queue, Error **errp) +static int raw_reconfigure_getfd(BlockDriverState *bs, int flags, + int *open_flags, uint64_t perm, bool force_dup, + Error **errp) { - BDRVRawState *s; - BDRVRawReopenState *rs; - QemuOpts *opts; - int ret = 0; - Error *local_err = NULL; - - assert(state != NULL); - assert(state->bs != NULL); - - s = state->bs->opaque; - - state->opaque = g_new0(BDRVRawReopenState, 1); - rs = state->opaque; - rs->fd = -1; - - /* Handle options changes */ - opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); - qemu_opts_absorb_qdict(opts, state->options, &local_err); - if (local_err) { - error_propagate(errp, local_err); - ret = -EINVAL; - goto out; - } - - rs->check_cache_dropped = qemu_opt_get_bool(opts, "x-check-cache-dropped", - s->check_cache_dropped); - - if (s->type == FTYPE_CD) { - rs->open_flags |= O_NONBLOCK; - } - - raw_parse_flags(state->flags, &rs->open_flags); - + BDRVRawState *s = bs->opaque; + int fd = -1; + int ret; + bool has_writers = perm & + (BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_RESIZE); int fcntl_flags = O_APPEND | O_NONBLOCK; #ifdef O_NOATIME fcntl_flags |= O_NOATIME; #endif + *open_flags = 0; + if (s->type == FTYPE_CD) { + *open_flags |= O_NONBLOCK; + } + + raw_parse_flags(flags, open_flags, has_writers); + #ifdef O_ASYNC /* Not all operating systems have O_ASYNC, and those that don't * will not let us track the state into rs->open_flags (typically @@ -873,46 +895,101 @@ static int raw_reopen_prepare(BDRVReopenState *state, assert((s->open_flags & O_ASYNC) == 0); #endif - if ((rs->open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { + if (!force_dup && *open_flags == s->open_flags) { + /* We're lucky, the existing fd is fine */ + return s->fd; + } + + if ((*open_flags & ~fcntl_flags) == (s->open_flags & ~fcntl_flags)) { /* dup the original fd */ - rs->fd = qemu_dup(s->fd); - if (rs->fd >= 0) { - ret = fcntl_setfl(rs->fd, rs->open_flags); + fd = qemu_dup(s->fd); + if (fd >= 0) { + ret = fcntl_setfl(fd, *open_flags); if (ret) { - qemu_close(rs->fd); - rs->fd = -1; + qemu_close(fd); + fd = -1; } } } /* If we cannot use fcntl, or fcntl failed, fall back to qemu_open() */ - if (rs->fd == -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(!(rs->open_flags & O_CREAT)); - rs->fd = qemu_open(normalized_filename, rs->open_flags); - if (rs->fd == -1) { + if (fd == -1) { + const char *normalized_filename = bs->filename; + ret = raw_normalize_devicepath(&normalized_filename, errp); + if (ret >= 0) { + assert(!(*open_flags & O_CREAT)); + fd = qemu_open(normalized_filename, *open_flags); + if (fd == -1) { error_setg_errno(errp, errno, "Could not reopen file"); - ret = -1; + return -1; } } } + return fd; +} + +static int raw_reopen_prepare(BDRVReopenState *state, + BlockReopenQueue *queue, Error **errp) +{ + BDRVRawState *s; + BDRVRawReopenState *rs; + QemuOpts *opts; + int ret; + Error *local_err = NULL; + + assert(state != NULL); + assert(state->bs != NULL); + + s = state->bs->opaque; + + state->opaque = g_new0(BDRVRawReopenState, 1); + rs = state->opaque; + + /* Handle options changes */ + opts = qemu_opts_create(&raw_runtime_opts, NULL, 0, &error_abort); + qemu_opts_absorb_qdict(opts, state->options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto out; + } + + rs->drop_cache = qemu_opt_get_bool_del(opts, "drop-cache", true); + rs->check_cache_dropped = + qemu_opt_get_bool_del(opts, "x-check-cache-dropped", false); + + /* This driver's reopen function doesn't currently allow changing + * other options, so let's put them back in the original QDict and + * bdrv_reopen_prepare() will detect changes and complain. */ + qemu_opts_to_qdict(opts, state->options); + + rs->fd = raw_reconfigure_getfd(state->bs, state->flags, &rs->open_flags, + state->perm, true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -1; + goto out; + } + /* Fail already reopen_prepare() if we can't get a working O_DIRECT * alignment with the new fd. */ if (rs->fd != -1) { raw_probe_alignment(state->bs, rs->fd, &local_err); if (local_err) { - qemu_close(rs->fd); - rs->fd = -1; error_propagate(errp, local_err); ret = -EINVAL; + goto out_fd; } } + s->reopen_state = state; + ret = 0; +out_fd: + if (ret < 0) { + qemu_close(rs->fd); + rs->fd = -1; + } out: qemu_opts_del(opts); return ret; @@ -923,6 +1000,7 @@ static void raw_reopen_commit(BDRVReopenState *state) BDRVRawReopenState *rs = state->opaque; BDRVRawState *s = state->bs->opaque; + s->drop_cache = rs->drop_cache; s->check_cache_dropped = rs->check_cache_dropped; s->open_flags = rs->open_flags; @@ -931,12 +1009,16 @@ static void raw_reopen_commit(BDRVReopenState *state) g_free(state->opaque); state->opaque = NULL; + + assert(s->reopen_state == state); + s->reopen_state = NULL; } static void raw_reopen_abort(BDRVReopenState *state) { BDRVRawReopenState *rs = state->opaque; + BDRVRawState *s = state->bs->opaque; /* nothing to do if NULL, we didn't get far enough */ if (rs == NULL) { @@ -949,6 +1031,9 @@ static void raw_reopen_abort(BDRVReopenState *state) } g_free(state->opaque); state->opaque = NULL; + + assert(s->reopen_state == state); + s->reopen_state = NULL; } static int hdev_get_max_transfer_length(BlockDriverState *bs, int fd) @@ -1110,20 +1195,24 @@ static int hdev_probe_geometry(BlockDriverState *bs, HDGeometry *geo) } #endif -static ssize_t handle_aiocb_ioctl(RawPosixAIOData *aiocb) +#if defined(__linux__) +static int handle_aiocb_ioctl(void *opaque) { + RawPosixAIOData *aiocb = opaque; int ret; - ret = ioctl(aiocb->aio_fildes, aiocb->aio_ioctl_cmd, aiocb->aio_ioctl_buf); + ret = ioctl(aiocb->aio_fildes, aiocb->ioctl.cmd, aiocb->ioctl.buf); if (ret == -1) { return -errno; } return 0; } +#endif /* linux */ -static ssize_t handle_aiocb_flush(RawPosixAIOData *aiocb) +static int handle_aiocb_flush(void *opaque) { + RawPosixAIOData *aiocb = opaque; BDRVRawState *s = aiocb->bs->opaque; int ret; @@ -1195,13 +1284,13 @@ static ssize_t handle_aiocb_rw_vector(RawPosixAIOData *aiocb) do { if (aiocb->aio_type & QEMU_AIO_WRITE) len = qemu_pwritev(aiocb->aio_fildes, - aiocb->aio_iov, - aiocb->aio_niov, + aiocb->io.iov, + aiocb->io.niov, aiocb->aio_offset); else len = qemu_preadv(aiocb->aio_fildes, - aiocb->aio_iov, - aiocb->aio_niov, + aiocb->io.iov, + aiocb->io.niov, aiocb->aio_offset); } while (len == -1 && errno == EINTR); @@ -1257,8 +1346,9 @@ static ssize_t handle_aiocb_rw_linear(RawPosixAIOData *aiocb, char *buf) return offset; } -static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) +static int handle_aiocb_rw(void *opaque) { + RawPosixAIOData *aiocb = opaque; ssize_t nbytes; char *buf; @@ -1267,8 +1357,9 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) * If there is just a single buffer, and it is properly aligned * we can just use plain pread/pwrite without any problems. */ - if (aiocb->aio_niov == 1) { - return handle_aiocb_rw_linear(aiocb, aiocb->aio_iov->iov_base); + if (aiocb->io.niov == 1) { + nbytes = handle_aiocb_rw_linear(aiocb, aiocb->io.iov->iov_base); + goto out; } /* * We have more than one iovec, and all are properly aligned. @@ -1280,7 +1371,7 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) nbytes = handle_aiocb_rw_vector(aiocb); if (nbytes == aiocb->aio_nbytes || (nbytes < 0 && nbytes != -ENOSYS)) { - return nbytes; + goto out; } preadv_present = false; } @@ -1298,16 +1389,17 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) */ buf = qemu_try_blockalign(aiocb->bs, aiocb->aio_nbytes); if (buf == NULL) { - return -ENOMEM; + nbytes = -ENOMEM; + goto out; } if (aiocb->aio_type & QEMU_AIO_WRITE) { char *p = buf; int i; - for (i = 0; i < aiocb->aio_niov; ++i) { - memcpy(p, aiocb->aio_iov[i].iov_base, aiocb->aio_iov[i].iov_len); - p += aiocb->aio_iov[i].iov_len; + for (i = 0; i < aiocb->io.niov; ++i) { + memcpy(p, aiocb->io.iov[i].iov_base, aiocb->io.iov[i].iov_len); + p += aiocb->io.iov[i].iov_len; } assert(p - buf == aiocb->aio_nbytes); } @@ -1318,12 +1410,12 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) size_t count = aiocb->aio_nbytes, copy; int i; - for (i = 0; i < aiocb->aio_niov && count; ++i) { + for (i = 0; i < aiocb->io.niov && count; ++i) { copy = count; - if (copy > aiocb->aio_iov[i].iov_len) { - copy = aiocb->aio_iov[i].iov_len; + if (copy > aiocb->io.iov[i].iov_len) { + copy = aiocb->io.iov[i].iov_len; } - memcpy(aiocb->aio_iov[i].iov_base, p, copy); + memcpy(aiocb->io.iov[i].iov_base, p, copy); assert(count >= copy); p += copy; count -= copy; @@ -1332,7 +1424,21 @@ static ssize_t handle_aiocb_rw(RawPosixAIOData *aiocb) } qemu_vfree(buf); - return nbytes; +out: + if (nbytes == aiocb->aio_nbytes) { + return 0; + } else if (nbytes >= 0 && nbytes < aiocb->aio_nbytes) { + if (aiocb->aio_type & QEMU_AIO_WRITE) { + return -EINVAL; + } else { + iov_memset(aiocb->io.iov, aiocb->io.niov, nbytes, + 0, aiocb->aio_nbytes - nbytes); + return 0; + } + } else { + assert(nbytes < 0); + return nbytes; + } } #ifdef CONFIG_XFS @@ -1348,7 +1454,7 @@ static int xfs_write_zeroes(BDRVRawState *s, int64_t offset, uint64_t bytes) if (xfsctl(NULL, s->fd, XFS_IOC_ZERO_RANGE, &fl) < 0) { err = errno; - DPRINTF("cannot write zero range (%s)\n", strerror(errno)); + trace_file_xfs_write_zeroes(strerror(errno)); return -err; } @@ -1367,7 +1473,7 @@ static int xfs_discard(BDRVRawState *s, int64_t offset, uint64_t bytes) if (xfsctl(NULL, s->fd, XFS_IOC_UNRESVSP64, &fl) < 0) { err = errno; - DPRINTF("cannot punch hole (%s)\n", strerror(errno)); + trace_file_xfs_discard(strerror(errno)); return -err; } @@ -1406,14 +1512,19 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) } #ifdef BLKZEROOUT - do { - uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; - if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { - return 0; - } - } while (errno == EINTR); + /* The BLKZEROOUT implementation in the kernel doesn't set + * BLKDEV_ZERO_NOFALLBACK, so we can't call this if we have to avoid slow + * fallbacks. */ + if (!(aiocb->aio_type & QEMU_AIO_NO_FALLBACK)) { + do { + uint64_t range[2] = { aiocb->aio_offset, aiocb->aio_nbytes }; + if (ioctl(aiocb->aio_fildes, BLKZEROOUT, range) == 0) { + return 0; + } + } while (errno == EINTR); - ret = translate_err(-errno); + ret = translate_err(-errno); + } #endif if (ret == -ENOTSUP) { @@ -1422,8 +1533,9 @@ static ssize_t handle_aiocb_write_zeroes_block(RawPosixAIOData *aiocb) return ret; } -static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) +static int handle_aiocb_write_zeroes(void *opaque) { + RawPosixAIOData *aiocb = opaque; #if defined(CONFIG_FALLOCATE) || defined(CONFIG_XFS) BDRVRawState *s = aiocb->bs->opaque; #endif @@ -1487,8 +1599,9 @@ static ssize_t handle_aiocb_write_zeroes(RawPosixAIOData *aiocb) return -ENOTSUP; } -static ssize_t handle_aiocb_write_zeroes_unmap(RawPosixAIOData *aiocb) +static int handle_aiocb_write_zeroes_unmap(void *opaque) { + RawPosixAIOData *aiocb = opaque; BDRVRawState *s G_GNUC_UNUSED = aiocb->bs->opaque; int ret; @@ -1530,18 +1643,20 @@ static off_t copy_file_range(int in_fd, off_t *in_off, int out_fd, } #endif -static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) +static int handle_aiocb_copy_range(void *opaque) { + RawPosixAIOData *aiocb = opaque; uint64_t bytes = aiocb->aio_nbytes; off_t in_off = aiocb->aio_offset; - off_t out_off = aiocb->aio_offset2; + off_t out_off = aiocb->copy_range.aio_offset2; while (bytes) { ssize_t ret = copy_file_range(aiocb->aio_fildes, &in_off, - aiocb->aio_fd2, &out_off, + aiocb->copy_range.aio_fd2, &out_off, bytes, 0); trace_file_copy_file_range(aiocb->bs, aiocb->aio_fildes, in_off, - aiocb->aio_fd2, out_off, bytes, 0, ret); + aiocb->copy_range.aio_fd2, out_off, bytes, + 0, ret); if (ret == 0) { /* No progress (e.g. when beyond EOF), let the caller fall back to * buffer I/O. */ @@ -1562,8 +1677,9 @@ static ssize_t handle_aiocb_copy_range(RawPosixAIOData *aiocb) return 0; } -static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) +static int handle_aiocb_discard(void *opaque) { + RawPosixAIOData *aiocb = opaque; int ret = -EOPNOTSUPP; BDRVRawState *s = aiocb->bs->opaque; @@ -1602,15 +1718,17 @@ static ssize_t handle_aiocb_discard(RawPosixAIOData *aiocb) return ret; } -static int handle_aiocb_truncate(RawPosixAIOData *aiocb) +static int handle_aiocb_truncate(void *opaque) { + RawPosixAIOData *aiocb = opaque; int result = 0; int64_t current_length = 0; char *buf = NULL; struct stat st; int fd = aiocb->aio_fildes; int64_t offset = aiocb->aio_offset; - Error **errp = aiocb->errp; + PreallocMode prealloc = aiocb->truncate.prealloc; + Error **errp = aiocb->truncate.errp; if (fstat(fd, &st) < 0) { result = -errno; @@ -1619,12 +1737,12 @@ static int handle_aiocb_truncate(RawPosixAIOData *aiocb) } current_length = st.st_size; - if (current_length > offset && aiocb->prealloc != PREALLOC_MODE_OFF) { + if (current_length > offset && prealloc != PREALLOC_MODE_OFF) { error_setg(errp, "Cannot use preallocation for shrinking files"); return -ENOTSUP; } - switch (aiocb->prealloc) { + switch (prealloc) { #ifdef CONFIG_POSIX_FALLOCATE case PREALLOC_MODE_FALLOC: /* @@ -1705,7 +1823,7 @@ static int handle_aiocb_truncate(RawPosixAIOData *aiocb) default: result = -ENOTSUP; error_setg(errp, "Unsupported preallocation mode: %s", - PreallocMode_str(aiocb->prealloc)); + PreallocMode_str(prealloc)); return result; } @@ -1721,104 +1839,19 @@ out: return result; } -static int aio_worker(void *arg) +static int coroutine_fn raw_thread_pool_submit(BlockDriverState *bs, + ThreadPoolFunc func, void *arg) { - RawPosixAIOData *aiocb = arg; - ssize_t ret = 0; - - switch (aiocb->aio_type & QEMU_AIO_TYPE_MASK) { - case QEMU_AIO_READ: - ret = handle_aiocb_rw(aiocb); - if (ret >= 0 && ret < aiocb->aio_nbytes) { - iov_memset(aiocb->aio_iov, aiocb->aio_niov, ret, - 0, aiocb->aio_nbytes - ret); - - ret = aiocb->aio_nbytes; - } - if (ret == aiocb->aio_nbytes) { - ret = 0; - } else if (ret >= 0 && ret < aiocb->aio_nbytes) { - ret = -EINVAL; - } - break; - case QEMU_AIO_WRITE: - ret = handle_aiocb_rw(aiocb); - if (ret == aiocb->aio_nbytes) { - ret = 0; - } else if (ret >= 0 && ret < aiocb->aio_nbytes) { - ret = -EINVAL; - } - break; - case QEMU_AIO_FLUSH: - ret = handle_aiocb_flush(aiocb); - break; - case QEMU_AIO_IOCTL: - ret = handle_aiocb_ioctl(aiocb); - break; - case QEMU_AIO_DISCARD: - ret = handle_aiocb_discard(aiocb); - break; - case QEMU_AIO_WRITE_ZEROES: - ret = handle_aiocb_write_zeroes(aiocb); - break; - case QEMU_AIO_WRITE_ZEROES | QEMU_AIO_DISCARD: - ret = handle_aiocb_write_zeroes_unmap(aiocb); - break; - case QEMU_AIO_COPY_RANGE: - ret = handle_aiocb_copy_range(aiocb); - break; - case QEMU_AIO_TRUNCATE: - ret = handle_aiocb_truncate(aiocb); - break; - default: - fprintf(stderr, "invalid aio request (0x%x)\n", aiocb->aio_type); - ret = -EINVAL; - break; - } - - g_free(aiocb); - return ret; -} - -static int paio_submit_co_full(BlockDriverState *bs, int fd, - int64_t offset, int fd2, int64_t offset2, - QEMUIOVector *qiov, - int bytes, int type) -{ - RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); - ThreadPool *pool; - - acb->bs = bs; - acb->aio_type = type; - acb->aio_fildes = fd; - acb->aio_fd2 = fd2; - acb->aio_offset2 = offset2; - - acb->aio_nbytes = bytes; - acb->aio_offset = offset; - - if (qiov) { - acb->aio_iov = qiov->iov; - acb->aio_niov = qiov->niov; - assert(qiov->size == bytes); - } - - trace_file_paio_submit_co(offset, bytes, type); - pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_co(pool, aio_worker, acb); -} - -static inline int paio_submit_co(BlockDriverState *bs, int fd, - int64_t offset, QEMUIOVector *qiov, - int bytes, int type) -{ - return paio_submit_co_full(bs, fd, offset, -1, 0, qiov, bytes, type); + /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ + ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); + return thread_pool_submit_co(pool, func, arg); } static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int type) { BDRVRawState *s = bs->opaque; + RawPosixAIOData acb; if (fd_open(bs) < 0) return -EIO; @@ -1841,7 +1874,20 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, uint64_t offset, } } - return paio_submit_co(bs, s->fd, offset, qiov, bytes, type); + acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = s->fd, + .aio_type = type, + .aio_offset = offset, + .aio_nbytes = bytes, + .io = { + .iov = qiov->iov, + .niov = qiov->niov, + }, + }; + + assert(qiov->size == bytes); + return raw_thread_pool_submit(bs, handle_aiocb_rw, &acb); } static int coroutine_fn raw_co_preadv(BlockDriverState *bs, uint64_t offset, @@ -1884,6 +1930,7 @@ static void raw_aio_unplug(BlockDriverState *bs) static int raw_co_flush_to_disk(BlockDriverState *bs) { BDRVRawState *s = bs->opaque; + RawPosixAIOData acb; int ret; ret = fd_open(bs); @@ -1891,7 +1938,13 @@ static int raw_co_flush_to_disk(BlockDriverState *bs) return ret; } - return paio_submit_co(bs, s->fd, 0, NULL, 0, QEMU_AIO_FLUSH); + acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = s->fd, + .aio_type = QEMU_AIO_FLUSH, + }; + + return raw_thread_pool_submit(bs, handle_aiocb_flush, &acb); } static void raw_aio_attach_aio_context(BlockDriverState *bs, @@ -1918,10 +1971,6 @@ static void raw_close(BlockDriverState *bs) qemu_close(s->fd); s->fd = -1; } - if (s->lock_fd >= 0) { - qemu_close(s->lock_fd); - s->lock_fd = -1; - } } /** @@ -1934,21 +1983,20 @@ static int coroutine_fn raw_regular_truncate(BlockDriverState *bs, int fd, int64_t offset, PreallocMode prealloc, Error **errp) { - RawPosixAIOData *acb = g_new(RawPosixAIOData, 1); - ThreadPool *pool; + RawPosixAIOData acb; - *acb = (RawPosixAIOData) { + acb = (RawPosixAIOData) { .bs = bs, .aio_fildes = fd, .aio_type = QEMU_AIO_TRUNCATE, .aio_offset = offset, - .prealloc = prealloc, - .errp = errp, + .truncate = { + .prealloc = prealloc, + .errp = errp, + }, }; - /* @bs can be NULL, bdrv_get_aio_context() returns the main context then */ - pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_co(pool, aio_worker, acb); + return raw_thread_pool_submit(bs, handle_aiocb_truncate, &acb); } static int coroutine_fn raw_co_truncate(BlockDriverState *bs, int64_t offset, @@ -2083,7 +2131,7 @@ again: #endif if (!fstat(fd, &sb) && (S_IFCHR & sb.st_mode)) { #ifdef DIOCGMEDIASIZE - if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) + if (ioctl(fd, DIOCGMEDIASIZE, (off_t *)&size)) #elif defined(DIOCGPART) { struct partinfo pi; @@ -2209,7 +2257,7 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) shared = BLK_PERM_ALL & ~BLK_PERM_RESIZE; /* Step one: Take locks */ - result = raw_apply_lock_bytes(fd, perm, ~shared, false, errp); + result = raw_apply_lock_bytes(NULL, fd, perm, ~shared, false, errp); if (result < 0) { goto out_close; } @@ -2217,6 +2265,9 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) /* Step two: Check that nobody else has taken conflicting locks */ result = raw_check_lock_bytes(fd, perm, shared, errp); if (result < 0) { + error_append_hint(errp, + "Is another process using the image [%s]?\n", + file_opts->filename); goto out_unlock; } @@ -2250,13 +2301,13 @@ raw_co_create(BlockdevCreateOptions *options, Error **errp) } out_unlock: - raw_apply_lock_bytes(fd, 0, 0, true, &local_err); + raw_apply_lock_bytes(NULL, fd, 0, 0, true, &local_err); if (local_err) { /* The above call should not fail, and if it does, that does * not mean the whole creation operation has failed. So * report it the user for their convenience, but do not report * it to the caller. */ - error_report_err(local_err); + warn_report_err(local_err); } out_close: @@ -2540,6 +2591,10 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, return; } + if (!s->drop_cache) { + return; + } + if (s->open_flags & O_DIRECT) { return; /* No host kernel page cache */ } @@ -2576,25 +2631,70 @@ static void coroutine_fn raw_co_invalidate_cache(BlockDriverState *bs, } static coroutine_fn int -raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +raw_do_pdiscard(BlockDriverState *bs, int64_t offset, int bytes, bool blkdev) { BDRVRawState *s = bs->opaque; + RawPosixAIOData acb; - return paio_submit_co(bs, s->fd, offset, NULL, bytes, QEMU_AIO_DISCARD); + acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = s->fd, + .aio_type = QEMU_AIO_DISCARD, + .aio_offset = offset, + .aio_nbytes = bytes, + }; + + if (blkdev) { + acb.aio_type |= QEMU_AIO_BLKDEV; + } + + return raw_thread_pool_submit(bs, handle_aiocb_discard, &acb); +} + +static coroutine_fn int +raw_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) +{ + return raw_do_pdiscard(bs, offset, bytes, false); +} + +static int coroutine_fn +raw_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, + BdrvRequestFlags flags, bool blkdev) +{ + BDRVRawState *s = bs->opaque; + RawPosixAIOData acb; + ThreadPoolFunc *handler; + + acb = (RawPosixAIOData) { + .bs = bs, + .aio_fildes = s->fd, + .aio_type = QEMU_AIO_WRITE_ZEROES, + .aio_offset = offset, + .aio_nbytes = bytes, + }; + + if (blkdev) { + acb.aio_type |= QEMU_AIO_BLKDEV; + } + if (flags & BDRV_REQ_NO_FALLBACK) { + acb.aio_type |= QEMU_AIO_NO_FALLBACK; + } + + if (flags & BDRV_REQ_MAY_UNMAP) { + acb.aio_type |= QEMU_AIO_DISCARD; + handler = handle_aiocb_write_zeroes_unmap; + } else { + handler = handle_aiocb_write_zeroes; + } + + return raw_thread_pool_submit(bs, handler, &acb); } static int coroutine_fn raw_co_pwrite_zeroes( BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - BDRVRawState *s = bs->opaque; - int operation = QEMU_AIO_WRITE_ZEROES; - - if (flags & BDRV_REQ_MAY_UNMAP) { - operation |= QEMU_AIO_DISCARD; - } - - return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); + return raw_do_pwrite_zeroes(bs, offset, bytes, flags, false); } static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) @@ -2631,12 +2731,78 @@ static QemuOptsList raw_create_opts = { static int raw_check_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared, Error **errp) { - return raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp); + BDRVRawState *s = bs->opaque; + BDRVRawReopenState *rs = NULL; + int open_flags; + int ret; + + if (s->perm_change_fd) { + /* + * In the context of reopen, this function may be called several times + * (directly and recursively while change permissions of the parent). + * This is even true for children that don't inherit from the original + * reopen node, so s->reopen_state is not set. + * + * Ignore all but the first call. + */ + return 0; + } + + if (s->reopen_state) { + /* We already have a new file descriptor to set permissions for */ + assert(s->reopen_state->perm == perm); + assert(s->reopen_state->shared_perm == shared); + rs = s->reopen_state->opaque; + s->perm_change_fd = rs->fd; + } else { + /* We may need a new fd if auto-read-only switches the mode */ + ret = raw_reconfigure_getfd(bs, bs->open_flags, &open_flags, perm, + false, errp); + if (ret < 0) { + return ret; + } else if (ret != s->fd) { + s->perm_change_fd = ret; + } + } + + /* Prepare permissions on old fd to avoid conflicts between old and new, + * but keep everything locked that new will need. */ + ret = raw_handle_perm_lock(bs, RAW_PL_PREPARE, perm, shared, errp); + if (ret < 0) { + goto fail; + } + + /* Copy locks to the new fd */ + if (s->perm_change_fd) { + ret = raw_apply_lock_bytes(NULL, s->perm_change_fd, perm, ~shared, + false, errp); + if (ret < 0) { + raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); + goto fail; + } + } + return 0; + +fail: + if (s->perm_change_fd && !s->reopen_state) { + qemu_close(s->perm_change_fd); + } + s->perm_change_fd = 0; + return ret; } static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared) { BDRVRawState *s = bs->opaque; + + /* For reopen, we have already switched to the new fd (.bdrv_set_perm is + * called after .bdrv_reopen_commit) */ + if (s->perm_change_fd && s->fd != s->perm_change_fd) { + qemu_close(s->fd); + s->fd = s->perm_change_fd; + } + s->perm_change_fd = 0; + raw_handle_perm_lock(bs, RAW_PL_COMMIT, perm, shared, NULL); s->perm = perm; s->shared_perm = shared; @@ -2644,6 +2810,15 @@ static void raw_set_perm(BlockDriverState *bs, uint64_t perm, uint64_t shared) static void raw_abort_perm_update(BlockDriverState *bs) { + BDRVRawState *s = bs->opaque; + + /* For reopen, .bdrv_reopen_abort is called afterwards and will close + * the file descriptor. */ + if (s->perm_change_fd && !s->reopen_state) { + qemu_close(s->perm_change_fd); + } + s->perm_change_fd = 0; + raw_handle_perm_lock(bs, RAW_PL_ABORT, 0, 0, NULL); } @@ -2665,6 +2840,7 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags) { + RawPosixAIOData acb; BDRVRawState *s = bs->opaque; BDRVRawState *src_s; @@ -2677,8 +2853,20 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, if (fd_open(src->bs) < 0 || fd_open(dst->bs) < 0) { return -EIO; } - return paio_submit_co_full(bs, src_s->fd, src_offset, s->fd, dst_offset, - NULL, bytes, QEMU_AIO_COPY_RANGE); + + acb = (RawPosixAIOData) { + .bs = bs, + .aio_type = QEMU_AIO_COPY_RANGE, + .aio_fildes = src_s->fd, + .aio_offset = src_offset, + .aio_nbytes = bytes, + .copy_range = { + .aio_fd2 = s->fd, + .aio_offset2 = dst_offset, + }, + }; + + return raw_thread_pool_submit(bs, handle_aiocb_copy_range, &acb); } BlockDriver bdrv_file = { @@ -2720,6 +2908,7 @@ BlockDriver bdrv_file = { .bdrv_set_perm = raw_set_perm, .bdrv_abort_perm_update = raw_abort_perm_update, .create_opts = &raw_create_opts, + .mutable_opts = mutable_opts, }; /***********************************************/ @@ -2761,7 +2950,7 @@ static char *FindEjectableOpticalMedia(io_iterator_t *mediaIterator) /* If a match was found, leave the loop */ if (*mediaIterator != 0) { - DPRINTF("Matching using %s\n", matching_array[index]); + trace_file_FindEjectableOpticalMedia(matching_array[index]); mediaType = g_strdup(matching_array[index]); break; } @@ -2821,7 +3010,7 @@ static bool setup_cdrom(char *bsd_path, Error **errp) 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); + trace_file_setup_cdrom(test_partition); pstrcpy(bsd_path, MAXPATHLEN, test_partition); } return partition_found; @@ -2916,8 +3105,7 @@ static bool hdev_is_sg(BlockDriverState *bs) ret = ioctl(s->fd, SG_GET_SCSI_ID, &scsiid); if (ret >= 0) { - DPRINTF("SG device found: type=%d, version=%d\n", - scsiid.scsi_type, sg_version); + trace_file_hdev_is_sg(scsiid.scsi_type, sg_version); return true; } @@ -3026,36 +3214,39 @@ hdev_open_Mac_error: } #if defined(__linux__) - -static BlockAIOCB *hdev_aio_ioctl(BlockDriverState *bs, - unsigned long int req, void *buf, - BlockCompletionFunc *cb, void *opaque) +static int coroutine_fn +hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { BDRVRawState *s = bs->opaque; - RawPosixAIOData *acb; - ThreadPool *pool; + RawPosixAIOData acb; + int ret; - if (fd_open(bs) < 0) - return NULL; + ret = fd_open(bs); + if (ret < 0) { + return ret; + } if (req == SG_IO && s->pr_mgr) { struct sg_io_hdr *io_hdr = buf; if (io_hdr->cmdp[0] == PERSISTENT_RESERVE_OUT || io_hdr->cmdp[0] == PERSISTENT_RESERVE_IN) { return pr_manager_execute(s->pr_mgr, bdrv_get_aio_context(bs), - s->fd, io_hdr, cb, opaque); + s->fd, io_hdr); } } - acb = g_new(RawPosixAIOData, 1); - acb->bs = bs; - acb->aio_type = QEMU_AIO_IOCTL; - acb->aio_fildes = s->fd; - acb->aio_offset = 0; - acb->aio_ioctl_buf = buf; - acb->aio_ioctl_cmd = req; - pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); - return thread_pool_submit_aio(pool, aio_worker, acb, cb, opaque); + acb = (RawPosixAIOData) { + .bs = bs, + .aio_type = QEMU_AIO_IOCTL, + .aio_fildes = s->fd, + .aio_offset = 0, + .ioctl = { + .buf = buf, + .cmd = req, + }, + }; + + return raw_thread_pool_submit(bs, handle_aiocb_ioctl, &acb); } #endif /* linux */ @@ -3072,22 +3263,18 @@ static int fd_open(BlockDriverState *bs) static coroutine_fn int hdev_co_pdiscard(BlockDriverState *bs, int64_t offset, int bytes) { - BDRVRawState *s = bs->opaque; int ret; ret = fd_open(bs); if (ret < 0) { return ret; } - return paio_submit_co(bs, s->fd, offset, NULL, bytes, - QEMU_AIO_DISCARD | QEMU_AIO_BLKDEV); + return raw_do_pdiscard(bs, offset, bytes, true); } static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags) { - BDRVRawState *s = bs->opaque; - int operation = QEMU_AIO_WRITE_ZEROES | QEMU_AIO_BLKDEV; int rc; rc = fd_open(bs); @@ -3095,11 +3282,7 @@ static coroutine_fn int hdev_co_pwrite_zeroes(BlockDriverState *bs, return rc; } - if (flags & BDRV_REQ_MAY_UNMAP) { - operation |= QEMU_AIO_DISCARD; - } - - return paio_submit_co(bs, s->fd, offset, NULL, bytes, operation); + return raw_do_pwrite_zeroes(bs, offset, bytes, flags, true); } static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts, @@ -3121,9 +3304,8 @@ static int coroutine_fn hdev_co_create_opts(const char *filename, QemuOpts *opts (void)has_prefix; - ret = raw_normalize_devicepath(&filename); + ret = raw_normalize_devicepath(&filename, errp); if (ret < 0) { - error_setg_errno(errp, -ret, "Could not normalize device path"); return ret; } @@ -3178,6 +3360,7 @@ static BlockDriver bdrv_host_device = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .mutable_opts = mutable_opts, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, .bdrv_co_pwrite_zeroes = hdev_co_pwrite_zeroes, @@ -3205,7 +3388,7 @@ static BlockDriver bdrv_host_device = { /* generic scsi device */ #ifdef __linux__ - .bdrv_aio_ioctl = hdev_aio_ioctl, + .bdrv_co_ioctl = hdev_co_ioctl, #endif }; @@ -3304,6 +3487,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .mutable_opts = mutable_opts, .bdrv_co_invalidate_cache = raw_co_invalidate_cache, @@ -3327,7 +3511,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_lock_medium = cdrom_lock_medium, /* generic scsi device */ - .bdrv_aio_ioctl = hdev_aio_ioctl, + .bdrv_co_ioctl = hdev_co_ioctl, }; #endif /* __linux__ */ @@ -3437,6 +3621,7 @@ static BlockDriver bdrv_host_cdrom = { .bdrv_reopen_abort = raw_reopen_abort, .bdrv_co_create_opts = hdev_co_create_opts, .create_opts = &raw_create_opts, + .mutable_opts = mutable_opts, .bdrv_co_preadv = raw_co_preadv, .bdrv_co_pwritev = raw_co_pwritev, diff --git a/block/file-win32.c b/block/file-win32.c index f1e2187f3b..6b2d67b239 100644 --- a/block/file-win32.c +++ b/block/file-win32.c @@ -176,7 +176,7 @@ int qemu_ftruncate64(int fd, int64_t length) BOOL res; if ((GetVersion() & 0x80000000UL) && (length >> 32) != 0) - return -1; + return -1; h = (HANDLE)_get_osfhandle(fd); @@ -184,13 +184,13 @@ int qemu_ftruncate64(int fd, int64_t length) li.HighPart = 0; li.LowPart = SetFilePointer (h, 0, &li.HighPart, FILE_CURRENT); if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - return -1; + return -1; } high = length >> 32; dw = SetFilePointer(h, (DWORD) length, &high, FILE_BEGIN); if (dw == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR) { - return -1; + return -1; } res = SetEndOfFile(h); @@ -203,7 +203,7 @@ static int set_sparse(int fd) { DWORD returned; return (int) DeviceIoControl((HANDLE)_get_osfhandle(fd), FSCTL_SET_SPARSE, - NULL, 0, NULL, 0, &returned, NULL); + NULL, 0, NULL, 0, &returned, NULL); } static void raw_detach_aio_context(BlockDriverState *bs) diff --git a/block/gluster.c b/block/gluster.c index 4fd55a9cc5..e664ca4462 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -9,6 +9,7 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include #include "block/block_int.h" #include "block/qdict.h" @@ -20,6 +21,10 @@ #include "qemu/option.h" #include "qemu/cutils.h" +#ifdef CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT +# define glfs_ftruncate(fd, offset) glfs_ftruncate(fd, offset, NULL, NULL) +#endif + #define GLUSTER_OPT_FILENAME "filename" #define GLUSTER_OPT_VOLUME "volume" #define GLUSTER_OPT_PATH "path" @@ -37,6 +42,12 @@ #define GLUSTER_DEBUG_MAX 9 #define GLUSTER_OPT_LOGFILE "logfile" #define GLUSTER_LOGFILE_DEFAULT "-" /* handled in libgfapi as /dev/stderr */ +/* + * Several versions of GlusterFS (3.12? -> 6.0.1) fail when the transfer size + * is greater or equal to 1024 MiB, so we are limiting the transfer size to 512 + * MiB to avoid this rare issue. + */ +#define GLUSTER_MAX_TRANSFER (512 * MiB) #define GERR_INDEX_HINT "hint: check in 'server' array index '%d'\n" @@ -72,7 +83,7 @@ typedef struct ListElement { GlfsPreopened saved; } ListElement; -static QLIST_HEAD(glfs_list, ListElement) glfs_list; +static QLIST_HEAD(, ListElement) glfs_list; static QemuOptsList qemu_gluster_create_opts = { .name = "qemu-gluster-create-opts", @@ -725,7 +736,11 @@ static struct glfs *qemu_gluster_init(BlockdevOptionsGluster *gconf, /* * AIO callback routine called from GlusterFS thread. */ -static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, void *arg) +static void gluster_finish_aiocb(struct glfs_fd *fd, ssize_t ret, +#ifdef CONFIG_GLUSTERFS_IOCB_HAS_STAT + struct glfs_stat *pre, struct glfs_stat *post, +#endif + void *arg) { GlusterAIOCB *acb = (GlusterAIOCB *)arg; @@ -849,8 +864,16 @@ static int qemu_gluster_open(BlockDriverState *bs, QDict *options, qemu_gluster_parse_flags(bdrv_flags, &open_flags); s->fd = glfs_open(s->glfs, gconf->path, open_flags); - if (!s->fd) { - ret = -errno; + ret = s->fd ? 0 : -errno; + + if (ret == -EACCES || ret == -EROFS) { + /* Try to degrade to read-only, but if it doesn't work, still use the + * normal error message. */ + if (bdrv_apply_auto_read_only(bs, NULL, NULL) == 0) { + open_flags = (open_flags & ~O_RDWR) | O_RDONLY; + s->fd = glfs_open(s->glfs, gconf->path, open_flags); + ret = s->fd ? 0 : -errno; + } } s->supports_seek_data = qemu_gluster_test_seek(s->fd); @@ -871,6 +894,11 @@ out: return ret; } +static void qemu_gluster_refresh_limits(BlockDriverState *bs, Error **errp) +{ + bs->bl.max_transfer = GLUSTER_MAX_TRANSFER; +} + static int qemu_gluster_reopen_prepare(BDRVReopenState *state, BlockReopenQueue *queue, Error **errp) { @@ -1487,6 +1515,21 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, } +static const char *const gluster_strong_open_opts[] = { + GLUSTER_OPT_VOLUME, + GLUSTER_OPT_PATH, + GLUSTER_OPT_TYPE, + GLUSTER_OPT_SERVER_PATTERN, + GLUSTER_OPT_HOST, + GLUSTER_OPT_PORT, + GLUSTER_OPT_TO, + GLUSTER_OPT_IPV4, + GLUSTER_OPT_IPV6, + GLUSTER_OPT_SOCKET, + + NULL +}; + static BlockDriver bdrv_gluster = { .format_name = "gluster", .protocol_name = "gluster", @@ -1513,7 +1556,9 @@ static BlockDriver bdrv_gluster = { .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes, #endif .bdrv_co_block_status = qemu_gluster_co_block_status, + .bdrv_refresh_limits = qemu_gluster_refresh_limits, .create_opts = &qemu_gluster_create_opts, + .strong_runtime_opts = gluster_strong_open_opts, }; static BlockDriver bdrv_gluster_tcp = { @@ -1542,7 +1587,9 @@ static BlockDriver bdrv_gluster_tcp = { .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes, #endif .bdrv_co_block_status = qemu_gluster_co_block_status, + .bdrv_refresh_limits = qemu_gluster_refresh_limits, .create_opts = &qemu_gluster_create_opts, + .strong_runtime_opts = gluster_strong_open_opts, }; static BlockDriver bdrv_gluster_unix = { @@ -1571,7 +1618,9 @@ static BlockDriver bdrv_gluster_unix = { .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes, #endif .bdrv_co_block_status = qemu_gluster_co_block_status, + .bdrv_refresh_limits = qemu_gluster_refresh_limits, .create_opts = &qemu_gluster_create_opts, + .strong_runtime_opts = gluster_strong_open_opts, }; /* rdma is deprecated (actually never supported for volfile fetch). @@ -1606,7 +1655,9 @@ static BlockDriver bdrv_gluster_rdma = { .bdrv_co_pwrite_zeroes = qemu_gluster_co_pwrite_zeroes, #endif .bdrv_co_block_status = qemu_gluster_co_block_status, + .bdrv_refresh_limits = qemu_gluster_refresh_limits, .create_opts = &qemu_gluster_create_opts, + .strong_runtime_opts = gluster_strong_open_opts, }; static void bdrv_gluster_init(void) diff --git a/block/io.c b/block/io.c index 7100344c7b..dfc153b8d8 100644 --- a/block/io.c +++ b/block/io.c @@ -38,8 +38,6 @@ /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) -static AioWait drain_all_aio_wait; - static void bdrv_parent_cb_resize(BlockDriverState *bs); static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, int64_t offset, int bytes, BdrvRequestFlags flags); @@ -268,10 +266,6 @@ bool bdrv_drain_poll(BlockDriverState *bs, bool recursive, static bool bdrv_drain_poll_top_level(BlockDriverState *bs, bool recursive, BdrvChild *ignore_parent) { - /* Execute pending BHs first and check everything else only after the BHs - * have executed. */ - while (aio_poll(bs->aio_context, false)); - return bdrv_drain_poll(bs, recursive, ignore_parent, false); } @@ -288,6 +282,18 @@ static void bdrv_co_drain_bh_cb(void *opaque) BlockDriverState *bs = data->bs; if (bs) { + AioContext *ctx = bdrv_get_aio_context(bs); + AioContext *co_ctx = qemu_coroutine_get_aio_context(co); + + /* + * When the coroutine yielded, the lock for its home context was + * released, so we need to re-acquire it here. If it explicitly + * acquired a different context, the lock is still held and we don't + * want to lock it a second time (or AIO_WAIT_WHILE() would hang). + */ + if (ctx == co_ctx) { + aio_context_acquire(ctx); + } bdrv_dec_in_flight(bs); if (data->begin) { bdrv_do_drained_begin(bs, data->recursive, data->parent, @@ -296,6 +302,9 @@ static void bdrv_co_drain_bh_cb(void *opaque) bdrv_do_drained_end(bs, data->recursive, data->parent, data->ignore_bds_parents); } + if (ctx == co_ctx) { + aio_context_release(ctx); + } } else { assert(data->begin); bdrv_drain_all_begin(); @@ -496,10 +505,6 @@ static bool bdrv_drain_all_poll(void) BlockDriverState *bs = NULL; bool result = false; - /* Execute pending BHs first (may modify the graph) and check everything - * else only after the BHs have executed. */ - while (aio_poll(qemu_get_aio_context(), false)); - /* bdrv_drain_poll() can't make changes to the graph and we are holding the * main AioContext lock, so iterating bdrv_next_all_states() is safe. */ while ((bs = bdrv_next_all_states(bs))) { @@ -550,7 +555,7 @@ void bdrv_drain_all_begin(void) } /* Now poll the in-flight requests */ - AIO_WAIT_WHILE(&drain_all_aio_wait, NULL, bdrv_drain_all_poll()); + AIO_WAIT_WHILE(NULL, bdrv_drain_all_poll()); while ((bs = bdrv_next_all_states(bs))) { bdrv_drain_assert_idle(bs); @@ -706,8 +711,7 @@ void bdrv_inc_in_flight(BlockDriverState *bs) void bdrv_wakeup(BlockDriverState *bs) { - aio_wait_kick(bdrv_get_aio_wait(bs)); - aio_wait_kick(&drain_all_aio_wait); + aio_wait_kick(); } void bdrv_dec_in_flight(BlockDriverState *bs) @@ -802,6 +806,7 @@ static void coroutine_fn bdrv_rw_co_entry(void *opaque) rwco->qiov->size, rwco->qiov, rwco->flags); } + aio_wait_kick(); } /* @@ -838,17 +843,13 @@ static int bdrv_prwv_co(BdrvChild *child, int64_t offset, static int bdrv_rw_co(BdrvChild *child, int64_t sector_num, uint8_t *buf, int nb_sectors, bool is_write, BdrvRequestFlags flags) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = (void *)buf, - .iov_len = nb_sectors * BDRV_SECTOR_SIZE, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, + nb_sectors * BDRV_SECTOR_SIZE); if (nb_sectors < 0 || nb_sectors > BDRV_REQUEST_MAX_SECTORS) { return -EINVAL; } - qemu_iovec_init_external(&qiov, &iov, 1); return bdrv_prwv_co(child, sector_num << BDRV_SECTOR_BITS, &qiov, is_write, flags); } @@ -875,13 +876,8 @@ int bdrv_write(BdrvChild *child, int64_t sector_num, int bdrv_pwrite_zeroes(BdrvChild *child, int64_t offset, int bytes, BdrvRequestFlags flags) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = NULL, - .iov_len = bytes, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes); - qemu_iovec_init_external(&qiov, &iov, 1); return bdrv_prwv_co(child, offset, &qiov, true, BDRV_REQ_ZERO_WRITE | flags); } @@ -913,8 +909,6 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) } ret = bdrv_block_status(bs, offset, bytes, &bytes, NULL, NULL); if (ret < 0) { - error_report("error getting block status at offset %" PRId64 ": %s", - offset, strerror(-ret)); return ret; } if (ret & BDRV_BLOCK_ZERO) { @@ -923,8 +917,6 @@ int bdrv_make_zero(BdrvChild *child, BdrvRequestFlags flags) } ret = bdrv_pwrite_zeroes(child, offset, bytes, flags); if (ret < 0) { - error_report("error writing zeroes at offset %" PRId64 ": %s", - offset, strerror(-ret)); return ret; } offset += bytes; @@ -945,17 +937,12 @@ int bdrv_preadv(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) int bdrv_pread(BdrvChild *child, int64_t offset, void *buf, int bytes) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = (void *)buf, - .iov_len = bytes, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); if (bytes < 0) { return -EINVAL; } - qemu_iovec_init_external(&qiov, &iov, 1); return bdrv_preadv(child, offset, &qiov); } @@ -973,17 +960,12 @@ int bdrv_pwritev(BdrvChild *child, int64_t offset, QEMUIOVector *qiov) int bdrv_pwrite(BdrvChild *child, int64_t offset, const void *buf, int bytes) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = (void *) buf, - .iov_len = bytes, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); if (bytes < 0) { return -EINVAL; } - qemu_iovec_init_external(&qiov, &iov, 1); return bdrv_pwritev(child, offset, &qiov); } @@ -1033,6 +1015,7 @@ static int coroutine_fn bdrv_driver_preadv(BlockDriverState *bs, unsigned int nb_sectors; assert(!(flags & ~BDRV_REQ_MASK)); + assert(!(flags & BDRV_REQ_NO_FALLBACK)); if (!drv) { return -ENOMEDIUM; @@ -1079,6 +1062,7 @@ static int coroutine_fn bdrv_driver_pwritev(BlockDriverState *bs, int ret; assert(!(flags & ~BDRV_REQ_MASK)); + assert(!(flags & BDRV_REQ_NO_FALLBACK)); if (!drv) { return -ENOMEDIUM; @@ -1160,7 +1144,6 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, void *bounce_buffer; BlockDriver *drv = bs->drv; - struct iovec iov; QEMUIOVector local_qiov; int64_t cluster_offset; int64_t cluster_bytes; @@ -1225,9 +1208,8 @@ static int coroutine_fn bdrv_co_do_copy_on_readv(BdrvChild *child, if (ret <= 0) { /* Must copy-on-read; use the bounce buffer */ - iov.iov_base = bounce_buffer; - iov.iov_len = pnum = MIN(pnum, MAX_BOUNCE_BUFFER); - qemu_iovec_init_external(&local_qiov, &iov, 1); + pnum = MIN(pnum, MAX_BOUNCE_BUFFER); + qemu_iovec_init_buf(&local_qiov, bounce_buffer, pnum); ret = bdrv_driver_preadv(bs, cluster_offset, pnum, &local_qiov, 0); @@ -1472,7 +1454,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, { BlockDriver *drv = bs->drv; QEMUIOVector qiov; - struct iovec iov = {0}; + void *buf = NULL; int ret = 0; bool need_flush = false; int head = 0; @@ -1487,6 +1469,10 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, return -ENOMEDIUM; } + if ((flags & ~bs->supported_zero_flags) & BDRV_REQ_NO_FALLBACK) { + return -ENOTSUP; + } + assert(alignment % bs->bl.request_alignment == 0); head = offset % alignment; tail = (offset + bytes) % alignment; @@ -1530,7 +1516,7 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, assert(!bs->supported_zero_flags); } - if (ret == -ENOTSUP) { + if (ret == -ENOTSUP && !(flags & BDRV_REQ_NO_FALLBACK)) { /* Fall back to bounce buffer if write zeroes is unsupported */ BdrvRequestFlags write_flags = flags & ~BDRV_REQ_ZERO_WRITE; @@ -1542,16 +1528,14 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, need_flush = true; } num = MIN(num, max_transfer); - iov.iov_len = num; - if (iov.iov_base == NULL) { - iov.iov_base = qemu_try_blockalign(bs, num); - if (iov.iov_base == NULL) { + if (buf == NULL) { + buf = qemu_try_blockalign0(bs, num); + if (buf == NULL) { ret = -ENOMEM; goto fail; } - memset(iov.iov_base, 0, num); } - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, buf, num); ret = bdrv_driver_pwritev(bs, offset, num, &qiov, write_flags); @@ -1559,8 +1543,8 @@ static int coroutine_fn bdrv_co_do_pwrite_zeroes(BlockDriverState *bs, * all future requests. */ if (num < max_transfer) { - qemu_vfree(iov.iov_base); - iov.iov_base = NULL; + qemu_vfree(buf); + buf = NULL; } } @@ -1572,7 +1556,7 @@ fail: if (ret == 0 && need_flush) { ret = bdrv_co_flush(bs); } - qemu_vfree(iov.iov_base); + qemu_vfree(buf); return ret; } @@ -1758,7 +1742,6 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, BlockDriverState *bs = child->bs; uint8_t *buf = NULL; QEMUIOVector local_qiov; - struct iovec iov; uint64_t align = bs->bl.request_alignment; unsigned int head_padding_bytes, tail_padding_bytes; int ret = 0; @@ -1770,11 +1753,7 @@ static int coroutine_fn bdrv_co_do_zero_pwritev(BdrvChild *child, assert(flags & BDRV_REQ_ZERO_WRITE); if (head_padding_bytes || tail_padding_bytes) { buf = qemu_blockalign(bs, align); - iov = (struct iovec) { - .iov_base = buf, - .iov_len = align, - }; - qemu_iovec_init_external(&local_qiov, &iov, 1); + qemu_iovec_init_buf(&local_qiov, buf, align); } if (head_padding_bytes) { uint64_t zero_bytes = MIN(bytes, align - head_padding_bytes); @@ -1880,17 +1859,12 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, if (offset & (align - 1)) { QEMUIOVector head_qiov; - struct iovec head_iov; mark_request_serialising(&req, align); wait_serialising_requests(&req); head_buf = qemu_blockalign(bs, align); - head_iov = (struct iovec) { - .iov_base = head_buf, - .iov_len = align, - }; - qemu_iovec_init_external(&head_qiov, &head_iov, 1); + qemu_iovec_init_buf(&head_qiov, head_buf, align); bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_HEAD); ret = bdrv_aligned_preadv(child, &req, offset & ~(align - 1), align, @@ -1919,7 +1893,6 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, if ((offset + bytes) & (align - 1)) { QEMUIOVector tail_qiov; - struct iovec tail_iov; size_t tail_bytes; bool waited; @@ -1928,11 +1901,7 @@ int coroutine_fn bdrv_co_pwritev(BdrvChild *child, assert(!waited || !use_local_qiov); tail_buf = qemu_blockalign(bs, align); - tail_iov = (struct iovec) { - .iov_base = tail_buf, - .iov_len = align, - }; - qemu_iovec_init_external(&tail_qiov, &tail_iov, 1); + qemu_iovec_init_buf(&tail_qiov, tail_buf, align); bdrv_debug_event(bs, BLKDBG_PWRITEV_RMW_TAIL); ret = bdrv_aligned_preadv(child, &req, (offset + bytes) & ~(align - 1), @@ -2275,6 +2244,7 @@ static void coroutine_fn bdrv_block_status_above_co_entry(void *opaque) data->offset, data->bytes, data->pnum, data->map, data->file); data->done = true; + aio_wait_kick(); } /* @@ -2434,6 +2404,7 @@ static void coroutine_fn bdrv_co_rw_vmstate_entry(void *opaque) { BdrvVmstateCo *co = opaque; co->ret = bdrv_co_rw_vmstate(co->bs, co->qiov, co->pos, co->is_read); + aio_wait_kick(); } static inline int @@ -2461,15 +2432,9 @@ bdrv_rw_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos, int bdrv_save_vmstate(BlockDriverState *bs, const uint8_t *buf, int64_t pos, int size) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = (void *) buf, - .iov_len = size, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); int ret; - qemu_iovec_init_external(&qiov, &iov, 1); - ret = bdrv_writev_vmstate(bs, &qiov, pos); if (ret < 0) { return ret; @@ -2486,14 +2451,9 @@ int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos) int bdrv_load_vmstate(BlockDriverState *bs, uint8_t *buf, int64_t pos, int size) { - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = buf, - .iov_len = size, - }; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, size); int ret; - qemu_iovec_init_external(&qiov, &iov, 1); ret = bdrv_readv_vmstate(bs, &qiov, pos); if (ret < 0) { return ret; @@ -2555,6 +2515,7 @@ static void coroutine_fn bdrv_flush_co_entry(void *opaque) FlushCo *rwco = opaque; rwco->ret = bdrv_co_flush(rwco->bs); + aio_wait_kick(); } int coroutine_fn bdrv_co_flush(BlockDriverState *bs) @@ -2700,6 +2661,7 @@ static void coroutine_fn bdrv_pdiscard_co_entry(void *opaque) DiscardCo *rwco = opaque; rwco->ret = bdrv_co_pdiscard(rwco->child, rwco->offset, rwco->bytes); + aio_wait_kick(); } int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int bytes) @@ -2993,6 +2955,10 @@ static int coroutine_fn bdrv_co_copy_range_internal( BdrvTrackedRequest req; int ret; + /* TODO We can support BDRV_REQ_NO_FALLBACK here */ + assert(!(read_flags & BDRV_REQ_NO_FALLBACK)); + assert(!(write_flags & BDRV_REQ_NO_FALLBACK)); + if (!dst || !dst->bs) { return -ENOMEDIUM; } @@ -3213,6 +3179,7 @@ static void coroutine_fn bdrv_truncate_co_entry(void *opaque) TruncateCo *tco = opaque; tco->ret = bdrv_co_truncate(tco->child, tco->offset, tco->prealloc, tco->errp); + aio_wait_kick(); } int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, @@ -3232,7 +3199,7 @@ int bdrv_truncate(BdrvChild *child, int64_t offset, PreallocMode prealloc, bdrv_truncate_co_entry(&tco); } else { co = qemu_coroutine_create(bdrv_truncate_co_entry, &tco); - qemu_coroutine_enter(co); + bdrv_coroutine_enter(child->bs, co); BDRV_POLL_WHILE(child->bs, tco.ret == NOT_DONE); } diff --git a/block/iscsi.c b/block/iscsi.c index bb69faf34a..f31c612d53 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -49,7 +49,9 @@ /* Conflict between scsi/utils.h and libiscsi! :( */ #define SCSI_XFER_NONE ISCSI_XFER_NONE #include +#define inline __attribute__((gnu_inline)) /* required for libiscsi v1.9.0 */ #include +#undef inline #undef SCSI_XFER_NONE QEMU_BUILD_BUG_ON((int)SCSI_XFER_NONE != (int)ISCSI_XFER_NONE); @@ -117,7 +119,6 @@ typedef struct IscsiAIOCB { QEMUBH *bh; IscsiLun *iscsilun; struct scsi_task *task; - uint8_t *buf; int status; int64_t sector_num; int nb_sectors; @@ -125,6 +126,7 @@ typedef struct IscsiAIOCB { #ifdef __linux__ sg_io_hdr_t *ioh; #endif + bool cancelled; } IscsiAIOCB; /* libiscsi uses time_t so its enough to process events every second */ @@ -143,6 +145,8 @@ static const unsigned iscsi_retry_times[] = {8, 32, 128, 512, 2048, 8192, 32768} * unallocated. */ #define ISCSI_CHECKALLOC_THRES 64 +#ifdef __linux__ + static void iscsi_bh_cb(void *p) { @@ -150,9 +154,6 @@ iscsi_bh_cb(void *p) qemu_bh_delete(acb->bh); - g_free(acb->buf); - acb->buf = NULL; - acb->common.cb(acb->common.opaque, acb->status); if (acb->task != NULL) { @@ -173,6 +174,8 @@ iscsi_schedule_bh(IscsiAIOCB *acb) qemu_bh_schedule(acb->bh); } +#endif + static void iscsi_co_generic_bh_cb(void *opaque) { struct IscsiTask *iTask = opaque; @@ -291,14 +294,22 @@ static void iscsi_co_init_iscsitask(IscsiLun *iscsilun, struct IscsiTask *iTask) }; } +#ifdef __linux__ + +/* Called (via iscsi_service) with QemuMutex held. */ static void iscsi_abort_task_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data) { IscsiAIOCB *acb = private_data; - acb->status = -ECANCELED; - iscsi_schedule_bh(acb); + /* If the command callback hasn't been called yet, drop the task */ + if (!acb->bh) { + /* Call iscsi_aio_ioctl_cb() with SCSI_STATUS_CANCELLED */ + iscsi_scsi_cancel_task(iscsi, acb->task); + } + + qemu_aio_unref(acb); /* acquired in iscsi_aio_cancel() */ } static void @@ -307,14 +318,25 @@ iscsi_aio_cancel(BlockAIOCB *blockacb) IscsiAIOCB *acb = (IscsiAIOCB *)blockacb; IscsiLun *iscsilun = acb->iscsilun; - if (acb->status != -EINPROGRESS) { + qemu_mutex_lock(&iscsilun->mutex); + + /* If it was cancelled or completed already, our work is done here */ + if (acb->cancelled || acb->status != -EINPROGRESS) { + qemu_mutex_unlock(&iscsilun->mutex); return; } - /* send a task mgmt call to the target to cancel the task on the target */ - iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, - iscsi_abort_task_cb, acb); + acb->cancelled = true; + qemu_aio_ref(acb); /* released in iscsi_abort_task_cb() */ + + /* send a task mgmt call to the target to cancel the task on the target */ + if (iscsi_task_mgmt_abort_task_async(iscsilun->iscsi, acb->task, + iscsi_abort_task_cb, acb) < 0) { + qemu_aio_unref(acb); /* since iscsi_abort_task_cb() won't be called */ + } + + qemu_mutex_unlock(&iscsilun->mutex); } static const AIOCBInfo iscsi_aiocb_info = { @@ -322,6 +344,7 @@ static const AIOCBInfo iscsi_aiocb_info = { .cancel_async = iscsi_aio_cancel, }; +#endif static void iscsi_process_read(void *arg); static void iscsi_process_write(void *arg); @@ -348,6 +371,8 @@ static void iscsi_timed_check_events(void *opaque) { IscsiLun *iscsilun = opaque; + qemu_mutex_lock(&iscsilun->mutex); + /* check for timed out requests */ iscsi_service(iscsilun->iscsi, 0); @@ -360,6 +385,8 @@ static void iscsi_timed_check_events(void *opaque) * to return to service once this situation changes. */ iscsi_set_events(iscsilun); + qemu_mutex_unlock(&iscsilun->mutex); + timer_mod(iscsilun->event_timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + EVENT_INTERVAL); } @@ -933,8 +960,13 @@ iscsi_aio_ioctl_cb(struct iscsi_context *iscsi, int status, { IscsiAIOCB *acb = opaque; - g_free(acb->buf); - acb->buf = NULL; + if (status == SCSI_STATUS_CANCELLED) { + if (!acb->bh) { + acb->status = -ECANCELED; + iscsi_schedule_bh(acb); + } + return; + } acb->status = 0; if (status < 0) { @@ -1010,8 +1042,8 @@ static BlockAIOCB *iscsi_aio_ioctl(BlockDriverState *bs, acb->iscsilun = iscsilun; acb->bh = NULL; acb->status = -EINPROGRESS; - acb->buf = NULL; acb->ioh = buf; + acb->cancelled = false; if (req != SG_IO) { iscsi_ioctl_handle_emulated(acb, req, buf); @@ -1844,7 +1876,7 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, iscsi_set_timeout(iscsi, timeout); #else if (timeout) { - error_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); + warn_report("iSCSI: ignoring timeout value for libiscsi <1.15.0"); } #endif @@ -1878,9 +1910,11 @@ static int iscsi_open(BlockDriverState *bs, QDict *options, int flags, /* Check the write protect flag of the LUN if we want to write */ if (iscsilun->type == TYPE_DISK && (flags & BDRV_O_RDWR) && iscsilun->write_protected) { - error_setg(errp, "Cannot open a write protected LUN as read-write"); - ret = -EACCES; - goto out; + ret = bdrv_apply_auto_read_only(bs, "LUN is write protected", errp); + if (ret < 0) { + goto out; + } + flags &= ~BDRV_O_RDWR; } iscsi_readcapacity_sync(iscsilun, &local_err); @@ -2421,6 +2455,20 @@ static QemuOptsList iscsi_create_opts = { } }; +static const char *const iscsi_strong_runtime_opts[] = { + "transport", + "portal", + "target", + "user", + "password", + "password-secret", + "lun", + "initiator-name", + "header-digest", + + NULL +}; + static BlockDriver bdrv_iscsi = { .format_name = "iscsi", .protocol_name = "iscsi", @@ -2455,6 +2503,8 @@ static BlockDriver bdrv_iscsi = { .bdrv_detach_aio_context = iscsi_detach_aio_context, .bdrv_attach_aio_context = iscsi_attach_aio_context, + + .strong_runtime_opts = iscsi_strong_runtime_opts, }; #if LIBISCSI_API_VERSION >= (20160603) @@ -2492,6 +2542,8 @@ static BlockDriver bdrv_iser = { .bdrv_detach_aio_context = iscsi_detach_aio_context, .bdrv_attach_aio_context = iscsi_attach_aio_context, + + .strong_runtime_opts = iscsi_strong_runtime_opts, }; #endif diff --git a/block/linux-aio.c b/block/linux-aio.c index 19eb922fdd..d4b61fb251 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -234,9 +234,9 @@ static void qemu_laio_process_completions(LinuxAioState *s) static void qemu_laio_process_completions_and_submit(LinuxAioState *s) { + aio_context_acquire(s->aio_context); qemu_laio_process_completions(s); - aio_context_acquire(s->aio_context); if (!s->io_q.plugged && !QSIMPLEQ_EMPTY(&s->io_q.pending)) { ioq_submit(s); } @@ -384,10 +384,10 @@ static int laio_do_submit(int fd, struct qemu_laiocb *laiocb, off_t offset, switch (type) { case QEMU_AIO_WRITE: io_prep_pwritev(iocbs, fd, qiov->iov, qiov->niov, offset); - break; + break; case QEMU_AIO_READ: io_prep_preadv(iocbs, fd, qiov->iov, qiov->niov, offset); - break; + break; /* Currently Linux kernel does not support other operations */ default: fprintf(stderr, "%s: invalid AIO request type 0x%x.\n", diff --git a/block/mirror.c b/block/mirror.c index b48c3f8cf5..ff15cfb197 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -72,13 +72,15 @@ typedef struct MirrorBlockJob { unsigned long *in_flight_bitmap; int in_flight; int64_t bytes_in_flight; - QTAILQ_HEAD(MirrorOpList, MirrorOp) ops_in_flight; + QTAILQ_HEAD(, MirrorOp) ops_in_flight; int ret; bool unmap; int target_cluster_size; int max_iov; bool initial_zeroing_ongoing; int in_active_write_counter; + bool prepared; + bool in_drain; } MirrorBlockJob; typedef struct MirrorBDSOpaque { @@ -198,7 +200,6 @@ static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) { MirrorBlockJob *s = op->s; - aio_context_acquire(blk_get_aio_context(s->common.blk)); if (ret < 0) { BlockErrorAction action; @@ -208,15 +209,14 @@ static void coroutine_fn mirror_write_complete(MirrorOp *op, int ret) s->ret = ret; } } + mirror_iteration_done(op, ret); - aio_context_release(blk_get_aio_context(s->common.blk)); } static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) { MirrorBlockJob *s = op->s; - aio_context_acquire(blk_get_aio_context(s->common.blk)); if (ret < 0) { BlockErrorAction action; @@ -227,12 +227,11 @@ static void coroutine_fn mirror_read_complete(MirrorOp *op, int ret) } mirror_iteration_done(op, ret); - } else { - ret = blk_co_pwritev(s->target, op->offset, - op->qiov.size, &op->qiov, 0); - mirror_write_complete(op, ret); + return; } - aio_context_release(blk_get_aio_context(s->common.blk)); + + ret = blk_co_pwritev(s->target, op->offset, op->qiov.size, &op->qiov, 0); + mirror_write_complete(op, ret); } /* Clip bytes relative to offset to not exceed end-of-file */ @@ -279,7 +278,8 @@ static int mirror_cow_align(MirrorBlockJob *s, int64_t *offset, return ret; } -static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) +static inline void coroutine_fn +mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) { MirrorOp *op; @@ -297,7 +297,8 @@ static inline void mirror_wait_for_any_operation(MirrorBlockJob *s, bool active) abort(); } -static inline void mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) +static inline void coroutine_fn +mirror_wait_for_free_in_flight_slot(MirrorBlockJob *s) { /* Only non-active operations use up in-flight slots */ mirror_wait_for_any_operation(s, false); @@ -600,33 +601,44 @@ static void mirror_free_init(MirrorBlockJob *s) * mirror_resume() because mirror_run() will begin iterating again * when the job is resumed. */ -static void mirror_wait_for_all_io(MirrorBlockJob *s) +static void coroutine_fn mirror_wait_for_all_io(MirrorBlockJob *s) { while (s->in_flight > 0) { mirror_wait_for_free_in_flight_slot(s); } } -typedef struct { - int ret; -} MirrorExitData; - -static void mirror_exit(Job *job, void *opaque) +/** + * mirror_exit_common: handle both abort() and prepare() cases. + * for .prepare, returns 0 on success and -errno on failure. + * for .abort cases, denoted by abort = true, MUST return 0. + */ +static int mirror_exit_common(Job *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockJob *bjob = &s->common; - MirrorExitData *data = opaque; MirrorBDSOpaque *bs_opaque = s->mirror_top_bs->opaque; AioContext *replace_aio_context = NULL; BlockDriverState *src = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); BlockDriverState *mirror_top_bs = s->mirror_top_bs; Error *local_err = NULL; + bool abort = job->ret < 0; + int ret = 0; + + if (s->prepared) { + return 0; + } + s->prepared = true; + + if (bdrv_chain_contains(src, target_bs)) { + bdrv_unfreeze_backing_chain(mirror_top_bs, target_bs); + } bdrv_release_dirty_bitmap(src, s->dirty_bitmap); - /* Make sure that the source BDS doesn't go away before we called - * job_completed(). */ + /* Make sure that the source BDS doesn't go away during bdrv_replace_node, + * before we can call bdrv_drained_end */ bdrv_ref(src); bdrv_ref(mirror_top_bs); bdrv_ref(target_bs); @@ -646,13 +658,13 @@ static void mirror_exit(Job *job, void *opaque) * required before it could become a backing file of target_bs. */ bdrv_child_try_set_perm(mirror_top_bs->backing, 0, BLK_PERM_ALL, &error_abort); - if (s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { + if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { BlockDriverState *backing = s->is_none_mode ? src : s->base; if (backing_bs(target_bs) != backing) { bdrv_set_backing_hd(target_bs, backing, &local_err); if (local_err) { error_report_err(local_err); - data->ret = -EPERM; + ret = -EPERM; } } } @@ -662,24 +674,23 @@ static void mirror_exit(Job *job, void *opaque) aio_context_acquire(replace_aio_context); } - if (s->should_complete && data->ret == 0) { - BlockDriverState *to_replace = src; - if (s->to_replace) { - to_replace = s->to_replace; - } + if (s->should_complete && !abort) { + BlockDriverState *to_replace = s->to_replace ?: src; + bool ro = bdrv_is_read_only(to_replace); - if (bdrv_get_flags(target_bs) != bdrv_get_flags(to_replace)) { - bdrv_reopen(target_bs, bdrv_get_flags(to_replace), NULL); + if (ro != bdrv_is_read_only(target_bs)) { + bdrv_reopen_set_read_only(target_bs, ro, NULL); } /* The mirror job has no requests in flight any more, but we need to * drain potential other users of the BDS before changing the graph. */ + assert(s->in_drain); bdrv_drained_begin(target_bs); bdrv_replace_node(to_replace, target_bs, &local_err); bdrv_drained_end(target_bs); if (local_err) { error_report_err(local_err); - data->ret = -EPERM; + ret = -EPERM; } } if (s->to_replace) { @@ -710,15 +721,27 @@ static void mirror_exit(Job *job, void *opaque) blk_insert_bs(bjob->blk, mirror_top_bs, &error_abort); bs_opaque->job = NULL; - job_completed(job, data->ret, NULL); - g_free(data); bdrv_drained_end(src); + s->in_drain = false; bdrv_unref(mirror_top_bs); bdrv_unref(src); + + return ret; } -static void mirror_throttle(MirrorBlockJob *s) +static int mirror_prepare(Job *job) +{ + return mirror_exit_common(job); +} + +static void mirror_abort(Job *job) +{ + int ret = mirror_exit_common(job); + assert(ret == 0); +} + +static void coroutine_fn mirror_throttle(MirrorBlockJob *s) { int64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); @@ -812,10 +835,9 @@ static int mirror_flush(MirrorBlockJob *s) return ret; } -static void coroutine_fn mirror_run(void *opaque) +static int coroutine_fn mirror_run(Job *job, Error **errp) { - MirrorBlockJob *s = opaque; - MirrorExitData *data; + MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); BlockDriverState *bs = s->mirror_top_bs->backing->bs; BlockDriverState *target_bs = blk_bs(s->target); bool need_drain = true; @@ -985,10 +1007,12 @@ static void coroutine_fn mirror_run(void *opaque) */ trace_mirror_before_drain(s, cnt); + s->in_drain = true; bdrv_drained_begin(bs); cnt = bdrv_get_dirty_count(s->dirty_bitmap); if (cnt > 0 || mirror_flush(s) < 0) { bdrv_drained_end(bs); + s->in_drain = false; continue; } @@ -1035,13 +1059,12 @@ immediate_exit: g_free(s->in_flight_bitmap); bdrv_dirty_iter_free(s->dbi); - data = g_malloc(sizeof(*data)); - data->ret = ret; - if (need_drain) { + s->in_drain = true; bdrv_drained_begin(bs); } - job_defer_to_main_loop(&s->common.job, mirror_exit, data); + + return ret; } static void mirror_complete(Job *job, Error **errp) @@ -1096,7 +1119,7 @@ static void mirror_complete(Job *job, Error **errp) job_enter(job); } -static void mirror_pause(Job *job) +static void coroutine_fn mirror_pause(Job *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common.job); @@ -1106,6 +1129,16 @@ static void mirror_pause(Job *job) static bool mirror_drained_poll(BlockJob *job) { MirrorBlockJob *s = container_of(job, MirrorBlockJob, common); + + /* If the job isn't paused nor cancelled, we can't be sure that it won't + * issue more requests. We make an exception if we've reached this point + * from one of our own drain sections, to avoid a deadlock waiting for + * ourselves. + */ + if (!s->common.job.paused && !s->common.job.cancelled && !s->in_drain) { + return true; + } + return !!s->in_flight; } @@ -1138,7 +1171,9 @@ static const BlockJobDriver mirror_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = mirror_run, + .run = mirror_run, + .prepare = mirror_prepare, + .abort = mirror_abort, .pause = mirror_pause, .complete = mirror_complete, }, @@ -1154,7 +1189,9 @@ static const BlockJobDriver commit_active_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = mirror_run, + .run = mirror_run, + .prepare = mirror_prepare, + .abort = mirror_abort, .pause = mirror_pause, .complete = mirror_complete, }, @@ -1163,29 +1200,28 @@ static const BlockJobDriver commit_active_job_driver = { .drain = mirror_drain, }; -static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, - uint64_t offset, uint64_t bytes, - QEMUIOVector *qiov, int flags) +static void coroutine_fn +do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) { - BdrvDirtyBitmapIter *iter; QEMUIOVector target_qiov; - uint64_t dirty_offset; - int dirty_bytes; + uint64_t dirty_offset = offset; + uint64_t dirty_bytes; if (qiov) { qemu_iovec_init(&target_qiov, qiov->niov); } - iter = bdrv_dirty_iter_new(job->dirty_bitmap); - bdrv_set_dirty_iter(iter, offset); - while (true) { bool valid_area; int ret; bdrv_dirty_bitmap_lock(job->dirty_bitmap); - valid_area = bdrv_dirty_iter_next_area(iter, offset + bytes, - &dirty_offset, &dirty_bytes); + dirty_bytes = MIN(offset + bytes - dirty_offset, INT_MAX); + valid_area = bdrv_dirty_bitmap_next_dirty_area(job->dirty_bitmap, + &dirty_offset, + &dirty_bytes); if (!valid_area) { bdrv_dirty_bitmap_unlock(job->dirty_bitmap); break; @@ -1241,9 +1277,10 @@ static void do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, break; } } + + dirty_offset += dirty_bytes; } - bdrv_dirty_iter_free(iter); if (qiov) { qemu_iovec_destroy(&target_qiov); } @@ -1414,22 +1451,17 @@ static int coroutine_fn bdrv_mirror_top_pdiscard(BlockDriverState *bs, NULL, 0); } -static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs, QDict *opts) +static void bdrv_mirror_top_refresh_filename(BlockDriverState *bs) { if (bs->backing == NULL) { /* we can be here after failed bdrv_attach_child in * bdrv_set_backing_hd */ return; } - bdrv_refresh_filename(bs->backing->bs); pstrcpy(bs->exact_filename, sizeof(bs->exact_filename), bs->backing->bs->filename); } -static void bdrv_mirror_top_close(BlockDriverState *bs) -{ -} - static void bdrv_mirror_top_child_perm(BlockDriverState *bs, BdrvChild *c, const BdrvChildRole *role, BlockReopenQueue *reopen_queue, @@ -1456,7 +1488,6 @@ static BlockDriver bdrv_mirror_top = { .bdrv_co_flush = bdrv_mirror_top_flush, .bdrv_co_block_status = bdrv_co_block_status_from_backing, .bdrv_refresh_filename = bdrv_mirror_top_refresh_filename, - .bdrv_close = bdrv_mirror_top_close, .bdrv_child_perm = bdrv_mirror_top_child_perm, }; @@ -1499,6 +1530,11 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, buf_size = DEFAULT_MIRROR_BUF_SIZE; } + if (bs == target) { + error_setg(errp, "Can't mirror node into itself"); + return; + } + /* In the case of active commit, add dummy driver to provide consistent * reads on the top, while disabling it in the intermediate nodes, and make * the backing chain writable. */ @@ -1512,7 +1548,8 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, } mirror_top_bs->total_sectors = bs->total_sectors; mirror_top_bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED; - mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED; + mirror_top_bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | + BDRV_REQ_NO_FALLBACK; bs_opaque = g_new0(MirrorBDSOpaque, 1); mirror_top_bs->opaque = bs_opaque; bdrv_set_aio_context(mirror_top_bs, bdrv_get_aio_context(bs)); @@ -1595,6 +1632,14 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, goto fail; } + ret = block_job_add_bdrv(&s->common, "source", bs, 0, + BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | + BLK_PERM_CONSISTENT_READ, + errp); + if (ret < 0) { + goto fail; + } + /* Required permissions are already taken with blk_new() */ block_job_add_bdrv(&s->common, "target", target, 0, BLK_PERM_ALL, &error_abort); @@ -1615,6 +1660,10 @@ static void mirror_start_job(const char *job_id, BlockDriverState *bs, goto fail; } } + + if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { + goto fail; + } } QTAILQ_INIT(&s->ops_in_flight); @@ -1632,6 +1681,9 @@ fail: g_free(s->replaces); blk_unref(s->target); bs_opaque->job = NULL; + if (s->dirty_bitmap) { + bdrv_release_dirty_bitmap(bs, s->dirty_bitmap); + } job_early_fail(&s->common.job); } @@ -1644,7 +1696,8 @@ fail: void mirror_start(const char *job_id, BlockDriverState *bs, BlockDriverState *target, const char *replaces, - int64_t speed, uint32_t granularity, int64_t buf_size, + int creation_flags, int64_t speed, + uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, @@ -1660,7 +1713,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, } is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? backing_bs(bs) : NULL; - mirror_start_job(job_id, bs, JOB_DEFAULT, target, replaces, + mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, backing_mode, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, is_none_mode, base, false, @@ -1674,13 +1727,15 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque, bool auto_complete, Error **errp) { - int orig_base_flags; + bool base_read_only; Error *local_err = NULL; - orig_base_flags = bdrv_get_flags(base); + base_read_only = bdrv_is_read_only(base); - if (bdrv_reopen(base, bs->open_flags, errp)) { - return; + if (base_read_only) { + if (bdrv_reopen_set_read_only(base, false, errp) < 0) { + return; + } } mirror_start_job(job_id, bs, creation_flags, base, NULL, speed, 0, 0, @@ -1699,6 +1754,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, error_restore_flags: /* ignore error and errp for bdrv_reopen, because we want to propagate * the original error */ - bdrv_reopen(base, orig_base_flags, NULL); + if (base_read_only) { + bdrv_reopen_set_read_only(base, true, NULL); + } return; } diff --git a/block/nbd-client.c b/block/nbd-client.c index 9686ecbd5e..790ecc1ee1 100644 --- a/block/nbd-client.c +++ b/block/nbd-client.c @@ -28,6 +28,8 @@ */ #include "qemu/osdep.h" + +#include "trace.h" #include "qapi/error.h" #include "nbd-client.h" @@ -51,15 +53,13 @@ static void nbd_teardown_connection(BlockDriverState *bs) { NBDClientSession *client = nbd_get_client_session(bs); - if (!client->ioc) { /* Already closed */ - return; - } + assert(client->ioc); /* finish any pending coroutines */ qio_channel_shutdown(client->ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); - BDRV_POLL_WHILE(bs, client->read_reply_co); + BDRV_POLL_WHILE(bs, client->connection_co); nbd_client_detach_aio_context(bs); object_unref(OBJECT(client->sioc)); @@ -68,7 +68,7 @@ static void nbd_teardown_connection(BlockDriverState *bs) client->ioc = NULL; } -static coroutine_fn void nbd_read_reply_entry(void *opaque) +static coroutine_fn void nbd_connection_entry(void *opaque) { NBDClientSession *s = opaque; uint64_t i; @@ -76,10 +76,21 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) Error *local_err = NULL; while (!s->quit) { + /* + * The NBD client can only really be considered idle when it has + * yielded from qio_channel_readv_all_eof(), waiting for data. This is + * the point where the additional scheduled coroutine entry happens + * after nbd_client_attach_aio_context(). + * + * Therefore we keep an additional in_flight reference all the time and + * only drop it temporarily here. + */ assert(s->reply.handle == 0); - ret = nbd_receive_reply(s->ioc, &s->reply, &local_err); + ret = nbd_receive_reply(s->bs, s->ioc, &s->reply, &local_err); + if (local_err) { - error_report_err(local_err); + trace_nbd_read_reply_entry_fail(ret, error_get_pretty(local_err)); + error_free(local_err); } if (ret <= 0) { break; @@ -99,14 +110,14 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) } /* We're woken up again by the request itself. Note that there - * is no race between yielding and reentering read_reply_co. This + * is no race between yielding and reentering connection_co. This * is because: * * - if the request runs on the same AioContext, it is only * entered after we yield * * - if the request runs on a different AioContext, reentering - * read_reply_co happens through a bottom half, which can only + * connection_co happens through a bottom half, which can only * run after we yield. */ aio_co_wake(s->requests[i].coroutine); @@ -115,7 +126,10 @@ static coroutine_fn void nbd_read_reply_entry(void *opaque) s->quit = true; nbd_recv_coroutines_wake_all(s); - s->read_reply_co = NULL; + bdrv_dec_in_flight(s->bs); + + s->connection_co = NULL; + aio_wait_kick(); } static int nbd_co_send_request(BlockDriverState *bs, @@ -150,10 +164,7 @@ static int nbd_co_send_request(BlockDriverState *bs, rc = -EIO; goto err; } - if (!s->ioc) { - rc = -EPIPE; - goto err; - } + assert(s->ioc); if (qiov) { qio_channel_set_cork(s->ioc, true); @@ -200,7 +211,8 @@ static inline uint64_t payload_advance64(uint8_t **payload) return ldq_be_p(*payload - 8); } -static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, +static int nbd_parse_offset_hole_payload(NBDClientSession *client, + NBDStructuredReplyChunk *chunk, uint8_t *payload, uint64_t orig_offset, QEMUIOVector *qiov, Error **errp) { @@ -222,6 +234,10 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, " region"); return -EINVAL; } + if (client->info.min_block && + !QEMU_IS_ALIGNED(hole_size, client->info.min_block)) { + trace_nbd_structured_read_compliance("hole"); + } qemu_iovec_memset(qiov, offset - orig_offset, 0, hole_size); @@ -229,8 +245,8 @@ static int nbd_parse_offset_hole_payload(NBDStructuredReplyChunk *chunk, } /* nbd_parse_blockstatus_payload - * support only one extent in reply and only for - * base:allocation context + * Based on our request, we expect only one extent in reply, for the + * base:allocation context. */ static int nbd_parse_blockstatus_payload(NBDClientSession *client, NBDStructuredReplyChunk *chunk, @@ -239,36 +255,69 @@ static int nbd_parse_blockstatus_payload(NBDClientSession *client, { uint32_t context_id; - if (chunk->length != sizeof(context_id) + sizeof(*extent)) { + /* The server succeeded, so it must have sent [at least] one extent */ + if (chunk->length < sizeof(context_id) + sizeof(*extent)) { error_setg(errp, "Protocol error: invalid payload for " "NBD_REPLY_TYPE_BLOCK_STATUS"); return -EINVAL; } context_id = payload_advance32(&payload); - if (client->info.meta_base_allocation_id != context_id) { + if (client->info.context_id != context_id) { error_setg(errp, "Protocol error: unexpected context id %d for " "NBD_REPLY_TYPE_BLOCK_STATUS, when negotiated context " "id is %d", context_id, - client->info.meta_base_allocation_id); + client->info.context_id); return -EINVAL; } extent->length = payload_advance32(&payload); extent->flags = payload_advance32(&payload); - if (extent->length == 0 || - (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, - client->info.min_block))) { + if (extent->length == 0) { error_setg(errp, "Protocol error: server sent status chunk with " - "invalid length"); + "zero length"); return -EINVAL; } - /* The server is allowed to send us extra information on the final - * extent; just clamp it to the length we requested. */ + /* + * A server sending unaligned block status is in violation of the + * protocol, but as qemu-nbd 3.1 is such a server (at least for + * POSIX files that are not a multiple of 512 bytes, since qemu + * rounds files up to 512-byte multiples but lseek(SEEK_HOLE) + * still sees an implicit hole beyond the real EOF), it's nicer to + * work around the misbehaving server. If the request included + * more than the final unaligned block, truncate it back to an + * aligned result; if the request was only the final block, round + * up to the full block and change the status to fully-allocated + * (always a safe status, even if it loses information). + */ + if (client->info.min_block && !QEMU_IS_ALIGNED(extent->length, + client->info.min_block)) { + trace_nbd_parse_blockstatus_compliance("extent length is unaligned"); + if (extent->length > client->info.min_block) { + extent->length = QEMU_ALIGN_DOWN(extent->length, + client->info.min_block); + } else { + extent->length = client->info.min_block; + extent->flags = 0; + } + } + + /* + * We used NBD_CMD_FLAG_REQ_ONE, so the server should not have + * sent us any more than one extent, nor should it have included + * status beyond our request in that extent. However, it's easy + * enough to ignore the server's noncompliance without killing the + * connection; just ignore trailing extents, and clamp things to + * the length of our request. + */ + if (chunk->length > sizeof(context_id) + sizeof(*extent)) { + trace_nbd_parse_blockstatus_compliance("more than one extent"); + } if (extent->length > orig_length) { extent->length = orig_length; + trace_nbd_parse_blockstatus_compliance("extent length too large"); } return 0; @@ -334,10 +383,9 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s, return -EINVAL; } - if (nbd_read(s->ioc, &offset, sizeof(offset), errp) < 0) { + if (nbd_read64(s->ioc, &offset, "OFFSET_DATA offset", errp) < 0) { return -EIO; } - be64_to_cpus(&offset); data_size = chunk->length - sizeof(offset); assert(data_size); @@ -347,6 +395,9 @@ static int nbd_co_receive_offset_data_payload(NBDClientSession *s, " region"); return -EINVAL; } + if (s->info.min_block && !QEMU_IS_ALIGNED(data_size, s->info.min_block)) { + trace_nbd_structured_read_compliance("data"); + } qemu_iovec_init(&sub_qiov, qiov->niov); qemu_iovec_concat(&sub_qiov, qiov, offset - orig_offset, data_size); @@ -384,7 +435,7 @@ static coroutine_fn int nbd_co_receive_structured_payload( } *payload = g_new(char, len); - ret = nbd_read(s->ioc, *payload, len, errp); + ret = nbd_read(s->ioc, *payload, len, "structured payload", errp); if (ret < 0) { g_free(*payload); *payload = NULL; @@ -422,14 +473,15 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } *request_ret = 0; - /* Wait until we're woken up by nbd_read_reply_entry. */ + /* Wait until we're woken up by nbd_connection_entry. */ s->requests[i].receiving = true; qemu_coroutine_yield(); s->requests[i].receiving = false; - if (!s->ioc || s->quit) { + if (s->quit) { error_setg(errp, "Connection closed"); return -EIO; } + assert(s->ioc); assert(s->reply.handle == handle); @@ -496,30 +548,29 @@ static coroutine_fn int nbd_co_do_receive_one_chunk( } /* nbd_co_receive_one_chunk - * Read reply, wake up read_reply_co and set s->quit if needed. + * Read reply, wake up connection_co and set s->quit if needed. * Return value is a fatal error code or normal nbd reply error code */ static coroutine_fn int nbd_co_receive_one_chunk( NBDClientSession *s, uint64_t handle, bool only_structured, - QEMUIOVector *qiov, NBDReply *reply, void **payload, Error **errp) + int *request_ret, QEMUIOVector *qiov, NBDReply *reply, void **payload, + Error **errp) { - int request_ret; int ret = nbd_co_do_receive_one_chunk(s, handle, only_structured, - &request_ret, qiov, payload, errp); + request_ret, qiov, payload, errp); if (ret < 0) { s->quit = true; } else { - /* For assert at loop start in nbd_read_reply_entry */ + /* For assert at loop start in nbd_connection_entry */ if (reply) { *reply = s->reply; } s->reply.handle = 0; - ret = request_ret; } - if (s->read_reply_co) { - aio_co_wake(s->read_reply_co); + if (s->connection_co) { + aio_co_wake(s->connection_co); } return ret; @@ -527,22 +578,17 @@ static coroutine_fn int nbd_co_receive_one_chunk( typedef struct NBDReplyChunkIter { int ret; - bool fatal; + int request_ret; Error *err; bool done, only_structured; } NBDReplyChunkIter; -static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, - int ret, Error **local_err) +static void nbd_iter_channel_error(NBDReplyChunkIter *iter, + int ret, Error **local_err) { assert(ret < 0); - if ((fatal && !iter->fatal) || iter->ret == 0) { - if (iter->ret != 0) { - error_free(iter->err); - iter->err = NULL; - } - iter->fatal = fatal; + if (!iter->ret) { iter->ret = ret; error_propagate(&iter->err, *local_err); } else { @@ -552,6 +598,15 @@ static void nbd_iter_error(NBDReplyChunkIter *iter, bool fatal, *local_err = NULL; } +static void nbd_iter_request_error(NBDReplyChunkIter *iter, int ret) +{ + assert(ret < 0); + + if (!iter->request_ret) { + iter->request_ret = ret; + } +} + /* NBD_FOREACH_REPLY_CHUNK */ #define NBD_FOREACH_REPLY_CHUNK(s, iter, handle, structured, \ @@ -567,13 +622,13 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, QEMUIOVector *qiov, NBDReply *reply, void **payload) { - int ret; + int ret, request_ret; NBDReply local_reply; NBDStructuredReplyChunk *chunk; Error *local_err = NULL; if (s->quit) { error_setg(&local_err, "Connection closed"); - nbd_iter_error(iter, true, -EIO, &local_err); + nbd_iter_channel_error(iter, -EIO, &local_err); goto break_loop; } @@ -587,14 +642,16 @@ static bool nbd_reply_chunk_iter_receive(NBDClientSession *s, } ret = nbd_co_receive_one_chunk(s, handle, iter->only_structured, - qiov, reply, payload, &local_err); + &request_ret, qiov, reply, payload, + &local_err); if (ret < 0) { - /* If it is a fatal error s->quit is set by nbd_co_receive_one_chunk */ - nbd_iter_error(iter, s->quit, ret, &local_err); + nbd_iter_channel_error(iter, ret, &local_err); + } else if (request_ret < 0) { + nbd_iter_request_error(iter, request_ret); } /* Do not execute the body of NBD_FOREACH_REPLY_CHUNK for simple reply. */ - if (nbd_reply_is_simple(&s->reply) || s->quit) { + if (nbd_reply_is_simple(reply) || s->quit) { goto break_loop; } @@ -627,7 +684,7 @@ break_loop: } static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, - Error **errp) + int *request_ret, Error **errp) { NBDReplyChunkIter iter; @@ -636,12 +693,13 @@ static int nbd_co_receive_return_code(NBDClientSession *s, uint64_t handle, } error_propagate(errp, iter.err); + *request_ret = iter.request_ret; return iter.ret; } static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, uint64_t offset, QEMUIOVector *qiov, - Error **errp) + int *request_ret, Error **errp) { NBDReplyChunkIter iter; NBDReply reply; @@ -662,11 +720,11 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, * in qiov */ break; case NBD_REPLY_TYPE_OFFSET_HOLE: - ret = nbd_parse_offset_hole_payload(&reply.structured, payload, + ret = nbd_parse_offset_hole_payload(s, &reply.structured, payload, offset, qiov, &local_err); if (ret < 0) { s->quit = true; - nbd_iter_error(&iter, true, ret, &local_err); + nbd_iter_channel_error(&iter, ret, &local_err); } break; default: @@ -676,7 +734,7 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, error_setg(&local_err, "Unexpected reply type: %d (%s) for CMD_READ", chunk->type, nbd_reply_type_lookup(chunk->type)); - nbd_iter_error(&iter, true, -EINVAL, &local_err); + nbd_iter_channel_error(&iter, -EINVAL, &local_err); } } @@ -685,12 +743,14 @@ static int nbd_co_receive_cmdread_reply(NBDClientSession *s, uint64_t handle, } error_propagate(errp, iter.err); + *request_ret = iter.request_ret; return iter.ret; } static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, uint64_t handle, uint64_t length, - NBDExtent *extent, Error **errp) + NBDExtent *extent, + int *request_ret, Error **errp) { NBDReplyChunkIter iter; NBDReply reply; @@ -699,9 +759,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, bool received = false; assert(!extent->length); - NBD_FOREACH_REPLY_CHUNK(s, iter, handle, s->info.structured_reply, - NULL, &reply, &payload) - { + NBD_FOREACH_REPLY_CHUNK(s, iter, handle, false, NULL, &reply, &payload) { int ret; NBDStructuredReplyChunk *chunk = &reply.structured; @@ -712,7 +770,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, if (received) { s->quit = true; error_setg(&local_err, "Several BLOCK_STATUS chunks in reply"); - nbd_iter_error(&iter, true, -EINVAL, &local_err); + nbd_iter_channel_error(&iter, -EINVAL, &local_err); } received = true; @@ -721,7 +779,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, &local_err); if (ret < 0) { s->quit = true; - nbd_iter_error(&iter, true, ret, &local_err); + nbd_iter_channel_error(&iter, ret, &local_err); } break; default: @@ -731,7 +789,7 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, "Unexpected reply type: %d (%s) " "for CMD_BLOCK_STATUS", chunk->type, nbd_reply_type_lookup(chunk->type)); - nbd_iter_error(&iter, true, -EINVAL, &local_err); + nbd_iter_channel_error(&iter, -EINVAL, &local_err); } } @@ -739,21 +797,20 @@ static int nbd_co_receive_blockstatus_reply(NBDClientSession *s, payload = NULL; } - if (!extent->length && !iter.err) { - error_setg(&iter.err, - "Server did not reply with any status extents"); - if (!iter.ret) { - iter.ret = -EIO; - } + if (!extent->length && !iter.request_ret) { + error_setg(&local_err, "Server did not reply with any status extents"); + nbd_iter_channel_error(&iter, -EIO, &local_err); } + error_propagate(errp, iter.err); + *request_ret = iter.request_ret; return iter.ret; } static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, QEMUIOVector *write_qiov) { - int ret; + int ret, request_ret; Error *local_err = NULL; NBDClientSession *client = nbd_get_client_session(bs); @@ -769,17 +826,22 @@ static int nbd_co_request(BlockDriverState *bs, NBDRequest *request, return ret; } - ret = nbd_co_receive_return_code(client, request->handle, &local_err); + ret = nbd_co_receive_return_code(client, request->handle, + &request_ret, &local_err); if (local_err) { - error_report_err(local_err); + trace_nbd_co_request_fail(request->from, request->len, request->handle, + request->flags, request->type, + nbd_cmd_lookup(request->type), + ret, error_get_pretty(local_err)); + error_free(local_err); } - return ret; + return ret ? ret : request_ret; } int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov, int flags) { - int ret; + int ret, request_ret; Error *local_err = NULL; NBDClientSession *client = nbd_get_client_session(bs); NBDRequest request = { @@ -794,17 +856,40 @@ int nbd_client_co_preadv(BlockDriverState *bs, uint64_t offset, if (!bytes) { return 0; } + /* + * Work around the fact that the block layer doesn't do + * byte-accurate sizing yet - if the read exceeds the server's + * advertised size because the block layer rounded size up, then + * truncate the request to the server and tail-pad with zero. + */ + if (offset >= client->info.size) { + assert(bytes < BDRV_SECTOR_SIZE); + qemu_iovec_memset(qiov, 0, 0, bytes); + return 0; + } + if (offset + bytes > client->info.size) { + uint64_t slop = offset + bytes - client->info.size; + + assert(slop < BDRV_SECTOR_SIZE); + qemu_iovec_memset(qiov, bytes - slop, 0, slop); + request.len -= slop; + } + ret = nbd_co_send_request(bs, &request, NULL); if (ret < 0) { return ret; } ret = nbd_co_receive_cmdread_reply(client, request.handle, offset, qiov, - &local_err); + &request_ret, &local_err); if (local_err) { - error_report_err(local_err); + trace_nbd_co_request_fail(request.from, request.len, request.handle, + request.flags, request.type, + nbd_cmd_lookup(request.type), + ret, error_get_pretty(local_err)); + error_free(local_err); } - return ret; + return ret ? ret : request_ret; } int nbd_client_co_pwritev(BlockDriverState *bs, uint64_t offset, @@ -898,7 +983,7 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, int64_t *pnum, int64_t *map, BlockDriverState **file) { - int64_t ret; + int ret, request_ret; NBDExtent extent = { 0 }; NBDClientSession *client = nbd_get_client_session(bs); Error *local_err = NULL; @@ -908,33 +993,60 @@ int coroutine_fn nbd_client_co_block_status(BlockDriverState *bs, .from = offset, .len = MIN(MIN_NON_ZERO(QEMU_ALIGN_DOWN(INT_MAX, bs->bl.request_alignment), - client->info.max_block), bytes), + client->info.max_block), + MIN(bytes, client->info.size - offset)), .flags = NBD_CMD_FLAG_REQ_ONE, }; if (!client->info.base_allocation) { *pnum = bytes; - return BDRV_BLOCK_DATA; + *map = offset; + *file = bs; + return BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID; } + /* + * Work around the fact that the block layer doesn't do + * byte-accurate sizing yet - if the status request exceeds the + * server's advertised size because the block layer rounded size + * up, we truncated the request to the server (above), or are + * called on just the hole. + */ + if (offset >= client->info.size) { + *pnum = bytes; + assert(bytes < BDRV_SECTOR_SIZE); + /* Intentionally don't report offset_valid for the hole */ + return BDRV_BLOCK_ZERO; + } + + if (client->info.min_block) { + assert(QEMU_IS_ALIGNED(request.len, client->info.min_block)); + } ret = nbd_co_send_request(bs, &request, NULL); if (ret < 0) { return ret; } ret = nbd_co_receive_blockstatus_reply(client, request.handle, bytes, - &extent, &local_err); + &extent, &request_ret, &local_err); if (local_err) { - error_report_err(local_err); + trace_nbd_co_request_fail(request.from, request.len, request.handle, + request.flags, request.type, + nbd_cmd_lookup(request.type), + ret, error_get_pretty(local_err)); + error_free(local_err); } - if (ret < 0) { - return ret; + if (ret < 0 || request_ret < 0) { + return ret ? ret : request_ret; } assert(extent.length); *pnum = extent.length; + *map = offset; + *file = bs; return (extent.flags & NBD_STATE_HOLE ? 0 : BDRV_BLOCK_DATA) | - (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0); + (extent.flags & NBD_STATE_ZERO ? BDRV_BLOCK_ZERO : 0) | + BDRV_BLOCK_OFFSET_VALID; } void nbd_client_detach_aio_context(BlockDriverState *bs) @@ -943,12 +1055,30 @@ void nbd_client_detach_aio_context(BlockDriverState *bs) qio_channel_detach_aio_context(QIO_CHANNEL(client->ioc)); } +static void nbd_client_attach_aio_context_bh(void *opaque) +{ + BlockDriverState *bs = opaque; + NBDClientSession *client = nbd_get_client_session(bs); + + /* The node is still drained, so we know the coroutine has yielded in + * nbd_read_eof(), the only place where bs->in_flight can reach 0, or it is + * entered for the first time. Both places are safe for entering the + * coroutine.*/ + qemu_aio_coroutine_enter(bs->aio_context, client->connection_co); + bdrv_dec_in_flight(bs); +} + void nbd_client_attach_aio_context(BlockDriverState *bs, AioContext *new_context) { NBDClientSession *client = nbd_get_client_session(bs); qio_channel_attach_aio_context(QIO_CHANNEL(client->ioc), new_context); - aio_co_schedule(new_context, client->read_reply_co); + + bdrv_inc_in_flight(bs); + + /* Need to wait here for the BH to run because the BH must run while the + * node is still drained. */ + aio_wait_bh_oneshot(new_context, nbd_client_attach_aio_context_bh, bs); } void nbd_client_close(BlockDriverState *bs) @@ -956,26 +1086,55 @@ void nbd_client_close(BlockDriverState *bs) NBDClientSession *client = nbd_get_client_session(bs); NBDRequest request = { .type = NBD_CMD_DISC }; - if (client->ioc == NULL) { - return; - } + assert(client->ioc); nbd_send_request(client->ioc, &request); nbd_teardown_connection(bs); } -int nbd_client_init(BlockDriverState *bs, - QIOChannelSocket *sioc, - const char *export, - QCryptoTLSCreds *tlscreds, - const char *hostname, - const char *x_dirty_bitmap, - Error **errp) +static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, + Error **errp) +{ + QIOChannelSocket *sioc; + Error *local_err = NULL; + + sioc = qio_channel_socket_new(); + qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); + + qio_channel_socket_connect_sync(sioc, saddr, &local_err); + if (local_err) { + object_unref(OBJECT(sioc)); + error_propagate(errp, local_err); + return NULL; + } + + qio_channel_set_delay(QIO_CHANNEL(sioc), false); + + return sioc; +} + +static int nbd_client_connect(BlockDriverState *bs, + SocketAddress *saddr, + const char *export, + QCryptoTLSCreds *tlscreds, + const char *hostname, + const char *x_dirty_bitmap, + Error **errp) { NBDClientSession *client = nbd_get_client_session(bs); int ret; + /* + * establish TCP connection, return error if it fails + * TODO: Configurable retry-until-timeout behaviour. + */ + QIOChannelSocket *sioc = nbd_establish_connection(saddr, errp); + + if (!sioc) { + return -ECONNREFUSED; + } + /* NBD handshake */ logout("session init %s\n", export); qio_channel_set_blocking(QIO_CHANNEL(sioc), true, NULL); @@ -984,19 +1143,27 @@ int nbd_client_init(BlockDriverState *bs, client->info.structured_reply = true; client->info.base_allocation = true; client->info.x_dirty_bitmap = g_strdup(x_dirty_bitmap); - ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), export, - tlscreds, hostname, + client->info.name = g_strdup(export ?: ""); + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), tlscreds, hostname, &client->ioc, &client->info, errp); g_free(client->info.x_dirty_bitmap); + g_free(client->info.name); if (ret < 0) { logout("Failed to negotiate with the NBD server\n"); + object_unref(OBJECT(sioc)); return ret; } - if (client->info.flags & NBD_FLAG_READ_ONLY && - !bdrv_is_read_only(bs)) { - error_setg(errp, - "request for write access conflicts with read-only export"); - return -EACCES; + if (x_dirty_bitmap && !client->info.base_allocation) { + error_setg(errp, "requested x-dirty-bitmap %s not found", + x_dirty_bitmap); + ret = -EINVAL; + goto fail; + } + if (client->info.flags & NBD_FLAG_READ_ONLY) { + ret = bdrv_apply_auto_read_only(bs, "NBD export is read-only", errp); + if (ret < 0) { + goto fail; + } } if (client->info.flags & NBD_FLAG_SEND_FUA) { bs->supported_write_flags = BDRV_REQ_FUA; @@ -1006,10 +1173,7 @@ int nbd_client_init(BlockDriverState *bs, bs->supported_zero_flags |= BDRV_REQ_MAY_UNMAP; } - qemu_co_mutex_init(&client->send_mutex); - qemu_co_queue_init(&client->free_sema); client->sioc = sioc; - object_ref(OBJECT(client->sioc)); if (!client->ioc) { client->ioc = QIO_CHANNEL(sioc); @@ -1019,9 +1183,44 @@ int nbd_client_init(BlockDriverState *bs, /* Now that we're connected, set the socket to be non-blocking and * kick the reply mechanism. */ qio_channel_set_blocking(QIO_CHANNEL(sioc), false, NULL); - client->read_reply_co = qemu_coroutine_create(nbd_read_reply_entry, client); + client->connection_co = qemu_coroutine_create(nbd_connection_entry, client); + bdrv_inc_in_flight(bs); nbd_client_attach_aio_context(bs, bdrv_get_aio_context(bs)); logout("Established connection with NBD server\n"); return 0; + + fail: + /* + * We have connected, but must fail for other reasons. The + * connection is still blocking; send NBD_CMD_DISC as a courtesy + * to the server. + */ + { + NBDRequest request = { .type = NBD_CMD_DISC }; + + nbd_send_request(client->ioc ?: QIO_CHANNEL(sioc), &request); + + object_unref(OBJECT(sioc)); + + return ret; + } +} + +int nbd_client_init(BlockDriverState *bs, + SocketAddress *saddr, + const char *export, + QCryptoTLSCreds *tlscreds, + const char *hostname, + const char *x_dirty_bitmap, + Error **errp) +{ + NBDClientSession *client = nbd_get_client_session(bs); + + client->bs = bs; + qemu_co_mutex_init(&client->send_mutex); + qemu_co_queue_init(&client->free_sema); + + return nbd_client_connect(bs, saddr, export, tlscreds, hostname, + x_dirty_bitmap, errp); } diff --git a/block/nbd-client.h b/block/nbd-client.h index cfc90550b9..09e03013d2 100644 --- a/block/nbd-client.h +++ b/block/nbd-client.h @@ -20,7 +20,7 @@ typedef struct { Coroutine *coroutine; uint64_t offset; /* original offset of the request */ - bool receiving; /* waiting for read_reply_co? */ + bool receiving; /* waiting for connection_co? */ } NBDClientRequest; typedef struct NBDClientSession { @@ -30,18 +30,19 @@ typedef struct NBDClientSession { CoMutex send_mutex; CoQueue free_sema; - Coroutine *read_reply_co; + Coroutine *connection_co; int in_flight; NBDClientRequest requests[MAX_NBD_REQUESTS]; NBDReply reply; + BlockDriverState *bs; bool quit; } NBDClientSession; NBDClientSession *nbd_get_client_session(BlockDriverState *bs); int nbd_client_init(BlockDriverState *bs, - QIOChannelSocket *sock, + SocketAddress *saddr, const char *export_name, QCryptoTLSCreds *tlscreds, const char *hostname, diff --git a/block/nbd.c b/block/nbd.c index e87699fb73..208be59602 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -295,30 +295,6 @@ NBDClientSession *nbd_get_client_session(BlockDriverState *bs) return &s->client; } -static QIOChannelSocket *nbd_establish_connection(SocketAddress *saddr, - Error **errp) -{ - QIOChannelSocket *sioc; - Error *local_err = NULL; - - sioc = qio_channel_socket_new(); - qio_channel_set_name(QIO_CHANNEL(sioc), "nbd-client"); - - qio_channel_socket_connect_sync(sioc, - saddr, - &local_err); - if (local_err) { - object_unref(OBJECT(sioc)); - error_propagate(errp, local_err); - return NULL; - } - - qio_channel_set_delay(QIO_CHANNEL(sioc), false); - - return sioc; -} - - static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) { Object *obj; @@ -394,7 +370,6 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, BDRVNBDState *s = bs->opaque; QemuOpts *opts = NULL; Error *local_err = NULL; - QIOChannelSocket *sioc = NULL; QCryptoTLSCreds *tlscreds = NULL; const char *hostname = NULL; int ret = -EINVAL; @@ -434,22 +409,11 @@ static int nbd_open(BlockDriverState *bs, QDict *options, int flags, hostname = s->saddr->u.inet.host; } - /* establish TCP connection, return error if it fails - * TODO: Configurable retry-until-timeout behaviour. - */ - sioc = nbd_establish_connection(s->saddr, errp); - if (!sioc) { - ret = -ECONNREFUSED; - goto error; - } - /* NBD handshake */ - ret = nbd_client_init(bs, sioc, s->export, tlscreds, hostname, + ret = nbd_client_init(bs, s->saddr, s->export, tlscreds, hostname, qemu_opt_get(opts, "x-dirty-bitmap"), errp); + error: - if (sioc) { - object_unref(OBJECT(sioc)); - } if (tlscreds) { object_unref(OBJECT(tlscreds)); } @@ -473,7 +437,24 @@ static void nbd_refresh_limits(BlockDriverState *bs, Error **errp) uint32_t min = s->info.min_block; uint32_t max = MIN_NON_ZERO(NBD_MAX_BUFFER_SIZE, s->info.max_block); - bs->bl.request_alignment = min ? min : BDRV_SECTOR_SIZE; + /* + * If the server did not advertise an alignment: + * - a size that is not sector-aligned implies that an alignment + * of 1 can be used to access those tail bytes + * - advertisement of block status requires an alignment of 1, so + * that we don't violate block layer constraints that block + * status is always aligned (as we can't control whether the + * server will report sub-sector extents, such as a hole at EOF + * on an unaligned POSIX file) + * - otherwise, assume the server is so old that we are safer avoiding + * sub-sector requests + */ + if (!min) { + min = (!QEMU_IS_ALIGNED(s->info.size, BDRV_SECTOR_SIZE) || + s->info.base_allocation) ? 1 : BDRV_SECTOR_SIZE; + } + + bs->bl.request_alignment = min; bs->bl.max_pdiscard = max; bs->bl.max_pwrite_zeroes = max; bs->bl.max_transfer = max; @@ -513,12 +494,9 @@ static void nbd_attach_aio_context(BlockDriverState *bs, nbd_client_attach_aio_context(bs, new_context); } -static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) +static void nbd_refresh_filename(BlockDriverState *bs) { BDRVNBDState *s = bs->opaque; - QDict *opts = qdict_new(); - QObject *saddr_qdict; - Visitor *ov; const char *host = NULL, *port = NULL, *path = NULL; if (s->saddr->type == SOCKET_ADDRESS_TYPE_INET) { @@ -531,8 +509,6 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) path = s->saddr->u.q_unix.path; } /* else can't represent as pseudo-filename */ - qdict_put_str(opts, "driver", "nbd"); - if (path && s->export) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nbd+unix:///%s?socket=%s", s->export, path); @@ -546,24 +522,29 @@ static void nbd_refresh_filename(BlockDriverState *bs, QDict *options) snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nbd://%s:%s", host, port); } - - ov = qobject_output_visitor_new(&saddr_qdict); - visit_type_SocketAddress(ov, NULL, &s->saddr, &error_abort); - visit_complete(ov, &saddr_qdict); - visit_free(ov); - qdict_put_obj(opts, "server", saddr_qdict); - - if (s->export) { - qdict_put_str(opts, "export", s->export); - } - if (s->tlscredsid) { - qdict_put_str(opts, "tls-creds", s->tlscredsid); - } - - qdict_flatten(opts); - bs->full_open_options = opts; } +static char *nbd_dirname(BlockDriverState *bs, Error **errp) +{ + /* The generic bdrv_dirname() implementation is able to work out some + * directory name for NBD nodes, but that would be wrong. So far there is no + * specification for how "export paths" would work, so NBD does not have + * directory names. */ + error_setg(errp, "Cannot generate a base directory for NBD nodes"); + return NULL; +} + +static const char *const nbd_strong_runtime_opts[] = { + "path", + "host", + "port", + "export", + "tls-creds", + "server.", + + NULL +}; + static BlockDriver bdrv_nbd = { .format_name = "nbd", .protocol_name = "nbd", @@ -582,6 +563,8 @@ static BlockDriver bdrv_nbd = { .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, + .bdrv_dirname = nbd_dirname, + .strong_runtime_opts = nbd_strong_runtime_opts, }; static BlockDriver bdrv_nbd_tcp = { @@ -602,6 +585,8 @@ static BlockDriver bdrv_nbd_tcp = { .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, + .bdrv_dirname = nbd_dirname, + .strong_runtime_opts = nbd_strong_runtime_opts, }; static BlockDriver bdrv_nbd_unix = { @@ -622,6 +607,8 @@ static BlockDriver bdrv_nbd_unix = { .bdrv_attach_aio_context = nbd_attach_aio_context, .bdrv_refresh_filename = nbd_refresh_filename, .bdrv_co_block_status = nbd_client_co_block_status, + .bdrv_dirname = nbd_dirname, + .strong_runtime_opts = nbd_strong_runtime_opts, }; static void bdrv_nbd_init(void) diff --git a/block/nfs.c b/block/nfs.c index eab1a2c408..531903610b 100644 --- a/block/nfs.c +++ b/block/nfs.c @@ -799,14 +799,9 @@ static int nfs_reopen_prepare(BDRVReopenState *state, return 0; } -static void nfs_refresh_filename(BlockDriverState *bs, QDict *options) +static void nfs_refresh_filename(BlockDriverState *bs) { NFSClient *client = bs->opaque; - QDict *opts = qdict_new(); - QObject *server_qdict; - Visitor *ov; - - qdict_put_str(opts, "driver", "nfs"); if (client->uid && !client->gid) { snprintf(bs->exact_filename, sizeof(bs->exact_filename), @@ -824,35 +819,20 @@ static void nfs_refresh_filename(BlockDriverState *bs, QDict *options) snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nfs://%s%s", client->server->host, client->path); } +} - ov = qobject_output_visitor_new(&server_qdict); - visit_type_NFSServer(ov, NULL, &client->server, &error_abort); - visit_complete(ov, &server_qdict); - qdict_put_obj(opts, "server", server_qdict); - qdict_put_str(opts, "path", client->path); +static char *nfs_dirname(BlockDriverState *bs, Error **errp) +{ + NFSClient *client = bs->opaque; - if (client->uid) { - qdict_put_int(opts, "user", client->uid); - } - if (client->gid) { - qdict_put_int(opts, "group", client->gid); - } - if (client->tcp_syncnt) { - qdict_put_int(opts, "tcp-syn-cnt", client->tcp_syncnt); - } - if (client->readahead) { - qdict_put_int(opts, "readahead-size", client->readahead); - } - if (client->pagecache) { - qdict_put_int(opts, "page-cache-size", client->pagecache); - } - if (client->debug) { - qdict_put_int(opts, "debug", client->debug); + if (client->uid || client->gid) { + bdrv_refresh_filename(bs); + error_setg(errp, "Cannot generate a base directory for NFS node '%s'", + bs->filename); + return NULL; } - visit_free(ov); - qdict_flatten(opts); - bs->full_open_options = opts; + return g_strdup_printf("nfs://%s%s/", client->server->host, client->path); } #ifdef LIBNFS_FEATURE_PAGECACHE @@ -864,6 +844,15 @@ static void coroutine_fn nfs_co_invalidate_cache(BlockDriverState *bs, } #endif +static const char *nfs_strong_runtime_opts[] = { + "path", + "user", + "group", + "server.", + + NULL +}; + static BlockDriver bdrv_nfs = { .format_name = "nfs", .protocol_name = "nfs", @@ -889,6 +878,9 @@ static BlockDriver bdrv_nfs = { .bdrv_detach_aio_context = nfs_detach_aio_context, .bdrv_attach_aio_context = nfs_attach_aio_context, .bdrv_refresh_filename = nfs_refresh_filename, + .bdrv_dirname = nfs_dirname, + + .strong_runtime_opts = nfs_strong_runtime_opts, #ifdef LIBNFS_FEATURE_PAGECACHE .bdrv_co_invalidate_cache = nfs_co_invalidate_cache, diff --git a/block/null.c b/block/null.c index 5d610fdfba..a322929478 100644 --- a/block/null.c +++ b/block/null.c @@ -97,10 +97,6 @@ static int null_file_open(BlockDriverState *bs, QDict *options, int flags, return ret; } -static void null_close(BlockDriverState *bs) -{ -} - static int64_t null_getlength(BlockDriverState *bs) { BDRVNullState *s = bs->opaque; @@ -243,19 +239,33 @@ static int coroutine_fn null_co_block_status(BlockDriverState *bs, return ret; } -static void null_refresh_filename(BlockDriverState *bs, QDict *opts) +static void null_refresh_filename(BlockDriverState *bs) { - qdict_del(opts, "filename"); + const QDictEntry *e; - if (!qdict_size(opts)) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://", - bs->drv->format_name); + for (e = qdict_first(bs->full_open_options); e; + e = qdict_next(bs->full_open_options, e)) + { + /* These options can be ignored */ + if (strcmp(qdict_entry_key(e), "filename") && + strcmp(qdict_entry_key(e), "driver") && + strcmp(qdict_entry_key(e), NULL_OPT_LATENCY)) + { + return; + } } - qdict_put_str(opts, "driver", bs->drv->format_name); - bs->full_open_options = qobject_ref(opts); + snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://", + bs->drv->format_name); } +static const char *const null_strong_runtime_opts[] = { + BLOCK_OPT_SIZE, + NULL_OPT_ZEROES, + + NULL +}; + static BlockDriver bdrv_null_co = { .format_name = "null-co", .protocol_name = "null-co", @@ -263,7 +273,6 @@ static BlockDriver bdrv_null_co = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_co_parse_filename, - .bdrv_close = null_close, .bdrv_getlength = null_getlength, .bdrv_co_preadv = null_co_preadv, @@ -274,6 +283,7 @@ static BlockDriver bdrv_null_co = { .bdrv_co_block_status = null_co_block_status, .bdrv_refresh_filename = null_refresh_filename, + .strong_runtime_opts = null_strong_runtime_opts, }; static BlockDriver bdrv_null_aio = { @@ -283,7 +293,6 @@ static BlockDriver bdrv_null_aio = { .bdrv_file_open = null_file_open, .bdrv_parse_filename = null_aio_parse_filename, - .bdrv_close = null_close, .bdrv_getlength = null_getlength, .bdrv_aio_preadv = null_aio_preadv, @@ -294,6 +303,7 @@ static BlockDriver bdrv_null_aio = { .bdrv_co_block_status = null_co_block_status, .bdrv_refresh_filename = null_refresh_filename, + .strong_runtime_opts = null_strong_runtime_opts, }; static void bdrv_null_init(void) diff --git a/block/nvme.c b/block/nvme.c index 6f71122bf5..0684bbd077 100644 --- a/block/nvme.c +++ b/block/nvme.c @@ -82,7 +82,7 @@ typedef volatile struct { uint8_t reserved1[0xec0]; uint8_t cmd_set_specfic[0x100]; uint32_t doorbells[]; -} QEMU_PACKED NVMeRegs; +} NVMeRegs; QEMU_BUILD_BUG_ON(offsetof(NVMeRegs, doorbells) != 0x1000); @@ -104,13 +104,16 @@ typedef struct { uint64_t nsze; /* Namespace size reported by identify command */ int nsid; /* The namespace id to read/write data. */ uint64_t max_transfer; - int plugged; + bool plugged; CoMutex dma_map_lock; CoQueue dma_flush_queue; /* Total size of mapped qiov, accessed under dma_map_lock */ int dma_map_count; + + /* PCI address (required for nvme_refresh_filename()) */ + char *device; } BDRVNVMeState; #define NVME_BLOCK_OPT_DEVICE "device" @@ -390,6 +393,7 @@ static void nvme_cmd_sync_cb(void *opaque, int ret) { int *pret = opaque; *pret = ret; + aio_wait_kick(); } static int nvme_cmd_sync(BlockDriverState *bs, NVMeQueuePair *q, @@ -489,10 +493,8 @@ static void nvme_handle_event(EventNotifier *n) BDRVNVMeState *s = container_of(n, BDRVNVMeState, irq_notifier); trace_nvme_handle_event(s); - aio_context_acquire(s->aio_context); event_notifier_test_and_clear(n); nvme_poll_queues(s); - aio_context_release(s->aio_context); } static bool nvme_add_io_queue(BlockDriverState *bs, Error **errp) @@ -558,6 +560,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, qemu_co_mutex_init(&s->dma_map_lock); qemu_co_queue_init(&s->dma_flush_queue); + s->device = g_strdup(device); s->nsid = namespace; s->aio_context = bdrv_get_aio_context(bs); ret = event_notifier_init(&s->irq_notifier, 0); @@ -569,13 +572,13 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, s->vfio = qemu_vfio_open_pci(device, errp); if (!s->vfio) { ret = -EINVAL; - goto fail; + goto out; } s->regs = qemu_vfio_pci_map_bar(s->vfio, 0, 0, NVME_BAR_SIZE, errp); if (!s->regs) { ret = -EINVAL; - goto fail; + goto out; } /* Perform initialize sequence as described in NVMe spec "7.6.1 @@ -585,7 +588,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, if (!(cap & (1ULL << 37))) { error_setg(errp, "Device doesn't support NVMe command set"); ret = -EINVAL; - goto fail; + goto out; } s->page_size = MAX(4096, 1 << (12 + ((cap >> 48) & 0xF))); @@ -603,7 +606,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, PRId64 " ms)", timeout_ms); ret = -ETIMEDOUT; - goto fail; + goto out; } } @@ -613,7 +616,7 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, s->queues[0] = nvme_create_queue_pair(bs, 0, NVME_QUEUE_SIZE, errp); if (!s->queues[0]) { ret = -EINVAL; - goto fail; + goto out; } QEMU_BUILD_BUG_ON(NVME_QUEUE_SIZE & 0xF000); s->regs->aqa = cpu_to_le32((NVME_QUEUE_SIZE << 16) | NVME_QUEUE_SIZE); @@ -633,14 +636,14 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, PRId64 " ms)", timeout_ms); ret = -ETIMEDOUT; - goto fail_queue; + goto out; } } ret = qemu_vfio_pci_init_irq(s->vfio, &s->irq_notifier, VFIO_PCI_MSIX_IRQ_INDEX, errp); if (ret) { - goto fail_queue; + goto out; } aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, false, nvme_handle_event, nvme_poll_cb); @@ -649,30 +652,15 @@ static int nvme_init(BlockDriverState *bs, const char *device, int namespace, if (local_err) { error_propagate(errp, local_err); ret = -EIO; - goto fail_handler; + goto out; } /* Set up command queues. */ if (!nvme_add_io_queue(bs, errp)) { ret = -EIO; - goto fail_handler; } - return 0; - -fail_handler: - aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, - false, NULL, NULL); -fail_queue: - nvme_free_queue_pair(bs, s->queues[0]); -fail: - g_free(s->queues); - if (s->regs) { - qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); - } - if (s->vfio) { - qemu_vfio_close(s->vfio); - } - event_notifier_cleanup(&s->irq_notifier); +out: + /* Cleaning up is done in nvme_file_open() upon error. */ return ret; } @@ -739,10 +727,14 @@ static void nvme_close(BlockDriverState *bs) for (i = 0; i < s->nr_queues; ++i) { nvme_free_queue_pair(bs, s->queues[i]); } + g_free(s->queues); aio_set_event_notifier(bdrv_get_aio_context(bs), &s->irq_notifier, false, NULL, NULL); + event_notifier_cleanup(&s->irq_notifier); qemu_vfio_pci_unmap_bar(s->vfio, 0, (void *)s->regs, 0, NVME_BAR_SIZE); qemu_vfio_close(s->vfio); + + g_free(s->device); } static int nvme_file_open(BlockDriverState *bs, QDict *options, int flags, @@ -852,7 +844,7 @@ try_map: } for (j = 0; j < qiov->iov[i].iov_len / s->page_size; j++) { - pagelist[entries++] = iova + j * s->page_size; + pagelist[entries++] = cpu_to_le64(iova + j * s->page_size); } trace_nvme_cmd_map_qiov_iov(s, i, qiov->iov[i].iov_base, qiov->iov[i].iov_len / s->page_size); @@ -865,20 +857,16 @@ try_map: case 0: abort(); case 1: - cmd->prp1 = cpu_to_le64(pagelist[0]); + cmd->prp1 = pagelist[0]; cmd->prp2 = 0; break; case 2: - cmd->prp1 = cpu_to_le64(pagelist[0]); - cmd->prp2 = cpu_to_le64(pagelist[1]);; + cmd->prp1 = pagelist[0]; + cmd->prp2 = pagelist[1]; break; default: - cmd->prp1 = cpu_to_le64(pagelist[0]); - cmd->prp2 = cpu_to_le64(req->prp_list_iova); - for (i = 0; i < entries - 1; ++i) { - pagelist[i] = cpu_to_le64(pagelist[i + 1]); - } - pagelist[entries - 1] = 0; + cmd->prp1 = pagelist[0]; + cmd->prp2 = cpu_to_le64(req->prp_list_iova + sizeof(uint64_t)); break; } trace_nvme_cmd_map_qiov(s, cmd, req, qiov, entries); @@ -1071,17 +1059,12 @@ static int nvme_reopen_prepare(BDRVReopenState *reopen_state, return 0; } -static void nvme_refresh_filename(BlockDriverState *bs, QDict *opts) +static void nvme_refresh_filename(BlockDriverState *bs) { - qdict_del(opts, "filename"); + BDRVNVMeState *s = bs->opaque; - if (!qdict_size(opts)) { - snprintf(bs->exact_filename, sizeof(bs->exact_filename), "%s://", - bs->drv->format_name); - } - - qdict_put_str(opts, "driver", bs->drv->format_name); - bs->full_open_options = qobject_ref(opts); + snprintf(bs->exact_filename, sizeof(bs->exact_filename), "nvme://%s/%i", + s->device, s->nsid); } static void nvme_refresh_limits(BlockDriverState *bs, Error **errp) @@ -1114,7 +1097,8 @@ static void nvme_attach_aio_context(BlockDriverState *bs, static void nvme_aio_plug(BlockDriverState *bs) { BDRVNVMeState *s = bs->opaque; - s->plugged++; + assert(!s->plugged); + s->plugged = true; } static void nvme_aio_unplug(BlockDriverState *bs) @@ -1122,14 +1106,13 @@ static void nvme_aio_unplug(BlockDriverState *bs) int i; BDRVNVMeState *s = bs->opaque; assert(s->plugged); - if (!--s->plugged) { - for (i = 1; i < s->nr_queues; i++) { - NVMeQueuePair *q = s->queues[i]; - qemu_mutex_lock(&q->lock); - nvme_kick(s, q); - nvme_process_completion(s, q); - qemu_mutex_unlock(&q->lock); - } + s->plugged = false; + for (i = 1; i < s->nr_queues; i++) { + NVMeQueuePair *q = s->queues[i]; + qemu_mutex_lock(&q->lock); + nvme_kick(s, q); + nvme_process_completion(s, q); + qemu_mutex_unlock(&q->lock); } } @@ -1154,6 +1137,13 @@ static void nvme_unregister_buf(BlockDriverState *bs, void *host) qemu_vfio_dma_unmap(s->vfio, host); } +static const char *const nvme_strong_runtime_opts[] = { + NVME_BLOCK_OPT_DEVICE, + NVME_BLOCK_OPT_NAMESPACE, + + NULL +}; + static BlockDriver bdrv_nvme = { .format_name = "nvme", .protocol_name = "nvme", @@ -1171,6 +1161,7 @@ static BlockDriver bdrv_nvme = { .bdrv_refresh_filename = nvme_refresh_filename, .bdrv_refresh_limits = nvme_refresh_limits, + .strong_runtime_opts = nvme_strong_runtime_opts, .bdrv_detach_aio_context = nvme_detach_aio_context, .bdrv_attach_aio_context = nvme_attach_aio_context, diff --git a/block/parallels.c b/block/parallels.c index cc9445879d..15bc97b759 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -220,23 +220,20 @@ static int64_t allocate_clusters(BlockDriverState *bs, int64_t sector_num, if (bs->backing) { int64_t nb_cow_sectors = to_allocate * s->tracks; int64_t nb_cow_bytes = nb_cow_sectors << BDRV_SECTOR_BITS; - QEMUIOVector qiov; - struct iovec iov = { - .iov_len = nb_cow_bytes, - .iov_base = qemu_blockalign(bs, nb_cow_bytes) - }; - qemu_iovec_init_external(&qiov, &iov, 1); + QEMUIOVector qiov = + QEMU_IOVEC_INIT_BUF(qiov, qemu_blockalign(bs, nb_cow_bytes), + nb_cow_bytes); ret = bdrv_co_preadv(bs->backing, idx * s->tracks * BDRV_SECTOR_SIZE, nb_cow_bytes, &qiov, 0); if (ret < 0) { - qemu_vfree(iov.iov_base); + qemu_vfree(qemu_iovec_buf(&qiov)); return ret; } ret = bdrv_co_pwritev(bs->file, s->data_end * BDRV_SECTOR_SIZE, nb_cow_bytes, &qiov, 0); - qemu_vfree(iov.iov_base); + qemu_vfree(qemu_iovec_buf(&qiov)); if (ret < 0) { return ret; } diff --git a/block/qapi.c b/block/qapi.c index 339727f0f4..21edab34fc 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -51,6 +51,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, return NULL; } + bdrv_refresh_filename(bs); + info = g_malloc0(sizeof(*info)); info->file = g_strdup(bs->filename); info->ro = bs->read_only; @@ -264,6 +266,8 @@ void bdrv_query_image_info(BlockDriverState *bs, goto out; } + bdrv_refresh_filename(bs); + info = g_new0(ImageInfo, 1); info->filename = g_strdup(bs->filename); info->format = g_strdup(bdrv_get_format_name(bs)); @@ -282,23 +286,20 @@ void bdrv_query_image_info(BlockDriverState *bs, info->dirty_flag = bdi.is_dirty; info->has_dirty_flag = true; } - info->format_specific = bdrv_get_specific_info(bs); + info->format_specific = bdrv_get_specific_info(bs, &err); + if (err) { + error_propagate(errp, err); + qapi_free_ImageInfo(info); + goto out; + } info->has_format_specific = info->format_specific != NULL; backing_filename = bs->backing_file; if (backing_filename[0] != '\0') { - char *backing_filename2 = g_malloc0(PATH_MAX); + char *backing_filename2; info->backing_filename = g_strdup(backing_filename); info->has_backing_filename = true; - bdrv_get_full_backing_filename(bs, backing_filename2, PATH_MAX, &err); - if (err) { - /* 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); - backing_filename2 = NULL; - error_free(err); - err = NULL; - } + backing_filename2 = bdrv_get_full_backing_filename(bs, NULL); /* Always report the full_backing_filename if present, even if it's the * same as backing_filename. That they are same is useful info. */ @@ -492,14 +493,14 @@ static void bdrv_query_blk_stats(BlockDeviceStats *ds, BlockBackend *blk) } bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_READ], - &ds->has_x_rd_latency_histogram, - &ds->x_rd_latency_histogram); + &ds->has_rd_latency_histogram, + &ds->rd_latency_histogram); bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_WRITE], - &ds->has_x_wr_latency_histogram, - &ds->x_wr_latency_histogram); + &ds->has_wr_latency_histogram, + &ds->wr_latency_histogram); bdrv_latency_histogram_stats(&stats->latency_histogram[BLOCK_ACCT_FLUSH], - &ds->has_x_flush_latency_histogram, - &ds->x_flush_latency_histogram); + &ds->has_flush_latency_histogram, + &ds->flush_latency_histogram); } static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs, @@ -594,7 +595,7 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, } } else { for (blk = blk_all_next(NULL); blk; blk = blk_all_next(blk)) { - BlockStatsList *info = g_malloc0(sizeof(*info)); + BlockStatsList *info; AioContext *ctx = blk_get_aio_context(blk); BlockStats *s; char *qdev; @@ -619,6 +620,7 @@ BlockStatsList *qmp_query_blockstats(bool has_query_nodes, bdrv_query_blk_stats(s->stats, blk); aio_context_release(ctx); + info = g_malloc0(sizeof(*info)); info->value = s; *p_next = info; p_next = &info->next; diff --git a/block/qcow.c b/block/qcow.c index 385d935258..10d2cf14b3 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -31,6 +31,7 @@ #include "qemu/module.h" #include "qemu/option.h" #include "qemu/bswap.h" +#include "qemu/cutils.h" #include #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -140,14 +141,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, if (ret < 0) { goto fail; } - be32_to_cpus(&header.magic); - be32_to_cpus(&header.version); - be64_to_cpus(&header.backing_file_offset); - be32_to_cpus(&header.backing_file_size); - be32_to_cpus(&header.mtime); - be64_to_cpus(&header.size); - be32_to_cpus(&header.crypt_method); - be64_to_cpus(&header.l1_table_offset); + header.magic = be32_to_cpu(header.magic); + header.version = be32_to_cpu(header.version); + header.backing_file_offset = be64_to_cpu(header.backing_file_offset); + header.backing_file_size = be32_to_cpu(header.backing_file_size); + header.mtime = be32_to_cpu(header.mtime); + header.size = be64_to_cpu(header.size); + header.crypt_method = be32_to_cpu(header.crypt_method); + header.l1_table_offset = be64_to_cpu(header.l1_table_offset); if (header.magic != QCOW_MAGIC) { error_setg(errp, "Image not in qcow format"); @@ -213,7 +214,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } s->crypto = qcrypto_block_open(crypto_opts, "encrypt.", - NULL, NULL, cflags, errp); + NULL, NULL, cflags, 1, errp); if (!s->crypto) { ret = -EINVAL; goto fail; @@ -270,7 +271,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, } for(i = 0;i < s->l1_size; i++) { - be64_to_cpus(&s->l1_table[i]); + s->l1_table[i] = be64_to_cpu(s->l1_table[i]); } /* alloc L2 cache (max. 64k * 16 * 8 = 8 MB) */ @@ -295,11 +296,13 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } ret = bdrv_pread(bs->file, header.backing_file_offset, - bs->backing_file, len); + bs->auto_backing_file, len); if (ret < 0) { goto fail; } - bs->backing_file[len] = '\0'; + bs->auto_backing_file[len] = '\0'; + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->auto_backing_file); } /* Disable migration when qcow images are used */ @@ -628,7 +631,6 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, int offset_in_cluster; int ret = 0, n; uint64_t cluster_offset; - struct iovec hd_iov; QEMUIOVector hd_qiov; uint8_t *buf; void *orig_buf; @@ -661,9 +663,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, if (!cluster_offset) { if (bs->backing) { /* read from the base image */ - hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n; - qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); + qemu_iovec_init_buf(&hd_qiov, buf, n); qemu_co_mutex_unlock(&s->lock); /* qcow2 emits this on bs->file instead of bs->backing */ BLKDBG_EVENT(bs->file, BLKDBG_READ_BACKING_AIO); @@ -688,9 +688,7 @@ static coroutine_fn int qcow_co_preadv(BlockDriverState *bs, uint64_t offset, ret = -EIO; break; } - hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n; - qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); + qemu_iovec_init_buf(&hd_qiov, buf, n); qemu_co_mutex_unlock(&s->lock); BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); ret = bdrv_co_preadv(bs->file, cluster_offset + offset_in_cluster, @@ -733,7 +731,6 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, int offset_in_cluster; uint64_t cluster_offset; int ret = 0, n; - struct iovec hd_iov; QEMUIOVector hd_qiov; uint8_t *buf; void *orig_buf; @@ -779,9 +776,7 @@ static coroutine_fn int qcow_co_pwritev(BlockDriverState *bs, uint64_t offset, } } - hd_iov.iov_base = (void *)buf; - hd_iov.iov_len = n; - qemu_iovec_init_external(&hd_qiov, &hd_iov, 1); + qemu_iovec_init_buf(&hd_qiov, buf, n); qemu_co_mutex_unlock(&s->lock); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, @@ -1062,7 +1057,6 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, { BDRVQcowState *s = bs->opaque; QEMUIOVector hd_qiov; - struct iovec iov; z_stream strm; int ret, out_len; uint8_t *buf, *out_buf; @@ -1128,11 +1122,7 @@ qcow_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, } cluster_offset &= s->cluster_offset_mask; - iov = (struct iovec) { - .iov_base = out_buf, - .iov_len = out_len, - }; - qemu_iovec_init_external(&hd_qiov, &iov, 1); + qemu_iovec_init_buf(&hd_qiov, out_buf, out_len); BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0); if (ret < 0) { @@ -1183,6 +1173,12 @@ static QemuOptsList qcow_create_opts = { } }; +static const char *const qcow_strong_runtime_opts[] = { + "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, + + NULL +}; + static BlockDriver bdrv_qcow = { .format_name = "qcow", .instance_size = sizeof(BDRVQcowState), @@ -1206,6 +1202,7 @@ static BlockDriver bdrv_qcow = { .bdrv_get_info = qcow_get_info, .create_opts = &qcow_create_opts, + .strong_runtime_opts = qcow_strong_runtime_opts, }; static void bdrv_qcow_init(void) diff --git a/block/qcow2-bitmap.c b/block/qcow2-bitmap.c index ba978ad2aa..e53a1609d7 100644 --- a/block/qcow2-bitmap.c +++ b/block/qcow2-bitmap.c @@ -77,8 +77,6 @@ typedef struct Qcow2BitmapTable { uint32_t size; /* number of 64bit entries */ QSIMPLEQ_ENTRY(Qcow2BitmapTable) entry; } Qcow2BitmapTable; -typedef QSIMPLEQ_HEAD(Qcow2BitmapTableList, Qcow2BitmapTable) - Qcow2BitmapTableList; typedef struct Qcow2Bitmap { Qcow2BitmapTable table; @@ -118,7 +116,7 @@ static inline void bitmap_table_to_be(uint64_t *bitmap_table, size_t size) size_t i; for (i = 0; i < size; ++i) { - cpu_to_be64s(&bitmap_table[i]); + bitmap_table[i] = cpu_to_be64(bitmap_table[i]); } } @@ -231,7 +229,7 @@ static int bitmap_table_load(BlockDriverState *bs, Qcow2BitmapTable *tb, } for (i = 0; i < tb->size; ++i) { - be64_to_cpus(&table[i]); + table[i] = be64_to_cpu(table[i]); ret = check_table_entry(table[i], s->cluster_size); if (ret < 0) { goto fail; @@ -345,11 +343,17 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, uint32_t granularity; BdrvDirtyBitmap *bitmap = NULL; - if (bm->flags & BME_FLAG_IN_USE) { - error_setg(errp, "Bitmap '%s' is in use", bm->name); + granularity = 1U << bm->granularity_bits; + bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); + if (bitmap == NULL) { goto fail; } + if (bm->flags & BME_FLAG_IN_USE) { + /* Data is unusable, skip loading it */ + return bitmap; + } + ret = bitmap_table_load(bs, &bm->table, &bitmap_table); if (ret < 0) { error_setg_errno(errp, -ret, @@ -358,12 +362,6 @@ static BdrvDirtyBitmap *load_bitmap(BlockDriverState *bs, goto fail; } - granularity = 1U << bm->granularity_bits; - bitmap = bdrv_create_dirty_bitmap(bs, granularity, bm->name, errp); - if (bitmap == NULL) { - goto fail; - } - ret = load_bitmap_data(bs, bitmap_table, bm->table.size, bitmap); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read bitmap '%s' from image", @@ -394,20 +392,20 @@ fail: static inline void bitmap_dir_entry_to_cpu(Qcow2BitmapDirEntry *entry) { - be64_to_cpus(&entry->bitmap_table_offset); - be32_to_cpus(&entry->bitmap_table_size); - be32_to_cpus(&entry->flags); - be16_to_cpus(&entry->name_size); - be32_to_cpus(&entry->extra_data_size); + entry->bitmap_table_offset = be64_to_cpu(entry->bitmap_table_offset); + entry->bitmap_table_size = be32_to_cpu(entry->bitmap_table_size); + entry->flags = be32_to_cpu(entry->flags); + entry->name_size = be16_to_cpu(entry->name_size); + entry->extra_data_size = be32_to_cpu(entry->extra_data_size); } static inline void bitmap_dir_entry_to_be(Qcow2BitmapDirEntry *entry) { - cpu_to_be64s(&entry->bitmap_table_offset); - cpu_to_be32s(&entry->bitmap_table_size); - cpu_to_be32s(&entry->flags); - cpu_to_be16s(&entry->name_size); - cpu_to_be32s(&entry->extra_data_size); + entry->bitmap_table_offset = cpu_to_be64(entry->bitmap_table_offset); + entry->bitmap_table_size = cpu_to_be32(entry->bitmap_table_size); + entry->flags = cpu_to_be32(entry->flags); + entry->name_size = cpu_to_be16(entry->name_size); + entry->extra_data_size = cpu_to_be32(entry->extra_data_size); } static inline int calc_dir_entry_size(size_t name_size, size_t extra_data_size) @@ -464,10 +462,25 @@ static int check_dir_entry(BlockDriverState *bs, Qcow2BitmapDirEntry *entry) return len; } - fail = (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) || - (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits)); + if (phys_bitmap_bytes > BME_MAX_PHYS_SIZE) { + return -EINVAL; + } - return fail ? -EINVAL : 0; + if (!(entry->flags & BME_FLAG_IN_USE) && + (len > ((phys_bitmap_bytes * 8) << entry->granularity_bits))) + { + /* + * We've loaded a valid bitmap (IN_USE not set) or we are going to + * store a valid bitmap, but the allocated bitmap table size is not + * enough to store this bitmap. + * + * Note, that it's OK to have an invalid bitmap with invalid size due + * to a bitmap that was not correctly saved after image resize. + */ + return -EINVAL; + } + + return 0; } static inline void bitmap_directory_to_be(uint8_t *dir, size_t size) @@ -780,7 +793,8 @@ static int bitmap_list_store(BlockDriverState *bs, Qcow2BitmapList *bm_list, * directory in-place (actually, turn-off the extension), which is checked * in qcow2_check_metadata_overlap() */ ret = qcow2_pre_write_overlap_check( - bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size); + bs, in_place ? QCOW2_OL_BITMAP_DIRECTORY : 0, dir_offset, dir_size, + false); if (ret < 0) { goto fail; } @@ -951,6 +965,7 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) Qcow2Bitmap *bm; GSList *created_dirty_bitmaps = NULL; bool header_updated = false; + bool needs_update = false; if (s->nb_bitmaps == 0) { /* No bitmaps - nothing to do */ @@ -964,35 +979,39 @@ bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp) } QSIMPLEQ_FOREACH(bm, bm_list, entry) { - if (!(bm->flags & BME_FLAG_IN_USE)) { - BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); - if (bitmap == NULL) { - goto fail; - } - - if (!(bm->flags & BME_FLAG_AUTO)) { - bdrv_disable_dirty_bitmap(bitmap); - } - bdrv_dirty_bitmap_set_persistance(bitmap, true); - bm->flags |= BME_FLAG_IN_USE; - created_dirty_bitmaps = - g_slist_append(created_dirty_bitmaps, bitmap); + BdrvDirtyBitmap *bitmap = load_bitmap(bs, bm, errp); + if (bitmap == NULL) { + goto fail; } + + bdrv_dirty_bitmap_set_persistence(bitmap, true); + if (bm->flags & BME_FLAG_IN_USE) { + bdrv_dirty_bitmap_set_inconsistent(bitmap); + } else { + /* NB: updated flags only get written if can_write(bs) is true. */ + bm->flags |= BME_FLAG_IN_USE; + needs_update = true; + } + if (!(bm->flags & BME_FLAG_AUTO)) { + bdrv_disable_dirty_bitmap(bitmap); + } + created_dirty_bitmaps = + g_slist_append(created_dirty_bitmaps, bitmap); } - if (created_dirty_bitmaps != NULL) { - if (can_write(bs)) { - /* in_use flags must be updated */ - int ret = update_ext_header_and_dir_in_place(bs, bm_list); - if (ret < 0) { - error_setg_errno(errp, -ret, "Can't update bitmap directory"); - goto fail; - } - header_updated = true; - } else { - g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, - (gpointer)true); + if (needs_update && can_write(bs)) { + /* in_use flags must be updated */ + int ret = update_ext_header_and_dir_in_place(bs, bm_list); + if (ret < 0) { + error_setg_errno(errp, -ret, "Can't update bitmap directory"); + goto fail; } + header_updated = true; + } + + if (!can_write(bs)) { + g_slist_foreach(created_dirty_bitmaps, set_readonly_helper, + (gpointer)true); } g_slist_free(created_dirty_bitmaps); @@ -1008,6 +1027,82 @@ fail: return false; } + +static Qcow2BitmapInfoFlagsList *get_bitmap_info_flags(uint32_t flags) +{ + Qcow2BitmapInfoFlagsList *list = NULL; + Qcow2BitmapInfoFlagsList **plist = &list; + int i; + + static const struct { + int bme; /* Bitmap directory entry flags */ + int info; /* The flags to report to the user */ + } map[] = { + { BME_FLAG_IN_USE, QCOW2_BITMAP_INFO_FLAGS_IN_USE }, + { BME_FLAG_AUTO, QCOW2_BITMAP_INFO_FLAGS_AUTO }, + }; + + int map_size = ARRAY_SIZE(map); + + for (i = 0; i < map_size; ++i) { + if (flags & map[i].bme) { + Qcow2BitmapInfoFlagsList *entry = + g_new0(Qcow2BitmapInfoFlagsList, 1); + entry->value = map[i].info; + *plist = entry; + plist = &entry->next; + flags &= ~map[i].bme; + } + } + /* Check if the BME_* mapping above is complete */ + assert(!flags); + + return list; +} + +/* + * qcow2_get_bitmap_info_list() + * Returns a list of QCOW2 bitmap details. + * In case of no bitmaps, the function returns NULL and + * the @errp parameter is not set. + * When bitmap information can not be obtained, the function returns + * NULL and the @errp parameter is set. + */ +Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, + Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + Qcow2BitmapList *bm_list; + Qcow2Bitmap *bm; + Qcow2BitmapInfoList *list = NULL; + Qcow2BitmapInfoList **plist = &list; + + if (s->nb_bitmaps == 0) { + return NULL; + } + + bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, + s->bitmap_directory_size, errp); + if (bm_list == NULL) { + return NULL; + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { + Qcow2BitmapInfo *info = g_new0(Qcow2BitmapInfo, 1); + Qcow2BitmapInfoList *obj = g_new0(Qcow2BitmapInfoList, 1); + info->granularity = 1U << bm->granularity_bits; + info->name = g_strdup(bm->name); + info->flags = get_bitmap_info_flags(bm->flags & ~BME_RESERVED_FLAGS); + obj->value = info; + *plist = obj; + plist = &obj->next; + } + + bitmap_list_free(bm_list); + + return list; +} + int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, Error **errp) { @@ -1038,23 +1133,21 @@ int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, } QSIMPLEQ_FOREACH(bm, bm_list, entry) { - if (!(bm->flags & BME_FLAG_IN_USE)) { - BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); - if (bitmap == NULL) { - continue; - } - - if (!bdrv_dirty_bitmap_readonly(bitmap)) { - error_setg(errp, "Bitmap %s is not readonly but not marked" - "'IN_USE' in the image. Something went wrong," - "all the bitmaps may be corrupted", bm->name); - ret = -EINVAL; - goto out; - } - - bm->flags |= BME_FLAG_IN_USE; - ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); + BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); + if (bitmap == NULL) { + continue; } + + if (!bdrv_dirty_bitmap_readonly(bitmap)) { + error_setg(errp, "Bitmap %s was loaded prior to rw-reopen, but was " + "not marked as readonly. This is a bug, something went " + "wrong. All of the bitmaps may be corrupted", bm->name); + ret = -EINVAL; + goto out; + } + + bm->flags |= BME_FLAG_IN_USE; + ro_dirty_bitmaps = g_slist_append(ro_dirty_bitmaps, bitmap); } if (ro_dirty_bitmaps != NULL) { @@ -1082,6 +1175,52 @@ int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp) return qcow2_reopen_bitmaps_rw_hint(bs, NULL, errp); } +/* Checks to see if it's safe to resize bitmaps */ +int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp) +{ + BDRVQcow2State *s = bs->opaque; + Qcow2BitmapList *bm_list; + Qcow2Bitmap *bm; + int ret = 0; + + if (s->nb_bitmaps == 0) { + return 0; + } + + bm_list = bitmap_list_load(bs, s->bitmap_directory_offset, + s->bitmap_directory_size, errp); + if (bm_list == NULL) { + return -EINVAL; + } + + QSIMPLEQ_FOREACH(bm, bm_list, entry) { + BdrvDirtyBitmap *bitmap = bdrv_find_dirty_bitmap(bs, bm->name); + if (bitmap == NULL) { + /* + * We rely on all bitmaps being in-memory to be able to resize them, + * Otherwise, we'd need to resize them on disk explicitly + */ + error_setg(errp, "Cannot resize qcow2 with persistent bitmaps that " + "were not loaded into memory"); + ret = -ENOTSUP; + goto out; + } + + /* + * The checks against readonly and busy are redundant, but certainly + * do no harm. checks against inconsistent are crucial: + */ + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { + ret = -ENOTSUP; + goto out; + } + } + +out: + bitmap_list_free(bm_list); + return ret; +} + /* store_bitmap_data() * Store bitmap to image, filling bitmap table accordingly. */ @@ -1150,7 +1289,7 @@ static uint64_t *store_bitmap_data(BlockDriverState *bs, memset(buf + write_size, 0, s->cluster_size - write_size); } - ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size); + ret = qcow2_pre_write_overlap_check(bs, 0, off, s->cluster_size, false); if (ret < 0) { error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); goto fail; @@ -1218,7 +1357,7 @@ static int store_bitmap(BlockDriverState *bs, Qcow2Bitmap *bm, Error **errp) } ret = qcow2_pre_write_overlap_check(bs, 0, tb_offset, - tb_size * sizeof(tb[0])); + tb_size * sizeof(tb[0]), false); if (ret < 0) { error_setg_errno(errp, -ret, "Qcow2 overlap check failed"); goto fail; @@ -1316,7 +1455,7 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) int ret; Qcow2BitmapList *bm_list; Qcow2Bitmap *bm; - Qcow2BitmapTableList drop_tables; + QSIMPLEQ_HEAD(, Qcow2BitmapTable) drop_tables; Qcow2BitmapTable *tb, *tb_next; if (!bdrv_has_changed_persistent_bitmaps(bs)) { @@ -1349,9 +1488,9 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) uint32_t granularity = bdrv_dirty_bitmap_granularity(bitmap); Qcow2Bitmap *bm; - if (!bdrv_dirty_bitmap_get_persistance(bitmap) || - bdrv_dirty_bitmap_readonly(bitmap)) - { + if (!bdrv_dirty_bitmap_get_persistence(bitmap) || + bdrv_dirty_bitmap_readonly(bitmap) || + bdrv_dirty_bitmap_inconsistent(bitmap)) { continue; } @@ -1418,6 +1557,22 @@ void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp) g_free(tb); } + QSIMPLEQ_FOREACH(bm, bm_list, entry) { + /* For safety, we remove bitmap after storing. + * We may be here in two cases: + * 1. bdrv_close. It's ok to drop bitmap. + * 2. inactivation. It means migration without 'dirty-bitmaps' + * capability, so bitmaps are not marked with + * BdrvDirtyBitmap.migration flags. It's not bad to drop them too, + * and reload on invalidation. + */ + if (bm->dirty_bitmap == NULL) { + continue; + } + + bdrv_release_dirty_bitmap(bs, bm->dirty_bitmap); + } + bitmap_list_free(bm_list); return; @@ -1451,7 +1606,7 @@ int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp) for (bitmap = bdrv_dirty_bitmap_next(bs, NULL); bitmap != NULL; bitmap = bdrv_dirty_bitmap_next(bs, bitmap)) { - if (bdrv_dirty_bitmap_get_persistance(bitmap)) { + if (bdrv_dirty_bitmap_get_persistence(bitmap)) { bdrv_dirty_bitmap_set_readonly(bitmap, true); } } diff --git a/block/qcow2-cache.c b/block/qcow2-cache.c index d9dafa31e5..df02e7b20a 100644 --- a/block/qcow2-cache.c +++ b/block/qcow2-cache.c @@ -205,13 +205,13 @@ static int qcow2_cache_entry_flush(BlockDriverState *bs, Qcow2Cache *c, int i) if (c == s->refcount_block_cache) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_REFCOUNT_BLOCK, - c->entries[i].offset, c->table_size); + c->entries[i].offset, c->table_size, false); } else if (c == s->l2_table_cache) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, - c->entries[i].offset, c->table_size); + c->entries[i].offset, c->table_size, false); } else { ret = qcow2_pre_write_overlap_check(bs, 0, - c->entries[i].offset, c->table_size); + c->entries[i].offset, c->table_size, false); } if (ret < 0) { diff --git a/block/qcow2-cluster.c b/block/qcow2-cluster.c index d37fe08b3d..974a4e8656 100644 --- a/block/qcow2-cluster.c +++ b/block/qcow2-cluster.c @@ -153,7 +153,7 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, /* the L1 position has not yet been updated, so these clusters must * indeed be completely free */ ret = qcow2_pre_write_overlap_check(bs, 0, new_l1_table_offset, - new_l1_size2); + new_l1_size2, false); if (ret < 0) { goto fail; } @@ -238,7 +238,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index) } ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, - s->l1_table_offset + 8 * l1_start_index, sizeof(buf)); + s->l1_table_offset + 8 * l1_start_index, sizeof(buf), false); if (ret < 0) { return ret; } @@ -285,6 +285,9 @@ static int l2_allocate(BlockDriverState *bs, int l1_index) goto fail; } + /* The offset must fit in the offset field of the L1 table entry */ + assert((l2_offset & L1E_OFFSET_MASK) == l2_offset); + /* If we're allocating the table at offset 0 then something is wrong */ if (l2_offset == 0) { qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " @@ -377,8 +380,8 @@ 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(int nb_clusters, int cluster_size, - uint64_t *l2_slice, uint64_t stop_flags) +static int count_contiguous_clusters(BlockDriverState *bs, int nb_clusters, + int cluster_size, uint64_t *l2_slice, uint64_t stop_flags) { int i; QCow2ClusterType first_cluster_type; @@ -386,12 +389,12 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, uint64_t first_entry = be64_to_cpu(l2_slice[0]); uint64_t offset = first_entry & mask; - if (!offset) { + first_cluster_type = qcow2_get_cluster_type(bs, first_entry); + if (first_cluster_type == QCOW2_CLUSTER_UNALLOCATED) { return 0; } /* must be allocated */ - first_cluster_type = qcow2_get_cluster_type(first_entry); assert(first_cluster_type == QCOW2_CLUSTER_NORMAL || first_cluster_type == QCOW2_CLUSTER_ZERO_ALLOC); @@ -402,14 +405,15 @@ static int count_contiguous_clusters(int nb_clusters, int cluster_size, } } - return i; + return i; } /* * Checks how many consecutive unallocated clusters in a given L2 * slice have the same cluster type. */ -static int count_contiguous_clusters_unallocated(int nb_clusters, +static int count_contiguous_clusters_unallocated(BlockDriverState *bs, + int nb_clusters, uint64_t *l2_slice, QCow2ClusterType wanted_type) { @@ -419,7 +423,7 @@ static int count_contiguous_clusters_unallocated(int nb_clusters, wanted_type == QCOW2_CLUSTER_UNALLOCATED); for (i = 0; i < nb_clusters; i++) { uint64_t entry = be64_to_cpu(l2_slice[i]); - QCow2ClusterType type = qcow2_get_cluster_type(entry); + QCow2ClusterType type = qcow2_get_cluster_type(bs, entry); if (type != wanted_type) { break; @@ -486,6 +490,7 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs, unsigned offset_in_cluster, QEMUIOVector *qiov) { + BDRVQcow2State *s = bs->opaque; int ret; if (qiov->size == 0) { @@ -493,13 +498,13 @@ static int coroutine_fn do_perform_cow_write(BlockDriverState *bs, } ret = qcow2_pre_write_overlap_check(bs, 0, - cluster_offset + offset_in_cluster, qiov->size); + cluster_offset + offset_in_cluster, qiov->size, true); if (ret < 0) { return ret; } BLKDBG_EVENT(bs->file, BLKDBG_COW_WRITE); - ret = bdrv_co_pwritev(bs->file, cluster_offset + offset_in_cluster, + ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster, qiov->size, qiov, 0); if (ret < 0) { return ret; @@ -592,7 +597,7 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, * true */ assert(nb_clusters <= INT_MAX); - type = qcow2_get_cluster_type(*cluster_offset); + type = qcow2_get_cluster_type(bs, *cluster_offset); if (s->qcow_version < 3 && (type == QCOW2_CLUSTER_ZERO_PLAIN || type == QCOW2_CLUSTER_ZERO_ALLOC)) { qcow2_signal_corruption(bs, true, -1, -1, "Zero cluster entry found" @@ -603,6 +608,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, } switch (type) { case QCOW2_CLUSTER_COMPRESSED: + if (has_data_file(bs)) { + qcow2_signal_corruption(bs, true, -1, -1, "Compressed cluster " + "entry found in image with external data " + "file (L2 offset: %#" PRIx64 ", L2 index: " + "%#x)", l2_offset, l2_index); + ret = -EIO; + goto fail; + } /* Compressed clusters can only be processed one by one */ c = 1; *cluster_offset &= L2E_COMPRESSED_OFFSET_SIZE_MASK; @@ -610,14 +623,14 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, case QCOW2_CLUSTER_ZERO_PLAIN: case QCOW2_CLUSTER_UNALLOCATED: /* how many empty clusters ? */ - c = count_contiguous_clusters_unallocated(nb_clusters, + c = count_contiguous_clusters_unallocated(bs, nb_clusters, &l2_slice[l2_index], type); *cluster_offset = 0; break; case QCOW2_CLUSTER_ZERO_ALLOC: case QCOW2_CLUSTER_NORMAL: /* how many allocated clusters ? */ - c = count_contiguous_clusters(nb_clusters, s->cluster_size, + c = count_contiguous_clusters(bs, nb_clusters, s->cluster_size, &l2_slice[l2_index], QCOW_OFLAG_ZERO); *cluster_offset &= L2E_OFFSET_MASK; if (offset_into_cluster(s, *cluster_offset)) { @@ -629,6 +642,17 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, ret = -EIO; goto fail; } + if (has_data_file(bs) && *cluster_offset != offset - offset_in_cluster) + { + qcow2_signal_corruption(bs, true, -1, -1, + "External data file host cluster offset %#" + PRIx64 " does not match guest cluster " + "offset: %#" PRIx64 + ", L2 index: %#x)", *cluster_offset, + offset - offset_in_cluster, l2_index); + ret = -EIO; + goto fail; + } break; default: abort(); @@ -732,19 +756,16 @@ static int get_cluster_table(BlockDriverState *bs, uint64_t offset, /* * alloc_compressed_cluster_offset * - * For a given offset of the disk image, return cluster offset in - * qcow2 file. - * - * If the offset is not found, allocate a new compressed cluster. - * - * Return the cluster offset if successful, - * Return 0, otherwise. + * For a given offset on the virtual disk, allocate a new compressed cluster + * and put the host offset of the cluster into *host_offset. If a cluster is + * already allocated at the offset, return an error. * + * Return 0 on success and -errno in error cases */ - -uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, - uint64_t offset, - int compressed_size) +int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, + uint64_t offset, + int compressed_size, + uint64_t *host_offset) { BDRVQcow2State *s = bs->opaque; int l2_index, ret; @@ -752,9 +773,13 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, int64_t cluster_offset; int nb_csectors; + if (has_data_file(bs)) { + return 0; + } + ret = get_cluster_table(bs, offset, &l2_slice, &l2_index); if (ret < 0) { - return 0; + return ret; } /* Compression can't overwrite anything. Fail if the cluster was already @@ -762,13 +787,13 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, cluster_offset = be64_to_cpu(l2_slice[l2_index]); if (cluster_offset & L2E_OFFSET_MASK) { qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - return 0; + return -EIO; } cluster_offset = qcow2_alloc_bytes(bs, compressed_size); if (cluster_offset < 0) { qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - return 0; + return cluster_offset; } nb_csectors = ((cluster_offset + compressed_size - 1) >> 9) - @@ -786,7 +811,8 @@ uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, l2_slice[l2_index] = cpu_to_be64(cluster_offset); qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - return cluster_offset; + *host_offset = cluster_offset & s->cluster_offset_mask; + return 0; } static int perform_cow(BlockDriverState *bs, QCowL2Meta *m) @@ -1010,14 +1036,14 @@ void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m) * write, but require COW to be performed (this includes yet unallocated space, * which must copy from the backing file) */ -static int count_cow_clusters(BDRVQcow2State *s, int nb_clusters, +static int count_cow_clusters(BlockDriverState *bs, int nb_clusters, uint64_t *l2_slice, int l2_index) { int i; for (i = 0; i < nb_clusters; i++) { uint64_t l2_entry = be64_to_cpu(l2_slice[l2_index + i]); - QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); + QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); switch(cluster_type) { case QCOW2_CLUSTER_NORMAL: @@ -1105,9 +1131,9 @@ static int handle_dependencies(BlockDriverState *bs, uint64_t guest_offset, /* * Checks how many already allocated clusters that don't require a copy on - * write there are at the given guest_offset (up to *bytes). If - * *host_offset is not zero, only physically contiguous clusters beginning at - * this host offset are counted. + * write there are at the given guest_offset (up to *bytes). If *host_offset is + * not INV_OFFSET, only physically contiguous clusters beginning at this host + * offset are counted. * * Note that guest_offset may not be cluster aligned. In this case, the * returned *host_offset points to exact byte referenced by guest_offset and @@ -1139,8 +1165,8 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, trace_qcow2_handle_copied(qemu_coroutine_self(), guest_offset, *host_offset, *bytes); - assert(*host_offset == 0 || offset_into_cluster(s, guest_offset) - == offset_into_cluster(s, *host_offset)); + assert(*host_offset == INV_OFFSET || offset_into_cluster(s, guest_offset) + == offset_into_cluster(s, *host_offset)); /* * Calculate the number of clusters to look for. We stop at L2 slice @@ -1162,7 +1188,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, cluster_offset = be64_to_cpu(l2_slice[l2_index]); /* Check how many clusters are already allocated and don't need COW */ - if (qcow2_get_cluster_type(cluster_offset) == QCOW2_CLUSTER_NORMAL + if (qcow2_get_cluster_type(bs, cluster_offset) == QCOW2_CLUSTER_NORMAL && (cluster_offset & QCOW_OFLAG_COPIED)) { /* If a specific host_offset is required, check it */ @@ -1178,7 +1204,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, goto out; } - if (*host_offset != 0 && !offset_matches) { + if (*host_offset != INV_OFFSET && !offset_matches) { *bytes = 0; ret = 0; goto out; @@ -1186,7 +1212,7 @@ static int handle_copied(BlockDriverState *bs, uint64_t guest_offset, /* We keep all QCOW_OFLAG_COPIED clusters */ keep_clusters = - count_contiguous_clusters(nb_clusters, s->cluster_size, + count_contiguous_clusters(bs, nb_clusters, s->cluster_size, &l2_slice[l2_index], QCOW_OFLAG_COPIED | QCOW_OFLAG_ZERO); assert(keep_clusters <= nb_clusters); @@ -1221,10 +1247,10 @@ out: * contain the number of clusters that have been allocated and are contiguous * in the image file. * - * If *host_offset is non-zero, it specifies the offset in the image file at - * which the new clusters must start. *nb_clusters can be 0 on return in this - * case if the cluster at host_offset is already in use. If *host_offset is - * zero, the clusters can be allocated anywhere in the image file. + * If *host_offset is not INV_OFFSET, it specifies the offset in the image file + * at which the new clusters must start. *nb_clusters can be 0 on return in + * this case if the cluster at host_offset is already in use. If *host_offset + * is INV_OFFSET, the clusters can be allocated anywhere in the image file. * * *host_offset is updated to contain the offset into the image file at which * the first allocated cluster starts. @@ -1241,9 +1267,16 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, trace_qcow2_do_alloc_clusters_offset(qemu_coroutine_self(), guest_offset, *host_offset, *nb_clusters); + if (has_data_file(bs)) { + assert(*host_offset == INV_OFFSET || + *host_offset == start_of_cluster(s, guest_offset)); + *host_offset = start_of_cluster(s, guest_offset); + return 0; + } + /* Allocate new clusters */ trace_qcow2_cluster_alloc_phys(qemu_coroutine_self()); - if (*host_offset == 0) { + if (*host_offset == INV_OFFSET) { int64_t cluster_offset = qcow2_alloc_clusters(bs, *nb_clusters * s->cluster_size); if (cluster_offset < 0) { @@ -1263,8 +1296,8 @@ static int do_alloc_cluster_offset(BlockDriverState *bs, uint64_t guest_offset, /* * Allocates new clusters for an area that either is yet unallocated or needs a - * copy on write. If *host_offset is non-zero, clusters are only allocated if - * the new allocation can match the specified host offset. + * copy on write. If *host_offset is not INV_OFFSET, clusters are only + * allocated if the new allocation can match the specified host offset. * * Note that guest_offset may not be cluster aligned. In this case, the * returned *host_offset points to exact byte referenced by guest_offset and @@ -1292,7 +1325,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, int ret; bool keep_old_clusters = false; - uint64_t alloc_cluster_offset = 0; + uint64_t alloc_cluster_offset = INV_OFFSET; trace_qcow2_handle_alloc(qemu_coroutine_self(), guest_offset, *host_offset, *bytes); @@ -1321,7 +1354,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, if (entry & QCOW_OFLAG_COMPRESSED) { nb_clusters = 1; } else { - nb_clusters = count_cow_clusters(s, nb_clusters, l2_slice, l2_index); + nb_clusters = count_cow_clusters(bs, nb_clusters, l2_slice, l2_index); } /* This function is only called when there were no non-COW clusters, so if @@ -1329,9 +1362,9 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, * wrong with our code. */ assert(nb_clusters > 0); - if (qcow2_get_cluster_type(entry) == QCOW2_CLUSTER_ZERO_ALLOC && + if (qcow2_get_cluster_type(bs, entry) == QCOW2_CLUSTER_ZERO_ALLOC && (entry & QCOW_OFLAG_COPIED) && - (!*host_offset || + (*host_offset == INV_OFFSET || start_of_cluster(s, *host_offset) == (entry & L2E_OFFSET_MASK))) { int preallocated_nb_clusters; @@ -1349,7 +1382,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, * would be fine, too, but count_cow_clusters() above has limited * nb_clusters already to a range of COW clusters */ preallocated_nb_clusters = - count_contiguous_clusters(nb_clusters, s->cluster_size, + count_contiguous_clusters(bs, nb_clusters, s->cluster_size, &l2_slice[l2_index], QCOW_OFLAG_COPIED); assert(preallocated_nb_clusters > 0); @@ -1363,9 +1396,10 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, qcow2_cache_put(s->l2_table_cache, (void **) &l2_slice); - if (!alloc_cluster_offset) { + if (alloc_cluster_offset == INV_OFFSET) { /* Allocate, if necessary at a given offset in the image file */ - alloc_cluster_offset = start_of_cluster(s, *host_offset); + alloc_cluster_offset = *host_offset == INV_OFFSET ? INV_OFFSET : + start_of_cluster(s, *host_offset); ret = do_alloc_cluster_offset(bs, guest_offset, &alloc_cluster_offset, &nb_clusters); if (ret < 0) { @@ -1378,16 +1412,7 @@ static int handle_alloc(BlockDriverState *bs, uint64_t guest_offset, return 0; } - /* !*host_offset would overwrite the image header and is reserved for - * "no host offset preferred". If 0 was a valid host offset, it'd - * trigger the following overlap check; do that now to avoid having an - * invalid value in *host_offset. */ - if (!alloc_cluster_offset) { - ret = qcow2_pre_write_overlap_check(bs, 0, alloc_cluster_offset, - nb_clusters * s->cluster_size); - assert(ret < 0); - goto fail; - } + assert(alloc_cluster_offset != INV_OFFSET); } /* @@ -1479,14 +1504,14 @@ int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, again: start = offset; remaining = *bytes; - cluster_offset = 0; - *host_offset = 0; + cluster_offset = INV_OFFSET; + *host_offset = INV_OFFSET; cur_bytes = 0; *m = NULL; while (true) { - if (!*host_offset) { + if (*host_offset == INV_OFFSET && cluster_offset != INV_OFFSET) { *host_offset = start_of_cluster(s, cluster_offset); } @@ -1494,7 +1519,10 @@ again: start += cur_bytes; remaining -= cur_bytes; - cluster_offset += cur_bytes; + + if (cluster_offset != INV_OFFSET) { + cluster_offset += cur_bytes; + } if (remaining == 0) { break; @@ -1566,81 +1594,11 @@ again: *bytes -= remaining; assert(*bytes > 0); - assert(*host_offset != 0); + assert(*host_offset != INV_OFFSET); return 0; } -static int decompress_buffer(uint8_t *out_buf, int out_buf_size, - const uint8_t *buf, int buf_size) -{ - z_stream strm1, *strm = &strm1; - int ret, out_len; - - memset(strm, 0, sizeof(*strm)); - - strm->next_in = (uint8_t *)buf; - strm->avail_in = buf_size; - strm->next_out = out_buf; - strm->avail_out = out_buf_size; - - ret = inflateInit2(strm, -12); - if (ret != Z_OK) - return -1; - ret = inflate(strm, Z_FINISH); - out_len = strm->next_out - out_buf; - if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || - out_len != out_buf_size) { - inflateEnd(strm); - return -1; - } - inflateEnd(strm); - return 0; -} - -int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset) -{ - BDRVQcow2State *s = bs->opaque; - int ret, csize, nb_csectors, sector_offset; - uint64_t coffset; - - coffset = cluster_offset & s->cluster_offset_mask; - if (s->cluster_cache_offset != coffset) { - nb_csectors = ((cluster_offset >> s->csize_shift) & s->csize_mask) + 1; - sector_offset = coffset & 511; - csize = nb_csectors * 512 - sector_offset; - - /* Allocate buffers on first decompress operation, most images are - * uncompressed and the memory overhead can be avoided. The buffers - * are freed in .bdrv_close(). - */ - if (!s->cluster_data) { - /* one more sector for decompressed data alignment */ - s->cluster_data = qemu_try_blockalign(bs->file->bs, - QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size + 512); - if (!s->cluster_data) { - return -ENOMEM; - } - } - if (!s->cluster_cache) { - s->cluster_cache = g_malloc(s->cluster_size); - } - - BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); - ret = bdrv_read(bs->file, coffset >> 9, s->cluster_data, - nb_csectors); - if (ret < 0) { - return ret; - } - if (decompress_buffer(s->cluster_cache, s->cluster_size, - s->cluster_data + sector_offset, csize) < 0) { - return -EIO; - } - s->cluster_cache_offset = coffset; - } - return 0; -} - /* * This discards as many clusters of nb_clusters as possible at once (i.e. * all clusters in the same L2 slice) and returns the number of discarded @@ -1683,7 +1641,7 @@ static int discard_in_l2_slice(BlockDriverState *bs, uint64_t offset, * If full_discard is true, the sector should not read back as zeroes, * but rather fall through to the backing file. */ - switch (qcow2_get_cluster_type(old_l2_entry)) { + switch (qcow2_get_cluster_type(bs, old_l2_entry)) { case QCOW2_CLUSTER_UNALLOCATED: if (full_discard || !bs->backing) { continue; @@ -1796,7 +1754,7 @@ static int zero_in_l2_slice(BlockDriverState *bs, uint64_t offset, * Minimize L2 changes if the cluster already reads back as * zeroes with correct allocation. */ - cluster_type = qcow2_get_cluster_type(old_offset); + cluster_type = qcow2_get_cluster_type(bs, old_offset); if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN || (cluster_type == QCOW2_CLUSTER_ZERO_ALLOC && !unmap)) { continue; @@ -1825,6 +1783,16 @@ int qcow2_cluster_zeroize(BlockDriverState *bs, uint64_t offset, int64_t cleared; int ret; + /* If we have to stay in sync with an external data file, zero out + * s->data_file first. */ + if (data_file_is_raw(bs)) { + assert(has_data_file(bs)); + ret = bdrv_co_pwrite_zeroes(s->data_file, offset, bytes, flags); + if (ret < 0) { + return ret; + } + } + /* Caller must pass aligned values, except at image end */ assert(QEMU_IS_ALIGNED(offset, s->cluster_size)); assert(QEMU_IS_ALIGNED(end_offset, s->cluster_size) || @@ -1938,7 +1906,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, uint64_t l2_entry = be64_to_cpu(l2_slice[j]); int64_t offset = l2_entry & L2E_OFFSET_MASK; QCow2ClusterType cluster_type = - qcow2_get_cluster_type(l2_entry); + qcow2_get_cluster_type(bs, l2_entry); if (cluster_type != QCOW2_CLUSTER_ZERO_PLAIN && cluster_type != QCOW2_CLUSTER_ZERO_ALLOC) { @@ -1992,7 +1960,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, } ret = qcow2_pre_write_overlap_check(bs, 0, offset, - s->cluster_size); + s->cluster_size, true); if (ret < 0) { if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { qcow2_free_clusters(bs, offset, s->cluster_size, @@ -2001,7 +1969,8 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, goto fail; } - ret = bdrv_pwrite_zeroes(bs->file, offset, s->cluster_size, 0); + ret = bdrv_pwrite_zeroes(s->data_file, offset, + s->cluster_size, 0); if (ret < 0) { if (cluster_type == QCOW2_CLUSTER_ZERO_PLAIN) { qcow2_free_clusters(bs, offset, s->cluster_size, @@ -2028,7 +1997,7 @@ static int expand_zero_clusters_in_l1(BlockDriverState *bs, uint64_t *l1_table, if (l2_dirty) { ret = qcow2_pre_write_overlap_check( bs, QCOW2_OL_INACTIVE_L2 | QCOW2_OL_ACTIVE_L2, - slice_offset, slice_size2); + slice_offset, slice_size2, false); if (ret < 0) { goto fail; } diff --git a/block/qcow2-refcount.c b/block/qcow2-refcount.c index 3c539f02e5..e0fe322500 100644 --- a/block/qcow2-refcount.c +++ b/block/qcow2-refcount.c @@ -31,7 +31,8 @@ #include "qemu/bswap.h" #include "qemu/cutils.h" -static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size); +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, + uint64_t max); static int QEMU_WARN_UNUSED_RESULT update_refcount(BlockDriverState *bs, int64_t offset, int64_t length, uint64_t addend, bool decrease, enum qcow2_discard_type type); @@ -362,11 +363,14 @@ static int alloc_refcount_block(BlockDriverState *bs, } /* Allocate the refcount block itself and mark it as used */ - int64_t new_block = alloc_clusters_noref(bs, s->cluster_size); + int64_t new_block = alloc_clusters_noref(bs, s->cluster_size, INT64_MAX); if (new_block < 0) { return new_block; } + /* The offset must fit in the offset field of the refcount table entry */ + assert((new_block & REFT_OFFSET_MASK) == new_block); + /* If we're allocating the block at offset 0 then something is wrong */ if (new_block == 0) { qcow2_signal_corruption(bs, true, -1, -1, "Preventing invalid " @@ -954,7 +958,8 @@ int qcow2_update_cluster_refcount(BlockDriverState *bs, /* return < 0 if error */ -static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size) +static int64_t alloc_clusters_noref(BlockDriverState *bs, uint64_t size, + uint64_t max) { BDRVQcow2State *s = bs->opaque; uint64_t i, nb_clusters, refcount; @@ -979,9 +984,9 @@ retry: } /* Make sure that all offsets in the "allocated" range are representable - * in an int64_t */ + * in the requested max */ if (s->free_cluster_index > 0 && - s->free_cluster_index - 1 > (INT64_MAX >> s->cluster_bits)) + s->free_cluster_index - 1 > (max >> s->cluster_bits)) { return -EFBIG; } @@ -1001,7 +1006,7 @@ int64_t qcow2_alloc_clusters(BlockDriverState *bs, uint64_t size) BLKDBG_EVENT(bs->file, BLKDBG_CLUSTER_ALLOC); do { - offset = alloc_clusters_noref(bs, size); + offset = alloc_clusters_noref(bs, size, QCOW_MAX_CLUSTER_OFFSET); if (offset < 0) { return offset; } @@ -1083,7 +1088,11 @@ int64_t qcow2_alloc_bytes(BlockDriverState *bs, int size) free_in_cluster = s->cluster_size - offset_into_cluster(s, offset); do { if (!offset || free_in_cluster < size) { - int64_t new_cluster = alloc_clusters_noref(bs, s->cluster_size); + int64_t new_cluster; + + new_cluster = alloc_clusters_noref(bs, s->cluster_size, + MIN(s->cluster_offset_mask, + QCOW_MAX_CLUSTER_OFFSET)); if (new_cluster < 0) { return new_cluster; } @@ -1147,8 +1156,20 @@ void qcow2_free_any_clusters(BlockDriverState *bs, uint64_t l2_entry, int nb_clusters, enum qcow2_discard_type type) { BDRVQcow2State *s = bs->opaque; + QCow2ClusterType ctype = qcow2_get_cluster_type(bs, l2_entry); - switch (qcow2_get_cluster_type(l2_entry)) { + if (has_data_file(bs)) { + if (s->discard_passthrough[type] && + (ctype == QCOW2_CLUSTER_NORMAL || + ctype == QCOW2_CLUSTER_ZERO_ALLOC)) + { + bdrv_pdiscard(s->data_file, l2_entry & L2E_OFFSET_MASK, + nb_clusters << s->cluster_bits); + } + return; + } + + switch (ctype) { case QCOW2_CLUSTER_COMPRESSED: { int nb_csectors; @@ -1291,7 +1312,7 @@ int qcow2_update_snapshot_refcount(BlockDriverState *bs, entry &= ~QCOW_OFLAG_COPIED; offset = entry & L2E_OFFSET_MASK; - switch (qcow2_get_cluster_type(entry)) { + switch (qcow2_get_cluster_type(bs, entry)) { case QCOW2_CLUSTER_COMPRESSED: nb_csectors = ((entry >> s->csize_shift) & s->csize_mask) + 1; @@ -1573,7 +1594,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, for(i = 0; i < s->l2_size; i++) { l2_entry = be64_to_cpu(l2_table[i]); - switch (qcow2_get_cluster_type(l2_entry)) { + switch (qcow2_get_cluster_type(bs, l2_entry)) { case QCOW2_CLUSTER_COMPRESSED: /* Compressed clusters don't have QCOW_OFLAG_COPIED */ if (l2_entry & QCOW_OFLAG_COPIED) { @@ -1584,6 +1605,13 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, res->corruptions++; } + if (has_data_file(bs)) { + fprintf(stderr, "ERROR compressed cluster %d with data file, " + "entry=0x%" PRIx64 "\n", i, l2_entry); + res->corruptions++; + break; + } + /* Mark cluster as used */ nb_csectors = ((l2_entry >> s->csize_shift) & s->csize_mask) + 1; @@ -1624,7 +1652,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, /* Correct offsets are cluster aligned */ if (offset_into_cluster(s, offset)) { - if (qcow2_get_cluster_type(l2_entry) == + if (qcow2_get_cluster_type(bs, l2_entry) == QCOW2_CLUSTER_ZERO_ALLOC) { fprintf(stderr, "%s offset=%" PRIx64 ": Preallocated zero " @@ -1640,7 +1668,7 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, l2_table[i] = cpu_to_be64(l2_entry); ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2 | QCOW2_OL_INACTIVE_L2, - l2e_offset, sizeof(uint64_t)); + l2e_offset, sizeof(uint64_t), false); if (ret < 0) { fprintf(stderr, "ERROR: Overlap check failed\n"); res->check_errors++; @@ -1674,11 +1702,13 @@ static int check_refcounts_l2(BlockDriverState *bs, BdrvCheckResult *res, } /* Mark cluster as used */ - ret = qcow2_inc_refcounts_imrt(bs, res, - refcount_table, refcount_table_size, - offset, s->cluster_size); - if (ret < 0) { - goto fail; + if (!has_data_file(bs)) { + ret = qcow2_inc_refcounts_imrt(bs, res, refcount_table, + refcount_table_size, + offset, s->cluster_size); + if (ret < 0) { + goto fail; + } } break; } @@ -1859,16 +1889,20 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, for (j = 0; j < s->l2_size; j++) { uint64_t l2_entry = be64_to_cpu(l2_table[j]); uint64_t data_offset = l2_entry & L2E_OFFSET_MASK; - QCow2ClusterType cluster_type = qcow2_get_cluster_type(l2_entry); + QCow2ClusterType cluster_type = qcow2_get_cluster_type(bs, l2_entry); if (cluster_type == QCOW2_CLUSTER_NORMAL || cluster_type == QCOW2_CLUSTER_ZERO_ALLOC) { - ret = qcow2_get_refcount(bs, - data_offset >> s->cluster_bits, - &refcount); - if (ret < 0) { - /* don't print message nor increment check_errors */ - continue; + if (has_data_file(bs)) { + refcount = 1; + } else { + ret = qcow2_get_refcount(bs, + data_offset >> s->cluster_bits, + &refcount); + if (ret < 0) { + /* don't print message nor increment check_errors */ + continue; + } } if ((refcount == 1) != ((l2_entry & QCOW_OFLAG_COPIED) != 0)) { fprintf(stderr, "%s OFLAG_COPIED data cluster: " @@ -1889,7 +1923,8 @@ static int check_oflag_copied(BlockDriverState *bs, BdrvCheckResult *res, if (l2_dirty) { ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L2, - l2_offset, s->cluster_size); + l2_offset, s->cluster_size, + false); if (ret < 0) { fprintf(stderr, "ERROR: Could not write L2 table; metadata " "overlap check failed: %s\n", strerror(-ret)); @@ -2061,6 +2096,12 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res, } /* snapshots */ + if (has_data_file(bs) && s->nb_snapshots) { + fprintf(stderr, "ERROR %d snapshots in image with data file\n", + s->nb_snapshots); + res->corruptions++; + } + for (i = 0; i < s->nb_snapshots; i++) { sn = s->snapshots + i; if (offset_into_cluster(s, sn->l1_table_offset)) { @@ -2357,7 +2398,7 @@ write_refblocks: } ret = qcow2_pre_write_overlap_check(bs, 0, refblock_offset, - s->cluster_size); + s->cluster_size, false); if (ret < 0) { fprintf(stderr, "ERROR writing refblock: %s\n", strerror(-ret)); goto fail; @@ -2408,7 +2449,8 @@ write_refblocks: } ret = qcow2_pre_write_overlap_check(bs, 0, reftable_offset, - reftable_size * sizeof(uint64_t)); + reftable_size * sizeof(uint64_t), + false); if (ret < 0) { fprintf(stderr, "ERROR writing reftable: %s\n", strerror(-ret)); goto fail; @@ -2719,15 +2761,17 @@ int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, } static const char *metadata_ol_names[] = { - [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", - [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", - [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", - [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", - [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", - [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", - [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", - [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", + [QCOW2_OL_MAIN_HEADER_BITNR] = "qcow2_header", + [QCOW2_OL_ACTIVE_L1_BITNR] = "active L1 table", + [QCOW2_OL_ACTIVE_L2_BITNR] = "active L2 table", + [QCOW2_OL_REFCOUNT_TABLE_BITNR] = "refcount table", + [QCOW2_OL_REFCOUNT_BLOCK_BITNR] = "refcount block", + [QCOW2_OL_SNAPSHOT_TABLE_BITNR] = "snapshot table", + [QCOW2_OL_INACTIVE_L1_BITNR] = "inactive L1 table", + [QCOW2_OL_INACTIVE_L2_BITNR] = "inactive L2 table", + [QCOW2_OL_BITMAP_DIRECTORY_BITNR] = "bitmap directory", }; +QEMU_BUILD_BUG_ON(QCOW2_OL_MAX_BITNR != ARRAY_SIZE(metadata_ol_names)); /* * First performs a check for metadata overlaps (through @@ -2740,10 +2784,15 @@ static const char *metadata_ol_names[] = { * overlaps; or a negative value (-errno) on error. */ int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, - int64_t size) + int64_t size, bool data_file) { - int ret = qcow2_check_metadata_overlap(bs, ign, offset, size); + int ret; + if (data_file && has_data_file(bs)) { + return 0; + } + + ret = qcow2_check_metadata_overlap(bs, ign, offset, size); if (ret < 0) { return ret; } else if (ret > 0) { @@ -2844,7 +2893,8 @@ static int flush_refblock(BlockDriverState *bs, uint64_t **reftable, if (reftable_index < *reftable_size && (*reftable)[reftable_index]) { offset = (*reftable)[reftable_index]; - ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size); + ret = qcow2_pre_write_overlap_check(bs, 0, offset, s->cluster_size, + false); if (ret < 0) { error_setg_errno(errp, -ret, "Overlap check failed"); return ret; @@ -3110,7 +3160,8 @@ int qcow2_change_refcount_order(BlockDriverState *bs, int refcount_order, /* Write the new reftable */ ret = qcow2_pre_write_overlap_check(bs, 0, new_reftable_offset, - new_reftable_size * sizeof(uint64_t)); + new_reftable_size * sizeof(uint64_t), + false); if (ret < 0) { error_setg_errno(errp, -ret, "Overlap check failed"); goto done; diff --git a/block/qcow2-snapshot.c b/block/qcow2-snapshot.c index bb6a5b7516..a6ffae89a6 100644 --- a/block/qcow2-snapshot.c +++ b/block/qcow2-snapshot.c @@ -184,7 +184,7 @@ static int qcow2_write_snapshots(BlockDriverState *bs) /* The snapshot list position has not yet been updated, so these clusters * must indeed be completely free */ - ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size); + ret = qcow2_pre_write_overlap_check(bs, 0, offset, snapshots_size, false); if (ret < 0) { goto fail; } @@ -353,16 +353,15 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) return -EFBIG; } + if (has_data_file(bs)) { + return -ENOTSUP; + } + memset(sn, 0, sizeof(*sn)); /* Generate an ID */ find_new_snapshot_id(bs, sn_info->id_str, sizeof(sn_info->id_str)); - /* Check that the ID is unique */ - if (find_snapshot_by_id_and_name(bs, sn_info->id_str, NULL) >= 0) { - return -EEXIST; - } - /* Populate sn with passed data */ sn->id_str = g_strdup(sn_info->id_str); sn->name = g_strdup(sn_info->name); @@ -394,7 +393,7 @@ int qcow2_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } ret = qcow2_pre_write_overlap_check(bs, 0, sn->l1_table_offset, - s->l1_size * sizeof(uint64_t)); + s->l1_size * sizeof(uint64_t), false); if (ret < 0) { goto fail; } @@ -471,6 +470,10 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) int ret; uint64_t *sn_l1_table = NULL; + if (has_data_file(bs)) { + return -ENOTSUP; + } + /* Search the snapshot */ snapshot_index = find_snapshot_by_id_or_name(bs, snapshot_id); if (snapshot_index < 0) { @@ -533,7 +536,8 @@ int qcow2_snapshot_goto(BlockDriverState *bs, const char *snapshot_id) } ret = qcow2_pre_write_overlap_check(bs, QCOW2_OL_ACTIVE_L1, - s->l1_table_offset, cur_l1_bytes); + s->l1_table_offset, cur_l1_bytes, + false); if (ret < 0) { goto fail; } @@ -603,6 +607,10 @@ int qcow2_snapshot_delete(BlockDriverState *bs, QCowSnapshot sn; int snapshot_index, ret; + if (has_data_file(bs)) { + return -ENOTSUP; + } + /* Search the snapshot */ snapshot_index = find_snapshot_by_id_and_name(bs, snapshot_id, name); if (snapshot_index < 0) { @@ -674,6 +682,9 @@ int qcow2_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_tab) QCowSnapshot *sn; int i; + if (has_data_file(bs)) { + return -ENOTSUP; + } if (!s->nb_snapshots) { *psn_tab = NULL; return s->nb_snapshots; diff --git a/block/qcow2.c b/block/qcow2.c index ec9e6238a0..3ace3b2209 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -73,6 +73,14 @@ typedef struct { #define QCOW2_EXT_MAGIC_FEATURE_TABLE 0x6803f857 #define QCOW2_EXT_MAGIC_CRYPTO_HEADER 0x0537be77 #define QCOW2_EXT_MAGIC_BITMAPS 0x23852875 +#define QCOW2_EXT_MAGIC_DATA_FILE 0x44415441 + +static int coroutine_fn +qcow2_co_preadv_compressed(BlockDriverState *bs, + uint64_t file_cluster_offset, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov); static int qcow2_probe(const uint8_t *buf, int buf_size, const char *filename) { @@ -132,7 +140,7 @@ static ssize_t qcow2_crypto_hdr_init_func(QCryptoBlock *block, size_t headerlen, /* Zero fill remaining space in cluster so it has predictable * content in case of future spec changes */ clusterlen = size_to_clusters(s, headerlen) * s->cluster_size; - assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen) == 0); + assert(qcow2_pre_write_overlap_check(bs, 0, ret, clusterlen, false) == 0); ret = bdrv_pwrite_zeroes(bs->file, ret + headerlen, clusterlen - headerlen, 0); @@ -210,8 +218,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, "pread fail from offset %" PRIu64, offset); return 1; } - be32_to_cpus(&ext.magic); - be32_to_cpus(&ext.len); + ext.magic = be32_to_cpu(ext.magic); + ext.len = be32_to_cpu(ext.len); offset += sizeof(ext); #ifdef DEBUG_EXT printf("ext.magic = 0x%x\n", ext.magic); @@ -279,8 +287,8 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, "Unable to read CRYPTO header extension"); return ret; } - be64_to_cpus(&s->crypto_header.offset); - be64_to_cpus(&s->crypto_header.length); + s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset); + s->crypto_header.length = be64_to_cpu(s->crypto_header.length); if ((s->crypto_header.offset % s->cluster_size) != 0) { error_setg(errp, "Encryption header offset '%" PRIu64 "' is " @@ -294,7 +302,7 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, } s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", qcow2_crypto_hdr_read_func, - bs, cflags, errp); + bs, cflags, 1, errp); if (!s->crypto) { return -EINVAL; } @@ -342,9 +350,11 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, return -EINVAL; } - be32_to_cpus(&bitmaps_ext.nb_bitmaps); - be64_to_cpus(&bitmaps_ext.bitmap_directory_size); - be64_to_cpus(&bitmaps_ext.bitmap_directory_offset); + bitmaps_ext.nb_bitmaps = be32_to_cpu(bitmaps_ext.nb_bitmaps); + bitmaps_ext.bitmap_directory_size = + be64_to_cpu(bitmaps_ext.bitmap_directory_size); + bitmaps_ext.bitmap_directory_offset = + be64_to_cpu(bitmaps_ext.bitmap_directory_offset); if (bitmaps_ext.nb_bitmaps > QCOW2_MAX_BITMAPS) { error_setg(errp, @@ -388,6 +398,21 @@ static int qcow2_read_extensions(BlockDriverState *bs, uint64_t start_offset, #endif break; + case QCOW2_EXT_MAGIC_DATA_FILE: + { + s->image_data_file = g_malloc0(ext.len + 1); + ret = bdrv_pread(bs->file, offset, s->image_data_file, ext.len); + if (ret < 0) { + error_setg_errno(errp, -ret, + "ERROR: Could not read data file name"); + return ret; + } +#ifdef DEBUG_EXT + printf("Qcow2: Got external data file %s\n", s->image_data_file); +#endif + break; + } + default: /* unknown magic - save it in case we need to rewrite the header */ /* If you add a new feature, make sure to also update the fast @@ -602,6 +627,30 @@ int qcow2_validate_table(BlockDriverState *bs, uint64_t offset, return 0; } +static const char *const mutable_opts[] = { + QCOW2_OPT_LAZY_REFCOUNTS, + QCOW2_OPT_DISCARD_REQUEST, + QCOW2_OPT_DISCARD_SNAPSHOT, + QCOW2_OPT_DISCARD_OTHER, + QCOW2_OPT_OVERLAP, + QCOW2_OPT_OVERLAP_TEMPLATE, + QCOW2_OPT_OVERLAP_MAIN_HEADER, + QCOW2_OPT_OVERLAP_ACTIVE_L1, + QCOW2_OPT_OVERLAP_ACTIVE_L2, + QCOW2_OPT_OVERLAP_REFCOUNT_TABLE, + QCOW2_OPT_OVERLAP_REFCOUNT_BLOCK, + QCOW2_OPT_OVERLAP_SNAPSHOT_TABLE, + QCOW2_OPT_OVERLAP_INACTIVE_L1, + QCOW2_OPT_OVERLAP_INACTIVE_L2, + QCOW2_OPT_OVERLAP_BITMAP_DIRECTORY, + QCOW2_OPT_CACHE_SIZE, + QCOW2_OPT_L2_CACHE_SIZE, + QCOW2_OPT_L2_CACHE_ENTRY_SIZE, + QCOW2_OPT_REFCOUNT_CACHE_SIZE, + QCOW2_OPT_CACHE_CLEAN_INTERVAL, + NULL +}; + static QemuOptsList qcow2_runtime_opts = { .name = "qcow2", .head = QTAILQ_HEAD_INITIALIZER(qcow2_runtime_opts.head), @@ -777,29 +826,37 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, uint64_t *refcount_cache_size, Error **errp) { BDRVQcow2State *s = bs->opaque; - uint64_t combined_cache_size; + uint64_t combined_cache_size, l2_cache_max_setting; bool l2_cache_size_set, refcount_cache_size_set, combined_cache_size_set; + bool l2_cache_entry_size_set; int min_refcount_cache = MIN_REFCOUNT_CACHE_SIZE * s->cluster_size; + uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; + uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); combined_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_CACHE_SIZE); l2_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_SIZE); refcount_cache_size_set = qemu_opt_get(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE); + l2_cache_entry_size_set = qemu_opt_get(opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE); combined_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_CACHE_SIZE, 0); - *l2_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, 0); + l2_cache_max_setting = qemu_opt_get_size(opts, QCOW2_OPT_L2_CACHE_SIZE, + DEFAULT_L2_CACHE_MAX_SIZE); *refcount_cache_size = qemu_opt_get_size(opts, QCOW2_OPT_REFCOUNT_CACHE_SIZE, 0); *l2_cache_entry_size = qemu_opt_get_size( opts, QCOW2_OPT_L2_CACHE_ENTRY_SIZE, s->cluster_size); + *l2_cache_size = MIN(max_l2_cache, l2_cache_max_setting); + if (combined_cache_size_set) { if (l2_cache_size_set && refcount_cache_size_set) { error_setg(errp, QCOW2_OPT_CACHE_SIZE ", " QCOW2_OPT_L2_CACHE_SIZE " and " QCOW2_OPT_REFCOUNT_CACHE_SIZE " may not be set " "at the same time"); return; - } else if (*l2_cache_size > combined_cache_size) { + } else if (l2_cache_size_set && + (l2_cache_max_setting > combined_cache_size)) { error_setg(errp, QCOW2_OPT_L2_CACHE_SIZE " may not exceed " QCOW2_OPT_CACHE_SIZE); return; @@ -814,9 +871,6 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, } else if (refcount_cache_size_set) { *l2_cache_size = combined_cache_size - *refcount_cache_size; } else { - uint64_t virtual_disk_size = bs->total_sectors * BDRV_SECTOR_SIZE; - uint64_t max_l2_cache = virtual_disk_size / (s->cluster_size / 8); - /* Assign as much memory as possible to the L2 cache, and * use the remainder for the refcount cache */ if (combined_cache_size >= max_l2_cache + min_refcount_cache) { @@ -828,17 +882,20 @@ static void read_cache_sizes(BlockDriverState *bs, QemuOpts *opts, *l2_cache_size = combined_cache_size - *refcount_cache_size; } } - } else { - if (!l2_cache_size_set) { - *l2_cache_size = MAX(DEFAULT_L2_CACHE_BYTE_SIZE, - (uint64_t)DEFAULT_L2_CACHE_CLUSTERS - * s->cluster_size); - } - if (!refcount_cache_size_set) { - *refcount_cache_size = min_refcount_cache; - } } + /* + * If the L2 cache is not enough to cover the whole disk then + * default to 4KB entries. Smaller entries reduce the cost of + * loads and evictions and increase I/O performance. + */ + if (*l2_cache_size < max_l2_cache && !l2_cache_entry_size_set) { + *l2_cache_entry_size = MIN(s->cluster_size, 4096); + } + + /* l2_cache_size and refcount_cache_size are ensured to have at least + * their minimum values in qcow2_update_options_prepare() */ + if (*l2_cache_entry_size < (1 << MIN_CLUSTER_BITS) || *l2_cache_entry_size > s->cluster_size || !is_power_of_2(*l2_cache_entry_size)) { @@ -948,7 +1005,7 @@ static int qcow2_update_options_prepare(BlockDriverState *bs, /* New interval for cache cleanup timer */ r->cache_clean_interval = qemu_opt_get_number(opts, QCOW2_OPT_CACHE_CLEAN_INTERVAL, - s->cache_clean_interval); + DEFAULT_CACHE_CLEAN_INTERVAL); #ifndef CONFIG_LINUX if (r->cache_clean_interval != 0) { error_setg(errp, QCOW2_OPT_CACHE_CLEAN_INTERVAL @@ -1157,26 +1214,26 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, uint64_t ext_end; uint64_t l1_vm_state_index; bool update_header = false; - bool header_updated = false; ret = bdrv_pread(bs->file, 0, &header, sizeof(header)); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read qcow2 header"); goto fail; } - be32_to_cpus(&header.magic); - be32_to_cpus(&header.version); - be64_to_cpus(&header.backing_file_offset); - be32_to_cpus(&header.backing_file_size); - be64_to_cpus(&header.size); - be32_to_cpus(&header.cluster_bits); - be32_to_cpus(&header.crypt_method); - be64_to_cpus(&header.l1_table_offset); - be32_to_cpus(&header.l1_size); - be64_to_cpus(&header.refcount_table_offset); - be32_to_cpus(&header.refcount_table_clusters); - be64_to_cpus(&header.snapshots_offset); - be32_to_cpus(&header.nb_snapshots); + header.magic = be32_to_cpu(header.magic); + header.version = be32_to_cpu(header.version); + header.backing_file_offset = be64_to_cpu(header.backing_file_offset); + header.backing_file_size = be32_to_cpu(header.backing_file_size); + header.size = be64_to_cpu(header.size); + header.cluster_bits = be32_to_cpu(header.cluster_bits); + header.crypt_method = be32_to_cpu(header.crypt_method); + header.l1_table_offset = be64_to_cpu(header.l1_table_offset); + header.l1_size = be32_to_cpu(header.l1_size); + header.refcount_table_offset = be64_to_cpu(header.refcount_table_offset); + header.refcount_table_clusters = + be32_to_cpu(header.refcount_table_clusters); + header.snapshots_offset = be64_to_cpu(header.snapshots_offset); + header.nb_snapshots = be32_to_cpu(header.nb_snapshots); if (header.magic != QCOW_MAGIC) { error_setg(errp, "Image is not in qcow2 format"); @@ -1212,11 +1269,12 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, header.refcount_order = 4; header.header_length = 72; } else { - be64_to_cpus(&header.incompatible_features); - be64_to_cpus(&header.compatible_features); - be64_to_cpus(&header.autoclear_features); - be32_to_cpus(&header.refcount_order); - be32_to_cpus(&header.header_length); + header.incompatible_features = + be64_to_cpu(header.incompatible_features); + header.compatible_features = be64_to_cpu(header.compatible_features); + header.autoclear_features = be64_to_cpu(header.autoclear_features); + header.refcount_order = be32_to_cpu(header.refcount_order); + header.header_length = be32_to_cpu(header.header_length); if (header.header_length < 104) { error_setg(errp, "qcow2 header too short"); @@ -1328,7 +1386,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, /* 2^(s->refcount_order - 3) is the refcount width in bytes */ s->refcount_block_bits = s->cluster_bits - (s->refcount_order - 3); s->refcount_block_size = 1 << s->refcount_block_bits; - bs->total_sectors = header.size / 512; + bs->total_sectors = header.size / BDRV_SECTOR_SIZE; s->csize_shift = (62 - (s->cluster_bits - 8)); s->csize_mask = (1 << (s->cluster_bits - 8)) - 1; s->cluster_offset_mask = (1LL << s->csize_shift) - 1; @@ -1405,7 +1463,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, goto fail; } for(i = 0;i < s->l1_size; i++) { - be64_to_cpus(&s->l1_table[i]); + s->l1_table[i] = be64_to_cpu(s->l1_table[i]); } } @@ -1415,7 +1473,6 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, goto fail; } - s->cluster_cache_offset = -1; s->flags = flags; ret = qcow2_refcount_init(bs); @@ -1435,6 +1492,47 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, goto fail; } + /* Open external data file */ + s->data_file = bdrv_open_child(NULL, options, "data-file", bs, &child_file, + true, &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto fail; + } + + if (s->incompatible_features & QCOW2_INCOMPAT_DATA_FILE) { + if (!s->data_file && s->image_data_file) { + s->data_file = bdrv_open_child(s->image_data_file, options, + "data-file", bs, &child_file, + false, errp); + if (!s->data_file) { + ret = -EINVAL; + goto fail; + } + } + if (!s->data_file) { + error_setg(errp, "'data-file' is required for this image"); + ret = -EINVAL; + goto fail; + } + } else { + if (s->data_file) { + error_setg(errp, "'data-file' can only be set for images with an " + "external data file"); + ret = -EINVAL; + goto fail; + } + + s->data_file = bs->file; + + if (data_file_is_raw(bs)) { + error_setg(errp, "data-file-raw requires a data file"); + ret = -EINVAL; + goto fail; + } + } + /* qcow2_read_extension may have set up the crypto context * if the crypt method needs a header region, some methods * don't need header extensions, so must check here @@ -1446,7 +1544,7 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, cflags |= QCRYPTO_BLOCK_OPEN_NO_IO; } s->crypto = qcrypto_block_open(s->crypto_opts, "encrypt.", - NULL, NULL, cflags, errp); + NULL, NULL, cflags, 1, errp); if (!s->crypto) { ret = -EINVAL; goto fail; @@ -1469,13 +1567,15 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, goto fail; } ret = bdrv_pread(bs->file, header.backing_file_offset, - bs->backing_file, len); + bs->auto_backing_file, len); if (ret < 0) { error_setg_errno(errp, -ret, "Could not read backing file name"); goto fail; } - bs->backing_file[len] = '\0'; - s->image_backing_file = g_strdup(bs->backing_file); + bs->auto_backing_file[len] = '\0'; + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->auto_backing_file); + s->image_backing_file = g_strdup(bs->auto_backing_file); } /* Internal snapshots */ @@ -1496,23 +1596,70 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, s->autoclear_features &= QCOW2_AUTOCLEAR_MASK; } - if (s->dirty_bitmaps_loaded) { - /* It's some kind of reopen. There are no known cases where we need to - * reload bitmaps in such a situation, so it's safer to skip them. - * - * Moreover, if we have some readonly bitmaps and we are reopening for - * rw we should reopen bitmaps correspondingly. - */ - if (bdrv_has_readonly_bitmaps(bs) && - !bdrv_is_read_only(bs) && !(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) - { - qcow2_reopen_bitmaps_rw_hint(bs, &header_updated, &local_err); - } - } else { - header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); - s->dirty_bitmaps_loaded = true; + /* == Handle persistent dirty bitmaps == + * + * We want load dirty bitmaps in three cases: + * + * 1. Normal open of the disk in active mode, not related to invalidation + * after migration. + * + * 2. Invalidation of the target vm after pre-copy phase of migration, if + * bitmaps are _not_ migrating through migration channel, i.e. + * 'dirty-bitmaps' capability is disabled. + * + * 3. Invalidation of source vm after failed or canceled migration. + * This is a very interesting case. There are two possible types of + * bitmaps: + * + * A. Stored on inactivation and removed. They should be loaded from the + * image. + * + * B. Not stored: not-persistent bitmaps and bitmaps, migrated through + * the migration channel (with dirty-bitmaps capability). + * + * On the other hand, there are two possible sub-cases: + * + * 3.1 disk was changed by somebody else while were inactive. In this + * case all in-RAM dirty bitmaps (both persistent and not) are + * definitely invalid. And we don't have any method to determine + * this. + * + * Simple and safe thing is to just drop all the bitmaps of type B on + * inactivation. But in this case we lose bitmaps in valid 4.2 case. + * + * On the other hand, resuming source vm, if disk was already changed + * is a bad thing anyway: not only bitmaps, the whole vm state is + * out of sync with disk. + * + * This means, that user or management tool, who for some reason + * decided to resume source vm, after disk was already changed by + * target vm, should at least drop all dirty bitmaps by hand. + * + * So, we can ignore this case for now, but TODO: "generation" + * extension for qcow2, to determine, that image was changed after + * last inactivation. And if it is changed, we will drop (or at least + * mark as 'invalid' all the bitmaps of type B, both persistent + * and not). + * + * 3.2 disk was _not_ changed while were inactive. Bitmaps may be saved + * to disk ('dirty-bitmaps' capability disabled), or not saved + * ('dirty-bitmaps' capability enabled), but we don't need to care + * of: let's load bitmaps as always: stored bitmaps will be loaded, + * and not stored has flag IN_USE=1 in the image and will be skipped + * on loading. + * + * One remaining possible case when we don't want load bitmaps: + * + * 4. Open disk in inactive mode in target vm (bitmaps are migrating or + * will be loaded on invalidation, no needs try loading them before) + */ + + if (!(bdrv_get_flags(bs) & BDRV_O_INACTIVE)) { + /* It's case 1, 2 or 3.2. Or 3.1 which is BUG in management layer. */ + bool header_updated = qcow2_load_dirty_bitmaps(bs, &local_err); + + update_header = update_header && !header_updated; } - update_header = update_header && !header_updated; if (local_err != NULL) { error_propagate(errp, local_err); ret = -EINVAL; @@ -1557,6 +1704,10 @@ static int coroutine_fn qcow2_do_open(BlockDriverState *bs, QDict *options, return ret; fail: + g_free(s->image_data_file); + if (has_data_file(bs)) { + bdrv_unref_child(bs, s->data_file); + } g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); qcow2_free_snapshots(bs); @@ -1619,6 +1770,7 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, /* From bdrv_co_create. */ qcow2_open_entry(&qoc); } else { + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); qemu_coroutine_enter(qemu_coroutine_create(qcow2_open_entry, &qoc)); BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); } @@ -1631,7 +1783,7 @@ static void qcow2_refresh_limits(BlockDriverState *bs, Error **errp) if (bs->encrypted) { /* Encryption works on a sector granularity */ - bs->bl.request_alignment = BDRV_SECTOR_SIZE; + bs->bl.request_alignment = qcrypto_block_get_sector_size(s->crypto); } bs->bl.pwrite_zeroes_alignment = s->cluster_size; bs->bl.pdiscard_alignment = s->cluster_size; @@ -1758,11 +1910,11 @@ static int coroutine_fn qcow2_co_block_status(BlockDriverState *bs, *pnum = bytes; - if (cluster_offset != 0 && ret != QCOW2_CLUSTER_COMPRESSED && + if ((ret == QCOW2_CLUSTER_NORMAL || ret == QCOW2_CLUSTER_ZERO_ALLOC) && !s->crypto) { index_in_cluster = offset & (s->cluster_size - 1); *map = cluster_offset | index_in_cluster; - *file = bs->file->bs; + *file = s->data_file->bs; status |= BDRV_BLOCK_OFFSET_VALID; } if (ret == QCOW2_CLUSTER_ZERO_PLAIN || ret == QCOW2_CLUSTER_ZERO_ALLOC) { @@ -1868,15 +2020,15 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, break; case QCOW2_CLUSTER_COMPRESSED: - /* add AIO support for compressed blocks ? */ - ret = qcow2_decompress_cluster(bs, cluster_offset); + qemu_co_mutex_unlock(&s->lock); + ret = qcow2_co_preadv_compressed(bs, cluster_offset, + offset, cur_bytes, + &hd_qiov); + qemu_co_mutex_lock(&s->lock); if (ret < 0) { goto fail; } - qemu_iovec_from_buf(&hd_qiov, 0, - s->cluster_cache + offset_in_cluster, - cur_bytes); break; case QCOW2_CLUSTER_NORMAL: @@ -1894,7 +2046,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, */ if (!cluster_data) { cluster_data = - qemu_try_blockalign(bs->file->bs, + qemu_try_blockalign(s->data_file->bs, QCOW_MAX_CRYPT_CLUSTERS * s->cluster_size); if (cluster_data == NULL) { @@ -1910,7 +2062,7 @@ static coroutine_fn int qcow2_co_preadv(BlockDriverState *bs, uint64_t offset, BLKDBG_EVENT(bs->file, BLKDBG_READ_AIO); qemu_co_mutex_unlock(&s->lock); - ret = bdrv_co_preadv(bs->file, + ret = bdrv_co_preadv(s->data_file, cluster_offset + offset_in_cluster, cur_bytes, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); @@ -2012,8 +2164,6 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, qemu_iovec_init(&hd_qiov, qiov->niov); - s->cluster_cache_offset = -1; /* disable compressed cache */ - qemu_co_mutex_lock(&s->lock); while (bytes != 0) { @@ -2071,7 +2221,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, } ret = qcow2_pre_write_overlap_check(bs, 0, - cluster_offset + offset_in_cluster, cur_bytes); + cluster_offset + offset_in_cluster, cur_bytes, true); if (ret < 0) { goto fail; } @@ -2085,7 +2235,7 @@ static coroutine_fn int qcow2_co_pwritev(BlockDriverState *bs, uint64_t offset, BLKDBG_EVENT(bs->file, BLKDBG_WRITE_AIO); trace_qcow2_writev_data(qemu_coroutine_self(), cluster_offset + offset_in_cluster); - ret = bdrv_co_pwritev(bs->file, + ret = bdrv_co_pwritev(s->data_file, cluster_offset + offset_in_cluster, cur_bytes, &hd_qiov, 0); qemu_co_mutex_lock(&s->lock); @@ -2127,9 +2277,9 @@ static int qcow2_inactivate(BlockDriverState *bs) qcow2_store_persistent_dirty_bitmaps(bs, &local_err); if (local_err != NULL) { result = -EINVAL; - error_report_err(local_err); - error_report("Persistent bitmaps are lost for node '%s'", - bdrv_get_device_or_node_name(bs)); + error_reportf_err(local_err, "Lost persistent bitmaps during " + "inactivation of node '%s': ", + bdrv_get_device_or_node_name(bs)); } ret = qcow2_cache_flush(bs, s->l2_table_cache); @@ -2174,11 +2324,14 @@ static void qcow2_close(BlockDriverState *bs) g_free(s->unknown_header_fields); cleanup_unknown_header_ext(bs); + g_free(s->image_data_file); g_free(s->image_backing_file); g_free(s->image_backing_format); - g_free(s->cluster_cache); - qemu_vfree(s->cluster_data); + if (has_data_file(bs)) { + bdrv_unref_child(bs, s->data_file); + } + qcow2_refcount_close(bs); qcow2_free_snapshots(bs); } @@ -2212,8 +2365,8 @@ static void coroutine_fn qcow2_co_invalidate_cache(BlockDriverState *bs, qemu_co_mutex_unlock(&s->lock); qobject_unref(options); if (local_err) { - error_propagate(errp, local_err); - error_prepend(errp, "Could not reopen qcow2 layer: "); + error_propagate_prepend(errp, local_err, + "Could not reopen qcow2 layer: "); bs->drv = NULL; return; } else if (ret < 0) { @@ -2348,15 +2501,28 @@ int qcow2_update_header(BlockDriverState *bs) buflen -= ret; } + /* External data file header extension */ + if (has_data_file(bs) && s->image_data_file) { + ret = header_ext_add(buf, QCOW2_EXT_MAGIC_DATA_FILE, + s->image_data_file, strlen(s->image_data_file), + buflen); + if (ret < 0) { + goto fail; + } + + buf += ret; + buflen -= ret; + } + /* Full disk encryption header pointer extension */ if (s->crypto_header.offset != 0) { - cpu_to_be64s(&s->crypto_header.offset); - cpu_to_be64s(&s->crypto_header.length); + s->crypto_header.offset = cpu_to_be64(s->crypto_header.offset); + s->crypto_header.length = cpu_to_be64(s->crypto_header.length); ret = header_ext_add(buf, QCOW2_EXT_MAGIC_CRYPTO_HEADER, &s->crypto_header, sizeof(s->crypto_header), buflen); - be64_to_cpus(&s->crypto_header.offset); - be64_to_cpus(&s->crypto_header.length); + s->crypto_header.offset = be64_to_cpu(s->crypto_header.offset); + s->crypto_header.length = be64_to_cpu(s->crypto_header.length); if (ret < 0) { goto fail; } @@ -2377,6 +2543,11 @@ int qcow2_update_header(BlockDriverState *bs) .bit = QCOW2_INCOMPAT_CORRUPT_BITNR, .name = "corrupt bit", }, + { + .type = QCOW2_FEAT_TYPE_INCOMPATIBLE, + .bit = QCOW2_INCOMPAT_DATA_FILE_BITNR, + .name = "external data file", + }, { .type = QCOW2_FEAT_TYPE_COMPATIBLE, .bit = QCOW2_COMPAT_LAZY_REFCOUNTS_BITNR, @@ -2465,10 +2636,18 @@ static int qcow2_change_backing_file(BlockDriverState *bs, { BDRVQcow2State *s = bs->opaque; + /* Adding a backing file means that the external data file alone won't be + * enough to make sense of the content */ + if (backing_file && data_file_is_raw(bs)) { + return -EINVAL; + } + if (backing_file && strlen(backing_file) > 1023) { return -EINVAL; } + pstrcpy(bs->auto_backing_file, sizeof(bs->auto_backing_file), + backing_file ?: ""); pstrcpy(bs->backing_file, sizeof(bs->backing_file), backing_file ?: ""); pstrcpy(bs->backing_format, sizeof(bs->backing_format), backing_fmt ?: ""); @@ -2544,6 +2723,7 @@ static int qcow2_set_up_encryption(BlockDriverState *bs, static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, uint64_t new_length) { + BDRVQcow2State *s = bs->opaque; uint64_t bytes; uint64_t host_offset = 0; unsigned int cur_bytes; @@ -2592,7 +2772,7 @@ static int coroutine_fn preallocate_co(BlockDriverState *bs, uint64_t offset, */ if (host_offset != 0) { uint8_t data = 0; - ret = bdrv_pwrite(bs->file, (host_offset + cur_bytes) - 1, + ret = bdrv_pwrite(s->data_file, (host_offset + cur_bytes) - 1, &data, 1); if (ret < 0) { return ret; @@ -2776,6 +2956,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) */ BlockBackend *blk = NULL; BlockDriverState *bs = NULL; + BlockDriverState *data_bs = NULL; QCowHeader *header; size_t cluster_size; int version; @@ -2872,6 +3053,32 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } refcount_order = ctz32(qcow2_opts->refcount_bits); + if (qcow2_opts->data_file_raw && !qcow2_opts->data_file) { + error_setg(errp, "data-file-raw requires data-file"); + ret = -EINVAL; + goto out; + } + if (qcow2_opts->data_file_raw && qcow2_opts->has_backing_file) { + error_setg(errp, "Backing file and data-file-raw cannot be used at " + "the same time"); + ret = -EINVAL; + goto out; + } + + if (qcow2_opts->data_file) { + if (version < 3) { + error_setg(errp, "External data files are only supported with " + "compatibility level 1.1 and above (use version=v3 or " + "greater)"); + ret = -EINVAL; + goto out; + } + data_bs = bdrv_open_blockdev_ref(qcow2_opts->data_file, errp); + if (data_bs == NULL) { + ret = -EIO; + goto out; + } + } /* Create BlockBackend to write to the image */ blk = blk_new(BLK_PERM_WRITE | BLK_PERM_RESIZE, BLK_PERM_ALL); @@ -2887,19 +3094,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) goto out; } - if (qcow2_opts->preallocation == PREALLOC_MODE_FULL || - qcow2_opts->preallocation == PREALLOC_MODE_FALLOC) - { - int64_t prealloc_size = - qcow2_calc_prealloc_size(qcow2_opts->size, cluster_size, - refcount_order); - - ret = blk_truncate(blk, prealloc_size, qcow2_opts->preallocation, errp); - if (ret < 0) { - goto out; - } - } - /* Write the header */ QEMU_BUILD_BUG_ON((1 << MIN_CLUSTER_BITS) < sizeof(*header)); header = g_malloc0(cluster_size); @@ -2923,6 +3117,14 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) header->compatible_features |= cpu_to_be64(QCOW2_COMPAT_LAZY_REFCOUNTS); } + if (data_bs) { + header->incompatible_features |= + cpu_to_be64(QCOW2_INCOMPAT_DATA_FILE); + } + if (qcow2_opts->data_file_raw) { + header->autoclear_features |= + cpu_to_be64(QCOW2_AUTOCLEAR_DATA_FILE_RAW); + } ret = blk_pwrite(blk, 0, header, cluster_size, 0); g_free(header); @@ -2953,6 +3155,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) options = qdict_new(); qdict_put_str(options, "driver", "qcow2"); qdict_put_str(options, "file", bs->node_name); + if (data_bs) { + qdict_put_str(options, "data-file", data_bs->node_name); + } blk = blk_new_open(NULL, NULL, options, BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_NO_FLUSH, &local_err); @@ -2973,6 +3178,12 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) abort(); } + /* Set the external data file if necessary */ + if (data_bs) { + BDRVQcow2State *s = blk_bs(blk)->opaque; + s->image_data_file = g_strdup(data_bs->filename); + } + /* Create a full header (including things like feature table) */ ret = qcow2_update_header(blk_bs(blk)); if (ret < 0) { @@ -2981,7 +3192,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } /* Okay, now that we have a valid image, let's give it the right size */ - ret = blk_truncate(blk, qcow2_opts->size, PREALLOC_MODE_OFF, errp); + ret = blk_truncate(blk, qcow2_opts->size, qcow2_opts->preallocation, errp); if (ret < 0) { error_prepend(errp, "Could not resize image: "); goto out; @@ -3013,19 +3224,6 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) } } - /* And if we're supposed to preallocate metadata, do that now */ - if (qcow2_opts->preallocation != PREALLOC_MODE_OFF) { - BDRVQcow2State *s = blk_bs(blk)->opaque; - qemu_co_mutex_lock(&s->lock); - ret = preallocate_co(blk_bs(blk), 0, qcow2_opts->size); - qemu_co_mutex_unlock(&s->lock); - - if (ret < 0) { - error_setg_errno(errp, -ret, "Could not preallocate metadata"); - goto out; - } - } - blk_unref(blk); blk = NULL; @@ -3038,6 +3236,9 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) options = qdict_new(); qdict_put_str(options, "driver", "qcow2"); qdict_put_str(options, "file", bs->node_name); + if (data_bs) { + qdict_put_str(options, "data-file", data_bs->node_name); + } blk = blk_new_open(NULL, NULL, options, BDRV_O_RDWR | BDRV_O_NO_BACKING | BDRV_O_NO_IO, &local_err); @@ -3051,6 +3252,7 @@ qcow2_co_create(BlockdevCreateOptions *create_options, Error **errp) out: blk_unref(blk); bdrv_unref(bs); + bdrv_unref(data_bs); return ret; } @@ -3061,6 +3263,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt QDict *qdict; Visitor *v; BlockDriverState *bs = NULL; + BlockDriverState *data_bs = NULL; Error *local_err = NULL; const char *val; int ret; @@ -3103,6 +3306,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt { BLOCK_OPT_REFCOUNT_BITS, "refcount-bits" }, { BLOCK_OPT_ENCRYPT, BLOCK_OPT_ENCRYPT_FORMAT }, { BLOCK_OPT_COMPAT_LEVEL, "version" }, + { BLOCK_OPT_DATA_FILE_RAW, "data-file-raw" }, { NULL, NULL }, }; @@ -3124,6 +3328,26 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt goto finish; } + /* Create and open an external data file (protocol layer) */ + val = qdict_get_try_str(qdict, BLOCK_OPT_DATA_FILE); + if (val) { + ret = bdrv_create_file(val, opts, errp); + if (ret < 0) { + goto finish; + } + + data_bs = bdrv_open(val, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, + errp); + if (data_bs == NULL) { + ret = -EIO; + goto finish; + } + + qdict_del(qdict, BLOCK_OPT_DATA_FILE); + qdict_put_str(qdict, "data-file", data_bs->node_name); + } + /* Set 'driver' and 'node' options */ qdict_put_str(qdict, "driver", "qcow2"); qdict_put_str(qdict, "file", bs->node_name); @@ -3158,6 +3382,7 @@ static int coroutine_fn qcow2_co_create_opts(const char *filename, QemuOpts *opt finish: qobject_unref(qdict); bdrv_unref(bs); + bdrv_unref(data_bs); qapi_free_BlockdevCreateOptions(create_options); return ret; } @@ -3308,7 +3533,7 @@ qcow2_co_copy_range_from(BlockDriverState *bs, goto out; case QCOW2_CLUSTER_NORMAL: - child = bs->file; + child = s->data_file; copy_offset += offset_into_cluster(s, src_offset); if ((copy_offset & 511) != 0) { ret = -EIO; @@ -3355,7 +3580,6 @@ qcow2_co_copy_range_to(BlockDriverState *bs, QCowL2Meta *l2meta = NULL; assert(!bs->encrypted); - s->cluster_cache_offset = -1; /* disable compressed cache */ qemu_co_mutex_lock(&s->lock); @@ -3379,14 +3603,14 @@ qcow2_co_copy_range_to(BlockDriverState *bs, assert((cluster_offset & 511) == 0); ret = qcow2_pre_write_overlap_check(bs, 0, - cluster_offset + offset_in_cluster, cur_bytes); + cluster_offset + offset_in_cluster, cur_bytes, true); if (ret < 0) { goto fail; } qemu_co_mutex_unlock(&s->lock); ret = bdrv_co_copy_range_to(src, src_offset, - bs->file, + s->data_file, cluster_offset + offset_in_cluster, cur_bytes, read_flags, write_flags); qemu_co_mutex_lock(&s->lock); @@ -3422,6 +3646,7 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, uint64_t old_length; int64_t new_l1_size; int ret; + QDict *options; if (prealloc != PREALLOC_MODE_OFF && prealloc != PREALLOC_MODE_METADATA && prealloc != PREALLOC_MODE_FALLOC && prealloc != PREALLOC_MODE_FULL) @@ -3446,14 +3671,12 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, } /* cannot proceed if image has bitmaps */ - if (s->nb_bitmaps) { - /* TODO: resize bitmaps in the image */ - error_setg(errp, "Can't resize an image which has bitmaps"); + if (qcow2_truncate_bitmaps_check(bs, errp)) { ret = -ENOTSUP; goto fail; } - old_length = bs->total_sectors * 512; + old_length = bs->total_sectors * BDRV_SECTOR_SIZE; new_l1_size = size_to_l1(s, offset); if (offset < old_length) { @@ -3540,6 +3763,17 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, int64_t old_file_size, new_file_size; uint64_t nb_new_data_clusters, nb_new_l2_tables; + /* With a data file, preallocation means just allocating the metadata + * and forwarding the truncate request to the data file */ + if (has_data_file(bs)) { + ret = preallocate_co(bs, old_length, offset); + if (ret < 0) { + error_setg_errno(errp, -ret, "Preallocation failed"); + goto fail; + } + break; + } + old_file_size = bdrv_getlength(bs->file->bs); if (old_file_size < 0) { error_setg_errno(errp, -old_file_size, @@ -3646,6 +3880,18 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, } } + bs->total_sectors = offset / BDRV_SECTOR_SIZE; + + if (has_data_file(bs)) { + if (prealloc == PREALLOC_MODE_METADATA) { + prealloc = PREALLOC_MODE_OFF; + } + ret = bdrv_co_truncate(s->data_file, offset, prealloc, errp); + if (ret < 0) { + goto fail; + } + } + /* write updated header.size */ offset = cpu_to_be64(offset); ret = bdrv_pwrite_sync(bs->file, offsetof(QCowHeader, size), @@ -3656,6 +3902,14 @@ static int coroutine_fn qcow2_co_truncate(BlockDriverState *bs, int64_t offset, } s->l1_vm_state_index = new_l1_size; + + /* Update cache sizes */ + options = qdict_clone_shallow(bs->options); + ret = qcow2_update_options(bs, options, s->flags, errp); + qobject_unref(options); + if (ret < 0) { + goto fail; + } ret = 0; fail: qemu_co_mutex_unlock(&s->lock); @@ -3665,14 +3919,15 @@ fail: /* * qcow2_compress() * - * @dest - destination buffer, at least of @size-1 bytes - * @src - source buffer, @size bytes + * @dest - destination buffer, @dest_size bytes + * @src - source buffer, @src_size bytes * * Returns: compressed size on success - * -1 if compression is inefficient + * -1 destination buffer is not enough to store compressed data * -2 on any other error */ -static ssize_t qcow2_compress(void *dest, const void *src, size_t size) +static ssize_t qcow2_compress(void *dest, size_t dest_size, + const void *src, size_t src_size) { ssize_t ret; z_stream strm; @@ -3681,20 +3936,20 @@ static ssize_t qcow2_compress(void *dest, const void *src, size_t size) memset(&strm, 0, sizeof(strm)); ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -12, 9, Z_DEFAULT_STRATEGY); - if (ret != 0) { + if (ret != Z_OK) { return -2; } /* strm.next_in is not const in old zlib versions, such as those used on * OpenBSD/NetBSD, so cast the const away */ - strm.avail_in = size; + strm.avail_in = src_size; strm.next_in = (void *) src; - strm.avail_out = size - 1; + strm.avail_out = dest_size; strm.next_out = dest; ret = deflate(&strm, Z_FINISH); if (ret == Z_STREAM_END) { - ret = size - 1 - strm.avail_out; + ret = dest_size - strm.avail_out; } else { ret = (ret == Z_OK ? -1 : -2); } @@ -3704,20 +3959,68 @@ static ssize_t qcow2_compress(void *dest, const void *src, size_t size) return ret; } +/* + * qcow2_decompress() + * + * Decompress some data (not more than @src_size bytes) to produce exactly + * @dest_size bytes. + * + * @dest - destination buffer, @dest_size bytes + * @src - source buffer, @src_size bytes + * + * Returns: 0 on success + * -1 on fail + */ +static ssize_t qcow2_decompress(void *dest, size_t dest_size, + const void *src, size_t src_size) +{ + int ret = 0; + z_stream strm; + + memset(&strm, 0, sizeof(strm)); + strm.avail_in = src_size; + strm.next_in = (void *) src; + strm.avail_out = dest_size; + strm.next_out = dest; + + ret = inflateInit2(&strm, -12); + if (ret != Z_OK) { + return -1; + } + + ret = inflate(&strm, Z_FINISH); + if ((ret != Z_STREAM_END && ret != Z_BUF_ERROR) || strm.avail_out != 0) { + /* We approve Z_BUF_ERROR because we need @dest buffer to be filled, but + * @src buffer may be processed partly (because in qcow2 we know size of + * compressed data with precision of one sector) */ + ret = -1; + } + + inflateEnd(&strm); + + return ret; +} + #define MAX_COMPRESS_THREADS 4 +typedef ssize_t (*Qcow2CompressFunc)(void *dest, size_t dest_size, + const void *src, size_t src_size); typedef struct Qcow2CompressData { void *dest; + size_t dest_size; const void *src; - size_t size; + size_t src_size; ssize_t ret; + + Qcow2CompressFunc func; } Qcow2CompressData; static int qcow2_compress_pool_func(void *opaque) { Qcow2CompressData *data = opaque; - data->ret = qcow2_compress(data->dest, data->src, data->size); + data->ret = data->func(data->dest, data->dest_size, + data->src, data->src_size); return 0; } @@ -3727,17 +4030,19 @@ static void qcow2_compress_complete(void *opaque, int ret) qemu_coroutine_enter(opaque); } -/* See qcow2_compress definition for parameters description */ -static ssize_t qcow2_co_compress(BlockDriverState *bs, - void *dest, const void *src, size_t size) +static ssize_t coroutine_fn +qcow2_co_do_compress(BlockDriverState *bs, void *dest, size_t dest_size, + const void *src, size_t src_size, Qcow2CompressFunc func) { BDRVQcow2State *s = bs->opaque; BlockAIOCB *acb; ThreadPool *pool = aio_get_thread_pool(bdrv_get_aio_context(bs)); Qcow2CompressData arg = { .dest = dest, + .dest_size = dest_size, .src = src, - .size = size, + .src_size = src_size, + .func = func, }; while (s->nb_compress_threads >= MAX_COMPRESS_THREADS) { @@ -3760,6 +4065,22 @@ static ssize_t qcow2_co_compress(BlockDriverState *bs, return arg.ret; } +static ssize_t coroutine_fn +qcow2_co_compress(BlockDriverState *bs, void *dest, size_t dest_size, + const void *src, size_t src_size) +{ + return qcow2_co_do_compress(bs, dest, dest_size, src, src_size, + qcow2_compress); +} + +static ssize_t coroutine_fn +qcow2_co_decompress(BlockDriverState *bs, void *dest, size_t dest_size, + const void *src, size_t src_size) +{ + return qcow2_co_do_compress(bs, dest, dest_size, src, src_size, + qcow2_decompress); +} + /* XXX: put compressed sectors first, then all the cluster aligned tables to avoid losing bytes in alignment */ static coroutine_fn int @@ -3768,21 +4089,23 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, { BDRVQcow2State *s = bs->opaque; QEMUIOVector hd_qiov; - struct iovec iov; int ret; size_t out_len; uint8_t *buf, *out_buf; - int64_t cluster_offset; + uint64_t cluster_offset; + + if (has_data_file(bs)) { + return -ENOTSUP; + } if (bytes == 0) { /* align end of file to a sector boundary to ease reading with sector based I/Os */ - cluster_offset = bdrv_getlength(bs->file->bs); - if (cluster_offset < 0) { - return cluster_offset; + int64_t len = bdrv_getlength(bs->file->bs); + if (len < 0) { + return len; } - return bdrv_co_truncate(bs->file, cluster_offset, PREALLOC_MODE_OFF, - NULL); + return bdrv_co_truncate(bs->file, len, PREALLOC_MODE_OFF, NULL); } if (offset_into_cluster(s, offset)) { @@ -3804,7 +4127,8 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, out_buf = g_malloc(s->cluster_size); - out_len = qcow2_co_compress(bs, out_buf, buf, s->cluster_size); + out_len = qcow2_co_compress(bs, out_buf, s->cluster_size - 1, + buf, s->cluster_size); if (out_len == -2) { ret = -EINVAL; goto fail; @@ -3818,29 +4142,23 @@ qcow2_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, } qemu_co_mutex_lock(&s->lock); - cluster_offset = - qcow2_alloc_compressed_cluster_offset(bs, offset, out_len); - if (!cluster_offset) { + ret = qcow2_alloc_compressed_cluster_offset(bs, offset, out_len, + &cluster_offset); + if (ret < 0) { qemu_co_mutex_unlock(&s->lock); - ret = -EIO; goto fail; } - cluster_offset &= s->cluster_offset_mask; - ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len); + ret = qcow2_pre_write_overlap_check(bs, 0, cluster_offset, out_len, true); qemu_co_mutex_unlock(&s->lock); if (ret < 0) { goto fail; } - iov = (struct iovec) { - .iov_base = out_buf, - .iov_len = out_len, - }; - qemu_iovec_init_external(&hd_qiov, &iov, 1); + qemu_iovec_init_buf(&hd_qiov, out_buf, out_len); - BLKDBG_EVENT(bs->file, BLKDBG_WRITE_COMPRESSED); - ret = bdrv_co_pwritev(bs->file, cluster_offset, out_len, &hd_qiov, 0); + BLKDBG_EVENT(s->data_file, BLKDBG_WRITE_COMPRESSED); + ret = bdrv_co_pwritev(s->data_file, cluster_offset, out_len, &hd_qiov, 0); if (ret < 0) { goto fail; } @@ -3852,6 +4170,52 @@ fail: return ret; } +static int coroutine_fn +qcow2_co_preadv_compressed(BlockDriverState *bs, + uint64_t file_cluster_offset, + uint64_t offset, + uint64_t bytes, + QEMUIOVector *qiov) +{ + BDRVQcow2State *s = bs->opaque; + int ret = 0, csize, nb_csectors; + uint64_t coffset; + uint8_t *buf, *out_buf; + QEMUIOVector local_qiov; + int offset_in_cluster = offset_into_cluster(s, offset); + + coffset = file_cluster_offset & s->cluster_offset_mask; + nb_csectors = ((file_cluster_offset >> s->csize_shift) & s->csize_mask) + 1; + csize = nb_csectors * 512 - (coffset & 511); + + buf = g_try_malloc(csize); + if (!buf) { + return -ENOMEM; + } + qemu_iovec_init_buf(&local_qiov, buf, csize); + + out_buf = qemu_blockalign(bs, s->cluster_size); + + BLKDBG_EVENT(bs->file, BLKDBG_READ_COMPRESSED); + ret = bdrv_co_preadv(bs->file, coffset, csize, &local_qiov, 0); + if (ret < 0) { + goto fail; + } + + if (qcow2_co_decompress(bs, out_buf, s->cluster_size, buf, csize) < 0) { + ret = -EIO; + goto fail; + } + + qemu_iovec_from_buf(qiov, 0, out_buf + offset_in_cluster, bytes); + +fail: + qemu_vfree(out_buf); + g_free(buf); + + return ret; +} + static int make_completely_empty(BlockDriverState *bs) { BDRVQcow2State *s = bs->opaque; @@ -4056,6 +4420,60 @@ static coroutine_fn int qcow2_co_flush_to_os(BlockDriverState *bs) return ret; } +static ssize_t qcow2_measure_crypto_hdr_init_func(QCryptoBlock *block, + size_t headerlen, void *opaque, Error **errp) +{ + size_t *headerlenp = opaque; + + /* Stash away the payload size */ + *headerlenp = headerlen; + return 0; +} + +static ssize_t qcow2_measure_crypto_hdr_write_func(QCryptoBlock *block, + size_t offset, const uint8_t *buf, size_t buflen, + void *opaque, Error **errp) +{ + /* Discard the bytes, we're not actually writing to an image */ + return buflen; +} + +/* Determine the number of bytes for the LUKS payload */ +static bool qcow2_measure_luks_headerlen(QemuOpts *opts, size_t *len, + Error **errp) +{ + QDict *opts_qdict; + QDict *cryptoopts_qdict; + QCryptoBlockCreateOptions *cryptoopts; + QCryptoBlock *crypto; + + /* Extract "encrypt." options into a qdict */ + opts_qdict = qemu_opts_to_qdict(opts, NULL); + qdict_extract_subqdict(opts_qdict, &cryptoopts_qdict, "encrypt."); + qobject_unref(opts_qdict); + + /* Build QCryptoBlockCreateOptions object from qdict */ + qdict_put_str(cryptoopts_qdict, "format", "luks"); + cryptoopts = block_crypto_create_opts_init(cryptoopts_qdict, errp); + qobject_unref(cryptoopts_qdict); + if (!cryptoopts) { + return false; + } + + /* Fake LUKS creation in order to determine the payload size */ + crypto = qcrypto_block_create(cryptoopts, "encrypt.", + qcow2_measure_crypto_hdr_init_func, + qcow2_measure_crypto_hdr_write_func, + len, errp); + qapi_free_QCryptoBlockCreateOptions(cryptoopts); + if (!crypto) { + return false; + } + + qcrypto_block_free(crypto); + return true; +} + static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, Error **errp) { @@ -4065,11 +4483,13 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, uint64_t virtual_size; /* disk size as seen by guest */ uint64_t refcount_bits; uint64_t l2_tables; + uint64_t luks_payload_size = 0; size_t cluster_size; int version; char *optstr; PreallocMode prealloc; bool has_backing_file; + bool has_luks; /* Parse image creation options */ cluster_size = qcow2_opt_get_cluster_size_del(opts, &local_err); @@ -4099,6 +4519,20 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, has_backing_file = !!optstr; g_free(optstr); + optstr = qemu_opt_get_del(opts, BLOCK_OPT_ENCRYPT_FORMAT); + has_luks = optstr && strcmp(optstr, "luks") == 0; + g_free(optstr); + + if (has_luks) { + size_t headerlen; + + if (!qcow2_measure_luks_headerlen(opts, &headerlen, &local_err)) { + goto err; + } + + luks_payload_size = ROUND_UP(headerlen, cluster_size); + } + virtual_size = qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0); virtual_size = ROUND_UP(virtual_size, cluster_size); @@ -4169,7 +4603,7 @@ static BlockMeasureInfo *qcow2_measure(QemuOpts *opts, BlockDriverState *in_bs, info = g_new(BlockMeasureInfo, 1); info->fully_allocated = qcow2_calc_prealloc_size(virtual_size, cluster_size, - ctz32(refcount_bits)); + ctz32(refcount_bits)) + luks_payload_size; /* Remove data clusters that are not required. This overestimates the * required size because metadata needed for the fully allocated file is @@ -4192,20 +4626,26 @@ static int qcow2_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } -static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) +static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs, + Error **errp) { BDRVQcow2State *s = bs->opaque; ImageInfoSpecific *spec_info; QCryptoBlockInfo *encrypt_info = NULL; + Error *local_err = NULL; if (s->crypto != NULL) { - encrypt_info = qcrypto_block_get_info(s->crypto, &error_abort); + encrypt_info = qcrypto_block_get_info(s->crypto, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return NULL; + } } spec_info = g_new(ImageInfoSpecific, 1); *spec_info = (ImageInfoSpecific){ .type = IMAGE_INFO_SPECIFIC_KIND_QCOW2, - .u.qcow2.data = g_new(ImageInfoSpecificQCow2, 1), + .u.qcow2.data = g_new0(ImageInfoSpecificQCow2, 1), }; if (s->qcow_version == 2) { *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ @@ -4213,6 +4653,13 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) .refcount_bits = s->refcount_bits, }; } else if (s->qcow_version == 3) { + Qcow2BitmapInfoList *bitmaps; + bitmaps = qcow2_get_bitmap_info_list(bs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + qapi_free_ImageInfoSpecific(spec_info); + return NULL; + } *spec_info->u.qcow2.data = (ImageInfoSpecificQCow2){ .compat = g_strdup("1.1"), .lazy_refcounts = s->compatible_features & @@ -4222,6 +4669,12 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs) QCOW2_INCOMPAT_CORRUPT, .has_corrupt = true, .refcount_bits = s->refcount_bits, + .has_bitmaps = !!bitmaps, + .bitmaps = bitmaps, + .has_data_file = !!s->image_data_file, + .data_file = g_strdup(s->image_data_file), + .has_data_file_raw = has_data_file(bs), + .data_file_raw = data_file_is_raw(bs), }; } else { /* if this assertion fails, this probably means a new version was @@ -4298,6 +4751,11 @@ static int qcow2_downgrade(BlockDriverState *bs, int target_version, return -ENOTSUP; } + if (has_data_file(bs)) { + error_setg(errp, "Cannot downgrade an image with a data file"); + return -ENOTSUP; + } + /* clear incompatible features */ if (s->incompatible_features & QCOW2_INCOMPAT_DIRTY) { ret = qcow2_mark_clean(bs); @@ -4419,8 +4877,9 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, 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; + const char *backing_file = NULL, *backing_format = NULL, *data_file = NULL; bool lazy_refcounts = s->use_lazy_refcounts; + bool data_file_raw = data_file_is_raw(bs); const char *compat = NULL; uint64_t cluster_size = s->cluster_size; bool encrypt; @@ -4501,6 +4960,21 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, "may not exceed 64 bits"); return -EINVAL; } + } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE)) { + data_file = qemu_opt_get(opts, BLOCK_OPT_DATA_FILE); + if (data_file && !has_data_file(bs)) { + error_setg(errp, "data-file can only be set for images that " + "use an external data file"); + return -EINVAL; + } + } else if (!strcmp(desc->name, BLOCK_OPT_DATA_FILE_RAW)) { + data_file_raw = qemu_opt_get_bool(opts, BLOCK_OPT_DATA_FILE_RAW, + data_file_raw); + if (data_file_raw && !data_file_is_raw(bs)) { + error_setg(errp, "data-file-raw cannot be set on existing " + "images"); + return -EINVAL; + } } else { /* if this point is reached, this probably means a new option was * added without having it covered here */ @@ -4547,6 +5021,24 @@ static int qcow2_amend_options(BlockDriverState *bs, QemuOpts *opts, } } + /* data-file-raw blocks backing files, so clear it first if requested */ + if (data_file_raw) { + s->autoclear_features |= QCOW2_AUTOCLEAR_DATA_FILE_RAW; + } else { + s->autoclear_features &= ~QCOW2_AUTOCLEAR_DATA_FILE_RAW; + } + + if (data_file) { + g_free(s->image_data_file); + s->image_data_file = *data_file ? g_strdup(data_file) : NULL; + } + + ret = qcow2_update_header(bs); + if (ret < 0) { + error_setg_errno(errp, -ret, "Failed to update the image header"); + return ret; + } + if (backing_file || backing_format) { ret = qcow2_change_backing_file(bs, backing_file ?: s->image_backing_file, @@ -4659,7 +5151,7 @@ void qcow2_signal_corruption(BlockDriverState *bs, bool fatal, int64_t offset, *node_name != '\0', node_name, message, offset >= 0, offset, size >= 0, size, - fatal, &error_abort); + fatal); g_free(message); if (fatal) { @@ -4694,6 +5186,16 @@ static QemuOptsList qcow2_create_opts = { .type = QEMU_OPT_STRING, .help = "Image format of the base image" }, + { + .name = BLOCK_OPT_DATA_FILE, + .type = QEMU_OPT_STRING, + .help = "File name of an external data file" + }, + { + .name = BLOCK_OPT_DATA_FILE_RAW, + .type = QEMU_OPT_BOOL, + .help = "The external data file must stay valid as a raw image" + }, { .name = BLOCK_OPT_ENCRYPT, .type = QEMU_OPT_BOOL, @@ -4741,6 +5243,12 @@ static QemuOptsList qcow2_create_opts = { } }; +static const char *const qcow2_strong_runtime_opts[] = { + "encrypt." BLOCK_CRYPTO_OPT_QCOW_KEY_SECRET, + + NULL +}; + BlockDriver bdrv_qcow2 = { .format_name = "qcow2", .instance_size = sizeof(BDRVQcow2State), @@ -4789,6 +5297,8 @@ BlockDriver bdrv_qcow2 = { .bdrv_inactivate = qcow2_inactivate, .create_opts = &qcow2_create_opts, + .strong_runtime_opts = qcow2_strong_runtime_opts, + .mutable_opts = mutable_opts, .bdrv_co_check = qcow2_co_check, .bdrv_amend_options = qcow2_amend_options, diff --git a/block/qcow2.h b/block/qcow2.h index 81b844e936..fdee297f33 100644 --- a/block/qcow2.h +++ b/block/qcow2.h @@ -27,6 +27,7 @@ #include "crypto/block.h" #include "qemu/coroutine.h" +#include "qemu/units.h" //#define DEBUG_ALLOC //#define DEBUG_ALLOC2 @@ -41,13 +42,19 @@ #define QCOW_MAX_CRYPT_CLUSTERS 32 #define QCOW_MAX_SNAPSHOTS 65536 +/* Field widths in qcow2 mean normal cluster offsets cannot reach + * 64PB; depending on cluster size, compressed clusters can have a + * smaller limit (64PB for up to 16k clusters, then ramps down to + * 512TB for 2M clusters). */ +#define QCOW_MAX_CLUSTER_OFFSET ((1ULL << 56) - 1) + /* 8 MB refcount table is enough for 2 PB images at 64k cluster size * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ -#define QCOW_MAX_REFTABLE_SIZE 0x800000 +#define QCOW_MAX_REFTABLE_SIZE (8 * MiB) /* 32 MB L1 table is enough for 2 PB images at 64k cluster size * (128 GB for 512 byte clusters, 2 EB for 2 MB clusters) */ -#define QCOW_MAX_L1_SIZE 0x2000000 +#define QCOW_MAX_L1_SIZE (32 * MiB) /* Allow for an average of 1k per snapshot table entry, should be plenty of * space for snapshot names and IDs */ @@ -73,13 +80,18 @@ /* Must be at least 4 to cover all cases of refcount table growth */ #define MIN_REFCOUNT_CACHE_SIZE 4 /* clusters */ -/* Whichever is more */ -#define DEFAULT_L2_CACHE_CLUSTERS 8 /* clusters */ -#define DEFAULT_L2_CACHE_BYTE_SIZE 1048576 /* bytes */ +#ifdef CONFIG_LINUX +#define DEFAULT_L2_CACHE_MAX_SIZE (32 * MiB) +#define DEFAULT_CACHE_CLEAN_INTERVAL 600 /* seconds */ +#else +#define DEFAULT_L2_CACHE_MAX_SIZE (8 * MiB) +/* Cache clean interval is currently available only on Linux, so must be 0 */ +#define DEFAULT_CACHE_CLEAN_INTERVAL 0 +#endif #define DEFAULT_CLUSTER_SIZE 65536 - +#define QCOW2_OPT_DATA_FILE "data-file" #define QCOW2_OPT_LAZY_REFCOUNTS "lazy-refcounts" #define QCOW2_OPT_DISCARD_REQUEST "pass-discard-request" #define QCOW2_OPT_DISCARD_SNAPSHOT "pass-discard-snapshot" @@ -186,13 +198,16 @@ enum { /* Incompatible feature bits */ enum { - QCOW2_INCOMPAT_DIRTY_BITNR = 0, - QCOW2_INCOMPAT_CORRUPT_BITNR = 1, - QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, - QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, + QCOW2_INCOMPAT_DIRTY_BITNR = 0, + QCOW2_INCOMPAT_CORRUPT_BITNR = 1, + QCOW2_INCOMPAT_DATA_FILE_BITNR = 2, + QCOW2_INCOMPAT_DIRTY = 1 << QCOW2_INCOMPAT_DIRTY_BITNR, + QCOW2_INCOMPAT_CORRUPT = 1 << QCOW2_INCOMPAT_CORRUPT_BITNR, + QCOW2_INCOMPAT_DATA_FILE = 1 << QCOW2_INCOMPAT_DATA_FILE_BITNR, - QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY - | QCOW2_INCOMPAT_CORRUPT, + QCOW2_INCOMPAT_MASK = QCOW2_INCOMPAT_DIRTY + | QCOW2_INCOMPAT_CORRUPT + | QCOW2_INCOMPAT_DATA_FILE, }; /* Compatible feature bits */ @@ -205,10 +220,13 @@ enum { /* Autoclear feature bits */ enum { - QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0, - QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR, + QCOW2_AUTOCLEAR_BITMAPS_BITNR = 0, + QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR = 1, + QCOW2_AUTOCLEAR_BITMAPS = 1 << QCOW2_AUTOCLEAR_BITMAPS_BITNR, + QCOW2_AUTOCLEAR_DATA_FILE_RAW = 1 << QCOW2_AUTOCLEAR_DATA_FILE_RAW_BITNR, - QCOW2_AUTOCLEAR_MASK = QCOW2_AUTOCLEAR_BITMAPS, + QCOW2_AUTOCLEAR_MASK = QCOW2_AUTOCLEAR_BITMAPS + | QCOW2_AUTOCLEAR_DATA_FILE_RAW, }; enum qcow2_discard_type { @@ -270,7 +288,7 @@ typedef struct BDRVQcow2State { uint8_t *cluster_cache; uint8_t *cluster_data; uint64_t cluster_cache_offset; - QLIST_HEAD(QCowClusterAlloc, QCowL2Meta) cluster_allocs; + QLIST_HEAD(, QCowL2Meta) cluster_allocs; uint64_t *refcount_table; uint64_t refcount_table_offset; @@ -295,7 +313,6 @@ typedef struct BDRVQcow2State { uint32_t nb_bitmaps; uint64_t bitmap_directory_size; uint64_t bitmap_directory_offset; - bool dirty_bitmaps_loaded; int flags; int qcow_version; @@ -327,9 +344,12 @@ typedef struct BDRVQcow2State { * override) */ char *image_backing_file; char *image_backing_format; + char *image_data_file; CoQueue compress_wait_queue; int nb_compress_threads; + + BdrvChild *data_file; } BDRVQcow2State; typedef struct Qcow2COWRegion { @@ -447,6 +467,20 @@ typedef enum QCow2MetadataOverlap { #define REFT_OFFSET_MASK 0xfffffffffffffe00ULL +#define INV_OFFSET (-1ULL) + +static inline bool has_data_file(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + return (s->data_file != bs->file); +} + +static inline bool data_file_is_raw(BlockDriverState *bs) +{ + BDRVQcow2State *s = bs->opaque; + return !!(s->autoclear_features & QCOW2_AUTOCLEAR_DATA_FILE_RAW); +} + static inline int64_t start_of_cluster(BDRVQcow2State *s, int64_t offset) { return offset & ~(s->cluster_size - 1); @@ -488,7 +522,8 @@ 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 QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry) +static inline QCow2ClusterType qcow2_get_cluster_type(BlockDriverState *bs, + uint64_t l2_entry) { if (l2_entry & QCOW_OFLAG_COMPRESSED) { return QCOW2_CLUSTER_COMPRESSED; @@ -498,7 +533,15 @@ static inline QCow2ClusterType qcow2_get_cluster_type(uint64_t l2_entry) } return QCOW2_CLUSTER_ZERO_PLAIN; } else if (!(l2_entry & L2E_OFFSET_MASK)) { - return QCOW2_CLUSTER_UNALLOCATED; + /* Offset 0 generally means unallocated, but it is ambiguous with + * external data files because 0 is a valid offset there. However, all + * clusters in external data files always have refcount 1, so we can + * rely on QCOW_OFLAG_COPIED to disambiguate. */ + if (has_data_file(bs) && (l2_entry & QCOW_OFLAG_COPIED)) { + return QCOW2_CLUSTER_NORMAL; + } else { + return QCOW2_CLUSTER_UNALLOCATED; + } } else { return QCOW2_CLUSTER_NORMAL; } @@ -589,7 +632,7 @@ void qcow2_process_discards(BlockDriverState *bs, int ret); int qcow2_check_metadata_overlap(BlockDriverState *bs, int ign, int64_t offset, int64_t size); int qcow2_pre_write_overlap_check(BlockDriverState *bs, int ign, int64_t offset, - int64_t size); + int64_t size, bool data_file); int qcow2_inc_refcounts_imrt(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size, @@ -606,7 +649,6 @@ int qcow2_grow_l1_table(BlockDriverState *bs, uint64_t min_size, bool exact_size); int qcow2_shrink_l1_table(BlockDriverState *bs, uint64_t max_size); int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index); -int qcow2_decompress_cluster(BlockDriverState *bs, uint64_t cluster_offset); int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, uint8_t *buf, int nb_sectors, bool enc, Error **errp); @@ -615,9 +657,10 @@ int qcow2_get_cluster_offset(BlockDriverState *bs, uint64_t offset, int qcow2_alloc_cluster_offset(BlockDriverState *bs, uint64_t offset, unsigned int *bytes, uint64_t *host_offset, QCowL2Meta **m); -uint64_t qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, - uint64_t offset, - int compressed_size); +int qcow2_alloc_compressed_cluster_offset(BlockDriverState *bs, + uint64_t offset, + int compressed_size, + uint64_t *host_offset); int qcow2_alloc_cluster_link_l2(BlockDriverState *bs, QCowL2Meta *m); void qcow2_alloc_cluster_abort(BlockDriverState *bs, QCowL2Meta *m); @@ -675,9 +718,12 @@ int qcow2_check_bitmaps_refcounts(BlockDriverState *bs, BdrvCheckResult *res, void **refcount_table, int64_t *refcount_table_size); bool qcow2_load_dirty_bitmaps(BlockDriverState *bs, Error **errp); +Qcow2BitmapInfoList *qcow2_get_bitmap_info_list(BlockDriverState *bs, + Error **errp); int qcow2_reopen_bitmaps_rw_hint(BlockDriverState *bs, bool *header_updated, Error **errp); int qcow2_reopen_bitmaps_rw(BlockDriverState *bs, Error **errp); +int qcow2_truncate_bitmaps_check(BlockDriverState *bs, Error **errp); void qcow2_store_persistent_dirty_bitmaps(BlockDriverState *bs, Error **errp); int qcow2_reopen_bitmaps_ro(BlockDriverState *bs, Error **errp); bool qcow2_can_store_new_dirty_bitmap(BlockDriverState *bs, diff --git a/block/qed-table.c b/block/qed-table.c index 7df5680adb..c497bd4aec 100644 --- a/block/qed-table.c +++ b/block/qed-table.c @@ -21,16 +21,11 @@ /* Called with table_lock held. */ static int qed_read_table(BDRVQEDState *s, uint64_t offset, QEDTable *table) { - QEMUIOVector qiov; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF( + qiov, table->offsets, s->header.cluster_size * s->header.table_size); int noffsets; int i, ret; - struct iovec iov = { - .iov_base = table->offsets, - .iov_len = s->header.cluster_size * s->header.table_size, - }; - qemu_iovec_init_external(&qiov, &iov, 1); - trace_qed_read_table(s, offset, table); qemu_co_mutex_unlock(&s->table_lock); @@ -71,7 +66,6 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, unsigned int sector_mask = BDRV_SECTOR_SIZE / sizeof(uint64_t) - 1; unsigned int start, end, i; QEDTable *new_table; - struct iovec iov; QEMUIOVector qiov; size_t len_bytes; int ret; @@ -85,11 +79,7 @@ static int qed_write_table(BDRVQEDState *s, uint64_t offset, QEDTable *table, len_bytes = (end - start) * sizeof(uint64_t); new_table = qemu_blockalign(s->bs, len_bytes); - iov = (struct iovec) { - .iov_base = new_table->offsets, - .iov_len = len_bytes, - }; - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, new_table->offsets, len_bytes); /* Byteswap table */ for (i = start; i < end; i++) { diff --git a/block/qed.c b/block/qed.c index 689ea9d4d5..89af05d524 100644 --- a/block/qed.c +++ b/block/qed.c @@ -113,18 +113,13 @@ static int coroutine_fn qed_write_header(BDRVQEDState *s) int nsectors = DIV_ROUND_UP(sizeof(QEDHeader), BDRV_SECTOR_SIZE); size_t len = nsectors * BDRV_SECTOR_SIZE; uint8_t *buf; - struct iovec iov; QEMUIOVector qiov; int ret; assert(s->allocating_acb || s->allocating_write_reqs_plugged); buf = qemu_blockalign(s->bs, len); - iov = (struct iovec) { - .iov_base = buf, - .iov_len = len, - }; - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, buf, len); ret = bdrv_co_preadv(s->bs->file, 0, qiov.size, &qiov, 0); if (ret < 0) { @@ -454,11 +449,14 @@ static int coroutine_fn bdrv_qed_do_open(BlockDriverState *bs, QDict *options, } ret = qed_read_string(bs->file, s->header.backing_filename_offset, - s->header.backing_filename_size, bs->backing_file, - sizeof(bs->backing_file)); + s->header.backing_filename_size, + bs->auto_backing_file, + sizeof(bs->auto_backing_file)); if (ret < 0) { return ret; } + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->auto_backing_file); if (s->header.features & QED_F_BACKING_FORMAT_NO_PROBE) { pstrcpy(bs->backing_format, sizeof(bs->backing_format), "raw"); @@ -559,6 +557,7 @@ static int bdrv_qed_open(BlockDriverState *bs, QDict *options, int flags, if (qemu_in_coroutine()) { bdrv_qed_open_entry(&qoc); } else { + assert(qemu_get_current_aio_context() == qemu_get_aio_context()); qemu_coroutine_enter(qemu_coroutine_create(bdrv_qed_open_entry, &qoc)); BDRV_POLL_WHILE(bs, qoc.ret == -EINPROGRESS); } @@ -912,7 +911,6 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, { QEMUIOVector qiov; QEMUIOVector *backing_qiov = NULL; - struct iovec iov; int ret; /* Skip copy entirely if there is no work to do */ @@ -920,11 +918,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, return 0; } - iov = (struct iovec) { - .iov_base = qemu_blockalign(s->bs, len), - .iov_len = len, - }; - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, qemu_blockalign(s->bs, len), len); ret = qed_read_backing_file(s, pos, &qiov, &backing_qiov); @@ -945,7 +939,7 @@ static int coroutine_fn qed_copy_from_backing_file(BDRVQEDState *s, } ret = 0; out: - qemu_vfree(iov.iov_base); + qemu_vfree(qemu_iovec_buf(&qiov)); return ret; } @@ -1446,8 +1440,12 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, BdrvRequestFlags flags) { BDRVQEDState *s = bs->opaque; - QEMUIOVector qiov; - struct iovec iov; + + /* + * Zero writes start without an I/O buffer. If a buffer becomes necessary + * then it will be allocated during request processing. + */ + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, bytes); /* Fall back if the request is not aligned */ if (qed_offset_into_cluster(s, offset) || @@ -1455,13 +1453,6 @@ static int coroutine_fn bdrv_qed_co_pwrite_zeroes(BlockDriverState *bs, return -ENOTSUP; } - /* Zero writes start without an I/O buffer. If a buffer becomes necessary - * then it will be allocated during request processing. - */ - iov.iov_base = NULL; - iov.iov_len = bytes; - - qemu_iovec_init_external(&qiov, &iov, 1); return qed_co_request(bs, offset >> BDRV_SECTOR_BITS, &qiov, bytes >> BDRV_SECTOR_BITS, QED_AIOCB_WRITE | QED_AIOCB_ZERO); @@ -1606,8 +1597,8 @@ static void coroutine_fn bdrv_qed_co_invalidate_cache(BlockDriverState *bs, ret = bdrv_qed_do_open(bs, NULL, bs->open_flags, &local_err); qemu_co_mutex_unlock(&s->table_lock); if (local_err) { - error_propagate(errp, local_err); - error_prepend(errp, "Could not reopen qed layer: "); + error_propagate_prepend(errp, local_err, + "Could not reopen qed layer: "); return; } else if (ret < 0) { error_setg_errno(errp, -ret, "Could not reopen qed layer"); diff --git a/block/quorum.c b/block/quorum.c index 9152da8c58..352f729136 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -199,7 +199,7 @@ static void quorum_report_bad(QuorumOpType type, uint64_t offset, } qapi_event_send_quorum_report_bad(type, !!msg, msg, node_name, start_sector, - end_sector - start_sector, &error_abort); + end_sector - start_sector); } static void quorum_report_failure(QuorumAIOCB *acb) @@ -210,7 +210,7 @@ static void quorum_report_failure(QuorumAIOCB *acb) BDRV_SECTOR_SIZE); qapi_event_send_quorum_failure(reference, start_sector, - end_sector - start_sector, &error_abort); + end_sector - start_sector); } static int quorum_vote_error(QuorumAIOCB *acb); @@ -437,23 +437,7 @@ static bool quorum_iovec_compare(QEMUIOVector *a, QEMUIOVector *b) return true; } -static void GCC_FMT_ATTR(2, 3) quorum_err(QuorumAIOCB *acb, - const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 " ", - acb->offset, acb->bytes); - vfprintf(stderr, fmt, ap); - fprintf(stderr, "\n"); - va_end(ap); - exit(1); -} - -static bool quorum_compare(QuorumAIOCB *acb, - QEMUIOVector *a, - QEMUIOVector *b) +static bool quorum_compare(QuorumAIOCB *acb, QEMUIOVector *a, QEMUIOVector *b) { BDRVQuorumState *s = acb->bs->opaque; ssize_t offset; @@ -462,8 +446,10 @@ static bool quorum_compare(QuorumAIOCB *acb, if (s->is_blkverify) { offset = qemu_iovec_compare(a, b); if (offset != -1) { - quorum_err(acb, "contents mismatch at offset %" PRIu64, - acb->offset + offset); + fprintf(stderr, "quorum: offset=%" PRIu64 " bytes=%" PRIu64 + " contents mismatch at offset %" PRIu64 "\n", + acb->offset, acb->bytes, acb->offset + offset); + exit(1); } return true; } @@ -926,13 +912,12 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, s->read_pattern = ret; if (s->read_pattern == QUORUM_READ_PATTERN_QUORUM) { - /* is the driver in blkverify mode */ - if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false) && - s->num_children == 2 && s->threshold == 2) { - s->is_blkverify = true; - } else if (qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false)) { - fprintf(stderr, "blkverify mode is set by setting blkverify=on " - "and using two files with vote_threshold=2\n"); + s->is_blkverify = qemu_opt_get_bool(opts, QUORUM_OPT_BLKVERIFY, false); + if (s->is_blkverify && (s->num_children != 2 || s->threshold != 2)) { + error_setg(&local_err, "blkverify=on can only be set if there are " + "exactly two files and vote-threshold is 2"); + ret = -EINVAL; + goto exit; } s->rewrite_corrupted = qemu_opt_get_bool(opts, QUORUM_OPT_REWRITE, @@ -1007,6 +992,11 @@ static void quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, char indexstr[32]; int ret; + if (s->is_blkverify) { + error_setg(errp, "Cannot add a child to a quorum in blkverify mode"); + return; + } + assert(s->num_children <= INT_MAX / sizeof(BdrvChild *)); if (s->num_children == INT_MAX / sizeof(BdrvChild *) || s->next_child_index == UINT_MAX) { @@ -1061,6 +1051,9 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, return; } + /* We know now that num_children > threshold, so blkverify must be false */ + assert(!s->is_blkverify); + bdrv_drained_begin(bs); /* We can safely remove this child now */ @@ -1072,36 +1065,64 @@ static void quorum_del_child(BlockDriverState *bs, BdrvChild *child, bdrv_drained_end(bs); } -static void quorum_refresh_filename(BlockDriverState *bs, QDict *options) +static void quorum_gather_child_options(BlockDriverState *bs, QDict *target, + bool backing_overridden) { BDRVQuorumState *s = bs->opaque; - QDict *opts; - QList *children; + QList *children_list; int i; - for (i = 0; i < s->num_children; i++) { - bdrv_refresh_filename(s->children[i]->bs); - if (!s->children[i]->bs->full_open_options) { - return; - } - } + /* + * The generic implementation for gathering child options in + * bdrv_refresh_filename() would use the names of the children + * as specified for bdrv_open_child() or bdrv_attach_child(), + * which is "children.%u" with %u being a value + * (s->next_child_index) that is incremented each time a new child + * is added (and never decremented). Since children can be + * deleted at runtime, there may be gaps in that enumeration. + * When creating a new quorum BDS and specifying the children for + * it through runtime options, the enumeration used there may not + * have any gaps, though. + * + * Therefore, we have to create a new gap-less enumeration here + * (which we can achieve by simply putting all of the children's + * full_open_options into a QList). + * + * XXX: Note that there are issues with the current child option + * structure quorum uses (such as the fact that children do + * not really have unique permanent names). Therefore, this + * is going to have to change in the future and ideally we + * want quorum to be covered by the generic implementation. + */ + + children_list = qlist_new(); + qdict_put(target, "children", children_list); - children = qlist_new(); for (i = 0; i < s->num_children; i++) { - qlist_append(children, + qlist_append(children_list, qobject_ref(s->children[i]->bs->full_open_options)); } - - opts = qdict_new(); - qdict_put_str(opts, "driver", "quorum"); - qdict_put_int(opts, QUORUM_OPT_VOTE_THRESHOLD, s->threshold); - qdict_put_bool(opts, QUORUM_OPT_BLKVERIFY, s->is_blkverify); - qdict_put_bool(opts, QUORUM_OPT_REWRITE, s->rewrite_corrupted); - qdict_put(opts, "children", children); - - bs->full_open_options = opts; } +static char *quorum_dirname(BlockDriverState *bs, Error **errp) +{ + /* In general, there are multiple BDSs with different dirnames below this + * one; so there is no unique dirname we could return (unless all are equal + * by chance, or there is only one). Therefore, to be consistent, just + * always return NULL. */ + error_setg(errp, "Cannot generate a base directory for quorum nodes"); + return NULL; +} + +static const char *const quorum_strong_runtime_opts[] = { + QUORUM_OPT_VOTE_THRESHOLD, + QUORUM_OPT_BLKVERIFY, + QUORUM_OPT_REWRITE, + QUORUM_OPT_READ_PATTERN, + + NULL +}; + static BlockDriver bdrv_quorum = { .format_name = "quorum", @@ -1109,7 +1130,8 @@ static BlockDriver bdrv_quorum = { .bdrv_open = quorum_open, .bdrv_close = quorum_close, - .bdrv_refresh_filename = quorum_refresh_filename, + .bdrv_gather_child_options = quorum_gather_child_options, + .bdrv_dirname = quorum_dirname, .bdrv_co_flush_to_disk = quorum_co_flush, @@ -1125,6 +1147,8 @@ static BlockDriver bdrv_quorum = { .is_filter = true, .bdrv_recurse_is_first_non_filter = quorum_recurse_is_first_non_filter, + + .strong_runtime_opts = quorum_strong_runtime_opts, }; static void bdrv_quorum_init(void) diff --git a/block/raw-format.c b/block/raw-format.c index 2fd69cdb08..385cdc2490 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -37,6 +37,8 @@ typedef struct BDRVRawState { bool has_size; } BDRVRawState; +static const char *const mutable_opts[] = { "offset", "size", NULL }; + static QemuOptsList raw_runtime_opts = { .name = "raw", .head = QTAILQ_HEAD_INITIALIZER(raw_runtime_opts.head), @@ -432,10 +434,11 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, bs->supported_write_flags = BDRV_REQ_WRITE_UNCHANGED | (BDRV_REQ_FUA & bs->file->bs->supported_write_flags); bs->supported_zero_flags = BDRV_REQ_WRITE_UNCHANGED | - ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP) & + ((BDRV_REQ_FUA | BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK) & bs->file->bs->supported_zero_flags); if (bs->probed && !bdrv_is_read_only(bs)) { + bdrv_refresh_filename(bs->file->bs); fprintf(stderr, "WARNING: Image format was not specified for '%s' and probing " "guessed raw.\n" @@ -459,10 +462,6 @@ static int raw_open(BlockDriverState *bs, QDict *options, int flags, return 0; } -static void raw_close(BlockDriverState *bs) -{ -} - static int raw_probe(const uint8_t *buf, int buf_size, const char *filename) { /* smallest possible positive score so that raw is used if and only if no @@ -535,6 +534,13 @@ static int coroutine_fn raw_co_copy_range_to(BlockDriverState *bs, read_flags, write_flags); } +static const char *const raw_strong_runtime_opts[] = { + "offset", + "size", + + NULL +}; + BlockDriver bdrv_raw = { .format_name = "raw", .instance_size = sizeof(BDRVRawState), @@ -543,7 +549,6 @@ BlockDriver bdrv_raw = { .bdrv_reopen_commit = &raw_reopen_commit, .bdrv_reopen_abort = &raw_reopen_abort, .bdrv_open = &raw_open, - .bdrv_close = &raw_close, .bdrv_child_perm = bdrv_filter_default_perms, .bdrv_co_create_opts = &raw_co_create_opts, .bdrv_co_preadv = &raw_co_preadv, @@ -565,7 +570,9 @@ BlockDriver bdrv_raw = { .bdrv_lock_medium = &raw_lock_medium, .bdrv_co_ioctl = &raw_co_ioctl, .create_opts = &raw_create_opts, - .bdrv_has_zero_init = &raw_has_zero_init + .bdrv_has_zero_init = &raw_has_zero_init, + .strong_runtime_opts = raw_strong_runtime_opts, + .mutable_opts = mutable_opts, }; static void bdrv_raw_init(void) diff --git a/block/rbd.c b/block/rbd.c index ca8e5bbace..0c549c9935 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -655,12 +655,61 @@ failed_opts: return r; } +static int qemu_rbd_convert_options(QDict *options, BlockdevOptionsRbd **opts, + Error **errp) +{ + Visitor *v; + Error *local_err = NULL; + + /* Convert the remaining options into a QAPI object */ + v = qobject_input_visitor_new_flat_confused(options, errp); + if (!v) { + return -EINVAL; + } + + visit_type_BlockdevOptionsRbd(v, NULL, opts, &local_err); + visit_free(v); + + if (local_err) { + error_propagate(errp, local_err); + return -EINVAL; + } + + return 0; +} + +static int qemu_rbd_attempt_legacy_options(QDict *options, + BlockdevOptionsRbd **opts, + char **keypairs) +{ + char *filename; + int r; + + filename = g_strdup(qdict_get_try_str(options, "filename")); + if (!filename) { + return -EINVAL; + } + qdict_del(options, "filename"); + + qemu_rbd_parse_filename(filename, options, NULL); + + /* keypairs freed by caller */ + *keypairs = g_strdup(qdict_get_try_str(options, "=keyvalue-pairs")); + if (*keypairs) { + qdict_del(options, "=keyvalue-pairs"); + } + + r = qemu_rbd_convert_options(options, opts, NULL); + + g_free(filename); + return r; +} + static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, Error **errp) { BDRVRBDState *s = bs->opaque; BlockdevOptionsRbd *opts = NULL; - Visitor *v; const QDictEntry *e; Error *local_err = NULL; char *keypairs, *secretid; @@ -676,20 +725,33 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, qdict_del(options, "password-secret"); } - /* Convert the remaining options into a QAPI object */ - v = qobject_input_visitor_new_flat_confused(options, errp); - if (!v) { - r = -EINVAL; - goto out; - } - - visit_type_BlockdevOptionsRbd(v, NULL, &opts, &local_err); - visit_free(v); - + r = qemu_rbd_convert_options(options, &opts, &local_err); if (local_err) { - error_propagate(errp, local_err); - r = -EINVAL; - goto out; + /* If keypairs are present, that means some options are present in + * the modern option format. Don't attempt to parse legacy option + * formats, as we won't support mixed usage. */ + if (keypairs) { + error_propagate(errp, local_err); + goto out; + } + + /* If the initial attempt to convert and process the options failed, + * we may be attempting to open an image file that has the rbd options + * specified in the older format consisting of all key/value pairs + * encoded in the filename. Go ahead and attempt to parse the + * filename, and see if we can pull out the required options. */ + r = qemu_rbd_attempt_legacy_options(options, &opts, &keypairs); + if (r < 0) { + /* Propagate the original error, not the legacy parsing fallback + * error, as the latter was just a best-effort attempt. */ + error_propagate(errp, local_err); + goto out; + } + /* Take care whenever deciding to actually deprecate; once this ability + * is removed, we will not be able to open any images with legacy-styled + * backing image strings. */ + warn_report("RBD options encoded in the filename as keyvalue pairs " + "is deprecated"); } /* Remove the processed options from the QDict (the visitor processes @@ -718,16 +780,10 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, /* If we are using an rbd snapshot, we must be r/o, otherwise * leave as-is */ if (s->snap != NULL) { - if (!bdrv_is_read_only(bs)) { - error_report("Opening rbd snapshots without an explicit " - "read-only=on option is deprecated. Future versions " - "will refuse to open the image instead of " - "automatically marking the image read-only."); - r = bdrv_set_read_only(bs, true, &local_err); - if (r < 0) { - error_propagate(errp, local_err); - goto failed_open; - } + r = bdrv_apply_auto_read_only(bs, "rbd snapshots are read-only", errp); + if (r < 0) { + rbd_close(s->image); + goto failed_open; } } @@ -1172,6 +1228,18 @@ static QemuOptsList qemu_rbd_create_opts = { } }; +static const char *const qemu_rbd_strong_runtime_opts[] = { + "pool", + "image", + "conf", + "snapshot", + "user", + "server.", + "password-secret", + + NULL +}; + static BlockDriver bdrv_rbd = { .format_name = "rbd", .instance_size = sizeof(BDRVRBDState), @@ -1209,6 +1277,8 @@ static BlockDriver bdrv_rbd = { #ifdef LIBRBD_SUPPORTS_INVALIDATE .bdrv_co_invalidate_cache = qemu_rbd_co_invalidate_cache, #endif + + .strong_runtime_opts = qemu_rbd_strong_runtime_opts, }; static void bdrv_rbd_init(void) diff --git a/block/replication.c b/block/replication.c index 6349d6958e..3d4dedddfc 100644 --- a/block/replication.c +++ b/block/replication.c @@ -20,6 +20,7 @@ #include "block/block_backup.h" #include "sysemu/block-backend.h" #include "qapi/error.h" +#include "qapi/qmp/qdict.h" #include "replication.h" typedef enum { @@ -39,8 +40,8 @@ typedef struct BDRVReplicationState { char *top_id; ReplicationState *rs; Error *blocker; - int orig_hidden_flags; - int orig_secondary_flags; + bool orig_hidden_read_only; + bool orig_secondary_read_only; int error; } BDRVReplicationState; @@ -218,9 +219,6 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, QEMUIOVector *qiov) { BDRVReplicationState *s = bs->opaque; - BdrvChild *child = s->secondary_disk; - BlockJob *job = NULL; - CowRequest req; int ret; if (s->mode == REPLICATION_MODE_PRIMARY) { @@ -233,28 +231,9 @@ static coroutine_fn int replication_co_readv(BlockDriverState *bs, return ret; } - if (child && child->bs) { - job = child->bs->job; - } - - if (job) { - uint64_t remaining_bytes = remaining_sectors * BDRV_SECTOR_SIZE; - - backup_wait_for_overlapping_requests(child->bs->job, - sector_num * BDRV_SECTOR_SIZE, - remaining_bytes); - backup_cow_request_begin(&req, child->bs->job, - sector_num * BDRV_SECTOR_SIZE, - remaining_bytes); - ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, - remaining_bytes, qiov, 0); - backup_cow_request_end(&req); - goto out; - } - ret = bdrv_co_preadv(bs->file, sector_num * BDRV_SECTOR_SIZE, remaining_sectors * BDRV_SECTOR_SIZE, qiov, 0); -out: + return replication_return_value(s, ret); } @@ -371,49 +350,42 @@ static void secondary_do_checkpoint(BDRVReplicationState *s, Error **errp) } } +/* This function is supposed to be called twice: + * first with writable = true, then with writable = false. + * The first call puts s->hidden_disk and s->secondary_disk in + * r/w mode, and the second puts them back in their original state. + */ static void reopen_backing_file(BlockDriverState *bs, bool writable, Error **errp) { BDRVReplicationState *s = bs->opaque; BlockReopenQueue *reopen_queue = NULL; - int orig_hidden_flags, orig_secondary_flags; - int new_hidden_flags, new_secondary_flags; Error *local_err = NULL; if (writable) { - orig_hidden_flags = s->orig_hidden_flags = - bdrv_get_flags(s->hidden_disk->bs); - new_hidden_flags = (orig_hidden_flags | BDRV_O_RDWR) & - ~BDRV_O_INACTIVE; - orig_secondary_flags = s->orig_secondary_flags = - bdrv_get_flags(s->secondary_disk->bs); - new_secondary_flags = (orig_secondary_flags | BDRV_O_RDWR) & - ~BDRV_O_INACTIVE; - } else { - orig_hidden_flags = (s->orig_hidden_flags | BDRV_O_RDWR) & - ~BDRV_O_INACTIVE; - new_hidden_flags = s->orig_hidden_flags; - orig_secondary_flags = (s->orig_secondary_flags | BDRV_O_RDWR) & - ~BDRV_O_INACTIVE; - new_secondary_flags = s->orig_secondary_flags; + s->orig_hidden_read_only = bdrv_is_read_only(s->hidden_disk->bs); + s->orig_secondary_read_only = bdrv_is_read_only(s->secondary_disk->bs); } bdrv_subtree_drained_begin(s->hidden_disk->bs); bdrv_subtree_drained_begin(s->secondary_disk->bs); - if (orig_hidden_flags != new_hidden_flags) { - reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, NULL, - new_hidden_flags); + if (s->orig_hidden_read_only) { + QDict *opts = qdict_new(); + qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); + reopen_queue = bdrv_reopen_queue(reopen_queue, s->hidden_disk->bs, + opts, true); } - if (!(orig_secondary_flags & BDRV_O_RDWR)) { + if (s->orig_secondary_read_only) { + QDict *opts = qdict_new(); + qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !writable); reopen_queue = bdrv_reopen_queue(reopen_queue, s->secondary_disk->bs, - NULL, new_secondary_flags); + opts, true); } if (reopen_queue) { - bdrv_reopen_multiple(bdrv_get_aio_context(bs), - reopen_queue, &local_err); + bdrv_reopen_multiple(reopen_queue, &local_err); error_propagate(errp, local_err); } @@ -643,8 +615,6 @@ static void replication_done(void *opaque, int ret) if (ret == 0) { s->stage = BLOCK_REPLICATION_DONE; - /* refresh top bs's filename */ - bdrv_refresh_filename(bs); s->active_disk = NULL; s->secondary_disk = NULL; s->hidden_disk = NULL; @@ -705,7 +675,14 @@ static void replication_stop(ReplicationState *rs, bool failover, Error **errp) aio_context_release(aio_context); } -BlockDriver bdrv_replication = { +static const char *const replication_strong_runtime_opts[] = { + REPLICATION_MODE, + REPLICATION_TOP_ID, + + NULL +}; + +static BlockDriver bdrv_replication = { .format_name = "replication", .instance_size = sizeof(BDRVReplicationState), @@ -721,6 +698,7 @@ BlockDriver bdrv_replication = { .bdrv_recurse_is_first_non_filter = replication_recurse_is_first_non_filter, .has_variable_length = true, + .strong_runtime_opts = replication_strong_runtime_opts, }; static void bdrv_replication_init(void) diff --git a/block/sheepdog.c b/block/sheepdog.c index b229a664d9..cbdfe9ab6e 100644 --- a/block/sheepdog.c +++ b/block/sheepdog.c @@ -28,6 +28,7 @@ #include "sysemu/block-backend.h" #include "qemu/bitops.h" #include "qemu/cutils.h" +#include "trace.h" #define SD_PROTO_VER 0x01 @@ -299,19 +300,6 @@ static inline size_t count_data_objs(const struct SheepdogInode *inode) (1UL << inode->block_size_shift)); } -#undef DPRINTF -#ifdef DEBUG_SDOG -#define DEBUG_SDOG_PRINT 1 -#else -#define DEBUG_SDOG_PRINT 0 -#endif -#define DPRINTF(fmt, args...) \ - do { \ - if (DEBUG_SDOG_PRINT) { \ - fprintf(stderr, "%s %d: " fmt, __func__, __LINE__, ##args); \ - } \ - } while (0) - typedef struct SheepdogAIOCB SheepdogAIOCB; typedef struct BDRVSheepdogState BDRVSheepdogState; @@ -391,12 +379,12 @@ struct BDRVSheepdogState { uint32_t aioreq_seq_num; /* Every aio request must be linked to either of these queues. */ - QLIST_HEAD(inflight_aio_head, AIOReq) inflight_aio_head; - QLIST_HEAD(failed_aio_head, AIOReq) failed_aio_head; + QLIST_HEAD(, AIOReq) inflight_aio_head; + QLIST_HEAD(, AIOReq) failed_aio_head; CoMutex queue_lock; CoQueue overlapping_queue; - QLIST_HEAD(inflight_aiocb_head, SheepdogAIOCB) inflight_aiocb_head; + QLIST_HEAD(, SheepdogAIOCB) inflight_aiocb_head; }; typedef struct BDRVSheepdogReopenState { @@ -572,7 +560,7 @@ static int connect_to_sdog(BDRVSheepdogState *s, Error **errp) if (s->addr->type == SOCKET_ADDRESS_TYPE_INET && fd >= 0) { int ret = socket_set_nodelay(fd); if (ret < 0) { - error_report("%s", strerror(errno)); + warn_report("can't set TCP_NODELAY: %s", strerror(errno)); } } @@ -750,7 +738,7 @@ static coroutine_fn void reconnect_to_sdog(void *opaque) Error *local_err = NULL; s->fd = get_sheep_fd(s, &local_err); if (s->fd < 0) { - DPRINTF("Wait for connection to be established\n"); + trace_sheepdog_reconnect_to_sdog(); error_report_err(local_err); qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000000ULL); } @@ -847,7 +835,7 @@ static void coroutine_fn aio_read_response(void *opaque) break; case AIOCB_FLUSH_CACHE: if (rsp.result == SD_RES_INVALID_PARMS) { - DPRINTF("disable cache since the server doesn't support it\n"); + trace_sheepdog_aio_read_response(); s->cache_flags = SD_FLAG_CMD_DIRECT; rsp.result = SD_RES_SUCCESS; } @@ -1224,7 +1212,7 @@ static int find_vdi_name(BDRVSheepdogState *s, const char *filename, SheepdogVdiReq hdr; SheepdogVdiRsp *rsp = (SheepdogVdiRsp *)&hdr; unsigned int wlen, rlen = 0; - char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN]; + char buf[SD_MAX_VDI_LEN + SD_MAX_VDI_TAG_LEN] QEMU_NONSTRING; fd = connect_to_sdog(s, errp); if (fd < 0) { @@ -1639,7 +1627,7 @@ static int sd_open(BlockDriverState *bs, QDict *options, int flags, s->discard_supported = true; if (snap_id || tag[0]) { - DPRINTF("%" PRIx32 " snapshot inode was open.\n", vid); + trace_sheepdog_open(vid); s->is_snapshot = true; } @@ -2252,7 +2240,7 @@ static void sd_close(BlockDriverState *bs) unsigned int wlen, rlen = 0; int fd, ret; - DPRINTF("%s\n", s->name); + trace_sheepdog_close(s->name); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2429,7 +2417,7 @@ static int sd_create_branch(BDRVSheepdogState *s) char *buf; bool deleted; - DPRINTF("%" PRIx32 " is snapshot.\n", s->inode.vdi_id); + trace_sheepdog_create_branch_snapshot(s->inode.vdi_id); buf = g_malloc(SD_INODE_SIZE); @@ -2445,7 +2433,7 @@ static int sd_create_branch(BDRVSheepdogState *s) goto out; } - DPRINTF("%" PRIx32 " is created.\n", vid); + trace_sheepdog_create_branch_created(vid); fd = connect_to_sdog(s, &local_err); if (fd < 0) { @@ -2467,7 +2455,7 @@ static int sd_create_branch(BDRVSheepdogState *s) s->is_snapshot = false; ret = 0; - DPRINTF("%" PRIx32 " was newly created.\n", s->inode.vdi_id); + trace_sheepdog_create_branch_new(s->inode.vdi_id); out: g_free(buf); @@ -2561,11 +2549,11 @@ static void coroutine_fn sd_co_rw_vector(SheepdogAIOCB *acb) } if (create) { - DPRINTF("update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld\n", - inode->vdi_id, oid, - vid_to_data_oid(inode->data_vdi_id[idx], idx), idx); + trace_sheepdog_co_rw_vector_update(inode->vdi_id, oid, + vid_to_data_oid(inode->data_vdi_id[idx], idx), + idx); oid = vid_to_data_oid(inode->vdi_id, idx); - DPRINTF("new oid %" PRIx64 "\n", oid); + trace_sheepdog_co_rw_vector_new(oid); } aio_req = alloc_aio_req(s, acb, oid, len, offset, flags, create, @@ -2670,9 +2658,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) SheepdogInode *inode; unsigned int datalen; - DPRINTF("sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " - "is_snapshot %d\n", sn_info->name, sn_info->id_str, - s->name, sn_info->vm_state_size, s->is_snapshot); + trace_sheepdog_snapshot_create_info(sn_info->name, sn_info->id_str, s->name, + sn_info->vm_state_size, s->is_snapshot); if (s->is_snapshot) { error_report("You can't create a snapshot of a snapshot VDI, " @@ -2681,7 +2668,7 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) return -EINVAL; } - DPRINTF("%s %s\n", sn_info->name, sn_info->id_str); + trace_sheepdog_snapshot_create(sn_info->name, sn_info->id_str); s->inode.vm_state_size = sn_info->vm_state_size; s->inode.vm_clock_nsec = sn_info->vm_clock_nsec; @@ -2726,8 +2713,8 @@ static int sd_snapshot_create(BlockDriverState *bs, QEMUSnapshotInfo *sn_info) } memcpy(&s->inode, inode, datalen); - DPRINTF("s->inode: name %s snap_id %x oid %x\n", - s->inode.name, s->inode.snap_id, s->inode.vdi_id); + trace_sheepdog_snapshot_create_inode(s->inode.name, s->inode.snap_id, + s->inode.vdi_id); cleanup: g_free(inode); @@ -3216,6 +3203,15 @@ static QemuOptsList sd_create_opts = { } }; +static const char *const sd_strong_runtime_opts[] = { + "vdi", + "snap-id", + "tag", + "server.", + + NULL +}; + static BlockDriver bdrv_sheepdog = { .format_name = "sheepdog", .protocol_name = "sheepdog", @@ -3251,6 +3247,7 @@ static BlockDriver bdrv_sheepdog = { .bdrv_attach_aio_context = sd_attach_aio_context, .create_opts = &sd_create_opts, + .strong_runtime_opts = sd_strong_runtime_opts, }; static BlockDriver bdrv_sheepdog_tcp = { @@ -3288,6 +3285,7 @@ static BlockDriver bdrv_sheepdog_tcp = { .bdrv_attach_aio_context = sd_attach_aio_context, .create_opts = &sd_create_opts, + .strong_runtime_opts = sd_strong_runtime_opts, }; static BlockDriver bdrv_sheepdog_unix = { @@ -3325,6 +3323,7 @@ static BlockDriver bdrv_sheepdog_unix = { .bdrv_attach_aio_context = sd_attach_aio_context, .create_opts = &sd_create_opts, + .strong_runtime_opts = sd_strong_runtime_opts, }; static void bdrv_sheepdog_init(void) diff --git a/block/snapshot.c b/block/snapshot.c index f9903bc94e..f2f48f926a 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -63,7 +63,7 @@ int bdrv_snapshot_find(BlockDriverState *bs, QEMUSnapshotInfo *sn_info, } for (i = 0; i < nb_sns; i++) { sn = &sn_tab[i]; - if (!strcmp(sn->id_str, name) || !strcmp(sn->name, name)) { + if (!strcmp(sn->name, name)) { *sn_info = *sn; ret = 0; break; @@ -218,7 +218,9 @@ int bdrv_snapshot_goto(BlockDriverState *bs, qobject_unref(file_options); qdict_put_str(options, "file", bdrv_get_node_name(file)); - drv->bdrv_close(bs); + if (drv->bdrv_close) { + drv->bdrv_close(bs); + } bdrv_unref_child(bs, bs->file); bs->file = NULL; @@ -299,26 +301,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs, return ret; } -int bdrv_snapshot_delete_by_id_or_name(BlockDriverState *bs, - const char *id_or_name, - Error **errp) -{ - int ret; - Error *local_err = NULL; - - ret = bdrv_snapshot_delete(bs, id_or_name, NULL, &local_err); - if (ret == -ENOENT || ret == -EINVAL) { - error_free(local_err); - local_err = NULL; - ret = bdrv_snapshot_delete(bs, NULL, id_or_name, &local_err); - } - - if (ret < 0) { - error_propagate(errp, local_err); - } - return ret; -} - int bdrv_snapshot_list(BlockDriverState *bs, QEMUSnapshotInfo **psn_info) { @@ -446,7 +428,8 @@ int bdrv_all_delete_snapshot(const char *name, BlockDriverState **first_bad_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); + ret = bdrv_snapshot_delete(bs, snapshot->id_str, + snapshot->name, err); } aio_context_release(ctx); if (ret < 0) { diff --git a/block/ssh.c b/block/ssh.c index 7fbc27abdf..190ef95300 100644 --- a/block/ssh.c +++ b/block/ssh.c @@ -41,27 +41,17 @@ #include "qapi/qmp/qstring.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" +#include "trace.h" -/* DEBUG_SSH=1 enables the DPRINTF (debugging printf) statements in - * this block driver code. - * +/* * TRACE_LIBSSH2= enables tracing in libssh2 itself. Note * that this requires that libssh2 was specially compiled with the * `./configure --enable-debug' option, so most likely you will have * to compile it yourself. The meaning of is described * here: http://www.libssh2.org/libssh2_trace.html */ -#define DEBUG_SSH 0 #define TRACE_LIBSSH2 0 /* or try: LIBSSH2_TRACE_SFTP */ -#define DPRINTF(fmt, ...) \ - do { \ - if (DEBUG_SSH) { \ - fprintf(stderr, "ssh: %-15s " fmt "\n", \ - __func__, ##__VA_ARGS__); \ - } \ - } while (0) - typedef struct BDRVSSHState { /* Coroutine. */ CoMutex lock; @@ -336,7 +326,7 @@ static int check_host_key_knownhosts(BDRVSSHState *s, switch (r) { case LIBSSH2_KNOWNHOST_CHECK_MATCH: /* OK */ - DPRINTF("host key OK: %s", found->key); + trace_ssh_check_host_key_knownhosts(found->key); break; case LIBSSH2_KNOWNHOST_CHECK_MISMATCH: ret = -EINVAL; @@ -721,8 +711,7 @@ static int connect_to_ssh(BDRVSSHState *s, BlockdevOptionsSsh *opts, } /* Open the remote file. */ - DPRINTF("opening file %s flags=0x%x creat_mode=0%o", - opts->path, ssh_flags, creat_mode); + trace_ssh_connect_to_ssh(opts->path, ssh_flags, creat_mode); s->sftp_handle = libssh2_sftp_open(s->sftp, opts->path, ssh_flags, creat_mode); if (!s->sftp_handle) { @@ -890,7 +879,7 @@ static int coroutine_fn ssh_co_create_opts(const char *filename, QemuOpts *opts, /* Get desired file size. */ ssh_opts->size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), BDRV_SECTOR_SIZE); - DPRINTF("total_size=%" PRIi64, ssh_opts->size); + trace_ssh_co_create_opts(ssh_opts->size); uri_options = qdict_new(); ret = parse_uri(filename, uri_options, errp); @@ -946,7 +935,7 @@ static void restart_coroutine(void *opaque) BDRVSSHState *s = bs->opaque; AioContext *ctx = bdrv_get_aio_context(bs); - DPRINTF("co=%p", restart->co); + trace_ssh_restart_coroutine(restart->co); aio_set_fd_handler(ctx, s->sock, false, NULL, NULL, NULL, NULL); aio_co_wake(restart->co); @@ -974,13 +963,12 @@ static coroutine_fn void co_yield(BDRVSSHState *s, BlockDriverState *bs) wr_handler = restart_coroutine; } - DPRINTF("s->sock=%d rd_handler=%p wr_handler=%p", s->sock, - rd_handler, wr_handler); + trace_ssh_co_yield(s->sock, rd_handler, wr_handler); aio_set_fd_handler(bdrv_get_aio_context(bs), s->sock, false, rd_handler, wr_handler, NULL, &restart); qemu_coroutine_yield(); - DPRINTF("s->sock=%d - back", s->sock); + trace_ssh_co_yield_back(s->sock); } /* SFTP has a function `libssh2_sftp_seek64' which seeks to a position @@ -1003,7 +991,7 @@ static void ssh_seek(BDRVSSHState *s, int64_t offset, int flags) bool force = (flags & SSH_SEEK_FORCE) != 0; if (force || op_read != s->offset_op_read || offset != s->offset) { - DPRINTF("seeking to offset=%" PRIi64, offset); + trace_ssh_seek(offset); libssh2_sftp_seek64(s->sftp_handle, offset); s->offset = offset; s->offset_op_read = op_read; @@ -1019,7 +1007,7 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, char *buf, *end_of_vec; struct iovec *i; - DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + trace_ssh_read(offset, size); ssh_seek(s, offset, SSH_SEEK_READ); @@ -1038,9 +1026,9 @@ static coroutine_fn int ssh_read(BDRVSSHState *s, BlockDriverState *bs, */ for (got = 0; got < size; ) { again: - DPRINTF("sftp_read buf=%p size=%zu", buf, end_of_vec - buf); + trace_ssh_read_buf(buf, end_of_vec - buf); r = libssh2_sftp_read(s->sftp_handle, buf, end_of_vec - buf); - DPRINTF("sftp_read returned %zd", r); + trace_ssh_read_return(r); if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { co_yield(s, bs); @@ -1094,7 +1082,7 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, char *buf, *end_of_vec; struct iovec *i; - DPRINTF("offset=%" PRIi64 " size=%zu", offset, size); + trace_ssh_write(offset, size); ssh_seek(s, offset, SSH_SEEK_WRITE); @@ -1108,9 +1096,9 @@ static int ssh_write(BDRVSSHState *s, BlockDriverState *bs, for (written = 0; written < size; ) { again: - DPRINTF("sftp_write buf=%p size=%zu", buf, end_of_vec - buf); + trace_ssh_write_buf(buf, end_of_vec - buf); r = libssh2_sftp_write(s->sftp_handle, buf, end_of_vec - buf); - DPRINTF("sftp_write returned %zd", r); + trace_ssh_write_return(r); if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { co_yield(s, bs); @@ -1187,7 +1175,7 @@ static coroutine_fn int ssh_flush(BDRVSSHState *s, BlockDriverState *bs) { int r; - DPRINTF("fsync"); + trace_ssh_flush(); again: r = libssh2_sftp_fsync(s->sftp_handle); if (r == LIBSSH2_ERROR_EAGAIN || r == LIBSSH2_ERROR_TIMEOUT) { @@ -1238,7 +1226,7 @@ static int64_t ssh_getlength(BlockDriverState *bs) /* Note we cannot make a libssh2 call here. */ length = (int64_t) s->attrs.filesize; - DPRINTF("length=%" PRIi64, length); + trace_ssh_getlength(length); return length; } @@ -1266,6 +1254,17 @@ static int coroutine_fn ssh_co_truncate(BlockDriverState *bs, int64_t offset, return ssh_grow_file(s, offset, errp); } +static const char *const ssh_strong_runtime_opts[] = { + "host", + "port", + "path", + "user", + "host_key_check", + "server.", + + NULL +}; + static BlockDriver bdrv_ssh = { .format_name = "ssh", .protocol_name = "ssh", @@ -1282,6 +1281,7 @@ static BlockDriver bdrv_ssh = { .bdrv_co_truncate = ssh_co_truncate, .bdrv_co_flush_to_disk = ssh_co_flush, .create_opts = &ssh_create_opts, + .strong_runtime_opts = ssh_strong_runtime_opts, }; static void bdrv_ssh_init(void) diff --git a/block/stream.c b/block/stream.c index 9264b68a1e..bfaebb861a 100644 --- a/block/stream.c +++ b/block/stream.c @@ -34,40 +34,45 @@ typedef struct StreamBlockJob { BlockDriverState *base; BlockdevOnError on_error; char *backing_file_str; - int bs_flags; + bool bs_read_only; + bool chain_frozen; } StreamBlockJob; static int coroutine_fn stream_populate(BlockBackend *blk, int64_t offset, uint64_t bytes, void *buf) { - struct iovec iov = { - .iov_base = buf, - .iov_len = bytes, - }; - QEMUIOVector qiov; + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buf, bytes); assert(bytes < SIZE_MAX); - qemu_iovec_init_external(&qiov, &iov, 1); /* Copy-on-read the unallocated clusters */ return blk_co_preadv(blk, offset, qiov.size, &qiov, BDRV_REQ_COPY_ON_READ); } -typedef struct { - int ret; -} StreamCompleteData; +static void stream_abort(Job *job) +{ + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); -static void stream_complete(Job *job, void *opaque) + if (s->chain_frozen) { + BlockJob *bjob = &s->common; + bdrv_unfreeze_backing_chain(blk_bs(bjob->blk), s->base); + } +} + +static int stream_prepare(Job *job) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockJob *bjob = &s->common; - StreamCompleteData *data = opaque; BlockDriverState *bs = blk_bs(bjob->blk); BlockDriverState *base = s->base; Error *local_err = NULL; + int ret = 0; - if (!job_is_cancelled(job) && bs->backing && data->ret == 0) { + bdrv_unfreeze_backing_chain(bs, base); + s->chain_frozen = false; + + if (bs->backing) { const char *base_id = NULL, *base_fmt = NULL; if (base) { base_id = s->backing_file_str; @@ -75,32 +80,36 @@ static void stream_complete(Job *job, void *opaque) base_fmt = base->drv->format_name; } } - data->ret = bdrv_change_backing_file(bs, base_id, base_fmt); + ret = bdrv_change_backing_file(bs, base_id, base_fmt); bdrv_set_backing_hd(bs, base, &local_err); if (local_err) { error_report_err(local_err); - data->ret = -EPERM; - goto out; + return -EPERM; } } -out: + return ret; +} + +static void stream_clean(Job *job) +{ + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); + BlockJob *bjob = &s->common; + BlockDriverState *bs = blk_bs(bjob->blk); + /* Reopen the image back in read-only mode if necessary */ - if (s->bs_flags != bdrv_get_flags(bs)) { + if (s->bs_read_only) { /* Give up write permissions before making it read-only */ blk_set_perm(bjob->blk, 0, BLK_PERM_ALL, &error_abort); - bdrv_reopen(bs, s->bs_flags, NULL); + bdrv_reopen_set_read_only(bs, true, NULL); } g_free(s->backing_file_str); - job_completed(job, data->ret, NULL); - g_free(data); } -static void coroutine_fn stream_run(void *opaque) +static int coroutine_fn stream_run(Job *job, Error **errp) { - StreamBlockJob *s = opaque; - StreamCompleteData *data; + StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockBackend *blk = s->common.blk; BlockDriverState *bs = blk_bs(blk); BlockDriverState *base = s->base; @@ -203,9 +212,7 @@ static void coroutine_fn stream_run(void *opaque) out: /* Modify backing chain and close BDSes in main loop */ - data = g_malloc(sizeof(*data)); - data->ret = ret; - job_defer_to_main_loop(&s->common.job, stream_complete, data); + return ret; } static const BlockJobDriver stream_job_driver = { @@ -213,7 +220,10 @@ static const BlockJobDriver stream_job_driver = { .instance_size = sizeof(StreamBlockJob), .job_type = JOB_TYPE_STREAM, .free = block_job_free, - .start = stream_run, + .run = stream_run, + .prepare = stream_prepare, + .abort = stream_abort, + .clean = stream_clean, .user_resume = block_job_user_resume, .drain = block_job_drain, }, @@ -221,17 +231,23 @@ static const BlockJobDriver stream_job_driver = { void stream_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, const char *backing_file_str, - int64_t speed, BlockdevOnError on_error, Error **errp) + int creation_flags, int64_t speed, + BlockdevOnError on_error, Error **errp) { StreamBlockJob *s; BlockDriverState *iter; - int orig_bs_flags; + bool bs_read_only; + + if (bdrv_freeze_backing_chain(bs, base, errp) < 0) { + return; + } /* Make sure that the image is opened in read-write mode */ - orig_bs_flags = bdrv_get_flags(bs); - if (!(orig_bs_flags & BDRV_O_RDWR)) { - if (bdrv_reopen(bs, orig_bs_flags | BDRV_O_RDWR, errp) != 0) { - return; + bs_read_only = bdrv_is_read_only(bs); + if (bs_read_only) { + if (bdrv_reopen_set_read_only(bs, false, errp) != 0) { + bs_read_only = false; + goto fail; } } @@ -243,7 +259,7 @@ void stream_start(const char *job_id, BlockDriverState *bs, BLK_PERM_GRAPH_MOD, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE, - speed, JOB_DEFAULT, NULL, NULL, errp); + speed, creation_flags, NULL, NULL, errp); if (!s) { goto fail; } @@ -260,7 +276,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, s->base = base; s->backing_file_str = g_strdup(backing_file_str); - s->bs_flags = orig_bs_flags; + s->bs_read_only = bs_read_only; + s->chain_frozen = true; s->on_error = on_error; trace_stream_start(bs, base, s); @@ -268,7 +285,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, return; fail: - if (orig_bs_flags != bdrv_get_flags(bs)) { - bdrv_reopen(bs, orig_bs_flags, NULL); + if (bs_read_only) { + bdrv_reopen_set_read_only(bs, true, NULL); } + bdrv_unfreeze_backing_chain(bs, base); } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index e297b04e17..a5a2037924 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -36,6 +36,7 @@ static void throttle_group_obj_init(Object *obj); static void throttle_group_obj_complete(UserCreatable *obj, Error **errp); +static void timer_cb(ThrottleGroupMember *tgm, bool is_write); /* The ThrottleGroup structure (with its ThrottleState) is shared * among different ThrottleGroupMembers and it's independent from @@ -221,6 +222,15 @@ static ThrottleGroupMember *next_throttle_token(ThrottleGroupMember *tgm, ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); ThrottleGroupMember *token, *start; + /* If this member has its I/O limits disabled then it means that + * it's being drained. Skip the round-robin search and return tgm + * immediately if it has pending requests. Otherwise we could be + * forcing it to wait for other member's throttled requests. */ + if (tgm_has_pending_reqs(tgm, is_write) && + atomic_read(&tgm->io_limits_disabled)) { + return tgm; + } + start = token = tg->tokens[is_write]; /* get next bs round in round robin style */ @@ -405,6 +415,9 @@ static void coroutine_fn throttle_group_restart_queue_entry(void *opaque) } g_free(data); + + atomic_dec(&tgm->restart_pending); + aio_wait_kick(); } static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write) @@ -415,15 +428,33 @@ static void throttle_group_restart_queue(ThrottleGroupMember *tgm, bool is_write rd->tgm = tgm; rd->is_write = is_write; + /* This function is called when a timer is fired or when + * throttle_group_restart_tgm() is called. Either way, there can + * be no timer pending on this tgm at this point */ + assert(!timer_pending(tgm->throttle_timers.timers[is_write])); + + atomic_inc(&tgm->restart_pending); + co = qemu_coroutine_create(throttle_group_restart_queue_entry, rd); aio_co_enter(tgm->aio_context, co); } void throttle_group_restart_tgm(ThrottleGroupMember *tgm) { + int i; + if (tgm->throttle_state) { - throttle_group_restart_queue(tgm, 0); - throttle_group_restart_queue(tgm, 1); + for (i = 0; i < 2; i++) { + QEMUTimer *t = tgm->throttle_timers.timers[i]; + if (timer_pending(t)) { + /* If there's a pending timer on this tgm, fire it now */ + timer_del(t); + timer_cb(tgm, i); + } else { + /* Else run the next request from the queue manually */ + throttle_group_restart_queue(tgm, i); + } + } } } @@ -512,6 +543,7 @@ void throttle_group_register_tgm(ThrottleGroupMember *tgm, tgm->throttle_state = ts; tgm->aio_context = ctx; + atomic_set(&tgm->restart_pending, 0); qemu_mutex_lock(&tg->lock); /* If the ThrottleGroup is new set this ThrottleGroupMember as the token */ @@ -558,16 +590,14 @@ void throttle_group_unregister_tgm(ThrottleGroupMember *tgm) return; } - assert(tgm->pending_reqs[0] == 0 && tgm->pending_reqs[1] == 0); - assert(qemu_co_queue_empty(&tgm->throttled_reqs[0])); - assert(qemu_co_queue_empty(&tgm->throttled_reqs[1])); + /* Wait for throttle_group_restart_queue_entry() coroutines to finish */ + AIO_WAIT_WHILE(tgm->aio_context, atomic_read(&tgm->restart_pending) > 0); qemu_mutex_lock(&tg->lock); for (i = 0; i < 2; i++) { - if (timer_pending(tgm->throttle_timers.timers[i])) { - tg->any_timer_armed[i] = false; - schedule_next_request(tgm, i); - } + assert(tgm->pending_reqs[i] == 0); + assert(qemu_co_queue_empty(&tgm->throttled_reqs[i])); + assert(!timer_pending(tgm->throttle_timers.timers[i])); if (tg->tokens[i] == tgm) { token = throttle_group_next_tgm(tgm); /* Take care of the case where this is the last tgm in the group */ diff --git a/block/throttle.c b/block/throttle.c index 636c9764aa..f64dcc27b9 100644 --- a/block/throttle.c +++ b/block/throttle.c @@ -227,6 +227,12 @@ static void coroutine_fn throttle_co_drain_end(BlockDriverState *bs) atomic_dec(&tgm->io_limits_disabled); } +static const char *const throttle_strong_runtime_opts[] = { + QEMU_OPT_THROTTLE_GROUP_NAME, + + NULL +}; + static BlockDriver bdrv_throttle = { .format_name = "throttle", .instance_size = sizeof(ThrottleGroupMember), @@ -259,6 +265,7 @@ static BlockDriver bdrv_throttle = { .bdrv_co_drain_end = throttle_co_drain_end, .is_filter = true, + .strong_runtime_opts = throttle_strong_runtime_opts, }; static void bdrv_throttle_init(void) diff --git a/block/trace-events b/block/trace-events index 3e8c47bb24..7335a42540 100644 --- a/block/trace-events +++ b/block/trace-events @@ -1,16 +1,16 @@ # See docs/devel/tracing.txt for syntax documentation. -# block.c +# ../block.c bdrv_open_common(void *bs, const char *filename, int flags, const char *format_name) "bs %p filename \"%s\" flags 0x%x format_name \"%s\"" bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d" -# block/block-backend.c +# block-backend.c blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_co_pwritev(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x" blk_root_attach(void *child, void *blk, void *bs) "child %p blk %p bs %p" blk_root_detach(void *child, void *blk, void *bs) "child %p blk %p bs %p" -# block/io.c +# io.c bdrv_co_preadv(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" bdrv_co_pwritev(void *bs, int64_t offset, int64_t nbytes, unsigned int flags) "bs %p offset %"PRId64" nbytes %"PRId64" flags 0x%x" bdrv_co_pwrite_zeroes(void *bs, int64_t offset, int count, int flags) "bs %p offset %"PRId64" count %d flags 0x%x" @@ -18,15 +18,15 @@ bdrv_co_do_copy_on_readv(void *bs, int64_t offset, unsigned int bytes, int64_t c bdrv_co_copy_range_from(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" bdrv_co_copy_range_to(void *src, uint64_t src_offset, void *dst, uint64_t dst_offset, uint64_t bytes, int read_flags, int write_flags) "src %p offset %"PRIu64" dst %p offset %"PRIu64" bytes %"PRIu64" rw flags 0x%x 0x%x" -# block/stream.c +# stream.c stream_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" stream_start(void *bs, void *base, void *s) "bs %p base %p s %p" -# block/commit.c +# commit.c commit_one_iteration(void *s, int64_t offset, uint64_t bytes, int is_allocated) "s %p offset %" PRId64 " bytes %" PRIu64 " is_allocated %d" commit_start(void *bs, void *base, void *top, void *s) "bs %p base %p top %p s %p" -# block/mirror.c +# mirror.c mirror_start(void *bs, void *s, void *opaque) "bs %p s %p opaque %p" mirror_restart_iter(void *s, int64_t cnt) "s %p dirty count %"PRId64 mirror_before_flush(void *s) "s %p" @@ -37,7 +37,7 @@ mirror_iteration_done(void *s, int64_t offset, uint64_t bytes, int ret) "s %p of mirror_yield(void *s, int64_t cnt, int buf_free_count, int in_flight) "s %p dirty count %"PRId64" free buffers %d in_flight %d" mirror_yield_in_flight(void *s, int64_t offset, int in_flight) "s %p offset %" PRId64 " in_flight %d" -# block/backup.c +# backup.c backup_do_cow_enter(void *job, int64_t start, int64_t offset, uint64_t bytes) "job %p start %" PRId64 " offset %" PRId64 " bytes %" PRIu64 backup_do_cow_return(void *job, int64_t offset, uint64_t bytes, int ret) "job %p offset %" PRId64 " bytes %" PRIu64 " ret %d" backup_do_cow_skip(void *job, int64_t start) "job %p start %"PRId64 @@ -46,7 +46,7 @@ backup_do_cow_read_fail(void *job, int64_t start, int ret) "job %p start %"PRId6 backup_do_cow_write_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" backup_do_cow_copy_range_fail(void *job, int64_t start, int ret) "job %p start %"PRId64" ret %d" -# blockdev.c +# ../blockdev.c qmp_block_job_cancel(void *job) "job %p" qmp_block_job_pause(void *job) "job %p" qmp_block_job_resume(void *job) "job %p" @@ -55,13 +55,12 @@ qmp_block_job_finalize(void *job) "job %p" qmp_block_job_dismiss(void *job) "job %p" qmp_block_stream(void *bs, void *job) "bs %p job %p" -# block/file-win32.c -# block/file-posix.c -file_paio_submit_co(int64_t offset, int count, int type) "offset %"PRId64" count %d type %d" +# file-posix.c +# file-win32.c file_paio_submit(void *acb, void *opaque, int64_t offset, int count, int type) "acb %p opaque %p offset %"PRId64" count %d type %d" file_copy_file_range(void *bs, int src, int64_t src_off, int dst, int64_t dst_off, int64_t bytes, int flags, int64_t ret) "bs %p src_fd %d offset %"PRIu64" dst_fd %d offset %"PRIu64" bytes %"PRIu64" flags %d ret %"PRId64 -# block/qcow2.c +# qcow2.c qcow2_writev_start_req(void *co, int64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" qcow2_writev_done_req(void *co, int ret) "co %p ret %d" qcow2_writev_start_part(void *co) "co %p" @@ -70,7 +69,7 @@ qcow2_writev_data(void *co, uint64_t offset) "co %p offset 0x%" PRIx64 qcow2_pwrite_zeroes_start_req(void *co, int64_t offset, int count) "co %p offset 0x%" PRIx64 " count %d" qcow2_pwrite_zeroes(void *co, int64_t offset, int count) "co %p offset 0x%" PRIx64 " count %d" -# block/qcow2-cluster.c +# qcow2-cluster.c qcow2_alloc_clusters_offset(void *co, uint64_t offset, int bytes) "co %p offset 0x%" PRIx64 " bytes %d" qcow2_handle_copied(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset 0x%" PRIx64 " host_offset 0x%" PRIx64 " bytes 0x%" PRIx64 qcow2_handle_alloc(void *co, uint64_t guest_offset, uint64_t host_offset, uint64_t bytes) "co %p guest_offset 0x%" PRIx64 " host_offset 0x%" PRIx64 " bytes 0x%" PRIx64 @@ -84,7 +83,7 @@ qcow2_l2_allocate_write_l2(void *bs, int l1_index) "bs %p l1_index %d" qcow2_l2_allocate_write_l1(void *bs, int l1_index) "bs %p l1_index %d" qcow2_l2_allocate_done(void *bs, int l1_index, int ret) "bs %p l1_index %d ret %d" -# block/qcow2-cache.c +# qcow2-cache.c qcow2_cache_get(void *co, int c, uint64_t offset, bool read_from_disk) "co %p is_l2_cache %d offset 0x%" PRIx64 " read_from_disk %d" qcow2_cache_get_replace_entry(void *co, int c, int i) "co %p is_l2_cache %d index %d" qcow2_cache_get_read(void *co, int c, int i) "co %p is_l2_cache %d index %d" @@ -92,18 +91,18 @@ qcow2_cache_get_done(void *co, int c, int i) "co %p is_l2_cache %d index %d" qcow2_cache_flush(void *co, int c) "co %p is_l2_cache %d" qcow2_cache_entry_flush(void *co, int c, int i) "co %p is_l2_cache %d index %d" -# block/qed-l2-cache.c +# qed-l2-cache.c qed_alloc_l2_cache_entry(void *l2_cache, void *entry) "l2_cache %p entry %p" qed_unref_l2_cache_entry(void *entry, int ref) "entry %p ref %d" qed_find_l2_cache_entry(void *l2_cache, void *entry, uint64_t offset, int ref) "l2_cache %p entry %p offset %"PRIu64" ref %d" -# block/qed-table.c +# qed-table.c qed_read_table(void *s, uint64_t offset, void *table) "s %p offset %"PRIu64" table %p" qed_read_table_cb(void *s, void *table, int ret) "s %p table %p ret %d" qed_write_table(void *s, uint64_t offset, void *table, unsigned int index, unsigned int n) "s %p offset %"PRIu64" table %p index %u n %u" qed_write_table_cb(void *s, void *table, int flush, int ret) "s %p table %p flush %d ret %d" -# block/qed.c +# qed.c qed_need_check_timer_cb(void *s) "s %p" qed_start_need_check_timer(void *s) "s %p" qed_cancel_need_check_timer(void *s) "s %p" @@ -116,7 +115,7 @@ qed_aio_write_prefill(void *s, void *acb, uint64_t start, size_t len, uint64_t o qed_aio_write_postfill(void *s, void *acb, uint64_t start, size_t len, uint64_t offset) "s %p acb %p start %"PRIu64" len %zu offset %"PRIu64 qed_aio_write_main(void *s, void *acb, int ret, uint64_t offset, size_t len) "s %p acb %p ret %d offset %"PRIu64" len %zu" -# block/vxhs.c +# vxhs.c vxhs_iio_callback(int error) "ctx is NULL: error %d" vxhs_iio_callback_chnfail(int err, int error) "QNIO channel failed, no i/o %d, %d" vxhs_iio_callback_unknwn(int opcode, int err) "unexpected opcode %d, errno %d" @@ -133,7 +132,7 @@ vxhs_parse_uri_hostinfo(char *host, int port) "Host: IP %s, Port %d" vxhs_close(char *vdisk_guid) "Closing vdisk %s" vxhs_get_creds(const char *cacert, const char *client_key, const char *client_cert) "cacert %s, client_key %s, client_cert %s" -# block/nvme.c +# nvme.c nvme_kick(void *s, int queue) "s %p queue %d" nvme_dma_flush_queue_wait(void *s) "s %p" nvme_error(int cmd_specific, int sq_head, int sqid, int cid, int status) "cmd_specific %d sq_head %d sqid %d cid %d status 0x%x" @@ -154,5 +153,58 @@ nvme_cmd_map_qiov(void *s, void *cmd, void *req, void *qiov, int entries) "s %p nvme_cmd_map_qiov_pages(void *s, int i, uint64_t page) "s %p page[%d] 0x%"PRIx64 nvme_cmd_map_qiov_iov(void *s, int i, void *page, int pages) "s %p iov[%d] %p pages %d" -# block/iscsi.c +# iscsi.c iscsi_xcopy(void *src_lun, uint64_t src_off, void *dst_lun, uint64_t dst_off, uint64_t bytes, int ret) "src_lun %p offset %"PRIu64" dst_lun %p offset %"PRIu64" bytes %"PRIu64" ret %d" + +# nbd-client.c +nbd_parse_blockstatus_compliance(const char *err) "ignoring extra data from non-compliant server: %s" +nbd_structured_read_compliance(const char *type) "server sent non-compliant unaligned read %s chunk" +nbd_read_reply_entry_fail(int ret, const char *err) "ret = %d, err: %s" +nbd_co_request_fail(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, uint16_t type, const char *name, int ret, const char *err) "Request failed { .from = %" PRIu64", .len = %" PRIu32 ", .handle = %" PRIu64 ", .flags = 0x%" PRIx16 ", .type = %" PRIu16 " (%s) } ret = %d, err: %s" + +# ssh.c +ssh_restart_coroutine(void *co) "co=%p" +ssh_flush(void) "fsync" +ssh_check_host_key_knownhosts(const char *key) "host key OK: %s" +ssh_connect_to_ssh(char *path, int flags, int mode) "opening file %s flags=0x%x creat_mode=0%o" +ssh_co_yield(int sock, void *rd_handler, void *wr_handler) "s->sock=%d rd_handler=%p wr_handler=%p" +ssh_co_yield_back(int sock) "s->sock=%d - back" +ssh_getlength(int64_t length) "length=%" PRIi64 +ssh_co_create_opts(uint64_t size) "total_size=%" PRIu64 +ssh_read(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" +ssh_read_buf(void *buf, size_t size) "sftp_read buf=%p size=%zu" +ssh_read_return(ssize_t ret) "sftp_read returned %zd" +ssh_write(int64_t offset, size_t size) "offset=%" PRIi64 " size=%zu" +ssh_write_buf(void *buf, size_t size) "sftp_write buf=%p size=%zu" +ssh_write_return(ssize_t ret) "sftp_write returned %zd" +ssh_seek(int64_t offset) "seeking to offset=%" PRIi64 + +# curl.c +curl_timer_cb(long timeout_ms) "timer callback timeout_ms %ld" +curl_sock_cb(int action, int fd) "sock action %d on fd %d" +curl_read_cb(size_t realsize) "just reading %zu bytes" +curl_open(const char *file) "opening %s" +curl_open_size(uint64_t size) "size = %" PRIu64 +curl_setup_preadv(uint64_t bytes, uint64_t start, const char *range) "reading %" PRIu64 " at %" PRIu64 " (%s)" +curl_close(void) "close" + +# file-posix.c +file_xfs_write_zeroes(const char *error) "cannot write zero range (%s)" +file_xfs_discard(const char *error) "cannot punch hole (%s)" +file_FindEjectableOpticalMedia(const char *media) "Matching using %s" +file_setup_cdrom(const char *partition) "Using %s as optical disc" +file_hdev_is_sg(int type, int version) "SG device found: type=%d, version=%d" + +# sheepdog.c +sheepdog_reconnect_to_sdog(void) "Wait for connection to be established" +sheepdog_aio_read_response(void) "disable cache since the server doesn't support it" +sheepdog_open(uint32_t vid) "0x%" PRIx32 " snapshot inode was open" +sheepdog_close(const char *name) "%s" +sheepdog_create_branch_snapshot(uint32_t vdi) "0x%" PRIx32 " is snapshot" +sheepdog_create_branch_created(uint32_t vdi) "0x%" PRIx32 " is created" +sheepdog_create_branch_new(uint32_t vdi) "0x%" PRIx32 " was newly created" +sheepdog_co_rw_vector_update(uint32_t vdi, uint64_t oid, uint64_t data, long idx) "update ino (%" PRIu32 ") %" PRIu64 " %" PRIu64 " %ld" +sheepdog_co_rw_vector_new(uint64_t oid) "new oid 0x%" PRIx64 +sheepdog_snapshot_create_info(const char *sn_name, const char *id, const char *name, int64_t size, int is_snapshot) "sn_info: name %s id_str %s s: name %s vm_state_size %" PRId64 " " "is_snapshot %d" +sheepdog_snapshot_create(const char *sn_name, const char *id) "%s %s" +sheepdog_snapshot_create_inode(const char *name, uint32_t snap, uint32_t vdi) "s->inode: name %s snap_id 0x%" PRIx32 " vdi 0x%" PRIx32 diff --git a/block/vdi.c b/block/vdi.c index 6555cffb88..e1c42ad732 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -85,7 +85,8 @@ #define BLOCK_OPT_STATIC "static" #define SECTOR_SIZE 512 -#define DEFAULT_CLUSTER_SIZE (1 * MiB) +#define DEFAULT_CLUSTER_SIZE 1048576 +/* Note: can't use 1 * MiB, because it's passed to stringify() */ #if defined(CONFIG_VDI_DEBUG) #define VDI_DEBUG 1 @@ -187,55 +188,56 @@ typedef struct { static void vdi_header_to_cpu(VdiHeader *header) { - le32_to_cpus(&header->signature); - le32_to_cpus(&header->version); - le32_to_cpus(&header->header_size); - le32_to_cpus(&header->image_type); - le32_to_cpus(&header->image_flags); - le32_to_cpus(&header->offset_bmap); - le32_to_cpus(&header->offset_data); - le32_to_cpus(&header->cylinders); - le32_to_cpus(&header->heads); - le32_to_cpus(&header->sectors); - le32_to_cpus(&header->sector_size); - le64_to_cpus(&header->disk_size); - le32_to_cpus(&header->block_size); - le32_to_cpus(&header->block_extra); - le32_to_cpus(&header->blocks_in_image); - le32_to_cpus(&header->blocks_allocated); - qemu_uuid_bswap(&header->uuid_image); - qemu_uuid_bswap(&header->uuid_last_snap); - qemu_uuid_bswap(&header->uuid_link); - qemu_uuid_bswap(&header->uuid_parent); + header->signature = le32_to_cpu(header->signature); + header->version = le32_to_cpu(header->version); + header->header_size = le32_to_cpu(header->header_size); + header->image_type = le32_to_cpu(header->image_type); + header->image_flags = le32_to_cpu(header->image_flags); + header->offset_bmap = le32_to_cpu(header->offset_bmap); + header->offset_data = le32_to_cpu(header->offset_data); + header->cylinders = le32_to_cpu(header->cylinders); + header->heads = le32_to_cpu(header->heads); + header->sectors = le32_to_cpu(header->sectors); + header->sector_size = le32_to_cpu(header->sector_size); + header->disk_size = le64_to_cpu(header->disk_size); + header->block_size = le32_to_cpu(header->block_size); + header->block_extra = le32_to_cpu(header->block_extra); + header->blocks_in_image = le32_to_cpu(header->blocks_in_image); + header->blocks_allocated = le32_to_cpu(header->blocks_allocated); + header->uuid_image = qemu_uuid_bswap(header->uuid_image); + header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap); + header->uuid_link = qemu_uuid_bswap(header->uuid_link); + header->uuid_parent = qemu_uuid_bswap(header->uuid_parent); } static void vdi_header_to_le(VdiHeader *header) { - cpu_to_le32s(&header->signature); - cpu_to_le32s(&header->version); - cpu_to_le32s(&header->header_size); - cpu_to_le32s(&header->image_type); - cpu_to_le32s(&header->image_flags); - cpu_to_le32s(&header->offset_bmap); - cpu_to_le32s(&header->offset_data); - cpu_to_le32s(&header->cylinders); - cpu_to_le32s(&header->heads); - cpu_to_le32s(&header->sectors); - cpu_to_le32s(&header->sector_size); - cpu_to_le64s(&header->disk_size); - cpu_to_le32s(&header->block_size); - cpu_to_le32s(&header->block_extra); - cpu_to_le32s(&header->blocks_in_image); - cpu_to_le32s(&header->blocks_allocated); - qemu_uuid_bswap(&header->uuid_image); - qemu_uuid_bswap(&header->uuid_last_snap); - qemu_uuid_bswap(&header->uuid_link); - qemu_uuid_bswap(&header->uuid_parent); + header->signature = cpu_to_le32(header->signature); + header->version = cpu_to_le32(header->version); + header->header_size = cpu_to_le32(header->header_size); + header->image_type = cpu_to_le32(header->image_type); + header->image_flags = cpu_to_le32(header->image_flags); + header->offset_bmap = cpu_to_le32(header->offset_bmap); + header->offset_data = cpu_to_le32(header->offset_data); + header->cylinders = cpu_to_le32(header->cylinders); + header->heads = cpu_to_le32(header->heads); + header->sectors = cpu_to_le32(header->sectors); + header->sector_size = cpu_to_le32(header->sector_size); + header->disk_size = cpu_to_le64(header->disk_size); + header->block_size = cpu_to_le32(header->block_size); + header->block_extra = cpu_to_le32(header->block_extra); + header->blocks_in_image = cpu_to_le32(header->blocks_in_image); + header->blocks_allocated = cpu_to_le32(header->blocks_allocated); + header->uuid_image = qemu_uuid_bswap(header->uuid_image); + header->uuid_last_snap = qemu_uuid_bswap(header->uuid_last_snap); + header->uuid_link = qemu_uuid_bswap(header->uuid_link); + header->uuid_parent = qemu_uuid_bswap(header->uuid_parent); } static void vdi_header_print(VdiHeader *header) { - char uuid[37]; + char uuidstr[37]; + QemuUUID uuid; logout("text %s", header->text); logout("signature 0x%08x\n", header->signature); logout("header size 0x%04x\n", header->header_size); @@ -254,14 +256,18 @@ static void vdi_header_print(VdiHeader *header) logout("block extra 0x%04x\n", header->block_extra); logout("blocks tot. 0x%04x\n", header->blocks_in_image); logout("blocks all. 0x%04x\n", header->blocks_allocated); - qemu_uuid_unparse(&header->uuid_image, uuid); - logout("uuid image %s\n", uuid); - qemu_uuid_unparse(&header->uuid_last_snap, uuid); - logout("uuid snap %s\n", uuid); - qemu_uuid_unparse(&header->uuid_link, uuid); - logout("uuid link %s\n", uuid); - qemu_uuid_unparse(&header->uuid_parent, uuid); - logout("uuid parent %s\n", uuid); + uuid = header->uuid_image; + qemu_uuid_unparse(&uuid, uuidstr); + logout("uuid image %s\n", uuidstr); + uuid = header->uuid_last_snap; + qemu_uuid_unparse(&uuid, uuidstr); + logout("uuid snap %s\n", uuidstr); + uuid = header->uuid_link; + qemu_uuid_unparse(&uuid, uuidstr); + logout("uuid link %s\n", uuidstr); + uuid = header->uuid_parent; + qemu_uuid_unparse(&uuid, uuidstr); + logout("uuid parent %s\n", uuidstr); } static int coroutine_fn vdi_co_check(BlockDriverState *bs, BdrvCheckResult *res, @@ -368,6 +374,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, size_t bmap_size; int ret; Error *local_err = NULL; + QemuUUID uuid_link, uuid_parent; bs->file = bdrv_open_child(NULL, options, "file", bs, &child_file, false, errp); @@ -395,6 +402,9 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } + uuid_link = header.uuid_link; + uuid_parent = header.uuid_parent; + if (header.disk_size % SECTOR_SIZE != 0) { /* 'VBoxManage convertfromraw' can create images with odd disk sizes. We accept them but round the disk size to the next multiple of @@ -432,7 +442,7 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } else if (header.block_size != DEFAULT_CLUSTER_SIZE) { error_setg(errp, "unsupported VDI image (block size %" PRIu32 - " is not %" PRIu64 ")", + " is not %" PRIu32 ")", header.block_size, DEFAULT_CLUSTER_SIZE); ret = -ENOTSUP; goto fail; @@ -444,11 +454,11 @@ static int vdi_open(BlockDriverState *bs, QDict *options, int flags, (uint64_t)header.blocks_in_image * header.block_size); ret = -ENOTSUP; goto fail; - } else if (!qemu_uuid_is_null(&header.uuid_link)) { + } else if (!qemu_uuid_is_null(&uuid_link)) { error_setg(errp, "unsupported VDI image (non-NULL link UUID)"); ret = -ENOTSUP; goto fail; - } else if (!qemu_uuid_is_null(&header.uuid_parent)) { + } else if (!qemu_uuid_is_null(&uuid_parent)) { error_setg(errp, "unsupported VDI image (non-NULL parent UUID)"); ret = -ENOTSUP; goto fail; @@ -733,6 +743,7 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, BlockDriverState *bs_file = NULL; BlockBackend *blk = NULL; uint32_t *bmap = NULL; + QemuUUID uuid; assert(create_options->driver == BLOCKDEV_DRIVER_VDI); vdi_opts = &create_options->u.vdi; @@ -819,8 +830,10 @@ static int coroutine_fn vdi_co_do_create(BlockdevCreateOptions *create_options, if (image_type == VDI_TYPE_STATIC) { header.blocks_allocated = blocks; } - qemu_uuid_generate(&header.uuid_image); - qemu_uuid_generate(&header.uuid_last_snap); + qemu_uuid_generate(&uuid); + header.uuid_image = uuid; + qemu_uuid_generate(&uuid); + header.uuid_last_snap = uuid; /* There is no need to set header.uuid_link or header.uuid_parent here. */ if (VDI_DEBUG) { vdi_header_print(&header); diff --git a/block/vhdx-endian.c b/block/vhdx-endian.c index 41fbdd2b8f..ebfa33cb8a 100644 --- a/block/vhdx-endian.c +++ b/block/vhdx-endian.c @@ -35,18 +35,18 @@ void vhdx_header_le_import(VHDXHeader *h) { assert(h != NULL); - le32_to_cpus(&h->signature); - le32_to_cpus(&h->checksum); - le64_to_cpus(&h->sequence_number); + h->signature = le32_to_cpu(h->signature); + h->checksum = le32_to_cpu(h->checksum); + h->sequence_number = le64_to_cpu(h->sequence_number); leguid_to_cpus(&h->file_write_guid); leguid_to_cpus(&h->data_write_guid); leguid_to_cpus(&h->log_guid); - le16_to_cpus(&h->log_version); - le16_to_cpus(&h->version); - le32_to_cpus(&h->log_length); - le64_to_cpus(&h->log_offset); + h->log_version = le16_to_cpu(h->log_version); + h->version = le16_to_cpu(h->version); + h->log_length = le32_to_cpu(h->log_length); + h->log_offset = le64_to_cpu(h->log_offset); } void vhdx_header_le_export(VHDXHeader *orig_h, VHDXHeader *new_h) @@ -80,68 +80,68 @@ void vhdx_log_desc_le_import(VHDXLogDescriptor *d) { assert(d != NULL); - le32_to_cpus(&d->signature); - le64_to_cpus(&d->file_offset); - le64_to_cpus(&d->sequence_number); + d->signature = le32_to_cpu(d->signature); + d->file_offset = le64_to_cpu(d->file_offset); + d->sequence_number = le64_to_cpu(d->sequence_number); } void vhdx_log_desc_le_export(VHDXLogDescriptor *d) { assert(d != NULL); - cpu_to_le32s(&d->signature); - cpu_to_le32s(&d->trailing_bytes); - cpu_to_le64s(&d->leading_bytes); - cpu_to_le64s(&d->file_offset); - cpu_to_le64s(&d->sequence_number); + d->signature = cpu_to_le32(d->signature); + d->trailing_bytes = cpu_to_le32(d->trailing_bytes); + d->leading_bytes = cpu_to_le64(d->leading_bytes); + d->file_offset = cpu_to_le64(d->file_offset); + d->sequence_number = cpu_to_le64(d->sequence_number); } void vhdx_log_data_le_import(VHDXLogDataSector *d) { assert(d != NULL); - le32_to_cpus(&d->data_signature); - le32_to_cpus(&d->sequence_high); - le32_to_cpus(&d->sequence_low); + d->data_signature = le32_to_cpu(d->data_signature); + d->sequence_high = le32_to_cpu(d->sequence_high); + d->sequence_low = le32_to_cpu(d->sequence_low); } void vhdx_log_data_le_export(VHDXLogDataSector *d) { assert(d != NULL); - cpu_to_le32s(&d->data_signature); - cpu_to_le32s(&d->sequence_high); - cpu_to_le32s(&d->sequence_low); + d->data_signature = cpu_to_le32(d->data_signature); + d->sequence_high = cpu_to_le32(d->sequence_high); + d->sequence_low = cpu_to_le32(d->sequence_low); } void vhdx_log_entry_hdr_le_import(VHDXLogEntryHeader *hdr) { assert(hdr != NULL); - le32_to_cpus(&hdr->signature); - le32_to_cpus(&hdr->checksum); - le32_to_cpus(&hdr->entry_length); - le32_to_cpus(&hdr->tail); - le64_to_cpus(&hdr->sequence_number); - le32_to_cpus(&hdr->descriptor_count); + hdr->signature = le32_to_cpu(hdr->signature); + hdr->checksum = le32_to_cpu(hdr->checksum); + hdr->entry_length = le32_to_cpu(hdr->entry_length); + hdr->tail = le32_to_cpu(hdr->tail); + hdr->sequence_number = le64_to_cpu(hdr->sequence_number); + hdr->descriptor_count = le32_to_cpu(hdr->descriptor_count); leguid_to_cpus(&hdr->log_guid); - le64_to_cpus(&hdr->flushed_file_offset); - le64_to_cpus(&hdr->last_file_offset); + hdr->flushed_file_offset = le64_to_cpu(hdr->flushed_file_offset); + hdr->last_file_offset = le64_to_cpu(hdr->last_file_offset); } void vhdx_log_entry_hdr_le_export(VHDXLogEntryHeader *hdr) { assert(hdr != NULL); - cpu_to_le32s(&hdr->signature); - cpu_to_le32s(&hdr->checksum); - cpu_to_le32s(&hdr->entry_length); - cpu_to_le32s(&hdr->tail); - cpu_to_le64s(&hdr->sequence_number); - cpu_to_le32s(&hdr->descriptor_count); + hdr->signature = cpu_to_le32(hdr->signature); + hdr->checksum = cpu_to_le32(hdr->checksum); + hdr->entry_length = cpu_to_le32(hdr->entry_length); + hdr->tail = cpu_to_le32(hdr->tail); + hdr->sequence_number = cpu_to_le64(hdr->sequence_number); + hdr->descriptor_count = cpu_to_le32(hdr->descriptor_count); cpu_to_leguids(&hdr->log_guid); - cpu_to_le64s(&hdr->flushed_file_offset); - cpu_to_le64s(&hdr->last_file_offset); + hdr->flushed_file_offset = cpu_to_le64(hdr->flushed_file_offset); + hdr->last_file_offset = cpu_to_le64(hdr->last_file_offset); } @@ -150,18 +150,18 @@ void vhdx_region_header_le_import(VHDXRegionTableHeader *hdr) { assert(hdr != NULL); - le32_to_cpus(&hdr->signature); - le32_to_cpus(&hdr->checksum); - le32_to_cpus(&hdr->entry_count); + hdr->signature = le32_to_cpu(hdr->signature); + hdr->checksum = le32_to_cpu(hdr->checksum); + hdr->entry_count = le32_to_cpu(hdr->entry_count); } void vhdx_region_header_le_export(VHDXRegionTableHeader *hdr) { assert(hdr != NULL); - cpu_to_le32s(&hdr->signature); - cpu_to_le32s(&hdr->checksum); - cpu_to_le32s(&hdr->entry_count); + hdr->signature = cpu_to_le32(hdr->signature); + hdr->checksum = cpu_to_le32(hdr->checksum); + hdr->entry_count = cpu_to_le32(hdr->entry_count); } void vhdx_region_entry_le_import(VHDXRegionTableEntry *e) @@ -169,9 +169,9 @@ void vhdx_region_entry_le_import(VHDXRegionTableEntry *e) assert(e != NULL); leguid_to_cpus(&e->guid); - le64_to_cpus(&e->file_offset); - le32_to_cpus(&e->length); - le32_to_cpus(&e->data_bits); + e->file_offset = le64_to_cpu(e->file_offset); + e->length = le32_to_cpu(e->length); + e->data_bits = le32_to_cpu(e->data_bits); } void vhdx_region_entry_le_export(VHDXRegionTableEntry *e) @@ -179,9 +179,9 @@ void vhdx_region_entry_le_export(VHDXRegionTableEntry *e) assert(e != NULL); cpu_to_leguids(&e->guid); - cpu_to_le64s(&e->file_offset); - cpu_to_le32s(&e->length); - cpu_to_le32s(&e->data_bits); + e->file_offset = cpu_to_le64(e->file_offset); + e->length = cpu_to_le32(e->length); + e->data_bits = cpu_to_le32(e->data_bits); } @@ -190,16 +190,16 @@ void vhdx_metadata_header_le_import(VHDXMetadataTableHeader *hdr) { assert(hdr != NULL); - le64_to_cpus(&hdr->signature); - le16_to_cpus(&hdr->entry_count); + hdr->signature = le64_to_cpu(hdr->signature); + hdr->entry_count = le16_to_cpu(hdr->entry_count); } void vhdx_metadata_header_le_export(VHDXMetadataTableHeader *hdr) { assert(hdr != NULL); - cpu_to_le64s(&hdr->signature); - cpu_to_le16s(&hdr->entry_count); + hdr->signature = cpu_to_le64(hdr->signature); + hdr->entry_count = cpu_to_le16(hdr->entry_count); } void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e) @@ -207,16 +207,16 @@ void vhdx_metadata_entry_le_import(VHDXMetadataTableEntry *e) assert(e != NULL); leguid_to_cpus(&e->item_id); - le32_to_cpus(&e->offset); - le32_to_cpus(&e->length); - le32_to_cpus(&e->data_bits); + e->offset = le32_to_cpu(e->offset); + e->length = le32_to_cpu(e->length); + e->data_bits = le32_to_cpu(e->data_bits); } void vhdx_metadata_entry_le_export(VHDXMetadataTableEntry *e) { assert(e != NULL); cpu_to_leguids(&e->item_id); - cpu_to_le32s(&e->offset); - cpu_to_le32s(&e->length); - cpu_to_le32s(&e->data_bits); + e->offset = cpu_to_le32(e->offset); + e->length = cpu_to_le32(e->length); + e->data_bits = cpu_to_le32(e->data_bits); } diff --git a/block/vhdx-log.c b/block/vhdx-log.c index d2f1b98199..3149ff08d8 100644 --- a/block/vhdx-log.c +++ b/block/vhdx-log.c @@ -803,6 +803,7 @@ int vhdx_parse_log(BlockDriverState *bs, BDRVVHDXState *s, bool *flushed, if (logs.valid) { if (bs->read_only) { + bdrv_refresh_filename(bs); ret = -EPERM; error_setg(errp, "VHDX image file '%s' opened read-only, but " @@ -835,11 +836,11 @@ static void vhdx_log_raw_to_le_sector(VHDXLogDescriptor *desc, /* 8 + 4084 + 4 = 4096, 1 log sector */ memcpy(&desc->leading_bytes, data, 8); data += 8; - cpu_to_le64s(&desc->leading_bytes); + desc->leading_bytes = cpu_to_le64(desc->leading_bytes); memcpy(sector->data, data, 4084); data += 4084; memcpy(&desc->trailing_bytes, data, 4); - cpu_to_le32s(&desc->trailing_bytes); + desc->trailing_bytes = cpu_to_le32(desc->trailing_bytes); data += 4; sector->sequence_high = (uint32_t) (seq >> 32); diff --git a/block/vhdx.c b/block/vhdx.c index 0795ca1985..b785aef4b7 100644 --- a/block/vhdx.c +++ b/block/vhdx.c @@ -156,7 +156,7 @@ uint32_t vhdx_update_checksum(uint8_t *buf, size_t size, int crc_offset) memset(buf + crc_offset, 0, sizeof(crc)); crc = crc32c(0xffffffff, buf, size); - cpu_to_le32s(&crc); + crc = cpu_to_le32(crc); memcpy(buf + crc_offset, &crc, sizeof(crc)); return crc; @@ -753,8 +753,8 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) goto exit; } - le32_to_cpus(&s->params.block_size); - le32_to_cpus(&s->params.data_bits); + s->params.block_size = le32_to_cpu(s->params.block_size); + s->params.data_bits = le32_to_cpu(s->params.data_bits); /* We now have the file parameters, so we can tell if this is a @@ -803,9 +803,9 @@ static int vhdx_parse_metadata(BlockDriverState *bs, BDRVVHDXState *s) goto exit; } - le64_to_cpus(&s->virtual_disk_size); - le32_to_cpus(&s->logical_sector_size); - le32_to_cpus(&s->physical_sector_size); + s->virtual_disk_size = le64_to_cpu(s->virtual_disk_size); + s->logical_sector_size = le32_to_cpu(s->logical_sector_size); + s->physical_sector_size = le32_to_cpu(s->physical_sector_size); if (s->params.block_size < VHDX_BLOCK_SIZE_MIN || s->params.block_size > VHDX_BLOCK_SIZE_MAX) { @@ -985,7 +985,7 @@ static int vhdx_open(BlockDriverState *bs, QDict *options, int flags, /* endian convert, and verify populated BAT field file offsets against * region table and log entries */ for (i = 0; i < s->bat_entries; i++) { - le64_to_cpus(&s->bat[i]); + s->bat[i] = le64_to_cpu(s->bat[i]); if (payblocks--) { /* payload bat entries */ if ((s->bat[i] & VHDX_BAT_STATE_BIT_MASK) == @@ -1509,7 +1509,7 @@ static int vhdx_create_new_metadata(BlockBackend *blk, mt_file_params->block_size = cpu_to_le32(block_size); if (type == VHDX_TYPE_FIXED) { mt_file_params->data_bits |= VHDX_PARAMS_LEAVE_BLOCKS_ALLOCED; - cpu_to_le32s(&mt_file_params->data_bits); + mt_file_params->data_bits = cpu_to_le32(mt_file_params->data_bits); } vhdx_guid_generate(&mt_page83->page_83_data); @@ -1656,7 +1656,7 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s, sinfo.file_offset = ROUND_UP(sinfo.file_offset, MiB); vhdx_update_bat_table_entry(blk_bs(blk), s, &sinfo, &unused, &unused, block_state); - cpu_to_le64s(&s->bat[sinfo.bat_idx]); + s->bat[sinfo.bat_idx] = cpu_to_le64(s->bat[sinfo.bat_idx]); sector_num += s->sectors_per_block; } ret = blk_pwrite(blk, file_offset, s->bat, length, 0); diff --git a/block/vhdx.h b/block/vhdx.h index 7003ab7a79..1bfb4e4f73 100644 --- a/block/vhdx.h +++ b/block/vhdx.h @@ -398,7 +398,7 @@ typedef struct BDRVVHDXState { bool log_replayed_on_open; - QLIST_HEAD(VHDXRegionHead, VHDXRegionEntry) regions; + QLIST_HEAD(, VHDXRegionEntry) regions; } BDRVVHDXState; void vhdx_guid_generate(MSGUID *guid); @@ -420,16 +420,16 @@ int vhdx_log_write_and_flush(BlockDriverState *bs, BDRVVHDXState *s, static inline void leguid_to_cpus(MSGUID *guid) { - le32_to_cpus(&guid->data1); - le16_to_cpus(&guid->data2); - le16_to_cpus(&guid->data3); + guid->data1 = le32_to_cpu(guid->data1); + guid->data2 = le16_to_cpu(guid->data2); + guid->data3 = le16_to_cpu(guid->data3); } static inline void cpu_to_leguids(MSGUID *guid) { - cpu_to_le32s(&guid->data1); - cpu_to_le16s(&guid->data2); - cpu_to_le16s(&guid->data3); + guid->data1 = cpu_to_le32(guid->data1); + guid->data2 = cpu_to_le16(guid->data2); + guid->data3 = cpu_to_le16(guid->data3); } void vhdx_header_le_import(VHDXHeader *h); diff --git a/block/vmdk.c b/block/vmdk.c index a9d0084e36..8dec6ef767 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "block/block_int.h" #include "sysemu/block-backend.h" +#include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qemu/error-report.h" #include "qemu/module.h" @@ -194,13 +195,15 @@ static int vmdk_probe(const uint8_t *buf, int buf_size, const char *filename) } if (end - p >= strlen("version=X\n")) { if (strncmp("version=1\n", p, strlen("version=1\n")) == 0 || - strncmp("version=2\n", p, strlen("version=2\n")) == 0) { + strncmp("version=2\n", p, strlen("version=2\n")) == 0 || + strncmp("version=3\n", p, strlen("version=3\n")) == 0) { return 100; } } if (end - p >= strlen("version=X\r\n")) { if (strncmp("version=1\r\n", p, strlen("version=1\r\n")) == 0 || - strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0) { + strncmp("version=2\r\n", p, strlen("version=2\r\n")) == 0 || + strncmp("version=3\r\n", p, strlen("version=3\r\n")) == 0) { return 100; } } @@ -386,12 +389,14 @@ static int vmdk_parent_open(BlockDriverState *bs) ret = -EINVAL; goto out; } - if ((end_name - p_name) > sizeof(bs->backing_file) - 1) { + if ((end_name - p_name) > sizeof(bs->auto_backing_file) - 1) { ret = -EINVAL; goto out; } - pstrcpy(bs->backing_file, end_name - p_name + 1, p_name); + pstrcpy(bs->auto_backing_file, end_name - p_name + 1, p_name); + pstrcpy(bs->backing_file, sizeof(bs->backing_file), + bs->auto_backing_file); } out: @@ -479,6 +484,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, extent->l1_table, l1_size); if (ret < 0) { + bdrv_refresh_filename(extent->file->bs); error_setg_errno(errp, -ret, "Could not read l1 table from extent '%s'", extent->file->bs->filename); @@ -499,6 +505,7 @@ static int vmdk_init_tables(BlockDriverState *bs, VmdkExtent *extent, extent->l1_backup_table, l1_size); if (ret < 0) { + bdrv_refresh_filename(extent->file->bs); error_setg_errno(errp, -ret, "Could not read l1 backup table from extent '%s'", extent->file->bs->filename); @@ -530,6 +537,7 @@ static int vmdk_open_vmfs_sparse(BlockDriverState *bs, ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { + bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, "Could not read header from file '%s'", file->bs->filename); @@ -607,6 +615,7 @@ static int vmdk_open_vmdk4(BlockDriverState *bs, ret = bdrv_pread(file, sizeof(magic), &header, sizeof(header)); if (ret < 0) { + bdrv_refresh_filename(file->bs); error_setg_errno(errp, -ret, "Could not read header from file '%s'", file->bs->filename); @@ -861,13 +870,13 @@ static int vmdk_parse_extents(const char *desc, BlockDriverState *bs, if (!path_is_absolute(fname) && !path_has_protocol(fname) && !desc_file_path[0]) { + bdrv_refresh_filename(bs->file->bs); error_setg(errp, "Cannot use relative extent paths with VMDK " "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_path = path_combine(desc_file_path, fname); ret = snprintf(extent_opt_prefix, 32, "extents.%d", s->num_extents); assert(ret < 32); @@ -1371,7 +1380,6 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, VmdkGrainMarker *data = NULL; uLongf buf_len; QEMUIOVector local_qiov; - struct iovec iov; int64_t write_offset; int64_t write_end_sector; @@ -1399,11 +1407,7 @@ static int vmdk_write_extent(VmdkExtent *extent, int64_t cluster_offset, data->size = cpu_to_le32(buf_len); n_bytes = buf_len + sizeof(VmdkGrainMarker); - iov = (struct iovec) { - .iov_base = data, - .iov_len = n_bytes, - }; - qemu_iovec_init_external(&local_qiov, &iov, 1); + qemu_iovec_init_buf(&local_qiov, data, n_bytes); BLKDBG_EVENT(extent->file, BLKDBG_WRITE_COMPRESSED); } else { @@ -1698,6 +1702,27 @@ static int coroutine_fn vmdk_co_pwritev_compressed(BlockDriverState *bs, uint64_t offset, uint64_t bytes, QEMUIOVector *qiov) { + if (bytes == 0) { + /* The caller will write bytes 0 to signal EOF. + * When receive it, we align EOF to a sector boundary. */ + BDRVVmdkState *s = bs->opaque; + int i, ret; + int64_t length; + + for (i = 0; i < s->num_extents; i++) { + length = bdrv_getlength(s->extents[i].file->bs); + if (length < 0) { + return length; + } + length = QEMU_ALIGN_UP(length, BDRV_SECTOR_SIZE); + ret = bdrv_truncate(s->extents[i].file, length, + PREALLOC_MODE_OFF, NULL); + if (ret < 0) { + return ret; + } + } + return 0; + } return vmdk_co_pwritev(bs, offset, bytes, qiov, 0); } @@ -1720,35 +1745,17 @@ static int coroutine_fn vmdk_co_pwrite_zeroes(BlockDriverState *bs, return ret; } -static int vmdk_create_extent(const char *filename, int64_t filesize, - bool flat, bool compress, bool zeroed_grain, - QemuOpts *opts, Error **errp) +static int vmdk_init_extent(BlockBackend *blk, + int64_t filesize, bool flat, + bool compress, bool zeroed_grain, + Error **errp) { int ret, i; - BlockBackend *blk = NULL; VMDK4Header header; - Error *local_err = NULL; uint32_t tmp, magic, grains, gd_sectors, gt_size, gt_count; uint32_t *gd_buf = NULL; int gd_buf_size; - ret = bdrv_create_file(filename, opts, &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - goto exit; - } - - blk = blk_new_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, - &local_err); - if (blk == NULL) { - error_propagate(errp, local_err); - ret = -EIO; - goto exit; - } - - blk_set_allow_write_beyond_eof(blk, true); - if (flat) { ret = blk_truncate(blk, filesize, PREALLOC_MODE_OFF, errp); goto exit; @@ -1842,18 +1849,53 @@ static int vmdk_create_extent(const char *filename, int64_t filesize, gd_buf, gd_buf_size, 0); if (ret < 0) { error_setg(errp, QERR_IO_ERROR); - goto exit; } ret = 0; exit: - if (blk) { - blk_unref(blk); - } g_free(gd_buf); return ret; } +static int vmdk_create_extent(const char *filename, int64_t filesize, + bool flat, bool compress, bool zeroed_grain, + BlockBackend **pbb, + QemuOpts *opts, Error **errp) +{ + int ret; + BlockBackend *blk = NULL; + Error *local_err = NULL; + + ret = bdrv_create_file(filename, opts, &local_err); + if (ret < 0) { + error_propagate(errp, local_err); + goto exit; + } + + blk = blk_new_open(filename, NULL, NULL, + BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, + &local_err); + if (blk == NULL) { + error_propagate(errp, local_err); + ret = -EIO; + goto exit; + } + + blk_set_allow_write_beyond_eof(blk, true); + + ret = vmdk_init_extent(blk, filesize, flat, compress, zeroed_grain, errp); +exit: + if (blk) { + if (pbb) { + *pbb = blk; + } else { + blk_unref(blk); + blk = NULL; + } + } + return ret; +} + static int filename_decompose(const char *filename, char *path, char *prefix, char *postfix, size_t buf_len, Error **errp) { @@ -1894,33 +1936,57 @@ static int filename_decompose(const char *filename, char *path, char *prefix, return VMDK_OK; } -static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts, - Error **errp) +/* + * idx == 0: get or create the descriptor file (also the image file if in a + * non-split format. + * idx >= 1: get the n-th extent if in a split subformat + */ +typedef BlockBackend *(*vmdk_create_extent_fn)(int64_t size, + int idx, + bool flat, + bool split, + bool compress, + bool zeroed_grain, + void *opaque, + Error **errp); + +static void vmdk_desc_add_extent(GString *desc, + const char *extent_line_fmt, + int64_t size, const char *filename) { - int idx = 0; - BlockBackend *new_blk = NULL; + char *basename = g_path_get_basename(filename); + + g_string_append_printf(desc, extent_line_fmt, + DIV_ROUND_UP(size, BDRV_SECTOR_SIZE), basename); + g_free(basename); +} + +static int coroutine_fn vmdk_co_do_create(int64_t size, + BlockdevVmdkSubformat subformat, + BlockdevVmdkAdapterType adapter_type, + const char *backing_file, + const char *hw_version, + bool compat6, + bool zeroed_grain, + vmdk_create_extent_fn extent_fn, + void *opaque, + Error **errp) +{ + int extent_idx; + BlockBackend *blk = NULL; + BlockBackend *extent_blk; Error *local_err = NULL; char *desc = NULL; - int64_t total_size = 0, filesize; - char *adapter_type = NULL; - char *backing_file = NULL; - char *hw_version = NULL; - char *fmt = NULL; int ret = 0; bool flat, split, compress; GString *ext_desc_lines; - char *path = g_malloc0(PATH_MAX); - char *prefix = g_malloc0(PATH_MAX); - char *postfix = g_malloc0(PATH_MAX); - char *desc_line = g_malloc0(BUF_SIZE); - char *ext_filename = g_malloc0(PATH_MAX); - char *desc_filename = g_malloc0(PATH_MAX); const int64_t split_size = 0x80000000; /* VMDK has constant split size */ - const char *desc_extent_line; + int64_t extent_size; + int64_t created_size = 0; + const char *extent_line_fmt; char *parent_desc_line = g_malloc0(BUF_SIZE); uint32_t parent_cid = 0xffffffff; uint32_t number_heads = 16; - bool zeroed_grain = false; uint32_t desc_offset = 0, desc_len; const char desc_template[] = "# Disk DescriptorFile\n" @@ -1944,71 +2010,35 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts ext_desc_lines = g_string_new(NULL); - if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) { - ret = -EINVAL; - goto exit; - } /* Read out options */ - total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), - BDRV_SECTOR_SIZE); - adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE); - backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); - hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION); - if (qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false)) { - if (strcmp(hw_version, "undefined")) { + if (compat6) { + if (hw_version) { error_setg(errp, "compat6 cannot be enabled with hwversion set"); ret = -EINVAL; goto exit; } - g_free(hw_version); - hw_version = g_strdup("6"); + hw_version = "6"; } - if (strcmp(hw_version, "undefined") == 0) { - g_free(hw_version); - hw_version = g_strdup("4"); - } - fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); - if (qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false)) { - zeroed_grain = true; + if (!hw_version) { + hw_version = "4"; } - if (!adapter_type) { - adapter_type = g_strdup("ide"); - } else if (strcmp(adapter_type, "ide") && - strcmp(adapter_type, "buslogic") && - strcmp(adapter_type, "lsilogic") && - strcmp(adapter_type, "legacyESX")) { - error_setg(errp, "Unknown adapter type: '%s'", adapter_type); - ret = -EINVAL; - goto exit; - } - if (strcmp(adapter_type, "ide") != 0) { + if (adapter_type != BLOCKDEV_VMDK_ADAPTER_TYPE_IDE) { /* that's the number of heads with which vmware operates when creating, exporting, etc. vmdk files with a non-ide adapter type */ number_heads = 255; } - if (!fmt) { - /* Default format to monolithicSparse */ - fmt = g_strdup("monolithicSparse"); - } else if (strcmp(fmt, "monolithicFlat") && - strcmp(fmt, "monolithicSparse") && - strcmp(fmt, "twoGbMaxExtentSparse") && - strcmp(fmt, "twoGbMaxExtentFlat") && - strcmp(fmt, "streamOptimized")) { - error_setg(errp, "Unknown subformat: '%s'", fmt); - ret = -EINVAL; - goto exit; - } - split = !(strcmp(fmt, "twoGbMaxExtentFlat") && - strcmp(fmt, "twoGbMaxExtentSparse")); - flat = !(strcmp(fmt, "monolithicFlat") && - strcmp(fmt, "twoGbMaxExtentFlat")); - compress = !strcmp(fmt, "streamOptimized"); + split = (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT) || + (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTSPARSE); + flat = (subformat == BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICFLAT) || + (subformat == BLOCKDEV_VMDK_SUBFORMAT_TWOGBMAXEXTENTFLAT); + compress = subformat == BLOCKDEV_VMDK_SUBFORMAT_STREAMOPTIMIZED; + if (flat) { - desc_extent_line = "RW %" PRId64 " FLAT \"%s\" 0\n"; + extent_line_fmt = "RW %" PRId64 " FLAT \"%s\" 0\n"; } else { - desc_extent_line = "RW %" PRId64 " SPARSE \"%s\"\n"; + extent_line_fmt = "RW %" PRId64 " SPARSE \"%s\"\n"; } if (flat && backing_file) { error_setg(errp, "Flat image can't have backing file"); @@ -2020,106 +2050,111 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts ret = -ENOTSUP; goto exit; } + + /* Create extents */ + if (split) { + extent_size = split_size; + } else { + extent_size = size; + } + if (!split && !flat) { + created_size = extent_size; + } else { + created_size = 0; + } + /* Get the descriptor file BDS */ + blk = extent_fn(created_size, 0, flat, split, compress, zeroed_grain, + opaque, errp); + if (!blk) { + ret = -EIO; + goto exit; + } + if (!split && !flat) { + vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, created_size, + blk_bs(blk)->filename); + } + if (backing_file) { - BlockBackend *blk; - char *full_backing = g_new0(char, PATH_MAX); - bdrv_get_full_backing_filename_from_filename(filename, backing_file, - full_backing, PATH_MAX, - &local_err); + BlockBackend *backing; + char *full_backing = + bdrv_get_full_backing_filename_from_filename(blk_bs(blk)->filename, + backing_file, + &local_err); if (local_err) { - g_free(full_backing); error_propagate(errp, local_err); ret = -ENOENT; goto exit; } + assert(full_backing); - blk = blk_new_open(full_backing, NULL, NULL, - BDRV_O_NO_BACKING, errp); + backing = blk_new_open(full_backing, NULL, NULL, + BDRV_O_NO_BACKING, errp); g_free(full_backing); - if (blk == NULL) { + if (backing == NULL) { ret = -EIO; goto exit; } - if (strcmp(blk_bs(blk)->drv->format_name, "vmdk")) { - blk_unref(blk); + if (strcmp(blk_bs(backing)->drv->format_name, "vmdk")) { + error_setg(errp, "Invalid backing file format: %s. Must be vmdk", + blk_bs(backing)->drv->format_name); + blk_unref(backing); ret = -EINVAL; goto exit; } - ret = vmdk_read_cid(blk_bs(blk), 0, &parent_cid); - blk_unref(blk); + ret = vmdk_read_cid(blk_bs(backing), 0, &parent_cid); + blk_unref(backing); if (ret) { + error_setg(errp, "Failed to read parent CID"); goto exit; } snprintf(parent_desc_line, BUF_SIZE, "parentFileNameHint=\"%s\"", backing_file); } - - /* Create extents */ - filesize = total_size; - while (filesize > 0) { - int64_t size = filesize; - - if (split && size > split_size) { - size = split_size; - } - if (split) { - snprintf(desc_filename, PATH_MAX, "%s-%c%03d%s", - prefix, flat ? 'f' : 's', ++idx, postfix); - } else if (flat) { - snprintf(desc_filename, PATH_MAX, "%s-flat%s", prefix, postfix); - } else { - snprintf(desc_filename, PATH_MAX, "%s%s", prefix, postfix); - } - snprintf(ext_filename, PATH_MAX, "%s%s", path, desc_filename); - - if (vmdk_create_extent(ext_filename, size, - flat, compress, zeroed_grain, opts, errp)) { + extent_idx = 1; + while (created_size < size) { + int64_t cur_size = MIN(size - created_size, extent_size); + extent_blk = extent_fn(cur_size, extent_idx, flat, split, compress, + zeroed_grain, opaque, errp); + if (!extent_blk) { ret = -EINVAL; goto exit; } - filesize -= size; - - /* Format description line */ - snprintf(desc_line, BUF_SIZE, - desc_extent_line, size / BDRV_SECTOR_SIZE, desc_filename); - g_string_append(ext_desc_lines, desc_line); + vmdk_desc_add_extent(ext_desc_lines, extent_line_fmt, cur_size, + blk_bs(extent_blk)->filename); + created_size += cur_size; + extent_idx++; + blk_unref(extent_blk); } + + /* Check whether we got excess extents */ + extent_blk = extent_fn(-1, extent_idx, flat, split, compress, zeroed_grain, + opaque, NULL); + if (extent_blk) { + blk_unref(extent_blk); + error_setg(errp, "List of extents contains unused extents"); + ret = -EINVAL; + goto exit; + } + /* generate descriptor file */ desc = g_strdup_printf(desc_template, g_random_int(), parent_cid, - fmt, + BlockdevVmdkSubformat_str(subformat), parent_desc_line, ext_desc_lines->str, hw_version, - total_size / + size / (int64_t)(63 * number_heads * BDRV_SECTOR_SIZE), number_heads, - adapter_type); + BlockdevVmdkAdapterType_str(adapter_type)); desc_len = strlen(desc); /* the descriptor offset = 0x200 */ if (!split && !flat) { desc_offset = 0x200; - } else { - ret = bdrv_create_file(filename, opts, &local_err); - if (ret < 0) { - error_propagate(errp, local_err); - goto exit; - } } - new_blk = blk_new_open(filename, NULL, NULL, - BDRV_O_RDWR | BDRV_O_RESIZE | BDRV_O_PROTOCOL, - &local_err); - if (new_blk == NULL) { - error_propagate(errp, local_err); - ret = -EIO; - goto exit; - } - - blk_set_allow_write_beyond_eof(new_blk, true); - - ret = blk_pwrite(new_blk, desc_offset, desc, desc_len, 0); + ret = blk_pwrite(blk, desc_offset, desc, desc_len, 0); if (ret < 0) { error_setg_errno(errp, -ret, "Could not write description"); goto exit; @@ -2127,12 +2162,152 @@ static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts /* bdrv_pwrite write padding zeros to align to sector, we don't need that * for description file */ if (desc_offset == 0) { - ret = blk_truncate(new_blk, desc_len, PREALLOC_MODE_OFF, errp); + ret = blk_truncate(blk, desc_len, PREALLOC_MODE_OFF, errp); + if (ret < 0) { + goto exit; + } } + ret = 0; exit: - if (new_blk) { - blk_unref(new_blk); + if (blk) { + blk_unref(blk); } + g_free(desc); + g_free(parent_desc_line); + g_string_free(ext_desc_lines, true); + return ret; +} + +typedef struct { + char *path; + char *prefix; + char *postfix; + QemuOpts *opts; +} VMDKCreateOptsData; + +static BlockBackend *vmdk_co_create_opts_cb(int64_t size, int idx, + bool flat, bool split, bool compress, + bool zeroed_grain, void *opaque, + Error **errp) +{ + BlockBackend *blk = NULL; + BlockDriverState *bs = NULL; + VMDKCreateOptsData *data = opaque; + char *ext_filename = NULL; + char *rel_filename = NULL; + + /* We're done, don't create excess extents. */ + if (size == -1) { + assert(errp == NULL); + return NULL; + } + + if (idx == 0) { + rel_filename = g_strdup_printf("%s%s", data->prefix, data->postfix); + } else if (split) { + rel_filename = g_strdup_printf("%s-%c%03d%s", + data->prefix, + flat ? 'f' : 's', idx, data->postfix); + } else { + assert(idx == 1); + rel_filename = g_strdup_printf("%s-flat%s", data->prefix, data->postfix); + } + + ext_filename = g_strdup_printf("%s%s", data->path, rel_filename); + g_free(rel_filename); + + if (vmdk_create_extent(ext_filename, size, + flat, compress, zeroed_grain, &blk, data->opts, + errp)) { + goto exit; + } + bdrv_unref(bs); +exit: + g_free(ext_filename); + return blk; +} + +static int coroutine_fn vmdk_co_create_opts(const char *filename, QemuOpts *opts, + Error **errp) +{ + Error *local_err = NULL; + char *desc = NULL; + int64_t total_size = 0; + char *adapter_type = NULL; + BlockdevVmdkAdapterType adapter_type_enum; + char *backing_file = NULL; + char *hw_version = NULL; + char *fmt = NULL; + BlockdevVmdkSubformat subformat; + int ret = 0; + char *path = g_malloc0(PATH_MAX); + char *prefix = g_malloc0(PATH_MAX); + char *postfix = g_malloc0(PATH_MAX); + char *desc_line = g_malloc0(BUF_SIZE); + char *ext_filename = g_malloc0(PATH_MAX); + char *desc_filename = g_malloc0(PATH_MAX); + char *parent_desc_line = g_malloc0(BUF_SIZE); + bool zeroed_grain; + bool compat6; + VMDKCreateOptsData data; + + if (filename_decompose(filename, path, prefix, postfix, PATH_MAX, errp)) { + ret = -EINVAL; + goto exit; + } + /* Read out options */ + total_size = ROUND_UP(qemu_opt_get_size_del(opts, BLOCK_OPT_SIZE, 0), + BDRV_SECTOR_SIZE); + adapter_type = qemu_opt_get_del(opts, BLOCK_OPT_ADAPTER_TYPE); + backing_file = qemu_opt_get_del(opts, BLOCK_OPT_BACKING_FILE); + hw_version = qemu_opt_get_del(opts, BLOCK_OPT_HWVERSION); + compat6 = qemu_opt_get_bool_del(opts, BLOCK_OPT_COMPAT6, false); + if (strcmp(hw_version, "undefined") == 0) { + g_free(hw_version); + hw_version = NULL; + } + fmt = qemu_opt_get_del(opts, BLOCK_OPT_SUBFMT); + zeroed_grain = qemu_opt_get_bool_del(opts, BLOCK_OPT_ZEROED_GRAIN, false); + + if (adapter_type) { + adapter_type_enum = qapi_enum_parse(&BlockdevVmdkAdapterType_lookup, + adapter_type, + BLOCKDEV_VMDK_ADAPTER_TYPE_IDE, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto exit; + } + } else { + adapter_type_enum = BLOCKDEV_VMDK_ADAPTER_TYPE_IDE; + } + + if (!fmt) { + /* Default format to monolithicSparse */ + subformat = BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE; + } else { + subformat = qapi_enum_parse(&BlockdevVmdkSubformat_lookup, + fmt, + BLOCKDEV_VMDK_SUBFORMAT_MONOLITHICSPARSE, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + ret = -EINVAL; + goto exit; + } + } + data = (VMDKCreateOptsData){ + .prefix = prefix, + .postfix = postfix, + .path = path, + .opts = opts, + }; + ret = vmdk_co_do_create(total_size, subformat, adapter_type_enum, + backing_file, hw_version, compat6, zeroed_grain, + vmdk_co_create_opts_cb, &data, errp); + +exit: g_free(adapter_type); g_free(backing_file); g_free(hw_version); @@ -2145,7 +2320,86 @@ exit: g_free(ext_filename); g_free(desc_filename); g_free(parent_desc_line); - g_string_free(ext_desc_lines, true); + return ret; +} + +static BlockBackend *vmdk_co_create_cb(int64_t size, int idx, + bool flat, bool split, bool compress, + bool zeroed_grain, void *opaque, + Error **errp) +{ + int ret; + BlockDriverState *bs; + BlockBackend *blk; + BlockdevCreateOptionsVmdk *opts = opaque; + + if (idx == 0) { + bs = bdrv_open_blockdev_ref(opts->file, errp); + } else { + int i; + BlockdevRefList *list = opts->extents; + for (i = 1; i < idx; i++) { + if (!list || !list->next) { + error_setg(errp, "Extent [%d] not specified", i); + return NULL; + } + list = list->next; + } + if (!list) { + error_setg(errp, "Extent [%d] not specified", idx - 1); + return NULL; + } + bs = bdrv_open_blockdev_ref(list->value, errp); + } + if (!bs) { + return NULL; + } + blk = blk_new(BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_RESIZE, + BLK_PERM_ALL); + if (blk_insert_bs(blk, bs, errp)) { + bdrv_unref(bs); + return NULL; + } + blk_set_allow_write_beyond_eof(blk, true); + bdrv_unref(bs); + + if (size != -1) { + ret = vmdk_init_extent(blk, size, flat, compress, zeroed_grain, errp); + if (ret) { + blk_unref(blk); + blk = NULL; + } + } + return blk; +} + +static int coroutine_fn vmdk_co_create(BlockdevCreateOptions *create_options, + Error **errp) +{ + int ret; + BlockdevCreateOptionsVmdk *opts; + + opts = &create_options->u.vmdk; + + /* Validate options */ + if (!QEMU_IS_ALIGNED(opts->size, BDRV_SECTOR_SIZE)) { + error_setg(errp, "Image size must be a multiple of 512 bytes"); + ret = -EINVAL; + goto out; + } + + ret = vmdk_co_do_create(opts->size, + opts->subformat, + opts->adapter_type, + opts->backing_file, + opts->hwversion, + false, + opts->zeroed_grain, + vmdk_co_create_cb, + opts, errp); + return ret; + +out: return ret; } @@ -2220,6 +2474,7 @@ static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent) { ImageInfo *info = g_new0(ImageInfo, 1); + bdrv_refresh_filename(extent->file->bs); *info = (ImageInfo){ .filename = g_strdup(extent->file->bs->filename), .format = g_strdup(extent->type), @@ -2293,7 +2548,8 @@ static int coroutine_fn vmdk_co_check(BlockDriverState *bs, return ret; } -static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs) +static ImageInfoSpecific *vmdk_get_specific_info(BlockDriverState *bs, + Error **errp) { int i; BDRVVmdkState *s = bs->opaque; @@ -2350,6 +2606,23 @@ static int vmdk_get_info(BlockDriverState *bs, BlockDriverInfo *bdi) return 0; } +static void vmdk_gather_child_options(BlockDriverState *bs, QDict *target, + bool backing_overridden) +{ + /* No children but file and backing can be explicitly specified (TODO) */ + qdict_put(target, "file", + qobject_ref(bs->file->bs->full_open_options)); + + if (backing_overridden) { + if (bs->backing) { + qdict_put(target, "backing", + qobject_ref(bs->backing->bs->full_open_options)); + } else { + qdict_put_null(target, "backing"); + } + } +} + static QemuOptsList vmdk_create_opts = { .name = "vmdk-create-opts", .head = QTAILQ_HEAD_INITIALIZER(vmdk_create_opts.head), @@ -2413,6 +2686,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_co_pwrite_zeroes = vmdk_co_pwrite_zeroes, .bdrv_close = vmdk_close, .bdrv_co_create_opts = vmdk_co_create_opts, + .bdrv_co_create = vmdk_co_create, .bdrv_co_flush_to_disk = vmdk_co_flush, .bdrv_co_block_status = vmdk_co_block_status, .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size, @@ -2420,6 +2694,7 @@ static BlockDriver bdrv_vmdk = { .bdrv_get_specific_info = vmdk_get_specific_info, .bdrv_refresh_limits = vmdk_refresh_limits, .bdrv_get_info = vmdk_get_info, + .bdrv_gather_child_options = vmdk_gather_child_options, .supports_backing = true, .create_opts = &vmdk_create_opts, diff --git a/block/vpc.c b/block/vpc.c index bf294abfa7..a902a4c54d 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -187,7 +187,7 @@ static uint32_t vpc_checksum(uint8_t* buf, size_t size) static int vpc_probe(const uint8_t *buf, int buf_size, const char *filename) { if (buf_size >= 8 && !strncmp((char *)buf, "conectix", 8)) - return 100; + return 100; return 0; } @@ -284,9 +284,11 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, checksum = be32_to_cpu(footer->checksum); footer->checksum = 0; - if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) - fprintf(stderr, "block-vpc: The header checksum of '%s' is " - "incorrect.\n", bs->filename); + if (vpc_checksum(s->footer_buf, HEADER_SIZE) != checksum) { + error_setg(errp, "Incorrect header checksum"); + ret = -EINVAL; + goto fail; + } /* Write 'checksum' back to footer, or else will leave it with zero. */ footer->checksum = cpu_to_be32(checksum); @@ -454,10 +456,12 @@ static int vpc_open(BlockDriverState *bs, QDict *options, int flags, } qemu_co_mutex_init(&s->lock); + qemu_opts_del(opts); return 0; fail: + qemu_opts_del(opts); qemu_vfree(s->pagetable); #ifdef CACHE g_free(s->pageentry_u8); @@ -975,6 +979,7 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, int64_t total_size; int disk_type; int ret = -EIO; + QemuUUID uuid; assert(opts->driver == BLOCKDEV_DRIVER_VPC); vpc_opts = &opts->u.vpc; @@ -1058,7 +1063,8 @@ static int coroutine_fn vpc_co_create(BlockdevCreateOptions *opts, footer->type = cpu_to_be32(disk_type); - qemu_uuid_generate(&footer->uuid); + qemu_uuid_generate(&uuid); + footer->uuid = uuid; footer->checksum = cpu_to_be32(vpc_checksum(buf, HEADER_SIZE)); @@ -1212,6 +1218,12 @@ static QemuOptsList vpc_create_opts = { } }; +static const char *const vpc_strong_runtime_opts[] = { + VPC_OPT_SIZE_CALC, + + NULL +}; + static BlockDriver bdrv_vpc = { .format_name = "vpc", .instance_size = sizeof(BDRVVPCState), @@ -1232,6 +1244,7 @@ static BlockDriver bdrv_vpc = { .create_opts = &vpc_create_opts, .bdrv_has_zero_init = vpc_has_zero_init, + .strong_runtime_opts = vpc_strong_runtime_opts, }; static void bdrv_vpc_init(void) diff --git a/block/vvfat.c b/block/vvfat.c index fc41841a5c..5f66787890 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -100,30 +100,26 @@ static inline void array_free(array_t* array) /* does not automatically grow */ static inline void* array_get(array_t* array,unsigned int index) { assert(index < array->next); + assert(array->pointer); return array->pointer + index * array->item_size; } -static inline int array_ensure_allocated(array_t* array, int index) +static inline void array_ensure_allocated(array_t *array, int index) { if((index + 1) * array->item_size > array->size) { int new_size = (index + 32) * array->item_size; array->pointer = g_realloc(array->pointer, new_size); - if (!array->pointer) - return -1; + assert(array->pointer); memset(array->pointer + array->size, 0, new_size - array->size); array->size = new_size; array->next = index + 1; } - - return 0; } static inline void* array_get_next(array_t* array) { unsigned int next = array->next; - if (array_ensure_allocated(array, next) < 0) - return NULL; - + array_ensure_allocated(array, next); array->next = next + 1; return array_get(array, next); } @@ -973,10 +969,10 @@ static int init_directories(BDRVVVFATState* s, mapping = array_get(&(s->mapping), i); if (mapping->mode & MODE_DIRECTORY) { + char *path = mapping->path; mapping->begin = cluster; if(read_directory(s, i)) { - error_setg(errp, "Could not read directory %s", - mapping->path); + error_setg(errp, "Could not read directory %s", path); return -1; } mapping = array_get(&(s->mapping), i); @@ -1262,15 +1258,9 @@ static int vvfat_open(BlockDriverState *bs, QDict *options, int flags, "Unable to set VVFAT to 'rw' when drive is read-only"); goto fail; } - } else if (!bdrv_is_read_only(bs)) { - error_report("Opening non-rw vvfat images without an explicit " - "read-only=on option is deprecated. Future versions " - "will refuse to open the image instead of " - "automatically marking the image read-only."); - /* read only is the default for safety */ - ret = bdrv_set_read_only(bs, true, &local_err); + } else { + ret = bdrv_apply_auto_read_only(bs, NULL, errp); if (ret < 0) { - error_propagate(errp, local_err); goto fail; } } @@ -2428,16 +2418,13 @@ static int commit_direntries(BDRVVVFATState* s, direntry_t* direntry = array_get(&(s->directory), dir_index); uint32_t first_cluster = dir_index == 0 ? 0 : begin_of_direntry(direntry); mapping_t* mapping = find_mapping_for_cluster(s, first_cluster); - int factor = 0x10 * s->sectors_per_cluster; int old_cluster_count, new_cluster_count; - int current_dir_index = mapping->info.dir.first_dir_index; - int first_dir_index = current_dir_index; + int current_dir_index; + int first_dir_index; int ret, i; uint32_t c; -DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapping->path, parent_mapping_index)); - assert(direntry); assert(mapping); assert(mapping->begin == first_cluster); @@ -2445,6 +2432,11 @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp assert(mapping->mode & MODE_DIRECTORY); assert(dir_index == 0 || is_directory(direntry)); + DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", + mapping->path, parent_mapping_index)); + + current_dir_index = mapping->info.dir.first_dir_index; + first_dir_index = current_dir_index; mapping->info.dir.parent_mapping_index = parent_mapping_index; if (first_cluster == 0) { @@ -2494,6 +2486,9 @@ DLOG(fprintf(stderr, "commit_direntries for %s, parent_mapping_index %d\n", mapp direntry = array_get(&(s->directory), first_dir_index + i); if (is_directory(direntry) && !is_dot(direntry)) { mapping = find_mapping_for_cluster(s, first_cluster); + if (mapping == NULL) { + return -1; + } assert(mapping->mode & MODE_DIRECTORY); ret = commit_direntries(s, first_dir_index + i, array_index(&(s->mapping), mapping)); @@ -2515,13 +2510,17 @@ static int commit_one_file(BDRVVVFATState* s, uint32_t first_cluster = c; mapping_t* mapping = find_mapping_for_cluster(s, c); uint32_t size = filesize_of_direntry(direntry); - char* cluster = g_malloc(s->cluster_size); + char *cluster; uint32_t i; int fd = 0; assert(offset < size); assert((offset % s->cluster_size) == 0); + if (mapping == NULL) { + return -1; + } + for (i = s->cluster_size; i < offset; i += s->cluster_size) c = modified_fat_get(s, c); @@ -2529,17 +2528,17 @@ static int commit_one_file(BDRVVVFATState* s, if (fd < 0) { fprintf(stderr, "Could not open %s... (%s, %d)\n", mapping->path, strerror(errno), errno); - g_free(cluster); return fd; } if (offset > 0) { if (lseek(fd, offset, SEEK_SET) != offset) { qemu_close(fd); - g_free(cluster); return -3; } } + cluster = g_malloc(s->cluster_size); + while (offset < size) { uint32_t c1; int rest_size = (size - offset > s->cluster_size ? @@ -2668,8 +2667,12 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) if (commit->action == ACTION_RENAME) { mapping_t* mapping = find_mapping_for_cluster(s, commit->param.rename.cluster); - char* old_path = mapping->path; + char *old_path; + if (mapping == NULL) { + return -1; + } + old_path = mapping->path; assert(commit->path); mapping->path = commit->path; if (rename(old_path, mapping->path)) @@ -2690,10 +2693,15 @@ static int handle_renames_and_mkdirs(BDRVVVFATState* s) direntry_t* d = direntry + i; if (is_file(d) || (is_directory(d) && !is_dot(d))) { + int l; + char *new_path; mapping_t* m = find_mapping_for_cluster(s, begin_of_direntry(d)); - int l = strlen(m->path); - char* new_path = g_malloc(l + diff + 1); + if (m == NULL) { + return -1; + } + l = strlen(m->path); + new_path = g_malloc(l + diff + 1); assert(!strncmp(m->path, mapping->path, l2)); @@ -3130,6 +3138,7 @@ static void vvfat_qcow_options(int *child_flags, QDict *child_options, int parent_flags, QDict *parent_options) { qdict_set_default_str(child_options, BDRV_OPT_READ_ONLY, "off"); + qdict_set_default_str(child_options, BDRV_OPT_AUTO_READ_ONLY, "off"); qdict_set_default_str(child_options, BDRV_OPT_CACHE_NO_FLUSH, "on"); } @@ -3244,6 +3253,16 @@ static void vvfat_close(BlockDriverState *bs) } } +static const char *const vvfat_strong_runtime_opts[] = { + "dir", + "fat-type", + "floppy", + "label", + "rw", + + NULL +}; + static BlockDriver bdrv_vvfat = { .format_name = "vvfat", .protocol_name = "fat", @@ -3258,6 +3277,8 @@ static BlockDriver bdrv_vvfat = { .bdrv_co_preadv = vvfat_co_preadv, .bdrv_co_pwritev = vvfat_co_pwritev, .bdrv_co_block_status = vvfat_co_block_status, + + .strong_runtime_opts = vvfat_strong_runtime_opts, }; static void bdrv_vvfat_init(void) diff --git a/block/vxhs.c b/block/vxhs.c index 0cb0a007e9..2e18229ba4 100644 --- a/block/vxhs.c +++ b/block/vxhs.c @@ -556,6 +556,16 @@ static int64_t vxhs_getlength(BlockDriverState *bs) return vdisk_size; } +static const char *const vxhs_strong_runtime_opts[] = { + VXHS_OPT_VDISK_ID, + "tls-creds", + VXHS_OPT_HOST, + VXHS_OPT_PORT, + VXHS_OPT_SERVER".", + + NULL +}; + static BlockDriver bdrv_vxhs = { .format_name = "vxhs", .protocol_name = "vxhs", @@ -567,6 +577,7 @@ static BlockDriver bdrv_vxhs = { .bdrv_getlength = vxhs_getlength, .bdrv_aio_preadv = vxhs_aio_preadv, .bdrv_aio_pwritev = vxhs_aio_pwritev, + .strong_runtime_opts = vxhs_strong_runtime_opts, }; static void bdrv_vxhs_init(void) diff --git a/block/write-threshold.c b/block/write-threshold.c index 1d48fc2077..85b78dc2a9 100644 --- a/block/write-threshold.c +++ b/block/write-threshold.c @@ -63,8 +63,7 @@ static int coroutine_fn before_write_notify(NotifierWithReturn *notifier, qapi_event_send_block_write_threshold( bs->node_name, amount, - bs->write_threshold_offset, - &error_abort); + bs->write_threshold_offset); /* autodisable to avoid flooding the monitor */ write_threshold_disable(bs); diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 1ef11041a7..66eebab318 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -23,6 +23,7 @@ typedef struct NBDServerData { QIONetListener *listener; QCryptoTLSCreds *tlscreds; + char *tlsauthz; } NBDServerData; static NBDServerData *nbd_server; @@ -36,8 +37,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, gpointer opaque) { qio_channel_set_name(QIO_CHANNEL(cioc), "nbd-server"); - nbd_client_new(NULL, cioc, - nbd_server->tlscreds, NULL, + nbd_client_new(cioc, nbd_server->tlscreds, nbd_server->tlsauthz, nbd_blockdev_client_closed); } @@ -53,6 +53,7 @@ static void nbd_server_free(NBDServerData *server) if (server->tlscreds) { object_unref(OBJECT(server->tlscreds)); } + g_free(server->tlsauthz); g_free(server); } @@ -88,7 +89,7 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) void nbd_server_start(SocketAddress *addr, const char *tls_creds, - Error **errp) + const char *tls_authz, Error **errp) { if (nbd_server) { error_setg(errp, "NBD server already running"); @@ -118,6 +119,8 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, } } + nbd_server->tlsauthz = g_strdup(tls_authz); + qio_net_listener_set_client_func(nbd_server->listener, nbd_accept, NULL, @@ -132,20 +135,23 @@ void nbd_server_start(SocketAddress *addr, const char *tls_creds, void qmp_nbd_server_start(SocketAddressLegacy *addr, bool has_tls_creds, const char *tls_creds, + bool has_tls_authz, const char *tls_authz, Error **errp) { SocketAddress *addr_flat = socket_address_flatten(addr); - nbd_server_start(addr_flat, tls_creds, errp); + nbd_server_start(addr_flat, tls_creds, tls_authz, errp); qapi_free_SocketAddress(addr_flat); } void qmp_nbd_server_add(const char *device, bool has_name, const char *name, - bool has_writable, bool writable, Error **errp) + bool has_writable, bool writable, + bool has_bitmap, const char *bitmap, Error **errp) { BlockDriverState *bs = NULL; BlockBackend *on_eject_blk; NBDExport *exp; + int64_t len; if (!nbd_server) { error_setg(errp, "NBD server not running"); @@ -168,6 +174,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, return; } + len = bdrv_getlength(bs); + if (len < 0) { + error_setg_errno(errp, -len, + "Failed to determine the NBD export's length"); + return; + } + if (!has_writable) { writable = false; } @@ -175,14 +188,13 @@ void qmp_nbd_server_add(const char *device, bool has_name, const char *name, writable = false; } - exp = nbd_export_new(bs, 0, -1, writable ? 0 : NBD_FLAG_READ_ONLY, + exp = nbd_export_new(bs, 0, len, name, NULL, bitmap, + writable ? 0 : NBD_FLAG_READ_ONLY, NULL, false, on_eject_blk, errp); if (!exp) { return; } - nbd_export_set_name(exp, name); - /* 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. */ @@ -215,31 +227,13 @@ void qmp_nbd_server_remove(const char *name, void qmp_nbd_server_stop(Error **errp) { - nbd_export_close_all(); - - nbd_server_free(nbd_server); - nbd_server = NULL; -} - -void qmp_x_nbd_server_add_bitmap(const char *name, const char *bitmap, - bool has_bitmap_export_name, - const char *bitmap_export_name, - Error **errp) -{ - NBDExport *exp; - if (!nbd_server) { error_setg(errp, "NBD server not running"); return; } - exp = nbd_export_find(name); - if (exp == NULL) { - error_setg(errp, "Export '%s' is not found", name); - return; - } + nbd_export_close_all(); - nbd_export_bitmap(exp, bitmap, - has_bitmap_export_name ? bitmap_export_name : bitmap, - errp); + nbd_server_free(nbd_server); + nbd_server = NULL; } diff --git a/blockdev.c b/blockdev.c index 3e719a22b9..f62bdd0003 100644 --- a/blockdev.c +++ b/blockdev.c @@ -531,7 +531,9 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, if ((buf = qemu_opt_get(opts, "format")) != NULL) { if (is_help_option(buf)) { error_printf("Supported formats:"); - bdrv_iterate_format(bdrv_format_print, NULL); + bdrv_iterate_format(bdrv_format_print, NULL, false); + error_printf("\nSupported formats (read-only):"); + bdrv_iterate_format(bdrv_format_print, NULL, true); error_printf("\n"); goto early_err; } @@ -590,6 +592,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, qdict_set_default_str(bs_opts, BDRV_OPT_CACHE_NO_FLUSH, "off"); qdict_set_default_str(bs_opts, BDRV_OPT_READ_ONLY, read_only ? "on" : "off"); + qdict_set_default_str(bs_opts, BDRV_OPT_AUTO_READ_ONLY, "on"); assert((bdrv_flags & BDRV_O_CACHE_MASK) == 0); if (runstate_check(RUN_STATE_INMIGRATE)) { @@ -730,30 +733,6 @@ QemuOptsList qemu_legacy_drive_opts = { .name = "if", .type = QEMU_OPT_STRING, .help = "interface (ide, scsi, sd, mtd, floppy, pflash, virtio)", - },{ - .name = "cyls", - .type = QEMU_OPT_NUMBER, - .help = "number of cylinders (ide disk geometry)", - },{ - .name = "heads", - .type = QEMU_OPT_NUMBER, - .help = "number of heads (ide disk geometry)", - },{ - .name = "secs", - .type = QEMU_OPT_NUMBER, - .help = "number of sectors (ide disk geometry)", - },{ - .name = "trans", - .type = QEMU_OPT_STRING, - .help = "chs translation (auto, lba, none)", - },{ - .name = "addr", - .type = QEMU_OPT_STRING, - .help = "pci address (virtio only)", - },{ - .name = "serial", - .type = QEMU_OPT_STRING, - .help = "disk serial number", },{ .name = "file", .type = QEMU_OPT_STRING, @@ -787,7 +766,8 @@ QemuOptsList qemu_legacy_drive_opts = { }, }; -DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) +DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type, + Error **errp) { const char *value; BlockBackend *blk; @@ -796,19 +776,13 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) QemuOpts *legacy_opts; DriveMediaType media = MEDIA_DISK; BlockInterfaceType type; - int cyls, heads, secs, translation; int max_devs, bus_id, unit_id, index; - const char *devaddr; const char *werror, *rerror; bool read_only = false; bool copy_on_read; - const char *serial; const char *filename; Error *local_err = NULL; int i; - const char *deprecated[] = { - "serial", "trans", "secs", "heads", "cyls", "addr" - }; bool locked; /* Change legacy command line options into QMP ones */ @@ -843,7 +817,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) qemu_opt_rename(all_opts, opt_renames[i].from, opt_renames[i].to, &local_err); if (local_err) { - error_report_err(local_err); + error_propagate(errp, local_err); return NULL; } } @@ -854,7 +828,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) bool writethrough; if (bdrv_parse_cache_mode(value, &flags, &writethrough) != 0) { - error_report("invalid cache option"); + error_setg(errp, "invalid cache option"); return NULL; } @@ -882,20 +856,10 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) &error_abort); qemu_opts_absorb_qdict(legacy_opts, bs_opts, &local_err); if (local_err) { - error_report_err(local_err); + error_propagate(errp, local_err); goto fail; } - /* Other deprecated options */ - if (!qtest_enabled()) { - for (i = 0; i < ARRAY_SIZE(deprecated); i++) { - if (qemu_opt_get(legacy_opts, deprecated[i]) != NULL) { - error_report("'%s' is deprecated, please use the corresponding " - "option of '-device' instead", deprecated[i]); - } - } - } - /* Media type */ value = qemu_opt_get(legacy_opts, "media"); if (value) { @@ -905,7 +869,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) media = MEDIA_CDROM; read_only = true; } else { - error_report("'%s' invalid media", value); + error_setg(errp, "'%s' invalid media", value); goto fail; } } @@ -930,64 +894,13 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) type++) { } if (type == IF_COUNT) { - error_report("unsupported bus type '%s'", value); + error_setg(errp, "unsupported bus type '%s'", value); goto fail; } } else { type = block_default_type; } - /* Geometry */ - cyls = qemu_opt_get_number(legacy_opts, "cyls", 0); - heads = qemu_opt_get_number(legacy_opts, "heads", 0); - secs = qemu_opt_get_number(legacy_opts, "secs", 0); - - if (cyls || heads || secs) { - if (cyls < 1) { - error_report("invalid physical cyls number"); - goto fail; - } - if (heads < 1) { - error_report("invalid physical heads number"); - goto fail; - } - if (secs < 1) { - error_report("invalid physical secs number"); - goto fail; - } - } - - translation = BIOS_ATA_TRANSLATION_AUTO; - value = qemu_opt_get(legacy_opts, "trans"); - if (value != NULL) { - if (!cyls) { - error_report("'%s' trans must be used with cyls, heads and secs", - value); - goto fail; - } - if (!strcmp(value, "none")) { - translation = BIOS_ATA_TRANSLATION_NONE; - } else if (!strcmp(value, "lba")) { - translation = BIOS_ATA_TRANSLATION_LBA; - } else if (!strcmp(value, "large")) { - translation = BIOS_ATA_TRANSLATION_LARGE; - } else if (!strcmp(value, "rechs")) { - translation = BIOS_ATA_TRANSLATION_RECHS; - } else if (!strcmp(value, "auto")) { - translation = BIOS_ATA_TRANSLATION_AUTO; - } else { - error_report("'%s' invalid translation type", value); - goto fail; - } - } - - if (media == MEDIA_CDROM) { - if (cyls || secs || heads) { - error_report("CHS can't be set with media=cdrom"); - goto fail; - } - } - /* Device address specified by bus/unit or index. * If none was specified, try to find the first free one. */ bus_id = qemu_opt_get_number(legacy_opts, "bus", 0); @@ -998,7 +911,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) if (index != -1) { if (bus_id != 0 || unit_id != -1) { - error_report("index cannot be used with bus and unit"); + error_setg(errp, "index cannot be used with bus and unit"); goto fail; } bus_id = drive_index_to_bus_id(type, index); @@ -1017,22 +930,19 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } if (max_devs && unit_id >= max_devs) { - error_report("unit %d too big (max is %d)", unit_id, max_devs - 1); + error_setg(errp, "unit %d too big (max is %d)", unit_id, max_devs - 1); goto fail; } if (drive_get(type, bus_id, unit_id) != NULL) { - error_report("drive with bus=%d, unit=%d (index=%d) exists", - bus_id, unit_id, index); + error_setg(errp, "drive with bus=%d, unit=%d (index=%d) exists", + bus_id, unit_id, index); goto fail; } /* Locked */ locked = qemu_opt_get_bool(legacy_opts, "locked", false); - /* Serial number */ - serial = qemu_opt_get(legacy_opts, "serial"); - /* no id supplied -> create one */ if (qemu_opts_id(all_opts) == NULL) { char *new_id; @@ -1052,12 +962,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } /* Add virtio block device */ - devaddr = qemu_opt_get(legacy_opts, "addr"); - if (devaddr && type != IF_VIRTIO) { - error_report("addr is not supported by this bus type"); - goto fail; - } - if (type == IF_VIRTIO) { QemuOpts *devopts; devopts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, @@ -1069,9 +973,6 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) } qemu_opt_set(devopts, "drive", qdict_get_str(bs_opts, "id"), &error_abort); - if (devaddr) { - qemu_opt_set(devopts, "addr", devaddr, &error_abort); - } } filename = qemu_opt_get(legacy_opts, "file"); @@ -1081,7 +982,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) if (werror != NULL) { if (type != IF_IDE && type != IF_SCSI && type != IF_VIRTIO && type != IF_NONE) { - error_report("werror is not supported by this bus type"); + error_setg(errp, "werror is not supported by this bus type"); goto fail; } qdict_put_str(bs_opts, "werror", werror); @@ -1091,7 +992,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) if (rerror != NULL) { if (type != IF_IDE && type != IF_VIRTIO && type != IF_SCSI && type != IF_NONE) { - error_report("rerror is not supported by this bus type"); + error_setg(errp, "rerror is not supported by this bus type"); goto fail; } qdict_put_str(bs_opts, "rerror", rerror); @@ -1101,9 +1002,7 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) blk = blockdev_init(filename, bs_opts, &local_err); bs_opts = NULL; if (!blk) { - if (local_err) { - error_report_err(local_err); - } + error_propagate(errp, local_err); goto fail; } else { assert(!local_err); @@ -1113,16 +1012,9 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) dinfo = g_malloc0(sizeof(*dinfo)); dinfo->opts = all_opts; - dinfo->cyls = cyls; - dinfo->heads = heads; - dinfo->secs = secs; - dinfo->trans = translation; - dinfo->type = type; dinfo->bus = bus_id; dinfo->unit = unit_id; - dinfo->devaddr = devaddr; - dinfo->serial = g_strdup(serial); dinfo->locked = locked; blk_set_legacy_dinfo(blk, dinfo); @@ -1374,7 +1266,6 @@ out_aio_context: * @node: The name of the BDS node to search for bitmaps * @name: The name of the bitmap to search for * @pbs: Output pointer for BDS lookup, if desired. Can be NULL. - * @paio: Output pointer for aio_context acquisition, if desired. Can be NULL. * @errp: Output pointer for error information. Can be NULL. * * @return: A bitmap object on success, or NULL on failure. @@ -1458,7 +1349,7 @@ struct BlkActionState { const BlkActionOps *ops; JobTxn *block_job_txn; TransactionProperties *txn_props; - QSIMPLEQ_ENTRY(BlkActionState) entry; + QTAILQ_ENTRY(BlkActionState) entry; }; /* internal snapshot private data */ @@ -1746,6 +1637,7 @@ static void external_snapshot_prepare(BlkActionState *common, error_setg_errno(errp, -size, "bdrv_getlength failed"); goto out; } + bdrv_refresh_filename(state->old_bs); bdrv_img_create(new_image_file, format, state->old_bs->filename, state->old_bs->drv->format_name, @@ -1757,7 +1649,7 @@ static void external_snapshot_prepare(BlkActionState *common, } options = qdict_new(); - if (s->has_snapshot_node_name) { + if (snapshot_node_name) { qdict_put_str(options, "node-name", snapshot_node_name); } qdict_put_str(options, "driver", format); @@ -1820,8 +1712,7 @@ static void external_snapshot_commit(BlkActionState *common) * 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 */ if (!atomic_read(&state->old_bs->copy_on_read)) { - bdrv_reopen(state->old_bs, state->old_bs->open_flags & ~BDRV_O_RDWR, - NULL); + bdrv_reopen_set_read_only(state->old_bs, true, NULL); } aio_context_release(aio_context); @@ -2083,7 +1974,7 @@ static void block_dirty_bitmap_add_prepare(BlkActionState *common, action->has_granularity, action->granularity, action->has_persistent, action->persistent, action->has_autoload, action->autoload, - action->has_x_disabled, action->x_disabled, + action->has_disabled, action->disabled, &local_err); if (!local_err) { @@ -2128,34 +2019,24 @@ static void block_dirty_bitmap_clear_prepare(BlkActionState *common, return; } - if (bdrv_dirty_bitmap_frozen(state->bitmap)) { - error_setg(errp, "Cannot modify a frozen bitmap"); - return; - } else if (bdrv_dirty_bitmap_qmp_locked(state->bitmap)) { - error_setg(errp, "Cannot modify a locked bitmap"); - return; - } else if (!bdrv_dirty_bitmap_enabled(state->bitmap)) { - error_setg(errp, "Cannot clear a disabled bitmap"); - return; - } else if (bdrv_dirty_bitmap_readonly(state->bitmap)) { - error_setg(errp, "Cannot clear a readonly bitmap"); + if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_DEFAULT, errp)) { return; } bdrv_clear_dirty_bitmap(state->bitmap, &state->backup); } -static void block_dirty_bitmap_clear_abort(BlkActionState *common) +static void block_dirty_bitmap_restore(BlkActionState *common) { BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, common, common); if (state->backup) { - bdrv_undo_clear_dirty_bitmap(state->bitmap, state->backup); + bdrv_restore_dirty_bitmap(state->bitmap, state->backup); } } -static void block_dirty_bitmap_clear_commit(BlkActionState *common) +static void block_dirty_bitmap_free_backup(BlkActionState *common) { BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, common, common); @@ -2174,7 +2055,7 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, return; } - action = common->action->u.x_block_dirty_bitmap_enable.data; + action = common->action->u.block_dirty_bitmap_enable.data; state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, NULL, @@ -2183,6 +2064,10 @@ static void block_dirty_bitmap_enable_prepare(BlkActionState *common, return; } + if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); bdrv_enable_dirty_bitmap(state->bitmap); } @@ -2208,7 +2093,7 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, return; } - action = common->action->u.x_block_dirty_bitmap_disable.data; + action = common->action->u.block_dirty_bitmap_disable.data; state->bitmap = block_dirty_bitmap_lookup(action->node, action->name, NULL, @@ -2217,6 +2102,10 @@ static void block_dirty_bitmap_disable_prepare(BlkActionState *common, return; } + if (bdrv_dirty_bitmap_check(state->bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { + return; + } + state->was_enabled = bdrv_dirty_bitmap_enabled(state->bitmap); bdrv_disable_dirty_bitmap(state->bitmap); } @@ -2231,6 +2120,30 @@ static void block_dirty_bitmap_disable_abort(BlkActionState *common) } } +static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node, + const char *target, + strList *bitmaps, + HBitmap **backup, + Error **errp); + +static void block_dirty_bitmap_merge_prepare(BlkActionState *common, + Error **errp) +{ + BlockDirtyBitmapMerge *action; + BlockDirtyBitmapState *state = DO_UPCAST(BlockDirtyBitmapState, + common, common); + + if (action_check_completion_mode(common, errp) < 0) { + return; + } + + action = common->action->u.block_dirty_bitmap_merge.data; + + state->bitmap = do_block_dirty_bitmap_merge(action->node, action->target, + action->bitmaps, &state->backup, + errp); +} + static void abort_prepare(BlkActionState *common, Error **errp) { error_setg(errp, "Transaction aborted using Abort action"); @@ -2289,19 +2202,31 @@ static const BlkActionOps actions[] = { [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, + .commit = block_dirty_bitmap_free_backup, + .abort = block_dirty_bitmap_restore, }, - [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_ENABLE] = { + [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_ENABLE] = { .instance_size = sizeof(BlockDirtyBitmapState), .prepare = block_dirty_bitmap_enable_prepare, .abort = block_dirty_bitmap_enable_abort, }, - [TRANSACTION_ACTION_KIND_X_BLOCK_DIRTY_BITMAP_DISABLE] = { + [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_DISABLE] = { .instance_size = sizeof(BlockDirtyBitmapState), .prepare = block_dirty_bitmap_disable_prepare, .abort = block_dirty_bitmap_disable_abort, - } + }, + [TRANSACTION_ACTION_KIND_BLOCK_DIRTY_BITMAP_MERGE] = { + .instance_size = sizeof(BlockDirtyBitmapState), + .prepare = block_dirty_bitmap_merge_prepare, + .commit = block_dirty_bitmap_free_backup, + .abort = block_dirty_bitmap_restore, + }, + /* Where are transactions for MIRROR, COMMIT and STREAM? + * Although these blockjobs use transaction callbacks like the backup job, + * these jobs do not necessarily adhere to transaction semantics. + * These jobs may not fully undo all of their actions on abort, nor do they + * necessarily work in transactions with more than one job in them. + */ }; /** @@ -2337,8 +2262,8 @@ void qmp_transaction(TransactionActionList *dev_list, BlkActionState *state, *next; Error *local_err = NULL; - QSIMPLEQ_HEAD(snap_bdrv_states, BlkActionState) snap_bdrv_states; - QSIMPLEQ_INIT(&snap_bdrv_states); + QTAILQ_HEAD(, BlkActionState) snap_bdrv_states; + QTAILQ_INIT(&snap_bdrv_states); /* Does this transaction get canceled as a group on failure? * If not, we don't really need to make a JobTxn. @@ -2369,7 +2294,7 @@ void qmp_transaction(TransactionActionList *dev_list, state->action = dev_info; state->block_job_txn = block_job_txn; state->txn_props = props; - QSIMPLEQ_INSERT_TAIL(&snap_bdrv_states, state, entry); + QTAILQ_INSERT_TAIL(&snap_bdrv_states, state, entry); state->ops->prepare(state, &local_err); if (local_err) { @@ -2378,7 +2303,7 @@ void qmp_transaction(TransactionActionList *dev_list, } } - QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { + QTAILQ_FOREACH(state, &snap_bdrv_states, entry) { if (state->ops->commit) { state->ops->commit(state); } @@ -2389,13 +2314,13 @@ void qmp_transaction(TransactionActionList *dev_list, delete_and_fail: /* failure, and it is all-or-none; roll back all operations */ - QSIMPLEQ_FOREACH(state, &snap_bdrv_states, entry) { + QTAILQ_FOREACH_REVERSE(state, &snap_bdrv_states, entry) { if (state->ops->abort) { state->ops->abort(state); } } exit: - QSIMPLEQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) { + QTAILQ_FOREACH_SAFE(state, &snap_bdrv_states, entry, next) { if (state->ops->clean) { state->ops->clean(state); } @@ -2720,7 +2645,7 @@ void qmp_blockdev_change_medium(bool has_device, const char *device, 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); + BDRV_O_PROTOCOL | BDRV_O_AUTO_RDONLY); if (!has_read_only) { read_only = BLOCKDEV_CHANGE_READ_ONLY_MODE_RETAIN; @@ -2896,6 +2821,7 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, { BlockDriverState *bs; BdrvDirtyBitmap *bitmap; + AioContext *aio_context = NULL; if (!name || name[0] == '\0') { error_setg(errp, "Bitmap name cannot be empty"); @@ -2930,22 +2856,28 @@ void qmp_block_dirty_bitmap_add(const char *node, const char *name, disabled = false; } - if (persistent && - !bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) - { - return; + if (persistent) { + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); + if (!bdrv_can_store_new_dirty_bitmap(bs, name, granularity, errp)) { + goto out; + } } bitmap = bdrv_create_dirty_bitmap(bs, granularity, name, errp); if (bitmap == NULL) { - return; + goto out; } if (disabled) { bdrv_disable_dirty_bitmap(bitmap); } - bdrv_dirty_bitmap_set_persistance(bitmap, persistent); + bdrv_dirty_bitmap_set_persistence(bitmap, persistent); + out: + if (aio_context) { + aio_context_release(aio_context); + } } void qmp_block_dirty_bitmap_remove(const char *node, const char *name, @@ -2954,33 +2886,33 @@ void qmp_block_dirty_bitmap_remove(const char *node, const char *name, BlockDriverState *bs; BdrvDirtyBitmap *bitmap; Error *local_err = NULL; + AioContext *aio_context = NULL; bitmap = block_dirty_bitmap_lookup(node, name, &bs, errp); if (!bitmap || !bs) { return; } - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently frozen and cannot be removed", - name); - return; - } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently locked and cannot be removed", - name); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_BUSY | BDRV_BITMAP_RO, + errp)) { return; } - if (bdrv_dirty_bitmap_get_persistance(bitmap)) { + if (bdrv_dirty_bitmap_get_persistence(bitmap)) { + aio_context = bdrv_get_aio_context(bs); + aio_context_acquire(aio_context); bdrv_remove_persistent_dirty_bitmap(bs, name, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); - return; + goto out; } } bdrv_release_dirty_bitmap(bs, bitmap); + out: + if (aio_context) { + aio_context_release(aio_context); + } } /** @@ -2998,30 +2930,14 @@ void qmp_block_dirty_bitmap_clear(const char *node, const char *name, return; } - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently frozen and cannot be modified", - name); - return; - } else if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently locked and cannot be modified", - name); - return; - } else if (!bdrv_dirty_bitmap_enabled(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently disabled and cannot be cleared", - name); - return; - } else if (bdrv_dirty_bitmap_readonly(bitmap)) { - error_setg(errp, "Bitmap '%s' is readonly and cannot be cleared", name); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, errp)) { return; } bdrv_clear_dirty_bitmap(bitmap, NULL); } -void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, +void qmp_block_dirty_bitmap_enable(const char *node, const char *name, Error **errp) { BlockDriverState *bs; @@ -3032,17 +2948,14 @@ void qmp_x_block_dirty_bitmap_enable(const char *node, const char *name, return; } - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently frozen and cannot be enabled", - name); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { return; } bdrv_enable_dirty_bitmap(bitmap); } -void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, +void qmp_block_dirty_bitmap_disable(const char *node, const char *name, Error **errp) { BlockDriverState *bs; @@ -3053,44 +2966,63 @@ void qmp_x_block_dirty_bitmap_disable(const char *node, const char *name, return; } - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_setg(errp, - "Bitmap '%s' is currently frozen and cannot be disabled", - name); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_ALLOW_RO, errp)) { return; } bdrv_disable_dirty_bitmap(bitmap); } -void qmp_x_block_dirty_bitmap_merge(const char *node, const char *dst_name, - const char *src_name, Error **errp) +static BdrvDirtyBitmap *do_block_dirty_bitmap_merge(const char *node, + const char *target, + strList *bitmaps, + HBitmap **backup, + Error **errp) { BlockDriverState *bs; - BdrvDirtyBitmap *dst, *src; + BdrvDirtyBitmap *dst, *src, *anon; + strList *lst; + Error *local_err = NULL; - dst = block_dirty_bitmap_lookup(node, dst_name, &bs, errp); + dst = block_dirty_bitmap_lookup(node, target, &bs, errp); if (!dst) { - return; + return NULL; } - if (bdrv_dirty_bitmap_frozen(dst)) { - error_setg(errp, "Bitmap '%s' is frozen and cannot be modified", - dst_name); - return; - } else if (bdrv_dirty_bitmap_readonly(dst)) { - error_setg(errp, "Bitmap '%s' is readonly and cannot be modified", - dst_name); - return; + anon = bdrv_create_dirty_bitmap(bs, bdrv_dirty_bitmap_granularity(dst), + NULL, errp); + if (!anon) { + return NULL; } - src = bdrv_find_dirty_bitmap(bs, src_name); - if (!src) { - error_setg(errp, "Dirty bitmap '%s' not found", src_name); - return; + for (lst = bitmaps; lst; lst = lst->next) { + src = bdrv_find_dirty_bitmap(bs, lst->value); + if (!src) { + error_setg(errp, "Dirty bitmap '%s' not found", lst->value); + dst = NULL; + goto out; + } + + bdrv_merge_dirty_bitmap(anon, src, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + dst = NULL; + goto out; + } } - bdrv_merge_dirty_bitmap(dst, src, errp); + /* Merge into dst; dst is unchanged on failure. */ + bdrv_merge_dirty_bitmap(dst, anon, backup, errp); + + out: + bdrv_release_dirty_bitmap(bs, anon); + return dst; +} + +void qmp_block_dirty_bitmap_merge(const char *node, const char *target, + strList *bitmaps, Error **errp) +{ + do_block_dirty_bitmap_merge(node, target, bitmaps, NULL, errp); } BlockDirtyBitmapSha256 *qmp_x_debug_block_dirty_bitmap_sha256(const char *node, @@ -3235,6 +3167,8 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, bool has_backing_file, const char *backing_file, bool has_speed, int64_t speed, bool has_on_error, BlockdevOnError on_error, + bool has_auto_finalize, bool auto_finalize, + bool has_auto_dismiss, bool auto_dismiss, Error **errp) { BlockDriverState *bs, *iter; @@ -3242,6 +3176,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, AioContext *aio_context; Error *local_err = NULL; const char *base_name = NULL; + int job_flags = JOB_DEFAULT; if (!has_on_error) { on_error = BLOCKDEV_ON_ERROR_REPORT; @@ -3282,6 +3217,7 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, goto out; } assert(bdrv_get_aio_context(base_bs) == aio_context); + bdrv_refresh_filename(base_bs); base_name = base_bs->filename; } @@ -3303,8 +3239,15 @@ void qmp_block_stream(bool has_job_id, const char *job_id, const char *device, /* backing_file string overrides base bs filename */ base_name = has_backing_file ? backing_file : base_name; + if (has_auto_finalize && !auto_finalize) { + job_flags |= JOB_MANUAL_FINALIZE; + } + if (has_auto_dismiss && !auto_dismiss) { + job_flags |= JOB_MANUAL_DISMISS; + } + stream_start(has_job_id ? job_id : NULL, bs, base_bs, base_name, - has_speed ? speed : 0, on_error, &local_err); + job_flags, has_speed ? speed : 0, on_error, &local_err); if (local_err) { error_propagate(errp, local_err); goto out; @@ -3317,11 +3260,15 @@ out: } void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, + bool has_base_node, const char *base_node, bool has_base, const char *base, + bool has_top_node, const char *top_node, bool has_top, const char *top, bool has_backing_file, const char *backing_file, bool has_speed, int64_t speed, bool has_filter_node_name, const char *filter_node_name, + bool has_auto_finalize, bool auto_finalize, + bool has_auto_dismiss, bool auto_dismiss, Error **errp) { BlockDriverState *bs; @@ -3333,6 +3280,7 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, * BlockdevOnError change for blkmirror makes it in */ BlockdevOnError on_error = BLOCKDEV_ON_ERROR_REPORT; + int job_flags = JOB_DEFAULT; if (!has_speed) { speed = 0; @@ -3340,6 +3288,12 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, if (!has_filter_node_name) { filter_node_name = NULL; } + if (has_auto_finalize && !auto_finalize) { + job_flags |= JOB_MANUAL_FINALIZE; + } + if (has_auto_dismiss && !auto_dismiss) { + job_flags |= JOB_MANUAL_DISMISS; + } /* Important Note: * libvirt relies on the DeviceNotFound error class in order to probe for @@ -3369,7 +3323,24 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, /* default top_bs is the active layer */ top_bs = bs; - if (has_top && top) { + if (has_top_node && has_top) { + error_setg(errp, "'top-node' and 'top' are mutually exclusive"); + goto out; + } else if (has_top_node) { + top_bs = bdrv_lookup_bs(NULL, top_node, errp); + if (top_bs == NULL) { + goto out; + } + if (!bdrv_chain_contains(bs, top_bs)) { + error_setg(errp, "'%s' is not in this backing file chain", + top_node); + goto out; + } + } else if (has_top && top) { + /* This strcmp() is just a shortcut, there is no need to + * refresh @bs's filename. If it mismatches, + * bdrv_find_backing_image() will do the refresh and may still + * return @bs. */ if (strcmp(bs->filename, top) != 0) { top_bs = bdrv_find_backing_image(bs, top); } @@ -3382,7 +3353,20 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, assert(bdrv_get_aio_context(top_bs) == aio_context); - if (has_base && base) { + if (has_base_node && has_base) { + error_setg(errp, "'base-node' and 'base' are mutually exclusive"); + goto out; + } else if (has_base_node) { + base_bs = bdrv_lookup_bs(NULL, base_node, errp); + if (base_bs == NULL) { + goto out; + } + if (!bdrv_chain_contains(top_bs, base_bs)) { + error_setg(errp, "'%s' is not in this backing file chain", + base_node); + goto out; + } + } else if (has_base && base) { base_bs = bdrv_find_backing_image(top_bs, base); } else { base_bs = bdrv_find_base(top_bs); @@ -3414,15 +3398,15 @@ void qmp_block_commit(bool has_job_id, const char *job_id, const char *device, goto out; } commit_active_start(has_job_id ? job_id : NULL, bs, base_bs, - JOB_DEFAULT, speed, on_error, + job_flags, speed, on_error, filter_node_name, NULL, NULL, false, &local_err); } else { BlockDriverState *overlay_bs = bdrv_find_overlay(bs, top_bs); if (bdrv_op_is_blocked(overlay_bs, BLOCK_OP_TYPE_COMMIT_TARGET, errp)) { goto out; } - commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, speed, - on_error, has_backing_file ? backing_file : NULL, + commit_start(has_job_id ? job_id : NULL, bs, base_bs, top_bs, job_flags, + speed, on_error, has_backing_file ? backing_file : NULL, filter_node_name, &local_err); } if (local_err != NULL) { @@ -3517,6 +3501,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, if (backup->mode != NEW_IMAGE_MODE_EXISTING) { assert(backup->format); if (source) { + bdrv_refresh_filename(source); bdrv_img_create(backup->target, backup->format, source->filename, source->drv->format_name, NULL, size, flags, false, &local_err); @@ -3560,10 +3545,7 @@ static BlockJob *do_drive_backup(DriveBackup *backup, JobTxn *txn, bdrv_unref(target_bs); goto out; } - if (bdrv_dirty_bitmap_qmp_locked(bmap)) { - error_setg(errp, - "Bitmap '%s' is currently locked and cannot be used for " - "backup", backup->bitmap); + if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) { goto out; } } @@ -3604,12 +3586,18 @@ BlockDeviceInfoList *qmp_query_named_block_nodes(Error **errp) return bdrv_named_nodes_list(errp); } +XDbgBlockGraph *qmp_x_debug_query_block_graph(Error **errp) +{ + return bdrv_get_xdbg_block_graph(errp); +} + BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, Error **errp) { BlockDriverState *bs; BlockDriverState *target_bs; Error *local_err = NULL; + BdrvDirtyBitmap *bmap = NULL; AioContext *aio_context; BlockJob *job = NULL; int job_flags = JOB_DEFAULT; @@ -3660,6 +3648,18 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, goto out; } } + + if (backup->has_bitmap) { + bmap = bdrv_find_dirty_bitmap(bs, backup->bitmap); + if (!bmap) { + error_setg(errp, "Bitmap '%s' could not be found", backup->bitmap); + goto out; + } + if (bdrv_dirty_bitmap_check(bmap, BDRV_BITMAP_DEFAULT, errp)) { + goto out; + } + } + if (!backup->auto_finalize) { job_flags |= JOB_MANUAL_FINALIZE; } @@ -3667,7 +3667,7 @@ BlockJob *do_blockdev_backup(BlockdevBackup *backup, JobTxn *txn, job_flags |= JOB_MANUAL_DISMISS; } job = backup_job_create(backup->job_id, bs, target_bs, backup->speed, - backup->sync, NULL, backup->compress, + backup->sync, bmap, backup->compress, backup->on_source_error, backup->on_target_error, job_flags, NULL, NULL, txn, &local_err); if (local_err != NULL) { @@ -3706,8 +3706,11 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, bool has_filter_node_name, const char *filter_node_name, bool has_copy_mode, MirrorCopyMode copy_mode, + bool has_auto_finalize, bool auto_finalize, + bool has_auto_dismiss, bool auto_dismiss, Error **errp) { + int job_flags = JOB_DEFAULT; if (!has_speed) { speed = 0; @@ -3733,6 +3736,12 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, if (!has_copy_mode) { copy_mode = MIRROR_COPY_MODE_BACKGROUND; } + if (has_auto_finalize && !auto_finalize) { + job_flags |= JOB_MANUAL_FINALIZE; + } + if (has_auto_dismiss && !auto_dismiss) { + job_flags |= JOB_MANUAL_DISMISS; + } if (granularity != 0 && (granularity < 512 || granularity > 1048576 * 64)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "granularity", @@ -3756,11 +3765,44 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, sync = MIRROR_SYNC_MODE_FULL; } + if (has_replaces) { + BlockDriverState *to_replace_bs; + AioContext *replace_aio_context; + int64_t bs_size, replace_size; + + bs_size = bdrv_getlength(bs); + if (bs_size < 0) { + error_setg_errno(errp, -bs_size, "Failed to query device's size"); + return; + } + + to_replace_bs = check_to_replace_node(bs, replaces, errp); + if (!to_replace_bs) { + return; + } + + replace_aio_context = bdrv_get_aio_context(to_replace_bs); + aio_context_acquire(replace_aio_context); + replace_size = bdrv_getlength(to_replace_bs); + aio_context_release(replace_aio_context); + + if (replace_size < 0) { + error_setg_errno(errp, -replace_size, + "Failed to query the replacement node's size"); + return; + } + if (bs_size != replace_size) { + error_setg(errp, "cannot replace image with a mirror image of " + "different size"); + return; + } + } + /* 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(job_id, bs, target, - has_replaces ? replaces : NULL, + has_replaces ? replaces : NULL, job_flags, speed, granularity, buf_size, sync, backing_mode, on_source_error, on_target_error, unmap, filter_node_name, copy_mode, errp); @@ -3816,33 +3858,11 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) } if (arg->has_replaces) { - BlockDriverState *to_replace_bs; - AioContext *replace_aio_context; - int64_t replace_size; - if (!arg->has_node_name) { error_setg(errp, "a node-name must be provided when replacing a" " named node of the graph"); goto out; } - - to_replace_bs = check_to_replace_node(bs, arg->replaces, &local_err); - - if (!to_replace_bs) { - error_propagate(errp, local_err); - goto out; - } - - replace_aio_context = bdrv_get_aio_context(to_replace_bs); - aio_context_acquire(replace_aio_context); - replace_size = bdrv_getlength(to_replace_bs); - aio_context_release(replace_aio_context); - - if (size != replace_size) { - error_setg(errp, "cannot replace image with a mirror image of " - "different size"); - goto out; - } } if (arg->mode == NEW_IMAGE_MODE_ABSOLUTE_PATHS) { @@ -3867,6 +3887,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) break; case NEW_IMAGE_MODE_ABSOLUTE_PATHS: /* create new image with backing file */ + bdrv_refresh_filename(source); bdrv_img_create(arg->target, format, source->filename, source->drv->format_name, @@ -3910,6 +3931,8 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) arg->has_unmap, arg->unmap, false, NULL, arg->has_copy_mode, arg->copy_mode, + arg->has_auto_finalize, arg->auto_finalize, + arg->has_auto_dismiss, arg->auto_dismiss, &local_err); bdrv_unref(target_bs); error_propagate(errp, local_err); @@ -3931,6 +3954,8 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, bool has_filter_node_name, const char *filter_node_name, bool has_copy_mode, MirrorCopyMode copy_mode, + bool has_auto_finalize, bool auto_finalize, + bool has_auto_dismiss, bool auto_dismiss, Error **errp) { BlockDriverState *bs; @@ -3964,6 +3989,8 @@ void qmp_blockdev_mirror(bool has_job_id, const char *job_id, true, true, has_filter_node_name, filter_node_name, has_copy_mode, copy_mode, + has_auto_finalize, auto_finalize, + has_auto_dismiss, auto_dismiss, &local_err); error_propagate(errp, local_err); @@ -4115,7 +4142,6 @@ void qmp_change_backing_file(const char *device, BlockDriverState *image_bs = NULL; Error *local_err = NULL; bool ro; - int open_flags; int ret; bs = qmp_get_root_bs(device, errp); @@ -4157,13 +4183,10 @@ void qmp_change_backing_file(const char *device, } /* if not r/w, reopen to make r/w */ - open_flags = image_bs->open_flags; ro = bdrv_is_read_only(image_bs); if (ro) { - bdrv_reopen(image_bs, open_flags | BDRV_O_RDWR, &local_err); - if (local_err) { - error_propagate(errp, local_err); + if (bdrv_reopen_set_read_only(image_bs, false, errp) != 0) { goto out; } } @@ -4179,7 +4202,7 @@ void qmp_change_backing_file(const char *device, } if (ro) { - bdrv_reopen(image_bs, open_flags, &local_err); + bdrv_reopen_set_read_only(image_bs, true, &local_err); error_propagate(errp, local_err); } @@ -4253,6 +4276,53 @@ fail: visit_free(v); } +void qmp_x_blockdev_reopen(BlockdevOptions *options, Error **errp) +{ + BlockDriverState *bs; + AioContext *ctx; + QObject *obj; + Visitor *v = qobject_output_visitor_new(&obj); + Error *local_err = NULL; + BlockReopenQueue *queue; + QDict *qdict; + + /* Check for the selected node name */ + if (!options->has_node_name) { + error_setg(errp, "Node name not specified"); + goto fail; + } + + bs = bdrv_find_node(options->node_name); + if (!bs) { + error_setg(errp, "Cannot find node named '%s'", options->node_name); + goto fail; + } + + /* Put all options in a QDict and flatten it */ + visit_type_BlockdevOptions(v, NULL, &options, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + visit_complete(v, &obj); + qdict = qobject_to(QDict, obj); + + qdict_flatten(qdict); + + /* Perform the reopen operation */ + ctx = bdrv_get_aio_context(bs); + aio_context_acquire(ctx); + bdrv_subtree_drained_begin(bs); + queue = bdrv_reopen_queue(NULL, bs, qdict, false); + bdrv_reopen_multiple(queue, errp); + bdrv_subtree_drained_end(bs); + aio_context_release(ctx); + +fail: + visit_free(v); +} + void qmp_blockdev_del(const char *node_name, Error **errp) { AioContext *aio_context; @@ -4274,7 +4344,7 @@ void qmp_blockdev_del(const char *node_name, Error **errp) goto out; } - if (!bs->monitor_list.tqe_prev) { + if (!QTAILQ_IN_USE(bs, monitor_list)) { error_setg(errp, "Node %s is not owned by the monitor", bs->node_name); goto out; @@ -4418,21 +4488,22 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, aio_context_release(old_context); } -void qmp_x_block_latency_histogram_set( - const char *device, +void qmp_block_latency_histogram_set( + const char *id, bool has_boundaries, uint64List *boundaries, bool has_boundaries_read, uint64List *boundaries_read, bool has_boundaries_write, uint64List *boundaries_write, bool has_boundaries_flush, uint64List *boundaries_flush, Error **errp) { - BlockBackend *blk = blk_by_name(device); + BlockBackend *blk = qmp_get_blk(NULL, id, errp); BlockAcctStats *stats; + int ret; if (!blk) { - error_setg(errp, "Device '%s' not found", device); return; } + stats = blk_get_stats(blk); if (!has_boundaries && !has_boundaries_read && !has_boundaries_write && @@ -4443,21 +4514,33 @@ void qmp_x_block_latency_histogram_set( } if (has_boundaries || has_boundaries_read) { - block_latency_histogram_set( + ret = block_latency_histogram_set( stats, BLOCK_ACCT_READ, has_boundaries_read ? boundaries_read : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set read boundaries fail", id); + return; + } } if (has_boundaries || has_boundaries_write) { - block_latency_histogram_set( + ret = block_latency_histogram_set( stats, BLOCK_ACCT_WRITE, has_boundaries_write ? boundaries_write : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set write boundaries fail", id); + return; + } } if (has_boundaries || has_boundaries_flush) { - block_latency_histogram_set( + ret = block_latency_histogram_set( stats, BLOCK_ACCT_FLUSH, has_boundaries_flush ? boundaries_flush : boundaries); + if (ret) { + error_setg(errp, "Device '%s' set flush boundaries fail", id); + return; + } } } diff --git a/blockjob.c b/blockjob.c index be5903aa96..730101d282 100644 --- a/blockjob.c +++ b/blockjob.c @@ -164,7 +164,7 @@ static bool child_job_drained_poll(BdrvChild *c) /* An inactive or completed job doesn't have any pending requests. Jobs * with !job->busy are either already paused or have a pause point after * being reentered, so no job driver code will run before they pause. */ - if (!job->busy || job_is_completed(job) || job->deferred_to_main_loop) { + if (!job->busy || job_is_completed(job)) { return false; } @@ -221,6 +221,11 @@ int block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, return 0; } +static void block_job_on_idle(Notifier *n, void *opaque) +{ + aio_wait_kick(); +} + bool block_job_is_internal(BlockJob *job) { return (job->job.id == NULL); @@ -315,8 +320,7 @@ static void block_job_event_cancelled(Notifier *n, void *opaque) job->job.id, job->job.progress_total, job->job.progress_current, - job->speed, - &error_abort); + job->speed); } static void block_job_event_completed(Notifier *n, void *opaque) @@ -338,8 +342,7 @@ static void block_job_event_completed(Notifier *n, void *opaque) job->job.progress_current, job->speed, !!msg, - msg, - &error_abort); + msg); } static void block_job_event_pending(Notifier *n, void *opaque) @@ -351,8 +354,7 @@ static void block_job_event_pending(Notifier *n, void *opaque) } qapi_event_send_block_job_pending(job_type(&job->job), - job->job.id, - &error_abort); + job->job.id); } static void block_job_event_ready(Notifier *n, void *opaque) @@ -367,7 +369,7 @@ static void block_job_event_ready(Notifier *n, void *opaque) job->job.id, job->job.progress_total, job->job.progress_current, - job->speed, &error_abort); + job->speed); } @@ -419,6 +421,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, job->finalize_completed_notifier.notify = block_job_event_completed; job->pending_notifier.notify = block_job_event_pending; job->ready_notifier.notify = block_job_event_ready; + job->idle_notifier.notify = block_job_on_idle; notifier_list_add(&job->job.on_finalize_cancelled, &job->finalize_cancelled_notifier); @@ -426,6 +429,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, &job->finalize_completed_notifier); notifier_list_add(&job->job.on_pending, &job->pending_notifier); notifier_list_add(&job->job.on_ready, &job->ready_notifier); + notifier_list_add(&job->job.on_idle, &job->idle_notifier); error_setg(&job->blocker, "block device is in use by block job: %s", job_type_str(&job->job)); @@ -494,12 +498,14 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockdevOnError on_err, qapi_event_send_block_job_error(job->job.id, is_read ? IO_OPERATION_TYPE_READ : IO_OPERATION_TYPE_WRITE, - action, &error_abort); + action); } if (action == BLOCK_ERROR_ACTION_STOP) { - job_pause(&job->job); - /* make the pause user visible, which will be resumed from QMP. */ - job->job.user_paused = true; + if (!job->job.user_paused) { + job_pause(&job->job); + /* make the pause user visible, which will be resumed from QMP. */ + job->job.user_paused = true; + } block_job_iostatus_set_err(job, error); } return action; diff --git a/bootdevice.c b/bootdevice.c index 1141009114..1d225202f9 100644 --- a/bootdevice.c +++ b/bootdevice.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "sysemu/reset.h" #include "hw/qdev-core.h" +#include "hw/boards.h" typedef struct FWBootEntry FWBootEntry; @@ -208,11 +209,13 @@ DeviceState *get_boot_device(uint32_t position) * memory pointed by "size" is assigned total length of the array in bytes * */ -char *get_boot_devices_list(size_t *size, bool ignore_suffixes) +char *get_boot_devices_list(size_t *size) { FWBootEntry *i; size_t total = 0; char *list = NULL; + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + bool ignore_suffixes = mc->ignore_boot_device_suffixes; QTAILQ_FOREACH(i, &fw_boot_order, link) { char *devpath = NULL, *suffix = NULL; diff --git a/bsd-user/elfload.c b/bsd-user/elfload.c index 7cccf3eb8b..32378af7b2 100644 --- a/bsd-user/elfload.c +++ b/bsd-user/elfload.c @@ -1367,7 +1367,7 @@ int load_elf_binary(struct linux_binprm * bprm, struct target_pt_regs * regs, if (!have_guest_base) { /* * Go through ELF program header table and find out whether - * any of the segments drop below our current mmap_min_addr and + * any of the segments drop below our current mmap_min_addr and * in that case set guest_base to corresponding address. */ for (i = 0, elf_ppnt = elf_phdata; i < elf_ex.e_phnum; diff --git a/bsd-user/x86_64/target_syscall.h b/bsd-user/x86_64/target_syscall.h index 211ce29e90..a5d779884f 100644 --- a/bsd-user/x86_64/target_syscall.h +++ b/bsd-user/x86_64/target_syscall.h @@ -12,7 +12,7 @@ struct target_pt_regs { abi_ulong rbp; abi_ulong rbx; /* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; + abi_ulong r11; abi_ulong r10; abi_ulong r9; abi_ulong r8; diff --git a/chardev/char-fe.c b/chardev/char-fe.c index b1f228e8b5..f3530a90e6 100644 --- a/chardev/char-fe.c +++ b/chardev/char-fe.c @@ -56,7 +56,7 @@ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len) int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) { Chardev *s = be->chr; - int offset = 0, counter = 10; + int offset = 0; int res; if (!s || !CHARDEV_GET_CLASS(s)->chr_sync_read) { @@ -88,10 +88,6 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len) } offset += res; - - if (!counter--) { - break; - } } if (qemu_chr_replay(s) && replay_mode == REPLAY_MODE_RECORD) { @@ -239,20 +235,26 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del) d->backends[b->tag] = NULL; } if (del) { - object_unparent(OBJECT(b->chr)); + Object *obj = OBJECT(b->chr); + if (obj->parent) { + object_unparent(obj); + } else { + object_unref(obj); + } } b->chr = NULL; } } -void qemu_chr_fe_set_handlers(CharBackend *b, - IOCanReadHandler *fd_can_read, - IOReadHandler *fd_read, - IOEventHandler *fd_event, - BackendChangeHandler *be_change, - void *opaque, - GMainContext *context, - bool set_open) +void qemu_chr_fe_set_handlers_full(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + BackendChangeHandler *be_change, + void *opaque, + GMainContext *context, + bool set_open, + bool sync_state) { Chardev *s; int fe_open; @@ -284,14 +286,24 @@ void qemu_chr_fe_set_handlers(CharBackend *b, qemu_chr_fe_take_focus(b); /* We're connecting to an already opened device, so let's make sure we also get the open event */ - if (s->be_open) { + if (sync_state && s->be_open) { qemu_chr_be_event(s, CHR_EVENT_OPENED); } } +} - if (CHARDEV_IS_MUX(s)) { - mux_chr_set_handlers(s, context); - } +void qemu_chr_fe_set_handlers(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + BackendChangeHandler *be_change, + void *opaque, + GMainContext *context, + bool set_open) +{ + qemu_chr_fe_set_handlers_full(b, fd_can_read, fd_read, fd_event, be_change, + opaque, context, set_open, + true); } void qemu_chr_fe_take_focus(CharBackend *b) diff --git a/chardev/char-io.c b/chardev/char-io.c index f81052481a..8ced184160 100644 --- a/chardev/char-io.c +++ b/chardev/char-io.c @@ -33,7 +33,6 @@ typedef struct IOWatchPoll { IOCanReadHandler *fd_can_read; GSourceFunc fd_read; void *opaque; - GMainContext *context; } IOWatchPoll; static IOWatchPoll *io_watch_poll_from_source(GSource *source) @@ -55,47 +54,24 @@ static gboolean io_watch_poll_prepare(GSource *source, 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, iwp->context); - } else { - g_source_destroy(iwp->src); + g_source_add_child_source(source, iwp->src); g_source_unref(iwp->src); + } else { + g_source_remove_child_source(source, iwp->src); iwp->src = NULL; } return FALSE; } -static gboolean io_watch_poll_check(GSource *source) -{ - return FALSE; -} - static gboolean io_watch_poll_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { - abort(); -} - -static void io_watch_poll_finalize(GSource *source) -{ - /* Due to a glib bug, removing the last reference to a source - * inside a finalize callback causes recursive locking (and a - * deadlock). This is not a problem inside other callbacks, - * including dispatch callbacks, so we call io_remove_watch_poll - * to remove this source. At this point, iwp->src must - * be NULL, or we would leak it. - * - * This would be solved much more elegantly by child sources, - * but we support older glib versions that do not have them. - */ - IOWatchPoll *iwp = io_watch_poll_from_source(source); - assert(iwp->src == NULL); + return G_SOURCE_CONTINUE; } static GSourceFuncs io_watch_poll_funcs = { .prepare = io_watch_poll_prepare, - .check = io_watch_poll_check, .dispatch = io_watch_poll_dispatch, - .finalize = io_watch_poll_finalize, }; GSource *io_add_watch_poll(Chardev *chr, @@ -115,7 +91,6 @@ GSource *io_add_watch_poll(Chardev *chr, iwp->ioc = ioc; iwp->fd_read = (GSourceFunc) fd_read; iwp->src = NULL; - iwp->context = context; name = g_strdup_printf("chardev-iowatch-%s", chr->label); g_source_set_name((GSource *)iwp, name); @@ -126,23 +101,10 @@ GSource *io_add_watch_poll(Chardev *chr, return (GSource *)iwp; } -static void io_remove_watch_poll(GSource *source) -{ - IOWatchPoll *iwp; - - iwp = io_watch_poll_from_source(source); - if (iwp->src) { - g_source_destroy(iwp->src); - g_source_unref(iwp->src); - iwp->src = NULL; - } - g_source_destroy(&iwp->parent); -} - void remove_fd_in_watch(Chardev *chr) { if (chr->gsource) { - io_remove_watch_poll(chr->gsource); + g_source_destroy(chr->gsource); chr->gsource = NULL; } } diff --git a/chardev/char-mux.c b/chardev/char-mux.c index 6055e76293..23aa82125d 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -278,18 +278,18 @@ static void char_mux_finalize(Object *obj) qemu_chr_fe_deinit(&d->chr, false); } -void mux_chr_set_handlers(Chardev *chr, GMainContext *context) +static void mux_chr_update_read_handlers(Chardev *chr) { MuxChardev *d = MUX_CHARDEV(chr); /* Fix up the real driver with mux routines */ - qemu_chr_fe_set_handlers(&d->chr, - mux_chr_can_read, - mux_chr_read, - mux_chr_event, - NULL, - chr, - context, true); + qemu_chr_fe_set_handlers_full(&d->chr, + mux_chr_can_read, + mux_chr_read, + mux_chr_event, + NULL, + chr, + chr->gcontext, true, false); } void mux_set_focus(Chardev *chr, int focus) @@ -367,7 +367,7 @@ static int open_muxes(Chardev *chr) * mark mux as OPENED so any new FEs will immediately receive * OPENED event */ - qemu_chr_be_event(chr, CHR_EVENT_OPENED); + chr->be_open = 1; return 0; } @@ -383,6 +383,7 @@ static void char_mux_class_init(ObjectClass *oc, void *data) cc->chr_add_watch = mux_chr_add_watch; cc->chr_be_event = mux_chr_be_event; cc->chr_machine_done = open_muxes; + cc->chr_update_read_handler = mux_chr_update_read_handlers; } static const TypeInfo char_mux_type_info = { diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 68fd4e20c3..b034332edd 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -31,24 +31,17 @@ #include "chardev/char-io.h" -#if defined(__linux__) || defined(__sun__) || defined(__FreeBSD__) \ - || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) \ - || defined(__GLIBC__) - typedef struct { Chardev parent; QIOChannel *ioc; int read_bytes; - /* Protected by the Chardev chr_write_lock. */ int connected; GSource *timer_src; - GSource *open_source; } PtyChardev; #define PTY_CHARDEV(obj) OBJECT_CHECK(PtyChardev, (obj), TYPE_CHARDEV_PTY) -static void pty_chr_update_read_handler_locked(Chardev *chr); static void pty_chr_state(Chardev *chr, int connected); static void pty_chr_timer_cancel(PtyChardev *s) @@ -60,32 +53,19 @@ static void pty_chr_timer_cancel(PtyChardev *s) } } -static void pty_chr_open_src_cancel(PtyChardev *s) -{ - if (s->open_source) { - g_source_destroy(s->open_source); - g_source_unref(s->open_source); - s->open_source = NULL; - } -} - static gboolean pty_chr_timer(gpointer opaque) { struct Chardev *chr = CHARDEV(opaque); PtyChardev *s = PTY_CHARDEV(opaque); - qemu_mutex_lock(&chr->chr_write_lock); pty_chr_timer_cancel(s); - pty_chr_open_src_cancel(s); if (!s->connected) { /* Next poll ... */ - pty_chr_update_read_handler_locked(chr); + qemu_chr_be_update_read_handlers(chr, chr->gcontext); } - qemu_mutex_unlock(&chr->chr_write_lock); return FALSE; } -/* Called with chr_write_lock held. */ static void pty_chr_rearm_timer(Chardev *chr, int ms) { PtyChardev *s = PTY_CHARDEV(chr); @@ -98,8 +78,7 @@ static void pty_chr_rearm_timer(Chardev *chr, int ms) g_free(name); } -/* Called with chr_write_lock held. */ -static void pty_chr_update_read_handler_locked(Chardev *chr) +static void pty_chr_update_read_handler(Chardev *chr) { PtyChardev *s = PTY_CHARDEV(chr); GPollFD pfd; @@ -121,24 +100,12 @@ static void pty_chr_update_read_handler_locked(Chardev *chr) } } -static void pty_chr_update_read_handler(Chardev *chr) -{ - qemu_mutex_lock(&chr->chr_write_lock); - pty_chr_update_read_handler_locked(chr); - qemu_mutex_unlock(&chr->chr_write_lock); -} - -/* Called with chr_write_lock held. */ static int char_pty_chr_write(Chardev *chr, const uint8_t *buf, int len) { PtyChardev *s = PTY_CHARDEV(chr); if (!s->connected) { - /* guest sends data, check for (re-)connect */ - pty_chr_update_read_handler_locked(chr); - if (!s->connected) { - return len; - } + return len; } return io_channel_send(s->ioc, buf, len); } @@ -187,23 +154,11 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) return TRUE; } -static gboolean qemu_chr_be_generic_open_func(gpointer opaque) -{ - Chardev *chr = CHARDEV(opaque); - PtyChardev *s = PTY_CHARDEV(opaque); - - s->open_source = NULL; - qemu_chr_be_event(chr, CHR_EVENT_OPENED); - return FALSE; -} - -/* Called with chr_write_lock held. */ static void pty_chr_state(Chardev *chr, int connected) { PtyChardev *s = PTY_CHARDEV(chr); if (!connected) { - pty_chr_open_src_cancel(s); remove_fd_in_watch(chr); s->connected = 0; /* (re-)connect poll interval for idle guests: once per second. @@ -213,13 +168,8 @@ static void pty_chr_state(Chardev *chr, int connected) } else { pty_chr_timer_cancel(s); if (!s->connected) { - g_assert(s->open_source == NULL); - s->open_source = g_idle_source_new(); s->connected = 1; - g_source_set_callback(s->open_source, - qemu_chr_be_generic_open_func, - chr, NULL); - g_source_attach(s->open_source, chr->gcontext); + qemu_chr_be_event(chr, CHR_EVENT_OPENED); } if (!chr->gsource) { chr->gsource = io_add_watch_poll(chr, s->ioc, @@ -235,11 +185,9 @@ static void char_pty_finalize(Object *obj) Chardev *chr = CHARDEV(obj); PtyChardev *s = PTY_CHARDEV(obj); - qemu_mutex_lock(&chr->chr_write_lock); pty_chr_state(chr, 0); object_unref(OBJECT(s->ioc)); pty_chr_timer_cancel(s); - qemu_mutex_unlock(&chr->chr_write_lock); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } @@ -263,7 +211,7 @@ static void char_pty_open(Chardev *chr, qemu_set_nonblock(master_fd); chr->filename = g_strdup_printf("pty:%s", pty_name); - error_report("char device redirected to %s (label %s)", + error_printf("char device redirected to %s (label %s)\n", pty_name, chr->label); s = PTY_CHARDEV(chr); @@ -299,5 +247,3 @@ static void register_types(void) } type_init(register_types); - -#endif diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 3299b46853..a8bae31b8d 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -57,7 +57,7 @@ static void qmp_chardev_open_serial(Chardev *chr, static void tty_serial_init(int fd, int speed, int parity, int data_bits, int stop_bits) { - struct termios tty; + struct termios tty = {0}; speed_t spd; #if 0 diff --git a/chardev/char-socket.c b/chardev/char-socket.c index efbad6ee7c..b2cf593107 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -26,13 +26,13 @@ #include "chardev/char.h" #include "io/channel-socket.h" #include "io/channel-tls.h" +#include "io/channel-websock.h" #include "io/net-listener.h" #include "qemu/error-report.h" #include "qemu/option.h" #include "qapi/error.h" #include "qapi/clone-visitor.h" #include "qapi/qapi-visit-sockets.h" -#include "sysemu/sysemu.h" #include "chardev/char-io.h" @@ -46,6 +46,12 @@ typedef struct { size_t buflen; } TCPChardevTelnetInit; +typedef enum { + TCP_CHARDEV_STATE_DISCONNECTED, + TCP_CHARDEV_STATE_CONNECTING, + TCP_CHARDEV_STATE_CONNECTED, +} TCPChardevState; + typedef struct { Chardev parent; QIOChannel *ioc; /* Client I/O channel */ @@ -53,7 +59,8 @@ typedef struct { QIONetListener *listener; GSource *hup_source; QCryptoTLSCreds *tls_creds; - int connected; + char *tls_authz; + TCPChardevState state; int max_size; int do_telnetopt; int do_nodelay; @@ -69,9 +76,13 @@ typedef struct { GSource *telnet_source; TCPChardevTelnetInit *telnet_init; + bool is_websock; + GSource *reconnect_timer; int64_t reconnect_time; bool connect_err_reported; + + QIOTask *connect_task; } SocketChardev; #define SOCKET_CHARDEV(obj) \ @@ -80,6 +91,21 @@ typedef struct { static gboolean socket_reconnect_timeout(gpointer opaque); static void tcp_chr_telnet_init(Chardev *chr); +static void tcp_chr_change_state(SocketChardev *s, TCPChardevState state) +{ + switch (state) { + case TCP_CHARDEV_STATE_DISCONNECTED: + break; + case TCP_CHARDEV_STATE_CONNECTING: + assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED); + break; + case TCP_CHARDEV_STATE_CONNECTED: + assert(s->state == TCP_CHARDEV_STATE_CONNECTING); + break; + } + s->state = state; +} + static void tcp_chr_reconn_timer_cancel(SocketChardev *s) { if (s->reconnect_timer) { @@ -94,7 +120,8 @@ static void qemu_chr_socket_restart_timer(Chardev *chr) SocketChardev *s = SOCKET_CHARDEV(chr); char *name; - assert(s->connected == 0); + assert(s->state == TCP_CHARDEV_STATE_DISCONNECTED); + assert(!s->reconnect_timer); name = g_strdup_printf("chardev-socket-reconnect-%s", chr->label); s->reconnect_timer = qemu_chr_timeout_add_ms(chr, s->reconnect_time * 1000, @@ -129,7 +156,7 @@ static int tcp_chr_write(Chardev *chr, const uint8_t *buf, int len) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (s->connected) { + if (s->state == TCP_CHARDEV_STATE_CONNECTED) { int ret = io_channel_send_full(s->ioc, buf, len, s->write_msgfds, s->write_msgfds_num); @@ -162,7 +189,7 @@ static int tcp_chr_read_poll(void *opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); - if (!s->connected) { + if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return 0; } s->max_size = qemu_chr_be_can_write(chr); @@ -275,7 +302,7 @@ static int tcp_set_msgfds(Chardev *chr, int *fds, int num) s->write_msgfds = NULL; s->write_msgfds_num = 0; - if (!s->connected || + if ((s->state != TCP_CHARDEV_STATE_CONNECTED) || !qio_channel_has_feature(s->ioc, QIO_CHANNEL_FEATURE_FD_PASS)) { return -1; @@ -354,6 +381,15 @@ static GSource *tcp_chr_add_watch(Chardev *chr, GIOCondition cond) return qio_channel_create_watch(s->ioc, cond); } +static void remove_hup_source(SocketChardev *s) +{ + if (s->hup_source != NULL) { + g_source_destroy(s->hup_source); + g_source_unref(s->hup_source); + s->hup_source = NULL; + } +} + static void tcp_chr_free_connection(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); @@ -368,11 +404,7 @@ static void tcp_chr_free_connection(Chardev *chr) s->read_msgfds_num = 0; } - if (s->hup_source != NULL) { - g_source_destroy(s->hup_source); - g_source_unref(s->hup_source); - s->hup_source = NULL; - } + remove_hup_source(s); tcp_set_msgfds(chr, NULL, 0); remove_fd_in_watch(chr); @@ -382,33 +414,40 @@ static void tcp_chr_free_connection(Chardev *chr) s->ioc = NULL; g_free(chr->filename); chr->filename = NULL; - s->connected = 0; + tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); } -static char *SocketAddress_to_str(const char *prefix, SocketAddress *addr, - bool is_listen, bool is_telnet) +static const char *qemu_chr_socket_protocol(SocketChardev *s) { - switch (addr->type) { + if (s->is_telnet) { + return "telnet"; + } + return s->is_websock ? "websocket" : "tcp"; +} + +static char *qemu_chr_socket_address(SocketChardev *s, const char *prefix) +{ + switch (s->addr->type) { case SOCKET_ADDRESS_TYPE_INET: return g_strdup_printf("%s%s:%s:%s%s", prefix, - is_telnet ? "telnet" : "tcp", - addr->u.inet.host, - addr->u.inet.port, - is_listen ? ",server" : ""); + qemu_chr_socket_protocol(s), + s->addr->u.inet.host, + s->addr->u.inet.port, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_UNIX: return g_strdup_printf("%sunix:%s%s", prefix, - addr->u.q_unix.path, - is_listen ? ",server" : ""); + s->addr->u.q_unix.path, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("%sfd:%s%s", prefix, addr->u.fd.str, - is_listen ? ",server" : ""); + return g_strdup_printf("%sfd:%s%s", prefix, s->addr->u.fd.str, + s->is_listen ? ",server" : ""); break; case SOCKET_ADDRESS_TYPE_VSOCK: return g_strdup_printf("%svsock:%s:%s", prefix, - addr->u.vsock.cid, - addr->u.vsock.port); + s->addr->u.vsock.cid, + s->addr->u.vsock.port); default: abort(); } @@ -419,18 +458,21 @@ static void update_disconnected_filename(SocketChardev *s) Chardev *chr = CHARDEV(s); g_free(chr->filename); - chr->filename = SocketAddress_to_str("disconnected:", s->addr, - s->is_listen, s->is_telnet); + if (s->addr) { + chr->filename = qemu_chr_socket_address(s, "disconnected:"); + } else { + chr->filename = g_strdup("disconnected:socket"); + } } /* NB may be called even if tcp_chr_connect has not been * reached, due to TLS or telnet initialization failure, - * so can *not* assume s->connected == true + * so can *not* assume s->state == TCP_CHARDEV_STATE_CONNECTED */ static void tcp_chr_disconnect(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); - bool emit_close = s->connected; + bool emit_close = s->state == TCP_CHARDEV_STATE_CONNECTED; tcp_chr_free_connection(chr); @@ -454,7 +496,8 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) uint8_t buf[CHR_READ_BUF_LEN]; int len, size; - if (!s->connected || s->max_size <= 0) { + if ((s->state != TCP_CHARDEV_STATE_CONNECTED) || + s->max_size <= 0) { return TRUE; } len = sizeof(buf); @@ -491,7 +534,7 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) SocketChardev *s = SOCKET_CHARDEV(chr); int size; - if (!s->connected) { + if (s->state != TCP_CHARDEV_STATE_CONNECTED) { return 0; } @@ -506,10 +549,12 @@ static int tcp_chr_sync_read(Chardev *chr, const uint8_t *buf, int len) return size; } -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) +static char *qemu_chr_compute_filename(SocketChardev *s) { + struct sockaddr_storage *ss = &s->sioc->localAddr; + struct sockaddr_storage *ps = &s->sioc->remoteAddr; + socklen_t ss_len = s->sioc->localAddrLen; + socklen_t ps_len = s->sioc->remoteAddrLen; char shost[NI_MAXHOST], sserv[NI_MAXSERV]; char phost[NI_MAXHOST], pserv[NI_MAXSERV]; const char *left = "", *right = ""; @@ -519,7 +564,7 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, case AF_UNIX: return g_strdup_printf("unix:%s%s", ((struct sockaddr_un *)(ss))->sun_path, - is_listen ? ",server" : ""); + s->is_listen ? ",server" : ""); #endif case AF_INET6: left = "["; @@ -531,9 +576,9 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, getnameinfo((struct sockaddr *) ps, ps_len, phost, sizeof(phost), pserv, sizeof(pserv), NI_NUMERICHOST | NI_NUMERICSERV); return g_strdup_printf("%s:%s%s%s:%s%s <-> %s%s%s:%s", - is_telnet ? "telnet" : "tcp", + qemu_chr_socket_protocol(s), left, shost, right, sserv, - is_listen ? ",server" : "", + s->is_listen ? ",server" : "", left, phost, right, pserv); default: @@ -541,28 +586,37 @@ static char *sockaddr_to_str(struct sockaddr_storage *ss, socklen_t ss_len, } } +static void update_ioc_handlers(SocketChardev *s) +{ + Chardev *chr = CHARDEV(s); + + if (s->state != TCP_CHARDEV_STATE_CONNECTED) { + return; + } + + remove_fd_in_watch(chr); + chr->gsource = io_add_watch_poll(chr, s->ioc, + tcp_chr_read_poll, + tcp_chr_read, chr, + chr->gcontext); + + remove_hup_source(s); + s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP); + g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup, + chr, NULL); + g_source_attach(s->hup_source, chr->gcontext); +} + static void tcp_chr_connect(void *opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); 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; - chr->gsource = io_add_watch_poll(chr, s->ioc, - tcp_chr_read_poll, - tcp_chr_read, - chr, chr->gcontext); - - s->hup_source = qio_channel_create_watch(s->ioc, G_IO_HUP); - g_source_set_callback(s->hup_source, (GSourceFunc)tcp_chr_hup, - chr, NULL); - g_source_attach(s->hup_source, chr->gcontext); + chr->filename = qemu_chr_compute_filename(s); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTED); + update_ioc_handlers(s); qemu_chr_be_event(chr, CHR_EVENT_OPENED); } @@ -579,7 +633,7 @@ static void tcp_chr_update_read_handler(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (s->listener) { + if (s->listener && s->state == TCP_CHARDEV_STATE_DISCONNECTED) { /* * It's possible that chardev context is changed in * qemu_chr_be_update_read_handlers(). Reset it for QIO net @@ -593,17 +647,7 @@ static void tcp_chr_update_read_handler(Chardev *chr) tcp_chr_telnet_init(CHARDEV(s)); } - if (!s->connected) { - return; - } - - remove_fd_in_watch(chr); - if (s->ioc) { - chr->gsource = io_add_watch_poll(chr, s->ioc, - tcp_chr_read_poll, - tcp_chr_read, chr, - chr->gcontext); - } + update_ioc_handlers(s); } static gboolean tcp_chr_telnet_init_io(QIOChannel *ioc, @@ -699,8 +743,7 @@ cont: } -static void tcp_chr_tls_handshake(QIOTask *task, - gpointer user_data) +static void tcp_chr_websock_handshake(QIOTask *task, gpointer user_data) { Chardev *chr = user_data; SocketChardev *s = user_data; @@ -717,6 +760,44 @@ static void tcp_chr_tls_handshake(QIOTask *task, } +static void tcp_chr_websock_init(Chardev *chr) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + QIOChannelWebsock *wioc = NULL; + gchar *name; + + wioc = qio_channel_websock_new_server(s->ioc); + + name = g_strdup_printf("chardev-websocket-server-%s", chr->label); + qio_channel_set_name(QIO_CHANNEL(wioc), name); + g_free(name); + object_unref(OBJECT(s->ioc)); + s->ioc = QIO_CHANNEL(wioc); + + qio_channel_websock_handshake(wioc, tcp_chr_websock_handshake, chr, NULL); +} + + +static void tcp_chr_tls_handshake(QIOTask *task, + gpointer user_data) +{ + Chardev *chr = user_data; + SocketChardev *s = user_data; + + if (qio_task_propagate_error(task, NULL)) { + tcp_chr_disconnect(chr); + } else { + if (s->is_websock) { + tcp_chr_websock_init(chr); + } else if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); + } else { + tcp_chr_connect(chr); + } + } +} + + static void tcp_chr_tls_init(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); @@ -724,15 +805,10 @@ static void tcp_chr_tls_init(Chardev *chr) Error *err = NULL; gchar *name; - if (!machine_init_done) { - /* This will be postponed to machine_done notifier */ - return; - } - if (s->is_listen) { tioc = qio_channel_tls_new_server( s->ioc, s->tls_creds, - NULL, /* XXX Use an ACL */ + s->tls_authz, &err); } else { tioc = qio_channel_tls_new_client( @@ -778,7 +854,7 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) { SocketChardev *s = SOCKET_CHARDEV(chr); - if (s->ioc != NULL) { + if (s->state != TCP_CHARDEV_STATE_CONNECTING) { return -1; } @@ -799,12 +875,12 @@ static int tcp_chr_new_client(Chardev *chr, QIOChannelSocket *sioc) if (s->tls_creds) { tcp_chr_tls_init(chr); + } else if (s->is_websock) { + tcp_chr_websock_init(chr); + } else if (s->do_telnetopt) { + tcp_chr_telnet_init(chr); } else { - if (s->do_telnetopt) { - tcp_chr_telnet_init(chr); - } else { - tcp_chr_connect(chr); - } + tcp_chr_connect(chr); } return 0; @@ -815,11 +891,17 @@ static int tcp_chr_add_client(Chardev *chr, int fd) { int ret; QIOChannelSocket *sioc; + SocketChardev *s = SOCKET_CHARDEV(chr); + + if (s->state != TCP_CHARDEV_STATE_DISCONNECTED) { + return -1; + } sioc = qio_channel_socket_new_fd(fd, NULL); if (!sioc) { return -1; } + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, sioc); ret = tcp_chr_new_client(chr, sioc); object_unref(OBJECT(sioc)); @@ -831,35 +913,125 @@ static void tcp_chr_accept(QIONetListener *listener, void *opaque) { Chardev *chr = CHARDEV(opaque); + SocketChardev *s = SOCKET_CHARDEV(chr); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); tcp_chr_set_client_ioc_name(chr, cioc); tcp_chr_new_client(chr, cioc); } -static int tcp_chr_wait_connected(Chardev *chr, Error **errp) + +static int tcp_chr_connect_client_sync(Chardev *chr, Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + QIOChannelSocket *sioc = qio_channel_socket_new(); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); + tcp_chr_set_client_ioc_name(chr, sioc); + if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { + tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); + object_unref(OBJECT(sioc)); + return -1; + } + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); + return 0; +} + + +static void tcp_chr_accept_server_sync(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelSocket *sioc; + info_report("QEMU waiting for connection on: %s", + chr->filename); + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); + sioc = qio_net_listener_wait_client(s->listener); + tcp_chr_set_client_ioc_name(chr, sioc); + tcp_chr_new_client(chr, sioc); + object_unref(OBJECT(sioc)); +} - /* It can't wait on s->connected, since it is set asynchronously - * in TLS and telnet cases, only wait for an accepted socket */ - while (!s->ioc) { + +static int tcp_chr_wait_connected(Chardev *chr, Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + const char *opts[] = { "telnet", "tn3270", "websock", "tls-creds" }; + bool optset[] = { s->is_telnet, s->is_tn3270, s->is_websock, s->tls_creds }; + size_t i; + + QEMU_BUILD_BUG_ON(G_N_ELEMENTS(opts) != G_N_ELEMENTS(optset)); + for (i = 0; i < G_N_ELEMENTS(opts); i++) { + if (optset[i]) { + error_setg(errp, + "'%s' option is incompatible with waiting for " + "connection completion", opts[i]); + return -1; + } + } + + tcp_chr_reconn_timer_cancel(s); + + /* + * We expect states to be as follows: + * + * - server + * - wait -> CONNECTED + * - nowait -> DISCONNECTED + * - client + * - reconnect == 0 -> CONNECTED + * - reconnect != 0 -> CONNECTING + * + */ + if (s->state == TCP_CHARDEV_STATE_CONNECTING) { + if (!s->connect_task) { + error_setg(errp, + "Unexpected 'connecting' state without connect task " + "while waiting for connection completion"); + return -1; + } + /* + * tcp_chr_wait_connected should only ever be run from the + * main loop thread associated with chr->gcontext, otherwise + * qio_task_wait_thread has a dangerous race condition with + * free'ing of the s->connect_task object. + * + * Acquiring the main context doesn't 100% prove we're in + * the main loop thread, but it does at least guarantee + * that the main loop won't be executed by another thread + * avoiding the race condition with the task idle callback. + */ + g_main_context_acquire(chr->gcontext); + qio_task_wait_thread(s->connect_task); + g_main_context_release(chr->gcontext); + + /* + * The completion callback (qemu_chr_socket_connected) for + * s->connect_task should have set this to NULL by the time + * qio_task_wait_thread has returned. + */ + assert(!s->connect_task); + + /* + * NB we are *not* guaranteed to have "s->state == ..CONNECTED" + * at this point as this first connect may be failed, so + * allow the next loop to run regardless. + */ + } + + while (s->state != TCP_CHARDEV_STATE_CONNECTED) { if (s->is_listen) { - info_report("QEMU waiting for connection on: %s", - chr->filename); - sioc = qio_net_listener_wait_client(s->listener); - tcp_chr_set_client_ioc_name(chr, sioc); - tcp_chr_new_client(chr, sioc); - object_unref(OBJECT(sioc)); + tcp_chr_accept_server_sync(chr); } else { - sioc = qio_channel_socket_new(); - tcp_chr_set_client_ioc_name(chr, sioc); - if (qio_channel_socket_connect_sync(sioc, s->addr, errp) < 0) { - object_unref(OBJECT(sioc)); - return -1; + Error *err = NULL; + if (tcp_chr_connect_client_sync(chr, &err) < 0) { + if (s->reconnect_time) { + error_free(err); + g_usleep(s->reconnect_time * 1000ULL * 1000ULL); + } else { + error_propagate(errp, err); + return -1; + } } - tcp_chr_new_client(chr, sioc); - object_unref(OBJECT(sioc)); } } @@ -884,6 +1056,7 @@ static void char_socket_finalize(Object *obj) if (s->tls_creds) { object_unref(OBJECT(s->tls_creds)); } + g_free(s->tls_authz); qemu_chr_be_event(chr, CHR_EVENT_CLOSED); } @@ -895,7 +1068,10 @@ static void qemu_chr_socket_connected(QIOTask *task, void *opaque) SocketChardev *s = SOCKET_CHARDEV(chr); Error *err = NULL; + s->connect_task = NULL; + if (qio_task_propagate_error(task, &err)) { + tcp_chr_change_state(s, TCP_CHARDEV_STATE_DISCONNECTED); check_report_connect_error(chr, err); error_free(err); goto cleanup; @@ -908,16 +1084,45 @@ cleanup: object_unref(OBJECT(sioc)); } -static void tcp_chr_connect_async(Chardev *chr) + +static void tcp_chr_connect_client_task(QIOTask *task, + gpointer opaque) +{ + QIOChannelSocket *ioc = QIO_CHANNEL_SOCKET(qio_task_get_source(task)); + SocketAddress *addr = opaque; + Error *err = NULL; + + qio_channel_socket_connect_sync(ioc, addr, &err); + + qio_task_set_error(task, err); +} + + +static void tcp_chr_connect_client_async(Chardev *chr) { SocketChardev *s = SOCKET_CHARDEV(chr); QIOChannelSocket *sioc; + tcp_chr_change_state(s, TCP_CHARDEV_STATE_CONNECTING); sioc = qio_channel_socket_new(); tcp_chr_set_client_ioc_name(chr, sioc); - qio_channel_socket_connect_async(sioc, s->addr, - qemu_chr_socket_connected, - chr, NULL, chr->gcontext); + /* + * Normally code would use the qio_channel_socket_connect_async + * method which uses a QIOTask + qio_task_set_error internally + * to avoid blocking. The tcp_chr_wait_connected method, however, + * needs a way to synchronize with completion of the background + * connect task which can't be done with the QIOChannelSocket + * async APIs. Thus we must use QIOTask directly to implement + * the non-blocking concept locally. + */ + s->connect_task = qio_task_new(OBJECT(sioc), + qemu_chr_socket_connected, + chr, NULL); + qio_task_run_in_thread(s->connect_task, + tcp_chr_connect_client_task, + s->addr, + NULL, + chr->gcontext); } static gboolean socket_reconnect_timeout(gpointer opaque) @@ -932,11 +1137,147 @@ static gboolean socket_reconnect_timeout(gpointer opaque) return false; } - tcp_chr_connect_async(chr); + tcp_chr_connect_client_async(chr); return false; } + +static int qmp_chardev_open_socket_server(Chardev *chr, + bool is_telnet, + bool is_waitconnect, + Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + char *name; + if (is_telnet) { + s->do_telnetopt = 1; + } + s->listener = qio_net_listener_new(); + + name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); + qio_net_listener_set_name(s->listener, name); + g_free(name); + + if (qio_net_listener_open_sync(s->listener, s->addr, errp) < 0) { + object_unref(OBJECT(s->listener)); + s->listener = NULL; + return -1; + } + + qapi_free_SocketAddress(s->addr); + s->addr = socket_local_address(s->listener->sioc[0]->fd, errp); + update_disconnected_filename(s); + + if (is_waitconnect) { + tcp_chr_accept_server_sync(chr); + } else { + qio_net_listener_set_client_func_full(s->listener, + tcp_chr_accept, + chr, NULL, + chr->gcontext); + } + + return 0; +} + + +static int qmp_chardev_open_socket_client(Chardev *chr, + int64_t reconnect, + Error **errp) +{ + SocketChardev *s = SOCKET_CHARDEV(chr); + + if (reconnect > 0) { + s->reconnect_time = reconnect; + tcp_chr_connect_client_async(chr); + return 0; + } else { + return tcp_chr_connect_client_sync(chr, errp); + } +} + + +static bool qmp_chardev_validate_socket(ChardevSocket *sock, + SocketAddress *addr, + Error **errp) +{ + /* Validate any options which have a dependency on address type */ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_FD: + if (sock->has_reconnect) { + error_setg(errp, + "'reconnect' option is incompatible with " + "'fd' address type"); + return false; + } + if (sock->has_tls_creds && + !(sock->has_server && sock->server)) { + error_setg(errp, + "'tls_creds' option is incompatible with " + "'fd' address type as client"); + return false; + } + break; + + case SOCKET_ADDRESS_TYPE_UNIX: + if (sock->has_tls_creds) { + error_setg(errp, + "'tls_creds' option is incompatible with " + "'unix' address type"); + return false; + } + break; + + case SOCKET_ADDRESS_TYPE_INET: + break; + + case SOCKET_ADDRESS_TYPE_VSOCK: + if (sock->has_tls_creds) { + error_setg(errp, + "'tls_creds' option is incompatible with " + "'vsock' address type"); + return false; + } + + default: + break; + } + + if (sock->has_tls_authz && !sock->has_tls_creds) { + error_setg(errp, "'tls_authz' option requires 'tls_creds' option"); + return false; + } + + /* Validate any options which have a dependancy on client vs server */ + if (!sock->has_server || sock->server) { + if (sock->has_reconnect) { + error_setg(errp, + "'reconnect' option is incompatible with " + "socket in server listen mode"); + return false; + } + } else { + if (sock->has_websocket && sock->websocket) { + error_setg(errp, "%s", "Websocket client is not implemented"); + return false; + } + if (sock->has_wait) { + warn_report("'wait' option is deprecated with " + "socket in client connect mode"); + if (sock->wait) { + error_setg(errp, "%s", + "'wait' option is incompatible with " + "socket in client connect mode"); + return false; + } + } + } + + return true; +} + + static void qmp_chardev_open_socket(Chardev *chr, ChardevBackend *backend, bool *be_opened, @@ -949,13 +1290,14 @@ static void qmp_chardev_open_socket(Chardev *chr, bool is_telnet = sock->has_telnet ? sock->telnet : false; bool is_tn3270 = sock->has_tn3270 ? sock->tn3270 : false; bool is_waitconnect = sock->has_wait ? sock->wait : false; + bool is_websock = sock->has_websocket ? sock->websocket : false; int64_t reconnect = sock->has_reconnect ? sock->reconnect : 0; - QIOChannelSocket *sioc = NULL; SocketAddress *addr; s->is_listen = is_listen; s->is_telnet = is_telnet; s->is_tn3270 = is_tn3270; + s->is_websock = is_websock; s->do_nodelay = do_nodelay; if (sock->tls_creds) { Object *creds; @@ -964,7 +1306,7 @@ static void qmp_chardev_open_socket(Chardev *chr, if (!creds) { error_setg(errp, "No TLS credentials with id '%s'", sock->tls_creds); - goto error; + return; } s->tls_creds = (QCryptoTLSCreds *) object_dynamic_cast(creds, @@ -972,26 +1314,31 @@ static void qmp_chardev_open_socket(Chardev *chr, if (!s->tls_creds) { error_setg(errp, "Object with id '%s' is not TLS credentials", sock->tls_creds); - goto error; + return; } 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; + return; } } else { if (s->tls_creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { error_setg(errp, "%s", "Expected TLS credentials for client endpoint"); - goto error; + return; } } } + s->tls_authz = g_strdup(sock->tls_authz); s->addr = addr = socket_address_flatten(sock->addr); + if (!qmp_chardev_validate_socket(sock, addr, errp)) { + return; + } + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_RECONNECTABLE); /* TODO SOCKET_ADDRESS_FD where fd has AF_UNIX */ if (addr->type == SOCKET_ADDRESS_TYPE_UNIX) { @@ -1003,71 +1350,25 @@ static void qmp_chardev_open_socket(Chardev *chr, update_disconnected_filename(s); - if (is_listen) { - if (is_telnet || is_tn3270) { - s->do_telnetopt = 1; + if (s->is_listen) { + if (qmp_chardev_open_socket_server(chr, is_telnet || is_tn3270, + is_waitconnect, errp) < 0) { + return; } - } else if (reconnect > 0) { - s->reconnect_time = reconnect; - } - - /* If reconnect_time is set, will do that in chr_machine_done. */ - if (!s->reconnect_time) { - if (s->is_listen) { - char *name; - s->listener = qio_net_listener_new(); - - name = g_strdup_printf("chardev-tcp-listener-%s", chr->label); - qio_net_listener_set_name(s->listener, name); - g_free(name); - - if (qio_net_listener_open_sync(s->listener, s->addr, errp) < 0) { - object_unref(OBJECT(s->listener)); - s->listener = NULL; - goto error; - } - - qapi_free_SocketAddress(s->addr); - s->addr = socket_local_address(s->listener->sioc[0]->fd, errp); - update_disconnected_filename(s); - - if (is_waitconnect && - qemu_chr_wait_connected(chr, errp) < 0) { - return; - } - if (!s->ioc) { - qio_net_listener_set_client_func_full(s->listener, - tcp_chr_accept, - chr, NULL, - chr->gcontext); - } - } else if (qemu_chr_wait_connected(chr, errp) < 0) { - goto error; + } else { + if (qmp_chardev_open_socket_client(chr, reconnect, errp) < 0) { + return; } } - - return; - -error: - if (sioc) { - object_unref(OBJECT(sioc)); - } } static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, Error **errp) { - bool is_listen = qemu_opt_get_bool(opts, "server", false); - bool is_waitconnect = is_listen && qemu_opt_get_bool(opts, "wait", true); - bool is_telnet = qemu_opt_get_bool(opts, "telnet", false); - bool is_tn3270 = qemu_opt_get_bool(opts, "tn3270", false); - bool do_nodelay = !qemu_opt_get_bool(opts, "delay", true); - int64_t reconnect = qemu_opt_get_number(opts, "reconnect", 0); const char *path = qemu_opt_get(opts, "path"); const char *host = qemu_opt_get(opts, "host"); const char *port = qemu_opt_get(opts, "port"); const char *fd = qemu_opt_get(opts, "fd"); - const char *tls_creds = qemu_opt_get(opts, "tls-creds"); SocketAddressLegacy *addr; ChardevSocket *sock; @@ -1077,43 +1378,41 @@ static void qemu_chr_parse_socket(QemuOpts *opts, ChardevBackend *backend, return; } - backend->type = CHARDEV_BACKEND_KIND_SOCKET; - if (path) { - if (tls_creds) { - error_setg(errp, "TLS can only be used over TCP socket"); - return; - } - } else if (host) { - if (!port) { - error_setg(errp, "chardev: socket: no port given"); - return; - } - } else if (fd) { - /* We don't know what host to validate against when in client mode */ - if (tls_creds && !is_listen) { - error_setg(errp, "TLS can not be used with pre-opened client FD"); - return; - } - } else { - g_assert_not_reached(); + if (host && !port) { + error_setg(errp, "chardev: socket: no port given"); + return; } + backend->type = CHARDEV_BACKEND_KIND_SOCKET; sock = backend->u.socket.data = g_new0(ChardevSocket, 1); qemu_chr_parse_common(opts, qapi_ChardevSocket_base(sock)); - sock->has_nodelay = true; - sock->nodelay = do_nodelay; + sock->has_nodelay = qemu_opt_get(opts, "delay"); + sock->nodelay = !qemu_opt_get_bool(opts, "delay", true); + /* + * We have different default to QMP for 'server', hence + * we can't just check for existence of 'server' + */ sock->has_server = true; - sock->server = is_listen; - sock->has_telnet = true; - sock->telnet = is_telnet; - sock->has_tn3270 = true; - sock->tn3270 = is_tn3270; - sock->has_wait = true; - sock->wait = is_waitconnect; - sock->has_reconnect = true; - sock->reconnect = reconnect; - sock->tls_creds = g_strdup(tls_creds); + sock->server = qemu_opt_get_bool(opts, "server", false); + sock->has_telnet = qemu_opt_get(opts, "telnet"); + sock->telnet = qemu_opt_get_bool(opts, "telnet", false); + sock->has_tn3270 = qemu_opt_get(opts, "tn3270"); + sock->tn3270 = qemu_opt_get_bool(opts, "tn3270", false); + sock->has_websocket = qemu_opt_get(opts, "websocket"); + sock->websocket = qemu_opt_get_bool(opts, "websocket", false); + /* + * We have different default to QMP for 'wait' when 'server' + * is set, hence we can't just check for existence of 'wait' + */ + sock->has_wait = qemu_opt_find(opts, "wait") || sock->server; + sock->wait = qemu_opt_get_bool(opts, "wait", true); + sock->has_reconnect = qemu_opt_find(opts, "reconnect"); + sock->reconnect = qemu_opt_get_number(opts, "reconnect", 0); + sock->has_tls_creds = qemu_opt_get(opts, "tls-creds"); + sock->tls_creds = g_strdup(qemu_opt_get(opts, "tls-creds")); + sock->has_tls_authz = qemu_opt_get(opts, "tls-authz"); + sock->tls_authz = g_strdup(qemu_opt_get(opts, "tls-authz")); addr = g_new0(SocketAddressLegacy, 1); if (path) { @@ -1158,22 +1457,7 @@ char_socket_get_connected(Object *obj, Error **errp) { SocketChardev *s = SOCKET_CHARDEV(obj); - return s->connected; -} - -static int tcp_chr_machine_done_hook(Chardev *chr) -{ - SocketChardev *s = SOCKET_CHARDEV(chr); - - if (s->reconnect_time) { - tcp_chr_connect_async(chr); - } - - if (s->ioc && s->tls_creds) { - tcp_chr_tls_init(chr); - } - - return 0; + return s->state == TCP_CHARDEV_STATE_CONNECTED; } static void char_socket_class_init(ObjectClass *oc, void *data) @@ -1191,7 +1475,6 @@ static void char_socket_class_init(ObjectClass *oc, void *data) cc->chr_add_client = tcp_chr_add_client; cc->chr_add_watch = tcp_chr_add_watch; cc->chr_update_read_handler = tcp_chr_update_read_handler; - cc->chr_machine_done = tcp_chr_machine_done_hook; object_class_property_add(oc, "addr", "SocketAddress", char_socket_get_addr, NULL, diff --git a/chardev/char.c b/chardev/char.c index 76d866e6fe..514cd6b0c3 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -193,6 +193,8 @@ void qemu_chr_be_update_read_handlers(Chardev *s, { ChardevClass *cc = CHARDEV_GET_CLASS(s); + assert(qemu_chr_has_feature(s, QEMU_CHAR_FEATURE_GCONTEXT) + || !context); s->gcontext = context; if (cc->chr_update_read_handler) { cc->chr_update_read_handler(s); @@ -240,6 +242,15 @@ static void char_init(Object *obj) chr->logfd = -1; qemu_mutex_init(&chr->chr_write_lock); + + /* + * Assume if chr_update_read_handler is implemented it will + * take the updated gcontext into account. + */ + if (CHARDEV_GET_CLASS(chr)->chr_update_read_handler) { + qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_GCONTEXT); + } + } static int null_chr_write(Chardev *chr, const uint8_t *buf, int len) @@ -329,7 +340,8 @@ int qemu_chr_wait_connected(Chardev *chr, Error **errp) return 0; } -QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) +QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename, + bool permit_mux_mon) { char host[65], port[33], width[8], height[8]; int pos; @@ -344,6 +356,10 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) } if (strstart(filename, "mon:", &p)) { + if (!permit_mux_mon) { + error_report("mon: isn't supported in this context"); + return NULL; + } filename = p; qemu_opt_set(opts, "mux", "on", &error_abort); if (strcmp(filename, "stdio") == 0) { @@ -404,7 +420,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) } if (strstart(filename, "tcp:", &p) || strstart(filename, "telnet:", &p) || - strstart(filename, "tn3270:", &p)) { + strstart(filename, "tn3270:", &p) || + strstart(filename, "websocket:", &p)) { if (sscanf(p, "%64[^:]:%32[^,]%n", host, port, &pos) < 2) { host[0] = 0; if (sscanf(p, ":%32[^,]%n", port, &pos) < 1) @@ -424,6 +441,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) qemu_opt_set(opts, "telnet", "on", &error_abort); } else if (strstart(filename, "tn3270:", &p)) { qemu_opt_set(opts, "tn3270", "on", &error_abort); + } else if (strstart(filename, "websocket:", &p)) { + qemu_opt_set(opts, "websocket", "on", &error_abort); } return opts; } @@ -471,6 +490,8 @@ QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename) return opts; } + error_report("'%s' is not a valid char driver", filename); + fail: qemu_opts_del(opts); return NULL; @@ -564,7 +585,7 @@ help_string_append(const char *name, void *opaque) { GString *str = opaque; - g_string_append_printf(str, "\n%s", name); + g_string_append_printf(str, "\n %s", name); } static const char *chardev_alias_translate(const char *name) @@ -615,7 +636,8 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp) return backend; } -Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) +Chardev *qemu_chr_new_from_opts(QemuOpts *opts, GMainContext *context, + Error **errp) { const ChardevClass *cc; Chardev *chr = NULL; @@ -629,7 +651,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) chardev_name_foreach(help_string_append, str); - error_report("Available chardev backend types: %s", str->str); + error_printf("Available chardev backend types: %s\n", str->str); g_string_free(str, true); return NULL; } @@ -655,7 +677,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) chr = qemu_chardev_new(bid ? bid : id, object_class_get_name(OBJECT_CLASS(cc)), - backend, errp); + backend, context, errp); if (chr == NULL) { goto out; @@ -668,7 +690,7 @@ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, Error **errp) backend->type = CHARDEV_BACKEND_KIND_MUX; backend->u.mux.data = g_new0(ChardevMux, 1); backend->u.mux.data->chardev = g_strdup(bid); - mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, errp); + mux = qemu_chardev_new(id, TYPE_CHARDEV_MUX, backend, context, errp); if (mux == NULL) { object_unparent(OBJECT(chr)); chr = NULL; @@ -683,7 +705,8 @@ out: return chr; } -Chardev *qemu_chr_new_noreplay(const char *label, const char *filename) +Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, + bool permit_mux_mon, GMainContext *context) { const char *p; Chardev *chr; @@ -694,25 +717,33 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename) return qemu_chr_find(p); } - opts = qemu_chr_parse_compat(label, filename); + opts = qemu_chr_parse_compat(label, filename, permit_mux_mon); if (!opts) return NULL; - chr = qemu_chr_new_from_opts(opts, &err); - if (err) { + chr = qemu_chr_new_from_opts(opts, context, &err); + if (!chr) { error_report_err(err); + goto out; } - if (chr && qemu_opt_get_bool(opts, "mux", 0)) { + + if (qemu_opt_get_bool(opts, "mux", 0)) { + assert(permit_mux_mon); monitor_init(chr, MONITOR_USE_READLINE); } + +out: qemu_opts_del(opts); return chr; } -Chardev *qemu_chr_new(const char *label, const char *filename) +static Chardev *qemu_chr_new_permit_mux_mon(const char *label, + const char *filename, + bool permit_mux_mon, + GMainContext *context) { Chardev *chr; - chr = qemu_chr_new_noreplay(label, filename); + chr = qemu_chr_new_noreplay(label, filename, permit_mux_mon, context); if (chr) { if (replay_mode != REPLAY_MODE_NONE) { qemu_chr_set_feature(chr, QEMU_CHAR_FEATURE_REPLAY); @@ -726,6 +757,18 @@ Chardev *qemu_chr_new(const char *label, const char *filename) return chr; } +Chardev *qemu_chr_new(const char *label, const char *filename, + GMainContext *context) +{ + return qemu_chr_new_permit_mux_mon(label, filename, false, context); +} + +Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename, + GMainContext *context) +{ + return qemu_chr_new_permit_mux_mon(label, filename, true, context); +} + static int qmp_query_chardev_foreach(Object *obj, void *data) { Chardev *chr = CHARDEV(obj); @@ -837,6 +880,12 @@ QemuOptsList qemu_chardev_opts = { },{ .name = "tls-creds", .type = QEMU_OPT_STRING, + },{ + .name = "tls-authz", + .type = QEMU_OPT_STRING, + },{ + .name = "websocket", + .type = QEMU_OPT_BOOL, },{ .name = "width", .type = QEMU_OPT_NUMBER, @@ -895,6 +944,7 @@ void qemu_chr_set_feature(Chardev *chr, Chardev *qemu_chardev_new(const char *id, const char *typename, ChardevBackend *backend, + GMainContext *gcontext, Error **errp) { Object *obj; @@ -907,6 +957,7 @@ Chardev *qemu_chardev_new(const char *id, const char *typename, obj = object_new(typename); chr = CHARDEV(obj); chr->label = g_strdup(id); + chr->gcontext = gcontext; qemu_char_open(chr, backend, &be_opened, &local_err); if (local_err) { @@ -951,7 +1002,7 @@ ChardevReturn *qmp_chardev_add(const char *id, ChardevBackend *backend, } chr = qemu_chardev_new(id, object_class_get_name(OBJECT_CLASS(cc)), - backend, errp); + backend, NULL, errp); if (!chr) { return NULL; } @@ -1009,7 +1060,7 @@ ChardevReturn *qmp_chardev_change(const char *id, ChardevBackend *backend, } chr_new = qemu_chardev_new(NULL, object_class_get_name(OBJECT_CLASS(cc)), - backend, errp); + backend, chr->gcontext, errp); if (!chr_new) { return NULL; } diff --git a/chardev/spice.c b/chardev/spice.c index e66e3ad568..22c30ae833 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -2,30 +2,12 @@ #include "trace.h" #include "ui/qemu-spice.h" #include "chardev/char.h" +#include "chardev/spice.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/option.h" -#include #include - -typedef struct SpiceChardev { - Chardev parent; - - SpiceCharDeviceInstance sin; - bool active; - bool blocked; - const uint8_t *datapos; - int datalen; - QLIST_ENTRY(SpiceChardev) next; -} SpiceChardev; - -#define TYPE_CHARDEV_SPICE "chardev-spice" -#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc" -#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport" - -#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE) - typedef struct SpiceCharSource { GSource source; SpiceChardev *scd; @@ -77,7 +59,6 @@ static int vmc_read(SpiceCharDeviceInstance *sin, uint8_t *buf, int len) return bytes; } -#if SPICE_SERVER_VERSION >= 0x000c02 static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) { SpiceChardev *scd = container_of(sin, SpiceChardev, sin); @@ -95,7 +76,6 @@ static void vmc_event(SpiceCharDeviceInstance *sin, uint8_t event) trace_spice_vmc_event(chr_event); qemu_chr_be_event(chr, chr_event); } -#endif static void vmc_state(SpiceCharDeviceInstance *sin, int connected) { @@ -119,9 +99,7 @@ static SpiceCharDeviceInterface vmc_interface = { .state = vmc_state, .write = vmc_write, .read = vmc_read, -#if SPICE_SERVER_VERSION >= 0x000c02 .event = vmc_event, -#endif #if SPICE_SERVER_VERSION >= 0x000c06 .flags = SPICE_CHAR_DEVICE_NOTIFY_WRITABLE, #endif @@ -152,15 +130,25 @@ static void vmc_unregister_interface(SpiceChardev *scd) static gboolean spice_char_source_prepare(GSource *source, gint *timeout) { SpiceCharSource *src = (SpiceCharSource *)source; + Chardev *chr = CHARDEV(src->scd); *timeout = -1; + if (!chr->be_open) { + return true; + } + return !src->scd->blocked; } static gboolean spice_char_source_check(GSource *source) { SpiceCharSource *src = (SpiceCharSource *)source; + Chardev *chr = CHARDEV(src->scd); + + if (!chr->be_open) { + return true; + } return !src->scd->blocked; } @@ -168,9 +156,12 @@ static gboolean spice_char_source_check(GSource *source) static gboolean spice_char_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { + SpiceCharSource *src = (SpiceCharSource *)source; + Chardev *chr = CHARDEV(src->scd); GIOFunc func = (GIOFunc)callback; + GIOCondition cond = chr->be_open ? G_IO_OUT : G_IO_HUP; - return func(NULL, G_IO_OUT, user_data); + return func(NULL, cond, user_data); } static GSourceFuncs SpiceCharSourceFuncs = { @@ -199,6 +190,12 @@ static int spice_chr_write(Chardev *chr, const uint8_t *buf, int len) int read_bytes; assert(s->datalen == 0); + + if (!chr->be_open) { + trace_spice_chr_discard_write(len); + return len; + } + s->datapos = buf; s->datalen = len; spice_server_char_device_wakeup(&s->sin); @@ -223,9 +220,7 @@ static void char_spice_finalize(Object *obj) } g_free((char *)s->sin.subtype); -#if SPICE_SERVER_VERSION >= 0x000c02 g_free((char *)s->sin.portname); -#endif } static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open) @@ -240,7 +235,6 @@ static void spice_vmc_set_fe_open(struct Chardev *chr, int fe_open) static void spice_port_set_fe_open(struct Chardev *chr, int fe_open) { -#if SPICE_SERVER_VERSION >= 0x000c02 SpiceChardev *s = SPICE_CHARDEV(chr); if (fe_open) { @@ -248,7 +242,6 @@ static void spice_port_set_fe_open(struct Chardev *chr, int fe_open) } else { spice_server_port_event(&s->sin, SPICE_PORT_EVENT_CLOSED); } -#endif } static void spice_chr_accept_input(struct Chardev *chr) @@ -295,14 +288,19 @@ static void qemu_chr_open_spice_vmc(Chardev *chr, } *be_opened = false; +#if SPICE_SERVER_VERSION < 0x000e02 + /* Spice < 0.14.2 doesn't explicitly open smartcard chardev */ + if (strcmp(type, "smartcard") == 0) { + *be_opened = true; + } +#endif chr_open(chr, type); } -#if SPICE_SERVER_VERSION >= 0x000c02 -static void qemu_chr_open_spice_port(Chardev *chr, - ChardevBackend *backend, - bool *be_opened, - Error **errp) +void qemu_chr_open_spice_port(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) { ChardevSpicePort *spiceport = backend->u.spiceport.data; const char *name = spiceport->fqdn; @@ -318,6 +316,11 @@ static void qemu_chr_open_spice_port(Chardev *chr, *be_opened = false; s = SPICE_CHARDEV(chr); s->sin.portname = g_strdup(name); + + if (using_spice) { + /* spice server already created */ + vmc_register_interface(s); + } } void qemu_spice_register_ports(void) @@ -331,7 +334,6 @@ void qemu_spice_register_ports(void) vmc_register_interface(s); } } -#endif static void qemu_chr_parse_spice_vmc(QemuOpts *opts, ChardevBackend *backend, Error **errp) diff --git a/chardev/trace-events b/chardev/trace-events index d0e5f3bbc1..5ea4408207 100644 --- a/chardev/trace-events +++ b/chardev/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# chardev/wctablet.c +# wctablet.c wct_init(void) "" wct_cmd_re(void) "" wct_cmd_st(void) "" @@ -9,7 +9,8 @@ wct_cmd_ts(int input) "0x%02x" wct_cmd_other(const char *cmd) "%s" wct_speed(int speed) "%d" -# chardev/spice.c +# spice.c +spice_chr_discard_write(int len) "spice chr write discarded %d" spice_vmc_write(ssize_t out, int len) "spice wrote %zd of requested %d" spice_vmc_read(int bytes, int len) "spice read %d of requested %d" spice_vmc_register_interface(void *scd) "spice vmc registered interface %p" diff --git a/chardev/wctablet.c b/chardev/wctablet.c index 969d014574..35dbd29a33 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -177,7 +177,7 @@ static void wctablet_input_sync(DeviceState *dev) } static QemuInputHandler wctablet_handler = { - .name = "QEMU Wacome Pen Tablet", + .name = "QEMU Wacom Pen Tablet", .mask = INPUT_EVENT_MASK_BTN | INPUT_EVENT_MASK_ABS, .event = wctablet_input_event, .sync = wctablet_input_sync, diff --git a/configure b/configure index 4c10a82af9..5fbcffd97d 100755 --- a/configure +++ b/configure @@ -107,6 +107,9 @@ update_cxxflags() { -Wstrict-prototypes|-Wmissing-prototypes|-Wnested-externs|\ -Wold-style-declaration|-Wold-style-definition|-Wredundant-decls) ;; + -std=gnu99) + QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }"-std=gnu++98" + ;; *) QEMU_CXXFLAGS=${QEMU_CXXFLAGS:+$QEMU_CXXFLAGS }$arg ;; @@ -195,8 +198,7 @@ supported_kvm_target() { i386:i386 | i386:x86_64 | i386:x32 | \ x86_64:i386 | x86_64:x86_64 | x86_64:x32 | \ mips:mips | mipsel:mips | \ - ppc:ppc | ppcemb:ppc | ppc64:ppc | \ - ppc:ppc64 | ppcemb:ppc64 | ppc64:ppc64 | \ + ppc:ppc | ppc64:ppc | ppc:ppc64 | ppc64:ppc64 | \ s390x:s390x) return 0 ;; @@ -297,6 +299,8 @@ if test -e "$source_path/.git" then git_update=yes git_submodules="ui/keycodemapdb" + git_submodules="$git_submodules tests/fp/berkeley-testfloat-3" + git_submodules="$git_submodules tests/fp/berkeley-softfloat-3" else git_update=no git_submodules="" @@ -323,6 +327,7 @@ git="git" # Don't accept a target_list environment variable. unset target_list +unset target_list_exclude # Default value for a variable defining feature "foo". # * foo="no" feature will only be used if --enable-foo arg is given @@ -344,7 +349,7 @@ docs="" fdt="" netmap="no" sdl="" -sdlabi="" +sdl_image="" virtfs="" mpath="" vnc="yes" @@ -356,7 +361,6 @@ vnc_png="" xkbcommon="" xen="" xen_ctrl_version="" -xen_pv_domain_build="no" xen_pci_passthrough="" linux_aio="" cap_ng="" @@ -365,16 +369,17 @@ libattr="" xfs="" tcg="yes" membarrier="" -vhost_net="no" -vhost_crypto="no" -vhost_scsi="no" -vhost_vsock="no" +vhost_net="" +vhost_crypto="" +vhost_scsi="" +vhost_vsock="" vhost_user="" kvm="no" hax="no" hvf="no" whpx="no" rdma="" +pvrdma="" gprof="no" debug_tcg="no" debug="no" @@ -402,7 +407,7 @@ includedir="\${prefix}/include" sysconfdir="\${prefix}/etc" local_statedir="\${prefix}/var" confsuffix="/qemu" -slirp="yes" +slirp="" oss_lib="" bsd="no" linux="no" @@ -426,12 +431,13 @@ usb_redir="" opengl="" opengl_dmabuf="no" cpuid_h="no" -avx2_opt="no" +avx2_opt="" zlib="yes" capstone="" lzo="" snappy="" bzip2="" +lzfse="" guest_agent="" guest_agent_with_vss="no" guest_agent_ntddscsi="no" @@ -451,20 +457,19 @@ glusterfs_xlator_opt="no" glusterfs_discard="no" glusterfs_fallocate="no" glusterfs_zerofill="no" +glusterfs_ftruncate_has_stat="no" +glusterfs_iocb_has_stat="no" gtk="" -gtkabi="" gtk_gl="no" tls_priority="NORMAL" gnutls="" -gnutls_rnd="" nettle="" -nettle_kdf="no" gcrypt="" gcrypt_hmac="no" -gcrypt_kdf="no" +auth_pam="" vte="" virglrenderer="" -tpm="yes" +tpm="" libssh2="" live_block_migration="yes" numa="" @@ -472,9 +477,20 @@ tcmalloc="no" jemalloc="no" replication="yes" vxhs="" +bochs="yes" +cloop="yes" +dmg="yes" +qcow1="yes" +vdi="yes" +vvfat="yes" +qed="yes" +parallels="yes" +sheepdog="yes" libxml2="" docker="no" debug_mutex="no" +libpmem="" +default_devices="yes" # cross compilers defaults, can be overridden with --cross-cc-ARCH cross_cc_aarch64="aarch64-linux-gnu-gcc" @@ -564,7 +580,6 @@ query_pkg_config() { "${pkg_config_exe}" ${QEMU_PKG_CONFIG_FLAGS} "$@" } pkg_config=query_pkg_config -sdl_config="${SDL_CONFIG-${cross_prefix}sdl-config}" sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" # If the user hasn't specified ARFLAGS, default to 'rv', just as make does. @@ -575,7 +590,7 @@ ARFLAGS="${ARFLAGS-rv}" # left shift of signed integers is well defined and has the expected # 2s-complement style results. (Both clang and gcc agree that it # provides these semantics.) -QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv $QEMU_CFLAGS" +QEMU_CFLAGS="-fno-strict-aliasing -fno-common -fwrapv -std=gnu99 $QEMU_CFLAGS" QEMU_CFLAGS="-Wall -Wundef -Wwrite-strings -Wmissing-prototypes $QEMU_CFLAGS" QEMU_CFLAGS="-Wstrict-prototypes -Wredundant-decls $QEMU_CFLAGS" QEMU_CFLAGS="-D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE $QEMU_CFLAGS" @@ -700,6 +715,12 @@ elif check_define __s390__ ; then else cpu="s390" fi +elif check_define __riscv ; then + if check_define _LP64 ; then + cpu="riscv64" + else + cpu="riscv32" + fi elif check_define __arm__ ; then cpu="arm" elif check_define __aarch64__ ; then @@ -712,7 +733,7 @@ ARCH= # Normalise host CPU name and set ARCH. # Note that this case should only have supported host CPUs, not guests. case "$cpu" in - ppc|ppc64|s390|s390x|sparc64|x32) + ppc|ppc64|s390|s390x|sparc64|x32|riscv32|riscv64) cpu="$cpu" supported_cpu="yes" eval "cross_cc_${cpu}=\$host_cc" @@ -765,6 +786,7 @@ case $targetos in MINGW32*) mingw32="yes" hax="yes" + vhost_user="no" audio_possible_drivers="dsound sdl" if check_include dsound.h; then audio_drv_list="dsound" @@ -775,13 +797,13 @@ MINGW32*) ;; GNU/kFreeBSD) bsd="yes" - audio_drv_list="oss" + audio_drv_list="oss try-sdl" audio_possible_drivers="oss sdl pa" ;; FreeBSD) bsd="yes" make="${MAKE-gmake}" - audio_drv_list="oss" + audio_drv_list="oss try-sdl" audio_possible_drivers="oss sdl pa" # needed for kinfo_getvmmap(3) in libutil.h LIBS="-lutil $LIBS" @@ -794,14 +816,15 @@ FreeBSD) DragonFly) bsd="yes" make="${MAKE-gmake}" - audio_drv_list="oss" + audio_drv_list="oss try-sdl" audio_possible_drivers="oss sdl pa" HOST_VARIANT_DIR="dragonfly" ;; NetBSD) bsd="yes" + hax="yes" make="${MAKE-gmake}" - audio_drv_list="oss" + audio_drv_list="oss try-sdl" audio_possible_drivers="oss sdl" oss_lib="-lossaudio" HOST_VARIANT_DIR="netbsd" @@ -810,7 +833,7 @@ NetBSD) OpenBSD) bsd="yes" make="${MAKE-gmake}" - audio_drv_list="sdl" + audio_drv_list="try-sdl" audio_possible_drivers="sdl" HOST_VARIANT_DIR="openbsd" supported_os="yes" @@ -826,7 +849,7 @@ Darwin) LDFLAGS="-arch x86_64 $LDFLAGS" fi cocoa="yes" - audio_drv_list="coreaudio" + audio_drv_list="coreaudio try-sdl" audio_possible_drivers="coreaudio sdl" LDFLAGS="-framework CoreFoundation -framework IOKit $LDFLAGS" libs_softmmu="-F/System/Library/Frameworks -framework Cocoa -framework IOKit $libs_softmmu" @@ -842,7 +865,7 @@ SunOS) install="${INSTALL-ginstall}" smbd="${SMBD-/usr/sfw/sbin/smbd}" if test -f /usr/include/sys/soundcard.h ; then - audio_drv_list="oss" + audio_drv_list="oss try-sdl" fi audio_possible_drivers="oss sdl" # needed for CMSG_ macros in sys/socket.h @@ -860,17 +883,14 @@ Haiku) LIBS="-lposix_error_mapper -lnetwork $LIBS" ;; Linux) - audio_drv_list="oss" + audio_drv_list="try-pa oss" audio_possible_drivers="oss alsa sdl pa" linux="yes" linux_user="yes" kvm="yes" - vhost_net="yes" - vhost_crypto="yes" - vhost_scsi="yes" - vhost_vsock="yes" - QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$(pwd)/linux-headers $QEMU_INCLUDES" + QEMU_INCLUDES="-I\$(SRC_PATH)/linux-headers -I$PWD/linux-headers $QEMU_INCLUDES" supported_os="yes" + libudev="yes" ;; esac @@ -895,9 +915,6 @@ fi if test "$mingw32" = "yes" ; then EXESUF=".exe" DSOSUF=".dll" - 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 -lopengl32 -lgdi32 $LIBS" @@ -974,6 +991,14 @@ for opt do --cpu=*) ;; --target-list=*) target_list="$optarg" + if test "$target_list_exclude"; then + error_exit "Can't mix --target-list with --target-list-exclude" + fi + ;; + --target-list-exclude=*) target_list_exclude="$optarg" + if test "$target_list"; then + error_exit "Can't mix --target-list-exclude with --target-list" + fi ;; --enable-trace-backends=*) trace_backends="$optarg" ;; @@ -982,6 +1007,10 @@ for opt do ;; --with-trace-file=*) trace_file="$optarg" ;; + --with-default-devices) default_devices="yes" + ;; + --without-default-devices) default_devices="no" + ;; --enable-gprof) gprof="yes" ;; --enable-gcov) gcov="yes" @@ -1027,7 +1056,9 @@ for opt do ;; --enable-sdl) sdl="yes" ;; - --with-sdlabi=*) sdlabi="$optarg" + --disable-sdl-image) sdl_image="no" + ;; + --enable-sdl-image) sdl_image="yes" ;; --disable-qom-cast-debug) qom_cast_debug="no" ;; @@ -1089,6 +1120,8 @@ for opt do ;; --disable-slirp) slirp="no" ;; + --enable-slirp=system) slirp="system" + ;; --disable-vde) vde="no" ;; --enable-vde) vde="yes" @@ -1105,10 +1138,6 @@ 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" @@ -1202,6 +1231,10 @@ for opt do ;; --enable-curses) curses="yes" ;; + --disable-iconv) iconv="no" + ;; + --enable-iconv) iconv="yes" + ;; --disable-curl) curl="no" ;; --enable-curl) curl="yes" @@ -1248,11 +1281,7 @@ for opt do ;; --disable-vhost-crypto) vhost_crypto="no" ;; - --enable-vhost-crypto) - vhost_crypto="yes" - if test "$mingw32" = "yes"; then - error_exit "vhost-crypto isn't available on win32" - fi + --enable-vhost-crypto) vhost_crypto="yes" ;; --disable-vhost-scsi) vhost_scsi="no" ;; @@ -1300,6 +1329,10 @@ for opt do ;; --enable-bzip2) bzip2="yes" ;; + --enable-lzfse) lzfse="yes" + ;; + --disable-lzfse) lzfse="no" + ;; --enable-guest-agent) guest_agent="yes" ;; --disable-guest-agent) guest_agent="no" @@ -1330,6 +1363,10 @@ for opt do ;; --disable-glusterfs) glusterfs="no" ;; + --disable-avx2) avx2_opt="no" + ;; + --enable-avx2) avx2_opt="yes" + ;; --enable-glusterfs) glusterfs="yes" ;; --disable-virtio-blk-data-plane|--enable-virtio-blk-data-plane) @@ -1359,11 +1396,17 @@ for opt do ;; --enable-gcrypt) gcrypt="yes" ;; + --disable-auth-pam) auth_pam="no" + ;; + --enable-auth-pam) auth_pam="yes" + ;; --enable-rdma) rdma="yes" ;; --disable-rdma) rdma="no" ;; - --with-gtkabi=*) gtkabi="$optarg" + --enable-pvrdma) pvrdma="yes" + ;; + --disable-pvrdma) pvrdma="no" ;; --disable-vte) vte="no" ;; @@ -1409,13 +1452,49 @@ for opt do ;; --enable-vxhs) vxhs="yes" ;; + --disable-bochs) bochs="no" + ;; + --enable-bochs) bochs="yes" + ;; + --disable-cloop) cloop="no" + ;; + --enable-cloop) cloop="yes" + ;; + --disable-dmg) dmg="no" + ;; + --enable-dmg) dmg="yes" + ;; + --disable-qcow1) qcow1="no" + ;; + --enable-qcow1) qcow1="yes" + ;; + --disable-vdi) vdi="no" + ;; + --enable-vdi) vdi="yes" + ;; + --disable-vvfat) vvfat="no" + ;; + --enable-vvfat) vvfat="yes" + ;; + --disable-qed) qed="no" + ;; + --enable-qed) qed="yes" + ;; + --disable-parallels) parallels="no" + ;; + --enable-parallels) parallels="yes" + ;; + --disable-sheepdog) sheepdog="no" + ;; + --enable-sheepdog) sheepdog="yes" + ;; --disable-vhost-user) vhost_user="no" ;; - --enable-vhost-user) - vhost_user="yes" - if test "$mingw32" = "yes"; then - error_exit "vhost-user isn't available on win32" - fi + --enable-vhost-user) vhost_user="yes" + ;; + --disable-vhost-kernel) vhost_kernel="no" + ;; + --enable-vhost-kernel) vhost_kernel="yes" ;; --disable-capstone) capstone="no" ;; @@ -1435,6 +1514,10 @@ for opt do ;; --disable-debug-mutex) debug_mutex=no ;; + --enable-libpmem) libpmem=yes + ;; + --disable-libpmem) libpmem=no + ;; *) echo "ERROR: unknown option $opt" echo "Try '$0 --help' for more information" @@ -1443,14 +1526,6 @@ for opt do esac done -if test "$vhost_user" = ""; then - if test "$mingw32" = "yes"; then - vhost_user="no" - else - vhost_user="yes" - fi -fi - case "$cpu" in ppc) CPU_CFLAGS="-m32" @@ -1535,9 +1610,26 @@ if [ "$bsd_user" = "yes" ]; then mak_wilds="${mak_wilds} $source_path/default-configs/*-bsd-user.mak" fi -for config in $mak_wilds; do - default_target_list="${default_target_list} $(basename "$config" .mak)" -done +if test -z "$target_list_exclude"; then + for config in $mak_wilds; do + default_target_list="${default_target_list} $(basename "$config" .mak)" + done +else + exclude_list=$(echo "$target_list_exclude" | sed -e 's/,/ /g') + for config in $mak_wilds; do + target="$(basename "$config" .mak)" + exclude="no" + for excl in $exclude_list; do + if test "$excl" = "$target"; then + exclude="yes" + break; + fi + done + if test "$exclude" = "no"; then + default_target_list="${default_target_list} $target" + fi + done +fi # Enumerate public trace backends for --help output trace_backend_list=$(echo $(grep -le '^PUBLIC = True$' "$source_path"/scripts/tracetool/backend/*.py | sed -e 's/^.*\/\(.*\)\.py$/\1/')) @@ -1556,6 +1648,7 @@ Standard options: --target-list=LIST set target list (default: build everything) $(echo Available targets: $default_target_list | \ fold -s -w 53 | sed -e 's/^/ /') + --target-list-exclude=LIST exclude a set of targets from the default target-list Advanced options (experts only): --source-path=PATH path of source code [$source_path] @@ -1621,8 +1714,6 @@ Advanced options (experts only): --tls-priority default TLS protocol/cipher priority string --enable-gprof QEMU profiling with gprof --enable-profiler profiler support - --enable-xen-pv-domain-build - xen pv domain builder --enable-debug-stack-usage track the maximum stack usage of stacks created by qemu_alloc_stack @@ -1645,12 +1736,13 @@ disabled with --disable-FEATURE, default is enabled if available: gnutls GNUTLS cryptography support nettle nettle cryptography support gcrypt libgcrypt cryptography support + auth-pam PAM access control sdl SDL UI - --with-sdlabi select preferred SDL ABI 1.2 or 2.0 + sdl_image SDL Image support for icons gtk gtk UI - --with-gtkabi select preferred GTK ABI 2.0 or 3.0 vte vte support for the gtk UI curses curses UI + iconv font glyph conversion support vnc VNC UI support vnc-sasl SASL encryption for VNC server vnc-jpeg JPEG lossy compression for VNC server @@ -1669,14 +1761,19 @@ disabled with --disable-FEATURE, default is enabled if available: hax HAX acceleration support hvf Hypervisor.framework acceleration support whpx Windows Hypervisor Platform acceleration support - rdma Enable RDMA-based migration and PVRDMA support + rdma Enable RDMA-based migration + pvrdma Enable PVRDMA support vde support for vde network netmap support for netmap network linux-aio Linux AIO support cap-ng libcap-ng support attr attr and xattr support - vhost-net vhost-net acceleration support - vhost-crypto vhost-crypto acceleration support + vhost-net vhost-net kernel acceleration support + vhost-vsock virtio sockets device support + vhost-scsi vhost-scsi kernel target support + vhost-crypto vhost-user-crypto backend support + vhost-kernel vhost kernel backend support + vhost-user vhost-user backend support spice spice rbd rados block device (rbd) libiscsi iscsi support @@ -1689,6 +1786,8 @@ disabled with --disable-FEATURE, default is enabled if available: snappy support of snappy compression library bzip2 support of bzip2 compression library (for reading bzip2-compressed dmg images) + lzfse support of lzfse compression library + (for reading lzfse-compressed dmg images) seccomp seccomp support coroutine-pool coroutine freelist (better performance) glusterfs GlusterFS backend @@ -1698,18 +1797,27 @@ disabled with --disable-FEATURE, default is enabled if available: libxml2 for Parallels image format tcmalloc tcmalloc support jemalloc jemalloc support + avx2 AVX2 optimization support replication replication support - vhost-vsock virtio sockets device support opengl opengl support virglrenderer virgl rendering support xfsctl xfsctl support qom-cast-debug cast debugging support - tools build qemu-io, qemu-nbd and qemu-image tools + tools build qemu-io, qemu-nbd and qemu-img tools vxhs Veritas HyperScale vDisk backend support + bochs bochs image format support + cloop cloop image format support + dmg dmg image format support + qcow1 qcow v1 image format support + vdi vdi image format support + vvfat vvfat image format support + qed qed image format support + parallels parallels image format support + sheepdog sheepdog block driver support crypto-afalg Linux AF_ALG crypto backend driver - vhost-user vhost-user support capstone capstone disassembler support debug-mutex mutex debugging support + libpmem libpmem support NOTE: The object files are built at the place where configure is launched EOF @@ -1727,6 +1835,9 @@ if ! $python -c 'import sys; sys.exit(sys.version_info < (2,7))'; then "Use --python=/path/to/python to specify a supported Python." fi +# Preserve python version since some functionality is dependent on it +python_version=$($python -V 2>&1 | sed -e 's/Python\ //') + # Suppress writing compiled files python="$python -B" @@ -1757,8 +1868,8 @@ fi # Consult white-list to determine whether to enable werror # by default. Only enable by default for git builds if test -z "$werror" ; then - if test -d "$source_path/.git" -a \ - \( "$linux" = "yes" -o "$mingw32" = "yes" \) ; then + if test -e "$source_path/.git" && \ + { test "$linux" = "yes" || test "$mingw32" = "yes"; }; then werror="yes" else werror="no" @@ -1773,13 +1884,37 @@ if test "$bogus_os" = "yes"; then error_exit "Unrecognized host OS (uname -s reports '$(uname -s)')" fi +# Check whether the compiler matches our minimum requirements: +cat > $TMPC << EOF +#if defined(__clang_major__) && defined(__clang_minor__) +# ifdef __apple_build_version__ +# if __clang_major__ < 5 || (__clang_major__ == 5 && __clang_minor__ < 1) +# error You need at least XCode Clang v5.1 to compile QEMU +# endif +# else +# if __clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 4) +# error You need at least Clang v3.4 to compile QEMU +# endif +# endif +#elif defined(__GNUC__) && defined(__GNUC_MINOR__) +# if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 8) +# error You need at least GCC v4.8 to compile QEMU +# endif +#else +# error You either need GCC or Clang to compiler QEMU +#endif +int main (void) { return 0; } +EOF +if ! compile_prog "" "" ; then + error_exit "You need at least GCC v4.8 or Clang v3.4 (or XCode Clang v5.1)" +fi + gcc_flags="-Wold-style-declaration -Wold-style-definition -Wtype-limits" gcc_flags="-Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers $gcc_flags" gcc_flags="-Wno-missing-include-dirs -Wempty-body -Wnested-externs $gcc_flags" gcc_flags="-Wendif-labels -Wno-shift-negative-value $gcc_flags" gcc_flags="-Wno-initializer-overrides -Wexpansion-to-defined $gcc_flags" gcc_flags="-Wno-string-plus-int $gcc_flags" -gcc_flags="-Wno-error=address-of-packed-member $gcc_flags" # Note that we do not add -Werror to gcc_flags here, because that would # enable it for all configure tests. If a configure test failed due # to -Werror this would just silently disable some features, @@ -1844,21 +1979,7 @@ else QEMU_CFLAGS="$QEMU_CFLAGS -Wno-missing-braces" fi -# Workaround for http://gcc.gnu.org/PR55489. Happens with -fPIE/-fPIC and -# large functions that use global variables. The bug is in all releases of -# GCC, but it became particularly acute in 4.6.x and 4.7.x. It is fixed in -# 4.7.3 and 4.8.0. We should be able to delete this at the end of 2013. -cat > $TMPC << EOF -#if __GNUC__ == 4 && (__GNUC_MINOR__ == 6 || (__GNUC_MINOR__ == 7 && __GNUC_PATCHLEVEL__ <= 2)) -int main(void) { return 0; } -#else -#error No bug in this compiler. -#endif -EOF -if compile_prog "-Werror -fno-gcse" "" ; then - TRANSLATE_OPT_CFLAGS=-fno-gcse -fi - +# Static linking is not possible with modules or PIE if test "$static" = "yes" ; then if test "$modules" = "yes" ; then error_exit "static and modules are mutually incompatible" @@ -2083,6 +2204,45 @@ else l2tpv3=no fi +######################################### +# vhost interdependencies and host support + +# vhost backends +test "$vhost_user" = "" && vhost_user=yes +if test "$vhost_user" = "yes" && test "$mingw32" = "yes"; then + error_exit "vhost-user isn't available on win32" +fi +test "$vhost_kernel" = "" && vhost_kernel=$linux +if test "$vhost_kernel" = "yes" && test "$linux" != "yes"; then + error_exit "vhost-kernel is only available on Linux" +fi + +# vhost-kernel devices +test "$vhost_scsi" = "" && vhost_scsi=$vhost_kernel +if test "$vhost_scsi" = "yes" && test "$vhost_kernel" != "yes"; then + error_exit "--enable-vhost-scsi requires --enable-vhost-kernel" +fi +test "$vhost_vsock" = "" && vhost_vsock=$vhost_kernel +if test "$vhost_vsock" = "yes" && test "$vhost_kernel" != "yes"; then + error_exit "--enable-vhost-vsock requires --enable-vhost-kernel" +fi + +# vhost-user backends +test "$vhost_net_user" = "" && vhost_net_user=$vhost_user +if test "$vhost_net_user" = "yes" && test "$vhost_user" = "no"; then + error_exit "--enable-vhost-net-user requires --enable-vhost-user" +fi +test "$vhost_crypto" = "" && vhost_crypto=$vhost_user +if test "$vhost_crypto" = "yes" && test "$vhost_user" = "no"; then + error_exit "--enable-vhost-crypto requires --enable-vhost-user" +fi + +# OR the vhost-kernel and vhost-user values for simplicity +if test "$vhost_net" = ""; then + test "$vhost_net_user" = "yes" && vhost_net=yes + test "$vhost_kernel" = "yes" && vhost_net=yes +fi + ########################################## # MinGW / Mingw-w64 localtime_r/gmtime_r check @@ -2140,23 +2300,6 @@ EOF fi fi -######################################### -# zlib check - -if test "$zlib" != "no" ; then - cat > $TMPC << EOF -#include -int main(void) { zlibVersion(); return 0; } -EOF - if compile_prog "" "-lz" ; then - : - else - error_exit "zlib check failed" \ - "Make sure to have the zlib libs and headers installed." - fi -fi -LIBS="$LIBS -lz" - ########################################## # lzo check @@ -2213,16 +2356,31 @@ EOF fi fi +########################################## +# lzfse check + +if test "$lzfse" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { lzfse_decode_scratch_size(); return 0; } +EOF + if compile_prog "" "-llzfse" ; then + lzfse="yes" + else + if test "$lzfse" = "yes"; then + feature_not_found "lzfse" "Install lzfse devel" + fi + lzfse="no" + fi +fi + ########################################## # libseccomp check +libseccomp_minver="2.2.0" if test "$seccomp" != "no" ; then case "$cpu" in - i386|x86_64) - libseccomp_minver="2.1.0" - ;; - mips) - libseccomp_minver="2.2.0" + i386|x86_64|mips) ;; arm|aarch64) libseccomp_minver="2.2.3" @@ -2273,7 +2431,6 @@ if test "$xen" != "no" ; then fi QEMU_CFLAGS="$QEMU_CFLAGS $($pkg_config --cflags $xen_pc)" libs_softmmu="$($pkg_config --libs $xen_pc) $libs_softmmu" - LDFLAGS="$($pkg_config --libs $xen_pc) $LDFLAGS" else xen_libs="-lxenstore -lxenctrl -lxenguest" @@ -2388,7 +2545,6 @@ int main(void) { xenforeignmemory_handle *xfmem; xenevtchn_handle *xe; xengnttab_handle *xg; - xen_domain_handle_t handle; xengnttab_grant_copy_segment_t* seg = NULL; xs_daemon_open(); @@ -2398,7 +2554,6 @@ int main(void) { 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_domain_create(xc, 0, handle, 0, NULL, NULL); xfmem = xenforeignmemory_open(0, 0); xenforeignmemory_map(xfmem, 0, 0, 0, 0, 0); @@ -2440,7 +2595,6 @@ int main(void) { xenforeignmemory_handle *xfmem; xenevtchn_handle *xe; xengnttab_handle *xg; - xen_domain_handle_t handle; xs_daemon_open(); @@ -2449,7 +2603,6 @@ int main(void) { 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_domain_create(xc, 0, handle, 0, NULL, NULL); xfmem = xenforeignmemory_open(0, 0); xenforeignmemory_map(xfmem, 0, 0, 0, 0, 0); @@ -2467,21 +2620,6 @@ EOF then xen_ctrl_version=40701 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=40700 - xen=yes # Xen 4.6 elif @@ -2592,12 +2730,6 @@ if test "$xen_pci_passthrough" != "no"; then fi 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 - ########################################## # Windows Hypervisor Platform accelerator (WHPX) check if test "$whpx" != "no" ; then @@ -2636,24 +2768,9 @@ fi # GTK probe if test "$gtk" != "no"; then - if test "$gtkabi" = ""; then - # The GTK ABI was not specified explicitly, so try whether 3.0 is available. - # Use 2.0 as a fallback if that is available. - if $pkg_config --exists "gtk+-3.0 >= 3.0.0"; then - gtkabi=3.0 - elif $pkg_config --exists "gtk+-2.0 >= 2.18.0"; then - gtkabi=2.0 - else - gtkabi=3.0 - fi - fi - gtkpackage="gtk+-$gtkabi" - gtkx11package="gtk+-x11-$gtkabi" - if test "$gtkabi" = "3.0" ; then - gtkversion="3.0.0" - else - gtkversion="2.18.0" - fi + gtkpackage="gtk+-3.0" + gtkx11package="gtk+-x11-3.0" + gtkversion="3.14.0" if $pkg_config --exists "$gtkpackage >= $gtkversion"; then gtk_cflags=$($pkg_config --cflags $gtkpackage) gtk_libs=$($pkg_config --libs $gtkpackage) @@ -2675,79 +2792,28 @@ fi ########################################## # GNUTLS probe -gnutls_works() { - # Unfortunately some distros have bad pkg-config information for gnutls - # such that it claims to exist but you get a compiler error if you try - # to use the options returned by --libs. Specifically, Ubuntu for --static - # builds doesn't work: - # https://bugs.launchpad.net/ubuntu/+source/gnutls26/+bug/1478035 - # - # So sanity check the cflags/libs before assuming gnutls can be used. - if ! $pkg_config --exists "gnutls"; then - return 1 - fi - - write_c_skeleton - compile_prog "$($pkg_config --cflags gnutls)" "$($pkg_config --libs gnutls)" -} - -gnutls_gcrypt=no -gnutls_nettle=no if test "$gnutls" != "no"; then - if gnutls_works; then + if $pkg_config --exists "gnutls >= 3.1.18"; then gnutls_cflags=$($pkg_config --cflags gnutls) gnutls_libs=$($pkg_config --libs gnutls) libs_softmmu="$gnutls_libs $libs_softmmu" libs_tools="$gnutls_libs $libs_tools" QEMU_CFLAGS="$QEMU_CFLAGS $gnutls_cflags" gnutls="yes" - - # 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 - elif $pkg_config --exists 'gnutls >= 2.12'; then - case $($pkg_config --libs --static gnutls) in - *gcrypt*) - gnutls_gcrypt=yes - gnutls_nettle=no - ;; - *nettle*) - gnutls_gcrypt=no - gnutls_nettle=yes - ;; - *) - gnutls_gcrypt=yes - gnutls_nettle=no - ;; - esac - else - gnutls_gcrypt=yes - gnutls_nettle=no - fi elif test "$gnutls" = "yes"; then - feature_not_found "gnutls" "Install gnutls devel" + feature_not_found "gnutls" "Install gnutls devel >= 3.1.18" else gnutls="no" - gnutls_rnd="no" fi -else - 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 +# explicitly if test -z "$gcrypt" then - if test "$nettle" = "yes" || test "$gnutls_nettle" = "yes" + if test "$nettle" = "yes" then gcrypt="no" fi @@ -2755,16 +2821,16 @@ 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 +# explicitly if test -z "$nettle" then - if test "$gcrypt" = "yes" || test "$gnutls_gcrypt" = "yes" + if test "$gcrypt" = "yes" then nettle="no" fi fi -has_libgcrypt_config() { +has_libgcrypt() { if ! has "libgcrypt-config" then return 1 @@ -2779,11 +2845,42 @@ has_libgcrypt_config() { fi fi + maj=`libgcrypt-config --version | awk -F . '{print $1}'` + min=`libgcrypt-config --version | awk -F . '{print $2}'` + + if test $maj != 1 || test $min -lt 5 + then + return 1 + fi + return 0 } + +if test "$nettle" != "no"; then + if $pkg_config --exists "nettle >= 2.7.1"; then + nettle_cflags=$($pkg_config --cflags nettle) + nettle_libs=$($pkg_config --libs nettle) + nettle_version=$($pkg_config --modversion nettle) + libs_softmmu="$nettle_libs $libs_softmmu" + libs_tools="$nettle_libs $libs_tools" + QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" + nettle="yes" + + if test -z "$gcrypt"; then + gcrypt="no" + fi + else + if test "$nettle" = "yes"; then + feature_not_found "nettle" "Install nettle devel >= 2.7.1" + else + nettle="no" + fi + fi +fi + if test "$gcrypt" != "no"; then - if has_libgcrypt_config; then + if has_libgcrypt; then gcrypt_cflags=$(libgcrypt-config --cflags) gcrypt_libs=$(libgcrypt-config --libs) # Debian has remove -lgpg-error from libgcrypt-config @@ -2797,22 +2894,6 @@ if test "$gcrypt" != "no"; then 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 cat > $TMPC << EOF #include @@ -2828,7 +2909,7 @@ EOF fi else if test "$gcrypt" = "yes"; then - feature_not_found "gcrypt" "Install gcrypt devel" + feature_not_found "gcrypt" "Install gcrypt devel >= 1.5.0" else gcrypt="no" fi @@ -2836,36 +2917,6 @@ EOF fi -if test "$nettle" != "no"; then - if $pkg_config --exists "nettle"; then - nettle_cflags=$($pkg_config --cflags nettle) - nettle_libs=$($pkg_config --libs nettle) - nettle_version=$($pkg_config --modversion nettle) - libs_softmmu="$nettle_libs $libs_softmmu" - libs_tools="$nettle_libs $libs_tools" - QEMU_CFLAGS="$QEMU_CFLAGS $nettle_cflags" - nettle="yes" - - cat > $TMPC << EOF -#include -#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 - 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" @@ -2885,6 +2936,33 @@ else fi +########################################## +# PAM probe + +if test "$auth_pam" != "no"; then + cat > $TMPC < +#include +int main(void) { + const char *service_name = "qemu"; + const char *user = "frank"; + const struct pam_conv *pam_conv = NULL; + pam_handle_t *pamh = NULL; + pam_start(service_name, user, pam_conv, &pamh); + return 0; +} +EOF + if compile_prog "" "-lpam" ; then + auth_pam=yes + else + if test "$auth_pam" = "yes"; then + feature_not_found "PAM" "Install PAM development package" + else + auth_pam=no + fi + fi +fi + ########################################## # getifaddrs (for tests/test-io-channel-socket ) @@ -2897,16 +2975,11 @@ fi # VTE probe if test "$vte" != "no"; then - if test "$gtkabi" = "3.0"; then - vteminversion="0.32.0" - if $pkg_config --exists "vte-2.91"; then - vtepackage="vte-2.91" - else - vtepackage="vte-2.90" - fi + vteminversion="0.32.0" + if $pkg_config --exists "vte-2.91"; then + vtepackage="vte-2.91" else - vtepackage="vte" - vteminversion="0.24.0" + vtepackage="vte-2.90" fi if $pkg_config --exists "$vtepackage >= $vteminversion"; then vte_cflags=$($pkg_config --cflags $vtepackage) @@ -2914,11 +2987,7 @@ if test "$vte" != "no"; then vteversion=$($pkg_config --modversion $vtepackage) vte="yes" elif test "$vte" = "yes"; then - if test "$gtkabi" = "3.0"; then - feature_not_found "vte" "Install libvte-2.90/2.91 devel" - else - feature_not_found "vte" "Install libvte devel" - fi + feature_not_found "vte" "Install libvte-2.90/2.91 devel" else vte="no" fi @@ -2932,37 +3001,11 @@ fi sdl_probe () { - sdl_too_old=no - if test "$sdlabi" = ""; then - if $pkg_config --exists "sdl2"; then - sdlabi=2.0 - elif $pkg_config --exists "sdl"; then - sdlabi=1.2 - else - sdlabi=2.0 - fi - fi - - if test $sdlabi = "2.0"; then - sdl_config=$sdl2_config - sdlname=sdl2 - sdlconfigname=sdl2_config - elif test $sdlabi = "1.2"; then - sdlname=sdl - sdlconfigname=sdl_config - else - error_exit "Unknown sdlabi $sdlabi, must be 1.2 or 2.0" - fi - - if test "$(basename $sdl_config)" != $sdlconfigname && ! has ${sdl_config}; then - sdl_config=$sdlconfigname - fi - - if $pkg_config $sdlname --exists; then - sdlconfig="$pkg_config $sdlname" + if $pkg_config sdl2 --exists; then + sdlconfig="$pkg_config sdl2" sdlversion=$($sdlconfig --modversion 2>/dev/null) elif has ${sdl_config}; then - sdlconfig="$sdl_config" + sdlconfig="$sdl2_config" sdlversion=$($sdlconfig --version) else if test "$sdl" = "yes" ; then @@ -2984,8 +3027,8 @@ EOF sdl_cflags=$($sdlconfig --cflags 2>/dev/null) sdl_cflags="$sdl_cflags -Wno-undef" # workaround 2.0.8 bug if test "$static" = "yes" ; then - if $pkg_config $sdlname --exists; then - sdl_libs=$($pkg_config $sdlname --static --libs 2>/dev/null) + if $pkg_config sdl2 --exists; then + sdl_libs=$($pkg_config sdl2 --static --libs 2>/dev/null) else sdl_libs=$($sdlconfig --static-libs 2>/dev/null) fi @@ -2993,14 +3036,10 @@ EOF sdl_libs=$($sdlconfig --libs 2>/dev/null) fi if compile_prog "$sdl_cflags" "$sdl_libs" ; then - if test $(echo $sdlversion | sed 's/[^0-9]//g') -lt 121 ; then - sdl_too_old=yes - else - sdl=yes - fi + sdl=yes # static link with sdl ? (note: sdl.pc's --static --libs is broken) - if test "$sdl" = "yes" -a "$static" = "yes" ; then + if test "$sdl" = "yes" && test "$static" = "yes" ; then if test $? = 0 && echo $sdl_libs | grep -- -laa > /dev/null; then sdl_libs="$sdl_libs $(aalib-config --static-libs 2>/dev/null)" sdl_cflags="$sdl_cflags $(aalib-config --cflags 2>/dev/null)" @@ -3013,16 +3052,49 @@ EOF fi # static link else # sdl not found if test "$sdl" = "yes" ; then - feature_not_found "sdl" "Install SDL devel" + feature_not_found "sdl" "Install SDL2 devel" fi sdl=no fi # sdl compile test } +sdl_image_probe () +{ + if test "$sdl_image" != "no" ; then + if $pkg_config SDL2_image --exists; then + if test "$static" = "yes"; then + sdl_image_libs=$($pkg_config SDL2_image --libs --static 2>/dev/null) + else + sdl_image_libs=$($pkg_config SDL2_image --libs 2>/dev/null) + fi + sdl_image_cflags=$($pkg_config SDL2_image --cflags 2>/dev/null) + sdl_image=yes + + sdl_cflags="$sdl_cflags $sdl_image_cflags" + sdl_libs="$sdl_libs $sdl_image_libs" + else + if test "$sdl_image" = "yes" ; then + feature_not_found "sdl_image" "Install SDL Image devel" + else + sdl_image=no + fi + fi + fi +} + if test "$sdl" != "no" ; then sdl_probe fi +if test "$sdl" = "yes" ; then + sdl_image_probe +else + if test "$sdl_image" = "yes"; then + echo "warning: SDL Image requested, but SDL is not available, disabling" + fi + sdl_image=no +fi + if test "$sdl" = "yes" ; then cat > $TMPC < @@ -3093,10 +3165,52 @@ EOF fi fi +########################################## +# PVRDMA detection + +cat > $TMPC < + +int +main(void) +{ + char buf = 0; + void *addr = &buf; + addr = mremap(addr, 0, 1, MREMAP_MAYMOVE | MREMAP_FIXED); + + return 0; +} +EOF + +if test "$rdma" = "yes" ; then + case "$pvrdma" in + "") + if compile_prog "" ""; then + pvrdma="yes" + else + pvrdma="no" + fi + ;; + "yes") + if ! compile_prog "" ""; then + error_exit "PVRDMA is not supported since mremap is not implemented" + fi + pvrdma="yes" + ;; + "no") + pvrdma="no" + ;; + esac +else + if test "$pvrdma" = "yes" ; then + error_exit "PVRDMA requires rdma suppport" + fi + pvrdma="no" +fi ########################################## # VNC SASL detection -if test "$vnc" = "yes" -a "$vnc_sasl" != "no" ; then +if test "$vnc" = "yes" && test "$vnc_sasl" != "no" ; then cat > $TMPC < #include @@ -3119,7 +3233,7 @@ fi ########################################## # VNC JPEG detection -if test "$vnc" = "yes" -a "$vnc_jpeg" != "no" ; then +if test "$vnc" = "yes" && test "$vnc_jpeg" != "no" ; then cat > $TMPC < #include @@ -3141,7 +3255,7 @@ fi ########################################## # VNC PNG detection -if test "$vnc" = "yes" -a "$vnc_png" != "no" ; then +if test "$vnc" = "yes" && test "$vnc_png" != "no" ; then cat > $TMPC < #include @@ -3186,20 +3300,6 @@ if test "$xkbcommon" != "no" ; then fi fi -########################################## -# fnmatch() probe, used for ACL routines -fnmatch="no" -cat > $TMPC << EOF -#include -int main(void) -{ - fnmatch("foo", "foo", 0); - return 0; -} -EOF -if compile_prog "" "" ; then - fnmatch="yes" -fi ########################################## # xfsctl() probe, used for file-posix.c @@ -3301,39 +3401,40 @@ fi ########################################## # Sound support libraries probe -audio_drv_probe() -{ - drv=$1 - hdr=$2 - lib=$3 - exp=$4 - cfl=$5 - cat > $TMPC << EOF -#include <$hdr> -int main(void) { $exp } -EOF - if compile_prog "$cfl" "$lib" ; then - : - else - error_exit "$drv check failed" \ - "Make sure to have the $drv libs and headers installed." - fi -} - audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/,/ /g') for drv in $audio_drv_list; do case $drv in - alsa) - audio_drv_probe $drv alsa/asoundlib.h -lasound \ - "return snd_pcm_close((snd_pcm_t *)0);" - alsa_libs="-lasound" + alsa | try-alsa) + if $pkg_config alsa --exists; then + alsa_libs=$($pkg_config alsa --libs) + if test "$drv" = "try-alsa"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-alsa/alsa/') + fi + else + if test "$drv" = "try-alsa"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-alsa//') + else + error_exit "$drv check failed" \ + "Make sure to have the $drv libs and headers installed." + fi + fi ;; - pa) - audio_drv_probe $drv pulse/pulseaudio.h "-lpulse" \ - "pa_context_set_source_output_volume(NULL, 0, NULL, NULL, NULL); return 0;" - pulse_libs="-lpulse" - audio_pt_int="yes" + pa | try-pa) + if $pkg_config libpulse --exists; then + pulse_libs=$($pkg_config libpulse --libs) + audio_pt_int="yes" + if test "$drv" = "try-pa"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa/pa/') + fi + else + if test "$drv" = "try-pa"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-pa//') + else + error_exit "$drv check failed" \ + "Make sure to have the $drv libs and headers installed." + fi + fi ;; sdl) @@ -3342,6 +3443,14 @@ for drv in $audio_drv_list; do fi ;; + try-sdl) + if test "$sdl" = "no"; then + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-sdl//') + else + audio_drv_list=$(echo "$audio_drv_list" | sed -e 's/try-sdl/sdl/') + fi + ;; + coreaudio) coreaudio_libs="-framework CoreAudio" ;; @@ -3355,10 +3464,6 @@ for drv in $audio_drv_list; do oss_libs="$oss_lib" ;; - wav) - # XXX: Probes for CoreAudio, DirectSound - ;; - *) echo "$audio_possible_drivers" | grep -q "\<$drv\>" || { error_exit "Unknown driver '$drv' selected" \ @@ -3388,8 +3493,52 @@ EOF fi fi +########################################## +# iconv probe +if test "$iconv" != "no" ; then + cat > $TMPC << EOF +#include +int main(void) { + iconv_t conv = iconv_open("WCHAR_T", "UCS-2"); + return conv != (iconv_t) -1; +} +EOF + iconv_prefix_list="/usr/local:/usr" + iconv_lib_list=":-liconv" + IFS=: + for iconv_prefix in $iconv_prefix_list; do + IFS=: + iconv_cflags="-I$iconv_prefix/include" + iconv_ldflags="-L$iconv_prefix/lib" + for iconv_link in $iconv_lib_list; do + unset IFS + iconv_lib="$iconv_ldflags $iconv_link" + echo "looking at iconv in '$iconv_cflags' '$iconv_lib'" >> config.log + if compile_prog "$iconv_cflags" "$iconv_lib" ; then + iconv_found=yes + break + fi + done + if test "$iconv_found" = yes ; then + break + fi + done + if test "$iconv_found" = "yes" ; then + iconv=yes + else + if test "$iconv" = "yes" ; then + feature_not_found "iconv" "Install iconv devel" + fi + iconv=no + fi +fi + ########################################## # curses probe +if test "$iconv" = "no" ; then + # curses will need iconv + curses=no +fi if test "$curses" != "no" ; then if test "$mingw32" = "yes" ; then curses_inc_list="$($pkg_config --cflags ncurses 2>/dev/null):" @@ -3403,14 +3552,17 @@ if test "$curses" != "no" ; then #include #include #include +#include int main(void) { + const char *codeset; wchar_t wch = L'w'; setlocale(LC_ALL, ""); resize_term(0, 0); addwstr(L"wide chars\n"); addnwstr(&wch, 1); add_wch(WACS_DEGREE); - return 0; + codeset = nl_langinfo(CODESET); + return codeset != 0; } EOF IFS=: @@ -3496,7 +3648,7 @@ fi # This workaround is required due to a bug in pkg-config file for glib as it # doesn't define GLIB_STATIC_COMPILATION for pkg-config --static -if test "$static" = yes -a "$mingw32" = yes; then +if test "$static" = yes && test "$mingw32" = yes; then QEMU_CFLAGS="-DGLIB_STATIC_COMPILATION $QEMU_CFLAGS" fi @@ -3512,6 +3664,14 @@ for i in $glib_modules; do fi done +if $pkg_config --atleast-version=$glib_req_ver gio-2.0; then + gio=yes + gio_cflags=$($pkg_config --cflags gio-2.0) + gio_libs=$($pkg_config --libs gio-2.0) +else + gio=no +fi + # 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 @@ -3536,12 +3696,6 @@ if ! compile_prog "$CFLAGS" "$LIBS" ; then "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 - glib_subprocess=no -fi - # Silence clang 3.5.0 warnings about glib attribute __alloc_size__ usage cat > $TMPC << EOF #include @@ -3554,6 +3708,29 @@ if ! compile_prog "$glib_cflags -Werror" "$glib_libs" ; then fi fi +######################################### +# zlib check + +if test "$zlib" != "no" ; then + if $pkg_config --exists zlib; then + zlib_cflags=$($pkg_config --cflags zlib) + zlib_libs=$($pkg_config --libs zlib) + QEMU_CFLAGS="$zlib_cflags $QEMU_CFLAGS" + LIBS="$zlib_libs $LIBS" + else + cat > $TMPC << EOF +#include +int main(void) { zlibVersion(); return 0; } +EOF + if compile_prog "" "-lz" ; then + LIBS="$LIBS -lz" + else + error_exit "zlib check failed" \ + "Make sure to have the zlib libs and headers installed." + fi + fi +fi + ########################################## # SHA command probe for modules if test "$modules" = yes; then @@ -3572,7 +3749,7 @@ fi ########################################## # pixman support probe -if test "$want_tools" = "no" -a "$softmmu" = "no"; then +if test "$want_tools" = "no" && test "$softmmu" = "no"; then pixman_cflags= pixman_libs= elif $pkg_config --atleast-version=0.21.8 pixman-1 > /dev/null 2>&1; then @@ -3587,6 +3764,7 @@ fi # libmpathpersist probe if test "$mpath" != "no" ; then + # probe for the new API cat > $TMPC < #include @@ -3608,8 +3786,26 @@ int main(void) { EOF if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then mpathpersist=yes + mpathpersist_new_api=yes else - mpathpersist=no + # probe for the old API + cat > $TMPC < +#include +unsigned mpath_mx_alloc_len = 1024; +int logsink; +int main(void) { + struct udev *udev = udev_new(); + mpath_lib_init(udev); + return 0; +} +EOF + if compile_prog "" "-ludev -lmultipath -lmpathpersist" ; then + mpathpersist=yes + mpathpersist_new_api=no + else + mpathpersist=no + fi fi else mpathpersist=no @@ -3668,13 +3864,13 @@ else done fi -if test "$mingw32" != yes -a "$pthread" = no; then +if test "$mingw32" != yes && test "$pthread" = no; then error_exit "pthread check failed" \ "Make sure to have the pthread libs and headers installed." fi -# check for pthread_setname_np -pthread_setname_np=no +# check for pthread_setname_np with thread id +pthread_setname_np_w_tid=no cat > $TMPC << EOF #include @@ -3688,7 +3884,24 @@ int main(void) } EOF if compile_prog "" "$pthread_lib" ; then - pthread_setname_np=yes + pthread_setname_np_w_tid=yes +fi + +# check for pthread_setname_np without thread id +pthread_setname_np_wo_tid=no +cat > $TMPC << EOF +#include + +static void *f(void *p) { pthread_setname_np("QEMU"); } +int main(void) +{ + pthread_t thread; + pthread_create(&thread, 0, f, 0); + return 0; +} +EOF +if compile_prog "" "$pthread_lib" ; then + pthread_setname_np_wo_tid=yes fi ########################################## @@ -3776,20 +3989,20 @@ EOF fi ########################################## -# TPM passthrough is only on x86 Linux +# TPM emulation is only on POSIX -if test "$targetos" = Linux && test "$cpu" = i386 -o "$cpu" = x86_64; then - tpm_passthrough=$tpm -else - tpm_passthrough=no +if test "$tpm" = ""; then + if test "$mingw32" = "yes"; then + tpm=no + else + tpm=yes + fi +elif test "$tpm" = "yes"; then + if test "$mingw32" = "yes" ; then + error_exit "TPM emulation only available on POSIX systems" + fi fi -# TPM emulator is for all posix systems -if test "$mingw32" != "yes"; then - tpm_emulator=$tpm -else - tpm_emulator=no -fi ########################################## # attr probe @@ -3874,7 +4087,7 @@ if test "$fdt" != "no" ; then cat > $TMPC << EOF #include #include -int main(void) { fdt_first_subnode(0, 0); return 0; } +int main(void) { fdt_check_full(NULL, 0); return 0; } EOF if compile_prog "" "$fdt_libs" ; then # system DTC is good - use it @@ -3944,6 +4157,16 @@ EOF fi fi +if test "$opengl" = "yes" && test "$have_x11" = "yes"; then + for target in $target_list; do + case $target in + lm32-softmmu) # milkymist-tmu2 requires X11 and OpenGL + need_x11=yes + ;; + esac + done +fi + ########################################## # libxml2 probe if test "$libxml2" != "no" ; then @@ -3976,6 +4199,38 @@ if test "$glusterfs" != "no" ; then glusterfs_fallocate="yes" glusterfs_zerofill="yes" fi + cat > $TMPC << EOF +#include + +int +main(void) +{ + /* new glfs_ftruncate() passes two additional args */ + return glfs_ftruncate(NULL, 0, NULL, NULL); +} +EOF + if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then + glusterfs_ftruncate_has_stat="yes" + fi + cat > $TMPC << EOF +#include + +/* new glfs_io_cbk() passes two additional glfs_stat structs */ +static void +glusterfs_iocb(glfs_fd_t *fd, ssize_t ret, struct glfs_stat *prestat, struct glfs_stat *poststat, void *data) +{} + +int +main(void) +{ + glfs_io_cbk iocb = &glusterfs_iocb; + iocb(NULL, 0 , NULL, NULL, NULL); + return 0; +} +EOF + if compile_prog "$glusterfs_cflags" "$glusterfs_libs" ; then + glusterfs_iocb_has_stat="yes" + fi else if test "$glusterfs" = "yes" ; then feature_not_found "GlusterFS backend support" \ @@ -4166,6 +4421,17 @@ if compile_prog "" "" ; then signalfd=yes fi +# check if optreset global is declared by +optreset="no" +cat > $TMPC << EOF +#include +int main(void) { return optreset; } +EOF + +if compile_prog "" "" ; then + optreset=yes +fi + # check if eventfd is supported eventfd=no cat > $TMPC << EOF @@ -4194,7 +4460,29 @@ if compile_prog "" "" ; then memfd=yes fi +# check for usbfs +have_usbfs=no +if test "$linux_user" = "yes"; then + cat > $TMPC << EOF +#include +#ifndef USBDEVFS_GET_CAPABILITIES +#error "USBDEVFS_GET_CAPABILITIES undefined" +#endif + +#ifndef USBDEVFS_DISCONNECT_CLAIM +#error "USBDEVFS_DISCONNECT_CLAIM undefined" +#endif + +int main(void) +{ + return 0; +} +EOF + if compile_prog "" ""; then + have_usbfs=yes + fi +fi # check for fallocate fallocate=no @@ -4449,13 +4737,24 @@ if compile_prog "" "" ; then syncfs=yes fi +# Check we have a new enough version of sphinx-build +has_sphinx_build() { + # This is a bit awkward but works: create a trivial document and + # try to run it with our configuration file (which enforces a + # version requirement). This will fail if either + # sphinx-build doesn't exist at all or if it is too old. + mkdir -p "$TMPDIR1/sphinx" + touch "$TMPDIR1/sphinx/index.rst" + sphinx-build -c "$source_path/docs" -b html "$TMPDIR1/sphinx" "$TMPDIR1/sphinx/out" >/dev/null 2>&1 +} + # Check if tools are available to build documentation. if test "$docs" != "no" ; then - if has makeinfo && has pod2man; then + if has makeinfo && has pod2man && has_sphinx_build; then docs=yes else if test "$docs" = "yes" ; then - feature_not_found "docs" "Install texinfo and Perl/perl-podlators" + feature_not_found "docs" "Install texinfo, Perl/perl-podlators and python-sphinx" fi docs=no fi @@ -4536,9 +4835,17 @@ elif compile_prog "" "$pthread_lib -lrt" ; then libs_qga="$libs_qga -lrt" fi -if test "$darwin" != "yes" -a "$mingw32" != "yes" -a "$solaris" != yes -a \ - "$haiku" != "yes" ; then +# Check whether we need to link libutil for openpty() +cat > $TMPC << EOF +extern int openpty(int *am, int *as, char *name, void *termp, void *winp); +int main(void) { return openpty(0, 0, 0, 0, 0); } +EOF + +if ! compile_prog "" "" ; then + if compile_prog "" "-lutil" ; then libs_softmmu="-lutil $libs_softmmu" + libs_tools="-lutil $libs_tools" + fi fi ########################################## @@ -4550,7 +4857,7 @@ int main(void) { spice_server_new(); return 0; } EOF spice_cflags=$($pkg_config --cflags spice-protocol spice-server 2>/dev/null) spice_libs=$($pkg_config --libs spice-protocol spice-server 2>/dev/null) - if $pkg_config --atleast-version=0.12.0 spice-server && \ + if $pkg_config --atleast-version=0.12.5 spice-server && \ $pkg_config --atleast-version=0.12.3 spice-protocol && \ compile_prog "$spice_cflags" "$spice_libs" ; then spice="yes" @@ -4561,7 +4868,7 @@ EOF else if test "$spice" = "yes" ; then feature_not_found "spice" \ - "Install spice-server(>=0.12.0) and spice-protocol(>=0.12.3) devel" + "Install spice-server(>=0.12.5) and spice-protocol(>=0.12.3) devel" fi spice="no" fi @@ -4612,7 +4919,8 @@ fi ########################################## # check if we have VSS SDK headers for win -if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$vss_win32_sdk" != "no" ; then +if test "$mingw32" = "yes" && test "$guest_agent" != "no" && \ + test "$vss_win32_sdk" != "no" ; then case "$vss_win32_sdk" in "") vss_win32_include="-isystem $source_path" ;; *\ *) # The SDK is installed in "Program Files" by default, but we cannot @@ -4651,7 +4959,8 @@ fi # VSS provider from the source. It is usually unnecessary because the # pre-compiled .tlb file is included. -if test "$mingw32" = "yes" -a "$guest_agent" != "no" -a "$guest_agent_with_vss" = "yes" ; then +if test "$mingw32" = "yes" && test "$guest_agent" != "no" && \ + test "$guest_agent_with_vss" = "yes" ; then if test -z "$win_sdk"; then programfiles="$PROGRAMFILES" test -n "$PROGRAMW6432" && programfiles="$PROGRAMW6432" @@ -4667,7 +4976,7 @@ fi ########################################## # check if mingw environment provides a recent ntddscsi.h -if test "$mingw32" = "yes" -a "$guest_agent" != "no"; then +if test "$mingw32" = "yes" && test "$guest_agent" != "no"; then cat > $TMPC << EOF #include #include @@ -4681,7 +4990,7 @@ int main(void) { EOF if compile_prog "" "" ; then guest_agent_ntddscsi=yes - libs_qga="-lsetupapi $libs_qga" + libs_qga="-lsetupapi -lcfgmgr32 $libs_qga" fi fi @@ -4714,7 +5023,7 @@ case "$capstone" in "" | yes) if $pkg_config capstone; then capstone=system - elif test -e "${source_path}/.git" -a $git_update = 'yes' ; then + elif test -e "${source_path}/.git" && test $git_update = 'yes' ; then capstone=git elif test -e "${source_path}/capstone/Makefile" ; then capstone=internal @@ -5086,7 +5395,7 @@ fi # There is no point enabling this if cpuid.h is not usable, # since we won't be able to select the new routines. -if test $cpuid_h = yes; then +if test "$cpuid_h" = "yes" && test "$avx2_opt" != "no"; then cat > $TMPC << EOF #pragma GCC push_options #pragma GCC target("avx2") @@ -5100,6 +5409,8 @@ int main(int argc, char *argv[]) { return bar(argv[0]); } EOF if compile_object "" ; then avx2_opt="yes" + else + avx2_opt="no" fi fi @@ -5108,11 +5419,6 @@ fi int128=no cat > $TMPC << EOF -#if defined(__clang_major__) && defined(__clang_minor__) -# if ((__clang_major__ < 3) || (__clang_major__ == 3) && (__clang_minor__ < 2)) -# error __int128_t does not work in CLANG before 3.2 -# endif -#endif __int128_t a; __uint128_t b; int main (void) { @@ -5146,6 +5452,21 @@ EOF fi fi +cmpxchg128=no +if test "$int128" = yes && test "$atomic128" = no; then + cat > $TMPC << EOF +int main(void) +{ + unsigned __int128 x = 0, y = 0; + __sync_val_compare_and_swap_16(&x, y, x); + return 0; +} +EOF + if compile_prog "" "" ; then + cmpxchg128=yes + fi +fi + ######################################### # See if 64-bit atomic operations are supported. # Note that without __atomic builtins, we can only @@ -5574,6 +5895,68 @@ if has "docker"; then docker=$($python $source_path/tests/docker/docker.py probe) fi +########################################## +# check for libpmem + +if test "$libpmem" != "no"; then + if $pkg_config --exists "libpmem"; then + libpmem="yes" + libpmem_libs=$($pkg_config --libs libpmem) + libpmem_cflags=$($pkg_config --cflags libpmem) + libs_softmmu="$libs_softmmu $libpmem_libs" + QEMU_CFLAGS="$QEMU_CFLAGS $libpmem_cflags" + else + if test "$libpmem" = "yes" ; then + feature_not_found "libpmem" "Install nvml or pmdk" + fi + libpmem="no" + fi +fi + +########################################## +# check for slirp + +case "$slirp" in + "" | yes) + if $pkg_config slirp; then + slirp=system + elif test -e "${source_path}/slirp/Makefile" ; then + slirp=internal + elif test -z "$slirp" ; then + slirp=no + else + feature_not_found "slirp" "Install slirp devel or git submodule" + fi + ;; + + system) + if ! $pkg_config slirp; then + feature_not_found "slirp" "Install slirp devel" + fi + ;; +esac + +case "$slirp" in + internal) + mkdir -p slirp + slirp_cflags="-I\$(SRC_PATH)/slirp/src -I\$(BUILD_DIR)/slirp/src" + slirp_libs="-L\$(BUILD_DIR)/slirp -lslirp" + ;; + + system) + slirp_version=$($pkg_config --modversion slirp 2>/dev/null) + slirp_cflags=$($pkg_config --cflags slirp 2>/dev/null) + slirp_libs=$($pkg_config --libs slirp 2>/dev/null) + ;; + + no) + ;; + *) + error_exit "Unknown state for slirp: $slirp" + ;; +esac + + ########################################## # End of CC checks # After here, no more $cc or $ld runs @@ -5617,6 +6000,17 @@ if test "$libnfs" != "no" ; then fi fi +########################################## +# Do we have libudev +if test "$libudev" != "no" ; then + if $pkg_config libudev && test "$static" != "yes"; then + libudev="yes" + libudev_libs=$($pkg_config --libs libudev) + else + libudev="no" + fi +fi + # Now we've finished running tests it's OK to add -Werror to the compiler flags if test "$werror" = "yes"; then QEMU_CFLAGS="-Werror $QEMU_CFLAGS" @@ -5644,10 +6038,23 @@ if test "$mingw32" = "yes" ; then done fi +# Disable OpenBSD W^X if available +if test "$tcg" = "yes" && test "$targetos" = "OpenBSD"; then + cat > $TMPC < Your SDL version is too old - please upgrade to have SDL support" -fi - -if test "$gtkabi" = "2.0"; then - echo - echo "WARNING: Use of GTK 2.0 is deprecated and will be removed in" - echo "WARNING: future releases. Please switch to using GTK 3.0" -fi - -if test "$sdlabi" = "1.2"; then - echo - echo "WARNING: Use of SDL 1.2 is deprecated and will be removed in" - echo "WARNING: future releases. Please switch to using SDL 2.0" -fi +echo "libpmem support $libpmem" +echo "libudev $libudev" +echo "default devices $default_devices" if test "$supported_cpu" = "no"; then echo @@ -6107,6 +6519,8 @@ if test "$mingw32" = "no" ; then fi echo "qemu_helperdir=$libexecdir" >> $config_host_mak echo "qemu_localedir=$qemu_localedir" >> $config_host_mak +echo "qemu_icondir=$qemu_icondir" >> $config_host_mak +echo "qemu_desktopdir=$qemu_desktopdir" >> $config_host_mak echo "libs_softmmu=$libs_softmmu" >> $config_host_mak echo "GIT=$git" >> $config_host_mak echo "GIT_SUBMODULES=$git_submodules" >> $config_host_mak @@ -6114,6 +6528,11 @@ echo "GIT_UPDATE=$git_update" >> $config_host_mak echo "ARCH=$ARCH" >> $config_host_mak +if test "$default_devices" = "yes" ; then + echo "CONFIG_MINIKCONF_MODE=--defconfig" >> $config_host_mak +else + echo "CONFIG_MINIKCONF_MODE=--allnoconfig" >> $config_host_mak +fi if test "$debug_tcg" = "yes" ; then echo "CONFIG_DEBUG_TCG=y" >> $config_host_mak fi @@ -6140,7 +6559,7 @@ if test "$mingw32" = "yes" ; then echo "WIN_SDK=\"$win_sdk\"" >> $config_host_mak fi if test "$guest_agent_ntddscsi" = "yes" ; then - echo "CONFIG_QGA_NTDDDISK=y" >> $config_host_mak + echo "CONFIG_QGA_NTDDSCSI=y" >> $config_host_mak fi if test "$guest_agent_msi" = "yes"; then echo "QEMU_GA_MSI_ENABLED=yes" >> $config_host_mak @@ -6175,9 +6594,14 @@ fi if test "$profiler" = "yes" ; then echo "CONFIG_PROFILER=y" >> $config_host_mak fi -if test "$slirp" = "yes" ; then +if test "$slirp" != "no"; then echo "CONFIG_SLIRP=y" >> $config_host_mak echo "CONFIG_SMBD_COMMAND=\"$smbd\"" >> $config_host_mak + echo "SLIRP_CFLAGS=$slirp_cflags" >> $config_host_mak + echo "SLIRP_LIBS=$slirp_libs" >> $config_host_mak +fi +if [ "$slirp" = "internal" ]; then + echo "config-host.h: subdir-slirp" >> $config_host_mak fi if test "$vde" = "yes" ; then echo "CONFIG_VDE=y" >> $config_host_mak @@ -6231,9 +6655,6 @@ if test "$xkbcommon" = "yes" ; then echo "XKBCOMMON_CFLAGS=$xkbcommon_cflags" >> $config_host_mak echo "XKBCOMMON_LIBS=$xkbcommon_libs" >> $config_host_mak fi -if test "$fnmatch" = "yes" ; then - echo "CONFIG_FNMATCH=y" >> $config_host_mak -fi if test "$xfs" = "yes" ; then echo "CONFIG_XFS=y" >> $config_host_mak fi @@ -6251,20 +6672,27 @@ if test "$modules" = "yes"; then echo "CONFIG_STAMP=_$( (echo $qemu_version; echo $pkgversion; cat $0) | $shacmd - | cut -f1 -d\ )" >> $config_host_mak echo "CONFIG_MODULES=y" >> $config_host_mak fi -if test "$have_x11" = "yes" -a "$need_x11" = "yes"; then +if test "$have_x11" = "yes" && test "$need_x11" = "yes"; then echo "CONFIG_X11=y" >> $config_host_mak echo "X11_CFLAGS=$x11_cflags" >> $config_host_mak echo "X11_LIBS=$x11_libs" >> $config_host_mak fi if test "$sdl" = "yes" ; then echo "CONFIG_SDL=m" >> $config_host_mak - echo "CONFIG_SDLABI=$sdlabi" >> $config_host_mak echo "SDL_CFLAGS=$sdl_cflags" >> $config_host_mak echo "SDL_LIBS=$sdl_libs" >> $config_host_mak + if test "$sdl_image" = "yes" ; then + echo "CONFIG_SDL_IMAGE=y" >> $config_host_mak + fi fi if test "$cocoa" = "yes" ; then echo "CONFIG_COCOA=y" >> $config_host_mak fi +if test "$iconv" = "yes" ; then + echo "CONFIG_ICONV=y" >> $config_host_mak + echo "ICONV_CFLAGS=$iconv_cflags" >> $config_host_mak + echo "ICONV_LIBS=$iconv_lib" >> $config_host_mak +fi if test "$curses" = "yes" ; then echo "CONFIG_CURSES=m" >> $config_host_mak echo "CURSES_CFLAGS=$curses_inc" >> $config_host_mak @@ -6285,6 +6713,9 @@ fi if test "$memfd" = "yes" ; then echo "CONFIG_MEMFD=y" >> $config_host_mak fi +if test "$have_usbfs" = "yes" ; then + echo "CONFIG_USBFS=y" >> $config_host_mak +fi if test "$fallocate" = "yes" ; then echo "CONFIG_FALLOCATE=y" >> $config_host_mak fi @@ -6364,44 +6795,39 @@ if test "$bluez" = "yes" ; then echo "CONFIG_BLUEZ=y" >> $config_host_mak echo "BLUEZ_CFLAGS=$bluez_cflags" >> $config_host_mak fi -if test "$glib_subprocess" = "yes" ; then - echo "CONFIG_HAS_GLIB_SUBPROCESS_TESTS=y" >> $config_host_mak -fi if test "$gtk" = "yes" ; then echo "CONFIG_GTK=m" >> $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 "$gio" = "yes" ; then + echo "CONFIG_GIO=y" >> $config_host_mak + echo "GIO_CFLAGS=$gio_cflags" >> $config_host_mak + echo "GIO_LIBS=$gio_libs" >> $config_host_mak +fi echo "CONFIG_TLS_PRIORITY=\"$tls_priority\"" >> $config_host_mak if test "$gnutls" = "yes" ; then echo "CONFIG_GNUTLS=y" >> $config_host_mak fi -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_hmac" = "yes" ; then echo "CONFIG_GCRYPT_HMAC=y" >> $config_host_mak fi - if test "$gcrypt_kdf" = "yes" ; then - echo "CONFIG_GCRYPT_KDF=y" >> $config_host_mak - fi fi 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 "$auth_pam" = "yes" ; then + echo "CONFIG_AUTH_PAM=y" >> $config_host_mak +fi if test "$have_ifaddrs_h" = "yes" ; then echo "HAVE_IFADDRS_H=y" >> $config_host_mak fi @@ -6432,9 +6858,6 @@ 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 @@ -6450,12 +6873,18 @@ if test "$virtfs" = "yes" ; then fi if test "$mpath" = "yes" ; then echo "CONFIG_MPATH=y" >> $config_host_mak + if test "$mpathpersist_new_api" = "yes"; then + echo "CONFIG_MPATH_NEW_API=y" >> $config_host_mak + fi fi if test "$vhost_scsi" = "yes" ; then echo "CONFIG_VHOST_SCSI=y" >> $config_host_mak fi -if test "$vhost_net" = "yes" -a "$vhost_user" = "yes"; then - echo "CONFIG_VHOST_NET_USED=y" >> $config_host_mak +if test "$vhost_net" = "yes" ; then + echo "CONFIG_VHOST_NET=y" >> $config_host_mak +fi +if test "$vhost_net_user" = "yes" ; then + echo "CONFIG_VHOST_NET_USER=y" >> $config_host_mak fi if test "$vhost_crypto" = "yes" ; then echo "CONFIG_VHOST_CRYPTO=y" >> $config_host_mak @@ -6463,6 +6892,9 @@ fi if test "$vhost_vsock" = "yes" ; then echo "CONFIG_VHOST_VSOCK=y" >> $config_host_mak fi +if test "$vhost_kernel" = "yes" ; then + echo "CONFIG_VHOST_KERNEL=y" >> $config_host_mak +fi if test "$vhost_user" = "yes" ; then echo "CONFIG_VHOST_USER=y" >> $config_host_mak fi @@ -6484,6 +6916,9 @@ fi if test "$signalfd" = "yes" ; then echo "CONFIG_SIGNALFD=y" >> $config_host_mak fi +if test "$optreset" = "yes" ; then + echo "HAVE_OPTRESET=y" >> $config_host_mak +fi if test "$tcg" = "yes"; then echo "CONFIG_TCG=y" >> $config_host_mak if test "$tcg_interpreter" = "yes" ; then @@ -6554,6 +6989,11 @@ if test "$bzip2" = "yes" ; then echo "BZIP2_LIBS=-lbz2" >> $config_host_mak fi +if test "$lzfse" = "yes" ; then + echo "CONFIG_LZFSE=y" >> $config_host_mak + echo "LZFSE_LIBS=-llzfse" >> $config_host_mak +fi + if test "$libiscsi" = "yes" ; then echo "CONFIG_LIBISCSI=m" >> $config_host_mak echo "LIBISCSI_CFLAGS=$libiscsi_cflags" >> $config_host_mak @@ -6639,6 +7079,10 @@ if test "$atomic128" = "yes" ; then echo "CONFIG_ATOMIC128=y" >> $config_host_mak fi +if test "$cmpxchg128" = "yes" ; then + echo "CONFIG_CMPXCHG128=y" >> $config_host_mak +fi + if test "$atomic64" = "yes" ; then echo "CONFIG_ATOMIC64=y" >> $config_host_mak fi @@ -6673,6 +7117,14 @@ if test "$glusterfs_zerofill" = "yes" ; then echo "CONFIG_GLUSTERFS_ZEROFILL=y" >> $config_host_mak fi +if test "$glusterfs_ftruncate_has_stat" = "yes" ; then + echo "CONFIG_GLUSTERFS_FTRUNCATE_HAS_STAT=y" >> $config_host_mak +fi + +if test "$glusterfs_iocb_has_stat" = "yes" ; then + echo "CONFIG_GLUSTERFS_IOCB_HAS_STAT=y" >> $config_host_mak +fi + if test "$libssh2" = "yes" ; then echo "CONFIG_LIBSSH2=m" >> $config_host_mak echo "LIBSSH2_CFLAGS=$libssh2_cflags" >> $config_host_mak @@ -6737,6 +7189,10 @@ if test "$rdma" = "yes" ; then echo "RDMA_LIBS=$rdma_libs" >> $config_host_mak fi +if test "$pvrdma" = "yes" ; then + echo "CONFIG_PVRDMA=y" >> $config_host_mak +fi + if test "$have_rtnetlink" = "yes" ; then echo "CONFIG_RTNETLINK=y" >> $config_host_mak fi @@ -6780,11 +7236,14 @@ fi # Hold two types of flag: # CONFIG_THREAD_SETNAME_BYTHREAD - we've got a way of setting the name on # a thread we have a handle to -# CONFIG_PTHREAD_SETNAME_NP - A way of doing it on a particular +# CONFIG_PTHREAD_SETNAME_NP_W_TID - A way of doing it on a particular # platform -if test "$pthread_setname_np" = "yes" ; then +if test "$pthread_setname_np_w_tid" = "yes" ; then echo "CONFIG_THREAD_SETNAME_BYTHREAD=y" >> $config_host_mak - echo "CONFIG_PTHREAD_SETNAME_NP=y" >> $config_host_mak + echo "CONFIG_PTHREAD_SETNAME_NP_W_TID=y" >> $config_host_mak +elif test "$pthread_setname_np_wo_tid" = "yes" ; then + echo "CONFIG_THREAD_SETNAME_BYTHREAD=y" >> $config_host_mak + echo "CONFIG_PTHREAD_SETNAME_NP_WO_TID=y" >> $config_host_mak fi if test "$vxhs" = "yes" ; then @@ -6792,16 +7251,50 @@ if test "$vxhs" = "yes" ; then echo "VXHS_LIBS=$vxhs_libs" >> $config_host_mak fi +if test "$libpmem" = "yes" ; then + echo "CONFIG_LIBPMEM=y" >> $config_host_mak +fi + +if test "$bochs" = "yes" ; then + echo "CONFIG_BOCHS=y" >> $config_host_mak +fi +if test "$cloop" = "yes" ; then + echo "CONFIG_CLOOP=y" >> $config_host_mak +fi +if test "$dmg" = "yes" ; then + echo "CONFIG_DMG=y" >> $config_host_mak +fi +if test "$qcow1" = "yes" ; then + echo "CONFIG_QCOW1=y" >> $config_host_mak +fi +if test "$vdi" = "yes" ; then + echo "CONFIG_VDI=y" >> $config_host_mak +fi +if test "$vvfat" = "yes" ; then + echo "CONFIG_VVFAT=y" >> $config_host_mak +fi +if test "$qed" = "yes" ; then + echo "CONFIG_QED=y" >> $config_host_mak +fi +if test "$parallels" = "yes" ; then + echo "CONFIG_PARALLELS=y" >> $config_host_mak +fi +if test "$sheepdog" = "yes" ; then + echo "CONFIG_SHEEPDOG=y" >> $config_host_mak +fi + if test "$tcg_interpreter" = "yes"; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/tci $QEMU_INCLUDES" elif test "$ARCH" = "sparc64" ; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/sparc $QEMU_INCLUDES" elif test "$ARCH" = "s390x" ; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/s390 $QEMU_INCLUDES" -elif test "$ARCH" = "x86_64" -o "$ARCH" = "x32" ; then +elif test "$ARCH" = "x86_64" || test "$ARCH" = "x32" ; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/i386 $QEMU_INCLUDES" elif test "$ARCH" = "ppc64" ; then QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/ppc $QEMU_INCLUDES" +elif test "$ARCH" = "riscv32" || test "$ARCH" = "riscv64" ; then + QEMU_INCLUDES="-I\$(SRC_PATH)/tcg/riscv $QEMU_INCLUDES" else QEMU_INCLUDES="-iquote \$(SRC_PATH)/tcg/\$(ARCH) $QEMU_INCLUDES" fi @@ -6816,6 +7309,7 @@ echo "INSTALL_DATA=$install -c -m 0644" >> $config_host_mak 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 "PYTHON_VERSION=$python_version" >> $config_host_mak echo "CC=$cc" >> $config_host_mak if $iasl -h > /dev/null 2>&1; then echo "IASL=$iasl" >> $config_host_mak @@ -6865,7 +7359,6 @@ 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 echo "CONFIG_GCOV=y" >> $config_host_mak echo "GCOV=$gcov_tool" >> $config_host_mak @@ -6875,6 +7368,11 @@ if test "$docker" != "no"; then echo "HAVE_USER_DOCKER=y" >> $config_host_mak fi +if test "$libudev" != "no"; then + echo "CONFIG_LIBUDEV=y" >> $config_host_mak + echo "LIBUDEV_LIBS=$libudev_libs" >> $config_host_mak +fi + # use included Linux headers if test "$linux" = "yes" ; then mkdir -p linux-headers @@ -6882,7 +7380,7 @@ if test "$linux" = "yes" ; then i386|x86_64|x32) linux_arch=x86 ;; - ppcemb|ppc|ppc64) + ppc|ppc64) linux_arch=powerpc ;; s390x) @@ -6912,7 +7410,7 @@ target_name=$(echo $target | cut -d '-' -f 1) target_bigendian="no" case "$target_name" in - armeb|aarch64_be|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or1k|ppc|ppcemb|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) + armeb|aarch64_be|hppa|lm32|m68k|microblaze|mips|mipsn32|mips64|moxie|or1k|ppc|ppc64|ppc64abi32|s390x|sh4eb|sparc|sparc64|sparc32plus|xtensaeb) target_bigendian=yes ;; esac @@ -6956,13 +7454,15 @@ TARGET_ABI_DIR="" case "$target_name" in i386) - gdb_xml_files="i386-32bit.xml i386-32bit-core.xml i386-32bit-sse.xml" + mttcg="yes" + gdb_xml_files="i386-32bit.xml" target_compiler=$cross_cc_i386 target_compiler_cflags=$cross_cc_ccflags_i386 ;; x86_64) TARGET_BASE_ARCH=i386 - gdb_xml_files="i386-64bit.xml i386-64bit-core.xml i386-64bit-sse.xml" + mttcg="yes" + gdb_xml_files="i386-64bit.xml" target_compiler=$cross_cc_x86_64 ;; alpha) @@ -7008,11 +7508,13 @@ case "$target_name" in target_compiler=$cross_cc_microblaze ;; mips|mipsel) + mttcg="yes" TARGET_ARCH=mips target_compiler=$cross_cc_mips echo "TARGET_ABI_MIPSO32=y" >> $config_target_mak ;; mipsn32|mipsn32el) + mttcg="yes" TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips target_compiler=$cross_cc_mipsn32 @@ -7020,6 +7522,7 @@ case "$target_name" in echo "TARGET_ABI32=y" >> $config_target_mak ;; mips64|mips64el) + mttcg="yes" TARGET_ARCH=mips64 TARGET_BASE_ARCH=mips target_compiler=$cross_cc_mips64 @@ -7040,12 +7543,6 @@ case "$target_name" in gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" target_compiler=$cross_cc_powerpc ;; - ppcemb) - TARGET_BASE_ARCH=ppc - TARGET_ABI_DIR=ppc - gdb_xml_files="power-core.xml power-fpu.xml power-altivec.xml power-spe.xml" - target_compiler=$cross_cc_ppcemb - ;; ppc64) TARGET_BASE_ARCH=ppc TARGET_ABI_DIR=ppc @@ -7073,12 +7570,14 @@ case "$target_name" in TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes + gdb_xml_files="riscv-32bit-cpu.xml riscv-32bit-fpu.xml riscv-32bit-csr.xml" target_compiler=$cross_cc_riscv32 ;; riscv64) TARGET_BASE_ARCH=riscv TARGET_ABI_DIR=riscv mttcg=yes + gdb_xml_files="riscv-64bit-cpu.xml riscv-64bit-fpu.xml riscv-64bit-csr.xml" target_compiler=$cross_cc_riscv64 ;; sh4|sh4eb) @@ -7116,6 +7615,7 @@ case "$target_name" in ;; xtensa|xtensaeb) TARGET_ARCH=xtensa + bflt="yes" mttcg="yes" target_compiler=$cross_cc_xtensa ;; @@ -7169,18 +7669,18 @@ fi if supported_xen_target $target; then echo "CONFIG_XEN=y" >> $config_target_mak + echo "$target/config-devices.mak: CONFIG_XEN=y" >> $config_host_mak if test "$xen_pci_passthrough" = yes; then echo "CONFIG_XEN_PCI_PASSTHROUGH=y" >> "$config_target_mak" fi +else + echo "$target/config-devices.mak: CONFIG_XEN=n" >> $config_host_mak fi if supported_kvm_target $target; then echo "CONFIG_KVM=y" >> $config_target_mak - if test "$vhost_net" = "yes" ; then - echo "CONFIG_VHOST_NET=y" >> $config_target_mak - if test "$vhost_user" = "yes" ; then - echo "CONFIG_VHOST_USER_NET_TEST_$target_name=y" >> $config_host_mak - fi - fi + echo "$target/config-devices.mak: CONFIG_KVM=y" >> $config_host_mak +else + echo "$target/config-devices.mak: CONFIG_KVM=n" >> $config_host_mak fi if supported_hax_target $target; then echo "CONFIG_HAX=y" >> $config_target_mak @@ -7215,7 +7715,7 @@ if test ! -z "$gdb_xml_files" ; then echo "TARGET_XML_FILES=$list" >> $config_target_mak fi -if test "$target_user_only" = "yes" -a "$bflt" = "yes"; then +if test "$target_user_only" = "yes" && test "$bflt" = "yes"; then echo "TARGET_HAS_BFLT=y" >> $config_target_mak fi if test "$target_bsd_user" = "yes" ; then @@ -7281,6 +7781,9 @@ for i in $ARCH $TARGET_BASE_ARCH ; do ;; mips*) disas_config "MIPS" + if test -n "${cxx}"; then + disas_config "NANOMIPS" + fi ;; moxie*) disas_config "MOXIE" @@ -7294,7 +7797,7 @@ for i in $ARCH $TARGET_BASE_ARCH ; do ppc*) disas_config "PPC" ;; - riscv) + riscv*) disas_config "RISCV" ;; s390*) @@ -7323,7 +7826,7 @@ alpha) esac if test "$gprof" = "yes" ; then - echo "TARGET_GPROF=yes" >> $config_target_mak + echo "TARGET_GPROF=y" >> $config_target_mak if test "$target_linux_user" = "yes" ; then cflags="-p $cflags" ldflags="-p $ldflags" @@ -7334,7 +7837,7 @@ if test "$gprof" = "yes" ; then fi fi -if test "$target_linux_user" = "yes" -o "$target_bsd_user" = "yes" ; then +if test "$target_linux_user" = "yes" || test "$target_bsd_user" = "yes" ; then ldflags="$ldflags $textseg_ldflags" fi @@ -7346,7 +7849,8 @@ fi # - we build the system emulation for s390x (qemu-system-s390x) # - KVM is enabled # - the linker supports --s390-pgste -if test "$TARGET_ARCH" = "s390x" -a "$target_softmmu" = "yes" -a "$ARCH" = "s390x" -a "$kvm" = "yes"; then +if test "$TARGET_ARCH" = "s390x" && test "$target_softmmu" = "yes" && \ + test "$ARCH" = "s390x" && test "$kvm" = "yes"; then if ld_has --s390-pgste ; then ldflags="-Wl,--s390-pgste $ldflags" fi @@ -7380,20 +7884,34 @@ if test "$ccache_cpp2" = "yes"; then echo "export CCACHE_CPP2=y" >> $config_host_mak fi -# build tree in object directory in case the source is not in the current directory +# If we're using a separate build tree, set it up now. +# DIRS are directories which we simply mkdir in the build tree; +# LINKS are things to symlink back into the source tree +# (these can be both files and directories). +# Caution: do not add files or directories here using wildcards. This +# will result in problems later if a new file matching the wildcard is +# added to the source tree -- nothing will cause configure to be rerun +# so the build tree will be missing the link back to the new file, and +# tests might fail. Prefer to keep the relevant files in their own +# directory and symlink the directory instead. DIRS="tests tests/tcg tests/tcg/cris tests/tcg/lm32 tests/libqos tests/qapi-schema tests/tcg/xtensa tests/qemu-iotests tests/vm" +DIRS="$DIRS tests/fp tests/qgraph" DIRS="$DIRS docs docs/interop fsdev scsi" DIRS="$DIRS pc-bios/optionrom pc-bios/spapr-rtas pc-bios/s390-ccw" DIRS="$DIRS roms/seabios roms/vgabios" -FILES="Makefile tests/tcg/Makefile qdict-test-data.txt" -FILES="$FILES tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" -FILES="$FILES tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile" -FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" -FILES="$FILES pc-bios/spapr-rtas/Makefile" -FILES="$FILES pc-bios/s390-ccw/Makefile" -FILES="$FILES roms/seabios/Makefile roms/vgabios/Makefile" -FILES="$FILES pc-bios/qemu-icon.bmp" -FILES="$FILES .gdbinit scripts" # scripts needed by relative path in .gdbinit +LINKS="Makefile tests/tcg/Makefile" +LINKS="$LINKS tests/tcg/cris/Makefile tests/tcg/cris/.gdbinit" +LINKS="$LINKS tests/tcg/lm32/Makefile tests/tcg/xtensa/Makefile po/Makefile" +LINKS="$LINKS tests/fp/Makefile" +LINKS="$LINKS pc-bios/optionrom/Makefile pc-bios/keymaps" +LINKS="$LINKS pc-bios/spapr-rtas/Makefile" +LINKS="$LINKS pc-bios/s390-ccw/Makefile" +LINKS="$LINKS roms/seabios/Makefile roms/vgabios/Makefile" +LINKS="$LINKS pc-bios/qemu-icon.bmp" +LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit +LINKS="$LINKS tests/acceptance tests/data" +LINKS="$LINKS tests/qemu-iotests/check" +LINKS="$LINKS python" for bios_file in \ $source_path/pc-bios/*.bin \ $source_path/pc-bios/*.lid \ @@ -7405,14 +7923,10 @@ for bios_file in \ $source_path/pc-bios/u-boot.* \ $source_path/pc-bios/palcode-* do - FILES="$FILES pc-bios/$(basename $bios_file)" -done -for test_file in $(find $source_path/tests/acpi-test-data -type f) -do - FILES="$FILES tests/acpi-test-data$(echo $test_file | sed -e 's/.*acpi-test-data//')" + LINKS="$LINKS pc-bios/$(basename $bios_file)" done mkdir -p $DIRS -for f in $FILES ; do +for f in $LINKS ; do if [ -e "$source_path/$f" ] && [ "$pwd_is_source_path" != "y" ]; then symlink "$source_path/$f" "$f" fi @@ -7434,25 +7948,13 @@ for rom in seabios vgabios ; do echo "RANLIB=$ranlib" >> $config_mak done -# set up tests data directory -for tests_subdir in acceptance data; do - if [ ! -e tests/$tests_subdir ]; then - symlink "$source_path/tests/$tests_subdir" tests/$tests_subdir - fi -done - # set up qemu-iotests in this build directory iotests_common_env="tests/qemu-iotests/common.env" -iotests_check="tests/qemu-iotests/check" echo "# Automatically generated by configure - do not modify" > "$iotests_common_env" echo >> "$iotests_common_env" echo "export PYTHON='$python'" >> "$iotests_common_env" -if [ ! -e "$iotests_check" ]; then - symlink "$source_path/$iotests_check" "$iotests_check" -fi - # Save the configure command line for later reuse. cat <config.status #!/bin/sh @@ -7461,6 +7963,46 @@ cat <config.status # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. EOD + +preserve_env() { + envname=$1 + + eval envval=\$$envname + + if test -n "$envval" + then + echo "$envname='$envval'" >> config.status + echo "export $envname" >> config.status + else + echo "unset $envname" >> config.status + fi +} + +# Preserve various env variables that influence what +# features/build target configure will detect +preserve_env AR +preserve_env AS +preserve_env CC +preserve_env CPP +preserve_env CXX +preserve_env INSTALL +preserve_env LD +preserve_env LD_LIBRARY_PATH +preserve_env LIBTOOL +preserve_env MAKE +preserve_env NM +preserve_env OBJCOPY +preserve_env PATH +preserve_env PKG_CONFIG +preserve_env PKG_CONFIG_LIBDIR +preserve_env PKG_CONFIG_PATH +preserve_env PYTHON +preserve_env SDL_CONFIG +preserve_env SDL2_CONFIG +preserve_env SMBD +preserve_env STRIP +preserve_env WINDRES + printf "exec" >>config.status printf " '%s'" "$0" "$@" >>config.status echo ' "$@"' >>config.status diff --git a/contrib/elf2dmp/Makefile.objs b/contrib/elf2dmp/Makefile.objs new file mode 100644 index 0000000000..e3140f58cf --- /dev/null +++ b/contrib/elf2dmp/Makefile.objs @@ -0,0 +1 @@ +elf2dmp-obj-y = main.o addrspace.o download.o pdb.o qemu_elf.o diff --git a/contrib/elf2dmp/addrspace.c b/contrib/elf2dmp/addrspace.c new file mode 100644 index 0000000000..8a76069cb5 --- /dev/null +++ b/contrib/elf2dmp/addrspace.c @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "addrspace.h" + +static struct pa_block *pa_space_find_block(struct pa_space *ps, uint64_t pa) +{ + size_t i; + for (i = 0; i < ps->block_nr; i++) { + if (ps->block[i].paddr <= pa && + pa <= ps->block[i].paddr + ps->block[i].size) { + return ps->block + i; + } + } + + return NULL; +} + +static uint8_t *pa_space_resolve(struct pa_space *ps, uint64_t pa) +{ + struct pa_block *block = pa_space_find_block(ps, pa); + + if (!block) { + return NULL; + } + + return block->addr + (pa - block->paddr); +} + +int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf) +{ + Elf64_Half phdr_nr = elf_getphdrnum(qemu_elf->map); + Elf64_Phdr *phdr = elf64_getphdr(qemu_elf->map); + size_t block_i = 0; + size_t i; + + ps->block_nr = 0; + + for (i = 0; i < phdr_nr; i++) { + if (phdr[i].p_type == PT_LOAD) { + ps->block_nr++; + } + } + + ps->block = malloc(sizeof(*ps->block) * ps->block_nr); + if (!ps->block) { + return 1; + } + + for (i = 0; i < phdr_nr; i++) { + if (phdr[i].p_type == PT_LOAD) { + ps->block[block_i] = (struct pa_block) { + .addr = (uint8_t *)qemu_elf->map + phdr[i].p_offset, + .paddr = phdr[i].p_paddr, + .size = phdr[i].p_filesz, + }; + block_i++; + } + } + + return 0; +} + +void pa_space_destroy(struct pa_space *ps) +{ + ps->block_nr = 0; + free(ps->block); +} + +void va_space_set_dtb(struct va_space *vs, uint64_t dtb) +{ + vs->dtb = dtb & 0x00ffffffffff000; +} + +void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb) +{ + vs->ps = ps; + va_space_set_dtb(vs, dtb); +} + +static uint64_t get_pml4e(struct va_space *vs, uint64_t va) +{ + uint64_t pa = (vs->dtb & 0xffffffffff000) | ((va & 0xff8000000000) >> 36); + + return *(uint64_t *)pa_space_resolve(vs->ps, pa); +} + +static uint64_t get_pdpi(struct va_space *vs, uint64_t va, uint64_t pml4e) +{ + uint64_t pdpte_paddr = (pml4e & 0xffffffffff000) | + ((va & 0x7FC0000000) >> 27); + + return *(uint64_t *)pa_space_resolve(vs->ps, pdpte_paddr); +} + +static uint64_t pde_index(uint64_t va) +{ + return (va >> 21) & 0x1FF; +} + +static uint64_t pdba_base(uint64_t pdpe) +{ + return pdpe & 0xFFFFFFFFFF000; +} + +static uint64_t get_pgd(struct va_space *vs, uint64_t va, uint64_t pdpe) +{ + uint64_t pgd_entry = pdba_base(pdpe) + pde_index(va) * 8; + + return *(uint64_t *)pa_space_resolve(vs->ps, pgd_entry); +} + +static uint64_t pte_index(uint64_t va) +{ + return (va >> 12) & 0x1FF; +} + +static uint64_t ptba_base(uint64_t pde) +{ + return pde & 0xFFFFFFFFFF000; +} + +static uint64_t get_pte(struct va_space *vs, uint64_t va, uint64_t pgd) +{ + uint64_t pgd_val = ptba_base(pgd) + pte_index(va) * 8; + + return *(uint64_t *)pa_space_resolve(vs->ps, pgd_val); +} + +static uint64_t get_paddr(uint64_t va, uint64_t pte) +{ + return (pte & 0xFFFFFFFFFF000) | (va & 0xFFF); +} + +static bool is_present(uint64_t entry) +{ + return entry & 0x1; +} + +static bool page_size_flag(uint64_t entry) +{ + return entry & (1 << 7); +} + +static uint64_t get_1GB_paddr(uint64_t va, uint64_t pdpte) +{ + return (pdpte & 0xfffffc0000000) | (va & 0x3fffffff); +} + +static uint64_t get_2MB_paddr(uint64_t va, uint64_t pgd_entry) +{ + return (pgd_entry & 0xfffffffe00000) | (va & 0x00000001fffff); +} + +static uint64_t va_space_va2pa(struct va_space *vs, uint64_t va) +{ + uint64_t pml4e, pdpe, pgd, pte; + + pml4e = get_pml4e(vs, va); + if (!is_present(pml4e)) { + return INVALID_PA; + } + + pdpe = get_pdpi(vs, va, pml4e); + if (!is_present(pdpe)) { + return INVALID_PA; + } + + if (page_size_flag(pdpe)) { + return get_1GB_paddr(va, pdpe); + } + + pgd = get_pgd(vs, va, pdpe); + if (!is_present(pgd)) { + return INVALID_PA; + } + + if (page_size_flag(pgd)) { + return get_2MB_paddr(va, pgd); + } + + pte = get_pte(vs, va, pgd); + if (!is_present(pte)) { + return INVALID_PA; + } + + return get_paddr(va, pte); +} + +void *va_space_resolve(struct va_space *vs, uint64_t va) +{ + uint64_t pa = va_space_va2pa(vs, va); + + if (pa == INVALID_PA) { + return NULL; + } + + return pa_space_resolve(vs->ps, pa); +} + +int va_space_rw(struct va_space *vs, uint64_t addr, + void *buf, size_t size, int is_write) +{ + while (size) { + uint64_t page = addr & PFN_MASK; + size_t s = (page + PAGE_SIZE) - addr; + void *ptr; + + s = (s > size) ? size : s; + + ptr = va_space_resolve(vs, addr); + if (!ptr) { + return 1; + } + + if (is_write) { + memcpy(ptr, buf, s); + } else { + memcpy(buf, ptr, s); + } + + size -= s; + buf = (uint8_t *)buf + s; + addr += s; + } + + return 0; +} diff --git a/contrib/elf2dmp/addrspace.h b/contrib/elf2dmp/addrspace.h new file mode 100644 index 0000000000..d87f6a18c6 --- /dev/null +++ b/contrib/elf2dmp/addrspace.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef ADDRSPACE_H +#define ADDRSPACE_H + +#include "qemu_elf.h" + +#define PAGE_BITS 12 +#define PAGE_SIZE (1ULL << PAGE_BITS) +#define PFN_MASK (~(PAGE_SIZE - 1)) + +#define INVALID_PA UINT64_MAX + +struct pa_block { + uint8_t *addr; + uint64_t paddr; + uint64_t size; +}; + +struct pa_space { + size_t block_nr; + struct pa_block *block; +}; + +struct va_space { + uint64_t dtb; + struct pa_space *ps; +}; + +int pa_space_create(struct pa_space *ps, QEMU_Elf *qemu_elf); +void pa_space_destroy(struct pa_space *ps); + +void va_space_create(struct va_space *vs, struct pa_space *ps, uint64_t dtb); +void va_space_set_dtb(struct va_space *vs, uint64_t dtb); +void *va_space_resolve(struct va_space *vs, uint64_t va); +int va_space_rw(struct va_space *vs, uint64_t addr, + void *buf, size_t size, int is_write); + +#endif /* ADDRSPACE_H */ diff --git a/contrib/elf2dmp/download.c b/contrib/elf2dmp/download.c new file mode 100644 index 0000000000..d09e607431 --- /dev/null +++ b/contrib/elf2dmp/download.c @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include +#include "download.h" + +int download_url(const char *name, const char *url) +{ + int err = 0; + FILE *file; + CURL *curl = curl_easy_init(); + + if (!curl) { + return 1; + } + + file = fopen(name, "wb"); + if (!file) { + err = 1; + goto out_curl; + } + + curl_easy_setopt(curl, CURLOPT_URL, url); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, file); + curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + + if (curl_easy_perform(curl) != CURLE_OK) { + err = 1; + fclose(file); + unlink(name); + goto out_curl; + } + + err = fclose(file); + +out_curl: + curl_easy_cleanup(curl); + + return err; +} diff --git a/contrib/elf2dmp/download.h b/contrib/elf2dmp/download.h new file mode 100644 index 0000000000..5c274925f7 --- /dev/null +++ b/contrib/elf2dmp/download.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef DOWNLOAD_H +#define DOWNLOAD_H + +int download_url(const char *name, const char *url); + +#endif /* DOWNLOAD_H */ diff --git a/contrib/elf2dmp/err.h b/contrib/elf2dmp/err.h new file mode 100644 index 0000000000..5456bd5a30 --- /dev/null +++ b/contrib/elf2dmp/err.h @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef ERR_H +#define ERR_H + +#define eprintf(...) fprintf(stderr, __VA_ARGS__) + +#endif /* ERR_H */ diff --git a/contrib/elf2dmp/kdbg.h b/contrib/elf2dmp/kdbg.h new file mode 100644 index 0000000000..002e3d0cd5 --- /dev/null +++ b/contrib/elf2dmp/kdbg.h @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef KDBG_H +#define KDBG_H + +typedef struct DBGKD_GET_VERSION64 { + uint16_t MajorVersion; + uint16_t MinorVersion; + uint8_t ProtocolVersion; + uint8_t KdSecondaryVersion; + uint16_t Flags; + uint16_t MachineType; + uint8_t MaxPacketType; + uint8_t MaxStateChange; + uint8_t MaxManipulate; + uint8_t Simulation; + uint16_t Unused[1]; + uint64_t KernBase; + uint64_t PsLoadedModuleList; + uint64_t DebuggerDataList; +} DBGKD_GET_VERSION64; + +#ifndef _WIN32 +typedef struct LIST_ENTRY64 { + struct LIST_ENTRY64 *Flink; + struct LIST_ENTRY64 *Blink; +} LIST_ENTRY64; +#endif + +typedef struct DBGKD_DEBUG_DATA_HEADER64 { + LIST_ENTRY64 List; + uint32_t OwnerTag; + uint32_t Size; +} DBGKD_DEBUG_DATA_HEADER64; + +typedef struct KDDEBUGGER_DATA64 { + DBGKD_DEBUG_DATA_HEADER64 Header; + + uint64_t KernBase; + uint64_t BreakpointWithStatus; + uint64_t SavedContext; + uint16_t ThCallbackStack; + uint16_t NextCallback; + uint16_t FramePointer; + uint16_t PaeEnabled:1; + uint64_t KiCallUserMode; + uint64_t KeUserCallbackDispatcher; + uint64_t PsLoadedModuleList; + uint64_t PsActiveProcessHead; + uint64_t PspCidTable; + uint64_t ExpSystemResourcesList; + uint64_t ExpPagedPoolDescriptor; + uint64_t ExpNumberOfPagedPools; + uint64_t KeTimeIncrement; + uint64_t KeBugCheckCallbackListHead; + uint64_t KiBugcheckData; + uint64_t IopErrorLogListHead; + uint64_t ObpRootDirectoryObject; + uint64_t ObpTypeObjectType; + uint64_t MmSystemCacheStart; + uint64_t MmSystemCacheEnd; + uint64_t MmSystemCacheWs; + uint64_t MmPfnDatabase; + uint64_t MmSystemPtesStart; + uint64_t MmSystemPtesEnd; + uint64_t MmSubsectionBase; + uint64_t MmNumberOfPagingFiles; + uint64_t MmLowestPhysicalPage; + uint64_t MmHighestPhysicalPage; + uint64_t MmNumberOfPhysicalPages; + uint64_t MmMaximumNonPagedPoolInBytes; + uint64_t MmNonPagedSystemStart; + uint64_t MmNonPagedPoolStart; + uint64_t MmNonPagedPoolEnd; + uint64_t MmPagedPoolStart; + uint64_t MmPagedPoolEnd; + uint64_t MmPagedPoolInformation; + uint64_t MmPageSize; + uint64_t MmSizeOfPagedPoolInBytes; + uint64_t MmTotalCommitLimit; + uint64_t MmTotalCommittedPages; + uint64_t MmSharedCommit; + uint64_t MmDriverCommit; + uint64_t MmProcessCommit; + uint64_t MmPagedPoolCommit; + uint64_t MmExtendedCommit; + uint64_t MmZeroedPageListHead; + uint64_t MmFreePageListHead; + uint64_t MmStandbyPageListHead; + uint64_t MmModifiedPageListHead; + uint64_t MmModifiedNoWritePageListHead; + uint64_t MmAvailablePages; + uint64_t MmResidentAvailablePages; + uint64_t PoolTrackTable; + uint64_t NonPagedPoolDescriptor; + uint64_t MmHighestUserAddress; + uint64_t MmSystemRangeStart; + uint64_t MmUserProbeAddress; + uint64_t KdPrintCircularBuffer; + uint64_t KdPrintCircularBufferEnd; + uint64_t KdPrintWritePointer; + uint64_t KdPrintRolloverCount; + uint64_t MmLoadedUserImageList; + + /* NT 5.1 Addition */ + + uint64_t NtBuildLab; + uint64_t KiNormalSystemCall; + + /* NT 5.0 hotfix addition */ + + uint64_t KiProcessorBlock; + uint64_t MmUnloadedDrivers; + uint64_t MmLastUnloadedDriver; + uint64_t MmTriageActionTaken; + uint64_t MmSpecialPoolTag; + uint64_t KernelVerifier; + uint64_t MmVerifierData; + uint64_t MmAllocatedNonPagedPool; + uint64_t MmPeakCommitment; + uint64_t MmTotalCommitLimitMaximum; + uint64_t CmNtCSDVersion; + + /* NT 5.1 Addition */ + + uint64_t MmPhysicalMemoryBlock; + uint64_t MmSessionBase; + uint64_t MmSessionSize; + uint64_t MmSystemParentTablePage; + + /* Server 2003 addition */ + + uint64_t MmVirtualTranslationBase; + uint16_t OffsetKThreadNextProcessor; + uint16_t OffsetKThreadTeb; + uint16_t OffsetKThreadKernelStack; + uint16_t OffsetKThreadInitialStack; + uint16_t OffsetKThreadApcProcess; + uint16_t OffsetKThreadState; + uint16_t OffsetKThreadBStore; + uint16_t OffsetKThreadBStoreLimit; + uint16_t SizeEProcess; + uint16_t OffsetEprocessPeb; + uint16_t OffsetEprocessParentCID; + uint16_t OffsetEprocessDirectoryTableBase; + uint16_t SizePrcb; + uint16_t OffsetPrcbDpcRoutine; + uint16_t OffsetPrcbCurrentThread; + uint16_t OffsetPrcbMhz; + uint16_t OffsetPrcbCpuType; + uint16_t OffsetPrcbVendorString; + uint16_t OffsetPrcbProcStateContext; + uint16_t OffsetPrcbNumber; + uint16_t SizeEThread; + uint64_t KdPrintCircularBufferPtr; + uint64_t KdPrintBufferSize; + uint64_t KeLoaderBlock; + uint16_t SizePcr; + uint16_t OffsetPcrSelfPcr; + uint16_t OffsetPcrCurrentPrcb; + uint16_t OffsetPcrContainedPrcb; + uint16_t OffsetPcrInitialBStore; + uint16_t OffsetPcrBStoreLimit; + uint16_t OffsetPcrInitialStack; + uint16_t OffsetPcrStackLimit; + uint16_t OffsetPrcbPcrPage; + uint16_t OffsetPrcbProcStateSpecialReg; + uint16_t GdtR0Code; + uint16_t GdtR0Data; + uint16_t GdtR0Pcr; + uint16_t GdtR3Code; + uint16_t GdtR3Data; + uint16_t GdtR3Teb; + uint16_t GdtLdt; + uint16_t GdtTss; + uint16_t Gdt64R3CmCode; + uint16_t Gdt64R3CmTeb; + uint64_t IopNumTriageDumpDataBlocks; + uint64_t IopTriageDumpDataBlocks; + + /* Longhorn addition */ + + uint64_t VfCrashDataBlock; + uint64_t MmBadPagesDetected; + uint64_t MmZeroedPageSingleBitErrorsDetected; + + /* Windows 7 addition */ + + uint64_t EtwpDebuggerData; + uint16_t OffsetPrcbContext; +} KDDEBUGGER_DATA64; + +#endif /* KDBG_H */ diff --git a/contrib/elf2dmp/main.c b/contrib/elf2dmp/main.c new file mode 100644 index 0000000000..1bfeb89ba7 --- /dev/null +++ b/contrib/elf2dmp/main.c @@ -0,0 +1,598 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#include + +#include "qemu/osdep.h" +#include "err.h" +#include "addrspace.h" +#include "pe.h" +#include "pdb.h" +#include "kdbg.h" +#include "download.h" +#include "qemu/win_dump_defs.h" + +#define SYM_URL_BASE "https://msdl.microsoft.com/download/symbols/" +#define PDB_NAME "ntkrnlmp.pdb" + +#define INITIAL_MXCSR 0x1f80 + +typedef struct idt_desc { + uint16_t offset1; /* offset bits 0..15 */ + uint16_t selector; + uint8_t ist; + uint8_t type_attr; + uint16_t offset2; /* offset bits 16..31 */ + uint32_t offset3; /* offset bits 32..63 */ + uint32_t rsrvd; +} __attribute__ ((packed)) idt_desc_t; + +static uint64_t idt_desc_addr(idt_desc_t desc) +{ + return (uint64_t)desc.offset1 | ((uint64_t)desc.offset2 << 16) | + ((uint64_t)desc.offset3 << 32); +} + +static const uint64_t SharedUserData = 0xfffff78000000000; + +#define KUSD_OFFSET_SUITE_MASK 0x2d0 +#define KUSD_OFFSET_PRODUCT_TYPE 0x264 + +#define SYM_RESOLVE(base, r, s) ((s = pdb_resolve(base, r, #s)),\ + s ? printf(#s" = 0x%016"PRIx64"\n", s) :\ + eprintf("Failed to resolve "#s"\n"), s) + +static uint64_t rol(uint64_t x, uint64_t y) +{ + return (x << y) | (x >> (64 - y)); +} + +/* + * Decoding algorithm can be found in Volatility project + */ +static void kdbg_decode(uint64_t *dst, uint64_t *src, size_t size, + uint64_t kwn, uint64_t kwa, uint64_t kdbe) +{ + size_t i; + assert(size % sizeof(uint64_t) == 0); + for (i = 0; i < size / sizeof(uint64_t); i++) { + uint64_t block; + + block = src[i]; + block = rol(block ^ kwn, (uint8_t)kwn); + block = __builtin_bswap64(block ^ kdbe) ^ kwa; + dst[i] = block; + } +} + +static KDDEBUGGER_DATA64 *get_kdbg(uint64_t KernBase, struct pdb_reader *pdb, + struct va_space *vs, uint64_t KdDebuggerDataBlock) +{ + const char OwnerTag[4] = "KDBG"; + KDDEBUGGER_DATA64 *kdbg = NULL; + DBGKD_DEBUG_DATA_HEADER64 kdbg_hdr; + bool decode = false; + uint64_t kwn, kwa, KdpDataBlockEncoded; + + if (va_space_rw(vs, + KdDebuggerDataBlock + offsetof(KDDEBUGGER_DATA64, Header), + &kdbg_hdr, sizeof(kdbg_hdr), 0)) { + eprintf("Failed to extract KDBG header\n"); + return NULL; + } + + if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) { + uint64_t KiWaitNever, KiWaitAlways; + + decode = true; + + if (!SYM_RESOLVE(KernBase, pdb, KiWaitNever) || + !SYM_RESOLVE(KernBase, pdb, KiWaitAlways) || + !SYM_RESOLVE(KernBase, pdb, KdpDataBlockEncoded)) { + return NULL; + } + + if (va_space_rw(vs, KiWaitNever, &kwn, sizeof(kwn), 0) || + va_space_rw(vs, KiWaitAlways, &kwa, sizeof(kwa), 0)) { + return NULL; + } + + printf("[KiWaitNever] = 0x%016"PRIx64"\n", kwn); + printf("[KiWaitAlways] = 0x%016"PRIx64"\n", kwa); + + /* + * If KDBG header can be decoded, KDBG size is available + * and entire KDBG can be decoded. + */ + printf("Decoding KDBG header...\n"); + kdbg_decode((uint64_t *)&kdbg_hdr, (uint64_t *)&kdbg_hdr, + sizeof(kdbg_hdr), kwn, kwa, KdpDataBlockEncoded); + + printf("Owner tag is \'%.4s\'\n", (char *)&kdbg_hdr.OwnerTag); + if (memcmp(&kdbg_hdr.OwnerTag, OwnerTag, sizeof(OwnerTag))) { + eprintf("Failed to decode KDBG header\n"); + return NULL; + } + } + + kdbg = malloc(kdbg_hdr.Size); + if (!kdbg) { + return NULL; + } + + if (va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 0)) { + eprintf("Failed to extract entire KDBG\n"); + return NULL; + } + + if (!decode) { + return kdbg; + } + + printf("Decoding KdDebuggerDataBlock...\n"); + kdbg_decode((uint64_t *)kdbg, (uint64_t *)kdbg, kdbg_hdr.Size, + kwn, kwa, KdpDataBlockEncoded); + + va_space_rw(vs, KdDebuggerDataBlock, kdbg, kdbg_hdr.Size, 1); + + return kdbg; +} + +static void win_context_init_from_qemu_cpu_state(WinContext *ctx, + QEMUCPUState *s) +{ + WinContext win_ctx = (WinContext){ + .ContextFlags = WIN_CTX_X64 | WIN_CTX_INT | WIN_CTX_SEG | WIN_CTX_CTL, + .MxCsr = INITIAL_MXCSR, + + .SegCs = s->cs.selector, + .SegSs = s->ss.selector, + .SegDs = s->ds.selector, + .SegEs = s->es.selector, + .SegFs = s->fs.selector, + .SegGs = s->gs.selector, + .EFlags = (uint32_t)s->rflags, + + .Rax = s->rax, + .Rbx = s->rbx, + .Rcx = s->rcx, + .Rdx = s->rdx, + .Rsp = s->rsp, + .Rbp = s->rbp, + .Rsi = s->rsi, + .Rdi = s->rdi, + .R8 = s->r8, + .R9 = s->r9, + .R10 = s->r10, + .R11 = s->r11, + .R12 = s->r12, + .R13 = s->r13, + .R14 = s->r14, + .R15 = s->r15, + + .Rip = s->rip, + .FltSave = { + .MxCsr = INITIAL_MXCSR, + }, + }; + + *ctx = win_ctx; +} + +/* + * Finds paging-structure hierarchy base, + * if previously set doesn't give access to kernel structures + */ +static int fix_dtb(struct va_space *vs, QEMU_Elf *qe) +{ + /* + * Firstly, test previously set DTB. + */ + if (va_space_resolve(vs, SharedUserData)) { + return 0; + } + + /* + * Secondly, find CPU which run system task. + */ + size_t i; + for (i = 0; i < qe->state_nr; i++) { + QEMUCPUState *s = qe->state[i]; + + if (is_system(s)) { + va_space_set_dtb(vs, s->cr[3]); + printf("DTB 0x%016"PRIx64" has been found from CPU #%zu" + " as system task CR3\n", vs->dtb, i); + return !(va_space_resolve(vs, SharedUserData)); + } + } + + /* + * Thirdly, use KERNEL_GS_BASE from CPU #0 as PRCB address and + * CR3 as [Prcb+0x7000] + */ + if (qe->has_kernel_gs_base) { + QEMUCPUState *s = qe->state[0]; + uint64_t Prcb = s->kernel_gs_base; + uint64_t *cr3 = va_space_resolve(vs, Prcb + 0x7000); + + if (!cr3) { + return 1; + } + + va_space_set_dtb(vs, *cr3); + printf("DirectoryTableBase = 0x%016"PRIx64" has been found from CPU #0" + " as interrupt handling CR3\n", vs->dtb); + return !(va_space_resolve(vs, SharedUserData)); + } + + return 1; +} + +static int fill_header(WinDumpHeader64 *hdr, struct pa_space *ps, + struct va_space *vs, uint64_t KdDebuggerDataBlock, + KDDEBUGGER_DATA64 *kdbg, uint64_t KdVersionBlock, int nr_cpus) +{ + uint32_t *suite_mask = va_space_resolve(vs, SharedUserData + + KUSD_OFFSET_SUITE_MASK); + int32_t *product_type = va_space_resolve(vs, SharedUserData + + KUSD_OFFSET_PRODUCT_TYPE); + DBGKD_GET_VERSION64 kvb; + WinDumpHeader64 h; + size_t i; + + QEMU_BUILD_BUG_ON(KUSD_OFFSET_SUITE_MASK >= PAGE_SIZE); + QEMU_BUILD_BUG_ON(KUSD_OFFSET_PRODUCT_TYPE >= PAGE_SIZE); + + if (!suite_mask || !product_type) { + return 1; + } + + if (va_space_rw(vs, KdVersionBlock, &kvb, sizeof(kvb), 0)) { + eprintf("Failed to extract KdVersionBlock\n"); + return 1; + } + + h = (WinDumpHeader64) { + .Signature = "PAGE", + .ValidDump = "DU64", + .MajorVersion = kvb.MajorVersion, + .MinorVersion = kvb.MinorVersion, + .DirectoryTableBase = vs->dtb, + .PfnDatabase = kdbg->MmPfnDatabase, + .PsLoadedModuleList = kdbg->PsLoadedModuleList, + .PsActiveProcessHead = kdbg->PsActiveProcessHead, + .MachineImageType = kvb.MachineType, + .NumberProcessors = nr_cpus, + .BugcheckCode = LIVE_SYSTEM_DUMP, + .KdDebuggerDataBlock = KdDebuggerDataBlock, + .DumpType = 1, + .Comment = "Hello from elf2dmp!", + .SuiteMask = *suite_mask, + .ProductType = *product_type, + .SecondaryDataState = kvb.KdSecondaryVersion, + .PhysicalMemoryBlock = (WinDumpPhyMemDesc64) { + .NumberOfRuns = ps->block_nr, + }, + .RequiredDumpSpace = sizeof(h), + }; + + for (i = 0; i < ps->block_nr; i++) { + h.PhysicalMemoryBlock.NumberOfPages += ps->block[i].size / PAGE_SIZE; + h.PhysicalMemoryBlock.Run[i] = (WinDumpPhyMemRun64) { + .BasePage = ps->block[i].paddr / PAGE_SIZE, + .PageCount = ps->block[i].size / PAGE_SIZE, + }; + } + + h.RequiredDumpSpace += h.PhysicalMemoryBlock.NumberOfPages << PAGE_BITS; + + *hdr = h; + + return 0; +} + +static int fill_context(KDDEBUGGER_DATA64 *kdbg, + struct va_space *vs, QEMU_Elf *qe) +{ + int i; + for (i = 0; i < qe->state_nr; i++) { + uint64_t Prcb; + uint64_t Context; + WinContext ctx; + QEMUCPUState *s = qe->state[i]; + + if (va_space_rw(vs, kdbg->KiProcessorBlock + sizeof(Prcb) * i, + &Prcb, sizeof(Prcb), 0)) { + eprintf("Failed to read CPU #%d PRCB location\n", i); + return 1; + } + + if (va_space_rw(vs, Prcb + kdbg->OffsetPrcbContext, + &Context, sizeof(Context), 0)) { + eprintf("Failed to read CPU #%d ContextFrame location\n", i); + return 1; + } + + printf("Filling context for CPU #%d...\n", i); + win_context_init_from_qemu_cpu_state(&ctx, s); + + if (va_space_rw(vs, Context, &ctx, sizeof(ctx), 1)) { + eprintf("Failed to fill CPU #%d context\n", i); + return 1; + } + } + + return 0; +} + +static int write_dump(struct pa_space *ps, + WinDumpHeader64 *hdr, const char *name) +{ + FILE *dmp_file = fopen(name, "wb"); + size_t i; + + if (!dmp_file) { + eprintf("Failed to open output file \'%s\'\n", name); + return 1; + } + + printf("Writing header to file...\n"); + + if (fwrite(hdr, sizeof(*hdr), 1, dmp_file) != 1) { + eprintf("Failed to write dump header\n"); + fclose(dmp_file); + return 1; + } + + for (i = 0; i < ps->block_nr; i++) { + struct pa_block *b = &ps->block[i]; + + printf("Writing block #%zu/%zu to file...\n", i, ps->block_nr); + if (fwrite(b->addr, b->size, 1, dmp_file) != 1) { + eprintf("Failed to write dump header\n"); + fclose(dmp_file); + return 1; + } + } + + return fclose(dmp_file); +} + +static int pe_get_pdb_symstore_hash(uint64_t base, void *start_addr, + char *hash, struct va_space *vs) +{ + const char e_magic[2] = "MZ"; + const char Signature[4] = "PE\0\0"; + const char sign_rsds[4] = "RSDS"; + IMAGE_DOS_HEADER *dos_hdr = start_addr; + IMAGE_NT_HEADERS64 nt_hdrs; + IMAGE_FILE_HEADER *file_hdr = &nt_hdrs.FileHeader; + IMAGE_OPTIONAL_HEADER64 *opt_hdr = &nt_hdrs.OptionalHeader; + IMAGE_DATA_DIRECTORY *data_dir = nt_hdrs.OptionalHeader.DataDirectory; + IMAGE_DEBUG_DIRECTORY debug_dir; + OMFSignatureRSDS rsds; + char *pdb_name; + size_t pdb_name_sz; + size_t i; + + QEMU_BUILD_BUG_ON(sizeof(*dos_hdr) >= PAGE_SIZE); + + if (memcmp(&dos_hdr->e_magic, e_magic, sizeof(e_magic))) { + return 1; + } + + if (va_space_rw(vs, base + dos_hdr->e_lfanew, + &nt_hdrs, sizeof(nt_hdrs), 0)) { + return 1; + } + + if (memcmp(&nt_hdrs.Signature, Signature, sizeof(Signature)) || + file_hdr->Machine != 0x8664 || opt_hdr->Magic != 0x020b) { + return 1; + } + + printf("Debug Directory RVA = 0x%08"PRIx32"\n", + (uint32_t)data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress); + + if (va_space_rw(vs, + base + data_dir[IMAGE_FILE_DEBUG_DIRECTORY].VirtualAddress, + &debug_dir, sizeof(debug_dir), 0)) { + return 1; + } + + if (debug_dir.Type != IMAGE_DEBUG_TYPE_CODEVIEW) { + return 1; + } + + if (va_space_rw(vs, + base + debug_dir.AddressOfRawData, + &rsds, sizeof(rsds), 0)) { + return 1; + } + + printf("CodeView signature is \'%.4s\'\n", rsds.Signature); + + if (memcmp(&rsds.Signature, sign_rsds, sizeof(sign_rsds))) { + return 1; + } + + pdb_name_sz = debug_dir.SizeOfData - sizeof(rsds); + pdb_name = malloc(pdb_name_sz); + if (!pdb_name) { + return 1; + } + + if (va_space_rw(vs, base + debug_dir.AddressOfRawData + + offsetof(OMFSignatureRSDS, name), pdb_name, pdb_name_sz, 0)) { + free(pdb_name); + return 1; + } + + printf("PDB name is \'%s\', \'%s\' expected\n", pdb_name, PDB_NAME); + + if (strcmp(pdb_name, PDB_NAME)) { + eprintf("Unexpected PDB name, it seems the kernel isn't found\n"); + free(pdb_name); + return 1; + } + + free(pdb_name); + + sprintf(hash, "%.08x%.04x%.04x%.02x%.02x", rsds.guid.a, rsds.guid.b, + rsds.guid.c, rsds.guid.d[0], rsds.guid.d[1]); + hash += 20; + for (i = 0; i < 6; i++, hash += 2) { + sprintf(hash, "%.02x", rsds.guid.e[i]); + } + + sprintf(hash, "%.01x", rsds.age); + + return 0; +} + +int main(int argc, char *argv[]) +{ + int err = 0; + QEMU_Elf qemu_elf; + struct pa_space ps; + struct va_space vs; + QEMUCPUState *state; + idt_desc_t first_idt_desc; + uint64_t KernBase; + void *nt_start_addr = NULL; + WinDumpHeader64 header; + char pdb_hash[34]; + char pdb_url[] = SYM_URL_BASE PDB_NAME + "/0123456789ABCDEF0123456789ABCDEFx/" PDB_NAME; + struct pdb_reader pdb; + uint64_t KdDebuggerDataBlock; + KDDEBUGGER_DATA64 *kdbg; + uint64_t KdVersionBlock; + + if (argc != 3) { + eprintf("usage:\n\t%s elf_file dmp_file\n", argv[0]); + return 1; + } + + if (QEMU_Elf_init(&qemu_elf, argv[1])) { + eprintf("Failed to initialize QEMU ELF dump\n"); + return 1; + } + + if (pa_space_create(&ps, &qemu_elf)) { + eprintf("Failed to initialize physical address space\n"); + err = 1; + goto out_elf; + } + + state = qemu_elf.state[0]; + printf("CPU #0 CR3 is 0x%016"PRIx64"\n", state->cr[3]); + + va_space_create(&vs, &ps, state->cr[3]); + if (fix_dtb(&vs, &qemu_elf)) { + eprintf("Failed to find paging base\n"); + err = 1; + goto out_elf; + } + + printf("CPU #0 IDT is at 0x%016"PRIx64"\n", state->idt.base); + + if (va_space_rw(&vs, state->idt.base, + &first_idt_desc, sizeof(first_idt_desc), 0)) { + eprintf("Failed to get CPU #0 IDT[0]\n"); + err = 1; + goto out_ps; + } + printf("CPU #0 IDT[0] -> 0x%016"PRIx64"\n", idt_desc_addr(first_idt_desc)); + + KernBase = idt_desc_addr(first_idt_desc) & ~(PAGE_SIZE - 1); + printf("Searching kernel downwards from 0x%016"PRIx64"...\n", KernBase); + + for (; KernBase >= 0xfffff78000000000; KernBase -= PAGE_SIZE) { + nt_start_addr = va_space_resolve(&vs, KernBase); + if (!nt_start_addr) { + continue; + } + + if (*(uint16_t *)nt_start_addr == 0x5a4d) { /* MZ */ + break; + } + } + + if (!nt_start_addr) { + eprintf("Failed to find NT kernel image\n"); + err = 1; + goto out_ps; + } + + printf("KernBase = 0x%016"PRIx64", signature is \'%.2s\'\n", KernBase, + (char *)nt_start_addr); + + if (pe_get_pdb_symstore_hash(KernBase, nt_start_addr, pdb_hash, &vs)) { + eprintf("Failed to get PDB symbol store hash\n"); + err = 1; + goto out_ps; + } + + sprintf(pdb_url, "%s%s/%s/%s", SYM_URL_BASE, PDB_NAME, pdb_hash, PDB_NAME); + printf("PDB URL is %s\n", pdb_url); + + if (download_url(PDB_NAME, pdb_url)) { + eprintf("Failed to download PDB file\n"); + err = 1; + goto out_ps; + } + + if (pdb_init_from_file(PDB_NAME, &pdb)) { + eprintf("Failed to initialize PDB reader\n"); + err = 1; + goto out_pdb_file; + } + + if (!SYM_RESOLVE(KernBase, &pdb, KdDebuggerDataBlock) || + !SYM_RESOLVE(KernBase, &pdb, KdVersionBlock)) { + err = 1; + goto out_pdb; + } + + kdbg = get_kdbg(KernBase, &pdb, &vs, KdDebuggerDataBlock); + if (!kdbg) { + err = 1; + goto out_pdb; + } + + if (fill_header(&header, &ps, &vs, KdDebuggerDataBlock, kdbg, + KdVersionBlock, qemu_elf.state_nr)) { + err = 1; + goto out_pdb; + } + + if (fill_context(kdbg, &vs, &qemu_elf)) { + err = 1; + goto out_pdb; + } + + if (write_dump(&ps, &header, argv[2])) { + eprintf("Failed to save dump\n"); + err = 1; + goto out_kdbg; + } + +out_kdbg: + free(kdbg); +out_pdb: + pdb_exit(&pdb); +out_pdb_file: + unlink(PDB_NAME); +out_ps: + pa_space_destroy(&ps); +out_elf: + QEMU_Elf_exit(&qemu_elf); + + return err; +} diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c new file mode 100644 index 0000000000..64af20f584 --- /dev/null +++ b/contrib/elf2dmp/pdb.c @@ -0,0 +1,311 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * Based on source of Wine project + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "qemu/osdep.h" +#include "pdb.h" +#include "err.h" + +static uint32_t pdb_get_file_size(const struct pdb_reader *r, unsigned idx) +{ + return r->ds.toc->file_size[idx]; +} + +static pdb_seg *get_seg_by_num(struct pdb_reader *r, size_t n) +{ + size_t i = 0; + char *ptr; + + for (ptr = r->segs; (ptr < r->segs + r->segs_size); ) { + i++; + ptr += 8; + if (i == n) { + break; + } + ptr += sizeof(pdb_seg); + } + + return (pdb_seg *)ptr; +} + +uint64_t pdb_find_public_v3_symbol(struct pdb_reader *r, const char *name) +{ + size_t size = pdb_get_file_size(r, r->symbols->gsym_file); + int length; + const union codeview_symbol *sym; + const uint8_t *root = r->modimage; + size_t i; + + for (i = 0; i < size; i += length) { + sym = (const void *)(root + i); + length = sym->generic.len + 2; + + if (!sym->generic.id || length < 4) { + break; + } + + if (sym->generic.id == S_PUB_V3 && + !strcmp(name, sym->public_v3.name)) { + pdb_seg *segment = get_seg_by_num(r, sym->public_v3.segment); + uint32_t sect_rva = segment->dword[1]; + uint64_t rva = sect_rva + sym->public_v3.offset; + + printf("%s: 0x%016x(%d:\'%.8s\') + 0x%08x = 0x%09"PRIx64"\n", name, + sect_rva, sym->public_v3.segment, + ((char *)segment - 8), sym->public_v3.offset, rva); + return rva; + } + } + + return 0; +} + +uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name) +{ + uint64_t rva = pdb_find_public_v3_symbol(r, name); + + if (!rva) { + return 0; + } + + return img_base + rva; +} + +static void pdb_reader_ds_exit(struct pdb_reader *r) +{ + free(r->ds.toc); +} + +static void pdb_exit_symbols(struct pdb_reader *r) +{ + free(r->modimage); + free(r->symbols); +} + +static void pdb_exit_segments(struct pdb_reader *r) +{ + free(r->segs); +} + +static void *pdb_ds_read(const PDB_DS_HEADER *header, + const uint32_t *block_list, int size) +{ + int i, nBlocks; + uint8_t *buffer; + + if (!size) { + return NULL; + } + + nBlocks = (size + header->block_size - 1) / header->block_size; + + buffer = malloc(nBlocks * header->block_size); + if (!buffer) { + return NULL; + } + + for (i = 0; i < nBlocks; i++) { + memcpy(buffer + i * header->block_size, (const char *)header + + block_list[i] * header->block_size, header->block_size); + } + + return buffer; +} + +static void *pdb_ds_read_file(struct pdb_reader* r, uint32_t file_number) +{ + const uint32_t *block_list; + uint32_t block_size; + const uint32_t *file_size; + size_t i; + + if (!r->ds.toc || file_number >= r->ds.toc->num_files) { + return NULL; + } + + file_size = r->ds.toc->file_size; + r->file_used[file_number / 32] |= 1 << (file_number % 32); + + if (file_size[file_number] == 0 || file_size[file_number] == 0xFFFFFFFF) { + return NULL; + } + + block_list = file_size + r->ds.toc->num_files; + block_size = r->ds.header->block_size; + + for (i = 0; i < file_number; i++) { + block_list += (file_size[i] + block_size - 1) / block_size; + } + + return pdb_ds_read(r->ds.header, block_list, file_size[file_number]); +} + +static int pdb_init_segments(struct pdb_reader *r) +{ + char *segs; + unsigned stream_idx = r->sidx.segments; + + segs = pdb_ds_read_file(r, stream_idx); + if (!segs) { + return 1; + } + + r->segs = segs; + r->segs_size = pdb_get_file_size(r, stream_idx); + + return 0; +} + +static int pdb_init_symbols(struct pdb_reader *r) +{ + int err = 0; + PDB_SYMBOLS *symbols; + PDB_STREAM_INDEXES *sidx = &r->sidx; + + memset(sidx, -1, sizeof(*sidx)); + + symbols = pdb_ds_read_file(r, 3); + if (!symbols) { + return 1; + } + + r->symbols = symbols; + + if (symbols->stream_index_size != sizeof(PDB_STREAM_INDEXES)) { + err = 1; + goto out_symbols; + } + + memcpy(sidx, (const char *)symbols + sizeof(PDB_SYMBOLS) + + symbols->module_size + symbols->offset_size + + symbols->hash_size + symbols->srcmodule_size + + symbols->pdbimport_size + symbols->unknown2_size, sizeof(*sidx)); + + /* Read global symbol table */ + r->modimage = pdb_ds_read_file(r, symbols->gsym_file); + if (!r->modimage) { + err = 1; + goto out_symbols; + } + + return 0; + +out_symbols: + free(symbols); + + return err; +} + +static int pdb_reader_ds_init(struct pdb_reader *r, PDB_DS_HEADER *hdr) +{ + memset(r->file_used, 0, sizeof(r->file_used)); + r->ds.header = hdr; + r->ds.toc = pdb_ds_read(hdr, (uint32_t *)((uint8_t *)hdr + + hdr->toc_page * hdr->block_size), hdr->toc_size); + + if (!r->ds.toc) { + return 1; + } + + return 0; +} + +static int pdb_reader_init(struct pdb_reader *r, void *data) +{ + int err = 0; + const char pdb7[] = "Microsoft C/C++ MSF 7.00"; + + if (memcmp(data, pdb7, sizeof(pdb7) - 1)) { + return 1; + } + + if (pdb_reader_ds_init(r, data)) { + return 1; + } + + r->ds.root = pdb_ds_read_file(r, 1); + if (!r->ds.root) { + err = 1; + goto out_ds; + } + + if (pdb_init_symbols(r)) { + err = 1; + goto out_root; + } + + if (pdb_init_segments(r)) { + err = 1; + goto out_sym; + } + + return 0; + +out_sym: + pdb_exit_symbols(r); +out_root: + free(r->ds.root); +out_ds: + pdb_reader_ds_exit(r); + + return err; +} + +static void pdb_reader_exit(struct pdb_reader *r) +{ + pdb_exit_segments(r); + pdb_exit_symbols(r); + free(r->ds.root); + pdb_reader_ds_exit(r); +} + +int pdb_init_from_file(const char *name, struct pdb_reader *reader) +{ + GError *gerr = NULL; + int err = 0; + void *map; + + reader->gmf = g_mapped_file_new(name, TRUE, &gerr); + if (gerr) { + eprintf("Failed to map PDB file \'%s\'\n", name); + return 1; + } + + reader->file_size = g_mapped_file_get_length(reader->gmf); + map = g_mapped_file_get_contents(reader->gmf); + if (pdb_reader_init(reader, map)) { + err = 1; + goto out_unmap; + } + + return 0; + +out_unmap: + g_mapped_file_unref(reader->gmf); + + return err; +} + +void pdb_exit(struct pdb_reader *reader) +{ + g_mapped_file_unref(reader->gmf); + pdb_reader_exit(reader); +} diff --git a/contrib/elf2dmp/pdb.h b/contrib/elf2dmp/pdb.h new file mode 100644 index 0000000000..4ea8925ee8 --- /dev/null +++ b/contrib/elf2dmp/pdb.h @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef PDB_H +#define PDB_H + + +#ifndef _WIN32 +typedef struct GUID { + unsigned int Data1; + unsigned short Data2; + unsigned short Data3; + unsigned char Data4[8]; +} GUID; +#endif + +struct PDB_FILE { + uint32_t size; + uint32_t unknown; +}; + +typedef struct PDB_DS_HEADER { + char signature[32]; + uint32_t block_size; + uint32_t unknown1; + uint32_t num_pages; + uint32_t toc_size; + uint32_t unknown2; + uint32_t toc_page; +} PDB_DS_HEADER; + +typedef struct PDB_DS_TOC { + uint32_t num_files; + uint32_t file_size[1]; +} PDB_DS_TOC; + +typedef struct PDB_DS_ROOT { + uint32_t Version; + uint32_t TimeDateStamp; + uint32_t Age; + GUID guid; + uint32_t cbNames; + char names[1]; +} PDB_DS_ROOT; + +typedef struct PDB_TYPES_OLD { + uint32_t version; + uint16_t first_index; + uint16_t last_index; + uint32_t type_size; + uint16_t file; + uint16_t pad; +} PDB_TYPES_OLD; + +typedef struct PDB_TYPES { + uint32_t version; + uint32_t type_offset; + uint32_t first_index; + uint32_t last_index; + uint32_t type_size; + uint16_t file; + uint16_t pad; + uint32_t hash_size; + uint32_t hash_base; + uint32_t hash_offset; + uint32_t hash_len; + uint32_t search_offset; + uint32_t search_len; + uint32_t unknown_offset; + uint32_t unknown_len; +} PDB_TYPES; + +typedef struct PDB_SYMBOL_RANGE { + uint16_t segment; + uint16_t pad1; + uint32_t offset; + uint32_t size; + uint32_t characteristics; + uint16_t index; + uint16_t pad2; +} PDB_SYMBOL_RANGE; + +typedef struct PDB_SYMBOL_RANGE_EX { + uint16_t segment; + uint16_t pad1; + uint32_t offset; + uint32_t size; + uint32_t characteristics; + uint16_t index; + uint16_t pad2; + uint32_t timestamp; + uint32_t unknown; +} PDB_SYMBOL_RANGE_EX; + +typedef struct PDB_SYMBOL_FILE { + uint32_t unknown1; + PDB_SYMBOL_RANGE range; + uint16_t flag; + uint16_t file; + uint32_t symbol_size; + uint32_t lineno_size; + uint32_t unknown2; + uint32_t nSrcFiles; + uint32_t attribute; + char filename[1]; +} PDB_SYMBOL_FILE; + +typedef struct PDB_SYMBOL_FILE_EX { + uint32_t unknown1; + PDB_SYMBOL_RANGE_EX range; + uint16_t flag; + uint16_t file; + uint32_t symbol_size; + uint32_t lineno_size; + uint32_t unknown2; + uint32_t nSrcFiles; + uint32_t attribute; + uint32_t reserved[2]; + char filename[1]; +} PDB_SYMBOL_FILE_EX; + +typedef struct PDB_SYMBOL_SOURCE { + uint16_t nModules; + uint16_t nSrcFiles; + uint16_t table[1]; +} PDB_SYMBOL_SOURCE; + +typedef struct PDB_SYMBOL_IMPORT { + uint32_t unknown1; + uint32_t unknown2; + uint32_t TimeDateStamp; + uint32_t Age; + char filename[1]; +} PDB_SYMBOL_IMPORT; + +typedef struct PDB_SYMBOLS_OLD { + uint16_t hash1_file; + uint16_t hash2_file; + uint16_t gsym_file; + uint16_t pad; + uint32_t module_size; + uint32_t offset_size; + uint32_t hash_size; + uint32_t srcmodule_size; +} PDB_SYMBOLS_OLD; + +typedef struct PDB_SYMBOLS { + uint32_t signature; + uint32_t version; + uint32_t unknown; + uint32_t hash1_file; + uint32_t hash2_file; + uint16_t gsym_file; + uint16_t unknown1; + uint32_t module_size; + uint32_t offset_size; + uint32_t hash_size; + uint32_t srcmodule_size; + uint32_t pdbimport_size; + uint32_t resvd0; + uint32_t stream_index_size; + uint32_t unknown2_size; + uint16_t resvd3; + uint16_t machine; + uint32_t resvd4; +} PDB_SYMBOLS; + +typedef struct { + uint16_t FPO; + uint16_t unk0; + uint16_t unk1; + uint16_t unk2; + uint16_t unk3; + uint16_t segments; +} PDB_STREAM_INDEXES_OLD; + +typedef struct { + uint16_t FPO; + uint16_t unk0; + uint16_t unk1; + uint16_t unk2; + uint16_t unk3; + uint16_t segments; + uint16_t unk4; + uint16_t unk5; + uint16_t unk6; + uint16_t FPO_EXT; + uint16_t unk7; +} PDB_STREAM_INDEXES; + +union codeview_symbol { + struct { + int16_t len; + int16_t id; + } generic; + + struct { + int16_t len; + int16_t id; + uint32_t symtype; + uint32_t offset; + uint16_t segment; + char name[1]; + } public_v3; +}; + +#define S_PUB_V3 0x110E + +typedef struct pdb_seg { + uint32_t dword[8]; +} __attribute__ ((packed)) pdb_seg; + +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_AMD64 0x8664 + +struct pdb_reader { + GMappedFile *gmf; + size_t file_size; + struct { + PDB_DS_HEADER *header; + PDB_DS_TOC *toc; + PDB_DS_ROOT *root; + } ds; + uint32_t file_used[1024]; + PDB_SYMBOLS *symbols; + PDB_STREAM_INDEXES sidx; + uint8_t *modimage; + char *segs; + size_t segs_size; +}; + +int pdb_init_from_file(const char *name, struct pdb_reader *reader); +void pdb_exit(struct pdb_reader *reader); +uint64_t pdb_resolve(uint64_t img_base, struct pdb_reader *r, const char *name); +uint64_t pdb_find_public_v3_symbol(struct pdb_reader *reader, const char *name); + +#endif /* PDB_H */ diff --git a/contrib/elf2dmp/pe.h b/contrib/elf2dmp/pe.h new file mode 100644 index 0000000000..c2a4a6ba7c --- /dev/null +++ b/contrib/elf2dmp/pe.h @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef PE_H +#define PE_H + + +#ifndef _WIN32 +typedef struct IMAGE_DOS_HEADER { + uint16_t e_magic; /* 0x00: MZ Header signature */ + uint16_t e_cblp; /* 0x02: Bytes on last page of file */ + uint16_t e_cp; /* 0x04: Pages in file */ + uint16_t e_crlc; /* 0x06: Relocations */ + uint16_t e_cparhdr; /* 0x08: Size of header in paragraphs */ + uint16_t e_minalloc; /* 0x0a: Minimum extra paragraphs needed */ + uint16_t e_maxalloc; /* 0x0c: Maximum extra paragraphs needed */ + uint16_t e_ss; /* 0x0e: Initial (relative) SS value */ + uint16_t e_sp; /* 0x10: Initial SP value */ + uint16_t e_csum; /* 0x12: Checksum */ + uint16_t e_ip; /* 0x14: Initial IP value */ + uint16_t e_cs; /* 0x16: Initial (relative) CS value */ + uint16_t e_lfarlc; /* 0x18: File address of relocation table */ + uint16_t e_ovno; /* 0x1a: Overlay number */ + uint16_t e_res[4]; /* 0x1c: Reserved words */ + uint16_t e_oemid; /* 0x24: OEM identifier (for e_oeminfo) */ + uint16_t e_oeminfo; /* 0x26: OEM information; e_oemid specific */ + uint16_t e_res2[10]; /* 0x28: Reserved words */ + uint32_t e_lfanew; /* 0x3c: Offset to extended header */ +} __attribute__ ((packed)) IMAGE_DOS_HEADER; + +typedef struct IMAGE_FILE_HEADER { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; +} __attribute__ ((packed)) IMAGE_FILE_HEADER; + +typedef struct IMAGE_DATA_DIRECTORY { + uint32_t VirtualAddress; + uint32_t Size; +} __attribute__ ((packed)) IMAGE_DATA_DIRECTORY; + +#define IMAGE_NUMBEROF_DIRECTORY_ENTRIES 16 + +typedef struct IMAGE_OPTIONAL_HEADER64 { + uint16_t Magic; /* 0x20b */ + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint64_t ImageBase; + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint64_t SizeOfStackReserve; + uint64_t SizeOfStackCommit; + uint64_t SizeOfHeapReserve; + uint64_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES]; +} __attribute__ ((packed)) IMAGE_OPTIONAL_HEADER64; + +typedef struct IMAGE_NT_HEADERS64 { + uint32_t Signature; + IMAGE_FILE_HEADER FileHeader; + IMAGE_OPTIONAL_HEADER64 OptionalHeader; +} __attribute__ ((packed)) IMAGE_NT_HEADERS64; + +typedef struct IMAGE_DEBUG_DIRECTORY { + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint32_t Type; + uint32_t SizeOfData; + uint32_t AddressOfRawData; + uint32_t PointerToRawData; +} __attribute__ ((packed)) IMAGE_DEBUG_DIRECTORY; + +#define IMAGE_DEBUG_TYPE_CODEVIEW 2 +#endif + +#define IMAGE_FILE_DEBUG_DIRECTORY 6 + +typedef struct guid_t { + uint32_t a; + uint16_t b; + uint16_t c; + uint8_t d[2]; + uint8_t e[6]; +} __attribute__ ((packed)) guid_t; + +typedef struct OMFSignatureRSDS { + char Signature[4]; + guid_t guid; + uint32_t age; + char name[]; +} __attribute__ ((packed)) OMFSignatureRSDS; + +#endif /* PE_H */ diff --git a/contrib/elf2dmp/qemu_elf.c b/contrib/elf2dmp/qemu_elf.c new file mode 100644 index 0000000000..0db7816586 --- /dev/null +++ b/contrib/elf2dmp/qemu_elf.c @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#include "qemu/osdep.h" +#include "err.h" +#include "qemu_elf.h" + +#define QEMU_NOTE_NAME "QEMU" + +#ifndef ROUND_UP +#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d))) +#endif + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +#define ELF_NOTE_SIZE(hdr_size, name_size, desc_size) \ + ((DIV_ROUND_UP((hdr_size), 4) + \ + DIV_ROUND_UP((name_size), 4) + \ + DIV_ROUND_UP((desc_size), 4)) * 4) + +int is_system(QEMUCPUState *s) +{ + return s->gs.base >> 63; +} + +static char *nhdr_get_name(Elf64_Nhdr *nhdr) +{ + return (char *)nhdr + ROUND_UP(sizeof(*nhdr), 4); +} + +static void *nhdr_get_desc(Elf64_Nhdr *nhdr) +{ + return nhdr_get_name(nhdr) + ROUND_UP(nhdr->n_namesz, 4); +} + +static Elf64_Nhdr *nhdr_get_next(Elf64_Nhdr *nhdr) +{ + return (void *)((uint8_t *)nhdr + ELF_NOTE_SIZE(sizeof(*nhdr), + nhdr->n_namesz, nhdr->n_descsz)); +} + +Elf64_Phdr *elf64_getphdr(void *map) +{ + Elf64_Ehdr *ehdr = map; + Elf64_Phdr *phdr = (void *)((uint8_t *)map + ehdr->e_phoff); + + return phdr; +} + +Elf64_Half elf_getphdrnum(void *map) +{ + Elf64_Ehdr *ehdr = map; + + return ehdr->e_phnum; +} + +static int init_states(QEMU_Elf *qe) +{ + Elf64_Phdr *phdr = elf64_getphdr(qe->map); + Elf64_Nhdr *start = (void *)((uint8_t *)qe->map + phdr[0].p_offset); + Elf64_Nhdr *end = (void *)((uint8_t *)start + phdr[0].p_memsz); + Elf64_Nhdr *nhdr; + size_t cpu_nr = 0; + + if (phdr[0].p_type != PT_NOTE) { + eprintf("Failed to find PT_NOTE\n"); + return 1; + } + + qe->has_kernel_gs_base = 1; + + for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { + if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { + QEMUCPUState *state = nhdr_get_desc(nhdr); + + if (state->size < sizeof(*state)) { + eprintf("CPU #%zu: QEMU CPU state size %u doesn't match\n", + cpu_nr, state->size); + /* + * We assume either every QEMU CPU state has KERNEL_GS_BASE or + * no one has. + */ + qe->has_kernel_gs_base = 0; + } + cpu_nr++; + } + } + + printf("%zu CPU states has been found\n", cpu_nr); + + qe->state = malloc(sizeof(*qe->state) * cpu_nr); + if (!qe->state) { + return 1; + } + + cpu_nr = 0; + + for (nhdr = start; nhdr < end; nhdr = nhdr_get_next(nhdr)) { + if (!strcmp(nhdr_get_name(nhdr), QEMU_NOTE_NAME)) { + qe->state[cpu_nr] = nhdr_get_desc(nhdr); + cpu_nr++; + } + } + + qe->state_nr = cpu_nr; + + return 0; +} + +static void exit_states(QEMU_Elf *qe) +{ + free(qe->state); +} + +int QEMU_Elf_init(QEMU_Elf *qe, const char *filename) +{ + GError *gerr = NULL; + int err = 0; + + qe->gmf = g_mapped_file_new(filename, TRUE, &gerr); + if (gerr) { + eprintf("Failed to map ELF dump file \'%s\'\n", filename); + return 1; + } + + qe->map = g_mapped_file_get_contents(qe->gmf); + qe->size = g_mapped_file_get_length(qe->gmf); + + if (init_states(qe)) { + eprintf("Failed to extract QEMU CPU states\n"); + err = 1; + goto out_unmap; + } + + return 0; + +out_unmap: + g_mapped_file_unref(qe->gmf); + + return err; +} + +void QEMU_Elf_exit(QEMU_Elf *qe) +{ + exit_states(qe); + g_mapped_file_unref(qe->gmf); +} diff --git a/contrib/elf2dmp/qemu_elf.h b/contrib/elf2dmp/qemu_elf.h new file mode 100644 index 0000000000..2a7963821a --- /dev/null +++ b/contrib/elf2dmp/qemu_elf.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2018 Virtuozzo International GmbH + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * + */ + +#ifndef ELF2DMP_ELF_H +#define ELF2DMP_ELF_H + +#include "elf.h" + +typedef struct QEMUCPUSegment { + uint32_t selector; + uint32_t limit; + uint32_t flags; + uint32_t pad; + uint64_t base; +} QEMUCPUSegment; + +typedef struct QEMUCPUState { + uint32_t version; + uint32_t size; + uint64_t rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp; + uint64_t r8, r9, r10, r11, r12, r13, r14, r15; + uint64_t rip, rflags; + QEMUCPUSegment cs, ds, es, fs, gs, ss; + QEMUCPUSegment ldt, tr, gdt, idt; + uint64_t cr[5]; + uint64_t kernel_gs_base; +} QEMUCPUState; + +int is_system(QEMUCPUState *s); + +typedef struct QEMU_Elf { + GMappedFile *gmf; + size_t size; + void *map; + QEMUCPUState **state; + size_t state_nr; + int has_kernel_gs_base; +} QEMU_Elf; + +int QEMU_Elf_init(QEMU_Elf *qe, const char *filename); +void QEMU_Elf_exit(QEMU_Elf *qe); + +Elf64_Phdr *elf64_getphdr(void *map); +Elf64_Half elf_getphdrnum(void *map); + +#endif /* ELF2DMP_ELF_H */ diff --git a/contrib/gitdm/aliases b/contrib/gitdm/aliases new file mode 100644 index 0000000000..07fd3391a5 --- /dev/null +++ b/contrib/gitdm/aliases @@ -0,0 +1,27 @@ +# +# This is the email aliases file, mapping secondary addresses +# onto a single, canonical address. Duplicates some info from .mailmap +# + +# weird commits +balrog@c046a42c-6fe2-441c-8c8c-71466251a162 balrogg@gmail.com +aliguori@c046a42c-6fe2-441c-8c8c-71466251a162 anthony@codemonkey.ws +aurel32@c046a42c-6fe2-441c-8c8c-71466251a162 aurelien@aurel32.net +blueswir1@c046a42c-6fe2-441c-8c8c-71466251a162 blauwirbel@gmail.com +edgar_igl@c046a42c-6fe2-441c-8c8c-71466251a162 edgar.iglesias@gmail.com +bellard@c046a42c-6fe2-441c-8c8c-71466251a162 fabrice@bellard.org +j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162 l_indien@magic.fr +pbrook@c046a42c-6fe2-441c-8c8c-71466251a162 paul@codesourcery.com +ths@c046a42c-6fe2-441c-8c8c-71466251a162 ths@networkno.de +malc@c046a42c-6fe2-441c-8c8c-71466251a162 av1474@comtv.ru + +# There is also a: +# (no author) <(no author)@c046a42c-6fe2-441c-8c8c-71466251a162> +# for the cvs2svn initialization commit e63c3dc74bf. + +# Next, translate a few commits where mailman rewrote the From: line due +# to strict SPF, although we prefer to avoid adding more entries like that. +"Ed Swierk via Qemu-devel" eswierk@skyportsystems.com +"Ian McKellar via Qemu-devel" ianloic@google.com +"Julia Suvorova via Qemu-devel" jusual@mail.ru +"Justin Terry (VM) via Qemu-devel" juterry@microsoft.com diff --git a/contrib/gitdm/domain-map b/contrib/gitdm/domain-map new file mode 100644 index 0000000000..fa9d454473 --- /dev/null +++ b/contrib/gitdm/domain-map @@ -0,0 +1,26 @@ +# +# QEMU gitdm domain-map +# +# This maps email domains to nice easy to read company names +# + +amd.com AMD +citrix.com Citrix +greensocs.com GreenSocs +fujitsu.com Fujitsu +huawei.com Huawei +ibm.com IBM +igalia.com Igalia +intel.com Intel +linaro.org Linaro +microsoft.com Microsoft +nokia.com Nokia +oracle.com Oracle +proxmox.com Proxmox +redhat.com Red Hat +siemens.com Siemens +sifive.com SiFive +suse.de SUSE +virtuozzo.com Virtuozzo +wdc.com Western Digital +xilinx.com Xilinx diff --git a/contrib/gitdm/filetypes.txt b/contrib/gitdm/filetypes.txt new file mode 100644 index 0000000000..165b71b3f9 --- /dev/null +++ b/contrib/gitdm/filetypes.txt @@ -0,0 +1,146 @@ +# -*- coding:utf-8 -*- +# Copyright (C) 2006 Libresoft +# +# 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 Library 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. +# +# Authors : Gregorio Robles +# Authors : Germán Póo-Caamaño +# +# This QEMU version is a cut-down version of what originally shipped +# in the gitdm sample-config directory. +# +# This file contains associations parameters regarding filetypes +# (documentation, develompent, multimedia, images...) +# +# format: +# filetype [] +# +# Order: +# The list should keep an order, so filetypes can be counted properly. +# ie. we want ltmain.sh -> 'build' instead of 'code'. +# +# If there is an filetype which is not in order but has values, it will +# be added at the end. +# +order build,tests,code,documentation,devel-doc,blobs + +# +# +# Code files (headers and the like included +# (most common languages first +# +filetype code \.c$ # C +filetype code \.inc.c$ # C +filetype code \.C$ # C++ +filetype code \.cpp$ # C++ +filetype code \.c\+\+$ # C++ +filetype code \.cxx$ # C++ +filetype code \.cc$ # C++ +filetype code \.h$ # C or C++ header +filetype code \.hh$ # C++ header +filetype code \.hpp$ # C++ header +filetype code \.hxx$ # C++ header +filetype code \.sh$ # Shell +filetype code \.pl$ # Perl +filetype code \.py$ # Python +filetype code \.s$ # Assembly +filetype code \.S$ # Assembly +filetype code \.asm$ # Assembly +filetype code \.awk$ # awk +filetype code ^common$ # script fragements +filetype code ^common.*$ # script fragements +filetype code (qom|qmp)-\w+$ # python script fragments + +# +# Interface/api files +# +filetype interface \.json$ # json +filetype interface \.hx$ # documented options + +# +# Test related blobs (unfortunately we can't filter out test code) +# +filetype tests \.hex$ +filetype tests \d{2,3}$ # test data 00-999 +filetype tests ^[A-Z]{4}$ # ACPI test data +filetype tests ^[A-Z]{4}\.*$ # ACPI test data +filetype tests \.out$ +filetype tests \.out\.nocache$ +filetype tests \.err$ +filetype tests \.exit$ # bad-if-FOO.exit etc +filetype tests \.decode$ +filetype tests \.yml$ # travis/shippable config + +# +# Development documentation files (for hacking generally) +# +filetype devel-doc ^readme.*$ +filetype devel-doc ^changelog.* +filetype devel-doc ^hacking.*$ +filetype devel-doc ^licen(s|c)e.*$ +filetype devel-doc ^copying.*$ +filetype devel-doc ^MAINTAINERS$ +filetype devel-doc ^BSD-2-Clause$ +filetype devel-doc ^BSD-3-Clause$ +filetype devel-doc ^GPL-2.0$ +filetype devel-doc \.txt$ +filetype devel-doc \.rst$ +filetype devel-doc \.texi$ +filetype devel-doc \.pod$ + +# +# Building, compiling, and configuration admin files +# +filetype build configure.*$ +filetype build Makefile$ +filetype build Makefile\.*$ +filetype build config$ +filetype build conf$ +filetype build \.cfg$ +filetype build \.mk$ +filetype build \.mak$ +filetype build \.docker$ +filetype build \.pre$ +filetype build ^.gitignore$ +filetype build ^.gitmodules$ +filetype build ^.gitpublish$ +filetype build ^.mailmap$ +filetype build ^.dir-locals.el$ +filetype build ^.editorconfig$ +filetype build ^.exrc$ +filetype build ^.gdbinit$ +filetype build \.cocci$ # Coccinelle semantic patches + +# +# Misc blobs +# +filetype blobs \.bin$ +filetype blobs \.dtb$ +filetype blobs \.dts$ +filetype blobs \.rom$ +filetype blobs \.img$ +filetype blobs \.ndrv$ +filetype blobs \.bmp$ +filetype blobs \.svg$ +filetype blobs ^pi_10.com$ + + +# +# Documentation files +# +filetype documentation \.html$ +filetype documentation \.txt$ +filetype documentation \.texi$ +filetype documentation \.po$ # translation files diff --git a/contrib/gitdm/group-map-academics b/contrib/gitdm/group-map-academics new file mode 100644 index 0000000000..08f9d81d13 --- /dev/null +++ b/contrib/gitdm/group-map-academics @@ -0,0 +1,14 @@ +# +# QEMU is quite often used for academic research purposes and we like +# it even better when the work is up-streamed so the project can +# benefit. +# +# We group our academic contributors here +# + +# Institute for System Programming of Russian Academy of Science +ispras.ru + +# Columbia University +cs.columbia.edu +cota@braap.org diff --git a/contrib/gitdm/group-map-cadence b/contrib/gitdm/group-map-cadence new file mode 100644 index 0000000000..ab97dd2fc3 --- /dev/null +++ b/contrib/gitdm/group-map-cadence @@ -0,0 +1,3 @@ +# Cadence Design Systems + +jcmvbkbc@gmail.com diff --git a/contrib/gitdm/group-map-codeweavers b/contrib/gitdm/group-map-codeweavers new file mode 100644 index 0000000000..c4803489e2 --- /dev/null +++ b/contrib/gitdm/group-map-codeweavers @@ -0,0 +1 @@ +sergio.g.delreal@gmail.com diff --git a/contrib/gitdm/group-map-ibm b/contrib/gitdm/group-map-ibm new file mode 100644 index 0000000000..da62fa3f44 --- /dev/null +++ b/contrib/gitdm/group-map-ibm @@ -0,0 +1,14 @@ +# +# Some IBM contributors submit via another domain +# + +aik@ozlabs.ru +andrew@aj.id.au +benh@kernel.crashing.org +clg@kaod.org +danielhb413@gmail.com +groug@kaod.org +jcfaracco@gmail.com +joel@jms.id.au +sjitindarsingh@gmail.com +tommusta@gmail.com diff --git a/contrib/gitdm/group-map-individuals b/contrib/gitdm/group-map-individuals new file mode 100644 index 0000000000..05e355d30e --- /dev/null +++ b/contrib/gitdm/group-map-individuals @@ -0,0 +1,15 @@ +# +# Individual and personal contributors +# +# This is simply to allow prolific developers with no company +# affiliations to be grouped together in the summary stats. +# + +f4bug@amsat.org +mjt@tls.msk.ru +mark.cave-ayland@ilande.co.uk +rth@twiddle.net +noring@nocrew.org +samuel.thibault@ens-lyon.org +aurelien@aurel32.net +balaton@eik.bme.hu diff --git a/contrib/gitdm/group-map-janustech b/contrib/gitdm/group-map-janustech new file mode 100644 index 0000000000..4ae7cc24f2 --- /dev/null +++ b/contrib/gitdm/group-map-janustech @@ -0,0 +1,5 @@ +# +# Janus Technologies contributors using non-corporate email +# + +marcel.apfelbaum@gmail.com diff --git a/contrib/gitdm/group-map-redhat b/contrib/gitdm/group-map-redhat new file mode 100644 index 0000000000..6d05c6b54f --- /dev/null +++ b/contrib/gitdm/group-map-redhat @@ -0,0 +1,7 @@ +# +# Red Hat contributors using non-corporate email +# + +david@gibson.dropbear.id.au +laurent@vivier.eu +pjp@fedoraproject.org diff --git a/contrib/gitdm/group-map-wavecomp b/contrib/gitdm/group-map-wavecomp new file mode 100644 index 0000000000..c5c57f0eaf --- /dev/null +++ b/contrib/gitdm/group-map-wavecomp @@ -0,0 +1,31 @@ +# +# Wave Computing acquired MIPS in June 2018. Also, from February 2013 +# to October 2017, MIPS was owned by Imagination Technologies. +# + +aleksandar.markovic@imgtec.com +aleksandar.markovic@mips.com +alex.smith@imgtec.com +andrew.bennett@imgtec.com +amarkovic@wavecomp.com +arikalo@wavecomp.com +chris@mips.com +dnikolic@wavecomp.com +ericj@mips.com +goran.ferenc@imgtec.com +james.cowgill@mips.com +james.hogan@imgtec.com +james.hogan@mips.com +leon.alrae@imgtec.com +matt.redfearn@imgtec.com +matthew.fortune@mips.com +miodrag.dinic@imgtec.com +paul@archlinuxmips.org +paul.burton@imgtec.com +petar.jovanovic@imgtec.com +petarj@mips.com +pburton@wavecomp.com +smarkovic@wavecomp.com +yongbok.kim@imgtec.com +yongbok.kim@mips.com +ysu@wavecomp.com diff --git a/contrib/ivshmem-client/ivshmem-client.h b/contrib/ivshmem-client/ivshmem-client.h index 5ee942262b..fe3cc4a03d 100644 --- a/contrib/ivshmem-client/ivshmem-client.h +++ b/contrib/ivshmem-client/ivshmem-client.h @@ -46,9 +46,7 @@ typedef struct IvshmemClientPeer { 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; /** @@ -73,7 +71,7 @@ struct IvshmemClient { int sock_fd; /**< unix sock filedesc */ int shm_fd; /**< shm file descriptor */ - IvshmemClientPeerList peer_list; /**< list of peers */ + QTAILQ_HEAD(, IvshmemClientPeer) peer_list; /**< list of peers */ IvshmemClientPeer local; /**< our own infos */ IvshmemClientNotifCb notif_cb; /**< notification callback */ diff --git a/contrib/ivshmem-server/ivshmem-server.h b/contrib/ivshmem-server/ivshmem-server.h index 4af08e1bb7..d870adb6a0 100644 --- a/contrib/ivshmem-server/ivshmem-server.h +++ b/contrib/ivshmem-server/ivshmem-server.h @@ -52,9 +52,6 @@ typedef struct IvshmemServerPeer { 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 @@ -72,7 +69,7 @@ typedef struct IvshmemServer { 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 */ + QTAILQ_HEAD(, IvshmemServerPeer) peer_list; /**< list of peers */ } IvshmemServer; /** diff --git a/contrib/libvhost-user/libvhost-user-glib.c b/contrib/libvhost-user/libvhost-user-glib.c index 545f089587..42660a1b36 100644 --- a/contrib/libvhost-user/libvhost-user-glib.c +++ b/contrib/libvhost-user/libvhost-user-glib.c @@ -68,15 +68,16 @@ static GSourceFuncs vug_src_funcs = { NULL }; -static GSource * -vug_source_new(VuDev *dev, int fd, GIOCondition cond, +GSource * +vug_source_new(VugDev *gdev, int fd, GIOCondition cond, vu_watch_cb vu_cb, gpointer data) { + VuDev *dev = &gdev->parent; GSource *gsrc; VugSrc *src; guint id; - g_assert(dev); + g_assert(gdev); g_assert(fd >= 0); g_assert(vu_cb); @@ -106,7 +107,7 @@ set_watch(VuDev *vu_dev, int fd, int vu_evt, vu_watch_cb cb, void *pvt) g_assert(cb); dev = container_of(vu_dev, VugDev, parent); - src = vug_source_new(vu_dev, fd, vu_evt, cb, pvt); + src = vug_source_new(dev, fd, vu_evt, cb, pvt); g_hash_table_replace(dev->fdmap, GINT_TO_POINTER(fd), src); } @@ -141,7 +142,7 @@ vug_init(VugDev *dev, int socket, dev->fdmap = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify) g_source_destroy); - dev->src = vug_source_new(&dev->parent, socket, G_IO_IN, vug_watch, NULL); + dev->src = vug_source_new(dev, socket, G_IO_IN, vug_watch, NULL); } void diff --git a/contrib/libvhost-user/libvhost-user-glib.h b/contrib/libvhost-user/libvhost-user-glib.h index 6b2110b94c..d3200f3afc 100644 --- a/contrib/libvhost-user/libvhost-user-glib.h +++ b/contrib/libvhost-user/libvhost-user-glib.h @@ -29,4 +29,7 @@ void vug_init(VugDev *dev, int socket, vu_panic_cb panic, const VuDevIface *iface); void vug_deinit(VugDev *dev); +GSource *vug_source_new(VugDev *dev, int fd, GIOCondition cond, + vu_watch_cb vu_cb, gpointer data); + #endif /* LIBVHOST_USER_GLIB_H */ diff --git a/contrib/libvhost-user/libvhost-user.c b/contrib/libvhost-user/libvhost-user.c index a6b46cdc03..e08d6c7b97 100644 --- a/contrib/libvhost-user/libvhost-user.c +++ b/contrib/libvhost-user/libvhost-user.c @@ -41,6 +41,8 @@ #endif #include "qemu/atomic.h" +#include "qemu/osdep.h" +#include "qemu/memfd.h" #include "libvhost-user.h" @@ -53,6 +55,18 @@ _min1 < _min2 ? _min1 : _min2; }) #endif +/* Round number down to multiple */ +#define ALIGN_DOWN(n, m) ((n) / (m) * (m)) + +/* Round number up to multiple */ +#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m)) + +/* Align each region to cache line size in inflight buffer */ +#define INFLIGHT_ALIGNMENT 64 + +/* The version of inflight buffer */ +#define INFLIGHT_VERSION 1 + #define VHOST_USER_HDR_SIZE offsetof(VhostUserMsg, payload.u64) /* The version of the protocol we support */ @@ -66,6 +80,20 @@ } \ } while (0) +static inline +bool has_feature(uint64_t features, unsigned int fbit) +{ + assert(fbit < 64); + return !!(features & (1ULL << fbit)); +} + +static inline +bool vu_has_feature(VuDev *dev, + unsigned int fbit) +{ + return has_feature(dev->features, fbit); +} + static const char * vu_request_to_string(unsigned int req) { @@ -100,6 +128,8 @@ vu_request_to_string(unsigned int req) REQ(VHOST_USER_POSTCOPY_ADVISE), REQ(VHOST_USER_POSTCOPY_LISTEN), REQ(VHOST_USER_POSTCOPY_END), + REQ(VHOST_USER_GET_INFLIGHT_FD), + REQ(VHOST_USER_SET_INFLIGHT_FD), REQ(VHOST_USER_MAX), }; #undef REQ @@ -790,10 +820,10 @@ vu_set_vring_addr_exec(VuDev *dev, VhostUserMsg *vmsg) 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); + DPRINT(" desc_user_addr: 0x%016" PRIx64 "\n", vra->desc_user_addr); + DPRINT(" used_user_addr: 0x%016" PRIx64 "\n", vra->used_user_addr); + DPRINT(" avail_user_addr: 0x%016" PRIx64 "\n", vra->avail_user_addr); + DPRINT(" log_guest_addr: 0x%016" PRIx64 "\n", vra->log_guest_addr); vq->vring.flags = vra->flags; vq->vring.desc = qva_to_va(dev, vra->desc_user_addr); @@ -890,6 +920,91 @@ vu_check_queue_msg_file(VuDev *dev, VhostUserMsg *vmsg) return true; } +static int +inflight_desc_compare(const void *a, const void *b) +{ + VuVirtqInflightDesc *desc0 = (VuVirtqInflightDesc *)a, + *desc1 = (VuVirtqInflightDesc *)b; + + if (desc1->counter > desc0->counter && + (desc1->counter - desc0->counter) < VIRTQUEUE_MAX_SIZE * 2) { + return 1; + } + + return -1; +} + +static int +vu_check_queue_inflights(VuDev *dev, VuVirtq *vq) +{ + int i = 0; + + if (!has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + if (unlikely(!vq->inflight)) { + return -1; + } + + if (unlikely(!vq->inflight->version)) { + /* initialize the buffer */ + vq->inflight->version = INFLIGHT_VERSION; + return 0; + } + + vq->used_idx = vq->vring.used->idx; + vq->resubmit_num = 0; + vq->resubmit_list = NULL; + vq->counter = 0; + + if (unlikely(vq->inflight->used_idx != vq->used_idx)) { + vq->inflight->desc[vq->inflight->last_batch_head].inflight = 0; + + barrier(); + + vq->inflight->used_idx = vq->used_idx; + } + + for (i = 0; i < vq->inflight->desc_num; i++) { + if (vq->inflight->desc[i].inflight == 1) { + vq->inuse++; + } + } + + vq->shadow_avail_idx = vq->last_avail_idx = vq->inuse + vq->used_idx; + + if (vq->inuse) { + vq->resubmit_list = malloc(sizeof(VuVirtqInflightDesc) * vq->inuse); + if (!vq->resubmit_list) { + return -1; + } + + for (i = 0; i < vq->inflight->desc_num; i++) { + if (vq->inflight->desc[i].inflight) { + vq->resubmit_list[vq->resubmit_num].index = i; + vq->resubmit_list[vq->resubmit_num].counter = + vq->inflight->desc[i].counter; + vq->resubmit_num++; + } + } + + if (vq->resubmit_num > 1) { + qsort(vq->resubmit_list, vq->resubmit_num, + sizeof(VuVirtqInflightDesc), inflight_desc_compare); + } + vq->counter = vq->resubmit_list[0].counter + 1; + } + + /* in case of I/O hang after reconnecting */ + if (eventfd_write(vq->kick_fd, 1)) { + return -1; + } + + return 0; +} + static bool vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg) { @@ -907,10 +1022,8 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].kick_fd = -1; } - if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) { - dev->vq[index].kick_fd = vmsg->fds[0]; - DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index); - } + dev->vq[index].kick_fd = vmsg->fds[0]; + DPRINT("Got kick_fd: %d for vq: %d\n", vmsg->fds[0], index); dev->vq[index].started = true; if (dev->iface->queue_set_started) { @@ -925,6 +1038,10 @@ vu_set_vring_kick_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].kick_fd, index); } + if (vu_check_queue_inflights(dev, &dev->vq[index])) { + vu_panic(dev, "Failed to check inflights for vq: %d\n", index); + } + return false; } @@ -995,8 +1112,11 @@ vu_set_vring_call_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].call_fd = -1; } - if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) { - dev->vq[index].call_fd = vmsg->fds[0]; + dev->vq[index].call_fd = vmsg->fds[0]; + + /* in case of I/O hang after reconnecting */ + if (eventfd_write(vmsg->fds[0], 1)) { + return -1; } DPRINT("Got call_fd: %d for vq: %d\n", vmsg->fds[0], index); @@ -1020,9 +1140,7 @@ vu_set_vring_err_exec(VuDev *dev, VhostUserMsg *vmsg) dev->vq[index].err_fd = -1; } - if (!(vmsg->payload.u64 & VHOST_USER_VRING_NOFD_MASK)) { - dev->vq[index].err_fd = vmsg->fds[0]; - } + dev->vq[index].err_fd = vmsg->fds[0]; return false; } @@ -1215,6 +1333,116 @@ vu_set_postcopy_end(VuDev *dev, VhostUserMsg *vmsg) return true; } +static inline uint64_t +vu_inflight_queue_size(uint16_t queue_size) +{ + return ALIGN_UP(sizeof(VuDescStateSplit) * queue_size + + sizeof(uint16_t), INFLIGHT_ALIGNMENT); +} + +static bool +vu_get_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) +{ + int fd; + void *addr; + uint64_t mmap_size; + uint16_t num_queues, queue_size; + + if (vmsg->size != sizeof(vmsg->payload.inflight)) { + vu_panic(dev, "Invalid get_inflight_fd message:%d", vmsg->size); + vmsg->payload.inflight.mmap_size = 0; + return true; + } + + num_queues = vmsg->payload.inflight.num_queues; + queue_size = vmsg->payload.inflight.queue_size; + + DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); + DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size); + + mmap_size = vu_inflight_queue_size(queue_size) * num_queues; + + addr = qemu_memfd_alloc("vhost-inflight", mmap_size, + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL, + &fd, NULL); + + if (!addr) { + vu_panic(dev, "Failed to alloc vhost inflight area"); + vmsg->payload.inflight.mmap_size = 0; + return true; + } + + memset(addr, 0, mmap_size); + + dev->inflight_info.addr = addr; + dev->inflight_info.size = vmsg->payload.inflight.mmap_size = mmap_size; + dev->inflight_info.fd = vmsg->fds[0] = fd; + vmsg->fd_num = 1; + vmsg->payload.inflight.mmap_offset = 0; + + DPRINT("send inflight mmap_size: %"PRId64"\n", + vmsg->payload.inflight.mmap_size); + DPRINT("send inflight mmap offset: %"PRId64"\n", + vmsg->payload.inflight.mmap_offset); + + return true; +} + +static bool +vu_set_inflight_fd(VuDev *dev, VhostUserMsg *vmsg) +{ + int fd, i; + uint64_t mmap_size, mmap_offset; + uint16_t num_queues, queue_size; + void *rc; + + if (vmsg->fd_num != 1 || + vmsg->size != sizeof(vmsg->payload.inflight)) { + vu_panic(dev, "Invalid set_inflight_fd message size:%d fds:%d", + vmsg->size, vmsg->fd_num); + return false; + } + + fd = vmsg->fds[0]; + mmap_size = vmsg->payload.inflight.mmap_size; + mmap_offset = vmsg->payload.inflight.mmap_offset; + num_queues = vmsg->payload.inflight.num_queues; + queue_size = vmsg->payload.inflight.queue_size; + + DPRINT("set_inflight_fd mmap_size: %"PRId64"\n", mmap_size); + DPRINT("set_inflight_fd mmap_offset: %"PRId64"\n", mmap_offset); + DPRINT("set_inflight_fd num_queues: %"PRId16"\n", num_queues); + DPRINT("set_inflight_fd queue_size: %"PRId16"\n", queue_size); + + rc = mmap(0, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, + fd, mmap_offset); + + if (rc == MAP_FAILED) { + vu_panic(dev, "set_inflight_fd mmap error: %s", strerror(errno)); + return false; + } + + if (dev->inflight_info.fd) { + close(dev->inflight_info.fd); + } + + if (dev->inflight_info.addr) { + munmap(dev->inflight_info.addr, dev->inflight_info.size); + } + + dev->inflight_info.fd = fd; + dev->inflight_info.addr = rc; + dev->inflight_info.size = mmap_size; + + for (i = 0; i < num_queues; i++) { + dev->vq[i].inflight = (VuVirtqInflight *)rc; + dev->vq[i].inflight->desc_num = queue_size; + rc = (void *)((char *)rc + vu_inflight_queue_size(queue_size)); + } + + return false; +} + static bool vu_process_message(VuDev *dev, VhostUserMsg *vmsg) { @@ -1285,13 +1513,18 @@ vu_process_message(VuDev *dev, VhostUserMsg *vmsg) case VHOST_USER_SET_CONFIG: return vu_set_config(dev, vmsg); case VHOST_USER_NONE: - break; + /* if you need processing before exit, override iface->process_msg */ + exit(0); case VHOST_USER_POSTCOPY_ADVISE: return vu_set_postcopy_advise(dev, vmsg); case VHOST_USER_POSTCOPY_LISTEN: return vu_set_postcopy_listen(dev, vmsg); case VHOST_USER_POSTCOPY_END: return vu_set_postcopy_end(dev, vmsg); + case VHOST_USER_GET_INFLIGHT_FD: + return vu_get_inflight_fd(dev, vmsg); + case VHOST_USER_SET_INFLIGHT_FD: + return vu_set_inflight_fd(dev, vmsg); default: vmsg_close_fds(vmsg); vu_panic(dev, "Unhandled request: %d", vmsg->request); @@ -1359,8 +1592,24 @@ vu_deinit(VuDev *dev) close(vq->err_fd); vq->err_fd = -1; } + + if (vq->resubmit_list) { + free(vq->resubmit_list); + vq->resubmit_list = NULL; + } + + vq->inflight = NULL; } + if (dev->inflight_info.addr) { + munmap(dev->inflight_info.addr, dev->inflight_info.size); + dev->inflight_info.addr = NULL; + } + + if (dev->inflight_info.fd > 0) { + close(dev->inflight_info.fd); + dev->inflight_info.fd = -1; + } vu_close_log(dev); if (dev->slave_fd != -1) { @@ -1687,20 +1936,6 @@ vu_queue_empty(VuDev *dev, VuVirtq *vq) return vring_avail_idx(vq) == vq->last_avail_idx; } -static inline -bool has_feature(uint64_t features, unsigned int fbit) -{ - assert(fbit < 64); - return !!(features & (1ULL << fbit)); -} - -static inline -bool vu_has_feature(VuDev *dev, - unsigned int fbit) -{ - return has_feature(dev->features, fbit); -} - static bool vring_notify(VuDev *dev, VuVirtq *vq) { @@ -1829,12 +2064,6 @@ virtqueue_map_desc(VuDev *dev, *p_num_sg = num_sg; } -/* Round number down to multiple */ -#define ALIGN_DOWN(n, m) ((n) / (m) * (m)) - -/* Round number up to multiple */ -#define ALIGN_UP(n, m) ALIGN_DOWN((n) + (m) - 1, (m)) - static void * virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -1853,49 +2082,20 @@ virtqueue_alloc_element(size_t sz, return elem; } -void * -vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) +static void * +vu_queue_map_desc(VuDev *dev, VuVirtq *vq, unsigned int idx, size_t sz) { - unsigned int i, head, max, desc_len; + struct vring_desc *desc = vq->vring.desc; uint64_t desc_addr, read_len; + unsigned int desc_len; + unsigned int max = vq->vring.num; + unsigned int i = idx; VuVirtqElement *elem; - unsigned out_num, in_num; + unsigned int out_num = 0, in_num = 0; struct iovec iov[VIRTQUEUE_MAX_SIZE]; struct vring_desc desc_buf[VIRTQUEUE_MAX_SIZE]; - struct vring_desc *desc; int rc; - if (unlikely(dev->broken) || - unlikely(!vq->vring.avail)) { - return NULL; - } - - if (vu_queue_empty(dev, 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. */ - out_num = in_num = 0; - - max = vq->vring.num; - if (vq->inuse >= vq->vring.num) { - vu_panic(dev, "Virtqueue size exceeded"); - return NULL; - } - - if (!virtqueue_get_head(dev, vq, vq->last_avail_idx++, &head)) { - return NULL; - } - - if (vu_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { - vring_set_avail_event(vq, vq->last_avail_idx); - } - - i = head; - desc = vq->vring.desc; if (desc[i].flags & VRING_DESC_F_INDIRECT) { if (desc[i].len % sizeof(struct vring_desc)) { vu_panic(dev, "Invalid size for indirect buffer table"); @@ -1947,12 +2147,13 @@ vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) } while (rc == VIRTQUEUE_READ_DESC_MORE); if (rc == VIRTQUEUE_READ_DESC_ERROR) { + vu_panic(dev, "read descriptor error"); return NULL; } /* Now copy what we have collected and mapped */ elem = virtqueue_alloc_element(sz, out_num, in_num); - elem->index = head; + elem->index = idx; for (i = 0; i < out_num; i++) { elem->out_sg[i] = iov[i]; } @@ -1960,11 +2161,142 @@ vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) elem->in_sg[i] = iov[out_num + i]; } + return elem; +} + +static int +vu_queue_inflight_get(VuDev *dev, VuVirtq *vq, int desc_idx) +{ + if (!has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + if (unlikely(!vq->inflight)) { + return -1; + } + + vq->inflight->desc[desc_idx].counter = vq->counter++; + vq->inflight->desc[desc_idx].inflight = 1; + + return 0; +} + +static int +vu_queue_inflight_pre_put(VuDev *dev, VuVirtq *vq, int desc_idx) +{ + if (!has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + if (unlikely(!vq->inflight)) { + return -1; + } + + vq->inflight->last_batch_head = desc_idx; + + return 0; +} + +static int +vu_queue_inflight_post_put(VuDev *dev, VuVirtq *vq, int desc_idx) +{ + if (!has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + if (unlikely(!vq->inflight)) { + return -1; + } + + barrier(); + + vq->inflight->desc[desc_idx].inflight = 0; + + barrier(); + + vq->inflight->used_idx = vq->used_idx; + + return 0; +} + +void * +vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz) +{ + int i; + unsigned int head; + VuVirtqElement *elem; + + if (unlikely(dev->broken) || + unlikely(!vq->vring.avail)) { + return NULL; + } + + if (unlikely(vq->resubmit_list && vq->resubmit_num > 0)) { + i = (--vq->resubmit_num); + elem = vu_queue_map_desc(dev, vq, vq->resubmit_list[i].index, sz); + + if (!vq->resubmit_num) { + free(vq->resubmit_list); + vq->resubmit_list = NULL; + } + + return elem; + } + + if (vu_queue_empty(dev, vq)) { + return NULL; + } + /* + * Needed after virtio_queue_empty(), see comment in + * virtqueue_num_heads(). + */ + smp_rmb(); + + if (vq->inuse >= vq->vring.num) { + vu_panic(dev, "Virtqueue size exceeded"); + return NULL; + } + + if (!virtqueue_get_head(dev, vq, vq->last_avail_idx++, &head)) { + return NULL; + } + + if (vu_has_feature(dev, VIRTIO_RING_F_EVENT_IDX)) { + vring_set_avail_event(vq, vq->last_avail_idx); + } + + elem = vu_queue_map_desc(dev, vq, head, sz); + + if (!elem) { + return NULL; + } + vq->inuse++; + vu_queue_inflight_get(dev, vq, head); + return elem; } +static void +vu_queue_detach_element(VuDev *dev, VuVirtq *vq, VuVirtqElement *elem, + size_t len) +{ + vq->inuse--; + /* unmap, when DMA support is added */ +} + +void +vu_queue_unpop(VuDev *dev, VuVirtq *vq, VuVirtqElement *elem, + size_t len) +{ + vq->last_avail_idx--; + vu_queue_detach_element(dev, vq, elem, len); +} + bool vu_queue_rewind(VuDev *dev, VuVirtq *vq, unsigned int num) { @@ -2106,5 +2438,7 @@ vu_queue_push(VuDev *dev, VuVirtq *vq, const VuVirtqElement *elem, unsigned int len) { vu_queue_fill(dev, vq, elem, len, 0); + vu_queue_inflight_pre_put(dev, vq, elem->index); vu_queue_flush(dev, vq, 1); + vu_queue_inflight_post_put(dev, vq, elem->index); } diff --git a/contrib/libvhost-user/libvhost-user.h b/contrib/libvhost-user/libvhost-user.h index 4aa55b4d2d..414ceb0a2f 100644 --- a/contrib/libvhost-user/libvhost-user.h +++ b/contrib/libvhost-user/libvhost-user.h @@ -53,6 +53,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_CONFIG = 9, VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12, VHOST_USER_PROTOCOL_F_MAX }; @@ -91,6 +92,8 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_ADVISE = 28, VHOST_USER_POSTCOPY_LISTEN = 29, VHOST_USER_POSTCOPY_END = 30, + VHOST_USER_GET_INFLIGHT_FD = 31, + VHOST_USER_SET_INFLIGHT_FD = 32, VHOST_USER_MAX } VhostUserRequest; @@ -138,6 +141,13 @@ typedef struct VhostUserVringArea { uint64_t offset; } VhostUserVringArea; +typedef struct VhostUserInflight { + uint64_t mmap_size; + uint64_t mmap_offset; + uint16_t num_queues; + uint16_t queue_size; +} VhostUserInflight; + #if defined(_WIN32) # define VU_PACKED __attribute__((gcc_struct, packed)) #else @@ -145,7 +155,7 @@ typedef struct VhostUserVringArea { #endif typedef struct VhostUserMsg { - VhostUserRequest request; + int request; #define VHOST_USER_VERSION_MASK (0x3) #define VHOST_USER_REPLY_MASK (0x1 << 2) @@ -163,6 +173,7 @@ typedef struct VhostUserMsg { VhostUserLog log; VhostUserConfig config; VhostUserVringArea area; + VhostUserInflight inflight; } payload; int fds[VHOST_MEMORY_MAX_NREGIONS]; @@ -234,9 +245,61 @@ typedef struct VuRing { uint32_t flags; } VuRing; +typedef struct VuDescStateSplit { + /* Indicate whether this descriptor is inflight or not. + * Only available for head-descriptor. */ + uint8_t inflight; + + /* Padding */ + uint8_t padding[5]; + + /* Maintain a list for the last batch of used descriptors. + * Only available when batching is used for submitting */ + uint16_t next; + + /* Used to preserve the order of fetching available descriptors. + * Only available for head-descriptor. */ + uint64_t counter; +} VuDescStateSplit; + +typedef struct VuVirtqInflight { + /* The feature flags of this region. Now it's initialized to 0. */ + uint64_t features; + + /* The version of this region. It's 1 currently. + * Zero value indicates a vm reset happened. */ + uint16_t version; + + /* The size of VuDescStateSplit array. It's equal to the virtqueue + * size. Slave could get it from queue size field of VhostUserInflight. */ + uint16_t desc_num; + + /* The head of list that track the last batch of used descriptors. */ + uint16_t last_batch_head; + + /* Storing the idx value of used ring */ + uint16_t used_idx; + + /* Used to track the state of each descriptor in descriptor table */ + VuDescStateSplit desc[0]; +} VuVirtqInflight; + +typedef struct VuVirtqInflightDesc { + uint16_t index; + uint64_t counter; +} VuVirtqInflightDesc; + typedef struct VuVirtq { VuRing vring; + VuVirtqInflight *inflight; + + VuVirtqInflightDesc *resubmit_list; + + uint16_t resubmit_num; + + uint64_t counter; + /* Next head to pop */ uint16_t last_avail_idx; @@ -279,11 +342,18 @@ typedef void (*vu_set_watch_cb) (VuDev *dev, int fd, int condition, vu_watch_cb cb, void *data); typedef void (*vu_remove_watch_cb) (VuDev *dev, int fd); +typedef struct VuDevInflightInfo { + int fd; + void *addr; + uint64_t size; +} VuDevInflightInfo; + struct VuDev { int sock; uint32_t nregions; VuDevRegion regions[VHOST_MEMORY_MAX_NREGIONS]; VuVirtq vq[VHOST_MAX_NR_VIRTQUEUE]; + VuDevInflightInfo inflight_info; int log_call_fd; int slave_fd; uint64_t log_size; @@ -458,6 +528,20 @@ void vu_queue_notify(VuDev *dev, VuVirtq *vq); */ void *vu_queue_pop(VuDev *dev, VuVirtq *vq, size_t sz); + +/** + * vu_queue_unpop: + * @dev: a VuDev context + * @vq: a VuVirtq queue + * @elem: The #VuVirtqElement + * @len: number of bytes written + * + * Pretend the most recent element wasn't popped from the virtqueue. The next + * call to vu_queue_pop() will refetch the element. + */ +void vu_queue_unpop(VuDev *dev, VuVirtq *vq, VuVirtqElement *elem, + size_t len); + /** * vu_queue_rewind: * @dev: a VuDev context diff --git a/contrib/rdmacm-mux/Makefile.objs b/contrib/rdmacm-mux/Makefile.objs new file mode 100644 index 0000000000..3df744af89 --- /dev/null +++ b/contrib/rdmacm-mux/Makefile.objs @@ -0,0 +1,3 @@ +ifdef CONFIG_PVRDMA +rdmacm-mux-obj-y = main.o +endif diff --git a/contrib/rdmacm-mux/main.c b/contrib/rdmacm-mux/main.c new file mode 100644 index 0000000000..21cc804367 --- /dev/null +++ b/contrib/rdmacm-mux/main.c @@ -0,0 +1,831 @@ +/* + * QEMU paravirtual RDMA - rdmacm-mux implementation + * + * Copyright (C) 2018 Oracle + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Yuval Shaia + * Marcel Apfelbaum + * + * 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 "sys/poll.h" +#include "sys/ioctl.h" +#include "pthread.h" +#include "syslog.h" + +#include "infiniband/verbs.h" +#include "infiniband/umad.h" +#include "infiniband/umad_types.h" +#include "infiniband/umad_sa.h" +#include "infiniband/umad_cm.h" + +#include "rdmacm-mux.h" + +#define SCALE_US 1000 +#define COMMID_TTL 2 /* How many SCALE_US a context of MAD session is saved */ +#define SLEEP_SECS 5 /* This is used both in poll() and thread */ +#define SERVER_LISTEN_BACKLOG 10 +#define MAX_CLIENTS 4096 +#define MAD_RMPP_VERSION 0 +#define MAD_METHOD_MASK0 0x8 + +#define IB_USER_MAD_LONGS_PER_METHOD_MASK (128 / (8 * sizeof(long))) + +#define CM_REQ_DGID_POS 80 +#define CM_SIDR_REQ_DGID_POS 44 + +/* The below can be override by command line parameter */ +#define UNIX_SOCKET_PATH "/var/run/rdmacm-mux" +/* Has format %s-%s-%d" -- */ +#define SOCKET_PATH_MAX (PATH_MAX - NAME_MAX - sizeof(int) - 2) +#define RDMA_PORT_NUM 1 + +typedef struct RdmaCmServerArgs { + char unix_socket_path[PATH_MAX]; + char rdma_dev_name[NAME_MAX]; + int rdma_port_num; +} RdmaCMServerArgs; + +typedef struct CommId2FdEntry { + int fd; + int ttl; /* Initialized to 2, decrement each timeout, entry delete when 0 */ + __be64 gid_ifid; +} CommId2FdEntry; + +typedef struct RdmaCmUMadAgent { + int port_id; + int agent_id; + GHashTable *gid2fd; /* Used to find fd of a given gid */ + GHashTable *commid2fd; /* Used to find fd on of a given comm_id */ +} RdmaCmUMadAgent; + +typedef struct RdmaCmServer { + bool run; + RdmaCMServerArgs args; + struct pollfd fds[MAX_CLIENTS]; + int nfds; + RdmaCmUMadAgent umad_agent; + pthread_t umad_recv_thread; + pthread_rwlock_t lock; +} RdmaCMServer; + +static RdmaCMServer server = {0}; + +static void usage(const char *progname) +{ + printf("Usage: %s [OPTION]...\n" + "Start a RDMA-CM multiplexer\n" + "\n" + "\t-h Show this help\n" + "\t-d rdma-device-name Name of RDMA device to register with\n" + "\t-s unix-socket-path Path to unix socket to listen on (default %s)\n" + "\t-p rdma-device-port Port number of RDMA device to register with (default %d)\n", + progname, UNIX_SOCKET_PATH, RDMA_PORT_NUM); +} + +static void help(const char *progname) +{ + fprintf(stderr, "Try '%s -h' for more information.\n", progname); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + char unix_socket_path[SOCKET_PATH_MAX]; + + strcpy(server.args.rdma_dev_name, ""); + strcpy(unix_socket_path, UNIX_SOCKET_PATH); + server.args.rdma_port_num = RDMA_PORT_NUM; + + while ((c = getopt(argc, argv, "hs:d:p:")) != -1) { + switch (c) { + case 'h': + usage(argv[0]); + exit(0); + + case 'd': + strncpy(server.args.rdma_dev_name, optarg, NAME_MAX - 1); + break; + + case 's': + /* This is temporary, final name will build below */ + strncpy(unix_socket_path, optarg, SOCKET_PATH_MAX); + break; + + case 'p': + server.args.rdma_port_num = atoi(optarg); + break; + + default: + help(argv[0]); + exit(1); + } + } + + if (!strcmp(server.args.rdma_dev_name, "")) { + fprintf(stderr, "Missing RDMA device name\n"); + help(argv[0]); + exit(1); + } + + /* Build unique unix-socket file name */ + snprintf(server.args.unix_socket_path, PATH_MAX, "%s-%s-%d", + unix_socket_path, server.args.rdma_dev_name, + server.args.rdma_port_num); + + syslog(LOG_INFO, "unix_socket_path=%s", server.args.unix_socket_path); + syslog(LOG_INFO, "rdma-device-name=%s", server.args.rdma_dev_name); + syslog(LOG_INFO, "rdma-device-port=%d", server.args.rdma_port_num); +} + +static void hash_tbl_alloc(void) +{ + + server.umad_agent.gid2fd = g_hash_table_new_full(g_int64_hash, + g_int64_equal, + g_free, g_free); + server.umad_agent.commid2fd = g_hash_table_new_full(g_int_hash, + g_int_equal, + g_free, g_free); +} + +static void hash_tbl_free(void) +{ + if (server.umad_agent.commid2fd) { + g_hash_table_destroy(server.umad_agent.commid2fd); + } + if (server.umad_agent.gid2fd) { + g_hash_table_destroy(server.umad_agent.gid2fd); + } +} + + +static int _hash_tbl_search_fd_by_ifid(__be64 *gid_ifid) +{ + int *fd; + + fd = g_hash_table_lookup(server.umad_agent.gid2fd, gid_ifid); + if (!fd) { + /* Let's try IPv4 */ + *gid_ifid |= 0x00000000ffff0000; + fd = g_hash_table_lookup(server.umad_agent.gid2fd, gid_ifid); + } + + return fd ? *fd : 0; +} + +static int hash_tbl_search_fd_by_ifid(int *fd, __be64 *gid_ifid) +{ + pthread_rwlock_rdlock(&server.lock); + *fd = _hash_tbl_search_fd_by_ifid(gid_ifid); + pthread_rwlock_unlock(&server.lock); + + if (!fd) { + syslog(LOG_WARNING, "Can't find matching for ifid 0x%llx\n", *gid_ifid); + return -ENOENT; + } + + return 0; +} + +static int hash_tbl_search_fd_by_comm_id(uint32_t comm_id, int *fd, + __be64 *gid_idid) +{ + CommId2FdEntry *fde; + + pthread_rwlock_rdlock(&server.lock); + fde = g_hash_table_lookup(server.umad_agent.commid2fd, &comm_id); + pthread_rwlock_unlock(&server.lock); + + if (!fde) { + syslog(LOG_WARNING, "Can't find matching for comm_id 0x%x\n", comm_id); + return -ENOENT; + } + + *fd = fde->fd; + *gid_idid = fde->gid_ifid; + + return 0; +} + +static RdmaCmMuxErrCode add_fd_ifid_pair(int fd, __be64 gid_ifid) +{ + int fd1; + + pthread_rwlock_wrlock(&server.lock); + + fd1 = _hash_tbl_search_fd_by_ifid(&gid_ifid); + if (fd1) { /* record already exist - an error */ + pthread_rwlock_unlock(&server.lock); + return fd == fd1 ? RDMACM_MUX_ERR_CODE_EEXIST : + RDMACM_MUX_ERR_CODE_EACCES; + } + + g_hash_table_insert(server.umad_agent.gid2fd, g_memdup(&gid_ifid, + sizeof(gid_ifid)), g_memdup(&fd, sizeof(fd))); + + pthread_rwlock_unlock(&server.lock); + + syslog(LOG_INFO, "0x%lx registered on socket %d", + be64toh((uint64_t)gid_ifid), fd); + + return RDMACM_MUX_ERR_CODE_OK; +} + +static RdmaCmMuxErrCode delete_fd_ifid_pair(int fd, __be64 gid_ifid) +{ + int fd1; + + pthread_rwlock_wrlock(&server.lock); + + fd1 = _hash_tbl_search_fd_by_ifid(&gid_ifid); + if (!fd1) { /* record not exist - an error */ + pthread_rwlock_unlock(&server.lock); + return RDMACM_MUX_ERR_CODE_ENOTFOUND; + } + + g_hash_table_remove(server.umad_agent.gid2fd, g_memdup(&gid_ifid, + sizeof(gid_ifid))); + pthread_rwlock_unlock(&server.lock); + + syslog(LOG_INFO, "0x%lx unregistered on socket %d", + be64toh((uint64_t)gid_ifid), fd); + + return RDMACM_MUX_ERR_CODE_OK; +} + +static void hash_tbl_save_fd_comm_id_pair(int fd, uint32_t comm_id, + uint64_t gid_ifid) +{ + CommId2FdEntry fde = {fd, COMMID_TTL, gid_ifid}; + + pthread_rwlock_wrlock(&server.lock); + g_hash_table_insert(server.umad_agent.commid2fd, + g_memdup(&comm_id, sizeof(comm_id)), + g_memdup(&fde, sizeof(fde))); + pthread_rwlock_unlock(&server.lock); +} + +static gboolean remove_old_comm_ids(gpointer key, gpointer value, + gpointer user_data) +{ + CommId2FdEntry *fde = (CommId2FdEntry *)value; + + return !fde->ttl--; +} + +static gboolean remove_entry_from_gid2fd(gpointer key, gpointer value, + gpointer user_data) +{ + if (*(int *)value == *(int *)user_data) { + syslog(LOG_INFO, "0x%lx unregistered on socket %d", + be64toh(*(uint64_t *)key), *(int *)value); + return true; + } + + return false; +} + +static void hash_tbl_remove_fd_ifid_pair(int fd) +{ + pthread_rwlock_wrlock(&server.lock); + g_hash_table_foreach_remove(server.umad_agent.gid2fd, + remove_entry_from_gid2fd, (gpointer)&fd); + pthread_rwlock_unlock(&server.lock); +} + +static int get_fd(const char *mad, int umad_len, int *fd, __be64 *gid_ifid) +{ + struct umad_hdr *hdr = (struct umad_hdr *)mad; + char *data = (char *)hdr + sizeof(*hdr); + int32_t comm_id = 0; + uint16_t attr_id = be16toh(hdr->attr_id); + int rc = 0; + + if (umad_len <= sizeof(*hdr)) { + rc = -EINVAL; + syslog(LOG_DEBUG, "Ignoring MAD packets with header only\n"); + goto out; + } + + switch (attr_id) { + case UMAD_CM_ATTR_REQ: + if (unlikely(umad_len < sizeof(*hdr) + CM_REQ_DGID_POS + + sizeof(*gid_ifid))) { + rc = -EINVAL; + syslog(LOG_WARNING, + "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, + attr_id); + goto out; + } + memcpy(gid_ifid, data + CM_REQ_DGID_POS, sizeof(*gid_ifid)); + rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid); + break; + + case UMAD_CM_ATTR_SIDR_REQ: + if (unlikely(umad_len < sizeof(*hdr) + CM_SIDR_REQ_DGID_POS + + sizeof(*gid_ifid))) { + rc = -EINVAL; + syslog(LOG_WARNING, + "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, + attr_id); + goto out; + } + memcpy(gid_ifid, data + CM_SIDR_REQ_DGID_POS, sizeof(*gid_ifid)); + rc = hash_tbl_search_fd_by_ifid(fd, gid_ifid); + break; + + case UMAD_CM_ATTR_REP: + /* Fall through */ + case UMAD_CM_ATTR_REJ: + /* Fall through */ + case UMAD_CM_ATTR_DREQ: + /* Fall through */ + case UMAD_CM_ATTR_DREP: + /* Fall through */ + case UMAD_CM_ATTR_RTU: + data += sizeof(comm_id); + /* Fall through */ + case UMAD_CM_ATTR_SIDR_REP: + if (unlikely(umad_len < sizeof(*hdr) + sizeof(comm_id))) { + rc = -EINVAL; + syslog(LOG_WARNING, + "Invalid MAD packet size (%d) for attr_id 0x%x\n", umad_len, + attr_id); + goto out; + } + memcpy(&comm_id, data, sizeof(comm_id)); + if (comm_id) { + rc = hash_tbl_search_fd_by_comm_id(comm_id, fd, gid_ifid); + } + break; + + default: + rc = -EINVAL; + syslog(LOG_WARNING, "Unsupported attr_id 0x%x\n", attr_id); + } + + syslog(LOG_DEBUG, "mad_to_vm: %d 0x%x 0x%x\n", *fd, attr_id, comm_id); + +out: + return rc; +} + +static void *umad_recv_thread_func(void *args) +{ + int rc; + RdmaCmMuxMsg msg = {}; + int fd = -2; + + msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ; + msg.hdr.op_code = RDMACM_MUX_OP_CODE_MAD; + + while (server.run) { + do { + msg.umad_len = sizeof(msg.umad.mad); + rc = umad_recv(server.umad_agent.port_id, &msg.umad, &msg.umad_len, + SLEEP_SECS * SCALE_US); + if ((rc == -EIO) || (rc == -EINVAL)) { + syslog(LOG_CRIT, "Fatal error while trying to read MAD"); + } + + if (rc == -ETIMEDOUT) { + g_hash_table_foreach_remove(server.umad_agent.commid2fd, + remove_old_comm_ids, NULL); + } + } while (rc && server.run); + + if (server.run) { + rc = get_fd(msg.umad.mad, msg.umad_len, &fd, + &msg.hdr.sgid.global.interface_id); + if (rc) { + continue; + } + + send(fd, &msg, sizeof(msg), 0); + } + } + + return NULL; +} + +static int read_and_process(int fd) +{ + int rc; + RdmaCmMuxMsg msg = {}; + struct umad_hdr *hdr; + uint32_t *comm_id = 0; + uint16_t attr_id; + + rc = recv(fd, &msg, sizeof(msg), 0); + syslog(LOG_DEBUG, "Socket %d, recv %d\n", fd, rc); + + if (rc < 0 && errno != EWOULDBLOCK) { + syslog(LOG_ERR, "Fail to read from socket %d\n", fd); + return -EIO; + } + + if (!rc) { + syslog(LOG_ERR, "Fail to read from socket %d\n", fd); + return -EPIPE; + } + + if (msg.hdr.msg_type != RDMACM_MUX_MSG_TYPE_REQ) { + syslog(LOG_WARNING, "Got non-request message (%d) from socket %d\n", + msg.hdr.msg_type, fd); + return -EPERM; + } + + switch (msg.hdr.op_code) { + case RDMACM_MUX_OP_CODE_REG: + rc = add_fd_ifid_pair(fd, msg.hdr.sgid.global.interface_id); + break; + + case RDMACM_MUX_OP_CODE_UNREG: + rc = delete_fd_ifid_pair(fd, msg.hdr.sgid.global.interface_id); + break; + + case RDMACM_MUX_OP_CODE_MAD: + /* If this is REQ or REP then store the pair comm_id,fd to be later + * used for other messages where gid is unknown */ + hdr = (struct umad_hdr *)msg.umad.mad; + attr_id = be16toh(hdr->attr_id); + if ((attr_id == UMAD_CM_ATTR_REQ) || (attr_id == UMAD_CM_ATTR_DREQ) || + (attr_id == UMAD_CM_ATTR_SIDR_REQ) || + (attr_id == UMAD_CM_ATTR_REP) || (attr_id == UMAD_CM_ATTR_DREP)) { + comm_id = (uint32_t *)(msg.umad.mad + sizeof(*hdr)); + hash_tbl_save_fd_comm_id_pair(fd, *comm_id, + msg.hdr.sgid.global.interface_id); + } + + syslog(LOG_DEBUG, "vm_to_mad: %d 0x%x 0x%x\n", fd, attr_id, + comm_id ? *comm_id : 0); + rc = umad_send(server.umad_agent.port_id, server.umad_agent.agent_id, + &msg.umad, msg.umad_len, 1, 0); + if (rc) { + syslog(LOG_ERR, + "Fail to send MAD message (0x%x) from socket %d, err=%d", + attr_id, fd, rc); + } + break; + + default: + syslog(LOG_ERR, "Got invalid op_code (%d) from socket %d", + msg.hdr.msg_type, fd); + rc = RDMACM_MUX_ERR_CODE_EINVAL; + } + + msg.hdr.msg_type = RDMACM_MUX_MSG_TYPE_RESP; + msg.hdr.err_code = rc; + rc = send(fd, &msg, sizeof(msg), 0); + + return rc == sizeof(msg) ? 0 : -EPIPE; +} + +static int accept_all(void) +{ + int fd, rc = 0;; + + pthread_rwlock_wrlock(&server.lock); + + do { + if ((server.nfds + 1) > MAX_CLIENTS) { + syslog(LOG_WARNING, "Too many clients (%d)", server.nfds); + rc = -EIO; + goto out; + } + + fd = accept(server.fds[0].fd, NULL, NULL); + if (fd < 0) { + if (errno != EWOULDBLOCK) { + syslog(LOG_WARNING, "accept() failed"); + rc = -EIO; + goto out; + } + break; + } + + syslog(LOG_INFO, "Client connected on socket %d\n", fd); + server.fds[server.nfds].fd = fd; + server.fds[server.nfds].events = POLLIN; + server.nfds++; + } while (fd != -1); + +out: + pthread_rwlock_unlock(&server.lock); + return rc; +} + +static void compress_fds(void) +{ + int i, j; + int closed = 0; + + pthread_rwlock_wrlock(&server.lock); + + for (i = 1; i < server.nfds; i++) { + if (!server.fds[i].fd) { + closed++; + for (j = i; j < server.nfds - 1; j++) { + server.fds[j] = server.fds[j + 1]; + } + } + } + + server.nfds -= closed; + + pthread_rwlock_unlock(&server.lock); +} + +static void close_fd(int idx) +{ + close(server.fds[idx].fd); + syslog(LOG_INFO, "Socket %d closed\n", server.fds[idx].fd); + hash_tbl_remove_fd_ifid_pair(server.fds[idx].fd); + server.fds[idx].fd = 0; +} + +static void run(void) +{ + int rc, nfds, i; + bool compress = false; + + syslog(LOG_INFO, "Service started"); + + while (server.run) { + rc = poll(server.fds, server.nfds, SLEEP_SECS * SCALE_US); + if (rc < 0) { + if (errno != EINTR) { + syslog(LOG_WARNING, "poll() failed"); + } + continue; + } + + if (rc == 0) { + continue; + } + + nfds = server.nfds; + for (i = 0; i < nfds; i++) { + syslog(LOG_DEBUG, "pollfd[%d]: revents 0x%x, events 0x%x\n", i, + server.fds[i].revents, server.fds[i].events); + if (server.fds[i].revents == 0) { + continue; + } + + if (server.fds[i].revents != POLLIN) { + if (i == 0) { + syslog(LOG_NOTICE, "Unexpected poll() event (0x%x)\n", + server.fds[i].revents); + } else { + close_fd(i); + compress = true; + } + continue; + } + + if (i == 0) { + rc = accept_all(); + if (rc) { + continue; + } + } else { + rc = read_and_process(server.fds[i].fd); + if (rc) { + close_fd(i); + compress = true; + } + } + } + + if (compress) { + compress = false; + compress_fds(); + } + } +} + +static void fini_listener(void) +{ + int i; + + if (server.fds[0].fd <= 0) { + return; + } + + for (i = server.nfds - 1; i >= 0; i--) { + if (server.fds[i].fd) { + close(server.fds[i].fd); + } + } + + unlink(server.args.unix_socket_path); +} + +static void fini_umad(void) +{ + if (server.umad_agent.agent_id) { + umad_unregister(server.umad_agent.port_id, server.umad_agent.agent_id); + } + + if (server.umad_agent.port_id) { + umad_close_port(server.umad_agent.port_id); + } + + hash_tbl_free(); +} + +static void fini(void) +{ + if (server.umad_recv_thread) { + pthread_join(server.umad_recv_thread, NULL); + server.umad_recv_thread = 0; + } + fini_umad(); + fini_listener(); + pthread_rwlock_destroy(&server.lock); + + syslog(LOG_INFO, "Service going down"); +} + +static int init_listener(void) +{ + struct sockaddr_un sun; + int rc, on = 1; + + server.fds[0].fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server.fds[0].fd < 0) { + syslog(LOG_ALERT, "socket() failed"); + return -EIO; + } + + rc = setsockopt(server.fds[0].fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, + sizeof(on)); + if (rc < 0) { + syslog(LOG_ALERT, "setsockopt() failed"); + rc = -EIO; + goto err; + } + + rc = ioctl(server.fds[0].fd, FIONBIO, (char *)&on); + if (rc < 0) { + syslog(LOG_ALERT, "ioctl() failed"); + rc = -EIO; + goto err; + } + + if (strlen(server.args.unix_socket_path) >= sizeof(sun.sun_path)) { + syslog(LOG_ALERT, + "Invalid unix_socket_path, size must be less than %ld\n", + sizeof(sun.sun_path)); + rc = -EINVAL; + goto err; + } + + sun.sun_family = AF_UNIX; + rc = snprintf(sun.sun_path, sizeof(sun.sun_path), "%s", + server.args.unix_socket_path); + if (rc < 0 || rc >= sizeof(sun.sun_path)) { + syslog(LOG_ALERT, "Could not copy unix socket path\n"); + rc = -EINVAL; + goto err; + } + + rc = bind(server.fds[0].fd, (struct sockaddr *)&sun, sizeof(sun)); + if (rc < 0) { + syslog(LOG_ALERT, "bind() failed"); + rc = -EIO; + goto err; + } + + rc = listen(server.fds[0].fd, SERVER_LISTEN_BACKLOG); + if (rc < 0) { + syslog(LOG_ALERT, "listen() failed"); + rc = -EIO; + goto err; + } + + server.fds[0].events = POLLIN; + server.nfds = 1; + server.run = true; + + return 0; + +err: + close(server.fds[0].fd); + return rc; +} + +static int init_umad(void) +{ + long method_mask[IB_USER_MAD_LONGS_PER_METHOD_MASK]; + + server.umad_agent.port_id = umad_open_port(server.args.rdma_dev_name, + server.args.rdma_port_num); + + if (server.umad_agent.port_id < 0) { + syslog(LOG_WARNING, "umad_open_port() failed"); + return -EIO; + } + + memset(&method_mask, 0, sizeof(method_mask)); + method_mask[0] = MAD_METHOD_MASK0; + server.umad_agent.agent_id = umad_register(server.umad_agent.port_id, + UMAD_CLASS_CM, + UMAD_SA_CLASS_VERSION, + MAD_RMPP_VERSION, method_mask); + if (server.umad_agent.agent_id < 0) { + syslog(LOG_WARNING, "umad_register() failed"); + return -EIO; + } + + hash_tbl_alloc(); + + return 0; +} + +static void signal_handler(int sig, siginfo_t *siginfo, void *context) +{ + static bool warned; + + /* Prevent stop if clients are connected */ + if (server.nfds != 1) { + if (!warned) { + syslog(LOG_WARNING, + "Can't stop while active client exist, resend SIGINT to overid"); + warned = true; + return; + } + } + + if (sig == SIGINT) { + server.run = false; + fini(); + } + + exit(0); +} + +static int init(void) +{ + int rc; + struct sigaction sig = {}; + + rc = init_listener(); + if (rc) { + return rc; + } + + rc = init_umad(); + if (rc) { + return rc; + } + + pthread_rwlock_init(&server.lock, 0); + + rc = pthread_create(&server.umad_recv_thread, NULL, umad_recv_thread_func, + NULL); + if (rc) { + syslog(LOG_ERR, "Fail to create UMAD receiver thread (%d)\n", rc); + return rc; + } + + sig.sa_sigaction = &signal_handler; + sig.sa_flags = SA_SIGINFO; + rc = sigaction(SIGINT, &sig, NULL); + if (rc < 0) { + syslog(LOG_ERR, "Fail to install SIGINT handler (%d)\n", errno); + return rc; + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + int rc; + + memset(&server, 0, sizeof(server)); + + parse_args(argc, argv); + + rc = init(); + if (rc) { + syslog(LOG_ERR, "Fail to initialize server (%d)\n", rc); + rc = -EAGAIN; + goto out; + } + + run(); + +out: + fini(); + + return rc; +} diff --git a/contrib/rdmacm-mux/rdmacm-mux.h b/contrib/rdmacm-mux/rdmacm-mux.h new file mode 100644 index 0000000000..942a802c47 --- /dev/null +++ b/contrib/rdmacm-mux/rdmacm-mux.h @@ -0,0 +1,61 @@ +/* + * QEMU paravirtual RDMA - rdmacm-mux declarations + * + * Copyright (C) 2018 Oracle + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Yuval Shaia + * Marcel Apfelbaum + * + * 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 RDMACM_MUX_H +#define RDMACM_MUX_H + +#include "linux/if.h" +#include "infiniband/verbs.h" +#include "infiniband/umad.h" +#include "rdma/rdma_user_cm.h" + +typedef enum RdmaCmMuxMsgType { + RDMACM_MUX_MSG_TYPE_REQ = 0, + RDMACM_MUX_MSG_TYPE_RESP = 1, +} RdmaCmMuxMsgType; + +typedef enum RdmaCmMuxOpCode { + RDMACM_MUX_OP_CODE_REG = 0, + RDMACM_MUX_OP_CODE_UNREG = 1, + RDMACM_MUX_OP_CODE_MAD = 2, +} RdmaCmMuxOpCode; + +typedef enum RdmaCmMuxErrCode { + RDMACM_MUX_ERR_CODE_OK = 0, + RDMACM_MUX_ERR_CODE_EINVAL = 1, + RDMACM_MUX_ERR_CODE_EEXIST = 2, + RDMACM_MUX_ERR_CODE_EACCES = 3, + RDMACM_MUX_ERR_CODE_ENOTFOUND = 4, +} RdmaCmMuxErrCode; + +typedef struct RdmaCmMuxHdr { + RdmaCmMuxMsgType msg_type; + RdmaCmMuxOpCode op_code; + union ibv_gid sgid; + RdmaCmMuxErrCode err_code; +} RdmaCmUHdr; + +typedef struct RdmaCmUMad { + struct ib_user_mad hdr; + char mad[RDMA_MAX_PRIVATE_DATA]; +} RdmaCmUMad; + +typedef struct RdmaCmMuxMsg { + RdmaCmUHdr hdr; + int umad_len; + RdmaCmUMad umad; +} RdmaCmMuxMsg; + +#endif diff --git a/contrib/vhost-user-blk/vhost-user-blk.c b/contrib/vhost-user-blk/vhost-user-blk.c index 571f114a56..43583f2659 100644 --- a/contrib/vhost-user-blk/vhost-user-blk.c +++ b/contrib/vhost-user-blk/vhost-user-blk.c @@ -20,7 +20,10 @@ #include "contrib/libvhost-user/libvhost-user-glib.h" #include "contrib/libvhost-user/libvhost-user.h" -#include +#if defined(__linux__) +#include +#include +#endif struct virtio_blk_inhdr { unsigned char status; @@ -60,6 +63,20 @@ static size_t vub_iov_size(const struct iovec *iov, return len; } +static size_t vub_iov_to_buf(const struct iovec *iov, + const unsigned int iov_cnt, void *buf) +{ + size_t len; + unsigned int i; + + len = 0; + for (i = 0; i < iov_cnt; i++) { + memcpy(buf + len, iov[i].iov_base, iov[i].iov_len); + len += iov[i].iov_len; + } + return len; +} + static void vub_panic_cb(VuDev *vu_dev, const char *buf) { VugDev *gdev; @@ -158,6 +175,44 @@ vub_writev(VubReq *req, struct iovec *iov, uint32_t iovcnt) return rc; } +static int +vub_discard_write_zeroes(VubReq *req, struct iovec *iov, uint32_t iovcnt, + uint32_t type) +{ + struct virtio_blk_discard_write_zeroes *desc; + ssize_t size; + void *buf; + + size = vub_iov_size(iov, iovcnt); + if (size != sizeof(*desc)) { + fprintf(stderr, "Invalid size %ld, expect %ld\n", size, sizeof(*desc)); + return -1; + } + buf = g_new0(char, size); + vub_iov_to_buf(iov, iovcnt, buf); + + #if defined(__linux__) && defined(BLKDISCARD) && defined(BLKZEROOUT) + VubDev *vdev_blk = req->vdev_blk; + desc = (struct virtio_blk_discard_write_zeroes *)buf; + uint64_t range[2] = { le64toh(desc->sector) << 9, + le32toh(desc->num_sectors) << 9 }; + if (type == VIRTIO_BLK_T_DISCARD) { + if (ioctl(vdev_blk->blk_fd, BLKDISCARD, range) == 0) { + g_free(buf); + return 0; + } + } else if (type == VIRTIO_BLK_T_WRITE_ZEROES) { + if (ioctl(vdev_blk->blk_fd, BLKZEROOUT, range) == 0) { + g_free(buf); + return 0; + } + } + #endif + + g_free(buf); + return -1; +} + static void vub_flush(VubReq *req) { @@ -213,44 +268,55 @@ static int vub_virtio_process_req(VubDev *vdev_blk, in_num--; type = le32toh(req->out->type); - switch (type & ~(VIRTIO_BLK_T_OUT | VIRTIO_BLK_T_BARRIER)) { - case VIRTIO_BLK_T_IN: { - ssize_t ret = 0; - bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = le64toh(req->out->sector); - if (is_write) { - ret = vub_writev(req, &elem->out_sg[1], out_num); - } else { - ret = vub_readv(req, &elem->in_sg[0], in_num); - } - if (ret >= 0) { - req->in->status = VIRTIO_BLK_S_OK; - } else { - req->in->status = VIRTIO_BLK_S_IOERR; - } - vub_req_complete(req); - break; + switch (type & ~VIRTIO_BLK_T_BARRIER) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: { + ssize_t ret = 0; + bool is_write = type & VIRTIO_BLK_T_OUT; + req->sector_num = le64toh(req->out->sector); + if (is_write) { + ret = vub_writev(req, &elem->out_sg[1], out_num); + } else { + ret = vub_readv(req, &elem->in_sg[0], in_num); } - case VIRTIO_BLK_T_FLUSH: { - vub_flush(req); + if (ret >= 0) { req->in->status = VIRTIO_BLK_S_OK; - vub_req_complete(req); - break; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; } - case VIRTIO_BLK_T_GET_ID: { - size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num), - VIRTIO_BLK_ID_BYTES); - snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk"); + vub_req_complete(req); + break; + } + case VIRTIO_BLK_T_FLUSH: + vub_flush(req); + req->in->status = VIRTIO_BLK_S_OK; + vub_req_complete(req); + break; + case VIRTIO_BLK_T_GET_ID: { + size_t size = MIN(vub_iov_size(&elem->in_sg[0], in_num), + VIRTIO_BLK_ID_BYTES); + snprintf(elem->in_sg[0].iov_base, size, "%s", "vhost_user_blk"); + req->in->status = VIRTIO_BLK_S_OK; + req->size = elem->in_sg[0].iov_len; + vub_req_complete(req); + break; + } + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: { + int rc; + rc = vub_discard_write_zeroes(req, &elem->out_sg[1], out_num, type); + if (rc == 0) { req->in->status = VIRTIO_BLK_S_OK; - req->size = elem->in_sg[0].iov_len; - vub_req_complete(req); - break; - } - default: { - req->in->status = VIRTIO_BLK_S_UNSUPP; - vub_req_complete(req); - break; + } else { + req->in->status = VIRTIO_BLK_S_IOERR; } + vub_req_complete(req); + break; + } + default: + req->in->status = VIRTIO_BLK_S_UNSUPP; + vub_req_complete(req); + break; } return 0; @@ -314,6 +380,10 @@ vub_get_features(VuDev *dev) 1ull << VIRTIO_BLK_F_TOPOLOGY | 1ull << VIRTIO_BLK_F_BLK_SIZE | 1ull << VIRTIO_BLK_F_FLUSH | + #if defined(__linux__) && defined(BLKDISCARD) && defined(BLKZEROOUT) + 1ull << VIRTIO_BLK_F_DISCARD | + 1ull << VIRTIO_BLK_F_WRITE_ZEROES | + #endif 1ull << VIRTIO_BLK_F_CONFIG_WCE | 1ull << VIRTIO_F_VERSION_1 | 1ull << VHOST_USER_F_PROTOCOL_FEATURES; @@ -455,7 +525,7 @@ vub_get_blocksize(int fd) #if defined(__linux__) && defined(BLKSSZGET) if (ioctl(fd, BLKSSZGET, &blocksize) == 0) { - return blocklen; + return blocksize; } #endif @@ -475,6 +545,13 @@ vub_initialize_config(int fd, struct virtio_blk_config *config) config->min_io_size = 1; config->opt_io_size = 1; config->num_queues = 1; + #if defined(__linux__) && defined(BLKDISCARD) && defined(BLKZEROOUT) + config->max_discard_sectors = 32768; + config->max_discard_seg = 1; + config->discard_sector_alignment = config->blk_size >> 9; + config->max_write_zeroes_sectors = 32768; + config->max_write_zeroes_seg = 1; + #endif } static VubDev * diff --git a/contrib/vhost-user-scsi/vhost-user-scsi.c b/contrib/vhost-user-scsi/vhost-user-scsi.c index 02c29019d1..496dd6e693 100644 --- a/contrib/vhost-user-scsi/vhost-user-scsi.c +++ b/contrib/vhost-user-scsi/vhost-user-scsi.c @@ -16,7 +16,6 @@ #include "contrib/libvhost-user/libvhost-user-glib.h" #include "standard-headers/linux/virtio_scsi.h" -#include #define VUS_ISCSI_INITIATOR "iqn.2016-11.com.nutanix:vhost-user-scsi" diff --git a/cpus-common.c b/cpus-common.c index 59f751ecf9..3ca58c64e8 100644 --- a/cpus-common.c +++ b/cpus-common.c @@ -84,7 +84,7 @@ void cpu_list_add(CPUState *cpu) } else { assert(!cpu_index_auto_assigned); } - QTAILQ_INSERT_TAIL(&cpus, cpu, node); + QTAILQ_INSERT_TAIL_RCU(&cpus, cpu, node); qemu_mutex_unlock(&qemu_cpu_list_lock); finish_safe_work(cpu); @@ -99,9 +99,9 @@ void cpu_list_remove(CPUState *cpu) return; } - assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus, CPUTailQ))); + assert(!(cpu_index_auto_assigned && cpu != QTAILQ_LAST(&cpus))); - QTAILQ_REMOVE(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus, cpu, node); cpu->cpu_index = UNASSIGNED_CPU_INDEX; qemu_mutex_unlock(&qemu_cpu_list_lock); } diff --git a/cpus.c b/cpus.c index f0e251b405..026df0dc5f 100644 --- a/cpus.c +++ b/cpus.c @@ -121,8 +121,6 @@ static bool all_cpu_threads_idle(void) /* Protected by TimersState seqlock */ static bool icount_sleep = true; -/* Conversion factor from emulated instructions to virtual clock ticks. */ -static int icount_time_shift; /* Arbitrarily pick 1MIPS as the minimum allowable speed. */ #define MAX_ICOUNT_SHIFT 10 @@ -131,20 +129,27 @@ typedef struct TimersState { int64_t cpu_ticks_prev; int64_t cpu_ticks_offset; - /* cpu_clock_offset can be read out of BQL, so protect it with - * this lock. + /* Protect fields that can be respectively read outside the + * BQL, and written from multiple threads. */ QemuSeqLock vm_clock_seqlock; - int64_t cpu_clock_offset; - int32_t cpu_ticks_enabled; - int64_t dummy; + QemuSpin vm_clock_lock; + + int16_t cpu_ticks_enabled; + + /* Conversion factor from emulated instructions to virtual clock ticks. */ + int16_t icount_time_shift; /* Compensate for varying guest execution speed. */ int64_t qemu_icount_bias; + + int64_t vm_clock_warp_start; + int64_t cpu_clock_offset; + /* Only written by TCG thread */ int64_t qemu_icount; + /* for adjusting icount */ - int64_t vm_clock_warp_start; QEMUTimer *icount_rt_timer; QEMUTimer *icount_vm_timer; QEMUTimer *icount_warp_timer; @@ -206,12 +211,12 @@ void qemu_tcg_configure(QemuOpts *opts, Error **errp) error_setg(errp, "No MTTCG when icount is enabled"); } else { #ifndef TARGET_SUPPORTS_MTTCG - error_report("Guest not yet converted to MTTCG - " - "you may get unexpected results"); + warn_report("Guest not yet converted to MTTCG - " + "you may get unexpected results"); #endif if (!check_tcg_memory_orders_compatible()) { - error_report("Guest expects a stronger memory ordering " - "than the host provides"); + warn_report("Guest expects a stronger memory ordering " + "than the host provides"); error_printf("This may cause strange/hard to debug errors\n"); } mttcg_enabled = true; @@ -240,21 +245,30 @@ static int64_t cpu_get_icount_executed(CPUState *cpu) * account executed instructions. This is done by the TCG vCPU * thread so the main-loop can see time has moved forward. */ -void cpu_update_icount(CPUState *cpu) +static void cpu_update_icount_locked(CPUState *cpu) { int64_t executed = cpu_get_icount_executed(cpu); cpu->icount_budget -= executed; -#ifdef CONFIG_ATOMIC64 - atomic_set__nocheck(&timers_state.qemu_icount, - atomic_read__nocheck(&timers_state.qemu_icount) + - executed); -#else /* FIXME: we need 64bit atomics to do this safely */ - timers_state.qemu_icount += executed; -#endif + atomic_set_i64(&timers_state.qemu_icount, + timers_state.qemu_icount + executed); } -int64_t cpu_get_icount_raw(void) +/* + * Update the global shared timer_state.qemu_icount to take into + * account executed instructions. This is done by the TCG vCPU + * thread so the main-loop can see time has moved forward. + */ +void cpu_update_icount(CPUState *cpu) +{ + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + cpu_update_icount_locked(cpu); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); +} + +static int64_t cpu_get_icount_raw_locked(void) { CPUState *cpu = current_cpu; @@ -264,22 +278,33 @@ int64_t cpu_get_icount_raw(void) exit(1); } /* Take into account what has run */ - cpu_update_icount(cpu); + cpu_update_icount_locked(cpu); } -#ifdef CONFIG_ATOMIC64 - return atomic_read__nocheck(&timers_state.qemu_icount); -#else /* FIXME: we need 64bit atomics to do this safely */ - return timers_state.qemu_icount; -#endif + /* The read is protected by the seqlock, but needs atomic64 to avoid UB */ + return atomic_read_i64(&timers_state.qemu_icount); +} + +static int64_t cpu_get_icount_locked(void) +{ + int64_t icount = cpu_get_icount_raw_locked(); + return atomic_read_i64(&timers_state.qemu_icount_bias) + + cpu_icount_to_ns(icount); +} + +int64_t cpu_get_icount_raw(void) +{ + int64_t icount; + unsigned start; + + do { + start = seqlock_read_begin(&timers_state.vm_clock_seqlock); + icount = cpu_get_icount_raw_locked(); + } while (seqlock_read_retry(&timers_state.vm_clock_seqlock, start)); + + return icount; } /* Return the virtual CPU time, based on the instruction counter. */ -static int64_t cpu_get_icount_locked(void) -{ - int64_t icount = cpu_get_icount_raw(); - return timers_state.qemu_icount_bias + cpu_icount_to_ns(icount); -} - int64_t cpu_get_icount(void) { int64_t icount; @@ -295,14 +320,29 @@ int64_t cpu_get_icount(void) int64_t cpu_icount_to_ns(int64_t icount) { - return icount << icount_time_shift; + return icount << atomic_read(&timers_state.icount_time_shift); +} + +static int64_t cpu_get_ticks_locked(void) +{ + int64_t ticks = timers_state.cpu_ticks_offset; + if (timers_state.cpu_ticks_enabled) { + ticks += cpu_get_host_ticks(); + } + + if (timers_state.cpu_ticks_prev > ticks) { + /* Non increasing ticks may happen if the host uses software suspend. */ + timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; + ticks = timers_state.cpu_ticks_prev; + } + + timers_state.cpu_ticks_prev = ticks; + return ticks; } /* return the time elapsed in VM between vm_start and vm_stop. Unless * icount is active, cpu_get_ticks() uses units of the host CPU cycle * counter. - * - * Caller must hold the BQL */ int64_t cpu_get_ticks(void) { @@ -312,19 +352,9 @@ int64_t cpu_get_ticks(void) return cpu_get_icount(); } - ticks = timers_state.cpu_ticks_offset; - if (timers_state.cpu_ticks_enabled) { - ticks += cpu_get_host_ticks(); - } - - if (timers_state.cpu_ticks_prev > ticks) { - /* Note: non increasing ticks may happen if the host uses - software suspend */ - timers_state.cpu_ticks_offset += timers_state.cpu_ticks_prev - ticks; - ticks = timers_state.cpu_ticks_prev; - } - - timers_state.cpu_ticks_prev = ticks; + qemu_spin_lock(&timers_state.vm_clock_lock); + ticks = cpu_get_ticks_locked(); + qemu_spin_unlock(&timers_state.vm_clock_lock); return ticks; } @@ -361,14 +391,15 @@ int64_t cpu_get_clock(void) */ void cpu_enable_ticks(void) { - /* Here, the really thing protected by seqlock is cpu_clock_offset. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (!timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset -= cpu_get_host_ticks(); timers_state.cpu_clock_offset -= get_clock(); timers_state.cpu_ticks_enabled = 1; } - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); } /* disable cpu_get_ticks() : the clock is stopped. You must not call @@ -377,14 +408,15 @@ void cpu_enable_ticks(void) */ void cpu_disable_ticks(void) { - /* Here, the really thing protected by seqlock is cpu_clock_offset. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (timers_state.cpu_ticks_enabled) { timers_state.cpu_ticks_offset += cpu_get_host_ticks(); timers_state.cpu_clock_offset = cpu_get_clock_locked(); timers_state.cpu_ticks_enabled = 0; } - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); } /* Correlation between real and virtual time is always going to be @@ -407,7 +439,8 @@ static void icount_adjust(void) return; } - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); cur_time = cpu_get_clock_locked(); cur_icount = cpu_get_icount_locked(); @@ -415,20 +448,24 @@ static void icount_adjust(void) /* FIXME: This is a very crude algorithm, somewhat prone to oscillation. */ if (delta > 0 && last_delta + ICOUNT_WOBBLE < delta * 2 - && icount_time_shift > 0) { + && timers_state.icount_time_shift > 0) { /* The guest is getting too far ahead. Slow time down. */ - icount_time_shift--; + atomic_set(&timers_state.icount_time_shift, + timers_state.icount_time_shift - 1); } if (delta < 0 && last_delta - ICOUNT_WOBBLE > delta * 2 - && icount_time_shift < MAX_ICOUNT_SHIFT) { + && timers_state.icount_time_shift < MAX_ICOUNT_SHIFT) { /* The guest is getting too far behind. Speed time up. */ - icount_time_shift++; + atomic_set(&timers_state.icount_time_shift, + timers_state.icount_time_shift + 1); } last_delta = delta; - timers_state.qemu_icount_bias = cur_icount - - (timers_state.qemu_icount << icount_time_shift); - seqlock_write_end(&timers_state.vm_clock_seqlock); + atomic_set_i64(&timers_state.qemu_icount_bias, + cur_icount - (timers_state.qemu_icount + << timers_state.icount_time_shift)); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); } static void icount_adjust_rt(void *opaque) @@ -448,7 +485,8 @@ static void icount_adjust_vm(void *opaque) static int64_t qemu_icount_round(int64_t count) { - return (count + (1 << icount_time_shift) - 1) >> icount_time_shift; + int shift = atomic_read(&timers_state.icount_time_shift); + return (count + (1 << shift) - 1) >> shift; } static void icount_warp_rt(void) @@ -468,10 +506,11 @@ static void icount_warp_rt(void) return; } - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (runstate_is_running()) { - int64_t clock = REPLAY_CLOCK(REPLAY_CLOCK_VIRTUAL_RT, - cpu_get_clock_locked()); + int64_t clock = REPLAY_CLOCK_LOCKED(REPLAY_CLOCK_VIRTUAL_RT, + cpu_get_clock_locked()); int64_t warp_delta; warp_delta = clock - timers_state.vm_clock_warp_start; @@ -484,10 +523,12 @@ static void icount_warp_rt(void) int64_t delta = clock - cur_icount; warp_delta = MIN(warp_delta, delta); } - timers_state.qemu_icount_bias += warp_delta; + atomic_set_i64(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + warp_delta); } timers_state.vm_clock_warp_start = -1; - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (qemu_clock_expired(QEMU_CLOCK_VIRTUAL)) { qemu_clock_notify(QEMU_CLOCK_VIRTUAL); @@ -512,9 +553,12 @@ void qtest_clock_warp(int64_t dest) int64_t deadline = qemu_clock_deadline_ns_all(QEMU_CLOCK_VIRTUAL); int64_t warp = qemu_soonest_timeout(dest - clock, deadline); - seqlock_write_begin(&timers_state.vm_clock_seqlock); - timers_state.qemu_icount_bias += warp; - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + atomic_set_i64(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + warp); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); qemu_clock_run_timers(QEMU_CLOCK_VIRTUAL); timerlist_run_timers(aio_context->tlg.tl[QEMU_CLOCK_VIRTUAL]); @@ -539,18 +583,29 @@ void qemu_start_warp_timer(void) return; } - /* warp clock deterministically in record/replay mode */ - if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) { - return; - } + if (replay_mode != REPLAY_MODE_PLAY) { + if (!all_cpu_threads_idle()) { + return; + } - if (!all_cpu_threads_idle()) { - return; - } + if (qtest_enabled()) { + /* When testing, qtest commands advance icount. */ + return; + } - if (qtest_enabled()) { - /* When testing, qtest commands advance icount. */ - return; + replay_checkpoint(CHECKPOINT_CLOCK_WARP_START); + } else { + /* warp clock deterministically in record/replay mode */ + if (!replay_checkpoint(CHECKPOINT_CLOCK_WARP_START)) { + /* vCPU is sleeping and warp can't be started. + It is probably a race condition: notification sent + to vCPU was processed in advance and vCPU went to sleep. + Therefore we have to wake it up for doing someting. */ + if (replay_has_checkpoint()) { + qemu_clock_notify(QEMU_CLOCK_VIRTUAL); + } + return; + } } /* We want to use the earliest deadline from ALL vm_clocks */ @@ -581,9 +636,12 @@ void qemu_start_warp_timer(void) * It is useful when we want a deterministic execution time, * isolated from host latencies. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); - timers_state.qemu_icount_bias += deadline; - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); + atomic_set_i64(&timers_state.qemu_icount_bias, + timers_state.qemu_icount_bias + deadline); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); qemu_clock_notify(QEMU_CLOCK_VIRTUAL); } else { /* @@ -594,12 +652,14 @@ void qemu_start_warp_timer(void) * you will not be sending network packets continuously instead of * every 100ms. */ - seqlock_write_begin(&timers_state.vm_clock_seqlock); + seqlock_write_lock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); if (timers_state.vm_clock_warp_start == -1 || timers_state.vm_clock_warp_start > clock) { timers_state.vm_clock_warp_start = clock; } - seqlock_write_end(&timers_state.vm_clock_seqlock); + seqlock_write_unlock(&timers_state.vm_clock_seqlock, + &timers_state.vm_clock_lock); timer_mod_anticipate(timers_state.icount_warp_timer, clock + deadline); } @@ -700,7 +760,7 @@ static const VMStateDescription vmstate_timers = { .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_INT64(cpu_ticks_offset, TimersState), - VMSTATE_INT64(dummy, TimersState), + VMSTATE_UNUSED(8), VMSTATE_INT64_V(cpu_clock_offset, TimersState, 2), VMSTATE_END_OF_LIST() }, @@ -781,6 +841,7 @@ int cpu_throttle_get_percentage(void) void cpu_ticks_init(void) { seqlock_init(&timers_state.vm_clock_seqlock); + qemu_spin_init(&timers_state.vm_clock_lock); vmstate_register(NULL, 0, &vmstate_timers, &timers_state); throttle_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL_RT, cpu_throttle_timer_tick, NULL); @@ -812,7 +873,7 @@ void configure_icount(QemuOpts *opts, Error **errp) } if (strcmp(option, "auto") != 0) { errno = 0; - icount_time_shift = strtol(option, &rem_str, 0); + timers_state.icount_time_shift = strtol(option, &rem_str, 0); if (errno != 0 || *rem_str != '\0' || !strlen(option)) { error_setg(errp, "icount: Invalid shift value"); } @@ -828,7 +889,7 @@ void configure_icount(QemuOpts *opts, Error **errp) /* 125MIPS seems a reasonable initial guess at the guest speed. It will be corrected fairly quickly anyway. */ - icount_time_shift = 3; + timers_state.icount_time_shift = 3; /* Have both realtime and virtual time triggers for speed adjustment. The realtime trigger catches emulated time passing too slowly, @@ -922,6 +983,8 @@ static void start_tcg_kick_timer(void) if (!tcg_kick_vcpu_timer && CPU_NEXT(first_cpu)) { tcg_kick_vcpu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, kick_tcg_thread, NULL); + } + if (tcg_kick_vcpu_timer && !timer_pending(tcg_kick_vcpu_timer)) { timer_mod(tcg_kick_vcpu_timer, qemu_tcg_next_kick()); } } @@ -929,9 +992,8 @@ static void start_tcg_kick_timer(void) static void stop_tcg_kick_timer(void) { assert(!mttcg_enabled); - if (tcg_kick_vcpu_timer) { + if (tcg_kick_vcpu_timer && timer_pending(tcg_kick_vcpu_timer)) { timer_del(tcg_kick_vcpu_timer); - tcg_kick_vcpu_timer = NULL; } } @@ -999,7 +1061,7 @@ static int do_vm_stop(RunState state, bool send_stop) runstate_set(state); vm_state_notify(0, state); if (send_stop) { - qapi_event_send_stop(&error_abort); + qapi_event_send_stop(); } } @@ -1146,16 +1208,20 @@ static void qemu_wait_io_event_common(CPUState *cpu) process_queued_cpu_work(cpu); } -static void qemu_tcg_rr_wait_io_event(CPUState *cpu) +static void qemu_tcg_rr_wait_io_event(void) { + CPUState *cpu; + while (all_cpu_threads_idle()) { stop_tcg_kick_timer(); - qemu_cond_wait(cpu->halt_cond, &qemu_global_mutex); + qemu_cond_wait(first_cpu->halt_cond, &qemu_global_mutex); } start_tcg_kick_timer(); - qemu_wait_io_event_common(cpu); + CPU_FOREACH(cpu) { + qemu_wait_io_event_common(cpu); + } } static void qemu_wait_io_event(CPUState *cpu) @@ -1255,6 +1321,7 @@ static void *qemu_dummy_cpu_thread_fn(void *arg) qemu_wait_io_event(cpu); } while (!cpu->unplug); + qemu_mutex_unlock_iothread(); rcu_unregister_thread(); return NULL; #endif @@ -1351,7 +1418,8 @@ static int tcg_cpu_exec(CPUState *cpu) ret = cpu_exec(cpu); cpu_exec_end(cpu); #ifdef CONFIG_PROFILER - tcg_time += profile_getclock() - ti; + atomic_set(&tcg_ctx->prof.cpu_exec_time, + tcg_ctx->prof.cpu_exec_time + profile_getclock() - ti); #endif return ret; } @@ -1479,7 +1547,15 @@ static void *qemu_tcg_rr_cpu_thread_fn(void *arg) atomic_mb_set(&cpu->exit_request, 0); } - qemu_tcg_rr_wait_io_event(cpu ? cpu : QTAILQ_FIRST(&cpus)); + if (use_icount && all_cpu_threads_idle()) { + /* + * When all cpus are sleeping (e.g in WFI), to avoid a deadlock + * in the main_loop, wake it up in order to start the warp timer. + */ + qemu_notify_event(); + } + + qemu_tcg_rr_wait_io_event(); deal_with_unplugged_cpus(); } @@ -1691,7 +1767,7 @@ static void qemu_cpu_kick_thread(CPUState *cpu) } cpu->thread_kicked = true; err = pthread_kill(cpu->thread->thread, SIG_IPI); - if (err) { + if (err && err != ESRCH) { fprintf(stderr, "qemu:%s: %s", __func__, strerror(err)); exit(1); } @@ -1750,10 +1826,16 @@ bool qemu_mutex_iothread_locked(void) return iothread_locked; } -void qemu_mutex_lock_iothread(void) +/* + * The BQL is taken from so many places that it is worth profiling the + * callers directly, instead of funneling them all through a single function. + */ +void qemu_mutex_lock_iothread_impl(const char *file, int line) { + QemuMutexLockFunc bql_lock = atomic_read(&qemu_bql_mutex_lock_func); + g_assert(!qemu_mutex_iothread_locked()); - qemu_mutex_lock(&qemu_global_mutex); + bql_lock(&qemu_global_mutex, file, line); iothread_locked = true; } @@ -2007,7 +2089,8 @@ void qemu_init_vcpu(CPUState *cpu) void cpu_stop_current(void) { if (current_cpu) { - qemu_cpu_stop(current_cpu, true); + current_cpu->stop = true; + cpu_exit(current_cpu); } } @@ -2047,13 +2130,13 @@ int vm_prepare_start(void) * the STOP event. */ if (runstate_is_running()) { - qapi_event_send_stop(&error_abort); - qapi_event_send_resume(&error_abort); + qapi_event_send_stop(); + qapi_event_send_resume(); return -1; } /* We are sending this now, but the CPUs will be resumed shortly later */ - qapi_event_send_resume(&error_abort); + qapi_event_send_resume(); replay_enable_events(); cpu_enable_ticks(); @@ -2191,7 +2274,6 @@ static CpuInfoArch sysemu_target_to_cpuinfo_arch(SysEmuTarget target) return CPU_INFO_ARCH_X86; case SYS_EMU_TARGET_PPC: - case SYS_EMU_TARGET_PPCEMB: case SYS_EMU_TARGET_PPC64: return CPU_INFO_ARCH_PPC; diff --git a/crypto/Makefile.objs b/crypto/Makefile.objs index 756bab111b..256c9aca1f 100644 --- a/crypto/Makefile.objs +++ b/crypto/Makefile.objs @@ -20,11 +20,11 @@ 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-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS_RND),n,y)) += random-platform.o +crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS)) += random-gnutls.o +crypto-obj-$(if $(CONFIG_GCRYPT),n,$(if $(CONFIG_GNUTLS),n,y)) += random-platform.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-$(CONFIG_NETTLE) += pbkdf-nettle.o +crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.o crypto-obj-y += ivgen.o crypto-obj-y += ivgen-essiv.o crypto-obj-y += ivgen-plain.o diff --git a/crypto/aes.c b/crypto/aes.c index 3456eacd08..86b3092324 100644 --- a/crypto/aes.c +++ b/crypto/aes.c @@ -1059,109 +1059,109 @@ const uint32_t AES_Td4[256] = { 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; static const u32 rcon[] = { - 0x01000000, 0x02000000, 0x04000000, 0x08000000, - 0x10000000, 0x20000000, 0x40000000, 0x80000000, - 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ + 0x01000000, 0x02000000, 0x04000000, 0x08000000, + 0x10000000, 0x20000000, 0x40000000, 0x80000000, + 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; /** * Expand the cipher key into the encryption key schedule. */ int AES_set_encrypt_key(const unsigned char *userKey, const int bits, - AES_KEY *key) { + AES_KEY *key) { - u32 *rk; - int i = 0; - u32 temp; + u32 *rk; + int i = 0; + u32 temp; - if (!userKey || !key) - return -1; - if (bits != 128 && bits != 192 && bits != 256) - return -2; + if (!userKey || !key) + return -1; + if (bits != 128 && bits != 192 && bits != 256) + return -2; - rk = key->rd_key; + rk = key->rd_key; - if (bits==128) - key->rounds = 10; - else if (bits==192) - key->rounds = 12; - else - key->rounds = 14; + if (bits==128) + key->rounds = 10; + else if (bits==192) + key->rounds = 12; + else + key->rounds = 14; - rk[0] = GETU32(userKey ); - rk[1] = GETU32(userKey + 4); - rk[2] = GETU32(userKey + 8); - rk[3] = GETU32(userKey + 12); - if (bits == 128) { - while (1) { - temp = rk[3]; - rk[4] = rk[0] ^ + rk[0] = GETU32(userKey ); + rk[1] = GETU32(userKey + 4); + rk[2] = GETU32(userKey + 8); + rk[3] = GETU32(userKey + 12); + if (bits == 128) { + while (1) { + temp = rk[3]; + rk[4] = rk[0] ^ (AES_Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (AES_Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (AES_Te4[(temp ) & 0xff] & 0x0000ff00) ^ (AES_Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[5] = rk[1] ^ rk[4]; - rk[6] = rk[2] ^ rk[5]; - rk[7] = rk[3] ^ rk[6]; - if (++i == 10) { - return 0; - } - rk += 4; - } - } - rk[4] = GETU32(userKey + 16); - rk[5] = GETU32(userKey + 20); - if (bits == 192) { - while (1) { - temp = rk[ 5]; - rk[ 6] = rk[ 0] ^ + rcon[i]; + rk[5] = rk[1] ^ rk[4]; + rk[6] = rk[2] ^ rk[5]; + rk[7] = rk[3] ^ rk[6]; + if (++i == 10) { + return 0; + } + rk += 4; + } + } + rk[4] = GETU32(userKey + 16); + rk[5] = GETU32(userKey + 20); + if (bits == 192) { + while (1) { + temp = rk[ 5]; + rk[ 6] = rk[ 0] ^ (AES_Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (AES_Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (AES_Te4[(temp ) & 0xff] & 0x0000ff00) ^ (AES_Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[ 7] = rk[ 1] ^ rk[ 6]; - rk[ 8] = rk[ 2] ^ rk[ 7]; - rk[ 9] = rk[ 3] ^ rk[ 8]; - if (++i == 8) { - return 0; - } - rk[10] = rk[ 4] ^ rk[ 9]; - rk[11] = rk[ 5] ^ rk[10]; - rk += 6; - } - } - rk[6] = GETU32(userKey + 24); - rk[7] = GETU32(userKey + 28); - if (bits == 256) { - while (1) { - temp = rk[ 7]; - rk[ 8] = rk[ 0] ^ + rcon[i]; + rk[ 7] = rk[ 1] ^ rk[ 6]; + rk[ 8] = rk[ 2] ^ rk[ 7]; + rk[ 9] = rk[ 3] ^ rk[ 8]; + if (++i == 8) { + return 0; + } + rk[10] = rk[ 4] ^ rk[ 9]; + rk[11] = rk[ 5] ^ rk[10]; + rk += 6; + } + } + rk[6] = GETU32(userKey + 24); + rk[7] = GETU32(userKey + 28); + if (bits == 256) { + while (1) { + temp = rk[ 7]; + rk[ 8] = rk[ 0] ^ (AES_Te4[(temp >> 16) & 0xff] & 0xff000000) ^ (AES_Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ (AES_Te4[(temp ) & 0xff] & 0x0000ff00) ^ (AES_Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; - rk[ 9] = rk[ 1] ^ rk[ 8]; - rk[10] = rk[ 2] ^ rk[ 9]; - rk[11] = rk[ 3] ^ rk[10]; - if (++i == 7) { - return 0; - } - temp = rk[11]; - rk[12] = rk[ 4] ^ + rcon[i]; + rk[ 9] = rk[ 1] ^ rk[ 8]; + rk[10] = rk[ 2] ^ rk[ 9]; + rk[11] = rk[ 3] ^ rk[10]; + if (++i == 7) { + return 0; + } + temp = rk[11]; + rk[12] = rk[ 4] ^ (AES_Te4[(temp >> 24) ] & 0xff000000) ^ (AES_Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^ (AES_Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^ (AES_Te4[(temp ) & 0xff] & 0x000000ff); - rk[13] = rk[ 5] ^ rk[12]; - rk[14] = rk[ 6] ^ rk[13]; - rk[15] = rk[ 7] ^ rk[14]; + rk[13] = rk[ 5] ^ rk[12]; + rk[14] = rk[ 6] ^ rk[13]; + rk[15] = rk[ 7] ^ rk[14]; - rk += 8; - } - } + rk += 8; + } + } abort(); } @@ -1169,51 +1169,51 @@ int AES_set_encrypt_key(const unsigned char *userKey, const int bits, * Expand the cipher key into the decryption key schedule. */ int AES_set_decrypt_key(const unsigned char *userKey, const int bits, - AES_KEY *key) { + AES_KEY *key) { u32 *rk; - int i, j, status; - u32 temp; + int i, j, status; + u32 temp; - /* first, start with an encryption schedule */ - status = AES_set_encrypt_key(userKey, bits, key); - if (status < 0) - return status; + /* first, start with an encryption schedule */ + status = AES_set_encrypt_key(userKey, bits, key); + if (status < 0) + return status; - rk = key->rd_key; + rk = key->rd_key; - /* invert the order of the round keys: */ - for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { - temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; - temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; - temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; - temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; - } - /* apply the inverse MixColumn transform to all round keys but the first and the last: */ - for (i = 1; i < (key->rounds); i++) { - rk += 4; - rk[0] = + /* invert the order of the round keys: */ + for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the first and the last: */ + for (i = 1; i < (key->rounds); i++) { + rk += 4; + rk[0] = AES_Td0[AES_Te4[(rk[0] >> 24) ] & 0xff] ^ AES_Td1[AES_Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ AES_Td2[AES_Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ AES_Td3[AES_Te4[(rk[0] ) & 0xff] & 0xff]; - rk[1] = + rk[1] = AES_Td0[AES_Te4[(rk[1] >> 24) ] & 0xff] ^ AES_Td1[AES_Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ AES_Td2[AES_Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ AES_Td3[AES_Te4[(rk[1] ) & 0xff] & 0xff]; - rk[2] = + rk[2] = AES_Td0[AES_Te4[(rk[2] >> 24) ] & 0xff] ^ AES_Td1[AES_Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ AES_Td2[AES_Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ AES_Td3[AES_Te4[(rk[2] ) & 0xff] & 0xff]; - rk[3] = + rk[3] = AES_Td0[AES_Te4[(rk[3] >> 24) ] & 0xff] ^ AES_Td1[AES_Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ AES_Td2[AES_Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ AES_Td3[AES_Te4[(rk[3] ) & 0xff] & 0xff]; - } - return 0; + } + return 0; } #ifndef AES_ASM @@ -1222,67 +1222,67 @@ int AES_set_decrypt_key(const unsigned char *userKey, const int bits, * in and out can overlap */ void AES_encrypt(const unsigned char *in, unsigned char *out, - const AES_KEY *key) { + const AES_KEY *key) { - const u32 *rk; - u32 s0, s1, s2, s3, t0, t1, t2, t3; + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL - int r; + int r; #endif /* ?FULL_UNROLL */ - assert(in && out && key); - rk = key->rd_key; + assert(in && out && key); + rk = key->rd_key; - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(in ) ^ rk[0]; - s1 = GETU32(in + 4) ^ rk[1]; - s2 = GETU32(in + 8) ^ rk[2]; - s3 = GETU32(in + 12) ^ rk[3]; + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(in ) ^ rk[0]; + s1 = GETU32(in + 4) ^ rk[1]; + s2 = GETU32(in + 8) ^ rk[2]; + s3 = GETU32(in + 12) ^ rk[3]; #ifdef FULL_UNROLL - /* round 1: */ + /* round 1: */ t0 = AES_Te0[s0 >> 24] ^ AES_Te1[(s1 >> 16) & 0xff] ^ AES_Te2[(s2 >> 8) & 0xff] ^ AES_Te3[s3 & 0xff] ^ rk[ 4]; t1 = AES_Te0[s1 >> 24] ^ AES_Te1[(s2 >> 16) & 0xff] ^ AES_Te2[(s3 >> 8) & 0xff] ^ AES_Te3[s0 & 0xff] ^ rk[ 5]; t2 = AES_Te0[s2 >> 24] ^ AES_Te1[(s3 >> 16) & 0xff] ^ AES_Te2[(s0 >> 8) & 0xff] ^ AES_Te3[s1 & 0xff] ^ rk[ 6]; t3 = AES_Te0[s3 >> 24] ^ AES_Te1[(s0 >> 16) & 0xff] ^ AES_Te2[(s1 >> 8) & 0xff] ^ AES_Te3[s2 & 0xff] ^ rk[ 7]; - /* round 2: */ + /* round 2: */ s0 = AES_Te0[t0 >> 24] ^ AES_Te1[(t1 >> 16) & 0xff] ^ AES_Te2[(t2 >> 8) & 0xff] ^ AES_Te3[t3 & 0xff] ^ rk[ 8]; s1 = AES_Te0[t1 >> 24] ^ AES_Te1[(t2 >> 16) & 0xff] ^ AES_Te2[(t3 >> 8) & 0xff] ^ AES_Te3[t0 & 0xff] ^ rk[ 9]; s2 = AES_Te0[t2 >> 24] ^ AES_Te1[(t3 >> 16) & 0xff] ^ AES_Te2[(t0 >> 8) & 0xff] ^ AES_Te3[t1 & 0xff] ^ rk[10]; s3 = AES_Te0[t3 >> 24] ^ AES_Te1[(t0 >> 16) & 0xff] ^ AES_Te2[(t1 >> 8) & 0xff] ^ AES_Te3[t2 & 0xff] ^ rk[11]; - /* round 3: */ + /* round 3: */ t0 = AES_Te0[s0 >> 24] ^ AES_Te1[(s1 >> 16) & 0xff] ^ AES_Te2[(s2 >> 8) & 0xff] ^ AES_Te3[s3 & 0xff] ^ rk[12]; t1 = AES_Te0[s1 >> 24] ^ AES_Te1[(s2 >> 16) & 0xff] ^ AES_Te2[(s3 >> 8) & 0xff] ^ AES_Te3[s0 & 0xff] ^ rk[13]; t2 = AES_Te0[s2 >> 24] ^ AES_Te1[(s3 >> 16) & 0xff] ^ AES_Te2[(s0 >> 8) & 0xff] ^ AES_Te3[s1 & 0xff] ^ rk[14]; t3 = AES_Te0[s3 >> 24] ^ AES_Te1[(s0 >> 16) & 0xff] ^ AES_Te2[(s1 >> 8) & 0xff] ^ AES_Te3[s2 & 0xff] ^ rk[15]; - /* round 4: */ + /* round 4: */ s0 = AES_Te0[t0 >> 24] ^ AES_Te1[(t1 >> 16) & 0xff] ^ AES_Te2[(t2 >> 8) & 0xff] ^ AES_Te3[t3 & 0xff] ^ rk[16]; s1 = AES_Te0[t1 >> 24] ^ AES_Te1[(t2 >> 16) & 0xff] ^ AES_Te2[(t3 >> 8) & 0xff] ^ AES_Te3[t0 & 0xff] ^ rk[17]; s2 = AES_Te0[t2 >> 24] ^ AES_Te1[(t3 >> 16) & 0xff] ^ AES_Te2[(t0 >> 8) & 0xff] ^ AES_Te3[t1 & 0xff] ^ rk[18]; s3 = AES_Te0[t3 >> 24] ^ AES_Te1[(t0 >> 16) & 0xff] ^ AES_Te2[(t1 >> 8) & 0xff] ^ AES_Te3[t2 & 0xff] ^ rk[19]; - /* round 5: */ + /* round 5: */ t0 = AES_Te0[s0 >> 24] ^ AES_Te1[(s1 >> 16) & 0xff] ^ AES_Te2[(s2 >> 8) & 0xff] ^ AES_Te3[s3 & 0xff] ^ rk[20]; t1 = AES_Te0[s1 >> 24] ^ AES_Te1[(s2 >> 16) & 0xff] ^ AES_Te2[(s3 >> 8) & 0xff] ^ AES_Te3[s0 & 0xff] ^ rk[21]; t2 = AES_Te0[s2 >> 24] ^ AES_Te1[(s3 >> 16) & 0xff] ^ AES_Te2[(s0 >> 8) & 0xff] ^ AES_Te3[s1 & 0xff] ^ rk[22]; t3 = AES_Te0[s3 >> 24] ^ AES_Te1[(s0 >> 16) & 0xff] ^ AES_Te2[(s1 >> 8) & 0xff] ^ AES_Te3[s2 & 0xff] ^ rk[23]; - /* round 6: */ + /* round 6: */ s0 = AES_Te0[t0 >> 24] ^ AES_Te1[(t1 >> 16) & 0xff] ^ AES_Te2[(t2 >> 8) & 0xff] ^ AES_Te3[t3 & 0xff] ^ rk[24]; s1 = AES_Te0[t1 >> 24] ^ AES_Te1[(t2 >> 16) & 0xff] ^ AES_Te2[(t3 >> 8) & 0xff] ^ AES_Te3[t0 & 0xff] ^ rk[25]; s2 = AES_Te0[t2 >> 24] ^ AES_Te1[(t3 >> 16) & 0xff] ^ AES_Te2[(t0 >> 8) & 0xff] ^ AES_Te3[t1 & 0xff] ^ rk[26]; s3 = AES_Te0[t3 >> 24] ^ AES_Te1[(t0 >> 16) & 0xff] ^ AES_Te2[(t1 >> 8) & 0xff] ^ AES_Te3[t2 & 0xff] ^ rk[27]; - /* round 7: */ + /* round 7: */ t0 = AES_Te0[s0 >> 24] ^ AES_Te1[(s1 >> 16) & 0xff] ^ AES_Te2[(s2 >> 8) & 0xff] ^ AES_Te3[s3 & 0xff] ^ rk[28]; t1 = AES_Te0[s1 >> 24] ^ AES_Te1[(s2 >> 16) & 0xff] ^ AES_Te2[(s3 >> 8) & 0xff] ^ AES_Te3[s0 & 0xff] ^ rk[29]; t2 = AES_Te0[s2 >> 24] ^ AES_Te1[(s3 >> 16) & 0xff] ^ AES_Te2[(s0 >> 8) & 0xff] ^ AES_Te3[s1 & 0xff] ^ rk[30]; t3 = AES_Te0[s3 >> 24] ^ AES_Te1[(s0 >> 16) & 0xff] ^ AES_Te2[(s1 >> 8) & 0xff] ^ AES_Te3[s2 & 0xff] ^ rk[31]; - /* round 8: */ + /* round 8: */ s0 = AES_Te0[t0 >> 24] ^ AES_Te1[(t1 >> 16) & 0xff] ^ AES_Te2[(t2 >> 8) & 0xff] ^ AES_Te3[t3 & 0xff] ^ rk[32]; s1 = AES_Te0[t1 >> 24] ^ AES_Te1[(t2 >> 16) & 0xff] ^ AES_Te2[(t3 >> 8) & 0xff] ^ AES_Te3[t0 & 0xff] ^ rk[33]; s2 = AES_Te0[t2 >> 24] ^ AES_Te1[(t3 >> 16) & 0xff] ^ AES_Te2[(t0 >> 8) & 0xff] ^ AES_Te3[t1 & 0xff] ^ rk[34]; s3 = AES_Te0[t3 >> 24] ^ AES_Te1[(t0 >> 16) & 0xff] ^ AES_Te2[(t1 >> 8) & 0xff] ^ AES_Te3[t2 & 0xff] ^ rk[35]; - /* round 9: */ + /* round 9: */ t0 = AES_Te0[s0 >> 24] ^ AES_Te1[(s1 >> 16) & 0xff] ^ AES_Te2[(s2 >> 8) & 0xff] ^ AES_Te3[s3 & 0xff] ^ rk[36]; t1 = AES_Te0[s1 >> 24] ^ AES_Te1[(s2 >> 16) & 0xff] ^ AES_Te2[(s3 >> 8) & 0xff] ^ AES_Te3[s0 & 0xff] ^ rk[37]; t2 = AES_Te0[s2 >> 24] ^ AES_Te1[(s3 >> 16) & 0xff] ^ AES_Te2[(s0 >> 8) & 0xff] ^ AES_Te3[s1 & 0xff] ^ rk[38]; @@ -1375,37 +1375,37 @@ void AES_encrypt(const unsigned char *in, unsigned char *out, } #endif /* ?FULL_UNROLL */ /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = + * apply last round and + * map cipher state to byte array block: + */ + s0 = (AES_Te4[(t0 >> 24) ] & 0xff000000) ^ (AES_Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Te4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[0]; - PUTU32(out , s0); - s1 = + rk[0]; + PUTU32(out , s0); + s1 = (AES_Te4[(t1 >> 24) ] & 0xff000000) ^ (AES_Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Te4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[1]; - PUTU32(out + 4, s1); - s2 = + rk[1]; + PUTU32(out + 4, s1); + s2 = (AES_Te4[(t2 >> 24) ] & 0xff000000) ^ (AES_Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Te4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[2]; - PUTU32(out + 8, s2); - s3 = + rk[2]; + PUTU32(out + 8, s2); + s3 = (AES_Te4[(t3 >> 24) ] & 0xff000000) ^ (AES_Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Te4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[3]; - PUTU32(out + 12, s3); + rk[3]; + PUTU32(out + 12, s3); } /* @@ -1413,21 +1413,21 @@ void AES_encrypt(const unsigned char *in, unsigned char *out, * in and out can overlap */ void AES_decrypt(const unsigned char *in, unsigned char *out, - const AES_KEY *key) { + const AES_KEY *key) { - const u32 *rk; - u32 s0, s1, s2, s3, t0, t1, t2, t3; + const u32 *rk; + u32 s0, s1, s2, s3, t0, t1, t2, t3; #ifndef FULL_UNROLL - int r; + int r; #endif /* ?FULL_UNROLL */ - assert(in && out && key); - rk = key->rd_key; + assert(in && out && key); + rk = key->rd_key; - /* - * map byte array block to cipher state - * and add initial round key: - */ + /* + * map byte array block to cipher state + * and add initial round key: + */ s0 = GETU32(in ) ^ rk[0]; s1 = GETU32(in + 4) ^ rk[1]; s2 = GETU32(in + 8) ^ rk[2]; @@ -1502,7 +1502,7 @@ void AES_decrypt(const unsigned char *in, unsigned char *out, t3 = AES_Td0[s3 >> 24] ^ AES_Td1[(s2 >> 16) & 0xff] ^ AES_Td2[(s1 >> 8) & 0xff] ^ AES_Td3[s0 & 0xff] ^ rk[55]; } } - rk += key->rounds << 2; + rk += key->rounds << 2; #else /* !FULL_UNROLL */ /* * Nr - 1 full rounds: @@ -1566,88 +1566,88 @@ void AES_decrypt(const unsigned char *in, unsigned char *out, } #endif /* ?FULL_UNROLL */ /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = + * apply last round and + * map cipher state to byte array block: + */ + s0 = (AES_Td4[(t0 >> 24) ] & 0xff000000) ^ (AES_Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Td4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[0]; - PUTU32(out , s0); - s1 = + rk[0]; + PUTU32(out , s0); + s1 = (AES_Td4[(t1 >> 24) ] & 0xff000000) ^ (AES_Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Td4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[1]; - PUTU32(out + 4, s1); - s2 = + rk[1]; + PUTU32(out + 4, s1); + s2 = (AES_Td4[(t2 >> 24) ] & 0xff000000) ^ (AES_Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Td4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[2]; - PUTU32(out + 8, s2); - s3 = + rk[2]; + PUTU32(out + 8, s2); + s3 = (AES_Td4[(t3 >> 24) ] & 0xff000000) ^ (AES_Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ (AES_Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ (AES_Td4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[3]; - PUTU32(out + 12, s3); + rk[3]; + PUTU32(out + 12, s3); } #endif /* AES_ASM */ void AES_cbc_encrypt(const unsigned char *in, unsigned char *out, - const unsigned long length, const AES_KEY *key, - unsigned char *ivec, const int enc) + const unsigned long length, const AES_KEY *key, + unsigned char *ivec, const int enc) { - unsigned long n; - unsigned long len = length; - unsigned char tmp[AES_BLOCK_SIZE]; + unsigned long n; + unsigned long len = length; + unsigned char tmp[AES_BLOCK_SIZE]; - assert(in && out && key && ivec); + assert(in && out && key && ivec); - if (enc) { - while (len >= AES_BLOCK_SIZE) { - for(n=0; n < AES_BLOCK_SIZE; ++n) - tmp[n] = in[n] ^ ivec[n]; - AES_encrypt(tmp, out, key); - memcpy(ivec, out, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } - if (len) { - for(n=0; n < len; ++n) - tmp[n] = in[n] ^ ivec[n]; - for(n=len; n < AES_BLOCK_SIZE; ++n) - tmp[n] = ivec[n]; - AES_encrypt(tmp, tmp, key); - memcpy(out, tmp, AES_BLOCK_SIZE); - memcpy(ivec, tmp, AES_BLOCK_SIZE); - } - } else { - while (len >= AES_BLOCK_SIZE) { - memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(in, out, key); - for(n=0; n < AES_BLOCK_SIZE; ++n) - out[n] ^= ivec[n]; - memcpy(ivec, tmp, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } - if (len) { - memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(tmp, tmp, key); - for(n=0; n < len; ++n) - out[n] = tmp[n] ^ ivec[n]; - memcpy(ivec, tmp, AES_BLOCK_SIZE); - } - } + if (enc) { + while (len >= AES_BLOCK_SIZE) { + for(n=0; n < AES_BLOCK_SIZE; ++n) + tmp[n] = in[n] ^ ivec[n]; + AES_encrypt(tmp, out, key); + memcpy(ivec, out, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (len) { + for(n=0; n < len; ++n) + tmp[n] = in[n] ^ ivec[n]; + for(n=len; n < AES_BLOCK_SIZE; ++n) + tmp[n] = ivec[n]; + AES_encrypt(tmp, tmp, key); + memcpy(out, tmp, AES_BLOCK_SIZE); + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } else { + while (len >= AES_BLOCK_SIZE) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(in, out, key); + for(n=0; n < AES_BLOCK_SIZE; ++n) + out[n] ^= ivec[n]; + memcpy(ivec, tmp, AES_BLOCK_SIZE); + len -= AES_BLOCK_SIZE; + in += AES_BLOCK_SIZE; + out += AES_BLOCK_SIZE; + } + if (len) { + memcpy(tmp, in, AES_BLOCK_SIZE); + AES_decrypt(tmp, tmp, key); + for(n=0; n < len; ++n) + out[n] = tmp[n] ^ ivec[n]; + memcpy(ivec, tmp, AES_BLOCK_SIZE); + } + } } diff --git a/crypto/afsplit.c b/crypto/afsplit.c index 825e2cff26..328d68c96b 100644 --- a/crypto/afsplit.c +++ b/crypto/afsplit.c @@ -8,19 +8,18 @@ * 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 + * 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 library is distributed in the hope that it will be useful, + * 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 - * 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 . + * 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" diff --git a/crypto/block-luks.c b/crypto/block-luks.c index 5738124773..5a69b3ce74 100644 --- a/crypto/block-luks.c +++ b/crypto/block-luks.c @@ -146,7 +146,7 @@ struct QCryptoBlockLUKSKeySlot { uint32_t key_offset; /* number of anti-forensic stripes */ uint32_t stripes; -} QEMU_PACKED; +}; QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSKeySlot) != 48); @@ -191,7 +191,7 @@ struct QCryptoBlockLUKSHeader { /* key slots */ QCryptoBlockLUKSKeySlot key_slots[QCRYPTO_BLOCK_LUKS_NUM_KEY_SLOTS]; -} QEMU_PACKED; +}; QEMU_BUILD_BUG_ON(sizeof(struct QCryptoBlockLUKSHeader) != 592); @@ -504,14 +504,14 @@ qcrypto_block_luks_load_key(QCryptoBlock *block, * 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) { + if (qcrypto_block_cipher_decrypt_helper(cipher, + niv, + ivgen, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + 0, + splitkey, + splitkeylen, + errp) < 0) { goto cleanup; } @@ -636,6 +636,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, + size_t n_threads, Error **errp) { QCryptoBlockLUKS *luks; @@ -836,11 +837,10 @@ qcrypto_block_luks_open(QCryptoBlock *block, goto fail; } - block->cipher = qcrypto_cipher_new(cipheralg, - ciphermode, - masterkey, masterkeylen, - errp); - if (!block->cipher) { + ret = qcrypto_block_init_cipher(block, cipheralg, ciphermode, + masterkey, masterkeylen, n_threads, + errp); + if (ret < 0) { ret = -ENOTSUP; goto fail; } @@ -863,7 +863,7 @@ qcrypto_block_luks_open(QCryptoBlock *block, fail: g_free(masterkey); - qcrypto_cipher_free(block->cipher); + qcrypto_block_free_cipher(block); qcrypto_ivgen_free(block->ivgen); g_free(luks); g_free(password); @@ -1030,11 +1030,9 @@ qcrypto_block_luks_create(QCryptoBlock *block, /* 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) { + if (qcrypto_block_init_cipher(block, luks_opts.cipher_alg, + luks_opts.cipher_mode, masterkey, + luks->header.key_bytes, 1, errp) < 0) { goto error; } @@ -1219,12 +1217,12 @@ qcrypto_block_luks_create(QCryptoBlock *block, /* 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) { + if (qcrypto_block_cipher_encrypt_helper(cipher, block->niv, ivgen, + QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, + 0, + splitkey, + splitkeylen, + errp) < 0) { goto error; } @@ -1341,6 +1339,9 @@ qcrypto_block_luks_create(QCryptoBlock *block, qcrypto_ivgen_free(ivgen); qcrypto_cipher_free(cipher); + qcrypto_block_free_cipher(block); + qcrypto_ivgen_free(block->ivgen); + g_free(luks); return -1; } @@ -1406,8 +1407,7 @@ qcrypto_block_luks_decrypt(QCryptoBlock *block, { assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); - return qcrypto_block_decrypt_helper(block->cipher, - block->niv, block->ivgen, + return qcrypto_block_decrypt_helper(block, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, offset, buf, len, errp); } @@ -1422,8 +1422,7 @@ qcrypto_block_luks_encrypt(QCryptoBlock *block, { assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE)); - return qcrypto_block_encrypt_helper(block->cipher, - block->niv, block->ivgen, + return qcrypto_block_encrypt_helper(block, QCRYPTO_BLOCK_LUKS_SECTOR_SIZE, offset, buf, len, errp); } diff --git a/crypto/block-qcow.c b/crypto/block-qcow.c index 4284e05167..cefb3b2a7b 100644 --- a/crypto/block-qcow.c +++ b/crypto/block-qcow.c @@ -44,6 +44,7 @@ qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED, static int qcrypto_block_qcow_init(QCryptoBlock *block, const char *keysecret, + size_t n_threads, Error **errp) { char *password; @@ -71,11 +72,11 @@ qcrypto_block_qcow_init(QCryptoBlock *block, 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 = qcrypto_block_init_cipher(block, QCRYPTO_CIPHER_ALG_AES_128, + QCRYPTO_CIPHER_MODE_CBC, + keybuf, G_N_ELEMENTS(keybuf), + n_threads, errp); + if (ret < 0) { ret = -ENOTSUP; goto fail; } @@ -86,7 +87,7 @@ qcrypto_block_qcow_init(QCryptoBlock *block, return 0; fail: - qcrypto_cipher_free(block->cipher); + qcrypto_block_free_cipher(block); qcrypto_ivgen_free(block->ivgen); return ret; } @@ -99,9 +100,12 @@ qcrypto_block_qcow_open(QCryptoBlock *block, QCryptoBlockReadFunc readfunc G_GNUC_UNUSED, void *opaque G_GNUC_UNUSED, unsigned int flags, + size_t n_threads, Error **errp) { if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) { + block->sector_size = QCRYPTO_BLOCK_QCOW_SECTOR_SIZE; + block->payload_offset = 0; return 0; } else { if (!options->u.qcow.key_secret) { @@ -110,8 +114,8 @@ qcrypto_block_qcow_open(QCryptoBlock *block, optprefix ? optprefix : ""); return -1; } - return qcrypto_block_qcow_init(block, - options->u.qcow.key_secret, errp); + return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, + n_threads, errp); } } @@ -131,7 +135,7 @@ qcrypto_block_qcow_create(QCryptoBlock *block, return -1; } /* QCow2 has no special header, since everything is hardwired */ - return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp); + return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, 1, errp); } @@ -150,8 +154,7 @@ qcrypto_block_qcow_decrypt(QCryptoBlock *block, { assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); - return qcrypto_block_decrypt_helper(block->cipher, - block->niv, block->ivgen, + return qcrypto_block_decrypt_helper(block, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE, offset, buf, len, errp); } @@ -166,8 +169,7 @@ qcrypto_block_qcow_encrypt(QCryptoBlock *block, { assert(QEMU_IS_ALIGNED(offset, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); assert(QEMU_IS_ALIGNED(len, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE)); - return qcrypto_block_encrypt_helper(block->cipher, - block->niv, block->ivgen, + return qcrypto_block_encrypt_helper(block, QCRYPTO_BLOCK_QCOW_SECTOR_SIZE, offset, buf, len, errp); } diff --git a/crypto/block.c b/crypto/block.c index e59d1140fe..d70d401f87 100644 --- a/crypto/block.c +++ b/crypto/block.c @@ -52,6 +52,7 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, + size_t n_threads, Error **errp) { QCryptoBlock *block = g_new0(QCryptoBlock, 1); @@ -69,11 +70,14 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, block->driver = qcrypto_block_drivers[options->format]; if (block->driver->open(block, options, optprefix, - readfunc, opaque, flags, errp) < 0) { + readfunc, opaque, flags, n_threads, errp) < 0) + { g_free(block); return NULL; } + qemu_mutex_init(&block->mutex); + return block; } @@ -105,6 +109,8 @@ QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options, return NULL; } + qemu_mutex_init(&block->mutex); + return block; } @@ -148,12 +154,97 @@ int qcrypto_block_encrypt(QCryptoBlock *block, QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block) { - return block->cipher; + /* Ciphers should be accessed through pop/push method to be thread-safe. + * Better, they should not be accessed externally at all (note, that + * pop/push are static functions) + * This function is used only in test with one thread (it's safe to skip + * pop/push interface), so it's enough to assert it here: + */ + assert(block->n_ciphers <= 1); + return block->ciphers ? block->ciphers[0] : NULL; } +static QCryptoCipher *qcrypto_block_pop_cipher(QCryptoBlock *block) +{ + QCryptoCipher *cipher; + + qemu_mutex_lock(&block->mutex); + + assert(block->n_free_ciphers > 0); + block->n_free_ciphers--; + cipher = block->ciphers[block->n_free_ciphers]; + + qemu_mutex_unlock(&block->mutex); + + return cipher; +} + + +static void qcrypto_block_push_cipher(QCryptoBlock *block, + QCryptoCipher *cipher) +{ + qemu_mutex_lock(&block->mutex); + + assert(block->n_free_ciphers < block->n_ciphers); + block->ciphers[block->n_free_ciphers] = cipher; + block->n_free_ciphers++; + + qemu_mutex_unlock(&block->mutex); +} + + +int qcrypto_block_init_cipher(QCryptoBlock *block, + QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, size_t nkey, + size_t n_threads, Error **errp) +{ + size_t i; + + assert(!block->ciphers && !block->n_ciphers && !block->n_free_ciphers); + + block->ciphers = g_new0(QCryptoCipher *, n_threads); + + for (i = 0; i < n_threads; i++) { + block->ciphers[i] = qcrypto_cipher_new(alg, mode, key, nkey, errp); + if (!block->ciphers[i]) { + qcrypto_block_free_cipher(block); + return -1; + } + block->n_ciphers++; + block->n_free_ciphers++; + } + + return 0; +} + + +void qcrypto_block_free_cipher(QCryptoBlock *block) +{ + size_t i; + + if (!block->ciphers) { + return; + } + + assert(block->n_ciphers == block->n_free_ciphers); + + for (i = 0; i < block->n_ciphers; i++) { + qcrypto_cipher_free(block->ciphers[i]); + } + + g_free(block->ciphers); + block->ciphers = NULL; + block->n_ciphers = block->n_free_ciphers = 0; +} + QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block) { + /* ivgen should be accessed under mutex. However, this function is used only + * in test with one thread, so it's enough to assert it here: + */ + assert(block->n_ciphers <= 1); return block->ivgen; } @@ -184,20 +275,29 @@ void qcrypto_block_free(QCryptoBlock *block) block->driver->cleanup(block); - qcrypto_cipher_free(block->cipher); + qcrypto_block_free_cipher(block); qcrypto_ivgen_free(block->ivgen); + qemu_mutex_destroy(&block->mutex); g_free(block); } -int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, - size_t niv, - QCryptoIVGen *ivgen, - int sectorsize, - uint64_t offset, - uint8_t *buf, - size_t len, - Error **errp) +typedef int (*QCryptoCipherEncDecFunc)(QCryptoCipher *cipher, + const void *in, + void *out, + size_t len, + Error **errp); + +static int do_qcrypto_block_cipher_encdec(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + QemuMutex *ivgen_mutex, + int sectorsize, + uint64_t offset, + uint8_t *buf, + size_t len, + QCryptoCipherEncDecFunc func, + Error **errp) { uint8_t *iv; int ret = -1; @@ -211,10 +311,15 @@ int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, while (len > 0) { size_t nbytes; if (niv) { - if (qcrypto_ivgen_calculate(ivgen, - startsector, - iv, niv, - errp) < 0) { + if (ivgen_mutex) { + qemu_mutex_lock(ivgen_mutex); + } + ret = qcrypto_ivgen_calculate(ivgen, startsector, iv, niv, errp); + if (ivgen_mutex) { + qemu_mutex_unlock(ivgen_mutex); + } + + if (ret < 0) { goto cleanup; } @@ -226,8 +331,7 @@ int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, } nbytes = len > sectorsize ? sectorsize : len; - if (qcrypto_cipher_decrypt(cipher, buf, buf, - nbytes, errp) < 0) { + if (func(cipher, buf, buf, nbytes, errp) < 0) { goto cleanup; } @@ -243,54 +347,69 @@ int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, } -int qcrypto_block_encrypt_helper(QCryptoCipher *cipher, - size_t niv, - QCryptoIVGen *ivgen, +int qcrypto_block_cipher_decrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t offset, + uint8_t *buf, + size_t len, + Error **errp) +{ + return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, NULL, sectorsize, + offset, buf, len, + qcrypto_cipher_decrypt, errp); +} + + +int qcrypto_block_cipher_encrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t offset, + uint8_t *buf, + size_t len, + Error **errp) +{ + return do_qcrypto_block_cipher_encdec(cipher, niv, ivgen, NULL, sectorsize, + offset, buf, len, + qcrypto_cipher_encrypt, errp); +} + +int qcrypto_block_decrypt_helper(QCryptoBlock *block, int sectorsize, uint64_t offset, uint8_t *buf, size_t len, Error **errp) { - uint8_t *iv; - int ret = -1; - uint64_t startsector = offset / sectorsize; + int ret; + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); - assert(QEMU_IS_ALIGNED(offset, sectorsize)); - assert(QEMU_IS_ALIGNED(len, sectorsize)); + ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, + &block->mutex, sectorsize, offset, buf, + len, qcrypto_cipher_decrypt, errp); - iv = niv ? g_new0(uint8_t, niv) : NULL; + qcrypto_block_push_cipher(block, cipher); + + return ret; +} + +int qcrypto_block_encrypt_helper(QCryptoBlock *block, + int sectorsize, + uint64_t offset, + uint8_t *buf, + size_t len, + Error **errp) +{ + int ret; + QCryptoCipher *cipher = qcrypto_block_pop_cipher(block); + + ret = do_qcrypto_block_cipher_encdec(cipher, block->niv, block->ivgen, + &block->mutex, sectorsize, offset, buf, + len, qcrypto_cipher_encrypt, errp); + + qcrypto_block_push_cipher(block, cipher); - 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/crypto/blockpriv.h b/crypto/blockpriv.h index 41840abcec..5438e822fd 100644 --- a/crypto/blockpriv.h +++ b/crypto/blockpriv.h @@ -22,6 +22,7 @@ #define QCRYPTO_BLOCKPRIV_H #include "crypto/block.h" +#include "qemu/thread.h" typedef struct QCryptoBlockDriver QCryptoBlockDriver; @@ -31,8 +32,12 @@ struct QCryptoBlock { const QCryptoBlockDriver *driver; void *opaque; - QCryptoCipher *cipher; + QCryptoCipher **ciphers; + size_t n_ciphers; + size_t n_free_ciphers; QCryptoIVGen *ivgen; + QemuMutex mutex; + QCryptoHashAlgorithm kdfhash; size_t niv; uint64_t payload_offset; /* In bytes */ @@ -46,6 +51,7 @@ struct QCryptoBlockDriver { QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, + size_t n_threads, Error **errp); int (*create)(QCryptoBlock *block, @@ -78,22 +84,44 @@ struct QCryptoBlockDriver { }; -int qcrypto_block_decrypt_helper(QCryptoCipher *cipher, - size_t niv, - QCryptoIVGen *ivgen, +int qcrypto_block_cipher_decrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t offset, + uint8_t *buf, + size_t len, + Error **errp); + +int qcrypto_block_cipher_encrypt_helper(QCryptoCipher *cipher, + size_t niv, + QCryptoIVGen *ivgen, + int sectorsize, + uint64_t offset, + uint8_t *buf, + size_t len, + Error **errp); + +int qcrypto_block_decrypt_helper(QCryptoBlock *block, int sectorsize, uint64_t offset, uint8_t *buf, size_t len, Error **errp); -int qcrypto_block_encrypt_helper(QCryptoCipher *cipher, - size_t niv, - QCryptoIVGen *ivgen, +int qcrypto_block_encrypt_helper(QCryptoBlock *block, int sectorsize, uint64_t offset, uint8_t *buf, size_t len, Error **errp); +int qcrypto_block_init_cipher(QCryptoBlock *block, + QCryptoCipherAlgorithm alg, + QCryptoCipherMode mode, + const uint8_t *key, size_t nkey, + size_t n_threads, Error **errp); + +void qcrypto_block_free_cipher(QCryptoBlock *block); + #endif /* QCRYPTO_BLOCKPRIV_H */ diff --git a/crypto/desrfb.c b/crypto/desrfb.c index ec47dea3bb..3274c36510 100644 --- a/crypto/desrfb.c +++ b/crypto/desrfb.c @@ -37,353 +37,353 @@ static void cookey(unsigned long *); static unsigned long KnL[32] = { 0L }; static const unsigned short bytebit[8] = { - 01, 02, 04, 010, 020, 040, 0100, 0200 }; + 01, 02, 04, 010, 020, 040, 0100, 0200 }; static const unsigned long bigbyte[24] = { - 0x800000L, 0x400000L, 0x200000L, 0x100000L, - 0x80000L, 0x40000L, 0x20000L, 0x10000L, - 0x8000L, 0x4000L, 0x2000L, 0x1000L, - 0x800L, 0x400L, 0x200L, 0x100L, - 0x80L, 0x40L, 0x20L, 0x10L, - 0x8L, 0x4L, 0x2L, 0x1L }; + 0x800000L, 0x400000L, 0x200000L, 0x100000L, + 0x80000L, 0x40000L, 0x20000L, 0x10000L, + 0x8000L, 0x4000L, 0x2000L, 0x1000L, + 0x800L, 0x400L, 0x200L, 0x100L, + 0x80L, 0x40L, 0x20L, 0x10L, + 0x8L, 0x4L, 0x2L, 0x1L }; /* Use the key schedule specified in the Standard (ANSI X3.92-1981). */ static const unsigned char pc1[56] = { - 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, - 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, - 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, - 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 }; static const unsigned char totrot[16] = { - 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; + 1,2,4,6,8,10,12,14,15,17,19,21,23,25,27,28 }; static const unsigned char pc2[48] = { - 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, - 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, - 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, - 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 }; /* Thanks to James Gillogly & Phil Karn! */ void deskey(unsigned char *key, int edf) { - register int i, j, l, m, n; - unsigned char pc1m[56], pcr[56]; - unsigned long kn[32]; + register int i, j, l, m, n; + unsigned char pc1m[56], pcr[56]; + unsigned long kn[32]; - for ( j = 0; j < 56; j++ ) { - l = pc1[j]; - m = l & 07; - pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; - } - for( i = 0; i < 16; i++ ) { - if( edf == DE1 ) m = (15 - i) << 1; - else m = i << 1; - n = m + 1; - kn[m] = kn[n] = 0L; - for( j = 0; j < 28; j++ ) { - l = j + totrot[i]; - if( l < 28 ) pcr[j] = pc1m[l]; - else pcr[j] = pc1m[l - 28]; - } - for( j = 28; j < 56; j++ ) { - l = j + totrot[i]; - if( l < 56 ) pcr[j] = pc1m[l]; - else pcr[j] = pc1m[l - 28]; - } - for( j = 0; j < 24; j++ ) { - if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; - if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; - } - } - cookey(kn); - return; - } + for ( j = 0; j < 56; j++ ) { + l = pc1[j]; + m = l & 07; + pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0; + } + for( i = 0; i < 16; i++ ) { + if( edf == DE1 ) m = (15 - i) << 1; + else m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for( j = 0; j < 28; j++ ) { + l = j + totrot[i]; + if( l < 28 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 28; j < 56; j++ ) { + l = j + totrot[i]; + if( l < 56 ) pcr[j] = pc1m[l]; + else pcr[j] = pc1m[l - 28]; + } + for( j = 0; j < 24; j++ ) { + if( pcr[pc2[j]] ) kn[m] |= bigbyte[j]; + if( pcr[pc2[j+24]] ) kn[n] |= bigbyte[j]; + } + } + cookey(kn); + return; + } static void cookey(register unsigned long *raw1) { - register unsigned long *cook, *raw0; - unsigned long dough[32]; - register int i; + register unsigned long *cook, *raw0; + unsigned long dough[32]; + register int i; - cook = dough; - for( i = 0; i < 16; i++, raw1++ ) { - raw0 = raw1++; - *cook = (*raw0 & 0x00fc0000L) << 6; - *cook |= (*raw0 & 0x00000fc0L) << 10; - *cook |= (*raw1 & 0x00fc0000L) >> 10; - *cook++ |= (*raw1 & 0x00000fc0L) >> 6; - *cook = (*raw0 & 0x0003f000L) << 12; - *cook |= (*raw0 & 0x0000003fL) << 16; - *cook |= (*raw1 & 0x0003f000L) >> 4; - *cook++ |= (*raw1 & 0x0000003fL); - } - usekey(dough); - return; - } + cook = dough; + for( i = 0; i < 16; i++, raw1++ ) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + usekey(dough); + return; + } void usekey(register unsigned long *from) { - register unsigned long *to, *endp; + register unsigned long *to, *endp; - to = KnL, endp = &KnL[32]; - while( to < endp ) *to++ = *from++; - return; - } + to = KnL, endp = &KnL[32]; + while( to < endp ) *to++ = *from++; + return; + } void des(unsigned char *inblock, unsigned char *outblock) { - unsigned long work[2]; + unsigned long work[2]; - scrunch(inblock, work); - desfunc(work, KnL); - unscrun(work, outblock); - return; - } + scrunch(inblock, work); + desfunc(work, KnL); + unscrun(work, outblock); + return; + } static void scrunch(register unsigned char *outof, register unsigned long *into) { - *into = (*outof++ & 0xffL) << 24; - *into |= (*outof++ & 0xffL) << 16; - *into |= (*outof++ & 0xffL) << 8; - *into++ |= (*outof++ & 0xffL); - *into = (*outof++ & 0xffL) << 24; - *into |= (*outof++ & 0xffL) << 16; - *into |= (*outof++ & 0xffL) << 8; - *into |= (*outof & 0xffL); - return; - } + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into++ |= (*outof++ & 0xffL); + *into = (*outof++ & 0xffL) << 24; + *into |= (*outof++ & 0xffL) << 16; + *into |= (*outof++ & 0xffL) << 8; + *into |= (*outof & 0xffL); + return; + } static void unscrun(register unsigned long *outof, register unsigned char *into) { - *into++ = (unsigned char)((*outof >> 24) & 0xffL); - *into++ = (unsigned char)((*outof >> 16) & 0xffL); - *into++ = (unsigned char)((*outof >> 8) & 0xffL); - *into++ = (unsigned char)(*outof++ & 0xffL); - *into++ = (unsigned char)((*outof >> 24) & 0xffL); - *into++ = (unsigned char)((*outof >> 16) & 0xffL); - *into++ = (unsigned char)((*outof >> 8) & 0xffL); - *into = (unsigned char)(*outof & 0xffL); - return; - } + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into++ = (unsigned char)(*outof++ & 0xffL); + *into++ = (unsigned char)((*outof >> 24) & 0xffL); + *into++ = (unsigned char)((*outof >> 16) & 0xffL); + *into++ = (unsigned char)((*outof >> 8) & 0xffL); + *into = (unsigned char)(*outof & 0xffL); + return; + } static const unsigned long SP1[64] = { - 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, - 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, - 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, - 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, - 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, - 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, - 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, - 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, - 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, - 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, - 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, - 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, - 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, - 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, - 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, - 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; + 0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L, + 0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L, + 0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L, + 0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L, + 0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L, + 0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L, + 0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L, + 0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L, + 0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L, + 0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L, + 0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L, + 0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L, + 0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L, + 0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L, + 0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L }; static const unsigned long SP2[64] = { - 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, - 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, - 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, - 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, - 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, - 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, - 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, - 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, - 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, - 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, - 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, - 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, - 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, - 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, - 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, - 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; + 0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L, + 0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L, + 0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L, + 0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L, + 0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L, + 0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L, + 0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L, + 0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L, + 0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L, + 0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L, + 0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L, + 0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L, + 0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L, + 0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L, + 0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L, + 0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L }; static const unsigned long SP3[64] = { - 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, - 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, - 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, - 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, - 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, - 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, - 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, - 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, - 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, - 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, - 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, - 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, - 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, - 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, - 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, - 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; + 0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L, + 0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L, + 0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L, + 0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L, + 0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L, + 0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L, + 0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L, + 0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L, + 0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L, + 0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L, + 0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L, + 0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L, + 0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L, + 0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L, + 0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L, + 0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L }; static const unsigned long SP4[64] = { - 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, - 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, - 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, - 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, - 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, - 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, - 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, - 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, - 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, - 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, - 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, - 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, - 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, - 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, - 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, - 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L, + 0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L, + 0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L, + 0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L, + 0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L, + 0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L, + 0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L, + 0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L, + 0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L, + 0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L, + 0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L, + 0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L, + 0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L }; static const unsigned long SP5[64] = { - 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, - 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, - 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, - 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, - 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, - 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, - 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, - 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, - 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, - 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, - 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, - 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, - 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, - 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, - 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, - 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; + 0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L, + 0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L, + 0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L, + 0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L, + 0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L, + 0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L, + 0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L, + 0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L, + 0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L, + 0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L, + 0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L, + 0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L, + 0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L, + 0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L, + 0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L, + 0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L }; static const unsigned long SP6[64] = { - 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, - 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, - 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, - 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, - 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, - 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, - 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, - 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, - 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, - 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, - 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, - 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, - 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, - 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, - 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, - 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; + 0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L, + 0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L, + 0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L, + 0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L, + 0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L, + 0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L, + 0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L, + 0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L, + 0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L, + 0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L, + 0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L, + 0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L, + 0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L, + 0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L, + 0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L }; static const unsigned long SP7[64] = { - 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, - 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, - 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, - 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, - 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, - 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, - 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, - 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, - 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, - 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, - 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, - 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, - 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, - 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, - 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, - 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; + 0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L, + 0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L, + 0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L, + 0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L, + 0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L, + 0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L, + 0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L, + 0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L, + 0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L, + 0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L, + 0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L, + 0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L, + 0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L, + 0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L, + 0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L, + 0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L }; static const unsigned long SP8[64] = { - 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, - 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, - 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, - 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, - 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, - 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, - 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, - 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, - 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, - 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, - 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, - 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, - 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, - 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, - 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, - 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; + 0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L, + 0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L, + 0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L, + 0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L, + 0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L, + 0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L, + 0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L, + 0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L, + 0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L, + 0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L, + 0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L, + 0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L, + 0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L, + 0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L, + 0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L, + 0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L }; static void desfunc(register unsigned long *block, register unsigned long *keys) { - register unsigned long fval, work, right, leftt; - register int round; + register unsigned long fval, work, right, leftt; + register int round; - leftt = block[0]; - right = block[1]; - work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; - right ^= work; - leftt ^= (work << 4); - work = ((leftt >> 16) ^ right) & 0x0000ffffL; - right ^= work; - leftt ^= (work << 16); - work = ((right >> 2) ^ leftt) & 0x33333333L; - leftt ^= work; - right ^= (work << 2); - work = ((right >> 8) ^ leftt) & 0x00ff00ffL; - leftt ^= work; - right ^= (work << 8); - right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; - work = (leftt ^ right) & 0xaaaaaaaaL; - leftt ^= work; - right ^= work; - leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; + leftt = block[0]; + right = block[1]; + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL; + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL; - for( round = 0; round < 8; round++ ) { - work = (right << 28) | (right >> 4); - work ^= *keys++; - fval = SP7[ work & 0x3fL]; - fval |= SP5[(work >> 8) & 0x3fL]; - fval |= SP3[(work >> 16) & 0x3fL]; - fval |= SP1[(work >> 24) & 0x3fL]; - work = right ^ *keys++; - fval |= SP8[ work & 0x3fL]; - fval |= SP6[(work >> 8) & 0x3fL]; - fval |= SP4[(work >> 16) & 0x3fL]; - fval |= SP2[(work >> 24) & 0x3fL]; - leftt ^= fval; - work = (leftt << 28) | (leftt >> 4); - work ^= *keys++; - fval = SP7[ work & 0x3fL]; - fval |= SP5[(work >> 8) & 0x3fL]; - fval |= SP3[(work >> 16) & 0x3fL]; - fval |= SP1[(work >> 24) & 0x3fL]; - work = leftt ^ *keys++; - fval |= SP8[ work & 0x3fL]; - fval |= SP6[(work >> 8) & 0x3fL]; - fval |= SP4[(work >> 16) & 0x3fL]; - fval |= SP2[(work >> 24) & 0x3fL]; - right ^= fval; - } + for( round = 0; round < 8; round++ ) { + work = (right << 28) | (right >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + leftt ^= fval; + work = (leftt << 28) | (leftt >> 4); + work ^= *keys++; + fval = SP7[ work & 0x3fL]; + fval |= SP5[(work >> 8) & 0x3fL]; + fval |= SP3[(work >> 16) & 0x3fL]; + fval |= SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + fval |= SP8[ work & 0x3fL]; + fval |= SP6[(work >> 8) & 0x3fL]; + fval |= SP4[(work >> 16) & 0x3fL]; + fval |= SP2[(work >> 24) & 0x3fL]; + right ^= fval; + } - right = (right << 31) | (right >> 1); - work = (leftt ^ right) & 0xaaaaaaaaL; - leftt ^= work; - right ^= work; - leftt = (leftt << 31) | (leftt >> 1); - work = ((leftt >> 8) ^ right) & 0x00ff00ffL; - right ^= work; - leftt ^= (work << 8); - work = ((leftt >> 2) ^ right) & 0x33333333L; - right ^= work; - leftt ^= (work << 2); - work = ((right >> 16) ^ leftt) & 0x0000ffffL; - leftt ^= work; - right ^= (work << 16); - work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; - leftt ^= work; - right ^= (work << 4); - *block++ = right; - *block = leftt; - return; - } + right = (right << 31) | (right >> 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = (leftt << 31) | (leftt >> 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + *block++ = right; + *block = leftt; + return; + } /* Validation sets: * diff --git a/crypto/init.c b/crypto/init.c index f131c42306..c30156405a 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -37,33 +37,14 @@ /* #define DEBUG_GNUTLS */ /* - * If GNUTLS is built against GCrypt then - * - * - 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.... - * - * When gcrypt >= 1.6.0 we must not initialize gcrypt threading - * because gcrypt will do that itself. - * - * So we need to init gcrypt threading if + * We need to init gcrypt threading if * * - gcrypt < 1.6.0 - * AND - * - gnutls < 2.12 - * OR - * - gnutls is disabled * */ #if (defined(CONFIG_GCRYPT) && \ - (!defined(CONFIG_GNUTLS) || \ - (LIBGNUTLS_VERSION_NUMBER < 0x020c00)) && \ - (!defined(GCRYPT_VERSION_NUMBER) || \ - (GCRYPT_VERSION_NUMBER < 0x010600))) + (GCRYPT_VERSION_NUMBER < 0x010600)) #define QCRYPTO_INIT_GCRYPT_THREADS #else #undef QCRYPTO_INIT_GCRYPT_THREADS diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 98ee0424e5..d6ab4a9862 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -72,14 +72,6 @@ qcrypto_tls_creds_check_cert_times(gnutls_x509_crt_t cert, } -#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, @@ -130,7 +122,6 @@ qcrypto_tls_creds_check_cert_basic_constraints(QCryptoTLSCredsX509 *creds, return 0; } -#endif static int @@ -299,14 +290,12 @@ qcrypto_tls_creds_check_cert(QCryptoTLSCredsX509 *creds, 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, @@ -615,7 +604,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, } if (cert != NULL && key != NULL) { -#if LIBGNUTLS_VERSION_NUMBER >= 0x030111 char *password = NULL; if (creds->passwordid) { password = qcrypto_secret_lookup_as_utf8(creds->passwordid, @@ -630,15 +618,6 @@ qcrypto_tls_creds_x509_load(QCryptoTLSCredsX509 *creds, password, 0); g_free(password); -#else /* LIBGNUTLS_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 if (ret < 0) { error_setg(errp, "Cannot load certificate '%s' & key '%s': %s", cert, key, gnutls_strerror(ret)); diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 66a6fbe19c..c3a920dfe8 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -24,7 +24,7 @@ #include "crypto/tlscredspsk.h" #include "crypto/tlscredsx509.h" #include "qapi/error.h" -#include "qemu/acl.h" +#include "authz/base.h" #include "trace.h" #ifdef CONFIG_GNUTLS @@ -37,7 +37,7 @@ struct QCryptoTLSSession { QCryptoTLSCreds *creds; gnutls_session_t handle; char *hostname; - char *aclname; + char *authzid; bool handshakeComplete; QCryptoTLSSessionWriteFunc writeFunc; QCryptoTLSSessionReadFunc readFunc; @@ -56,7 +56,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) gnutls_deinit(session->handle); g_free(session->hostname); g_free(session->peername); - g_free(session->aclname); + g_free(session->authzid); object_unref(OBJECT(session->creds)); g_free(session); } @@ -90,18 +90,12 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) } #define TLS_PRIORITY_ADDITIONAL_ANON "+ANON-DH" - -#if GNUTLS_VERSION_MAJOR >= 3 -#define TLS_ECDHE_PSK "+ECDHE-PSK:" -#else -#define TLS_ECDHE_PSK "" -#endif -#define TLS_PRIORITY_ADDITIONAL_PSK TLS_ECDHE_PSK "+DHE-PSK:+PSK" +#define TLS_PRIORITY_ADDITIONAL_PSK "+ECDHE-PSK:+DHE-PSK:+PSK" QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds, const char *hostname, - const char *aclname, + const char *authzid, QCryptoTLSCredsEndpoint endpoint, Error **errp) { @@ -111,13 +105,13 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, session = g_new0(QCryptoTLSSession, 1); trace_qcrypto_tls_session_new( session, creds, hostname ? hostname : "", - aclname ? aclname : "", endpoint); + authzid ? authzid : "", endpoint); if (hostname) { session->hostname = g_strdup(hostname); } - if (aclname) { - session->aclname = g_strdup(aclname); + if (authzid) { + session->authzid = g_strdup(authzid); } session->creds = creds; object_ref(OBJECT(creds)); @@ -268,6 +262,7 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, unsigned int nCerts, i; time_t now; gnutls_x509_crt_t cert = NULL; + Error *err = NULL; now = time(NULL); if (now == ((time_t)-1)) { @@ -355,19 +350,17 @@ qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, 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); + if (session->authzid) { + bool allow; + + allow = qauthz_is_allowed_by_id(session->authzid, + session->peername, &err); + if (err) { + error_propagate(errp, err); goto error; } - - allow = qemu_acl_party_is_allowed(acl, session->peername); - if (!allow) { - error_setg(errp, "TLS x509 ACL check for %s is denied", + error_setg(errp, "TLS x509 authz check for %s is denied", session->peername); goto error; } @@ -479,6 +472,9 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, case GNUTLS_E_INTERRUPTED: errno = EINTR; break; + case GNUTLS_E_PREMATURE_TERMINATION: + errno = ECONNABORTED; + break; default: errno = EIO; break; @@ -558,7 +554,7 @@ qcrypto_tls_session_get_peer_name(QCryptoTLSSession *session) QCryptoTLSSession * qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, const char *hostname G_GNUC_UNUSED, - const char *aclname G_GNUC_UNUSED, + const char *authzid G_GNUC_UNUSED, QCryptoTLSCredsEndpoint endpoint G_GNUC_UNUSED, Error **errp) { diff --git a/crypto/trace-events b/crypto/trace-events index 597389b73c..9e594d30e8 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -1,16 +1,16 @@ # See docs/devel/tracing.txt for syntax documentation. -# crypto/tlscreds.c +# 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 +# tlscredsanon.c qcrypto_tls_creds_anon_load(void *creds, const char *dir) "TLS creds anon load creds=%p dir=%s" -# crypto/tlscredspsk.c +# tlscredspsk.c qcrypto_tls_creds_psk_load(void *creds, const char *dir) "TLS creds psk load creds=%p dir=%s" -# crypto/tlscredsx509.c +# 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" @@ -18,6 +18,6 @@ qcrypto_tls_creds_x509_check_key_purpose(void *creds, const char *file, int stat 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" +# tlssession.c +qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d" qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s" diff --git a/crypto/xts.c b/crypto/xts.c index 95212341f6..4277ad40de 100644 --- a/crypto/xts.c +++ b/crypto/xts.c @@ -24,52 +24,75 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "crypto/xts.h" -static void xts_mult_x(uint8_t *I) -{ - int x; - uint8_t t, tt; +typedef union { + uint8_t b[XTS_BLOCK_SIZE]; + uint64_t u[2]; +} xts_uint128; - 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; +static inline void xts_uint128_xor(xts_uint128 *D, + const xts_uint128 *S1, + const xts_uint128 *S2) +{ + D->u[0] = S1->u[0] ^ S2->u[0]; + D->u[1] = S1->u[1] ^ S2->u[1]; +} + +static inline void xts_uint128_cpu_to_les(xts_uint128 *v) +{ + cpu_to_le64s(&v->u[0]); + cpu_to_le64s(&v->u[1]); +} + +static inline void xts_uint128_le_to_cpus(xts_uint128 *v) +{ + le64_to_cpus(&v->u[0]); + le64_to_cpus(&v->u[1]); +} + +static void xts_mult_x(xts_uint128 *I) +{ + uint64_t tt; + + xts_uint128_le_to_cpus(I); + + tt = I->u[0] >> 63; + I->u[0] <<= 1; + + if (I->u[1] >> 63) { + I->u[0] ^= 0x87; } + I->u[1] <<= 1; + I->u[1] |= tt; + + xts_uint128_cpu_to_les(I); } /** - * xts_tweak_uncrypt: + * xts_tweak_encdec: * @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 + * @src: buffer providing the input text of XTS_BLOCK_SIZE bytes + * @dst: buffer to output the output text of XTS_BLOCK_SIZE bytes * @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes * - * Decrypt data with a tweak + * Encrypt/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) +static inline void xts_tweak_encdec(const void *ctx, + xts_cipher_func *func, + const xts_uint128 *src, + xts_uint128 *dst, + xts_uint128 *iv) { - unsigned long x; - /* tweak encrypt block i */ - for (x = 0; x < XTS_BLOCK_SIZE; x++) { - dst[x] = src[x] ^ iv[x]; - } + xts_uint128_xor(dst, src, iv); - func(ctx, XTS_BLOCK_SIZE, dst, dst); + func(ctx, XTS_BLOCK_SIZE, dst->b, dst->b); - for (x = 0; x < XTS_BLOCK_SIZE; x++) { - dst[x] = dst[x] ^ iv[x]; - } + xts_uint128_xor(dst, dst, iv); /* LFSR the tweak */ xts_mult_x(iv); @@ -85,7 +108,7 @@ void xts_decrypt(const void *datactx, uint8_t *dst, const uint8_t *src) { - uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE]; + xts_uint128 PP, CC, T; unsigned long i, m, mo, lim; /* get number of blocks */ @@ -102,72 +125,53 @@ void xts_decrypt(const void *datactx, } /* encrypt the iv */ - encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); + encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv); - for (i = 0; i < lim; i++) { - xts_tweak_decrypt(datactx, decfunc, src, dst, T); + if (QEMU_PTR_IS_ALIGNED(src, sizeof(uint64_t)) && + QEMU_PTR_IS_ALIGNED(dst, sizeof(uint64_t))) { + xts_uint128 *S = (xts_uint128 *)src; + xts_uint128 *D = (xts_uint128 *)dst; + for (i = 0; i < lim; i++, S++, D++) { + xts_tweak_encdec(datactx, decfunc, S, D, &T); + } + } else { + xts_uint128 D; - src += XTS_BLOCK_SIZE; - dst += XTS_BLOCK_SIZE; + for (i = 0; i < lim; i++) { + memcpy(&D, src, XTS_BLOCK_SIZE); + xts_tweak_encdec(datactx, decfunc, &D, &D, &T); + memcpy(dst, &D, XTS_BLOCK_SIZE); + 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); + xts_uint128 S, D; + memcpy(&CC, &T, XTS_BLOCK_SIZE); + xts_mult_x(&CC); /* PP = tweak decrypt block m-1 */ - xts_tweak_decrypt(datactx, decfunc, src, PP, CC); + memcpy(&S, src, XTS_BLOCK_SIZE); + xts_tweak_encdec(datactx, decfunc, &S, &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]; + CC.b[i] = src[XTS_BLOCK_SIZE + i]; + dst[XTS_BLOCK_SIZE + i] = PP.b[i]; } for (; i < XTS_BLOCK_SIZE; i++) { - CC[i] = PP[i]; + CC.b[i] = PP.b[i]; } /* Pm-1 = Tweak uncrypt CC */ - xts_tweak_decrypt(datactx, decfunc, CC, dst, T); + xts_tweak_encdec(datactx, decfunc, &CC, &D, &T); + memcpy(dst, &D, XTS_BLOCK_SIZE); } /* 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); + decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T.b); } @@ -180,7 +184,7 @@ void xts_encrypt(const void *datactx, uint8_t *dst, const uint8_t *src) { - uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE]; + xts_uint128 PP, CC, T; unsigned long i, m, mo, lim; /* get number of blocks */ @@ -197,34 +201,50 @@ void xts_encrypt(const void *datactx, } /* encrypt the iv */ - encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv); + encfunc(tweakctx, XTS_BLOCK_SIZE, T.b, iv); - for (i = 0; i < lim; i++) { - xts_tweak_encrypt(datactx, encfunc, src, dst, T); + if (QEMU_PTR_IS_ALIGNED(src, sizeof(uint64_t)) && + QEMU_PTR_IS_ALIGNED(dst, sizeof(uint64_t))) { + xts_uint128 *S = (xts_uint128 *)src; + xts_uint128 *D = (xts_uint128 *)dst; + for (i = 0; i < lim; i++, S++, D++) { + xts_tweak_encdec(datactx, encfunc, S, D, &T); + } + } else { + xts_uint128 D; - dst += XTS_BLOCK_SIZE; - src += XTS_BLOCK_SIZE; + for (i = 0; i < lim; i++) { + memcpy(&D, src, XTS_BLOCK_SIZE); + xts_tweak_encdec(datactx, encfunc, &D, &D, &T); + memcpy(dst, &D, XTS_BLOCK_SIZE); + + dst += XTS_BLOCK_SIZE; + src += XTS_BLOCK_SIZE; + } } /* if length is not a multiple of XTS_BLOCK_SIZE then */ if (mo > 0) { + xts_uint128 S, D; /* CC = tweak encrypt block m-1 */ - xts_tweak_encrypt(datactx, encfunc, src, CC, T); + memcpy(&S, src, XTS_BLOCK_SIZE); + xts_tweak_encdec(datactx, encfunc, &S, &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]; + PP.b[i] = src[XTS_BLOCK_SIZE + i]; + dst[XTS_BLOCK_SIZE + i] = CC.b[i]; } for (; i < XTS_BLOCK_SIZE; i++) { - PP[i] = CC[i]; + PP.b[i] = CC.b[i]; } /* Cm-1 = Tweak encrypt PP */ - xts_tweak_encrypt(datactx, encfunc, PP, dst, T); + xts_tweak_encdec(datactx, encfunc, &PP, &D, &T); + memcpy(dst, &D, XTS_BLOCK_SIZE); } /* Decrypt the iv back */ - decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T); + decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T.b); } diff --git a/default-configs/aarch64-softmmu.mak b/default-configs/aarch64-softmmu.mak index 6f790f061a..4ea9add003 100644 --- a/default-configs/aarch64-softmmu.mak +++ b/default-configs/aarch64-softmmu.mak @@ -8,4 +8,5 @@ CONFIG_DDC=y CONFIG_DPCD=y CONFIG_XLNX_ZYNQMP=y CONFIG_XLNX_ZYNQMP_ARM=y +CONFIG_XLNX_VERSAL=y CONFIG_ARM_SMMUV3=y diff --git a/default-configs/alpha-softmmu.mak b/default-configs/alpha-softmmu.mak index bbe361f01a..d186fe8e9b 100644 --- a/default-configs/alpha-softmmu.mak +++ b/default-configs/alpha-softmmu.mak @@ -1,21 +1,10 @@ # Default configuration for alpha-softmmu -include pci.mak -include usb.mak -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_I82374=y -CONFIG_I8254=y -CONFIG_I8257=y -CONFIG_PARALLEL=y -CONFIG_PARALLEL_ISA=y -CONFIG_FDC=y -CONFIG_PCKBD=y -CONFIG_VGA_CIRRUS=y -CONFIG_IDE_CORE=y -CONFIG_IDE_QDEV=y -CONFIG_VMWARE_VGA=y -CONFIG_IDE_CMD646=y -CONFIG_I8259=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n +#CONFIG_TEST_DEVICES=n + +# Boards: +# +CONFIG_DP264=y diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak index 834d45cfaf..613d19a06d 100644 --- a/default-configs/arm-softmmu.mak +++ b/default-configs/arm-softmmu.mak @@ -1,14 +1,12 @@ # Default configuration for arm-softmmu -include pci.mak -include usb.mak +CONFIG_PCI=y +CONFIG_PCI_DEVICES=y +CONFIG_PCI_TESTDEV=y CONFIG_VGA=y CONFIG_NAND=y CONFIG_ECC=y CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_PTIMER=y -CONFIG_SD=y CONFIG_MAX7310=y CONFIG_WM8750=y CONFIG_TWL92230=y @@ -26,7 +24,6 @@ CONFIG_DDC=y CONFIG_SII9022=y CONFIG_ADS7846=y CONFIG_MAX111X=y -CONFIG_SSI=y CONFIG_SSI_SD=y CONFIG_SSI_M25P80=y CONFIG_LAN9118=y @@ -38,7 +35,6 @@ CONFIG_DS1338=y CONFIG_PFLASH_CFI01=y CONFIG_PFLASH_CFI02=y CONFIG_MICRODRIVE=y -CONFIG_USB=y CONFIG_USB_MUSB=y CONFIG_USB_EHCI_SYSBUS=y CONFIG_PLATFORM_BUS=y @@ -52,7 +48,6 @@ CONFIG_ARM_V7M=y CONFIG_NETDUINO2=y CONFIG_ARM_GIC=y -CONFIG_ARM_GIC_KVM=$(CONFIG_KVM) CONFIG_ARM_TIMER=y CONFIG_ARM_MPTIMER=y CONFIG_A9_GTIMER=y @@ -72,7 +67,6 @@ CONFIG_CADENCE=y CONFIG_XGMAC=y CONFIG_EXYNOS4=y CONFIG_PXA2XX=y -CONFIG_I2C=y CONFIG_BITBANG_I2C=y CONFIG_FRAMEBUFFER=y CONFIG_XILINX_SPIPS=y @@ -90,6 +84,7 @@ CONFIG_TUSB6010=y CONFIG_IMX=y CONFIG_MAINSTONE=y CONFIG_MPS2=y +CONFIG_MUSCA=y CONFIG_NSERIES=y CONFIG_RASPI=y CONFIG_REALVIEW=y @@ -101,25 +96,32 @@ CONFIG_STM32F2XX_SYSCFG=y CONFIG_STM32F2XX_ADC=y CONFIG_STM32F2XX_SPI=y CONFIG_STM32F205_SOC=y +CONFIG_NRF51_SOC=y CONFIG_CMSDK_APB_TIMER=y +CONFIG_CMSDK_APB_DUALTIMER=y CONFIG_CMSDK_APB_UART=y +CONFIG_CMSDK_APB_WATCHDOG=y CONFIG_MPS2_FPGAIO=y CONFIG_MPS2_SCC=y CONFIG_TZ_MPC=y +CONFIG_TZ_MSC=y CONFIG_TZ_PPC=y -CONFIG_IOTKIT=y +CONFIG_ARMSSE=y CONFIG_IOTKIT_SECCTL=y +CONFIG_IOTKIT_SYSCTL=y +CONFIG_IOTKIT_SYSINFO=y +CONFIG_ARMSSE_CPUID=y +CONFIG_ARMSSE_MHU=y CONFIG_VERSATILE=y CONFIG_VERSATILE_PCI=y CONFIG_VERSATILE_I2C=y -CONFIG_PCI_GENERIC=y -CONFIG_VFIO_XGMAC=y -CONFIG_VFIO_AMD_XGBE=y +CONFIG_PCI_EXPRESS=y +CONFIG_PCI_EXPRESS_GENERIC_BRIDGE=y CONFIG_SDHCI=y CONFIG_INTEGRATOR=y @@ -133,6 +135,7 @@ CONFIG_FSL_IMX6=y CONFIG_FSL_IMX31=y CONFIG_FSL_IMX25=y CONFIG_FSL_IMX7=y +CONFIG_FSL_IMX6UL=y CONFIG_IMX_I2C=y @@ -141,14 +144,19 @@ CONFIG_XIO3130=y CONFIG_IOH3420=y CONFIG_I82801B11=y CONFIG_ACPI=y +CONFIG_ARM_VIRT=y CONFIG_SMBIOS=y CONFIG_ASPEED_SOC=y +CONFIG_SMBUS_EEPROM=y CONFIG_GPIO_KEY=y CONFIG_MSF2=y CONFIG_FW_CFG_DMA=y CONFIG_XILINX_AXI=y -CONFIG_PCI_DESIGNWARE=y +CONFIG_PCI_EXPRESS_DESIGNWARE=y CONFIG_STRONGARM=y CONFIG_HIGHBANK=y CONFIG_MUSICPAL=y + +# for realview and versatilepb +CONFIG_LSI_SCSI_PCI=y diff --git a/default-configs/cris-softmmu.mak b/default-configs/cris-softmmu.mak index d970d50158..5932cf4d06 100644 --- a/default-configs/cris-softmmu.mak +++ b/default-configs/cris-softmmu.mak @@ -1,6 +1,5 @@ # Default configuration for cris-softmmu -CONFIG_ETRAXFS=y -CONFIG_NAND=y -CONFIG_PTIMER=y -CONFIG_PFLASH_CFI02=y +# Boards: +# +CONFIG_AXIS=y diff --git a/default-configs/hppa-softmmu.mak b/default-configs/hppa-softmmu.mak index 4badc0521e..b64c5eb3ff 100644 --- a/default-configs/hppa-softmmu.mak +++ b/default-configs/hppa-softmmu.mak @@ -1,12 +1,9 @@ -include pci.mak -include usb.mak -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_ISA_BUS=y -CONFIG_I8259=y -CONFIG_E1000_PCI=y -CONFIG_IDE_ISA=y -CONFIG_IDE_CMD646=y -# CONFIG_IDE_MMIO=y -CONFIG_VIRTIO_VGA=y -CONFIG_MC146818RTC=y +# Default configuration for hppa-softmmu + +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n + +# Boards: +# +CONFIG_DINO=y diff --git a/default-configs/i386-softmmu.mak b/default-configs/i386-softmmu.mak index 8c7d4a0fa0..ba3fb3ff50 100644 --- a/default-configs/i386-softmmu.mak +++ b/default-configs/i386-softmmu.mak @@ -1,68 +1,27 @@ # Default configuration for i386-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_QXL=$(CONFIG_SPICE) -CONFIG_VGA_ISA=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_VMXNET3_PCI=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_SERIAL_ISA=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_X86_ICH=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y -CONFIG_HPET=y -CONFIG_APPLESMC=y -CONFIG_I8259=y -CONFIG_PFLASH_CFI01=y -CONFIG_TPM_TIS=$(CONFIG_TPM) -CONFIG_TPM_CRB=$(CONFIG_TPM) -CONFIG_MC146818RTC=y -CONFIG_PCI_PIIX=y -CONFIG_WDT_IB700=y -CONFIG_ISA_DEBUG=y -CONFIG_ISA_TESTDEV=y -CONFIG_VMPORT=y -CONFIG_SGA=y -CONFIG_LPC_ICH9=y -CONFIG_PCI_Q35=y -CONFIG_APIC=y -CONFIG_IOAPIC=y -CONFIG_PVPANIC=y -CONFIG_MEM_HOTPLUG=y -CONFIG_NVDIMM=y -CONFIG_ACPI_NVDIMM=y -CONFIG_PCIE_PORT=y -CONFIG_XIO3130=y -CONFIG_IOH3420=y -CONFIG_I82801B11=y -CONFIG_SMBIOS=y -CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) -CONFIG_PXB=y -CONFIG_ACPI_VMGENID=y -CONFIG_FW_CFG_DMA=y -CONFIG_I2C=y -CONFIG_SEV=$(CONFIG_KVM) -CONFIG_VTD=y -CONFIG_AMD_IOMMU=y +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_AMD_IOMMU=n +#CONFIG_APPLESMC=n +#CONFIG_FDC=n +#CONFIG_HPET=n +#CONFIG_HYPERV=n +#CONFIG_ISA_DEBUG=n +#CONFIG_ISA_IPMI_BT=n +#CONFIG_ISA_IPMI_KCS=n +#CONFIG_PCI_DEVICES=n +#CONFIG_PVPANIC=n +#CONFIG_QXL=n +#CONFIG_SEV=n +#CONFIG_SGA=n +#CONFIG_TEST_DEVICES=n +#CONFIG_TPM_CRB=n +#CONFIG_TPM_TIS=n +#CONFIG_VTD=n + +# Boards: +# +CONFIG_ISAPC=y +CONFIG_I440FX=y +CONFIG_Q35=y diff --git a/default-configs/lm32-softmmu.mak b/default-configs/lm32-softmmu.mak index 4889348a10..6d259665d6 100644 --- a/default-configs/lm32-softmmu.mak +++ b/default-configs/lm32-softmmu.mak @@ -1,10 +1,10 @@ # Default configuration for lm32-softmmu +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_MILKYMIST_TMU2=n # disabling it actually causes compile-time failures + +# Boards: +# CONFIG_LM32=y CONFIG_MILKYMIST=y -CONFIG_MILKYMIST_TMU2=$(CONFIG_OPENGL) -CONFIG_FRAMEBUFFER=y -CONFIG_PTIMER=y -CONFIG_PFLASH_CFI01=y -CONFIG_PFLASH_CFI02=y -CONFIG_SD=y diff --git a/default-configs/m68k-softmmu.mak b/default-configs/m68k-softmmu.mak index 60f7cdfbf2..e17495e2a0 100644 --- a/default-configs/m68k-softmmu.mak +++ b/default-configs/m68k-softmmu.mak @@ -1,4 +1,6 @@ # Default configuration for m68k-softmmu -CONFIG_COLDFIRE=y -CONFIG_PTIMER=y +# Boards: +# +CONFIG_AN5206=y +CONFIG_MCF5208=y diff --git a/default-configs/microblaze-softmmu.mak b/default-configs/microblaze-softmmu.mak index 7fca8e4c99..db8c6e4bba 100644 --- a/default-configs/microblaze-softmmu.mak +++ b/default-configs/microblaze-softmmu.mak @@ -1,12 +1,7 @@ # Default configuration for microblaze-softmmu -CONFIG_PTIMER=y -CONFIG_PFLASH_CFI01=y -CONFIG_SERIAL=y -CONFIG_XILINX=y -CONFIG_XILINX_AXI=y -CONFIG_XILINX_SPI=y -CONFIG_XILINX_ETHLITE=y -CONFIG_SSI=y -CONFIG_SSI_M25P80=y -CONFIG_XLNX_ZYNQMP=y +# Boards: +# +CONFIG_PETALOGIX_S3ADSP1800=y +CONFIG_PETALOGIX_ML605=y +CONFIG_XLNX_ZYNQMP_PMU=y diff --git a/default-configs/mips-softmmu-common.mak b/default-configs/mips-softmmu-common.mak index fae2347ee7..8e54a74b7a 100644 --- a/default-configs/mips-softmmu-common.mak +++ b/default-configs/mips-softmmu-common.mak @@ -1,10 +1,9 @@ # Common mips*-softmmu CONFIG defines -include pci.mak -include sound.mak -include usb.mak +CONFIG_ISA_BUS=y +CONFIG_PCI=y +CONFIG_PCI_DEVICES=y CONFIG_ESP=y -CONFIG_SCSI=y CONFIG_VGA_ISA=y CONFIG_VGA_ISA_MM=y CONFIG_VGA_CIRRUS=y @@ -31,8 +30,13 @@ 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 -CONFIG_I2C=y +CONFIG_R4K=y +CONFIG_MALTA=y +CONFIG_PCNET_PCI=y +CONFIG_MIPSSIM=y +CONFIG_ACPI_SMBUS=y +CONFIG_SMBUS_EEPROM=y +CONFIG_TEST_DEVICES=y diff --git a/default-configs/mips64el-softmmu.mak b/default-configs/mips64el-softmmu.mak index c2ae313f47..a67c9517a2 100644 --- a/default-configs/mips64el-softmmu.mak +++ b/default-configs/mips64el-softmmu.mak @@ -6,10 +6,14 @@ CONFIG_RC4030=y CONFIG_DP8393X=y CONFIG_DS1225Y=y CONFIG_FULONG=y +CONFIG_ATI_VGA=y +CONFIG_RTL8139_PCI=y CONFIG_JAZZ=y CONFIG_G364FB=y CONFIG_JAZZ_LED=y CONFIG_VT82C686=y +CONFIG_AHCI=y CONFIG_MIPS_BOSTON=y CONFIG_FITLOADER=y -CONFIG_PCI_XILINX=y +CONFIG_PCI_EXPRESS=y +CONFIG_PCI_EXPRESS_XILINX=y diff --git a/default-configs/moxie-softmmu.mak b/default-configs/moxie-softmmu.mak index e00d099994..bd50da3c58 100644 --- a/default-configs/moxie-softmmu.mak +++ b/default-configs/moxie-softmmu.mak @@ -1,7 +1,5 @@ # Default configuration for moxie-softmmu -CONFIG_ISA_BUS=y -CONFIG_MC146818RTC=y -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_VGA=y +# Boards: +# +CONFIG_MOXIESIM=y diff --git a/default-configs/nios2-softmmu.mak b/default-configs/nios2-softmmu.mak index 74dc70caae..e11dc54960 100644 --- a/default-configs/nios2-softmmu.mak +++ b/default-configs/nios2-softmmu.mak @@ -1,6 +1,5 @@ # Default configuration for nios2-softmmu -CONFIG_NIOS2=y -CONFIG_SERIAL=y -CONFIG_PTIMER=y -CONFIG_ALTERA_TIMER=y +# Boards: +# +CONFIG_NIOS2_10M50=y diff --git a/default-configs/or1k-softmmu.mak b/default-configs/or1k-softmmu.mak index 6f5824fd48..168101c39a 100644 --- a/default-configs/or1k-softmmu.mak +++ b/default-configs/or1k-softmmu.mak @@ -1,5 +1,5 @@ # Default configuration for or1k-softmmu -CONFIG_SERIAL=y -CONFIG_OPENCORES_ETH=y -CONFIG_OMPIC=y +# Boards: +# +CONFIG_OR1K_SIM=y diff --git a/default-configs/pci.mak b/default-configs/pci.mak deleted file mode 100644 index de53d20ac6..0000000000 --- a/default-configs/pci.mak +++ /dev/null @@ -1,48 +0,0 @@ -CONFIG_PCI=y -# For now, CONFIG_IDE_CORE requires ISA, so we enable it here -CONFIG_ISA_BUS=y -CONFIG_VIRTIO_PCI=y -include virtio.mak -CONFIG_USB_UHCI=y -CONFIG_USB_OHCI=y -CONFIG_USB_EHCI=y -CONFIG_USB_XHCI=y -CONFIG_USB_XHCI_NEC=y -CONFIG_NE2000_PCI=y -CONFIG_EEPRO100_PCI=y -CONFIG_PCNET_PCI=y -CONFIG_PCNET_COMMON=y -CONFIG_AC97=y -CONFIG_HDA=y -CONFIG_ES1370=y -CONFIG_SCSI=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_E1000E_PCI=y -CONFIG_IDE_CORE=y -CONFIG_IDE_QDEV=y -CONFIG_IDE_PCI=y -CONFIG_AHCI=y -CONFIG_ESP=y -CONFIG_ESP_PCI=y -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_SERIAL_PCI=y -CONFIG_CAN_BUS=y -CONFIG_CAN_SJA1000=y -CONFIG_CAN_PCI=y -CONFIG_IPACK=y -CONFIG_WDT_IB6300ESB=y -CONFIG_PCI_TESTDEV=y -CONFIG_NVME_PCI=y -CONFIG_SD=y -CONFIG_SDHCI=y -CONFIG_EDU=y -CONFIG_VGA=y -CONFIG_VGA_PCI=y -CONFIG_IVSHMEM_DEVICE=$(CONFIG_IVSHMEM) -CONFIG_ROCKER=y diff --git a/default-configs/ppc-softmmu.mak b/default-configs/ppc-softmmu.mak index 3181bbf163..bf86128a0c 100644 --- a/default-configs/ppc-softmmu.mak +++ b/default-configs/ppc-softmmu.mak @@ -1,70 +1,19 @@ # Default configuration for ppc-softmmu -include pci.mak -include sound.mak -include usb.mak - # For embedded PPCs: -CONFIG_PPC4XX=y -CONFIG_M48T59=y -CONFIG_SERIAL=y -CONFIG_I8257=y -CONFIG_OPENPIC=y -CONFIG_PPCE500_PCI=y -CONFIG_PFLASH_CFI01=y -CONFIG_PFLASH_CFI02=y -CONFIG_PTIMER=y -CONFIG_I8259=y -CONFIG_XILINX=y -CONFIG_XILINX_ETHLITE=y +CONFIG_MPC_I2C=y +CONFIG_DS1338=y CONFIG_E500=y -CONFIG_OPENPIC_KVM=$(call land,$(CONFIG_E500),$(CONFIG_KVM)) -CONFIG_PLATFORM_BUS=y -CONFIG_ETSEC=y +CONFIG_PPC405=y +CONFIG_PPC440=y +CONFIG_VIRTEX=y + # For Sam460ex CONFIG_SAM460EX=y -CONFIG_USB_EHCI_SYSBUS=y -CONFIG_SM501=y -CONFIG_DDC=y -CONFIG_IDE_SII3112=y -CONFIG_I2C=y -CONFIG_BITBANG_I2C=y -CONFIG_M41T80=y -CONFIG_VGA_CIRRUS=y # For Macs -CONFIG_MAC=y -CONFIG_ESCC=y -CONFIG_MACIO=y -CONFIG_MACIO_GPIO=y -CONFIG_SUNGEM=y -CONFIG_MOS6522=y -CONFIG_CUDA=y -CONFIG_ADB=y -CONFIG_MAC_NVRAM=y -CONFIG_MAC_DBDMA=y -CONFIG_MAC_PMU=y -CONFIG_HEATHROW_PIC=y -CONFIG_GRACKLE_PCI=y -CONFIG_UNIN_PCI=y -CONFIG_DEC_PCI=y -CONFIG_IDE_MACIO=y +CONFIG_MAC_OLDWORLD=y +CONFIG_MAC_NEWWORLD=y # For PReP CONFIG_PREP=y -CONFIG_PREP_PCI=y -CONFIG_SERIAL_ISA=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_RS6000_MC=y -CONFIG_PARALLEL=y -CONFIG_I82374=y -CONFIG_I82378=y -CONFIG_I8254=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_NE2000_ISA=y -CONFIG_PC87312=y -CONFIG_PCSPK=y -CONFIG_IDE_ISA=y -CONFIG_CS4231A=y diff --git a/default-configs/ppc64-softmmu.mak b/default-configs/ppc64-softmmu.mak index b94af6c7c6..cca52665d9 100644 --- a/default-configs/ppc64-softmmu.mak +++ b/default-configs/ppc64-softmmu.mak @@ -5,15 +5,6 @@ include ppc-softmmu.mak # For PowerNV CONFIG_POWERNV=y -CONFIG_IPMI=y -CONFIG_IPMI_LOCAL=y -CONFIG_IPMI_EXTERN=y -CONFIG_ISA_IPMI_BT=y # For pSeries CONFIG_PSERIES=y -CONFIG_VIRTIO_VGA=y -CONFIG_XICS=$(CONFIG_PSERIES) -CONFIG_XICS_SPAPR=$(CONFIG_PSERIES) -CONFIG_XICS_KVM=$(call land,$(CONFIG_PSERIES),$(CONFIG_KVM)) -CONFIG_MEM_HOTPLUG=y diff --git a/default-configs/ppcemb-softmmu.mak b/default-configs/ppcemb-softmmu.mak deleted file mode 100644 index ac44f150c6..0000000000 --- a/default-configs/ppcemb-softmmu.mak +++ /dev/null @@ -1,23 +0,0 @@ -# Default configuration for ppcemb-softmmu - -include pci.mak -include sound.mak -include usb.mak -CONFIG_PPC4XX=y -CONFIG_M48T59=y -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_I8257=y -CONFIG_OPENPIC=y -CONFIG_PFLASH_CFI01=y -CONFIG_PFLASH_CFI02=y -CONFIG_PTIMER=y -CONFIG_I8259=y -CONFIG_XILINX=y -CONFIG_XILINX_ETHLITE=y -CONFIG_USB_EHCI_SYSBUS=y -CONFIG_SM501=y -CONFIG_DDC=y -CONFIG_IDE_SII3112=y -CONFIG_I2C=y -CONFIG_BITBANG_I2C=y diff --git a/default-configs/riscv32-softmmu.mak b/default-configs/riscv32-softmmu.mak index 7937c69e22..1ae077ed87 100644 --- a/default-configs/riscv32-softmmu.mak +++ b/default-configs/riscv32-softmmu.mak @@ -1,7 +1,12 @@ -# Default configuration for riscv-softmmu +# Default configuration for riscv32-softmmu -CONFIG_SERIAL=y -CONFIG_VIRTIO_MMIO=y -include virtio.mak +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n -CONFIG_CADENCE=y +# Boards: +# +CONFIG_SPIKE=y +CONFIG_SIFIVE_E=y +CONFIG_SIFIVE_U=y +CONFIG_RISCV_VIRT=y diff --git a/default-configs/riscv64-softmmu.mak b/default-configs/riscv64-softmmu.mak index 7937c69e22..235c6f473f 100644 --- a/default-configs/riscv64-softmmu.mak +++ b/default-configs/riscv64-softmmu.mak @@ -1,7 +1,3 @@ -# Default configuration for riscv-softmmu +# Default configuration for riscv64-softmmu -CONFIG_SERIAL=y -CONFIG_VIRTIO_MMIO=y -include virtio.mak - -CONFIG_CADENCE=y +include riscv32-softmmu.mak diff --git a/default-configs/s390x-softmmu.mak b/default-configs/s390x-softmmu.mak index d6b67d50f0..f2287a133f 100644 --- a/default-configs/s390x-softmmu.mak +++ b/default-configs/s390x-softmmu.mak @@ -1,9 +1,13 @@ -CONFIG_PCI=y -CONFIG_VIRTIO_PCI=$(CONFIG_PCI) -include virtio.mak -CONFIG_SCLPCONSOLE=y -CONFIG_TERMINAL3270=y -CONFIG_S390_FLIC=y -CONFIG_S390_FLIC_KVM=$(CONFIG_KVM) -CONFIG_VFIO_CCW=$(CONFIG_LINUX) -CONFIG_WDT_DIAG288=y +# Default configuration for s390x-softmmu + +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_TERMINAL3270=n +#CONFIG_VFIO_AP=n +#CONFIG_VFIO_CCW=n +#CONFIG_VIRTIO_PCI=n +#CONFIG_WDT_DIAG288=n + +# Boards: +# +CONFIG_S390_CCW_VIRTIO=y diff --git a/default-configs/sh4-softmmu.mak b/default-configs/sh4-softmmu.mak index caeccd55be..565e8b0b5d 100644 --- a/default-configs/sh4-softmmu.mak +++ b/default-configs/sh4-softmmu.mak @@ -1,21 +1,11 @@ -# Default configuration for sh4-softmmu +# Default configuration for sh4eb-softmmu -include pci.mak -include usb.mak -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_PTIMER=y -CONFIG_PFLASH_CFI02=y -CONFIG_SH4=y -CONFIG_IDE_MMIO=y -CONFIG_SM501=y -CONFIG_I2C=y -CONFIG_DDC=y -CONFIG_ISA_TESTDEV=y -CONFIG_I82378=y -CONFIG_I8259=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_I82374=y -CONFIG_I8257=y -CONFIG_MC146818RTC=y +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n +#CONFIG_TEST_DEVICES=n + +# Boards: +# +CONFIG_R2D=y +CONFIG_SHIX=y diff --git a/default-configs/sh4eb-softmmu.mak b/default-configs/sh4eb-softmmu.mak index 53b9cd7b5a..522a7a50fa 100644 --- a/default-configs/sh4eb-softmmu.mak +++ b/default-configs/sh4eb-softmmu.mak @@ -1,21 +1,3 @@ # Default configuration for sh4eb-softmmu -include pci.mak -include usb.mak -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_PTIMER=y -CONFIG_PFLASH_CFI02=y -CONFIG_SH4=y -CONFIG_IDE_MMIO=y -CONFIG_SM501=y -CONFIG_I2C=y -CONFIG_DDC=y -CONFIG_ISA_TESTDEV=y -CONFIG_I82378=y -CONFIG_I8259=y -CONFIG_I8254=y -CONFIG_PCSPK=y -CONFIG_I82374=y -CONFIG_I8257=y -CONFIG_MC146818RTC=y +include sh4-softmmu.mak diff --git a/default-configs/sound.mak b/default-configs/sound.mak deleted file mode 100644 index 4f22c34b5d..0000000000 --- a/default-configs/sound.mak +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_SB16=y -CONFIG_ADLIB=y -CONFIG_GUS=y -CONFIG_CS4231A=y diff --git a/default-configs/sparc-softmmu.mak b/default-configs/sparc-softmmu.mak index 12f97eeb20..ee85218115 100644 --- a/default-configs/sparc-softmmu.mak +++ b/default-configs/sparc-softmmu.mak @@ -1,21 +1,11 @@ # Default configuration for sparc-softmmu -CONFIG_ISA_BUS=y -CONFIG_ECC=y -CONFIG_SCSI=y -CONFIG_ESP=y -CONFIG_ESCC=y -CONFIG_M48T59=y -CONFIG_PTIMER=y -CONFIG_FDC=y -CONFIG_EMPTY_SLOT=y -CONFIG_PCNET_COMMON=y -CONFIG_LANCE=y -CONFIG_TCX=y -CONFIG_CG3=y -CONFIG_SLAVIO=y -CONFIG_CS4231=y -CONFIG_GRLIB=y -CONFIG_STP2000=y -CONFIG_ECCMEMCTL=y +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_TCX=n +#CONFIG_CG3=n + +# Boards: +# CONFIG_SUN4M=y +CONFIG_LEON3=y diff --git a/default-configs/sparc64-softmmu.mak b/default-configs/sparc64-softmmu.mak index 52edafe547..e50030a229 100644 --- a/default-configs/sparc64-softmmu.mak +++ b/default-configs/sparc64-softmmu.mak @@ -1,20 +1,12 @@ # Default configuration for sparc64-softmmu -include pci.mak -include usb.mak -CONFIG_M48T59=y -CONFIG_PTIMER=y -CONFIG_SERIAL=y -CONFIG_SERIAL_ISA=y -CONFIG_PARALLEL=y -CONFIG_PCKBD=y -CONFIG_FDC=y -CONFIG_IDE_ISA=y -CONFIG_IDE_CMD646=y -CONFIG_PCI_SABRE=y -CONFIG_SIMBA=y -CONFIG_SUNHME=y -CONFIG_MC146818RTC=y -CONFIG_ISA_TESTDEV=y -CONFIG_EMPTY_SLOT=y -CONFIG_SUN4V_RTC=y +# Uncomment the following lines to disable these optional devices: +# +#CONFIG_PCI_DEVICES=n +#CONFIG_SUNHME=n +#CONFIG_TEST_DEVICES=n + +# Boards: +# +CONFIG_SUN4U=y +CONFIG_NIAGARA=y diff --git a/default-configs/tricore-softmmu.mak b/default-configs/tricore-softmmu.mak index e69de29bb2..c397cff38a 100644 --- a/default-configs/tricore-softmmu.mak +++ b/default-configs/tricore-softmmu.mak @@ -0,0 +1 @@ +CONFIG_TRICORE=y diff --git a/default-configs/unicore32-softmmu.mak b/default-configs/unicore32-softmmu.mak index 5f6c4a8047..0bfce48c6d 100644 --- a/default-configs/unicore32-softmmu.mak +++ b/default-configs/unicore32-softmmu.mak @@ -1,5 +1,5 @@ # Default configuration for unicore32-softmmu -CONFIG_ISA_BUS=y + +# Boards: +# CONFIG_PUV3=y -CONFIG_PTIMER=y -CONFIG_PCKBD=y diff --git a/default-configs/usb.mak b/default-configs/usb.mak deleted file mode 100644 index e42cfeabbe..0000000000 --- a/default-configs/usb.mak +++ /dev/null @@ -1,11 +0,0 @@ -CONFIG_USB=y -CONFIG_USB_TABLET_WACOM=y -CONFIG_USB_STORAGE_BOT=y -CONFIG_USB_STORAGE_UAS=y -CONFIG_USB_STORAGE_MTP=y -CONFIG_SCSI=y -CONFIG_USB_SMARTCARD=y -CONFIG_USB_AUDIO=y -CONFIG_USB_SERIAL=y -CONFIG_USB_NETWORK=y -CONFIG_USB_BLUETOOTH=y diff --git a/default-configs/virtio.mak b/default-configs/virtio.mak deleted file mode 100644 index 1304849018..0000000000 --- a/default-configs/virtio.mak +++ /dev/null @@ -1,14 +0,0 @@ -CONFIG_VHOST_USER_SCSI=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VHOST_USER_BLK=$(call land,$(CONFIG_VHOST_USER),$(CONFIG_LINUX)) -CONFIG_VIRTIO=y -CONFIG_VIRTIO_9P=y -CONFIG_VIRTIO_BALLOON=y -CONFIG_VIRTIO_BLK=y -CONFIG_VIRTIO_CRYPTO=y -CONFIG_VIRTIO_GPU=y -CONFIG_VIRTIO_INPUT=y -CONFIG_VIRTIO_NET=y -CONFIG_VIRTIO_RNG=y -CONFIG_SCSI=y -CONFIG_VIRTIO_SCSI=y -CONFIG_VIRTIO_SERIAL=y diff --git a/default-configs/x86_64-softmmu.mak b/default-configs/x86_64-softmmu.mak index 0390b4303c..64b2ee2960 100644 --- a/default-configs/x86_64-softmmu.mak +++ b/default-configs/x86_64-softmmu.mak @@ -1,68 +1,3 @@ # Default configuration for x86_64-softmmu -include pci.mak -include sound.mak -include usb.mak -CONFIG_QXL=$(CONFIG_SPICE) -CONFIG_VGA_ISA=y -CONFIG_VGA_CIRRUS=y -CONFIG_VMWARE_VGA=y -CONFIG_VMXNET3_PCI=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_SERIAL_ISA=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_X86_ICH=y -CONFIG_ACPI_MEMORY_HOTPLUG=y -CONFIG_ACPI_CPU_HOTPLUG=y -CONFIG_APM=y -CONFIG_I8257=y -CONFIG_IDE_ISA=y -CONFIG_IDE_PIIX=y -CONFIG_NE2000_ISA=y -CONFIG_HPET=y -CONFIG_APPLESMC=y -CONFIG_I8259=y -CONFIG_PFLASH_CFI01=y -CONFIG_TPM_TIS=$(CONFIG_TPM) -CONFIG_TPM_CRB=$(CONFIG_TPM) -CONFIG_MC146818RTC=y -CONFIG_PCI_PIIX=y -CONFIG_WDT_IB700=y -CONFIG_ISA_DEBUG=y -CONFIG_ISA_TESTDEV=y -CONFIG_VMPORT=y -CONFIG_SGA=y -CONFIG_LPC_ICH9=y -CONFIG_PCI_Q35=y -CONFIG_APIC=y -CONFIG_IOAPIC=y -CONFIG_PVPANIC=y -CONFIG_MEM_HOTPLUG=y -CONFIG_NVDIMM=y -CONFIG_ACPI_NVDIMM=y -CONFIG_PCIE_PORT=y -CONFIG_XIO3130=y -CONFIG_IOH3420=y -CONFIG_I82801B11=y -CONFIG_SMBIOS=y -CONFIG_HYPERV_TESTDEV=$(CONFIG_KVM) -CONFIG_PXB=y -CONFIG_ACPI_VMGENID=y -CONFIG_FW_CFG_DMA=y -CONFIG_I2C=y -CONFIG_SEV=$(CONFIG_KVM) -CONFIG_VTD=y -CONFIG_AMD_IOMMU=y +include i386-softmmu.mak diff --git a/default-configs/xtensa-softmmu.mak b/default-configs/xtensa-softmmu.mak index 9d8899cde7..7e4d1cc097 100644 --- a/default-configs/xtensa-softmmu.mak +++ b/default-configs/xtensa-softmmu.mak @@ -1,5 +1,6 @@ # Default configuration for Xtensa -CONFIG_SERIAL=y -CONFIG_OPENCORES_ETH=y -CONFIG_PFLASH_CFI01=y +# Boards: +# +CONFIG_XTENSA_SIM=y +CONFIG_XTENSA_XTFPGA=y diff --git a/default-configs/xtensaeb-softmmu.mak b/default-configs/xtensaeb-softmmu.mak index 9d8899cde7..f7e48c750c 100644 --- a/default-configs/xtensaeb-softmmu.mak +++ b/default-configs/xtensaeb-softmmu.mak @@ -1,5 +1,3 @@ # Default configuration for Xtensa -CONFIG_SERIAL=y -CONFIG_OPENCORES_ETH=y -CONFIG_PFLASH_CFI01=y +include xtensa-softmmu.mak diff --git a/device-hotplug.c b/device-hotplug.c index 23fd6656f1..6153259d71 100644 --- a/device-hotplug.c +++ b/device-hotplug.c @@ -28,6 +28,7 @@ #include "sysemu/block-backend.h" #include "sysemu/blockdev.h" #include "qapi/qmp/qdict.h" +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "sysemu/sysemu.h" @@ -36,6 +37,7 @@ static DriveInfo *add_init_drive(const char *optstr) { + Error *err = NULL; DriveInfo *dinfo; QemuOpts *opts; MachineClass *mc; @@ -45,8 +47,9 @@ static DriveInfo *add_init_drive(const char *optstr) return NULL; mc = MACHINE_GET_CLASS(current_machine); - dinfo = drive_new(opts, mc->block_default_type); - if (!dinfo) { + dinfo = drive_new(opts, mc->block_default_type, &err); + if (err) { + error_report_err(err); qemu_opts_del(opts); return NULL; } @@ -69,10 +72,6 @@ void hmp_drive_add(Monitor *mon, const QDict *qdict) if (!dinfo) { goto err; } - if (dinfo->devaddr) { - monitor_printf(mon, "Parameter addr not supported\n"); - goto err; - } switch (dinfo->type) { case IF_NONE: diff --git a/device_tree.c b/device_tree.c index 6d9c9726f6..f8b46b3c73 100644 --- a/device_tree.c +++ b/device_tree.c @@ -84,6 +84,10 @@ void *load_device_tree(const char *filename_path, int *sizep) filename_path); goto fail; } + if (dt_size > INT_MAX / 2 - 10000) { + error_report("Device tree file '%s' is too large", filename_path); + goto fail; + } /* Expand to 2x size to give enough room for manipulation. */ dt_size += 10000; @@ -91,7 +95,7 @@ void *load_device_tree(const char *filename_path, int *sizep) /* First allocate space in qemu for device tree */ fdt = g_malloc0(dt_size); - dt_file_load_size = load_image(filename_path, fdt); + dt_file_load_size = load_image_size(filename_path, fdt, dt_size); if (dt_file_load_size < 0) { error_report("Unable to open device tree file '%s'", filename_path); diff --git a/disas.c b/disas.c index 5325b7e6be..d9aa713a40 100644 --- a/disas.c +++ b/disas.c @@ -522,8 +522,14 @@ void disas(FILE *out, void *code, unsigned long size) # ifdef _ARCH_PPC64 s.info.cap_mode = CS_MODE_64; # endif -#elif defined(__riscv__) - print_insn = print_insn_riscv; +#elif defined(__riscv) && defined(CONFIG_RISCV_DIS) +#if defined(_ILP32) || (__riscv_xlen == 32) + print_insn = print_insn_riscv32; +#elif defined(_LP64) + print_insn = print_insn_riscv64; +#else +#error unsupported RISC-V ABI +#endif #elif defined(__aarch64__) && defined(CONFIG_ARM_A64_DIS) print_insn = print_insn_arm_a64; s.info.cap_arch = CS_ARCH_ARM64; @@ -588,7 +594,10 @@ static int physical_read_memory(bfd_vma memaddr, bfd_byte *myaddr, int length, struct disassemble_info *info) { - cpu_physical_memory_read(memaddr, myaddr, length); + CPUDebug *s = container_of(info, CPUDebug, info); + + address_space_read(s->cpu->as, memaddr, MEMTXATTRS_UNSPECIFIED, + myaddr, length); return 0; } diff --git a/disas/Makefile.objs b/disas/Makefile.objs index 213be2fab2..3c1cdce026 100644 --- a/disas/Makefile.objs +++ b/disas/Makefile.objs @@ -14,6 +14,7 @@ common-obj-$(CONFIG_I386_DIS) += i386.o common-obj-$(CONFIG_M68K_DIS) += m68k.o common-obj-$(CONFIG_MICROBLAZE_DIS) += microblaze.o common-obj-$(CONFIG_MIPS_DIS) += mips.o +common-obj-$(CONFIG_NANOMIPS_DIS) += nanomips.o common-obj-$(CONFIG_NIOS2_DIS) += nios2.o common-obj-$(CONFIG_MOXIE_DIS) += moxie.o common-obj-$(CONFIG_PPC_DIS) += ppc.o diff --git a/disas/alpha.c b/disas/alpha.c index b7b0ae0d92..a0c9ecd49d 100644 --- a/disas/alpha.c +++ b/disas/alpha.c @@ -672,7 +672,7 @@ extract_ev6hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED) OPCODE is the instruction opcode. MASK is the opcode mask; this is used to tell the disassembler - which bits in the actual opcode must match OPCODE. + which bits in the actual opcode must match OPCODE. OPERANDS is the list of operands. @@ -699,10 +699,10 @@ extract_ev6hwjhint(unsigned insn, int *invalid ATTRIBUTE_UNUSED) And two annotations: EV56 BUT opcodes that are officially introduced as of the ev56, - but with defined results on previous implementations. + but with defined results on previous implementations. EV56 UNA opcodes that were introduced as of the ev56 with - presumably undefined results on previous implementations + presumably undefined results on previous implementations that were not assigned to a particular extension. */ @@ -832,7 +832,7 @@ const struct alpha_opcode alpha_opcodes[] = { { "cmovgt", OPR(0x11,0x66), BASE, ARG_OPR }, { "cmovgt", OPRL(0x11,0x66), BASE, ARG_OPRL }, { "implver", OPRL_(0x11,0x6C)|(31<<21)|(1<<13), - 0xFFFFFFE0, BASE, { RC } }, /* ev56 but */ + 0xFFFFFFE0, BASE, { RC } }, /* ev56 but */ { "mskbl", OPR(0x12,0x02), BASE, ARG_OPR }, { "mskbl", OPRL(0x12,0x02), BASE, ARG_OPRL }, diff --git a/disas/arm.c b/disas/arm.c index dda7b2a943..17ea120b44 100644 --- a/disas/arm.c +++ b/disas/arm.c @@ -1077,7 +1077,7 @@ static const struct opcode32 arm_opcodes[] = %S print Thumb register (bits 3..5 as high number if bit 6 set) %D print Thumb register (bits 0..2 as high number if bit 7 set) %I print bitfield as a signed decimal - (top bit of range being the sign bit) + (top bit of range being the sign bit) %N print Thumb register mask (with LR) %O print Thumb register mask (with PC) %M print Thumb register mask diff --git a/disas/i386.c b/disas/i386.c index a557e678ec..fc03b9f06a 100644 --- a/disas/i386.c +++ b/disas/i386.c @@ -6075,7 +6075,7 @@ OP_EM (int bytemode, int sizeflag) { bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode; used_prefixes |= (prefixes & PREFIX_DATA); - } + } OP_E (bytemode, sizeflag); return; } @@ -6112,7 +6112,7 @@ OP_EMC (int bytemode, int sizeflag) { bytemode = (prefixes & PREFIX_DATA) ? x_mode : q_mode; used_prefixes |= (prefixes & PREFIX_DATA); - } + } OP_E (bytemode, sizeflag); return; } diff --git a/disas/m68k.c b/disas/m68k.c index a687df437c..e544c7137f 100644 --- a/disas/m68k.c +++ b/disas/m68k.c @@ -350,7 +350,7 @@ struct m68k_opcode_alias * all (modes 0-6,7.0-4) ~ alterable memory (modes 2-6,7.0,7.1) - (not 0,1,7.2-4) + (not 0,1,7.2-4) % alterable (modes 0-6,7.0,7.1) (not 7.2-4) ; data (modes 0,2-6,7.0-4) @@ -1623,6 +1623,7 @@ print_insn_arg (const char *d, case 'X': place = '8'; + /* fall through */ case 'Y': case 'Z': case 'W': @@ -1646,7 +1647,7 @@ print_insn_arg (const char *d, case 0x15: name = "%val"; break; case 0x16: name = "%scc"; break; case 0x17: name = "%ac"; break; - case 0x18: name = "%psr"; break; + case 0x18: name = "%psr"; break; case 0x19: name = "%pcsr"; break; case 0x1c: case 0x1d: diff --git a/disas/microblaze.c b/disas/microblaze.c index 598ecbc89d..c23605043a 100644 --- a/disas/microblaze.c +++ b/disas/microblaze.c @@ -176,7 +176,6 @@ enum microblaze_instr_type { #define REG_TLBSX 36869 /* MMU: TLB Search Index reg */ /* alternate names for gen purpose regs */ -#define REG_SP 1 /* stack pointer */ #define REG_ROSDP 2 /* read-only small data pointer */ #define REG_RWSDP 13 /* read-write small data pointer */ diff --git a/disas/nanomips.cpp b/disas/nanomips.cpp new file mode 100644 index 0000000000..c8495b1a19 --- /dev/null +++ b/disas/nanomips.cpp @@ -0,0 +1,22400 @@ +/* + * Source file for nanoMIPS disassembler component of QEMU + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Matthew Fortune + * Copyright (C) 2018 Aleksandar Markovic + * + * 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 . + * + */ + +/* + * Documentation used while implementing this component: + * + * [1] "MIPS® Architecture Base: nanoMIPS32(tm) Instruction Set Technical + * Reference Manual", Revision 01.01, April 27, 2018 + */ + +extern "C" { +#include "qemu/osdep.h" +#include "disas/bfd.h" +} + +#include +#include +#include +#include +#include + +#include "nanomips.h" + +#define IMGASSERTONCE(test) + + +int nanomips_dis(char *buf, + unsigned address, + unsigned short one, + unsigned short two, + unsigned short three) +{ + std::string disasm; + uint16 bits[3] = {one, two, three}; + + NMD::TABLE_ENTRY_TYPE type; + NMD d(address, NMD::ALL_ATTRIBUTES); + int size = d.Disassemble(bits, disasm, type); + + strcpy(buf, disasm.c_str()); + return size; +} + +int print_insn_nanomips(bfd_vma memaddr, struct disassemble_info *info) +{ + int status; + bfd_byte buffer[2]; + uint16_t insn1 = 0, insn2 = 0, insn3 = 0; + char buf[200]; + + info->bytes_per_chunk = 2; + info->display_endian = info->endian; + info->insn_info_valid = 1; + info->branch_delay_insns = 0; + info->data_size = 0; + info->insn_type = dis_nonbranch; + info->target = 0; + info->target2 = 0; + + status = (*info->read_memory_func)(memaddr, buffer, 2, info); + if (status != 0) { + (*info->memory_error_func)(status, memaddr, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_BIG) { + insn1 = bfd_getb16(buffer); + } else { + insn1 = bfd_getl16(buffer); + } + (*info->fprintf_func)(info->stream, "%04x ", insn1); + + /* Handle 32-bit opcodes. */ + if ((insn1 & 0x1000) == 0) { + status = (*info->read_memory_func)(memaddr + 2, buffer, 2, info); + if (status != 0) { + (*info->memory_error_func)(status, memaddr + 2, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_BIG) { + insn2 = bfd_getb16(buffer); + } else { + insn2 = bfd_getl16(buffer); + } + (*info->fprintf_func)(info->stream, "%04x ", insn2); + } else { + (*info->fprintf_func)(info->stream, " "); + } + /* Handle 48-bit opcodes. */ + if ((insn1 >> 10) == 0x18) { + status = (*info->read_memory_func)(memaddr + 4, buffer, 2, info); + if (status != 0) { + (*info->memory_error_func)(status, memaddr + 4, info); + return -1; + } + + if (info->endian == BFD_ENDIAN_BIG) { + insn3 = bfd_getb16(buffer); + } else { + insn3 = bfd_getl16(buffer); + } + (*info->fprintf_func)(info->stream, "%04x ", insn3); + } else { + (*info->fprintf_func)(info->stream, " "); + } + + int length = nanomips_dis(buf, memaddr, insn1, insn2, insn3); + + /* FIXME: Should probably use a hash table on the major opcode here. */ + + (*info->fprintf_func) (info->stream, "%s", buf); + if (length > 0) { + return length / 8; + } + + info->insn_type = dis_noninsn; + + return insn3 ? 6 : insn2 ? 4 : 2; +} + + +namespace img +{ + address addr32(address a) + { + return a; + } + + std::string format(const char *format, ...) + { + char buffer[256]; + va_list args; + va_start(args, format); + int err = vsprintf(buffer, format, args); + if (err < 0) { + perror(buffer); + } + va_end(args); + return buffer; + } + + std::string format(const char *format, + std::string s) + { + char buffer[256]; + + sprintf(buffer, format, s.c_str()); + + return buffer; + } + + std::string format(const char *format, + std::string s1, + std::string s2) + { + char buffer[256]; + + sprintf(buffer, format, s1.c_str(), s2.c_str()); + + return buffer; + } + + std::string format(const char *format, + std::string s1, + std::string s2, + std::string s3) + { + char buffer[256]; + + sprintf(buffer, format, s1.c_str(), s2.c_str(), s3.c_str()); + + return buffer; + } + + std::string format(const char *format, + std::string s1, + std::string s2, + std::string s3, + std::string s4) + { + char buffer[256]; + + sprintf(buffer, format, s1.c_str(), s2.c_str(), s3.c_str(), + s4.c_str()); + + return buffer; + } + + std::string format(const char *format, + std::string s1, + std::string s2, + std::string s3, + std::string s4, + std::string s5) + { + char buffer[256]; + + sprintf(buffer, format, s1.c_str(), s2.c_str(), s3.c_str(), + s4.c_str(), s5.c_str()); + + return buffer; + } + + std::string format(const char *format, + uint64 d, + std::string s2) + { + char buffer[256]; + + sprintf(buffer, format, d, s2.c_str()); + + return buffer; + } + + std::string format(const char *format, + std::string s1, + uint64 d, + std::string s2) + { + char buffer[256]; + + sprintf(buffer, format, s1.c_str(), d, s2.c_str()); + + return buffer; + } + + std::string format(const char *format, + std::string s1, + std::string s2, + uint64 d) + { + char buffer[256]; + + sprintf(buffer, format, s1.c_str(), s2.c_str(), d); + + return buffer; + } + + char as_char(int c) + { + return static_cast(c); + } +}; + + +std::string to_string(img::address a) +{ + char buffer[256]; + sprintf(buffer, "0x%" PRIx64, a); + return buffer; +} + + +uint64 extract_bits(uint64 data, uint32 bit_offset, uint32 bit_size) +{ + return (data << (64 - (bit_size + bit_offset))) >> (64 - bit_size); +} + + +int64 sign_extend(int64 data, int msb) +{ + uint64 shift = 63 - msb; + return (data << shift) >> shift; +} + + +uint64 NMD::renumber_registers(uint64 index, uint64 *register_list, + size_t register_list_size) +{ + if (index < register_list_size) { + return register_list[index]; + } + + throw std::runtime_error(img::format( + "Invalid register mapping index %" PRIu64 + ", size of list = %zu", + index, register_list_size)); +} + + +/* + * NMD::decode_gpr_gpr4() - decoder for 'gpr4' gpr encoding type + * + * Map a 4-bit code to the 5-bit register space according to this pattern: + * + * 1 0 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * | | | | | | | | | | | | | | | | + * | | | | | | | | | | | | | | | | + * | | | | | | | | | | | └---------------┐ + * | | | | | | | | | | └---------------┐ | + * | | | | | | | | | └---------------┐ | | + * | | | | | | | | └---------------┐ | | | + * | | | | | | | | | | | | | | | | + * | | | | | | | | | | | | | | | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * Used in handling following instructions: + * + * - ADDU[4X4] + * - LW[4X4] + * - MOVEP[REV] + * - MUL[4X4] + * - SW[4X4] + */ +uint64 NMD::decode_gpr_gpr4(uint64 d) +{ + static uint64 register_list[] = { 8, 9, 10, 11, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +/* + * NMD::decode_gpr_gpr4_zero() - decoder for 'gpr4.zero' gpr encoding type + * + * Map a 4-bit code to the 5-bit register space according to this pattern: + * + * 1 0 + * 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * | | | | | | | | | | | | | | | | + * | | | | | | | | | | | | └---------------------┐ + * | | | | | | | | | | | └---------------┐ | + * | | | | | | | | | | └---------------┐ | | + * | | | | | | | | | └---------------┐ | | | + * | | | | | | | | └---------------┐ | | | | + * | | | | | | | | | | | | | | | | + * | | | | | | | | | | | | | | | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * This pattern is the same one used for 'gpr4' gpr encoding type, except for + * the input value 3, that is mapped to the output value 0 instead of 11. + * + * Used in handling following instructions: + * + * - MOVE.BALC + * - MOVEP + * - SW[4X4] + */ +uint64 NMD::decode_gpr_gpr4_zero(uint64 d) +{ + static uint64 register_list[] = { 8, 9, 10, 0, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +/* + * NMD::decode_gpr_gpr3() - decoder for 'gpr3' gpr encoding type + * + * Map a 3-bit code to the 5-bit register space according to this pattern: + * + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | | | + * | | | └-----------------------┐ + * | | └-----------------------┐ | + * | └-----------------------┐ | | + * └-----------------------┐ | | | + * | | | | | | | | + * ┌-------┘ | | | | | | | + * | ┌-------┘ | | | | | | + * | | ┌-------┘ | | | | | + * | | | ┌-------┘ | | | | + * | | | | | | | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * Used in handling following instructions: + * + * - ADDIU[R1.SP] + * - ADDIU[R2] + * - ADDU[16] + * - AND[16] + * - ANDI[16] + * - BEQC[16] + * - BEQZC[16] + * - BNEC[16] + * - BNEZC[16] + * - LB[16] + * - LBU[16] + * - LH[16] + * - LHU[16] + * - LI[16] + * - LW[16] + * - LW[GP16] + * - LWXS[16] + * - NOT[16] + * - OR[16] + * - SB[16] + * - SH[16] + * - SLL[16] + * - SRL[16] + * - SUBU[16] + * - SW[16] + * - XOR[16] + */ +uint64 NMD::decode_gpr_gpr3(uint64 d) +{ + static uint64 register_list[] = { 16, 17, 18, 19, 4, 5, 6, 7 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +/* + * NMD::decode_gpr_gpr3_src_store() - decoder for 'gpr3.src.store' gpr encoding + * type + * + * Map a 3-bit code to the 5-bit register space according to this pattern: + * + * 7 6 5 4 3 2 1 0 + * | | | | | | | | + * | | | | | | | └-----------------------┐ + * | | | └-----------------------┐ | + * | | └-----------------------┐ | | + * | └-----------------------┐ | | | + * └-----------------------┐ | | | | + * | | | | | | | | + * ┌-------┘ | | | | | | | + * | ┌-------┘ | | | | | | + * | | ┌-------┘ | | | | | + * | | | | | | | | + * | | | | | | | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * This pattern is the same one used for 'gpr3' gpr encoding type, except for + * the input value 0, that is mapped to the output value 0 instead of 16. + * + * Used in handling following instructions: + * + * - SB[16] + * - SH[16] + * - SW[16] + * - SW[GP16] + */ +uint64 NMD::decode_gpr_gpr3_src_store(uint64 d) +{ + static uint64 register_list[] = { 0, 17, 18, 19, 4, 5, 6, 7 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +/* + * NMD::decode_gpr_gpr2_reg1() - decoder for 'gpr2.reg1' gpr encoding type + * + * Map a 2-bit code to the 5-bit register space according to this pattern: + * + * 3 2 1 0 + * | | | | + * | | | | + * | | | └-------------------┐ + * | | └-------------------┐ | + * | └-------------------┐ | | + * └-------------------┐ | | | + * | | | | + * | | | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * Used in handling following instructions: + * + * - MOVEP + * - MOVEP[REV] + */ +uint64 NMD::decode_gpr_gpr2_reg1(uint64 d) +{ + static uint64 register_list[] = { 4, 5, 6, 7 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +/* + * NMD::decode_gpr_gpr2_reg2() - decoder for 'gpr2.reg2' gpr encoding type + * + * Map a 2-bit code to the 5-bit register space according to this pattern: + * + * 3 2 1 0 + * | | | | + * | | | | + * | | | └-----------------┐ + * | | └-----------------┐ | + * | └-----------------┐ | | + * └-----------------┐ | | | + * | | | | + * | | | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * Used in handling following instructions: + * + * - MOVEP + * - MOVEP[REV] + */ +uint64 NMD::decode_gpr_gpr2_reg2(uint64 d) +{ + static uint64 register_list[] = { 5, 6, 7, 8 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +/* + * NMD::decode_gpr_gpr1() - decoder for 'gpr1' gpr encoding type + * + * Map a 1-bit code to the 5-bit register space according to this pattern: + * + * 1 0 + * | | + * | | + * | └---------------------┐ + * └---------------------┐ | + * | | + * | | + * | | + * | | + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * 3 2 1 0 + * + * Used in handling following instruction: + * + * - MOVE.BALC + */ +uint64 NMD::decode_gpr_gpr1(uint64 d) +{ + static uint64 register_list[] = { 4, 5 }; + return renumber_registers(d, register_list, + sizeof(register_list) / sizeof(register_list[0])); +} + + +uint64 NMD::copy(uint64 d) +{ + return d; +} + + +int64 NMD::copy(int64 d) +{ + return d; +} + + +int64 NMD::neg_copy(uint64 d) +{ + return 0ll - d; +} + + +int64 NMD::neg_copy(int64 d) +{ + return -d; +} + + +/* strange wrapper around gpr3 */ +uint64 NMD::encode_rs3_and_check_rs3_ge_rt3(uint64 d) +{ +return decode_gpr_gpr3(d); +} + + +/* strange wrapper around gpr3 */ +uint64 NMD::encode_rs3_and_check_rs3_lt_rt3(uint64 d) +{ + return decode_gpr_gpr3(d); +} + + +/* nop - done by extraction function */ +uint64 NMD::encode_s_from_address(uint64 d) +{ + return d; +} + + +/* nop - done by extraction function */ +uint64 NMD::encode_u_from_address(uint64 d) +{ + return d; +} + + +/* nop - done by extraction function */ +uint64 NMD::encode_s_from_s_hi(uint64 d) +{ + return d; +} + + +uint64 NMD::encode_count3_from_count(uint64 d) +{ + IMGASSERTONCE(d < 8); + return d == 0ull ? 8ull : d; +} + + +uint64 NMD::encode_shift3_from_shift(uint64 d) +{ + IMGASSERTONCE(d < 8); + return d == 0ull ? 8ull : d; +} + + +/* special value for load literal */ +int64 NMD::encode_eu_from_s_li16(uint64 d) +{ + IMGASSERTONCE(d < 128); + return d == 127 ? -1 : (int64)d; +} + + +uint64 NMD::encode_msbd_from_size(uint64 d) +{ + IMGASSERTONCE(d < 32); + return d + 1; +} + + +uint64 NMD::encode_eu_from_u_andi16(uint64 d) +{ + IMGASSERTONCE(d < 16); + if (d == 12) { + return 0x00ffull; + } + if (d == 13) { + return 0xffffull; + } + return d; +} + + +uint64 NMD::encode_msbd_from_pos_and_size(uint64 d) +{ + IMGASSERTONCE(0); + return d; +} + + +/* save16 / restore16 ???? */ +uint64 NMD::encode_rt1_from_rt(uint64 d) +{ + return d ? 31 : 30; +} + + +/* ? */ +uint64 NMD::encode_lsb_from_pos_and_size(uint64 d) +{ + return d; +} + + +std::string NMD::save_restore_list(uint64 rt, uint64 count, uint64 gp) +{ + std::string str; + + for (uint64 counter = 0; counter != count; counter++) { + bool use_gp = gp && (counter == count - 1); + uint64 this_rt = use_gp ? 28 : ((rt & 0x10) | (rt + counter)) & 0x1f; + str += img::format(",%s", GPR(this_rt)); + } + + return str; +} + + +std::string NMD::GPR(uint64 reg) +{ + static const char *gpr_reg[32] = { + "zero", "at", "v0", "v1", "a0", "a1", "a2", "a3", + "a4", "a5", "a6", "a7", "r12", "r13", "r14", "r15", + "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", + "r24", "r25", "k0", "k1", "gp", "sp", "fp", "ra" + }; + + if (reg < 32) { + return gpr_reg[reg]; + } + + throw std::runtime_error(img::format("Invalid GPR register index %" PRIu64, + reg)); +} + + +std::string NMD::FPR(uint64 reg) +{ + static const char *fpr_reg[32] = { + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31" + }; + + if (reg < 32) { + return fpr_reg[reg]; + } + + throw std::runtime_error(img::format("Invalid FPR register index %" PRIu64, + reg)); +} + + +std::string NMD::AC(uint64 reg) +{ + static const char *ac_reg[4] = { + "ac0", "ac1", "ac2", "ac3" + }; + + if (reg < 4) { + return ac_reg[reg]; + } + + throw std::runtime_error(img::format("Invalid AC register index %" PRIu64, + reg)); +} + + +std::string NMD::IMMEDIATE(uint64 value) +{ + return img::format("0x%" PRIx64, value); +} + + +std::string NMD::IMMEDIATE(int64 value) +{ + return img::format("%" PRId64, value); +} + + +std::string NMD::CPR(uint64 reg) +{ + /* needs more work */ + return img::format("CP%" PRIu64, reg); +} + + +std::string NMD::ADDRESS(uint64 value, int instruction_size) +{ + /* token for string replace */ + /* const char TOKEN_REPLACE = (char)0xa2; */ + img::address address = m_pc + value + instruction_size; + /* symbol replacement */ + /* return img::as_char(TOKEN_REPLACE) + to_string(address); */ + return to_string(address); +} + + +uint64 NMD::extract_op_code_value(const uint16 * data, int size) +{ + switch (size) { + case 16: + return data[0]; + case 32: + return ((uint64)data[0] << 16) | data[1]; + case 48: + return ((uint64)data[0] << 32) | ((uint64)data[1] << 16) | data[2]; + default: + return data[0]; + } +} + + +int NMD::Disassemble(const uint16 * data, std::string & dis, + NMD::TABLE_ENTRY_TYPE & type) +{ + return Disassemble(data, dis, type, MAJOR, 2); +} + + +/* + * Recurse through tables until the instruction is found then return + * the string and size + * + * inputs: + * pointer to a word stream, + * disassember table and size + * returns: + * instruction size - negative is error + * disassembly string - on error will constain error string + */ +int NMD::Disassemble(const uint16 * data, std::string & dis, + NMD::TABLE_ENTRY_TYPE & type, const Pool *table, + int table_size) +{ + try + { + for (int i = 0; i < table_size; i++) { + uint64 op_code = extract_op_code_value(data, + table[i].instructions_size); + if ((op_code & table[i].mask) == table[i].value) { + /* possible match */ + conditional_function cond = table[i].condition; + if ((cond == 0) || (this->*cond)(op_code)) { + try + { + if (table[i].type == pool) { + return Disassemble(data, dis, type, + table[i].next_table, + table[i].next_table_size); + } else if ((table[i].type == instruction) || + (table[i].type == call_instruction) || + (table[i].type == branch_instruction) || + (table[i].type == return_instruction)) { + if ((table[i].attributes != 0) && + (m_requested_instruction_categories & + table[i].attributes) == 0) { + /* + * failed due to instruction having + * an ASE attribute and the requested version + * not having that attribute + */ + dis = "ASE attribute missmatch"; + return -5; + } + disassembly_function dis_fn = table[i].disassembly; + if (dis_fn == 0) { + dis = "disassembler failure - bad table entry"; + return -6; + } + type = table[i].type; + dis = (this->*dis_fn)(op_code); + return table[i].instructions_size; + } else { + dis = "reserved instruction"; + return -2; + } + } + catch (std::runtime_error & e) + { + dis = e.what(); + return -3; /* runtime error */ + } + } + } + } + } + catch (std::exception & e) + { + dis = e.what(); + return -4; /* runtime error */ + } + + dis = "failed to disassemble"; + return -1; /* failed to disassemble */ +} + + +uint64 NMD::extract_code_18_to_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 19); + return value; +} + + +uint64 NMD::extract_shift3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 3); + return value; +} + + +uint64 NMD::extract_u_11_10_9_8_7_6_5_4_3__s3(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 9) << 3; + return value; +} + + +uint64 NMD::extract_count_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 4); + return value; +} + + +uint64 NMD::extract_rtz3_9_8_7(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 7, 3); + return value; +} + + +uint64 NMD::extract_u_17_to_1__s1(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 1, 17) << 1; + return value; +} + + +int64 NMD::extract_s__se9_20_19_18_17_16_15_14_13_12_11(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 11, 10); + value = sign_extend(value, 9); + return value; +} + + +int64 NMD::extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 11; + value |= extract_bits(instruction, 1, 10) << 1; + value = sign_extend(value, 11); + return value; +} + + +uint64 NMD::extract_u_10(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 10, 1); + return value; +} + + +uint64 NMD::extract_rtz4_27_26_25_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 3); + value |= extract_bits(instruction, 25, 1) << 3; + return value; +} + + +uint64 NMD::extract_sa_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 5); + return value; +} + + +uint64 NMD::extract_shift_4_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 5); + return value; +} + + +uint64 NMD::extract_shiftx_10_9_8_7__s1(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 7, 4) << 1; + return value; +} + + +uint64 NMD::extract_hint_25_24_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 5); + return value; +} + + +uint64 NMD::extract_count3_14_13_12(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 12, 3); + return value; +} + + +int64 NMD::extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 31; + value |= extract_bits(instruction, 2, 10) << 21; + value |= extract_bits(instruction, 12, 9) << 12; + value = sign_extend(value, 31); + return value; +} + + +int64 NMD::extract_s__se7_0_6_5_4_3_2_1_s1(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 7; + value |= extract_bits(instruction, 1, 6) << 1; + value = sign_extend(value, 7); + return value; +} + + +uint64 NMD::extract_u2_10_9(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 9, 2); + return value; +} + + +uint64 NMD::extract_code_25_24_23_22_21_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 10); + return value; +} + + +uint64 NMD::extract_rs_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +uint64 NMD::extract_u_2_1__s1(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 1, 2) << 1; + return value; +} + + +uint64 NMD::extract_stripe_6(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 6, 1); + return value; +} + + +uint64 NMD::extract_ac_15_14(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 14, 2); + return value; +} + + +uint64 NMD::extract_shift_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +uint64 NMD::extract_rdl_25_24(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 24, 1); + return value; +} + + +int64 NMD::extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 10; + value |= extract_bits(instruction, 1, 9) << 1; + value = sign_extend(value, 10); + return value; +} + + +uint64 NMD::extract_eu_6_5_4_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 7); + return value; +} + + +uint64 NMD::extract_shift_5_4_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 6); + return value; +} + + +uint64 NMD::extract_count_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 4); + return value; +} + + +uint64 NMD::extract_code_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 3); + return value; +} + + +uint64 NMD::extract_u_11_10_9_8_7_6_5_4_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 12); + return value; +} + + +uint64 NMD::extract_rs_4_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 5); + return value; +} + + +uint64 NMD::extract_u_20_to_3__s3(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 18) << 3; + return value; +} + + +uint64 NMD::extract_u_3_2_1_0__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 4) << 2; + return value; +} + + +uint64 NMD::extract_cofun_25_24_23(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 23); + return value; +} + + +uint64 NMD::extract_u_2_1_0__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 3) << 2; + return value; +} + + +uint64 NMD::extract_rd3_3_2_1(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 1, 3); + return value; +} + + +uint64 NMD::extract_sa_15_14_13_12(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 12, 4); + return value; +} + + +uint64 NMD::extract_rt_25_24_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 5); + return value; +} + + +uint64 NMD::extract_ru_7_6_5_4_3(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 5); + return value; +} + + +uint64 NMD::extract_u_17_to_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 18); + return value; +} + + +uint64 NMD::extract_rsz4_4_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 3); + value |= extract_bits(instruction, 4, 1) << 3; + return value; +} + + +int64 NMD::extract_s__se21_0_20_to_1_s1(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 21; + value |= extract_bits(instruction, 1, 20) << 1; + value = sign_extend(value, 21); + return value; +} + + +uint64 NMD::extract_op_25_to_3(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 23); + return value; +} + + +uint64 NMD::extract_rs4_4_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 3); + value |= extract_bits(instruction, 4, 1) << 3; + return value; +} + + +uint64 NMD::extract_bit_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 3); + return value; +} + + +uint64 NMD::extract_rt_41_40_39_38_37(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 37, 5); + return value; +} + + +int64 NMD::extract_shift__se5_21_20_19_18_17_16(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 16, 6); + value = sign_extend(value, 5); + return value; +} + + +uint64 NMD::extract_rd2_3_8(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 1) << 1; + value |= extract_bits(instruction, 8, 1); + return value; +} + + +uint64 NMD::extract_code_17_to_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 18); + return value; +} + + +uint64 NMD::extract_size_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +int64 NMD::extract_s__se8_15_7_6_5_4_3_2_s2(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 2, 6) << 2; + value |= extract_bits(instruction, 15, 1) << 8; + value = sign_extend(value, 8); + return value; +} + + +uint64 NMD::extract_u_15_to_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 16); + return value; +} + + +uint64 NMD::extract_fs_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +int64 NMD::extract_s__se8_15_7_6_5_4_3_2_1_0(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 8); + value |= extract_bits(instruction, 15, 1) << 8; + value = sign_extend(value, 8); + return value; +} + + +uint64 NMD::extract_stype_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +uint64 NMD::extract_rtl_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 9, 1); + return value; +} + + +uint64 NMD::extract_hs_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +uint64 NMD::extract_sel_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 3); + return value; +} + + +uint64 NMD::extract_lsb_4_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 5); + return value; +} + + +uint64 NMD::extract_gp_2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 2, 1); + return value; +} + + +uint64 NMD::extract_rt3_9_8_7(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 7, 3); + return value; +} + + +uint64 NMD::extract_ft_25_24_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 5); + return value; +} + + +uint64 NMD::extract_u_17_16_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 7); + return value; +} + + +uint64 NMD::extract_cs_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +uint64 NMD::extract_rt4_9_7_6_5(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 5, 3); + value |= extract_bits(instruction, 9, 1) << 3; + return value; +} + + +uint64 NMD::extract_msbt_10_9_8_7_6(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 6, 5); + return value; +} + + +uint64 NMD::extract_u_5_4_3_2_1_0__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 6) << 2; + return value; +} + + +uint64 NMD::extract_sa_15_14_13(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 13, 3); + return value; +} + + +int64 NMD::extract_s__se14_0_13_to_1_s1(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 14; + value |= extract_bits(instruction, 1, 13) << 1; + value = sign_extend(value, 14); + return value; +} + + +uint64 NMD::extract_rs3_6_5_4(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 4, 3); + return value; +} + + +uint64 NMD::extract_u_31_to_0__s32(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 32) << 32; + return value; +} + + +uint64 NMD::extract_shift_10_9_8_7_6(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 6, 5); + return value; +} + + +uint64 NMD::extract_cs_25_24_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 5); + return value; +} + + +uint64 NMD::extract_shiftx_11_10_9_8_7_6(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 6, 6); + return value; +} + + +uint64 NMD::extract_rt_9_8_7_6_5(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 5, 5); + return value; +} + + +uint64 NMD::extract_op_25_24_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 5); + return value; +} + + +uint64 NMD::extract_u_6_5_4_3_2_1_0__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 7) << 2; + return value; +} + + +uint64 NMD::extract_bit_16_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 6); + return value; +} + + +uint64 NMD::extract_mask_20_19_18_17_16_15_14(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 14, 7); + return value; +} + + +uint64 NMD::extract_eu_3_2_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 4); + return value; +} + + +uint64 NMD::extract_u_7_6_5_4__s4(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 4, 4) << 4; + return value; +} + + +int64 NMD::extract_s__se8_15_7_6_5_4_3_s3(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 3, 5) << 3; + value |= extract_bits(instruction, 15, 1) << 8; + value = sign_extend(value, 8); + return value; +} + + +uint64 NMD::extract_ft_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 5); + return value; +} + + +int64 NMD::extract_s__se31_15_to_0_31_to_16(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 16) << 16; + value |= extract_bits(instruction, 16, 16); + value = sign_extend(value, 31); + return value; +} + + +uint64 NMD::extract_u_20_19_18_17_16_15_14_13(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 13, 8); + return value; +} + + +uint64 NMD::extract_u_17_to_2__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 2, 16) << 2; + return value; +} + + +uint64 NMD::extract_rd_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 5); + return value; +} + + +uint64 NMD::extract_c0s_20_19_18_17_16(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 16, 5); + return value; +} + + +uint64 NMD::extract_code_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 2); + return value; +} + + +int64 NMD::extract_s__se25_0_24_to_1_s1(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 1) << 25; + value |= extract_bits(instruction, 1, 24) << 1; + value = sign_extend(value, 25); + return value; +} + + +uint64 NMD::extract_u_1_0(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 2); + return value; +} + + +uint64 NMD::extract_u_3_8__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 3, 1) << 3; + value |= extract_bits(instruction, 8, 1) << 2; + return value; +} + + +uint64 NMD::extract_fd_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 5); + return value; +} + + +uint64 NMD::extract_u_4_3_2_1_0__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 5) << 2; + return value; +} + + +uint64 NMD::extract_rtz4_9_7_6_5(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 5, 3); + value |= extract_bits(instruction, 9, 1) << 3; + return value; +} + + +uint64 NMD::extract_sel_15_14_13_12_11(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 11, 5); + return value; +} + + +uint64 NMD::extract_ct_25_24_23_22_21(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 21, 5); + return value; +} + + +uint64 NMD::extract_u_20_to_2__s2(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 2, 19) << 2; + return value; +} + + +int64 NMD::extract_s__se3_4_2_1_0(uint64 instruction) +{ + int64 value = 0; + value |= extract_bits(instruction, 0, 3); + value |= extract_bits(instruction, 4, 1) << 3; + value = sign_extend(value, 3); + return value; +} + + +uint64 NMD::extract_u_3_2_1_0__s1(uint64 instruction) +{ + uint64 value = 0; + value |= extract_bits(instruction, 0, 4) << 1; + return value; +} + + + +bool NMD::ADDIU_32__cond(uint64 instruction) +{ + uint64 rt = extract_rt_25_24_23_22_21(instruction); + return rt != 0; +} + + +bool NMD::ADDIU_RS5__cond(uint64 instruction) +{ + uint64 rt = extract_rt_9_8_7_6_5(instruction); + return rt != 0; +} + + +bool NMD::BALRSC_cond(uint64 instruction) +{ + uint64 rt = extract_rt_25_24_23_22_21(instruction); + return rt != 0; +} + + +bool NMD::BEQC_16__cond(uint64 instruction) +{ + uint64 rs3 = extract_rs3_6_5_4(instruction); + uint64 rt3 = extract_rt3_9_8_7(instruction); + uint64 u = extract_u_3_2_1_0__s1(instruction); + return rs3 < rt3 && u != 0; +} + + +bool NMD::BNEC_16__cond(uint64 instruction) +{ + uint64 rs3 = extract_rs3_6_5_4(instruction); + uint64 rt3 = extract_rt3_9_8_7(instruction); + uint64 u = extract_u_3_2_1_0__s1(instruction); + return rs3 >= rt3 && u != 0; +} + + +bool NMD::MOVE_cond(uint64 instruction) +{ + uint64 rt = extract_rt_9_8_7_6_5(instruction); + return rt != 0; +} + + +bool NMD::P16_BR1_cond(uint64 instruction) +{ + uint64 u = extract_u_3_2_1_0__s1(instruction); + return u != 0; +} + + +bool NMD::PREF_S9__cond(uint64 instruction) +{ + uint64 hint = extract_hint_25_24_23_22_21(instruction); + return hint != 31; +} + + +bool NMD::PREFE_cond(uint64 instruction) +{ + uint64 hint = extract_hint_25_24_23_22_21(instruction); + return hint != 31; +} + + +bool NMD::SLTU_cond(uint64 instruction) +{ + uint64 rd = extract_rd_15_14_13_12_11(instruction); + return rd != 0; +} + + + +/* + * ABS.D fd, fs - Floating Point Absolute Value + * + * 3 2 1 + * 10987654321098765432109876543210 + * 010001 00000 000101 + * fmt ----- + * fs ----- + * fd ----- + */ +std::string NMD::ABS_D(uint64 instruction) +{ + uint64 fd_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string fs = FPR(copy(fs_value)); + std::string fd = FPR(copy(fd_value)); + + return img::format("ABS.D %s, %s", fd, fs); +} + + +/* + * ABS.S fd, fs - Floating Point Absolute Value + * + * 3 2 1 + * 10987654321098765432109876543210 + * 010001 00000 000101 + * fmt ----- + * fd ----- + * fs ----- + */ +std::string NMD::ABS_S(uint64 instruction) +{ + uint64 fd_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string fs = FPR(copy(fs_value)); + std::string fd = FPR(copy(fd_value)); + + return img::format("ABS.S %s, %s", fd, fs); +} + + +/* + * [DSP] ABSQ_S.PH rt, rs - Find absolute value of two fractional halfwords + * with 16-bit saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0001000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ABSQ_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("ABSQ_S.PH %s, %s", rt, rs); +} + + +/* + * [DSP] ABSQ_S.QB rt, rs - Find absolute value of four fractional byte values + * with 8-bit saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0000000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ABSQ_S_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("ABSQ_S.QB %s, %s", rt, rs); +} + + +/* + * [DSP] ABSQ_S.W rt, rs - Find absolute value of fractional word with 32-bit + * saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ABSQ_S_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("ABSQ_S.W %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ACLR(uint64 instruction) +{ + uint64 bit_value = extract_bit_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string bit = IMMEDIATE(copy(bit_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("ACLR %s, %s(%s)", bit, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADD %s, %s, %s", rd, rs, rt); +} + + +/* + * ADD.D fd, fs, ft - Floating Point Add + * + * 3 2 1 + * 10987654321098765432109876543210 + * 010001 000101 + * fmt ----- + * ft ----- + * fs ----- + * fd ----- + */ +std::string NMD::ADD_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + std::string fd = FPR(copy(fd_value)); + + return img::format("ADD.D %s, %s, %s", fd, fs, ft); +} + + +/* + * ADD.S fd, fs, ft - Floating Point Add + * + * 3 2 1 + * 10987654321098765432109876543210 + * 010001 000101 + * fmt ----- + * ft ----- + * fs ----- + * fd ----- + */ +std::string NMD::ADD_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + std::string fd = FPR(copy(fd_value)); + + return img::format("ADD.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_15_to_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ADDIU %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("ADDIU %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_GP48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("ADDIU %s, $%d, %s", rt, 28, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_GP_B_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ADDIU %s, $%d, %s", rt, 28, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_GP_W_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_20_to_2__s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ADDIU %s, $%d, %s", rt, 28, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_NEG_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(neg_copy(u_value)); + + return img::format("ADDIU %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_R1_SP_(uint64 instruction) +{ + uint64 u_value = extract_u_5_4_3_2_1_0__s2(instruction); + uint64 rt3_value = extract_rt3_9_8_7(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ADDIU %s, $%d, %s", rt3, 29, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0010000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::ADDIU_R2_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_2_1_0__s2(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ADDIU %s, %s, %s", rt3, rs3, u); +} + + +/* + * ADDIU[RS5] rt, s5 - Add Signed Word and Set Carry Bit + * + * 5432109876543210 + * 100100 1 + * rt ----- + * s - --- + */ +std::string NMD::ADDIU_RS5_(uint64 instruction) +{ + uint64 rt_value = extract_rt_9_8_7_6_5(instruction); + int64 s_value = extract_s__se3_4_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("ADDIU %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDIUPC_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + int64 s_value = extract_s__se21_0_20_to_1_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("ADDIUPC %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDIUPC_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 6); + + return img::format("ADDIUPC %s, %s", rt, s); +} + + +/* + * [DSP] ADDQ.PH rd, rt, rs - Add fractional halfword vectors + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00000001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQ_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQ.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDQ_S.PH rd, rt, rs - Add fractional halfword vectors with 16-bit + * saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10000001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQ_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQ_S.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDQ_S.W rd, rt, rs - Add fractional words with 32-bit saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1100000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQ_S_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQ_S.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDQH.PH rd, rt, rs - Add fractional halfword vectors and shift + * right to halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQH_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQH.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDQH_R.PH rd, rt, rs - Add fractional halfword vectors and shift + * right to halve results with rounding + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQH_R_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQH_R.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDQH_R.W rd, rt, rs - Add fractional words and shift right to halve + * results with rounding + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQH_R_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQH_R.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDQH.W rd, rt, rs - Add fractional words and shift right to halve + * results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDQH_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDQH.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDSC rd, rt, rs - Add two signed words and set carry bit + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDSC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDSC %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDU[16] rd3, rs3, rt3 - + * + * 5432109876543210 + * 101100 0 + * rt3 --- + * rs3 --- + * rd3 --- + */ +std::string NMD::ADDU_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 rd3_value = extract_rd3_3_2_1(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string rd3 = GPR(decode_gpr_gpr3(rd3_value)); + + return img::format("ADDU %s, %s, %s", rd3, rs3, rt3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDU_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDU_4X4_(uint64 instruction) +{ + uint64 rt4_value = extract_rt4_9_7_6_5(instruction); + uint64 rs4_value = extract_rs4_4_2_1_0(instruction); + + std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); + + return img::format("ADDU %s, %s", rs4, rt4); +} + + +/* + * [DSP] ADDU.PH rd, rt, rs - Add two pairs of unsigned halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00100001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDU_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDU.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDU.QB rd, rt, rs - Unsigned Add Quad Byte Vectors + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00011001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDU_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDU.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] ADDU_S.PH rd, rt, rs - Add two pairs of unsigned halfwords with 16-bit + * saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10100001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDU_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDU_S.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDU_S.QB rd, rt, rs - Unsigned Add Quad Byte Vectors + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10011001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDU_S_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDU_S.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDUH.QB rd, rt, rs - Unsigned Add Vector Quad-Bytes And Right Shift + * to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00101001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDUH_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDUH.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDUH_R.QB rd, rt, rs - Unsigned Add Vector Quad-Bytes And Right Shift + * to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10101001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDUH_R_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDUH_R.QB %s, %s, %s", rd, rs, rt); +} + +/* + * ADDWC rd, rt, rs - Add Word with Carry Bit + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1111000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ADDWC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ADDWC %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ALUIPC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + int64 s_value = extract_s__se31_0_11_to_2_20_to_12_s12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("ALUIPC %s, %%pcrel_hi(%s)", rt, s); +} + + +/* + * AND[16] rt3, rs3 - + * + * 5432109876543210 + * 101100 + * rt3 --- + * rs3 --- + * eu ---- + */ +std::string NMD::AND_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("AND %s, %s", rs3, rt3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::AND_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("AND %s, %s, %s", rd, rs, rt); +} + + +/* + * ANDI rt, rs, u - + * + * 5432109876543210 + * 101100 + * rt3 --- + * rs3 --- + * eu ---- + */ +std::string NMD::ANDI_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 eu_value = extract_eu_3_2_1_0(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string eu = IMMEDIATE(encode_eu_from_u_andi16(eu_value)); + + return img::format("ANDI %s, %s, %s", rt3, rs3, eu); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ANDI_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ANDI %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::APPEND(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("APPEND %s, %s, %s", rt, rs, sa); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ASET(uint64 instruction) +{ + uint64 bit_value = extract_bit_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string bit = IMMEDIATE(copy(bit_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("ASET %s, %s(%s)", bit, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BALC_16_(uint64 instruction) +{ + int64 s_value = extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(instruction); + + std::string s = ADDRESS(encode_s_from_address(s_value), 2); + + return img::format("BALC %s", s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BALC_32_(uint64 instruction) +{ + int64 s_value = extract_s__se25_0_24_to_1_s1(instruction); + + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BALC %s", s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BALRSC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("BALRSC %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BBEQZC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 bit_value = extract_bit_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string bit = IMMEDIATE(copy(bit_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BBEQZC %s, %s, %s", rt, bit, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BBNEZC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 bit_value = extract_bit_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string bit = IMMEDIATE(copy(bit_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BBNEZC %s, %s, %s", rt, bit, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BC_16_(uint64 instruction) +{ + int64 s_value = extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(instruction); + + std::string s = ADDRESS(encode_s_from_address(s_value), 2); + + return img::format("BC %s", s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BC_32_(uint64 instruction) +{ + int64 s_value = extract_s__se25_0_24_to_1_s1(instruction); + + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BC %s", s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BC1EQZC(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BC1EQZC %s, %s", ft, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BC1NEZC(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BC1NEZC %s, %s", ft, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BC2EQZC(uint64 instruction) +{ + uint64 ct_value = extract_ct_25_24_23_22_21(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string ct = CPR(copy(ct_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BC2EQZC %s, %s", ct, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BC2NEZC(uint64 instruction) +{ + uint64 ct_value = extract_ct_25_24_23_22_21(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string ct = CPR(copy(ct_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BC2NEZC %s, %s", ct, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BEQC_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_3_2_1_0__s1(instruction); + + std::string rs3 = GPR(encode_rs3_and_check_rs3_lt_rt3(rs3_value)); + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = ADDRESS(encode_u_from_address(u_value), 2); + + return img::format("BEQC %s, %s, %s", rs3, rt3, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BEQC_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BEQC %s, %s, %s", rs, rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BEQIC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BEQIC %s, %s, %s", rt, u, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BEQZC_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + int64 s_value = extract_s__se7_0_6_5_4_3_2_1_s1(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 2); + + return img::format("BEQZC %s, %s", rt3, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BGEC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BGEC %s, %s, %s", rs, rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BGEIC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BGEIC %s, %s, %s", rt, u, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BGEIUC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BGEIUC %s, %s, %s", rt, u, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BGEUC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BGEUC %s, %s, %s", rs, rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BLTC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BLTC %s, %s, %s", rs, rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BLTIC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BLTIC %s, %s, %s", rt, u, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BLTIUC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BLTIUC %s, %s, %s", rt, u, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BLTUC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BLTUC %s, %s, %s", rs, rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BNEC_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_3_2_1_0__s1(instruction); + + std::string rs3 = GPR(encode_rs3_and_check_rs3_ge_rt3(rs3_value)); + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = ADDRESS(encode_u_from_address(u_value), 2); + + return img::format("BNEC %s, %s, %s", rs3, rt3, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BNEC_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BNEC %s, %s, %s", rs, rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BNEIC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_16_15_14_13_12_11(instruction); + int64 s_value = extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BNEIC %s, %s, %s", rt, u, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BNEZC_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + int64 s_value = extract_s__se7_0_6_5_4_3_2_1_s1(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 2); + + return img::format("BNEZC %s, %s", rt3, s); +} + + +/* + * [DSP] BPOSGE32C offset - Branch on greater than or equal to value 32 in + * DSPControl Pos field + * + * 3 2 1 + * 10987654321098765432109876543210 + * 100010xxxxx0010001 + * s[13:1] ------------- + * s[14] - + */ +std::string NMD::BPOSGE32C(uint64 instruction) +{ + int64 s_value = extract_s__se14_0_13_to_1_s1(instruction); + + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("BPOSGE32C %s", s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BREAK_16_(uint64 instruction) +{ + uint64 code_value = extract_code_2_1_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("BREAK %s", code); +} + + +/* + * BREAK code - Break. Cause a Breakpoint exception + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BREAK_32_(uint64 instruction) +{ + uint64 code_value = extract_code_18_to_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("BREAK %s", code); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::BRSC(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + + return img::format("BRSC %s", rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CACHE(uint64 instruction) +{ + uint64 op_value = extract_op_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string op = IMMEDIATE(copy(op_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("CACHE %s, %s(%s)", op, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CACHEE(uint64 instruction) +{ + uint64 op_value = extract_op_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string op = IMMEDIATE(copy(op_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("CACHEE %s, %s(%s)", op, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CEIL_L_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CEIL.L.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CEIL_L_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CEIL.L.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CEIL_W_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CEIL.W.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CEIL_W_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CEIL.W.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CFC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("CFC1 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CFC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("CFC2 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CLASS_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CLASS.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CLASS_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CLASS.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CLO(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("CLO %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CLZ(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("CLZ %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_AF_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.AF.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_AF_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.AF.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_EQ_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.EQ.D %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] CMP.EQ.PH rs, rt - Compare vectors of signed integer halfword values + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxxx0000000101 + * rt ----- + * rs ----- + */ +std::string NMD::CMP_EQ_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMP.EQ.PH %s, %s", rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_EQ_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.EQ.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_LE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.LE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] CMP.LE.PH rs, rt - Compare vectors of signed integer halfword values + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxxx0010000101 + * rt ----- + * rs ----- + */ +std::string NMD::CMP_LE_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMP.LE.PH %s, %s", rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_LE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.LE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_LT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.LT.D %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] CMP.LT.PH rs, rt - Compare vectors of signed integer halfword values + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxxx0001000101 + * rt ----- + * rs ----- + */ +std::string NMD::CMP_LT_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMP.LT.PH %s, %s", rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_LT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.LT.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_NE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.NE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_NE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.NE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_OR_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.OR.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_OR_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.OR.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SAF_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SAF.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SAF_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SAF.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SEQ_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SEQ.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SEQ_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SEQ.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SLE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SLE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SLE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SLE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SLT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SLT.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SLT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SLT.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SNE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SNE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SNE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SNE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SOR_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SOR.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SOR_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SOR.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SUEQ_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SUEQ.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SUEQ_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SUEQ.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SULE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SULE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SULE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SULE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SULT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SULT.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SULT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SULT.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SUN_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SUN.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SUNE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SUNE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SUNE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SUNE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_SUN_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.SUN.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_UEQ_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.UEQ.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_UEQ_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.UEQ.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_ULE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.ULE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_ULE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.ULE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_ULT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.ULT.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_ULT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.ULT.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_UN_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.UN.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_UNE_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.UNE.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_UNE_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.UNE.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMP_UN_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("CMP.UN.S %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] CMPGDU.EQ.QB rd, rs, rt - Compare unsigned vector of + * four bytes and write result to GPR and DSPControl + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMPGDU_EQ_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPGDU.EQ.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] CMPGDU.LE.QB rd, rs, rt - Compare unsigned vector of + * four bytes and write result to GPR and DSPControl + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1000000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMPGDU_LE_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPGDU.LE.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] CMPGDU.EQ.QB rd, rs, rt - Compare unsigned vector of + * four bytes and write result to GPR and DSPControl + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0111000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMPGDU_LT_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPGDU.LT.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] CMPGU.EQ.QB rd, rs, rt - Compare vectors of unsigned + * byte values and write result to a GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0011000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMPGU_EQ_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPGU.EQ.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] CMPGU.LE.QB rd, rs, rt - Compare vectors of unsigned + * byte values and write result to a GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0101000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMPGU_LE_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPGU.LE.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] CMPGU.LT.QB rd, rs, rt - Compare vectors of unsigned + * byte values and write result to a GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0100000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CMPGU_LT_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPGU.LT.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] CMPU.EQ.QB rd, rs, rt - Compare vectors of unsigned + * byte values + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxxx1001000101 + * rt ----- + * rs ----- + */ +std::string NMD::CMPU_EQ_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPU.EQ.QB %s, %s", rs, rt); +} + + +/* + * [DSP] CMPU.LE.QB rd, rs, rt - Compare vectors of unsigned + * byte values + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxxx1011000101 + * rt ----- + * rs ----- + */ +std::string NMD::CMPU_LE_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPU.LE.QB %s, %s", rs, rt); +} + + +/* + * [DSP] CMPU.LT.QB rd, rs, rt - Compare vectors of unsigned + * byte values + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxxx1010000101 + * rt ----- + * rs ----- + */ +std::string NMD::CMPU_LT_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("CMPU.LT.QB %s, %s", rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::COP2_1(uint64 instruction) +{ + uint64 cofun_value = extract_cofun_25_24_23(instruction); + + std::string cofun = IMMEDIATE(copy(cofun_value)); + + return img::format("COP2_1 %s", cofun); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CTC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("CTC1 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CTC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("CTC2 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_D_L(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.D.L %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_D_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.D.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_D_W(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.D.W %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_L_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.L.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_L_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.L.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_S_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.S.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_S_L(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.S.L %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_S_PL(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.S.PL %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_S_PU(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.S.PU %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_S_W(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.S.W %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_W_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.W.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::CVT_W_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("CVT.W.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DADDIU_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("DADDIU %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DADDIU_NEG_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(neg_copy(u_value)); + + return img::format("DADDIU %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DADDIU_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("DADDIU %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DADD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DADD %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DADDU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DADDU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DCLO(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("DCLO %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DCLZ(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("DCLZ %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DDIV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DDIV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DDIVU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DDIVU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DERET(uint64 instruction) +{ + (void)instruction; + + return "DERET "; +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DEXTM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string lsb = IMMEDIATE(copy(lsb_value)); + std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + + return img::format("DEXTM %s, %s, %s, %s", rt, rs, lsb, msbd); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DEXT(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string lsb = IMMEDIATE(copy(lsb_value)); + std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + + return img::format("DEXT %s, %s, %s, %s", rt, rs, lsb, msbd); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DEXTU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string lsb = IMMEDIATE(copy(lsb_value)); + std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + + return img::format("DEXTU %s, %s, %s, %s", rt, rs, lsb, msbd); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DINSM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); + std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + /* !!!!!!!!!! - no conversion function */ + + return img::format("DINSM %s, %s, %s, %s", rt, rs, pos, size); + /* hand edited */ +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DINS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); + std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + /* !!!!!!!!!! - no conversion function */ + + return img::format("DINS %s, %s, %s, %s", rt, rs, pos, size); + /* hand edited */ +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DINSU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); + std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + /* !!!!!!!!!! - no conversion function */ + + return img::format("DINSU %s, %s, %s, %s", rt, rs, pos, size); + /* hand edited */ +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("DI %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DIV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DIV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DIV_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("DIV.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DIV_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("DIV.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DIVU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DIVU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DLSA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + uint64 u2_value = extract_u2_10_9(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string u2 = IMMEDIATE(copy(u2_value)); + + return img::format("DLSA %s, %s, %s, %s", rd, rs, rt, u2); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DLUI_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + uint64 u_value = extract_u_31_to_0__s32(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("DLUI %s, %s", rt, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMFC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("DMFC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMFC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("DMFC1 %s, %s", rt, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMFC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("DMFC2 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMFGC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("DMFGC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMOD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DMOD %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMODU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DMODU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMTC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("DMTC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMTC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("DMTC1 %s, %s", rt, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMTC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("DMTC2 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMTGC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("DMTGC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMT(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("DMT %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMUH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DMUH %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMUHU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DMUHU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMUL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DMUL %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DMULU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DMULU %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] DPA.W.PH ac, rs, rt - Dot product with accumulate on + * vector integer halfword elements + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00000010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::DPA_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPA.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAQ_SA_L_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAQ_SA.L.W %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAQ_S_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAQ_S.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAQX_SA_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAQX_SA.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAQX_S_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAQX_S.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAU_H_QBL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAU.H.QBL %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAU_H_QBR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAU.H.QBR %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPAX_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPAX.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPS_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPS.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSQ_SA_L_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSQ_SA.L.W %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSQ_S_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSQ_S.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSQX_SA_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSQX_SA.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSQX_S_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSQX_S.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSU_H_QBL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSU.H.QBL %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSU_H_QBR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSU.H.QBR %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DPSX_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DPSX.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * DROTR - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DROTR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DROTR %s, %s, %s", rt, rs, shift); +} + + +/* + * DROTR[32] - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0110 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DROTR32(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DROTR32 %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DROTRV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DROTRV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DROTX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shiftx_value = extract_shiftx_11_10_9_8_7_6(instruction); + uint64 shift_value = extract_shift_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + std::string shiftx = IMMEDIATE(copy(shiftx_value)); + + return img::format("DROTX %s, %s, %s, %s", rt, rs, shift, shiftx); +} + + +/* + * DSLL - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0000 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DSLL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DSLL %s, %s, %s", rt, rs, shift); +} + + +/* + * DSLL[32] - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0000 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DSLL32(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DSLL32 %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DSLLV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DSLLV %s, %s, %s", rd, rs, rt); +} + + +/* + * DSRA - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0100 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DSRA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DSRA %s, %s, %s", rt, rs, shift); +} + + +/* + * DSRA[32] - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0100 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DSRA32(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DSRA32 %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DSRAV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DSRAV %s, %s, %s", rd, rs, rt); +} + + +/* + * DSRL - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0100 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DSRL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DSRL %s, %s, %s", rt, rs, shift); +} + + +/* + * DSRL[32] - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 10o000 1100xxx0010 + * rt ----- + * rs ----- + * shift ----- + */ +std::string NMD::DSRL32(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("DSRL32 %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DSRLV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DSRLV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DSUB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DSUB %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DSUBU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("DSUBU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DVPE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("DVPE %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::DVP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("DVP %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EHB(uint64 instruction) +{ + (void)instruction; + + return "EHB "; +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("EI %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EMT(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("EMT %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ERET(uint64 instruction) +{ + (void)instruction; + + return "ERET "; +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ERETNC(uint64 instruction) +{ + (void)instruction; + + return "ERETNC "; +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EVP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("EVP %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EVPE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("EVPE %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXT(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string lsb = IMMEDIATE(copy(lsb_value)); + std::string msbd = IMMEDIATE(encode_msbd_from_size(msbd_value)); + + return img::format("EXT %s, %s, %s, %s", rt, rs, lsb, msbd); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXTD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + uint64 shift_value = extract_shift_10_9_8_7_6(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTD %s, %s, %s, %s", rd, rs, rt, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXTD32(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + uint64 shift_value = extract_shift_10_9_8_7_6(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTD32 %s, %s, %s, %s", rd, rs, rt, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXTPDP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 size_value = extract_size_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string size = IMMEDIATE(copy(size_value)); + + return img::format("EXTPDP %s, %s, %s", rt, ac, size); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXTPDPV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("EXTPDPV %s, %s, %s", rt, ac, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXTP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 size_value = extract_size_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string size = IMMEDIATE(copy(size_value)); + + return img::format("EXTP %s, %s, %s", rt, ac, size); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::EXTPV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("EXTPV %s, %s, %s", rt, ac, rs); +} + + +/* + * [DSP] EXTR_RS.W rt, ac, shift - Extract word value from accumulator to GPR + * with right shift + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10111001111111 + * rt ----- + * shift ----- + * ac -- + */ +std::string NMD::EXTR_RS_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 shift_value = extract_shift_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTR_RS.W %s, %s, %s", rt, ac, shift); +} + + +/* + * [DSP] EXTR_R.W rt, ac, shift - Extract word value from accumulator to GPR + * with right shift + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01111001111111 + * rt ----- + * shift ----- + * ac -- + */ +std::string NMD::EXTR_R_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 shift_value = extract_shift_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTR_R.W %s, %s, %s", rt, ac, shift); +} + + +/* + * [DSP] EXTR_S.H rt, ac, shift - Extract halfword value from accumulator + * to GPR with right shift and saturate + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11111001111111 + * rt ----- + * shift ----- + * ac -- + */ +std::string NMD::EXTR_S_H(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 shift_value = extract_shift_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTR_S.H %s, %s, %s", rt, ac, shift); +} + + +/* + * [DSP] EXTR.W rt, ac, shift - Extract word value from accumulator to GPR + * with right shift + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00111001111111 + * rt ----- + * shift ----- + * ac -- + */ +std::string NMD::EXTR_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 shift_value = extract_shift_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTR.W %s, %s, %s", rt, ac, shift); +} + + +/* + * [DSP] EXTRV_RS.W rt, ac, rs - Extract word value with variable + * right shift from accumulator to GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10111010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::EXTRV_RS_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("EXTRV_RS.W %s, %s, %s", rt, ac, rs); +} + + +/* + * [DSP] EXTRV_R.W rt, ac, rs - Extract word value with variable + * right shift from accumulator to GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01111010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::EXTRV_R_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("EXTRV_R.W %s, %s, %s", rt, ac, rs); +} + + +/* + * [DSP] EXTRV_S.H rt, ac, rs - Extract halfword value variable from + * accumulator to GPR with right shift and saturate + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11111010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::EXTRV_S_H(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("EXTRV_S.H %s, %s, %s", rt, ac, rs); +} + + +/* + * [DSP] EXTRV.W rt, ac, rs - Extract word value with variable + * right shift from accumulator to GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00111010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::EXTRV_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("EXTRV.W %s, %s, %s", rt, ac, rs); +} + + +/* + * EXTW - Extract Word + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 011111 + * rt ----- + * rs ----- + * rd ----- + * shift ----- + */ +std::string NMD::EXTW(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + uint64 shift_value = extract_shift_10_9_8_7_6(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("EXTW %s, %s, %s, %s", rd, rs, rt, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::FLOOR_L_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("FLOOR.L.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::FLOOR_L_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("FLOOR.L.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::FLOOR_W_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("FLOOR.W.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::FLOOR_W_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("FLOOR.W.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::FORK(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("FORK %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::HYPCALL(uint64 instruction) +{ + uint64 code_value = extract_code_17_to_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("HYPCALL %s", code); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::HYPCALL_16_(uint64 instruction) +{ + uint64 code_value = extract_code_1_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("HYPCALL %s", code); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::INS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 msbd_value = extract_msbt_10_9_8_7_6(instruction); + uint64 lsb_value = extract_lsb_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string pos = IMMEDIATE(encode_lsb_from_pos_and_size(lsb_value)); + std::string size = IMMEDIATE(encode_lsb_from_pos_and_size(msbd_value)); + /* !!!!!!!!!! - no conversion function */ + + return img::format("INS %s, %s, %s, %s", rt, rs, pos, size); + /* hand edited */ +} + + +/* + * [DSP] INSV rt, rs - Insert bit field variable + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0100000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::INSV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("INSV %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::IRET(uint64 instruction) +{ + (void)instruction; + + return "IRET "; +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::JALRC_16_(uint64 instruction) +{ + uint64 rt_value = extract_rt_9_8_7_6_5(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("JALRC $%d, %s", 31, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::JALRC_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("JALRC %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::JALRC_HB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("JALRC.HB %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::JRC(uint64 instruction) +{ + uint64 rt_value = extract_rt_9_8_7_6_5(instruction); + + std::string rt = GPR(copy(rt_value)); + + return img::format("JRC %s", rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LB_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_1_0(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("LB %s, %s(%s)", rt3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LB_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LB %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LB_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LB %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LB_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LB %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LBE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBU_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_1_0(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("LBU %s, %s(%s)", rt3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBU_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LBU %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBU_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LBU %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBU_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LBU %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBUE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LBUE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBUX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LBUX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LBX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LBX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LD_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_20_to_3__s3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LD %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LD_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LD %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LD_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LD %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDC1_GP_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_2__s2(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LDC1 %s, %s($%d)", ft, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDC1_S9_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LDC1 %s, %s(%s)", ft, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDC1_U12_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LDC1 %s, %s(%s)", ft, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDC1XS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LDC1XS %s, %s(%s)", ft, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDC1X(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LDC1X %s, %s(%s)", ft, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDC2(uint64 instruction) +{ + uint64 ct_value = extract_ct_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string ct = CPR(copy(ct_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LDC2 %s, %s(%s)", ct, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("LDM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDPC_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 6); + + return img::format("LDPC %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LDX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LDXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LDXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LH_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_2_1__s1(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("LH %s, %s(%s)", rt3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LH_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_1__s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LH %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LH_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LH %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LH_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LH %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LHE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHU_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_2_1__s1(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("LHU %s, %s(%s)", rt3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHU_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_1__s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LHU %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHU_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LHU %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHU_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LHU %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHUE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LHUE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHUX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LHUX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHUXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LHUXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LHXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LHX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LHX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LI_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 eu_value = extract_eu_6_5_4_3_2_1_0(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string eu = IMMEDIATE(encode_eu_from_s_li16(eu_value)); + + return img::format("LI %s, %s", rt3, eu); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LI_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("LI %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LL %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LLD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_s3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LLD %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LLDP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ru_value = extract_ru_7_6_5_4_3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ru = GPR(copy(ru_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LLDP %s, %s, (%s)", rt, ru, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LLE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LLE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LLWP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ru_value = extract_ru_7_6_5_4_3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ru = GPR(copy(ru_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LLWP %s, %s, (%s)", rt, ru, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LLWPE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ru_value = extract_ru_7_6_5_4_3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ru = GPR(copy(ru_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LLWPE %s, %s, (%s)", rt, ru, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LSA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + uint64 u2_value = extract_u2_10_9(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + std::string u2 = IMMEDIATE(copy(u2_value)); + + return img::format("LSA %s, %s, %s, %s", rd, rs, rt, u2); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LUI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + int64 s_value = extract_s__se31_0_11_to_2_20_to_12_s12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("LUI %s, %%hi(%s)", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_3_2_1_0__s2(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("LW %s, %s(%s)", rt3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_4X4_(uint64 instruction) +{ + uint64 rt4_value = extract_rt4_9_7_6_5(instruction); + uint64 rs4_value = extract_rs4_4_2_1_0(instruction); + uint64 u_value = extract_u_3_8__s2(instruction); + + std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + + return img::format("LW %s, %s(%s)", rt4, u, rs4); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_20_to_2__s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LW %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_GP16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 u_value = extract_u_6_5_4_3_2_1_0__s2(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LW %s, %s($%d)", rt3, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LW %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_SP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_9_8_7_6_5(instruction); + uint64 u_value = extract_u_4_3_2_1_0__s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LW %s, %s($%d)", rt, u, 29); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LW_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LW %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWC1_GP_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_2__s2(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LWC1 %s, %s($%d)", ft, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWC1_S9_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LWC1 %s, %s(%s)", ft, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWC1_U12_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LWC1 %s, %s(%s)", ft, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWC1X(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LWC1X %s, %s(%s)", ft, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWC1XS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LWC1XS %s, %s(%s)", ft, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWC2(uint64 instruction) +{ + uint64 ct_value = extract_ct_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string ct = CPR(copy(ct_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LWC2 %s, %s(%s)", ct, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LWE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("LWM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWPC_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 6); + + return img::format("LWPC %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWU_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_2__s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("LWU %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWU_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LWU %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWU_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("LWU %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWUX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LWUX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWUXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LWUXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LWX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWXS_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 rd3_value = extract_rd3_3_2_1(instruction); + + std::string rd3 = GPR(decode_gpr_gpr3(rd3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string rt3 = IMMEDIATE(decode_gpr_gpr3(rt3_value)); + + return img::format("LWXS %s, %s(%s)", rd3, rs3, rt3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::LWXS_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("LWXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * [DSP] MADD ac, rs, rt - Multiply two words and add to the specified + * accumulator + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MADD_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MADD %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MADDF_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MADDF.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MADDF_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MADDF.S %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] MADDU ac, rs, rt - Multiply two unsigned words and add to the + * specified accumulator + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MADDU_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MADDU %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MAQ_S.W.PHL ac, rs, rt - Multiply the left-most single vector + * fractional halfword elements with accumulation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAQ_S_W_PHL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MAQ_S.W.PHL %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MAQ_S.W.PHR ac, rs, rt - Multiply the right-most single vector + * fractional halfword elements with accumulation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAQ_S_W_PHR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MAQ_S.W.PHR %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MAQ_SA.W.PHL ac, rs, rt - Multiply the left-most single vector + * fractional halfword elements with saturating accumulation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAQ_SA_W_PHL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MAQ_SA.W.PHL %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MAQ_SA.W.PHR ac, rs, rt - Multiply the right-most single vector + * fractional halfword elements with saturating accumulation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAQ_SA_W_PHR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MAQ_SA.W.PHR %s, %s, %s", ac, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAX_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MAX.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAX_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MAX.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAXA_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MAXA.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MAXA_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MAXA.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MFC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("MFC1 %s, %s", rt, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("MFC2 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFGC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MFGC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFHC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MFHC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFHC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("MFHC1 %s, %s", rt, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFHC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("MFHC2 %s, %s", rt, cs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFHGC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MFHGC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * [DSP] MFHI rs, ac - Move from HI register + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxx 00000001111111 + * rt ----- + * ac -- + */ +std::string NMD::MFHI_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("MFHI %s, %s", rt, ac); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFHTR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + uint64 u_value = extract_u_10(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = IMMEDIATE(copy(c0s_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MFHTR %s, %s, %s, %s", rt, c0s, u, sel); +} + + +/* + * [DSP] MFLO rs, ac - Move from HI register + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 xxxxx 01000001111111 + * rt ----- + * ac -- + */ +std::string NMD::MFLO_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("MFLO %s, %s", rt, ac); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MFTR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + uint64 u_value = extract_u_10(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = IMMEDIATE(copy(c0s_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MFTR %s, %s, %s, %s", rt, c0s, u, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MIN_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MIN.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MIN_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MIN.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MINA_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MINA.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MINA_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MINA.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MOD %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MODSUB rd, rs, rt - Modular subtraction on an index value + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MODSUB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MODSUB %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1010010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MODU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MODU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOV_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("MOV.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOV_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("MOV.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOVE_BALC(uint64 instruction) +{ + uint64 rtz4_value = extract_rtz4_27_26_25_23_22_21(instruction); + uint64 rd1_value = extract_rdl_25_24(instruction); + int64 s_value = extract_s__se21_0_20_to_1_s1(instruction); + + std::string rd1 = GPR(decode_gpr_gpr1(rd1_value)); + std::string rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 4); + + return img::format("MOVE.BALC %s, %s, %s", rd1, rtz4, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOVEP(uint64 instruction) +{ + uint64 rtz4_value = extract_rtz4_9_7_6_5(instruction); + uint64 rd2_value = extract_rd2_3_8(instruction); + uint64 rsz4_value = extract_rsz4_4_2_1_0(instruction); + + std::string rd2 = GPR(decode_gpr_gpr2_reg1(rd2_value)); + std::string re2 = GPR(decode_gpr_gpr2_reg2(rd2_value)); + /* !!!!!!!!!! - no conversion function */ + std::string rsz4 = GPR(decode_gpr_gpr4_zero(rsz4_value)); + std::string rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value)); + + return img::format("MOVEP %s, %s, %s, %s", rd2, re2, rsz4, rtz4); + /* hand edited */ +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOVEP_REV_(uint64 instruction) +{ + uint64 rt4_value = extract_rt4_9_7_6_5(instruction); + uint64 rd2_value = extract_rd2_3_8(instruction); + uint64 rs4_value = extract_rs4_4_2_1_0(instruction); + + std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); + std::string rd2 = GPR(decode_gpr_gpr2_reg1(rd2_value)); + std::string rs2 = GPR(decode_gpr_gpr2_reg2(rd2_value)); + /* !!!!!!!!!! - no conversion function */ + + return img::format("MOVEP %s, %s, %s, %s", rs4, rt4, rd2, rs2); + /* hand edited */ +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOVE(uint64 instruction) +{ + uint64 rt_value = extract_rt_9_8_7_6_5(instruction); + uint64 rs_value = extract_rs_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("MOVE %s, %s", rt, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOVN(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MOVN %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MOVZ(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MOVZ %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MSUB ac, rs, rt - Multiply word and subtract from accumulator + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10101010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::MSUB_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MSUB %s, %s, %s", ac, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MSUBF_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MSUBF.D %s, %s, %s", fd, fs, ft); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MSUBF_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MSUBF.S %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] MSUBU ac, rs, rt - Multiply word and add to accumulator + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11101010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::MSUBU_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MSUBU %s, %s, %s", ac, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MTC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("MTC1 %s, %s", rt, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("MTC2 %s, %s", rt, cs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTGC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MTGC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTHC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MTHC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTHC1(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("MTHC1 %s, %s", rt, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTHC2(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 cs_value = extract_cs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string cs = CPR(copy(cs_value)); + + return img::format("MTHC2 %s, %s", rt, cs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTHGC0(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = CPR(copy(c0s_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MTHGC0 %s, %s, %s", rt, c0s, sel); +} + + +/* + * [DSP] MTHI rs, ac - Move to HI register + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000xxxxx 10000001111111 + * rs ----- + * ac -- + */ +std::string NMD::MTHI_DSP_(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("MTHI %s, %s", rs, ac); +} + + +/* + * [DSP] MTHLIP rs, ac - Copy LO to HI and a GPR to LO and increment pos by 32 + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000xxxxx 00001001111111 + * rs ----- + * ac -- + */ +std::string NMD::MTHLIP(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("MTHLIP %s, %s", rs, ac); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTHTR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + uint64 u_value = extract_u_10(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = IMMEDIATE(copy(c0s_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MTHTR %s, %s, %s, %s", rt, c0s, u, sel); +} + + +/* + * [DSP] MTLO rs, ac - Move to LO register + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000xxxxx 11000001111111 + * rs ----- + * ac -- + */ +std::string NMD::MTLO_DSP_(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("MTLO %s, %s", rs, ac); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MTTR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 c0s_value = extract_c0s_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_15_14_13_12_11(instruction); + uint64 u_value = extract_u_10(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string c0s = IMMEDIATE(copy(c0s_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("MTTR %s, %s, %s, %s", rt, c0s, u, sel); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MUH %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUHU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MUHU %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUL_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MUL %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUL_4X4_(uint64 instruction) +{ + uint64 rt4_value = extract_rt4_9_7_6_5(instruction); + uint64 rs4_value = extract_rs4_4_2_1_0(instruction); + + std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + std::string rt4 = GPR(decode_gpr_gpr4(rt4_value)); + + return img::format("MUL %s, %s", rs4, rt4); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUL_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MUL.D %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] MUL.PH rd, rs, rt - Multiply vector integer half words to same size + * products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00000101101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUL_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MUL.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MUL_S.PH rd, rs, rt - Multiply vector integer half words to same size + * products (saturated) + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10000101101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUL_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MUL_S.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MUL_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("MUL.S %s, %s, %s", fd, fs, ft); +} + + +/* + * [DSP] MULEQ_S.W.PHL rd, rs, rt - Multiply vector fractional left halfwords + * to expanded width products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0000100101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULEQ_S_W_PHL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULEQ_S.W.PHL %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULEQ_S.W.PHR rd, rs, rt - Multiply vector fractional right halfwords + * to expanded width products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0001100101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULEQ_S_W_PHR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULEQ_S.W.PHR %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULEU_S.PH.QBL rd, rs, rt - Multiply vector fractional left bytes + * by halfwords to halfword products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0010010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULEU_S_PH_QBL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULEU_S.PH.QBL %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULEU_S.PH.QBR rd, rs, rt - Multiply vector fractional right bytes + * by halfwords to halfword products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0011010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULEU_S_PH_QBR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULEU_S.PH.QBR %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULQ_RS.PH rd, rs, rt - Multiply vector fractional halfwords + * to fractional halfword products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0100010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULQ_RS_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULQ_RS.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULQ_RS.W rd, rs, rt - Multiply fractional words to same size + * product with saturation and rounding + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0110010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULQ_RS_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULQ_RS.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULQ_S.PH rd, rs, rt - Multiply fractional halfwords to same size + * products + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0101010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULQ_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULQ_S.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULQ_S.W rd, rs, rt - Multiply fractional words to same size product + * with saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0111010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULQ_S_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULQ_S.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] MULSA.W.PH ac, rs, rt - Multiply and subtract vector integer halfword + * elements and accumulate + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 10110010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::MULSA_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULSA.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MULSAQ_S.W.PH ac, rs, rt - Multiply and subtract vector fractional + * halfwords and accumulate + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11110010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::MULSAQ_S_W_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULSAQ_S.W.PH %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MULT ac, rs, rt - Multiply word + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00110010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::MULT_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULT %s, %s, %s", ac, rs, rt); +} + + +/* + * [DSP] MULTU ac, rs, rt - Multiply unsigned word + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01110010111111 + * rt ----- + * rs ----- + * ac -- + */ +std::string NMD::MULTU_DSP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string ac = AC(copy(ac_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULTU %s, %s, %s", ac, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::MULU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("MULU %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::NEG_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("NEG.D %s, %s", ft, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::NEG_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("NEG.S %s, %s", ft, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::NOP_16_(uint64 instruction) +{ + (void)instruction; + + return "NOP "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::NOP_32_(uint64 instruction) +{ + (void)instruction; + + return "NOP "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::NOR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("NOR %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::NOT_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("NOT %s, %s", rt3, rs3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::OR_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + + return img::format("OR %s, %s", rs3, rt3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::OR_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("OR %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ORI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("ORI %s, %s, %s", rt, rs, u); +} + + +/* + * [DSP] PACKRL.PH rd, rs, rt - Pack a word using the right halfword from one + * source register and left halfword from another source register + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PACKRL_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PACKRL.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PAUSE(uint64 instruction) +{ + (void)instruction; + + return "PAUSE "; +} + + +/* + * [DSP] PICK.PH rd, rs, rt - Pick a vector of halfwords based on condition + * code bits + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PICK_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PICK.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] PICK.QB rd, rs, rt - Pick a vector of byte values based on condition + * code bits + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PICK_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PICK.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] PRECEQ.W.PHL rt, rs - Expand the precision of the left-most element + * of a paired halfword + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEQ_W_PHL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEQ.W.PHL %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEQ.W.PHR rt, rs - Expand the precision of the right-most element + * of a paired halfword + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEQ_W_PHR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEQ.W.PHR %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEQU.PH.QBLA rt, rs - Expand the precision of the two + * left-alternate elements of a quad byte vector + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEQU_PH_QBLA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEQU.PH.QBLA %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEQU.PH.QBL rt, rs - Expand the precision of the two left-most + * elements of a quad byte vector + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEQU_PH_QBL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEQU.PH.QBL %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEQU.PH.QBRA rt, rs - Expand the precision of the two + * right-alternate elements of a quad byte vector + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEQU_PH_QBRA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEQU.PH.QBRA %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEQU.PH.QBR rt, rs - Expand the precision of the two right-most + * elements of a quad byte vector + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEQU_PH_QBR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEQU.PH.QBR %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEU.PH.QBLA rt, rs - Expand the precision of the two + * left-alternate elements of a quad byte vector to four unsigned + * halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEU_PH_QBLA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEU.PH.QBLA %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEU.PH.QBL rt, rs - Expand the precision of the two left-most + * elements of a quad byte vector to form unsigned halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEU_PH_QBL(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEU.PH.QBL %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEU.PH.QBRA rt, rs - Expand the precision of the two + * right-alternate elements of a quad byte vector to form four + * unsigned halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEU_PH_QBRA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEU.PH.QBRA %s, %s", rt, rs); +} + + +/* + * [DSP] PRECEU.PH.QBR rt, rs - Expand the precision of the two right-most + * elements of a quad byte vector to form unsigned halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECEU_PH_QBR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PRECEU.PH.QBR %s, %s", rt, rs); +} + + +/* + * [DSP] PRECR.QB.PH rd, rs, rt - Reduce the precision of four integer + * halfwords to four bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0001101101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECR_QB_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PRECR.QB.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] PRECR_SRA.PH.W rt, rs, sa - Reduce the precision of two integer + * words to halfwords after a right shift + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECR_SRA_PH_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("PRECR_SRA.PH.W %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] PRECR_SRA_R.PH.W rt, rs, sa - Reduce the precision of two integer + * words to halfwords after a right shift with rounding + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECR_SRA_R_PH_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("PRECR_SRA_R.PH.W %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] PRECRQ.PH.W rd, rs, rt - Reduce the precision of fractional + * words to fractional halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECRQ_PH_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PRECRQ.PH.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] PRECRQ.QB.PH rd, rs, rt - Reduce the precision of four fractional + * halfwords to four bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0010101101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECRQ_QB_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PRECRQ.QB.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] PRECRQ_RS.PH.W rd, rs, rt - Reduce the precision of fractional + * words to halfwords with rounding and saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECRQ_RS_PH_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PRECRQ_RS.PH.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] PRECRQU_S.QB.PH rd, rs, rt - Reduce the precision of fractional + * halfwords to unsigned bytes with saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PRECRQU_S_QB_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("PRECRQU_S.QB.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PREF_S9_(uint64 instruction) +{ + uint64 hint_value = extract_hint_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string hint = IMMEDIATE(copy(hint_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PREF %s, %s(%s)", hint, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PREF_U12_(uint64 instruction) +{ + uint64 hint_value = extract_hint_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string hint = IMMEDIATE(copy(hint_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PREF %s, %s(%s)", hint, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PREFE(uint64 instruction) +{ + uint64 hint_value = extract_hint_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string hint = IMMEDIATE(copy(hint_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("PREFE %s, %s(%s)", hint, s, rs); +} + + +/* + * [DSP] PREPEND rt, rs, sa - Right shift and prepend bits to the MSB + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::PREPEND(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("PREPEND %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] RADDU.W.QB rt, rs - Unsigned reduction add of vector quad bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 1111000100111111 + * rt ----- + * rs ----- + */ +std::string NMD::RADDU_W_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("RADDU.W.QB %s, %s", rt, rs); +} + + +/* + * [DSP] RDDSP rt, mask - Read DSPControl register fields to a GPR + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00011001111111 + * rt ----- + * mask ------- + */ +std::string NMD::RDDSP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 mask_value = extract_mask_20_19_18_17_16_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string mask = IMMEDIATE(copy(mask_value)); + + return img::format("RDDSP %s, %s", rt, mask); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RDHWR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 hs_value = extract_hs_20_19_18_17_16(instruction); + uint64 sel_value = extract_sel_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string hs = CPR(copy(hs_value)); + std::string sel = IMMEDIATE(copy(sel_value)); + + return img::format("RDHWR %s, %s, %s", rt, hs, sel); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RDPGPR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("RDPGPR %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RECIP_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("RECIP.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RECIP_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("RECIP.S %s, %s", ft, fs); +} + + +/* + * [DSP] REPL.PH rd, s - Replicate immediate integer into all vector element + * positions + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x0000111101 + * rt ----- + * s ---------- + */ +std::string NMD::REPL_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + int64 s_value = extract_s__se9_20_19_18_17_16_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + + return img::format("REPL.PH %s, %s", rt, s); +} + + +/* + * [DSP] REPL.QB rd, u - Replicate immediate integer into all vector element + * positions + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x010111111111 + * rt ----- + * u -------- + */ +std::string NMD::REPL_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_20_19_18_17_16_15_14_13(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("REPL.QB %s, %s", rt, u); +} + + +/* + * [DSP] REPLV.PH rt, rs - Replicate a halfword into all vector element + * positions + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0000001100111111 + * rt ----- + * rs ----- + */ +std::string NMD::REPLV_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("REPLV.PH %s, %s", rt, rs); +} + + +/* + * [DSP] REPLV.QB rt, rs - Replicate byte into all vector element positions + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0001001100111111 + * rt ----- + * rs ----- + */ +std::string NMD::REPLV_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("REPLV.QB %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RESTORE_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 count_value = extract_count_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); + uint64 gp_value = extract_gp_2(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + return img::format("RESTORE %s%s", u, + save_restore_list(rt_value, count_value, gp_value)); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RESTORE_JRC_16_(uint64 instruction) +{ + uint64 rt1_value = extract_rtl_11(instruction); + uint64 u_value = extract_u_7_6_5_4__s4(instruction); + uint64 count_value = extract_count_3_2_1_0(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + return img::format("RESTORE.JRC %s%s", u, + save_restore_list(encode_rt1_from_rt(rt1_value), count_value, 0)); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RESTORE_JRC_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 count_value = extract_count_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); + uint64 gp_value = extract_gp_2(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + return img::format("RESTORE.JRC %s%s", u, + save_restore_list(rt_value, count_value, gp_value)); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RESTOREF(uint64 instruction) +{ + uint64 count_value = extract_count_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + std::string count = IMMEDIATE(copy(count_value)); + + return img::format("RESTOREF %s, %s", u, count); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RINT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("RINT.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RINT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("RINT.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROTR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("ROTR %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROTRV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("ROTRV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROTX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shiftx_value = extract_shiftx_10_9_8_7__s1(instruction); + uint64 stripe_value = extract_stripe_6(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + std::string shiftx = IMMEDIATE(copy(shiftx_value)); + std::string stripe = IMMEDIATE(copy(stripe_value)); + + return img::format("ROTX %s, %s, %s, %s, %s", + rt, rs, shift, shiftx, stripe); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROUND_L_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("ROUND.L.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROUND_L_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("ROUND.L.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROUND_W_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("ROUND.W.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::ROUND_W_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("ROUND.W.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RSQRT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("RSQRT.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110000101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::RSQRT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("RSQRT.S %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SAVE_16_(uint64 instruction) +{ + uint64 rt1_value = extract_rtl_11(instruction); + uint64 u_value = extract_u_7_6_5_4__s4(instruction); + uint64 count_value = extract_count_3_2_1_0(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + return img::format("SAVE %s%s", u, + save_restore_list(encode_rt1_from_rt(rt1_value), count_value, 0)); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SAVE_32_(uint64 instruction) +{ + uint64 count_value = extract_count_19_18_17_16(instruction); + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); + uint64 gp_value = extract_gp_2(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + return img::format("SAVE %s%s", u, + save_restore_list(rt_value, count_value, gp_value)); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SAVEF(uint64 instruction) +{ + uint64 count_value = extract_count_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3__s3(instruction); + + std::string u = IMMEDIATE(copy(u_value)); + std::string count = IMMEDIATE(copy(count_value)); + + return img::format("SAVEF %s, %s", u, count); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SB_16_(uint64 instruction) +{ + uint64 rtz3_value = extract_rtz3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_1_0(instruction); + + std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("SB %s, %s(%s)", rtz3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SB_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SB %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SB_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SB %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SB_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SB %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SBE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SBE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SBX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SBX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SC(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SC %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SCD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_s3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SCD %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SCDP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ru_value = extract_ru_7_6_5_4_3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ru = GPR(copy(ru_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SCDP %s, %s, (%s)", rt, ru, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SCE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SCE %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SCWP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ru_value = extract_ru_7_6_5_4_3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ru = GPR(copy(ru_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SCWP %s, %s, (%s)", rt, ru, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SCWPE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ru_value = extract_ru_7_6_5_4_3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string ru = GPR(copy(ru_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SCWPE %s, %s, (%s)", rt, ru, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SD_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_20_to_3__s3(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SD %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SD_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SD %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SD_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SD %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDBBP_16_(uint64 instruction) +{ + uint64 code_value = extract_code_2_1_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("SDBBP %s", code); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDBBP_32_(uint64 instruction) +{ + uint64 code_value = extract_code_18_to_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("SDBBP %s", code); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDC1_GP_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_2__s2(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SDC1 %s, %s($%d)", ft, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDC1_S9_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SDC1 %s, %s(%s)", ft, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDC1_U12_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SDC1 %s, %s(%s)", ft, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDC1X(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SDC1X %s, %s(%s)", ft, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDC1XS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SDC1XS %s, %s(%s)", ft, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDC2(uint64 instruction) +{ + uint64 cs_value = extract_cs_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string cs = CPR(copy(cs_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SDC2 %s, %s(%s)", cs, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("SDM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDPC_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 6); + + return img::format("SDPC %s, %s", rt, s); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SDXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SDX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SDX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SEB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SEB %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SEH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SEH %s, %s", rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SEL_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SEL.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SEL_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SEL.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SELEQZ_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SELEQZ.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SELEQZ_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SELEQZ.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SELNEZ_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SELNEZ.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SELNEZ_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SELNEZ.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SEQI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SEQI %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SH_16_(uint64 instruction) +{ + uint64 rtz3_value = extract_rtz3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_2_1__s1(instruction); + + std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("SH %s, %s(%s)", rtz3, u, rs3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SH_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_1__s1(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SH %s, %s($%d)", rt, u, 28); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SH_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SH %s, %s(%s)", rt, s, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SH_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SH %s, %s(%s)", rt, u, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHE %s, %s(%s)", rt, s, rs); +} + + +/* + * [DSP] SHILO ac, shift - Shift an accumulator value leaving the result in + * the same accumulator + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000xxxx xxxx0000011101 + * shift ------ + * ac -- + */ +std::string NMD::SHILO(uint64 instruction) +{ + int64 shift_value = extract_shift__se5_21_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string shift = IMMEDIATE(copy(shift_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("SHILO %s, %s", ac, shift); +} + + +/* + * [DSP] SHILOV ac, rs - Variable shift of accumulator value leaving the result + * in the same accumulator + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000xxxxx 01001001111111 + * rs ----- + * ac -- + */ +std::string NMD::SHILOV(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ac_value = extract_ac_15_14(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string ac = AC(copy(ac_value)); + + return img::format("SHILOV %s, %s", ac, rs); +} + + +/* + * [DSP] SHLL.PH rt, rs, sa - Shift left logical vector pair halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 001110110101 + * rt ----- + * rs ----- + * sa ---- + */ +std::string NMD::SHLL_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHLL.PH %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] SHLL.QB rt, rs, sa - Shift left logical vector quad bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 0100001111111 + * rt ----- + * rs ----- + * sa --- + */ +std::string NMD::SHLL_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHLL.QB %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] SHLL_S.PH rt, rs, sa - Shift left logical vector pair halfwords + * with saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 001110110101 + * rt ----- + * rs ----- + * sa ---- + */ +std::string NMD::SHLL_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHLL_S.PH %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] SHLL_S.PH rt, rs, sa - Shift left logical word with saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1111110101 + * rt ----- + * rs ----- + * sa ----- + */ +std::string NMD::SHLL_S_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHLL_S.W %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] SHLLV.PH rd, rt, rs - Shift left logical variable vector pair + * halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01110001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHLLV_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHLLV.PH %s, %s, %s", rd, rt, rs); +} + + +/* + * [DSP] SHLLV_S.QB rd, rt, rs - Shift left logical variable vector quad bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1110010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHLLV_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHLLV.QB %s, %s, %s", rd, rt, rs); +} + + +/* + * [DSP] SHLLV.PH rd, rt, rs - Shift left logical variable vector pair + * halfwords with saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11110001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHLLV_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHLLV_S.PH %s, %s, %s", rd, rt, rs); +} + + +/* + * [DSP] SHLLV_S.W rd, rt, rs - Shift left logical variable vector word + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1111010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHLLV_S_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHLLV_S.W %s, %s, %s", rd, rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRA_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRA.PH %s, %s, %s", rt, rs, sa); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRA_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRA.QB %s, %s, %s", rt, rs, sa); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRA_R_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRA_R.PH %s, %s, %s", rt, rs, sa); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRA_R_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRA_R.QB %s, %s, %s", rt, rs, sa); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRA_R_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12_11(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRA_R.W %s, %s, %s", rt, rs, sa); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRAV_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRAV.PH %s, %s, %s", rd, rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRAV_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRAV.QB %s, %s, %s", rd, rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRAV_R_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRAV_R.PH %s, %s, %s", rd, rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRAV_R_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRAV_R.QB %s, %s, %s", rd, rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRAV_R_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRAV_R.W %s, %s, %s", rd, rt, rs); +} + + +/* + * [DSP] SHRL.PH rt, rs, sa - Shift right logical two halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 001111111111 + * rt ----- + * rs ----- + * sa ---- + */ +std::string NMD::SHRL_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRL.PH %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] SHRL.QB rt, rs, sa - Shift right logical vector quad bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 1100001111111 + * rt ----- + * rs ----- + * sa --- + */ +std::string NMD::SHRL_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 sa_value = extract_sa_15_14_13(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string sa = IMMEDIATE(copy(sa_value)); + + return img::format("SHRL.QB %s, %s, %s", rt, rs, sa); +} + + +/* + * [DSP] SHLLV.PH rd, rt, rs - Shift right logical variable vector pair of + * halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1100010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRLV_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRLV.PH %s, %s, %s", rd, rt, rs); +} + + +/* + * [DSP] SHLLV.QB rd, rt, rs - Shift right logical variable vector quad bytes + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 x1101010101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHRLV_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SHRLV.QB %s, %s, %s", rd, rt, rs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SHX %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SHXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SHXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SIGRIE(uint64 instruction) +{ + uint64 code_value = extract_code_18_to_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("SIGRIE %s", code); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLL_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 shift3_value = extract_shift3_2_1_0(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string shift3 = IMMEDIATE(encode_shift3_from_shift(shift3_value)); + + return img::format("SLL %s, %s, %s", rt3, rs3, shift3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLL_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("SLL %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLLV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SLLV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLT(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SLT %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLTI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SLTI %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLTIU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SLTIU %s, %s, %s", rt, rs, u); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SLTU(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SLTU %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SOV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SOV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SPECIAL2(uint64 instruction) +{ + uint64 op_value = extract_op_25_to_3(instruction); + + std::string op = IMMEDIATE(copy(op_value)); + + return img::format("SPECIAL2 %s", op); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SQRT_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("SQRT.D %s, %s", ft, fs); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SQRT_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("SQRT.S %s, %s", ft, fs); +} + + +/* + * SRA rd, rt, sa - Shift Word Right Arithmetic + * + * 3 2 1 + * 10987654321098765432109876543210 + * 00000000000 000011 + * rt ----- + * rd ----- + * sa ----- + */ +std::string NMD::SRA(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("SRA %s, %s, %s", rt, rs, shift); +} + + +/* + * SRAV rd, rt, rs - Shift Word Right Arithmetic Variable + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00000000111 + * rs ----- + * rt ----- + * rd ----- + */ +std::string NMD::SRAV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SRAV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00000000111 + * rs ----- + * rt ----- + * rd ----- + */ +std::string NMD::SRL_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 shift3_value = extract_shift3_2_1_0(instruction); + + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string shift3 = IMMEDIATE(encode_shift3_from_shift(shift3_value)); + + return img::format("SRL %s, %s, %s", rt3, rs3, shift3); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SRL_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 shift_value = extract_shift_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string shift = IMMEDIATE(copy(shift_value)); + + return img::format("SRL %s, %s, %s", rt, rs, shift); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SRLV(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SRLV %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUB %s, %s, %s", rd, rs, rt); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUB_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SUB.D %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUB_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + uint64 fd_value = extract_fd_15_14_13_12_11(instruction); + + std::string fd = FPR(copy(fd_value)); + std::string fs = FPR(copy(fs_value)); + std::string ft = FPR(copy(ft_value)); + + return img::format("SUB.S %s, %s, %s", fd, fs, ft); +} + + +/* + * + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQ_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQ.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBQ.S.PH rd, rt, rs - Subtract fractional halfword vectors and shift + * right to halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQ_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQ_S.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBQ.S.W rd, rt, rs - Subtract fractional halfword vectors and shift + * right to halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQ_S_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQ_S.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBQH.PH rd, rt, rs - Subtract fractional halfword vectors and shift + * right to halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQH_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQH.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBQH_R.PH rd, rt, rs - Subtract fractional halfword vectors and shift + * right to halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQH_R_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQH_R.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBQH_R.W rd, rt, rs - Subtract fractional halfword vectors and shift + * right to halve results with rounding + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11001001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQH_R_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQH_R.W %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBQH.W rd, rs, rt - Subtract fractional words and shift right to + * halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBQH_W(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBQH.W %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBU_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 rd3_value = extract_rd3_3_2_1(instruction); + + std::string rd3 = GPR(decode_gpr_gpr3(rd3_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + + return img::format("SUBU %s, %s, %s", rd3, rs3, rt3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBU_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBU %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBU.PH rd, rs, rt - Subtract unsigned unsigned halfwords + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01100001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBU_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBU.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBU.QB rd, rs, rt - Subtract unsigned quad byte vectors + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01011001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBU_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBU.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBU_S.PH rd, rs, rt - Subtract unsigned unsigned halfwords with + * 8-bit saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11100001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBU_S_PH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBU_S.PH %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBU_S.QB rd, rs, rt - Subtract unsigned quad byte vectors with + * 8-bit saturation + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11011001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBU_S_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBU_S.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBUH.QB rd, rs, rt - Subtract unsigned bytes and right shift + * to halve results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01101001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBUH_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBUH.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * [DSP] SUBUH_R.QB rd, rs, rt - Subtract unsigned bytes and right shift + * to halve results with rounding + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 11101001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SUBUH_R_QB(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SUBUH_R.QB %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_16_(uint64 instruction) +{ + uint64 rtz3_value = extract_rtz3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + uint64 u_value = extract_u_3_2_1_0__s2(instruction); + + std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + + return img::format("SW %s, %s(%s)", rtz3, u, rs3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_4X4_(uint64 instruction) +{ + uint64 rtz4_value = extract_rtz4_9_7_6_5(instruction); + uint64 rs4_value = extract_rs4_4_2_1_0(instruction); + uint64 u_value = extract_u_3_8__s2(instruction); + + std::string rtz4 = GPR(decode_gpr_gpr4_zero(rtz4_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs4 = GPR(decode_gpr_gpr4(rs4_value)); + + return img::format("SW %s, %s(%s)", rtz4, u, rs4); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_GP16_(uint64 instruction) +{ + uint64 u_value = extract_u_6_5_4_3_2_1_0__s2(instruction); + uint64 rtz3_value = extract_rtz3_9_8_7(instruction); + + std::string rtz3 = GPR(decode_gpr_gpr3_src_store(rtz3_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SW %s, %s($%d)", rtz3, u, 28); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_GP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 u_value = extract_u_20_to_2__s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SW %s, %s($%d)", rt, u, 28); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_S9_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SW %s, %s(%s)", rt, s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_SP_(uint64 instruction) +{ + uint64 rt_value = extract_rt_9_8_7_6_5(instruction); + uint64 u_value = extract_u_4_3_2_1_0__s2(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SW %s, %s($%d)", rt, u, 29); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SW_U12_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SW %s, %s(%s)", rt, u, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWC1_GP_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 u_value = extract_u_17_to_2__s2(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("SWC1 %s, %s($%d)", ft, u, 28); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWC1_S9_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SWC1 %s, %s(%s)", ft, s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWC1_U12_(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string u = IMMEDIATE(copy(u_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SWC1 %s, %s(%s)", ft, u, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWC1X(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SWC1X %s, %s(%s)", ft, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWC1XS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 ft_value = extract_ft_15_14_13_12_11(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SWC1XS %s, %s(%s)", ft, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWC2(uint64 instruction) +{ + uint64 cs_value = extract_cs_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string cs = CPR(copy(cs_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SWC2 %s, %s(%s)", cs, s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SWE %s, %s(%s)", rt, s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("SWM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWPC_48_(uint64 instruction) +{ + uint64 rt_value = extract_rt_41_40_39_38_37(instruction); + int64 s_value = extract_s__se31_15_to_0_31_to_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = ADDRESS(encode_s_from_address(s_value), 6); + + return img::format("SWPC %s, %s", rt, s); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWX(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SWX %s, %s(%s)", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SWXS(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("SWXS %s, %s(%s)", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SYNC(uint64 instruction) +{ + uint64 stype_value = extract_stype_20_19_18_17_16(instruction); + + std::string stype = IMMEDIATE(copy(stype_value)); + + return img::format("SYNC %s", stype); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SYNCI(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SYNCI %s(%s)", s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SYNCIE(uint64 instruction) +{ + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("SYNCIE %s(%s)", s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::SYSCALL_16_(uint64 instruction) +{ + uint64 code_value = extract_code_1_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("SYSCALL %s", code); +} + + +/* + * SYSCALL code - System Call. Cause a System Call Exception + * + * 3 2 1 + * 10987654321098765432109876543210 + * 00000000000010 + * code ------------------ + */ +std::string NMD::SYSCALL_32_(uint64 instruction) +{ + uint64 code_value = extract_code_17_to_0(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("SYSCALL %s", code); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TEQ(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("TEQ %s, %s", rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBGINV(uint64 instruction) +{ + (void)instruction; + + return "TLBGINV "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBGINVF(uint64 instruction) +{ + (void)instruction; + + return "TLBGINVF "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBGP(uint64 instruction) +{ + (void)instruction; + + return "TLBGP "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBGR(uint64 instruction) +{ + (void)instruction; + + return "TLBGR "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBGWI(uint64 instruction) +{ + (void)instruction; + + return "TLBGWI "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBGWR(uint64 instruction) +{ + (void)instruction; + + return "TLBGWR "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBINV(uint64 instruction) +{ + (void)instruction; + + return "TLBINV "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBINVF(uint64 instruction) +{ + (void)instruction; + + return "TLBINVF "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBP(uint64 instruction) +{ + (void)instruction; + + return "TLBP "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBR(uint64 instruction) +{ + (void)instruction; + + return "TLBR "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBWI(uint64 instruction) +{ + (void)instruction; + + return "TLBWI "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TLBWR(uint64 instruction) +{ + (void)instruction; + + return "TLBWR "; +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TNE(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("TNE %s, %s", rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TRUNC_L_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("TRUNC.L.D %s, %s", ft, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TRUNC_L_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("TRUNC.L.S %s, %s", ft, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TRUNC_W_D(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("TRUNC.W.D %s, %s", ft, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::TRUNC_W_S(uint64 instruction) +{ + uint64 ft_value = extract_ft_25_24_23_22_21(instruction); + uint64 fs_value = extract_fs_20_19_18_17_16(instruction); + + std::string ft = FPR(copy(ft_value)); + std::string fs = FPR(copy(fs_value)); + + return img::format("TRUNC.W.S %s, %s", ft, fs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UALDM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("UALDM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UALH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("UALH %s, %s(%s)", rt, s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UALWM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("UALWM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UASDM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("UASDM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UASH(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("UASH %s, %s(%s)", rt, s, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UASWM(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + int64 s_value = extract_s__se8_15_7_6_5_4_3_2_1_0(instruction); + uint64 count3_value = extract_count3_14_13_12(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string s = IMMEDIATE(copy(s_value)); + std::string rs = GPR(copy(rs_value)); + std::string count3 = IMMEDIATE(encode_count3_from_count(count3_value)); + + return img::format("UASWM %s, %s(%s), %s", rt, s, rs, count3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::UDI(uint64 instruction) +{ + uint64 op_value = extract_op_25_to_3(instruction); + + std::string op = IMMEDIATE(copy(op_value)); + + return img::format("UDI %s", op); +} + + +/* + * WAIT code - Enter Wait State + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 1100001101111111 + * code ---------- + */ +std::string NMD::WAIT(uint64 instruction) +{ + uint64 code_value = extract_code_25_24_23_22_21_20_19_18_17_16(instruction); + + std::string code = IMMEDIATE(copy(code_value)); + + return img::format("WAIT %s", code); +} + + +/* + * [DSP] WRDSP rt, mask - Write selected fields from a GPR to the DSPControl + * register + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 01011001111111 + * rt ----- + * mask ------- + */ +std::string NMD::WRDSP(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 mask_value = extract_mask_20_19_18_17_16_15_14(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string mask = IMMEDIATE(copy(mask_value)); + + return img::format("WRDSP %s, %s", rt, mask); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::WRPGPR(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("WRPGPR %s, %s", rt, rs); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::XOR_16_(uint64 instruction) +{ + uint64 rt3_value = extract_rt3_9_8_7(instruction); + uint64 rs3_value = extract_rs3_6_5_4(instruction); + + std::string rs3 = GPR(decode_gpr_gpr3(rs3_value)); + std::string rt3 = GPR(decode_gpr_gpr3(rt3_value)); + + return img::format("XOR %s, %s", rs3, rt3); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::XOR_32_(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 rd_value = extract_rd_15_14_13_12_11(instruction); + + std::string rd = GPR(copy(rd_value)); + std::string rs = GPR(copy(rs_value)); + std::string rt = GPR(copy(rt_value)); + + return img::format("XOR %s, %s, %s", rd, rs, rt); +} + + +/* + * ADDQH_R.W rd, rt, rs - Add Fractional Words And Shift Right to Halve Results + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + * rd ----- + */ +std::string NMD::XORI(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + uint64 u_value = extract_u_11_10_9_8_7_6_5_4_3_2_1_0(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + std::string u = IMMEDIATE(copy(u_value)); + + return img::format("XORI %s, %s, %s", rt, rs, u); +} + + +/* + * YIELD rt, rs - + * + * 3 2 1 + * 10987654321098765432109876543210 + * 001000 00010001101 + * rt ----- + * rs ----- + */ +std::string NMD::YIELD(uint64 instruction) +{ + uint64 rt_value = extract_rt_25_24_23_22_21(instruction); + uint64 rs_value = extract_rs_20_19_18_17_16(instruction); + + std::string rt = GPR(copy(rt_value)); + std::string rs = GPR(copy(rs_value)); + + return img::format("YIELD %s, %s", rt, rs); +} + + + +/* + * nanoMIPS instruction pool organization + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * + * ┌─ P.ADDIU ─── P.RI ─── P.SYSCALL + * │ + * │ ┌─ P.TRAP + * │ │ + * │ ┌─ _POOL32A0_0 ─┼─ P.CMOVE + * │ │ │ + * │ │ └─ P.SLTU + * │ ┌─ _POOL32A0 ─┤ + * │ │ │ + * │ │ │ + * │ │ └─ _POOL32A0_1 ─── CRC32 + * │ │ + * ├─ P32A ─┤ + * │ │ ┌─ PP.LSX + * │ │ ┌─ P.LSX ─────┤ + * │ │ │ └─ PP.LSXS + * │ └─ _POOL32A7 ─┤ + * │ │ ┌─ POOL32Axf_4 + * │ └─ POOL32Axf ─┤ + * │ └─ POOL32Axf_5 + * │ + * ├─ PBAL + * │ + * ├─ P.GP.W ┌─ PP.LSX + * ┌─ P32 ─┤ │ + * │ ├─ P.GP.BH ─┴─ PP.LSXS + * │ │ + * │ ├─ P.J ─────── PP.BALRSC + * │ │ + * │ ├─ P48I + * │ │ ┌─ P.SR + * │ │ │ + * │ │ ├─ P.SHIFT + * │ │ │ + * │ ├─ P.U12 ───┼─ P.ROTX + * │ │ │ + * │ │ ├─ P.INS + * │ │ │ + * │ │ └─ P.EXT + * │ │ + * │ ├─ P.LS.U12 ── P.PREF.U12 + * │ │ + * │ ├─ P.BR1 ───── P.BR3A + * │ │ + * │ │ ┌─ P.LS.S0 ─── P16.SYSCALL + * │ │ │ + * │ │ │ ┌─ P.LL + * │ │ ├─ P.LS.S1 ─┤ + * │ │ │ └─ P.SC + * │ │ │ + * │ │ │ ┌─ P.PREFE + * MAJOR ─┤ ├─ P.LS.S9 ─┤ │ + * │ │ ├─ P.LS.E0 ─┼─ P.LLE + * │ │ │ │ + * │ │ │ └─ P.SCE + * │ │ │ + * │ │ ├─ P.LS.WM + * │ │ │ + * │ │ └─ P.LS.UAWM + * │ │ + * │ │ + * │ ├─ P.BR2 + * │ │ + * │ ├─ P.BRI + * │ │ + * │ └─ P.LUI + * │ + * │ + * │ ┌─ P16.MV ──── P16.RI ─── P16.SYSCALL + * │ │ + * │ ├─ P16.SR + * │ │ + * │ ├─ P16.SHIFT + * │ │ + * │ ├─ P16.4x4 + * │ │ + * │ ├─ P16C ────── POOL16C_0 ── POOL16C_00 + * │ │ + * └─ P16 ─┼─ P16.LB + * │ + * ├─ P16.A1 + * │ + * ├─ P16.LH + * │ + * ├─ P16.A2 ──── P.ADDIU[RS5] + * │ + * ├─ P16.ADDU + * │ + * └─ P16.BR ──┬─ P16.JRC + * │ + * └─ P16.BR1 + * + * + * (FP, DPS, and some minor instruction pools are omitted from the diagram) + * + */ + +NMD::Pool NMD::P_SYSCALL[2] = { + { instruction , 0 , 0 , 32, + 0xfffc0000, 0x00080000, &NMD::SYSCALL_32_ , 0, + 0x0 }, /* SYSCALL[32] */ + { instruction , 0 , 0 , 32, + 0xfffc0000, 0x000c0000, &NMD::HYPCALL , 0, + CP0_ | VZ_ }, /* HYPCALL */ +}; + + +NMD::Pool NMD::P_RI[4] = { + { instruction , 0 , 0 , 32, + 0xfff80000, 0x00000000, &NMD::SIGRIE , 0, + 0x0 }, /* SIGRIE */ + { pool , P_SYSCALL , 2 , 32, + 0xfff80000, 0x00080000, 0 , 0, + 0x0 }, /* P.SYSCALL */ + { instruction , 0 , 0 , 32, + 0xfff80000, 0x00100000, &NMD::BREAK_32_ , 0, + 0x0 }, /* BREAK[32] */ + { instruction , 0 , 0 , 32, + 0xfff80000, 0x00180000, &NMD::SDBBP_32_ , 0, + EJTAG_ }, /* SDBBP[32] */ +}; + + +NMD::Pool NMD::P_ADDIU[2] = { + { pool , P_RI , 4 , 32, + 0xffe00000, 0x00000000, 0 , 0, + 0x0 }, /* P.RI */ + { instruction , 0 , 0 , 32, + 0xfc000000, 0x00000000, &NMD::ADDIU_32_ , &NMD::ADDIU_32__cond , + 0x0 }, /* ADDIU[32] */ +}; + + +NMD::Pool NMD::P_TRAP[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000000, &NMD::TEQ , 0, + XMMS_ }, /* TEQ */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000400, &NMD::TNE , 0, + XMMS_ }, /* TNE */ +}; + + +NMD::Pool NMD::P_CMOVE[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000210, &NMD::MOVZ , 0, + 0x0 }, /* MOVZ */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000610, &NMD::MOVN , 0, + 0x0 }, /* MOVN */ +}; + + +NMD::Pool NMD::P_D_MT_VPE[2] = { + { instruction , 0 , 0 , 32, + 0xfc1f3fff, 0x20010ab0, &NMD::DMT , 0, + MT_ }, /* DMT */ + { instruction , 0 , 0 , 32, + 0xfc1f3fff, 0x20000ab0, &NMD::DVPE , 0, + MT_ }, /* DVPE */ +}; + + +NMD::Pool NMD::P_E_MT_VPE[2] = { + { instruction , 0 , 0 , 32, + 0xfc1f3fff, 0x20010eb0, &NMD::EMT , 0, + MT_ }, /* EMT */ + { instruction , 0 , 0 , 32, + 0xfc1f3fff, 0x20000eb0, &NMD::EVPE , 0, + MT_ }, /* EVPE */ +}; + + +NMD::Pool NMD::_P_MT_VPE[2] = { + { pool , P_D_MT_VPE , 2 , 32, + 0xfc003fff, 0x20000ab0, 0 , 0, + 0x0 }, /* P.D_MT_VPE */ + { pool , P_E_MT_VPE , 2 , 32, + 0xfc003fff, 0x20000eb0, 0 , 0, + 0x0 }, /* P.E_MT_VPE */ +}; + + +NMD::Pool NMD::P_MT_VPE[8] = { + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x200002b0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(0) */ + { pool , _P_MT_VPE , 2 , 32, + 0xfc003bff, 0x20000ab0, 0 , 0, + 0x0 }, /* _P.MT_VPE */ + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x200012b0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x20001ab0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x200022b0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x20002ab0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x200032b0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc003bff, 0x20003ab0, 0 , 0, + 0x0 }, /* P.MT_VPE~*(7) */ +}; + + +NMD::Pool NMD::P_DVP[2] = { + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20000390, &NMD::DVP , 0, + 0x0 }, /* DVP */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20000790, &NMD::EVP , 0, + 0x0 }, /* EVP */ +}; + + +NMD::Pool NMD::P_SLTU[2] = { + { pool , P_DVP , 2 , 32, + 0xfc00fbff, 0x20000390, 0 , 0, + 0x0 }, /* P.DVP */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000390, &NMD::SLTU , &NMD::SLTU_cond , + 0x0 }, /* SLTU */ +}; + + +NMD::Pool NMD::_POOL32A0[128] = { + { pool , P_TRAP , 2 , 32, + 0xfc0003ff, 0x20000000, 0 , 0, + 0x0 }, /* P.TRAP */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000008, &NMD::SEB , 0, + XMMS_ }, /* SEB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000010, &NMD::SLLV , 0, + 0x0 }, /* SLLV */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000018, &NMD::MUL_32_ , 0, + 0x0 }, /* MUL[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000020, 0 , 0, + 0x0 }, /* _POOL32A0~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000028, 0 , 0, + 0x0 }, /* _POOL32A0~*(5) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000030, &NMD::MFC0 , 0, + 0x0 }, /* MFC0 */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000038, &NMD::MFHC0 , 0, + CP0_ | MVH_ }, /* MFHC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000040, 0 , 0, + 0x0 }, /* _POOL32A0~*(8) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000048, &NMD::SEH , 0, + 0x0 }, /* SEH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000050, &NMD::SRLV , 0, + 0x0 }, /* SRLV */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000058, &NMD::MUH , 0, + 0x0 }, /* MUH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000060, 0 , 0, + 0x0 }, /* _POOL32A0~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000068, 0 , 0, + 0x0 }, /* _POOL32A0~*(13) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000070, &NMD::MTC0 , 0, + CP0_ }, /* MTC0 */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000078, &NMD::MTHC0 , 0, + CP0_ | MVH_ }, /* MTHC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000080, 0 , 0, + 0x0 }, /* _POOL32A0~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000088, 0 , 0, + 0x0 }, /* _POOL32A0~*(17) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000090, &NMD::SRAV , 0, + 0x0 }, /* SRAV */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000098, &NMD::MULU , 0, + 0x0 }, /* MULU */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000a0, 0 , 0, + 0x0 }, /* _POOL32A0~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000a8, 0 , 0, + 0x0 }, /* _POOL32A0~*(21) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000b0, &NMD::MFGC0 , 0, + CP0_ | VZ_ }, /* MFGC0 */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000b8, &NMD::MFHGC0 , 0, + CP0_ | VZ_ | MVH_ }, /* MFHGC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000c0, 0 , 0, + 0x0 }, /* _POOL32A0~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000c8, 0 , 0, + 0x0 }, /* _POOL32A0~*(25) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000d0, &NMD::ROTRV , 0, + 0x0 }, /* ROTRV */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000d8, &NMD::MUHU , 0, + 0x0 }, /* MUHU */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000e0, 0 , 0, + 0x0 }, /* _POOL32A0~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000e8, 0 , 0, + 0x0 }, /* _POOL32A0~*(29) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000f0, &NMD::MTGC0 , 0, + CP0_ | VZ_ }, /* MTGC0 */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000f8, &NMD::MTHGC0 , 0, + CP0_ | VZ_ | MVH_ }, /* MTHGC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000100, 0 , 0, + 0x0 }, /* _POOL32A0~*(32) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000108, 0 , 0, + 0x0 }, /* _POOL32A0~*(33) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000110, &NMD::ADD , 0, + XMMS_ }, /* ADD */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000118, &NMD::DIV , 0, + 0x0 }, /* DIV */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000120, 0 , 0, + 0x0 }, /* _POOL32A0~*(36) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000128, 0 , 0, + 0x0 }, /* _POOL32A0~*(37) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000130, &NMD::DMFC0 , 0, + CP0_ | MIPS64_ }, /* DMFC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000138, 0 , 0, + 0x0 }, /* _POOL32A0~*(39) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000140, 0 , 0, + 0x0 }, /* _POOL32A0~*(40) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000148, 0 , 0, + 0x0 }, /* _POOL32A0~*(41) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000150, &NMD::ADDU_32_ , 0, + 0x0 }, /* ADDU[32] */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000158, &NMD::MOD , 0, + 0x0 }, /* MOD */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000160, 0 , 0, + 0x0 }, /* _POOL32A0~*(44) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000168, 0 , 0, + 0x0 }, /* _POOL32A0~*(45) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000170, &NMD::DMTC0 , 0, + CP0_ | MIPS64_ }, /* DMTC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000178, 0 , 0, + 0x0 }, /* _POOL32A0~*(47) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000180, 0 , 0, + 0x0 }, /* _POOL32A0~*(48) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000188, 0 , 0, + 0x0 }, /* _POOL32A0~*(49) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000190, &NMD::SUB , 0, + XMMS_ }, /* SUB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000198, &NMD::DIVU , 0, + 0x0 }, /* DIVU */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001a0, 0 , 0, + 0x0 }, /* _POOL32A0~*(52) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001a8, 0 , 0, + 0x0 }, /* _POOL32A0~*(53) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001b0, &NMD::DMFGC0 , 0, + CP0_ | MIPS64_ | VZ_}, /* DMFGC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001b8, 0 , 0, + 0x0 }, /* _POOL32A0~*(55) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001c0, &NMD::RDHWR , 0, + XMMS_ }, /* RDHWR */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001c8, 0 , 0, + 0x0 }, /* _POOL32A0~*(57) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001d0, &NMD::SUBU_32_ , 0, + 0x0 }, /* SUBU[32] */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001d8, &NMD::MODU , 0, + 0x0 }, /* MODU */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001e0, 0 , 0, + 0x0 }, /* _POOL32A0~*(60) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001e8, 0 , 0, + 0x0 }, /* _POOL32A0~*(61) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001f0, &NMD::DMTGC0 , 0, + CP0_ | MIPS64_ | VZ_}, /* DMTGC0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001f8, 0 , 0, + 0x0 }, /* _POOL32A0~*(63) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000200, 0 , 0, + 0x0 }, /* _POOL32A0~*(64) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000208, 0 , 0, + 0x0 }, /* _POOL32A0~*(65) */ + { pool , P_CMOVE , 2 , 32, + 0xfc0003ff, 0x20000210, 0 , 0, + 0x0 }, /* P.CMOVE */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000218, 0 , 0, + 0x0 }, /* _POOL32A0~*(67) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000220, 0 , 0, + 0x0 }, /* _POOL32A0~*(68) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000228, &NMD::FORK , 0, + MT_ }, /* FORK */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000230, &NMD::MFTR , 0, + MT_ }, /* MFTR */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000238, &NMD::MFHTR , 0, + MT_ }, /* MFHTR */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000240, 0 , 0, + 0x0 }, /* _POOL32A0~*(72) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000248, 0 , 0, + 0x0 }, /* _POOL32A0~*(73) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000250, &NMD::AND_32_ , 0, + 0x0 }, /* AND[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000258, 0 , 0, + 0x0 }, /* _POOL32A0~*(75) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000260, 0 , 0, + 0x0 }, /* _POOL32A0~*(76) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000268, &NMD::YIELD , 0, + MT_ }, /* YIELD */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000270, &NMD::MTTR , 0, + MT_ }, /* MTTR */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000278, &NMD::MTHTR , 0, + MT_ }, /* MTHTR */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000280, 0 , 0, + 0x0 }, /* _POOL32A0~*(80) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000288, 0 , 0, + 0x0 }, /* _POOL32A0~*(81) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000290, &NMD::OR_32_ , 0, + 0x0 }, /* OR[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000298, 0 , 0, + 0x0 }, /* _POOL32A0~*(83) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002a0, 0 , 0, + 0x0 }, /* _POOL32A0~*(84) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002a8, 0 , 0, + 0x0 }, /* _POOL32A0~*(85) */ + { pool , P_MT_VPE , 8 , 32, + 0xfc0003ff, 0x200002b0, 0 , 0, + 0x0 }, /* P.MT_VPE */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002b8, 0 , 0, + 0x0 }, /* _POOL32A0~*(87) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002c0, 0 , 0, + 0x0 }, /* _POOL32A0~*(88) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002c8, 0 , 0, + 0x0 }, /* _POOL32A0~*(89) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200002d0, &NMD::NOR , 0, + 0x0 }, /* NOR */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002d8, 0 , 0, + 0x0 }, /* _POOL32A0~*(91) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002e0, 0 , 0, + 0x0 }, /* _POOL32A0~*(92) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002e8, 0 , 0, + 0x0 }, /* _POOL32A0~*(93) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002f0, 0 , 0, + 0x0 }, /* _POOL32A0~*(94) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002f8, 0 , 0, + 0x0 }, /* _POOL32A0~*(95) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000300, 0 , 0, + 0x0 }, /* _POOL32A0~*(96) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000308, 0 , 0, + 0x0 }, /* _POOL32A0~*(97) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000310, &NMD::XOR_32_ , 0, + 0x0 }, /* XOR[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000318, 0 , 0, + 0x0 }, /* _POOL32A0~*(99) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000320, 0 , 0, + 0x0 }, /* _POOL32A0~*(100) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000328, 0 , 0, + 0x0 }, /* _POOL32A0~*(101) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000330, 0 , 0, + 0x0 }, /* _POOL32A0~*(102) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000338, 0 , 0, + 0x0 }, /* _POOL32A0~*(103) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000340, 0 , 0, + 0x0 }, /* _POOL32A0~*(104) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000348, 0 , 0, + 0x0 }, /* _POOL32A0~*(105) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000350, &NMD::SLT , 0, + 0x0 }, /* SLT */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000358, 0 , 0, + 0x0 }, /* _POOL32A0~*(107) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000360, 0 , 0, + 0x0 }, /* _POOL32A0~*(108) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000368, 0 , 0, + 0x0 }, /* _POOL32A0~*(109) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000370, 0 , 0, + 0x0 }, /* _POOL32A0~*(110) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000378, 0 , 0, + 0x0 }, /* _POOL32A0~*(111) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000380, 0 , 0, + 0x0 }, /* _POOL32A0~*(112) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000388, 0 , 0, + 0x0 }, /* _POOL32A0~*(113) */ + { pool , P_SLTU , 2 , 32, + 0xfc0003ff, 0x20000390, 0 , 0, + 0x0 }, /* P.SLTU */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000398, 0 , 0, + 0x0 }, /* _POOL32A0~*(115) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003a0, 0 , 0, + 0x0 }, /* _POOL32A0~*(116) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003a8, 0 , 0, + 0x0 }, /* _POOL32A0~*(117) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003b0, 0 , 0, + 0x0 }, /* _POOL32A0~*(118) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003b8, 0 , 0, + 0x0 }, /* _POOL32A0~*(119) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003c0, 0 , 0, + 0x0 }, /* _POOL32A0~*(120) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003c8, 0 , 0, + 0x0 }, /* _POOL32A0~*(121) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200003d0, &NMD::SOV , 0, + 0x0 }, /* SOV */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003d8, 0 , 0, + 0x0 }, /* _POOL32A0~*(123) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003e0, 0 , 0, + 0x0 }, /* _POOL32A0~*(124) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003e8, 0 , 0, + 0x0 }, /* _POOL32A0~*(125) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003f0, 0 , 0, + 0x0 }, /* _POOL32A0~*(126) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003f8, 0 , 0, + 0x0 }, /* _POOL32A0~*(127) */ +}; + + +NMD::Pool NMD::ADDQ__S__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000000d, &NMD::ADDQ_PH , 0, + DSP_ }, /* ADDQ.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000040d, &NMD::ADDQ_S_PH , 0, + DSP_ }, /* ADDQ_S.PH */ +}; + + +NMD::Pool NMD::MUL__S__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000002d, &NMD::MUL_PH , 0, + DSP_ }, /* MUL.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000042d, &NMD::MUL_S_PH , 0, + DSP_ }, /* MUL_S.PH */ +}; + + +NMD::Pool NMD::ADDQH__R__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000004d, &NMD::ADDQH_PH , 0, + DSP_ }, /* ADDQH.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000044d, &NMD::ADDQH_R_PH , 0, + DSP_ }, /* ADDQH_R.PH */ +}; + + +NMD::Pool NMD::ADDQH__R__W[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000008d, &NMD::ADDQH_W , 0, + DSP_ }, /* ADDQH.W */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000048d, &NMD::ADDQH_R_W , 0, + DSP_ }, /* ADDQH_R.W */ +}; + + +NMD::Pool NMD::ADDU__S__QB[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200000cd, &NMD::ADDU_QB , 0, + DSP_ }, /* ADDU.QB */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200004cd, &NMD::ADDU_S_QB , 0, + DSP_ }, /* ADDU_S.QB */ +}; + + +NMD::Pool NMD::ADDU__S__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000010d, &NMD::ADDU_PH , 0, + DSP_ }, /* ADDU.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000050d, &NMD::ADDU_S_PH , 0, + DSP_ }, /* ADDU_S.PH */ +}; + + +NMD::Pool NMD::ADDUH__R__QB[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000014d, &NMD::ADDUH_QB , 0, + DSP_ }, /* ADDUH.QB */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000054d, &NMD::ADDUH_R_QB , 0, + DSP_ }, /* ADDUH_R.QB */ +}; + + +NMD::Pool NMD::SHRAV__R__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000018d, &NMD::SHRAV_PH , 0, + DSP_ }, /* SHRAV.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000058d, &NMD::SHRAV_R_PH , 0, + DSP_ }, /* SHRAV_R.PH */ +}; + + +NMD::Pool NMD::SHRAV__R__QB[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200001cd, &NMD::SHRAV_QB , 0, + DSP_ }, /* SHRAV.QB */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200005cd, &NMD::SHRAV_R_QB , 0, + DSP_ }, /* SHRAV_R.QB */ +}; + + +NMD::Pool NMD::SUBQ__S__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000020d, &NMD::SUBQ_PH , 0, + DSP_ }, /* SUBQ.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000060d, &NMD::SUBQ_S_PH , 0, + DSP_ }, /* SUBQ_S.PH */ +}; + + +NMD::Pool NMD::SUBQH__R__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000024d, &NMD::SUBQH_PH , 0, + DSP_ }, /* SUBQH.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000064d, &NMD::SUBQH_R_PH , 0, + DSP_ }, /* SUBQH_R.PH */ +}; + + +NMD::Pool NMD::SUBQH__R__W[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000028d, &NMD::SUBQH_W , 0, + DSP_ }, /* SUBQH.W */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000068d, &NMD::SUBQH_R_W , 0, + DSP_ }, /* SUBQH_R.W */ +}; + + +NMD::Pool NMD::SUBU__S__QB[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200002cd, &NMD::SUBU_QB , 0, + DSP_ }, /* SUBU.QB */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200006cd, &NMD::SUBU_S_QB , 0, + DSP_ }, /* SUBU_S.QB */ +}; + + +NMD::Pool NMD::SUBU__S__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000030d, &NMD::SUBU_PH , 0, + DSP_ }, /* SUBU.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000070d, &NMD::SUBU_S_PH , 0, + DSP_ }, /* SUBU_S.PH */ +}; + + +NMD::Pool NMD::SHRA__R__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000335, &NMD::SHRA_PH , 0, + DSP_ }, /* SHRA.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000735, &NMD::SHRA_R_PH , 0, + DSP_ }, /* SHRA_R.PH */ +}; + + +NMD::Pool NMD::SUBUH__R__QB[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000034d, &NMD::SUBUH_QB , 0, + DSP_ }, /* SUBUH.QB */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000074d, &NMD::SUBUH_R_QB , 0, + DSP_ }, /* SUBUH_R.QB */ +}; + + +NMD::Pool NMD::SHLLV__S__PH[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000038d, &NMD::SHLLV_PH , 0, + DSP_ }, /* SHLLV.PH */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x2000078d, &NMD::SHLLV_S_PH , 0, + DSP_ }, /* SHLLV_S.PH */ +}; + + +NMD::Pool NMD::SHLL__S__PH[4] = { + { instruction , 0 , 0 , 32, + 0xfc000fff, 0x200003b5, &NMD::SHLL_PH , 0, + DSP_ }, /* SHLL.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x200007b5, 0 , 0, + 0x0 }, /* SHLL[_S].PH~*(1) */ + { instruction , 0 , 0 , 32, + 0xfc000fff, 0x20000bb5, &NMD::SHLL_S_PH , 0, + DSP_ }, /* SHLL_S.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x20000fb5, 0 , 0, + 0x0 }, /* SHLL[_S].PH~*(3) */ +}; + + +NMD::Pool NMD::PRECR_SRA__R__PH_W[2] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200003cd, &NMD::PRECR_SRA_PH_W , 0, + DSP_ }, /* PRECR_SRA.PH.W */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200007cd, &NMD::PRECR_SRA_R_PH_W , 0, + DSP_ }, /* PRECR_SRA_R.PH.W */ +}; + + +NMD::Pool NMD::_POOL32A5[128] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000005, &NMD::CMP_EQ_PH , 0, + DSP_ }, /* CMP.EQ.PH */ + { pool , ADDQ__S__PH , 2 , 32, + 0xfc0003ff, 0x2000000d, 0 , 0, + 0x0 }, /* ADDQ[_S].PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000015, 0 , 0, + 0x0 }, /* _POOL32A5~*(2) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x2000001d, &NMD::SHILO , 0, + DSP_ }, /* SHILO */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000025, &NMD::MULEQ_S_W_PHL , 0, + DSP_ }, /* MULEQ_S.W.PHL */ + { pool , MUL__S__PH , 2 , 32, + 0xfc0003ff, 0x2000002d, 0 , 0, + 0x0 }, /* MUL[_S].PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000035, 0 , 0, + 0x0 }, /* _POOL32A5~*(6) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x2000003d, &NMD::REPL_PH , 0, + DSP_ }, /* REPL.PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000045, &NMD::CMP_LT_PH , 0, + DSP_ }, /* CMP.LT.PH */ + { pool , ADDQH__R__PH , 2 , 32, + 0xfc0003ff, 0x2000004d, 0 , 0, + 0x0 }, /* ADDQH[_R].PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000055, 0 , 0, + 0x0 }, /* _POOL32A5~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000005d, 0 , 0, + 0x0 }, /* _POOL32A5~*(11) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000065, &NMD::MULEQ_S_W_PHR , 0, + DSP_ }, /* MULEQ_S.W.PHR */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x2000006d, &NMD::PRECR_QB_PH , 0, + DSP_ }, /* PRECR.QB.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000075, 0 , 0, + 0x0 }, /* _POOL32A5~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000007d, 0 , 0, + 0x0 }, /* _POOL32A5~*(15) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000085, &NMD::CMP_LE_PH , 0, + DSP_ }, /* CMP.LE.PH */ + { pool , ADDQH__R__W , 2 , 32, + 0xfc0003ff, 0x2000008d, 0 , 0, + 0x0 }, /* ADDQH[_R].W */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000095, &NMD::MULEU_S_PH_QBL , 0, + DSP_ }, /* MULEU_S.PH.QBL */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000009d, 0 , 0, + 0x0 }, /* _POOL32A5~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000a5, 0 , 0, + 0x0 }, /* _POOL32A5~*(20) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000ad, &NMD::PRECRQ_QB_PH , 0, + DSP_ }, /* PRECRQ.QB.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000b5, 0 , 0, + 0x0 }, /* _POOL32A5~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000bd, 0 , 0, + 0x0 }, /* _POOL32A5~*(23) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000c5, &NMD::CMPGU_EQ_QB , 0, + DSP_ }, /* CMPGU.EQ.QB */ + { pool , ADDU__S__QB , 2 , 32, + 0xfc0003ff, 0x200000cd, 0 , 0, + 0x0 }, /* ADDU[_S].QB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000d5, &NMD::MULEU_S_PH_QBR , 0, + DSP_ }, /* MULEU_S.PH.QBR */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000dd, 0 , 0, + 0x0 }, /* _POOL32A5~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000e5, 0 , 0, + 0x0 }, /* _POOL32A5~*(28) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200000ed, &NMD::PRECRQ_PH_W , 0, + DSP_ }, /* PRECRQ.PH.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000f5, 0 , 0, + 0x0 }, /* _POOL32A5~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200000fd, 0 , 0, + 0x0 }, /* _POOL32A5~*(31) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000105, &NMD::CMPGU_LT_QB , 0, + DSP_ }, /* CMPGU.LT.QB */ + { pool , ADDU__S__PH , 2 , 32, + 0xfc0003ff, 0x2000010d, 0 , 0, + 0x0 }, /* ADDU[_S].PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000115, &NMD::MULQ_RS_PH , 0, + DSP_ }, /* MULQ_RS.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000011d, 0 , 0, + 0x0 }, /* _POOL32A5~*(35) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000125, 0 , 0, + 0x0 }, /* _POOL32A5~*(36) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x2000012d, &NMD::PRECRQ_RS_PH_W , 0, + DSP_ }, /* PRECRQ_RS.PH.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000135, 0 , 0, + 0x0 }, /* _POOL32A5~*(38) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000013d, 0 , 0, + 0x0 }, /* _POOL32A5~*(39) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000145, &NMD::CMPGU_LE_QB , 0, + DSP_ }, /* CMPGU.LE.QB */ + { pool , ADDUH__R__QB , 2 , 32, + 0xfc0003ff, 0x2000014d, 0 , 0, + 0x0 }, /* ADDUH[_R].QB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000155, &NMD::MULQ_S_PH , 0, + DSP_ }, /* MULQ_S.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000015d, 0 , 0, + 0x0 }, /* _POOL32A5~*(43) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000165, 0 , 0, + 0x0 }, /* _POOL32A5~*(44) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x2000016d, &NMD::PRECRQU_S_QB_PH , 0, + DSP_ }, /* PRECRQU_S.QB.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000175, 0 , 0, + 0x0 }, /* _POOL32A5~*(46) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000017d, 0 , 0, + 0x0 }, /* _POOL32A5~*(47) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000185, &NMD::CMPGDU_EQ_QB , 0, + DSP_ }, /* CMPGDU.EQ.QB */ + { pool , SHRAV__R__PH , 2 , 32, + 0xfc0003ff, 0x2000018d, 0 , 0, + 0x0 }, /* SHRAV[_R].PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000195, &NMD::MULQ_RS_W , 0, + DSP_ }, /* MULQ_RS.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000019d, 0 , 0, + 0x0 }, /* _POOL32A5~*(51) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001a5, 0 , 0, + 0x0 }, /* _POOL32A5~*(52) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001ad, &NMD::PACKRL_PH , 0, + DSP_ }, /* PACKRL.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001b5, 0 , 0, + 0x0 }, /* _POOL32A5~*(54) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001bd, 0 , 0, + 0x0 }, /* _POOL32A5~*(55) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001c5, &NMD::CMPGDU_LT_QB , 0, + DSP_ }, /* CMPGDU.LT.QB */ + { pool , SHRAV__R__QB , 2 , 32, + 0xfc0003ff, 0x200001cd, 0 , 0, + 0x0 }, /* SHRAV[_R].QB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001d5, &NMD::MULQ_S_W , 0, + DSP_ }, /* MULQ_S.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001dd, 0 , 0, + 0x0 }, /* _POOL32A5~*(59) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001e5, 0 , 0, + 0x0 }, /* _POOL32A5~*(60) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200001ed, &NMD::PICK_QB , 0, + DSP_ }, /* PICK.QB */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001f5, 0 , 0, + 0x0 }, /* _POOL32A5~*(62) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200001fd, 0 , 0, + 0x0 }, /* _POOL32A5~*(63) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000205, &NMD::CMPGDU_LE_QB , 0, + DSP_ }, /* CMPGDU.LE.QB */ + { pool , SUBQ__S__PH , 2 , 32, + 0xfc0003ff, 0x2000020d, 0 , 0, + 0x0 }, /* SUBQ[_S].PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000215, &NMD::APPEND , 0, + DSP_ }, /* APPEND */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000021d, 0 , 0, + 0x0 }, /* _POOL32A5~*(67) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000225, 0 , 0, + 0x0 }, /* _POOL32A5~*(68) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x2000022d, &NMD::PICK_PH , 0, + DSP_ }, /* PICK.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000235, 0 , 0, + 0x0 }, /* _POOL32A5~*(70) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000023d, 0 , 0, + 0x0 }, /* _POOL32A5~*(71) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000245, &NMD::CMPU_EQ_QB , 0, + DSP_ }, /* CMPU.EQ.QB */ + { pool , SUBQH__R__PH , 2 , 32, + 0xfc0003ff, 0x2000024d, 0 , 0, + 0x0 }, /* SUBQH[_R].PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000255, &NMD::PREPEND , 0, + DSP_ }, /* PREPEND */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000025d, 0 , 0, + 0x0 }, /* _POOL32A5~*(75) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000265, 0 , 0, + 0x0 }, /* _POOL32A5~*(76) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000026d, 0 , 0, + 0x0 }, /* _POOL32A5~*(77) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000275, 0 , 0, + 0x0 }, /* _POOL32A5~*(78) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000027d, 0 , 0, + 0x0 }, /* _POOL32A5~*(79) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000285, &NMD::CMPU_LT_QB , 0, + DSP_ }, /* CMPU.LT.QB */ + { pool , SUBQH__R__W , 2 , 32, + 0xfc0003ff, 0x2000028d, 0 , 0, + 0x0 }, /* SUBQH[_R].W */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000295, &NMD::MODSUB , 0, + DSP_ }, /* MODSUB */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000029d, 0 , 0, + 0x0 }, /* _POOL32A5~*(83) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002a5, 0 , 0, + 0x0 }, /* _POOL32A5~*(84) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002ad, 0 , 0, + 0x0 }, /* _POOL32A5~*(85) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002b5, 0 , 0, + 0x0 }, /* _POOL32A5~*(86) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002bd, 0 , 0, + 0x0 }, /* _POOL32A5~*(87) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200002c5, &NMD::CMPU_LE_QB , 0, + DSP_ }, /* CMPU.LE.QB */ + { pool , SUBU__S__QB , 2 , 32, + 0xfc0003ff, 0x200002cd, 0 , 0, + 0x0 }, /* SUBU[_S].QB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200002d5, &NMD::SHRAV_R_W , 0, + DSP_ }, /* SHRAV_R.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002dd, 0 , 0, + 0x0 }, /* _POOL32A5~*(91) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002e5, 0 , 0, + 0x0 }, /* _POOL32A5~*(92) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002ed, 0 , 0, + 0x0 }, /* _POOL32A5~*(93) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200002f5, &NMD::SHRA_R_W , 0, + DSP_ }, /* SHRA_R.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200002fd, 0 , 0, + 0x0 }, /* _POOL32A5~*(95) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000305, &NMD::ADDQ_S_W , 0, + DSP_ }, /* ADDQ_S.W */ + { pool , SUBU__S__PH , 2 , 32, + 0xfc0003ff, 0x2000030d, 0 , 0, + 0x0 }, /* SUBU[_S].PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000315, &NMD::SHRLV_PH , 0, + DSP_ }, /* SHRLV.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000031d, 0 , 0, + 0x0 }, /* _POOL32A5~*(99) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000325, 0 , 0, + 0x0 }, /* _POOL32A5~*(100) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000032d, 0 , 0, + 0x0 }, /* _POOL32A5~*(101) */ + { pool , SHRA__R__PH , 2 , 32, + 0xfc0003ff, 0x20000335, 0 , 0, + 0x0 }, /* SHRA[_R].PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000033d, 0 , 0, + 0x0 }, /* _POOL32A5~*(103) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000345, &NMD::SUBQ_S_W , 0, + DSP_ }, /* SUBQ_S.W */ + { pool , SUBUH__R__QB , 2 , 32, + 0xfc0003ff, 0x2000034d, 0 , 0, + 0x0 }, /* SUBUH[_R].QB */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000355, &NMD::SHRLV_QB , 0, + DSP_ }, /* SHRLV.QB */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000035d, 0 , 0, + 0x0 }, /* _POOL32A5~*(107) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000365, 0 , 0, + 0x0 }, /* _POOL32A5~*(108) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000036d, 0 , 0, + 0x0 }, /* _POOL32A5~*(109) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x20000375, 0 , 0, + 0x0 }, /* _POOL32A5~*(110) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000037d, 0 , 0, + 0x0 }, /* _POOL32A5~*(111) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000385, &NMD::ADDSC , 0, + DSP_ }, /* ADDSC */ + { pool , SHLLV__S__PH , 2 , 32, + 0xfc0003ff, 0x2000038d, 0 , 0, + 0x0 }, /* SHLLV[_S].PH */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x20000395, &NMD::SHLLV_QB , 0, + DSP_ }, /* SHLLV.QB */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x2000039d, 0 , 0, + 0x0 }, /* _POOL32A5~*(115) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003a5, 0 , 0, + 0x0 }, /* _POOL32A5~*(116) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003ad, 0 , 0, + 0x0 }, /* _POOL32A5~*(117) */ + { pool , SHLL__S__PH , 4 , 32, + 0xfc0003ff, 0x200003b5, 0 , 0, + 0x0 }, /* SHLL[_S].PH */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003bd, 0 , 0, + 0x0 }, /* _POOL32A5~*(119) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200003c5, &NMD::ADDWC , 0, + DSP_ }, /* ADDWC */ + { pool , PRECR_SRA__R__PH_W , 2 , 32, + 0xfc0003ff, 0x200003cd, 0 , 0, + 0x0 }, /* PRECR_SRA[_R].PH.W */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200003d5, &NMD::SHLLV_S_W , 0, + DSP_ }, /* SHLLV_S.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003dd, 0 , 0, + 0x0 }, /* _POOL32A5~*(123) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003e5, 0 , 0, + 0x0 }, /* _POOL32A5~*(124) */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003ed, 0 , 0, + 0x0 }, /* _POOL32A5~*(125) */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0x200003f5, &NMD::SHLL_S_W , 0, + DSP_ }, /* SHLL_S.W */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0x200003fd, 0 , 0, + 0x0 }, /* _POOL32A5~*(127) */ +}; + + +NMD::Pool NMD::PP_LSX[16] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000007, &NMD::LBX , 0, + 0x0 }, /* LBX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000087, &NMD::SBX , 0, + XMMS_ }, /* SBX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000107, &NMD::LBUX , 0, + 0x0 }, /* LBUX */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0x20000187, 0 , 0, + 0x0 }, /* PP.LSX~*(3) */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000207, &NMD::LHX , 0, + 0x0 }, /* LHX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000287, &NMD::SHX , 0, + XMMS_ }, /* SHX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000307, &NMD::LHUX , 0, + 0x0 }, /* LHUX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000387, &NMD::LWUX , 0, + MIPS64_ }, /* LWUX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000407, &NMD::LWX , 0, + 0x0 }, /* LWX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000487, &NMD::SWX , 0, + XMMS_ }, /* SWX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000507, &NMD::LWC1X , 0, + CP1_ }, /* LWC1X */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000587, &NMD::SWC1X , 0, + CP1_ }, /* SWC1X */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000607, &NMD::LDX , 0, + MIPS64_ }, /* LDX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000687, &NMD::SDX , 0, + MIPS64_ }, /* SDX */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000707, &NMD::LDC1X , 0, + CP1_ }, /* LDC1X */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000787, &NMD::SDC1X , 0, + CP1_ }, /* SDC1X */ +}; + + +NMD::Pool NMD::PP_LSXS[16] = { + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0x20000047, 0 , 0, + 0x0 }, /* PP.LSXS~*(0) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0x200000c7, 0 , 0, + 0x0 }, /* PP.LSXS~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0x20000147, 0 , 0, + 0x0 }, /* PP.LSXS~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0x200001c7, 0 , 0, + 0x0 }, /* PP.LSXS~*(3) */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000247, &NMD::LHXS , 0, + 0x0 }, /* LHXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200002c7, &NMD::SHXS , 0, + XMMS_ }, /* SHXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000347, &NMD::LHUXS , 0, + 0x0 }, /* LHUXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200003c7, &NMD::LWUXS , 0, + MIPS64_ }, /* LWUXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000447, &NMD::LWXS_32_ , 0, + 0x0 }, /* LWXS[32] */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200004c7, &NMD::SWXS , 0, + XMMS_ }, /* SWXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000547, &NMD::LWC1XS , 0, + CP1_ }, /* LWC1XS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200005c7, &NMD::SWC1XS , 0, + CP1_ }, /* SWC1XS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000647, &NMD::LDXS , 0, + MIPS64_ }, /* LDXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200006c7, &NMD::SDXS , 0, + MIPS64_ }, /* SDXS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x20000747, &NMD::LDC1XS , 0, + CP1_ }, /* LDC1XS */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0x200007c7, &NMD::SDC1XS , 0, + CP1_ }, /* SDC1XS */ +}; + + +NMD::Pool NMD::P_LSX[2] = { + { pool , PP_LSX , 16 , 32, + 0xfc00007f, 0x20000007, 0 , 0, + 0x0 }, /* PP.LSX */ + { pool , PP_LSXS , 16 , 32, + 0xfc00007f, 0x20000047, 0 , 0, + 0x0 }, /* PP.LSXS */ +}; + + +NMD::Pool NMD::POOL32Axf_1_0[4] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000007f, &NMD::MFHI_DSP_ , 0, + DSP_ }, /* MFHI[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000107f, &NMD::MFLO_DSP_ , 0, + DSP_ }, /* MFLO[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000207f, &NMD::MTHI_DSP_ , 0, + DSP_ }, /* MTHI[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000307f, &NMD::MTLO_DSP_ , 0, + DSP_ }, /* MTLO[DSP] */ +}; + + +NMD::Pool NMD::POOL32Axf_1_1[4] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000027f, &NMD::MTHLIP , 0, + DSP_ }, /* MTHLIP */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000127f, &NMD::SHILOV , 0, + DSP_ }, /* SHILOV */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0x2000227f, 0 , 0, + 0x0 }, /* POOL32Axf_1_1~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0x2000327f, 0 , 0, + 0x0 }, /* POOL32Axf_1_1~*(3) */ +}; + + +NMD::Pool NMD::POOL32Axf_1_3[4] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000067f, &NMD::RDDSP , 0, + DSP_ }, /* RDDSP */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000167f, &NMD::WRDSP , 0, + DSP_ }, /* WRDSP */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000267f, &NMD::EXTP , 0, + DSP_ }, /* EXTP */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x2000367f, &NMD::EXTPDP , 0, + DSP_ }, /* EXTPDP */ +}; + + +NMD::Pool NMD::POOL32Axf_1_4[2] = { + { instruction , 0 , 0 , 32, + 0xfc001fff, 0x2000087f, &NMD::SHLL_QB , 0, + DSP_ }, /* SHLL.QB */ + { instruction , 0 , 0 , 32, + 0xfc001fff, 0x2000187f, &NMD::SHRL_QB , 0, + DSP_ }, /* SHRL.QB */ +}; + + +NMD::Pool NMD::MAQ_S_A__W_PHR[2] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20000a7f, &NMD::MAQ_S_W_PHR , 0, + DSP_ }, /* MAQ_S.W.PHR */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20002a7f, &NMD::MAQ_SA_W_PHR , 0, + DSP_ }, /* MAQ_SA.W.PHR */ +}; + + +NMD::Pool NMD::MAQ_S_A__W_PHL[2] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20001a7f, &NMD::MAQ_S_W_PHL , 0, + DSP_ }, /* MAQ_S.W.PHL */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20003a7f, &NMD::MAQ_SA_W_PHL , 0, + DSP_ }, /* MAQ_SA.W.PHL */ +}; + + +NMD::Pool NMD::POOL32Axf_1_5[2] = { + { pool , MAQ_S_A__W_PHR , 2 , 32, + 0xfc001fff, 0x20000a7f, 0 , 0, + 0x0 }, /* MAQ_S[A].W.PHR */ + { pool , MAQ_S_A__W_PHL , 2 , 32, + 0xfc001fff, 0x20001a7f, 0 , 0, + 0x0 }, /* MAQ_S[A].W.PHL */ +}; + + +NMD::Pool NMD::POOL32Axf_1_7[4] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20000e7f, &NMD::EXTR_W , 0, + DSP_ }, /* EXTR.W */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20001e7f, &NMD::EXTR_R_W , 0, + DSP_ }, /* EXTR_R.W */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20002e7f, &NMD::EXTR_RS_W , 0, + DSP_ }, /* EXTR_RS.W */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20003e7f, &NMD::EXTR_S_H , 0, + DSP_ }, /* EXTR_S.H */ +}; + + +NMD::Pool NMD::POOL32Axf_1[8] = { + { pool , POOL32Axf_1_0 , 4 , 32, + 0xfc000fff, 0x2000007f, 0 , 0, + 0x0 }, /* POOL32Axf_1_0 */ + { pool , POOL32Axf_1_1 , 4 , 32, + 0xfc000fff, 0x2000027f, 0 , 0, + 0x0 }, /* POOL32Axf_1_1 */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x2000047f, 0 , 0, + 0x0 }, /* POOL32Axf_1~*(2) */ + { pool , POOL32Axf_1_3 , 4 , 32, + 0xfc000fff, 0x2000067f, 0 , 0, + 0x0 }, /* POOL32Axf_1_3 */ + { pool , POOL32Axf_1_4 , 2 , 32, + 0xfc000fff, 0x2000087f, 0 , 0, + 0x0 }, /* POOL32Axf_1_4 */ + { pool , POOL32Axf_1_5 , 2 , 32, + 0xfc000fff, 0x20000a7f, 0 , 0, + 0x0 }, /* POOL32Axf_1_5 */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x20000c7f, 0 , 0, + 0x0 }, /* POOL32Axf_1~*(6) */ + { pool , POOL32Axf_1_7 , 4 , 32, + 0xfc000fff, 0x20000e7f, 0 , 0, + 0x0 }, /* POOL32Axf_1_7 */ +}; + + +NMD::Pool NMD::POOL32Axf_2_DSP__0_7[8] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200000bf, &NMD::DPA_W_PH , 0, + DSP_ }, /* DPA.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200002bf, &NMD::DPAQ_S_W_PH , 0, + DSP_ }, /* DPAQ_S.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200004bf, &NMD::DPS_W_PH , 0, + DSP_ }, /* DPS.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200006bf, &NMD::DPSQ_S_W_PH , 0, + DSP_ }, /* DPSQ_S.W.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0x200008bf, 0 , 0, + 0x0 }, /* POOL32Axf_2(DSP)_0_7~*(4) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20000abf, &NMD::MADD_DSP_ , 0, + DSP_ }, /* MADD[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20000cbf, &NMD::MULT_DSP_ , 0, + DSP_ }, /* MULT[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20000ebf, &NMD::EXTRV_W , 0, + DSP_ }, /* EXTRV.W */ +}; + + +NMD::Pool NMD::POOL32Axf_2_DSP__8_15[8] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200010bf, &NMD::DPAX_W_PH , 0, + DSP_ }, /* DPAX.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200012bf, &NMD::DPAQ_SA_L_W , 0, + DSP_ }, /* DPAQ_SA.L.W */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200014bf, &NMD::DPSX_W_PH , 0, + DSP_ }, /* DPSX.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200016bf, &NMD::DPSQ_SA_L_W , 0, + DSP_ }, /* DPSQ_SA.L.W */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0x200018bf, 0 , 0, + 0x0 }, /* POOL32Axf_2(DSP)_8_15~*(4) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20001abf, &NMD::MADDU_DSP_ , 0, + DSP_ }, /* MADDU[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20001cbf, &NMD::MULTU_DSP_ , 0, + DSP_ }, /* MULTU[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20001ebf, &NMD::EXTRV_R_W , 0, + DSP_ }, /* EXTRV_R.W */ +}; + + +NMD::Pool NMD::POOL32Axf_2_DSP__16_23[8] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200020bf, &NMD::DPAU_H_QBL , 0, + DSP_ }, /* DPAU.H.QBL */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200022bf, &NMD::DPAQX_S_W_PH , 0, + DSP_ }, /* DPAQX_S.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200024bf, &NMD::DPSU_H_QBL , 0, + DSP_ }, /* DPSU.H.QBL */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200026bf, &NMD::DPSQX_S_W_PH , 0, + DSP_ }, /* DPSQX_S.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200028bf, &NMD::EXTPV , 0, + DSP_ }, /* EXTPV */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20002abf, &NMD::MSUB_DSP_ , 0, + DSP_ }, /* MSUB[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20002cbf, &NMD::MULSA_W_PH , 0, + DSP_ }, /* MULSA.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20002ebf, &NMD::EXTRV_RS_W , 0, + DSP_ }, /* EXTRV_RS.W */ +}; + + +NMD::Pool NMD::POOL32Axf_2_DSP__24_31[8] = { + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200030bf, &NMD::DPAU_H_QBR , 0, + DSP_ }, /* DPAU.H.QBR */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200032bf, &NMD::DPAQX_SA_W_PH , 0, + DSP_ }, /* DPAQX_SA.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200034bf, &NMD::DPSU_H_QBR , 0, + DSP_ }, /* DPSU.H.QBR */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200036bf, &NMD::DPSQX_SA_W_PH , 0, + DSP_ }, /* DPSQX_SA.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x200038bf, &NMD::EXTPDPV , 0, + DSP_ }, /* EXTPDPV */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20003abf, &NMD::MSUBU_DSP_ , 0, + DSP_ }, /* MSUBU[DSP] */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20003cbf, &NMD::MULSAQ_S_W_PH , 0, + DSP_ }, /* MULSAQ_S.W.PH */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0x20003ebf, &NMD::EXTRV_S_H , 0, + DSP_ }, /* EXTRV_S.H */ +}; + + +NMD::Pool NMD::POOL32Axf_2[4] = { + { pool , POOL32Axf_2_DSP__0_7, 8 , 32, + 0xfc0031ff, 0x200000bf, 0 , 0, + 0x0 }, /* POOL32Axf_2(DSP)_0_7 */ + { pool , POOL32Axf_2_DSP__8_15, 8 , 32, + 0xfc0031ff, 0x200010bf, 0 , 0, + 0x0 }, /* POOL32Axf_2(DSP)_8_15 */ + { pool , POOL32Axf_2_DSP__16_23, 8 , 32, + 0xfc0031ff, 0x200020bf, 0 , 0, + 0x0 }, /* POOL32Axf_2(DSP)_16_23 */ + { pool , POOL32Axf_2_DSP__24_31, 8 , 32, + 0xfc0031ff, 0x200030bf, 0 , 0, + 0x0 }, /* POOL32Axf_2(DSP)_24_31 */ +}; + + +NMD::Pool NMD::POOL32Axf_4[128] = { + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000013f, &NMD::ABSQ_S_QB , 0, + DSP_ }, /* ABSQ_S.QB */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000033f, &NMD::REPLV_PH , 0, + DSP_ }, /* REPLV.PH */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000053f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000073f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000093f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20000b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20000d3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20000f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(7) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000113f, &NMD::ABSQ_S_PH , 0, + DSP_ }, /* ABSQ_S.PH */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000133f, &NMD::REPLV_QB , 0, + DSP_ }, /* REPLV.QB */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000153f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000173f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(11) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000193f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20001b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20001d3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20001f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(15) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000213f, &NMD::ABSQ_S_W , 0, + DSP_ }, /* ABSQ_S.W */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000233f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(17) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000253f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000273f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000293f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20002b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20002d3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20002f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000313f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000333f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000353f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000373f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000393f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20003b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20003d3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20003f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(31) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000413f, &NMD::INSV , 0, + DSP_ }, /* INSV */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000433f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(33) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000453f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(34) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000473f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(35) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000493f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(36) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20004b3f, &NMD::CLO , 0, + XMMS_ }, /* CLO */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20004d3f, &NMD::MFC2 , 0, + CP2_ }, /* MFC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20004f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(39) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000513f, &NMD::PRECEQ_W_PHL , 0, + DSP_ }, /* PRECEQ.W.PHL */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000533f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(41) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000553f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(42) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000573f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(43) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000593f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(44) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20005b3f, &NMD::CLZ , 0, + XMMS_ }, /* CLZ */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20005d3f, &NMD::MTC2 , 0, + CP2_ }, /* MTC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20005f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(47) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000613f, &NMD::PRECEQ_W_PHR , 0, + DSP_ }, /* PRECEQ.W.PHR */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000633f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(49) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000653f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(50) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000673f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(51) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000693f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(52) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20006b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(53) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20006d3f, &NMD::DMFC2 , 0, + CP2_ }, /* DMFC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20006f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(55) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000713f, &NMD::PRECEQU_PH_QBL , 0, + DSP_ }, /* PRECEQU.PH.QBL */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000733f, &NMD::PRECEQU_PH_QBLA , 0, + DSP_ }, /* PRECEQU.PH.QBLA */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000753f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(58) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000773f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(59) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000793f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(60) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20007b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(61) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20007d3f, &NMD::DMTC2 , 0, + CP2_ }, /* DMTC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20007f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(63) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000813f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(64) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000833f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(65) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000853f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(66) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000873f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(67) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000893f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(68) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20008b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(69) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20008d3f, &NMD::MFHC2 , 0, + CP2_ }, /* MFHC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20008f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(71) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000913f, &NMD::PRECEQU_PH_QBR , 0, + DSP_ }, /* PRECEQU.PH.QBR */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000933f, &NMD::PRECEQU_PH_QBRA , 0, + DSP_ }, /* PRECEQU.PH.QBRA */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000953f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(74) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000973f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(75) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000993f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(76) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20009b3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(77) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x20009d3f, &NMD::MTHC2 , 0, + CP2_ }, /* MTHC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20009f3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(79) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000a13f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(80) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000a33f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(81) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000a53f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(82) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000a73f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(83) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000a93f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(84) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ab3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(85) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ad3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(86) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000af3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(87) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000b13f, &NMD::PRECEU_PH_QBL , 0, + DSP_ }, /* PRECEU.PH.QBL */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000b33f, &NMD::PRECEU_PH_QBLA , 0, + DSP_ }, /* PRECEU.PH.QBLA */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000b53f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(90) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000b73f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(91) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000b93f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(92) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000bb3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(93) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000bd3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(94) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000bf3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(95) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c13f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(96) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c33f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(97) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c53f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(98) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c73f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(99) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c93f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(100) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000cb3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(101) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000cd3f, &NMD::CFC2 , 0, + CP2_ }, /* CFC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000cf3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(103) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000d13f, &NMD::PRECEU_PH_QBR , 0, + DSP_ }, /* PRECEU.PH.QBR */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000d33f, &NMD::PRECEU_PH_QBRA , 0, + DSP_ }, /* PRECEU.PH.QBRA */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d53f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(106) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d73f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(107) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d93f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(108) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000db3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(109) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000dd3f, &NMD::CTC2 , 0, + CP2_ }, /* CTC2 */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000df3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(111) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e13f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(112) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e33f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(113) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e53f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(114) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e73f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(115) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e93f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(116) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000eb3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(117) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ed3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(118) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ef3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(119) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000f13f, &NMD::RADDU_W_QB , 0, + DSP_ }, /* RADDU.W.QB */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f33f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(121) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f53f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(122) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f73f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(123) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f93f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(124) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000fb3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(125) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000fd3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(126) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ff3f, 0 , 0, + 0x0 }, /* POOL32Axf_4~*(127) */ +}; + + +NMD::Pool NMD::POOL32Axf_5_group0[32] = { + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000017f, &NMD::TLBGP , 0, + CP0_ | VZ_ | TLB_ }, /* TLBGP */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000037f, &NMD::TLBP , 0, + CP0_ | TLB_ }, /* TLBP */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000057f, &NMD::TLBGINV , 0, + CP0_ | VZ_ | TLB_ | TLBINV_}, /* TLBGINV */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000077f, &NMD::TLBINV , 0, + CP0_ | TLB_ | TLBINV_}, /* TLBINV */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000097f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20000b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20000d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20000f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(7) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000117f, &NMD::TLBGR , 0, + CP0_ | VZ_ | TLB_ }, /* TLBGR */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000137f, &NMD::TLBR , 0, + CP0_ | TLB_ }, /* TLBR */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000157f, &NMD::TLBGINVF , 0, + CP0_ | VZ_ | TLB_ | TLBINV_}, /* TLBGINVF */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000177f, &NMD::TLBINVF , 0, + CP0_ | TLB_ | TLBINV_}, /* TLBINVF */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000197f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20001b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20001d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20001f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(15) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000217f, &NMD::TLBGWI , 0, + CP0_ | VZ_ | TLB_ }, /* TLBGWI */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000237f, &NMD::TLBWI , 0, + CP0_ | TLB_ }, /* TLBWI */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000257f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000277f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000297f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20002b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20002d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20002f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(23) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000317f, &NMD::TLBGWR , 0, + CP0_ | VZ_ | TLB_ }, /* TLBGWR */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000337f, &NMD::TLBWR , 0, + CP0_ | TLB_ }, /* TLBWR */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000357f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000377f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000397f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20003b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20003d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20003f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0~*(31) */ +}; + + +NMD::Pool NMD::POOL32Axf_5_group1[32] = { + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000417f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(0) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000437f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000457f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(2) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000477f, &NMD::DI , 0, + 0x0 }, /* DI */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000497f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20004b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20004d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20004f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000517f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(8) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000537f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(9) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000557f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(10) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000577f, &NMD::EI , 0, + 0x0 }, /* EI */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000597f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20005b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20005d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20005f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(15) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000617f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000637f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(17) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000657f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000677f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000697f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20006b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20006d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20006f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000717f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000737f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000757f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000777f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000797f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20007b7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20007d7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x20007f7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1~*(31) */ +}; + + +NMD::Pool NMD::ERETx[2] = { + { instruction , 0 , 0 , 32, + 0xfc01ffff, 0x2000f37f, &NMD::ERET , 0, + 0x0 }, /* ERET */ + { instruction , 0 , 0 , 32, + 0xfc01ffff, 0x2001f37f, &NMD::ERETNC , 0, + 0x0 }, /* ERETNC */ +}; + + +NMD::Pool NMD::POOL32Axf_5_group3[32] = { + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c17f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(0) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000c37f, &NMD::WAIT , 0, + 0x0 }, /* WAIT */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c57f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c77f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000c97f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000cb7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000cd7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000cf7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d17f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(8) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000d37f, &NMD::IRET , 0, + MCU_ }, /* IRET */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d57f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d77f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(11) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000d97f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000db7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000dd7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000df7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(15) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000e17f, &NMD::RDPGPR , 0, + CP0_ }, /* RDPGPR */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000e37f, &NMD::DERET , 0, + EJTAG_ }, /* DERET */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e57f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e77f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000e97f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000eb7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ed7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ef7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(23) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0x2000f17f, &NMD::WRPGPR , 0, + CP0_ }, /* WRPGPR */ + { pool , ERETx , 2 , 32, + 0xfc00ffff, 0x2000f37f, 0 , 0, + 0x0 }, /* ERETx */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f57f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f77f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000f97f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000fb7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000fd7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0x2000ff7f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3~*(31) */ +}; + + +NMD::Pool NMD::POOL32Axf_5[4] = { + { pool , POOL32Axf_5_group0 , 32 , 32, + 0xfc00c1ff, 0x2000017f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group0 */ + { pool , POOL32Axf_5_group1 , 32 , 32, + 0xfc00c1ff, 0x2000417f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group1 */ + { reserved_block , 0 , 0 , 32, + 0xfc00c1ff, 0x2000817f, 0 , 0, + 0x0 }, /* POOL32Axf_5~*(2) */ + { pool , POOL32Axf_5_group3 , 32 , 32, + 0xfc00c1ff, 0x2000c17f, 0 , 0, + 0x0 }, /* POOL32Axf_5_group3 */ +}; + + +NMD::Pool NMD::SHRA__R__QB[2] = { + { instruction , 0 , 0 , 32, + 0xfc001fff, 0x200001ff, &NMD::SHRA_QB , 0, + DSP_ }, /* SHRA.QB */ + { instruction , 0 , 0 , 32, + 0xfc001fff, 0x200011ff, &NMD::SHRA_R_QB , 0, + DSP_ }, /* SHRA_R.QB */ +}; + + +NMD::Pool NMD::POOL32Axf_7[8] = { + { pool , SHRA__R__QB , 2 , 32, + 0xfc000fff, 0x200001ff, 0 , 0, + 0x0 }, /* SHRA[_R].QB */ + { instruction , 0 , 0 , 32, + 0xfc000fff, 0x200003ff, &NMD::SHRL_PH , 0, + DSP_ }, /* SHRL.PH */ + { instruction , 0 , 0 , 32, + 0xfc000fff, 0x200005ff, &NMD::REPL_QB , 0, + DSP_ }, /* REPL.QB */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x200007ff, 0 , 0, + 0x0 }, /* POOL32Axf_7~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x200009ff, 0 , 0, + 0x0 }, /* POOL32Axf_7~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x20000bff, 0 , 0, + 0x0 }, /* POOL32Axf_7~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x20000dff, 0 , 0, + 0x0 }, /* POOL32Axf_7~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc000fff, 0x20000fff, 0 , 0, + 0x0 }, /* POOL32Axf_7~*(7) */ +}; + + +NMD::Pool NMD::POOL32Axf[8] = { + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0x2000003f, 0 , 0, + 0x0 }, /* POOL32Axf~*(0) */ + { pool , POOL32Axf_1 , 8 , 32, + 0xfc0001ff, 0x2000007f, 0 , 0, + 0x0 }, /* POOL32Axf_1 */ + { pool , POOL32Axf_2 , 4 , 32, + 0xfc0001ff, 0x200000bf, 0 , 0, + 0x0 }, /* POOL32Axf_2 */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0x200000ff, 0 , 0, + 0x0 }, /* POOL32Axf~*(3) */ + { pool , POOL32Axf_4 , 128 , 32, + 0xfc0001ff, 0x2000013f, 0 , 0, + 0x0 }, /* POOL32Axf_4 */ + { pool , POOL32Axf_5 , 4 , 32, + 0xfc0001ff, 0x2000017f, 0 , 0, + 0x0 }, /* POOL32Axf_5 */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0x200001bf, 0 , 0, + 0x0 }, /* POOL32Axf~*(6) */ + { pool , POOL32Axf_7 , 8 , 32, + 0xfc0001ff, 0x200001ff, 0 , 0, + 0x0 }, /* POOL32Axf_7 */ +}; + + +NMD::Pool NMD::_POOL32A7[8] = { + { pool , P_LSX , 2 , 32, + 0xfc00003f, 0x20000007, 0 , 0, + 0x0 }, /* P.LSX */ + { instruction , 0 , 0 , 32, + 0xfc00003f, 0x2000000f, &NMD::LSA , 0, + 0x0 }, /* LSA */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0x20000017, 0 , 0, + 0x0 }, /* _POOL32A7~*(2) */ + { instruction , 0 , 0 , 32, + 0xfc00003f, 0x2000001f, &NMD::EXTW , 0, + 0x0 }, /* EXTW */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0x20000027, 0 , 0, + 0x0 }, /* _POOL32A7~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0x2000002f, 0 , 0, + 0x0 }, /* _POOL32A7~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0x20000037, 0 , 0, + 0x0 }, /* _POOL32A7~*(6) */ + { pool , POOL32Axf , 8 , 32, + 0xfc00003f, 0x2000003f, 0 , 0, + 0x0 }, /* POOL32Axf */ +}; + + +NMD::Pool NMD::P32A[8] = { + { pool , _POOL32A0 , 128 , 32, + 0xfc000007, 0x20000000, 0 , 0, + 0x0 }, /* _POOL32A0 */ + { instruction , 0 , 0 , 32, + 0xfc000007, 0x20000001, &NMD::SPECIAL2 , 0, + UDI_ }, /* SPECIAL2 */ + { instruction , 0 , 0 , 32, + 0xfc000007, 0x20000002, &NMD::COP2_1 , 0, + CP2_ }, /* COP2_1 */ + { instruction , 0 , 0 , 32, + 0xfc000007, 0x20000003, &NMD::UDI , 0, + UDI_ }, /* UDI */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0x20000004, 0 , 0, + 0x0 }, /* P32A~*(4) */ + { pool , _POOL32A5 , 128 , 32, + 0xfc000007, 0x20000005, 0 , 0, + 0x0 }, /* _POOL32A5 */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0x20000006, 0 , 0, + 0x0 }, /* P32A~*(6) */ + { pool , _POOL32A7 , 8 , 32, + 0xfc000007, 0x20000007, 0 , 0, + 0x0 }, /* _POOL32A7 */ +}; + + +NMD::Pool NMD::P_GP_D[2] = { + { instruction , 0 , 0 , 32, + 0xfc000007, 0x40000001, &NMD::LD_GP_ , 0, + MIPS64_ }, /* LD[GP] */ + { instruction , 0 , 0 , 32, + 0xfc000007, 0x40000005, &NMD::SD_GP_ , 0, + MIPS64_ }, /* SD[GP] */ +}; + + +NMD::Pool NMD::P_GP_W[4] = { + { instruction , 0 , 0 , 32, + 0xfc000003, 0x40000000, &NMD::ADDIU_GP_W_ , 0, + 0x0 }, /* ADDIU[GP.W] */ + { pool , P_GP_D , 2 , 32, + 0xfc000003, 0x40000001, 0 , 0, + 0x0 }, /* P.GP.D */ + { instruction , 0 , 0 , 32, + 0xfc000003, 0x40000002, &NMD::LW_GP_ , 0, + 0x0 }, /* LW[GP] */ + { instruction , 0 , 0 , 32, + 0xfc000003, 0x40000003, &NMD::SW_GP_ , 0, + 0x0 }, /* SW[GP] */ +}; + + +NMD::Pool NMD::POOL48I[32] = { + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600000000000ull, &NMD::LI_48_ , 0, + XMMS_ }, /* LI[48] */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600100000000ull, &NMD::ADDIU_48_ , 0, + XMMS_ }, /* ADDIU[48] */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600200000000ull, &NMD::ADDIU_GP48_ , 0, + XMMS_ }, /* ADDIU[GP48] */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600300000000ull, &NMD::ADDIUPC_48_ , 0, + XMMS_ }, /* ADDIUPC[48] */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600400000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(4) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600500000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(5) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600600000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(6) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600700000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(7) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600800000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(8) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600900000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(9) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600a00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(10) */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600b00000000ull, &NMD::LWPC_48_ , 0, + XMMS_ }, /* LWPC[48] */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600c00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(12) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600d00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(13) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600e00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(14) */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x600f00000000ull, &NMD::SWPC_48_ , 0, + XMMS_ }, /* SWPC[48] */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601000000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(16) */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601100000000ull, &NMD::DADDIU_48_ , 0, + MIPS64_ }, /* DADDIU[48] */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601200000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(18) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601300000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(19) */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601400000000ull, &NMD::DLUI_48_ , 0, + MIPS64_ }, /* DLUI[48] */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601500000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(21) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601600000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(22) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601700000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(23) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601800000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(24) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601900000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(25) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601a00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(26) */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601b00000000ull, &NMD::LDPC_48_ , 0, + MIPS64_ }, /* LDPC[48] */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601c00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(28) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601d00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(29) */ + { reserved_block , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601e00000000ull, 0 , 0, + 0x0 }, /* POOL48I~*(30) */ + { instruction , 0 , 0 , 48, + 0xfc1f00000000ull, 0x601f00000000ull, &NMD::SDPC_48_ , 0, + MIPS64_ }, /* SDPC[48] */ +}; + + +NMD::Pool NMD::PP_SR[4] = { + { instruction , 0 , 0 , 32, + 0xfc10f003, 0x80003000, &NMD::SAVE_32_ , 0, + 0x0 }, /* SAVE[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc10f003, 0x80003001, 0 , 0, + 0x0 }, /* PP.SR~*(1) */ + { instruction , 0 , 0 , 32, + 0xfc10f003, 0x80003002, &NMD::RESTORE_32_ , 0, + 0x0 }, /* RESTORE[32] */ + { return_instruction , 0 , 0 , 32, + 0xfc10f003, 0x80003003, &NMD::RESTORE_JRC_32_ , 0, + 0x0 }, /* RESTORE.JRC[32] */ +}; + + +NMD::Pool NMD::P_SR_F[8] = { + { instruction , 0 , 0 , 32, + 0xfc10f007, 0x80103000, &NMD::SAVEF , 0, + CP1_ }, /* SAVEF */ + { instruction , 0 , 0 , 32, + 0xfc10f007, 0x80103001, &NMD::RESTOREF , 0, + CP1_ }, /* RESTOREF */ + { reserved_block , 0 , 0 , 32, + 0xfc10f007, 0x80103002, 0 , 0, + 0x0 }, /* P.SR.F~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc10f007, 0x80103003, 0 , 0, + 0x0 }, /* P.SR.F~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc10f007, 0x80103004, 0 , 0, + 0x0 }, /* P.SR.F~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc10f007, 0x80103005, 0 , 0, + 0x0 }, /* P.SR.F~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc10f007, 0x80103006, 0 , 0, + 0x0 }, /* P.SR.F~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc10f007, 0x80103007, 0 , 0, + 0x0 }, /* P.SR.F~*(7) */ +}; + + +NMD::Pool NMD::P_SR[2] = { + { pool , PP_SR , 4 , 32, + 0xfc10f000, 0x80003000, 0 , 0, + 0x0 }, /* PP.SR */ + { pool , P_SR_F , 8 , 32, + 0xfc10f000, 0x80103000, 0 , 0, + 0x0 }, /* P.SR.F */ +}; + + +NMD::Pool NMD::P_SLL[5] = { + { instruction , 0 , 0 , 32, + 0xffe0f1ff, 0x8000c000, &NMD::NOP_32_ , 0, + 0x0 }, /* NOP[32] */ + { instruction , 0 , 0 , 32, + 0xffe0f1ff, 0x8000c003, &NMD::EHB , 0, + 0x0 }, /* EHB */ + { instruction , 0 , 0 , 32, + 0xffe0f1ff, 0x8000c005, &NMD::PAUSE , 0, + 0x0 }, /* PAUSE */ + { instruction , 0 , 0 , 32, + 0xffe0f1ff, 0x8000c006, &NMD::SYNC , 0, + 0x0 }, /* SYNC */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c000, &NMD::SLL_32_ , 0, + 0x0 }, /* SLL[32] */ +}; + + +NMD::Pool NMD::P_SHIFT[16] = { + { pool , P_SLL , 5 , 32, + 0xfc00f1e0, 0x8000c000, 0 , 0, + 0x0 }, /* P.SLL */ + { reserved_block , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c020, 0 , 0, + 0x0 }, /* P.SHIFT~*(1) */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c040, &NMD::SRL_32_ , 0, + 0x0 }, /* SRL[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c060, 0 , 0, + 0x0 }, /* P.SHIFT~*(3) */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c080, &NMD::SRA , 0, + 0x0 }, /* SRA */ + { reserved_block , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c0a0, 0 , 0, + 0x0 }, /* P.SHIFT~*(5) */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c0c0, &NMD::ROTR , 0, + 0x0 }, /* ROTR */ + { reserved_block , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c0e0, 0 , 0, + 0x0 }, /* P.SHIFT~*(7) */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c100, &NMD::DSLL , 0, + MIPS64_ }, /* DSLL */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c120, &NMD::DSLL32 , 0, + MIPS64_ }, /* DSLL32 */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c140, &NMD::DSRL , 0, + MIPS64_ }, /* DSRL */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c160, &NMD::DSRL32 , 0, + MIPS64_ }, /* DSRL32 */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c180, &NMD::DSRA , 0, + MIPS64_ }, /* DSRA */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c1a0, &NMD::DSRA32 , 0, + MIPS64_ }, /* DSRA32 */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c1c0, &NMD::DROTR , 0, + MIPS64_ }, /* DROTR */ + { instruction , 0 , 0 , 32, + 0xfc00f1e0, 0x8000c1e0, &NMD::DROTR32 , 0, + MIPS64_ }, /* DROTR32 */ +}; + + +NMD::Pool NMD::P_ROTX[4] = { + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000d000, &NMD::ROTX , 0, + XMMS_ }, /* ROTX */ + { reserved_block , 0 , 0 , 32, + 0xfc00f820, 0x8000d020, 0 , 0, + 0x0 }, /* P.ROTX~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f820, 0x8000d800, 0 , 0, + 0x0 }, /* P.ROTX~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f820, 0x8000d820, 0 , 0, + 0x0 }, /* P.ROTX~*(3) */ +}; + + +NMD::Pool NMD::P_INS[4] = { + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000e000, &NMD::INS , 0, + XMMS_ }, /* INS */ + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000e020, &NMD::DINSU , 0, + MIPS64_ }, /* DINSU */ + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000e800, &NMD::DINSM , 0, + MIPS64_ }, /* DINSM */ + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000e820, &NMD::DINS , 0, + MIPS64_ }, /* DINS */ +}; + + +NMD::Pool NMD::P_EXT[4] = { + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000f000, &NMD::EXT , 0, + XMMS_ }, /* EXT */ + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000f020, &NMD::DEXTU , 0, + MIPS64_ }, /* DEXTU */ + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000f800, &NMD::DEXTM , 0, + MIPS64_ }, /* DEXTM */ + { instruction , 0 , 0 , 32, + 0xfc00f820, 0x8000f820, &NMD::DEXT , 0, + MIPS64_ }, /* DEXT */ +}; + + +NMD::Pool NMD::P_U12[16] = { + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80000000, &NMD::ORI , 0, + 0x0 }, /* ORI */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80001000, &NMD::XORI , 0, + 0x0 }, /* XORI */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80002000, &NMD::ANDI_32_ , 0, + 0x0 }, /* ANDI[32] */ + { pool , P_SR , 2 , 32, + 0xfc00f000, 0x80003000, 0 , 0, + 0x0 }, /* P.SR */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80004000, &NMD::SLTI , 0, + 0x0 }, /* SLTI */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80005000, &NMD::SLTIU , 0, + 0x0 }, /* SLTIU */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80006000, &NMD::SEQI , 0, + 0x0 }, /* SEQI */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x80007000, 0 , 0, + 0x0 }, /* P.U12~*(7) */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80008000, &NMD::ADDIU_NEG_ , 0, + 0x0 }, /* ADDIU[NEG] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x80009000, &NMD::DADDIU_U12_ , 0, + MIPS64_ }, /* DADDIU[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8000a000, &NMD::DADDIU_NEG_ , 0, + MIPS64_ }, /* DADDIU[NEG] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8000b000, &NMD::DROTX , 0, + MIPS64_ }, /* DROTX */ + { pool , P_SHIFT , 16 , 32, + 0xfc00f000, 0x8000c000, 0 , 0, + 0x0 }, /* P.SHIFT */ + { pool , P_ROTX , 4 , 32, + 0xfc00f000, 0x8000d000, 0 , 0, + 0x0 }, /* P.ROTX */ + { pool , P_INS , 4 , 32, + 0xfc00f000, 0x8000e000, 0 , 0, + 0x0 }, /* P.INS */ + { pool , P_EXT , 4 , 32, + 0xfc00f000, 0x8000f000, 0 , 0, + 0x0 }, /* P.EXT */ +}; + + +NMD::Pool NMD::RINT_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000020, &NMD::RINT_S , 0, + CP1_ }, /* RINT.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000220, &NMD::RINT_D , 0, + CP1_ }, /* RINT.D */ +}; + + +NMD::Pool NMD::ADD_fmt0[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000030, &NMD::ADD_S , 0, + CP1_ }, /* ADD.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa0000230, 0 , 0, + CP1_ }, /* ADD.fmt0~*(1) */ +}; + + +NMD::Pool NMD::SELEQZ_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000038, &NMD::SELEQZ_S , 0, + CP1_ }, /* SELEQZ.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000238, &NMD::SELEQZ_D , 0, + CP1_ }, /* SELEQZ.D */ +}; + + +NMD::Pool NMD::CLASS_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000060, &NMD::CLASS_S , 0, + CP1_ }, /* CLASS.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000260, &NMD::CLASS_D , 0, + CP1_ }, /* CLASS.D */ +}; + + +NMD::Pool NMD::SUB_fmt0[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000070, &NMD::SUB_S , 0, + CP1_ }, /* SUB.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa0000270, 0 , 0, + CP1_ }, /* SUB.fmt0~*(1) */ +}; + + +NMD::Pool NMD::SELNEZ_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000078, &NMD::SELNEZ_S , 0, + CP1_ }, /* SELNEZ.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000278, &NMD::SELNEZ_D , 0, + CP1_ }, /* SELNEZ.D */ +}; + + +NMD::Pool NMD::MUL_fmt0[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00000b0, &NMD::MUL_S , 0, + CP1_ }, /* MUL.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa00002b0, 0 , 0, + CP1_ }, /* MUL.fmt0~*(1) */ +}; + + +NMD::Pool NMD::SEL_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00000b8, &NMD::SEL_S , 0, + CP1_ }, /* SEL.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00002b8, &NMD::SEL_D , 0, + CP1_ }, /* SEL.D */ +}; + + +NMD::Pool NMD::DIV_fmt0[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00000f0, &NMD::DIV_S , 0, + CP1_ }, /* DIV.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa00002f0, 0 , 0, + CP1_ }, /* DIV.fmt0~*(1) */ +}; + + +NMD::Pool NMD::ADD_fmt1[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000130, &NMD::ADD_D , 0, + CP1_ }, /* ADD.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa0000330, 0 , 0, + CP1_ }, /* ADD.fmt1~*(1) */ +}; + + +NMD::Pool NMD::SUB_fmt1[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa0000170, &NMD::SUB_D , 0, + CP1_ }, /* SUB.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa0000370, 0 , 0, + CP1_ }, /* SUB.fmt1~*(1) */ +}; + + +NMD::Pool NMD::MUL_fmt1[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00001b0, &NMD::MUL_D , 0, + CP1_ }, /* MUL.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa00003b0, 0 , 0, + CP1_ }, /* MUL.fmt1~*(1) */ +}; + + +NMD::Pool NMD::MADDF_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00001b8, &NMD::MADDF_S , 0, + CP1_ }, /* MADDF.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00003b8, &NMD::MADDF_D , 0, + CP1_ }, /* MADDF.D */ +}; + + +NMD::Pool NMD::DIV_fmt1[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00001f0, &NMD::DIV_D , 0, + CP1_ }, /* DIV.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0003ff, 0xa00003f0, 0 , 0, + CP1_ }, /* DIV.fmt1~*(1) */ +}; + + +NMD::Pool NMD::MSUBF_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00001f8, &NMD::MSUBF_S , 0, + CP1_ }, /* MSUBF.S */ + { instruction , 0 , 0 , 32, + 0xfc0003ff, 0xa00003f8, &NMD::MSUBF_D , 0, + CP1_ }, /* MSUBF.D */ +}; + + +NMD::Pool NMD::POOL32F_0[64] = { + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000000, 0 , 0, + CP1_ }, /* POOL32F_0~*(0) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000008, 0 , 0, + CP1_ }, /* POOL32F_0~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000010, 0 , 0, + CP1_ }, /* POOL32F_0~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000018, 0 , 0, + CP1_ }, /* POOL32F_0~*(3) */ + { pool , RINT_fmt , 2 , 32, + 0xfc0001ff, 0xa0000020, 0 , 0, + CP1_ }, /* RINT.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000028, 0 , 0, + CP1_ }, /* POOL32F_0~*(5) */ + { pool , ADD_fmt0 , 2 , 32, + 0xfc0001ff, 0xa0000030, 0 , 0, + CP1_ }, /* ADD.fmt0 */ + { pool , SELEQZ_fmt , 2 , 32, + 0xfc0001ff, 0xa0000038, 0 , 0, + CP1_ }, /* SELEQZ.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000040, 0 , 0, + CP1_ }, /* POOL32F_0~*(8) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000048, 0 , 0, + CP1_ }, /* POOL32F_0~*(9) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000050, 0 , 0, + CP1_ }, /* POOL32F_0~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000058, 0 , 0, + CP1_ }, /* POOL32F_0~*(11) */ + { pool , CLASS_fmt , 2 , 32, + 0xfc0001ff, 0xa0000060, 0 , 0, + CP1_ }, /* CLASS.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000068, 0 , 0, + CP1_ }, /* POOL32F_0~*(13) */ + { pool , SUB_fmt0 , 2 , 32, + 0xfc0001ff, 0xa0000070, 0 , 0, + CP1_ }, /* SUB.fmt0 */ + { pool , SELNEZ_fmt , 2 , 32, + 0xfc0001ff, 0xa0000078, 0 , 0, + CP1_ }, /* SELNEZ.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000080, 0 , 0, + CP1_ }, /* POOL32F_0~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000088, 0 , 0, + CP1_ }, /* POOL32F_0~*(17) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000090, 0 , 0, + CP1_ }, /* POOL32F_0~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000098, 0 , 0, + CP1_ }, /* POOL32F_0~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000a0, 0 , 0, + CP1_ }, /* POOL32F_0~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000a8, 0 , 0, + CP1_ }, /* POOL32F_0~*(21) */ + { pool , MUL_fmt0 , 2 , 32, + 0xfc0001ff, 0xa00000b0, 0 , 0, + CP1_ }, /* MUL.fmt0 */ + { pool , SEL_fmt , 2 , 32, + 0xfc0001ff, 0xa00000b8, 0 , 0, + CP1_ }, /* SEL.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000c0, 0 , 0, + CP1_ }, /* POOL32F_0~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000c8, 0 , 0, + CP1_ }, /* POOL32F_0~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000d0, 0 , 0, + CP1_ }, /* POOL32F_0~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000d8, 0 , 0, + CP1_ }, /* POOL32F_0~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000e0, 0 , 0, + CP1_ }, /* POOL32F_0~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000e8, 0 , 0, + CP1_ }, /* POOL32F_0~*(29) */ + { pool , DIV_fmt0 , 2 , 32, + 0xfc0001ff, 0xa00000f0, 0 , 0, + CP1_ }, /* DIV.fmt0 */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00000f8, 0 , 0, + CP1_ }, /* POOL32F_0~*(31) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000100, 0 , 0, + CP1_ }, /* POOL32F_0~*(32) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000108, 0 , 0, + CP1_ }, /* POOL32F_0~*(33) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000110, 0 , 0, + CP1_ }, /* POOL32F_0~*(34) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000118, 0 , 0, + CP1_ }, /* POOL32F_0~*(35) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000120, 0 , 0, + CP1_ }, /* POOL32F_0~*(36) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000128, 0 , 0, + CP1_ }, /* POOL32F_0~*(37) */ + { pool , ADD_fmt1 , 2 , 32, + 0xfc0001ff, 0xa0000130, 0 , 0, + CP1_ }, /* ADD.fmt1 */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000138, 0 , 0, + CP1_ }, /* POOL32F_0~*(39) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000140, 0 , 0, + CP1_ }, /* POOL32F_0~*(40) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000148, 0 , 0, + CP1_ }, /* POOL32F_0~*(41) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000150, 0 , 0, + CP1_ }, /* POOL32F_0~*(42) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000158, 0 , 0, + CP1_ }, /* POOL32F_0~*(43) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000160, 0 , 0, + CP1_ }, /* POOL32F_0~*(44) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000168, 0 , 0, + CP1_ }, /* POOL32F_0~*(45) */ + { pool , SUB_fmt1 , 2 , 32, + 0xfc0001ff, 0xa0000170, 0 , 0, + CP1_ }, /* SUB.fmt1 */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000178, 0 , 0, + CP1_ }, /* POOL32F_0~*(47) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000180, 0 , 0, + CP1_ }, /* POOL32F_0~*(48) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000188, 0 , 0, + CP1_ }, /* POOL32F_0~*(49) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000190, 0 , 0, + CP1_ }, /* POOL32F_0~*(50) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa0000198, 0 , 0, + CP1_ }, /* POOL32F_0~*(51) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001a0, 0 , 0, + CP1_ }, /* POOL32F_0~*(52) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001a8, 0 , 0, + CP1_ }, /* POOL32F_0~*(53) */ + { pool , MUL_fmt1 , 2 , 32, + 0xfc0001ff, 0xa00001b0, 0 , 0, + CP1_ }, /* MUL.fmt1 */ + { pool , MADDF_fmt , 2 , 32, + 0xfc0001ff, 0xa00001b8, 0 , 0, + CP1_ }, /* MADDF.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001c0, 0 , 0, + CP1_ }, /* POOL32F_0~*(56) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001c8, 0 , 0, + CP1_ }, /* POOL32F_0~*(57) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001d0, 0 , 0, + CP1_ }, /* POOL32F_0~*(58) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001d8, 0 , 0, + CP1_ }, /* POOL32F_0~*(59) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001e0, 0 , 0, + CP1_ }, /* POOL32F_0~*(60) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xa00001e8, 0 , 0, + CP1_ }, /* POOL32F_0~*(61) */ + { pool , DIV_fmt1 , 2 , 32, + 0xfc0001ff, 0xa00001f0, 0 , 0, + CP1_ }, /* DIV.fmt1 */ + { pool , MSUBF_fmt , 2 , 32, + 0xfc0001ff, 0xa00001f8, 0 , 0, + CP1_ }, /* MSUBF.fmt */ +}; + + +NMD::Pool NMD::MIN_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa0000003, &NMD::MIN_S , 0, + CP1_ }, /* MIN.S */ + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa0000203, &NMD::MIN_D , 0, + CP1_ }, /* MIN.D */ +}; + + +NMD::Pool NMD::MAX_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa000000b, &NMD::MAX_S , 0, + CP1_ }, /* MAX.S */ + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa000020b, &NMD::MAX_D , 0, + CP1_ }, /* MAX.D */ +}; + + +NMD::Pool NMD::MINA_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa0000023, &NMD::MINA_S , 0, + CP1_ }, /* MINA.S */ + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa0000223, &NMD::MINA_D , 0, + CP1_ }, /* MINA.D */ +}; + + +NMD::Pool NMD::MAXA_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa000002b, &NMD::MAXA_S , 0, + CP1_ }, /* MAXA.S */ + { instruction , 0 , 0 , 32, + 0xfc00023f, 0xa000022b, &NMD::MAXA_D , 0, + CP1_ }, /* MAXA.D */ +}; + + +NMD::Pool NMD::CVT_L_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000013b, &NMD::CVT_L_S , 0, + CP1_ }, /* CVT.L.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000413b, &NMD::CVT_L_D , 0, + CP1_ }, /* CVT.L.D */ +}; + + +NMD::Pool NMD::RSQRT_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000023b, &NMD::RSQRT_S , 0, + CP1_ }, /* RSQRT.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000423b, &NMD::RSQRT_D , 0, + CP1_ }, /* RSQRT.D */ +}; + + +NMD::Pool NMD::FLOOR_L_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000033b, &NMD::FLOOR_L_S , 0, + CP1_ }, /* FLOOR.L.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000433b, &NMD::FLOOR_L_D , 0, + CP1_ }, /* FLOOR.L.D */ +}; + + +NMD::Pool NMD::CVT_W_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000093b, &NMD::CVT_W_S , 0, + CP1_ }, /* CVT.W.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000493b, &NMD::CVT_W_D , 0, + CP1_ }, /* CVT.W.D */ +}; + + +NMD::Pool NMD::SQRT_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0000a3b, &NMD::SQRT_S , 0, + CP1_ }, /* SQRT.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0004a3b, &NMD::SQRT_D , 0, + CP1_ }, /* SQRT.D */ +}; + + +NMD::Pool NMD::FLOOR_W_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0000b3b, &NMD::FLOOR_W_S , 0, + CP1_ }, /* FLOOR.W.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0004b3b, &NMD::FLOOR_W_D , 0, + CP1_ }, /* FLOOR.W.D */ +}; + + +NMD::Pool NMD::RECIP_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000123b, &NMD::RECIP_S , 0, + CP1_ }, /* RECIP.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000523b, &NMD::RECIP_D , 0, + CP1_ }, /* RECIP.D */ +}; + + +NMD::Pool NMD::CEIL_L_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000133b, &NMD::CEIL_L_S , 0, + CP1_ }, /* CEIL.L.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000533b, &NMD::CEIL_L_D , 0, + CP1_ }, /* CEIL.L.D */ +}; + + +NMD::Pool NMD::CEIL_W_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0001b3b, &NMD::CEIL_W_S , 0, + CP1_ }, /* CEIL.W.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0005b3b, &NMD::CEIL_W_D , 0, + CP1_ }, /* CEIL.W.D */ +}; + + +NMD::Pool NMD::TRUNC_L_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000233b, &NMD::TRUNC_L_S , 0, + CP1_ }, /* TRUNC.L.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000633b, &NMD::TRUNC_L_D , 0, + CP1_ }, /* TRUNC.L.D */ +}; + + +NMD::Pool NMD::TRUNC_W_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0002b3b, &NMD::TRUNC_W_S , 0, + CP1_ }, /* TRUNC.W.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0006b3b, &NMD::TRUNC_W_D , 0, + CP1_ }, /* TRUNC.W.D */ +}; + + +NMD::Pool NMD::ROUND_L_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000333b, &NMD::ROUND_L_S , 0, + CP1_ }, /* ROUND.L.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000733b, &NMD::ROUND_L_D , 0, + CP1_ }, /* ROUND.L.D */ +}; + + +NMD::Pool NMD::ROUND_W_fmt[2] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0003b3b, &NMD::ROUND_W_S , 0, + CP1_ }, /* ROUND.W.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0007b3b, &NMD::ROUND_W_D , 0, + CP1_ }, /* ROUND.W.D */ +}; + + +NMD::Pool NMD::POOL32Fxf_0[64] = { + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000003b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(0) */ + { pool , CVT_L_fmt , 2 , 32, + 0xfc003fff, 0xa000013b, 0 , 0, + CP1_ }, /* CVT.L.fmt */ + { pool , RSQRT_fmt , 2 , 32, + 0xfc003fff, 0xa000023b, 0 , 0, + CP1_ }, /* RSQRT.fmt */ + { pool , FLOOR_L_fmt , 2 , 32, + 0xfc003fff, 0xa000033b, 0 , 0, + CP1_ }, /* FLOOR.L.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000043b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000053b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000063b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000073b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000083b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(8) */ + { pool , CVT_W_fmt , 2 , 32, + 0xfc003fff, 0xa000093b, 0 , 0, + CP1_ }, /* CVT.W.fmt */ + { pool , SQRT_fmt , 2 , 32, + 0xfc003fff, 0xa0000a3b, 0 , 0, + CP1_ }, /* SQRT.fmt */ + { pool , FLOOR_W_fmt , 2 , 32, + 0xfc003fff, 0xa0000b3b, 0 , 0, + CP1_ }, /* FLOOR.W.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0000c3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0000d3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0000e3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0000f3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(15) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000103b, &NMD::CFC1 , 0, + CP1_ }, /* CFC1 */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000113b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(17) */ + { pool , RECIP_fmt , 2 , 32, + 0xfc003fff, 0xa000123b, 0 , 0, + CP1_ }, /* RECIP.fmt */ + { pool , CEIL_L_fmt , 2 , 32, + 0xfc003fff, 0xa000133b, 0 , 0, + CP1_ }, /* CEIL.L.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000143b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000153b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000163b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000173b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(23) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000183b, &NMD::CTC1 , 0, + CP1_ }, /* CTC1 */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000193b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0001a3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(26) */ + { pool , CEIL_W_fmt , 2 , 32, + 0xfc003fff, 0xa0001b3b, 0 , 0, + CP1_ }, /* CEIL.W.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0001c3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0001d3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0001e3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0001f3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(31) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000203b, &NMD::MFC1 , 0, + CP1_ }, /* MFC1 */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000213b, &NMD::CVT_S_PL , 0, + CP1_ }, /* CVT.S.PL */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000223b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(34) */ + { pool , TRUNC_L_fmt , 2 , 32, + 0xfc003fff, 0xa000233b, 0 , 0, + CP1_ }, /* TRUNC.L.fmt */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000243b, &NMD::DMFC1 , 0, + CP1_ | MIPS64_ }, /* DMFC1 */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000253b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(37) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000263b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(38) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000273b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(39) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000283b, &NMD::MTC1 , 0, + CP1_ }, /* MTC1 */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000293b, &NMD::CVT_S_PU , 0, + CP1_ }, /* CVT.S.PU */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0002a3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(42) */ + { pool , TRUNC_W_fmt , 2 , 32, + 0xfc003fff, 0xa0002b3b, 0 , 0, + CP1_ }, /* TRUNC.W.fmt */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa0002c3b, &NMD::DMTC1 , 0, + CP1_ | MIPS64_ }, /* DMTC1 */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0002d3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(45) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0002e3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(46) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0002f3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(47) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000303b, &NMD::MFHC1 , 0, + CP1_ }, /* MFHC1 */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000313b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(49) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000323b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(50) */ + { pool , ROUND_L_fmt , 2 , 32, + 0xfc003fff, 0xa000333b, 0 , 0, + CP1_ }, /* ROUND.L.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000343b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(52) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000353b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(53) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000363b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(54) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000373b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(55) */ + { instruction , 0 , 0 , 32, + 0xfc003fff, 0xa000383b, &NMD::MTHC1 , 0, + CP1_ }, /* MTHC1 */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa000393b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(57) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0003a3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(58) */ + { pool , ROUND_W_fmt , 2 , 32, + 0xfc003fff, 0xa0003b3b, 0 , 0, + CP1_ }, /* ROUND.W.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0003c3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(60) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0003d3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(61) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0003e3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(62) */ + { reserved_block , 0 , 0 , 32, + 0xfc003fff, 0xa0003f3b, 0 , 0, + CP1_ }, /* POOL32Fxf_0~*(63) */ +}; + + +NMD::Pool NMD::MOV_fmt[4] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000007b, &NMD::MOV_S , 0, + CP1_ }, /* MOV.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000207b, &NMD::MOV_D , 0, + CP1_ }, /* MOV.D */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa000407b, 0 , 0, + CP1_ }, /* MOV.fmt~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa000607b, 0 , 0, + CP1_ }, /* MOV.fmt~*(3) */ +}; + + +NMD::Pool NMD::ABS_fmt[4] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000037b, &NMD::ABS_S , 0, + CP1_ }, /* ABS.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000237b, &NMD::ABS_D , 0, + CP1_ }, /* ABS.D */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa000437b, 0 , 0, + CP1_ }, /* ABS.fmt~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa000637b, 0 , 0, + CP1_ }, /* ABS.fmt~*(3) */ +}; + + +NMD::Pool NMD::NEG_fmt[4] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0000b7b, &NMD::NEG_S , 0, + CP1_ }, /* NEG.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0002b7b, &NMD::NEG_D , 0, + CP1_ }, /* NEG.D */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa0004b7b, 0 , 0, + CP1_ }, /* NEG.fmt~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa0006b7b, 0 , 0, + CP1_ }, /* NEG.fmt~*(3) */ +}; + + +NMD::Pool NMD::CVT_D_fmt[4] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000137b, &NMD::CVT_D_S , 0, + CP1_ }, /* CVT.D.S */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000337b, &NMD::CVT_D_W , 0, + CP1_ }, /* CVT.D.W */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa000537b, &NMD::CVT_D_L , 0, + CP1_ }, /* CVT.D.L */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa000737b, 0 , 0, + CP1_ }, /* CVT.D.fmt~*(3) */ +}; + + +NMD::Pool NMD::CVT_S_fmt[4] = { + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0001b7b, &NMD::CVT_S_D , 0, + CP1_ }, /* CVT.S.D */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0003b7b, &NMD::CVT_S_W , 0, + CP1_ }, /* CVT.S.W */ + { instruction , 0 , 0 , 32, + 0xfc007fff, 0xa0005b7b, &NMD::CVT_S_L , 0, + CP1_ }, /* CVT.S.L */ + { reserved_block , 0 , 0 , 32, + 0xfc007fff, 0xa0007b7b, 0 , 0, + CP1_ }, /* CVT.S.fmt~*(3) */ +}; + + +NMD::Pool NMD::POOL32Fxf_1[32] = { + { pool , MOV_fmt , 4 , 32, + 0xfc001fff, 0xa000007b, 0 , 0, + CP1_ }, /* MOV.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000017b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000027b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(2) */ + { pool , ABS_fmt , 4 , 32, + 0xfc001fff, 0xa000037b, 0 , 0, + CP1_ }, /* ABS.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000047b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000057b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000067b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000077b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000087b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(8) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000097b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(9) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0000a7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(10) */ + { pool , NEG_fmt , 4 , 32, + 0xfc001fff, 0xa0000b7b, 0 , 0, + CP1_ }, /* NEG.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0000c7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0000d7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0000e7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0000f7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(15) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000107b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000117b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(17) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000127b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(18) */ + { pool , CVT_D_fmt , 4 , 32, + 0xfc001fff, 0xa000137b, 0 , 0, + CP1_ }, /* CVT.D.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000147b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000157b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000167b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000177b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000187b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa000197b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0001a7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(26) */ + { pool , CVT_S_fmt , 4 , 32, + 0xfc001fff, 0xa0001b7b, 0 , 0, + CP1_ }, /* CVT.S.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0001c7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0001d7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0001e7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc001fff, 0xa0001f7b, 0 , 0, + CP1_ }, /* POOL32Fxf_1~*(31) */ +}; + + +NMD::Pool NMD::POOL32Fxf[4] = { + { pool , POOL32Fxf_0 , 64 , 32, + 0xfc0000ff, 0xa000003b, 0 , 0, + CP1_ }, /* POOL32Fxf_0 */ + { pool , POOL32Fxf_1 , 32 , 32, + 0xfc0000ff, 0xa000007b, 0 , 0, + CP1_ }, /* POOL32Fxf_1 */ + { reserved_block , 0 , 0 , 32, + 0xfc0000ff, 0xa00000bb, 0 , 0, + CP1_ }, /* POOL32Fxf~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc0000ff, 0xa00000fb, 0 , 0, + CP1_ }, /* POOL32Fxf~*(3) */ +}; + + +NMD::Pool NMD::POOL32F_3[8] = { + { pool , MIN_fmt , 2 , 32, + 0xfc00003f, 0xa0000003, 0 , 0, + CP1_ }, /* MIN.fmt */ + { pool , MAX_fmt , 2 , 32, + 0xfc00003f, 0xa000000b, 0 , 0, + CP1_ }, /* MAX.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa0000013, 0 , 0, + CP1_ }, /* POOL32F_3~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa000001b, 0 , 0, + CP1_ }, /* POOL32F_3~*(3) */ + { pool , MINA_fmt , 2 , 32, + 0xfc00003f, 0xa0000023, 0 , 0, + CP1_ }, /* MINA.fmt */ + { pool , MAXA_fmt , 2 , 32, + 0xfc00003f, 0xa000002b, 0 , 0, + CP1_ }, /* MAXA.fmt */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa0000033, 0 , 0, + CP1_ }, /* POOL32F_3~*(6) */ + { pool , POOL32Fxf , 4 , 32, + 0xfc00003f, 0xa000003b, 0 , 0, + CP1_ }, /* POOL32Fxf */ +}; + + +NMD::Pool NMD::CMP_condn_S[32] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000005, &NMD::CMP_AF_S , 0, + CP1_ }, /* CMP.AF.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000045, &NMD::CMP_UN_S , 0, + CP1_ }, /* CMP.UN.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000085, &NMD::CMP_EQ_S , 0, + CP1_ }, /* CMP.EQ.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00000c5, &NMD::CMP_UEQ_S , 0, + CP1_ }, /* CMP.UEQ.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000105, &NMD::CMP_LT_S , 0, + CP1_ }, /* CMP.LT.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000145, &NMD::CMP_ULT_S , 0, + CP1_ }, /* CMP.ULT.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000185, &NMD::CMP_LE_S , 0, + CP1_ }, /* CMP.LE.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00001c5, &NMD::CMP_ULE_S , 0, + CP1_ }, /* CMP.ULE.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000205, &NMD::CMP_SAF_S , 0, + CP1_ }, /* CMP.SAF.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000245, &NMD::CMP_SUN_S , 0, + CP1_ }, /* CMP.SUN.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000285, &NMD::CMP_SEQ_S , 0, + CP1_ }, /* CMP.SEQ.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00002c5, &NMD::CMP_SUEQ_S , 0, + CP1_ }, /* CMP.SUEQ.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000305, &NMD::CMP_SLT_S , 0, + CP1_ }, /* CMP.SLT.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000345, &NMD::CMP_SULT_S , 0, + CP1_ }, /* CMP.SULT.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000385, &NMD::CMP_SLE_S , 0, + CP1_ }, /* CMP.SLE.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00003c5, &NMD::CMP_SULE_S , 0, + CP1_ }, /* CMP.SULE.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000405, 0 , 0, + CP1_ }, /* CMP.condn.S~*(16) */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000445, &NMD::CMP_OR_S , 0, + CP1_ }, /* CMP.OR.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000485, &NMD::CMP_UNE_S , 0, + CP1_ }, /* CMP.UNE.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00004c5, &NMD::CMP_NE_S , 0, + CP1_ }, /* CMP.NE.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000505, 0 , 0, + CP1_ }, /* CMP.condn.S~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000545, 0 , 0, + CP1_ }, /* CMP.condn.S~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000585, 0 , 0, + CP1_ }, /* CMP.condn.S~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa00005c5, 0 , 0, + CP1_ }, /* CMP.condn.S~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000605, 0 , 0, + CP1_ }, /* CMP.condn.S~*(24) */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000645, &NMD::CMP_SOR_S , 0, + CP1_ }, /* CMP.SOR.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000685, &NMD::CMP_SUNE_S , 0, + CP1_ }, /* CMP.SUNE.S */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00006c5, &NMD::CMP_SNE_S , 0, + CP1_ }, /* CMP.SNE.S */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000705, 0 , 0, + CP1_ }, /* CMP.condn.S~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000745, 0 , 0, + CP1_ }, /* CMP.condn.S~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000785, 0 , 0, + CP1_ }, /* CMP.condn.S~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa00007c5, 0 , 0, + CP1_ }, /* CMP.condn.S~*(31) */ +}; + + +NMD::Pool NMD::CMP_condn_D[32] = { + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000015, &NMD::CMP_AF_D , 0, + CP1_ }, /* CMP.AF.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000055, &NMD::CMP_UN_D , 0, + CP1_ }, /* CMP.UN.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000095, &NMD::CMP_EQ_D , 0, + CP1_ }, /* CMP.EQ.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00000d5, &NMD::CMP_UEQ_D , 0, + CP1_ }, /* CMP.UEQ.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000115, &NMD::CMP_LT_D , 0, + CP1_ }, /* CMP.LT.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000155, &NMD::CMP_ULT_D , 0, + CP1_ }, /* CMP.ULT.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000195, &NMD::CMP_LE_D , 0, + CP1_ }, /* CMP.LE.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00001d5, &NMD::CMP_ULE_D , 0, + CP1_ }, /* CMP.ULE.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000215, &NMD::CMP_SAF_D , 0, + CP1_ }, /* CMP.SAF.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000255, &NMD::CMP_SUN_D , 0, + CP1_ }, /* CMP.SUN.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000295, &NMD::CMP_SEQ_D , 0, + CP1_ }, /* CMP.SEQ.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00002d5, &NMD::CMP_SUEQ_D , 0, + CP1_ }, /* CMP.SUEQ.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000315, &NMD::CMP_SLT_D , 0, + CP1_ }, /* CMP.SLT.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000355, &NMD::CMP_SULT_D , 0, + CP1_ }, /* CMP.SULT.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000395, &NMD::CMP_SLE_D , 0, + CP1_ }, /* CMP.SLE.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00003d5, &NMD::CMP_SULE_D , 0, + CP1_ }, /* CMP.SULE.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000415, 0 , 0, + CP1_ }, /* CMP.condn.D~*(16) */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000455, &NMD::CMP_OR_D , 0, + CP1_ }, /* CMP.OR.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000495, &NMD::CMP_UNE_D , 0, + CP1_ }, /* CMP.UNE.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00004d5, &NMD::CMP_NE_D , 0, + CP1_ }, /* CMP.NE.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000515, 0 , 0, + CP1_ }, /* CMP.condn.D~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000555, 0 , 0, + CP1_ }, /* CMP.condn.D~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000595, 0 , 0, + CP1_ }, /* CMP.condn.D~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa00005d5, 0 , 0, + CP1_ }, /* CMP.condn.D~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000615, 0 , 0, + CP1_ }, /* CMP.condn.D~*(24) */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000655, &NMD::CMP_SOR_D , 0, + CP1_ }, /* CMP.SOR.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa0000695, &NMD::CMP_SUNE_D , 0, + CP1_ }, /* CMP.SUNE.D */ + { instruction , 0 , 0 , 32, + 0xfc0007ff, 0xa00006d5, &NMD::CMP_SNE_D , 0, + CP1_ }, /* CMP.SNE.D */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000715, 0 , 0, + CP1_ }, /* CMP.condn.D~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000755, 0 , 0, + CP1_ }, /* CMP.condn.D~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa0000795, 0 , 0, + CP1_ }, /* CMP.condn.D~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc0007ff, 0xa00007d5, 0 , 0, + CP1_ }, /* CMP.condn.D~*(31) */ +}; + + +NMD::Pool NMD::POOL32F_5[8] = { + { pool , CMP_condn_S , 32 , 32, + 0xfc00003f, 0xa0000005, 0 , 0, + CP1_ }, /* CMP.condn.S */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa000000d, 0 , 0, + CP1_ }, /* POOL32F_5~*(1) */ + { pool , CMP_condn_D , 32 , 32, + 0xfc00003f, 0xa0000015, 0 , 0, + CP1_ }, /* CMP.condn.D */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa000001d, 0 , 0, + CP1_ }, /* POOL32F_5~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa0000025, 0 , 0, + CP1_ }, /* POOL32F_5~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa000002d, 0 , 0, + CP1_ }, /* POOL32F_5~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa0000035, 0 , 0, + CP1_ }, /* POOL32F_5~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xa000003d, 0 , 0, + CP1_ }, /* POOL32F_5~*(7) */ +}; + + +NMD::Pool NMD::POOL32F[8] = { + { pool , POOL32F_0 , 64 , 32, + 0xfc000007, 0xa0000000, 0 , 0, + CP1_ }, /* POOL32F_0 */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xa0000001, 0 , 0, + CP1_ }, /* POOL32F~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xa0000002, 0 , 0, + CP1_ }, /* POOL32F~*(2) */ + { pool , POOL32F_3 , 8 , 32, + 0xfc000007, 0xa0000003, 0 , 0, + CP1_ }, /* POOL32F_3 */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xa0000004, 0 , 0, + CP1_ }, /* POOL32F~*(4) */ + { pool , POOL32F_5 , 8 , 32, + 0xfc000007, 0xa0000005, 0 , 0, + CP1_ }, /* POOL32F_5 */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xa0000006, 0 , 0, + CP1_ }, /* POOL32F~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xa0000007, 0 , 0, + CP1_ }, /* POOL32F~*(7) */ +}; + + +NMD::Pool NMD::POOL32S_0[64] = { + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000000, 0 , 0, + 0x0 }, /* POOL32S_0~*(0) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000008, &NMD::DLSA , 0, + MIPS64_ }, /* DLSA */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000010, &NMD::DSLLV , 0, + MIPS64_ }, /* DSLLV */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000018, &NMD::DMUL , 0, + MIPS64_ }, /* DMUL */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000020, 0 , 0, + 0x0 }, /* POOL32S_0~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000028, 0 , 0, + 0x0 }, /* POOL32S_0~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000030, 0 , 0, + 0x0 }, /* POOL32S_0~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000038, 0 , 0, + 0x0 }, /* POOL32S_0~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000040, 0 , 0, + 0x0 }, /* POOL32S_0~*(8) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000048, 0 , 0, + 0x0 }, /* POOL32S_0~*(9) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000050, &NMD::DSRLV , 0, + MIPS64_ }, /* DSRLV */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000058, &NMD::DMUH , 0, + MIPS64_ }, /* DMUH */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000060, 0 , 0, + 0x0 }, /* POOL32S_0~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000068, 0 , 0, + 0x0 }, /* POOL32S_0~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000070, 0 , 0, + 0x0 }, /* POOL32S_0~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000078, 0 , 0, + 0x0 }, /* POOL32S_0~*(15) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000080, 0 , 0, + 0x0 }, /* POOL32S_0~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000088, 0 , 0, + 0x0 }, /* POOL32S_0~*(17) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000090, &NMD::DSRAV , 0, + MIPS64_ }, /* DSRAV */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000098, &NMD::DMULU , 0, + MIPS64_ }, /* DMULU */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000a0, 0 , 0, + 0x0 }, /* POOL32S_0~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000a8, 0 , 0, + 0x0 }, /* POOL32S_0~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000b0, 0 , 0, + 0x0 }, /* POOL32S_0~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000b8, 0 , 0, + 0x0 }, /* POOL32S_0~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000c0, 0 , 0, + 0x0 }, /* POOL32S_0~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000c8, 0 , 0, + 0x0 }, /* POOL32S_0~*(25) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc00000d0, &NMD::DROTRV , 0, + MIPS64_ }, /* DROTRV */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc00000d8, &NMD::DMUHU , 0, + MIPS64_ }, /* DMUHU */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000e0, 0 , 0, + 0x0 }, /* POOL32S_0~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000e8, 0 , 0, + 0x0 }, /* POOL32S_0~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000f0, 0 , 0, + 0x0 }, /* POOL32S_0~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000f8, 0 , 0, + 0x0 }, /* POOL32S_0~*(31) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000100, 0 , 0, + 0x0 }, /* POOL32S_0~*(32) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000108, 0 , 0, + 0x0 }, /* POOL32S_0~*(33) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000110, &NMD::DADD , 0, + MIPS64_ }, /* DADD */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000118, &NMD::DDIV , 0, + MIPS64_ }, /* DDIV */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000120, 0 , 0, + 0x0 }, /* POOL32S_0~*(36) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000128, 0 , 0, + 0x0 }, /* POOL32S_0~*(37) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000130, 0 , 0, + 0x0 }, /* POOL32S_0~*(38) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000138, 0 , 0, + 0x0 }, /* POOL32S_0~*(39) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000140, 0 , 0, + 0x0 }, /* POOL32S_0~*(40) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000148, 0 , 0, + 0x0 }, /* POOL32S_0~*(41) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000150, &NMD::DADDU , 0, + MIPS64_ }, /* DADDU */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000158, &NMD::DMOD , 0, + MIPS64_ }, /* DMOD */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000160, 0 , 0, + 0x0 }, /* POOL32S_0~*(44) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000168, 0 , 0, + 0x0 }, /* POOL32S_0~*(45) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000170, 0 , 0, + 0x0 }, /* POOL32S_0~*(46) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000178, 0 , 0, + 0x0 }, /* POOL32S_0~*(47) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000180, 0 , 0, + 0x0 }, /* POOL32S_0~*(48) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc0000188, 0 , 0, + 0x0 }, /* POOL32S_0~*(49) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000190, &NMD::DSUB , 0, + MIPS64_ }, /* DSUB */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc0000198, &NMD::DDIVU , 0, + MIPS64_ }, /* DDIVU */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001a0, 0 , 0, + 0x0 }, /* POOL32S_0~*(52) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001a8, 0 , 0, + 0x0 }, /* POOL32S_0~*(53) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001b0, 0 , 0, + 0x0 }, /* POOL32S_0~*(54) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001b8, 0 , 0, + 0x0 }, /* POOL32S_0~*(55) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001c0, 0 , 0, + 0x0 }, /* POOL32S_0~*(56) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001c8, 0 , 0, + 0x0 }, /* POOL32S_0~*(57) */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc00001d0, &NMD::DSUBU , 0, + MIPS64_ }, /* DSUBU */ + { instruction , 0 , 0 , 32, + 0xfc0001ff, 0xc00001d8, &NMD::DMODU , 0, + MIPS64_ }, /* DMODU */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001e0, 0 , 0, + 0x0 }, /* POOL32S_0~*(60) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001e8, 0 , 0, + 0x0 }, /* POOL32S_0~*(61) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001f0, 0 , 0, + 0x0 }, /* POOL32S_0~*(62) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001f8, 0 , 0, + 0x0 }, /* POOL32S_0~*(63) */ +}; + + +NMD::Pool NMD::POOL32Sxf_4[128] = { + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000013c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(0) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000033c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000053c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000073c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000093c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0000b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0000d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0000f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000113c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(8) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000133c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(9) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000153c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000173c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(11) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000193c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0001b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0001d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0001f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(15) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000213c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000233c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(17) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000253c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000273c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000293c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0002b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0002d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0002f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000313c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000333c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000353c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000373c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000393c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0003b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0003d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0003f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(31) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000413c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(32) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000433c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(33) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000453c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(34) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000473c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(35) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000493c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(36) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0xc0004b3c, &NMD::DCLO , 0, + MIPS64_ }, /* DCLO */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0004d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(38) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0004f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(39) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000513c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(40) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000533c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(41) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000553c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(42) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000573c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(43) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000593c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(44) */ + { instruction , 0 , 0 , 32, + 0xfc00ffff, 0xc0005b3c, &NMD::DCLZ , 0, + MIPS64_ }, /* DCLZ */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0005d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(46) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0005f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(47) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000613c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(48) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000633c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(49) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000653c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(50) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000673c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(51) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000693c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(52) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0006b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(53) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0006d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(54) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0006f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(55) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000713c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(56) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000733c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(57) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000753c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(58) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000773c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(59) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000793c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(60) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0007b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(61) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0007d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(62) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0007f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(63) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000813c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(64) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000833c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(65) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000853c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(66) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000873c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(67) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000893c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(68) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0008b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(69) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0008d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(70) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0008f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(71) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000913c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(72) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000933c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(73) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000953c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(74) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000973c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(75) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000993c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(76) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0009b3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(77) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0009d3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(78) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc0009f3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(79) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000a13c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(80) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000a33c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(81) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000a53c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(82) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000a73c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(83) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000a93c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(84) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000ab3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(85) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000ad3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(86) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000af3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(87) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000b13c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(88) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000b33c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(89) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000b53c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(90) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000b73c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(91) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000b93c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(92) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000bb3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(93) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000bd3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(94) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000bf3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(95) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000c13c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(96) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000c33c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(97) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000c53c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(98) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000c73c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(99) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000c93c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(100) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000cb3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(101) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000cd3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(102) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000cf3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(103) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000d13c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(104) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000d33c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(105) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000d53c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(106) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000d73c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(107) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000d93c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(108) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000db3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(109) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000dd3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(110) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000df3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(111) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000e13c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(112) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000e33c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(113) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000e53c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(114) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000e73c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(115) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000e93c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(116) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000eb3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(117) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000ed3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(118) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000ef3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(119) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000f13c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(120) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000f33c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(121) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000f53c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(122) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000f73c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(123) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000f93c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(124) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000fb3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(125) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000fd3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(126) */ + { reserved_block , 0 , 0 , 32, + 0xfc00ffff, 0xc000ff3c, 0 , 0, + 0x0 }, /* POOL32Sxf_4~*(127) */ +}; + + +NMD::Pool NMD::POOL32Sxf[8] = { + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc000003c, 0 , 0, + 0x0 }, /* POOL32Sxf~*(0) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc000007c, 0 , 0, + 0x0 }, /* POOL32Sxf~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000bc, 0 , 0, + 0x0 }, /* POOL32Sxf~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00000fc, 0 , 0, + 0x0 }, /* POOL32Sxf~*(3) */ + { pool , POOL32Sxf_4 , 128 , 32, + 0xfc0001ff, 0xc000013c, 0 , 0, + 0x0 }, /* POOL32Sxf_4 */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc000017c, 0 , 0, + 0x0 }, /* POOL32Sxf~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001bc, 0 , 0, + 0x0 }, /* POOL32Sxf~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc0001ff, 0xc00001fc, 0 , 0, + 0x0 }, /* POOL32Sxf~*(7) */ +}; + + +NMD::Pool NMD::POOL32S_4[8] = { + { instruction , 0 , 0 , 32, + 0xfc00003f, 0xc0000004, &NMD::EXTD , 0, + MIPS64_ }, /* EXTD */ + { instruction , 0 , 0 , 32, + 0xfc00003f, 0xc000000c, &NMD::EXTD32 , 0, + MIPS64_ }, /* EXTD32 */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xc0000014, 0 , 0, + 0x0 }, /* POOL32S_4~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xc000001c, 0 , 0, + 0x0 }, /* POOL32S_4~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xc0000024, 0 , 0, + 0x0 }, /* POOL32S_4~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xc000002c, 0 , 0, + 0x0 }, /* POOL32S_4~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00003f, 0xc0000034, 0 , 0, + 0x0 }, /* POOL32S_4~*(6) */ + { pool , POOL32Sxf , 8 , 32, + 0xfc00003f, 0xc000003c, 0 , 0, + 0x0 }, /* POOL32Sxf */ +}; + + +NMD::Pool NMD::POOL32S[8] = { + { pool , POOL32S_0 , 64 , 32, + 0xfc000007, 0xc0000000, 0 , 0, + 0x0 }, /* POOL32S_0 */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xc0000001, 0 , 0, + 0x0 }, /* POOL32S~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xc0000002, 0 , 0, + 0x0 }, /* POOL32S~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xc0000003, 0 , 0, + 0x0 }, /* POOL32S~*(3) */ + { pool , POOL32S_4 , 8 , 32, + 0xfc000007, 0xc0000004, 0 , 0, + 0x0 }, /* POOL32S_4 */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xc0000005, 0 , 0, + 0x0 }, /* POOL32S~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xc0000006, 0 , 0, + 0x0 }, /* POOL32S~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc000007, 0xc0000007, 0 , 0, + 0x0 }, /* POOL32S~*(7) */ +}; + + +NMD::Pool NMD::P_LUI[2] = { + { instruction , 0 , 0 , 32, + 0xfc000002, 0xe0000000, &NMD::LUI , 0, + 0x0 }, /* LUI */ + { instruction , 0 , 0 , 32, + 0xfc000002, 0xe0000002, &NMD::ALUIPC , 0, + 0x0 }, /* ALUIPC */ +}; + + +NMD::Pool NMD::P_GP_LH[2] = { + { instruction , 0 , 0 , 32, + 0xfc1c0001, 0x44100000, &NMD::LH_GP_ , 0, + 0x0 }, /* LH[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0001, 0x44100001, &NMD::LHU_GP_ , 0, + 0x0 }, /* LHU[GP] */ +}; + + +NMD::Pool NMD::P_GP_SH[2] = { + { instruction , 0 , 0 , 32, + 0xfc1c0001, 0x44140000, &NMD::SH_GP_ , 0, + 0x0 }, /* SH[GP] */ + { reserved_block , 0 , 0 , 32, + 0xfc1c0001, 0x44140001, 0 , 0, + 0x0 }, /* P.GP.SH~*(1) */ +}; + + +NMD::Pool NMD::P_GP_CP1[4] = { + { instruction , 0 , 0 , 32, + 0xfc1c0003, 0x44180000, &NMD::LWC1_GP_ , 0, + CP1_ }, /* LWC1[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0003, 0x44180001, &NMD::SWC1_GP_ , 0, + CP1_ }, /* SWC1[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0003, 0x44180002, &NMD::LDC1_GP_ , 0, + CP1_ }, /* LDC1[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0003, 0x44180003, &NMD::SDC1_GP_ , 0, + CP1_ }, /* SDC1[GP] */ +}; + + +NMD::Pool NMD::P_GP_M64[4] = { + { instruction , 0 , 0 , 32, + 0xfc1c0003, 0x441c0000, &NMD::LWU_GP_ , 0, + MIPS64_ }, /* LWU[GP] */ + { reserved_block , 0 , 0 , 32, + 0xfc1c0003, 0x441c0001, 0 , 0, + 0x0 }, /* P.GP.M64~*(1) */ + { reserved_block , 0 , 0 , 32, + 0xfc1c0003, 0x441c0002, 0 , 0, + 0x0 }, /* P.GP.M64~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc1c0003, 0x441c0003, 0 , 0, + 0x0 }, /* P.GP.M64~*(3) */ +}; + + +NMD::Pool NMD::P_GP_BH[8] = { + { instruction , 0 , 0 , 32, + 0xfc1c0000, 0x44000000, &NMD::LB_GP_ , 0, + 0x0 }, /* LB[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0000, 0x44040000, &NMD::SB_GP_ , 0, + 0x0 }, /* SB[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0000, 0x44080000, &NMD::LBU_GP_ , 0, + 0x0 }, /* LBU[GP] */ + { instruction , 0 , 0 , 32, + 0xfc1c0000, 0x440c0000, &NMD::ADDIU_GP_B_ , 0, + 0x0 }, /* ADDIU[GP.B] */ + { pool , P_GP_LH , 2 , 32, + 0xfc1c0000, 0x44100000, 0 , 0, + 0x0 }, /* P.GP.LH */ + { pool , P_GP_SH , 2 , 32, + 0xfc1c0000, 0x44140000, 0 , 0, + 0x0 }, /* P.GP.SH */ + { pool , P_GP_CP1 , 4 , 32, + 0xfc1c0000, 0x44180000, 0 , 0, + 0x0 }, /* P.GP.CP1 */ + { pool , P_GP_M64 , 4 , 32, + 0xfc1c0000, 0x441c0000, 0 , 0, + 0x0 }, /* P.GP.M64 */ +}; + + +NMD::Pool NMD::P_LS_U12[16] = { + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84000000, &NMD::LB_U12_ , 0, + 0x0 }, /* LB[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84001000, &NMD::SB_U12_ , 0, + 0x0 }, /* SB[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84002000, &NMD::LBU_U12_ , 0, + 0x0 }, /* LBU[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84003000, &NMD::PREF_U12_ , 0, + 0x0 }, /* PREF[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84004000, &NMD::LH_U12_ , 0, + 0x0 }, /* LH[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84005000, &NMD::SH_U12_ , 0, + 0x0 }, /* SH[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84006000, &NMD::LHU_U12_ , 0, + 0x0 }, /* LHU[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84007000, &NMD::LWU_U12_ , 0, + MIPS64_ }, /* LWU[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84008000, &NMD::LW_U12_ , 0, + 0x0 }, /* LW[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x84009000, &NMD::SW_U12_ , 0, + 0x0 }, /* SW[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8400a000, &NMD::LWC1_U12_ , 0, + CP1_ }, /* LWC1[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8400b000, &NMD::SWC1_U12_ , 0, + CP1_ }, /* SWC1[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8400c000, &NMD::LD_U12_ , 0, + MIPS64_ }, /* LD[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8400d000, &NMD::SD_U12_ , 0, + MIPS64_ }, /* SD[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8400e000, &NMD::LDC1_U12_ , 0, + CP1_ }, /* LDC1[U12] */ + { instruction , 0 , 0 , 32, + 0xfc00f000, 0x8400f000, &NMD::SDC1_U12_ , 0, + CP1_ }, /* SDC1[U12] */ +}; + + +NMD::Pool NMD::P_PREF_S9_[2] = { + { instruction , 0 , 0 , 32, + 0xffe07f00, 0xa7e01800, &NMD::SYNCI , 0, + 0x0 }, /* SYNCI */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4001800, &NMD::PREF_S9_ , &NMD::PREF_S9__cond , + 0x0 }, /* PREF[S9] */ +}; + + +NMD::Pool NMD::P_LS_S0[16] = { + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4000000, &NMD::LB_S9_ , 0, + 0x0 }, /* LB[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4000800, &NMD::SB_S9_ , 0, + 0x0 }, /* SB[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4001000, &NMD::LBU_S9_ , 0, + 0x0 }, /* LBU[S9] */ + { pool , P_PREF_S9_ , 2 , 32, + 0xfc007f00, 0xa4001800, 0 , 0, + 0x0 }, /* P.PREF[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4002000, &NMD::LH_S9_ , 0, + 0x0 }, /* LH[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4002800, &NMD::SH_S9_ , 0, + 0x0 }, /* SH[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4003000, &NMD::LHU_S9_ , 0, + 0x0 }, /* LHU[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4003800, &NMD::LWU_S9_ , 0, + MIPS64_ }, /* LWU[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4004000, &NMD::LW_S9_ , 0, + 0x0 }, /* LW[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4004800, &NMD::SW_S9_ , 0, + 0x0 }, /* SW[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4005000, &NMD::LWC1_S9_ , 0, + CP1_ }, /* LWC1[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4005800, &NMD::SWC1_S9_ , 0, + CP1_ }, /* SWC1[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4006000, &NMD::LD_S9_ , 0, + MIPS64_ }, /* LD[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4006800, &NMD::SD_S9_ , 0, + MIPS64_ }, /* SD[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4007000, &NMD::LDC1_S9_ , 0, + CP1_ }, /* LDC1[S9] */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4007800, &NMD::SDC1_S9_ , 0, + CP1_ }, /* SDC1[S9] */ +}; + + +NMD::Pool NMD::ASET_ACLR[2] = { + { instruction , 0 , 0 , 32, + 0xfe007f00, 0xa4001100, &NMD::ASET , 0, + MCU_ }, /* ASET */ + { instruction , 0 , 0 , 32, + 0xfe007f00, 0xa6001100, &NMD::ACLR , 0, + MCU_ }, /* ACLR */ +}; + + +NMD::Pool NMD::P_LL[4] = { + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005100, &NMD::LL , 0, + 0x0 }, /* LL */ + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005101, &NMD::LLWP , 0, + XNP_ }, /* LLWP */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005102, 0 , 0, + 0x0 }, /* P.LL~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005103, 0 , 0, + 0x0 }, /* P.LL~*(3) */ +}; + + +NMD::Pool NMD::P_SC[4] = { + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005900, &NMD::SC , 0, + 0x0 }, /* SC */ + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005901, &NMD::SCWP , 0, + XNP_ }, /* SCWP */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005902, 0 , 0, + 0x0 }, /* P.SC~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005903, 0 , 0, + 0x0 }, /* P.SC~*(3) */ +}; + + +NMD::Pool NMD::P_LLD[8] = { + { instruction , 0 , 0 , 32, + 0xfc007f07, 0xa4007100, &NMD::LLD , 0, + MIPS64_ }, /* LLD */ + { instruction , 0 , 0 , 32, + 0xfc007f07, 0xa4007101, &NMD::LLDP , 0, + MIPS64_ }, /* LLDP */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007102, 0 , 0, + 0x0 }, /* P.LLD~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007103, 0 , 0, + 0x0 }, /* P.LLD~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007104, 0 , 0, + 0x0 }, /* P.LLD~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007105, 0 , 0, + 0x0 }, /* P.LLD~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007106, 0 , 0, + 0x0 }, /* P.LLD~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007107, 0 , 0, + 0x0 }, /* P.LLD~*(7) */ +}; + + +NMD::Pool NMD::P_SCD[8] = { + { instruction , 0 , 0 , 32, + 0xfc007f07, 0xa4007900, &NMD::SCD , 0, + MIPS64_ }, /* SCD */ + { instruction , 0 , 0 , 32, + 0xfc007f07, 0xa4007901, &NMD::SCDP , 0, + MIPS64_ }, /* SCDP */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007902, 0 , 0, + 0x0 }, /* P.SCD~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007903, 0 , 0, + 0x0 }, /* P.SCD~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007904, 0 , 0, + 0x0 }, /* P.SCD~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007905, 0 , 0, + 0x0 }, /* P.SCD~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007906, 0 , 0, + 0x0 }, /* P.SCD~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f07, 0xa4007907, 0 , 0, + 0x0 }, /* P.SCD~*(7) */ +}; + + +NMD::Pool NMD::P_LS_S1[16] = { + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4000100, 0 , 0, + 0x0 }, /* P.LS.S1~*(0) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4000900, 0 , 0, + 0x0 }, /* P.LS.S1~*(1) */ + { pool , ASET_ACLR , 2 , 32, + 0xfc007f00, 0xa4001100, 0 , 0, + 0x0 }, /* ASET_ACLR */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4001900, 0 , 0, + 0x0 }, /* P.LS.S1~*(3) */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4002100, &NMD::UALH , 0, + XMMS_ }, /* UALH */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4002900, &NMD::UASH , 0, + XMMS_ }, /* UASH */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4003100, 0 , 0, + 0x0 }, /* P.LS.S1~*(6) */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4003900, &NMD::CACHE , 0, + CP0_ }, /* CACHE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4004100, &NMD::LWC2 , 0, + CP2_ }, /* LWC2 */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4004900, &NMD::SWC2 , 0, + CP2_ }, /* SWC2 */ + { pool , P_LL , 4 , 32, + 0xfc007f00, 0xa4005100, 0 , 0, + 0x0 }, /* P.LL */ + { pool , P_SC , 4 , 32, + 0xfc007f00, 0xa4005900, 0 , 0, + 0x0 }, /* P.SC */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4006100, &NMD::LDC2 , 0, + CP2_ }, /* LDC2 */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4006900, &NMD::SDC2 , 0, + CP2_ }, /* SDC2 */ + { pool , P_LLD , 8 , 32, + 0xfc007f00, 0xa4007100, 0 , 0, + 0x0 }, /* P.LLD */ + { pool , P_SCD , 8 , 32, + 0xfc007f00, 0xa4007900, 0 , 0, + 0x0 }, /* P.SCD */ +}; + + +NMD::Pool NMD::P_PREFE[2] = { + { instruction , 0 , 0 , 32, + 0xffe07f00, 0xa7e01a00, &NMD::SYNCIE , 0, + CP0_ | EVA_ }, /* SYNCIE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4001a00, &NMD::PREFE , &NMD::PREFE_cond , + CP0_ | EVA_ }, /* PREFE */ +}; + + +NMD::Pool NMD::P_LLE[4] = { + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005200, &NMD::LLE , 0, + CP0_ | EVA_ }, /* LLE */ + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005201, &NMD::LLWPE , 0, + CP0_ | EVA_ }, /* LLWPE */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005202, 0 , 0, + 0x0 }, /* P.LLE~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005203, 0 , 0, + 0x0 }, /* P.LLE~*(3) */ +}; + + +NMD::Pool NMD::P_SCE[4] = { + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005a00, &NMD::SCE , 0, + CP0_ | EVA_ }, /* SCE */ + { instruction , 0 , 0 , 32, + 0xfc007f03, 0xa4005a01, &NMD::SCWPE , 0, + CP0_ | EVA_ }, /* SCWPE */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005a02, 0 , 0, + 0x0 }, /* P.SCE~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f03, 0xa4005a03, 0 , 0, + 0x0 }, /* P.SCE~*(3) */ +}; + + +NMD::Pool NMD::P_LS_E0[16] = { + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4000200, &NMD::LBE , 0, + CP0_ | EVA_ }, /* LBE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4000a00, &NMD::SBE , 0, + CP0_ | EVA_ }, /* SBE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4001200, &NMD::LBUE , 0, + CP0_ | EVA_ }, /* LBUE */ + { pool , P_PREFE , 2 , 32, + 0xfc007f00, 0xa4001a00, 0 , 0, + 0x0 }, /* P.PREFE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4002200, &NMD::LHE , 0, + CP0_ | EVA_ }, /* LHE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4002a00, &NMD::SHE , 0, + CP0_ | EVA_ }, /* SHE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4003200, &NMD::LHUE , 0, + CP0_ | EVA_ }, /* LHUE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4003a00, &NMD::CACHEE , 0, + CP0_ | EVA_ }, /* CACHEE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4004200, &NMD::LWE , 0, + CP0_ | EVA_ }, /* LWE */ + { instruction , 0 , 0 , 32, + 0xfc007f00, 0xa4004a00, &NMD::SWE , 0, + CP0_ | EVA_ }, /* SWE */ + { pool , P_LLE , 4 , 32, + 0xfc007f00, 0xa4005200, 0 , 0, + 0x0 }, /* P.LLE */ + { pool , P_SCE , 4 , 32, + 0xfc007f00, 0xa4005a00, 0 , 0, + 0x0 }, /* P.SCE */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4006200, 0 , 0, + 0x0 }, /* P.LS.E0~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4006a00, 0 , 0, + 0x0 }, /* P.LS.E0~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4007200, 0 , 0, + 0x0 }, /* P.LS.E0~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc007f00, 0xa4007a00, 0 , 0, + 0x0 }, /* P.LS.E0~*(15) */ +}; + + +NMD::Pool NMD::P_LS_WM[2] = { + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000400, &NMD::LWM , 0, + XMMS_ }, /* LWM */ + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000c00, &NMD::SWM , 0, + XMMS_ }, /* SWM */ +}; + + +NMD::Pool NMD::P_LS_UAWM[2] = { + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000500, &NMD::UALWM , 0, + XMMS_ }, /* UALWM */ + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000d00, &NMD::UASWM , 0, + XMMS_ }, /* UASWM */ +}; + + +NMD::Pool NMD::P_LS_DM[2] = { + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000600, &NMD::LDM , 0, + MIPS64_ }, /* LDM */ + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000e00, &NMD::SDM , 0, + MIPS64_ }, /* SDM */ +}; + + +NMD::Pool NMD::P_LS_UADM[2] = { + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000700, &NMD::UALDM , 0, + MIPS64_ }, /* UALDM */ + { instruction , 0 , 0 , 32, + 0xfc000f00, 0xa4000f00, &NMD::UASDM , 0, + MIPS64_ }, /* UASDM */ +}; + + +NMD::Pool NMD::P_LS_S9[8] = { + { pool , P_LS_S0 , 16 , 32, + 0xfc000700, 0xa4000000, 0 , 0, + 0x0 }, /* P.LS.S0 */ + { pool , P_LS_S1 , 16 , 32, + 0xfc000700, 0xa4000100, 0 , 0, + 0x0 }, /* P.LS.S1 */ + { pool , P_LS_E0 , 16 , 32, + 0xfc000700, 0xa4000200, 0 , 0, + 0x0 }, /* P.LS.E0 */ + { reserved_block , 0 , 0 , 32, + 0xfc000700, 0xa4000300, 0 , 0, + 0x0 }, /* P.LS.S9~*(3) */ + { pool , P_LS_WM , 2 , 32, + 0xfc000700, 0xa4000400, 0 , 0, + 0x0 }, /* P.LS.WM */ + { pool , P_LS_UAWM , 2 , 32, + 0xfc000700, 0xa4000500, 0 , 0, + 0x0 }, /* P.LS.UAWM */ + { pool , P_LS_DM , 2 , 32, + 0xfc000700, 0xa4000600, 0 , 0, + 0x0 }, /* P.LS.DM */ + { pool , P_LS_UADM , 2 , 32, + 0xfc000700, 0xa4000700, 0 , 0, + 0x0 }, /* P.LS.UADM */ +}; + + +NMD::Pool NMD::P_BAL[2] = { + { branch_instruction , 0 , 0 , 32, + 0xfe000000, 0x28000000, &NMD::BC_32_ , 0, + 0x0 }, /* BC[32] */ + { call_instruction , 0 , 0 , 32, + 0xfe000000, 0x2a000000, &NMD::BALC_32_ , 0, + 0x0 }, /* BALC[32] */ +}; + + +NMD::Pool NMD::P_BALRSC[2] = { + { branch_instruction , 0 , 0 , 32, + 0xffe0f000, 0x48008000, &NMD::BRSC , 0, + 0x0 }, /* BRSC */ + { call_instruction , 0 , 0 , 32, + 0xfc00f000, 0x48008000, &NMD::BALRSC , &NMD::BALRSC_cond , + 0x0 }, /* BALRSC */ +}; + + +NMD::Pool NMD::P_J[16] = { + { call_instruction , 0 , 0 , 32, + 0xfc00f000, 0x48000000, &NMD::JALRC_32_ , 0, + 0x0 }, /* JALRC[32] */ + { call_instruction , 0 , 0 , 32, + 0xfc00f000, 0x48001000, &NMD::JALRC_HB , 0, + 0x0 }, /* JALRC.HB */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48002000, 0 , 0, + 0x0 }, /* P.J~*(2) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48003000, 0 , 0, + 0x0 }, /* P.J~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48004000, 0 , 0, + 0x0 }, /* P.J~*(4) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48005000, 0 , 0, + 0x0 }, /* P.J~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48006000, 0 , 0, + 0x0 }, /* P.J~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48007000, 0 , 0, + 0x0 }, /* P.J~*(7) */ + { pool , P_BALRSC , 2 , 32, + 0xfc00f000, 0x48008000, 0 , 0, + 0x0 }, /* P.BALRSC */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x48009000, 0 , 0, + 0x0 }, /* P.J~*(9) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x4800a000, 0 , 0, + 0x0 }, /* P.J~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x4800b000, 0 , 0, + 0x0 }, /* P.J~*(11) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x4800c000, 0 , 0, + 0x0 }, /* P.J~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x4800d000, 0 , 0, + 0x0 }, /* P.J~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x4800e000, 0 , 0, + 0x0 }, /* P.J~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc00f000, 0x4800f000, 0 , 0, + 0x0 }, /* P.J~*(15) */ +}; + + +NMD::Pool NMD::P_BR3A[32] = { + { branch_instruction , 0 , 0 , 32, + 0xfc1fc000, 0x88004000, &NMD::BC1EQZC , 0, + CP1_ }, /* BC1EQZC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1fc000, 0x88014000, &NMD::BC1NEZC , 0, + CP1_ }, /* BC1NEZC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1fc000, 0x88024000, &NMD::BC2EQZC , 0, + CP2_ }, /* BC2EQZC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1fc000, 0x88034000, &NMD::BC2NEZC , 0, + CP2_ }, /* BC2NEZC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1fc000, 0x88044000, &NMD::BPOSGE32C , 0, + DSP_ }, /* BPOSGE32C */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88054000, 0 , 0, + 0x0 }, /* P.BR3A~*(5) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88064000, 0 , 0, + 0x0 }, /* P.BR3A~*(6) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88074000, 0 , 0, + 0x0 }, /* P.BR3A~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88084000, 0 , 0, + 0x0 }, /* P.BR3A~*(8) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88094000, 0 , 0, + 0x0 }, /* P.BR3A~*(9) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x880a4000, 0 , 0, + 0x0 }, /* P.BR3A~*(10) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x880b4000, 0 , 0, + 0x0 }, /* P.BR3A~*(11) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x880c4000, 0 , 0, + 0x0 }, /* P.BR3A~*(12) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x880d4000, 0 , 0, + 0x0 }, /* P.BR3A~*(13) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x880e4000, 0 , 0, + 0x0 }, /* P.BR3A~*(14) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x880f4000, 0 , 0, + 0x0 }, /* P.BR3A~*(15) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88104000, 0 , 0, + 0x0 }, /* P.BR3A~*(16) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88114000, 0 , 0, + 0x0 }, /* P.BR3A~*(17) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88124000, 0 , 0, + 0x0 }, /* P.BR3A~*(18) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88134000, 0 , 0, + 0x0 }, /* P.BR3A~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88144000, 0 , 0, + 0x0 }, /* P.BR3A~*(20) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88154000, 0 , 0, + 0x0 }, /* P.BR3A~*(21) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88164000, 0 , 0, + 0x0 }, /* P.BR3A~*(22) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88174000, 0 , 0, + 0x0 }, /* P.BR3A~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88184000, 0 , 0, + 0x0 }, /* P.BR3A~*(24) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x88194000, 0 , 0, + 0x0 }, /* P.BR3A~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x881a4000, 0 , 0, + 0x0 }, /* P.BR3A~*(26) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x881b4000, 0 , 0, + 0x0 }, /* P.BR3A~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x881c4000, 0 , 0, + 0x0 }, /* P.BR3A~*(28) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x881d4000, 0 , 0, + 0x0 }, /* P.BR3A~*(29) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x881e4000, 0 , 0, + 0x0 }, /* P.BR3A~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc1fc000, 0x881f4000, 0 , 0, + 0x0 }, /* P.BR3A~*(31) */ +}; + + +NMD::Pool NMD::P_BR1[4] = { + { branch_instruction , 0 , 0 , 32, + 0xfc00c000, 0x88000000, &NMD::BEQC_32_ , 0, + 0x0 }, /* BEQC[32] */ + { pool , P_BR3A , 32 , 32, + 0xfc00c000, 0x88004000, 0 , 0, + 0x0 }, /* P.BR3A */ + { branch_instruction , 0 , 0 , 32, + 0xfc00c000, 0x88008000, &NMD::BGEC , 0, + 0x0 }, /* BGEC */ + { branch_instruction , 0 , 0 , 32, + 0xfc00c000, 0x8800c000, &NMD::BGEUC , 0, + 0x0 }, /* BGEUC */ +}; + + +NMD::Pool NMD::P_BR2[4] = { + { branch_instruction , 0 , 0 , 32, + 0xfc00c000, 0xa8000000, &NMD::BNEC_32_ , 0, + 0x0 }, /* BNEC[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc00c000, 0xa8004000, 0 , 0, + 0x0 }, /* P.BR2~*(1) */ + { branch_instruction , 0 , 0 , 32, + 0xfc00c000, 0xa8008000, &NMD::BLTC , 0, + 0x0 }, /* BLTC */ + { branch_instruction , 0 , 0 , 32, + 0xfc00c000, 0xa800c000, &NMD::BLTUC , 0, + 0x0 }, /* BLTUC */ +}; + + +NMD::Pool NMD::P_BRI[8] = { + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc8000000, &NMD::BEQIC , 0, + 0x0 }, /* BEQIC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc8040000, &NMD::BBEQZC , 0, + XMMS_ }, /* BBEQZC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc8080000, &NMD::BGEIC , 0, + 0x0 }, /* BGEIC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc80c0000, &NMD::BGEIUC , 0, + 0x0 }, /* BGEIUC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc8100000, &NMD::BNEIC , 0, + 0x0 }, /* BNEIC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc8140000, &NMD::BBNEZC , 0, + XMMS_ }, /* BBNEZC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc8180000, &NMD::BLTIC , 0, + 0x0 }, /* BLTIC */ + { branch_instruction , 0 , 0 , 32, + 0xfc1c0000, 0xc81c0000, &NMD::BLTIUC , 0, + 0x0 }, /* BLTIUC */ +}; + + +NMD::Pool NMD::P32[32] = { + { pool , P_ADDIU , 2 , 32, + 0xfc000000, 0x00000000, 0 , 0, + 0x0 }, /* P.ADDIU */ + { pool , P32A , 8 , 32, + 0xfc000000, 0x20000000, 0 , 0, + 0x0 }, /* P32A */ + { pool , P_GP_W , 4 , 32, + 0xfc000000, 0x40000000, 0 , 0, + 0x0 }, /* P.GP.W */ + { pool , POOL48I , 32 , 48, + 0xfc0000000000ull, 0x600000000000ull, 0 , 0, + 0x0 }, /* POOL48I */ + { pool , P_U12 , 16 , 32, + 0xfc000000, 0x80000000, 0 , 0, + 0x0 }, /* P.U12 */ + { pool , POOL32F , 8 , 32, + 0xfc000000, 0xa0000000, 0 , 0, + CP1_ }, /* POOL32F */ + { pool , POOL32S , 8 , 32, + 0xfc000000, 0xc0000000, 0 , 0, + 0x0 }, /* POOL32S */ + { pool , P_LUI , 2 , 32, + 0xfc000000, 0xe0000000, 0 , 0, + 0x0 }, /* P.LUI */ + { instruction , 0 , 0 , 32, + 0xfc000000, 0x04000000, &NMD::ADDIUPC_32_ , 0, + 0x0 }, /* ADDIUPC[32] */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x24000000, 0 , 0, + 0x0 }, /* P32~*(5) */ + { pool , P_GP_BH , 8 , 32, + 0xfc000000, 0x44000000, 0 , 0, + 0x0 }, /* P.GP.BH */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x64000000, 0 , 0, + 0x0 }, /* P32~*(13) */ + { pool , P_LS_U12 , 16 , 32, + 0xfc000000, 0x84000000, 0 , 0, + 0x0 }, /* P.LS.U12 */ + { pool , P_LS_S9 , 8 , 32, + 0xfc000000, 0xa4000000, 0 , 0, + 0x0 }, /* P.LS.S9 */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0xc4000000, 0 , 0, + 0x0 }, /* P32~*(25) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0xe4000000, 0 , 0, + 0x0 }, /* P32~*(29) */ + { call_instruction , 0 , 0 , 32, + 0xfc000000, 0x08000000, &NMD::MOVE_BALC , 0, + XMMS_ }, /* MOVE.BALC */ + { pool , P_BAL , 2 , 32, + 0xfc000000, 0x28000000, 0 , 0, + 0x0 }, /* P.BAL */ + { pool , P_J , 16 , 32, + 0xfc000000, 0x48000000, 0 , 0, + 0x0 }, /* P.J */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x68000000, 0 , 0, + 0x0 }, /* P32~*(14) */ + { pool , P_BR1 , 4 , 32, + 0xfc000000, 0x88000000, 0 , 0, + 0x0 }, /* P.BR1 */ + { pool , P_BR2 , 4 , 32, + 0xfc000000, 0xa8000000, 0 , 0, + 0x0 }, /* P.BR2 */ + { pool , P_BRI , 8 , 32, + 0xfc000000, 0xc8000000, 0 , 0, + 0x0 }, /* P.BRI */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0xe8000000, 0 , 0, + 0x0 }, /* P32~*(30) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x0c000000, 0 , 0, + 0x0 }, /* P32~*(3) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x2c000000, 0 , 0, + 0x0 }, /* P32~*(7) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x4c000000, 0 , 0, + 0x0 }, /* P32~*(11) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x6c000000, 0 , 0, + 0x0 }, /* P32~*(15) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0x8c000000, 0 , 0, + 0x0 }, /* P32~*(19) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0xac000000, 0 , 0, + 0x0 }, /* P32~*(23) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0xcc000000, 0 , 0, + 0x0 }, /* P32~*(27) */ + { reserved_block , 0 , 0 , 32, + 0xfc000000, 0xec000000, 0 , 0, + 0x0 }, /* P32~*(31) */ +}; + + +NMD::Pool NMD::P16_SYSCALL[2] = { + { instruction , 0 , 0 , 16, + 0xfffc , 0x1008 , &NMD::SYSCALL_16_ , 0, + 0x0 }, /* SYSCALL[16] */ + { instruction , 0 , 0 , 16, + 0xfffc , 0x100c , &NMD::HYPCALL_16_ , 0, + CP0_ | VZ_ }, /* HYPCALL[16] */ +}; + + +NMD::Pool NMD::P16_RI[4] = { + { reserved_block , 0 , 0 , 16, + 0xfff8 , 0x1000 , 0 , 0, + 0x0 }, /* P16.RI~*(0) */ + { pool , P16_SYSCALL , 2 , 16, + 0xfff8 , 0x1008 , 0 , 0, + 0x0 }, /* P16.SYSCALL */ + { instruction , 0 , 0 , 16, + 0xfff8 , 0x1010 , &NMD::BREAK_16_ , 0, + 0x0 }, /* BREAK[16] */ + { instruction , 0 , 0 , 16, + 0xfff8 , 0x1018 , &NMD::SDBBP_16_ , 0, + EJTAG_ }, /* SDBBP[16] */ +}; + + +NMD::Pool NMD::P16_MV[2] = { + { pool , P16_RI , 4 , 16, + 0xffe0 , 0x1000 , 0 , 0, + 0x0 }, /* P16.RI */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0x1000 , &NMD::MOVE , &NMD::MOVE_cond , + 0x0 }, /* MOVE */ +}; + + +NMD::Pool NMD::P16_SHIFT[2] = { + { instruction , 0 , 0 , 16, + 0xfc08 , 0x3000 , &NMD::SLL_16_ , 0, + 0x0 }, /* SLL[16] */ + { instruction , 0 , 0 , 16, + 0xfc08 , 0x3008 , &NMD::SRL_16_ , 0, + 0x0 }, /* SRL[16] */ +}; + + +NMD::Pool NMD::POOL16C_00[4] = { + { instruction , 0 , 0 , 16, + 0xfc0f , 0x5000 , &NMD::NOT_16_ , 0, + 0x0 }, /* NOT[16] */ + { instruction , 0 , 0 , 16, + 0xfc0f , 0x5004 , &NMD::XOR_16_ , 0, + 0x0 }, /* XOR[16] */ + { instruction , 0 , 0 , 16, + 0xfc0f , 0x5008 , &NMD::AND_16_ , 0, + 0x0 }, /* AND[16] */ + { instruction , 0 , 0 , 16, + 0xfc0f , 0x500c , &NMD::OR_16_ , 0, + 0x0 }, /* OR[16] */ +}; + + +NMD::Pool NMD::POOL16C_0[2] = { + { pool , POOL16C_00 , 4 , 16, + 0xfc03 , 0x5000 , 0 , 0, + 0x0 }, /* POOL16C_00 */ + { reserved_block , 0 , 0 , 16, + 0xfc03 , 0x5002 , 0 , 0, + 0x0 }, /* POOL16C_0~*(1) */ +}; + + +NMD::Pool NMD::P16C[2] = { + { pool , POOL16C_0 , 2 , 16, + 0xfc01 , 0x5000 , 0 , 0, + 0x0 }, /* POOL16C_0 */ + { instruction , 0 , 0 , 16, + 0xfc01 , 0x5001 , &NMD::LWXS_16_ , 0, + 0x0 }, /* LWXS[16] */ +}; + + +NMD::Pool NMD::P16_A1[2] = { + { reserved_block , 0 , 0 , 16, + 0xfc40 , 0x7000 , 0 , 0, + 0x0 }, /* P16.A1~*(0) */ + { instruction , 0 , 0 , 16, + 0xfc40 , 0x7040 , &NMD::ADDIU_R1_SP_ , 0, + 0x0 }, /* ADDIU[R1.SP] */ +}; + + +NMD::Pool NMD::P_ADDIU_RS5_[2] = { + { instruction , 0 , 0 , 16, + 0xffe8 , 0x9008 , &NMD::NOP_16_ , 0, + 0x0 }, /* NOP[16] */ + { instruction , 0 , 0 , 16, + 0xfc08 , 0x9008 , &NMD::ADDIU_RS5_ , &NMD::ADDIU_RS5__cond , + 0x0 }, /* ADDIU[RS5] */ +}; + + +NMD::Pool NMD::P16_A2[2] = { + { instruction , 0 , 0 , 16, + 0xfc08 , 0x9000 , &NMD::ADDIU_R2_ , 0, + 0x0 }, /* ADDIU[R2] */ + { pool , P_ADDIU_RS5_ , 2 , 16, + 0xfc08 , 0x9008 , 0 , 0, + 0x0 }, /* P.ADDIU[RS5] */ +}; + + +NMD::Pool NMD::P16_ADDU[2] = { + { instruction , 0 , 0 , 16, + 0xfc01 , 0xb000 , &NMD::ADDU_16_ , 0, + 0x0 }, /* ADDU[16] */ + { instruction , 0 , 0 , 16, + 0xfc01 , 0xb001 , &NMD::SUBU_16_ , 0, + 0x0 }, /* SUBU[16] */ +}; + + +NMD::Pool NMD::P16_JRC[2] = { + { branch_instruction , 0 , 0 , 16, + 0xfc1f , 0xd800 , &NMD::JRC , 0, + 0x0 }, /* JRC */ + { call_instruction , 0 , 0 , 16, + 0xfc1f , 0xd810 , &NMD::JALRC_16_ , 0, + 0x0 }, /* JALRC[16] */ +}; + + +NMD::Pool NMD::P16_BR1[2] = { + { branch_instruction , 0 , 0 , 16, + 0xfc00 , 0xd800 , &NMD::BEQC_16_ , &NMD::BEQC_16__cond , + XMMS_ }, /* BEQC[16] */ + { branch_instruction , 0 , 0 , 16, + 0xfc00 , 0xd800 , &NMD::BNEC_16_ , &NMD::BNEC_16__cond , + XMMS_ }, /* BNEC[16] */ +}; + + +NMD::Pool NMD::P16_BR[2] = { + { pool , P16_JRC , 2 , 16, + 0xfc0f , 0xd800 , 0 , 0, + 0x0 }, /* P16.JRC */ + { pool , P16_BR1 , 2 , 16, + 0xfc00 , 0xd800 , 0 , &NMD::P16_BR1_cond , + 0x0 }, /* P16.BR1 */ +}; + + +NMD::Pool NMD::P16_SR[2] = { + { instruction , 0 , 0 , 16, + 0xfd00 , 0x1c00 , &NMD::SAVE_16_ , 0, + 0x0 }, /* SAVE[16] */ + { return_instruction , 0 , 0 , 16, + 0xfd00 , 0x1d00 , &NMD::RESTORE_JRC_16_ , 0, + 0x0 }, /* RESTORE.JRC[16] */ +}; + + +NMD::Pool NMD::P16_4X4[4] = { + { instruction , 0 , 0 , 16, + 0xfd08 , 0x3c00 , &NMD::ADDU_4X4_ , 0, + XMMS_ }, /* ADDU[4X4] */ + { instruction , 0 , 0 , 16, + 0xfd08 , 0x3c08 , &NMD::MUL_4X4_ , 0, + XMMS_ }, /* MUL[4X4] */ + { reserved_block , 0 , 0 , 16, + 0xfd08 , 0x3d00 , 0 , 0, + 0x0 }, /* P16.4X4~*(2) */ + { reserved_block , 0 , 0 , 16, + 0xfd08 , 0x3d08 , 0 , 0, + 0x0 }, /* P16.4X4~*(3) */ +}; + + +NMD::Pool NMD::P16_LB[4] = { + { instruction , 0 , 0 , 16, + 0xfc0c , 0x5c00 , &NMD::LB_16_ , 0, + 0x0 }, /* LB[16] */ + { instruction , 0 , 0 , 16, + 0xfc0c , 0x5c04 , &NMD::SB_16_ , 0, + 0x0 }, /* SB[16] */ + { instruction , 0 , 0 , 16, + 0xfc0c , 0x5c08 , &NMD::LBU_16_ , 0, + 0x0 }, /* LBU[16] */ + { reserved_block , 0 , 0 , 16, + 0xfc0c , 0x5c0c , 0 , 0, + 0x0 }, /* P16.LB~*(3) */ +}; + + +NMD::Pool NMD::P16_LH[4] = { + { instruction , 0 , 0 , 16, + 0xfc09 , 0x7c00 , &NMD::LH_16_ , 0, + 0x0 }, /* LH[16] */ + { instruction , 0 , 0 , 16, + 0xfc09 , 0x7c01 , &NMD::SH_16_ , 0, + 0x0 }, /* SH[16] */ + { instruction , 0 , 0 , 16, + 0xfc09 , 0x7c08 , &NMD::LHU_16_ , 0, + 0x0 }, /* LHU[16] */ + { reserved_block , 0 , 0 , 16, + 0xfc09 , 0x7c09 , 0 , 0, + 0x0 }, /* P16.LH~*(3) */ +}; + + +NMD::Pool NMD::P16[32] = { + { pool , P16_MV , 2 , 16, + 0xfc00 , 0x1000 , 0 , 0, + 0x0 }, /* P16.MV */ + { pool , P16_SHIFT , 2 , 16, + 0xfc00 , 0x3000 , 0 , 0, + 0x0 }, /* P16.SHIFT */ + { pool , P16C , 2 , 16, + 0xfc00 , 0x5000 , 0 , 0, + 0x0 }, /* P16C */ + { pool , P16_A1 , 2 , 16, + 0xfc00 , 0x7000 , 0 , 0, + 0x0 }, /* P16.A1 */ + { pool , P16_A2 , 2 , 16, + 0xfc00 , 0x9000 , 0 , 0, + 0x0 }, /* P16.A2 */ + { pool , P16_ADDU , 2 , 16, + 0xfc00 , 0xb000 , 0 , 0, + 0x0 }, /* P16.ADDU */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xd000 , &NMD::LI_16_ , 0, + 0x0 }, /* LI[16] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xf000 , &NMD::ANDI_16_ , 0, + 0x0 }, /* ANDI[16] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0x1400 , &NMD::LW_16_ , 0, + 0x0 }, /* LW[16] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0x3400 , &NMD::LW_SP_ , 0, + 0x0 }, /* LW[SP] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0x5400 , &NMD::LW_GP16_ , 0, + 0x0 }, /* LW[GP16] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0x7400 , &NMD::LW_4X4_ , 0, + XMMS_ }, /* LW[4X4] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0x9400 , &NMD::SW_16_ , 0, + 0x0 }, /* SW[16] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xb400 , &NMD::SW_SP_ , 0, + 0x0 }, /* SW[SP] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xd400 , &NMD::SW_GP16_ , 0, + 0x0 }, /* SW[GP16] */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xf400 , &NMD::SW_4X4_ , 0, + XMMS_ }, /* SW[4X4] */ + { branch_instruction , 0 , 0 , 16, + 0xfc00 , 0x1800 , &NMD::BC_16_ , 0, + 0x0 }, /* BC[16] */ + { call_instruction , 0 , 0 , 16, + 0xfc00 , 0x3800 , &NMD::BALC_16_ , 0, + 0x0 }, /* BALC[16] */ + { reserved_block , 0 , 0 , 16, + 0xfc00 , 0x5800 , 0 , 0, + 0x0 }, /* P16~*(10) */ + { reserved_block , 0 , 0 , 16, + 0xfc00 , 0x7800 , 0 , 0, + 0x0 }, /* P16~*(14) */ + { branch_instruction , 0 , 0 , 16, + 0xfc00 , 0x9800 , &NMD::BEQZC_16_ , 0, + 0x0 }, /* BEQZC[16] */ + { branch_instruction , 0 , 0 , 16, + 0xfc00 , 0xb800 , &NMD::BNEZC_16_ , 0, + 0x0 }, /* BNEZC[16] */ + { pool , P16_BR , 2 , 16, + 0xfc00 , 0xd800 , 0 , 0, + 0x0 }, /* P16.BR */ + { reserved_block , 0 , 0 , 16, + 0xfc00 , 0xf800 , 0 , 0, + 0x0 }, /* P16~*(30) */ + { pool , P16_SR , 2 , 16, + 0xfc00 , 0x1c00 , 0 , 0, + 0x0 }, /* P16.SR */ + { pool , P16_4X4 , 4 , 16, + 0xfc00 , 0x3c00 , 0 , 0, + 0x0 }, /* P16.4X4 */ + { pool , P16_LB , 4 , 16, + 0xfc00 , 0x5c00 , 0 , 0, + 0x0 }, /* P16.LB */ + { pool , P16_LH , 4 , 16, + 0xfc00 , 0x7c00 , 0 , 0, + 0x0 }, /* P16.LH */ + { reserved_block , 0 , 0 , 16, + 0xfc00 , 0x9c00 , 0 , 0, + 0x0 }, /* P16~*(19) */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xbc00 , &NMD::MOVEP , 0, + XMMS_ }, /* MOVEP */ + { reserved_block , 0 , 0 , 16, + 0xfc00 , 0xdc00 , 0 , 0, + 0x0 }, /* P16~*(27) */ + { instruction , 0 , 0 , 16, + 0xfc00 , 0xfc00 , &NMD::MOVEP_REV_ , 0, + XMMS_ }, /* MOVEP[REV] */ +}; + + +NMD::Pool NMD::MAJOR[2] = { + { pool , P32 , 32 , 32, + 0x10000000, 0x00000000, 0 , 0, + 0x0 }, /* P32 */ + { pool , P16 , 32 , 16, + 0x1000 , 0x1000 , 0 , 0, + 0x0 }, /* P16 */ +}; diff --git a/disas/nanomips.h b/disas/nanomips.h new file mode 100644 index 0000000000..243c3e38d2 --- /dev/null +++ b/disas/nanomips.h @@ -0,0 +1,1076 @@ +/* + * Header file for nanoMIPS disassembler component of QEMU + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Matthew Fortune + * Copyright (C) 2018 Aleksandar Markovic + * + * 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 NANOMIPS_DISASSEMBLER_H +#define NANOMIPS_DISASSEMBLER_H + +#include + +typedef int64_t int64; +typedef uint64_t uint64; +typedef uint32_t uint32; +typedef uint16_t uint16; + +namespace img +{ + typedef uint64_t address; +} + + +class NMD +{ +public: + + enum TABLE_ENTRY_TYPE { + instruction, + call_instruction, + branch_instruction, + return_instruction, + reserved_block, + pool, + }; + + enum TABLE_ATTRIBUTE_TYPE { + MIPS64_ = 0x00000001, + XNP_ = 0x00000002, + XMMS_ = 0x00000004, + EVA_ = 0x00000008, + DSP_ = 0x00000010, + MT_ = 0x00000020, + EJTAG_ = 0x00000040, + TLBINV_ = 0x00000080, + CP0_ = 0x00000100, + CP1_ = 0x00000200, + CP2_ = 0x00000400, + UDI_ = 0x00000800, + MCU_ = 0x00001000, + VZ_ = 0x00002000, + TLB_ = 0x00004000, + MVH_ = 0x00008000, + ALL_ATTRIBUTES = 0xffffffffull, + }; + + + NMD(img::address pc, TABLE_ATTRIBUTE_TYPE requested_instruction_categories) + : m_pc(pc) + , m_requested_instruction_categories(requested_instruction_categories) + { + } + + int Disassemble(const uint16 *data, std::string & dis, + TABLE_ENTRY_TYPE & type); + +private: + + img::address m_pc; + TABLE_ATTRIBUTE_TYPE m_requested_instruction_categories; + + typedef std::string(NMD:: *disassembly_function)(uint64 instruction); + typedef bool(NMD:: *conditional_function)(uint64 instruction); + + struct Pool { + TABLE_ENTRY_TYPE type; + struct Pool *next_table; + int next_table_size; + int instructions_size; + uint64 mask; + uint64 value; + disassembly_function disassembly; + conditional_function condition; + uint64 attributes; + }; + + uint64 extract_op_code_value(const uint16 *data, int size); + int Disassemble(const uint16 *data, std::string & dis, + TABLE_ENTRY_TYPE & type, const Pool *table, int table_size); + + uint64 renumber_registers(uint64 index, uint64 *register_list, + size_t register_list_size); + + uint64 decode_gpr_gpr4(uint64 d); + uint64 decode_gpr_gpr4_zero(uint64 d); + uint64 decode_gpr_gpr3(uint64 d); + uint64 decode_gpr_gpr3_src_store(uint64 d); + uint64 decode_gpr_gpr2_reg1(uint64 d); + uint64 decode_gpr_gpr2_reg2(uint64 d); + uint64 decode_gpr_gpr1(uint64 d); + + uint64 copy(uint64 d); + int64 copy(int64 d); + int64 neg_copy(uint64 d); + int64 neg_copy(int64 d); + uint64 encode_rs3_and_check_rs3_ge_rt3(uint64 d); + uint64 encode_rs3_and_check_rs3_lt_rt3(uint64 d); + uint64 encode_s_from_address(uint64 d); + uint64 encode_u_from_address(uint64 d); + uint64 encode_s_from_s_hi(uint64 d); + uint64 encode_count3_from_count(uint64 d); + uint64 encode_shift3_from_shift(uint64 d); + int64 encode_eu_from_s_li16(uint64 d); + uint64 encode_msbd_from_size(uint64 d); + uint64 encode_eu_from_u_andi16(uint64 d); + + uint64 encode_msbd_from_pos_and_size(uint64 d); + + uint64 encode_rt1_from_rt(uint64 d); + uint64 encode_lsb_from_pos_and_size(uint64 d); + + std::string save_restore_list(uint64 rt, uint64 count, uint64 gp); + + std::string GPR(uint64 reg); + std::string FPR(uint64 reg); + std::string AC(uint64 reg); + std::string IMMEDIATE(uint64 value); + std::string IMMEDIATE(int64 value); + std::string CPR(uint64 reg); + std::string ADDRESS(uint64 value, int instruction_size); + + int64 extract_s__se3_4_2_1_0(uint64 instruction); + int64 extract_s__se7_0_6_5_4_3_2_1_s1(uint64 instruction); + int64 extract_s__se8_15_7_6_5_4_3_s3(uint64 instruction); + int64 extract_s__se8_15_7_6_5_4_3_2_s2(uint64 instruction); + int64 extract_s__se8_15_7_6_5_4_3_2_1_0(uint64 instruction); + int64 extract_s__se9_20_19_18_17_16_15_14_13_12_11(uint64 instruction); + int64 extract_s__se10_0_9_8_7_6_5_4_3_2_1_s1(uint64 instruction); + int64 extract_s__se11_0_10_9_8_7_6_5_4_3_2_1_0_s1(uint64 instruction); + int64 extract_s__se14_0_13_to_1_s1(uint64 instruction); + int64 extract_s__se21_0_20_to_1_s1(uint64 instruction); + int64 extract_s__se25_0_24_to_1_s1(uint64 instruction); + int64 extract_s__se31_15_to_0_31_to_16(uint64 instruction); + int64 extract_s__se31_0_11_to_2_20_to_12_s12(uint64 instruction); + int64 extract_shift__se5_21_20_19_18_17_16(uint64 instruction); + + uint64 extract_ac_15_14(uint64 instruction); + uint64 extract_bit_16_15_14_13_12_11(uint64 instruction); + uint64 extract_bit_23_22_21(uint64 instruction); + uint64 extract_c0s_20_19_18_17_16(uint64 instruction); + uint64 extract_code_17_to_0(uint64 instruction); + uint64 extract_code_18_to_0(uint64 instruction); + uint64 extract_code_1_0(uint64 instruction); + uint64 extract_code_2_1_0(uint64 instruction); + uint64 extract_code_25_24_23_22_21_20_19_18_17_16(uint64 instruction); + uint64 extract_cofun_25_24_23(uint64 instruction); + uint64 extract_count3_14_13_12(uint64 instruction); + uint64 extract_count_3_2_1_0(uint64 instruction); + uint64 extract_count_19_18_17_16(uint64 instruction); + uint64 extract_cs_20_19_18_17_16(uint64 instruction); + uint64 extract_cs_25_24_23_22_21(uint64 instruction); + uint64 extract_ct_25_24_23_22_21(uint64 instruction); + uint64 extract_eu_3_2_1_0(uint64 instruction); + uint64 extract_eu_6_5_4_3_2_1_0(uint64 instruction); + uint64 extract_fd_15_14_13_12_11(uint64 instruction); + uint64 extract_fs_20_19_18_17_16(uint64 instruction); + uint64 extract_ft_15_14_13_12_11(uint64 instruction); + uint64 extract_ft_25_24_23_22_21(uint64 instruction); + uint64 extract_gp_2(uint64 instruction); + uint64 extract_hint_25_24_23_22_21(uint64 instruction); + uint64 extract_hs_20_19_18_17_16(uint64 instruction); + uint64 extract_lsb_4_3_2_1_0(uint64 instruction); + uint64 extract_mask_20_19_18_17_16_15_14(uint64 instruction); + uint64 extract_msbt_10_9_8_7_6(uint64 instruction); + uint64 extract_op_25_24_23_22_21(uint64 instruction); + uint64 extract_op_25_to_3(uint64 instruction); + uint64 extract_rdl_25_24(uint64 instruction); + uint64 extract_rd2_3_8(uint64 instruction); + uint64 extract_rd3_3_2_1(uint64 instruction); + uint64 extract_rd_15_14_13_12_11(uint64 instruction); + uint64 extract_rs3_6_5_4(uint64 instruction); + uint64 extract_rs4_4_2_1_0(uint64 instruction); + uint64 extract_rs_4_3_2_1_0(uint64 instruction); + uint64 extract_rs_20_19_18_17_16(uint64 instruction); + uint64 extract_rsz4_4_2_1_0(uint64 instruction); + uint64 extract_rtl_11(uint64 instruction); + uint64 extract_rt3_9_8_7(uint64 instruction); + uint64 extract_rt4_9_7_6_5(uint64 instruction); + uint64 extract_rt_25_24_23_22_21(uint64 instruction); + uint64 extract_rt_41_40_39_38_37(uint64 instruction); + uint64 extract_rt_9_8_7_6_5(uint64 instruction); + uint64 extract_rtz3_9_8_7(uint64 instruction); + uint64 extract_rtz4_27_26_25_23_22_21(uint64 instruction); + uint64 extract_rtz4_9_7_6_5(uint64 instruction); + uint64 extract_ru_7_6_5_4_3(uint64 instruction); + uint64 extract_sa_15_14_13_12_11(uint64 instruction); + uint64 extract_sa_15_14_13_12(uint64 instruction); + uint64 extract_sa_15_14_13(uint64 instruction); + uint64 extract_sel_13_12_11(uint64 instruction); + uint64 extract_sel_15_14_13_12_11(uint64 instruction); + uint64 extract_shift3_2_1_0(uint64 instruction); + uint64 extract_shift_4_3_2_1_0(uint64 instruction); + uint64 extract_shift_5_4_3_2_1_0(uint64 instruction); + uint64 extract_shift_20_19_18_17_16(uint64 instruction); + uint64 extract_shift_10_9_8_7_6(uint64 instruction); + uint64 extract_shiftx_11_10_9_8_7_6(uint64 instruction); + uint64 extract_shiftx_10_9_8_7__s1(uint64 instruction); + uint64 extract_size_20_19_18_17_16(uint64 instruction); + uint64 extract_stripe_6(uint64 instruction); + uint64 extract_stype_20_19_18_17_16(uint64 instruction); + uint64 extract_u2_10_9(uint64 instruction); + uint64 extract_u_11_10_9_8_7_6_5_4_3_2_1_0(uint64 instruction); + uint64 extract_u_15_to_0(uint64 instruction); + uint64 extract_u_17_to_0(uint64 instruction); + uint64 extract_u_1_0(uint64 instruction); + uint64 extract_u_3_2_1_0__s1(uint64 instruction); + uint64 extract_u_2_1_0__s2(uint64 instruction); + uint64 extract_u_3_2_1_0__s2(uint64 instruction); + uint64 extract_u_4_3_2_1_0__s2(uint64 instruction); + uint64 extract_u_5_4_3_2_1_0__s2(uint64 instruction); + uint64 extract_u_6_5_4_3_2_1_0__s2(uint64 instruction); + uint64 extract_u_31_to_0__s32(uint64 instruction); + uint64 extract_u_10(uint64 instruction); + uint64 extract_u_17_16_15_14_13_12_11(uint64 instruction); + uint64 extract_u_20_19_18_17_16_15_14_13(uint64 instruction); + uint64 extract_u_17_to_1__s1(uint64 instruction); + uint64 extract_u_2_1__s1(uint64 instruction); + uint64 extract_u_17_to_2__s2(uint64 instruction); + uint64 extract_u_20_to_2__s2(uint64 instruction); + uint64 extract_u_20_to_3__s3(uint64 instruction); + uint64 extract_u_3_8__s2(uint64 instruction); + uint64 extract_u_11_10_9_8_7_6_5_4_3__s3(uint64 instruction); + uint64 extract_u_7_6_5_4__s4(uint64 instruction); + + bool ADDIU_32__cond(uint64 instruction); + bool ADDIU_RS5__cond(uint64 instruction); + bool BALRSC_cond(uint64 instruction); + bool BEQC_16__cond(uint64 instruction); + bool BNEC_16__cond(uint64 instruction); + bool MOVE_cond(uint64 instruction); + bool P16_BR1_cond(uint64 instruction); + bool PREF_S9__cond(uint64 instruction); + bool PREFE_cond(uint64 instruction); + bool SLTU_cond(uint64 instruction); + + std::string ABS_D(uint64 instruction); + std::string ABS_S(uint64 instruction); + std::string ABSQ_S_PH(uint64 instruction); + std::string ABSQ_S_QB(uint64 instruction); + std::string ABSQ_S_W(uint64 instruction); + std::string ACLR(uint64 instruction); + std::string ADD(uint64 instruction); + std::string ADD_D(uint64 instruction); + std::string ADD_S(uint64 instruction); + std::string ADDIU_32_(uint64 instruction); + std::string ADDIU_48_(uint64 instruction); + std::string ADDIU_GP48_(uint64 instruction); + std::string ADDIU_GP_B_(uint64 instruction); + std::string ADDIU_GP_W_(uint64 instruction); + std::string ADDIU_NEG_(uint64 instruction); + std::string ADDIU_R1_SP_(uint64 instruction); + std::string ADDIU_R2_(uint64 instruction); + std::string ADDIU_RS5_(uint64 instruction); + std::string ADDIUPC_32_(uint64 instruction); + std::string ADDIUPC_48_(uint64 instruction); + std::string ADDQ_PH(uint64 instruction); + std::string ADDQ_S_PH(uint64 instruction); + std::string ADDQ_S_W(uint64 instruction); + std::string ADDQH_PH(uint64 instruction); + std::string ADDQH_R_PH(uint64 instruction); + std::string ADDQH_R_W(uint64 instruction); + std::string ADDQH_W(uint64 instruction); + std::string ADDSC(uint64 instruction); + std::string ADDU_16_(uint64 instruction); + std::string ADDU_32_(uint64 instruction); + std::string ADDU_4X4_(uint64 instruction); + std::string ADDU_PH(uint64 instruction); + std::string ADDU_QB(uint64 instruction); + std::string ADDU_S_PH(uint64 instruction); + std::string ADDU_S_QB(uint64 instruction); + std::string ADDUH_QB(uint64 instruction); + std::string ADDUH_R_QB(uint64 instruction); + std::string ADDWC(uint64 instruction); + std::string ALUIPC(uint64 instruction); + std::string AND_16_(uint64 instruction); + std::string AND_32_(uint64 instruction); + std::string ANDI_16_(uint64 instruction); + std::string ANDI_32_(uint64 instruction); + std::string APPEND(uint64 instruction); + std::string ASET(uint64 instruction); + std::string BALC_16_(uint64 instruction); + std::string BALC_32_(uint64 instruction); + std::string BALRSC(uint64 instruction); + std::string BBEQZC(uint64 instruction); + std::string BBNEZC(uint64 instruction); + std::string BC_16_(uint64 instruction); + std::string BC_32_(uint64 instruction); + std::string BC1EQZC(uint64 instruction); + std::string BC1NEZC(uint64 instruction); + std::string BC2EQZC(uint64 instruction); + std::string BC2NEZC(uint64 instruction); + std::string BEQC_16_(uint64 instruction); + std::string BEQC_32_(uint64 instruction); + std::string BEQIC(uint64 instruction); + std::string BEQZC_16_(uint64 instruction); + std::string BGEC(uint64 instruction); + std::string BGEIC(uint64 instruction); + std::string BGEIUC(uint64 instruction); + std::string BGEUC(uint64 instruction); + std::string BLTC(uint64 instruction); + std::string BLTIC(uint64 instruction); + std::string BLTIUC(uint64 instruction); + std::string BLTUC(uint64 instruction); + std::string BNEC_16_(uint64 instruction); + std::string BNEC_32_(uint64 instruction); + std::string BNEIC(uint64 instruction); + std::string BNEZC_16_(uint64 instruction); + std::string BPOSGE32C(uint64 instruction); + std::string BREAK_16_(uint64 instruction); + std::string BREAK_32_(uint64 instruction); + std::string BRSC(uint64 instruction); + std::string CACHE(uint64 instruction); + std::string CACHEE(uint64 instruction); + std::string CEIL_L_D(uint64 instruction); + std::string CEIL_L_S(uint64 instruction); + std::string CEIL_W_D(uint64 instruction); + std::string CEIL_W_S(uint64 instruction); + std::string CFC1(uint64 instruction); + std::string CFC2(uint64 instruction); + std::string CLASS_D(uint64 instruction); + std::string CLASS_S(uint64 instruction); + std::string CLO(uint64 instruction); + std::string CLZ(uint64 instruction); + std::string CMP_AF_D(uint64 instruction); + std::string CMP_AF_S(uint64 instruction); + std::string CMP_EQ_D(uint64 instruction); + std::string CMP_EQ_PH(uint64 instruction); + std::string CMP_EQ_S(uint64 instruction); + std::string CMP_LE_D(uint64 instruction); + std::string CMP_LE_PH(uint64 instruction); + std::string CMP_LE_S(uint64 instruction); + std::string CMP_LT_D(uint64 instruction); + std::string CMP_LT_PH(uint64 instruction); + std::string CMP_LT_S(uint64 instruction); + std::string CMP_NE_D(uint64 instruction); + std::string CMP_NE_S(uint64 instruction); + std::string CMP_OR_D(uint64 instruction); + std::string CMP_OR_S(uint64 instruction); + std::string CMP_SAF_D(uint64 instruction); + std::string CMP_SAF_S(uint64 instruction); + std::string CMP_SEQ_D(uint64 instruction); + std::string CMP_SEQ_S(uint64 instruction); + std::string CMP_SLE_D(uint64 instruction); + std::string CMP_SLE_S(uint64 instruction); + std::string CMP_SLT_D(uint64 instruction); + std::string CMP_SLT_S(uint64 instruction); + std::string CMP_SNE_D(uint64 instruction); + std::string CMP_SNE_S(uint64 instruction); + std::string CMP_SOR_D(uint64 instruction); + std::string CMP_SOR_S(uint64 instruction); + std::string CMP_SUEQ_D(uint64 instruction); + std::string CMP_SUEQ_S(uint64 instruction); + std::string CMP_SULE_D(uint64 instruction); + std::string CMP_SULE_S(uint64 instruction); + std::string CMP_SULT_D(uint64 instruction); + std::string CMP_SULT_S(uint64 instruction); + std::string CMP_SUN_D(uint64 instruction); + std::string CMP_SUN_S(uint64 instruction); + std::string CMP_SUNE_D(uint64 instruction); + std::string CMP_SUNE_S(uint64 instruction); + std::string CMP_UEQ_D(uint64 instruction); + std::string CMP_UEQ_S(uint64 instruction); + std::string CMP_ULE_D(uint64 instruction); + std::string CMP_ULE_S(uint64 instruction); + std::string CMP_ULT_D(uint64 instruction); + std::string CMP_ULT_S(uint64 instruction); + std::string CMP_UN_D(uint64 instruction); + std::string CMP_UN_S(uint64 instruction); + std::string CMP_UNE_D(uint64 instruction); + std::string CMP_UNE_S(uint64 instruction); + std::string CMPGDU_EQ_QB(uint64 instruction); + std::string CMPGDU_LE_QB(uint64 instruction); + std::string CMPGDU_LT_QB(uint64 instruction); + std::string CMPGU_EQ_QB(uint64 instruction); + std::string CMPGU_LE_QB(uint64 instruction); + std::string CMPGU_LT_QB(uint64 instruction); + std::string CMPU_EQ_QB(uint64 instruction); + std::string CMPU_LE_QB(uint64 instruction); + std::string CMPU_LT_QB(uint64 instruction); + std::string COP2_1(uint64 instruction); + std::string CTC1(uint64 instruction); + std::string CTC2(uint64 instruction); + std::string CVT_D_L(uint64 instruction); + std::string CVT_D_S(uint64 instruction); + std::string CVT_D_W(uint64 instruction); + std::string CVT_L_D(uint64 instruction); + std::string CVT_L_S(uint64 instruction); + std::string CVT_S_D(uint64 instruction); + std::string CVT_S_L(uint64 instruction); + std::string CVT_S_PL(uint64 instruction); + std::string CVT_S_PU(uint64 instruction); + std::string CVT_S_W(uint64 instruction); + std::string CVT_W_D(uint64 instruction); + std::string CVT_W_S(uint64 instruction); + std::string DADDIU_48_(uint64 instruction); + std::string DADDIU_NEG_(uint64 instruction); + std::string DADDIU_U12_(uint64 instruction); + std::string DADD(uint64 instruction); + std::string DADDU(uint64 instruction); + std::string DCLO(uint64 instruction); + std::string DCLZ(uint64 instruction); + std::string DDIV(uint64 instruction); + std::string DDIVU(uint64 instruction); + std::string DERET(uint64 instruction); + std::string DEXTM(uint64 instruction); + std::string DEXT(uint64 instruction); + std::string DEXTU(uint64 instruction); + std::string DINSM(uint64 instruction); + std::string DINS(uint64 instruction); + std::string DINSU(uint64 instruction); + std::string DI(uint64 instruction); + std::string DIV(uint64 instruction); + std::string DIV_D(uint64 instruction); + std::string DIV_S(uint64 instruction); + std::string DIVU(uint64 instruction); + std::string DLSA(uint64 instruction); + std::string DLUI_48_(uint64 instruction); + std::string DMFC0(uint64 instruction); + std::string DMFC1(uint64 instruction); + std::string DMFC2(uint64 instruction); + std::string DMFGC0(uint64 instruction); + std::string DMOD(uint64 instruction); + std::string DMODU(uint64 instruction); + std::string DMTC0(uint64 instruction); + std::string DMTC1(uint64 instruction); + std::string DMTC2(uint64 instruction); + std::string DMTGC0(uint64 instruction); + std::string DMT(uint64 instruction); + std::string DMUH(uint64 instruction); + std::string DMUHU(uint64 instruction); + std::string DMUL(uint64 instruction); + std::string DMULU(uint64 instruction); + std::string DPAQ_S_W_PH(uint64 instruction); + std::string DPAQ_SA_L_W(uint64 instruction); + std::string DPAQX_S_W_PH(uint64 instruction); + std::string DPAQX_SA_W_PH(uint64 instruction); + std::string DPAU_H_QBL(uint64 instruction); + std::string DPAU_H_QBR(uint64 instruction); + std::string DPA_W_PH(uint64 instruction); + std::string DPAX_W_PH(uint64 instruction); + std::string DPS_W_PH(uint64 instruction); + std::string DPSQ_SA_L_W(uint64 instruction); + std::string DPSQ_S_W_PH(uint64 instruction); + std::string DPSQX_SA_W_PH(uint64 instruction); + std::string DPSQX_S_W_PH(uint64 instruction); + std::string DPSU_H_QBL(uint64 instruction); + std::string DPSU_H_QBR(uint64 instruction); + std::string DPSX_W_PH(uint64 instruction); + std::string DROTR(uint64 instruction); + std::string DROTR32(uint64 instruction); + std::string DROTRV(uint64 instruction); + std::string DROTX(uint64 instruction); + std::string DSLL(uint64 instruction); + std::string DSLL32(uint64 instruction); + std::string DSLLV(uint64 instruction); + std::string DSRA(uint64 instruction); + std::string DSRA32(uint64 instruction); + std::string DSRAV(uint64 instruction); + std::string DSRL32(uint64 instruction); + std::string DSRL(uint64 instruction); + std::string DSRLV(uint64 instruction); + std::string DSUB(uint64 instruction); + std::string DSUBU(uint64 instruction); + std::string DVP(uint64 instruction); + std::string DVPE(uint64 instruction); + std::string EHB(uint64 instruction); + std::string EI(uint64 instruction); + std::string EMT(uint64 instruction); + std::string ERET(uint64 instruction); + std::string ERETNC(uint64 instruction); + std::string EVP(uint64 instruction); + std::string EVPE(uint64 instruction); + std::string EXT(uint64 instruction); + std::string EXTD(uint64 instruction); + std::string EXTD32(uint64 instruction); + std::string EXTP(uint64 instruction); + std::string EXTPDP(uint64 instruction); + std::string EXTPDPV(uint64 instruction); + std::string EXTPV(uint64 instruction); + std::string EXTR_RS_W(uint64 instruction); + std::string EXTR_R_W(uint64 instruction); + std::string EXTR_S_H(uint64 instruction); + std::string EXTR_W(uint64 instruction); + std::string EXTRV_R_W(uint64 instruction); + std::string EXTRV_RS_W(uint64 instruction); + std::string EXTRV_S_H(uint64 instruction); + std::string EXTRV_W(uint64 instruction); + std::string EXTW(uint64 instruction); + std::string FLOOR_L_D(uint64 instruction); + std::string FLOOR_L_S(uint64 instruction); + std::string FLOOR_W_D(uint64 instruction); + std::string FLOOR_W_S(uint64 instruction); + std::string FORK(uint64 instruction); + std::string HYPCALL(uint64 instruction); + std::string HYPCALL_16_(uint64 instruction); + std::string INS(uint64 instruction); + std::string INSV(uint64 instruction); + std::string IRET(uint64 instruction); + std::string JALRC_16_(uint64 instruction); + std::string JALRC_32_(uint64 instruction); + std::string JALRC_HB(uint64 instruction); + std::string JRC(uint64 instruction); + std::string LB_16_(uint64 instruction); + std::string LB_GP_(uint64 instruction); + std::string LB_S9_(uint64 instruction); + std::string LB_U12_(uint64 instruction); + std::string LBE(uint64 instruction); + std::string LBU_16_(uint64 instruction); + std::string LBU_GP_(uint64 instruction); + std::string LBU_S9_(uint64 instruction); + std::string LBU_U12_(uint64 instruction); + std::string LBUE(uint64 instruction); + std::string LBUX(uint64 instruction); + std::string LBX(uint64 instruction); + std::string LD_GP_(uint64 instruction); + std::string LD_S9_(uint64 instruction); + std::string LD_U12_(uint64 instruction); + std::string LDC1_GP_(uint64 instruction); + std::string LDC1_S9_(uint64 instruction); + std::string LDC1_U12_(uint64 instruction); + std::string LDC1X(uint64 instruction); + std::string LDC1XS(uint64 instruction); + std::string LDC2(uint64 instruction); + std::string LDM(uint64 instruction); + std::string LDPC_48_(uint64 instruction); + std::string LDX(uint64 instruction); + std::string LDXS(uint64 instruction); + std::string LH_16_(uint64 instruction); + std::string LH_GP_(uint64 instruction); + std::string LH_S9_(uint64 instruction); + std::string LH_U12_(uint64 instruction); + std::string LHE(uint64 instruction); + std::string LHU_16_(uint64 instruction); + std::string LHU_GP_(uint64 instruction); + std::string LHU_S9_(uint64 instruction); + std::string LHU_U12_(uint64 instruction); + std::string LHUE(uint64 instruction); + std::string LHUX(uint64 instruction); + std::string LHUXS(uint64 instruction); + std::string LHX(uint64 instruction); + std::string LHXS(uint64 instruction); + std::string LI_16_(uint64 instruction); + std::string LI_48_(uint64 instruction); + std::string LL(uint64 instruction); + std::string LLD(uint64 instruction); + std::string LLDP(uint64 instruction); + std::string LLE(uint64 instruction); + std::string LLWP(uint64 instruction); + std::string LLWPE(uint64 instruction); + std::string LSA(uint64 instruction); + std::string LUI(uint64 instruction); + std::string LW_16_(uint64 instruction); + std::string LW_4X4_(uint64 instruction); + std::string LWC1_GP_(uint64 instruction); + std::string LWC1_S9_(uint64 instruction); + std::string LWC1_U12_(uint64 instruction); + std::string LWC1X(uint64 instruction); + std::string LWC1XS(uint64 instruction); + std::string LWC2(uint64 instruction); + std::string LWE(uint64 instruction); + std::string LW_GP_(uint64 instruction); + std::string LW_GP16_(uint64 instruction); + std::string LWM(uint64 instruction); + std::string LWPC_48_(uint64 instruction); + std::string LW_S9_(uint64 instruction); + std::string LW_SP_(uint64 instruction); + std::string LW_U12_(uint64 instruction); + std::string LWU_GP_(uint64 instruction); + std::string LWU_S9_(uint64 instruction); + std::string LWU_U12_(uint64 instruction); + std::string LWUX(uint64 instruction); + std::string LWUXS(uint64 instruction); + std::string LWX(uint64 instruction); + std::string LWXS_16_(uint64 instruction); + std::string LWXS_32_(uint64 instruction); + std::string MADD_DSP_(uint64 instruction); + std::string MADDF_D(uint64 instruction); + std::string MADDF_S(uint64 instruction); + std::string MADDU_DSP_(uint64 instruction); + std::string MAQ_S_W_PHL(uint64 instruction); + std::string MAQ_S_W_PHR(uint64 instruction); + std::string MAQ_SA_W_PHL(uint64 instruction); + std::string MAQ_SA_W_PHR(uint64 instruction); + std::string MAX_D(uint64 instruction); + std::string MAX_S(uint64 instruction); + std::string MAXA_D(uint64 instruction); + std::string MAXA_S(uint64 instruction); + std::string MFC0(uint64 instruction); + std::string MFC1(uint64 instruction); + std::string MFC2(uint64 instruction); + std::string MFGC0(uint64 instruction); + std::string MFHC0(uint64 instruction); + std::string MFHC1(uint64 instruction); + std::string MFHC2(uint64 instruction); + std::string MFHGC0(uint64 instruction); + std::string MFHI_DSP_(uint64 instruction); + std::string MFHTR(uint64 instruction); + std::string MFLO_DSP_(uint64 instruction); + std::string MFTR(uint64 instruction); + std::string MIN_D(uint64 instruction); + std::string MIN_S(uint64 instruction); + std::string MINA_D(uint64 instruction); + std::string MINA_S(uint64 instruction); + std::string MOD(uint64 instruction); + std::string MODSUB(uint64 instruction); + std::string MODU(uint64 instruction); + std::string MOV_D(uint64 instruction); + std::string MOV_S(uint64 instruction); + std::string MOVE_BALC(uint64 instruction); + std::string MOVEP(uint64 instruction); + std::string MOVEP_REV_(uint64 instruction); + std::string MOVE(uint64 instruction); + std::string MOVN(uint64 instruction); + std::string MOVZ(uint64 instruction); + std::string MSUB_DSP_(uint64 instruction); + std::string MSUBF_D(uint64 instruction); + std::string MSUBF_S(uint64 instruction); + std::string MSUBU_DSP_(uint64 instruction); + std::string MTC0(uint64 instruction); + std::string MTC1(uint64 instruction); + std::string MTC2(uint64 instruction); + std::string MTGC0(uint64 instruction); + std::string MTHC0(uint64 instruction); + std::string MTHC1(uint64 instruction); + std::string MTHC2(uint64 instruction); + std::string MTHGC0(uint64 instruction); + std::string MTHI_DSP_(uint64 instruction); + std::string MTHLIP(uint64 instruction); + std::string MTHTR(uint64 instruction); + std::string MTLO_DSP_(uint64 instruction); + std::string MTTR(uint64 instruction); + std::string MUH(uint64 instruction); + std::string MUHU(uint64 instruction); + std::string MUL_32_(uint64 instruction); + std::string MUL_4X4_(uint64 instruction); + std::string MUL_D(uint64 instruction); + std::string MUL_PH(uint64 instruction); + std::string MUL_S(uint64 instruction); + std::string MUL_S_PH(uint64 instruction); + std::string MULEQ_S_W_PHL(uint64 instruction); + std::string MULEQ_S_W_PHR(uint64 instruction); + std::string MULEU_S_PH_QBL(uint64 instruction); + std::string MULEU_S_PH_QBR(uint64 instruction); + std::string MULQ_RS_PH(uint64 instruction); + std::string MULQ_RS_W(uint64 instruction); + std::string MULQ_S_PH(uint64 instruction); + std::string MULQ_S_W(uint64 instruction); + std::string MULSA_W_PH(uint64 instruction); + std::string MULSAQ_S_W_PH(uint64 instruction); + std::string MULT_DSP_(uint64 instruction); + std::string MULTU_DSP_(uint64 instruction); + std::string MULU(uint64 instruction); + std::string NEG_D(uint64 instruction); + std::string NEG_S(uint64 instruction); + std::string NOP_16_(uint64 instruction); + std::string NOP_32_(uint64 instruction); + std::string NOR(uint64 instruction); + std::string NOT_16_(uint64 instruction); + std::string OR_16_(uint64 instruction); + std::string OR_32_(uint64 instruction); + std::string ORI(uint64 instruction); + std::string PACKRL_PH(uint64 instruction); + std::string PAUSE(uint64 instruction); + std::string PICK_PH(uint64 instruction); + std::string PICK_QB(uint64 instruction); + std::string PRECEQ_W_PHL(uint64 instruction); + std::string PRECEQ_W_PHR(uint64 instruction); + std::string PRECEQU_PH_QBL(uint64 instruction); + std::string PRECEQU_PH_QBLA(uint64 instruction); + std::string PRECEQU_PH_QBR(uint64 instruction); + std::string PRECEQU_PH_QBRA(uint64 instruction); + std::string PRECEU_PH_QBL(uint64 instruction); + std::string PRECEU_PH_QBLA(uint64 instruction); + std::string PRECEU_PH_QBR(uint64 instruction); + std::string PRECEU_PH_QBRA(uint64 instruction); + std::string PRECR_QB_PH(uint64 instruction); + std::string PRECR_SRA_PH_W(uint64 instruction); + std::string PRECR_SRA_R_PH_W(uint64 instruction); + std::string PRECRQ_PH_W(uint64 instruction); + std::string PRECRQ_QB_PH(uint64 instruction); + std::string PRECRQ_RS_PH_W(uint64 instruction); + std::string PRECRQU_S_QB_PH(uint64 instruction); + std::string PREF_S9_(uint64 instruction); + std::string PREF_U12_(uint64 instruction); + std::string PREFE(uint64 instruction); + std::string PREPEND(uint64 instruction); + std::string RADDU_W_QB(uint64 instruction); + std::string RDDSP(uint64 instruction); + std::string RDHWR(uint64 instruction); + std::string RDPGPR(uint64 instruction); + std::string RECIP_D(uint64 instruction); + std::string RECIP_S(uint64 instruction); + std::string REPL_PH(uint64 instruction); + std::string REPL_QB(uint64 instruction); + std::string REPLV_PH(uint64 instruction); + std::string REPLV_QB(uint64 instruction); + std::string RESTORE_32_(uint64 instruction); + std::string RESTORE_JRC_16_(uint64 instruction); + std::string RESTORE_JRC_32_(uint64 instruction); + std::string RESTOREF(uint64 instruction); + std::string RINT_D(uint64 instruction); + std::string RINT_S(uint64 instruction); + std::string ROTR(uint64 instruction); + std::string ROTRV(uint64 instruction); + std::string ROTX(uint64 instruction); + std::string ROUND_L_D(uint64 instruction); + std::string ROUND_L_S(uint64 instruction); + std::string ROUND_W_D(uint64 instruction); + std::string ROUND_W_S(uint64 instruction); + std::string RSQRT_D(uint64 instruction); + std::string RSQRT_S(uint64 instruction); + std::string SAVE_16_(uint64 instruction); + std::string SAVE_32_(uint64 instruction); + std::string SAVEF(uint64 instruction); + std::string SB_16_(uint64 instruction); + std::string SB_GP_(uint64 instruction); + std::string SB_S9_(uint64 instruction); + std::string SB_U12_(uint64 instruction); + std::string SBE(uint64 instruction); + std::string SBX(uint64 instruction); + std::string SC(uint64 instruction); + std::string SCD(uint64 instruction); + std::string SCDP(uint64 instruction); + std::string SCE(uint64 instruction); + std::string SCWP(uint64 instruction); + std::string SCWPE(uint64 instruction); + std::string SD_GP_(uint64 instruction); + std::string SD_S9_(uint64 instruction); + std::string SD_U12_(uint64 instruction); + std::string SDBBP_16_(uint64 instruction); + std::string SDBBP_32_(uint64 instruction); + std::string SDC1_GP_(uint64 instruction); + std::string SDC1_S9_(uint64 instruction); + std::string SDC1_U12_(uint64 instruction); + std::string SDC1X(uint64 instruction); + std::string SDC1XS(uint64 instruction); + std::string SDC2(uint64 instruction); + std::string SDM(uint64 instruction); + std::string SDPC_48_(uint64 instruction); + std::string SDX(uint64 instruction); + std::string SDXS(uint64 instruction); + std::string SEB(uint64 instruction); + std::string SEH(uint64 instruction); + std::string SEL_D(uint64 instruction); + std::string SEL_S(uint64 instruction); + std::string SELEQZ_D(uint64 instruction); + std::string SELEQZ_S(uint64 instruction); + std::string SELNEZ_D(uint64 instruction); + std::string SELNEZ_S(uint64 instruction); + std::string SEQI(uint64 instruction); + std::string SH_16_(uint64 instruction); + std::string SH_GP_(uint64 instruction); + std::string SH_S9_(uint64 instruction); + std::string SH_U12_(uint64 instruction); + std::string SHE(uint64 instruction); + std::string SHILO(uint64 instruction); + std::string SHILOV(uint64 instruction); + std::string SHLL_PH(uint64 instruction); + std::string SHLL_QB(uint64 instruction); + std::string SHLL_S_PH(uint64 instruction); + std::string SHLL_S_W(uint64 instruction); + std::string SHLLV_PH(uint64 instruction); + std::string SHLLV_QB(uint64 instruction); + std::string SHLLV_S_PH(uint64 instruction); + std::string SHLLV_S_W(uint64 instruction); + std::string SHRA_PH(uint64 instruction); + std::string SHRA_QB(uint64 instruction); + std::string SHRA_R_PH(uint64 instruction); + std::string SHRA_R_QB(uint64 instruction); + std::string SHRA_R_W(uint64 instruction); + std::string SHRAV_PH(uint64 instruction); + std::string SHRAV_QB(uint64 instruction); + std::string SHRAV_R_PH(uint64 instruction); + std::string SHRAV_R_QB(uint64 instruction); + std::string SHRAV_R_W(uint64 instruction); + std::string SHRL_PH(uint64 instruction); + std::string SHRL_QB(uint64 instruction); + std::string SHRLV_PH(uint64 instruction); + std::string SHRLV_QB(uint64 instruction); + std::string SHX(uint64 instruction); + std::string SHXS(uint64 instruction); + std::string SIGRIE(uint64 instruction); + std::string SLL_16_(uint64 instruction); + std::string SLL_32_(uint64 instruction); + std::string SLLV(uint64 instruction); + std::string SLT(uint64 instruction); + std::string SLTI(uint64 instruction); + std::string SLTIU(uint64 instruction); + std::string SLTU(uint64 instruction); + std::string SOV(uint64 instruction); + std::string SPECIAL2(uint64 instruction); + std::string SQRT_D(uint64 instruction); + std::string SQRT_S(uint64 instruction); + std::string SRA(uint64 instruction); + std::string SRAV(uint64 instruction); + std::string SRL_16_(uint64 instruction); + std::string SRL_32_(uint64 instruction); + std::string SRLV(uint64 instruction); + std::string SUB(uint64 instruction); + std::string SUB_D(uint64 instruction); + std::string SUB_S(uint64 instruction); + std::string SUBQ_PH(uint64 instruction); + std::string SUBQ_S_PH(uint64 instruction); + std::string SUBQ_S_W(uint64 instruction); + std::string SUBQH_PH(uint64 instruction); + std::string SUBQH_R_PH(uint64 instruction); + std::string SUBQH_R_W(uint64 instruction); + std::string SUBQH_W(uint64 instruction); + std::string SUBU_16_(uint64 instruction); + std::string SUBU_32_(uint64 instruction); + std::string SUBU_PH(uint64 instruction); + std::string SUBU_QB(uint64 instruction); + std::string SUBU_S_PH(uint64 instruction); + std::string SUBU_S_QB(uint64 instruction); + std::string SUBUH_QB(uint64 instruction); + std::string SUBUH_R_QB(uint64 instruction); + std::string SW_16_(uint64 instruction); + std::string SW_4X4_(uint64 instruction); + std::string SW_GP16_(uint64 instruction); + std::string SW_GP_(uint64 instruction); + std::string SW_S9_(uint64 instruction); + std::string SW_SP_(uint64 instruction); + std::string SW_U12_(uint64 instruction); + std::string SWC1_GP_(uint64 instruction); + std::string SWC1_S9_(uint64 instruction); + std::string SWC1_U12_(uint64 instruction); + std::string SWC1X(uint64 instruction); + std::string SWC1XS(uint64 instruction); + std::string SWC2(uint64 instruction); + std::string SWE(uint64 instruction); + std::string SWM(uint64 instruction); + std::string SWPC_48_(uint64 instruction); + std::string SWX(uint64 instruction); + std::string SWXS(uint64 instruction); + std::string SYNC(uint64 instruction); + std::string SYNCI(uint64 instruction); + std::string SYNCIE(uint64 instruction); + std::string SYSCALL_16_(uint64 instruction); + std::string SYSCALL_32_(uint64 instruction); + std::string TEQ(uint64 instruction); + std::string TLBGINV(uint64 instruction); + std::string TLBGINVF(uint64 instruction); + std::string TLBGP(uint64 instruction); + std::string TLBGR(uint64 instruction); + std::string TLBGWI(uint64 instruction); + std::string TLBGWR(uint64 instruction); + std::string TLBINV(uint64 instruction); + std::string TLBINVF(uint64 instruction); + std::string TLBP(uint64 instruction); + std::string TLBR(uint64 instruction); + std::string TLBWI(uint64 instruction); + std::string TLBWR(uint64 instruction); + std::string TNE(uint64 instruction); + std::string TRUNC_L_D(uint64 instruction); + std::string TRUNC_L_S(uint64 instruction); + std::string TRUNC_W_D(uint64 instruction); + std::string TRUNC_W_S(uint64 instruction); + std::string UALDM(uint64 instruction); + std::string UALH(uint64 instruction); + std::string UALWM(uint64 instruction); + std::string UASDM(uint64 instruction); + std::string UASH(uint64 instruction); + std::string UASWM(uint64 instruction); + std::string UDI(uint64 instruction); + std::string WAIT(uint64 instruction); + std::string WRDSP(uint64 instruction); + std::string WRPGPR(uint64 instruction); + std::string XOR_16_(uint64 instruction); + std::string XOR_32_(uint64 instruction); + std::string XORI(uint64 instruction); + std::string YIELD(uint64 instruction); + + static Pool P_SYSCALL[2]; + static Pool P_RI[4]; + static Pool P_ADDIU[2]; + static Pool P_TRAP[2]; + static Pool P_CMOVE[2]; + static Pool P_D_MT_VPE[2]; + static Pool P_E_MT_VPE[2]; + static Pool _P_MT_VPE[2]; + static Pool P_MT_VPE[8]; + static Pool P_DVP[2]; + static Pool P_SLTU[2]; + static Pool _POOL32A0[128]; + static Pool ADDQ__S__PH[2]; + static Pool MUL__S__PH[2]; + static Pool ADDQH__R__PH[2]; + static Pool ADDQH__R__W[2]; + static Pool ADDU__S__QB[2]; + static Pool ADDU__S__PH[2]; + static Pool ADDUH__R__QB[2]; + static Pool SHRAV__R__PH[2]; + static Pool SHRAV__R__QB[2]; + static Pool SUBQ__S__PH[2]; + static Pool SUBQH__R__PH[2]; + static Pool SUBQH__R__W[2]; + static Pool SUBU__S__QB[2]; + static Pool SUBU__S__PH[2]; + static Pool SHRA__R__PH[2]; + static Pool SUBUH__R__QB[2]; + static Pool SHLLV__S__PH[2]; + static Pool SHLL__S__PH[4]; + static Pool PRECR_SRA__R__PH_W[2]; + static Pool _POOL32A5[128]; + static Pool PP_LSX[16]; + static Pool PP_LSXS[16]; + static Pool P_LSX[2]; + static Pool POOL32Axf_1_0[4]; + static Pool POOL32Axf_1_1[4]; + static Pool POOL32Axf_1_3[4]; + static Pool POOL32Axf_1_4[2]; + static Pool MAQ_S_A__W_PHR[2]; + static Pool MAQ_S_A__W_PHL[2]; + static Pool POOL32Axf_1_5[2]; + static Pool POOL32Axf_1_7[4]; + static Pool POOL32Axf_1[8]; + static Pool POOL32Axf_2_DSP__0_7[8]; + static Pool POOL32Axf_2_DSP__8_15[8]; + static Pool POOL32Axf_2_DSP__16_23[8]; + static Pool POOL32Axf_2_DSP__24_31[8]; + static Pool POOL32Axf_2[4]; + static Pool POOL32Axf_4[128]; + static Pool POOL32Axf_5_group0[32]; + static Pool POOL32Axf_5_group1[32]; + static Pool ERETx[2]; + static Pool POOL32Axf_5_group3[32]; + static Pool POOL32Axf_5[4]; + static Pool SHRA__R__QB[2]; + static Pool POOL32Axf_7[8]; + static Pool POOL32Axf[8]; + static Pool _POOL32A7[8]; + static Pool P32A[8]; + static Pool P_GP_D[2]; + static Pool P_GP_W[4]; + static Pool POOL48I[32]; + static Pool PP_SR[4]; + static Pool P_SR_F[8]; + static Pool P_SR[2]; + static Pool P_SLL[5]; + static Pool P_SHIFT[16]; + static Pool P_ROTX[4]; + static Pool P_INS[4]; + static Pool P_EXT[4]; + static Pool P_U12[16]; + static Pool RINT_fmt[2]; + static Pool ADD_fmt0[2]; + static Pool SELEQZ_fmt[2]; + static Pool CLASS_fmt[2]; + static Pool SUB_fmt0[2]; + static Pool SELNEZ_fmt[2]; + static Pool MUL_fmt0[2]; + static Pool SEL_fmt[2]; + static Pool DIV_fmt0[2]; + static Pool ADD_fmt1[2]; + static Pool SUB_fmt1[2]; + static Pool MUL_fmt1[2]; + static Pool MADDF_fmt[2]; + static Pool DIV_fmt1[2]; + static Pool MSUBF_fmt[2]; + static Pool POOL32F_0[64]; + static Pool MIN_fmt[2]; + static Pool MAX_fmt[2]; + static Pool MINA_fmt[2]; + static Pool MAXA_fmt[2]; + static Pool CVT_L_fmt[2]; + static Pool RSQRT_fmt[2]; + static Pool FLOOR_L_fmt[2]; + static Pool CVT_W_fmt[2]; + static Pool SQRT_fmt[2]; + static Pool FLOOR_W_fmt[2]; + static Pool RECIP_fmt[2]; + static Pool CEIL_L_fmt[2]; + static Pool CEIL_W_fmt[2]; + static Pool TRUNC_L_fmt[2]; + static Pool TRUNC_W_fmt[2]; + static Pool ROUND_L_fmt[2]; + static Pool ROUND_W_fmt[2]; + static Pool POOL32Fxf_0[64]; + static Pool MOV_fmt[4]; + static Pool ABS_fmt[4]; + static Pool NEG_fmt[4]; + static Pool CVT_D_fmt[4]; + static Pool CVT_S_fmt[4]; + static Pool POOL32Fxf_1[32]; + static Pool POOL32Fxf[4]; + static Pool POOL32F_3[8]; + static Pool CMP_condn_S[32]; + static Pool CMP_condn_D[32]; + static Pool POOL32F_5[8]; + static Pool POOL32F[8]; + static Pool POOL32S_0[64]; + static Pool POOL32Sxf_4[128]; + static Pool POOL32Sxf[8]; + static Pool POOL32S_4[8]; + static Pool POOL32S[8]; + static Pool P_LUI[2]; + static Pool P_GP_LH[2]; + static Pool P_GP_SH[2]; + static Pool P_GP_CP1[4]; + static Pool P_GP_M64[4]; + static Pool P_GP_BH[8]; + static Pool P_LS_U12[16]; + static Pool P_PREF_S9_[2]; + static Pool P_LS_S0[16]; + static Pool ASET_ACLR[2]; + static Pool P_LL[4]; + static Pool P_SC[4]; + static Pool P_LLD[8]; + static Pool P_SCD[8]; + static Pool P_LS_S1[16]; + static Pool P_PREFE[2]; + static Pool P_LLE[4]; + static Pool P_SCE[4]; + static Pool P_LS_E0[16]; + static Pool P_LS_WM[2]; + static Pool P_LS_UAWM[2]; + static Pool P_LS_DM[2]; + static Pool P_LS_UADM[2]; + static Pool P_LS_S9[8]; + static Pool P_BAL[2]; + static Pool P_BALRSC[2]; + static Pool P_J[16]; + static Pool P_BR3A[32]; + static Pool P_BR1[4]; + static Pool P_BR2[4]; + static Pool P_BRI[8]; + static Pool P32[32]; + static Pool P16_SYSCALL[2]; + static Pool P16_RI[4]; + static Pool P16_MV[2]; + static Pool P16_SHIFT[2]; + static Pool POOL16C_00[4]; + static Pool POOL16C_0[2]; + static Pool P16C[2]; + static Pool P16_A1[2]; + static Pool P_ADDIU_RS5_[2]; + static Pool P16_A2[2]; + static Pool P16_ADDU[2]; + static Pool P16_JRC[2]; + static Pool P16_BR1[2]; + static Pool P16_BR[2]; + static Pool P16_SR[2]; + static Pool P16_4X4[4]; + static Pool P16_LB[4]; + static Pool P16_LH[4]; + static Pool P16[32]; + static Pool MAJOR[2]; + +}; + +#endif diff --git a/disas/ppc.c b/disas/ppc.c index 5ab9c35a84..da1140ba2b 100644 --- a/disas/ppc.c +++ b/disas/ppc.c @@ -3734,6 +3734,8 @@ const struct powerpc_opcode powerpc_opcodes[] = { { "addmeo.", XO(31,234,1,1), XORB_MASK, PPCCOM, { RT, RA } }, { "ameo.", XO(31,234,1,1), XORB_MASK, PWRCOM, { RT, RA } }, +{ "addex", XO(31,170,0,0), XO_MASK, POWER9, { RT, RA, RB } }, + { "mullw", XO(31,235,0,0), XO_MASK, PPCCOM, { RT, RA, RB } }, { "muls", XO(31,235,0,0), XO_MASK, PWRCOM, { RT, RA, RB } }, { "mullw.", XO(31,235,0,1), XO_MASK, PPCCOM, { RT, RA, RB } }, diff --git a/disas/riscv.c b/disas/riscv.c index 7fd1019623..27546dd790 100644 --- a/disas/riscv.c +++ b/disas/riscv.c @@ -87,33 +87,10 @@ typedef enum { typedef enum { rvc_end, - rvc_simm_6, - rvc_imm_6, - rvc_imm_7, - rvc_imm_8, - rvc_imm_9, - rvc_imm_10, - rvc_imm_12, - rvc_imm_18, - rvc_imm_nz, - rvc_imm_x2, - rvc_imm_x4, - rvc_imm_x8, - rvc_imm_x16, - rvc_rd_b3, - rvc_rs1_b3, - rvc_rs2_b3, - rvc_rd_eq_rs1, rvc_rd_eq_ra, - rvc_rd_eq_sp, rvc_rd_eq_x0, - rvc_rs1_eq_sp, rvc_rs1_eq_x0, rvc_rs2_eq_x0, - rvc_rd_ne_x0_x2, - rvc_rd_ne_x0, - rvc_rs1_ne_x0, - rvc_rs2_ne_x0, rvc_rs2_eq_rs1, rvc_rs1_eq_ra, rvc_imm_eq_zero, @@ -2522,111 +2499,16 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) uint8_t rd = dec->rd, rs1 = dec->rs1, rs2 = dec->rs2; while (*c != rvc_end) { switch (*c) { - case rvc_simm_6: - if (!(imm >= -32 && imm < 32)) { - return false; - } - break; - case rvc_imm_6: - if (!(imm <= 63)) { - return false; - } - break; - case rvc_imm_7: - if (!(imm <= 127)) { - return false; - } - break; - case rvc_imm_8: - if (!(imm <= 255)) { - return false; - } - break; - case rvc_imm_9: - if (!(imm <= 511)) { - return false; - } - break; - case rvc_imm_10: - if (!(imm <= 1023)) { - return false; - } - break; - case rvc_imm_12: - if (!(imm <= 4095)) { - return false; - } - break; - case rvc_imm_18: - if (!(imm <= 262143)) { - return false; - } - break; - case rvc_imm_nz: - if (!(imm != 0)) { - return false; - } - break; - case rvc_imm_x2: - if (!((imm & 0b1) == 0)) { - return false; - } - break; - case rvc_imm_x4: - if (!((imm & 0b11) == 0)) { - return false; - } - break; - case rvc_imm_x8: - if (!((imm & 0b111) == 0)) { - return false; - } - break; - case rvc_imm_x16: - if (!((imm & 0b1111) == 0)) { - return false; - } - break; - case rvc_rd_b3: - if (!(rd >= 8 && rd <= 15)) { - return false; - } - break; - case rvc_rs1_b3: - if (!(rs1 >= 8 && rs1 <= 15)) { - return false; - } - break; - case rvc_rs2_b3: - if (!(rs2 >= 8 && rs2 <= 15)) { - return false; - } - break; - case rvc_rd_eq_rs1: - if (!(rd == rs1)) { - return false; - } - break; case rvc_rd_eq_ra: if (!(rd == 1)) { return false; } break; - case rvc_rd_eq_sp: - if (!(rd == 2)) { - return false; - } - break; case rvc_rd_eq_x0: if (!(rd == 0)) { return false; } break; - case rvc_rs1_eq_sp: - if (!(rs1 == 2)) { - return false; - } - break; case rvc_rs1_eq_x0: if (!(rs1 == 0)) { return false; @@ -2637,26 +2519,6 @@ static bool check_constraints(rv_decode *dec, const rvc_constraint *c) return false; } break; - case rvc_rd_ne_x0_x2: - if (!(rd != 0 && rd != 2)) { - return false; - } - break; - case rvc_rd_ne_x0: - if (!(rd != 0)) { - return false; - } - break; - case rvc_rs1_ne_x0: - if (!(rs1 != 0)) { - return false; - } - break; - case rvc_rs2_ne_x0: - if (!(rs2 != 0)) { - return false; - } - break; case rvc_rs2_eq_rs1: if (!(rs2 == rs1)) { return false; diff --git a/docs/COLO-FT.txt b/docs/COLO-FT.txt index d7c7dcda8f..ad24680d13 100644 --- a/docs/COLO-FT.txt +++ b/docs/COLO-FT.txt @@ -102,14 +102,48 @@ to make sure the state of VM in Secondary side is always consistent with VM in Primary side. COLO Proxy: -Delivers packets to Primary and Seconday, and then compare the responses from +Delivers packets to Primary and Secondary, and then compare the responses from both side. Then decide whether to start a checkpoint according to some rules. -Please refer to docs/colo-proxy.txt for more informations. +Please refer to docs/colo-proxy.txt for more information. Note: HeartBeat has not been implemented yet, so you need to trigger failover process by using 'x-colo-lost-heartbeat' command. +== COLO operation status == + ++-----------------+ +| | +| Start COLO | +| | ++--------+--------+ + | + | Main qmp command: + | migrate-set-capabilities with x-colo + | migrate + | + v ++--------+--------+ +| | +| COLO running | +| | ++--------+--------+ + | + | Main qmp command: + | x-colo-lost-heartbeat + | or + | some error happened + v ++--------+--------+ +| | send qmp event: +| COLO failover | COLO_EXIT +| | ++-----------------+ + +COLO use the qmp command to switch and report operation status. +The diagram just shows the main qmp command, you can get the detail +in test procedure. + == Test procedure == 1. Startup qemu Primary: @@ -139,7 +173,7 @@ Secondary: { 'execute': 'nbd-server-start', 'arguments': {'addr': {'type': 'inet', 'data': {'host': 'xx.xx.xx.xx', 'port': '8889'} } } } -{'execute': 'nbd-server-add', 'arguments': {'device': 'secondeary-disk0', 'writable': true } } +{'execute': 'nbd-server-add', 'arguments': {'device': 'secondary-disk0', 'writable': true } } Note: a. The qmp command nbd-server-start and nbd-server-add must be run diff --git a/docs/amd-memory-encryption.txt b/docs/amd-memory-encryption.txt index f483795eaa..43bf3ee6a5 100644 --- a/docs/amd-memory-encryption.txt +++ b/docs/amd-memory-encryption.txt @@ -97,7 +97,7 @@ References AMD Memory Encryption whitepaper: http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf -Secure Encrypted Virutualization Key Management: +Secure Encrypted Virtualization Key Management: [1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf KVM Forum slides: diff --git a/docs/can.txt b/docs/can.txt index 7ba23b259a..9fa6ed51c8 100644 --- a/docs/can.txt +++ b/docs/can.txt @@ -99,7 +99,7 @@ Links to other resources https://gitlab.fel.cvut.cz/canbus/qemu-canbus (3) RTEMS page describing project https://devel.rtems.org/wiki/Developer/Simulators/QEMU/CANEmulation - (4) RTLWS 2015 article about the projevt and its use with CANopen emulation + (4) RTLWS 2015 article about the project and its use with CANopen emulation http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can.pdf Slides http://rtime.felk.cvut.cz/publications/public/rtlws2015-qemu-can-slides.pdf diff --git a/docs/colo-proxy.txt b/docs/colo-proxy.txt index 1f8e4b4e77..fa1cef0278 100644 --- a/docs/colo-proxy.txt +++ b/docs/colo-proxy.txt @@ -41,7 +41,7 @@ Below is a COLO proxy ascii figure: | | +------------------------------------------------------+ | | | | |netfilter| | | | | | netfilter | | | | +----------+ +----------------------------+ | | | +-----------------------------------------------------------+ | -| | | | | | out | | | | | | filter excute order | | +| | | | | | out | | | | | | filter execute order | | | | | | +-----------------------------+ | | | | | | +-------------------> | | | | | | | | | | | | | | | | TCP | | | | +-----+--+-+ +-----v----+ +-----v----+ |pri +----+----+sec| | | | +------------+ +---+----+---v+rewriter++ +------------+ | | @@ -53,7 +53,7 @@ Below is a COLO proxy ascii figure: | | | tx | rx rx | | | | | tx all | rx | | | | | | | | | | +-----------------------------------------------------------+ | | | | +--------------+ | | | | | | -| | | filter excute order | | | | | | | +| | | filter execute order | | | | | | | | | | +----------------> | | | +--------------------------------------------------------+ | | +-----------------------------------------+ | | | | | | | | | @@ -92,7 +92,7 @@ but do nothing, just pass to next filter. Redirect Server Filter --> COLO-Compare COLO-compare receive primary guest packet then -waiting scondary redirect packet to compare it. +waiting secondary redirect packet to compare it. If packet same,send queued primary packet and clear queued secondary packet, Otherwise send primary packet and do checkpoint. diff --git a/docs/conf.py b/docs/conf.py new file mode 100644 index 0000000000..befbcc6c3e --- /dev/null +++ b/docs/conf.py @@ -0,0 +1,216 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file, created by +# sphinx-quickstart on Thu Jan 31 16:40:14 2019. +# +# This config file can be used in one of two ways: +# (1) as a common config file which is included by the conf.py +# for each of QEMU's manuals: in this case sphinx-build is run multiple +# times, once per subdirectory. +# (2) as a top level conf file which will result in building all +# the manuals into a single document: in this case sphinx-build is +# run once, on the top-level docs directory. +# +# QEMU's makefiles take option (1), which allows us to install +# only the ones the user cares about (in particular we don't want +# to ship the 'devel' manual to end-users). +# Third-party sites such as readthedocs.org will take option (2). +# +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import os +import sys + +# The per-manual conf.py will set qemu_docdir for a single-manual build; +# otherwise set it here if this is an entire-manual-set build. +# This is always the absolute path of the docs/ directory in the source tree. +try: + qemu_docdir +except NameError: + qemu_docdir = os.path.abspath(".") + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use an absolute path starting from qemu_docdir. +# +# sys.path.insert(0, os.path.join(qemu_docdir, "my_subdir")) + + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# 1.3 is where the 'alabaster' theme was shipped with Sphinx. +needs_sphinx = '1.3' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'QEMU' +copyright = u'2019, The QEMU Project Developers' +author = u'The QEMU Project Developers' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. + +# Extract this information from the VERSION file, for the benefit of +# standalone Sphinx runs as used by readthedocs.org. Builds run from +# the Makefile will pass version and release on the sphinx-build +# command line, which override this. +try: + extracted_version = None + with open(os.path.join(qemu_docdir, '../VERSION')) as f: + extracted_version = f.readline().strip() +except: + pass +finally: + if extracted_version: + version = release = extracted_version + else: + version = release = "unknown version" + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = False + +# Sphinx defaults to warning about use of :option: for options not defined +# with "option::" in the document being processed. Turn that off. +suppress_warnings = ["ref.option"] + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = 'alabaster' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# We initialize this to empty here, so the per-manual conf.py can just +# add individual key/value entries. +html_theme_options = { +} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# QEMU doesn't yet have any static files, so comment this out so we don't +# get a warning about a missing directory. +# If we do ever add this then it would probably be better to call the +# subdirectory sphinx_static, as the Linux kernel does. +# html_static_path = ['_static'] + +# Custom sidebar templates, must be a dictionary that maps document names +# to template names. +# +# This is required for the alabaster theme +# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars +html_sidebars = { + '**': [ + 'about.html', + 'navigation.html', + 'searchbox.html', + ] +} + +# Don't copy the rST source files to the HTML output directory, +# and don't put links to the sources into the output HTML. +html_copy_source = False + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'QEMUdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'QEMU.tex', u'QEMU Documentation', + u'The QEMU Project Developers', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'qemu', u'QEMU Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'QEMU', u'QEMU Documentation', + author, 'QEMU', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/docs/cpu-hotplug.rst b/docs/cpu-hotplug.rst new file mode 100644 index 0000000000..d0b06403f1 --- /dev/null +++ b/docs/cpu-hotplug.rst @@ -0,0 +1,142 @@ +=================== +Virtual CPU hotplug +=================== + +A complete example of vCPU hotplug (and hot-unplug) using QMP +``device_add`` and ``device_del``. + +vCPU hotplug +------------ + +(1) Launch QEMU as follows (note that the "maxcpus" is mandatory to + allow vCPU hotplug):: + + $ qemu-system-x86_64 -display none -no-user-config -m 2048 \ + -nodefaults -monitor stdio -machine pc,accel=kvm,usb=off \ + -smp 1,maxcpus=2 -cpu IvyBridge-IBRS \ + -qmp unix:/tmp/qmp-sock,server,nowait + +(2) Run 'qmp-shell' (located in the source tree, under: "scripts/qmp/) + to connect to the just-launched QEMU:: + + $> ./qmp-shell -p -v /tmp/qmp-sock + [...] + (QEMU) + +(3) Find out which CPU types could be plugged, and into which sockets:: + + (QEMU) query-hotpluggable-cpus + { + "execute": "query-hotpluggable-cpus", + "arguments": {} + } + { + "return": [ + { + "type": "IvyBridge-IBRS-x86_64-cpu", + "vcpus-count": 1, + "props": { + "socket-id": 1, + "core-id": 0, + "thread-id": 0 + } + }, + { + "qom-path": "/machine/unattached/device[0]", + "type": "IvyBridge-IBRS-x86_64-cpu", + "vcpus-count": 1, + "props": { + "socket-id": 0, + "core-id": 0, + "thread-id": 0 + } + } + ] + } + (QEMU) + +(4) The ``query-hotpluggable-cpus`` command returns an object for CPUs + that are present (containing a "qom-path" member) or which may be + hot-plugged (no "qom-path" member). From its output in step (3), we + can see that ``IvyBridge-IBRS-x86_64-cpu`` is present in socket 0, + while hot-plugging a CPU into socket 1 requires passing the listed + properties to QMP ``device_add``:: + + (QEMU) device_add id=cpu-2 driver=IvyBridge-IBRS-x86_64-cpu socket-id=1 core-id=0 thread-id=0 + { + "execute": "device_add", + "arguments": { + "socket-id": 1, + "driver": "IvyBridge-IBRS-x86_64-cpu", + "id": "cpu-2", + "core-id": 0, + "thread-id": 0 + } + } + { + "return": {} + } + (QEMU) + +(5) Optionally, run QMP `query-cpus-fast` for some details about the + vCPUs:: + + (QEMU) query-cpus-fast + { + "execute": "query-cpus-fast", + "arguments": {} + } + { + "return": [ + { + "qom-path": "/machine/unattached/device[0]", + "target": "x86_64", + "thread-id": 11534, + "cpu-index": 0, + "props": { + "socket-id": 0, + "core-id": 0, + "thread-id": 0 + }, + "arch": "x86" + }, + { + "qom-path": "/machine/peripheral/cpu-2", + "target": "x86_64", + "thread-id": 12106, + "cpu-index": 1, + "props": { + "socket-id": 1, + "core-id": 0, + "thread-id": 0 + }, + "arch": "x86" + } + ] + } + (QEMU) + +vCPU hot-unplug +--------------- + +From the 'qmp-shell', invoke the QMP ``device_del`` command:: + + (QEMU) device_del id=cpu-2 + { + "execute": "device_del", + "arguments": { + "id": "cpu-2" + } + } + { + "return": {} + } + (QEMU) + +.. note:: + vCPU hot-unplug requires guest cooperation; so the ``device_del`` + command above does not guarantee vCPU removal -- it's a "request to + unplug". At this point, the guest will get a System Control + Interrupt (SCI) and calls the ACPI handler for the affected vCPU + device. Then the guest kernel will bring the vCPU offline and tell + QEMU to unplug it. diff --git a/docs/devel/build-system.txt b/docs/devel/build-system.txt index 52501f2ad9..addd274eeb 100644 --- a/docs/devel/build-system.txt +++ b/docs/devel/build-system.txt @@ -393,7 +393,7 @@ all use $(obj) as a prefix to the target, e.g. This file provides the entry point used to build each individual system or userspace emulator target. Each enabled target has its own subdirectory. For example if configure is run with the argument -'--target-list=x86_64-softmmu', then a sub-directory 'x86_64-softmu' +'--target-list=x86_64-softmmu', then a sub-directory 'x86_64-softmmu' will be created, containing a 'Makefile' which symlinks back to Makefile.target @@ -417,7 +417,6 @@ into each QEMU system and userspace emulator targets. They merely contain a long list of config variable definitions. For example, default-configs/x86_64-softmmu.mak has: - include pci.mak include sound.mak include usb.mak CONFIG_QXL=$(CONFIG_SPICE) diff --git a/docs/devel/conf.py b/docs/devel/conf.py new file mode 100644 index 0000000000..7441f87e7f --- /dev/null +++ b/docs/devel/conf.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'devel' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = u'Developer''s Guide' diff --git a/docs/devel/decodetree.rst b/docs/devel/decodetree.rst new file mode 100644 index 0000000000..44ac621ea8 --- /dev/null +++ b/docs/devel/decodetree.rst @@ -0,0 +1,221 @@ +======================== +Decodetree Specification +======================== + +A *decodetree* is built from instruction *patterns*. A pattern may +represent a single architectural instruction or a group of same, depending +on what is convenient for further processing. + +Each pattern has both *fixedbits* and *fixedmask*, the combination of which +describes the condition under which the pattern is matched:: + + (insn & fixedmask) == fixedbits + +Each pattern may have *fields*, which are extracted from the insn and +passed along to the translator. Examples of such are registers, +immediates, and sub-opcodes. + +In support of patterns, one may declare *fields*, *argument sets*, and +*formats*, each of which may be re-used to simplify further definitions. + +Fields +====== + +Syntax:: + + field_def := '%' identifier ( unnamed_field )+ ( !function=identifier )? + unnamed_field := number ':' ( 's' ) number + +For *unnamed_field*, the first number is the least-significant bit position +of the field and the second number is the length of the field. If the 's' is +present, the field is considered signed. If multiple ``unnamed_fields`` are +present, they are concatenated. In this way one can define disjoint fields. + +If ``!function`` is specified, the concatenated result is passed through the +named function, taking and returning an integral value. + +FIXME: the fields of the structure into which this result will be stored +is restricted to ``int``. Which means that we cannot expand 64-bit items. + +Field examples: + ++---------------------------+---------------------------------------------+ +| Input | Generated code | ++===========================+=============================================+ +| %disp 0:s16 | sextract(i, 0, 16) | ++---------------------------+---------------------------------------------+ +| %imm9 16:6 10:3 | extract(i, 16, 6) << 3 | extract(i, 10, 3) | ++---------------------------+---------------------------------------------+ +| %disp12 0:s1 1:1 2:10 | sextract(i, 0, 1) << 11 | | +| | extract(i, 1, 1) << 10 | | +| | extract(i, 2, 10) | ++---------------------------+---------------------------------------------+ +| %shimm8 5:s8 13:1 | expand_shimm8(sextract(i, 5, 8) << 1 | | +| !function=expand_shimm8 | extract(i, 13, 1)) | ++---------------------------+---------------------------------------------+ + +Argument Sets +============= + +Syntax:: + + args_def := '&' identifier ( args_elt )+ ( !extern )? + args_elt := identifier + +Each *args_elt* defines an argument within the argument set. +Each argument set will be rendered as a C structure "arg_$name" +with each of the fields being one of the member arguments. + +If ``!extern`` is specified, the backing structure is assumed +to have been already declared, typically via a second decoder. + +Argument sets are useful when one wants to define helper functions +for the translator functions that can perform operations on a common +set of arguments. This can ensure, for instance, that the ``AND`` +pattern and the ``OR`` pattern put their operands into the same named +structure, so that a common ``gen_logic_insn`` may be able to handle +the operations common between the two. + +Argument set examples:: + + ®3 ra rb rc + &loadstore reg base offset + + +Formats +======= + +Syntax:: + + fmt_def := '@' identifier ( fmt_elt )+ + fmt_elt := fixedbit_elt | field_elt | field_ref | args_ref + fixedbit_elt := [01.-]+ + field_elt := identifier ':' 's'? number + field_ref := '%' identifier | identifier '=' '%' identifier + args_ref := '&' identifier + +Defining a format is a handy way to avoid replicating groups of fields +across many instruction patterns. + +A *fixedbit_elt* describes a contiguous sequence of bits that must +be 1, 0, or don't care. The difference between '.' and '-' +is that '.' means that the bit will be covered with a field or a +final 0 or 1 from the pattern, and '-' means that the bit is really +ignored by the cpu and will not be specified. + +A *field_elt* describes a simple field only given a width; the position of +the field is implied by its position with respect to other *fixedbit_elt* +and *field_elt*. + +If any *fixedbit_elt* or *field_elt* appear, then all bits must be defined. +Padding with a *fixedbit_elt* of all '.' is an easy way to accomplish that. + +A *field_ref* incorporates a field by reference. This is the only way to +add a complex field to a format. A field may be renamed in the process +via assignment to another identifier. This is intended to allow the +same argument set be used with disjoint named fields. + +A single *args_ref* may specify an argument set to use for the format. +The set of fields in the format must be a subset of the arguments in +the argument set. If an argument set is not specified, one will be +inferred from the set of fields. + +It is recommended, but not required, that all *field_ref* and *args_ref* +appear at the end of the line, not interleaving with *fixedbit_elf* or +*field_elt*. + +Format examples:: + + @opr ...... ra:5 rb:5 ... 0 ....... rc:5 + @opi ...... ra:5 lit:8 1 ....... rc:5 + +Patterns +======== + +Syntax:: + + pat_def := identifier ( pat_elt )+ + pat_elt := fixedbit_elt | field_elt | field_ref | args_ref | fmt_ref | const_elt + fmt_ref := '@' identifier + const_elt := identifier '=' number + +The *fixedbit_elt* and *field_elt* specifiers are unchanged from formats. +A pattern that does not specify a named format will have one inferred +from a referenced argument set (if present) and the set of fields. + +A *const_elt* allows a argument to be set to a constant value. This may +come in handy when fields overlap between patterns and one has to +include the values in the *fixedbit_elt* instead. + +The decoder will call a translator function for each pattern matched. + +Pattern examples:: + + addl_r 010000 ..... ..... .... 0000000 ..... @opr + addl_i 010000 ..... ..... .... 0000000 ..... @opi + +which will, in part, invoke:: + + trans_addl_r(ctx, &arg_opr, insn) + +and:: + + trans_addl_i(ctx, &arg_opi, insn) + +Pattern Groups +============== + +Syntax:: + + group := '{' ( pat_def | group )+ '}' + +A *group* begins with a lone open-brace, with all subsequent lines +indented two spaces, and ending with a lone close-brace. Groups +may be nested, increasing the required indentation of the lines +within the nested group to two spaces per nesting level. + +Unlike ungrouped patterns, grouped patterns are allowed to overlap. +Conflicts are resolved by selecting the patterns in order. If all +of the fixedbits for a pattern match, its translate function will +be called. If the translate function returns false, then subsequent +patterns within the group will be matched. + +The following example from PA-RISC shows specialization of the *or* +instruction:: + + { + { + nop 000010 ----- ----- 0000 001001 0 00000 + copy 000010 00000 r1:5 0000 001001 0 rt:5 + } + or 000010 rt2:5 r1:5 cf:4 001001 0 rt:5 + } + +When the *cf* field is zero, the instruction has no side effects, +and may be specialized. When the *rt* field is zero, the output +is discarded and so the instruction has no effect. When the *rt2* +field is zero, the operation is ``reg[rt] | 0`` and so encodes +the canonical register copy operation. + +The output from the generator might look like:: + + switch (insn & 0xfc000fe0) { + case 0x08000240: + /* 000010.. ........ ....0010 010..... */ + if ((insn & 0x0000f000) == 0x00000000) { + /* 000010.. ........ 00000010 010..... */ + if ((insn & 0x0000001f) == 0x00000000) { + /* 000010.. ........ 00000010 01000000 */ + extract_decode_Fmt_0(&u.f_decode0, insn); + if (trans_nop(ctx, &u.f_decode0)) return true; + } + if ((insn & 0x03e00000) == 0x00000000) { + /* 00001000 000..... 00000010 010..... */ + extract_decode_Fmt_1(&u.f_decode1, insn); + if (trans_copy(ctx, &u.f_decode1)) return true; + } + } + extract_decode_Fmt_2(&u.f_decode2, insn); + if (trans_or(ctx, &u.f_decode2)) return true; + return false; + } diff --git a/docs/devel/index.rst b/docs/devel/index.rst new file mode 100644 index 0000000000..ebbab636ce --- /dev/null +++ b/docs/devel/index.rst @@ -0,0 +1,22 @@ +.. This is the top level page for the 'devel' manual. + + +QEMU Developer's Guide +====================== + +This manual documents various parts of the internals of QEMU. +You only need to read it if you are interested in reading or +modifying QEMU's source code. + +Contents: + +.. toctree:: + :maxdepth: 2 + + kconfig + loads-stores + memory + migration + stable-process + testing + decodetree diff --git a/docs/devel/kconfig.rst b/docs/devel/kconfig.rst new file mode 100644 index 0000000000..cce146f87d --- /dev/null +++ b/docs/devel/kconfig.rst @@ -0,0 +1,306 @@ +================ +QEMU and Kconfig +================ + +QEMU is a very versatile emulator; it can be built for a variety of +targets, where each target can emulate various boards and at the same +time different targets can share large amounts of code. For example, +a POWER and an x86 board can run the same code to emulate a PCI network +card, even though the boards use different PCI host bridges, and they +can run the same code to emulate a SCSI disk while using different +SCSI adapters. ARM, s390 and x86 boards can all present a virtio-blk +disk to their guests, but with three different virtio guest interfaces. + +Each QEMU target enables a subset of the boards, devices and buses that +are included in QEMU's source code. As a result, each QEMU executable +only links a small subset of the files that form QEMU's source code; +anything that is not needed to support a particular target is culled. + +QEMU uses a simple domain-specific language to describe the dependencies +between components. This is useful for two reasons: + +* new targets and boards can be added without knowing in detail the + architecture of the hardware emulation subsystems. Boards only have + to list the components they need, and the compiled executable will + include all the required dependencies and all the devices that the + user can add to that board; + +* users can easily build reduced versions of QEMU that support only a subset + of boards or devices. For example, by default most targets will include + all emulated PCI devices that QEMU supports, but the build process is + configurable and it is easy to drop unnecessary (or otherwise unwanted) + code to make a leaner binary. + +This domain-specific language is based on the Kconfig language that +originated in the Linux kernel, though it was heavily simplified and +the handling of dependencies is stricter in QEMU. + +Unlike Linux, there is no user interface to edit the configuration, which +is instead specified in per-target files under the ``default-configs/`` +directory of the QEMU source tree. This is because, unlike Linux, +configuration and dependencies can be treated as a black box when building +QEMU; the default configuration that QEMU ships with should be okay in +almost all cases. + +The Kconfig language +-------------------- + +Kconfig defines configurable components in files named ``hw/*/Kconfig``. +Note that configurable components are _not_ visible in C code as preprocessor +symbols; they are only visible in the Makefile. Each configurable component +defines a Makefile variable whose name starts with ``CONFIG_``. + +All elements have boolean (true/false) type; truth is written as ``y``, while +falsehood is written ``n``. They are defined in a Kconfig +stanza like the following:: + + config ARM_VIRT + bool + imply PCI_DEVICES + imply VFIO_AMD_XGBE + imply VFIO_XGMAC + select A15MPCORE + select ACPI + select ARM_SMMUV3 + +The ``config`` keyword introduces a new configuration element. In the example +above, Makefiles will have access to a variable named ``CONFIG_ARM_VIRT``, +with value ``y`` or ``n`` (respectively for boolean true and false). + +Boolean expressions can be used within the language, whenever ```` +is written in the remainder of this section. The ``&&``, ``||`` and +``!`` operators respectively denote conjunction (AND), disjunction (OR) +and negation (NOT). + +The ``bool`` data type declaration is optional, but it is suggested to +include it for clarity and future-proofing. After ``bool`` the following +directives can be included: + +**dependencies**: ``depends on `` + + This defines a dependency for this configurable element. Dependencies + evaluate an expression and force the value of the variable to false + if the expression is false. + +**reverse dependencies**: ``select [if ]`` + + While ``depends on`` can force a symbol to false, reverse dependencies can + be used to force another symbol to true. In the following example, + ``CONFIG_BAZ`` will be true whenever ``CONFIG_FOO`` is true:: + + config FOO + select BAZ + + The optional expression will prevent ``select`` from having any effect + unless it is true. + + Note that unlike Linux's Kconfig implementation, QEMU will detect + contradictions between ``depends on`` and ``select`` statements and prevent + you from building such a configuration. + +**default value**: ``default [if ]`` + + Default values are assigned to the config symbol if no other value was + set by the user via ``default-configs/*.mak`` files, and only if + ``select`` or ``depends on`` directives do not force the value to true + or false respectively. ```` can be ``y`` or ``n``; it cannot + be an arbitrary Boolean expression. However, a condition for applying + the default value can be added with ``if``. + + A configuration element can have any number of default values (usually, + if more than one default is present, they will have different + conditions). If multiple default values satisfy their condition, + only the first defined one is active. + +**reverse default** (weak reverse dependency): ``imply [if ]`` + + This is similar to ``select`` as it applies a lower limit of ``y`` + to another symbol. However, the lower limit is only a default + and the "implied" symbol's value may still be set to ``n`` from a + ``default-configs/*.mak`` files. The following two examples are + equivalent:: + + config FOO + bool + imply BAZ + + config BAZ + bool + default y if FOO + + The next section explains where to use ``imply`` or ``default y``. + +Guidelines for writing Kconfig files +------------------------------------ + +Configurable elements in QEMU fall under five broad groups. Each group +declares its dependencies in different ways: + +**subsystems**, of which **buses** are a special case + + Example:: + + config SCSI + bool + + Subsystems always default to false (they have no ``default`` directive) + and are never visible in ``default-configs/*.mak`` files. It's + up to other symbols to ``select`` whatever subsystems they require. + + They sometimes have ``select`` directives to bring in other required + subsystems or buses. For example, ``AUX`` (the DisplayPort auxiliary + channel "bus") selects ``I2C`` because it can act as an I2C master too. + +**devices** + + Example:: + + config MEGASAS_SCSI_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SCSI + + Devices are the most complex of the five. They can have a variety + of directives that cooperate so that a default configuration includes + all the devices that can be accessed from QEMU. + + Devices *depend on* the bus that they lie on, for example a PCI + device would specify ``depends on PCI``. An MMIO device will likely + have no ``depends on`` directive. Devices also *select* the buses + that the device provides, for example a SCSI adapter would specify + ``select SCSI``. Finally, devices are usually ``default y`` if and + only if they have at least one ``depends on``; the default could be + conditional on a device group. + + Devices also select any optional subsystem that they use; for example + a video card might specify ``select EDID`` if it needs to build EDID + information and publish it to the guest. + +**device groups** + + Example:: + + config PCI_DEVICES + bool + + Device groups provide a convenient mechanism to enable/disable many + devices in one go. This is useful when a set of devices is likely to + be enabled/disabled by several targets. Device groups usually need + no directive and are not used in the Makefile either; they only appear + as conditions for ``default y`` directives. + + QEMU currently has two device groups, ``PCI_DEVICES`` and + ``TEST_DEVICES``. PCI devices usually have a ``default y if + PCI_DEVICES`` directive rather than just ``default y``. This lets + some boards (notably s390) easily support a subset of PCI devices, + for example only VFIO (passthrough) and virtio-pci devices. + ``TEST_DEVICES`` instead is used for devices that are rarely used on + production virtual machines, but provide useful hooks to test QEMU + or KVM. + +**boards** + + Example:: + + config SUN4M + bool + imply TCX + imply CG3 + select CS4231 + select ECCMEMCTL + select EMPTY_SLOT + select ESCC + select ESP + select FDC + select SLAVIO + select LANCE + select M48T59 + select STP2000 + + Boards specify their constituent devices using ``imply`` and ``select`` + directives. A device should be listed under ``select`` if the board + cannot be started at all without it. It should be listed under + ``imply`` if (depending on the QEMU command line) the board may or + may not be started without it. Boards also default to false; they are + enabled by the ``default-configs/*.mak`` for the target they apply to. + +**internal elements** + + Example:: + + config ECCMEMCTL + bool + select ECC + + Internal elements group code that is useful in several boards or + devices. They are usually enabled with ``select`` and in turn select + other elements; they are never visible in ``default-configs/*.mak`` + files, and often not even in the Makefile. + +Writing and modifying default configurations +-------------------------------------------- + +In addition to the Kconfig files under hw/, each target also includes +a file called ``default-configs/TARGETNAME-softmmu.mak``. These files +initialize some Kconfig variables to non-default values and provide the +starting point to turn on devices and subsystems. + +A file in ``default-configs/`` looks like the following example:: + + # Default configuration for alpha-softmmu + + # Uncomment the following lines to disable these optional devices: + # + #CONFIG_PCI_DEVICES=n + #CONFIG_TEST_DEVICES=n + + # Boards: + # + CONFIG_DP264=y + +The first part, consisting of commented-out ``=n`` assignments, tells +the user which devices or device groups are implied by the boards. +The second part, consisting of ``=y`` assignments, tells the user which +boards are supported by the target. The user will typically modify +the default configuration by uncommenting lines in the first group, +or commenting out lines in the second group. + +It is also possible to run QEMU's configure script with the +``--with-default-devices`` option. When this is done, everything defaults +to ``n`` unless it is ``select``ed or explicitly switched on in the +``.mak`` files. In other words, ``default`` and ``imply`` directives +are disabled. When QEMU is built with this option, the user will probably +want to change some lines in the first group, for example like this:: + + CONFIG_PCI_DEVICES=y + #CONFIG_TEST_DEVICES=n + +and/or pick a subset of the devices in those device groups. Right now +there is no single place that lists all the optional devices for +``CONFIG_PCI_DEVICES`` and ``CONFIG_TEST_DEVICES``. In the future, +we expect that ``.mak`` files will be automatically generated, so that +they will include all these symbols and some help text on what they do. + +``Kconfig.host`` +---------------- + +In some special cases, a configurable element depends on host features +that are detected by QEMU's configure script; for example some devices +depend on the availability of KVM or on the presence of a library on +the host. + +These symbols should be listed in ``Kconfig.host`` like this:: + + config KVM + bool + +and also listed as follows in the top-level Makefile's ``MINIKCONF_ARGS`` +variable:: + + MINIKCONF_ARGS = \ + $@ $*-config.devices.mak.d $< $(MINIKCONF_INPUTS) \ + CONFIG_KVM=$(CONFIG_KVM) \ + CONFIG_SPICE=$(CONFIG_SPICE) \ + CONFIG_TPM=$(CONFIG_TPM) \ + ... diff --git a/docs/devel/loads-stores.rst b/docs/devel/loads-stores.rst index 57d8c524bf..c74cd090e6 100644 --- a/docs/devel/loads-stores.rst +++ b/docs/devel/loads-stores.rst @@ -253,6 +253,22 @@ Regexes for git grep - ``\`` - ``\`` +``address_space_write_rom`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This function performs a write by physical address like +``address_space_write``, except that if the write is to a ROM then +the ROM contents will be modified, even though a write by the guest +CPU to the ROM would be ignored. This is used for non-guest writes +like writes from the gdb debug stub or initial loading of ROM contents. + +Note that portions of the write which attempt to write data to a +device will be silently ignored -- only real RAM and ROM will +be written to. + +Regexes for git grep + - ``address_space_write_rom`` + ``{ld,st}*_phys`` ~~~~~~~~~~~~~~~~~ @@ -315,25 +331,6 @@ For new code they are better avoided: Regexes for git grep - ``\`` -``cpu_physical_memory_write_rom`` -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This function performs a write by physical address like -``address_space_write``, except that if the write is to a ROM then -the ROM contents will be modified, even though a write by the guest -CPU to the ROM would be ignored. - -Note that unlike ``cpu_physical_memory_write()`` this function takes -an AddressSpace argument, but unlike ``address_space_write()`` this -function does not take a ``MemTxAttrs`` or return a ``MemTxResult``. - -**TODO**: we should probably clean up this inconsistency and -turn the function into ``address_space_write_rom`` with an API -matching ``address_space_write``. - -``cpu_physical_memory_write_rom`` - - ``cpu_memory_rw_debug`` ~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/devel/memory.txt b/docs/devel/memory.rst similarity index 82% rename from docs/devel/memory.txt rename to docs/devel/memory.rst index c1dee1252c..b6a4c37ea5 100644 --- a/docs/devel/memory.txt +++ b/docs/devel/memory.rst @@ -1,19 +1,20 @@ +============== The memory API ============== The memory API models the memory and I/O buses and controllers of a QEMU machine. It attempts to allow modelling of: - - ordinary RAM - - memory-mapped I/O (MMIO) - - memory controllers that can dynamically reroute physical memory regions - to different destinations +- ordinary RAM +- memory-mapped I/O (MMIO) +- memory controllers that can dynamically reroute physical memory regions + to different destinations The memory model provides support for - - tracking RAM changes by the guest - - setting up coalesced memory for kvm - - setting up ioeventfd regions for kvm +- tracking RAM changes by the guest +- setting up coalesced memory for kvm +- setting up ioeventfd regions for kvm Memory is modelled as an acyclic graph of MemoryRegion objects. Sinks (leaves) are RAM and MMIO regions, while other nodes represent @@ -98,25 +99,30 @@ ROM device memory region types), this host memory needs to be copied to the destination on migration. These APIs which allocate the host memory for you will also register the memory so it is migrated: - - memory_region_init_ram() - - memory_region_init_rom() - - memory_region_init_rom_device() + +- memory_region_init_ram() +- memory_region_init_rom() +- memory_region_init_rom_device() For most devices and boards this is the correct thing. If you have a special case where you need to manage the migration of the backing memory yourself, you can call the functions: - - memory_region_init_ram_nomigrate() - - memory_region_init_rom_nomigrate() - - memory_region_init_rom_device_nomigrate() + +- memory_region_init_ram_nomigrate() +- memory_region_init_rom_nomigrate() +- memory_region_init_rom_device_nomigrate() + which only initialize the MemoryRegion and leave handling migration to the caller. The functions: - - memory_region_init_resizeable_ram() - - memory_region_init_ram_from_file() - - memory_region_init_ram_from_fd() - - memory_region_init_ram_ptr() - - memory_region_init_ram_device_ptr() + +- memory_region_init_resizeable_ram() +- memory_region_init_ram_from_file() +- memory_region_init_ram_from_fd() +- memory_region_init_ram_ptr() +- memory_region_init_ram_device_ptr() + are for special cases only, and so they do not automatically register the backing memory for migration; the caller must manage migration if necessary. @@ -218,7 +224,7 @@ For example, suppose we have a container A of size 0x8000 with two subregions B and C. B is a container mapped at 0x2000, size 0x4000, priority 2; C is an MMIO region mapped at 0x0, size 0x6000, priority 1. B currently has two of its own subregions: D of size 0x1000 at offset 0 and E of size 0x1000 at -offset 0x2000. As a diagram: +offset 0x2000. As a diagram:: 0 1000 2000 3000 4000 5000 6000 7000 8000 |------|------|------|------|------|------|------|------| @@ -228,8 +234,9 @@ offset 0x2000. As a diagram: D: [DDDDD] E: [EEEEE] -The regions that will be seen within this address range then are: - [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] +The regions that will be seen within this address range then are:: + + [CCCCCCCCCCCC][DDDDD][CCCCC][EEEEE][CCCCC] Since B has higher priority than C, its subregions appear in the flat map even where they overlap with C. In ranges where B has not mapped anything @@ -237,8 +244,9 @@ C's region appears. If B had provided its own MMIO operations (ie it was not a pure container) then these would be used for any addresses in its range not handled by -D or E, and the result would be: - [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB] +D or E, and the result would be:: + + [CCCCCCCCCCCC][DDDDD][BBBBB][EEEEE][BBBBB] Priority values are local to a container, because the priorities of two regions are only compared when they are both children of the same container. @@ -257,6 +265,7 @@ guest accesses an address: - all direct subregions of the root region are matched against the address, in descending priority order + - if the address lies outside the region offset/size, the subregion is discarded - if the subregion is a leaf (RAM or MMIO), the search terminates, returning @@ -270,36 +279,39 @@ guest accesses an address: address range), then if this is a container with its own MMIO or RAM backing the search terminates, returning the container itself. Otherwise we continue with the next subregion in priority order + - if none of the subregions match the address then the search terminates with no match found Example memory map ------------------ -system_memory: container@0-2^48-1 - | - +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff) - | - +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff) - | - +---- vga-window: alias@0xa0000-0xbffff ---> #pci (0xa0000-0xbffff) - | (prio 1) - | - +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) +:: -pci (0-2^32-1) - | - +--- vga-area: container@0xa0000-0xbffff - | | - | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff) - | | - | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff) - | - +---- vram: ram@0xe1000000-0xe1ffffff - | - +---- vga-mmio: mmio@0xe2000000-0xe200ffff + system_memory: container@0-2^48-1 + | + +---- lomem: alias@0-0xdfffffff ---> #ram (0-0xdfffffff) + | + +---- himem: alias@0x100000000-0x11fffffff ---> #ram (0xe0000000-0xffffffff) + | + +---- vga-window: alias@0xa0000-0xbffff ---> #pci (0xa0000-0xbffff) + | (prio 1) + | + +---- pci-hole: alias@0xe0000000-0xffffffff ---> #pci (0xe0000000-0xffffffff) -ram: ram@0x00000000-0xffffffff + pci (0-2^32-1) + | + +--- vga-area: container@0xa0000-0xbffff + | | + | +--- alias@0x00000-0x7fff ---> #vram (0x010000-0x017fff) + | | + | +--- alias@0x08000-0xffff ---> #vram (0x020000-0x027fff) + | + +---- vram: ram@0xe1000000-0xe1ffffff + | + +---- vga-mmio: mmio@0xe2000000-0xe200ffff + + ram: ram@0x00000000-0xffffffff This is a (simplified) PC memory map. The 4GB RAM block is mapped into the system address space via two aliases: "lomem" is a 1:1 mapping of the first @@ -326,21 +338,26 @@ visible as the pci-hole alias clips it to a 0.5GB range. MMIO Operations --------------- -MMIO regions are provided with ->read() and ->write() callbacks; in addition -various constraints can be supplied to control how these callbacks are called: +MMIO regions are provided with ->read() and ->write() callbacks, +which are sufficient for most devices. Some devices change behaviour +based on the attributes used for the memory transaction, or need +to be able to respond that the access should provoke a bus error +rather than completing successfully; those devices can use the +->read_with_attrs() and ->write_with_attrs() callbacks instead. - - .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.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 - emulated using four 1-byte writes, if .impl.max_access_size = 1. - - .impl.unaligned specifies that the *implementation* supports unaligned - accesses; if false, unaligned accesses will be emulated by two aligned - accesses. - - .old_mmio eases the porting of code that was formerly using - cpu_register_io_memory(). It should not be used in new code. +In addition 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.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 + emulated using four 1-byte writes, if .impl.max_access_size = 1. +- .impl.unaligned specifies that the *implementation* supports unaligned + accesses; if false, unaligned accesses will be emulated by two aligned + accesses. diff --git a/docs/devel/migration.rst b/docs/devel/migration.rst index 6ed3fce061..220059679a 100644 --- a/docs/devel/migration.rst +++ b/docs/devel/migration.rst @@ -240,10 +240,13 @@ should succeed even with the data missing. To support this the subsection can be connected to a device property and from there to a versioned machine type. -One important note is that the post_load() function is called "after" -loading all subsections, because a newer subsection could change same -value that it uses. A flag, and the combination of pre_load and post_load -can be used to detect whether a subsection was loaded, and to +The 'pre_load' and 'post_load' functions on subsections are only +called if the subsection is loaded. + +One important note is that the outer post_load() function is called "after" +loading all subsections, because a newer subsection could change the same +value that it uses. A flag, and the combination of outer pre_load and +post_load can be used to detect whether a subsection was loaded, and to fall back on default behaviour when the subsection isn't present. Example: @@ -315,8 +318,8 @@ For example: the property to false. c) Add a static bool support_foo function that tests the property. d) Add a subsection with a .needed set to the support_foo function - e) (potentially) Add a pre_load that sets up a default value for 'foo' - to be used if the subsection isn't loaded. + e) (potentially) Add an outer pre_load that sets up a default value + for 'foo' to be used if the subsection isn't loaded. Now that subsection will not be generated when using an older machine type and the migration stream will be accepted by older @@ -416,8 +419,13 @@ The functions to do that are inside a vmstate definition, and are called: This function is called before we save the state of one device. -Example: You can look at hpet.c, that uses the three function to -massage the state that is transferred. +- ``int (*post_save)(void *opaque);`` + + This function is called after we save the state of one device + (even upon failure, unless the call to pre_save returned an error). + +Example: You can look at hpet.c, that uses the first three functions +to massage the state that is transferred. The ``VMSTATE_WITH_TMP`` macro may be useful when the migration data doesn't match the stored device data well; it allows an @@ -432,6 +440,7 @@ Examples of such memory API functions are: - memory_region_add_subregion() - memory_region_del_subregion() - memory_region_set_readonly() + - memory_region_set_nonvolatile() - memory_region_set_enabled() - memory_region_set_address() - memory_region_set_alias_offset() diff --git a/docs/devel/qapi-code-gen.txt b/docs/devel/qapi-code-gen.txt index c2e11465f0..b517b0cfbf 100644 --- a/docs/devel/qapi-code-gen.txt +++ b/docs/devel/qapi-code-gen.txt @@ -26,7 +26,7 @@ how the schemas, scripts, and resulting code are used. == QMP/Guest agent schema == A QAPI schema file is designed to be loosely based on JSON -(http://www.ietf.org/rfc/rfc7159.txt) with changes for quoting style +(http://www.ietf.org/rfc/rfc8259.txt) with changes for quoting style and the use of comments; a QAPI schema file is then parsed by a python code generation program. A valid QAPI schema consists of a series of top-level expressions, with no commas between them. Where @@ -752,6 +752,25 @@ gets its generated code guarded like this: #endif /* defined(HAVE_BAR) */ #endif /* defined(CONFIG_FOO) */ +Where a member can be defined with a single string value for its type, +it is also possible to supply a dictionary instead with both 'type' +and 'if' keys. + +Example: a conditional 'bar' member + +{ 'struct': 'IfStruct', 'data': + { 'foo': 'int', + 'bar': { 'type': 'int', 'if': 'defined(IFCOND)'} } } + +An enum value can be replaced by a dictionary with a 'name' and a 'if' +key. + +Example: a conditional 'bar' enum member. + +{ 'enum': 'IfEnum', 'data': + [ 'foo', + { 'name' : 'bar', 'if': 'defined(IFCOND)' } ] } + Please note that you are responsible to ensure that the C code will compile with an arbitrary combination of conditions, since the generators are unable to check it at this point. @@ -1035,7 +1054,7 @@ Example: #ifndef EXAMPLE_QAPI_TYPES_H #define EXAMPLE_QAPI_TYPES_H -[Built-in types omitted...] + #include "qapi/qapi-builtin-types.h" typedef struct UserDefOne UserDefOne; @@ -1062,7 +1081,7 @@ Example: UserDefOneList *arg1; }; - #endif + #endif /* EXAMPLE_QAPI_TYPES_H */ $ cat qapi-generated/example-qapi-types.c [Uninteresting stuff omitted...] @@ -1092,6 +1111,21 @@ Example: visit_free(v); } +[Uninteresting stuff omitted...] + +For a modular QAPI schema (see section Include directives), code for +each sub-module SUBDIR/SUBMODULE.json is actually generated into + +SUBDIR/$(prefix)qapi-types-SUBMODULE.h +SUBDIR/$(prefix)qapi-types-SUBMODULE.c + +If qapi-gen.py is run with option --builtins, additional files are +created: + +qapi-builtin-types.h - C types corresponding to built-in types + +qapi-builtin-types.c - Cleanup functions for the above C types + === Code generated for visiting QAPI types === These are the visitor functions used to walk through and convert @@ -1118,7 +1152,9 @@ Example: #ifndef EXAMPLE_QAPI_VISIT_H #define EXAMPLE_QAPI_VISIT_H -[Visitors for built-in types omitted...] + #include "qapi/qapi-builtin-visit.h" + #include "example-qapi-types.h" + void visit_type_UserDefOne_members(Visitor *v, UserDefOne *obj, Error **errp); void visit_type_UserDefOne(Visitor *v, const char *name, UserDefOne **obj, Error **errp); @@ -1126,7 +1162,7 @@ Example: void visit_type_q_obj_my_command_arg_members(Visitor *v, q_obj_my_command_arg *obj, Error **errp); - #endif + #endif /* EXAMPLE_QAPI_VISIT_H */ $ cat qapi-generated/example-qapi-visit.c [Uninteresting stuff omitted...] @@ -1219,6 +1255,21 @@ Example: error_propagate(errp, err); } +[Uninteresting stuff omitted...] + +For a modular QAPI schema (see section Include directives), code for +each sub-module SUBDIR/SUBMODULE.json is actually generated into + +SUBDIR/$(prefix)qapi-visit-SUBMODULE.h +SUBDIR/$(prefix)qapi-visit-SUBMODULE.c + +If qapi-gen.py is run with option --builtins, additional files are +created: + +qapi-builtin-visit.h - Visitor functions for built-in types + +qapi-builtin-visit.c - Declarations for these visitor functions + === Code generated for commands === These are the marshaling/dispatch functions for the commands defined @@ -1238,18 +1289,17 @@ Example: $ cat qapi-generated/example-qapi-commands.h [Uninteresting stuff omitted...] - #ifndef EXAMPLE_QMP_COMMANDS_H - #define EXAMPLE_QMP_COMMANDS_H + #ifndef EXAMPLE_QAPI_COMMANDS_H + #define EXAMPLE_QAPI_COMMANDS_H #include "example-qapi-types.h" - #include "qapi/qmp/qdict.h" #include "qapi/qmp/dispatch.h" - void example_qmp_init_marshal(QmpCommandList *cmds); UserDefOne *qmp_my_command(UserDefOneList *arg1, Error **errp); void qmp_marshal_my_command(QDict *args, QObject **ret, Error **errp); + void example_qmp_init_marshal(QmpCommandList *cmds); - #endif + #endif /* EXAMPLE_QAPI_COMMANDS_H */ $ cat qapi-generated/example-qapi-commands.c [Uninteresting stuff omitted...] @@ -1316,6 +1366,14 @@ Example: qmp_marshal_my_command, QCO_NO_OPTIONS); } +[Uninteresting stuff omitted...] + +For a modular QAPI schema (see section Include directives), code for +each sub-module SUBDIR/SUBMODULE.json is actually generated into + +SUBDIR/$(prefix)qapi-commands-SUBMODULE.h +SUBDIR/$(prefix)qapi-commands-SUBMODULE.c + === Code generated for events === This is the code related to events defined in the schema, providing @@ -1323,58 +1381,68 @@ qapi_event_send_EVENT(). The following files are created: -$(prefix)qapi-events.h - Function prototypes for each event type, plus an - enumeration of all event names +$(prefix)qapi-events.h - Function prototypes for each event type $(prefix)qapi-events.c - Implementation of functions to send an event +$(prefix)qapi-emit-events.h - Enumeration of all event names, and + common event code declarations + +$(prefix)qapi-emit-events.c - Common event code definitions + Example: $ cat qapi-generated/example-qapi-events.h [Uninteresting stuff omitted...] - #ifndef EXAMPLE_QAPI_EVENT_H - #define EXAMPLE_QAPI_EVENT_H + #ifndef EXAMPLE_QAPI_EVENTS_H + #define EXAMPLE_QAPI_EVENTS_H - #include "qapi/qmp/qdict.h" + #include "qapi/util.h" #include "example-qapi-types.h" + void qapi_event_send_my_event(void); - 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; - - #define example_QAPIEvent_str(val) \ - qapi_enum_lookup(example_QAPIEvent_lookup, (val)) - - extern const char *const example_QAPIEvent_lookup[]; - - #endif + #endif /* EXAMPLE_QAPI_EVENTS_H */ $ cat qapi-generated/example-qapi-events.c [Uninteresting stuff omitted...] - void qapi_event_send_my_event(Error **errp) + void qapi_event_send_my_event(void) { QDict *qmp; - Error *err = NULL; - QMPEventFuncEmit emit; - - emit = qmp_event_get_func_emit(); - if (!emit) { - return; - } qmp = qmp_event_build_dict("MY_EVENT"); - emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp, &err); + example_qapi_event_emit(EXAMPLE_QAPI_EVENT_MY_EVENT, qmp); - error_propagate(errp, err); qobject_unref(qmp); } +[Uninteresting stuff omitted...] + $ cat qapi-generated/example-qapi-emit-events.h +[Uninteresting stuff omitted...] + + #ifndef EXAMPLE_QAPI_EMIT_EVENTS_H + #define EXAMPLE_QAPI_EMIT_EVENTS_H + + #include "qapi/util.h" + + typedef enum example_QAPIEvent { + EXAMPLE_QAPI_EVENT_MY_EVENT, + EXAMPLE_QAPI_EVENT__MAX, + } example_QAPIEvent; + + #define example_QAPIEvent_str(val) \ + qapi_enum_lookup(&example_QAPIEvent_lookup, (val)) + + extern const QEnumLookup example_QAPIEvent_lookup; + + void example_qapi_event_emit(example_QAPIEvent event, QDict *qdict); + + #endif /* EXAMPLE_QAPI_EMIT_EVENTS_H */ + $ cat qapi-generated/example-qapi-emit-events.c +[Uninteresting stuff omitted...] + const QEnumLookup example_QAPIEvent_lookup = { .array = (const char *const[]) { [EXAMPLE_QAPI_EVENT_MY_EVENT] = "MY_EVENT", @@ -1382,6 +1450,14 @@ Example: .size = EXAMPLE_QAPI_EVENT__MAX }; +[Uninteresting stuff omitted...] + +For a modular QAPI schema (see section Include directives), code for +each sub-module SUBDIR/SUBMODULE.json is actually generated into + +SUBDIR/$(prefix)qapi-events-SUBMODULE.h +SUBDIR/$(prefix)qapi-events-SUBMODULE.c + === Code generated for introspection === The following files are created: @@ -1396,30 +1472,93 @@ Example: $ cat qapi-generated/example-qapi-introspect.h [Uninteresting stuff omitted...] - #ifndef EXAMPLE_QMP_INTROSPECT_H - #define EXAMPLE_QMP_INTROSPECT_H + #ifndef EXAMPLE_QAPI_INTROSPECT_H + #define EXAMPLE_QAPI_INTROSPECT_H - extern const QLitObject qmp_schema_qlit; + #include "qapi/qmp/qlit.h" - #endif + extern const QLitObject example_qmp_schema_qlit; + + #endif /* EXAMPLE_QAPI_INTROSPECT_H */ $ cat qapi-generated/example-qapi-introspect.c [Uninteresting stuff omitted...] const QLitObject example_qmp_schema_qlit = QLIT_QLIST(((QLitObject[]) { QLIT_QDICT(((QLitDictEntry[]) { - { "arg-type", QLIT_QSTR("0") }, - { "meta-type", QLIT_QSTR("event") }, - { "name", QLIT_QSTR("Event") }, - { } + { "arg-type", QLIT_QSTR("0"), }, + { "meta-type", QLIT_QSTR("command"), }, + { "name", QLIT_QSTR("my-command"), }, + { "ret-type", QLIT_QSTR("1"), }, + {} })), QLIT_QDICT(((QLitDictEntry[]) { - { "members", QLIT_QLIST(((QLitObject[]) { - { } - })) }, - { "meta-type", QLIT_QSTR("object") }, - { "name", QLIT_QSTR("0") }, - { } + { "arg-type", QLIT_QSTR("2"), }, + { "meta-type", QLIT_QSTR("event"), }, + { "name", QLIT_QSTR("MY_EVENT"), }, + {} })), - ... - { } + /* "0" = q_obj_my-command-arg */ + QLIT_QDICT(((QLitDictEntry[]) { + { "members", QLIT_QLIST(((QLitObject[]) { + QLIT_QDICT(((QLitDictEntry[]) { + { "name", QLIT_QSTR("arg1"), }, + { "type", QLIT_QSTR("[1]"), }, + {} + })), + {} + })), }, + { "meta-type", QLIT_QSTR("object"), }, + { "name", QLIT_QSTR("0"), }, + {} + })), + /* "1" = UserDefOne */ + QLIT_QDICT(((QLitDictEntry[]) { + { "members", QLIT_QLIST(((QLitObject[]) { + QLIT_QDICT(((QLitDictEntry[]) { + { "name", QLIT_QSTR("integer"), }, + { "type", QLIT_QSTR("int"), }, + {} + })), + QLIT_QDICT(((QLitDictEntry[]) { + { "default", QLIT_QNULL, }, + { "name", QLIT_QSTR("string"), }, + { "type", QLIT_QSTR("str"), }, + {} + })), + {} + })), }, + { "meta-type", QLIT_QSTR("object"), }, + { "name", QLIT_QSTR("1"), }, + {} + })), + /* "2" = q_empty */ + QLIT_QDICT(((QLitDictEntry[]) { + { "members", QLIT_QLIST(((QLitObject[]) { + {} + })), }, + { "meta-type", QLIT_QSTR("object"), }, + { "name", QLIT_QSTR("2"), }, + {} + })), + QLIT_QDICT(((QLitDictEntry[]) { + { "element-type", QLIT_QSTR("1"), }, + { "meta-type", QLIT_QSTR("array"), }, + { "name", QLIT_QSTR("[1]"), }, + {} + })), + QLIT_QDICT(((QLitDictEntry[]) { + { "json-type", QLIT_QSTR("int"), }, + { "meta-type", QLIT_QSTR("builtin"), }, + { "name", QLIT_QSTR("int"), }, + {} + })), + QLIT_QDICT(((QLitDictEntry[]) { + { "json-type", QLIT_QSTR("string"), }, + { "meta-type", QLIT_QSTR("builtin"), }, + { "name", QLIT_QSTR("str"), }, + {} + })), + {} })); + +[Uninteresting stuff omitted...] diff --git a/docs/devel/testing.rst b/docs/devel/testing.rst index 8e1fa3a66e..60f897d915 100644 --- a/docs/devel/testing.rst +++ b/docs/devel/testing.rst @@ -43,15 +43,13 @@ add a new unit test: 3. Add the test to ``tests/Makefile.include``. First, name the unit test program and add it to ``$(check-unit-y)``; then add a rule to build the - executable. Optionally, you can add a magical variable to support ``gcov``. - For example: + executable. For example: .. code:: check-unit-y += tests/foo-test$(EXESUF) tests/foo-test$(EXESUF): tests/foo-test.o $(test-util-obj-y) ... - gcov-files-foo-test-y = util/foo.c Since unit tests don't require environment variables, the simplest way to debug a unit test failure is often directly invoking it or even running it under @@ -61,6 +59,7 @@ variable (which affects memory reclamation and catches invalid pointers better) and gtester options. If necessary, you can run .. code:: + make check-unit V=1 and copy the actual command line which executes the unit test, then run @@ -118,6 +117,7 @@ and using gdb on the test is still simple to do: find out the actual command from the output of .. code:: + make check-qtest V=1 which you can run manually. @@ -301,7 +301,7 @@ An alternative method to set up permissions is by adding the current user to .. code:: $ sudo groupadd docker - $ sudo usermod $USER -G docker + $ sudo usermod $USER -a -G docker $ sudo chown :docker /var/run/docker.sock Note that any one of above configurations makes it possible for the user to @@ -434,6 +434,7 @@ Debugging Add ``DEBUG=1`` and/or ``V=1`` to the make command to allow interactive debugging and verbose output. If this is not enough, see the next section. +``V=1`` will be propagated down into the make jobs in the guest. Manual invocation ----------------- @@ -544,10 +545,39 @@ Tests based on ``avocado_qemu.Test`` can easily: - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html -Installation ------------- +Running tests +------------- -To install Avocado and its dependencies, run: +You can run the acceptance tests simply by executing: + +.. code:: + + make check-acceptance + +This involves the automatic creation of Python virtual environment +within the build tree (at ``tests/venv``) which will have all the +right dependencies, and will save tests results also within the +build tree (at ``tests/results``). + +Note: the build environment must be using a Python 3 stack, and have +the ``venv`` and ``pip`` packages installed. If necessary, make sure +``configure`` is called with ``--python=`` and that those modules are +available. On Debian and Ubuntu based systems, depending on the +specific version, they may be on packages named ``python3-venv`` and +``python3-pip``. + +The scripts installed inside the virtual environment may be used +without an "activation". For instance, the Avocado test runner +may be invoked by running: + + .. code:: + + tests/venv/bin/avocado run $OPTION1 $OPTION2 tests/acceptance/ + +Manual Installation +------------------- + +To manually install Avocado and its dependencies, run: .. code:: @@ -570,7 +600,6 @@ the ``avocado_qemu.Test`` class. Here's a simple usage example: class Version(Test): """ - :avocado: enable :avocado: tags=quick """ def test_qmp_human_info_version(self): @@ -604,7 +633,46 @@ instance, available at ``self.vm``. Because many tests will tweak the QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) is left to the test writer. -At test "tear down", ``avocado_qemu.Test`` handles the QEMUMachine +The base test class has also support for tests with more than one +QEMUMachine. The way to get machines is through the ``self.get_vm()`` +method which will return a QEMUMachine instance. The ``self.get_vm()`` +method accepts arguments that will be passed to the QEMUMachine creation +and also an optional `name` attribute so you can identify a specific +machine and get it more than once through the tests methods. A simple +and hypothetical example follows: + +.. code:: + + from avocado_qemu import Test + + + class MultipleMachines(Test): + """ + :avocado: enable + """ + def test_multiple_machines(self): + first_machine = self.get_vm() + second_machine = self.get_vm() + self.get_vm(name='third_machine').launch() + + first_machine.launch() + second_machine.launch() + + first_res = first_machine.command( + 'human-monitor-command', + command_line='info version') + + second_res = second_machine.command( + 'human-monitor-command', + command_line='info version') + + third_res = self.get_vm(name='third_machine').command( + 'human-monitor-command', + command_line='info version') + + self.assertEquals(first_res, second_res, third_res) + +At test "tear down", ``avocado_qemu.Test`` handles all the QEMUMachines shutdown. QEMUMachine @@ -688,11 +756,15 @@ The exact QEMU binary to be used on QEMUMachine. Uninstalling Avocado -------------------- -If you've followed the installation instructions above, you can easily -uninstall Avocado. Start by listing the packages you have installed:: +If you've followed the manual installation instructions above, you can +easily uninstall Avocado. Start by listing the packages you have +installed:: pip list --user And remove any package you want with:: pip uninstall + +If you've used ``make check-acceptance``, the Python virtual environment where +Avocado is installed will be cleaned up as part of ``make check-clean``. diff --git a/docs/devel/tracing.txt b/docs/devel/tracing.txt index bc52f12485..056aa56496 100644 --- a/docs/devel/tracing.txt +++ b/docs/devel/tracing.txt @@ -317,6 +317,10 @@ probes: --target-name x86_64 \ qemu.stp +To facilitate simple usage of systemtap where there merely needs to be printf +logging of certain probes, a helper script "qemu-trace-stap" is provided. +Consult its manual page for guidance on its usage. + == Trace event properties == Each event in the "trace-events-all" file can be prefixed with a space-separated diff --git a/docs/generic-loader.txt b/docs/generic-loader.txt index 31bbcd42f6..a9603a2af7 100644 --- a/docs/generic-loader.txt +++ b/docs/generic-loader.txt @@ -56,25 +56,25 @@ An example of setting CPU 0's PC to 0x8000 is: Loading Files ------------- -The loader device also allows files to be loaded into memory. It can load raw -files and ELF executable files. Raw files are loaded verbatim. ELF executable -files are loaded by an ELF loader. The syntax is shown below: +The loader device also allows files to be loaded into memory. It can load ELF, +U-Boot, and Intel HEX executable formats as well as raw images. The syntax is +shown below: -device loader,file=[,addr=][,cpu-num=][,force-raw=] - A file to be loaded into memory - - The addr in memory that the file should be loaded. This is - ignored if you are using an ELF (unless force-raw is true). - This is required if you aren't loading an ELF. + - The memory address where the file should be loaded. This is + required for raw images and ignored for non-raw files. - This specifies the CPU that should be used. This is an optional argument and will cause the CPU's PC to be set to - where the image is stored or in the case of an ELF file to - the value in the header. This option should only be used - for the boot image. + the memory address where the raw file is loaded or the entry + point specified in the executable format header. This option + should only be used for the boot image. This will also cause the image to be written to the specified CPU's address space. If not specified, the default is CPU 0. - Setting force-raw=on forces the file to be treated as a raw - image. This can be used to load ELF files as if they were raw. + image. This can be used to load supported executable formats + as if they were raw. All values are parsed using the standard QemuOps parsing. This allows the user to specify any values in any format supported. By default the values diff --git a/docs/index.rst b/docs/index.rst new file mode 100644 index 0000000000..3690955dd1 --- /dev/null +++ b/docs/index.rst @@ -0,0 +1,15 @@ +.. QEMU documentation master file, created by + sphinx-quickstart on Thu Jan 31 16:40:14 2019. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to QEMU's documentation! +================================ + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + + interop/index + devel/index + diff --git a/docs/interop/conf.py b/docs/interop/conf.py new file mode 100644 index 0000000000..cf3c69d4a7 --- /dev/null +++ b/docs/interop/conf.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# +# QEMU documentation build configuration file for the 'interop' manual. +# +# This includes the top level conf file and then makes any necessary tweaks. +import sys +import os + +qemu_docdir = os.path.abspath("..") +parent_config = os.path.join(qemu_docdir, "conf.py") +exec(compile(open(parent_config, "rb").read(), parent_config, 'exec')) + +# This slightly misuses the 'description', but is the best way to get +# the manual title to appear in the sidebar. +html_theme_options['description'] = u'System Emulation Management and Interoperability Guide' diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 28f9bc1591..ff8c2ce5f2 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -212,9 +212,13 @@ # # @executable: Identifies the firmware executable. The firmware # executable may be shared by multiple virtual machine -# definitions. The corresponding QEMU command line option -# is "-drive -# if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format". +# definitions. The preferred corresponding QEMU command +# line options are +# -drive if=none,id=pflash0,readonly=on,file=@executable.@filename,format=@executable.@format +# -machine pflash0=pflash0 +# or equivalent -blockdev instead of -drive. +# With QEMU versions older than 4.0, you have to use +# -drive if=pflash,unit=0,readonly=on,file=@executable.@filename,format=@executable.@format # # @nvram-template: Identifies the NVRAM template compatible with # @executable. Management software instantiates an @@ -225,9 +229,13 @@ # individual copies of it are. An NVRAM file is # typically used for persistently storing the # non-volatile UEFI variables of a virtual machine -# definition. The corresponding QEMU command line -# option is "-drive -# if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format". +# definition. The preferred corresponding QEMU +# command line options are +# -drive if=none,id=pflash1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format +# -machine pflash1=pflash1 +# or equivalent -blockdev instead of -drive. +# With QEMU versions older than 4.0, you have to use +# -drive if=pflash,unit=1,readonly=off,file=FILENAME_OF_PRIVATE_NVRAM_FILE,format=@nvram-template.@format # # Since: 3.0 ## diff --git a/docs/interop/index.rst b/docs/interop/index.rst new file mode 100644 index 0000000000..2df977dd52 --- /dev/null +++ b/docs/interop/index.rst @@ -0,0 +1,18 @@ +.. This is the top level page for the 'interop' manual. + + +QEMU System Emulation Management and Interoperability Guide +=========================================================== + +This manual contains documents and specifications that are useful +for making QEMU interoperate with other software. + +Contents: + +.. toctree:: + :maxdepth: 2 + + bitmaps + live-block-operations + pr-helper + diff --git a/docs/interop/live-block-operations.rst b/docs/interop/live-block-operations.rst index 734252bc80..48afdc7927 100644 --- a/docs/interop/live-block-operations.rst +++ b/docs/interop/live-block-operations.rst @@ -129,7 +129,7 @@ To show some example invocations of command-line, we will use the following invocation of QEMU, with a QMP server running over UNIX socket:: - $ ./x86_64-softmmu/qemu-system-x86_64 -display none -nodefconfig \ + $ ./x86_64-softmmu/qemu-system-x86_64 -display none -no-user-config \ -M q35 -nodefaults -m 512 \ -blockdev node-name=node-A,driver=qcow2,file.driver=file,file.node-name=file,file.filename=./a.qcow2 \ -device virtio-blk,drive=node-A,id=virtio0 \ @@ -694,7 +694,7 @@ instance, with the following invocation. (As noted earlier, for simplicity's sake, the destination QEMU is started on the same host, but it could be located elsewhere):: - $ ./x86_64-softmmu/qemu-system-x86_64 -display none -nodefconfig \ + $ ./x86_64-softmmu/qemu-system-x86_64 -display none -no-user-config \ -M q35 -nodefaults -m 512 \ -blockdev node-name=node-TargetDisk,driver=qcow2,file.driver=file,file.node-name=file,file.filename=./target-disk.qcow2 \ -device virtio-blk,drive=node-TargetDisk,id=virtio0 \ diff --git a/docs/interop/nbd.txt b/docs/interop/nbd.txt index 77b5f45911..fc64473e02 100644 --- a/docs/interop/nbd.txt +++ b/docs/interop/nbd.txt @@ -15,7 +15,6 @@ Qemu supports the "base:allocation" metadata context as defined in the NBD protocol specification, and also defines an additional metadata namespace "qemu". - == "qemu" namespace == The "qemu" namespace currently contains only one type of context, @@ -36,3 +35,21 @@ in addition to "qemu:dirty-bitmap:": namespace. * "qemu:dirty-bitmap:" - returns list of all available dirty-bitmap metadata contexts. + += Features by version = + +The following list documents which qemu version first implemented +various features (both as a server exposing the feature, and as a +client taking advantage of the feature when present), to make it +easier to plan for cross-version interoperability. Note that in +several cases, the initial release containing a feature may require +additional patches from the corresponding stable branch to fix bugs in +the operation of that feature. + +* 2.6: NBD_OPT_STARTTLS with TLS X.509 Certificates +* 2.8: NBD_CMD_WRITE_ZEROES +* 2.10: NBD_OPT_GO, NBD_INFO_BLOCK +* 2.11: NBD_OPT_STRUCTURED_REPLY +* 2.12: NBD_CMD_BLOCK_STATUS for "base:allocation" +* 3.0: NBD_OPT_STARTTLS with TLS Pre-Shared Keys (PSK), +NBD_CMD_BLOCK_STATUS for "qemu:dirty-bitmap:", NBD_CMD_CACHE diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.txt index 845d40a086..af5711e533 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.txt @@ -40,7 +40,18 @@ The first cluster of a qcow2 image contains the file header: with larger cluster sizes. 24 - 31: size - Virtual disk size in bytes + Virtual disk size in bytes. + + Note: qemu has an implementation limit of 32 MB as + the maximum L1 table size. With a 2 MB cluster + size, it is unable to populate a virtual cluster + beyond 2 EB (61 bits); with a 512 byte cluster + size, it is unable to populate a virtual size + larger than 128 GB (37 bits). Meanwhile, L1/L2 + table layouts limit an image to no more than 64 PB + (56 bits) of populated clusters, and an image may + hit other limits first (such as a file system's + maximum size). 32 - 35: crypt_method 0 for no encryption @@ -86,7 +97,19 @@ in the description of a field. be written to (unless for regaining consistency). - Bits 2-63: Reserved (set to 0) + Bit 2: External data file bit. If this bit is set, an + external data file is used. Guest clusters are + then stored in the external data file. For such + images, clusters in the external data file are + not refcounted. The offset field in the + Standard Cluster Descriptor must match the + guest offset and neither compressed clusters + nor internal snapshots are supported. + + An External Data File Name header extension may + be present if this bit is set. + + Bits 3-63: Reserved (set to 0) 80 - 87: compatible_features Bitmask of compatible features. An implementation can @@ -115,7 +138,21 @@ in the description of a field. bit is unset, the bitmaps extension data must be considered inconsistent. - Bits 1-63: Reserved (set to 0) + Bit 1: If this bit is set, the external data file can + be read as a consistent standalone raw image + without looking at the qcow2 metadata. + + Setting this bit has a performance impact for + some operations on the image (e.g. writing + zeros requires writing to the data file instead + of only setting the zero flag in the L2 table + entry) and conflicts with backing files. + + This bit may only be set if the External Data + File bit (incompatible feature bit 1) is also + set. + + Bits 2-63: Reserved (set to 0) 96 - 99: refcount_order Describes the width of a reference count block entry (width @@ -133,10 +170,11 @@ be stored. Each extension has a structure like the following: Byte 0 - 3: Header extension type: 0x00000000 - End of the header extension area - 0xE2792ACA - Backing file format name + 0xE2792ACA - Backing file format name string 0x6803f857 - Feature name table 0x23852875 - Bitmaps extension 0x0537be77 - Full disk encryption header pointer + 0x44415441 - External data file name string other - Unknown header extension, can be safely ignored @@ -158,6 +196,16 @@ data of compatible features that it doesn't support. Compatible features that need space for additional data can use a header extension. +== String header extensions == + +Some header extensions (such as the backing file format name and the external +data file name) are just a single string. In this case, the header extension +length is the string length and the string is not '\0' terminated. (The header +extension padding can make it look like a string is '\0' terminated, but +neither is padding always necessary nor is there a guarantee that zero bytes +are used for padding.) + + == Feature name table == The feature name table is an optional header extension that contains the name @@ -326,6 +374,17 @@ in the image file. It contains pointers to the second level structures which are called refcount blocks and are exactly one cluster in size. +Although a large enough refcount table can reserve clusters past 64 PB +(56 bits) (assuming the underlying protocol can even be sized that +large), note that some qcow2 metadata such as L1/L2 tables must point +to clusters prior to that point. + +Note: qemu has an implementation limit of 8 MB as the maximum refcount +table size. With a 2 MB cluster size and a default refcount_order of +4, it is unable to reference host resources beyond 2 EB (61 bits); in +the worst case, with a 512 cluster size and refcount_order of 6, it is +unable to access beyond 32 GB (35 bits). + Given an offset into the image file, the refcount of its cluster can be obtained as follows: @@ -365,6 +424,16 @@ The L1 table has a variable size (stored in the header) and may use multiple clusters, however it must be contiguous in the image file. L2 tables are exactly one cluster in size. +The L1 and L2 tables have implications on the maximum virtual file +size; for a given L1 table size, a larger cluster size is required for +the guest to have access to more space. Furthermore, a virtual +cluster must currently map to a host offset below 64 PB (56 bits) +(although this limit could be relaxed by putting reserved bits into +use). Additionally, as cluster size increases, the maximum host +offset for a compressed cluster is reduced (a 2M cluster size requires +compressed clusters to reside below 512 TB (49 bits), and this limit +cannot be relaxed without an incompatible layout change). + Given an offset into the virtual disk, the offset into the image file can be obtained as follows: @@ -405,6 +474,11 @@ L2 table entry: This information is only accurate in L2 tables that are reachable from the active L1 table. + With external data files, all guest clusters have an + implicit refcount of 1 (because of the fixed host = guest + mapping for guest cluster offsets), so this bit should be 1 + for all allocated clusters. + Standard Cluster Descriptor: Bit 0: If set to 1, the cluster reads as all zeros. The host @@ -418,8 +492,10 @@ Standard Cluster Descriptor: 1 - 8: Reserved (set to 0) 9 - 55: Bits 9-55 of host cluster offset. Must be aligned to a - cluster boundary. If the offset is 0, the cluster is - unallocated. + cluster boundary. If the offset is 0 and bit 63 is clear, + the cluster is unallocated. The offset may only be 0 with + bit 63 set (indicating a host cluster offset of 0) when an + external data file is used. 56 - 61: Reserved (set to 0) @@ -427,7 +503,9 @@ Standard Cluster Descriptor: Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)): Bit 0 - x-1: Host cluster offset. This is usually _not_ aligned to a - cluster or sector boundary! + cluster or sector boundary! If cluster_bits is + small enough that this field includes bits beyond + 55, those upper bits must be set to 0. x - 61: Number of additional 512-byte sectors used for the compressed data, beyond the sector containing the offset @@ -555,7 +633,10 @@ Structure of a bitmap directory entry: Bit 0: in_use The bitmap was not saved correctly and may be - inconsistent. + inconsistent. Although the bitmap metadata is still + well-formed from a qcow2 perspective, the metadata + (such as the auto flag or bitmap size) or data + contents may be outdated. 1: auto The bitmap must reflect all changes of the virtual @@ -683,8 +764,8 @@ 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. +representation in RAM after each write or metadata change. 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 diff --git a/docs/interop/qmp-spec.txt b/docs/interop/qmp-spec.txt index 1566b8ae5e..adcf86754d 100644 --- a/docs/interop/qmp-spec.txt +++ b/docs/interop/qmp-spec.txt @@ -20,9 +20,9 @@ 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. +This section details the protocol format. For the purpose of this +document, "Server" is either QEMU or the QEMU Guest Agent, and +"Client" is any application communicating with it via QMP. JSON data structures, when mentioned in this document, are always in the following format: @@ -32,11 +32,10 @@ following format: Where DATA-STRUCTURE-NAME is any valid JSON data structure, as defined by the JSON standard: -http://www.ietf.org/rfc/rfc7159.txt +http://www.ietf.org/rfc/rfc8259.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. +The server expects its input to be encoded in UTF-8, and sends its +output encoded in ASCII. For convenience, json-object members mentioned in this document will be in a certain order. However, in real protocol usage they can be in @@ -131,8 +130,9 @@ to pass "id" with out-of-band commands. Passing it with all commands is recommended for clients that accept capability "oob". If the client sends in-band commands faster than the server can -execute them, the server will eventually drop commands to limit the -queue length. The sever sends event COMMAND_DROPPED then. +execute them, the server will stop reading the requests from the QMP +channel until the request queue length is reduced to an acceptable +range. Only a few commands support out-of-band execution. The ones that do have "allow-oob": true in output of query-qmp-schema. @@ -215,16 +215,31 @@ Some events are rate-limited to at most one per second. If additional dropped, and the last one is delayed. "Similar" normally means same event type. See qmp-events.txt for details. -2.6 QGA Synchronization +2.6 Forcing the JSON parser into known-good state +------------------------------------------------- + +Incomplete or invalid input can leave the server's JSON parser in a +state where it can't parse additional commands. To get it back into +known-good state, the client should provoke a lexical error. + +The cleanest way to do that is sending an ASCII control character +other than '\t' (horizontal tab), '\r' (carriage return), or '\n' (new +line). + +Sadly, older versions of QEMU can fail to flag this as an error. If a +client needs to deal with them, it should send a 0xFF byte. + +2.7 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. +When a client connects to QGA over a transport lacking proper +connection semantics such as virtio-serial, QGA may have read partial +input from a previous client. The client needs to force QGA's parser +into known-good state using the previous section's technique. +Moreover, the client may receive output a previous client didn't read. +To help with skipping that output, QGA provides the +'guest-sync-delimited' command. Refer to its documentation for +details. 3. QMP Examples diff --git a/docs/interop/vhost-user.json b/docs/interop/vhost-user.json new file mode 100644 index 0000000000..ae88c03117 --- /dev/null +++ b/docs/interop/vhost-user.json @@ -0,0 +1,232 @@ +# -*- Mode: Python -*- +# +# Copyright (C) 2018 Red Hat, Inc. +# +# Authors: +# Marc-André Lureau +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +## +# = vhost user backend discovery & capabilities +## + +## +# @VHostUserBackendType: +# +# List the various vhost user backend types. +# +# @9p: 9p virtio console +# @balloon: virtio balloon +# @block: virtio block +# @caif: virtio caif +# @console: virtio console +# @crypto: virtio crypto +# @gpu: virtio gpu +# @input: virtio input +# @net: virtio net +# @rng: virtio rng +# @rpmsg: virtio remote processor messaging +# @rproc-serial: virtio remoteproc serial link +# @scsi: virtio scsi +# @vsock: virtio vsock transport +# +# Since: 4.0 +## +{ + 'enum': 'VHostUserBackendType', + 'data': [ + '9p', + 'balloon', + 'block', + 'caif', + 'console', + 'crypto', + 'gpu', + 'input', + 'net', + 'rng', + 'rpmsg', + 'rproc-serial', + 'scsi', + 'vsock' + ] +} + +## +# @VHostUserBackendInputFeature: +# +# List of vhost user "input" features. +# +# @evdev-path: The --evdev-path command line option is supported. +# @no-grab: The --no-grab command line option is supported. +# +# Since: 4.0 +## +{ + 'enum': 'VHostUserBackendInputFeature', + 'data': [ 'evdev-path', 'no-grab' ] +} + +## +# @VHostUserBackendCapabilitiesInput: +# +# Capabilities reported by vhost user "input" backends +# +# @features: list of supported features. +# +# Since: 4.0 +## +{ + 'struct': 'VHostUserBackendCapabilitiesInput', + 'data': { + 'features': [ 'VHostUserBackendInputFeature' ] + } +} + +## +# @VHostUserBackendGPUFeature: +# +# List of vhost user "gpu" features. +# +# @render-node: The --render-node command line option is supported. +# @virgl: The --virgl command line option is supported. +# +# Since: 4.0 +## +{ + 'enum': 'VHostUserBackendGPUFeature', + 'data': [ 'render-node', 'virgl' ] +} + +## +# @VHostUserBackendCapabilitiesGPU: +# +# Capabilities reported by vhost user "gpu" backends. +# +# @features: list of supported features. +# +# Since: 4.0 +## +{ + 'struct': 'VHostUserBackendCapabilitiesGPU', + 'data': { + 'features': [ 'VHostUserBackendGPUFeature' ] + } +} + +## +# @VHostUserBackendCapabilities: +# +# Capabilities reported by vhost user backends. +# +# @type: The vhost user backend type. +# +# Since: 4.0 +## +{ + 'union': 'VHostUserBackendCapabilities', + 'base': { 'type': 'VHostUserBackendType' }, + 'discriminator': 'type', + 'data': { + 'input': 'VHostUserBackendCapabilitiesInput', + 'gpu': 'VHostUserBackendCapabilitiesGPU' + } +} + +## +# @VhostUserBackend: +# +# Describes a vhost user backend to management software. +# +# It is possible for multiple @VhostUserBackend elements to match the +# search criteria of management software. Applications thus need rules +# to pick one of the many matches, and users need the ability to +# override distro defaults. +# +# It is recommended to create vhost user backend JSON files (each +# containing a single @VhostUserBackend root element) with a +# double-digit prefix, for example "50-qemu-gpu.json", +# "50-crosvm-gpu.json", etc, so they can be sorted in predictable +# order. The backend JSON files should be searched for in three +# directories: +# +# - /usr/share/qemu/vhost-user -- populated by distro-provided +# packages (XDG_DATA_DIRS covers +# /usr/share by default), +# +# - /etc/qemu/vhost-user -- exclusively for sysadmins' local additions, +# +# - $XDG_CONFIG_HOME/qemu/vhost-user -- exclusively for per-user local +# additions (XDG_CONFIG_HOME +# defaults to $HOME/.config). +# +# Top-down, the list of directories goes from general to specific. +# +# Management software should build a list of files from all three +# locations, then sort the list by filename (i.e., basename +# component). Management software should choose the first JSON file on +# the sorted list that matches the search criteria. If a more specific +# directory has a file with same name as a less specific directory, +# then the file in the more specific directory takes effect. If the +# more specific file is zero length, it hides the less specific one. +# +# For example, if a distro ships +# +# - /usr/share/qemu/vhost-user/50-qemu-gpu.json +# +# - /usr/share/qemu/vhost-user/50-crosvm-gpu.json +# +# then the sysadmin can prevent the default QEMU being used at all with +# +# $ touch /etc/qemu/vhost-user/50-qemu-gpu.json +# +# The sysadmin can replace/alter the distro default OVMF with +# +# $ vim /etc/qemu/vhost-user/50-qemu-gpu.json +# +# or they can provide a parallel QEMU GPU with higher priority +# +# $ vim /etc/qemu/vhost-user/10-qemu-gpu.json +# +# or they can provide a parallel OVMF with lower priority +# +# $ vim /etc/qemu/vhost-user/99-qemu-gpu.json +# +# @type: The vhost user backend type. +# +# @description: Provides a human-readable description of the backend. +# Management software may or may not display @description. +# +# @binary: Absolute path to the backend binary. +# +# @tags: An optional list of auxiliary strings associated with the +# backend for which @description is not appropriate, due to the +# latter's possible exposure to the end-user. @tags serves +# development and debugging purposes only, and management +# software shall explicitly ignore it. +# +# Since: 4.0 +# +# Example: +# +# { +# "description": "QEMU vhost-user-gpu", +# "type": "gpu", +# "binary": "/usr/libexec/qemu/vhost-user-gpu", +# "tags": [ +# "CONFIG_OPENGL_DMABUF=y" +# ] +# } +# +## +{ + 'struct' : 'VhostUserBackend', + 'data' : { + 'description': 'str', + 'type': 'VHostUserBackendType', + 'binary': 'str', + '*tags': [ 'str' ] + } +} diff --git a/docs/interop/vhost-user.txt b/docs/interop/vhost-user.txt index f59667f498..4dbd530cb9 100644 --- a/docs/interop/vhost-user.txt +++ b/docs/interop/vhost-user.txt @@ -17,8 +17,13 @@ The protocol defines 2 sides of the communication, master and slave. Master is the application that shares its virtqueues, in our case QEMU. Slave is the consumer of the virtqueues. -In the current implementation QEMU is the Master, and the Slave is intended to -be a software Ethernet switch running in user space, such as Snabbswitch. +In the current implementation QEMU is the Master, and the Slave is the +external process consuming the virtio queues, for example a software +Ethernet switch running in user space, such as Snabbswitch, or a block +device backend processing read & write to a virtual disk. In order to +facilitate interoperability between various backend implementations, +it is recommended to follow the "Backend program conventions" +described in this document. Master and slave can be either a client (i.e. connecting) or server (listening) in the socket communication. @@ -142,6 +147,17 @@ Depending on the request type, payload can be: Offset: a 64-bit offset of this area from the start of the supplied file descriptor + * Inflight description + ----------------------------------------------------- + | mmap size | mmap offset | num queues | queue size | + ----------------------------------------------------- + + mmap size: a 64-bit size of area to track inflight I/O + mmap offset: a 64-bit offset of this area from the start + of the supplied file descriptor + num queues: a 16-bit number of virtqueues + queue size: a 16-bit size of virtqueues + In QEMU the vhost-user message is implemented with the following struct: typedef struct VhostUserMsg { @@ -157,6 +173,7 @@ typedef struct VhostUserMsg { struct vhost_iotlb_msg iotlb; VhostUserConfig config; VhostUserVringArea area; + VhostUserInflight inflight; }; } QEMU_PACKED VhostUserMsg; @@ -175,6 +192,7 @@ the ones that do: * VHOST_USER_GET_PROTOCOL_FEATURES * VHOST_USER_GET_VRING_BASE * VHOST_USER_SET_LOG_BASE (if VHOST_USER_PROTOCOL_F_LOG_SHMFD) + * VHOST_USER_GET_INFLIGHT_FD (if VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD) [ Also see the section on REPLY_ACK protocol extension. ] @@ -188,6 +206,7 @@ in the ancillary data: * VHOST_USER_SET_VRING_CALL * VHOST_USER_SET_VRING_ERR * VHOST_USER_SET_SLAVE_REQ_FD + * VHOST_USER_SET_INFLIGHT_FD (if VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD) 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. @@ -382,6 +401,256 @@ If VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD protocol feature is negotiated, slave can send file descriptors (at most 8 descriptors in each message) to master via ancillary data using this fd communication channel. +Inflight I/O tracking +--------------------- + +To support reconnecting after restart or crash, slave may need to resubmit +inflight I/Os. If virtqueue is processed in order, we can easily achieve +that by getting the inflight descriptors from descriptor table (split virtqueue) +or descriptor ring (packed virtqueue). However, it can't work when we process +descriptors out-of-order because some entries which store the information of +inflight descriptors in available ring (split virtqueue) or descriptor +ring (packed virtqueue) might be overrided by new entries. To solve this +problem, slave need to allocate an extra buffer to store this information of inflight +descriptors and share it with master for persistent. VHOST_USER_GET_INFLIGHT_FD and +VHOST_USER_SET_INFLIGHT_FD are used to transfer this buffer between master +and slave. And the format of this buffer is described below: + +------------------------------------------------------- +| queue0 region | queue1 region | ... | queueN region | +------------------------------------------------------- + +N is the number of available virtqueues. Slave could get it from num queues +field of VhostUserInflight. + +For split virtqueue, queue region can be implemented as: + +typedef struct DescStateSplit { + /* Indicate whether this descriptor is inflight or not. + * Only available for head-descriptor. */ + uint8_t inflight; + + /* Padding */ + uint8_t padding[5]; + + /* Maintain a list for the last batch of used descriptors. + * Only available when batching is used for submitting */ + uint16_t next; + + /* Used to preserve the order of fetching available descriptors. + * Only available for head-descriptor. */ + uint64_t counter; +} DescStateSplit; + +typedef struct QueueRegionSplit { + /* The feature flags of this region. Now it's initialized to 0. */ + uint64_t features; + + /* The version of this region. It's 1 currently. + * Zero value indicates an uninitialized buffer */ + uint16_t version; + + /* The size of DescStateSplit array. It's equal to the virtqueue + * size. Slave could get it from queue size field of VhostUserInflight. */ + uint16_t desc_num; + + /* The head of list that track the last batch of used descriptors. */ + uint16_t last_batch_head; + + /* Store the idx value of used ring */ + uint16_t used_idx; + + /* Used to track the state of each descriptor in descriptor table */ + DescStateSplit desc[0]; +} QueueRegionSplit; + +To track inflight I/O, the queue region should be processed as follows: + +When receiving available buffers from the driver: + + 1. Get the next available head-descriptor index from available ring, i + + 2. Set desc[i].counter to the value of global counter + + 3. Increase global counter by 1 + + 4. Set desc[i].inflight to 1 + +When supplying used buffers to the driver: + + 1. Get corresponding used head-descriptor index, i + + 2. Set desc[i].next to last_batch_head + + 3. Set last_batch_head to i + + 4. Steps 1,2,3 may be performed repeatedly if batching is possible + + 5. Increase the idx value of used ring by the size of the batch + + 6. Set the inflight field of each DescStateSplit entry in the batch to 0 + + 7. Set used_idx to the idx value of used ring + +When reconnecting: + + 1. If the value of used_idx does not match the idx value of used ring (means + the inflight field of DescStateSplit entries in last batch may be incorrect), + + (a) Subtract the value of used_idx from the idx value of used ring to get + last batch size of DescStateSplit entries + + (b) Set the inflight field of each DescStateSplit entry to 0 in last batch + list which starts from last_batch_head + + (c) Set used_idx to the idx value of used ring + + 2. Resubmit inflight DescStateSplit entries in order of their counter value + +For packed virtqueue, queue region can be implemented as: + +typedef struct DescStatePacked { + /* Indicate whether this descriptor is inflight or not. + * Only available for head-descriptor. */ + uint8_t inflight; + + /* Padding */ + uint8_t padding; + + /* Link to the next free entry */ + uint16_t next; + + /* Link to the last entry of descriptor list. + * Only available for head-descriptor. */ + uint16_t last; + + /* The length of descriptor list. + * Only available for head-descriptor. */ + uint16_t num; + + /* Used to preserve the order of fetching available descriptors. + * Only available for head-descriptor. */ + uint64_t counter; + + /* The buffer id */ + uint16_t id; + + /* The descriptor flags */ + uint16_t flags; + + /* The buffer length */ + uint32_t len; + + /* The buffer address */ + uint64_t addr; +} DescStatePacked; + +typedef struct QueueRegionPacked { + /* The feature flags of this region. Now it's initialized to 0. */ + uint64_t features; + + /* The version of this region. It's 1 currently. + * Zero value indicates an uninitialized buffer */ + uint16_t version; + + /* The size of DescStatePacked array. It's equal to the virtqueue + * size. Slave could get it from queue size field of VhostUserInflight. */ + uint16_t desc_num; + + /* The head of free DescStatePacked entry list */ + uint16_t free_head; + + /* The old head of free DescStatePacked entry list */ + uint16_t old_free_head; + + /* The used index of descriptor ring */ + uint16_t used_idx; + + /* The old used index of descriptor ring */ + uint16_t old_used_idx; + + /* Device ring wrap counter */ + uint8_t used_wrap_counter; + + /* The old device ring wrap counter */ + uint8_t old_used_wrap_counter; + + /* Padding */ + uint8_t padding[7]; + + /* Used to track the state of each descriptor fetched from descriptor ring */ + DescStatePacked desc[0]; +} QueueRegionPacked; + +To track inflight I/O, the queue region should be processed as follows: + +When receiving available buffers from the driver: + + 1. Get the next available descriptor entry from descriptor ring, d + + 2. If d is head descriptor, + + (a) Set desc[old_free_head].num to 0 + + (b) Set desc[old_free_head].counter to the value of global counter + + (c) Increase global counter by 1 + + (d) Set desc[old_free_head].inflight to 1 + + 3. If d is last descriptor, set desc[old_free_head].last to free_head + + 4. Increase desc[old_free_head].num by 1 + + 5. Set desc[free_head].addr, desc[free_head].len, desc[free_head].flags, + desc[free_head].id to d.addr, d.len, d.flags, d.id + + 6. Set free_head to desc[free_head].next + + 7. If d is last descriptor, set old_free_head to free_head + +When supplying used buffers to the driver: + + 1. Get corresponding used head-descriptor entry from descriptor ring, d + + 2. Get corresponding DescStatePacked entry, e + + 3. Set desc[e.last].next to free_head + + 4. Set free_head to the index of e + + 5. Steps 1,2,3,4 may be performed repeatedly if batching is possible + + 6. Increase used_idx by the size of the batch and update used_wrap_counter if needed + + 7. Update d.flags + + 8. Set the inflight field of each head DescStatePacked entry in the batch to 0 + + 9. Set old_free_head, old_used_idx, old_used_wrap_counter to free_head, used_idx, + used_wrap_counter + +When reconnecting: + + 1. If used_idx does not match old_used_idx (means the inflight field of DescStatePacked + entries in last batch may be incorrect), + + (a) Get the next descriptor ring entry through old_used_idx, d + + (b) Use old_used_wrap_counter to calculate the available flags + + (c) If d.flags is not equal to the calculated flags value (means slave has + submitted the buffer to guest driver before crash, so it has to commit the + in-progres update), set old_free_head, old_used_idx, old_used_wrap_counter + to free_head, used_idx, used_wrap_counter + + 2. Set free_head, used_idx, used_wrap_counter to old_free_head, old_used_idx, + old_used_wrap_counter (roll back any in-progress update) + + 3. Set the inflight field of each DescStatePacked entry in free list to 0 + + 4. Resubmit inflight DescStatePacked entries in order of their counter value + Protocol features ----------------- @@ -397,6 +666,7 @@ Protocol features #define VHOST_USER_PROTOCOL_F_CONFIG 9 #define VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD 10 #define VHOST_USER_PROTOCOL_F_HOST_NOTIFIER 11 +#define VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD 12 Master message types -------------------- @@ -666,12 +936,12 @@ Master message types Equivalent ioctl: VHOST_SET_VRING_ENDIAN Master payload: vring state description - Set the endianess of a VQ for legacy devices. Little-endian is indicated + Set the endianness of a VQ for legacy devices. Little-endian is indicated with state.num set to 0 and big-endian is indicated with state.num set to 1. Other values are invalid. This request should be sent only when VHOST_USER_PROTOCOL_F_CROSS_ENDIAN has been negotiated. - Backends that negotiated this feature should handle both endianesses + Backends that negotiated this feature should handle both endiannesses and expect this message once (per VQ) during device configuration (ie. before the master starts the VQ). @@ -761,6 +1031,26 @@ Master message types was previously sent. The value returned is an error indication; 0 is success. + * VHOST_USER_GET_INFLIGHT_FD + Id: 31 + Equivalent ioctl: N/A + Master payload: inflight description + + When VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD protocol feature has been + successfully negotiated, this message is submitted by master to get + a shared buffer from slave. The shared buffer will be used to track + inflight I/O by slave. QEMU should retrieve a new one when vm reset. + + * VHOST_USER_SET_INFLIGHT_FD + Id: 32 + Equivalent ioctl: N/A + Master payload: inflight description + + When VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD protocol feature has been + successfully negotiated, this message is submitted by master to send + the shared inflight buffer back to slave so that slave could get + inflight I/O after a crash or restart. + Slave message types ------------------- @@ -835,3 +1125,95 @@ resilient for selective requests. For the message types that already solicit a reply from the client, the presence of VHOST_USER_PROTOCOL_F_REPLY_ACK or need_reply bit being set brings no behavioural change. (See the 'Communication' section for details.) + +Backend program conventions +--------------------------- + +vhost-user backends can provide various devices & services and may +need to be configured manually depending on the use case. However, it +is a good idea to follow the conventions listed here when +possible. Users, QEMU or libvirt, can then rely on some common +behaviour to avoid heterogenous configuration and management of the +backend programs and facilitate interoperability. + +Each backend installed on a host system should come with at least one +JSON file that conforms to the vhost-user.json schema. Each file +informs the management applications about the backend type, and binary +location. In addition, it defines rules for management apps for +picking the highest priority backend when multiple match the search +criteria (see @VhostUserBackend documentation in the schema file). + +If the backend is not capable of enabling a requested feature on the +host (such as 3D acceleration with virgl), or the initialization +failed, the backend should fail to start early and exit with a status +!= 0. It may also print a message to stderr for further details. + +The backend program must not daemonize itself, but it may be +daemonized by the management layer. It may also have a restricted +access to the system. + +File descriptors 0, 1 and 2 will exist, and have regular +stdin/stdout/stderr usage (they may have been redirected to /dev/null +by the management layer, or to a log handler). + +The backend program must end (as quickly and cleanly as possible) when +the SIGTERM signal is received. Eventually, it may receive SIGKILL by +the management layer after a few seconds. + +The following command line options have an expected behaviour. They +are mandatory, unless explicitly said differently: + +* --socket-path=PATH + +This option specify the location of the vhost-user Unix domain socket. +It is incompatible with --fd. + +* --fd=FDNUM + +When this argument is given, the backend program is started with the +vhost-user socket as file descriptor FDNUM. It is incompatible with +--socket-path. + +* --print-capabilities + +Output to stdout the backend capabilities in JSON format, and then +exit successfully. Other options and arguments should be ignored, and +the backend program should not perform its normal function. The +capabilities can be reported dynamically depending on the host +capabilities. + +The JSON output is described in the vhost-user.json schema, by +@VHostUserBackendCapabilities. Example: +{ + "type": "foo", + "features": [ + "feature-a", + "feature-b" + ] +} + +vhost-user-input +---------------- + +Command line options: + +* --evdev-path=PATH (optional) + +Specify the linux input device. + +* --no-grab (optional) + +Do no request exclusive access to the input device. + +vhost-user-gpu +-------------- + +Command line options: + +* --render-node=PATH (optional) + +Specify the GPU DRM render node. + +* --virgl (optional) + +Enable virgl rendering support. diff --git a/docs/multiseat.txt b/docs/multiseat.txt index 8dde36c845..11850c96ff 100644 --- a/docs/multiseat.txt +++ b/docs/multiseat.txt @@ -9,7 +9,7 @@ First you must compile qemu with a user interface supporting multihead/multiseat and input event routing. Right now this list includes sdl2, gtk (both 2+3) and vnc: - ./configure --enable-sdl --with-sdlabi=2.0 + ./configure --enable-sdl or diff --git a/docs/nvdimm.txt b/docs/nvdimm.txt index 24b443b655..7231c2d78f 100644 --- a/docs/nvdimm.txt +++ b/docs/nvdimm.txt @@ -49,8 +49,9 @@ Multiple vNVDIMM devices can be created if multiple pairs of "-object" and "-device" are provided. For above command line options, if the guest OS has the proper NVDIMM -driver, it should be able to detect a NVDIMM device which is in the -persistent memory mode and whose size is $NVDIMM_SIZE. +driver (e.g. "CONFIG_ACPI_NFIT=y" under Linux), it should be able to +detect a NVDIMM device which is in the persistent memory mode and whose +size is $NVDIMM_SIZE. Note: @@ -173,3 +174,25 @@ There are currently two valid values for this option: the NVDIMMs in the event of power loss. This implies that the platform also supports flushing dirty data through the memory controller on power loss. + +If the vNVDIMM backend is in host persistent memory that can be accessed in +SNIA NVM Programming Model [1] (e.g., Intel NVDIMM), it's suggested to set +the 'pmem' option of memory-backend-file to 'on'. When 'pmem' is 'on' and QEMU +is built with libpmem [2] support (configured with --enable-libpmem), QEMU +will take necessary operations to guarantee the persistence of its own writes +to the vNVDIMM backend(e.g., in vNVDIMM label emulation and live migration). +If 'pmem' is 'on' while there is no libpmem support, qemu will exit and report +a "lack of libpmem support" message to ensure the persistence is available. +For example, if we want to ensure the persistence for some backend file, +use the QEMU command line: + + -object memory-backend-file,id=nv_mem,mem-path=/XXX/yyy,size=4G,pmem=on + +References +---------- + +[1] NVM Programming Model (NPM) + Version 1.2 + https://www.snia.org/sites/default/files/technical_work/final/NVMProgrammingModel_v1.2.pdf +[2] Persistent Memory Development Kit (PMDK), formerly known as NVML project, home page: + http://pmem.io/pmdk/ diff --git a/docs/pvrdma.txt b/docs/pvrdma.txt index 5599318159..0f0dd8a7e5 100644 --- a/docs/pvrdma.txt +++ b/docs/pvrdma.txt @@ -9,8 +9,9 @@ It works with its Linux Kernel driver AS IS, no need for any special guest modifications. While it complies with the VMware device, it can also communicate with bare -metal RDMA-enabled machines and does not require an RDMA HCA in the host, it -can work with Soft-RoCE (rxe). +metal RDMA-enabled machines as peers. + +It does not require an RDMA HCA in the host, it can work with Soft-RoCE (rxe). It does not require the whole guest RAM to be pinned allowing memory over-commit and, even if not implemented yet, migration support will be @@ -78,29 +79,118 @@ the required RDMA libraries. 3. Usage ======== + + +3.1 VM Memory settings +====================== Currently the device is working only with memory backed RAM and it must be mark as "shared": -m 1G \ -object memory-backend-ram,id=mb1,size=1G,share \ -numa node,memdev=mb1 \ -The pvrdma device is composed of two functions: - - Function 0 is a vmxnet Ethernet Device which is redundant in Guest - but is required to pass the ibdevice GID using its MAC. - Examples: - For an rxe backend using eth0 interface it will use its mac: - -device vmxnet3,addr=.0,multifunction=on,mac= - For an SRIOV VF, we take the Ethernet Interface exposed by it: - -device vmxnet3,multifunction=on,mac= - - Function 1 is the actual device: - -device pvrdma,addr=.1,backend-dev=,backend-gid-idx=,backend-port= - where the ibdevice can be rxe or RDMA VF (e.g. mlx5_4) - Note: Pay special attention that the GID at backend-gid-idx matches vmxnet's MAC. - The rules of conversion are part of the RoCE spec, but since manual conversion - is not required, spotting problems is not hard: - Example: GID: fe80:0000:0000:0000:7efe:90ff:fecb:743a - MAC: 7c:fe:90:cb:74:3a - Note the difference between the first byte of the MAC and the GID. + +3.2 MAD Multiplexer +=================== +MAD Multiplexer is a service that exposes MAD-like interface for VMs in +order to overcome the limitation where only single entity can register with +MAD layer to send and receive RDMA-CM MAD packets. + +To build rdmacm-mux run +# make rdmacm-mux + +Before running the rdmacm-mux make sure that both ib_cm and rdma_cm kernel +modules aren't loaded, otherwise the rdmacm-mux service will fail to start. + +The application accepts 3 command line arguments and exposes a UNIX socket +to pass control and data to it. +-d rdma-device-name Name of RDMA device to register with +-s unix-socket-path Path to unix socket to listen (default /var/run/rdmacm-mux) +-p rdma-device-port Port number of RDMA device to register with (default 1) +The final UNIX socket file name is a concatenation of the 3 arguments so +for example for device mlx5_0 on port 2 this /var/run/rdmacm-mux-mlx5_0-2 +will be created. + +pvrdma requires this service. + +Please refer to contrib/rdmacm-mux for more details. + + +3.3 Service exposed by libvirt daemon +===================================== +The control over the RDMA device's GID table is done by updating the +device's Ethernet function addresses. +Usually the first GID entry is determined by the MAC address, the second by +the first IPv6 address and the third by the IPv4 address. Other entries can +be added by adding more IP addresses. The opposite is the same, i.e. +whenever an address is removed, the corresponding GID entry is removed. +The process is done by the network and RDMA stacks. Whenever an address is +added the ib_core driver is notified and calls the device driver add_gid +function which in turn update the device. +To support this in pvrdma device the device hooks into the create_bind and +destroy_bind HW commands triggered by pvrdma driver in guest. + +Whenever changed is made to the pvrdma port's GID table a special QMP +messages is sent to be processed by libvirt to update the address of the +backend Ethernet device. + +pvrdma requires that libvirt service will be up. + + +3.4 PCI devices settings +======================== +RoCE device exposes two functions - an Ethernet and RDMA. +To support it, pvrdma device is composed of two PCI functions, an Ethernet +device of type vmxnet3 on PCI slot 0 and a PVRDMA device on PCI slot 1. The +Ethernet function can be used for other Ethernet purposes such as IP. + + +3.5 Device parameters +===================== +- netdev: Specifies the Ethernet device function name on the host for + example enp175s0f0. For Soft-RoCE device (rxe) this would be the Ethernet + device used to create it. +- ibdev: The IB device name on host for example rxe0, mlx5_0 etc. +- mad-chardev: The name of the MAD multiplexer char device. +- ibport: In case of multi-port device (such as Mellanox's HCA) this + specify the port to use. If not set 1 will be used. +- dev-caps-max-mr-size: The maximum size of MR. +- dev-caps-max-qp: Maximum number of QPs. +- dev-caps-max-cq: Maximum number of CQs. +- dev-caps-max-mr: Maximum number of MRs. +- dev-caps-max-pd: Maximum number of PDs. +- dev-caps-max-ah: Maximum number of AHs. + +Notes: +- The first 3 parameters are mandatory settings, the rest have their + defaults. +- The last 8 parameters (the ones that prefixed by dev-caps) defines the top + limits but the final values is adjusted by the backend device limitations. +- netdev can be extracted from ibdev's sysfs + (/sys/class/infiniband//device/net/) + + +3.6 Example +=========== +Define bridge device with vmxnet3 network backend: + + + + +
+ + +Define pvrdma device: + + + + + + + + + + diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt index 8a09a5cc5f..d57f409861 100644 --- a/docs/qcow2-cache.txt +++ b/docs/qcow2-cache.txt @@ -55,7 +55,7 @@ value can improve the I/O performance significantly. The refcount blocks ------------------- -The qcow2 format also mantains a reference count for each cluster. +The qcow2 format also maintains 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. @@ -79,14 +79,14 @@ 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 +The part of the 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 +(16), this becomes: disk_size = l2_cache_size * 8192 disk_size = refcount_cache_size * 32768 @@ -97,12 +97,16 @@ 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 +For example, 1MB of L2 cache is needed to cover every 8 GB of the virtual +image size (given that the default cluster size is used): - 1048576 / 131072 = 8 GB of virtual disk covered by that cache - 262144 / 32768 = 8 GB + 8 GB / 8192 = 1 MB + +The refcount cache is 4 times the cluster size by default. With the default +cluster size of 64 KB, it is 256 KB (262144 bytes). This is sufficient for +8 GB of image size: + + 262144 * 32768 = 8 GB How to configure the cache sizes @@ -121,8 +125,15 @@ There are a few things that need to be taken into account: - Both caches must have a size that is a multiple of the cluster size (or the cache entry size: see "Using smaller cache sizes" below). - - The default L2 cache size is 8 clusters or 1MB (whichever is more), - and the minimum is 2 clusters (or 2 cache entries, see below). + - The maximum L2 cache size is 32 MB by default on Linux platforms (enough + for full coverage of 256 GB images, with the default cluster size). This + value can be modified using the "l2-cache-size" option. QEMU will not use + more memory than needed to hold all of the image's L2 tables, regardless + of this max. value. + On non-Linux platforms the maximal value is smaller by default (8 MB) and + this difference stems from the fact that on Linux the cache can be cleared + periodically if needed, using the "cache-clean-interval" option (see below). + The minimal L2 cache size is 2 clusters (or 2 cache entries, see below). - The default (and minimum) refcount cache size is 4 clusters. @@ -130,6 +141,9 @@ There are a few things that need to be taken into account: memory as possible to the L2 cache before increasing the refcount cache size. + - At most two of "l2-cache-size", "refcount-cache-size", and "cache-size" + can be set simultaneously. + Unlike L2 tables, refcount blocks are not used during normal I/O but only during allocations and internal snapshots. In most cases they are accessed sequentially (even during random guest I/O) so increasing the @@ -144,10 +158,10 @@ refcount cache is as small as possible unless overridden by the user. Using smaller cache entries --------------------------- -The qcow2 L2 cache stores complete tables by default. This means that -if QEMU needs an entry from an L2 table then the whole table is read -from disk and is kept in the cache. If the cache is full then a -complete table needs to be evicted first. +The qcow2 L2 cache can store complete tables. This means that if QEMU +needs an entry from an L2 table then the whole table is read from disk +and is kept in the cache. If the cache is full then a complete table +needs to be evicted first. This can be inefficient with large cluster sizes since it results in more disk I/O and wastes more cache memory. @@ -158,6 +172,9 @@ it smaller than the cluster size. This can be configured using the -drive file=hd.qcow2,l2-cache-size=2097152,l2-cache-entry-size=4096 +Since QEMU 4.0 the value of l2-cache-entry-size defaults to 4KB (or +the cluster size if it's smaller). + Some things to take into account: - The L2 cache entry size has the same restrictions as the cluster @@ -171,15 +188,18 @@ Some things to take into account: - Try different entry sizes to see which one gives faster performance in your case. The block size of the host filesystem is generally a - good default (usually 4096 bytes in the case of ext4). + good default (usually 4096 bytes in the case of ext4, hence the + default). - Only the L2 cache can be configured this way. The refcount cache always uses the cluster size as the entry size. - If the L2 cache is big enough to hold all of the image's L2 tables - (as explained in the "Choosing the right cache sizes" section - earlier in this document) then none of this is necessary and you - can omit the "l2-cache-entry-size" parameter altogether. + (as explained in the "Choosing the right cache sizes" and "How to + configure the cache sizes" sections in this document) then none of + this is necessary and you can omit the "l2-cache-entry-size" + parameter altogether. In this case QEMU makes the entry size + equal to the cluster size by default. Reducing the memory usage @@ -187,18 +207,18 @@ 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. +The parameter "cache-clean-interval" defines an interval (in seconds), +after which all the cache entries that haven't been accessed during the +interval are removed from memory. Setting this parameter to 0 disables this +feature. -This example removes all unused cache entries every 15 minutes: +The following 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. +If unset, the default value for this parameter is 600 on platforms which +support this functionality, and is 0 (disabled) on other platforms. -Note that this functionality currently relies on the MADV_DONTNEED -argument for madvise() to actually free the memory. This is a -Linux-specific feature, so cache-clean-interval is not supported in -other systems. +This functionality currently relies on the MADV_DONTNEED argument for +madvise() to actually free the memory. This is a Linux-specific feature, +so cache-clean-interval is not supported on other systems. diff --git a/docs/qdev-device-use.txt b/docs/qdev-device-use.txt index 98229b3405..cc53e97dcd 100644 --- a/docs/qdev-device-use.txt +++ b/docs/qdev-device-use.txt @@ -190,10 +190,6 @@ The appropriate DEVNAME depends on the machine type. For type "pc": -device usb-braille,chardev=braille -chardev braille,id=braille -* -virtioconsole becomes - -device virtio-serial-pci,class=C,vectors=V,ioeventfd=IOEVENTFD,max_ports=N - -device virtconsole,is_console=NUM,nr=NR,name=NAME - LEGACY-CHARDEV translates to -chardev HOST-OPTS... as follows: * null becomes -chardev null diff --git a/docs/qemu-block-drivers.texi b/docs/qemu-block-drivers.texi index 38e9f34cc9..da06a9bc83 100644 --- a/docs/qemu-block-drivers.texi +++ b/docs/qemu-block-drivers.texi @@ -632,7 +632,7 @@ qemu-system-i386 -drive file=iscsi://127.0.0.1/iqn.qemu.test/1 \ @end example -Howto set up a simple iSCSI target on loopback and accessing it via QEMU: +How to set up a simple iSCSI target on loopback and access it via QEMU: @example This example shows how to set up an iSCSI target with one CDROM and one DISK using the Linux STGT software target. This target is available on Red Hat based diff --git a/docs/qemu-cpu-models.texi b/docs/qemu-cpu-models.texi new file mode 100644 index 0000000000..23c11dc86f --- /dev/null +++ b/docs/qemu-cpu-models.texi @@ -0,0 +1,663 @@ +@c man begin SYNOPSIS +QEMU / KVM CPU model configuration +@c man end + +@c man begin DESCRIPTION + +@menu +* recommendations_cpu_models_x86:: Recommendations for KVM CPU model configuration on x86 hosts +* recommendations_cpu_models_MIPS:: Supported CPU model configurations on MIPS hosts +* cpu_model_syntax_apps:: Syntax for configuring CPU models +@end menu + +QEMU / KVM virtualization supports two ways to configure CPU models + +@table @option + +@item Host passthrough + +This passes the host CPU model features, model, stepping, exactly to the +guest. Note that KVM may filter out some host CPU model features if they +cannot be supported with virtualization. Live migration is unsafe when +this mode is used as libvirt / QEMU cannot guarantee a stable CPU is +exposed to the guest across hosts. This is the recommended CPU to use, +provided live migration is not required. + +@item Named model + +QEMU comes with a number of predefined named CPU models, that typically +refer to specific generations of hardware released by Intel and AMD. +These allow the guest VMs to have a degree of isolation from the host CPU, +allowing greater flexibility in live migrating between hosts with differing +hardware. +@end table + +In both cases, it is possible to optionally add or remove individual CPU +features, to alter what is presented to the guest by default. + +Libvirt supports a third way to configure CPU models known as "Host model". +This uses the QEMU "Named model" feature, automatically picking a CPU model +that is similar the host CPU, and then adding extra features to approximate +the host model as closely as possible. This does not guarantee the CPU family, +stepping, etc will precisely match the host CPU, as they would with "Host +passthrough", but gives much of the benefit of passthrough, while making +live migration safe. + +@node recommendations_cpu_models_x86 +@subsection Recommendations for KVM CPU model configuration on x86 hosts + +The information that follows provides recommendations for configuring +CPU models on x86 hosts. The goals are to maximise performance, while +protecting guest OS against various CPU hardware flaws, and optionally +enabling live migration between hosts with heterogeneous CPU models. + +@menu +* preferred_cpu_models_intel_x86:: Preferred CPU models for Intel x86 hosts +* important_cpu_features_intel_x86:: Important CPU features for Intel x86 hosts +* preferred_cpu_models_amd_x86:: Preferred CPU models for AMD x86 hosts +* important_cpu_features_amd_x86:: Important CPU features for AMD x86 hosts +* default_cpu_models_x86:: Default x86 CPU models +* other_non_recommended_cpu_models_x86:: Other non-recommended x86 CPUs +@end menu + +@node preferred_cpu_models_intel_x86 +@subsubsection Preferred CPU models for Intel x86 hosts + +The following CPU models are preferred for use on Intel hosts. Administrators / +applications are recommended to use the CPU model that matches the generation +of the host CPUs in use. In a deployment with a mixture of host CPU models +between machines, if live migration compatibility is required, use the newest +CPU model that is compatible across all desired hosts. + +@table @option +@item @code{Skylake-Server} +@item @code{Skylake-Server-IBRS} + +Intel Xeon Processor (Skylake, 2016) + + +@item @code{Skylake-Client} +@item @code{Skylake-Client-IBRS} + +Intel Core Processor (Skylake, 2015) + + +@item @code{Broadwell} +@item @code{Broadwell-IBRS} +@item @code{Broadwell-noTSX} +@item @code{Broadwell-noTSX-IBRS} + +Intel Core Processor (Broadwell, 2014) + + +@item @code{Haswell} +@item @code{Haswell-IBRS} +@item @code{Haswell-noTSX} +@item @code{Haswell-noTSX-IBRS} + +Intel Core Processor (Haswell, 2013) + + +@item @code{IvyBridge} +@item @code{IvyBridge-IBRS} + +Intel Xeon E3-12xx v2 (Ivy Bridge, 2012) + + +@item @code{SandyBridge} +@item @code{SandyBridge-IBRS} + +Intel Xeon E312xx (Sandy Bridge, 2011) + + +@item @code{Westmere} +@item @code{Westmere-IBRS} + +Westmere E56xx/L56xx/X56xx (Nehalem-C, 2010) + + +@item @code{Nehalem} +@item @code{Nehalem-IBRS} + +Intel Core i7 9xx (Nehalem Class Core i7, 2008) + + +@item @code{Penryn} + +Intel Core 2 Duo P9xxx (Penryn Class Core 2, 2007) + + +@item @code{Conroe} + +Intel Celeron_4x0 (Conroe/Merom Class Core 2, 2006) + +@end table + +@node important_cpu_features_intel_x86 +@subsubsection Important CPU features for Intel x86 hosts + +The following are important CPU features that should be used on Intel x86 +hosts, when available in the host CPU. Some of them require explicit +configuration to enable, as they are not included by default in some, or all, +of the named CPU models listed above. In general all of these features are +included if using "Host passthrough" or "Host model". + + +@table @option + +@item @code{pcid} + +Recommended to mitigate the cost of the Meltdown (CVE-2017-5754) fix + +Included by default in Haswell, Broadwell & Skylake Intel CPU models. + +Should be explicitly turned on for Westmere, SandyBridge, and IvyBridge +Intel CPU models. Note that some desktop/mobile Westmere CPUs cannot +support this feature. + + +@item @code{spec-ctrl} + +Required to enable the Spectre v2 (CVE-2017-5715) fix. + +Included by default in Intel CPU models with -IBRS suffix. + +Must be explicitly turned on for Intel CPU models without -IBRS suffix. + +Requires the host CPU microcode to support this feature before it +can be used for guest CPUs. + + +@item @code{stibp} + +Required to enable stronger Spectre v2 (CVE-2017-5715) fixes in some +operating systems. + +Must be explicitly turned on for all Intel CPU models. + +Requires the host CPU microcode to support this feature before it +can be used for guest CPUs. + + +@item @code{ssbd} + +Required to enable the CVE-2018-3639 fix + +Not included by default in any Intel CPU model. + +Must be explicitly turned on for all Intel CPU models. + +Requires the host CPU microcode to support this feature before it +can be used for guest CPUs. + + +@item @code{pdpe1gb} + +Recommended to allow guest OS to use 1GB size pages + +Not included by default in any Intel CPU model. + +Should be explicitly turned on for all Intel CPU models. + +Note that not all CPU hardware will support this feature. +@end table + + +@node preferred_cpu_models_amd_x86 +@subsubsection Preferred CPU models for AMD x86 hosts + +The following CPU models are preferred for use on Intel hosts. Administrators / +applications are recommended to use the CPU model that matches the generation +of the host CPUs in use. In a deployment with a mixture of host CPU models +between machines, if live migration compatibility is required, use the newest +CPU model that is compatible across all desired hosts. + +@table @option + +@item @code{EPYC} +@item @code{EPYC-IBPB} + +AMD EPYC Processor (2017) + + +@item @code{Opteron_G5} + +AMD Opteron 63xx class CPU (2012) + + +@item @code{Opteron_G4} + +AMD Opteron 62xx class CPU (2011) + + +@item @code{Opteron_G3} + +AMD Opteron 23xx (Gen 3 Class Opteron, 2009) + + +@item @code{Opteron_G2} + +AMD Opteron 22xx (Gen 2 Class Opteron, 2006) + + +@item @code{Opteron_G1} + +AMD Opteron 240 (Gen 1 Class Opteron, 2004) +@end table + +@node important_cpu_features_amd_x86 +@subsubsection Important CPU features for AMD x86 hosts + +The following are important CPU features that should be used on AMD x86 +hosts, when available in the host CPU. Some of them require explicit +configuration to enable, as they are not included by default in some, or all, +of the named CPU models listed above. In general all of these features are +included if using "Host passthrough" or "Host model". + + +@table @option + +@item @code{ibpb} + +Required to enable the Spectre v2 (CVE-2017-5715) fix. + +Included by default in AMD CPU models with -IBPB suffix. + +Must be explicitly turned on for AMD CPU models without -IBPB suffix. + +Requires the host CPU microcode to support this feature before it +can be used for guest CPUs. + + +@item @code{stibp} + +Required to enable stronger Spectre v2 (CVE-2017-5715) fixes in some +operating systems. + +Must be explicitly turned on for all AMD CPU models. + +Requires the host CPU microcode to support this feature before it +can be used for guest CPUs. + + +@item @code{virt-ssbd} + +Required to enable the CVE-2018-3639 fix + +Not included by default in any AMD CPU model. + +Must be explicitly turned on for all AMD CPU models. + +This should be provided to guests, even if amd-ssbd is also +provided, for maximum guest compatibility. + +Note for some QEMU / libvirt versions, this must be force enabled +when when using "Host model", because this is a virtual feature +that doesn't exist in the physical host CPUs. + + +@item @code{amd-ssbd} + +Required to enable the CVE-2018-3639 fix + +Not included by default in any AMD CPU model. + +Must be explicitly turned on for all AMD CPU models. + +This provides higher performance than virt-ssbd so should be +exposed to guests whenever available in the host. virt-ssbd +should none the less also be exposed for maximum guest +compatibility as some kernels only know about virt-ssbd. + + +@item @code{amd-no-ssb} + +Recommended to indicate the host is not vulnerable CVE-2018-3639 + +Not included by default in any AMD CPU model. + +Future hardware generations of CPU will not be vulnerable to +CVE-2018-3639, and thus the guest should be told not to enable +its mitigations, by exposing amd-no-ssb. This is mutually +exclusive with virt-ssbd and amd-ssbd. + + +@item @code{pdpe1gb} + +Recommended to allow guest OS to use 1GB size pages + +Not included by default in any AMD CPU model. + +Should be explicitly turned on for all AMD CPU models. + +Note that not all CPU hardware will support this feature. +@end table + + +@node default_cpu_models_x86 +@subsubsection Default x86 CPU models + +The default QEMU CPU models are designed such that they can run on all hosts. +If an application does not wish to do perform any host compatibility checks +before launching guests, the default is guaranteed to work. + +The default CPU models will, however, leave the guest OS vulnerable to various +CPU hardware flaws, so their use is strongly discouraged. Applications should +follow the earlier guidance to setup a better CPU configuration, with host +passthrough recommended if live migration is not needed. + +@table @option +@item @code{qemu32} +@item @code{qemu64} + +QEMU Virtual CPU version 2.5+ (32 & 64 bit variants) + +qemu64 is used for x86_64 guests and qemu32 is used for i686 guests, when no +-cpu argument is given to QEMU, or no is provided in libvirt XML. +@end table + + +@node other_non_recommended_cpu_models_x86 +@subsubsection Other non-recommended x86 CPUs + +The following CPUs models are compatible with most AMD and Intel x86 hosts, but +their usage is discouraged, as they expose a very limited featureset, which +prevents guests having optimal performance. + +@table @option + +@item @code{kvm32} +@item @code{kvm64} + +Common KVM processor (32 & 64 bit variants) + +Legacy models just for historical compatibility with ancient QEMU versions. + + +@item @code{486} +@item @code{athlon} +@item @code{phenom} +@item @code{coreduo} +@item @code{core2duo} +@item @code{n270} +@item @code{pentium} +@item @code{pentium2} +@item @code{pentium3} + +Various very old x86 CPU models, mostly predating the introduction of +hardware assisted virtualization, that should thus not be required for +running virtual machines. +@end table + +@node recommendations_cpu_models_MIPS +@subsection Supported CPU model configurations on MIPS hosts + +QEMU supports variety of MIPS CPU models: + +@menu +* cpu_models_MIPS32:: Supported CPU models for MIPS32 hosts +* cpu_models_MIPS64:: Supported CPU models for MIPS64 hosts +* cpu_models_nanoMIPS:: Supported CPU models for nanoMIPS hosts +* preferred_cpu_models_MIPS:: Preferred CPU models for MIPS hosts +@end menu + +@node cpu_models_MIPS32 +@subsubsection Supported CPU models for MIPS32 hosts + +The following CPU models are supported for use on MIPS32 hosts. Administrators / +applications are recommended to use the CPU model that matches the generation +of the host CPUs in use. In a deployment with a mixture of host CPU models +between machines, if live migration compatibility is required, use the newest +CPU model that is compatible across all desired hosts. + +@table @option +@item @code{mips32r6-generic} + +MIPS32 Processor (Release 6, 2015) + + +@item @code{P5600} + +MIPS32 Processor (P5600, 2014) + + +@item @code{M14K} +@item @code{M14Kc} + +MIPS32 Processor (M14K, 2009) + + +@item @code{74Kf} + +MIPS32 Processor (74K, 2007) + + +@item @code{34Kf} + +MIPS32 Processor (34K, 2006) + + +@item @code{24Kc} +@item @code{24KEc} +@item @code{24Kf} + +MIPS32 Processor (24K, 2003) + + +@item @code{4Kc} +@item @code{4Km} +@item @code{4KEcR1} +@item @code{4KEmR1} +@item @code{4KEc} +@item @code{4KEm} + +MIPS32 Processor (4K, 1999) +@end table + +@node cpu_models_MIPS64 +@subsubsection Supported CPU models for MIPS64 hosts + +The following CPU models are supported for use on MIPS64 hosts. Administrators / +applications are recommended to use the CPU model that matches the generation +of the host CPUs in use. In a deployment with a mixture of host CPU models +between machines, if live migration compatibility is required, use the newest +CPU model that is compatible across all desired hosts. + +@table @option +@item @code{I6400} + +MIPS64 Processor (Release 6, 2014) + + +@item @code{Loongson-2F} + +MIPS64 Processor (Loongson 2, 2008) + + +@item @code{Loongson-2E} + +MIPS64 Processor (Loongson 2, 2006) + + +@item @code{mips64dspr2} + +MIPS64 Processor (Release 2, 2006) + + +@item @code{MIPS64R2-generic} +@item @code{5KEc} +@item @code{5KEf} + +MIPS64 Processor (Release 2, 2002) + + +@item @code{20Kc} + +MIPS64 Processor (20K, 2000) + + +@item @code{5Kc} +@item @code{5Kf} + +MIPS64 Processor (5K, 1999) + + +@item @code{VR5432} + +MIPS64 Processor (VR, 1998) + + +@item @code{R4000} + +MIPS64 Processor (MIPS III, 1991) +@end table + +@node cpu_models_nanoMIPS +@subsubsection Supported CPU models for nanoMIPS hosts + +The following CPU models are supported for use on nanoMIPS hosts. Administrators / +applications are recommended to use the CPU model that matches the generation +of the host CPUs in use. In a deployment with a mixture of host CPU models +between machines, if live migration compatibility is required, use the newest +CPU model that is compatible across all desired hosts. + +@table @option +@item @code{I7200} + +MIPS I7200 (nanoMIPS, 2018) + +@end table + +@node preferred_cpu_models_MIPS +@subsubsection Preferred CPU models for MIPS hosts + +The following CPU models are preferred for use on different MIPS hosts: + +@table @option +@item @code{MIPS III} +R4000 + +@item @code{MIPS32R2} +34Kf + +@item @code{MIPS64R6} +I6400 + +@item @code{nanoMIPS} +I7200 +@end table + +@node cpu_model_syntax_apps +@subsection Syntax for configuring CPU models + +The example below illustrate the approach to configuring the various +CPU models / features in QEMU and libvirt + +@menu +* cpu_model_syntax_qemu:: QEMU command line +* cpu_model_syntax_libvirt:: Libvirt guest XML +@end menu + +@node cpu_model_syntax_qemu +@subsubsection QEMU command line + +@table @option + +@item Host passthrough + +@example + $ qemu-system-x86_64 -cpu host +@end example + +With feature customization: + +@example + $ qemu-system-x86_64 -cpu host,-vmx,... +@end example + +@item Named CPU models + +@example + $ qemu-system-x86_64 -cpu Westmere +@end example + +With feature customization: + +@example + $ qemu-system-x86_64 -cpu Westmere,+pcid,... +@end example + +@end table + +@node cpu_model_syntax_libvirt +@subsubsection Libvirt guest XML + +@table @option + +@item Host passthrough + +@example + +@end example + +With feature customization: + +@example + + + ... + +@end example + +@item Host model + +@example + +@end example + +With feature customization: + +@example + + + ... + +@end example + +@item Named model + +@example + + + +@end example + +With feature customization: + +@example + + + + ... + +@end example + +@end table + +@c man end + +@ignore + +@setfilename qemu-cpu-models +@settitle QEMU / KVM CPU model configuration + +@c man begin SEEALSO +The HTML documentation of QEMU for more precise information and Linux +user mode emulator invocation. +@c man end + +@c man begin AUTHOR +Daniel P. Berrange +@c man end + +@end ignore diff --git a/docs/rdma.txt b/docs/rdma.txt index e6f9902617..a86e992c84 100644 --- a/docs/rdma.txt +++ b/docs/rdma.txt @@ -30,7 +30,7 @@ of the significantly lower latency and higher throughput over TCP/IP. This is because the RDMA I/O architecture reduces the number of interrupts and data copies by bypassing the host networking stack. In particular, a TCP-based migration, under certain types of memory-bound workloads, may take a more -unpredicatable amount of time to complete the migration if the amount of +unpredictable amount of time to complete the migration if the amount of memory tracked during each live migration iteration round cannot keep pace with the rate of dirty memory produced by the workload. @@ -408,7 +408,7 @@ socket is broken during a non-RDMA based migration. TODO: ===== 1. Currently, 'ulimit -l' mlock() limits as well as cgroups swap limits - are not compatible with infinband memory pinning and will result in + are not compatible with infiniband memory pinning and will result in an aborted migration (but with the source VM left unaffected). 2. Use of the recent /proc//pagemap would likely speed up the use of KSM and ballooning while using RDMA. diff --git a/docs/replay.txt b/docs/replay.txt index 2e21e9ccb0..ee6aee9861 100644 --- a/docs/replay.txt +++ b/docs/replay.txt @@ -290,7 +290,7 @@ E.g., '-serial stdio' in record mode, and '-serial null' in replay mode. Replay log format ----------------- -Record/replay log consits of the header and the sequence of execution +Record/replay log consists of the header and the sequence of execution events. The header includes 4-byte replay version id and 8-byte reserved field. Version is updated every time replay log format changes to prevent using replay log created by another build of qemu. @@ -320,7 +320,7 @@ Here is the list of events that are written into the log: async event id from the following list: - REPLAY_ASYNC_EVENT_BH. Bottom-half callback. This event synchronizes callbacks that affect virtual machine state, but normally called - asyncronously. + asynchronously. Argument: 8-byte operation id. - REPLAY_ASYNC_EVENT_INPUT. Input device event. Contains parameters of keyboard and mouse input operations diff --git a/docs/specs/ivshmem-spec.txt b/docs/specs/ivshmem-spec.txt index a1f5499796..042f7eae22 100644 --- a/docs/specs/ivshmem-spec.txt +++ b/docs/specs/ivshmem-spec.txt @@ -17,12 +17,16 @@ get interrupted by its peers. There are two basic configurations: -- Just shared memory: -device ivshmem-plain,memdev=HMB,... +- 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,... +- Shared memory plus interrupts: + + -device ivshmem-doorbell,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 diff --git a/docs/specs/pci-testdev.txt b/docs/specs/pci-testdev.txt index 128ae222ef..4280a1e73c 100644 --- a/docs/specs/pci-testdev.txt +++ b/docs/specs/pci-testdev.txt @@ -1,11 +1,11 @@ pci-test is a device used for testing low level IO -device implements up to two BARs: BAR0 and BAR1. -Each BAR can be memory or IO. Guests must detect -BAR type and act accordingly. +device implements up to three BARs: BAR0, BAR1 and BAR2. +Each of BAR 0+1 can be memory or IO. Guests must detect +BAR types and act accordingly. -Each BAR size is up to 4K bytes. -Each BAR starts with the following header: +BAR 0+1 size is up to 4K bytes each. +BAR 0+1 starts with the following header: typedef struct PCITestDevHdr { uint8_t test; <- write-only, starts a given test number @@ -24,3 +24,8 @@ All registers are little endian. device is expected to always implement tests 0 to N on each BAR, and to add new tests with higher numbers. In this way a guest can scan test numbers until it detects an access type that it does not support on this BAR, then stop. + +BAR2 is a 64bit memory bar, without backing storage. It is disabled +by default and can be enabled using the membar= property. This +can be used to test whether guests handle pci bars of a specific +(possibly quite large) size correctly. diff --git a/docs/specs/standard-vga.txt b/docs/specs/standard-vga.txt index 19d2a74509..18f75f1b30 100644 --- a/docs/specs/standard-vga.txt +++ b/docs/specs/standard-vga.txt @@ -61,7 +61,7 @@ MMIO area spec Likewise applies to the pci variant only for obvious reasons. -0000 - 03ff : reserved, for possible virtio extension. +0000 - 03ff : edid data blob. 0400 - 041f : vga ioports (0x3c0 -> 0x3df), remapped 1:1. word access is supported, bytes are written in little endia order (aka index port first), diff --git a/docs/specs/tpm.txt b/docs/specs/tpm.txt index 0e9bbebe1d..5d8c26b1ad 100644 --- a/docs/specs/tpm.txt +++ b/docs/specs/tpm.txt @@ -20,6 +20,40 @@ QEMU files related to TPM TIS interface: - hw/tpm/tpm_tis.h +QEMU also implements a TPM CRB interface following the Trusted Computing +Group's specification "TCG PC Client Platform TPM Profile (PTP) +Specification", Family "2.0", Level 00 Revision 01.03 v22, May 22, 2017. +This specification, or a later version of it, can be accessed from the +following URL: + +https://trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/ + +The CRB interface makes a memory mapped IO region in the area 0xfed40000 - +0xfed40fff (1 locality) available to the guest operating system. + +QEMU files related to TPM CRB interface: + - hw/tpm/tpm_crb.c + += fw_cfg interface = + +The bios/firmware may read the "etc/tpm/config" fw_cfg entry for +configuring the guest appropriately. + +The entry of 6 bytes has the following content, in little-endian: + + #define TPM_VERSION_UNSPEC 0 + #define TPM_VERSION_1_2 1 + #define TPM_VERSION_2_0 2 + + #define TPM_PPI_VERSION_NONE 0 + #define TPM_PPI_VERSION_1_30 1 + + struct FwCfgTPMConfig { + uint32_t tpmppi_address; /* PPI memory location */ + uint8_t tpm_version; /* TPM version */ + uint8_t tpmppi_version; /* PPI version */ + }; + = ACPI Interface = The TPM device is defined with ACPI ID "PNP0C31". QEMU builds a SSDT and passes @@ -42,6 +76,91 @@ URL: https://trustedcomputinggroup.org/tcg-acpi-specification/ +== ACPI PPI Interface == + +QEMU supports the Physical Presence Interface (PPI) for TPM 1.2 and TPM 2. This +interface requires ACPI and firmware support. The specification can be found at +the following URL: + +https://trustedcomputinggroup.org/resource/tcg-physical-presence-interface-specification/ + +PPI enables a system administrator (root) to request a modification to the +TPM upon reboot. The PPI specification defines the operation requests and the +actions the firmware has to take. The system administrator passes the operation +request number to the firmware through an ACPI interface which writes this +number to a memory location that the firmware knows. Upon reboot, the firmware +finds the number and sends commands to the the TPM. The firmware writes the TPM +result code and the operation request number to a memory location that ACPI can +read from and pass the result on to the administrator. + +The PPI specification defines a set of mandatory and optional operations for +the firmware to implement. The ACPI interface also allows an administrator to +list the supported operations. In QEMU the ACPI code is generated by QEMU, yet +the firmware needs to implement support on a per-operations basis, and +different firmwares may support a different subset. Therefore, QEMU introduces +the virtual memory device for PPI where the firmware can indicate which +operations it supports and ACPI can enable the ones that are supported and +disable all others. This interface lies in main memory and has the following +layout: + + +----------+--------+--------+-------------------------------------------+ + | Field | Length | Offset | Description | + +----------+--------+--------+-------------------------------------------+ + | func | 0x100 | 0x000 | Firmware sets values for each supported | + | | | | operation. See defined values below. | + +----------+--------+--------+-------------------------------------------+ + | ppin | 0x1 | 0x100 | SMI interrupt to use. Set by firmware. | + | | | | Not supported. | + +----------+--------+--------+-------------------------------------------+ + | ppip | 0x4 | 0x101 | ACPI function index to pass to SMM code. | + | | | | Set by ACPI. Not supported. | + +----------+--------+--------+-------------------------------------------+ + | pprp | 0x4 | 0x105 | Result of last executed operation. Set by | + | | | | firmware. See function index 5 for values.| + +----------+--------+--------+-------------------------------------------+ + | pprq | 0x4 | 0x109 | Operation request number to execute. See | + | | | | 'Physical Presence Interface Operation | + | | | | Summary' tables in specs. Set by ACPI. | + +----------+--------+--------+-------------------------------------------+ + | pprm | 0x4 | 0x10d | Operation request optional parameter. | + | | | | Values depend on operation. Set by ACPI. | + +----------+--------+--------+-------------------------------------------+ + | lppr | 0x4 | 0x111 | Last executed operation request number. | + | | | | Copied from pprq field by firmware. | + +----------+--------+--------+-------------------------------------------+ + | fret | 0x4 | 0x115 | Result code from SMM function. | + | | | | Not supported. | + +----------+--------+--------+-------------------------------------------+ + | res1 | 0x40 | 0x119 | Reserved for future use | + +----------+--------+--------+-------------------------------------------+ + | next_step| 0x1 | 0x159 | Operation to execute after reboot by | + | | | | firmware. Used by firmware. | + +----------+--------+--------+-------------------------------------------+ + | movv | 0x1 | 0x15a | Memory overwrite variable | + +----------+--------+--------+-------------------------------------------+ + + The following values are supported for the 'func' field. They correspond + to the values used by ACPI function index 8. + + +----------+-------------------------------------------------------------+ + | value | Description | + +----------+-------------------------------------------------------------+ + | 0 | Operation is not implemented. | + +----------+-------------------------------------------------------------+ + | 1 | Operation is only accessible through firmware. | + +----------+-------------------------------------------------------------+ + | 2 | Operation is blocked for OS by firmware configuration. | + +----------+-------------------------------------------------------------+ + | 3 | Operation is allowed and physically present user required. | + +----------+-------------------------------------------------------------+ + | 4 | Operation is allowed and physically present user is not | + | | required. | + +----------+-------------------------------------------------------------+ + +The location of the table is given by the fw_cfg tpmppi_address field. +The PPI memory region size is 0x400 (TPM_PPI_ADDR_SIZE) to leave +enough room for future updates. + QEMU files related to TPM ACPI tables: - hw/i386/acpi-build.c diff --git a/docs/usb2.txt b/docs/usb2.txt index f63c8d9465..172614d3a7 100644 --- a/docs/usb2.txt +++ b/docs/usb2.txt @@ -94,8 +94,8 @@ physical port addressing First you can (for all USB devices) specify the physical port where the device will show up in the guest. This can be done using the -"port" property. UHCI has two root ports (1,2). EHCI has four root -ports (1-4), the emulated (1.1) USB hub has eight ports. +"port" property. UHCI has two root ports (1,2). EHCI has six root +ports (1-6), the emulated (1.1) USB hub has eight ports. Plugging a tablet into UHCI port 1 works like this: diff --git a/docs/vfio-ap.txt b/docs/vfio-ap.txt new file mode 100644 index 0000000000..b1eb2deeaf --- /dev/null +++ b/docs/vfio-ap.txt @@ -0,0 +1,876 @@ +Adjunct Processor (AP) Device +============================= + +Contents: +========= +* Introduction +* AP Architectural Overview +* Start Interpretive Execution (SIE) Instruction +* AP Matrix Configuration on Linux Host +* Starting a Linux Guest Configured with an AP Matrix +* Example: Configure AP Matrices for Three Linux Guests + +Introduction: +============ +The IBM Adjunct Processor (AP) Cryptographic Facility is comprised +of three AP instructions and from 1 to 256 PCIe cryptographic adapter cards. +These AP devices provide cryptographic functions to all CPUs assigned to a +linux system running in an IBM Z system LPAR. + +On s390x, AP adapter cards are exposed via the AP bus. This document +describes how those cards may be made available to KVM guests using the +VFIO mediated device framework. + +AP Architectural Overview: +========================= +In order understand the terminology used in the rest of this document, let's +start with some definitions: + +* AP adapter + + An AP adapter is an IBM Z adapter card that can perform cryptographic + functions. There can be from 0 to 256 adapters assigned to an LPAR depending + on the machine model. Adapters assigned to the LPAR in which a linux host is + running will be available to the linux host. Each adapter is identified by a + number from 0 to 255; however, the maximum adapter number allowed is + determined by machine model. When installed, an AP adapter is accessed by + AP instructions executed by any CPU. + +* AP domain + + An adapter is partitioned into domains. Each domain can be thought of as + a set of hardware registers for processing AP instructions. An adapter can + hold up to 256 domains; however, the maximum domain number allowed is + determined by machine model. Each domain is identified by a number from 0 to + 255. Domains can be further classified into two types: + + * Usage domains are domains that can be accessed directly to process AP + commands + + * Control domains are domains that are accessed indirectly by AP + commands sent to a usage domain to control or change the domain; for + example, to set a secure private key for the domain. + +* AP Queue + + An AP queue is the means by which an AP command-request message is sent to an + AP usage domain inside a specific AP. An AP queue is identified by a tuple + comprised of an AP adapter ID (APID) and an AP queue index (APQI). The + APQI corresponds to a given usage domain number within the adapter. This tuple + forms an AP Queue Number (APQN) uniquely identifying an AP queue. AP + instructions include a field containing the APQN to identify the AP queue to + which the AP command-request message is to be sent for processing. + +* AP Instructions: + + There are three AP instructions: + + * NQAP: to enqueue an AP command-request message to a queue + * DQAP: to dequeue an AP command-reply message from a queue + * PQAP: to administer the queues + + AP instructions identify the domain that is targeted to process the AP + command; this must be one of the usage domains. An AP command may modify a + domain that is not one of the usage domains, but the modified domain + must be one of the control domains. + +Start Interpretive Execution (SIE) Instruction +============================================== +A KVM guest is started by executing the Start Interpretive Execution (SIE) +instruction. The SIE state description is a control block that contains the +state information for a KVM guest and is supplied as input to the SIE +instruction. The SIE state description contains a satellite control block called +the Crypto Control Block (CRYCB). The CRYCB contains three fields to identify +the adapters, usage domains and control domains assigned to the KVM guest: + +* The AP Mask (APM) field is a bit mask that identifies the AP adapters assigned + to the KVM guest. Each bit in the mask, from left to right, corresponds to + an APID from 0-255. If a bit is set, the corresponding adapter is valid for + use by the KVM guest. + +* The AP Queue Mask (AQM) field is a bit mask identifying the AP usage domains + assigned to the KVM guest. Each bit in the mask, from left to right, + corresponds to an AP queue index (APQI) from 0-255. If a bit is set, the + corresponding queue is valid for use by the KVM guest. + +* The AP Domain Mask field is a bit mask that identifies the AP control domains + assigned to the KVM guest. The ADM bit mask controls which domains can be + changed by an AP command-request message sent to a usage domain from the + guest. Each bit in the mask, from left to right, corresponds to a domain from + 0-255. If a bit is set, the corresponding domain can be modified by an AP + command-request message sent to a usage domain. + +If you recall from the description of an AP Queue, AP instructions include +an APQN to identify the AP adapter and AP queue to which an AP command-request +message is to be sent (NQAP and PQAP instructions), or from which a +command-reply message is to be received (DQAP instruction). The validity of an +APQN is defined by the matrix calculated from the APM and AQM; it is the +cross product of all assigned adapter numbers (APM) with all assigned queue +indexes (AQM). For example, if adapters 1 and 2 and usage domains 5 and 6 are +assigned to a guest, the APQNs (1,5), (1,6), (2,5) and (2,6) will be valid for +the guest. + +The APQNs can provide secure key functionality - i.e., a private key is stored +on the adapter card for each of its domains - so each APQN must be assigned to +at most one guest or the linux host. + + Example 1: Valid configuration: + ------------------------------ + Guest1: adapters 1,2 domains 5,6 + Guest2: adapter 1,2 domain 7 + + This is valid because both guests have a unique set of APQNs: Guest1 has + APQNs (1,5), (1,6), (2,5) and (2,6); Guest2 has APQNs (1,7) and (2,7). + + Example 2: Valid configuration: + ------------------------------ + Guest1: adapters 1,2 domains 5,6 + Guest2: adapters 3,4 domains 5,6 + + This is also valid because both guests have a unique set of APQNs: + Guest1 has APQNs (1,5), (1,6), (2,5), (2,6); + Guest2 has APQNs (3,5), (3,6), (4,5), (4,6) + + Example 3: Invalid configuration: + -------------------------------- + Guest1: adapters 1,2 domains 5,6 + Guest2: adapter 1 domains 6,7 + + This is an invalid configuration because both guests have access to + APQN (1,6). + +AP Matrix Configuration on Linux Host: +===================================== +A linux system is a guest of the LPAR in which it is running and has access to +the AP resources configured for the LPAR. The LPAR's AP matrix is +configured via its Activation Profile which can be edited on the HMC. When the +linux system is started, the AP bus will detect the AP devices assigned to the +LPAR and create the following in sysfs: + +/sys/bus/ap +... [devices] +...... xx.yyyy +...... ... +...... cardxx +...... ... + +Where: + cardxx is AP adapter number xx (in hex) +....xx.yyyy is an APQN with xx specifying the APID and yyyy specifying the + APQI + +For example, if AP adapters 5 and 6 and domains 4, 71 (0x47), 171 (0xab) and +255 (0xff) are configured for the LPAR, the sysfs representation on the linux +host system would look like this: + +/sys/bus/ap +... [devices] +...... 05.0004 +...... 05.0047 +...... 05.00ab +...... 05.00ff +...... 06.0004 +...... 06.0047 +...... 06.00ab +...... 06.00ff +...... card05 +...... card06 + +A set of default device drivers are also created to control each type of AP +device that can be assigned to the LPAR on which a linux host is running: + +/sys/bus/ap +... [drivers] +...... [cex2acard] for Crypto Express 2/3 accelerator cards +...... [cex2aqueue] for AP queues served by Crypto Express 2/3 + accelerator cards +...... [cex4card] for Crypto Express 4/5/6 accelerator and coprocessor + cards +...... [cex4queue] for AP queues served by Crypto Express 4/5/6 + accelerator and coprocessor cards +...... [pcixcccard] for Crypto Express 2/3 coprocessor cards +...... [pcixccqueue] for AP queues served by Crypto Express 2/3 + coprocessor cards + +Binding AP devices to device drivers +------------------------------------ +There are two sysfs files that specify bitmasks marking a subset of the APQN +range as 'usable by the default AP queue device drivers' or 'not usable by the +default device drivers' and thus available for use by the alternate device +driver(s). The sysfs locations of the masks are: + + /sys/bus/ap/apmask + /sys/bus/ap/aqmask + + The 'apmask' is a 256-bit mask that identifies a set of AP adapter IDs + (APID). Each bit in the mask, from left to right (i.e., from most significant + to least significant bit in big endian order), corresponds to an APID from + 0-255. If a bit is set, the APID is marked as usable only by the default AP + queue device drivers; otherwise, the APID is usable by the vfio_ap + device driver. + + The 'aqmask' is a 256-bit mask that identifies a set of AP queue indexes + (APQI). Each bit in the mask, from left to right (i.e., from most significant + to least significant bit in big endian order), corresponds to an APQI from + 0-255. If a bit is set, the APQI is marked as usable only by the default AP + queue device drivers; otherwise, the APQI is usable by the vfio_ap device + driver. + + Take, for example, the following mask: + + 0x7dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + + It indicates: + + 1, 2, 3, 4, 5, and 7-255 belong to the default drivers' pool, and 0 and 6 + belong to the vfio_ap device driver's pool. + + The APQN of each AP queue device assigned to the linux host is checked by the + AP bus against the set of APQNs derived from the cross product of APIDs + and APQIs marked as usable only by the default AP queue device drivers. If a + match is detected, only the default AP queue device drivers will be probed; + otherwise, the vfio_ap device driver will be probed. + + By default, the two masks are set to reserve all APQNs for use by the default + AP queue device drivers. There are two ways the default masks can be changed: + + 1. The sysfs mask files can be edited by echoing a string into the + respective sysfs mask file in one of two formats: + + * An absolute hex string starting with 0x - like "0x12345678" - sets + the mask. If the given string is shorter than the mask, it is padded + with 0s on the right; for example, specifying a mask value of 0x41 is + the same as specifying: + + 0x4100000000000000000000000000000000000000000000000000000000000000 + + Keep in mind that the mask reads from left to right (i.e., most + significant to least significant bit in big endian order), so the mask + above identifies device numbers 1 and 7 (01000001). + + If the string is longer than the mask, the operation is terminated with + an error (EINVAL). + + * Individual bits in the mask can be switched on and off by specifying + each bit number to be switched in a comma separated list. Each bit + number string must be prepended with a ('+') or minus ('-') to indicate + the corresponding bit is to be switched on ('+') or off ('-'). Some + valid values are: + + "+0" switches bit 0 on + "-13" switches bit 13 off + "+0x41" switches bit 65 on + "-0xff" switches bit 255 off + + The following example: + +0,-6,+0x47,-0xf0 + + Switches bits 0 and 71 (0x47) on + Switches bits 6 and 240 (0xf0) off + + Note that the bits not specified in the list remain as they were before + the operation. + + 2. The masks can also be changed at boot time via parameters on the kernel + command line like this: + + ap.apmask=0xffff ap.aqmask=0x40 + + This would create the following masks: + + apmask: + 0xffff000000000000000000000000000000000000000000000000000000000000 + + aqmask: + 0x4000000000000000000000000000000000000000000000000000000000000000 + + Resulting in these two pools: + + default drivers pool: adapter 0-15, domain 1 + alternate drivers pool: adapter 16-255, domains 0, 2-255 + +Configuring an AP matrix for a linux guest. +------------------------------------------ +The sysfs interfaces for configuring an AP matrix for a guest are built on the +VFIO mediated device framework. To configure an AP matrix for a guest, a +mediated matrix device must first be created for the /sys/devices/vfio_ap/matrix +device. When the vfio_ap device driver is loaded, it registers with the VFIO +mediated device framework. When the driver registers, the sysfs interfaces for +creating mediated matrix devices is created: + +/sys/devices +... [vfio_ap] +......[matrix] +......... [mdev_supported_types] +............ [vfio_ap-passthrough] +............... create +............... [devices] + +A mediated AP matrix device is created by writing a UUID to the attribute file +named 'create', for example: + + uuidgen > create + + or + + echo $uuid > create + +When a mediated AP matrix device is created, a sysfs directory named after +the UUID is created in the 'devices' subdirectory: + +/sys/devices +... [vfio_ap] +......[matrix] +......... [mdev_supported_types] +............ [vfio_ap-passthrough] +............... create +............... [devices] +.................. [$uuid] + +There will also be three sets of attribute files created in the mediated +matrix device's sysfs directory to configure an AP matrix for the +KVM guest: + +/sys/devices +... [vfio_ap] +......[matrix] +......... [mdev_supported_types] +............ [vfio_ap-passthrough] +............... create +............... [devices] +.................. [$uuid] +..................... assign_adapter +..................... assign_control_domain +..................... assign_domain +..................... matrix +..................... unassign_adapter +..................... unassign_control_domain +..................... unassign_domain + +assign_adapter + To assign an AP adapter to the mediated matrix device, its APID is written + to the 'assign_adapter' file. This may be done multiple times to assign more + than one adapter. The APID may be specified using conventional semantics + as a decimal, hexadecimal, or octal number. For example, to assign adapters + 4, 5 and 16 to a mediated matrix device in decimal, hexadecimal and octal + respectively: + + echo 4 > assign_adapter + echo 0x5 > assign_adapter + echo 020 > assign_adapter + + In order to successfully assign an adapter: + + * The adapter number specified must represent a value from 0 up to the + maximum adapter number allowed by the machine model. If an adapter number + higher than the maximum is specified, the operation will terminate with + an error (ENODEV). + + * All APQNs that can be derived from the adapter ID being assigned and the + IDs of the previously assigned domains must be bound to the vfio_ap device + driver. If no domains have yet been assigned, then there must be at least + one APQN with the specified APID bound to the vfio_ap driver. If no such + APQNs are bound to the driver, the operation will terminate with an + error (EADDRNOTAVAIL). + + No APQN that can be derived from the adapter ID and the IDs of the + previously assigned domains can be assigned to another mediated matrix + device. If an APQN is assigned to another mediated matrix device, the + operation will terminate with an error (EADDRINUSE). + +unassign_adapter + To unassign an AP adapter, its APID is written to the 'unassign_adapter' + file. This may also be done multiple times to unassign more than one adapter. + +assign_domain + To assign a usage domain, the domain number is written into the + 'assign_domain' file. This may be done multiple times to assign more than one + usage domain. The domain number is specified using conventional semantics as + a decimal, hexadecimal, or octal number. For example, to assign usage domains + 4, 8, and 71 to a mediated matrix device in decimal, hexadecimal and octal + respectively: + + echo 4 > assign_domain + echo 0x8 > assign_domain + echo 0107 > assign_domain + + In order to successfully assign a domain: + + * The domain number specified must represent a value from 0 up to the + maximum domain number allowed by the machine model. If a domain number + higher than the maximum is specified, the operation will terminate with + an error (ENODEV). + + * All APQNs that can be derived from the domain ID being assigned and the IDs + of the previously assigned adapters must be bound to the vfio_ap device + driver. If no domains have yet been assigned, then there must be at least + one APQN with the specified APQI bound to the vfio_ap driver. If no such + APQNs are bound to the driver, the operation will terminate with an + error (EADDRNOTAVAIL). + + No APQN that can be derived from the domain ID being assigned and the IDs + of the previously assigned adapters can be assigned to another mediated + matrix device. If an APQN is assigned to another mediated matrix device, + the operation will terminate with an error (EADDRINUSE). + +unassign_domain + To unassign a usage domain, the domain number is written into the + 'unassign_domain' file. This may be done multiple times to unassign more than + one usage domain. + +assign_control_domain + To assign a control domain, the domain number is written into the + 'assign_control_domain' file. This may be done multiple times to + assign more than one control domain. The domain number may be specified using + conventional semantics as a decimal, hexadecimal, or octal number. For + example, to assign control domains 4, 8, and 71 to a mediated matrix device + in decimal, hexadecimal and octal respectively: + + echo 4 > assign_domain + echo 0x8 > assign_domain + echo 0107 > assign_domain + + In order to successfully assign a control domain, the domain number + specified must represent a value from 0 up to the maximum domain number + allowed by the machine model. If a control domain number higher than the + maximum is specified, the operation will terminate with an error (ENODEV). + +unassign_control_domain + To unassign a control domain, the domain number is written into the + 'unassign_domain' file. This may be done multiple times to unassign more than + one control domain. + +Notes: No changes to the AP matrix will be allowed while a guest using +the mediated matrix device is running. Attempts to assign an adapter, +domain or control domain will be rejected and an error (EBUSY) returned. + +Starting a Linux Guest Configured with an AP Matrix: +=================================================== +To provide a mediated matrix device for use by a guest, the following option +must be specified on the QEMU command line: + + -device vfio_ap,sysfsdev=$path-to-mdev + +The sysfsdev parameter specifies the path to the mediated matrix device. +There are a number of ways to specify this path: + +/sys/devices/vfio_ap/matrix/$uuid +/sys/bus/mdev/devices/$uuid +/sys/bus/mdev/drivers/vfio_mdev/$uuid +/sys/devices/vfio_ap/matrix/mdev_supported_types/vfio_ap-passthrough/devices/$uuid + +When the linux guest is started, the guest will open the mediated +matrix device's file descriptor to get information about the mediated matrix +device. The vfio_ap device driver will update the APM, AQM, and ADM fields in +the guest's CRYCB with the adapter, usage domain and control domains assigned +via the mediated matrix device's sysfs attribute files. Programs running on the +linux guest will then: + +1. Have direct access to the APQNs derived from the cross product of the AP + adapter numbers (APID) and queue indexes (APQI) specified in the APM and AQM + fields of the guests's CRYCB respectively. These APQNs identify the AP queues + that are valid for use by the guest; meaning, AP commands can be sent by the + guest to any of these queues for processing. + +2. Have authorization to process AP commands to change a control domain + identified in the ADM field of the guest's CRYCB. The AP command must be sent + to a valid APQN (see 1 above). + +CPU model features: + +Three CPU model features are available for controlling guest access to AP +facilities: + +1. AP facilities feature + + The AP facilities feature indicates that AP facilities are installed on the + guest. This feature will be exposed for use only if the AP facilities + are installed on the host system. The feature is s390-specific and is + represented as a parameter of the -cpu option on the QEMU command line: + + qemu-system-s390x -cpu $model,ap=on|off + + Where: + + $model is the CPU model defined for the guest (defaults to the model of + the host system if not specified). + + ap=on|off indicates whether AP facilities are installed (on) or not + (off). The default for CPU models zEC12 or newer + is ap=on. AP facilities must be installed on the guest if a + vfio-ap device (-device vfio-ap,sysfsdev=$path) is configured + for the guest, or the guest will fail to start. + +2. Query Configuration Information (QCI) facility + + The QCI facility is used by the AP bus running on the guest to query the + configuration of the AP facilities. This facility will be available + only if the QCI facility is installed on the host system. The feature is + s390-specific and is represented as a parameter of the -cpu option on the + QEMU command line: + + qemu-system-s390x -cpu $model,apqci=on|off + + Where: + + $model is the CPU model defined for the guest + + apqci=on|off indicates whether the QCI facility is installed (on) or + not (off). The default for CPU models zEC12 or newer + is apqci=on; for older models, QCI will not be installed. + + If QCI is installed (apqci=on) but AP facilities are not + (ap=off), an error message will be logged, but the guest + will be allowed to start. It makes no sense to have QCI + installed if the AP facilities are not; this is considered + an invalid configuration. + + If the QCI facility is not installed, APQNs with an APQI + greater than 15 will not be detected by the AP bus + running on the guest. + +3. Adjunct Process Facility Test (APFT) facility + + The APFT facility is used by the AP bus running on the guest to test the + AP facilities available for a given AP queue. This facility will be available + only if the APFT facility is installed on the host system. The feature is + s390-specific and is represented as a parameter of the -cpu option on the + QEMU command line: + + qemu-system-s390x -cpu $model,apft=on|off + + Where: + + $model is the CPU model defined for the guest (defaults to the model of + the host system if not specified). + + apft=on|off indicates whether the APFT facility is installed (on) or + not (off). The default for CPU models zEC12 and + newer is apft=on for older models, APFT will not be + installed. + + If APFT is installed (apft=on) but AP facilities are not + (ap=off), an error message will be logged, but the guest + will be allowed to start. It makes no sense to have APFT + installed if the AP facilities are not; this is considered + an invalid configuration. + + It also makes no sense to turn APFT off because the AP bus + running on the guest will not detect CEX4 and newer devices + without it. Since only CEX4 and newer devices are supported + for guest usage, no AP devices can be made accessible to a + guest started without APFT installed. + +Hot plug a vfio-ap device into a running guest: +============================================== +Only one vfio-ap device can be attached to the virtual machine's ap-bus, so a +vfio-ap device can be hot plugged if and only if no vfio-ap device is attached +to the bus already, whether via the QEMU command line or a prior hot plug +action. + +To hot plug a vfio-ap device, use the QEMU device_add command: + + (qemu) device_add vfio-ap,sysfsdev="$path-to-mdev" + + Where the '$path-to-mdev' value specifies the absolute path to a mediated + device to which AP resources to be used by the guest have been assigned. + +Note that on Linux guests, the AP devices will be created in the +/sys/bus/ap/devices directory when the AP bus subsequently performs its periodic +scan, so there may be a short delay before the AP devices are accessible on the +guest. + +The command will fail if: + +* A vfio-ap device has already been attached to the virtual machine's ap-bus. + +* The CPU model features for controlling guest access to AP facilities are not + enabled (see 'CPU model features' subsection in the previous section). + +Hot unplug a vfio-ap device from a running guest: +================================================ +A vfio-ap device can be unplugged from a running KVM guest if a vfio-ap device +has been attached to the virtual machine's ap-bus via the QEMU command line +or a prior hot plug action. + +To hot unplug a vfio-ap device, use the QEMU device_del command: + + (qemu) device_del vfio-ap,sysfsdev="$path-to-mdev" + + Where $path-to-mdev is the same as the path specified when the vfio-ap + device was attached to the virtual machine's ap-bus. + +On a Linux guest, the AP devices will be removed from the /sys/bus/ap/devices +directory on the guest when the AP bus subsequently performs its periodic scan, +so there may be a short delay before the AP devices are no longer accessible by +the guest. + +The command will fail if the $path-to-mdev specified on the device_del command +does not match the value specified when the vfio-ap device was attached to +the virtual machine's ap-bus. + +Example: Configure AP Matrixes for Three Linux Guests: +===================================================== +Let's now provide an example to illustrate how KVM guests may be given +access to AP facilities. For this example, we will show how to configure +three guests such that executing the lszcrypt command on the guests would +look like this: + +Guest1 +------ +CARD.DOMAIN TYPE MODE +------------------------------ +05 CEX5C CCA-Coproc +05.0004 CEX5C CCA-Coproc +05.00ab CEX5C CCA-Coproc +06 CEX5A Accelerator +06.0004 CEX5A Accelerator +06.00ab CEX5C CCA-Coproc + +Guest2 +------ +CARD.DOMAIN TYPE MODE +------------------------------ +05 CEX5A Accelerator +05.0047 CEX5A Accelerator +05.00ff CEX5A Accelerator (5,4), (5,171), (6,4), (6,171), + +Guest3 +------ +CARD.DOMAIN TYPE MODE +------------------------------ +06 CEX5A Accelerator +06.0047 CEX5A Accelerator +06.00ff CEX5A Accelerator + +These are the steps: + +1. Install the vfio_ap module on the linux host. The dependency chain for the + vfio_ap module is: + * iommu + * s390 + * zcrypt + * vfio + * vfio_mdev + * vfio_mdev_device + * KVM + + To build the vfio_ap module, the kernel build must be configured with the + following Kconfig elements selected: + * IOMMU_SUPPORT + * S390 + * ZCRYPT + * S390_AP_IOMMU + * VFIO + * VFIO_MDEV + * VFIO_MDEV_DEVICE + * KVM + + If using make menuconfig select the following to build the vfio_ap module: + -> Device Drivers + -> IOMMU Hardware Support + select S390 AP IOMMU Support + -> VFIO Non-Privileged userspace driver framework + -> Mediated device driver framework + -> VFIO driver for Mediated devices + -> I/O subsystem + -> VFIO support for AP devices + +2. Secure the AP queues to be used by the three guests so that the host can not + access them. To secure the AP queues 05.0004, 05.0047, 05.00ab, 05.00ff, + 06.0004, 06.0047, 06.00ab, and 06.00ff for use by the vfio_ap device driver, + the corresponding APQNs must be removed from the default queue drivers pool + as follows: + + echo -5,-6 > /sys/bus/ap/apmask + + echo -4,-0x47,-0xab,-0xff > /sys/bus/ap/aqmask + + This will result in AP queues 05.0004, 05.0047, 05.00ab, 05.00ff, 06.0004, + 06.0047, 06.00ab, and 06.00ff getting bound to the vfio_ap device driver. The + sysfs directory for the vfio_ap device driver will now contain symbolic links + to the AP queue devices bound to it: + + /sys/bus/ap + ... [drivers] + ...... [vfio_ap] + ......... [05.0004] + ......... [05.0047] + ......... [05.00ab] + ......... [05.00ff] + ......... [06.0004] + ......... [06.0047] + ......... [06.00ab] + ......... [06.00ff] + + Keep in mind that only type 10 and newer adapters (i.e., CEX4 and later) + can be bound to the vfio_ap device driver. The reason for this is to + simplify the implementation by not needlessly complicating the design by + supporting older devices that will go out of service in the relatively near + future, and for which there are few older systems on which to test. + + The administrator, therefore, must take care to secure only AP queues that + can be bound to the vfio_ap device driver. The device type for a given AP + queue device can be read from the parent card's sysfs directory. For example, + to see the hardware type of the queue 05.0004: + + cat /sys/bus/ap/devices/card05/hwtype + + The hwtype must be 10 or higher (CEX4 or newer) in order to be bound to the + vfio_ap device driver. + +3. Create the mediated devices needed to configure the AP matrixes for the + three guests and to provide an interface to the vfio_ap driver for + use by the guests: + + /sys/devices/vfio_ap/matrix/ + --- [mdev_supported_types] + ------ [vfio_ap-passthrough] (passthrough mediated matrix device type) + --------- create + --------- [devices] + + To create the mediated devices for the three guests: + + uuidgen > create + uuidgen > create + uuidgen > create + + or + + echo $uuid1 > create + echo $uuid2 > create + echo $uuid3 > create + + This will create three mediated devices in the [devices] subdirectory named + after the UUID used to create the mediated device. We'll call them $uuid1, + $uuid2 and $uuid3 and this is the sysfs directory structure after creation: + + /sys/devices/vfio_ap/matrix/ + --- [mdev_supported_types] + ------ [vfio_ap-passthrough] + --------- [devices] + ------------ [$uuid1] + --------------- assign_adapter + --------------- assign_control_domain + --------------- assign_domain + --------------- matrix + --------------- unassign_adapter + --------------- unassign_control_domain + --------------- unassign_domain + + ------------ [$uuid2] + --------------- assign_adapter + --------------- assign_control_domain + --------------- assign_domain + --------------- matrix + --------------- unassign_adapter + ----------------unassign_control_domain + ----------------unassign_domain + + ------------ [$uuid3] + --------------- assign_adapter + --------------- assign_control_domain + --------------- assign_domain + --------------- matrix + --------------- unassign_adapter + ----------------unassign_control_domain + ----------------unassign_domain + +4. The administrator now needs to configure the matrixes for the mediated + devices $uuid1 (for Guest1), $uuid2 (for Guest2) and $uuid3 (for Guest3). + + This is how the matrix is configured for Guest1: + + echo 5 > assign_adapter + echo 6 > assign_adapter + echo 4 > assign_domain + echo 0xab > assign_domain + + Control domains can similarly be assigned using the assign_control_domain + sysfs file. + + If a mistake is made configuring an adapter, domain or control domain, + you can use the unassign_xxx interfaces to unassign the adapter, domain or + control domain. + + To display the matrix configuration for Guest1: + + cat matrix + + The output will display the APQNs in the format xx.yyyy, where xx is + the adapter number and yyyy is the domain number. The output for Guest1 + will look like this: + + 05.0004 + 05.00ab + 06.0004 + 06.00ab + + This is how the matrix is configured for Guest2: + + echo 5 > assign_adapter + echo 0x47 > assign_domain + echo 0xff > assign_domain + + This is how the matrix is configured for Guest3: + + echo 6 > assign_adapter + echo 0x47 > assign_domain + echo 0xff > assign_domain + +5. Start Guest1: + + /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on \ + -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid1 ... + +7. Start Guest2: + + /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on \ + -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid2 ... + +7. Start Guest3: + + /usr/bin/qemu-system-s390x ... -cpu host,ap=on,apqci=on,apft=on \ + -device vfio-ap,sysfsdev=/sys/devices/vfio_ap/matrix/$uuid3 ... + +When the guest is shut down, the mediated matrix devices may be removed. + +Using our example again, to remove the mediated matrix device $uuid1: + + /sys/devices/vfio_ap/matrix/ + --- [mdev_supported_types] + ------ [vfio_ap-passthrough] + --------- [devices] + ------------ [$uuid1] + --------------- remove + + + echo 1 > remove + + This will remove all of the mdev matrix device's sysfs structures including + the mdev device itself. To recreate and reconfigure the mdev matrix device, + all of the steps starting with step 3 will have to be performed again. Note + that the remove will fail if a guest using the mdev is still running. + + It is not necessary to remove an mdev matrix device, but one may want to + remove it if no guest will use it during the remaining lifetime of the linux + host. If the mdev matrix device is removed, one may want to also reconfigure + the pool of adapters and queues reserved for use by the default drivers. + +Limitations +=========== +* The KVM/kernel interfaces do not provide a way to prevent restoring an APQN + to the default drivers pool of a queue that is still assigned to a mediated + device in use by a guest. It is incumbent upon the administrator to + ensure there is no mediated device in use by a guest to which the APQN is + assigned lest the host be given access to the private data of the AP queue + device, such as a private key configured specifically for the guest. + +* Dynamically assigning AP resources to or unassigning AP resources from a + mediated matrix device - see 'Configuring an AP matrix for a linux guest' + section above - while a running guest is using it is currently not supported. + +* Live guest migration is not supported for guests using AP devices. If a guest + is using AP devices, the vfio-ap device configured for the guest must be + unplugged before migrating the guest (see 'Hot unplug a vfio-ap device from a + running guest' section above. diff --git a/docs/virtio-balloon-stats.txt b/docs/virtio-balloon-stats.txt index 9985e1dffc..1732cc8c8a 100644 --- a/docs/virtio-balloon-stats.txt +++ b/docs/virtio-balloon-stats.txt @@ -61,9 +61,9 @@ It's also important to note the following: respond to the request the timer will never be re-armed, which has the same effect as disabling polling -Here are a few examples. QEMU is started with '-balloon virtio', which -generates '/machine/peripheral-anon/device[1]' as the QOM path for the -balloon device. +Here are a few examples. QEMU is started with '-device virtio-balloon', +which generates '/machine/peripheral-anon/device[1]' as the QOM path for +the balloon device. Enable polling with 2 seconds interval: diff --git a/dtc b/dtc index e54388015a..88f18909db 160000 --- a/dtc +++ b/dtc @@ -1 +1 @@ -Subproject commit e54388015af1fb4bf04d0bca99caba1074d9cc42 +Subproject commit 88f18909db731a627456f26d779445f84e449536 diff --git a/dump.c b/dump.c index 04467b353e..107a67165a 100644 --- a/dump.c +++ b/dump.c @@ -192,7 +192,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump64(s, filesz); phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length); - phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr); + phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr) ?: phdr.p_paddr; assert(memory_mapping->length >= filesz); @@ -216,7 +216,8 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump32(s, filesz); phdr.p_memsz = cpu_to_dump32(s, memory_mapping->length); - phdr.p_vaddr = cpu_to_dump32(s, memory_mapping->virt_addr); + phdr.p_vaddr = + cpu_to_dump32(s, memory_mapping->virt_addr) ?: phdr.p_paddr; assert(memory_mapping->length >= filesz); @@ -1557,7 +1558,7 @@ static void get_max_mapnr(DumpState *s) { GuestPhysBlock *last_block; - last_block = QTAILQ_LAST(&s->guest_phys_blocks.head, GuestPhysBlockHead); + last_block = QTAILQ_LAST(&s->guest_phys_blocks.head); s->max_mapnr = dump_paddr_to_pfn(s, last_block->target_end); } @@ -1742,7 +1743,7 @@ static void dump_init(DumpState *s, int fd, bool has_format, warn_report("guest note is not present"); } else if (size < note_head_size || size > MAX_GUEST_NOTE_SIZE) { warn_report("guest note size is invalid: %" PRIu32, size); - } else if (format != VMCOREINFO_FORMAT_ELF) { + } else if (format != FW_CFG_VMCOREINFO_FORMAT_ELF) { warn_report("guest note format is unsupported: %" PRIu16, format); } else { s->guest_note = g_malloc(size + 1); /* +1 for adding \0 */ @@ -1890,8 +1891,7 @@ static void dump_process(DumpState *s, Error **errp) /* should never fail */ assert(result); qapi_event_send_dump_completed(result, !!local_err, (local_err ? \ - error_get_pretty(local_err) : NULL), - &error_abort); + error_get_pretty(local_err) : NULL)); qapi_free_DumpQueryResult(result); error_propagate(errp, local_err); diff --git a/exec.c b/exec.c index 4f5df07b6a..6ab62f4eee 100644 --- a/exec.c +++ b/exec.c @@ -87,26 +87,6 @@ AddressSpace address_space_memory; MemoryRegion io_mem_rom, io_mem_notdirty; static MemoryRegion io_mem_unassigned; - -/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */ -#define RAM_PREALLOC (1 << 0) - -/* RAM is mmap-ed with MAP_SHARED */ -#define RAM_SHARED (1 << 1) - -/* Only a portion of RAM (used_length) is actually used, and migrated. - * This used_length size can change across reboots. - */ -#define RAM_RESIZEABLE (1 << 2) - -/* UFFDIO_ZEROPAGE is available on this RAMBlock to atomically - * zero the page and wake waiting processes. - * (Set during postcopy) - */ -#define RAM_UF_ZEROPAGE (1 << 3) - -/* RAM can be migrated */ -#define RAM_MIGRATABLE (1 << 4) #endif #ifdef TARGET_PAGE_BITS_VARY @@ -114,7 +94,8 @@ int target_page_bits; bool target_page_bits_decided; #endif -struct CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); +CPUTailQ cpus = QTAILQ_HEAD_INITIALIZER(cpus); + /* current CPU in the current thread. It is only valid inside cpu_exec() */ __thread CPUState *current_cpu; @@ -402,12 +383,6 @@ static MemoryRegionSection *phys_page_find(AddressSpaceDispatch *d, hwaddr addr) } } -bool memory_region_is_unassigned(MemoryRegion *mr) -{ - return mr != &io_mem_rom && mr != &io_mem_notdirty && !mr->rom_device - && mr != &io_mem_watch; -} - /* Called from RCU critical section */ static MemoryRegionSection *address_space_lookup_region(AddressSpaceDispatch *d, hwaddr addr, @@ -690,7 +665,7 @@ static void tcg_register_iommu_notifier(CPUState *cpu, int i; for (i = 0; i < cpu->iommu_notifiers->len; i++) { - notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i); if (notifier->mr == mr && notifier->iommu_idx == iommu_idx) { break; } @@ -698,7 +673,8 @@ static void tcg_register_iommu_notifier(CPUState *cpu, if (i == cpu->iommu_notifiers->len) { /* Not found, add a new entry at the end of the array */ cpu->iommu_notifiers = g_array_set_size(cpu->iommu_notifiers, i + 1); - notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + notifier = g_new0(TCGIOMMUNotifier, 1); + g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i) = notifier; notifier->mr = mr; notifier->iommu_idx = iommu_idx; @@ -730,8 +706,9 @@ static void tcg_iommu_free_notifier_list(CPUState *cpu) TCGIOMMUNotifier *notifier; for (i = 0; i < cpu->iommu_notifiers->len; i++) { - notifier = &g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier, i); + notifier = g_array_index(cpu->iommu_notifiers, TCGIOMMUNotifier *, i); memory_region_unregister_iommu_notifier(notifier->mr, ¬ifier->n); + g_free(notifier); } g_array_free(cpu->iommu_notifiers, true); } @@ -991,6 +968,7 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) tcg_target_initialized = true; cc->tcg_initialize(); } + tlb_init(cpu); #ifndef CONFIG_USER_ONLY if (qdev_get_vmsd(DEVICE(cpu)) == NULL) { @@ -1000,7 +978,7 @@ void cpu_exec_realizefn(CPUState *cpu, Error **errp) vmstate_register(NULL, cpu->cpu_index, cc->vmsd, cpu); } - cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier)); + cpu->iommu_notifiers = g_array_new(false, true, sizeof(TCGIOMMUNotifier *)); #endif } @@ -1621,35 +1599,49 @@ static void register_multipage(FlatView *fv, phys_page_set(d, start_addr >> TARGET_PAGE_BITS, num_pages, section_index); } +/* + * The range in *section* may look like this: + * + * |s|PPPPPPP|s| + * + * where s stands for subpage and P for page. + */ void flatview_add_to_dispatch(FlatView *fv, MemoryRegionSection *section) { - MemoryRegionSection now = *section, remain = *section; + MemoryRegionSection remain = *section; Int128 page_size = int128_make64(TARGET_PAGE_SIZE); - if (now.offset_within_address_space & ~TARGET_PAGE_MASK) { - uint64_t left = TARGET_PAGE_ALIGN(now.offset_within_address_space) - - now.offset_within_address_space; + /* register first subpage */ + if (remain.offset_within_address_space & ~TARGET_PAGE_MASK) { + uint64_t left = TARGET_PAGE_ALIGN(remain.offset_within_address_space) + - remain.offset_within_address_space; + MemoryRegionSection now = remain; now.size = int128_min(int128_make64(left), now.size); register_subpage(fv, &now); - } else { - now.size = int128_zero(); - } - while (int128_ne(remain.size, now.size)) { + if (int128_eq(remain.size, now.size)) { + return; + } remain.size = int128_sub(remain.size, now.size); remain.offset_within_address_space += int128_get64(now.size); remain.offset_within_region += int128_get64(now.size); - now = remain; - if (int128_lt(remain.size, page_size)) { - register_subpage(fv, &now); - } else if (remain.offset_within_address_space & ~TARGET_PAGE_MASK) { - now.size = page_size; - register_subpage(fv, &now); - } else { - now.size = int128_and(now.size, int128_neg(page_size)); - register_multipage(fv, &now); - } } + + /* register whole pages */ + if (int128_ge(remain.size, page_size)) { + MemoryRegionSection now = remain; + now.size = int128_and(now.size, int128_neg(page_size)); + register_multipage(fv, &now); + if (int128_eq(remain.size, now.size)) { + return; + } + remain.size = int128_sub(remain.size, now.size); + remain.offset_within_address_space += int128_get64(now.size); + remain.offset_within_region += int128_get64(now.size); + } + + /* register last subpage */ + register_subpage(fv, &remain); } void qemu_flush_coalesced_mmio_buffer(void) @@ -1700,9 +1692,10 @@ static int find_max_supported_pagesize(Object *obj, void *opaque) long *hpsize_min = opaque; if (object_dynamic_cast(obj, TYPE_MEMORY_BACKEND)) { - long hpsize = host_memory_backend_pagesize(MEMORY_BACKEND(obj)); + HostMemoryBackend *backend = MEMORY_BACKEND(obj); + long hpsize = host_memory_backend_pagesize(backend); - if (hpsize < *hpsize_min) { + if (host_memory_backend_is_mapped(backend) && (hpsize < *hpsize_min)) { *hpsize_min = hpsize; } } @@ -1760,7 +1753,7 @@ long qemu_getrampagesize(void) } #endif -#ifdef __linux__ +#ifdef CONFIG_POSIX static int64_t get_file_size(int fd) { int64_t size = lseek(fd, 0, SEEK_END); @@ -1895,7 +1888,7 @@ static void *file_ram_alloc(RAMBlock *block, if (mem_prealloc) { os_mem_prealloc(fd, area, memory, smp_cpus, errp); if (errp && *errp) { - qemu_ram_munmap(area, memory); + qemu_ram_munmap(fd, area, memory); return NULL; } } @@ -1994,6 +1987,21 @@ const char *qemu_ram_get_idstr(RAMBlock *rb) return rb->idstr; } +void *qemu_ram_get_host_addr(RAMBlock *rb) +{ + return rb->host; +} + +ram_addr_t qemu_ram_get_offset(RAMBlock *rb) +{ + return rb->offset; +} + +ram_addr_t qemu_ram_get_used_length(RAMBlock *rb) +{ + return rb->used_length; +} + bool qemu_ram_is_shared(RAMBlock *rb) { return rb->flags & RAM_SHARED; @@ -2256,15 +2264,18 @@ static void ram_block_add(RAMBlock *new_block, Error **errp, bool shared) } } -#ifdef __linux__ +#ifdef CONFIG_POSIX RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, - bool share, int fd, + uint32_t ram_flags, int fd, Error **errp) { RAMBlock *new_block; Error *local_err = NULL; int64_t file_size; + /* Just support these ram flags by now. */ + assert((ram_flags & ~(RAM_SHARED | RAM_PMEM)) == 0); + if (xen_enabled()) { error_setg(errp, "-mem-path not supported with Xen"); return NULL; @@ -2300,14 +2311,14 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, new_block->mr = mr; new_block->used_length = size; new_block->max_length = size; - new_block->flags = share ? RAM_SHARED : 0; + new_block->flags = ram_flags; new_block->host = file_ram_alloc(new_block, size, fd, !file_size, errp); if (!new_block->host) { g_free(new_block); return NULL; } - ram_block_add(new_block, &local_err, share); + ram_block_add(new_block, &local_err, ram_flags & RAM_SHARED); if (local_err) { g_free(new_block); error_propagate(errp, local_err); @@ -2319,7 +2330,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, - bool share, const char *mem_path, + uint32_t ram_flags, const char *mem_path, Error **errp) { int fd; @@ -2331,7 +2342,7 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, return NULL; } - block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp); + block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, errp); if (!block) { if (created) { unlink(mem_path); @@ -2413,7 +2424,7 @@ static void reclaim_ramblock(RAMBlock *block) xen_invalidate_map_cache_entry(block->host); #ifndef _WIN32 } else if (block->fd >= 0) { - qemu_ram_munmap(block->host, block->max_length); + qemu_ram_munmap(block->fd, block->host, block->max_length); close(block->fd); #endif } else { @@ -2870,10 +2881,10 @@ static const MemoryRegionOps watch_mem_ops = { }; static MemTxResult flatview_read(FlatView *fv, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, int len); + MemTxAttrs attrs, uint8_t *buf, hwaddr len); static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, int len); -static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, + const uint8_t *buf, hwaddr len); +static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, bool is_write, MemTxAttrs attrs); static MemTxResult subpage_read(void *opaque, hwaddr addr, uint64_t *data, @@ -3121,10 +3132,10 @@ MemoryRegion *get_system_io(void) /* physical memory access (slow version, mainly for debug) */ #if defined(CONFIG_USER_ONLY) int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, int len, int is_write) + uint8_t *buf, target_ulong len, int is_write) { - int l, flags; - target_ulong page; + int flags; + target_ulong l, page; void * p; while (len > 0) { @@ -3183,6 +3194,19 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); } +void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size) +{ + /* + * In principle this function would work on other memory region types too, + * but the ROM device use case is the only one where this operation is + * necessary. Other memory regions should use the + * address_space_read/write() APIs. + */ + assert(memory_region_is_romd(mr)); + + invalidate_and_set_dirty(mr, addr, size); +} + static int memory_access_size(MemoryRegion *mr, unsigned l, hwaddr addr) { unsigned access_size_max = mr->ops->valid.max_access_size; @@ -3237,7 +3261,7 @@ static bool prepare_mmio_access(MemoryRegion *mr) static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, const uint8_t *buf, - int len, hwaddr addr1, + hwaddr len, hwaddr addr1, hwaddr l, MemoryRegion *mr) { uint8_t *ptr; @@ -3282,7 +3306,7 @@ static MemTxResult flatview_write_continue(FlatView *fv, hwaddr addr, /* Called from RCU critical section. */ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, int len) + const uint8_t *buf, hwaddr len) { hwaddr l; hwaddr addr1; @@ -3300,7 +3324,7 @@ static MemTxResult flatview_write(FlatView *fv, hwaddr addr, MemTxAttrs attrs, /* Called within RCU critical section. */ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, uint8_t *buf, - int len, hwaddr addr1, hwaddr l, + hwaddr len, hwaddr addr1, hwaddr l, MemoryRegion *mr) { uint8_t *ptr; @@ -3343,7 +3367,7 @@ MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, /* Called from RCU critical section. */ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, int len) + MemTxAttrs attrs, uint8_t *buf, hwaddr len) { hwaddr l; hwaddr addr1; @@ -3356,7 +3380,7 @@ static MemTxResult flatview_read(FlatView *fv, hwaddr addr, } MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, int len) + MemTxAttrs attrs, uint8_t *buf, hwaddr len) { MemTxResult result = MEMTX_OK; FlatView *fv; @@ -3373,7 +3397,7 @@ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, int len) + const uint8_t *buf, hwaddr len) { MemTxResult result = MEMTX_OK; FlatView *fv; @@ -3389,7 +3413,7 @@ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, } MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - uint8_t *buf, int len, bool is_write) + uint8_t *buf, hwaddr len, bool is_write) { if (is_write) { return address_space_write(as, addr, attrs, buf, len); @@ -3399,7 +3423,7 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, } void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, - int len, int is_write) + hwaddr len, int is_write) { address_space_rw(&address_space_memory, addr, MEMTXATTRS_UNSPECIFIED, buf, len, is_write); @@ -3410,8 +3434,12 @@ enum write_rom_type { FLUSH_CACHE, }; -static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, - hwaddr addr, const uint8_t *buf, int len, enum write_rom_type type) +static inline MemTxResult address_space_write_rom_internal(AddressSpace *as, + hwaddr addr, + MemTxAttrs attrs, + const uint8_t *buf, + hwaddr len, + enum write_rom_type type) { hwaddr l; uint8_t *ptr; @@ -3421,8 +3449,7 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, rcu_read_lock(); while (len > 0) { l = len; - mr = address_space_translate(as, addr, &addr1, &l, true, - MEMTXATTRS_UNSPECIFIED); + mr = address_space_translate(as, addr, &addr1, &l, true, attrs); if (!(memory_region_is_ram(mr) || memory_region_is_romd(mr))) { @@ -3445,16 +3472,19 @@ static inline void cpu_physical_memory_write_rom_internal(AddressSpace *as, addr += l; } rcu_read_unlock(); + return MEMTX_OK; } /* used for ROM loading : can write in RAM and ROM */ -void cpu_physical_memory_write_rom(AddressSpace *as, hwaddr addr, - const uint8_t *buf, int len) +MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, + const uint8_t *buf, hwaddr len) { - cpu_physical_memory_write_rom_internal(as, addr, buf, len, WRITE_DATA); + return address_space_write_rom_internal(as, addr, attrs, + buf, len, WRITE_DATA); } -void cpu_flush_icache_range(hwaddr start, int len) +void cpu_flush_icache_range(hwaddr start, hwaddr len) { /* * This function should do the same thing as an icache flush that was @@ -3466,8 +3496,9 @@ void cpu_flush_icache_range(hwaddr start, int len) return; } - cpu_physical_memory_write_rom_internal(&address_space_memory, - start, NULL, len, FLUSH_CACHE); + address_space_write_rom_internal(&address_space_memory, + start, MEMTXATTRS_UNSPECIFIED, + NULL, len, FLUSH_CACHE); } typedef struct { @@ -3486,7 +3517,7 @@ typedef struct MapClient { } MapClient; QemuMutex map_client_list_lock; -static QLIST_HEAD(map_client_list, MapClient) map_client_list +static QLIST_HEAD(, MapClient) map_client_list = QLIST_HEAD_INITIALIZER(map_client_list); static void cpu_unregister_map_client_do(MapClient *client) @@ -3556,7 +3587,7 @@ static void cpu_notify_map_clients(void) qemu_mutex_unlock(&map_client_list_lock); } -static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, +static bool flatview_access_valid(FlatView *fv, hwaddr addr, hwaddr len, bool is_write, MemTxAttrs attrs) { MemoryRegion *mr; @@ -3579,7 +3610,7 @@ static bool flatview_access_valid(FlatView *fv, hwaddr addr, int len, } bool address_space_access_valid(AddressSpace *as, hwaddr addr, - int len, bool is_write, + hwaddr len, bool is_write, MemTxAttrs attrs) { FlatView *fv; @@ -3832,7 +3863,7 @@ static inline MemoryRegion *address_space_translate_cached( */ void address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, - void *buf, int len) + void *buf, hwaddr len) { hwaddr addr1, l; MemoryRegion *mr; @@ -3850,7 +3881,7 @@ address_space_read_cached_slow(MemoryRegionCache *cache, hwaddr addr, */ void address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, - const void *buf, int len) + const void *buf, hwaddr len) { hwaddr addr1, l; MemoryRegion *mr; @@ -3873,11 +3904,10 @@ address_space_write_cached_slow(MemoryRegionCache *cache, hwaddr addr, /* virtual memory access for debug (includes writing to ROM) */ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, int len, int is_write) + uint8_t *buf, target_ulong len, int is_write) { - int l; hwaddr phys_addr; - target_ulong page; + target_ulong l, page; cpu_synchronize_state(cpu); while (len > 0) { @@ -3895,12 +3925,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->cpu_ases[asidx].as, - phys_addr, buf, l); + address_space_write_rom(cpu->cpu_ases[asidx].as, phys_addr, + attrs, buf, l); } else { address_space_rw(cpu->cpu_ases[asidx].as, phys_addr, - MEMTXATTRS_UNSPECIFIED, - buf, l, 0); + attrs, buf, l, 0); } len -= l; buf += l; @@ -3929,11 +3958,6 @@ int qemu_target_page_bits_min(void) } #endif -/* - * A helper function for the _utterly broken_ virtio device model to find out if - * it's running on a big endian machine. Don't do this at home kids! - */ -bool target_words_bigendian(void); bool target_words_bigendian(void) { #if defined(TARGET_WORDS_BIGENDIAN) @@ -3967,28 +3991,7 @@ int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque) rcu_read_lock(); RAMBLOCK_FOREACH(block) { - ret = func(block->idstr, block->host, block->offset, - block->used_length, opaque); - if (ret) { - break; - } - } - rcu_read_unlock(); - return ret; -} - -int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque) -{ - RAMBlock *block; - int ret = 0; - - rcu_read_lock(); - RAMBLOCK_FOREACH(block) { - if (!qemu_ram_is_migratable(block)) { - continue; - } - ret = func(block->idstr, block->host, block->offset, - block->used_length, opaque); + ret = func(block, opaque); if (ret) { break; } @@ -4092,6 +4095,11 @@ err: return ret; } +bool ramblock_is_pmem(RAMBlock *rb) +{ + return rb->flags & RAM_PMEM; +} + #endif void page_size_init(void) diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 16c0bcb6fa..7b8895726c 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -495,15 +495,15 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 1; } #elif defined(TARGET_MIPS) - /* For MIPS, the (inf,zero,qnan) case sets InvalidOp and returns - * the default NaN - */ - if (infzero) { - float_raise(float_flag_invalid, status); - return 3; - } - if (snan_bit_is_one(status)) { + /* + * For MIPS systems that conform to IEEE754-1985, the (inf,zero,nan) + * case sets InvalidOp and returns the default NaN + */ + if (infzero) { + float_raise(float_flag_invalid, status); + return 3; + } /* Prefer sNaN over qNaN, in the a, b, c order. */ if (is_snan(a_cls)) { return 0; @@ -519,6 +519,14 @@ static int pickNaNMulAdd(FloatClass a_cls, FloatClass b_cls, FloatClass c_cls, return 2; } } else { + /* + * For MIPS systems that conform to IEEE754-2008, the (inf,zero,nan) + * case sets InvalidOp and returns the input value 'c' + */ + if (infzero) { + float_raise(float_flag_invalid, status); + return 2; + } /* Prefer sNaN over qNaN, in the c, a, b order. */ if (is_snan(c_cls)) { return 2; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 8cd2400081..2ba36ec370 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -83,6 +83,7 @@ this code that are retained. * target-dependent and needs the TARGET_* macros. */ #include "qemu/osdep.h" +#include #include "qemu/bitops.h" #include "fpu/softfloat.h" @@ -95,6 +96,324 @@ this code that are retained. *----------------------------------------------------------------------------*/ #include "fpu/softfloat-macros.h" +/* + * Hardfloat + * + * Fast emulation of guest FP instructions is challenging for two reasons. + * First, FP instruction semantics are similar but not identical, particularly + * when handling NaNs. Second, emulating at reasonable speed the guest FP + * exception flags is not trivial: reading the host's flags register with a + * feclearexcept & fetestexcept pair is slow [slightly slower than soft-fp], + * and trapping on every FP exception is not fast nor pleasant to work with. + * + * We address these challenges by leveraging the host FPU for a subset of the + * operations. To do this we expand on the idea presented in this paper: + * + * Guo, Yu-Chuan, et al. "Translating the ARM Neon and VFP instructions in a + * binary translator." Software: Practice and Experience 46.12 (2016):1591-1615. + * + * The idea is thus to leverage the host FPU to (1) compute FP operations + * and (2) identify whether FP exceptions occurred while avoiding + * expensive exception flag register accesses. + * + * An important optimization shown in the paper is that given that exception + * flags are rarely cleared by the guest, we can avoid recomputing some flags. + * This is particularly useful for the inexact flag, which is very frequently + * raised in floating-point workloads. + * + * We optimize the code further by deferring to soft-fp whenever FP exception + * detection might get hairy. Two examples: (1) when at least one operand is + * denormal/inf/NaN; (2) when operands are not guaranteed to lead to a 0 result + * and the result is < the minimum normal. + */ +#define GEN_INPUT_FLUSH__NOCHECK(name, soft_t) \ + static inline void name(soft_t *a, float_status *s) \ + { \ + if (unlikely(soft_t ## _is_denormal(*a))) { \ + *a = soft_t ## _set_sign(soft_t ## _zero, \ + soft_t ## _is_neg(*a)); \ + s->float_exception_flags |= float_flag_input_denormal; \ + } \ + } + +GEN_INPUT_FLUSH__NOCHECK(float32_input_flush__nocheck, float32) +GEN_INPUT_FLUSH__NOCHECK(float64_input_flush__nocheck, float64) +#undef GEN_INPUT_FLUSH__NOCHECK + +#define GEN_INPUT_FLUSH1(name, soft_t) \ + static inline void name(soft_t *a, float_status *s) \ + { \ + if (likely(!s->flush_inputs_to_zero)) { \ + return; \ + } \ + soft_t ## _input_flush__nocheck(a, s); \ + } + +GEN_INPUT_FLUSH1(float32_input_flush1, float32) +GEN_INPUT_FLUSH1(float64_input_flush1, float64) +#undef GEN_INPUT_FLUSH1 + +#define GEN_INPUT_FLUSH2(name, soft_t) \ + static inline void name(soft_t *a, soft_t *b, float_status *s) \ + { \ + if (likely(!s->flush_inputs_to_zero)) { \ + return; \ + } \ + soft_t ## _input_flush__nocheck(a, s); \ + soft_t ## _input_flush__nocheck(b, s); \ + } + +GEN_INPUT_FLUSH2(float32_input_flush2, float32) +GEN_INPUT_FLUSH2(float64_input_flush2, float64) +#undef GEN_INPUT_FLUSH2 + +#define GEN_INPUT_FLUSH3(name, soft_t) \ + static inline void name(soft_t *a, soft_t *b, soft_t *c, float_status *s) \ + { \ + if (likely(!s->flush_inputs_to_zero)) { \ + return; \ + } \ + soft_t ## _input_flush__nocheck(a, s); \ + soft_t ## _input_flush__nocheck(b, s); \ + soft_t ## _input_flush__nocheck(c, s); \ + } + +GEN_INPUT_FLUSH3(float32_input_flush3, float32) +GEN_INPUT_FLUSH3(float64_input_flush3, float64) +#undef GEN_INPUT_FLUSH3 + +/* + * Choose whether to use fpclassify or float32/64_* primitives in the generated + * hardfloat functions. Each combination of number of inputs and float size + * gets its own value. + */ +#if defined(__x86_64__) +# define QEMU_HARDFLOAT_1F32_USE_FP 0 +# define QEMU_HARDFLOAT_1F64_USE_FP 1 +# define QEMU_HARDFLOAT_2F32_USE_FP 0 +# define QEMU_HARDFLOAT_2F64_USE_FP 1 +# define QEMU_HARDFLOAT_3F32_USE_FP 0 +# define QEMU_HARDFLOAT_3F64_USE_FP 1 +#else +# define QEMU_HARDFLOAT_1F32_USE_FP 0 +# define QEMU_HARDFLOAT_1F64_USE_FP 0 +# define QEMU_HARDFLOAT_2F32_USE_FP 0 +# define QEMU_HARDFLOAT_2F64_USE_FP 0 +# define QEMU_HARDFLOAT_3F32_USE_FP 0 +# define QEMU_HARDFLOAT_3F64_USE_FP 0 +#endif + +/* + * QEMU_HARDFLOAT_USE_ISINF chooses whether to use isinf() over + * float{32,64}_is_infinity when !USE_FP. + * On x86_64/aarch64, using the former over the latter can yield a ~6% speedup. + * On power64 however, using isinf() reduces fp-bench performance by up to 50%. + */ +#if defined(__x86_64__) || defined(__aarch64__) +# define QEMU_HARDFLOAT_USE_ISINF 1 +#else +# define QEMU_HARDFLOAT_USE_ISINF 0 +#endif + +/* + * Some targets clear the FP flags before most FP operations. This prevents + * the use of hardfloat, since hardfloat relies on the inexact flag being + * already set. + */ +#if defined(TARGET_PPC) || defined(__FAST_MATH__) +# if defined(__FAST_MATH__) +# warning disabling hardfloat due to -ffast-math: hardfloat requires an exact \ + IEEE implementation +# endif +# define QEMU_NO_HARDFLOAT 1 +# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN +#else +# define QEMU_NO_HARDFLOAT 0 +# define QEMU_SOFTFLOAT_ATTR QEMU_FLATTEN __attribute__((noinline)) +#endif + +static inline bool can_use_fpu(const float_status *s) +{ + if (QEMU_NO_HARDFLOAT) { + return false; + } + return likely(s->float_exception_flags & float_flag_inexact && + s->float_rounding_mode == float_round_nearest_even); +} + +/* + * Hardfloat generation functions. Each operation can have two flavors: + * either using softfloat primitives (e.g. float32_is_zero_or_normal) for + * most condition checks, or native ones (e.g. fpclassify). + * + * The flavor is chosen by the callers. Instead of using macros, we rely on the + * compiler to propagate constants and inline everything into the callers. + * + * We only generate functions for operations with two inputs, since only + * these are common enough to justify consolidating them into common code. + */ + +typedef union { + float32 s; + float h; +} union_float32; + +typedef union { + float64 s; + double h; +} union_float64; + +typedef bool (*f32_check_fn)(union_float32 a, union_float32 b); +typedef bool (*f64_check_fn)(union_float64 a, union_float64 b); + +typedef float32 (*soft_f32_op2_fn)(float32 a, float32 b, float_status *s); +typedef float64 (*soft_f64_op2_fn)(float64 a, float64 b, float_status *s); +typedef float (*hard_f32_op2_fn)(float a, float b); +typedef double (*hard_f64_op2_fn)(double a, double b); + +/* 2-input is-zero-or-normal */ +static inline bool f32_is_zon2(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + /* + * Not using a temp variable for consecutive fpclassify calls ends up + * generating faster code. + */ + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO); + } + return float32_is_zero_or_normal(a.s) && + float32_is_zero_or_normal(b.s); +} + +static inline bool f64_is_zon2(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO); + } + return float64_is_zero_or_normal(a.s) && + float64_is_zero_or_normal(b.s); +} + +/* 3-input is-zero-or-normal */ +static inline +bool f32_is_zon3(union_float32 a, union_float32 b, union_float32 c) +{ + if (QEMU_HARDFLOAT_3F32_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO) && + (fpclassify(c.h) == FP_NORMAL || fpclassify(c.h) == FP_ZERO); + } + return float32_is_zero_or_normal(a.s) && + float32_is_zero_or_normal(b.s) && + float32_is_zero_or_normal(c.s); +} + +static inline +bool f64_is_zon3(union_float64 a, union_float64 b, union_float64 c) +{ + if (QEMU_HARDFLOAT_3F64_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + (fpclassify(b.h) == FP_NORMAL || fpclassify(b.h) == FP_ZERO) && + (fpclassify(c.h) == FP_NORMAL || fpclassify(c.h) == FP_ZERO); + } + return float64_is_zero_or_normal(a.s) && + float64_is_zero_or_normal(b.s) && + float64_is_zero_or_normal(c.s); +} + +static inline bool f32_is_inf(union_float32 a) +{ + if (QEMU_HARDFLOAT_USE_ISINF) { + return isinf(a.h); + } + return float32_is_infinity(a.s); +} + +static inline bool f64_is_inf(union_float64 a) +{ + if (QEMU_HARDFLOAT_USE_ISINF) { + return isinf(a.h); + } + return float64_is_infinity(a.s); +} + +/* Note: @fast_test and @post can be NULL */ +static inline float32 +float32_gen2(float32 xa, float32 xb, float_status *s, + hard_f32_op2_fn hard, soft_f32_op2_fn soft, + f32_check_fn pre, f32_check_fn post, + f32_check_fn fast_test, soft_f32_op2_fn fast_op) +{ + union_float32 ua, ub, ur; + + ua.s = xa; + ub.s = xb; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float32_input_flush2(&ua.s, &ub.s, s); + if (unlikely(!pre(ua, ub))) { + goto soft; + } + if (fast_test && fast_test(ua, ub)) { + return fast_op(ua.s, ub.s, s); + } + + ur.h = hard(ua.h, ub.h); + if (unlikely(f32_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabsf(ur.h) <= FLT_MIN)) { + if (post == NULL || post(ua, ub)) { + goto soft; + } + } + return ur.s; + + soft: + return soft(ua.s, ub.s, s); +} + +static inline float64 +float64_gen2(float64 xa, float64 xb, float_status *s, + hard_f64_op2_fn hard, soft_f64_op2_fn soft, + f64_check_fn pre, f64_check_fn post, + f64_check_fn fast_test, soft_f64_op2_fn fast_op) +{ + union_float64 ua, ub, ur; + + ua.s = xa; + ub.s = xb; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float64_input_flush2(&ua.s, &ub.s, s); + if (unlikely(!pre(ua, ub))) { + goto soft; + } + if (fast_test && fast_test(ua, ub)) { + return fast_op(ua.s, ub.s, s); + } + + ur.h = hard(ua.h, ub.h); + if (unlikely(f64_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabs(ur.h) <= DBL_MIN)) { + if (post == NULL || post(ua, ub)) { + goto soft; + } + } + return ur.s; + + soft: + return soft(ua.s, ub.s, s); +} + /*---------------------------------------------------------------------------- | Returns the fraction bits of the half-precision floating-point value `a'. *----------------------------------------------------------------------------*/ @@ -336,8 +655,8 @@ static inline float64 float64_pack_raw(FloatParts p) #include "softfloat-specialize.h" /* Canonicalize EXP and FRAC, setting CLS. */ -static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, - float_status *status) +static FloatParts sf_canonicalize(FloatParts part, const FloatFmt *parm, + float_status *status) { if (part.exp == parm->exp_max && !parm->arm_althp) { if (part.frac == 0) { @@ -377,6 +696,7 @@ static FloatParts canonicalize(FloatParts part, const FloatFmt *parm, static FloatParts round_canonical(FloatParts p, float_status *s, const FloatFmt *parm) { + const uint64_t frac_lsb = parm->frac_lsb; const uint64_t frac_lsbm1 = parm->frac_lsbm1; const uint64_t round_mask = parm->round_mask; const uint64_t roundeven_mask = parm->roundeven_mask; @@ -412,6 +732,10 @@ static FloatParts round_canonical(FloatParts p, float_status *s, inc = p.sign ? round_mask : 0; overflow_norm = !p.sign; break; + case float_round_to_odd: + overflow_norm = true; + inc = frac & frac_lsb ? 0 : round_mask; + break; default: g_assert_not_reached(); } @@ -459,9 +783,14 @@ static FloatParts round_canonical(FloatParts p, float_status *s, shift64RightJamming(frac, 1 - exp, &frac); if (frac & round_mask) { /* Need to recompute round-to-even. */ - if (s->float_rounding_mode == float_round_nearest_even) { + switch (s->float_rounding_mode) { + case float_round_nearest_even: inc = ((frac & roundeven_mask) != frac_lsbm1 ? frac_lsbm1 : 0); + break; + case float_round_to_odd: + inc = frac & frac_lsb ? 0 : round_mask; + break; } flags |= float_flag_inexact; frac += inc; @@ -513,7 +842,7 @@ static FloatParts round_canonical(FloatParts p, float_status *s, static FloatParts float16a_unpack_canonical(float16 f, float_status *s, const FloatFmt *params) { - return canonicalize(float16_unpack_raw(f), params, s); + return sf_canonicalize(float16_unpack_raw(f), params, s); } static FloatParts float16_unpack_canonical(float16 f, float_status *s) @@ -534,7 +863,7 @@ static float16 float16_round_pack_canonical(FloatParts p, float_status *s) static FloatParts float32_unpack_canonical(float32 f, float_status *s) { - return canonicalize(float32_unpack_raw(f), &float32_params, s); + return sf_canonicalize(float32_unpack_raw(f), &float32_params, s); } static float32 float32_round_pack_canonical(FloatParts p, float_status *s) @@ -544,7 +873,7 @@ static float32 float32_round_pack_canonical(FloatParts p, float_status *s) static FloatParts float64_unpack_canonical(float64 f, float_status *s) { - return canonicalize(float64_unpack_raw(f), &float64_params, s); + return sf_canonicalize(float64_unpack_raw(f), &float64_params, s); } static float64 float64_round_pack_canonical(FloatParts p, float_status *s) @@ -701,7 +1030,7 @@ static FloatParts addsub_floats(FloatParts a, FloatParts b, bool subtract, } a.frac += b.frac; if (a.frac & DECOMPOSED_OVERFLOW_BIT) { - a.frac >>= 1; + shift64RightJamming(a.frac, 1, &a.frac); a.exp += 1; } return a; @@ -726,8 +1055,7 @@ static FloatParts addsub_floats(FloatParts a, FloatParts b, bool subtract, * IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -float16 __attribute__((flatten)) float16_add(float16 a, float16 b, - float_status *status) +float16 QEMU_FLATTEN float16_add(float16 a, float16 b, float_status *status) { FloatParts pa = float16_unpack_canonical(a, status); FloatParts pb = float16_unpack_canonical(b, status); @@ -736,28 +1064,7 @@ float16 __attribute__((flatten)) float16_add(float16 a, float16 b, return float16_round_pack_canonical(pr, status); } -float32 __attribute__((flatten)) float32_add(float32 a, float32 b, - float_status *status) -{ - FloatParts pa = float32_unpack_canonical(a, status); - FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, false, status); - - return float32_round_pack_canonical(pr, status); -} - -float64 __attribute__((flatten)) float64_add(float64 a, float64 b, - float_status *status) -{ - FloatParts pa = float64_unpack_canonical(a, status); - FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, false, status); - - return float64_round_pack_canonical(pr, status); -} - -float16 __attribute__((flatten)) float16_sub(float16 a, float16 b, - float_status *status) +float16 QEMU_FLATTEN float16_sub(float16 a, float16 b, float_status *status) { FloatParts pa = float16_unpack_canonical(a, status); FloatParts pb = float16_unpack_canonical(b, status); @@ -766,26 +1073,121 @@ float16 __attribute__((flatten)) float16_sub(float16 a, float16 b, return float16_round_pack_canonical(pr, status); } -float32 __attribute__((flatten)) float32_sub(float32 a, float32 b, - float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_addsub(float32 a, float32 b, bool subtract, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); + FloatParts pr = addsub_floats(pa, pb, subtract, status); return float32_round_pack_canonical(pr, status); } -float64 __attribute__((flatten)) float64_sub(float64 a, float64 b, - float_status *status) +static inline float32 soft_f32_add(float32 a, float32 b, float_status *status) +{ + return soft_f32_addsub(a, b, false, status); +} + +static inline float32 soft_f32_sub(float32 a, float32 b, float_status *status) +{ + return soft_f32_addsub(a, b, true, status); +} + +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_addsub(float64 a, float64 b, bool subtract, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); - FloatParts pr = addsub_floats(pa, pb, true, status); + FloatParts pr = addsub_floats(pa, pb, subtract, status); return float64_round_pack_canonical(pr, status); } +static inline float64 soft_f64_add(float64 a, float64 b, float_status *status) +{ + return soft_f64_addsub(a, b, false, status); +} + +static inline float64 soft_f64_sub(float64 a, float64 b, float_status *status) +{ + return soft_f64_addsub(a, b, true, status); +} + +static float hard_f32_add(float a, float b) +{ + return a + b; +} + +static float hard_f32_sub(float a, float b) +{ + return a - b; +} + +static double hard_f64_add(double a, double b) +{ + return a + b; +} + +static double hard_f64_sub(double a, double b) +{ + return a - b; +} + +static bool f32_addsub_post(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + return !(fpclassify(a.h) == FP_ZERO && fpclassify(b.h) == FP_ZERO); + } + return !(float32_is_zero(a.s) && float32_is_zero(b.s)); +} + +static bool f64_addsub_post(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return !(fpclassify(a.h) == FP_ZERO && fpclassify(b.h) == FP_ZERO); + } else { + return !(float64_is_zero(a.s) && float64_is_zero(b.s)); + } +} + +static float32 float32_addsub(float32 a, float32 b, float_status *s, + hard_f32_op2_fn hard, soft_f32_op2_fn soft) +{ + return float32_gen2(a, b, s, hard, soft, + f32_is_zon2, f32_addsub_post, NULL, NULL); +} + +static float64 float64_addsub(float64 a, float64 b, float_status *s, + hard_f64_op2_fn hard, soft_f64_op2_fn soft) +{ + return float64_gen2(a, b, s, hard, soft, + f64_is_zon2, f64_addsub_post, NULL, NULL); +} + +float32 QEMU_FLATTEN +float32_add(float32 a, float32 b, float_status *s) +{ + return float32_addsub(a, b, s, hard_f32_add, soft_f32_add); +} + +float32 QEMU_FLATTEN +float32_sub(float32 a, float32 b, float_status *s) +{ + return float32_addsub(a, b, s, hard_f32_sub, soft_f32_sub); +} + +float64 QEMU_FLATTEN +float64_add(float64 a, float64 b, float_status *s) +{ + return float64_addsub(a, b, s, hard_f64_add, soft_f64_add); +} + +float64 QEMU_FLATTEN +float64_sub(float64 a, float64 b, float_status *s) +{ + return float64_addsub(a, b, s, hard_f64_sub, soft_f64_sub); +} + /* * Returns the result of multiplying the floating-point values `a' and * `b'. The operation is performed according to the IEC/IEEE Standard @@ -835,8 +1237,7 @@ static FloatParts mul_floats(FloatParts a, FloatParts b, float_status *s) g_assert_not_reached(); } -float16 __attribute__((flatten)) float16_mul(float16 a, float16 b, - float_status *status) +float16 QEMU_FLATTEN float16_mul(float16 a, float16 b, float_status *status) { FloatParts pa = float16_unpack_canonical(a, status); FloatParts pb = float16_unpack_canonical(b, status); @@ -845,8 +1246,8 @@ float16 __attribute__((flatten)) float16_mul(float16 a, float16 b, return float16_round_pack_canonical(pr, status); } -float32 __attribute__((flatten)) float32_mul(float32 a, float32 b, - float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_mul(float32 a, float32 b, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); @@ -855,8 +1256,8 @@ float32 __attribute__((flatten)) float32_mul(float32 a, float32 b, return float32_round_pack_canonical(pr, status); } -float64 __attribute__((flatten)) float64_mul(float64 a, float64 b, - float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_mul(float64 a, float64 b, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); @@ -865,6 +1266,54 @@ float64 __attribute__((flatten)) float64_mul(float64 a, float64 b, return float64_round_pack_canonical(pr, status); } +static float hard_f32_mul(float a, float b) +{ + return a * b; +} + +static double hard_f64_mul(double a, double b) +{ + return a * b; +} + +static bool f32_mul_fast_test(union_float32 a, union_float32 b) +{ + return float32_is_zero(a.s) || float32_is_zero(b.s); +} + +static bool f64_mul_fast_test(union_float64 a, union_float64 b) +{ + return float64_is_zero(a.s) || float64_is_zero(b.s); +} + +static float32 f32_mul_fast_op(float32 a, float32 b, float_status *s) +{ + bool signbit = float32_is_neg(a) ^ float32_is_neg(b); + + return float32_set_sign(float32_zero, signbit); +} + +static float64 f64_mul_fast_op(float64 a, float64 b, float_status *s) +{ + bool signbit = float64_is_neg(a) ^ float64_is_neg(b); + + return float64_set_sign(float64_zero, signbit); +} + +float32 QEMU_FLATTEN +float32_mul(float32 a, float32 b, float_status *s) +{ + return float32_gen2(a, b, s, hard_f32_mul, soft_f32_mul, + f32_is_zon2, NULL, f32_mul_fast_test, f32_mul_fast_op); +} + +float64 QEMU_FLATTEN +float64_mul(float64 a, float64 b, float_status *s) +{ + return float64_gen2(a, b, s, hard_f64_mul, soft_f64_mul, + f64_is_zon2, NULL, f64_mul_fast_test, f64_mul_fast_op); +} + /* * Returns the result of multiplying the floating-point values `a' and * `b' then adding 'c', with no intermediate rounding step after the @@ -1068,7 +1517,7 @@ static FloatParts muladd_floats(FloatParts a, FloatParts b, FloatParts c, return a; } -float16 __attribute__((flatten)) float16_muladd(float16 a, float16 b, float16 c, +float16 QEMU_FLATTEN float16_muladd(float16 a, float16 b, float16 c, int flags, float_status *status) { FloatParts pa = float16_unpack_canonical(a, status); @@ -1079,8 +1528,9 @@ float16 __attribute__((flatten)) float16_muladd(float16 a, float16 b, float16 c, return float16_round_pack_canonical(pr, status); } -float32 __attribute__((flatten)) float32_muladd(float32 a, float32 b, float32 c, - int flags, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_muladd(float32 a, float32 b, float32 c, int flags, + float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); @@ -1090,8 +1540,9 @@ float32 __attribute__((flatten)) float32_muladd(float32 a, float32 b, float32 c, return float32_round_pack_canonical(pr, status); } -float64 __attribute__((flatten)) float64_muladd(float64 a, float64 b, float64 c, - int flags, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_muladd(float64 a, float64 b, float64 c, int flags, + float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); @@ -1101,6 +1552,150 @@ float64 __attribute__((flatten)) float64_muladd(float64 a, float64 b, float64 c, return float64_round_pack_canonical(pr, status); } +static bool force_soft_fma; + +float32 QEMU_FLATTEN +float32_muladd(float32 xa, float32 xb, float32 xc, int flags, float_status *s) +{ + union_float32 ua, ub, uc, ur; + + ua.s = xa; + ub.s = xb; + uc.s = xc; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + if (unlikely(flags & float_muladd_halve_result)) { + goto soft; + } + + float32_input_flush3(&ua.s, &ub.s, &uc.s, s); + if (unlikely(!f32_is_zon3(ua, ub, uc))) { + goto soft; + } + + if (unlikely(force_soft_fma)) { + goto soft; + } + + /* + * When (a || b) == 0, there's no need to check for under/over flow, + * since we know the addend is (normal || 0) and the product is 0. + */ + if (float32_is_zero(ua.s) || float32_is_zero(ub.s)) { + union_float32 up; + bool prod_sign; + + prod_sign = float32_is_neg(ua.s) ^ float32_is_neg(ub.s); + prod_sign ^= !!(flags & float_muladd_negate_product); + up.s = float32_set_sign(float32_zero, prod_sign); + + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + ur.h = up.h + uc.h; + } else { + union_float32 ua_orig = ua; + union_float32 uc_orig = uc; + + if (flags & float_muladd_negate_product) { + ua.h = -ua.h; + } + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + + ur.h = fmaf(ua.h, ub.h, uc.h); + + if (unlikely(f32_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabsf(ur.h) <= FLT_MIN)) { + ua = ua_orig; + uc = uc_orig; + goto soft; + } + } + if (flags & float_muladd_negate_result) { + return float32_chs(ur.s); + } + return ur.s; + + soft: + return soft_f32_muladd(ua.s, ub.s, uc.s, flags, s); +} + +float64 QEMU_FLATTEN +float64_muladd(float64 xa, float64 xb, float64 xc, int flags, float_status *s) +{ + union_float64 ua, ub, uc, ur; + + ua.s = xa; + ub.s = xb; + uc.s = xc; + + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + if (unlikely(flags & float_muladd_halve_result)) { + goto soft; + } + + float64_input_flush3(&ua.s, &ub.s, &uc.s, s); + if (unlikely(!f64_is_zon3(ua, ub, uc))) { + goto soft; + } + + if (unlikely(force_soft_fma)) { + goto soft; + } + + /* + * When (a || b) == 0, there's no need to check for under/over flow, + * since we know the addend is (normal || 0) and the product is 0. + */ + if (float64_is_zero(ua.s) || float64_is_zero(ub.s)) { + union_float64 up; + bool prod_sign; + + prod_sign = float64_is_neg(ua.s) ^ float64_is_neg(ub.s); + prod_sign ^= !!(flags & float_muladd_negate_product); + up.s = float64_set_sign(float64_zero, prod_sign); + + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + ur.h = up.h + uc.h; + } else { + union_float64 ua_orig = ua; + union_float64 uc_orig = uc; + + if (flags & float_muladd_negate_product) { + ua.h = -ua.h; + } + if (flags & float_muladd_negate_c) { + uc.h = -uc.h; + } + + ur.h = fma(ua.h, ub.h, uc.h); + + if (unlikely(f64_is_inf(ur))) { + s->float_exception_flags |= float_flag_overflow; + } else if (unlikely(fabs(ur.h) <= FLT_MIN)) { + ua = ua_orig; + uc = uc_orig; + goto soft; + } + } + if (flags & float_muladd_negate_result) { + return float64_chs(ur.s); + } + return ur.s; + + soft: + return soft_f64_muladd(ua.s, ub.s, uc.s, flags, s); +} + /* * Returns the result of dividing the floating-point value `a' by the * corresponding value `b'. The operation is performed according to @@ -1112,19 +1707,38 @@ static FloatParts div_floats(FloatParts a, FloatParts b, float_status *s) bool sign = a.sign ^ b.sign; if (a.cls == float_class_normal && b.cls == float_class_normal) { - uint64_t temp_lo, temp_hi; + uint64_t n0, n1, q, r; int exp = a.exp - b.exp; + + /* + * We want a 2*N / N-bit division to produce exactly an N-bit + * result, so that we do not lose any precision and so that we + * do not have to renormalize afterward. If A.frac < B.frac, + * then division would produce an (N-1)-bit result; shift A left + * by one to produce the an N-bit result, and decrement the + * exponent to match. + * + * The udiv_qrnnd algorithm that we're using requires normalization, + * i.e. the msb of the denominator must be set. Since we know that + * DECOMPOSED_BINARY_POINT is msb-1, the inputs must be shifted left + * by one (more), and the remainder must be shifted right by one. + */ if (a.frac < b.frac) { exp -= 1; - shortShift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 1, - &temp_hi, &temp_lo); + shift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 2, &n1, &n0); } else { - shortShift128Left(0, a.frac, DECOMPOSED_BINARY_POINT, - &temp_hi, &temp_lo); + shift128Left(0, a.frac, DECOMPOSED_BINARY_POINT + 1, &n1, &n0); } - /* LSB of quot is set if inexact which roundandpack will use - * to set flags. Yet again we re-use a for the result */ - a.frac = div128To64(temp_lo, temp_hi, b.frac); + q = udiv_qrnnd(&r, n1, n0, b.frac << 1); + + /* + * Set lsb if there is a remainder, to set inexact. + * As mentioned above, to find the actual value of the remainder we + * would need to shift right, but (1) we are only concerned about + * non-zero-ness, and (2) the remainder will always be even because + * both inputs to the division primitive are even. + */ + a.frac = q | (r != 0); a.sign = sign; a.exp = exp; return a; @@ -1170,7 +1784,8 @@ float16 float16_div(float16 a, float16 b, float_status *status) return float16_round_pack_canonical(pr, status); } -float32 float32_div(float32 a, float32 b, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_div(float32 a, float32 b, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pb = float32_unpack_canonical(b, status); @@ -1179,7 +1794,8 @@ float32 float32_div(float32 a, float32 b, float_status *status) return float32_round_pack_canonical(pr, status); } -float64 float64_div(float64 a, float64 b, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_div(float64 a, float64 b, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pb = float64_unpack_canonical(b, status); @@ -1188,6 +1804,64 @@ float64 float64_div(float64 a, float64 b, float_status *status) return float64_round_pack_canonical(pr, status); } +static float hard_f32_div(float a, float b) +{ + return a / b; +} + +static double hard_f64_div(double a, double b) +{ + return a / b; +} + +static bool f32_div_pre(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + fpclassify(b.h) == FP_NORMAL; + } + return float32_is_zero_or_normal(a.s) && float32_is_normal(b.s); +} + +static bool f64_div_pre(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return (fpclassify(a.h) == FP_NORMAL || fpclassify(a.h) == FP_ZERO) && + fpclassify(b.h) == FP_NORMAL; + } + return float64_is_zero_or_normal(a.s) && float64_is_normal(b.s); +} + +static bool f32_div_post(union_float32 a, union_float32 b) +{ + if (QEMU_HARDFLOAT_2F32_USE_FP) { + return fpclassify(a.h) != FP_ZERO; + } + return !float32_is_zero(a.s); +} + +static bool f64_div_post(union_float64 a, union_float64 b) +{ + if (QEMU_HARDFLOAT_2F64_USE_FP) { + return fpclassify(a.h) != FP_ZERO; + } + return !float64_is_zero(a.s); +} + +float32 QEMU_FLATTEN +float32_div(float32 a, float32 b, float_status *s) +{ + return float32_gen2(a, b, s, hard_f32_div, soft_f32_div, + f32_div_pre, f32_div_post, NULL, NULL); +} + +float64 QEMU_FLATTEN +float64_div(float64 a, float64 b, float_status *s) +{ + return float64_gen2(a, b, s, hard_f64_div, soft_f64_div, + f64_div_pre, f64_div_post, NULL, NULL); +} + /* * Float to Float conversions * @@ -1293,19 +1967,23 @@ float32 float64_to_float32(float64 a, float_status *s) * Arithmetic. */ -static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) +static FloatParts round_to_int(FloatParts a, int rmode, + int scale, float_status *s) { - if (is_nan(a.cls)) { - return return_nan(a, s); - } - switch (a.cls) { + case float_class_qnan: + case float_class_snan: + return return_nan(a, s); + case float_class_zero: case float_class_inf: - case float_class_qnan: /* already "integral" */ break; + case float_class_normal: + scale = MIN(MAX(scale, -0x10000), 0x10000); + a.exp += scale; + if (a.exp >= DECOMPOSED_BINARY_POINT) { /* already integral */ break; @@ -1314,7 +1992,7 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) bool one; /* all fractional */ s->float_exception_flags |= float_flag_inexact; - switch (rounding_mode) { + switch (rmode) { case float_round_nearest_even: one = a.exp == -1 && a.frac > DECOMPOSED_IMPLICIT_BIT; break; @@ -1330,6 +2008,9 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) case float_round_down: one = a.sign; break; + case float_round_to_odd: + one = true; + break; default: g_assert_not_reached(); } @@ -1347,7 +2028,7 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) uint64_t rnd_mask = rnd_even_mask >> 1; uint64_t inc; - switch (rounding_mode) { + switch (rmode) { case float_round_nearest_even: inc = ((a.frac & rnd_even_mask) != frac_lsbm1 ? frac_lsbm1 : 0); break; @@ -1363,6 +2044,9 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) case float_round_down: inc = a.sign ? rnd_mask : 0; break; + case float_round_to_odd: + inc = a.frac & frac_lsb ? 0 : rnd_mask; + break; default: g_assert_not_reached(); } @@ -1387,28 +2071,21 @@ static FloatParts round_to_int(FloatParts a, int rounding_mode, float_status *s) float16 float16_round_to_int(float16 a, float_status *s) { FloatParts pa = float16_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float16_round_pack_canonical(pr, s); } float32 float32_round_to_int(float32 a, float_status *s) { FloatParts pa = float32_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float32_round_pack_canonical(pr, s); } float64 float64_round_to_int(float64 a, float_status *s) { FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, s->float_rounding_mode, s); - return float64_round_pack_canonical(pr, s); -} - -float64 float64_trunc_to_int(float64 a, float_status *s) -{ - FloatParts pa = float64_unpack_canonical(a, s); - FloatParts pr = round_to_int(pa, float_round_to_zero, s); + FloatParts pr = round_to_int(pa, s->float_rounding_mode, 0, s); return float64_round_pack_canonical(pr, s); } @@ -1423,13 +2100,13 @@ float64 float64_trunc_to_int(float64 a, float_status *s) * is returned. */ -static int64_t round_to_int_and_pack(FloatParts in, int rmode, +static int64_t round_to_int_and_pack(FloatParts in, int rmode, int scale, int64_t min, int64_t max, float_status *s) { uint64_t r; int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, s); + FloatParts p = round_to_int(in, rmode, scale, s); switch (p.cls) { case float_class_snan: @@ -1469,38 +2146,158 @@ static int64_t round_to_int_and_pack(FloatParts in, int rmode, } } -#define FLOAT_TO_INT(fsz, isz) \ -int ## isz ## _t float ## fsz ## _to_int ## isz(float ## fsz a, \ - float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_int_and_pack(p, s->float_rounding_mode, \ - INT ## isz ## _MIN, INT ## isz ## _MAX,\ - s); \ -} \ - \ -int ## isz ## _t float ## fsz ## _to_int ## isz ## _round_to_zero \ - (float ## fsz a, float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_int_and_pack(p, float_round_to_zero, \ - INT ## isz ## _MIN, INT ## isz ## _MAX,\ - s); \ +int16_t float16_to_int16_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); } -FLOAT_TO_INT(16, 16) -FLOAT_TO_INT(16, 32) -FLOAT_TO_INT(16, 64) +int32_t float16_to_int32_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} -FLOAT_TO_INT(32, 16) -FLOAT_TO_INT(32, 32) -FLOAT_TO_INT(32, 64) +int64_t float16_to_int64_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float16_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} -FLOAT_TO_INT(64, 16) -FLOAT_TO_INT(64, 32) -FLOAT_TO_INT(64, 64) +int16_t float32_to_int16_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} -#undef FLOAT_TO_INT +int32_t float32_to_int32_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float32_to_int64_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float32_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float64_to_int16_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT16_MIN, INT16_MAX, s); +} + +int32_t float64_to_int32_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT32_MIN, INT32_MAX, s); +} + +int64_t float64_to_int64_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_int_and_pack(float64_unpack_canonical(a, s), + rmode, scale, INT64_MIN, INT64_MAX, s); +} + +int16_t float16_to_int16(float16 a, float_status *s) +{ + return float16_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float16_to_int32(float16 a, float_status *s) +{ + return float16_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float16_to_int64(float16 a, float_status *s) +{ + return float16_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float32_to_int16(float32 a, float_status *s) +{ + return float32_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float32_to_int32(float32 a, float_status *s) +{ + return float32_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float32_to_int64(float32 a, float_status *s) +{ + return float32_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float64_to_int16(float64 a, float_status *s) +{ + return float64_to_int16_scalbn(a, s->float_rounding_mode, 0, s); +} + +int32_t float64_to_int32(float64 a, float_status *s) +{ + return float64_to_int32_scalbn(a, s->float_rounding_mode, 0, s); +} + +int64_t float64_to_int64(float64 a, float_status *s) +{ + return float64_to_int64_scalbn(a, s->float_rounding_mode, 0, s); +} + +int16_t float16_to_int16_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float16_to_int32_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float16_to_int64_round_to_zero(float16 a, float_status *s) +{ + return float16_to_int64_scalbn(a, float_round_to_zero, 0, s); +} + +int16_t float32_to_int16_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float32_to_int32_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float32_to_int64_round_to_zero(float32 a, float_status *s) +{ + return float32_to_int64_scalbn(a, float_round_to_zero, 0, s); +} + +int16_t float64_to_int16_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int16_scalbn(a, float_round_to_zero, 0, s); +} + +int32_t float64_to_int32_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int32_scalbn(a, float_round_to_zero, 0, s); +} + +int64_t float64_to_int64_round_to_zero(float64 a, float_status *s) +{ + return float64_to_int64_scalbn(a, float_round_to_zero, 0, s); +} /* * Returns the result of converting the floating-point value `a' to @@ -1515,11 +2312,12 @@ FLOAT_TO_INT(64, 64) * flag. */ -static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, - float_status *s) +static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, int scale, + uint64_t max, float_status *s) { int orig_flags = get_float_exception_flags(s); - FloatParts p = round_to_int(in, rmode, s); + FloatParts p = round_to_int(in, rmode, scale, s); + uint64_t r; switch (p.cls) { case float_class_snan: @@ -1532,8 +2330,6 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, case float_class_zero: return 0; case float_class_normal: - { - uint64_t r; if (p.sign) { s->float_exception_flags = orig_flags | float_flag_invalid; return 0; @@ -1555,45 +2351,165 @@ static uint64_t round_to_uint_and_pack(FloatParts in, int rmode, uint64_t max, if (r > max) { s->float_exception_flags = orig_flags | float_flag_invalid; return max; - } else { - return r; } - } + return r; default: g_assert_not_reached(); } } -#define FLOAT_TO_UINT(fsz, isz) \ -uint ## isz ## _t float ## fsz ## _to_uint ## isz(float ## fsz a, \ - float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_uint_and_pack(p, s->float_rounding_mode, \ - UINT ## isz ## _MAX, s); \ -} \ - \ -uint ## isz ## _t float ## fsz ## _to_uint ## isz ## _round_to_zero \ - (float ## fsz a, float_status *s) \ -{ \ - FloatParts p = float ## fsz ## _unpack_canonical(a, s); \ - return round_to_uint_and_pack(p, float_round_to_zero, \ - UINT ## isz ## _MAX, s); \ +uint16_t float16_to_uint16_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); } -FLOAT_TO_UINT(16, 16) -FLOAT_TO_UINT(16, 32) -FLOAT_TO_UINT(16, 64) +uint32_t float16_to_uint32_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} -FLOAT_TO_UINT(32, 16) -FLOAT_TO_UINT(32, 32) -FLOAT_TO_UINT(32, 64) +uint64_t float16_to_uint64_scalbn(float16 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float16_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} -FLOAT_TO_UINT(64, 16) -FLOAT_TO_UINT(64, 32) -FLOAT_TO_UINT(64, 64) +uint16_t float32_to_uint16_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} -#undef FLOAT_TO_UINT +uint32_t float32_to_uint32_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} + +uint64_t float32_to_uint64_scalbn(float32 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float32_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float64_to_uint16_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT16_MAX, s); +} + +uint32_t float64_to_uint32_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT32_MAX, s); +} + +uint64_t float64_to_uint64_scalbn(float64 a, int rmode, int scale, + float_status *s) +{ + return round_to_uint_and_pack(float64_unpack_canonical(a, s), + rmode, scale, UINT64_MAX, s); +} + +uint16_t float16_to_uint16(float16 a, float_status *s) +{ + return float16_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float16_to_uint32(float16 a, float_status *s) +{ + return float16_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float16_to_uint64(float16 a, float_status *s) +{ + return float16_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float32_to_uint16(float32 a, float_status *s) +{ + return float32_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float32_to_uint32(float32 a, float_status *s) +{ + return float32_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float32_to_uint64(float32 a, float_status *s) +{ + return float32_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float64_to_uint16(float64 a, float_status *s) +{ + return float64_to_uint16_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint32_t float64_to_uint32(float64 a, float_status *s) +{ + return float64_to_uint32_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint64_t float64_to_uint64(float64 a, float_status *s) +{ + return float64_to_uint64_scalbn(a, s->float_rounding_mode, 0, s); +} + +uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *s) +{ + return float16_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} + +uint16_t float32_to_uint16_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float32_to_uint32_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float32_to_uint64_round_to_zero(float32 a, float_status *s) +{ + return float32_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} + +uint16_t float64_to_uint16_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint16_scalbn(a, float_round_to_zero, 0, s); +} + +uint32_t float64_to_uint32_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint32_scalbn(a, float_round_to_zero, 0, s); +} + +uint64_t float64_to_uint64_round_to_zero(float64 a, float_status *s) +{ + return float64_to_uint64_scalbn(a, float_round_to_zero, 0, s); +} /* * Integer to float conversions @@ -1603,81 +2519,122 @@ FLOAT_TO_UINT(64, 64) * to the IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts int_to_float(int64_t a, float_status *status) +static FloatParts int_to_float(int64_t a, int scale, float_status *status) { - FloatParts r = {}; + FloatParts r = { .sign = false }; + if (a == 0) { r.cls = float_class_zero; - r.sign = false; - } else if (a == (1ULL << 63)) { - r.cls = float_class_normal; - r.sign = true; - r.frac = DECOMPOSED_IMPLICIT_BIT; - r.exp = 63; } else { - uint64_t f; - if (a < 0) { - f = -a; - r.sign = true; - } else { - f = a; - r.sign = false; - } - int shift = clz64(f) - 1; + uint64_t f = a; + int shift; + r.cls = float_class_normal; - r.exp = (DECOMPOSED_BINARY_POINT - shift); - r.frac = f << shift; + if (a < 0) { + f = -f; + r.sign = true; + } + shift = clz64(f) - 1; + scale = MIN(MAX(scale, -0x10000), 0x10000); + + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = (shift < 0 ? DECOMPOSED_IMPLICIT_BIT : f << shift); } return r; } +float16 int64_to_float16_scalbn(int64_t a, int scale, float_status *status) +{ + FloatParts pa = int_to_float(a, scale, status); + return float16_round_pack_canonical(pa, status); +} + +float16 int32_to_float16_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float16_scalbn(a, scale, status); +} + +float16 int16_to_float16_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float16_scalbn(a, scale, status); +} + float16 int64_to_float16(int64_t a, float_status *status) { - FloatParts pa = int_to_float(a, status); - return float16_round_pack_canonical(pa, status); + return int64_to_float16_scalbn(a, 0, status); } float16 int32_to_float16(int32_t a, float_status *status) { - return int64_to_float16(a, status); + return int64_to_float16_scalbn(a, 0, status); } float16 int16_to_float16(int16_t a, float_status *status) { - return int64_to_float16(a, status); + return int64_to_float16_scalbn(a, 0, status); +} + +float32 int64_to_float32_scalbn(int64_t a, int scale, float_status *status) +{ + FloatParts pa = int_to_float(a, scale, status); + return float32_round_pack_canonical(pa, status); +} + +float32 int32_to_float32_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float32_scalbn(a, scale, status); +} + +float32 int16_to_float32_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float32_scalbn(a, scale, status); } float32 int64_to_float32(int64_t a, float_status *status) { - FloatParts pa = int_to_float(a, status); - return float32_round_pack_canonical(pa, status); + return int64_to_float32_scalbn(a, 0, status); } float32 int32_to_float32(int32_t a, float_status *status) { - return int64_to_float32(a, status); + return int64_to_float32_scalbn(a, 0, status); } float32 int16_to_float32(int16_t a, float_status *status) { - return int64_to_float32(a, status); + return int64_to_float32_scalbn(a, 0, status); +} + +float64 int64_to_float64_scalbn(int64_t a, int scale, float_status *status) +{ + FloatParts pa = int_to_float(a, scale, status); + return float64_round_pack_canonical(pa, status); +} + +float64 int32_to_float64_scalbn(int32_t a, int scale, float_status *status) +{ + return int64_to_float64_scalbn(a, scale, status); +} + +float64 int16_to_float64_scalbn(int16_t a, int scale, float_status *status) +{ + return int64_to_float64_scalbn(a, scale, status); } float64 int64_to_float64(int64_t a, float_status *status) { - FloatParts pa = int_to_float(a, status); - return float64_round_pack_canonical(pa, status); + return int64_to_float64_scalbn(a, 0, status); } float64 int32_to_float64(int32_t a, float_status *status) { - return int64_to_float64(a, status); + return int64_to_float64_scalbn(a, 0, status); } float64 int16_to_float64(int16_t a, float_status *status) { - return int64_to_float64(a, status); + return int64_to_float64_scalbn(a, 0, status); } @@ -1689,73 +2646,120 @@ float64 int16_to_float64(int16_t a, float_status *status) * IEC/IEEE Standard for Binary Floating-Point Arithmetic. */ -static FloatParts uint_to_float(uint64_t a, float_status *status) +static FloatParts uint_to_float(uint64_t a, int scale, float_status *status) { - FloatParts r = { .sign = false}; + FloatParts r = { .sign = false }; if (a == 0) { r.cls = float_class_zero; } else { - int spare_bits = clz64(a) - 1; + scale = MIN(MAX(scale, -0x10000), 0x10000); r.cls = float_class_normal; - r.exp = DECOMPOSED_BINARY_POINT - spare_bits; - if (spare_bits < 0) { - shift64RightJamming(a, -spare_bits, &a); + if ((int64_t)a < 0) { + r.exp = DECOMPOSED_BINARY_POINT + 1 + scale; + shift64RightJamming(a, 1, &a); r.frac = a; } else { - r.frac = a << spare_bits; + int shift = clz64(a) - 1; + r.exp = DECOMPOSED_BINARY_POINT - shift + scale; + r.frac = a << shift; } } return r; } +float16 uint64_to_float16_scalbn(uint64_t a, int scale, float_status *status) +{ + FloatParts pa = uint_to_float(a, scale, status); + return float16_round_pack_canonical(pa, status); +} + +float16 uint32_to_float16_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float16_scalbn(a, scale, status); +} + +float16 uint16_to_float16_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float16_scalbn(a, scale, status); +} + float16 uint64_to_float16(uint64_t a, float_status *status) { - FloatParts pa = uint_to_float(a, status); - return float16_round_pack_canonical(pa, status); + return uint64_to_float16_scalbn(a, 0, status); } float16 uint32_to_float16(uint32_t a, float_status *status) { - return uint64_to_float16(a, status); + return uint64_to_float16_scalbn(a, 0, status); } float16 uint16_to_float16(uint16_t a, float_status *status) { - return uint64_to_float16(a, status); + return uint64_to_float16_scalbn(a, 0, status); +} + +float32 uint64_to_float32_scalbn(uint64_t a, int scale, float_status *status) +{ + FloatParts pa = uint_to_float(a, scale, status); + return float32_round_pack_canonical(pa, status); +} + +float32 uint32_to_float32_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float32_scalbn(a, scale, status); +} + +float32 uint16_to_float32_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float32_scalbn(a, scale, status); } float32 uint64_to_float32(uint64_t a, float_status *status) { - FloatParts pa = uint_to_float(a, status); - return float32_round_pack_canonical(pa, status); + return uint64_to_float32_scalbn(a, 0, status); } float32 uint32_to_float32(uint32_t a, float_status *status) { - return uint64_to_float32(a, status); + return uint64_to_float32_scalbn(a, 0, status); } float32 uint16_to_float32(uint16_t a, float_status *status) { - return uint64_to_float32(a, status); + return uint64_to_float32_scalbn(a, 0, status); +} + +float64 uint64_to_float64_scalbn(uint64_t a, int scale, float_status *status) +{ + FloatParts pa = uint_to_float(a, scale, status); + return float64_round_pack_canonical(pa, status); +} + +float64 uint32_to_float64_scalbn(uint32_t a, int scale, float_status *status) +{ + return uint64_to_float64_scalbn(a, scale, status); +} + +float64 uint16_to_float64_scalbn(uint16_t a, int scale, float_status *status) +{ + return uint64_to_float64_scalbn(a, scale, status); } float64 uint64_to_float64(uint64_t a, float_status *status) { - FloatParts pa = uint_to_float(a, status); - return float64_round_pack_canonical(pa, status); + return uint64_to_float64_scalbn(a, 0, status); } float64 uint32_to_float64(uint32_t a, float_status *status) { - return uint64_to_float64(a, status); + return uint64_to_float64_scalbn(a, 0, status); } float64 uint16_to_float64(uint16_t a, float_status *status) { - return uint64_to_float64(a, status); + return uint64_to_float64_scalbn(a, 0, status); } /* Float Min/Max */ @@ -1937,28 +2941,109 @@ static int compare_floats(FloatParts a, FloatParts b, bool is_quiet, } } -#define COMPARE(sz) \ -int float ## sz ## _compare(float ## sz a, float ## sz b, \ - float_status *s) \ +#define COMPARE(name, attr, sz) \ +static int attr \ +name(float ## sz a, float ## sz b, bool is_quiet, float_status *s) \ { \ FloatParts pa = float ## sz ## _unpack_canonical(a, s); \ FloatParts pb = float ## sz ## _unpack_canonical(b, s); \ - return compare_floats(pa, pb, false, s); \ -} \ -int float ## sz ## _compare_quiet(float ## sz a, float ## sz b, \ - float_status *s) \ -{ \ - FloatParts pa = float ## sz ## _unpack_canonical(a, s); \ - FloatParts pb = float ## sz ## _unpack_canonical(b, s); \ - return compare_floats(pa, pb, true, s); \ + return compare_floats(pa, pb, is_quiet, s); \ } -COMPARE(16) -COMPARE(32) -COMPARE(64) +COMPARE(soft_f16_compare, QEMU_FLATTEN, 16) +COMPARE(soft_f32_compare, QEMU_SOFTFLOAT_ATTR, 32) +COMPARE(soft_f64_compare, QEMU_SOFTFLOAT_ATTR, 64) #undef COMPARE +int float16_compare(float16 a, float16 b, float_status *s) +{ + return soft_f16_compare(a, b, false, s); +} + +int float16_compare_quiet(float16 a, float16 b, float_status *s) +{ + return soft_f16_compare(a, b, true, s); +} + +static int QEMU_FLATTEN +f32_compare(float32 xa, float32 xb, bool is_quiet, float_status *s) +{ + union_float32 ua, ub; + + ua.s = xa; + ub.s = xb; + + if (QEMU_NO_HARDFLOAT) { + goto soft; + } + + float32_input_flush2(&ua.s, &ub.s, s); + if (isgreaterequal(ua.h, ub.h)) { + if (isgreater(ua.h, ub.h)) { + return float_relation_greater; + } + return float_relation_equal; + } + if (likely(isless(ua.h, ub.h))) { + return float_relation_less; + } + /* The only condition remaining is unordered. + * Fall through to set flags. + */ + soft: + return soft_f32_compare(ua.s, ub.s, is_quiet, s); +} + +int float32_compare(float32 a, float32 b, float_status *s) +{ + return f32_compare(a, b, false, s); +} + +int float32_compare_quiet(float32 a, float32 b, float_status *s) +{ + return f32_compare(a, b, true, s); +} + +static int QEMU_FLATTEN +f64_compare(float64 xa, float64 xb, bool is_quiet, float_status *s) +{ + union_float64 ua, ub; + + ua.s = xa; + ub.s = xb; + + if (QEMU_NO_HARDFLOAT) { + goto soft; + } + + float64_input_flush2(&ua.s, &ub.s, s); + if (isgreaterequal(ua.h, ub.h)) { + if (isgreater(ua.h, ub.h)) { + return float_relation_greater; + } + return float_relation_equal; + } + if (likely(isless(ua.h, ub.h))) { + return float_relation_less; + } + /* The only condition remaining is unordered. + * Fall through to set flags. + */ + soft: + return soft_f64_compare(ua.s, ub.s, is_quiet, s); +} + +int float64_compare(float64 a, float64 b, float_status *s) +{ + return f64_compare(a, b, false, s); +} + +int float64_compare_quiet(float64 a, float64 b, float_status *s) +{ + return f64_compare(a, b, true, s); +} + /* Multiply A by 2 raised to the power N. */ static FloatParts scalbn_decomposed(FloatParts a, int n, float_status *s) { @@ -2071,27 +3156,83 @@ static FloatParts sqrt_float(FloatParts a, float_status *s, const FloatFmt *p) return a; } -float16 __attribute__((flatten)) float16_sqrt(float16 a, float_status *status) +float16 QEMU_FLATTEN float16_sqrt(float16 a, float_status *status) { FloatParts pa = float16_unpack_canonical(a, status); FloatParts pr = sqrt_float(pa, status, &float16_params); return float16_round_pack_canonical(pr, status); } -float32 __attribute__((flatten)) float32_sqrt(float32 a, float_status *status) +static float32 QEMU_SOFTFLOAT_ATTR +soft_f32_sqrt(float32 a, float_status *status) { FloatParts pa = float32_unpack_canonical(a, status); FloatParts pr = sqrt_float(pa, status, &float32_params); return float32_round_pack_canonical(pr, status); } -float64 __attribute__((flatten)) float64_sqrt(float64 a, float_status *status) +static float64 QEMU_SOFTFLOAT_ATTR +soft_f64_sqrt(float64 a, float_status *status) { FloatParts pa = float64_unpack_canonical(a, status); FloatParts pr = sqrt_float(pa, status, &float64_params); return float64_round_pack_canonical(pr, status); } +float32 QEMU_FLATTEN float32_sqrt(float32 xa, float_status *s) +{ + union_float32 ua, ur; + + ua.s = xa; + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float32_input_flush1(&ua.s, s); + if (QEMU_HARDFLOAT_1F32_USE_FP) { + if (unlikely(!(fpclassify(ua.h) == FP_NORMAL || + fpclassify(ua.h) == FP_ZERO) || + signbit(ua.h))) { + goto soft; + } + } else if (unlikely(!float32_is_zero_or_normal(ua.s) || + float32_is_neg(ua.s))) { + goto soft; + } + ur.h = sqrtf(ua.h); + return ur.s; + + soft: + return soft_f32_sqrt(ua.s, s); +} + +float64 QEMU_FLATTEN float64_sqrt(float64 xa, float_status *s) +{ + union_float64 ua, ur; + + ua.s = xa; + if (unlikely(!can_use_fpu(s))) { + goto soft; + } + + float64_input_flush1(&ua.s, s); + if (QEMU_HARDFLOAT_1F64_USE_FP) { + if (unlikely(!(fpclassify(ua.h) == FP_NORMAL || + fpclassify(ua.h) == FP_ZERO) || + signbit(ua.h))) { + goto soft; + } + } else if (unlikely(!float64_is_zero_or_normal(ua.s) || + float64_is_neg(ua.s))) { + goto soft; + } + ur.h = sqrt(ua.h); + return ur.s; + + soft: + return soft_f64_sqrt(ua.s, s); +} + /*---------------------------------------------------------------------------- | The pattern for a default generated NaN. *----------------------------------------------------------------------------*/ @@ -2199,6 +3340,9 @@ static int32_t roundAndPackInt32(flag zSign, uint64_t absZ, float_status *status case float_round_down: roundIncrement = zSign ? 0x7f : 0; break; + case float_round_to_odd: + roundIncrement = absZ & 0x80 ? 0 : 0x7f; + break; default: abort(); } @@ -2253,6 +3397,9 @@ static int64_t roundAndPackInt64(flag zSign, uint64_t absZ0, uint64_t absZ1, case float_round_down: increment = zSign && absZ1; break; + case float_round_to_odd: + increment = !(absZ0 & 1) && absZ1; + break; default: abort(); } @@ -2309,6 +3456,9 @@ static int64_t roundAndPackUint64(flag zSign, uint64_t absZ0, case float_round_down: increment = zSign && absZ1; break; + case float_round_to_odd: + increment = !(absZ0 & 1) && absZ1; + break; default: abort(); } @@ -2359,7 +3509,7 @@ static void { int8_t shiftCount; - shiftCount = countLeadingZeros32( aSig ) - 8; + shiftCount = clz32(aSig) - 8; *zSigPtr = aSig<flush_to_zero) { @@ -2440,6 +3595,13 @@ static float32 roundAndPackFloat32(flag zSign, int zExp, uint32_t zSig, if (isTiny && roundBits) { float_raise(float_flag_underflow, status); } + if (roundingMode == float_round_to_odd) { + /* + * For round-to-odd case, the roundIncrement depends on + * zSig which just changed. + */ + roundIncrement = zSig & 0x80 ? 0 : 0x7f; + } } } if (roundBits) { @@ -2467,7 +3629,7 @@ static float32 { int8_t shiftCount; - shiftCount = countLeadingZeros32( zSig ) - 1; + shiftCount = clz32(zSig) - 1; return roundAndPackFloat32(zSign, zExp - shiftCount, zSig<>( - shiftCount ); *zSig1Ptr = aSig1<<( shiftCount & 63 ); @@ -2984,7 +4146,7 @@ static void *zExpPtr = - shiftCount - 63; } else { - shiftCount = countLeadingZeros64( aSig0 ) - 15; + shiftCount = clz64(aSig0) - 15; shortShift128Left( aSig0, aSig1, shiftCount, zSig0Ptr, zSig1Ptr ); *zExpPtr = 1 - shiftCount; } @@ -3173,7 +4335,7 @@ static float128 normalizeRoundAndPackFloat128(flag zSign, int32_t zExp, zSig1 = 0; zExp -= 64; } - shiftCount = countLeadingZeros64( zSig0 ) - 15; + shiftCount = clz64(zSig0) - 15; if ( 0 <= shiftCount ) { zSig2 = 0; shortShift128Left( zSig0, zSig1, shiftCount, &zSig0, &zSig1 ); @@ -3205,7 +4367,7 @@ floatx80 int32_to_floatx80(int32_t a, float_status *status) if ( a == 0 ) return packFloatx80( 0, 0, 0 ); zSign = ( a < 0 ); absA = zSign ? - a : a; - shiftCount = countLeadingZeros32( absA ) + 32; + shiftCount = clz32(absA) + 32; zSig = absA; return packFloatx80( zSign, 0x403E - shiftCount, zSig< 0xffffffff) { + res = 0xffffffff; + } else { + return v; + } + set_float_exception_flags(old_exc_flags, status); + float_raise(float_flag_invalid, status); + return res; +} + /*---------------------------------------------------------------------------- | Returns the result of converting the quadruple-precision floating-point | value `a' to the single-precision floating-point format. The conversion @@ -5843,6 +7034,15 @@ float128 float128_round_to_int(float128 a, float_status *status) add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low); } break; + case float_round_to_odd: + /* + * Note that if lastBitMask == 0, the last bit is the lsb + * of high, and roundBitsMask == -1. + */ + if ((lastBitMask ? z.low & lastBitMask : z.high & 1) == 0) { + add128(z.high, z.low, 0, roundBitsMask, &z.high, &z.low); + } + break; default: abort(); } @@ -5854,7 +7054,7 @@ float128 float128_round_to_int(float128 a, float_status *status) status->float_exception_flags |= float_flag_inexact; aSign = extractFloat128Sign( a ); switch (status->float_rounding_mode) { - case float_round_nearest_even: + case float_round_nearest_even: if ( ( aExp == 0x3FFE ) && ( extractFloat128Frac0( a ) | extractFloat128Frac1( a ) ) @@ -5867,14 +7067,17 @@ float128 float128_round_to_int(float128 a, float_status *status) return packFloat128(aSign, 0x3FFF, 0, 0); } break; - case float_round_down: + case float_round_down: return aSign ? packFloat128( 1, 0x3FFF, 0, 0 ) : packFloat128( 0, 0, 0, 0 ); - case float_round_up: + case float_round_up: return aSign ? packFloat128( 1, 0, 0, 0 ) : packFloat128( 0, 0x3FFF, 0, 0 ); + + case float_round_to_odd: + return packFloat128(aSign, 0x3FFF, 0, 0); } return packFloat128( aSign, 0, 0, 0 ); } @@ -5907,6 +7110,12 @@ float128 float128_round_to_int(float128 a, float_status *status) z.high += roundBitsMask; } break; + case float_round_to_odd: + if ((z.high & lastBitMask) == 0) { + z.high |= (a.low != 0); + z.high += roundBitsMask; + } + break; default: abort(); } @@ -6871,3 +8080,24 @@ float128 float128_scalbn(float128 a, int n, float_status *status) , status); } + +static void __attribute__((constructor)) softfloat_init(void) +{ + union_float64 ua, ub, uc, ur; + + if (QEMU_NO_HARDFLOAT) { + return; + } + /* + * Test that the host's FMA is not obviously broken. For example, + * glibc < 2.23 can perform an incorrect FMA on certain hosts; see + * https://sourceware.org/bugzilla/show_bug.cgi?id=13304 + */ + ua.s = 0x0020000000000001ULL; + ub.s = 0x3ca0000000000000ULL; + uc.s = 0x0020000000000000ULL; + ur.h = fma(ua.h, ub.h, uc.h); + if (ur.s != 0x0020000000000001ULL) { + force_soft_fma = true; + } +} diff --git a/fsdev/Makefile.objs b/fsdev/Makefile.objs index fb38017c0b..24bbb3e75c 100644 --- a/fsdev/Makefile.objs +++ b/fsdev/Makefile.objs @@ -1,7 +1,10 @@ # Lots of the fsdev/9pcode is pulled in by vl.c via qemu_fsdev_add. # only pull in the actual 9p backend if we also enabled virtio or xen. -common-obj-$(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO),$(CONFIG_XEN))) = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o -common-obj-$(call lnot,$(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO),$(CONFIG_XEN)))) = qemu-fsdev-dummy.o +ifeq ($(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO_9P),$(CONFIG_XEN))),y) +common-obj-y = qemu-fsdev.o 9p-marshal.o 9p-iov-marshal.o +else +common-obj-y = qemu-fsdev-dummy.o +endif common-obj-y += qemu-fsdev-opts.o qemu-fsdev-throttle.o # Toplevel always builds this; targets without virtio will put it in diff --git a/fsdev/qemu-fsdev-dummy.c b/fsdev/qemu-fsdev-dummy.c index 6dc0fbc4c4..489cd29081 100644 --- a/fsdev/qemu-fsdev-dummy.c +++ b/fsdev/qemu-fsdev-dummy.c @@ -15,7 +15,7 @@ #include "qemu/config-file.h" #include "qemu/module.h" -int qemu_fsdev_add(QemuOpts *opts) +int qemu_fsdev_add(QemuOpts *opts, Error **errp) { return 0; } diff --git a/fsdev/qemu-fsdev.c b/fsdev/qemu-fsdev.c index 8a4afbffbd..54cb36a212 100644 --- a/fsdev/qemu-fsdev.c +++ b/fsdev/qemu-fsdev.c @@ -18,19 +18,16 @@ #include "qemu/error-report.h" #include "qemu/option.h" -static QTAILQ_HEAD(FsDriverEntry_head, FsDriverListEntry) fsdriver_entries = +static QTAILQ_HEAD(, FsDriverListEntry) fsdriver_entries = QTAILQ_HEAD_INITIALIZER(fsdriver_entries); static FsDriverTable FsDrivers[] = { { .name = "local", .ops = &local_ops}, -#ifdef CONFIG_OPEN_BY_HANDLE - { .name = "handle", .ops = &handle_ops}, -#endif { .name = "synth", .ops = &synth_ops}, { .name = "proxy", .ops = &proxy_ops}, }; -int qemu_fsdev_add(QemuOpts *opts) +int qemu_fsdev_add(QemuOpts *opts, Error **errp) { int i; struct FsDriverListEntry *fsle; @@ -38,10 +35,9 @@ int qemu_fsdev_add(QemuOpts *opts) const char *fsdriver = qemu_opt_get(opts, "fsdriver"); const char *writeout = qemu_opt_get(opts, "writeout"); bool ro = qemu_opt_get_bool(opts, "readonly", 0); - Error *local_err = NULL; if (!fsdev_id) { - error_report("fsdev: No id specified"); + error_setg(errp, "fsdev: No id specified"); return -1; } @@ -53,11 +49,11 @@ int qemu_fsdev_add(QemuOpts *opts) } if (i == ARRAY_SIZE(FsDrivers)) { - error_report("fsdev: fsdriver %s not found", fsdriver); + error_setg(errp, "fsdev: fsdriver %s not found", fsdriver); return -1; } } else { - error_report("fsdev: No fsdriver specified"); + error_setg(errp, "fsdev: No fsdriver specified"); return -1; } @@ -76,8 +72,7 @@ int qemu_fsdev_add(QemuOpts *opts) } if (fsle->fse.ops->parse_opts) { - if (fsle->fse.ops->parse_opts(opts, &fsle->fse, &local_err)) { - error_report_err(local_err); + if (fsle->fse.ops->parse_opts(opts, &fsle->fse, errp)) { g_free(fsle->fse.fsdev_id); g_free(fsle); return -1; diff --git a/fsdev/qemu-fsdev.h b/fsdev/qemu-fsdev.h index 65e4b1cfab..d9716b4144 100644 --- a/fsdev/qemu-fsdev.h +++ b/fsdev/qemu-fsdev.h @@ -38,7 +38,7 @@ typedef struct FsDriverListEntry { QTAILQ_ENTRY(FsDriverListEntry) next; } FsDriverListEntry; -int qemu_fsdev_add(QemuOpts *opts); +int qemu_fsdev_add(QemuOpts *opts, Error **errp); FsDriverEntry *get_fsdev_fsentry(char *id); extern FileOperations local_ops; extern FileOperations handle_ops; diff --git a/gdb-xml/i386-32bit-core.xml b/gdb-xml/i386-32bit-core.xml deleted file mode 100644 index 7aeeeca3b2..0000000000 --- a/gdb-xml/i386-32bit-core.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gdb-xml/i386-32bit-sse.xml b/gdb-xml/i386-32bit-sse.xml deleted file mode 100644 index 57678473d6..0000000000 --- a/gdb-xml/i386-32bit-sse.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gdb-xml/i386-32bit.xml b/gdb-xml/i386-32bit.xml index 956fc7f45f..872fcea9c2 100644 --- a/gdb-xml/i386-32bit.xml +++ b/gdb-xml/i386-32bit.xml @@ -8,7 +8,185 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/i386-64bit-core.xml b/gdb-xml/i386-64bit-core.xml deleted file mode 100644 index 5088d84ceb..0000000000 --- a/gdb-xml/i386-64bit-core.xml +++ /dev/null @@ -1,73 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gdb-xml/i386-64bit-sse.xml b/gdb-xml/i386-64bit-sse.xml deleted file mode 100644 index e86efc9ce5..0000000000 --- a/gdb-xml/i386-64bit-sse.xml +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gdb-xml/i386-64bit.xml b/gdb-xml/i386-64bit.xml index 0b2f00ccbe..6d88969211 100644 --- a/gdb-xml/i386-64bit.xml +++ b/gdb-xml/i386-64bit.xml @@ -5,10 +5,212 @@ are permitted in any medium without royalty provided the copyright notice and this notice are preserved. --> - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/riscv-32bit-cpu.xml b/gdb-xml/riscv-32bit-cpu.xml new file mode 100644 index 0000000000..0d07aaec85 --- /dev/null +++ b/gdb-xml/riscv-32bit-cpu.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/riscv-32bit-csr.xml b/gdb-xml/riscv-32bit-csr.xml new file mode 100644 index 0000000000..da1bf19e2f --- /dev/null +++ b/gdb-xml/riscv-32bit-csr.xml @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/riscv-32bit-fpu.xml b/gdb-xml/riscv-32bit-fpu.xml new file mode 100644 index 0000000000..1eaae9119e --- /dev/null +++ b/gdb-xml/riscv-32bit-fpu.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/riscv-64bit-cpu.xml b/gdb-xml/riscv-64bit-cpu.xml new file mode 100644 index 0000000000..b8aa424ae4 --- /dev/null +++ b/gdb-xml/riscv-64bit-cpu.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/riscv-64bit-csr.xml b/gdb-xml/riscv-64bit-csr.xml new file mode 100644 index 0000000000..6aa4bed9f5 --- /dev/null +++ b/gdb-xml/riscv-64bit-csr.xml @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdb-xml/riscv-64bit-fpu.xml b/gdb-xml/riscv-64bit-fpu.xml new file mode 100644 index 0000000000..794854cc01 --- /dev/null +++ b/gdb-xml/riscv-64bit-fpu.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/gdbstub.c b/gdbstub.c index d6ab95006c..d54abd17cc 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -20,7 +20,6 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/cutils.h" -#include "cpu.h" #include "trace-root.h" #ifdef CONFIG_USER_ONLY #include "qemu.h" @@ -30,6 +29,7 @@ #include "chardev/char-fe.h" #include "sysemu/sysemu.h" #include "exec/gdbstub.h" +#include "hw/cpu/cluster.h" #endif #define MAX_PACKET_LENGTH 4096 @@ -297,6 +297,13 @@ typedef struct GDBRegisterState { struct GDBRegisterState *next; } GDBRegisterState; +typedef struct GDBProcess { + uint32_t pid; + bool attached; + + char target_xml[1024]; +} GDBProcess; + enum RSState { RS_INACTIVE, RS_IDLE, @@ -325,6 +332,9 @@ typedef struct GDBState { CharBackend chr; Chardev *mon_chr; #endif + bool multiprocess; + GDBProcess *processes; + int process_num; char syscall_buf[256]; gdb_syscall_complete_cb current_syscall_cb; } GDBState; @@ -632,13 +642,162 @@ static int memtox(char *buf, const char *mem, int len) return p - buf; } -static const char *get_feature_xml(const char *p, const char **newp, - CPUClass *cc) +static uint32_t gdb_get_cpu_pid(const GDBState *s, CPUState *cpu) +{ + /* TODO: In user mode, we should use the task state PID */ + if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) { + /* Return the default process' PID */ + return s->processes[s->process_num - 1].pid; + } + return cpu->cluster_index + 1; +} + +static GDBProcess *gdb_get_process(const GDBState *s, uint32_t pid) +{ + int i; + + if (!pid) { + /* 0 means any process, we take the first one */ + return &s->processes[0]; + } + + for (i = 0; i < s->process_num; i++) { + if (s->processes[i].pid == pid) { + return &s->processes[i]; + } + } + + return NULL; +} + +static GDBProcess *gdb_get_cpu_process(const GDBState *s, CPUState *cpu) +{ + return gdb_get_process(s, gdb_get_cpu_pid(s, cpu)); +} + +static CPUState *find_cpu(uint32_t thread_id) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (cpu_gdb_index(cpu) == thread_id) { + return cpu; + } + } + + return NULL; +} + +static CPUState *get_first_cpu_in_process(const GDBState *s, + GDBProcess *process) +{ + CPUState *cpu; + + CPU_FOREACH(cpu) { + if (gdb_get_cpu_pid(s, cpu) == process->pid) { + return cpu; + } + } + + return NULL; +} + +static CPUState *gdb_next_cpu_in_process(const GDBState *s, CPUState *cpu) +{ + uint32_t pid = gdb_get_cpu_pid(s, cpu); + cpu = CPU_NEXT(cpu); + + while (cpu) { + if (gdb_get_cpu_pid(s, cpu) == pid) { + break; + } + + cpu = CPU_NEXT(cpu); + } + + return cpu; +} + +/* Return the cpu following @cpu, while ignoring unattached processes. */ +static CPUState *gdb_next_attached_cpu(const GDBState *s, CPUState *cpu) +{ + cpu = CPU_NEXT(cpu); + + while (cpu) { + if (gdb_get_cpu_process(s, cpu)->attached) { + break; + } + + cpu = CPU_NEXT(cpu); + } + + return cpu; +} + +/* Return the first attached cpu */ +static CPUState *gdb_first_attached_cpu(const GDBState *s) +{ + CPUState *cpu = first_cpu; + GDBProcess *process = gdb_get_cpu_process(s, cpu); + + if (!process->attached) { + return gdb_next_attached_cpu(s, cpu); + } + + return cpu; +} + +static CPUState *gdb_get_cpu(const GDBState *s, uint32_t pid, uint32_t tid) +{ + GDBProcess *process; + CPUState *cpu; + + if (!pid && !tid) { + /* 0 means any process/thread, we take the first attached one */ + return gdb_first_attached_cpu(s); + } else if (pid && !tid) { + /* any thread in a specific process */ + process = gdb_get_process(s, pid); + + if (process == NULL) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return get_first_cpu_in_process(s, process); + } else { + /* a specific thread */ + cpu = find_cpu(tid); + + if (cpu == NULL) { + return NULL; + } + + process = gdb_get_cpu_process(s, cpu); + + if (pid && process->pid != pid) { + return NULL; + } + + if (!process->attached) { + return NULL; + } + + return cpu; + } +} + +static const char *get_feature_xml(const GDBState *s, const char *p, + const char **newp, GDBProcess *process) { size_t len; int i; const char *name; - static char target_xml[1024]; + CPUState *cpu = get_first_cpu_in_process(s, process); + CPUClass *cc = CPU_GET_CLASS(cpu); len = 0; while (p[len] && p[len] != ':') @@ -647,36 +806,37 @@ static const char *get_feature_xml(const char *p, const char **newp, name = NULL; if (strncmp(p, "target.xml", len) == 0) { - /* Generate the XML description for this CPU. */ - if (!target_xml[0]) { - GDBRegisterState *r; - CPUState *cpu = first_cpu; + char *buf = process->target_xml; + const size_t buf_sz = sizeof(process->target_xml); - pstrcat(target_xml, sizeof(target_xml), + /* Generate the XML description for this CPU. */ + if (!buf[0]) { + GDBRegisterState *r; + + pstrcat(buf, buf_sz, "" "" ""); 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), ""); + pstrcat(buf, buf_sz, ""); + pstrcat(buf, buf_sz, arch); + pstrcat(buf, buf_sz, ""); g_free(arch); } - pstrcat(target_xml, sizeof(target_xml), "gdb_core_xml_file); - pstrcat(target_xml, sizeof(target_xml), "\"/>"); + pstrcat(buf, buf_sz, "gdb_core_xml_file); + pstrcat(buf, buf_sz, "\"/>"); for (r = cpu->gdb_regs; r; r = r->next) { - pstrcat(target_xml, sizeof(target_xml), "xml); - pstrcat(target_xml, sizeof(target_xml), "\"/>"); + pstrcat(buf, buf_sz, "xml); + pstrcat(buf, buf_sz, "\"/>"); } - pstrcat(target_xml, sizeof(target_xml), ""); + pstrcat(buf, buf_sz, ""); } - return target_xml; + return buf; } if (cc->gdb_get_dynamic_xml) { - CPUState *cpu = first_cpu; char *xmlname = g_strndup(p, len); const char *xml = cc->gdb_get_dynamic_xml(cpu, xmlname); @@ -863,6 +1023,24 @@ static int gdb_breakpoint_remove(target_ulong addr, target_ulong len, int type) } } +static inline void gdb_cpu_breakpoint_remove_all(CPUState *cpu) +{ + cpu_breakpoint_remove_all(cpu, BP_GDB); +#ifndef CONFIG_USER_ONLY + cpu_watchpoint_remove_all(cpu, BP_GDB); +#endif +} + +static void gdb_process_breakpoint_remove_all(const GDBState *s, GDBProcess *p) +{ + CPUState *cpu = get_first_cpu_in_process(s, p); + + while (cpu) { + gdb_cpu_breakpoint_remove_all(cpu); + cpu = gdb_next_cpu_in_process(s, cpu); + } +} + static void gdb_breakpoint_remove_all(void) { CPUState *cpu; @@ -873,10 +1051,7 @@ static void gdb_breakpoint_remove_all(void) } CPU_FOREACH(cpu) { - cpu_breakpoint_remove_all(cpu, BP_GDB); -#ifndef CONFIG_USER_ONLY - cpu_watchpoint_remove_all(cpu, BP_GDB); -#endif + gdb_cpu_breakpoint_remove_all(cpu); } } @@ -888,17 +1063,71 @@ static void gdb_set_cpu_pc(GDBState *s, target_ulong pc) cpu_set_pc(cpu, pc); } -static CPUState *find_cpu(uint32_t thread_id) +static char *gdb_fmt_thread_id(const GDBState *s, CPUState *cpu, + char *buf, size_t buf_size) { - CPUState *cpu; - - CPU_FOREACH(cpu) { - if (cpu_gdb_index(cpu) == thread_id) { - return cpu; - } + if (s->multiprocess) { + snprintf(buf, buf_size, "p%02x.%02x", + gdb_get_cpu_pid(s, cpu), cpu_gdb_index(cpu)); + } else { + snprintf(buf, buf_size, "%02x", cpu_gdb_index(cpu)); } - return NULL; + return buf; +} + +typedef enum GDBThreadIdKind { + GDB_ONE_THREAD = 0, + GDB_ALL_THREADS, /* One process, all threads */ + GDB_ALL_PROCESSES, + GDB_READ_THREAD_ERR +} GDBThreadIdKind; + +static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, + uint32_t *pid, uint32_t *tid) +{ + unsigned long p, t; + int ret; + + if (*buf == 'p') { + buf++; + ret = qemu_strtoul(buf, &buf, 16, &p); + + if (ret) { + return GDB_READ_THREAD_ERR; + } + + /* Skip '.' */ + buf++; + } else { + p = 1; + } + + ret = qemu_strtoul(buf, &buf, 16, &t); + + if (ret) { + return GDB_READ_THREAD_ERR; + } + + *end_buf = buf; + + if (p == -1) { + return GDB_ALL_PROCESSES; + } + + if (pid) { + *pid = p; + } + + if (t == -1) { + return GDB_ALL_THREADS; + } + + if (tid) { + *tid = t; + } + + return GDB_ONE_THREAD; } static int is_query_packet(const char *p, const char *query, char separator) @@ -916,11 +1145,14 @@ static int is_query_packet(const char *p, const char *query, char separator) */ static int gdb_handle_vcont(GDBState *s, const char *p) { - int res, idx, signal = 0; + int res, signal = 0; char cur_action; char *newstates; unsigned long tmp; + uint32_t pid, tid; + GDBProcess *process; CPUState *cpu; + GDBThreadIdKind kind; #ifdef CONFIG_USER_ONLY int max_cpus = 1; /* global variable max_cpus exists only in system mode */ @@ -962,25 +1194,57 @@ static int gdb_handle_vcont(GDBState *s, const char *p) res = -ENOTSUP; goto out; } - /* thread specification. special values: (none), -1 = all; 0 = any */ - if ((p[0] == ':' && p[1] == '-' && p[2] == '1') || (p[0] != ':')) { - if (*p == ':') { - p += 3; - } - for (idx = 0; idx < max_cpus; idx++) { - if (newstates[idx] == 1) { - newstates[idx] = cur_action; + + if (*p == '\0' || *p == ';') { + /* + * No thread specifier, action is on "all threads". The + * specification is unclear regarding the process to act on. We + * choose all processes. + */ + kind = GDB_ALL_PROCESSES; + } else if (*p++ == ':') { + kind = read_thread_id(p, &p, &pid, &tid); + } else { + res = -ENOTSUP; + goto out; + } + + switch (kind) { + case GDB_READ_THREAD_ERR: + res = -EINVAL; + goto out; + + case GDB_ALL_PROCESSES: + cpu = gdb_first_attached_cpu(s); + while (cpu) { + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; } + + cpu = gdb_next_attached_cpu(s, cpu); } - } else if (*p == ':') { - p++; - res = qemu_strtoul(p, &p, 16, &tmp); - if (res) { + break; + + case GDB_ALL_THREADS: + process = gdb_get_process(s, pid); + + if (!process->attached) { + res = -EINVAL; goto out; } - /* 0 means any thread, so we pick the first valid CPU */ - cpu = tmp ? find_cpu(tmp) : first_cpu; + cpu = get_first_cpu_in_process(s, process); + while (cpu) { + if (newstates[cpu->cpu_index] == 1) { + newstates[cpu->cpu_index] = cur_action; + } + + cpu = gdb_next_cpu_in_process(s, cpu); + } + break; + + case GDB_ONE_THREAD: + cpu = gdb_get_cpu(s, pid, tid); /* invalid CPU/thread specified */ if (!cpu) { @@ -992,6 +1256,7 @@ static int gdb_handle_vcont(GDBState *s, const char *p) if (newstates[cpu->cpu_index] == 1) { newstates[cpu->cpu_index] = cur_action; } + break; } } s->signal = signal; @@ -1006,24 +1271,30 @@ out: static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; + GDBProcess *process; CPUClass *cc; const char *p; - uint32_t thread; + uint32_t pid, tid; int ch, reg_size, type, res; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; + char thread_id[16]; uint8_t *registers; target_ulong addr, len; + GDBThreadIdKind thread_kind; trace_gdbstub_io_command(line_buf); p = line_buf; ch = *p++; switch(ch) { + case '!': + put_packet(s, "OK"); + break; case '?': /* TODO: Make this return the correct value for user-mode. */ - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", GDB_SIGNAL_TRAP, - cpu_gdb_index(s->c_cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, + gdb_fmt_thread_id(s, s->c_cpu, thread_id, sizeof(thread_id))); put_packet(s, buf); /* Remove all the breakpoints when this query is issued, * because gdb is doing and initial connect and the state @@ -1063,6 +1334,46 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) goto unknown_command; } break; + } else if (strncmp(p, "Attach;", 7) == 0) { + unsigned long pid; + + p += 7; + + if (qemu_strtoul(p, &p, 16, &pid)) { + put_packet(s, "E22"); + break; + } + + process = gdb_get_process(s, pid); + + if (process == NULL) { + put_packet(s, "E22"); + break; + } + + cpu = get_first_cpu_in_process(s, process); + + if (cpu == NULL) { + /* Refuse to attach an empty process */ + put_packet(s, "E22"); + break; + } + + process->attached = true; + + s->g_cpu = cpu; + s->c_cpu = cpu; + + snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); + + put_packet(s, buf); + break; + } else if (strncmp(p, "Kill;", 5) == 0) { + /* Kill the target */ + put_packet(s, "OK"); + error_report("QEMU: Terminated via GDBstub"); + exit(0); } else { goto unknown_command; } @@ -1072,9 +1383,40 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) exit(0); case 'D': /* Detach packet */ - gdb_breakpoint_remove_all(); - gdb_syscall_mode = GDB_SYS_DISABLED; - gdb_continue(s); + pid = 1; + + if (s->multiprocess) { + unsigned long lpid; + if (*p != ';') { + put_packet(s, "E22"); + break; + } + + if (qemu_strtoul(p + 1, &p, 16, &lpid)) { + put_packet(s, "E22"); + break; + } + + pid = lpid; + } + + process = gdb_get_process(s, pid); + gdb_process_breakpoint_remove_all(s, process); + process->attached = false; + + if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { + s->c_cpu = gdb_first_attached_cpu(s); + } + + if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { + s->g_cpu = gdb_first_attached_cpu(s); + } + + if (s->c_cpu == NULL) { + /* No more process attached */ + gdb_syscall_mode = GDB_SYS_DISABLED; + gdb_continue(s); + } put_packet(s, "OK"); break; case 's': @@ -1221,12 +1563,18 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; case 'H': type = *p++; - thread = strtoull(p, (char **)&p, 16); - if (thread == -1 || thread == 0) { + + thread_kind = read_thread_id(p, &p, &pid, &tid); + if (thread_kind == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + + if (thread_kind != GDB_ONE_THREAD) { put_packet(s, "OK"); break; } - cpu = find_cpu(thread); + cpu = gdb_get_cpu(s, pid, tid); if (cpu == NULL) { put_packet(s, "E22"); break; @@ -1246,8 +1594,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'T': - thread = strtoull(p, (char **)&p, 16); - cpu = find_cpu(thread); + thread_kind = read_thread_id(p, &p, &pid, &tid); + if (thread_kind == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + cpu = gdb_get_cpu(s, pid, tid); if (cpu != NULL) { put_packet(s, "OK"); @@ -1281,31 +1633,55 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); break; } else if (strcmp(p,"C") == 0) { - /* "Current thread" remains vague in the spec, so always return - * the first CPU (gdb returns the first thread). */ - put_packet(s, "QC1"); + /* + * "Current thread" remains vague in the spec, so always return + * the first thread of the current process (gdb returns the + * first thread). + */ + cpu = get_first_cpu_in_process(s, gdb_get_cpu_process(s, s->g_cpu)); + snprintf(buf, sizeof(buf), "QC%s", + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id))); + put_packet(s, buf); break; } else if (strcmp(p,"fThreadInfo") == 0) { - s->query_cpu = first_cpu; + s->query_cpu = gdb_first_attached_cpu(s); goto report_cpuinfo; } else if (strcmp(p,"sThreadInfo") == 0) { report_cpuinfo: if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%x", cpu_gdb_index(s->query_cpu)); + snprintf(buf, sizeof(buf), "m%s", + gdb_fmt_thread_id(s, s->query_cpu, + thread_id, sizeof(thread_id))); put_packet(s, buf); - s->query_cpu = CPU_NEXT(s->query_cpu); + s->query_cpu = gdb_next_attached_cpu(s, s->query_cpu); } else put_packet(s, "l"); break; } else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { - thread = strtoull(p+16, (char **)&p, 16); - cpu = find_cpu(thread); + if (read_thread_id(p + 16, &p, &pid, &tid) == GDB_READ_THREAD_ERR) { + put_packet(s, "E22"); + break; + } + cpu = gdb_get_cpu(s, pid, tid); if (cpu != NULL) { cpu_synchronize_state(cpu); - /* memtohex() doubles the required space */ - len = snprintf((char *)mem_buf, sizeof(buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, - cpu->halted ? "halted " : "running"); + + if (s->multiprocess && (s->process_num > 1)) { + /* Print the CPU model and name in multiprocess mode */ + ObjectClass *oc = object_get_class(OBJECT(cpu)); + const char *cpu_model = object_class_get_name(oc); + char *cpu_name = + object_get_canonical_path_component(OBJECT(cpu)); + len = snprintf((char *)mem_buf, sizeof(buf) / 2, + "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); + g_free(cpu_name); + } else { + /* memtohex() doubles the required space */ + len = snprintf((char *)mem_buf, sizeof(buf) / 2, + "CPU#%d [%s]", cpu->cpu_index, + cpu->halted ? "halted " : "running"); + } trace_gdbstub_op_extra_info((char *)mem_buf); memtohex(buf, mem_buf, len); put_packet(s, buf); @@ -1347,6 +1723,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) if (cc->gdb_core_xml_file != NULL) { pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); } + + if (strstr(p, "multiprocess+")) { + s->multiprocess = true; + } + pstrcat(buf, sizeof(buf), ";multiprocess+"); + put_packet(s, buf); break; } @@ -1354,14 +1736,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) const char *xml; target_ulong total_len; - cc = CPU_GET_CLASS(first_cpu); + process = gdb_get_cpu_process(s, s->g_cpu); + cc = CPU_GET_CLASS(s->g_cpu); if (cc->gdb_core_xml_file == NULL) { goto unknown_command; } gdb_has_xml = true; p += 19; - xml = get_feature_xml(p, &p, cc); + xml = get_feature_xml(s, p, &p, process); if (!xml) { snprintf(buf, sizeof(buf), "E00"); put_packet(s, buf); @@ -1412,6 +1795,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) void gdb_set_stop_cpu(CPUState *cpu) { + GDBProcess *p = gdb_get_cpu_process(gdbserver_state, cpu); + + if (!p->attached) { + /* + * Having a stop CPU corresponding to a process that is not attached + * confuses GDB. So we ignore the request. + */ + return; + } + gdbserver_state->c_cpu = cpu; gdbserver_state->g_cpu = cpu; } @@ -1422,6 +1815,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) GDBState *s = gdbserver_state; CPUState *cpu = s->c_cpu; char buf[256]; + char thread_id[16]; const char *type; int ret; @@ -1433,6 +1827,14 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) put_packet(s, s->syscall_buf); return; } + + if (cpu == NULL) { + /* No process attached */ + return; + } + + gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id)); + switch (state) { case RUN_STATE_DEBUG: if (cpu->watchpoint_hit) { @@ -1450,8 +1852,8 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) trace_gdbstub_hit_watchpoint(type, cpu_gdb_index(cpu), (target_ulong)cpu->watchpoint_hit->vaddr); snprintf(buf, sizeof(buf), - "T%02xthread:%02x;%swatch:" TARGET_FMT_lx ";", - GDB_SIGNAL_TRAP, cpu_gdb_index(cpu), type, + "T%02xthread:%s;%swatch:" TARGET_FMT_lx ";", + GDB_SIGNAL_TRAP, thread_id, type, (target_ulong)cpu->watchpoint_hit->vaddr); cpu->watchpoint_hit = NULL; goto send_packet; @@ -1493,7 +1895,7 @@ static void gdb_vm_state_change(void *opaque, int running, RunState state) break; } gdb_set_stop_cpu(cpu); - snprintf(buf, sizeof(buf), "T%02xthread:%02x;", ret, cpu_gdb_index(cpu)); + snprintf(buf, sizeof(buf), "T%02xthread:%s;", ret, thread_id); send_packet: put_packet(s, buf); @@ -1752,6 +2154,31 @@ void gdb_exit(CPUArchState *env, int code) #endif } +/* + * Create the process that will contain all the "orphan" CPUs (that are not + * part of a CPU cluster). Note that if this process contains no CPUs, it won't + * be attachable and thus will be invisible to the user. + */ +static void create_default_process(GDBState *s) +{ + GDBProcess *process; + int max_pid = 0; + + if (s->process_num) { + max_pid = s->processes[s->process_num - 1].pid; + } + + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); + process = &s->processes[s->process_num - 1]; + + /* We need an available PID slot for this process */ + assert(max_pid < UINT32_MAX); + + process->pid = max_pid + 1; + process->attached = false; + process->target_xml[0] = '\0'; +} + #ifdef CONFIG_USER_ONLY int gdb_handlesig(CPUState *cpu, int sig) @@ -1847,8 +2274,10 @@ static bool gdb_accept(void) } s = g_malloc0(sizeof(GDBState)); - s->c_cpu = first_cpu; - s->g_cpu = first_cpu; + create_default_process(s); + s->processes[0].attached = true; + s->c_cpu = gdb_first_attached_cpu(s); + s->g_cpu = s->c_cpu; s->fd = fd; gdb_has_xml = false; @@ -1934,8 +2363,19 @@ static void gdb_chr_receive(void *opaque, const uint8_t *buf, int size) static void gdb_chr_event(void *opaque, int event) { + int i; + GDBState *s = (GDBState *) opaque; + switch (event) { case CHR_EVENT_OPENED: + /* Start with first process attached, others detached */ + for (i = 0; i < s->process_num; i++) { + s->processes[i].attached = !i; + } + + s->c_cpu = gdb_first_attached_cpu(s); + s->g_cpu = s->c_cpu; + vm_stop(RUN_STATE_PAUSED); gdb_has_xml = false; break; @@ -2005,6 +2445,66 @@ static const TypeInfo char_gdb_type_info = { .class_init = char_gdb_class_init, }; +static int find_cpu_clusters(Object *child, void *opaque) +{ + if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) { + GDBState *s = (GDBState *) opaque; + CPUClusterState *cluster = CPU_CLUSTER(child); + GDBProcess *process; + + s->processes = g_renew(GDBProcess, s->processes, ++s->process_num); + + process = &s->processes[s->process_num - 1]; + + /* + * GDB process IDs -1 and 0 are reserved. To avoid subtle errors at + * runtime, we enforce here that the machine does not use a cluster ID + * that would lead to PID 0. + */ + assert(cluster->cluster_id != UINT32_MAX); + process->pid = cluster->cluster_id + 1; + process->attached = false; + process->target_xml[0] = '\0'; + + return 0; + } + + return object_child_foreach(child, find_cpu_clusters, opaque); +} + +static int pid_order(const void *a, const void *b) +{ + GDBProcess *pa = (GDBProcess *) a; + GDBProcess *pb = (GDBProcess *) b; + + if (pa->pid < pb->pid) { + return -1; + } else if (pa->pid > pb->pid) { + return 1; + } else { + return 0; + } +} + +static void create_processes(GDBState *s) +{ + object_child_foreach(object_get_root(), find_cpu_clusters, s); + + if (s->processes) { + /* Sort by PID */ + qsort(s->processes, s->process_num, sizeof(s->processes[0]), pid_order); + } + + create_default_process(s); +} + +static void cleanup_processes(GDBState *s) +{ + g_free(s->processes); + s->process_num = 0; + s->processes = NULL; +} + int gdbserver_start(const char *device) { trace_gdbstub_op_start(device); @@ -2038,7 +2538,11 @@ int gdbserver_start(const char *device) sigaction(SIGINT, &act, NULL); } #endif - chr = qemu_chr_new_noreplay("gdb", device); + /* + * FIXME: it's a bit weird to allow using a mux chardev here + * and implicitly setup a monitor. We may want to break this. + */ + chr = qemu_chr_new_noreplay("gdb", device, true, NULL); if (!chr) return -1; } @@ -2052,20 +2556,22 @@ int gdbserver_start(const char *device) /* Initialize a monitor terminal for gdb */ mon_chr = qemu_chardev_new(NULL, TYPE_CHARDEV_GDB, - NULL, &error_abort); + NULL, NULL, &error_abort); monitor_init(mon_chr, 0); } else { qemu_chr_fe_deinit(&s->chr, true); mon_chr = s->mon_chr; + cleanup_processes(s); memset(s, 0, sizeof(GDBState)); s->mon_chr = mon_chr; } - s->c_cpu = first_cpu; - s->g_cpu = first_cpu; + + create_processes(s); + if (chr) { qemu_chr_fe_init(&s->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&s->chr, gdb_chr_can_receive, gdb_chr_receive, - gdb_chr_event, NULL, NULL, NULL, true); + gdb_chr_event, NULL, s, NULL, true); } s->state = chr ? RS_IDLE : RS_INACTIVE; s->mon_chr = mon_chr; diff --git a/gitdm.config b/gitdm.config new file mode 100644 index 0000000000..c01c219078 --- /dev/null +++ b/gitdm.config @@ -0,0 +1,51 @@ +# +# This is the gitdm configuration file for QEMU. +# +# It is to be used with LWN's git dataminer tool for generating +# reports about development activity in the QEMU repo. The LWN gitdm +# tool can be found at: +# +# git://git.lwn.net/gitdm.git +# +# A run to generate a report for the last year of activity would be +# +# git log --numstat --since "Last Year" | gitdm -n -l 10 +# + +# EmailAliases lets us cope with developers who use more +# than one address or have changed addresses. This duplicates some of +# the information in the existing .mailmap but in a slightly different +# form. +# +EmailAliases contrib/gitdm/aliases + +# +# EmailMap does the main work of mapping addresses onto +# employers. +# +EmailMap contrib/gitdm/domain-map + +# +# Use GroupMap to map a file full of addresses to the +# same employer. This is used for people that don't post from easily +# identifiable corporate emails. +# + +GroupMap contrib/gitdm/group-map-redhat Red Hat +GroupMap contrib/gitdm/group-map-wavecomp Wave Computing +GroupMap contrib/gitdm/group-map-cadence Cadence Design Systems +GroupMap contrib/gitdm/group-map-codeweavers CodeWeavers +GroupMap contrib/gitdm/group-map-ibm IBM +GroupMap contrib/gitdm/group-map-janustech Janus Technologies + +# Also group together our prolific individual contributors +# and those working under academic auspices +GroupMap contrib/gitdm/group-map-individuals (None) +GroupMap contrib/gitdm/group-map-academics Academics (various) + +# +# +# Use FileTypeMap to map a file types to file names using regular +# regular expressions. +# +FileTypeMap contrib/gitdm/filetypes.txt diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 70639f656a..c59444c461 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -202,6 +202,20 @@ STEXI @item info pic @findex info pic Show PIC state. +ETEXI + + { + .name = "rdma", + .args_type = "", + .params = "", + .help = "show RDMA state", + .cmd = hmp_info_rdma, + }, + +STEXI +@item info rdma +@findex info rdma +Show RDMA state. ETEXI { @@ -297,6 +311,28 @@ STEXI @item info opcount @findex info opcount Show dynamic compiler opcode counters +ETEXI + + { + .name = "sync-profile", + .args_type = "mean:-m,no_coalesce:-n,max:i?", + .params = "[-m] [-n] [max]", + .help = "show synchronization profiling info, up to max entries " + "(default: 10), sorted by total wait time. (-m: sort by " + "mean wait time; -n: do not coalesce objects with the " + "same call site)", + .cmd = hmp_info_sync_profile, + }, + +STEXI +@item info sync-profile [-m|-n] [@var{max}] +@findex info sync-profile +Show synchronization profiling info, up to @var{max} entries (default: 10), +sorted by total wait time. + -m: sort by mean wait time + -n: do not coalesce objects with the same call site +When different objects that share the same call site are coalesced, the "Object" +field shows---enclosed in brackets---the number of objects being coalesced. ETEXI { diff --git a/hmp-commands.hx b/hmp-commands.hx index 91dfe51c37..9b4035965c 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -350,49 +350,57 @@ ETEXI { .name = "savevm", .args_type = "name:s?", - .params = "[tag|id]", - .help = "save a VM snapshot. If no tag or id are provided, a new snapshot is created", + .params = "tag", + .help = "save a VM snapshot. If no tag is provided, a new snapshot is created", .cmd = hmp_savevm, }, STEXI -@item savevm [@var{tag}|@var{id}] +@item savevm @var{tag} @findex savevm Create a snapshot of the whole virtual machine. If @var{tag} is provided, it is used as human readable identifier. If there is already -a snapshot with the same tag or ID, it is replaced. More info at +a snapshot with the same tag, it is replaced. More info at @ref{vm_snapshots}. + +Since 4.0, savevm stopped allowing the snapshot id to be set, accepting +only @var{tag} as parameter. ETEXI { .name = "loadvm", .args_type = "name:s", - .params = "tag|id", - .help = "restore a VM snapshot from its tag or id", + .params = "tag", + .help = "restore a VM snapshot from its tag", .cmd = hmp_loadvm, .command_completion = loadvm_completion, }, STEXI -@item loadvm @var{tag}|@var{id} +@item loadvm @var{tag} @findex loadvm Set the whole virtual machine to the snapshot identified by the tag -@var{tag} or the unique snapshot ID @var{id}. +@var{tag}. + +Since 4.0, loadvm stopped accepting snapshot id as parameter. ETEXI { .name = "delvm", .args_type = "name:s", - .params = "tag|id", - .help = "delete a VM snapshot from its tag or id", + .params = "tag", + .help = "delete a VM snapshot from its tag", .cmd = hmp_delvm, .command_completion = delvm_completion, }, STEXI -@item delvm @var{tag}|@var{id} +@item delvm @var{tag} @findex delvm -Delete the snapshot identified by @var{tag} or @var{id}. +Delete the snapshot identified by @var{tag}. + +Since 4.0, delvm stopped deleting snapshots by snapshot id, accepting +only @var{tag} as parameter. ETEXI { @@ -643,6 +651,21 @@ sendkey ctrl-alt-f1 This command is useful to send keys that your graphical user interface intercepts at low level, such as @code{ctrl-alt-f1} in X Window. +ETEXI + { + .name = "sync-profile", + .args_type = "op:s?", + .params = "[on|off|reset]", + .help = "enable, disable or reset synchronization profiling. " + "With no arguments, prints whether profiling is on or off.", + .cmd = hmp_sync_profile, + }, + +STEXI +@item sync-profile [on|off|reset] +@findex sync-profile +Enable, disable or reset synchronization profiling. With no arguments, prints +whether profiling is on or off. ETEXI { @@ -913,6 +936,22 @@ Bug: can screw up when the buffer contains invalid UTF-8 sequences, NUL characters, after the ring buffer lost data, and when reading stops because the size limit is reached. +ETEXI + + { + .name = "announce_self", + .args_type = "", + .params = "", + .help = "Trigger GARP/RARP announcements", + .cmd = hmp_announce_self, + }, + +STEXI +@item announce_self +@findex announce_self +Trigger a round of GARP/RARP broadcasts; this is useful for explicitly updating the +network infrastructure after a reconfiguration or some forms of migration. +The timings of the round are set by the migration announce parameters. ETEXI { @@ -1306,7 +1345,6 @@ ETEXI .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" "[,snapshot=on|off][,cache=on|off]\n" "[,readonly=on|off][,copy-on-read=on|off]", .help = "add drive to PCI storage controller", @@ -1835,14 +1873,16 @@ ETEXI .name = "cpu-add", .args_type = "id:i", .params = "id", - .help = "add cpu", + .help = "add cpu (deprecated, use device_add instead)", .cmd = hmp_cpu_add, }, STEXI @item cpu-add @var{id} @findex cpu-add -Add CPU with id @var{id} +Add CPU with id @var{id}. This command is deprecated, please ++use @code{device_add} instead. For details, refer to +'docs/cpu-hotplug.rst'. ETEXI { diff --git a/hmp.c b/hmp.c index 2aafb50e8e..8eec768088 100644 --- a/hmp.c +++ b/hmp.c @@ -51,6 +51,7 @@ #include "qemu/error-report.h" #include "exec/ramlist.h" #include "hw/intc/intc.h" +#include "hw/rdma/rdma.h" #include "migration/snapshot.h" #include "migration/misc.h" @@ -62,7 +63,7 @@ static void hmp_handle_error(Monitor *mon, Error **errp) { assert(errp); if (*errp) { - error_report_err(*errp); + error_reportf_err(*errp, "Error: "); } } @@ -166,6 +167,27 @@ void hmp_info_mice(Monitor *mon, const QDict *qdict) qapi_free_MouseInfoList(mice_list); } +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("tcp:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + void hmp_info_migrate(Monitor *mon, const QDict *qdict) { MigrationInfo *info; @@ -236,6 +258,8 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->ram->page_size >> 10); monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", info->ram->multifd_bytes >> 10); + monitor_printf(mon, "pages-per-second: %" PRIu64 "\n", + info->ram->pages_per_second); if (info->ram->dirty_pages_rate) { monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", @@ -271,6 +295,19 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->xbzrle_cache->overflow); } + if (info->has_compression) { + monitor_printf(mon, "compression pages: %" PRIu64 " pages\n", + info->compression->pages); + monitor_printf(mon, "compression busy: %" PRIu64 "\n", + info->compression->busy); + monitor_printf(mon, "compression busy rate: %0.2f\n", + info->compression->busy_rate); + monitor_printf(mon, "compressed size: %" PRIu64 "\n", + info->compression->compressed_size); + monitor_printf(mon, "compression rate: %0.2f\n", + info->compression->compression_rate); + } + if (info->has_cpu_throttle_percentage) { monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", info->cpu_throttle_percentage); @@ -291,6 +328,18 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) g_free(str); visit_free(v); } + if (info->has_socket_address) { + SocketAddressList *addr; + + monitor_printf(mon, "socket address: [\n"); + + for (addr = info->socket_address; addr; addr = addr->next) { + char *s = SocketAddress_to_str(addr->value); + monitor_printf(mon, "\t%s\n", s); + g_free(s); + } + monitor_printf(mon, "]\n"); + } qapi_free_MigrationInfo(info); qapi_free_MigrationCapabilityStatusList(caps); } @@ -319,6 +368,18 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) params = qmp_query_migrate_parameters(NULL); if (params) { + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_INITIAL), + params->announce_initial); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_MAX), + params->announce_max); + monitor_printf(mon, "%s: %" PRIu64 "\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_ROUNDS), + params->announce_rounds); + monitor_printf(mon, "%s: %" PRIu64 " ms\n", + MigrationParameter_str(MIGRATION_PARAMETER_ANNOUNCE_STEP), + params->announce_step); assert(params->has_compress_level); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_LEVEL), @@ -327,6 +388,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_THREADS), params->compress_threads); + assert(params->has_compress_wait_thread); + monitor_printf(mon, "%s: %s\n", + MigrationParameter_str(MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD), + params->compress_wait_thread ? "on" : "off"); assert(params->has_decompress_threads); monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_DECOMPRESS_THREADS), @@ -339,6 +404,10 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%s: %u\n", MigrationParameter_str(MIGRATION_PARAMETER_CPU_THROTTLE_INCREMENT), params->cpu_throttle_increment); + assert(params->has_max_cpu_throttle); + monitor_printf(mon, "%s: %u\n", + MigrationParameter_str(MIGRATION_PARAMETER_MAX_CPU_THROTTLE), + params->max_cpu_throttle); assert(params->has_tls_creds); monitor_printf(mon, "%s: '%s'\n", MigrationParameter_str(MIGRATION_PARAMETER_TLS_CREDS), @@ -364,17 +433,17 @@ void hmp_info_migrate_parameters(Monitor *mon, const QDict *qdict) MigrationParameter_str(MIGRATION_PARAMETER_BLOCK_INCREMENTAL), params->block_incremental ? "on" : "off"); monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_X_MULTIFD_CHANNELS), - params->x_multifd_channels); - monitor_printf(mon, "%s: %u\n", - MigrationParameter_str(MIGRATION_PARAMETER_X_MULTIFD_PAGE_COUNT), - params->x_multifd_page_count); + MigrationParameter_str(MIGRATION_PARAMETER_MULTIFD_CHANNELS), + params->multifd_channels); monitor_printf(mon, "%s: %" PRIu64 "\n", MigrationParameter_str(MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE), params->xbzrle_cache_size); monitor_printf(mon, "%s: %" PRIu64 "\n", MigrationParameter_str(MIGRATION_PARAMETER_MAX_POSTCOPY_BANDWIDTH), params->max_postcopy_bandwidth); + monitor_printf(mon, " %s: '%s'\n", + MigrationParameter_str(MIGRATION_PARAMETER_TLS_AUTHZ), + params->has_tls_authz ? params->tls_authz : ""); } qapi_free_MigrationParameters(params); @@ -816,6 +885,10 @@ static void hmp_info_pci_device(Monitor *mon, const PciDeviceInfo *dev) monitor_printf(mon, ": PCI device %04" PRIx64 ":%04" PRIx64 "\n", dev->id->vendor, dev->id->device); + if (dev->id->has_subsystem_vendor && dev->id->has_subsystem) { + monitor_printf(mon, " PCI subsystem %04" PRIx64 ":%04" PRIx64 "\n", + dev->id->subsystem_vendor, dev->id->subsystem); + } if (dev->has_irq) { monitor_printf(mon, " IRQ %" PRId64 ".\n", dev->irq); @@ -941,6 +1014,32 @@ void hmp_info_pic(Monitor *mon, const QDict *qdict) hmp_info_pic_foreach, mon); } +static int hmp_info_rdma_foreach(Object *obj, void *opaque) +{ + RdmaProvider *rdma; + RdmaProviderClass *k; + Monitor *mon = opaque; + + if (object_dynamic_cast(obj, INTERFACE_RDMA_PROVIDER)) { + rdma = RDMA_PROVIDER(obj); + k = RDMA_PROVIDER_GET_CLASS(obj); + if (k->print_statistics) { + k->print_statistics(mon, rdma); + } else { + monitor_printf(mon, "RDMA statistics not available for %s.\n", + object_get_typename(obj)); + } + } + + return 0; +} + +void hmp_info_rdma(Monitor *mon, const QDict *qdict) +{ + object_child_foreach_recursive(object_get_root(), + hmp_info_rdma_foreach, mon); +} + void hmp_info_pci(Monitor *mon, const QDict *qdict) { PciInfoList *info_list, *info; @@ -1062,6 +1161,30 @@ void hmp_stop(Monitor *mon, const QDict *qdict) qmp_stop(NULL); } +void hmp_sync_profile(Monitor *mon, const QDict *qdict) +{ + const char *op = qdict_get_try_str(qdict, "op"); + + if (op == NULL) { + bool on = qsp_is_enabled(); + + monitor_printf(mon, "sync-profile is %s\n", on ? "on" : "off"); + return; + } + if (!strcmp(op, "on")) { + qsp_enable(); + } else if (!strcmp(op, "off")) { + qsp_disable(); + } else if (!strcmp(op, "reset")) { + qsp_reset(); + } else { + Error *err = NULL; + + error_setg(&err, QERR_INVALID_PARAMETER, op); + hmp_handle_error(mon, &err); + } +} + void hmp_system_reset(Monitor *mon, const QDict *qdict) { qmp_system_reset(NULL); @@ -1171,7 +1294,10 @@ void hmp_cont(Monitor *mon, const QDict *qdict) void hmp_system_wakeup(Monitor *mon, const QDict *qdict) { - qmp_system_wakeup(NULL); + Error *err = NULL; + + qmp_system_wakeup(&err); + hmp_handle_error(mon, &err); } void hmp_nmi(Monitor *mon, const QDict *qdict) @@ -1504,6 +1630,11 @@ void hmp_info_snapshots(Monitor *mon, const QDict *qdict) } +void hmp_announce_self(Monitor *mon, const QDict *qdict) +{ + qmp_announce_self(migrate_announce_params(), NULL); +} + void hmp_migrate_cancel(Monitor *mon, const QDict *qdict) { qmp_migrate_cancel(NULL); @@ -1623,6 +1754,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_compress_threads = true; visit_type_int(v, param, &p->compress_threads, &err); break; + case MIGRATION_PARAMETER_COMPRESS_WAIT_THREAD: + p->has_compress_wait_thread = true; + visit_type_bool(v, param, &p->compress_wait_thread, &err); + break; case MIGRATION_PARAMETER_DECOMPRESS_THREADS: p->has_decompress_threads = true; visit_type_int(v, param, &p->decompress_threads, &err); @@ -1635,6 +1770,10 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_cpu_throttle_increment = true; visit_type_int(v, param, &p->cpu_throttle_increment, &err); break; + case MIGRATION_PARAMETER_MAX_CPU_THROTTLE: + p->has_max_cpu_throttle = true; + visit_type_int(v, param, &p->max_cpu_throttle, &err); + break; case MIGRATION_PARAMETER_TLS_CREDS: p->has_tls_creds = true; p->tls_creds = g_new0(StrOrNull, 1); @@ -1647,6 +1786,12 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->tls_hostname->type = QTYPE_QSTRING; visit_type_str(v, param, &p->tls_hostname->u.s, &err); break; + case MIGRATION_PARAMETER_TLS_AUTHZ: + p->has_tls_authz = true; + p->tls_authz = g_new0(StrOrNull, 1); + p->tls_authz->type = QTYPE_QSTRING; + visit_type_str(v, param, &p->tls_authz->u.s, &err); + break; case MIGRATION_PARAMETER_MAX_BANDWIDTH: p->has_max_bandwidth = true; /* @@ -1673,19 +1818,17 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_block_incremental = true; visit_type_bool(v, param, &p->block_incremental, &err); break; - case MIGRATION_PARAMETER_X_MULTIFD_CHANNELS: - p->has_x_multifd_channels = true; - visit_type_int(v, param, &p->x_multifd_channels, &err); - break; - case MIGRATION_PARAMETER_X_MULTIFD_PAGE_COUNT: - p->has_x_multifd_page_count = true; - visit_type_int(v, param, &p->x_multifd_page_count, &err); + case MIGRATION_PARAMETER_MULTIFD_CHANNELS: + p->has_multifd_channels = true; + visit_type_int(v, param, &p->multifd_channels, &err); break; case MIGRATION_PARAMETER_XBZRLE_CACHE_SIZE: p->has_xbzrle_cache_size = true; visit_type_size(v, param, &cache_size, &err); - if (err || cache_size > INT64_MAX - || (size_t)cache_size != cache_size) { + if (err) { + break; + } + if (cache_size > INT64_MAX || (size_t)cache_size != cache_size) { error_setg(&err, "Invalid size %s", valuestr); break; } @@ -1695,6 +1838,22 @@ void hmp_migrate_set_parameter(Monitor *mon, const QDict *qdict) p->has_max_postcopy_bandwidth = true; visit_type_size(v, param, &p->max_postcopy_bandwidth, &err); break; + case MIGRATION_PARAMETER_ANNOUNCE_INITIAL: + p->has_announce_initial = true; + visit_type_size(v, param, &p->announce_initial, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_MAX: + p->has_announce_max = true; + visit_type_size(v, param, &p->announce_max, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_ROUNDS: + p->has_announce_rounds = true; + visit_type_size(v, param, &p->announce_rounds, &err); + break; + case MIGRATION_PARAMETER_ANNOUNCE_STEP: + p->has_announce_step = true; + visit_type_size(v, param, &p->announce_step, &err); + break; default: assert(0); } @@ -1865,8 +2024,9 @@ void hmp_block_stream(Monitor *mon, const QDict *qdict) int64_t speed = qdict_get_try_int(qdict, "speed", 0); qmp_block_stream(true, device, device, base != NULL, base, false, NULL, - false, NULL, qdict_haskey(qdict, "speed"), speed, - true, BLOCKDEV_ON_ERROR_REPORT, &error); + false, NULL, qdict_haskey(qdict, "speed"), speed, true, + BLOCKDEV_ON_ERROR_REPORT, false, false, false, false, + &error); hmp_handle_error(mon, &error); } @@ -1961,6 +2121,7 @@ static void hmp_migrate_status_cb(void *opaque) } monitor_resume(status->mon); timer_del(status->timer); + timer_free(status->timer); g_free(status); } @@ -2243,7 +2404,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) goto exit; } - nbd_server_start(addr, NULL, &local_err); + nbd_server_start(addr, NULL, NULL, &local_err); qapi_free_SocketAddress(addr); if (local_err != NULL) { goto exit; @@ -2264,7 +2425,7 @@ void hmp_nbd_server_start(Monitor *mon, const QDict *qdict) } qmp_nbd_server_add(info->value->device, false, NULL, - true, writable, &local_err); + true, writable, false, NULL, &local_err); if (local_err != NULL) { qmp_nbd_server_stop(NULL); @@ -2285,7 +2446,8 @@ void hmp_nbd_server_add(Monitor *mon, const QDict *qdict) bool writable = qdict_get_try_bool(qdict, "writable", false); Error *local_err = NULL; - qmp_nbd_server_add(device, !!name, name, true, writable, &local_err); + qmp_nbd_server_add(device, !!name, name, true, writable, + false, NULL, &local_err); hmp_handle_error(mon, &local_err); } @@ -2313,6 +2475,8 @@ void hmp_cpu_add(Monitor *mon, const QDict *qdict) int cpuid; Error *err = NULL; + error_report("cpu_add is deprecated, please use device_add instead"); + cpuid = qdict_get_int(qdict, "id"); qmp_cpu_add(cpuid, &err); hmp_handle_error(mon, &err); @@ -2328,7 +2492,7 @@ void hmp_chardev_add(Monitor *mon, const QDict *qdict) if (opts == NULL) { error_setg(&err, "Parsing chardev args failed"); } else { - qemu_chr_new_from_opts(opts, &err); + qemu_chr_new_from_opts(opts, NULL, &err); qemu_opts_del(opts); } hmp_handle_error(mon, &err); diff --git a/hmp.h b/hmp.h index 33354f1bdd..43617f2646 100644 --- a/hmp.h +++ b/hmp.h @@ -36,15 +36,18 @@ void hmp_info_spice(Monitor *mon, const QDict *qdict); void hmp_info_balloon(Monitor *mon, const QDict *qdict); void hmp_info_irq(Monitor *mon, const QDict *qdict); void hmp_info_pic(Monitor *mon, const QDict *qdict); +void hmp_info_rdma(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_sync_profile(Monitor *mon, const QDict *qdict); void hmp_system_reset(Monitor *mon, const QDict *qdict); void hmp_system_powerdown(Monitor *mon, const QDict *qdict); void hmp_exit_preconfig(Monitor *mon, const QDict *qdict); +void hmp_announce_self(Monitor *mon, const QDict *qdict); void hmp_cpu(Monitor *mon, const QDict *qdict); void hmp_memsave(Monitor *mon, const QDict *qdict); void hmp_pmemsave(Monitor *mon, const QDict *qdict); diff --git a/hw/9pfs/9p-handle.c b/hw/9pfs/9p-handle.c deleted file mode 100644 index f3641dbe4a..0000000000 --- a/hw/9pfs/9p-handle.c +++ /dev/null @@ -1,708 +0,0 @@ -/* - * 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 "qemu/option.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 - -typedef struct HandleData { - int mountfd; - int handle_bytes; -} HandleData; - -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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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.stream); -} - -static int handle_open(FsContext *ctx, V9fsPath *fs_path, - int flags, V9fsFidOpenState *fs) -{ - HandleData *data = (HandleData *) 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.stream = fdopendir(ret); - if (!fs->dir.stream) { - return -1; - } - return 0; -} - -static void handle_rewinddir(FsContext *ctx, V9fsFidOpenState *fs) -{ - rewinddir(fs->dir.stream); -} - -static off_t handle_telldir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return telldir(fs->dir.stream); -} - -static struct dirent *handle_readdir(FsContext *ctx, V9fsFidOpenState *fs) -{ - return readdir(fs->dir.stream); -} - -static void handle_seekdir(FsContext *ctx, V9fsFidOpenState *fs, off_t off) -{ - seekdir(fs->dir.stream, 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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.stream); - } 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - int fd; - HandleData *data = (HandleData *) ctx->private; - - fd = open_by_handle(data->mountfd, fs_path->data, O_NONBLOCK); - if (fd < 0) { - return fd; - } - ret = futimens(fd, buf); - close(fd); - 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.stream); - } 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) 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; - HandleData *data = (HandleData *) ctx->private; - - dirfd = open_by_handle(data->mountfd, dir->data, O_PATH); - if (dirfd < 0) { - return dirfd; - } - - ret = unlinkat(dirfd, name, flags); - - 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, Error **errp) -{ - int ret, mnt_id; - struct statfs stbuf; - struct file_handle fh; - HandleData *data = g_malloc(sizeof(HandleData)); - - 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 void handle_cleanup(FsContext *ctx) -{ - HandleData *data = ctx->private; - - close(data->mountfd); - g_free(data); -} - -static int handle_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) -{ - const char *sec_model = qemu_opt_get(opts, "security_model"); - const char *path = qemu_opt_get(opts, "path"); - - warn_report("handle backend is deprecated"); - - 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, - .cleanup = handle_cleanup, - .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 = handle_readdir, - .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/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index c30f4f26bd..08e673a79c 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1509,8 +1509,8 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) fsdev_throttle_parse_opts(opts, &fse->fst, &local_err); if (local_err) { - error_propagate(errp, local_err); - error_prepend(errp, "invalid throttle configuration: "); + error_propagate_prepend(errp, local_err, + "invalid throttle configuration: "); return -1; } diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index eef289e394..55821343e5 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1331,8 +1331,8 @@ static void coroutine_fn v9fs_walk(void *opaque) 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); + wnames = g_new0(V9fsString, nwnames); + qids = g_new0(V9fsQID, nwnames); for (i = 0; i < nwnames; i++) { err = pdu_unmarshal(pdu, offset, "s", &wnames[i]); if (err < 0) { @@ -1391,7 +1391,9 @@ static void coroutine_fn v9fs_walk(void *opaque) err = -EINVAL; goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); } else { newfidp = alloc_fid(s, newfid); if (newfidp == NULL) { @@ -2160,6 +2162,7 @@ static void coroutine_fn v9fs_create(void *opaque) V9fsString extension; int iounit; V9fsPDU *pdu = opaque; + V9fsState *s = pdu->s; v9fs_path_init(&path); v9fs_string_init(&name); @@ -2200,7 +2203,9 @@ static void coroutine_fn v9fs_create(void *opaque) if (err < 0) { goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); err = v9fs_co_opendir(pdu, fidp); if (err < 0) { goto out; @@ -2216,7 +2221,9 @@ static void coroutine_fn v9fs_create(void *opaque) if (err < 0) { goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); } else if (perm & P9_STAT_MODE_LINK) { int32_t ofid = atoi(extension.data); V9fsFidState *ofidp = get_fid(pdu, ofid); @@ -2234,7 +2241,9 @@ static void coroutine_fn v9fs_create(void *opaque) fidp->fid_type = P9_FID_NONE; goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); err = v9fs_co_lstat(pdu, &fidp->path, &stbuf); if (err < 0) { fidp->fid_type = P9_FID_NONE; @@ -2272,7 +2281,9 @@ static void coroutine_fn v9fs_create(void *opaque) if (err < 0) { goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); } else if (perm & P9_STAT_MODE_NAMED_PIPE) { err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, 0, S_IFIFO | (perm & 0777), &stbuf); @@ -2283,7 +2294,9 @@ static void coroutine_fn v9fs_create(void *opaque) if (err < 0) { goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); } else if (perm & P9_STAT_MODE_SOCKET) { err = v9fs_co_mknod(pdu, fidp, &name, fidp->uid, -1, 0, S_IFSOCK | (perm & 0777), &stbuf); @@ -2294,7 +2307,9 @@ static void coroutine_fn v9fs_create(void *opaque) if (err < 0) { goto out; } + v9fs_path_write_lock(s); v9fs_path_copy(&fidp->path, &path); + v9fs_path_unlock(s); } else { err = v9fs_co_open2(pdu, fidp, &name, -1, omode_to_uflags(mode)|O_CREAT, perm, &stbuf); @@ -2840,6 +2855,7 @@ static void coroutine_fn v9fs_wstat(void *opaque) 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); @@ -2905,7 +2921,9 @@ static void coroutine_fn v9fs_wstat(void *opaque) } } if (v9stat.name.size != 0) { + v9fs_path_write_lock(s); err = v9fs_complete_rename(pdu, fidp, -1, &v9stat.name); + v9fs_path_unlock(s); if (err < 0) { goto out; } diff --git a/hw/9pfs/Kconfig b/hw/9pfs/Kconfig new file mode 100644 index 0000000000..8c5032c575 --- /dev/null +++ b/hw/9pfs/Kconfig @@ -0,0 +1,4 @@ +config VIRTIO_9P + bool + default y + depends on VIRTFS && VIRTIO diff --git a/hw/9pfs/Makefile.objs b/hw/9pfs/Makefile.objs index e3fa673665..70ded6fd8f 100644 --- a/hw/9pfs/Makefile.objs +++ b/hw/9pfs/Makefile.objs @@ -1,12 +1,9 @@ -ifeq ($(call lor,$(CONFIG_VIRTIO_9P),$(CONFIG_XEN)),y) common-obj-y = 9p.o 9p-util.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 -endif common-obj-$(CONFIG_XEN) += xen-9p-backend.o obj-$(CONFIG_VIRTIO_9P) += virtio-9p-device.o diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 88791bc327..9c22837cda 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -140,10 +140,10 @@ int coroutine_fn v9fs_co_open2(V9fsPDU *pdu, V9fsFidState *fidp, cred.fc_gid = gid; /* * Hold the directory fid lock so that directory path name - * don't change. Read lock is fine because this fid cannot - * be used by any other operation. + * don't change. Take the write lock to be sure this fid + * cannot be used by another operation. */ - v9fs_path_read_lock(s); + v9fs_path_write_lock(s); v9fs_co_run_in_worker( { err = s->ops->open2(&s->ctx, &fidp->path, diff --git a/hw/9pfs/trace-events b/hw/9pfs/trace-events index 881e4c4dd8..c0a0a4ab5d 100644 --- a/hw/9pfs/trace-events +++ b/hw/9pfs/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/9pfs/virtio-9p.c +# 9p.c v9fs_rcancel(uint16_t tag, uint8_t id) "tag %d id %d" 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" diff --git a/hw/9pfs/xen-9p-backend.c b/hw/9pfs/xen-9p-backend.c index 6026780f95..25ab04d95a 100644 --- a/hw/9pfs/xen-9p-backend.c +++ b/hw/9pfs/xen-9p-backend.c @@ -12,8 +12,9 @@ #include "hw/hw.h" #include "hw/9pfs/9p.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "hw/9pfs/xen-9pfs.h" +#include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/option.h" #include "fsdev/qemu-fsdev.h" @@ -44,7 +45,7 @@ typedef struct Xen9pfsRing { } Xen9pfsRing; typedef struct Xen9pfsDev { - struct XenDevice xendev; /* must be first */ + struct XenLegacyDevice xendev; /* must be first */ V9fsState state; char *path; char *security_model; @@ -55,7 +56,7 @@ typedef struct Xen9pfsDev { Xen9pfsRing *rings; } Xen9pfsDev; -static void xen_9pfs_disconnect(struct XenDevice *xendev); +static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev); static void xen_9pfs_in_sg(Xen9pfsRing *ring, struct iovec *in_sg, @@ -177,7 +178,7 @@ static void xen_9pfs_init_out_iov_from_pdu(V9fsPDU *pdu, g_free(ring->sg); - ring->sg = g_malloc0(sizeof(*ring->sg) * 2); + ring->sg = g_new0(struct iovec, 2); xen_9pfs_out_sg(ring, ring->sg, &num, pdu->idx); *piov = ring->sg; *pniov = num; @@ -195,7 +196,7 @@ static void xen_9pfs_init_in_iov_from_pdu(V9fsPDU *pdu, g_free(ring->sg); - ring->sg = g_malloc0(sizeof(*ring->sg) * 2); + ring->sg = g_new0(struct iovec, 2); xen_9pfs_in_sg(ring, ring->sg, &num, pdu->idx, size); buf_size = iov_size(ring->sg, num); @@ -242,7 +243,7 @@ static const V9fsTransport xen_9p_transport = { .push_and_notify = xen_9pfs_push_and_notify, }; -static int xen_9pfs_init(struct XenDevice *xendev) +static int xen_9pfs_init(struct XenLegacyDevice *xendev) { return 0; } @@ -304,7 +305,7 @@ static void xen_9pfs_evtchn_event(void *opaque) qemu_bh_schedule(ring->bh); } -static void xen_9pfs_disconnect(struct XenDevice *xendev) +static void xen_9pfs_disconnect(struct XenLegacyDevice *xendev) { Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); int i; @@ -320,7 +321,7 @@ static void xen_9pfs_disconnect(struct XenDevice *xendev) } } -static int xen_9pfs_free(struct XenDevice *xendev) +static int xen_9pfs_free(struct XenLegacyDevice *xendev) { Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); int i; @@ -353,8 +354,9 @@ static int xen_9pfs_free(struct XenDevice *xendev) return 0; } -static int xen_9pfs_connect(struct XenDevice *xendev) +static int xen_9pfs_connect(struct XenLegacyDevice *xendev) { + Error *err = NULL; int i; Xen9pfsDev *xen_9pdev = container_of(xendev, Xen9pfsDev, xendev); V9fsState *s = &xen_9pdev->state; @@ -366,7 +368,7 @@ static int xen_9pfs_connect(struct XenDevice *xendev) return -1; } - xen_9pdev->rings = g_malloc0(xen_9pdev->num_rings * sizeof(Xen9pfsRing)); + xen_9pdev->rings = g_new0(Xen9pfsRing, xen_9pdev->num_rings); for (i = 0; i < xen_9pdev->num_rings; i++) { char *str; int ring_order; @@ -452,7 +454,10 @@ static int xen_9pfs_connect(struct XenDevice *xendev) qemu_opt_set(fsdev, "path", xen_9pdev->path, NULL); qemu_opt_set(fsdev, "security_model", xen_9pdev->security_model, NULL); qemu_opts_set_id(fsdev, s->fsconf.fsdev_id); - qemu_fsdev_add(fsdev); + qemu_fsdev_add(fsdev, &err); + if (err) { + error_report_err(err); + } v9fs_device_realize_common(s, &xen_9p_transport, NULL); return 0; @@ -462,7 +467,7 @@ out: return -1; } -static void xen_9pfs_alloc(struct XenDevice *xendev) +static void xen_9pfs_alloc(struct XenLegacyDevice *xendev) { xenstore_write_be_str(xendev, "versions", VERSIONS); xenstore_write_be_int(xendev, "max-rings", MAX_RINGS); diff --git a/hw/Kconfig b/hw/Kconfig new file mode 100644 index 0000000000..88b9f15007 --- /dev/null +++ b/hw/Kconfig @@ -0,0 +1,74 @@ +# devices Kconfig +source 9pfs/Kconfig +source acpi/Kconfig +source adc/Kconfig +source audio/Kconfig +source block/Kconfig +source bt/Kconfig +source char/Kconfig +source core/Kconfig +source display/Kconfig +source dma/Kconfig +source gpio/Kconfig +source hyperv/Kconfig +source i2c/Kconfig +source ide/Kconfig +source input/Kconfig +source intc/Kconfig +source ipack/Kconfig +source ipmi/Kconfig +source isa/Kconfig +source mem/Kconfig +source misc/Kconfig +source net/Kconfig +source nvram/Kconfig +source pci-bridge/Kconfig +source pci-host/Kconfig +source pcmcia/Kconfig +source pci/Kconfig +source rdma/Kconfig +source scsi/Kconfig +source sd/Kconfig +source smbios/Kconfig +source ssi/Kconfig +source timer/Kconfig +source tpm/Kconfig +source usb/Kconfig +source virtio/Kconfig +source vfio/Kconfig +source watchdog/Kconfig + +# arch Kconfig +source arm/Kconfig +source alpha/Kconfig +source cris/Kconfig +source hppa/Kconfig +source i386/Kconfig +source lm32/Kconfig +source m68k/Kconfig +source microblaze/Kconfig +source mips/Kconfig +source moxie/Kconfig +source nios2/Kconfig +source openrisc/Kconfig +source ppc/Kconfig +source riscv/Kconfig +source s390x/Kconfig +source sh4/Kconfig +source sparc/Kconfig +source sparc64/Kconfig +source tricore/Kconfig +source unicore32/Kconfig +source xtensa/Kconfig + +# Symbols used by multiple targets +config TEST_DEVICES + bool + +config XILINX + bool + select PTIMER # for hw/timer/xilinx_timer.c + +config XILINX_AXI + bool + select PTIMER # for hw/dma/xilinx_axidma.c diff --git a/hw/Makefile.objs b/hw/Makefile.objs index 312b0eb4d9..8d4c98027f 100644 --- a/hw/Makefile.objs +++ b/hw/Makefile.objs @@ -1,4 +1,4 @@ -devices-dirs-$(call land,$(CONFIG_VIRTFS),$(call lor,$(CONFIG_VIRTIO),$(CONFIG_XEN))) += 9pfs/ +devices-dirs-$(call lor,$(CONFIG_VIRTIO_9P),$(call land,$(CONFIG_VIRTFS),$(CONFIG_XEN))) += 9pfs/ devices-dirs-$(CONFIG_SOFTMMU) += acpi/ devices-dirs-$(CONFIG_SOFTMMU) += adc/ devices-dirs-$(CONFIG_SOFTMMU) += audio/ @@ -9,7 +9,8 @@ devices-dirs-$(CONFIG_SOFTMMU) += cpu/ devices-dirs-$(CONFIG_SOFTMMU) += display/ devices-dirs-$(CONFIG_SOFTMMU) += dma/ devices-dirs-$(CONFIG_SOFTMMU) += gpio/ -devices-dirs-$(CONFIG_SOFTMMU) += i2c/ +devices-dirs-$(CONFIG_HYPERV) += hyperv/ +devices-dirs-$(CONFIG_I2C) += i2c/ devices-dirs-$(CONFIG_SOFTMMU) += ide/ devices-dirs-$(CONFIG_SOFTMMU) += input/ devices-dirs-$(CONFIG_SOFTMMU) += intc/ @@ -29,12 +30,12 @@ devices-dirs-$(CONFIG_SOFTMMU) += ssi/ devices-dirs-$(CONFIG_SOFTMMU) += timer/ devices-dirs-$(CONFIG_TPM) += tpm/ devices-dirs-$(CONFIG_SOFTMMU) += usb/ -devices-dirs-$(CONFIG_SOFTMMU) += vfio/ +devices-dirs-$(CONFIG_VFIO) += vfio/ devices-dirs-$(CONFIG_SOFTMMU) += virtio/ devices-dirs-$(CONFIG_SOFTMMU) += watchdog/ devices-dirs-$(CONFIG_SOFTMMU) += xbox/ devices-dirs-$(CONFIG_SOFTMMU) += xen/ -devices-dirs-$(CONFIG_MEM_HOTPLUG) += mem/ +devices-dirs-$(CONFIG_MEM_DEVICE) += mem/ devices-dirs-$(CONFIG_SOFTMMU) += smbios/ devices-dirs-y += core/ common-obj-y += $(devices-dirs-y) diff --git a/hw/acpi/Kconfig b/hw/acpi/Kconfig new file mode 100644 index 0000000000..eca3beed75 --- /dev/null +++ b/hw/acpi/Kconfig @@ -0,0 +1,29 @@ +config ACPI + bool + +config ACPI_X86 + bool + select ACPI + select ACPI_NVDIMM + select ACPI_CPU_HOTPLUG + select ACPI_MEMORY_HOTPLUG + +config ACPI_X86_ICH + bool + select ACPI_X86 + +config ACPI_CPU_HOTPLUG + bool + +config ACPI_MEMORY_HOTPLUG + bool + select MEM_DEVICE + +config ACPI_NVDIMM + bool + depends on ACPI + +config ACPI_VMGENID + bool + default y + depends on PC diff --git a/hw/acpi/Makefile.objs b/hw/acpi/Makefile.objs index 11c35bcb44..2d46e3789a 100644 --- a/hw/acpi/Makefile.objs +++ b/hw/acpi/Makefile.objs @@ -11,6 +11,7 @@ common-obj-$(call lnot,$(CONFIG_ACPI_X86)) += acpi-stub.o common-obj-y += acpi_interface.o common-obj-y += bios-linker-loader.o common-obj-y += aml-build.o +common-obj-$(CONFIG_TPM) += tpm.o common-obj-$(CONFIG_IPMI) += ipmi.o common-obj-$(call lnot,$(CONFIG_IPMI)) += ipmi-stub.o diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 1e43cd736d..555c24f21d 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -1589,6 +1589,74 @@ void acpi_build_tables_cleanup(AcpiBuildTables *tables, bool mfre) g_array_free(tables->vmgenid, mfre); } +/* + * ACPI spec 5.2.5.3 Root System Description Pointer (RSDP). + * (Revision 1.0 or later) + */ +void +build_rsdp(GArray *tbl, BIOSLinker *linker, AcpiRsdpData *rsdp_data) +{ + int tbl_off = tbl->len; /* Table offset in the RSDP file */ + + switch (rsdp_data->revision) { + case 0: + /* With ACPI 1.0, we must have an RSDT pointer */ + g_assert(rsdp_data->rsdt_tbl_offset); + break; + case 2: + /* With ACPI 2.0+, we must have an XSDT pointer */ + g_assert(rsdp_data->xsdt_tbl_offset); + break; + default: + /* Only revisions 0 (ACPI 1.0) and 2 (ACPI 2.0+) are valid for RSDP */ + g_assert_not_reached(); + } + + bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, tbl, 16, + true /* fseg memory */); + + g_array_append_vals(tbl, "RSD PTR ", 8); /* Signature */ + build_append_int_noprefix(tbl, 0, 1); /* Checksum */ + g_array_append_vals(tbl, rsdp_data->oem_id, 6); /* OEMID */ + build_append_int_noprefix(tbl, rsdp_data->revision, 1); /* Revision */ + build_append_int_noprefix(tbl, 0, 4); /* RsdtAddress */ + if (rsdp_data->rsdt_tbl_offset) { + /* RSDT address to be filled by guest linker */ + bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, + tbl_off + 16, 4, + ACPI_BUILD_TABLE_FILE, + *rsdp_data->rsdt_tbl_offset); + } + + /* Checksum to be filled by guest linker */ + bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, + tbl_off, 20, /* ACPI rev 1.0 RSDP size */ + 8); + + if (rsdp_data->revision == 0) { + /* ACPI 1.0 RSDP, we're done */ + return; + } + + build_append_int_noprefix(tbl, 36, 4); /* Length */ + + /* XSDT address to be filled by guest linker */ + build_append_int_noprefix(tbl, 0, 8); /* XsdtAddress */ + /* We already validated our xsdt pointer */ + bios_linker_loader_add_pointer(linker, ACPI_BUILD_RSDP_FILE, + tbl_off + 24, 8, + ACPI_BUILD_TABLE_FILE, + *rsdp_data->xsdt_tbl_offset); + + build_append_int_noprefix(tbl, 0, 1); /* Extended Checksum */ + build_append_int_noprefix(tbl, 0, 3); /* Reserved */ + + /* Extended checksum to be filled by Guest linker */ + bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, + tbl_off, 36, /* ACPI rev 2.0 RSDP size */ + 32); +} + /* Build rsdt table */ void build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c index d16b8bbcb1..626c04a39f 100644 --- a/hw/acpi/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -283,6 +283,8 @@ void bios_linker_loader_add_pointer(BIOSLinker *linker, const BiosLinkerFileEntry *source_file = bios_linker_find_file(linker, src_file); + assert(dst_file); + assert(source_file); assert(dst_patched_offset < dst_file->blob->len); assert(dst_patched_offset + dst_patched_size <= dst_file->blob->len); assert(src_offset < source_file->blob->len); diff --git a/hw/acpi/core.c b/hw/acpi/core.c index f467779b3a..a2ded45343 100644 --- a/hw/acpi/core.c +++ b/hw/acpi/core.c @@ -35,14 +35,18 @@ struct acpi_table_header { uint16_t _length; /* our length, not actual part of the hdr */ /* allows easier parsing for fw_cfg clients */ - char sig[4]; /* ACPI signature (4 ASCII characters) */ + char sig[4] + QEMU_NONSTRING; /* ACPI signature (4 ASCII characters) */ uint32_t length; /* Length of table, in bytes, including header */ uint8_t revision; /* ACPI Specification minor version # */ uint8_t checksum; /* To make sum of entire table == 0 */ - char oem_id[6]; /* OEM identification */ - char oem_table_id[8]; /* OEM table identification */ + char oem_id[6] + QEMU_NONSTRING; /* OEM identification */ + char oem_table_id[8] + QEMU_NONSTRING; /* OEM table identification */ uint32_t oem_revision; /* OEM revision number */ - char asl_compiler_id[4]; /* ASL compiler vendor ID */ + char asl_compiler_id[4] + QEMU_NONSTRING; /* ASL compiler vendor ID */ uint32_t asl_compiler_revision; /* ASL compiler revision number */ } QEMU_PACKED; @@ -303,14 +307,6 @@ out: error_propagate(errp, err); } -static bool acpi_table_builtin = false; - -void acpi_table_add_builtin(const QemuOpts *opts, Error **errp) -{ - acpi_table_builtin = true; - acpi_table_add(opts, errp); -} - unsigned acpi_table_len(void *current) { struct acpi_table_header *hdr = current - sizeof(hdr->_length); @@ -326,7 +322,7 @@ void *acpi_table_hdr(void *h) uint8_t *acpi_table_first(void) { - if (acpi_table_builtin || !acpi_tables) { + if (!acpi_tables) { return NULL; } return acpi_table_hdr(acpi_tables + ACPI_TABLE_PFX_SIZE); @@ -522,7 +518,8 @@ static uint32_t acpi_pm_tmr_get(ACPIREGS *ar) static void acpi_pm_tmr_timer(void *opaque) { ACPIREGS *ar = opaque; - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER); + + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_PMTIMER, NULL); ar->tmr.update_sci(ar); } @@ -578,7 +575,7 @@ static void acpi_pm1_cnt_write(ACPIREGS *ar, uint16_t val) break; default: if (sus_typ == ar->pm1.cnt.s4_val) { /* S4 request */ - qapi_event_send_suspend_disk(&error_abort); + qapi_event_send_suspend_disk(); qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } break; @@ -625,6 +622,12 @@ void acpi_pm1_cnt_init(ACPIREGS *ar, MemoryRegion *parent, ar->pm1.cnt.s4_val = s4_val; ar->wakeup.notify = acpi_notify_wakeup; qemu_register_wakeup_notifier(&ar->wakeup); + + /* + * Register wake-up support in QMP query-current-machine API + */ + qemu_register_wakeup_support(); + memory_region_init_io(&ar->pm1.cnt.io, memory_region_owner(parent), &acpi_pm_cnt_ops, ar, "acpi-cnt", 2); memory_region_add_subregion(parent, 4, &ar->pm1.cnt.io); diff --git a/hw/acpi/cpu.c b/hw/acpi/cpu.c index 5ae595ecbe..7a90c8f82d 100644 --- a/hw/acpi/cpu.c +++ b/hw/acpi/cpu.c @@ -117,7 +117,7 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, DeviceState *dev = NULL; HotplugHandler *hotplug_ctrl = NULL; - if (!cdev->cpu) { + if (!cdev->cpu || cdev->cpu == first_cpu) { trace_cpuhp_acpi_ejecting_invalid_cpu(cpu_st->selector); break; } @@ -126,6 +126,7 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, dev = DEVICE(cdev->cpu); hotplug_ctrl = qdev_get_hotplug_handler(dev); hotplug_handler_unplug(hotplug_ctrl, dev, NULL); + object_unparent(OBJECT(dev)); } break; case ACPI_CPU_CMD_OFFSET_WR: @@ -160,7 +161,7 @@ static void cpu_hotplug_wr(void *opaque, hwaddr addr, uint64_t data, cdev = &cpu_st->devs[cpu_st->selector]; cdev->ost_status = data; info = acpi_cpu_device_status(cpu_st->selector, cdev); - qapi_event_send_acpi_device_ost(info, &error_abort); + qapi_event_send_acpi_device_ost(info); qapi_free_ACPIOSTInfo(info); trace_cpuhp_acpi_write_ost_status(cpu_st->selector, cdev->ost_status); @@ -508,7 +509,7 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, GArray *madt_buf = g_array_new(0, 1, 1); int arch_id = arch_ids->cpus[i].arch_id; - if (opts.apci_1_compatible && arch_id < 255) { + if (opts.acpi_1_compatible && arch_id < 255) { dev = aml_processor(i, 0, 0, CPU_NAME_FMT, i); } else { dev = aml_device(CPU_NAME_FMT, i); @@ -541,9 +542,11 @@ void build_cpus_aml(Aml *table, MachineState *machine, CPUHotplugFeatures opts, aml_buffer(madt_buf->len, (uint8_t *)madt_buf->data))); g_array_free(madt_buf, true); - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, aml_call1(CPU_EJECT_METHOD, uid)); - aml_append(dev, method); + if (CPU(arch_ids->cpus[i].cpu) != first_cpu) { + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, aml_call1(CPU_EJECT_METHOD, uid)); + aml_append(dev, method); + } method = aml_method("_OST", 3, AML_SERIALIZED); aml_append(method, diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index c5d8646abc..e53dfe1ee3 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -483,13 +483,24 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp) NULL); } +void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) && + !lpc->pm.acpi_memory_hotplug.is_enabled) + error_setg(errp, + "memory hotplug is not enabled: %s.memory-hotplug-support " + "is not set", object_get_typename(OBJECT(lpc))); +} + void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { ICH9LPCState *lpc = ICH9_LPC_DEVICE(hotplug_dev); - if (lpc->pm.acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { nvdimm_acpi_plug_cb(hotplug_dev, dev); } else { diff --git a/hw/acpi/memory_hotplug.c b/hw/acpi/memory_hotplug.c index 0ff1712c4c..297812d5f7 100644 --- a/hw/acpi/memory_hotplug.c +++ b/hw/acpi/memory_hotplug.c @@ -161,7 +161,7 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, /* TODO: implement memory removal on guest signal */ info = acpi_memory_device_status(mem_st->selector, mdev); - qapi_event_send_acpi_device_ost(info, &error_abort); + qapi_event_send_acpi_device_ost(info); qapi_free_ACPIOSTInfo(info); break; case 0x14: /* set is_* fields */ @@ -185,11 +185,11 @@ static void acpi_memory_hotplug_write(void *opaque, hwaddr addr, uint64_t data, if (local_err) { trace_mhp_acpi_pc_dimm_delete_failed(mem_st->selector); qapi_event_send_mem_unplug_error(dev->id, - error_get_pretty(local_err), - &error_abort); + error_get_pretty(local_err)); error_free(local_err); break; } + object_unparent(OBJECT(dev)); trace_mhp_acpi_pc_dimm_deleted(mem_st->selector); } break; @@ -687,15 +687,15 @@ void build_memory_hotplug_aml(Aml *table, uint32_t nr_mem, method = aml_method("_OST", 3, AML_NOTSERIALIZED); s = 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(method, + 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 = MEMORY_SLOT_EJECT_METHOD; - aml_append(method, aml_return(aml_call2( - s, aml_name("_UID"), aml_arg(0)))); + aml_append(method, + aml_call2(s, aml_name("_UID"), aml_arg(0))); aml_append(dev, method); aml_append(dev_container, dev); diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 27eeb6609f..9fdad6dc3f 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -382,7 +382,7 @@ nvdimm_build_structure_caps(GArray *structures, uint32_t capabilities) nfit_caps->capabilities = cpu_to_le32(capabilities); } -static GArray *nvdimm_build_device_structure(AcpiNVDIMMState *state) +static GArray *nvdimm_build_device_structure(NVDIMMState *state) { GSList *device_list = nvdimm_get_device_list(); GArray *structures = g_array_new(false, true /* clear */, 1); @@ -416,7 +416,7 @@ static void nvdimm_init_fit_buffer(NvdimmFitBuffer *fit_buf) fit_buf->fit = g_array_new(false, true /* clear */, 1); } -static void nvdimm_build_fit_buffer(AcpiNVDIMMState *state) +static void nvdimm_build_fit_buffer(NVDIMMState *state) { NvdimmFitBuffer *fit_buf = &state->fit_buf; @@ -425,12 +425,12 @@ static void nvdimm_build_fit_buffer(AcpiNVDIMMState *state) fit_buf->dirty = true; } -void nvdimm_plug(AcpiNVDIMMState *state) +void nvdimm_plug(NVDIMMState *state) { nvdimm_build_fit_buffer(state); } -static void nvdimm_build_nfit(AcpiNVDIMMState *state, GArray *table_offsets, +static void nvdimm_build_nfit(NVDIMMState *state, GArray *table_offsets, GArray *table_data, BIOSLinker *linker) { NvdimmFitBuffer *fit_buf = &state->fit_buf; @@ -570,7 +570,7 @@ nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr) #define NVDIMM_QEMU_RSVD_HANDLE_ROOT 0x10000 /* Read FIT data, defined in docs/specs/acpi_nvdimm.txt. */ -static void nvdimm_dsm_func_read_fit(AcpiNVDIMMState *state, NvdimmDsmIn *in, +static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in, hwaddr dsm_mem_addr) { NvdimmFitBuffer *fit_buf = &state->fit_buf; @@ -581,7 +581,7 @@ static void nvdimm_dsm_func_read_fit(AcpiNVDIMMState *state, NvdimmDsmIn *in, int size; read_fit = (NvdimmFuncReadFITIn *)in->arg3; - le32_to_cpus(&read_fit->offset); + read_fit->offset = le32_to_cpu(read_fit->offset); fit = fit_buf->fit; @@ -619,7 +619,7 @@ exit: } static void -nvdimm_dsm_handle_reserved_root_method(AcpiNVDIMMState *state, +nvdimm_dsm_handle_reserved_root_method(NVDIMMState *state, NvdimmDsmIn *in, hwaddr dsm_mem_addr) { switch (in->function) { @@ -742,8 +742,8 @@ static void nvdimm_dsm_get_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, int size; get_label_data = (NvdimmFuncGetLabelDataIn *)in->arg3; - le32_to_cpus(&get_label_data->offset); - le32_to_cpus(&get_label_data->length); + get_label_data->offset = le32_to_cpu(get_label_data->offset); + get_label_data->length = le32_to_cpu(get_label_data->length); nvdimm_debug("Read Label Data: offset %#x length %#x.\n", get_label_data->offset, get_label_data->length); @@ -781,8 +781,8 @@ static void nvdimm_dsm_set_label_data(NVDIMMDevice *nvdimm, NvdimmDsmIn *in, set_label_data = (NvdimmFuncSetLabelDataIn *)in->arg3; - le32_to_cpus(&set_label_data->offset); - le32_to_cpus(&set_label_data->length); + set_label_data->offset = le32_to_cpu(set_label_data->offset); + set_label_data->length = le32_to_cpu(set_label_data->length); nvdimm_debug("Write Label Data: offset %#x length %#x.\n", set_label_data->offset, set_label_data->length); @@ -863,7 +863,7 @@ nvdimm_dsm_read(void *opaque, hwaddr addr, unsigned size) static void nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - AcpiNVDIMMState *state = opaque; + NVDIMMState *state = opaque; NvdimmDsmIn *in; hwaddr dsm_mem_addr = val; @@ -877,9 +877,9 @@ nvdimm_dsm_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) in = g_new(NvdimmDsmIn, 1); cpu_physical_memory_read(dsm_mem_addr, in, sizeof(*in)); - le32_to_cpus(&in->revision); - le32_to_cpus(&in->function); - le32_to_cpus(&in->handle); + in->revision = le32_to_cpu(in->revision); + in->function = le32_to_cpu(in->function); + in->handle = le32_to_cpu(in->handle); nvdimm_debug("Revision %#x Handler %#x Function %#x.\n", in->revision, in->handle, in->function); @@ -925,7 +925,7 @@ void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) } } -void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, +void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io, FWCfgState *fw_cfg, Object *owner) { memory_region_init_io(&state->io_mr, owner, &nvdimm_dsm_ops, state, @@ -992,7 +992,7 @@ static void nvdimm_build_common_dsm(Aml *dev) field = aml_field(NVDIMM_DSM_IOPORT, AML_DWORD_ACC, AML_NOLOCK, AML_PRESERVE); aml_append(field, aml_named_field(NVDIMM_DSM_NOTIFY, - sizeof(uint32_t) * BITS_PER_BYTE)); + NVDIMM_ACPI_IO_LEN * BITS_PER_BYTE)); aml_append(method, field); /* @@ -1086,7 +1086,7 @@ static void nvdimm_build_common_dsm(Aml *dev) */ aml_append(method, aml_store(handle, aml_name(NVDIMM_DSM_HANDLE))); aml_append(method, aml_store(aml_arg(1), aml_name(NVDIMM_DSM_REVISION))); - aml_append(method, aml_store(aml_arg(2), aml_name(NVDIMM_DSM_FUNCTION))); + aml_append(method, aml_store(function, aml_name(NVDIMM_DSM_FUNCTION))); /* * The fourth parameter (Arg3) of _DSM is a package which contains @@ -1260,7 +1260,7 @@ static void nvdimm_build_nvdimm_devices(Aml *root_dev, uint32_t ram_slots) } static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, - BIOSLinker *linker, GArray *dsm_dma_arrea, + BIOSLinker *linker, GArray *dsm_dma_area, uint32_t ram_slots) { Aml *ssdt, *sb_scope, *dev; @@ -1307,7 +1307,7 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, NVDIMM_ACPI_MEM_ADDR); bios_linker_loader_alloc(linker, - NVDIMM_DSM_MEM_FILE, dsm_dma_arrea, + NVDIMM_DSM_MEM_FILE, dsm_dma_area, sizeof(NvdimmDsmIn), false /* high memory */); bios_linker_loader_add_pointer(linker, ACPI_BUILD_TABLE_FILE, mem_addr_offset, sizeof(uint32_t), @@ -1319,7 +1319,7 @@ static void nvdimm_build_ssdt(GArray *table_offsets, GArray *table_data, } void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, - BIOSLinker *linker, AcpiNVDIMMState *state, + BIOSLinker *linker, NVDIMMState *state, uint32_t ram_slots) { GSList *device_list; diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 80d42e12ff..88e4ae1bcd 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -30,6 +30,7 @@ #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "hw/acpi/acpi.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" @@ -153,6 +154,7 @@ static bool acpi_pcihp_pc_no_hotplug(AcpiPciHpState *s, PCIDevice *dev) static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slots) { + HotplugHandler *hotplug_ctrl; BusChild *kid, *next; int slot = ctz32(slots); PCIBus *bus = acpi_pcihp_find_hotplug_bus(s, bsel); @@ -170,6 +172,8 @@ static void acpi_pcihp_eject_slot(AcpiPciHpState *s, unsigned bsel, unsigned slo PCIDevice *dev = PCI_DEVICE(qdev); if (PCI_SLOT(dev->devfn) == slot) { if (!acpi_pcihp_pc_no_hotplug(s, dev)) { + hotplug_ctrl = qdev_get_hotplug_handler(qdev); + hotplug_handler_unplug(hotplug_ctrl, qdev, &error_abort); object_unparent(OBJECT(qdev)); } } @@ -217,31 +221,61 @@ void acpi_pcihp_reset(AcpiPciHpState *s) acpi_pcihp_update(s); } +void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + /* Only hotplugged devices need the hotplug capability. */ + if (dev->hotplugged && + acpi_pcihp_get_bsel(pci_get_bus(PCI_DEVICE(dev))) < 0) { + error_setg(errp, "Unsupported bus. Bus doesn't have property '" + ACPI_PCIHP_PROP_BSEL "' set"); + return; + } +} + void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); int slot = PCI_SLOT(pdev->devfn); - int bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev)); - if (bsel < 0) { - error_setg(errp, "Unsupported bus. Bus doesn't have property '" - ACPI_PCIHP_PROP_BSEL "' set"); - return; - } + int bsel; /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an * event when the device is disabled later. */ if (!dev->hotplugged) { + /* + * Overwrite the default hotplug handler with the ACPI PCI one + * for cold plugged bridges only. + */ + if (!s->legacy_piix && + object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { + PCIBus *sec = pci_bridge_get_sec_bus(PCI_BRIDGE(pdev)); + + qbus_set_hotplug_handler(BUS(sec), OBJECT(hotplug_dev), + &error_abort); + /* We don't have to overwrite any other hotplug handler yet */ + assert(QLIST_EMPTY(&sec->child)); + } + return; } + bsel = acpi_pcihp_get_bsel(pci_get_bus(pdev)); + g_assert(bsel >= 0); s->acpi_pcihp_pci_status[bsel].up |= (1U << slot); acpi_send_event(DEVICE(hotplug_dev), ACPI_PCI_HOTPLUG_STATUS); } void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) +{ + object_property_set_bool(OBJECT(dev), false, "realized", NULL); +} + +void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, + AcpiPciHpState *s, DeviceState *dev, + Error **errp) { PCIDevice *pdev = PCI_DEVICE(dev); int slot = PCI_SLOT(pdev->devfn); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 6404af5f33..9c079d6834 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -28,7 +28,6 @@ #include "sysemu/sysemu.h" #include "qapi/error.h" #include "qemu/range.h" -#include "hw/nvram/fw_cfg.h" #include "exec/address-spaces.h" #include "hw/acpi/piix4.h" #include "hw/acpi/pcihp.h" @@ -173,6 +172,7 @@ static int vmstate_acpi_post_load(void *opaque, int version_id) PIIX4PMState *s = opaque; pm_io_space_update(s); + smbus_io_space_update(s); return 0; } @@ -301,6 +301,11 @@ static const VMStateDescription vmstate_cpuhp_state = { } }; +static bool piix4_vmstate_need_smbus(void *opaque, int version_id) +{ + return pm_smbus_vmstate_needed(); +} + /* qemu-kvm 1.2 uses version 3 but advertised as 2 * To support incoming qemu-kvm 1.2 migration, change version_id * and minimum_version_id to 2 below (which breaks migration from @@ -320,6 +325,8 @@ static const VMStateDescription vmstate_acpi = { VMSTATE_UINT16(ar.pm1.evt.en, PIIX4PMState), VMSTATE_UINT16(ar.pm1.cnt.cnt, PIIX4PMState), VMSTATE_STRUCT(apm, PIIX4PMState, 0, vmstate_apm, APMState), + VMSTATE_STRUCT_TEST(smb, PIIX4PMState, piix4_vmstate_need_smbus, 3, + pmsmb_vmstate, PMSMBus), VMSTATE_TIMER_PTR(ar.tmr.timer, PIIX4PMState), VMSTATE_INT64(ar.tmr.overflow_time, PIIX4PMState), VMSTATE_STRUCT(ar.gpe, PIIX4PMState, 2, vmstate_gpe, ACPIGPE), @@ -370,13 +377,32 @@ static void piix4_pm_powerdown_req(Notifier *n, void *opaque) acpi_pm1_evt_power_down(&s->ar); } +static void piix4_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + PIIX4PMState *s = PIIX4_PM(hotplug_dev); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_pre_plug_cb(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (!s->acpi_memory_hotplug.is_enabled) { + error_setg(errp, + "memory hotplug is not enabled: %s.memory-hotplug-support " + "is not set", object_get_typename(OBJECT(s))); + } + } else if ( + !object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { + error_setg(errp, "acpi: device pre plug request for not supported" + " device type: %s", object_get_typename(OBJECT(dev))); + } +} + static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PIIX4PMState *s = PIIX4_PM(hotplug_dev); - if (s->acpi_memory_hotplug.is_enabled && - object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { + if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) { nvdimm_acpi_plug_cb(hotplug_dev, dev); } else { @@ -392,8 +418,7 @@ static void piix4_device_plug_cb(HotplugHandler *hotplug_dev, acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); } } else { - error_setg(errp, "acpi: device plug request for not supported device" - " type: %s", object_get_typename(OBJECT(dev))); + g_assert_not_reached(); } } @@ -407,8 +432,8 @@ static void piix4_device_unplug_request_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_request_cb(hotplug_dev, &s->acpi_memory_hotplug, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, - errp); + acpi_pcihp_device_unplug_request_cb(hotplug_dev, &s->acpi_pci_hotplug, + dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && !s->cpu_hotplug_legacy) { acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); @@ -426,6 +451,9 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, if (s->acpi_memory_hotplug.is_enabled && object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { acpi_memory_unplug_cb(&s->acpi_memory_hotplug, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_unplug_cb(hotplug_dev, &s->acpi_pci_hotplug, dev, + errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU) && !s->cpu_hotplug_legacy) { acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); @@ -435,15 +463,6 @@ static void piix4_device_unplug_cb(HotplugHandler *hotplug_dev, } } -static void piix4_update_bus_hotplug(PCIBus *pci_bus, void *opaque) -{ - PIIX4PMState *s = opaque; - - /* pci_bus cannot outlive PIIX4PMState, because /machine keeps it alive - * and it's not hot-unpluggable */ - qbus_set_hotplug_handler(BUS(pci_bus), DEVICE(s), &error_abort); -} - static void piix4_pm_machine_ready(Notifier *n, void *opaque) { PIIX4PMState *s = container_of(n, PIIX4PMState, machine_ready); @@ -457,12 +476,6 @@ static void piix4_pm_machine_ready(Notifier *n, void *opaque) pci_conf[0x63] = 0x60; pci_conf[0x67] = (memory_region_present(io_as, 0x3f8) ? 0x08 : 0) | (memory_region_present(io_as, 0x2f8) ? 0x90 : 0); - - if (s->use_acpi_pci_hotplug) { - pci_for_each_bus(pci_get_bus(d), piix4_update_bus_hotplug, s); - } else { - piix4_update_bus_hotplug(pci_get_bus(d), s); - } } static void piix4_pm_add_propeties(PIIX4PMState *s) @@ -512,7 +525,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x09; - pm_smbus_init(DEVICE(dev), &s->smb); + pm_smbus_init(DEVICE(dev), &s->smb, true); memory_region_set_enabled(&s->smb.io, pci_conf[0xd2] & 1); memory_region_add_subregion(pci_address_space_io(dev), s->smb_io_base, &s->smb.io); @@ -536,6 +549,7 @@ static void piix4_pm_realize(PCIDevice *dev, Error **errp) piix4_acpi_system_hot_add_init(pci_address_space_io(dev), pci_get_bus(dev), s); + qbus_set_hotplug_handler(BUS(pci_get_bus(dev)), OBJECT(s), &error_abort); piix4_pm_add_propeties(s); } @@ -702,6 +716,7 @@ static void piix4_pm_class_init(ObjectClass *klass, void *data) */ dc->user_creatable = false; dc->hotpluggable = false; + hc->pre_plug = piix4_device_pre_plug_cb; hc->plug = piix4_device_plug_cb; hc->unplug_request = piix4_device_unplug_request_cb; hc->unplug = piix4_device_unplug_cb; diff --git a/hw/acpi/tpm.c b/hw/acpi/tpm.c new file mode 100644 index 0000000000..b96459e45b --- /dev/null +++ b/hw/acpi/tpm.c @@ -0,0 +1,459 @@ +/* Support for generating ACPI TPM tables + * + * Copyright (C) 2018 IBM, Corp. + * Copyright (C) 2018 Red Hat Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, see . + */ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/acpi/tpm.h" + +void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev) +{ + Aml *method, *field, *ifctx, *ifctx2, *ifctx3, *func_mask, + *not_implemented, *pak, *tpm2, *tpm3, *pprm, *pprq, *zero, *one; + + if (!object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) { + return; + } + + zero = aml_int(0); + one = aml_int(1); + func_mask = aml_int(TPM_PPI_FUNC_MASK); + not_implemented = aml_int(TPM_PPI_FUNC_NOT_IMPLEMENTED); + + /* + * TPP2 is for the registers that ACPI code used to pass + * the PPI code and parameter (PPRQ, PPRM) to the firmware. + */ + aml_append(dev, + aml_operation_region("TPP2", AML_SYSTEM_MEMORY, + aml_int(TPM_PPI_ADDR_BASE + 0x100), + 0x5A)); + field = aml_field("TPP2", AML_ANY_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("PPIN", 8)); + aml_append(field, aml_named_field("PPIP", 32)); + aml_append(field, aml_named_field("PPRP", 32)); + aml_append(field, aml_named_field("PPRQ", 32)); + aml_append(field, aml_named_field("PPRM", 32)); + aml_append(field, aml_named_field("LPPR", 32)); + aml_append(dev, field); + pprq = aml_name("PPRQ"); + pprm = aml_name("PPRM"); + + aml_append(dev, + aml_operation_region( + "TPP3", AML_SYSTEM_MEMORY, + aml_int(TPM_PPI_ADDR_BASE + + 0x15a /* movv, docs/specs/tpm.txt */), + 0x1)); + field = aml_field("TPP3", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("MOVV", 8)); + aml_append(dev, field); + + /* + * DerefOf in Windows is broken with SYSTEM_MEMORY. Use a dynamic + * operation region inside of a method for getting FUNC[op]. + */ + method = aml_method("TPFN", 1, AML_SERIALIZED); + { + Aml *op = aml_arg(0); + ifctx = aml_if(aml_lgreater_equal(op, aml_int(0x100))); + { + aml_append(ifctx, aml_return(zero)); + } + aml_append(method, ifctx); + + aml_append(method, + aml_operation_region("TPP1", AML_SYSTEM_MEMORY, + aml_add(aml_int(TPM_PPI_ADDR_BASE), op, NULL), 0x1)); + field = aml_field("TPP1", AML_BYTE_ACC, AML_NOLOCK, AML_PRESERVE); + aml_append(field, aml_named_field("TPPF", 8)); + aml_append(method, field); + aml_append(method, aml_return(aml_name("TPPF"))); + } + aml_append(dev, method); + + /* + * Use global TPM2 & TPM3 variables to workaround Windows ACPI bug + * when returning packages. + */ + pak = aml_package(2); + aml_append(pak, zero); + aml_append(pak, zero); + aml_append(dev, aml_name_decl("TPM2", pak)); + tpm2 = aml_name("TPM2"); + + pak = aml_package(3); + aml_append(pak, zero); + aml_append(pak, zero); + aml_append(pak, zero); + aml_append(dev, aml_name_decl("TPM3", pak)); + tpm3 = aml_name("TPM3"); + + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + uint8_t zerobyte[1] = { 0 }; + Aml *function, *arguments, *rev, *op, *op_arg, *op_flags, *uuid; + + uuid = aml_arg(0); + rev = aml_arg(1); + function = aml_arg(2); + arguments = aml_arg(3); + op = aml_local(0); + op_flags = aml_local(1); + + /* Physical Presence Interface */ + ifctx = aml_if( + aml_equal(uuid, + aml_touuid("3DDDFAA6-361B-4EB4-A424-8D10089D1653"))); + { + /* standard DSM query function */ + ifctx2 = aml_if(aml_equal(function, zero)); + { + uint8_t byte_list[2] = { 0xff, 0x01 }; /* functions 1-8 */ + + aml_append(ifctx2, + aml_return(aml_buffer(sizeof(byte_list), + byte_list))); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.0: 2.1.1 Get Physical Presence Interface Version + * + * Arg 2 (Integer): Function Index = 1 + * Arg 3 (Package): Arguments = Empty Package + * Returns: Type: String + */ + ifctx2 = aml_if(aml_equal(function, one)); + { + aml_append(ifctx2, aml_return(aml_string("1.3"))); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.0: 2.1.3 Submit TPM Operation Request to Pre-OS Environment + * + * Arg 2 (Integer): Function Index = 2 + * Arg 3 (Package): Arguments = Package: Type: Integer + * Operation Value of the Request + * Returns: Type: Integer + * 0: Success + * 1: Operation Value of the Request Not Supported + * 2: General Failure + */ + ifctx2 = aml_if(aml_equal(function, aml_int(2))); + { + /* get opcode */ + aml_append(ifctx2, + aml_store(aml_derefof(aml_index(arguments, + zero)), op)); + + /* get opcode flags */ + aml_append(ifctx2, + aml_store(aml_call1("TPFN", op), op_flags)); + + /* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */ + ifctx3 = aml_if( + aml_equal( + aml_and(op_flags, func_mask, NULL), + not_implemented)); + { + /* 1: Operation Value of the Request Not Supported */ + aml_append(ifctx3, aml_return(one)); + } + aml_append(ifctx2, ifctx3); + + aml_append(ifctx2, aml_store(op, pprq)); + aml_append(ifctx2, aml_store(zero, pprm)); + /* 0: success */ + aml_append(ifctx2, aml_return(zero)); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.0: 2.1.4 Get Pending TPM Operation Requested By the OS + * + * Arg 2 (Integer): Function Index = 3 + * Arg 3 (Package): Arguments = Empty Package + * Returns: Type: Package of Integers + * Integer 1: Function Return code + * 0: Success + * 1: General Failure + * Integer 2: Pending operation requested by the OS + * 0: None + * >0: Operation Value of the Pending Request + * Integer 3: Optional argument to pending operation + * requested by the OS + * 0: None + * >0: Argument Value of the Pending Request + */ + ifctx2 = aml_if(aml_equal(function, aml_int(3))); + { + /* + * Revision ID of 1, no integer parameter beyond + * parameter two are expected + */ + ifctx3 = aml_if(aml_equal(rev, one)); + { + /* TPM2[1] = PPRQ */ + aml_append(ifctx3, + aml_store(pprq, aml_index(tpm2, one))); + aml_append(ifctx3, aml_return(tpm2)); + } + aml_append(ifctx2, ifctx3); + + /* + * A return value of {0, 23, 1} indicates that + * operation 23 with argument 1 is pending. + */ + ifctx3 = aml_if(aml_equal(rev, aml_int(2))); + { + /* TPM3[1] = PPRQ */ + aml_append(ifctx3, + aml_store(pprq, aml_index(tpm3, one))); + /* TPM3[2] = PPRM */ + aml_append(ifctx3, + aml_store(pprm, aml_index(tpm3, aml_int(2)))); + aml_append(ifctx3, aml_return(tpm3)); + } + aml_append(ifctx2, ifctx3); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.0: 2.1.5 Get Platform-Specific Action to Transition to + * Pre-OS Environment + * + * Arg 2 (Integer): Function Index = 4 + * Arg 3 (Package): Arguments = Empty Package + * Returns: Type: Integer + * 0: None + * 1: Shutdown + * 2: Reboot + * 3: OS Vendor-specific + */ + ifctx2 = aml_if(aml_equal(function, aml_int(4))); + { + /* reboot */ + aml_append(ifctx2, aml_return(aml_int(2))); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.0: 2.1.6 Return TPM Operation Response to OS Environment + * + * Arg 2 (Integer): Function Index = 5 + * Arg 3 (Package): Arguments = Empty Package + * Returns: Type: Package of Integer + * Integer 1: Function Return code + * 0: Success + * 1: General Failure + * Integer 2: Most recent operation request + * 0: None + * >0: Operation Value of the most recent request + * Integer 3: Response to the most recent operation request + * 0: Success + * 0x00000001..0x00000FFF: Corresponding TPM + * error code + * 0xFFFFFFF0: User Abort or timeout of dialog + * 0xFFFFFFF1: firmware Failure + */ + ifctx2 = aml_if(aml_equal(function, aml_int(5))); + { + /* TPM3[1] = LPPR */ + aml_append(ifctx2, + aml_store(aml_name("LPPR"), + aml_index(tpm3, one))); + /* TPM3[2] = PPRP */ + aml_append(ifctx2, + aml_store(aml_name("PPRP"), + aml_index(tpm3, aml_int(2)))); + aml_append(ifctx2, aml_return(tpm3)); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.0: 2.1.7 Submit preferred user language + * + * Arg 2 (Integer): Function Index = 6 + * Arg 3 (Package): Arguments = String Package + * Preferred language code + * Returns: Type: Integer + * Function Return Code + * 3: Not implemented + */ + ifctx2 = aml_if(aml_equal(function, aml_int(6))); + { + /* 3 = not implemented */ + aml_append(ifctx2, aml_return(aml_int(3))); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.1: 2.1.7 Submit TPM Operation Request to + * Pre-OS Environment 2 + * + * Arg 2 (Integer): Function Index = 7 + * Arg 3 (Package): Arguments = Package: Type: Integer + * Integer 1: Operation Value of the Request + * Integer 2: Argument for Operation (optional) + * Returns: Type: Integer + * 0: Success + * 1: Not Implemented + * 2: General Failure + * 3: Operation blocked by current firmware settings + */ + ifctx2 = aml_if(aml_equal(function, aml_int(7))); + { + /* get opcode */ + aml_append(ifctx2, aml_store(aml_derefof(aml_index(arguments, + zero)), + op)); + + /* get opcode flags */ + aml_append(ifctx2, aml_store(aml_call1("TPFN", op), + op_flags)); + /* if func[opcode] & TPM_PPI_FUNC_NOT_IMPLEMENTED */ + ifctx3 = aml_if( + aml_equal( + aml_and(op_flags, func_mask, NULL), + not_implemented)); + { + /* 1: not implemented */ + aml_append(ifctx3, aml_return(one)); + } + aml_append(ifctx2, ifctx3); + + /* if func[opcode] & TPM_PPI_FUNC_BLOCKED */ + ifctx3 = aml_if( + aml_equal( + aml_and(op_flags, func_mask, NULL), + aml_int(TPM_PPI_FUNC_BLOCKED))); + { + /* 3: blocked by firmware */ + aml_append(ifctx3, aml_return(aml_int(3))); + } + aml_append(ifctx2, ifctx3); + + /* revision to integer */ + ifctx3 = aml_if(aml_equal(rev, one)); + { + /* revision 1 */ + /* PPRQ = op */ + aml_append(ifctx3, aml_store(op, pprq)); + /* no argument, PPRM = 0 */ + aml_append(ifctx3, aml_store(zero, pprm)); + } + aml_append(ifctx2, ifctx3); + + ifctx3 = aml_if(aml_equal(rev, aml_int(2))); + { + /* revision 2 */ + /* PPRQ = op */ + op_arg = aml_derefof(aml_index(arguments, one)); + aml_append(ifctx3, aml_store(op, pprq)); + /* PPRM = arg3[1] */ + aml_append(ifctx3, aml_store(op_arg, pprm)); + } + aml_append(ifctx2, ifctx3); + /* 0: success */ + aml_append(ifctx2, aml_return(zero)); + } + aml_append(ifctx, ifctx2); + + /* + * PPI 1.1: 2.1.8 Get User Confirmation Status for Operation + * + * Arg 2 (Integer): Function Index = 8 + * Arg 3 (Package): Arguments = Package: Type: Integer + * Operation Value that may need user confirmation + * Returns: Type: Integer + * 0: Not implemented + * 1: Firmware only + * 2: Blocked for OS by firmware configuration + * 3: Allowed and physically present user required + * 4: Allowed and physically present user not required + */ + ifctx2 = aml_if(aml_equal(function, aml_int(8))); + { + /* get opcode */ + aml_append(ifctx2, + aml_store(aml_derefof(aml_index(arguments, + zero)), + op)); + + /* get opcode flags */ + aml_append(ifctx2, aml_store(aml_call1("TPFN", op), + op_flags)); + /* return confirmation status code */ + aml_append(ifctx2, + aml_return( + aml_and(op_flags, func_mask, NULL))); + } + aml_append(ifctx, ifctx2); + + aml_append(ifctx, aml_return(aml_buffer(1, zerobyte))); + } + aml_append(method, ifctx); + + /* + * "TCG Platform Reset Attack Mitigation Specification 1.00", + * Chapter 6 "ACPI _DSM Function" + */ + ifctx = aml_if( + aml_equal(uuid, + aml_touuid("376054ED-CC13-4675-901C-4756D7F2D45D"))); + { + /* standard DSM query function */ + ifctx2 = aml_if(aml_equal(function, zero)); + { + uint8_t byte_list[1] = { 0x03 }; /* functions 1-2 supported */ + + aml_append(ifctx2, + aml_return(aml_buffer(sizeof(byte_list), + byte_list))); + } + aml_append(ifctx, ifctx2); + + /* + * TCG Platform Reset Attack Mitigation Specification 1.0 Ch.6 + * + * Arg 2 (Integer): Function Index = 1 + * Arg 3 (Package): Arguments = Package: Type: Integer + * Operation Value of the Request + * Returns: Type: Integer + * 0: Success + * 1: General Failure + */ + ifctx2 = aml_if(aml_equal(function, one)); + { + aml_append(ifctx2, + aml_store(aml_derefof(aml_index(arguments, zero)), + op)); + { + aml_append(ifctx2, aml_store(op, aml_name("MOVV"))); + + /* 0: success */ + aml_append(ifctx2, aml_return(zero)); + } + } + aml_append(ifctx, ifctx2); + } + aml_append(method, ifctx); + } + aml_append(dev, method); +} diff --git a/hw/acpi/trace-events b/hw/acpi/trace-events index df0024f8b2..6272d8a9e7 100644 --- a/hw/acpi/trace-events +++ b/hw/acpi/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/acpi/memory_hotplug.c +# memory_hotplug.c mhp_acpi_invalid_slot_selected(uint32_t slot) "0x%"PRIx32 mhp_acpi_ejecting_invalid_slot(uint32_t slot) "0x%"PRIx32 mhp_acpi_read_addr_lo(uint32_t slot, uint32_t addr) "slot[0x%"PRIx32"] addr lo: 0x%"PRIx32 @@ -17,7 +17,7 @@ mhp_acpi_clear_remove_evt(uint32_t slot) "slot[0x%"PRIx32"] clear remove event" mhp_acpi_pc_dimm_deleted(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm deleted" mhp_acpi_pc_dimm_delete_failed(uint32_t slot) "slot[0x%"PRIx32"] pc-dimm delete failed" -# hw/acpi/cpu.c +# cpu.c cpuhp_acpi_invalid_idx_selected(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_read_flags(uint32_t idx, uint8_t flags) "idx[0x%"PRIx32"] flags: 0x%"PRIx8 cpuhp_acpi_write_idx(uint32_t idx) "set active cpu idx: 0x%"PRIx32 @@ -31,6 +31,6 @@ cpuhp_acpi_ejecting_cpu(uint32_t idx) "0x%"PRIx32 cpuhp_acpi_write_ost_ev(uint32_t slot, uint32_t ev) "idx[0x%"PRIx32"] OST EVENT: 0x%"PRIx32 cpuhp_acpi_write_ost_status(uint32_t slot, uint32_t st) "idx[0x%"PRIx32"] OST STATUS: 0x%"PRIx32 -# hw/acpi/tco.c +# tco.c tco_timer_reload(int ticks, int msec) "ticks=%d (%d ms)" tco_timer_expired(int timeouts_no, bool strap, bool no_reboot) "timeouts_no=%d no_reboot=%d/%d" diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index d78b579a20..02717a8b0d 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -30,8 +30,7 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, * first, since that's what the guest expects */ g_array_set_size(guid, VMGENID_FW_CFG_SIZE - ARRAY_SIZE(guid_le.data)); - guid_le = vms->guid; - qemu_uuid_bswap(&guid_le); + guid_le = qemu_uuid_bswap(vms->guid); /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" * see docs/specs/vmgenid.txt for more details @@ -149,8 +148,7 @@ static void vmgenid_update_guest(VmGenIdState *vms) * however, will expect the fields to be little-endian. * Perform a byte swap immediately before writing. */ - guid_le = vms->guid; - qemu_uuid_bswap(&guid_le); + guid_le = qemu_uuid_bswap(vms->guid); /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" * see docs/specs/vmgenid.txt for more details. diff --git a/hw/adc/Kconfig b/hw/adc/Kconfig new file mode 100644 index 0000000000..25d2229fb8 --- /dev/null +++ b/hw/adc/Kconfig @@ -0,0 +1,2 @@ +config STM32F2XX_ADC + bool diff --git a/hw/alpha/Kconfig b/hw/alpha/Kconfig new file mode 100644 index 0000000000..15c59ff264 --- /dev/null +++ b/hw/alpha/Kconfig @@ -0,0 +1,13 @@ +config DP264 + bool + imply PCI_DEVICES + imply TEST_DEVICES + imply E1000_PCI + select I82374 + select I8254 + select I8259 + select IDE_CMD646 + select MC146818RTC + select PCI + select PCKBD + select SMC37C669 diff --git a/hw/alpha/Makefile.objs b/hw/alpha/Makefile.objs index 5c742756f0..62fdf3edec 100644 --- a/hw/alpha/Makefile.objs +++ b/hw/alpha/Makefile.objs @@ -1 +1 @@ -obj-y += dp264.o pci.o typhoon.o +obj-$(CONFIG_DP264) += dp264.o pci.o typhoon.o diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 80b987f7fb..0347eb897c 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -114,7 +114,7 @@ static void clipper_init(MachineState *machine) error_report("no palcode provided"); exit(1); } - size = load_elf(palcode_filename, cpu_alpha_superpage_to_phys, + size = load_elf(palcode_filename, NULL, cpu_alpha_superpage_to_phys, NULL, &palcode_entry, &palcode_low, &palcode_high, 0, EM_ALPHA, 0, 0); if (size < 0) { @@ -133,7 +133,7 @@ static void clipper_init(MachineState *machine) if (kernel_filename) { uint64_t param_offset; - size = load_elf(kernel_filename, cpu_alpha_superpage_to_phys, + size = load_elf(kernel_filename, NULL, cpu_alpha_superpage_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, 0, EM_ALPHA, 0, 0); if (size < 0) { @@ -150,7 +150,8 @@ static void clipper_init(MachineState *machine) } if (initrd_filename) { - long initrd_base, initrd_size; + long initrd_base; + int64_t initrd_size; initrd_size = get_image_size(initrd_filename); if (initrd_size < 0) { diff --git a/hw/alpha/trace-events b/hw/alpha/trace-events index 46024cca0b..5b8315f27f 100644 --- a/hw/alpha/trace-events +++ b/hw/alpha/trace-events @@ -1,4 +1,4 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/alpha/pci.c +# pci.c alpha_pci_iack_write(void) "" diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index d74b5b55e1..9d57361c67 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -11,7 +11,6 @@ #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" -#include "hw/devices.h" #include "sysemu/sysemu.h" #include "alpha_sys.h" #include "exec/address-spaces.h" @@ -75,7 +74,9 @@ static void cpu_irq_change(AlphaCPU *cpu, uint64_t req) } } -static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) +static MemTxResult cchip_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) { CPUState *cpu = current_cpu; TyphoonState *s = opaque; @@ -196,11 +197,11 @@ static uint64_t cchip_read(void *opaque, hwaddr addr, unsigned size) break; default: - cpu_unassigned_access(cpu, addr, false, false, 0, size); - return -1; + return MEMTX_ERROR; } - return ret; + *data = ret; + return MEMTX_OK; } static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size) @@ -209,7 +210,8 @@ static uint64_t dchip_read(void *opaque, hwaddr addr, unsigned size) return 0; } -static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) +static MemTxResult pchip_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) { TyphoonState *s = opaque; uint64_t ret = 0; @@ -294,15 +296,16 @@ static uint64_t pchip_read(void *opaque, hwaddr addr, unsigned size) break; default: - cpu_unassigned_access(current_cpu, addr, false, false, 0, size); - return -1; + return MEMTX_ERROR; } - return ret; + *data = ret; + return MEMTX_OK; } -static void cchip_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static MemTxResult cchip_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { TyphoonState *s = opaque; uint64_t oldval, newval; @@ -446,9 +449,10 @@ static void cchip_write(void *opaque, hwaddr addr, break; default: - cpu_unassigned_access(current_cpu, addr, true, false, 0, size); - return; + return MEMTX_ERROR; } + + return MEMTX_OK; } static void dchip_write(void *opaque, hwaddr addr, @@ -457,8 +461,9 @@ static void dchip_write(void *opaque, hwaddr addr, /* Skip this. It's all related to DRAM timing and setup. */ } -static void pchip_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static MemTxResult pchip_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size, + MemTxAttrs attrs) { TyphoonState *s = opaque; uint64_t oldval; @@ -553,14 +558,15 @@ static void pchip_write(void *opaque, hwaddr addr, break; default: - cpu_unassigned_access(current_cpu, addr, true, false, 0, size); - return; + return MEMTX_ERROR; } + + return MEMTX_OK; } static const MemoryRegionOps cchip_ops = { - .read = cchip_read, - .write = cchip_write, + .read_with_attrs = cchip_read, + .write_with_attrs = cchip_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 8, @@ -587,8 +593,8 @@ static const MemoryRegionOps dchip_ops = { }; static const MemoryRegionOps pchip_ops = { - .read = pchip_read, - .write = pchip_write, + .read_with_attrs = pchip_read, + .write_with_attrs = pchip_write, .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 8, @@ -657,8 +663,8 @@ static bool window_translate(TyphoonWindow *win, hwaddr addr, pte_addr |= (addr & (wsm | 0xfe000)) >> 10; return pte_translate(pte_addr, ret); } else { - /* Direct-mapped translation. */ - return make_iommu_tlbe(tba & ~wsm_ext, wsm_ext, ret); + /* Direct-mapped translation. */ + return make_iommu_tlbe(tba & ~wsm_ext, wsm_ext, ret); } } @@ -693,7 +699,7 @@ static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, /* Check the fourth window for DAC disable. */ if ((pchip->win[3].wba & 0x80000000000ull) == 0 - && window_translate(&pchip->win[3], addr, &ret)) { + && window_translate(&pchip->win[3], addr, &ret)) { goto success; } } else { @@ -704,7 +710,7 @@ static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, if (pchip->ctl & 0x40) { /* See 10.1.4.4; in particular <39:35> is ignored. */ make_iommu_tlbe(0, 0x007ffffffffull, &ret); - goto success; + goto success; } } @@ -716,8 +722,8 @@ static IOMMUTLBEntry typhoon_translate_iommu(IOMMUMemoryRegion *iommu, pte_addr = pchip->win[3].tba & 0x7ffc00000ull; pte_addr |= (addr & 0xffffe000u) >> 10; if (pte_translate(pte_addr, &ret)) { - goto success; - } + goto success; + } } } } @@ -932,23 +938,10 @@ PCIBus *typhoon_init(ram_addr_t ram_size, ISABus **isa_bus, return b; } -static int typhoon_pcihost_init(SysBusDevice *dev) -{ - return 0; -} - -static void typhoon_pcihost_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = typhoon_pcihost_init; -} - static const TypeInfo typhoon_pcihost_info = { .name = TYPE_TYPHOON_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(TyphoonState), - .class_init = typhoon_pcihost_class_init, }; static void typhoon_iommu_memory_region_class_init(ObjectClass *klass, diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig new file mode 100644 index 0000000000..d298fbdc89 --- /dev/null +++ b/hw/arm/Kconfig @@ -0,0 +1,124 @@ +config ARM_VIRT + bool + imply VFIO_PLATFORM + +config DIGIC + bool + select PTIMER + +config EXYNOS4 + bool + select PTIMER + +config HIGHBANK + bool + +config INTEGRATOR + bool + +config MAINSTONE + bool + +config MUSICPAL + bool + select PTIMER + +config NETDUINO2 + bool + +config NSERIES + bool + +config OMAP + bool + +config PXA2XX + bool + +config REALVIEW + bool + +config STELLARIS + bool + +config STRONGARM + bool + +config VERSATILE + bool + +config ZYNQ + bool + +config ARM_V7M + bool + +config ALLWINNER_A10 + bool + +config RASPI + bool + +config STM32F205_SOC + bool + +config XLNX_ZYNQMP_ARM + bool + +config XLNX_VERSAL + bool + +config FSL_IMX25 + bool + +config FSL_IMX31 + bool + +config FSL_IMX6 + bool + +config ASPEED_SOC + bool + +config MPS2 + bool + +config FSL_IMX7 + bool + +config ARM_SMMUV3 + bool + +config FSL_IMX6UL + bool + +config NRF51_SOC + bool + +config MSF2 + bool + select PTIMER + +config ZAURUS + bool + +config A9MPCORE + bool + +config A15MPCORE + bool + +config ARM11MPCORE + bool + +config ARMSSE + bool + +config ARMSSE_CPUID + bool + +config ARMSSE_MHU + bool + +config MUSCA + bool diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index d51fcecaf2..fa57c7c770 100644 --- a/hw/arm/Makefile.objs +++ b/hw/arm/Makefile.objs @@ -1,4 +1,5 @@ -obj-y += boot.o virt.o sysbus-fdt.o +obj-y += boot.o sysbus-fdt.o +obj-$(CONFIG_ARM_VIRT) += virt.o obj-$(CONFIG_ACPI) += virt-acpi-build.o obj-$(CONFIG_DIGIC) += digic_boards.o obj-$(CONFIG_EXYNOS4) += exynos4_boards.o @@ -26,6 +27,7 @@ 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_ARM) += xlnx-zynqmp.o xlnx-zcu102.o +obj-$(CONFIG_XLNX_VERSAL) += xlnx-versal.o xlnx-versal-virt.o obj-$(CONFIG_FSL_IMX25) += fsl-imx25.o imx25_pdk.o obj-$(CONFIG_FSL_IMX31) += fsl-imx31.o kzm.o obj-$(CONFIG_FSL_IMX6) += fsl-imx6.o sabrelite.o @@ -33,6 +35,9 @@ obj-$(CONFIG_ASPEED_SOC) += aspeed_soc.o aspeed.o obj-$(CONFIG_MPS2) += mps2.o obj-$(CONFIG_MPS2) += mps2-tz.o obj-$(CONFIG_MSF2) += msf2-soc.o msf2-som.o -obj-$(CONFIG_IOTKIT) += iotkit.o +obj-$(CONFIG_MUSCA) += musca.o +obj-$(CONFIG_ARMSSE) += armsse.o obj-$(CONFIG_FSL_IMX7) += fsl-imx7.o mcimx7d-sabre.o obj-$(CONFIG_ARM_SMMUV3) += smmu-common.o smmuv3.o +obj-$(CONFIG_FSL_IMX6UL) += fsl-imx6ul.o mcimx6ul-evk.o +obj-$(CONFIG_NRF51_SOC) += nrf51_soc.o microbit.o diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index 9fe875cdb5..06ec6f4dc8 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -20,8 +20,8 @@ #include "qemu-common.h" #include "cpu.h" #include "hw/sysbus.h" -#include "hw/devices.h" #include "hw/arm/allwinner-a10.h" +#include "hw/misc/unimp.h" static void aw_a10_init(Object *obj) { @@ -85,6 +85,11 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sysbusdev, 4, s->irq[67]); sysbus_connect_irq(sysbusdev, 5, s->irq[68]); + memory_region_init_ram(&s->sram_a, OBJECT(dev), "sram A", 48 * KiB, + &error_fatal); + memory_region_add_subregion(get_system_memory(), 0x00000000, &s->sram_a); + create_unimplemented_device("a10-sram-ctrl", 0x01c00000, 4 * KiB); + /* FIXME use qdev NIC properties instead of nd_table[] */ if (nd_table[0].used) { qemu_check_nic_model(&nd_table[0], TYPE_AW_EMAC); diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c new file mode 100644 index 0000000000..76cc690579 --- /dev/null +++ b/hw/arm/armsse.c @@ -0,0 +1,1284 @@ +/* + * Arm SSE (Subsystems for Embedded): IoTKit + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/bitops.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/arm/armsse.h" +#include "hw/arm/arm.h" + +/* Format of the System Information block SYS_CONFIG register */ +typedef enum SysConfigFormat { + IoTKitFormat, + SSE200Format, +} SysConfigFormat; + +struct ARMSSEInfo { + const char *name; + int sram_banks; + int num_cpus; + uint32_t sys_version; + uint32_t cpuwait_rst; + SysConfigFormat sys_config_format; + bool has_mhus; + bool has_ppus; + bool has_cachectrl; + bool has_cpusecctrl; + bool has_cpuid; +}; + +static const ARMSSEInfo armsse_variants[] = { + { + .name = TYPE_IOTKIT, + .sram_banks = 1, + .num_cpus = 1, + .sys_version = 0x41743, + .cpuwait_rst = 0, + .sys_config_format = IoTKitFormat, + .has_mhus = false, + .has_ppus = false, + .has_cachectrl = false, + .has_cpusecctrl = false, + .has_cpuid = false, + }, + { + .name = TYPE_SSE200, + .sram_banks = 4, + .num_cpus = 2, + .sys_version = 0x22041743, + .cpuwait_rst = 2, + .sys_config_format = SSE200Format, + .has_mhus = true, + .has_ppus = true, + .has_cachectrl = true, + .has_cpusecctrl = true, + .has_cpuid = true, + }, +}; + +static uint32_t armsse_sys_config_value(ARMSSE *s, const ARMSSEInfo *info) +{ + /* Return the SYS_CONFIG value for this SSE */ + uint32_t sys_config; + + switch (info->sys_config_format) { + case IoTKitFormat: + sys_config = 0; + sys_config = deposit32(sys_config, 0, 4, info->sram_banks); + sys_config = deposit32(sys_config, 4, 4, s->sram_addr_width - 12); + break; + case SSE200Format: + sys_config = 0; + sys_config = deposit32(sys_config, 0, 4, info->sram_banks); + sys_config = deposit32(sys_config, 4, 5, s->sram_addr_width); + sys_config = deposit32(sys_config, 24, 4, 2); + if (info->num_cpus > 1) { + sys_config = deposit32(sys_config, 10, 1, 1); + sys_config = deposit32(sys_config, 20, 4, info->sram_banks - 1); + sys_config = deposit32(sys_config, 28, 4, 2); + } + break; + default: + g_assert_not_reached(); + } + return sys_config; +} + +/* Clock frequency in HZ of the 32KHz "slow clock" */ +#define S32KCLK (32 * 1000) + +/* Is internal IRQ n shared between CPUs in a multi-core SSE ? */ +static bool irq_is_common[32] = { + [0 ... 5] = true, + /* 6, 7: per-CPU MHU interrupts */ + [8 ... 12] = true, + /* 13: per-CPU icache interrupt */ + /* 14: reserved */ + [15 ... 20] = true, + /* 21: reserved */ + [22 ... 26] = true, + /* 27: reserved */ + /* 28, 29: per-CPU CTI interrupts */ + /* 30, 31: reserved */ +}; + +/* + * Create an alias region in @container of @size bytes starting at @base + * which mirrors the memory starting at @orig. + */ +static void make_alias(ARMSSE *s, MemoryRegion *mr, MemoryRegion *container, + const char *name, hwaddr base, hwaddr size, hwaddr orig) +{ + memory_region_init_alias(mr, NULL, name, container, orig, size); + /* The alias is even lower priority than unimplemented_device regions */ + memory_region_add_subregion_overlap(container, base, mr, -1500); +} + +static void irq_status_forwarder(void *opaque, int n, int level) +{ + qemu_irq destirq = opaque; + + qemu_set_irq(destirq, level); +} + +static void nsccfg_handler(void *opaque, int n, int level) +{ + ARMSSE *s = ARMSSE(opaque); + + s->nsccfg = level; +} + +static void armsse_forward_ppc(ARMSSE *s, const char *ppcname, int ppcnum) +{ + /* Each of the 4 AHB and 4 APB PPCs that might be present in a + * system using the ARMSSE has a collection of control lines which + * are provided by the security controller and which we want to + * expose as control lines on the ARMSSE device itself, so the + * code using the ARMSSE can wire them up to the PPCs. + */ + SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; + DeviceState *armssedev = DEVICE(s); + DeviceState *dev_secctl = DEVICE(&s->secctl); + DeviceState *dev_splitter = DEVICE(splitter); + char *name; + + name = g_strdup_printf("%s_nonsec", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + name = g_strdup_printf("%s_ap", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + name = g_strdup_printf("%s_irq_enable", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + name = g_strdup_printf("%s_irq_clear", ppcname); + qdev_pass_gpios(dev_secctl, armssedev, name); + g_free(name); + + /* irq_status is a little more tricky, because we need to + * split it so we can send it both to the security controller + * and to our OR gate for the NVIC interrupt line. + * Connect up the splitter's outputs, and create a GPIO input + * which will pass the line state to the input splitter. + */ + name = g_strdup_printf("%s_irq_status", ppcname); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + name, 0)); + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); + s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); + qdev_init_gpio_in_named_with_opaque(armssedev, irq_status_forwarder, + s->irq_status_in[ppcnum], name, 1); + g_free(name); +} + +static void armsse_forward_sec_resp_cfg(ARMSSE *s) +{ + /* Forward the 3rd output from the splitter device as a + * named GPIO output of the armsse object. + */ + DeviceState *dev = DEVICE(s); + DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); + + qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); + s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, + s->sec_resp_cfg, 1); + qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); +} + +static void armsse_init(Object *obj) +{ + ARMSSE *s = ARMSSE(obj); + ARMSSEClass *asc = ARMSSE_GET_CLASS(obj); + const ARMSSEInfo *info = asc->info; + int i; + + assert(info->sram_banks <= MAX_SRAM_BANKS); + assert(info->num_cpus <= SSE_MAX_CPUS); + + memory_region_init(&s->container, obj, "armsse-container", UINT64_MAX); + + for (i = 0; i < info->num_cpus; i++) { + /* + * We put each CPU in its own cluster as they are logically + * distinct and may be configured differently. + */ + char *name; + + name = g_strdup_printf("cluster%d", i); + object_initialize_child(obj, name, &s->cluster[i], + sizeof(s->cluster[i]), TYPE_CPU_CLUSTER, + &error_abort, NULL); + qdev_prop_set_uint32(DEVICE(&s->cluster[i]), "cluster-id", i); + g_free(name); + + name = g_strdup_printf("armv7m%d", i); + sysbus_init_child_obj(OBJECT(&s->cluster[i]), name, + &s->armv7m[i], sizeof(s->armv7m), TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->armv7m[i]), "cpu-type", + ARM_CPU_TYPE_NAME("cortex-m33")); + g_free(name); + name = g_strdup_printf("arm-sse-cpu-container%d", i); + memory_region_init(&s->cpu_container[i], obj, name, UINT64_MAX); + g_free(name); + if (i > 0) { + name = g_strdup_printf("arm-sse-container-alias%d", i); + memory_region_init_alias(&s->container_alias[i - 1], obj, + name, &s->container, 0, UINT64_MAX); + g_free(name); + } + } + + sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), + TYPE_IOTKIT_SECCTL); + sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), + TYPE_TZ_PPC); + sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), + TYPE_TZ_PPC); + for (i = 0; i < info->sram_banks; i++) { + char *name = g_strdup_printf("mpc%d", i); + sysbus_init_child_obj(obj, name, &s->mpc[i], + sizeof(s->mpc[i]), TYPE_TZ_MPC); + g_free(name); + } + object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, + sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + + for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { + char *name = g_strdup_printf("mpc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), + TYPE_CMSDK_APB_TIMER); + sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), + TYPE_CMSDK_APB_DUALTIMER); + sysbus_init_child_obj(obj, "s32kwatchdog", &s->s32kwatchdog, + sizeof(s->s32kwatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "nswatchdog", &s->nswatchdog, + sizeof(s->nswatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "swatchdog", &s->swatchdog, + sizeof(s->swatchdog), TYPE_CMSDK_APB_WATCHDOG); + sysbus_init_child_obj(obj, "armsse-sysctl", &s->sysctl, + sizeof(s->sysctl), TYPE_IOTKIT_SYSCTL); + sysbus_init_child_obj(obj, "armsse-sysinfo", &s->sysinfo, + sizeof(s->sysinfo), TYPE_IOTKIT_SYSINFO); + if (info->has_mhus) { + sysbus_init_child_obj(obj, "mhu0", &s->mhu[0], sizeof(s->mhu[0]), + TYPE_ARMSSE_MHU); + sysbus_init_child_obj(obj, "mhu1", &s->mhu[1], sizeof(s->mhu[1]), + TYPE_ARMSSE_MHU); + } + if (info->has_ppus) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("CPU%dCORE_PPU", i); + int ppuidx = CPU0CORE_PPU + i; + + sysbus_init_child_obj(obj, name, &s->ppu[ppuidx], + sizeof(s->ppu[ppuidx]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + sysbus_init_child_obj(obj, "DBG_PPU", &s->ppu[DBG_PPU], + sizeof(s->ppu[DBG_PPU]), + TYPE_UNIMPLEMENTED_DEVICE); + for (i = 0; i < info->sram_banks; i++) { + char *name = g_strdup_printf("RAM%d_PPU", i); + int ppuidx = RAM0_PPU + i; + + sysbus_init_child_obj(obj, name, &s->ppu[ppuidx], + sizeof(s->ppu[ppuidx]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + } + if (info->has_cachectrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cachectrl%d", i); + + sysbus_init_child_obj(obj, name, &s->cachectrl[i], + sizeof(s->cachectrl[i]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + } + if (info->has_cpusecctrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cpusecctrl%d", i); + + sysbus_init_child_obj(obj, name, &s->cpusecctrl[i], + sizeof(s->cpusecctrl[i]), + TYPE_UNIMPLEMENTED_DEVICE); + g_free(name); + } + } + if (info->has_cpuid) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cpuid%d", i); + + sysbus_init_child_obj(obj, name, &s->cpuid[i], + sizeof(s->cpuid[i]), + TYPE_ARMSSE_CPUID); + g_free(name); + } + } + object_initialize_child(obj, "nmi-orgate", &s->nmi_orgate, + sizeof(s->nmi_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, + sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, + &error_abort, NULL); + object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, + sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, + &error_abort, NULL); + for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { + char *name = g_strdup_printf("ppc-irq-splitter-%d", i); + SplitIRQ *splitter = &s->ppc_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + if (info->num_cpus > 1) { + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) { + if (irq_is_common[i]) { + char *name = g_strdup_printf("cpu-irq-splitter%d", i); + SplitIRQ *splitter = &s->cpu_irq_splitter[i]; + + object_initialize_child(obj, name, splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_abort, NULL); + g_free(name); + } + } + } +} + +static void armsse_exp_irq(void *opaque, int n, int level) +{ + qemu_irq *irqarray = opaque; + + qemu_set_irq(irqarray[n], level); +} + +static void armsse_mpcexp_status(void *opaque, int n, int level) +{ + ARMSSE *s = ARMSSE(opaque); + qemu_set_irq(s->mpcexp_status_in[n], level); +} + +static qemu_irq armsse_get_common_irq_in(ARMSSE *s, int irqno) +{ + /* + * Return a qemu_irq which can be used to signal IRQ n to + * all CPUs in the SSE. + */ + ARMSSEClass *asc = ARMSSE_GET_CLASS(s); + const ARMSSEInfo *info = asc->info; + + assert(irq_is_common[irqno]); + + if (info->num_cpus == 1) { + /* Only one CPU -- just connect directly to it */ + return qdev_get_gpio_in(DEVICE(&s->armv7m[0]), irqno); + } else { + /* Connect to the splitter which feeds all CPUs */ + return qdev_get_gpio_in(DEVICE(&s->cpu_irq_splitter[irqno]), 0); + } +} + +static void map_ppu(ARMSSE *s, int ppuidx, const char *name, hwaddr addr) +{ + /* Map a PPU unimplemented device stub */ + DeviceState *dev = DEVICE(&s->ppu[ppuidx]); + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", 0x1000); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ppu[ppuidx]), 0, addr); +} + +static void armsse_realize(DeviceState *dev, Error **errp) +{ + ARMSSE *s = ARMSSE(dev); + ARMSSEClass *asc = ARMSSE_GET_CLASS(dev); + const ARMSSEInfo *info = asc->info; + int i; + MemoryRegion *mr; + Error *err = NULL; + SysBusDevice *sbd_apb_ppc0; + SysBusDevice *sbd_secctl; + DeviceState *dev_apb_ppc0; + DeviceState *dev_apb_ppc1; + DeviceState *dev_secctl; + DeviceState *dev_splitter; + uint32_t addr_width_max; + + if (!s->board_memory) { + error_setg(errp, "memory property was not set"); + return; + } + + if (!s->mainclk_frq) { + error_setg(errp, "MAINCLK property was not set"); + return; + } + + /* max SRAM_ADDR_WIDTH: 24 - log2(SRAM_NUM_BANK) */ + assert(is_power_of_2(info->sram_banks)); + addr_width_max = 24 - ctz32(info->sram_banks); + if (s->sram_addr_width < 1 || s->sram_addr_width > addr_width_max) { + error_setg(errp, "SRAM_ADDR_WIDTH must be between 1 and %d", + addr_width_max); + return; + } + + /* Handling of which devices should be available only to secure + * code is usually done differently for M profile than for A profile. + * Instead of putting some devices only into the secure address space, + * devices exist in both address spaces but with hard-wired security + * permissions that will cause the CPU to fault for non-secure accesses. + * + * The ARMSSE has an IDAU (Implementation Defined Access Unit), + * which specifies hard-wired security permissions for different + * areas of the physical address space. For the ARMSSE IDAU, the + * top 4 bits of the physical address are the IDAU region ID, and + * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS + * region, otherwise it is an S region. + * + * The various devices and RAMs are generally all mapped twice, + * once into a region that the IDAU defines as secure and once + * into a non-secure region. They sit behind either a Memory + * Protection Controller (for RAM) or a Peripheral Protection + * Controller (for devices), which allow a more fine grained + * configuration of whether non-secure accesses are permitted. + * + * (The other place that guest software can configure security + * permissions is in the architected SAU (Security Attribution + * Unit), which is entirely inside the CPU. The IDAU can upgrade + * the security attributes for a region to more restrictive than + * the SAU specifies, but cannot downgrade them.) + * + * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff + * 0x20000000..0x2007ffff 32KB FPGA block RAM + * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff + * 0x40000000..0x4000ffff base peripheral region 1 + * 0x40010000..0x4001ffff CPU peripherals (none for ARMSSE) + * 0x40020000..0x4002ffff system control element peripherals + * 0x40080000..0x400fffff base peripheral region 2 + * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff + */ + + memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -2); + + for (i = 0; i < info->num_cpus; i++) { + DeviceState *cpudev = DEVICE(&s->armv7m[i]); + Object *cpuobj = OBJECT(&s->armv7m[i]); + int j; + char *gpioname; + + qdev_prop_set_uint32(cpudev, "num-irq", s->exp_numirq + 32); + /* + * In real hardware the initial Secure VTOR is set from the INITSVTOR* + * registers in the IoT Kit System Control Register block. In QEMU + * we set the initial value here, and also the reset value of the + * sysctl register, from this object's QOM init-svtor property. + * If the guest changes the INITSVTOR* registers at runtime then the + * code in iotkit-sysctl.c will update the CPU init-svtor property + * (which will then take effect on the next CPU warm-reset). + * + * Note that typically a board using the SSE-200 will have a system + * control processor whose boot firmware initializes the INITSVTOR* + * registers before powering up the CPUs. QEMU doesn't emulate + * the control processor, so instead we behave in the way that the + * firmware does: the initial value should be set by the board code + * (using the init-svtor property on the ARMSSE object) to match + * whatever its firmware does. + */ + qdev_prop_set_uint32(cpudev, "init-svtor", s->init_svtor); + /* + * CPUs start powered down if the corresponding bit in the CPUWAIT + * register is 1. In real hardware the CPUWAIT register reset value is + * a configurable property of the SSE-200 (via the CPUWAIT0_RST and + * CPUWAIT1_RST parameters), but since all the boards we care about + * start CPU0 and leave CPU1 powered off, we hard-code that in + * info->cpuwait_rst for now. We can add QOM properties for this + * later if necessary. + */ + if (extract32(info->cpuwait_rst, i, 1)) { + object_property_set_bool(cpuobj, true, "start-powered-off", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + if (i > 0) { + memory_region_add_subregion_overlap(&s->cpu_container[i], 0, + &s->container_alias[i - 1], -1); + } else { + memory_region_add_subregion_overlap(&s->cpu_container[i], 0, + &s->container, -1); + } + object_property_set_link(cpuobj, OBJECT(&s->cpu_container[i]), + "memory", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(cpuobj, OBJECT(s), "idau", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(cpuobj, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* + * The cluster must be realized after the armv7m container, as + * the container's CPU object is only created on realize, and the + * CPU must exist and have been parented into the cluster before + * the cluster is realized. + */ + object_property_set_bool(OBJECT(&s->cluster[i]), + true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + /* Connect EXP_IRQ/EXP_CPUn_IRQ GPIOs to the NVIC's lines 32 and up */ + s->exp_irqs[i] = g_new(qemu_irq, s->exp_numirq); + for (j = 0; j < s->exp_numirq; j++) { + s->exp_irqs[i][j] = qdev_get_gpio_in(cpudev, j + 32); + } + if (i == 0) { + gpioname = g_strdup("EXP_IRQ"); + } else { + gpioname = g_strdup_printf("EXP_CPU%d_IRQ", i); + } + qdev_init_gpio_in_named_with_opaque(dev, armsse_exp_irq, + s->exp_irqs[i], + gpioname, s->exp_numirq); + g_free(gpioname); + } + + /* Wire up the splitters that connect common IRQs to all CPUs */ + if (info->num_cpus > 1) { + for (i = 0; i < ARRAY_SIZE(s->cpu_irq_splitter); i++) { + if (irq_is_common[i]) { + Object *splitter = OBJECT(&s->cpu_irq_splitter[i]); + DeviceState *devs = DEVICE(splitter); + int cpunum; + + object_property_set_int(splitter, info->num_cpus, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(splitter, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + for (cpunum = 0; cpunum < info->num_cpus; cpunum++) { + DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]); + + qdev_connect_gpio_out(devs, cpunum, + qdev_get_gpio_in(cpudev, i)); + } + } + } + } + + /* Set up the big aliases first */ + make_alias(s, &s->alias1, &s->container, "alias 1", + 0x10000000, 0x10000000, 0x00000000); + make_alias(s, &s->alias2, &s->container, + "alias 2", 0x30000000, 0x10000000, 0x20000000); + /* The 0x50000000..0x5fffffff region is not a pure alias: it has + * a few extra devices that only appear there (generally the + * control interfaces for the protection controllers). + * We implement this by mapping those devices over the top of this + * alias MR at a higher priority. Some of the devices in this range + * are per-CPU, so we must put this alias in the per-cpu containers. + */ + for (i = 0; i < info->num_cpus; i++) { + make_alias(s, &s->alias3[i], &s->cpu_container[i], + "alias 3", 0x50000000, 0x10000000, 0x40000000); + } + + /* Security controller */ + object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sbd_secctl = SYS_BUS_DEVICE(&s->secctl); + dev_secctl = DEVICE(&s->secctl); + sysbus_mmio_map(sbd_secctl, 0, 0x50080000); + sysbus_mmio_map(sbd_secctl, 1, 0x40080000); + + s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); + qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); + + /* The sec_resp_cfg output from the security controller must be split into + * multiple lines, one for each of the PPCs within the ARMSSE and one + * that will be an output from the ARMSSE to the system. + */ + object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + dev_splitter = DEVICE(&s->sec_resp_splitter); + qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, + qdev_get_gpio_in(dev_splitter, 0)); + + /* Each SRAM bank lives behind its own Memory Protection Controller */ + for (i = 0; i < info->sram_banks; i++) { + char *ramname = g_strdup_printf("armsse.sram%d", i); + SysBusDevice *sbd_mpc; + uint32_t sram_bank_size = 1 << s->sram_addr_width; + + memory_region_init_ram(&s->sram[i], NULL, ramname, + sram_bank_size, &err); + g_free(ramname); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_link(OBJECT(&s->mpc[i]), OBJECT(&s->sram[i]), + "downstream", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* Map the upstream end of the MPC into the right place... */ + sbd_mpc = SYS_BUS_DEVICE(&s->mpc[i]); + memory_region_add_subregion(&s->container, + 0x20000000 + i * sram_bank_size, + sysbus_mmio_get_region(sbd_mpc, 1)); + /* ...and its register interface */ + memory_region_add_subregion(&s->container, 0x50083000 + i * 0x1000, + sysbus_mmio_get_region(sbd_mpc, 0)); + } + + /* We must OR together lines from the MPC splitters to go to the NVIC */ + object_property_set_int(OBJECT(&s->mpc_irq_orgate), + IOTS_NUM_EXP_MPC + info->sram_banks, + "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, + armsse_get_common_irq_in(s, 9)); + + /* Devices behind APB PPC0: + * 0x40000000: timer0 + * 0x40001000: timer1 + * 0x40002000: dual timer + * 0x40003000: MHU0 (SSE-200 only) + * 0x40004000: MHU1 (SSE-200 only) + * We must configure and realize each downstream device and connect + * it to the appropriate PPC port; then we can realize the PPC and + * map its upstream ends to the right place in the container. + */ + qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, + armsse_get_common_irq_in(s, 3)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, + armsse_get_common_irq_in(s, 4)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + + qdev_prop_set_uint32(DEVICE(&s->dualtimer), "pclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->dualtimer), 0, + armsse_get_common_irq_in(s, 5)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (info->has_mhus) { + /* + * An SSE-200 with only one CPU should have only one MHU created, + * with the region where the second MHU usually is being RAZ/WI. + * We don't implement that SSE-200 config; if we want to support + * it then this code needs to be enhanced to handle creating the + * RAZ/WI region instead of the second MHU. + */ + assert(info->num_cpus == ARRAY_SIZE(s->mhu)); + + for (i = 0; i < ARRAY_SIZE(s->mhu); i++) { + char *port; + int cpunum; + SysBusDevice *mhu_sbd = SYS_BUS_DEVICE(&s->mhu[i]); + + object_property_set_bool(OBJECT(&s->mhu[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + port = g_strdup_printf("port[%d]", i + 3); + mr = sysbus_mmio_get_region(mhu_sbd, 0); + object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), + port, &err); + g_free(port); + if (err) { + error_propagate(errp, err); + return; + } + + /* + * Each MHU has an irq line for each CPU: + * MHU 0 irq line 0 -> CPU 0 IRQ 6 + * MHU 0 irq line 1 -> CPU 1 IRQ 6 + * MHU 1 irq line 0 -> CPU 0 IRQ 7 + * MHU 1 irq line 1 -> CPU 1 IRQ 7 + */ + for (cpunum = 0; cpunum < info->num_cpus; cpunum++) { + DeviceState *cpudev = DEVICE(&s->armv7m[cpunum]); + + sysbus_connect_irq(mhu_sbd, cpunum, + qdev_get_gpio_in(cpudev, 6 + i)); + } + } + } + + object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); + dev_apb_ppc0 = DEVICE(&s->apb_ppc0); + + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); + memory_region_add_subregion(&s->container, 0x40000000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); + memory_region_add_subregion(&s->container, 0x40001000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); + memory_region_add_subregion(&s->container, 0x40002000, mr); + if (info->has_mhus) { + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 3); + memory_region_add_subregion(&s->container, 0x40003000, mr); + mr = sysbus_mmio_get_region(sbd_apb_ppc0, 4); + memory_region_add_subregion(&s->container, 0x40004000, mr); + } + for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_nonsec", i)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_ap", i)); + } + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "irq_enable", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "irq_clear", 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_apb_ppc0, + "cfg_sec_resp", 0)); + + /* All the PPC irq lines (from the 2 internal PPCs and the 8 external + * ones) are sent individually to the security controller, and also + * ORed together to give a single combined PPC interrupt to the NVIC. + */ + object_property_set_int(OBJECT(&s->ppc_irq_orgate), + NUM_PPCS, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, + armsse_get_common_irq_in(s, 10)); + + /* + * 0x40010000 .. 0x4001ffff (and the 0x5001000... secure-only alias): + * private per-CPU region (all these devices are SSE-200 only): + * 0x50010000: L1 icache control registers + * 0x50011000: CPUSECCTRL (CPU local security control registers) + * 0x4001f000 and 0x5001f000: CPU_IDENTITY register block + */ + if (info->has_cachectrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("cachectrl%d", i); + MemoryRegion *mr; + + qdev_prop_set_string(DEVICE(&s->cachectrl[i]), "name", name); + g_free(name); + qdev_prop_set_uint64(DEVICE(&s->cachectrl[i]), "size", 0x1000); + object_property_set_bool(OBJECT(&s->cachectrl[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cachectrl[i]), 0); + memory_region_add_subregion(&s->cpu_container[i], 0x50010000, mr); + } + } + if (info->has_cpusecctrl) { + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("CPUSECCTRL%d", i); + MemoryRegion *mr; + + qdev_prop_set_string(DEVICE(&s->cpusecctrl[i]), "name", name); + g_free(name); + qdev_prop_set_uint64(DEVICE(&s->cpusecctrl[i]), "size", 0x1000); + object_property_set_bool(OBJECT(&s->cpusecctrl[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpusecctrl[i]), 0); + memory_region_add_subregion(&s->cpu_container[i], 0x50011000, mr); + } + } + if (info->has_cpuid) { + for (i = 0; i < info->num_cpus; i++) { + MemoryRegion *mr; + + qdev_prop_set_uint32(DEVICE(&s->cpuid[i]), "CPUID", i); + object_property_set_bool(OBJECT(&s->cpuid[i]), true, + "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->cpuid[i]), 0); + memory_region_add_subregion(&s->cpu_container[i], 0x4001F000, mr); + } + } + + /* 0x40020000 .. 0x4002ffff : ARMSSE system control peripheral region */ + /* Devices behind APB PPC1: + * 0x4002f000: S32K timer + */ + qdev_prop_set_uint32(DEVICE(&s->s32ktimer), "pclk-frq", S32KCLK); + object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32ktimer), 0, + armsse_get_common_irq_in(s, 2)); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); + object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); + memory_region_add_subregion(&s->container, 0x4002f000, mr); + + dev_apb_ppc1 = DEVICE(&s->apb_ppc1); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_nonsec", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_ap", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "irq_enable", 0)); + qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, + qdev_get_gpio_in_named(dev_apb_ppc1, + "irq_clear", 0)); + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in_named(dev_apb_ppc1, + "cfg_sec_resp", 0)); + + object_property_set_int(OBJECT(&s->sysinfo), info->sys_version, + "SYS_VERSION", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_int(OBJECT(&s->sysinfo), + armsse_sys_config_value(s, info), + "SYS_CONFIG", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->sysinfo), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + /* System information registers */ + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysinfo), 0, 0x40020000); + /* System control registers */ + object_property_set_int(OBJECT(&s->sysctl), info->sys_version, + "SYS_VERSION", &err); + object_property_set_int(OBJECT(&s->sysctl), info->cpuwait_rst, + "CPUWAIT_RST", &err); + object_property_set_int(OBJECT(&s->sysctl), s->init_svtor, + "INITSVTOR0_RST", &err); + object_property_set_int(OBJECT(&s->sysctl), s->init_svtor, + "INITSVTOR1_RST", &err); + object_property_set_bool(OBJECT(&s->sysctl), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_mmio_map(SYS_BUS_DEVICE(&s->sysctl), 0, 0x50021000); + + if (info->has_ppus) { + /* CPUnCORE_PPU for each CPU */ + for (i = 0; i < info->num_cpus; i++) { + char *name = g_strdup_printf("CPU%dCORE_PPU", i); + + map_ppu(s, CPU0CORE_PPU + i, name, 0x50023000 + i * 0x2000); + /* + * We don't support CPU debug so don't create the + * CPU0DEBUG_PPU at 0x50024000 and 0x50026000. + */ + g_free(name); + } + map_ppu(s, DBG_PPU, "DBG_PPU", 0x50029000); + + for (i = 0; i < info->sram_banks; i++) { + char *name = g_strdup_printf("RAM%d_PPU", i); + + map_ppu(s, RAM0_PPU + i, name, 0x5002a000 + i * 0x1000); + g_free(name); + } + } + + /* This OR gate wires together outputs from the secure watchdogs to NMI */ + object_property_set_int(OBJECT(&s->nmi_orgate), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->nmi_orgate), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + qdev_connect_gpio_out(DEVICE(&s->nmi_orgate), 0, + qdev_get_gpio_in_named(DEVICE(&s->armv7m), "NMI", 0)); + + qdev_prop_set_uint32(DEVICE(&s->s32kwatchdog), "wdogclk-frq", S32KCLK); + object_property_set_bool(OBJECT(&s->s32kwatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 0)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->s32kwatchdog), 0, 0x5002e000); + + /* 0x40080000 .. 0x4008ffff : ARMSSE second Base peripheral region */ + + qdev_prop_set_uint32(DEVICE(&s->nswatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->nswatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->nswatchdog), 0, + armsse_get_common_irq_in(s, 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->nswatchdog), 0, 0x40081000); + + qdev_prop_set_uint32(DEVICE(&s->swatchdog), "wdogclk-frq", s->mainclk_frq); + object_property_set_bool(OBJECT(&s->swatchdog), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + sysbus_connect_irq(SYS_BUS_DEVICE(&s->swatchdog), 0, + qdev_get_gpio_in(DEVICE(&s->nmi_orgate), 1)); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->swatchdog), 0, 0x50081000); + + for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { + Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); + + object_property_set_int(splitter, 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(splitter, true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + } + + for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { + char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); + + armsse_forward_ppc(s, ppcname, i); + g_free(ppcname); + } + + for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { + char *ppcname = g_strdup_printf("apb_ppcexp%d", i); + + armsse_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); + g_free(ppcname); + } + + for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { + /* Wire up IRQ splitter for internal PPCs */ + DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); + char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", + i - NUM_EXTERNAL_PPCS); + TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; + + qdev_connect_gpio_out(devs, 0, + qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); + qdev_connect_gpio_out(devs, 1, + qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); + qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, + qdev_get_gpio_in(devs, 0)); + g_free(gpioname); + } + + /* Wire up the splitters for the MPC IRQs */ + for (i = 0; i < IOTS_NUM_EXP_MPC + info->sram_banks; i++) { + SplitIRQ *splitter = &s->mpc_irq_splitter[i]; + DeviceState *dev_splitter = DEVICE(splitter); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(splitter), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + if (i < IOTS_NUM_EXP_MPC) { + /* Splitter input is from GPIO input line */ + s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpcexp_status", i)); + } else { + /* Splitter input is from our own MPC */ + qdev_connect_gpio_out_named(DEVICE(&s->mpc[i - IOTS_NUM_EXP_MPC]), + "irq", 0, + qdev_get_gpio_in(dev_splitter, 0)); + qdev_connect_gpio_out(dev_splitter, 0, + qdev_get_gpio_in_named(dev_secctl, + "mpc_status", 0)); + } + + qdev_connect_gpio_out(dev_splitter, 1, + qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); + } + /* Create GPIO inputs which will pass the line state for our + * mpcexp_irq inputs to the correct splitter devices. + */ + qdev_init_gpio_in_named(dev, armsse_mpcexp_status, "mpcexp_status", + IOTS_NUM_EXP_MPC); + + armsse_forward_sec_resp_cfg(s); + + /* Forward the MSC related signals */ + qdev_pass_gpios(dev_secctl, dev, "mscexp_status"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_clear"); + qdev_pass_gpios(dev_secctl, dev, "mscexp_ns"); + qdev_connect_gpio_out_named(dev_secctl, "msc_irq", 0, + armsse_get_common_irq_in(s, 11)); + + /* + * Expose our container region to the board model; this corresponds + * to the AHB Slave Expansion ports which allow bus master devices + * (eg DMA controllers) in the board model to make transactions into + * devices in the ARMSSE. + */ + sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->container); + + system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; +} + +static void armsse_idau_check(IDAUInterface *ii, uint32_t address, + int *iregion, bool *exempt, bool *ns, bool *nsc) +{ + /* + * For ARMSSE systems the IDAU responses are simple logical functions + * of the address bits. The NSC attribute is guest-adjustable via the + * NSCCFG register in the security controller. + */ + ARMSSE *s = ARMSSE(ii); + int region = extract32(address, 28, 4); + + *ns = !(region & 1); + *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); + /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ + *exempt = (address & 0xeff00000) == 0xe0000000; + *iregion = region; +} + +static const VMStateDescription armsse_vmstate = { + .name = "iotkit", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(nsccfg, ARMSSE), + VMSTATE_END_OF_LIST() + } +}; + +static Property armsse_properties[] = { + DEFINE_PROP_LINK("memory", ARMSSE, board_memory, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("EXP_NUMIRQ", ARMSSE, exp_numirq, 64), + DEFINE_PROP_UINT32("MAINCLK", ARMSSE, mainclk_frq, 0), + DEFINE_PROP_UINT32("SRAM_ADDR_WIDTH", ARMSSE, sram_addr_width, 15), + DEFINE_PROP_UINT32("init-svtor", ARMSSE, init_svtor, 0x10000000), + DEFINE_PROP_END_OF_LIST() +}; + +static void armsse_reset(DeviceState *dev) +{ + ARMSSE *s = ARMSSE(dev); + + s->nsccfg = 0; +} + +static void armsse_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); + ARMSSEClass *asc = ARMSSE_CLASS(klass); + + dc->realize = armsse_realize; + dc->vmsd = &armsse_vmstate; + dc->props = armsse_properties; + dc->reset = armsse_reset; + iic->check = armsse_idau_check; + asc->info = data; +} + +static const TypeInfo armsse_info = { + .name = TYPE_ARMSSE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSE), + .instance_init = armsse_init, + .abstract = true, + .interfaces = (InterfaceInfo[]) { + { TYPE_IDAU_INTERFACE }, + { } + } +}; + +static void armsse_register_types(void) +{ + int i; + + type_register_static(&armsse_info); + + for (i = 0; i < ARRAY_SIZE(armsse_variants); i++) { + TypeInfo ti = { + .name = armsse_variants[i].name, + .parent = TYPE_ARMSSE, + .class_init = armsse_class_init, + .class_data = (void *)&armsse_variants[i], + }; + type_register(&ti); + } +} + +type_init(armsse_register_types); diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 6b07666057..c4b2a9a1f5 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -158,7 +158,12 @@ static void armv7m_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - s->cpu = ARM_CPU(object_new(s->cpu_type)); + s->cpu = ARM_CPU(object_new_with_props(s->cpu_type, OBJECT(s), "cpu", + &err, NULL)); + if (err != NULL) { + error_propagate(errp, err); + return; + } object_property_set_link(OBJECT(s->cpu), OBJECT(&s->container), "memory", &error_abort); @@ -177,11 +182,21 @@ static void armv7m_realize(DeviceState *dev, Error **errp) return; } } + if (object_property_find(OBJECT(s->cpu), "start-powered-off", NULL)) { + object_property_set_bool(OBJECT(s->cpu), s->start_powered_off, + "start-powered-off", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + } - /* Tell the CPU where the NVIC is; it will fail realize if it doesn't - * have one. + /* + * Tell the CPU where the NVIC is; it will fail realize if it doesn't + * have one. Similarly, tell the NVIC where its CPU is. */ s->cpu->env.nvic = &s->nvic; + s->nvic.cpu = s->cpu; object_property_set_bool(OBJECT(s->cpu), true, "realized", &err); if (err != NULL) { @@ -202,6 +217,7 @@ static void armv7m_realize(DeviceState *dev, Error **errp) */ qdev_pass_gpios(DEVICE(&s->nvic), dev, NULL); qdev_pass_gpios(DEVICE(&s->nvic), dev, "SYSRESETREQ"); + qdev_pass_gpios(DEVICE(&s->nvic), dev, "NMI"); /* Wire the NVIC up to the CPU */ sbd = SYS_BUS_DEVICE(&s->nvic); @@ -211,25 +227,27 @@ static void armv7m_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->container, 0xe000e000, sysbus_mmio_get_region(sbd, 0)); - for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { - Object *obj = OBJECT(&s->bitband[i]); - SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]); + if (s->enable_bitband) { + for (i = 0; i < ARRAY_SIZE(s->bitband); i++) { + Object *obj = OBJECT(&s->bitband[i]); + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->bitband[i]); - object_property_set_int(obj, bitband_input_addr[i], "base", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } - object_property_set_link(obj, OBJECT(s->board_memory), - "source-memory", &error_abort); - object_property_set_bool(obj, true, "realized", &err); - if (err != NULL) { - error_propagate(errp, err); - return; - } + object_property_set_int(obj, bitband_input_addr[i], "base", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + object_property_set_link(obj, OBJECT(s->board_memory), + "source-memory", &error_abort); + object_property_set_bool(obj, true, "realized", &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } - memory_region_add_subregion(&s->container, bitband_output_addr[i], - sysbus_mmio_get_region(sbd, 0)); + memory_region_add_subregion(&s->container, bitband_output_addr[i], + sysbus_mmio_get_region(sbd, 0)); + } } } @@ -239,6 +257,9 @@ static Property armv7m_properties[] = { MemoryRegion *), DEFINE_PROP_LINK("idau", ARMv7MState, idau, TYPE_IDAU_INTERFACE, Object *), DEFINE_PROP_UINT32("init-svtor", ARMv7MState, init_svtor, 0), + DEFINE_PROP_BOOL("enable-bitband", ARMv7MState, enable_bitband, false), + DEFINE_PROP_BOOL("start-powered-off", ARMv7MState, start_powered_off, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -281,11 +302,6 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) big_endian = 0; #endif - if (!kernel_filename && !qtest_enabled()) { - error_report("Guest image must be specified (using -kernel)"); - exit(1); - } - if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { asidx = ARMASIdx_S; } else { @@ -294,7 +310,8 @@ void armv7m_load_kernel(ARMCPU *cpu, const char *kernel_filename, int mem_size) as = cpu_get_address_space(cs, asidx); if (kernel_filename) { - image_size = load_elf_as(kernel_filename, NULL, NULL, &entry, &lowaddr, + image_size = load_elf_as(kernel_filename, NULL, NULL, NULL, + &entry, &lowaddr, NULL, big_endian, EM_ARM, 1, 0, as); if (image_size < 0) { image_size = load_image_targphys_as(kernel_filename, 0, diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index bb9d33848d..996812498d 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -15,9 +15,10 @@ #include "cpu.h" #include "exec/address-spaces.h" #include "hw/arm/arm.h" +#include "hw/arm/aspeed.h" #include "hw/arm/aspeed_soc.h" #include "hw/boards.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "qemu/log.h" #include "sysemu/block-backend.h" #include "hw/loader.h" @@ -28,25 +29,10 @@ static struct arm_boot_info aspeed_board_binfo = { .nb_cpus = 1, }; -typedef struct AspeedBoardState { +struct AspeedBoardState { AspeedSoCState soc; MemoryRegion ram; -} AspeedBoardState; - -typedef struct AspeedBoardConfig { - const char *soc_name; - uint32_t hw_strap1; - const char *fmc_model; - const char *spi_model; - uint32_t num_cs; - void (*i2c_init)(AspeedBoardState *bmc); -} AspeedBoardConfig; - -enum { - PALMETTO_BMC, - AST2500_EVB, - ROMULUS_BMC, - WITHERSPOON_BMC, + MemoryRegion max_ram; }; /* Palmetto hardware value: 0x120CE416 */ @@ -87,44 +73,25 @@ enum { /* Witherspoon hardware value: 0xF10AD216 (but use romulus definition) */ #define WITHERSPOON_BMC_HW_STRAP1 ROMULUS_BMC_HW_STRAP1 -static void palmetto_bmc_i2c_init(AspeedBoardState *bmc); -static void ast2500_evb_i2c_init(AspeedBoardState *bmc); -static void romulus_bmc_i2c_init(AspeedBoardState *bmc); -static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc); +/* + * The max ram region is for firmwares that scan the address space + * with load/store to guess how much RAM the SoC has. + */ +static uint64_t max_ram_read(void *opaque, hwaddr offset, unsigned size) +{ + return 0; +} -static const AspeedBoardConfig aspeed_boards[] = { - [PALMETTO_BMC] = { - .soc_name = "ast2400-a1", - .hw_strap1 = PALMETTO_BMC_HW_STRAP1, - .fmc_model = "n25q256a", - .spi_model = "mx25l25635e", - .num_cs = 1, - .i2c_init = palmetto_bmc_i2c_init, - }, - [AST2500_EVB] = { - .soc_name = "ast2500-a1", - .hw_strap1 = AST2500_EVB_HW_STRAP1, - .fmc_model = "n25q256a", - .spi_model = "mx25l25635e", - .num_cs = 1, - .i2c_init = ast2500_evb_i2c_init, - }, - [ROMULUS_BMC] = { - .soc_name = "ast2500-a1", - .hw_strap1 = ROMULUS_BMC_HW_STRAP1, - .fmc_model = "n25q256a", - .spi_model = "mx66l1g45g", - .num_cs = 2, - .i2c_init = romulus_bmc_i2c_init, - }, - [WITHERSPOON_BMC] = { - .soc_name = "ast2500-a1", - .hw_strap1 = WITHERSPOON_BMC_HW_STRAP1, - .fmc_model = "mx25l25635e", - .spi_model = "mx66l1g45g", - .num_cs = 2, - .i2c_init = witherspoon_bmc_i2c_init, - }, +static void max_ram_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + /* Discard writes */ +} + +static const MemoryRegionOps max_ram_ops = { + .read = max_ram_read, + .write = max_ram_write, + .endianness = DEVICE_NATIVE_ENDIAN, }; #define FIRMWARE_ADDR 0x0 @@ -187,6 +154,7 @@ static void aspeed_board_init(MachineState *machine, AspeedBoardState *bmc; AspeedSoCClass *sc; DriveInfo *drive0 = drive_get(IF_MTD, 0, 0); + ram_addr_t max_ram_size; bmc = g_new0(AspeedBoardState, 1); object_initialize(&bmc->soc, (sizeof(bmc->soc)), cfg->soc_name); @@ -226,6 +194,14 @@ static void aspeed_board_init(MachineState *machine, object_property_add_const_link(OBJECT(&bmc->soc), "ram", OBJECT(&bmc->ram), &error_abort); + max_ram_size = object_property_get_uint(OBJECT(&bmc->soc), "max-ram-size", + &error_abort); + memory_region_init_io(&bmc->max_ram, NULL, &max_ram_ops, NULL, + "max_ram", max_ram_size - ram_size); + memory_region_add_subregion(get_system_memory(), + sc->info->sdram_base + ram_size, + &bmc->max_ram); + aspeed_board_init_flashes(&bmc->soc.fmc, cfg->fmc_model, &error_abort); aspeed_board_init_flashes(&bmc->soc.spi[0], cfg->spi_model, &error_abort); @@ -282,30 +258,6 @@ static void palmetto_bmc_i2c_init(AspeedBoardState *bmc) object_property_set_int(OBJECT(dev), 110000, "temperature3", &error_abort); } -static void palmetto_bmc_init(MachineState *machine) -{ - aspeed_board_init(machine, &aspeed_boards[PALMETTO_BMC]); -} - -static void palmetto_bmc_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "OpenPOWER Palmetto BMC (ARM926EJ-S)"; - mc->init = palmetto_bmc_init; - mc->max_cpus = 1; - mc->no_sdcard = 1; - mc->no_floppy = 1; - mc->no_cdrom = 1; - mc->no_parallel = 1; -} - -static const TypeInfo palmetto_bmc_type = { - .name = MACHINE_TYPE_NAME("palmetto-bmc"), - .parent = TYPE_MACHINE, - .class_init = palmetto_bmc_class_init, -}; - static void ast2500_evb_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; @@ -322,30 +274,6 @@ static void ast2500_evb_i2c_init(AspeedBoardState *bmc) i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); } -static void ast2500_evb_init(MachineState *machine) -{ - aspeed_board_init(machine, &aspeed_boards[AST2500_EVB]); -} - -static void ast2500_evb_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "Aspeed AST2500 EVB (ARM1176)"; - mc->init = ast2500_evb_init; - mc->max_cpus = 1; - mc->no_sdcard = 1; - mc->no_floppy = 1; - mc->no_cdrom = 1; - mc->no_parallel = 1; -} - -static const TypeInfo ast2500_evb_type = { - .name = MACHINE_TYPE_NAME("ast2500-evb"), - .parent = TYPE_MACHINE, - .class_init = ast2500_evb_class_init, -}; - static void romulus_bmc_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; @@ -355,30 +283,6 @@ static void romulus_bmc_i2c_init(AspeedBoardState *bmc) i2c_create_slave(aspeed_i2c_get_bus(DEVICE(&soc->i2c), 11), "ds1338", 0x32); } -static void romulus_bmc_init(MachineState *machine) -{ - aspeed_board_init(machine, &aspeed_boards[ROMULUS_BMC]); -} - -static void romulus_bmc_class_init(ObjectClass *oc, void *data) -{ - MachineClass *mc = MACHINE_CLASS(oc); - - mc->desc = "OpenPOWER Romulus BMC (ARM1176)"; - mc->init = romulus_bmc_init; - mc->max_cpus = 1; - mc->no_sdcard = 1; - mc->no_floppy = 1; - mc->no_cdrom = 1; - mc->no_parallel = 1; -} - -static const TypeInfo romulus_bmc_type = { - .name = MACHINE_TYPE_NAME("romulus-bmc"), - .parent = TYPE_MACHINE, - .class_init = romulus_bmc_class_init, -}; - static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) { AspeedSoCState *soc = &bmc->soc; @@ -402,36 +306,91 @@ static void witherspoon_bmc_i2c_init(AspeedBoardState *bmc) 0x60); } -static void witherspoon_bmc_init(MachineState *machine) +static void aspeed_machine_init(MachineState *machine) { - aspeed_board_init(machine, &aspeed_boards[WITHERSPOON_BMC]); + AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); + + aspeed_board_init(machine, amc->board); } -static void witherspoon_bmc_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + const AspeedBoardConfig *board = data; - mc->desc = "OpenPOWER Witherspoon BMC (ARM1176)"; - mc->init = witherspoon_bmc_init; + mc->desc = board->desc; + mc->init = aspeed_machine_init; mc->max_cpus = 1; mc->no_sdcard = 1; mc->no_floppy = 1; mc->no_cdrom = 1; mc->no_parallel = 1; + amc->board = board; } -static const TypeInfo witherspoon_bmc_type = { - .name = MACHINE_TYPE_NAME("witherspoon-bmc"), +static const TypeInfo aspeed_machine_type = { + .name = TYPE_ASPEED_MACHINE, .parent = TYPE_MACHINE, - .class_init = witherspoon_bmc_class_init, + .instance_size = sizeof(AspeedMachine), + .class_size = sizeof(AspeedMachineClass), + .abstract = true, }; -static void aspeed_machine_init(void) +static const AspeedBoardConfig aspeed_boards[] = { + { + .name = MACHINE_TYPE_NAME("palmetto-bmc"), + .desc = "OpenPOWER Palmetto BMC (ARM926EJ-S)", + .soc_name = "ast2400-a1", + .hw_strap1 = PALMETTO_BMC_HW_STRAP1, + .fmc_model = "n25q256a", + .spi_model = "mx25l25635e", + .num_cs = 1, + .i2c_init = palmetto_bmc_i2c_init, + }, { + .name = MACHINE_TYPE_NAME("ast2500-evb"), + .desc = "Aspeed AST2500 EVB (ARM1176)", + .soc_name = "ast2500-a1", + .hw_strap1 = AST2500_EVB_HW_STRAP1, + .fmc_model = "w25q256", + .spi_model = "mx25l25635e", + .num_cs = 1, + .i2c_init = ast2500_evb_i2c_init, + }, { + .name = MACHINE_TYPE_NAME("romulus-bmc"), + .desc = "OpenPOWER Romulus BMC (ARM1176)", + .soc_name = "ast2500-a1", + .hw_strap1 = ROMULUS_BMC_HW_STRAP1, + .fmc_model = "n25q256a", + .spi_model = "mx66l1g45g", + .num_cs = 2, + .i2c_init = romulus_bmc_i2c_init, + }, { + .name = MACHINE_TYPE_NAME("witherspoon-bmc"), + .desc = "OpenPOWER Witherspoon BMC (ARM1176)", + .soc_name = "ast2500-a1", + .hw_strap1 = WITHERSPOON_BMC_HW_STRAP1, + .fmc_model = "mx25l25635e", + .spi_model = "mx66l1g45g", + .num_cs = 2, + .i2c_init = witherspoon_bmc_i2c_init, + }, +}; + +static void aspeed_machine_types(void) { - type_register_static(&palmetto_bmc_type); - type_register_static(&ast2500_evb_type); - type_register_static(&romulus_bmc_type); - type_register_static(&witherspoon_bmc_type); + int i; + + type_register_static(&aspeed_machine_type); + for (i = 0; i < ARRAY_SIZE(aspeed_boards); ++i) { + TypeInfo ti = { + .name = aspeed_boards[i].name, + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_class_init, + .class_data = (void *)&aspeed_boards[i], + }; + type_register(&ti); + } } -type_init(aspeed_machine_init) +type_init(aspeed_machine_types) diff --git a/hw/arm/aspeed_soc.c b/hw/arm/aspeed_soc.c index e68911af0f..a27233d487 100644 --- a/hw/arm/aspeed_soc.c +++ b/hw/arm/aspeed_soc.c @@ -155,6 +155,8 @@ static void aspeed_soc_init(Object *obj) sc->info->silicon_rev); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), "ram-size", &error_abort); + object_property_add_alias(obj, "max-ram-size", OBJECT(&s->sdmc), + "max-ram-size", &error_abort); for (i = 0; i < sc->info->wdts_num; i++) { object_initialize(&s->wdt[i], sizeof(s->wdt[i]), TYPE_ASPEED_WDT); diff --git a/hw/arm/boot.c b/hw/arm/boot.c index e09201cc97..a830655e1a 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -24,18 +24,22 @@ #include "qemu/config-file.h" #include "qemu/option.h" #include "exec/address-spaces.h" +#include "qemu/units.h" /* Kernel boot protocol is specified in the kernel docs * Documentation/arm/Booting and Documentation/arm64/booting.txt * They have different preferred image load offsets from system RAM base. */ -#define KERNEL_ARGS_ADDR 0x100 -#define KERNEL_LOAD_ADDR 0x00010000 +#define KERNEL_ARGS_ADDR 0x100 +#define KERNEL_NOLOAD_ADDR 0x02000000 +#define KERNEL_LOAD_ADDR 0x00010000 #define KERNEL64_LOAD_ADDR 0x00080000 #define ARM64_TEXT_OFFSET_OFFSET 8 #define ARM64_MAGIC_OFFSET 56 +#define BOOTLOADER_MAX_SIZE (4 * KiB) + AddressSpace *arm_boot_address_space(ARMCPU *cpu, const struct arm_boot_info *info) { @@ -60,8 +64,10 @@ typedef enum { 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_ARGPTR_LO, /* overwrite with pointer to kernel args */ + FIXUP_ARGPTR_HI, /* overwrite with pointer to kernel args (high half) */ + FIXUP_ENTRYPOINT_LO, /* overwrite with kernel entry point */ + FIXUP_ENTRYPOINT_HI, /* overwrite with kernel entry point (high half) */ 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 */ @@ -80,10 +86,10 @@ static const ARMInsnFixup bootloader_aarch64[] = { { 0xaa1f03e3 }, /* mov x3, xzr */ { 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */ { 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */ - { 0, FIXUP_ARGPTR }, /* arg: .word @DTB Lower 32-bits */ - { 0 }, /* .word @DTB Higher 32-bits */ - { 0, FIXUP_ENTRYPOINT }, /* entry: .word @Kernel Entry Lower 32-bits */ - { 0 }, /* .word @Kernel Entry Higher 32-bits */ + { 0, FIXUP_ARGPTR_LO }, /* arg: .word @DTB Lower 32-bits */ + { 0, FIXUP_ARGPTR_HI}, /* .word @DTB Higher 32-bits */ + { 0, FIXUP_ENTRYPOINT_LO }, /* entry: .word @Kernel Entry Lower 32-bits */ + { 0, FIXUP_ENTRYPOINT_HI }, /* .word @Kernel Entry Higher 32-bits */ { 0, FIXUP_TERMINATOR } }; @@ -103,8 +109,8 @@ static const ARMInsnFixup bootloader[] = { { 0xe59f2004 }, /* ldr r2, [pc, #4] */ { 0xe59ff004 }, /* ldr pc, [pc, #4] */ { 0, FIXUP_BOARDID }, - { 0, FIXUP_ARGPTR }, - { 0, FIXUP_ENTRYPOINT }, + { 0, FIXUP_ARGPTR_LO }, + { 0, FIXUP_ENTRYPOINT_LO }, { 0, FIXUP_TERMINATOR } }; @@ -171,8 +177,10 @@ static void write_bootloader(const char *name, hwaddr addr, break; case FIXUP_BOARDID: case FIXUP_BOARD_SETUP: - case FIXUP_ARGPTR: - case FIXUP_ENTRYPOINT: + case FIXUP_ARGPTR_LO: + case FIXUP_ARGPTR_HI: + case FIXUP_ENTRYPOINT_LO: + case FIXUP_ENTRYPOINT_HI: case FIXUP_GIC_CPU_IF: case FIXUP_BOOTREG: case FIXUP_DSB: @@ -184,6 +192,8 @@ static void write_bootloader(const char *name, hwaddr addr, code[i] = tswap32(insn); } + assert((len * sizeof(uint32_t)) < BOOTLOADER_MAX_SIZE); + rom_add_blob_fixed_as(name, code, len * sizeof(uint32_t), addr, as); g_free(code); @@ -413,6 +423,32 @@ static void set_kernel_args_old(const struct arm_boot_info *info, } } +static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base, + uint32_t scells, hwaddr mem_len, + int numa_node_id) +{ + char *nodename; + int ret; + + nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); + ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, mem_base, + scells, mem_len); + if (ret < 0) { + goto out; + } + + /* only set the NUMA ID if it is specified */ + if (numa_node_id >= 0) { + ret = qemu_fdt_setprop_cell(fdt, nodename, + "numa-node-id", numa_node_id); + } +out: + g_free(nodename); + return ret; +} + static void fdt_add_psci_node(void *fdt) { uint32_t cpu_suspend_fn; @@ -492,7 +528,6 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, void *fdt = NULL; int size, rc, n = 0; uint32_t acells, scells; - char *nodename; unsigned int i; hwaddr mem_base, mem_len; char **node_path; @@ -566,35 +601,24 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, mem_base = binfo->loader_start; for (i = 0; i < nb_numa_nodes; i++) { mem_len = numa_info[i].node_mem; - nodename = g_strdup_printf("/memory@%" PRIx64, mem_base); - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", - acells, mem_base, - scells, mem_len); + rc = fdt_add_memory_node(fdt, acells, mem_base, + scells, mem_len, i); if (rc < 0) { - fprintf(stderr, "couldn't set %s/reg for node %d\n", nodename, - i); + fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n", + mem_base); goto fail; } - qemu_fdt_setprop_cell(fdt, nodename, "numa-node-id", i); mem_base += mem_len; - g_free(nodename); } } else { - nodename = g_strdup_printf("/memory@%" PRIx64, binfo->loader_start); - qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory"); - - rc = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", - acells, binfo->loader_start, - scells, binfo->ram_size); + rc = fdt_add_memory_node(fdt, acells, binfo->loader_start, + scells, binfo->ram_size, -1); if (rc < 0) { - fprintf(stderr, "couldn't set %s reg\n", nodename); + fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n", + binfo->loader_start); goto fail; } - g_free(nodename); } rc = fdt_path_offset(fdt, "/chosen"); @@ -687,10 +711,6 @@ static void do_cpu_reset(void *opaque) g_assert_not_reached(); } - if (!env->aarch64) { - env->thumb = info->entry & 1; - entry &= 0xfffffffe; - } cpu_set_pc(cs, entry); } else { /* If we are booting Linux then we need to check whether we are @@ -736,6 +756,17 @@ static void do_cpu_reset(void *opaque) } } + if (!env->aarch64 && !info->secure_boot && + arm_feature(env, ARM_FEATURE_EL2)) { + /* + * This is an AArch32 boot not to Secure state, and + * we have Hyp mode available, so boot the kernel into + * Hyp mode. This is not how the CPU comes out of reset, + * so we need to manually put it there. + */ + cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw); + } + if (cs == first_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); @@ -818,9 +849,9 @@ static int do_arm_linux_init(Object *obj, void *opaque) 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, AddressSpace *as) +static int64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, + uint64_t *lowaddr, uint64_t *highaddr, + int elf_machine, AddressSpace *as) { bool elf_is64; union { @@ -829,7 +860,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } elf_header; int data_swab = 0; bool big_endian; - uint64_t ret = -1; + int64_t ret = -1; Error *err = NULL; @@ -864,7 +895,7 @@ static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry, } } - ret = load_elf_as(info->kernel_filename, NULL, NULL, + ret = load_elf_as(info->kernel_filename, NULL, NULL, NULL, pentry, lowaddr, highaddr, big_endian, elf_machine, 1, data_swab, as); if (ret <= 0) { @@ -908,6 +939,19 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, memcpy(&hdrvals, buffer + ARM64_TEXT_OFFSET_OFFSET, sizeof(hdrvals)); if (hdrvals[1] != 0) { kernel_load_offset = le64_to_cpu(hdrvals[0]); + + /* + * We write our startup "bootloader" at the very bottom of RAM, + * so that bit can't be used for the image. Luckily the Image + * format specification is that the image requests only an offset + * from a 2MB boundary, not an absolute load address. So if the + * image requests an offset that might mean it overlaps with the + * bootloader, we can just load it starting at 2MB+offset rather + * than 0MB + offset. + */ + if (kernel_load_offset < BOOTLOADER_MAX_SIZE) { + kernel_load_offset += 2 * MiB; + } } } @@ -919,9 +963,12 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base, return size; } -void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) +static void arm_setup_direct_kernel_boot(ARMCPU *cpu, + struct arm_boot_info *info) { + /* Set up for a direct boot of a kernel image file. */ CPUState *cs; + AddressSpace *as = arm_boot_address_space(cpu, info); int kernel_size; int initrd_size; int is_linux = 0; @@ -929,70 +976,6 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) int elf_machine; hwaddr entry; static const ARMInsnFixup *primary_loader; - AddressSpace *as = arm_boot_address_space(cpu, info); - - /* CPU objects (unlike devices) are not automatically reset on system - * reset, so we must always register a handler to do so. If we're - * actually loading a kernel, the handler is also responsible for - * arranging that we start it correctly. - */ - for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { - qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); - } - - /* 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())); - - info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); - info->dtb_limit = 0; - - /* Load the kernel. */ - if (!info->kernel_filename || info->firmware_loaded) { - - if (have_dtb(info)) { - /* If we have a device tree blob, but no kernel to supply it to (or - * the kernel is supposed to be loaded by the bootloader), copy the - * DTB to the base of RAM for the bootloader to pick up. - */ - info->dtb_start = info->loader_start; - } - - if (info->kernel_filename) { - FWCfgState *fw_cfg; - bool try_decompressing_kernel; - - fw_cfg = fw_cfg_find(); - try_decompressing_kernel = arm_feature(&cpu->env, - ARM_FEATURE_AARCH64); - - /* Expose the kernel, the command line, and the initrd in fw_cfg. - * We don't process them here at all, it's all left to the - * firmware. - */ - load_image_to_fw_cfg(fw_cfg, - FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, - info->kernel_filename, - try_decompressing_kernel); - load_image_to_fw_cfg(fw_cfg, - FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, - info->initrd_filename, false); - - if (info->kernel_cmdline) { - fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, - strlen(info->kernel_cmdline) + 1); - fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, - info->kernel_cmdline); - } - } - - /* We will start from address 0 (typically a boot ROM image) in the - * same way as hardware. - */ - return; - } if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { primary_loader = bootloader_aarch64; @@ -1015,7 +998,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) if (info->nb_cpus == 0) info->nb_cpus = 1; - /* We want to put the initrd far enough into RAM that when the + /* + * 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 * enough room for a decent sized initrd, and on boards with large @@ -1032,12 +1016,14 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr, &elf_high_addr, elf_machine, as); if (kernel_size > 0 && have_dtb(info)) { - /* If there is still some room left at the base of RAM, try and put + /* + * 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. */ if (elf_low_addr > info->loader_start || elf_high_addr < info->loader_start) { - /* Set elf_low_addr as address limit for arm_load_dtb if it may be + /* + * Set elf_low_addr as address limit for arm_load_dtb if it may be * pointing into RAM, otherwise pass '0' (no limit) */ if (elf_low_addr < info->loader_start) { @@ -1049,7 +1035,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) } entry = elf_entry; if (kernel_size < 0) { - kernel_size = load_uimage_as(info->kernel_filename, &entry, NULL, + uint64_t loadaddr = info->loader_start + KERNEL_NOLOAD_ADDR; + kernel_size = load_uimage_as(info->kernel_filename, &entry, &loadaddr, &is_linux, NULL, NULL, as); } if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) { @@ -1097,7 +1084,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) 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 + /* + * for device tree boot, we pass the DTB directly in r2. Otherwise * we point to the kernel args. */ if (have_dtb(info)) { @@ -1123,9 +1111,13 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) /* Place the DTB after the initrd in memory with alignment. */ info->dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align); - fixupcontext[FIXUP_ARGPTR] = info->dtb_start; + fixupcontext[FIXUP_ARGPTR_LO] = info->dtb_start; + fixupcontext[FIXUP_ARGPTR_HI] = info->dtb_start >> 32; } else { - fixupcontext[FIXUP_ARGPTR] = info->loader_start + KERNEL_ARGS_ADDR; + fixupcontext[FIXUP_ARGPTR_LO] = + info->loader_start + KERNEL_ARGS_ADDR; + fixupcontext[FIXUP_ARGPTR_HI] = + (info->loader_start + KERNEL_ARGS_ADDR) >> 32; if (info->ram_size >= (1ULL << 32)) { error_report("RAM size must be less than 4GB to boot" " Linux kernel using ATAGS (try passing a device tree" @@ -1133,7 +1125,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) exit(1); } } - fixupcontext[FIXUP_ENTRYPOINT] = entry; + fixupcontext[FIXUP_ENTRYPOINT_LO] = entry; + fixupcontext[FIXUP_ENTRYPOINT_HI] = entry >> 32; write_bootloader("bootloader", info->loader_start, primary_loader, fixupcontext, as); @@ -1145,7 +1138,8 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) info->write_board_setup(cpu, info); } - /* Notify devices which need to fake up firmware initialization + /* + * Notify devices which need to fake up firmware initialization * that we're doing a direct kernel boot. */ object_child_foreach_recursive(object_get_root(), @@ -1156,6 +1150,88 @@ void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { ARM_CPU(cs)->env.boot_info = info; } +} + +static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info) +{ + /* Set up for booting firmware (which might load a kernel via fw_cfg) */ + + if (have_dtb(info)) { + /* + * If we have a device tree blob, but no kernel to supply it to (or + * the kernel is supposed to be loaded by the bootloader), copy the + * DTB to the base of RAM for the bootloader to pick up. + */ + info->dtb_start = info->loader_start; + } + + if (info->kernel_filename) { + FWCfgState *fw_cfg; + bool try_decompressing_kernel; + + fw_cfg = fw_cfg_find(); + try_decompressing_kernel = arm_feature(&cpu->env, + ARM_FEATURE_AARCH64); + + /* + * Expose the kernel, the command line, and the initrd in fw_cfg. + * We don't process them here at all, it's all left to the + * firmware. + */ + load_image_to_fw_cfg(fw_cfg, + FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA, + info->kernel_filename, + try_decompressing_kernel); + load_image_to_fw_cfg(fw_cfg, + FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA, + info->initrd_filename, false); + + if (info->kernel_cmdline) { + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(info->kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, + info->kernel_cmdline); + } + } + + /* + * We will start from address 0 (typically a boot ROM image) in the + * same way as hardware. Leave env->boot_info NULL, so that + * do_cpu_reset() knows it does not need to alter the PC on reset. + */ +} + +void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info) +{ + CPUState *cs; + AddressSpace *as = arm_boot_address_space(cpu, info); + + /* + * CPU objects (unlike devices) are not automatically reset on system + * reset, so we must always register a handler to do so. If we're + * actually loading a kernel, the handler is also responsible for + * arranging that we start it correctly. + */ + for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) { + qemu_register_reset(do_cpu_reset, ARM_CPU(cs)); + } + + /* + * 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())); + + info->dtb_filename = qemu_opt_get(qemu_get_machine_opts(), "dtb"); + info->dtb_limit = 0; + + /* Load the kernel. */ + if (!info->kernel_filename || info->firmware_loaded) { + arm_setup_firmware_boot(cpu, info); + } else { + arm_setup_direct_kernel_boot(cpu, info); + } if (!info->skip_dtb_autoload && have_dtb(info)) { if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as) < 0) { diff --git a/hw/arm/collie.c b/hw/arm/collie.c index 48b732c176..d12604c573 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -9,10 +9,10 @@ * GNU GPL, version 2 or (at your option) any later version. */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "hw/hw.h" #include "hw/sysbus.h" #include "hw/boards.h" -#include "hw/devices.h" #include "strongarm.h" #include "hw/arm/arm.h" #include "hw/block/flash.h" @@ -36,14 +36,14 @@ static void collie_init(MachineState *machine) s = sa1110_init(sysmem, collie_binfo.ram_size, machine->cpu_type); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(SA_CS0, NULL, "collie.fl1", 0x02000000, + pflash_cfi01_register(SA_CS0, "collie.fl1", 0x02000000, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); + 64 * KiB, 4, 0x00, 0x00, 0x00, 0x00, 0); dinfo = drive_get(IF_PFLASH, 0, 1); - pflash_cfi01_register(SA_CS1, NULL, "collie.fl2", 0x02000000, + pflash_cfi01_register(SA_CS1, "collie.fl2", 0x02000000, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - (64 * 1024), 512, 4, 0x00, 0x00, 0x00, 0x00, 0); + 64 * KiB, 4, 0x00, 0x00, 0x00, 0x00, 0); sysbus_create_simple("scoop", 0x40800000, NULL); diff --git a/hw/arm/cubieboard.c b/hw/arm/cubieboard.c index 32f1edd2fa..84187d3916 100644 --- a/hw/arm/cubieboard.c +++ b/hw/arm/cubieboard.c @@ -20,7 +20,6 @@ #include "qemu-common.h" #include "cpu.h" #include "hw/sysbus.h" -#include "hw/devices.h" #include "hw/boards.h" #include "hw/arm/allwinner-a10.h" diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 9f11dcd11f..304e4d1a29 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -129,9 +129,8 @@ static void digic4_add_k8p3215uqb_rom(DigicBoardState *s, hwaddr addr, #define FLASH_K8P3215UQB_SIZE (4 * 1024 * 1024) #define FLASH_K8P3215UQB_SECTOR_SIZE (64 * 1024) - pflash_cfi02_register(addr, NULL, "pflash", FLASH_K8P3215UQB_SIZE, + pflash_cfi02_register(addr, "pflash", FLASH_K8P3215UQB_SIZE, NULL, FLASH_K8P3215UQB_SECTOR_SIZE, - FLASH_K8P3215UQB_SIZE / FLASH_K8P3215UQB_SECTOR_SIZE, DIGIC4_ROM_MAX_SIZE / FLASH_K8P3215UQB_SIZE, 4, 0x00EC, 0x007E, 0x0003, 0x0001, diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index b7463a71ec..af82e95542 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -162,7 +162,7 @@ static uint64_t exynos4210_calc_affinity(int cpu) Exynos4210State *exynos4210_init(MemoryRegion *system_mem) { - Exynos4210State *s = g_new(Exynos4210State, 1); + Exynos4210State *s = g_new0(Exynos4210State, 1); qemu_irq gate_irq[EXYNOS4210_NCPUS][EXYNOS4210_IRQ_GATE_NINPUTS]; SysBusDevice *busdev; DeviceState *dev; @@ -352,19 +352,19 @@ Exynos4210State *exynos4210_init(MemoryRegion *system_mem) /*** UARTs ***/ exynos4210_uart_create(EXYNOS4210_UART0_BASE_ADDR, - EXYNOS4210_UART0_FIFO_SIZE, 0, NULL, + EXYNOS4210_UART0_FIFO_SIZE, 0, serial_hd(0), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 0)]); exynos4210_uart_create(EXYNOS4210_UART1_BASE_ADDR, - EXYNOS4210_UART1_FIFO_SIZE, 1, NULL, + EXYNOS4210_UART1_FIFO_SIZE, 1, serial_hd(1), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 1)]); exynos4210_uart_create(EXYNOS4210_UART2_BASE_ADDR, - EXYNOS4210_UART2_FIFO_SIZE, 2, NULL, + EXYNOS4210_UART2_FIFO_SIZE, 2, serial_hd(2), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 2)]); exynos4210_uart_create(EXYNOS4210_UART3_BASE_ADDR, - EXYNOS4210_UART3_FIFO_SIZE, 3, NULL, + EXYNOS4210_UART3_FIFO_SIZE, 3, serial_hd(3), s->irq_table[exynos4210_get_irq(EXYNOS4210_UART_INT_GRP, 3)]); /*** SD/MMC host controllers ***/ diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c new file mode 100644 index 0000000000..4b56bfa8d1 --- /dev/null +++ b/hw/arm/fsl-imx6ul.c @@ -0,0 +1,621 @@ +/* + * Copyright (c) 2018 Jean-Christophe Dubois + * + * i.MX6UL SOC emulation. + * + * Based on hw/arm/fsl-imx7.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. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx6ul.h" +#include "hw/misc/unimp.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" + +#define NAME_SIZE 20 + +static void fsl_imx6ul_init(Object *obj) +{ + FslIMX6ULState *s = FSL_IMX6UL(obj); + char name[NAME_SIZE]; + int i; + + for (i = 0; i < MIN(smp_cpus, FSL_IMX6UL_NUM_CPUS); i++) { + snprintf(name, NAME_SIZE, "cpu%d", i); + object_initialize_child(obj, name, &s->cpu[i], sizeof(s->cpu[i]), + "cortex-a7-" TYPE_ARM_CPU, &error_abort, NULL); + } + + /* + * A7MPCORE + */ + sysbus_init_child_obj(obj, "a7mpcore", &s->a7mpcore, sizeof(s->a7mpcore), + TYPE_A15MPCORE_PRIV); + + /* + * CCM + */ + sysbus_init_child_obj(obj, "ccm", &s->ccm, sizeof(s->ccm), TYPE_IMX6UL_CCM); + + /* + * SRC + */ + sysbus_init_child_obj(obj, "src", &s->src, sizeof(s->src), TYPE_IMX6_SRC); + + /* + * GPCv2 + */ + sysbus_init_child_obj(obj, "gpcv2", &s->gpcv2, sizeof(s->gpcv2), + TYPE_IMX_GPCV2); + + /* + * SNVS + */ + sysbus_init_child_obj(obj, "snvs", &s->snvs, sizeof(s->snvs), + TYPE_IMX7_SNVS); + + /* + * GPR + */ + sysbus_init_child_obj(obj, "gpr", &s->gpr, sizeof(s->gpr), + TYPE_IMX7_GPR); + + /* + * GPIOs 1 to 5 + */ + for (i = 0; i < FSL_IMX6UL_NUM_GPIOS; i++) { + snprintf(name, NAME_SIZE, "gpio%d", i); + sysbus_init_child_obj(obj, name, &s->gpio[i], sizeof(s->gpio[i]), + TYPE_IMX_GPIO); + } + + /* + * GPT 1, 2 + */ + for (i = 0; i < FSL_IMX6UL_NUM_GPTS; i++) { + snprintf(name, NAME_SIZE, "gpt%d", i); + sysbus_init_child_obj(obj, name, &s->gpt[i], sizeof(s->gpt[i]), + TYPE_IMX7_GPT); + } + + /* + * EPIT 1, 2 + */ + for (i = 0; i < FSL_IMX6UL_NUM_EPITS; i++) { + snprintf(name, NAME_SIZE, "epit%d", i + 1); + sysbus_init_child_obj(obj, name, &s->epit[i], sizeof(s->epit[i]), + TYPE_IMX_EPIT); + } + + /* + * eCSPI + */ + for (i = 0; i < FSL_IMX6UL_NUM_ECSPIS; i++) { + snprintf(name, NAME_SIZE, "spi%d", i + 1); + sysbus_init_child_obj(obj, name, &s->spi[i], sizeof(s->spi[i]), + TYPE_IMX_SPI); + } + + /* + * I2C + */ + for (i = 0; i < FSL_IMX6UL_NUM_I2CS; i++) { + snprintf(name, NAME_SIZE, "i2c%d", i + 1); + sysbus_init_child_obj(obj, name, &s->i2c[i], sizeof(s->i2c[i]), + TYPE_IMX_I2C); + } + + /* + * UART + */ + for (i = 0; i < FSL_IMX6UL_NUM_UARTS; i++) { + snprintf(name, NAME_SIZE, "uart%d", i); + sysbus_init_child_obj(obj, name, &s->uart[i], sizeof(s->uart[i]), + TYPE_IMX_SERIAL); + } + + /* + * Ethernet + */ + for (i = 0; i < FSL_IMX6UL_NUM_ETHS; i++) { + snprintf(name, NAME_SIZE, "eth%d", i); + sysbus_init_child_obj(obj, name, &s->eth[i], sizeof(s->eth[i]), + TYPE_IMX_ENET); + } + + /* + * SDHCI + */ + for (i = 0; i < FSL_IMX6UL_NUM_USDHCS; i++) { + snprintf(name, NAME_SIZE, "usdhc%d", i); + sysbus_init_child_obj(obj, name, &s->usdhc[i], sizeof(s->usdhc[i]), + TYPE_IMX_USDHC); + } + + /* + * Watchdog + */ + for (i = 0; i < FSL_IMX6UL_NUM_WDTS; i++) { + snprintf(name, NAME_SIZE, "wdt%d", i); + sysbus_init_child_obj(obj, name, &s->wdt[i], sizeof(s->wdt[i]), + TYPE_IMX2_WDT); + } +} + +static void fsl_imx6ul_realize(DeviceState *dev, Error **errp) +{ + FslIMX6ULState *s = FSL_IMX6UL(dev); + int i; + qemu_irq irq; + char name[NAME_SIZE]; + + if (smp_cpus > FSL_IMX6UL_NUM_CPUS) { + error_setg(errp, "%s: Only %d CPUs are supported (%d requested)", + TYPE_FSL_IMX6UL, FSL_IMX6UL_NUM_CPUS, smp_cpus); + return; + } + + for (i = 0; i < smp_cpus; i++) { + Object *o = OBJECT(&s->cpu[i]); + + object_property_set_int(o, QEMU_PSCI_CONDUIT_SMC, + "psci-conduit", &error_abort); + + /* On uniprocessor, the CBAR is set to 0 */ + if (smp_cpus > 1) { + object_property_set_int(o, FSL_IMX6UL_A7MPCORE_ADDR, + "reset-cbar", &error_abort); + } + + if (i) { + /* Secondary CPUs start in PSCI powered-down state */ + object_property_set_bool(o, true, + "start-powered-off", &error_abort); + } + + object_property_set_bool(o, true, "realized", &error_abort); + } + + /* + * A7MPCORE + */ + object_property_set_int(OBJECT(&s->a7mpcore), smp_cpus, "num-cpu", + &error_abort); + object_property_set_int(OBJECT(&s->a7mpcore), + FSL_IMX6UL_MAX_IRQ + GIC_INTERNAL, + "num-irq", &error_abort); + object_property_set_bool(OBJECT(&s->a7mpcore), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->a7mpcore), 0, FSL_IMX6UL_A7MPCORE_ADDR); + + for (i = 0; i < smp_cpus; i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->a7mpcore); + DeviceState *d = DEVICE(qemu_get_cpu(i)); + + irq = qdev_get_gpio_in(d, ARM_CPU_IRQ); + sysbus_connect_irq(sbd, i, irq); + sysbus_connect_irq(sbd, i + smp_cpus, qdev_get_gpio_in(d, ARM_CPU_FIQ)); + sysbus_connect_irq(sbd, i + 2 * smp_cpus, + qdev_get_gpio_in(d, ARM_CPU_VIRQ)); + sysbus_connect_irq(sbd, i + 3 * smp_cpus, + qdev_get_gpio_in(d, ARM_CPU_VFIQ)); + } + + /* + * A7MPCORE DAP + */ + create_unimplemented_device("a7mpcore-dap", FSL_IMX6UL_A7MPCORE_DAP_ADDR, + 0x100000); + + /* + * GPT 1, 2 + */ + for (i = 0; i < FSL_IMX6UL_NUM_GPTS; i++) { + static const hwaddr FSL_IMX6UL_GPTn_ADDR[FSL_IMX6UL_NUM_GPTS] = { + FSL_IMX6UL_GPT1_ADDR, + FSL_IMX6UL_GPT2_ADDR, + }; + + static const int FSL_IMX6UL_GPTn_IRQ[FSL_IMX6UL_NUM_GPTS] = { + FSL_IMX6UL_GPT1_IRQ, + FSL_IMX6UL_GPT2_IRQ, + }; + + s->gpt[i].ccm = IMX_CCM(&s->ccm); + object_property_set_bool(OBJECT(&s->gpt[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpt[i]), 0, + FSL_IMX6UL_GPTn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpt[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_GPTn_IRQ[i])); + } + + /* + * EPIT 1, 2 + */ + for (i = 0; i < FSL_IMX6UL_NUM_EPITS; i++) { + static const hwaddr FSL_IMX6UL_EPITn_ADDR[FSL_IMX6UL_NUM_EPITS] = { + FSL_IMX6UL_EPIT1_ADDR, + FSL_IMX6UL_EPIT2_ADDR, + }; + + static const int FSL_IMX6UL_EPITn_IRQ[FSL_IMX6UL_NUM_EPITS] = { + FSL_IMX6UL_EPIT1_IRQ, + FSL_IMX6UL_EPIT2_IRQ, + }; + + s->epit[i].ccm = IMX_CCM(&s->ccm); + object_property_set_bool(OBJECT(&s->epit[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->epit[i]), 0, + FSL_IMX6UL_EPITn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->epit[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_EPITn_IRQ[i])); + } + + /* + * GPIO + */ + for (i = 0; i < FSL_IMX6UL_NUM_GPIOS; i++) { + static const hwaddr FSL_IMX6UL_GPIOn_ADDR[FSL_IMX6UL_NUM_GPIOS] = { + FSL_IMX6UL_GPIO1_ADDR, + FSL_IMX6UL_GPIO2_ADDR, + FSL_IMX6UL_GPIO3_ADDR, + FSL_IMX6UL_GPIO4_ADDR, + FSL_IMX6UL_GPIO5_ADDR, + }; + + static const int FSL_IMX6UL_GPIOn_LOW_IRQ[FSL_IMX6UL_NUM_GPIOS] = { + FSL_IMX6UL_GPIO1_LOW_IRQ, + FSL_IMX6UL_GPIO2_LOW_IRQ, + FSL_IMX6UL_GPIO3_LOW_IRQ, + FSL_IMX6UL_GPIO4_LOW_IRQ, + FSL_IMX6UL_GPIO5_LOW_IRQ, + }; + + static const int FSL_IMX6UL_GPIOn_HIGH_IRQ[FSL_IMX6UL_NUM_GPIOS] = { + FSL_IMX6UL_GPIO1_HIGH_IRQ, + FSL_IMX6UL_GPIO2_HIGH_IRQ, + FSL_IMX6UL_GPIO3_HIGH_IRQ, + FSL_IMX6UL_GPIO4_HIGH_IRQ, + FSL_IMX6UL_GPIO5_HIGH_IRQ, + }; + + object_property_set_bool(OBJECT(&s->gpio[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpio[i]), 0, + FSL_IMX6UL_GPIOn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_GPIOn_LOW_IRQ[i])); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gpio[i]), 1, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_GPIOn_HIGH_IRQ[i])); + } + + /* + * IOMUXC and IOMUXC_GPR + */ + for (i = 0; i < 1; i++) { + static const hwaddr FSL_IMX6UL_IOMUXCn_ADDR[FSL_IMX6UL_NUM_IOMUXCS] = { + FSL_IMX6UL_IOMUXC_ADDR, + FSL_IMX6UL_IOMUXC_GPR_ADDR, + }; + + snprintf(name, NAME_SIZE, "iomuxc%d", i); + create_unimplemented_device(name, FSL_IMX6UL_IOMUXCn_ADDR[i], 0x4000); + } + + /* + * CCM + */ + object_property_set_bool(OBJECT(&s->ccm), true, "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->ccm), 0, FSL_IMX6UL_CCM_ADDR); + + /* + * SRC + */ + object_property_set_bool(OBJECT(&s->src), true, "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->src), 0, FSL_IMX6UL_SRC_ADDR); + + /* + * GPCv2 + */ + object_property_set_bool(OBJECT(&s->gpcv2), true, + "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpcv2), 0, FSL_IMX6UL_GPC_ADDR); + + /* Initialize all ECSPI */ + for (i = 0; i < FSL_IMX6UL_NUM_ECSPIS; i++) { + static const hwaddr FSL_IMX6UL_SPIn_ADDR[FSL_IMX6UL_NUM_ECSPIS] = { + FSL_IMX6UL_ECSPI1_ADDR, + FSL_IMX6UL_ECSPI2_ADDR, + FSL_IMX6UL_ECSPI3_ADDR, + FSL_IMX6UL_ECSPI4_ADDR, + }; + + static const int FSL_IMX6UL_SPIn_IRQ[FSL_IMX6UL_NUM_ECSPIS] = { + FSL_IMX6UL_ECSPI1_IRQ, + FSL_IMX6UL_ECSPI2_IRQ, + FSL_IMX6UL_ECSPI3_IRQ, + FSL_IMX6UL_ECSPI4_IRQ, + }; + + /* Initialize the SPI */ + object_property_set_bool(OBJECT(&s->spi[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->spi[i]), 0, + FSL_IMX6UL_SPIn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->spi[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_SPIn_IRQ[i])); + } + + /* + * I2C + */ + for (i = 0; i < FSL_IMX6UL_NUM_I2CS; i++) { + static const hwaddr FSL_IMX6UL_I2Cn_ADDR[FSL_IMX6UL_NUM_I2CS] = { + FSL_IMX6UL_I2C1_ADDR, + FSL_IMX6UL_I2C2_ADDR, + FSL_IMX6UL_I2C3_ADDR, + FSL_IMX6UL_I2C4_ADDR, + }; + + static const int FSL_IMX6UL_I2Cn_IRQ[FSL_IMX6UL_NUM_I2CS] = { + FSL_IMX6UL_I2C1_IRQ, + FSL_IMX6UL_I2C2_IRQ, + FSL_IMX6UL_I2C3_IRQ, + FSL_IMX6UL_I2C4_IRQ, + }; + + object_property_set_bool(OBJECT(&s->i2c[i]), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->i2c[i]), 0, FSL_IMX6UL_I2Cn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->i2c[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_I2Cn_IRQ[i])); + } + + /* + * UART + */ + for (i = 0; i < FSL_IMX6UL_NUM_UARTS; i++) { + static const hwaddr FSL_IMX6UL_UARTn_ADDR[FSL_IMX6UL_NUM_UARTS] = { + FSL_IMX6UL_UART1_ADDR, + FSL_IMX6UL_UART2_ADDR, + FSL_IMX6UL_UART3_ADDR, + FSL_IMX6UL_UART4_ADDR, + FSL_IMX6UL_UART5_ADDR, + FSL_IMX6UL_UART6_ADDR, + FSL_IMX6UL_UART7_ADDR, + FSL_IMX6UL_UART8_ADDR, + }; + + static const int FSL_IMX6UL_UARTn_IRQ[FSL_IMX6UL_NUM_UARTS] = { + FSL_IMX6UL_UART1_IRQ, + FSL_IMX6UL_UART2_IRQ, + FSL_IMX6UL_UART3_IRQ, + FSL_IMX6UL_UART4_IRQ, + FSL_IMX6UL_UART5_IRQ, + FSL_IMX6UL_UART6_IRQ, + FSL_IMX6UL_UART7_IRQ, + FSL_IMX6UL_UART8_IRQ, + }; + + qdev_prop_set_chr(DEVICE(&s->uart[i]), "chardev", serial_hd(i)); + + object_property_set_bool(OBJECT(&s->uart[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->uart[i]), 0, + FSL_IMX6UL_UARTn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_UARTn_IRQ[i])); + } + + /* + * Ethernet + */ + for (i = 0; i < FSL_IMX6UL_NUM_ETHS; i++) { + static const hwaddr FSL_IMX6UL_ENETn_ADDR[FSL_IMX6UL_NUM_ETHS] = { + FSL_IMX6UL_ENET1_ADDR, + FSL_IMX6UL_ENET2_ADDR, + }; + + static const int FSL_IMX6UL_ENETn_IRQ[FSL_IMX6UL_NUM_ETHS] = { + FSL_IMX6UL_ENET1_IRQ, + FSL_IMX6UL_ENET2_IRQ, + }; + + static const int FSL_IMX6UL_ENETn_TIMER_IRQ[FSL_IMX6UL_NUM_ETHS] = { + FSL_IMX6UL_ENET1_TIMER_IRQ, + FSL_IMX6UL_ENET2_TIMER_IRQ, + }; + + object_property_set_uint(OBJECT(&s->eth[i]), + FSL_IMX6UL_ETH_NUM_TX_RINGS, + "tx-ring-num", &error_abort); + qdev_set_nic_properties(DEVICE(&s->eth[i]), &nd_table[i]); + object_property_set_bool(OBJECT(&s->eth[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->eth[i]), 0, + FSL_IMX6UL_ENETn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_ENETn_IRQ[i])); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->eth[i]), 1, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_ENETn_TIMER_IRQ[i])); + } + + /* + * USDHC + */ + for (i = 0; i < FSL_IMX6UL_NUM_USDHCS; i++) { + static const hwaddr FSL_IMX6UL_USDHCn_ADDR[FSL_IMX6UL_NUM_USDHCS] = { + FSL_IMX6UL_USDHC1_ADDR, + FSL_IMX6UL_USDHC2_ADDR, + }; + + static const int FSL_IMX6UL_USDHCn_IRQ[FSL_IMX6UL_NUM_USDHCS] = { + FSL_IMX6UL_USDHC1_IRQ, + FSL_IMX6UL_USDHC2_IRQ, + }; + + object_property_set_bool(OBJECT(&s->usdhc[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + FSL_IMX6UL_USDHCn_ADDR[i]); + + sysbus_connect_irq(SYS_BUS_DEVICE(&s->usdhc[i]), 0, + qdev_get_gpio_in(DEVICE(&s->a7mpcore), + FSL_IMX6UL_USDHCn_IRQ[i])); + } + + /* + * SNVS + */ + object_property_set_bool(OBJECT(&s->snvs), true, "realized", &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->snvs), 0, FSL_IMX6UL_SNVS_HP_ADDR); + + /* + * Watchdog + */ + for (i = 0; i < FSL_IMX6UL_NUM_WDTS; i++) { + static const hwaddr FSL_IMX6UL_WDOGn_ADDR[FSL_IMX6UL_NUM_WDTS] = { + FSL_IMX6UL_WDOG1_ADDR, + FSL_IMX6UL_WDOG2_ADDR, + FSL_IMX6UL_WDOG3_ADDR, + }; + + object_property_set_bool(OBJECT(&s->wdt[i]), true, "realized", + &error_abort); + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->wdt[i]), 0, + FSL_IMX6UL_WDOGn_ADDR[i]); + } + + /* + * GPR + */ + object_property_set_bool(OBJECT(&s->gpr), true, "realized", + &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->gpr), 0, FSL_IMX6UL_IOMUXC_GPR_ADDR); + + /* + * SDMA + */ + create_unimplemented_device("sdma", FSL_IMX6UL_SDMA_ADDR, 0x4000); + + /* + * APHB_DMA + */ + create_unimplemented_device("aphb_dma", FSL_IMX6UL_APBH_DMA_ADDR, + FSL_IMX6UL_APBH_DMA_SIZE); + + /* + * ADCs + */ + for (i = 0; i < FSL_IMX6UL_NUM_ADCS; i++) { + static const hwaddr FSL_IMX6UL_ADCn_ADDR[FSL_IMX6UL_NUM_ADCS] = { + FSL_IMX6UL_ADC1_ADDR, + FSL_IMX6UL_ADC2_ADDR, + }; + + snprintf(name, NAME_SIZE, "adc%d", i); + create_unimplemented_device(name, FSL_IMX6UL_ADCn_ADDR[i], 0x4000); + } + + /* + * LCD + */ + create_unimplemented_device("lcdif", FSL_IMX6UL_LCDIF_ADDR, 0x4000); + + /* + * ROM memory + */ + memory_region_init_rom(&s->rom, NULL, "imx6ul.rom", + FSL_IMX6UL_ROM_SIZE, &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_ROM_ADDR, + &s->rom); + + /* + * CAAM memory + */ + memory_region_init_rom(&s->caam, NULL, "imx6ul.caam", + FSL_IMX6UL_CAAM_MEM_SIZE, &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_CAAM_MEM_ADDR, + &s->caam); + + /* + * OCRAM memory + */ + memory_region_init_ram(&s->ocram, NULL, "imx6ul.ocram", + FSL_IMX6UL_OCRAM_MEM_SIZE, + &error_abort); + memory_region_add_subregion(get_system_memory(), FSL_IMX6UL_OCRAM_MEM_ADDR, + &s->ocram); + + /* + * internal OCRAM (128 KB) is aliased over 512 KB + */ + memory_region_init_alias(&s->ocram_alias, NULL, "imx6ul.ocram_alias", + &s->ocram, 0, FSL_IMX6UL_OCRAM_ALIAS_SIZE); + memory_region_add_subregion(get_system_memory(), + FSL_IMX6UL_OCRAM_ALIAS_ADDR, &s->ocram_alias); +} + +static void fsl_imx6ul_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = fsl_imx6ul_realize; + dc->desc = "i.MX6UL SOC"; + /* Reason: Uses serial_hds and nd_table in realize() directly */ + dc->user_creatable = false; +} + +static const TypeInfo fsl_imx6ul_type_info = { + .name = TYPE_FSL_IMX6UL, + .parent = TYPE_DEVICE, + .instance_size = sizeof(FslIMX6ULState), + .instance_init = fsl_imx6ul_init, + .class_init = fsl_imx6ul_class_init, +}; + +static void fsl_imx6ul_register_types(void) +{ + type_register_static(&fsl_imx6ul_type_info); +} +type_init(fsl_imx6ul_register_types) diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index d5e26855a5..7663ad6861 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -209,6 +209,10 @@ static void fsl_imx7_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, i, irq); irq = qdev_get_gpio_in(d, ARM_CPU_FIQ); sysbus_connect_irq(sbd, i + smp_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VIRQ); + sysbus_connect_irq(sbd, i + 2 * smp_cpus, irq); + irq = qdev_get_gpio_in(d, ARM_CPU_VFIQ); + sysbus_connect_irq(sbd, i + 3 * smp_cpus, irq); } /* diff --git a/hw/arm/gumstix.c b/hw/arm/gumstix.c index 56cb763c4e..79886ce378 100644 --- a/hw/arm/gumstix.c +++ b/hw/arm/gumstix.c @@ -72,10 +72,9 @@ static void connex_init(MachineState *machine) #else be = 0; #endif - if (!pflash_cfi01_register(0x00000000, NULL, "connext.rom", connex_rom, + if (!pflash_cfi01_register(0x00000000, "connext.rom", connex_rom, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, connex_rom / sector_len, - 2, 0, 0, 0, 0, be)) { + sector_len, 2, 0, 0, 0, 0, be)) { error_report("Error registering flash memory"); exit(1); } @@ -109,10 +108,9 @@ static void verdex_init(MachineState *machine) #else be = 0; #endif - if (!pflash_cfi01_register(0x00000000, NULL, "verdex.rom", verdex_rom, + if (!pflash_cfi01_register(0x00000000, "verdex.rom", verdex_rom, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, verdex_rom / sector_len, - 2, 0, 0, 0, 0, be)) { + sector_len, 2, 0, 0, 0, 0, be)) { error_report("Error registering flash memory"); exit(1); } diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 6d42fce2c3..96ccf18d86 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -21,7 +21,6 @@ #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" @@ -243,6 +242,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) int n; qemu_irq cpu_irq[4]; qemu_irq cpu_fiq[4]; + qemu_irq cpu_virq[4]; + qemu_irq cpu_vfiq[4]; MemoryRegion *sysram; MemoryRegion *dram; MemoryRegion *sysmem; @@ -282,6 +283,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) 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); + cpu_virq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VIRQ); + cpu_vfiq[n] = qdev_get_gpio_in(DEVICE(cpu), ARM_CPU_VFIQ); } sysmem = get_system_memory(); @@ -329,6 +332,8 @@ static void calxeda_init(MachineState *machine, enum cxmachines machine_id) for (n = 0; n < smp_cpus; n++) { sysbus_connect_irq(busdev, n, cpu_irq[n]); sysbus_connect_irq(busdev, n + smp_cpus, cpu_fiq[n]); + sysbus_connect_irq(busdev, n + 2 * smp_cpus, cpu_virq[n]); + sysbus_connect_irq(busdev, n + 3 * smp_cpus, cpu_vfiq[n]); } for (n = 0; n < 128; n++) { diff --git a/hw/arm/iotkit.c b/hw/arm/iotkit.c deleted file mode 100644 index 8cadc8b160..0000000000 --- a/hw/arm/iotkit.c +++ /dev/null @@ -1,679 +0,0 @@ -/* - * Arm IoT Kit - * - * Copyright (c) 2018 Linaro Limited - * Written 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 version 2 or - * (at your option) any later version. - */ - -#include "qemu/osdep.h" -#include "qemu/log.h" -#include "qapi/error.h" -#include "trace.h" -#include "hw/sysbus.h" -#include "hw/registerfields.h" -#include "hw/arm/iotkit.h" -#include "hw/misc/unimp.h" -#include "hw/arm/arm.h" - -/* Create an alias region of @size bytes starting at @base - * which mirrors the memory starting at @orig. - */ -static void make_alias(IoTKit *s, MemoryRegion *mr, const char *name, - hwaddr base, hwaddr size, hwaddr orig) -{ - memory_region_init_alias(mr, NULL, name, &s->container, orig, size); - /* The alias is even lower priority than unimplemented_device regions */ - memory_region_add_subregion_overlap(&s->container, base, mr, -1500); -} - -static void irq_status_forwarder(void *opaque, int n, int level) -{ - qemu_irq destirq = opaque; - - qemu_set_irq(destirq, level); -} - -static void nsccfg_handler(void *opaque, int n, int level) -{ - IoTKit *s = IOTKIT(opaque); - - s->nsccfg = level; -} - -static void iotkit_forward_ppc(IoTKit *s, const char *ppcname, int ppcnum) -{ - /* Each of the 4 AHB and 4 APB PPCs that might be present in a - * system using the IoTKit has a collection of control lines which - * are provided by the security controller and which we want to - * expose as control lines on the IoTKit device itself, so the - * code using the IoTKit can wire them up to the PPCs. - */ - SplitIRQ *splitter = &s->ppc_irq_splitter[ppcnum]; - DeviceState *iotkitdev = DEVICE(s); - DeviceState *dev_secctl = DEVICE(&s->secctl); - DeviceState *dev_splitter = DEVICE(splitter); - char *name; - - name = g_strdup_printf("%s_nonsec", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - name = g_strdup_printf("%s_ap", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - name = g_strdup_printf("%s_irq_enable", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - name = g_strdup_printf("%s_irq_clear", ppcname); - qdev_pass_gpios(dev_secctl, iotkitdev, name); - g_free(name); - - /* irq_status is a little more tricky, because we need to - * split it so we can send it both to the security controller - * and to our OR gate for the NVIC interrupt line. - * Connect up the splitter's outputs, and create a GPIO input - * which will pass the line state to the input splitter. - */ - name = g_strdup_printf("%s_irq_status", ppcname); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_secctl, - name, 0)); - qdev_connect_gpio_out(dev_splitter, 1, - qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), ppcnum)); - s->irq_status_in[ppcnum] = qdev_get_gpio_in(dev_splitter, 0); - qdev_init_gpio_in_named_with_opaque(iotkitdev, irq_status_forwarder, - s->irq_status_in[ppcnum], name, 1); - g_free(name); -} - -static void iotkit_forward_sec_resp_cfg(IoTKit *s) -{ - /* Forward the 3rd output from the splitter device as a - * named GPIO output of the iotkit object. - */ - DeviceState *dev = DEVICE(s); - DeviceState *dev_splitter = DEVICE(&s->sec_resp_splitter); - - qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); - s->sec_resp_cfg_in = qemu_allocate_irq(irq_status_forwarder, - s->sec_resp_cfg, 1); - qdev_connect_gpio_out(dev_splitter, 2, s->sec_resp_cfg_in); -} - -static void iotkit_init(Object *obj) -{ - IoTKit *s = IOTKIT(obj); - int i; - - memory_region_init(&s->container, obj, "iotkit-container", UINT64_MAX); - - sysbus_init_child_obj(obj, "armv7m", &s->armv7m, sizeof(s->armv7m), - TYPE_ARMV7M); - qdev_prop_set_string(DEVICE(&s->armv7m), "cpu-type", - ARM_CPU_TYPE_NAME("cortex-m33")); - - sysbus_init_child_obj(obj, "secctl", &s->secctl, sizeof(s->secctl), - TYPE_IOTKIT_SECCTL); - sysbus_init_child_obj(obj, "apb-ppc0", &s->apb_ppc0, sizeof(s->apb_ppc0), - TYPE_TZ_PPC); - sysbus_init_child_obj(obj, "apb-ppc1", &s->apb_ppc1, sizeof(s->apb_ppc1), - TYPE_TZ_PPC); - sysbus_init_child_obj(obj, "mpc", &s->mpc, sizeof(s->mpc), TYPE_TZ_MPC); - object_initialize_child(obj, "mpc-irq-orgate", &s->mpc_irq_orgate, - sizeof(s->mpc_irq_orgate), TYPE_OR_IRQ, - &error_abort, NULL); - - for (i = 0; i < ARRAY_SIZE(s->mpc_irq_splitter); i++) { - char *name = g_strdup_printf("mpc-irq-splitter-%d", i); - SplitIRQ *splitter = &s->mpc_irq_splitter[i]; - - object_initialize_child(obj, name, splitter, sizeof(*splitter), - TYPE_SPLIT_IRQ, &error_abort, NULL); - g_free(name); - } - sysbus_init_child_obj(obj, "timer0", &s->timer0, sizeof(s->timer0), - TYPE_CMSDK_APB_TIMER); - sysbus_init_child_obj(obj, "timer1", &s->timer1, sizeof(s->timer1), - TYPE_CMSDK_APB_TIMER); - sysbus_init_child_obj(obj, "dualtimer", &s->dualtimer, sizeof(s->dualtimer), - TYPE_UNIMPLEMENTED_DEVICE); - object_initialize_child(obj, "ppc-irq-orgate", &s->ppc_irq_orgate, - sizeof(s->ppc_irq_orgate), TYPE_OR_IRQ, - &error_abort, NULL); - object_initialize_child(obj, "sec-resp-splitter", &s->sec_resp_splitter, - sizeof(s->sec_resp_splitter), TYPE_SPLIT_IRQ, - &error_abort, NULL); - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { - char *name = g_strdup_printf("ppc-irq-splitter-%d", i); - SplitIRQ *splitter = &s->ppc_irq_splitter[i]; - - object_initialize_child(obj, name, splitter, sizeof(*splitter), - TYPE_SPLIT_IRQ, &error_abort, NULL); - g_free(name); - } - sysbus_init_child_obj(obj, "s32ktimer", &s->s32ktimer, sizeof(s->s32ktimer), - TYPE_UNIMPLEMENTED_DEVICE); -} - -static void iotkit_exp_irq(void *opaque, int n, int level) -{ - IoTKit *s = IOTKIT(opaque); - - qemu_set_irq(s->exp_irqs[n], level); -} - -static void iotkit_mpcexp_status(void *opaque, int n, int level) -{ - IoTKit *s = IOTKIT(opaque); - qemu_set_irq(s->mpcexp_status_in[n], level); -} - -static void iotkit_realize(DeviceState *dev, Error **errp) -{ - IoTKit *s = IOTKIT(dev); - int i; - MemoryRegion *mr; - Error *err = NULL; - SysBusDevice *sbd_apb_ppc0; - SysBusDevice *sbd_secctl; - DeviceState *dev_apb_ppc0; - DeviceState *dev_apb_ppc1; - DeviceState *dev_secctl; - DeviceState *dev_splitter; - - if (!s->board_memory) { - error_setg(errp, "memory property was not set"); - return; - } - - if (!s->mainclk_frq) { - error_setg(errp, "MAINCLK property was not set"); - return; - } - - /* Handling of which devices should be available only to secure - * code is usually done differently for M profile than for A profile. - * Instead of putting some devices only into the secure address space, - * devices exist in both address spaces but with hard-wired security - * permissions that will cause the CPU to fault for non-secure accesses. - * - * The IoTKit has an IDAU (Implementation Defined Access Unit), - * which specifies hard-wired security permissions for different - * areas of the physical address space. For the IoTKit IDAU, the - * top 4 bits of the physical address are the IDAU region ID, and - * if bit 28 (ie the lowest bit of the ID) is 0 then this is an NS - * region, otherwise it is an S region. - * - * The various devices and RAMs are generally all mapped twice, - * once into a region that the IDAU defines as secure and once - * into a non-secure region. They sit behind either a Memory - * Protection Controller (for RAM) or a Peripheral Protection - * Controller (for devices), which allow a more fine grained - * configuration of whether non-secure accesses are permitted. - * - * (The other place that guest software can configure security - * permissions is in the architected SAU (Security Attribution - * Unit), which is entirely inside the CPU. The IDAU can upgrade - * the security attributes for a region to more restrictive than - * the SAU specifies, but cannot downgrade them.) - * - * 0x10000000..0x1fffffff alias of 0x00000000..0x0fffffff - * 0x20000000..0x2007ffff 32KB FPGA block RAM - * 0x30000000..0x3fffffff alias of 0x20000000..0x2fffffff - * 0x40000000..0x4000ffff base peripheral region 1 - * 0x40010000..0x4001ffff CPU peripherals (none for IoTKit) - * 0x40020000..0x4002ffff system control element peripherals - * 0x40080000..0x400fffff base peripheral region 2 - * 0x50000000..0x5fffffff alias of 0x40000000..0x4fffffff - */ - - memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); - - qdev_prop_set_uint32(DEVICE(&s->armv7m), "num-irq", s->exp_numirq + 32); - /* In real hardware the initial Secure VTOR is set from the INITSVTOR0 - * register in the IoT Kit System Control Register block, and the - * initial value of that is in turn specifiable by the FPGA that - * instantiates the IoT Kit. In QEMU we don't implement this wrinkle, - * and simply set the CPU's init-svtor to the IoT Kit default value. - */ - qdev_prop_set_uint32(DEVICE(&s->armv7m), "init-svtor", 0x10000000); - object_property_set_link(OBJECT(&s->armv7m), OBJECT(&s->container), - "memory", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_link(OBJECT(&s->armv7m), OBJECT(s), "idau", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - /* Connect our EXP_IRQ GPIOs to the NVIC's lines 32 and up. */ - s->exp_irqs = g_new(qemu_irq, s->exp_numirq); - for (i = 0; i < s->exp_numirq; i++) { - s->exp_irqs[i] = qdev_get_gpio_in(DEVICE(&s->armv7m), i + 32); - } - qdev_init_gpio_in_named(dev, iotkit_exp_irq, "EXP_IRQ", s->exp_numirq); - - /* Set up the big aliases first */ - make_alias(s, &s->alias1, "alias 1", 0x10000000, 0x10000000, 0x00000000); - make_alias(s, &s->alias2, "alias 2", 0x30000000, 0x10000000, 0x20000000); - /* The 0x50000000..0x5fffffff region is not a pure alias: it has - * a few extra devices that only appear there (generally the - * control interfaces for the protection controllers). - * We implement this by mapping those devices over the top of this - * alias MR at a higher priority. - */ - make_alias(s, &s->alias3, "alias 3", 0x50000000, 0x10000000, 0x40000000); - - - /* Security controller */ - object_property_set_bool(OBJECT(&s->secctl), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sbd_secctl = SYS_BUS_DEVICE(&s->secctl); - dev_secctl = DEVICE(&s->secctl); - sysbus_mmio_map(sbd_secctl, 0, 0x50080000); - sysbus_mmio_map(sbd_secctl, 1, 0x40080000); - - s->nsc_cfg_in = qemu_allocate_irq(nsccfg_handler, s, 1); - qdev_connect_gpio_out_named(dev_secctl, "nsc_cfg", 0, s->nsc_cfg_in); - - /* The sec_resp_cfg output from the security controller must be split into - * multiple lines, one for each of the PPCs within the IoTKit and one - * that will be an output from the IoTKit to the system. - */ - object_property_set_int(OBJECT(&s->sec_resp_splitter), 3, - "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->sec_resp_splitter), true, - "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - dev_splitter = DEVICE(&s->sec_resp_splitter); - qdev_connect_gpio_out_named(dev_secctl, "sec_resp_cfg", 0, - qdev_get_gpio_in(dev_splitter, 0)); - - /* This RAM lives behind the Memory Protection Controller */ - memory_region_init_ram(&s->sram0, NULL, "iotkit.sram0", 0x00008000, &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_link(OBJECT(&s->mpc), OBJECT(&s->sram0), - "downstream", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->mpc), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - /* Map the upstream end of the MPC into the right place... */ - memory_region_add_subregion(&s->container, 0x20000000, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), - 1)); - /* ...and its register interface */ - memory_region_add_subregion(&s->container, 0x50083000, - sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->mpc), - 0)); - - /* We must OR together lines from the MPC splitters to go to the NVIC */ - object_property_set_int(OBJECT(&s->mpc_irq_orgate), - IOTS_NUM_EXP_MPC + IOTS_NUM_MPC, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->mpc_irq_orgate), true, - "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - qdev_connect_gpio_out(DEVICE(&s->mpc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 9)); - - /* Devices behind APB PPC0: - * 0x40000000: timer0 - * 0x40001000: timer1 - * 0x40002000: dual timer - * We must configure and realize each downstream device and connect - * it to the appropriate PPC port; then we can realize the PPC and - * map its upstream ends to the right place in the container. - */ - qdev_prop_set_uint32(DEVICE(&s->timer0), "pclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->timer0), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer0), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 3)); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer0), 0); - object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[0]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - qdev_prop_set_uint32(DEVICE(&s->timer1), "pclk-frq", s->mainclk_frq); - object_property_set_bool(OBJECT(&s->timer1), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer1), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 4)); - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->timer1), 0); - object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[1]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - qdev_prop_set_string(DEVICE(&s->dualtimer), "name", "Dual timer"); - qdev_prop_set_uint64(DEVICE(&s->dualtimer), "size", 0x1000); - object_property_set_bool(OBJECT(&s->dualtimer), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->dualtimer), 0); - object_property_set_link(OBJECT(&s->apb_ppc0), OBJECT(mr), "port[2]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_bool(OBJECT(&s->apb_ppc0), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - sbd_apb_ppc0 = SYS_BUS_DEVICE(&s->apb_ppc0); - dev_apb_ppc0 = DEVICE(&s->apb_ppc0); - - mr = sysbus_mmio_get_region(sbd_apb_ppc0, 0); - memory_region_add_subregion(&s->container, 0x40000000, mr); - mr = sysbus_mmio_get_region(sbd_apb_ppc0, 1); - memory_region_add_subregion(&s->container, 0x40001000, mr); - mr = sysbus_mmio_get_region(sbd_apb_ppc0, 2); - memory_region_add_subregion(&s->container, 0x40002000, mr); - for (i = 0; i < IOTS_APB_PPC0_NUM_PORTS; i++) { - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_nonsec", i, - qdev_get_gpio_in_named(dev_apb_ppc0, - "cfg_nonsec", i)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_ap", i, - qdev_get_gpio_in_named(dev_apb_ppc0, - "cfg_ap", i)); - } - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_enable", 0, - qdev_get_gpio_in_named(dev_apb_ppc0, - "irq_enable", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc0_irq_clear", 0, - qdev_get_gpio_in_named(dev_apb_ppc0, - "irq_clear", 0)); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_apb_ppc0, - "cfg_sec_resp", 0)); - - /* All the PPC irq lines (from the 2 internal PPCs and the 8 external - * ones) are sent individually to the security controller, and also - * ORed together to give a single combined PPC interrupt to the NVIC. - */ - object_property_set_int(OBJECT(&s->ppc_irq_orgate), - NUM_PPCS, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(&s->ppc_irq_orgate), true, - "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - qdev_connect_gpio_out(DEVICE(&s->ppc_irq_orgate), 0, - qdev_get_gpio_in(DEVICE(&s->armv7m), 10)); - - /* 0x40010000 .. 0x4001ffff: private CPU region: unused in IoTKit */ - - /* 0x40020000 .. 0x4002ffff : IoTKit system control peripheral region */ - /* Devices behind APB PPC1: - * 0x4002f000: S32K timer - */ - qdev_prop_set_string(DEVICE(&s->s32ktimer), "name", "S32KTIMER"); - qdev_prop_set_uint64(DEVICE(&s->s32ktimer), "size", 0x1000); - object_property_set_bool(OBJECT(&s->s32ktimer), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->s32ktimer), 0); - object_property_set_link(OBJECT(&s->apb_ppc1), OBJECT(mr), "port[0]", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_bool(OBJECT(&s->apb_ppc1), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->apb_ppc1), 0); - memory_region_add_subregion(&s->container, 0x4002f000, mr); - - dev_apb_ppc1 = DEVICE(&s->apb_ppc1); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_nonsec", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "cfg_nonsec", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_ap", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "cfg_ap", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_enable", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "irq_enable", 0)); - qdev_connect_gpio_out_named(dev_secctl, "apb_ppc1_irq_clear", 0, - qdev_get_gpio_in_named(dev_apb_ppc1, - "irq_clear", 0)); - qdev_connect_gpio_out(dev_splitter, 1, - qdev_get_gpio_in_named(dev_apb_ppc1, - "cfg_sec_resp", 0)); - - /* Using create_unimplemented_device() maps the stub into the - * system address space rather than into our container, but the - * overall effect to the guest is the same. - */ - create_unimplemented_device("SYSINFO", 0x40020000, 0x1000); - - create_unimplemented_device("SYSCONTROL", 0x50021000, 0x1000); - create_unimplemented_device("S32KWATCHDOG", 0x5002e000, 0x1000); - - /* 0x40080000 .. 0x4008ffff : IoTKit second Base peripheral region */ - - create_unimplemented_device("NS watchdog", 0x40081000, 0x1000); - create_unimplemented_device("S watchdog", 0x50081000, 0x1000); - - for (i = 0; i < ARRAY_SIZE(s->ppc_irq_splitter); i++) { - Object *splitter = OBJECT(&s->ppc_irq_splitter[i]); - - object_property_set_int(splitter, 2, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(splitter, true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - } - - for (i = 0; i < IOTS_NUM_AHB_EXP_PPC; i++) { - char *ppcname = g_strdup_printf("ahb_ppcexp%d", i); - - iotkit_forward_ppc(s, ppcname, i); - g_free(ppcname); - } - - for (i = 0; i < IOTS_NUM_APB_EXP_PPC; i++) { - char *ppcname = g_strdup_printf("apb_ppcexp%d", i); - - iotkit_forward_ppc(s, ppcname, i + IOTS_NUM_AHB_EXP_PPC); - g_free(ppcname); - } - - for (i = NUM_EXTERNAL_PPCS; i < NUM_PPCS; i++) { - /* Wire up IRQ splitter for internal PPCs */ - DeviceState *devs = DEVICE(&s->ppc_irq_splitter[i]); - char *gpioname = g_strdup_printf("apb_ppc%d_irq_status", - i - NUM_EXTERNAL_PPCS); - TZPPC *ppc = (i == NUM_EXTERNAL_PPCS) ? &s->apb_ppc0 : &s->apb_ppc1; - - qdev_connect_gpio_out(devs, 0, - qdev_get_gpio_in_named(dev_secctl, gpioname, 0)); - qdev_connect_gpio_out(devs, 1, - qdev_get_gpio_in(DEVICE(&s->ppc_irq_orgate), i)); - qdev_connect_gpio_out_named(DEVICE(ppc), "irq", 0, - qdev_get_gpio_in(devs, 0)); - g_free(gpioname); - } - - /* Wire up the splitters for the MPC IRQs */ - for (i = 0; i < IOTS_NUM_EXP_MPC + IOTS_NUM_MPC; i++) { - SplitIRQ *splitter = &s->mpc_irq_splitter[i]; - DeviceState *dev_splitter = DEVICE(splitter); - - object_property_set_int(OBJECT(splitter), 2, "num-lines", &err); - if (err) { - error_propagate(errp, err); - return; - } - object_property_set_bool(OBJECT(splitter), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - if (i < IOTS_NUM_EXP_MPC) { - /* Splitter input is from GPIO input line */ - s->mpcexp_status_in[i] = qdev_get_gpio_in(dev_splitter, 0); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_secctl, - "mpcexp_status", i)); - } else { - /* Splitter input is from our own MPC */ - qdev_connect_gpio_out_named(DEVICE(&s->mpc), "irq", 0, - qdev_get_gpio_in(dev_splitter, 0)); - qdev_connect_gpio_out(dev_splitter, 0, - qdev_get_gpio_in_named(dev_secctl, - "mpc_status", 0)); - } - - qdev_connect_gpio_out(dev_splitter, 1, - qdev_get_gpio_in(DEVICE(&s->mpc_irq_orgate), i)); - } - /* Create GPIO inputs which will pass the line state for our - * mpcexp_irq inputs to the correct splitter devices. - */ - qdev_init_gpio_in_named(dev, iotkit_mpcexp_status, "mpcexp_status", - IOTS_NUM_EXP_MPC); - - iotkit_forward_sec_resp_cfg(s); - - system_clock_scale = NANOSECONDS_PER_SECOND / s->mainclk_frq; -} - -static void iotkit_idau_check(IDAUInterface *ii, uint32_t address, - int *iregion, bool *exempt, bool *ns, bool *nsc) -{ - /* For IoTKit systems the IDAU responses are simple logical functions - * of the address bits. The NSC attribute is guest-adjustable via the - * NSCCFG register in the security controller. - */ - IoTKit *s = IOTKIT(ii); - int region = extract32(address, 28, 4); - - *ns = !(region & 1); - *nsc = (region == 1 && (s->nsccfg & 1)) || (region == 3 && (s->nsccfg & 2)); - /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ - *exempt = (address & 0xeff00000) == 0xe0000000; - *iregion = region; -} - -static const VMStateDescription iotkit_vmstate = { - .name = "iotkit", - .version_id = 1, - .minimum_version_id = 1, - .fields = (VMStateField[]) { - VMSTATE_UINT32(nsccfg, IoTKit), - VMSTATE_END_OF_LIST() - } -}; - -static Property iotkit_properties[] = { - DEFINE_PROP_LINK("memory", IoTKit, board_memory, TYPE_MEMORY_REGION, - MemoryRegion *), - DEFINE_PROP_UINT32("EXP_NUMIRQ", IoTKit, exp_numirq, 64), - DEFINE_PROP_UINT32("MAINCLK", IoTKit, mainclk_frq, 0), - DEFINE_PROP_END_OF_LIST() -}; - -static void iotkit_reset(DeviceState *dev) -{ - IoTKit *s = IOTKIT(dev); - - s->nsccfg = 0; -} - -static void iotkit_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); - - dc->realize = iotkit_realize; - dc->vmsd = &iotkit_vmstate; - dc->props = iotkit_properties; - dc->reset = iotkit_reset; - iic->check = iotkit_idau_check; -} - -static const TypeInfo iotkit_info = { - .name = TYPE_IOTKIT, - .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(IoTKit), - .instance_init = iotkit_init, - .class_init = iotkit_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_IDAU_INTERFACE }, - { } - } -}; - -static void iotkit_register_types(void) -{ - type_register_static(&iotkit_info); -} - -type_init(iotkit_register_types); diff --git a/hw/arm/mainstone.c b/hw/arm/mainstone.c index 0beb5c426b..e96738ad26 100644 --- a/hw/arm/mainstone.c +++ b/hw/arm/mainstone.c @@ -148,12 +148,11 @@ static void mainstone_common_init(MemoryRegion *address_space_mem, exit(1); } - if (!pflash_cfi01_register(mainstone_flash_base[i], NULL, + if (!pflash_cfi01_register(mainstone_flash_base[i], i ? "mainstone.flash1" : "mainstone.flash0", MAINSTONE_FLASH, blk_by_legacy_dinfo(dinfo), - sector_len, MAINSTONE_FLASH / sector_len, - 4, 0, 0, 0, 0, be)) { + sector_len, 4, 0, 0, 0, 0, be)) { error_report("Error registering flash memory"); exit(1); } diff --git a/hw/arm/mcimx6ul-evk.c b/hw/arm/mcimx6ul-evk.c new file mode 100644 index 0000000000..fb2b015bf6 --- /dev/null +++ b/hw/arm/mcimx6ul-evk.c @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2018 Jean-Christophe Dubois + * + * MCIMX6UL_EVK Board System emulation. + * + * This code is licensed under the GPL, version 2 or later. + * See the file `COPYING' in the top level directory. + * + * It (partially) emulates a mcimx6ul_evk board, with a Freescale + * i.MX6ul SoC + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "hw/arm/fsl-imx6ul.h" +#include "hw/boards.h" +#include "sysemu/sysemu.h" +#include "qemu/error-report.h" +#include "sysemu/qtest.h" + +typedef struct { + FslIMX6ULState soc; + MemoryRegion ram; +} MCIMX6ULEVK; + +static void mcimx6ul_evk_init(MachineState *machine) +{ + static struct arm_boot_info boot_info; + MCIMX6ULEVK *s = g_new0(MCIMX6ULEVK, 1); + int i; + + if (machine->ram_size > FSL_IMX6UL_MMDC_SIZE) { + error_report("RAM size " RAM_ADDR_FMT " above max supported (%08x)", + machine->ram_size, FSL_IMX6UL_MMDC_SIZE); + exit(1); + } + + boot_info = (struct arm_boot_info) { + .loader_start = FSL_IMX6UL_MMDC_ADDR, + .board_id = -1, + .ram_size = machine->ram_size, + .kernel_filename = machine->kernel_filename, + .kernel_cmdline = machine->kernel_cmdline, + .initrd_filename = machine->initrd_filename, + .nb_cpus = smp_cpus, + }; + + object_initialize_child(OBJECT(machine), "soc", &s->soc, sizeof(s->soc), + TYPE_FSL_IMX6UL, &error_fatal, NULL); + + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + + memory_region_allocate_system_memory(&s->ram, NULL, "mcimx6ul-evk.ram", + machine->ram_size); + memory_region_add_subregion(get_system_memory(), + FSL_IMX6UL_MMDC_ADDR, &s->ram); + + for (i = 0; i < FSL_IMX6UL_NUM_USDHCS; i++) { + BusState *bus; + DeviceState *carddev; + DriveInfo *di; + BlockBackend *blk; + + di = drive_get_next(IF_SD); + blk = di ? blk_by_legacy_dinfo(di) : NULL; + bus = qdev_get_child_bus(DEVICE(&s->soc.usdhc[i]), "sd-bus"); + 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); + } + + if (!qtest_enabled()) { + arm_load_kernel(&s->soc.cpu[0], &boot_info); + } +} + +static void mcimx6ul_evk_machine_init(MachineClass *mc) +{ + mc->desc = "Freescale i.MX6UL Evaluation Kit (Cortex A7)"; + mc->init = mcimx6ul_evk_init; + mc->max_cpus = FSL_IMX6UL_NUM_CPUS; +} +DEFINE_MACHINE("mcimx6ul-evk", mcimx6ul_evk_machine_init) diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c new file mode 100644 index 0000000000..da67bf6d9d --- /dev/null +++ b/hw/arm/microbit.c @@ -0,0 +1,85 @@ +/* + * BBC micro:bit machine + * http://tech.microbit.org/hardware/ + * + * Copyright 2018 Joel Stanley + * + * 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 "hw/boards.h" +#include "hw/arm/arm.h" +#include "sysemu/sysemu.h" +#include "exec/address-spaces.h" + +#include "hw/arm/nrf51_soc.h" +#include "hw/i2c/microbit_i2c.h" + +typedef struct { + MachineState parent; + + NRF51State nrf51; + MicrobitI2CState i2c; +} MicrobitMachineState; + +#define TYPE_MICROBIT_MACHINE MACHINE_TYPE_NAME("microbit") + +#define MICROBIT_MACHINE(obj) \ + OBJECT_CHECK(MicrobitMachineState, obj, TYPE_MICROBIT_MACHINE) + +static void microbit_init(MachineState *machine) +{ + MicrobitMachineState *s = MICROBIT_MACHINE(machine); + MemoryRegion *system_memory = get_system_memory(); + MemoryRegion *mr; + Object *soc = OBJECT(&s->nrf51); + Object *i2c = OBJECT(&s->i2c); + + sysbus_init_child_obj(OBJECT(machine), "nrf51", soc, sizeof(s->nrf51), + TYPE_NRF51_SOC); + qdev_prop_set_chr(DEVICE(&s->nrf51), "serial0", serial_hd(0)); + object_property_set_link(soc, OBJECT(system_memory), "memory", + &error_fatal); + object_property_set_bool(soc, true, "realized", &error_fatal); + + /* + * Overlap the TWI stub device into the SoC. This is a microbit-specific + * hack until we implement the nRF51 TWI controller properly and the + * magnetometer/accelerometer devices. + */ + sysbus_init_child_obj(OBJECT(machine), "microbit.twi", i2c, + sizeof(s->i2c), TYPE_MICROBIT_I2C); + object_property_set_bool(i2c, true, "realized", &error_fatal); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(i2c), 0); + memory_region_add_subregion_overlap(&s->nrf51.container, NRF51_TWI_BASE, + mr, -1); + + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, + NRF51_SOC(soc)->flash_size); +} + +static void microbit_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "BBC micro:bit"; + mc->init = microbit_init; + mc->max_cpus = 1; +} + +static const TypeInfo microbit_info = { + .name = TYPE_MICROBIT_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(MicrobitMachineState), + .class_init = microbit_machine_class_init, +}; + +static void microbit_machine_init(void) +{ + type_register_static(µbit_info); +} + +type_init(microbit_machine_init); diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 22180c56fb..f79f090a4a 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -15,6 +15,7 @@ * as seen by the guest depend significantly on the FPGA image. * This source file covers the following FPGA images, for TrustZone cores: * "mps2-an505" -- Cortex-M33 as documented in ARM Application Note AN505 + * "mps2-an521" -- Dual Cortex-M33 as documented in Application Note AN521 * * Links to the TRM for the board itself and to the various Application * Notes which document the FPGA images can be found here: @@ -24,10 +25,16 @@ * http://infocenter.arm.com/help/topic/com.arm.doc.100112_0200_06_en/versatile_express_cortex_m_prototyping_systems_v2m_mps2_and_v2m_mps2plus_technical_reference_100112_0200_06_en.pdf * Application Note AN505: * http://infocenter.arm.com/help/topic/com.arm.doc.dai0505b/index.html + * Application Note AN521: + * http://infocenter.arm.com/help/topic/com.arm.doc.dai0521c/index.html * * The AN505 defers to the Cortex-M33 processor ARMv8M IoT Kit FVP User Guide * (ARM ECM0601256) for the details of some of the device layout: * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Similarly, the AN521 uses the SSE-200, and the SSE-200 TRM defines + * most of the device layout: + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * */ #include "qemu/osdep.h" @@ -45,25 +52,31 @@ #include "hw/misc/mps2-scc.h" #include "hw/misc/mps2-fpgaio.h" #include "hw/misc/tz-mpc.h" -#include "hw/arm/iotkit.h" -#include "hw/devices.h" +#include "hw/misc/tz-msc.h" +#include "hw/arm/armsse.h" +#include "hw/dma/pl080.h" +#include "hw/ssi/pl022.h" #include "net/net.h" #include "hw/core/split-irq.h" +#define MPS2TZ_NUMIRQ 92 + typedef enum MPS2TZFPGAType { FPGA_AN505, + FPGA_AN521, } MPS2TZFPGAType; typedef struct { MachineClass parent; MPS2TZFPGAType fpga_type; uint32_t scc_id; + const char *armsse_type; } MPS2TZMachineClass; typedef struct { MachineState parent; - IoTKit iotkit; + ARMSSE iotkit; MemoryRegion psram; MemoryRegion ssram[3]; MemoryRegion ssram1_m; @@ -71,20 +84,23 @@ typedef struct { MPS2FPGAIO fpgaio; TZPPC ppc[5]; TZMPC ssram_mpc[3]; - UnimplementedDeviceState spi[5]; + PL022State spi[5]; UnimplementedDeviceState i2c[4]; UnimplementedDeviceState i2s_audio; UnimplementedDeviceState gpio[4]; - UnimplementedDeviceState dma[4]; UnimplementedDeviceState gfx; + PL080State dma[4]; + TZMSC msc[4]; CMSDKAPBUART uart[5]; SplitIRQ sec_resp_splitter; qemu_or_irq uart_irq_orgate; DeviceState *lan9118; + SplitIRQ cpu_irq_splitter[MPS2TZ_NUMIRQ]; } MPS2TZMachineState; #define TYPE_MPS2TZ_MACHINE "mps2tz" #define TYPE_MPS2TZ_AN505_MACHINE MACHINE_TYPE_NAME("mps2-an505") +#define TYPE_MPS2TZ_AN521_MACHINE MACHINE_TYPE_NAME("mps2-an521") #define MPS2TZ_MACHINE(obj) \ OBJECT_CHECK(MPS2TZMachineState, obj, TYPE_MPS2TZ_MACHINE) @@ -107,14 +123,21 @@ static void make_ram_alias(MemoryRegion *mr, const char *name, memory_region_add_subregion(get_system_memory(), base, mr); } -static void init_sysbus_child(Object *parent, const char *childname, - void *child, size_t childsize, - const char *childtype) +static qemu_irq get_sse_irq_in(MPS2TZMachineState *mms, int irqno) { - object_initialize(child, childsize, childtype); - object_property_add_child(parent, childname, OBJECT(child), &error_abort); - qdev_set_parent_bus(DEVICE(child), sysbus_get_default()); + /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */ + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); + assert(irqno < MPS2TZ_NUMIRQ); + + switch (mmc->fpga_type) { + case FPGA_AN505: + return qdev_get_gpio_in_named(DEVICE(&mms->iotkit), "EXP_IRQ", irqno); + case FPGA_AN521: + return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0); + default: + g_assert_not_reached(); + } } /* Most of the devices in the AN505 FPGA image sit behind @@ -149,9 +172,9 @@ static MemoryRegion *make_unimp_dev(MPS2TZMachineState *mms, */ UnimplementedDeviceState *uds = opaque; - init_sysbus_child(OBJECT(mms), name, uds, - sizeof(UnimplementedDeviceState), - TYPE_UNIMPLEMENTED_DEVICE); + sysbus_init_child_obj(OBJECT(mms), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); qdev_prop_set_string(DEVICE(uds), "name", name); qdev_prop_set_uint64(DEVICE(uds), "size", size); object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); @@ -167,23 +190,19 @@ static MemoryRegion *make_uart(MPS2TZMachineState *mms, void *opaque, int txirqno = i * 2 + 1; int combirqno = i + 10; SysBusDevice *s; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); DeviceState *orgate_dev = DEVICE(&mms->uart_irq_orgate); - init_sysbus_child(OBJECT(mms), name, uart, - sizeof(mms->uart[0]), TYPE_CMSDK_APB_UART); + sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]), + TYPE_CMSDK_APB_UART); qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i)); qdev_prop_set_uint32(DEVICE(uart), "pclk-frq", SYSCLK_FRQ); object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); s = SYS_BUS_DEVICE(uart); - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", txirqno)); - sysbus_connect_irq(s, 1, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", rxirqno)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, txirqno)); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, rxirqno)); sysbus_connect_irq(s, 2, qdev_get_gpio_in(orgate_dev, i * 2)); sysbus_connect_irq(s, 3, qdev_get_gpio_in(orgate_dev, i * 2 + 1)); - sysbus_connect_irq(s, 4, qdev_get_gpio_in_named(iotkitdev, - "EXP_IRQ", combirqno)); + sysbus_connect_irq(s, 4, get_sse_irq_in(mms, combirqno)); return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); } @@ -198,7 +217,7 @@ static MemoryRegion *make_scc(MPS2TZMachineState *mms, void *opaque, sccdev = DEVICE(scc); qdev_set_parent_bus(sccdev, sysbus_get_default()); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); - qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); object_property_set_bool(OBJECT(scc), true, "realized", &error_fatal); return sysbus_mmio_get_region(SYS_BUS_DEVICE(sccdev), 0); @@ -219,7 +238,6 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, const char *name, hwaddr size) { SysBusDevice *s; - DeviceState *iotkitdev = DEVICE(&mms->iotkit); NICInfo *nd = &nd_table[0]; /* In hardware this is a LAN9220; the LAN9118 is software compatible @@ -231,7 +249,7 @@ static MemoryRegion *make_eth_dev(MPS2TZMachineState *mms, void *opaque, qdev_init_nofail(mms->lan9118); s = SYS_BUS_DEVICE(mms->lan9118); - sysbus_connect_irq(s, 0, qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 16)); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 16)); return sysbus_mmio_get_region(s, 0); } @@ -248,8 +266,8 @@ static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, memory_region_init_ram(ssram, NULL, name, ramsize[i], &error_fatal); - init_sysbus_child(OBJECT(mms), mpcname, mpc, - sizeof(mms->ssram_mpc[0]), TYPE_TZ_MPC); + sysbus_init_child_obj(OBJECT(mms), mpcname, mpc, sizeof(mms->ssram_mpc[0]), + TYPE_TZ_MPC); object_property_set_link(OBJECT(mpc), OBJECT(ssram), "downstream", &error_fatal); object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal); @@ -273,9 +291,89 @@ static MemoryRegion *make_mpc(MPS2TZMachineState *mms, void *opaque, return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); } +static MemoryRegion *make_dma(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL080State *dma = opaque; + int i = dma - &mms->dma[0]; + SysBusDevice *s; + char *mscname = g_strdup_printf("%s-msc", name); + TZMSC *msc = &mms->msc[i]; + DeviceState *iotkitdev = DEVICE(&mms->iotkit); + MemoryRegion *msc_upstream; + MemoryRegion *msc_downstream; + + /* + * Each DMA device is a PL081 whose transaction master interface + * is guarded by a Master Security Controller. The downstream end of + * the MSC connects to the IoTKit AHB Slave Expansion port, so the + * DMA devices can see all devices and memory that the CPU does. + */ + sysbus_init_child_obj(OBJECT(mms), mscname, msc, sizeof(*msc), TYPE_TZ_MSC); + msc_downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(&mms->iotkit), 0); + object_property_set_link(OBJECT(msc), OBJECT(msc_downstream), + "downstream", &error_fatal); + object_property_set_link(OBJECT(msc), OBJECT(mms), + "idau", &error_fatal); + object_property_set_bool(OBJECT(msc), true, "realized", &error_fatal); + + qdev_connect_gpio_out_named(DEVICE(msc), "irq", 0, + qdev_get_gpio_in_named(iotkitdev, + "mscexp_status", i)); + qdev_connect_gpio_out_named(iotkitdev, "mscexp_clear", i, + qdev_get_gpio_in_named(DEVICE(msc), + "irq_clear", 0)); + qdev_connect_gpio_out_named(iotkitdev, "mscexp_ns", i, + qdev_get_gpio_in_named(DEVICE(msc), + "cfg_nonsec", 0)); + qdev_connect_gpio_out(DEVICE(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc) + i, + qdev_get_gpio_in_named(DEVICE(msc), + "cfg_sec_resp", 0)); + msc_upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(msc), 0); + + sysbus_init_child_obj(OBJECT(mms), name, dma, sizeof(*dma), TYPE_PL081); + object_property_set_link(OBJECT(dma), OBJECT(msc_upstream), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(dma), true, "realized", &error_fatal); + + s = SYS_BUS_DEVICE(dma); + /* Wire up DMACINTR, DMACINTERR, DMACINTTC */ + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 58 + i * 3)); + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, 56 + i * 3)); + sysbus_connect_irq(s, 2, get_sse_irq_in(mms, 57 + i * 3)); + + g_free(mscname); + return sysbus_mmio_get_region(s, 0); +} + +static MemoryRegion *make_spi(MPS2TZMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * The AN505 has five PL022 SPI controllers. + * One of these should have the LCD controller behind it; the others + * are connected only to the FPGA's "general purpose SPI connector" + * or "shield" expansion connectors. + * Note that if we do implement devices behind SPI, the chip select + * lines are set via the "MISC" register in the MPS2 FPGAIO device. + */ + PL022State *spi = opaque; + int i = spi - &mms->spi[0]; + SysBusDevice *s; + + sysbus_init_child_obj(OBJECT(mms), name, spi, sizeof(mms->spi[0]), + TYPE_PL022); + object_property_set_bool(OBJECT(spi), true, "realized", &error_fatal); + s = SYS_BUS_DEVICE(spi); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, 51 + i)); + return sysbus_mmio_get_region(s, 0); +} + static void mps2tz_common_init(MachineState *machine) { MPS2TZMachineState *mms = MPS2TZ_MACHINE(machine); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_GET_CLASS(mms); MachineClass *mc = MACHINE_GET_CLASS(machine); MemoryRegion *system_memory = get_system_memory(); DeviceState *iotkitdev; @@ -288,24 +386,52 @@ static void mps2tz_common_init(MachineState *machine) exit(1); } - init_sysbus_child(OBJECT(machine), "iotkit", &mms->iotkit, - sizeof(mms->iotkit), TYPE_IOTKIT); + sysbus_init_child_obj(OBJECT(machine), "iotkit", &mms->iotkit, + sizeof(mms->iotkit), mmc->armsse_type); iotkitdev = DEVICE(&mms->iotkit); object_property_set_link(OBJECT(&mms->iotkit), OBJECT(system_memory), "memory", &error_abort); - qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", 92); + qdev_prop_set_uint32(iotkitdev, "EXP_NUMIRQ", MPS2TZ_NUMIRQ); qdev_prop_set_uint32(iotkitdev, "MAINCLK", SYSCLK_FRQ); object_property_set_bool(OBJECT(&mms->iotkit), true, "realized", &error_fatal); + /* + * The AN521 needs us to create splitters to feed the IRQ inputs + * for each CPU in the SSE-200 from each device in the board. + */ + if (mmc->fpga_type == FPGA_AN521) { + for (i = 0; i < MPS2TZ_NUMIRQ; i++) { + char *name = g_strdup_printf("mps2-irq-splitter%d", i); + SplitIRQ *splitter = &mms->cpu_irq_splitter[i]; + + object_initialize_child(OBJECT(machine), name, + splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_fatal, NULL); + g_free(name); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(splitter), true, "realized", + &error_fatal); + qdev_connect_gpio_out(DEVICE(splitter), 0, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "EXP_IRQ", i)); + qdev_connect_gpio_out(DEVICE(splitter), 1, + qdev_get_gpio_in_named(DEVICE(&mms->iotkit), + "EXP_CPU1_IRQ", i)); + } + } + /* The sec_resp_cfg output from the IoTKit must be split into multiple - * lines, one for each of the PPCs we create here. + * lines, one for each of the PPCs we create here, plus one per MSC. */ object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter), TYPE_SPLIT_IRQ); object_property_add_child(OBJECT(machine), "sec-resp-splitter", OBJECT(&mms->sec_resp_splitter), &error_abort); - object_property_set_int(OBJECT(&mms->sec_resp_splitter), 5, + object_property_set_int(OBJECT(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc) + ARRAY_SIZE(mms->msc), "num-lines", &error_fatal); object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true, "realized", &error_fatal); @@ -347,7 +473,7 @@ static void mps2tz_common_init(MachineState *machine) object_property_set_bool(OBJECT(&mms->uart_irq_orgate), true, "realized", &error_fatal); qdev_connect_gpio_out(DEVICE(&mms->uart_irq_orgate), 0, - qdev_get_gpio_in_named(iotkitdev, "EXP_IRQ", 15)); + get_sse_irq_in(mms, 15)); /* Most of the devices in the FPGA are behind Peripheral Protection * Controllers. The required order for initializing things is: @@ -370,11 +496,11 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "apb_ppcexp1", .ports = { - { "spi0", make_unimp_dev, &mms->spi[0], 0x40205000, 0x1000 }, - { "spi1", make_unimp_dev, &mms->spi[1], 0x40206000, 0x1000 }, - { "spi2", make_unimp_dev, &mms->spi[2], 0x40209000, 0x1000 }, - { "spi3", make_unimp_dev, &mms->spi[3], 0x4020a000, 0x1000 }, - { "spi4", make_unimp_dev, &mms->spi[4], 0x4020b000, 0x1000 }, + { "spi0", make_spi, &mms->spi[0], 0x40205000, 0x1000 }, + { "spi1", make_spi, &mms->spi[1], 0x40206000, 0x1000 }, + { "spi2", make_spi, &mms->spi[2], 0x40209000, 0x1000 }, + { "spi3", make_spi, &mms->spi[3], 0x4020a000, 0x1000 }, + { "spi4", make_spi, &mms->spi[4], 0x4020b000, 0x1000 }, { "uart0", make_uart, &mms->uart[0], 0x40200000, 0x1000 }, { "uart1", make_uart, &mms->uart[1], 0x40201000, 0x1000 }, { "uart2", make_uart, &mms->uart[2], 0x40202000, 0x1000 }, @@ -406,10 +532,10 @@ static void mps2tz_common_init(MachineState *machine) }, { .name = "ahb_ppcexp1", .ports = { - { "dma0", make_unimp_dev, &mms->dma[0], 0x40110000, 0x1000 }, - { "dma1", make_unimp_dev, &mms->dma[1], 0x40111000, 0x1000 }, - { "dma2", make_unimp_dev, &mms->dma[2], 0x40112000, 0x1000 }, - { "dma3", make_unimp_dev, &mms->dma[3], 0x40113000, 0x1000 }, + { "dma0", make_dma, &mms->dma[0], 0x40110000, 0x1000 }, + { "dma1", make_dma, &mms->dma[1], 0x40111000, 0x1000 }, + { "dma2", make_dma, &mms->dma[2], 0x40112000, 0x1000 }, + { "dma3", make_dma, &mms->dma[3], 0x40113000, 0x1000 }, }, }, }; @@ -421,8 +547,8 @@ static void mps2tz_common_init(MachineState *machine) int port; char *gpioname; - init_sysbus_child(OBJECT(machine), ppcinfo->name, ppc, - sizeof(TZPPC), TYPE_TZ_PPC); + sysbus_init_child_obj(OBJECT(machine), ppcinfo->name, ppc, + sizeof(TZPPC), TYPE_TZ_PPC); ppcdev = DEVICE(ppc); for (port = 0; port < TZ_NUM_PORTS; port++) { @@ -490,12 +616,31 @@ static void mps2tz_common_init(MachineState *machine) armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x400000); } +static void mps2_tz_idau_check(IDAUInterface *ii, uint32_t address, + int *iregion, bool *exempt, bool *ns, bool *nsc) +{ + /* + * The MPS2 TZ FPGA images have IDAUs in them which are connected to + * the Master Security Controllers. Thes have the same logic as + * is used by the IoTKit for the IDAU connected to the CPU, except + * that MSCs don't care about the NSC attribute. + */ + int region = extract32(address, 28, 4); + + *ns = !(region & 1); + *nsc = false; + /* 0xe0000000..0xe00fffff and 0xf0000000..0xf00fffff are exempt */ + *exempt = (address & 0xeff00000) == 0xe0000000; + *iregion = region; +} + static void mps2tz_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); mc->init = mps2tz_common_init; - mc->max_cpus = 1; + iic->check = mps2_tz_idau_check; } static void mps2tz_an505_class_init(ObjectClass *oc, void *data) @@ -504,9 +649,28 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); mc->desc = "ARM MPS2 with AN505 FPGA image for Cortex-M33"; + mc->default_cpus = 1; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; mmc->fpga_type = FPGA_AN505; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); - mmc->scc_id = 0x41040000 | (505 << 4); + mmc->scc_id = 0x41045050; + mmc->armsse_type = TYPE_IOTKIT; +} + +static void mps2tz_an521_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); + + mc->desc = "ARM MPS2 with AN521 FPGA image for dual Cortex-M33"; + mc->default_cpus = 2; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mmc->fpga_type = FPGA_AN521; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mmc->scc_id = 0x41045210; + mmc->armsse_type = TYPE_SSE200; } static const TypeInfo mps2tz_info = { @@ -516,6 +680,10 @@ static const TypeInfo mps2tz_info = { .instance_size = sizeof(MPS2TZMachineState), .class_size = sizeof(MPS2TZMachineClass), .class_init = mps2tz_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_IDAU_INTERFACE }, + { } + }, }; static const TypeInfo mps2tz_an505_info = { @@ -524,10 +692,17 @@ static const TypeInfo mps2tz_an505_info = { .class_init = mps2tz_an505_class_init, }; +static const TypeInfo mps2tz_an521_info = { + .name = TYPE_MPS2TZ_AN521_MACHINE, + .parent = TYPE_MPS2TZ_MACHINE, + .class_init = mps2tz_an521_class_init, +}; + static void mps2tz_machine_init(void) { type_register_static(&mps2tz_info); type_register_static(&mps2tz_an505_info); + type_register_static(&mps2tz_an521_info); } type_init(mps2tz_machine_init); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index c3946da317..e3d698ba6c 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -34,6 +34,7 @@ #include "hw/misc/unimp.h" #include "hw/char/cmsdk-apb-uart.h" #include "hw/timer/cmsdk-apb-timer.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" #include "hw/misc/mps2-scc.h" #include "hw/devices.h" #include "net/net.h" @@ -64,6 +65,7 @@ typedef struct { MemoryRegion blockram_m3; MemoryRegion sram; MPS2SCC scc; + CMSDKAPBDualTimer dualtimer; } MPS2MachineState; #define TYPE_MPS2_MACHINE "mps2" @@ -186,6 +188,7 @@ static void mps2_common_init(MachineState *machine) g_assert_not_reached(); } qdev_prop_set_string(armv7m, "cpu-type", machine->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); object_property_set_link(OBJECT(&mms->armv7m), OBJECT(system_memory), "memory", &error_abort); object_property_set_bool(OBJECT(&mms->armv7m), true, "realized", @@ -296,11 +299,20 @@ static void mps2_common_init(MachineState *machine) cmsdk_apb_timer_create(0x40000000, qdev_get_gpio_in(armv7m, 8), SYSCLK_FRQ); cmsdk_apb_timer_create(0x40001000, qdev_get_gpio_in(armv7m, 9), SYSCLK_FRQ); + sysbus_init_child_obj(OBJECT(mms), "dualtimer", &mms->dualtimer, + sizeof(mms->dualtimer), TYPE_CMSDK_APB_DUALTIMER); + qdev_prop_set_uint32(DEVICE(&mms->dualtimer), "pclk-frq", SYSCLK_FRQ); + object_property_set_bool(OBJECT(&mms->dualtimer), true, "realized", + &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(&mms->dualtimer), 0, + qdev_get_gpio_in(armv7m, 10)); + sysbus_mmio_map(SYS_BUS_DEVICE(&mms->dualtimer), 0, 0x40002000); + object_initialize(&mms->scc, sizeof(mms->scc), TYPE_MPS2_SCC); sccdev = DEVICE(&mms->scc); qdev_set_parent_bus(sccdev, sysbus_get_default()); qdev_prop_set_uint32(sccdev, "scc-cfg4", 0x2); - qdev_prop_set_uint32(sccdev, "scc-aid", 0x02000008); + qdev_prop_set_uint32(sccdev, "scc-aid", 0x00200008); qdev_prop_set_uint32(sccdev, "scc-id", mmc->scc_id); object_property_set_bool(OBJECT(&mms->scc), true, "realized", &error_fatal); @@ -335,7 +347,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN385 FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN385; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mmc->scc_id = 0x41040000 | (385 << 4); + mmc->scc_id = 0x41043850; } static void mps2_an511_class_init(ObjectClass *oc, void *data) @@ -346,7 +358,7 @@ static void mps2_an511_class_init(ObjectClass *oc, void *data) mc->desc = "ARM MPS2 with AN511 DesignStart FPGA image for Cortex-M3"; mmc->fpga_type = FPGA_AN511; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m3"); - mmc->scc_id = 0x4104000 | (511 << 4); + mmc->scc_id = 0x41045110; } static const TypeInfo mps2_info = { diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index dbefade644..2702e90b45 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -117,6 +117,7 @@ static void m2sxxx_soc_realize(DeviceState *dev_soc, Error **errp) armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 81); qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); object_property_set_link(OBJECT(&s->armv7m), OBJECT(get_system_memory()), "memory", &error_abort); object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); diff --git a/hw/arm/musca.c b/hw/arm/musca.c new file mode 100644 index 0000000000..23aff43f4b --- /dev/null +++ b/hw/arm/musca.c @@ -0,0 +1,669 @@ +/* + * Arm Musca-B1 test chip board emulation + * + * Copyright (c) 2019 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * The Musca boards are a reference implementation of a system using + * the SSE-200 subsystem for embedded: + * https://developer.arm.com/products/system-design/development-boards/iot-test-chips-and-boards/musca-a-test-chip-board + * https://developer.arm.com/products/system-design/development-boards/iot-test-chips-and-boards/musca-b-test-chip-board + * We model the A and B1 variants of this board, as described in the TRMs: + * http://infocenter.arm.com/help/topic/com.arm.doc.101107_0000_00_en/index.html + * http://infocenter.arm.com/help/topic/com.arm.doc.101312_0000_00_en/index.html + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "hw/arm/arm.h" +#include "hw/arm/armsse.h" +#include "hw/boards.h" +#include "hw/char/pl011.h" +#include "hw/core/split-irq.h" +#include "hw/misc/tz-mpc.h" +#include "hw/misc/tz-ppc.h" +#include "hw/misc/unimp.h" +#include "hw/timer/pl031.h" + +#define MUSCA_NUMIRQ_MAX 96 +#define MUSCA_PPC_MAX 3 +#define MUSCA_MPC_MAX 5 + +typedef struct MPCInfo MPCInfo; + +typedef enum MuscaType { + MUSCA_A, + MUSCA_B1, +} MuscaType; + +typedef struct { + MachineClass parent; + MuscaType type; + uint32_t init_svtor; + int sram_addr_width; + int num_irqs; + const MPCInfo *mpc_info; + int num_mpcs; +} MuscaMachineClass; + +typedef struct { + MachineState parent; + + ARMSSE sse; + /* RAM and flash */ + MemoryRegion ram[MUSCA_MPC_MAX]; + SplitIRQ cpu_irq_splitter[MUSCA_NUMIRQ_MAX]; + SplitIRQ sec_resp_splitter; + TZPPC ppc[MUSCA_PPC_MAX]; + MemoryRegion container; + UnimplementedDeviceState eflash[2]; + UnimplementedDeviceState qspi; + TZMPC mpc[MUSCA_MPC_MAX]; + UnimplementedDeviceState mhu[2]; + UnimplementedDeviceState pwm[3]; + UnimplementedDeviceState i2s; + PL011State uart[2]; + UnimplementedDeviceState i2c[2]; + UnimplementedDeviceState spi; + UnimplementedDeviceState scc; + UnimplementedDeviceState timer; + PL031State rtc; + UnimplementedDeviceState pvt; + UnimplementedDeviceState sdio; + UnimplementedDeviceState gpio; + UnimplementedDeviceState cryptoisland; +} MuscaMachineState; + +#define TYPE_MUSCA_MACHINE "musca" +#define TYPE_MUSCA_A_MACHINE MACHINE_TYPE_NAME("musca-a") +#define TYPE_MUSCA_B1_MACHINE MACHINE_TYPE_NAME("musca-b1") + +#define MUSCA_MACHINE(obj) \ + OBJECT_CHECK(MuscaMachineState, obj, TYPE_MUSCA_MACHINE) +#define MUSCA_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(MuscaMachineClass, obj, TYPE_MUSCA_MACHINE) +#define MUSCA_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(MuscaMachineClass, klass, TYPE_MUSCA_MACHINE) + +/* + * Main SYSCLK frequency in Hz + * TODO this should really be different for the two cores, but we + * don't model that in our SSE-200 model yet. + */ +#define SYSCLK_FRQ 40000000 + +static qemu_irq get_sse_irq_in(MuscaMachineState *mms, int irqno) +{ + /* Return a qemu_irq which will signal IRQ n to all CPUs in the SSE. */ + assert(irqno < MUSCA_NUMIRQ_MAX); + + return qdev_get_gpio_in(DEVICE(&mms->cpu_irq_splitter[irqno]), 0); +} + +/* + * Most of the devices in the Musca board sit behind Peripheral Protection + * Controllers. These data structures define the layout of which devices + * sit behind which PPCs. + * The devfn for each port is a function which creates, configures + * and initializes the device, returning the MemoryRegion which + * needs to be plugged into the downstream end of the PPC port. + */ +typedef MemoryRegion *MakeDevFn(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size); + +typedef struct PPCPortInfo { + const char *name; + MakeDevFn *devfn; + void *opaque; + hwaddr addr; + hwaddr size; +} PPCPortInfo; + +typedef struct PPCInfo { + const char *name; + PPCPortInfo ports[TZ_NUM_PORTS]; +} PPCInfo; + +static MemoryRegion *make_unimp_dev(MuscaMachineState *mms, + void *opaque, const char *name, hwaddr size) +{ + /* + * Initialize, configure and realize a TYPE_UNIMPLEMENTED_DEVICE, + * and return a pointer to its MemoryRegion. + */ + UnimplementedDeviceState *uds = opaque; + + sysbus_init_child_obj(OBJECT(mms), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(uds), "name", name); + qdev_prop_set_uint64(DEVICE(uds), "size", size); + object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0); +} + +typedef enum MPCInfoType { + MPC_RAM, + MPC_ROM, + MPC_CRYPTOISLAND, +} MPCInfoType; + +struct MPCInfo { + const char *name; + hwaddr addr; + hwaddr size; + MPCInfoType type; +}; + +/* Order of the MPCs here must match the order of the bits in SECMPCINTSTATUS */ +static const MPCInfo a_mpc_info[] = { { + .name = "qspi", + .type = MPC_ROM, + .addr = 0x00200000, + .size = 0x00800000, + }, { + .name = "sram", + .type = MPC_RAM, + .addr = 0x00000000, + .size = 0x00200000, + } +}; + +static const MPCInfo b1_mpc_info[] = { { + .name = "qspi", + .type = MPC_ROM, + .addr = 0x00000000, + .size = 0x02000000, + }, { + .name = "sram", + .type = MPC_RAM, + .addr = 0x0a400000, + .size = 0x00080000, + }, { + .name = "eflash0", + .type = MPC_ROM, + .addr = 0x0a000000, + .size = 0x00200000, + }, { + .name = "eflash1", + .type = MPC_ROM, + .addr = 0x0a200000, + .size = 0x00200000, + }, { + .name = "cryptoisland", + .type = MPC_CRYPTOISLAND, + .addr = 0x0a000000, + .size = 0x00200000, + } +}; + +static MemoryRegion *make_mpc(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * Create an MPC and the RAM or flash behind it. + * MPC 0: eFlash 0 + * MPC 1: eFlash 1 + * MPC 2: SRAM + * MPC 3: QSPI flash + * MPC 4: CryptoIsland + * For now we implement the flash regions as ROM (ie not programmable) + * (with their control interface memory regions being unimplemented + * stubs behind the PPCs). + * The whole CryptoIsland region behind its MPC is an unimplemented stub. + */ + MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms); + TZMPC *mpc = opaque; + int i = mpc - &mms->mpc[0]; + MemoryRegion *downstream; + MemoryRegion *upstream; + UnimplementedDeviceState *uds; + char *mpcname; + const MPCInfo *mpcinfo = mmc->mpc_info; + + mpcname = g_strdup_printf("%s-mpc", mpcinfo[i].name); + + switch (mpcinfo[i].type) { + case MPC_ROM: + downstream = &mms->ram[i]; + memory_region_init_rom(downstream, NULL, mpcinfo[i].name, + mpcinfo[i].size, &error_fatal); + break; + case MPC_RAM: + downstream = &mms->ram[i]; + memory_region_init_ram(downstream, NULL, mpcinfo[i].name, + mpcinfo[i].size, &error_fatal); + break; + case MPC_CRYPTOISLAND: + /* We don't implement the CryptoIsland yet */ + uds = &mms->cryptoisland; + sysbus_init_child_obj(OBJECT(mms), name, uds, + sizeof(UnimplementedDeviceState), + TYPE_UNIMPLEMENTED_DEVICE); + qdev_prop_set_string(DEVICE(uds), "name", mpcinfo[i].name); + qdev_prop_set_uint64(DEVICE(uds), "size", mpcinfo[i].size); + object_property_set_bool(OBJECT(uds), true, "realized", &error_fatal); + downstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(uds), 0); + break; + default: + g_assert_not_reached(); + } + + sysbus_init_child_obj(OBJECT(mms), mpcname, mpc, sizeof(mms->mpc[0]), + TYPE_TZ_MPC); + object_property_set_link(OBJECT(mpc), OBJECT(downstream), + "downstream", &error_fatal); + object_property_set_bool(OBJECT(mpc), true, "realized", &error_fatal); + /* Map the upstream end of the MPC into system memory */ + upstream = sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 1); + memory_region_add_subregion(get_system_memory(), mpcinfo[i].addr, upstream); + /* and connect its interrupt to the SSE-200 */ + qdev_connect_gpio_out_named(DEVICE(mpc), "irq", 0, + qdev_get_gpio_in_named(DEVICE(&mms->sse), + "mpcexp_status", i)); + + g_free(mpcname); + /* Return the register interface MR for our caller to map behind the PPC */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(mpc), 0); +} + +static MemoryRegion *make_rtc(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL031State *rtc = opaque; + + sysbus_init_child_obj(OBJECT(mms), name, rtc, sizeof(mms->rtc), TYPE_PL031); + object_property_set_bool(OBJECT(rtc), true, "realized", &error_fatal); + sysbus_connect_irq(SYS_BUS_DEVICE(rtc), 0, get_sse_irq_in(mms, 39)); + return sysbus_mmio_get_region(SYS_BUS_DEVICE(rtc), 0); +} + +static MemoryRegion *make_uart(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + PL011State *uart = opaque; + int i = uart - &mms->uart[0]; + int irqbase = 7 + i * 6; + SysBusDevice *s; + + sysbus_init_child_obj(OBJECT(mms), name, uart, sizeof(mms->uart[0]), + TYPE_PL011); + qdev_prop_set_chr(DEVICE(uart), "chardev", serial_hd(i)); + object_property_set_bool(OBJECT(uart), true, "realized", &error_fatal); + s = SYS_BUS_DEVICE(uart); + sysbus_connect_irq(s, 0, get_sse_irq_in(mms, irqbase + 5)); /* combined */ + sysbus_connect_irq(s, 1, get_sse_irq_in(mms, irqbase + 0)); /* RX */ + sysbus_connect_irq(s, 2, get_sse_irq_in(mms, irqbase + 1)); /* TX */ + sysbus_connect_irq(s, 3, get_sse_irq_in(mms, irqbase + 2)); /* RT */ + sysbus_connect_irq(s, 4, get_sse_irq_in(mms, irqbase + 3)); /* MS */ + sysbus_connect_irq(s, 5, get_sse_irq_in(mms, irqbase + 4)); /* E */ + return sysbus_mmio_get_region(SYS_BUS_DEVICE(uart), 0); +} + +static MemoryRegion *make_musca_a_devs(MuscaMachineState *mms, void *opaque, + const char *name, hwaddr size) +{ + /* + * Create the container MemoryRegion for all the devices that live + * behind the Musca-A PPC's single port. These devices don't have a PPC + * port each, but we use the PPCPortInfo struct as a convenient way + * to describe them. Note that addresses here are relative to the base + * address of the PPC port region: 0x40100000, and devices appear both + * at the 0x4... NS region and the 0x5... S region. + */ + int i; + MemoryRegion *container = &mms->container; + + const PPCPortInfo devices[] = { + { "uart0", make_uart, &mms->uart[0], 0x1000, 0x1000 }, + { "uart1", make_uart, &mms->uart[1], 0x2000, 0x1000 }, + { "spi", make_unimp_dev, &mms->spi, 0x3000, 0x1000 }, + { "i2c0", make_unimp_dev, &mms->i2c[0], 0x4000, 0x1000 }, + { "i2c1", make_unimp_dev, &mms->i2c[1], 0x5000, 0x1000 }, + { "i2s", make_unimp_dev, &mms->i2s, 0x6000, 0x1000 }, + { "pwm0", make_unimp_dev, &mms->pwm[0], 0x7000, 0x1000 }, + { "rtc", make_rtc, &mms->rtc, 0x8000, 0x1000 }, + { "qspi", make_unimp_dev, &mms->qspi, 0xa000, 0x1000 }, + { "timer", make_unimp_dev, &mms->timer, 0xb000, 0x1000 }, + { "scc", make_unimp_dev, &mms->scc, 0xc000, 0x1000 }, + { "pwm1", make_unimp_dev, &mms->pwm[1], 0xe000, 0x1000 }, + { "pwm2", make_unimp_dev, &mms->pwm[2], 0xf000, 0x1000 }, + { "gpio", make_unimp_dev, &mms->gpio, 0x10000, 0x1000 }, + { "mpc0", make_mpc, &mms->mpc[0], 0x12000, 0x1000 }, + { "mpc1", make_mpc, &mms->mpc[1], 0x13000, 0x1000 }, + }; + + memory_region_init(container, OBJECT(mms), "musca-device-container", size); + + for (i = 0; i < ARRAY_SIZE(devices); i++) { + const PPCPortInfo *pinfo = &devices[i]; + MemoryRegion *mr; + + mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size); + memory_region_add_subregion(container, pinfo->addr, mr); + } + + return &mms->container; +} + +static void musca_init(MachineState *machine) +{ + MuscaMachineState *mms = MUSCA_MACHINE(machine); + MuscaMachineClass *mmc = MUSCA_MACHINE_GET_CLASS(mms); + MachineClass *mc = MACHINE_GET_CLASS(machine); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *ssedev; + DeviceState *dev_splitter; + const PPCInfo *ppcs; + int num_ppcs; + int i; + + assert(mmc->num_irqs <= MUSCA_NUMIRQ_MAX); + assert(mmc->num_mpcs <= MUSCA_MPC_MAX); + + if (strcmp(machine->cpu_type, mc->default_cpu_type) != 0) { + error_report("This board can only be used with CPU %s", + mc->default_cpu_type); + exit(1); + } + + sysbus_init_child_obj(OBJECT(machine), "sse-200", &mms->sse, + sizeof(mms->sse), TYPE_SSE200); + ssedev = DEVICE(&mms->sse); + object_property_set_link(OBJECT(&mms->sse), OBJECT(system_memory), + "memory", &error_fatal); + qdev_prop_set_uint32(ssedev, "EXP_NUMIRQ", mmc->num_irqs); + qdev_prop_set_uint32(ssedev, "init-svtor", mmc->init_svtor); + qdev_prop_set_uint32(ssedev, "SRAM_ADDR_WIDTH", mmc->sram_addr_width); + qdev_prop_set_uint32(ssedev, "MAINCLK", SYSCLK_FRQ); + object_property_set_bool(OBJECT(&mms->sse), true, "realized", + &error_fatal); + + /* + * We need to create splitters to feed the IRQ inputs + * for each CPU in the SSE-200 from each device in the board. + */ + for (i = 0; i < mmc->num_irqs; i++) { + char *name = g_strdup_printf("musca-irq-splitter%d", i); + SplitIRQ *splitter = &mms->cpu_irq_splitter[i]; + + object_initialize_child(OBJECT(machine), name, + splitter, sizeof(*splitter), + TYPE_SPLIT_IRQ, &error_fatal, NULL); + g_free(name); + + object_property_set_int(OBJECT(splitter), 2, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(splitter), true, "realized", + &error_fatal); + qdev_connect_gpio_out(DEVICE(splitter), 0, + qdev_get_gpio_in_named(ssedev, "EXP_IRQ", i)); + qdev_connect_gpio_out(DEVICE(splitter), 1, + qdev_get_gpio_in_named(ssedev, + "EXP_CPU1_IRQ", i)); + } + + /* + * The sec_resp_cfg output from the SSE-200 must be split into multiple + * lines, one for each of the PPCs we create here. + */ + object_initialize(&mms->sec_resp_splitter, sizeof(mms->sec_resp_splitter), + TYPE_SPLIT_IRQ); + object_property_add_child(OBJECT(machine), "sec-resp-splitter", + OBJECT(&mms->sec_resp_splitter), &error_fatal); + object_property_set_int(OBJECT(&mms->sec_resp_splitter), + ARRAY_SIZE(mms->ppc), "num-lines", &error_fatal); + object_property_set_bool(OBJECT(&mms->sec_resp_splitter), true, + "realized", &error_fatal); + dev_splitter = DEVICE(&mms->sec_resp_splitter); + qdev_connect_gpio_out_named(ssedev, "sec_resp_cfg", 0, + qdev_get_gpio_in(dev_splitter, 0)); + + /* + * Most of the devices in the board are behind Peripheral Protection + * Controllers. The required order for initializing things is: + * + initialize the PPC + * + initialize, configure and realize downstream devices + * + connect downstream device MemoryRegions to the PPC + * + realize the PPC + * + map the PPC's MemoryRegions to the places in the address map + * where the downstream devices should appear + * + wire up the PPC's control lines to the SSE object + * + * The PPC mapping differs for the -A and -B1 variants; the -A version + * is much simpler, using only a single port of a single PPC and putting + * all the devices behind that. + */ + const PPCInfo a_ppcs[] = { { + .name = "ahb_ppcexp0", + .ports = { + { "musca-devices", make_musca_a_devs, 0, 0x40100000, 0x100000 }, + }, + }, + }; + + /* + * Devices listed with an 0x4.. address appear in both the NS 0x4.. region + * and the 0x5.. S region. Devices listed with an 0x5.. address appear + * only in the S region. + */ + const PPCInfo b1_ppcs[] = { { + .name = "apb_ppcexp0", + .ports = { + { "eflash0", make_unimp_dev, &mms->eflash[0], + 0x52400000, 0x1000 }, + { "eflash1", make_unimp_dev, &mms->eflash[1], + 0x52500000, 0x1000 }, + { "qspi", make_unimp_dev, &mms->qspi, 0x42800000, 0x100000 }, + { "mpc0", make_mpc, &mms->mpc[0], 0x52000000, 0x1000 }, + { "mpc1", make_mpc, &mms->mpc[1], 0x52100000, 0x1000 }, + { "mpc2", make_mpc, &mms->mpc[2], 0x52200000, 0x1000 }, + { "mpc3", make_mpc, &mms->mpc[3], 0x52300000, 0x1000 }, + { "mhu0", make_unimp_dev, &mms->mhu[0], 0x42600000, 0x100000 }, + { "mhu1", make_unimp_dev, &mms->mhu[1], 0x42700000, 0x100000 }, + { }, /* port 9: unused */ + { }, /* port 10: unused */ + { }, /* port 11: unused */ + { }, /* port 12: unused */ + { }, /* port 13: unused */ + { "mpc4", make_mpc, &mms->mpc[4], 0x52e00000, 0x1000 }, + }, + }, { + .name = "apb_ppcexp1", + .ports = { + { "pwm0", make_unimp_dev, &mms->pwm[0], 0x40101000, 0x1000 }, + { "pwm1", make_unimp_dev, &mms->pwm[1], 0x40102000, 0x1000 }, + { "pwm2", make_unimp_dev, &mms->pwm[2], 0x40103000, 0x1000 }, + { "i2s", make_unimp_dev, &mms->i2s, 0x40104000, 0x1000 }, + { "uart0", make_uart, &mms->uart[0], 0x40105000, 0x1000 }, + { "uart1", make_uart, &mms->uart[1], 0x40106000, 0x1000 }, + { "i2c0", make_unimp_dev, &mms->i2c[0], 0x40108000, 0x1000 }, + { "i2c1", make_unimp_dev, &mms->i2c[1], 0x40109000, 0x1000 }, + { "spi", make_unimp_dev, &mms->spi, 0x4010a000, 0x1000 }, + { "scc", make_unimp_dev, &mms->scc, 0x5010b000, 0x1000 }, + { "timer", make_unimp_dev, &mms->timer, 0x4010c000, 0x1000 }, + { "rtc", make_rtc, &mms->rtc, 0x4010d000, 0x1000 }, + { "pvt", make_unimp_dev, &mms->pvt, 0x4010e000, 0x1000 }, + { "sdio", make_unimp_dev, &mms->sdio, 0x4010f000, 0x1000 }, + }, + }, { + .name = "ahb_ppcexp0", + .ports = { + { }, /* port 0: unused */ + { "gpio", make_unimp_dev, &mms->gpio, 0x41000000, 0x1000 }, + }, + }, + }; + + switch (mmc->type) { + case MUSCA_A: + ppcs = a_ppcs; + num_ppcs = ARRAY_SIZE(a_ppcs); + break; + case MUSCA_B1: + ppcs = b1_ppcs; + num_ppcs = ARRAY_SIZE(b1_ppcs); + break; + default: + g_assert_not_reached(); + } + assert(num_ppcs <= MUSCA_PPC_MAX); + + for (i = 0; i < num_ppcs; i++) { + const PPCInfo *ppcinfo = &ppcs[i]; + TZPPC *ppc = &mms->ppc[i]; + DeviceState *ppcdev; + int port; + char *gpioname; + + sysbus_init_child_obj(OBJECT(machine), ppcinfo->name, ppc, + sizeof(TZPPC), TYPE_TZ_PPC); + ppcdev = DEVICE(ppc); + + for (port = 0; port < TZ_NUM_PORTS; port++) { + const PPCPortInfo *pinfo = &ppcinfo->ports[port]; + MemoryRegion *mr; + char *portname; + + if (!pinfo->devfn) { + continue; + } + + mr = pinfo->devfn(mms, pinfo->opaque, pinfo->name, pinfo->size); + portname = g_strdup_printf("port[%d]", port); + object_property_set_link(OBJECT(ppc), OBJECT(mr), + portname, &error_fatal); + g_free(portname); + } + + object_property_set_bool(OBJECT(ppc), true, "realized", &error_fatal); + + for (port = 0; port < TZ_NUM_PORTS; port++) { + const PPCPortInfo *pinfo = &ppcinfo->ports[port]; + + if (!pinfo->devfn) { + continue; + } + sysbus_mmio_map(SYS_BUS_DEVICE(ppc), port, pinfo->addr); + + gpioname = g_strdup_printf("%s_nonsec", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, port, + qdev_get_gpio_in_named(ppcdev, + "cfg_nonsec", + port)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_ap", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, port, + qdev_get_gpio_in_named(ppcdev, + "cfg_ap", port)); + g_free(gpioname); + } + + gpioname = g_strdup_printf("%s_irq_enable", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, 0, + qdev_get_gpio_in_named(ppcdev, + "irq_enable", 0)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_clear", ppcinfo->name); + qdev_connect_gpio_out_named(ssedev, gpioname, 0, + qdev_get_gpio_in_named(ppcdev, + "irq_clear", 0)); + g_free(gpioname); + gpioname = g_strdup_printf("%s_irq_status", ppcinfo->name); + qdev_connect_gpio_out_named(ppcdev, "irq", 0, + qdev_get_gpio_in_named(ssedev, + gpioname, 0)); + g_free(gpioname); + + qdev_connect_gpio_out(dev_splitter, i, + qdev_get_gpio_in_named(ppcdev, + "cfg_sec_resp", 0)); + } + + armv7m_load_kernel(ARM_CPU(first_cpu), machine->kernel_filename, 0x2000000); +} + +static void musca_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->default_cpus = 2; + mc->min_cpus = mc->default_cpus; + mc->max_cpus = mc->default_cpus; + mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-m33"); + mc->init = musca_init; +} + +static void musca_a_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); + + mc->desc = "ARM Musca-A board (dual Cortex-M33)"; + mmc->type = MUSCA_A; + mmc->init_svtor = 0x10200000; + mmc->sram_addr_width = 15; + mmc->num_irqs = 64; + mmc->mpc_info = a_mpc_info; + mmc->num_mpcs = ARRAY_SIZE(a_mpc_info); +} + +static void musca_b1_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); + + mc->desc = "ARM Musca-B1 board (dual Cortex-M33)"; + mmc->type = MUSCA_B1; + /* + * This matches the DAPlink firmware which boots from QSPI. There + * is also a firmware blob which boots from the eFlash, which + * uses init_svtor = 0x1A000000. QEMU doesn't currently support that, + * though we could in theory expose a machine property on the command + * line to allow the user to request eFlash boot. + */ + mmc->init_svtor = 0x10000000; + mmc->sram_addr_width = 17; + mmc->num_irqs = 96; + mmc->mpc_info = b1_mpc_info; + mmc->num_mpcs = ARRAY_SIZE(b1_mpc_info); +} + +static const TypeInfo musca_info = { + .name = TYPE_MUSCA_MACHINE, + .parent = TYPE_MACHINE, + .abstract = true, + .instance_size = sizeof(MuscaMachineState), + .class_size = sizeof(MuscaMachineClass), + .class_init = musca_class_init, +}; + +static const TypeInfo musca_a_info = { + .name = TYPE_MUSCA_A_MACHINE, + .parent = TYPE_MUSCA_MACHINE, + .class_init = musca_a_class_init, +}; + +static const TypeInfo musca_b1_info = { + .name = TYPE_MUSCA_B1_MACHINE, + .parent = TYPE_MUSCA_MACHINE, + .class_init = musca_b1_class_init, +}; + +static void musca_machine_init(void) +{ + type_register_static(&musca_info); + type_register_static(&musca_a_info); + type_register_static(&musca_b1_info); +} + +type_init(musca_machine_init); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index c807010e83..93ec3c5698 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -15,7 +15,6 @@ #include "cpu.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" -#include "hw/devices.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -1147,14 +1146,13 @@ static const MemoryRegionOps mv88w8618_wlan_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int mv88w8618_wlan_init(SysBusDevice *dev) +static void mv88w8618_wlan_realize(DeviceState *dev, Error **errp) { MemoryRegion *iomem = g_new(MemoryRegion, 1); memory_region_init_io(iomem, OBJECT(dev), &mv88w8618_wlan_ops, NULL, "musicpal-wlan", MP_WLAN_SIZE); - sysbus_init_mmio(dev, iomem); - return 0; + sysbus_init_mmio(SYS_BUS_DEVICE(dev), iomem); } /* GPIO register offsets */ @@ -1637,16 +1635,16 @@ static void musicpal_init(MachineState *machine) * image is smaller than 32 MB. */ #ifdef TARGET_WORDS_BIGENDIAN - pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, + pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, "musicpal.flash", flash_size, - blk, 0x10000, (flash_size + 0xffff) >> 16, + blk, 0x10000, MP_FLASH_SIZE_MAX / flash_size, 2, 0x00BF, 0x236D, 0x0000, 0x0000, 0x5555, 0x2AAA, 1); #else - pflash_cfi02_register(0x100000000ULL-MP_FLASH_SIZE_MAX, NULL, + pflash_cfi02_register(0x100000000ULL - MP_FLASH_SIZE_MAX, "musicpal.flash", flash_size, - blk, 0x10000, (flash_size + 0xffff) >> 16, + blk, 0x10000, MP_FLASH_SIZE_MAX / flash_size, 2, 0x00BF, 0x236D, 0x0000, 0x0000, 0x5555, 0x2AAA, 0); @@ -1693,9 +1691,10 @@ static void musicpal_init(MachineState *machine) } wm8750_dev = i2c_create_slave(i2c, TYPE_WM8750, MP_WM_ADDR); - dev = qdev_create(NULL, "mv88w8618_audio"); + dev = qdev_create(NULL, TYPE_MV88W8618_AUDIO); s = SYS_BUS_DEVICE(dev); - qdev_prop_set_ptr(dev, "wm8750", wm8750_dev); + object_property_set_link(OBJECT(dev), OBJECT(wm8750_dev), + "wm8750", NULL); qdev_init_nofail(dev); sysbus_mmio_map(s, 0, MP_AUDIO_BASE); sysbus_connect_irq(s, 0, pic[MP_AUDIO_IRQ]); @@ -1719,9 +1718,9 @@ DEFINE_MACHINE("musicpal", musicpal_machine_init) static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = mv88w8618_wlan_init; + dc->realize = mv88w8618_wlan_realize; } static const TypeInfo mv88w8618_wlan_info = { diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c new file mode 100644 index 0000000000..3e633d160e --- /dev/null +++ b/hw/arm/nrf51_soc.c @@ -0,0 +1,245 @@ +/* + * Nordic Semiconductor nRF51 SoC + * http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf + * + * Copyright 2018 Joel Stanley + * + * 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 "hw/arm/arm.h" +#include "hw/sysbus.h" +#include "hw/boards.h" +#include "hw/misc/unimp.h" +#include "exec/address-spaces.h" +#include "sysemu/sysemu.h" +#include "qemu/log.h" +#include "cpu.h" + +#include "hw/arm/nrf51.h" +#include "hw/arm/nrf51_soc.h" + +/* + * The size and base is for the NRF51822 part. If other parts + * are supported in the future, add a sub-class of NRF51SoC for + * the specific variants + */ +#define NRF51822_FLASH_PAGES 256 +#define NRF51822_SRAM_PAGES 16 +#define NRF51822_FLASH_SIZE (NRF51822_FLASH_PAGES * NRF51_PAGE_SIZE) +#define NRF51822_SRAM_SIZE (NRF51822_SRAM_PAGES * NRF51_PAGE_SIZE) + +#define BASE_TO_IRQ(base) ((base >> 12) & 0x1F) + +static uint64_t clock_read(void *opaque, hwaddr addr, unsigned int size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u]\n", + __func__, addr, size); + return 1; +} + +static void clock_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n", + __func__, addr, data, size); +} + +static const MemoryRegionOps clock_ops = { + .read = clock_read, + .write = clock_write +}; + + +static void nrf51_soc_realize(DeviceState *dev_soc, Error **errp) +{ + NRF51State *s = NRF51_SOC(dev_soc); + MemoryRegion *mr; + Error *err = NULL; + uint8_t i = 0; + hwaddr base_addr = 0; + + if (!s->board_memory) { + error_setg(errp, "memory property was not set"); + return; + } + + object_property_set_link(OBJECT(&s->cpu), OBJECT(&s->container), "memory", + &err); + if (err) { + error_propagate(errp, err); + return; + } + object_property_set_bool(OBJECT(&s->cpu), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion_overlap(&s->container, 0, s->board_memory, -1); + + memory_region_init_ram(&s->sram, OBJECT(s), "nrf51.sram", s->sram_size, + &err); + if (err) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(&s->container, NRF51_SRAM_BASE, &s->sram); + + /* UART */ + object_property_set_bool(OBJECT(&s->uart), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->uart), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_UART_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->uart), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), + BASE_TO_IRQ(NRF51_UART_BASE))); + + /* RNG */ + object_property_set_bool(OBJECT(&s->rng), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->rng), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_RNG_BASE, mr, 0); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->rng), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), + BASE_TO_IRQ(NRF51_RNG_BASE))); + + /* UICR, FICR, NVMC, FLASH */ + object_property_set_uint(OBJECT(&s->nvm), s->flash_size, "flash-size", + &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_bool(OBJECT(&s->nvm), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_NVMC_BASE, mr, 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 1); + memory_region_add_subregion_overlap(&s->container, NRF51_FICR_BASE, mr, 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 2); + memory_region_add_subregion_overlap(&s->container, NRF51_UICR_BASE, mr, 0); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->nvm), 3); + memory_region_add_subregion_overlap(&s->container, NRF51_FLASH_BASE, mr, 0); + + /* GPIO */ + object_property_set_bool(OBJECT(&s->gpio), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(&s->gpio), 0); + memory_region_add_subregion_overlap(&s->container, NRF51_GPIO_BASE, mr, 0); + + /* Pass all GPIOs to the SOC layer so they are available to the board */ + qdev_pass_gpios(DEVICE(&s->gpio), dev_soc, NULL); + + /* TIMER */ + for (i = 0; i < NRF51_NUM_TIMERS; i++) { + object_property_set_bool(OBJECT(&s->timer[i]), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + base_addr = NRF51_TIMER_BASE + i * NRF51_TIMER_SIZE; + + sysbus_mmio_map(SYS_BUS_DEVICE(&s->timer[i]), 0, base_addr); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->timer[i]), 0, + qdev_get_gpio_in(DEVICE(&s->cpu), + BASE_TO_IRQ(base_addr))); + } + + /* STUB Peripherals */ + memory_region_init_io(&s->clock, NULL, &clock_ops, NULL, + "nrf51_soc.clock", 0x1000); + memory_region_add_subregion_overlap(&s->container, + NRF51_IOMEM_BASE, &s->clock, -1); + + create_unimplemented_device("nrf51_soc.io", NRF51_IOMEM_BASE, + NRF51_IOMEM_SIZE); + create_unimplemented_device("nrf51_soc.private", + NRF51_PRIVATE_BASE, NRF51_PRIVATE_SIZE); +} + +static void nrf51_soc_init(Object *obj) +{ + uint8_t i = 0; + + NRF51State *s = NRF51_SOC(obj); + + memory_region_init(&s->container, obj, "nrf51-container", UINT64_MAX); + + sysbus_init_child_obj(OBJECT(s), "armv6m", OBJECT(&s->cpu), sizeof(s->cpu), + TYPE_ARMV7M); + qdev_prop_set_string(DEVICE(&s->cpu), "cpu-type", + ARM_CPU_TYPE_NAME("cortex-m0")); + qdev_prop_set_uint32(DEVICE(&s->cpu), "num-irq", 32); + + sysbus_init_child_obj(obj, "uart", &s->uart, sizeof(s->uart), + TYPE_NRF51_UART); + object_property_add_alias(obj, "serial0", OBJECT(&s->uart), "chardev", + &error_abort); + + sysbus_init_child_obj(obj, "rng", &s->rng, sizeof(s->rng), + TYPE_NRF51_RNG); + + sysbus_init_child_obj(obj, "nvm", &s->nvm, sizeof(s->nvm), TYPE_NRF51_NVM); + + sysbus_init_child_obj(obj, "gpio", &s->gpio, sizeof(s->gpio), + TYPE_NRF51_GPIO); + + for (i = 0; i < NRF51_NUM_TIMERS; i++) { + sysbus_init_child_obj(obj, "timer[*]", &s->timer[i], + sizeof(s->timer[i]), TYPE_NRF51_TIMER); + + } +} + +static Property nrf51_soc_properties[] = { + DEFINE_PROP_LINK("memory", NRF51State, board_memory, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("sram-size", NRF51State, sram_size, NRF51822_SRAM_SIZE), + DEFINE_PROP_UINT32("flash-size", NRF51State, flash_size, + NRF51822_FLASH_SIZE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void nrf51_soc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = nrf51_soc_realize; + dc->props = nrf51_soc_properties; +} + +static const TypeInfo nrf51_soc_info = { + .name = TYPE_NRF51_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51State), + .instance_init = nrf51_soc_init, + .class_init = nrf51_soc_class_init, +}; + +static void nrf51_soc_types(void) +{ + type_register_static(&nrf51_soc_info); +} +type_init(nrf51_soc_types) diff --git a/hw/arm/omap2.c b/hw/arm/omap2.c index 3c7d1364a9..446223906e 100644 --- a/hw/arm/omap2.c +++ b/hw/arm/omap2.c @@ -273,7 +273,7 @@ static void omap_eac_format_update(struct omap_eac_s *s) * does I2S specify it? */ /* All register writes are 16 bits so we we store 16-bit samples * in the buffers regardless of AGCFR[B8_16] value. */ - fmt.fmt = AUD_FMT_U16; + fmt.fmt = AUDIO_FORMAT_U16; s->codec.in_voice = AUD_open_in(&s->codec.card, s->codec.in_voice, "eac.codec.in", s, omap_eac_in_cb, &fmt); @@ -799,7 +799,7 @@ static struct omap_sti_s *omap_sti_init(struct omap_target_agent_s *ta, s->irq = irq; omap_sti_reset(s); - qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null"), + qemu_chr_fe_init(&s->chr, chr ?: qemu_chr_new("null", "null", NULL), &error_abort); memory_region_init_io(&s->iomem, NULL, &omap_sti_ops, s, "omap.sti", diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 84550f0236..95a4fe7e7f 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -152,11 +152,10 @@ static void sx1_init(MachineState *machine, const int version) #endif if ((dinfo = drive_get(IF_PFLASH, 0, fl_idx)) != NULL) { - if (!pflash_cfi01_register(OMAP_CS0_BASE, NULL, + if (!pflash_cfi01_register(OMAP_CS0_BASE, "omap_sx1.flash0-1", flash_size, blk_by_legacy_dinfo(dinfo), - sector_size, flash_size / sector_size, - 4, 0, 0, 0, 0, be)) { + sector_size, 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory %d.\n", fl_idx); } @@ -176,11 +175,10 @@ static void sx1_init(MachineState *machine, const int version) memory_region_add_subregion(address_space, OMAP_CS1_BASE + flash1_size, &cs[1]); - if (!pflash_cfi01_register(OMAP_CS1_BASE, NULL, + if (!pflash_cfi01_register(OMAP_CS1_BASE, "omap_sx1.flash1-1", flash1_size, blk_by_legacy_dinfo(dinfo), - sector_size, flash1_size / sector_size, - 4, 0, 0, 0, 0, be)) { + sector_size, 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory %d.\n", fl_idx); } diff --git a/hw/arm/pxa2xx.c b/hw/arm/pxa2xx.c index b67b0cefb6..3d7c88910e 100644 --- a/hw/arm/pxa2xx.c +++ b/hw/arm/pxa2xx.c @@ -409,7 +409,7 @@ static uint64_t pxa2xx_mm_read(void *opaque, hwaddr addr, case MDCNFG ... SA1110: if ((addr & 3) == 0) return s->mm_regs[addr >> 2]; - + /* fall through */ default: printf("%s: Bad register " REG_FMT "\n", __func__, addr); break; @@ -1286,7 +1286,7 @@ static int pxa2xx_i2c_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int pxa2xx_i2c_rx(I2CSlave *i2c) +static uint8_t pxa2xx_i2c_rx(I2CSlave *i2c) { PXA2xxI2CSlaveState *slave = PXA2XX_I2C_SLAVE(i2c); PXA2xxI2CState *s = slave->host; diff --git a/hw/arm/realview.c b/hw/arm/realview.c index cd585d9469..242f5a87b6 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -201,7 +201,13 @@ static void realview_init(MachineState *machine, pl011_create(0x1000c000, pic[15], serial_hd(3)); /* DMA controller is optional, apparently. */ - sysbus_create_simple("pl081", 0x10030000, pic[24]); + dev = qdev_create(NULL, "pl081"); + object_property_set_link(OBJECT(dev), OBJECT(sysmem), "downstream", + &error_fatal); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, 0x10030000); + sysbus_connect_irq(busdev, 0, pic[24]); sysbus_create_simple("sp804", 0x10011000, pic[4]); sysbus_create_simple("sp804", 0x10012000, pic[5]); @@ -251,7 +257,8 @@ static void realview_init(MachineState *machine, } n = drive_get_max_bus(IF_SCSI); while (n >= 0) { - lsi53c895a_create(pci_bus); + dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); + lsi53c8xx_handle_legacy_cmdline(dev); n--; } } diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 55c75d65d2..bbf4b8721a 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -311,6 +311,7 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) SMMUState *s = opaque; SMMUPciBus *sbus = g_hash_table_lookup(s->smmu_pcibus_by_busptr, bus); SMMUDevice *sdev; + static unsigned int index; if (!sbus) { sbus = g_malloc0(sizeof(SMMUPciBus) + @@ -321,9 +322,8 @@ static AddressSpace *smmu_find_add_as(PCIBus *bus, void *opaque, int devfn) sdev = sbus->pbdev[devfn]; if (!sdev) { - char *name = g_strdup_printf("%s-%d-%d", - s->mrtypename, - pci_bus_num(bus), devfn); + char *name = g_strdup_printf("%s-%d-%d", s->mrtypename, devfn, index++); + sdev = sbus->pbdev[devfn] = g_new0(SMMUDevice, 1); sdev->smmu = s; diff --git a/hw/arm/smmuv3-internal.h b/hw/arm/smmuv3-internal.h index bab25d640e..19540f8f41 100644 --- a/hw/arm/smmuv3-internal.h +++ b/hw/arm/smmuv3-internal.h @@ -442,17 +442,17 @@ typedef struct SMMUEventInfo { #define EVT_Q_OVERFLOW (1 << 31) -#define EVT_SET_TYPE(x, v) deposit32((x)->word[0], 0 , 8 , v) -#define EVT_SET_SSV(x, v) deposit32((x)->word[0], 11, 1 , v) -#define EVT_SET_SSID(x, v) deposit32((x)->word[0], 12, 20, v) -#define EVT_SET_SID(x, v) ((x)->word[1] = v) -#define EVT_SET_STAG(x, v) deposit32((x)->word[2], 0 , 16, v) -#define EVT_SET_STALL(x, v) deposit32((x)->word[2], 31, 1 , v) -#define EVT_SET_PNU(x, v) deposit32((x)->word[3], 1 , 1 , v) -#define EVT_SET_IND(x, v) deposit32((x)->word[3], 2 , 1 , v) -#define EVT_SET_RNW(x, v) deposit32((x)->word[3], 3 , 1 , v) -#define EVT_SET_S2(x, v) deposit32((x)->word[3], 7 , 1 , v) -#define EVT_SET_CLASS(x, v) deposit32((x)->word[3], 8 , 2 , v) +#define EVT_SET_TYPE(x, v) ((x)->word[0] = deposit32((x)->word[0], 0 , 8 , v)) +#define EVT_SET_SSV(x, v) ((x)->word[0] = deposit32((x)->word[0], 11, 1 , v)) +#define EVT_SET_SSID(x, v) ((x)->word[0] = deposit32((x)->word[0], 12, 20, v)) +#define EVT_SET_SID(x, v) ((x)->word[1] = v) +#define EVT_SET_STAG(x, v) ((x)->word[2] = deposit32((x)->word[2], 0 , 16, v)) +#define EVT_SET_STALL(x, v) ((x)->word[2] = deposit32((x)->word[2], 31, 1 , v)) +#define EVT_SET_PNU(x, v) ((x)->word[3] = deposit32((x)->word[3], 1 , 1 , v)) +#define EVT_SET_IND(x, v) ((x)->word[3] = deposit32((x)->word[3], 2 , 1 , v)) +#define EVT_SET_RNW(x, v) ((x)->word[3] = deposit32((x)->word[3], 3 , 1 , v)) +#define EVT_SET_S2(x, v) ((x)->word[3] = deposit32((x)->word[3], 7 , 1 , v)) +#define EVT_SET_CLASS(x, v) ((x)->word[3] = deposit32((x)->word[3], 8 , 2 , v)) #define EVT_SET_ADDR(x, addr) \ do { \ (x)->word[5] = (uint32_t)(addr >> 32); \ @@ -460,8 +460,8 @@ typedef struct SMMUEventInfo { } while (0) #define EVT_SET_ADDR2(x, addr) \ do { \ - deposit32((x)->word[7], 3, 29, addr >> 16); \ - deposit32((x)->word[7], 0, 16, addr & 0xffff);\ + (x)->word[7] = deposit32((x)->word[7], 3, 29, addr >> 16); \ + (x)->word[7] = deposit32((x)->word[7], 0, 16, addr & 0xffff);\ } while (0) void smmuv3_record_event(SMMUv3State *s, SMMUEventInfo *event); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index bb6a24e9b8..8c4e99fecc 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -136,7 +136,7 @@ static MemTxResult smmuv3_write_eventq(SMMUv3State *s, Evt *evt) return r; } - if (smmuv3_q_empty(q)) { + if (!smmuv3_q_empty(q)) { smmuv3_trigger_irq(s, SMMU_IRQ_EVTQ, 0); } return MEMTX_OK; diff --git a/hw/arm/spitz.c b/hw/arm/spitz.c index c4bc3deedf..22f5958b9d 100644 --- a/hw/arm/spitz.c +++ b/hw/arm/spitz.c @@ -21,7 +21,6 @@ #include "hw/ssi/ssi.h" #include "hw/block/flash.h" #include "qemu/timer.h" -#include "hw/devices.h" #include "hw/arm/sharpsl.h" #include "ui/console.h" #include "hw/audio/wm8750.h" diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index dc521b4a5a..05f86749f4 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -22,6 +22,7 @@ #include "sysemu/sysemu.h" #include "hw/arm/armv7m.h" #include "hw/char/pl011.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" #include "hw/misc/unimp.h" #include "cpu.h" @@ -131,7 +132,7 @@ static void gptm_tick(void *opaque) s->state |= 1; if ((s->control & 0x20)) { /* Output trigger. */ - qemu_irq_pulse(s->trigger); + qemu_irq_pulse(s->trigger); } if (s->mode[0] & 1) { /* One-shot. */ @@ -811,7 +812,7 @@ static void stellaris_i2c_write(void *opaque, hwaddr offset, /* TODO: Handle errors. */ if (s->msa & 1) { /* Recv */ - s->mdr = i2c_recv(s->bus) & 0xff; + s->mdr = i2c_recv(s->bus); } else { /* Send */ i2c_send(s->bus, s->mdr); @@ -1243,7 +1244,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) * Stellaris LM3S6965 Microcontroller Data Sheet (rev I) * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf * - * 40000000 wdtimer (unimplemented) + * 40000000 wdtimer * 40002000 i2c (unimplemented) * 40004000 GPIO * 40005000 GPIO @@ -1304,6 +1305,7 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) nvic = qdev_create(NULL, TYPE_ARMV7M); qdev_prop_set_uint32(nvic, "num-irq", NUM_IRQ_LINES); qdev_prop_set_string(nvic, "cpu-type", ms->cpu_type); + qdev_prop_set_bit(nvic, "enable-bitband", true); object_property_set_link(OBJECT(nvic), OBJECT(get_system_memory()), "memory", &error_abort); /* This will exit with an error if the user passed us a bad cpu_type */ @@ -1337,6 +1339,24 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) stellaris_sys_init(0x400fe000, qdev_get_gpio_in(nvic, 28), board, nd_table[0].macaddr.a); + + if (board->dc1 & (1 << 3)) { /* watchdog present */ + dev = qdev_create(NULL, TYPE_LUMINARY_WATCHDOG); + + /* system_clock_scale is valid now */ + uint32_t mainclk = NANOSECONDS_PER_SECOND / system_clock_scale; + qdev_prop_set_uint32(dev, "wdogclk-frq", mainclk); + + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), + 0, + 0x40000000u); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), + 0, + qdev_get_gpio_in(nvic, 18)); + } + + for (i = 0; i < 7; i++) { if (board->dc4 & (1 << i)) { gpio_dev[i] = sysbus_create_simple("pl061_luminary", gpio_addr[i], @@ -1430,7 +1450,6 @@ static void stellaris_init(MachineState *ms, stellaris_board_info *board) /* Add dummy regions for the devices we don't implement yet, * so guest accesses don't cause unlogged crashes. */ - create_unimplemented_device("wdtimer", 0x40000000, 0x1000); create_unimplemented_device("i2c-0", 0x40002000, 0x1000); create_unimplemented_device("i2c-2", 0x40021000, 0x1000); create_unimplemented_device("PWM", 0x40028000, 0x1000); diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index c486d06a8b..980e5af13c 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -109,6 +109,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) armv7m = DEVICE(&s->armv7m); qdev_prop_set_uint32(armv7m, "num-irq", 96); qdev_prop_set_string(armv7m, "cpu-type", s->cpu_type); + qdev_prop_set_bit(armv7m, "enable-bitband", true); object_property_set_link(OBJECT(&s->armv7m), OBJECT(get_system_memory()), "memory", &error_abort); object_property_set_bool(OBJECT(&s->armv7m), true, "realized", &err); diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index ec2627374d..644a9c45b4 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -587,12 +587,12 @@ static void strongarm_gpio_write(void *opaque, hwaddr offset, switch (offset) { case GPDR: /* GPIO Pin-Direction registers */ - s->dir = value; + s->dir = value & 0x0fffffff; strongarm_gpio_handler_update(s); break; case GPSR: /* GPIO Pin-Output Set registers */ - s->olevel |= value; + s->olevel |= value & 0x0fffffff; strongarm_gpio_handler_update(s); break; diff --git a/hw/arm/sysbus-fdt.c b/hw/arm/sysbus-fdt.c index 43d6a7bb48..ad698d4832 100644 --- a/hw/arm/sysbus-fdt.c +++ b/hw/arm/sysbus-fdt.c @@ -50,11 +50,13 @@ typedef struct PlatformBusFDTData { PlatformBusDevice *pbus; } PlatformBusFDTData; -/* struct that associates a device type name and a node creation function */ -typedef struct NodeCreationPair { +/* struct that allows to match a device and create its FDT node */ +typedef struct BindingEntry { const char *typename; - int (*add_fdt_node_fn)(SysBusDevice *sbdev, void *opaque); -} NodeCreationPair; + const char *compat; + int (*add_fn)(SysBusDevice *sbdev, void *opaque); + bool (*match_fn)(SysBusDevice *sbdev, const struct BindingEntry *combo); +} BindingEntry; /* helpers */ @@ -413,6 +415,27 @@ static int add_amd_xgbe_fdt_node(SysBusDevice *sbdev, void *opaque) return 0; } +/* DT compatible matching */ +static bool vfio_platform_match(SysBusDevice *sbdev, + const BindingEntry *entry) +{ + VFIOPlatformDevice *vdev = VFIO_PLATFORM_DEVICE(sbdev); + const char *compat; + unsigned int n; + + for (n = vdev->num_compat, compat = vdev->compat; n > 0; + n--, compat += strlen(compat) + 1) { + if (!strcmp(entry->compat, compat)) { + return true; + } + } + + return false; +} + +#define VFIO_PLATFORM_BINDING(compat, add_fn) \ + {TYPE_VFIO_PLATFORM, (compat), (add_fn), vfio_platform_match} + #endif /* CONFIG_LINUX */ static int no_fdt_node(SysBusDevice *sbdev, void *opaque) @@ -420,14 +443,23 @@ static int no_fdt_node(SysBusDevice *sbdev, void *opaque) return 0; } -/* list of supported dynamic sysbus devices */ -static const NodeCreationPair add_fdt_node_functions[] = { +/* Device type based matching */ +static bool type_match(SysBusDevice *sbdev, const BindingEntry *entry) +{ + return !strcmp(object_get_typename(OBJECT(sbdev)), entry->typename); +} + +#define TYPE_BINDING(type, add_fn) {(type), NULL, (add_fn), NULL} + +/* list of supported dynamic sysbus bindings */ +static const BindingEntry bindings[] = { #ifdef CONFIG_LINUX - {TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node}, - {TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node}, + TYPE_BINDING(TYPE_VFIO_CALXEDA_XGMAC, add_calxeda_midway_xgmac_fdt_node), + TYPE_BINDING(TYPE_VFIO_AMD_XGBE, add_amd_xgbe_fdt_node), + VFIO_PLATFORM_BINDING("amd,xgbe-seattle-v1a", add_amd_xgbe_fdt_node), #endif - {TYPE_RAMFB_DEVICE, no_fdt_node}, - {"", NULL}, /* last element */ + TYPE_BINDING(TYPE_RAMFB_DEVICE, no_fdt_node), + TYPE_BINDING("", NULL), /* last element */ }; /* Generic Code */ @@ -446,12 +478,15 @@ static void add_fdt_node(SysBusDevice *sbdev, void *opaque) { int i, ret; - for (i = 0; i < ARRAY_SIZE(add_fdt_node_functions); i++) { - if (!strcmp(object_get_typename(OBJECT(sbdev)), - add_fdt_node_functions[i].typename)) { - ret = add_fdt_node_functions[i].add_fdt_node_fn(sbdev, opaque); - assert(!ret); - return; + for (i = 0; i < ARRAY_SIZE(bindings); i++) { + const BindingEntry *iter = &bindings[i]; + + if (type_match(sbdev, iter)) { + if (!iter->match_fn || iter->match_fn(sbdev, iter)) { + ret = iter->add_fn(sbdev, opaque); + assert(!ret); + return; + } } } error_report("Device %s can not be dynamically instantiated", diff --git a/hw/arm/tosa.c b/hw/arm/tosa.c index 7a925fa5e6..eef9d427e7 100644 --- a/hw/arm/tosa.c +++ b/hw/arm/tosa.c @@ -197,10 +197,10 @@ static int tosa_dac_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int tosa_dac_recv(I2CSlave *s) +static uint8_t tosa_dac_recv(I2CSlave *s) { printf("%s: recv not supported!!!\n", __func__); - return -1; + return 0xff; } static void tosa_tg_init(PXA2xxState *cpu) diff --git a/hw/arm/trace-events b/hw/arm/trace-events index 27b11d655d..0acedcedc6 100644 --- a/hw/arm/trace-events +++ b/hw/arm/trace-events @@ -1,25 +1,21 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/arm/virt-acpi-build.c +# virt-acpi-build.c virt_acpi_setup(void) "No fw cfg or ACPI disabled. Bailing out." -# hw/arm/smmu-common.c +# smmu-common.c smmu_add_mr(const char *name) "%s" -smmu_page_walk(int stage, uint64_t baseaddr, int first_level, uint64_t start, uint64_t end) "stage=%d, baseaddr=0x%"PRIx64", first level=%d, start=0x%"PRIx64", end=0x%"PRIx64 -smmu_lookup_table(int level, uint64_t baseaddr, int granule_sz, uint64_t start, uint64_t end, int flags, uint64_t subpage_size) "level=%d baseaddr=0x%"PRIx64" granule=%d, start=0x%"PRIx64" end=0x%"PRIx64" flags=%d subpage_size=0x%"PRIx64 smmu_ptw_level(int level, uint64_t iova, size_t subpage_size, uint64_t baseaddr, uint32_t offset, uint64_t pte) "level=%d iova=0x%"PRIx64" subpage_sz=0x%zx baseaddr=0x%"PRIx64" offset=%d => pte=0x%"PRIx64 smmu_ptw_invalid_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint32_t offset, uint64_t pte) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" offset=%d pte=0x%"PRIx64 smmu_ptw_page_pte(int stage, int level, uint64_t iova, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t address) "stage=%d level=%d iova=0x%"PRIx64" base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" page address = 0x%"PRIx64 smmu_ptw_block_pte(int stage, int level, uint64_t baseaddr, uint64_t pteaddr, uint64_t pte, uint64_t iova, uint64_t gpa, int bsize_mb) "stage=%d level=%d base@=0x%"PRIx64" pte@=0x%"PRIx64" pte=0x%"PRIx64" iova=0x%"PRIx64" block address = 0x%"PRIx64" block size = %d MiB" smmu_get_pte(uint64_t baseaddr, int index, uint64_t pteaddr, uint64_t pte) "baseaddr=0x%"PRIx64" index=0x%x, pteaddr=0x%"PRIx64", pte=0x%"PRIx64 -smmu_iotlb_cache_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" -smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" smmu_iotlb_inv_all(void) "IOTLB invalidate all" smmu_iotlb_inv_asid(uint16_t asid) "IOTLB invalidate asid=%d" smmu_iotlb_inv_iova(uint16_t asid, uint64_t addr) "IOTLB invalidate asid=%d addr=0x%"PRIx64 smmu_inv_notifiers_mr(const char *name) "iommu mr=%s" -#hw/arm/smmuv3.c +# smmuv3.c smmuv3_read_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" smmuv3_trigger_irq(int irq) "irq=%d" smmuv3_write_gerror(uint32_t toggled, uint32_t gerror) "toggled=0x%x, new GERROR=0x%x" @@ -29,12 +25,7 @@ smmuv3_cmdq_consume(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t con smmuv3_cmdq_opcode(const char *opcode) "<--- %s" smmuv3_cmdq_consume_out(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "prod:%d, cons:%d, prod_wrap:%d, cons_wrap:%d " smmuv3_cmdq_consume_error(const char *cmd_name, uint8_t cmd_error) "Error on %s command execution: %d" -smmuv3_update(bool is_empty, uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "q empty:%d prod:%d cons:%d p.wrap:%d p.cons:%d" -smmuv3_update_check_cmd(int error) "cmdq not enabled or error :0x%x" smmuv3_write_mmio(uint64_t addr, uint64_t val, unsigned size, uint32_t r) "addr: 0x%"PRIx64" val:0x%"PRIx64" size: 0x%x(%d)" -smmuv3_write_mmio_idr(uint64_t addr, uint64_t val) "write to RO/Unimpl reg 0x%"PRIx64" val64:0x%"PRIx64 -smmuv3_write_mmio_evtq_cons_bef_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "Before clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" -smmuv3_write_mmio_evtq_cons_after_clear(uint32_t prod, uint32_t cons, uint8_t prod_wrap, uint8_t cons_wrap) "after clearing interrupt prod:0x%x cons:0x%x prod.w:%d cons.w:%d" smmuv3_record_event(const char *type, uint32_t sid) "%s sid=%d" smmuv3_find_ste(uint16_t sid, uint32_t features, uint16_t sid_split) "SID:0x%x features:0x%x, sid_split:0x%x" smmuv3_find_ste_2lvl(uint64_t strtab_base, uint64_t l1ptr, int l1_ste_offset, uint64_t l2ptr, int l2_ste_offset, int max_l2_ste) "strtab_base:0x%"PRIx64" l1ptr:0x%"PRIx64" l1_off:0x%x, l2ptr:0x%"PRIx64" l2_off:0x%x max_l2_ste:%d" @@ -55,6 +46,8 @@ smmuv3_cmdq_tlbi_nh_va(int vmid, int asid, uint64_t addr, bool leaf) "vmid =%d a smmuv3_cmdq_tlbi_nh_vaa(int vmid, uint64_t addr) "vmid =%d addr=0x%"PRIx64 smmuv3_cmdq_tlbi_nh(void) "" smmuv3_cmdq_tlbi_nh_asid(uint16_t asid) "asid=%d" +smmu_iotlb_cache_hit(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache HIT asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" +smmu_iotlb_cache_miss(uint16_t asid, uint64_t addr, uint32_t hit, uint32_t miss, uint32_t p) "IOTLB cache MISS asid=%d addr=0x%"PRIx64" hit=%d miss=%d hit rate=%d" smmuv3_config_cache_inv(uint32_t sid) "Config cache INV for sid %d" smmuv3_notify_flag_add(const char *iommu) "ADD SMMUNotifier node for iommu mr=%s" smmuv3_notify_flag_del(const char *iommu) "DEL SMMUNotifier node for iommu mr=%s" diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index a5a06b6d40..d67181810a 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -278,7 +278,8 @@ static void versatile_init(MachineState *machine, int board_id) } n = drive_get_max_bus(IF_SCSI); while (n >= 0) { - lsi53c895a_create(pci_bus); + dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); + lsi53c8xx_handle_legacy_cmdline(dev); n--; } @@ -287,7 +288,14 @@ static void versatile_init(MachineState *machine, int board_id) pl011_create(0x101f3000, pic[14], serial_hd(2)); pl011_create(0x10009000, sic[6], serial_hd(3)); - sysbus_create_simple("pl080", 0x10130000, pic[17]); + dev = qdev_create(NULL, "pl080"); + object_property_set_link(OBJECT(dev), OBJECT(sysmem), "downstream", + &error_fatal); + qdev_init_nofail(dev); + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, 0x10130000); + sysbus_connect_irq(busdev, 0, pic[17]); + sysbus_create_simple("sp804", 0x101e2000, pic[4]); sysbus_create_simple("sp804", 0x101e3000, pic[5]); @@ -357,11 +365,10 @@ static void versatile_init(MachineState *machine, int board_id) /* 0x34000000 NOR Flash */ dinfo = drive_get(IF_PFLASH, 0, 0); - if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, NULL, "versatile.flash", + if (!pflash_cfi01_register(VERSATILE_FLASH_ADDR, "versatile.flash", VERSATILE_FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, VERSATILE_FLASH_SECT_SIZE, - VERSATILE_FLASH_SIZE / VERSATILE_FLASH_SECT_SIZE, 4, 0x0089, 0x0018, 0x0000, 0x0, 0)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); } diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 5bfe2e4348..f07134c424 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -172,6 +172,7 @@ typedef struct { typedef struct { MachineState parent; bool secure; + bool virt; } VexpressMachineState; #define TYPE_VEXPRESS_MACHINE "vexpress" @@ -203,7 +204,7 @@ struct VEDBoardInfo { }; static void init_cpus(const char *cpu_type, const char *privdev, - hwaddr periphbase, qemu_irq *pic, bool secure) + hwaddr periphbase, qemu_irq *pic, bool secure, bool virt) { DeviceState *dev; SysBusDevice *busdev; @@ -216,6 +217,11 @@ static void init_cpus(const char *cpu_type, const char *privdev, if (!secure) { object_property_set_bool(cpuobj, false, "has_el3", NULL); } + if (!virt) { + if (object_property_find(cpuobj, "has_el2", NULL)) { + object_property_set_bool(cpuobj, false, "has_el2", NULL); + } + } if (object_property_find(cpuobj, "reset-cbar", NULL)) { object_property_set_int(cpuobj, periphbase, @@ -251,6 +257,10 @@ static void init_cpus(const char *cpu_type, const char *privdev, sysbus_connect_irq(busdev, n, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(busdev, n + smp_cpus, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(busdev, n + 2 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(busdev, n + 3 * smp_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } } @@ -285,7 +295,8 @@ static void a9_daughterboard_init(const VexpressMachineState *vms, memory_region_add_subregion(sysmem, 0x60000000, ram); /* 0x1e000000 A9MPCore (SCU) private memory region */ - init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, vms->secure); + init_cpus(cpu_type, TYPE_A9MPCORE_PRIV, 0x1e000000, pic, + vms->secure, vms->virt); /* Daughterboard peripherals : 0x10020000 .. 0x20000000 */ @@ -366,7 +377,8 @@ static void a15_daughterboard_init(const VexpressMachineState *vms, memory_region_add_subregion(sysmem, 0x80000000, ram); /* 0x2c000000 A15MPCore private memory region (GIC) */ - init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure); + init_cpus(cpu_type, TYPE_A15MPCORE_PRIV, 0x2c000000, pic, vms->secure, + vms->virt); /* A15 daughterboard peripherals: */ @@ -500,10 +512,10 @@ static void vexpress_modify_dtb(const struct arm_boot_info *info, void *fdt) /* Open code a private version of pflash registration since we * need to set non-default device width for VExpress platform. */ -static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, - DriveInfo *di) +static PFlashCFI01 *ve_pflash_cfi01_register(hwaddr base, const char *name, + DriveInfo *di) { - DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); if (di) { qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(di), @@ -524,7 +536,7 @@ static pflash_t *ve_pflash_cfi01_register(hwaddr base, const char *name, qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return OBJECT_CHECK(pflash_t, (dev), "cfi.pflash01"); + return PFLASH_CFI01(dev); } static void vexpress_common_init(MachineState *machine) @@ -536,7 +548,7 @@ static void vexpress_common_init(MachineState *machine) qemu_irq pic[64]; uint32_t sys_id; DriveInfo *dinfo; - pflash_t *pflash0; + PFlashCFI01 *pflash0; I2CBus *i2c; ram_addr_t vram_size, sram_size; MemoryRegion *sysmem = get_system_memory(); @@ -701,8 +713,8 @@ static void vexpress_common_init(MachineState *machine) daughterboard->bootinfo.smp_bootreg_addr = map[VE_SYSREGS] + 0x30; daughterboard->bootinfo.gic_cpu_if_addr = daughterboard->gic_cpu_if_addr; daughterboard->bootinfo.modify_dtb = vexpress_modify_dtb; - /* Indicate that when booting Linux we should be in secure state */ - daughterboard->bootinfo.secure_boot = true; + /* When booting Linux we should be in secure state if the CPU has one. */ + daughterboard->bootinfo.secure_boot = vms->secure; arm_load_kernel(ARM_CPU(first_cpu), &daughterboard->bootinfo); } @@ -720,6 +732,20 @@ static void vexpress_set_secure(Object *obj, bool value, Error **errp) vms->secure = value; } +static bool vexpress_get_virt(Object *obj, Error **errp) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + return vms->virt; +} + +static void vexpress_set_virt(Object *obj, bool value, Error **errp) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + vms->virt = value; +} + static void vexpress_instance_init(Object *obj) { VexpressMachineState *vms = VEXPRESS_MACHINE(obj); @@ -734,6 +760,32 @@ static void vexpress_instance_init(Object *obj) NULL); } +static void vexpress_a15_instance_init(Object *obj) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + /* + * For the vexpress-a15, EL2 is by default enabled if EL3 is, + * but can also be specifically set to on or off. + */ + vms->virt = true; + object_property_add_bool(obj, "virtualization", vexpress_get_virt, + vexpress_set_virt, NULL); + object_property_set_description(obj, "virtualization", + "Set on/off to enable/disable the ARM " + "Virtualization Extensions " + "(defaults to same as 'secure')", + NULL); +} + +static void vexpress_a9_instance_init(Object *obj) +{ + VexpressMachineState *vms = VEXPRESS_MACHINE(obj); + + /* The A9 doesn't have the virt extensions */ + vms->virt = false; +} + static void vexpress_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -780,12 +832,14 @@ static const TypeInfo vexpress_a9_info = { .name = TYPE_VEXPRESS_A9_MACHINE, .parent = TYPE_VEXPRESS_MACHINE, .class_init = vexpress_a9_class_init, + .instance_init = vexpress_a9_instance_init, }; static const TypeInfo vexpress_a15_info = { .name = TYPE_VEXPRESS_A15_MACHINE, .parent = TYPE_VEXPRESS_MACHINE, .class_init = vexpress_a15_class_init, + .instance_init = vexpress_a15_instance_init, }; static void vexpress_machine_init(void) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 6ea47e2588..bf9c0bc2f4 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -229,8 +229,8 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, 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; + hwaddr base_mmio_high = memmap[VIRT_HIGH_PCIE_MMIO].base; + hwaddr size_mmio_high = memmap[VIRT_HIGH_PCIE_MMIO].size; aml_append(rbuf, aml_qword_memory(AML_POS_DECODE, AML_MIN_FIXED, AML_MAX_FIXED, @@ -366,36 +366,6 @@ static void acpi_dsdt_add_power_button(Aml *scope) aml_append(scope, dev); } -/* RSDP */ -static GArray * -build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned xsdt_tbl_offset) -{ - AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); - unsigned xsdt_pa_size = sizeof(rsdp->xsdt_physical_address); - unsigned xsdt_pa_offset = - (char *)&rsdp->xsdt_physical_address - rsdp_table->data; - - bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16, - true /* fseg memory */); - - memcpy(&rsdp->signature, "RSD PTR ", sizeof(rsdp->signature)); - memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, sizeof(rsdp->oem_id)); - rsdp->length = cpu_to_le32(sizeof(*rsdp)); - rsdp->revision = 0x02; - - /* Address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, - ACPI_BUILD_RSDP_FILE, xsdt_pa_offset, xsdt_pa_size, - ACPI_BUILD_TABLE_FILE, xsdt_tbl_offset); - - /* Checksum to be filled by Guest linker */ - bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - (char *)rsdp - rsdp_table->data, sizeof *rsdp, - (char *)&rsdp->checksum - rsdp_table->data); - - return rsdp_table; -} - static void build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { @@ -435,7 +405,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) its->identifiers[0] = 0; /* MADT translation_id */ if (vms->iommu == VIRT_IOMMU_SMMUV3) { - int irq = vms->irqmap[VIRT_SMMU]; + int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; /* SMMUv3 node */ smmu_offset = iort_node_offset + node_size; @@ -448,6 +418,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) smmu->mapping_count = cpu_to_le32(1); smmu->mapping_offset = cpu_to_le32(sizeof(*smmu)); smmu->base_address = cpu_to_le64(vms->memmap[VIRT_SMMU].base); + smmu->flags = cpu_to_le32(ACPI_IORT_SMMU_V3_COHACC_OVERRIDE); smmu->event_gsiv = cpu_to_le32(irq); smmu->pri_gsiv = cpu_to_le32(irq + 1); smmu->gerr_gsiv = cpu_to_le32(irq + 2); @@ -562,10 +533,12 @@ build_srat(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) mem_base = vms->memmap[VIRT_MEM].base; for (i = 0; i < nb_numa_nodes; ++i) { - numamem = acpi_data_push(table_data, sizeof(*numamem)); - build_srat_memory(numamem, mem_base, numa_info[i].node_mem, i, - MEM_AFFINITY_ENABLED); - mem_base += numa_info[i].node_mem; + if (numa_info[i].node_mem > 0) { + numamem = acpi_data_push(table_data, sizeof(*numamem)); + build_srat_memory(numamem, mem_base, numa_info[i].node_mem, i, + MEM_AFFINITY_ENABLED); + mem_base += numa_info[i].node_mem; + } } build_header(linker, table_data, (void *)(table_data->data + srat_start), @@ -587,8 +560,8 @@ build_mcfg(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Only a single allocation so no need to play with segments */ mcfg->allocation[0].pci_segment = cpu_to_le16(0); mcfg->allocation[0].start_bus_number = 0; - mcfg->allocation[0].end_bus_number = (memmap[ecam_id].size - / PCIE_MMCFG_SIZE_MIN) - 1; + mcfg->allocation[0].end_bus_number = + PCIE_MMCFG_BUS(memmap[ecam_id].size - 1); build_header(linker, table_data, (void *)(table_data->data + mcfg_start), "MCFG", table_data->len - mcfg_start, 1, NULL, NULL); @@ -659,6 +632,8 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) gicc->length = sizeof(*gicc); if (vms->gic_version == 2) { gicc->base_address = cpu_to_le64(memmap[VIRT_GIC_CPU].base); + gicc->gich_base_address = cpu_to_le64(memmap[VIRT_GIC_HYP].base); + gicc->gicv_base_address = cpu_to_le64(memmap[VIRT_GIC_VCPU].base); } gicc->cpu_interface_number = cpu_to_le32(i); gicc->arm_mpidr = cpu_to_le64(armcpu->mp_affinity); @@ -668,8 +643,8 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (arm_feature(&armcpu->env, ARM_FEATURE_PMU)) { gicc->performance_interrupt = cpu_to_le32(PPI(VIRTUAL_PMU_IRQ)); } - if (vms->virt && vms->gic_version == 3) { - gicc->vgic_interrupt = cpu_to_le32(PPI(ARCH_GICV3_MAINT_IRQ)); + if (vms->virt) { + gicc->vgic_interrupt = cpu_to_le32(PPI(ARCH_GIC_MAINT_IRQ)); } } @@ -688,8 +663,10 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) 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_REDIST2].base); - gicr->range_length = cpu_to_le32(memmap[VIRT_GIC_REDIST2].size); + gicr->base_address = + cpu_to_le64(memmap[VIRT_HIGH_GIC_REDIST2].base); + gicr->range_length = + cpu_to_le32(memmap[VIRT_HIGH_GIC_REDIST2].size); } if (its_class_name() && !vmc->no_its) { @@ -850,7 +827,15 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) build_xsdt(tables_blob, tables->linker, table_offsets, NULL, NULL); /* RSDP is in FSEG memory, so allocate it separately */ - build_rsdp(tables->rsdp, tables->linker, xsdt); + { + AcpiRsdpData rsdp_data = { + .revision = 2, + .oem_id = ACPI_BUILD_APPNAME6, + .xsdt_tbl_offset = &xsdt, + .rsdt_tbl_offset = NULL, + }; + build_rsdp(tables->rsdp, tables->linker, &rsdp_data); + } /* Cleanup memory that's no longer used. */ g_array_free(table_offsets, true); diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 281ddcdf6e..ce2664a30b 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -29,21 +29,21 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/arm/arm.h" #include "hw/arm/primecell.h" #include "hw/arm/virt.h" +#include "hw/block/flash.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" #include "hw/display/ramfb.h" -#include "hw/devices.h" #include "net/net.h" #include "sysemu/device_tree.h" #include "sysemu/numa.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" -#include "hw/compat.h" #include "hw/loader.h" #include "exec/address-spaces.h" #include "qemu/bitops.h" @@ -55,10 +55,12 @@ #include "hw/intc/arm_gic.h" #include "hw/intc/arm_gicv3_common.h" #include "kvm_arm.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "qapi/visitor.h" #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" +#include "hw/acpi/acpi.h" +#include "target/arm/internals.h" #define DEFINE_VIRT_MACHINE_LATEST(major, minor, latest) \ static void virt_##major##_##minor##_class_init(ObjectClass *oc, \ @@ -74,7 +76,6 @@ static const TypeInfo machvirt_##major##_##minor##_info = { \ .name = MACHINE_TYPE_NAME("virt-" # major "." # minor), \ .parent = TYPE_VIRT_MACHINE, \ - .instance_init = virt_##major##_##minor##_instance_init, \ .class_init = virt_##major##_##minor##_class_init, \ }; \ static void machvirt_machine_##major##_##minor##_init(void) \ @@ -94,22 +95,9 @@ #define PLATFORM_BUS_NUM_IRQS 64 -/* 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) +/* Legacy RAM limit in GB (< version 4.0) */ +#define LEGACY_RAMLIMIT_GB 255 +#define LEGACY_RAMLIMIT_BYTES (LEGACY_RAMLIMIT_GB * GiB) /* Addresses and sizes of our components. * 0..128MB is space for a flash device so we can run bootrom code such as UEFI. @@ -123,7 +111,7 @@ * Note that devices should generally be placed at multiples of 0x10000, * to accommodate guests using 64K pages. */ -static const MemMapEntry a15memmap[] = { +static const MemMapEntry base_memmap[] = { /* Space up to 0x8000000 is reserved for a boot ROM */ [VIRT_FLASH] = { 0, 0x08000000 }, [VIRT_CPUPERIPHS] = { 0x08000000, 0x00020000 }, @@ -131,6 +119,8 @@ static const MemMapEntry a15memmap[] = { [VIRT_GIC_DIST] = { 0x08000000, 0x00010000 }, [VIRT_GIC_CPU] = { 0x08010000, 0x00010000 }, [VIRT_GIC_V2M] = { 0x08020000, 0x00001000 }, + [VIRT_GIC_HYP] = { 0x08030000, 0x00010000 }, + [VIRT_GIC_VCPU] = { 0x08040000, 0x00010000 }, /* 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 */ @@ -148,12 +138,26 @@ static const MemMapEntry a15memmap[] = { [VIRT_PCIE_MMIO] = { 0x10000000, 0x2eff0000 }, [VIRT_PCIE_PIO] = { 0x3eff0000, 0x00010000 }, [VIRT_PCIE_ECAM] = { 0x3f000000, 0x01000000 }, - [VIRT_MEM] = { 0x40000000, RAMLIMIT_BYTES }, + /* Actual RAM size depends on initial RAM and device memory settings */ + [VIRT_MEM] = { GiB, LEGACY_RAMLIMIT_BYTES }, +}; + +/* + * Highmem IO Regions: This memory map is floating, located after the RAM. + * Each MemMapEntry base (GPA) will be dynamically computed, depending on the + * top of the RAM, so that its base get the same alignment as the size, + * ie. a 512GiB entry will be aligned on a 512GiB boundary. If there is + * less than 256GiB of RAM, the floating area starts at the 256GiB mark. + * Note the extended_memmap is sized so that it eventually also includes the + * base_memmap entries (VIRT_HIGH_GIC_REDIST2 index is greater than the last + * index of base_memmap). + */ +static MemMapEntry extended_memmap[] = { /* Additional 64 MB redist region (can contain up to 512 redistributors) */ - [VIRT_GIC_REDIST2] = { 0x4000000000ULL, 0x4000000 }, - [VIRT_PCIE_ECAM_HIGH] = { 0x4010000000ULL, 0x10000000 }, - /* Second PCIe window, 512GB wide at the 512GB boundary */ - [VIRT_PCIE_MMIO_HIGH] = { 0x8000000000ULL, 0x8000000000ULL }, + [VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB }, + [VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB }, + /* Second PCIe window */ + [VIRT_HIGH_PCIE_MMIO] = { 0x0, 512 * GiB }, }; static const int a15irqmap[] = { @@ -172,6 +176,7 @@ static const char *valid_cpus[] = { ARM_CPU_TYPE_NAME("cortex-a15"), ARM_CPU_TYPE_NAME("cortex-a53"), ARM_CPU_TYPE_NAME("cortex-a57"), + ARM_CPU_TYPE_NAME("cortex-a72"), ARM_CPU_TYPE_NAME("host"), ARM_CPU_TYPE_NAME("max"), }; @@ -430,28 +435,43 @@ static void fdt_add_gic_node(VirtMachineState *vms) 2, vms->memmap[VIRT_GIC_REDIST].size); } else { qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", - 2, vms->memmap[VIRT_GIC_DIST].base, - 2, vms->memmap[VIRT_GIC_DIST].size, - 2, vms->memmap[VIRT_GIC_REDIST].base, - 2, vms->memmap[VIRT_GIC_REDIST].size, - 2, vms->memmap[VIRT_GIC_REDIST2].base, - 2, vms->memmap[VIRT_GIC_REDIST2].size); + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_REDIST].base, + 2, vms->memmap[VIRT_GIC_REDIST].size, + 2, vms->memmap[VIRT_HIGH_GIC_REDIST2].base, + 2, vms->memmap[VIRT_HIGH_GIC_REDIST2].size); } if (vms->virt) { qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", - GIC_FDT_IRQ_TYPE_PPI, ARCH_GICV3_MAINT_IRQ, + GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, GIC_FDT_IRQ_FLAGS_LEVEL_HI); } } else { /* 'cortex-a15-gic' means 'GIC v2' */ qemu_fdt_setprop_string(vms->fdt, nodename, "compatible", "arm,cortex-a15-gic"); - qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", - 2, vms->memmap[VIRT_GIC_DIST].base, - 2, vms->memmap[VIRT_GIC_DIST].size, - 2, vms->memmap[VIRT_GIC_CPU].base, - 2, vms->memmap[VIRT_GIC_CPU].size); + if (!vms->virt) { + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_CPU].base, + 2, vms->memmap[VIRT_GIC_CPU].size); + } else { + qemu_fdt_setprop_sized_cells(vms->fdt, nodename, "reg", + 2, vms->memmap[VIRT_GIC_DIST].base, + 2, vms->memmap[VIRT_GIC_DIST].size, + 2, vms->memmap[VIRT_GIC_CPU].base, + 2, vms->memmap[VIRT_GIC_CPU].size, + 2, vms->memmap[VIRT_GIC_HYP].base, + 2, vms->memmap[VIRT_GIC_HYP].size, + 2, vms->memmap[VIRT_GIC_VCPU].base, + 2, vms->memmap[VIRT_GIC_VCPU].size); + qemu_fdt_setprop_cells(vms->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_PPI, ARCH_GIC_MAINT_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + } } qemu_fdt_setprop_cell(vms->fdt, nodename, "phandle", vms->gic_phandle); @@ -568,11 +588,16 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) if (nb_redist_regions == 2) { uint32_t redist1_capacity = - vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; + vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; qdev_prop_set_uint32(gicdev, "redist-region-count[1]", MIN(smp_cpus - redist0_count, redist1_capacity)); } + } else { + if (!kvm_irqchip_in_kernel()) { + qdev_prop_set_bit(gicdev, "has-virtualization-extensions", + vms->virt); + } } qdev_init_nofail(gicdev); gicbusdev = SYS_BUS_DEVICE(gicdev); @@ -580,10 +605,15 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) if (type == 3) { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_REDIST].base); if (nb_redist_regions == 2) { - sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_REDIST2].base); + sysbus_mmio_map(gicbusdev, 2, + vms->memmap[VIRT_HIGH_GIC_REDIST2].base); } } else { sysbus_mmio_map(gicbusdev, 1, vms->memmap[VIRT_GIC_CPU].base); + if (vms->virt) { + sysbus_mmio_map(gicbusdev, 2, vms->memmap[VIRT_GIC_HYP].base); + sysbus_mmio_map(gicbusdev, 3, vms->memmap[VIRT_GIC_VCPU].base); + } } /* Wire the outputs from each CPU's generic timer and the GICv3 @@ -610,9 +640,17 @@ static void create_gic(VirtMachineState *vms, qemu_irq *pic) ppibase + timer_irq[irq])); } - qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", 0, - qdev_get_gpio_in(gicdev, ppibase - + ARCH_GICV3_MAINT_IRQ)); + if (type == 3) { + qemu_irq irq = qdev_get_gpio_in(gicdev, + ppibase + ARCH_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, irq); + } else if (vms->virt) { + qemu_irq irq = qdev_get_gpio_in(gicdev, + ppibase + ARCH_GIC_MAINT_IRQ); + sysbus_connect_irq(gicbusdev, i + 4 * smp_cpus, irq); + } + qdev_connect_gpio_out_named(cpudev, "pmu-interrupt", 0, qdev_get_gpio_in(gicdev, ppibase + VIRTUAL_PMU_IRQ)); @@ -678,6 +716,10 @@ static void create_uart(const VirtMachineState *vms, qemu_irq *pic, int uart, /* Mark as not usable by the normal world */ qemu_fdt_setprop_string(vms->fdt, nodename, "status", "disabled"); qemu_fdt_setprop_string(vms->fdt, nodename, "secure-status", "okay"); + + qemu_fdt_add_subnode(vms->fdt, "/secure-chosen"); + qemu_fdt_setprop_string(vms->fdt, "/secure-chosen", "stdout-path", + nodename); } g_free(nodename); @@ -837,7 +879,7 @@ static void create_one_flash(const char *name, hwaddr flashbase, * parameters as the flash devices on the Versatile Express board. */ DriveInfo *dinfo = drive_get_next(IF_PFLASH); - DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); const uint64_t sectorlength = 256 * 1024; @@ -1051,8 +1093,8 @@ static void create_pcie(VirtMachineState *vms, qemu_irq *pic) { hwaddr base_mmio = vms->memmap[VIRT_PCIE_MMIO].base; hwaddr size_mmio = vms->memmap[VIRT_PCIE_MMIO].size; - hwaddr base_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].base; - hwaddr size_mmio_high = vms->memmap[VIRT_PCIE_MMIO_HIGH].size; + hwaddr base_mmio_high = vms->memmap[VIRT_HIGH_PCIE_MMIO].base; + hwaddr size_mmio_high = vms->memmap[VIRT_HIGH_PCIE_MMIO].size; hwaddr base_pio = vms->memmap[VIRT_PCIE_PIO].base; hwaddr size_pio = vms->memmap[VIRT_PCIE_PIO].size; hwaddr base_ecam, size_ecam; @@ -1240,10 +1282,6 @@ static void virt_build_smbios(VirtMachineState *vms) size_t smbios_tables_len, smbios_anchor_len; const char *product = "QEMU Virtual Machine"; - if (!vms->fw_cfg) { - return; - } - if (kvm_enabled()) { product = "KVM Virtual Machine"; } @@ -1316,6 +1354,62 @@ static uint64_t virt_cpu_mp_affinity(VirtMachineState *vms, int idx) return arm_cpu_mp_affinity(idx, clustersz); } +static void virt_set_memmap(VirtMachineState *vms) +{ + MachineState *ms = MACHINE(vms); + hwaddr base, device_memory_base, device_memory_size; + int i; + + vms->memmap = extended_memmap; + + for (i = 0; i < ARRAY_SIZE(base_memmap); i++) { + vms->memmap[i] = base_memmap[i]; + } + + if (ms->ram_slots > ACPI_MAX_RAM_SLOTS) { + error_report("unsupported number of memory slots: %"PRIu64, + ms->ram_slots); + exit(EXIT_FAILURE); + } + + /* + * We compute the base of the high IO region depending on the + * amount of initial and device memory. The device memory start/size + * is aligned on 1GiB. We never put the high IO region below 256GiB + * so that if maxram_size is < 255GiB we keep the legacy memory map. + * The device region size assumes 1GiB page max alignment per slot. + */ + device_memory_base = + ROUND_UP(vms->memmap[VIRT_MEM].base + ms->ram_size, GiB); + device_memory_size = ms->maxram_size - ms->ram_size + ms->ram_slots * GiB; + + /* Base address of the high IO region */ + base = device_memory_base + ROUND_UP(device_memory_size, GiB); + if (base < device_memory_base) { + error_report("maxmem/slots too huge"); + exit(EXIT_FAILURE); + } + if (base < vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES) { + base = vms->memmap[VIRT_MEM].base + LEGACY_RAMLIMIT_BYTES; + } + + for (i = VIRT_LOWMEMMAP_LAST; i < ARRAY_SIZE(extended_memmap); i++) { + hwaddr size = extended_memmap[i].size; + + base = ROUND_UP(base, size); + vms->memmap[i].base = base; + vms->memmap[i].size = size; + base += size; + } + vms->highest_gpa = base - 1; + if (device_memory_size > 0) { + ms->device_memory = g_malloc0(sizeof(*ms->device_memory)); + ms->device_memory->base = device_memory_base; + memory_region_init(&ms->device_memory->mr, OBJECT(vms), + "device-memory", device_memory_size); + } +} + static void machvirt_init(MachineState *machine) { VirtMachineState *vms = VIRT_MACHINE(machine); @@ -1330,6 +1424,14 @@ static void machvirt_init(MachineState *machine) bool firmware_loaded = bios_name || drive_get(IF_PFLASH, 0, 0); bool aarch64 = true; + /* + * In accelerated mode, the memory map is computed earlier in kvm_type() + * to create a VM with the right number of IPA bits. + */ + if (!vms->memmap) { + virt_set_memmap(vms); + } + /* We can probe only here because during property set * KVM is not available yet */ @@ -1380,8 +1482,10 @@ static void machvirt_init(MachineState *machine) * many redistributors we can fit into the memory map. */ if (vms->gic_version == 3) { - virt_max_cpus = vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; - virt_max_cpus += vms->memmap[VIRT_GIC_REDIST2].size / GICV3_REDIST_SIZE; + virt_max_cpus = + vms->memmap[VIRT_GIC_REDIST].size / GICV3_REDIST_SIZE; + virt_max_cpus += + vms->memmap[VIRT_HIGH_GIC_REDIST2].size / GICV3_REDIST_SIZE; } else { virt_max_cpus = GIC_NCPU; } @@ -1395,11 +1499,6 @@ static void machvirt_init(MachineState *machine) vms->smp_cpus = smp_cpus; - if (machine->ram_size > vms->memmap[VIRT_MEM].size) { - error_report("mach-virt: cannot model more than %dGB RAM", RAMLIMIT_GB); - exit(1); - } - if (vms->virt && kvm_enabled()) { error_report("mach-virt: KVM does not support providing " "Virtualization extensions to the guest CPU"); @@ -1487,9 +1586,29 @@ static void machvirt_init(MachineState *machine) fdt_add_timer_nodes(vms); fdt_add_cpu_nodes(vms); + if (!kvm_enabled()) { + ARMCPU *cpu = ARM_CPU(first_cpu); + bool aarch64 = object_property_get_bool(OBJECT(cpu), "aarch64", NULL); + + if (aarch64 && vms->highmem) { + int requested_pa_size, pamax = arm_pamax(cpu); + + requested_pa_size = 64 - clz64(vms->highest_gpa); + if (pamax < requested_pa_size) { + error_report("VCPU supports less PA bits (%d) than requested " + "by the memory map (%d)", pamax, requested_pa_size); + exit(1); + } + } + } + memory_region_allocate_system_memory(ram, NULL, "mach-virt.ram", machine->ram_size); memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base, ram); + if (machine->device_memory) { + memory_region_add_subregion(sysmem, machine->device_memory->base, + &machine->device_memory->mr); + } create_flash(vms, sysmem, secure_sysmem ? secure_sysmem : sysmem); @@ -1710,6 +1829,36 @@ static HotplugHandler *virt_machine_get_hotplug_handler(MachineState *machine, return NULL; } +/* + * for arm64 kvm_type [7-0] encodes the requested number of bits + * in the IPA address space + */ +static int virt_kvm_type(MachineState *ms, const char *type_str) +{ + VirtMachineState *vms = VIRT_MACHINE(ms); + int max_vm_pa_size = kvm_arm_get_max_vm_ipa_size(ms); + int requested_pa_size; + + /* we freeze the memory map to compute the highest gpa */ + virt_set_memmap(vms); + + requested_pa_size = 64 - clz64(vms->highest_gpa); + + if (requested_pa_size > max_vm_pa_size) { + error_report("-m and ,maxmem option values " + "require an IPA range (%d bits) larger than " + "the one supported by the host (%d bits)", + requested_pa_size, max_vm_pa_size); + exit(1); + } + /* + * By default we return 0 which corresponds to an implicit legacy + * 40b IPA setting. Otherwise we return the actual requested PA + * logsize + */ + return requested_pa_size > 40 ? requested_pa_size : 0; +} + static void virt_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1724,6 +1873,7 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_CALXEDA_XGMAC); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_AMD_XGBE); machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); + machine_class_allow_dynamic_sysbus_dev(mc, TYPE_VFIO_PLATFORM); mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; mc->pci_allow_0_address = true; @@ -1733,34 +1883,13 @@ static void virt_machine_class_init(ObjectClass *oc, void *data) mc->cpu_index_to_instance_props = virt_cpu_index_to_props; mc->default_cpu_type = ARM_CPU_TYPE_NAME("cortex-a15"); mc->get_default_cpu_node_id = virt_get_default_cpu_node_id; + mc->kvm_type = virt_kvm_type; assert(!mc->get_hotplug_handler); mc->get_hotplug_handler = virt_machine_get_hotplug_handler; hc->plug = virt_machine_device_plug_cb; } -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, - .interfaces = (InterfaceInfo[]) { - { TYPE_HOTPLUG_HANDLER }, - { } - }, -}; - -static void machvirt_machine_init(void) -{ - type_register_static(&virt_machine_info); -} -type_init(machvirt_machine_init); - -#define VIRT_COMPAT_2_12 \ - HW_COMPAT_2_12 - -static void virt_3_0_instance_init(Object *obj) +static void virt_instance_init(Object *obj) { VirtMachineState *vms = VIRT_MACHINE(obj); VirtMachineClass *vmc = VIRT_MACHINE_GET_CLASS(vms); @@ -1826,93 +1955,91 @@ static void virt_3_0_instance_init(Object *obj) "Valid values are none and smmuv3", NULL); - vms->memmap = a15memmap; vms->irqmap = a15irqmap; } +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, + .instance_init = virt_instance_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, +}; + +static void machvirt_machine_init(void) +{ + type_register_static(&virt_machine_info); +} +type_init(machvirt_machine_init); + +static void virt_machine_4_0_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(4, 0) + +static void virt_machine_3_1_options(MachineClass *mc) +{ + virt_machine_4_0_options(mc); + compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); +} +DEFINE_VIRT_MACHINE(3, 1) + static void virt_machine_3_0_options(MachineClass *mc) { + virt_machine_3_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(3, 0) - -static void virt_2_12_instance_init(Object *obj) -{ - virt_3_0_instance_init(obj); -} +DEFINE_VIRT_MACHINE(3, 0) static void virt_machine_2_12_options(MachineClass *mc) { VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); virt_machine_3_0_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_12); + compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); vmc->no_highmem_ecam = true; mc->max_cpus = 255; } DEFINE_VIRT_MACHINE(2, 12) -#define VIRT_COMPAT_2_11 \ - HW_COMPAT_2_11 - -static void virt_2_11_instance_init(Object *obj) -{ - virt_2_12_instance_init(obj); -} - static void virt_machine_2_11_options(MachineClass *mc) { VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); virt_machine_2_12_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_11); + compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); vmc->smbios_old_sys_ver = true; } DEFINE_VIRT_MACHINE(2, 11) -#define VIRT_COMPAT_2_10 \ - HW_COMPAT_2_10 - -static void virt_2_10_instance_init(Object *obj) -{ - virt_2_11_instance_init(obj); -} - static void virt_machine_2_10_options(MachineClass *mc) { virt_machine_2_11_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_10); + compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); + /* before 2.11 we never faulted accesses to bad addresses */ + mc->ignore_memory_transaction_failures = true; } DEFINE_VIRT_MACHINE(2, 10) -#define VIRT_COMPAT_2_9 \ - HW_COMPAT_2_9 - -static void virt_2_9_instance_init(Object *obj) -{ - virt_2_10_instance_init(obj); -} - static void virt_machine_2_9_options(MachineClass *mc) { virt_machine_2_10_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_9); + compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); } DEFINE_VIRT_MACHINE(2, 9) -#define VIRT_COMPAT_2_8 \ - HW_COMPAT_2_8 - -static void virt_2_8_instance_init(Object *obj) -{ - virt_2_9_instance_init(obj); -} - static void virt_machine_2_8_options(MachineClass *mc) { VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); virt_machine_2_9_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_8); + compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); /* For 2.8 and earlier we falsely claimed in the DT that * our timers were edge-triggered, not level-triggered. */ @@ -1920,20 +2047,12 @@ static void virt_machine_2_8_options(MachineClass *mc) } DEFINE_VIRT_MACHINE(2, 8) -#define VIRT_COMPAT_2_7 \ - HW_COMPAT_2_7 - -static void virt_2_7_instance_init(Object *obj) -{ - virt_2_8_instance_init(obj); -} - static void virt_machine_2_7_options(MachineClass *mc) { VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); virt_machine_2_8_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_7); + compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); /* ITS was introduced with 2.8 */ vmc->no_its = true; /* Stick with 1K pages for migration compatibility */ @@ -1941,20 +2060,12 @@ static void virt_machine_2_7_options(MachineClass *mc) } DEFINE_VIRT_MACHINE(2, 7) -#define VIRT_COMPAT_2_6 \ - HW_COMPAT_2_6 - -static void virt_2_6_instance_init(Object *obj) -{ - virt_2_7_instance_init(obj); -} - static void virt_machine_2_6_options(MachineClass *mc) { VirtMachineClass *vmc = VIRT_MACHINE_CLASS(OBJECT_CLASS(mc)); virt_machine_2_7_options(mc); - SET_MACHINE_COMPAT(mc, VIRT_COMPAT_2_6); + compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); vmc->disallow_affinity_adjustment = true; /* Disable PMU for 2.6 as PMU support was first introduced in 2.7 */ vmc->no_pmu = true; diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index f1496d2927..b3b8215759 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -205,12 +205,11 @@ static void zynq_init(MachineState *machine) DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); /* AMD */ - pflash_cfi02_register(0xe2000000, NULL, "zynq.pflash", FLASH_SIZE, + pflash_cfi02_register(0xe2000000, "zynq.pflash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - FLASH_SECTOR_SIZE, - FLASH_SIZE/FLASH_SECTOR_SIZE, 1, + FLASH_SECTOR_SIZE, 1, 1, 0x0066, 0x0022, 0x0000, 0x0000, 0x0555, 0x2aa, - 0); + 0); dev = qdev_create(NULL, "xilinx,zynq_slcr"); qdev_init_nofail(dev); @@ -294,7 +293,7 @@ static void zynq_init(MachineState *machine) busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, 0xF8003000); sysbus_connect_irq(busdev, 0, pic[45-IRQ_OFFSET]); /* abort irq line */ - for (n = 0; n < 8; ++n) { /* event irqs */ + for (n = 0; n < ARRAY_SIZE(dma_irqs); ++n) { /* event irqs */ sysbus_connect_irq(busdev, n + 1, pic[dma_irqs[n] - IRQ_OFFSET]); } diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c new file mode 100644 index 0000000000..f95fde2309 --- /dev/null +++ b/hw/arm/xlnx-versal-virt.c @@ -0,0 +1,495 @@ +/* + * Xilinx Versal Virtual board. + * + * Copyright (c) 2018 Xilinx Inc. + * Written by Edgar E. Iglesias + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "sysemu/device_tree.h" +#include "exec/address-spaces.h" +#include "hw/boards.h" +#include "hw/sysbus.h" +#include "hw/arm/sysbus-fdt.h" +#include "hw/arm/fdt.h" +#include "cpu.h" +#include "hw/arm/xlnx-versal.h" + +#define TYPE_XLNX_VERSAL_VIRT_MACHINE MACHINE_TYPE_NAME("xlnx-versal-virt") +#define XLNX_VERSAL_VIRT_MACHINE(obj) \ + OBJECT_CHECK(VersalVirt, (obj), TYPE_XLNX_VERSAL_VIRT_MACHINE) + +typedef struct VersalVirt { + MachineState parent_obj; + + Versal soc; + MemoryRegion mr_ddr; + + void *fdt; + int fdt_size; + struct { + uint32_t gic; + uint32_t ethernet_phy[2]; + uint32_t clk_125Mhz; + uint32_t clk_25Mhz; + } phandle; + struct arm_boot_info binfo; + + struct { + bool secure; + } cfg; +} VersalVirt; + +static void fdt_create(VersalVirt *s) +{ + MachineClass *mc = MACHINE_GET_CLASS(s); + int i; + + s->fdt = create_device_tree(&s->fdt_size); + if (!s->fdt) { + error_report("create_device_tree() failed"); + exit(1); + } + + /* Allocate all phandles. */ + s->phandle.gic = qemu_fdt_alloc_phandle(s->fdt); + for (i = 0; i < ARRAY_SIZE(s->phandle.ethernet_phy); i++) { + s->phandle.ethernet_phy[i] = qemu_fdt_alloc_phandle(s->fdt); + } + s->phandle.clk_25Mhz = qemu_fdt_alloc_phandle(s->fdt); + s->phandle.clk_125Mhz = qemu_fdt_alloc_phandle(s->fdt); + + /* Create /chosen node for load_dtb. */ + qemu_fdt_add_subnode(s->fdt, "/chosen"); + + /* Header */ + qemu_fdt_setprop_cell(s->fdt, "/", "interrupt-parent", s->phandle.gic); + qemu_fdt_setprop_cell(s->fdt, "/", "#size-cells", 0x2); + qemu_fdt_setprop_cell(s->fdt, "/", "#address-cells", 0x2); + qemu_fdt_setprop_string(s->fdt, "/", "model", mc->desc); + qemu_fdt_setprop_string(s->fdt, "/", "compatible", "xlnx-versal-virt"); +} + +static void fdt_add_clk_node(VersalVirt *s, const char *name, + unsigned int freq_hz, uint32_t phandle) +{ + qemu_fdt_add_subnode(s->fdt, name); + qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle); + qemu_fdt_setprop_cell(s->fdt, name, "clock-frequency", freq_hz); + qemu_fdt_setprop_cell(s->fdt, name, "#clock-cells", 0x0); + qemu_fdt_setprop_string(s->fdt, name, "compatible", "fixed-clock"); + qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0); +} + +static void fdt_add_cpu_nodes(VersalVirt *s, uint32_t psci_conduit) +{ + int i; + + qemu_fdt_add_subnode(s->fdt, "/cpus"); + qemu_fdt_setprop_cell(s->fdt, "/cpus", "#size-cells", 0x0); + qemu_fdt_setprop_cell(s->fdt, "/cpus", "#address-cells", 1); + + for (i = XLNX_VERSAL_NR_ACPUS - 1; i >= 0; i--) { + char *name = g_strdup_printf("/cpus/cpu@%d", i); + ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(i)); + + qemu_fdt_add_subnode(s->fdt, name); + qemu_fdt_setprop_cell(s->fdt, name, "reg", armcpu->mp_affinity); + if (psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) { + qemu_fdt_setprop_string(s->fdt, name, "enable-method", "psci"); + } + qemu_fdt_setprop_string(s->fdt, name, "device_type", "cpu"); + qemu_fdt_setprop_string(s->fdt, name, "compatible", + armcpu->dtb_compatible); + g_free(name); + } +} + +static void fdt_add_gic_nodes(VersalVirt *s) +{ + char *nodename; + + nodename = g_strdup_printf("/gic@%x", MM_GIC_APU_DIST_MAIN); + qemu_fdt_add_subnode(s->fdt, nodename); + qemu_fdt_setprop_cell(s->fdt, nodename, "phandle", s->phandle.gic); + qemu_fdt_setprop_cells(s->fdt, nodename, "interrupts", + GIC_FDT_IRQ_TYPE_PPI, VERSAL_GIC_MAINT_IRQ, + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop(s->fdt, nodename, "interrupt-controller", NULL, 0); + qemu_fdt_setprop_sized_cells(s->fdt, nodename, "reg", + 2, MM_GIC_APU_DIST_MAIN, + 2, MM_GIC_APU_DIST_MAIN_SIZE, + 2, MM_GIC_APU_REDIST_0, + 2, MM_GIC_APU_REDIST_0_SIZE); + qemu_fdt_setprop_cell(s->fdt, nodename, "#interrupt-cells", 3); + qemu_fdt_setprop_string(s->fdt, nodename, "compatible", "arm,gic-v3"); + g_free(nodename); +} + +static void fdt_add_timer_nodes(VersalVirt *s) +{ + const char compat[] = "arm,armv8-timer"; + uint32_t irqflags = GIC_FDT_IRQ_FLAGS_LEVEL_HI; + + qemu_fdt_add_subnode(s->fdt, "/timer"); + qemu_fdt_setprop_cells(s->fdt, "/timer", "interrupts", + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_S_EL1_IRQ, irqflags, + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL1_IRQ, irqflags, + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_VIRT_IRQ, irqflags, + GIC_FDT_IRQ_TYPE_PPI, VERSAL_TIMER_NS_EL2_IRQ, irqflags); + qemu_fdt_setprop(s->fdt, "/timer", "compatible", + compat, sizeof(compat)); +} + +static void fdt_add_uart_nodes(VersalVirt *s) +{ + uint64_t addrs[] = { MM_UART1, MM_UART0 }; + unsigned int irqs[] = { VERSAL_UART1_IRQ_0, VERSAL_UART0_IRQ_0 }; + const char compat[] = "arm,pl011\0arm,sbsa-uart"; + const char clocknames[] = "uartclk\0apb_pclk"; + int i; + + for (i = 0; i < ARRAY_SIZE(addrs); i++) { + char *name = g_strdup_printf("/uart@%" PRIx64, addrs[i]); + qemu_fdt_add_subnode(s->fdt, name); + qemu_fdt_setprop_cell(s->fdt, name, "current-speed", 115200); + qemu_fdt_setprop_cells(s->fdt, name, "clocks", + s->phandle.clk_125Mhz, s->phandle.clk_125Mhz); + qemu_fdt_setprop(s->fdt, name, "clock-names", + clocknames, sizeof(clocknames)); + + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irqs[i], + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, addrs[i], 2, 0x1000); + qemu_fdt_setprop(s->fdt, name, "compatible", + compat, sizeof(compat)); + qemu_fdt_setprop(s->fdt, name, "u-boot,dm-pre-reloc", NULL, 0); + + if (addrs[i] == MM_UART0) { + /* Select UART0. */ + qemu_fdt_setprop_string(s->fdt, "/chosen", "stdout-path", name); + } + g_free(name); + } +} + +static void fdt_add_fixed_link_nodes(VersalVirt *s, char *gemname, + uint32_t phandle) +{ + char *name = g_strdup_printf("%s/fixed-link", gemname); + + qemu_fdt_add_subnode(s->fdt, name); + qemu_fdt_setprop_cell(s->fdt, name, "phandle", phandle); + qemu_fdt_setprop(s->fdt, name, "full-duplex", NULL, 0); + qemu_fdt_setprop_cell(s->fdt, name, "speed", 1000); + g_free(name); +} + +static void fdt_add_gem_nodes(VersalVirt *s) +{ + uint64_t addrs[] = { MM_GEM1, MM_GEM0 }; + unsigned int irqs[] = { VERSAL_GEM1_IRQ_0, VERSAL_GEM0_IRQ_0 }; + const char clocknames[] = "pclk\0hclk\0tx_clk\0rx_clk"; + const char compat_gem[] = "cdns,zynqmp-gem\0cdns,gem"; + int i; + + for (i = 0; i < ARRAY_SIZE(addrs); i++) { + char *name = g_strdup_printf("/ethernet@%" PRIx64, addrs[i]); + qemu_fdt_add_subnode(s->fdt, name); + + fdt_add_fixed_link_nodes(s, name, s->phandle.ethernet_phy[i]); + qemu_fdt_setprop_string(s->fdt, name, "phy-mode", "rgmii-id"); + qemu_fdt_setprop_cell(s->fdt, name, "phy-handle", + s->phandle.ethernet_phy[i]); + qemu_fdt_setprop_cells(s->fdt, name, "clocks", + s->phandle.clk_25Mhz, s->phandle.clk_25Mhz, + s->phandle.clk_25Mhz, s->phandle.clk_25Mhz); + qemu_fdt_setprop(s->fdt, name, "clock-names", + clocknames, sizeof(clocknames)); + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irqs[i], + GIC_FDT_IRQ_FLAGS_LEVEL_HI, + GIC_FDT_IRQ_TYPE_SPI, irqs[i], + GIC_FDT_IRQ_FLAGS_LEVEL_HI); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, addrs[i], 2, 0x1000); + qemu_fdt_setprop(s->fdt, name, "compatible", + compat_gem, sizeof(compat_gem)); + qemu_fdt_setprop_cell(s->fdt, name, "#address-cells", 1); + qemu_fdt_setprop_cell(s->fdt, name, "#size-cells", 0); + g_free(name); + } +} + +static void fdt_nop_memory_nodes(void *fdt, Error **errp) +{ + Error *err = NULL; + char **node_path; + int n = 0; + + node_path = qemu_fdt_node_unit_path(fdt, "memory", &err); + if (err) { + error_propagate(errp, err); + return; + } + while (node_path[n]) { + if (g_str_has_prefix(node_path[n], "/memory")) { + qemu_fdt_nop_node(fdt, node_path[n]); + } + n++; + } + g_strfreev(node_path); +} + +static void fdt_add_memory_nodes(VersalVirt *s, void *fdt, uint64_t ram_size) +{ + /* Describes the various split DDR access regions. */ + static const struct { + uint64_t base; + uint64_t size; + } addr_ranges[] = { + { MM_TOP_DDR, MM_TOP_DDR_SIZE }, + { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, + { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, + { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } + }; + uint64_t mem_reg_prop[8] = {0}; + uint64_t size = ram_size; + Error *err = NULL; + char *name; + int i; + + fdt_nop_memory_nodes(fdt, &err); + if (err) { + error_report_err(err); + return; + } + + name = g_strdup_printf("/memory@%x", MM_TOP_DDR); + for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { + uint64_t mapsize; + + mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; + + mem_reg_prop[i * 2] = addr_ranges[i].base; + mem_reg_prop[i * 2 + 1] = mapsize; + size -= mapsize; + } + qemu_fdt_add_subnode(fdt, name); + qemu_fdt_setprop_string(fdt, name, "device_type", "memory"); + + switch (i) { + case 1: + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, mem_reg_prop[0], + 2, mem_reg_prop[1]); + break; + case 2: + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, mem_reg_prop[0], + 2, mem_reg_prop[1], + 2, mem_reg_prop[2], + 2, mem_reg_prop[3]); + break; + case 3: + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, mem_reg_prop[0], + 2, mem_reg_prop[1], + 2, mem_reg_prop[2], + 2, mem_reg_prop[3], + 2, mem_reg_prop[4], + 2, mem_reg_prop[5]); + break; + case 4: + qemu_fdt_setprop_sized_cells(fdt, name, "reg", + 2, mem_reg_prop[0], + 2, mem_reg_prop[1], + 2, mem_reg_prop[2], + 2, mem_reg_prop[3], + 2, mem_reg_prop[4], + 2, mem_reg_prop[5], + 2, mem_reg_prop[6], + 2, mem_reg_prop[7]); + break; + default: + g_assert_not_reached(); + } + g_free(name); +} + +static void versal_virt_modify_dtb(const struct arm_boot_info *binfo, + void *fdt) +{ + VersalVirt *s = container_of(binfo, VersalVirt, binfo); + + fdt_add_memory_nodes(s, fdt, binfo->ram_size); +} + +static void *versal_virt_get_dtb(const struct arm_boot_info *binfo, + int *fdt_size) +{ + const VersalVirt *board = container_of(binfo, VersalVirt, binfo); + + *fdt_size = board->fdt_size; + return board->fdt; +} + +#define NUM_VIRTIO_TRANSPORT 8 +static void create_virtio_regions(VersalVirt *s) +{ + int virtio_mmio_size = 0x200; + int i; + + for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { + char *name = g_strdup_printf("virtio%d", i);; + hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; + int irq = VERSAL_RSVD_IRQ_FIRST + i; + MemoryRegion *mr; + DeviceState *dev; + qemu_irq pic_irq; + + pic_irq = qdev_get_gpio_in(DEVICE(&s->soc.fpd.apu.gic), irq); + dev = qdev_create(NULL, "virtio-mmio"); + object_property_add_child(OBJECT(&s->soc), name, OBJECT(dev), + &error_fatal); + qdev_init_nofail(dev); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, pic_irq); + mr = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(&s->soc.mr_ps, base, mr); + g_free(name); + } + + for (i = 0; i < NUM_VIRTIO_TRANSPORT; i++) { + hwaddr base = MM_TOP_RSVD + i * virtio_mmio_size; + int irq = VERSAL_RSVD_IRQ_FIRST + i; + char *name = g_strdup_printf("/virtio_mmio@%" PRIx64, base); + + qemu_fdt_add_subnode(s->fdt, name); + qemu_fdt_setprop(s->fdt, name, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cells(s->fdt, name, "interrupts", + GIC_FDT_IRQ_TYPE_SPI, irq, + GIC_FDT_IRQ_FLAGS_EDGE_LO_HI); + qemu_fdt_setprop_sized_cells(s->fdt, name, "reg", + 2, base, 2, virtio_mmio_size); + qemu_fdt_setprop_string(s->fdt, name, "compatible", "virtio,mmio"); + g_free(name); + } +} + +static void versal_virt_init(MachineState *machine) +{ + VersalVirt *s = XLNX_VERSAL_VIRT_MACHINE(machine); + int psci_conduit = QEMU_PSCI_CONDUIT_DISABLED; + + /* + * If the user provides an Operating System to be loaded, we expect them + * to use the -kernel command line option. + * + * Users can load firmware or boot-loaders with the -device loader options. + * + * When loading an OS, we generate a dtb and let arm_load_kernel() select + * where it gets loaded. This dtb will be passed to the kernel in x0. + * + * If there's no -kernel option, we generate a DTB and place it at 0x1000 + * for the bootloaders or firmware to pick up. + * + * If users want to provide their own DTB, they can use the -dtb option. + * These dtb's will have their memory nodes modified to match QEMU's + * selected ram_size option before they get passed to the kernel or fw. + * + * When loading an OS, we turn on QEMU's PSCI implementation with SMC + * as the PSCI conduit. When there's no -kernel, we assume the user + * provides EL3 firmware to handle PSCI. + */ + if (machine->kernel_filename) { + psci_conduit = QEMU_PSCI_CONDUIT_SMC; + } + + memory_region_allocate_system_memory(&s->mr_ddr, NULL, "ddr", + machine->ram_size); + + sysbus_init_child_obj(OBJECT(machine), "xlnx-ve", &s->soc, + sizeof(s->soc), TYPE_XLNX_VERSAL); + object_property_set_link(OBJECT(&s->soc), OBJECT(&s->mr_ddr), + "ddr", &error_abort); + object_property_set_int(OBJECT(&s->soc), psci_conduit, + "psci-conduit", &error_abort); + object_property_set_bool(OBJECT(&s->soc), true, "realized", &error_fatal); + + fdt_create(s); + create_virtio_regions(s); + fdt_add_gem_nodes(s); + fdt_add_uart_nodes(s); + fdt_add_gic_nodes(s); + fdt_add_timer_nodes(s); + fdt_add_cpu_nodes(s, psci_conduit); + fdt_add_clk_node(s, "/clk125", 125000000, s->phandle.clk_125Mhz); + fdt_add_clk_node(s, "/clk25", 25000000, s->phandle.clk_25Mhz); + + /* Make the APU cpu address space visible to virtio and other + * modules unaware of muliple address-spaces. */ + memory_region_add_subregion_overlap(get_system_memory(), + 0, &s->soc.fpd.apu.mr, 0); + + s->binfo.ram_size = machine->ram_size; + s->binfo.kernel_filename = machine->kernel_filename; + s->binfo.kernel_cmdline = machine->kernel_cmdline; + s->binfo.initrd_filename = machine->initrd_filename; + s->binfo.loader_start = 0x0; + s->binfo.get_dtb = versal_virt_get_dtb; + s->binfo.modify_dtb = versal_virt_modify_dtb; + if (machine->kernel_filename) { + arm_load_kernel(s->soc.fpd.apu.cpu[0], &s->binfo); + } else { + AddressSpace *as = arm_boot_address_space(s->soc.fpd.apu.cpu[0], + &s->binfo); + /* Some boot-loaders (e.g u-boot) don't like blobs at address 0 (NULL). + * Offset things by 4K. */ + s->binfo.loader_start = 0x1000; + s->binfo.dtb_limit = 0x1000000; + if (arm_load_dtb(s->binfo.loader_start, + &s->binfo, s->binfo.dtb_limit, as) < 0) { + exit(EXIT_FAILURE); + } + } +} + +static void versal_virt_machine_instance_init(Object *obj) +{ +} + +static void versal_virt_machine_class_init(ObjectClass *oc, void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->desc = "Xilinx Versal Virtual development board"; + mc->init = versal_virt_init; + mc->max_cpus = XLNX_VERSAL_NR_ACPUS; + mc->default_cpus = XLNX_VERSAL_NR_ACPUS; + mc->no_cdrom = true; +} + +static const TypeInfo versal_virt_machine_init_typeinfo = { + .name = TYPE_XLNX_VERSAL_VIRT_MACHINE, + .parent = TYPE_MACHINE, + .class_init = versal_virt_machine_class_init, + .instance_init = versal_virt_machine_instance_init, + .instance_size = sizeof(VersalVirt), +}; + +static void versal_virt_machine_init_register_types(void) +{ + type_register_static(&versal_virt_machine_init_typeinfo); +} + +type_init(versal_virt_machine_init_register_types) + diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c new file mode 100644 index 0000000000..5ee58c09be --- /dev/null +++ b/hw/arm/xlnx-versal.c @@ -0,0 +1,323 @@ +/* + * Xilinx Versal SoC model. + * + * Copyright (c) 2018 Xilinx Inc. + * Written by Edgar E. Iglesias + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu-common.h" +#include "qemu/log.h" +#include "hw/sysbus.h" +#include "net/net.h" +#include "sysemu/sysemu.h" +#include "sysemu/kvm.h" +#include "hw/arm/arm.h" +#include "kvm_arm.h" +#include "hw/misc/unimp.h" +#include "hw/intc/arm_gicv3_common.h" +#include "hw/arm/xlnx-versal.h" + +#define XLNX_VERSAL_ACPU_TYPE ARM_CPU_TYPE_NAME("cortex-a72") +#define GEM_REVISION 0x40070106 + +static void versal_create_apu_cpus(Versal *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->fpd.apu.cpu); i++) { + Object *obj; + char *name; + + obj = object_new(XLNX_VERSAL_ACPU_TYPE); + if (!obj) { + /* Secondary CPUs start in PSCI powered-down state */ + error_report("Unable to create apu.cpu[%d] of type %s", + i, XLNX_VERSAL_ACPU_TYPE); + exit(EXIT_FAILURE); + } + + name = g_strdup_printf("apu-cpu[%d]", i); + object_property_add_child(OBJECT(s), name, obj, &error_fatal); + g_free(name); + + object_property_set_int(obj, s->cfg.psci_conduit, + "psci-conduit", &error_abort); + if (i) { + object_property_set_bool(obj, true, + "start-powered-off", &error_abort); + } + + object_property_set_int(obj, ARRAY_SIZE(s->fpd.apu.cpu), + "core-count", &error_abort); + object_property_set_link(obj, OBJECT(&s->fpd.apu.mr), "memory", + &error_abort); + object_property_set_bool(obj, true, "realized", &error_fatal); + s->fpd.apu.cpu[i] = ARM_CPU(obj); + } +} + +static void versal_create_apu_gic(Versal *s, qemu_irq *pic) +{ + static const uint64_t addrs[] = { + MM_GIC_APU_DIST_MAIN, + MM_GIC_APU_REDIST_0 + }; + SysBusDevice *gicbusdev; + DeviceState *gicdev; + int nr_apu_cpus = ARRAY_SIZE(s->fpd.apu.cpu); + int i; + + sysbus_init_child_obj(OBJECT(s), "apu-gic", + &s->fpd.apu.gic, sizeof(s->fpd.apu.gic), + gicv3_class_name()); + gicbusdev = SYS_BUS_DEVICE(&s->fpd.apu.gic); + gicdev = DEVICE(&s->fpd.apu.gic); + qdev_prop_set_uint32(gicdev, "revision", 3); + qdev_prop_set_uint32(gicdev, "num-cpu", 2); + qdev_prop_set_uint32(gicdev, "num-irq", XLNX_VERSAL_NR_IRQS + 32); + qdev_prop_set_uint32(gicdev, "len-redist-region-count", 1); + qdev_prop_set_uint32(gicdev, "redist-region-count[0]", 2); + qdev_prop_set_bit(gicdev, "has-security-extensions", true); + + object_property_set_bool(OBJECT(&s->fpd.apu.gic), true, "realized", + &error_fatal); + + for (i = 0; i < ARRAY_SIZE(addrs); i++) { + MemoryRegion *mr; + + mr = sysbus_mmio_get_region(gicbusdev, i); + memory_region_add_subregion(&s->fpd.apu.mr, addrs[i], mr); + } + + for (i = 0; i < nr_apu_cpus; i++) { + DeviceState *cpudev = DEVICE(s->fpd.apu.cpu[i]); + int ppibase = XLNX_VERSAL_NR_IRQS + i * GIC_INTERNAL + GIC_NR_SGIS; + qemu_irq maint_irq; + int ti; + /* Mapping from the output timer irq lines from the CPU to the + * GIC PPI inputs. + */ + const int timer_irq[] = { + [GTIMER_PHYS] = VERSAL_TIMER_NS_EL1_IRQ, + [GTIMER_VIRT] = VERSAL_TIMER_VIRT_IRQ, + [GTIMER_HYP] = VERSAL_TIMER_NS_EL2_IRQ, + [GTIMER_SEC] = VERSAL_TIMER_S_EL1_IRQ, + }; + + for (ti = 0; ti < ARRAY_SIZE(timer_irq); ti++) { + qdev_connect_gpio_out(cpudev, ti, + qdev_get_gpio_in(gicdev, + ppibase + timer_irq[ti])); + } + maint_irq = qdev_get_gpio_in(gicdev, + ppibase + VERSAL_GIC_MAINT_IRQ); + qdev_connect_gpio_out_named(cpudev, "gicv3-maintenance-interrupt", + 0, maint_irq); + sysbus_connect_irq(gicbusdev, i, qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); + sysbus_connect_irq(gicbusdev, i + nr_apu_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicbusdev, i + 2 * nr_apu_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicbusdev, i + 3 * nr_apu_cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); + } + + for (i = 0; i < XLNX_VERSAL_NR_IRQS; i++) { + pic[i] = qdev_get_gpio_in(gicdev, i); + } +} + +static void versal_create_uarts(Versal *s, qemu_irq *pic) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.uart); i++) { + static const int irqs[] = { VERSAL_UART0_IRQ_0, VERSAL_UART1_IRQ_0}; + static const uint64_t addrs[] = { MM_UART0, MM_UART1 }; + char *name = g_strdup_printf("uart%d", i); + DeviceState *dev; + MemoryRegion *mr; + + dev = qdev_create(NULL, "pl011"); + s->lpd.iou.uart[i] = SYS_BUS_DEVICE(dev); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); + qdev_init_nofail(dev); + + mr = sysbus_mmio_get_region(s->lpd.iou.uart[i], 0); + memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + + sysbus_connect_irq(s->lpd.iou.uart[i], 0, pic[irqs[i]]); + g_free(name); + } +} + +static void versal_create_gems(Versal *s, qemu_irq *pic) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->lpd.iou.gem); i++) { + static const int irqs[] = { VERSAL_GEM0_IRQ_0, VERSAL_GEM1_IRQ_0}; + static const uint64_t addrs[] = { MM_GEM0, MM_GEM1 }; + char *name = g_strdup_printf("gem%d", i); + NICInfo *nd = &nd_table[i]; + DeviceState *dev; + MemoryRegion *mr; + + dev = qdev_create(NULL, "cadence_gem"); + s->lpd.iou.gem[i] = SYS_BUS_DEVICE(dev); + object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); + if (nd->used) { + qemu_check_nic_model(nd, "cadence_gem"); + qdev_set_nic_properties(dev, nd); + } + object_property_set_int(OBJECT(s->lpd.iou.gem[i]), + 2, "num-priority-queues", + &error_abort); + object_property_set_link(OBJECT(s->lpd.iou.gem[i]), + OBJECT(&s->mr_ps), "dma", + &error_abort); + qdev_init_nofail(dev); + + mr = sysbus_mmio_get_region(s->lpd.iou.gem[i], 0); + memory_region_add_subregion(&s->mr_ps, addrs[i], mr); + + sysbus_connect_irq(s->lpd.iou.gem[i], 0, pic[irqs[i]]); + g_free(name); + } +} + +/* This takes the board allocated linear DDR memory and creates aliases + * for each split DDR range/aperture on the Versal address map. + */ +static void versal_map_ddr(Versal *s) +{ + uint64_t size = memory_region_size(s->cfg.mr_ddr); + /* Describes the various split DDR access regions. */ + static const struct { + uint64_t base; + uint64_t size; + } addr_ranges[] = { + { MM_TOP_DDR, MM_TOP_DDR_SIZE }, + { MM_TOP_DDR_2, MM_TOP_DDR_2_SIZE }, + { MM_TOP_DDR_3, MM_TOP_DDR_3_SIZE }, + { MM_TOP_DDR_4, MM_TOP_DDR_4_SIZE } + }; + uint64_t offset = 0; + int i; + + assert(ARRAY_SIZE(addr_ranges) == ARRAY_SIZE(s->noc.mr_ddr_ranges)); + for (i = 0; i < ARRAY_SIZE(addr_ranges) && size; i++) { + char *name; + uint64_t mapsize; + + mapsize = size < addr_ranges[i].size ? size : addr_ranges[i].size; + name = g_strdup_printf("noc-ddr-range%d", i); + /* Create the MR alias. */ + memory_region_init_alias(&s->noc.mr_ddr_ranges[i], OBJECT(s), + name, s->cfg.mr_ddr, + offset, mapsize); + + /* Map it onto the NoC MR. */ + memory_region_add_subregion(&s->mr_ps, addr_ranges[i].base, + &s->noc.mr_ddr_ranges[i]); + offset += mapsize; + size -= mapsize; + g_free(name); + } +} + +static void versal_unimp_area(Versal *s, const char *name, + MemoryRegion *mr, + hwaddr base, hwaddr size) +{ + DeviceState *dev = qdev_create(NULL, TYPE_UNIMPLEMENTED_DEVICE); + MemoryRegion *mr_dev; + + qdev_prop_set_string(dev, "name", name); + qdev_prop_set_uint64(dev, "size", size); + object_property_add_child(OBJECT(s), name, OBJECT(dev), &error_fatal); + qdev_init_nofail(dev); + + mr_dev = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_add_subregion(mr, base, mr_dev); +} + +static void versal_unimp(Versal *s) +{ + versal_unimp_area(s, "psm", &s->mr_ps, + MM_PSM_START, MM_PSM_END - MM_PSM_START); + versal_unimp_area(s, "crl", &s->mr_ps, + MM_CRL, MM_CRL_SIZE); + versal_unimp_area(s, "crf", &s->mr_ps, + MM_FPD_CRF, MM_FPD_CRF_SIZE); + versal_unimp_area(s, "iou-scntr", &s->mr_ps, + MM_IOU_SCNTR, MM_IOU_SCNTR_SIZE); + versal_unimp_area(s, "iou-scntr-seucre", &s->mr_ps, + MM_IOU_SCNTRS, MM_IOU_SCNTRS_SIZE); +} + +static void versal_realize(DeviceState *dev, Error **errp) +{ + Versal *s = XLNX_VERSAL(dev); + qemu_irq pic[XLNX_VERSAL_NR_IRQS]; + + versal_create_apu_cpus(s); + versal_create_apu_gic(s, pic); + versal_create_uarts(s, pic); + versal_create_gems(s, pic); + versal_map_ddr(s); + versal_unimp(s); + + /* Create the On Chip Memory (OCM). */ + memory_region_init_ram(&s->lpd.mr_ocm, OBJECT(s), "ocm", + MM_OCM_SIZE, &error_fatal); + + memory_region_add_subregion_overlap(&s->mr_ps, MM_OCM, &s->lpd.mr_ocm, 0); + memory_region_add_subregion_overlap(&s->fpd.apu.mr, 0, &s->mr_ps, 0); +} + +static void versal_init(Object *obj) +{ + Versal *s = XLNX_VERSAL(obj); + + memory_region_init(&s->fpd.apu.mr, obj, "mr-apu", UINT64_MAX); + memory_region_init(&s->mr_ps, obj, "mr-ps-switch", UINT64_MAX); +} + +static Property versal_properties[] = { + DEFINE_PROP_LINK("ddr", Versal, cfg.mr_ddr, TYPE_MEMORY_REGION, + MemoryRegion *), + DEFINE_PROP_UINT32("psci-conduit", Versal, cfg.psci_conduit, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void versal_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = versal_realize; + dc->props = versal_properties; + /* No VMSD since we haven't got any top-level SoC state to save. */ +} + +static const TypeInfo versal_info = { + .name = TYPE_XLNX_VERSAL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Versal), + .instance_init = versal_init, + .class_init = versal_class_init, +}; + +static void versal_register_types(void) +{ + type_register_static(&versal_info); +} + +type_init(versal_register_types); diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index 8de4868eb9..4f8bc41d9d 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -29,12 +29,17 @@ #define ARM_PHYS_TIMER_PPI 30 #define ARM_VIRT_TIMER_PPI 27 +#define ARM_HYP_TIMER_PPI 26 +#define ARM_SEC_TIMER_PPI 29 +#define GIC_MAINTENANCE_PPI 25 #define GEM_REVISION 0x40070106 #define GIC_BASE_ADDR 0xf9000000 #define GIC_DIST_ADDR 0xf9010000 #define GIC_CPU_ADDR 0xf9020000 +#define GIC_VIFACE_ADDR 0xf9040000 +#define GIC_VCPU_ADDR 0xf9060000 #define SATA_INTR 133 #define SATA_ADDR 0xFD0C0000 @@ -111,11 +116,54 @@ static const int adma_ch_intr[XLNX_ZYNQMP_NUM_ADMA_CH] = { typedef struct XlnxZynqMPGICRegion { int region_index; uint32_t address; + uint32_t offset; + bool virt; } XlnxZynqMPGICRegion; static const XlnxZynqMPGICRegion xlnx_zynqmp_gic_regions[] = { - { .region_index = 0, .address = GIC_DIST_ADDR, }, - { .region_index = 1, .address = GIC_CPU_ADDR, }, + /* Distributor */ + { + .region_index = 0, + .address = GIC_DIST_ADDR, + .offset = 0, + .virt = false + }, + + /* CPU interface */ + { + .region_index = 1, + .address = GIC_CPU_ADDR, + .offset = 0, + .virt = false + }, + { + .region_index = 1, + .address = GIC_CPU_ADDR + 0x10000, + .offset = 0x1000, + .virt = false + }, + + /* Virtual interface */ + { + .region_index = 2, + .address = GIC_VIFACE_ADDR, + .offset = 0, + .virt = true + }, + + /* Virtual CPU interface */ + { + .region_index = 3, + .address = GIC_VCPU_ADDR, + .offset = 0, + .virt = true + }, + { + .region_index = 3, + .address = GIC_VCPU_ADDR + 0x10000, + .offset = 0x1000, + .virt = true + }, }; static inline int arm_gic_ppi_index(int cpu_nr, int ppi_index) @@ -130,12 +178,22 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, int i; int num_rpus = MIN(smp_cpus - XLNX_ZYNQMP_NUM_APU_CPUS, XLNX_ZYNQMP_NUM_RPU_CPUS); + if (num_rpus <= 0) { + /* Don't create rpu-cluster object if there's nothing to put in it */ + return; + } + + object_initialize_child(OBJECT(s), "rpu-cluster", &s->rpu_cluster, + sizeof(s->rpu_cluster), TYPE_CPU_CLUSTER, + &error_abort, NULL); + qdev_prop_set_uint32(DEVICE(&s->rpu_cluster), "cluster-id", 1); + for (i = 0; i < num_rpus; i++) { char *name; object_initialize(&s->rpu_cpu[i], sizeof(s->rpu_cpu[i]), "cortex-r5f-" TYPE_ARM_CPU); - object_property_add_child(OBJECT(s), "rpu-cpu[*]", + object_property_add_child(OBJECT(&s->rpu_cluster), "rpu-cpu[*]", OBJECT(&s->rpu_cpu[i]), &error_abort); name = object_get_canonical_path_component(OBJECT(&s->rpu_cpu[i])); @@ -157,6 +215,8 @@ static void xlnx_zynqmp_create_rpu(XlnxZynqMPState *s, const char *boot_cpu, return; } } + + qdev_init_nofail(DEVICE(&s->rpu_cluster)); } static void xlnx_zynqmp_init(Object *obj) @@ -165,10 +225,16 @@ static void xlnx_zynqmp_init(Object *obj) int i; int num_apus = MIN(smp_cpus, XLNX_ZYNQMP_NUM_APU_CPUS); + object_initialize_child(obj, "apu-cluster", &s->apu_cluster, + sizeof(s->apu_cluster), TYPE_CPU_CLUSTER, + &error_abort, NULL); + qdev_prop_set_uint32(DEVICE(&s->apu_cluster), "cluster-id", 0); + for (i = 0; i < num_apus; i++) { - object_initialize_child(obj, "apu-cpu[*]", &s->apu_cpu[i], - sizeof(s->apu_cpu[i]), - "cortex-a53-" TYPE_ARM_CPU, &error_abort, NULL); + object_initialize_child(OBJECT(&s->apu_cluster), "apu-cpu[*]", + &s->apu_cpu[i], sizeof(s->apu_cpu[i]), + "cortex-a53-" TYPE_ARM_CPU, &error_abort, + NULL); } sysbus_init_child_obj(obj, "gic", &s->gic, sizeof(s->gic), @@ -281,6 +347,11 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) 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", num_apus); + qdev_prop_set_bit(DEVICE(&s->gic), "has-security-extensions", s->secure); + qdev_prop_set_bit(DEVICE(&s->gic), + "has-virtualization-extensions", s->virt); + + qdev_init_nofail(DEVICE(&s->apu_cluster)); /* Realize APUs before realizing the GIC. KVM requires this. */ for (i = 0; i < num_apus; i++) { @@ -325,19 +396,23 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) for (i = 0; i < XLNX_ZYNQMP_GIC_REGIONS; i++) { SysBusDevice *gic = SYS_BUS_DEVICE(&s->gic); const XlnxZynqMPGICRegion *r = &xlnx_zynqmp_gic_regions[i]; - MemoryRegion *mr = sysbus_mmio_get_region(gic, r->region_index); + MemoryRegion *mr; uint32_t addr = r->address; int j; - sysbus_mmio_map(gic, r->region_index, addr); + if (r->virt && !s->virt) { + continue; + } + mr = sysbus_mmio_get_region(gic, r->region_index); for (j = 0; j < XLNX_ZYNQMP_GIC_ALIASES; j++) { MemoryRegion *alias = &s->gic_mr[i][j]; - addr += XLNX_ZYNQMP_GIC_REGION_SIZE; memory_region_init_alias(alias, OBJECT(s), "zynqmp-gic-alias", mr, - 0, XLNX_ZYNQMP_GIC_REGION_SIZE); + r->offset, XLNX_ZYNQMP_GIC_REGION_SIZE); memory_region_add_subregion(system_memory, addr, alias); + + addr += XLNX_ZYNQMP_GIC_REGION_SIZE; } } @@ -347,12 +422,33 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i, qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]), ARM_CPU_IRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + num_apus, + qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]), + ARM_CPU_FIQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + num_apus * 2, + qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]), + ARM_CPU_VIRQ)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + num_apus * 3, + qdev_get_gpio_in(DEVICE(&s->apu_cpu[i]), + ARM_CPU_VFIQ)); irq = qdev_get_gpio_in(DEVICE(&s->gic), arm_gic_ppi_index(i, ARM_PHYS_TIMER_PPI)); - qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 0, irq); + qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), GTIMER_PHYS, irq); irq = qdev_get_gpio_in(DEVICE(&s->gic), arm_gic_ppi_index(i, ARM_VIRT_TIMER_PPI)); - qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), 1, irq); + qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), GTIMER_VIRT, irq); + irq = qdev_get_gpio_in(DEVICE(&s->gic), + arm_gic_ppi_index(i, ARM_HYP_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), GTIMER_HYP, irq); + irq = qdev_get_gpio_in(DEVICE(&s->gic), + arm_gic_ppi_index(i, ARM_SEC_TIMER_PPI)); + qdev_connect_gpio_out(DEVICE(&s->apu_cpu[i]), GTIMER_SEC, irq); + + if (s->virt) { + irq = qdev_get_gpio_in(DEVICE(&s->gic), + arm_gic_ppi_index(i, GIC_MAINTENANCE_PPI)); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->gic), i + num_apus * 4, irq); + } } if (s->has_rpu) { diff --git a/hw/arm/z2.c b/hw/arm/z2.c index 697a822f1e..1f906ef20b 100644 --- a/hw/arm/z2.c +++ b/hw/arm/z2.c @@ -15,7 +15,6 @@ #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/ssi.h" #include "hw/boards.h" @@ -243,7 +242,7 @@ static int aer915_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int aer915_recv(I2CSlave *slave) +static uint8_t aer915_recv(I2CSlave *slave) { AER915State *s = AER915(slave); int retval = 0x00; @@ -324,11 +323,9 @@ static void z2_init(MachineState *machine) exit(1); } - if (!pflash_cfi01_register(Z2_FLASH_BASE, - NULL, "z2.flash0", Z2_FLASH_SIZE, + if (!pflash_cfi01_register(Z2_FLASH_BASE, "z2.flash0", Z2_FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - sector_len, Z2_FLASH_SIZE / sector_len, - 4, 0, 0, 0, 0, be)) { + sector_len, 4, 0, 0, 0, 0, be)) { error_report("Error registering flash memory"); exit(1); } diff --git a/hw/audio/Kconfig b/hw/audio/Kconfig new file mode 100644 index 0000000000..e9c6fed826 --- /dev/null +++ b/hw/audio/Kconfig @@ -0,0 +1,52 @@ +config SB16 + bool + default y + depends on ISA_BUS + +config ES1370 + bool + default y if PCI_DEVICES + depends on PCI + +config AC97 + bool + default y if PCI_DEVICES + depends on PCI + +config ADLIB + bool + default y + depends on ISA_BUS + +config GUS + bool + default y + depends on ISA_BUS + +config CS4231A + bool + default y + depends on ISA_BUS + +config HDA + bool + default y if PCI_DEVICES + depends on PCI + +config PCSPK + bool + default y + depends on I8254 + +config WM8750 + bool + depends on I2C + +config PL041 + bool + +config CS4231 + bool + +config MARVELL_88W8618 + bool diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 8800448e20..ffd8607ba6 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -125,6 +125,10 @@ enum { #define MUTE_SHIFT 15 +#define TYPE_AC97 "AC97" +#define AC97(obj) \ + OBJECT_CHECK(AC97LinkState, (obj), TYPE_AC97) + #define REC_MASK 7 enum { REC_MIC = 0, @@ -324,7 +328,7 @@ static void open_voice (AC97LinkState *s, int index, int freq) as.freq = freq; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; if (freq > 0) { @@ -1415,7 +1419,7 @@ static void ac97_exit (PCIDevice *dev) static int ac97_init (PCIBus *bus) { - pci_create_simple (bus, -1, "AC97"); + pci_create_simple(bus, -1, TYPE_AC97); return 0; } @@ -1443,7 +1447,7 @@ static void ac97_class_init (ObjectClass *klass, void *data) } static const TypeInfo ac97_info = { - .name = "AC97", + .name = TYPE_AC97, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (AC97DeviceState), .class_init = ac97_class_init, diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 97b876c7e0..0957780a3d 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -269,7 +269,7 @@ static void adlib_realizefn (DeviceState *dev, Error **errp) as.freq = s->freq; as.nchannels = SHIFT; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = AUDIO_HOST_ENDIANNESS; AUD_register_card ("adlib", &s->card); diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index aaebec1839..62da75eefe 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -288,7 +288,7 @@ static void cs_reset_voices (CSState *s, uint32_t val) switch ((val >> 5) & ((s->dregs[MODE_And_ID] & MODE2) ? 7 : 3)) { case 0: - as.fmt = AUD_FMT_U8; + as.fmt = AUDIO_FORMAT_U8; s->shift = as.nchannels == 2; break; @@ -298,15 +298,16 @@ static void cs_reset_voices (CSState *s, uint32_t val) case 3: s->tab = ALawDecompressTable; x_law: - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = AUDIO_HOST_ENDIANNESS; s->shift = as.nchannels == 2; break; case 6: as.endianness = 1; + /* fall through */ case 2: - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; s->shift = as.nchannels; break; diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 59cf252754..a5314d66fd 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -414,14 +414,14 @@ static void es1370_update_voices (ES1370State *s, uint32_t ctl, uint32_t sctl) i, new_freq, 1 << (new_fmt & 1), - (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8, + (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8, d->shift); if (new_freq) { struct audsettings as; as.freq = new_freq; as.nchannels = 1 << (new_fmt & 1); - as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8; + as.fmt = (new_fmt & 2) ? AUDIO_FORMAT_S16 : AUDIO_FORMAT_U8; as.endianness = 0; if (i == ADC_CHANNEL) { @@ -474,82 +474,7 @@ static inline uint32_t es1370_fixup (ES1370State *s, uint32_t addr) return addr; } -static void es1370_writeb(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - uint32_t shift, mask; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_CONTROL: - case ES1370_REG_CONTROL + 1: - case ES1370_REG_CONTROL + 2: - case ES1370_REG_CONTROL + 3: - shift = (addr - ES1370_REG_CONTROL) << 3; - mask = 0xff << shift; - val = (s->ctl & ~mask) | ((val & 0xff) << shift); - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - case ES1370_REG_MEMPAGE: - s->mempage = val; - break; - case ES1370_REG_SERIAL_CONTROL: - case ES1370_REG_SERIAL_CONTROL + 1: - case ES1370_REG_SERIAL_CONTROL + 2: - case ES1370_REG_SERIAL_CONTROL + 3: - shift = (addr - ES1370_REG_SERIAL_CONTROL) << 3; - mask = 0xff << shift; - val = (s->sctl & ~mask) | ((val & 0xff) << shift); - es1370_maybe_lower_irq (s, val); - es1370_update_voices (s, s->ctl, val); - print_sctl (val); - break; - default: - lwarn ("writeb %#x <- %#x\n", addr, val); - break; - } -} - -static void es1370_writew(void *opaque, uint32_t addr, uint32_t val) -{ - ES1370State *s = opaque; - addr = es1370_fixup (s, addr); - uint32_t shift, mask; - struct chan *d = &s->chan[0]; - - switch (addr) { - case ES1370_REG_CODEC: - dolog ("ignored codec write address %#x, data %#x\n", - (val >> 8) & 0xff, val & 0xff); - s->codec = val; - break; - - case ES1370_REG_CONTROL: - case ES1370_REG_CONTROL + 2: - shift = (addr != ES1370_REG_CONTROL) << 4; - mask = 0xffff << shift; - val = (s->ctl & ~mask) | ((val & 0xffff) << shift); - es1370_update_voices (s, val, s->sctl); - print_ctl (val); - break; - - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; - case ES1370_REG_DAC1_SCOUNT: - d->scount = (d->scount & ~0xffff) | (val & 0xffff); - break; - - default: - lwarn ("writew %#x <- %#x\n", addr, val); - break; - } -} - -static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) +static void es1370_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { ES1370State *s = opaque; struct chan *d = &s->chan[0]; @@ -572,21 +497,22 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) print_sctl (val); break; - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; case ES1370_REG_DAC1_SCOUNT: + case ES1370_REG_DAC2_SCOUNT: + case ES1370_REG_ADC_SCOUNT: + d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2; d->scount = (val & 0xffff) | (d->scount & ~0xffff); ldebug ("chan %td CURR_SAMP_CT %d, SAMP_CT %d\n", d - &s->chan[0], val >> 16, (val & 0xffff)); break; case ES1370_REG_ADC_FRAMEADR: - d++; - case ES1370_REG_DAC2_FRAMEADR: - d++; + d += 2; + goto frameadr; case ES1370_REG_DAC1_FRAMEADR: + case ES1370_REG_DAC2_FRAMEADR: + d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3; + frameadr: d->frame_addr = val; ldebug ("chan %td frame address %#x\n", d - &s->chan[0], val); break; @@ -599,10 +525,12 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) break; case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; + d += 2; + goto framecnt; case ES1370_REG_DAC1_FRAMECNT: + case ES1370_REG_DAC2_FRAMECNT: + d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3; + framecnt: d->frame_cnt = val; d->leftover = 0; ldebug ("chan %td frame count %d, buffer size %d\n", @@ -615,84 +543,7 @@ static void es1370_writel(void *opaque, uint32_t addr, uint32_t val) } } -static uint32_t es1370_readb(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - uint32_t val; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case 0x1b: /* Legacy */ - lwarn ("Attempt to read from legacy register\n"); - val = 5; - break; - case ES1370_REG_MEMPAGE: - val = s->mempage; - break; - case ES1370_REG_CONTROL + 0: - case ES1370_REG_CONTROL + 1: - case ES1370_REG_CONTROL + 2: - case ES1370_REG_CONTROL + 3: - val = s->ctl >> ((addr - ES1370_REG_CONTROL) << 3); - break; - case ES1370_REG_STATUS + 0: - case ES1370_REG_STATUS + 1: - case ES1370_REG_STATUS + 2: - case ES1370_REG_STATUS + 3: - val = s->status >> ((addr - ES1370_REG_STATUS) << 3); - break; - default: - val = ~0; - lwarn ("readb %#x -> %#x\n", addr, val); - break; - } - return val; -} - -static uint32_t es1370_readw(void *opaque, uint32_t addr) -{ - ES1370State *s = opaque; - struct chan *d = &s->chan[0]; - uint32_t val; - - addr = es1370_fixup (s, addr); - - switch (addr) { - case ES1370_REG_ADC_SCOUNT + 2: - d++; - case ES1370_REG_DAC2_SCOUNT + 2: - d++; - case ES1370_REG_DAC1_SCOUNT + 2: - val = d->scount >> 16; - break; - - case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; - case ES1370_REG_DAC1_FRAMECNT: - val = d->frame_cnt & 0xffff; - break; - - case ES1370_REG_ADC_FRAMECNT + 2: - d++; - case ES1370_REG_DAC2_FRAMECNT + 2: - d++; - case ES1370_REG_DAC1_FRAMECNT + 2: - val = d->frame_cnt >> 16; - break; - - default: - val = ~0; - lwarn ("readw %#x -> %#x\n", addr, val); - break; - } - - return val; -} - -static uint32_t es1370_readl(void *opaque, uint32_t addr) +static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) { ES1370State *s = opaque; uint32_t val; @@ -717,11 +568,10 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr) val = s->sctl; break; - case ES1370_REG_ADC_SCOUNT: - d++; - case ES1370_REG_DAC2_SCOUNT: - d++; case ES1370_REG_DAC1_SCOUNT: + case ES1370_REG_DAC2_SCOUNT: + case ES1370_REG_ADC_SCOUNT: + d += (addr - ES1370_REG_DAC1_SCOUNT) >> 2; val = d->scount; #ifdef DEBUG_ES1370 { @@ -736,10 +586,12 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr) break; case ES1370_REG_ADC_FRAMECNT: - d++; - case ES1370_REG_DAC2_FRAMECNT: - d++; + d += 2; + goto framecnt; case ES1370_REG_DAC1_FRAMECNT: + case ES1370_REG_DAC2_FRAMECNT: + d += (addr - ES1370_REG_DAC1_FRAMECNT) >> 3; + framecnt: val = d->frame_cnt; #ifdef DEBUG_ES1370 { @@ -754,10 +606,12 @@ static uint32_t es1370_readl(void *opaque, uint32_t addr) break; case ES1370_REG_ADC_FRAMEADR: - d++; - case ES1370_REG_DAC2_FRAMEADR: - d++; + d += 2; + goto frameadr; case ES1370_REG_DAC1_FRAMEADR: + case ES1370_REG_DAC2_FRAMEADR: + d += (addr - ES1370_REG_DAC1_FRAMEADR) >> 3; + frameadr: val = d->frame_addr; break; @@ -908,44 +762,17 @@ static void es1370_adc_callback (void *opaque, int avail) es1370_run_channel (s, ADC_CHANNEL, avail); } -static uint64_t es1370_read(void *opaque, hwaddr addr, - unsigned size) -{ - switch (size) { - case 1: - return es1370_readb(opaque, addr); - case 2: - return es1370_readw(opaque, addr); - case 4: - return es1370_readl(opaque, addr); - default: - return -1; - } -} - -static void es1370_write(void *opaque, hwaddr addr, uint64_t val, - unsigned size) -{ - switch (size) { - case 1: - es1370_writeb(opaque, addr, val); - break; - case 2: - es1370_writew(opaque, addr, val); - break; - case 4: - es1370_writel(opaque, addr, val); - break; - } -} - static const MemoryRegionOps es1370_io_ops = { .read = es1370_read, .write = es1370_write, - .impl = { + .valid = { .min_access_size = 1, .max_access_size = 4, }, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, .endianness = DEVICE_LITTLE_ENDIAN, }; diff --git a/hw/audio/fmopl.h b/hw/audio/fmopl.h index e7e578a48e..e008e72d7a 100644 --- a/hw/audio/fmopl.h +++ b/hw/audio/fmopl.h @@ -72,8 +72,8 @@ typedef struct fm_opl_f { /* Rhythm sention */ uint8_t rhythm; /* Rhythm mode , key flag */ /* time tables */ - int32_t AR_TABLE[75]; /* atttack rate tables */ - int32_t DR_TABLE[75]; /* decay rate tables */ + int32_t AR_TABLE[76]; /* attack rate tables */ + int32_t DR_TABLE[76]; /* decay rate tables */ uint32_t FN_TABLE[1024]; /* fnumber -> increment counter */ /* LFO */ int32_t *ams_table; diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 8e0b27e0f2..b3e2a7fdd5 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -251,7 +251,7 @@ static void gus_realizefn (DeviceState *dev, Error **errp) as.freq = s->freq; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = GUS_ENDIANNESS; s->voice = AUD_open_out ( diff --git a/hw/audio/gusemu_hal.c b/hw/audio/gusemu_hal.c index 1150fc4426..ae40ca341c 100644 --- a/hw/audio/gusemu_hal.c +++ b/hw/audio/gusemu_hal.c @@ -261,6 +261,7 @@ void gus_write(GUSEmuState * state, int port, int size, unsigned int data) GUSregb(IRQStatReg2x6) = 0x10; GUS_irqrequest(state, state->gusirq, 1); } + /* fall through */ case 0x20D: /* SB2xCd no IRQ */ GUSregb(SB2xCd) = (uint8_t) data; break; diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 617a1c1016..c25bfa38b1 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -99,9 +99,9 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) } switch (format & AC_FMT_BITS_MASK) { - case AC_FMT_BITS_8: as->fmt = AUD_FMT_S8; break; - case AC_FMT_BITS_16: as->fmt = AUD_FMT_S16; break; - case AC_FMT_BITS_32: as->fmt = AUD_FMT_S32; break; + case AC_FMT_BITS_8: as->fmt = AUDIO_FORMAT_S8; break; + case AC_FMT_BITS_16: as->fmt = AUDIO_FORMAT_S16; break; + case AC_FMT_BITS_32: as->fmt = AUDIO_FORMAT_S32; break; } as->nchannels = ((format & AC_FMT_CHAN_MASK) >> AC_FMT_CHAN_SHIFT) + 1; @@ -134,12 +134,12 @@ static void hda_codec_parse_fmt(uint32_t format, struct audsettings *as) /* -------------------------------------------------------------------------- */ static const char *fmt2name[] = { - [ AUD_FMT_U8 ] = "PCM-U8", - [ AUD_FMT_S8 ] = "PCM-S8", - [ AUD_FMT_U16 ] = "PCM-U16", - [ AUD_FMT_S16 ] = "PCM-S16", - [ AUD_FMT_U32 ] = "PCM-U32", - [ AUD_FMT_S32 ] = "PCM-S32", + [ AUDIO_FORMAT_U8 ] = "PCM-U8", + [ AUDIO_FORMAT_S8 ] = "PCM-S8", + [ AUDIO_FORMAT_U16 ] = "PCM-U16", + [ AUDIO_FORMAT_S16 ] = "PCM-S16", + [ AUDIO_FORMAT_U32 ] = "PCM-U32", + [ AUDIO_FORMAT_S32 ] = "PCM-S32", }; typedef struct HDAAudioState HDAAudioState; diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 23a2cf6484..33e333cc26 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -23,6 +23,7 @@ #include "hw/pci/msi.h" #include "qemu/timer.h" #include "qemu/bitops.h" +#include "qemu/log.h" #include "hw/audio/soundhw.h" #include "intel-hda.h" #include "intel-hda-defs.h" @@ -929,6 +930,11 @@ static void intel_hda_reg_write(IntelHDAState *d, const IntelHDAReg *reg, uint32 if (!reg) { return; } + if (!reg->wmask) { + qemu_log_mask(LOG_GUEST_ERROR, "intel-hda: write to r/o reg %s\n", + reg->name); + return; + } if (d->debug) { time_t now = time(NULL); diff --git a/hw/audio/lm4549.c b/hw/audio/lm4549.c index a46f2301af..af8b22b541 100644 --- a/hw/audio/lm4549.c +++ b/hw/audio/lm4549.c @@ -185,7 +185,7 @@ void lm4549_write(lm4549_state *s, struct audsettings as; as.freq = value; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; s->voice = AUD_open_out( @@ -255,7 +255,7 @@ static int lm4549_post_load(void *opaque, int version_id) struct audsettings as; as.freq = freq; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; s->voice = AUD_open_out( @@ -292,7 +292,7 @@ void lm4549_init(lm4549_state *s, lm4549_callback data_req_cb, void* opaque) /* Open a default voice */ as.freq = 48000; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; s->voice = AUD_open_out( diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c index e546892d3c..035dd76cbf 100644 --- a/hw/audio/marvell_88w8618.c +++ b/hw/audio/marvell_88w8618.c @@ -12,9 +12,9 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/hw.h" -#include "hw/i2c/i2c.h" #include "hw/audio/wm8750.h" #include "audio/audio.h" +#include "qapi/error.h" #define MP_AUDIO_SIZE 0x00001000 @@ -38,7 +38,6 @@ #define MP_AUDIO_CLOCK_24MHZ (1 << 9) #define MP_AUDIO_MONO (1 << 14) -#define TYPE_MV88W8618_AUDIO "mv88w8618_audio" #define MV88W8618_AUDIO(obj) \ OBJECT_CHECK(mv88w8618_audio_state, (obj), TYPE_MV88W8618_AUDIO) @@ -252,6 +251,11 @@ static void mv88w8618_audio_init(Object *obj) memory_region_init_io(&s->iomem, obj, &mv88w8618_audio_ops, s, "audio", MP_AUDIO_SIZE); sysbus_init_mmio(dev, &s->iomem); + + object_property_add_link(OBJECT(dev), "wm8750", TYPE_WM8750, + (Object **) &s->wm, + qdev_prop_allow_set_link_before_realize, + 0, &error_abort); } static void mv88w8618_audio_realize(DeviceState *dev, Error **errp) @@ -279,11 +283,6 @@ static const VMStateDescription mv88w8618_audio_vmsd = { } }; -static Property mv88w8618_audio_properties[] = { - DEFINE_PROP_PTR("wm8750", mv88w8618_audio_state, wm), - {/* end of list */}, -}; - static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -291,8 +290,6 @@ static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) dc->realize = mv88w8618_audio_realize; dc->reset = mv88w8618_audio_reset; dc->vmsd = &mv88w8618_audio_vmsd; - dc->props = mv88w8618_audio_properties; - /* Reason: pointer property "wm8750" */ dc->user_creatable = false; } diff --git a/hw/audio/milkymist-ac97.c b/hw/audio/milkymist-ac97.c index bc8db71ae0..90cce1e6ed 100644 --- a/hw/audio/milkymist-ac97.c +++ b/hw/audio/milkymist-ac97.c @@ -308,7 +308,7 @@ static void milkymist_ac97_realize(DeviceState *dev, Error **errp) as.freq = 48000; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 1; s->voice_in = AUD_open_in(&s->card, s->voice_in, diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 908696d483..9c7fd74aeb 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -114,7 +114,7 @@ static void pcspk_callback(void *opaque, int free) static int pcspk_audio_init(ISABus *bus) { PCSpkState *s = pcspk_state; - struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUD_FMT_U8, 0}; + struct audsettings as = {PCSPK_SAMPLE_RATE, 1, AUDIO_FORMAT_U8, 0}; AUD_register_card(s_spk, &s->card); @@ -222,6 +222,7 @@ static void pcspk_class_initfn(ObjectClass *klass, void *data) dc->vmsd = &vmstate_spk; dc->props = pcspk_properties; /* Reason: realize sets global pcspk_state */ + /* Reason: pit object link */ dc->user_creatable = false; } diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 5a4d32364e..65ea0cd938 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -66,7 +66,7 @@ typedef struct SB16State { int fmt_stereo; int fmt_signed; int fmt_bits; - audfmt_e fmt; + AudioFormat fmt; int dma_auto; int block_size; int fifo; @@ -224,7 +224,7 @@ static void continue_dma8 (SB16State *s) static void dma_cmd8 (SB16State *s, int mask, int dma_len) { - s->fmt = AUD_FMT_U8; + s->fmt = AUDIO_FORMAT_U8; s->use_hdma = 0; s->fmt_bits = 8; s->fmt_signed = 0; @@ -319,18 +319,18 @@ static void dma_cmd (SB16State *s, uint8_t cmd, uint8_t d0, int dma_len) if (16 == s->fmt_bits) { if (s->fmt_signed) { - s->fmt = AUD_FMT_S16; + s->fmt = AUDIO_FORMAT_S16; } else { - s->fmt = AUD_FMT_U16; + s->fmt = AUDIO_FORMAT_U16; } } else { if (s->fmt_signed) { - s->fmt = AUD_FMT_S8; + s->fmt = AUDIO_FORMAT_S8; } else { - s->fmt = AUD_FMT_U8; + s->fmt = AUDIO_FORMAT_U8; } } @@ -741,10 +741,15 @@ static void complete (SB16State *s) ldebug ("set time const %d\n", s->time_const); break; - case 0x42: /* FT2 sets output freq with this, go figure */ - qemu_log_mask(LOG_UNIMP, "cmd 0x42 might not do what it think it" - " should\n"); case 0x41: + case 0x42: + /* + * 0x41 is documented as setting the output sample rate, + * and 0x42 the input sample rate, but in fact SB16 hardware + * seems to have only a single sample rate under the hood, + * and FT2 sets output freq with this (go figure). Compare: + * http://homepages.cae.wisc.edu/~brodskye/sb16doc/sb16doc.html#SamplingRate + */ s->freq = dsp_get_hilo (s); ldebug ("set freq %d\n", s->freq); break; @@ -847,7 +852,7 @@ static void legacy_reset (SB16State *s) as.freq = s->freq; as.nchannels = 1; - as.fmt = AUD_FMT_U8; + as.fmt = AUDIO_FORMAT_U8; as.endianness = 0; s->voice = AUD_open_out ( diff --git a/hw/audio/trace-events b/hw/audio/trace-events index 5891b4e2b9..60556b4a97 100644 --- a/hw/audio/trace-events +++ b/hw/audio/trace-events @@ -1,12 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/audio/cs4231.c +# cs4231.c cs4231_mem_readl_dreg(uint32_t reg, uint32_t ret) "read dreg %d: 0x%02x" cs4231_mem_readl_reg(uint32_t reg, uint32_t ret) "read reg %d: 0x%08x" cs4231_mem_writel_reg(uint32_t reg, uint32_t old, uint32_t val) "write reg %d: 0x%08x -> 0x%08x" cs4231_mem_writel_dreg(uint32_t reg, uint32_t old, uint32_t val) "write dreg %d: 0x%02x -> 0x%02x" -# hw/audio/milkymist-ac97.c +# milkymist-ac97.c milkymist_ac97_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_ac97_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_ac97_pulse_irq_crrequest(void) "Pulse IRQ CR request" @@ -18,7 +18,7 @@ milkymist_ac97_in_cb_transferred(int transferred) "transferred %d" milkymist_ac97_out_cb(int free, uint32_t remaining) "free %d remaining %u" milkymist_ac97_out_cb_transferred(int transferred) "transferred %d" -# hw/audio/hda-codec.c +# hda-codec.c hda_audio_running(const char *stream, int nr, bool running) "st %s, nr %d, run %d" hda_audio_format(const char *stream, int chan, const char *fmt, int freq) "st %s, %d x %s @ %d Hz" hda_audio_adjust(const char *stream, int pos) "st %s, pos %d" diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index f4aa838f62..ca0ad73caf 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -201,7 +201,7 @@ static void wm8750_set_format(WM8750State *s) in_fmt.endianness = 0; in_fmt.nchannels = 2; in_fmt.freq = s->adc_hz; - in_fmt.fmt = AUD_FMT_S16; + in_fmt.fmt = AUDIO_FORMAT_S16; s->adc_voice[0] = AUD_open_in(&s->card, s->adc_voice[0], CODEC ".input1", s, wm8750_audio_in_cb, &in_fmt); @@ -214,7 +214,7 @@ static void wm8750_set_format(WM8750State *s) out_fmt.endianness = 0; out_fmt.nchannels = 2; out_fmt.freq = s->dac_hz; - out_fmt.fmt = AUD_FMT_S16; + out_fmt.fmt = AUDIO_FORMAT_S16; s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], CODEC ".speaker", s, wm8750_audio_out_cb, &out_fmt); @@ -561,7 +561,7 @@ static int wm8750_tx(I2CSlave *i2c, uint8_t data) return 0; } -static int wm8750_rx(I2CSlave *i2c) +static uint8_t wm8750_rx(I2CSlave *i2c) { return 0x00; } @@ -681,7 +681,7 @@ uint32_t wm8750_adc_dat(void *opaque) if (s->idx_in >= sizeof(s->data_in)) { wm8750_in_load(s); if (s->idx_in >= sizeof(s->data_in)) { - return 0x80008000; /* silence in AUD_FMT_S16 sample format */ + return 0x80008000; /* silence in AUDIO_FORMAT_S16 sample format */ } } diff --git a/hw/block/Kconfig b/hw/block/Kconfig new file mode 100644 index 0000000000..df96dc5dcc --- /dev/null +++ b/hw/block/Kconfig @@ -0,0 +1,39 @@ +config FDC + bool + # FIXME: there is no separate file for the MMIO floppy disk controller, so + # select ISA_BUS here instead of polluting each board that requires one + select ISA_BUS + +config SSI_M25P80 + bool + +config NAND + bool + +config PFLASH_CFI01 + bool + +config PFLASH_CFI02 + bool + +config ECC + bool + +config ONENAND + bool + +config NVME_PCI + bool + default y if PCI_DEVICES + depends on PCI + +config VIRTIO_BLK + bool + default y + depends on VIRTIO + +config VHOST_USER_BLK + bool + # Only PCI devices are provided for now + default y if VIRTIO_PCI + depends on VIRTIO && VHOST_USER && LINUX diff --git a/hw/block/Makefile.objs b/hw/block/Makefile.objs index 53ce5751ae..f5f643f0cc 100644 --- a/hw/block/Makefile.objs +++ b/hw/block/Makefile.objs @@ -4,7 +4,7 @@ common-obj-$(CONFIG_SSI_M25P80) += m25p80.o common-obj-$(CONFIG_NAND) += nand.o common-obj-$(CONFIG_PFLASH_CFI01) += pflash_cfi01.o common-obj-$(CONFIG_PFLASH_CFI02) += pflash_cfi02.o -common-obj-$(CONFIG_XEN) += xen_disk.o +common-obj-$(CONFIG_XEN) += xen-block.o common-obj-$(CONFIG_ECC) += ecc.o common-obj-$(CONFIG_ONENAND) += onenand.o common-obj-$(CONFIG_NVME_PCI) += nvme.o @@ -12,5 +12,6 @@ common-obj-$(CONFIG_NVME_PCI) += nvme.o obj-$(CONFIG_SH4) += tc58128.o obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o -obj-$(CONFIG_VIRTIO_BLK) += dataplane/ obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk.o + +obj-y += dataplane/ diff --git a/hw/block/block.c b/hw/block/block.c index bef24bfca8..5f46e845b4 100644 --- a/hw/block/block.c +++ b/hw/block/block.c @@ -13,7 +13,6 @@ #include "hw/block/block.h" #include "qapi/error.h" #include "qapi/qapi-types-block.h" -#include "qemu/error-report.h" void blkconf_locked(BlockConf *conf, bool *locked) { @@ -23,17 +22,51 @@ void blkconf_locked(BlockConf *conf, bool *locked) *locked = dinfo->locked; } -void blkconf_serial(BlockConf *conf, char **serial) +/* + * Read the entire contents of @blk into @buf. + * @blk's contents must be @size bytes, and @size must be at most + * BDRV_REQUEST_MAX_BYTES. + * On success, return true. + * On failure, store an error through @errp and return false. + * Note that the error messages do not identify the block backend. + * TODO Since callers don't either, this can result in confusing + * errors. + * This function not intended for actual block devices, which read on + * demand. It's for things like memory devices that (ab)use a block + * backend to provide persistence. + */ +bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, + Error **errp) { - DriveInfo *dinfo; + int64_t blk_len; + int ret; - if (!*serial) { - /* try to fall back to value set with legacy -drive serial=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - *serial = g_strdup(dinfo->serial); - } + blk_len = blk_getlength(blk); + if (blk_len < 0) { + error_setg_errno(errp, -blk_len, + "can't get size of block backend"); + return false; } + if (blk_len != size) { + error_setg(errp, "device requires %" HWADDR_PRIu " bytes, " + "block backend provides %" PRIu64 " bytes", + size, blk_len); + return false; + } + + /* + * We could loop for @size > BDRV_REQUEST_MAX_BYTES, but if we + * ever get to the point we want to read *gigabytes* here, we + * should probably rework the device to be more like an actual + * block device and read only on demand. + */ + assert(size <= BDRV_REQUEST_MAX_BYTES); + ret = blk_pread(blk, 0, buf, size); + if (ret < 0) { + error_setg_errno(errp, -ret, "can't read block backend"); + return false; + } + return true; } void blkconf_blocksizes(BlockConf *conf) @@ -116,20 +149,6 @@ bool blkconf_geometry(BlockConf *conf, int *ptrans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp) { - DriveInfo *dinfo; - - if (!conf->cyls && !conf->heads && !conf->secs) { - /* try to fall back to value set with legacy -drive cyls=... */ - dinfo = blk_legacy_dinfo(conf->blk); - if (dinfo) { - conf->cyls = dinfo->cyls; - conf->heads = dinfo->heads; - conf->secs = dinfo->secs; - if (ptrans) { - *ptrans = dinfo->trans; - } - } - } if (!conf->cyls && !conf->heads && !conf->secs) { hd_geometry_guess(conf->blk, &conf->cyls, &conf->heads, &conf->secs, diff --git a/hw/block/dataplane/Makefile.objs b/hw/block/dataplane/Makefile.objs index e786f66421..0c5270268e 100644 --- a/hw/block/dataplane/Makefile.objs +++ b/hw/block/dataplane/Makefile.objs @@ -1 +1,2 @@ -obj-y += virtio-blk.o +obj-$(CONFIG_VIRTIO_BLK) += virtio-blk.o +obj-$(CONFIG_XEN) += xen-block.o diff --git a/hw/block/dataplane/trace-events b/hw/block/dataplane/trace-events index 1a7ea277b0..843cc4e7b1 100644 --- a/hw/block/dataplane/trace-events +++ b/hw/block/dataplane/trace-events @@ -1,5 +1,5 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/block/dataplane/virtio-blk.c +# virtio-blk.c virtio_blk_data_plane_start(void *s) "dataplane %p" virtio_blk_data_plane_stop(void *s) "dataplane %p" diff --git a/hw/block/dataplane/xen-block.c b/hw/block/dataplane/xen-block.c new file mode 100644 index 0000000000..bb8f1186e4 --- /dev/null +++ b/hw/block/dataplane/xen-block.c @@ -0,0 +1,820 @@ +/* + * Copyright (c) 2018 Citrix Systems Inc. + * (c) Gerd Hoffmann + * + * 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 . + * + * 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/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "hw/xen/xen_common.h" +#include "hw/block/xen_blkif.h" +#include "sysemu/block-backend.h" +#include "sysemu/iothread.h" +#include "xen-block.h" + +typedef struct XenBlockRequest { + blkif_request_t req; + int16_t status; + off_t start; + QEMUIOVector v; + void *buf; + size_t size; + int presync; + int aio_inflight; + int aio_errors; + XenBlockDataPlane *dataplane; + QLIST_ENTRY(XenBlockRequest) list; + BlockAcctCookie acct; +} XenBlockRequest; + +struct XenBlockDataPlane { + XenDevice *xendev; + XenEventChannel *event_channel; + unsigned int *ring_ref; + unsigned int nr_ring_ref; + void *sring; + int protocol; + blkif_back_rings_t rings; + int more_work; + QLIST_HEAD(inflight_head, XenBlockRequest) inflight; + QLIST_HEAD(freelist_head, XenBlockRequest) freelist; + int requests_total; + int requests_inflight; + unsigned int max_requests; + BlockBackend *blk; + QEMUBH *bh; + IOThread *iothread; + AioContext *ctx; +}; + +static void reset_request(XenBlockRequest *request) +{ + memset(&request->req, 0, sizeof(request->req)); + request->status = 0; + request->start = 0; + request->size = 0; + request->presync = 0; + + request->aio_inflight = 0; + request->aio_errors = 0; + + request->dataplane = NULL; + memset(&request->list, 0, sizeof(request->list)); + memset(&request->acct, 0, sizeof(request->acct)); + + qemu_iovec_reset(&request->v); +} + +static XenBlockRequest *xen_block_start_request(XenBlockDataPlane *dataplane) +{ + XenBlockRequest *request = NULL; + + if (QLIST_EMPTY(&dataplane->freelist)) { + if (dataplane->requests_total >= dataplane->max_requests) { + goto out; + } + /* allocate new struct */ + request = g_malloc0(sizeof(*request)); + request->dataplane = dataplane; + /* + * We cannot need more pages per requests than this, and since we + * re-use requests, allocate the memory once here. It will be freed + * xen_block_dataplane_destroy() when the request list is freed. + */ + request->buf = qemu_memalign(XC_PAGE_SIZE, + BLKIF_MAX_SEGMENTS_PER_REQUEST * + XC_PAGE_SIZE); + dataplane->requests_total++; + qemu_iovec_init(&request->v, 1); + } else { + /* get one from freelist */ + request = QLIST_FIRST(&dataplane->freelist); + QLIST_REMOVE(request, list); + } + QLIST_INSERT_HEAD(&dataplane->inflight, request, list); + dataplane->requests_inflight++; + +out: + return request; +} + +static void xen_block_finish_request(XenBlockRequest *request) +{ + XenBlockDataPlane *dataplane = request->dataplane; + + QLIST_REMOVE(request, list); + dataplane->requests_inflight--; +} + +static void xen_block_release_request(XenBlockRequest *request) +{ + XenBlockDataPlane *dataplane = request->dataplane; + + QLIST_REMOVE(request, list); + reset_request(request); + request->dataplane = dataplane; + QLIST_INSERT_HEAD(&dataplane->freelist, request, list); + dataplane->requests_inflight--; +} + +/* + * translate request into iovec + start offset + * do sanity checks along the way + */ +static int xen_block_parse_request(XenBlockRequest *request) +{ + XenBlockDataPlane *dataplane = request->dataplane; + size_t len; + int i; + + switch (request->req.operation) { + case BLKIF_OP_READ: + break; + case BLKIF_OP_FLUSH_DISKCACHE: + request->presync = 1; + if (!request->req.nr_segments) { + return 0; + } + /* fall through */ + case BLKIF_OP_WRITE: + break; + case BLKIF_OP_DISCARD: + return 0; + default: + error_report("error: unknown operation (%d)", request->req.operation); + goto err; + }; + + if (request->req.operation != BLKIF_OP_READ && + blk_is_read_only(dataplane->blk)) { + error_report("error: write req for ro device"); + goto err; + } + + request->start = request->req.sector_number * XEN_BLKIF_SECTOR_SIZE; + for (i = 0; i < request->req.nr_segments; i++) { + if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { + error_report("error: nr_segments too big"); + goto err; + } + if (request->req.seg[i].first_sect > request->req.seg[i].last_sect) { + error_report("error: first > last sector"); + goto err; + } + if (request->req.seg[i].last_sect * XEN_BLKIF_SECTOR_SIZE >= + XC_PAGE_SIZE) { + error_report("error: page crossing"); + goto err; + } + + len = (request->req.seg[i].last_sect - + request->req.seg[i].first_sect + 1) * XEN_BLKIF_SECTOR_SIZE; + request->size += len; + } + if (request->start + request->size > blk_getlength(dataplane->blk)) { + error_report("error: access beyond end of file"); + goto err; + } + return 0; + +err: + request->status = BLKIF_RSP_ERROR; + return -1; +} + +static int xen_block_copy_request(XenBlockRequest *request) +{ + XenBlockDataPlane *dataplane = request->dataplane; + XenDevice *xendev = dataplane->xendev; + XenDeviceGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; + int i, count; + bool to_domain = (request->req.operation == BLKIF_OP_READ); + void *virt = request->buf; + Error *local_err = NULL; + + if (request->req.nr_segments == 0) { + return 0; + } + + count = request->req.nr_segments; + + for (i = 0; i < count; i++) { + if (to_domain) { + segs[i].dest.foreign.ref = request->req.seg[i].gref; + segs[i].dest.foreign.offset = request->req.seg[i].first_sect * + XEN_BLKIF_SECTOR_SIZE; + segs[i].source.virt = virt; + } else { + segs[i].source.foreign.ref = request->req.seg[i].gref; + segs[i].source.foreign.offset = request->req.seg[i].first_sect * + XEN_BLKIF_SECTOR_SIZE; + segs[i].dest.virt = virt; + } + segs[i].len = (request->req.seg[i].last_sect - + request->req.seg[i].first_sect + 1) * + XEN_BLKIF_SECTOR_SIZE; + virt += segs[i].len; + } + + xen_device_copy_grant_refs(xendev, to_domain, segs, count, &local_err); + + if (local_err) { + error_reportf_err(local_err, "failed to copy data: "); + + request->aio_errors++; + return -1; + } + + return 0; +} + +static int xen_block_do_aio(XenBlockRequest *request); +static int xen_block_send_response(XenBlockRequest *request); + +static void xen_block_complete_aio(void *opaque, int ret) +{ + XenBlockRequest *request = opaque; + XenBlockDataPlane *dataplane = request->dataplane; + + aio_context_acquire(dataplane->ctx); + + if (ret != 0) { + error_report("%s I/O error", + request->req.operation == BLKIF_OP_READ ? + "read" : "write"); + request->aio_errors++; + } + + request->aio_inflight--; + if (request->presync) { + request->presync = 0; + xen_block_do_aio(request); + goto done; + } + if (request->aio_inflight > 0) { + goto done; + } + + switch (request->req.operation) { + case BLKIF_OP_READ: + /* in case of failure request->aio_errors is increased */ + if (ret == 0) { + xen_block_copy_request(request); + } + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + default: + break; + } + + request->status = request->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; + xen_block_finish_request(request); + + switch (request->req.operation) { + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!request->req.nr_segments) { + break; + } + /* fall through */ + case BLKIF_OP_READ: + if (request->status == BLKIF_RSP_OKAY) { + block_acct_done(blk_get_stats(dataplane->blk), &request->acct); + } else { + block_acct_failed(blk_get_stats(dataplane->blk), &request->acct); + } + break; + case BLKIF_OP_DISCARD: + default: + break; + } + if (xen_block_send_response(request)) { + Error *local_err = NULL; + + xen_device_notify_event_channel(dataplane->xendev, + dataplane->event_channel, + &local_err); + if (local_err) { + error_report_err(local_err); + } + } + xen_block_release_request(request); + + qemu_bh_schedule(dataplane->bh); + +done: + aio_context_release(dataplane->ctx); +} + +static bool xen_block_split_discard(XenBlockRequest *request, + blkif_sector_t sector_number, + uint64_t nr_sectors) +{ + XenBlockDataPlane *dataplane = request->dataplane; + int64_t byte_offset; + int byte_chunk; + uint64_t byte_remaining; + uint64_t sec_start = sector_number; + uint64_t sec_count = nr_sectors; + + /* Wrap around, or overflowing byte limit? */ + if (sec_start + sec_count < sec_count || + sec_start + sec_count > INT64_MAX / XEN_BLKIF_SECTOR_SIZE) { + return false; + } + + byte_offset = sec_start * XEN_BLKIF_SECTOR_SIZE; + byte_remaining = sec_count * XEN_BLKIF_SECTOR_SIZE; + + do { + byte_chunk = byte_remaining > BDRV_REQUEST_MAX_BYTES ? + BDRV_REQUEST_MAX_BYTES : byte_remaining; + request->aio_inflight++; + blk_aio_pdiscard(dataplane->blk, byte_offset, byte_chunk, + xen_block_complete_aio, request); + byte_remaining -= byte_chunk; + byte_offset += byte_chunk; + } while (byte_remaining > 0); + + return true; +} + +static int xen_block_do_aio(XenBlockRequest *request) +{ + XenBlockDataPlane *dataplane = request->dataplane; + + if (request->req.nr_segments && + (request->req.operation == BLKIF_OP_WRITE || + request->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && + xen_block_copy_request(request)) { + goto err; + } + + request->aio_inflight++; + if (request->presync) { + blk_aio_flush(request->dataplane->blk, xen_block_complete_aio, + request); + return 0; + } + + switch (request->req.operation) { + case BLKIF_OP_READ: + qemu_iovec_add(&request->v, request->buf, request->size); + block_acct_start(blk_get_stats(dataplane->blk), &request->acct, + request->v.size, BLOCK_ACCT_READ); + request->aio_inflight++; + blk_aio_preadv(dataplane->blk, request->start, &request->v, 0, + xen_block_complete_aio, request); + break; + case BLKIF_OP_WRITE: + case BLKIF_OP_FLUSH_DISKCACHE: + if (!request->req.nr_segments) { + break; + } + + qemu_iovec_add(&request->v, request->buf, request->size); + block_acct_start(blk_get_stats(dataplane->blk), &request->acct, + request->v.size, + request->req.operation == BLKIF_OP_WRITE ? + BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH); + request->aio_inflight++; + blk_aio_pwritev(dataplane->blk, request->start, &request->v, 0, + xen_block_complete_aio, request); + break; + case BLKIF_OP_DISCARD: + { + struct blkif_request_discard *req = (void *)&request->req; + if (!xen_block_split_discard(request, req->sector_number, + req->nr_sectors)) { + goto err; + } + break; + } + default: + /* unknown operation (shouldn't happen -- parse catches this) */ + goto err; + } + + xen_block_complete_aio(request, 0); + + return 0; + +err: + xen_block_finish_request(request); + request->status = BLKIF_RSP_ERROR; + return -1; +} + +static int xen_block_send_response(XenBlockRequest *request) +{ + XenBlockDataPlane *dataplane = request->dataplane; + int send_notify = 0; + int have_requests = 0; + blkif_response_t *resp; + + /* Place on the response ring for the relevant domain. */ + switch (dataplane->protocol) { + case BLKIF_PROTOCOL_NATIVE: + resp = (blkif_response_t *)RING_GET_RESPONSE( + &dataplane->rings.native, + dataplane->rings.native.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_32: + resp = (blkif_response_t *)RING_GET_RESPONSE( + &dataplane->rings.x86_32_part, + dataplane->rings.x86_32_part.rsp_prod_pvt); + break; + case BLKIF_PROTOCOL_X86_64: + resp = (blkif_response_t *)RING_GET_RESPONSE( + &dataplane->rings.x86_64_part, + dataplane->rings.x86_64_part.rsp_prod_pvt); + break; + default: + return 0; + } + + resp->id = request->req.id; + resp->operation = request->req.operation; + resp->status = request->status; + + dataplane->rings.common.rsp_prod_pvt++; + + RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&dataplane->rings.common, + send_notify); + if (dataplane->rings.common.rsp_prod_pvt == + dataplane->rings.common.req_cons) { + /* + * Tail check for pending requests. Allows frontend to avoid + * notifications if requests are already in flight (lower + * overheads and promotes batching). + */ + RING_FINAL_CHECK_FOR_REQUESTS(&dataplane->rings.common, + have_requests); + } else if (RING_HAS_UNCONSUMED_REQUESTS(&dataplane->rings.common)) { + have_requests = 1; + } + + if (have_requests) { + dataplane->more_work++; + } + return send_notify; +} + +static int xen_block_get_request(XenBlockDataPlane *dataplane, + XenBlockRequest *request, RING_IDX rc) +{ + switch (dataplane->protocol) { + case BLKIF_PROTOCOL_NATIVE: { + blkif_request_t *req = + RING_GET_REQUEST(&dataplane->rings.native, rc); + + memcpy(&request->req, req, sizeof(request->req)); + break; + } + case BLKIF_PROTOCOL_X86_32: { + blkif_x86_32_request_t *req = + RING_GET_REQUEST(&dataplane->rings.x86_32_part, rc); + + blkif_get_x86_32_req(&request->req, req); + break; + } + case BLKIF_PROTOCOL_X86_64: { + blkif_x86_64_request_t *req = + RING_GET_REQUEST(&dataplane->rings.x86_64_part, rc); + + blkif_get_x86_64_req(&request->req, req); + break; + } + } + /* Prevent the compiler from accessing the on-ring fields instead. */ + barrier(); + return 0; +} + +/* + * Threshold of in-flight requests above which we will start using + * blk_io_plug()/blk_io_unplug() to batch requests. + */ +#define IO_PLUG_THRESHOLD 1 + +static void xen_block_handle_requests(XenBlockDataPlane *dataplane) +{ + RING_IDX rc, rp; + XenBlockRequest *request; + int inflight_atstart = dataplane->requests_inflight; + int batched = 0; + + dataplane->more_work = 0; + + rc = dataplane->rings.common.req_cons; + rp = dataplane->rings.common.sring->req_prod; + xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ + + /* + * If there was more than IO_PLUG_THRESHOLD requests in flight + * when we got here, this is an indication that there the bottleneck + * is below us, so it's worth beginning to batch up I/O requests + * rather than submitting them immediately. The maximum number + * of requests we're willing to batch is the number already in + * flight, so it can grow up to max_requests when the bottleneck + * is below us. + */ + if (inflight_atstart > IO_PLUG_THRESHOLD) { + blk_io_plug(dataplane->blk); + } + while (rc != rp) { + /* pull request from ring */ + if (RING_REQUEST_CONS_OVERFLOW(&dataplane->rings.common, rc)) { + break; + } + request = xen_block_start_request(dataplane); + if (request == NULL) { + dataplane->more_work++; + break; + } + xen_block_get_request(dataplane, request, rc); + dataplane->rings.common.req_cons = ++rc; + + /* parse them */ + if (xen_block_parse_request(request) != 0) { + switch (request->req.operation) { + case BLKIF_OP_READ: + block_acct_invalid(blk_get_stats(dataplane->blk), + BLOCK_ACCT_READ); + break; + case BLKIF_OP_WRITE: + block_acct_invalid(blk_get_stats(dataplane->blk), + BLOCK_ACCT_WRITE); + break; + case BLKIF_OP_FLUSH_DISKCACHE: + block_acct_invalid(blk_get_stats(dataplane->blk), + BLOCK_ACCT_FLUSH); + default: + break; + }; + + if (xen_block_send_response(request)) { + Error *local_err = NULL; + + xen_device_notify_event_channel(dataplane->xendev, + dataplane->event_channel, + &local_err); + if (local_err) { + error_report_err(local_err); + } + } + xen_block_release_request(request); + continue; + } + + if (inflight_atstart > IO_PLUG_THRESHOLD && + batched >= inflight_atstart) { + blk_io_unplug(dataplane->blk); + } + xen_block_do_aio(request); + if (inflight_atstart > IO_PLUG_THRESHOLD) { + if (batched >= inflight_atstart) { + blk_io_plug(dataplane->blk); + batched = 0; + } else { + batched++; + } + } + } + if (inflight_atstart > IO_PLUG_THRESHOLD) { + blk_io_unplug(dataplane->blk); + } + + if (dataplane->more_work && + dataplane->requests_inflight < dataplane->max_requests) { + qemu_bh_schedule(dataplane->bh); + } +} + +static void xen_block_dataplane_bh(void *opaque) +{ + XenBlockDataPlane *dataplane = opaque; + + aio_context_acquire(dataplane->ctx); + xen_block_handle_requests(dataplane); + aio_context_release(dataplane->ctx); +} + +static void xen_block_dataplane_event(void *opaque) +{ + XenBlockDataPlane *dataplane = opaque; + + qemu_bh_schedule(dataplane->bh); +} + +XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev, + BlockConf *conf, + IOThread *iothread) +{ + XenBlockDataPlane *dataplane = g_new0(XenBlockDataPlane, 1); + + dataplane->xendev = xendev; + dataplane->blk = conf->blk; + + QLIST_INIT(&dataplane->inflight); + QLIST_INIT(&dataplane->freelist); + + if (iothread) { + dataplane->iothread = iothread; + object_ref(OBJECT(dataplane->iothread)); + dataplane->ctx = iothread_get_aio_context(dataplane->iothread); + } else { + dataplane->ctx = qemu_get_aio_context(); + } + dataplane->bh = aio_bh_new(dataplane->ctx, xen_block_dataplane_bh, + dataplane); + + return dataplane; +} + +void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane) +{ + XenBlockRequest *request; + + if (!dataplane) { + return; + } + + while (!QLIST_EMPTY(&dataplane->freelist)) { + request = QLIST_FIRST(&dataplane->freelist); + QLIST_REMOVE(request, list); + qemu_iovec_destroy(&request->v); + qemu_vfree(request->buf); + g_free(request); + } + + qemu_bh_delete(dataplane->bh); + if (dataplane->iothread) { + object_unref(OBJECT(dataplane->iothread)); + } + + g_free(dataplane); +} + +void xen_block_dataplane_stop(XenBlockDataPlane *dataplane) +{ + XenDevice *xendev; + + if (!dataplane) { + return; + } + + aio_context_acquire(dataplane->ctx); + blk_set_aio_context(dataplane->blk, qemu_get_aio_context()); + aio_context_release(dataplane->ctx); + + xendev = dataplane->xendev; + + if (dataplane->event_channel) { + Error *local_err = NULL; + + xen_device_unbind_event_channel(xendev, dataplane->event_channel, + &local_err); + dataplane->event_channel = NULL; + + if (local_err) { + error_report_err(local_err); + } + } + + if (dataplane->sring) { + Error *local_err = NULL; + + xen_device_unmap_grant_refs(xendev, dataplane->sring, + dataplane->nr_ring_ref, &local_err); + dataplane->sring = NULL; + + if (local_err) { + error_report_err(local_err); + } + } + + g_free(dataplane->ring_ref); + dataplane->ring_ref = NULL; +} + +void xen_block_dataplane_start(XenBlockDataPlane *dataplane, + const unsigned int ring_ref[], + unsigned int nr_ring_ref, + unsigned int event_channel, + unsigned int protocol, + Error **errp) +{ + XenDevice *xendev = dataplane->xendev; + Error *local_err = NULL; + unsigned int ring_size; + unsigned int i; + + dataplane->nr_ring_ref = nr_ring_ref; + dataplane->ring_ref = g_new(unsigned int, nr_ring_ref); + + for (i = 0; i < nr_ring_ref; i++) { + dataplane->ring_ref[i] = ring_ref[i]; + } + + dataplane->protocol = protocol; + + ring_size = XC_PAGE_SIZE * dataplane->nr_ring_ref; + switch (dataplane->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + dataplane->max_requests = __CONST_RING_SIZE(blkif, ring_size); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + dataplane->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + dataplane->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size); + break; + } + default: + error_setg(errp, "unknown protocol %u", dataplane->protocol); + return; + } + + xen_device_set_max_grant_refs(xendev, dataplane->nr_ring_ref, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto stop; + } + + dataplane->sring = xen_device_map_grant_refs(xendev, + dataplane->ring_ref, + dataplane->nr_ring_ref, + PROT_READ | PROT_WRITE, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto stop; + } + + switch (dataplane->protocol) { + case BLKIF_PROTOCOL_NATIVE: + { + blkif_sring_t *sring_native = dataplane->sring; + + BACK_RING_INIT(&dataplane->rings.native, sring_native, ring_size); + break; + } + case BLKIF_PROTOCOL_X86_32: + { + blkif_x86_32_sring_t *sring_x86_32 = dataplane->sring; + + BACK_RING_INIT(&dataplane->rings.x86_32_part, sring_x86_32, + ring_size); + break; + } + case BLKIF_PROTOCOL_X86_64: + { + blkif_x86_64_sring_t *sring_x86_64 = dataplane->sring; + + BACK_RING_INIT(&dataplane->rings.x86_64_part, sring_x86_64, + ring_size); + break; + } + } + + dataplane->event_channel = + xen_device_bind_event_channel(xendev, event_channel, + xen_block_dataplane_event, dataplane, + &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto stop; + } + + aio_context_acquire(dataplane->ctx); + blk_set_aio_context(dataplane->blk, dataplane->ctx); + aio_context_release(dataplane->ctx); + return; + +stop: + xen_block_dataplane_stop(dataplane); +} diff --git a/hw/block/dataplane/xen-block.h b/hw/block/dataplane/xen-block.h new file mode 100644 index 0000000000..d6fa6d26dd --- /dev/null +++ b/hw/block/dataplane/xen-block.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 HW_BLOCK_DATAPLANE_XEN_BLOCK_H +#define HW_BLOCK_DATAPLANE_XEN_BLOCK_H + +#include "hw/block/block.h" +#include "hw/xen/xen-bus.h" +#include "sysemu/iothread.h" + +typedef struct XenBlockDataPlane XenBlockDataPlane; + +XenBlockDataPlane *xen_block_dataplane_create(XenDevice *xendev, + BlockConf *conf, + IOThread *iothread); +void xen_block_dataplane_destroy(XenBlockDataPlane *dataplane); +void xen_block_dataplane_start(XenBlockDataPlane *dataplane, + const unsigned int ring_ref[], + unsigned int nr_ring_ref, + unsigned int event_channel, + unsigned int protocol, + Error **errp); +void xen_block_dataplane_stop(XenBlockDataPlane *dataplane); + +#endif /* HW_BLOCK_DATAPLANE_XEN_BLOCK_H */ diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 2e9c1e1e2f..6f19f127a5 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -1617,7 +1617,7 @@ static void fdctrl_stop_transfer(FDCtrl *fdctrl, uint8_t status0, fdctrl->fifo[5] = cur_drv->sect; fdctrl->fifo[6] = FD_SECTOR_SC; fdctrl->data_dir = FD_DIR_READ; - if (!(fdctrl->msr & FD_MSR_NONDMA)) { + if (fdctrl->dma_chann != -1 && !(fdctrl->msr & FD_MSR_NONDMA)) { IsaDmaClass *k = ISADMA_GET_CLASS(fdctrl->dma); k->release_DREQ(fdctrl->dma, fdctrl->dma_chann); } diff --git a/hw/block/nvme.c b/hw/block/nvme.c index 5e508ab1b3..7caf92532a 100644 --- a/hw/block/nvme.c +++ b/hw/block/nvme.c @@ -272,7 +272,9 @@ static void nvme_post_cqes(void *opaque) sizeof(req->cqe)); QTAILQ_INSERT_TAIL(&sq->req_list, req, entry); } - nvme_irq_assert(n, cq); + if (cq->tail != cq->head) { + nvme_irq_assert(n, cq); + } } static void nvme_enqueue_req_completion(NvmeCQueue *cq, NvmeRequest *req) @@ -322,8 +324,8 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, const uint8_t data_shift = ns->id_ns.lbaf[lba_index].ds; uint64_t slba = le64_to_cpu(rw->slba); uint32_t nlb = le16_to_cpu(rw->nlb) + 1; - uint64_t aio_slba = slba << (data_shift - BDRV_SECTOR_BITS); - uint32_t aio_nlb = nlb << (data_shift - BDRV_SECTOR_BITS); + uint64_t offset = slba << data_shift; + uint32_t count = nlb << data_shift; if (unlikely(slba + nlb > ns->id_ns.nsze)) { trace_nvme_err_invalid_lba_range(slba, nlb, ns->id_ns.nsze); @@ -333,7 +335,7 @@ static uint16_t nvme_write_zeros(NvmeCtrl *n, NvmeNamespace *ns, NvmeCmd *cmd, req->has_sg = false; block_acct_start(blk_get_stats(n->conf.blk), &req->acct, 0, BLOCK_ACCT_WRITE); - req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, aio_slba, aio_nlb, + req->aiocb = blk_aio_pwrite_zeroes(n->conf.blk, offset, count, BDRV_REQ_MAY_UNMAP, nvme_rw_cb, req); return NVME_NO_COMPLETE; } @@ -554,6 +556,7 @@ static uint16_t nvme_del_cq(NvmeCtrl *n, NvmeCmd *cmd) trace_nvme_err_invalid_del_cq_notempty(qid); return NVME_INVALID_QUEUE_DEL; } + nvme_irq_deassert(n, cq); trace_nvme_del_cq(qid); nvme_free_cq(cq, n); return NVME_SUCCESS; @@ -797,6 +800,8 @@ static void nvme_clear_ctrl(NvmeCtrl *n) { int i; + blk_drain(n->conf.blk); + for (i = 0; i < n->num_queues; i++) { if (n->sq[i] != NULL) { nvme_free_sq(n->sq[i], n); @@ -1175,16 +1180,13 @@ static void nvme_cmb_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { NvmeCtrl *n = (NvmeCtrl *)opaque; - memcpy(&n->cmbuf[addr], &data, size); + stn_le_p(&n->cmbuf[addr], size, data); } static uint64_t nvme_cmb_read(void *opaque, hwaddr addr, unsigned size) { - uint64_t val; NvmeCtrl *n = (NvmeCtrl *)opaque; - - memcpy(&val, &n->cmbuf[addr], size); - return val; + return ldn_le_p(&n->cmbuf[addr], size); } static const MemoryRegionOps nvme_cmb_ops = { @@ -1192,7 +1194,7 @@ static const MemoryRegionOps nvme_cmb_ops = { .write = nvme_cmb_write, .endianness = DEVICE_LITTLE_ENDIAN, .impl = { - .min_access_size = 2, + .min_access_size = 1, .max_access_size = 8, }, }; @@ -1206,6 +1208,11 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) int64_t bs_size; uint8_t *pci_conf; + if (!n->num_queues) { + error_setg(errp, "num_queues can't be zero"); + return; + } + if (!n->conf.blk) { error_setg(errp, "drive property not set"); return; @@ -1217,7 +1224,6 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) return; } - blkconf_serial(&n->conf, &n->serial); if (!n->serial) { error_setg(errp, "serial property not set"); return; @@ -1232,7 +1238,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) pci_conf[PCI_INTERRUPT_PIN] = 1; pci_config_set_prog_interface(pci_dev->config, 0x2); pci_config_set_class(pci_dev->config, PCI_CLASS_STORAGE_EXPRESS); - pcie_endpoint_cap_init(&n->parent_obj, 0x80); + pcie_endpoint_cap_init(pci_dev, 0x80); n->num_namespaces = 1; n->reg_size = pow2ceil(0x1004 + 2 * (n->num_queues + 1) * 4); @@ -1244,10 +1250,10 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) memory_region_init_io(&n->iomem, OBJECT(n), &nvme_mmio_ops, n, "nvme", n->reg_size); - pci_register_bar(&n->parent_obj, 0, + pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64, &n->iomem); - msix_init_exclusive_bar(&n->parent_obj, n->num_queues, 4, NULL); + msix_init_exclusive_bar(pci_dev, n->num_queues, 4, NULL); id->vid = cpu_to_le16(pci_get_word(pci_conf + PCI_VENDOR_ID)); id->ssvid = cpu_to_le16(pci_get_word(pci_conf + PCI_SUBSYSTEM_VENDOR_ID)); @@ -1302,7 +1308,7 @@ static void nvme_realize(PCIDevice *pci_dev, Error **errp) n->cmbuf = g_malloc0(NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); memory_region_init_io(&n->ctrl_mem, OBJECT(n), &nvme_cmb_ops, n, "nvme-cmb", NVME_CMBSZ_GETSIZE(n->bar.cmbsz)); - pci_register_bar(&n->parent_obj, NVME_CMBLOC_BIR(n->bar.cmbloc), + pci_register_bar(pci_dev, NVME_CMBLOC_BIR(n->bar.cmbloc), PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_TYPE_64 | PCI_BASE_ADDRESS_MEM_PREFETCH, &n->ctrl_mem); @@ -1332,10 +1338,10 @@ static void nvme_exit(PCIDevice *pci_dev) g_free(n->namespaces); g_free(n->cq); g_free(n->sq); - if (n->cmbsz) { - memory_region_unref(&n->ctrl_mem); - } + if (n->cmb_size_mb) { + g_free(n->cmbuf); + } msix_uninit_exclusive_bar(pci_dev); } @@ -1380,7 +1386,7 @@ static void nvme_instance_init(Object *obj) } static const TypeInfo nvme_info = { - .name = "nvme", + .name = TYPE_NVME, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(NvmeCtrl), .class_init = nvme_class_init, diff --git a/hw/block/nvme.h b/hw/block/nvme.h index cabcf20c32..56c9d4b4b1 100644 --- a/hw/block/nvme.h +++ b/hw/block/nvme.h @@ -29,8 +29,8 @@ typedef struct NvmeSQueue { uint64_t dma_addr; QEMUTimer *timer; NvmeRequest *io_req; - QTAILQ_HEAD(sq_req_list, NvmeRequest) req_list; - QTAILQ_HEAD(out_req_list, NvmeRequest) out_req_list; + QTAILQ_HEAD(, NvmeRequest) req_list; + QTAILQ_HEAD(, NvmeRequest) out_req_list; QTAILQ_ENTRY(NvmeSQueue) entry; } NvmeSQueue; @@ -45,8 +45,8 @@ typedef struct NvmeCQueue { uint32_t size; uint64_t dma_addr; QEMUTimer *timer; - QTAILQ_HEAD(sq_list, NvmeSQueue) sq_list; - QTAILQ_HEAD(cq_req_list, NvmeRequest) req_list; + QTAILQ_HEAD(, NvmeSQueue) sq_list; + QTAILQ_HEAD(, NvmeRequest) req_list; } NvmeCQueue; typedef struct NvmeNamespace { diff --git a/hw/block/onenand.c b/hw/block/onenand.c index 0cb8d7fa13..f11118a687 100644 --- a/hw/block/onenand.c +++ b/hw/block/onenand.c @@ -28,6 +28,7 @@ #include "exec/memory.h" #include "hw/sysbus.h" #include "qemu/error-report.h" +#include "qemu/log.h" /* 11 for 2kB-page OneNAND ("2nd generation") and 10 for 1kB-page chips */ #define PAGE_SHIFT 11 @@ -594,8 +595,8 @@ static void onenand_command(OneNANDState *s) default: s->status |= ONEN_ERR_CMD; s->intstatus |= ONEN_INT; - fprintf(stderr, "%s: unknown OneNAND command %x\n", - __func__, s->command); + qemu_log_mask(LOG_GUEST_ERROR, "unknown OneNAND command %x\n", + s->command); } onenand_intr_update(s); @@ -608,7 +609,7 @@ static uint64_t onenand_read(void *opaque, hwaddr addr, int offset = addr >> s->shift; switch (offset) { - case 0x0000 ... 0xc000: + case 0x0000 ... 0xbffe: return lduw_le_p(s->boot[0] + addr); case 0xf000: /* Manufacturer ID */ @@ -657,12 +658,13 @@ static uint64_t onenand_read(void *opaque, hwaddr addr, case 0xff02: /* ECC Result of spare area data */ case 0xff03: /* ECC Result of main area data */ case 0xff04: /* ECC Result of spare area data */ - hw_error("%s: implement ECC\n", __func__); + qemu_log_mask(LOG_UNIMP, + "onenand: ECC result registers unimplemented\n"); return 0x0000; } - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, "read of unknown OneNAND register 0x%x\n", + offset); return 0; } @@ -706,8 +708,9 @@ static void onenand_write(void *opaque, hwaddr addr, break; default: - fprintf(stderr, "%s: unknown OneNAND boot command %"PRIx64"\n", - __func__, value); + qemu_log_mask(LOG_GUEST_ERROR, + "unknown OneNAND boot command %" PRIx64 "\n", + value); } break; @@ -757,8 +760,9 @@ static void onenand_write(void *opaque, hwaddr addr, break; default: - fprintf(stderr, "%s: unknown OneNAND register %x\n", - __func__, offset); + qemu_log_mask(LOG_GUEST_ERROR, + "write to unknown OneNAND register 0x%x\n", + offset); } } @@ -768,9 +772,9 @@ static const MemoryRegionOps onenand_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int onenand_initfn(SysBusDevice *sbd) +static void onenand_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); OneNANDState *s = ONE_NAND(dev); uint32_t size = 1 << (24 + ((s->id.dev >> 4) & 7)); void *ram; @@ -790,14 +794,14 @@ static int onenand_initfn(SysBusDevice *sbd) 0xff, size + (size >> 5)); } else { if (blk_is_read_only(s->blk)) { - error_report("Can't use a read-only drive"); - return -1; + error_setg(errp, "Can't use a read-only drive"); + return; } blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, BLK_PERM_ALL, &local_err); if (local_err) { - error_report_err(local_err); - return -1; + error_propagate(errp, local_err); + return; } s->blk_cur = s->blk; } @@ -822,7 +826,6 @@ static int onenand_initfn(SysBusDevice *sbd) | ((s->id.dev & 0xff) << 8) | (s->id.ver & 0xff), &vmstate_onenand, s); - return 0; } static Property onenand_properties[] = { @@ -837,9 +840,8 @@ static Property onenand_properties[] = { static void onenand_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = onenand_initfn; + dc->realize = onenand_realize; dc->reset = onenand_system_reset; dc->props = onenand_properties; } diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index bffb4c40e7..16dfae14b8 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -38,6 +38,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/block/block.h" #include "hw/block/flash.h" #include "sysemu/block-backend.h" #include "qapi/error.h" @@ -49,12 +50,6 @@ #include "sysemu/sysemu.h" #include "trace.h" -#define PFLASH_BUG(fmt, ...) \ -do { \ - fprintf(stderr, "PFLASH: Possible BUG - " fmt, ## __VA_ARGS__); \ - exit(1); \ -} while(0) - /* #define PFLASH_DEBUG */ #ifdef PFLASH_DEBUG #define DPRINTF(fmt, ...) \ @@ -65,12 +60,10 @@ do { \ #define DPRINTF(fmt, ...) do { } while (0) #endif -#define CFI_PFLASH01(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH01) - #define PFLASH_BE 0 #define PFLASH_SECURE 1 -struct pflash_t { +struct PFlashCFI01 { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ @@ -109,17 +102,17 @@ static const VMStateDescription vmstate_pflash = { .minimum_version_id = 1, .post_load = pflash_post_load, .fields = (VMStateField[]) { - VMSTATE_UINT8(wcycle, pflash_t), - VMSTATE_UINT8(cmd, pflash_t), - VMSTATE_UINT8(status, pflash_t), - VMSTATE_UINT64(counter, pflash_t), + VMSTATE_UINT8(wcycle, PFlashCFI01), + VMSTATE_UINT8(cmd, PFlashCFI01), + VMSTATE_UINT8(status, PFlashCFI01), + VMSTATE_UINT64(counter, PFlashCFI01), VMSTATE_END_OF_LIST() } }; static void pflash_timer (void *opaque) { - pflash_t *pfl = opaque; + PFlashCFI01 *pfl = opaque; trace_pflash_timer_expired(pfl->cmd); /* Reset flash */ @@ -133,7 +126,7 @@ static void pflash_timer (void *opaque) * If this code is called we know we have a device_width set for * this flash. */ -static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) +static uint32_t pflash_cfi_query(PFlashCFI01 *pfl, hwaddr offset) { int i; uint32_t resp = 0; @@ -193,7 +186,7 @@ static uint32_t pflash_cfi_query(pflash_t *pfl, hwaddr offset) /* Perform a device id query based on the bank width of the flash. */ -static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) +static uint32_t pflash_devid_query(PFlashCFI01 *pfl, hwaddr offset) { int i; uint32_t resp; @@ -241,7 +234,7 @@ static uint32_t pflash_devid_query(pflash_t *pfl, hwaddr offset) return resp; } -static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, +static uint32_t pflash_data_read(PFlashCFI01 *pfl, hwaddr offset, int width, int be) { uint8_t *p; @@ -284,8 +277,8 @@ static uint32_t pflash_data_read(pflash_t *pfl, hwaddr offset, return ret; } -static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, - int width, int be) +static uint32_t pflash_read(PFlashCFI01 *pfl, hwaddr offset, + int width, int be) { hwaddr boff; uint32_t ret; @@ -398,7 +391,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, } /* update flash content on disk */ -static void pflash_update(pflash_t *pfl, int offset, +static void pflash_update(PFlashCFI01 *pfl, int offset, int size) { int offset_end; @@ -412,7 +405,7 @@ static void pflash_update(pflash_t *pfl, int offset, } } -static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, +static inline void pflash_data_write(PFlashCFI01 *pfl, hwaddr offset, uint32_t value, int width, int be) { uint8_t *p = pfl->storage; @@ -448,7 +441,7 @@ static inline void pflash_data_write(pflash_t *pfl, hwaddr offset, } -static void pflash_write(pflash_t *pfl, hwaddr offset, +static void pflash_write(PFlashCFI01 *pfl, hwaddr offset, uint32_t value, int width, int be) { uint8_t *p; @@ -507,6 +500,10 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, break; case 0xe8: /* Write to buffer */ DPRINTF("%s: Write to buffer\n", __func__); + /* FIXME should save @offset, @width for case 1+ */ + qemu_log_mask(LOG_UNIMP, + "%s: Write to buffer emulation is flawed\n", + __func__); pfl->status |= 0x80; /* Ready! */ break; case 0xf0: /* Probe for AMD flash */ @@ -550,6 +547,7 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, /* Mask writeblock size based on device width, or bank width if * device width not specified. */ + /* FIXME check @offset, @width */ if (pfl->device_width) { value = extract32(value, 0, pfl->device_width * 8); } else { @@ -587,7 +585,13 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, case 2: switch (pfl->cmd) { case 0xe8: /* Block write */ + /* FIXME check @offset, @width */ if (!pfl->ro) { + /* + * FIXME writing straight to memory is *wrong*. We + * should write to a buffer, and flush it to memory + * only on confirm command (see below). + */ pflash_data_write(pfl, offset, value, width, be); } else { pfl->status |= 0x10; /* Programming error */ @@ -603,6 +607,7 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, pfl->wcycle++; if (!pfl->ro) { /* Flush the entire write buffer onto backing storage. */ + /* FIXME premature! */ pflash_update(pfl, offset & mask, pfl->writeblock_size); } else { pfl->status |= 0x10; /* Programming error */ @@ -619,11 +624,15 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, switch (pfl->cmd) { case 0xe8: /* Block write */ if (cmd == 0xd0) { + /* FIXME this is where we should write out the buffer */ pfl->wcycle = 0; pfl->status |= 0x80; } else { - DPRINTF("%s: unknown command for \"write block\"\n", __func__); - PFLASH_BUG("Write block confirm"); + qemu_log_mask(LOG_UNIMP, + "%s: Aborting write to buffer not implemented," + " the data is already written to storage!\n" + "Flash device reset into READ mode.\n", + __func__); goto reset_flash; } break; @@ -654,7 +663,7 @@ static void pflash_write(pflash_t *pfl, hwaddr offset, static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_t *value, unsigned len, MemTxAttrs attrs) { - pflash_t *pfl = opaque; + PFlashCFI01 *pfl = opaque; bool be = !!(pfl->features & (1 << PFLASH_BE)); if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { @@ -668,7 +677,7 @@ static MemTxResult pflash_mem_read_with_attrs(void *opaque, hwaddr addr, uint64_ static MemTxResult pflash_mem_write_with_attrs(void *opaque, hwaddr addr, uint64_t value, unsigned len, MemTxAttrs attrs) { - pflash_t *pfl = opaque; + PFlashCFI01 *pfl = opaque; bool be = !!(pfl->features & (1 << PFLASH_BE)); if ((pfl->features & (1 << PFLASH_SECURE)) && !attrs.secure) { @@ -687,7 +696,7 @@ static const MemoryRegionOps pflash_cfi01_ops = { static void pflash_cfi01_realize(DeviceState *dev, Error **errp) { - pflash_t *pfl = CFI_PFLASH01(dev); + PFlashCFI01 *pfl = PFLASH_CFI01(dev); uint64_t total_len; int ret; uint64_t blocks_per_device, sector_len_per_device, device_len; @@ -722,13 +731,6 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) } device_len = sector_len_per_device * blocks_per_device; - /* XXX: to be fixed */ -#if 0 - if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && - total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024)) - return NULL; -#endif - memory_region_init_rom_device( &pfl->mem, OBJECT(dev), &pflash_cfi01_ops, @@ -755,12 +757,9 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) } if (pfl->blk) { - /* read the initial flash content */ - ret = blk_pread(pfl->blk, 0, pfl->storage, total_len); - - if (ret < 0) { + if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, total_len, + errp)) { vmstate_unregister_ram(&pfl->mem, DEVICE(pfl)); - error_setg(errp, "failed to read the initial flash content"); return; } } @@ -864,14 +863,14 @@ static void pflash_cfi01_realize(DeviceState *dev, Error **errp) } static Property pflash_cfi01_properties[] = { - DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), + DEFINE_PROP_DRIVE("drive", PFlashCFI01, blk), /* num-blocks is the number of blocks actually visible to the guest, * ie the total size of the device divided by the sector length. * If we're emulating flash devices wired in parallel the actual * number of blocks per indvidual device will differ. */ - DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), - DEFINE_PROP_UINT64("sector-length", struct pflash_t, sector_len, 0), + DEFINE_PROP_UINT32("num-blocks", PFlashCFI01, nb_blocs, 0), + DEFINE_PROP_UINT64("sector-length", PFlashCFI01, sector_len, 0), /* width here is the overall width of this QEMU device in bytes. * The QEMU device may be emulating a number of flash devices * wired up in parallel; the width of each individual flash @@ -888,17 +887,17 @@ static Property pflash_cfi01_properties[] = { * 16 bit devices making up a 32 bit wide QEMU device. This * is deprecated for new uses of this device. */ - DEFINE_PROP_UINT8("width", struct pflash_t, bank_width, 0), - DEFINE_PROP_UINT8("device-width", struct pflash_t, device_width, 0), - DEFINE_PROP_UINT8("max-device-width", struct pflash_t, max_device_width, 0), - DEFINE_PROP_BIT("big-endian", struct pflash_t, features, PFLASH_BE, 0), - DEFINE_PROP_BIT("secure", struct pflash_t, features, PFLASH_SECURE, 0), - DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), - DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), - DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), - DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), - DEFINE_PROP_STRING("name", struct pflash_t, name), - DEFINE_PROP_BOOL("old-multiple-chip-handling", struct pflash_t, + DEFINE_PROP_UINT8("width", PFlashCFI01, bank_width, 0), + DEFINE_PROP_UINT8("device-width", PFlashCFI01, device_width, 0), + DEFINE_PROP_UINT8("max-device-width", PFlashCFI01, max_device_width, 0), + DEFINE_PROP_BIT("big-endian", PFlashCFI01, features, PFLASH_BE, 0), + DEFINE_PROP_BIT("secure", PFlashCFI01, features, PFLASH_SECURE, 0), + DEFINE_PROP_UINT16("id0", PFlashCFI01, ident0, 0), + DEFINE_PROP_UINT16("id1", PFlashCFI01, ident1, 0), + DEFINE_PROP_UINT16("id2", PFlashCFI01, ident2, 0), + DEFINE_PROP_UINT16("id3", PFlashCFI01, ident3, 0), + DEFINE_PROP_STRING("name", PFlashCFI01, name), + DEFINE_PROP_BOOL("old-multiple-chip-handling", PFlashCFI01, old_multiple_chip_handling, false), DEFINE_PROP_END_OF_LIST(), }; @@ -915,9 +914,9 @@ static void pflash_cfi01_class_init(ObjectClass *klass, void *data) static const TypeInfo pflash_cfi01_info = { - .name = TYPE_CFI_PFLASH01, + .name = TYPE_PFLASH_CFI01, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct pflash_t), + .instance_size = sizeof(PFlashCFI01), .class_init = pflash_cfi01_class_init, }; @@ -928,20 +927,23 @@ static void pflash_cfi01_register_types(void) type_init(pflash_cfi01_register_types) -pflash_t *pflash_cfi01_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, - uint32_t sector_len, int nb_blocs, - int bank_width, uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, int be) +PFlashCFI01 *pflash_cfi01_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, + int bank_width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, + int be) { - DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH01); + DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); if (blk) { qdev_prop_set_drive(dev, "drive", blk, &error_abort); } - qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); + assert(size % sector_len == 0); + qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); qdev_prop_set_uint64(dev, "sector-length", sector_len); qdev_prop_set_uint8(dev, "width", bank_width); qdev_prop_set_bit(dev, "big-endian", !!be); @@ -953,17 +955,22 @@ pflash_t *pflash_cfi01_register(hwaddr base, qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return CFI_PFLASH01(dev); + return PFLASH_CFI01(dev); } -MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl) +BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl) +{ + return fl->blk; +} + +MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl) { return &fl->mem; } static void postload_update_cb(void *opaque, int running, RunState state) { - pflash_t *pfl = opaque; + PFlashCFI01 *pfl = opaque; /* This is called after bdrv_invalidate_cache_all. */ qemu_del_vm_change_state_handler(pfl->vmstate); @@ -975,7 +982,7 @@ static void postload_update_cb(void *opaque, int running, RunState state) static int pflash_post_load(void *opaque, int version_id) { - pflash_t *pfl = opaque; + PFlashCFI01 *pfl = opaque; if (!pfl->ro) { pfl->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 0f8b7b8c7b..f2c6201f81 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -37,6 +37,7 @@ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/block/block.h" #include "hw/block/flash.h" #include "qapi/error.h" #include "qemu/timer.h" @@ -57,9 +58,7 @@ do { \ #define PFLASH_LAZY_ROMD_THRESHOLD 42 -#define CFI_PFLASH02(obj) OBJECT_CHECK(pflash_t, (obj), TYPE_CFI_PFLASH02) - -struct pflash_t { +struct PFlashCFI02 { /*< private >*/ SysBusDevice parent_obj; /*< public >*/ @@ -84,7 +83,7 @@ struct pflash_t { uint16_t unlock_addr0; uint16_t unlock_addr1; uint8_t cfi_table[0x52]; - QEMUTimer *timer; + QEMUTimer timer; /* The device replicates the flash memory across its memory space. Emulate * that by having a container (.mem) filled with an array of aliases * (.mem_mappings) pointing to the flash memory (.orig_mem). @@ -101,7 +100,7 @@ struct pflash_t { /* * Set up replicated mappings of the same region. */ -static void pflash_setup_mappings(pflash_t *pfl) +static void pflash_setup_mappings(PFlashCFI02 *pfl) { unsigned i; hwaddr size = memory_region_size(&pfl->orig_mem); @@ -115,7 +114,7 @@ static void pflash_setup_mappings(pflash_t *pfl) } } -static void pflash_register_memory(pflash_t *pfl, int rom_mode) +static void pflash_register_memory(PFlashCFI02 *pfl, int rom_mode) { memory_region_rom_device_set_romd(&pfl->orig_mem, rom_mode); pfl->rom_mode = rom_mode; @@ -123,7 +122,7 @@ static void pflash_register_memory(pflash_t *pfl, int rom_mode) static void pflash_timer (void *opaque) { - pflash_t *pfl = opaque; + PFlashCFI02 *pfl = opaque; trace_pflash_timer_expired(pfl->cmd); /* Reset flash */ @@ -137,8 +136,8 @@ static void pflash_timer (void *opaque) pfl->cmd = 0; } -static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, - int width, int be) +static uint32_t pflash_read(PFlashCFI02 *pfl, hwaddr offset, + int width, int be) { hwaddr boff; uint32_t ret; @@ -246,7 +245,7 @@ static uint32_t pflash_read (pflash_t *pfl, hwaddr offset, } /* update flash content on disk */ -static void pflash_update(pflash_t *pfl, int offset, +static void pflash_update(PFlashCFI02 *pfl, int offset, int size) { int offset_end; @@ -260,8 +259,8 @@ static void pflash_update(pflash_t *pfl, int offset, } } -static void pflash_write (pflash_t *pfl, hwaddr offset, - uint32_t value, int width, int be) +static void pflash_write(PFlashCFI02 *pfl, hwaddr offset, + uint32_t value, int width, int be) { hwaddr boff; uint8_t *p; @@ -429,7 +428,7 @@ 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) + + timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (NANOSECONDS_PER_SECOND * 5)); break; case 0x30: @@ -444,7 +443,7 @@ 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) + + timer_mod(&pfl->timer, qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + (NANOSECONDS_PER_SECOND / 2)); break; default: @@ -533,7 +532,7 @@ static const MemoryRegionOps pflash_cfi02_ops_le = { static void pflash_cfi02_realize(DeviceState *dev, Error **errp) { - pflash_t *pfl = CFI_PFLASH02(dev); + PFlashCFI02 *pfl = PFLASH_CFI02(dev); uint32_t chip_len; int ret; Error *local_err = NULL; @@ -552,12 +551,6 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) } chip_len = pfl->sector_len * pfl->nb_blocs; - /* XXX: to be fixed */ -#if 0 - if (total_len != (8 * 1024 * 1024) && total_len != (16 * 1024 * 1024) && - total_len != (32 * 1024 * 1024) && total_len != (64 * 1024 * 1024)) - return NULL; -#endif memory_region_init_rom_device(&pfl->orig_mem, OBJECT(pfl), pfl->be ? &pflash_cfi02_ops_be : &pflash_cfi02_ops_le, @@ -583,11 +576,9 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) } if (pfl->blk) { - /* read the initial flash content */ - ret = blk_pread(pfl->blk, 0, pfl->storage, chip_len); - if (ret < 0) { + if (!blk_check_size_and_read_all(pfl->blk, pfl->storage, chip_len, + errp)) { vmstate_unregister_ram(&pfl->orig_mem, DEVICE(pfl)); - error_setg(errp, "failed to read the initial flash content"); return; } } @@ -596,7 +587,7 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) pfl->rom_mode = 1; sysbus_init_mmio(SYS_BUS_DEVICE(dev), &pfl->mem); - pfl->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); + timer_init_ns(&pfl->timer, QEMU_CLOCK_VIRTUAL, pflash_timer, pfl); pfl->wcycle = 0; pfl->cmd = 0; pfl->status = 0; @@ -679,35 +670,42 @@ static void pflash_cfi02_realize(DeviceState *dev, Error **errp) } static Property pflash_cfi02_properties[] = { - DEFINE_PROP_DRIVE("drive", struct pflash_t, blk), - DEFINE_PROP_UINT32("num-blocks", struct pflash_t, nb_blocs, 0), - DEFINE_PROP_UINT32("sector-length", struct pflash_t, sector_len, 0), - DEFINE_PROP_UINT8("width", struct pflash_t, width, 0), - DEFINE_PROP_UINT8("mappings", struct pflash_t, mappings, 0), - DEFINE_PROP_UINT8("big-endian", struct pflash_t, be, 0), - DEFINE_PROP_UINT16("id0", struct pflash_t, ident0, 0), - DEFINE_PROP_UINT16("id1", struct pflash_t, ident1, 0), - DEFINE_PROP_UINT16("id2", struct pflash_t, ident2, 0), - DEFINE_PROP_UINT16("id3", struct pflash_t, ident3, 0), - DEFINE_PROP_UINT16("unlock-addr0", struct pflash_t, unlock_addr0, 0), - DEFINE_PROP_UINT16("unlock-addr1", struct pflash_t, unlock_addr1, 0), - DEFINE_PROP_STRING("name", struct pflash_t, name), + DEFINE_PROP_DRIVE("drive", PFlashCFI02, blk), + DEFINE_PROP_UINT32("num-blocks", PFlashCFI02, nb_blocs, 0), + DEFINE_PROP_UINT32("sector-length", PFlashCFI02, sector_len, 0), + DEFINE_PROP_UINT8("width", PFlashCFI02, width, 0), + DEFINE_PROP_UINT8("mappings", PFlashCFI02, mappings, 0), + DEFINE_PROP_UINT8("big-endian", PFlashCFI02, be, 0), + DEFINE_PROP_UINT16("id0", PFlashCFI02, ident0, 0), + DEFINE_PROP_UINT16("id1", PFlashCFI02, ident1, 0), + DEFINE_PROP_UINT16("id2", PFlashCFI02, ident2, 0), + DEFINE_PROP_UINT16("id3", PFlashCFI02, ident3, 0), + DEFINE_PROP_UINT16("unlock-addr0", PFlashCFI02, unlock_addr0, 0), + DEFINE_PROP_UINT16("unlock-addr1", PFlashCFI02, unlock_addr1, 0), + DEFINE_PROP_STRING("name", PFlashCFI02, name), DEFINE_PROP_END_OF_LIST(), }; +static void pflash_cfi02_unrealize(DeviceState *dev, Error **errp) +{ + PFlashCFI02 *pfl = PFLASH_CFI02(dev); + timer_del(&pfl->timer); +} + static void pflash_cfi02_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pflash_cfi02_realize; + dc->unrealize = pflash_cfi02_unrealize; dc->props = pflash_cfi02_properties; set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo pflash_cfi02_info = { - .name = TYPE_CFI_PFLASH02, + .name = TYPE_PFLASH_CFI02, .parent = TYPE_SYS_BUS_DEVICE, - .instance_size = sizeof(struct pflash_t), + .instance_size = sizeof(PFlashCFI02), .class_init = pflash_cfi02_class_init, }; @@ -718,22 +716,25 @@ static void pflash_cfi02_register_types(void) type_init(pflash_cfi02_register_types) -pflash_t *pflash_cfi02_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, uint32_t sector_len, - int nb_blocs, int nb_mappings, int width, - uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, - uint16_t unlock_addr0, uint16_t unlock_addr1, - int be) +PFlashCFI02 *pflash_cfi02_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, + int nb_mappings, int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, + uint16_t unlock_addr0, + uint16_t unlock_addr1, + int be) { - DeviceState *dev = qdev_create(NULL, TYPE_CFI_PFLASH02); + DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI02); if (blk) { qdev_prop_set_drive(dev, "drive", blk, &error_abort); } - qdev_prop_set_uint32(dev, "num-blocks", nb_blocs); + assert(size % sector_len == 0); + qdev_prop_set_uint32(dev, "num-blocks", size / sector_len); qdev_prop_set_uint32(dev, "sector-length", sector_len); qdev_prop_set_uint8(dev, "width", width); qdev_prop_set_uint8(dev, "mappings", nb_mappings); @@ -748,5 +749,5 @@ pflash_t *pflash_cfi02_register(hwaddr base, qdev_init_nofail(dev); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - return CFI_PFLASH02(dev); + return PFLASH_CFI02(dev); } diff --git a/hw/block/tc58128.c b/hw/block/tc58128.c index 808ad76ba6..d0fae248dc 100644 --- a/hw/block/tc58128.c +++ b/hw/block/tc58128.c @@ -38,7 +38,8 @@ static void init_dev(tc58128_dev * dev, const char *filename) memset(dev->flash_contents, 0xff, FLASH_SIZE); if (filename) { /* Load flash image skipping the first block */ - ret = load_image(filename, dev->flash_contents + 528 * 32); + ret = load_image_size(filename, dev->flash_contents + 528 * 32, + FLASH_SIZE - 528 * 32); if (ret < 0) { if (!qtest_enabled()) { error_report("Could not load flash image %s", filename); diff --git a/hw/block/trace-events b/hw/block/trace-events index 335c092450..b92039a573 100644 --- a/hw/block/trace-events +++ b/hw/block/trace-events @@ -1,10 +1,11 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/block/fdc.c +# fdc.c fdc_ioport_read(uint8_t reg, uint8_t value) "read reg 0x%02x val 0x%02x" fdc_ioport_write(uint8_t reg, uint8_t value) "write reg 0x%02x val 0x%02x" -# hw/block/pflash_cfi0?.c +# pflash_cfi02.c +# pflash_cfi01.c pflash_reset(void) "reset" pflash_read(uint64_t offset, uint8_t cmd, int width, uint8_t wcycle) "offset:0x%04"PRIx64" cmd:0x%02x width:%d wcycle:%u" pflash_write(uint64_t offset, uint32_t value, int width, uint8_t wcycle) "offset:0x%04"PRIx64" value:0x%03x width:%d wcycle:%u" @@ -17,18 +18,18 @@ pflash_manufacturer_id(uint16_t id) "Read Manufacturer ID: 0x%04x" pflash_device_id(uint16_t id) "Read Device ID: 0x%04x" pflash_device_info(uint64_t offset) "Read Device Information offset:0x%04"PRIx64 -# hw/block/virtio-blk.c +# virtio-blk.c virtio_blk_req_complete(void *vdev, void *req, int status) "vdev %p req %p status %d" virtio_blk_rw_complete(void *vdev, void *req, int ret) "vdev %p req %p ret %d" virtio_blk_handle_write(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu" virtio_blk_handle_read(void *vdev, void *req, uint64_t sector, size_t nsectors) "vdev %p req %p sector %"PRIu64" nsectors %zu" virtio_blk_submit_multireq(void *vdev, void *mrb, int start, int num_reqs, uint64_t offset, size_t size, bool is_write) "vdev %p mrb %p start %d num_reqs %d offset %"PRIu64" size %zu is_write %d" -# hw/block/hd-geometry.c +# hd-geometry.c hd_geometry_lchs_guess(void *blk, int cyls, int heads, int secs) "blk %p LCHS %d %d %d" hd_geometry_guess(void *blk, uint32_t cyls, uint32_t heads, uint32_t secs, int trans) "blk %p CHS %u %u %u trans %d" -# hw/block/nvme.c +# nvme.c # nvme traces for successful events nvme_irq_msix(uint32_t vector) "raising MSI-X IRQ vector %u" nvme_irq_pin(void) "pulsing IRQ pin" @@ -63,9 +64,7 @@ nvme_err_invalid_dma(void) "PRP/SGL is too small for transfer size" nvme_err_invalid_prplist_ent(uint64_t prplist) "PRP list entry is null or not page aligned: 0x%"PRIx64"" nvme_err_invalid_prp2_align(uint64_t prp2) "PRP2 is not page aligned: 0x%"PRIx64"" nvme_err_invalid_prp2_missing(void) "PRP2 is null and more data to be transferred" -nvme_err_invalid_field(void) "invalid field" nvme_err_invalid_prp(void) "invalid PRP" -nvme_err_invalid_sgl(void) "invalid SGL" nvme_err_invalid_ns(uint32_t ns, uint32_t limit) "invalid namespace %u not within 1-%u" nvme_err_invalid_opc(uint8_t opc) "invalid opcode 0x%"PRIx8"" nvme_err_invalid_admin_opc(uint8_t opc) "invalid admin opcode 0x%"PRIx8"" @@ -121,9 +120,17 @@ nvme_ub_db_wr_invalid_cqhead(uint32_t qid, uint16_t new_head) "completion queue nvme_ub_db_wr_invalid_sq(uint32_t qid) "submission queue doorbell write for nonexistent queue, sqid=%"PRIu32", ignoring" nvme_ub_db_wr_invalid_sqtail(uint32_t qid, uint16_t new_tail) "submission queue doorbell write value beyond queue size, sqid=%"PRIu32", new_head=%"PRIu16", ignoring" -# hw/block/xen_disk.c -xen_disk_alloc(char *name) "%s" -xen_disk_init(char *name) "%s" -xen_disk_connect(char *name) "%s" -xen_disk_disconnect(char *name) "%s" -xen_disk_free(char *name) "%s" +# xen-block.c +xen_block_realize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" +xen_block_connect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" +xen_block_disconnect(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" +xen_block_unrealize(const char *type, uint32_t disk, uint32_t partition) "%s d%up%u" +xen_block_size(const char *type, uint32_t disk, uint32_t partition, int64_t sectors) "%s d%up%u %"PRIi64 +xen_disk_realize(void) "" +xen_disk_unrealize(void) "" +xen_cdrom_realize(void) "" +xen_cdrom_unrealize(void) "" +xen_block_blockdev_add(char *str) "%s" +xen_block_blockdev_del(const char *node_name) "%s" +xen_block_device_create(unsigned int number) "%u" +xen_block_device_destroy(unsigned int number) "%u" diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index d755223643..28b81368f7 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -38,6 +38,8 @@ static const int user_feature_bits[] = { VIRTIO_BLK_F_RO, VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_CONFIG_WCE, + VIRTIO_BLK_F_DISCARD, + VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_F_VERSION_1, VIRTIO_RING_F_INDIRECT_DESC, VIRTIO_RING_F_EVENT_IDX, @@ -126,6 +128,21 @@ static void vhost_user_blk_start(VirtIODevice *vdev) } s->dev.acked_features = vdev->guest_features; + + if (!s->inflight->addr) { + ret = vhost_dev_get_inflight(&s->dev, s->queue_size, s->inflight); + if (ret < 0) { + error_report("Error get inflight: %d", -ret); + goto err_guest_notifiers; + } + } + + ret = vhost_dev_set_inflight(&s->dev, s->inflight); + if (ret < 0) { + error_report("Error set inflight: %d", -ret); + goto err_guest_notifiers; + } + ret = vhost_dev_start(&s->dev, vdev); if (ret < 0) { error_report("Error starting vhost: %d", -ret); @@ -204,6 +221,8 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, virtio_add_feature(&features, VIRTIO_BLK_F_BLK_SIZE); virtio_add_feature(&features, VIRTIO_BLK_F_FLUSH); virtio_add_feature(&features, VIRTIO_BLK_F_RO); + virtio_add_feature(&features, VIRTIO_BLK_F_DISCARD); + virtio_add_feature(&features, VIRTIO_BLK_F_WRITE_ZEROES); if (s->config_wce) { virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); @@ -217,14 +236,46 @@ static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, static void vhost_user_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) { + VHostUserBlk *s = VHOST_USER_BLK(vdev); + int i; + if (!(virtio_host_has_feature(vdev, VIRTIO_F_VERSION_1) && + !virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1))) { + return; + } + + if (s->dev.started) { + return; + } + + /* Some guests kick before setting VIRTIO_CONFIG_S_DRIVER_OK so start + * vhost here instead of waiting for .set_status(). + */ + vhost_user_blk_start(vdev); + + /* Kick right away to begin processing requests already in vring */ + for (i = 0; i < s->dev.nvqs; i++) { + VirtQueue *kick_vq = virtio_get_queue(vdev, i); + + if (!virtio_queue_get_desc_addr(vdev, i)) { + continue; + } + event_notifier_set(virtio_queue_get_host_notifier(kick_vq)); + } +} + +static void vhost_user_blk_reset(VirtIODevice *vdev) +{ + VHostUserBlk *s = VHOST_USER_BLK(vdev); + + vhost_dev_free_inflight(s->inflight); } static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(vdev); - VhostUserState *user; + struct vhost_virtqueue *vqs = NULL; int i, ret; if (!s->chardev.chr) { @@ -242,15 +293,10 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) return; } - user = vhost_user_init(); - if (!user) { - error_setg(errp, "vhost-user-blk: failed to init vhost_user"); + if (!vhost_user_init(&s->vhost_user, &s->chardev, errp)) { return; } - user->chr = &s->chardev; - s->vhost_user = user; - virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, sizeof(struct virtio_blk_config)); @@ -259,14 +305,17 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) vhost_user_blk_handle_output); } + s->inflight = g_new0(struct vhost_inflight, 1); + s->dev.nvqs = s->num_queues; s->dev.vqs = g_new(struct vhost_virtqueue, s->dev.nvqs); s->dev.vq_index = 0; s->dev.backend_features = 0; + vqs = s->dev.vqs; vhost_dev_set_config_notifier(&s->dev, &blk_ops); - ret = vhost_dev_init(&s->dev, s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); + ret = vhost_dev_init(&s->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { error_setg(errp, "vhost-user-blk: vhost initialization failed: %s", strerror(-ret)); @@ -289,29 +338,25 @@ static void vhost_user_blk_device_realize(DeviceState *dev, Error **errp) vhost_err: vhost_dev_cleanup(&s->dev); virtio_err: - g_free(s->dev.vqs); + g_free(vqs); + g_free(s->inflight); virtio_cleanup(vdev); - - vhost_user_cleanup(user); - g_free(user); - s->vhost_user = NULL; + vhost_user_cleanup(&s->vhost_user); } static void vhost_user_blk_device_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserBlk *s = VHOST_USER_BLK(dev); + struct vhost_virtqueue *vqs = s->dev.vqs; vhost_user_blk_set_status(vdev, 0); vhost_dev_cleanup(&s->dev); - g_free(s->dev.vqs); + vhost_dev_free_inflight(s->inflight); + g_free(vqs); + g_free(s->inflight); virtio_cleanup(vdev); - - if (s->vhost_user) { - vhost_user_cleanup(s->vhost_user); - g_free(s->vhost_user); - s->vhost_user = NULL; - } + vhost_user_cleanup(&s->vhost_user); } static void vhost_user_blk_instance_init(Object *obj) @@ -354,6 +399,7 @@ static void vhost_user_blk_class_init(ObjectClass *klass, void *data) vdc->set_config = vhost_user_blk_set_config; vdc->get_features = vhost_user_blk_get_features; vdc->set_status = vhost_user_blk_set_status; + vdc->reset = vhost_user_blk_reset; } static const TypeInfo vhost_user_blk_info = { diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 50b5c869e3..06e57a4d39 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -28,6 +28,29 @@ #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" +/* Config size before the discard support (hide associated config fields) */ +#define VIRTIO_BLK_CFG_SIZE offsetof(struct virtio_blk_config, \ + max_discard_sectors) +/* + * Starting from the discard feature, we can use this array to properly + * set the config size depending on the features enabled. + */ +static VirtIOFeature feature_sizes[] = { + {.flags = 1ULL << VIRTIO_BLK_F_DISCARD, + .end = virtio_endof(struct virtio_blk_config, discard_sector_alignment)}, + {.flags = 1ULL << VIRTIO_BLK_F_WRITE_ZEROES, + .end = virtio_endof(struct virtio_blk_config, write_zeroes_may_unmap)}, + {} +}; + +static void virtio_blk_set_config_size(VirtIOBlock *s, uint64_t host_features) +{ + s->config_size = MAX(VIRTIO_BLK_CFG_SIZE, + virtio_feature_get_config_size(feature_sizes, host_features)); + + assert(s->config_size <= sizeof(struct virtio_blk_config)); +} + static void virtio_blk_init_request(VirtIOBlock *s, VirtQueue *vq, VirtIOBlockReq *req) { @@ -61,11 +84,10 @@ static void virtio_blk_req_complete(VirtIOBlockReq *req, unsigned char status) } static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, - bool is_read) + bool is_read, bool acct_failed) { - BlockErrorAction action = blk_get_error_action(req->dev->blk, - is_read, error); VirtIOBlock *s = req->dev; + BlockErrorAction action = blk_get_error_action(s->blk, is_read, error); if (action == BLOCK_ERROR_ACTION_STOP) { /* Break the link as the next request is going to be parsed from the @@ -75,7 +97,9 @@ static int virtio_blk_handle_rw_error(VirtIOBlockReq *req, int error, s->rq = req; } else if (action == BLOCK_ERROR_ACTION_REPORT) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - block_acct_failed(blk_get_stats(s->blk), &req->acct); + if (acct_failed) { + block_acct_failed(blk_get_stats(s->blk), &req->acct); + } virtio_blk_free_request(req); } @@ -96,14 +120,14 @@ static void virtio_blk_rw_complete(void *opaque, int ret) trace_virtio_blk_rw_complete(vdev, req, ret); if (req->qiov.nalloc != -1) { - /* If nalloc is != 1 req->qiov is a local copy of the original - * external iovec. It was allocated in submit_merged_requests - * to be able to merge requests. */ + /* If nalloc is != -1 req->qiov is a local copy of the original + * external iovec. It was allocated in submit_requests to be + * able to merge requests. */ qemu_iovec_destroy(&req->qiov); } if (ret) { - int p = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); + int p = virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type); bool is_read = !(p & VIRTIO_BLK_T_OUT); /* Note that memory may be dirtied on read failure. If the * virtio request is not completed here, as is the case for @@ -113,13 +137,13 @@ static void virtio_blk_rw_complete(void *opaque, int ret) * the memory until the request is completed (which will * happen on the other side of the migration). */ - if (virtio_blk_handle_rw_error(req, -ret, is_read)) { + if (virtio_blk_handle_rw_error(req, -ret, is_read, true)) { continue; } } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - block_acct_done(blk_get_stats(req->dev->blk), &req->acct); + block_acct_done(blk_get_stats(s->blk), &req->acct); virtio_blk_free_request(req); } aio_context_release(blk_get_aio_context(s->conf.conf.blk)); @@ -132,13 +156,37 @@ static void virtio_blk_flush_complete(void *opaque, int ret) aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); if (ret) { - if (virtio_blk_handle_rw_error(req, -ret, 0)) { + if (virtio_blk_handle_rw_error(req, -ret, 0, true)) { goto out; } } virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); - block_acct_done(blk_get_stats(req->dev->blk), &req->acct); + block_acct_done(blk_get_stats(s->blk), &req->acct); + virtio_blk_free_request(req); + +out: + aio_context_release(blk_get_aio_context(s->conf.conf.blk)); +} + +static void virtio_blk_discard_write_zeroes_complete(void *opaque, int ret) +{ + VirtIOBlockReq *req = opaque; + VirtIOBlock *s = req->dev; + bool is_write_zeroes = (virtio_ldl_p(VIRTIO_DEVICE(s), &req->out.type) & + ~VIRTIO_BLK_T_BARRIER) == VIRTIO_BLK_T_WRITE_ZEROES; + + aio_context_acquire(blk_get_aio_context(s->conf.conf.blk)); + if (ret) { + if (virtio_blk_handle_rw_error(req, -ret, false, is_write_zeroes)) { + goto out; + } + } + + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); + if (is_write_zeroes) { + block_acct_done(blk_get_stats(s->blk), &req->acct); + } virtio_blk_free_request(req); out: @@ -212,9 +260,9 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) { int status = VIRTIO_BLK_S_OK; struct virtio_scsi_inhdr *scsi = NULL; - VirtIODevice *vdev = VIRTIO_DEVICE(req->dev); - VirtQueueElement *elem = &req->elem; VirtIOBlock *blk = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(blk); + VirtQueueElement *elem = &req->elem; #ifdef __linux__ int i; @@ -240,7 +288,7 @@ static int virtio_blk_handle_scsi_req(VirtIOBlockReq *req) */ scsi = (void *)elem->in_sg[elem->in_num - 2].iov_base; - if (!blk->conf.scsi) { + if (!virtio_has_feature(blk->host_features, VIRTIO_BLK_F_SCSI)) { status = VIRTIO_BLK_S_UNSUPP; goto fail; } @@ -444,16 +492,18 @@ static void virtio_blk_submit_multireq(BlockBackend *blk, MultiReqBuffer *mrb) static void virtio_blk_handle_flush(VirtIOBlockReq *req, MultiReqBuffer *mrb) { - block_acct_start(blk_get_stats(req->dev->blk), &req->acct, 0, + VirtIOBlock *s = req->dev; + + block_acct_start(blk_get_stats(s->blk), &req->acct, 0, BLOCK_ACCT_FLUSH); /* * Make sure all outstanding writes are posted to the backing device. */ if (mrb->is_write && mrb->num_reqs > 0) { - virtio_blk_submit_multireq(req->dev->blk, mrb); + virtio_blk_submit_multireq(s->blk, mrb); } - blk_aio_flush(req->dev->blk, virtio_blk_flush_complete, req); + blk_aio_flush(s->blk, virtio_blk_flush_complete, req); } static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, @@ -478,11 +528,89 @@ static bool virtio_blk_sect_range_ok(VirtIOBlock *dev, return true; } +static uint8_t virtio_blk_handle_discard_write_zeroes(VirtIOBlockReq *req, + struct virtio_blk_discard_write_zeroes *dwz_hdr, bool is_write_zeroes) +{ + VirtIOBlock *s = req->dev; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + uint64_t sector; + uint32_t num_sectors, flags, max_sectors; + uint8_t err_status; + int bytes; + + sector = virtio_ldq_p(vdev, &dwz_hdr->sector); + num_sectors = virtio_ldl_p(vdev, &dwz_hdr->num_sectors); + flags = virtio_ldl_p(vdev, &dwz_hdr->flags); + max_sectors = is_write_zeroes ? s->conf.max_write_zeroes_sectors : + s->conf.max_discard_sectors; + + /* + * max_sectors is at most BDRV_REQUEST_MAX_SECTORS, this check + * make us sure that "num_sectors << BDRV_SECTOR_BITS" can fit in + * the integer variable. + */ + if (unlikely(num_sectors > max_sectors)) { + err_status = VIRTIO_BLK_S_IOERR; + goto err; + } + + bytes = num_sectors << BDRV_SECTOR_BITS; + + if (unlikely(!virtio_blk_sect_range_ok(s, sector, bytes))) { + err_status = VIRTIO_BLK_S_IOERR; + goto err; + } + + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for discard + * and write zeroes commands if any unknown flag is set. + */ + if (unlikely(flags & ~VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + err_status = VIRTIO_BLK_S_UNSUPP; + goto err; + } + + if (is_write_zeroes) { /* VIRTIO_BLK_T_WRITE_ZEROES */ + int blk_aio_flags = 0; + + if (flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP) { + blk_aio_flags |= BDRV_REQ_MAY_UNMAP; + } + + block_acct_start(blk_get_stats(s->blk), &req->acct, bytes, + BLOCK_ACCT_WRITE); + + blk_aio_pwrite_zeroes(s->blk, sector << BDRV_SECTOR_BITS, + bytes, blk_aio_flags, + virtio_blk_discard_write_zeroes_complete, req); + } else { /* VIRTIO_BLK_T_DISCARD */ + /* + * The device MUST set the status byte to VIRTIO_BLK_S_UNSUPP for + * discard commands if the unmap flag is set. + */ + if (unlikely(flags & VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP)) { + err_status = VIRTIO_BLK_S_UNSUPP; + goto err; + } + + blk_aio_pdiscard(s->blk, sector << BDRV_SECTOR_BITS, bytes, + virtio_blk_discard_write_zeroes_complete, req); + } + + return VIRTIO_BLK_S_OK; + +err: + if (is_write_zeroes) { + block_acct_invalid(blk_get_stats(s->blk), BLOCK_ACCT_WRITE); + } + return err_status; +} + static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) { uint32_t type; struct iovec *in_iov = req->elem.in_sg; - struct iovec *iov = req->elem.out_sg; + struct iovec *out_iov = req->elem.out_sg; unsigned in_num = req->elem.in_num; unsigned out_num = req->elem.out_num; VirtIOBlock *s = req->dev; @@ -493,13 +621,13 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) return -1; } - if (unlikely(iov_to_buf(iov, out_num, 0, &req->out, + if (unlikely(iov_to_buf(out_iov, out_num, 0, &req->out, sizeof(req->out)) != sizeof(req->out))) { virtio_error(vdev, "virtio-blk request outhdr too short"); return -1; } - iov_discard_front(&iov, &out_num, sizeof(req->out)); + iov_discard_front(&out_iov, &out_num, sizeof(req->out)); if (in_iov[in_num - 1].iov_len < sizeof(struct virtio_blk_inhdr)) { virtio_error(vdev, "virtio-blk request inhdr too short"); @@ -513,7 +641,7 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) - sizeof(struct virtio_blk_inhdr); iov_discard_back(in_iov, &in_num, sizeof(struct virtio_blk_inhdr)); - type = virtio_ldl_p(VIRTIO_DEVICE(req->dev), &req->out.type); + type = virtio_ldl_p(vdev, &req->out.type); /* VIRTIO_BLK_T_OUT defines the command direction. VIRTIO_BLK_T_BARRIER * is an optional flag. Although a guest should not send this flag if @@ -522,11 +650,10 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) case VIRTIO_BLK_T_IN: { bool is_write = type & VIRTIO_BLK_T_OUT; - req->sector_num = virtio_ldq_p(VIRTIO_DEVICE(req->dev), - &req->out.sector); + req->sector_num = virtio_ldq_p(vdev, &req->out.sector); if (is_write) { - qemu_iovec_init_external(&req->qiov, iov, out_num); + qemu_iovec_init_external(&req->qiov, out_iov, out_num); trace_virtio_blk_handle_write(vdev, req, req->sector_num, req->qiov.size / BDRV_SECTOR_SIZE); } else { @@ -535,25 +662,23 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) req->qiov.size / BDRV_SECTOR_SIZE); } - if (!virtio_blk_sect_range_ok(req->dev, req->sector_num, - req->qiov.size)) { + if (!virtio_blk_sect_range_ok(s, req->sector_num, req->qiov.size)) { virtio_blk_req_complete(req, VIRTIO_BLK_S_IOERR); - block_acct_invalid(blk_get_stats(req->dev->blk), + block_acct_invalid(blk_get_stats(s->blk), is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); virtio_blk_free_request(req); return 0; } - block_acct_start(blk_get_stats(req->dev->blk), - &req->acct, req->qiov.size, + block_acct_start(blk_get_stats(s->blk), &req->acct, req->qiov.size, is_write ? BLOCK_ACCT_WRITE : BLOCK_ACCT_READ); /* merge would exceed maximum number of requests or IO direction * changes */ if (mrb->num_reqs > 0 && (mrb->num_reqs == VIRTIO_BLK_MAX_MERGE_REQS || is_write != mrb->is_write || - !req->dev->conf.request_merging)) { - virtio_blk_submit_multireq(req->dev->blk, mrb); + !s->conf.request_merging)) { + virtio_blk_submit_multireq(s->blk, mrb); } assert(mrb->num_reqs < VIRTIO_BLK_MAX_MERGE_REQS); @@ -569,8 +694,6 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) break; case VIRTIO_BLK_T_GET_ID: { - VirtIOBlock *s = req->dev; - /* * NB: per existing s/n string convention the string is * terminated by '\0' only when shorter than buffer. @@ -584,6 +707,47 @@ static int virtio_blk_handle_request(VirtIOBlockReq *req, MultiReqBuffer *mrb) virtio_blk_free_request(req); break; } + /* + * VIRTIO_BLK_T_DISCARD and VIRTIO_BLK_T_WRITE_ZEROES are defined with + * VIRTIO_BLK_T_OUT flag set. We masked this flag in the switch statement, + * so we must mask it for these requests, then we will check if it is set. + */ + case VIRTIO_BLK_T_DISCARD & ~VIRTIO_BLK_T_OUT: + case VIRTIO_BLK_T_WRITE_ZEROES & ~VIRTIO_BLK_T_OUT: + { + struct virtio_blk_discard_write_zeroes dwz_hdr; + size_t out_len = iov_size(out_iov, out_num); + bool is_write_zeroes = (type & ~VIRTIO_BLK_T_BARRIER) == + VIRTIO_BLK_T_WRITE_ZEROES; + uint8_t err_status; + + /* + * Unsupported if VIRTIO_BLK_T_OUT is not set or the request contains + * more than one segment. + */ + if (unlikely(!(type & VIRTIO_BLK_T_OUT) || + out_len > sizeof(dwz_hdr))) { + virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); + virtio_blk_free_request(req); + return 0; + } + + if (unlikely(iov_to_buf(out_iov, out_num, 0, &dwz_hdr, + sizeof(dwz_hdr)) != sizeof(dwz_hdr))) { + virtio_error(vdev, "virtio-blk discard/write_zeroes header" + " too short"); + return -1; + } + + err_status = virtio_blk_handle_discard_write_zeroes(req, &dwz_hdr, + is_write_zeroes); + if (err_status != VIRTIO_BLK_S_OK) { + virtio_blk_req_complete(req, err_status); + virtio_blk_free_request(req); + } + + break; + } default: virtio_blk_req_complete(req, VIRTIO_BLK_S_UNSUPP); virtio_blk_free_request(req); @@ -677,6 +841,7 @@ static void virtio_blk_dma_restart_bh(void *opaque) if (mrb.num_reqs) { virtio_blk_submit_multireq(s->blk, &mrb); } + blk_dec_in_flight(s->conf.conf.blk); aio_context_release(blk_get_aio_context(s->conf.conf.blk)); } @@ -690,8 +855,11 @@ static void virtio_blk_dma_restart_cb(void *opaque, int running, } if (!s->bh) { + /* FIXME The data plane is not started yet, so these requests are + * processed in the main thread. */ s->bh = aio_bh_new(blk_get_aio_context(s->conf.conf.blk), virtio_blk_dma_restart_bh, s); + blk_inc_in_flight(s->conf.conf.blk); qemu_bh_schedule(s->bh); } } @@ -763,7 +931,25 @@ static void virtio_blk_update_config(VirtIODevice *vdev, uint8_t *config) blkcfg.alignment_offset = 0; blkcfg.wce = blk_enable_write_cache(s->blk); virtio_stw_p(vdev, &blkcfg.num_queues, s->conf.num_queues); - memcpy(config, &blkcfg, sizeof(struct virtio_blk_config)); + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD)) { + virtio_stl_p(vdev, &blkcfg.max_discard_sectors, + s->conf.max_discard_sectors); + virtio_stl_p(vdev, &blkcfg.discard_sector_alignment, + blk_size >> BDRV_SECTOR_BITS); + /* + * We support only one segment per request since multiple segments + * are not widely used and there are no userspace APIs that allow + * applications to submit multiple segments in a single call. + */ + virtio_stl_p(vdev, &blkcfg.max_discard_seg, 1); + } + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES)) { + virtio_stl_p(vdev, &blkcfg.max_write_zeroes_sectors, + s->conf.max_write_zeroes_sectors); + blkcfg.write_zeroes_may_unmap = 1; + virtio_stl_p(vdev, &blkcfg.max_write_zeroes_seg, 1); + } + memcpy(config, &blkcfg, s->config_size); } static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) @@ -771,7 +957,7 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) VirtIOBlock *s = VIRTIO_BLK(vdev); struct virtio_blk_config blkcfg; - memcpy(&blkcfg, config, sizeof(blkcfg)); + memcpy(&blkcfg, config, s->config_size); aio_context_acquire(blk_get_aio_context(s->blk)); blk_set_enable_write_cache(s->blk, blkcfg.wce != 0); @@ -783,12 +969,15 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, { VirtIOBlock *s = VIRTIO_BLK(vdev); + /* Firstly sync all virtio-blk possible supported features */ + features |= s->host_features; + virtio_add_feature(&features, VIRTIO_BLK_F_SEG_MAX); 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 (s->conf.scsi) { + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_SCSI)) { error_setg(errp, "Please set scsi=off for virtio-blk devices in order to use virtio 1.0"); return 0; } @@ -797,9 +986,6 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, virtio_add_feature(&features, VIRTIO_BLK_F_SCSI); } - if (s->conf.config_wce) { - virtio_add_feature(&features, VIRTIO_BLK_F_CONFIG_WCE); - } if (blk_enable_write_cache(s->blk)) { virtio_add_feature(&features, VIRTIO_BLK_F_WCE); } @@ -935,7 +1121,6 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - blkconf_serial(&conf->conf, &conf->serial); if (!blkconf_apply_backend_options(&conf->conf, blk_is_read_only(conf->conf.blk), true, errp)) { @@ -955,8 +1140,28 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) return; } - virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, - sizeof(struct virtio_blk_config)); + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_DISCARD) && + (!conf->max_discard_sectors || + conf->max_discard_sectors > BDRV_REQUEST_MAX_SECTORS)) { + error_setg(errp, "invalid max-discard-sectors property (%" PRIu32 ")" + ", must be between 1 and %d", + conf->max_discard_sectors, (int)BDRV_REQUEST_MAX_SECTORS); + return; + } + + if (virtio_has_feature(s->host_features, VIRTIO_BLK_F_WRITE_ZEROES) && + (!conf->max_write_zeroes_sectors || + conf->max_write_zeroes_sectors > BDRV_REQUEST_MAX_SECTORS)) { + error_setg(errp, "invalid max-write-zeroes-sectors property (%" PRIu32 + "), must be between 1 and %d", + conf->max_write_zeroes_sectors, + (int)BDRV_REQUEST_MAX_SECTORS); + return; + } + + virtio_blk_set_config_size(s, s->host_features); + + virtio_init(vdev, "virtio-blk", VIRTIO_ID_BLOCK, s->config_size); s->blk = conf->conf.blk; s->rq = NULL; @@ -1015,9 +1220,11 @@ static Property virtio_blk_properties[] = { DEFINE_BLOCK_ERROR_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_BLOCK_CHS_PROPERTIES(VirtIOBlock, conf.conf), DEFINE_PROP_STRING("serial", VirtIOBlock, conf.serial), - DEFINE_PROP_BIT("config-wce", VirtIOBlock, conf.config_wce, 0, true), + DEFINE_PROP_BIT64("config-wce", VirtIOBlock, host_features, + VIRTIO_BLK_F_CONFIG_WCE, true), #ifdef __linux__ - DEFINE_PROP_BIT("scsi", VirtIOBlock, conf.scsi, 0, false), + DEFINE_PROP_BIT64("scsi", VirtIOBlock, host_features, + VIRTIO_BLK_F_SCSI, false), #endif DEFINE_PROP_BIT("request-merging", VirtIOBlock, conf.request_merging, 0, true), @@ -1025,6 +1232,14 @@ static Property virtio_blk_properties[] = { DEFINE_PROP_UINT16("queue-size", VirtIOBlock, conf.queue_size, 128), DEFINE_PROP_LINK("iothread", VirtIOBlock, conf.iothread, TYPE_IOTHREAD, IOThread *), + DEFINE_PROP_BIT64("discard", VirtIOBlock, host_features, + VIRTIO_BLK_F_DISCARD, true), + DEFINE_PROP_BIT64("write-zeroes", VirtIOBlock, host_features, + VIRTIO_BLK_F_WRITE_ZEROES, true), + DEFINE_PROP_UINT32("max-discard-sectors", VirtIOBlock, + conf.max_discard_sectors, BDRV_REQUEST_MAX_SECTORS), + DEFINE_PROP_UINT32("max-write-zeroes-sectors", VirtIOBlock, + conf.max_write_zeroes_sectors, BDRV_REQUEST_MAX_SECTORS), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c new file mode 100644 index 0000000000..ef635be4c2 --- /dev/null +++ b/hw/block/xen-block.c @@ -0,0 +1,1017 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 "qemu/cutils.h" +#include "qemu/option.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-block-core.h" +#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-visit-block-core.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/visitor.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" +#include "hw/hw.h" +#include "hw/xen/xen_common.h" +#include "hw/block/xen_blkif.h" +#include "hw/xen/xen-block.h" +#include "hw/xen/xen-backend.h" +#include "sysemu/blockdev.h" +#include "sysemu/block-backend.h" +#include "sysemu/iothread.h" +#include "dataplane/xen-block.h" +#include "trace.h" + +static char *xen_block_get_name(XenDevice *xendev, Error **errp) +{ + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); + XenBlockVdev *vdev = &blockdev->props.vdev; + + return g_strdup_printf("%lu", vdev->number); +} + +static void xen_block_disconnect(XenDevice *xendev, Error **errp) +{ + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); + const char *type = object_get_typename(OBJECT(blockdev)); + XenBlockVdev *vdev = &blockdev->props.vdev; + + trace_xen_block_disconnect(type, vdev->disk, vdev->partition); + + xen_block_dataplane_stop(blockdev->dataplane); +} + +static void xen_block_connect(XenDevice *xendev, Error **errp) +{ + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); + const char *type = object_get_typename(OBJECT(blockdev)); + XenBlockVdev *vdev = &blockdev->props.vdev; + unsigned int order, nr_ring_ref, *ring_ref, event_channel, protocol; + char *str; + + trace_xen_block_connect(type, vdev->disk, vdev->partition); + + if (xen_device_frontend_scanf(xendev, "ring-page-order", "%u", + &order) != 1) { + nr_ring_ref = 1; + ring_ref = g_new(unsigned int, nr_ring_ref); + + if (xen_device_frontend_scanf(xendev, "ring-ref", "%u", + &ring_ref[0]) != 1) { + error_setg(errp, "failed to read ring-ref"); + g_free(ring_ref); + return; + } + } else if (order <= blockdev->props.max_ring_page_order) { + unsigned int i; + + nr_ring_ref = 1 << order; + ring_ref = g_new(unsigned int, nr_ring_ref); + + for (i = 0; i < nr_ring_ref; i++) { + const char *key = g_strdup_printf("ring-ref%u", i); + + if (xen_device_frontend_scanf(xendev, key, "%u", + &ring_ref[i]) != 1) { + error_setg(errp, "failed to read %s", key); + g_free((gpointer)key); + g_free(ring_ref); + return; + } + + g_free((gpointer)key); + } + } else { + error_setg(errp, "invalid ring-page-order (%d)", order); + return; + } + + if (xen_device_frontend_scanf(xendev, "event-channel", "%u", + &event_channel) != 1) { + error_setg(errp, "failed to read event-channel"); + g_free(ring_ref); + return; + } + + if (xen_device_frontend_scanf(xendev, "protocol", "%ms", + &str) != 1) { + protocol = BLKIF_PROTOCOL_NATIVE; + } else { + if (strcmp(str, XEN_IO_PROTO_ABI_X86_32) == 0) { + protocol = BLKIF_PROTOCOL_X86_32; + } else if (strcmp(str, XEN_IO_PROTO_ABI_X86_64) == 0) { + protocol = BLKIF_PROTOCOL_X86_64; + } else { + protocol = BLKIF_PROTOCOL_NATIVE; + } + + free(str); + } + + xen_block_dataplane_start(blockdev->dataplane, ring_ref, nr_ring_ref, + event_channel, protocol, errp); + + g_free(ring_ref); +} + +static void xen_block_unrealize(XenDevice *xendev, Error **errp) +{ + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); + XenBlockDeviceClass *blockdev_class = + XEN_BLOCK_DEVICE_GET_CLASS(xendev); + const char *type = object_get_typename(OBJECT(blockdev)); + XenBlockVdev *vdev = &blockdev->props.vdev; + + if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) { + return; + } + + trace_xen_block_unrealize(type, vdev->disk, vdev->partition); + + /* Disconnect from the frontend in case this has not already happened */ + xen_block_disconnect(xendev, NULL); + + xen_block_dataplane_destroy(blockdev->dataplane); + blockdev->dataplane = NULL; + + if (blockdev_class->unrealize) { + blockdev_class->unrealize(blockdev, errp); + } +} + +static void xen_block_set_size(XenBlockDevice *blockdev) +{ + const char *type = object_get_typename(OBJECT(blockdev)); + XenBlockVdev *vdev = &blockdev->props.vdev; + BlockConf *conf = &blockdev->props.conf; + int64_t sectors = blk_getlength(conf->blk) / XEN_BLKIF_SECTOR_SIZE; + XenDevice *xendev = XEN_DEVICE(blockdev); + + trace_xen_block_size(type, vdev->disk, vdev->partition, sectors); + + xen_device_backend_printf(xendev, "sectors", "%"PRIi64, sectors); +} + +static void xen_block_resize_cb(void *opaque) +{ + XenBlockDevice *blockdev = opaque; + XenDevice *xendev = XEN_DEVICE(blockdev); + enum xenbus_state state = xen_device_backend_get_state(xendev); + + xen_block_set_size(blockdev); + + /* + * Mimic the behaviour of Linux xen-blkback and re-write the state + * to trigger the frontend watch. + */ + xen_device_backend_printf(xendev, "state", "%u", state); +} + +static const BlockDevOps xen_block_dev_ops = { + .resize_cb = xen_block_resize_cb, +}; + +static void xen_block_realize(XenDevice *xendev, Error **errp) +{ + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); + XenBlockDeviceClass *blockdev_class = + XEN_BLOCK_DEVICE_GET_CLASS(xendev); + const char *type = object_get_typename(OBJECT(blockdev)); + XenBlockVdev *vdev = &blockdev->props.vdev; + BlockConf *conf = &blockdev->props.conf; + Error *local_err = NULL; + + if (vdev->type == XEN_BLOCK_VDEV_TYPE_INVALID) { + error_setg(errp, "vdev property not set"); + return; + } + + trace_xen_block_realize(type, vdev->disk, vdev->partition); + + if (blockdev_class->realize) { + blockdev_class->realize(blockdev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + + /* + * The blkif protocol does not deal with removable media, so it must + * always be present, even for CDRom devices. + */ + assert(conf->blk); + if (!blk_is_inserted(conf->blk)) { + error_setg(errp, "device needs media, but drive is empty"); + return; + } + + if (!blkconf_apply_backend_options(conf, blockdev->info & VDISK_READONLY, + true, errp)) { + return; + } + + if (!(blockdev->info & VDISK_CDROM) && + !blkconf_geometry(conf, NULL, 65535, 255, 255, errp)) { + return; + } + + blkconf_blocksizes(conf); + + if (conf->logical_block_size != XEN_BLKIF_SECTOR_SIZE) { + error_setg(errp, "logical_block_size != %u not supported", + XEN_BLKIF_SECTOR_SIZE); + return; + } + + if (conf->logical_block_size > conf->physical_block_size) { + error_setg( + errp, "logical_block_size > physical_block_size not supported"); + return; + } + + blk_set_dev_ops(conf->blk, &xen_block_dev_ops, blockdev); + blk_set_guest_block_size(conf->blk, conf->logical_block_size); + + if (conf->discard_granularity == -1) { + conf->discard_granularity = conf->physical_block_size; + } + + if (blk_get_flags(conf->blk) & BDRV_O_UNMAP) { + xen_device_backend_printf(xendev, "feature-discard", "%u", 1); + xen_device_backend_printf(xendev, "discard-granularity", "%u", + conf->discard_granularity); + } + + xen_device_backend_printf(xendev, "feature-flush-cache", "%u", 1); + xen_device_backend_printf(xendev, "max-ring-page-order", "%u", + blockdev->props.max_ring_page_order); + xen_device_backend_printf(xendev, "info", "%u", blockdev->info); + + xen_device_frontend_printf(xendev, "virtual-device", "%lu", + vdev->number); + xen_device_frontend_printf(xendev, "device-type", "%s", + blockdev->device_type); + + xen_device_backend_printf(xendev, "sector-size", "%u", + XEN_BLKIF_SECTOR_SIZE); + + xen_block_set_size(blockdev); + + blockdev->dataplane = + xen_block_dataplane_create(xendev, conf, blockdev->props.iothread); +} + +static void xen_block_frontend_changed(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp) +{ + enum xenbus_state backend_state = xen_device_backend_get_state(xendev); + Error *local_err = NULL; + + switch (frontend_state) { + case XenbusStateInitialised: + case XenbusStateConnected: + if (backend_state == XenbusStateConnected) { + break; + } + + xen_block_disconnect(xendev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + break; + } + + xen_block_connect(xendev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + break; + } + + xen_device_backend_set_state(xendev, XenbusStateConnected); + break; + + case XenbusStateClosing: + xen_device_backend_set_state(xendev, XenbusStateClosing); + break; + + case XenbusStateClosed: + xen_block_disconnect(xendev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + break; + } + + xen_device_backend_set_state(xendev, XenbusStateClosed); + break; + + default: + break; + } +} + +static char *disk_to_vbd_name(unsigned int disk) +{ + char *name, *prefix = (disk >= 26) ? + disk_to_vbd_name((disk / 26) - 1) : g_strdup(""); + + name = g_strdup_printf("%s%c", prefix, 'a' + disk % 26); + g_free(prefix); + + return name; +} + +static void xen_block_get_vdev(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop); + char *str; + + switch (vdev->type) { + case XEN_BLOCK_VDEV_TYPE_DP: + str = g_strdup_printf("d%lup%lu", vdev->disk, vdev->partition); + break; + + case XEN_BLOCK_VDEV_TYPE_XVD: + case XEN_BLOCK_VDEV_TYPE_HD: + case XEN_BLOCK_VDEV_TYPE_SD: { + char *name = disk_to_vbd_name(vdev->disk); + + str = g_strdup_printf("%s%s%lu", + (vdev->type == XEN_BLOCK_VDEV_TYPE_XVD) ? + "xvd" : + (vdev->type == XEN_BLOCK_VDEV_TYPE_HD) ? + "hd" : + "sd", + name, vdev->partition); + g_free(name); + break; + } + default: + error_setg(errp, "invalid vdev type"); + return; + } + + visit_type_str(v, name, &str, errp); + g_free(str); +} + +static int vbd_name_to_disk(const char *name, const char **endp, + unsigned long *disk) +{ + unsigned int n = 0; + + while (*name != '\0') { + if (!g_ascii_isalpha(*name) || !g_ascii_islower(*name)) { + break; + } + + n *= 26; + n += *name++ - 'a' + 1; + } + *endp = name; + + if (!n) { + return -1; + } + + *disk = n - 1; + + return 0; +} + +static void xen_block_set_vdev(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + XenBlockVdev *vdev = qdev_get_prop_ptr(dev, prop); + Error *local_err = NULL; + char *str, *p; + const char *end; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_str(v, name, &str, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + p = strchr(str, 'd'); + if (!p) { + goto invalid; + } + + *p++ = '\0'; + if (*str == '\0') { + vdev->type = XEN_BLOCK_VDEV_TYPE_DP; + } else if (strcmp(str, "xv") == 0) { + vdev->type = XEN_BLOCK_VDEV_TYPE_XVD; + } else if (strcmp(str, "h") == 0) { + vdev->type = XEN_BLOCK_VDEV_TYPE_HD; + } else if (strcmp(str, "s") == 0) { + vdev->type = XEN_BLOCK_VDEV_TYPE_SD; + } else { + goto invalid; + } + + if (vdev->type == XEN_BLOCK_VDEV_TYPE_DP) { + if (qemu_strtoul(p, &end, 10, &vdev->disk)) { + goto invalid; + } + + if (*end == 'p') { + if (*(++end) == '\0') { + goto invalid; + } + } + } else { + if (vbd_name_to_disk(p, &end, &vdev->disk)) { + goto invalid; + } + } + + if (*end != '\0') { + p = (char *)end; + + if (qemu_strtoul(p, &end, 10, &vdev->partition)) { + goto invalid; + } + + if (*end != '\0') { + goto invalid; + } + } else { + vdev->partition = 0; + } + + switch (vdev->type) { + case XEN_BLOCK_VDEV_TYPE_DP: + case XEN_BLOCK_VDEV_TYPE_XVD: + if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) { + vdev->number = (202 << 8) | (vdev->disk << 4) | + vdev->partition; + } else if (vdev->disk < (1 << 20) && vdev->partition < (1 << 8)) { + vdev->number = (1 << 28) | (vdev->disk << 8) | + vdev->partition; + } else { + goto invalid; + } + break; + + case XEN_BLOCK_VDEV_TYPE_HD: + if ((vdev->disk == 0 || vdev->disk == 1) && + vdev->partition < (1 << 6)) { + vdev->number = (3 << 8) | (vdev->disk << 6) | vdev->partition; + } else if ((vdev->disk == 2 || vdev->disk == 3) && + vdev->partition < (1 << 6)) { + vdev->number = (22 << 8) | ((vdev->disk - 2) << 6) | + vdev->partition; + } else { + goto invalid; + } + break; + + case XEN_BLOCK_VDEV_TYPE_SD: + if (vdev->disk < (1 << 4) && vdev->partition < (1 << 4)) { + vdev->number = (8 << 8) | (vdev->disk << 4) | vdev->partition; + } else { + goto invalid; + } + break; + + default: + goto invalid; + } + + g_free(str); + return; + +invalid: + error_setg(errp, "invalid virtual disk specifier"); + + vdev->type = XEN_BLOCK_VDEV_TYPE_INVALID; + g_free(str); +} + +/* + * This property deals with 'vdev' names adhering to the Xen VBD naming + * scheme described in: + * + * https://xenbits.xen.org/docs/unstable/man/xen-vbd-interface.7.html + */ +const PropertyInfo xen_block_prop_vdev = { + .name = "str", + .description = "Virtual Disk specifier: d*p*/xvd*/hd*/sd*", + .get = xen_block_get_vdev, + .set = xen_block_set_vdev, +}; + +static Property xen_block_props[] = { + DEFINE_PROP("vdev", XenBlockDevice, props.vdev, + xen_block_prop_vdev, XenBlockVdev), + DEFINE_BLOCK_PROPERTIES(XenBlockDevice, props.conf), + DEFINE_PROP_UINT32("max-ring-page-order", XenBlockDevice, + props.max_ring_page_order, 4), + DEFINE_PROP_LINK("iothread", XenBlockDevice, props.iothread, + TYPE_IOTHREAD, IOThread *), + DEFINE_PROP_END_OF_LIST() +}; + +static void xen_block_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); + + xendev_class->backend = "qdisk"; + xendev_class->device = "vbd"; + xendev_class->get_name = xen_block_get_name; + xendev_class->realize = xen_block_realize; + xendev_class->frontend_changed = xen_block_frontend_changed; + xendev_class->unrealize = xen_block_unrealize; + + dev_class->props = xen_block_props; +} + +static const TypeInfo xen_block_type_info = { + .name = TYPE_XEN_BLOCK_DEVICE, + .parent = TYPE_XEN_DEVICE, + .instance_size = sizeof(XenBlockDevice), + .abstract = true, + .class_size = sizeof(XenBlockDeviceClass), + .class_init = xen_block_class_init, +}; + +static void xen_disk_unrealize(XenBlockDevice *blockdev, Error **errp) +{ + trace_xen_disk_unrealize(); +} + +static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp) +{ + BlockConf *conf = &blockdev->props.conf; + + trace_xen_disk_realize(); + + blockdev->device_type = "disk"; + + if (!conf->blk) { + error_setg(errp, "drive property not set"); + return; + } + + blockdev->info = blk_is_read_only(conf->blk) ? VDISK_READONLY : 0; +} + +static void xen_disk_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class); + + blockdev_class->realize = xen_disk_realize; + blockdev_class->unrealize = xen_disk_unrealize; + + dev_class->desc = "Xen Disk Device"; +} + +static const TypeInfo xen_disk_type_info = { + .name = TYPE_XEN_DISK_DEVICE, + .parent = TYPE_XEN_BLOCK_DEVICE, + .instance_size = sizeof(XenDiskDevice), + .class_init = xen_disk_class_init, +}; + +static void xen_cdrom_unrealize(XenBlockDevice *blockdev, Error **errp) +{ + trace_xen_cdrom_unrealize(); +} + +static void xen_cdrom_realize(XenBlockDevice *blockdev, Error **errp) +{ + BlockConf *conf = &blockdev->props.conf; + + trace_xen_cdrom_realize(); + + blockdev->device_type = "cdrom"; + + if (!conf->blk) { + int rc; + + /* Set up an empty drive */ + conf->blk = blk_new(0, BLK_PERM_ALL); + + rc = blk_attach_dev(conf->blk, DEVICE(blockdev)); + if (!rc) { + error_setg_errno(errp, -rc, "failed to create drive"); + return; + } + } + + blockdev->info = VDISK_READONLY | VDISK_CDROM; +} + +static void xen_cdrom_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class); + + blockdev_class->realize = xen_cdrom_realize; + blockdev_class->unrealize = xen_cdrom_unrealize; + + dev_class->desc = "Xen CD-ROM Device"; +} + +static const TypeInfo xen_cdrom_type_info = { + .name = TYPE_XEN_CDROM_DEVICE, + .parent = TYPE_XEN_BLOCK_DEVICE, + .instance_size = sizeof(XenCDRomDevice), + .class_init = xen_cdrom_class_init, +}; + +static void xen_block_register_types(void) +{ + type_register_static(&xen_block_type_info); + type_register_static(&xen_disk_type_info); + type_register_static(&xen_cdrom_type_info); +} + +type_init(xen_block_register_types) + +static void xen_block_blockdev_del(const char *node_name, Error **errp) +{ + trace_xen_block_blockdev_del(node_name); + + qmp_blockdev_del(node_name, errp); +} + +static char *xen_block_blockdev_add(const char *id, QDict *qdict, + Error **errp) +{ + const char *driver = qdict_get_try_str(qdict, "driver"); + BlockdevOptions *options = NULL; + Error *local_err = NULL; + char *node_name; + Visitor *v; + + if (!driver) { + error_setg(errp, "no 'driver' parameter"); + return NULL; + } + + node_name = g_strdup_printf("%s-%s", id, driver); + qdict_put_str(qdict, "node-name", node_name); + + trace_xen_block_blockdev_add(node_name); + + v = qobject_input_visitor_new(QOBJECT(qdict)); + visit_type_BlockdevOptions(v, NULL, &options, &local_err); + visit_free(v); + + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + qmp_blockdev_add(options, &local_err); + + if (local_err) { + error_propagate(errp, local_err); + goto fail; + } + + qapi_free_BlockdevOptions(options); + + return node_name; + +fail: + if (options) { + qapi_free_BlockdevOptions(options); + } + g_free(node_name); + + return NULL; +} + +static void xen_block_drive_destroy(XenBlockDrive *drive, Error **errp) +{ + char *node_name = drive->node_name; + + if (node_name) { + Error *local_err = NULL; + + xen_block_blockdev_del(node_name, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + g_free(node_name); + drive->node_name = NULL; + } + g_free(drive->id); + g_free(drive); +} + +static XenBlockDrive *xen_block_drive_create(const char *id, + const char *device_type, + QDict *opts, Error **errp) +{ + const char *params = qdict_get_try_str(opts, "params"); + const char *mode = qdict_get_try_str(opts, "mode"); + const char *direct_io_safe = qdict_get_try_str(opts, "direct-io-safe"); + const char *discard_enable = qdict_get_try_str(opts, "discard-enable"); + char *driver = NULL; + char *filename = NULL; + XenBlockDrive *drive = NULL; + Error *local_err = NULL; + QDict *file_layer; + QDict *driver_layer; + + if (params) { + char **v = g_strsplit(params, ":", 2); + + if (v[1] == NULL) { + filename = g_strdup(v[0]); + driver = g_strdup("raw"); + } else { + if (strcmp(v[0], "aio") == 0) { + driver = g_strdup("raw"); + } else if (strcmp(v[0], "vhd") == 0) { + driver = g_strdup("vpc"); + } else { + driver = g_strdup(v[0]); + } + filename = g_strdup(v[1]); + } + + g_strfreev(v); + } else { + error_setg(errp, "no params"); + goto done; + } + + assert(filename); + assert(driver); + + drive = g_new0(XenBlockDrive, 1); + drive->id = g_strdup(id); + + file_layer = qdict_new(); + driver_layer = qdict_new(); + + qdict_put_str(file_layer, "driver", "file"); + qdict_put_str(file_layer, "filename", filename); + g_free(filename); + + if (mode && *mode != 'w') { + qdict_put_bool(file_layer, "read-only", true); + } + + if (direct_io_safe) { + unsigned long value; + + if (!qemu_strtoul(direct_io_safe, NULL, 2, &value) && !!value) { + QDict *cache_qdict = qdict_new(); + + qdict_put_bool(cache_qdict, "direct", true); + qdict_put(file_layer, "cache", cache_qdict); + + qdict_put_str(file_layer, "aio", "native"); + } + } + + if (discard_enable) { + unsigned long value; + + if (!qemu_strtoul(discard_enable, NULL, 2, &value) && !!value) { + qdict_put_str(file_layer, "discard", "unmap"); + qdict_put_str(driver_layer, "discard", "unmap"); + } + } + + /* + * It is necessary to turn file locking off as an emulated device + * may have already opened the same image file. + */ + qdict_put_str(file_layer, "locking", "off"); + + qdict_put_str(driver_layer, "driver", driver); + g_free(driver); + + qdict_put(driver_layer, "file", file_layer); + + g_assert(!drive->node_name); + drive->node_name = xen_block_blockdev_add(drive->id, driver_layer, + &local_err); + + qobject_unref(driver_layer); + +done: + if (local_err) { + error_propagate(errp, local_err); + xen_block_drive_destroy(drive, NULL); + return NULL; + } + + return drive; +} + +static const char *xen_block_drive_get_node_name(XenBlockDrive *drive) +{ + return drive->node_name ? drive->node_name : ""; +} + +static void xen_block_iothread_destroy(XenBlockIOThread *iothread, + Error **errp) +{ + qmp_object_del(iothread->id, errp); + + g_free(iothread->id); + g_free(iothread); +} + +static XenBlockIOThread *xen_block_iothread_create(const char *id, + Error **errp) +{ + XenBlockIOThread *iothread = g_new(XenBlockIOThread, 1); + Error *local_err = NULL; + + iothread->id = g_strdup(id); + + qmp_object_add(TYPE_IOTHREAD, id, false, NULL, &local_err); + if (local_err) { + error_propagate(errp, local_err); + + g_free(iothread->id); + g_free(iothread); + return NULL; + } + + return iothread; +} + +static void xen_block_device_create(XenBackendInstance *backend, + QDict *opts, Error **errp) +{ + XenBus *xenbus = xen_backend_get_bus(backend); + const char *name = xen_backend_get_name(backend); + unsigned long number; + const char *vdev, *device_type; + XenBlockDrive *drive = NULL; + XenBlockIOThread *iothread = NULL; + XenDevice *xendev = NULL; + Error *local_err = NULL; + const char *type; + XenBlockDevice *blockdev; + + if (qemu_strtoul(name, NULL, 10, &number)) { + error_setg(errp, "failed to parse name '%s'", name); + goto fail; + } + + trace_xen_block_device_create(number); + + vdev = qdict_get_try_str(opts, "dev"); + if (!vdev) { + error_setg(errp, "no dev parameter"); + goto fail; + } + + device_type = qdict_get_try_str(opts, "device-type"); + if (!device_type) { + error_setg(errp, "no device-type parameter"); + goto fail; + } + + if (!strcmp(device_type, "disk")) { + type = TYPE_XEN_DISK_DEVICE; + } else if (!strcmp(device_type, "cdrom")) { + type = TYPE_XEN_CDROM_DEVICE; + } else { + error_setg(errp, "invalid device-type parameter '%s'", device_type); + goto fail; + } + + drive = xen_block_drive_create(vdev, device_type, opts, &local_err); + if (!drive) { + error_propagate_prepend(errp, local_err, "failed to create drive: "); + goto fail; + } + + iothread = xen_block_iothread_create(vdev, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to create iothread: "); + goto fail; + } + + xendev = XEN_DEVICE(qdev_create(BUS(xenbus), type)); + blockdev = XEN_BLOCK_DEVICE(xendev); + + object_property_set_str(OBJECT(xendev), vdev, "vdev", &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, "failed to set 'vdev': "); + goto fail; + } + + object_property_set_str(OBJECT(xendev), + xen_block_drive_get_node_name(drive), "drive", + &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, "failed to set 'drive': "); + goto fail; + } + + object_property_set_str(OBJECT(xendev), iothread->id, "iothread", + &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to set 'iothread': "); + goto fail; + } + + blockdev->iothread = iothread; + blockdev->drive = drive; + + object_property_set_bool(OBJECT(xendev), true, "realized", &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "realization of device %s failed: ", + type); + goto fail; + } + + xen_backend_set_device(backend, xendev); + return; + +fail: + if (xendev) { + object_unparent(OBJECT(xendev)); + } + + if (iothread) { + xen_block_iothread_destroy(iothread, NULL); + } + + if (drive) { + xen_block_drive_destroy(drive, NULL); + } +} + +static void xen_block_device_destroy(XenBackendInstance *backend, + Error **errp) +{ + XenDevice *xendev = xen_backend_get_device(backend); + XenBlockDevice *blockdev = XEN_BLOCK_DEVICE(xendev); + XenBlockVdev *vdev = &blockdev->props.vdev; + XenBlockDrive *drive = blockdev->drive; + XenBlockIOThread *iothread = blockdev->iothread; + + trace_xen_block_device_destroy(vdev->number); + + object_unparent(OBJECT(xendev)); + + if (iothread) { + Error *local_err = NULL; + + xen_block_iothread_destroy(iothread, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to destroy iothread: "); + return; + } + } + + if (drive) { + Error *local_err = NULL; + + xen_block_drive_destroy(drive, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to destroy drive: "); + } + } +} + +static const XenBackendInfo xen_block_backend_info = { + .type = "qdisk", + .create = xen_block_device_create, + .destroy = xen_block_device_destroy, +}; + +static void xen_block_register_backend(void) +{ + xen_backend_register(&xen_block_backend_info); +} + +xen_backend_init(xen_block_register_backend); diff --git a/hw/block/xen_blkif.h b/hw/block/xen_blkif.h index 3e6e1ea365..a353693ea0 100644 --- a/hw/block/xen_blkif.h +++ b/hw/block/xen_blkif.h @@ -143,4 +143,6 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, } } +#define XEN_BLKIF_SECTOR_SIZE 512 + #endif /* XEN_BLKIF_H */ diff --git a/hw/block/xen_disk.c b/hw/block/xen_disk.c deleted file mode 100644 index 36eff94f84..0000000000 --- a/hw/block/xen_disk.c +++ /dev/null @@ -1,1011 +0,0 @@ -/* - * xen paravirt block device backend - * - * (c) Gerd Hoffmann - * - * 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 . - * - * 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/units.h" -#include -#include - -#include "hw/hw.h" -#include "hw/xen/xen_backend.h" -#include "xen_blkif.h" -#include "sysemu/blockdev.h" -#include "sysemu/iothread.h" -#include "sysemu/block-backend.h" -#include "qapi/error.h" -#include "qapi/qmp/qdict.h" -#include "qapi/qmp/qstring.h" -#include "trace.h" - -/* ------------------------------------------------------------- */ - -#define BLOCK_SIZE 512 -#define IOCB_COUNT (BLKIF_MAX_SEGMENTS_PER_REQUEST + 2) - -struct ioreq { - blkif_request_t req; - int16_t status; - - /* parsed request */ - off_t start; - QEMUIOVector v; - void *buf; - size_t size; - int presync; - - /* aio status */ - int aio_inflight; - int aio_errors; - - struct XenBlkDev *blkdev; - QLIST_ENTRY(ioreq) list; - BlockAcctCookie acct; -}; - -#define MAX_RING_PAGE_ORDER 4 - -struct XenBlkDev { - struct XenDevice xendev; /* must be first */ - char *params; - char *mode; - char *type; - char *dev; - char *devtype; - bool directiosafe; - const char *fileproto; - const char *filename; - unsigned int ring_ref[1 << MAX_RING_PAGE_ORDER]; - unsigned int nr_ring_ref; - void *sring; - int64_t file_blk; - int64_t file_size; - int protocol; - blkif_back_rings_t rings; - int more_work; - - /* request lists */ - QLIST_HEAD(inflight_head, ioreq) inflight; - QLIST_HEAD(finished_head, ioreq) finished; - QLIST_HEAD(freelist_head, ioreq) freelist; - int requests_total; - int requests_inflight; - int requests_finished; - unsigned int max_requests; - - gboolean feature_discard; - - /* qemu block driver */ - DriveInfo *dinfo; - BlockBackend *blk; - QEMUBH *bh; - - IOThread *iothread; - AioContext *ctx; -}; - -/* ------------------------------------------------------------- */ - -static void ioreq_reset(struct ioreq *ioreq) -{ - memset(&ioreq->req, 0, sizeof(ioreq->req)); - ioreq->status = 0; - ioreq->start = 0; - ioreq->buf = NULL; - ioreq->size = 0; - ioreq->presync = 0; - - ioreq->aio_inflight = 0; - ioreq->aio_errors = 0; - - ioreq->blkdev = NULL; - memset(&ioreq->list, 0, sizeof(ioreq->list)); - memset(&ioreq->acct, 0, sizeof(ioreq->acct)); - - qemu_iovec_reset(&ioreq->v); -} - -static struct ioreq *ioreq_start(struct XenBlkDev *blkdev) -{ - struct ioreq *ioreq = NULL; - - if (QLIST_EMPTY(&blkdev->freelist)) { - if (blkdev->requests_total >= blkdev->max_requests) { - goto out; - } - /* allocate new struct */ - ioreq = g_malloc0(sizeof(*ioreq)); - ioreq->blkdev = blkdev; - blkdev->requests_total++; - qemu_iovec_init(&ioreq->v, 1); - } else { - /* get one from freelist */ - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); - } - QLIST_INSERT_HEAD(&blkdev->inflight, ioreq, list); - blkdev->requests_inflight++; - -out: - return ioreq; -} - -static void ioreq_finish(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - QLIST_REMOVE(ioreq, list); - QLIST_INSERT_HEAD(&blkdev->finished, ioreq, list); - blkdev->requests_inflight--; - blkdev->requests_finished++; -} - -static void ioreq_release(struct ioreq *ioreq, bool finish) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - QLIST_REMOVE(ioreq, list); - ioreq_reset(ioreq); - ioreq->blkdev = blkdev; - QLIST_INSERT_HEAD(&blkdev->freelist, ioreq, list); - if (finish) { - blkdev->requests_finished--; - } else { - blkdev->requests_inflight--; - } -} - -/* - * translate request into iovec + start offset - * do sanity checks along the way - */ -static int ioreq_parse(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - struct XenDevice *xendev = &blkdev->xendev; - size_t len; - int i; - - xen_pv_printf(xendev, 3, - "op %d, nr %d, handle %d, id %" PRId64 ", sector %" PRId64 "\n", - ioreq->req.operation, ioreq->req.nr_segments, - ioreq->req.handle, ioreq->req.id, ioreq->req.sector_number); - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - break; - case BLKIF_OP_FLUSH_DISKCACHE: - ioreq->presync = 1; - if (!ioreq->req.nr_segments) { - return 0; - } - /* fall through */ - case BLKIF_OP_WRITE: - break; - case BLKIF_OP_DISCARD: - return 0; - default: - xen_pv_printf(xendev, 0, "error: unknown operation (%d)\n", - ioreq->req.operation); - goto err; - }; - - if (ioreq->req.operation != BLKIF_OP_READ && blkdev->mode[0] != 'w') { - xen_pv_printf(xendev, 0, "error: write req for ro device\n"); - goto err; - } - - ioreq->start = ioreq->req.sector_number * blkdev->file_blk; - for (i = 0; i < ioreq->req.nr_segments; i++) { - if (i == BLKIF_MAX_SEGMENTS_PER_REQUEST) { - xen_pv_printf(xendev, 0, "error: nr_segments too big\n"); - goto err; - } - if (ioreq->req.seg[i].first_sect > ioreq->req.seg[i].last_sect) { - xen_pv_printf(xendev, 0, "error: first > last sector\n"); - goto err; - } - if (ioreq->req.seg[i].last_sect * BLOCK_SIZE >= XC_PAGE_SIZE) { - xen_pv_printf(xendev, 0, "error: page crossing\n"); - goto err; - } - - len = (ioreq->req.seg[i].last_sect - ioreq->req.seg[i].first_sect + 1) * blkdev->file_blk; - ioreq->size += len; - } - if (ioreq->start + ioreq->size > blkdev->file_size) { - xen_pv_printf(xendev, 0, "error: access beyond end of file\n"); - goto err; - } - return 0; - -err: - ioreq->status = BLKIF_RSP_ERROR; - return -1; -} - -static int ioreq_grant_copy(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - struct XenDevice *xendev = &blkdev->xendev; - XenGrantCopySegment segs[BLKIF_MAX_SEGMENTS_PER_REQUEST]; - int i, count, rc; - int64_t file_blk = blkdev->file_blk; - bool to_domain = (ioreq->req.operation == BLKIF_OP_READ); - void *virt = ioreq->buf; - - if (ioreq->req.nr_segments == 0) { - return 0; - } - - count = ioreq->req.nr_segments; - - for (i = 0; i < count; i++) { - if (to_domain) { - segs[i].dest.foreign.ref = ioreq->req.seg[i].gref; - segs[i].dest.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].source.virt = virt; - } else { - segs[i].source.foreign.ref = ioreq->req.seg[i].gref; - segs[i].source.foreign.offset = ioreq->req.seg[i].first_sect * file_blk; - segs[i].dest.virt = virt; - } - segs[i].len = (ioreq->req.seg[i].last_sect - - ioreq->req.seg[i].first_sect + 1) * file_blk; - virt += segs[i].len; - } - - rc = xen_be_copy_grant_refs(xendev, to_domain, segs, count); - - if (rc) { - xen_pv_printf(xendev, 0, - "failed to copy data %d\n", rc); - ioreq->aio_errors++; - return -1; - } - - return rc; -} - -static int ioreq_runio_qemu_aio(struct ioreq *ioreq); - -static void qemu_aio_complete(void *opaque, int ret) -{ - struct ioreq *ioreq = opaque; - struct XenBlkDev *blkdev = ioreq->blkdev; - struct XenDevice *xendev = &blkdev->xendev; - - aio_context_acquire(blkdev->ctx); - - if (ret != 0) { - xen_pv_printf(xendev, 0, "%s I/O error\n", - ioreq->req.operation == BLKIF_OP_READ ? "read" : "write"); - ioreq->aio_errors++; - } - - ioreq->aio_inflight--; - if (ioreq->presync) { - ioreq->presync = 0; - ioreq_runio_qemu_aio(ioreq); - goto done; - } - if (ioreq->aio_inflight > 0) { - goto done; - } - - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - /* in case of failure ioreq->aio_errors is increased */ - if (ret == 0) { - ioreq_grant_copy(ioreq); - } - qemu_vfree(ioreq->buf); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - qemu_vfree(ioreq->buf); - break; - default: - break; - } - - ioreq->status = ioreq->aio_errors ? BLKIF_RSP_ERROR : BLKIF_RSP_OKAY; - ioreq_finish(ioreq); - - switch (ioreq->req.operation) { - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - case BLKIF_OP_READ: - if (ioreq->status == BLKIF_RSP_OKAY) { - block_acct_done(blk_get_stats(blkdev->blk), &ioreq->acct); - } else { - block_acct_failed(blk_get_stats(blkdev->blk), &ioreq->acct); - } - break; - case BLKIF_OP_DISCARD: - default: - break; - } - qemu_bh_schedule(blkdev->bh); - -done: - aio_context_release(blkdev->ctx); -} - -static bool blk_split_discard(struct ioreq *ioreq, blkif_sector_t sector_number, - uint64_t nr_sectors) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - int64_t byte_offset; - int byte_chunk; - uint64_t byte_remaining, limit; - uint64_t sec_start = sector_number; - uint64_t sec_count = nr_sectors; - - /* Wrap around, or overflowing byte limit? */ - if (sec_start + sec_count < sec_count || - sec_start + sec_count > INT64_MAX >> BDRV_SECTOR_BITS) { - return false; - } - - limit = BDRV_REQUEST_MAX_SECTORS << BDRV_SECTOR_BITS; - byte_offset = sec_start << BDRV_SECTOR_BITS; - byte_remaining = sec_count << BDRV_SECTOR_BITS; - - do { - byte_chunk = byte_remaining > limit ? limit : byte_remaining; - ioreq->aio_inflight++; - blk_aio_pdiscard(blkdev->blk, byte_offset, byte_chunk, - qemu_aio_complete, ioreq); - byte_remaining -= byte_chunk; - byte_offset += byte_chunk; - } while (byte_remaining > 0); - - return true; -} - -static int ioreq_runio_qemu_aio(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - - ioreq->buf = qemu_memalign(XC_PAGE_SIZE, ioreq->size); - if (ioreq->req.nr_segments && - (ioreq->req.operation == BLKIF_OP_WRITE || - ioreq->req.operation == BLKIF_OP_FLUSH_DISKCACHE) && - ioreq_grant_copy(ioreq)) { - qemu_vfree(ioreq->buf); - goto err; - } - - ioreq->aio_inflight++; - if (ioreq->presync) { - blk_aio_flush(ioreq->blkdev->blk, qemu_aio_complete, ioreq); - return 0; - } - - switch (ioreq->req.operation) { - case BLKIF_OP_READ: - qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); - block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - ioreq->v.size, BLOCK_ACCT_READ); - ioreq->aio_inflight++; - blk_aio_preadv(blkdev->blk, ioreq->start, &ioreq->v, 0, - qemu_aio_complete, ioreq); - break; - case BLKIF_OP_WRITE: - case BLKIF_OP_FLUSH_DISKCACHE: - if (!ioreq->req.nr_segments) { - break; - } - - qemu_iovec_add(&ioreq->v, ioreq->buf, ioreq->size); - block_acct_start(blk_get_stats(blkdev->blk), &ioreq->acct, - ioreq->v.size, - ioreq->req.operation == BLKIF_OP_WRITE ? - BLOCK_ACCT_WRITE : BLOCK_ACCT_FLUSH); - ioreq->aio_inflight++; - blk_aio_pwritev(blkdev->blk, ioreq->start, &ioreq->v, 0, - qemu_aio_complete, ioreq); - break; - case BLKIF_OP_DISCARD: - { - struct blkif_request_discard *req = (void *)&ioreq->req; - if (!blk_split_discard(ioreq, req->sector_number, req->nr_sectors)) { - goto err; - } - break; - } - default: - /* unknown operation (shouldn't happen -- parse catches this) */ - goto err; - } - - qemu_aio_complete(ioreq, 0); - - return 0; - -err: - ioreq_finish(ioreq); - ioreq->status = BLKIF_RSP_ERROR; - return -1; -} - -static int blk_send_response_one(struct ioreq *ioreq) -{ - struct XenBlkDev *blkdev = ioreq->blkdev; - int send_notify = 0; - int have_requests = 0; - blkif_response_t *resp; - - /* Place on the response ring for the relevant domain. */ - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.native, - blkdev->rings.native.rsp_prod_pvt); - break; - case BLKIF_PROTOCOL_X86_32: - resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_32_part, - blkdev->rings.x86_32_part.rsp_prod_pvt); - break; - case BLKIF_PROTOCOL_X86_64: - resp = (blkif_response_t *) RING_GET_RESPONSE(&blkdev->rings.x86_64_part, - blkdev->rings.x86_64_part.rsp_prod_pvt); - break; - default: - return 0; - } - - resp->id = ioreq->req.id; - resp->operation = ioreq->req.operation; - resp->status = ioreq->status; - - blkdev->rings.common.rsp_prod_pvt++; - - RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&blkdev->rings.common, send_notify); - if (blkdev->rings.common.rsp_prod_pvt == blkdev->rings.common.req_cons) { - /* - * Tail check for pending requests. Allows frontend to avoid - * notifications if requests are already in flight (lower - * overheads and promotes batching). - */ - RING_FINAL_CHECK_FOR_REQUESTS(&blkdev->rings.common, have_requests); - } else if (RING_HAS_UNCONSUMED_REQUESTS(&blkdev->rings.common)) { - have_requests = 1; - } - - if (have_requests) { - blkdev->more_work++; - } - return send_notify; -} - -/* walk finished list, send outstanding responses, free requests */ -static void blk_send_response_all(struct XenBlkDev *blkdev) -{ - struct ioreq *ioreq; - int send_notify = 0; - - while (!QLIST_EMPTY(&blkdev->finished)) { - ioreq = QLIST_FIRST(&blkdev->finished); - send_notify += blk_send_response_one(ioreq); - ioreq_release(ioreq, true); - } - if (send_notify) { - xen_pv_send_notify(&blkdev->xendev); - } -} - -static int blk_get_request(struct XenBlkDev *blkdev, struct ioreq *ioreq, RING_IDX rc) -{ - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - memcpy(&ioreq->req, RING_GET_REQUEST(&blkdev->rings.native, rc), - sizeof(ioreq->req)); - break; - case BLKIF_PROTOCOL_X86_32: - blkif_get_x86_32_req(&ioreq->req, - RING_GET_REQUEST(&blkdev->rings.x86_32_part, rc)); - break; - case BLKIF_PROTOCOL_X86_64: - blkif_get_x86_64_req(&ioreq->req, - RING_GET_REQUEST(&blkdev->rings.x86_64_part, rc)); - break; - } - /* Prevent the compiler from accessing the on-ring fields instead. */ - barrier(); - return 0; -} - -static void blk_handle_requests(struct XenBlkDev *blkdev) -{ - RING_IDX rc, rp; - struct ioreq *ioreq; - - blkdev->more_work = 0; - - rc = blkdev->rings.common.req_cons; - rp = blkdev->rings.common.sring->req_prod; - xen_rmb(); /* Ensure we see queued requests up to 'rp'. */ - - blk_send_response_all(blkdev); - while (rc != rp) { - /* pull request from ring */ - if (RING_REQUEST_CONS_OVERFLOW(&blkdev->rings.common, rc)) { - break; - } - ioreq = ioreq_start(blkdev); - if (ioreq == NULL) { - blkdev->more_work++; - break; - } - blk_get_request(blkdev, ioreq, rc); - blkdev->rings.common.req_cons = ++rc; - - /* 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_pv_send_notify(&blkdev->xendev); - } - ioreq_release(ioreq, false); - continue; - } - - ioreq_runio_qemu_aio(ioreq); - } - - if (blkdev->more_work && blkdev->requests_inflight < blkdev->max_requests) { - qemu_bh_schedule(blkdev->bh); - } -} - -/* ------------------------------------------------------------- */ - -static void blk_bh(void *opaque) -{ - struct XenBlkDev *blkdev = opaque; - - aio_context_acquire(blkdev->ctx); - blk_handle_requests(blkdev); - aio_context_release(blkdev->ctx); -} - -static void blk_alloc(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - Error *err = NULL; - - trace_xen_disk_alloc(xendev->name); - - QLIST_INIT(&blkdev->inflight); - QLIST_INIT(&blkdev->finished); - QLIST_INIT(&blkdev->freelist); - - blkdev->iothread = iothread_create(xendev->name, &err); - assert(!err); - - blkdev->ctx = iothread_get_aio_context(blkdev->iothread); - blkdev->bh = aio_bh_new(blkdev->ctx, blk_bh, blkdev); -} - -static void blk_parse_discard(struct XenBlkDev *blkdev) -{ - struct XenDevice *xendev = &blkdev->xendev; - int enable; - - blkdev->feature_discard = true; - - if (xenstore_read_be_int(xendev, "discard-enable", &enable) == 0) { - blkdev->feature_discard = !!enable; - } - - if (blkdev->feature_discard) { - xenstore_write_be_int(xendev, "feature-discard", 1); - } -} - -static int blk_init(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int info = 0; - char *directiosafe = NULL; - - trace_xen_disk_init(xendev->name); - - /* read xenstore entries */ - if (blkdev->params == NULL) { - char *h = NULL; - blkdev->params = xenstore_read_be_str(xendev, "params"); - if (blkdev->params != NULL) { - h = strchr(blkdev->params, ':'); - } - if (h != NULL) { - blkdev->fileproto = blkdev->params; - blkdev->filename = h+1; - *h = 0; - } else { - blkdev->fileproto = ""; - blkdev->filename = blkdev->params; - } - } - 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(xendev, "mode"); - } - if (blkdev->type == NULL) { - blkdev->type = xenstore_read_be_str(xendev, "type"); - } - if (blkdev->dev == NULL) { - blkdev->dev = xenstore_read_be_str(xendev, "dev"); - } - if (blkdev->devtype == NULL) { - blkdev->devtype = xenstore_read_be_str(xendev, "device-type"); - } - directiosafe = xenstore_read_be_str(xendev, "direct-io-safe"); - blkdev->directiosafe = (directiosafe && atoi(directiosafe)); - - /* do we have all we need? */ - if (blkdev->params == NULL || - blkdev->mode == NULL || - blkdev->type == NULL || - blkdev->dev == NULL) { - goto out_error; - } - - /* read-only ? */ - if (strcmp(blkdev->mode, "w")) { - info |= VDISK_READONLY; - } - - /* cdrom ? */ - if (blkdev->devtype && !strcmp(blkdev->devtype, "cdrom")) { - info |= VDISK_CDROM; - } - - blkdev->file_blk = BLOCK_SIZE; - - /* fill info - * blk_connect supplies sector-size and sectors - */ - xenstore_write_be_int(xendev, "feature-flush-cache", 1); - xenstore_write_be_int(xendev, "info", info); - - xenstore_write_be_int(xendev, "max-ring-page-order", - MAX_RING_PAGE_ORDER); - - blk_parse_discard(blkdev); - - g_free(directiosafe); - return 0; - -out_error: - g_free(blkdev->params); - blkdev->params = NULL; - g_free(blkdev->mode); - blkdev->mode = NULL; - g_free(blkdev->type); - blkdev->type = NULL; - g_free(blkdev->dev); - blkdev->dev = NULL; - g_free(blkdev->devtype); - blkdev->devtype = NULL; - g_free(directiosafe); - blkdev->directiosafe = false; - return -1; -} - -static int blk_connect(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - int index, qflags; - bool readonly = true; - bool writethrough = true; - int order, ring_ref; - unsigned int ring_size, max_grants; - unsigned int i; - - trace_xen_disk_connect(xendev->name); - - /* read-only ? */ - if (blkdev->directiosafe) { - qflags = BDRV_O_NOCACHE | BDRV_O_NATIVE_AIO; - } else { - qflags = 0; - writethrough = false; - } - if (strcmp(blkdev->mode, "w") == 0) { - qflags |= BDRV_O_RDWR; - readonly = false; - } - if (blkdev->feature_discard) { - qflags |= BDRV_O_UNMAP; - } - - /* init qemu block driver */ - index = (xendev->dev - 202 * 256) / 16; - blkdev->dinfo = drive_get(IF_XEN, 0, index); - if (!blkdev->dinfo) { - Error *local_err = NULL; - QDict *options = NULL; - - if (strcmp(blkdev->fileproto, "")) { - options = qdict_new(); - qdict_put_str(options, "driver", blkdev->fileproto); - } - - /* setup via xenbus -> create new block driver instance */ - xen_pv_printf(xendev, 2, "create new bdrv (xenbus setup)\n"); - blkdev->blk = blk_new_open(blkdev->filename, NULL, options, - qflags, &local_err); - if (!blkdev->blk) { - xen_pv_printf(xendev, 0, "error: %s\n", - error_get_pretty(local_err)); - error_free(local_err); - return -1; - } - blk_set_enable_write_cache(blkdev->blk, !writethrough); - } else { - /* setup via qemu cmdline -> already setup for us */ - xen_pv_printf(xendev, 2, - "get configured bdrv (cmdline setup)\n"); - blkdev->blk = blk_by_legacy_dinfo(blkdev->dinfo); - if (blk_is_read_only(blkdev->blk) && !readonly) { - xen_pv_printf(xendev, 0, "Unexpected read-only drive"); - blkdev->blk = NULL; - return -1; - } - /* blkdev->blk is not create by us, we get a reference - * so we can blk_unref() unconditionally */ - blk_ref(blkdev->blk); - } - blk_attach_dev_legacy(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_pv_printf(xendev, 1, "blk_getlength: %d (%s) | drv %s\n", - (int)blkdev->file_size, strerror(-blkdev->file_size), - drv_name ?: "-"); - blkdev->file_size = 0; - } - - xen_pv_printf(xendev, 1, "type \"%s\", fileproto \"%s\", filename \"%s\"," - " size %" PRId64 " (%" PRId64 " MB)\n", - blkdev->type, blkdev->fileproto, blkdev->filename, - blkdev->file_size, blkdev->file_size / MiB); - - /* Fill in number of sector size and number of sectors */ - xenstore_write_be_int(xendev, "sector-size", blkdev->file_blk); - xenstore_write_be_int64(xendev, "sectors", - blkdev->file_size / blkdev->file_blk); - - if (xenstore_read_fe_int(xendev, "ring-page-order", - &order) == -1) { - blkdev->nr_ring_ref = 1; - - if (xenstore_read_fe_int(xendev, "ring-ref", - &ring_ref) == -1) { - return -1; - } - blkdev->ring_ref[0] = ring_ref; - - } else if (order >= 0 && order <= MAX_RING_PAGE_ORDER) { - blkdev->nr_ring_ref = 1 << order; - - for (i = 0; i < blkdev->nr_ring_ref; i++) { - char *key; - - key = g_strdup_printf("ring-ref%u", i); - if (!key) { - return -1; - } - - if (xenstore_read_fe_int(xendev, key, - &ring_ref) == -1) { - g_free(key); - return -1; - } - blkdev->ring_ref[i] = ring_ref; - - g_free(key); - } - } else { - xen_pv_printf(xendev, 0, "invalid ring-page-order: %d\n", - order); - return -1; - } - - if (xenstore_read_fe_int(xendev, "event-channel", - &xendev->remote_port) == -1) { - return -1; - } - - if (!xendev->protocol) { - blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_NATIVE) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_32) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_X86_32; - } else if (strcmp(xendev->protocol, XEN_IO_PROTO_ABI_X86_64) == 0) { - blkdev->protocol = BLKIF_PROTOCOL_X86_64; - } else { - blkdev->protocol = BLKIF_PROTOCOL_NATIVE; - } - - ring_size = XC_PAGE_SIZE * blkdev->nr_ring_ref; - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - { - blkdev->max_requests = __CONST_RING_SIZE(blkif, ring_size); - break; - } - case BLKIF_PROTOCOL_X86_32: - { - blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_32, ring_size); - break; - } - case BLKIF_PROTOCOL_X86_64: - { - blkdev->max_requests = __CONST_RING_SIZE(blkif_x86_64, ring_size); - break; - } - default: - return -1; - } - - /* Add on the number needed for the ring pages */ - max_grants = blkdev->nr_ring_ref; - - xen_be_set_max_grant_refs(xendev, max_grants); - blkdev->sring = xen_be_map_grant_refs(xendev, blkdev->ring_ref, - blkdev->nr_ring_ref, - PROT_READ | PROT_WRITE); - if (!blkdev->sring) { - return -1; - } - - switch (blkdev->protocol) { - case BLKIF_PROTOCOL_NATIVE: - { - blkif_sring_t *sring_native = blkdev->sring; - BACK_RING_INIT(&blkdev->rings.native, sring_native, ring_size); - break; - } - case BLKIF_PROTOCOL_X86_32: - { - blkif_x86_32_sring_t *sring_x86_32 = blkdev->sring; - - BACK_RING_INIT(&blkdev->rings.x86_32_part, sring_x86_32, ring_size); - break; - } - case BLKIF_PROTOCOL_X86_64: - { - blkif_x86_64_sring_t *sring_x86_64 = blkdev->sring; - - BACK_RING_INIT(&blkdev->rings.x86_64_part, sring_x86_64, ring_size); - break; - } - } - - blk_set_aio_context(blkdev->blk, blkdev->ctx); - - xen_be_bind_evtchn(xendev); - - xen_pv_printf(xendev, 1, "ok: proto %s, nr-ring-ref %u, " - "remote port %d, local port %d\n", - xendev->protocol, blkdev->nr_ring_ref, - xendev->remote_port, xendev->local_port); - return 0; -} - -static void blk_disconnect(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - trace_xen_disk_disconnect(xendev->name); - - aio_context_acquire(blkdev->ctx); - - if (blkdev->blk) { - blk_set_aio_context(blkdev->blk, qemu_get_aio_context()); - blk_detach_dev(blkdev->blk, blkdev); - blk_unref(blkdev->blk); - blkdev->blk = NULL; - } - xen_pv_unbind_evtchn(xendev); - - aio_context_release(blkdev->ctx); - - if (blkdev->sring) { - xen_be_unmap_grant_refs(xendev, blkdev->sring, - blkdev->nr_ring_ref); - blkdev->sring = NULL; - } -} - -static int blk_free(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - struct ioreq *ioreq; - - trace_xen_disk_free(xendev->name); - - blk_disconnect(xendev); - - while (!QLIST_EMPTY(&blkdev->freelist)) { - ioreq = QLIST_FIRST(&blkdev->freelist); - QLIST_REMOVE(ioreq, list); - qemu_iovec_destroy(&ioreq->v); - g_free(ioreq); - } - - g_free(blkdev->params); - g_free(blkdev->mode); - g_free(blkdev->type); - g_free(blkdev->dev); - g_free(blkdev->devtype); - qemu_bh_delete(blkdev->bh); - iothread_destroy(blkdev->iothread); - return 0; -} - -static void blk_event(struct XenDevice *xendev) -{ - struct XenBlkDev *blkdev = container_of(xendev, struct XenBlkDev, xendev); - - qemu_bh_schedule(blkdev->bh); -} - -struct XenDevOps xen_blkdev_ops = { - .flags = DEVOPS_FLAG_NEED_GNTDEV, - .size = sizeof(struct XenBlkDev), - .alloc = blk_alloc, - .init = blk_init, - .initialise = blk_connect, - .disconnect = blk_disconnect, - .event = blk_event, - .free = blk_free, -}; diff --git a/hw/bt/Kconfig b/hw/bt/Kconfig new file mode 100644 index 0000000000..554a9ee75e --- /dev/null +++ b/hw/bt/Kconfig @@ -0,0 +1,2 @@ +config BLUETOOTH + bool diff --git a/hw/bt/hci-csr.c b/hw/bt/hci-csr.c index 0341ded50c..fa6660a113 100644 --- a/hw/bt/hci-csr.c +++ b/hw/bt/hci-csr.c @@ -501,7 +501,7 @@ static const TypeInfo char_hci_type_info = { Chardev *uart_hci_init(void) { return qemu_chardev_new(NULL, TYPE_CHARDEV_HCI, - NULL, &error_abort); + NULL, NULL, &error_abort); } static void register_types(void) diff --git a/hw/char/Kconfig b/hw/char/Kconfig new file mode 100644 index 0000000000..6360c9fffa --- /dev/null +++ b/hw/char/Kconfig @@ -0,0 +1,42 @@ +config ESCC + bool + +config PARALLEL + bool + default y + depends on ISA_BUS + +config PL011 + bool + +config SERIAL + bool + +config SERIAL_ISA + bool + default y + depends on ISA_BUS + select SERIAL + +config SERIAL_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SERIAL + +config VIRTIO_SERIAL + bool + default y + depends on VIRTIO + +config STM32F2XX_USART + bool + +config CMSDK_APB_UART + bool + +config SCLPCONSOLE + bool + +config TERMINAL3270 + bool diff --git a/hw/char/Makefile.objs b/hw/char/Makefile.objs index b570531291..cf086e7114 100644 --- a/hw/char/Makefile.objs +++ b/hw/char/Makefile.objs @@ -1,7 +1,8 @@ common-obj-$(CONFIG_IPACK) += ipoctal232.o common-obj-$(CONFIG_ESCC) += escc.o +common-obj-$(CONFIG_NRF51_SOC) += nrf51_uart.o common-obj-$(CONFIG_PARALLEL) += parallel.o -common-obj-$(CONFIG_PARALLEL) += parallel-isa.o +common-obj-$(CONFIG_ISA_BUS) += parallel-isa.o common-obj-$(CONFIG_PL011) += pl011.o common-obj-$(CONFIG_SERIAL) += serial.o common-obj-$(CONFIG_SERIAL_ISA) += serial-isa.o diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index bac11bec58..e1d258b611 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -239,9 +239,10 @@ static const MemoryRegionOps grlib_apbuart_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int grlib_apbuart_init(SysBusDevice *dev) +static void grlib_apbuart_realize(DeviceState *dev, Error **errp) { UART *uart = GRLIB_APB_UART(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); qemu_chr_fe_set_handlers(&uart->chr, grlib_apbuart_can_receive, @@ -249,14 +250,12 @@ static int grlib_apbuart_init(SysBusDevice *dev) grlib_apbuart_event, NULL, uart, NULL, true); - sysbus_init_irq(dev, &uart->irq); + sysbus_init_irq(sbd, &uart->irq); memory_region_init_io(&uart->iomem, OBJECT(uart), &grlib_apbuart_ops, uart, "uart", UART_REG_SIZE); - sysbus_init_mmio(dev, &uart->iomem); - - return 0; + sysbus_init_mmio(sbd, &uart->iomem); } static void grlib_apbuart_reset(DeviceState *d) @@ -280,9 +279,8 @@ static Property grlib_apbuart_properties[] = { static void grlib_apbuart_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = grlib_apbuart_init; + dc->realize = grlib_apbuart_realize; dc->reset = grlib_apbuart_reset; dc->props = grlib_apbuart_properties; } diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 0747db9f2b..1e363190e3 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -74,8 +74,9 @@ static void imx_update(IMXSerialState *s) mask = (s->ucr1 & UCR1_TXMPTYEN) ? USR2_TXFE : 0; /* * TCEN and TXDC are both bit 3 + * RDR and DREN are both bit 0 */ - mask |= s->ucr4 & UCR4_TCEN; + mask |= s->ucr4 & (UCR4_TCEN | UCR4_DREN); usr2 = s->usr2 & mask; diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c new file mode 100644 index 0000000000..2f5fae6167 --- /dev/null +++ b/hw/char/nrf51_uart.c @@ -0,0 +1,330 @@ +/* + * nRF51 SoC UART emulation + * + * See nRF51 Series Reference Manual, "29 Universal Asynchronous + * Receiver/Transmitter" for hardware specifications: + * http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * + * Copyright (c) 2018 Julia Suvorova + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "hw/char/nrf51_uart.h" +#include "trace.h" + +static void nrf51_uart_update_irq(NRF51UARTState *s) +{ + bool irq = false; + + irq |= (s->reg[R_UART_RXDRDY] && + (s->reg[R_UART_INTEN] & R_UART_INTEN_RXDRDY_MASK)); + irq |= (s->reg[R_UART_TXDRDY] && + (s->reg[R_UART_INTEN] & R_UART_INTEN_TXDRDY_MASK)); + irq |= (s->reg[R_UART_ERROR] && + (s->reg[R_UART_INTEN] & R_UART_INTEN_ERROR_MASK)); + irq |= (s->reg[R_UART_RXTO] && + (s->reg[R_UART_INTEN] & R_UART_INTEN_RXTO_MASK)); + + qemu_set_irq(s->irq, irq); +} + +static uint64_t uart_read(void *opaque, hwaddr addr, unsigned int size) +{ + NRF51UARTState *s = NRF51_UART(opaque); + uint64_t r; + + if (!s->enabled) { + return 0; + } + + switch (addr) { + case A_UART_RXD: + r = s->rx_fifo[s->rx_fifo_pos]; + if (s->rx_started && s->rx_fifo_len) { + s->rx_fifo_pos = (s->rx_fifo_pos + 1) % UART_FIFO_LENGTH; + s->rx_fifo_len--; + if (s->rx_fifo_len) { + s->reg[R_UART_RXDRDY] = 1; + nrf51_uart_update_irq(s); + } + qemu_chr_fe_accept_input(&s->chr); + } + break; + case A_UART_INTENSET: + case A_UART_INTENCLR: + case A_UART_INTEN: + r = s->reg[R_UART_INTEN]; + break; + default: + r = s->reg[addr / 4]; + break; + } + + trace_nrf51_uart_read(addr, r, size); + + return r; +} + +static gboolean uart_transmit(GIOChannel *chan, GIOCondition cond, void *opaque) +{ + NRF51UARTState *s = NRF51_UART(opaque); + int r; + uint8_t c = s->reg[R_UART_TXD]; + + s->watch_tag = 0; + + r = qemu_chr_fe_write(&s->chr, &c, 1); + if (r <= 0) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + uart_transmit, s); + if (!s->watch_tag) { + /* The hardware has no transmit error reporting, + * so silently drop the byte + */ + goto buffer_drained; + } + return FALSE; + } + +buffer_drained: + s->reg[R_UART_TXDRDY] = 1; + s->pending_tx_byte = false; + return FALSE; +} + +static void uart_cancel_transmit(NRF51UARTState *s) +{ + if (s->watch_tag) { + g_source_remove(s->watch_tag); + s->watch_tag = 0; + } +} + +static void uart_write(void *opaque, hwaddr addr, + uint64_t value, unsigned int size) +{ + NRF51UARTState *s = NRF51_UART(opaque); + + trace_nrf51_uart_write(addr, value, size); + + if (!s->enabled && (addr != A_UART_ENABLE)) { + return; + } + + switch (addr) { + case A_UART_TXD: + if (!s->pending_tx_byte && s->tx_started) { + s->reg[R_UART_TXD] = value; + s->pending_tx_byte = true; + uart_transmit(NULL, G_IO_OUT, s); + } + break; + case A_UART_INTEN: + s->reg[R_UART_INTEN] = value; + break; + case A_UART_INTENSET: + s->reg[R_UART_INTEN] |= value; + break; + case A_UART_INTENCLR: + s->reg[R_UART_INTEN] &= ~value; + break; + case A_UART_TXDRDY ... A_UART_RXTO: + s->reg[addr / 4] = value; + break; + case A_UART_ERRORSRC: + s->reg[addr / 4] &= ~value; + break; + case A_UART_RXD: + break; + case A_UART_RXDRDY: + if (value == 0) { + s->reg[R_UART_RXDRDY] = 0; + } + break; + case A_UART_STARTTX: + if (value == 1) { + s->tx_started = true; + } + break; + case A_UART_STARTRX: + if (value == 1) { + s->rx_started = true; + } + break; + case A_UART_ENABLE: + if (value) { + if (value == 4) { + s->enabled = true; + } + break; + } + s->enabled = false; + value = 1; + /* fall through */ + case A_UART_SUSPEND: + case A_UART_STOPTX: + if (value == 1) { + s->tx_started = false; + } + /* fall through */ + case A_UART_STOPRX: + if (addr != A_UART_STOPTX && value == 1) { + s->rx_started = false; + s->reg[R_UART_RXTO] = 1; + } + break; + default: + s->reg[addr / 4] = value; + break; + } + nrf51_uart_update_irq(s); +} + +static const MemoryRegionOps uart_ops = { + .read = uart_read, + .write = uart_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void nrf51_uart_reset(DeviceState *dev) +{ + NRF51UARTState *s = NRF51_UART(dev); + + s->pending_tx_byte = 0; + + uart_cancel_transmit(s); + + memset(s->reg, 0, sizeof(s->reg)); + + s->reg[R_UART_PSELRTS] = 0xFFFFFFFF; + s->reg[R_UART_PSELTXD] = 0xFFFFFFFF; + s->reg[R_UART_PSELCTS] = 0xFFFFFFFF; + s->reg[R_UART_PSELRXD] = 0xFFFFFFFF; + s->reg[R_UART_BAUDRATE] = 0x4000000; + + s->rx_fifo_len = 0; + s->rx_fifo_pos = 0; + s->rx_started = false; + s->tx_started = false; + s->enabled = false; +} + +static void uart_receive(void *opaque, const uint8_t *buf, int size) +{ + + NRF51UARTState *s = NRF51_UART(opaque); + int i; + + if (size == 0 || s->rx_fifo_len >= UART_FIFO_LENGTH) { + return; + } + + for (i = 0; i < size; i++) { + uint32_t pos = (s->rx_fifo_pos + s->rx_fifo_len) % UART_FIFO_LENGTH; + s->rx_fifo[pos] = buf[i]; + s->rx_fifo_len++; + } + + s->reg[R_UART_RXDRDY] = 1; + nrf51_uart_update_irq(s); +} + +static int uart_can_receive(void *opaque) +{ + NRF51UARTState *s = NRF51_UART(opaque); + + return s->rx_started ? (UART_FIFO_LENGTH - s->rx_fifo_len) : 0; +} + +static void uart_event(void *opaque, int event) +{ + NRF51UARTState *s = NRF51_UART(opaque); + + if (event == CHR_EVENT_BREAK) { + s->reg[R_UART_ERRORSRC] |= 3; + s->reg[R_UART_ERROR] = 1; + nrf51_uart_update_irq(s); + } +} + +static void nrf51_uart_realize(DeviceState *dev, Error **errp) +{ + NRF51UARTState *s = NRF51_UART(dev); + + qemu_chr_fe_set_handlers(&s->chr, uart_can_receive, uart_receive, + uart_event, NULL, s, NULL, true); +} + +static void nrf51_uart_init(Object *obj) +{ + NRF51UARTState *s = NRF51_UART(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &uart_ops, s, + "nrf51_soc.uart", UART_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); +} + +static int nrf51_uart_post_load(void *opaque, int version_id) +{ + NRF51UARTState *s = NRF51_UART(opaque); + + if (s->pending_tx_byte) { + s->watch_tag = qemu_chr_fe_add_watch(&s->chr, G_IO_OUT | G_IO_HUP, + uart_transmit, s); + } + + return 0; +} + +static const VMStateDescription nrf51_uart_vmstate = { + .name = "nrf51_soc.uart", + .post_load = nrf51_uart_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(reg, NRF51UARTState, 0x56C), + VMSTATE_UINT8_ARRAY(rx_fifo, NRF51UARTState, UART_FIFO_LENGTH), + VMSTATE_UINT32(rx_fifo_pos, NRF51UARTState), + VMSTATE_UINT32(rx_fifo_len, NRF51UARTState), + VMSTATE_BOOL(rx_started, NRF51UARTState), + VMSTATE_BOOL(tx_started, NRF51UARTState), + VMSTATE_BOOL(pending_tx_byte, NRF51UARTState), + VMSTATE_BOOL(enabled, NRF51UARTState), + VMSTATE_END_OF_LIST() + } +}; + +static Property nrf51_uart_properties[] = { + DEFINE_PROP_CHR("chardev", NRF51UARTState, chr), + DEFINE_PROP_END_OF_LIST(), +}; + +static void nrf51_uart_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = nrf51_uart_reset; + dc->realize = nrf51_uart_realize; + dc->props = nrf51_uart_properties; + dc->vmsd = &nrf51_uart_vmstate; +} + +static const TypeInfo nrf51_uart_info = { + .name = TYPE_NRF51_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51UARTState), + .instance_init = nrf51_uart_init, + .class_init = nrf51_uart_class_init +}; + +static void nrf51_uart_register_types(void) +{ + type_register_static(&nrf51_uart_info); +} + +type_init(nrf51_uart_register_types) diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 6fd1b9cf6b..b3bb1cfcec 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -63,7 +63,7 @@ struct omap_uart_s *omap_uart_init(hwaddr base, s->irq = irq; s->serial = serial_mm_init(get_system_memory(), base, 2, irq, omap_clk_getrate(fclk)/16, - chr ?: qemu_chr_new(label, "null"), + chr ?: qemu_chr_new(label, "null", NULL), DEVICE_NATIVE_ENDIAN); return s; } @@ -183,6 +183,6 @@ void omap_uart_attach(struct omap_uart_s *s, Chardev *chr) /* TODO: Should reuse or destroy current s->serial */ s->serial = serial_mm_init(get_system_memory(), s->base, 2, s->irq, omap_clk_getrate(s->fclk) / 16, - chr ?: qemu_chr_new("null", "null"), + chr ?: qemu_chr_new("null", "null", NULL), DEVICE_NATIVE_ENDIAN); } diff --git a/hw/char/parallel-isa.c b/hw/char/parallel-isa.c index 639e179585..a043832e72 100644 --- a/hw/char/parallel-isa.c +++ b/hw/char/parallel-isa.c @@ -1,6 +1,9 @@ /* * QEMU Parallel PORT (ISA bus helpers) * + * These functions reside in a separate file since they also might be + * required for linking when compiling QEMU without CONFIG_PARALLEL. + * * Copyright (c) 2003 Fabrice Bellard * * SPDX-License-Identifier: MIT diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 2aa277fc4f..e5dd448f85 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -7,40 +7,24 @@ * This code is licensed under the GPL. */ +/* + * QEMU interface: + * + sysbus MMIO region 0: device registers + * + sysbus IRQ 0: UARTINTR (combined interrupt line) + * + sysbus IRQ 1: UARTRXINTR (receive FIFO interrupt line) + * + sysbus IRQ 2: UARTTXINTR (transmit FIFO interrupt line) + * + sysbus IRQ 3: UARTRTINTR (receive timeout interrupt line) + * + sysbus IRQ 4: UARTMSINTR (momem status interrupt line) + * + sysbus IRQ 5: UARTEINTR (error interrupt line) + */ + #include "qemu/osdep.h" +#include "hw/char/pl011.h" #include "hw/sysbus.h" #include "chardev/char-fe.h" #include "qemu/log.h" #include "trace.h" -#define TYPE_PL011 "pl011" -#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011) - -typedef struct PL011State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t readbuff; - uint32_t flags; - uint32_t lcr; - uint32_t rsr; - uint32_t cr; - uint32_t dmacr; - uint32_t int_enabled; - uint32_t int_level; - uint32_t read_fifo[16]; - uint32_t ilpr; - uint32_t ibrd; - uint32_t fbrd; - uint32_t ifl; - int read_pos; - int read_count; - int read_trigger; - CharBackend chr; - qemu_irq irq; - const unsigned char *id; -} PL011State; - #define PL011_INT_TX 0x20 #define PL011_INT_RX 0x10 @@ -49,18 +33,46 @@ typedef struct PL011State { #define PL011_FLAG_TXFF 0x20 #define PL011_FLAG_RXFE 0x10 +/* Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC */ +#define INT_OE (1 << 10) +#define INT_BE (1 << 9) +#define INT_PE (1 << 8) +#define INT_FE (1 << 7) +#define INT_RT (1 << 6) +#define INT_TX (1 << 5) +#define INT_RX (1 << 4) +#define INT_DSR (1 << 3) +#define INT_DCD (1 << 2) +#define INT_CTS (1 << 1) +#define INT_RI (1 << 0) +#define INT_E (INT_OE | INT_BE | INT_PE | INT_FE) +#define INT_MS (INT_RI | INT_DSR | INT_DCD | INT_CTS) + static const unsigned char pl011_id_arm[8] = { 0x11, 0x10, 0x14, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; static const unsigned char pl011_id_luminary[8] = { 0x11, 0x00, 0x18, 0x01, 0x0d, 0xf0, 0x05, 0xb1 }; +/* Which bits in the interrupt status matter for each outbound IRQ line ? */ +static const uint32_t irqmask[] = { + INT_E | INT_MS | INT_RT | INT_TX | INT_RX, /* combined IRQ */ + INT_RX, + INT_TX, + INT_RT, + INT_MS, + INT_E, +}; + static void pl011_update(PL011State *s) { uint32_t flags; + int i; flags = s->int_level & s->int_enabled; trace_pl011_irq_state(flags != 0); - qemu_set_irq(s->irq, flags != 0); + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + qemu_set_irq(s->irq[i], (flags & irqmask[i]) != 0); + } } static uint64_t pl011_read(void *opaque, hwaddr offset, @@ -131,7 +143,7 @@ static uint64_t pl011_read(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "pl011_read: Bad offset %x\n", (int)offset); + "pl011_read: Bad offset 0x%x\n", (int)offset); r = 0; break; } @@ -220,7 +232,7 @@ static void pl011_write(void *opaque, hwaddr offset, break; default: qemu_log_mask(LOG_GUEST_ERROR, - "pl011_write: Bad offset %x\n", (int)offset); + "pl011_write: Bad offset 0x%x\n", (int)offset); } } @@ -311,10 +323,13 @@ static void pl011_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); PL011State *s = PL011(obj); + int i; memory_region_init_io(&s->iomem, OBJECT(s), &pl011_ops, s, "pl011", 0x1000); sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_irq(sbd, &s->irq); + for (i = 0; i < ARRAY_SIZE(s->irq); i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } s->read_trigger = 1; s->ifl = 0x12; @@ -357,7 +372,7 @@ static void pl011_luminary_init(Object *obj) } static const TypeInfo pl011_luminary_info = { - .name = "pl011_luminary", + .name = TYPE_PL011_LUMINARY, .parent = TYPE_PL011, .instance_init = pl011_luminary_init, }; diff --git a/hw/char/serial.c b/hw/char/serial.c index 251f40fdac..7c42a2abfc 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -345,9 +345,9 @@ static void serial_ioport_write(void *opaque, hwaddr addr, uint64_t val, default: case 0: if (s->lcr & UART_LCR_DLAB) { - if (size == 2) { + if (size == 1) { s->divider = (s->divider & 0xff00) | val; - } else if (size == 4) { + } else { s->divider = val; } serial_update_parameters(s); @@ -611,7 +611,7 @@ static void serial_receive1(void *opaque, const uint8_t *buf, int size) SerialState *s = opaque; if (s->wakeup) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); } if(s->fcr & UART_FCR_FE) { int i; diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 373a40595f..67740b7ee6 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -29,6 +29,7 @@ #include "hw/sh4/sh.h" #include "chardev/char-fe.h" #include "qapi/error.h" +#include "qemu/timer.h" //#define DEBUG_SERIAL @@ -63,6 +64,8 @@ typedef struct { int rtrg; CharBackend chr; + QEMUTimer *fifo_timeout_timer; + uint64_t etu; /* Elementary Time Unit (ns) */ qemu_irq eri; qemu_irq rxi; @@ -87,7 +90,7 @@ static void sh_serial_write(void *opaque, hwaddr offs, #ifdef DEBUG_SERIAL printf("sh_serial: write offs=0x%02x val=0x%02x\n", - offs, val); + offs, val); #endif switch(offs) { case 0x00: /* SMR */ @@ -95,17 +98,17 @@ static void sh_serial_write(void *opaque, hwaddr offs, return; case 0x04: /* BRR */ s->brr = val; - return; + return; case 0x08: /* SCR */ /* TODO : For SH7751, SCIF mask should be 0xfb. */ s->scr = val & ((s->feat & SH_SERIAL_FEAT_SCIF) ? 0xfa : 0xff); if (!(val & (1 << 5))) s->flags |= SH_SERIAL_FLAG_TEND; if ((s->feat & SH_SERIAL_FEAT_SCIF) && s->txi) { - qemu_set_irq(s->txi, val & (1 << 7)); + qemu_set_irq(s->txi, val & (1 << 7)); } if (!(val & (1 << 6))) { - qemu_set_irq(s->rxi, 0); + qemu_set_irq(s->rxi, 0); } return; case 0x0c: /* FTDR / TDR */ @@ -114,9 +117,9 @@ static void sh_serial_write(void *opaque, hwaddr offs, /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&s->chr, &ch, 1); - } - s->dr = val; - s->flags &= ~SH_SERIAL_FLAG_TDE; + } + s->dr = val; + s->flags &= ~SH_SERIAL_FLAG_TDE; return; #if 0 case 0x14: /* FRDR / RDR */ @@ -207,7 +210,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, break; case 0x04: ret = s->brr; - break; + break; case 0x08: ret = s->scr; break; @@ -285,7 +288,7 @@ static uint64_t sh_serial_read(void *opaque, hwaddr offs, } #ifdef DEBUG_SERIAL printf("sh_serial: read offs=0x%02x val=0x%x\n", - offs, ret); + offs, ret); #endif if (ret & ~((1 << 16) - 1)) { @@ -314,6 +317,16 @@ static int sh_serial_can_receive1(void *opaque) return sh_serial_can_receive(s); } +static void sh_serial_timeout_int(void *opaque) +{ + sh_serial_state *s = opaque; + + s->flags |= SH_SERIAL_FLAG_RDF; + if (s->scr & (1 << 6) && s->rxi) { + qemu_set_irq(s->rxi, 1); + } +} + static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) { sh_serial_state *s = opaque; @@ -330,8 +343,12 @@ static void sh_serial_receive1(void *opaque, const uint8_t *buf, int size) if (s->rx_cnt >= s->rtrg) { s->flags |= SH_SERIAL_FLAG_RDF; if (s->scr & (1 << 6) && s->rxi) { + timer_del(s->fifo_timeout_timer); qemu_set_irq(s->rxi, 1); } + } else { + timer_mod(s->fifo_timeout_timer, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 15 * s->etu); } } } @@ -402,6 +419,9 @@ void sh_serial_init(MemoryRegion *sysmem, sh_serial_event, NULL, s, NULL, true); } + s->fifo_timeout_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, + sh_serial_timeout_int, s); + s->etu = NANOSECONDS_PER_SECOND / 9600; s->eri = eri_source; s->rxi = rxi_source; s->txi = txi_source; diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 6748334ded..617303dbaf 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -10,27 +10,27 @@ #define VTERM_BUFSIZE 16 -typedef struct VIOsPAPRVTYDevice { - VIOsPAPRDevice sdev; +typedef struct SpaprVioVty { + SpaprVioDevice sdev; CharBackend chardev; uint32_t in, out; uint8_t buf[VTERM_BUFSIZE]; -} VIOsPAPRVTYDevice; +} SpaprVioVty; #define TYPE_VIO_SPAPR_VTY_DEVICE "spapr-vty" #define VIO_SPAPR_VTY_DEVICE(obj) \ - OBJECT_CHECK(VIOsPAPRVTYDevice, (obj), TYPE_VIO_SPAPR_VTY_DEVICE) + OBJECT_CHECK(SpaprVioVty, (obj), TYPE_VIO_SPAPR_VTY_DEVICE) static int vty_can_receive(void *opaque) { - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(opaque); + SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(opaque); return VTERM_BUFSIZE - (dev->in - dev->out); } static void vty_receive(void *opaque, const uint8_t *buf, int size) { - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(opaque); + SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(opaque); int i; if ((dev->in == dev->out) && size) { @@ -51,9 +51,9 @@ static void vty_receive(void *opaque, const uint8_t *buf, int size) } } -static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) +static int vty_getchars(SpaprVioDevice *sdev, uint8_t *buf, int max) { - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); + SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev); int n = 0; while ((n < max) && (dev->out != dev->in)) { @@ -83,18 +83,18 @@ static int vty_getchars(VIOsPAPRDevice *sdev, uint8_t *buf, int max) return n; } -void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len) +void vty_putchars(SpaprVioDevice *sdev, uint8_t *buf, int len) { - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); + SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev); /* XXX this blocks entire thread. Rewrite to use * qemu_chr_fe_write and background I/O callbacks */ qemu_chr_fe_write_all(&dev->chardev, buf, len); } -static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) +static void spapr_vty_realize(SpaprVioDevice *sdev, Error **errp) { - VIOsPAPRVTYDevice *dev = VIO_SPAPR_VTY_DEVICE(sdev); + SpaprVioVty *dev = VIO_SPAPR_VTY_DEVICE(sdev); if (!qemu_chr_fe_backend_connected(&dev->chardev)) { error_setg(errp, "chardev property not set"); @@ -106,14 +106,14 @@ static void spapr_vty_realize(VIOsPAPRDevice *sdev, Error **errp) } /* Forward declaration */ -static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_put_term_char(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong len = args[1]; target_ulong char0_7 = args[2]; target_ulong char8_15 = args[3]; - VIOsPAPRDevice *sdev; + SpaprVioDevice *sdev; uint8_t buf[16]; sdev = vty_lookup(spapr, reg); @@ -133,14 +133,14 @@ static target_ulong h_put_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_get_term_char(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong *len = args + 0; target_ulong *char0_7 = args + 1; target_ulong *char8_15 = args + 2; - VIOsPAPRDevice *sdev; + SpaprVioDevice *sdev; uint8_t buf[16]; sdev = vty_lookup(spapr, reg); @@ -159,7 +159,7 @@ static target_ulong h_get_term_char(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev) +void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev) { DeviceState *dev; @@ -169,8 +169,8 @@ void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev) } static Property spapr_vty_properties[] = { - DEFINE_SPAPR_PROPERTIES(VIOsPAPRVTYDevice, sdev), - DEFINE_PROP_CHR("chardev", VIOsPAPRVTYDevice, chardev), + DEFINE_SPAPR_PROPERTIES(SpaprVioVty, sdev), + DEFINE_PROP_CHR("chardev", SpaprVioVty, chardev), DEFINE_PROP_END_OF_LIST(), }; @@ -179,11 +179,11 @@ static const VMStateDescription vmstate_spapr_vty = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_SPAPR_VIO(sdev, VIOsPAPRVTYDevice), + VMSTATE_SPAPR_VIO(sdev, SpaprVioVty), - VMSTATE_UINT32(in, VIOsPAPRVTYDevice), - VMSTATE_UINT32(out, VIOsPAPRVTYDevice), - VMSTATE_BUFFER(buf, VIOsPAPRVTYDevice), + VMSTATE_UINT32(in, SpaprVioVty), + VMSTATE_UINT32(out, SpaprVioVty), + VMSTATE_BUFFER(buf, SpaprVioVty), VMSTATE_END_OF_LIST() }, }; @@ -191,7 +191,7 @@ static const VMStateDescription vmstate_spapr_vty = { static void spapr_vty_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); k->realize = spapr_vty_realize; k->dt_name = "vty"; @@ -205,13 +205,13 @@ static void spapr_vty_class_init(ObjectClass *klass, void *data) static const TypeInfo spapr_vty_info = { .name = TYPE_VIO_SPAPR_VTY_DEVICE, .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(VIOsPAPRVTYDevice), + .instance_size = sizeof(SpaprVioVty), .class_init = spapr_vty_class_init, }; -VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus) +SpaprVioDevice *spapr_vty_get_default(SpaprVioBus *bus) { - VIOsPAPRDevice *sdev, *selected; + SpaprVioDevice *sdev, *selected; BusChild *kid; /* @@ -246,9 +246,9 @@ VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus) return selected; } -VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg) +SpaprVioDevice *vty_lookup(SpaprMachineState *spapr, target_ulong reg) { - VIOsPAPRDevice *sdev; + SpaprVioDevice *sdev; sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); if (!sdev && reg == 0) { diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 032b5fda13..10392c70e2 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -53,14 +53,13 @@ static void stm32f2xx_usart_receive(void *opaque, const uint8_t *buf, int size) { STM32F2XXUsartState *s = opaque; - s->usart_dr = *buf; - if (!(s->usart_cr1 & USART_CR1_UE && s->usart_cr1 & USART_CR1_RE)) { /* USART not enabled - drop the chars */ DB_PRINT("Dropping the chars\n"); return; } + s->usart_dr = *buf; s->usart_sr |= USART_SR_RXNE; if (s->usart_cr1 & USART_CR1_RXNEIE) { @@ -202,7 +201,7 @@ static void stm32f2xx_usart_init(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); memory_region_init_io(&s->mmio, obj, &stm32f2xx_usart_ops, s, - TYPE_STM32F2XX_USART, 0x2000); + TYPE_STM32F2XX_USART, 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index e9c45e55b1..35b079d5c4 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -31,7 +31,7 @@ typedef struct Terminal3270 { uint8_t outv[OUTPUT_BUFFER_SIZE]; int in_len; bool handshake_done; - GSource *timer_src; + guint timer_tag; } Terminal3270; #define TYPE_TERMINAL_3270 "x-terminal3270" @@ -47,10 +47,9 @@ static int terminal_can_read(void *opaque) static void terminal_timer_cancel(Terminal3270 *t) { - if (t->timer_src) { - g_source_destroy(t->timer_src); - g_source_unref(t->timer_src); - t->timer_src = NULL; + if (t->timer_tag) { + g_source_remove(t->timer_tag); + t->timer_tag = 0; } } @@ -100,8 +99,7 @@ static void terminal_read(void *opaque, const uint8_t *buf, int size) assert(size <= (INPUT_BUFFER_SIZE - t->in_len)); terminal_timer_cancel(t); - t->timer_src = qemu_chr_timeout_add_ms(t->chr.chr, 600 * 1000, - send_timing_mark_cb, t); + t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t); memcpy(&t->inv[t->in_len], buf, size); t->in_len += size; if (t->in_len < 2) { @@ -160,8 +158,7 @@ static void chr_event(void *opaque, int event) * char-socket.c. Once qemu receives the terminal-type of the * client, mark handshake done and trigger everything rolling again. */ - t->timer_src = qemu_chr_timeout_add_ms(t->chr.chr, 600 * 1000, - send_timing_mark_cb, t); + t->timer_tag = g_timeout_add_seconds(600, send_timing_mark_cb, t); break; case CHR_EVENT_CLOSED: sch->curr_status.scsw.dstat = SCSW_DSTAT_DEVICE_END; diff --git a/hw/char/trace-events b/hw/char/trace-events index b64213d4dd..2ce7f2f998 100644 --- a/hw/char/trace-events +++ b/hw/char/trace-events @@ -1,47 +1,47 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/char/parallel.c +# parallel.c parallel_ioport_read(const char *desc, uint16_t addr, uint8_t value) "read [%s] addr 0x%02x val 0x%02x" parallel_ioport_write(const char *desc, uint16_t addr, uint8_t value) "write [%s] addr 0x%02x val 0x%02x" -# hw/char/serial.c +# serial.c serial_ioport_read(uint16_t addr, uint8_t value) "read addr 0x%02x val 0x%02x" serial_ioport_write(uint16_t addr, uint8_t value) "write addr 0x%02x val 0x%02x" -# hw/char/virtio-serial-bus.c +# virtio-serial-bus.c virtio_serial_send_control_event(unsigned int port, uint16_t event, uint16_t value) "port %u, event %u, value %u" virtio_serial_throttle_port(unsigned int port, bool throttle) "port %u, throttle %d" virtio_serial_handle_control_message(uint16_t event, uint16_t value) "event %u, value %u" virtio_serial_handle_control_message_port(unsigned int port) "port %u" -# hw/char/virtio-console.c +# virtio-console.c virtio_console_flush_buf(unsigned int port, size_t len, ssize_t ret) "port %u, in_len %zu, out_len %zd" virtio_console_chr_read(unsigned int port, int size) "port %u, size %d" virtio_console_chr_event(unsigned int port, int event) "port %u, event %d" -# hw/char/grlib_apbuart.c +# 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 -# hw/char/lm32_juart.c +# lm32_juart.c lm32_juart_get_jtx(uint32_t value) "jtx 0x%08x" lm32_juart_set_jtx(uint32_t value) "jtx 0x%08x" lm32_juart_get_jrx(uint32_t value) "jrx 0x%08x" lm32_juart_set_jrx(uint32_t value) "jrx 0x%08x" -# hw/char/lm32_uart.c +# lm32_uart.c 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/char/milkymist-uart.c +# milkymist-uart.c milkymist_uart_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_uart_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_uart_raise_irq(void) "Raise IRQ" milkymist_uart_lower_irq(void) "Lower IRQ" -# hw/char/escc.c +# escc.c escc_put_queue(char channel, int b) "channel %c put: 0x%02x" escc_get_queue(char channel, int val) "channel %c get 0x%02x" escc_update_irq(int irq) "IRQ = %d" @@ -56,7 +56,7 @@ escc_sunkbd_event_out(int ch) "Translated keycode 0x%2.2x" escc_kbd_command(int val) "Command %d" escc_sunmouse_event(int dx, int dy, int buttons_state) "dx=%d dy=%d buttons=0x%01x" -# hw/char/pl011.c +# pl011.c pl011_irq_state(int level) "irq state %d" pl011_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" pl011_read_fifo(int read_count) "FIFO read, read_count now %d" @@ -65,7 +65,7 @@ pl011_can_receive(uint32_t lcr, int read_count, int r) "LCR 0x%08x read_count %d pl011_put_fifo(uint32_t c, int read_count) "new char 0x%x read_count now %d" pl011_put_fifo_full(void) "FIFO now full, RXFF set" -# hw/char/cmsdk_apb_uart.c +# cmsdk-apb-uart.c cmsdk_apb_uart_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_uart_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB UART write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_uart_reset(void) "CMSDK APB UART: reset" @@ -73,3 +73,7 @@ cmsdk_apb_uart_receive(uint8_t c) "CMSDK APB UART: got character 0x%x from backe cmsdk_apb_uart_tx_pending(void) "CMSDK APB UART: character send to backend pending" cmsdk_apb_uart_tx(uint8_t c) "CMSDK APB UART: character 0x%x sent to backend" cmsdk_apb_uart_set_params(int speed) "CMSDK APB UART: params set to %d 8N1" + +# nrf51_uart.c +nrf51_uart_read(uint64_t addr, uint64_t r, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" +nrf51_uart_write(uint64_t addr, uint64_t value, unsigned int size) "addr 0x%" PRIx64 " value 0x%" PRIx64 " size %u" diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index 679a824888..2cbe1d4ed5 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -114,8 +114,7 @@ static void set_guest_connected(VirtIOSerialPort *port, int guest_connected) } if (dev->id) { - qapi_event_send_vserport_change(dev->id, guest_connected, - &error_abort); + qapi_event_send_vserport_change(dev->id, guest_connected); } } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index d2dd8ab502..bdd917bbb8 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -667,9 +667,9 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) /* The config space (ignored on the far end in current versions) */ get_config(vdev, (uint8_t *)&config); - qemu_put_be16s(f, &config.cols); - qemu_put_be16s(f, &config.rows); - qemu_put_be32s(f, &config.max_nr_ports); + qemu_put_be16(f, config.cols); + qemu_put_be16(f, config.rows); + qemu_put_be32(f, config.max_nr_ports); /* The ports map */ max_nr_ports = s->serial.max_virtserial_ports; @@ -696,7 +696,7 @@ static void virtio_serial_save_device(VirtIODevice *vdev, QEMUFile *f) qemu_put_byte(f, port->guest_connected); qemu_put_byte(f, port->host_connected); - elem_popped = 0; + elem_popped = 0; if (port->elem) { elem_popped = 1; } @@ -1052,7 +1052,7 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) /* Spawn a new virtio-serial bus on which the ports will ride as devices */ qbus_create_inplace(&vser->bus, sizeof(vser->bus), TYPE_VIRTIO_SERIAL_BUS, dev, vdev->bus_name); - qbus_set_hotplug_handler(BUS(&vser->bus), DEVICE(vser), errp); + qbus_set_hotplug_handler(BUS(&vser->bus), OBJECT(vser), errp); vser->bus.vser = vser; QTAILQ_INIT(&vser->ports); diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index 8b4b4bf523..91f34ef06c 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "hw/hw.h" #include "chardev/char-fe.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include @@ -39,7 +39,7 @@ struct buffer { }; struct XenConsole { - struct XenDevice xendev; /* must be first */ + struct XenLegacyDevice xendev; /* must be first */ struct buffer buffer; char console[XEN_BUFSIZE]; int ring_ref; @@ -60,34 +60,34 @@ static void buffer_append(struct XenConsole *con) size = prod - cons; if ((size == 0) || (size > sizeof(intf->out))) - return; + return; if ((buffer->capacity - buffer->size) < size) { - buffer->capacity += (size + 1024); - buffer->data = g_realloc(buffer->data, buffer->capacity); + buffer->capacity += (size + 1024); + buffer->data = g_realloc(buffer->data, buffer->capacity); } while (cons != prod) - buffer->data[buffer->size++] = intf->out[ - MASK_XENCONS_IDX(cons++, intf->out)]; + buffer->data[buffer->size++] = intf->out[ + MASK_XENCONS_IDX(cons++, intf->out)]; xen_mb(); intf->out_cons = cons; xen_pv_send_notify(&con->xendev); if (buffer->max_capacity && - buffer->size > buffer->max_capacity) { - /* Discard the middle of the data. */ + buffer->size > buffer->max_capacity) { + /* Discard the middle of the data. */ - size_t over = buffer->size - buffer->max_capacity; - uint8_t *maxpos = buffer->data + buffer->max_capacity; + size_t over = buffer->size - buffer->max_capacity; + uint8_t *maxpos = buffer->data + buffer->max_capacity; - memmove(maxpos - over, maxpos, over); - buffer->data = g_realloc(buffer->data, buffer->max_capacity); - buffer->size = buffer->capacity = buffer->max_capacity; + memmove(maxpos - over, maxpos, over); + buffer->data = g_realloc(buffer->data, buffer->max_capacity); + buffer->size = buffer->capacity = buffer->max_capacity; - if (buffer->consumed > buffer->max_capacity - over) - buffer->consumed = buffer->max_capacity - over; + if (buffer->consumed > buffer->max_capacity - over) + buffer->consumed = buffer->max_capacity - over; } } @@ -95,8 +95,8 @@ static void buffer_advance(struct buffer *buffer, size_t len) { buffer->consumed += len; if (buffer->consumed == buffer->size) { - buffer->consumed = 0; - buffer->size = 0; + buffer->consumed = 0; + buffer->size = 0; } } @@ -111,7 +111,7 @@ static int ring_free_bytes(struct XenConsole *con) space = prod - cons; if (space > sizeof(intf->in)) - return 0; /* ring is screwed: ignore it */ + return 0; /* ring is screwed: ignore it */ return (sizeof(intf->in) - space); } @@ -132,12 +132,12 @@ static void xencons_receive(void *opaque, const uint8_t *buf, int len) max = ring_free_bytes(con); /* The can_receive() func limits this, but check again anyway */ if (max < len) - len = max; + len = max; prod = intf->in_prod; for (i = 0; i < len; i++) { - intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = - buf[i]; + intf->in[MASK_XENCONS_IDX(prod++, intf->in)] = + buf[i]; } xen_wmb(); intf->in_prod = prod; @@ -173,7 +173,7 @@ static void xencons_send(struct XenConsole *con) /* -------------------------------------------------------------------- */ -static int con_init(struct XenDevice *xendev) +static int con_init(struct XenLegacyDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); char *type, *dom, label[32]; @@ -207,7 +207,12 @@ static int con_init(struct XenDevice *xendev) } else { snprintf(label, sizeof(label), "xencons%d", con->xendev.dev); qemu_chr_fe_init(&con->chr, - qemu_chr_new(label, output), &error_abort); + /* + * FIXME: sure we want to support implicit + * muxed monitors here? + */ + qemu_chr_new_mux_mon(label, output, NULL), + &error_abort); } xenstore_store_pv_console_info(con->xendev.dev, @@ -218,17 +223,17 @@ out: return ret; } -static int con_initialise(struct XenDevice *xendev) +static int con_initialise(struct XenLegacyDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); int limit; if (xenstore_read_int(con->console, "ring-ref", &con->ring_ref) == -1) - return -1; + return -1; if (xenstore_read_int(con->console, "port", &con->xendev.remote_port) == -1) - return -1; + return -1; if (xenstore_read_int(con->console, "limit", &limit) == 0) - con->buffer.max_capacity = limit; + con->buffer.max_capacity = limit; if (!xendev->dev) { xen_pfn_t mfn = con->ring_ref; @@ -240,7 +245,7 @@ static int con_initialise(struct XenDevice *xendev) PROT_READ | PROT_WRITE); } if (!con->sring) - return -1; + return -1; xen_be_bind_evtchn(&con->xendev); qemu_chr_fe_set_handlers(&con->chr, xencons_can_receive, @@ -248,14 +253,14 @@ static int con_initialise(struct XenDevice *xendev) xen_pv_printf(xendev, 1, "ring mfn %d, remote port %d, local port %d, limit %zd\n", - con->ring_ref, - con->xendev.remote_port, - con->xendev.local_port, - con->buffer.max_capacity); + con->ring_ref, + con->xendev.remote_port, + con->xendev.local_port, + con->buffer.max_capacity); return 0; } -static void con_disconnect(struct XenDevice *xendev) +static void con_disconnect(struct XenLegacyDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); @@ -272,13 +277,13 @@ static void con_disconnect(struct XenDevice *xendev) } } -static void con_event(struct XenDevice *xendev) +static void con_event(struct XenLegacyDevice *xendev) { struct XenConsole *con = container_of(xendev, struct XenConsole, xendev); buffer_append(con); if (con->buffer.size - con->buffer.consumed) - xencons_send(con); + xencons_send(con); } /* -------------------------------------------------------------------- */ diff --git a/hw/core/Kconfig b/hw/core/Kconfig new file mode 100644 index 0000000000..c2a1ae8122 --- /dev/null +++ b/hw/core/Kconfig @@ -0,0 +1,11 @@ +config EMPTY_SLOT + bool + +config PTIMER + bool + +config FITLOADER + bool + +config PLATFORM_BUS + bool diff --git a/hw/core/Makefile.objs b/hw/core/Makefile.objs index eb88ca979e..a799c83815 100644 --- a/hw/core/Makefile.objs +++ b/hw/core/Makefile.objs @@ -20,6 +20,5 @@ common-obj-$(CONFIG_SOFTMMU) += register.o common-obj-$(CONFIG_SOFTMMU) += or-irq.o common-obj-$(CONFIG_SOFTMMU) += split-irq.o common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o - -obj-$(CONFIG_SOFTMMU) += generic-loader.o -obj-$(CONFIG_SOFTMMU) += null-machine.o +common-obj-$(CONFIG_SOFTMMU) += generic-loader.o +common-obj-$(CONFIG_SOFTMMU) += null-machine.o diff --git a/hw/core/bus.c b/hw/core/bus.c index 4651f24486..e09843f6ab 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -22,22 +22,15 @@ #include "hw/qdev.h" #include "qapi/error.h" -static void qbus_set_hotplug_handler_internal(BusState *bus, Object *handler, - Error **errp) +void qbus_set_hotplug_handler(BusState *bus, Object *handler, Error **errp) { - object_property_set_link(OBJECT(bus), OBJECT(handler), QDEV_HOTPLUG_HANDLER_PROPERTY, errp); } -void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, Error **errp) -{ - qbus_set_hotplug_handler_internal(bus, OBJECT(handler), errp); -} - void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp) { - qbus_set_hotplug_handler_internal(bus, OBJECT(bus), errp); + qbus_set_hotplug_handler(bus, OBJECT(bus), errp); } int qbus_walk_children(BusState *bus, diff --git a/hw/core/empty_slot.c b/hw/core/empty_slot.c index c1b9c2b104..239f78e2a7 100644 --- a/hw/core/empty_slot.c +++ b/hw/core/empty_slot.c @@ -71,21 +71,20 @@ void empty_slot_init(hwaddr addr, uint64_t slot_size) } } -static int empty_slot_init1(SysBusDevice *dev) +static void empty_slot_realize(DeviceState *dev, Error **errp) { EmptySlot *s = EMPTY_SLOT(dev); memory_region_init_io(&s->iomem, OBJECT(s), &empty_slot_ops, s, "empty-slot", s->size); - sysbus_init_mmio(dev, &s->iomem); - return 0; + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } static void empty_slot_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - k->init = empty_slot_init1; + dc->realize = empty_slot_realize; } static const TypeInfo empty_slot_info = { diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index cb0e68486d..3695dd439c 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -130,23 +130,23 @@ static void generic_loader_realize(DeviceState *dev, Error **errp) s->cpu = first_cpu; } -#ifdef TARGET_WORDS_BIGENDIAN - big_endian = 1; -#else - big_endian = 0; -#endif + big_endian = target_words_bigendian(); if (s->file) { AddressSpace *as = s->cpu ? s->cpu->as : NULL; if (!s->force_raw) { - size = load_elf_as(s->file, NULL, NULL, &entry, NULL, NULL, + size = load_elf_as(s->file, NULL, NULL, NULL, &entry, NULL, NULL, big_endian, 0, 0, 0, as); if (size < 0) { size = load_uimage_as(s->file, &entry, NULL, NULL, NULL, NULL, as); } + + if (size < 0) { + size = load_targphys_hex_as(s->file, &entry, as); + } } if (size < 0 || s->force_raw) { @@ -200,6 +200,7 @@ static void generic_loader_class_init(ObjectClass *klass, void *data) dc->unrealize = generic_loader_unrealize; dc->props = generic_loader_props; dc->desc = "Generic Loader"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static TypeInfo generic_loader_info = { diff --git a/hw/core/hotplug.c b/hw/core/hotplug.c index 2253072d0e..17ac986685 100644 --- a/hw/core/hotplug.c +++ b/hw/core/hotplug.c @@ -35,16 +35,6 @@ void hotplug_handler_plug(HotplugHandler *plug_handler, } } -void hotplug_handler_post_plug(HotplugHandler *plug_handler, - DeviceState *plugged_dev) -{ - HotplugHandlerClass *hdc = HOTPLUG_HANDLER_GET_CLASS(plug_handler); - - if (hdc->post_plug) { - hdc->post_plug(plug_handler, plugged_dev); - } -} - void hotplug_handler_unplug_request(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) diff --git a/hw/core/loader.c b/hw/core/loader.c index bbb6e65bb5..fe5cb24122 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -61,9 +61,10 @@ static int roms_loaded; /* return the size or -1 if error */ -int get_image_size(const char *filename) +int64_t get_image_size(const char *filename) { - int fd, size; + int fd; + int64_t size; fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) return -1; @@ -72,50 +73,24 @@ int get_image_size(const char *filename) return size; } -/* return the size or -1 if error */ -/* deprecated, because caller does not specify buffer size! */ -int load_image(const char *filename, uint8_t *addr) -{ - int fd, size; - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return -1; - size = lseek(fd, 0, SEEK_END); - if (size == -1) { - fprintf(stderr, "file %-20s: get size error: %s\n", - filename, strerror(errno)); - close(fd); - return -1; - } - - lseek(fd, 0, SEEK_SET); - if (read(fd, addr, size) != size) { - close(fd); - return -1; - } - close(fd); - return size; -} - /* return the size or -1 if error */ ssize_t load_image_size(const char *filename, void *addr, size_t size) { int fd; - ssize_t actsize; + ssize_t actsize, l = 0; fd = open(filename, O_RDONLY | O_BINARY); if (fd < 0) { return -1; } - actsize = read(fd, addr, size); - if (actsize < 0) { - close(fd); - return -1; + while ((actsize = read(fd, addr + l, size - l)) > 0) { + l += actsize; } + close(fd); - return actsize; + return actsize < 0 ? -1 : l; } /* read()-like version */ @@ -268,26 +243,26 @@ int load_aout(const char *filename, hwaddr addr, int max_sz, case OMAGIC: if (e.a_text + e.a_data > max_sz) goto fail; - lseek(fd, N_TXTOFF(e), SEEK_SET); - size = read_targphys(filename, fd, addr, e.a_text + e.a_data); - if (size < 0) - goto fail; - break; + lseek(fd, N_TXTOFF(e), SEEK_SET); + size = read_targphys(filename, fd, addr, e.a_text + e.a_data); + if (size < 0) + goto fail; + break; case NMAGIC: if (N_DATADDR(e, target_page_size) + e.a_data > max_sz) goto fail; - lseek(fd, N_TXTOFF(e), SEEK_SET); - size = read_targphys(filename, fd, addr, e.a_text); - if (size < 0) - goto fail; + lseek(fd, N_TXTOFF(e), SEEK_SET); + size = read_targphys(filename, fd, addr, e.a_text); + if (size < 0) + goto fail; ret = read_targphys(filename, fd, addr + N_DATADDR(e, target_page_size), e.a_data); - if (ret < 0) - goto fail; - size += ret; - break; + if (ret < 0) + goto fail; + size += ret; + break; default: - goto fail; + goto fail; } close(fd); return size; @@ -420,37 +395,42 @@ fail: } /* 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), +int load_elf(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + 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 data_swab) { - return load_elf_as(filename, translate_fn, translate_opaque, pentry, - lowaddr, highaddr, big_endian, elf_machine, clear_lsb, - data_swab, NULL); + return load_elf_as(filename, elf_note_fn, translate_fn, translate_opaque, + pentry, lowaddr, highaddr, big_endian, elf_machine, + clear_lsb, data_swab, NULL); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf_as(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), 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 data_swab, AddressSpace *as) { - return load_elf_ram(filename, translate_fn, translate_opaque, + return load_elf_ram(filename, elf_note_fn, translate_fn, translate_opaque, pentry, lowaddr, highaddr, big_endian, elf_machine, clear_lsb, data_swab, as, true); } /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf_ram(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), 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 data_swab, AddressSpace *as, bool load_rom) { - return load_elf_ram_sym(filename, translate_fn, translate_opaque, + return load_elf_ram_sym(filename, elf_note_fn, + translate_fn, translate_opaque, pentry, lowaddr, highaddr, big_endian, elf_machine, clear_lsb, data_swab, as, load_rom, NULL); @@ -458,6 +438,7 @@ int load_elf_ram(const char *filename, /* return < 0 if error, otherwise the number of bytes loaded in memory */ int load_elf_ram_sym(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, @@ -500,11 +481,13 @@ int load_elf_ram_sym(const char *filename, lseek(fd, 0, SEEK_SET); if (e_ident[EI_CLASS] == ELFCLASS64) { - ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab, + ret = load_elf64(filename, fd, elf_note_fn, + translate_fn, translate_opaque, must_swab, pentry, lowaddr, highaddr, elf_machine, clear_lsb, data_swab, as, load_rom, sym_cb); } else { - ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab, + ret = load_elf32(filename, fd, elf_note_fn, + translate_fn, translate_opaque, must_swab, pentry, lowaddr, highaddr, elf_machine, clear_lsb, data_swab, as, load_rom, sym_cb); } @@ -637,13 +620,26 @@ static int load_uboot_image(const char *filename, hwaddr *ep, hwaddr *loadaddr, goto out; if (hdr->ih_type != image_type) { - fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, - image_type); - goto out; + if (!(image_type == IH_TYPE_KERNEL && + hdr->ih_type == IH_TYPE_KERNEL_NOLOAD)) { + fprintf(stderr, "Wrong image type %d, expected %d\n", hdr->ih_type, + image_type); + goto out; + } } /* TODO: Implement other image types. */ switch (hdr->ih_type) { + case IH_TYPE_KERNEL_NOLOAD: + if (!loadaddr || *loadaddr == LOAD_UIMAGE_LOADADDR_INVALID) { + fprintf(stderr, "this image format (kernel_noload) cannot be " + "loaded on this machine type"); + goto out; + } + + hdr->ih_load = *loadaddr + sizeof(*hdr); + hdr->ih_ep += hdr->ih_load; + /* fall through */ case IH_TYPE_KERNEL: address = hdr->ih_load; if (translate_fn) { @@ -840,6 +836,8 @@ struct Rom { char *fw_dir; char *fw_file; + bool committed; + hwaddr addr; QTAILQ_ENTRY(Rom) next; }; @@ -847,6 +845,17 @@ struct Rom { static FWCfgState *fw_cfg; static QTAILQ_HEAD(, Rom) roms = QTAILQ_HEAD_INITIALIZER(roms); +/* rom->data must be heap-allocated (do not use with rom_add_elf_program()) */ +static void rom_free(Rom *rom) +{ + g_free(rom->data); + g_free(rom->path); + g_free(rom->name); + g_free(rom->fw_dir); + g_free(rom->fw_file); + g_free(rom); +} + static inline bool rom_order_compare(Rom *rom, Rom *item) { return ((uintptr_t)(void *)rom->as > (uintptr_t)(void *)item->as) || @@ -866,6 +875,8 @@ static void rom_insert(Rom *rom) rom->as = &address_space_memory; } + rom->committed = false; + /* List is ordered by load address in the same address space */ QTAILQ_FOREACH(item, &roms, next) { if (rom_order_compare(rom, item)) { @@ -995,15 +1006,7 @@ err: if (fd != -1) close(fd); - g_free(rom->data); - g_free(rom->path); - g_free(rom->name); - if (fw_dir) { - g_free(rom->fw_dir); - g_free(rom->fw_file); - } - g_free(rom); - + rom_free(rom); return -1; } @@ -1095,8 +1098,8 @@ static void rom_reset(void *unused) void *host = memory_region_get_ram_ptr(rom->mr); memcpy(host, rom->data, rom->datasize); } else { - cpu_physical_memory_write_rom(rom->as, rom->addr, rom->data, - rom->datasize); + address_space_write_rom(rom->as, rom->addr, MEMTXATTRS_UNSPECIFIED, + rom->data, rom->datasize); } if (rom->isrom) { /* rom needs to be written only once */ @@ -1165,6 +1168,34 @@ void rom_reset_order_override(void) fw_cfg_reset_order_override(fw_cfg); } +void rom_transaction_begin(void) +{ + Rom *rom; + + /* Ignore ROMs added without the transaction API */ + QTAILQ_FOREACH(rom, &roms, next) { + rom->committed = true; + } +} + +void rom_transaction_end(bool commit) +{ + Rom *rom; + Rom *tmp; + + QTAILQ_FOREACH_SAFE(rom, &roms, next, tmp) { + if (rom->committed) { + continue; + } + if (commit) { + rom->committed = true; + } else { + QTAILQ_REMOVE(&roms, rom, next); + rom_free(rom); + } + } +} + static Rom *find_rom(hwaddr addr, size_t size) { Rom *rom; @@ -1286,3 +1317,252 @@ void hmp_info_roms(Monitor *mon, const QDict *qdict) } } } + +typedef enum HexRecord HexRecord; +enum HexRecord { + DATA_RECORD = 0, + EOF_RECORD, + EXT_SEG_ADDR_RECORD, + START_SEG_ADDR_RECORD, + EXT_LINEAR_ADDR_RECORD, + START_LINEAR_ADDR_RECORD, +}; + +/* Each record contains a 16-bit address which is combined with the upper 16 + * bits of the implicit "next address" to form a 32-bit address. + */ +#define NEXT_ADDR_MASK 0xffff0000 + +#define DATA_FIELD_MAX_LEN 0xff +#define LEN_EXCEPT_DATA 0x5 +/* 0x5 = sizeof(byte_count) + sizeof(address) + sizeof(record_type) + + * sizeof(checksum) */ +typedef struct { + uint8_t byte_count; + uint16_t address; + uint8_t record_type; + uint8_t data[DATA_FIELD_MAX_LEN]; + uint8_t checksum; +} HexLine; + +/* return 0 or -1 if error */ +static bool parse_record(HexLine *line, uint8_t *our_checksum, const uint8_t c, + uint32_t *index, const bool in_process) +{ + /* +-------+---------------+-------+---------------------+--------+ + * | byte | |record | | | + * | count | address | type | data |checksum| + * +-------+---------------+-------+---------------------+--------+ + * ^ ^ ^ ^ ^ ^ + * |1 byte | 2 bytes |1 byte | 0-255 bytes | 1 byte | + */ + uint8_t value = 0; + uint32_t idx = *index; + /* ignore space */ + if (g_ascii_isspace(c)) { + return true; + } + if (!g_ascii_isxdigit(c) || !in_process) { + return false; + } + value = g_ascii_xdigit_value(c); + value = (idx & 0x1) ? (value & 0xf) : (value << 4); + if (idx < 2) { + line->byte_count |= value; + } else if (2 <= idx && idx < 6) { + line->address <<= 4; + line->address += g_ascii_xdigit_value(c); + } else if (6 <= idx && idx < 8) { + line->record_type |= value; + } else if (8 <= idx && idx < 8 + 2 * line->byte_count) { + line->data[(idx - 8) >> 1] |= value; + } else if (8 + 2 * line->byte_count <= idx && + idx < 10 + 2 * line->byte_count) { + line->checksum |= value; + } else { + return false; + } + *our_checksum += value; + ++(*index); + return true; +} + +typedef struct { + const char *filename; + HexLine line; + uint8_t *bin_buf; + hwaddr *start_addr; + int total_size; + uint32_t next_address_to_write; + uint32_t current_address; + uint32_t current_rom_index; + uint32_t rom_start_address; + AddressSpace *as; +} HexParser; + +/* return size or -1 if error */ +static int handle_record_type(HexParser *parser) +{ + HexLine *line = &(parser->line); + switch (line->record_type) { + case DATA_RECORD: + parser->current_address = + (parser->next_address_to_write & NEXT_ADDR_MASK) | line->address; + /* verify this is a contiguous block of memory */ + if (parser->current_address != parser->next_address_to_write) { + if (parser->current_rom_index != 0) { + rom_add_blob_fixed_as(parser->filename, parser->bin_buf, + parser->current_rom_index, + parser->rom_start_address, parser->as); + } + parser->rom_start_address = parser->current_address; + parser->current_rom_index = 0; + } + + /* copy from line buffer to output bin_buf */ + memcpy(parser->bin_buf + parser->current_rom_index, line->data, + line->byte_count); + parser->current_rom_index += line->byte_count; + parser->total_size += line->byte_count; + /* save next address to write */ + parser->next_address_to_write = + parser->current_address + line->byte_count; + break; + + case EOF_RECORD: + if (parser->current_rom_index != 0) { + rom_add_blob_fixed_as(parser->filename, parser->bin_buf, + parser->current_rom_index, + parser->rom_start_address, parser->as); + } + return parser->total_size; + case EXT_SEG_ADDR_RECORD: + case EXT_LINEAR_ADDR_RECORD: + if (line->byte_count != 2 && line->address != 0) { + return -1; + } + + if (parser->current_rom_index != 0) { + rom_add_blob_fixed_as(parser->filename, parser->bin_buf, + parser->current_rom_index, + parser->rom_start_address, parser->as); + } + + /* save next address to write, + * in case of non-contiguous block of memory */ + parser->next_address_to_write = (line->data[0] << 12) | + (line->data[1] << 4); + if (line->record_type == EXT_LINEAR_ADDR_RECORD) { + parser->next_address_to_write <<= 12; + } + + parser->rom_start_address = parser->next_address_to_write; + parser->current_rom_index = 0; + break; + + case START_SEG_ADDR_RECORD: + if (line->byte_count != 4 && line->address != 0) { + return -1; + } + + /* x86 16-bit CS:IP segmented addressing */ + *(parser->start_addr) = (((line->data[0] << 8) | line->data[1]) << 4) + + ((line->data[2] << 8) | line->data[3]); + break; + + case START_LINEAR_ADDR_RECORD: + if (line->byte_count != 4 && line->address != 0) { + return -1; + } + + *(parser->start_addr) = ldl_be_p(line->data); + break; + + default: + return -1; + } + + return parser->total_size; +} + +/* return size or -1 if error */ +static int parse_hex_blob(const char *filename, hwaddr *addr, uint8_t *hex_blob, + size_t hex_blob_size, AddressSpace *as) +{ + bool in_process = false; /* avoid re-enter and + * check whether record begin with ':' */ + uint8_t *end = hex_blob + hex_blob_size; + uint8_t our_checksum = 0; + uint32_t record_index = 0; + HexParser parser = { + .filename = filename, + .bin_buf = g_malloc(hex_blob_size), + .start_addr = addr, + .as = as, + }; + + rom_transaction_begin(); + + for (; hex_blob < end; ++hex_blob) { + switch (*hex_blob) { + case '\r': + case '\n': + if (!in_process) { + break; + } + + in_process = false; + if ((LEN_EXCEPT_DATA + parser.line.byte_count) * 2 != + record_index || + our_checksum != 0) { + parser.total_size = -1; + goto out; + } + + if (handle_record_type(&parser) == -1) { + parser.total_size = -1; + goto out; + } + break; + + /* start of a new record. */ + case ':': + memset(&parser.line, 0, sizeof(HexLine)); + in_process = true; + record_index = 0; + break; + + /* decoding lines */ + default: + if (!parse_record(&parser.line, &our_checksum, *hex_blob, + &record_index, in_process)) { + parser.total_size = -1; + goto out; + } + break; + } + } + +out: + g_free(parser.bin_buf); + rom_transaction_end(parser.total_size != -1); + return parser.total_size; +} + +/* return size or -1 if error */ +int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as) +{ + gsize hex_blob_size; + gchar *hex_blob; + int total_size = 0; + + if (!g_file_get_contents(filename, &hex_blob, &hex_blob_size, NULL)) { + return -1; + } + + total_size = parse_hex_blob(filename, entry, (uint8_t *)hex_blob, + hex_blob_size, as); + + g_free(hex_blob); + return total_size; +} diff --git a/hw/core/machine.c b/hw/core/machine.c index a9aeb22f03..743fef2898 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -21,6 +21,135 @@ #include "sysemu/numa.h" #include "qemu/error-report.h" #include "sysemu/qtest.h" +#include "hw/pci/pci.h" +#include "hw/mem/nvdimm.h" + +GlobalProperty hw_compat_3_1[] = { + { "pcie-root-port", "x-speed", "2_5" }, + { "pcie-root-port", "x-width", "1" }, + { "memory-backend-file", "x-use-canonical-path-for-ramblock-id", "true" }, + { "memory-backend-memfd", "x-use-canonical-path-for-ramblock-id", "true" }, + { "tpm-crb", "ppi", "false" }, + { "tpm-tis", "ppi", "false" }, + { "usb-kbd", "serial", "42" }, + { "usb-mouse", "serial", "42" }, + { "usb-kbd", "serial", "42" }, + { "virtio-blk-device", "discard", "false" }, + { "virtio-blk-device", "write-zeroes", "false" }, +}; +const size_t hw_compat_3_1_len = G_N_ELEMENTS(hw_compat_3_1); + +GlobalProperty hw_compat_3_0[] = {}; +const size_t hw_compat_3_0_len = G_N_ELEMENTS(hw_compat_3_0); + +GlobalProperty hw_compat_2_12[] = { + { "migration", "decompress-error-check", "off" }, + { "hda-audio", "use-timer", "false" }, + { "cirrus-vga", "global-vmstate", "true" }, + { "VGA", "global-vmstate", "true" }, + { "vmware-svga", "global-vmstate", "true" }, + { "qxl-vga", "global-vmstate", "true" }, +}; +const size_t hw_compat_2_12_len = G_N_ELEMENTS(hw_compat_2_12); + +GlobalProperty hw_compat_2_11[] = { + { "hpet", "hpet-offset-saved", "false" }, + { "virtio-blk-pci", "vectors", "2" }, + { "vhost-user-blk-pci", "vectors", "2" }, + { "e1000", "migrate_tso_props", "off" }, +}; +const size_t hw_compat_2_11_len = G_N_ELEMENTS(hw_compat_2_11); + +GlobalProperty hw_compat_2_10[] = { + { "virtio-mouse-device", "wheel-axis", "false" }, + { "virtio-tablet-device", "wheel-axis", "false" }, +}; +const size_t hw_compat_2_10_len = G_N_ELEMENTS(hw_compat_2_10); + +GlobalProperty hw_compat_2_9[] = { + { "pci-bridge", "shpc", "off" }, + { "intel-iommu", "pt", "off" }, + { "virtio-net-device", "x-mtu-bypass-backend", "off" }, + { "pcie-root-port", "x-migrate-msix", "false" }, +}; +const size_t hw_compat_2_9_len = G_N_ELEMENTS(hw_compat_2_9); + +GlobalProperty hw_compat_2_8[] = { + { "fw_cfg_mem", "x-file-slots", "0x10" }, + { "fw_cfg_io", "x-file-slots", "0x10" }, + { "pflash_cfi01", "old-multiple-chip-handling", "on" }, + { "pci-bridge", "shpc", "on" }, + { TYPE_PCI_DEVICE, "x-pcie-extcap-init", "off" }, + { "virtio-pci", "x-pcie-deverr-init", "off" }, + { "virtio-pci", "x-pcie-lnkctl-init", "off" }, + { "virtio-pci", "x-pcie-pm-init", "off" }, + { "cirrus-vga", "vgamem_mb", "8" }, + { "isa-cirrus-vga", "vgamem_mb", "8" }, +}; +const size_t hw_compat_2_8_len = G_N_ELEMENTS(hw_compat_2_8); + +GlobalProperty hw_compat_2_7[] = { + { "virtio-pci", "page-per-vq", "on" }, + { "virtio-serial-device", "emergency-write", "off" }, + { "ioapic", "version", "0x11" }, + { "intel-iommu", "x-buggy-eim", "true" }, + { "virtio-pci", "x-ignore-backend-features", "on" }, +}; +const size_t hw_compat_2_7_len = G_N_ELEMENTS(hw_compat_2_7); + +GlobalProperty hw_compat_2_6[] = { + { "virtio-mmio", "format_transport_address", "off" }, + /* Optional because not all virtio-pci devices support legacy mode */ + { "virtio-pci", "disable-modern", "on", .optional = true }, + { "virtio-pci", "disable-legacy", "off", .optional = true }, +}; +const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6); + +GlobalProperty hw_compat_2_5[] = { + { "isa-fdc", "fallback", "144" }, + { "pvscsi", "x-old-pci-configuration", "on" }, + { "pvscsi", "x-disable-pcie", "on" }, + { "vmxnet3", "x-old-msi-offsets", "on" }, + { "vmxnet3", "x-disable-pcie", "on" }, +}; +const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); + +GlobalProperty hw_compat_2_4[] = { + { "virtio-blk-device", "scsi", "true" }, + { "e1000", "extra_mac_registers", "off" }, + { "virtio-pci", "x-disable-pcie", "on" }, + { "virtio-pci", "migrate-extra", "off" }, + { "fw_cfg_mem", "dma_enabled", "off" }, + { "fw_cfg_io", "dma_enabled", "off" } +}; +const size_t hw_compat_2_4_len = G_N_ELEMENTS(hw_compat_2_4); + +GlobalProperty hw_compat_2_3[] = { + { "virtio-blk-pci", "any_layout", "off" }, + { "virtio-balloon-pci", "any_layout", "off" }, + { "virtio-serial-pci", "any_layout", "off" }, + { "virtio-9p-pci", "any_layout", "off" }, + { "virtio-rng-pci", "any_layout", "off" }, + { TYPE_PCI_DEVICE, "x-pcie-lnksta-dllla", "off" }, + { "migration", "send-configuration", "off" }, + { "migration", "send-section-footer", "off" }, + { "migration", "store-global-state", "off" }, +}; +const size_t hw_compat_2_3_len = G_N_ELEMENTS(hw_compat_2_3); + +GlobalProperty hw_compat_2_2[] = {}; +const size_t hw_compat_2_2_len = G_N_ELEMENTS(hw_compat_2_2); + +GlobalProperty hw_compat_2_1[] = { + { "intel-hda", "old_msi_addr", "on" }, + { "VGA", "qemu-extended-regs", "off" }, + { "secondary-vga", "qemu-extended-regs", "off" }, + { "virtio-scsi-pci", "any_layout", "off" }, + { "usb-mouse", "usb_version", "1" }, + { "usb-kbd", "usb_version", "1" }, + { "virtio-pci", "virtio-pci-bus-master-bug-migration", "on" }, +}; +const size_t hw_compat_2_1_len = G_N_ELEMENTS(hw_compat_2_1); static char *machine_get_accel(Object *obj, Error **errp) { @@ -324,6 +453,9 @@ static void machine_set_enforce_config_section(Object *obj, bool value, { MachineState *ms = MACHINE(obj); + warn_report("enforce-config-section is deprecated, please use " + "-global migration.send-configuration=on|off instead"); + ms->enforce_config_section = value; } @@ -350,6 +482,47 @@ static void machine_set_memory_encryption(Object *obj, const char *value, ms->memory_encryption = g_strdup(value); } +static bool machine_get_nvdimm(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->nvdimms_state->is_enabled; +} + +static void machine_set_nvdimm(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->nvdimms_state->is_enabled = value; +} + +static char *machine_get_nvdimm_persistence(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return g_strdup(ms->nvdimms_state->persistence_string); +} + +static void machine_set_nvdimm_persistence(Object *obj, const char *value, + Error **errp) +{ + MachineState *ms = MACHINE(obj); + NVDIMMState *nvdimms_state = ms->nvdimms_state; + + if (strcmp(value, "cpu") == 0) { + nvdimms_state->persistence = 3; + } else if (strcmp(value, "mem-ctrl") == 0) { + nvdimms_state->persistence = 2; + } else { + error_setg(errp, "-machine nvdimm-persistence=%s: unsupported option", + value); + return; + } + + g_free(nvdimms_state->persistence_string); + nvdimms_state->persistence_string = g_strdup(value); +} + void machine_class_allow_dynamic_sysbus_dev(MachineClass *mc, const char *type) { strList *item = g_new0(strList, 1); @@ -588,7 +761,7 @@ static void machine_class_init(ObjectClass *oc, void *data) object_class_property_add_bool(oc, "dump-guest-core", machine_get_dump_guest_core, machine_set_dump_guest_core, &error_abort); object_class_property_set_description(oc, "dump-guest-core", - "Include guest memory in a core dump", &error_abort); + "Include guest memory in a core dump", &error_abort); object_class_property_add_bool(oc, "mem-merge", machine_get_mem_merge, machine_set_mem_merge, &error_abort); @@ -633,7 +806,7 @@ static void machine_class_init(ObjectClass *oc, void *data) machine_get_memory_encryption, machine_set_memory_encryption, &error_abort); object_class_property_set_description(oc, "memory-encryption", - "Set memory encyption object to use", &error_abort); + "Set memory encryption object to use", &error_abort); } static void machine_class_base_init(ObjectClass *oc, void *data) @@ -644,19 +817,44 @@ static void machine_class_base_init(ObjectClass *oc, void *data) assert(g_str_has_suffix(cname, TYPE_MACHINE_SUFFIX)); mc->name = g_strndup(cname, strlen(cname) - strlen(TYPE_MACHINE_SUFFIX)); + mc->compat_props = g_ptr_array_new(); } } static void machine_initfn(Object *obj) { MachineState *ms = MACHINE(obj); + MachineClass *mc = MACHINE_GET_CLASS(obj); ms->kernel_irqchip_allowed = true; + ms->kernel_irqchip_split = mc->default_kernel_irqchip_split; ms->kvm_shadow_mem = -1; ms->dump_guest_core = true; ms->mem_merge = true; ms->enable_graphics = true; + if (mc->nvdimm_supported) { + Object *obj = OBJECT(ms); + + ms->nvdimms_state = g_new0(NVDIMMState, 1); + object_property_add_bool(obj, "nvdimm", + machine_get_nvdimm, machine_set_nvdimm, + &error_abort); + object_property_set_description(obj, "nvdimm", + "Set on/off to enable/disable " + "NVDIMM instantiation", NULL); + + object_property_add_str(obj, "nvdimm-persistence", + machine_get_nvdimm_persistence, + machine_set_nvdimm_persistence, + &error_abort); + object_property_set_description(obj, "nvdimm-persistence", + "Set NVDIMM persistence" + "Valid values are cpu, mem-ctrl", + NULL); + } + + /* Register notifier when init is done for sysbus sanity checks */ ms->sysbus_notifier.notify = machine_init_notify; qemu_add_machine_init_done_notifier(&ms->sysbus_notifier); @@ -675,6 +873,7 @@ static void machine_finalize(Object *obj) g_free(ms->dt_compatible); g_free(ms->firmware); g_free(ms->device_memory); + g_free(ms->nvdimms_state); } bool machine_usb(MachineState *machine) @@ -793,8 +992,9 @@ void machine_run_board_init(MachineState *machine) MachineClass *machine_class = MACHINE_GET_CLASS(machine); numa_complete_configuration(machine); - if (nb_numa_nodes) + if (nb_numa_nodes) { machine_numa_finish_cpu_init(machine); + } /* If the machine supports the valid_cpu_types check and the user * specified a CPU with -cpu check here that the user CPU is supported. @@ -830,34 +1030,6 @@ void machine_run_board_init(MachineState *machine) machine_class->init(machine); } -static void machine_class_finalize(ObjectClass *klass, void *data) -{ - MachineClass *mc = MACHINE_CLASS(klass); - - if (mc->compat_props) { - g_array_free(mc->compat_props, true); - } - g_free(mc->name); -} - -void machine_register_compat_props(MachineState *machine) -{ - MachineClass *mc = MACHINE_GET_CLASS(machine); - int i; - GlobalProperty *p; - - if (!mc->compat_props) { - return; - } - - for (i = 0; i < mc->compat_props->len; i++) { - p = g_array_index(mc->compat_props, GlobalProperty *, i); - /* Machine compat_props must never cause errors: */ - p->errp = &error_abort; - qdev_prop_register_global(p); - } -} - static const TypeInfo machine_info = { .name = TYPE_MACHINE, .parent = TYPE_OBJECT, @@ -865,7 +1037,6 @@ static const TypeInfo machine_info = { .class_size = sizeof(MachineClass), .class_init = machine_class_init, .class_base_init = machine_class_base_init, - .class_finalize = machine_class_finalize, .instance_size = sizeof(MachineState), .instance_init = machine_initfn, .instance_finalize = machine_finalize, diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index cde4d3eb57..76d3f8e39c 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -18,7 +18,7 @@ #include "hw/boards.h" #include "sysemu/sysemu.h" #include "exec/address-spaces.h" -#include "cpu.h" +#include "qom/cpu.h" static void machine_none_init(MachineState *mch) { diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 8b22fb51c9..b45a7ef54b 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -288,10 +288,6 @@ static void set_netdev(Object *obj, Visitor *v, const char *name, } for (i = 0; i < queues; i++) { - if (peers[i] == NULL) { - err = -ENOENT; - goto out; - } if (peers[i]->peer) { err = -EEXIST; diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 35072dec1e..5da1439a8b 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -1173,58 +1173,35 @@ void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value) *ptr = value; } -static GList *global_props; +static GPtrArray *global_props(void) +{ + static GPtrArray *gp; + + if (!gp) { + gp = g_ptr_array_new(); + } + + return gp; +} void qdev_prop_register_global(GlobalProperty *prop) { - global_props = g_list_append(global_props, prop); -} - -void register_compat_prop(const char *driver, - const char *property, - const char *value) -{ - GlobalProperty *p = g_new0(GlobalProperty, 1); - - /* Any compat_props must never cause error */ - p->errp = &error_abort; - p->driver = driver; - p->property = property; - p->value = value; - qdev_prop_register_global(p); -} - -void register_compat_props_array(GlobalProperty *prop) -{ - for (; prop && prop->driver; prop++) { - register_compat_prop(prop->driver, prop->property, prop->value); - } -} - -void qdev_prop_register_global_list(GlobalProperty *props) -{ - int i; - - for (i = 0; props[i].driver != NULL; i++) { - qdev_prop_register_global(props+i); - } + g_ptr_array_add(global_props(), prop); } int qdev_prop_check_globals(void) { - GList *l; - int ret = 0; + int i, ret = 0; - for (l = global_props; l; l = l->next) { - GlobalProperty *prop = l->data; + for (i = 0; i < global_props()->len; i++) { + GlobalProperty *prop; ObjectClass *oc; DeviceClass *dc; + + prop = g_ptr_array_index(global_props(), i); if (prop->used) { continue; } - if (!prop->user_provided) { - continue; - } oc = object_class_by_name(prop->driver); oc = object_class_dynamic_cast(oc, TYPE_DEVICE); if (!oc) { @@ -1246,28 +1223,8 @@ int qdev_prop_check_globals(void) void qdev_prop_set_globals(DeviceState *dev) { - GList *l; - - for (l = global_props; l; l = l->next) { - GlobalProperty *prop = l->data; - Error *err = NULL; - - if (object_dynamic_cast(OBJECT(dev), prop->driver) == NULL) { - continue; - } - prop->used = true; - object_property_parse(OBJECT(dev), prop->value, prop->property, &err); - if (err != NULL) { - error_prepend(&err, "can't apply global %s.%s=%s: ", - prop->driver, prop->property, prop->value); - if (!dev->hotplugged && prop->errp) { - error_propagate(prop->errp, err); - } else { - assert(prop->user_provided); - warn_report_err(err); - } - } - } + object_apply_global_props(OBJECT(dev), global_props(), + dev->hotplugged ? NULL : &error_fatal); } /* --- 64bit unsigned int 'size' type --- */ @@ -1327,3 +1284,179 @@ const PropertyInfo qdev_prop_off_auto_pcibar = { .set = set_enum, .set_default_value = set_default_value_enum, }; + +/* --- PCIELinkSpeed 2_5/5/8/16 -- */ + +static void get_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + PCIExpLinkSpeed *p = qdev_get_prop_ptr(dev, prop); + int speed; + + switch (*p) { + case QEMU_PCI_EXP_LNK_2_5GT: + speed = PCIE_LINK_SPEED_2_5; + break; + case QEMU_PCI_EXP_LNK_5GT: + speed = PCIE_LINK_SPEED_5; + break; + case QEMU_PCI_EXP_LNK_8GT: + speed = PCIE_LINK_SPEED_8; + break; + case QEMU_PCI_EXP_LNK_16GT: + speed = PCIE_LINK_SPEED_16; + break; + default: + /* Unreachable */ + abort(); + } + + visit_type_enum(v, prop->name, &speed, prop->info->enum_table, errp); +} + +static void set_prop_pcielinkspeed(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + PCIExpLinkSpeed *p = qdev_get_prop_ptr(dev, prop); + int speed; + Error *local_err = NULL; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_enum(v, prop->name, &speed, prop->info->enum_table, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + switch (speed) { + case PCIE_LINK_SPEED_2_5: + *p = QEMU_PCI_EXP_LNK_2_5GT; + break; + case PCIE_LINK_SPEED_5: + *p = QEMU_PCI_EXP_LNK_5GT; + break; + case PCIE_LINK_SPEED_8: + *p = QEMU_PCI_EXP_LNK_8GT; + break; + case PCIE_LINK_SPEED_16: + *p = QEMU_PCI_EXP_LNK_16GT; + break; + default: + /* Unreachable */ + abort(); + } +} + +const PropertyInfo qdev_prop_pcie_link_speed = { + .name = "PCIELinkSpeed", + .description = "2_5/5/8/16", + .enum_table = &PCIELinkSpeed_lookup, + .get = get_prop_pcielinkspeed, + .set = set_prop_pcielinkspeed, + .set_default_value = set_default_value_enum, +}; + +/* --- PCIELinkWidth 1/2/4/8/12/16/32 -- */ + +static void get_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + PCIExpLinkWidth *p = qdev_get_prop_ptr(dev, prop); + int width; + + switch (*p) { + case QEMU_PCI_EXP_LNK_X1: + width = PCIE_LINK_WIDTH_1; + break; + case QEMU_PCI_EXP_LNK_X2: + width = PCIE_LINK_WIDTH_2; + break; + case QEMU_PCI_EXP_LNK_X4: + width = PCIE_LINK_WIDTH_4; + break; + case QEMU_PCI_EXP_LNK_X8: + width = PCIE_LINK_WIDTH_8; + break; + case QEMU_PCI_EXP_LNK_X12: + width = PCIE_LINK_WIDTH_12; + break; + case QEMU_PCI_EXP_LNK_X16: + width = PCIE_LINK_WIDTH_16; + break; + case QEMU_PCI_EXP_LNK_X32: + width = PCIE_LINK_WIDTH_32; + break; + default: + /* Unreachable */ + abort(); + } + + visit_type_enum(v, prop->name, &width, prop->info->enum_table, errp); +} + +static void set_prop_pcielinkwidth(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + DeviceState *dev = DEVICE(obj); + Property *prop = opaque; + PCIExpLinkWidth *p = qdev_get_prop_ptr(dev, prop); + int width; + Error *local_err = NULL; + + if (dev->realized) { + qdev_prop_set_after_realize(dev, name, errp); + return; + } + + visit_type_enum(v, prop->name, &width, prop->info->enum_table, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + switch (width) { + case PCIE_LINK_WIDTH_1: + *p = QEMU_PCI_EXP_LNK_X1; + break; + case PCIE_LINK_WIDTH_2: + *p = QEMU_PCI_EXP_LNK_X2; + break; + case PCIE_LINK_WIDTH_4: + *p = QEMU_PCI_EXP_LNK_X4; + break; + case PCIE_LINK_WIDTH_8: + *p = QEMU_PCI_EXP_LNK_X8; + break; + case PCIE_LINK_WIDTH_12: + *p = QEMU_PCI_EXP_LNK_X12; + break; + case PCIE_LINK_WIDTH_16: + *p = QEMU_PCI_EXP_LNK_X16; + break; + case PCIE_LINK_WIDTH_32: + *p = QEMU_PCI_EXP_LNK_X32; + break; + default: + /* Unreachable */ + abort(); + } +} + +const PropertyInfo qdev_prop_pcie_link_width = { + .name = "PCIELinkWidth", + .description = "1/2/4/8/12/16/32", + .enum_table = &PCIELinkWidth_lookup, + .get = get_prop_pcielinkwidth, + .set = set_prop_pcielinkwidth, + .set_default_value = set_default_value_enum, +}; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 529b82de18..f9b6efe509 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -59,6 +59,8 @@ static void bus_remove_child(BusState *bus, DeviceState *child) snprintf(name, sizeof(name), "child[%d]", kid->index); QTAILQ_REMOVE(&bus->children, kid, sibling); + bus->num_children--; + /* This gives back ownership of kid->child back to us. */ object_property_del(OBJECT(bus), name, NULL); object_unref(OBJECT(kid->child)); @@ -73,6 +75,7 @@ static void bus_add_child(BusState *bus, DeviceState *child) char name[32]; BusChild *kid = g_malloc0(sizeof(*kid)); + bus->num_children++; kid->index = bus->max_index++; kid->child = child; object_ref(OBJECT(kid->child)); @@ -158,7 +161,7 @@ DeviceState *qdev_try_create(BusState *bus, const char *type) return dev; } -static QTAILQ_HEAD(device_listeners, DeviceListener) device_listeners +static QTAILQ_HEAD(, DeviceListener) device_listeners = QTAILQ_HEAD_INITIALIZER(device_listeners); enum ListenerDirection { Forward, Reverse }; @@ -177,7 +180,7 @@ enum ListenerDirection { Forward, Reverse }; break; \ case Reverse: \ QTAILQ_FOREACH_REVERSE(_listener, &device_listeners, \ - device_listeners, link) { \ + link) { \ if (_listener->_callback) { \ _listener->_callback(_listener, ##_args); \ } \ @@ -233,14 +236,20 @@ HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev) return NULL; } +HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev) +{ + if (dev->parent_bus) { + return dev->parent_bus->hotplug_handler; + } + return NULL; +} + HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev) { - HotplugHandler *hotplug_ctrl; + HotplugHandler *hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); - if (dev->parent_bus && dev->parent_bus->hotplug_handler) { - hotplug_ctrl = dev->parent_bus->hotplug_handler; - } else { - hotplug_ctrl = qdev_get_machine_hotplug_handler(dev); + if (hotplug_ctrl == NULL && dev->parent_bus) { + hotplug_ctrl = qdev_get_bus_hotplug_handler(dev); } return hotplug_ctrl; } @@ -286,8 +295,7 @@ void qbus_reset_all_fn(void *opaque) void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - /* just zap it */ - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } /* @@ -643,7 +651,7 @@ static void qdev_get_legacy_property(Object *obj, Visitor *v, * the string depends on the property type. Legacy properties are only * needed for "info qtree". * - * Do not use this is new code! QOM Properties added through this interface + * Do not use this in new code! QOM Properties added through this interface * will be given names in the "legacy" namespace. */ static void qdev_property_add_legacy(DeviceState *dev, Property *prop, @@ -832,14 +840,6 @@ static void device_set_realized(Object *obj, bool value, Error **errp) DEVICE_LISTENER_CALL(realize, Forward, dev); - if (hotplug_ctrl) { - hotplug_handler_plug(hotplug_ctrl, dev, &local_err); - } - - if (local_err != NULL) { - goto post_realize_fail; - } - /* * always free/re-initialize here since the value cannot be cleaned up * in device_unrealize due to its usage later on in the unplug path @@ -869,8 +869,12 @@ static void device_set_realized(Object *obj, bool value, Error **errp) dev->pending_deleted_event = false; if (hotplug_ctrl) { - hotplug_handler_post_plug(hotplug_ctrl, dev); - } + hotplug_handler_plug(hotplug_ctrl, dev, &local_err); + if (local_err != NULL) { + goto child_realize_fail; + } + } + } else if (!value && dev->realized) { Error **local_errp = NULL; QLIST_FOREACH(bus, &dev->child_bus, sibling) { @@ -976,6 +980,11 @@ static void device_initfn(Object *obj) static void device_post_init(Object *obj) { + /* + * Note: ordered so that the user's global properties take + * precedence. + */ + object_apply_compat_props(obj); qdev_prop_set_globals(DEVICE(obj)); } @@ -1000,8 +1009,7 @@ static void device_finalize(Object *obj) if (dev->pending_deleted_event) { g_assert(dev->canonical_path); - qapi_event_send_device_deleted(!!dev->id, dev->id, dev->canonical_path, - &error_abort); + qapi_event_send_device_deleted(!!dev->id, dev->id, dev->canonical_path); g_free(dev->canonical_path); dev->canonical_path = NULL; } diff --git a/hw/core/reset.c b/hw/core/reset.c index 84c8869371..9c477f2bf5 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -35,7 +35,7 @@ typedef struct QEMUResetEntry { void *opaque; } QEMUResetEntry; -static QTAILQ_HEAD(reset_handlers, QEMUResetEntry) reset_handlers = +static QTAILQ_HEAD(, QEMUResetEntry) reset_handlers = QTAILQ_HEAD_INITIALIZER(reset_handlers); void qemu_register_reset(QEMUResetHandler *func, void *opaque) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 3c8e53b188..307cf90a51 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -201,18 +201,13 @@ void sysbus_init_ioports(SysBusDevice *dev, uint32_t ioport, uint32_t size) } } -/* TODO remove once all sysbus devices have been converted to realize */ +/* The purpose of preserving this empty realize function + * is to prevent the parent_realize field of some subclasses + * from being set to NULL to break the normal init/realize + * of some devices. + */ static void sysbus_realize(DeviceState *dev, Error **errp) { - SysBusDevice *sd = SYS_BUS_DEVICE(dev); - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(sd); - - if (!sbc->init) { - return; - } - if (sbc->init(sd) < 0) { - error_setg(errp, "Device initialization failed"); - } } DeviceState *sysbus_create_varargs(const char *name, @@ -293,16 +288,8 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) { SysBusDevice *s = SYS_BUS_DEVICE(dev); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_GET_CLASS(s); - /* for the explicit unit address fallback case: */ char *addr, *fw_dev_path; - if (s->num_mmio) { - return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), - s->mmio[0].addr); - } - if (s->num_pio) { - return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); - } if (sbc->explicit_ofw_unit_address) { addr = sbc->explicit_ofw_unit_address(s); if (addr) { @@ -311,6 +298,13 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) return fw_dev_path; } } + if (s->num_mmio) { + return g_strdup_printf("%s@" TARGET_FMT_plx, qdev_fw_name(dev), + s->mmio[0].addr); + } + if (s->num_pio) { + return g_strdup_printf("%s@i%04x", qdev_fw_name(dev), s->pio[0]); + } return g_strdup(qdev_fw_name(dev)); } @@ -363,9 +357,6 @@ static void main_system_bus_create(void) qbus_create_inplace(main_system_bus, system_bus_info.instance_size, TYPE_SYSTEM_BUS, NULL, "main-system-bus"); OBJECT(main_system_bus)->free = g_free; - object_property_add_child(container_get(qdev_get_machine(), - "/unattached"), - "sysbus", OBJECT(main_system_bus), NULL); } BusState *sysbus_get_default(void) diff --git a/hw/core/uboot_image.h b/hw/core/uboot_image.h index 34c11a70a6..608022de6e 100644 --- a/hw/core/uboot_image.h +++ b/hw/core/uboot_image.h @@ -124,6 +124,7 @@ #define IH_TYPE_SCRIPT 6 /* Script file */ #define IH_TYPE_FILESYSTEM 7 /* Filesystem Image (any type) */ #define IH_TYPE_FLATDT 8 /* Binary Flat Device Tree Blob */ +#define IH_TYPE_KERNEL_NOLOAD 14 /* OS Kernel Image (noload) */ /* * Compression Types diff --git a/hw/cpu/Kconfig b/hw/cpu/Kconfig new file mode 100644 index 0000000000..1767d028ac --- /dev/null +++ b/hw/cpu/Kconfig @@ -0,0 +1,8 @@ +config ARM11MPCORE + bool + +config A9MPCORE + bool + +config A15MPCORE + bool diff --git a/hw/cpu/Makefile.objs b/hw/cpu/Makefile.objs index cd52d20b65..8db9e8a7b3 100644 --- a/hw/cpu/Makefile.objs +++ b/hw/cpu/Makefile.objs @@ -2,4 +2,4 @@ obj-$(CONFIG_ARM11MPCORE) += arm11mpcore.o obj-$(CONFIG_REALVIEW) += realview_mpcore.o obj-$(CONFIG_A9MPCORE) += a9mpcore.o obj-$(CONFIG_A15MPCORE) += a15mpcore.o -common-obj-y += core.o +common-obj-y += core.o cluster.o diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 43c1079493..5649843cd8 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -53,6 +53,7 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) int i; Error *err = NULL; bool has_el3; + bool has_el2 = false; Object *cpuobj; gicdev = DEVICE(&s->gic); @@ -67,6 +68,10 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) 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); + /* Similarly for virtualization support */ + has_el2 = object_property_find(cpuobj, "has_el2", NULL) && + object_property_get_bool(cpuobj, "has_el2", &error_abort); + qdev_prop_set_bit(gicdev, "has-virtualization-extensions", has_el2); } object_property_set_bool(OBJECT(&s->gic), true, "realized", &err); @@ -103,20 +108,40 @@ static void a15mp_priv_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(gicdev, ppibase + timer_irq[irq])); } + if (has_el2) { + /* Connect the GIC maintenance interrupt to PPI ID 25 */ + sysbus_connect_irq(SYS_BUS_DEVICE(gicdev), i + 4 * s->num_cpu, + qdev_get_gpio_in(gicdev, ppibase + 25)); + } } /* Memory map (addresses are offsets from PERIPHBASE): * 0x0000-0x0fff -- reserved * 0x1000-0x1fff -- GIC Distributor * 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) + * 0x4000-0x4fff -- GIC virtual interface control for this CPU + * 0x5000-0x51ff -- GIC virtual interface control for CPU 0 + * 0x5200-0x53ff -- GIC virtual interface control for CPU 1 + * 0x5400-0x55ff -- GIC virtual interface control for CPU 2 + * 0x5600-0x57ff -- GIC virtual interface control for CPU 3 + * 0x6000-0x7fff -- GIC virtual CPU interface */ memory_region_add_subregion(&s->container, 0x1000, sysbus_mmio_get_region(busdev, 0)); memory_region_add_subregion(&s->container, 0x2000, sysbus_mmio_get_region(busdev, 1)); + if (has_el2) { + memory_region_add_subregion(&s->container, 0x4000, + sysbus_mmio_get_region(busdev, 2)); + memory_region_add_subregion(&s->container, 0x6000, + sysbus_mmio_get_region(busdev, 3)); + for (i = 0; i < s->num_cpu; i++) { + hwaddr base = 0x5000 + i * 0x200; + MemoryRegion *mr = sysbus_mmio_get_region(busdev, + 4 + s->num_cpu + i); + memory_region_add_subregion(&s->container, base, mr); + } + } } static Property a15mp_priv_properties[] = { diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c new file mode 100644 index 0000000000..6f5f037b3c --- /dev/null +++ b/hw/cpu/cluster.c @@ -0,0 +1,99 @@ +/* + * QEMU CPU cluster + * + * Copyright (c) 2018 GreenSocs SAS + * + * 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/cpu/cluster.h" +#include "qom/cpu.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "qemu/cutils.h" + +static Property cpu_cluster_properties[] = { + DEFINE_PROP_UINT32("cluster-id", CPUClusterState, cluster_id, 0), + DEFINE_PROP_END_OF_LIST() +}; + +typedef struct CallbackData { + CPUClusterState *cluster; + int cpu_count; +} CallbackData; + +static int add_cpu_to_cluster(Object *obj, void *opaque) +{ + CallbackData *cbdata = opaque; + CPUState *cpu = (CPUState *)object_dynamic_cast(obj, TYPE_CPU); + + if (cpu) { + cpu->cluster_index = cbdata->cluster->cluster_id; + cbdata->cpu_count++; + } + return 0; +} + +static void cpu_cluster_realize(DeviceState *dev, Error **errp) +{ + /* Iterate through all our CPU children and set their cluster_index */ + CPUClusterState *cluster = CPU_CLUSTER(dev); + Object *cluster_obj = OBJECT(dev); + CallbackData cbdata = { + .cluster = cluster, + .cpu_count = 0, + }; + + if (cluster->cluster_id >= MAX_CLUSTERS) { + error_setg(errp, "cluster-id must be less than %d", MAX_CLUSTERS); + return; + } + + object_child_foreach_recursive(cluster_obj, add_cpu_to_cluster, &cbdata); + + /* + * A cluster with no CPUs is a bug in the board/SoC code that created it; + * if you hit this during development of new code, check that you have + * created the CPUs and parented them into the cluster object before + * realizing the cluster object. + */ + assert(cbdata.cpu_count > 0); +} + +static void cpu_cluster_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = cpu_cluster_properties; + dc->realize = cpu_cluster_realize; + + /* This is not directly for users, CPU children must be attached by code */ + dc->user_creatable = false; +} + +static const TypeInfo cpu_cluster_type_info = { + .name = TYPE_CPU_CLUSTER, + .parent = TYPE_DEVICE, + .instance_size = sizeof(CPUClusterState), + .class_init = cpu_cluster_class_init, +}; + +static void cpu_cluster_register_types(void) +{ + type_register_static(&cpu_cluster_type_info); +} + +type_init(cpu_cluster_register_types) diff --git a/hw/cris/Kconfig b/hw/cris/Kconfig new file mode 100644 index 0000000000..884ad2cbc0 --- /dev/null +++ b/hw/cris/Kconfig @@ -0,0 +1,9 @@ +config AXIS + bool + select ETRAXFS + select PFLASH_CFI02 + select NAND + +config ETRAXFS + bool + select PTIMER diff --git a/hw/cris/Makefile.objs b/hw/cris/Makefile.objs index 7624173f77..a4a27b3a13 100644 --- a/hw/cris/Makefile.objs +++ b/hw/cris/Makefile.objs @@ -1,2 +1,2 @@ obj-y += boot.o -obj-y += axis_dev88.o +obj-$(CONFIG_AXIS) += axis_dev88.o diff --git a/hw/cris/boot.c b/hw/cris/boot.c index f896ed7f86..95cba2151b 100644 --- a/hw/cris/boot.c +++ b/hw/cris/boot.c @@ -75,7 +75,8 @@ void cris_load_image(CRISCPU *cpu, struct cris_load_info *li) env->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, + image_size = load_elf(li->image_filename, NULL, + translate_kernel_address, NULL, &entry, NULL, &high, 0, EM_CRIS, 0, 0); li->entry = entry; if (image_size < 0) { diff --git a/hw/display/Kconfig b/hw/display/Kconfig new file mode 100644 index 0000000000..72be57a403 --- /dev/null +++ b/hw/display/Kconfig @@ -0,0 +1,114 @@ +config EDID + bool + +config FW_CFG_DMA + bool + +config ADS7846 + bool + +config VGA_CIRRUS + bool + default y if PCI_DEVICES + depends on PCI + select VGA + +config G364FB + bool + +config JAZZ_LED + bool + +config PL110 + bool + +config SII9022 + bool + depends on I2C + +config SSD0303 + bool + depends on I2C + +config SSD0323 + bool + +config VGA_PCI + bool + default y if PCI_DEVICES + depends on PCI + select VGA + select EDID + +config VGA_ISA + bool + depends on ISA_BUS + select VGA + +config VGA_ISA_MM + bool + select VGA + +config VMWARE_VGA + bool + default y if PCI_DEVICES + depends on PCI + select VGA + +config BOCHS_DISPLAY + bool + default y if PCI_DEVICES + depends on PCI + select VGA + select EDID + +config BLIZZARD + bool + +config FRAMEBUFFER + bool + +config MILKYMIST_TMU2 + bool + depends on OPENGL && X11 + +config SM501 + bool + select I2C + select DDC + select SERIAL + +config TCX + bool + +config CG3 + bool + +config VGA + bool + +config QXL + bool + depends on SPICE && PCI + select VGA + +config VIRTIO_GPU + bool + default y + depends on VIRTIO + select EDID + +config VIRTIO_VGA + bool + # defaults to "N", enabled by specific boards + depends on VIRTIO_PCI + select VGA + +config DPCD + bool + +config ATI_VGA + bool + default y if PCI_DEVICES + depends on PCI + select VGA diff --git a/hw/display/Makefile.objs b/hw/display/Makefile.objs index fb8408c6d0..dbd453ab1b 100644 --- a/hw/display/Makefile.objs +++ b/hw/display/Makefile.objs @@ -1,8 +1,11 @@ -common-obj-y += ramfb.o -common-obj-y += ramfb-standalone.o +common-obj-$(CONFIG_EDID) += edid-generate.o edid-region.o + +common-obj-$(CONFIG_FW_CFG_DMA) += ramfb.o +common-obj-$(CONFIG_FW_CFG_DMA) += ramfb-standalone.o common-obj-$(CONFIG_ADS7846) += ads7846.o common-obj-$(CONFIG_VGA_CIRRUS) += cirrus_vga.o +common-obj-$(call land,$(CONFIG_VGA_CIRRUS),$(CONFIG_VGA_ISA))+=cirrus_vga_isa.o common-obj-$(CONFIG_G364FB) += g364fb.o common-obj-$(CONFIG_JAZZ_LED) += jazz_led.o common-obj-$(CONFIG_PL110) += pl110.o @@ -12,20 +15,20 @@ common-obj-$(CONFIG_SSD0323) += ssd0323.o common-obj-$(CONFIG_XEN) += xenfb.o common-obj-$(CONFIG_VGA_PCI) += vga-pci.o -common-obj-$(CONFIG_VGA_PCI) += bochs-display.o common-obj-$(CONFIG_VGA_ISA) += vga-isa.o common-obj-$(CONFIG_VGA_ISA_MM) += vga-isa-mm.o common-obj-$(CONFIG_VMWARE_VGA) += vmware_vga.o +common-obj-$(CONFIG_BOCHS_DISPLAY) += bochs-display.o common-obj-$(CONFIG_BLIZZARD) += blizzard.o common-obj-$(CONFIG_EXYNOS4) += exynos4210_fimd.o common-obj-$(CONFIG_FRAMEBUFFER) += framebuffer.o -common-obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o +obj-$(CONFIG_MILKYMIST) += milkymist-vgafb.o common-obj-$(CONFIG_ZAURUS) += tc6393xb.o -common-obj-$(CONFIG_MILKYMIST_TMU2) += milkymist-tmu2.o -milkymist-tmu2.o-cflags := $(X11_CFLAGS) -milkymist-tmu2.o-libs := $(X11_LIBS) +obj-$(CONFIG_MILKYMIST_TMU2) += milkymist-tmu2.o +milkymist-tmu2.o-cflags := $(X11_CFLAGS) $(OPENGL_CFLAGS) +milkymist-tmu2.o-libs := $(X11_LIBS) $(OPENGL_LIBS) obj-$(CONFIG_OMAP) += omap_dss.o obj-$(CONFIG_OMAP) += omap_lcdc.o @@ -48,3 +51,5 @@ virtio-gpu-3d.o-cflags := $(VIRGL_CFLAGS) virtio-gpu-3d.o-libs += $(VIRGL_LIBS) obj-$(CONFIG_DPCD) += dpcd.o obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dp.o + +obj-$(CONFIG_ATI_VGA) += ati.o ati_2d.o ati_dbg.o diff --git a/hw/display/ati.c b/hw/display/ati.c new file mode 100644 index 0000000000..db409be3c9 --- /dev/null +++ b/hw/display/ati.c @@ -0,0 +1,859 @@ +/* + * QEMU ATI SVGA emulation + * + * Copyright (c) 2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +/* + * WARNING: + * This is very incomplete and only enough for Linux console and some + * unaccelerated X output at the moment. + * Currently it's little more than a frame buffer with minimal functions, + * other more advanced features of the hardware are yet to be implemented. + * We only aim for Rage 128 Pro (and some RV100) and 2D only at first, + * No 3D at all yet (maybe after 2D works, but feel free to improve it) + */ + +#include "ati_int.h" +#include "ati_regs.h" +#include "vga_regs.h" +#include "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/hw.h" +#include "ui/console.h" +#include "trace.h" + +#define ATI_DEBUG_HW_CURSOR 0 + +static const struct { + const char *name; + uint16_t dev_id; +} ati_model_aliases[] = { + { "rage128p", PCI_DEVICE_ID_ATI_RAGE128_PF }, + { "rv100", PCI_DEVICE_ID_ATI_RADEON_QY }, +}; + +enum { VGA_MODE, EXT_MODE }; + +static void ati_vga_switch_mode(ATIVGAState *s) +{ + DPRINTF("%d -> %d\n", + s->mode, !!(s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN)); + if (s->regs.crtc_gen_cntl & CRTC2_EXT_DISP_EN) { + /* Extended mode enabled */ + s->mode = EXT_MODE; + if (s->regs.crtc_gen_cntl & CRTC2_EN) { + /* CRT controller enabled, use CRTC values */ + uint32_t offs = s->regs.crtc_offset & 0x07ffffff; + int stride = (s->regs.crtc_pitch & 0x7ff) * 8; + int bpp = 0; + int h, v; + + if (s->regs.crtc_h_total_disp == 0) { + s->regs.crtc_h_total_disp = ((640 / 8) - 1) << 16; + } + if (s->regs.crtc_v_total_disp == 0) { + s->regs.crtc_v_total_disp = (480 - 1) << 16; + } + h = ((s->regs.crtc_h_total_disp >> 16) + 1) * 8; + v = (s->regs.crtc_v_total_disp >> 16) + 1; + switch (s->regs.crtc_gen_cntl & CRTC_PIX_WIDTH_MASK) { + case CRTC_PIX_WIDTH_4BPP: + bpp = 4; + break; + case CRTC_PIX_WIDTH_8BPP: + bpp = 8; + break; + case CRTC_PIX_WIDTH_15BPP: + bpp = 15; + break; + case CRTC_PIX_WIDTH_16BPP: + bpp = 16; + break; + case CRTC_PIX_WIDTH_24BPP: + bpp = 24; + break; + case CRTC_PIX_WIDTH_32BPP: + bpp = 32; + break; + default: + qemu_log_mask(LOG_UNIMP, "Unsupported bpp value\n"); + } + assert(bpp != 0); + DPRINTF("Switching to %dx%d %d %d @ %x\n", h, v, stride, bpp, offs); + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); + /* reset VBE regs then set up mode */ + s->vga.vbe_regs[VBE_DISPI_INDEX_XRES] = h; + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] = v; + s->vga.vbe_regs[VBE_DISPI_INDEX_BPP] = bpp; + /* enable mode via ioport so it updates vga regs */ + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_ENABLED | + VBE_DISPI_LFB_ENABLED | VBE_DISPI_NOCLEARMEM | + (s->regs.dac_cntl & DAC_8BIT_EN ? VBE_DISPI_8BIT_DAC : 0)); + /* now set offset and stride after enable as that resets these */ + if (stride) { + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_VIRT_WIDTH); + vbe_ioport_write_data(&s->vga, 0, stride); + if (offs % stride == 0) { + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_Y_OFFSET); + vbe_ioport_write_data(&s->vga, 0, offs / stride); + } else { + /* FIXME what to do with this? */ + error_report("VGA offset is not multiple of pitch, " + "expect bad picture"); + } + } + } + } else { + /* VGA mode enabled */ + s->mode = VGA_MODE; + vbe_ioport_write_index(&s->vga, 0, VBE_DISPI_INDEX_ENABLE); + vbe_ioport_write_data(&s->vga, 0, VBE_DISPI_DISABLED); + } +} + +/* Used by host side hardware cursor */ +static void ati_cursor_define(ATIVGAState *s) +{ + uint8_t data[1024]; + uint8_t *src; + int i, j, idx = 0; + + if ((s->regs.cur_offset & BIT(31)) || s->cursor_guest_mode) { + return; /* Do not update cursor if locked or rendered by guest */ + } + /* FIXME handle cur_hv_offs correctly */ + src = s->vga.vram_ptr + (s->regs.crtc_offset & 0x07ffffff) + + s->regs.cur_offset - (s->regs.cur_hv_offs >> 16) - + (s->regs.cur_hv_offs & 0xffff) * 16; + for (i = 0; i < 64; i++) { + for (j = 0; j < 8; j++, idx++) { + data[idx] = src[i * 16 + j]; + data[512 + idx] = src[i * 16 + j + 8]; + } + } + if (!s->cursor) { + s->cursor = cursor_alloc(64, 64); + } + cursor_set_mono(s->cursor, s->regs.cur_color1, s->regs.cur_color0, + &data[512], 1, &data[0]); + dpy_cursor_define(s->vga.con, s->cursor); +} + +/* Alternatively support guest rendered hardware cursor */ +static void ati_cursor_invalidate(VGACommonState *vga) +{ + ATIVGAState *s = container_of(vga, ATIVGAState, vga); + int size = (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) ? 64 : 0; + + if (s->regs.cur_offset & BIT(31)) { + return; /* Do not update cursor if locked */ + } + if (s->cursor_size != size || + vga->hw_cursor_x != s->regs.cur_hv_pos >> 16 || + vga->hw_cursor_y != (s->regs.cur_hv_pos & 0xffff) || + s->cursor_offset != s->regs.cur_offset - (s->regs.cur_hv_offs >> 16) - + (s->regs.cur_hv_offs & 0xffff) * 16) { + /* Remove old cursor then update and show new one if needed */ + vga_invalidate_scanlines(vga, vga->hw_cursor_y, vga->hw_cursor_y + 63); + vga->hw_cursor_x = s->regs.cur_hv_pos >> 16; + vga->hw_cursor_y = s->regs.cur_hv_pos & 0xffff; + s->cursor_offset = s->regs.cur_offset - (s->regs.cur_hv_offs >> 16) - + (s->regs.cur_hv_offs & 0xffff) * 16; + s->cursor_size = size; + if (size) { + vga_invalidate_scanlines(vga, + vga->hw_cursor_y, vga->hw_cursor_y + 63); + } + } +} + +static void ati_cursor_draw_line(VGACommonState *vga, uint8_t *d, int scr_y) +{ + ATIVGAState *s = container_of(vga, ATIVGAState, vga); + uint8_t *src; + uint32_t *dp = (uint32_t *)d; + int i, j, h; + + if (!(s->regs.crtc_gen_cntl & CRTC2_CUR_EN) || + scr_y < vga->hw_cursor_y || scr_y >= vga->hw_cursor_y + 64 || + scr_y > s->regs.crtc_v_total_disp >> 16) { + return; + } + /* FIXME handle cur_hv_offs correctly */ + src = s->vga.vram_ptr + (s->regs.crtc_offset & 0x07ffffff) + + s->cursor_offset + (scr_y - vga->hw_cursor_y) * 16; + dp = &dp[vga->hw_cursor_x]; + h = ((s->regs.crtc_h_total_disp >> 16) + 1) * 8; + for (i = 0; i < 8; i++) { + uint32_t color; + uint8_t abits = src[i]; + uint8_t xbits = src[i + 8]; + for (j = 0; j < 8; j++, abits <<= 1, xbits <<= 1) { + if (abits & BIT(7)) { + if (xbits & BIT(7)) { + color = dp[i * 8 + j] ^ 0xffffffff; /* complement */ + } else { + continue; /* transparent, no change */ + } + } else { + color = (xbits & BIT(7) ? s->regs.cur_color1 : + s->regs.cur_color0) << 8 | 0xff; + } + if (vga->hw_cursor_x + i * 8 + j >= h) { + return; /* end of screen, don't span to next line */ + } + dp[i * 8 + j] = color; + } + } +} + +static inline uint64_t ati_reg_read_offs(uint32_t reg, int offs, + unsigned int size) +{ + if (offs == 0 && size == 4) { + return reg; + } else { + return extract32(reg, offs * BITS_PER_BYTE, size * BITS_PER_BYTE); + } +} + +static uint64_t ati_mm_read(void *opaque, hwaddr addr, unsigned int size) +{ + ATIVGAState *s = opaque; + uint64_t val = 0; + + switch (addr) { + case MM_INDEX: + val = s->regs.mm_index; + break; + case MM_DATA ... MM_DATA + 3: + /* indexed access to regs or memory */ + if (s->regs.mm_index & BIT(31)) { + uint32_t idx = s->regs.mm_index & ~BIT(31); + if (idx <= s->vga.vram_size - size) { + val = ldn_le_p(s->vga.vram_ptr + idx, size); + } + } else { + val = ati_mm_read(s, s->regs.mm_index + addr - MM_DATA, size); + } + break; + case BIOS_0_SCRATCH ... BUS_CNTL - 1: + { + int i = (addr - BIOS_0_SCRATCH) / 4; + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF && i > 3) { + break; + } + val = ati_reg_read_offs(s->regs.bios_scratch[i], + addr - (BIOS_0_SCRATCH + i * 4), size); + break; + } + case CRTC_GEN_CNTL ... CRTC_GEN_CNTL + 3: + val = ati_reg_read_offs(s->regs.crtc_gen_cntl, + addr - CRTC_GEN_CNTL, size); + break; + case CRTC_EXT_CNTL ... CRTC_EXT_CNTL + 3: + val = ati_reg_read_offs(s->regs.crtc_ext_cntl, + addr - CRTC_EXT_CNTL, size); + break; + case DAC_CNTL: + val = s->regs.dac_cntl; + break; +/* case GPIO_MONID: FIXME hook up DDC I2C here */ + case PALETTE_INDEX: + /* FIXME unaligned access */ + val = vga_ioport_read(&s->vga, VGA_PEL_IR) << 16; + val |= vga_ioport_read(&s->vga, VGA_PEL_IW) & 0xff; + break; + case PALETTE_DATA: + val = vga_ioport_read(&s->vga, VGA_PEL_D); + break; + case CNFG_MEMSIZE: + val = s->vga.vram_size; + break; + case MC_STATUS: + val = 5; + break; + case RBBM_STATUS: + case GUI_STAT: + val = 64; /* free CMDFIFO entries */ + break; + case CRTC_H_TOTAL_DISP: + val = s->regs.crtc_h_total_disp; + break; + case CRTC_H_SYNC_STRT_WID: + val = s->regs.crtc_h_sync_strt_wid; + break; + case CRTC_V_TOTAL_DISP: + val = s->regs.crtc_v_total_disp; + break; + case CRTC_V_SYNC_STRT_WID: + val = s->regs.crtc_v_sync_strt_wid; + break; + case CRTC_OFFSET: + val = s->regs.crtc_offset; + break; + case CRTC_OFFSET_CNTL: + val = s->regs.crtc_offset_cntl; + break; + case CRTC_PITCH: + val = s->regs.crtc_pitch; + break; + case 0xf00 ... 0xfff: + val = pci_default_read_config(&s->dev, addr - 0xf00, size); + break; + case CUR_OFFSET: + val = s->regs.cur_offset; + break; + case CUR_HORZ_VERT_POSN: + val = s->regs.cur_hv_pos; + val |= s->regs.cur_offset & BIT(31); + break; + case CUR_HORZ_VERT_OFF: + val = s->regs.cur_hv_offs; + val |= s->regs.cur_offset & BIT(31); + break; + case CUR_CLR0: + val = s->regs.cur_color0; + break; + case CUR_CLR1: + val = s->regs.cur_color1; + break; + case DST_OFFSET: + val = s->regs.dst_offset; + break; + case DST_PITCH: + val = s->regs.dst_pitch; + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + val &= s->regs.dst_tile << 16; + } + break; + case DST_WIDTH: + val = s->regs.dst_width; + break; + case DST_HEIGHT: + val = s->regs.dst_height; + break; + case SRC_X: + val = s->regs.src_x; + break; + case SRC_Y: + val = s->regs.src_y; + break; + case DST_X: + val = s->regs.dst_x; + break; + case DST_Y: + val = s->regs.dst_y; + break; + case DP_GUI_MASTER_CNTL: + val = s->regs.dp_gui_master_cntl; + break; + case SRC_OFFSET: + val = s->regs.src_offset; + break; + case SRC_PITCH: + val = s->regs.src_pitch; + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + val &= s->regs.src_tile << 16; + } + break; + case DP_BRUSH_BKGD_CLR: + val = s->regs.dp_brush_bkgd_clr; + break; + case DP_BRUSH_FRGD_CLR: + val = s->regs.dp_brush_frgd_clr; + break; + case DP_SRC_FRGD_CLR: + val = s->regs.dp_src_frgd_clr; + break; + case DP_SRC_BKGD_CLR: + val = s->regs.dp_src_bkgd_clr; + break; + case DP_CNTL: + val = s->regs.dp_cntl; + break; + case DP_DATATYPE: + val = s->regs.dp_datatype; + break; + case DP_MIX: + val = s->regs.dp_mix; + break; + case DP_WRITE_MASK: + val = s->regs.dp_write_mask; + break; + case DEFAULT_OFFSET: + val = s->regs.default_offset; + break; + case DEFAULT_PITCH: + val = s->regs.default_pitch; + break; + case DEFAULT_SC_BOTTOM_RIGHT: + val = s->regs.default_sc_bottom_right; + break; + default: + break; + } + if (addr < CUR_OFFSET || addr > CUR_CLR1 || ATI_DEBUG_HW_CURSOR) { + trace_ati_mm_read(size, addr, ati_reg_name(addr & ~3ULL), val); + } + return val; +} + +static inline void ati_reg_write_offs(uint32_t *reg, int offs, + uint64_t data, unsigned int size) +{ + if (offs == 0 && size == 4) { + *reg = data; + } else { + *reg = deposit32(*reg, offs * BITS_PER_BYTE, size * BITS_PER_BYTE, + data); + } +} + +static void ati_mm_write(void *opaque, hwaddr addr, + uint64_t data, unsigned int size) +{ + ATIVGAState *s = opaque; + + if (addr < CUR_OFFSET || addr > CUR_CLR1 || ATI_DEBUG_HW_CURSOR) { + trace_ati_mm_write(size, addr, ati_reg_name(addr & ~3ULL), data); + } + switch (addr) { + case MM_INDEX: + s->regs.mm_index = data; + break; + case MM_DATA ... MM_DATA + 3: + /* indexed access to regs or memory */ + if (s->regs.mm_index & BIT(31)) { + uint32_t idx = s->regs.mm_index & ~BIT(31); + if (idx <= s->vga.vram_size - size) { + stn_le_p(s->vga.vram_ptr + idx, size, data); + } + } else { + ati_mm_write(s, s->regs.mm_index + addr - MM_DATA, data, size); + } + break; + case BIOS_0_SCRATCH ... BUS_CNTL - 1: + { + int i = (addr - BIOS_0_SCRATCH) / 4; + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF && i > 3) { + break; + } + ati_reg_write_offs(&s->regs.bios_scratch[i], + addr - (BIOS_0_SCRATCH + i * 4), data, size); + break; + } + case CRTC_GEN_CNTL ... CRTC_GEN_CNTL + 3: + { + uint32_t val = s->regs.crtc_gen_cntl; + ati_reg_write_offs(&s->regs.crtc_gen_cntl, + addr - CRTC_GEN_CNTL, data, size); + if ((val & CRTC2_CUR_EN) != (s->regs.crtc_gen_cntl & CRTC2_CUR_EN)) { + if (s->cursor_guest_mode) { + s->vga.force_shadow = !!(s->regs.crtc_gen_cntl & CRTC2_CUR_EN); + } else { + if (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) { + ati_cursor_define(s); + } + dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16, + s->regs.cur_hv_pos & 0xffff, + (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) != 0); + } + } + if ((val & (CRTC2_EXT_DISP_EN | CRTC2_EN)) != + (s->regs.crtc_gen_cntl & (CRTC2_EXT_DISP_EN | CRTC2_EN))) { + ati_vga_switch_mode(s); + } + break; + } + case CRTC_EXT_CNTL ... CRTC_EXT_CNTL + 3: + { + uint32_t val = s->regs.crtc_ext_cntl; + ati_reg_write_offs(&s->regs.crtc_ext_cntl, + addr - CRTC_EXT_CNTL, data, size); + if (s->regs.crtc_ext_cntl & CRT_CRTC_DISPLAY_DIS) { + DPRINTF("Display disabled\n"); + s->vga.ar_index &= ~BIT(5); + } else { + DPRINTF("Display enabled\n"); + s->vga.ar_index |= BIT(5); + ati_vga_switch_mode(s); + } + if ((val & CRT_CRTC_DISPLAY_DIS) != + (s->regs.crtc_ext_cntl & CRT_CRTC_DISPLAY_DIS)) { + ati_vga_switch_mode(s); + } + break; + } + case DAC_CNTL: + s->regs.dac_cntl = data & 0xffffe3ff; + s->vga.dac_8bit = !!(data & DAC_8BIT_EN); + break; +/* case GPIO_MONID: FIXME hook up DDC I2C here */ + case PALETTE_INDEX ... PALETTE_INDEX + 3: + if (size == 4) { + vga_ioport_write(&s->vga, VGA_PEL_IR, (data >> 16) & 0xff); + vga_ioport_write(&s->vga, VGA_PEL_IW, data & 0xff); + } else { + if (addr == PALETTE_INDEX) { + vga_ioport_write(&s->vga, VGA_PEL_IW, data & 0xff); + } else { + vga_ioport_write(&s->vga, VGA_PEL_IR, data & 0xff); + } + } + break; + case PALETTE_DATA ... PALETTE_DATA + 3: + data <<= addr - PALETTE_DATA; + data = bswap32(data) >> 8; + vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); + data >>= 8; + vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); + data >>= 8; + vga_ioport_write(&s->vga, VGA_PEL_D, data & 0xff); + break; + case CRTC_H_TOTAL_DISP: + s->regs.crtc_h_total_disp = data & 0x07ff07ff; + break; + case CRTC_H_SYNC_STRT_WID: + s->regs.crtc_h_sync_strt_wid = data & 0x17bf1fff; + break; + case CRTC_V_TOTAL_DISP: + s->regs.crtc_v_total_disp = data & 0x0fff0fff; + break; + case CRTC_V_SYNC_STRT_WID: + s->regs.crtc_v_sync_strt_wid = data & 0x9f0fff; + break; + case CRTC_OFFSET: + s->regs.crtc_offset = data & 0xc7ffffff; + break; + case CRTC_OFFSET_CNTL: + s->regs.crtc_offset_cntl = data; /* FIXME */ + break; + case CRTC_PITCH: + s->regs.crtc_pitch = data & 0x07ff07ff; + break; + case 0xf00 ... 0xfff: + /* read-only copy of PCI config space so ignore writes */ + break; + case CUR_OFFSET: + if (s->regs.cur_offset != (data & 0x87fffff0)) { + s->regs.cur_offset = data & 0x87fffff0; + ati_cursor_define(s); + } + break; + case CUR_HORZ_VERT_POSN: + s->regs.cur_hv_pos = data & 0x3fff0fff; + if (data & BIT(31)) { + s->regs.cur_offset |= data & BIT(31); + } else if (s->regs.cur_offset & BIT(31)) { + s->regs.cur_offset &= ~BIT(31); + ati_cursor_define(s); + } + if (!s->cursor_guest_mode && + (s->regs.crtc_gen_cntl & CRTC2_CUR_EN) && !(data & BIT(31))) { + dpy_mouse_set(s->vga.con, s->regs.cur_hv_pos >> 16, + s->regs.cur_hv_pos & 0xffff, 1); + } + break; + case CUR_HORZ_VERT_OFF: + s->regs.cur_hv_offs = data & 0x3f003f; + if (data & BIT(31)) { + s->regs.cur_offset |= data & BIT(31); + } else if (s->regs.cur_offset & BIT(31)) { + s->regs.cur_offset &= ~BIT(31); + ati_cursor_define(s); + } + break; + case CUR_CLR0: + if (s->regs.cur_color0 != (data & 0xffffff)) { + s->regs.cur_color0 = data & 0xffffff; + ati_cursor_define(s); + } + break; + case CUR_CLR1: + /* + * Update cursor unconditionally here because some clients set up + * other registers before actually writing cursor data to memory at + * offset so we would miss cursor change unless always updating here + */ + s->regs.cur_color1 = data & 0xffffff; + ati_cursor_define(s); + break; + case DST_OFFSET: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.dst_offset = data & 0xfffffff0; + } else { + s->regs.dst_offset = data & 0xfffffc00; + } + break; + case DST_PITCH: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.dst_pitch = data & 0x3fff; + s->regs.dst_tile = (data >> 16) & 1; + } else { + s->regs.dst_pitch = data & 0x3ff0; + } + break; + case DST_TILE: + if (s->dev_id == PCI_DEVICE_ID_ATI_RADEON_QY) { + s->regs.dst_tile = data & 3; + } + break; + case DST_WIDTH: + s->regs.dst_width = data & 0x3fff; + ati_2d_blt(s); + break; + case DST_HEIGHT: + s->regs.dst_height = data & 0x3fff; + break; + case SRC_X: + s->regs.src_x = data & 0x3fff; + break; + case SRC_Y: + s->regs.src_y = data & 0x3fff; + break; + case DST_X: + s->regs.dst_x = data & 0x3fff; + break; + case DST_Y: + s->regs.dst_y = data & 0x3fff; + break; + case SRC_PITCH_OFFSET: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.src_offset = (data & 0x1fffff) << 5; + s->regs.src_pitch = (data >> 21) & 0x3ff; + s->regs.src_tile = data >> 31; + } else { + s->regs.src_offset = (data & 0x3fffff) << 11; + s->regs.src_pitch = (data & 0x3fc00000) >> 16; + s->regs.src_tile = (data >> 30) & 1; + } + break; + case DST_PITCH_OFFSET: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.dst_offset = (data & 0x1fffff) << 5; + s->regs.dst_pitch = (data >> 21) & 0x3ff; + s->regs.dst_tile = data >> 31; + } else { + s->regs.dst_offset = (data & 0x3fffff) << 11; + s->regs.dst_pitch = (data & 0x3fc00000) >> 16; + s->regs.dst_tile = data >> 30; + } + break; + case SRC_Y_X: + s->regs.src_x = data & 0x3fff; + s->regs.src_y = (data >> 16) & 0x3fff; + break; + case DST_Y_X: + s->regs.dst_x = data & 0x3fff; + s->regs.dst_y = (data >> 16) & 0x3fff; + break; + case DST_HEIGHT_WIDTH: + s->regs.dst_width = data & 0x3fff; + s->regs.dst_height = (data >> 16) & 0x3fff; + ati_2d_blt(s); + break; + case DP_GUI_MASTER_CNTL: + s->regs.dp_gui_master_cntl = data & 0xf800000f; + s->regs.dp_datatype = (data & 0x0f00) >> 8 | (data & 0x30f0) << 4 | + (data & 0x4000) << 16; + s->regs.dp_mix = (data & GMC_ROP3_MASK) | (data & 0x7000000) >> 16; + break; + case DST_WIDTH_X: + s->regs.dst_x = data & 0x3fff; + s->regs.dst_width = (data >> 16) & 0x3fff; + ati_2d_blt(s); + break; + case SRC_X_Y: + s->regs.src_y = data & 0x3fff; + s->regs.src_x = (data >> 16) & 0x3fff; + break; + case DST_X_Y: + s->regs.dst_y = data & 0x3fff; + s->regs.dst_x = (data >> 16) & 0x3fff; + break; + case DST_WIDTH_HEIGHT: + s->regs.dst_height = data & 0x3fff; + s->regs.dst_width = (data >> 16) & 0x3fff; + ati_2d_blt(s); + break; + case DST_HEIGHT_Y: + s->regs.dst_y = data & 0x3fff; + s->regs.dst_height = (data >> 16) & 0x3fff; + break; + case SRC_OFFSET: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.src_offset = data & 0xfffffff0; + } else { + s->regs.src_offset = data & 0xfffffc00; + } + break; + case SRC_PITCH: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.src_pitch = data & 0x3fff; + s->regs.src_tile = (data >> 16) & 1; + } else { + s->regs.src_pitch = data & 0x3ff0; + } + break; + case DP_BRUSH_BKGD_CLR: + s->regs.dp_brush_bkgd_clr = data; + break; + case DP_BRUSH_FRGD_CLR: + s->regs.dp_brush_frgd_clr = data; + break; + case DP_CNTL: + s->regs.dp_cntl = data; + break; + case DP_DATATYPE: + s->regs.dp_datatype = data & 0xe0070f0f; + break; + case DP_MIX: + s->regs.dp_mix = data & 0x00ff0700; + break; + case DP_WRITE_MASK: + s->regs.dp_write_mask = data; + break; + case DEFAULT_OFFSET: + data &= (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF ? + 0x03fffc00 : 0xfffffc00); + s->regs.default_offset = data; + break; + case DEFAULT_PITCH: + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + s->regs.default_pitch = data & 0x103ff; + } + break; + case DEFAULT_SC_BOTTOM_RIGHT: + s->regs.default_sc_bottom_right = data & 0x3fff3fff; + break; + default: + break; + } +} + +static const MemoryRegionOps ati_mm_ops = { + .read = ati_mm_read, + .write = ati_mm_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void ati_vga_realize(PCIDevice *dev, Error **errp) +{ + ATIVGAState *s = ATI_VGA(dev); + VGACommonState *vga = &s->vga; + + if (s->model) { + int i; + for (i = 0; i < ARRAY_SIZE(ati_model_aliases); i++) { + if (!strcmp(s->model, ati_model_aliases[i].name)) { + s->dev_id = ati_model_aliases[i].dev_id; + break; + } + } + if (i >= ARRAY_SIZE(ati_model_aliases)) { + warn_report("Unknown ATI VGA model name, " + "using default rage128p"); + } + } + if (s->dev_id != PCI_DEVICE_ID_ATI_RAGE128_PF && + s->dev_id != PCI_DEVICE_ID_ATI_RADEON_QY) { + error_setg(errp, "Unknown ATI VGA device id, " + "only 0x5046 and 0x5159 are supported"); + return; + } + pci_set_word(dev->config + PCI_DEVICE_ID, s->dev_id); + + if (s->dev_id == PCI_DEVICE_ID_ATI_RADEON_QY && + s->vga.vram_size_mb < 16) { + warn_report("Too small video memory for device id"); + s->vga.vram_size_mb = 16; + } + + /* init vga bits */ + vga_common_init(vga, OBJECT(s)); + vga_init(vga, OBJECT(s), pci_address_space(dev), + pci_address_space_io(dev), true); + vga->con = graphic_console_init(DEVICE(s), 0, s->vga.hw_ops, &s->vga); + if (s->cursor_guest_mode) { + vga->cursor_invalidate = ati_cursor_invalidate; + vga->cursor_draw_line = ati_cursor_draw_line; + } + + /* mmio register space */ + memory_region_init_io(&s->mm, OBJECT(s), &ati_mm_ops, s, + "ati.mmregs", 0x4000); + /* io space is alias to beginning of mmregs */ + memory_region_init_alias(&s->io, OBJECT(s), "ati.io", &s->mm, 0, 0x100); + + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &vga->vram); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &s->io); + pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mm); +} + +static void ati_vga_reset(DeviceState *dev) +{ + ATIVGAState *s = ATI_VGA(dev); + + /* reset vga */ + vga_common_reset(&s->vga); + s->mode = VGA_MODE; +} + +static void ati_vga_exit(PCIDevice *dev) +{ + ATIVGAState *s = ATI_VGA(dev); + + graphic_console_close(s->vga.con); +} + +static Property ati_vga_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", ATIVGAState, vga.vram_size_mb, 16), + DEFINE_PROP_STRING("model", ATIVGAState, model), + DEFINE_PROP_UINT16("x-device-id", ATIVGAState, dev_id, + PCI_DEVICE_ID_ATI_RAGE128_PF), + DEFINE_PROP_BOOL("guest_hwcursor", ATIVGAState, cursor_guest_mode, false), + DEFINE_PROP_END_OF_LIST() +}; + +static void ati_vga_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + dc->reset = ati_vga_reset; + dc->props = ati_vga_properties; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); + + k->class_id = PCI_CLASS_DISPLAY_VGA; + k->vendor_id = PCI_VENDOR_ID_ATI; + k->device_id = PCI_DEVICE_ID_ATI_RAGE128_PF; + k->romfile = "vgabios-stdvga.bin"; + k->realize = ati_vga_realize; + k->exit = ati_vga_exit; +} + +static const TypeInfo ati_vga_info = { + .name = TYPE_ATI_VGA, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(ATIVGAState), + .class_init = ati_vga_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void ati_vga_register_types(void) +{ + type_register_static(&ati_vga_info); +} + +type_init(ati_vga_register_types) diff --git a/hw/display/ati_2d.c b/hw/display/ati_2d.c new file mode 100644 index 0000000000..bc98ba6eeb --- /dev/null +++ b/hw/display/ati_2d.c @@ -0,0 +1,167 @@ +/* + * QEMU ATI SVGA emulation + * 2D engine functions + * + * Copyright (c) 2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#include "ati_int.h" +#include "ati_regs.h" +#include "qemu/log.h" +#include "ui/pixel_ops.h" + +/* + * NOTE: + * This is 2D _acceleration_ and supposed to be fast. Therefore, don't try to + * reinvent the wheel (unlikely to get better with a naive implementation than + * existing libraries) and avoid (poorly) reimplementing gfx primitives. + * That is unnecessary and would become a performance problem. Instead, try to + * map to and reuse existing optimised facilities (e.g. pixman) wherever + * possible. + */ + +static int ati_bpp_from_datatype(ATIVGAState *s) +{ + switch (s->regs.dp_datatype & 0xf) { + case 2: + return 8; + case 3: + case 4: + return 16; + case 5: + return 24; + case 6: + return 32; + default: + qemu_log_mask(LOG_UNIMP, "Unknown dst datatype %d\n", + s->regs.dp_datatype & 0xf); + return 0; + } +} + +void ati_2d_blt(ATIVGAState *s) +{ + /* FIXME it is probably more complex than this and may need to be */ + /* rewritten but for now as a start just to get some output: */ + DisplaySurface *ds = qemu_console_surface(s->vga.con); + DPRINTF("%p %u ds: %p %d %d rop: %x\n", s->vga.vram_ptr, + s->vga.vbe_start_addr, surface_data(ds), surface_stride(ds), + surface_bits_per_pixel(ds), + (s->regs.dp_mix & GMC_ROP3_MASK) >> 16); + DPRINTF("%d %d, %d %d, (%d,%d) -> (%d,%d) %dx%d\n", s->regs.src_offset, + s->regs.dst_offset, s->regs.src_pitch, s->regs.dst_pitch, + s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height); + switch (s->regs.dp_mix & GMC_ROP3_MASK) { + case ROP3_SRCCOPY: + { + uint8_t *src_bits, *dst_bits, *end; + int src_stride, dst_stride, bpp = ati_bpp_from_datatype(s); + src_bits = s->vga.vram_ptr + s->regs.src_offset; + dst_bits = s->vga.vram_ptr + s->regs.dst_offset; + src_stride = s->regs.src_pitch; + dst_stride = s->regs.dst_pitch; + + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + src_bits += s->regs.crtc_offset & 0x07ffffff; + dst_bits += s->regs.crtc_offset & 0x07ffffff; + src_stride *= bpp; + dst_stride *= bpp; + } + src_stride /= sizeof(uint32_t); + dst_stride /= sizeof(uint32_t); + + DPRINTF("pixman_blt(%p, %p, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d)\n", + src_bits, dst_bits, src_stride, dst_stride, bpp, bpp, + s->regs.src_x, s->regs.src_y, s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height); + end = s->vga.vram_ptr + s->vga.vram_size; + if (src_bits >= end || dst_bits >= end || + src_bits + (s->regs.src_y + s->regs.dst_height) * src_stride + + s->regs.src_x >= end || + dst_bits + (s->regs.dst_y + s->regs.dst_height) * dst_stride + + s->regs.dst_x >= end) { + qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); + return; + } + pixman_blt((uint32_t *)src_bits, (uint32_t *)dst_bits, + src_stride, dst_stride, bpp, bpp, + s->regs.src_x, s->regs.src_y, + s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height); + if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr && + dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr + + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) { + memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr + + s->regs.dst_offset + + s->regs.dst_y * surface_stride(ds), + s->regs.dst_height * surface_stride(ds)); + } + s->regs.dst_x += s->regs.dst_width; + s->regs.dst_y += s->regs.dst_height; + break; + } + case ROP3_PATCOPY: + case ROP3_BLACKNESS: + case ROP3_WHITENESS: + { + uint8_t *dst_bits, *end; + int dst_stride, bpp = ati_bpp_from_datatype(s); + uint32_t filler = 0; + dst_bits = s->vga.vram_ptr + s->regs.dst_offset; + dst_stride = s->regs.dst_pitch; + + if (s->dev_id == PCI_DEVICE_ID_ATI_RAGE128_PF) { + dst_bits += s->regs.crtc_offset & 0x07ffffff; + dst_stride *= bpp; + } + dst_stride /= sizeof(uint32_t); + + switch (s->regs.dp_mix & GMC_ROP3_MASK) { + case ROP3_PATCOPY: + filler = bswap32(s->regs.dp_brush_frgd_clr); + break; + case ROP3_BLACKNESS: + filler = rgb_to_pixel32(s->vga.palette[0], s->vga.palette[1], + s->vga.palette[2]) << 8 | 0xff; + break; + case ROP3_WHITENESS: + filler = rgb_to_pixel32(s->vga.palette[3], s->vga.palette[4], + s->vga.palette[5]) << 8 | 0xff; + break; + } + + DPRINTF("pixman_fill(%p, %d, %d, %d, %d, %d, %d, %x)\n", + dst_bits, dst_stride, bpp, + s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height, + filler); + end = s->vga.vram_ptr + s->vga.vram_size; + if (dst_bits >= end || + dst_bits + (s->regs.dst_y + s->regs.dst_height) * dst_stride + + s->regs.dst_x >= end) { + qemu_log_mask(LOG_UNIMP, "blt outside vram not implemented\n"); + return; + } + pixman_fill((uint32_t *)dst_bits, dst_stride, bpp, + s->regs.dst_x, s->regs.dst_y, + s->regs.dst_width, s->regs.dst_height, + filler); + if (dst_bits >= s->vga.vram_ptr + s->vga.vbe_start_addr && + dst_bits < s->vga.vram_ptr + s->vga.vbe_start_addr + + s->vga.vbe_regs[VBE_DISPI_INDEX_YRES] * s->vga.vbe_line_offset) { + memory_region_set_dirty(&s->vga.vram, s->vga.vbe_start_addr + + s->regs.dst_offset + + s->regs.dst_y * surface_stride(ds), + s->regs.dst_height * surface_stride(ds)); + } + s->regs.dst_y += s->regs.dst_height; + break; + } + default: + qemu_log_mask(LOG_UNIMP, "Unimplemented ati_2d blt op %x\n", + (s->regs.dp_mix & GMC_ROP3_MASK) >> 16); + } +} diff --git a/hw/display/ati_dbg.c b/hw/display/ati_dbg.c new file mode 100644 index 0000000000..1e6c32624e --- /dev/null +++ b/hw/display/ati_dbg.c @@ -0,0 +1,259 @@ +#include "ati_int.h" + +#ifdef DEBUG_ATI +struct ati_regdesc { + const char *name; + int num; +}; + +static struct ati_regdesc ati_reg_names[] = { + {"MM_INDEX", 0x0000}, + {"MM_DATA", 0x0004}, + {"CLOCK_CNTL_INDEX", 0x0008}, + {"CLOCK_CNTL_DATA", 0x000c}, + {"BIOS_0_SCRATCH", 0x0010}, + {"BUS_CNTL", 0x0030}, + {"BUS_CNTL1", 0x0034}, + {"GEN_INT_CNTL", 0x0040}, + {"CRTC_GEN_CNTL", 0x0050}, + {"CRTC_EXT_CNTL", 0x0054}, + {"DAC_CNTL", 0x0058}, + {"GPIO_MONID", 0x0068}, + {"I2C_CNTL_1", 0x0094}, + {"PALETTE_INDEX", 0x00b0}, + {"PALETTE_DATA", 0x00b4}, + {"CNFG_CNTL", 0x00e0}, + {"GEN_RESET_CNTL", 0x00f0}, + {"CNFG_MEMSIZE", 0x00f8}, + {"MEM_CNTL", 0x0140}, + {"MC_FB_LOCATION", 0x0148}, + {"MC_AGP_LOCATION", 0x014C}, + {"MC_STATUS", 0x0150}, + {"MEM_POWER_MISC", 0x015c}, + {"AGP_BASE", 0x0170}, + {"AGP_CNTL", 0x0174}, + {"AGP_APER_OFFSET", 0x0178}, + {"PCI_GART_PAGE", 0x017c}, + {"PC_NGUI_MODE", 0x0180}, + {"PC_NGUI_CTLSTAT", 0x0184}, + {"MPP_TB_CONFIG", 0x01C0}, + {"MPP_GP_CONFIG", 0x01C8}, + {"VIPH_CONTROL", 0x01D0}, + {"CRTC_H_TOTAL_DISP", 0x0200}, + {"CRTC_H_SYNC_STRT_WID", 0x0204}, + {"CRTC_V_TOTAL_DISP", 0x0208}, + {"CRTC_V_SYNC_STRT_WID", 0x020c}, + {"CRTC_VLINE_CRNT_VLINE", 0x0210}, + {"CRTC_CRNT_FRAME", 0x0214}, + {"CRTC_GUI_TRIG_VLINE", 0x0218}, + {"CRTC_OFFSET", 0x0224}, + {"CRTC_OFFSET_CNTL", 0x0228}, + {"CRTC_PITCH", 0x022c}, + {"OVR_CLR", 0x0230}, + {"OVR_WID_LEFT_RIGHT", 0x0234}, + {"OVR_WID_TOP_BOTTOM", 0x0238}, + {"CUR_OFFSET", 0x0260}, + {"CUR_HORZ_VERT_POSN", 0x0264}, + {"CUR_HORZ_VERT_OFF", 0x0268}, + {"CUR_CLR0", 0x026c}, + {"CUR_CLR1", 0x0270}, + {"LVDS_GEN_CNTL", 0x02d0}, + {"DDA_CONFIG", 0x02e0}, + {"DDA_ON_OFF", 0x02e4}, + {"VGA_DDA_CONFIG", 0x02e8}, + {"VGA_DDA_ON_OFF", 0x02ec}, + {"CRTC2_H_TOTAL_DISP", 0x0300}, + {"CRTC2_H_SYNC_STRT_WID", 0x0304}, + {"CRTC2_V_TOTAL_DISP", 0x0308}, + {"CRTC2_V_SYNC_STRT_WID", 0x030c}, + {"CRTC2_VLINE_CRNT_VLINE", 0x0310}, + {"CRTC2_CRNT_FRAME", 0x0314}, + {"CRTC2_GUI_TRIG_VLINE", 0x0318}, + {"CRTC2_OFFSET", 0x0324}, + {"CRTC2_OFFSET_CNTL", 0x0328}, + {"CRTC2_PITCH", 0x032c}, + {"DDA2_CONFIG", 0x03e0}, + {"DDA2_ON_OFF", 0x03e4}, + {"CRTC2_GEN_CNTL", 0x03f8}, + {"CRTC2_STATUS", 0x03fc}, + {"OV0_SCALE_CNTL", 0x0420}, + {"SUBPIC_CNTL", 0x0540}, + {"PM4_BUFFER_OFFSET", 0x0700}, + {"PM4_BUFFER_CNTL", 0x0704}, + {"PM4_BUFFER_WM_CNTL", 0x0708}, + {"PM4_BUFFER_DL_RPTR_ADDR", 0x070c}, + {"PM4_BUFFER_DL_RPTR", 0x0710}, + {"PM4_BUFFER_DL_WPTR", 0x0714}, + {"PM4_VC_FPU_SETUP", 0x071c}, + {"PM4_FPU_CNTL", 0x0720}, + {"PM4_VC_FORMAT", 0x0724}, + {"PM4_VC_CNTL", 0x0728}, + {"PM4_VC_I01", 0x072c}, + {"PM4_VC_VLOFF", 0x0730}, + {"PM4_VC_VLSIZE", 0x0734}, + {"PM4_IW_INDOFF", 0x0738}, + {"PM4_IW_INDSIZE", 0x073c}, + {"PM4_FPU_FPX0", 0x0740}, + {"PM4_FPU_FPY0", 0x0744}, + {"PM4_FPU_FPX1", 0x0748}, + {"PM4_FPU_FPY1", 0x074c}, + {"PM4_FPU_FPX2", 0x0750}, + {"PM4_FPU_FPY2", 0x0754}, + {"PM4_FPU_FPY3", 0x0758}, + {"PM4_FPU_FPY4", 0x075c}, + {"PM4_FPU_FPY5", 0x0760}, + {"PM4_FPU_FPY6", 0x0764}, + {"PM4_FPU_FPR", 0x0768}, + {"PM4_FPU_FPG", 0x076c}, + {"PM4_FPU_FPB", 0x0770}, + {"PM4_FPU_FPA", 0x0774}, + {"PM4_FPU_INTXY0", 0x0780}, + {"PM4_FPU_INTXY1", 0x0784}, + {"PM4_FPU_INTXY2", 0x0788}, + {"PM4_FPU_INTARGB", 0x078c}, + {"PM4_FPU_FPTWICEAREA", 0x0790}, + {"PM4_FPU_DMAJOR01", 0x0794}, + {"PM4_FPU_DMAJOR12", 0x0798}, + {"PM4_FPU_DMAJOR02", 0x079c}, + {"PM4_FPU_STAT", 0x07a0}, + {"PM4_STAT", 0x07b8}, + {"PM4_TEST_CNTL", 0x07d0}, + {"PM4_MICROCODE_ADDR", 0x07d4}, + {"PM4_MICROCODE_RADDR", 0x07d8}, + {"PM4_MICROCODE_DATAH", 0x07dc}, + {"PM4_MICROCODE_DATAL", 0x07e0}, + {"PM4_CMDFIFO_ADDR", 0x07e4}, + {"PM4_CMDFIFO_DATAH", 0x07e8}, + {"PM4_CMDFIFO_DATAL", 0x07ec}, + {"PM4_BUFFER_ADDR", 0x07f0}, + {"PM4_BUFFER_DATAH", 0x07f4}, + {"PM4_BUFFER_DATAL", 0x07f8}, + {"PM4_MICRO_CNTL", 0x07fc}, + {"CAP0_TRIG_CNTL", 0x0950}, + {"CAP1_TRIG_CNTL", 0x09c0}, + {"RBBM_STATUS", 0x0e40}, + {"PM4_FIFO_DATA_EVEN", 0x1000}, + {"PM4_FIFO_DATA_ODD", 0x1004}, + {"DST_OFFSET", 0x1404}, + {"DST_PITCH", 0x1408}, + {"DST_WIDTH", 0x140c}, + {"DST_HEIGHT", 0x1410}, + {"SRC_X", 0x1414}, + {"SRC_Y", 0x1418}, + {"DST_X", 0x141c}, + {"DST_Y", 0x1420}, + {"SRC_PITCH_OFFSET", 0x1428}, + {"DST_PITCH_OFFSET", 0x142c}, + {"SRC_Y_X", 0x1434}, + {"DST_Y_X", 0x1438}, + {"DST_HEIGHT_WIDTH", 0x143c}, + {"DP_GUI_MASTER_CNTL", 0x146c}, + {"BRUSH_SCALE", 0x1470}, + {"BRUSH_Y_X", 0x1474}, + {"DP_BRUSH_BKGD_CLR", 0x1478}, + {"DP_BRUSH_FRGD_CLR", 0x147c}, + {"DST_WIDTH_X", 0x1588}, + {"DST_HEIGHT_WIDTH_8", 0x158c}, + {"SRC_X_Y", 0x1590}, + {"DST_X_Y", 0x1594}, + {"DST_WIDTH_HEIGHT", 0x1598}, + {"DST_WIDTH_X_INCY", 0x159c}, + {"DST_HEIGHT_Y", 0x15a0}, + {"DST_X_SUB", 0x15a4}, + {"DST_Y_SUB", 0x15a8}, + {"SRC_OFFSET", 0x15ac}, + {"SRC_PITCH", 0x15b0}, + {"DST_HEIGHT_WIDTH_BW", 0x15b4}, + {"CLR_CMP_CNTL", 0x15c0}, + {"CLR_CMP_CLR_SRC", 0x15c4}, + {"CLR_CMP_CLR_DST", 0x15c8}, + {"CLR_CMP_MASK", 0x15cc}, + {"DP_SRC_FRGD_CLR", 0x15d8}, + {"DP_SRC_BKGD_CLR", 0x15dc}, + {"DST_BRES_ERR", 0x1628}, + {"DST_BRES_INC", 0x162c}, + {"DST_BRES_DEC", 0x1630}, + {"DST_BRES_LNTH", 0x1634}, + {"DST_BRES_LNTH_SUB", 0x1638}, + {"SC_LEFT", 0x1640}, + {"SC_RIGHT", 0x1644}, + {"SC_TOP", 0x1648}, + {"SC_BOTTOM", 0x164c}, + {"SRC_SC_RIGHT", 0x1654}, + {"SRC_SC_BOTTOM", 0x165c}, + {"GUI_DEBUG0", 0x16a0}, + {"GUI_DEBUG1", 0x16a4}, + {"GUI_TIMEOUT", 0x16b0}, + {"GUI_TIMEOUT0", 0x16b4}, + {"GUI_TIMEOUT1", 0x16b8}, + {"GUI_PROBE", 0x16bc}, + {"DP_CNTL", 0x16c0}, + {"DP_DATATYPE", 0x16c4}, + {"DP_MIX", 0x16c8}, + {"DP_WRITE_MASK", 0x16cc}, + {"DP_CNTL_XDIR_YDIR_YMAJOR", 0x16d0}, + {"DEFAULT_OFFSET", 0x16e0}, + {"DEFAULT_PITCH", 0x16e4}, + {"DEFAULT_SC_BOTTOM_RIGHT", 0x16e8}, + {"SC_TOP_LEFT", 0x16ec}, + {"SC_BOTTOM_RIGHT", 0x16f0}, + {"SRC_SC_BOTTOM_RIGHT", 0x16f4}, + {"DST_TILE", 0x1700}, + {"WAIT_UNTIL", 0x1720}, + {"CACHE_CNTL", 0x1724}, + {"GUI_STAT", 0x1740}, + {"PC_GUI_MODE", 0x1744}, + {"PC_GUI_CTLSTAT", 0x1748}, + {"PC_DEBUG_MODE", 0x1760}, + {"BRES_DST_ERR_DEC", 0x1780}, + {"TRAIL_BRES_T12_ERR_DEC", 0x1784}, + {"TRAIL_BRES_T12_INC", 0x1788}, + {"DP_T12_CNTL", 0x178c}, + {"DST_BRES_T1_LNTH", 0x1790}, + {"DST_BRES_T2_LNTH", 0x1794}, + {"SCALE_SRC_HEIGHT_WIDTH", 0x1994}, + {"SCALE_OFFSET_0", 0x1998}, + {"SCALE_PITCH", 0x199c}, + {"SCALE_X_INC", 0x19a0}, + {"SCALE_Y_INC", 0x19a4}, + {"SCALE_HACC", 0x19a8}, + {"SCALE_VACC", 0x19ac}, + {"SCALE_DST_X_Y", 0x19b0}, + {"SCALE_DST_HEIGHT_WIDTH", 0x19b4}, + {"SCALE_3D_CNTL", 0x1a00}, + {"SCALE_3D_DATATYPE", 0x1a20}, + {"SETUP_CNTL", 0x1bc4}, + {"SOLID_COLOR", 0x1bc8}, + {"WINDOW_XY_OFFSET", 0x1bcc}, + {"DRAW_LINE_POINT", 0x1bd0}, + {"SETUP_CNTL_PM4", 0x1bd4}, + {"DST_PITCH_OFFSET_C", 0x1c80}, + {"DP_GUI_MASTER_CNTL_C", 0x1c84}, + {"SC_TOP_LEFT_C", 0x1c88}, + {"SC_BOTTOM_RIGHT_C", 0x1c8c}, + {"CLR_CMP_MASK_3D", 0x1A28}, + {"MISC_3D_STATE_CNTL_REG", 0x1CA0}, + {"MC_SRC1_CNTL", 0x19D8}, + {"TEX_CNTL", 0x1800}, + {"RAGE128_MPP_TB_CONFIG", 0x01c0}, + {NULL, -1} +}; + +const char *ati_reg_name(int num) +{ + int i; + + num &= ~3; + for (i = 0; ati_reg_names[i].name; i++) { + if (ati_reg_names[i].num == num) { + return ati_reg_names[i].name; + } + } + return "unknown"; +} +#else +const char *ati_reg_name(int num) +{ + return ""; +} +#endif diff --git a/hw/display/ati_int.h b/hw/display/ati_int.h new file mode 100644 index 0000000000..a6f3e20e63 --- /dev/null +++ b/hw/display/ati_int.h @@ -0,0 +1,96 @@ +/* + * QEMU ATI SVGA emulation + * + * Copyright (c) 2019 BALATON Zoltan + * + * This work is licensed under the GNU GPL license version 2 or later. + */ + +#ifndef ATI_INT_H +#define ATI_INT_H + +#include "qemu/osdep.h" +#include "hw/pci/pci.h" +#include "vga_int.h" + +/*#define DEBUG_ATI*/ + +#ifdef DEBUG_ATI +#define DPRINTF(fmt, ...) printf("%s: " fmt, __func__, ## __VA_ARGS__) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define PCI_VENDOR_ID_ATI 0x1002 +/* Rage128 Pro GL */ +#define PCI_DEVICE_ID_ATI_RAGE128_PF 0x5046 +/* Radeon RV100 (VE) */ +#define PCI_DEVICE_ID_ATI_RADEON_QY 0x5159 + +#define TYPE_ATI_VGA "ati-vga" +#define ATI_VGA(obj) OBJECT_CHECK(ATIVGAState, (obj), TYPE_ATI_VGA) + +typedef struct ATIVGARegs { + uint32_t mm_index; + uint32_t bios_scratch[8]; + uint32_t crtc_gen_cntl; + uint32_t crtc_ext_cntl; + uint32_t dac_cntl; + uint32_t crtc_h_total_disp; + uint32_t crtc_h_sync_strt_wid; + uint32_t crtc_v_total_disp; + uint32_t crtc_v_sync_strt_wid; + uint32_t crtc_offset; + uint32_t crtc_offset_cntl; + uint32_t crtc_pitch; + uint32_t cur_offset; + uint32_t cur_hv_pos; + uint32_t cur_hv_offs; + uint32_t cur_color0; + uint32_t cur_color1; + uint32_t dst_offset; + uint32_t dst_pitch; + uint32_t dst_tile; + uint32_t dst_width; + uint32_t dst_height; + uint32_t src_offset; + uint32_t src_pitch; + uint32_t src_tile; + uint32_t src_x; + uint32_t src_y; + uint32_t dst_x; + uint32_t dst_y; + uint32_t dp_gui_master_cntl; + uint32_t dp_brush_bkgd_clr; + uint32_t dp_brush_frgd_clr; + uint32_t dp_src_frgd_clr; + uint32_t dp_src_bkgd_clr; + uint32_t dp_cntl; + uint32_t dp_datatype; + uint32_t dp_mix; + uint32_t dp_write_mask; + uint32_t default_offset; + uint32_t default_pitch; + uint32_t default_sc_bottom_right; +} ATIVGARegs; + +typedef struct ATIVGAState { + PCIDevice dev; + VGACommonState vga; + char *model; + uint16_t dev_id; + uint8_t mode; + bool cursor_guest_mode; + uint16_t cursor_size; + uint32_t cursor_offset; + QEMUCursor *cursor; + MemoryRegion io; + MemoryRegion mm; + ATIVGARegs regs; +} ATIVGAState; + +const char *ati_reg_name(int num); + +void ati_2d_blt(ATIVGAState *s); + +#endif /* ATI_INT_H */ diff --git a/hw/display/ati_regs.h b/hw/display/ati_regs.h new file mode 100644 index 0000000000..923bfd33ce --- /dev/null +++ b/hw/display/ati_regs.h @@ -0,0 +1,461 @@ +/* + * ATI VGA register definitions + * + * based on: + * linux/include/video/aty128.h + * Register definitions for ATI Rage128 boards + * Anthony Tong , 1999 + * Brad Douglas , 2000 + * + * and linux/include/video/radeon.h + * + * This work is licensed under the GNU GPL license version 2. + */ + +/* + * Register mapping: + * 0x0000-0x00ff Misc regs also accessible via io and mmio space + * 0x0100-0x0eff Misc regs only accessible via mmio + * 0x0f00-0x0fff Read-only copy of PCI config regs + * 0x1000-0x13ff Concurrent Command Engine (CCE) regs + * 0x1400-0x1fff GUI (drawing engine) regs + */ + +#ifndef ATI_REGS_H +#define ATI_REGS_H + +#undef DEFAULT_PITCH /* needed for mingw builds */ + +#define MM_INDEX 0x0000 +#define MM_DATA 0x0004 +#define CLOCK_CNTL_INDEX 0x0008 +#define CLOCK_CNTL_DATA 0x000c +#define BIOS_0_SCRATCH 0x0010 +#define BUS_CNTL 0x0030 +#define BUS_CNTL1 0x0034 +#define GEN_INT_CNTL 0x0040 +#define CRTC_GEN_CNTL 0x0050 +#define CRTC_EXT_CNTL 0x0054 +#define DAC_CNTL 0x0058 +#define GPIO_MONID 0x0068 +#define I2C_CNTL_1 0x0094 +#define PALETTE_INDEX 0x00b0 +#define PALETTE_DATA 0x00b4 +#define CNFG_CNTL 0x00e0 +#define GEN_RESET_CNTL 0x00f0 +#define CNFG_MEMSIZE 0x00f8 +#define MEM_CNTL 0x0140 +#define MC_FB_LOCATION 0x0148 +#define MC_AGP_LOCATION 0x014C +#define MC_STATUS 0x0150 +#define MEM_POWER_MISC 0x015c +#define AGP_BASE 0x0170 +#define AGP_CNTL 0x0174 +#define AGP_APER_OFFSET 0x0178 +#define PCI_GART_PAGE 0x017c +#define PC_NGUI_MODE 0x0180 +#define PC_NGUI_CTLSTAT 0x0184 +#define MPP_TB_CONFIG 0x01C0 +#define MPP_GP_CONFIG 0x01C8 +#define VIPH_CONTROL 0x01D0 +#define CRTC_H_TOTAL_DISP 0x0200 +#define CRTC_H_SYNC_STRT_WID 0x0204 +#define CRTC_V_TOTAL_DISP 0x0208 +#define CRTC_V_SYNC_STRT_WID 0x020c +#define CRTC_VLINE_CRNT_VLINE 0x0210 +#define CRTC_CRNT_FRAME 0x0214 +#define CRTC_GUI_TRIG_VLINE 0x0218 +#define CRTC_OFFSET 0x0224 +#define CRTC_OFFSET_CNTL 0x0228 +#define CRTC_PITCH 0x022c +#define OVR_CLR 0x0230 +#define OVR_WID_LEFT_RIGHT 0x0234 +#define OVR_WID_TOP_BOTTOM 0x0238 +#define CUR_OFFSET 0x0260 +#define CUR_HORZ_VERT_POSN 0x0264 +#define CUR_HORZ_VERT_OFF 0x0268 +#define CUR_CLR0 0x026c +#define CUR_CLR1 0x0270 +#define LVDS_GEN_CNTL 0x02d0 +#define DDA_CONFIG 0x02e0 +#define DDA_ON_OFF 0x02e4 +#define VGA_DDA_CONFIG 0x02e8 +#define VGA_DDA_ON_OFF 0x02ec +#define CRTC2_H_TOTAL_DISP 0x0300 +#define CRTC2_H_SYNC_STRT_WID 0x0304 +#define CRTC2_V_TOTAL_DISP 0x0308 +#define CRTC2_V_SYNC_STRT_WID 0x030c +#define CRTC2_VLINE_CRNT_VLINE 0x0310 +#define CRTC2_CRNT_FRAME 0x0314 +#define CRTC2_GUI_TRIG_VLINE 0x0318 +#define CRTC2_OFFSET 0x0324 +#define CRTC2_OFFSET_CNTL 0x0328 +#define CRTC2_PITCH 0x032c +#define DDA2_CONFIG 0x03e0 +#define DDA2_ON_OFF 0x03e4 +#define CRTC2_GEN_CNTL 0x03f8 +#define CRTC2_STATUS 0x03fc +#define OV0_SCALE_CNTL 0x0420 +#define SUBPIC_CNTL 0x0540 +#define PM4_BUFFER_OFFSET 0x0700 +#define PM4_BUFFER_CNTL 0x0704 +#define PM4_BUFFER_WM_CNTL 0x0708 +#define PM4_BUFFER_DL_RPTR_ADDR 0x070c +#define PM4_BUFFER_DL_RPTR 0x0710 +#define PM4_BUFFER_DL_WPTR 0x0714 +#define PM4_VC_FPU_SETUP 0x071c +#define PM4_FPU_CNTL 0x0720 +#define PM4_VC_FORMAT 0x0724 +#define PM4_VC_CNTL 0x0728 +#define PM4_VC_I01 0x072c +#define PM4_VC_VLOFF 0x0730 +#define PM4_VC_VLSIZE 0x0734 +#define PM4_IW_INDOFF 0x0738 +#define PM4_IW_INDSIZE 0x073c +#define PM4_FPU_FPX0 0x0740 +#define PM4_FPU_FPY0 0x0744 +#define PM4_FPU_FPX1 0x0748 +#define PM4_FPU_FPY1 0x074c +#define PM4_FPU_FPX2 0x0750 +#define PM4_FPU_FPY2 0x0754 +#define PM4_FPU_FPY3 0x0758 +#define PM4_FPU_FPY4 0x075c +#define PM4_FPU_FPY5 0x0760 +#define PM4_FPU_FPY6 0x0764 +#define PM4_FPU_FPR 0x0768 +#define PM4_FPU_FPG 0x076c +#define PM4_FPU_FPB 0x0770 +#define PM4_FPU_FPA 0x0774 +#define PM4_FPU_INTXY0 0x0780 +#define PM4_FPU_INTXY1 0x0784 +#define PM4_FPU_INTXY2 0x0788 +#define PM4_FPU_INTARGB 0x078c +#define PM4_FPU_FPTWICEAREA 0x0790 +#define PM4_FPU_DMAJOR01 0x0794 +#define PM4_FPU_DMAJOR12 0x0798 +#define PM4_FPU_DMAJOR02 0x079c +#define PM4_FPU_STAT 0x07a0 +#define PM4_STAT 0x07b8 +#define PM4_TEST_CNTL 0x07d0 +#define PM4_MICROCODE_ADDR 0x07d4 +#define PM4_MICROCODE_RADDR 0x07d8 +#define PM4_MICROCODE_DATAH 0x07dc +#define PM4_MICROCODE_DATAL 0x07e0 +#define PM4_CMDFIFO_ADDR 0x07e4 +#define PM4_CMDFIFO_DATAH 0x07e8 +#define PM4_CMDFIFO_DATAL 0x07ec +#define PM4_BUFFER_ADDR 0x07f0 +#define PM4_BUFFER_DATAH 0x07f4 +#define PM4_BUFFER_DATAL 0x07f8 +#define PM4_MICRO_CNTL 0x07fc +#define CAP0_TRIG_CNTL 0x0950 +#define CAP1_TRIG_CNTL 0x09c0 + +#define RBBM_STATUS 0x0e40 + +/* + * GUI Block Memory Mapped Registers + * These registers are FIFOed. + */ +#define PM4_FIFO_DATA_EVEN 0x1000 +#define PM4_FIFO_DATA_ODD 0x1004 + +#define DST_OFFSET 0x1404 +#define DST_PITCH 0x1408 +#define DST_WIDTH 0x140c +#define DST_HEIGHT 0x1410 +#define SRC_X 0x1414 +#define SRC_Y 0x1418 +#define DST_X 0x141c +#define DST_Y 0x1420 +#define SRC_PITCH_OFFSET 0x1428 +#define DST_PITCH_OFFSET 0x142c +#define SRC_Y_X 0x1434 +#define DST_Y_X 0x1438 +#define DST_HEIGHT_WIDTH 0x143c +#define DP_GUI_MASTER_CNTL 0x146c +#define BRUSH_SCALE 0x1470 +#define BRUSH_Y_X 0x1474 +#define DP_BRUSH_BKGD_CLR 0x1478 +#define DP_BRUSH_FRGD_CLR 0x147c +#define DST_WIDTH_X 0x1588 +#define DST_HEIGHT_WIDTH_8 0x158c +#define SRC_X_Y 0x1590 +#define DST_X_Y 0x1594 +#define DST_WIDTH_HEIGHT 0x1598 +#define DST_WIDTH_X_INCY 0x159c +#define DST_HEIGHT_Y 0x15a0 +#define DST_X_SUB 0x15a4 +#define DST_Y_SUB 0x15a8 +#define SRC_OFFSET 0x15ac +#define SRC_PITCH 0x15b0 +#define DST_HEIGHT_WIDTH_BW 0x15b4 +#define CLR_CMP_CNTL 0x15c0 +#define CLR_CMP_CLR_SRC 0x15c4 +#define CLR_CMP_CLR_DST 0x15c8 +#define CLR_CMP_MASK 0x15cc +#define DP_SRC_FRGD_CLR 0x15d8 +#define DP_SRC_BKGD_CLR 0x15dc +#define DST_BRES_ERR 0x1628 +#define DST_BRES_INC 0x162c +#define DST_BRES_DEC 0x1630 +#define DST_BRES_LNTH 0x1634 +#define DST_BRES_LNTH_SUB 0x1638 +#define SC_LEFT 0x1640 +#define SC_RIGHT 0x1644 +#define SC_TOP 0x1648 +#define SC_BOTTOM 0x164c +#define SRC_SC_RIGHT 0x1654 +#define SRC_SC_BOTTOM 0x165c +#define GUI_DEBUG0 0x16a0 +#define GUI_DEBUG1 0x16a4 +#define GUI_TIMEOUT 0x16b0 +#define GUI_TIMEOUT0 0x16b4 +#define GUI_TIMEOUT1 0x16b8 +#define GUI_PROBE 0x16bc +#define DP_CNTL 0x16c0 +#define DP_DATATYPE 0x16c4 +#define DP_MIX 0x16c8 +#define DP_WRITE_MASK 0x16cc +#define DP_CNTL_XDIR_YDIR_YMAJOR 0x16d0 +#define DEFAULT_OFFSET 0x16e0 +#define DEFAULT_PITCH 0x16e4 +#define DEFAULT_SC_BOTTOM_RIGHT 0x16e8 +#define SC_TOP_LEFT 0x16ec +#define SC_BOTTOM_RIGHT 0x16f0 +#define SRC_SC_BOTTOM_RIGHT 0x16f4 +#define DST_TILE 0x1700 +#define WAIT_UNTIL 0x1720 +#define CACHE_CNTL 0x1724 +#define GUI_STAT 0x1740 +#define PC_GUI_MODE 0x1744 +#define PC_GUI_CTLSTAT 0x1748 +#define PC_DEBUG_MODE 0x1760 +#define BRES_DST_ERR_DEC 0x1780 +#define TRAIL_BRES_T12_ERR_DEC 0x1784 +#define TRAIL_BRES_T12_INC 0x1788 +#define DP_T12_CNTL 0x178c +#define DST_BRES_T1_LNTH 0x1790 +#define DST_BRES_T2_LNTH 0x1794 +#define SCALE_SRC_HEIGHT_WIDTH 0x1994 +#define SCALE_OFFSET_0 0x1998 +#define SCALE_PITCH 0x199c +#define SCALE_X_INC 0x19a0 +#define SCALE_Y_INC 0x19a4 +#define SCALE_HACC 0x19a8 +#define SCALE_VACC 0x19ac +#define SCALE_DST_X_Y 0x19b0 +#define SCALE_DST_HEIGHT_WIDTH 0x19b4 +#define SCALE_3D_CNTL 0x1a00 +#define SCALE_3D_DATATYPE 0x1a20 +#define SETUP_CNTL 0x1bc4 +#define SOLID_COLOR 0x1bc8 +#define WINDOW_XY_OFFSET 0x1bcc +#define DRAW_LINE_POINT 0x1bd0 +#define SETUP_CNTL_PM4 0x1bd4 +#define DST_PITCH_OFFSET_C 0x1c80 +#define DP_GUI_MASTER_CNTL_C 0x1c84 +#define SC_TOP_LEFT_C 0x1c88 +#define SC_BOTTOM_RIGHT_C 0x1c8c + +#define CLR_CMP_MASK_3D 0x1A28 +#define MISC_3D_STATE_CNTL_REG 0x1CA0 +#define MC_SRC1_CNTL 0x19D8 +#define TEX_CNTL 0x1800 + +/* CONSTANTS */ +#define GUI_ACTIVE 0x80000000 +#define ENGINE_IDLE 0x0 + +#define PLL_WR_EN 0x00000080 + +#define CLK_PIN_CNTL 0x01 +#define PPLL_CNTL 0x02 +#define PPLL_REF_DIV 0x03 +#define PPLL_DIV_0 0x04 +#define PPLL_DIV_1 0x05 +#define PPLL_DIV_2 0x06 +#define PPLL_DIV_3 0x07 +#define VCLK_ECP_CNTL 0x08 +#define HTOTAL_CNTL 0x09 +#define X_MPLL_REF_FB_DIV 0x0a +#define XPLL_CNTL 0x0b +#define XDLL_CNTL 0x0c +#define XCLK_CNTL 0x0d +#define MPLL_CNTL 0x0e +#define MCLK_CNTL 0x0f +#define AGP_PLL_CNTL 0x10 +#define FCP_CNTL 0x12 +#define PLL_TEST_CNTL 0x13 +#define P2PLL_CNTL 0x2a +#define P2PLL_REF_DIV 0x2b +#define P2PLL_DIV_0 0x2b +#define POWER_MANAGEMENT 0x2f + +#define PPLL_RESET 0x00000001 +#define PPLL_ATOMIC_UPDATE_EN 0x00010000 +#define PPLL_VGA_ATOMIC_UPDATE_EN 0x00020000 +#define PPLL_REF_DIV_MASK 0x000003FF +#define PPLL_FB3_DIV_MASK 0x000007FF +#define PPLL_POST3_DIV_MASK 0x00070000 +#define PPLL_ATOMIC_UPDATE_R 0x00008000 +#define PPLL_ATOMIC_UPDATE_W 0x00008000 +#define MEM_CFG_TYPE_MASK 0x00000003 +#define XCLK_SRC_SEL_MASK 0x00000007 +#define XPLL_FB_DIV_MASK 0x0000FF00 +#define X_MPLL_REF_DIV_MASK 0x000000FF + +/* Config control values (CONFIG_CNTL) */ +#define CFG_VGA_IO_DIS 0x00000400 + +/* CRTC control values (CRTC_GEN_CNTL) */ +#define CRTC_CSYNC_EN 0x00000010 + +#define CRTC2_DBL_SCAN_EN 0x00000001 +#define CRTC2_DISPLAY_DIS 0x00800000 +#define CRTC2_FIFO_EXTSENSE 0x00200000 +#define CRTC2_ICON_EN 0x00100000 +#define CRTC2_CUR_EN 0x00010000 +#define CRTC2_EXT_DISP_EN 0x01000000 +#define CRTC2_EN 0x02000000 +#define CRTC2_DISP_REQ_EN_B 0x04000000 + +#define CRTC_PIX_WIDTH_MASK 0x00000700 +#define CRTC_PIX_WIDTH_4BPP 0x00000100 +#define CRTC_PIX_WIDTH_8BPP 0x00000200 +#define CRTC_PIX_WIDTH_15BPP 0x00000300 +#define CRTC_PIX_WIDTH_16BPP 0x00000400 +#define CRTC_PIX_WIDTH_24BPP 0x00000500 +#define CRTC_PIX_WIDTH_32BPP 0x00000600 + +/* DAC_CNTL bit constants */ +#define DAC_8BIT_EN 0x00000100 +#define DAC_MASK 0xFF000000 +#define DAC_BLANKING 0x00000004 +#define DAC_RANGE_CNTL 0x00000003 +#define DAC_CLK_SEL 0x00000010 +#define DAC_PALETTE_ACCESS_CNTL 0x00000020 +#define DAC_PALETTE2_SNOOP_EN 0x00000040 +#define DAC_PDWN 0x00008000 + +/* CRTC_EXT_CNTL */ +#define CRT_CRTC_DISPLAY_DIS 0x00000400 +#define CRT_CRTC_ON 0x00008000 + +/* GEN_RESET_CNTL bit constants */ +#define SOFT_RESET_GUI 0x00000001 +#define SOFT_RESET_VCLK 0x00000100 +#define SOFT_RESET_PCLK 0x00000200 +#define SOFT_RESET_ECP 0x00000400 +#define SOFT_RESET_DISPENG_XCLK 0x00000800 + +/* PC_GUI_CTLSTAT bit constants */ +#define PC_BUSY_INIT 0x10000000 +#define PC_BUSY_GUI 0x20000000 +#define PC_BUSY_NGUI 0x40000000 +#define PC_BUSY 0x80000000 + +#define BUS_MASTER_DIS 0x00000040 +#define PM4_BUFFER_CNTL_NONPM4 0x00000000 + +/* DP_DATATYPE bit constants */ +#define DST_8BPP 0x00000002 +#define DST_15BPP 0x00000003 +#define DST_16BPP 0x00000004 +#define DST_24BPP 0x00000005 +#define DST_32BPP 0x00000006 + +#define BRUSH_SOLIDCOLOR 0x00000d00 + +/* DP_GUI_MASTER_CNTL bit constants */ +#define GMC_SRC_PITCH_OFFSET_DEFAULT 0x00000000 +#define GMC_DST_PITCH_OFFSET_DEFAULT 0x00000000 +#define GMC_SRC_CLIP_DEFAULT 0x00000000 +#define GMC_DST_CLIP_DEFAULT 0x00000000 +#define GMC_BRUSH_SOLIDCOLOR 0x000000d0 +#define GMC_SRC_DSTCOLOR 0x00003000 +#define GMC_BYTE_ORDER_MSB_TO_LSB 0x00000000 +#define GMC_DP_SRC_RECT 0x02000000 +#define GMC_3D_FCN_EN_CLR 0x00000000 +#define GMC_AUX_CLIP_CLEAR 0x20000000 +#define GMC_DST_CLR_CMP_FCN_CLEAR 0x10000000 +#define GMC_WRITE_MASK_SET 0x40000000 +#define GMC_DP_CONVERSION_TEMP_6500 0x00000000 + +/* DP_GUI_MASTER_CNTL ROP3 named constants */ +#define GMC_ROP3_MASK 0x00ff0000 +#define ROP3_BLACKNESS 0x00000000 +#define ROP3_SRCCOPY 0x00cc0000 +#define ROP3_PATCOPY 0x00f00000 +#define ROP3_WHITENESS 0x00ff0000 + +#define SRC_DSTCOLOR 0x00030000 + +/* DP_CNTL bit constants */ +#define DST_X_RIGHT_TO_LEFT 0x00000000 +#define DST_X_LEFT_TO_RIGHT 0x00000001 +#define DST_Y_BOTTOM_TO_TOP 0x00000000 +#define DST_Y_TOP_TO_BOTTOM 0x00000002 +#define DST_X_MAJOR 0x00000000 +#define DST_Y_MAJOR 0x00000004 +#define DST_X_TILE 0x00000008 +#define DST_Y_TILE 0x00000010 +#define DST_LAST_PEL 0x00000020 +#define DST_TRAIL_X_RIGHT_TO_LEFT 0x00000000 +#define DST_TRAIL_X_LEFT_TO_RIGHT 0x00000040 +#define DST_TRAP_FILL_RIGHT_TO_LEFT 0x00000000 +#define DST_TRAP_FILL_LEFT_TO_RIGHT 0x00000080 +#define DST_BRES_SIGN 0x00000100 +#define DST_HOST_BIG_ENDIAN_EN 0x00000200 +#define DST_POLYLINE_NONLAST 0x00008000 +#define DST_RASTER_STALL 0x00010000 +#define DST_POLY_EDGE 0x00040000 + +/* DP_MIX bit constants */ +#define DP_SRC_RECT 0x00000200 +#define DP_SRC_HOST 0x00000300 +#define DP_SRC_HOST_BYTEALIGN 0x00000400 + +/* LVDS_GEN_CNTL constants */ +#define LVDS_BL_MOD_LEVEL_MASK 0x0000ff00 +#define LVDS_BL_MOD_LEVEL_SHIFT 8 +#define LVDS_BL_MOD_EN 0x00010000 +#define LVDS_DIGION 0x00040000 +#define LVDS_BLON 0x00080000 +#define LVDS_ON 0x00000001 +#define LVDS_DISPLAY_DIS 0x00000002 +#define LVDS_PANEL_TYPE_2PIX_PER_CLK 0x00000004 +#define LVDS_PANEL_24BITS_TFT 0x00000008 +#define LVDS_FRAME_MOD_NO 0x00000000 +#define LVDS_FRAME_MOD_2_LEVELS 0x00000010 +#define LVDS_FRAME_MOD_4_LEVELS 0x00000020 +#define LVDS_RST_FM 0x00000040 +#define LVDS_EN 0x00000080 + +/* CRTC2_GEN_CNTL constants */ +#define CRTC2_EN 0x02000000 + +/* POWER_MANAGEMENT constants */ +#define PWR_MGT_ON 0x00000001 +#define PWR_MGT_MODE_MASK 0x00000006 +#define PWR_MGT_MODE_PIN 0x00000000 +#define PWR_MGT_MODE_REGISTER 0x00000002 +#define PWR_MGT_MODE_TIMER 0x00000004 +#define PWR_MGT_MODE_PCI 0x00000006 +#define PWR_MGT_AUTO_PWR_UP_EN 0x00000008 +#define PWR_MGT_ACTIVITY_PIN_ON 0x00000010 +#define PWR_MGT_STANDBY_POL 0x00000020 +#define PWR_MGT_SUSPEND_POL 0x00000040 +#define PWR_MGT_SELF_REFRESH 0x00000080 +#define PWR_MGT_ACTIVITY_PIN_EN 0x00000100 +#define PWR_MGT_KEYBD_SNOOP 0x00000200 +#define PWR_MGT_TRISTATE_MEM_EN 0x00000800 +#define PWR_MGT_SELW4MS 0x00001000 +#define PWR_MGT_SLOWDOWN_MCLK 0x00002000 + +#define PMI_PMSCR_REG 0x60 + +/* used by ATI bug fix for hardware ROM */ +#define RAGE128_MPP_TB_CONFIG 0x01c0 + +#endif /* ATI_REGS_H */ diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index 3355f4c131..599863e4e1 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -34,6 +34,13 @@ #define DEFAULT_VCRAM_SIZE 0x4000000 #define BCM2835_FB_OFFSET 0x00100000 +/* Maximum permitted framebuffer size; experimentally determined on an rpi2 */ +#define XRES_MAX 3840 +#define YRES_MAX 2560 +/* Framebuffer size used if guest requests zero size */ +#define XRES_SMALL 592 +#define YRES_SMALL 488 + static void fb_invalidate_display(void *opaque) { BCM2835FBState *s = BCM2835_FB(opaque); @@ -52,7 +59,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, int bpp = surface_bits_per_pixel(surface); while (width--) { - switch (s->bpp) { + switch (s->config.bpp) { case 8: /* lookup palette starting at video ram base * TODO: cache translation, rather than doing this each time! @@ -91,7 +98,7 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, break; } - if (s->pixo == 0) { + if (s->config.pixo == 0) { /* swap to BGR pixel format */ uint8_t tmp = r; r = b; @@ -126,6 +133,18 @@ static void draw_line_src16(void *opaque, uint8_t *dst, const uint8_t *src, } } +static bool fb_use_offsets(BCM2835FBConfig *config) +{ + /* + * Return true if we should use the viewport offsets. + * Experimentally, the hardware seems to do this only if the + * viewport size is larger than the physical screen. (It doesn't + * prevent the guest setting this silly viewport setting, though...) + */ + return config->xres_virtual > config->xres && + config->yres_virtual > config->yres; +} + static void fb_update_display(void *opaque) { BCM2835FBState *s = opaque; @@ -134,13 +153,19 @@ static void fb_update_display(void *opaque) int last = 0; int src_width = 0; int dest_width = 0; + uint32_t xoff = 0, yoff = 0; - if (s->lock || !s->xres) { + if (s->lock || !s->config.xres) { return; } - src_width = s->xres * (s->bpp >> 3); - dest_width = s->xres; + src_width = bcm2835_fb_get_pitch(&s->config); + if (fb_use_offsets(&s->config)) { + xoff = s->config.xoffset; + yoff = s->config.yoffset; + } + + dest_width = s->config.xres; switch (surface_bits_per_pixel(surface)) { case 0: @@ -165,89 +190,104 @@ static void fb_update_display(void *opaque) } if (s->invalidate) { - framebuffer_update_memory_section(&s->fbsection, s->dma_mr, s->base, - s->yres, src_width); + hwaddr base = s->config.base + xoff + (hwaddr)yoff * src_width; + framebuffer_update_memory_section(&s->fbsection, s->dma_mr, + base, + s->config.yres, src_width); } - framebuffer_update_display(surface, &s->fbsection, s->xres, s->yres, + framebuffer_update_display(surface, &s->fbsection, + s->config.xres, s->config.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); + dpy_gfx_update(s->con, 0, first, s->config.xres, + last - first + 1); } s->invalidate = false; } -static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) +void bcm2835_fb_validate_config(BCM2835FBConfig *config) { - value &= ~0xf; + /* + * Validate the config, and clip any bogus values into range, + * as the hardware does. Note that fb_update_display() relies on + * this happening to prevent it from performing out-of-range + * accesses on redraw. + */ + config->xres = MIN(config->xres, XRES_MAX); + config->xres_virtual = MIN(config->xres_virtual, XRES_MAX); + config->yres = MIN(config->yres, YRES_MAX); + config->yres_virtual = MIN(config->yres_virtual, YRES_MAX); + /* + * These are not minima: a 40x40 framebuffer will be accepted. + * They're only used as defaults if the guest asks for zero size. + */ + if (config->xres == 0) { + config->xres = XRES_SMALL; + } + if (config->yres == 0) { + config->yres = YRES_SMALL; + } + if (config->xres_virtual == 0) { + config->xres_virtual = config->xres; + } + if (config->yres_virtual == 0) { + config->yres_virtual = config->yres; + } + + if (fb_use_offsets(config)) { + /* Clip the offsets so the viewport is within the physical screen */ + config->xoffset = MIN(config->xoffset, + config->xres_virtual - config->xres); + config->yoffset = MIN(config->yoffset, + config->yres_virtual - config->yres); + } +} + +void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig) +{ 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->config = *newconfig; s->invalidate = true; - qemu_console_resize(s->con, s->xres, s->yres); + qemu_console_resize(s->con, s->config.xres, s->config.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) +static void bcm2835_fb_mbox_push(BCM2835FBState *s, uint32_t value) { - s->lock = true; + uint32_t pitch; + uint32_t size; + BCM2835FBConfig newconf; - /* 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; - } + value &= ~0xf; - /* TODO - Manage properly virtual resolution */ + newconf.xres = ldl_le_phys(&s->dma_as, value); + newconf.yres = ldl_le_phys(&s->dma_as, value + 4); + newconf.xres_virtual = ldl_le_phys(&s->dma_as, value + 8); + newconf.yres_virtual = ldl_le_phys(&s->dma_as, value + 12); + newconf.bpp = ldl_le_phys(&s->dma_as, value + 20); + newconf.xoffset = ldl_le_phys(&s->dma_as, value + 24); + newconf.yoffset = ldl_le_phys(&s->dma_as, value + 28); - s->pitch = s->xres * (s->bpp >> 3); - s->size = s->yres * s->pitch; + newconf.base = s->vcram_base | (value & 0xc0000000); + newconf.base += BCM2835_FB_OFFSET; - s->invalidate = true; - qemu_console_resize(s->con, s->xres, s->yres); - s->lock = false; + bcm2835_fb_validate_config(&newconf); + + pitch = bcm2835_fb_get_pitch(&newconf); + size = bcm2835_fb_get_size(&newconf); + + stl_le_phys(&s->dma_as, value + 16, pitch); + stl_le_phys(&s->dma_as, value + 32, newconf.base); + stl_le_phys(&s->dma_as, value + 36, size); + + bcm2835_fb_reconfigure(s, &newconf); } static uint64_t bcm2835_fb_read(void *opaque, hwaddr offset, unsigned size) @@ -312,18 +352,17 @@ static const VMStateDescription vmstate_bcm2835_fb = { 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_UINT32(config.xres, BCM2835FBState), + VMSTATE_UINT32(config.yres, BCM2835FBState), + VMSTATE_UINT32(config.xres_virtual, BCM2835FBState), + VMSTATE_UINT32(config.yres_virtual, BCM2835FBState), + VMSTATE_UINT32(config.xoffset, BCM2835FBState), + VMSTATE_UINT32(config.yoffset, BCM2835FBState), + VMSTATE_UINT32(config.bpp, BCM2835FBState), + VMSTATE_UINT32(config.base, BCM2835FBState), + VMSTATE_UNUSED(8), /* Was pitch and size */ + VMSTATE_UINT32(config.pixo, BCM2835FBState), + VMSTATE_UINT32(config.alpha, BCM2835FBState), VMSTATE_END_OF_LIST() } }; @@ -349,13 +388,7 @@ static void bcm2835_fb_reset(DeviceState *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->config = s->initial_config; s->invalidate = true; s->lock = false; @@ -379,24 +412,33 @@ static void bcm2835_fb_realize(DeviceState *dev, Error **errp) return; } + /* Fill in the parts of initial_config that are not set by QOM properties */ + s->initial_config.xres_virtual = s->initial_config.xres; + s->initial_config.yres_virtual = s->initial_config.yres; + s->initial_config.xoffset = 0; + s->initial_config.yoffset = 0; + s->initial_config.base = s->vcram_base + BCM2835_FB_OFFSET; + 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); + qemu_console_resize(s->con, s->config.xres, s->config.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_UINT32("xres", BCM2835FBState, initial_config.xres, 640), + DEFINE_PROP_UINT32("yres", BCM2835FBState, initial_config.yres, 480), + DEFINE_PROP_UINT32("bpp", BCM2835FBState, initial_config.bpp, 16), + DEFINE_PROP_UINT32("pixo", BCM2835FBState, + initial_config.pixo, 1), /* 1=RGB, 0=BGR */ + DEFINE_PROP_UINT32("alpha", BCM2835FBState, + initial_config.alpha, 2), /* alpha ignored */ DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 09d8944a1b..3d439eb240 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -9,6 +9,7 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/display/bochs-vbe.h" +#include "hw/display/edid.h" #include "qapi/error.h" @@ -35,9 +36,13 @@ typedef struct BochsDisplayState { MemoryRegion mmio; MemoryRegion vbe; MemoryRegion qext; + MemoryRegion edid; /* device config */ uint64_t vgamem; + bool enable_edid; + qemu_edid_info edid_info; + uint8_t edid_blob[256]; /* device registers */ uint16_t vbe_regs[VBE_DISPI_INDEX_NB]; @@ -283,6 +288,12 @@ static void bochs_display_realize(PCIDevice *dev, Error **errp) pci_register_bar(&s->pci, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&s->pci, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio); + if (s->enable_edid) { + qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info); + qemu_edid_region_io(&s->edid, obj, s->edid_blob, sizeof(s->edid_blob)); + memory_region_add_subregion(&s->mmio, 0, &s->edid); + } + if (pci_bus_is_express(pci_get_bus(dev))) { dev->cap_present |= QEMU_PCI_CAP_EXPRESS; ret = pcie_endpoint_cap_init(dev, 0x80); @@ -325,6 +336,8 @@ static void bochs_display_exit(PCIDevice *dev) static Property bochs_display_properties[] = { DEFINE_PROP_SIZE("vgamem", BochsDisplayState, vgamem, 16 * MiB), + DEFINE_PROP_BOOL("edid", BochsDisplayState, enable_edid, false), + DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 6fff4852c5..e50d97e48c 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -232,6 +232,7 @@ static void cg3_reg_write(void *opaque, hwaddr addr, uint64_t val, s->b[s->dac_index] = regval; /* Index autoincrement */ s->dac_index = (s->dac_index + 1) & 0xff; + /* fall through */ default: s->dac_state = 0; break; @@ -306,7 +307,7 @@ static void cg3_realizefn(DeviceState *dev, Error **errp) ret = load_image_mr(fcode_filename, &s->rom); g_free(fcode_filename); if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { - error_report("cg3: could not load prom '%s'", CG3_ROM_FILE); + warn_report("cg3: could not load prom '%s'", CG3_ROM_FILE); } } diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 7583b18c29..a0e71469f4 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -33,8 +33,8 @@ #include "hw/hw.h" #include "hw/pci/pci.h" #include "ui/pixel_ops.h" -#include "vga_int.h" #include "hw/loader.h" +#include "cirrus_vga_internal.h" /* * TODO: @@ -52,16 +52,6 @@ * ***************************************/ -// ID -#define CIRRUS_ID_CLGD5422 (0x23<<2) -#define CIRRUS_ID_CLGD5426 (0x24<<2) -#define CIRRUS_ID_CLGD5424 (0x25<<2) -#define CIRRUS_ID_CLGD5428 (0x26<<2) -#define CIRRUS_ID_CLGD5430 (0x28<<2) -#define CIRRUS_ID_CLGD5434 (0x2A<<2) -#define CIRRUS_ID_CLGD5436 (0x2B<<2) -#define CIRRUS_ID_CLGD5446 (0x2E<<2) - // sequencer 0x07 #define CIRRUS_SR7_BPP_VGA 0x00 #define CIRRUS_SR7_BPP_SVGA 0x01 @@ -176,65 +166,10 @@ #define CIRRUS_PNPMMIO_SIZE 0x1000 -struct CirrusVGAState; -typedef void (*cirrus_bitblt_rop_t) (struct CirrusVGAState *s, - uint32_t dstaddr, uint32_t srcaddr, - int dstpitch, int srcpitch, - int bltwidth, int bltheight); typedef void (*cirrus_fill_t)(struct CirrusVGAState *s, uint32_t dstaddr, int dst_pitch, int width, int height); -typedef struct CirrusVGAState { - VGACommonState vga; - - MemoryRegion cirrus_vga_io; - MemoryRegion cirrus_linear_io; - MemoryRegion cirrus_linear_bitblt_io; - MemoryRegion cirrus_mmio_io; - MemoryRegion pci_bar; - bool linear_vram; /* vga.vram mapped over cirrus_linear_io */ - MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */ - MemoryRegion low_mem; /* always mapped, overridden by: */ - MemoryRegion cirrus_bank[2]; /* aliases at 0xa0000-0xb0000 */ - uint32_t cirrus_addr_mask; - uint32_t linear_mmio_mask; - uint8_t cirrus_shadow_gr0; - uint8_t cirrus_shadow_gr1; - uint8_t cirrus_hidden_dac_lockindex; - uint8_t cirrus_hidden_dac_data; - uint32_t cirrus_bank_base[2]; - uint32_t cirrus_bank_limit[2]; - uint8_t cirrus_hidden_palette[48]; - bool enable_blitter; - int cirrus_blt_pixelwidth; - int cirrus_blt_width; - int cirrus_blt_height; - int cirrus_blt_dstpitch; - int cirrus_blt_srcpitch; - uint32_t cirrus_blt_fgcol; - uint32_t cirrus_blt_bgcol; - uint32_t cirrus_blt_dstaddr; - uint32_t cirrus_blt_srcaddr; - uint8_t cirrus_blt_mode; - uint8_t cirrus_blt_modeext; - cirrus_bitblt_rop_t cirrus_rop; -#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */ - uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE]; - uint8_t *cirrus_srcptr; - uint8_t *cirrus_srcptr_end; - uint32_t cirrus_srccounter; - /* hwcursor display state */ - int last_hw_cursor_size; - int last_hw_cursor_x; - int last_hw_cursor_y; - int last_hw_cursor_y_start; - int last_hw_cursor_y_end; - int real_vram_size; /* XXX: suppress that */ - int device_id; - int bustype; -} CirrusVGAState; - typedef struct PCICirrusVGAState { PCIDevice dev; CirrusVGAState cirrus_vga; @@ -244,16 +179,6 @@ typedef struct PCICirrusVGAState { #define PCI_CIRRUS_VGA(obj) \ OBJECT_CHECK(PCICirrusVGAState, (obj), TYPE_PCI_CIRRUS_VGA) -#define TYPE_ISA_CIRRUS_VGA "isa-cirrus-vga" -#define ISA_CIRRUS_VGA(obj) \ - OBJECT_CHECK(ISACirrusVGAState, (obj), TYPE_ISA_CIRRUS_VGA) - -typedef struct ISACirrusVGAState { - ISADevice parent_obj; - - CirrusVGAState cirrus_vga; -} ISACirrusVGAState; - static uint8_t rop_to_index[256]; /*************************************** @@ -1426,7 +1351,8 @@ static void cirrus_vga_write_sr(CirrusVGAState * s, uint32_t val) s->vga.hw_cursor_y = (val << 3) | (s->vga.sr_index >> 5); break; case 0x07: // Extended Sequencer Mode - cirrus_update_memory_access(s); + cirrus_update_memory_access(s); + /* fall through */ case 0x08: // EEPROM Control case 0x09: // Scratch Register 0 case 0x0a: // Scratch Register 1 @@ -2820,15 +2746,16 @@ static int cirrus_post_load(void *opaque, int version_id) s->vga.gr[0x00] = s->cirrus_shadow_gr0 & 0x0f; s->vga.gr[0x01] = s->cirrus_shadow_gr1 & 0x0f; + cirrus_update_bank_ptr(s, 0); + cirrus_update_bank_ptr(s, 1); cirrus_update_memory_access(s); /* force refresh */ s->vga.graphic_mode = -1; - cirrus_update_bank_ptr(s, 0); - cirrus_update_bank_ptr(s, 1); + return 0; } -static const VMStateDescription vmstate_cirrus_vga = { +const VMStateDescription vmstate_cirrus_vga = { .name = "cirrus_vga", .version_id = 2, .minimum_version_id = 1, @@ -2931,10 +2858,9 @@ static const MemoryRegionOps cirrus_vga_io_ops = { }, }; -static void cirrus_init_common(CirrusVGAState *s, Object *owner, - int device_id, int is_pci, - MemoryRegion *system_memory, - MemoryRegion *system_io) +void cirrus_init_common(CirrusVGAState *s, Object *owner, + int device_id, int is_pci, + MemoryRegion *system_memory, MemoryRegion *system_io) { int i; static int inited; @@ -3028,62 +2954,6 @@ static void cirrus_init_common(CirrusVGAState *s, Object *owner, qemu_register_reset(cirrus_reset, s); } -/*************************************** - * - * ISA bus support - * - ***************************************/ - -static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) -{ - ISADevice *isadev = ISA_DEVICE(dev); - ISACirrusVGAState *d = ISA_CIRRUS_VGA(dev); - VGACommonState *s = &d->cirrus_vga.vga; - - /* follow real hardware, cirrus card emulated has 4 MB video memory. - Also accept 8 MB/16 MB for backward compatibility. */ - if (s->vram_size_mb != 4 && s->vram_size_mb != 8 && - s->vram_size_mb != 16) { - error_setg(errp, "Invalid cirrus_vga ram size '%u'", - s->vram_size_mb); - return; - } - s->global_vmstate = true; - vga_common_init(s, OBJECT(dev)); - cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, - isa_address_space(isadev), - isa_address_space_io(isadev)); - s->con = graphic_console_init(dev, 0, s->hw_ops, s); - rom_add_vga(VGABIOS_CIRRUS_FILENAME); - /* XXX ISA-LFB support */ - /* FIXME not qdev yet */ -} - -static Property isa_cirrus_vga_properties[] = { - DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, - cirrus_vga.vga.vram_size_mb, 4), - DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, - cirrus_vga.enable_blitter, true), - DEFINE_PROP_END_OF_LIST(), -}; - -static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->vmsd = &vmstate_cirrus_vga; - dc->realize = isa_cirrus_vga_realizefn; - dc->props = isa_cirrus_vga_properties; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); -} - -static const TypeInfo isa_cirrus_vga_info = { - .name = TYPE_ISA_CIRRUS_VGA, - .parent = TYPE_ISA_DEVICE, - .instance_size = sizeof(ISACirrusVGAState), - .class_init = isa_cirrus_vga_class_init, -}; - /*************************************** * * PCI bus support @@ -3170,7 +3040,6 @@ static const TypeInfo cirrus_vga_info = { static void cirrus_vga_register_types(void) { - type_register_static(&isa_cirrus_vga_info); type_register_static(&cirrus_vga_info); } diff --git a/hw/display/cirrus_vga_internal.h b/hw/display/cirrus_vga_internal.h new file mode 100644 index 0000000000..a78ebbd920 --- /dev/null +++ b/hw/display/cirrus_vga_internal.h @@ -0,0 +1,103 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator, ISA bus support + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * 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 CIRRUS_VGA_INTERNAL_H +#define CIRRUS_VGA_INTERNAL_H + +#include "vga_int.h" + +/* IDs */ +#define CIRRUS_ID_CLGD5422 (0x23 << 2) +#define CIRRUS_ID_CLGD5426 (0x24 << 2) +#define CIRRUS_ID_CLGD5424 (0x25 << 2) +#define CIRRUS_ID_CLGD5428 (0x26 << 2) +#define CIRRUS_ID_CLGD5430 (0x28 << 2) +#define CIRRUS_ID_CLGD5434 (0x2A << 2) +#define CIRRUS_ID_CLGD5436 (0x2B << 2) +#define CIRRUS_ID_CLGD5446 (0x2E << 2) + +extern const VMStateDescription vmstate_cirrus_vga; + +struct CirrusVGAState; +typedef void (*cirrus_bitblt_rop_t)(struct CirrusVGAState *s, + uint32_t dstaddr, uint32_t srcaddr, + int dstpitch, int srcpitch, + int bltwidth, int bltheight); + +typedef struct CirrusVGAState { + VGACommonState vga; + + MemoryRegion cirrus_vga_io; + MemoryRegion cirrus_linear_io; + MemoryRegion cirrus_linear_bitblt_io; + MemoryRegion cirrus_mmio_io; + MemoryRegion pci_bar; + bool linear_vram; /* vga.vram mapped over cirrus_linear_io */ + MemoryRegion low_mem_container; /* container for 0xa0000-0xc0000 */ + MemoryRegion low_mem; /* always mapped, overridden by: */ + MemoryRegion cirrus_bank[2]; /* aliases at 0xa0000-0xb0000 */ + uint32_t cirrus_addr_mask; + uint32_t linear_mmio_mask; + uint8_t cirrus_shadow_gr0; + uint8_t cirrus_shadow_gr1; + uint8_t cirrus_hidden_dac_lockindex; + uint8_t cirrus_hidden_dac_data; + uint32_t cirrus_bank_base[2]; + uint32_t cirrus_bank_limit[2]; + uint8_t cirrus_hidden_palette[48]; + bool enable_blitter; + int cirrus_blt_pixelwidth; + int cirrus_blt_width; + int cirrus_blt_height; + int cirrus_blt_dstpitch; + int cirrus_blt_srcpitch; + uint32_t cirrus_blt_fgcol; + uint32_t cirrus_blt_bgcol; + uint32_t cirrus_blt_dstaddr; + uint32_t cirrus_blt_srcaddr; + uint8_t cirrus_blt_mode; + uint8_t cirrus_blt_modeext; + cirrus_bitblt_rop_t cirrus_rop; +#define CIRRUS_BLTBUFSIZE (2048 * 4) /* one line width */ + uint8_t cirrus_bltbuf[CIRRUS_BLTBUFSIZE]; + uint8_t *cirrus_srcptr; + uint8_t *cirrus_srcptr_end; + uint32_t cirrus_srccounter; + /* hwcursor display state */ + int last_hw_cursor_size; + int last_hw_cursor_x; + int last_hw_cursor_y; + int last_hw_cursor_y_start; + int last_hw_cursor_y_end; + int real_vram_size; /* XXX: suppress that */ + int device_id; + int bustype; +} CirrusVGAState; + +void cirrus_init_common(CirrusVGAState *s, Object *owner, + int device_id, int is_pci, + MemoryRegion *system_memory, MemoryRegion *system_io); + +#endif diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c new file mode 100644 index 0000000000..fa10b74230 --- /dev/null +++ b/hw/display/cirrus_vga_isa.c @@ -0,0 +1,98 @@ +/* + * QEMU Cirrus CLGD 54xx VGA Emulator, ISA bus support + * + * Copyright (c) 2004 Fabrice Bellard + * Copyright (c) 2004 Makoto Suzuki (suzu) + * + * 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/loader.h" +#include "hw/isa/isa.h" +#include "cirrus_vga_internal.h" + +#define TYPE_ISA_CIRRUS_VGA "isa-cirrus-vga" +#define ISA_CIRRUS_VGA(obj) \ + OBJECT_CHECK(ISACirrusVGAState, (obj), TYPE_ISA_CIRRUS_VGA) + +typedef struct ISACirrusVGAState { + ISADevice parent_obj; + + CirrusVGAState cirrus_vga; +} ISACirrusVGAState; + +static void isa_cirrus_vga_realizefn(DeviceState *dev, Error **errp) +{ + ISADevice *isadev = ISA_DEVICE(dev); + ISACirrusVGAState *d = ISA_CIRRUS_VGA(dev); + VGACommonState *s = &d->cirrus_vga.vga; + + /* follow real hardware, cirrus card emulated has 4 MB video memory. + Also accept 8 MB/16 MB for backward compatibility. */ + if (s->vram_size_mb != 4 && s->vram_size_mb != 8 && + s->vram_size_mb != 16) { + error_setg(errp, "Invalid cirrus_vga ram size '%u'", + s->vram_size_mb); + return; + } + s->global_vmstate = true; + vga_common_init(s, OBJECT(dev)); + cirrus_init_common(&d->cirrus_vga, OBJECT(dev), CIRRUS_ID_CLGD5430, 0, + isa_address_space(isadev), + isa_address_space_io(isadev)); + s->con = graphic_console_init(dev, 0, s->hw_ops, s); + rom_add_vga(VGABIOS_CIRRUS_FILENAME); + /* XXX ISA-LFB support */ + /* FIXME not qdev yet */ +} + +static Property isa_cirrus_vga_properties[] = { + DEFINE_PROP_UINT32("vgamem_mb", struct ISACirrusVGAState, + cirrus_vga.vga.vram_size_mb, 4), + DEFINE_PROP_BOOL("blitter", struct ISACirrusVGAState, + cirrus_vga.enable_blitter, true), + DEFINE_PROP_END_OF_LIST(), +}; + +static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_cirrus_vga; + dc->realize = isa_cirrus_vga_realizefn; + dc->props = isa_cirrus_vga_properties; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo isa_cirrus_vga_info = { + .name = TYPE_ISA_CIRRUS_VGA, + .parent = TYPE_ISA_DEVICE, + .instance_size = sizeof(ISACirrusVGAState), + .class_init = isa_cirrus_vga_class_init, +}; + +static void cirrus_vga_isa_register_types(void) +{ + type_register_static(&isa_cirrus_vga_info); +} + +type_init(cirrus_vga_isa_register_types) diff --git a/hw/display/edid-generate.c b/hw/display/edid-generate.c new file mode 100644 index 0000000000..77d9127344 --- /dev/null +++ b/hw/display/edid-generate.c @@ -0,0 +1,439 @@ +/* + * QEMU EDID generator. + * + * 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/bswap.h" +#include "hw/display/edid.h" + +static const struct edid_mode { + uint32_t xres; + uint32_t yres; + uint32_t byte; + uint32_t xtra3; + uint32_t bit; + uint32_t dta; +} modes[] = { + /* dea/dta extension timings (all @ 50 Hz) */ + { .xres = 5120, .yres = 2160, .dta = 125 }, + { .xres = 4096, .yres = 2160, .dta = 101 }, + { .xres = 3840, .yres = 2160, .dta = 96 }, + { .xres = 2560, .yres = 1080, .dta = 89 }, + { .xres = 2048, .yres = 1152 }, + { .xres = 1920, .yres = 1080, .dta = 31 }, + + /* additional standard timings 3 (all @ 60Hz) */ + { .xres = 1920, .yres = 1440, .xtra3 = 11, .bit = 5 }, + { .xres = 1920, .yres = 1200, .xtra3 = 10, .bit = 0 }, + { .xres = 1856, .yres = 1392, .xtra3 = 10, .bit = 3 }, + { .xres = 1792, .yres = 1344, .xtra3 = 10, .bit = 5 }, + { .xres = 1600, .yres = 1200, .xtra3 = 9, .bit = 2 }, + { .xres = 1680, .yres = 1050, .xtra3 = 9, .bit = 5 }, + { .xres = 1440, .yres = 1050, .xtra3 = 8, .bit = 1 }, + { .xres = 1440, .yres = 900, .xtra3 = 8, .bit = 5 }, + { .xres = 1360, .yres = 768, .xtra3 = 8, .bit = 7 }, + { .xres = 1280, .yres = 1024, .xtra3 = 7, .bit = 1 }, + { .xres = 1280, .yres = 960, .xtra3 = 7, .bit = 3 }, + { .xres = 1280, .yres = 768, .xtra3 = 7, .bit = 6 }, + + /* established timings (all @ 60Hz) */ + { .xres = 1024, .yres = 768, .byte = 36, .bit = 3 }, + { .xres = 800, .yres = 600, .byte = 35, .bit = 0 }, + { .xres = 640, .yres = 480, .byte = 35, .bit = 5 }, +}; + +static void edid_ext_dta(uint8_t *dta) +{ + dta[0] = 0x02; + dta[1] = 0x03; + dta[2] = 0x05; + dta[3] = 0x00; + + /* video data block */ + dta[4] = 0x40; +} + +static void edid_ext_dta_mode(uint8_t *dta, uint8_t nr) +{ + dta[dta[2]] = nr; + dta[2]++; + dta[4]++; +} + +static int edid_std_mode(uint8_t *mode, uint32_t xres, uint32_t yres) +{ + uint32_t aspect; + + if (xres == 0 || yres == 0) { + mode[0] = 0x01; + mode[1] = 0x01; + return 0; + + } else if (xres * 10 == yres * 16) { + aspect = 0; + } else if (xres * 3 == yres * 4) { + aspect = 1; + } else if (xres * 4 == yres * 5) { + aspect = 2; + } else if (xres * 9 == yres * 16) { + aspect = 3; + } else { + return -1; + } + + if ((xres / 8) - 31 > 255) { + return -1; + } + + mode[0] = (xres / 8) - 31; + mode[1] = ((aspect << 6) | (60 - 60)); + return 0; +} + +static void edid_fill_modes(uint8_t *edid, uint8_t *xtra3, uint8_t *dta, + uint32_t maxx, uint32_t maxy) +{ + const struct edid_mode *mode; + int std = 38; + int rc, i; + + for (i = 0; i < ARRAY_SIZE(modes); i++) { + mode = modes + i; + + if ((maxx && mode->xres > maxx) || + (maxy && mode->yres > maxy)) { + continue; + } + + if (mode->byte) { + edid[mode->byte] |= (1 << mode->bit); + } else if (mode->xtra3 && xtra3) { + xtra3[mode->xtra3] |= (1 << mode->bit); + } else if (std < 54) { + rc = edid_std_mode(edid + std, mode->xres, mode->yres); + if (rc == 0) { + std += 2; + } + } + + if (dta && mode->dta) { + edid_ext_dta_mode(dta, mode->dta); + } + } + + while (std < 54) { + edid_std_mode(edid + std, 0, 0); + std += 2; + } +} + +static void edid_checksum(uint8_t *edid) +{ + uint32_t sum = 0; + int i; + + for (i = 0; i < 127; i++) { + sum += edid[i]; + } + sum &= 0xff; + if (sum) { + edid[127] = 0x100 - sum; + } +} + +static void edid_desc_type(uint8_t *desc, uint8_t type) +{ + desc[0] = 0; + desc[1] = 0; + desc[2] = 0; + desc[3] = type; + desc[4] = 0; +} + +static void edid_desc_text(uint8_t *desc, uint8_t type, + const char *text) +{ + size_t len; + + edid_desc_type(desc, type); + memset(desc + 5, ' ', 13); + + len = strlen(text); + if (len > 12) { + len = 12; + } + memcpy(desc + 5, text, len); + desc[5 + len] = '\n'; +} + +static void edid_desc_ranges(uint8_t *desc) +{ + edid_desc_type(desc, 0xfd); + + /* vertical (50 -> 125 Hz) */ + desc[5] = 50; + desc[6] = 125; + + /* horizontal (30 -> 160 kHz) */ + desc[7] = 30; + desc[8] = 160; + + /* max dot clock (1200 MHz) */ + desc[9] = 1200 / 10; + + /* no extended timing information */ + desc[10] = 0x01; + + /* padding */ + desc[11] = '\n'; + memset(desc + 12, ' ', 6); +} + +/* additional standard timings 3 */ +static void edid_desc_xtra3_std(uint8_t *desc) +{ + edid_desc_type(desc, 0xf7); + desc[5] = 10; +} + +static void edid_desc_dummy(uint8_t *desc) +{ + edid_desc_type(desc, 0x10); +} + +static void edid_desc_timing(uint8_t *desc, + uint32_t xres, uint32_t yres, + uint32_t dpi) +{ + /* physical display size */ + uint32_t xmm = xres * dpi / 254; + uint32_t ymm = yres * dpi / 254; + + /* pull some realistic looking timings out of thin air */ + uint32_t xfront = xres * 25 / 100; + uint32_t xsync = xres * 3 / 100; + uint32_t xblank = xres * 35 / 100; + + uint32_t yfront = yres * 5 / 1000; + uint32_t ysync = yres * 5 / 1000; + uint32_t yblank = yres * 35 / 1000; + + uint32_t clock = 75 * (xres + xblank) * (yres + yblank); + + stl_le_p(desc, clock / 10000); + + desc[2] = xres & 0xff; + desc[3] = xblank & 0xff; + desc[4] = (((xres & 0xf00) >> 4) | + ((xblank & 0xf00) >> 8)); + + desc[5] = yres & 0xff; + desc[6] = yblank & 0xff; + desc[7] = (((yres & 0xf00) >> 4) | + ((yblank & 0xf00) >> 8)); + + desc[8] = xfront & 0xff; + desc[9] = xsync & 0xff; + + desc[10] = (((yfront & 0x00f) << 4) | + ((ysync & 0x00f) << 0)); + desc[11] = (((xfront & 0x300) >> 2) | + ((xsync & 0x300) >> 4) | + ((yfront & 0x030) >> 2) | + ((ysync & 0x030) >> 4)); + + desc[12] = xmm & 0xff; + desc[13] = ymm & 0xff; + desc[14] = (((xmm & 0xf00) >> 4) | + ((ymm & 0xf00) >> 8)); + + desc[17] = 0x18; +} + +static uint32_t edid_to_10bit(float value) +{ + return (uint32_t)(value * 1024 + 0.5); +} + +static void edid_colorspace(uint8_t *edid, + float rx, float ry, + float gx, float gy, + float bx, float by, + float wx, float wy) +{ + uint32_t red_x = edid_to_10bit(rx); + uint32_t red_y = edid_to_10bit(ry); + uint32_t green_x = edid_to_10bit(gx); + uint32_t green_y = edid_to_10bit(gy); + uint32_t blue_x = edid_to_10bit(bx); + uint32_t blue_y = edid_to_10bit(by); + uint32_t white_x = edid_to_10bit(wx); + uint32_t white_y = edid_to_10bit(wy); + + edid[25] = (((red_x & 0x03) << 6) | + ((red_y & 0x03) << 4) | + ((green_x & 0x03) << 2) | + ((green_y & 0x03) << 0)); + edid[26] = (((blue_x & 0x03) << 6) | + ((blue_y & 0x03) << 4) | + ((white_x & 0x03) << 2) | + ((white_y & 0x03) << 0)); + edid[27] = red_x >> 2; + edid[28] = red_y >> 2; + edid[29] = green_x >> 2; + edid[30] = green_y >> 2; + edid[31] = blue_x >> 2; + edid[32] = blue_y >> 2; + edid[33] = white_x >> 2; + edid[34] = white_y >> 2; +} + +void qemu_edid_generate(uint8_t *edid, size_t size, + qemu_edid_info *info) +{ + uint32_t desc = 54; + uint8_t *xtra3 = NULL; + uint8_t *dta = NULL; + + /* =============== set defaults =============== */ + + if (!info->vendor || strlen(info->vendor) != 3) { + info->vendor = "RHT"; + } + if (!info->name) { + info->name = "QEMU Monitor"; + } + if (!info->dpi) { + info->dpi = 100; + } + if (!info->prefx) { + info->prefx = 1024; + } + if (!info->prefy) { + info->prefy = 768; + } + + /* =============== extensions =============== */ + + if (size >= 256) { + dta = edid + 128; + edid[126]++; + edid_ext_dta(dta); + } + + /* =============== header information =============== */ + + /* fixed */ + edid[0] = 0x00; + edid[1] = 0xff; + edid[2] = 0xff; + edid[3] = 0xff; + edid[4] = 0xff; + edid[5] = 0xff; + edid[6] = 0xff; + edid[7] = 0x00; + + /* manufacturer id, product code, serial number */ + uint16_t vendor_id = ((((info->vendor[0] - '@') & 0x1f) << 10) | + (((info->vendor[1] - '@') & 0x1f) << 5) | + (((info->vendor[2] - '@') & 0x1f) << 0)); + uint16_t model_nr = 0x1234; + uint32_t serial_nr = info->serial ? atoi(info->serial) : 0; + stw_be_p(edid + 8, vendor_id); + stw_le_p(edid + 10, model_nr); + stl_le_p(edid + 12, serial_nr); + + /* manufacture week and year */ + edid[16] = 42; + edid[17] = 2014 - 1990; + + /* edid version */ + edid[18] = 1; + edid[19] = 4; + + + /* =============== basic display parameters =============== */ + + /* video input: digital, 8bpc, displayport */ + edid[20] = 0xa5; + + /* screen size: undefined */ + edid[21] = info->prefx * info->dpi / 2540; + edid[22] = info->prefy * info->dpi / 2540; + + /* display gamma: 2.2 */ + edid[23] = 220 - 100; + + /* supported features bitmap: std sRGB, preferred timing */ + edid[24] = 0x06; + + + /* =============== chromaticity coordinates =============== */ + + /* standard sRGB colorspace */ + edid_colorspace(edid, + 0.6400, 0.3300, /* red */ + 0.3000, 0.6000, /* green */ + 0.1500, 0.0600, /* blue */ + 0.3127, 0.3290); /* white point */ + + /* =============== established timing bitmap =============== */ + /* =============== standard timing information =============== */ + + /* both filled by edid_fill_modes() */ + + + /* =============== descriptor blocks =============== */ + + edid_desc_timing(edid + desc, info->prefx, info->prefy, info->dpi); + desc += 18; + + edid_desc_ranges(edid + desc); + desc += 18; + + if (info->name) { + edid_desc_text(edid + desc, 0xfc, info->name); + desc += 18; + } + + if (info->serial) { + edid_desc_text(edid + desc, 0xff, info->serial); + desc += 18; + } + + if (desc < 126) { + xtra3 = edid + desc; + edid_desc_xtra3_std(xtra3); + desc += 18; + } + + while (desc < 126) { + edid_desc_dummy(edid + desc); + desc += 18; + } + + /* =============== finish up =============== */ + + edid_fill_modes(edid, xtra3, dta, info->maxx, info->maxy); + edid_checksum(edid); + if (dta) { + edid_checksum(dta); + } +} + +size_t qemu_edid_size(uint8_t *edid) +{ + uint32_t exts; + + if (edid[0] != 0x00 || + edid[1] != 0xff) { + /* doesn't look like a valid edid block */ + return 0; + } + + exts = edid[126]; + return 128 * (exts + 1); +} diff --git a/hw/display/edid-region.c b/hw/display/edid-region.c new file mode 100644 index 0000000000..9a15734d3a --- /dev/null +++ b/hw/display/edid-region.c @@ -0,0 +1,33 @@ +#include "qemu/osdep.h" +#include "qemu-common.h" +#include "hw/display/edid.h" + +static uint64_t edid_region_read(void *ptr, hwaddr addr, unsigned size) +{ + uint8_t *edid = ptr; + + return edid[addr]; +} + +static void edid_region_write(void *ptr, hwaddr addr, + uint64_t val, unsigned size) +{ + /* read only */ +} + +static const MemoryRegionOps edid_region_ops = { + .read = edid_region_read, + .write = edid_region_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +void qemu_edid_region_io(MemoryRegion *region, Object *owner, + uint8_t *edid, size_t size) +{ + memory_region_init_io(region, owner, &edid_region_ops, + edid, "edid", size); +} diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index f011ea5b00..083b3172da 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1272,8 +1272,6 @@ static void exynos4210_fimd_update(void *opaque) uint8_t *host_fb_addr; bool is_dirty = false; const int global_width = (s->vidtcon[2] & FIMD_VIDTCON2_SIZE_MASK) + 1; - const int global_height = ((s->vidtcon[2] >> FIMD_VIDTCON2_VER_SHIFT) & - FIMD_VIDTCON2_SIZE_MASK) + 1; if (!s || !s->console || !s->enabled || surface_bits_per_pixel(qemu_console_surface(s->console)) == 0) { @@ -1329,7 +1327,7 @@ static void exynos4210_fimd_update(void *opaque) fimd_copy_line_toqemu(global_width, s->ifb + global_width * line * RGBA_SIZE, d + global_width * line * bpp); } - dpy_gfx_update(s->console, 0, 0, global_width, global_height); + dpy_gfx_update_full(s->console); } s->invalidate = false; s->vidintcon[1] |= FIMD_VIDINT_INTFRMPEND; diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index fbc2b2422d..3407adf98d 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -229,7 +229,7 @@ static void g364fb_draw_blank(G364State *s) d += surface_stride(surface); } - dpy_gfx_update(s->con, 0, 0, s->width, s->height); + dpy_gfx_update_full(s->con); s->blanked = 1; } @@ -489,18 +489,16 @@ typedef struct { G364State g364; } G364SysBusState; -static int g364fb_sysbus_init(SysBusDevice *sbd) +static void g364fb_sysbus_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); G364SysBusState *sbs = G364(dev); G364State *s = &sbs->g364; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); g364fb_init(dev, s); sysbus_init_irq(sbd, &s->irq); sysbus_init_mmio(sbd, &s->mem_ctrl); sysbus_init_mmio(sbd, &s->mem_vram); - - return 0; } static void g364fb_sysbus_reset(DeviceState *d) @@ -518,9 +516,8 @@ static Property g364fb_sysbus_properties[] = { static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = g364fb_sysbus_init; + dc->realize = g364fb_sysbus_realize; set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->desc = "G364 framebuffer"; dc->reset = g364fb_sysbus_reset; diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index 3c97d56434..eb7933d2a3 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -214,8 +214,7 @@ static void jazz_led_update_display(void *opaque) } s->state = REDRAW_NONE; - dpy_gfx_update(s->con, 0, 0, - surface_width(surface), surface_height(surface)); + dpy_gfx_update_full(s->con); } static void jazz_led_invalidate_display(void *opaque) diff --git a/hw/display/milkymist-tmu2.c b/hw/display/milkymist-tmu2.c index 3ce44fdfce..b33fc234e9 100644 --- a/hw/display/milkymist-tmu2.c +++ b/hw/display/milkymist-tmu2.c @@ -31,6 +31,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "hw/display/milkymist_tmu2.h" #include #include @@ -499,3 +500,51 @@ static void milkymist_tmu2_register_types(void) } type_init(milkymist_tmu2_register_types) + +DeviceState *milkymist_tmu2_create(hwaddr base, qemu_irq irq) +{ + DeviceState *dev; + Display *d; + GLXFBConfig *configs; + int nelements; + int ver_major, ver_minor; + + /* check that GLX will work */ + d = XOpenDisplay(NULL); + if (d == NULL) { + return NULL; + } + + if (!glXQueryVersion(d, &ver_major, &ver_minor)) { + /* + * Yeah, sometimes getting the GLX version can fail. + * Isn't X beautiful? + */ + XCloseDisplay(d); + return NULL; + } + + if ((ver_major < 1) || ((ver_major == 1) && (ver_minor < 3))) { + printf("Your GLX version is %d.%d," + "but TMU emulation needs at least 1.3. TMU disabled.\n", + ver_major, ver_minor); + XCloseDisplay(d); + return NULL; + } + + configs = glXChooseFBConfig(d, 0, glx_fbconfig_attr, &nelements); + if (configs == NULL) { + XCloseDisplay(d); + return NULL; + } + + XFree(configs); + XCloseDisplay(d); + + dev = qdev_create(NULL, TYPE_MILKYMIST_TMU2); + qdev_init_nofail(dev); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); + + return dev; +} diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index c62b9a5e75..14ad2b352d 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -98,6 +98,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) { VGACommonState *vga = &qxl->vga; DisplaySurface *surface; + int width = qxl->guest_head0_width ?: qxl->guest_primary.surface.width; + int height = qxl->guest_head0_height ?: qxl->guest_primary.surface.height; int i; if (qxl->guest_primary.resized) { @@ -111,8 +113,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl_set_rect_to_surface(qxl, &qxl->dirty[0]); qxl->num_dirty_rects = 1; trace_qxl_render_guest_primary_resized( - qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, + width, + height, qxl->guest_primary.qxl_stride, qxl->guest_primary.bytes_pp, qxl->guest_primary.bits_pp); @@ -120,15 +122,15 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) pixman_format_code_t format = qemu_default_pixman_format(qxl->guest_primary.bits_pp, true); surface = qemu_create_displaysurface_from - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height, + (width, + height, format, qxl->guest_primary.abs_stride, qxl->guest_primary.data); } else { surface = qemu_create_displaysurface - (qxl->guest_primary.surface.width, - qxl->guest_primary.surface.height); + (width, + height); } dpy_gfx_replace_surface(vga->con, surface); } @@ -144,8 +146,8 @@ static void qxl_render_update_area_unlocked(PCIQXLDevice *qxl) qxl->dirty[i].top < 0 || qxl->dirty[i].left > qxl->dirty[i].right || qxl->dirty[i].top > qxl->dirty[i].bottom || - qxl->dirty[i].right > qxl->guest_primary.surface.width || - qxl->dirty[i].bottom > qxl->guest_primary.surface.height) { + qxl->dirty[i].right > width || + qxl->dirty[i].bottom > height) { continue; } qxl_blit(qxl, qxl->dirty+i); @@ -234,12 +236,28 @@ static QEMUCursor *qxl_cursor(PCIQXLDevice *qxl, QXLCursor *cursor, uint32_t group_id) { QEMUCursor *c; + uint8_t *and_mask, *xor_mask; size_t size; c = cursor_alloc(cursor->header.width, cursor->header.height); c->hot_x = cursor->header.hot_spot_x; c->hot_y = cursor->header.hot_spot_y; switch (cursor->header.type) { + case SPICE_CURSOR_TYPE_MONO: + /* Assume that the full cursor is available in a single chunk. */ + size = 2 * cursor_get_mono_bpl(c) * c->height; + if (size != cursor->data_size) { + fprintf(stderr, "%s: bad monochrome cursor %ux%u with size %u\n", + __func__, c->width, c->height, cursor->data_size); + goto fail; + } + and_mask = cursor->chunk.data; + xor_mask = and_mask + cursor_get_mono_bpl(c) * c->height; + cursor_set_mono(c, 0xffffff, 0x000000, xor_mask, 1, and_mask); + if (qxl->debug > 2) { + cursor_print_ascii_art(c, "qxl/mono"); + } + break; case SPICE_CURSOR_TYPE_ALPHA: size = sizeof(uint32_t) * cursor->header.width * cursor->header.height; qxl_unpack_chunks(c->data, size, qxl, &cursor->chunk, group_id); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 830c392c53..c8ce5781e0 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -259,6 +259,8 @@ static void qxl_spice_destroy_surfaces(PCIQXLDevice *qxl, qxl_async_io async) static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) { + QXLMonitorsConfig *cfg; + trace_qxl_spice_monitors_config(qxl->id); if (replay) { /* @@ -274,7 +276,8 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) QXL_COOKIE_TYPE_POST_LOAD_MONITORS_CONFIG, 0)); } else { -#if SPICE_SERVER_VERSION >= 0x000c06 /* release 0.12.6 */ +/* >= release 0.12.6, < release 0.14.2 */ +#if SPICE_SERVER_VERSION >= 0x000c06 && SPICE_SERVER_VERSION < 0x000e02 if (qxl->max_outputs) { spice_qxl_set_max_monitors(&qxl->ssd.qxl, qxl->max_outputs); } @@ -286,6 +289,16 @@ static void qxl_spice_monitors_config_async(PCIQXLDevice *qxl, int replay) (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_IO, QXL_IO_MONITORS_CONFIG_ASYNC)); } + + cfg = qxl_phys2virt(qxl, qxl->guest_monitors_config, MEMSLOT_GROUP_GUEST); + if (cfg != NULL && cfg->count == 1) { + qxl->guest_primary.resized = 1; + qxl->guest_head0_width = cfg->heads[0].width; + qxl->guest_head0_height = cfg->heads[0].height; + } else { + qxl->guest_head0_width = 0; + qxl->guest_head0_height = 0; + } } void qxl_spice_reset_image_cache(PCIQXLDevice *qxl) @@ -836,7 +849,7 @@ static int interface_get_cursor_command(QXLInstance *sin, struct QXLCommandExt * qxl->guest_primary.commands++; qxl_track_command(qxl, ext); qxl_log_command(qxl, "csr", ext); - if (qxl->id == 0) { + if (qxl->have_vga) { qxl_render_cursor(qxl, ext); } trace_qxl_ring_cursor_get(qxl->id, qxl_mode_to_string(qxl->mode)); @@ -1177,9 +1190,7 @@ static void qxl_enter_vga_mode(PCIQXLDevice *d) return; } trace_qxl_enter_vga_mode(d->id); -#if SPICE_SERVER_VERSION >= 0x000c03 /* release 0.12.3 */ spice_qxl_driver_unload(&d->ssd.qxl); -#endif graphic_console_set_hwops(d->ssd.dcl.con, d->vga.hw_ops, &d->vga); update_displaychangelistener(&d->ssd.dcl, GUI_REFRESH_INTERVAL_DEFAULT); qemu_spice_create_host_primary(&d->ssd); @@ -1243,7 +1254,7 @@ static void qxl_soft_reset(PCIQXLDevice *d) d->current_async = QXL_UNDEFINED_IO; qemu_mutex_unlock(&d->async_lock); - if (d->id == 0) { + if (d->have_vga) { qxl_enter_vga_mode(d); } else { d->mode = QXL_MODE_UNDEFINED; @@ -1753,10 +1764,16 @@ async_common: qxl_set_mode(d, val, 0); break; case QXL_IO_LOG: - trace_qxl_io_log(d->id, d->ram->log_buf); - if (d->guestdebug) { - fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), d->ram->log_buf); + if (TRACE_QXL_IO_LOG_ENABLED || d->guestdebug) { + /* We cannot trust the guest to NUL terminate d->ram->log_buf */ + char *log_buf = g_strndup((const char *)d->ram->log_buf, + sizeof(d->ram->log_buf)); + trace_qxl_io_log(d->id, log_buf); + if (d->guestdebug) { + fprintf(stderr, "qxl/guest-%d: %" PRId64 ": %s", d->id, + qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), log_buf); + } + g_free(log_buf); } break; case QXL_IO_RESET: @@ -1881,7 +1898,31 @@ static void qxl_send_events(PCIQXLDevice *d, uint32_t events) trace_qxl_send_events_vm_stopped(d->id, events); return; } - old_pending = atomic_fetch_or(&d->ram->int_pending, le_events); + /* + * Older versions of Spice forgot to define the QXLRam struct + * with the '__aligned__(4)' attribute. clang 7 and newer will + * thus warn that atomic_fetch_or(&d->ram->int_pending, ...) + * might be a misaligned atomic access, and will generate an + * out-of-line call for it, which results in a link error since + * we don't currently link against libatomic. + * + * In fact we set up d->ram in init_qxl_ram() so it always starts + * at a 4K boundary, so we know that &d->ram->int_pending is + * naturally aligned for a uint32_t. Newer Spice versions + * (with Spice commit beda5ec7a6848be20c0cac2a9a8ef2a41e8069c1) + * will fix the bug directly. To deal with older versions, + * we tell the compiler to assume the address really is aligned. + * Any compiler which cares about the misalignment will have + * __builtin_assume_aligned. + */ +#ifdef HAS_ASSUME_ALIGNED +#define ALIGNED_UINT32_PTR(P) ((uint32_t *)__builtin_assume_aligned(P, 4)) +#else +#define ALIGNED_UINT32_PTR(P) ((uint32_t *)P) +#endif + + old_pending = atomic_fetch_or(ALIGNED_UINT32_PTR(&d->ram->int_pending), + le_events); if ((old_pending & le_events) == le_events) { return; } @@ -2057,7 +2098,6 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) qemu_spice_display_init_common(&qxl->ssd); qxl->mode = QXL_MODE_UNDEFINED; - qxl->generation = 1; qxl->num_memslots = NUM_MEMSLOTS; qemu_mutex_init(&qxl->track_lock); qemu_mutex_init(&qxl->async_lock); @@ -2104,7 +2144,7 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) memory_region_init_io(&qxl->io_bar, OBJECT(qxl), &qxl_io_ops, qxl, "qxl-ioports", io_size); - if (qxl->id == 0) { + if (qxl->have_vga) { vga_dirty_log_start(&qxl->vga); } memory_region_set_flush_coalesced(&qxl->io_bar); @@ -2136,7 +2176,7 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) /* print pci bar details */ dprint(qxl, 1, "ram/%s: %" PRId64 " MB [region 0]\n", - qxl->id == 0 ? "pri" : "sec", qxl->vga.vram_size / MiB); + qxl->have_vga ? "pri" : "sec", qxl->vga.vram_size / MiB); dprint(qxl, 1, "vram/32: %" PRIx64 " MB [region 1]\n", qxl->vram32_size / MiB); dprint(qxl, 1, "vram/64: %" PRIx64 " MB %s\n", @@ -2149,6 +2189,17 @@ static void qxl_realize_common(PCIQXLDevice *qxl, Error **errp) SPICE_INTERFACE_QXL_MAJOR, SPICE_INTERFACE_QXL_MINOR); return; } + +#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */ + char device_address[256] = ""; + if (qemu_spice_fill_device_address(qxl->vga.con, device_address, 256)) { + spice_qxl_set_device_info(&qxl->ssd.qxl, + device_address, + 0, + qxl->max_outputs); + } +#endif + qemu_add_vm_change_state_handler(qxl_vm_change_state_handler, qxl); qxl->update_irq = qemu_bh_new(qxl_update_irq_bh, qxl); @@ -2164,7 +2215,6 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) VGACommonState *vga = &qxl->vga; Error *local_err = NULL; - qxl->id = 0; qxl_init_ramsize(qxl); vga->vbe_size = qxl->vgamem_size; vga->vram_size_mb = qxl->vga.vram_size / MiB; @@ -2175,8 +2225,15 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) vga, "vga"); portio_list_set_flush_coalesced(&qxl->vga_port_list); portio_list_add(&qxl->vga_port_list, pci_address_space_io(dev), 0x3b0); + qxl->have_vga = true; vga->con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); + qxl->id = qemu_console_get_index(vga->con); /* == channel_id */ + if (qxl->id != 0) { + error_setg(errp, "primary qxl-vga device must be console 0 " + "(first display device on the command line)"); + return; + } qxl_realize_common(qxl, &local_err); if (local_err) { @@ -2191,15 +2248,14 @@ static void qxl_realize_primary(PCIDevice *dev, Error **errp) static void qxl_realize_secondary(PCIDevice *dev, Error **errp) { - static int device_id = 1; PCIQXLDevice *qxl = PCI_QXL(dev); - qxl->id = device_id++; qxl_init_ramsize(qxl); memory_region_init_ram(&qxl->vga.vram, OBJECT(dev), "qxl.vgavram", qxl->vga.vram_size, &error_fatal); qxl->vga.vram_ptr = memory_region_get_ram_ptr(&qxl->vga.vram); qxl->vga.con = graphic_console_init(DEVICE(dev), 0, &qxl_ops, qxl); + qxl->id = qemu_console_get_index(qxl->vga.con); /* == channel_id */ qxl_realize_common(qxl, errp); } diff --git a/hw/display/qxl.h b/hw/display/qxl.h index 089696ef62..6f9d1f21fa 100644 --- a/hw/display/qxl.h +++ b/hw/display/qxl.h @@ -34,6 +34,7 @@ typedef struct PCIQXLDevice { PortioList vga_port_list; SimpleSpiceDisplay ssd; int id; + bool have_vga; uint32_t debug; uint32_t guestdebug; uint32_t cmdlog; @@ -43,7 +44,6 @@ typedef struct PCIQXLDevice { enum qxl_mode mode; uint32_t cmdflags; - int generation; uint32_t revision; int32_t num_memslots; @@ -79,6 +79,8 @@ typedef struct PCIQXLDevice { QXLPHYSICAL guest_cursor; QXLPHYSICAL guest_monitors_config; + uint32_t guest_head0_width; + uint32_t guest_head0_height; QemuMutex track_lock; diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index c0d241ba01..da3229a1f6 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -1,7 +1,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/loader.h" -#include "hw/isa/isa.h" #include "hw/display/ramfb.h" #include "ui/console.h" #include "sysemu/sysemu.h" diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c index eaf11a6e7b..9994385c35 100644 --- a/hw/display/sii9022.c +++ b/hw/display/sii9022.c @@ -79,7 +79,7 @@ static int sii9022_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int sii9022_rx(I2CSlave *i2c) +static uint8_t sii9022_rx(I2CSlave *i2c) { sii9022_state *s = SII9022(i2c); uint8_t res = 0x00; diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 874260a143..2122291308 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -32,13 +32,13 @@ #include "hw/hw.h" #include "hw/char/serial.h" #include "ui/console.h" -#include "hw/devices.h" #include "hw/sysbus.h" #include "hw/pci/pci.h" #include "hw/i2c/i2c.h" #include "hw/i2c/i2c-ddc.h" #include "qemu/range.h" #include "ui/pixel_ops.h" +#include "qemu/bswap.h" /* * Status: 2010/05/07 @@ -812,9 +812,11 @@ static void sm501_2d_operation(SM501State *s) FILL_RECT(1, uint8_t); break; case 1: + color = cpu_to_le16(color); FILL_RECT(2, uint16_t); break; case 2: + color = cpu_to_le32(color); FILL_RECT(4, uint32_t); break; } diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index eb90ba26be..8edf34986c 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -62,10 +62,10 @@ typedef struct { uint8_t framebuffer[132*8]; } ssd0303_state; -static int ssd0303_recv(I2CSlave *i2c) +static uint8_t ssd0303_recv(I2CSlave *i2c) { BADF("Reads not implemented\n"); - return -1; + return 0xff; } static int ssd0303_send(I2CSlave *i2c, uint8_t data) diff --git a/hw/display/tc6393xb.c b/hw/display/tc6393xb.c index 8392e59493..e1b1e302f2 100644 --- a/hw/display/tc6393xb.c +++ b/hw/display/tc6393xb.c @@ -319,7 +319,7 @@ static void tc6393xb_scr_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) SCR_REG_B(DEBUG); } fprintf(stderr, "tc6393xb_scr: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); + (uint32_t) addr, value & 0xff); } #undef SCR_REG_B #undef SCR_REG_W @@ -358,7 +358,7 @@ static void tc6393xb_nand_cfg_writeb(TC6393xbState *s, hwaddr addr, uint32_t val return; } fprintf(stderr, "tc6393xb_nand_cfg: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); + (uint32_t) addr, value & 0xff); } static uint32_t tc6393xb_nand_readb(TC6393xbState *s, hwaddr addr) { @@ -421,7 +421,7 @@ static void tc6393xb_nand_writeb(TC6393xbState *s, hwaddr addr, uint32_t value) return; } fprintf(stderr, "tc6393xb_nand: unhandled write at %08x: %02x\n", - (uint32_t) addr, value & 0xff); + (uint32_t) addr, value & 0xff); } #define BITS 8 @@ -461,7 +461,7 @@ static void tc6393xb_draw_graphic(TC6393xbState *s, int full_update) return; } - dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); + dpy_gfx_update_full(s->con); } static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) @@ -480,7 +480,7 @@ static void tc6393xb_draw_blank(TC6393xbState *s, int full_update) d += surface_stride(surface); } - dpy_gfx_update(s->con, 0, 0, s->scr_width, s->scr_height); + dpy_gfx_update_full(s->con); } static void tc6393xb_update_display(void *opaque) diff --git a/hw/display/tcx.c b/hw/display/tcx.c index b2786ee8d0..66f2459226 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -823,7 +823,7 @@ static void tcx_realizefn(DeviceState *dev, Error **errp) ret = load_image_mr(fcode_filename, &s->rom); g_free(fcode_filename); if (ret < 0 || ret > FCODE_MAX_ROM_SIZE) { - error_report("tcx: could not load prom '%s'", TCX_ROM_FILE); + warn_report("tcx: could not load prom '%s'", TCX_ROM_FILE); } } diff --git a/hw/display/trace-events b/hw/display/trace-events index 5a48c6cb6a..ba7787b180 100644 --- a/hw/display/trace-events +++ b/hw/display/trace-events @@ -1,29 +1,29 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/display/jazz_led.c +# jazz_led.c jazz_led_read(uint64_t addr, uint8_t val) "read addr=0x%"PRIx64": 0x%x" jazz_led_write(uint64_t addr, uint8_t new) "write addr=0x%"PRIx64": 0x%x" -# hw/display/xenfb.c +# xenfb.c xenfb_mouse_event(void *opaque, int dx, int dy, int dz, int button_state, int abs_pointer_wanted) "%p x %d y %d z %d bs 0x%x abs %d" xenfb_key_event(void *opaque, int scancode, int button_state) "%p scancode %d bs 0x%x" xenfb_input_connected(void *xendev, int abs_pointer_wanted) "%p abs %d" -# hw/display/g364fb.c +# g364fb.c g364fb_read(uint64_t addr, uint32_t val) "read addr=0x%"PRIx64": 0x%x" g364fb_write(uint64_t addr, uint32_t new) "write addr=0x%"PRIx64": 0x%x" -# hw/display/milkymist-tmu2.c +# milkymist-tmu2.c milkymist_tmu2_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_tmu2_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_tmu2_start(void) "Start TMU" milkymist_tmu2_pulse_irq(void) "Pulse IRQ" -# hw/display/milkymist-vgafb.c +# milkymist-vgafb.c milkymist_vgafb_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_vgafb_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" -# hw/display/vmware_vga.c +# vmware_vga.c vmware_value_read(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_value_write(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_palette_read(uint32_t index, uint32_t value) "index %d, value 0x%x" @@ -32,9 +32,11 @@ vmware_scratch_read(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_scratch_write(uint32_t index, uint32_t value) "index %d, value 0x%x" vmware_setmode(uint32_t w, uint32_t h, uint32_t bpp) "%dx%d @ %d bpp" -# hw/display/virtio-gpu.c +# virtio-gpu-3d.c +# virtio-gpu.c virtio_gpu_features(bool virgl) "virgl %d" virtio_gpu_cmd_get_display_info(void) "" +virtio_gpu_cmd_get_edid(uint32_t scanout) "scanout %d" virtio_gpu_cmd_set_scanout(uint32_t id, uint32_t res, uint32_t w, uint32_t h, uint32_t x, uint32_t y) "id %d, res 0x%x, w %d, h %d, x %d, y %d" virtio_gpu_cmd_res_create_2d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h) "res 0x%x, fmt 0x%x, w %d, h %d" virtio_gpu_cmd_res_create_3d(uint32_t res, uint32_t fmt, uint32_t w, uint32_t h, uint32_t d) "res 0x%x, fmt 0x%x, w %d, h %d, d %d" @@ -54,7 +56,7 @@ virtio_gpu_update_cursor(uint32_t scanout, uint32_t x, uint32_t y, const char *t 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 -# hw/display/qxl.c +# qxl.c disable qxl_interface_set_mm_time(int qid, uint32_t mm_time) "%d %d" disable qxl_io_write_vga(int qid, const char *mode, uint32_t addr, uint32_t val) "%d %s addr=%u val=%u" qxl_create_guest_primary(int qid, uint32_t width, uint32_t height, uint64_t mem, uint32_t format, uint32_t position) "%d %ux%u mem=0x%" PRIx64 " %u,%u" @@ -72,7 +74,7 @@ qxl_interface_update_area_complete_rest(int qid, uint32_t num_updated_rects) "%d qxl_interface_update_area_complete_overflow(int qid, int max) "%d max=%d" qxl_interface_update_area_complete_schedule_bh(int qid, uint32_t num_dirty) "%d #dirty=%d" qxl_io_destroy_primary_ignored(int qid, const char *mode) "%d %s" -qxl_io_log(int qid, const uint8_t *log_buf) "%d %s" +qxl_io_log(int qid, const char *log_buf) "%d %s" qxl_io_read_unexpected(int qid) "%d" qxl_io_unexpected_vga_mode(int qid, uint64_t addr, uint64_t val, const char *desc) "%d 0x%"PRIx64"=%"PRIu64" (%s)" qxl_io_write(int qid, const char *mode, uint64_t addr, const char *aname, uint64_t val, unsigned size, int async) "%d %s addr=%"PRIu64 " (%s) val=%"PRIu64" size=%u async=%d" @@ -116,24 +118,27 @@ qxl_client_monitors_config_capped(int qid, int requested, int limit) "%d %d %d" qxl_client_monitors_config_crc(int qid, unsigned size, uint32_t crc32) "%d %u %u" qxl_set_client_capabilities_unsupported_by_revision(int qid, int revision) "%d revision=%d" -# hw/display/qxl-render.c +# qxl-render.c qxl_render_blit(int32_t stride, int32_t left, int32_t right, int32_t top, int32_t bottom) "stride=%d [%d, %d, %d, %d]" qxl_render_guest_primary_resized(int32_t width, int32_t height, int32_t stride, int32_t bytes_pp, int32_t bits_pp) "%dx%d, stride %d, bpp %d, depth %d" qxl_render_update_area_done(void *cookie) "%p" -# hw/display/vga.c +# vga.c vga_std_read_io(uint32_t addr, uint32_t val) "addr 0x%x, val 0x%x" vga_std_write_io(uint32_t addr, uint32_t val) "addr 0x%x, val 0x%x" vga_vbe_read(uint32_t index, uint32_t val) "index 0x%x, val 0x%x" vga_vbe_write(uint32_t index, uint32_t val) "index 0x%x, val 0x%x" -# hw/display/cirrus_vga.c +# cirrus_vga.c vga_cirrus_read_io(uint32_t addr, uint32_t val) "addr 0x%x, val 0x%x" vga_cirrus_write_io(uint32_t addr, uint32_t val) "addr 0x%x, val 0x%x" -vga_cirrus_read_blt(uint32_t offset, uint32_t val) "offset 0x%x, val 0x%x" vga_cirrus_write_blt(uint32_t offset, uint32_t val) "offset 0x%x, val 0x%x" -# hw/display/sii9022.c +# sii9022.c sii9022_read_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" sii9022_write_reg(uint8_t addr, uint8_t val) "addr 0x%02x, val 0x%02x" sii9022_switch_mode(const char *mode) "mode: %s" + +# ati.c +ati_mm_read(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 " %s -> 0x%"PRIx64 +ati_mm_write(unsigned int size, uint64_t addr, const char *name, uint64_t val) "%u 0x%"PRIx64 " %s <- 0x%"PRIx64 diff --git a/hw/display/vga-isa-mm.c b/hw/display/vga-isa-mm.c index 232216cad0..215e649719 100644 --- a/hw/display/vga-isa-mm.c +++ b/hw/display/vga-isa-mm.c @@ -36,64 +36,30 @@ typedef struct ISAVGAMMState { } ISAVGAMMState; /* Memory mapped interface */ -static uint32_t vga_mm_readb (void *opaque, hwaddr addr) +static uint64_t vga_mm_read(void *opaque, hwaddr addr, unsigned size) { ISAVGAMMState *s = opaque; - return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xff; + return vga_ioport_read(&s->vga, addr >> s->it_shift) & + MAKE_64BIT_MASK(0, size * 8); } -static void vga_mm_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void vga_mm_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { ISAVGAMMState *s = opaque; - vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xff); -} - -static uint32_t vga_mm_readw (void *opaque, hwaddr addr) -{ - ISAVGAMMState *s = opaque; - - return vga_ioport_read(&s->vga, addr >> s->it_shift) & 0xffff; -} - -static void vga_mm_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ISAVGAMMState *s = opaque; - - vga_ioport_write(&s->vga, addr >> s->it_shift, value & 0xffff); -} - -static uint32_t vga_mm_readl (void *opaque, hwaddr addr) -{ - ISAVGAMMState *s = opaque; - - return vga_ioport_read(&s->vga, addr >> s->it_shift); -} - -static void vga_mm_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ISAVGAMMState *s = opaque; - - vga_ioport_write(&s->vga, addr >> s->it_shift, value); + vga_ioport_write(&s->vga, addr >> s->it_shift, + value & MAKE_64BIT_MASK(0, size * 8)); } static const MemoryRegionOps vga_mm_ctrl_ops = { - .old_mmio = { - .read = { - vga_mm_readb, - vga_mm_readw, - vga_mm_readl, - }, - .write = { - vga_mm_writeb, - vga_mm_writew, - vga_mm_writel, - }, - }, + .read = vga_mm_read, + .write = vga_mm_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index e9e62eac70..a17c96e703 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -30,18 +30,22 @@ #include "ui/pixel_ops.h" #include "qemu/timer.h" #include "hw/loader.h" +#include "hw/display/edid.h" enum vga_pci_flags { PCI_VGA_FLAG_ENABLE_MMIO = 1, PCI_VGA_FLAG_ENABLE_QEXT = 2, + PCI_VGA_FLAG_ENABLE_EDID = 3, }; typedef struct PCIVGAState { PCIDevice dev; VGACommonState vga; uint32_t flags; + qemu_edid_info edid_info; MemoryRegion mmio; - MemoryRegion mrs[3]; + MemoryRegion mrs[4]; + uint8_t edid[256]; } PCIVGAState; #define TYPE_PCI_VGA "pci-vga" @@ -195,8 +199,10 @@ void pci_std_vga_mmio_region_init(VGACommonState *s, Object *owner, MemoryRegion *parent, MemoryRegion *subs, - bool qext) + bool qext, bool edid) { + PCIVGAState *d = container_of(s, PCIVGAState, vga); + memory_region_init_io(&subs[0], owner, &pci_vga_ioport_ops, s, "vga ioports remapped", PCI_VGA_IOPORT_SIZE); memory_region_add_subregion(parent, PCI_VGA_IOPORT_OFFSET, @@ -213,6 +219,12 @@ void pci_std_vga_mmio_region_init(VGACommonState *s, memory_region_add_subregion(parent, PCI_VGA_QEXT_OFFSET, &subs[2]); } + + if (edid) { + qemu_edid_generate(d->edid, sizeof(d->edid), &d->edid_info); + qemu_edid_region_io(&subs[3], owner, d->edid, sizeof(d->edid)); + memory_region_add_subregion(parent, 0, &subs[3]); + } } static void pci_std_vga_realize(PCIDevice *dev, Error **errp) @@ -220,6 +232,7 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) PCIVGAState *d = PCI_VGA(dev); VGACommonState *s = &d->vga; bool qext = false; + bool edid = false; /* vga + console init */ vga_common_init(s, OBJECT(dev)); @@ -240,7 +253,11 @@ static void pci_std_vga_realize(PCIDevice *dev, Error **errp) qext = true; pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); } - pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext); + if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_EDID)) { + edid = true; + } + pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, + qext, edid); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); } @@ -263,6 +280,7 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) PCIVGAState *d = PCI_VGA(dev); VGACommonState *s = &d->vga; bool qext = false; + bool edid = false; /* vga + console init */ vga_common_init(s, OBJECT(dev)); @@ -276,7 +294,10 @@ static void pci_secondary_vga_realize(PCIDevice *dev, Error **errp) qext = true; pci_set_byte(&d->dev.config[PCI_REVISION_ID], 2); } - pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext); + if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_EDID)) { + edid = true; + } + pci_std_vga_mmio_region_init(s, OBJECT(dev), &d->mmio, d->mrs, qext, edid); pci_register_bar(&d->dev, 0, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->vram); pci_register_bar(&d->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); @@ -288,6 +309,14 @@ static void pci_secondary_vga_exit(PCIDevice *dev) VGACommonState *s = &d->vga; graphic_console_close(s->con); + memory_region_del_subregion(&d->mmio, &d->mrs[0]); + memory_region_del_subregion(&d->mmio, &d->mrs[1]); + if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_QEXT)) { + memory_region_del_subregion(&d->mmio, &d->mrs[2]); + } + if (d->flags & (1 << PCI_VGA_FLAG_ENABLE_EDID)) { + memory_region_del_subregion(&d->mmio, &d->mrs[3]); + } } static void pci_secondary_vga_init(Object *obj) @@ -308,6 +337,9 @@ static Property vga_pci_properties[] = { DEFINE_PROP_BIT("mmio", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_MMIO, true), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), + DEFINE_PROP_BIT("edid", + PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_EDID, false), + DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), DEFINE_PROP_BOOL("global-vmstate", PCIVGAState, vga.global_vmstate, false), DEFINE_PROP_END_OF_LIST(), }; @@ -316,6 +348,9 @@ static Property secondary_pci_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", PCIVGAState, vga.vram_size_mb, 16), DEFINE_PROP_BIT("qemu-extended-regs", PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_QEXT, true), + DEFINE_PROP_BIT("edid", + PCIVGAState, flags, PCI_VGA_FLAG_ENABLE_EDID, false), + DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/display/vga.c b/hw/display/vga.c index 802cfd47db..910a23c12e 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -85,10 +85,10 @@ const uint8_t gr_mask[16] = { #define cbswap_32(__x) \ ((uint32_t)( \ - (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ - (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ - (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ - (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) + (((uint32_t)(__x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(__x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(__x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(__x) & (uint32_t)0xff000000UL) >> 24) )) #ifdef HOST_WORDS_BIGENDIAN #define PAT(x) cbswap_32(x) @@ -1745,8 +1745,7 @@ static void vga_draw_blank(VGACommonState *s, int full_update) memset(d, 0, w); d += surface_stride(surface); } - dpy_gfx_update(s->con, 0, 0, - s->last_scr_width, s->last_scr_height); + dpy_gfx_update_full(s->con); } #define GMODE_TEXT 0 diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 339661bc01..55c418eab5 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -166,7 +166,6 @@ MemoryRegion *vga_init_io(VGACommonState *s, Object *obj, const MemoryRegionPortio **vbe_ports); void vga_common_reset(VGACommonState *s); -void vga_sync_dirty_bitmap(VGACommonState *s); void vga_dirty_log_start(VGACommonState *s); void vga_dirty_log_stop(VGACommonState *s); @@ -197,6 +196,6 @@ void pci_std_vga_mmio_region_init(VGACommonState *s, Object *owner, MemoryRegion *parent, MemoryRegion *subs, - bool qext); + bool qext, bool edid); #endif diff --git a/hw/display/virtio-gpu-3d.c b/hw/display/virtio-gpu-3d.c index 3558f38fe8..2d302526ab 100644 --- a/hw/display/virtio-gpu-3d.c +++ b/hw/display/virtio-gpu-3d.c @@ -86,7 +86,7 @@ static void virgl_cmd_resource_unref(VirtIOGPU *g, &res_iovs, &num_iovs); if (res_iovs != NULL && num_iovs != 0) { - virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs); + virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs); } virgl_renderer_resource_unref(unref.resource_id); } @@ -291,7 +291,7 @@ static void virgl_resource_attach_backing(VirtIOGPU *g, 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, NULL, &res_iovs); + ret = virtio_gpu_create_mapping_iov(g, &att_rb, cmd, NULL, &res_iovs); if (ret != 0) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; @@ -301,7 +301,7 @@ static void virgl_resource_attach_backing(VirtIOGPU *g, res_iovs, att_rb.nr_entries); if (ret != 0) - virtio_gpu_cleanup_mapping_iov(res_iovs, att_rb.nr_entries); + virtio_gpu_cleanup_mapping_iov(g, res_iovs, att_rb.nr_entries); } static void virgl_resource_detach_backing(VirtIOGPU *g, @@ -320,7 +320,7 @@ static void virgl_resource_detach_backing(VirtIOGPU *g, if (res_iovs == NULL || num_iovs == 0) { return; } - virtio_gpu_cleanup_mapping_iov(res_iovs, num_iovs); + virtio_gpu_cleanup_mapping_iov(g, res_iovs, num_iovs); } @@ -404,11 +404,6 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, { 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: @@ -468,6 +463,9 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: virtio_gpu_get_display_info(g, cmd); break; + case VIRTIO_GPU_CMD_GET_EDID: + virtio_gpu_get_edid(g, cmd); + break; default: cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; break; @@ -498,9 +496,9 @@ static void virgl_write_fence(void *opaque, uint32_t fence) 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. - */ + * 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; } @@ -604,22 +602,6 @@ void virtio_gpu_virgl_reset(VirtIOGPU *g) } } -void virtio_gpu_gl_block(void *opaque, bool block) -{ - VirtIOGPU *g = opaque; - - if (block) { - g->renderer_blocked++; - } else { - g->renderer_blocked--; - } - assert(g->renderer_blocked >= 0); - - if (g->renderer_blocked == 0) { - virtio_gpu_process_cmdq(g); - } -} - int virtio_gpu_virgl_init(VirtIOGPU *g) { int ret; diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index cece4aa495..bdcd33c925 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -19,6 +19,20 @@ #include "hw/virtio/virtio-pci.h" #include "hw/virtio/virtio-gpu.h" +typedef struct VirtIOGPUPCI VirtIOGPUPCI; + +/* + * virtio-gpu-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" +#define VIRTIO_GPU_PCI(obj) \ + OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) + +struct VirtIOGPUPCI { + VirtIOPCIProxy parent_obj; + VirtIOGPU vdev; +}; + static Property virtio_gpu_pci_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), DEFINE_PROP_END_OF_LIST(), @@ -69,9 +83,8 @@ static void virtio_gpu_initfn(Object *obj) TYPE_VIRTIO_GPU); } -static const TypeInfo virtio_gpu_pci_info = { - .name = TYPE_VIRTIO_GPU_PCI, - .parent = TYPE_VIRTIO_PCI, +static const VirtioPCIDeviceTypeInfo virtio_gpu_pci_info = { + .generic_name = TYPE_VIRTIO_GPU_PCI, .instance_size = sizeof(VirtIOGPUPCI), .instance_init = virtio_gpu_initfn, .class_init = virtio_gpu_pci_class_init, @@ -79,6 +92,6 @@ static const TypeInfo virtio_gpu_pci_info = { static void virtio_gpu_pci_register_types(void) { - type_register_static(&virtio_gpu_pci_info); + virtio_pci_types_register(&virtio_gpu_pci_info); } type_init(virtio_gpu_pci_register_types) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 3ddd29c0de..9e37e0ac96 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -17,9 +17,11 @@ #include "qemu/iov.h" #include "ui/console.h" #include "trace.h" +#include "sysemu/dma.h" #include "hw/virtio/virtio.h" #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-bus.h" +#include "hw/display/edid.h" #include "migration/blocker.h" #include "qemu/log.h" #include "qapi/error.h" @@ -29,7 +31,8 @@ static struct virtio_gpu_simple_resource* virtio_gpu_find_resource(VirtIOGPU *g, uint32_t resource_id); -static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res); +static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res); static void virtio_gpu_ctrl_hdr_bswap(struct virtio_gpu_ctrl_hdr *hdr) @@ -205,6 +208,9 @@ static uint64_t virtio_gpu_get_features(VirtIODevice *vdev, uint64_t features, if (virtio_gpu_virgl_enabled(g->conf)) { features |= (1 << VIRTIO_GPU_F_VIRGL); } + if (virtio_gpu_edid_enabled(g->conf)) { + features |= (1 << VIRTIO_GPU_F_EDID); + } return features; } @@ -299,6 +305,40 @@ void virtio_gpu_get_display_info(VirtIOGPU *g, sizeof(display_info)); } +static void +virtio_gpu_generate_edid(VirtIOGPU *g, int scanout, + struct virtio_gpu_resp_edid *edid) +{ + qemu_edid_info info = { + .prefx = g->req_state[scanout].width, + .prefy = g->req_state[scanout].height, + }; + + edid->size = cpu_to_le32(sizeof(edid->edid)); + qemu_edid_generate(edid->edid, sizeof(edid->edid), &info); +} + +void virtio_gpu_get_edid(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd) +{ + struct virtio_gpu_resp_edid edid; + struct virtio_gpu_cmd_get_edid get_edid; + + VIRTIO_GPU_FILL_CMD(get_edid); + virtio_gpu_bswap_32(&get_edid, sizeof(get_edid)); + + if (get_edid.scanout >= g->conf.max_outputs) { + cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER; + return; + } + + trace_virtio_gpu_cmd_get_edid(get_edid.scanout); + memset(&edid, 0, sizeof(edid)); + edid.hdr.type = VIRTIO_GPU_RESP_OK_EDID; + virtio_gpu_generate_edid(g, get_edid.scanout, &edid); + virtio_gpu_ctrl_response(g, cmd, &edid.hdr, sizeof(edid)); +} + static pixman_format_code_t get_pixman_format(uint32_t virtio_gpu_format) { switch (virtio_gpu_format) { @@ -421,11 +461,6 @@ static void virtio_gpu_disable_scanout(VirtIOGPU *g, int scanout_id) scanout->height ?: 480, "Guest disabled display."); } - - if (g->disable_scanout) { - g->disable_scanout(g, scanout_id); - } - dpy_gfx_replace_surface(scanout->con, ds); scanout->resource_id = 0; scanout->ds = NULL; @@ -447,7 +482,7 @@ static void virtio_gpu_resource_destroy(VirtIOGPU *g, } pixman_image_unref(res->image); - virtio_gpu_cleanup_mapping(res); + virtio_gpu_cleanup_mapping(g, res); QTAILQ_REMOVE(&g->reslist, res, next); g->hostmem -= res->hostmem; g_free(res); @@ -693,7 +728,8 @@ static void virtio_gpu_set_scanout(VirtIOGPU *g, scanout->height = ss.r.height; } -int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, +int virtio_gpu_create_mapping_iov(VirtIOGPU *g, + struct virtio_gpu_resource_attach_backing *ab, struct virtio_gpu_ctrl_command *cmd, uint64_t **addr, struct iovec **iov) { @@ -729,7 +765,8 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, uint32_t l = le32_to_cpu(ents[i].length); hwaddr len = l; (*iov)[i].iov_len = l; - (*iov)[i].iov_base = cpu_physical_memory_map(a, &len, 1); + (*iov)[i].iov_base = dma_memory_map(VIRTIO_DEVICE(g)->dma_as, + a, &len, DMA_DIRECTION_TO_DEVICE); if (addr) { (*addr)[i] = a; } @@ -737,7 +774,7 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, qemu_log_mask(LOG_GUEST_ERROR, "%s: failed to map MMIO memory for" " resource %d element %d\n", __func__, ab->resource_id, i); - virtio_gpu_cleanup_mapping_iov(*iov, i); + virtio_gpu_cleanup_mapping_iov(g, *iov, i); g_free(ents); *iov = NULL; if (addr) { @@ -751,20 +788,24 @@ int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, return 0; } -void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count) +void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g, + struct iovec *iov, uint32_t count) { int i; for (i = 0; i < count; i++) { - cpu_physical_memory_unmap(iov[i].iov_base, iov[i].iov_len, 1, - iov[i].iov_len); + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, + iov[i].iov_base, iov[i].iov_len, + DMA_DIRECTION_TO_DEVICE, + iov[i].iov_len); } g_free(iov); } -static void virtio_gpu_cleanup_mapping(struct virtio_gpu_simple_resource *res) +static void virtio_gpu_cleanup_mapping(VirtIOGPU *g, + struct virtio_gpu_simple_resource *res) { - virtio_gpu_cleanup_mapping_iov(res->iov, res->iov_cnt); + virtio_gpu_cleanup_mapping_iov(g, res->iov, res->iov_cnt); res->iov = NULL; res->iov_cnt = 0; g_free(res->addrs); @@ -796,7 +837,7 @@ virtio_gpu_resource_attach_backing(VirtIOGPU *g, return; } - ret = virtio_gpu_create_mapping_iov(&ab, cmd, &res->addrs, &res->iov); + ret = virtio_gpu_create_mapping_iov(g, &ab, cmd, &res->addrs, &res->iov); if (ret != 0) { cmd->error = VIRTIO_GPU_RESP_ERR_UNSPEC; return; @@ -823,7 +864,7 @@ virtio_gpu_resource_detach_backing(VirtIOGPU *g, cmd->error = VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID; return; } - virtio_gpu_cleanup_mapping(res); + virtio_gpu_cleanup_mapping(g, res); } static void virtio_gpu_simple_process_cmd(VirtIOGPU *g, @@ -836,6 +877,9 @@ static void virtio_gpu_simple_process_cmd(VirtIOGPU *g, case VIRTIO_GPU_CMD_GET_DISPLAY_INFO: virtio_gpu_get_display_info(g, cmd); break; + case VIRTIO_GPU_CMD_GET_EDID: + virtio_gpu_get_edid(g, cmd); + break; case VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: virtio_gpu_resource_create_2d(g, cmd); break; @@ -886,12 +930,14 @@ void virtio_gpu_process_cmdq(VirtIOGPU *g) while (!QTAILQ_EMPTY(&g->cmdq)) { cmd = QTAILQ_FIRST(&g->cmdq); + if (g->renderer_blocked) { + break; + } + /* 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++; @@ -933,7 +979,6 @@ static void virtio_gpu_handle_ctrl(VirtIODevice *vdev, VirtQueue *vq) cmd->vq = vq; cmd->error = 0; cmd->finished = false; - cmd->waiting = false; QTAILQ_INSERT_TAIL(&g->cmdq, cmd, next); cmd = virtqueue_pop(vq, sizeof(struct virtio_gpu_ctrl_command)); } @@ -1027,14 +1072,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; + + if (block) { + g->renderer_blocked++; + } else { + g->renderer_blocked--; + } + assert(g->renderer_blocked >= 0); + + if (g->renderer_blocked == 0) { +#ifdef CONFIG_VIRGL + if (g->renderer_reset) { + g->renderer_reset = false; + virtio_gpu_virgl_reset(g); + } +#endif + 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, -#ifdef CONFIG_VIRGL .gl_block = virtio_gpu_gl_block, -#endif }; static const VMStateDescription vmstate_virtio_gpu_scanout = { @@ -1070,7 +1135,7 @@ static const VMStateDescription vmstate_virtio_gpu_scanouts = { }; static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { VirtIOGPU *g = opaque; struct virtio_gpu_simple_resource *res; @@ -1098,7 +1163,7 @@ static int virtio_gpu_save(QEMUFile *f, void *opaque, size_t size, } static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, - VMStateField *field) + const VMStateField *field) { VirtIOGPU *g = opaque; struct virtio_gpu_simple_resource *res; @@ -1148,16 +1213,21 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, for (i = 0; i < res->iov_cnt; i++) { hwaddr len = res->iov[i].iov_len; res->iov[i].iov_base = - cpu_physical_memory_map(res->addrs[i], &len, 1); + dma_memory_map(VIRTIO_DEVICE(g)->dma_as, + res->addrs[i], &len, DMA_DIRECTION_TO_DEVICE); + if (!res->iov[i].iov_base || len != res->iov[i].iov_len) { /* Clean up the half-a-mapping we just created... */ if (res->iov[i].iov_base) { - cpu_physical_memory_unmap(res->iov[i].iov_base, - len, 0, 0); + dma_memory_unmap(VIRTIO_DEVICE(g)->dma_as, + res->iov[i].iov_base, + res->iov[i].iov_len, + DMA_DIRECTION_TO_DEVICE, + res->iov[i].iov_len); } /* ...and the mappings for previous loop iterations */ res->iov_cnt = i; - virtio_gpu_cleanup_mapping(res); + virtio_gpu_cleanup_mapping(g, res); pixman_image_unref(res->image); g_free(res); return -EINVAL; @@ -1187,7 +1257,7 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, } dpy_gfx_replace_surface(scanout->con, scanout->ds); - dpy_gfx_update(scanout->con, 0, 0, scanout->width, scanout->height); + dpy_gfx_update_full(scanout->con); if (scanout->cursor.resource_id) { update_cursor(g, &scanout->cursor); } @@ -1205,11 +1275,6 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) Error *local_err = NULL; int i; - if (virtio_host_has_feature(vdev, VIRTIO_F_IOMMU_PLATFORM)) { - error_setg(errp, "virtio-gpu does not support vIOMMU yet"); - return; - } - if (g->conf.max_outputs > VIRTIO_GPU_MAX_SCANOUTS) { error_setg(errp, "invalid max_outputs > %d", VIRTIO_GPU_MAX_SCANOUTS); return; @@ -1235,10 +1300,9 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) } } - g->config_size = sizeof(struct virtio_gpu_config); g->virtio_config.num_scanouts = cpu_to_le32(g->conf.max_outputs); virtio_init(VIRTIO_DEVICE(g), "virtio-gpu", VIRTIO_ID_GPU, - g->config_size); + sizeof(struct virtio_gpu_config)); g->req_state[0].width = g->conf.xres; g->req_state[0].height = g->conf.yres; @@ -1265,7 +1329,6 @@ static void virtio_gpu_device_realize(DeviceState *qdev, Error **errp) QTAILQ_INIT(&g->fenceq); g->enabled_output_bitmask = 1; - g->qdev = qdev; for (i = 0; i < g->conf.max_outputs; i++) { g->scanout[i].con = @@ -1293,6 +1356,7 @@ static void virtio_gpu_reset(VirtIODevice *vdev) { VirtIOGPU *g = VIRTIO_GPU(vdev); struct virtio_gpu_simple_resource *res, *tmp; + struct virtio_gpu_ctrl_command *cmd; int i; g->enable = 0; @@ -1309,9 +1373,26 @@ static void virtio_gpu_reset(VirtIODevice *vdev) g->scanout[i].ds = NULL; } + while (!QTAILQ_EMPTY(&g->cmdq)) { + cmd = QTAILQ_FIRST(&g->cmdq); + QTAILQ_REMOVE(&g->cmdq, cmd, next); + g_free(cmd); + } + + while (!QTAILQ_EMPTY(&g->fenceq)) { + cmd = QTAILQ_FIRST(&g->fenceq); + QTAILQ_REMOVE(&g->fenceq, cmd, next); + g->inflight--; + g_free(cmd); + } + #ifdef CONFIG_VIRGL if (g->use_virgl_renderer) { - virtio_gpu_virgl_reset(g); + if (g->renderer_blocked) { + g->renderer_reset = true; + } else { + virtio_gpu_virgl_reset(g); + } g->use_virgl_renderer = 0; } #endif @@ -1353,6 +1434,8 @@ static Property virtio_gpu_properties[] = { DEFINE_PROP_BIT("stats", VirtIOGPU, conf.flags, VIRTIO_GPU_FLAG_STATS_ENABLED, false), #endif + DEFINE_PROP_BIT("edid", VirtIOGPU, conf.flags, + VIRTIO_GPU_FLAG_EDID_ENABLED, false), DEFINE_PROP_UINT32("xres", VirtIOGPU, conf.xres, 1024), DEFINE_PROP_UINT32("yres", VirtIOGPU, conf.yres, 768), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index 701d980872..a2b803b75f 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -3,6 +3,7 @@ #include "hw/pci/pci.h" #include "vga_int.h" #include "hw/virtio/virtio-pci.h" +#include "hw/virtio/virtio-gpu.h" #include "qapi/error.h" /* @@ -11,6 +12,10 @@ #define TYPE_VIRTIO_VGA "virtio-vga" #define VIRTIO_VGA(obj) \ OBJECT_CHECK(VirtIOVGA, (obj), TYPE_VIRTIO_VGA) +#define VIRTIO_VGA_GET_CLASS(obj) \ + OBJECT_GET_CLASS(VirtIOVGAClass, obj, TYPE_VIRTIO_VGA) +#define VIRTIO_VGA_CLASS(klass) \ + OBJECT_CLASS_CHECK(VirtIOVGAClass, klass, TYPE_VIRTIO_VGA) typedef struct VirtIOVGA { VirtIOPCIProxy parent_obj; @@ -19,6 +24,11 @@ typedef struct VirtIOVGA { MemoryRegion vga_mrs[3]; } VirtIOVGA; +typedef struct VirtIOVGAClass { + VirtioPCIClass parent_class; + DeviceReset parent_reset; +} VirtIOVGAClass; + static void virtio_vga_invalidate_display(void *opaque) { VirtIOVGA *vvga = opaque; @@ -75,16 +85,6 @@ static void virtio_vga_gl_block(void *opaque, bool block) } } -static void virtio_vga_disable_scanout(VirtIOGPU *g, int scanout_id) -{ - VirtIOVGA *vvga = container_of(g, VirtIOVGA, vdev); - - if (scanout_id == 0) { - /* reset surface if needed */ - vvga->vga.graphic_mode = -1; - } -} - static const GraphicHwOps virtio_vga_ops = { .invalidate = virtio_vga_invalidate_display, .gfx_update = virtio_vga_update_display, @@ -163,10 +163,9 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) /* add stdvga mmio regions */ pci_std_vga_mmio_region_init(vga, OBJECT(vvga), &vpci_dev->modern_bar, - vvga->vga_mrs, true); + vvga->vga_mrs, true, false); vga->con = g->scanout[0].con; - g->disable_scanout = virtio_vga_disable_scanout; graphic_console_set_hwops(vga->con, &virtio_vga_ops, vvga); for (i = 0; i < g->conf.max_outputs; i++) { @@ -178,9 +177,14 @@ static void virtio_vga_realize(VirtIOPCIProxy *vpci_dev, Error **errp) static void virtio_vga_reset(DeviceState *dev) { + VirtIOVGAClass *klass = VIRTIO_VGA_GET_CLASS(dev); VirtIOVGA *vvga = VIRTIO_VGA(dev); - vvga->vdev.enable = 0; + /* reset virtio-gpu */ + klass->parent_reset(dev); + + /* reset vga */ + vga_common_reset(&vvga->vga); vga_dirty_log_start(&vvga->vga); } @@ -193,13 +197,15 @@ static void virtio_vga_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + VirtIOVGAClass *v = VIRTIO_VGA_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); dc->props = virtio_vga_properties; - dc->reset = virtio_vga_reset; dc->vmsd = &vmstate_virtio_vga; dc->hotpluggable = false; + device_class_set_parent_reset(dc, virtio_vga_reset, + &v->parent_reset); k->realize = virtio_vga_realize; pcidev_k->romfile = "vgabios-virtio.bin"; @@ -214,17 +220,17 @@ static void virtio_vga_inst_initfn(Object *obj) TYPE_VIRTIO_GPU); } -static TypeInfo virtio_vga_info = { - .name = TYPE_VIRTIO_VGA, - .parent = TYPE_VIRTIO_PCI, +static VirtioPCIDeviceTypeInfo virtio_vga_info = { + .generic_name = TYPE_VIRTIO_VGA, .instance_size = sizeof(struct VirtIOVGA), .instance_init = virtio_vga_inst_initfn, + .class_size = sizeof(struct VirtIOVGAClass), .class_init = virtio_vga_class_init, }; static void virtio_vga_register_types(void) { - type_register_static(&virtio_vga_info); + virtio_pci_types_register(&virtio_vga_info); } type_init(virtio_vga_register_types) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 0bbb78b9a6..afbf1c5973 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1116,7 +1116,6 @@ static inline void vmsvga_check_size(struct vmsvga_state_s *s) static void vmsvga_update_display(void *opaque) { struct vmsvga_state_s *s = opaque; - DisplaySurface *surface; if (!s->enable || !s->config) { /* in standard vga mode */ @@ -1125,15 +1124,13 @@ static void vmsvga_update_display(void *opaque) } vmsvga_check_size(s); - surface = qemu_console_surface(s->vga.con); vmsvga_fifo_run(s); vmsvga_update_rect_flush(s); if (s->invalidated) { s->invalidated = 0; - dpy_gfx_update(s->vga.con, 0, 0, - surface_width(surface), surface_height(surface)); + dpy_gfx_update_full(s->vga.con); } } diff --git a/hw/display/xenfb.c b/hw/display/xenfb.c index 0330dc6f61..6202f1150e 100644 --- a/hw/display/xenfb.c +++ b/hw/display/xenfb.c @@ -30,7 +30,7 @@ #include "hw/hw.h" #include "ui/input.h" #include "ui/console.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include #include @@ -46,7 +46,7 @@ /* -------------------------------------------------------------------- */ struct common { - struct XenDevice xendev; /* must be first */ + struct XenLegacyDevice xendev; /* must be first */ void *page; }; @@ -342,14 +342,14 @@ static QemuInputHandler xenfb_rel_mouse = { .sync = xenfb_mouse_sync, }; -static int input_init(struct XenDevice *xendev) +static int input_init(struct XenLegacyDevice *xendev) { xenstore_write_be_int(xendev, "feature-abs-pointer", 1); xenstore_write_be_int(xendev, "feature-raw-pointer", 1); return 0; } -static int input_initialise(struct XenDevice *xendev) +static int input_initialise(struct XenLegacyDevice *xendev) { struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); int rc; @@ -361,7 +361,7 @@ static int input_initialise(struct XenDevice *xendev) return 0; } -static void input_connected(struct XenDevice *xendev) +static void input_connected(struct XenLegacyDevice *xendev) { struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); @@ -395,7 +395,7 @@ static void input_connected(struct XenDevice *xendev) } } -static void input_disconnect(struct XenDevice *xendev) +static void input_disconnect(struct XenLegacyDevice *xendev) { struct XenInput *in = container_of(xendev, struct XenInput, c.xendev); @@ -410,7 +410,7 @@ static void input_disconnect(struct XenDevice *xendev) common_unbind(&in->c); } -static void input_event(struct XenDevice *xendev) +static void input_event(struct XenLegacyDevice *xendev) { struct XenInput *xenfb = container_of(xendev, struct XenInput, c.xendev); struct xenkbd_page *page = xenfb->c.page; @@ -867,7 +867,7 @@ static void xenfb_handle_events(struct XenFB *xenfb) page->out_cons = cons; } -static int fb_init(struct XenDevice *xendev) +static int fb_init(struct XenLegacyDevice *xendev) { #ifdef XENFB_TYPE_RESIZE xenstore_write_be_int(xendev, "feature-resize", 1); @@ -875,7 +875,7 @@ static int fb_init(struct XenDevice *xendev) return 0; } -static int fb_initialise(struct XenDevice *xendev) +static int fb_initialise(struct XenLegacyDevice *xendev) { struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); struct xenfb_page *fb_page; @@ -912,7 +912,7 @@ static int fb_initialise(struct XenDevice *xendev) return 0; } -static void fb_disconnect(struct XenDevice *xendev) +static void fb_disconnect(struct XenLegacyDevice *xendev) { struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); @@ -935,7 +935,8 @@ static void fb_disconnect(struct XenDevice *xendev) fb->bug_trigger = 0; } -static void fb_frontend_changed(struct XenDevice *xendev, const char *node) +static void fb_frontend_changed(struct XenLegacyDevice *xendev, + const char *node) { struct XenFB *fb = container_of(xendev, struct XenFB, c.xendev); @@ -953,7 +954,7 @@ static void fb_frontend_changed(struct XenDevice *xendev, const char *node) } } -static void fb_event(struct XenDevice *xendev) +static void fb_event(struct XenLegacyDevice *xendev) { struct XenFB *xenfb = container_of(xendev, struct XenFB, c.xendev); diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 6439bd05ef..11b09bd18c 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1186,8 +1186,7 @@ static void xlnx_dp_update_display(void *opaque) /* * XXX: We might want to update only what changed. */ - dpy_gfx_update(s->console, 0, 0, surface_width(s->g_plane.surface), - surface_height(s->g_plane.surface)); + dpy_gfx_update_full(s->console); } static const GraphicHwOps xlnx_dp_gfx_ops = { @@ -1261,7 +1260,7 @@ static void xlnx_dp_realize(DeviceState *dev, Error **errp) as.freq = 44100; as.nchannels = 2; - as.fmt = AUD_FMT_S16; + as.fmt = AUDIO_FORMAT_S16; as.endianness = 0; AUD_register_card("xlnx_dp.audio", &s->aud_card); diff --git a/hw/dma/Kconfig b/hw/dma/Kconfig new file mode 100644 index 0000000000..751dec5426 --- /dev/null +++ b/hw/dma/Kconfig @@ -0,0 +1,21 @@ +config RC4030 + bool + +config PL080 + bool + +config PL330 + bool + +config I82374 + bool + select I8257 + +config I8257 + bool + +config ZYNQ_DEVCFG + bool + +config STP2000 + bool diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 52675e97c9..3e1f13a4aa 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -26,6 +26,7 @@ #include "hw/isa/isa.h" #include "hw/dma/i8257.h" #include "qemu/main-loop.h" +#include "qemu/log.h" #include "trace.h" #define I8257(obj) \ @@ -185,7 +186,8 @@ static void i8257_write_cont(void *opaque, hwaddr nport, uint64_t data, switch (iport) { case 0x00: /* command */ if ((data != 0) && (data & CMD_NOT_SUPPORTED)) { - dolog("command %"PRIx64" not supported\n", data); + qemu_log_mask(LOG_UNIMP, "%s: cmd 0x%02"PRIx64" not supported\n", + __func__, data); return; } d->command = data; diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 7724c93b8f..ef15d3e628 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -11,8 +11,9 @@ #include "hw/sysbus.h" #include "exec/address-spaces.h" #include "qemu/log.h" +#include "hw/dma/pl080.h" +#include "qapi/error.h" -#define PL080_MAX_CHANNELS 8 #define PL080_CONF_E 0x1 #define PL080_CONF_M1 0x2 #define PL080_CONF_M2 0x4 @@ -30,36 +31,6 @@ #define PL080_CCTRL_D 0x02000000 #define PL080_CCTRL_S 0x01000000 -typedef struct { - uint32_t src; - uint32_t dest; - uint32_t lli; - uint32_t ctrl; - uint32_t conf; -} pl080_channel; - -#define TYPE_PL080 "pl080" -#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080) - -typedef struct PL080State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint8_t tc_int; - uint8_t tc_mask; - uint8_t err_int; - uint8_t err_mask; - uint32_t conf; - uint32_t sync; - uint32_t req_single; - uint32_t req_burst; - pl080_channel chan[PL080_MAX_CHANNELS]; - int nchannels; - /* Flag to avoid recursive DMA invocations. */ - int running; - qemu_irq irq; -} PL080State; - static const VMStateDescription vmstate_pl080_channel = { .name = "pl080_channel", .version_id = 1, @@ -105,11 +76,12 @@ static const unsigned char pl081_id[] = static void pl080_update(PL080State *s) { - if ((s->tc_int & s->tc_mask) - || (s->err_int & s->err_mask)) - qemu_irq_raise(s->irq); - else - qemu_irq_lower(s->irq); + bool tclevel = (s->tc_int & s->tc_mask); + bool errlevel = (s->err_int & s->err_mask); + + qemu_set_irq(s->interr, errlevel); + qemu_set_irq(s->inttc, tclevel); + qemu_set_irq(s->irq, errlevel || tclevel); } static void pl080_run(PL080State *s) @@ -138,7 +110,6 @@ static void pl080_run(PL080State *s) if ((s->conf & PL080_CONF_E) == 0) return; -hw_error("DMA active\n"); /* If we are already in the middle of a DMA operation then indicate that there may be new DMA requests and return immediately. */ if (s->running) { @@ -190,14 +161,16 @@ again: swidth = 1 << ((ch->ctrl >> 18) & 7); dwidth = 1 << ((ch->ctrl >> 21) & 7); for (n = 0; n < dwidth; n+= swidth) { - cpu_physical_memory_read(ch->src, buff + n, swidth); + address_space_read(&s->downstream_as, ch->src, + MEMTXATTRS_UNSPECIFIED, buff + n, swidth); if (ch->ctrl & PL080_CCTRL_SI) ch->src += swidth; } xsize = (dwidth < swidth) ? swidth : dwidth; /* ??? This may pad the value incorrectly for dwidth < 32. */ for (n = 0; n < xsize; n += dwidth) { - cpu_physical_memory_write(ch->dest + n, buff + n, dwidth); + address_space_write(&s->downstream_as, ch->dest + n, + MEMTXATTRS_UNSPECIFIED, buff + n, dwidth); if (ch->ctrl & PL080_CCTRL_DI) ch->dest += swidth; } @@ -207,19 +180,19 @@ again: if (size == 0) { /* Transfer complete. */ if (ch->lli) { - ch->src = address_space_ldl_le(&address_space_memory, + ch->src = address_space_ldl_le(&s->downstream_as, ch->lli, MEMTXATTRS_UNSPECIFIED, NULL); - ch->dest = address_space_ldl_le(&address_space_memory, + ch->dest = address_space_ldl_le(&s->downstream_as, ch->lli + 4, MEMTXATTRS_UNSPECIFIED, NULL); - ch->ctrl = address_space_ldl_le(&address_space_memory, + ch->ctrl = address_space_ldl_le(&s->downstream_as, ch->lli + 12, MEMTXATTRS_UNSPECIFIED, NULL); - ch->lli = address_space_ldl_le(&address_space_memory, + ch->lli = address_space_ldl_le(&s->downstream_as, ch->lli + 8, MEMTXATTRS_UNSPECIFIED, NULL); @@ -255,7 +228,7 @@ static uint64_t pl080_read(void *opaque, hwaddr offset, i = (offset & 0xe0) >> 5; if (i >= s->nchannels) goto bad_offset; - switch (offset >> 2) { + switch ((offset >> 2) & 7) { case 0: /* SrcAddr */ return s->chan[i].src; case 1: /* DestAddr */ @@ -316,7 +289,7 @@ static void pl080_write(void *opaque, hwaddr offset, i = (offset & 0xe0) >> 5; if (i >= s->nchannels) goto bad_offset; - switch (offset >> 2) { + switch ((offset >> 2) & 7) { case 0: /* SrcAddr */ s->chan[i].src = value; break; @@ -334,6 +307,7 @@ static void pl080_write(void *opaque, hwaddr offset, pl080_run(s); break; } + return; } switch (offset >> 2) { case 2: /* IntTCClear */ @@ -374,6 +348,30 @@ static const MemoryRegionOps pl080_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; +static void pl080_reset(DeviceState *dev) +{ + PL080State *s = PL080(dev); + int i; + + s->tc_int = 0; + s->tc_mask = 0; + s->err_int = 0; + s->err_mask = 0; + s->conf = 0; + s->sync = 0; + s->req_single = 0; + s->req_burst = 0; + s->running = 0; + + for (i = 0; i < s->nchannels; i++) { + s->chan[i].src = 0; + s->chan[i].dest = 0; + s->chan[i].lli = 0; + s->chan[i].ctrl = 0; + s->chan[i].conf = 0; + } +} + static void pl080_init(Object *obj) { SysBusDevice *sbd = SYS_BUS_DEVICE(obj); @@ -382,9 +380,23 @@ static void pl080_init(Object *obj) memory_region_init_io(&s->iomem, OBJECT(s), &pl080_ops, s, "pl080", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); + sysbus_init_irq(sbd, &s->interr); + sysbus_init_irq(sbd, &s->inttc); s->nchannels = 8; } +static void pl080_realize(DeviceState *dev, Error **errp) +{ + PL080State *s = PL080(dev); + + if (!s->downstream) { + error_setg(errp, "PL080 'downstream' link not set"); + return; + } + + address_space_init(&s->downstream_as, s->downstream, "pl080-downstream"); +} + static void pl081_init(Object *obj) { PL080State *s = PL080(obj); @@ -392,11 +404,20 @@ static void pl081_init(Object *obj) s->nchannels = 2; } +static Property pl080_properties[] = { + DEFINE_PROP_LINK("downstream", PL080State, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_END_OF_LIST(), +}; + static void pl080_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); dc->vmsd = &vmstate_pl080; + dc->realize = pl080_realize; + dc->props = pl080_properties; + dc->reset = pl080_reset; } static const TypeInfo pl080_info = { @@ -408,7 +429,7 @@ static const TypeInfo pl080_info = { }; static const TypeInfo pl081_info = { - .name = "pl081", + .name = TYPE_PL081, .parent = TYPE_PL080, .instance_init = pl081_init, }; diff --git a/hw/dma/puv3_dma.c b/hw/dma/puv3_dma.c index b97a6c1767..c89eade029 100644 --- a/hw/dma/puv3_dma.c +++ b/hw/dma/puv3_dma.c @@ -76,7 +76,7 @@ static const MemoryRegionOps puv3_dma_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int puv3_dma_init(SysBusDevice *dev) +static void puv3_dma_realize(DeviceState *dev, Error **errp) { PUV3DMAState *s = PUV3_DMA(dev); int i; @@ -87,16 +87,14 @@ static int puv3_dma_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &puv3_dma_ops, s, "puv3_dma", PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } static void puv3_dma_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = puv3_dma_init; + dc->realize = puv3_dma_realize; } static const TypeInfo puv3_dma_info = { diff --git a/hw/dma/pxa2xx_dma.c b/hw/dma/pxa2xx_dma.c index f4eb26cf17..d498de8ffe 100644 --- a/hw/dma/pxa2xx_dma.c +++ b/hw/dma/pxa2xx_dma.c @@ -228,7 +228,7 @@ static void pxa2xx_dma_run(PXA2xxDMAState *s) !(ch->state & DCSR_NODESCFETCH)) pxa2xx_dma_descriptor_fetch(s, c); break; - } + } } ch->cmd = (ch->cmd & ~DCMD_LEN) | length; @@ -283,7 +283,7 @@ static uint64_t pxa2xx_dma_read(void *opaque, hwaddr offset, case DCSR0 ... DCSR31: channel = offset >> 2; - if (s->chan[channel].request) + if (s->chan[channel].request) return s->chan[channel].state | DCSR_REQPEND; return s->chan[channel].state; diff --git a/hw/dma/soc_dma.c b/hw/dma/soc_dma.c index 45516241c6..58502721fa 100644 --- a/hw/dma/soc_dma.c +++ b/hw/dma/soc_dma.c @@ -345,7 +345,7 @@ void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, while (entry < dma->memmap + dma->memmap_size && entry->addr <= virt_base) entry ++; - } + } memmove(entry + 1, entry, (uint8_t *) (dma->memmap + dma->memmap_size ++) - diff --git a/hw/dma/trace-events b/hw/dma/trace-events index 22f53d0ff2..e4498428c5 100644 --- a/hw/dma/trace-events +++ b/hw/dma/trace-events @@ -1,12 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/dma/rc4030.c +# rc4030.c jazzio_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" jazzio_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" rc4030_read(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" rc4030_write(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" -# hw/dma/sparc32_dma.c +# sparc32_dma.c ledma_memory_read(uint64_t addr, int len) "DMA read addr 0x%"PRIx64 " len %d" ledma_memory_write(uint64_t addr, int len) "DMA write addr 0x%"PRIx64 " len %d" sparc32_dma_set_irq_raise(void) "Raise IRQ" @@ -18,5 +18,5 @@ sparc32_dma_mem_writel(uint64_t addr, uint32_t old, uint32_t val) "write dmareg sparc32_dma_enable_raise(void) "Raise DMA enable" sparc32_dma_enable_lower(void) "Lower DMA enable" -# hw/dma/i8257.c +# i8257.c i8257_unregistered_dma(int nchan, int dma_pos, int dma_len) "unregistered DMA channel used nchan=%d dma_pos=%d dma_len=%d" diff --git a/hw/gpio/Kconfig b/hw/gpio/Kconfig new file mode 100644 index 0000000000..9227cb5598 --- /dev/null +++ b/hw/gpio/Kconfig @@ -0,0 +1,9 @@ +config MAX7310 + bool + depends on I2C + +config PL061 + bool + +config GPIO_KEY + bool diff --git a/hw/gpio/Makefile.objs b/hw/gpio/Makefile.objs index fa0a72e6d0..e5da0cb54f 100644 --- a/hw/gpio/Makefile.objs +++ b/hw/gpio/Makefile.objs @@ -8,3 +8,4 @@ common-obj-$(CONFIG_GPIO_KEY) += gpio_key.o obj-$(CONFIG_OMAP) += omap_gpio.o obj-$(CONFIG_IMX) += imx_gpio.o obj-$(CONFIG_RASPI) += bcm2835_gpio.o +obj-$(CONFIG_NRF51_SOC) += nrf51_gpio.o diff --git a/hw/gpio/max7310.c b/hw/gpio/max7310.c index a560e3afd2..c6f686c3eb 100644 --- a/hw/gpio/max7310.c +++ b/hw/gpio/max7310.c @@ -39,7 +39,7 @@ static void max7310_reset(DeviceState *dev) s->command = 0x00; } -static int max7310_rx(I2CSlave *i2c) +static uint8_t max7310_rx(I2CSlave *i2c) { MAX7310State *s = MAX7310(i2c); @@ -118,7 +118,7 @@ static int max7310_tx(I2CSlave *i2c, uint8_t data) break; case 0x00: /* Input port - ignore writes */ - break; + break; default: #ifdef VERBOSE printf("%s: unknown register %02x\n", __func__, s->command); diff --git a/hw/gpio/nrf51_gpio.c b/hw/gpio/nrf51_gpio.c new file mode 100644 index 0000000000..87a2f2a0dc --- /dev/null +++ b/hw/gpio/nrf51_gpio.c @@ -0,0 +1,315 @@ +/* + * nRF51 System-on-Chip general purpose input/output register definition + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Copyright 2018 Steffen Görtz + * + * 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 "qemu/log.h" +#include "hw/gpio/nrf51_gpio.h" +#include "trace.h" + +/* + * Check if the output driver is connected to the direction switch + * given the current configuration and logic level. + * It is not differentiated between standard and "high"(-power) drive modes. + */ +static bool is_connected(uint32_t config, uint32_t level) +{ + bool state; + uint32_t drive_config = extract32(config, 8, 3); + + switch (drive_config) { + case 0 ... 3: + state = true; + break; + case 4 ... 5: + state = level != 0; + break; + case 6 ... 7: + state = level == 0; + break; + default: + g_assert_not_reached(); + break; + } + + return state; +} + +static int pull_value(uint32_t config) +{ + int pull = extract32(config, 2, 2); + if (pull == NRF51_GPIO_PULLDOWN) { + return 0; + } else if (pull == NRF51_GPIO_PULLUP) { + return 1; + } + return -1; +} + +static void update_output_irq(NRF51GPIOState *s, size_t i, + bool connected, bool level) +{ + int64_t irq_level = connected ? level : -1; + bool old_connected = extract32(s->old_out_connected, i, 1); + bool old_level = extract32(s->old_out, i, 1); + + if ((old_connected != connected) || (old_level != level)) { + qemu_set_irq(s->output[i], irq_level); + trace_nrf51_gpio_update_output_irq(i, irq_level); + } + + s->old_out = deposit32(s->old_out, i, 1, level); + s->old_out_connected = deposit32(s->old_out_connected, i, 1, connected); +} + +static void update_state(NRF51GPIOState *s) +{ + int pull; + size_t i; + bool connected_out, dir, connected_in, out, in, input; + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + pull = pull_value(s->cnf[i]); + dir = extract32(s->cnf[i], 0, 1); + connected_in = extract32(s->in_mask, i, 1); + out = extract32(s->out, i, 1); + in = extract32(s->in, i, 1); + input = !extract32(s->cnf[i], 1, 1); + connected_out = is_connected(s->cnf[i], out) && dir; + + if (!input) { + if (pull >= 0) { + /* Input buffer disconnected from external drives */ + s->in = deposit32(s->in, i, 1, pull); + } + } else { + if (connected_out && connected_in && out != in) { + /* Pin both driven externally and internally */ + qemu_log_mask(LOG_GUEST_ERROR, + "GPIO pin %zu short circuited\n", i); + } + if (!connected_in) { + /* + * Floating input: the output stimulates IN if connected, + * otherwise pull-up/pull-down resistors put a value on both + * IN and OUT. + */ + if (pull >= 0 && !connected_out) { + connected_out = true; + out = pull; + } + if (connected_out) { + s->in = deposit32(s->in, i, 1, out); + } + } + } + update_output_irq(s, i, connected_out, out); + } +} + +/* + * Direction is exposed in both the DIR register and the DIR bit + * of each PINs CNF configuration register. Reflect bits for pins in DIR + * to individual pin configuration registers. + */ +static void reflect_dir_bit_in_cnf(NRF51GPIOState *s) +{ + size_t i; + + uint32_t value = s->dir; + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + s->cnf[i] = (s->cnf[i] & ~(1UL)) | ((value >> i) & 0x01); + } +} + +static uint64_t nrf51_gpio_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51GPIOState *s = NRF51_GPIO(opaque); + uint64_t r = 0; + size_t idx; + + switch (offset) { + case NRF51_GPIO_REG_OUT ... NRF51_GPIO_REG_OUTCLR: + r = s->out; + break; + + case NRF51_GPIO_REG_IN: + r = s->in; + break; + + case NRF51_GPIO_REG_DIR ... NRF51_GPIO_REG_DIRCLR: + r = s->dir; + break; + + case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: + idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; + r = s->cnf[idx]; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_nrf51_gpio_read(offset, r); + + return r; +} + +static void nrf51_gpio_write(void *opaque, hwaddr offset, + uint64_t value, unsigned int size) +{ + NRF51GPIOState *s = NRF51_GPIO(opaque); + size_t idx; + + trace_nrf51_gpio_write(offset, value); + + switch (offset) { + case NRF51_GPIO_REG_OUT: + s->out = value; + break; + + case NRF51_GPIO_REG_OUTSET: + s->out |= value; + break; + + case NRF51_GPIO_REG_OUTCLR: + s->out &= ~value; + break; + + case NRF51_GPIO_REG_DIR: + s->dir = value; + reflect_dir_bit_in_cnf(s); + break; + + case NRF51_GPIO_REG_DIRSET: + s->dir |= value; + reflect_dir_bit_in_cnf(s); + break; + + case NRF51_GPIO_REG_DIRCLR: + s->dir &= ~value; + reflect_dir_bit_in_cnf(s); + break; + + case NRF51_GPIO_REG_CNF_START ... NRF51_GPIO_REG_CNF_END: + idx = (offset - NRF51_GPIO_REG_CNF_START) / 4; + s->cnf[idx] = value; + /* + * direction is exposed in both the DIR register and the DIR bit + * of each PINs CNF configuration register. + */ + s->dir = (s->dir & ~(1UL << idx)) | ((value & 0x01) << idx); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + update_state(s); +} + +static const MemoryRegionOps gpio_ops = { + .read = nrf51_gpio_read, + .write = nrf51_gpio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static void nrf51_gpio_set(void *opaque, int line, int value) +{ + NRF51GPIOState *s = NRF51_GPIO(opaque); + + trace_nrf51_gpio_set(line, value); + + assert(line >= 0 && line < NRF51_GPIO_PINS); + + s->in_mask = deposit32(s->in_mask, line, 1, value >= 0); + if (value >= 0) { + s->in = deposit32(s->in, line, 1, value != 0); + } + + update_state(s); +} + +static void nrf51_gpio_reset(DeviceState *dev) +{ + NRF51GPIOState *s = NRF51_GPIO(dev); + size_t i; + + s->out = 0; + s->old_out = 0; + s->old_out_connected = 0; + s->in = 0; + s->in_mask = 0; + s->dir = 0; + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + s->cnf[i] = 0x00000002; + } +} + +static const VMStateDescription vmstate_nrf51_gpio = { + .name = TYPE_NRF51_GPIO, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(out, NRF51GPIOState), + VMSTATE_UINT32(in, NRF51GPIOState), + VMSTATE_UINT32(in_mask, NRF51GPIOState), + VMSTATE_UINT32(dir, NRF51GPIOState), + VMSTATE_UINT32_ARRAY(cnf, NRF51GPIOState, NRF51_GPIO_PINS), + VMSTATE_UINT32(old_out, NRF51GPIOState), + VMSTATE_UINT32(old_out_connected, NRF51GPIOState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_gpio_init(Object *obj) +{ + NRF51GPIOState *s = NRF51_GPIO(obj); + + memory_region_init_io(&s->mmio, obj, &gpio_ops, s, + TYPE_NRF51_GPIO, NRF51_GPIO_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + + qdev_init_gpio_in(DEVICE(s), nrf51_gpio_set, NRF51_GPIO_PINS); + qdev_init_gpio_out(DEVICE(s), s->output, NRF51_GPIO_PINS); +} + +static void nrf51_gpio_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &vmstate_nrf51_gpio; + dc->reset = nrf51_gpio_reset; + dc->desc = "nRF51 GPIO"; +} + +static const TypeInfo nrf51_gpio_info = { + .name = TYPE_NRF51_GPIO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51GPIOState), + .instance_init = nrf51_gpio_init, + .class_init = nrf51_gpio_class_init +}; + +static void nrf51_gpio_register_types(void) +{ + type_register_static(&nrf51_gpio_info); +} + +type_init(nrf51_gpio_register_types) diff --git a/hw/gpio/puv3_gpio.c b/hw/gpio/puv3_gpio.c index 445afccf9f..33241b8564 100644 --- a/hw/gpio/puv3_gpio.c +++ b/hw/gpio/puv3_gpio.c @@ -99,36 +99,35 @@ static const MemoryRegionOps puv3_gpio_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int puv3_gpio_init(SysBusDevice *dev) +static void puv3_gpio_realize(DeviceState *dev, Error **errp) { PUV3GPIOState *s = PUV3_GPIO(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); s->reg_GPLR = 0; s->reg_GPDR = 0; /* FIXME: these irqs not handled yet */ - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW0]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW1]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW2]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW3]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW4]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW5]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW6]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOLOW7]); - sysbus_init_irq(dev, &s->irq[PUV3_IRQS_GPIOHIGH]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW0]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW1]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW2]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW3]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW4]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW5]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW6]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOLOW7]); + sysbus_init_irq(sbd, &s->irq[PUV3_IRQS_GPIOHIGH]); memory_region_init_io(&s->iomem, OBJECT(s), &puv3_gpio_ops, s, "puv3_gpio", PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; + sysbus_init_mmio(sbd, &s->iomem); } static void puv3_gpio_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = puv3_gpio_init; + dc->realize = puv3_gpio_realize; } static const TypeInfo puv3_gpio_info = { diff --git a/hw/gpio/trace-events b/hw/gpio/trace-events new file mode 100644 index 0000000000..c1271fdfb2 --- /dev/null +++ b/hw/gpio/trace-events @@ -0,0 +1,7 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# nrf51_gpio.c +nrf51_gpio_read(uint64_t offset, uint64_t r) "offset 0x%" PRIx64 " value 0x%" PRIx64 +nrf51_gpio_write(uint64_t offset, uint64_t value) "offset 0x%" PRIx64 " value 0x%" PRIx64 +nrf51_gpio_set(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 +nrf51_gpio_update_output_irq(int64_t line, int64_t value) "line %" PRIi64 " value %" PRIi64 diff --git a/hw/hppa/Kconfig b/hw/hppa/Kconfig new file mode 100644 index 0000000000..6e5d74a825 --- /dev/null +++ b/hw/hppa/Kconfig @@ -0,0 +1,12 @@ +config DINO + bool + imply PCI_DEVICES + imply E1000_PCI + imply VIRTIO_VGA + select PCI + select SERIAL + select ISA_BUS + select I8259 + select IDE_CMD646 + select MC146818RTC + select LSI_SCSI_PCI diff --git a/hw/hppa/Makefile.objs b/hw/hppa/Makefile.objs index bef241ed25..67838f50a3 100644 --- a/hw/hppa/Makefile.objs +++ b/hw/hppa/Makefile.objs @@ -1 +1 @@ -obj-y += machine.o pci.o dino.o +obj-$(CONFIG_DINO) += pci.o machine.o dino.o diff --git a/hw/hppa/dino.c b/hw/hppa/dino.c index 564b938e3a..4d1380c51f 100644 --- a/hw/hppa/dino.c +++ b/hw/hppa/dino.c @@ -15,7 +15,6 @@ #include "qapi/error.h" #include "cpu.h" #include "hw/hw.h" -#include "hw/devices.h" #include "sysemu/sysemu.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" @@ -105,6 +104,7 @@ typedef struct DinoState { MemoryRegion bm; MemoryRegion bm_ram_alias; MemoryRegion bm_pci_alias; + MemoryRegion bm_cpu_alias; MemoryRegion cpu0_eir_mem; } DinoState; @@ -177,7 +177,7 @@ static MemTxResult dino_chip_read_with_attrs(void *opaque, hwaddr addr, case DINO_PCI_IO_DATA ... DINO_PCI_IO_DATA + 3: /* Read from PCI IO space. */ io = &address_space_io; - ioaddr = s->parent_obj.config_reg; + ioaddr = s->parent_obj.config_reg + (addr & 3); switch (size) { case 1: val = address_space_ldub(io, ioaddr, attrs, &ret); @@ -249,7 +249,7 @@ static MemTxResult dino_chip_write_with_attrs(void *opaque, hwaddr addr, case DINO_IO_DATA ... DINO_PCI_IO_DATA + 3: /* Write into PCI IO space. */ io = &address_space_io; - ioaddr = s->parent_obj.config_reg; + ioaddr = s->parent_obj.config_reg + (addr & 3); switch (size) { case 1: address_space_stb(io, ioaddr, val, attrs, &ret); @@ -359,6 +359,27 @@ static const MemoryRegionOps dino_config_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static uint64_t dino_config_addr_read(void *opaque, hwaddr addr, unsigned len) +{ + PCIHostState *s = opaque; + return s->config_reg; +} + +static void dino_config_addr_write(void *opaque, hwaddr addr, + uint64_t val, unsigned len) +{ + PCIHostState *s = opaque; + s->config_reg = val & ~3U; +} + +static const MemoryRegionOps dino_config_addr_ops = { + .read = dino_config_addr_read, + .write = dino_config_addr_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, +}; + static AddressSpace *dino_pcihost_set_iommu(PCIBus *bus, void *opaque, int devfn) { @@ -439,7 +460,7 @@ PCIBus *dino_init(MemoryRegion *addr_space, /* Dino PCI config. */ memory_region_init_io(&s->parent_obj.conf_mem, OBJECT(&s->parent_obj), - &pci_host_conf_be_ops, dev, "pci-conf-idx", 4); + &dino_config_addr_ops, dev, "pci-conf-idx", 4); memory_region_init_io(&s->parent_obj.data_mem, OBJECT(&s->parent_obj), &dino_config_data_ops, dev, "pci-conf-data", 4); memory_region_add_subregion(&s->this_mem, DINO_PCI_CONFIG_ADDR, @@ -473,12 +494,17 @@ PCIBus *dino_init(MemoryRegion *addr_space, memory_region_init_alias(&s->bm_pci_alias, OBJECT(s), "bm-pci", &s->pci_mem, 0xf0000000 + DINO_MEM_CHUNK_SIZE, - 31 * DINO_MEM_CHUNK_SIZE); + 30 * DINO_MEM_CHUNK_SIZE); + memory_region_init_alias(&s->bm_cpu_alias, OBJECT(s), + "bm-cpu", addr_space, 0xfff00000, + 0xfffff); memory_region_add_subregion(&s->bm, 0, &s->bm_ram_alias); memory_region_add_subregion(&s->bm, 0xf0000000 + DINO_MEM_CHUNK_SIZE, &s->bm_pci_alias); + memory_region_add_subregion(&s->bm, 0xfff00000, + &s->bm_cpu_alias); address_space_init(&s->bm_as, &s->bm, "pci-bm"); pci_setup_iommu(b, dino_pcihost_set_iommu, s); @@ -488,17 +514,10 @@ PCIBus *dino_init(MemoryRegion *addr_space, return b; } -static int dino_pcihost_init(SysBusDevice *dev) -{ - return 0; -} - static void dino_pcihost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = dino_pcihost_init; dc->vmsd = &vmstate_dino; } diff --git a/hw/hppa/hppa_hardware.h b/hw/hppa/hppa_hardware.h index 2c61b1f77c..af2f5ee2bd 100644 --- a/hw/hppa/hppa_hardware.h +++ b/hw/hppa/hppa_hardware.h @@ -19,7 +19,7 @@ #define LASI_PS2KBD_HPA 0xffd08000 #define LASI_PS2MOU_HPA 0xffd08100 #define LASI_GFX_HPA 0xf8000000 -#define CPU_HPA 0xfff10000 +#define CPU_HPA 0xfffb0000 #define MEMORY_HPA 0xfffbf000 #define PCI_HPA DINO_HPA /* PCI bus */ @@ -36,5 +36,5 @@ #define PORT_SERIAL1 (DINO_UART_HPA + 0x800) #define PORT_SERIAL2 (LASI_UART_HPA + 0x800) -#define HPPA_MAX_CPUS 32 /* max. number of SMP CPUs */ +#define HPPA_MAX_CPUS 8 /* max. number of SMP CPUs */ #define CPU_CLOCK_MHZ 250 /* emulate a 250 MHz CPU */ diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index cf7c61c6cc..d1b1d3caa4 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -59,6 +59,7 @@ static void machine_hppa_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + DeviceState *dev; PCIBus *pci_bus; ISABus *isa_bus; qemu_irq rtc_irq, serial_irq; @@ -115,7 +116,8 @@ static void machine_hppa_init(MachineState *machine) } /* SCSI disk setup. */ - lsi53c895a_create(pci_bus); + dev = DEVICE(pci_create_simple(pci_bus, -1, "lsi53c895a")); + lsi53c8xx_handle_legacy_cmdline(dev); /* Network setup. e1000 is good enough, failing Tulip support. */ for (i = 0; i < nb_nics; i++) { @@ -133,8 +135,8 @@ static void machine_hppa_init(MachineState *machine) exit(1); } - size = load_elf(firmware_filename, NULL, - NULL, &firmware_entry, &firmware_low, &firmware_high, + size = load_elf(firmware_filename, NULL, NULL, NULL, + &firmware_entry, &firmware_low, &firmware_high, true, EM_PARISC, 0, 0); /* Unfortunately, load_elf sign-extends reading elf32. */ @@ -163,7 +165,7 @@ static void machine_hppa_init(MachineState *machine) /* Load kernel */ if (kernel_filename) { - size = load_elf(kernel_filename, &cpu_hppa_to_phys, + size = load_elf(kernel_filename, NULL, &cpu_hppa_to_phys, NULL, &kernel_entry, &kernel_low, &kernel_high, true, EM_PARISC, 0, 0); @@ -189,7 +191,7 @@ static void machine_hppa_init(MachineState *machine) if (initrd_filename) { ram_addr_t initrd_base; - long initrd_size; + int64_t initrd_size; initrd_size = get_image_size(initrd_filename); if (initrd_size < 0) { diff --git a/hw/hppa/trace-events b/hw/hppa/trace-events index 14c67937e1..4e2acb6176 100644 --- a/hw/hppa/trace-events +++ b/hw/hppa/trace-events @@ -1,4 +1,4 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/hppa/pci.c +# pci.c hppa_pci_iack_write(void) "" diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig new file mode 100644 index 0000000000..a1fa8ff9be --- /dev/null +++ b/hw/hyperv/Kconfig @@ -0,0 +1,8 @@ +config HYPERV + bool + depends on KVM + +config HYPERV_TESTDEV + bool + default y if TEST_DEVICES + depends on HYPERV diff --git a/hw/hyperv/Makefile.objs b/hw/hyperv/Makefile.objs new file mode 100644 index 0000000000..edaca2f763 --- /dev/null +++ b/hw/hyperv/Makefile.objs @@ -0,0 +1,2 @@ +obj-y += hyperv.o +obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c new file mode 100644 index 0000000000..8758635227 --- /dev/null +++ b/hw/hyperv/hyperv.c @@ -0,0 +1,658 @@ +/* + * Hyper-V guest/hypervisor interaction + * + * Copyright (c) 2015-2018 Virtuozzo International GmbH. + * + * 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/main-loop.h" +#include "qapi/error.h" +#include "exec/address-spaces.h" +#include "sysemu/kvm.h" +#include "qemu/bitops.h" +#include "qemu/error-report.h" +#include "qemu/queue.h" +#include "qemu/rcu.h" +#include "qemu/rcu_queue.h" +#include "hw/hyperv/hyperv.h" + +typedef struct SynICState { + DeviceState parent_obj; + + CPUState *cs; + + bool enabled; + hwaddr msg_page_addr; + hwaddr event_page_addr; + MemoryRegion msg_page_mr; + MemoryRegion event_page_mr; + struct hyperv_message_page *msg_page; + struct hyperv_event_flags_page *event_page; +} SynICState; + +#define TYPE_SYNIC "hyperv-synic" +#define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC) + +static SynICState *get_synic(CPUState *cs) +{ + return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); +} + +static void synic_update(SynICState *synic, bool enable, + hwaddr msg_page_addr, hwaddr event_page_addr) +{ + + synic->enabled = enable; + if (synic->msg_page_addr != msg_page_addr) { + if (synic->msg_page_addr) { + memory_region_del_subregion(get_system_memory(), + &synic->msg_page_mr); + } + if (msg_page_addr) { + memory_region_add_subregion(get_system_memory(), msg_page_addr, + &synic->msg_page_mr); + } + synic->msg_page_addr = msg_page_addr; + } + if (synic->event_page_addr != event_page_addr) { + if (synic->event_page_addr) { + memory_region_del_subregion(get_system_memory(), + &synic->event_page_mr); + } + if (event_page_addr) { + memory_region_add_subregion(get_system_memory(), event_page_addr, + &synic->event_page_mr); + } + synic->event_page_addr = event_page_addr; + } +} + +void hyperv_synic_update(CPUState *cs, bool enable, + hwaddr msg_page_addr, hwaddr event_page_addr) +{ + SynICState *synic = get_synic(cs); + + if (!synic) { + return; + } + + synic_update(synic, enable, msg_page_addr, event_page_addr); +} + +static void synic_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SynICState *synic = SYNIC(dev); + char *msgp_name, *eventp_name; + uint32_t vp_index; + + /* memory region names have to be globally unique */ + vp_index = hyperv_vp_index(synic->cs); + msgp_name = g_strdup_printf("synic-%u-msg-page", vp_index); + eventp_name = g_strdup_printf("synic-%u-event-page", vp_index); + + memory_region_init_ram(&synic->msg_page_mr, obj, msgp_name, + sizeof(*synic->msg_page), &error_abort); + memory_region_init_ram(&synic->event_page_mr, obj, eventp_name, + sizeof(*synic->event_page), &error_abort); + synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); + synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); + + g_free(msgp_name); + g_free(eventp_name); +} +static void synic_reset(DeviceState *dev) +{ + SynICState *synic = SYNIC(dev); + memset(synic->msg_page, 0, sizeof(*synic->msg_page)); + memset(synic->event_page, 0, sizeof(*synic->event_page)); + synic_update(synic, false, 0, 0); +} + +static void synic_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = synic_realize; + dc->reset = synic_reset; + dc->user_creatable = false; +} + +void hyperv_synic_add(CPUState *cs) +{ + Object *obj; + SynICState *synic; + + obj = object_new(TYPE_SYNIC); + synic = SYNIC(obj); + synic->cs = cs; + object_property_add_child(OBJECT(cs), "synic", obj, &error_abort); + object_unref(obj); + object_property_set_bool(obj, true, "realized", &error_abort); +} + +void hyperv_synic_reset(CPUState *cs) +{ + SynICState *synic = get_synic(cs); + + if (synic) { + device_reset(DEVICE(synic)); + } +} + +static const TypeInfo synic_type_info = { + .name = TYPE_SYNIC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(SynICState), + .class_init = synic_class_init, +}; + +static void synic_register_types(void) +{ + type_register_static(&synic_type_info); +} + +type_init(synic_register_types) + +/* + * KVM has its own message producers (SynIC timers). To guarantee + * serialization with both KVM vcpu and the guest cpu, the messages are first + * staged in an intermediate area and then posted to the SynIC message page in + * the vcpu thread. + */ +typedef struct HvSintStagedMessage { + /* message content staged by hyperv_post_msg */ + struct hyperv_message msg; + /* callback + data (r/o) to complete the processing in a BH */ + HvSintMsgCb cb; + void *cb_data; + /* message posting status filled by cpu_post_msg */ + int status; + /* passing the buck: */ + enum { + /* initial state */ + HV_STAGED_MSG_FREE, + /* + * hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE -> + * BUSY), copies msg, and schedules cpu_post_msg on the assigned cpu + */ + HV_STAGED_MSG_BUSY, + /* + * cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot, + * notify the guest, records the status, marks the posting done (BUSY + * -> POSTED), and schedules sint_msg_bh BH + */ + HV_STAGED_MSG_POSTED, + /* + * sint_msg_bh (BH) verifies that the posting is done, runs the + * callback, and starts over (POSTED -> FREE) + */ + } state; +} HvSintStagedMessage; + +struct HvSintRoute { + uint32_t sint; + SynICState *synic; + int gsi; + EventNotifier sint_set_notifier; + EventNotifier sint_ack_notifier; + + HvSintStagedMessage *staged_msg; + + unsigned refcount; +}; + +static CPUState *hyperv_find_vcpu(uint32_t vp_index) +{ + CPUState *cs = qemu_get_cpu(vp_index); + assert(hyperv_vp_index(cs) == vp_index); + return cs; +} + +/* + * BH to complete the processing of a staged message. + */ +static void sint_msg_bh(void *opaque) +{ + HvSintRoute *sint_route = opaque; + HvSintStagedMessage *staged_msg = sint_route->staged_msg; + + if (atomic_read(&staged_msg->state) != HV_STAGED_MSG_POSTED) { + /* status nor ready yet (spurious ack from guest?), ignore */ + return; + } + + staged_msg->cb(staged_msg->cb_data, staged_msg->status); + staged_msg->status = 0; + + /* staged message processing finished, ready to start over */ + atomic_set(&staged_msg->state, HV_STAGED_MSG_FREE); + /* drop the reference taken in hyperv_post_msg */ + hyperv_sint_route_unref(sint_route); +} + +/* + * Worker to transfer the message from the staging area into the SynIC message + * page in vcpu context. + */ +static void cpu_post_msg(CPUState *cs, run_on_cpu_data data) +{ + HvSintRoute *sint_route = data.host_ptr; + HvSintStagedMessage *staged_msg = sint_route->staged_msg; + SynICState *synic = sint_route->synic; + struct hyperv_message *dst_msg; + bool wait_for_sint_ack = false; + + assert(staged_msg->state == HV_STAGED_MSG_BUSY); + + if (!synic->enabled || !synic->msg_page_addr) { + staged_msg->status = -ENXIO; + goto posted; + } + + dst_msg = &synic->msg_page->slot[sint_route->sint]; + + if (dst_msg->header.message_type != HV_MESSAGE_NONE) { + dst_msg->header.message_flags |= HV_MESSAGE_FLAG_PENDING; + staged_msg->status = -EAGAIN; + wait_for_sint_ack = true; + } else { + memcpy(dst_msg, &staged_msg->msg, sizeof(*dst_msg)); + staged_msg->status = hyperv_sint_route_set_sint(sint_route); + } + + memory_region_set_dirty(&synic->msg_page_mr, 0, sizeof(*synic->msg_page)); + +posted: + atomic_set(&staged_msg->state, HV_STAGED_MSG_POSTED); + /* + * Notify the msg originator of the progress made; if the slot was busy we + * set msg_pending flag in it so it will be the guest who will do EOM and + * trigger the notification from KVM via sint_ack_notifier + */ + if (!wait_for_sint_ack) { + aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh, + sint_route); + } +} + +/* + * Post a Hyper-V message to the staging area, for delivery to guest in the + * vcpu thread. + */ +int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *src_msg) +{ + HvSintStagedMessage *staged_msg = sint_route->staged_msg; + + assert(staged_msg); + + /* grab the staging area */ + if (atomic_cmpxchg(&staged_msg->state, HV_STAGED_MSG_FREE, + HV_STAGED_MSG_BUSY) != HV_STAGED_MSG_FREE) { + return -EAGAIN; + } + + memcpy(&staged_msg->msg, src_msg, sizeof(*src_msg)); + + /* hold a reference on sint_route until the callback is finished */ + hyperv_sint_route_ref(sint_route); + + /* schedule message posting attempt in vcpu thread */ + async_run_on_cpu(sint_route->synic->cs, cpu_post_msg, + RUN_ON_CPU_HOST_PTR(sint_route)); + return 0; +} + +static void sint_ack_handler(EventNotifier *notifier) +{ + HvSintRoute *sint_route = container_of(notifier, HvSintRoute, + sint_ack_notifier); + event_notifier_test_and_clear(notifier); + + /* + * the guest consumed the previous message so complete the current one with + * -EAGAIN and let the msg originator retry + */ + aio_bh_schedule_oneshot(qemu_get_aio_context(), sint_msg_bh, sint_route); +} + +/* + * Set given event flag for a given sint on a given vcpu, and signal the sint. + */ +int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) +{ + int ret; + SynICState *synic = sint_route->synic; + unsigned long *flags, set_mask; + unsigned set_idx; + + if (eventno > HV_EVENT_FLAGS_COUNT) { + return -EINVAL; + } + if (!synic->enabled || !synic->event_page_addr) { + return -ENXIO; + } + + set_idx = BIT_WORD(eventno); + set_mask = BIT_MASK(eventno); + flags = synic->event_page->slot[sint_route->sint].flags; + + if ((atomic_fetch_or(&flags[set_idx], set_mask) & set_mask) != set_mask) { + memory_region_set_dirty(&synic->event_page_mr, 0, + sizeof(*synic->event_page)); + ret = hyperv_sint_route_set_sint(sint_route); + } else { + ret = 0; + } + return ret; +} + +HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, + HvSintMsgCb cb, void *cb_data) +{ + HvSintRoute *sint_route; + EventNotifier *ack_notifier; + int r, gsi; + CPUState *cs; + SynICState *synic; + + cs = hyperv_find_vcpu(vp_index); + if (!cs) { + return NULL; + } + + synic = get_synic(cs); + if (!synic) { + return NULL; + } + + sint_route = g_new0(HvSintRoute, 1); + r = event_notifier_init(&sint_route->sint_set_notifier, false); + if (r) { + goto err; + } + + + ack_notifier = cb ? &sint_route->sint_ack_notifier : NULL; + if (ack_notifier) { + sint_route->staged_msg = g_new0(HvSintStagedMessage, 1); + sint_route->staged_msg->cb = cb; + sint_route->staged_msg->cb_data = cb_data; + + r = event_notifier_init(ack_notifier, false); + if (r) { + goto err_sint_set_notifier; + } + + event_notifier_set_handler(ack_notifier, sint_ack_handler); + } + + gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, sint); + if (gsi < 0) { + goto err_gsi; + } + + r = kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, + &sint_route->sint_set_notifier, + ack_notifier, gsi); + if (r) { + goto err_irqfd; + } + sint_route->gsi = gsi; + sint_route->synic = synic; + sint_route->sint = sint; + sint_route->refcount = 1; + + return sint_route; + +err_irqfd: + kvm_irqchip_release_virq(kvm_state, gsi); +err_gsi: + if (ack_notifier) { + event_notifier_set_handler(ack_notifier, NULL); + event_notifier_cleanup(ack_notifier); + g_free(sint_route->staged_msg); + } +err_sint_set_notifier: + event_notifier_cleanup(&sint_route->sint_set_notifier); +err: + g_free(sint_route); + + return NULL; +} + +void hyperv_sint_route_ref(HvSintRoute *sint_route) +{ + sint_route->refcount++; +} + +void hyperv_sint_route_unref(HvSintRoute *sint_route) +{ + if (!sint_route) { + return; + } + + assert(sint_route->refcount > 0); + + if (--sint_route->refcount) { + return; + } + + 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); + if (sint_route->staged_msg) { + event_notifier_set_handler(&sint_route->sint_ack_notifier, NULL); + event_notifier_cleanup(&sint_route->sint_ack_notifier); + g_free(sint_route->staged_msg); + } + event_notifier_cleanup(&sint_route->sint_set_notifier); + g_free(sint_route); +} + +int hyperv_sint_route_set_sint(HvSintRoute *sint_route) +{ + return event_notifier_set(&sint_route->sint_set_notifier); +} + +typedef struct MsgHandler { + struct rcu_head rcu; + QLIST_ENTRY(MsgHandler) link; + uint32_t conn_id; + HvMsgHandler handler; + void *data; +} MsgHandler; + +typedef struct EventFlagHandler { + struct rcu_head rcu; + QLIST_ENTRY(EventFlagHandler) link; + uint32_t conn_id; + EventNotifier *notifier; +} EventFlagHandler; + +static QLIST_HEAD(, MsgHandler) msg_handlers; +static QLIST_HEAD(, EventFlagHandler) event_flag_handlers; +static QemuMutex handlers_mutex; + +static void __attribute__((constructor)) hv_init(void) +{ + QLIST_INIT(&msg_handlers); + QLIST_INIT(&event_flag_handlers); + qemu_mutex_init(&handlers_mutex); +} + +int hyperv_set_msg_handler(uint32_t conn_id, HvMsgHandler handler, void *data) +{ + int ret; + MsgHandler *mh; + + qemu_mutex_lock(&handlers_mutex); + QLIST_FOREACH(mh, &msg_handlers, link) { + if (mh->conn_id == conn_id) { + if (handler) { + ret = -EEXIST; + } else { + QLIST_REMOVE_RCU(mh, link); + g_free_rcu(mh, rcu); + ret = 0; + } + goto unlock; + } + } + + if (handler) { + mh = g_new(MsgHandler, 1); + mh->conn_id = conn_id; + mh->handler = handler; + mh->data = data; + QLIST_INSERT_HEAD_RCU(&msg_handlers, mh, link); + ret = 0; + } else { + ret = -ENOENT; + } +unlock: + qemu_mutex_unlock(&handlers_mutex); + return ret; +} + +uint16_t hyperv_hcall_post_message(uint64_t param, bool fast) +{ + uint16_t ret; + hwaddr len; + struct hyperv_post_message_input *msg; + MsgHandler *mh; + + if (fast) { + return HV_STATUS_INVALID_HYPERCALL_CODE; + } + if (param & (__alignof__(*msg) - 1)) { + return HV_STATUS_INVALID_ALIGNMENT; + } + + len = sizeof(*msg); + msg = cpu_physical_memory_map(param, &len, 0); + if (len < sizeof(*msg)) { + ret = HV_STATUS_INSUFFICIENT_MEMORY; + goto unmap; + } + if (msg->payload_size > sizeof(msg->payload)) { + ret = HV_STATUS_INVALID_HYPERCALL_INPUT; + goto unmap; + } + + ret = HV_STATUS_INVALID_CONNECTION_ID; + rcu_read_lock(); + QLIST_FOREACH_RCU(mh, &msg_handlers, link) { + if (mh->conn_id == (msg->connection_id & HV_CONNECTION_ID_MASK)) { + ret = mh->handler(msg, mh->data); + break; + } + } + rcu_read_unlock(); + +unmap: + cpu_physical_memory_unmap(msg, len, 0, 0); + return ret; +} + +static int set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier) +{ + int ret; + EventFlagHandler *handler; + + qemu_mutex_lock(&handlers_mutex); + QLIST_FOREACH(handler, &event_flag_handlers, link) { + if (handler->conn_id == conn_id) { + if (notifier) { + ret = -EEXIST; + } else { + QLIST_REMOVE_RCU(handler, link); + g_free_rcu(handler, rcu); + ret = 0; + } + goto unlock; + } + } + + if (notifier) { + handler = g_new(EventFlagHandler, 1); + handler->conn_id = conn_id; + handler->notifier = notifier; + QLIST_INSERT_HEAD_RCU(&event_flag_handlers, handler, link); + ret = 0; + } else { + ret = -ENOENT; + } +unlock: + qemu_mutex_unlock(&handlers_mutex); + return ret; +} + +static bool process_event_flags_userspace; + +int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier) +{ + if (!process_event_flags_userspace && + !kvm_check_extension(kvm_state, KVM_CAP_HYPERV_EVENTFD)) { + process_event_flags_userspace = true; + + warn_report("Hyper-V event signaling is not supported by this kernel; " + "using slower userspace hypercall processing"); + } + + if (!process_event_flags_userspace) { + struct kvm_hyperv_eventfd hvevfd = { + .conn_id = conn_id, + .fd = notifier ? event_notifier_get_fd(notifier) : -1, + .flags = notifier ? 0 : KVM_HYPERV_EVENTFD_DEASSIGN, + }; + + return kvm_vm_ioctl(kvm_state, KVM_HYPERV_EVENTFD, &hvevfd); + } + return set_event_flag_handler(conn_id, notifier); +} + +uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) +{ + uint16_t ret; + EventFlagHandler *handler; + + if (unlikely(!fast)) { + hwaddr addr = param; + + if (addr & (__alignof__(addr) - 1)) { + return HV_STATUS_INVALID_ALIGNMENT; + } + + param = ldq_phys(&address_space_memory, addr); + } + + /* + * Per spec, bits 32-47 contain the extra "flag number". However, we + * have no use for it, and in all known usecases it is zero, so just + * report lookup failure if it isn't. + */ + if (param & 0xffff00000000ULL) { + return HV_STATUS_INVALID_PORT_ID; + } + /* remaining bits are reserved-zero */ + if (param & ~HV_CONNECTION_ID_MASK) { + return HV_STATUS_INVALID_HYPERCALL_INPUT; + } + + ret = HV_STATUS_INVALID_CONNECTION_ID; + rcu_read_lock(); + QLIST_FOREACH_RCU(handler, &event_flag_handlers, link) { + if (handler->conn_id == param) { + event_notifier_set(handler->notifier); + ret = 0; + break; + } + } + rcu_read_unlock(); + return ret; +} diff --git a/hw/hyperv/hyperv_testdev.c b/hw/hyperv/hyperv_testdev.c new file mode 100644 index 0000000000..4880333cf5 --- /dev/null +++ b/hw/hyperv/hyperv_testdev.c @@ -0,0 +1,327 @@ +/* + * 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 "qemu/main-loop.h" +#include "qemu/queue.h" +#include "hw/qdev.h" +#include "hw/isa/isa.h" +#include "hw/hyperv/hyperv.h" + +typedef struct TestSintRoute { + QLIST_ENTRY(TestSintRoute) le; + uint8_t vp_index; + uint8_t sint; + HvSintRoute *sint_route; +} TestSintRoute; + +typedef struct TestMsgConn { + QLIST_ENTRY(TestMsgConn) le; + uint8_t conn_id; + HvSintRoute *sint_route; + struct hyperv_message msg; +} TestMsgConn; + +typedef struct TestEvtConn { + QLIST_ENTRY(TestEvtConn) le; + uint8_t conn_id; + HvSintRoute *sint_route; + EventNotifier notifier; +} TestEvtConn; + +struct HypervTestDev { + ISADevice parent_obj; + MemoryRegion sint_control; + QLIST_HEAD(, TestSintRoute) sint_routes; + QLIST_HEAD(, TestMsgConn) msg_conns; + QLIST_HEAD(, TestEvtConn) evt_conns; +}; +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, + HV_TEST_DEV_MSG_CONN_CREATE, + HV_TEST_DEV_MSG_CONN_DESTROY, + HV_TEST_DEV_EVT_CONN_CREATE, + HV_TEST_DEV_EVT_CONN_DESTROY, +}; + +static void sint_route_create(HypervTestDev *dev, + uint8_t vp_index, uint8_t sint) +{ + TestSintRoute *sint_route; + + sint_route = g_new0(TestSintRoute, 1); + assert(sint_route); + + sint_route->vp_index = vp_index; + sint_route->sint = sint; + + sint_route->sint_route = hyperv_sint_route_new(vp_index, sint, NULL, NULL); + assert(sint_route->sint_route); + + QLIST_INSERT_HEAD(&dev->sint_routes, sint_route, le); +} + +static TestSintRoute *sint_route_find(HypervTestDev *dev, + uint8_t vp_index, uint8_t sint) +{ + TestSintRoute *sint_route; + + QLIST_FOREACH(sint_route, &dev->sint_routes, le) { + if (sint_route->vp_index == vp_index && sint_route->sint == sint) { + return sint_route; + } + } + assert(false); + return NULL; +} + +static void sint_route_destroy(HypervTestDev *dev, + uint8_t vp_index, uint8_t sint) +{ + TestSintRoute *sint_route; + + sint_route = sint_route_find(dev, vp_index, sint); + QLIST_REMOVE(sint_route, le); + hyperv_sint_route_unref(sint_route->sint_route); + g_free(sint_route); +} + +static void sint_route_set_sint(HypervTestDev *dev, + uint8_t vp_index, uint8_t sint) +{ + TestSintRoute *sint_route; + + sint_route = sint_route_find(dev, vp_index, sint); + + hyperv_sint_route_set_sint(sint_route->sint_route); +} + +static void msg_retry(void *opaque) +{ + TestMsgConn *conn = opaque; + assert(!hyperv_post_msg(conn->sint_route, &conn->msg)); +} + +static void msg_cb(void *data, int status) +{ + TestMsgConn *conn = data; + + if (!status) { + return; + } + + assert(status == -EAGAIN); + + aio_bh_schedule_oneshot(qemu_get_aio_context(), msg_retry, conn); +} + +static uint16_t msg_handler(const struct hyperv_post_message_input *msg, + void *data) +{ + int ret; + TestMsgConn *conn = data; + + /* post the same message we've got */ + conn->msg.header.message_type = msg->message_type; + assert(msg->payload_size < sizeof(conn->msg.payload)); + conn->msg.header.payload_size = msg->payload_size; + memcpy(&conn->msg.payload, msg->payload, msg->payload_size); + + ret = hyperv_post_msg(conn->sint_route, &conn->msg); + + switch (ret) { + case 0: + return HV_STATUS_SUCCESS; + case -EAGAIN: + return HV_STATUS_INSUFFICIENT_BUFFERS; + default: + return HV_STATUS_INVALID_HYPERCALL_INPUT; + } +} + +static void msg_conn_create(HypervTestDev *dev, uint8_t vp_index, + uint8_t sint, uint8_t conn_id) +{ + TestMsgConn *conn; + + conn = g_new0(TestMsgConn, 1); + assert(conn); + + conn->conn_id = conn_id; + + conn->sint_route = hyperv_sint_route_new(vp_index, sint, msg_cb, conn); + assert(conn->sint_route); + + assert(!hyperv_set_msg_handler(conn->conn_id, msg_handler, conn)); + + QLIST_INSERT_HEAD(&dev->msg_conns, conn, le); +} + +static void msg_conn_destroy(HypervTestDev *dev, uint8_t conn_id) +{ + TestMsgConn *conn; + + QLIST_FOREACH(conn, &dev->msg_conns, le) { + if (conn->conn_id == conn_id) { + QLIST_REMOVE(conn, le); + hyperv_set_msg_handler(conn->conn_id, NULL, NULL); + hyperv_sint_route_unref(conn->sint_route); + g_free(conn); + return; + } + } + assert(false); +} + +static void evt_conn_handler(EventNotifier *notifier) +{ + TestEvtConn *conn = container_of(notifier, TestEvtConn, notifier); + + event_notifier_test_and_clear(notifier); + + /* signal the same event flag we've got */ + assert(!hyperv_set_event_flag(conn->sint_route, conn->conn_id)); +} + +static void evt_conn_create(HypervTestDev *dev, uint8_t vp_index, + uint8_t sint, uint8_t conn_id) +{ + TestEvtConn *conn; + + conn = g_new0(TestEvtConn, 1); + assert(conn); + + conn->conn_id = conn_id; + + conn->sint_route = hyperv_sint_route_new(vp_index, sint, NULL, NULL); + assert(conn->sint_route); + + assert(!event_notifier_init(&conn->notifier, false)); + + event_notifier_set_handler(&conn->notifier, evt_conn_handler); + + assert(!hyperv_set_event_flag_handler(conn_id, &conn->notifier)); + + QLIST_INSERT_HEAD(&dev->evt_conns, conn, le); +} + +static void evt_conn_destroy(HypervTestDev *dev, uint8_t conn_id) +{ + TestEvtConn *conn; + + QLIST_FOREACH(conn, &dev->evt_conns, le) { + if (conn->conn_id == conn_id) { + QLIST_REMOVE(conn, le); + hyperv_set_event_flag_handler(conn->conn_id, NULL); + event_notifier_set_handler(&conn->notifier, NULL); + event_notifier_cleanup(&conn->notifier); + hyperv_sint_route_unref(conn->sint_route); + g_free(conn); + return; + } + } + assert(false); +} + +static uint64_t hv_test_dev_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + +static void hv_test_dev_write(void *opaque, hwaddr addr, uint64_t data, + uint32_t len) +{ + HypervTestDev *dev = HYPERV_TEST_DEV(opaque); + uint8_t sint = data & 0xFF; + uint8_t vp_index = (data >> 8ULL) & 0xFF; + uint8_t ctl = (data >> 16ULL) & 0xFF; + uint8_t conn_id = (data >> 24ULL) & 0xFF; + + switch (ctl) { + case HV_TEST_DEV_SINT_ROUTE_CREATE: + sint_route_create(dev, vp_index, sint); + break; + case HV_TEST_DEV_SINT_ROUTE_DESTROY: + sint_route_destroy(dev, vp_index, sint); + break; + case HV_TEST_DEV_SINT_ROUTE_SET_SINT: + sint_route_set_sint(dev, vp_index, sint); + break; + case HV_TEST_DEV_MSG_CONN_CREATE: + msg_conn_create(dev, vp_index, sint, conn_id); + break; + case HV_TEST_DEV_MSG_CONN_DESTROY: + msg_conn_destroy(dev, conn_id); + break; + case HV_TEST_DEV_EVT_CONN_CREATE: + evt_conn_create(dev, vp_index, sint, conn_id); + break; + case HV_TEST_DEV_EVT_CONN_DESTROY: + evt_conn_destroy(dev, conn_id); + break; + default: + break; + } +} + +static const MemoryRegionOps synic_test_sint_ops = { + .read = hv_test_dev_read, + .write = hv_test_dev_write, + .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); + + QLIST_INIT(&dev->sint_routes); + QLIST_INIT(&dev->msg_conns); + QLIST_INIT(&dev->evt_conns); + 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/hw/i2c/Kconfig b/hw/i2c/Kconfig new file mode 100644 index 0000000000..820b24de5b --- /dev/null +++ b/hw/i2c/Kconfig @@ -0,0 +1,31 @@ +config I2C + bool + +config SMBUS_EEPROM + bool + depends on I2C + +config DDC + bool + depends on I2C + select EDID + +config VERSATILE_I2C + bool + select I2C + +config ACPI_SMBUS + bool + select I2C + +config BITBANG_I2C + bool + select I2C + +config IMX_I2C + bool + select I2C + +config MPC_I2C + bool + select I2C diff --git a/hw/i2c/Makefile.objs b/hw/i2c/Makefile.objs index 37cacde978..5f76b6a990 100644 --- a/hw/i2c/Makefile.objs +++ b/hw/i2c/Makefile.objs @@ -1,11 +1,14 @@ -common-obj-$(CONFIG_I2C) += core.o smbus.o smbus_eeprom.o +common-obj-$(CONFIG_I2C) += core.o smbus_slave.o smbus_master.o +common-obj-$(CONFIG_SMBUS_EEPROM) += smbus_eeprom.o common-obj-$(CONFIG_DDC) += i2c-ddc.o common-obj-$(CONFIG_VERSATILE_I2C) += versatile_i2c.o -common-obj-$(CONFIG_ACPI_X86) += smbus_ich9.o -common-obj-$(CONFIG_APM) += pm_smbus.o +common-obj-$(CONFIG_ACPI_X86_ICH) += smbus_ich9.o +common-obj-$(CONFIG_ACPI_SMBUS) += 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 common-obj-$(CONFIG_ASPEED_SOC) += aspeed_i2c.o +common-obj-$(CONFIG_NRF51_SOC) += microbit_i2c.o +common-obj-$(CONFIG_MPC_I2C) += mpc_i2c.o obj-$(CONFIG_OMAP) += omap_i2c.o obj-$(CONFIG_PPC4XX) += ppc4xx_i2c.o diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index c762c7366a..a085510cfd 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -52,6 +52,13 @@ #define I2CD_AC_TIMING_REG2 0x08 /* Clock and AC Timing Control #1 */ #define I2CD_INTR_CTRL_REG 0x0c /* I2CD Interrupt Control */ #define I2CD_INTR_STS_REG 0x10 /* I2CD Interrupt Status */ + +#define I2CD_INTR_SLAVE_ADDR_MATCH (0x1 << 31) /* 0: addr1 1: addr2 */ +#define I2CD_INTR_SLAVE_ADDR_RX_PENDING (0x1 << 30) +/* bits[19-16] Reserved */ + +/* All bits below are cleared by writing 1 */ +#define I2CD_INTR_SLAVE_INACTIVE_TIMEOUT (0x1 << 15) #define I2CD_INTR_SDA_DL_TIMEOUT (0x1 << 14) #define I2CD_INTR_BUS_RECOVER_DONE (0x1 << 13) #define I2CD_INTR_SMBUS_ALERT (0x1 << 12) /* Bus [0-3] only */ @@ -59,7 +66,7 @@ #define I2CD_INTR_SMBUS_DEV_ALERT_ADDR (0x1 << 10) /* Removed */ #define I2CD_INTR_SMBUS_DEF_ADDR (0x1 << 9) /* Removed */ #define I2CD_INTR_GCALL_ADDR (0x1 << 8) /* Removed */ -#define I2CD_INTR_SLAVE_MATCH (0x1 << 7) /* use RX_DONE */ +#define I2CD_INTR_SLAVE_ADDR_RX_MATCH (0x1 << 7) /* use RX_DONE */ #define I2CD_INTR_SCL_TIMEOUT (0x1 << 6) #define I2CD_INTR_ABNORMAL (0x1 << 5) #define I2CD_INTR_NORMAL_STOP (0x1 << 4) @@ -180,6 +187,21 @@ static uint8_t aspeed_i2c_get_state(AspeedI2CBus *bus) return (bus->cmd >> I2CD_TX_STATE_SHIFT) & I2CD_TX_STATE_MASK; } +static void aspeed_i2c_handle_rx_cmd(AspeedI2CBus *bus) +{ + uint8_t ret; + + aspeed_i2c_set_state(bus, I2CD_MRXD); + ret = i2c_recv(bus->bus); + bus->intr_status |= I2CD_INTR_RX_DONE; + bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; + if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { + i2c_nack(bus->bus); + } + bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); + aspeed_i2c_set_state(bus, I2CD_MACTIVE); +} + /* * The state machine needs some refinement. It is only used to track * invalid STOP commands for the moment. @@ -188,7 +210,6 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) { bus->cmd &= ~0xFFFF; bus->cmd |= value & 0xFFFF; - bus->intr_status = 0; if (bus->cmd & I2CD_M_START_CMD) { uint8_t state = aspeed_i2c_get_state(bus) & I2CD_MACTIVE ? @@ -226,23 +247,9 @@ static void aspeed_i2c_bus_handle_cmd(AspeedI2CBus *bus, uint64_t value) aspeed_i2c_set_state(bus, I2CD_MACTIVE); } - if (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) { - int ret; - - aspeed_i2c_set_state(bus, I2CD_MRXD); - ret = i2c_recv(bus->bus); - if (ret < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: read failed\n", __func__); - ret = 0xff; - } else { - bus->intr_status |= I2CD_INTR_RX_DONE; - } - bus->buf = (ret & I2CD_BYTE_BUF_RX_MASK) << I2CD_BYTE_BUF_RX_SHIFT; - if (bus->cmd & I2CD_M_S_RX_CMD_LAST) { - i2c_nack(bus->bus); - } - bus->cmd &= ~(I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST); - aspeed_i2c_set_state(bus, I2CD_MACTIVE); + if ((bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST)) && + !(bus->intr_status & I2CD_INTR_RX_DONE)) { + aspeed_i2c_handle_rx_cmd(bus); } if (bus->cmd & I2CD_M_STOP_CMD) { @@ -263,6 +270,7 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { AspeedI2CBus *bus = opaque; + bool handle_rx; switch (offset) { case I2CD_FUN_CTRL_REG: @@ -283,9 +291,17 @@ static void aspeed_i2c_bus_write(void *opaque, hwaddr offset, bus->intr_ctrl = value & 0x7FFF; break; case I2CD_INTR_STS_REG: + handle_rx = (bus->intr_status & I2CD_INTR_RX_DONE) && + (value & I2CD_INTR_RX_DONE); bus->intr_status &= ~(value & 0x7FFF); - bus->controller->intr_status &= ~(1 << bus->id); - qemu_irq_lower(bus->controller->irq); + if (!bus->intr_status) { + bus->controller->intr_status &= ~(1 << bus->id); + qemu_irq_lower(bus->controller->irq); + } + if (handle_rx && (bus->cmd & (I2CD_M_RX_CMD | I2CD_M_S_RX_CMD_LAST))) { + aspeed_i2c_handle_rx_cmd(bus); + aspeed_i2c_bus_raise_interrupt(bus); + } break; case I2CD_DEV_ADDR_REG: qemu_log_mask(LOG_UNIMP, "%s: slave mode not implemented\n", diff --git a/hw/i2c/bitbang_i2c.h b/hw/i2c/bitbang_i2c.h index 3a7126d5de..9443021710 100644 --- a/hw/i2c/bitbang_i2c.h +++ b/hw/i2c/bitbang_i2c.h @@ -3,8 +3,6 @@ #include "hw/i2c/i2c.h" -typedef struct bitbang_i2c_interface bitbang_i2c_interface; - #define BITBANG_I2C_SDA 0 #define BITBANG_I2C_SCL 1 diff --git a/hw/i2c/core.c b/hw/i2c/core.c index b54725985a..15237ad073 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -191,23 +191,17 @@ int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send) } return ret ? -1 : 0; } else { - if ((QLIST_EMPTY(&bus->current_devs)) || (bus->broadcast)) { - return -1; - } - - sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); - if (sc->recv) { - s = QLIST_FIRST(&bus->current_devs)->elt; - ret = sc->recv(s); - trace_i2c_recv(s->address, ret); - if (ret < 0) { - return ret; - } else { - *data = ret; - return 0; + ret = 0xff; + if (!QLIST_EMPTY(&bus->current_devs) && !bus->broadcast) { + sc = I2C_SLAVE_GET_CLASS(QLIST_FIRST(&bus->current_devs)->elt); + if (sc->recv) { + s = QLIST_FIRST(&bus->current_devs)->elt; + ret = sc->recv(s); + trace_i2c_recv(s->address, ret); } } - return -1; + *data = ret; + return 0; } } @@ -216,12 +210,12 @@ int i2c_send(I2CBus *bus, uint8_t data) return i2c_send_recv(bus, &data, true); } -int i2c_recv(I2CBus *bus) +uint8_t i2c_recv(I2CBus *bus) { - uint8_t data; - int ret = i2c_send_recv(bus, &data, false); + uint8_t data = 0xff; - return ret < 0 ? ret : data; + i2c_send_recv(bus, &data, false); + return data; } void i2c_nack(I2CBus *bus) diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c index c96fa7d7be..d154b05739 100644 --- a/hw/i2c/exynos4210_i2c.c +++ b/hw/i2c/exynos4210_i2c.c @@ -106,16 +106,10 @@ static inline void exynos4210_i2c_raise_interrupt(Exynos4210I2CState *s) static void exynos4210_i2c_data_receive(void *opaque) { Exynos4210I2CState *s = (Exynos4210I2CState *)opaque; - int ret; s->i2cstat &= ~I2CSTAT_LAST_BIT; s->scl_free = false; - ret = i2c_recv(s->bus); - if (ret < 0 && (s->i2ccon & I2CCON_ACK_GEN)) { - s->i2cstat |= I2CSTAT_LAST_BIT; /* Data is not acknowledged */ - } else { - s->i2cds = ret; - } + s->i2cds = i2c_recv(s->bus); exynos4210_i2c_raise_interrupt(s); } diff --git a/hw/i2c/i2c-ddc.c b/hw/i2c/i2c-ddc.c index bec0c91e2d..7aa8727771 100644 --- a/hw/i2c/i2c-ddc.c +++ b/hw/i2c/i2c-ddc.c @@ -32,197 +32,6 @@ } \ } while (0) -/* Structure defining a monitor's characteristics in a - * readable format: this should be passed to build_edid_blob() - * to convert it into the 128 byte binary EDID blob. - * Not all bits of the EDID are customisable here. - */ -struct EDIDData { - char manuf_id[3]; /* three upper case letters */ - uint16_t product_id; - uint32_t serial_no; - uint8_t manuf_week; - int manuf_year; - uint8_t h_cm; - uint8_t v_cm; - uint8_t gamma; - char monitor_name[14]; - char serial_no_string[14]; - /* Range limits */ - uint8_t vmin; /* Hz */ - uint8_t vmax; /* Hz */ - uint8_t hmin; /* kHz */ - uint8_t hmax; /* kHz */ - uint8_t pixclock; /* MHz / 10 */ - uint8_t timing_data[18]; -}; - -typedef struct EDIDData EDIDData; - -/* EDID data for a simple LCD monitor */ -static const EDIDData lcd_edid = { - /* The manuf_id ought really to be an assigned EISA ID */ - .manuf_id = "QMU", - .product_id = 0, - .serial_no = 1, - .manuf_week = 1, - .manuf_year = 2011, - .h_cm = 40, - .v_cm = 30, - .gamma = 0x78, - .monitor_name = "QEMU monitor", - .serial_no_string = "1", - .vmin = 40, - .vmax = 120, - .hmin = 30, - .hmax = 100, - .pixclock = 18, - .timing_data = { - /* Borrowed from a 21" LCD */ - 0x48, 0x3f, 0x40, 0x30, 0x62, 0xb0, 0x32, 0x40, 0x40, - 0xc0, 0x13, 0x00, 0x98, 0x32, 0x11, 0x00, 0x00, 0x1e - } -}; - -static uint8_t manuf_char_to_int(char c) -{ - return (c - 'A') & 0x1f; -} - -static void write_ascii_descriptor_block(uint8_t *descblob, uint8_t blocktype, - const char *string) -{ - /* Write an EDID Descriptor Block of the "ascii string" type */ - int i; - descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0; - descblob[3] = blocktype; - /* The rest is 13 bytes of ASCII; if less then the rest must - * be filled with newline then spaces - */ - for (i = 5; i < 19; i++) { - descblob[i] = string[i - 5]; - if (!descblob[i]) { - break; - } - } - if (i < 19) { - descblob[i++] = '\n'; - } - for ( ; i < 19; i++) { - descblob[i] = ' '; - } -} - -static void write_range_limits_descriptor(const EDIDData *edid, - uint8_t *descblob) -{ - int i; - descblob[0] = descblob[1] = descblob[2] = descblob[4] = 0; - descblob[3] = 0xfd; - descblob[5] = edid->vmin; - descblob[6] = edid->vmax; - descblob[7] = edid->hmin; - descblob[8] = edid->hmax; - descblob[9] = edid->pixclock; - descblob[10] = 0; - descblob[11] = 0xa; - for (i = 12; i < 19; i++) { - descblob[i] = 0x20; - } -} - -static void build_edid_blob(const EDIDData *edid, uint8_t *blob) -{ - /* Write an EDID 1.3 format blob (128 bytes) based - * on the EDIDData structure. - */ - int i; - uint8_t cksum; - - /* 00-07 : header */ - blob[0] = blob[7] = 0; - for (i = 1 ; i < 7; i++) { - blob[i] = 0xff; - } - /* 08-09 : manufacturer ID */ - blob[8] = (manuf_char_to_int(edid->manuf_id[0]) << 2) - | (manuf_char_to_int(edid->manuf_id[1]) >> 3); - blob[9] = (manuf_char_to_int(edid->manuf_id[1]) << 5) - | manuf_char_to_int(edid->manuf_id[2]); - /* 10-11 : product ID code */ - blob[10] = edid->product_id; - blob[11] = edid->product_id >> 8; - blob[12] = edid->serial_no; - blob[13] = edid->serial_no >> 8; - blob[14] = edid->serial_no >> 16; - blob[15] = edid->serial_no >> 24; - /* 16 : week of manufacture */ - blob[16] = edid->manuf_week; - /* 17 : year of manufacture - 1990 */ - blob[17] = edid->manuf_year - 1990; - /* 18, 19 : EDID version and revision */ - blob[18] = 1; - blob[19] = 3; - /* 20 - 24 : basic display parameters */ - /* We are always a digital display */ - blob[20] = 0x80; - /* 21, 22 : max h/v size in cm */ - blob[21] = edid->h_cm; - blob[22] = edid->v_cm; - /* 23 : gamma (divide by 100 then add 1 for actual value) */ - blob[23] = edid->gamma; - /* 24 feature support: no power management, RGB, preferred timing mode, - * standard colour space - */ - blob[24] = 0x0e; - /* 25 - 34 : chromaticity coordinates. These are the - * standard sRGB chromaticity values - */ - blob[25] = 0xee; - blob[26] = 0x91; - blob[27] = 0xa3; - blob[28] = 0x54; - blob[29] = 0x4c; - blob[30] = 0x99; - blob[31] = 0x26; - blob[32] = 0x0f; - blob[33] = 0x50; - blob[34] = 0x54; - /* 35, 36 : Established timings: claim to support everything */ - blob[35] = blob[36] = 0xff; - /* 37 : manufacturer's reserved timing: none */ - blob[37] = 0; - /* 38 - 53 : standard timing identification - * don't claim anything beyond what the 'established timings' - * already provide. Unused slots must be (0x1, 0x1) - */ - for (i = 38; i < 54; i++) { - blob[i] = 0x1; - } - /* 54 - 71 : descriptor block 1 : must be preferred timing data */ - memcpy(blob + 54, edid->timing_data, 18); - /* 72 - 89, 90 - 107, 108 - 125 : descriptor block 2, 3, 4 - * Order not important, but we must have a monitor name and a - * range limits descriptor. - */ - write_range_limits_descriptor(edid, blob + 72); - write_ascii_descriptor_block(blob + 90, 0xfc, edid->monitor_name); - write_ascii_descriptor_block(blob + 108, 0xff, edid->serial_no_string); - - /* 126 : extension flag */ - blob[126] = 0; - - cksum = 0; - for (i = 0; i < 127; i++) { - cksum += blob[i]; - } - /* 127 : checksum */ - blob[127] = -cksum; - if (DEBUG_I2CDDC) { - qemu_hexdump((char *)blob, stdout, "", 128); - } -} - static void i2c_ddc_reset(DeviceState *ds) { I2CDDCState *s = I2CDDC(ds); @@ -242,12 +51,12 @@ static int i2c_ddc_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int i2c_ddc_rx(I2CSlave *i2c) +static uint8_t i2c_ddc_rx(I2CSlave *i2c) { I2CDDCState *s = I2CDDC(i2c); int value; - value = s->edid_blob[s->reg]; + value = s->edid_blob[s->reg % sizeof(s->edid_blob)]; s->reg++; return value; } @@ -270,7 +79,8 @@ static int i2c_ddc_tx(I2CSlave *i2c, uint8_t data) static void i2c_ddc_init(Object *obj) { I2CDDCState *s = I2CDDC(obj); - build_edid_blob(&lcd_edid, s->edid_blob); + + qemu_edid_generate(s->edid_blob, sizeof(s->edid_blob), &s->edid_info); } static const VMStateDescription vmstate_i2c_ddc = { @@ -283,6 +93,11 @@ static const VMStateDescription vmstate_i2c_ddc = { } }; +static Property i2c_ddc_properties[] = { + DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info), + DEFINE_PROP_END_OF_LIST(), +}; + static void i2c_ddc_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -290,6 +105,7 @@ static void i2c_ddc_class_init(ObjectClass *oc, void *data) dc->reset = i2c_ddc_reset; dc->vmsd = &vmstate_i2c_ddc; + dc->props = i2c_ddc_properties; isc->event = i2c_ddc_event; isc->recv = i2c_ddc_rx; isc->send = i2c_ddc_tx; diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index 6c81b98ebd..6da5224e2e 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -120,7 +120,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, value = s->i2dr_read; if (imx_i2c_is_master(s)) { - int ret = 0xff; + uint8_t ret = 0xff; if (s->address == ADDR_RESET) { /* something is wrong as the address is not set */ @@ -133,15 +133,7 @@ static uint64_t imx_i2c_read(void *opaque, hwaddr offset, } 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; - } + imx_i2c_raise_interrupt(s); } s->i2dr_read = ret; diff --git a/hw/i2c/microbit_i2c.c b/hw/i2c/microbit_i2c.c new file mode 100644 index 0000000000..793f1b0f8b --- /dev/null +++ b/hw/i2c/microbit_i2c.c @@ -0,0 +1,127 @@ +/* + * Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface + * http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf + * + * This is a microbit-specific stub for the TWI controller on the nRF51 SoC. + * We don't emulate I2C devices but the firmware probes the + * accelerometer/magnetometer on startup and panics if they are not found. + * Therefore we stub out the probing. + * + * In the future this file could evolve into a full nRF51 TWI controller + * device. + * + * Copyright 2018 Steffen Görtz + * Copyright 2019 Red Hat, Inc. + * + * 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 "qemu/log.h" +#include "hw/i2c/microbit_i2c.h" + +static const uint32_t twi_read_sequence[] = {0x5A, 0x5A, 0x40}; + +static uint64_t microbit_i2c_read(void *opaque, hwaddr addr, unsigned int size) +{ + MicrobitI2CState *s = opaque; + uint64_t data = 0x00; + + switch (addr) { + case NRF51_TWI_EVENT_STOPPED: + data = 0x01; + break; + case NRF51_TWI_EVENT_RXDREADY: + data = 0x01; + break; + case NRF51_TWI_EVENT_TXDSENT: + data = 0x01; + break; + case NRF51_TWI_REG_RXD: + data = twi_read_sequence[s->read_idx]; + if (s->read_idx < G_N_ELEMENTS(twi_read_sequence)) { + s->read_idx++; + } + break; + default: + data = s->regs[addr / sizeof(s->regs[0])]; + break; + } + + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " [%u] = %" PRIx32 "\n", + __func__, addr, size, (uint32_t)data); + + + return data; +} + +static void microbit_i2c_write(void *opaque, hwaddr addr, uint64_t data, + unsigned int size) +{ + MicrobitI2CState *s = opaque; + + qemu_log_mask(LOG_UNIMP, "%s: 0x%" HWADDR_PRIx " <- 0x%" PRIx64 " [%u]\n", + __func__, addr, data, size); + s->regs[addr / sizeof(s->regs[0])] = data; +} + +static const MemoryRegionOps microbit_i2c_ops = { + .read = microbit_i2c_read, + .write = microbit_i2c_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static const VMStateDescription microbit_i2c_vmstate = { + .name = TYPE_MICROBIT_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(regs, MicrobitI2CState, MICROBIT_I2C_NREGS), + VMSTATE_UINT32(read_idx, MicrobitI2CState), + }, +}; + +static void microbit_i2c_reset(DeviceState *dev) +{ + MicrobitI2CState *s = MICROBIT_I2C(dev); + + memset(s->regs, 0, sizeof(s->regs)); + s->read_idx = 0; +} + +static void microbit_i2c_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + MicrobitI2CState *s = MICROBIT_I2C(dev); + + memory_region_init_io(&s->iomem, OBJECT(s), µbit_i2c_ops, s, + "microbit.twi", NRF51_TWI_SIZE); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void microbit_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = µbit_i2c_vmstate; + dc->reset = microbit_i2c_reset; + dc->realize = microbit_i2c_realize; + dc->desc = "Microbit I2C controller"; +} + +static const TypeInfo microbit_i2c_info = { + .name = TYPE_MICROBIT_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MicrobitI2CState), + .class_init = microbit_i2c_class_init, +}; + +static void microbit_i2c_register_types(void) +{ + type_register_static(µbit_i2c_info); +} + +type_init(microbit_i2c_register_types) diff --git a/hw/i2c/mpc_i2c.c b/hw/i2c/mpc_i2c.c new file mode 100644 index 0000000000..693ca7ef6b --- /dev/null +++ b/hw/i2c/mpc_i2c.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2014 Freescale Semiconductor, Inc. All rights reserved. + * + * Author: Amit Tomar, + * + * Description: + * This file is derived from IMX I2C controller, + * by Jean-Christophe DUBOIS . + * + * Thanks to Scott Wood and Alexander Graf for their kind help on this. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, version 2 or later, + * as published by the Free Software Foundation. + * + * 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/i2c/i2c.h" +#include "qemu/log.h" +#include "hw/sysbus.h" + +/* #define DEBUG_I2C */ + +#ifdef DEBUG_I2C +#define DPRINTF(fmt, ...) \ + do { fprintf(stderr, "mpc_i2c[%s]: " fmt, __func__, ## __VA_ARGS__); \ + } while (0) +#else +#define DPRINTF(fmt, ...) do {} while (0) +#endif + +#define TYPE_MPC_I2C "mpc-i2c" +#define MPC_I2C(obj) \ + OBJECT_CHECK(MPCI2CState, (obj), TYPE_MPC_I2C) + +#define MPC_I2C_ADR 0x00 +#define MPC_I2C_FDR 0x04 +#define MPC_I2C_CR 0x08 +#define MPC_I2C_SR 0x0c +#define MPC_I2C_DR 0x10 +#define MPC_I2C_DFSRR 0x14 + +#define CCR_MEN (1 << 7) +#define CCR_MIEN (1 << 6) +#define CCR_MSTA (1 << 5) +#define CCR_MTX (1 << 4) +#define CCR_TXAK (1 << 3) +#define CCR_RSTA (1 << 2) +#define CCR_BCST (1 << 0) + +#define CSR_MCF (1 << 7) +#define CSR_MAAS (1 << 6) +#define CSR_MBB (1 << 5) +#define CSR_MAL (1 << 4) +#define CSR_SRW (1 << 2) +#define CSR_MIF (1 << 1) +#define CSR_RXAK (1 << 0) + +#define CADR_MASK 0xFE +#define CFDR_MASK 0x3F +#define CCR_MASK 0xFC +#define CSR_MASK 0xED +#define CDR_MASK 0xFF + +#define CYCLE_RESET 0xFF + +typedef struct MPCI2CState { + SysBusDevice parent_obj; + + I2CBus *bus; + qemu_irq irq; + MemoryRegion iomem; + + uint8_t address; + uint8_t adr; + uint8_t fdr; + uint8_t cr; + uint8_t sr; + uint8_t dr; + uint8_t dfssr; +} MPCI2CState; + +static bool mpc_i2c_is_enabled(MPCI2CState *s) +{ + return s->cr & CCR_MEN; +} + +static bool mpc_i2c_is_master(MPCI2CState *s) +{ + return s->cr & CCR_MSTA; +} + +static bool mpc_i2c_direction_is_tx(MPCI2CState *s) +{ + return s->cr & CCR_MTX; +} + +static bool mpc_i2c_irq_pending(MPCI2CState *s) +{ + return s->sr & CSR_MIF; +} + +static bool mpc_i2c_irq_is_enabled(MPCI2CState *s) +{ + return s->cr & CCR_MIEN; +} + +static void mpc_i2c_reset(DeviceState *dev) +{ + MPCI2CState *i2c = MPC_I2C(dev); + + i2c->address = 0xFF; + i2c->adr = 0x00; + i2c->fdr = 0x00; + i2c->cr = 0x00; + i2c->sr = 0x81; + i2c->dr = 0x00; +} + +static void mpc_i2c_irq(MPCI2CState *s) +{ + bool irq_active = false; + + if (mpc_i2c_is_enabled(s) && mpc_i2c_irq_is_enabled(s) + && mpc_i2c_irq_pending(s)) { + irq_active = true; + } + + if (irq_active) { + qemu_irq_raise(s->irq); + } else { + qemu_irq_lower(s->irq); + } +} + +static void mpc_i2c_soft_reset(MPCI2CState *s) +{ + /* This is a soft reset. ADR is preserved during soft resets */ + uint8_t adr = s->adr; + mpc_i2c_reset(DEVICE(s)); + s->adr = adr; +} + +static void mpc_i2c_address_send(MPCI2CState *s) +{ + /* if returns non zero slave address is not right */ + if (i2c_start_transfer(s->bus, s->dr >> 1, s->dr & (0x01))) { + s->sr |= CSR_RXAK; + } else { + s->address = s->dr; + s->sr &= ~CSR_RXAK; + s->sr |= CSR_MCF; /* Set after Byte Transfer is completed */ + s->sr |= CSR_MIF; /* Set after Byte Transfer is completed */ + mpc_i2c_irq(s); + } +} + +static void mpc_i2c_data_send(MPCI2CState *s) +{ + if (i2c_send(s->bus, s->dr)) { + /* End of transfer */ + s->sr |= CSR_RXAK; + i2c_end_transfer(s->bus); + } else { + s->sr &= ~CSR_RXAK; + s->sr |= CSR_MCF; /* Set after Byte Transfer is completed */ + s->sr |= CSR_MIF; /* Set after Byte Transfer is completed */ + mpc_i2c_irq(s); + } +} + +static void mpc_i2c_data_recive(MPCI2CState *s) +{ + int ret; + /* get the next byte */ + ret = i2c_recv(s->bus); + if (ret >= 0) { + s->sr |= CSR_MCF; /* Set after Byte Transfer is completed */ + s->sr |= CSR_MIF; /* Set after Byte Transfer is completed */ + mpc_i2c_irq(s); + } else { + DPRINTF("read failed for device"); + ret = 0xff; + } + s->dr = ret; +} + +static uint64_t mpc_i2c_read(void *opaque, hwaddr addr, unsigned size) +{ + MPCI2CState *s = opaque; + uint8_t value; + + switch (addr) { + case MPC_I2C_ADR: + value = s->adr; + break; + case MPC_I2C_FDR: + value = s->fdr; + break; + case MPC_I2C_CR: + value = s->cr; + break; + case MPC_I2C_SR: + value = s->sr; + break; + case MPC_I2C_DR: + value = s->dr; + if (mpc_i2c_is_master(s)) { /* master mode */ + if (mpc_i2c_direction_is_tx(s)) { + DPRINTF("MTX is set not in recv mode\n"); + } else { + mpc_i2c_data_recive(s); + } + } + break; + default: + value = 0; + DPRINTF("ERROR: Bad read addr 0x%x\n", (unsigned int)addr); + break; + } + + DPRINTF("%s: addr " TARGET_FMT_plx " %02" PRIx32 "\n", __func__, + addr, value); + return (uint64_t)value; +} + +static void mpc_i2c_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + MPCI2CState *s = opaque; + + DPRINTF("%s: addr " TARGET_FMT_plx " val %08" PRIx64 "\n", __func__, + addr, value); + switch (addr) { + case MPC_I2C_ADR: + s->adr = value & CADR_MASK; + break; + case MPC_I2C_FDR: + s->fdr = value & CFDR_MASK; + break; + case MPC_I2C_CR: + if (mpc_i2c_is_enabled(s) && ((value & CCR_MEN) == 0)) { + mpc_i2c_soft_reset(s); + break; + } + /* normal write */ + s->cr = value & CCR_MASK; + if (mpc_i2c_is_master(s)) { /* master mode */ + /* set the bus to busy after master is set as per RM */ + s->sr |= CSR_MBB; + } else { + /* bus is not busy anymore */ + s->sr &= ~CSR_MBB; + /* Reset the address for fresh write/read cycle */ + if (s->address != CYCLE_RESET) { + i2c_end_transfer(s->bus); + s->address = CYCLE_RESET; + } + } + /* For restart end the onging transfer */ + if (s->cr & CCR_RSTA) { + if (s->address != CYCLE_RESET) { + s->address = CYCLE_RESET; + i2c_end_transfer(s->bus); + s->cr &= ~CCR_RSTA; + } + } + break; + case MPC_I2C_SR: + s->sr = value & CSR_MASK; + /* Lower the interrupt */ + if (!(s->sr & CSR_MIF) || !(s->sr & CSR_MAL)) { + mpc_i2c_irq(s); + } + break; + case MPC_I2C_DR: + /* if the device is not enabled, nothing to do */ + if (!mpc_i2c_is_enabled(s)) { + break; + } + s->dr = value & CDR_MASK; + if (mpc_i2c_is_master(s)) { /* master mode */ + if (s->address == CYCLE_RESET) { + mpc_i2c_address_send(s); + } else { + mpc_i2c_data_send(s); + } + } + break; + case MPC_I2C_DFSRR: + s->dfssr = value; + break; + default: + DPRINTF("ERROR: Bad write addr 0x%x\n", (unsigned int)addr); + break; + } +} + +static const MemoryRegionOps i2c_ops = { + .read = mpc_i2c_read, + .write = mpc_i2c_write, + .valid.max_access_size = 1, + .endianness = DEVICE_NATIVE_ENDIAN, +}; + +static const VMStateDescription mpc_i2c_vmstate = { + .name = TYPE_MPC_I2C, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(address, MPCI2CState), + VMSTATE_UINT8(adr, MPCI2CState), + VMSTATE_UINT8(fdr, MPCI2CState), + VMSTATE_UINT8(cr, MPCI2CState), + VMSTATE_UINT8(sr, MPCI2CState), + VMSTATE_UINT8(dr, MPCI2CState), + VMSTATE_UINT8(dfssr, MPCI2CState), + VMSTATE_END_OF_LIST() + } +}; + +static void mpc_i2c_realize(DeviceState *dev, Error **errp) +{ + MPCI2CState *i2c = MPC_I2C(dev); + sysbus_init_irq(SYS_BUS_DEVICE(dev), &i2c->irq); + memory_region_init_io(&i2c->iomem, OBJECT(i2c), &i2c_ops, i2c, + "mpc-i2c", 0x14); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &i2c->iomem); + i2c->bus = i2c_init_bus(DEVICE(dev), "i2c"); +} + +static void mpc_i2c_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &mpc_i2c_vmstate ; + dc->reset = mpc_i2c_reset; + dc->realize = mpc_i2c_realize; + dc->desc = "MPC I2C Controller"; +} + +static const TypeInfo mpc_i2c_type_info = { + .name = TYPE_MPC_I2C, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MPCI2CState), + .class_init = mpc_i2c_class_init, +}; + +static void mpc_i2c_register_types(void) +{ + type_register_static(&mpc_i2c_type_info); +} + +type_init(mpc_i2c_register_types) diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 0d26e0f6b5..e48544f909 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -19,10 +19,9 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" +#include "hw/boards.h" #include "hw/i2c/pm_smbus.h" -#include "hw/i2c/smbus.h" - -/* no save/load? */ +#include "hw/i2c/smbus_master.h" #define SMBHSTSTS 0x00 #define SMBHSTCNT 0x02 @@ -31,20 +30,40 @@ #define SMBHSTDAT0 0x05 #define SMBHSTDAT1 0x06 #define SMBBLKDAT 0x07 +#define SMBAUXCTL 0x0d -#define STS_HOST_BUSY (1) -#define STS_INTR (1<<1) -#define STS_DEV_ERR (1<<2) -#define STS_BUS_ERR (1<<3) -#define STS_FAILED (1<<4) -#define STS_SMBALERT (1<<5) -#define STS_INUSE_STS (1<<6) -#define STS_BYTE_DONE (1<<7) +#define STS_HOST_BUSY (1 << 0) +#define STS_INTR (1 << 1) +#define STS_DEV_ERR (1 << 2) +#define STS_BUS_ERR (1 << 3) +#define STS_FAILED (1 << 4) +#define STS_SMBALERT (1 << 5) +#define STS_INUSE_STS (1 << 6) +#define STS_BYTE_DONE (1 << 7) /* Signs of successfully transaction end : * ByteDoneStatus = 1 (STS_BYTE_DONE) and INTR = 1 (STS_INTR ) */ -//#define DEBUG +#define CTL_INTREN (1 << 0) +#define CTL_KILL (1 << 1) +#define CTL_LAST_BYTE (1 << 5) +#define CTL_START (1 << 6) +#define CTL_PEC_EN (1 << 7) +#define CTL_RETURN_MASK 0x1f + +#define PROT_QUICK 0 +#define PROT_BYTE 1 +#define PROT_BYTE_DATA 2 +#define PROT_WORD_DATA 3 +#define PROT_PROC_CALL 4 +#define PROT_BLOCK_DATA 5 +#define PROT_I2C_BLOCK_READ 6 + +#define AUX_PEC (1 << 0) +#define AUX_BLK (1 << 1) +#define AUX_MASK 0x3 + +/*#define DEBUG*/ #ifdef DEBUG # define SMBUS_DPRINTF(format, ...) printf(format, ## __VA_ARGS__) @@ -62,19 +81,17 @@ static void smb_transaction(PMSMBus *s) I2CBus *bus = s->smbus; int ret; - assert(s->smb_stat & STS_HOST_BUSY); - s->smb_stat &= ~STS_HOST_BUSY; - SMBUS_DPRINTF("SMBus trans addr=0x%02x prot=0x%02x\n", addr, prot); /* Transaction isn't exec if STS_DEV_ERR bit set */ if ((s->smb_stat & STS_DEV_ERR) != 0) { goto error; } + switch(prot) { - case 0x0: + case PROT_QUICK: ret = smbus_quick_command(bus, addr, read); goto done; - case 0x1: + case PROT_BYTE: if (read) { ret = smbus_receive_byte(bus, addr); goto data8; @@ -82,7 +99,7 @@ static void smb_transaction(PMSMBus *s) ret = smbus_send_byte(bus, addr, cmd); goto done; } - case 0x2: + case PROT_BYTE_DATA: if (read) { ret = smbus_read_byte(bus, addr, cmd); goto data8; @@ -91,22 +108,84 @@ static void smb_transaction(PMSMBus *s) goto done; } break; - case 0x3: + case PROT_WORD_DATA: if (read) { ret = smbus_read_word(bus, addr, cmd); goto data16; } else { - ret = smbus_write_word(bus, addr, cmd, (s->smb_data1 << 8) | s->smb_data0); + ret = smbus_write_word(bus, addr, cmd, + (s->smb_data1 << 8) | s->smb_data0); goto done; } break; - case 0x5: + case PROT_I2C_BLOCK_READ: + /* According to the Linux i2c-i801 driver: + * NB: page 240 of ICH5 datasheet shows that the R/#W + * bit should be cleared here, even when reading. + * However if SPD Write Disable is set (Lynx Point and later), + * the read will fail if we don't set the R/#W bit. + * So at least Linux may or may not set the read bit here. + * So just ignore the read bit for this command. + */ + if (i2c_start_transfer(bus, addr, 0)) { + goto error; + } + ret = i2c_send(bus, s->smb_data1); + if (ret) { + goto error; + } + if (i2c_start_transfer(bus, addr, 1)) { + goto error; + } + s->in_i2c_block_read = true; + s->smb_blkdata = i2c_recv(s->smbus); + s->op_done = false; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + goto out; + + case PROT_BLOCK_DATA: if (read) { - ret = smbus_read_block(bus, addr, cmd, s->smb_data); - goto data8; + ret = smbus_read_block(bus, addr, cmd, s->smb_data, + sizeof(s->smb_data), !s->i2c_enable, + !s->i2c_enable); + if (ret < 0) { + goto error; + } + s->smb_index = 0; + s->op_done = false; + if (s->smb_auxctl & AUX_BLK) { + s->smb_stat |= STS_INTR; + } else { + s->smb_blkdata = s->smb_data[0]; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + } + s->smb_data0 = ret; + goto out; } else { - ret = smbus_write_block(bus, addr, cmd, s->smb_data, s->smb_data0); - goto done; + if (s->smb_auxctl & AUX_BLK) { + if (s->smb_index != s->smb_data0) { + s->smb_index = 0; + goto error; + } + /* Data is already all written to the queue, just do + the operation. */ + s->smb_index = 0; + ret = smbus_write_block(bus, addr, cmd, s->smb_data, + s->smb_data0, !s->i2c_enable); + if (ret < 0) { + goto error; + } + s->op_done = true; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else { + s->op_done = false; + s->smb_stat |= STS_HOST_BUSY | STS_BYTE_DONE; + s->smb_data[0] = s->smb_blkdata; + s->smb_index = 0; + ret = 0; + } + goto out; } break; default: @@ -128,38 +207,140 @@ done: if (ret < 0) { goto error; } - s->smb_stat |= STS_BYTE_DONE | STS_INTR; + s->smb_stat |= STS_INTR; +out: return; error: s->smb_stat |= STS_DEV_ERR; return; - } static void smb_transaction_start(PMSMBus *s) { - /* Do not execute immediately the command ; it will be - * executed when guest will read SMB_STAT register */ - s->smb_stat |= STS_HOST_BUSY; + if (s->smb_ctl & CTL_INTREN) { + smb_transaction(s); + s->start_transaction_on_status_read = false; + } else { + /* Do not execute immediately the command; it will be + * executed when guest will read SMB_STAT register. This + * is to work around a bug in AMIBIOS (that is working + * around another bug in some specific hardware) where + * it waits for STS_HOST_BUSY to be set before waiting + * checking for status. If STS_HOST_BUSY doesn't get + * set, it gets stuck. */ + s->smb_stat |= STS_HOST_BUSY; + s->start_transaction_on_status_read = true; + } +} + +static bool +smb_irq_value(PMSMBus *s) +{ + return ((s->smb_stat & ~STS_HOST_BUSY) != 0) && (s->smb_ctl & CTL_INTREN); +} + +static bool +smb_byte_by_byte(PMSMBus *s) +{ + if (s->op_done) { + return false; + } + if (s->in_i2c_block_read) { + return true; + } + return !(s->smb_auxctl & AUX_BLK); } static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, unsigned width) { PMSMBus *s = opaque; + uint8_t clear_byte_done; SMBUS_DPRINTF("SMB writeb port=0x%04" HWADDR_PRIx " val=0x%02" PRIx64 "\n", addr, val); switch(addr) { case SMBHSTSTS: - s->smb_stat = (~(val & 0xff)) & s->smb_stat; - s->smb_index = 0; + clear_byte_done = s->smb_stat & val & STS_BYTE_DONE; + s->smb_stat &= ~(val & ~STS_HOST_BUSY); + if (clear_byte_done && smb_byte_by_byte(s)) { + uint8_t read = s->smb_addr & 0x01; + + if (s->in_i2c_block_read) { + /* See comment below PROT_I2C_BLOCK_READ above. */ + read = 1; + } + + s->smb_index++; + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { + s->smb_index = 0; + } + if (!read && s->smb_index == s->smb_data0) { + uint8_t prot = (s->smb_ctl >> 2) & 0x07; + uint8_t cmd = s->smb_cmd; + uint8_t addr = s->smb_addr >> 1; + int ret; + + if (prot == PROT_I2C_BLOCK_READ) { + s->smb_stat |= STS_DEV_ERR; + goto out; + } + + ret = smbus_write_block(s->smbus, addr, cmd, s->smb_data, + s->smb_data0, !s->i2c_enable); + if (ret < 0) { + s->smb_stat |= STS_DEV_ERR; + goto out; + } + s->op_done = true; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else if (!read) { + s->smb_data[s->smb_index] = s->smb_blkdata; + s->smb_stat |= STS_BYTE_DONE; + } else if (s->smb_ctl & CTL_LAST_BYTE) { + s->op_done = true; + if (s->in_i2c_block_read) { + s->in_i2c_block_read = false; + s->smb_blkdata = i2c_recv(s->smbus); + i2c_nack(s->smbus); + i2c_end_transfer(s->smbus); + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + } + s->smb_index = 0; + s->smb_stat |= STS_INTR; + s->smb_stat &= ~STS_HOST_BUSY; + } else { + if (s->in_i2c_block_read) { + s->smb_blkdata = i2c_recv(s->smbus); + } else { + s->smb_blkdata = s->smb_data[s->smb_index]; + } + s->smb_stat |= STS_BYTE_DONE; + } + } break; case SMBHSTCNT: - s->smb_ctl = val; - if (val & 0x40) + s->smb_ctl = val & ~CTL_START; /* CTL_START always reads 0 */ + if (val & CTL_START) { + if (!s->op_done) { + s->smb_index = 0; + s->op_done = true; + if (s->in_i2c_block_read) { + s->in_i2c_block_read = false; + i2c_end_transfer(s->smbus); + } + } smb_transaction_start(s); + } + if (s->smb_ctl & CTL_KILL) { + s->op_done = true; + s->smb_index = 0; + s->smb_stat |= STS_FAILED; + s->smb_stat &= ~STS_HOST_BUSY; + } break; case SMBHSTCMD: s->smb_cmd = val; @@ -174,13 +355,26 @@ static void smb_ioport_writeb(void *opaque, hwaddr addr, uint64_t val, s->smb_data1 = val; break; case SMBBLKDAT: - s->smb_data[s->smb_index++] = val; - if (s->smb_index > 31) + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { s->smb_index = 0; + } + if (s->smb_auxctl & AUX_BLK) { + s->smb_data[s->smb_index++] = val; + } else { + s->smb_blkdata = val; + } + break; + case SMBAUXCTL: + s->smb_auxctl = val & AUX_MASK; break; default: break; } + + out: + if (s->set_irq) { + s->set_irq(s, smb_irq_value(s)); + } } static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) @@ -191,14 +385,15 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) switch(addr) { case SMBHSTSTS: val = s->smb_stat; - if (s->smb_stat & STS_HOST_BUSY) { + if (s->start_transaction_on_status_read) { /* execute command now */ + s->start_transaction_on_status_read = false; + s->smb_stat &= ~STS_HOST_BUSY; smb_transaction(s); } break; case SMBHSTCNT: - s->smb_index = 0; - val = s->smb_ctl & 0x1f; + val = s->smb_ctl & CTL_RETURN_MASK; break; case SMBHSTCMD: val = s->smb_cmd; @@ -213,18 +408,44 @@ static uint64_t smb_ioport_readb(void *opaque, hwaddr addr, unsigned width) val = s->smb_data1; break; case SMBBLKDAT: - val = s->smb_data[s->smb_index++]; - if (s->smb_index > 31) - s->smb_index = 0; + if (s->smb_auxctl & AUX_BLK && !s->in_i2c_block_read) { + if (s->smb_index >= PM_SMBUS_MAX_MSG_SIZE) { + s->smb_index = 0; + } + val = s->smb_data[s->smb_index++]; + if (!s->op_done && s->smb_index == s->smb_data0) { + s->op_done = true; + s->smb_index = 0; + s->smb_stat &= ~STS_HOST_BUSY; + } + } else { + val = s->smb_blkdata; + } + break; + case SMBAUXCTL: + val = s->smb_auxctl; break; default: val = 0; break; } - SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", addr, val); + SMBUS_DPRINTF("SMB readb port=0x%04" HWADDR_PRIx " val=0x%02x\n", + addr, val); + + if (s->set_irq) { + s->set_irq(s, smb_irq_value(s)); + } + return val; } +static void pm_smbus_reset(PMSMBus *s) +{ + s->op_done = true; + s->smb_index = 0; + s->smb_stat = 0; +} + static const MemoryRegionOps pm_smbus_ops = { .read = smb_ioport_readb, .write = smb_ioport_writeb, @@ -233,9 +454,44 @@ static const MemoryRegionOps pm_smbus_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -void pm_smbus_init(DeviceState *parent, PMSMBus *smb) +bool pm_smbus_vmstate_needed(void) { + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + + return !mc->smbus_no_migration_support; +} + +const VMStateDescription pmsmb_vmstate = { + .name = "pmsmb", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT8(smb_stat, PMSMBus), + VMSTATE_UINT8(smb_ctl, PMSMBus), + VMSTATE_UINT8(smb_cmd, PMSMBus), + VMSTATE_UINT8(smb_addr, PMSMBus), + VMSTATE_UINT8(smb_data0, PMSMBus), + VMSTATE_UINT8(smb_data1, PMSMBus), + VMSTATE_UINT32(smb_index, PMSMBus), + VMSTATE_UINT8_ARRAY(smb_data, PMSMBus, PM_SMBUS_MAX_MSG_SIZE), + VMSTATE_UINT8(smb_auxctl, PMSMBus), + VMSTATE_UINT8(smb_blkdata, PMSMBus), + VMSTATE_BOOL(i2c_enable, PMSMBus), + VMSTATE_BOOL(op_done, PMSMBus), + VMSTATE_BOOL(in_i2c_block_read, PMSMBus), + VMSTATE_BOOL(start_transaction_on_status_read, PMSMBus), + VMSTATE_END_OF_LIST() + } +}; + +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk) +{ + smb->op_done = true; + smb->reset = pm_smbus_reset; smb->smbus = i2c_init_bus(parent, "i2c"); + if (force_aux_blk) { + smb->smb_auxctl |= AUX_BLK; + } memory_region_init_io(&smb->io, OBJECT(parent), &pm_smbus_ops, smb, "pm-smbus", 64); } diff --git a/hw/i2c/smbus.c b/hw/i2c/smbus.c deleted file mode 100644 index 587ce1ab7f..0000000000 --- a/hw/i2c/smbus.c +++ /dev/null @@ -1,364 +0,0 @@ -/* - * QEMU SMBus device emulation. - * - * Copyright (c) 2007 CodeSourcery. - * Written by Paul Brook - * - * This code is licensed under the LGPL. - */ - -/* TODO: Implement PEC. */ - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" - -//#define DEBUG_SMBUS 1 - -#ifdef DEBUG_SMBUS -#define DPRINTF(fmt, ...) \ -do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) -#endif - -enum { - SMBUS_IDLE, - SMBUS_WRITE_DATA, - SMBUS_RECV_BYTE, - SMBUS_READ_DATA, - SMBUS_DONE, - SMBUS_CONFUSED = -1 -}; - -static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) -{ - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - DPRINTF("Quick Command %d\n", recv); - if (sc->quick_cmd) { - sc->quick_cmd(dev, recv); - } -} - -static void smbus_do_write(SMBusDevice *dev) -{ - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - - if (dev->data_len == 0) { - smbus_do_quick_cmd(dev, 0); - } else if (dev->data_len == 1) { - DPRINTF("Send Byte\n"); - if (sc->send_byte) { - sc->send_byte(dev, dev->data_buf[0]); - } - } else { - dev->command = dev->data_buf[0]; - DPRINTF("Command %d len %d\n", dev->command, dev->data_len - 1); - if (sc->write_data) { - sc->write_data(dev, dev->command, dev->data_buf + 1, - dev->data_len - 1); - } - } -} - -static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - - switch (event) { - case I2C_START_SEND: - switch (dev->mode) { - case SMBUS_IDLE: - DPRINTF("Incoming data\n"); - dev->mode = SMBUS_WRITE_DATA; - break; - default: - BADF("Unexpected send start condition in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - break; - - case I2C_START_RECV: - switch (dev->mode) { - case SMBUS_IDLE: - DPRINTF("Read mode\n"); - dev->mode = SMBUS_RECV_BYTE; - break; - case SMBUS_WRITE_DATA: - if (dev->data_len == 0) { - BADF("Read after write with no data\n"); - dev->mode = SMBUS_CONFUSED; - } else { - if (dev->data_len > 1) { - smbus_do_write(dev); - } else { - dev->command = dev->data_buf[0]; - DPRINTF("%02x: Command %d\n", dev->i2c.address, - dev->command); - } - DPRINTF("Read mode\n"); - dev->data_len = 0; - dev->mode = SMBUS_READ_DATA; - } - break; - default: - BADF("Unexpected recv start condition in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - break; - - case I2C_FINISH: - switch (dev->mode) { - case SMBUS_WRITE_DATA: - smbus_do_write(dev); - break; - case SMBUS_RECV_BYTE: - smbus_do_quick_cmd(dev, 1); - break; - case SMBUS_READ_DATA: - BADF("Unexpected stop during receive\n"); - break; - default: - /* Nothing to do. */ - break; - } - dev->mode = SMBUS_IDLE; - dev->data_len = 0; - break; - - case I2C_NACK: - switch (dev->mode) { - case SMBUS_DONE: - /* Nothing to do. */ - break; - case SMBUS_READ_DATA: - dev->mode = SMBUS_DONE; - break; - default: - BADF("Unexpected NACK in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - break; - } - } - - return 0; -} - -static int smbus_i2c_recv(I2CSlave *s) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); - int ret; - - switch (dev->mode) { - case SMBUS_RECV_BYTE: - if (sc->receive_byte) { - ret = sc->receive_byte(dev); - } else { - ret = 0; - } - DPRINTF("Receive Byte %02x\n", ret); - dev->mode = SMBUS_DONE; - break; - case SMBUS_READ_DATA: - if (sc->read_data) { - ret = sc->read_data(dev, dev->command, dev->data_len); - dev->data_len++; - } else { - ret = 0; - } - DPRINTF("Read data %02x\n", ret); - break; - default: - BADF("Unexpected read in state %d\n", dev->mode); - dev->mode = SMBUS_CONFUSED; - ret = 0; - break; - } - return ret; -} - -static int smbus_i2c_send(I2CSlave *s, uint8_t data) -{ - SMBusDevice *dev = SMBUS_DEVICE(s); - - switch (dev->mode) { - case SMBUS_WRITE_DATA: - DPRINTF("Write data %02x\n", data); - dev->data_buf[dev->data_len++] = data; - break; - default: - BADF("Unexpected write in state %d\n", dev->mode); - break; - } - return 0; -} - -/* Master device commands. */ -int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) -{ - if (i2c_start_transfer(bus, addr, read)) { - return -1; - } - i2c_end_transfer(bus); - return 0; -} - -int smbus_receive_byte(I2CBus *bus, uint8_t addr) -{ - uint8_t data; - - if (i2c_start_transfer(bus, addr, 1)) { - return -1; - } - data = i2c_recv(bus); - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, data); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) -{ - uint8_t data; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); - return -1; - } - data = i2c_recv(bus); - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, data); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) -{ - uint16_t data; - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); - return -1; - } - data = i2c_recv(bus); - data |= i2c_recv(bus) << 8; - i2c_nack(bus); - i2c_end_transfer(bus); - return data; -} - -int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) -{ - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, data & 0xff); - i2c_send(bus, data >> 8); - i2c_end_transfer(bus); - return 0; -} - -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data) -{ - int len; - int i; - - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - if (i2c_start_transfer(bus, addr, 1)) { - i2c_end_transfer(bus); - return -1; - } - len = i2c_recv(bus); - if (len > 32) { - len = 0; - } - for (i = 0; i < len; i++) { - data[i] = i2c_recv(bus); - } - i2c_nack(bus); - i2c_end_transfer(bus); - return len; -} - -int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len) -{ - int i; - - if (len > 32) - len = 32; - - if (i2c_start_transfer(bus, addr, 0)) { - return -1; - } - i2c_send(bus, command); - i2c_send(bus, len); - for (i = 0; i < len; i++) { - i2c_send(bus, data[i]); - } - i2c_end_transfer(bus); - return 0; -} - -static void smbus_device_class_init(ObjectClass *klass, void *data) -{ - I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); - - sc->event = smbus_i2c_event; - sc->recv = smbus_i2c_recv; - sc->send = smbus_i2c_send; -} - -static const TypeInfo smbus_device_type_info = { - .name = TYPE_SMBUS_DEVICE, - .parent = TYPE_I2C_SLAVE, - .instance_size = sizeof(SMBusDevice), - .abstract = true, - .class_size = sizeof(SMBusDeviceClass), - .class_init = smbus_device_class_init, -}; - -static void smbus_device_register_types(void) -{ - type_register_static(&smbus_device_type_info); -} - -type_init(smbus_device_register_types) diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index f18aa3de35..37167e7244 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -23,40 +23,38 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" #include "hw/hw.h" +#include "hw/boards.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_slave.h" +#include "hw/i2c/smbus_eeprom.h" //#define DEBUG +#define TYPE_SMBUS_EEPROM "smbus-eeprom" + +#define SMBUS_EEPROM(obj) \ + OBJECT_CHECK(SMBusEEPROMDevice, (obj), TYPE_SMBUS_EEPROM) + +#define SMBUS_EEPROM_SIZE 256 + typedef struct SMBusEEPROMDevice { SMBusDevice smbusdev; - void *data; + uint8_t data[SMBUS_EEPROM_SIZE]; + void *init_data; uint8_t offset; + bool accessed; } SMBusEEPROMDevice; -static void eeprom_quick_cmd(SMBusDevice *dev, uint8_t read) -{ -#ifdef DEBUG - printf("eeprom_quick_cmd: addr=0x%02x read=%d\n", dev->i2c.address, read); -#endif -} - -static void eeprom_send_byte(SMBusDevice *dev, uint8_t val) -{ - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; -#ifdef DEBUG - printf("eeprom_send_byte: addr=0x%02x val=0x%02x\n", - dev->i2c.address, val); -#endif - eeprom->offset = val; -} - static uint8_t eeprom_receive_byte(SMBusDevice *dev) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); uint8_t *data = eeprom->data; uint8_t val = data[eeprom->offset++]; + + eeprom->accessed = true; #ifdef DEBUG printf("eeprom_receive_byte: addr=0x%02x val=0x%02x\n", dev->i2c.address, val); @@ -64,48 +62,77 @@ static uint8_t eeprom_receive_byte(SMBusDevice *dev) return val; } -static void eeprom_write_data(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len) +static int eeprom_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - int n; + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); + uint8_t *data = eeprom->data; + + eeprom->accessed = true; #ifdef DEBUG printf("eeprom_write_byte: addr=0x%02x cmd=0x%02x val=0x%02x\n", - dev->i2c.address, cmd, buf[0]); + dev->i2c.address, buf[0], buf[1]); #endif - /* A page write operation is not a valid SMBus command. - It is a block write without a length byte. Fortunately we - get the full block anyway. */ - /* TODO: Should this set the current location? */ - if (cmd + len > 256) - n = 256 - cmd; - else - n = len; - memcpy(eeprom->data + cmd, buf, n); - len -= n; - if (len) - memcpy(eeprom->data, buf + n, len); + /* len is guaranteed to be > 0 */ + eeprom->offset = buf[0]; + buf++; + len--; + + for (; len > 0; len--) { + data[eeprom->offset] = *buf++; + eeprom->offset = (eeprom->offset + 1) % SMBUS_EEPROM_SIZE; + } + + return 0; } -static uint8_t eeprom_read_data(SMBusDevice *dev, uint8_t cmd, int n) +static bool smbus_eeprom_vmstate_needed(void *opaque) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *) dev; - /* If this is the first byte then set the current position. */ - if (n == 0) - eeprom->offset = cmd; - /* As with writes, we implement block reads without the - SMBus length byte. */ - return eeprom_receive_byte(dev); + MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); + SMBusEEPROMDevice *eeprom = opaque; + + return (eeprom->accessed || smbus_vmstate_needed(&eeprom->smbusdev)) && + !mc->smbus_no_migration_support; +} + +static const VMStateDescription vmstate_smbus_eeprom = { + .name = "smbus-eeprom", + .version_id = 1, + .minimum_version_id = 1, + .needed = smbus_eeprom_vmstate_needed, + .fields = (VMStateField[]) { + VMSTATE_SMBUS_DEVICE(smbusdev, SMBusEEPROMDevice), + VMSTATE_UINT8_ARRAY(data, SMBusEEPROMDevice, SMBUS_EEPROM_SIZE), + VMSTATE_UINT8(offset, SMBusEEPROMDevice), + VMSTATE_BOOL(accessed, SMBusEEPROMDevice), + VMSTATE_END_OF_LIST() + } +}; + +/* + * Reset the EEPROM contents to the initial state on a reset. This + * isn't really how an EEPROM works, of course, but the general + * principle of QEMU is to restore function on reset to what it would + * be if QEMU was stopped and started. + * + * The proper thing to do would be to have a backing blockdev to hold + * the contents and restore that on startup, and not do this on reset. + * But until that time, act as if we had been stopped and restarted. + */ +static void smbus_eeprom_reset(DeviceState *dev) +{ + SMBusEEPROMDevice *eeprom = SMBUS_EEPROM(dev); + + memcpy(eeprom->data, eeprom->init_data, SMBUS_EEPROM_SIZE); + eeprom->offset = 0; } static void smbus_eeprom_realize(DeviceState *dev, Error **errp) { - SMBusEEPROMDevice *eeprom = (SMBusEEPROMDevice *)dev; - - eeprom->offset = 0; + smbus_eeprom_reset(dev); } static Property smbus_eeprom_properties[] = { - DEFINE_PROP_PTR("data", SMBusEEPROMDevice, data), + DEFINE_PROP_PTR("data", SMBusEEPROMDevice, init_data), DEFINE_PROP_END_OF_LIST(), }; @@ -115,18 +142,17 @@ static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); dc->realize = smbus_eeprom_realize; - sc->quick_cmd = eeprom_quick_cmd; - sc->send_byte = eeprom_send_byte; + dc->reset = smbus_eeprom_reset; sc->receive_byte = eeprom_receive_byte; sc->write_data = eeprom_write_data; - sc->read_data = eeprom_read_data; dc->props = smbus_eeprom_properties; + dc->vmsd = &vmstate_smbus_eeprom; /* Reason: pointer property "data" */ dc->user_creatable = false; } static const TypeInfo smbus_eeprom_info = { - .name = "smbus-eeprom", + .name = TYPE_SMBUS_EEPROM, .parent = TYPE_SMBUS_DEVICE, .instance_size = sizeof(SMBusEEPROMDevice), .class_init = smbus_eeprom_class_initfn, @@ -143,7 +169,7 @@ void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf) { DeviceState *dev; - dev = qdev_create((BusState *) smbus, "smbus-eeprom"); + dev = qdev_create((BusState *) smbus, TYPE_SMBUS_EEPROM); qdev_prop_set_uint8(dev, "address", address); qdev_prop_set_ptr(dev, "data", eeprom_buf); qdev_init_nofail(dev); @@ -153,12 +179,143 @@ void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, const uint8_t *eeprom_spd, int eeprom_spd_size) { int i; - uint8_t *eeprom_buf = g_malloc0(8 * 256); /* XXX: make this persistent */ + /* XXX: make this persistent */ + + assert(nb_eeprom <= 8); + uint8_t *eeprom_buf = g_malloc0(8 * SMBUS_EEPROM_SIZE); if (eeprom_spd_size > 0) { memcpy(eeprom_buf, eeprom_spd, eeprom_spd_size); } for (i = 0; i < nb_eeprom; i++) { - smbus_eeprom_init_one(smbus, 0x50 + i, eeprom_buf + (i * 256)); + smbus_eeprom_init_one(smbus, 0x50 + i, + eeprom_buf + (i * SMBUS_EEPROM_SIZE)); } } + +/* Generate SDRAM SPD EEPROM data describing a module of type and size */ +uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t ram_size, + Error **errp) +{ + uint8_t *spd; + uint8_t nbanks; + uint16_t density; + uint32_t size; + int min_log2, max_log2, sz_log2; + int i; + + switch (type) { + case SDR: + min_log2 = 2; + max_log2 = 9; + break; + case DDR: + min_log2 = 5; + max_log2 = 12; + break; + case DDR2: + min_log2 = 7; + max_log2 = 14; + break; + default: + g_assert_not_reached(); + } + size = ram_size >> 20; /* work in terms of megabytes */ + if (size < 4) { + error_setg(errp, "SDRAM size is too small"); + return NULL; + } + sz_log2 = 31 - clz32(size); + size = 1U << sz_log2; + if (ram_size > size * MiB) { + error_setg(errp, "SDRAM size 0x"RAM_ADDR_FMT" is not a power of 2, " + "truncating to %u MB", ram_size, size); + } + if (sz_log2 < min_log2) { + error_setg(errp, + "Memory size is too small for SDRAM type, adjusting type"); + if (size >= 32) { + type = DDR; + min_log2 = 5; + max_log2 = 12; + } else { + type = SDR; + min_log2 = 2; + max_log2 = 9; + } + } + + nbanks = 1; + while (sz_log2 > max_log2 && nbanks < 8) { + sz_log2--; + nbanks++; + } + + if (size > (1ULL << sz_log2) * nbanks) { + error_setg(errp, "Memory size is too big for SDRAM, truncating"); + } + + /* split to 2 banks if possible to avoid a bug in MIPS Malta firmware */ + if (nbanks == 1 && sz_log2 > min_log2) { + sz_log2--; + nbanks++; + } + + density = 1ULL << (sz_log2 - 2); + switch (type) { + case DDR2: + density = (density & 0xe0) | (density >> 8 & 0x1f); + break; + case DDR: + density = (density & 0xf8) | (density >> 8 & 0x07); + break; + case SDR: + default: + density &= 0xff; + break; + } + + spd = g_malloc0(256); + spd[0] = 128; /* data bytes in EEPROM */ + spd[1] = 8; /* log2 size of EEPROM */ + spd[2] = type; + spd[3] = 13; /* row address bits */ + spd[4] = 10; /* column address bits */ + spd[5] = (type == DDR2 ? nbanks - 1 : nbanks); + spd[6] = 64; /* module data width */ + /* reserved / data width high */ + spd[8] = 4; /* interface voltage level */ + spd[9] = 0x25; /* highest CAS latency */ + spd[10] = 1; /* access time */ + /* DIMM configuration 0 = non-ECC */ + spd[12] = 0x82; /* refresh requirements */ + spd[13] = 8; /* primary SDRAM width */ + /* ECC SDRAM width */ + spd[15] = (type == DDR2 ? 0 : 1); /* reserved / delay for random col rd */ + spd[16] = 12; /* burst lengths supported */ + spd[17] = 4; /* banks per SDRAM device */ + spd[18] = 12; /* ~CAS latencies supported */ + spd[19] = (type == DDR2 ? 0 : 1); /* reserved / ~CS latencies supported */ + spd[20] = 2; /* DIMM type / ~WE latencies */ + /* module features */ + /* memory chip features */ + spd[23] = 0x12; /* clock cycle time @ medium CAS latency */ + /* data access time */ + /* clock cycle time @ short CAS latency */ + /* data access time */ + spd[27] = 20; /* min. row precharge time */ + spd[28] = 15; /* min. row active row delay */ + spd[29] = 20; /* min. ~RAS to ~CAS delay */ + spd[30] = 45; /* min. active to precharge time */ + spd[31] = density; + spd[32] = 20; /* addr/cmd setup time */ + spd[33] = 8; /* addr/cmd hold time */ + spd[34] = 20; /* data input setup time */ + spd[35] = 8; /* data input hold time */ + + /* checksum */ + for (i = 0; i < 63; i++) { + spd[63] += spd[i]; + } + return spd; +} diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 007cb6701d..7b24be8256 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -29,8 +29,6 @@ #include "hw/i2c/pm_smbus.h" #include "hw/pci/pci.h" #include "sysemu/sysemu.h" -#include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" #include "hw/i386/ich9.h" @@ -40,15 +38,25 @@ typedef struct ICH9SMBState { PCIDevice dev; + bool irq_enabled; + PMSMBus smb; } ICH9SMBState; +static bool ich9_vmstate_need_smbus(void *opaque, int version_id) +{ + return pm_smbus_vmstate_needed(); +} + static const VMStateDescription vmstate_ich9_smbus = { .name = "ich9_smb", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_PCI_DEVICE(dev, struct ICH9SMBState), + VMSTATE_PCI_DEVICE(dev, ICH9SMBState), + VMSTATE_BOOL_TEST(irq_enabled, ICH9SMBState, ich9_vmstate_need_smbus), + VMSTATE_STRUCT_TEST(smb, ICH9SMBState, ich9_vmstate_need_smbus, 1, + pmsmb_vmstate, PMSMBus), VMSTATE_END_OF_LIST() } }; @@ -61,12 +69,16 @@ static void ich9_smbus_write_config(PCIDevice *d, uint32_t address, pci_default_write_config(d, address, val, len); if (range_covers_byte(address, len, ICH9_SMB_HOSTC)) { uint8_t hostc = s->dev.config[ICH9_SMB_HOSTC]; - if ((hostc & ICH9_SMB_HOSTC_HST_EN) && - !(hostc & ICH9_SMB_HOSTC_I2C_EN)) { + if (hostc & ICH9_SMB_HOSTC_HST_EN) { memory_region_set_enabled(&s->smb.io, true); } else { memory_region_set_enabled(&s->smb.io, false); } + s->smb.i2c_enable = (hostc & ICH9_SMB_HOSTC_I2C_EN) != 0; + if (hostc & ICH9_SMB_HOSTC_SSRESET) { + s->smb.reset(&s->smb); + s->dev.config[ICH9_SMB_HOSTC] &= ~ICH9_SMB_HOSTC_SSRESET; + } } } @@ -80,7 +92,7 @@ static void ich9_smbus_realize(PCIDevice *d, Error **errp) pci_set_byte(d->config + ICH9_SMB_HOSTC, 0); /* TODO bar0, bar1: 64bit BAR support*/ - pm_smbus_init(&d->qdev, &s->smb); + pm_smbus_init(&d->qdev, &s->smb, false); pci_register_bar(d, ICH9_SMB_SMB_BASE_BAR, PCI_BASE_ADDRESS_SPACE_IO, &s->smb.io); } @@ -105,11 +117,25 @@ static void ich9_smb_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } +static void ich9_smb_set_irq(PMSMBus *pmsmb, bool enabled) +{ + ICH9SMBState *s = pmsmb->opaque; + + if (enabled == s->irq_enabled) { + return; + } + + s->irq_enabled = enabled; + pci_set_irq(&s->dev, enabled); +} + I2CBus *ich9_smb_init(PCIBus *bus, int devfn, uint32_t smb_io_base) { PCIDevice *d = pci_create_simple_multifunction(bus, devfn, true, TYPE_ICH9_SMB_DEVICE); ICH9SMBState *s = ICH9_SMB_DEVICE(d); + s->smb.set_irq = ich9_smb_set_irq; + s->smb.opaque = s; return s->smb.smbus; } diff --git a/hw/i2c/smbus_master.c b/hw/i2c/smbus_master.c new file mode 100644 index 0000000000..0a6223744c --- /dev/null +++ b/hw/i2c/smbus_master.c @@ -0,0 +1,165 @@ +/* + * QEMU SMBus host (master) emulation. + * + * This code emulates SMBus transactions from the master point of view, + * it runs the individual I2C transaction to do the SMBus protocol + * over I2C. + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/smbus_master.h" + +/* Master device commands. */ +int smbus_quick_command(I2CBus *bus, uint8_t addr, int read) +{ + if (i2c_start_transfer(bus, addr, read)) { + return -1; + } + i2c_end_transfer(bus); + return 0; +} + +int smbus_receive_byte(I2CBus *bus, uint8_t addr) +{ + uint8_t data; + + if (i2c_start_transfer(bus, addr, 1)) { + return -1; + } + data = i2c_recv(bus); + i2c_nack(bus); + i2c_end_transfer(bus); + return data; +} + +int smbus_send_byte(I2CBus *bus, uint8_t addr, uint8_t data) +{ + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, data); + i2c_end_transfer(bus); + return 0; +} + +int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command) +{ + uint8_t data; + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + if (i2c_start_transfer(bus, addr, 1)) { + i2c_end_transfer(bus); + return -1; + } + data = i2c_recv(bus); + i2c_nack(bus); + i2c_end_transfer(bus); + return data; +} + +int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data) +{ + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + i2c_send(bus, data); + i2c_end_transfer(bus); + return 0; +} + +int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command) +{ + uint16_t data; + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + if (i2c_start_transfer(bus, addr, 1)) { + i2c_end_transfer(bus); + return -1; + } + data = i2c_recv(bus); + data |= i2c_recv(bus) << 8; + i2c_nack(bus); + i2c_end_transfer(bus); + return data; +} + +int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data) +{ + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + i2c_send(bus, data & 0xff); + i2c_send(bus, data >> 8); + i2c_end_transfer(bus); + return 0; +} + +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd) +{ + int rlen; + int i; + + if (send_cmd) { + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + } + if (i2c_start_transfer(bus, addr, 1)) { + if (send_cmd) { + i2c_end_transfer(bus); + } + return -1; + } + if (recv_len) { + rlen = i2c_recv(bus); + } else { + rlen = len; + } + if (rlen > len) { + rlen = 0; + } + for (i = 0; i < rlen; i++) { + data[i] = i2c_recv(bus); + } + i2c_nack(bus); + i2c_end_transfer(bus); + return rlen; +} + +int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool send_len) +{ + int i; + + if (len > 32) { + len = 32; + } + + if (i2c_start_transfer(bus, addr, 0)) { + return -1; + } + i2c_send(bus, command); + if (send_len) { + i2c_send(bus, len); + } + for (i = 0; i < len; i++) { + i2c_send(bus, data[i]); + } + i2c_end_transfer(bus); + return 0; +} diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c new file mode 100644 index 0000000000..9a2d314d1a --- /dev/null +++ b/hw/i2c/smbus_slave.c @@ -0,0 +1,236 @@ +/* + * QEMU SMBus device emulation. + * + * This code is a helper for SMBus device emulation. It implements an + * I2C device inteface and runs the SMBus protocol from the device + * point of view and maps those to simple calls to emulate. + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This code is licensed under the LGPL. + */ + +/* TODO: Implement PEC. */ + +#include "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/i2c/i2c.h" +#include "hw/i2c/smbus_slave.h" + +//#define DEBUG_SMBUS 1 + +#ifdef DEBUG_SMBUS +#define DPRINTF(fmt, ...) \ +do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) +#else +#define DPRINTF(fmt, ...) do {} while(0) +#define BADF(fmt, ...) \ +do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0) +#endif + +enum { + SMBUS_IDLE, + SMBUS_WRITE_DATA, + SMBUS_READ_DATA, + SMBUS_DONE, + SMBUS_CONFUSED = -1 +}; + +static void smbus_do_quick_cmd(SMBusDevice *dev, int recv) +{ + SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); + + DPRINTF("Quick Command %d\n", recv); + if (sc->quick_cmd) { + sc->quick_cmd(dev, recv); + } +} + +static void smbus_do_write(SMBusDevice *dev) +{ + SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); + + DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len); + if (sc->write_data) { + sc->write_data(dev, dev->data_buf, dev->data_len); + } +} + +static int smbus_i2c_event(I2CSlave *s, enum i2c_event event) +{ + SMBusDevice *dev = SMBUS_DEVICE(s); + + switch (event) { + case I2C_START_SEND: + switch (dev->mode) { + case SMBUS_IDLE: + DPRINTF("Incoming data\n"); + dev->mode = SMBUS_WRITE_DATA; + break; + + default: + BADF("Unexpected send start condition in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + break; + + case I2C_START_RECV: + switch (dev->mode) { + case SMBUS_IDLE: + DPRINTF("Read mode\n"); + dev->mode = SMBUS_READ_DATA; + break; + + case SMBUS_WRITE_DATA: + if (dev->data_len == 0) { + BADF("Read after write with no data\n"); + dev->mode = SMBUS_CONFUSED; + } else { + smbus_do_write(dev); + DPRINTF("Read mode\n"); + dev->mode = SMBUS_READ_DATA; + } + break; + + default: + BADF("Unexpected recv start condition in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + break; + + case I2C_FINISH: + if (dev->data_len == 0) { + if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) { + smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA); + } + } else { + switch (dev->mode) { + case SMBUS_WRITE_DATA: + smbus_do_write(dev); + break; + + case SMBUS_READ_DATA: + BADF("Unexpected stop during receive\n"); + break; + + default: + /* Nothing to do. */ + break; + } + } + dev->mode = SMBUS_IDLE; + dev->data_len = 0; + break; + + case I2C_NACK: + switch (dev->mode) { + case SMBUS_DONE: + /* Nothing to do. */ + break; + + case SMBUS_READ_DATA: + dev->mode = SMBUS_DONE; + break; + + default: + BADF("Unexpected NACK in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + } + + return 0; +} + +static uint8_t smbus_i2c_recv(I2CSlave *s) +{ + SMBusDevice *dev = SMBUS_DEVICE(s); + SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev); + uint8_t ret = 0xff; + + switch (dev->mode) { + case SMBUS_READ_DATA: + if (sc->receive_byte) { + ret = sc->receive_byte(dev); + } + DPRINTF("Read data %02x\n", ret); + break; + + default: + BADF("Unexpected read in state %d\n", dev->mode); + dev->mode = SMBUS_CONFUSED; + break; + } + + return ret; +} + +static int smbus_i2c_send(I2CSlave *s, uint8_t data) +{ + SMBusDevice *dev = SMBUS_DEVICE(s); + + switch (dev->mode) { + case SMBUS_WRITE_DATA: + DPRINTF("Write data %02x\n", data); + if (dev->data_len >= sizeof(dev->data_buf)) { + BADF("Too many bytes sent\n"); + } else { + dev->data_buf[dev->data_len++] = data; + } + break; + + default: + BADF("Unexpected write in state %d\n", dev->mode); + break; + } + + return 0; +} + +static void smbus_device_class_init(ObjectClass *klass, void *data) +{ + I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); + + sc->event = smbus_i2c_event; + sc->recv = smbus_i2c_recv; + sc->send = smbus_i2c_send; +} + +bool smbus_vmstate_needed(SMBusDevice *dev) +{ + return dev->mode != SMBUS_IDLE; +} + +const VMStateDescription vmstate_smbus_device = { + .name = TYPE_SMBUS_DEVICE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_I2C_SLAVE(i2c, SMBusDevice), + VMSTATE_INT32(mode, SMBusDevice), + VMSTATE_INT32(data_len, SMBusDevice), + VMSTATE_UINT8_ARRAY(data_buf, SMBusDevice, SMBUS_DATA_MAX_LEN), + VMSTATE_END_OF_LIST() + } +}; + +static const TypeInfo smbus_device_type_info = { + .name = TYPE_SMBUS_DEVICE, + .parent = TYPE_I2C_SLAVE, + .instance_size = sizeof(SMBusDevice), + .abstract = true, + .class_size = sizeof(SMBusDeviceClass), + .class_init = smbus_device_class_init, +}; + +static void smbus_device_register_types(void) +{ + type_register_static(&smbus_device_type_info); +} + +type_init(smbus_device_register_types) diff --git a/hw/i2c/trace-events b/hw/i2c/trace-events index d339b61202..e1c810d5bd 100644 --- a/hw/i2c/trace-events +++ b/hw/i2c/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/i2c/core.c +# core.c i2c_event(const char *event, uint8_t address) "%s(addr:0x%02x)" i2c_send(uint8_t address, uint8_t data) "send(addr:0x%02x) data:0x%02x" diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig new file mode 100644 index 0000000000..a6aed7c131 --- /dev/null +++ b/hw/i386/Kconfig @@ -0,0 +1,101 @@ +config SEV + bool + depends on KVM + +config PC + bool + imply APPLESMC + imply HYPERV + imply ISA_IPMI_KCS + imply ISA_IPMI_BT + imply ISA_DEBUG + imply PARALLEL + imply PCI_DEVICES + imply PVPANIC + imply QXL + imply SEV + imply SGA + imply TEST_DEVICES + imply TPM_CRB + imply TPM_TIS + imply VGA_PCI + imply VIRTIO_VGA + select FDC + select I8259 + select I8254 + select PCKBD + select PCSPK + select I8257 + select MC146818RTC + # For ACPI builder: + select SERIAL_ISA + select ACPI_VMGENID + +config PC_PCI + bool + select APIC + select IOAPIC + select APM + select PC + +config PC_ACPI + bool + select ACPI_X86 + select ACPI_CPU_HOTPLUG + select ACPI_MEMORY_HOTPLUG + select SMBUS_EEPROM + select PFLASH_CFI01 + depends on ACPI_SMBUS + +config I440FX + bool + imply E1000_PCI + select PC_PCI + select PC_ACPI + select ACPI_SMBUS + select PCI_PIIX + select IDE_PIIX + select DIMM + select SMBIOS + select VMPORT + select VMMOUSE + select FW_CFG_DMA + +config ISAPC + bool + select ISA_BUS + select PC + select IDE_ISA + select VGA_ISA + # FIXME: it is in the same file as i440fx, and does not compile + # if separated + depends on I440FX + +config Q35 + bool + imply VTD + imply AMD_IOMMU + imply E1000E_PCI_EXPRESS + select PC_PCI + select PC_ACPI + select PCI_EXPRESS_Q35 + select LPC_ICH9 + select AHCI + select DIMM + select SMBIOS + select VMPORT + select VMMOUSE + select FW_CFG_DMA + +config VTD + bool + +config AMD_IOMMU + bool + +config VMPORT + bool + +config VMMOUSE + bool + depends on VMPORT diff --git a/hw/i386/Makefile.objs b/hw/i386/Makefile.objs index fa87a14152..27248a0777 100644 --- a/hw/i386/Makefile.objs +++ b/hw/i386/Makefile.objs @@ -1,9 +1,12 @@ obj-$(CONFIG_KVM) += kvm/ obj-y += multiboot.o -obj-y += pc.o pc_piix.o pc_q35.o +obj-y += pc.o +obj-$(CONFIG_I440FX) += pc_piix.o +obj-$(CONFIG_Q35) += pc_q35.o obj-y += pc_sysfw.o -obj-$(CONFIG_VTD) += x86-iommu.o intel_iommu.o -obj-$(CONFIG_AMD_IOMMU) += x86-iommu.o amd_iommu.o +obj-y += x86-iommu.o +obj-$(CONFIG_VTD) += intel_iommu.o +obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o obj-$(CONFIG_XEN) += ../xenpv/ xen/ obj-$(CONFIG_VMPORT) += vmport.o obj-$(CONFIG_VMMOUSE) += vmmouse.o diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 974f223850..701a11b441 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -119,6 +119,12 @@ typedef struct AcpiBuildPciBusHotplugState { bool pcihp_bridge_en; } AcpiBuildPciBusHotplugState; +typedef struct FwCfgTPMConfig { + uint32_t tpmppi_address; + uint8_t tpm_version; + uint8_t tpmppi_version; +} QEMU_PACKED FwCfgTPMConfig; + static void init_common_fadt_data(Object *o, AcpiFadtData *data) { uint32_t io = object_property_get_uint(o, ACPI_PM_PROP_PM_IO_BASE, NULL); @@ -295,7 +301,7 @@ static void acpi_align_size(GArray *blob, unsigned align) /* FACS */ static void -build_facs(GArray *table_data, BIOSLinker *linker) +build_facs(GArray *table_data) { AcpiFacsDescriptorRev1 *facs = acpi_data_push(table_data, sizeof *facs); memcpy(&facs->signature, "FACS", 4); @@ -1799,6 +1805,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, uint32_t nr_mem = machine->ram_slots; int root_bus_limit = 0xFF; PCIBus *bus = NULL; + TPMIf *tpm = tpm_find(); int i; dsdt = init_aml_allocator(); @@ -1843,7 +1850,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); } else { CPUHotplugFeatures opts = { - .apci_1_compatible = true, .has_legacy_cphp = true + .acpi_1_compatible = true, .has_legacy_cphp = true }; build_cpus_aml(dsdt, machine, opts, pm->cpu_hp_io_base, "\\_SB.PCI0", "\\_GPE._E02"); @@ -1863,7 +1870,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(scope, method); } - if (pcms->acpi_nvdimm_state.is_enabled) { + if (machine->nvdimms_state->is_enabled) { method = aml_method("_E04", 0, AML_NOTSERIALIZED); aml_append(method, aml_notify(aml_name("\\_SB.NVDR"), aml_int(0x80))); @@ -2136,9 +2143,17 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* Scan all PCI buses. Generate tables to support hotplug. */ build_append_pci_bus_devices(scope, bus, pm->pcihp_bridge_en); - if (TPM_IS_TIS(tpm_find())) { - dev = aml_device("ISA.TPM"); - aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0C31"))); + if (TPM_IS_TIS(tpm)) { + if (misc->tpm_version == TPM_VERSION_2_0) { + dev = aml_device("TPM"); + aml_append(dev, aml_name_decl("_HID", + aml_string("MSFT0101"))); + } else { + dev = aml_device("ISA.TPM"); + aml_append(dev, aml_name_decl("_HID", + aml_eisaid("PNP0C31"))); + } + aml_append(dev, aml_name_decl("_STA", aml_int(0xF))); crs = aml_resource_template(); aml_append(crs, aml_memory32_fixed(TPM_TIS_ADDR_BASE, @@ -2150,6 +2165,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, */ /* aml_append(crs, aml_irq_no_flags(TPM_TIS_IRQ)); */ aml_append(dev, aml_name_decl("_CRS", crs)); + + tpm_build_ppi_acpi(tpm, dev); + aml_append(scope, dev); } @@ -2157,7 +2175,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } } - if (TPM_IS_CRB(tpm_find())) { + if (TPM_IS_CRB(tpm)) { dev = aml_device("TPM"); aml_append(dev, aml_name_decl("_HID", aml_string("MSFT0101"))); crs = aml_resource_template(); @@ -2169,6 +2187,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(method, aml_return(aml_int(0x0f))); aml_append(dev, method); + tpm_build_ppi_acpi(tpm, dev); + aml_append(sb_scope, dev); } @@ -2254,64 +2274,6 @@ build_tpm2(GArray *table_data, BIOSLinker *linker, GArray *tcpalog) #define HOLE_640K_START (640 * KiB) #define HOLE_640K_END (1 * MiB) -static void build_srat_hotpluggable_memory(GArray *table_data, uint64_t base, - uint64_t len, int default_node) -{ - MemoryDeviceInfoList *info_list = qmp_memory_device_list(); - MemoryDeviceInfoList *info; - MemoryDeviceInfo *mi; - PCDIMMDeviceInfo *di; - uint64_t end = base + len, cur, size; - bool is_nvdimm; - AcpiSratMemoryAffinity *numamem; - MemoryAffinityFlags flags; - - for (cur = base, info = info_list; - cur < end; - cur += size, info = info->next) { - numamem = acpi_data_push(table_data, sizeof *numamem); - - if (!info) { - /* - * Entry is required for Windows to enable memory hotplug in OS - * and for Linux to enable SWIOTLB when booted with less than - * 4G of RAM. Windows works better if the entry sets proximity - * to the highest NUMA node in the machine at the end of the - * reserved space. - * Memory devices may override proximity set by this entry, - * providing _PXM method if necessary. - */ - build_srat_memory(numamem, end - 1, 1, default_node, - MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); - break; - } - - mi = info->value; - is_nvdimm = (mi->type == MEMORY_DEVICE_INFO_KIND_NVDIMM); - di = !is_nvdimm ? mi->u.dimm.data : mi->u.nvdimm.data; - - if (cur < di->addr) { - build_srat_memory(numamem, cur, di->addr - cur, default_node, - MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); - numamem = acpi_data_push(table_data, sizeof *numamem); - } - - size = di->size; - - flags = MEM_AFFINITY_ENABLED; - if (di->hotpluggable) { - flags |= MEM_AFFINITY_HOTPLUGGABLE; - } - if (is_nvdimm) { - flags |= MEM_AFFINITY_NON_VOLATILE; - } - - build_srat_memory(numamem, di->addr, size, di->node, flags); - } - - qapi_free_MemoryDeviceInfoList(info_list); -} - static void build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) { @@ -2417,10 +2379,19 @@ build_srat(GArray *table_data, BIOSLinker *linker, MachineState *machine) build_srat_memory(numamem, 0, 0, 0, MEM_AFFINITY_NOFLAGS); } + /* + * Entry is required for Windows to enable memory hotplug in OS + * and for Linux to enable SWIOTLB when booted with less than + * 4G of RAM. Windows works better if the entry sets proximity + * to the highest NUMA node in the machine. + * Memory devices may override proximity set by this entry, + * providing _PXM method if necessary. + */ if (hotplugabble_address_space_size) { - build_srat_hotpluggable_memory(table_data, machine->device_memory->base, - hotplugabble_address_space_size, - pcms->numa_nodes - 1); + numamem = acpi_data_push(table_data, sizeof *numamem); + build_srat_memory(numamem, machine->device_memory->base, + hotplugabble_address_space_size, pcms->numa_nodes - 1, + MEM_AFFINITY_HOTPLUGGABLE | MEM_AFFINITY_ENABLED); } build_header(linker, table_data, @@ -2478,7 +2449,7 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker) IntelIOMMUState *intel_iommu = INTEL_IOMMU_DEVICE(iommu); assert(iommu); - if (iommu->intr_supported) { + if (x86_iommu_ir_supported(iommu)) { dmar_flags |= 0x1; /* Flags: 0x1: INT_REMAP */ } @@ -2519,9 +2490,12 @@ build_dmar_q35(GArray *table_data, BIOSLinker *linker) * IVRS table as specified in AMD IOMMU Specification v2.62, Section 5.2 * accessible here http://support.amd.com/TechDocs/48882_IOMMU.pdf */ +#define IOAPIC_SB_DEVID (uint64_t)PCI_BUILD_BDF(0, PCI_DEVFN(0x14, 0)) + static void build_amd_iommu(GArray *table_data, BIOSLinker *linker) { + int ivhd_table_len = 28; int iommu_start = table_data->len; AMDVIState *s = AMD_IOMMU_DEVICE(x86_iommu_get_default()); @@ -2543,8 +2517,16 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker) (1UL << 6) | /* PrefSup */ (1UL << 7), /* PPRSup */ 1); + + /* + * When interrupt remapping is supported, we add a special IVHD device + * for type IO-APIC. + */ + if (x86_iommu_ir_supported(x86_iommu_get_default())) { + ivhd_table_len += 8; + } /* IVHD length */ - build_append_int_noprefix(table_data, 28, 2); + build_append_int_noprefix(table_data, ivhd_table_len, 2); /* DeviceID */ build_append_int_noprefix(table_data, s->devid, 2); /* Capability offset */ @@ -2559,7 +2541,8 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker) build_append_int_noprefix(table_data, (48UL << 30) | /* HATS */ (48UL << 28) | /* GATS */ - (1UL << 2), /* GTSup */ + (1UL << 2) | /* GTSup */ + (1UL << 6), /* GASup */ 4); /* * Type 1 device entry reporting all devices @@ -2568,36 +2551,25 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker) */ build_append_int_noprefix(table_data, 0x0000001, 4); + /* + * Add a special IVHD device type. + * Refer to spec - Table 95: IVHD device entry type codes + * + * Linux IOMMU driver checks for the special IVHD device (type IO-APIC). + * See Linux kernel commit 'c2ff5cf5294bcbd7fa50f7d860e90a66db7e5059' + */ + if (x86_iommu_ir_supported(x86_iommu_get_default())) { + build_append_int_noprefix(table_data, + (0x1ull << 56) | /* type IOAPIC */ + (IOAPIC_SB_DEVID << 40) | /* IOAPIC devid */ + 0x48, /* special device */ + 8); + } + build_header(linker, table_data, (void *)(table_data->data + iommu_start), "IVRS", table_data->len - iommu_start, 1, NULL, NULL); } -static GArray * -build_rsdp(GArray *rsdp_table, BIOSLinker *linker, unsigned rsdt_tbl_offset) -{ - AcpiRsdpDescriptor *rsdp = acpi_data_push(rsdp_table, sizeof *rsdp); - unsigned rsdt_pa_size = sizeof(rsdp->rsdt_physical_address); - unsigned rsdt_pa_offset = - (char *)&rsdp->rsdt_physical_address - rsdp_table->data; - - bios_linker_loader_alloc(linker, ACPI_BUILD_RSDP_FILE, rsdp_table, 16, - true /* fseg memory */); - - memcpy(&rsdp->signature, "RSD PTR ", 8); - memcpy(rsdp->oem_id, ACPI_BUILD_APPNAME6, 6); - /* Address to be filled by Guest linker */ - bios_linker_loader_add_pointer(linker, - ACPI_BUILD_RSDP_FILE, rsdt_pa_offset, rsdt_pa_size, - ACPI_BUILD_TABLE_FILE, rsdt_tbl_offset); - - /* Checksum to be filled by Guest linker */ - bios_linker_loader_add_checksum(linker, ACPI_BUILD_RSDP_FILE, - (char *)rsdp - rsdp_table->data, sizeof *rsdp, - (char *)&rsdp->checksum - rsdp_table->data); - - return rsdp_table; -} - typedef struct AcpiBuildState { /* Copy of table in RAM (for patching). */ @@ -2668,7 +2640,7 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) * requirements. */ facs = tables_blob->len; - build_facs(tables_blob, tables->linker); + build_facs(tables_blob); /* DSDT is pointed to by FADT */ dsdt = tables_blob->len; @@ -2735,9 +2707,9 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) build_dmar_q35(tables_blob, tables->linker); } } - if (pcms->acpi_nvdimm_state.is_enabled) { + if (machine->nvdimms_state->is_enabled) { nvdimm_build_acpi(table_offsets, tables_blob, tables->linker, - &pcms->acpi_nvdimm_state, machine->ram_slots); + machine->nvdimms_state, machine->ram_slots); } /* Add tables supplied by user (if any) */ @@ -2754,7 +2726,25 @@ void acpi_build(AcpiBuildTables *tables, MachineState *machine) slic_oem.id, slic_oem.table_id); /* RSDP is in FSEG memory, so allocate it separately */ - build_rsdp(tables->rsdp, tables->linker, rsdt); + { + AcpiRsdpData rsdp_data = { + .revision = 0, + .oem_id = ACPI_BUILD_APPNAME6, + .xsdt_tbl_offset = NULL, + .rsdt_tbl_offset = &rsdt, + }; + build_rsdp(tables->rsdp, tables->linker, &rsdp_data); + if (!pcmc->rsdp_in_ram) { + /* We used to allocate some extra space for RSDP revision 2 but + * only used the RSDP revision 0 space. The extra bytes were + * zeroed out and not used. + * Here we continue wasting those extra 16 bytes to make sure we + * don't break migration for machine types 2.2 and older due to + * RSDP blob size mismatch. + */ + build_append_int_noprefix(tables->rsdp, 0, 16); + } + } /* We'll expose it all to Guest so we want to reduce * chance of size changes. @@ -2880,6 +2870,8 @@ void acpi_setup(void) AcpiBuildTables tables; AcpiBuildState *build_state; Object *vmgenid_dev; + TPMIf *tpm; + static FwCfgTPMConfig tpm_config; if (!pcms->fw_cfg) { ACPI_BUILD_DPRINTF("No fw cfg. Bailing out.\n"); @@ -2914,6 +2906,17 @@ void acpi_setup(void) fw_cfg_add_file(pcms->fw_cfg, ACPI_BUILD_TPMLOG_FILE, tables.tcpalog->data, acpi_data_len(tables.tcpalog)); + tpm = tpm_find(); + if (tpm && object_property_get_bool(OBJECT(tpm), "ppi", &error_abort)) { + tpm_config = (FwCfgTPMConfig) { + .tpmppi_address = cpu_to_le32(TPM_PPI_ADDR_BASE), + .tpm_version = tpm_get_version(tpm), + .tpmppi_version = TPM_PPI_VERSION_1_30 + }; + fw_cfg_add_file(pcms->fw_cfg, "etc/tpm/config", + &tpm_config, sizeof tpm_config); + } + vmgenid_dev = find_vmgenid_dev(); if (vmgenid_dev) { vmgenid_add_fw_cfg(VMGENID(vmgenid_dev), pcms->fw_cfg, diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 1fd669fef8..6eabdf9917 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -2,7 +2,7 @@ * QEMU emulation of AMD IOMMU (AMD-Vi) * * Copyright (C) 2011 Eduard - Gabriel Munteanu - * Copyright (C) 2015 David Kiarie, + * Copyright (C) 2015, 2016 David Kiarie Kahurani * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +26,9 @@ #include "amd_iommu.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "hw/i386/apic_internal.h" #include "trace.h" +#include "hw/i386/apic-msidef.h" /* used AMD-Vi MMIO registers */ const char *amdvi_mmio_low[] = { @@ -55,6 +57,7 @@ struct AMDVIAddressSpace { uint8_t bus_num; /* bus number */ uint8_t devfn; /* device function */ AMDVIState *iommu_state; /* AMDVI - one per machine */ + MemoryRegion root; /* AMDVI Root memory map region */ IOMMUMemoryRegion iommu; /* Device's address translation region */ MemoryRegion iommu_ir; /* Device's interrupt remapping region */ AddressSpace as; /* device's corresponding address space */ @@ -605,6 +608,7 @@ static void amdvi_handle_control_write(AMDVIState *s) s->completion_wait_intr = !!(control & AMDVI_MMIO_CONTROL_COMWAITINTEN); s->cmdbuf_enabled = s->enabled && !!(control & AMDVI_MMIO_CONTROL_CMDBUFLEN); + s->ga_enabled = !!(control & AMDVI_MMIO_CONTROL_GAEN); /* update the flags depending on the control register */ if (s->cmdbuf_enabled) { @@ -807,7 +811,7 @@ static inline uint64_t amdvi_get_perms(uint64_t entry) AMDVI_DEV_PERM_SHIFT; } -/* a valid entry should have V = 1 and reserved bits honoured */ +/* validate that reserved bits are honoured */ static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, uint64_t *dte) { @@ -820,7 +824,7 @@ static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, return false; } - return dte[0] & AMDVI_DEV_VALID; + return true; } /* get a device table entry given the devid */ @@ -966,8 +970,12 @@ static void amdvi_do_translate(AMDVIAddressSpace *as, hwaddr addr, return; } - /* devices with V = 0 are not translated */ if (!amdvi_get_dte(s, devid, entry)) { + return; + } + + /* devices with V = 0 are not translated */ + if (!(entry[0] & AMDVI_DEV_VALID)) { goto out; } @@ -1026,10 +1034,366 @@ static IOMMUTLBEntry amdvi_translate(IOMMUMemoryRegion *iommu, hwaddr addr, return ret; } +static int amdvi_get_irte(AMDVIState *s, MSIMessage *origin, uint64_t *dte, + union irte *irte, uint16_t devid) +{ + uint64_t irte_root, offset; + + irte_root = dte[2] & AMDVI_IR_PHYS_ADDR_MASK; + offset = (origin->data & AMDVI_IRTE_OFFSET) << 2; + + trace_amdvi_ir_irte(irte_root, offset); + + if (dma_memory_read(&address_space_memory, irte_root + offset, + irte, sizeof(*irte))) { + trace_amdvi_ir_err("failed to get irte"); + return -AMDVI_IR_GET_IRTE; + } + + trace_amdvi_ir_irte_val(irte->val); + + return 0; +} + +static int amdvi_int_remap_legacy(AMDVIState *iommu, + MSIMessage *origin, + MSIMessage *translated, + uint64_t *dte, + X86IOMMUIrq *irq, + uint16_t sid) +{ + int ret; + union irte irte; + + /* get interrupt remapping table */ + ret = amdvi_get_irte(iommu, origin, dte, &irte, sid); + if (ret < 0) { + return ret; + } + + if (!irte.fields.valid) { + trace_amdvi_ir_target_abort("RemapEn is disabled"); + return -AMDVI_IR_TARGET_ABORT; + } + + if (irte.fields.guest_mode) { + error_report_once("guest mode is not zero"); + return -AMDVI_IR_ERR; + } + + if (irte.fields.int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) { + error_report_once("reserved int_type"); + return -AMDVI_IR_ERR; + } + + irq->delivery_mode = irte.fields.int_type; + irq->vector = irte.fields.vector; + irq->dest_mode = irte.fields.dm; + irq->redir_hint = irte.fields.rq_eoi; + irq->dest = irte.fields.destination; + + return 0; +} + +static int amdvi_get_irte_ga(AMDVIState *s, MSIMessage *origin, uint64_t *dte, + struct irte_ga *irte, uint16_t devid) +{ + uint64_t irte_root, offset; + + irte_root = dte[2] & AMDVI_IR_PHYS_ADDR_MASK; + offset = (origin->data & AMDVI_IRTE_OFFSET) << 4; + trace_amdvi_ir_irte(irte_root, offset); + + if (dma_memory_read(&address_space_memory, irte_root + offset, + irte, sizeof(*irte))) { + trace_amdvi_ir_err("failed to get irte_ga"); + return -AMDVI_IR_GET_IRTE; + } + + trace_amdvi_ir_irte_ga_val(irte->hi.val, irte->lo.val); + return 0; +} + +static int amdvi_int_remap_ga(AMDVIState *iommu, + MSIMessage *origin, + MSIMessage *translated, + uint64_t *dte, + X86IOMMUIrq *irq, + uint16_t sid) +{ + int ret; + struct irte_ga irte; + + /* get interrupt remapping table */ + ret = amdvi_get_irte_ga(iommu, origin, dte, &irte, sid); + if (ret < 0) { + return ret; + } + + if (!irte.lo.fields_remap.valid) { + trace_amdvi_ir_target_abort("RemapEn is disabled"); + return -AMDVI_IR_TARGET_ABORT; + } + + if (irte.lo.fields_remap.guest_mode) { + error_report_once("guest mode is not zero"); + return -AMDVI_IR_ERR; + } + + if (irte.lo.fields_remap.int_type > AMDVI_IOAPIC_INT_TYPE_ARBITRATED) { + error_report_once("reserved int_type is set"); + return -AMDVI_IR_ERR; + } + + irq->delivery_mode = irte.lo.fields_remap.int_type; + irq->vector = irte.hi.fields.vector; + irq->dest_mode = irte.lo.fields_remap.dm; + irq->redir_hint = irte.lo.fields_remap.rq_eoi; + irq->dest = irte.lo.fields_remap.destination; + + return 0; +} + +static int __amdvi_int_remap_msi(AMDVIState *iommu, + MSIMessage *origin, + MSIMessage *translated, + uint64_t *dte, + X86IOMMUIrq *irq, + uint16_t sid) +{ + int ret; + uint8_t int_ctl; + + int_ctl = (dte[2] >> AMDVI_IR_INTCTL_SHIFT) & 3; + trace_amdvi_ir_intctl(int_ctl); + + switch (int_ctl) { + case AMDVI_IR_INTCTL_PASS: + memcpy(translated, origin, sizeof(*origin)); + return 0; + case AMDVI_IR_INTCTL_REMAP: + break; + case AMDVI_IR_INTCTL_ABORT: + trace_amdvi_ir_target_abort("int_ctl abort"); + return -AMDVI_IR_TARGET_ABORT; + default: + trace_amdvi_ir_err("int_ctl reserved"); + return -AMDVI_IR_ERR; + } + + if (iommu->ga_enabled) { + ret = amdvi_int_remap_ga(iommu, origin, translated, dte, irq, sid); + } else { + ret = amdvi_int_remap_legacy(iommu, origin, translated, dte, irq, sid); + } + + return ret; +} + +/* Interrupt remapping for MSI/MSI-X entry */ +static int amdvi_int_remap_msi(AMDVIState *iommu, + MSIMessage *origin, + MSIMessage *translated, + uint16_t sid) +{ + int ret = 0; + uint64_t pass = 0; + uint64_t dte[4] = { 0 }; + X86IOMMUIrq irq = { 0 }; + uint8_t dest_mode, delivery_mode; + + assert(origin && translated); + + /* + * When IOMMU is enabled, interrupt remap request will come either from + * IO-APIC or PCI device. If interrupt is from PCI device then it will + * have a valid requester id but if the interrupt is from IO-APIC + * then requester id will be invalid. + */ + if (sid == X86_IOMMU_SID_INVALID) { + sid = AMDVI_IOAPIC_SB_DEVID; + } + + trace_amdvi_ir_remap_msi_req(origin->address, origin->data, sid); + + /* check if device table entry is set before we go further. */ + if (!iommu || !iommu->devtab_len) { + memcpy(translated, origin, sizeof(*origin)); + goto out; + } + + if (!amdvi_get_dte(iommu, sid, dte)) { + return -AMDVI_IR_ERR; + } + + /* Check if IR is enabled in DTE */ + if (!(dte[2] & AMDVI_IR_REMAP_ENABLE)) { + memcpy(translated, origin, sizeof(*origin)); + goto out; + } + + /* validate that we are configure with intremap=on */ + if (!x86_iommu_ir_supported(X86_IOMMU_DEVICE(iommu))) { + trace_amdvi_err("Interrupt remapping is enabled in the guest but " + "not in the host. Use intremap=on to enable interrupt " + "remapping in amd-iommu."); + return -AMDVI_IR_ERR; + } + + if (origin->address & AMDVI_MSI_ADDR_HI_MASK) { + trace_amdvi_err("MSI address high 32 bits non-zero when " + "Interrupt Remapping enabled."); + return -AMDVI_IR_ERR; + } + + if ((origin->address & AMDVI_MSI_ADDR_LO_MASK) != APIC_DEFAULT_ADDRESS) { + trace_amdvi_err("MSI is not from IOAPIC."); + return -AMDVI_IR_ERR; + } + + /* + * The MSI data register [10:8] are used to get the upstream interrupt type. + * + * See MSI/MSI-X format: + * https://pdfs.semanticscholar.org/presentation/9420/c279e942eca568157711ef5c92b800c40a79.pdf + * (page 5) + */ + delivery_mode = (origin->data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 7; + + switch (delivery_mode) { + case AMDVI_IOAPIC_INT_TYPE_FIXED: + case AMDVI_IOAPIC_INT_TYPE_ARBITRATED: + trace_amdvi_ir_delivery_mode("fixed/arbitrated"); + ret = __amdvi_int_remap_msi(iommu, origin, translated, dte, &irq, sid); + if (ret < 0) { + goto remap_fail; + } else { + /* Translate IRQ to MSI messages */ + x86_iommu_irq_to_msi_message(&irq, translated); + goto out; + } + break; + case AMDVI_IOAPIC_INT_TYPE_SMI: + error_report("SMI is not supported!"); + ret = -AMDVI_IR_ERR; + break; + case AMDVI_IOAPIC_INT_TYPE_NMI: + pass = dte[3] & AMDVI_DEV_NMI_PASS_MASK; + trace_amdvi_ir_delivery_mode("nmi"); + break; + case AMDVI_IOAPIC_INT_TYPE_INIT: + pass = dte[3] & AMDVI_DEV_INT_PASS_MASK; + trace_amdvi_ir_delivery_mode("init"); + break; + case AMDVI_IOAPIC_INT_TYPE_EINT: + pass = dte[3] & AMDVI_DEV_EINT_PASS_MASK; + trace_amdvi_ir_delivery_mode("eint"); + break; + default: + trace_amdvi_ir_delivery_mode("unsupported delivery_mode"); + ret = -AMDVI_IR_ERR; + break; + } + + if (ret < 0) { + goto remap_fail; + } + + /* + * The MSI address register bit[2] is used to get the destination + * mode. The dest_mode 1 is valid for fixed and arbitrated interrupts + * only. + */ + dest_mode = (origin->address >> MSI_ADDR_DEST_MODE_SHIFT) & 1; + if (dest_mode) { + trace_amdvi_ir_err("invalid dest_mode"); + ret = -AMDVI_IR_ERR; + goto remap_fail; + } + + if (pass) { + memcpy(translated, origin, sizeof(*origin)); + } else { + trace_amdvi_ir_err("passthrough is not enabled"); + ret = -AMDVI_IR_ERR; + goto remap_fail; + } + +out: + trace_amdvi_ir_remap_msi(origin->address, origin->data, + translated->address, translated->data); + return 0; + +remap_fail: + return ret; +} + +static int amdvi_int_remap(X86IOMMUState *iommu, + MSIMessage *origin, + MSIMessage *translated, + uint16_t sid) +{ + return amdvi_int_remap_msi(AMD_IOMMU_DEVICE(iommu), origin, + translated, sid); +} + +static MemTxResult amdvi_mem_ir_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + int ret; + MSIMessage from = { 0, 0 }, to = { 0, 0 }; + uint16_t sid = AMDVI_IOAPIC_SB_DEVID; + + from.address = (uint64_t) addr + AMDVI_INT_ADDR_FIRST; + from.data = (uint32_t) value; + + trace_amdvi_mem_ir_write_req(addr, value, size); + + if (!attrs.unspecified) { + /* We have explicit Source ID */ + sid = attrs.requester_id; + } + + ret = amdvi_int_remap_msi(opaque, &from, &to, sid); + if (ret < 0) { + /* TODO: log the event using IOMMU log event interface */ + error_report_once("failed to remap interrupt from devid 0x%x", sid); + return MEMTX_ERROR; + } + + apic_get_class()->send_msi(&to); + + trace_amdvi_mem_ir_write(to.address, to.data); + return MEMTX_OK; +} + +static MemTxResult amdvi_mem_ir_read(void *opaque, hwaddr addr, + uint64_t *data, unsigned size, + MemTxAttrs attrs) +{ + return MEMTX_OK; +} + +static const MemoryRegionOps amdvi_ir_ops = { + .read_with_attrs = amdvi_mem_ir_read, + .write_with_attrs = amdvi_mem_ir_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl = { + .min_access_size = 4, + .max_access_size = 4, + }, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) { + char name[128]; AMDVIState *s = opaque; - AMDVIAddressSpace **iommu_as; + AMDVIAddressSpace **iommu_as, *amdvi_dev_as; int bus_num = pci_bus_num(bus); iommu_as = s->address_spaces[bus_num]; @@ -1042,19 +1406,45 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) /* set up AMD-Vi region */ if (!iommu_as[devfn]) { + snprintf(name, sizeof(name), "amd_iommu_devfn_%d", devfn); + iommu_as[devfn] = g_malloc0(sizeof(AMDVIAddressSpace)); iommu_as[devfn]->bus_num = (uint8_t)bus_num; iommu_as[devfn]->devfn = (uint8_t)devfn; iommu_as[devfn]->iommu_state = s; - memory_region_init_iommu(&iommu_as[devfn]->iommu, - sizeof(iommu_as[devfn]->iommu), + amdvi_dev_as = iommu_as[devfn]; + + /* + * Memory region relationships looks like (Address range shows + * only lower 32 bits to make it short in length...): + * + * |-----------------+-------------------+----------| + * | Name | Address range | Priority | + * |-----------------+-------------------+----------+ + * | amdvi_root | 00000000-ffffffff | 0 | + * | amdvi_iommu | 00000000-ffffffff | 1 | + * | amdvi_iommu_ir | fee00000-feefffff | 64 | + * |-----------------+-------------------+----------| + */ + memory_region_init_iommu(&amdvi_dev_as->iommu, + sizeof(amdvi_dev_as->iommu), TYPE_AMD_IOMMU_MEMORY_REGION, OBJECT(s), - "amd-iommu", UINT64_MAX); - address_space_init(&iommu_as[devfn]->as, - MEMORY_REGION(&iommu_as[devfn]->iommu), - "amd-iommu"); + "amd_iommu", UINT64_MAX); + memory_region_init(&amdvi_dev_as->root, OBJECT(s), + "amdvi_root", UINT64_MAX); + address_space_init(&amdvi_dev_as->as, &amdvi_dev_as->root, name); + memory_region_init_io(&amdvi_dev_as->iommu_ir, OBJECT(s), + &amdvi_ir_ops, s, "amd_iommu_ir", + AMDVI_INT_ADDR_SIZE); + memory_region_add_subregion_overlap(&amdvi_dev_as->root, + AMDVI_INT_ADDR_FIRST, + &amdvi_dev_as->iommu_ir, + 64); + memory_region_add_subregion_overlap(&amdvi_dev_as->root, 0, + MEMORY_REGION(&amdvi_dev_as->iommu), + 1); } return &iommu_as[devfn]->as; } @@ -1172,6 +1562,9 @@ static void amdvi_realize(DeviceState *dev, Error **err) return; } + /* Pseudo address space under root PCI bus. */ + pcms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); + /* set up MMIO */ memory_region_init_io(&s->mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", AMDVI_MMIO_SIZE); @@ -1205,6 +1598,7 @@ static void amdvi_class_init(ObjectClass *klass, void* data) dc->vmsd = &vmstate_amdvi; dc->hotpluggable = false; dc_class->realize = amdvi_realize; + dc_class->int_remap = amdvi_int_remap; /* Supported by the pc-q35-* machine types */ dc->user_creatable = true; } diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 874030582d..0ff9095f32 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -2,7 +2,7 @@ * QEMU emulation of an AMD IOMMU (AMD-Vi) * * Copyright (C) 2011 Eduard - Gabriel Munteanu - * Copyright (C) 2015 David Kiarie, + * Copyright (C) 2015, 2016 David Kiarie Kahurani * * 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 @@ -103,6 +103,7 @@ #define AMDVI_MMIO_CONTROL_EVENTINTEN (1ULL << 3) #define AMDVI_MMIO_CONTROL_COMWAITINTEN (1ULL << 4) #define AMDVI_MMIO_CONTROL_CMDBUFLEN (1ULL << 12) +#define AMDVI_MMIO_CONTROL_GAEN (1ULL << 17) /* MMIO status register bits */ #define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4) @@ -175,7 +176,7 @@ /* extended feature support */ #define AMDVI_EXT_FEATURES (AMDVI_FEATURE_PREFETCH | AMDVI_FEATURE_PPR | \ AMDVI_FEATURE_IA | AMDVI_FEATURE_GT | AMDVI_FEATURE_HE | \ - AMDVI_GATS_MODE | AMDVI_HATS_MODE) + AMDVI_GATS_MODE | AMDVI_HATS_MODE | AMDVI_FEATURE_GA) /* capabilities header */ #define AMDVI_CAPAB_FEATURES (AMDVI_CAPAB_FLAT_EXT | \ @@ -206,8 +207,94 @@ #define AMDVI_COMMAND_SIZE 16 -#define AMDVI_INT_ADDR_FIRST 0xfee00000 -#define AMDVI_INT_ADDR_LAST 0xfeefffff +#define AMDVI_INT_ADDR_FIRST 0xfee00000 +#define AMDVI_INT_ADDR_LAST 0xfeefffff +#define AMDVI_INT_ADDR_SIZE (AMDVI_INT_ADDR_LAST - AMDVI_INT_ADDR_FIRST + 1) +#define AMDVI_MSI_ADDR_HI_MASK (0xffffffff00000000ULL) +#define AMDVI_MSI_ADDR_LO_MASK (0x00000000ffffffffULL) + +/* SB IOAPIC is always on this device in AMD systems */ +#define AMDVI_IOAPIC_SB_DEVID PCI_BUILD_BDF(0, PCI_DEVFN(0x14, 0)) + +/* Interrupt remapping errors */ +#define AMDVI_IR_ERR 0x1 +#define AMDVI_IR_GET_IRTE 0x2 +#define AMDVI_IR_TARGET_ABORT 0x3 + +/* Interrupt remapping */ +#define AMDVI_IR_REMAP_ENABLE 1ULL +#define AMDVI_IR_INTCTL_SHIFT 60 +#define AMDVI_IR_INTCTL_ABORT 0 +#define AMDVI_IR_INTCTL_PASS 1 +#define AMDVI_IR_INTCTL_REMAP 2 + +#define AMDVI_IR_PHYS_ADDR_MASK (((1ULL << 45) - 1) << 6) + +/* MSI data 10:0 bits (section 2.2.5.1 Fig 14) */ +#define AMDVI_IRTE_OFFSET 0x7ff + +/* Delivery mode of MSI data (same as IOAPIC deilver mode encoding) */ +#define AMDVI_IOAPIC_INT_TYPE_FIXED 0x0 +#define AMDVI_IOAPIC_INT_TYPE_ARBITRATED 0x1 +#define AMDVI_IOAPIC_INT_TYPE_SMI 0x2 +#define AMDVI_IOAPIC_INT_TYPE_NMI 0x4 +#define AMDVI_IOAPIC_INT_TYPE_INIT 0x5 +#define AMDVI_IOAPIC_INT_TYPE_EINT 0x7 + +/* Pass through interrupt */ +#define AMDVI_DEV_INT_PASS_MASK (1ULL << 56) +#define AMDVI_DEV_EINT_PASS_MASK (1ULL << 57) +#define AMDVI_DEV_NMI_PASS_MASK (1ULL << 58) +#define AMDVI_DEV_LINT0_PASS_MASK (1ULL << 62) +#define AMDVI_DEV_LINT1_PASS_MASK (1ULL << 63) + +/* Interrupt remapping table fields (Guest VAPIC not enabled) */ +union irte { + uint32_t val; + struct { + uint32_t valid:1, + no_fault:1, + int_type:3, + rq_eoi:1, + dm:1, + guest_mode:1, + destination:8, + vector:8, + rsvd:8; + } fields; +}; + +/* Interrupt remapping table fields (Guest VAPIC is enabled) */ +union irte_ga_lo { + uint64_t val; + + /* For int remapping */ + struct { + uint64_t valid:1, + no_fault:1, + /* ------ */ + int_type:3, + rq_eoi:1, + dm:1, + /* ------ */ + guest_mode:1, + destination:8, + rsvd_1:48; + } fields_remap; +}; + +union irte_ga_hi { + uint64_t val; + struct { + uint64_t vector:8, + rsvd_2:56; + } fields; +}; + +struct irte_ga { + union irte_ga_lo lo; + union irte_ga_hi hi; +}; #define TYPE_AMD_IOMMU_DEVICE "amd-iommu" #define AMD_IOMMU_DEVICE(obj)\ @@ -278,6 +365,9 @@ typedef struct AMDVIState { /* IOTLB */ GHashTable *iotlb; + + /* Interrupt remapping */ + bool ga_enabled; } AMDVIState; #endif diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0a8cd4e9cc..2558f48fe6 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -37,6 +37,30 @@ #include "kvm_i386.h" #include "trace.h" +/* context entry operations */ +#define VTD_CE_GET_RID2PASID(ce) \ + ((ce)->val[1] & VTD_SM_CONTEXT_ENTRY_RID2PASID_MASK) +#define VTD_CE_GET_PASID_DIR_TABLE(ce) \ + ((ce)->val[0] & VTD_PASID_DIR_BASE_ADDR_MASK) + +/* pe operations */ +#define VTD_PE_GET_TYPE(pe) ((pe)->val[0] & VTD_SM_PASID_ENTRY_PGTT) +#define VTD_PE_GET_LEVEL(pe) (2 + (((pe)->val[0] >> 2) & VTD_SM_PASID_ENTRY_AW)) +#define VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write) {\ + if (ret_fr) { \ + ret_fr = -ret_fr; \ + if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { \ + trace_vtd_fault_disabled(); \ + } else { \ + vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); \ + } \ + goto error; \ + } \ +} + +static void vtd_address_space_refresh_all(IntelIOMMUState *s); +static void vtd_address_space_unmap(VTDAddressSpace *as, IOMMUNotifier *n); + static void vtd_define_quad(IntelIOMMUState *s, hwaddr addr, uint64_t val, uint64_t wmask, uint64_t w1cmask) { @@ -138,6 +162,15 @@ static inline void vtd_iommu_unlock(IntelIOMMUState *s) qemu_mutex_unlock(&s->iommu_lock); } +static void vtd_update_scalable_state(IntelIOMMUState *s) +{ + uint64_t val = vtd_get_quad_raw(s, DMAR_RTADDR_REG); + + if (s->scalable_mode) { + s->root_scalable = val & VTD_RTADDR_SMT; + } +} + /* Whether the address space needs to notify new mappings */ static inline gboolean vtd_as_has_map_notifier(VTDAddressSpace *as) { @@ -227,6 +260,14 @@ static void vtd_reset_iotlb(IntelIOMMUState *s) vtd_iommu_unlock(s); } +static void vtd_reset_caches(IntelIOMMUState *s) +{ + vtd_iommu_lock(s); + vtd_reset_iotlb_locked(s); + vtd_reset_context_cache_locked(s); + vtd_iommu_unlock(s); +} + static uint64_t vtd_get_iotlb_key(uint64_t gfn, uint16_t source_id, uint32_t level) { @@ -311,14 +352,14 @@ static void vtd_generate_fault_event(IntelIOMMUState *s, uint32_t pre_fsts) { if (pre_fsts & VTD_FSTS_PPF || pre_fsts & VTD_FSTS_PFO || pre_fsts & VTD_FSTS_IQE) { - trace_vtd_err("There are previous interrupt conditions " - "to be serviced by software, fault event " - "is not generated."); + error_report_once("There are previous interrupt conditions " + "to be serviced by software, fault event " + "is not generated"); return; } vtd_set_clear_mask_long(s, DMAR_FECTL_REG, 0, VTD_FECTL_IP); if (vtd_get_long_raw(s, DMAR_FECTL_REG) & VTD_FECTL_IM) { - trace_vtd_err("Interrupt Mask set, irq is not generated."); + error_report_once("Interrupt Mask set, irq is not generated"); } else { vtd_generate_interrupt(s, DMAR_FEADDR_REG, DMAR_FEDATA_REG); vtd_set_clear_mask_long(s, DMAR_FECTL_REG, VTD_FECTL_IP, 0); @@ -426,20 +467,20 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, trace_vtd_dmar_fault(source_id, fault, addr, is_write); if (fsts_reg & VTD_FSTS_PFO) { - trace_vtd_err("New fault is not recorded due to " - "Primary Fault Overflow."); + error_report_once("New fault is not recorded due to " + "Primary Fault Overflow"); return; } if (vtd_try_collapse_fault(s, source_id)) { - trace_vtd_err("New fault is not recorded due to " - "compression of faults."); + error_report_once("New fault is not recorded due to " + "compression of faults"); return; } if (vtd_is_frcd_set(s, s->next_frcd_reg)) { - trace_vtd_err("Next Fault Recording Reg is used, " - "new fault is not recorded, set PFO field."); + error_report_once("Next Fault Recording Reg is used, " + "new fault is not recorded, set PFO field"); vtd_set_clear_mask_long(s, DMAR_FSTS_REG, 0, VTD_FSTS_PFO); return; } @@ -447,8 +488,8 @@ static void vtd_report_dmar_fault(IntelIOMMUState *s, uint16_t source_id, vtd_record_frcd(s, s->next_frcd_reg, source_id, addr, fault, is_write); if (fsts_reg & VTD_FSTS_PPF) { - trace_vtd_err("There are pending faults already, " - "fault event is not generated."); + error_report_once("There are pending faults already, " + "fault event is not generated"); vtd_set_frcd_and_update_ppf(s, s->next_frcd_reg); s->next_frcd_reg++; if (s->next_frcd_reg == DMAR_FRCD_REG_NR) { @@ -501,9 +542,15 @@ static void vtd_generate_completion_event(IntelIOMMUState *s) } } -static inline bool vtd_root_entry_present(VTDRootEntry *root) +static inline bool vtd_root_entry_present(IntelIOMMUState *s, + VTDRootEntry *re, + uint8_t devfn) { - return root->val & VTD_ROOT_ENTRY_P; + if (s->root_scalable && devfn > UINT8_MAX / 2) { + return re->hi & VTD_ROOT_ENTRY_P; + } + + return re->lo & VTD_ROOT_ENTRY_P; } static int vtd_get_root_entry(IntelIOMMUState *s, uint8_t index, @@ -513,11 +560,11 @@ static int vtd_get_root_entry(IntelIOMMUState *s, uint8_t index, addr = s->root + index * sizeof(*re); if (dma_memory_read(&address_space_memory, addr, re, sizeof(*re))) { - trace_vtd_re_invalid(re->rsvd, re->val); - re->val = 0; + re->lo = 0; return -VTD_FR_ROOT_TABLE_INV; } - re->val = le64_to_cpu(re->val); + re->lo = le64_to_cpu(re->lo); + re->hi = le64_to_cpu(re->hi); return 0; } @@ -526,19 +573,35 @@ static inline bool vtd_ce_present(VTDContextEntry *context) return context->lo & VTD_CONTEXT_ENTRY_P; } -static int vtd_get_context_entry_from_root(VTDRootEntry *root, uint8_t index, +static int vtd_get_context_entry_from_root(IntelIOMMUState *s, + VTDRootEntry *re, + uint8_t index, VTDContextEntry *ce) { - dma_addr_t addr; + dma_addr_t addr, ce_size; /* we have checked that root entry is present */ - addr = (root->val & VTD_ROOT_ENTRY_CTP) + index * sizeof(*ce); - if (dma_memory_read(&address_space_memory, addr, ce, sizeof(*ce))) { - trace_vtd_re_invalid(root->rsvd, root->val); + ce_size = s->root_scalable ? VTD_CTX_ENTRY_SCALABLE_SIZE : + VTD_CTX_ENTRY_LEGACY_SIZE; + + if (s->root_scalable && index > UINT8_MAX / 2) { + index = index & (~VTD_DEVFN_CHECK_MASK); + addr = re->hi & VTD_ROOT_ENTRY_CTP; + } else { + addr = re->lo & VTD_ROOT_ENTRY_CTP; + } + + addr = addr + index * ce_size; + if (dma_memory_read(&address_space_memory, addr, ce, ce_size)) { return -VTD_FR_CONTEXT_TABLE_INV; } + ce->lo = le64_to_cpu(ce->lo); ce->hi = le64_to_cpu(ce->hi); + if (ce_size == VTD_CTX_ENTRY_SCALABLE_SIZE) { + ce->val[2] = le64_to_cpu(ce->val[2]); + ce->val[3] = le64_to_cpu(ce->val[3]); + } return 0; } @@ -591,38 +654,16 @@ static inline bool vtd_is_level_supported(IntelIOMMUState *s, uint32_t level) (1ULL << (level - 2 + VTD_CAP_SAGAW_SHIFT)); } -/* Get the page-table level that hardware should use for the second-level - * page-table walk from the Address Width field of context-entry. - */ -static inline uint32_t vtd_ce_get_level(VTDContextEntry *ce) -{ - return 2 + (ce->hi & VTD_CONTEXT_ENTRY_AW); -} - -static inline uint32_t vtd_ce_get_agaw(VTDContextEntry *ce) -{ - return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9; -} - -static inline uint32_t vtd_ce_get_type(VTDContextEntry *ce) -{ - return ce->lo & VTD_CONTEXT_ENTRY_TT; -} - /* Return true if check passed, otherwise false */ -static inline bool vtd_ce_type_check(X86IOMMUState *x86_iommu, - VTDContextEntry *ce) +static inline bool vtd_pe_type_check(X86IOMMUState *x86_iommu, + VTDPASIDEntry *pe) { - switch (vtd_ce_get_type(ce)) { - case VTD_CONTEXT_TT_MULTI_LEVEL: - /* Always supported */ + switch (VTD_PE_GET_TYPE(pe)) { + case VTD_SM_PASID_ENTRY_FLT: + case VTD_SM_PASID_ENTRY_SLT: + case VTD_SM_PASID_ENTRY_NESTED: break; - case VTD_CONTEXT_TT_DEV_IOTLB: - if (!x86_iommu->dt_supported) { - return false; - } - break; - case VTD_CONTEXT_TT_PASS_THROUGH: + case VTD_SM_PASID_ENTRY_PT: if (!x86_iommu->pt_supported) { return false; } @@ -634,21 +675,226 @@ static inline bool vtd_ce_type_check(X86IOMMUState *x86_iommu, return true; } -static inline uint64_t vtd_iova_limit(VTDContextEntry *ce, uint8_t aw) +static int vtd_get_pasid_dire(dma_addr_t pasid_dir_base, + uint32_t pasid, + VTDPASIDDirEntry *pdire) { - uint32_t ce_agaw = vtd_ce_get_agaw(ce); + uint32_t index; + dma_addr_t addr, entry_size; + + index = VTD_PASID_DIR_INDEX(pasid); + entry_size = VTD_PASID_DIR_ENTRY_SIZE; + addr = pasid_dir_base + index * entry_size; + if (dma_memory_read(&address_space_memory, addr, pdire, entry_size)) { + return -VTD_FR_PASID_TABLE_INV; + } + + return 0; +} + +static int vtd_get_pasid_entry(IntelIOMMUState *s, + uint32_t pasid, + VTDPASIDDirEntry *pdire, + VTDPASIDEntry *pe) +{ + uint32_t index; + dma_addr_t addr, entry_size; + X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); + + index = VTD_PASID_TABLE_INDEX(pasid); + entry_size = VTD_PASID_ENTRY_SIZE; + addr = pdire->val & VTD_PASID_TABLE_BASE_ADDR_MASK; + addr = addr + index * entry_size; + if (dma_memory_read(&address_space_memory, addr, pe, entry_size)) { + return -VTD_FR_PASID_TABLE_INV; + } + + /* Do translation type check */ + if (!vtd_pe_type_check(x86_iommu, pe)) { + return -VTD_FR_PASID_TABLE_INV; + } + + if (!vtd_is_level_supported(s, VTD_PE_GET_LEVEL(pe))) { + return -VTD_FR_PASID_TABLE_INV; + } + + return 0; +} + +static int vtd_get_pasid_entry_from_pasid(IntelIOMMUState *s, + dma_addr_t pasid_dir_base, + uint32_t pasid, + VTDPASIDEntry *pe) +{ + int ret; + VTDPASIDDirEntry pdire; + + ret = vtd_get_pasid_dire(pasid_dir_base, pasid, &pdire); + if (ret) { + return ret; + } + + ret = vtd_get_pasid_entry(s, pasid, &pdire, pe); + if (ret) { + return ret; + } + + return ret; +} + +static int vtd_ce_get_rid2pasid_entry(IntelIOMMUState *s, + VTDContextEntry *ce, + VTDPASIDEntry *pe) +{ + uint32_t pasid; + dma_addr_t pasid_dir_base; + int ret = 0; + + pasid = VTD_CE_GET_RID2PASID(ce); + pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce); + ret = vtd_get_pasid_entry_from_pasid(s, pasid_dir_base, pasid, pe); + + return ret; +} + +static int vtd_ce_get_pasid_fpd(IntelIOMMUState *s, + VTDContextEntry *ce, + bool *pe_fpd_set) +{ + int ret; + uint32_t pasid; + dma_addr_t pasid_dir_base; + VTDPASIDDirEntry pdire; + VTDPASIDEntry pe; + + pasid = VTD_CE_GET_RID2PASID(ce); + pasid_dir_base = VTD_CE_GET_PASID_DIR_TABLE(ce); + + ret = vtd_get_pasid_dire(pasid_dir_base, pasid, &pdire); + if (ret) { + return ret; + } + + if (pdire.val & VTD_PASID_DIR_FPD) { + *pe_fpd_set = true; + return 0; + } + + ret = vtd_get_pasid_entry(s, pasid, &pdire, &pe); + if (ret) { + return ret; + } + + if (pe.val[0] & VTD_PASID_ENTRY_FPD) { + *pe_fpd_set = true; + } + + return 0; +} + +/* Get the page-table level that hardware should use for the second-level + * page-table walk from the Address Width field of context-entry. + */ +static inline uint32_t vtd_ce_get_level(VTDContextEntry *ce) +{ + return 2 + (ce->hi & VTD_CONTEXT_ENTRY_AW); +} + +static uint32_t vtd_get_iova_level(IntelIOMMUState *s, + VTDContextEntry *ce) +{ + VTDPASIDEntry pe; + + if (s->root_scalable) { + vtd_ce_get_rid2pasid_entry(s, ce, &pe); + return VTD_PE_GET_LEVEL(&pe); + } + + return vtd_ce_get_level(ce); +} + +static inline uint32_t vtd_ce_get_agaw(VTDContextEntry *ce) +{ + return 30 + (ce->hi & VTD_CONTEXT_ENTRY_AW) * 9; +} + +static uint32_t vtd_get_iova_agaw(IntelIOMMUState *s, + VTDContextEntry *ce) +{ + VTDPASIDEntry pe; + + if (s->root_scalable) { + vtd_ce_get_rid2pasid_entry(s, ce, &pe); + return 30 + ((pe.val[0] >> 2) & VTD_SM_PASID_ENTRY_AW) * 9; + } + + return vtd_ce_get_agaw(ce); +} + +static inline uint32_t vtd_ce_get_type(VTDContextEntry *ce) +{ + return ce->lo & VTD_CONTEXT_ENTRY_TT; +} + +/* Only for Legacy Mode. Return true if check passed, otherwise false */ +static inline bool vtd_ce_type_check(X86IOMMUState *x86_iommu, + VTDContextEntry *ce) +{ + switch (vtd_ce_get_type(ce)) { + case VTD_CONTEXT_TT_MULTI_LEVEL: + /* Always supported */ + break; + case VTD_CONTEXT_TT_DEV_IOTLB: + if (!x86_iommu->dt_supported) { + error_report_once("%s: DT specified but not supported", __func__); + return false; + } + break; + case VTD_CONTEXT_TT_PASS_THROUGH: + if (!x86_iommu->pt_supported) { + error_report_once("%s: PT specified but not supported", __func__); + return false; + } + break; + default: + /* Unknown type */ + error_report_once("%s: unknown ce type: %"PRIu32, __func__, + vtd_ce_get_type(ce)); + return false; + } + return true; +} + +static inline uint64_t vtd_iova_limit(IntelIOMMUState *s, + VTDContextEntry *ce, uint8_t aw) +{ + uint32_t ce_agaw = vtd_get_iova_agaw(s, ce); return 1ULL << MIN(ce_agaw, aw); } /* Return true if IOVA passes range check, otherwise false. */ -static inline bool vtd_iova_range_check(uint64_t iova, VTDContextEntry *ce, +static inline bool vtd_iova_range_check(IntelIOMMUState *s, + uint64_t iova, VTDContextEntry *ce, uint8_t aw) { /* * Check if @iova is above 2^X-1, where X is the minimum of MGAW * in CAP_REG and AW in context-entry. */ - return !(iova & ~(vtd_iova_limit(ce, aw) - 1)); + return !(iova & ~(vtd_iova_limit(s, ce, aw) - 1)); +} + +static dma_addr_t vtd_get_iova_pgtbl_base(IntelIOMMUState *s, + VTDContextEntry *ce) +{ + VTDPASIDEntry pe; + + if (s->root_scalable) { + vtd_ce_get_rid2pasid_entry(s, ce, &pe); + return pe.val[0] & VTD_SM_PASID_ENTRY_SLPTPTR; + } + + return vtd_ce_get_slpt_base(ce); } /* @@ -694,18 +940,20 @@ static VTDBus *vtd_find_as_from_bus_num(IntelIOMMUState *s, uint8_t bus_num) /* Given the @iova, get relevant @slptep. @slpte_level will be the last level * of the translation, can be used for deciding the size of large page. */ -static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, +static int vtd_iova_to_slpte(IntelIOMMUState *s, VTDContextEntry *ce, + uint64_t iova, bool is_write, uint64_t *slptep, uint32_t *slpte_level, bool *reads, bool *writes, uint8_t aw_bits) { - dma_addr_t addr = vtd_ce_get_slpt_base(ce); - uint32_t level = vtd_ce_get_level(ce); + dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce); + uint32_t level = vtd_get_iova_level(s, ce); uint32_t offset; uint64_t slpte; uint64_t access_right_check; - if (!vtd_iova_range_check(iova, ce, aw_bits)) { - trace_vtd_err_dmar_iova_overflow(iova); + if (!vtd_iova_range_check(s, iova, ce, aw_bits)) { + error_report_once("%s: detected IOVA overflow (iova=0x%" PRIx64 ")", + __func__, iova); return -VTD_FR_ADDR_BEYOND_MGAW; } @@ -717,8 +965,9 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, slpte = vtd_get_slpte(addr, offset); if (slpte == (uint64_t)-1) { - trace_vtd_err_dmar_slpte_read_error(iova, level); - if (level == vtd_ce_get_level(ce)) { + error_report_once("%s: detected read error on DMAR slpte " + "(iova=0x%" PRIx64 ")", __func__, iova); + if (level == vtd_get_iova_level(s, ce)) { /* Invalid programming of context-entry */ return -VTD_FR_CONTEXT_ENTRY_INV; } else { @@ -728,11 +977,17 @@ static int vtd_iova_to_slpte(VTDContextEntry *ce, uint64_t iova, bool is_write, *reads = (*reads) && (slpte & VTD_SL_R); *writes = (*writes) && (slpte & VTD_SL_W); if (!(slpte & access_right_check)) { - trace_vtd_err_dmar_slpte_perm_error(iova, level, slpte, is_write); + error_report_once("%s: detected slpte permission error " + "(iova=0x%" PRIx64 ", level=0x%" PRIx32 ", " + "slpte=0x%" PRIx64 ", write=%d)", __func__, + iova, level, slpte, is_write); return is_write ? -VTD_FR_WRITE : -VTD_FR_READ; } if (vtd_slpte_nonzero_rsvd(slpte, level)) { - trace_vtd_err_dmar_slpte_resv_error(iova, level, slpte); + error_report_once("%s: detected splte reserve non-zero " + "iova=0x%" PRIx64 ", level=0x%" PRIx32 + "slpte=0x%" PRIx64 ")", __func__, iova, + level, slpte); return -VTD_FR_PAGING_ENTRY_RSVD; } @@ -941,29 +1196,96 @@ next: /** * vtd_page_walk - walk specific IOVA range, and call the hook * + * @s: intel iommu state * @ce: context entry to walk upon * @start: IOVA address to start the walk * @end: IOVA range end address (start <= addr < end) * @info: page walking information struct */ -static int vtd_page_walk(VTDContextEntry *ce, uint64_t start, uint64_t end, +static int vtd_page_walk(IntelIOMMUState *s, VTDContextEntry *ce, + uint64_t start, uint64_t end, vtd_page_walk_info *info) { - dma_addr_t addr = vtd_ce_get_slpt_base(ce); - uint32_t level = vtd_ce_get_level(ce); + dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce); + uint32_t level = vtd_get_iova_level(s, ce); - if (!vtd_iova_range_check(start, ce, info->aw)) { + if (!vtd_iova_range_check(s, start, ce, info->aw)) { return -VTD_FR_ADDR_BEYOND_MGAW; } - if (!vtd_iova_range_check(end, ce, info->aw)) { + if (!vtd_iova_range_check(s, end, ce, info->aw)) { /* Fix end so that it reaches the maximum */ - end = vtd_iova_limit(ce, info->aw); + end = vtd_iova_limit(s, ce, info->aw); } return vtd_page_walk_level(addr, start, end, level, true, true, info); } +static int vtd_root_entry_rsvd_bits_check(IntelIOMMUState *s, + VTDRootEntry *re) +{ + /* Legacy Mode reserved bits check */ + if (!s->root_scalable && + (re->hi || (re->lo & VTD_ROOT_ENTRY_RSVD(s->aw_bits)))) + goto rsvd_err; + + /* Scalable Mode reserved bits check */ + if (s->root_scalable && + ((re->lo & VTD_ROOT_ENTRY_RSVD(s->aw_bits)) || + (re->hi & VTD_ROOT_ENTRY_RSVD(s->aw_bits)))) + goto rsvd_err; + + return 0; + +rsvd_err: + error_report_once("%s: invalid root entry: hi=0x%"PRIx64 + ", lo=0x%"PRIx64, + __func__, re->hi, re->lo); + return -VTD_FR_ROOT_ENTRY_RSVD; +} + +static inline int vtd_context_entry_rsvd_bits_check(IntelIOMMUState *s, + VTDContextEntry *ce) +{ + if (!s->root_scalable && + (ce->hi & VTD_CONTEXT_ENTRY_RSVD_HI || + ce->lo & VTD_CONTEXT_ENTRY_RSVD_LO(s->aw_bits))) { + error_report_once("%s: invalid context entry: hi=%"PRIx64 + ", lo=%"PRIx64" (reserved nonzero)", + __func__, ce->hi, ce->lo); + return -VTD_FR_CONTEXT_ENTRY_RSVD; + } + + if (s->root_scalable && + (ce->val[0] & VTD_SM_CONTEXT_ENTRY_RSVD_VAL0(s->aw_bits) || + ce->val[1] & VTD_SM_CONTEXT_ENTRY_RSVD_VAL1 || + ce->val[2] || + ce->val[3])) { + error_report_once("%s: invalid context entry: val[3]=%"PRIx64 + ", val[2]=%"PRIx64 + ", val[1]=%"PRIx64 + ", val[0]=%"PRIx64" (reserved nonzero)", + __func__, ce->val[3], ce->val[2], + ce->val[1], ce->val[0]); + return -VTD_FR_CONTEXT_ENTRY_RSVD; + } + + return 0; +} + +static int vtd_ce_rid2pasid_check(IntelIOMMUState *s, + VTDContextEntry *ce) +{ + VTDPASIDEntry pe; + + /* + * Make sure in Scalable Mode, a present context entry + * has valid rid2pasid setting, which includes valid + * rid2pasid field and corresponding pasid entry setting + */ + return vtd_ce_get_rid2pasid_entry(s, ce, &pe); +} + /* Map a device to its corresponding domain (context-entry) */ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, uint8_t devfn, VTDContextEntry *ce) @@ -977,18 +1299,18 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return ret_fr; } - if (!vtd_root_entry_present(&re)) { + if (!vtd_root_entry_present(s, &re, devfn)) { /* Not error - it's okay we don't have root entry. */ trace_vtd_re_not_present(bus_num); return -VTD_FR_ROOT_ENTRY_P; } - if (re.rsvd || (re.val & VTD_ROOT_ENTRY_RSVD(s->aw_bits))) { - trace_vtd_re_invalid(re.rsvd, re.val); - return -VTD_FR_ROOT_ENTRY_RSVD; + ret_fr = vtd_root_entry_rsvd_bits_check(s, &re); + if (ret_fr) { + return ret_fr; } - ret_fr = vtd_get_context_entry_from_root(&re, devfn, ce); + ret_fr = vtd_get_context_entry_from_root(s, &re, devfn, ce); if (ret_fr) { return ret_fr; } @@ -999,22 +1321,38 @@ static int vtd_dev_to_context_entry(IntelIOMMUState *s, uint8_t bus_num, return -VTD_FR_CONTEXT_ENTRY_P; } - if ((ce->hi & VTD_CONTEXT_ENTRY_RSVD_HI) || - (ce->lo & VTD_CONTEXT_ENTRY_RSVD_LO(s->aw_bits))) { - trace_vtd_ce_invalid(ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_RSVD; + ret_fr = vtd_context_entry_rsvd_bits_check(s, ce); + if (ret_fr) { + return ret_fr; } /* Check if the programming of context-entry is valid */ - if (!vtd_is_level_supported(s, vtd_ce_get_level(ce))) { - trace_vtd_ce_invalid(ce->hi, ce->lo); + if (!s->root_scalable && + !vtd_is_level_supported(s, vtd_ce_get_level(ce))) { + error_report_once("%s: invalid context entry: hi=%"PRIx64 + ", lo=%"PRIx64" (level %d not supported)", + __func__, ce->hi, ce->lo, + vtd_ce_get_level(ce)); return -VTD_FR_CONTEXT_ENTRY_INV; } - /* Do translation type check */ - if (!vtd_ce_type_check(x86_iommu, ce)) { - trace_vtd_ce_invalid(ce->hi, ce->lo); - return -VTD_FR_CONTEXT_ENTRY_INV; + if (!s->root_scalable) { + /* Do translation type check */ + if (!vtd_ce_type_check(x86_iommu, ce)) { + /* Errors dumped in vtd_ce_type_check() */ + return -VTD_FR_CONTEXT_ENTRY_INV; + } + } else { + /* + * Check if the programming of context-entry.rid2pasid + * and corresponding pasid setting is valid, and thus + * avoids to check pasid entry fetching result in future + * helper function calling. + */ + ret_fr = vtd_ce_rid2pasid_check(s, ce); + if (ret_fr) { + return ret_fr; + } } return 0; @@ -1027,7 +1365,19 @@ static int vtd_sync_shadow_page_hook(IOMMUTLBEntry *entry, return 0; } -/* If context entry is NULL, we'll try to fetch it on our own. */ +static uint16_t vtd_get_domain_id(IntelIOMMUState *s, + VTDContextEntry *ce) +{ + VTDPASIDEntry pe; + + if (s->root_scalable) { + vtd_ce_get_rid2pasid_entry(s, ce, &pe); + return VTD_SM_PASID_ENTRY_DID(pe.val[1]); + } + + return VTD_CONTEXT_ENTRY_DID(ce->hi); +} + static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, VTDContextEntry *ce, hwaddr addr, hwaddr size) @@ -1039,69 +1389,62 @@ static int vtd_sync_shadow_page_table_range(VTDAddressSpace *vtd_as, .notify_unmap = true, .aw = s->aw_bits, .as = vtd_as, + .domain_id = vtd_get_domain_id(s, ce), }; - VTDContextEntry ce_cache; - int ret; - if (ce) { - /* If the caller provided context entry, use it */ - ce_cache = *ce; - } else { - /* If the caller didn't provide ce, try to fetch */ - ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), - vtd_as->devfn, &ce_cache); - if (ret) { - /* - * This should not really happen, but in case it happens, - * we just skip the sync for this time. After all we even - * don't have the root table pointer! - */ - trace_vtd_err("Detected invalid context entry when " - "trying to sync shadow page table"); - return 0; - } - } - - info.domain_id = VTD_CONTEXT_ENTRY_DID(ce_cache.hi); - - return vtd_page_walk(&ce_cache, addr, addr + size, &info); + return vtd_page_walk(s, ce, addr, addr + size, &info); } static int vtd_sync_shadow_page_table(VTDAddressSpace *vtd_as) { - return vtd_sync_shadow_page_table_range(vtd_as, NULL, 0, UINT64_MAX); -} - -/* - * Fetch translation type for specific device. Returns <0 if error - * happens, otherwise return the shifted type to check against - * VTD_CONTEXT_TT_*. - */ -static int vtd_dev_get_trans_type(VTDAddressSpace *as) -{ - IntelIOMMUState *s; - VTDContextEntry ce; int ret; + VTDContextEntry ce; + IOMMUNotifier *n; - s = as->iommu_state; - - ret = vtd_dev_to_context_entry(s, pci_bus_num(as->bus), - as->devfn, &ce); + ret = vtd_dev_to_context_entry(vtd_as->iommu_state, + pci_bus_num(vtd_as->bus), + vtd_as->devfn, &ce); if (ret) { + if (ret == -VTD_FR_CONTEXT_ENTRY_P) { + /* + * It's a valid scenario to have a context entry that is + * not present. For example, when a device is removed + * from an existing domain then the context entry will be + * zeroed by the guest before it was put into another + * domain. When this happens, instead of synchronizing + * the shadow pages we should invalidate all existing + * mappings and notify the backends. + */ + IOMMU_NOTIFIER_FOREACH(n, &vtd_as->iommu) { + vtd_address_space_unmap(vtd_as, n); + } + ret = 0; + } return ret; } - return vtd_ce_get_type(&ce); + return vtd_sync_shadow_page_table_range(vtd_as, &ce, 0, UINT64_MAX); } +/* + * Check if specific device is configed to bypass address + * translation for DMA requests. In Scalable Mode, bypass + * 1st-level translation or 2nd-level translation, it depends + * on PGTT setting. + */ static bool vtd_dev_pt_enabled(VTDAddressSpace *as) { + IntelIOMMUState *s; + VTDContextEntry ce; + VTDPASIDEntry pe; int ret; assert(as); - ret = vtd_dev_get_trans_type(as); - if (ret < 0) { + s = as->iommu_state; + ret = vtd_dev_to_context_entry(s, pci_bus_num(as->bus), + as->devfn, &ce); + if (ret) { /* * Possibly failed to parse the context entry for some reason * (e.g., during init, or any guest configuration errors on @@ -1111,7 +1454,17 @@ static bool vtd_dev_pt_enabled(VTDAddressSpace *as) return false; } - return ret == VTD_CONTEXT_TT_PASS_THROUGH; + if (s->root_scalable) { + ret = vtd_ce_get_rid2pasid_entry(s, &ce, &pe); + if (ret) { + error_report_once("%s: vtd_ce_get_rid2pasid_entry error: %"PRId32, + __func__, ret); + return false; + } + return (VTD_PE_GET_TYPE(&pe) == VTD_SM_PASID_ENTRY_PT); + } + + return (vtd_ce_get_type(&ce) == VTD_CONTEXT_TT_PASS_THROUGH); } /* Return whether the device is using IOMMU translation. */ @@ -1123,7 +1476,7 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) assert(as); - use_iommu = as->iommu_state->dmar_enabled & !vtd_dev_pt_enabled(as); + use_iommu = as->iommu_state->dmar_enabled && !vtd_dev_pt_enabled(as); trace_vtd_switch_address_space(pci_bus_num(as->bus), VTD_PCI_SLOT(as->devfn), @@ -1141,11 +1494,11 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) /* Turn off first then on the other */ if (use_iommu) { - memory_region_set_enabled(&as->sys_alias, false); + memory_region_set_enabled(&as->nodmar, false); memory_region_set_enabled(MEMORY_REGION(&as->iommu), true); } else { memory_region_set_enabled(MEMORY_REGION(&as->iommu), false); - memory_region_set_enabled(&as->sys_alias, true); + memory_region_set_enabled(&as->nodmar, true); } if (take_bql) { @@ -1191,6 +1544,7 @@ static const bool vtd_qualified_faults[] = { [VTD_FR_ROOT_ENTRY_RSVD] = false, [VTD_FR_PAGING_ENTRY_RSVD] = true, [VTD_FR_CONTEXT_ENTRY_TT] = true, + [VTD_FR_PASID_TABLE_INV] = false, [VTD_FR_RESERVED_ERR] = false, [VTD_FR_MAX] = false, }; @@ -1292,18 +1646,17 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, cc_entry->context_cache_gen); ce = cc_entry->context_entry; is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; + if (!is_fpd_set && s->root_scalable) { + ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set); + VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write); + } } else { ret_fr = vtd_dev_to_context_entry(s, bus_num, devfn, &ce); is_fpd_set = ce.lo & VTD_CONTEXT_ENTRY_FPD; - if (ret_fr) { - ret_fr = -ret_fr; - if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { - trace_vtd_fault_disabled(); - } else { - vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); - } - goto error; + if (!ret_fr && !is_fpd_set && s->root_scalable) { + ret_fr = vtd_ce_get_pasid_fpd(s, &ce, &is_fpd_set); } + VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write); /* Update context-cache */ trace_vtd_iotlb_cc_update(bus_num, devfn, ce.hi, ce.lo, cc_entry->context_cache_gen, @@ -1337,21 +1690,13 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, return true; } - ret_fr = vtd_iova_to_slpte(&ce, addr, is_write, &slpte, &level, + ret_fr = vtd_iova_to_slpte(s, &ce, addr, is_write, &slpte, &level, &reads, &writes, s->aw_bits); - if (ret_fr) { - ret_fr = -ret_fr; - if (is_fpd_set && vtd_is_qualified_fault(ret_fr)) { - trace_vtd_fault_disabled(); - } else { - vtd_report_dmar_fault(s, source_id, addr, ret_fr, is_write); - } - goto error; - } + VTD_PE_GET_FPD_ERR(ret_fr, is_fpd_set, s, source_id, addr, is_write); page_mask = vtd_slpt_level_page_mask(level); access_flags = IOMMU_ACCESS_FLAG(reads, writes); - vtd_update_iotlb(s, source_id, VTD_CONTEXT_ENTRY_DID(ce.hi), addr, slpte, + vtd_update_iotlb(s, source_id, vtd_get_domain_id(s, &ce), addr, slpte, access_flags, level); out: vtd_iommu_unlock(s); @@ -1373,10 +1718,11 @@ error: static void vtd_root_table_setup(IntelIOMMUState *s) { s->root = vtd_get_quad_raw(s, DMAR_RTADDR_REG); - s->root_extended = s->root & VTD_RTADDR_RTT; s->root &= VTD_RTADDR_ADDR_MASK(s->aw_bits); - trace_vtd_reg_dmar_root(s->root, s->root_extended); + vtd_update_scalable_state(s); + + trace_vtd_reg_dmar_root(s->root, s->root_scalable); } static void vtd_iec_notify_all(IntelIOMMUState *s, bool global, @@ -1418,7 +1764,7 @@ static void vtd_context_global_invalidate(IntelIOMMUState *s) vtd_reset_context_cache_locked(s); } vtd_iommu_unlock(s); - vtd_switch_address_space_all(s); + vtd_address_space_refresh_all(s); /* * From VT-d spec 6.5.2.1, a global context entry invalidation * should be followed by a IOTLB global invalidation, so we should @@ -1514,7 +1860,8 @@ static uint64_t vtd_context_cache_invalidate(IntelIOMMUState *s, uint64_t val) break; default: - trace_vtd_err("Context cache invalidate type error."); + error_report_once("%s: invalid context: 0x%" PRIx64, + __func__, val); caig = 0; } return caig; @@ -1542,7 +1889,7 @@ static void vtd_iotlb_domain_invalidate(IntelIOMMUState *s, uint16_t domain_id) QLIST_FOREACH(vtd_as, &s->vtd_as_with_notifiers, next) { if (!vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce) && - domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + domain_id == vtd_get_domain_id(s, &ce)) { vtd_sync_shadow_page_table(vtd_as); } } @@ -1560,7 +1907,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, QLIST_FOREACH(vtd_as, &(s->vtd_as_with_notifiers), next) { ret = vtd_dev_to_context_entry(s, pci_bus_num(vtd_as->bus), vtd_as->devfn, &ce); - if (!ret && domain_id == VTD_CONTEXT_ENTRY_DID(ce.hi)) { + if (!ret && domain_id == vtd_get_domain_id(s, &ce)) { if (vtd_as_has_map_notifier(vtd_as)) { /* * As long as we have MAP notifications registered in @@ -1634,7 +1981,8 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) am = VTD_IVA_AM(addr); addr = VTD_IVA_ADDR(addr); if (am > VTD_MAMV) { - trace_vtd_err("IOTLB PSI flush: address mask overflow."); + error_report_once("%s: address mask overflow: 0x%" PRIx64, + __func__, vtd_get_quad_raw(s, DMAR_IVA_REG)); iaig = 0; break; } @@ -1643,7 +1991,8 @@ static uint64_t vtd_iotlb_flush(IntelIOMMUState *s, uint64_t val) break; default: - trace_vtd_err("IOTLB flush: invalid granularity."); + error_report_once("%s: invalid granularity: 0x%" PRIx64, + __func__, val); iaig = 0; } return iaig; @@ -1666,7 +2015,7 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) if (en) { s->iq = iqa_val & VTD_IQA_IQA_MASK(s->aw_bits); /* 2^(x+8) entries */ - s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8); + s->iq_size = 1UL << ((iqa_val & VTD_IQA_QS) + 8 - (s->iq_dw ? 1 : 0)); s->qi_enabled = true; trace_vtd_inv_qi_setup(s->iq, s->iq_size); /* Ok - report back to driver */ @@ -1692,7 +2041,10 @@ static void vtd_handle_gcmd_qie(IntelIOMMUState *s, bool en) /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_QIES, 0); } else { - trace_vtd_err_qi_disable(s->iq_head, s->iq_tail, s->iq_last_desc_type); + error_report_once("%s: detected improper state when disable QI " + "(head=0x%x, tail=0x%x, last_type=%d)", + __func__, + s->iq_head, s->iq_tail, s->iq_last_desc_type); } } } @@ -1703,6 +2055,8 @@ static void vtd_handle_gcmd_srtp(IntelIOMMUState *s) vtd_root_table_setup(s); /* Ok - report back to driver */ vtd_set_clear_mask_long(s, DMAR_GSTS_REG, 0, VTD_GSTS_RTPS); + vtd_reset_caches(s); + vtd_address_space_refresh_all(s); } /* Set Interrupt Remap Table Pointer */ @@ -1735,7 +2089,8 @@ static void vtd_handle_gcmd_te(IntelIOMMUState *s, bool en) vtd_set_clear_mask_long(s, DMAR_GSTS_REG, VTD_GSTS_TES, 0); } - vtd_switch_address_space_all(s); + vtd_reset_caches(s); + vtd_address_space_refresh_all(s); } /* Handle Interrupt Remap Enable/Disable */ @@ -1793,8 +2148,8 @@ static void vtd_handle_ccmd_write(IntelIOMMUState *s) /* Context-cache invalidation request */ if (val & VTD_CCMD_ICC) { if (s->qi_enabled) { - trace_vtd_err("Queued Invalidation enabled, " - "should not use register-based invalidation"); + error_report_once("Queued Invalidation enabled, " + "should not use register-based invalidation"); return; } ret = vtd_context_cache_invalidate(s, val); @@ -1814,8 +2169,8 @@ static void vtd_handle_iotlb_write(IntelIOMMUState *s) /* IOTLB invalidation request */ if (val & VTD_TLB_IVT) { if (s->qi_enabled) { - trace_vtd_err("Queued Invalidation enabled, " - "should not use register-based invalidation."); + error_report_once("Queued Invalidation enabled, " + "should not use register-based invalidation"); return; } ret = vtd_iotlb_flush(s, val); @@ -1827,19 +2182,24 @@ static void vtd_handle_iotlb_write(IntelIOMMUState *s) } /* Fetch an Invalidation Descriptor from the Invalidation Queue */ -static bool vtd_get_inv_desc(dma_addr_t base_addr, uint32_t offset, +static bool vtd_get_inv_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { - dma_addr_t addr = base_addr + offset * sizeof(*inv_desc); - if (dma_memory_read(&address_space_memory, addr, inv_desc, - sizeof(*inv_desc))) { - trace_vtd_err("Read INV DESC failed."); - inv_desc->lo = 0; - inv_desc->hi = 0; + dma_addr_t base_addr = s->iq; + uint32_t offset = s->iq_head; + uint32_t dw = s->iq_dw ? 32 : 16; + dma_addr_t addr = base_addr + offset * dw; + + if (dma_memory_read(&address_space_memory, addr, inv_desc, dw)) { + error_report_once("Read INV DESC failed."); return false; } inv_desc->lo = le64_to_cpu(inv_desc->lo); inv_desc->hi = le64_to_cpu(inv_desc->hi); + if (dw == 32) { + inv_desc->val[2] = le64_to_cpu(inv_desc->val[2]); + inv_desc->val[3] = le64_to_cpu(inv_desc->val[3]); + } return true; } @@ -1847,7 +2207,9 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { if ((inv_desc->hi & VTD_INV_DESC_WAIT_RSVD_HI) || (inv_desc->lo & VTD_INV_DESC_WAIT_RSVD_LO)) { - trace_vtd_inv_desc_wait_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64 + " (reserved nonzero)", __func__, inv_desc->hi, + inv_desc->lo); return false; } if (inv_desc->lo & VTD_INV_DESC_WAIT_SW) { @@ -1870,7 +2232,9 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) /* Interrupt flag */ vtd_generate_completion_event(s); } else { - trace_vtd_inv_desc_wait_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64 + " (unknown type)", __func__, inv_desc->hi, + inv_desc->lo); return false; } return true; @@ -1882,7 +2246,9 @@ static bool vtd_process_context_cache_desc(IntelIOMMUState *s, uint16_t sid, fmask; if ((inv_desc->lo & VTD_INV_DESC_CC_RSVD) || inv_desc->hi) { - trace_vtd_inv_desc_cc_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid cc inv desc: hi=%"PRIx64", lo=%"PRIx64 + " (reserved nonzero)", __func__, inv_desc->hi, + inv_desc->lo); return false; } switch (inv_desc->lo & VTD_INV_DESC_CC_G) { @@ -1901,7 +2267,9 @@ static bool vtd_process_context_cache_desc(IntelIOMMUState *s, break; default: - trace_vtd_inv_desc_cc_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid cc inv desc: hi=%"PRIx64", lo=%"PRIx64 + " (invalid type)", __func__, inv_desc->hi, + inv_desc->lo); return false; } return true; @@ -1915,7 +2283,9 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) if ((inv_desc->lo & VTD_INV_DESC_IOTLB_RSVD_LO) || (inv_desc->hi & VTD_INV_DESC_IOTLB_RSVD_HI)) { - trace_vtd_inv_desc_iotlb_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (reserved bits unzero)\n", + __func__, inv_desc->hi, inv_desc->lo); return false; } @@ -1934,14 +2304,20 @@ static bool vtd_process_iotlb_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) addr = VTD_INV_DESC_IOTLB_ADDR(inv_desc->hi); am = VTD_INV_DESC_IOTLB_AM(inv_desc->hi); if (am > VTD_MAMV) { - trace_vtd_inv_desc_iotlb_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (am=%u > VTD_MAMV=%u)\n", + __func__, inv_desc->hi, inv_desc->lo, + am, (unsigned)VTD_MAMV); return false; } vtd_iotlb_page_invalidate(s, domain_id, addr, am); break; default: - trace_vtd_inv_desc_iotlb_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid iotlb inv desc: hi=0x%"PRIx64 + ", lo=0x%"PRIx64" (type mismatch: 0x%llx)\n", + __func__, inv_desc->hi, inv_desc->lo, + inv_desc->lo & VTD_INV_DESC_IOTLB_G); return false; } return true; @@ -1981,7 +2357,9 @@ static bool vtd_process_device_iotlb_desc(IntelIOMMUState *s, if ((inv_desc->lo & VTD_INV_DESC_DEVICE_IOTLB_RSVD_LO) || (inv_desc->hi & VTD_INV_DESC_DEVICE_IOTLB_RSVD_HI)) { - trace_vtd_inv_desc_iotlb_invalid(inv_desc->hi, inv_desc->lo); + error_report_once("%s: invalid dev-iotlb inv desc: hi=%"PRIx64 + ", lo=%"PRIx64" (reserved nonzero)", __func__, + inv_desc->hi, inv_desc->lo); return false; } @@ -2027,10 +2405,11 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) uint8_t desc_type; trace_vtd_inv_qi_head(s->iq_head); - if (!vtd_get_inv_desc(s->iq, s->iq_head, &inv_desc)) { + if (!vtd_get_inv_desc(s, &inv_desc)) { s->iq_last_desc_type = VTD_INV_DESC_NONE; return false; } + desc_type = inv_desc.lo & VTD_INV_DESC_TYPE; /* FIXME: should update at first or at last? */ s->iq_last_desc_type = desc_type; @@ -2050,6 +2429,17 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) } break; + /* + * TODO: the entity of below two cases will be implemented in future series. + * To make guest (which integrates scalable mode support patch set in + * iommu driver) work, just return true is enough so far. + */ + case VTD_INV_DESC_PC: + break; + + case VTD_INV_DESC_PIOTLB: + break; + case VTD_INV_DESC_WAIT: trace_vtd_inv_desc("wait", inv_desc.hi, inv_desc.lo); if (!vtd_process_wait_desc(s, &inv_desc)) { @@ -2072,7 +2462,9 @@ static bool vtd_process_inv_desc(IntelIOMMUState *s) break; default: - trace_vtd_inv_desc_invalid(inv_desc.hi, inv_desc.lo); + error_report_once("%s: invalid inv desc: hi=%"PRIx64", lo=%"PRIx64 + " (unknown type)", __func__, inv_desc.hi, + inv_desc.lo); return false; } s->iq_head++; @@ -2089,7 +2481,9 @@ static void vtd_fetch_inv_desc(IntelIOMMUState *s) if (s->iq_tail >= s->iq_size) { /* Detects an invalid Tail pointer */ - trace_vtd_err_qi_tail(s->iq_tail, s->iq_size); + error_report_once("%s: detected invalid QI tail " + "(tail=0x%x, size=0x%x)", + __func__, s->iq_tail, s->iq_size); vtd_handle_inv_queue_error(s); return; } @@ -2111,7 +2505,12 @@ static void vtd_handle_iqt_write(IntelIOMMUState *s) { uint64_t val = vtd_get_quad_raw(s, DMAR_IQT_REG); - s->iq_tail = VTD_IQT_QT(val); + if (s->iq_dw && (val & VTD_IQT_QT_256_RSV_BIT)) { + error_report_once("%s: RSV bit is set: val=0x%"PRIx64, + __func__, val); + return; + } + s->iq_tail = VTD_IQT_QT(s->iq_dw, val); trace_vtd_inv_qi_tail(s->iq_tail); if (s->qi_enabled && !(vtd_get_long_raw(s, DMAR_FSTS_REG) & VTD_FSTS_IQE)) { @@ -2188,7 +2587,8 @@ static uint64_t vtd_mem_read(void *opaque, hwaddr addr, unsigned size) trace_vtd_reg_read(addr, size); if (addr + size > DMAR_REG_SIZE) { - trace_vtd_err("Read MMIO over range."); + error_report_once("%s: MMIO over range: addr=0x%" PRIx64 + " size=0x%u", __func__, addr, size); return (uint64_t)-1; } @@ -2239,7 +2639,8 @@ static void vtd_mem_write(void *opaque, hwaddr addr, trace_vtd_reg_write(addr, size, val); if (addr + size > DMAR_REG_SIZE) { - trace_vtd_err("Write MMIO over range."); + error_report_once("%s: MMIO over range: addr=0x%" PRIx64 + " size=0x%u", __func__, addr, size); return; } @@ -2378,6 +2779,12 @@ static void vtd_mem_write(void *opaque, hwaddr addr, } else { vtd_set_quad(s, addr, val); } + if (s->ecap & VTD_ECAP_SMTS && + val & VTD_IQA_DW_MASK) { + s->iq_dw = true; + } else { + s->iq_dw = false; + } break; case DMAR_IQA_REG_HI: @@ -2500,10 +2907,12 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, iotlb.iova, iotlb.translated_addr, iotlb.addr_mask); } else { - trace_vtd_err_dmar_translate(pci_bus_num(vtd_as->bus), - VTD_PCI_SLOT(vtd_as->devfn), - VTD_PCI_FUNC(vtd_as->devfn), - iotlb.iova); + error_report_once("%s: detected translation failure " + "(dev=%02x:%02x:%02x, iova=0x%" PRIx64 ")", + __func__, pci_bus_num(vtd_as->bus), + VTD_PCI_SLOT(vtd_as->devfn), + VTD_PCI_FUNC(vtd_as->devfn), + addr); } return iotlb; @@ -2517,7 +2926,7 @@ static void vtd_iommu_notify_flag_changed(IOMMUMemoryRegion *iommu, IntelIOMMUState *s = vtd_as->iommu_state; if (!s->caching_mode && new & IOMMU_NOTIFIER_MAP) { - error_report("We need to set caching-mode=1 for intel-iommu to enable " + error_report("We need to set caching-mode=on for intel-iommu to enable " "device assignment with IOMMU protection."); exit(1); } @@ -2543,6 +2952,15 @@ static int vtd_post_load(void *opaque, int version_id) */ vtd_switch_address_space_all(iommu); + /* + * We don't need to migrate the root_scalable because we can + * simply do the calculation after the loading is complete. We + * can actually do similar things with root, dmar_enabled, etc. + * however since we've had them already so we'd better keep them + * for compatibility of migration. + */ + vtd_update_scalable_state(iommu); + return 0; } @@ -2563,7 +2981,7 @@ static const VMStateDescription vtd_vmstate = { VMSTATE_UINT16(next_frcd_reg, IntelIOMMUState), VMSTATE_UINT8_ARRAY(csr, IntelIOMMUState, DMAR_REG_SIZE), VMSTATE_UINT8(iq_last_desc_type, IntelIOMMUState), - VMSTATE_BOOL(root_extended, IntelIOMMUState), + VMSTATE_UNUSED(1), /* bool root_extended is obsolete by VT-d */ VMSTATE_BOOL(dmar_enabled, IntelIOMMUState), VMSTATE_BOOL(qi_enabled, IntelIOMMUState), VMSTATE_BOOL(intr_enabled, IntelIOMMUState), @@ -2591,9 +3009,11 @@ static Property vtd_properties[] = { DEFINE_PROP_ON_OFF_AUTO("eim", IntelIOMMUState, intr_eim, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("x-buggy-eim", IntelIOMMUState, buggy_eim, false), - DEFINE_PROP_UINT8("x-aw-bits", IntelIOMMUState, aw_bits, + DEFINE_PROP_UINT8("aw-bits", IntelIOMMUState, aw_bits, VTD_HOST_ADDRESS_WIDTH), DEFINE_PROP_BOOL("caching-mode", IntelIOMMUState, caching_mode, FALSE), + DEFINE_PROP_BOOL("x-scalable-mode", IntelIOMMUState, scalable_mode, FALSE), + DEFINE_PROP_BOOL("dma-drain", IntelIOMMUState, dma_drain, true), DEFINE_PROP_END_OF_LIST(), }; @@ -2610,7 +3030,8 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, addr = iommu->intr_root + index * sizeof(*entry); if (dma_memory_read(&address_space_memory, addr, entry, sizeof(*entry))) { - trace_vtd_err("Memory read failed for IRTE."); + error_report_once("%s: read failed: ind=0x%x addr=0x%" PRIx64, + __func__, index, addr); return -VTD_FR_IR_ROOT_INVAL; } @@ -2618,15 +3039,19 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, le64_to_cpu(entry->data[0])); if (!entry->irte.present) { - trace_vtd_err_irte(index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); + error_report_once("%s: detected non-present IRTE " + "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", + __func__, index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); return -VTD_FR_IR_ENTRY_P; } if (entry->irte.__reserved_0 || entry->irte.__reserved_1 || entry->irte.__reserved_2) { - trace_vtd_err_irte(index, le64_to_cpu(entry->data[1]), - le64_to_cpu(entry->data[0])); + error_report_once("%s: detected non-zero reserved IRTE " + "(index=%u, high=0x%" PRIx64 ", low=0x%" PRIx64 ")", + __func__, index, le64_to_cpu(entry->data[1]), + le64_to_cpu(entry->data[0])); return -VTD_FR_IR_IRTE_RSVD; } @@ -2640,7 +3065,9 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, case VTD_SVT_ALL: mask = vtd_svt_mask[entry->irte.sid_q]; if ((source_id & mask) != (sid & mask)) { - trace_vtd_err_irte_sid(index, sid, source_id); + error_report_once("%s: invalid IRTE SID " + "(index=%u, sid=%u, source_id=%u)", + __func__, index, sid, source_id); return -VTD_FR_IR_SID_ERR; } break; @@ -2650,13 +3077,17 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, bus_min = source_id & 0xff; bus = sid >> 8; if (bus > bus_max || bus < bus_min) { - trace_vtd_err_irte_sid_bus(index, bus, bus_min, bus_max); + error_report_once("%s: invalid SVT_BUS " + "(index=%u, bus=%u, min=%u, max=%u)", + __func__, index, bus, bus_min, bus_max); return -VTD_FR_IR_SID_ERR; } break; default: - trace_vtd_err_irte_svt(index, entry->irte.sid_vtype); + error_report_once("%s: detected invalid IRTE SVT " + "(index=%u, type=%d)", __func__, + index, entry->irte.sid_vtype); /* Take this as verification failure. */ return -VTD_FR_IR_SID_ERR; break; @@ -2668,7 +3099,7 @@ static int vtd_irte_get(IntelIOMMUState *iommu, uint16_t index, /* Fetch IRQ information of specific IR index */ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, - VTDIrq *irq, uint16_t sid) + X86IOMMUIrq *irq, uint16_t sid) { VTD_IR_TableEntry irte = {}; int ret = 0; @@ -2697,30 +3128,6 @@ static int vtd_remap_irq_get(IntelIOMMUState *iommu, uint16_t index, return 0; } -/* Generate one MSI message from VTDIrq info */ -static void vtd_generate_msi_message(VTDIrq *irq, MSIMessage *msg_out) -{ - VTD_MSIMessage msg = {}; - - /* Generate address bits */ - msg.dest_mode = irq->dest_mode; - msg.redir_hint = irq->redir_hint; - msg.dest = irq->dest; - msg.__addr_hi = irq->dest & 0xffffff00; - msg.__addr_head = cpu_to_le32(0xfee); - /* Keep this from original MSI address bits */ - msg.__not_used = irq->msi_addr_last_bits; - - /* Generate data bits */ - msg.vector = irq->vector; - msg.delivery_mode = irq->delivery_mode; - msg.level = 1; - msg.trigger_mode = irq->trigger_mode; - - msg_out->address = msg.msi_addr; - msg_out->data = msg.msi_data; -} - /* Interrupt remapping for MSI/MSI-X entry */ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, MSIMessage *origin, @@ -2730,7 +3137,7 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, int ret = 0; VTD_IR_MSIAddress addr; uint16_t index; - VTDIrq irq = {}; + X86IOMMUIrq irq = {}; assert(origin && translated); @@ -2742,14 +3149,15 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, } if (origin->address & VTD_MSI_ADDR_HI_MASK) { - trace_vtd_err("MSI address high 32 bits non-zero when " - "Interrupt Remapping enabled."); + error_report_once("%s: MSI address high 32 bits non-zero detected: " + "address=0x%" PRIx64, __func__, origin->address); return -VTD_FR_IR_REQ_RSVD; } addr.data = origin->address & VTD_MSI_ADDR_LO_MASK; if (addr.addr.__head != 0xfee) { - trace_vtd_err("MSI addr low 32 bit invalid."); + error_report_once("%s: MSI address low 32 bit invalid: 0x%" PRIx32, + __func__, addr.data); return -VTD_FR_IR_REQ_RSVD; } @@ -2777,7 +3185,10 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, if (addr.addr.sub_valid) { trace_vtd_ir_remap_type("MSI"); if (origin->data & VTD_IR_MSI_DATA_RESERVED) { - trace_vtd_err_ir_msi_invalid(sid, origin->address, origin->data); + error_report_once("%s: invalid IR MSI " + "(sid=%u, address=0x%" PRIx64 + ", data=0x%" PRIx32 ")", + __func__, sid, origin->address, origin->data); return -VTD_FR_IR_REQ_RSVD; } } else { @@ -2805,8 +3216,8 @@ static int vtd_interrupt_remap_msi(IntelIOMMUState *iommu, */ irq.msi_addr_last_bits = addr.addr.__not_care; - /* Translate VTDIrq to MSI message */ - vtd_generate_msi_message(&irq, translated); + /* Translate X86IOMMUIrq to MSI message */ + x86_iommu_irq_to_msi_message(&irq, translated); out: trace_vtd_ir_remap_msi(origin->address, origin->data, @@ -2890,7 +3301,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as = vtd_bus->dev_as[devfn]; if (!vtd_dev_as) { - snprintf(name, sizeof(name), "intel_iommu_devfn_%d", devfn); + snprintf(name, sizeof(name), "vtd-%02x.%x", PCI_SLOT(devfn), + PCI_FUNC(devfn)); vtd_bus->dev_as[devfn] = vtd_dev_as = g_malloc0(sizeof(VTDAddressSpace)); vtd_dev_as->bus = bus; @@ -2899,44 +3311,53 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, int devfn) vtd_dev_as->context_cache_entry.context_cache_gen = 0; vtd_dev_as->iova_tree = iova_tree_new(); + memory_region_init(&vtd_dev_as->root, OBJECT(s), name, UINT64_MAX); + address_space_init(&vtd_dev_as->as, &vtd_dev_as->root, "vtd-root"); + /* - * Memory region relationships looks like (Address range shows - * only lower 32 bits to make it short in length...): - * - * |-----------------+-------------------+----------| - * | Name | Address range | Priority | - * |-----------------+-------------------+----------+ - * | vtd_root | 00000000-ffffffff | 0 | - * | intel_iommu | 00000000-ffffffff | 1 | - * | vtd_sys_alias | 00000000-ffffffff | 1 | - * | intel_iommu_ir | fee00000-feefffff | 64 | - * |-----------------+-------------------+----------| - * - * We enable/disable DMAR by switching enablement for - * vtd_sys_alias and intel_iommu regions. IR region is always - * enabled. + * Build the DMAR-disabled container with aliases to the + * shared MRs. Note that aliasing to a shared memory region + * could help the memory API to detect same FlatViews so we + * can have devices to share the same FlatView when DMAR is + * disabled (either by not providing "intel_iommu=on" or with + * "iommu=pt"). It will greatly reduce the total number of + * FlatViews of the system hence VM runs faster. */ + memory_region_init_alias(&vtd_dev_as->nodmar, OBJECT(s), + "vtd-nodmar", &s->mr_nodmar, 0, + memory_region_size(&s->mr_nodmar)); + + /* + * Build the per-device DMAR-enabled container. + * + * TODO: currently we have per-device IOMMU memory region only + * because we have per-device IOMMU notifiers for devices. If + * one day we can abstract the IOMMU notifiers out of the + * memory regions then we can also share the same memory + * region here just like what we've done above with the nodmar + * region. + */ + strcat(name, "-dmar"); memory_region_init_iommu(&vtd_dev_as->iommu, sizeof(vtd_dev_as->iommu), TYPE_INTEL_IOMMU_MEMORY_REGION, OBJECT(s), - "intel_iommu_dmar", - UINT64_MAX); - memory_region_init_alias(&vtd_dev_as->sys_alias, OBJECT(s), - "vtd_sys_alias", get_system_memory(), - 0, memory_region_size(get_system_memory())); - memory_region_init_io(&vtd_dev_as->iommu_ir, OBJECT(s), - &vtd_mem_ir_ops, s, "intel_iommu_ir", - VTD_INTERRUPT_ADDR_SIZE); - memory_region_init(&vtd_dev_as->root, OBJECT(s), - "vtd_root", UINT64_MAX); - memory_region_add_subregion_overlap(&vtd_dev_as->root, + name, UINT64_MAX); + memory_region_init_alias(&vtd_dev_as->iommu_ir, OBJECT(s), "vtd-ir", + &s->mr_ir, 0, memory_region_size(&s->mr_ir)); + memory_region_add_subregion_overlap(MEMORY_REGION(&vtd_dev_as->iommu), VTD_INTERRUPT_ADDR_FIRST, - &vtd_dev_as->iommu_ir, 64); - address_space_init(&vtd_dev_as->as, &vtd_dev_as->root, name); - memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, - &vtd_dev_as->sys_alias, 1); + &vtd_dev_as->iommu_ir, 1); + + /* + * Hook both the containers under the root container, we + * switch between DMAR & noDMAR by enable/disable + * corresponding sub-containers + */ memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, MEMORY_REGION(&vtd_dev_as->iommu), - 1); + 0); + memory_region_add_subregion_overlap(&vtd_dev_as->root, 0, + &vtd_dev_as->nodmar, 0); + vtd_switch_address_space(vtd_dev_as); } return vtd_dev_as; @@ -3014,6 +3435,12 @@ static void vtd_address_space_unmap_all(IntelIOMMUState *s) } } +static void vtd_address_space_refresh_all(IntelIOMMUState *s) +{ + vtd_address_space_unmap_all(s); + vtd_switch_address_space_all(s); +} + static int vtd_replay_hook(IOMMUTLBEntry *entry, void *private) { memory_region_notify_one((IOMMUNotifier *)private, entry); @@ -3035,9 +3462,11 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) vtd_address_space_unmap(vtd_as, n); if (vtd_dev_to_context_entry(s, bus_n, vtd_as->devfn, &ce) == 0) { - trace_vtd_replay_ce_valid(bus_n, PCI_SLOT(vtd_as->devfn), + trace_vtd_replay_ce_valid(s->root_scalable ? "scalable mode" : + "legacy mode", + bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn), - VTD_CONTEXT_ENTRY_DID(ce.hi), + vtd_get_domain_id(s, &ce), ce.hi, ce.lo); if (vtd_as_has_map_notifier(vtd_as)) { /* This is required only for MAP typed notifiers */ @@ -3047,10 +3476,10 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) .notify_unmap = false, .aw = s->aw_bits, .as = vtd_as, - .domain_id = VTD_CONTEXT_ENTRY_DID(ce.hi), + .domain_id = vtd_get_domain_id(s, &ce), }; - vtd_page_walk(&ce, 0, ~0ULL, &info); + vtd_page_walk(s, &ce, 0, ~0ULL, &info); } } else { trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), @@ -3073,18 +3502,23 @@ static void vtd_init(IntelIOMMUState *s) memset(s->womask, 0, DMAR_REG_SIZE); s->root = 0; - s->root_extended = false; + s->root_scalable = false; s->dmar_enabled = false; + s->intr_enabled = false; s->iq_head = 0; s->iq_tail = 0; s->iq = 0; s->iq_size = 0; s->qi_enabled = false; s->iq_last_desc_type = VTD_INV_DESC_NONE; + s->iq_dw = false; s->next_frcd_reg = 0; s->cap = VTD_CAP_FRO | VTD_CAP_NFR | VTD_CAP_ND | VTD_CAP_MAMV | VTD_CAP_PSI | VTD_CAP_SLLPS | VTD_CAP_SAGAW_39bit | VTD_CAP_MGAW(s->aw_bits); + if (s->dma_drain) { + s->cap |= VTD_CAP_DRAIN; + } if (s->aw_bits == VTD_HOST_AW_48BIT) { s->cap |= VTD_CAP_SAGAW_48bit; } @@ -3103,7 +3537,7 @@ static void vtd_init(IntelIOMMUState *s) vtd_paging_entry_rsvd_field[7] = VTD_SPTE_LPAGE_L3_RSVD_MASK(s->aw_bits); vtd_paging_entry_rsvd_field[8] = VTD_SPTE_LPAGE_L4_RSVD_MASK(s->aw_bits); - if (x86_iommu->intr_supported) { + if (x86_iommu_ir_supported(x86_iommu)) { s->ecap |= VTD_ECAP_IR | VTD_ECAP_MHMV; if (s->intr_eim == ON_OFF_AUTO_ON) { s->ecap |= VTD_ECAP_EIM; @@ -3123,10 +3557,12 @@ static void vtd_init(IntelIOMMUState *s) s->cap |= VTD_CAP_CM; } - vtd_iommu_lock(s); - vtd_reset_context_cache_locked(s); - vtd_reset_iotlb_locked(s); - vtd_iommu_unlock(s); + /* TODO: read cap/ecap from host to decide which cap to be exposed. */ + if (s->scalable_mode) { + s->ecap |= VTD_ECAP_SMTS | VTD_ECAP_SRS | VTD_ECAP_SLTS; + } + + vtd_reset_caches(s); /* Define registers with default values and bit semantics */ vtd_define_long(s, DMAR_VER_REG, 0x10UL, 0, 0); @@ -3135,7 +3571,7 @@ static void vtd_init(IntelIOMMUState *s) vtd_define_long(s, DMAR_GCMD_REG, 0, 0xff800000UL, 0); vtd_define_long_wo(s, DMAR_GCMD_REG, 0xff800000UL); vtd_define_long(s, DMAR_GSTS_REG, 0, 0, 0); - vtd_define_quad(s, DMAR_RTADDR_REG, 0, 0xfffffffffffff000ULL, 0); + vtd_define_quad(s, DMAR_RTADDR_REG, 0, 0xfffffffffffffc00ULL, 0); vtd_define_quad(s, DMAR_CCMD_REG, 0, 0xe0000003ffffffffULL, 0); vtd_define_quad_wo(s, DMAR_CCMD_REG, 0x3ffff0000ULL); @@ -3158,7 +3594,7 @@ static void vtd_init(IntelIOMMUState *s) vtd_define_quad(s, DMAR_IQH_REG, 0, 0, 0); vtd_define_quad(s, DMAR_IQT_REG, 0, 0x7fff0ULL, 0); - vtd_define_quad(s, DMAR_IQA_REG, 0, 0xfffffffffffff007ULL, 0); + vtd_define_quad(s, DMAR_IQA_REG, 0, 0xfffffffffffff807ULL, 0); vtd_define_long(s, DMAR_ICS_REG, 0, 0, 0x1UL); vtd_define_long(s, DMAR_IECTL_REG, 0x80000000UL, 0x80000000UL, 0); vtd_define_long(s, DMAR_IEDATA_REG, 0, 0xffffffffUL, 0); @@ -3189,11 +3625,7 @@ static void vtd_reset(DeviceState *dev) IntelIOMMUState *s = INTEL_IOMMU_DEVICE(dev); vtd_init(s); - - /* - * When device reset, throw away all mappings and external caches - */ - vtd_address_space_unmap_all(s); + vtd_address_space_refresh_all(s); } static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) @@ -3211,21 +3643,14 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) { X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); - /* Currently Intel IOMMU IR only support "kernel-irqchip={off|split}" */ - if (x86_iommu->intr_supported && kvm_irqchip_in_kernel() && - !kvm_irqchip_is_split()) { - error_setg(errp, "Intel Interrupt Remapping cannot work with " - "kernel-irqchip=on, please use 'split|off'."); - return false; - } - if (s->intr_eim == ON_OFF_AUTO_ON && !x86_iommu->intr_supported) { + if (s->intr_eim == ON_OFF_AUTO_ON && !x86_iommu_ir_supported(x86_iommu)) { error_setg(errp, "eim=on cannot be selected without intremap=on"); return false; } if (s->intr_eim == ON_OFF_AUTO_AUTO) { s->intr_eim = (kvm_irqchip_in_kernel() || s->buggy_eim) - && x86_iommu->intr_supported ? + && x86_iommu_ir_supported(x86_iommu) ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; } if (s->intr_eim == ON_OFF_AUTO_ON && !s->buggy_eim) { @@ -3248,6 +3673,11 @@ static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) return false; } + if (s->scalable_mode && !s->dma_drain) { + error_setg(errp, "Need to set dma_drain for scalable mode"); + return false; + } + return true; } @@ -3270,6 +3700,21 @@ static void vtd_realize(DeviceState *dev, Error **errp) 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); + + /* Create the shared memory regions by all devices */ + memory_region_init(&s->mr_nodmar, OBJECT(s), "vtd-nodmar", + UINT64_MAX); + memory_region_init_io(&s->mr_ir, OBJECT(s), &vtd_mem_ir_ops, + s, "vtd-ir", VTD_INTERRUPT_ADDR_SIZE); + memory_region_init_alias(&s->mr_sys_alias, OBJECT(s), + "vtd-sys-alias", get_system_memory(), 0, + memory_region_size(get_system_memory())); + memory_region_add_subregion_overlap(&s->mr_nodmar, 0, + &s->mr_sys_alias, 0); + memory_region_add_subregion_overlap(&s->mr_nodmar, + VTD_INTERRUPT_ADDR_FIRST, + &s->mr_ir, 1); + 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, diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index d084099ed9..c1235a7063 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -171,7 +171,7 @@ #define VTD_CCMD_FM(val) (((val) >> 32) & 3ULL) /* RTADDR_REG */ -#define VTD_RTADDR_RTT (1ULL << 11) +#define VTD_RTADDR_SMT (1ULL << 10) #define VTD_RTADDR_ADDR_MASK(aw) (VTD_HAW_MASK(aw) ^ 0xfffULL) /* IRTA_REG */ @@ -189,6 +189,9 @@ #define VTD_ECAP_EIM (1ULL << 4) #define VTD_ECAP_PT (1ULL << 6) #define VTD_ECAP_MHMV (15ULL << 20) +#define VTD_ECAP_SRS (1ULL << 31) +#define VTD_ECAP_SMTS (1ULL << 43) +#define VTD_ECAP_SLTS (1ULL << 46) /* CAP_REG */ /* (offset >> 4) << 24 */ @@ -203,6 +206,9 @@ #define VTD_CAP_MAMV (VTD_MAMV << 48) #define VTD_CAP_PSI (1ULL << 39) #define VTD_CAP_SLLPS ((1ULL << 34) | (1ULL << 35)) +#define VTD_CAP_DRAIN_WRITE (1ULL << 54) +#define VTD_CAP_DRAIN_READ (1ULL << 55) +#define VTD_CAP_DRAIN (VTD_CAP_DRAIN_READ | VTD_CAP_DRAIN_WRITE) #define VTD_CAP_CM (1ULL << 7) /* Supported Adjusted Guest Address Widths */ @@ -214,11 +220,14 @@ #define VTD_CAP_SAGAW_48bit (0x4ULL << VTD_CAP_SAGAW_SHIFT) /* IQT_REG */ -#define VTD_IQT_QT(val) (((val) >> 4) & 0x7fffULL) +#define VTD_IQT_QT(dw_bit, val) (dw_bit ? (((val) >> 5) & 0x3fffULL) : \ + (((val) >> 4) & 0x7fffULL)) +#define VTD_IQT_QT_256_RSV_BIT 0x10 /* IQA_REG */ #define VTD_IQA_IQA_MASK(aw) (VTD_HAW_MASK(aw) ^ 0xfffULL) #define VTD_IQA_QS 0x7ULL +#define VTD_IQA_DW_MASK 0x800 /* IQH_REG */ #define VTD_IQH_QH_SHIFT 4 @@ -291,6 +300,8 @@ typedef enum VTDFaultReason { * request while disabled */ VTD_FR_IR_SID_ERR = 0x26, /* Invalid Source-ID */ + VTD_FR_PASID_TABLE_INV = 0x58, /*Invalid PASID table entry */ + /* This is not a normal fault reason. We use this to indicate some faults * that are not referenced by the VT-d specification. * Fault event with such reason should not be recorded. @@ -318,6 +329,9 @@ union VTDInvDesc { uint64_t lo; uint64_t hi; }; + struct { + uint64_t val[4]; + }; union { VTDInvDescIEC iec; }; @@ -332,6 +346,8 @@ typedef union VTDInvDesc VTDInvDesc; #define VTD_INV_DESC_IEC 0x4 /* Interrupt Entry Cache Invalidate Descriptor */ #define VTD_INV_DESC_WAIT 0x5 /* Invalidation Wait Descriptor */ +#define VTD_INV_DESC_PIOTLB 0x6 /* PASID-IOTLB Invalidate Desc */ +#define VTD_INV_DESC_PC 0x7 /* PASID-cache Invalidate Desc */ #define VTD_INV_DESC_NONE 0 /* Not an Invalidate Descriptor */ /* Masks for Invalidation Wait Descriptor*/ @@ -408,8 +424,8 @@ typedef struct VTDIOTLBPageInvInfo VTDIOTLBPageInvInfo; #define VTD_PAGE_MASK_1G (~((1ULL << VTD_PAGE_SHIFT_1G) - 1)) struct VTDRootEntry { - uint64_t val; - uint64_t rsvd; + uint64_t lo; + uint64_t hi; }; typedef struct VTDRootEntry VTDRootEntry; @@ -420,6 +436,8 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_ROOT_ENTRY_NR (VTD_PAGE_SIZE / sizeof(VTDRootEntry)) #define VTD_ROOT_ENTRY_RSVD(aw) (0xffeULL | ~VTD_HAW_MASK(aw)) +#define VTD_DEVFN_CHECK_MASK 0x80 + /* Masks for struct VTDContextEntry */ /* lo */ #define VTD_CONTEXT_ENTRY_P (1ULL << 0) @@ -438,6 +456,38 @@ typedef struct VTDRootEntry VTDRootEntry; #define VTD_CONTEXT_ENTRY_NR (VTD_PAGE_SIZE / sizeof(VTDContextEntry)) +#define VTD_CTX_ENTRY_LEGACY_SIZE 16 +#define VTD_CTX_ENTRY_SCALABLE_SIZE 32 + +#define VTD_SM_CONTEXT_ENTRY_RID2PASID_MASK 0xfffff +#define VTD_SM_CONTEXT_ENTRY_RSVD_VAL0(aw) (0x1e0ULL | ~VTD_HAW_MASK(aw)) +#define VTD_SM_CONTEXT_ENTRY_RSVD_VAL1 0xffffffffffe00000ULL + +/* PASID Table Related Definitions */ +#define VTD_PASID_DIR_BASE_ADDR_MASK (~0xfffULL) +#define VTD_PASID_TABLE_BASE_ADDR_MASK (~0xfffULL) +#define VTD_PASID_DIR_ENTRY_SIZE 8 +#define VTD_PASID_ENTRY_SIZE 64 +#define VTD_PASID_DIR_BITS_MASK (0x3fffULL) +#define VTD_PASID_DIR_INDEX(pasid) (((pasid) >> 6) & VTD_PASID_DIR_BITS_MASK) +#define VTD_PASID_DIR_FPD (1ULL << 1) /* Fault Processing Disable */ +#define VTD_PASID_TABLE_BITS_MASK (0x3fULL) +#define VTD_PASID_TABLE_INDEX(pasid) ((pasid) & VTD_PASID_TABLE_BITS_MASK) +#define VTD_PASID_ENTRY_FPD (1ULL << 1) /* Fault Processing Disable */ + +/* PASID Granular Translation Type Mask */ +#define VTD_SM_PASID_ENTRY_PGTT (7ULL << 6) +#define VTD_SM_PASID_ENTRY_FLT (1ULL << 6) +#define VTD_SM_PASID_ENTRY_SLT (2ULL << 6) +#define VTD_SM_PASID_ENTRY_NESTED (3ULL << 6) +#define VTD_SM_PASID_ENTRY_PT (4ULL << 6) + +#define VTD_SM_PASID_ENTRY_AW 7ULL /* Adjusted guest-address-width */ +#define VTD_SM_PASID_ENTRY_DID(val) ((val) & VTD_DOMAIN_ID_MASK) + +/* Second Level Page Translation Pointer*/ +#define VTD_SM_PASID_ENTRY_SLPTPTR (~0xfffULL) + /* Paging Structure common */ #define VTD_SL_PT_PAGE_SIZE_MASK (1ULL << 7) /* Bits to decide the offset for each level */ diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 0bf1c60a06..25ea783bec 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -147,6 +147,15 @@ static void kvm_update_clock(KVMClockState *s) s->clock_is_reliable = kvm_has_adjust_clock_stable(); } +static void do_kvmclock_ctrl(CPUState *cpu, run_on_cpu_data data) +{ + int ret = kvm_vcpu_ioctl(cpu, KVM_KVMCLOCK_CTRL, 0); + + if (ret && ret != -EINVAL) { + fprintf(stderr, "%s: %s\n", __func__, strerror(-ret)); + } +} + static void kvmclock_vm_state_change(void *opaque, int running, RunState state) { @@ -183,13 +192,7 @@ static void kvmclock_vm_state_change(void *opaque, int running, return; } CPU_FOREACH(cpu) { - ret = kvm_vcpu_ioctl(cpu, KVM_KVMCLOCK_CTRL, 0); - if (ret) { - if (ret != -EINVAL) { - fprintf(stderr, "%s: %s\n", __func__, strerror(-ret)); - } - return; - } + run_on_cpu(cpu, do_kvmclock_ctrl, RUN_ON_CPU_NULL); } } else { diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 5b40d75439..e453692199 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -163,7 +163,7 @@ static void kvm_ioapic_class_init(ObjectClass *klass, void *data) } static const TypeInfo kvm_ioapic_info = { - .name = "kvm-ioapic", + .name = TYPE_KVM_IOAPIC, .parent = TYPE_IOAPIC_COMMON, .instance_size = sizeof(KVMIOAPICState), .class_init = kvm_ioapic_class_init, diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index d519e206c5..a3e33fbe5e 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -181,12 +181,12 @@ int load_multiboot(FWCfgState *fw_cfg, if (!is_multiboot) return 0; /* no multiboot */ - mb_debug("qemu: I believe we found a multiboot image!"); + mb_debug("I believe we found a multiboot image!"); memset(bootinfo, 0, sizeof(bootinfo)); memset(&mbs, 0, sizeof(mbs)); if (flags & 0x00000004) { /* MULTIBOOT_HEADER_HAS_VBE */ - error_report("qemu: multiboot knows VBE. we don't."); + error_report("multiboot knows VBE. we don't"); } if (!(flags & 0x00010000)) { /* MULTIBOOT_HEADER_HAS_ADDR */ uint64_t elf_entry; @@ -199,7 +199,7 @@ int load_multiboot(FWCfgState *fw_cfg, exit(1); } - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, &elf_low, &elf_high, 0, I386_ELF_MACHINE, 0, 0); if (kernel_size < 0) { @@ -216,7 +216,7 @@ int load_multiboot(FWCfgState *fw_cfg, exit(1); } - mb_debug("qemu: loading multiboot-elf kernel " + mb_debug("loading multiboot-elf kernel " "(%#x bytes) with entry %#zx", mb_kernel_size, (size_t)mh_entry_addr); } else { @@ -270,7 +270,7 @@ int load_multiboot(FWCfgState *fw_cfg, mb_debug("multiboot: load_addr = %#x", mh_load_addr); mb_debug("multiboot: load_end_addr = %#x", mh_load_end_addr); mb_debug("multiboot: bss_end_addr = %#x", mh_bss_end_addr); - mb_debug("qemu: loading multiboot kernel (%#x bytes) at %#x", + mb_debug("loading multiboot kernel (%#x bytes) at %#x", mb_load_size, mh_load_addr); mbs.mb_buf = g_malloc(mb_kernel_size); @@ -343,7 +343,11 @@ int load_multiboot(FWCfgState *fw_cfg, mbs.mb_buf_size = TARGET_PAGE_ALIGN(mb_mod_length + mbs.mb_buf_size); mbs.mb_buf = g_realloc(mbs.mb_buf, mbs.mb_buf_size); - load_image(one_file, (unsigned char *)mbs.mb_buf + offs); + if (load_image_size(one_file, (unsigned char *)mbs.mb_buf + offs, + mbs.mb_buf_size - offs) < 0) { + error_report("Error loading file '%s'", one_file); + exit(1); + } mb_add_mod(&mbs, mbs.mb_buf_phys + offs, mbs.mb_buf_phys + offs + mb_mod_length, c); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 66802fbfc2..d03b67ef85 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -37,7 +37,7 @@ #include "hw/pci/pci_bus.h" #include "hw/nvram/fw_cfg.h" #include "hw/timer/hpet.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "hw/loader.h" #include "elf.h" #include "multiboot.h" @@ -54,6 +54,7 @@ #include "sysemu/qtest.h" #include "kvm_i386.h" #include "hw/xen/xen.h" +#include "hw/xen/start_info.h" #include "ui/qemu-spice.h" #include "exec/memory.h" #include "exec/address-spaces.h" @@ -72,8 +73,10 @@ #include "qapi/visitor.h" #include "qom/cpu.h" #include "hw/nmi.h" +#include "hw/usb.h" #include "hw/i386/intel_iommu.h" #include "hw/net/ne2000-isa.h" +#include "standard-headers/asm-x86/bootparam.h" /* debug PC/ISA interrupts */ //#define DEBUG_IRQ @@ -109,6 +112,244 @@ static struct e820_entry *e820_table; static unsigned e820_entries; struct hpet_fw_config hpet_cfg = {.count = UINT8_MAX}; +/* Physical Address of PVH entry point read from kernel ELF NOTE */ +static size_t pvh_start_addr; + +GlobalProperty pc_compat_3_1[] = { + { "intel-iommu", "dma-drain", "off" }, + { "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "off" }, + { "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "off" }, + { "Opteron_G4" "-" TYPE_X86_CPU, "npt", "off" }, + { "Opteron_G4" "-" TYPE_X86_CPU, "nrip-save", "off" }, + { "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "off" }, + { "Opteron_G5" "-" TYPE_X86_CPU, "npt", "off" }, + { "Opteron_G5" "-" TYPE_X86_CPU, "nrip-save", "off" }, + { "EPYC" "-" TYPE_X86_CPU, "npt", "off" }, + { "EPYC" "-" TYPE_X86_CPU, "nrip-save", "off" }, + { "EPYC-IBPB" "-" TYPE_X86_CPU, "npt", "off" }, + { "EPYC-IBPB" "-" TYPE_X86_CPU, "nrip-save", "off" }, + { "Skylake-Client" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Skylake-Client-IBRS" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Skylake-Server" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Skylake-Server-IBRS" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Cascadelake-Server" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Icelake-Client" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Icelake-Server" "-" TYPE_X86_CPU, "mpx", "on" }, + { "Cascadelake-Server" "-" TYPE_X86_CPU, "stepping", "5" }, + { TYPE_X86_CPU, "x-intel-pt-auto-level", "off" }, +}; +const size_t pc_compat_3_1_len = G_N_ELEMENTS(pc_compat_3_1); + +GlobalProperty pc_compat_3_0[] = { + { TYPE_X86_CPU, "x-hv-synic-kvm-only", "on" }, + { "Skylake-Server" "-" TYPE_X86_CPU, "pku", "off" }, + { "Skylake-Server-IBRS" "-" TYPE_X86_CPU, "pku", "off" }, +}; +const size_t pc_compat_3_0_len = G_N_ELEMENTS(pc_compat_3_0); + +GlobalProperty pc_compat_2_12[] = { + { TYPE_X86_CPU, "legacy-cache", "on" }, + { TYPE_X86_CPU, "topoext", "off" }, + { "EPYC-" TYPE_X86_CPU, "xlevel", "0x8000000a" }, + { "EPYC-IBPB-" TYPE_X86_CPU, "xlevel", "0x8000000a" }, +}; +const size_t pc_compat_2_12_len = G_N_ELEMENTS(pc_compat_2_12); + +GlobalProperty pc_compat_2_11[] = { + { TYPE_X86_CPU, "x-migrate-smi-count", "off" }, + { "Skylake-Server" "-" TYPE_X86_CPU, "clflushopt", "off" }, +}; +const size_t pc_compat_2_11_len = G_N_ELEMENTS(pc_compat_2_11); + +GlobalProperty pc_compat_2_10[] = { + { TYPE_X86_CPU, "x-hv-max-vps", "0x40" }, + { "i440FX-pcihost", "x-pci-hole64-fix", "off" }, + { "q35-pcihost", "x-pci-hole64-fix", "off" }, +}; +const size_t pc_compat_2_10_len = G_N_ELEMENTS(pc_compat_2_10); + +GlobalProperty pc_compat_2_9[] = { + { "mch", "extended-tseg-mbytes", "0" }, +}; +const size_t pc_compat_2_9_len = G_N_ELEMENTS(pc_compat_2_9); + +GlobalProperty pc_compat_2_8[] = { + { TYPE_X86_CPU, "tcg-cpuid", "off" }, + { "kvmclock", "x-mach-use-reliable-get-clock", "off" }, + { "ICH9-LPC", "x-smi-broadcast", "off" }, + { TYPE_X86_CPU, "vmware-cpuid-freq", "off" }, + { "Haswell-" TYPE_X86_CPU, "stepping", "1" }, +}; +const size_t pc_compat_2_8_len = G_N_ELEMENTS(pc_compat_2_8); + +GlobalProperty pc_compat_2_7[] = { + { TYPE_X86_CPU, "l3-cache", "off" }, + { TYPE_X86_CPU, "full-cpuid-auto-level", "off" }, + { "Opteron_G3" "-" TYPE_X86_CPU, "family", "15" }, + { "Opteron_G3" "-" TYPE_X86_CPU, "model", "6" }, + { "Opteron_G3" "-" TYPE_X86_CPU, "stepping", "1" }, + { "isa-pcspk", "migrate", "off" }, +}; +const size_t pc_compat_2_7_len = G_N_ELEMENTS(pc_compat_2_7); + +GlobalProperty pc_compat_2_6[] = { + { TYPE_X86_CPU, "cpuid-0xb", "off" }, + { "vmxnet3", "romfile", "" }, + { TYPE_X86_CPU, "fill-mtrr-mask", "off" }, + { "apic-common", "legacy-instance-id", "on", } +}; +const size_t pc_compat_2_6_len = G_N_ELEMENTS(pc_compat_2_6); + +GlobalProperty pc_compat_2_5[] = {}; +const size_t pc_compat_2_5_len = G_N_ELEMENTS(pc_compat_2_5); + +GlobalProperty pc_compat_2_4[] = { + PC_CPU_MODEL_IDS("2.4.0") + { "Haswell-" TYPE_X86_CPU, "abm", "off" }, + { "Haswell-noTSX-" TYPE_X86_CPU, "abm", "off" }, + { "Broadwell-" TYPE_X86_CPU, "abm", "off" }, + { "Broadwell-noTSX-" TYPE_X86_CPU, "abm", "off" }, + { "host" "-" TYPE_X86_CPU, "host-cache-info", "on" }, + { TYPE_X86_CPU, "check", "off" }, + { "qemu64" "-" TYPE_X86_CPU, "sse4a", "on" }, + { "qemu64" "-" TYPE_X86_CPU, "abm", "on" }, + { "qemu64" "-" TYPE_X86_CPU, "popcnt", "on" }, + { "qemu32" "-" TYPE_X86_CPU, "popcnt", "on" }, + { "Opteron_G2" "-" TYPE_X86_CPU, "rdtscp", "on" }, + { "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "on" }, + { "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "on" }, + { "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "on", } +}; +const size_t pc_compat_2_4_len = G_N_ELEMENTS(pc_compat_2_4); + +GlobalProperty pc_compat_2_3[] = { + PC_CPU_MODEL_IDS("2.3.0") + { TYPE_X86_CPU, "arat", "off" }, + { "qemu64" "-" TYPE_X86_CPU, "min-level", "4" }, + { "kvm64" "-" TYPE_X86_CPU, "min-level", "5" }, + { "pentium3" "-" TYPE_X86_CPU, "min-level", "2" }, + { "n270" "-" TYPE_X86_CPU, "min-level", "5" }, + { "Conroe" "-" TYPE_X86_CPU, "min-level", "4" }, + { "Penryn" "-" TYPE_X86_CPU, "min-level", "4" }, + { "Nehalem" "-" TYPE_X86_CPU, "min-level", "4" }, + { "n270" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Penryn" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Conroe" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Nehalem" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Westmere" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "SandyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "IvyBridge" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Haswell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Haswell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Broadwell" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { "Broadwell-noTSX" "-" TYPE_X86_CPU, "min-xlevel", "0x8000000a" }, + { TYPE_X86_CPU, "kvm-no-smi-migration", "on" }, +}; +const size_t pc_compat_2_3_len = G_N_ELEMENTS(pc_compat_2_3); + +GlobalProperty pc_compat_2_2[] = { + PC_CPU_MODEL_IDS("2.2.0") + { "kvm64" "-" TYPE_X86_CPU, "vme", "off" }, + { "kvm32" "-" TYPE_X86_CPU, "vme", "off" }, + { "Conroe" "-" TYPE_X86_CPU, "vme", "off" }, + { "Penryn" "-" TYPE_X86_CPU, "vme", "off" }, + { "Nehalem" "-" TYPE_X86_CPU, "vme", "off" }, + { "Westmere" "-" TYPE_X86_CPU, "vme", "off" }, + { "SandyBridge" "-" TYPE_X86_CPU, "vme", "off" }, + { "Haswell" "-" TYPE_X86_CPU, "vme", "off" }, + { "Broadwell" "-" TYPE_X86_CPU, "vme", "off" }, + { "Opteron_G1" "-" TYPE_X86_CPU, "vme", "off" }, + { "Opteron_G2" "-" TYPE_X86_CPU, "vme", "off" }, + { "Opteron_G3" "-" TYPE_X86_CPU, "vme", "off" }, + { "Opteron_G4" "-" TYPE_X86_CPU, "vme", "off" }, + { "Opteron_G5" "-" TYPE_X86_CPU, "vme", "off" }, + { "Haswell" "-" TYPE_X86_CPU, "f16c", "off" }, + { "Haswell" "-" TYPE_X86_CPU, "rdrand", "off" }, + { "Broadwell" "-" TYPE_X86_CPU, "f16c", "off" }, + { "Broadwell" "-" TYPE_X86_CPU, "rdrand", "off" }, +}; +const size_t pc_compat_2_2_len = G_N_ELEMENTS(pc_compat_2_2); + +GlobalProperty pc_compat_2_1[] = { + PC_CPU_MODEL_IDS("2.1.0") + { "coreduo" "-" TYPE_X86_CPU, "vmx", "on" }, + { "core2duo" "-" TYPE_X86_CPU, "vmx", "on" }, +}; +const size_t pc_compat_2_1_len = G_N_ELEMENTS(pc_compat_2_1); + +GlobalProperty pc_compat_2_0[] = { + PC_CPU_MODEL_IDS("2.0.0") + { "virtio-scsi-pci", "any_layout", "off" }, + { "PIIX4_PM", "memory-hotplug-support", "off" }, + { "apic", "version", "0x11" }, + { "nec-usb-xhci", "superspeed-ports-first", "off" }, + { "nec-usb-xhci", "force-pcie-endcap", "on" }, + { "pci-serial", "prog_if", "0" }, + { "pci-serial-2x", "prog_if", "0" }, + { "pci-serial-4x", "prog_if", "0" }, + { "virtio-net-pci", "guest_announce", "off" }, + { "ICH9-LPC", "memory-hotplug-support", "off" }, + { "xio3130-downstream", COMPAT_PROP_PCP, "off" }, + { "ioh3420", COMPAT_PROP_PCP, "off" }, +}; +const size_t pc_compat_2_0_len = G_N_ELEMENTS(pc_compat_2_0); + +GlobalProperty pc_compat_1_7[] = { + PC_CPU_MODEL_IDS("1.7.0") + { TYPE_USB_DEVICE, "msos-desc", "no" }, + { "PIIX4_PM", "acpi-pci-hotplug-with-bridge-support", "off" }, + { "hpet", HPET_INTCAP, "4" }, +}; +const size_t pc_compat_1_7_len = G_N_ELEMENTS(pc_compat_1_7); + +GlobalProperty pc_compat_1_6[] = { + PC_CPU_MODEL_IDS("1.6.0") + { "e1000", "mitigation", "off" }, + { "qemu64-" TYPE_X86_CPU, "model", "2" }, + { "qemu32-" TYPE_X86_CPU, "model", "3" }, + { "i440FX-pcihost", "short_root_bus", "1" }, + { "q35-pcihost", "short_root_bus", "1" }, +}; +const size_t pc_compat_1_6_len = G_N_ELEMENTS(pc_compat_1_6); + +GlobalProperty pc_compat_1_5[] = { + PC_CPU_MODEL_IDS("1.5.0") + { "Conroe-" TYPE_X86_CPU, "model", "2" }, + { "Conroe-" TYPE_X86_CPU, "min-level", "2" }, + { "Penryn-" TYPE_X86_CPU, "model", "2" }, + { "Penryn-" TYPE_X86_CPU, "min-level", "2" }, + { "Nehalem-" TYPE_X86_CPU, "model", "2" }, + { "Nehalem-" TYPE_X86_CPU, "min-level", "2" }, + { "virtio-net-pci", "any_layout", "off" }, + { TYPE_X86_CPU, "pmu", "on" }, + { "i440FX-pcihost", "short_root_bus", "0" }, + { "q35-pcihost", "short_root_bus", "0" }, +}; +const size_t pc_compat_1_5_len = G_N_ELEMENTS(pc_compat_1_5); + +GlobalProperty pc_compat_1_4[] = { + PC_CPU_MODEL_IDS("1.4.0") + { "scsi-hd", "discard_granularity", "0" }, + { "scsi-cd", "discard_granularity", "0" }, + { "scsi-disk", "discard_granularity", "0" }, + { "ide-hd", "discard_granularity", "0" }, + { "ide-cd", "discard_granularity", "0" }, + { "ide-drive", "discard_granularity", "0" }, + { "virtio-blk-pci", "discard_granularity", "0" }, + /* DEV_NVECTORS_UNSPECIFIED as a uint32_t string: */ + { "virtio-serial-pci", "vectors", "0xFFFFFFFF" }, + { "virtio-net-pci", "ctrl_guest_offloads", "off" }, + { "e1000", "romfile", "pxe-e1000.rom" }, + { "ne2k_pci", "romfile", "pxe-ne2k_pci.rom" }, + { "pcnet", "romfile", "pxe-pcnet.rom" }, + { "rtl8139", "romfile", "pxe-rtl8139.rom" }, + { "virtio-net-pci", "romfile", "pxe-virtio.rom" }, + { "486-" TYPE_X86_CPU, "model", "0" }, + { "n270" "-" TYPE_X86_CPU, "movbe", "off" }, + { "Westmere" "-" TYPE_X86_CPU, "pclmulqdq", "off" }, +}; +const size_t pc_compat_1_4_len = G_N_ELEMENTS(pc_compat_1_4); + void gsi_handler(void *opaque, int n, int level) { GSIState *s = opaque; @@ -825,13 +1066,6 @@ static long get_file_size(FILE *f) return size; } -/* setup_data types */ -#define SETUP_NONE 0 -#define SETUP_E820_EXT 1 -#define SETUP_DTB 2 -#define SETUP_PCI 3 -#define SETUP_EFI 4 - struct setup_data { uint64_t next; uint32_t type; @@ -839,14 +1073,117 @@ struct setup_data { uint8_t data[0]; } __attribute__((packed)); + +/* + * The entry point into the kernel for PVH boot is different from + * the native entry point. The PVH entry is defined by the x86/HVM + * direct boot ABI and is available in an ELFNOTE in the kernel binary. + * + * This function is passed to load_elf() when it is called from + * load_elfboot() which then additionally checks for an ELF Note of + * type XEN_ELFNOTE_PHYS32_ENTRY and passes it to this function to + * parse the PVH entry address from the ELF Note. + * + * Due to trickery in elf_opts.h, load_elf() is actually available as + * load_elf32() or load_elf64() and this routine needs to be able + * to deal with being called as 32 or 64 bit. + * + * The address of the PVH entry point is saved to the 'pvh_start_addr' + * global variable. (although the entry point is 32-bit, the kernel + * binary can be either 32-bit or 64-bit). + */ +static uint64_t read_pvh_start_addr(void *arg1, void *arg2, bool is64) +{ + size_t *elf_note_data_addr; + + /* Check if ELF Note header passed in is valid */ + if (arg1 == NULL) { + return 0; + } + + if (is64) { + struct elf64_note *nhdr64 = (struct elf64_note *)arg1; + uint64_t nhdr_size64 = sizeof(struct elf64_note); + uint64_t phdr_align = *(uint64_t *)arg2; + uint64_t nhdr_namesz = nhdr64->n_namesz; + + elf_note_data_addr = + ((void *)nhdr64) + nhdr_size64 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + } else { + struct elf32_note *nhdr32 = (struct elf32_note *)arg1; + uint32_t nhdr_size32 = sizeof(struct elf32_note); + uint32_t phdr_align = *(uint32_t *)arg2; + uint32_t nhdr_namesz = nhdr32->n_namesz; + + elf_note_data_addr = + ((void *)nhdr32) + nhdr_size32 + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align); + } + + pvh_start_addr = *elf_note_data_addr; + + return pvh_start_addr; +} + +static bool load_elfboot(const char *kernel_filename, + int kernel_file_size, + uint8_t *header, + size_t pvh_xen_start_addr, + FWCfgState *fw_cfg) +{ + uint32_t flags = 0; + uint32_t mh_load_addr = 0; + uint32_t elf_kernel_size = 0; + uint64_t elf_entry; + uint64_t elf_low, elf_high; + int kernel_size; + + if (ldl_p(header) != 0x464c457f) { + return false; /* no elfboot */ + } + + bool elf_is64 = header[EI_CLASS] == ELFCLASS64; + flags = elf_is64 ? + ((Elf64_Ehdr *)header)->e_flags : ((Elf32_Ehdr *)header)->e_flags; + + if (flags & 0x00010004) { /* LOAD_ELF_HEADER_HAS_ADDR */ + error_report("elfboot unsupported flags = %x", flags); + exit(1); + } + + uint64_t elf_note_type = XEN_ELFNOTE_PHYS32_ENTRY; + kernel_size = load_elf(kernel_filename, read_pvh_start_addr, + NULL, &elf_note_type, &elf_entry, + &elf_low, &elf_high, 0, I386_ELF_MACHINE, + 0, 0); + + if (kernel_size < 0) { + error_report("Error while loading elf kernel"); + exit(1); + } + mh_load_addr = elf_low; + elf_kernel_size = elf_high - elf_low; + + if (pvh_start_addr == 0) { + error_report("Error loading uncompressed kernel without PVH ELF Note"); + exit(1); + } + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ENTRY, pvh_start_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_ADDR, mh_load_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_KERNEL_SIZE, elf_kernel_size); + + return true; +} + static void load_linux(PCMachineState *pcms, FWCfgState *fw_cfg) { uint16_t protocol; - int setup_size, kernel_size, initrd_size = 0, cmdline_size; + int setup_size, kernel_size, cmdline_size; int dtb_size, setup_data_offset; uint32_t initrd_max; - uint8_t header[8192], *setup, *kernel, *initrd_data; + uint8_t header[8192], *setup, *kernel; hwaddr real_addr, prot_addr, cmdline_addr, initrd_addr = 0; FILE *f; char *vmode; @@ -878,12 +1215,70 @@ static void load_linux(PCMachineState *pcms, if (ldl_p(header+0x202) == 0x53726448) { protocol = lduw_p(header+0x206); } else { - /* This looks like a multiboot kernel. If it is, let's stop - treating it like a Linux kernel. */ + /* + * This could be a multiboot kernel. If it is, let's stop treating it + * like a Linux kernel. + * Note: some multiboot images could be in the ELF format (the same of + * PVH), so we try multiboot first since we check the multiboot magic + * header before to load it. + */ if (load_multiboot(fw_cfg, f, kernel_filename, initrd_filename, kernel_cmdline, kernel_size, header)) { return; } + /* + * Check if the file is an uncompressed kernel file (ELF) and load it, + * saving the PVH entry point used by the x86/HVM direct boot ABI. + * If load_elfboot() is successful, populate the fw_cfg info. + */ + if (pcmc->pvh_enabled && + load_elfboot(kernel_filename, kernel_size, + header, pvh_start_addr, fw_cfg)) { + fclose(f); + + fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, + strlen(kernel_cmdline) + 1); + fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA, kernel_cmdline); + + fw_cfg_add_i32(fw_cfg, FW_CFG_SETUP_SIZE, sizeof(header)); + fw_cfg_add_bytes(fw_cfg, FW_CFG_SETUP_DATA, + header, sizeof(header)); + + /* load initrd */ + if (initrd_filename) { + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + + if (!g_file_get_contents(initrd_filename, &initrd_data, + &initrd_size, &gerr)) { + fprintf(stderr, "qemu: error reading initrd %s: %s\n", + initrd_filename, gerr->message); + exit(1); + } + + initrd_max = pcms->below_4g_mem_size - pcmc->acpi_data_size - 1; + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); + exit(1); + } + + initrd_addr = (initrd_max - initrd_size) & ~4095; + + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); + fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, + initrd_size); + } + + option_rom[nb_option_roms].bootindex = 0; + option_rom[nb_option_roms].name = "pvh.bin"; + nb_option_roms++; + + return; + } protocol = 0; } @@ -915,7 +1310,26 @@ static void load_linux(PCMachineState *pcms, #endif /* highest address for loading the initrd */ - if (protocol >= 0x203) { + if (protocol >= 0x20c && + lduw_p(header+0x236) & XLF_CAN_BE_LOADED_ABOVE_4G) { + /* + * Linux has supported initrd up to 4 GB for a very long time (2007, + * long before XLF_CAN_BE_LOADED_ABOVE_4G which was added in 2013), + * though it only sets initrd_max to 2 GB to "work around bootloader + * bugs". Luckily, QEMU firmware(which does something like bootloader) + * has supported this. + * + * It's believed that if XLF_CAN_BE_LOADED_ABOVE_4G is set, initrd can + * be loaded into any address. + * + * In addition, initrd_max is uint32_t simply because QEMU doesn't + * support the 64-bit boot protocol (specifically the ext_ramdisk_image + * field). + * + * Therefore here just limit initrd_max to UINT32_MAX simply as well. + */ + initrd_max = UINT32_MAX; + } else if (protocol >= 0x203) { initrd_max = ldl_p(header+0x22c); } else { initrd_max = 0x37ffffff; @@ -969,23 +1383,30 @@ static void load_linux(PCMachineState *pcms, /* load initrd */ if (initrd_filename) { + gsize initrd_size; + gchar *initrd_data; + GError *gerr = NULL; + if (protocol < 0x200) { fprintf(stderr, "qemu: linux kernel too old to load a ram disk\n"); exit(1); } - initrd_size = get_image_size(initrd_filename); - if (initrd_size < 0) { + if (!g_file_get_contents(initrd_filename, &initrd_data, + &initrd_size, &gerr)) { fprintf(stderr, "qemu: error reading initrd %s: %s\n", - initrd_filename, strerror(errno)); + initrd_filename, gerr->message); + exit(1); + } + if (initrd_size >= initrd_max) { + fprintf(stderr, "qemu: initrd is too large, cannot support." + "(max: %"PRIu32", need %"PRId64")\n", + initrd_max, (uint64_t)initrd_size); exit(1); } initrd_addr = (initrd_max-initrd_size) & ~4095; - initrd_data = g_malloc(initrd_size); - load_image(initrd_filename, initrd_data); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, initrd_addr); fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); fw_cfg_add_bytes(fw_cfg, FW_CFG_INITRD_DATA, initrd_data, initrd_size); @@ -1242,7 +1663,7 @@ void pc_machine_done(Notifier *notifier, void *data) if (pcms->apic_id_limit > 255 && !xen_enabled()) { IntelIOMMUState *iommu = INTEL_IOMMU_DEVICE(x86_iommu_get_default()); - if (!iommu || !iommu->x86_iommu.intr_supported || + if (!iommu || !x86_iommu_ir_supported(X86_IOMMU_DEVICE(iommu)) || iommu->intr_eim != ON_OFF_AUTO_ON) { error_report("current -smp configuration requires " "Extended Interrupt Mode enabled. " @@ -1278,33 +1699,6 @@ void pc_pci_as_mapping_init(Object *owner, MemoryRegion *system_memory, pci_address_space, -1); } -void pc_acpi_init(const char *default_dsdt) -{ - char *filename; - - if (acpi_tables != NULL) { - /* manually set via -acpitable, leave it alone */ - return; - } - - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, default_dsdt); - if (filename == NULL) { - warn_report("failed to find %s", default_dsdt); - } else { - QemuOpts *opts = qemu_opts_create(qemu_find_opts("acpi"), NULL, 0, - &error_abort); - Error *err = NULL; - - qemu_opt_set(opts, "file", filename, &error_abort); - - acpi_table_add_builtin(opts, &err); - if (err) { - warn_reportf_err(err, "failed to load %s: ", filename); - } - g_free(filename); - } -} - void xen_load_linux(PCMachineState *pcms) { int i; @@ -1320,6 +1714,7 @@ void xen_load_linux(PCMachineState *pcms) for (i = 0; i < nb_option_roms; i++) { assert(!strcmp(option_rom[i].name, "linuxboot.bin") || !strcmp(option_rom[i].name, "linuxboot_dma.bin") || + !strcmp(option_rom[i].name, "pvh.bin") || !strcmp(option_rom[i].name, "multiboot.bin")); rom_add_option(option_rom[i].name, option_rom[i].bootindex); } @@ -1419,7 +1814,7 @@ void pc_memory_init(PCMachineState *pcms, } /* Initialize PC system firmware */ - pc_system_firmware_init(rom_memory, !pcmc->pci_enabled); + pc_system_firmware_init(pcms, rom_memory); option_rom_mr = g_malloc(sizeof(*option_rom_mr)); memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, @@ -1663,9 +2058,9 @@ void ioapic_init_gsi(GSIState *gsi_state, const char *parent_name) unsigned int i; if (kvm_ioapic_in_kernel()) { - dev = qdev_create(NULL, "kvm-ioapic"); + dev = qdev_create(NULL, TYPE_KVM_IOAPIC); } else { - dev = qdev_create(NULL, "ioapic"); + dev = qdev_create(NULL, TYPE_IOAPIC); } if (parent_name) { object_property_add_child(object_resolve_path(parent_name, NULL), @@ -1684,7 +2079,11 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { const PCMachineState *pcms = PC_MACHINE(hotplug_dev); + const PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + const MachineState *ms = MACHINE(hotplug_dev); const bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); + const uint64_t legacy_align = TARGET_PAGE_SIZE; + Error *local_err = NULL; /* * When -no-acpi is used with Q35 machine type, no ACPI is built, @@ -1697,40 +2096,39 @@ static void pc_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - if (is_nvdimm && !pcms->acpi_nvdimm_state.is_enabled) { + if (is_nvdimm && !ms->nvdimms_state->is_enabled) { error_setg(errp, "nvdimm is not enabled: missing 'nvdimm' in '-M'"); return; } + + hotplug_handler_pre_plug(pcms->acpi_dev, dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pc_dimm_pre_plug(PC_DIMM(dev), MACHINE(hotplug_dev), + pcmc->enforce_aligned_dimm ? NULL : &legacy_align, errp); } static void pc_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - 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, &error_abort); - uint64_t align = TARGET_PAGE_SIZE; + MachineState *ms = MACHINE(hotplug_dev); bool is_nvdimm = object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM); - if (memory_region_get_alignment(mr) && pcmc->enforce_aligned_dimm) { - align = memory_region_get_alignment(mr); - } - - pc_dimm_plug(dev, MACHINE(pcms), align, &local_err); + pc_dimm_plug(PC_DIMM(dev), MACHINE(pcms), &local_err); if (local_err) { goto out; } if (is_nvdimm) { - nvdimm_plug(&pcms->acpi_nvdimm_state); + nvdimm_plug(ms->nvdimms_state); } - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort); + hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &error_abort); out: error_propagate(errp, local_err); } @@ -1738,7 +2136,6 @@ out: static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - HotplugHandlerClass *hhc; Error *local_err = NULL; PCMachineState *pcms = PC_MACHINE(hotplug_dev); @@ -1759,9 +2156,8 @@ static void pc_memory_unplug_request(HotplugHandler *hotplug_dev, goto out; } - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - + hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, + &local_err); out: error_propagate(errp, local_err); } @@ -1770,19 +2166,15 @@ static void pc_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCMachineState *pcms = PC_MACHINE(hotplug_dev); - HotplugHandlerClass *hhc; Error *local_err = NULL; - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - + hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); if (local_err) { goto out; } - pc_dimm_unplug(dev, MACHINE(pcms)); - object_unparent(OBJECT(dev)); - + pc_dimm_unplug(PC_DIMM(dev), MACHINE(pcms)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); out: error_propagate(errp, local_err); } @@ -1817,14 +2209,12 @@ static void pc_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *found_cpu; - HotplugHandlerClass *hhc; Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); if (pcms->acpi_dev) { - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); + hotplug_handler_plug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); if (local_err) { goto out; } @@ -1848,7 +2238,6 @@ static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { int idx = -1; - HotplugHandlerClass *hhc; Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); @@ -1865,9 +2254,8 @@ static void pc_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, goto out; } - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - + hotplug_handler_unplug_request(HOTPLUG_HANDLER(pcms->acpi_dev), dev, + &local_err); if (local_err) { goto out; } @@ -1881,21 +2269,18 @@ static void pc_cpu_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { CPUArchId *found_cpu; - HotplugHandlerClass *hhc; Error *local_err = NULL; X86CPU *cpu = X86_CPU(dev); PCMachineState *pcms = PC_MACHINE(hotplug_dev); - hhc = HOTPLUG_HANDLER_GET_CLASS(pcms->acpi_dev); - hhc->unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); - + hotplug_handler_unplug(HOTPLUG_HANDLER(pcms->acpi_dev), dev, &local_err); if (local_err) { goto out; } found_cpu = pc_find_cpu_slot(MACHINE(pcms), cpu->apic_id, NULL); found_cpu->cpu = NULL; - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); /* decrement the number of CPUs */ pcms->boot_cpus--; @@ -2061,7 +2446,7 @@ static void pc_machine_device_unplug_cb(HotplugHandler *hotplug_dev, } } -static HotplugHandler *pc_get_hotpug_handler(MachineState *machine, +static HotplugHandler *pc_get_hotplug_handler(MachineState *machine, DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || @@ -2181,86 +2566,46 @@ static void pc_machine_set_smm(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &pcms->smm, errp); } -static bool pc_machine_get_nvdimm(Object *obj, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - return pcms->acpi_nvdimm_state.is_enabled; -} - -static void pc_machine_set_nvdimm(Object *obj, bool value, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - pcms->acpi_nvdimm_state.is_enabled = value; -} - -static char *pc_machine_get_nvdimm_persistence(Object *obj, Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - - return g_strdup(pcms->acpi_nvdimm_state.persistence_string); -} - -static void pc_machine_set_nvdimm_persistence(Object *obj, const char *value, - Error **errp) -{ - PCMachineState *pcms = PC_MACHINE(obj); - AcpiNVDIMMState *nvdimm_state = &pcms->acpi_nvdimm_state; - - if (strcmp(value, "cpu") == 0) - nvdimm_state->persistence = 3; - else if (strcmp(value, "mem-ctrl") == 0) - nvdimm_state->persistence = 2; - else { - error_report("-machine nvdimm-persistence=%s: unsupported option", value); - exit(EXIT_FAILURE); - } - - g_free(nvdimm_state->persistence_string); - nvdimm_state->persistence_string = g_strdup(value); -} - static bool pc_machine_get_smbus(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - return pcms->smbus; + return pcms->smbus_enabled; } static void pc_machine_set_smbus(Object *obj, bool value, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - pcms->smbus = value; + pcms->smbus_enabled = value; } static bool pc_machine_get_sata(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - return pcms->sata; + return pcms->sata_enabled; } static void pc_machine_set_sata(Object *obj, bool value, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - pcms->sata = value; + pcms->sata_enabled = value; } static bool pc_machine_get_pit(Object *obj, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - return pcms->pit; + return pcms->pit_enabled; } static void pc_machine_set_pit(Object *obj, bool value, Error **errp) { PCMachineState *pcms = PC_MACHINE(obj); - pcms->pit = value; + pcms->pit_enabled = value; } static void pc_machine_initfn(Object *obj) @@ -2270,13 +2615,13 @@ static void pc_machine_initfn(Object *obj) pcms->max_ram_below_4g = 0; /* use default */ pcms->smm = ON_OFF_AUTO_AUTO; pcms->vmport = ON_OFF_AUTO_AUTO; - /* nvdimm is disabled on default. */ - pcms->acpi_nvdimm_state.is_enabled = false; /* acpi build is enabled by default if machine supports it */ pcms->acpi_build_enabled = PC_MACHINE_GET_CLASS(pcms)->has_acpi_build; - pcms->smbus = true; - pcms->sata = true; - pcms->pit = true; + pcms->smbus_enabled = true; + pcms->sata_enabled = true; + pcms->pit_enabled = true; + + pc_system_flash_create(pcms); } static void pc_machine_reset(void) @@ -2389,8 +2734,9 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) pcmc->acpi_data_size = 0x20000 + 0x8000; pcmc->save_tsc_khz = true; pcmc->linuxboot_dma_enabled = true; + pcmc->pvh_enabled = true; assert(!mc->get_hotplug_handler); - mc->get_hotplug_handler = pc_get_hotpug_handler; + mc->get_hotplug_handler = pc_get_hotplug_handler; mc->cpu_index_to_instance_props = pc_cpu_index_to_props; mc->get_default_cpu_node_id = pc_get_default_cpu_node_id; mc->possible_cpu_arch_ids = pc_possible_cpu_arch_ids; @@ -2407,6 +2753,7 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) hc->unplug = pc_machine_device_unplug_cb; nc->nmi_monitor_handler = x86_nmi; mc->default_cpu_type = TARGET_DEFAULT_CPU_TYPE; + mc->nvdimm_supported = true; object_class_property_add(oc, PC_MACHINE_DEVMEM_REGION_SIZE, "int", pc_machine_get_device_memory_region_size, NULL, @@ -2431,13 +2778,6 @@ static void pc_machine_class_init(ObjectClass *oc, void *data) object_class_property_set_description(oc, PC_MACHINE_VMPORT, "Enable vmport (pc & q35)", &error_abort); - object_class_property_add_bool(oc, PC_MACHINE_NVDIMM, - pc_machine_get_nvdimm, pc_machine_set_nvdimm, &error_abort); - - object_class_property_add_str(oc, PC_MACHINE_NVDIMM_PERSIST, - pc_machine_get_nvdimm_persistence, - pc_machine_set_nvdimm_persistence, &error_abort); - object_class_property_add_bool(oc, PC_MACHINE_SMBUS, pc_machine_get_smbus, pc_machine_set_smbus, &error_abort); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index dc09466b3e..8ad8e885c6 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -30,7 +30,7 @@ #include "hw/i386/pc.h" #include "hw/i386/apic.h" #include "hw/display/ramfb.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" #include "hw/usb.h" @@ -42,7 +42,7 @@ #include "sysemu/sysemu.h" #include "hw/sysbus.h" #include "sysemu/arch_init.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/xen/xen.h" #include "exec/memory.h" #include "exec/address-spaces.h" @@ -239,7 +239,8 @@ static void pc_init1(MachineState *machine, /* init basic PC hardware */ pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, true, - (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit, 0x4); + (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled, + 0x4); pc_nic_init(pcmc, isa_bus, pci_bus); @@ -296,8 +297,8 @@ static void pc_init1(MachineState *machine, PC_MACHINE_ACPI_DEVICE_PROP, &error_abort); } - if (pcms->acpi_nvdimm_state.is_enabled) { - nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, + if (machine->nvdimms_state->is_enabled) { + nvdimm_init_acpi_state(machine->nvdimms_state, system_io, pcms->fw_cfg, OBJECT(pcms)); } } @@ -309,7 +310,7 @@ static void pc_init1(MachineState *machine, * HW_COMPAT_*, PC_COMPAT_*, or * pc_*_machine_options(). */ -static void pc_compat_2_3(MachineState *machine) +static void pc_compat_2_3_fn(MachineState *machine) { PCMachineState *pcms = PC_MACHINE(machine); if (kvm_enabled()) { @@ -317,47 +318,46 @@ static void pc_compat_2_3(MachineState *machine) } } -static void pc_compat_2_2(MachineState *machine) +static void pc_compat_2_2_fn(MachineState *machine) { - pc_compat_2_3(machine); - machine->suppress_vmdesc = true; + pc_compat_2_3_fn(machine); } -static void pc_compat_2_1(MachineState *machine) +static void pc_compat_2_1_fn(MachineState *machine) { - pc_compat_2_2(machine); + pc_compat_2_2_fn(machine); x86_cpu_change_kvm_default("svm", NULL); } -static void pc_compat_2_0(MachineState *machine) +static void pc_compat_2_0_fn(MachineState *machine) { - pc_compat_2_1(machine); + pc_compat_2_1_fn(machine); } -static void pc_compat_1_7(MachineState *machine) +static void pc_compat_1_7_fn(MachineState *machine) { - pc_compat_2_0(machine); + pc_compat_2_0_fn(machine); x86_cpu_change_kvm_default("x2apic", NULL); } -static void pc_compat_1_6(MachineState *machine) +static void pc_compat_1_6_fn(MachineState *machine) { - pc_compat_1_7(machine); + pc_compat_1_7_fn(machine); } -static void pc_compat_1_5(MachineState *machine) +static void pc_compat_1_5_fn(MachineState *machine) { - pc_compat_1_6(machine); + pc_compat_1_6_fn(machine); } -static void pc_compat_1_4(MachineState *machine) +static void pc_compat_1_4_fn(MachineState *machine) { - pc_compat_1_5(machine); + pc_compat_1_5_fn(machine); } static void pc_compat_1_3(MachineState *machine) { - pc_compat_1_4(machine); + pc_compat_1_4_fn(machine); enable_compat_apic_id_mode(); } @@ -368,7 +368,7 @@ static void pc_compat_1_2(MachineState *machine) x86_cpu_change_kvm_default("kvm-pv-eoi", NULL); } -/* PC compat function for pc-0.10 to pc-0.13 */ +/* PC compat function for pc-0.12 and pc-0.13 */ static void pc_compat_0_13(MachineState *machine) { pc_compat_1_2(machine); @@ -428,22 +428,47 @@ static void pc_i440fx_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); } -static void pc_i440fx_3_0_machine_options(MachineClass *m) +static void pc_i440fx_4_0_machine_options(MachineClass *m) { pc_i440fx_machine_options(m); m->alias = "pc"; m->is_default = 1; } +DEFINE_I440FX_MACHINE(v4_0, "pc-i440fx-4.0", NULL, + pc_i440fx_4_0_machine_options); + +static void pc_i440fx_3_1_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_i440fx_4_0_machine_options(m); + m->is_default = 0; + m->smbus_no_migration_support = true; + m->alias = NULL; + pcmc->pvh_enabled = false; + compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); + compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); +} + +DEFINE_I440FX_MACHINE(v3_1, "pc-i440fx-3.1", NULL, + pc_i440fx_3_1_machine_options); + +static void pc_i440fx_3_0_machine_options(MachineClass *m) +{ + pc_i440fx_3_1_machine_options(m); + compat_props_add(m->compat_props, hw_compat_3_0, hw_compat_3_0_len); + compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); +} + DEFINE_I440FX_MACHINE(v3_0, "pc-i440fx-3.0", NULL, pc_i440fx_3_0_machine_options); static void pc_i440fx_2_12_machine_options(MachineClass *m) { pc_i440fx_3_0_machine_options(m); - m->is_default = 0; - m->alias = NULL; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); + compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); + compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, @@ -452,7 +477,8 @@ DEFINE_I440FX_MACHINE(v2_12, "pc-i440fx-2.12", NULL, static void pc_i440fx_2_11_machine_options(MachineClass *m) { pc_i440fx_2_12_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); + compat_props_add(m->compat_props, hw_compat_2_11, hw_compat_2_11_len); + compat_props_add(m->compat_props, pc_compat_2_11, pc_compat_2_11_len); } DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", NULL, @@ -461,7 +487,8 @@ DEFINE_I440FX_MACHINE(v2_11, "pc-i440fx-2.11", NULL, static void pc_i440fx_2_10_machine_options(MachineClass *m) { pc_i440fx_2_11_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_10); + compat_props_add(m->compat_props, hw_compat_2_10, hw_compat_2_10_len); + compat_props_add(m->compat_props, pc_compat_2_10, pc_compat_2_10_len); m->auto_enable_numa_with_memhp = false; } @@ -471,7 +498,8 @@ DEFINE_I440FX_MACHINE(v2_10, "pc-i440fx-2.10", NULL, static void pc_i440fx_2_9_machine_options(MachineClass *m) { pc_i440fx_2_10_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_9); + compat_props_add(m->compat_props, hw_compat_2_9, hw_compat_2_9_len); + compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; } @@ -481,108 +509,114 @@ DEFINE_I440FX_MACHINE(v2_9, "pc-i440fx-2.9", NULL, static void pc_i440fx_2_8_machine_options(MachineClass *m) { pc_i440fx_2_9_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_8); + compat_props_add(m->compat_props, hw_compat_2_8, hw_compat_2_8_len); + compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } DEFINE_I440FX_MACHINE(v2_8, "pc-i440fx-2.8", NULL, pc_i440fx_2_8_machine_options); - static void pc_i440fx_2_7_machine_options(MachineClass *m) { pc_i440fx_2_8_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_7); + compat_props_add(m->compat_props, hw_compat_2_7, hw_compat_2_7_len); + compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } DEFINE_I440FX_MACHINE(v2_7, "pc-i440fx-2.7", NULL, pc_i440fx_2_7_machine_options); - static void pc_i440fx_2_6_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_i440fx_2_7_machine_options(m); pcmc->legacy_cpu_hotplug = true; pcmc->linuxboot_dma_enabled = false; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); + compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); + compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } 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); pcmc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); + compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); + compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } 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); + compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); + compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } DEFINE_I440FX_MACHINE(v2_4, "pc-i440fx-2.4", NULL, pc_i440fx_2_4_machine_options) - static void pc_i440fx_2_3_machine_options(MachineClass *m) { pc_i440fx_2_4_machine_options(m); m->hw_version = "2.3.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_3); + compat_props_add(m->compat_props, hw_compat_2_3, hw_compat_2_3_len); + compat_props_add(m->compat_props, pc_compat_2_3, pc_compat_2_3_len); } -DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3, +DEFINE_I440FX_MACHINE(v2_3, "pc-i440fx-2.3", pc_compat_2_3_fn, pc_i440fx_2_3_machine_options); - 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); + m->default_machine_opts = "firmware=bios-256k.bin,suppress-vmdesc=on"; + compat_props_add(m->compat_props, hw_compat_2_2, hw_compat_2_2_len); + compat_props_add(m->compat_props, pc_compat_2_2, pc_compat_2_2_len); pcmc->rsdp_in_ram = false; } -DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2, +DEFINE_I440FX_MACHINE(v2_2, "pc-i440fx-2.2", pc_compat_2_2_fn, pc_i440fx_2_2_machine_options); - 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); + compat_props_add(m->compat_props, hw_compat_2_1, hw_compat_2_1_len); + compat_props_add(m->compat_props, pc_compat_2_1, pc_compat_2_1_len); pcmc->smbios_uuid_encoded = false; pcmc->enforce_aligned_dimm = false; } -DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1, +DEFINE_I440FX_MACHINE(v2_1, "pc-i440fx-2.1", pc_compat_2_1_fn, pc_i440fx_2_1_machine_options); - - 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); + compat_props_add(m->compat_props, pc_compat_2_0, pc_compat_2_0_len); pcmc->smbios_legacy_mode = true; pcmc->has_reserved_memory = false; /* This value depends on the actual DSDT and SSDT compiled into @@ -605,400 +639,216 @@ static void pc_i440fx_2_0_machine_options(MachineClass *m) pcmc->acpi_data_size = 0x10000; } -DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0, +DEFINE_I440FX_MACHINE(v2_0, "pc-i440fx-2.0", pc_compat_2_0_fn, pc_i440fx_2_0_machine_options); - 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); + compat_props_add(m->compat_props, pc_compat_1_7, pc_compat_1_7_len); 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, +DEFINE_I440FX_MACHINE(v1_7, "pc-i440fx-1.7", pc_compat_1_7_fn, pc_i440fx_1_7_machine_options); - 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); + compat_props_add(m->compat_props, pc_compat_1_6, pc_compat_1_6_len); pcmc->has_acpi_build = false; } -DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6, +DEFINE_I440FX_MACHINE(v1_6, "pc-i440fx-1.6", pc_compat_1_6_fn, pc_i440fx_1_6_machine_options); - 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); + compat_props_add(m->compat_props, pc_compat_1_5, pc_compat_1_5_len); } -DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5, +DEFINE_I440FX_MACHINE(v1_5, "pc-i440fx-1.5", pc_compat_1_5_fn, pc_i440fx_1_5_machine_options); - 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); + compat_props_add(m->compat_props, pc_compat_1_4, pc_compat_1_4_len); } -DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4, +DEFINE_I440FX_MACHINE(v1_4, "pc-i440fx-1.4", pc_compat_1_4_fn, pc_i440fx_1_4_machine_options); - -#define PC_COMPAT_1_3 \ - PC_CPU_MODEL_IDS("1.3.0") \ - {\ - .driver = "usb-tablet",\ - .property = "usb_version",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-net-pci",\ - .property = "ctrl_mac_addr",\ - .value = "off", \ - },{ \ - .driver = "virtio-net-pci", \ - .property = "mq", \ - .value = "off", \ - }, {\ - .driver = "e1000",\ - .property = "autonegotiation",\ - .value = "off",\ - }, - - static void pc_i440fx_1_3_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("1.3.0") + { "usb-tablet", "usb_version", "1" }, + { "virtio-net-pci", "ctrl_mac_addr", "off" }, + { "virtio-net-pci", "mq", "off" }, + { "e1000", "autonegotiation", "off" }, + }; + pc_i440fx_1_4_machine_options(m); m->hw_version = "1.3.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_3); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v1_3, "pc-1.3", pc_compat_1_3, pc_i440fx_1_3_machine_options); -#define PC_COMPAT_1_2 \ - PC_CPU_MODEL_IDS("1.2.0") \ - {\ - .driver = "nec-usb-xhci",\ - .property = "msi",\ - .value = "off",\ - },{\ - .driver = "nec-usb-xhci",\ - .property = "msix",\ - .value = "off",\ - },{\ - .driver = "ivshmem",\ - .property = "use64",\ - .value = "0",\ - },{\ - .driver = "qxl",\ - .property = "revision",\ - .value = stringify(3),\ - },{\ - .driver = "qxl-vga",\ - .property = "revision",\ - .value = stringify(3),\ - },{\ - .driver = "VGA",\ - .property = "mmio",\ - .value = "off",\ - }, - static void pc_i440fx_1_2_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("1.2.0") + { "nec-usb-xhci", "msi", "off" }, + { "nec-usb-xhci", "msix", "off" }, + { "qxl", "revision", "3" }, + { "qxl-vga", "revision", "3" }, + { "VGA", "mmio", "off" }, + }; + pc_i440fx_1_3_machine_options(m); m->hw_version = "1.2.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_2); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v1_2, "pc-1.2", pc_compat_1_2, pc_i440fx_1_2_machine_options); -#define PC_COMPAT_1_1 \ - PC_CPU_MODEL_IDS("1.1.0") \ - {\ - .driver = "virtio-scsi-pci",\ - .property = "hotplug",\ - .value = "off",\ - },{\ - .driver = "virtio-scsi-pci",\ - .property = "param_change",\ - .value = "off",\ - },{\ - .driver = "VGA",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "vmware-svga",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "qxl-vga",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "qxl",\ - .property = "vgamem_mb",\ - .value = stringify(8),\ - },{\ - .driver = "virtio-blk-pci",\ - .property = "config-wce",\ - .value = "off",\ - }, - static void pc_i440fx_1_1_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("1.1.0") + { "virtio-scsi-pci", "hotplug", "off" }, + { "virtio-scsi-pci", "param_change", "off" }, + { "VGA", "vgamem_mb", "8" }, + { "vmware-svga", "vgamem_mb", "8" }, + { "qxl-vga", "vgamem_mb", "8" }, + { "qxl", "vgamem_mb", "8" }, + { "virtio-blk-pci", "config-wce", "off" }, + }; + pc_i440fx_1_2_machine_options(m); m->hw_version = "1.1.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_1); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v1_1, "pc-1.1", pc_compat_1_2, pc_i440fx_1_1_machine_options); - -#define PC_COMPAT_1_0 \ - PC_CPU_MODEL_IDS("1.0") \ - {\ - .driver = TYPE_ISA_FDC,\ - .property = "check_media_rate",\ - .value = "off",\ - }, {\ - .driver = "virtio-balloon-pci",\ - .property = "class",\ - .value = stringify(PCI_CLASS_MEMORY_RAM),\ - },{\ - .driver = "apic-common",\ - .property = "vapic",\ - .value = "off",\ - },{\ - .driver = TYPE_USB_DEVICE,\ - .property = "full-path",\ - .value = "no",\ - }, - static void pc_i440fx_1_0_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("1.0") + { TYPE_ISA_FDC, "check_media_rate", "off" }, + { "virtio-balloon-pci", "class", stringify(PCI_CLASS_MEMORY_RAM) }, + { "apic-common", "vapic", "off" }, + { TYPE_USB_DEVICE, "full-path", "no" }, + }; + pc_i440fx_1_1_machine_options(m); m->hw_version = "1.0"; - SET_MACHINE_COMPAT(m, PC_COMPAT_1_0); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v1_0, "pc-1.0", pc_compat_1_2, pc_i440fx_1_0_machine_options); -#define PC_COMPAT_0_15 \ - PC_CPU_MODEL_IDS("0.15") - static void pc_i440fx_0_15_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("0.15") + }; + pc_i440fx_1_0_machine_options(m); m->hw_version = "0.15"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_15); + m->deprecation_reason = "use a newer machine type instead"; + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v0_15, "pc-0.15", pc_compat_1_2, pc_i440fx_0_15_machine_options); -#define PC_COMPAT_0_14 \ - PC_CPU_MODEL_IDS("0.14") \ - {\ - .driver = "virtio-blk-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "virtio-net-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "virtio-balloon-pci",\ - .property = "event_idx",\ - .value = "off",\ - },{\ - .driver = "qxl",\ - .property = "revision",\ - .value = stringify(2),\ - },{\ - .driver = "qxl-vga",\ - .property = "revision",\ - .value = stringify(2),\ - }, - static void pc_i440fx_0_14_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("0.14") + { "virtio-blk-pci", "event_idx", "off" }, + { "virtio-serial-pci", "event_idx", "off" }, + { "virtio-net-pci", "event_idx", "off" }, + { "virtio-balloon-pci", "event_idx", "off" }, + { "qxl", "revision", "2" }, + { "qxl-vga", "revision", "2" }, + }; + pc_i440fx_0_15_machine_options(m); m->hw_version = "0.14"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_14); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v0_14, "pc-0.14", pc_compat_1_2, pc_i440fx_0_14_machine_options); - -#define PC_COMPAT_0_13 \ - PC_CPU_MODEL_IDS("0.13") \ - {\ - .driver = TYPE_PCI_DEVICE,\ - .property = "command_serr_enable",\ - .value = "off",\ - },{\ - .driver = "AC97",\ - .property = "use_broken_id",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-9p-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = "VGA",\ - .property = "rombar",\ - .value = stringify(0),\ - },{\ - .driver = "vmware-svga",\ - .property = "rombar",\ - .value = stringify(0),\ - }, - static void pc_i440fx_0_13_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("0.13") + { TYPE_PCI_DEVICE, "command_serr_enable", "off" }, + { "AC97", "use_broken_id", "1" }, + { "virtio-9p-pci", "vectors", "0" }, + { "VGA", "rombar", "0" }, + { "vmware-svga", "rombar", "0" }, + }; + pc_i440fx_0_14_machine_options(m); m->hw_version = "0.13"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_13); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); pcmc->kvmclock_enabled = false; } DEFINE_I440FX_MACHINE(v0_13, "pc-0.13", pc_compat_0_13, pc_i440fx_0_13_machine_options); - -#define PC_COMPAT_0_12 \ - PC_CPU_MODEL_IDS("0.12") \ - {\ - .driver = "virtio-serial-pci",\ - .property = "max_ports",\ - .value = stringify(1),\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = "usb-mouse",\ - .property = "serial",\ - .value = "1",\ - },{\ - .driver = "usb-tablet",\ - .property = "serial",\ - .value = "1",\ - },{\ - .driver = "usb-kbd",\ - .property = "serial",\ - .value = "1",\ - }, - static void pc_i440fx_0_12_machine_options(MachineClass *m) { + static GlobalProperty compat[] = { + PC_CPU_MODEL_IDS("0.12") + { "virtio-serial-pci", "max_ports", "1" }, + { "virtio-serial-pci", "vectors", "0" }, + { "usb-mouse", "serial", "1" }, + { "usb-tablet", "serial", "1" }, + { "usb-kbd", "serial", "1" }, + }; + pc_i440fx_0_13_machine_options(m); m->hw_version = "0.12"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_12); + compat_props_add(m->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_I440FX_MACHINE(v0_12, "pc-0.12", pc_compat_0_13, pc_i440fx_0_12_machine_options); - -#define PC_COMPAT_0_11 \ - PC_CPU_MODEL_IDS("0.11") \ - {\ - .driver = "virtio-blk-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = TYPE_PCI_DEVICE,\ - .property = "rombar",\ - .value = stringify(0),\ - },{\ - .driver = "ide-drive",\ - .property = "ver",\ - .value = "0.11",\ - },{\ - .driver = "scsi-disk",\ - .property = "ver",\ - .value = "0.11",\ - }, - -static void pc_i440fx_0_11_machine_options(MachineClass *m) -{ - pc_i440fx_0_12_machine_options(m); - m->hw_version = "0.11"; - m->deprecation_reason = "use a newer machine type instead"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_11); -} - -DEFINE_I440FX_MACHINE(v0_11, "pc-0.11", pc_compat_0_13, - pc_i440fx_0_11_machine_options); - - -#define PC_COMPAT_0_10 \ - PC_CPU_MODEL_IDS("0.10") \ - {\ - .driver = "virtio-blk-pci",\ - .property = "class",\ - .value = stringify(PCI_CLASS_STORAGE_OTHER),\ - },{\ - .driver = "virtio-serial-pci",\ - .property = "class",\ - .value = stringify(PCI_CLASS_DISPLAY_OTHER),\ - },{\ - .driver = "virtio-net-pci",\ - .property = "vectors",\ - .value = stringify(0),\ - },{\ - .driver = "ide-drive",\ - .property = "ver",\ - .value = "0.10",\ - },{\ - .driver = "scsi-disk",\ - .property = "ver",\ - .value = "0.10",\ - }, - -static void pc_i440fx_0_10_machine_options(MachineClass *m) -{ - pc_i440fx_0_11_machine_options(m); - m->hw_version = "0.10"; - SET_MACHINE_COMPAT(m, PC_COMPAT_0_10); -} - -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; diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 532241e3f8..372c6b73be 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -33,7 +33,7 @@ #include "hw/hw.h" #include "hw/loader.h" #include "sysemu/arch_init.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/boards.h" #include "hw/timer/mc146818rtc.h" #include "hw/xen/xen.h" @@ -47,7 +47,7 @@ #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" #include "hw/display/ramfb.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "hw/ide/pci.h" #include "hw/ide/ahci.h" #include "hw/usb.h" @@ -58,6 +58,59 @@ /* ICH9 AHCI has 6 ports */ #define MAX_SATA_PORTS 6 +struct ehci_companions { + const char *name; + int func; + int port; +}; + +static const struct ehci_companions ich9_1d[] = { + { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, + { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, + { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, +}; + +static const struct ehci_companions ich9_1a[] = { + { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, + { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, + { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, +}; + +static int ehci_create_ich9_with_companions(PCIBus *bus, int slot) +{ + const struct ehci_companions *comp; + PCIDevice *ehci, *uhci; + BusState *usbbus; + const char *name; + int i; + + switch (slot) { + case 0x1d: + name = "ich9-usb-ehci1"; + comp = ich9_1d; + break; + case 0x1a: + name = "ich9-usb-ehci2"; + comp = ich9_1a; + break; + default: + return -1; + } + + ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name); + qdev_init_nofail(&ehci->qdev); + usbbus = QLIST_FIRST(&ehci->qdev.child_bus); + + for (i = 0; i < 3; i++) { + uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func), + true, comp[i].name); + qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); + qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); + qdev_init_nofail(&uhci->qdev); + } + return 0; +} + /* PC hardware initialisation */ static void pc_q35_init(MachineState *machine) { @@ -236,13 +289,13 @@ static void pc_q35_init(MachineState *machine) /* init basic PC hardware */ pc_basic_device_init(isa_bus, pcms->gsi, &rtc_state, !mc->no_floppy, - (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit, + (pcms->vmport != ON_OFF_AUTO_ON), pcms->pit_enabled, 0xff0104); /* connect pm stuff to lpc */ ich9_lpc_pm_init(lpc, pc_machine_is_smm_enabled(pcms)); - if (pcms->sata) { + if (pcms->sata_enabled) { /* ahci and SATA device, for q35 1 ahci controller is built-in */ ahci = pci_create_simple_multifunction(host_bus, PCI_DEVFN(ICH9_SATA1_DEV, @@ -262,7 +315,7 @@ static void pc_q35_init(MachineState *machine) ehci_create_ich9_with_companions(host_bus, 0x1d); } - if (pcms->smbus) { + if (pcms->smbus_enabled) { /* TODO: Populate SPD eeprom data. */ smbus_eeprom_init(ich9_smb_init(host_bus, PCI_DEVFN(ICH9_SMB_DEV, ICH9_SMB_FUNC), @@ -276,8 +329,8 @@ static void pc_q35_init(MachineState *machine) pc_vga_init(isa_bus, host_bus); pc_nic_init(pcmc, isa_bus, host_bus); - if (pcms->acpi_nvdimm_state.is_enabled) { - nvdimm_init_acpi_state(&pcms->acpi_nvdimm_state, system_io, + if (machine->nvdimms_state->is_enabled) { + nvdimm_init_acpi_state(machine->nvdimms_state, system_io, pcms->fw_cfg, OBJECT(pcms)); } } @@ -304,6 +357,7 @@ static void pc_q35_machine_options(MachineClass *m) m->units_per_default_bus = 1; m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; + m->default_kernel_irqchip_split = true; m->no_floppy = 1; machine_class_allow_dynamic_sysbus_dev(m, TYPE_AMD_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); @@ -311,20 +365,46 @@ static void pc_q35_machine_options(MachineClass *m) m->max_cpus = 288; } -static void pc_q35_3_0_machine_options(MachineClass *m) +static void pc_q35_4_0_machine_options(MachineClass *m) { pc_q35_machine_options(m); m->alias = "q35"; } +DEFINE_Q35_MACHINE(v4_0, "pc-q35-4.0", NULL, + pc_q35_4_0_machine_options); + +static void pc_q35_3_1_machine_options(MachineClass *m) +{ + PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + + pc_q35_4_0_machine_options(m); + m->default_kernel_irqchip_split = false; + m->smbus_no_migration_support = true; + m->alias = NULL; + pcmc->pvh_enabled = false; + compat_props_add(m->compat_props, hw_compat_3_1, hw_compat_3_1_len); + compat_props_add(m->compat_props, pc_compat_3_1, pc_compat_3_1_len); +} + +DEFINE_Q35_MACHINE(v3_1, "pc-q35-3.1", NULL, + pc_q35_3_1_machine_options); + +static void pc_q35_3_0_machine_options(MachineClass *m) +{ + pc_q35_3_1_machine_options(m); + compat_props_add(m->compat_props, hw_compat_3_0, hw_compat_3_0_len); + compat_props_add(m->compat_props, pc_compat_3_0, pc_compat_3_0_len); +} + DEFINE_Q35_MACHINE(v3_0, "pc-q35-3.0", NULL, pc_q35_3_0_machine_options); static void pc_q35_2_12_machine_options(MachineClass *m) { pc_q35_3_0_machine_options(m); - m->alias = NULL; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_12); + compat_props_add(m->compat_props, hw_compat_2_12, hw_compat_2_12_len); + compat_props_add(m->compat_props, pc_compat_2_12, pc_compat_2_12_len); } DEFINE_Q35_MACHINE(v2_12, "pc-q35-2.12", NULL, @@ -336,7 +416,8 @@ static void pc_q35_2_11_machine_options(MachineClass *m) pc_q35_2_12_machine_options(m); pcmc->default_nic_model = "e1000"; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_11); + compat_props_add(m->compat_props, hw_compat_2_11, hw_compat_2_11_len); + compat_props_add(m->compat_props, pc_compat_2_11, pc_compat_2_11_len); } DEFINE_Q35_MACHINE(v2_11, "pc-q35-2.11", NULL, @@ -345,7 +426,8 @@ DEFINE_Q35_MACHINE(v2_11, "pc-q35-2.11", NULL, static void pc_q35_2_10_machine_options(MachineClass *m) { pc_q35_2_11_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_10); + compat_props_add(m->compat_props, hw_compat_2_10, hw_compat_2_10_len); + compat_props_add(m->compat_props, pc_compat_2_10, pc_compat_2_10_len); m->numa_auto_assign_ram = numa_legacy_auto_assign_ram; m->auto_enable_numa_with_memhp = false; } @@ -356,7 +438,8 @@ DEFINE_Q35_MACHINE(v2_10, "pc-q35-2.10", NULL, static void pc_q35_2_9_machine_options(MachineClass *m) { pc_q35_2_10_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_9); + compat_props_add(m->compat_props, hw_compat_2_9, hw_compat_2_9_len); + compat_props_add(m->compat_props, pc_compat_2_9, pc_compat_2_9_len); } DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL, @@ -365,7 +448,8 @@ DEFINE_Q35_MACHINE(v2_9, "pc-q35-2.9", NULL, static void pc_q35_2_8_machine_options(MachineClass *m) { pc_q35_2_9_machine_options(m); - SET_MACHINE_COMPAT(m, PC_COMPAT_2_8); + compat_props_add(m->compat_props, hw_compat_2_8, hw_compat_2_8_len); + compat_props_add(m->compat_props, pc_compat_2_8, pc_compat_2_8_len); } DEFINE_Q35_MACHINE(v2_8, "pc-q35-2.8", NULL, @@ -375,7 +459,8 @@ static void pc_q35_2_7_machine_options(MachineClass *m) { pc_q35_2_8_machine_options(m); m->max_cpus = 255; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_7); + compat_props_add(m->compat_props, hw_compat_2_7, hw_compat_2_7_len); + compat_props_add(m->compat_props, pc_compat_2_7, pc_compat_2_7_len); } DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, @@ -384,10 +469,12 @@ DEFINE_Q35_MACHINE(v2_7, "pc-q35-2.7", NULL, static void pc_q35_2_6_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_2_7_machine_options(m); pcmc->legacy_cpu_hotplug = true; pcmc->linuxboot_dma_enabled = false; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_6); + compat_props_add(m->compat_props, hw_compat_2_6, hw_compat_2_6_len); + compat_props_add(m->compat_props, pc_compat_2_6, pc_compat_2_6_len); } DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, @@ -396,10 +483,12 @@ DEFINE_Q35_MACHINE(v2_6, "pc-q35-2.6", NULL, static void pc_q35_2_5_machine_options(MachineClass *m) { PCMachineClass *pcmc = PC_MACHINE_CLASS(m); + pc_q35_2_6_machine_options(m); pcmc->save_tsc_khz = false; m->legacy_fw_cfg_order = 1; - SET_MACHINE_COMPAT(m, PC_COMPAT_2_5); + compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); + compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); } DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL, @@ -408,10 +497,12 @@ DEFINE_Q35_MACHINE(v2_5, "pc-q35-2.5", NULL, static void pc_q35_2_4_machine_options(MachineClass *m) { 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); + compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); + compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); } DEFINE_Q35_MACHINE(v2_4, "pc-q35-2.4", NULL, diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 091e22dd60..c628540774 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -40,10 +40,16 @@ #define BIOS_FILENAME "bios.bin" -typedef struct PcSysFwDevice { - SysBusDevice busdev; - uint8_t isapc_ram_fw; -} PcSysFwDevice; +/* + * We don't have a theoretically justifiable exact lower bound on the base + * address of any flash mapping. In practice, the IO-APIC MMIO range is + * [0xFEE00000..0xFEE01000] -- see IO_APIC_DEFAULT_ADDRESS --, leaving free + * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in + * size. + */ +#define FLASH_SIZE_LIMIT (8 * MiB) + +#define FLASH_SECTOR_SIZE 4096 static void pc_isa_bios_init(MemoryRegion *rom_memory, MemoryRegion *flash_mem, @@ -76,100 +82,118 @@ static void pc_isa_bios_init(MemoryRegion *rom_memory, memory_region_set_readonly(isa_bios, true); } -#define FLASH_MAP_UNIT_MAX 2 +static PFlashCFI01 *pc_pflash_create(PCMachineState *pcms, + const char *name, + const char *alias_prop_name) +{ + DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); -/* We don't have a theoretically justifiable exact lower bound on the base - * address of any flash mapping. In practice, the IO-APIC MMIO range is - * [0xFEE00000..0xFEE01000[ -- see IO_APIC_DEFAULT_ADDRESS --, leaving free - * only 18MB-4KB below 4G. For now, restrict the cumulative mapping to 8MB in - * size. - */ -#define FLASH_MAP_BASE_MIN ((hwaddr)(4 * GiB - 8 * MiB)) + qdev_prop_set_uint64(dev, "sector-length", FLASH_SECTOR_SIZE); + qdev_prop_set_uint8(dev, "width", 1); + qdev_prop_set_string(dev, "name", name); + object_property_add_child(OBJECT(pcms), name, OBJECT(dev), + &error_abort); + object_property_add_alias(OBJECT(pcms), alias_prop_name, + OBJECT(dev), "drive", &error_abort); + return PFLASH_CFI01(dev); +} -/* This function maps flash drives from 4G downward, in order of their unit - * numbers. The mapping starts at unit#0, with unit number increments of 1, and - * stops before the first missing flash drive, or before - * unit#FLASH_MAP_UNIT_MAX, whichever is reached first. +void pc_system_flash_create(PCMachineState *pcms) +{ + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + + if (pcmc->pci_enabled) { + pcms->flash[0] = pc_pflash_create(pcms, "system.flash0", + "pflash0"); + pcms->flash[1] = pc_pflash_create(pcms, "system.flash1", + "pflash1"); + } +} + +static void pc_system_flash_cleanup_unused(PCMachineState *pcms) +{ + char *prop_name; + int i; + Object *dev_obj; + + assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); + + for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + dev_obj = OBJECT(pcms->flash[i]); + if (!object_property_get_bool(dev_obj, "realized", &error_abort)) { + prop_name = g_strdup_printf("pflash%d", i); + object_property_del(OBJECT(pcms), prop_name, &error_abort); + g_free(prop_name); + object_unparent(dev_obj); + pcms->flash[i] = NULL; + } + } +} + +/* + * Map the pcms->flash[] from 4GiB downward, and realize. + * Map them in descending order, i.e. pcms->flash[0] at the top, + * without gaps. + * Stop at the first pcms->flash[0] lacking a block backend. + * Set each flash's size from its block backend. Fatal error if the + * size isn't a non-zero multiple of 4KiB, or the total size exceeds + * FLASH_SIZE_LIMIT. * - * Addressing within one flash drive is of course not reversed. - * - * An error message is printed and the process exits if: - * - the size of the backing file for a flash drive is non-positive, or not a - * multiple of the required sector size, or - * - the current mapping's base address would fall below FLASH_MAP_BASE_MIN. - * - * The drive with unit#0 (if available) is mapped at the highest address, and - * it is passed to pc_isa_bios_init(). Merging several drives for isa-bios is + * If pcms->flash[0] has a block backend, its memory is passed to + * pc_isa_bios_init(). Merging several flash devices for isa-bios is * not supported. */ -static void pc_system_flash_init(MemoryRegion *rom_memory) +static void pc_system_flash_map(PCMachineState *pcms, + MemoryRegion *rom_memory) { - int unit; - DriveInfo *pflash_drv; + hwaddr total_size = 0; + int i; BlockBackend *blk; int64_t size; - char *fatal_errmsg = NULL; - hwaddr phys_addr = 0x100000000ULL; - int sector_bits, sector_size; - pflash_t *system_flash; + PFlashCFI01 *system_flash; MemoryRegion *flash_mem; - char name[64]; void *flash_ptr; int ret, flash_size; - sector_bits = 12; - sector_size = 1 << sector_bits; + assert(PC_MACHINE_GET_CLASS(pcms)->pci_enabled); - for (unit = 0; - (unit < FLASH_MAP_UNIT_MAX && - (pflash_drv = drive_get(IF_PFLASH, 0, unit)) != NULL); - ++unit) { - blk = blk_by_legacy_dinfo(pflash_drv); + for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + system_flash = pcms->flash[i]; + blk = pflash_cfi01_get_blk(system_flash); + if (!blk) { + break; + } size = blk_getlength(blk); if (size < 0) { - fatal_errmsg = g_strdup_printf("failed to get backing file size"); - } else if (size == 0) { - fatal_errmsg = g_strdup_printf("PC system firmware (pflash) " - "cannot have zero size"); - } else if ((size % sector_size) != 0) { - fatal_errmsg = g_strdup_printf("PC system firmware (pflash) " - "must be a multiple of 0x%x", sector_size); - } else if (phys_addr < size || phys_addr - size < FLASH_MAP_BASE_MIN) { - fatal_errmsg = g_strdup_printf("oversized backing file, pflash " - "segments cannot be mapped under " - TARGET_FMT_plx, FLASH_MAP_BASE_MIN); + error_report("can't get size of block device %s: %s", + blk_name(blk), strerror(-size)); + exit(1); } - if (fatal_errmsg != NULL) { - Location loc; - - /* push a new, "none" location on the location stack; overwrite its - * contents with the location saved in the option; print the error - * (includes location); pop the top - */ - loc_push_none(&loc); - if (pflash_drv->opts != NULL) { - qemu_opts_loc_restore(pflash_drv->opts); - } - error_report("%s", fatal_errmsg); - loc_pop(&loc); - g_free(fatal_errmsg); + if (size == 0 || size % FLASH_SECTOR_SIZE != 0) { + error_report("system firmware block device %s has invalid size " + "%" PRId64, + blk_name(blk), size); + info_report("its size must be a non-zero multiple of 0x%x", + FLASH_SECTOR_SIZE); + exit(1); + } + if ((hwaddr)size != size + || total_size > HWADDR_MAX - size + || total_size + size > FLASH_SIZE_LIMIT) { + error_report("combined size of system firmware exceeds " + "%" PRIu64 " bytes", + FLASH_SIZE_LIMIT); exit(1); } - phys_addr -= size; + total_size += size; + qdev_prop_set_uint32(DEVICE(system_flash), "num-blocks", + size / FLASH_SECTOR_SIZE); + qdev_init_nofail(DEVICE(system_flash)); + sysbus_mmio_map(SYS_BUS_DEVICE(system_flash), 0, + 0x100000000ULL - total_size); - /* pflash_cfi01_register() creates a deep copy of the name */ - snprintf(name, sizeof name, "system.flash%d", unit); - system_flash = pflash_cfi01_register(phys_addr, NULL /* qdev */, name, - size, blk, sector_size, - size >> sector_bits, - 1 /* width */, - 0x0000 /* id0 */, - 0x0000 /* id1 */, - 0x0000 /* id2 */, - 0x0000 /* id3 */, - 0 /* be */); - if (unit == 0) { + if (i == 0) { flash_mem = pflash_cfi01_get_memory(system_flash); pc_isa_bios_init(rom_memory, flash_mem, size); @@ -240,24 +264,63 @@ static void old_pc_system_rom_init(MemoryRegion *rom_memory, bool isapc_ram_fw) bios); } -void pc_system_firmware_init(MemoryRegion *rom_memory, bool isapc_ram_fw) +void pc_system_firmware_init(PCMachineState *pcms, + MemoryRegion *rom_memory) { + PCMachineClass *pcmc = PC_MACHINE_GET_CLASS(pcms); + int i; DriveInfo *pflash_drv; + BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; + Location loc; - pflash_drv = drive_get(IF_PFLASH, 0, 0); - - if (isapc_ram_fw || pflash_drv == NULL) { - /* When a pflash drive is not found, use rom-mode */ - old_pc_system_rom_init(rom_memory, isapc_ram_fw); + if (!pcmc->pci_enabled) { + old_pc_system_rom_init(rom_memory, true); return; } - if (kvm_enabled() && !kvm_readonly_mem_enabled()) { - /* Older KVM cannot execute from device memory. So, flash memory - * cannot be used unless the readonly memory kvm capability is present. */ - fprintf(stderr, "qemu: pflash with kvm requires KVM readonly memory support\n"); - exit(1); + /* Map legacy -drive if=pflash to machine properties */ + for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + pflash_blk[i] = pflash_cfi01_get_blk(pcms->flash[i]); + pflash_drv = drive_get(IF_PFLASH, 0, i); + if (!pflash_drv) { + continue; + } + loc_push_none(&loc); + qemu_opts_loc_restore(pflash_drv->opts); + if (pflash_blk[i]) { + error_report("clashes with -machine"); + exit(1); + } + pflash_blk[i] = blk_by_legacy_dinfo(pflash_drv); + qdev_prop_set_drive(DEVICE(pcms->flash[i]), + "drive", pflash_blk[i], &error_fatal); + loc_pop(&loc); } - pc_system_flash_init(rom_memory); + /* Reject gaps */ + for (i = 1; i < ARRAY_SIZE(pcms->flash); i++) { + if (pflash_blk[i] && !pflash_blk[i - 1]) { + error_report("pflash%d requires pflash%d", i, i - 1); + exit(1); + } + } + + if (!pflash_blk[0]) { + /* Machine property pflash0 not set, use ROM mode */ + old_pc_system_rom_init(rom_memory, false); + } else { + if (kvm_enabled() && !kvm_readonly_mem_enabled()) { + /* + * Older KVM cannot execute from device memory. So, flash + * memory cannot be used unless the readonly memory kvm + * capability is present. + */ + error_report("pflash with kvm requires KVM readonly memory support"); + exit(1); + } + + pc_system_flash_map(pcms, rom_memory); + } + + pc_system_flash_cleanup_unused(pcms); } diff --git a/hw/i386/trace-events b/hw/i386/trace-events index e14d06ec83..c8bc464bc5 100644 --- a/hw/i386/trace-events +++ b/hw/i386/trace-events @@ -1,23 +1,19 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/i386/x86-iommu.c +# x86-iommu.c x86_iommu_iec_notify(bool global, uint32_t index, uint32_t mask) "Notify IEC invalidation: global=%d index=%" PRIu32 " mask=%" PRIu32 -# hw/i386/intel_iommu.c +# intel_iommu.c vtd_inv_desc(const char *type, uint64_t hi, uint64_t lo) "invalidate desc type %s high 0x%"PRIx64" low 0x%"PRIx64 -vtd_inv_desc_invalid(uint64_t hi, uint64_t lo) "invalid inv desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_cc_domain(uint16_t domain) "context invalidate domain 0x%"PRIx16 vtd_inv_desc_cc_global(void) "context invalidate globally" vtd_inv_desc_cc_device(uint8_t bus, uint8_t dev, uint8_t fn) "context invalidate device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_inv_desc_cc_devices(uint16_t sid, uint16_t fmask) "context invalidate devices sid 0x%"PRIx16" fmask 0x%"PRIx16 -vtd_inv_desc_cc_invalid(uint64_t hi, uint64_t lo) "invalid context-cache desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_iotlb_global(void) "iotlb invalidate global" vtd_inv_desc_iotlb_domain(uint16_t domain) "iotlb invalidate whole domain 0x%"PRIx16 vtd_inv_desc_iotlb_pages(uint16_t domain, uint64_t addr, uint8_t mask) "iotlb invalidate domain 0x%"PRIx16" addr 0x%"PRIx64" mask 0x%"PRIx8 -vtd_inv_desc_iotlb_invalid(uint64_t hi, uint64_t lo) "invalid iotlb desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_wait_sw(uint64_t addr, uint32_t data) "wait invalidate status write addr 0x%"PRIx64" data 0x%"PRIx32 vtd_inv_desc_wait_irq(const char *msg) "%s" -vtd_inv_desc_wait_invalid(uint64_t hi, uint64_t lo) "invalid wait desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_wait_write_fail(uint64_t hi, uint64_t lo) "write fail for wait desc hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_inv_desc_iec(uint32_t granularity, uint32_t index, uint32_t mask) "granularity 0x%"PRIx32" index 0x%"PRIx32" mask 0x%"PRIx32 vtd_inv_qi_enable(bool enable) "enabled %d" @@ -27,16 +23,14 @@ vtd_inv_qi_tail(uint16_t head) "write tail %d" vtd_inv_qi_fetch(void) "" vtd_context_cache_reset(void) "" vtd_re_not_present(uint8_t bus) "Root entry bus %"PRIu8" not present" -vtd_re_invalid(uint64_t hi, uint64_t lo) "invalid root entry hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_ce_not_present(uint8_t bus, uint8_t devfn) "Context entry bus %"PRIu8" devfn %"PRIu8" not present" -vtd_ce_invalid(uint64_t hi, uint64_t lo) "invalid context entry hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_iotlb_page_hit(uint16_t sid, uint64_t addr, uint64_t slpte, uint16_t domain) "IOTLB page hit sid 0x%"PRIx16" iova 0x%"PRIx64" slpte 0x%"PRIx64" domain 0x%"PRIx16 vtd_iotlb_page_update(uint16_t sid, uint64_t addr, uint64_t slpte, uint16_t domain) "IOTLB page update sid 0x%"PRIx16" iova 0x%"PRIx64" slpte 0x%"PRIx64" domain 0x%"PRIx16 vtd_iotlb_cc_hit(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32_t gen) "IOTLB context hit bus 0x%"PRIx8" devfn 0x%"PRIx8" high 0x%"PRIx64" low 0x%"PRIx64" gen %"PRIu32 vtd_iotlb_cc_update(uint8_t bus, uint8_t devfn, uint64_t high, uint64_t low, uint32_t gen1, uint32_t gen2) "IOTLB context update bus 0x%"PRIx8" devfn 0x%"PRIx8" high 0x%"PRIx64" low 0x%"PRIx64" gen %"PRIu32" -> gen %"PRIu32 vtd_iotlb_reset(const char *reason) "IOTLB reset (reason: %s)" vtd_fault_disabled(void) "Fault processing disabled for context entry" -vtd_replay_ce_valid(uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 +vtd_replay_ce_valid(const char *mode, uint8_t bus, uint8_t dev, uint8_t fn, uint16_t domain, uint64_t hi, uint64_t lo) "%s: replay valid context device %02"PRIx8":%02"PRIx8".%02"PRIx8" domain 0x%"PRIx16" hi 0x%"PRIx64" lo 0x%"PRIx64 vtd_replay_ce_invalid(uint8_t bus, uint8_t dev, uint8_t fn) "replay invalid context device %02"PRIx8":%02"PRIx8".%02"PRIx8 vtd_page_walk_level(uint64_t addr, uint32_t level, uint64_t start, uint64_t end) "walk (base=0x%"PRIx64", level=%"PRIu32") iova range 0x%"PRIx64" - 0x%"PRIx64 vtd_page_walk_one(uint16_t domain, uint64_t iova, uint64_t gpa, uint64_t mask, int perm) "domain 0x%"PRIu16" iova 0x%"PRIx64" -> gpa 0x%"PRIx64" mask 0x%"PRIx64" perm %d" @@ -51,7 +45,7 @@ vtd_pt_enable_fast_path(uint16_t sid, bool success) "sid 0x%"PRIu16" %d" vtd_irq_generate(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PRIx64 vtd_reg_read(uint64_t addr, uint64_t size) "addr 0x%"PRIx64" size 0x%"PRIx64 vtd_reg_write(uint64_t addr, uint64_t size, uint64_t val) "addr 0x%"PRIx64" size 0x%"PRIx64" value 0x%"PRIx64 -vtd_reg_dmar_root(uint64_t addr, bool extended) "addr 0x%"PRIx64" extended %d" +vtd_reg_dmar_root(uint64_t addr, bool scalable) "addr 0x%"PRIx64" scalable %d" vtd_reg_ir_root(uint64_t addr, uint32_t size) "addr 0x%"PRIx64" size 0x%"PRIx32 vtd_reg_write_gcmd(uint32_t status, uint32_t val) "status 0x%"PRIx32" value 0x%"PRIx32 vtd_reg_write_fectl(uint32_t value) "value 0x%"PRIx32 @@ -69,24 +63,11 @@ vtd_ir_remap_msi_req(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" data 0x%"PR vtd_fsts_ppf(bool set) "FSTS PPF bit set to %d" vtd_fsts_clear_ip(void) "" vtd_frr_new(int index, uint64_t hi, uint64_t lo) "index %d high 0x%"PRIx64" low 0x%"PRIx64 -vtd_err(const char *str) "%s" -vtd_err_dmar_iova_overflow(uint64_t iova) "iova 0x%"PRIx64 -vtd_err_dmar_slpte_read_error(uint64_t iova, int level) "iova 0x%"PRIx64" level %d" -vtd_err_dmar_slpte_perm_error(uint64_t iova, int level, uint64_t slpte, bool is_write) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64" write %d" -vtd_err_dmar_slpte_resv_error(uint64_t iova, int level, uint64_t slpte) "iova 0x%"PRIx64" level %d slpte 0x%"PRIx64 -vtd_err_dmar_translate(uint8_t bus, uint8_t slot, uint8_t func, uint64_t iova) "dev %02x:%02x.%02x iova 0x%"PRIx64 vtd_warn_invalid_qi_tail(uint16_t tail) "tail 0x%"PRIx16 -vtd_err_qi_disable(uint16_t head, uint16_t tail, int type) "head 0x%"PRIx16" tail 0x%"PRIx16" last_desc_type %d" -vtd_err_qi_tail(uint16_t tail, uint16_t size) "tail 0x%"PRIx16" size 0x%"PRIx16 -vtd_err_irte(int index, uint64_t lo, uint64_t hi) "index %d low 0x%"PRIx64" high 0x%"PRIx64 -vtd_err_irte_sid(int index, uint16_t req, uint16_t target) "index %d SVT_ALL sid 0x%"PRIx16" (should be: 0x%"PRIx16")" -vtd_err_irte_sid_bus(int index, uint8_t bus, uint8_t min, uint8_t max) "index %d SVT_BUS bus 0x%"PRIx8" (should be: 0x%"PRIx8"-0x%"PRIx8")" -vtd_err_irte_svt(int index, int type) "index %d SVT type %d" -vtd_err_ir_msi_invalid(uint16_t sid, uint64_t addr, uint64_t data) "sid 0x%"PRIx16" addr 0x%"PRIx64" data 0x%"PRIx64 vtd_warn_ir_vector(uint16_t sid, int index, int vec, int target) "sid 0x%"PRIx16" index %d vec %d (should be: %d)" vtd_warn_ir_trigger(uint16_t sid, int index, int trig, int target) "sid 0x%"PRIx16" index %d trigger %d (should be: %d)" -# hw/i386/amd_iommu.c +# amd_iommu.c amdvi_evntlog_fail(uint64_t addr, uint32_t head) "error: fail to write at addr 0x%"PRIx64" + offset 0x%"PRIx32 amdvi_cache_update(uint16_t domid, uint8_t bus, uint8_t slot, uint8_t func, uint64_t gpa, uint64_t txaddr) " update iotlb domid 0x%"PRIx16" devid: %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64 amdvi_completion_wait_fail(uint64_t addr) "error: fail to write at address 0x%"PRIx64 @@ -114,7 +95,19 @@ amdvi_mode_invalid(uint8_t level, uint64_t addr)"error: translation level 0x%"PR amdvi_page_fault(uint64_t addr) "error: page fault accessing guest physical address 0x%"PRIx64 amdvi_iotlb_hit(uint8_t bus, uint8_t slot, uint8_t func, uint64_t addr, uint64_t txaddr) "hit iotlb devid %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64 amdvi_translation_result(uint8_t bus, uint8_t slot, uint8_t func, uint64_t addr, uint64_t txaddr) "devid: %02x:%02x.%x gpa 0x%"PRIx64" hpa 0x%"PRIx64 +amdvi_mem_ir_write_req(uint64_t addr, uint64_t val, uint32_t size) "addr 0x%"PRIx64" data 0x%"PRIx64" size 0x%"PRIx32 +amdvi_mem_ir_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" data 0x%"PRIx64 +amdvi_ir_remap_msi_req(uint64_t addr, uint64_t data, uint8_t devid) "addr 0x%"PRIx64" data 0x%"PRIx64" devid 0x%"PRIx8 +amdvi_ir_remap_msi(uint64_t addr, uint64_t data, uint64_t addr2, uint64_t data2) "(addr 0x%"PRIx64", data 0x%"PRIx64") -> (addr 0x%"PRIx64", data 0x%"PRIx64")" +amdvi_err(const char *str) "%s" +amdvi_ir_irte(uint64_t addr, uint64_t data) "addr 0x%"PRIx64" offset 0x%"PRIx64 +amdvi_ir_irte_val(uint32_t data) "data 0x%"PRIx32 +amdvi_ir_err(const char *str) "%s" +amdvi_ir_intctl(uint8_t val) "int_ctl 0x%"PRIx8 +amdvi_ir_target_abort(const char *str) "%s" +amdvi_ir_delivery_mode(const char *str) "%s" +amdvi_ir_irte_ga_val(uint64_t hi, uint64_t lo) "hi 0x%"PRIx64" lo 0x%"PRIx64 -# hw/i386/vmport.c +# vmport.c vmport_register(unsigned char command, void *func, void *opaque) "command: 0x%02x func: %p opaque: %p" vmport_command(unsigned char command) "command: 0x%02x" diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 8a01a2dd25..d1534c1ae0 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -25,6 +25,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "trace.h" +#include "sysemu/kvm.h" void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, iec_notify_fn fn, void *data) @@ -52,6 +53,30 @@ void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global, } } +/* Generate one MSI message from VTDIrq info */ +void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *msg_out) +{ + X86IOMMU_MSIMessage msg = {}; + + /* Generate address bits */ + msg.dest_mode = irq->dest_mode; + msg.redir_hint = irq->redir_hint; + msg.dest = irq->dest; + msg.__addr_hi = irq->dest & 0xffffff00; + msg.__addr_head = cpu_to_le32(0xfee); + /* Keep this from original MSI address bits */ + msg.__not_used = irq->msi_addr_last_bits; + + /* Generate data bits */ + msg.vector = irq->vector; + msg.delivery_mode = irq->delivery_mode; + msg.level = 1; + msg.trigger_mode = irq->trigger_mode; + + msg_out->address = msg.msi_addr; + msg_out->data = msg.msi_data; +} + /* Default X86 IOMMU device */ static X86IOMMUState *x86_iommu_default = NULL; @@ -87,6 +112,7 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) PCMachineState *pcms = PC_MACHINE(object_dynamic_cast(OBJECT(ms), TYPE_PC_MACHINE)); QLIST_INIT(&x86_iommu->iec_notifiers); + bool irq_all_kernel = kvm_irqchip_in_kernel() && !kvm_irqchip_is_split(); if (!pcms || !pcms->bus) { error_setg(errp, "Machine-type '%s' not supported by IOMMU", @@ -94,6 +120,19 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) return; } + /* If the user didn't specify IR, choose a default value for it */ + if (x86_iommu->intr_supported == ON_OFF_AUTO_AUTO) { + x86_iommu->intr_supported = irq_all_kernel ? + ON_OFF_AUTO_OFF : ON_OFF_AUTO_ON; + } + + /* Both Intel and AMD IOMMU IR only support "kernel-irqchip={off|split}" */ + if (x86_iommu_ir_supported(x86_iommu) && irq_all_kernel) { + error_setg(errp, "Interrupt Remapping cannot work with " + "kernel-irqchip=on, please use 'split|off'."); + return; + } + if (x86_class->realize) { x86_class->realize(dev, errp); } @@ -102,7 +141,8 @@ static void x86_iommu_realize(DeviceState *dev, Error **errp) } static Property x86_iommu_properties[] = { - DEFINE_PROP_BOOL("intremap", X86IOMMUState, intr_supported, false), + DEFINE_PROP_ON_OFF_AUTO("intremap", X86IOMMUState, + intr_supported, ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("device-iotlb", X86IOMMUState, dt_supported, false), DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true), DEFINE_PROP_END_OF_LIST(), @@ -115,6 +155,11 @@ static void x86_iommu_class_init(ObjectClass *klass, void *data) dc->props = x86_iommu_properties; } +bool x86_iommu_ir_supported(X86IOMMUState *s) +{ + return s->intr_supported == ON_OFF_AUTO_ON; +} + static const TypeInfo x86_iommu_info = { .name = TYPE_X86_IOMMU_DEVICE, .parent = TYPE_SYS_BUS_DEVICE, diff --git a/hw/i386/xen/trace-events b/hw/i386/xen/trace-events index 8a9077cd4e..ca3a4948ba 100644 --- a/hw/i386/xen/trace-events +++ b/hw/i386/xen/trace-events @@ -1,7 +1,9 @@ -# hw/i386/xen/xen_platform.c +# See docs/devel/tracing.txt for syntax documentation. + +# xen_platform.c xen_platform_log(char *s) "xen platform: %s" -# hw/i386/xen/xen_pvdevice.c +# xen_pvdevice.c xen_pv_mmio_read(uint64_t addr) "WARNING: read from Xen PV Device MMIO space (address 0x%"PRIx64")" xen_pv_mmio_write(uint64_t addr) "WARNING: write to Xen PV Device MMIO space (address 0x%"PRIx64")" diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index 935a3676c8..2939122e7c 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -16,7 +16,8 @@ #include "hw/i386/pc.h" #include "hw/i386/apic-msidef.h" #include "hw/xen/xen_common.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus.h" #include "qapi/error.h" #include "qapi/qapi-commands-misc.h" #include "qemu/error-report.h" @@ -570,7 +571,7 @@ static void xen_io_del(MemoryListener *listener, } static void xen_device_realize(DeviceListener *listener, - DeviceState *dev) + DeviceState *dev) { XenIOState *state = container_of(listener, XenIOState, device_listener); @@ -588,7 +589,7 @@ static void xen_device_realize(DeviceListener *listener, } static void xen_device_unrealize(DeviceListener *listener, - DeviceState *dev) + DeviceState *dev) { XenIOState *state = container_of(listener, XenIOState, device_listener); @@ -1405,6 +1406,11 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) state->wakeup.notify = xen_wakeup_notifier; qemu_register_wakeup_notifier(&state->wakeup); + /* + * Register wake-up support in QMP query-current-machine API + */ + qemu_register_wakeup_support(); + rc = xen_map_ioreq_server(state); if (rc < 0) { goto err; @@ -1479,6 +1485,8 @@ void xen_hvm_init(PCMachineState *pcms, MemoryRegion **ram_memory) QLIST_INIT(&state->dev_list); device_listener_register(&state->device_listener); + xen_bus_init(); + /* Initialize backend core & drivers */ if (xen_be_init() != 0) { error_report("xen backend core setup failed"); diff --git a/hw/i386/xen/xen-mapcache.c b/hw/i386/xen/xen-mapcache.c index 4e4f069a24..254759f776 100644 --- a/hw/i386/xen/xen-mapcache.c +++ b/hw/i386/xen/xen-mapcache.c @@ -14,7 +14,7 @@ #include -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "qemu/bitmap.h" #include @@ -71,7 +71,7 @@ typedef struct MapCacheRev { typedef struct MapCache { MapCacheEntry *entry; unsigned long nr_buckets; - QTAILQ_HEAD(map_cache_head, MapCacheRev) locked_entries; + QTAILQ_HEAD(, MapCacheRev) locked_entries; /* For most cases (>99.9%), the page address is the same. */ MapCacheEntry *last_entry; @@ -184,9 +184,14 @@ static void xen_remap_bucket(MapCacheEntry *entry, pfns[i] = (address_index << (MCACHE_BUCKET_SHIFT-XC_PAGE_SHIFT)) + i; } + /* + * If the caller has requested the mapping at a specific address use + * MAP_FIXED to make sure it's honored. + */ if (!dummy) { vaddr_base = xenforeignmemory_map2(xen_fmem, xen_domid, vaddr, - PROT_READ | PROT_WRITE, 0, + PROT_READ | PROT_WRITE, + vaddr ? MAP_FIXED : 0, nb_pfn, pfns, err); if (vaddr_base == NULL) { perror("xenforeignmemory_map2"); @@ -198,7 +203,8 @@ static void xen_remap_bucket(MapCacheEntry *entry, * mapping immediately due to certain circumstances (i.e. on resume now) */ vaddr_base = mmap(vaddr, size, PROT_READ | PROT_WRITE, - MAP_ANON | MAP_SHARED, -1, 0); + MAP_ANON | MAP_SHARED | (vaddr ? MAP_FIXED : 0), + -1, 0); if (vaddr_base == MAP_FAILED) { perror("mmap"); exit(-1); diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index deb7a0c374..16afb54fee 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -30,7 +30,7 @@ #include "hw/pci/pci.h" #include "hw/irq.h" #include "hw/xen/xen_common.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "trace.h" #include "exec/address-spaces.h" #include "sysemu/block-backend.h" diff --git a/hw/ide/Kconfig b/hw/ide/Kconfig new file mode 100644 index 0000000000..ab47b6a7a3 --- /dev/null +++ b/hw/ide/Kconfig @@ -0,0 +1,54 @@ +config IDE_CORE + bool + +config IDE_QDEV + bool + select IDE_CORE + +config IDE_PCI + bool + depends on PCI + select IDE_CORE + +config IDE_ISA + bool + depends on ISA_BUS + select IDE_QDEV + +config IDE_PIIX + bool + select IDE_PCI + select IDE_QDEV + +config IDE_CMD646 + bool + select IDE_PCI + select IDE_QDEV + +config IDE_MACIO + bool + select IDE_QDEV + +config IDE_MMIO + bool + select IDE_QDEV + +config IDE_VIA + bool + select IDE_PCI + select IDE_QDEV + +config MICRODRIVE + bool + select IDE_QDEV + +config AHCI + bool + default y if PCI_DEVICES + depends on PCI + select IDE_QDEV + +config IDE_SII3112 + bool + select IDE_PCI + select IDE_QDEV diff --git a/hw/ide/Makefile.objs b/hw/ide/Makefile.objs index fc328ffbe8..a142add90e 100644 --- a/hw/ide/Makefile.objs +++ b/hw/ide/Makefile.objs @@ -1,8 +1,8 @@ common-obj-$(CONFIG_IDE_CORE) += core.o atapi.o common-obj-$(CONFIG_IDE_QDEV) += qdev.o common-obj-$(CONFIG_IDE_PCI) += pci.o -common-obj-$(CONFIG_IDE_ISA) += isa.o -common-obj-$(CONFIG_IDE_PIIX) += piix.o +common-obj-$(CONFIG_IDE_ISA) += isa.o ioport.o +common-obj-$(CONFIG_IDE_PIIX) += piix.o ioport.o common-obj-$(CONFIG_IDE_CMD646) += cmd646.o common-obj-$(CONFIG_IDE_MACIO) += macio.o common-obj-$(CONFIG_IDE_MMIO) += mmio.o diff --git a/hw/ide/atapi.c b/hw/ide/atapi.c index 39e473f9c2..1b0f66cc08 100644 --- a/hw/ide/atapi.c +++ b/hw/ide/atapi.c @@ -174,16 +174,15 @@ static void cd_read_sector_cb(void *opaque, int ret) static int cd_read_sector(IDEState *s) { + void *buf; + 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 = ATAPI_SECTOR_SIZE; - qemu_iovec_init_external(&s->qiov, &s->iov, 1); + buf = (s->cd_sector_size == 2352) ? s->io_buffer + 16 : s->io_buffer; + qemu_iovec_init_buf(&s->qiov, buf, ATAPI_SECTOR_SIZE); trace_cd_read_sector(s->lba); @@ -421,9 +420,8 @@ static void ide_atapi_cmd_read_dma_cb(void *opaque, int ret) data_offset = 0; } trace_ide_atapi_cmd_read_dma_cb_aio(s, s->lba, n); - s->bus->dma->iov.iov_base = (void *)(s->io_buffer + data_offset); - s->bus->dma->iov.iov_len = n * ATAPI_SECTOR_SIZE; - qemu_iovec_init_external(&s->bus->dma->qiov, &s->bus->dma->iov, 1); + qemu_iovec_init_buf(&s->bus->dma->qiov, s->io_buffer + data_offset, + n * ATAPI_SECTOR_SIZE); s->bus->dma->aiocb = ide_buffered_readv(s, (int64_t)s->lba << 2, &s->bus->dma->qiov, n * 4, diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 6bb92d717f..5a5679134a 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -50,86 +50,6 @@ static void cmd646_update_irq(PCIDevice *pd); -static uint64_t cmd646_cmd_read(void *opaque, hwaddr addr, - unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (addr != 2 || size != 1) { - return ((uint64_t)1 << (size * 8)) - 1; - } - return ide_status_read(cmd646bar->bus, addr + 2); -} - -static void cmd646_cmd_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (addr != 2 || size != 1) { - return; - } - ide_cmd_write(cmd646bar->bus, addr + 2, data); -} - -static const MemoryRegionOps cmd646_cmd_ops = { - .read = cmd646_cmd_read, - .write = cmd646_cmd_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static uint64_t cmd646_data_read(void *opaque, hwaddr addr, - unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (size == 1) { - return ide_ioport_read(cmd646bar->bus, addr); - } else if (addr == 0) { - if (size == 2) { - return ide_data_readw(cmd646bar->bus, addr); - } else { - return ide_data_readl(cmd646bar->bus, addr); - } - } - return ((uint64_t)1 << (size * 8)) - 1; -} - -static void cmd646_data_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - CMD646BAR *cmd646bar = opaque; - - if (size == 1) { - ide_ioport_write(cmd646bar->bus, addr, data); - } else if (addr == 0) { - if (size == 2) { - ide_data_writew(cmd646bar->bus, addr, data); - } else { - ide_data_writel(cmd646bar->bus, addr, data); - } - } -} - -static const MemoryRegionOps cmd646_data_ops = { - .read = cmd646_data_read, - .write = cmd646_data_write, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static void setup_cmd646_bar(PCIIDEState *d, int bus_num) -{ - IDEBus *bus = &d->bus[bus_num]; - CMD646BAR *bar = &d->cmd646_bar[bus_num]; - - bar->bus = bus; - bar->pci_dev = d; - memory_region_init_io(&bar->cmd, OBJECT(d), &cmd646_cmd_ops, bar, - "cmd646-cmd", 4); - memory_region_init_io(&bar->data, OBJECT(d), &cmd646_data_ops, bar, - "cmd646-data", 8); -} - static void cmd646_update_dma_interrupts(PCIDevice *pd) { /* Sync DMA interrupt status from UDMA interrupt status */ @@ -346,12 +266,22 @@ static void pci_cmd646_ide_realize(PCIDevice *dev, Error **errp) dev->wmask[MRDMODE] = 0x0; dev->w1cmask[MRDMODE] = MRDMODE_INTR_CH0 | MRDMODE_INTR_CH1; - setup_cmd646_bar(d, 0); - setup_cmd646_bar(d, 1); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].data); - pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[0].cmd); - pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].data); - pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd646_bar[1].cmd); + memory_region_init_io(&d->data_bar[0], OBJECT(d), &pci_ide_data_le_ops, + &d->bus[0], "cmd646-data0", 8); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->data_bar[0]); + + memory_region_init_io(&d->cmd_bar[0], OBJECT(d), &pci_ide_cmd_le_ops, + &d->bus[0], "cmd646-cmd0", 4); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd_bar[0]); + + memory_region_init_io(&d->data_bar[1], OBJECT(d), &pci_ide_data_le_ops, + &d->bus[1], "cmd646-data1", 8); + pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->data_bar[1]); + + memory_region_init_io(&d->cmd_bar[1], OBJECT(d), &pci_ide_cmd_le_ops, + &d->bus[1], "cmd646-cmd1", 4); + pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd_bar[1]); + bmdma_setup_bar(d); pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); diff --git a/hw/ide/core.c b/hw/ide/core.c index b37d4f6414..5e3dcdb298 100644 --- a/hw/ide/core.c +++ b/hw/ide/core.c @@ -35,6 +35,7 @@ #include "sysemu/block-backend.h" #include "qapi/error.h" #include "qemu/cutils.h" +#include "sysemu/replay.h" #include "hw/ide/internal.h" #include "trace.h" @@ -487,7 +488,7 @@ static void ide_issue_trim_cb(void *opaque, int ret) done: iocb->aiocb = NULL; if (iocb->bh) { - qemu_bh_schedule(iocb->bh); + replay_bh_schedule_event(iocb->bh); } } @@ -582,16 +583,16 @@ int64_t ide_get_sector(IDEState *s) int64_t sector_num; if (s->select & 0x40) { /* lba */ - if (!s->lba48) { - sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | - (s->lcyl << 8) | s->sector; - } else { - sector_num = ((int64_t)s->hob_hcyl << 40) | - ((int64_t) s->hob_lcyl << 32) | - ((int64_t) s->hob_sector << 24) | - ((int64_t) s->hcyl << 16) | - ((int64_t) s->lcyl << 8) | s->sector; - } + if (!s->lba48) { + sector_num = ((s->select & 0x0f) << 24) | (s->hcyl << 16) | + (s->lcyl << 8) | s->sector; + } else { + sector_num = ((int64_t)s->hob_hcyl << 40) | + ((int64_t) s->hob_lcyl << 32) | + ((int64_t) s->hob_sector << 24) | + ((int64_t) s->hcyl << 16) | + ((int64_t) s->lcyl << 8) | s->sector; + } } else { sector_num = ((s->hcyl << 8) | s->lcyl) * s->heads * s->sectors + (s->select & 0x0f) * s->sectors + (s->sector - 1); @@ -603,19 +604,19 @@ void ide_set_sector(IDEState *s, int64_t sector_num) { unsigned int cyl, r; if (s->select & 0x40) { - if (!s->lba48) { + if (!s->lba48) { s->select = (s->select & 0xf0) | (sector_num >> 24); s->hcyl = (sector_num >> 16); s->lcyl = (sector_num >> 8); s->sector = (sector_num); - } else { - s->sector = sector_num; - s->lcyl = sector_num >> 8; - s->hcyl = sector_num >> 16; - s->hob_sector = sector_num >> 24; - s->hob_lcyl = sector_num >> 32; - s->hob_hcyl = sector_num >> 40; - } + } else { + s->sector = sector_num; + s->lcyl = sector_num >> 8; + s->hcyl = sector_num >> 16; + s->hob_sector = sector_num >> 24; + s->hob_lcyl = sector_num >> 32; + s->hob_hcyl = sector_num >> 40; + } } else { cyl = sector_num / (s->heads * s->sectors); r = sector_num % (s->heads * s->sectors); @@ -636,13 +637,15 @@ 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, + assert(req->qiov.size == req->original_qiov->size); + qemu_iovec_from_buf(req->original_qiov, 0, + req->qiov.local_iov.iov_base, req->original_qiov->size); } req->original_cb(req->original_opaque, ret); } QLIST_REMOVE(req, list); - qemu_vfree(req->iov.iov_base); + qemu_vfree(qemu_iovec_buf(&req->qiov)); g_free(req); } @@ -667,9 +670,8 @@ BlockAIOCB *ide_buffered_readv(IDEState *s, int64_t sector_num, 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); + qemu_iovec_init_buf(&req->qiov, blk_blockalign(s->blk, iov->size), + iov->size); aioreq = blk_aio_preadv(s->blk, sector_num << BDRV_SECTOR_BITS, &req->qiov, 0, ide_buffered_readv_cb, req); @@ -781,9 +783,7 @@ static void ide_sector_read(IDEState *s) return; } - s->iov.iov_base = s->io_buffer; - s->iov.iov_len = n * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&s->qiov, &s->iov, 1); + qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE); block_acct_start(blk_get_stats(s->blk), &s->acct, n * BDRV_SECTOR_SIZE, BLOCK_ACCT_READ); @@ -1052,9 +1052,7 @@ static void ide_sector_write(IDEState *s) return; } - s->iov.iov_base = s->io_buffer; - s->iov.iov_len = n * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&s->qiov, &s->iov, 1); + qemu_iovec_init_buf(&s->qiov, s->io_buffer, n * BDRV_SECTOR_SIZE); block_acct_start(blk_get_stats(s->blk), &s->acct, n * BDRV_SECTOR_SIZE, BLOCK_ACCT_WRITE); @@ -1195,17 +1193,17 @@ static void ide_cmd_lba48_transform(IDEState *s, int lba48) * full sector count in ->nsector and ignore ->hob_nsector from now */ if (!s->lba48) { - if (!s->nsector) - s->nsector = 256; + if (!s->nsector) + s->nsector = 256; } else { - if (!s->nsector && !s->hob_nsector) - s->nsector = 65536; - else { - int lo = s->nsector; - int hi = s->hob_nsector; + if (!s->nsector && !s->hob_nsector) + s->nsector = 65536; + else { + int lo = s->nsector; + int hi = s->hob_nsector; - s->nsector = (hi << 8) | lo; - } + s->nsector = (hi << 8) | lo; + } } } @@ -1265,35 +1263,35 @@ void ide_ioport_write(void *opaque, uint32_t addr, uint32_t val) bus->ifs[1].feature = val; break; case ATA_IOPORT_WR_SECTOR_COUNT: - ide_clear_hob(bus); - bus->ifs[0].hob_nsector = bus->ifs[0].nsector; - bus->ifs[1].hob_nsector = bus->ifs[1].nsector; + ide_clear_hob(bus); + bus->ifs[0].hob_nsector = bus->ifs[0].nsector; + bus->ifs[1].hob_nsector = bus->ifs[1].nsector; bus->ifs[0].nsector = val; bus->ifs[1].nsector = val; break; case ATA_IOPORT_WR_SECTOR_NUMBER: - ide_clear_hob(bus); - bus->ifs[0].hob_sector = bus->ifs[0].sector; - bus->ifs[1].hob_sector = bus->ifs[1].sector; + ide_clear_hob(bus); + bus->ifs[0].hob_sector = bus->ifs[0].sector; + bus->ifs[1].hob_sector = bus->ifs[1].sector; bus->ifs[0].sector = val; bus->ifs[1].sector = val; break; case ATA_IOPORT_WR_CYLINDER_LOW: - ide_clear_hob(bus); - bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl; - bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl; + ide_clear_hob(bus); + bus->ifs[0].hob_lcyl = bus->ifs[0].lcyl; + bus->ifs[1].hob_lcyl = bus->ifs[1].lcyl; bus->ifs[0].lcyl = val; bus->ifs[1].lcyl = val; break; case ATA_IOPORT_WR_CYLINDER_HIGH: - ide_clear_hob(bus); - bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl; - bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl; + ide_clear_hob(bus); + bus->ifs[0].hob_hcyl = bus->ifs[0].hcyl; + bus->ifs[1].hob_hcyl = bus->ifs[1].hcyl; bus->ifs[0].hcyl = val; bus->ifs[1].hcyl = val; break; case ATA_IOPORT_WR_DEVICE_HEAD: - /* FIXME: HOB readback uses bit 7 */ + /* FIXME: HOB readback uses bit 7 */ bus->ifs[0].select = (val & ~0x10) | 0xa0; bus->ifs[1].select = (val | 0x10) | 0xa0; /* select drive */ @@ -2163,7 +2161,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) } else if (!hob) { ret = s->error; } else { - ret = s->hob_feature; + ret = s->hob_feature; } break; case ATA_IOPORT_RR_SECTOR_COUNT: @@ -2172,7 +2170,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) } else if (!hob) { ret = s->nsector & 0xff; } else { - ret = s->hob_nsector; + ret = s->hob_nsector; } break; case ATA_IOPORT_RR_SECTOR_NUMBER: @@ -2181,7 +2179,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) } else if (!hob) { ret = s->sector; } else { - ret = s->hob_sector; + ret = s->hob_sector; } break; case ATA_IOPORT_RR_CYLINDER_LOW: @@ -2190,7 +2188,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) } else if (!hob) { ret = s->lcyl; } else { - ret = s->hob_lcyl; + ret = s->hob_lcyl; } break; case ATA_IOPORT_RR_CYLINDER_HIGH: @@ -2199,7 +2197,7 @@ uint32_t ide_ioport_read(void *opaque, uint32_t addr) } else if (!hob) { ret = s->hcyl; } else { - ret = s->hob_hcyl; + ret = s->hob_hcyl; } break; case ATA_IOPORT_RR_DEVICE_HEAD: @@ -2715,31 +2713,6 @@ void ide_exit(IDEState *s) qemu_vfree(s->io_buffer); } -static const MemoryRegionPortio ide_portio_list[] = { - { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, - { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew }, - { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel }, - PORTIO_END_OF_LIST(), -}; - -static const MemoryRegionPortio ide_portio2_list[] = { - { 0, 1, 1, .read = ide_status_read, .write = ide_cmd_write }, - PORTIO_END_OF_LIST(), -}; - -void ide_init_ioport(IDEBus *bus, ISADevice *dev, int iobase, int iobase2) -{ - /* ??? Assume only ISA and PCI configurations, and that the PCI-ISA - bridge has been setup properly to always register with ISA. */ - isa_register_portio_list(dev, &bus->portio_list, - iobase, ide_portio_list, bus, "ide"); - - if (iobase2) { - isa_register_portio_list(dev, &bus->portio2_list, - iobase2, ide_portio2_list, bus, "ide"); - } -} - static bool is_identify_set(void *opaque, int version_id) { IDEState *s = opaque; @@ -2877,7 +2850,7 @@ static const VMStateDescription vmstate_ide_drive_pio_state = { .fields = (VMStateField[]) { VMSTATE_INT32(req_nb_sectors, IDEState), VMSTATE_VARRAY_INT32(io_buffer, IDEState, io_buffer_total_len, 1, - vmstate_info_uint8, uint8_t), + vmstate_info_uint8, uint8_t), VMSTATE_INT32(cur_io_buffer_offset, IDEState), VMSTATE_INT32(cur_io_buffer_len, IDEState), VMSTATE_UINT8(end_transfer_fn_idx, IDEState), diff --git a/hw/ide/ioport.c b/hw/ide/ioport.c new file mode 100644 index 0000000000..a0b3c1f6a1 --- /dev/null +++ b/hw/ide/ioport.c @@ -0,0 +1,66 @@ +/* + * QEMU IDE disk and CD/DVD-ROM Emulator + * + * Copyright (c) 2003 Fabrice Bellard + * Copyright (c) 2006 Openedhand Ltd. + * + * 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/isa/isa.h" +#include "qemu/error-report.h" +#include "qemu/timer.h" +#include "sysemu/sysemu.h" +#include "sysemu/blockdev.h" +#include "sysemu/dma.h" +#include "hw/block/block.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "qemu/cutils.h" +#include "sysemu/replay.h" + +#include "hw/ide/internal.h" +#include "trace.h" + +static const MemoryRegionPortio ide_portio_list[] = { + { 0, 8, 1, .read = ide_ioport_read, .write = ide_ioport_write }, + { 0, 1, 2, .read = ide_data_readw, .write = ide_data_writew }, + { 0, 1, 4, .read = ide_data_readl, .write = ide_data_writel }, + PORTIO_END_OF_LIST(), +}; + +static const MemoryRegionPortio ide_portio2_list[] = { + { 0, 1, 1, .read = ide_status_read, .write = ide_cmd_write }, + PORTIO_END_OF_LIST(), +}; + +void ide_init_ioport(IDEBus *bus, ISADevice *dev, int iobase, int iobase2) +{ + /* ??? Assume only ISA and PCI configurations, and that the PCI-ISA + bridge has been setup properly to always register with ISA. */ + isa_register_portio_list(dev, &bus->portio_list, + iobase, ide_portio_list, bus, "ide"); + + if (iobase2) { + isa_register_portio_list(dev, &bus->portio2_list, + iobase2, ide_portio2_list, bus, "ide"); + } +} diff --git a/hw/ide/macio.c b/hw/ide/macio.c index d3a85cba3b..bab8c45a43 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -26,6 +26,7 @@ #include "hw/hw.h" #include "hw/ppc/mac.h" #include "hw/ppc/mac_dbdma.h" +#include "hw/misc/macio/macio.h" #include "sysemu/block-backend.h" #include "sysemu/dma.h" @@ -460,6 +461,7 @@ static void macio_ide_initfn(Object *obj) static Property macio_ide_properties[] = { DEFINE_PROP_UINT32("channel", MACIOIDEState, channel, 0), + DEFINE_PROP_UINT32("addr", MACIOIDEState, addr, -1), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/ide/pci.c b/hw/ide/pci.c index fe1ceeb0cd..942613a9a9 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -25,7 +25,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" -#include "hw/isa/isa.h" #include "sysemu/dma.h" #include "qemu/error-report.h" #include "hw/ide/pci.h" @@ -37,6 +36,71 @@ (IDE_RETRY_DMA | IDE_RETRY_PIO | \ IDE_RETRY_READ | IDE_RETRY_FLUSH) +static uint64_t pci_ide_cmd_read(void *opaque, hwaddr addr, unsigned size) +{ + IDEBus *bus = opaque; + + if (addr != 2 || size != 1) { + return ((uint64_t)1 << (size * 8)) - 1; + } + return ide_status_read(bus, addr + 2); +} + +static void pci_ide_cmd_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + IDEBus *bus = opaque; + + if (addr != 2 || size != 1) { + return; + } + ide_cmd_write(bus, addr + 2, data); +} + +const MemoryRegionOps pci_ide_cmd_le_ops = { + .read = pci_ide_cmd_read, + .write = pci_ide_cmd_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static uint64_t pci_ide_data_read(void *opaque, hwaddr addr, unsigned size) +{ + IDEBus *bus = opaque; + + if (size == 1) { + return ide_ioport_read(bus, addr); + } else if (addr == 0) { + if (size == 2) { + return ide_data_readw(bus, addr); + } else { + return ide_data_readl(bus, addr); + } + } + return ((uint64_t)1 << (size * 8)) - 1; +} + +static void pci_ide_data_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + IDEBus *bus = opaque; + + if (size == 1) { + ide_ioport_write(bus, addr, data); + } else if (addr == 0) { + if (size == 2) { + ide_data_writew(bus, addr, data); + } else { + ide_data_writel(bus, addr, data); + } + } +} + +const MemoryRegionOps pci_ide_data_le_ops = { + .read = pci_ide_data_read, + .write = pci_ide_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + static void bmdma_start_dma(IDEDMA *dma, IDEState *s, BlockCompletionFunc *dma_cb) { diff --git a/hw/ide/piix.c b/hw/ide/piix.c index a3afe1fd29..885c16e938 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" -#include "hw/isa/isa.h" #include "sysemu/block-backend.h" #include "sysemu/sysemu.h" #include "sysemu/blockdev.h" diff --git a/hw/ide/qdev.c b/hw/ide/qdev.c index 11dd6ee611..a75e49bc2a 100644 --- a/hw/ide/qdev.c +++ b/hw/ide/qdev.c @@ -190,7 +190,6 @@ static void ide_dev_initfn(IDEDevice *dev, IDEDriveKind kind, Error **errp) blkconf_locked(&dev->conf, &dev->locked); - blkconf_serial(&dev->conf, &dev->serial); if (kind != IDE_CD) { if (!blkconf_geometry(&dev->conf, &dev->chs_trans, 65535, 16, 255, errp)) { diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index 743a50ed51..59db09cfe4 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -88,35 +88,19 @@ static uint64_t sii3112_reg_read(void *opaque, hwaddr addr, val |= (uint32_t)d->i.bmdma[1].status << 16; break; case 0x80 ... 0x87: - if (size == 1) { - val = ide_ioport_read(&d->i.bus[0], addr - 0x80); - } else if (addr == 0x80) { - val = (size == 2) ? ide_data_readw(&d->i.bus[0], 0) : - ide_data_readl(&d->i.bus[0], 0); - } else { - val = (1ULL << (size * 8)) - 1; - } + val = pci_ide_data_le_ops.read(&d->i.bus[0], addr - 0x80, size); break; case 0x8a: - val = (size == 1) ? ide_status_read(&d->i.bus[0], 4) : - (1ULL << (size * 8)) - 1; + val = pci_ide_cmd_le_ops.read(&d->i.bus[0], 2, size); break; case 0xa0: val = d->regs[0].confstat; break; case 0xc0 ... 0xc7: - if (size == 1) { - val = ide_ioport_read(&d->i.bus[1], addr - 0xc0); - } else if (addr == 0xc0) { - val = (size == 2) ? ide_data_readw(&d->i.bus[1], 0) : - ide_data_readl(&d->i.bus[1], 0); - } else { - val = (1ULL << (size * 8)) - 1; - } + val = pci_ide_data_le_ops.read(&d->i.bus[1], addr - 0xc0, size); break; case 0xca: - val = (size == 1) ? ide_status_read(&d->i.bus[0], 4) : - (1ULL << (size * 8)) - 1; + val = pci_ide_cmd_le_ops.read(&d->i.bus[1], 2, size); break; case 0xe0: val = d->regs[1].confstat; @@ -186,36 +170,16 @@ static void sii3112_reg_write(void *opaque, hwaddr addr, bmdma_addr_ioport_ops.write(&d->i.bmdma[1], addr - 12, val, size); break; case 0x80 ... 0x87: - if (size == 1) { - ide_ioport_write(&d->i.bus[0], addr - 0x80, val); - } else if (addr == 0x80) { - if (size == 2) { - ide_data_writew(&d->i.bus[0], 0, val); - } else { - ide_data_writel(&d->i.bus[0], 0, val); - } - } + pci_ide_data_le_ops.write(&d->i.bus[0], addr - 0x80, val, size); break; case 0x8a: - if (size == 1) { - ide_cmd_write(&d->i.bus[0], 4, val); - } + pci_ide_cmd_le_ops.write(&d->i.bus[0], 2, val, size); break; case 0xc0 ... 0xc7: - if (size == 1) { - ide_ioport_write(&d->i.bus[1], addr - 0xc0, val); - } else if (addr == 0xc0) { - if (size == 2) { - ide_data_writew(&d->i.bus[1], 0, val); - } else { - ide_data_writel(&d->i.bus[1], 0, val); - } - } + pci_ide_data_le_ops.write(&d->i.bus[1], addr - 0xc0, val, size); break; case 0xca: - if (size == 1) { - ide_cmd_write(&d->i.bus[1], 4, val); - } + pci_ide_cmd_le_ops.write(&d->i.bus[1], 2, val, size); break; case 0x100: d->regs[0].scontrol = val & 0xfff; diff --git a/hw/ide/trace-events b/hw/ide/trace-events index 65d6f9034d..2e4162629f 100644 --- a/hw/ide/trace-events +++ b/hw/ide/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/ide/core.c +# core.c # portio ide_ioport_read(uint32_t addr, const char *reg, uint32_t val, void *bus, void *s) "IDE PIO rd @ 0x%"PRIx32" (%s); val 0x%02"PRIx32"; bus %p IDEState %p" ide_ioport_write(uint32_t addr, const char *reg, uint32_t val, void *bus, void *s) "IDE PIO wr @ 0x%"PRIx32" (%s); val 0x%02"PRIx32"; bus %p IDEState %p" @@ -23,30 +23,30 @@ ide_dma_cb(void *s, int64_t sector_num, int n, const char *dma) "IDEState %p; se # BMDMA HBAs: -# hw/ide/cmd646.c +# cmd646.c bmdma_read_cmd646(uint64_t addr, uint32_t val) "bmdma: readb 0x%"PRIx64" : 0x%02x" bmdma_write_cmd646(uint64_t addr, uint64_t val) "bmdma: writeb 0x%"PRIx64" : 0x%02"PRIx64 -# hw/ide/pci.c +# pci.c bmdma_reset(void) "" bmdma_cmd_writeb(uint32_t val) "val: 0x%08x" bmdma_addr_read(uint64_t data) "data: 0x%016"PRIx64 bmdma_addr_write(uint64_t data) "data: 0x%016"PRIx64 -# hw/ide/piix.c +# piix.c bmdma_read(uint64_t addr, uint8_t val) "bmdma: readb 0x%"PRIx64" : 0x%02x" bmdma_write(uint64_t addr, uint64_t val) "bmdma: writeb 0x%"PRIx64" : 0x%02"PRIx64 -# hw/ide/sii3112.c +# sii3112.c sii3112_read(int size, uint64_t addr, uint64_t val) "bmdma: read (size %d) 0x%"PRIx64" : 0x%02"PRIx64 sii3112_write(int size, uint64_t addr, uint64_t val) "bmdma: write (size %d) 0x%"PRIx64" : 0x%02"PRIx64 sii3112_set_irq(int channel, int level) "channel %d level %d" -# hw/ide/via.c +# via.c bmdma_read_via(uint64_t addr, uint32_t val) "bmdma: readb 0x%"PRIx64" : 0x%02x" bmdma_write_via(uint64_t addr, uint64_t val) "bmdma: writeb 0x%"PRIx64" : 0x%02"PRIx64 -# hw/ide/atapi.c +# atapi.c cd_read_sector_sync(int lba) "lba=%d" cd_read_sector_cb(int lba, int ret) "lba=%d ret=%d" cd_read_sector(int lba) "lba=%d" @@ -62,7 +62,7 @@ ide_atapi_cmd_read_dma_cb_aio(void *s, int lba, int n) "IDEState: %p; aio read: # Warning: Verbose ide_atapi_cmd_packet(void *s, uint16_t limit, const char *packet) "IDEState: %p; limit=0x%x packet: %s" -# hw/ide/ahci.c +# ahci.c ahci_port_read(void *s, int port, const char *reg, int offset, uint32_t ret) "ahci(%p)[%d]: port read [reg:%s] @ 0x%x: 0x%08x" ahci_port_read_default(void *s, int port, const char *reg, int offset) "ahci(%p)[%d]: unimplemented port read [reg:%s] @ 0x%x" ahci_irq_raise(void *s) "ahci(%p): raise irq" @@ -91,7 +91,6 @@ ahci_populate_sglist_short_map(void *s, int port) "ahci(%p)[%d]: mapped less tha ahci_populate_sglist_bad_offset(void *s, int port, int off_idx, int64_t off_pos) "ahci(%p)[%d]: Incorrect offset! off_idx: %d, off_pos: %"PRId64 ncq_finish(void *s, int port, uint8_t tag) "ahci(%p)[%d][tag:%d]: NCQ transfer finished" execute_ncq_command_read(void *s, int port, uint8_t tag, int count, int64_t lba) "ahci(%p)[%d][tag:%d]: NCQ reading %d sectors from LBA %"PRId64 -execute_ncq_command_write(void *s, int port, uint8_t tag, int count, int64_t lba) "ahci(%p)[%d][tag:%d]: NCQ writing %d sectors to LBA %"PRId64 execute_ncq_command_unsup(void *s, int port, uint8_t tag, uint8_t cmd) "ahci(%p)[%d][tag:%d]: error: unsupported NCQ command (0x%02x) received" process_ncq_command_mismatch(void *s, int port, uint8_t tag, uint8_t slot) "ahci(%p)[%d][tag:%d]: Warning: NCQ slot (%d) did not match the given tag" process_ncq_command_aux(void *s, int port, uint8_t tag) "ahci(%p)[%d][tag:%d]: Warn: Attempt to use NCQ auxiliary fields" @@ -115,9 +114,11 @@ ahci_dma_prepare_buf_fail(void *s, int port) "ahci(%p)[%d]: sglist population fa ahci_dma_rw_buf(void *s, int port, int l) "ahci(%p)[%d] len=0x%x" ahci_cmd_done(void *s, int port) "ahci(%p)[%d]: cmd done" ahci_reset(void *s) "ahci(%p): HBA reset" -allwinner_ahci_mem_read(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): read a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" -allwinner_ahci_mem_write(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): write a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" # Warning: Verbose handle_reg_h2d_fis_dump(void *s, int port, const char *fis) "ahci(%p)[%d]: %s" handle_cmd_fis_dump(void *s, int port, const char *fis) "ahci(%p)[%d]: %s" + +# ahci-allwinner.c +allwinner_ahci_mem_read(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): read a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" +allwinner_ahci_mem_write(void *s, void *a, uint64_t addr, uint64_t val, unsigned size) "ahci(%p): write a=%p addr=0x%"PRIx64" val=0x%"PRIx64", size=%d" diff --git a/hw/ide/via.c b/hw/ide/via.c index 238f038d72..ac9385228c 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -26,7 +26,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" -#include "hw/isa/isa.h" #include "sysemu/sysemu.h" #include "sysemu/dma.h" @@ -102,7 +101,24 @@ static void bmdma_setup_bar(PCIIDEState *d) } } -static void via_reset(void *opaque) +static void via_ide_set_irq(void *opaque, int n, int level) +{ + PCIDevice *d = PCI_DEVICE(opaque); + + if (level) { + d->config[0x70 + n * 8] |= 0x80; + } else { + d->config[0x70 + n * 8] &= ~0x80; + } + + level = (d->config[0x70] & 0x80) || (d->config[0x78] & 0x80); + n = pci_get_byte(d->config + PCI_INTERRUPT_LINE); + if (n) { + qemu_set_irq(isa_get_irq(NULL, n), level); + } +} + +static void via_ide_reset(void *opaque) { PCIIDEState *d = opaque; PCIDevice *pd = PCI_DEVICE(d); @@ -113,7 +129,7 @@ static void via_reset(void *opaque) ide_bus_reset(&d->bus[i]); } - pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_WAIT); + pci_set_word(pci_conf + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_WAIT); pci_set_word(pci_conf + PCI_STATUS, PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); @@ -144,22 +160,42 @@ static void via_reset(void *opaque) pci_set_long(pci_conf + 0xc0, 0x00020001); } -static void vt82c686b_init_ports(PCIIDEState *d) { - static const struct { - int iobase; - int iobase2; - int isairq; - } port_info[] = { - {0x1f0, 0x3f6, 14}, - {0x170, 0x376, 15}, - }; +static void via_ide_realize(PCIDevice *dev, Error **errp) +{ + PCIIDEState *d = PCI_IDE(dev); + uint8_t *pci_conf = dev->config; int i; + pci_config_set_prog_interface(pci_conf, 0x8f); /* native PCI ATA mode */ + pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); + dev->wmask[PCI_INTERRUPT_LINE] = 0xf; + + qemu_register_reset(via_ide_reset, d); + + memory_region_init_io(&d->data_bar[0], OBJECT(d), &pci_ide_data_le_ops, + &d->bus[0], "via-ide0-data", 8); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &d->data_bar[0]); + + memory_region_init_io(&d->cmd_bar[0], OBJECT(d), &pci_ide_cmd_le_ops, + &d->bus[0], "via-ide0-cmd", 4); + pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd_bar[0]); + + memory_region_init_io(&d->data_bar[1], OBJECT(d), &pci_ide_data_le_ops, + &d->bus[1], "via-ide1-data", 8); + pci_register_bar(dev, 2, PCI_BASE_ADDRESS_SPACE_IO, &d->data_bar[1]); + + memory_region_init_io(&d->cmd_bar[1], OBJECT(d), &pci_ide_cmd_le_ops, + &d->bus[1], "via-ide1-cmd", 4); + pci_register_bar(dev, 3, PCI_BASE_ADDRESS_SPACE_IO, &d->cmd_bar[1]); + + bmdma_setup_bar(d); + pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); + + vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); + for (i = 0; i < 2; i++) { ide_bus_new(&d->bus[i], sizeof(d->bus[i]), DEVICE(d), i, 2); - ide_init_ioport(&d->bus[i], NULL, port_info[i].iobase, - port_info[i].iobase2); - ide_init2(&d->bus[i], isa_get_irq(NULL, port_info[i].isairq)); + ide_init2(&d->bus[i], qemu_allocate_irq(via_ide_set_irq, d, i)); bmdma_init(&d->bus[i], &d->bmdma[i], d); d->bmdma[i].bus = &d->bus[i]; @@ -167,25 +203,7 @@ static void vt82c686b_init_ports(PCIIDEState *d) { } } -/* via ide func */ -static void vt82c686b_ide_realize(PCIDevice *dev, Error **errp) -{ - PCIIDEState *d = PCI_IDE(dev); - uint8_t *pci_conf = dev->config; - - pci_config_set_prog_interface(pci_conf, 0x8a); /* legacy ATA mode */ - pci_set_long(pci_conf + PCI_CAPABILITY_LIST, 0x000000c0); - - qemu_register_reset(via_reset, d); - bmdma_setup_bar(d); - pci_register_bar(dev, 4, PCI_BASE_ADDRESS_SPACE_IO, &d->bmdma_bar); - - vmstate_register(DEVICE(dev), 0, &vmstate_ide_pci, d); - - vt82c686b_init_ports(d); -} - -static void vt82c686b_ide_exitfn(PCIDevice *dev) +static void via_ide_exitfn(PCIDevice *dev) { PCIIDEState *d = PCI_IDE(dev); unsigned i; @@ -196,7 +214,7 @@ static void vt82c686b_ide_exitfn(PCIDevice *dev) } } -void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) +void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn) { PCIDevice *dev; @@ -209,8 +227,8 @@ static void via_ide_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - k->realize = vt82c686b_ide_realize; - k->exit = vt82c686b_ide_exitfn; + k->realize = via_ide_realize; + k->exit = via_ide_exitfn; k->vendor_id = PCI_VENDOR_ID_VIA; k->device_id = PCI_DEVICE_ID_VIA_IDE; k->revision = 0x06; diff --git a/hw/input/Kconfig b/hw/input/Kconfig new file mode 100644 index 0000000000..e2e66f0858 --- /dev/null +++ b/hw/input/Kconfig @@ -0,0 +1,33 @@ +config ADB + bool + +config LM832X + bool + depends on I2C + +config PCKBD + bool + default y + depends on ISA_BUS + +config PL050 + bool + +config STELLARIS_INPUT + bool + +config TSC2005 + bool + +config VIRTIO_INPUT + bool + default y + depends on VIRTIO + +config VIRTIO_INPUT_HOST + bool + default y + depends on VIRTIO && LINUX + +config TSC210X + bool diff --git a/hw/input/lm832x.c b/hw/input/lm832x.c index 74da30d9ca..1fc7b86f19 100644 --- a/hw/input/lm832x.c +++ b/hw/input/lm832x.c @@ -66,7 +66,7 @@ typedef struct { struct { uint16_t file[256]; - uint8_t faddr; + uint8_t faddr; uint8_t addr[3]; QEMUTimer *tm[3]; } pwm; @@ -401,7 +401,7 @@ static int lm_i2c_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int lm_i2c_rx(I2CSlave *i2c) +static uint8_t lm_i2c_rx(I2CSlave *i2c) { LM823KbdState *s = LM8323(i2c); diff --git a/hw/input/milkymist-softusb.c b/hw/input/milkymist-softusb.c index ef8f47cd83..8766a17d9e 100644 --- a/hw/input/milkymist-softusb.c +++ b/hw/input/milkymist-softusb.c @@ -245,32 +245,31 @@ static void milkymist_softusb_reset(DeviceState *d) s->regs[R_CTRL] = CTRL_RESET; } -static int milkymist_softusb_init(SysBusDevice *dev) +static void milkymist_softusb_realize(DeviceState *dev, Error **errp) { MilkymistSoftUsbState *s = MILKYMIST_SOFTUSB(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(sbd, &s->irq); memory_region_init_io(&s->regs_region, OBJECT(s), &softusb_mmio_ops, s, "milkymist-softusb", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); + sysbus_init_mmio(sbd, &s->regs_region); /* register pmem and dmem */ memory_region_init_ram_nomigrate(&s->pmem, OBJECT(s), "milkymist-softusb.pmem", 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); + sysbus_init_mmio(sbd, &s->pmem); memory_region_init_ram_nomigrate(&s->dmem, OBJECT(s), "milkymist-softusb.dmem", 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); + sysbus_init_mmio(sbd, &s->dmem); hid_init(&s->hid_kbd, HID_KEYBOARD, softusb_kbd_hid_datain); hid_init(&s->hid_mouse, HID_MOUSE, softusb_mouse_hid_datain); - - return 0; } static const VMStateDescription vmstate_milkymist_softusb = { @@ -296,9 +295,8 @@ static Property milkymist_softusb_properties[] = { static void milkymist_softusb_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_softusb_init; + dc->realize = milkymist_softusb_realize; dc->reset = milkymist_softusb_reset; dc->vmsd = &vmstate_milkymist_softusb; dc->props = milkymist_softusb_properties; diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 07c8801387..47a606f5e3 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -30,14 +30,7 @@ #include "hw/input/i8042.h" #include "sysemu/sysemu.h" -/* debug PC keyboard */ -//#define DEBUG_KBD -#ifdef DEBUG_KBD -#define DPRINTF(fmt, ...) \ - do { printf("KBD: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) -#endif +#include "trace.h" /* Keyboard Controller Commands */ #define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ @@ -55,7 +48,7 @@ #define KBD_CCMD_WRITE_OUTPORT 0xD1 /* write output port */ #define KBD_CCMD_WRITE_OBUF 0xD2 #define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if - initiated by the auxiliary device */ + initiated by the auxiliary device */ #define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ #define KBD_CCMD_DISABLE_A20 0xDD /* HP vectra only ? */ #define KBD_CCMD_ENABLE_A20 0xDF /* HP vectra only ? */ @@ -210,7 +203,7 @@ static uint64_t kbd_read_status(void *opaque, hwaddr addr, KBDState *s = opaque; int val; val = s->status; - DPRINTF("kbd: read status=0x%02x\n", val); + trace_pckbd_kbd_read_status(val); return val; } @@ -224,7 +217,7 @@ static void kbd_queue(KBDState *s, int b, int aux) static void outport_write(KBDState *s, uint32_t val) { - DPRINTF("kbd: write outport=0x%02x\n", val); + trace_pckbd_outport_write(val); s->outport = val; qemu_set_irq(s->a20_out, (val >> 1) & 1); if (!(val & 1)) { @@ -237,7 +230,7 @@ static void kbd_write_command(void *opaque, hwaddr addr, { KBDState *s = opaque; - DPRINTF("kbd: write cmd=0x%02" PRIx64 "\n", val); + trace_pckbd_kbd_write_command(val); /* Bits 3-0 of the output port P2 of the keyboard controller may be pulsed * low for approximately 6 micro seconds. Bits 3-0 of the KBD_CCMD_PULSE @@ -326,7 +319,7 @@ static uint64_t kbd_read_data(void *opaque, hwaddr addr, else val = ps2_read_data(s->kbd); - DPRINTF("kbd: read data=0x%02x\n", val); + trace_pckbd_kbd_read_data(val); return val; } @@ -335,7 +328,7 @@ static void kbd_write_data(void *opaque, hwaddr addr, { KBDState *s = opaque; - DPRINTF("kbd: write data=0x%02" PRIx64 "\n", val); + trace_pckbd_kbd_write_data(val); switch(s->write_cmd) { case 0: @@ -574,6 +567,7 @@ static void i8042_class_initfn(ObjectClass *klass, void *data) dc->realize = i8042_realizefn; dc->vmsd = &vmstate_kbd_isa; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } static const TypeInfo i8042_info = { diff --git a/hw/input/pl050.c b/hw/input/pl050.c index be9cd57b17..15bffbfcad 100644 --- a/hw/input/pl050.c +++ b/hw/input/pl050.c @@ -139,19 +139,19 @@ static const MemoryRegionOps pl050_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int pl050_initfn(SysBusDevice *dev) +static void pl050_realize(DeviceState *dev, Error **errp) { PL050State *s = PL050(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); memory_region_init_io(&s->iomem, OBJECT(s), &pl050_ops, s, "pl050", 0x1000); - sysbus_init_mmio(dev, &s->iomem); - sysbus_init_irq(dev, &s->irq); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); if (s->is_mouse) { s->dev = ps2_mouse_init(pl050_update, s); } else { s->dev = ps2_kbd_init(pl050_update, s); } - return 0; } static void pl050_keyboard_init(Object *obj) @@ -183,9 +183,8 @@ static const TypeInfo pl050_mouse_info = { static void pl050_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(oc); - sdc->init = pl050_initfn; + dc->realize = pl050_realize; dc->vmsd = &vmstate_pl050; } diff --git a/hw/input/ps2.c b/hw/input/ps2.c index fdfcadf9a1..d3161f1e7c 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -255,7 +255,7 @@ static void ps2_put_keycode(void *opaque, int keycode) PS2KbdState *s = opaque; trace_ps2_put_keycode(opaque, keycode); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); if (s->translate) { if (keycode == 0xf0) { @@ -285,7 +285,7 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src, return; } - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); assert(evt->type == INPUT_EVENT_KIND_KEY); qcode = qemu_input_key_value_to_qcode(key->key); @@ -748,7 +748,7 @@ static void ps2_mouse_sync(DeviceState *dev) } if (s->mouse_buttons) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); } if (!(s->mouse_status & MOUSE_STATUS_REMOTE)) { /* if not remote, send event. Multiple events are sent if @@ -914,7 +914,12 @@ static void ps2_common_post_load(PS2State *s) uint8_t tmp_data[PS2_QUEUE_SIZE]; /* set the useful data buffer queue size, < PS2_QUEUE_SIZE */ - size = (q->count < 0 || q->count > PS2_QUEUE_SIZE) ? 0 : q->count; + size = q->count; + if (q->count < 0) { + size = 0; + } else if (q->count > PS2_QUEUE_SIZE) { + size = PS2_QUEUE_SIZE; + } /* move the queue elements to the start of data array */ for (i = 0; i < size; i++) { @@ -929,7 +934,6 @@ static void ps2_common_post_load(PS2State *s) q->rptr = 0; q->wptr = (size == PS2_QUEUE_SIZE) ? 0 : size; q->count = size; - s->update_irq(s->update_arg, q->count != 0); } static void ps2_kbd_reset(void *opaque) @@ -938,7 +942,7 @@ static void ps2_kbd_reset(void *opaque) trace_ps2_kbd_reset(opaque); ps2_common_reset(&s->common); - s->scan_enabled = 0; + s->scan_enabled = 1; s->translate = 0; s->scancode_set = 2; s->modifiers = 0; diff --git a/hw/input/trace-events b/hw/input/trace-events index 3965a842ae..cf072fa2f8 100644 --- a/hw/input/trace-events +++ b/hw/input/trace-events @@ -1,20 +1,27 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/input/adb-kbd.c +# adb-kbd.c adb_kbd_no_key(void) "Ignoring NO_KEY" adb_kbd_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" adb_kbd_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" adb_kbd_request_change_addr(int devaddr) "change addr to 0x%x" adb_kbd_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" -# hw/input/adb-mouse.c +# adb-mouse.c adb_mouse_flush(void) "flush" adb_mouse_writereg(int reg, uint8_t val) "reg %d val 0x%2.2x" adb_mouse_readreg(int reg, uint8_t val0, uint8_t val1) "reg %d obuf[0] 0x%2.2x obuf[1] 0x%2.2x" adb_mouse_request_change_addr(int devaddr) "change addr to 0x%x" adb_mouse_request_change_addr_and_handler(int devaddr, int handler) "change addr and handler to 0x%x, 0x%x" -# hw/input/ps2.c +# pckbd.c +pckbd_kbd_read_data(uint32_t val) "0x%02x" +pckbd_kbd_read_status(int status) "0x%02x" +pckbd_outport_write(uint32_t val) "0x%02x" +pckbd_kbd_write_command(uint64_t val) "0x%02"PRIx64 +pckbd_kbd_write_data(uint64_t val) "0x%02"PRIx64 + +# ps2.c ps2_put_keycode(void *opaque, int keycode) "%p keycode 0x%02x" ps2_keyboard_event(void *opaque, int qcode, int down, unsigned int modifier, unsigned int modifiers) "%p qcode %d down %d modifier 0x%x modifiers 0x%x" ps2_read_data(void *opaque) "%p" @@ -30,19 +37,19 @@ ps2_mouse_reset(void *opaque) "%p" ps2_kbd_init(void *s) "%p" ps2_mouse_init(void *s) "%p" -# hw/input/milkymist-softusb.c +# milkymist-softusb.c milkymist_softusb_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_softusb_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_softusb_mevt(uint8_t m) "m %d" milkymist_softusb_kevt(uint8_t m) "m %d" milkymist_softusb_pulse_irq(void) "Pulse IRQ" -# hw/input/hid.c +# hid.c hid_kbd_queue_full(void) "queue full" hid_kbd_queue_empty(void) "queue empty" -# hw/input/tsc2005.c +# tsc2005.c tsc2005_sense(const char *state) "touchscreen sense %s" -# hw/input/virtio +# virtio-input.c virtio_input_queue_full(void) "queue full" diff --git a/hw/input/tsc210x.c b/hw/input/tsc210x.c index 1cad57f644..41731619bb 100644 --- a/hw/input/tsc210x.c +++ b/hw/input/tsc210x.c @@ -318,7 +318,7 @@ static void tsc2102_audio_output_update(TSC210xState *s) fmt.endianness = 0; fmt.nchannels = 2; fmt.freq = s->codec.tx_rate; - fmt.fmt = AUD_FMT_S16; + fmt.fmt = AUDIO_FORMAT_S16; s->dac_voice[0] = AUD_open_out(&s->card, s->dac_voice[0], "tsc2102.sink", s, (void *) tsc210x_audio_out_cb, &fmt); @@ -552,10 +552,8 @@ static void tsc2102_data_register_write( return; default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_data_register_write: " - "no such register: 0x%02x\n", reg); -#endif + qemu_log_mask(LOG_GUEST_ERROR, "tsc2102_data_register_write: " + "no such register: 0x%02x\n", reg); } } @@ -577,7 +575,7 @@ static void tsc2102_control_register_write( case 0x01: /* Status / Keypad Control */ if ((s->model & 0xff00) == 0x2100) s->pin_func = value >> 14; - else { + else { s->kb.scan = (value >> 14) & 1; s->kb.debounce = (value >> 11) & 7; if (s->kb.intr && s->kb.scan) { @@ -636,10 +634,8 @@ static void tsc2102_control_register_write( default: bad_reg: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_control_register_write: " - "no such register: 0x%02x\n", reg); -#endif + qemu_log_mask(LOG_GUEST_ERROR, "tsc2102_control_register_write: " + "no such register: 0x%02x\n", reg); } } @@ -764,10 +760,8 @@ static void tsc2102_audio_register_write( return; default: -#ifdef TSC_VERBOSE - fprintf(stderr, "tsc2102_audio_register_write: " - "no such register: 0x%02x\n", reg); -#endif + qemu_log_mask(LOG_GUEST_ERROR, "tsc2102_audio_register_write: " + "no such register: 0x%02x\n", reg); } } diff --git a/hw/intc/Kconfig b/hw/intc/Kconfig new file mode 100644 index 0000000000..5347f8412c --- /dev/null +++ b/hw/intc/Kconfig @@ -0,0 +1,60 @@ +config HEATHROW_PIC + bool + +config I8259 + bool + +config PL190 + bool + +config IOAPIC + bool + +config ARM_GIC + bool + select MSI_NONBROKEN + +config OPENPIC + bool + select MSI_NONBROKEN + +config APIC + bool + select MSI_NONBROKEN + +config ARM_GIC_KVM + bool + default y + depends on ARM_GIC && KVM + +config OPENPIC_KVM + bool + default y + depends on OPENPIC && KVM + +config XICS + bool + depends on POWERNV || PSERIES + +config XICS_SPAPR + bool + select XICS + +config XICS_KVM + bool + default y + depends on XICS && KVM + +config ALLWINNER_A10_PIC + bool + +config S390_FLIC + bool + +config S390_FLIC_KVM + bool + default y + depends on S390_FLIC && KVM + +config OMPIC + bool diff --git a/hw/intc/Makefile.objs b/hw/intc/Makefile.objs index 0e9963f5ee..df712c3e6c 100644 --- a/hw/intc/Makefile.objs +++ b/hw/intc/Makefile.objs @@ -37,7 +37,9 @@ obj-$(CONFIG_SH4) += sh_intc.o obj-$(CONFIG_XICS) += xics.o obj-$(CONFIG_XICS_SPAPR) += xics_spapr.o obj-$(CONFIG_XICS_KVM) += xics_kvm.o -obj-$(CONFIG_POWERNV) += xics_pnv.o +obj-$(CONFIG_XIVE) += xive.o +obj-$(CONFIG_XIVE_SPAPR) += spapr_xive.o +obj-$(CONFIG_POWERNV) += xics_pnv.o pnv_xive.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 diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index 11f13663c8..1aa628cbbb 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -17,7 +17,6 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "hw/devices.h" #include "sysemu/sysemu.h" #include "hw/intc/allwinner-a10-pic.h" #include "qemu/log.h" diff --git a/hw/intc/apic.c b/hw/intc/apic.c index 6fda52b86c..6ea619c360 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -122,9 +122,10 @@ static void apic_sync_vapic(APICCommonState *s, int sync_type) } vapic_state.irr = vector & 0xff; - cpu_physical_memory_write_rom(&address_space_memory, - s->vapic_paddr + start, - ((void *)&vapic_state) + start, length); + address_space_write_rom(&address_space_memory, + s->vapic_paddr + start, + MEMTXATTRS_UNSPECIFIED, + ((void *)&vapic_state) + start, length); } } @@ -441,7 +442,7 @@ static int apic_find_dest(uint8_t dest) for (i = 0; i < MAX_APICS; i++) { apic = local_apics[i]; - if (apic && apic->id == dest) + if (apic && apic->id == dest) return i; if (!apic) break; @@ -650,31 +651,17 @@ static void apic_timer(void *opaque) apic_timer_update(s, s->next_time); } -static uint32_t apic_mem_readb(void *opaque, hwaddr addr) -{ - return 0; -} - -static uint32_t apic_mem_readw(void *opaque, hwaddr addr) -{ - return 0; -} - -static void apic_mem_writeb(void *opaque, hwaddr addr, uint32_t val) -{ -} - -static void apic_mem_writew(void *opaque, hwaddr addr, uint32_t val) -{ -} - -static uint32_t apic_mem_readl(void *opaque, hwaddr addr) +static uint64_t apic_mem_read(void *opaque, hwaddr addr, unsigned size) { DeviceState *dev; APICCommonState *s; uint32_t val; int index; + if (size < 4) { + return 0; + } + dev = cpu_get_current_apic(); if (!dev) { return 0; @@ -765,11 +752,17 @@ static void apic_send_msi(MSIMessage *msi) apic_deliver_irq(dest, dest_mode, delivery, vector, trigger_mode); } -static void apic_mem_writel(void *opaque, hwaddr addr, uint32_t val) +static void apic_mem_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { DeviceState *dev; APICCommonState *s; int index = (addr >> 4) & 0xff; + + if (size < 4) { + return; + } + if (addr > 0xfff || !index) { /* MSI and MMIO APIC are at the same memory location, * but actually not on the global bus: MSI is on PCI bus @@ -880,10 +873,12 @@ static void apic_post_load(APICCommonState *s) } static const MemoryRegionOps apic_io_ops = { - .old_mmio = { - .read = { apic_mem_readb, apic_mem_readw, apic_mem_readl, }, - .write = { apic_mem_writeb, apic_mem_writew, apic_mem_writel, }, - }, + .read = apic_mem_read, + .write = apic_mem_write, + .impl.min_access_size = 1, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 34dc84ae81..b3ac2d11fc 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -61,6 +61,11 @@ static inline int gic_get_current_cpu(GICState *s) return 0; } +static inline int gic_get_current_vcpu(GICState *s) +{ + return gic_get_current_cpu(s) + GIC_NCPU; +} + /* Return true if this GIC config has interrupt groups, which is * true if we're a GICv2, or a GICv1 with the security extensions. */ @@ -69,97 +74,288 @@ static inline bool gic_has_groups(GICState *s) return s->revision == 2 || s->security_extn; } +static inline bool gic_cpu_ns_access(GICState *s, int cpu, MemTxAttrs attrs) +{ + return !gic_is_vcpu(cpu) && s->security_extn && !attrs.secure; +} + +static inline void gic_get_best_irq(GICState *s, int cpu, + int *best_irq, int *best_prio, int *group) +{ + int irq; + int cm = 1 << cpu; + + *best_irq = 1023; + *best_prio = 0x100; + + for (irq = 0; irq < s->num_irq; irq++) { + if (GIC_DIST_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm) && + (!GIC_DIST_TEST_ACTIVE(irq, cm)) && + (irq < GIC_INTERNAL || GIC_DIST_TARGET(irq) & cm)) { + if (GIC_DIST_GET_PRIORITY(irq, cpu) < *best_prio) { + *best_prio = GIC_DIST_GET_PRIORITY(irq, cpu); + *best_irq = irq; + } + } + } + + if (*best_irq < 1023) { + *group = GIC_DIST_TEST_GROUP(*best_irq, cm); + } +} + +static inline void gic_get_best_virq(GICState *s, int cpu, + int *best_irq, int *best_prio, int *group) +{ + int lr_idx = 0; + + *best_irq = 1023; + *best_prio = 0x100; + + for (lr_idx = 0; lr_idx < s->num_lrs; lr_idx++) { + uint32_t lr_entry = s->h_lr[lr_idx][cpu]; + int state = GICH_LR_STATE(lr_entry); + + if (state == GICH_LR_STATE_PENDING) { + int prio = GICH_LR_PRIORITY(lr_entry); + + if (prio < *best_prio) { + *best_prio = prio; + *best_irq = GICH_LR_VIRT_ID(lr_entry); + *group = GICH_LR_GROUP(lr_entry); + } + } + } +} + +/* Return true if IRQ signaling is enabled for the given cpu and at least one + * of the given groups: + * - in the non-virt case, the distributor must be enabled for one of the + * given groups + * - in the virt case, the virtual interface must be enabled. + * - in all cases, the (v)CPU interface must be enabled for one of the given + * groups. + */ +static inline bool gic_irq_signaling_enabled(GICState *s, int cpu, bool virt, + int group_mask) +{ + if (!virt && !(s->ctlr & group_mask)) { + return false; + } + + if (virt && !(s->h_hcr[cpu] & R_GICH_HCR_EN_MASK)) { + return false; + } + + if (!(s->cpu_ctlr[cpu] & group_mask)) { + return false; + } + + return true; +} + /* TODO: Many places that call this routine could be optimized. */ /* Update interrupt status after enabled or pending bits have been changed. */ -void gic_update(GICState *s) +static inline void gic_update_internal(GICState *s, bool virt) { int best_irq; int best_prio; - int irq; int irq_level, fiq_level; - int cpu; - int cm; + int cpu, cpu_iface; + int group = 0; + qemu_irq *irq_lines = virt ? s->parent_virq : s->parent_irq; + qemu_irq *fiq_lines = virt ? s->parent_vfiq : s->parent_fiq; 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)) - || !(s->cpu_ctlr[cpu] & (GICC_CTLR_EN_GRP0 | GICC_CTLR_EN_GRP1))) { - qemu_irq_lower(s->parent_irq[cpu]); - qemu_irq_lower(s->parent_fiq[cpu]); + cpu_iface = virt ? (cpu + GIC_NCPU) : cpu; + + s->current_pending[cpu_iface] = 1023; + if (!gic_irq_signaling_enabled(s, cpu, virt, + GICD_CTLR_EN_GRP0 | GICD_CTLR_EN_GRP1)) { + qemu_irq_lower(irq_lines[cpu]); + qemu_irq_lower(fiq_lines[cpu]); continue; } - best_prio = 0x100; - best_irq = 1023; - for (irq = 0; irq < s->num_irq; irq++) { - if (GIC_TEST_ENABLED(irq, cm) && gic_test_pending(s, irq, cm) && - (!GIC_TEST_ACTIVE(irq, cm)) && - (irq < GIC_INTERNAL || GIC_TARGET(irq) & cm)) { - if (GIC_GET_PRIORITY(irq, cpu) < best_prio) { - best_prio = GIC_GET_PRIORITY(irq, cpu); - best_irq = irq; - } - } + + if (virt) { + gic_get_best_virq(s, cpu, &best_irq, &best_prio, &group); + } else { + gic_get_best_irq(s, cpu, &best_irq, &best_prio, &group); } if (best_irq != 1023) { - trace_gic_update_bestirq(cpu, best_irq, best_prio, - s->priority_mask[cpu], s->running_priority[cpu]); + trace_gic_update_bestirq(virt ? "vcpu" : "cpu", cpu, + best_irq, best_prio, + s->priority_mask[cpu_iface], + s->running_priority[cpu_iface]); } irq_level = fiq_level = 0; - if (best_prio < s->priority_mask[cpu]) { - s->current_pending[cpu] = best_irq; - if (best_prio < s->running_priority[cpu]) { - int group = GIC_TEST_GROUP(best_irq, cm); - - if (extract32(s->ctlr, group, 1) && - extract32(s->cpu_ctlr[cpu], group, 1)) { - if (group == 0 && s->cpu_ctlr[cpu] & GICC_CTLR_FIQ_EN) { + if (best_prio < s->priority_mask[cpu_iface]) { + s->current_pending[cpu_iface] = best_irq; + if (best_prio < s->running_priority[cpu_iface]) { + if (gic_irq_signaling_enabled(s, cpu, virt, 1 << group)) { + if (group == 0 && + s->cpu_ctlr[cpu_iface] & GICC_CTLR_FIQ_EN) { DPRINTF("Raised pending FIQ %d (cpu %d)\n", - best_irq, cpu); + best_irq, cpu_iface); fiq_level = 1; - trace_gic_update_set_irq(cpu, "fiq", fiq_level); + trace_gic_update_set_irq(cpu, virt ? "vfiq" : "fiq", + fiq_level); } else { DPRINTF("Raised pending IRQ %d (cpu %d)\n", - best_irq, cpu); + best_irq, cpu_iface); irq_level = 1; - trace_gic_update_set_irq(cpu, "irq", irq_level); + trace_gic_update_set_irq(cpu, virt ? "virq" : "irq", + irq_level); } } } } - qemu_set_irq(s->parent_irq[cpu], irq_level); - qemu_set_irq(s->parent_fiq[cpu], fiq_level); + qemu_set_irq(irq_lines[cpu], irq_level); + qemu_set_irq(fiq_lines[cpu], fiq_level); } } -void gic_set_pending_private(GICState *s, int cpu, int irq) +static void gic_update(GICState *s) { - int cm = 1 << cpu; + gic_update_internal(s, false); +} - if (gic_test_pending(s, irq, cm)) { - return; +/* Return true if this LR is empty, i.e. the corresponding bit + * in ELRSR is set. + */ +static inline bool gic_lr_entry_is_free(uint32_t entry) +{ + return (GICH_LR_STATE(entry) == GICH_LR_STATE_INVALID) + && (GICH_LR_HW(entry) || !GICH_LR_EOI(entry)); +} + +/* Return true if this LR should trigger an EOI maintenance interrupt, i.e. the + * corrsponding bit in EISR is set. + */ +static inline bool gic_lr_entry_is_eoi(uint32_t entry) +{ + return (GICH_LR_STATE(entry) == GICH_LR_STATE_INVALID) + && !GICH_LR_HW(entry) && GICH_LR_EOI(entry); +} + +static inline void gic_extract_lr_info(GICState *s, int cpu, + int *num_eoi, int *num_valid, int *num_pending) +{ + int lr_idx; + + *num_eoi = 0; + *num_valid = 0; + *num_pending = 0; + + for (lr_idx = 0; lr_idx < s->num_lrs; lr_idx++) { + uint32_t *entry = &s->h_lr[lr_idx][cpu]; + + if (gic_lr_entry_is_eoi(*entry)) { + (*num_eoi)++; + } + + if (GICH_LR_STATE(*entry) != GICH_LR_STATE_INVALID) { + (*num_valid)++; + } + + if (GICH_LR_STATE(*entry) == GICH_LR_STATE_PENDING) { + (*num_pending)++; + } + } +} + +static void gic_compute_misr(GICState *s, int cpu) +{ + uint32_t value = 0; + int vcpu = cpu + GIC_NCPU; + + int num_eoi, num_valid, num_pending; + + gic_extract_lr_info(s, cpu, &num_eoi, &num_valid, &num_pending); + + /* EOI */ + if (num_eoi) { + value |= R_GICH_MISR_EOI_MASK; } - DPRINTF("Set %d pending cpu %d\n", irq, cpu); - GIC_SET_PENDING(irq, cm); - gic_update(s); + /* U: true if only 0 or 1 LR entry is valid */ + if ((s->h_hcr[cpu] & R_GICH_HCR_UIE_MASK) && (num_valid < 2)) { + value |= R_GICH_MISR_U_MASK; + } + + /* LRENP: EOICount is not 0 */ + if ((s->h_hcr[cpu] & R_GICH_HCR_LRENPIE_MASK) && + ((s->h_hcr[cpu] & R_GICH_HCR_EOICount_MASK) != 0)) { + value |= R_GICH_MISR_LRENP_MASK; + } + + /* NP: no pending interrupts */ + if ((s->h_hcr[cpu] & R_GICH_HCR_NPIE_MASK) && (num_pending == 0)) { + value |= R_GICH_MISR_NP_MASK; + } + + /* VGrp0E: group0 virq signaling enabled */ + if ((s->h_hcr[cpu] & R_GICH_HCR_VGRP0EIE_MASK) && + (s->cpu_ctlr[vcpu] & GICC_CTLR_EN_GRP0)) { + value |= R_GICH_MISR_VGrp0E_MASK; + } + + /* VGrp0D: group0 virq signaling disabled */ + if ((s->h_hcr[cpu] & R_GICH_HCR_VGRP0DIE_MASK) && + !(s->cpu_ctlr[vcpu] & GICC_CTLR_EN_GRP0)) { + value |= R_GICH_MISR_VGrp0D_MASK; + } + + /* VGrp1E: group1 virq signaling enabled */ + if ((s->h_hcr[cpu] & R_GICH_HCR_VGRP1EIE_MASK) && + (s->cpu_ctlr[vcpu] & GICC_CTLR_EN_GRP1)) { + value |= R_GICH_MISR_VGrp1E_MASK; + } + + /* VGrp1D: group1 virq signaling disabled */ + if ((s->h_hcr[cpu] & R_GICH_HCR_VGRP1DIE_MASK) && + !(s->cpu_ctlr[vcpu] & GICC_CTLR_EN_GRP1)) { + value |= R_GICH_MISR_VGrp1D_MASK; + } + + s->h_misr[cpu] = value; +} + +static void gic_update_maintenance(GICState *s) +{ + int cpu = 0; + int maint_level; + + for (cpu = 0; cpu < s->num_cpu; cpu++) { + gic_compute_misr(s, cpu); + maint_level = (s->h_hcr[cpu] & R_GICH_HCR_EN_MASK) && s->h_misr[cpu]; + + trace_gic_update_maintenance_irq(cpu, maint_level); + qemu_set_irq(s->maintenance_irq[cpu], maint_level); + } +} + +static void gic_update_virt(GICState *s) +{ + gic_update_internal(s, true); + gic_update_maintenance(s); } static void gic_set_irq_11mpcore(GICState *s, int irq, int level, int cm, int target) { if (level) { - GIC_SET_LEVEL(irq, cm); - if (GIC_TEST_EDGE_TRIGGER(irq) || GIC_TEST_ENABLED(irq, cm)) { + GIC_DIST_SET_LEVEL(irq, cm); + if (GIC_DIST_TEST_EDGE_TRIGGER(irq) || GIC_DIST_TEST_ENABLED(irq, cm)) { DPRINTF("Set %d pending mask %x\n", irq, target); - GIC_SET_PENDING(irq, target); + GIC_DIST_SET_PENDING(irq, target); } } else { - GIC_CLEAR_LEVEL(irq, cm); + GIC_DIST_CLEAR_LEVEL(irq, cm); } } @@ -167,13 +363,13 @@ static void gic_set_irq_generic(GICState *s, int irq, int level, int cm, int target) { if (level) { - GIC_SET_LEVEL(irq, cm); + GIC_DIST_SET_LEVEL(irq, cm); DPRINTF("Set %d pending mask %x\n", irq, target); - if (GIC_TEST_EDGE_TRIGGER(irq)) { - GIC_SET_PENDING(irq, target); + if (GIC_DIST_TEST_EDGE_TRIGGER(irq)) { + GIC_DIST_SET_PENDING(irq, target); } } else { - GIC_CLEAR_LEVEL(irq, cm); + GIC_DIST_CLEAR_LEVEL(irq, cm); } } @@ -192,7 +388,7 @@ static void gic_set_irq(void *opaque, int irq, int level) /* The first external input line is internal interrupt 32. */ cm = ALL_CPU_MASK; irq += GIC_INTERNAL; - target = GIC_TARGET(irq); + target = GIC_DIST_TARGET(irq); } else { int cpu; irq -= (s->num_irq - GIC_INTERNAL); @@ -204,7 +400,7 @@ static void gic_set_irq(void *opaque, int irq, int level) assert(irq >= GIC_NR_SGIS); - if (level == GIC_TEST_LEVEL(irq, cm)) { + if (level == GIC_DIST_TEST_LEVEL(irq, cm)) { return; } @@ -224,11 +420,12 @@ static uint16_t gic_get_current_pending_irq(GICState *s, int cpu, uint16_t pending_irq = s->current_pending[cpu]; if (pending_irq < GIC_MAXIRQ && gic_has_groups(s)) { - int group = GIC_TEST_GROUP(pending_irq, (1 << cpu)); + int group = gic_test_group(s, pending_irq, cpu); + /* On a GIC without the security extensions, reading this register * behaves in the same way as a secure access to a GIC with them. */ - bool secure = !s->security_extn || attrs.secure; + bool secure = !gic_cpu_ns_access(s, cpu, attrs); if (group == 0 && !secure) { /* Group0 interrupts hidden from Non-secure access */ @@ -255,7 +452,7 @@ static int gic_get_group_priority(GICState *s, int cpu, int irq) if (gic_has_groups(s) && !(s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) && - GIC_TEST_GROUP(irq, (1 << cpu))) { + gic_test_group(s, irq, cpu)) { bpr = s->abpr[cpu] - 1; assert(bpr >= 0); } else { @@ -268,7 +465,7 @@ static int gic_get_group_priority(GICState *s, int cpu, int irq) */ mask = ~0U << ((bpr & 7) + 1); - return GIC_GET_PRIORITY(irq, cpu) & mask; + return gic_get_priority(s, irq, cpu) & mask; } static void gic_activate_irq(GICState *s, int cpu, int irq) @@ -277,18 +474,25 @@ static void gic_activate_irq(GICState *s, int cpu, int irq) * and update the running priority. */ int prio = gic_get_group_priority(s, cpu, irq); - int preemption_level = prio >> (GIC_MIN_BPR + 1); + int min_bpr = gic_is_vcpu(cpu) ? GIC_VIRT_MIN_BPR : GIC_MIN_BPR; + int preemption_level = prio >> (min_bpr + 1); int regno = preemption_level / 32; int bitno = preemption_level % 32; + uint32_t *papr = NULL; - if (gic_has_groups(s) && GIC_TEST_GROUP(irq, (1 << cpu))) { - s->nsapr[regno][cpu] |= (1 << bitno); + if (gic_is_vcpu(cpu)) { + assert(regno == 0); + papr = &s->h_apr[gic_get_vcpu_real_id(cpu)]; + } else if (gic_has_groups(s) && gic_test_group(s, irq, cpu)) { + papr = &s->nsapr[regno][cpu]; } else { - s->apr[regno][cpu] |= (1 << bitno); + papr = &s->apr[regno][cpu]; } + *papr |= (1 << bitno); + s->running_priority[cpu] = prio; - GIC_SET_ACTIVE(irq, 1 << cpu); + gic_set_active(s, irq, cpu); } static int gic_get_prio_from_apr_bits(GICState *s, int cpu) @@ -297,6 +501,16 @@ static int gic_get_prio_from_apr_bits(GICState *s, int cpu) * on the set bits in the Active Priority Registers. */ int i; + + if (gic_is_vcpu(cpu)) { + uint32_t apr = s->h_apr[gic_get_vcpu_real_id(cpu)]; + if (apr) { + return ctz32(apr) << (GIC_VIRT_MIN_BPR + 1); + } else { + return 0x100; + } + } + for (i = 0; i < GIC_NR_APRS; i++) { uint32_t apr = s->apr[i][cpu] | s->nsapr[i][cpu]; if (!apr) { @@ -325,83 +539,111 @@ static void gic_drop_prio(GICState *s, int cpu, int group) * 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; + if (gic_is_vcpu(cpu)) { + int rcpu = gic_get_vcpu_real_id(cpu); - for (i = 0; i < GIC_NR_APRS; i++) { - uint32_t *papr = group ? &s->nsapr[i][cpu] : &s->apr[i][cpu]; - if (!*papr) { - continue; + if (s->h_apr[rcpu]) { + /* Clear lowest set bit */ + s->h_apr[rcpu] &= s->h_apr[rcpu] - 1; + } + } else { + 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; } - /* Clear lowest set bit */ - *papr &= *papr - 1; - break; } s->running_priority[cpu] = gic_get_prio_from_apr_bits(s, cpu); } +static inline uint32_t gic_clear_pending_sgi(GICState *s, int irq, int cpu) +{ + int src; + uint32_t ret; + + if (!gic_is_vcpu(cpu)) { + /* Lookup the source CPU for the SGI and clear this in the + * sgi_pending map. Return the src and clear the overall pending + * state on this CPU if the SGI is not pending from any CPUs. + */ + assert(s->sgi_pending[irq][cpu] != 0); + src = ctz32(s->sgi_pending[irq][cpu]); + s->sgi_pending[irq][cpu] &= ~(1 << src); + if (s->sgi_pending[irq][cpu] == 0) { + gic_clear_pending(s, irq, cpu); + } + ret = irq | ((src & 0x7) << 10); + } else { + uint32_t *lr_entry = gic_get_lr_entry(s, irq, cpu); + src = GICH_LR_CPUID(*lr_entry); + + gic_clear_pending(s, irq, cpu); + ret = irq | (src << 10); + } + + return ret; +} + uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs) { - int ret, irq, src; - int cm = 1 << cpu; + int ret, irq; /* gic_get_current_pending_irq() will return 1022 or 1023 appropriately * 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); - trace_gic_acknowledge_irq(cpu, irq); + trace_gic_acknowledge_irq(gic_is_vcpu(cpu) ? "vcpu" : "cpu", + gic_get_vcpu_real_id(cpu), irq); if (irq >= GIC_MAXIRQ) { DPRINTF("ACK, no pending interrupt or it is hidden: %d\n", irq); return irq; } - if (GIC_GET_PRIORITY(irq, cpu) >= s->running_priority[cpu]) { + if (gic_get_priority(s, irq, cpu) >= s->running_priority[cpu]) { DPRINTF("ACK, pending interrupt (%d) has insufficient priority\n", irq); return 1023; } + gic_activate_irq(s, cpu, irq); + if (s->revision == REV_11MPCORE) { /* Clear pending flags for both level and edge triggered interrupts. * Level triggered IRQs will be reasserted once they become inactive. */ - GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm); + gic_clear_pending(s, irq, cpu); ret = irq; } else { if (irq < GIC_NR_SGIS) { - /* Lookup the source CPU for the SGI and clear this in the - * sgi_pending map. Return the src and clear the overall pending - * state on this CPU if the SGI is not pending from any CPUs. - */ - assert(s->sgi_pending[irq][cpu] != 0); - src = ctz32(s->sgi_pending[irq][cpu]); - s->sgi_pending[irq][cpu] &= ~(1 << src); - if (s->sgi_pending[irq][cpu] == 0) { - GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm); - } - ret = irq | ((src & 0x7) << 10); + ret = gic_clear_pending_sgi(s, irq, cpu); } else { - /* Clear pending state for both level and edge triggered - * interrupts. (level triggered interrupts with an active line - * remain pending, see gic_test_pending) - */ - GIC_CLEAR_PENDING(irq, GIC_TEST_MODEL(irq) ? ALL_CPU_MASK : cm); + gic_clear_pending(s, irq, cpu); ret = irq; } } - gic_activate_irq(s, cpu, irq); - gic_update(s); + if (gic_is_vcpu(cpu)) { + gic_update_virt(s); + } else { + gic_update(s); + } DPRINTF("ACK %d\n", irq); return ret; } -void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val, +void gic_dist_set_priority(GICState *s, int cpu, int irq, uint8_t val, MemTxAttrs attrs) { if (s->security_extn && !attrs.secure) { - if (!GIC_TEST_GROUP(irq, (1 << cpu))) { + if (!GIC_DIST_TEST_GROUP(irq, (1 << cpu))) { return; /* Ignore Non-secure access of Group0 IRQ */ } val = 0x80 | (val >> 1); /* Non-secure view */ @@ -414,13 +656,13 @@ void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val, } } -static uint32_t gic_get_priority(GICState *s, int cpu, int irq, +static uint32_t gic_dist_get_priority(GICState *s, int cpu, int irq, MemTxAttrs attrs) { - uint32_t prio = GIC_GET_PRIORITY(irq, cpu); + uint32_t prio = GIC_DIST_GET_PRIORITY(irq, cpu); if (s->security_extn && !attrs.secure) { - if (!GIC_TEST_GROUP(irq, (1 << cpu))) { + if (!GIC_DIST_TEST_GROUP(irq, (1 << cpu))) { return 0; /* Non-secure access cannot read priority of Group0 IRQ */ } prio = (prio << 1) & 0xff; /* Non-secure view */ @@ -431,7 +673,7 @@ static uint32_t gic_get_priority(GICState *s, int cpu, int irq, static void gic_set_priority_mask(GICState *s, int cpu, uint8_t pmask, MemTxAttrs attrs) { - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { if (s->priority_mask[cpu] & 0x80) { /* Priority Mask in upper half */ pmask = 0x80 | (pmask >> 1); @@ -447,7 +689,7 @@ static uint32_t gic_get_priority_mask(GICState *s, int cpu, MemTxAttrs attrs) { uint32_t pmask = s->priority_mask[cpu]; - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { if (pmask & 0x80) { /* Priority Mask in upper half, return Non-secure view */ pmask = (pmask << 1) & 0xff; @@ -463,7 +705,7 @@ static uint32_t gic_get_cpu_control(GICState *s, int cpu, MemTxAttrs attrs) { uint32_t ret = s->cpu_ctlr[cpu]; - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { /* Construct the NS banked view of GICC_CTLR from the correct * bits of the S banked view. We don't need to move the bypass * control bits because we don't implement that (IMPDEF) part @@ -479,7 +721,7 @@ static void gic_set_cpu_control(GICState *s, int cpu, uint32_t value, { uint32_t mask; - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { /* The NS view can only write certain bits in the register; * the rest are unchanged */ @@ -510,7 +752,7 @@ static uint8_t gic_get_running_priority(GICState *s, int cpu, MemTxAttrs attrs) return 0xff; } - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { if (s->running_priority[cpu] & 0x80) { /* Running priority in upper half of range: return the Non-secure * view of the priority. @@ -534,7 +776,7 @@ static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs) /* Before GICv2 prio-drop and deactivate are not separable */ return false; } - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE_NS; } return s->cpu_ctlr[cpu] & GICC_CTLR_EOIMODE; @@ -542,23 +784,21 @@ static bool gic_eoi_split(GICState *s, int cpu, MemTxAttrs attrs) static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { - int cm = 1 << cpu; int group; - if (irq >= s->num_irq) { + if (irq >= GIC_MAXIRQ || (!gic_is_vcpu(cpu) && irq >= s->num_irq)) { /* * This handles two cases: * 1. If software writes the ID of a spurious interrupt [ie 1023] * to the GICC_DIR, the GIC ignores that write. * 2. If software writes the number of a non-existent interrupt * this must be a subcase of "value written is not an active interrupt" - * and so this is UNPREDICTABLE. We choose to ignore it. + * and so this is UNPREDICTABLE. We choose to ignore it. For vCPUs, + * all IRQs potentially exist, so this limit does not apply. */ return; } - 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, @@ -566,20 +806,70 @@ static void gic_deactivate_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) return; } - if (s->security_extn && !attrs.secure && !group) { + if (gic_is_vcpu(cpu) && !gic_virq_is_valid(s, irq, cpu)) { + /* This vIRQ does not have an LR entry which is either active or + * pending and active. Increment EOICount and ignore the write. + */ + int rcpu = gic_get_vcpu_real_id(cpu); + s->h_hcr[rcpu] += 1 << R_GICH_HCR_EOICount_SHIFT; + + /* Update the virtual interface in case a maintenance interrupt should + * be raised. + */ + gic_update_virt(s); + return; + } + + group = gic_has_groups(s) && gic_test_group(s, irq, cpu); + + if (gic_cpu_ns_access(s, cpu, attrs) && !group) { DPRINTF("Non-secure DI for Group0 interrupt %d ignored\n", irq); return; } - GIC_CLEAR_ACTIVE(irq, cm); + gic_clear_active(s, irq, cpu); } -void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) +static void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) { int cm = 1 << cpu; int group; DPRINTF("EOI %d\n", irq); + if (gic_is_vcpu(cpu)) { + /* The call to gic_prio_drop() will clear a bit in GICH_APR iff the + * running prio is < 0x100. + */ + bool prio_drop = s->running_priority[cpu] < 0x100; + + if (irq >= GIC_MAXIRQ) { + /* Ignore spurious interrupt */ + return; + } + + gic_drop_prio(s, cpu, 0); + + if (!gic_eoi_split(s, cpu, attrs)) { + bool valid = gic_virq_is_valid(s, irq, cpu); + if (prio_drop && !valid) { + /* We are in a situation where: + * - V_CTRL.EOIMode is false (no EOI split), + * - The call to gic_drop_prio() cleared a bit in GICH_APR, + * - This vIRQ does not have an LR entry which is either + * active or pending and active. + * In that case, we must increment EOICount. + */ + int rcpu = gic_get_vcpu_real_id(cpu); + s->h_hcr[rcpu] += 1 << R_GICH_HCR_EOICount_SHIFT; + } else if (valid) { + gic_clear_active(s, irq, cpu); + } + } + + gic_update_virt(s); + return; + } + if (irq >= s->num_irq) { /* This handles two cases: * 1. If software writes the ID of a spurious interrupt [ie 1023] @@ -598,16 +888,17 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) if (s->revision == REV_11MPCORE) { /* Mark level triggered interrupts as pending if they are still raised. */ - if (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_ENABLED(irq, cm) - && GIC_TEST_LEVEL(irq, cm) && (GIC_TARGET(irq) & cm) != 0) { + if (!GIC_DIST_TEST_EDGE_TRIGGER(irq) && GIC_DIST_TEST_ENABLED(irq, cm) + && GIC_DIST_TEST_LEVEL(irq, cm) + && (GIC_DIST_TARGET(irq) & cm) != 0) { DPRINTF("Set %d pending mask %x\n", irq, cm); - GIC_SET_PENDING(irq, cm); + GIC_DIST_SET_PENDING(irq, cm); } } - group = gic_has_groups(s) && GIC_TEST_GROUP(irq, cm); + group = gic_has_groups(s) && gic_test_group(s, irq, cpu); - if (s->security_extn && !attrs.secure && !group) { + if (gic_cpu_ns_access(s, cpu, attrs) && !group) { DPRINTF("Non-secure EOI for Group0 interrupt %d ignored\n", irq); return; } @@ -621,7 +912,7 @@ void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs) /* 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_clear_active(s, irq, cpu); } gic_update(s); } @@ -664,12 +955,12 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) res = 0; if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) { /* Every byte offset holds 8 group status bits */ - irq = (offset - 0x080) * 8 + GIC_BASE_IRQ; + irq = (offset - 0x080) * 8; if (irq >= s->num_irq) { goto bad_reg; } for (i = 0; i < 8; i++) { - if (GIC_TEST_GROUP(irq + i, cm)) { + if (GIC_DIST_TEST_GROUP(irq + i, cm)) { res |= (1 << i); } } @@ -683,17 +974,16 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) irq = (offset - 0x100) * 8; else irq = (offset - 0x180) * 8; - irq += GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; res = 0; for (i = 0; i < 8; i++) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - if (GIC_TEST_ENABLED(irq + i, cm)) { + if (GIC_DIST_TEST_ENABLED(irq + i, cm)) { res |= (1 << i); } } @@ -703,14 +993,13 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) irq = (offset - 0x200) * 8; else irq = (offset - 0x280) * 8; - irq += GIC_BASE_IRQ; if (irq >= s->num_irq) goto bad_reg; res = 0; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } @@ -719,35 +1008,42 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) } } } else if (offset < 0x400) { - /* Interrupt Active. */ - irq = (offset - 0x300) * 8 + GIC_BASE_IRQ; + /* Interrupt Set/Clear Active. */ + if (offset < 0x380) { + irq = (offset - 0x300) * 8; + } else if (s->revision == 2) { + irq = (offset - 0x380) * 8; + } else { + goto bad_reg; + } + if (irq >= s->num_irq) goto bad_reg; res = 0; mask = (irq < GIC_INTERNAL) ? cm : ALL_CPU_MASK; for (i = 0; i < 8; i++) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - if (GIC_TEST_ACTIVE(irq + i, mask)) { + if (GIC_DIST_TEST_ACTIVE(irq + i, mask)) { res |= (1 << i); } } } else if (offset < 0x800) { /* Interrupt Priority. */ - irq = (offset - 0x400) + GIC_BASE_IRQ; + irq = (offset - 0x400); if (irq >= s->num_irq) goto bad_reg; - res = gic_get_priority(s, cpu, irq, attrs); + res = gic_dist_get_priority(s, cpu, irq, attrs); } else if (offset < 0xc00) { /* Interrupt CPU Target. */ if (s->num_cpu == 1 && s->revision != REV_11MPCORE) { /* For uniprocessor GICs these RAZ/WI */ res = 0; } else { - irq = (offset - 0x800) + GIC_BASE_IRQ; + irq = (offset - 0x800); if (irq >= s->num_irq) { goto bad_reg; } @@ -756,25 +1052,27 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) } else if (irq < GIC_INTERNAL) { res = cm; } else { - res = GIC_TARGET(irq); + res = GIC_DIST_TARGET(irq); } } } else if (offset < 0xf00) { /* Interrupt Configuration. */ - irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; + irq = (offset - 0xc00) * 4; if (irq >= s->num_irq) goto bad_reg; res = 0; for (i = 0; i < 4; i++) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - if (GIC_TEST_MODEL(irq + i)) + if (GIC_DIST_TEST_MODEL(irq + i)) { res |= (1 << (i * 2)); - if (GIC_TEST_EDGE_TRIGGER(irq + i)) + } + if (GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) { res |= (2 << (i * 2)); + } } } else if (offset < 0xf10) { goto bad_reg; @@ -792,7 +1090,7 @@ static uint32_t gic_dist_readb(void *opaque, hwaddr offset, MemTxAttrs attrs) } if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq, 1 << cpu)) { res = 0; /* Ignore Non-secure access of Group0 IRQ */ } else { res = s->sgi_pending[irq][cpu]; @@ -833,20 +1131,23 @@ static MemTxResult gic_dist_read(void *opaque, hwaddr offset, uint64_t *data, switch (size) { case 1: *data = gic_dist_readb(opaque, offset, attrs); - return MEMTX_OK; + break; case 2: *data = gic_dist_readb(opaque, offset, attrs); *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8; - return MEMTX_OK; + break; case 4: *data = gic_dist_readb(opaque, offset, attrs); *data |= gic_dist_readb(opaque, offset + 1, attrs) << 8; *data |= gic_dist_readb(opaque, offset + 2, attrs) << 16; *data |= gic_dist_readb(opaque, offset + 3, attrs) << 24; - return MEMTX_OK; + break; default: return MEMTX_ERROR; } + + trace_gic_dist_read(offset, size, *data); + return MEMTX_OK; } static void gic_dist_writeb(void *opaque, hwaddr offset, @@ -879,7 +1180,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, */ if (!(s->security_extn && !attrs.secure) && gic_has_groups(s)) { /* Every byte offset holds 8 group status bits */ - irq = (offset - 0x80) * 8 + GIC_BASE_IRQ; + irq = (offset - 0x80) * 8; if (irq >= s->num_irq) { goto bad_reg; } @@ -888,10 +1189,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (value & (1 << i)) { /* Group1 (Non-secure) */ - GIC_SET_GROUP(irq + i, cm); + GIC_DIST_SET_GROUP(irq + i, cm); } else { /* Group0 (Secure) */ - GIC_CLEAR_GROUP(irq + i, cm); + GIC_DIST_CLEAR_GROUP(irq + i, cm); } } } @@ -900,7 +1201,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } } else if (offset < 0x180) { /* Interrupt Set Enable. */ - irq = (offset - 0x100) * 8 + GIC_BASE_IRQ; + irq = (offset - 0x100) * 8; if (irq >= s->num_irq) goto bad_reg; if (irq < GIC_NR_SGIS) { @@ -910,31 +1211,32 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for (i = 0; i < 8; i++) { if (value & (1 << i)) { int mask = - (irq < GIC_INTERNAL) ? (1 << cpu) : GIC_TARGET(irq + i); + (irq < GIC_INTERNAL) ? (1 << cpu) + : GIC_DIST_TARGET(irq + i); int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - if (!GIC_TEST_ENABLED(irq + i, cm)) { + if (!GIC_DIST_TEST_ENABLED(irq + i, cm)) { DPRINTF("Enabled IRQ %d\n", irq + i); trace_gic_enable_irq(irq + i); } - GIC_SET_ENABLED(irq + i, cm); + GIC_DIST_SET_ENABLED(irq + i, cm); /* If a raised level triggered IRQ enabled then mark is as pending. */ - if (GIC_TEST_LEVEL(irq + i, mask) - && !GIC_TEST_EDGE_TRIGGER(irq + i)) { + if (GIC_DIST_TEST_LEVEL(irq + i, mask) + && !GIC_DIST_TEST_EDGE_TRIGGER(irq + i)) { DPRINTF("Set %d pending mask %x\n", irq + i, mask); - GIC_SET_PENDING(irq + i, mask); + GIC_DIST_SET_PENDING(irq + i, mask); } } } } else if (offset < 0x200) { /* Interrupt Clear Enable. */ - irq = (offset - 0x180) * 8 + GIC_BASE_IRQ; + irq = (offset - 0x180) * 8; if (irq >= s->num_irq) goto bad_reg; if (irq < GIC_NR_SGIS) { @@ -946,20 +1248,20 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - if (GIC_TEST_ENABLED(irq + i, cm)) { + if (GIC_DIST_TEST_ENABLED(irq + i, cm)) { DPRINTF("Disabled IRQ %d\n", irq + i); trace_gic_disable_irq(irq + i); } - GIC_CLEAR_ENABLED(irq + i, cm); + GIC_DIST_CLEAR_ENABLED(irq + i, cm); } } } else if (offset < 0x280) { /* Interrupt Set Pending. */ - irq = (offset - 0x200) * 8 + GIC_BASE_IRQ; + irq = (offset - 0x200) * 8; if (irq >= s->num_irq) goto bad_reg; if (irq < GIC_NR_SGIS) { @@ -969,16 +1271,16 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for (i = 0; i < 8; i++) { if (value & (1 << i)) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } - GIC_SET_PENDING(irq + i, GIC_TARGET(irq + i)); + GIC_DIST_SET_PENDING(irq + i, GIC_DIST_TARGET(irq + i)); } } } else if (offset < 0x300) { /* Interrupt Clear Pending. */ - irq = (offset - 0x280) * 8 + GIC_BASE_IRQ; + irq = (offset - 0x280) * 8; if (irq >= s->num_irq) goto bad_reg; if (irq < GIC_NR_SGIS) { @@ -987,7 +1289,7 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for (i = 0; i < 8; i++) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } @@ -995,24 +1297,69 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, for per-CPU interrupts. It's unclear whether this is the corect behavior. */ if (value & (1 << i)) { - GIC_CLEAR_PENDING(irq + i, ALL_CPU_MASK); + GIC_DIST_CLEAR_PENDING(irq + i, ALL_CPU_MASK); + } + } + } else if (offset < 0x380) { + /* Interrupt Set Active. */ + if (s->revision != 2) { + goto bad_reg; + } + + irq = (offset - 0x300) * 8; + if (irq >= s->num_irq) { + goto bad_reg; + } + + /* This register is banked per-cpu for PPIs */ + int cm = irq < GIC_INTERNAL ? (1 << cpu) : ALL_CPU_MASK; + + for (i = 0; i < 8; i++) { + if (s->security_extn && !attrs.secure && + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + + if (value & (1 << i)) { + GIC_DIST_SET_ACTIVE(irq + i, cm); } } } else if (offset < 0x400) { - /* Interrupt Active. */ - goto bad_reg; + /* Interrupt Clear Active. */ + if (s->revision != 2) { + goto bad_reg; + } + + irq = (offset - 0x380) * 8; + if (irq >= s->num_irq) { + goto bad_reg; + } + + /* This register is banked per-cpu for PPIs */ + int cm = irq < GIC_INTERNAL ? (1 << cpu) : ALL_CPU_MASK; + + for (i = 0; i < 8; i++) { + if (s->security_extn && !attrs.secure && + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { + continue; /* Ignore Non-secure access of Group0 IRQ */ + } + + if (value & (1 << i)) { + GIC_DIST_CLEAR_ACTIVE(irq + i, cm); + } + } } else if (offset < 0x800) { /* Interrupt Priority. */ - irq = (offset - 0x400) + GIC_BASE_IRQ; + irq = (offset - 0x400); if (irq >= s->num_irq) goto bad_reg; - gic_set_priority(s, cpu, irq, value, attrs); + gic_dist_set_priority(s, cpu, irq, value, attrs); } else if (offset < 0xc00) { /* Interrupt CPU Target. RAZ/WI on uniprocessor GICs, with the * annoying exception of the 11MPCore's GIC. */ if (s->num_cpu != 1 || s->revision == REV_11MPCORE) { - irq = (offset - 0x800) + GIC_BASE_IRQ; + irq = (offset - 0x800); if (irq >= s->num_irq) { goto bad_reg; } @@ -1025,28 +1372,28 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, } } else if (offset < 0xf00) { /* Interrupt Configuration. */ - irq = (offset - 0xc00) * 4 + GIC_BASE_IRQ; + irq = (offset - 0xc00) * 4; if (irq >= s->num_irq) goto bad_reg; if (irq < GIC_NR_SGIS) value |= 0xaa; for (i = 0; i < 4; i++) { if (s->security_extn && !attrs.secure && - !GIC_TEST_GROUP(irq + i, 1 << cpu)) { + !GIC_DIST_TEST_GROUP(irq + i, 1 << cpu)) { continue; /* Ignore Non-secure access of Group0 IRQ */ } if (s->revision == REV_11MPCORE) { if (value & (1 << (i * 2))) { - GIC_SET_MODEL(irq + i); + GIC_DIST_SET_MODEL(irq + i); } else { - GIC_CLEAR_MODEL(irq + i); + GIC_DIST_CLEAR_MODEL(irq + i); } } if (value & (2 << (i * 2))) { - GIC_SET_EDGE_TRIGGER(irq + i); + GIC_DIST_SET_EDGE_TRIGGER(irq + i); } else { - GIC_CLEAR_EDGE_TRIGGER(irq + i); + GIC_DIST_CLEAR_EDGE_TRIGGER(irq + i); } } } else if (offset < 0xf10) { @@ -1060,10 +1407,10 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, irq = (offset - 0xf10); if (!s->security_extn || attrs.secure || - GIC_TEST_GROUP(irq, 1 << cpu)) { + GIC_DIST_TEST_GROUP(irq, 1 << cpu)) { s->sgi_pending[irq][cpu] &= ~value; if (s->sgi_pending[irq][cpu] == 0) { - GIC_CLEAR_PENDING(irq, 1 << cpu); + GIC_DIST_CLEAR_PENDING(irq, 1 << cpu); } } } else if (offset < 0xf30) { @@ -1074,8 +1421,8 @@ static void gic_dist_writeb(void *opaque, hwaddr offset, irq = (offset - 0xf20); if (!s->security_extn || attrs.secure || - GIC_TEST_GROUP(irq, 1 << cpu)) { - GIC_SET_PENDING(irq, 1 << cpu); + GIC_DIST_TEST_GROUP(irq, 1 << cpu)) { + GIC_DIST_SET_PENDING(irq, 1 << cpu); s->sgi_pending[irq][cpu] |= value; } } else { @@ -1122,7 +1469,7 @@ static void gic_dist_writel(void *opaque, hwaddr offset, mask = ALL_CPU_MASK; break; } - GIC_SET_PENDING(irq, mask); + GIC_DIST_SET_PENDING(irq, mask); target_cpu = ctz32(mask); while (target_cpu < GIC_NCPU) { s->sgi_pending[irq][target_cpu] |= (1 << cpu); @@ -1139,6 +1486,8 @@ static void gic_dist_writel(void *opaque, hwaddr offset, static MemTxResult gic_dist_write(void *opaque, hwaddr offset, uint64_t data, unsigned size, MemTxAttrs attrs) { + trace_gic_dist_write(offset, size, data); + switch (size) { case 1: gic_dist_writeb(opaque, offset, data, attrs); @@ -1227,7 +1576,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, *data = gic_get_priority_mask(s, cpu, attrs); break; case 0x08: /* Binary Point */ - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { if (s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) { /* NS view of BPR when CBPR is 1 */ *data = MIN(s->bpr[cpu] + 1, 7); @@ -1254,7 +1603,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, * With security extensions, secure access: ABPR (alias of NS BPR) * With security extensions, nonsecure access: RAZ/WI */ - if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { + if (!gic_has_groups(s) || (gic_cpu_ns_access(s, cpu, attrs))) { *data = 0; } else { *data = s->abpr[cpu]; @@ -1263,10 +1612,13 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, case 0xd0: case 0xd4: case 0xd8: case 0xdc: { int regno = (offset - 0xd0) / 4; + int nr_aprs = gic_is_vcpu(cpu) ? GIC_VIRT_NR_APRS : GIC_NR_APRS; - if (regno >= GIC_NR_APRS || s->revision != 2) { + if (regno >= nr_aprs || s->revision != 2) { *data = 0; - } else if (s->security_extn && !attrs.secure) { + } else if (gic_is_vcpu(cpu)) { + *data = s->h_apr[gic_get_vcpu_real_id(cpu)]; + } else if (gic_cpu_ns_access(s, cpu, attrs)) { /* NS view of GICC_APR is the top half of GIC_NSAPR */ *data = gic_apr_ns_view(s, regno, cpu); } else { @@ -1279,7 +1631,7 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, int regno = (offset - 0xe0) / 4; if (regno >= GIC_NR_APRS || s->revision != 2 || !gic_has_groups(s) || - (s->security_extn && !attrs.secure)) { + gic_cpu_ns_access(s, cpu, attrs) || gic_is_vcpu(cpu)) { *data = 0; } else { *data = s->nsapr[regno][cpu]; @@ -1292,12 +1644,18 @@ static MemTxResult gic_cpu_read(GICState *s, int cpu, int offset, *data = 0; break; } + + trace_gic_cpu_read(gic_is_vcpu(cpu) ? "vcpu" : "cpu", + gic_get_vcpu_real_id(cpu), offset, *data); return MEMTX_OK; } static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, uint32_t value, MemTxAttrs attrs) { + trace_gic_cpu_write(gic_is_vcpu(cpu) ? "vcpu" : "cpu", + gic_get_vcpu_real_id(cpu), offset, value); + switch (offset) { case 0x00: /* Control */ gic_set_cpu_control(s, cpu, value, attrs); @@ -1306,7 +1664,7 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, gic_set_priority_mask(s, cpu, value, attrs); break; case 0x08: /* Binary Point */ - if (s->security_extn && !attrs.secure) { + if (gic_cpu_ns_access(s, cpu, attrs)) { if (s->cpu_ctlr[cpu] & GICC_CTLR_CBPR) { /* WI when CBPR is 1 */ return MEMTX_OK; @@ -1314,14 +1672,15 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, s->abpr[cpu] = MAX(value & 0x7, GIC_MIN_ABPR); } } else { - s->bpr[cpu] = MAX(value & 0x7, GIC_MIN_BPR); + int min_bpr = gic_is_vcpu(cpu) ? GIC_VIRT_MIN_BPR : GIC_MIN_BPR; + s->bpr[cpu] = MAX(value & 0x7, min_bpr); } break; case 0x10: /* End Of Interrupt */ gic_complete_irq(s, cpu, value & 0x3ff, attrs); return MEMTX_OK; case 0x1c: /* Aliased Binary Point */ - if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { + if (!gic_has_groups(s) || (gic_cpu_ns_access(s, cpu, attrs))) { /* unimplemented, or NS access: RAZ/WI */ return MEMTX_OK; } else { @@ -1331,11 +1690,14 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, case 0xd0: case 0xd4: case 0xd8: case 0xdc: { int regno = (offset - 0xd0) / 4; + int nr_aprs = gic_is_vcpu(cpu) ? GIC_VIRT_NR_APRS : GIC_NR_APRS; - if (regno >= GIC_NR_APRS || s->revision != 2) { + if (regno >= nr_aprs || s->revision != 2) { return MEMTX_OK; } - if (s->security_extn && !attrs.secure) { + if (gic_is_vcpu(cpu)) { + s->h_apr[gic_get_vcpu_real_id(cpu)] = value; + } else if (gic_cpu_ns_access(s, cpu, attrs)) { /* NS view of GICC_APR is the top half of GIC_NSAPR */ gic_apr_write_ns_view(s, regno, cpu, value); } else { @@ -1350,7 +1712,10 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, if (regno >= GIC_NR_APRS || s->revision != 2) { return MEMTX_OK; } - if (!gic_has_groups(s) || (s->security_extn && !attrs.secure)) { + if (gic_is_vcpu(cpu)) { + return MEMTX_OK; + } + if (!gic_has_groups(s) || (gic_cpu_ns_access(s, cpu, attrs))) { return MEMTX_OK; } s->nsapr[regno][cpu] = value; @@ -1365,7 +1730,13 @@ static MemTxResult gic_cpu_write(GICState *s, int cpu, int offset, "gic_cpu_write: Bad offset %x\n", (int)offset); return MEMTX_OK; } - gic_update(s); + + if (gic_is_vcpu(cpu)) { + gic_update_virt(s); + } else { + gic_update(s); + } + return MEMTX_OK; } @@ -1407,6 +1778,222 @@ static MemTxResult gic_do_cpu_write(void *opaque, hwaddr addr, return gic_cpu_write(s, id, addr, value, attrs); } +static MemTxResult gic_thisvcpu_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + GICState *s = (GICState *)opaque; + + return gic_cpu_read(s, gic_get_current_vcpu(s), addr, data, attrs); +} + +static MemTxResult gic_thisvcpu_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + GICState *s = (GICState *)opaque; + + return gic_cpu_write(s, gic_get_current_vcpu(s), addr, value, attrs); +} + +static uint32_t gic_compute_eisr(GICState *s, int cpu, int lr_start) +{ + int lr_idx; + uint32_t ret = 0; + + for (lr_idx = lr_start; lr_idx < s->num_lrs; lr_idx++) { + uint32_t *entry = &s->h_lr[lr_idx][cpu]; + ret = deposit32(ret, lr_idx - lr_start, 1, + gic_lr_entry_is_eoi(*entry)); + } + + return ret; +} + +static uint32_t gic_compute_elrsr(GICState *s, int cpu, int lr_start) +{ + int lr_idx; + uint32_t ret = 0; + + for (lr_idx = lr_start; lr_idx < s->num_lrs; lr_idx++) { + uint32_t *entry = &s->h_lr[lr_idx][cpu]; + ret = deposit32(ret, lr_idx - lr_start, 1, + gic_lr_entry_is_free(*entry)); + } + + return ret; +} + +static void gic_vmcr_write(GICState *s, uint32_t value, MemTxAttrs attrs) +{ + int vcpu = gic_get_current_vcpu(s); + uint32_t ctlr; + uint32_t abpr; + uint32_t bpr; + uint32_t prio_mask; + + ctlr = FIELD_EX32(value, GICH_VMCR, VMCCtlr); + abpr = FIELD_EX32(value, GICH_VMCR, VMABP); + bpr = FIELD_EX32(value, GICH_VMCR, VMBP); + prio_mask = FIELD_EX32(value, GICH_VMCR, VMPriMask) << 3; + + gic_set_cpu_control(s, vcpu, ctlr, attrs); + s->abpr[vcpu] = MAX(abpr, GIC_VIRT_MIN_ABPR); + s->bpr[vcpu] = MAX(bpr, GIC_VIRT_MIN_BPR); + gic_set_priority_mask(s, vcpu, prio_mask, attrs); +} + +static MemTxResult gic_hyp_read(void *opaque, int cpu, hwaddr addr, + uint64_t *data, MemTxAttrs attrs) +{ + GICState *s = ARM_GIC(opaque); + int vcpu = cpu + GIC_NCPU; + + switch (addr) { + case A_GICH_HCR: /* Hypervisor Control */ + *data = s->h_hcr[cpu]; + break; + + case A_GICH_VTR: /* VGIC Type */ + *data = FIELD_DP32(0, GICH_VTR, ListRegs, s->num_lrs - 1); + *data = FIELD_DP32(*data, GICH_VTR, PREbits, + GIC_VIRT_MAX_GROUP_PRIO_BITS - 1); + *data = FIELD_DP32(*data, GICH_VTR, PRIbits, + (7 - GIC_VIRT_MIN_BPR) - 1); + break; + + case A_GICH_VMCR: /* Virtual Machine Control */ + *data = FIELD_DP32(0, GICH_VMCR, VMCCtlr, + extract32(s->cpu_ctlr[vcpu], 0, 10)); + *data = FIELD_DP32(*data, GICH_VMCR, VMABP, s->abpr[vcpu]); + *data = FIELD_DP32(*data, GICH_VMCR, VMBP, s->bpr[vcpu]); + *data = FIELD_DP32(*data, GICH_VMCR, VMPriMask, + extract32(s->priority_mask[vcpu], 3, 5)); + break; + + case A_GICH_MISR: /* Maintenance Interrupt Status */ + *data = s->h_misr[cpu]; + break; + + case A_GICH_EISR0: /* End of Interrupt Status 0 and 1 */ + case A_GICH_EISR1: + *data = gic_compute_eisr(s, cpu, (addr - A_GICH_EISR0) * 8); + break; + + case A_GICH_ELRSR0: /* Empty List Status 0 and 1 */ + case A_GICH_ELRSR1: + *data = gic_compute_elrsr(s, cpu, (addr - A_GICH_ELRSR0) * 8); + break; + + case A_GICH_APR: /* Active Priorities */ + *data = s->h_apr[cpu]; + break; + + case A_GICH_LR0 ... A_GICH_LR63: /* List Registers */ + { + int lr_idx = (addr - A_GICH_LR0) / 4; + + if (lr_idx > s->num_lrs) { + *data = 0; + } else { + *data = s->h_lr[lr_idx][cpu]; + } + break; + } + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "gic_hyp_read: Bad offset %" HWADDR_PRIx "\n", addr); + return MEMTX_OK; + } + + trace_gic_hyp_read(addr, *data); + return MEMTX_OK; +} + +static MemTxResult gic_hyp_write(void *opaque, int cpu, hwaddr addr, + uint64_t value, MemTxAttrs attrs) +{ + GICState *s = ARM_GIC(opaque); + int vcpu = cpu + GIC_NCPU; + + trace_gic_hyp_write(addr, value); + + switch (addr) { + case A_GICH_HCR: /* Hypervisor Control */ + s->h_hcr[cpu] = value & GICH_HCR_MASK; + break; + + case A_GICH_VMCR: /* Virtual Machine Control */ + gic_vmcr_write(s, value, attrs); + break; + + case A_GICH_APR: /* Active Priorities */ + s->h_apr[cpu] = value; + s->running_priority[vcpu] = gic_get_prio_from_apr_bits(s, vcpu); + break; + + case A_GICH_LR0 ... A_GICH_LR63: /* List Registers */ + { + int lr_idx = (addr - A_GICH_LR0) / 4; + + if (lr_idx > s->num_lrs) { + return MEMTX_OK; + } + + s->h_lr[lr_idx][cpu] = value & GICH_LR_MASK; + trace_gic_lr_entry(cpu, lr_idx, s->h_lr[lr_idx][cpu]); + break; + } + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "gic_hyp_write: Bad offset %" HWADDR_PRIx "\n", addr); + return MEMTX_OK; + } + + gic_update_virt(s); + return MEMTX_OK; +} + +static MemTxResult gic_thiscpu_hyp_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + GICState *s = (GICState *)opaque; + + return gic_hyp_read(s, gic_get_current_cpu(s), addr, data, attrs); +} + +static MemTxResult gic_thiscpu_hyp_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + GICState *s = (GICState *)opaque; + + return gic_hyp_write(s, gic_get_current_cpu(s), addr, value, attrs); +} + +static MemTxResult gic_do_hyp_read(void *opaque, hwaddr addr, uint64_t *data, + unsigned size, MemTxAttrs attrs) +{ + GICState **backref = (GICState **)opaque; + GICState *s = *backref; + int id = (backref - s->backref); + + return gic_hyp_read(s, id, addr, data, attrs); +} + +static MemTxResult gic_do_hyp_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size, + MemTxAttrs attrs) +{ + GICState **backref = (GICState **)opaque; + GICState *s = *backref; + int id = (backref - s->backref); + + return gic_hyp_write(s, id + GIC_NCPU, addr, value, attrs); + +} + static const MemoryRegionOps gic_ops[2] = { { .read_with_attrs = gic_dist_read, @@ -1426,11 +2013,24 @@ 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) -{ - gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); -} +static const MemoryRegionOps gic_virt_ops[2] = { + { + .read_with_attrs = gic_thiscpu_hyp_read, + .write_with_attrs = gic_thiscpu_hyp_write, + .endianness = DEVICE_NATIVE_ENDIAN, + }, + { + .read_with_attrs = gic_thisvcpu_read, + .write_with_attrs = gic_thisvcpu_write, + .endianness = DEVICE_NATIVE_ENDIAN, + } +}; + +static const MemoryRegionOps gic_viface_ops = { + .read_with_attrs = gic_do_hyp_read, + .write_with_attrs = gic_do_hyp_write, + .endianness = DEVICE_NATIVE_ENDIAN, +}; static void arm_gic_realize(DeviceState *dev, Error **errp) { @@ -1453,8 +2053,11 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) return; } - /* This creates distributor and main CPU interface (s->cpuiomem[0]) */ - gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops); + /* This creates distributor, main CPU interface (s->cpuiomem[0]) and if + * enabled, virtualization extensions related interfaces (main virtual + * interface (s->vifaceiomem[0]) and virtual CPU interface). + */ + gic_init_irqs_and_mmio(s, gic_set_irq, gic_ops, gic_virt_ops); /* Extra core-specific regions for the CPU interfaces. This is * necessary for "franken-GIC" implementations, for example on @@ -1470,6 +2073,19 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) &s->backref[i], "gic_cpu", 0x100); sysbus_init_mmio(sbd, &s->cpuiomem[i+1]); } + + /* Extra core-specific regions for virtual interfaces. This is required by + * the GICv2 specification. + */ + if (s->virt_extn) { + for (i = 0; i < s->num_cpu; i++) { + memory_region_init_io(&s->vifaceiomem[i + 1], OBJECT(s), + &gic_viface_ops, &s->backref[i], + "gic_viface", 0x200); + sysbus_init_mmio(sbd, &s->vifaceiomem[i + 1]); + } + } + } static void arm_gic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index aee50a20e0..57569a4e59 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -46,6 +46,13 @@ static int gic_post_load(void *opaque, int version_id) return 0; } +static bool gic_virt_state_needed(void *opaque) +{ + GICState *s = (GICState *)opaque; + + return s->virt_extn; +} + static const VMStateDescription vmstate_gic_irq_state = { .name = "arm_gic_irq_state", .version_id = 1, @@ -62,6 +69,30 @@ static const VMStateDescription vmstate_gic_irq_state = { } }; +static const VMStateDescription vmstate_gic_virt_state = { + .name = "arm_gic_virt_state", + .version_id = 1, + .minimum_version_id = 1, + .needed = gic_virt_state_needed, + .fields = (VMStateField[]) { + /* Virtual interface */ + VMSTATE_UINT32_ARRAY(h_hcr, GICState, GIC_NCPU), + VMSTATE_UINT32_ARRAY(h_misr, GICState, GIC_NCPU), + VMSTATE_UINT32_2DARRAY(h_lr, GICState, GIC_MAX_LR, GIC_NCPU), + VMSTATE_UINT32_ARRAY(h_apr, GICState, GIC_NCPU), + + /* Virtual CPU interfaces */ + VMSTATE_UINT32_SUB_ARRAY(cpu_ctlr, GICState, GIC_NCPU, GIC_NCPU), + VMSTATE_UINT16_SUB_ARRAY(priority_mask, GICState, GIC_NCPU, GIC_NCPU), + VMSTATE_UINT16_SUB_ARRAY(running_priority, GICState, GIC_NCPU, GIC_NCPU), + VMSTATE_UINT16_SUB_ARRAY(current_pending, GICState, GIC_NCPU, GIC_NCPU), + VMSTATE_UINT8_SUB_ARRAY(bpr, GICState, GIC_NCPU, GIC_NCPU), + VMSTATE_UINT8_SUB_ARRAY(abpr, GICState, GIC_NCPU, GIC_NCPU), + + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_gic = { .name = "arm_gic", .version_id = 12, @@ -70,26 +101,31 @@ static const VMStateDescription vmstate_gic = { .post_load = gic_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32(ctlr, GICState), - VMSTATE_UINT32_ARRAY(cpu_ctlr, GICState, GIC_NCPU), + VMSTATE_UINT32_SUB_ARRAY(cpu_ctlr, GICState, 0, GIC_NCPU), VMSTATE_STRUCT_ARRAY(irq_state, GICState, GIC_MAXIRQ, 1, vmstate_gic_irq_state, gic_irq_state), 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_UINT8_2DARRAY(sgi_pending, GICState, GIC_NR_SGIS, GIC_NCPU), - VMSTATE_UINT16_ARRAY(priority_mask, 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_UINT16_SUB_ARRAY(priority_mask, GICState, 0, GIC_NCPU), + VMSTATE_UINT16_SUB_ARRAY(running_priority, GICState, 0, GIC_NCPU), + VMSTATE_UINT16_SUB_ARRAY(current_pending, GICState, 0, GIC_NCPU), + VMSTATE_UINT8_SUB_ARRAY(bpr, GICState, 0, GIC_NCPU), + VMSTATE_UINT8_SUB_ARRAY(abpr, GICState, 0, 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() + }, + .subsections = (const VMStateDescription * []) { + &vmstate_gic_virt_state, + NULL } }; void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, - const MemoryRegionOps *ops) + const MemoryRegionOps *ops, + const MemoryRegionOps *virt_ops) { SysBusDevice *sbd = SYS_BUS_DEVICE(s); int i = s->num_irq - GIC_INTERNAL; @@ -116,6 +152,11 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, for (i = 0; i < s->num_cpu; i++) { sysbus_init_irq(sbd, &s->parent_vfiq[i]); } + if (s->virt_extn) { + for (i = 0; i < s->num_cpu; i++) { + sysbus_init_irq(sbd, &s->maintenance_irq[i]); + } + } /* Distributor */ memory_region_init_io(&s->iomem, OBJECT(s), ops, s, "gic_dist", 0x1000); @@ -127,6 +168,17 @@ void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, 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]); + + if (s->virt_extn) { + memory_region_init_io(&s->vifaceiomem[0], OBJECT(s), virt_ops, + s, "gic_viface", 0x1000); + sysbus_init_mmio(sbd, &s->vifaceiomem[0]); + + memory_region_init_io(&s->vcpuiomem, OBJECT(s), + virt_ops ? &virt_ops[1] : NULL, + s, "gic_vcpu", 0x2000); + sysbus_init_mmio(sbd, &s->vcpuiomem); + } } static void arm_gic_common_realize(DeviceState *dev, Error **errp) @@ -139,7 +191,6 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) s->num_cpu, GIC_NCPU); return; } - s->num_irq += GIC_BASE_IRQ; if (s->num_irq > GIC_MAXIRQ) { error_setg(errp, "requested %u interrupt lines exceeds GIC maximum %d", @@ -163,6 +214,48 @@ static void arm_gic_common_realize(DeviceState *dev, Error **errp) "the security extensions"); return; } + + if (s->virt_extn) { + if (s->revision != 2) { + error_setg(errp, "GIC virtualization extensions are only " + "supported by revision 2"); + return; + } + + /* For now, set the number of implemented LRs to 4, as found in most + * real GICv2. This could be promoted as a QOM property if we need to + * emulate a variant with another num_lrs. + */ + s->num_lrs = 4; + } +} + +static inline void arm_gic_common_reset_irq_state(GICState *s, int first_cpu, + int resetprio) +{ + int i, j; + + for (i = first_cpu; i < first_cpu + s->num_cpu; i++) { + if (s->revision == REV_11MPCORE) { + s->priority_mask[i] = 0xf0; + } else { + s->priority_mask[i] = resetprio; + } + s->current_pending[i] = 1023; + s->running_priority[i] = 0x100; + s->cpu_ctlr[i] = 0; + s->bpr[i] = gic_is_vcpu(i) ? GIC_VIRT_MIN_BPR : GIC_MIN_BPR; + s->abpr[i] = gic_is_vcpu(i) ? GIC_VIRT_MIN_ABPR : GIC_MIN_ABPR; + + if (!gic_is_vcpu(i)) { + for (j = 0; j < GIC_INTERNAL; j++) { + s->priority1[j][i] = resetprio; + } + for (j = 0; j < GIC_NR_SGIS; j++) { + s->sgi_pending[j][i] = 0; + } + } + } } static void arm_gic_common_reset(DeviceState *dev) @@ -185,27 +278,18 @@ static void arm_gic_common_reset(DeviceState *dev) } 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] = resetprio; - } - s->current_pending[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] = resetprio; - } - for (j = 0; j < GIC_NR_SGIS; j++) { - s->sgi_pending[j][i] = 0; - } + arm_gic_common_reset_irq_state(s, 0, resetprio); + + if (s->virt_extn) { + /* vCPU states are stored at indexes GIC_NCPU .. GIC_NCPU+num_cpu. + * The exposed vCPU interface does not have security extensions. + */ + arm_gic_common_reset_irq_state(s, GIC_NCPU, 0); } + for (i = 0; i < GIC_NR_SGIS; i++) { - GIC_SET_ENABLED(i, ALL_CPU_MASK); - GIC_SET_EDGE_TRIGGER(i); + GIC_DIST_SET_ENABLED(i, ALL_CPU_MASK); + GIC_DIST_SET_EDGE_TRIGGER(i); } for (i = 0; i < ARRAY_SIZE(s->priority2); i++) { @@ -222,7 +306,20 @@ static void arm_gic_common_reset(DeviceState *dev) } if (s->security_extn && s->irq_reset_nonsecure) { for (i = 0; i < GIC_MAXIRQ; i++) { - GIC_SET_GROUP(i, ALL_CPU_MASK); + GIC_DIST_SET_GROUP(i, ALL_CPU_MASK); + } + } + + if (s->virt_extn) { + for (i = 0; i < s->num_lrs; i++) { + for (j = 0; j < s->num_cpu; j++) { + s->h_lr[i][j] = 0; + } + } + + for (i = 0; i < s->num_cpu; i++) { + s->h_hcr[i] = 0; + s->h_misr[i] = 0; } } @@ -255,6 +352,8 @@ static Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("revision", GICState, revision, 1), /* True if the GIC should implement the security extensions */ DEFINE_PROP_BOOL("has-security-extensions", GICState, security_extn, 0), + /* True if the GIC should implement the virtualization extensions */ + DEFINE_PROP_BOOL("has-virtualization-extensions", GICState, virt_extn, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 86665080bd..a611e8ee12 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -140,10 +140,10 @@ static void translate_group(GICState *s, int irq, int cpu, int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (to_kernel) { - *field = GIC_TEST_GROUP(irq, cm); + *field = GIC_DIST_TEST_GROUP(irq, cm); } else { if (*field & 1) { - GIC_SET_GROUP(irq, cm); + GIC_DIST_SET_GROUP(irq, cm); } } } @@ -154,10 +154,10 @@ static void translate_enabled(GICState *s, int irq, int cpu, int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (to_kernel) { - *field = GIC_TEST_ENABLED(irq, cm); + *field = GIC_DIST_TEST_ENABLED(irq, cm); } else { if (*field & 1) { - GIC_SET_ENABLED(irq, cm); + GIC_DIST_SET_ENABLED(irq, cm); } } } @@ -171,7 +171,7 @@ static void translate_pending(GICState *s, int irq, int cpu, *field = gic_test_pending(s, irq, cm); } else { if (*field & 1) { - GIC_SET_PENDING(irq, cm); + GIC_DIST_SET_PENDING(irq, cm); /* TODO: Capture is level-line is held high in the kernel */ } } @@ -183,10 +183,10 @@ static void translate_active(GICState *s, int irq, int cpu, int cm = (irq < GIC_INTERNAL) ? (1 << cpu) : ALL_CPU_MASK; if (to_kernel) { - *field = GIC_TEST_ACTIVE(irq, cm); + *field = GIC_DIST_TEST_ACTIVE(irq, cm); } else { if (*field & 1) { - GIC_SET_ACTIVE(irq, cm); + GIC_DIST_SET_ACTIVE(irq, cm); } } } @@ -195,10 +195,10 @@ static void translate_trigger(GICState *s, int irq, int cpu, uint32_t *field, bool to_kernel) { if (to_kernel) { - *field = (GIC_TEST_EDGE_TRIGGER(irq)) ? 0x2 : 0x0; + *field = (GIC_DIST_TEST_EDGE_TRIGGER(irq)) ? 0x2 : 0x0; } else { if (*field & 0x2) { - GIC_SET_EDGE_TRIGGER(irq); + GIC_DIST_SET_EDGE_TRIGGER(irq); } } } @@ -207,9 +207,10 @@ static void translate_priority(GICState *s, int irq, int cpu, uint32_t *field, bool to_kernel) { if (to_kernel) { - *field = GIC_GET_PRIORITY(irq, cpu) & 0xff; + *field = GIC_DIST_GET_PRIORITY(irq, cpu) & 0xff; } else { - gic_set_priority(s, cpu, irq, *field & 0xff, MEMTXATTRS_UNSPECIFIED); + gic_dist_set_priority(s, cpu, irq, + *field & 0xff, MEMTXATTRS_UNSPECIFIED); } } @@ -510,6 +511,12 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) return; } + if (s->virt_extn) { + error_setg(errp, "the in-kernel VGIC does not implement the " + "virtualization extensions"); + return; + } + if (!kvm_arm_gic_can_save_restore(s)) { error_setg(&s->migration_blocker, "This operating system kernel does " "not support vGICv2 migration"); @@ -521,7 +528,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) } } - gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL); + gic_init_irqs_and_mmio(s, kvm_arm_gicv2_set_irq, NULL, NULL); for (i = 0; i < s->num_irq - GIC_INTERNAL; i++) { qemu_irq irq = qdev_get_gpio_in(dev, i); diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index 2a60568d82..cbad6037f1 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -85,7 +85,10 @@ static bool icv_access(CPUARMState *env, int hcr_flags) * * access if NS EL1 and either IMO or FMO == 1: * CTLR, DIR, PMR, RPR */ - return (env->cp15.hcr_el2 & hcr_flags) && arm_current_el(env) == 1 + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool flagmatch = hcr_el2 & hcr_flags & (HCR_IMO | HCR_FMO); + + return flagmatch && arm_current_el(env) == 1 && !arm_is_secure_below_el3(env); } @@ -1549,8 +1552,9 @@ static void icc_dir_write(CPUARMState *env, const ARMCPRegInfo *ri, /* No need to include !IsSecure in route_*_to_el2 as it's only * tested in cases where we know !IsSecure is true. */ - route_fiq_to_el2 = env->cp15.hcr_el2 & HCR_FMO; - route_irq_to_el2 = env->cp15.hcr_el2 & HCR_IMO; + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + route_fiq_to_el2 = hcr_el2 & HCR_FMO; + route_irq_to_el2 = hcr_el2 & HCR_IMO; switch (arm_current_el(env)) { case 3: @@ -1892,8 +1896,8 @@ static CPAccessResult gicv3_irqfiq_access(CPUARMState *env, if ((env->cp15.scr_el3 & (SCR_FIQ | SCR_IRQ)) == (SCR_FIQ | SCR_IRQ)) { switch (el) { case 1: - if (arm_is_secure_below_el3(env) || - ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) == 0)) { + /* Note that arm_hcr_el2_eff takes secure state into account. */ + if ((arm_hcr_el2_eff(env) & (HCR_IMO | HCR_FMO)) == 0) { r = CP_ACCESS_TRAP_EL3; } break; @@ -1933,8 +1937,8 @@ static CPAccessResult gicv3_dir_access(CPUARMState *env, static CPAccessResult gicv3_sgi_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { - if ((env->cp15.hcr_el2 & (HCR_IMO | HCR_FMO)) && - arm_current_el(env) == 1 && !arm_is_secure_below_el3(env)) { + if (arm_current_el(env) == 1 && + (arm_hcr_el2_eff(env) & (HCR_IMO | HCR_FMO)) != 0) { /* Takes priority over a possible EL3 trap */ return CP_ACCESS_TRAP_EL2; } @@ -1958,8 +1962,7 @@ static CPAccessResult gicv3_fiq_access(CPUARMState *env, if (env->cp15.scr_el3 & SCR_FIQ) { switch (el) { case 1: - if (arm_is_secure_below_el3(env) || - ((env->cp15.hcr_el2 & HCR_FMO) == 0)) { + if ((arm_hcr_el2_eff(env) & HCR_FMO) == 0) { r = CP_ACCESS_TRAP_EL3; } break; @@ -1998,8 +2001,7 @@ static CPAccessResult gicv3_irq_access(CPUARMState *env, if (env->cp15.scr_el3 & SCR_IRQ) { switch (el) { case 1: - if (arm_is_secure_below_el3(env) || - ((env->cp15.hcr_el2 & HCR_IMO) == 0)) { + if ((arm_hcr_el2_eff(env) & HCR_IMO) == 0) { r = CP_ACCESS_TRAP_EL3; } break; diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index 271ebe461c..01573abb48 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -211,7 +211,7 @@ static void kvm_arm_its_reset(DeviceState *dev) return; } - error_report("ITS KVM: full reset is not supported by the host kernel"); + warn_report("ITS KVM: full reset is not supported by the host kernel"); if (!kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ITS_REGS, GITS_CTLR)) { diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index cd1e7f1729..ab822f4251 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -420,6 +420,8 @@ static void set_prio(NVICState *s, unsigned irq, bool secure, uint8_t prio) assert(irq > ARMV7M_EXCP_NMI); /* only use for configurable prios */ assert(irq < s->num_irq); + prio &= MAKE_64BIT_MASK(8 - s->num_prio_bits, s->num_prio_bits); + if (secure) { assert(exc_is_banked(irq)); s->sec_vectors[irq].prio = prio; @@ -772,6 +774,24 @@ static void set_irq_level(void *opaque, int n, int level) } } +/* callback when external NMI line is changed */ +static void nvic_nmi_trigger(void *opaque, int n, int level) +{ + NVICState *s = opaque; + + trace_nvic_set_nmi_level(level); + + /* + * The architecture doesn't specify whether NMI should share + * the normal-interrupt behaviour of being resampled on + * exception handler return. We choose not to, so just + * set NMI pending here and don't track the current level. + */ + if (level) { + armv7m_nvic_set_pending(s, ARMV7M_EXCP_NMI, false); + } +} + static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) { ARMCPU *cpu = s->cpu; @@ -779,6 +799,9 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) switch (offset) { case 4: /* Interrupt Control Type. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V7)) { + goto bad_offset; + } return ((s->num_irq - NVIC_FIRST_IRQ) / 32) - 1; case 0xc: /* CPPWR */ if (!arm_feature(&cpu->env, ARM_FEATURE_V8)) { @@ -867,6 +890,9 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) } return val; case 0xd10: /* System Control. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V7)) { + goto bad_offset; + } return cpu->env.v7m.scr[attrs.secure]; case 0xd14: /* Configuration Control. */ /* The BFHFNMIGN bit is the only non-banked bit; we @@ -876,6 +902,9 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) val |= cpu->env.v7m.ccr[M_REG_NS] & R_V7M_CCR_BFHFNMIGN_MASK; return val; case 0xd24: /* System Handler Control and State (SHCSR) */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V7)) { + goto bad_offset; + } val = 0; if (attrs.secure) { if (s->sec_vectors[ARMV7M_EXCP_MEM].active) { @@ -988,12 +1017,21 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) } return val; case 0xd2c: /* Hard Fault Status. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } return cpu->env.v7m.hfsr; case 0xd30: /* Debug Fault Status. */ return cpu->env.v7m.dfsr; case 0xd34: /* MMFAR MemManage Fault Address */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } return cpu->env.v7m.mmfar[attrs.secure]; case 0xd38: /* Bus Fault Address. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } return cpu->env.v7m.bfar; case 0xd3c: /* Aux Fault Status. */ /* TODO: Implement fault status registers. */ @@ -1017,17 +1055,17 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) case 0xd5c: /* MMFR3. */ return cpu->id_mmfr3; case 0xd60: /* ISAR0. */ - return cpu->id_isar0; + return cpu->isar.id_isar0; case 0xd64: /* ISAR1. */ - return cpu->id_isar1; + return cpu->isar.id_isar1; case 0xd68: /* ISAR2. */ - return cpu->id_isar2; + return cpu->isar.id_isar2; case 0xd6c: /* ISAR3. */ - return cpu->id_isar3; + return cpu->isar.id_isar3; case 0xd70: /* ISAR4. */ - return cpu->id_isar4; + return cpu->isar.id_isar4; case 0xd74: /* ISAR5. */ - return cpu->id_isar5; + return cpu->isar.id_isar5; case 0xd78: /* CLIDR */ return cpu->clidr; case 0xd7c: /* CTR */ @@ -1263,9 +1301,12 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, "Setting VECTRESET when not in DEBUG mode " "is UNPREDICTABLE\n"); } - s->prigroup[attrs.secure] = extract32(value, - R_V7M_AIRCR_PRIGROUP_SHIFT, - R_V7M_AIRCR_PRIGROUP_LENGTH); + if (arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + s->prigroup[attrs.secure] = + extract32(value, + R_V7M_AIRCR_PRIGROUP_SHIFT, + R_V7M_AIRCR_PRIGROUP_LENGTH); + } if (attrs.secure) { /* These bits are only writable by secure */ cpu->env.v7m.aircr = value & @@ -1288,6 +1329,9 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, } break; case 0xd10: /* System Control. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V7)) { + goto bad_offset; + } /* We don't implement deep-sleep so these bits are RAZ/WI. * The other bits in the register are banked. * QEMU's implementation ignores SEVONPEND and SLEEPONEXIT, which @@ -1297,6 +1341,10 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, cpu->env.v7m.scr[attrs.secure] = value; break; case 0xd14: /* Configuration Control. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } + /* Enforce RAZ/WI on reserved and must-RAZ/WI bits */ value &= (R_V7M_CCR_STKALIGN_MASK | R_V7M_CCR_BFHFNMIGN_MASK | @@ -1321,6 +1369,9 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, cpu->env.v7m.ccr[attrs.secure] = value; break; case 0xd24: /* System Handler Control and State (SHCSR) */ + if (!arm_feature(&cpu->env, ARM_FEATURE_V7)) { + goto bad_offset; + } if (attrs.secure) { s->sec_vectors[ARMV7M_EXCP_MEM].active = (value & (1 << 0)) != 0; /* Secure HardFault active bit cannot be written */ @@ -1389,15 +1440,24 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, nvic_irq_update(s); break; case 0xd2c: /* Hard Fault Status. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } cpu->env.v7m.hfsr &= ~value; /* W1C */ break; case 0xd30: /* Debug Fault Status. */ cpu->env.v7m.dfsr &= ~value; /* W1C */ break; case 0xd34: /* Mem Manage Address. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } cpu->env.v7m.mmfar[attrs.secure] = value; return; case 0xd38: /* Bus Fault Address. */ + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } cpu->env.v7m.bfar = value; return; case 0xd3c: /* Aux Fault Status. */ @@ -1627,6 +1687,11 @@ static void nvic_writel(NVICState *s, uint32_t offset, uint32_t value, case 0xf00: /* Software Triggered Interrupt Register */ { int excnum = (value & 0x1ff) + NVIC_FIRST_IRQ; + + if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { + goto bad_offset; + } + if (excnum < s->num_irq) { armv7m_nvic_set_pending(s, excnum, false); } @@ -1752,6 +1817,11 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr, break; case 0x300 ... 0x33f: /* NVIC Active */ val = 0; + + if (!arm_feature(&s->cpu->env, ARM_FEATURE_V7)) { + break; + } + startvec = 8 * (offset - 0x300) + NVIC_FIRST_IRQ; /* vector # */ for (i = 0, end = size * 8; i < end && startvec + i < s->num_irq; i++) { @@ -1771,7 +1841,13 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr, } } break; - case 0xd18 ... 0xd23: /* System Handler Priority (SHPR1, SHPR2, SHPR3) */ + case 0xd18 ... 0xd1b: /* System Handler Priority (SHPR1) */ + if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_MAIN)) { + val = 0; + break; + } + /* fall through */ + case 0xd1c ... 0xd23: /* System Handler Priority (SHPR2, SHPR3) */ val = 0; for (i = 0; i < size; i++) { unsigned hdlidx = (offset - 0xd14) + i; @@ -1784,6 +1860,10 @@ static MemTxResult nvic_sysreg_read(void *opaque, hwaddr addr, } break; case 0xd28 ... 0xd2b: /* Configurable Fault Status (CFSR) */ + if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_MAIN)) { + val = 0; + break; + }; /* The BFSR bits [15:8] are shared between security states * and we store them in the NS copy */ @@ -1876,7 +1956,12 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr, } nvic_irq_update(s); return MEMTX_OK; - case 0xd18 ... 0xd23: /* System Handler Priority (SHPR1, SHPR2, SHPR3) */ + case 0xd18 ... 0xd1b: /* System Handler Priority (SHPR1) */ + if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_MAIN)) { + return MEMTX_OK; + } + /* fall through */ + case 0xd1c ... 0xd23: /* System Handler Priority (SHPR2, SHPR3) */ for (i = 0; i < size; i++) { unsigned hdlidx = (offset - 0xd14) + i; int newprio = extract32(value, i * 8, 8); @@ -1890,6 +1975,9 @@ static MemTxResult nvic_sysreg_write(void *opaque, hwaddr addr, nvic_irq_update(s); return MEMTX_OK; case 0xd28 ... 0xd2b: /* Configurable Fault Status (CFSR) */ + if (!arm_feature(&s->cpu->env, ARM_FEATURE_M_MAIN)) { + return MEMTX_OK; + } /* All bits are W1C, so construct 32 bit value with 0s in * the parts not written by the access size */ @@ -2186,8 +2274,7 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) Error *err = NULL; int regionlen; - s->cpu = ARM_CPU(qemu_get_cpu(0)); - + /* The armv7m container object will have set our CPU pointer */ if (!s->cpu || !arm_feature(&s->cpu->env, ARM_FEATURE_M)) { error_setg(errp, "The NVIC can only be used with a Cortex-M CPU"); return; @@ -2203,6 +2290,8 @@ static void armv7m_nvic_realize(DeviceState *dev, Error **errp) /* include space for internal exception vectors */ s->num_irq += NVIC_FIRST_IRQ; + s->num_prio_bits = arm_feature(&s->cpu->env, ARM_FEATURE_V7) ? 8 : 2; + object_property_set_bool(OBJECT(&s->systick[M_REG_NS]), true, "realized", &err); if (err != NULL) { @@ -2310,6 +2399,7 @@ static void armv7m_nvic_instance_init(Object *obj) qdev_init_gpio_out_named(dev, &nvic->sysresetreq, "SYSRESETREQ", 1); qdev_init_gpio_in_named(dev, nvic_systick_trigger, "systick-trigger", M_REG_NUM_BANKS); + qdev_init_gpio_in_named(dev, nvic_nmi_trigger, "NMI", 1); } static void armv7m_nvic_class_init(ObjectClass *klass, void *data) diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index cfa5bc7365..421469f2ef 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -7,7 +7,9 @@ * 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). + * not PMU interrupt, or AXI counters). + * + * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti * * Ref: * https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2836/QA7_rev3.4.pdf @@ -18,6 +20,9 @@ #include "qemu/log.h" #define REG_GPU_ROUTE 0x0c +#define REG_LOCALTIMERROUTING 0x24 +#define REG_LOCALTIMERCONTROL 0x34 +#define REG_LOCALTIMERACK 0x38 #define REG_TIMERCONTROL 0x40 #define REG_MBOXCONTROL 0x50 #define REG_IRQSRC 0x60 @@ -43,6 +48,13 @@ #define IRQ_TIMER 11 #define IRQ_MAX IRQ_TIMER +#define LOCALTIMER_FREQ 38400000 +#define LOCALTIMER_INTFLAG (1 << 31) +#define LOCALTIMER_RELOAD (1 << 30) +#define LOCALTIMER_INTENABLE (1 << 29) +#define LOCALTIMER_ENABLE (1 << 28) +#define LOCALTIMER_VALUE(x) ((x) & 0xfffffff) + static void deliver_local(BCM2836ControlState *s, uint8_t core, uint8_t irq, uint32_t controlreg, uint8_t controlidx) { @@ -78,6 +90,20 @@ static void bcm2836_control_update(BCM2836ControlState *s) s->fiqsrc[s->route_gpu_fiq] |= (uint32_t)1 << IRQ_GPU; } + /* + * handle the control module 'local timer' interrupt for one of the + * cores' IRQ/FIQ; this is distinct from the per-CPU timer + * interrupts handled below. + */ + if ((s->local_timer_control & LOCALTIMER_INTENABLE) && + (s->local_timer_control & LOCALTIMER_INTFLAG)) { + if (s->route_localtimer & 4) { + s->fiqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER; + } else { + s->irqsrc[(s->route_localtimer & 3)] |= (uint32_t)1 << IRQ_TIMER; + } + } + for (i = 0; i < BCM2836_NCORES; i++) { /* handle local timer interrupts for this core */ if (s->timerirqs[i]) { @@ -162,6 +188,54 @@ static void bcm2836_control_set_gpu_fiq(void *opaque, int irq, int level) bcm2836_control_update(s); } +static void bcm2836_control_local_timer_set_next(void *opaque) +{ + BCM2836ControlState *s = opaque; + uint64_t next_event; + + assert(LOCALTIMER_VALUE(s->local_timer_control) > 0); + + next_event = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + muldiv64(LOCALTIMER_VALUE(s->local_timer_control), + NANOSECONDS_PER_SECOND, LOCALTIMER_FREQ); + timer_mod(&s->timer, next_event); +} + +static void bcm2836_control_local_timer_tick(void *opaque) +{ + BCM2836ControlState *s = opaque; + + bcm2836_control_local_timer_set_next(s); + + s->local_timer_control |= LOCALTIMER_INTFLAG; + bcm2836_control_update(s); +} + +static void bcm2836_control_local_timer_control(void *opaque, uint32_t val) +{ + BCM2836ControlState *s = opaque; + + s->local_timer_control = val; + if (val & LOCALTIMER_ENABLE) { + bcm2836_control_local_timer_set_next(s); + } else { + timer_del(&s->timer); + } +} + +static void bcm2836_control_local_timer_ack(void *opaque, uint32_t val) +{ + BCM2836ControlState *s = opaque; + + if (val & LOCALTIMER_INTFLAG) { + s->local_timer_control &= ~LOCALTIMER_INTFLAG; + } + if ((val & LOCALTIMER_RELOAD) && + (s->local_timer_control & LOCALTIMER_ENABLE)) { + bcm2836_control_local_timer_set_next(s); + } +} + static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size) { BCM2836ControlState *s = opaque; @@ -170,6 +244,12 @@ static uint64_t bcm2836_control_read(void *opaque, hwaddr offset, unsigned size) 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_LOCALTIMERROUTING) { + return s->route_localtimer; + } else if (offset == REG_LOCALTIMERCONTROL) { + return s->local_timer_control; + } else if (offset == REG_LOCALTIMERACK) { + return 0; } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { return s->timercontrol[(offset - REG_TIMERCONTROL) >> 2]; } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { @@ -195,6 +275,12 @@ static void bcm2836_control_write(void *opaque, hwaddr offset, if (offset == REG_GPU_ROUTE) { s->route_gpu_irq = val & 0x3; s->route_gpu_fiq = (val >> 2) & 0x3; + } else if (offset == REG_LOCALTIMERROUTING) { + s->route_localtimer = val & 7; + } else if (offset == REG_LOCALTIMERCONTROL) { + bcm2836_control_local_timer_control(s, val); + } else if (offset == REG_LOCALTIMERACK) { + bcm2836_control_local_timer_ack(s, val); } else if (offset >= REG_TIMERCONTROL && offset < REG_MBOXCONTROL) { s->timercontrol[(offset - REG_TIMERCONTROL) >> 2] = val & 0xff; } else if (offset >= REG_MBOXCONTROL && offset < REG_IRQSRC) { @@ -227,6 +313,10 @@ static void bcm2836_control_reset(DeviceState *d) s->route_gpu_irq = s->route_gpu_fiq = 0; + timer_del(&s->timer); + s->route_localtimer = 0; + s->local_timer_control = 0; + for (i = 0; i < BCM2836_NCORES; i++) { s->timercontrol[i] = 0; s->mailboxcontrol[i] = 0; @@ -263,11 +353,15 @@ static void bcm2836_control_init(Object *obj) /* 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); + + /* create a qemu virtual timer */ + timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL, + bcm2836_control_local_timer_tick, s); } static const VMStateDescription vmstate_bcm2836_control = { .name = TYPE_BCM2836_CONTROL, - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(mailboxes, BCM2836ControlState, @@ -277,6 +371,9 @@ static const VMStateDescription vmstate_bcm2836_control = { VMSTATE_UINT32_ARRAY(timercontrol, BCM2836ControlState, BCM2836_NCORES), VMSTATE_UINT32_ARRAY(mailboxcontrol, BCM2836ControlState, BCM2836_NCORES), + VMSTATE_TIMER_V(timer, BCM2836ControlState, 2), + VMSTATE_UINT32_V(local_timer_control, BCM2836ControlState, 2), + VMSTATE_UINT8_V(route_localtimer, BCM2836ControlState, 2), VMSTATE_END_OF_LIST() } }; diff --git a/hw/intc/gic_internal.h b/hw/intc/gic_internal.h index 7fe87b13de..8d29b40ca1 100644 --- a/hw/intc/gic_internal.h +++ b/hw/intc/gic_internal.h @@ -21,36 +21,36 @@ #ifndef QEMU_ARM_GIC_INTERNAL_H #define QEMU_ARM_GIC_INTERNAL_H +#include "hw/registerfields.h" #include "hw/intc/arm_gic.h" #define ALL_CPU_MASK ((unsigned)(((1 << GIC_NCPU) - 1))) -#define GIC_BASE_IRQ 0 - -#define GIC_SET_ENABLED(irq, cm) s->irq_state[irq].enabled |= (cm) -#define GIC_CLEAR_ENABLED(irq, cm) s->irq_state[irq].enabled &= ~(cm) -#define GIC_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) -#define GIC_SET_PENDING(irq, cm) s->irq_state[irq].pending |= (cm) -#define GIC_CLEAR_PENDING(irq, cm) s->irq_state[irq].pending &= ~(cm) -#define GIC_SET_ACTIVE(irq, cm) s->irq_state[irq].active |= (cm) -#define GIC_CLEAR_ACTIVE(irq, cm) s->irq_state[irq].active &= ~(cm) -#define GIC_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) -#define GIC_SET_MODEL(irq) s->irq_state[irq].model = true -#define GIC_CLEAR_MODEL(irq) s->irq_state[irq].model = false -#define GIC_TEST_MODEL(irq) s->irq_state[irq].model -#define GIC_SET_LEVEL(irq, cm) s->irq_state[irq].level |= (cm) -#define GIC_CLEAR_LEVEL(irq, cm) s->irq_state[irq].level &= ~(cm) -#define GIC_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) -#define GIC_SET_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = true -#define GIC_CLEAR_EDGE_TRIGGER(irq) s->irq_state[irq].edge_trigger = false -#define GIC_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger) -#define GIC_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ +#define GIC_DIST_SET_ENABLED(irq, cm) (s->irq_state[irq].enabled |= (cm)) +#define GIC_DIST_CLEAR_ENABLED(irq, cm) (s->irq_state[irq].enabled &= ~(cm)) +#define GIC_DIST_TEST_ENABLED(irq, cm) ((s->irq_state[irq].enabled & (cm)) != 0) +#define GIC_DIST_SET_PENDING(irq, cm) (s->irq_state[irq].pending |= (cm)) +#define GIC_DIST_CLEAR_PENDING(irq, cm) (s->irq_state[irq].pending &= ~(cm)) +#define GIC_DIST_SET_ACTIVE(irq, cm) (s->irq_state[irq].active |= (cm)) +#define GIC_DIST_CLEAR_ACTIVE(irq, cm) (s->irq_state[irq].active &= ~(cm)) +#define GIC_DIST_TEST_ACTIVE(irq, cm) ((s->irq_state[irq].active & (cm)) != 0) +#define GIC_DIST_SET_MODEL(irq) (s->irq_state[irq].model = true) +#define GIC_DIST_CLEAR_MODEL(irq) (s->irq_state[irq].model = false) +#define GIC_DIST_TEST_MODEL(irq) (s->irq_state[irq].model) +#define GIC_DIST_SET_LEVEL(irq, cm) (s->irq_state[irq].level |= (cm)) +#define GIC_DIST_CLEAR_LEVEL(irq, cm) (s->irq_state[irq].level &= ~(cm)) +#define GIC_DIST_TEST_LEVEL(irq, cm) ((s->irq_state[irq].level & (cm)) != 0) +#define GIC_DIST_SET_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger = true) +#define GIC_DIST_CLEAR_EDGE_TRIGGER(irq) \ + (s->irq_state[irq].edge_trigger = false) +#define GIC_DIST_TEST_EDGE_TRIGGER(irq) (s->irq_state[irq].edge_trigger) +#define GIC_DIST_GET_PRIORITY(irq, cpu) (((irq) < GIC_INTERNAL) ? \ s->priority1[irq][cpu] : \ s->priority2[(irq) - GIC_INTERNAL]) -#define GIC_TARGET(irq) s->irq_target[irq] -#define GIC_CLEAR_GROUP(irq, cm) (s->irq_state[irq].group &= ~(cm)) -#define GIC_SET_GROUP(irq, cm) (s->irq_state[irq].group |= (cm)) -#define GIC_TEST_GROUP(irq, cm) ((s->irq_state[irq].group & (cm)) != 0) +#define GIC_DIST_TARGET(irq) (s->irq_target[irq]) +#define GIC_DIST_CLEAR_GROUP(irq, cm) (s->irq_state[irq].group &= ~(cm)) +#define GIC_DIST_SET_GROUP(irq, cm) (s->irq_state[irq].group |= (cm)) +#define GIC_DIST_TEST_GROUP(irq, cm) ((s->irq_state[irq].group & (cm)) != 0) #define GICD_CTLR_EN_GRP0 (1U << 0) #define GICD_CTLR_EN_GRP1 (1U << 1) @@ -63,6 +63,91 @@ #define GICC_CTLR_EOIMODE (1U << 9) #define GICC_CTLR_EOIMODE_NS (1U << 10) +REG32(GICH_HCR, 0x0) + FIELD(GICH_HCR, EN, 0, 1) + FIELD(GICH_HCR, UIE, 1, 1) + FIELD(GICH_HCR, LRENPIE, 2, 1) + FIELD(GICH_HCR, NPIE, 3, 1) + FIELD(GICH_HCR, VGRP0EIE, 4, 1) + FIELD(GICH_HCR, VGRP0DIE, 5, 1) + FIELD(GICH_HCR, VGRP1EIE, 6, 1) + FIELD(GICH_HCR, VGRP1DIE, 7, 1) + FIELD(GICH_HCR, EOICount, 27, 5) + +#define GICH_HCR_MASK \ + (R_GICH_HCR_EN_MASK | R_GICH_HCR_UIE_MASK | \ + R_GICH_HCR_LRENPIE_MASK | R_GICH_HCR_NPIE_MASK | \ + R_GICH_HCR_VGRP0EIE_MASK | R_GICH_HCR_VGRP0DIE_MASK | \ + R_GICH_HCR_VGRP1EIE_MASK | R_GICH_HCR_VGRP1DIE_MASK | \ + R_GICH_HCR_EOICount_MASK) + +REG32(GICH_VTR, 0x4) + FIELD(GICH_VTR, ListRegs, 0, 6) + FIELD(GICH_VTR, PREbits, 26, 3) + FIELD(GICH_VTR, PRIbits, 29, 3) + +REG32(GICH_VMCR, 0x8) + FIELD(GICH_VMCR, VMCCtlr, 0, 10) + FIELD(GICH_VMCR, VMABP, 18, 3) + FIELD(GICH_VMCR, VMBP, 21, 3) + FIELD(GICH_VMCR, VMPriMask, 27, 5) + +REG32(GICH_MISR, 0x10) + FIELD(GICH_MISR, EOI, 0, 1) + FIELD(GICH_MISR, U, 1, 1) + FIELD(GICH_MISR, LRENP, 2, 1) + FIELD(GICH_MISR, NP, 3, 1) + FIELD(GICH_MISR, VGrp0E, 4, 1) + FIELD(GICH_MISR, VGrp0D, 5, 1) + FIELD(GICH_MISR, VGrp1E, 6, 1) + FIELD(GICH_MISR, VGrp1D, 7, 1) + +REG32(GICH_EISR0, 0x20) +REG32(GICH_EISR1, 0x24) +REG32(GICH_ELRSR0, 0x30) +REG32(GICH_ELRSR1, 0x34) +REG32(GICH_APR, 0xf0) + +REG32(GICH_LR0, 0x100) + FIELD(GICH_LR0, VirtualID, 0, 10) + FIELD(GICH_LR0, PhysicalID, 10, 10) + FIELD(GICH_LR0, CPUID, 10, 3) + FIELD(GICH_LR0, EOI, 19, 1) + FIELD(GICH_LR0, Priority, 23, 5) + FIELD(GICH_LR0, State, 28, 2) + FIELD(GICH_LR0, Grp1, 30, 1) + FIELD(GICH_LR0, HW, 31, 1) + +/* Last LR register */ +REG32(GICH_LR63, 0x1fc) + +#define GICH_LR_MASK \ + (R_GICH_LR0_VirtualID_MASK | R_GICH_LR0_PhysicalID_MASK | \ + R_GICH_LR0_CPUID_MASK | R_GICH_LR0_EOI_MASK | \ + R_GICH_LR0_Priority_MASK | R_GICH_LR0_State_MASK | \ + R_GICH_LR0_Grp1_MASK | R_GICH_LR0_HW_MASK) + +#define GICH_LR_STATE_INVALID 0 +#define GICH_LR_STATE_PENDING 1 +#define GICH_LR_STATE_ACTIVE 2 +#define GICH_LR_STATE_ACTIVE_PENDING 3 + +#define GICH_LR_VIRT_ID(entry) (FIELD_EX32(entry, GICH_LR0, VirtualID)) +#define GICH_LR_PHYS_ID(entry) (FIELD_EX32(entry, GICH_LR0, PhysicalID)) +#define GICH_LR_CPUID(entry) (FIELD_EX32(entry, GICH_LR0, CPUID)) +#define GICH_LR_EOI(entry) (FIELD_EX32(entry, GICH_LR0, EOI)) +#define GICH_LR_PRIORITY(entry) (FIELD_EX32(entry, GICH_LR0, Priority) << 3) +#define GICH_LR_STATE(entry) (FIELD_EX32(entry, GICH_LR0, State)) +#define GICH_LR_GROUP(entry) (FIELD_EX32(entry, GICH_LR0, Grp1)) +#define GICH_LR_HW(entry) (FIELD_EX32(entry, GICH_LR0, HW)) + +#define GICH_LR_CLEAR_PENDING(entry) \ + ((entry) &= ~(GICH_LR_STATE_PENDING << R_GICH_LR0_State_SHIFT)) +#define GICH_LR_SET_ACTIVE(entry) \ + ((entry) |= (GICH_LR_STATE_ACTIVE << R_GICH_LR0_State_SHIFT)) +#define GICH_LR_CLEAR_ACTIVE(entry) \ + ((entry) &= ~(GICH_LR_STATE_ACTIVE << R_GICH_LR0_State_SHIFT)) + /* Valid bits for GICC_CTLR for GICv1, v1 with security extensions, * GICv2 and GICv2 with security extensions: */ @@ -74,13 +159,9 @@ /* The special cases for the revision property: */ #define REV_11MPCORE 0 -void gic_set_pending_private(GICState *s, int cpu, int irq); uint32_t gic_acknowledge_irq(GICState *s, int cpu, MemTxAttrs attrs); -void gic_complete_irq(GICState *s, int cpu, int irq, MemTxAttrs attrs); -void gic_update(GICState *s); -void gic_init_irqs_and_distributor(GICState *s); -void gic_set_priority(GICState *s, int cpu, int irq, uint8_t val, - MemTxAttrs attrs); +void gic_dist_set_priority(GICState *s, int cpu, int irq, uint8_t val, + MemTxAttrs attrs); static inline bool gic_test_pending(GICState *s, int irq, int cm) { @@ -93,7 +174,148 @@ static inline bool gic_test_pending(GICState *s, int irq, int cm) * GICD_ISPENDR to set the state pending. */ return (s->irq_state[irq].pending & cm) || - (!GIC_TEST_EDGE_TRIGGER(irq) && GIC_TEST_LEVEL(irq, cm)); + (!GIC_DIST_TEST_EDGE_TRIGGER(irq) && GIC_DIST_TEST_LEVEL(irq, cm)); + } +} + +static inline bool gic_is_vcpu(int cpu) +{ + return cpu >= GIC_NCPU; +} + +static inline int gic_get_vcpu_real_id(int cpu) +{ + return (cpu >= GIC_NCPU) ? (cpu - GIC_NCPU) : cpu; +} + +/* Return true if the given vIRQ state exists in a LR and is either active or + * pending and active. + * + * This function is used to check that a guest's `end of interrupt' or + * `interrupts deactivation' request is valid, and matches with a LR of an + * already acknowledged vIRQ (i.e. has the active bit set in its state). + */ +static inline bool gic_virq_is_valid(GICState *s, int irq, int vcpu) +{ + int cpu = gic_get_vcpu_real_id(vcpu); + int lr_idx; + + for (lr_idx = 0; lr_idx < s->num_lrs; lr_idx++) { + uint32_t *entry = &s->h_lr[lr_idx][cpu]; + + if ((GICH_LR_VIRT_ID(*entry) == irq) && + (GICH_LR_STATE(*entry) & GICH_LR_STATE_ACTIVE)) { + return true; + } + } + + return false; +} + +/* Return a pointer on the LR entry matching the given vIRQ. + * + * This function is used to retrieve an LR for which we know for sure that the + * corresponding vIRQ exists in the current context (i.e. its current state is + * not `invalid'): + * - Either the corresponding vIRQ has been validated with gic_virq_is_valid() + * so it is `active' or `active and pending', + * - Or it was pending and has been selected by gic_get_best_virq(). It is now + * `pending', `active' or `active and pending', depending on what the guest + * already did with this vIRQ. + * + * Having multiple LRs with the same VirtualID leads to UNPREDICTABLE + * behaviour in the GIC. We choose to return the first one that matches. + */ +static inline uint32_t *gic_get_lr_entry(GICState *s, int irq, int vcpu) +{ + int cpu = gic_get_vcpu_real_id(vcpu); + int lr_idx; + + for (lr_idx = 0; lr_idx < s->num_lrs; lr_idx++) { + uint32_t *entry = &s->h_lr[lr_idx][cpu]; + + if ((GICH_LR_VIRT_ID(*entry) == irq) && + (GICH_LR_STATE(*entry) != GICH_LR_STATE_INVALID)) { + return entry; + } + } + + g_assert_not_reached(); +} + +static inline bool gic_test_group(GICState *s, int irq, int cpu) +{ + if (gic_is_vcpu(cpu)) { + uint32_t *entry = gic_get_lr_entry(s, irq, cpu); + return GICH_LR_GROUP(*entry); + } else { + return GIC_DIST_TEST_GROUP(irq, 1 << cpu); + } +} + +static inline void gic_clear_pending(GICState *s, int irq, int cpu) +{ + if (gic_is_vcpu(cpu)) { + uint32_t *entry = gic_get_lr_entry(s, irq, cpu); + GICH_LR_CLEAR_PENDING(*entry); + } else { + /* Clear pending state for both level and edge triggered + * interrupts. (level triggered interrupts with an active line + * remain pending, see gic_test_pending) + */ + GIC_DIST_CLEAR_PENDING(irq, GIC_DIST_TEST_MODEL(irq) ? ALL_CPU_MASK + : (1 << cpu)); + } +} + +static inline void gic_set_active(GICState *s, int irq, int cpu) +{ + if (gic_is_vcpu(cpu)) { + uint32_t *entry = gic_get_lr_entry(s, irq, cpu); + GICH_LR_SET_ACTIVE(*entry); + } else { + GIC_DIST_SET_ACTIVE(irq, 1 << cpu); + } +} + +static inline void gic_clear_active(GICState *s, int irq, int cpu) +{ + if (gic_is_vcpu(cpu)) { + uint32_t *entry = gic_get_lr_entry(s, irq, cpu); + GICH_LR_CLEAR_ACTIVE(*entry); + + if (GICH_LR_HW(*entry)) { + /* Hardware interrupt. We must forward the deactivation request to + * the distributor. + */ + int phys_irq = GICH_LR_PHYS_ID(*entry); + int rcpu = gic_get_vcpu_real_id(cpu); + + if (phys_irq < GIC_NR_SGIS || phys_irq >= GIC_MAXIRQ) { + /* UNPREDICTABLE behaviour, we choose to ignore the request */ + return; + } + + /* This is equivalent to a NS write to DIR on the physical CPU + * interface. Hence group0 interrupt deactivation is ignored if + * the GIC is secure. + */ + if (!s->security_extn || GIC_DIST_TEST_GROUP(phys_irq, 1 << rcpu)) { + GIC_DIST_CLEAR_ACTIVE(phys_irq, 1 << rcpu); + } + } + } else { + GIC_DIST_CLEAR_ACTIVE(irq, 1 << cpu); + } +} + +static inline int gic_get_priority(GICState *s, int irq, int cpu) +{ + if (gic_is_vcpu(cpu)) { + uint32_t *entry = gic_get_lr_entry(s, irq, cpu); + return GICH_LR_PRIORITY(*entry); + } else { + return GIC_DIST_GET_PRIORITY(irq, cpu); } } diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index b6896ac4ce..9d75f84d3b 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -21,7 +21,7 @@ */ #include "qemu/osdep.h" -#include "qemu/error-report.h" +#include "qapi/error.h" #include "monitor/monitor.h" #include "hw/hw.h" #include "hw/i386/pc.h" @@ -393,9 +393,9 @@ static void ioapic_realize(DeviceState *dev, Error **errp) IOAPICCommonState *s = IOAPIC_COMMON(dev); if (s->version != 0x11 && s->version != 0x20) { - error_report("IOAPIC only supports version 0x11 or 0x20 " - "(default: 0x%x).", IOAPIC_VER_DEF); - exit(1); + error_setg(errp, "IOAPIC only supports version 0x11 or 0x20 " + "(default: 0x%x).", IOAPIC_VER_DEF); + return; } memory_region_init_io(&s->io_memory, OBJECT(s), &ioapic_io_ops, s, @@ -429,7 +429,7 @@ static void ioapic_class_init(ObjectClass *klass, void *data) } static const TypeInfo ioapic_info = { - .name = "ioapic", + .name = TYPE_IOAPIC, .parent = TYPE_IOAPIC_COMMON, .instance_size = sizeof(IOAPICCommonState), .class_init = ioapic_class_init, diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c new file mode 100644 index 0000000000..bb0877cbdf --- /dev/null +++ b/hw/intc/pnv_xive.c @@ -0,0 +1,1753 @@ +/* + * QEMU PowerPC XIVE interrupt controller model + * + * Copyright (c) 2017-2019, IBM Corporation. + * + * 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 "qemu/log.h" +#include "qapi/error.h" +#include "target/ppc/cpu.h" +#include "sysemu/cpus.h" +#include "sysemu/dma.h" +#include "monitor/monitor.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/pnv.h" +#include "hw/ppc/pnv_core.h" +#include "hw/ppc/pnv_xscom.h" +#include "hw/ppc/pnv_xive.h" +#include "hw/ppc/xive_regs.h" +#include "hw/ppc/ppc.h" + +#include + +#include "pnv_xive_regs.h" + +#define XIVE_DEBUG + +/* + * Virtual structures table (VST) + */ +#define SBE_PER_BYTE 4 + +typedef struct XiveVstInfo { + const char *name; + uint32_t size; + uint32_t max_blocks; +} XiveVstInfo; + +static const XiveVstInfo vst_infos[] = { + [VST_TSEL_IVT] = { "EAT", sizeof(XiveEAS), 16 }, + [VST_TSEL_SBE] = { "SBE", 1, 16 }, + [VST_TSEL_EQDT] = { "ENDT", sizeof(XiveEND), 16 }, + [VST_TSEL_VPDT] = { "VPDT", sizeof(XiveNVT), 32 }, + + /* + * Interrupt fifo backing store table (not modeled) : + * + * 0 - IPI, + * 1 - HWD, + * 2 - First escalate, + * 3 - Second escalate, + * 4 - Redistribution, + * 5 - IPI cascaded queue ? + */ + [VST_TSEL_IRQ] = { "IRQ", 1, 6 }, +}; + +#define xive_error(xive, fmt, ...) \ + qemu_log_mask(LOG_GUEST_ERROR, "XIVE[%x] - " fmt "\n", \ + (xive)->chip->chip_id, ## __VA_ARGS__); + +/* + * QEMU version of the GETFIELD/SETFIELD macros + * + * TODO: It might be better to use the existing extract64() and + * deposit64() but this means that all the register definitions will + * change and become incompatible with the ones found in skiboot. + * + * Keep it as it is for now until we find a common ground. + */ +static inline uint64_t GETFIELD(uint64_t mask, uint64_t word) +{ + return (word & mask) >> ctz64(mask); +} + +static inline uint64_t SETFIELD(uint64_t mask, uint64_t word, + uint64_t value) +{ + return (word & ~mask) | ((value << ctz64(mask)) & mask); +} + +/* + * Remote access to controllers. HW uses MMIOs. For now, a simple scan + * of the chips is good enough. + * + * TODO: Block scope support + */ +static PnvXive *pnv_xive_get_ic(uint8_t blk) +{ + PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); + int i; + + for (i = 0; i < pnv->num_chips; i++) { + Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); + PnvXive *xive = &chip9->xive; + + if (xive->chip->chip_id == blk) { + return xive; + } + } + return NULL; +} + +/* + * VST accessors for SBE, EAT, ENDT, NVT + * + * Indirect VST tables are arrays of VSDs pointing to a page (of same + * size). Each page is a direct VST table. + */ + +#define XIVE_VSD_SIZE 8 + +/* Indirect page size can be 4K, 64K, 2M, 16M. */ +static uint64_t pnv_xive_vst_page_size_allowed(uint32_t page_shift) +{ + return page_shift == 12 || page_shift == 16 || + page_shift == 21 || page_shift == 24; +} + +static uint64_t pnv_xive_vst_size(uint64_t vsd) +{ + uint64_t vst_tsize = 1ull << (GETFIELD(VSD_TSIZE, vsd) + 12); + + /* + * Read the first descriptor to get the page size of the indirect + * table. + */ + if (VSD_INDIRECT & vsd) { + uint32_t nr_pages = vst_tsize / XIVE_VSD_SIZE; + uint32_t page_shift; + + vsd = ldq_be_dma(&address_space_memory, vsd & VSD_ADDRESS_MASK); + page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + + if (!pnv_xive_vst_page_size_allowed(page_shift)) { + return 0; + } + + return nr_pages * (1ull << page_shift); + } + + return vst_tsize; +} + +static uint64_t pnv_xive_vst_addr_direct(PnvXive *xive, uint32_t type, + uint64_t vsd, uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + + return vst_addr + idx * info->size; +} + +static uint64_t pnv_xive_vst_addr_indirect(PnvXive *xive, uint32_t type, + uint64_t vsd, uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vsd_addr; + uint32_t vsd_idx; + uint32_t page_shift; + uint32_t vst_per_page; + + /* Get the page size of the indirect table. */ + vsd_addr = vsd & VSD_ADDRESS_MASK; + vsd = ldq_be_dma(&address_space_memory, vsd_addr); + + if (!(vsd & VSD_ADDRESS_MASK)) { + xive_error(xive, "VST: invalid %s entry %x !?", info->name, 0); + return 0; + } + + page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + + if (!pnv_xive_vst_page_size_allowed(page_shift)) { + xive_error(xive, "VST: invalid %s page shift %d", info->name, + page_shift); + return 0; + } + + vst_per_page = (1ull << page_shift) / info->size; + vsd_idx = idx / vst_per_page; + + /* Load the VSD we are looking for, if not already done */ + if (vsd_idx) { + vsd_addr = vsd_addr + vsd_idx * XIVE_VSD_SIZE; + vsd = ldq_be_dma(&address_space_memory, vsd_addr); + + if (!(vsd & VSD_ADDRESS_MASK)) { + xive_error(xive, "VST: invalid %s entry %x !?", info->name, 0); + return 0; + } + + /* + * Check that the pages have a consistent size across the + * indirect table + */ + if (page_shift != GETFIELD(VSD_TSIZE, vsd) + 12) { + xive_error(xive, "VST: %s entry %x indirect page size differ !?", + info->name, idx); + return 0; + } + } + + return pnv_xive_vst_addr_direct(xive, type, vsd, (idx % vst_per_page)); +} + +static uint64_t pnv_xive_vst_addr(PnvXive *xive, uint32_t type, uint8_t blk, + uint32_t idx) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t vsd; + uint32_t idx_max; + + if (blk >= info->max_blocks) { + xive_error(xive, "VST: invalid block id %d for VST %s %d !?", + blk, info->name, idx); + return 0; + } + + vsd = xive->vsds[type][blk]; + + /* Remote VST access */ + if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { + xive = pnv_xive_get_ic(blk); + + return xive ? pnv_xive_vst_addr(xive, type, blk, idx) : 0; + } + + idx_max = pnv_xive_vst_size(vsd) / info->size - 1; + if (idx > idx_max) { +#ifdef XIVE_DEBUG + xive_error(xive, "VST: %s entry %x/%x out of range [ 0 .. %x ] !?", + info->name, blk, idx, idx_max); +#endif + return 0; + } + + if (VSD_INDIRECT & vsd) { + return pnv_xive_vst_addr_indirect(xive, type, vsd, idx); + } + + return pnv_xive_vst_addr_direct(xive, type, vsd, idx); +} + +static int pnv_xive_vst_read(PnvXive *xive, uint32_t type, uint8_t blk, + uint32_t idx, void *data) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t addr = pnv_xive_vst_addr(xive, type, blk, idx); + + if (!addr) { + return -1; + } + + cpu_physical_memory_read(addr, data, info->size); + return 0; +} + +#define XIVE_VST_WORD_ALL -1 + +static int pnv_xive_vst_write(PnvXive *xive, uint32_t type, uint8_t blk, + uint32_t idx, void *data, uint32_t word_number) +{ + const XiveVstInfo *info = &vst_infos[type]; + uint64_t addr = pnv_xive_vst_addr(xive, type, blk, idx); + + if (!addr) { + return -1; + } + + if (word_number == XIVE_VST_WORD_ALL) { + cpu_physical_memory_write(addr, data, info->size); + } else { + cpu_physical_memory_write(addr + word_number * 4, + data + word_number * 4, 4); + } + return 0; +} + +static int pnv_xive_get_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + XiveEND *end) +{ + return pnv_xive_vst_read(PNV_XIVE(xrtr), VST_TSEL_EQDT, blk, idx, end); +} + +static int pnv_xive_write_end(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + XiveEND *end, uint8_t word_number) +{ + return pnv_xive_vst_write(PNV_XIVE(xrtr), VST_TSEL_EQDT, blk, idx, end, + word_number); +} + +static int pnv_xive_end_update(PnvXive *xive, uint8_t blk, uint32_t idx) +{ + int i; + uint64_t eqc_watch[4]; + + for (i = 0; i < ARRAY_SIZE(eqc_watch); i++) { + eqc_watch[i] = cpu_to_be64(xive->regs[(VC_EQC_CWATCH_DAT0 >> 3) + i]); + } + + return pnv_xive_vst_write(xive, VST_TSEL_EQDT, blk, idx, eqc_watch, + XIVE_VST_WORD_ALL); +} + +static int pnv_xive_get_nvt(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + XiveNVT *nvt) +{ + return pnv_xive_vst_read(PNV_XIVE(xrtr), VST_TSEL_VPDT, blk, idx, nvt); +} + +static int pnv_xive_write_nvt(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + XiveNVT *nvt, uint8_t word_number) +{ + return pnv_xive_vst_write(PNV_XIVE(xrtr), VST_TSEL_VPDT, blk, idx, nvt, + word_number); +} + +static int pnv_xive_nvt_update(PnvXive *xive, uint8_t blk, uint32_t idx) +{ + int i; + uint64_t vpc_watch[8]; + + for (i = 0; i < ARRAY_SIZE(vpc_watch); i++) { + vpc_watch[i] = cpu_to_be64(xive->regs[(PC_VPC_CWATCH_DAT0 >> 3) + i]); + } + + return pnv_xive_vst_write(xive, VST_TSEL_VPDT, blk, idx, vpc_watch, + XIVE_VST_WORD_ALL); +} + +static int pnv_xive_get_eas(XiveRouter *xrtr, uint8_t blk, uint32_t idx, + XiveEAS *eas) +{ + PnvXive *xive = PNV_XIVE(xrtr); + + if (pnv_xive_get_ic(blk) != xive) { + xive_error(xive, "VST: EAS %x is remote !?", XIVE_SRCNO(blk, idx)); + return -1; + } + + return pnv_xive_vst_read(xive, VST_TSEL_IVT, blk, idx, eas); +} + +static int pnv_xive_eas_update(PnvXive *xive, uint8_t blk, uint32_t idx) +{ + /* All done. */ + return 0; +} + +static XiveTCTX *pnv_xive_get_tctx(XiveRouter *xrtr, CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + XiveTCTX *tctx = XIVE_TCTX(pnv_cpu_state(cpu)->intc); + PnvXive *xive = NULL; + CPUPPCState *env = &cpu->env; + int pir = env->spr_cb[SPR_PIR].default_value; + + /* + * Perform an extra check on the HW thread enablement. + * + * The TIMA is shared among the chips and to identify the chip + * from which the access is being done, we extract the chip id + * from the PIR. + */ + xive = pnv_xive_get_ic((pir >> 8) & 0xf); + if (!xive) { + return NULL; + } + + if (!(xive->regs[PC_THREAD_EN_REG0 >> 3] & PPC_BIT(pir & 0x3f))) { + xive_error(PNV_XIVE(xrtr), "IC: CPU %x is not enabled", pir); + } + + return tctx; +} + +/* + * The internal sources (IPIs) of the interrupt controller have no + * knowledge of the XIVE chip on which they reside. Encode the block + * id in the source interrupt number before forwarding the source + * event notification to the Router. This is required on a multichip + * system. + */ +static void pnv_xive_notify(XiveNotifier *xn, uint32_t srcno) +{ + PnvXive *xive = PNV_XIVE(xn); + uint8_t blk = xive->chip->chip_id; + + xive_router_notify(xn, XIVE_SRCNO(blk, srcno)); +} + +/* + * XIVE helpers + */ + +static uint64_t pnv_xive_vc_size(PnvXive *xive) +{ + return (~xive->regs[CQ_VC_BARM >> 3] + 1) & CQ_VC_BARM_MASK; +} + +static uint64_t pnv_xive_edt_shift(PnvXive *xive) +{ + return ctz64(pnv_xive_vc_size(xive) / XIVE_TABLE_EDT_MAX); +} + +static uint64_t pnv_xive_pc_size(PnvXive *xive) +{ + return (~xive->regs[CQ_PC_BARM >> 3] + 1) & CQ_PC_BARM_MASK; +} + +static uint32_t pnv_xive_nr_ipis(PnvXive *xive) +{ + uint8_t blk = xive->chip->chip_id; + + return pnv_xive_vst_size(xive->vsds[VST_TSEL_SBE][blk]) * SBE_PER_BYTE; +} + +static uint32_t pnv_xive_nr_ends(PnvXive *xive) +{ + uint8_t blk = xive->chip->chip_id; + + return pnv_xive_vst_size(xive->vsds[VST_TSEL_EQDT][blk]) + / vst_infos[VST_TSEL_EQDT].size; +} + +/* + * EDT Table + * + * The Virtualization Controller MMIO region containing the IPI ESB + * pages and END ESB pages is sub-divided into "sets" which map + * portions of the VC region to the different ESB pages. It is + * configured at runtime through the EDT "Domain Table" to let the + * firmware decide how to split the VC address space between IPI ESB + * pages and END ESB pages. + */ + +/* + * Computes the overall size of the IPI or the END ESB pages + */ +static uint64_t pnv_xive_edt_size(PnvXive *xive, uint64_t type) +{ + uint64_t edt_size = 1ull << pnv_xive_edt_shift(xive); + uint64_t size = 0; + int i; + + for (i = 0; i < XIVE_TABLE_EDT_MAX; i++) { + uint64_t edt_type = GETFIELD(CQ_TDR_EDT_TYPE, xive->edt[i]); + + if (edt_type == type) { + size += edt_size; + } + } + + return size; +} + +/* + * Maps an offset of the VC region in the IPI or END region using the + * layout defined by the EDT "Domaine Table" + */ +static uint64_t pnv_xive_edt_offset(PnvXive *xive, uint64_t vc_offset, + uint64_t type) +{ + int i; + uint64_t edt_size = 1ull << pnv_xive_edt_shift(xive); + uint64_t edt_offset = vc_offset; + + for (i = 0; i < XIVE_TABLE_EDT_MAX && (i * edt_size) < vc_offset; i++) { + uint64_t edt_type = GETFIELD(CQ_TDR_EDT_TYPE, xive->edt[i]); + + if (edt_type != type) { + edt_offset -= edt_size; + } + } + + return edt_offset; +} + +static void pnv_xive_edt_resize(PnvXive *xive) +{ + uint64_t ipi_edt_size = pnv_xive_edt_size(xive, CQ_TDR_EDT_IPI); + uint64_t end_edt_size = pnv_xive_edt_size(xive, CQ_TDR_EDT_EQ); + + memory_region_set_size(&xive->ipi_edt_mmio, ipi_edt_size); + memory_region_add_subregion(&xive->ipi_mmio, 0, &xive->ipi_edt_mmio); + + memory_region_set_size(&xive->end_edt_mmio, end_edt_size); + memory_region_add_subregion(&xive->end_mmio, 0, &xive->end_edt_mmio); +} + +/* + * XIVE Table configuration. Only EDT is supported. + */ +static int pnv_xive_table_set_data(PnvXive *xive, uint64_t val) +{ + uint64_t tsel = xive->regs[CQ_TAR >> 3] & CQ_TAR_TSEL; + uint8_t tsel_index = GETFIELD(CQ_TAR_TSEL_INDEX, xive->regs[CQ_TAR >> 3]); + uint64_t *xive_table; + uint8_t max_index; + + switch (tsel) { + case CQ_TAR_TSEL_BLK: + max_index = ARRAY_SIZE(xive->blk); + xive_table = xive->blk; + break; + case CQ_TAR_TSEL_MIG: + max_index = ARRAY_SIZE(xive->mig); + xive_table = xive->mig; + break; + case CQ_TAR_TSEL_EDT: + max_index = ARRAY_SIZE(xive->edt); + xive_table = xive->edt; + break; + case CQ_TAR_TSEL_VDT: + max_index = ARRAY_SIZE(xive->vdt); + xive_table = xive->vdt; + break; + default: + xive_error(xive, "IC: invalid table %d", (int) tsel); + return -1; + } + + if (tsel_index >= max_index) { + xive_error(xive, "IC: invalid index %d", (int) tsel_index); + return -1; + } + + xive_table[tsel_index] = val; + + if (xive->regs[CQ_TAR >> 3] & CQ_TAR_TBL_AUTOINC) { + xive->regs[CQ_TAR >> 3] = + SETFIELD(CQ_TAR_TSEL_INDEX, xive->regs[CQ_TAR >> 3], ++tsel_index); + } + + /* + * EDT configuration is complete. Resize the MMIO windows exposing + * the IPI and the END ESBs in the VC region. + */ + if (tsel == CQ_TAR_TSEL_EDT && tsel_index == ARRAY_SIZE(xive->edt)) { + pnv_xive_edt_resize(xive); + } + + return 0; +} + +/* + * Virtual Structure Tables (VST) configuration + */ +static void pnv_xive_vst_set_exclusive(PnvXive *xive, uint8_t type, + uint8_t blk, uint64_t vsd) +{ + XiveENDSource *end_xsrc = &xive->end_source; + XiveSource *xsrc = &xive->ipi_source; + const XiveVstInfo *info = &vst_infos[type]; + uint32_t page_shift = GETFIELD(VSD_TSIZE, vsd) + 12; + uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + + /* Basic checks */ + + if (VSD_INDIRECT & vsd) { + if (!(xive->regs[VC_GLOBAL_CONFIG >> 3] & VC_GCONF_INDIRECT)) { + xive_error(xive, "VST: %s indirect tables are not enabled", + info->name); + return; + } + + if (!pnv_xive_vst_page_size_allowed(page_shift)) { + xive_error(xive, "VST: invalid %s page shift %d", info->name, + page_shift); + return; + } + } + + if (!QEMU_IS_ALIGNED(vst_addr, 1ull << page_shift)) { + xive_error(xive, "VST: %s table address 0x%"PRIx64" is not aligned with" + " page shift %d", info->name, vst_addr, page_shift); + return; + } + + /* Record the table configuration (in SRAM on HW) */ + xive->vsds[type][blk] = vsd; + + /* Now tune the models with the configuration provided by the FW */ + + switch (type) { + case VST_TSEL_IVT: /* Nothing to be done */ + break; + + case VST_TSEL_EQDT: + /* + * Backing store pages for the END. Compute the number of ENDs + * provisioned by FW and resize the END ESB window accordingly. + */ + memory_region_set_size(&end_xsrc->esb_mmio, pnv_xive_nr_ends(xive) * + (1ull << (end_xsrc->esb_shift + 1))); + memory_region_add_subregion(&xive->end_edt_mmio, 0, + &end_xsrc->esb_mmio); + break; + + case VST_TSEL_SBE: + /* + * Backing store pages for the source PQ bits. The model does + * not use these PQ bits backed in RAM because the XiveSource + * model has its own. Compute the number of IRQs provisioned + * by FW and resize the IPI ESB window accordingly. + */ + memory_region_set_size(&xsrc->esb_mmio, pnv_xive_nr_ipis(xive) * + (1ull << xsrc->esb_shift)); + memory_region_add_subregion(&xive->ipi_edt_mmio, 0, &xsrc->esb_mmio); + break; + + case VST_TSEL_VPDT: /* Not modeled */ + case VST_TSEL_IRQ: /* Not modeled */ + /* + * These tables contains the backing store pages for the + * interrupt fifos of the VC sub-engine in case of overflow. + */ + break; + + default: + g_assert_not_reached(); + } +} + +/* + * Both PC and VC sub-engines are configured as each use the Virtual + * Structure Tables : SBE, EAS, END and NVT. + */ +static void pnv_xive_vst_set_data(PnvXive *xive, uint64_t vsd, bool pc_engine) +{ + uint8_t mode = GETFIELD(VSD_MODE, vsd); + uint8_t type = GETFIELD(VST_TABLE_SELECT, + xive->regs[VC_VSD_TABLE_ADDR >> 3]); + uint8_t blk = GETFIELD(VST_TABLE_BLOCK, + xive->regs[VC_VSD_TABLE_ADDR >> 3]); + uint64_t vst_addr = vsd & VSD_ADDRESS_MASK; + + if (type > VST_TSEL_IRQ) { + xive_error(xive, "VST: invalid table type %d", type); + return; + } + + if (blk >= vst_infos[type].max_blocks) { + xive_error(xive, "VST: invalid block id %d for" + " %s table", blk, vst_infos[type].name); + return; + } + + /* + * Only take the VC sub-engine configuration into account because + * the XiveRouter model combines both VC and PC sub-engines + */ + if (pc_engine) { + return; + } + + if (!vst_addr) { + xive_error(xive, "VST: invalid %s table address", vst_infos[type].name); + return; + } + + switch (mode) { + case VSD_MODE_FORWARD: + xive->vsds[type][blk] = vsd; + break; + + case VSD_MODE_EXCLUSIVE: + pnv_xive_vst_set_exclusive(xive, type, blk, vsd); + break; + + default: + xive_error(xive, "VST: unsupported table mode %d", mode); + return; + } +} + +/* + * Interrupt controller MMIO region. The layout is compatible between + * 4K and 64K pages : + * + * Page 0 sub-engine BARs + * 0x000 - 0x3FF IC registers + * 0x400 - 0x7FF PC registers + * 0x800 - 0xFFF VC registers + * + * Page 1 Notify page (writes only) + * 0x000 - 0x7FF HW interrupt triggers (PSI, PHB) + * 0x800 - 0xFFF forwards and syncs + * + * Page 2 LSI Trigger page (writes only) (not modeled) + * Page 3 LSI SB EOI page (reads only) (not modeled) + * + * Page 4-7 indirect TIMA + */ + +/* + * IC - registers MMIO + */ +static void pnv_xive_ic_reg_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + MemoryRegion *sysmem = get_system_memory(); + uint32_t reg = offset >> 3; + bool is_chip0 = xive->chip->chip_id == 0; + + switch (offset) { + + /* + * XIVE CQ (PowerBus bridge) settings + */ + case CQ_MSGSND: /* msgsnd for doorbells */ + case CQ_FIRMASK_OR: /* FIR error reporting */ + break; + case CQ_PBI_CTL: + if (val & CQ_PBI_PC_64K) { + xive->pc_shift = 16; + } + if (val & CQ_PBI_VC_64K) { + xive->vc_shift = 16; + } + break; + case CQ_CFG_PB_GEN: /* PowerBus General Configuration */ + /* + * TODO: CQ_INT_ADDR_OPT for 1-block-per-chip mode + */ + break; + + /* + * XIVE Virtualization Controller settings + */ + case VC_GLOBAL_CONFIG: + break; + + /* + * XIVE Presenter Controller settings + */ + case PC_GLOBAL_CONFIG: + /* + * PC_GCONF_CHIPID_OVR + * Overrides Int command Chip ID with the Chip ID field (DEBUG) + */ + break; + case PC_TCTXT_CFG: + /* + * TODO: block group support + * + * PC_TCTXT_CFG_BLKGRP_EN + * PC_TCTXT_CFG_HARD_CHIPID_BLK : + * Moves the chipid into block field for hardwired CAM compares. + * Block offset value is adjusted to 0b0..01 & ThrdId + * + * Will require changes in xive_presenter_tctx_match(). I am + * not sure how to handle that yet. + */ + + /* Overrides hardwired chip ID with the chip ID field */ + if (val & PC_TCTXT_CHIPID_OVERRIDE) { + xive->tctx_chipid = GETFIELD(PC_TCTXT_CHIPID, val); + } + break; + case PC_TCTXT_TRACK: + /* + * PC_TCTXT_TRACK_EN: + * enable block tracking and exchange of block ownership + * information between Interrupt controllers + */ + break; + + /* + * Misc settings + */ + case VC_SBC_CONFIG: /* Store EOI configuration */ + /* + * Configure store EOI if required by firwmare (skiboot has removed + * support recently though) + */ + if (val & (VC_SBC_CONF_CPLX_CIST | VC_SBC_CONF_CIST_BOTH)) { + object_property_set_int(OBJECT(&xive->ipi_source), + XIVE_SRC_STORE_EOI, "flags", &error_fatal); + } + break; + + case VC_EQC_CONFIG: /* TODO: silent escalation */ + case VC_AIB_TX_ORDER_TAG2: /* relax ordering */ + break; + + /* + * XIVE BAR settings (XSCOM only) + */ + case CQ_RST_CTL: + /* bit4: resets all BAR registers */ + break; + + case CQ_IC_BAR: /* IC BAR. 8 pages */ + xive->ic_shift = val & CQ_IC_BAR_64K ? 16 : 12; + if (!(val & CQ_IC_BAR_VALID)) { + xive->ic_base = 0; + if (xive->regs[reg] & CQ_IC_BAR_VALID) { + memory_region_del_subregion(&xive->ic_mmio, + &xive->ic_reg_mmio); + memory_region_del_subregion(&xive->ic_mmio, + &xive->ic_notify_mmio); + memory_region_del_subregion(&xive->ic_mmio, + &xive->ic_lsi_mmio); + memory_region_del_subregion(&xive->ic_mmio, + &xive->tm_indirect_mmio); + + memory_region_del_subregion(sysmem, &xive->ic_mmio); + } + } else { + xive->ic_base = val & ~(CQ_IC_BAR_VALID | CQ_IC_BAR_64K); + if (!(xive->regs[reg] & CQ_IC_BAR_VALID)) { + memory_region_add_subregion(sysmem, xive->ic_base, + &xive->ic_mmio); + + memory_region_add_subregion(&xive->ic_mmio, 0, + &xive->ic_reg_mmio); + memory_region_add_subregion(&xive->ic_mmio, + 1ul << xive->ic_shift, + &xive->ic_notify_mmio); + memory_region_add_subregion(&xive->ic_mmio, + 2ul << xive->ic_shift, + &xive->ic_lsi_mmio); + memory_region_add_subregion(&xive->ic_mmio, + 4ull << xive->ic_shift, + &xive->tm_indirect_mmio); + } + } + break; + + case CQ_TM1_BAR: /* TM BAR. 4 pages. Map only once */ + case CQ_TM2_BAR: /* second TM BAR. for hotplug. Not modeled */ + xive->tm_shift = val & CQ_TM_BAR_64K ? 16 : 12; + if (!(val & CQ_TM_BAR_VALID)) { + xive->tm_base = 0; + if (xive->regs[reg] & CQ_TM_BAR_VALID && is_chip0) { + memory_region_del_subregion(sysmem, &xive->tm_mmio); + } + } else { + xive->tm_base = val & ~(CQ_TM_BAR_VALID | CQ_TM_BAR_64K); + if (!(xive->regs[reg] & CQ_TM_BAR_VALID) && is_chip0) { + memory_region_add_subregion(sysmem, xive->tm_base, + &xive->tm_mmio); + } + } + break; + + case CQ_PC_BARM: + xive->regs[reg] = val; + memory_region_set_size(&xive->pc_mmio, pnv_xive_pc_size(xive)); + break; + case CQ_PC_BAR: /* From 32M to 512G */ + if (!(val & CQ_PC_BAR_VALID)) { + xive->pc_base = 0; + if (xive->regs[reg] & CQ_PC_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->pc_mmio); + } + } else { + xive->pc_base = val & ~(CQ_PC_BAR_VALID); + if (!(xive->regs[reg] & CQ_PC_BAR_VALID)) { + memory_region_add_subregion(sysmem, xive->pc_base, + &xive->pc_mmio); + } + } + break; + + case CQ_VC_BARM: + xive->regs[reg] = val; + memory_region_set_size(&xive->vc_mmio, pnv_xive_vc_size(xive)); + break; + case CQ_VC_BAR: /* From 64M to 4TB */ + if (!(val & CQ_VC_BAR_VALID)) { + xive->vc_base = 0; + if (xive->regs[reg] & CQ_VC_BAR_VALID) { + memory_region_del_subregion(sysmem, &xive->vc_mmio); + } + } else { + xive->vc_base = val & ~(CQ_VC_BAR_VALID); + if (!(xive->regs[reg] & CQ_VC_BAR_VALID)) { + memory_region_add_subregion(sysmem, xive->vc_base, + &xive->vc_mmio); + } + } + break; + + /* + * XIVE Table settings. + */ + case CQ_TAR: /* Table Address */ + break; + case CQ_TDR: /* Table Data */ + pnv_xive_table_set_data(xive, val); + break; + + /* + * XIVE VC & PC Virtual Structure Table settings + */ + case VC_VSD_TABLE_ADDR: + case PC_VSD_TABLE_ADDR: /* Virtual table selector */ + break; + case VC_VSD_TABLE_DATA: /* Virtual table setting */ + case PC_VSD_TABLE_DATA: + pnv_xive_vst_set_data(xive, val, offset == PC_VSD_TABLE_DATA); + break; + + /* + * Interrupt fifo overflow in memory backing store (Not modeled) + */ + case VC_IRQ_CONFIG_IPI: + case VC_IRQ_CONFIG_HW: + case VC_IRQ_CONFIG_CASCADE1: + case VC_IRQ_CONFIG_CASCADE2: + case VC_IRQ_CONFIG_REDIST: + case VC_IRQ_CONFIG_IPI_CASC: + break; + + /* + * XIVE hardware thread enablement + */ + case PC_THREAD_EN_REG0: /* Physical Thread Enable */ + case PC_THREAD_EN_REG1: /* Physical Thread Enable (fused core) */ + break; + + case PC_THREAD_EN_REG0_SET: + xive->regs[PC_THREAD_EN_REG0 >> 3] |= val; + break; + case PC_THREAD_EN_REG1_SET: + xive->regs[PC_THREAD_EN_REG1 >> 3] |= val; + break; + case PC_THREAD_EN_REG0_CLR: + xive->regs[PC_THREAD_EN_REG0 >> 3] &= ~val; + break; + case PC_THREAD_EN_REG1_CLR: + xive->regs[PC_THREAD_EN_REG1 >> 3] &= ~val; + break; + + /* + * Indirect TIMA access set up. Defines the PIR of the HW thread + * to use. + */ + case PC_TCTXT_INDIR0 ... PC_TCTXT_INDIR3: + break; + + /* + * XIVE PC & VC cache updates for EAS, NVT and END + */ + case VC_IVC_SCRUB_MASK: + break; + case VC_IVC_SCRUB_TRIG: + pnv_xive_eas_update(xive, GETFIELD(PC_SCRUB_BLOCK_ID, val), + GETFIELD(VC_SCRUB_OFFSET, val)); + break; + + case VC_EQC_SCRUB_MASK: + case VC_EQC_CWATCH_SPEC: + case VC_EQC_CWATCH_DAT0 ... VC_EQC_CWATCH_DAT3: + break; + case VC_EQC_SCRUB_TRIG: + pnv_xive_end_update(xive, GETFIELD(VC_SCRUB_BLOCK_ID, val), + GETFIELD(VC_SCRUB_OFFSET, val)); + break; + + case PC_VPC_SCRUB_MASK: + case PC_VPC_CWATCH_SPEC: + case PC_VPC_CWATCH_DAT0 ... PC_VPC_CWATCH_DAT7: + break; + case PC_VPC_SCRUB_TRIG: + pnv_xive_nvt_update(xive, GETFIELD(PC_SCRUB_BLOCK_ID, val), + GETFIELD(PC_SCRUB_OFFSET, val)); + break; + + + /* + * XIVE PC & VC cache invalidation + */ + case PC_AT_KILL: + break; + case VC_AT_MACRO_KILL: + break; + case PC_AT_KILL_MASK: + case VC_AT_MACRO_KILL_MASK: + break; + + default: + xive_error(xive, "IC: invalid write to reg=0x%"HWADDR_PRIx, offset); + return; + } + + xive->regs[reg] = val; +} + +static uint64_t pnv_xive_ic_reg_read(void *opaque, hwaddr offset, unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + uint64_t val = 0; + uint32_t reg = offset >> 3; + + switch (offset) { + case CQ_CFG_PB_GEN: + case CQ_IC_BAR: + case CQ_TM1_BAR: + case CQ_TM2_BAR: + case CQ_PC_BAR: + case CQ_PC_BARM: + case CQ_VC_BAR: + case CQ_VC_BARM: + case CQ_TAR: + case CQ_TDR: + case CQ_PBI_CTL: + + case PC_TCTXT_CFG: + case PC_TCTXT_TRACK: + case PC_TCTXT_INDIR0: + case PC_TCTXT_INDIR1: + case PC_TCTXT_INDIR2: + case PC_TCTXT_INDIR3: + case PC_GLOBAL_CONFIG: + + case PC_VPC_SCRUB_MASK: + case PC_VPC_CWATCH_SPEC: + case PC_VPC_CWATCH_DAT0: + case PC_VPC_CWATCH_DAT1: + case PC_VPC_CWATCH_DAT2: + case PC_VPC_CWATCH_DAT3: + case PC_VPC_CWATCH_DAT4: + case PC_VPC_CWATCH_DAT5: + case PC_VPC_CWATCH_DAT6: + case PC_VPC_CWATCH_DAT7: + + case VC_GLOBAL_CONFIG: + case VC_AIB_TX_ORDER_TAG2: + + case VC_IRQ_CONFIG_IPI: + case VC_IRQ_CONFIG_HW: + case VC_IRQ_CONFIG_CASCADE1: + case VC_IRQ_CONFIG_CASCADE2: + case VC_IRQ_CONFIG_REDIST: + case VC_IRQ_CONFIG_IPI_CASC: + + case VC_EQC_SCRUB_MASK: + case VC_EQC_CWATCH_DAT0: + case VC_EQC_CWATCH_DAT1: + case VC_EQC_CWATCH_DAT2: + case VC_EQC_CWATCH_DAT3: + + case VC_EQC_CWATCH_SPEC: + case VC_IVC_SCRUB_MASK: + case VC_SBC_CONFIG: + case VC_AT_MACRO_KILL_MASK: + case VC_VSD_TABLE_ADDR: + case PC_VSD_TABLE_ADDR: + case VC_VSD_TABLE_DATA: + case PC_VSD_TABLE_DATA: + case PC_THREAD_EN_REG0: + case PC_THREAD_EN_REG1: + val = xive->regs[reg]; + break; + + /* + * XIVE hardware thread enablement + */ + case PC_THREAD_EN_REG0_SET: + case PC_THREAD_EN_REG0_CLR: + val = xive->regs[PC_THREAD_EN_REG0 >> 3]; + break; + case PC_THREAD_EN_REG1_SET: + case PC_THREAD_EN_REG1_CLR: + val = xive->regs[PC_THREAD_EN_REG1 >> 3]; + break; + + case CQ_MSGSND: /* Identifies which cores have msgsnd enabled. */ + val = 0xffffff0000000000; + break; + + /* + * XIVE PC & VC cache updates for EAS, NVT and END + */ + case PC_VPC_SCRUB_TRIG: + case VC_IVC_SCRUB_TRIG: + case VC_EQC_SCRUB_TRIG: + xive->regs[reg] &= ~VC_SCRUB_VALID; + val = xive->regs[reg]; + break; + + /* + * XIVE PC & VC cache invalidation + */ + case PC_AT_KILL: + xive->regs[reg] &= ~PC_AT_KILL_VALID; + val = xive->regs[reg]; + break; + case VC_AT_MACRO_KILL: + xive->regs[reg] &= ~VC_KILL_VALID; + val = xive->regs[reg]; + break; + + /* + * XIVE synchronisation + */ + case VC_EQC_CONFIG: + val = VC_EQC_SYNC_MASK; + break; + + default: + xive_error(xive, "IC: invalid read reg=0x%"HWADDR_PRIx, offset); + } + + return val; +} + +static const MemoryRegionOps pnv_xive_ic_reg_ops = { + .read = pnv_xive_ic_reg_read, + .write = pnv_xive_ic_reg_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * IC - Notify MMIO port page (write only) + */ +#define PNV_XIVE_FORWARD_IPI 0x800 /* Forward IPI */ +#define PNV_XIVE_FORWARD_HW 0x880 /* Forward HW */ +#define PNV_XIVE_FORWARD_OS_ESC 0x900 /* Forward OS escalation */ +#define PNV_XIVE_FORWARD_HW_ESC 0x980 /* Forward Hyp escalation */ +#define PNV_XIVE_FORWARD_REDIS 0xa00 /* Forward Redistribution */ +#define PNV_XIVE_RESERVED5 0xa80 /* Cache line 5 PowerBUS operation */ +#define PNV_XIVE_RESERVED6 0xb00 /* Cache line 6 PowerBUS operation */ +#define PNV_XIVE_RESERVED7 0xb80 /* Cache line 7 PowerBUS operation */ + +/* VC synchronisation */ +#define PNV_XIVE_SYNC_IPI 0xc00 /* Sync IPI */ +#define PNV_XIVE_SYNC_HW 0xc80 /* Sync HW */ +#define PNV_XIVE_SYNC_OS_ESC 0xd00 /* Sync OS escalation */ +#define PNV_XIVE_SYNC_HW_ESC 0xd80 /* Sync Hyp escalation */ +#define PNV_XIVE_SYNC_REDIS 0xe00 /* Sync Redistribution */ + +/* PC synchronisation */ +#define PNV_XIVE_SYNC_PULL 0xe80 /* Sync pull context */ +#define PNV_XIVE_SYNC_PUSH 0xf00 /* Sync push context */ +#define PNV_XIVE_SYNC_VPC 0xf80 /* Sync remove VPC store */ + +static void pnv_xive_ic_hw_trigger(PnvXive *xive, hwaddr addr, uint64_t val) +{ + /* + * Forward the source event notification directly to the Router. + * The source interrupt number should already be correctly encoded + * with the chip block id by the sending device (PHB, PSI). + */ + xive_router_notify(XIVE_NOTIFIER(xive), val); +} + +static void pnv_xive_ic_notify_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + + /* VC: HW triggers */ + switch (addr) { + case 0x000 ... 0x7FF: + pnv_xive_ic_hw_trigger(opaque, addr, val); + break; + + /* VC: Forwarded IRQs */ + case PNV_XIVE_FORWARD_IPI: + case PNV_XIVE_FORWARD_HW: + case PNV_XIVE_FORWARD_OS_ESC: + case PNV_XIVE_FORWARD_HW_ESC: + case PNV_XIVE_FORWARD_REDIS: + /* TODO: forwarded IRQs. Should be like HW triggers */ + xive_error(xive, "IC: forwarded at @0x%"HWADDR_PRIx" IRQ 0x%"PRIx64, + addr, val); + break; + + /* VC syncs */ + case PNV_XIVE_SYNC_IPI: + case PNV_XIVE_SYNC_HW: + case PNV_XIVE_SYNC_OS_ESC: + case PNV_XIVE_SYNC_HW_ESC: + case PNV_XIVE_SYNC_REDIS: + break; + + /* PC syncs */ + case PNV_XIVE_SYNC_PULL: + case PNV_XIVE_SYNC_PUSH: + case PNV_XIVE_SYNC_VPC: + break; + + default: + xive_error(xive, "IC: invalid notify write @%"HWADDR_PRIx, addr); + } +} + +static uint64_t pnv_xive_ic_notify_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + + /* loads are invalid */ + xive_error(xive, "IC: invalid notify read @%"HWADDR_PRIx, addr); + return -1; +} + +static const MemoryRegionOps pnv_xive_ic_notify_ops = { + .read = pnv_xive_ic_notify_read, + .write = pnv_xive_ic_notify_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * IC - LSI MMIO handlers (not modeled) + */ + +static void pnv_xive_ic_lsi_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + + xive_error(xive, "IC: LSI invalid write @%"HWADDR_PRIx, addr); +} + +static uint64_t pnv_xive_ic_lsi_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + + xive_error(xive, "IC: LSI invalid read @%"HWADDR_PRIx, addr); + return -1; +} + +static const MemoryRegionOps pnv_xive_ic_lsi_ops = { + .read = pnv_xive_ic_lsi_read, + .write = pnv_xive_ic_lsi_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * IC - Indirect TIMA MMIO handlers + */ + +/* + * When the TIMA is accessed from the indirect page, the thread id + * (PIR) has to be configured in the IC registers before. This is used + * for resets and for debug purpose also. + */ +static XiveTCTX *pnv_xive_get_indirect_tctx(PnvXive *xive) +{ + uint64_t tctxt_indir = xive->regs[PC_TCTXT_INDIR0 >> 3]; + PowerPCCPU *cpu = NULL; + int pir; + + if (!(tctxt_indir & PC_TCTXT_INDIR_VALID)) { + xive_error(xive, "IC: no indirect TIMA access in progress"); + return NULL; + } + + pir = GETFIELD(PC_TCTXT_INDIR_THRDID, tctxt_indir) & 0xff; + cpu = ppc_get_vcpu_by_pir(pir); + if (!cpu) { + xive_error(xive, "IC: invalid PIR %x for indirect access", pir); + return NULL; + } + + /* Check that HW thread is XIVE enabled */ + if (!(xive->regs[PC_THREAD_EN_REG0 >> 3] & PPC_BIT(pir & 0x3f))) { + xive_error(xive, "IC: CPU %x is not enabled", pir); + } + + return XIVE_TCTX(pnv_cpu_state(cpu)->intc); +} + +static void xive_tm_indirect_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + XiveTCTX *tctx = pnv_xive_get_indirect_tctx(PNV_XIVE(opaque)); + + xive_tctx_tm_write(tctx, offset, value, size); +} + +static uint64_t xive_tm_indirect_read(void *opaque, hwaddr offset, + unsigned size) +{ + XiveTCTX *tctx = pnv_xive_get_indirect_tctx(PNV_XIVE(opaque)); + + return xive_tctx_tm_read(tctx, offset, size); +} + +static const MemoryRegionOps xive_tm_indirect_ops = { + .read = xive_tm_indirect_read, + .write = xive_tm_indirect_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +/* + * Interrupt controller XSCOM region. + */ +static uint64_t pnv_xive_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + switch (addr >> 3) { + case X_VC_EQC_CONFIG: + /* FIXME (skiboot): This is the only XSCOM load. Bizarre. */ + return VC_EQC_SYNC_MASK; + default: + return pnv_xive_ic_reg_read(opaque, addr, size); + } +} + +static void pnv_xive_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + pnv_xive_ic_reg_write(opaque, addr, val, size); +} + +static const MemoryRegionOps pnv_xive_xscom_ops = { + .read = pnv_xive_xscom_read, + .write = pnv_xive_xscom_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + } +}; + +/* + * Virtualization Controller MMIO region containing the IPI and END ESB pages + */ +static uint64_t pnv_xive_vc_read(void *opaque, hwaddr offset, + unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + uint64_t edt_index = offset >> pnv_xive_edt_shift(xive); + uint64_t edt_type = 0; + uint64_t edt_offset; + MemTxResult result; + AddressSpace *edt_as = NULL; + uint64_t ret = -1; + + if (edt_index < XIVE_TABLE_EDT_MAX) { + edt_type = GETFIELD(CQ_TDR_EDT_TYPE, xive->edt[edt_index]); + } + + switch (edt_type) { + case CQ_TDR_EDT_IPI: + edt_as = &xive->ipi_as; + break; + case CQ_TDR_EDT_EQ: + edt_as = &xive->end_as; + break; + default: + xive_error(xive, "VC: invalid EDT type for read @%"HWADDR_PRIx, offset); + return -1; + } + + /* Remap the offset for the targeted address space */ + edt_offset = pnv_xive_edt_offset(xive, offset, edt_type); + + ret = address_space_ldq(edt_as, edt_offset, MEMTXATTRS_UNSPECIFIED, + &result); + + if (result != MEMTX_OK) { + xive_error(xive, "VC: %s read failed at @0x%"HWADDR_PRIx " -> @0x%" + HWADDR_PRIx, edt_type == CQ_TDR_EDT_IPI ? "IPI" : "END", + offset, edt_offset); + return -1; + } + + return ret; +} + +static void pnv_xive_vc_write(void *opaque, hwaddr offset, + uint64_t val, unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + uint64_t edt_index = offset >> pnv_xive_edt_shift(xive); + uint64_t edt_type = 0; + uint64_t edt_offset; + MemTxResult result; + AddressSpace *edt_as = NULL; + + if (edt_index < XIVE_TABLE_EDT_MAX) { + edt_type = GETFIELD(CQ_TDR_EDT_TYPE, xive->edt[edt_index]); + } + + switch (edt_type) { + case CQ_TDR_EDT_IPI: + edt_as = &xive->ipi_as; + break; + case CQ_TDR_EDT_EQ: + edt_as = &xive->end_as; + break; + default: + xive_error(xive, "VC: invalid EDT type for write @%"HWADDR_PRIx, + offset); + return; + } + + /* Remap the offset for the targeted address space */ + edt_offset = pnv_xive_edt_offset(xive, offset, edt_type); + + address_space_stq(edt_as, edt_offset, val, MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { + xive_error(xive, "VC: write failed at @0x%"HWADDR_PRIx, edt_offset); + } +} + +static const MemoryRegionOps pnv_xive_vc_ops = { + .read = pnv_xive_vc_read, + .write = pnv_xive_vc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +/* + * Presenter Controller MMIO region. The Virtualization Controller + * updates the IPB in the NVT table when required. Not modeled. + */ +static uint64_t pnv_xive_pc_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + + xive_error(xive, "PC: invalid read @%"HWADDR_PRIx, addr); + return -1; +} + +static void pnv_xive_pc_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + PnvXive *xive = PNV_XIVE(opaque); + + xive_error(xive, "PC: invalid write to VC @%"HWADDR_PRIx, addr); +} + +static const MemoryRegionOps pnv_xive_pc_ops = { + .read = pnv_xive_pc_read, + .write = pnv_xive_pc_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon) +{ + XiveRouter *xrtr = XIVE_ROUTER(xive); + uint8_t blk = xive->chip->chip_id; + uint32_t srcno0 = XIVE_SRCNO(blk, 0); + uint32_t nr_ipis = pnv_xive_nr_ipis(xive); + uint32_t nr_ends = pnv_xive_nr_ends(xive); + XiveEAS eas; + XiveEND end; + int i; + + monitor_printf(mon, "XIVE[%x] Source %08x .. %08x\n", blk, srcno0, + srcno0 + nr_ipis - 1); + xive_source_pic_print_info(&xive->ipi_source, srcno0, mon); + + monitor_printf(mon, "XIVE[%x] EAT %08x .. %08x\n", blk, srcno0, + srcno0 + nr_ipis - 1); + for (i = 0; i < nr_ipis; i++) { + if (xive_router_get_eas(xrtr, blk, i, &eas)) { + break; + } + if (!xive_eas_is_masked(&eas)) { + xive_eas_pic_print_info(&eas, i, mon); + } + } + + monitor_printf(mon, "XIVE[%x] ENDT %08x .. %08x\n", blk, 0, nr_ends - 1); + for (i = 0; i < nr_ends; i++) { + if (xive_router_get_end(xrtr, blk, i, &end)) { + break; + } + xive_end_pic_print_info(&end, i, mon); + } +} + +static void pnv_xive_reset(void *dev) +{ + PnvXive *xive = PNV_XIVE(dev); + XiveSource *xsrc = &xive->ipi_source; + XiveENDSource *end_xsrc = &xive->end_source; + + /* + * Use the PnvChip id to identify the XIVE interrupt controller. + * It can be overriden by configuration at runtime. + */ + xive->tctx_chipid = xive->chip->chip_id; + + /* Default page size (Should be changed at runtime to 64k) */ + xive->ic_shift = xive->vc_shift = xive->pc_shift = 12; + + /* Clear subregions */ + if (memory_region_is_mapped(&xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->ipi_edt_mmio, &xsrc->esb_mmio); + } + + if (memory_region_is_mapped(&xive->ipi_edt_mmio)) { + memory_region_del_subregion(&xive->ipi_mmio, &xive->ipi_edt_mmio); + } + + if (memory_region_is_mapped(&end_xsrc->esb_mmio)) { + memory_region_del_subregion(&xive->end_edt_mmio, &end_xsrc->esb_mmio); + } + + if (memory_region_is_mapped(&xive->end_edt_mmio)) { + memory_region_del_subregion(&xive->end_mmio, &xive->end_edt_mmio); + } +} + +static void pnv_xive_init(Object *obj) +{ + PnvXive *xive = PNV_XIVE(obj); + + object_initialize_child(obj, "ipi_source", &xive->ipi_source, + sizeof(xive->ipi_source), TYPE_XIVE_SOURCE, + &error_abort, NULL); + object_initialize_child(obj, "end_source", &xive->end_source, + sizeof(xive->end_source), TYPE_XIVE_END_SOURCE, + &error_abort, NULL); +} + +/* + * Maximum number of IRQs and ENDs supported by HW + */ +#define PNV_XIVE_NR_IRQS (PNV9_XIVE_VC_SIZE / (1ull << XIVE_ESB_64K_2PAGE)) +#define PNV_XIVE_NR_ENDS (PNV9_XIVE_VC_SIZE / (1ull << XIVE_ESB_64K_2PAGE)) + +static void pnv_xive_realize(DeviceState *dev, Error **errp) +{ + PnvXive *xive = PNV_XIVE(dev); + XiveSource *xsrc = &xive->ipi_source; + XiveENDSource *end_xsrc = &xive->end_source; + Error *local_err = NULL; + Object *obj; + + obj = object_property_get_link(OBJECT(dev), "chip", &local_err); + if (!obj) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'chip' not found: "); + return; + } + + /* The PnvChip id identifies the XIVE interrupt controller. */ + xive->chip = PNV_CHIP(obj); + + /* + * The XiveSource and XiveENDSource objects are realized with the + * maximum allowed HW configuration. The ESB MMIO regions will be + * resized dynamically when the controller is configured by the FW + * to limit accesses to resources not provisioned. + */ + object_property_set_int(OBJECT(xsrc), PNV_XIVE_NR_IRQS, "nr-irqs", + &error_fatal); + object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive), + &error_fatal); + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + object_property_set_int(OBJECT(end_xsrc), PNV_XIVE_NR_ENDS, "nr-ends", + &error_fatal); + object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive), + &error_fatal); + object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Default page size. Generally changed at runtime to 64k */ + xive->ic_shift = xive->vc_shift = xive->pc_shift = 12; + + /* XSCOM region, used for initial configuration of the BARs */ + memory_region_init_io(&xive->xscom_regs, OBJECT(dev), &pnv_xive_xscom_ops, + xive, "xscom-xive", PNV9_XSCOM_XIVE_SIZE << 3); + + /* Interrupt controller MMIO regions */ + memory_region_init(&xive->ic_mmio, OBJECT(dev), "xive-ic", + PNV9_XIVE_IC_SIZE); + + memory_region_init_io(&xive->ic_reg_mmio, OBJECT(dev), &pnv_xive_ic_reg_ops, + xive, "xive-ic-reg", 1 << xive->ic_shift); + memory_region_init_io(&xive->ic_notify_mmio, OBJECT(dev), + &pnv_xive_ic_notify_ops, + xive, "xive-ic-notify", 1 << xive->ic_shift); + + /* The Pervasive LSI trigger and EOI pages (not modeled) */ + memory_region_init_io(&xive->ic_lsi_mmio, OBJECT(dev), &pnv_xive_ic_lsi_ops, + xive, "xive-ic-lsi", 2 << xive->ic_shift); + + /* Thread Interrupt Management Area (Indirect) */ + memory_region_init_io(&xive->tm_indirect_mmio, OBJECT(dev), + &xive_tm_indirect_ops, + xive, "xive-tima-indirect", PNV9_XIVE_TM_SIZE); + /* + * Overall Virtualization Controller MMIO region containing the + * IPI ESB pages and END ESB pages. The layout is defined by the + * EDT "Domain table" and the accesses are dispatched using + * address spaces for each. + */ + memory_region_init_io(&xive->vc_mmio, OBJECT(xive), &pnv_xive_vc_ops, xive, + "xive-vc", PNV9_XIVE_VC_SIZE); + + memory_region_init(&xive->ipi_mmio, OBJECT(xive), "xive-vc-ipi", + PNV9_XIVE_VC_SIZE); + address_space_init(&xive->ipi_as, &xive->ipi_mmio, "xive-vc-ipi"); + memory_region_init(&xive->end_mmio, OBJECT(xive), "xive-vc-end", + PNV9_XIVE_VC_SIZE); + address_space_init(&xive->end_as, &xive->end_mmio, "xive-vc-end"); + + /* + * The MMIO windows exposing the IPI ESBs and the END ESBs in the + * VC region. Their size is configured by the FW in the EDT table. + */ + memory_region_init(&xive->ipi_edt_mmio, OBJECT(xive), "xive-vc-ipi-edt", 0); + memory_region_init(&xive->end_edt_mmio, OBJECT(xive), "xive-vc-end-edt", 0); + + /* Presenter Controller MMIO region (not modeled) */ + memory_region_init_io(&xive->pc_mmio, OBJECT(xive), &pnv_xive_pc_ops, xive, + "xive-pc", PNV9_XIVE_PC_SIZE); + + /* Thread Interrupt Management Area (Direct) */ + memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, + xive, "xive-tima", PNV9_XIVE_TM_SIZE); + + qemu_register_reset(pnv_xive_reset, dev); +} + +static int pnv_xive_dt_xscom(PnvXScomInterface *dev, void *fdt, + int xscom_offset) +{ + const char compat[] = "ibm,power9-xive-x"; + char *name; + int offset; + uint32_t lpc_pcba = PNV9_XSCOM_XIVE_BASE; + uint32_t reg[] = { + cpu_to_be32(lpc_pcba), + cpu_to_be32(PNV9_XSCOM_XIVE_SIZE) + }; + + name = g_strdup_printf("xive@%x", lpc_pcba); + offset = fdt_add_subnode(fdt, xscom_offset, name); + _FDT(offset); + g_free(name); + + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop(fdt, offset, "compatible", compat, + sizeof(compat)))); + return 0; +} + +static Property pnv_xive_properties[] = { + DEFINE_PROP_UINT64("ic-bar", PnvXive, ic_base, 0), + DEFINE_PROP_UINT64("vc-bar", PnvXive, vc_base, 0), + DEFINE_PROP_UINT64("pc-bar", PnvXive, pc_base, 0), + DEFINE_PROP_UINT64("tm-bar", PnvXive, tm_base, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_xive_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); + XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); + + xdc->dt_xscom = pnv_xive_dt_xscom; + + dc->desc = "PowerNV XIVE Interrupt Controller"; + dc->realize = pnv_xive_realize; + dc->props = pnv_xive_properties; + + xrc->get_eas = pnv_xive_get_eas; + xrc->get_end = pnv_xive_get_end; + xrc->write_end = pnv_xive_write_end; + xrc->get_nvt = pnv_xive_get_nvt; + xrc->write_nvt = pnv_xive_write_nvt; + xrc->get_tctx = pnv_xive_get_tctx; + + xnc->notify = pnv_xive_notify; +}; + +static const TypeInfo pnv_xive_info = { + .name = TYPE_PNV_XIVE, + .parent = TYPE_XIVE_ROUTER, + .instance_init = pnv_xive_init, + .instance_size = sizeof(PnvXive), + .class_init = pnv_xive_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_xive_register_types(void) +{ + type_register_static(&pnv_xive_info); +} + +type_init(pnv_xive_register_types) diff --git a/hw/intc/pnv_xive_regs.h b/hw/intc/pnv_xive_regs.h new file mode 100644 index 0000000000..c78f030c02 --- /dev/null +++ b/hw/intc/pnv_xive_regs.h @@ -0,0 +1,248 @@ +/* + * QEMU PowerPC XIVE interrupt controller model + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_PNV_XIVE_REGS_H +#define PPC_PNV_XIVE_REGS_H + +/* IC register offsets 0x0 - 0x400 */ +#define CQ_SWI_CMD_HIST 0x020 +#define CQ_SWI_CMD_POLL 0x028 +#define CQ_SWI_CMD_BCAST 0x030 +#define CQ_SWI_CMD_ASSIGN 0x038 +#define CQ_SWI_CMD_BLK_UPD 0x040 +#define CQ_SWI_RSP 0x048 +#define CQ_CFG_PB_GEN 0x050 +#define CQ_INT_ADDR_OPT PPC_BITMASK(14, 15) +#define CQ_MSGSND 0x058 +#define CQ_CNPM_SEL 0x078 +#define CQ_IC_BAR 0x080 +#define CQ_IC_BAR_VALID PPC_BIT(0) +#define CQ_IC_BAR_64K PPC_BIT(1) +#define CQ_TM1_BAR 0x90 +#define CQ_TM2_BAR 0x0a0 +#define CQ_TM_BAR_VALID PPC_BIT(0) +#define CQ_TM_BAR_64K PPC_BIT(1) +#define CQ_PC_BAR 0x0b0 +#define CQ_PC_BAR_VALID PPC_BIT(0) +#define CQ_PC_BARM 0x0b8 +#define CQ_PC_BARM_MASK PPC_BITMASK(26, 38) +#define CQ_VC_BAR 0x0c0 +#define CQ_VC_BAR_VALID PPC_BIT(0) +#define CQ_VC_BARM 0x0c8 +#define CQ_VC_BARM_MASK PPC_BITMASK(21, 37) +#define CQ_TAR 0x0f0 +#define CQ_TAR_TBL_AUTOINC PPC_BIT(0) +#define CQ_TAR_TSEL PPC_BITMASK(12, 15) +#define CQ_TAR_TSEL_BLK PPC_BIT(12) +#define CQ_TAR_TSEL_MIG PPC_BIT(13) +#define CQ_TAR_TSEL_VDT PPC_BIT(14) +#define CQ_TAR_TSEL_EDT PPC_BIT(15) +#define CQ_TAR_TSEL_INDEX PPC_BITMASK(26, 31) +#define CQ_TDR 0x0f8 +#define CQ_TDR_VDT_VALID PPC_BIT(0) +#define CQ_TDR_VDT_BLK PPC_BITMASK(11, 15) +#define CQ_TDR_VDT_INDEX PPC_BITMASK(28, 31) +#define CQ_TDR_EDT_TYPE PPC_BITMASK(0, 1) +#define CQ_TDR_EDT_INVALID 0 +#define CQ_TDR_EDT_IPI 1 +#define CQ_TDR_EDT_EQ 2 +#define CQ_TDR_EDT_BLK PPC_BITMASK(12, 15) +#define CQ_TDR_EDT_INDEX PPC_BITMASK(26, 31) +#define CQ_PBI_CTL 0x100 +#define CQ_PBI_PC_64K PPC_BIT(5) +#define CQ_PBI_VC_64K PPC_BIT(6) +#define CQ_PBI_LNX_TRIG PPC_BIT(7) +#define CQ_PBI_FORCE_TM_LOCAL PPC_BIT(22) +#define CQ_PBO_CTL 0x108 +#define CQ_AIB_CTL 0x110 +#define CQ_RST_CTL 0x118 +#define CQ_FIRMASK 0x198 +#define CQ_FIRMASK_AND 0x1a0 +#define CQ_FIRMASK_OR 0x1a8 + +/* PC LBS1 register offsets 0x400 - 0x800 */ +#define PC_TCTXT_CFG 0x400 +#define PC_TCTXT_CFG_BLKGRP_EN PPC_BIT(0) +#define PC_TCTXT_CFG_TARGET_EN PPC_BIT(1) +#define PC_TCTXT_CFG_LGS_EN PPC_BIT(2) +#define PC_TCTXT_CFG_STORE_ACK PPC_BIT(3) +#define PC_TCTXT_CFG_HARD_CHIPID_BLK PPC_BIT(8) +#define PC_TCTXT_CHIPID_OVERRIDE PPC_BIT(9) +#define PC_TCTXT_CHIPID PPC_BITMASK(12, 15) +#define PC_TCTXT_INIT_AGE PPC_BITMASK(30, 31) +#define PC_TCTXT_TRACK 0x408 +#define PC_TCTXT_TRACK_EN PPC_BIT(0) +#define PC_TCTXT_INDIR0 0x420 +#define PC_TCTXT_INDIR_VALID PPC_BIT(0) +#define PC_TCTXT_INDIR_THRDID PPC_BITMASK(9, 15) +#define PC_TCTXT_INDIR1 0x428 +#define PC_TCTXT_INDIR2 0x430 +#define PC_TCTXT_INDIR3 0x438 +#define PC_THREAD_EN_REG0 0x440 +#define PC_THREAD_EN_REG0_SET 0x448 +#define PC_THREAD_EN_REG0_CLR 0x450 +#define PC_THREAD_EN_REG1 0x460 +#define PC_THREAD_EN_REG1_SET 0x468 +#define PC_THREAD_EN_REG1_CLR 0x470 +#define PC_GLOBAL_CONFIG 0x480 +#define PC_GCONF_INDIRECT PPC_BIT(32) +#define PC_GCONF_CHIPID_OVR PPC_BIT(40) +#define PC_GCONF_CHIPID PPC_BITMASK(44, 47) +#define PC_VSD_TABLE_ADDR 0x488 +#define PC_VSD_TABLE_DATA 0x490 +#define PC_AT_KILL 0x4b0 +#define PC_AT_KILL_VALID PPC_BIT(0) +#define PC_AT_KILL_BLOCK_ID PPC_BITMASK(27, 31) +#define PC_AT_KILL_OFFSET PPC_BITMASK(48, 60) +#define PC_AT_KILL_MASK 0x4b8 + +/* PC LBS2 register offsets */ +#define PC_VPC_CACHE_ENABLE 0x708 +#define PC_VPC_CACHE_EN_MASK PPC_BITMASK(0, 31) +#define PC_VPC_SCRUB_TRIG 0x710 +#define PC_VPC_SCRUB_MASK 0x718 +#define PC_SCRUB_VALID PPC_BIT(0) +#define PC_SCRUB_WANT_DISABLE PPC_BIT(1) +#define PC_SCRUB_WANT_INVAL PPC_BIT(2) +#define PC_SCRUB_BLOCK_ID PPC_BITMASK(27, 31) +#define PC_SCRUB_OFFSET PPC_BITMASK(45, 63) +#define PC_VPC_CWATCH_SPEC 0x738 +#define PC_VPC_CWATCH_CONFLICT PPC_BIT(0) +#define PC_VPC_CWATCH_FULL PPC_BIT(8) +#define PC_VPC_CWATCH_BLOCKID PPC_BITMASK(27, 31) +#define PC_VPC_CWATCH_OFFSET PPC_BITMASK(45, 63) +#define PC_VPC_CWATCH_DAT0 0x740 +#define PC_VPC_CWATCH_DAT1 0x748 +#define PC_VPC_CWATCH_DAT2 0x750 +#define PC_VPC_CWATCH_DAT3 0x758 +#define PC_VPC_CWATCH_DAT4 0x760 +#define PC_VPC_CWATCH_DAT5 0x768 +#define PC_VPC_CWATCH_DAT6 0x770 +#define PC_VPC_CWATCH_DAT7 0x778 + +/* VC0 register offsets 0x800 - 0xFFF */ +#define VC_GLOBAL_CONFIG 0x800 +#define VC_GCONF_INDIRECT PPC_BIT(32) +#define VC_VSD_TABLE_ADDR 0x808 +#define VC_VSD_TABLE_DATA 0x810 +#define VC_IVE_ISB_BLOCK_MODE 0x818 +#define VC_EQD_BLOCK_MODE 0x820 +#define VC_VPS_BLOCK_MODE 0x828 +#define VC_IRQ_CONFIG_IPI 0x840 +#define VC_IRQ_CONFIG_MEMB_EN PPC_BIT(45) +#define VC_IRQ_CONFIG_MEMB_SZ PPC_BITMASK(46, 51) +#define VC_IRQ_CONFIG_HW 0x848 +#define VC_IRQ_CONFIG_CASCADE1 0x850 +#define VC_IRQ_CONFIG_CASCADE2 0x858 +#define VC_IRQ_CONFIG_REDIST 0x860 +#define VC_IRQ_CONFIG_IPI_CASC 0x868 +#define VC_AIB_TX_ORDER_TAG2_REL_TF PPC_BIT(20) +#define VC_AIB_TX_ORDER_TAG2 0x890 +#define VC_AT_MACRO_KILL 0x8b0 +#define VC_AT_MACRO_KILL_MASK 0x8b8 +#define VC_KILL_VALID PPC_BIT(0) +#define VC_KILL_TYPE PPC_BITMASK(14, 15) +#define VC_KILL_IRQ 0 +#define VC_KILL_IVC 1 +#define VC_KILL_SBC 2 +#define VC_KILL_EQD 3 +#define VC_KILL_BLOCK_ID PPC_BITMASK(27, 31) +#define VC_KILL_OFFSET PPC_BITMASK(48, 60) +#define VC_EQC_CACHE_ENABLE 0x908 +#define VC_EQC_CACHE_EN_MASK PPC_BITMASK(0, 15) +#define VC_EQC_SCRUB_TRIG 0x910 +#define VC_EQC_SCRUB_MASK 0x918 +#define VC_EQC_CONFIG 0x920 +#define X_VC_EQC_CONFIG 0x214 /* XSCOM register */ +#define VC_EQC_CONF_SYNC_IPI PPC_BIT(32) +#define VC_EQC_CONF_SYNC_HW PPC_BIT(33) +#define VC_EQC_CONF_SYNC_ESC1 PPC_BIT(34) +#define VC_EQC_CONF_SYNC_ESC2 PPC_BIT(35) +#define VC_EQC_CONF_SYNC_REDI PPC_BIT(36) +#define VC_EQC_CONF_EQP_INTERLEAVE PPC_BIT(38) +#define VC_EQC_CONF_ENABLE_END_s_BIT PPC_BIT(39) +#define VC_EQC_CONF_ENABLE_END_u_BIT PPC_BIT(40) +#define VC_EQC_CONF_ENABLE_END_c_BIT PPC_BIT(41) +#define VC_EQC_CONF_ENABLE_MORE_QSZ PPC_BIT(42) +#define VC_EQC_CONF_SKIP_ESCALATE PPC_BIT(43) +#define VC_EQC_CWATCH_SPEC 0x928 +#define VC_EQC_CWATCH_CONFLICT PPC_BIT(0) +#define VC_EQC_CWATCH_FULL PPC_BIT(8) +#define VC_EQC_CWATCH_BLOCKID PPC_BITMASK(28, 31) +#define VC_EQC_CWATCH_OFFSET PPC_BITMASK(40, 63) +#define VC_EQC_CWATCH_DAT0 0x930 +#define VC_EQC_CWATCH_DAT1 0x938 +#define VC_EQC_CWATCH_DAT2 0x940 +#define VC_EQC_CWATCH_DAT3 0x948 +#define VC_IVC_SCRUB_TRIG 0x990 +#define VC_IVC_SCRUB_MASK 0x998 +#define VC_SBC_SCRUB_TRIG 0xa10 +#define VC_SBC_SCRUB_MASK 0xa18 +#define VC_SCRUB_VALID PPC_BIT(0) +#define VC_SCRUB_WANT_DISABLE PPC_BIT(1) +#define VC_SCRUB_WANT_INVAL PPC_BIT(2) /* EQC and SBC only */ +#define VC_SCRUB_BLOCK_ID PPC_BITMASK(28, 31) +#define VC_SCRUB_OFFSET PPC_BITMASK(40, 63) +#define VC_IVC_CACHE_ENABLE 0x988 +#define VC_IVC_CACHE_EN_MASK PPC_BITMASK(0, 15) +#define VC_SBC_CACHE_ENABLE 0xa08 +#define VC_SBC_CACHE_EN_MASK PPC_BITMASK(0, 15) +#define VC_IVC_CACHE_SCRUB_TRIG 0x990 +#define VC_IVC_CACHE_SCRUB_MASK 0x998 +#define VC_SBC_CACHE_ENABLE 0xa08 +#define VC_SBC_CACHE_SCRUB_TRIG 0xa10 +#define VC_SBC_CACHE_SCRUB_MASK 0xa18 +#define VC_SBC_CONFIG 0xa20 +#define VC_SBC_CONF_CPLX_CIST PPC_BIT(44) +#define VC_SBC_CONF_CIST_BOTH PPC_BIT(45) +#define VC_SBC_CONF_NO_UPD_PRF PPC_BIT(59) + +/* VC1 register offsets */ + +/* VSD Table address register definitions (shared) */ +#define VST_ADDR_AUTOINC PPC_BIT(0) +#define VST_TABLE_SELECT PPC_BITMASK(13, 15) +#define VST_TSEL_IVT 0 +#define VST_TSEL_SBE 1 +#define VST_TSEL_EQDT 2 +#define VST_TSEL_VPDT 3 +#define VST_TSEL_IRQ 4 /* VC only */ +#define VST_TABLE_BLOCK PPC_BITMASK(27, 31) + +/* Number of queue overflow pages */ +#define VC_QUEUE_OVF_COUNT 6 + +/* + * Bits in a VSD entry. + * + * Note: the address is naturally aligned, we don't use a PPC_BITMASK, + * but just a mask to apply to the address before OR'ing it in. + * + * Note: VSD_FIRMWARE is a SW bit ! It hijacks an unused bit in the + * VSD and is only meant to be used in indirect mode ! + */ +#define VSD_MODE PPC_BITMASK(0, 1) +#define VSD_MODE_SHARED 1 +#define VSD_MODE_EXCLUSIVE 2 +#define VSD_MODE_FORWARD 3 +#define VSD_ADDRESS_MASK 0x0ffffffffffff000ull +#define VSD_MIGRATION_REG PPC_BITMASK(52, 55) +#define VSD_INDIRECT PPC_BIT(56) +#define VSD_TSIZE PPC_BITMASK(59, 63) +#define VSD_FIRMWARE PPC_BIT(2) /* Read warning above */ + +#define VC_EQC_SYNC_MASK \ + (VC_EQC_CONF_SYNC_IPI | \ + VC_EQC_CONF_SYNC_HW | \ + VC_EQC_CONF_SYNC_ESC1 | \ + VC_EQC_CONF_SYNC_ESC2 | \ + VC_EQC_CONF_SYNC_REDI) + + +#endif /* PPC_PNV_XIVE_REGS_H */ diff --git a/hw/intc/puv3_intc.c b/hw/intc/puv3_intc.c index ef8488aacc..69ddc8c19a 100644 --- a/hw/intc/puv3_intc.c +++ b/hw/intc/puv3_intc.c @@ -101,10 +101,10 @@ static const MemoryRegionOps puv3_intc_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int puv3_intc_init(SysBusDevice *sbd) +static void puv3_intc_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); PUV3INTCState *s = PUV3_INTC(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); qdev_init_gpio_in(dev, puv3_intc_handler, PUV3_IRQS_NR); sysbus_init_irq(sbd, &s->parent_irq); @@ -115,15 +115,12 @@ static int puv3_intc_init(SysBusDevice *sbd) memory_region_init_io(&s->iomem, OBJECT(s), &puv3_intc_ops, s, "puv3_intc", PUV3_REGS_OFFSET); sysbus_init_mmio(sbd, &s->iomem); - - return 0; } static void puv3_intc_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - - sdc->init = puv3_intc_init; + DeviceClass *dc = DEVICE_CLASS(klass); + dc->realize = puv3_intc_realize; } static const TypeInfo puv3_intc_info = { diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 3f804ad52e..a03df37560 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -376,7 +376,7 @@ static void kvm_s390_release_adapter_routes(S390FLICState *fs, * reached */ static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { KVMS390FLICState *flic = opaque; int len = FLIC_SAVE_INITIAL_SIZE; @@ -426,7 +426,7 @@ static int kvm_flic_save(QEMUFile *f, void *opaque, size_t size, * in QEMUFile */ static int kvm_flic_load(QEMUFile *f, void *opaque, size_t size, - VMStateField *field) + const VMStateField *field) { uint64_t len = 0; uint64_t count = 0; diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c new file mode 100644 index 0000000000..097f88d460 --- /dev/null +++ b/hw/intc/spapr_xive.c @@ -0,0 +1,1487 @@ +/* + * QEMU PowerPC sPAPR XIVE interrupt controller model + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * 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 "qemu/log.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "target/ppc/cpu.h" +#include "sysemu/cpus.h" +#include "monitor/monitor.h" +#include "hw/ppc/fdt.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "hw/ppc/spapr_xive.h" +#include "hw/ppc/xive.h" +#include "hw/ppc/xive_regs.h" + +/* + * XIVE Virtualization Controller BAR and Thread Managment BAR that we + * use for the ESB pages and the TIMA pages + */ +#define SPAPR_XIVE_VC_BASE 0x0006010000000000ull +#define SPAPR_XIVE_TM_BASE 0x0006030203180000ull + +/* + * The allocation of VP blocks is a complex operation in OPAL and the + * VP identifiers have a relation with the number of HW chips, the + * size of the VP blocks, VP grouping, etc. The QEMU sPAPR XIVE + * controller model does not have the same constraints and can use a + * simple mapping scheme of the CPU vcpu_id + * + * These identifiers are never returned to the OS. + */ + +#define SPAPR_XIVE_NVT_BASE 0x400 + +/* + * The sPAPR machine has a unique XIVE IC device. Assign a fixed value + * to the controller block id value. It can nevertheless be changed + * for testing purpose. + */ +#define SPAPR_XIVE_BLOCK_ID 0x0 + +/* + * sPAPR NVT and END indexing helpers + */ +static uint32_t spapr_xive_nvt_to_target(uint8_t nvt_blk, uint32_t nvt_idx) +{ + return nvt_idx - SPAPR_XIVE_NVT_BASE; +} + +static void spapr_xive_cpu_to_nvt(PowerPCCPU *cpu, + uint8_t *out_nvt_blk, uint32_t *out_nvt_idx) +{ + assert(cpu); + + if (out_nvt_blk) { + *out_nvt_blk = SPAPR_XIVE_BLOCK_ID; + } + + if (out_nvt_blk) { + *out_nvt_idx = SPAPR_XIVE_NVT_BASE + cpu->vcpu_id; + } +} + +static int spapr_xive_target_to_nvt(uint32_t target, + uint8_t *out_nvt_blk, uint32_t *out_nvt_idx) +{ + PowerPCCPU *cpu = spapr_find_cpu(target); + + if (!cpu) { + return -1; + } + + spapr_xive_cpu_to_nvt(cpu, out_nvt_blk, out_nvt_idx); + return 0; +} + +/* + * sPAPR END indexing uses a simple mapping of the CPU vcpu_id, 8 + * priorities per CPU + */ +static void spapr_xive_cpu_to_end(PowerPCCPU *cpu, uint8_t prio, + uint8_t *out_end_blk, uint32_t *out_end_idx) +{ + assert(cpu); + + if (out_end_blk) { + *out_end_blk = SPAPR_XIVE_BLOCK_ID; + } + + if (out_end_idx) { + *out_end_idx = (cpu->vcpu_id << 3) + prio; + } +} + +static int spapr_xive_target_to_end(uint32_t target, uint8_t prio, + uint8_t *out_end_blk, uint32_t *out_end_idx) +{ + PowerPCCPU *cpu = spapr_find_cpu(target); + + if (!cpu) { + return -1; + } + + spapr_xive_cpu_to_end(cpu, prio, out_end_blk, out_end_idx); + return 0; +} + +/* + * On sPAPR machines, use a simplified output for the XIVE END + * structure dumping only the information related to the OS EQ. + */ +static void spapr_xive_end_pic_print_info(SpaprXive *xive, XiveEND *end, + Monitor *mon) +{ + uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); + uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1); + uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0); + uint32_t qentries = 1 << (qsize + 10); + uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6); + uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); + + monitor_printf(mon, "%3d/%d % 6d/%5d ^%d", + spapr_xive_nvt_to_target(0, nvt), + priority, qindex, qentries, qgen); + + xive_end_queue_pic_print_info(end, 6, mon); + monitor_printf(mon, "]"); +} + +void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon) +{ + XiveSource *xsrc = &xive->source; + int i; + + monitor_printf(mon, " LSIN PQ EISN CPU/PRIO EQ\n"); + + for (i = 0; i < xive->nr_irqs; i++) { + uint8_t pq = xive_source_esb_get(xsrc, i); + XiveEAS *eas = &xive->eat[i]; + + if (!xive_eas_is_valid(eas)) { + continue; + } + + monitor_printf(mon, " %08x %s %c%c%c %s %08x ", i, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' ', + xive_eas_is_masked(eas) ? "M" : " ", + (int) xive_get_field64(EAS_END_DATA, eas->w)); + + if (!xive_eas_is_masked(eas)) { + uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); + XiveEND *end; + + assert(end_idx < xive->nr_ends); + end = &xive->endt[end_idx]; + + if (xive_end_is_valid(end)) { + spapr_xive_end_pic_print_info(xive, end, mon); + } + } + monitor_printf(mon, "\n"); + } +} + +static void spapr_xive_map_mmio(SpaprXive *xive) +{ + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 0, xive->vc_base); + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 1, xive->end_base); + sysbus_mmio_map(SYS_BUS_DEVICE(xive), 2, xive->tm_base); +} + +void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable) +{ + memory_region_set_enabled(&xive->source.esb_mmio, enable); + memory_region_set_enabled(&xive->tm_mmio, enable); + + /* Disable the END ESBs until a guest OS makes use of them */ + memory_region_set_enabled(&xive->end_source.esb_mmio, false); +} + +/* + * When a Virtual Processor is scheduled to run on a HW thread, the + * hypervisor pushes its identifier in the OS CAM line. Emulate the + * same behavior under QEMU. + */ +void spapr_xive_set_tctx_os_cam(XiveTCTX *tctx) +{ + uint8_t nvt_blk; + uint32_t nvt_idx; + uint32_t nvt_cam; + + spapr_xive_cpu_to_nvt(POWERPC_CPU(tctx->cs), &nvt_blk, &nvt_idx); + + nvt_cam = cpu_to_be32(TM_QW1W2_VO | xive_nvt_cam_line(nvt_blk, nvt_idx)); + memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &nvt_cam, 4); +} + +static void spapr_xive_end_reset(XiveEND *end) +{ + memset(end, 0, sizeof(*end)); + + /* switch off the escalation and notification ESBs */ + end->w1 = cpu_to_be32(END_W1_ESe_Q | END_W1_ESn_Q); +} + +static void spapr_xive_reset(void *dev) +{ + SpaprXive *xive = SPAPR_XIVE(dev); + int i; + + /* + * The XiveSource has its own reset handler, which mask off all + * IRQs (!P|Q) + */ + + /* Mask all valid EASs in the IRQ number space. */ + for (i = 0; i < xive->nr_irqs; i++) { + XiveEAS *eas = &xive->eat[i]; + if (xive_eas_is_valid(eas)) { + eas->w = cpu_to_be64(EAS_VALID | EAS_MASKED); + } else { + eas->w = 0; + } + } + + /* Clear all ENDs */ + for (i = 0; i < xive->nr_ends; i++) { + spapr_xive_end_reset(&xive->endt[i]); + } +} + +static void spapr_xive_instance_init(Object *obj) +{ + SpaprXive *xive = SPAPR_XIVE(obj); + + object_initialize_child(obj, "source", &xive->source, sizeof(xive->source), + TYPE_XIVE_SOURCE, &error_abort, NULL); + + object_initialize_child(obj, "end_source", &xive->end_source, + sizeof(xive->end_source), TYPE_XIVE_END_SOURCE, + &error_abort, NULL); +} + +static void spapr_xive_realize(DeviceState *dev, Error **errp) +{ + SpaprXive *xive = SPAPR_XIVE(dev); + XiveSource *xsrc = &xive->source; + XiveENDSource *end_xsrc = &xive->end_source; + Error *local_err = NULL; + + if (!xive->nr_irqs) { + error_setg(errp, "Number of interrupt needs to be greater 0"); + return; + } + + if (!xive->nr_ends) { + error_setg(errp, "Number of interrupt needs to be greater 0"); + return; + } + + /* + * Initialize the internal sources, for IPIs and virtual devices. + */ + object_property_set_int(OBJECT(xsrc), xive->nr_irqs, "nr-irqs", + &error_fatal); + object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(xive), + &error_fatal); + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * Initialize the END ESB source + */ + object_property_set_int(OBJECT(end_xsrc), xive->nr_irqs, "nr-ends", + &error_fatal); + object_property_add_const_link(OBJECT(end_xsrc), "xive", OBJECT(xive), + &error_fatal); + object_property_set_bool(OBJECT(end_xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Set the mapping address of the END ESB pages after the source ESBs */ + xive->end_base = xive->vc_base + (1ull << xsrc->esb_shift) * xsrc->nr_irqs; + + /* + * Allocate the routing tables + */ + xive->eat = g_new0(XiveEAS, xive->nr_irqs); + xive->endt = g_new0(XiveEND, xive->nr_ends); + + /* TIMA initialization */ + memory_region_init_io(&xive->tm_mmio, OBJECT(xive), &xive_tm_ops, xive, + "xive.tima", 4ull << TM_SHIFT); + + /* Define all XIVE MMIO regions on SysBus */ + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xsrc->esb_mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &end_xsrc->esb_mmio); + sysbus_init_mmio(SYS_BUS_DEVICE(xive), &xive->tm_mmio); + + /* Map all regions */ + spapr_xive_map_mmio(xive); + + xive->nodename = g_strdup_printf("interrupt-controller@%" PRIx64, + xive->tm_base + XIVE_TM_USER_PAGE * (1 << TM_SHIFT)); + + qemu_register_reset(spapr_xive_reset, dev); +} + +static int spapr_xive_get_eas(XiveRouter *xrtr, uint8_t eas_blk, + uint32_t eas_idx, XiveEAS *eas) +{ + SpaprXive *xive = SPAPR_XIVE(xrtr); + + if (eas_idx >= xive->nr_irqs) { + return -1; + } + + *eas = xive->eat[eas_idx]; + return 0; +} + +static int spapr_xive_get_end(XiveRouter *xrtr, + uint8_t end_blk, uint32_t end_idx, XiveEND *end) +{ + SpaprXive *xive = SPAPR_XIVE(xrtr); + + if (end_idx >= xive->nr_ends) { + return -1; + } + + memcpy(end, &xive->endt[end_idx], sizeof(XiveEND)); + return 0; +} + +static int spapr_xive_write_end(XiveRouter *xrtr, uint8_t end_blk, + uint32_t end_idx, XiveEND *end, + uint8_t word_number) +{ + SpaprXive *xive = SPAPR_XIVE(xrtr); + + if (end_idx >= xive->nr_ends) { + return -1; + } + + memcpy(&xive->endt[end_idx], end, sizeof(XiveEND)); + return 0; +} + +static int spapr_xive_get_nvt(XiveRouter *xrtr, + uint8_t nvt_blk, uint32_t nvt_idx, XiveNVT *nvt) +{ + uint32_t vcpu_id = spapr_xive_nvt_to_target(nvt_blk, nvt_idx); + PowerPCCPU *cpu = spapr_find_cpu(vcpu_id); + + if (!cpu) { + /* TODO: should we assert() if we can find a NVT ? */ + return -1; + } + + /* + * sPAPR does not maintain a NVT table. Return that the NVT is + * valid if we have found a matching CPU + */ + nvt->w0 = cpu_to_be32(NVT_W0_VALID); + return 0; +} + +static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, + uint32_t nvt_idx, XiveNVT *nvt, + uint8_t word_number) +{ + /* + * We don't need to write back to the NVTs because the sPAPR + * machine should never hit a non-scheduled NVT. It should never + * get called. + */ + g_assert_not_reached(); +} + +static XiveTCTX *spapr_xive_get_tctx(XiveRouter *xrtr, CPUState *cs) +{ + PowerPCCPU *cpu = POWERPC_CPU(cs); + + return spapr_cpu_state(cpu)->tctx; +} + +static const VMStateDescription vmstate_spapr_xive_end = { + .name = TYPE_SPAPR_XIVE "/end", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT32(w0, XiveEND), + VMSTATE_UINT32(w1, XiveEND), + VMSTATE_UINT32(w2, XiveEND), + VMSTATE_UINT32(w3, XiveEND), + VMSTATE_UINT32(w4, XiveEND), + VMSTATE_UINT32(w5, XiveEND), + VMSTATE_UINT32(w6, XiveEND), + VMSTATE_UINT32(w7, XiveEND), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_spapr_xive_eas = { + .name = TYPE_SPAPR_XIVE "/eas", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField []) { + VMSTATE_UINT64(w, XiveEAS), + VMSTATE_END_OF_LIST() + }, +}; + +static const VMStateDescription vmstate_spapr_xive = { + .name = TYPE_SPAPR_XIVE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_EQUAL(nr_irqs, SpaprXive, NULL), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(eat, SpaprXive, nr_irqs, + vmstate_spapr_xive_eas, XiveEAS), + VMSTATE_STRUCT_VARRAY_POINTER_UINT32(endt, SpaprXive, nr_ends, + vmstate_spapr_xive_end, XiveEND), + VMSTATE_END_OF_LIST() + }, +}; + +static Property spapr_xive_properties[] = { + DEFINE_PROP_UINT32("nr-irqs", SpaprXive, nr_irqs, 0), + DEFINE_PROP_UINT32("nr-ends", SpaprXive, nr_ends, 0), + DEFINE_PROP_UINT64("vc-base", SpaprXive, vc_base, SPAPR_XIVE_VC_BASE), + DEFINE_PROP_UINT64("tm-base", SpaprXive, tm_base, SPAPR_XIVE_TM_BASE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void spapr_xive_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); + + dc->desc = "sPAPR XIVE Interrupt Controller"; + dc->props = spapr_xive_properties; + dc->realize = spapr_xive_realize; + dc->vmsd = &vmstate_spapr_xive; + + xrc->get_eas = spapr_xive_get_eas; + xrc->get_end = spapr_xive_get_end; + xrc->write_end = spapr_xive_write_end; + xrc->get_nvt = spapr_xive_get_nvt; + xrc->write_nvt = spapr_xive_write_nvt; + xrc->get_tctx = spapr_xive_get_tctx; +} + +static const TypeInfo spapr_xive_info = { + .name = TYPE_SPAPR_XIVE, + .parent = TYPE_XIVE_ROUTER, + .instance_init = spapr_xive_instance_init, + .instance_size = sizeof(SpaprXive), + .class_init = spapr_xive_class_init, +}; + +static void spapr_xive_register_types(void) +{ + type_register_static(&spapr_xive_info); +} + +type_init(spapr_xive_register_types) + +bool spapr_xive_irq_claim(SpaprXive *xive, uint32_t lisn, bool lsi) +{ + XiveSource *xsrc = &xive->source; + + if (lisn >= xive->nr_irqs) { + return false; + } + + xive->eat[lisn].w |= cpu_to_be64(EAS_VALID); + if (lsi) { + xive_source_irq_set_lsi(xsrc, lisn); + } + return true; +} + +bool spapr_xive_irq_free(SpaprXive *xive, uint32_t lisn) +{ + if (lisn >= xive->nr_irqs) { + return false; + } + + xive->eat[lisn].w &= cpu_to_be64(~EAS_VALID); + return true; +} + +/* + * XIVE hcalls + * + * The terminology used by the XIVE hcalls is the following : + * + * TARGET vCPU number + * EQ Event Queue assigned by OS to receive event data + * ESB page for source interrupt management + * LISN Logical Interrupt Source Number identifying a source in the + * machine + * EISN Effective Interrupt Source Number used by guest OS to + * identify source in the guest + * + * The EAS, END, NVT structures are not exposed. + */ + +/* + * Linux hosts under OPAL reserve priority 7 for their own escalation + * interrupts (DD2.X POWER9). So we only allow the guest to use + * priorities [0..6]. + */ +static bool spapr_xive_priority_is_reserved(uint8_t priority) +{ + switch (priority) { + case 0 ... 6: + return false; + case 7: /* OPAL escalation queue */ + default: + return true; + } +} + +/* + * The H_INT_GET_SOURCE_INFO hcall() is used to obtain the logical + * real address of the MMIO page through which the Event State Buffer + * entry associated with the value of the "lisn" parameter is managed. + * + * Parameters: + * Input + * - R4: "flags" + * Bits 0-63 reserved + * - R5: "lisn" is per "interrupts", "interrupt-map", or + * "ibm,xive-lisn-ranges" properties, or as returned by the + * ibm,query-interrupt-source-number RTAS call, or as returned + * by the H_ALLOCATE_VAS_WINDOW hcall + * + * Output + * - R4: "flags" + * Bits 0-59: Reserved + * Bit 60: H_INT_ESB must be used for Event State Buffer + * management + * Bit 61: 1 == LSI 0 == MSI + * Bit 62: the full function page supports trigger + * Bit 63: Store EOI Supported + * - R5: Logical Real address of full function Event State Buffer + * management page, -1 if H_INT_ESB hcall flag is set to 1. + * - R6: Logical Real Address of trigger only Event State Buffer + * management page or -1. + * - R7: Power of 2 page size for the ESB management pages returned in + * R5 and R6. + */ + +#define SPAPR_XIVE_SRC_H_INT_ESB PPC_BIT(60) /* ESB manage with H_INT_ESB */ +#define SPAPR_XIVE_SRC_LSI PPC_BIT(61) /* Virtual LSI type */ +#define SPAPR_XIVE_SRC_TRIGGER PPC_BIT(62) /* Trigger and management + on same page */ +#define SPAPR_XIVE_SRC_STORE_EOI PPC_BIT(63) /* Store EOI support */ + +static target_ulong h_int_get_source_info(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + XiveSource *xsrc = &xive->source; + target_ulong flags = args[0]; + target_ulong lisn = args[1]; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags) { + return H_PARAMETER; + } + + if (lisn >= xive->nr_irqs) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + if (!xive_eas_is_valid(&xive->eat[lisn])) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + /* + * All sources are emulated under the main XIVE object and share + * the same characteristics. + */ + args[0] = 0; + if (!xive_source_esb_has_2page(xsrc)) { + args[0] |= SPAPR_XIVE_SRC_TRIGGER; + } + if (xsrc->esb_flags & XIVE_SRC_STORE_EOI) { + args[0] |= SPAPR_XIVE_SRC_STORE_EOI; + } + + /* + * Force the use of the H_INT_ESB hcall in case of an LSI + * interrupt. This is necessary under KVM to re-trigger the + * interrupt if the level is still asserted + */ + if (xive_source_irq_is_lsi(xsrc, lisn)) { + args[0] |= SPAPR_XIVE_SRC_H_INT_ESB | SPAPR_XIVE_SRC_LSI; + } + + if (!(args[0] & SPAPR_XIVE_SRC_H_INT_ESB)) { + args[1] = xive->vc_base + xive_source_esb_mgmt(xsrc, lisn); + } else { + args[1] = -1; + } + + if (xive_source_esb_has_2page(xsrc) && + !(args[0] & SPAPR_XIVE_SRC_H_INT_ESB)) { + args[2] = xive->vc_base + xive_source_esb_page(xsrc, lisn); + } else { + args[2] = -1; + } + + if (xive_source_esb_has_2page(xsrc)) { + args[3] = xsrc->esb_shift - 1; + } else { + args[3] = xsrc->esb_shift; + } + + return H_SUCCESS; +} + +/* + * The H_INT_SET_SOURCE_CONFIG hcall() is used to assign a Logical + * Interrupt Source to a target. The Logical Interrupt Source is + * designated with the "lisn" parameter and the target is designated + * with the "target" and "priority" parameters. Upon return from the + * hcall(), no additional interrupts will be directed to the old EQ. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-61: Reserved + * Bit 62: set the "eisn" in the EAS + * Bit 63: masks the interrupt source in the hardware interrupt + * control structure. An interrupt masked by this mechanism will + * be dropped, but it's source state bits will still be + * set. There is no race-free way of unmasking and restoring the + * source. Thus this should only be used in interrupts that are + * also masked at the source, and only in cases where the + * interrupt is not meant to be used for a large amount of time + * because no valid target exists for it for example + * - R5: "lisn" is per "interrupts", "interrupt-map", or + * "ibm,xive-lisn-ranges" properties, or as returned by the + * ibm,query-interrupt-source-number RTAS call, or as returned by + * the H_ALLOCATE_VAS_WINDOW hcall + * - R6: "target" is per "ibm,ppc-interrupt-server#s" or + * "ibm,ppc-interrupt-gserver#s" + * - R7: "priority" is a valid priority not in + * "ibm,plat-res-int-priorities" + * - R8: "eisn" is the guest EISN associated with the "lisn" + * + * Output: + * - None + */ + +#define SPAPR_XIVE_SRC_SET_EISN PPC_BIT(62) +#define SPAPR_XIVE_SRC_MASK PPC_BIT(63) + +static target_ulong h_int_set_source_config(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + XiveEAS eas, new_eas; + target_ulong flags = args[0]; + target_ulong lisn = args[1]; + target_ulong target = args[2]; + target_ulong priority = args[3]; + target_ulong eisn = args[4]; + uint8_t end_blk; + uint32_t end_idx; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags & ~(SPAPR_XIVE_SRC_SET_EISN | SPAPR_XIVE_SRC_MASK)) { + return H_PARAMETER; + } + + if (lisn >= xive->nr_irqs) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + eas = xive->eat[lisn]; + if (!xive_eas_is_valid(&eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + /* priority 0xff is used to reset the EAS */ + if (priority == 0xff) { + new_eas.w = cpu_to_be64(EAS_VALID | EAS_MASKED); + goto out; + } + + if (flags & SPAPR_XIVE_SRC_MASK) { + new_eas.w = eas.w | cpu_to_be64(EAS_MASKED); + } else { + new_eas.w = eas.w & cpu_to_be64(~EAS_MASKED); + } + + if (spapr_xive_priority_is_reserved(priority)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: priority " TARGET_FMT_ld + " is reserved\n", priority); + return H_P4; + } + + /* + * Validate that "target" is part of the list of threads allocated + * to the partition. For that, find the END corresponding to the + * target. + */ + if (spapr_xive_target_to_end(target, priority, &end_blk, &end_idx)) { + return H_P3; + } + + new_eas.w = xive_set_field64(EAS_END_BLOCK, new_eas.w, end_blk); + new_eas.w = xive_set_field64(EAS_END_INDEX, new_eas.w, end_idx); + + if (flags & SPAPR_XIVE_SRC_SET_EISN) { + new_eas.w = xive_set_field64(EAS_END_DATA, new_eas.w, eisn); + } + +out: + xive->eat[lisn] = new_eas; + return H_SUCCESS; +} + +/* + * The H_INT_GET_SOURCE_CONFIG hcall() is used to determine to which + * target/priority pair is assigned to the specified Logical Interrupt + * Source. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-63 Reserved + * - R5: "lisn" is per "interrupts", "interrupt-map", or + * "ibm,xive-lisn-ranges" properties, or as returned by the + * ibm,query-interrupt-source-number RTAS call, or as + * returned by the H_ALLOCATE_VAS_WINDOW hcall + * + * Output: + * - R4: Target to which the specified Logical Interrupt Source is + * assigned + * - R5: Priority to which the specified Logical Interrupt Source is + * assigned + * - R6: EISN for the specified Logical Interrupt Source (this will be + * equivalent to the LISN if not changed by H_INT_SET_SOURCE_CONFIG) + */ +static target_ulong h_int_get_source_config(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + target_ulong flags = args[0]; + target_ulong lisn = args[1]; + XiveEAS eas; + XiveEND *end; + uint8_t nvt_blk; + uint32_t end_idx, nvt_idx; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags) { + return H_PARAMETER; + } + + if (lisn >= xive->nr_irqs) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + eas = xive->eat[lisn]; + if (!xive_eas_is_valid(&eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + /* EAS_END_BLOCK is unused on sPAPR */ + end_idx = xive_get_field64(EAS_END_INDEX, eas.w); + + assert(end_idx < xive->nr_ends); + end = &xive->endt[end_idx]; + + nvt_blk = xive_get_field32(END_W6_NVT_BLOCK, end->w6); + nvt_idx = xive_get_field32(END_W6_NVT_INDEX, end->w6); + args[0] = spapr_xive_nvt_to_target(nvt_blk, nvt_idx); + + if (xive_eas_is_masked(&eas)) { + args[1] = 0xff; + } else { + args[1] = xive_get_field32(END_W7_F0_PRIORITY, end->w7); + } + + args[2] = xive_get_field64(EAS_END_DATA, eas.w); + + return H_SUCCESS; +} + +/* + * The H_INT_GET_QUEUE_INFO hcall() is used to get the logical real + * address of the notification management page associated with the + * specified target and priority. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-63 Reserved + * - R5: "target" is per "ibm,ppc-interrupt-server#s" or + * "ibm,ppc-interrupt-gserver#s" + * - R6: "priority" is a valid priority not in + * "ibm,plat-res-int-priorities" + * + * Output: + * - R4: Logical real address of notification page + * - R5: Power of 2 page size of the notification page + */ +static target_ulong h_int_get_queue_info(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + XiveENDSource *end_xsrc = &xive->end_source; + target_ulong flags = args[0]; + target_ulong target = args[1]; + target_ulong priority = args[2]; + XiveEND *end; + uint8_t end_blk; + uint32_t end_idx; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags) { + return H_PARAMETER; + } + + /* + * H_STATE should be returned if a H_INT_RESET is in progress. + * This is not needed when running the emulation under QEMU + */ + + if (spapr_xive_priority_is_reserved(priority)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: priority " TARGET_FMT_ld + " is reserved\n", priority); + return H_P3; + } + + /* + * Validate that "target" is part of the list of threads allocated + * to the partition. For that, find the END corresponding to the + * target. + */ + if (spapr_xive_target_to_end(target, priority, &end_blk, &end_idx)) { + return H_P2; + } + + assert(end_idx < xive->nr_ends); + end = &xive->endt[end_idx]; + + args[0] = xive->end_base + (1ull << (end_xsrc->esb_shift + 1)) * end_idx; + if (xive_end_is_enqueue(end)) { + args[1] = xive_get_field32(END_W0_QSIZE, end->w0) + 12; + } else { + args[1] = 0; + } + + return H_SUCCESS; +} + +/* + * The H_INT_SET_QUEUE_CONFIG hcall() is used to set or reset a EQ for + * a given "target" and "priority". It is also used to set the + * notification config associated with the EQ. An EQ size of 0 is + * used to reset the EQ config for a given target and priority. If + * resetting the EQ config, the END associated with the given "target" + * and "priority" will be changed to disable queueing. + * + * Upon return from the hcall(), no additional interrupts will be + * directed to the old EQ (if one was set). The old EQ (if one was + * set) should be investigated for interrupts that occurred prior to + * or during the hcall(). + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-62: Reserved + * Bit 63: Unconditional Notify (n) per the XIVE spec + * - R5: "target" is per "ibm,ppc-interrupt-server#s" or + * "ibm,ppc-interrupt-gserver#s" + * - R6: "priority" is a valid priority not in + * "ibm,plat-res-int-priorities" + * - R7: "eventQueue": The logical real address of the start of the EQ + * - R8: "eventQueueSize": The power of 2 EQ size per "ibm,xive-eq-sizes" + * + * Output: + * - None + */ + +#define SPAPR_XIVE_END_ALWAYS_NOTIFY PPC_BIT(63) + +static target_ulong h_int_set_queue_config(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + target_ulong flags = args[0]; + target_ulong target = args[1]; + target_ulong priority = args[2]; + target_ulong qpage = args[3]; + target_ulong qsize = args[4]; + XiveEND end; + uint8_t end_blk, nvt_blk; + uint32_t end_idx, nvt_idx; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags & ~SPAPR_XIVE_END_ALWAYS_NOTIFY) { + return H_PARAMETER; + } + + /* + * H_STATE should be returned if a H_INT_RESET is in progress. + * This is not needed when running the emulation under QEMU + */ + + if (spapr_xive_priority_is_reserved(priority)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: priority " TARGET_FMT_ld + " is reserved\n", priority); + return H_P3; + } + + /* + * Validate that "target" is part of the list of threads allocated + * to the partition. For that, find the END corresponding to the + * target. + */ + + if (spapr_xive_target_to_end(target, priority, &end_blk, &end_idx)) { + return H_P2; + } + + assert(end_idx < xive->nr_ends); + memcpy(&end, &xive->endt[end_idx], sizeof(XiveEND)); + + switch (qsize) { + case 12: + case 16: + case 21: + case 24: + end.w2 = cpu_to_be32((qpage >> 32) & 0x0fffffff); + end.w3 = cpu_to_be32(qpage & 0xffffffff); + end.w0 |= cpu_to_be32(END_W0_ENQUEUE); + end.w0 = xive_set_field32(END_W0_QSIZE, end.w0, qsize - 12); + break; + case 0: + /* reset queue and disable queueing */ + spapr_xive_end_reset(&end); + goto out; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid EQ size %"PRIx64"\n", + qsize); + return H_P5; + } + + if (qsize) { + hwaddr plen = 1 << qsize; + void *eq; + + /* + * Validate the guest EQ. We should also check that the queue + * has been zeroed by the OS. + */ + eq = address_space_map(CPU(cpu)->as, qpage, &plen, true, + MEMTXATTRS_UNSPECIFIED); + if (plen != 1 << qsize) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to map EQ @0x%" + HWADDR_PRIx "\n", qpage); + return H_P4; + } + address_space_unmap(CPU(cpu)->as, eq, plen, true, plen); + } + + /* "target" should have been validated above */ + if (spapr_xive_target_to_nvt(target, &nvt_blk, &nvt_idx)) { + g_assert_not_reached(); + } + + /* + * Ensure the priority and target are correctly set (they will not + * be right after allocation) + */ + end.w6 = xive_set_field32(END_W6_NVT_BLOCK, 0ul, nvt_blk) | + xive_set_field32(END_W6_NVT_INDEX, 0ul, nvt_idx); + end.w7 = xive_set_field32(END_W7_F0_PRIORITY, 0ul, priority); + + if (flags & SPAPR_XIVE_END_ALWAYS_NOTIFY) { + end.w0 |= cpu_to_be32(END_W0_UCOND_NOTIFY); + } else { + end.w0 &= cpu_to_be32((uint32_t)~END_W0_UCOND_NOTIFY); + } + + /* + * The generation bit for the END starts at 1 and The END page + * offset counter starts at 0. + */ + end.w1 = cpu_to_be32(END_W1_GENERATION) | + xive_set_field32(END_W1_PAGE_OFF, 0ul, 0ul); + end.w0 |= cpu_to_be32(END_W0_VALID); + + /* + * TODO: issue syncs required to ensure all in-flight interrupts + * are complete on the old END + */ + +out: + /* Update END */ + memcpy(&xive->endt[end_idx], &end, sizeof(XiveEND)); + return H_SUCCESS; +} + +/* + * The H_INT_GET_QUEUE_CONFIG hcall() is used to get a EQ for a given + * target and priority. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-62: Reserved + * Bit 63: Debug: Return debug data + * - R5: "target" is per "ibm,ppc-interrupt-server#s" or + * "ibm,ppc-interrupt-gserver#s" + * - R6: "priority" is a valid priority not in + * "ibm,plat-res-int-priorities" + * + * Output: + * - R4: "flags": + * Bits 0-61: Reserved + * Bit 62: The value of Event Queue Generation Number (g) per + * the XIVE spec if "Debug" = 1 + * Bit 63: The value of Unconditional Notify (n) per the XIVE spec + * - R5: The logical real address of the start of the EQ + * - R6: The power of 2 EQ size per "ibm,xive-eq-sizes" + * - R7: The value of Event Queue Offset Counter per XIVE spec + * if "Debug" = 1, else 0 + * + */ + +#define SPAPR_XIVE_END_DEBUG PPC_BIT(63) + +static target_ulong h_int_get_queue_config(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + target_ulong flags = args[0]; + target_ulong target = args[1]; + target_ulong priority = args[2]; + XiveEND *end; + uint8_t end_blk; + uint32_t end_idx; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags & ~SPAPR_XIVE_END_DEBUG) { + return H_PARAMETER; + } + + /* + * H_STATE should be returned if a H_INT_RESET is in progress. + * This is not needed when running the emulation under QEMU + */ + + if (spapr_xive_priority_is_reserved(priority)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: priority " TARGET_FMT_ld + " is reserved\n", priority); + return H_P3; + } + + /* + * Validate that "target" is part of the list of threads allocated + * to the partition. For that, find the END corresponding to the + * target. + */ + if (spapr_xive_target_to_end(target, priority, &end_blk, &end_idx)) { + return H_P2; + } + + assert(end_idx < xive->nr_ends); + end = &xive->endt[end_idx]; + + args[0] = 0; + if (xive_end_is_notify(end)) { + args[0] |= SPAPR_XIVE_END_ALWAYS_NOTIFY; + } + + if (xive_end_is_enqueue(end)) { + args[1] = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32 + | be32_to_cpu(end->w3); + args[2] = xive_get_field32(END_W0_QSIZE, end->w0) + 12; + } else { + args[1] = 0; + args[2] = 0; + } + + /* TODO: do we need any locking on the END ? */ + if (flags & SPAPR_XIVE_END_DEBUG) { + /* Load the event queue generation number into the return flags */ + args[0] |= (uint64_t)xive_get_field32(END_W1_GENERATION, end->w1) << 62; + + /* Load R7 with the event queue offset counter */ + args[3] = xive_get_field32(END_W1_PAGE_OFF, end->w1); + } else { + args[3] = 0; + } + + return H_SUCCESS; +} + +/* + * The H_INT_SET_OS_REPORTING_LINE hcall() is used to set the + * reporting cache line pair for the calling thread. The reporting + * cache lines will contain the OS interrupt context when the OS + * issues a CI store byte to @TIMA+0xC10 to acknowledge the OS + * interrupt. The reporting cache lines can be reset by inputting -1 + * in "reportingLine". Issuing the CI store byte without reporting + * cache lines registered will result in the data not being accessible + * to the OS. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-63: Reserved + * - R5: "reportingLine": The logical real address of the reporting cache + * line pair + * + * Output: + * - None + */ +static target_ulong h_int_set_os_reporting_line(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + /* + * H_STATE should be returned if a H_INT_RESET is in progress. + * This is not needed when running the emulation under QEMU + */ + + /* TODO: H_INT_SET_OS_REPORTING_LINE */ + return H_FUNCTION; +} + +/* + * The H_INT_GET_OS_REPORTING_LINE hcall() is used to get the logical + * real address of the reporting cache line pair set for the input + * "target". If no reporting cache line pair has been set, -1 is + * returned. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-63: Reserved + * - R5: "target" is per "ibm,ppc-interrupt-server#s" or + * "ibm,ppc-interrupt-gserver#s" + * - R6: "reportingLine": The logical real address of the reporting + * cache line pair + * + * Output: + * - R4: The logical real address of the reporting line if set, else -1 + */ +static target_ulong h_int_get_os_reporting_line(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + /* + * H_STATE should be returned if a H_INT_RESET is in progress. + * This is not needed when running the emulation under QEMU + */ + + /* TODO: H_INT_GET_OS_REPORTING_LINE */ + return H_FUNCTION; +} + +/* + * The H_INT_ESB hcall() is used to issue a load or store to the ESB + * page for the input "lisn". This hcall is only supported for LISNs + * that have the ESB hcall flag set to 1 when returned from hcall() + * H_INT_GET_SOURCE_INFO. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-62: Reserved + * bit 63: Store: Store=1, store operation, else load operation + * - R5: "lisn" is per "interrupts", "interrupt-map", or + * "ibm,xive-lisn-ranges" properties, or as returned by the + * ibm,query-interrupt-source-number RTAS call, or as + * returned by the H_ALLOCATE_VAS_WINDOW hcall + * - R6: "esbOffset" is the offset into the ESB page for the load or + * store operation + * - R7: "storeData" is the data to write for a store operation + * + * Output: + * - R4: The value of the load if load operation, else -1 + */ + +#define SPAPR_XIVE_ESB_STORE PPC_BIT(63) + +static target_ulong h_int_esb(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + XiveEAS eas; + target_ulong flags = args[0]; + target_ulong lisn = args[1]; + target_ulong offset = args[2]; + target_ulong data = args[3]; + hwaddr mmio_addr; + XiveSource *xsrc = &xive->source; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags & ~SPAPR_XIVE_ESB_STORE) { + return H_PARAMETER; + } + + if (lisn >= xive->nr_irqs) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + eas = xive->eat[lisn]; + if (!xive_eas_is_valid(&eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + if (offset > (1ull << xsrc->esb_shift)) { + return H_P3; + } + + mmio_addr = xive->vc_base + xive_source_esb_mgmt(xsrc, lisn) + offset; + + if (dma_memory_rw(&address_space_memory, mmio_addr, &data, 8, + (flags & SPAPR_XIVE_ESB_STORE))) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to access ESB @0x%" + HWADDR_PRIx "\n", mmio_addr); + return H_HARDWARE; + } + args[0] = (flags & SPAPR_XIVE_ESB_STORE) ? -1 : data; + return H_SUCCESS; +} + +/* + * The H_INT_SYNC hcall() is used to issue hardware syncs that will + * ensure any in flight events for the input lisn are in the event + * queue. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-63: Reserved + * - R5: "lisn" is per "interrupts", "interrupt-map", or + * "ibm,xive-lisn-ranges" properties, or as returned by the + * ibm,query-interrupt-source-number RTAS call, or as + * returned by the H_ALLOCATE_VAS_WINDOW hcall + * + * Output: + * - None + */ +static target_ulong h_int_sync(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + XiveEAS eas; + target_ulong flags = args[0]; + target_ulong lisn = args[1]; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags) { + return H_PARAMETER; + } + + if (lisn >= xive->nr_irqs) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + eas = xive->eat[lisn]; + if (!xive_eas_is_valid(&eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Invalid LISN " TARGET_FMT_lx "\n", + lisn); + return H_P2; + } + + /* + * H_STATE should be returned if a H_INT_RESET is in progress. + * This is not needed when running the emulation under QEMU + */ + + /* This is not real hardware. Nothing to be done */ + return H_SUCCESS; +} + +/* + * The H_INT_RESET hcall() is used to reset all of the partition's + * interrupt exploitation structures to their initial state. This + * means losing all previously set interrupt state set via + * H_INT_SET_SOURCE_CONFIG and H_INT_SET_QUEUE_CONFIG. + * + * Parameters: + * Input: + * - R4: "flags" + * Bits 0-63: Reserved + * + * Output: + * - None + */ +static target_ulong h_int_reset(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + SpaprXive *xive = spapr->xive; + target_ulong flags = args[0]; + + if (!spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + return H_FUNCTION; + } + + if (flags) { + return H_PARAMETER; + } + + device_reset(DEVICE(xive)); + return H_SUCCESS; +} + +void spapr_xive_hcall_init(SpaprMachineState *spapr) +{ + spapr_register_hypercall(H_INT_GET_SOURCE_INFO, h_int_get_source_info); + spapr_register_hypercall(H_INT_SET_SOURCE_CONFIG, h_int_set_source_config); + spapr_register_hypercall(H_INT_GET_SOURCE_CONFIG, h_int_get_source_config); + spapr_register_hypercall(H_INT_GET_QUEUE_INFO, h_int_get_queue_info); + spapr_register_hypercall(H_INT_SET_QUEUE_CONFIG, h_int_set_queue_config); + spapr_register_hypercall(H_INT_GET_QUEUE_CONFIG, h_int_get_queue_config); + spapr_register_hypercall(H_INT_SET_OS_REPORTING_LINE, + h_int_set_os_reporting_line); + spapr_register_hypercall(H_INT_GET_OS_REPORTING_LINE, + h_int_get_os_reporting_line); + spapr_register_hypercall(H_INT_ESB, h_int_esb); + spapr_register_hypercall(H_INT_SYNC, h_int_sync); + spapr_register_hypercall(H_INT_RESET, h_int_reset); +} + +void spapr_dt_xive(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt, + uint32_t phandle) +{ + SpaprXive *xive = spapr->xive; + int node; + uint64_t timas[2 * 2]; + /* Interrupt number ranges for the IPIs */ + uint32_t lisn_ranges[] = { + cpu_to_be32(0), + cpu_to_be32(nr_servers), + }; + /* + * EQ size - the sizes of pages supported by the system 4K, 64K, + * 2M, 16M. We only advertise 64K for the moment. + */ + uint32_t eq_sizes[] = { + cpu_to_be32(16), /* 64K */ + }; + /* + * The following array is in sync with the reserved priorities + * defined by the 'spapr_xive_priority_is_reserved' routine. + */ + uint32_t plat_res_int_priorities[] = { + cpu_to_be32(7), /* start */ + cpu_to_be32(0xf8), /* count */ + }; + + /* Thread Interrupt Management Area : User (ring 3) and OS (ring 2) */ + timas[0] = cpu_to_be64(xive->tm_base + + XIVE_TM_USER_PAGE * (1ull << TM_SHIFT)); + timas[1] = cpu_to_be64(1ull << TM_SHIFT); + timas[2] = cpu_to_be64(xive->tm_base + + XIVE_TM_OS_PAGE * (1ull << TM_SHIFT)); + timas[3] = cpu_to_be64(1ull << TM_SHIFT); + + _FDT(node = fdt_add_subnode(fdt, 0, xive->nodename)); + + _FDT(fdt_setprop_string(fdt, node, "device_type", "power-ivpe")); + _FDT(fdt_setprop(fdt, node, "reg", timas, sizeof(timas))); + + _FDT(fdt_setprop_string(fdt, node, "compatible", "ibm,power-ivpe")); + _FDT(fdt_setprop(fdt, node, "ibm,xive-eq-sizes", eq_sizes, + sizeof(eq_sizes))); + _FDT(fdt_setprop(fdt, node, "ibm,xive-lisn-ranges", lisn_ranges, + sizeof(lisn_ranges))); + + /* For Linux to link the LSIs to the interrupt controller. */ + _FDT(fdt_setprop(fdt, node, "interrupt-controller", NULL, 0)); + _FDT(fdt_setprop_cell(fdt, node, "#interrupt-cells", 2)); + + /* For SLOF */ + _FDT(fdt_setprop_cell(fdt, node, "linux,phandle", phandle)); + _FDT(fdt_setprop_cell(fdt, node, "phandle", phandle)); + + /* + * The "ibm,plat-res-int-priorities" property defines the priority + * ranges reserved by the hypervisor + */ + _FDT(fdt_setprop(fdt, 0, "ibm,plat-res-int-priorities", + plat_res_int_priorities, sizeof(plat_res_int_priorities))); +} diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 5fb18e65c9..a28bdce925 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -1,13 +1,13 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/intc/i8259.c +# i8259.c pic_update_irq(bool master, uint8_t imr, uint8_t irr, uint8_t padd) "master %d imr %"PRIu8" irr %"PRIu8" padd %"PRIu8 pic_set_irq(bool master, int irq, int level) "master %d irq %d level %d" pic_interrupt(int irq, int intno) "irq %d intno %d" pic_ioport_write(bool master, uint64_t addr, uint64_t val) "master %d addr 0x%"PRIx64" val 0x%"PRIx64 pic_ioport_read(bool master, uint64_t addr, int val) "master %d addr 0x%"PRIx64" val 0x%x" -# hw/intc/apic_common.c +# apic_common.c cpu_set_apic_base(uint64_t val) "0x%016"PRIx64 cpu_get_apic_base(uint64_t val) "0x%016"PRIx64 # coalescing @@ -15,13 +15,13 @@ apic_report_irq_delivered(int apic_irq_delivered) "coalescing %d" apic_reset_irq_delivered(int apic_irq_delivered) "old coalescing %d" apic_get_irq_delivered(int apic_irq_delivered) "returning coalescing %d" -# hw/intc/apic.c +# apic.c apic_local_deliver(int vector, uint32_t lvt) "vector %d delivery mode %d" apic_deliver_irq(uint8_t dest, uint8_t dest_mode, uint8_t delivery_mode, uint8_t vector_num, uint8_t trigger_mode) "dest %d dest_mode %d delivery_mode %d vector %d trigger_mode %d" apic_mem_readl(uint64_t addr, uint32_t val) "0x%"PRIx64" = 0x%08x" apic_mem_writel(uint64_t addr, uint32_t val) "0x%"PRIx64" = 0x%08x" -# hw/intc/ioapic.c +# ioapic.c ioapic_set_remote_irr(int n) "set remote irr for pin %d" ioapic_clear_remote_irr(int n, int vector) "clear remote irr for pin %d vector %d" ioapic_eoi_broadcast(int vector) "EOI broadcast for vector %d" @@ -29,7 +29,7 @@ ioapic_mem_read(uint8_t addr, uint8_t regsel, uint8_t size, uint32_t val) "ioapi ioapic_mem_write(uint8_t addr, uint8_t regsel, uint8_t size, uint32_t val) "ioapic mem write addr 0x%"PRIx8" regsel: 0x%"PRIx8" size 0x%"PRIx8" val 0x%"PRIx32 ioapic_set_irq(int vector, int level) "vector: %d level: %d" -# hw/intc/slavio_intctl.c +# slavio_intctl.c slavio_intctl_mem_readl(uint32_t cpu, uint64_t addr, uint32_t ret) "read cpu %d reg 0x%"PRIx64" = 0x%x" slavio_intctl_mem_writel(uint32_t cpu, uint64_t addr, uint32_t val) "write cpu %d reg 0x%"PRIx64" = 0x%x" slavio_intctl_mem_writel_clear(uint32_t cpu, uint32_t val, uint32_t intreg_pending) "Cleared cpu %d irq mask 0x%x, curmask 0x%x" @@ -43,14 +43,14 @@ slavio_check_interrupts(uint32_t pending, uint32_t intregm_disabled) "pending 0x slavio_set_irq(uint32_t target_cpu, int irq, uint32_t pil, int level) "Set cpu %d irq %d -> pil %d level %d" slavio_set_timer_irq_cpu(int cpu, int level) "Set cpu %d local timer level %d" -# hw/intc/grlib_irqmp.c +# grlib_irqmp.c grlib_irqmp_check_irqs(uint32_t pend, uint32_t force, uint32_t mask, uint32_t lvl1, uint32_t lvl2) "pend:0x%04x force:0x%04x mask:0x%04x lvl1:0x%04x lvl0:0x%04x" grlib_irqmp_ack(int intno) "interrupt:%d" grlib_irqmp_set_irq(int irq) "Raise CPU IRQ %d" grlib_irqmp_readl_unknown(uint64_t addr) "addr 0x%"PRIx64 grlib_irqmp_writel_unknown(uint64_t addr, uint32_t value) "addr 0x%"PRIx64" value 0x%x" -# hw/intc/lm32_pic.c +# lm32_pic.c lm32_pic_raise_irq(void) "Raise CPU interrupt" lm32_pic_lower_irq(void) "Lower CPU interrupt" lm32_pic_interrupt(int irq, int level) "Set IRQ%d %d" @@ -59,7 +59,7 @@ lm32_pic_set_ip(uint32_t ip) "ip 0x%08x" lm32_pic_get_im(uint32_t im) "im 0x%08x" lm32_pic_get_ip(uint32_t ip) "ip 0x%08x" -# hw/intc/xics.c +# xics.c xics_icp_check_ipi(int server, uint8_t mfrr) "CPU %d can take IPI mfrr=0x%x" xics_icp_accept(uint32_t old_xirr, uint32_t new_xirr) "icp_accept: XIRR 0x%"PRIx32"->0x%"PRIx32 xics_icp_eoi(int server, uint32_t xirr, uint32_t new_xirr) "icp_eoi: server %d given XIRR 0x%"PRIx32" new XIRR 0x%"PRIx32 @@ -72,31 +72,39 @@ xics_ics_simple_write_xive(int nr, int srcno, int server, uint8_t priority) "ics xics_ics_simple_reject(int nr, int srcno) "reject irq 0x%x [src %d]" xics_ics_simple_eoi(int nr) "ics_eoi: irq 0x%x" -# hw/intc/s390_flic_kvm.c +# s390_flic_kvm.c flic_create_device(int err) "flic: create device failed %d" flic_no_device_api(int err) "flic: no Device Contral API support %d" flic_reset_failed(int err) "flic: reset failed %d" -# hw/intc/s390_flic.c +# s390_flic.c qemu_s390_airq_suppressed(uint8_t type, uint8_t isc) "flic: adapter I/O interrupt suppressed (type 0x%x isc 0x%x)" qemu_s390_suppress_airq(uint8_t isc, const char *from, const char *to) "flic: for isc 0x%x, suppress airq by modifying ais mode from %s to %s" -# hw/intc/aspeed_vic.c +# 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 -# hw/intc/arm_gic.c +# arm_gic.c gic_enable_irq(int irq) "irq %d enabled" gic_disable_irq(int irq) "irq %d disabled" gic_set_irq(int irq, int level, int cpumask, int target) "irq %d level %d cpumask 0x%x target 0x%x" -gic_update_bestirq(int cpu, int irq, int prio, int priority_mask, int running_priority) "cpu %d irq %d priority %d cpu priority mask %d cpu running priority %d" +gic_update_bestirq(const char *s, int cpu, int irq, int prio, int priority_mask, int running_priority) "%s %d irq %d priority %d cpu priority mask %d cpu running priority %d" gic_update_set_irq(int cpu, const char *name, int level) "cpu[%d]: %s = %d" -gic_acknowledge_irq(int cpu, int irq) "cpu %d acknowledged irq %d" +gic_acknowledge_irq(const char *s, int cpu, int irq) "%s %d acknowledged irq %d" +gic_cpu_write(const char *s, int cpu, int addr, uint32_t val) "%s %d iface write at 0x%08x 0x%08" PRIx32 +gic_cpu_read(const char *s, int cpu, int addr, uint32_t val) "%s %d iface read at 0x%08x: 0x%08" PRIx32 +gic_hyp_read(int addr, uint32_t val) "hyp read at 0x%08x: 0x%08" PRIx32 +gic_hyp_write(int addr, uint32_t val) "hyp write at 0x%08x: 0x%08" PRIx32 +gic_dist_read(int addr, unsigned int size, uint32_t val) "dist read at 0x%08x size %u: 0x%08" PRIx32 +gic_dist_write(int addr, unsigned int size, uint32_t val) "dist write at 0x%08x size %u: 0x%08" PRIx32 +gic_lr_entry(int cpu, int entry, uint32_t val) "cpu %d: new lr entry %d: 0x%08" PRIx32 +gic_update_maintenance_irq(int cpu, int val) "cpu %d: maintenance = %d" -# hw/intc/arm_gicv3_cpuif.c +# arm_gicv3_cpuif.c gicv3_icc_pmr_read(uint32_t cpu, uint64_t val) "GICv3 ICC_PMR read cpu 0x%x value 0x%" PRIx64 gicv3_icc_pmr_write(uint32_t cpu, uint64_t val) "GICv3 ICC_PMR write cpu 0x%x value 0x%" PRIx64 gicv3_icc_bpr_read(int grp, uint32_t cpu, uint64_t val) "GICv3 ICC_BPR%d read cpu 0x%x value 0x%" PRIx64 @@ -155,14 +163,14 @@ gicv3_icv_eoir_write(int grp, uint32_t cpu, uint64_t val) "GICv3 ICV_EOIR%d writ gicv3_cpuif_virt_update(uint32_t cpuid, int idx) "GICv3 CPU i/f 0x%x virt HPPI update LR index %d" gicv3_cpuif_virt_set_irqs(uint32_t cpuid, int fiqlevel, int irqlevel, int maintlevel) "GICv3 CPU i/f 0x%x virt HPPI update: setting FIQ %d IRQ %d maintenance-irq %d" -# hw/intc/arm_gicv3_dist.c +# arm_gicv3_dist.c gicv3_dist_read(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" gicv3_dist_badread(uint64_t offset, unsigned size, bool secure) "GICv3 distributor read: offset 0x%" PRIx64 " size %u secure %d: error" gicv3_dist_write(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" gicv3_dist_badwrite(uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 distributor write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d: error" gicv3_dist_set_irq(int irq, int level) "GICv3 distributor interrupt %d level changed to %d" -# hw/intc/arm_gicv3_redist.c +# arm_gicv3_redist.c gicv3_redist_read(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor 0x%x read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" gicv3_redist_badread(uint32_t cpu, uint64_t offset, unsigned size, bool secure) "GICv3 redistributor 0x%x read: offset 0x%" PRIx64 " size %u secure %d: error" gicv3_redist_write(uint32_t cpu, uint64_t offset, uint64_t data, unsigned size, bool secure) "GICv3 redistributor 0x%x write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u secure %d" @@ -170,7 +178,7 @@ gicv3_redist_badwrite(uint32_t cpu, uint64_t offset, uint64_t data, unsigned siz gicv3_redist_set_irq(uint32_t cpu, int irq, int level) "GICv3 redistributor 0x%x interrupt %d level changed to %d" gicv3_redist_send_sgi(uint32_t cpu, int irq) "GICv3 redistributor 0x%x pending SGI %d" -# hw/intc/armv7m_nvic.c +# armv7m_nvic.c nvic_recompute_state(int vectpending, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d vectpending_prio %d exception_prio %d" nvic_recompute_state_secure(int vectpending, bool vectpending_is_s_banked, int vectpending_prio, int exception_prio) "NVIC state recomputed: vectpending %d is_s_banked %d vectpending_prio %d exception_prio %d" nvic_set_prio(int irq, bool secure, uint8_t prio) "NVIC set irq %d secure-bank %d priority %d" @@ -179,15 +187,15 @@ nvic_escalate_prio(int irq, int irqprio, int runprio) "NVIC escalating irq %d to nvic_escalate_disabled(int irq) "NVIC escalating irq %d to HardFault: disabled" nvic_set_pending(int irq, bool secure, bool targets_secure, bool derived, int en, int prio) "NVIC set pending irq %d secure-bank %d targets_secure %d derived %d (enabled: %d priority %d)" nvic_clear_pending(int irq, bool secure, int en, int prio) "NVIC clear pending irq %d secure-bank %d (enabled: %d priority %d)" -nvic_set_pending_level(int irq) "NVIC set pending: irq %d higher prio than vectpending: setting irq line to 1" nvic_acknowledge_irq(int irq, int prio) "NVIC acknowledge IRQ: %d now active (prio %d)" nvic_get_pending_irq_info(int irq, bool secure) "NVIC next IRQ %d: targets_secure: %d" nvic_complete_irq(int irq, bool secure) "NVIC complete IRQ %d (secure %d)" nvic_set_irq_level(int irq, int level) "NVIC external irq %d level set to %d" +nvic_set_nmi_level(int level) "NVIC external NMI level set to %d" nvic_sysreg_read(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" nvic_sysreg_write(uint64_t addr, uint32_t value, unsigned size) "NVIC sysreg write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" -# hw/intc/heathrow_pic.c +# heathrow_pic.c heathrow_write(uint64_t addr, unsigned int n, uint64_t value) "0x%"PRIx64" %u: 0x%"PRIx64 heathrow_read(uint64_t addr, unsigned int n, uint64_t value) "0x%"PRIx64" %u: 0x%"PRIx64 heathrow_set_irq(int num, int level) "set_irq: num=0x%02x level=%d" diff --git a/hw/intc/xics.c b/hw/intc/xics.c index c90c893228..af7dc709ab 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -37,18 +37,18 @@ #include "qapi/visitor.h" #include "monitor/monitor.h" #include "hw/intc/intc.h" +#include "sysemu/kvm.h" void icp_pic_print_info(ICPState *icp, Monitor *mon) { - ICPStateClass *icpc = ICP_GET_CLASS(icp); int cpu_index = icp->cs ? icp->cs->cpu_index : -1; if (!icp->output) { return; } - if (icpc->synchronize_state) { - icpc->synchronize_state(icp); + if (kvm_irqchip_in_kernel()) { + icp_synchronize_state(icp); } monitor_printf(mon, "CPU %d XIRR=%08x (%p) PP=%02x MFRR=%02x\n", @@ -58,7 +58,6 @@ void icp_pic_print_info(ICPState *icp, Monitor *mon) void ics_pic_print_info(ICSState *ics, Monitor *mon) { - ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); uint32_t i; monitor_printf(mon, "ICS %4x..%4x %p\n", @@ -68,8 +67,8 @@ void ics_pic_print_info(ICSState *ics, Monitor *mon) return; } - if (icsc->synchronize_state) { - icsc->synchronize_state(ics); + if (kvm_irqchip_in_kernel()) { + ics_synchronize_state(ics); } for (i = 0; i < ics->nr_irqs; i++) { @@ -252,25 +251,23 @@ static void icp_irq(ICSState *ics, int server, int nr, uint8_t priority) } } -static int icp_dispatch_pre_save(void *opaque) +static int icp_pre_save(void *opaque) { ICPState *icp = opaque; - ICPStateClass *info = ICP_GET_CLASS(icp); - if (info->pre_save) { - info->pre_save(icp); + if (kvm_irqchip_in_kernel()) { + icp_get_kvm_state(icp); } return 0; } -static int icp_dispatch_post_load(void *opaque, int version_id) +static int icp_post_load(void *opaque, int version_id) { ICPState *icp = opaque; - ICPStateClass *info = ICP_GET_CLASS(icp); - if (info->post_load) { - return info->post_load(icp, version_id); + if (kvm_irqchip_in_kernel()) { + return icp_set_kvm_state(icp); } return 0; @@ -280,8 +277,8 @@ static const VMStateDescription vmstate_icp_server = { .name = "icp/server", .version_id = 1, .minimum_version_id = 1, - .pre_save = icp_dispatch_pre_save, - .post_load = icp_dispatch_post_load, + .pre_save = icp_pre_save, + .post_load = icp_post_load, .fields = (VMStateField[]) { /* Sanity check */ VMSTATE_UINT32(xirr, ICPState), @@ -291,7 +288,7 @@ static const VMStateDescription vmstate_icp_server = { }, }; -static void icp_reset(DeviceState *dev) +static void icp_reset_handler(void *dev) { ICPState *icp = ICP(dev); @@ -301,13 +298,10 @@ static void icp_reset(DeviceState *dev) /* Make all outputs are deasserted */ qemu_set_irq(icp->output, 0); -} -static void icp_reset_handler(void *dev) -{ - DeviceClass *dc = DEVICE_GET_CLASS(dev); - - dc->reset(dev); + if (kvm_irqchip_in_kernel()) { + icp_set_kvm_state(ICP(dev)); + } } static void icp_realize(DeviceState *dev, Error **errp) @@ -320,8 +314,9 @@ static void icp_realize(DeviceState *dev, Error **errp) obj = object_property_get_link(OBJECT(dev), ICP_PROP_XICS, &err); if (!obj) { - error_propagate(errp, err); - error_prepend(errp, "required link '" ICP_PROP_XICS "' not found: "); + error_propagate_prepend(errp, err, + "required link '" ICP_PROP_XICS + "' not found: "); return; } @@ -329,8 +324,9 @@ static void icp_realize(DeviceState *dev, Error **errp) obj = object_property_get_link(OBJECT(dev), ICP_PROP_CPU, &err); if (!obj) { - error_propagate(errp, err); - error_prepend(errp, "required link '" ICP_PROP_CPU "' not found: "); + error_propagate_prepend(errp, err, + "required link '" ICP_PROP_CPU + "' not found: "); return; } @@ -342,6 +338,9 @@ static void icp_realize(DeviceState *dev, Error **errp) case PPC_FLAGS_INPUT_POWER7: icp->output = env->irq_inputs[POWER7_INPUT_INT]; break; + case PPC_FLAGS_INPUT_POWER9: /* For SPAPR xics emulation */ + icp->output = env->irq_inputs[POWER9_INPUT_INT]; + break; case PPC_FLAGS_INPUT_970: icp->output = env->irq_inputs[PPC970_INPUT_INT]; @@ -352,6 +351,14 @@ static void icp_realize(DeviceState *dev, Error **errp) return; } + if (kvm_irqchip_in_kernel()) { + icp_kvm_realize(dev, &err); + if (err) { + error_propagate(errp, err); + return; + } + } + qemu_register_reset(icp_reset_handler, dev); vmstate_register(NULL, icp->cs->cpu_index, &vmstate_icp_server, icp); } @@ -370,7 +377,6 @@ static void icp_class_init(ObjectClass *klass, void *data) dc->realize = icp_realize; dc->unrealize = icp_unrealize; - dc->reset = icp_reset; } static const TypeInfo icp_info = { @@ -459,10 +465,15 @@ static void ics_simple_set_irq_lsi(ICSState *ics, int srcno, int val) ics_simple_resend_lsi(ics, srcno); } -static void ics_simple_set_irq(void *opaque, int srcno, int val) +void ics_simple_set_irq(void *opaque, int srcno, int val) { ICSState *ics = (ICSState *)opaque; + if (kvm_irqchip_in_kernel()) { + ics_kvm_set_irq(ics, srcno, val); + return; + } + if (ics->irqs[srcno].flags & XICS_FLAGS_IRQ_LSI) { ics_simple_set_irq_lsi(ics, srcno, val); } else { @@ -550,6 +561,10 @@ static void ics_simple_reset(DeviceState *dev) ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); icsc->parent_reset(dev); + + if (kvm_irqchip_in_kernel()) { + ics_set_kvm_state(ICS_BASE(dev)); + } } static void ics_simple_reset_handler(void *dev) @@ -569,8 +584,6 @@ static void ics_simple_realize(DeviceState *dev, Error **errp) return; } - ics->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); - qemu_register_reset(ics_simple_reset_handler, ics); } @@ -624,8 +637,9 @@ static void ics_base_realize(DeviceState *dev, Error **errp) obj = object_property_get_link(OBJECT(dev), ICS_PROP_XICS, &err); if (!obj) { - error_propagate(errp, err); - error_prepend(errp, "required link '" ICS_PROP_XICS "' not found: "); + error_propagate_prepend(errp, err, + "required link '" ICS_PROP_XICS + "' not found: "); return; } ics->xics = XICS_FABRIC(obj); @@ -644,25 +658,23 @@ static void ics_base_instance_init(Object *obj) ics->offset = XICS_IRQ_BASE; } -static int ics_base_dispatch_pre_save(void *opaque) +static int ics_base_pre_save(void *opaque) { ICSState *ics = opaque; - ICSStateClass *info = ICS_BASE_GET_CLASS(ics); - if (info->pre_save) { - info->pre_save(ics); + if (kvm_irqchip_in_kernel()) { + ics_get_kvm_state(ics); } return 0; } -static int ics_base_dispatch_post_load(void *opaque, int version_id) +static int ics_base_post_load(void *opaque, int version_id) { ICSState *ics = opaque; - ICSStateClass *info = ICS_BASE_GET_CLASS(ics); - if (info->post_load) { - return info->post_load(ics, version_id); + if (kvm_irqchip_in_kernel()) { + return ics_set_kvm_state(ics); } return 0; @@ -686,8 +698,8 @@ static const VMStateDescription vmstate_ics_base = { .name = "ics", .version_id = 1, .minimum_version_id = 1, - .pre_save = ics_base_dispatch_pre_save, - .post_load = ics_base_dispatch_post_load, + .pre_save = ics_base_pre_save, + .post_load = ics_base_post_load, .fields = (VMStateField[]) { /* Sanity check */ VMSTATE_UINT32_EQUAL(nr_irqs, ICSState, NULL), @@ -746,6 +758,10 @@ void ics_set_irq_type(ICSState *ics, int srcno, bool lsi) ics->irqs[srcno].flags |= lsi ? XICS_FLAGS_IRQ_LSI : XICS_FLAGS_IRQ_MSI; + + if (kvm_irqchip_in_kernel()) { + ics_set_kvm_state_one(ics, srcno); + } } static void xics_register_types(void) diff --git a/hw/intc/xics_kvm.c b/hw/intc/xics_kvm.c index 30c3769a20..78a252e6df 100644 --- a/hw/intc/xics_kvm.c +++ b/hw/intc/xics_kvm.c @@ -34,6 +34,7 @@ #include "sysemu/kvm.h" #include "hw/ppc/spapr.h" #include "hw/ppc/xics.h" +#include "hw/ppc/xics_spapr.h" #include "kvm_ppc.h" #include "qemu/config-file.h" #include "qemu/error-report.h" @@ -53,7 +54,7 @@ static QLIST_HEAD(, KVMEnabledICP) /* * ICP-KVM */ -static void icp_get_kvm_state(ICPState *icp) +void icp_get_kvm_state(ICPState *icp) { uint64_t state; int ret; @@ -82,14 +83,14 @@ static void do_icp_synchronize_state(CPUState *cpu, run_on_cpu_data arg) icp_get_kvm_state(arg.host_ptr); } -static void icp_synchronize_state(ICPState *icp) +void icp_synchronize_state(ICPState *icp) { if (icp->cs) { run_on_cpu(icp->cs, do_icp_synchronize_state, RUN_ON_CPU_HOST_PTR(icp)); } } -static int icp_set_kvm_state(ICPState *icp, int version_id) +int icp_set_kvm_state(ICPState *icp) { uint64_t state; int ret; @@ -114,20 +115,9 @@ static int icp_set_kvm_state(ICPState *icp, int version_id) return 0; } -static void icp_kvm_reset(DeviceState *dev) -{ - ICPStateClass *icpc = ICP_GET_CLASS(dev); - - icpc->parent_reset(dev); - - icp_set_kvm_state(ICP(dev), 1); -} - -static void icp_kvm_realize(DeviceState *dev, Error **errp) +void icp_kvm_realize(DeviceState *dev, Error **errp) { ICPState *icp = ICP(dev); - ICPStateClass *icpc = ICP_GET_CLASS(icp); - Error *local_err = NULL; CPUState *cs; KVMEnabledICP *enabled_icp; unsigned long vcpu_id; @@ -137,12 +127,6 @@ static void icp_kvm_realize(DeviceState *dev, Error **errp) abort(); } - icpc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - cs = icp->cs; vcpu_id = kvm_arch_vcpu_id(cs); @@ -168,47 +152,19 @@ static void icp_kvm_realize(DeviceState *dev, Error **errp) QLIST_INSERT_HEAD(&kvm_enabled_icps, enabled_icp, node); } -static void icp_kvm_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - ICPStateClass *icpc = ICP_CLASS(klass); - - device_class_set_parent_realize(dc, icp_kvm_realize, - &icpc->parent_realize); - device_class_set_parent_reset(dc, icp_kvm_reset, - &icpc->parent_reset); - - icpc->pre_save = icp_get_kvm_state; - icpc->post_load = icp_set_kvm_state; - icpc->synchronize_state = icp_synchronize_state; -} - -static const TypeInfo icp_kvm_info = { - .name = TYPE_KVM_ICP, - .parent = TYPE_ICP, - .instance_size = sizeof(ICPState), - .class_init = icp_kvm_class_init, - .class_size = sizeof(ICPStateClass), -}; - /* * ICS-KVM */ -static void ics_get_kvm_state(ICSState *ics) +void ics_get_kvm_state(ICSState *ics) { uint64_t state; int i; - Error *local_err = NULL; for (i = 0; i < ics->nr_irqs; i++) { ICSIRQState *irq = &ics->irqs[i]; kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, - i + ics->offset, &state, false, &local_err); - if (local_err) { - error_report_err(local_err); - exit(1); - } + i + ics->offset, &state, false, &error_fatal); irq->server = state & KVM_XICS_DESTINATION_MASK; irq->saved_priority = (state >> KVM_XICS_PRIORITY_SHIFT) @@ -252,50 +208,62 @@ static void ics_get_kvm_state(ICSState *ics) } } -static void ics_synchronize_state(ICSState *ics) +void ics_synchronize_state(ICSState *ics) { ics_get_kvm_state(ics); } -static int ics_set_kvm_state(ICSState *ics, int version_id) +int ics_set_kvm_state_one(ICSState *ics, int srcno) { uint64_t state; - int i; Error *local_err = NULL; + ICSIRQState *irq = &ics->irqs[srcno]; + int ret; + + state = irq->server; + state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) + << KVM_XICS_PRIORITY_SHIFT; + if (irq->priority != irq->saved_priority) { + assert(irq->priority == 0xff); + state |= KVM_XICS_MASKED; + } + + if (irq->flags & XICS_FLAGS_IRQ_LSI) { + state |= KVM_XICS_LEVEL_SENSITIVE; + if (irq->status & XICS_STATUS_ASSERTED) { + state |= KVM_XICS_PENDING; + } + } else { + if (irq->status & XICS_STATUS_MASKED_PENDING) { + state |= KVM_XICS_PENDING; + } + } + if (irq->status & XICS_STATUS_PRESENTED) { + state |= KVM_XICS_PRESENTED; + } + if (irq->status & XICS_STATUS_QUEUED) { + state |= KVM_XICS_QUEUED; + } + + ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, + srcno + ics->offset, &state, true, &local_err); + if (local_err) { + error_report_err(local_err); + return ret; + } + + return 0; +} + +int ics_set_kvm_state(ICSState *ics) +{ + int i; for (i = 0; i < ics->nr_irqs; i++) { - ICSIRQState *irq = &ics->irqs[i]; int ret; - state = irq->server; - state |= (uint64_t)(irq->saved_priority & KVM_XICS_PRIORITY_MASK) - << KVM_XICS_PRIORITY_SHIFT; - if (irq->priority != irq->saved_priority) { - assert(irq->priority == 0xff); - state |= KVM_XICS_MASKED; - } - - if (ics->irqs[i].flags & XICS_FLAGS_IRQ_LSI) { - state |= KVM_XICS_LEVEL_SENSITIVE; - if (irq->status & XICS_STATUS_ASSERTED) { - state |= KVM_XICS_PENDING; - } - } else { - if (irq->status & XICS_STATUS_MASKED_PENDING) { - state |= KVM_XICS_PENDING; - } - } - if (irq->status & XICS_STATUS_PRESENTED) { - state |= KVM_XICS_PRESENTED; - } - if (irq->status & XICS_STATUS_QUEUED) { - state |= KVM_XICS_QUEUED; - } - - ret = kvm_device_access(kernel_xics_fd, KVM_DEV_XICS_GRP_SOURCES, - i + ics->offset, &state, true, &local_err); - if (local_err) { - error_report_err(local_err); + ret = ics_set_kvm_state_one(ics, i); + if (ret) { return ret; } } @@ -303,9 +271,8 @@ static int ics_set_kvm_state(ICSState *ics, int version_id) return 0; } -static void ics_kvm_set_irq(void *opaque, int srcno, int val) +void ics_kvm_set_irq(ICSState *ics, int srcno, int val) { - ICSState *ics = opaque; struct kvm_irq_level args; int rc; @@ -324,63 +291,7 @@ static void ics_kvm_set_irq(void *opaque, int srcno, int val) } } -static void ics_kvm_reset(DeviceState *dev) -{ - ICSStateClass *icsc = ICS_BASE_GET_CLASS(dev); - - icsc->parent_reset(dev); - - ics_set_kvm_state(ICS_KVM(dev), 1); -} - -static void ics_kvm_reset_handler(void *dev) -{ - ics_kvm_reset(dev); -} - -static void ics_kvm_realize(DeviceState *dev, Error **errp) -{ - ICSState *ics = ICS_KVM(dev); - ICSStateClass *icsc = ICS_BASE_GET_CLASS(ics); - Error *local_err = NULL; - - icsc->parent_realize(dev, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - ics->qirqs = qemu_allocate_irqs(ics_kvm_set_irq, ics, ics->nr_irqs); - - qemu_register_reset(ics_kvm_reset_handler, ics); -} - -static void ics_kvm_class_init(ObjectClass *klass, void *data) -{ - ICSStateClass *icsc = ICS_BASE_CLASS(klass); - DeviceClass *dc = DEVICE_CLASS(klass); - - device_class_set_parent_realize(dc, ics_kvm_realize, - &icsc->parent_realize); - device_class_set_parent_reset(dc, ics_kvm_reset, - &icsc->parent_reset); - - icsc->pre_save = ics_get_kvm_state; - icsc->post_load = ics_set_kvm_state; - icsc->synchronize_state = ics_synchronize_state; -} - -static const TypeInfo ics_kvm_info = { - .name = TYPE_ICS_KVM, - .parent = TYPE_ICS_BASE, - .instance_size = sizeof(ICSState), - .class_init = ics_kvm_class_init, -}; - -/* - * XICS-KVM - */ - -static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_dummy(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -389,7 +300,7 @@ static void rtas_dummy(PowerPCCPU *cpu, sPAPRMachineState *spapr, __func__); } -int xics_kvm_init(sPAPRMachineState *spapr, Error **errp) +int xics_kvm_init(SpaprMachineState *spapr, Error **errp) { int rc; @@ -449,11 +360,3 @@ fail: kvmppc_define_rtas_kernel_token(0, "ibm,int-off"); return -1; } - -static void xics_kvm_register_types(void) -{ - type_register_static(&ics_kvm_info); - type_register_static(&icp_kvm_info); -} - -type_init(xics_kvm_register_types) diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 2e27b92b87..9d2b8adef7 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -31,7 +31,9 @@ #include "trace.h" #include "qemu/timer.h" #include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/xics.h" +#include "hw/ppc/xics_spapr.h" #include "hw/ppc/fdt.h" #include "qapi/visitor.h" @@ -39,16 +41,16 @@ * Guest interfaces */ -static target_ulong h_cppr(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_cppr(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong cppr = args[0]; - icp_set_cppr(ICP(cpu->intc), cppr); + icp_set_cppr(spapr_cpu_state(cpu)->icp, cppr); return H_SUCCESS; } -static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_ipi(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong mfrr = args[1]; @@ -62,39 +64,46 @@ static target_ulong h_ipi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_xirr(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_xirr(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { - uint32_t xirr = icp_accept(ICP(cpu->intc)); + uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp); args[0] = xirr; return H_SUCCESS; } -static target_ulong h_xirr_x(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_xirr_x(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { - uint32_t xirr = icp_accept(ICP(cpu->intc)); + uint32_t xirr = icp_accept(spapr_cpu_state(cpu)->icp); args[0] = xirr; args[1] = cpu_get_host_ticks(); return H_SUCCESS; } -static target_ulong h_eoi(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_eoi(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong xirr = args[0]; - icp_eoi(ICP(cpu->intc), xirr); + icp_eoi(spapr_cpu_state(cpu)->icp, xirr); return H_SUCCESS; } -static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_ipoll(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { + ICPState *icp = xics_icp_get(XICS_FABRIC(spapr), args[0]); uint32_t mfrr; - uint32_t xirr = icp_ipoll(ICP(cpu->intc), &mfrr); + uint32_t xirr; + + if (!icp) { + return H_PARAMETER; + } + + xirr = icp_ipoll(icp, &mfrr); args[0] = xirr; args[1] = mfrr; @@ -102,7 +111,7 @@ static target_ulong h_ipoll(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_set_xive(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -135,7 +144,7 @@ static void rtas_set_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_get_xive(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -165,7 +174,7 @@ static void rtas_get_xive(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 2, ics->irqs[srcno].priority); } -static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_int_off(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -196,7 +205,7 @@ static void rtas_int_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_int_on(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -228,7 +237,7 @@ static void rtas_int_on(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -void xics_spapr_init(sPAPRMachineState *spapr) +void xics_spapr_init(SpaprMachineState *spapr) { /* Registration of global state belongs into realize */ spapr_rtas_register(RTAS_IBM_SET_XIVE, "ibm,set-xive", rtas_set_xive); @@ -244,14 +253,15 @@ void xics_spapr_init(sPAPRMachineState *spapr) spapr_register_hypercall(H_IPOLL, h_ipoll); } -void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle) +void spapr_dt_xics(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt, + uint32_t phandle) { uint32_t interrupt_server_ranges_prop[] = { 0, cpu_to_be32(nr_servers), }; int node; - _FDT(node = fdt_add_subnode(fdt, 0, "interrupt-controller")); + _FDT(node = fdt_add_subnode(fdt, 0, XICS_NODENAME)); _FDT(fdt_setprop_string(fdt, node, "device_type", "PowerPC-External-Interrupt-Presentation")); diff --git a/hw/intc/xive.c b/hw/intc/xive.c new file mode 100644 index 0000000000..a0b87001da --- /dev/null +++ b/hw/intc/xive.c @@ -0,0 +1,1693 @@ +/* + * QEMU PowerPC XIVE interrupt controller model + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * 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 "qemu/log.h" +#include "qapi/error.h" +#include "target/ppc/cpu.h" +#include "sysemu/cpus.h" +#include "sysemu/dma.h" +#include "hw/qdev-properties.h" +#include "monitor/monitor.h" +#include "hw/ppc/xive.h" +#include "hw/ppc/xive_regs.h" + +/* + * XIVE Thread Interrupt Management context + */ + +/* + * Convert a priority number to an Interrupt Pending Buffer (IPB) + * register, which indicates a pending interrupt at the priority + * corresponding to the bit number + */ +static uint8_t priority_to_ipb(uint8_t priority) +{ + return priority > XIVE_PRIORITY_MAX ? + 0 : 1 << (XIVE_PRIORITY_MAX - priority); +} + +/* + * Convert an Interrupt Pending Buffer (IPB) register to a Pending + * Interrupt Priority Register (PIPR), which contains the priority of + * the most favored pending notification. + */ +static uint8_t ipb_to_pipr(uint8_t ibp) +{ + return ibp ? clz32((uint32_t)ibp << 24) : 0xff; +} + +static void ipb_update(uint8_t *regs, uint8_t priority) +{ + regs[TM_IPB] |= priority_to_ipb(priority); + regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); +} + +static uint8_t exception_mask(uint8_t ring) +{ + switch (ring) { + case TM_QW1_OS: + return TM_QW1_NSR_EO; + case TM_QW3_HV_PHYS: + return TM_QW3_NSR_HE; + default: + g_assert_not_reached(); + } +} + +static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) +{ + uint8_t *regs = &tctx->regs[ring]; + uint8_t nsr = regs[TM_NSR]; + uint8_t mask = exception_mask(ring); + + qemu_irq_lower(tctx->output); + + if (regs[TM_NSR] & mask) { + uint8_t cppr = regs[TM_PIPR]; + + regs[TM_CPPR] = cppr; + + /* Reset the pending buffer bit */ + regs[TM_IPB] &= ~priority_to_ipb(cppr); + regs[TM_PIPR] = ipb_to_pipr(regs[TM_IPB]); + + /* Drop Exception bit */ + regs[TM_NSR] &= ~mask; + } + + return (nsr << 8) | regs[TM_CPPR]; +} + +static void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring) +{ + uint8_t *regs = &tctx->regs[ring]; + + if (regs[TM_PIPR] < regs[TM_CPPR]) { + switch (ring) { + case TM_QW1_OS: + regs[TM_NSR] |= TM_QW1_NSR_EO; + break; + case TM_QW3_HV_PHYS: + regs[TM_NSR] |= (TM_QW3_NSR_HE_PHYS << 6); + break; + default: + g_assert_not_reached(); + } + qemu_irq_raise(tctx->output); + } +} + +static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) +{ + if (cppr > XIVE_PRIORITY_MAX) { + cppr = 0xff; + } + + tctx->regs[ring + TM_CPPR] = cppr; + + /* CPPR has changed, check if we need to raise a pending exception */ + xive_tctx_notify(tctx, ring); +} + +/* + * XIVE Thread Interrupt Management Area (TIMA) + */ + +static void xive_tm_set_hv_cppr(XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size) +{ + xive_tctx_set_cppr(tctx, TM_QW3_HV_PHYS, value & 0xff); +} + +static uint64_t xive_tm_ack_hv_reg(XiveTCTX *tctx, hwaddr offset, unsigned size) +{ + return xive_tctx_accept(tctx, TM_QW3_HV_PHYS); +} + +static uint64_t xive_tm_pull_pool_ctx(XiveTCTX *tctx, hwaddr offset, + unsigned size) +{ + uint64_t ret; + + ret = tctx->regs[TM_QW2_HV_POOL + TM_WORD2] & TM_QW2W2_POOL_CAM; + tctx->regs[TM_QW2_HV_POOL + TM_WORD2] &= ~TM_QW2W2_POOL_CAM; + return ret; +} + +static void xive_tm_vt_push(XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size) +{ + tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = value & 0xff; +} + +static uint64_t xive_tm_vt_poll(XiveTCTX *tctx, hwaddr offset, unsigned size) +{ + return tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] & 0xff; +} + +/* + * Define an access map for each page of the TIMA that we will use in + * the memory region ops to filter values when doing loads and stores + * of raw registers values + * + * Registers accessibility bits : + * + * 0x0 - no access + * 0x1 - write only + * 0x2 - read only + * 0x3 - read/write + */ + +static const uint8_t xive_tm_hw_view[] = { + /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, + /* QW-1 OS */ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, + /* QW-2 POOL */ 0, 0, 3, 3, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, + /* QW-3 PHYS */ 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, +}; + +static const uint8_t xive_tm_hv_view[] = { + /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, + /* QW-1 OS */ 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 0, 0, 0, + /* QW-2 POOL */ 0, 0, 3, 3, 0, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, + /* QW-3 PHYS */ 3, 3, 3, 3, 0, 3, 0, 3, 3, 0, 0, 3, 0, 0, 0, 0, +}; + +static const uint8_t xive_tm_os_view[] = { + /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, + /* QW-1 OS */ 2, 3, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, + /* QW-2 POOL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* QW-3 PHYS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +static const uint8_t xive_tm_user_view[] = { + /* QW-0 User */ 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* QW-1 OS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* QW-2 POOL */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + /* QW-3 PHYS */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +/* + * Overall TIMA access map for the thread interrupt management context + * registers + */ +static const uint8_t *xive_tm_views[] = { + [XIVE_TM_HW_PAGE] = xive_tm_hw_view, + [XIVE_TM_HV_PAGE] = xive_tm_hv_view, + [XIVE_TM_OS_PAGE] = xive_tm_os_view, + [XIVE_TM_USER_PAGE] = xive_tm_user_view, +}; + +/* + * Computes a register access mask for a given offset in the TIMA + */ +static uint64_t xive_tm_mask(hwaddr offset, unsigned size, bool write) +{ + uint8_t page_offset = (offset >> TM_SHIFT) & 0x3; + uint8_t reg_offset = offset & 0x3F; + uint8_t reg_mask = write ? 0x1 : 0x2; + uint64_t mask = 0x0; + int i; + + for (i = 0; i < size; i++) { + if (xive_tm_views[page_offset][reg_offset + i] & reg_mask) { + mask |= (uint64_t) 0xff << (8 * (size - i - 1)); + } + } + + return mask; +} + +static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, + unsigned size) +{ + uint8_t ring_offset = offset & 0x30; + uint8_t reg_offset = offset & 0x3F; + uint64_t mask = xive_tm_mask(offset, size, true); + int i; + + /* + * Only 4 or 8 bytes stores are allowed and the User ring is + * excluded + */ + if (size < 4 || !mask || ring_offset == TM_QW0_USER) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA @%" + HWADDR_PRIx"\n", offset); + return; + } + + /* + * Use the register offset for the raw values and filter out + * reserved values + */ + for (i = 0; i < size; i++) { + uint8_t byte_mask = (mask >> (8 * (size - i - 1))); + if (byte_mask) { + tctx->regs[reg_offset + i] = (value >> (8 * (size - i - 1))) & + byte_mask; + } + } +} + +static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size) +{ + uint8_t ring_offset = offset & 0x30; + uint8_t reg_offset = offset & 0x3F; + uint64_t mask = xive_tm_mask(offset, size, false); + uint64_t ret; + int i; + + /* + * Only 4 or 8 bytes loads are allowed and the User ring is + * excluded + */ + if (size < 4 || !mask || ring_offset == TM_QW0_USER) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access at TIMA @%" + HWADDR_PRIx"\n", offset); + return -1; + } + + /* Use the register offset for the raw values */ + ret = 0; + for (i = 0; i < size; i++) { + ret |= (uint64_t) tctx->regs[reg_offset + i] << (8 * (size - i - 1)); + } + + /* filter out reserved values */ + return ret & mask; +} + +/* + * The TM context is mapped twice within each page. Stores and loads + * to the first mapping below 2K write and read the specified values + * without modification. The second mapping above 2K performs specific + * state changes (side effects) in addition to setting/returning the + * interrupt management area context of the processor thread. + */ +static uint64_t xive_tm_ack_os_reg(XiveTCTX *tctx, hwaddr offset, unsigned size) +{ + return xive_tctx_accept(tctx, TM_QW1_OS); +} + +static void xive_tm_set_os_cppr(XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size) +{ + xive_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); +} + +/* + * Adjust the IPB to allow a CPU to process event queues of other + * priorities during one physical interrupt cycle. + */ +static void xive_tm_set_os_pending(XiveTCTX *tctx, hwaddr offset, + uint64_t value, unsigned size) +{ + ipb_update(&tctx->regs[TM_QW1_OS], value & 0xff); + xive_tctx_notify(tctx, TM_QW1_OS); +} + +/* + * Define a mapping of "special" operations depending on the TIMA page + * offset and the size of the operation. + */ +typedef struct XiveTmOp { + uint8_t page_offset; + uint32_t op_offset; + unsigned size; + void (*write_handler)(XiveTCTX *tctx, hwaddr offset, uint64_t value, + unsigned size); + uint64_t (*read_handler)(XiveTCTX *tctx, hwaddr offset, unsigned size); +} XiveTmOp; + +static const XiveTmOp xive_tm_operations[] = { + /* + * MMIOs below 2K : raw values and special operations without side + * effects + */ + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, xive_tm_vt_poll }, + + /* MMIOs above 2K : special operations with side effects */ + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, xive_tm_ack_os_reg }, + { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, xive_tm_ack_hv_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, xive_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, xive_tm_pull_pool_ctx }, +}; + +static const XiveTmOp *xive_tm_find_op(hwaddr offset, unsigned size, bool write) +{ + uint8_t page_offset = (offset >> TM_SHIFT) & 0x3; + uint32_t op_offset = offset & 0xFFF; + int i; + + for (i = 0; i < ARRAY_SIZE(xive_tm_operations); i++) { + const XiveTmOp *xto = &xive_tm_operations[i]; + + /* Accesses done from a more privileged TIMA page is allowed */ + if (xto->page_offset >= page_offset && + xto->op_offset == op_offset && + xto->size == size && + ((write && xto->write_handler) || (!write && xto->read_handler))) { + return xto; + } + } + return NULL; +} + +/* + * TIMA MMIO handlers + */ +void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, + unsigned size) +{ + const XiveTmOp *xto; + + /* + * TODO: check V bit in Q[0-3]W2 + */ + + /* + * First, check for special operations in the 2K region + */ + if (offset & 0x800) { + xto = xive_tm_find_op(offset, size, true); + if (!xto) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA" + "@%"HWADDR_PRIx"\n", offset); + } else { + xto->write_handler(tctx, offset, value, size); + } + return; + } + + /* + * Then, for special operations in the region below 2K. + */ + xto = xive_tm_find_op(offset, size, true); + if (xto) { + xto->write_handler(tctx, offset, value, size); + return; + } + + /* + * Finish with raw access to the register values + */ + xive_tm_raw_write(tctx, offset, value, size); +} + +uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size) +{ + const XiveTmOp *xto; + + /* + * TODO: check V bit in Q[0-3]W2 + */ + + /* + * First, check for special operations in the 2K region + */ + if (offset & 0x800) { + xto = xive_tm_find_op(offset, size, false); + if (!xto) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" + "@%"HWADDR_PRIx"\n", offset); + return -1; + } + return xto->read_handler(tctx, offset, size); + } + + /* + * Then, for special operations in the region below 2K. + */ + xto = xive_tm_find_op(offset, size, false); + if (xto) { + return xto->read_handler(tctx, offset, size); + } + + /* + * Finish with raw access to the register values + */ + return xive_tm_raw_read(tctx, offset, size); +} + +static void xive_tm_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + XiveTCTX *tctx = xive_router_get_tctx(XIVE_ROUTER(opaque), current_cpu); + + xive_tctx_tm_write(tctx, offset, value, size); +} + +static uint64_t xive_tm_read(void *opaque, hwaddr offset, unsigned size) +{ + XiveTCTX *tctx = xive_router_get_tctx(XIVE_ROUTER(opaque), current_cpu); + + return xive_tctx_tm_read(tctx, offset, size); +} + +const MemoryRegionOps xive_tm_ops = { + .read = xive_tm_read, + .write = xive_tm_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static inline uint32_t xive_tctx_word2(uint8_t *ring) +{ + return *((uint32_t *) &ring[TM_WORD2]); +} + +static char *xive_tctx_ring_print(uint8_t *ring) +{ + uint32_t w2 = xive_tctx_word2(ring); + + return g_strdup_printf("%02x %02x %02x %02x %02x " + "%02x %02x %02x %08x", + ring[TM_NSR], ring[TM_CPPR], ring[TM_IPB], ring[TM_LSMFB], + ring[TM_ACK_CNT], ring[TM_INC], ring[TM_AGE], ring[TM_PIPR], + be32_to_cpu(w2)); +} + +static const char * const xive_tctx_ring_names[] = { + "USER", "OS", "POOL", "PHYS", +}; + +void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon) +{ + int cpu_index = tctx->cs ? tctx->cs->cpu_index : -1; + int i; + + monitor_printf(mon, "CPU[%04x]: QW NSR CPPR IPB LSMFB ACK# INC AGE PIPR" + " W2\n", cpu_index); + + for (i = 0; i < XIVE_TM_RING_COUNT; i++) { + char *s = xive_tctx_ring_print(&tctx->regs[i * XIVE_TM_RING_SIZE]); + monitor_printf(mon, "CPU[%04x]: %4s %s\n", cpu_index, + xive_tctx_ring_names[i], s); + g_free(s); + } +} + +static void xive_tctx_reset(void *dev) +{ + XiveTCTX *tctx = XIVE_TCTX(dev); + + memset(tctx->regs, 0, sizeof(tctx->regs)); + + /* Set some defaults */ + tctx->regs[TM_QW1_OS + TM_LSMFB] = 0xFF; + tctx->regs[TM_QW1_OS + TM_ACK_CNT] = 0xFF; + tctx->regs[TM_QW1_OS + TM_AGE] = 0xFF; + + /* + * Initialize PIPR to 0xFF to avoid phantom interrupts when the + * CPPR is first set. + */ + tctx->regs[TM_QW1_OS + TM_PIPR] = + ipb_to_pipr(tctx->regs[TM_QW1_OS + TM_IPB]); + tctx->regs[TM_QW3_HV_PHYS + TM_PIPR] = + ipb_to_pipr(tctx->regs[TM_QW3_HV_PHYS + TM_IPB]); +} + +static void xive_tctx_realize(DeviceState *dev, Error **errp) +{ + XiveTCTX *tctx = XIVE_TCTX(dev); + PowerPCCPU *cpu; + CPUPPCState *env; + Object *obj; + Error *local_err = NULL; + + obj = object_property_get_link(OBJECT(dev), "cpu", &local_err); + if (!obj) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'cpu' not found: "); + return; + } + + cpu = POWERPC_CPU(obj); + tctx->cs = CPU(obj); + + env = &cpu->env; + switch (PPC_INPUT(env)) { + case PPC_FLAGS_INPUT_POWER9: + tctx->output = env->irq_inputs[POWER9_INPUT_INT]; + break; + + default: + error_setg(errp, "XIVE interrupt controller does not support " + "this CPU bus model"); + return; + } + + qemu_register_reset(xive_tctx_reset, dev); +} + +static void xive_tctx_unrealize(DeviceState *dev, Error **errp) +{ + qemu_unregister_reset(xive_tctx_reset, dev); +} + +static const VMStateDescription vmstate_xive_tctx = { + .name = TYPE_XIVE_TCTX, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BUFFER(regs, XiveTCTX), + VMSTATE_END_OF_LIST() + }, +}; + +static void xive_tctx_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "XIVE Interrupt Thread Context"; + dc->realize = xive_tctx_realize; + dc->unrealize = xive_tctx_unrealize; + dc->vmsd = &vmstate_xive_tctx; +} + +static const TypeInfo xive_tctx_info = { + .name = TYPE_XIVE_TCTX, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XiveTCTX), + .class_init = xive_tctx_class_init, +}; + +Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp) +{ + Error *local_err = NULL; + Object *obj; + + obj = object_new(TYPE_XIVE_TCTX); + object_property_add_child(cpu, TYPE_XIVE_TCTX, obj, &error_abort); + object_unref(obj); + object_property_add_const_link(obj, "cpu", cpu, &error_abort); + object_property_set_bool(obj, true, "realized", &local_err); + if (local_err) { + goto error; + } + + return obj; + +error: + object_unparent(obj); + error_propagate(errp, local_err); + return NULL; +} + +/* + * XIVE ESB helpers + */ + +static uint8_t xive_esb_set(uint8_t *pq, uint8_t value) +{ + uint8_t old_pq = *pq & 0x3; + + *pq &= ~0x3; + *pq |= value & 0x3; + + return old_pq; +} + +static bool xive_esb_trigger(uint8_t *pq) +{ + uint8_t old_pq = *pq & 0x3; + + switch (old_pq) { + case XIVE_ESB_RESET: + xive_esb_set(pq, XIVE_ESB_PENDING); + return true; + case XIVE_ESB_PENDING: + case XIVE_ESB_QUEUED: + xive_esb_set(pq, XIVE_ESB_QUEUED); + return false; + case XIVE_ESB_OFF: + xive_esb_set(pq, XIVE_ESB_OFF); + return false; + default: + g_assert_not_reached(); + } +} + +static bool xive_esb_eoi(uint8_t *pq) +{ + uint8_t old_pq = *pq & 0x3; + + switch (old_pq) { + case XIVE_ESB_RESET: + case XIVE_ESB_PENDING: + xive_esb_set(pq, XIVE_ESB_RESET); + return false; + case XIVE_ESB_QUEUED: + xive_esb_set(pq, XIVE_ESB_PENDING); + return true; + case XIVE_ESB_OFF: + xive_esb_set(pq, XIVE_ESB_OFF); + return false; + default: + g_assert_not_reached(); + } +} + +/* + * XIVE Interrupt Source (or IVSE) + */ + +uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno) +{ + assert(srcno < xsrc->nr_irqs); + + return xsrc->status[srcno] & 0x3; +} + +uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq) +{ + assert(srcno < xsrc->nr_irqs); + + return xive_esb_set(&xsrc->status[srcno], pq); +} + +/* + * Returns whether the event notification should be forwarded. + */ +static bool xive_source_lsi_trigger(XiveSource *xsrc, uint32_t srcno) +{ + uint8_t old_pq = xive_source_esb_get(xsrc, srcno); + + xsrc->status[srcno] |= XIVE_STATUS_ASSERTED; + + switch (old_pq) { + case XIVE_ESB_RESET: + xive_source_esb_set(xsrc, srcno, XIVE_ESB_PENDING); + return true; + default: + return false; + } +} + +/* + * Returns whether the event notification should be forwarded. + */ +static bool xive_source_esb_trigger(XiveSource *xsrc, uint32_t srcno) +{ + bool ret; + + assert(srcno < xsrc->nr_irqs); + + ret = xive_esb_trigger(&xsrc->status[srcno]); + + if (xive_source_irq_is_lsi(xsrc, srcno) && + xive_source_esb_get(xsrc, srcno) == XIVE_ESB_QUEUED) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: queued an event on LSI IRQ %d\n", srcno); + } + + return ret; +} + +/* + * Returns whether the event notification should be forwarded. + */ +static bool xive_source_esb_eoi(XiveSource *xsrc, uint32_t srcno) +{ + bool ret; + + assert(srcno < xsrc->nr_irqs); + + ret = xive_esb_eoi(&xsrc->status[srcno]); + + /* + * LSI sources do not set the Q bit but they can still be + * asserted, in which case we should forward a new event + * notification + */ + if (xive_source_irq_is_lsi(xsrc, srcno) && + xsrc->status[srcno] & XIVE_STATUS_ASSERTED) { + ret = xive_source_lsi_trigger(xsrc, srcno); + } + + return ret; +} + +/* + * Forward the source event notification to the Router + */ +static void xive_source_notify(XiveSource *xsrc, int srcno) +{ + XiveNotifierClass *xnc = XIVE_NOTIFIER_GET_CLASS(xsrc->xive); + + if (xnc->notify) { + xnc->notify(xsrc->xive, srcno); + } +} + +/* + * In a two pages ESB MMIO setting, even page is the trigger page, odd + * page is for management + */ +static inline bool addr_is_even(hwaddr addr, uint32_t shift) +{ + return !((addr >> shift) & 1); +} + +static inline bool xive_source_is_trigger_page(XiveSource *xsrc, hwaddr addr) +{ + return xive_source_esb_has_2page(xsrc) && + addr_is_even(addr, xsrc->esb_shift - 1); +} + +/* + * ESB MMIO loads + * Trigger page Management/EOI page + * + * ESB MMIO setting 2 pages 1 or 2 pages + * + * 0x000 .. 0x3FF -1 EOI and return 0|1 + * 0x400 .. 0x7FF -1 EOI and return 0|1 + * 0x800 .. 0xBFF -1 return PQ + * 0xC00 .. 0xCFF -1 return PQ and atomically PQ=00 + * 0xD00 .. 0xDFF -1 return PQ and atomically PQ=01 + * 0xE00 .. 0xDFF -1 return PQ and atomically PQ=10 + * 0xF00 .. 0xDFF -1 return PQ and atomically PQ=11 + */ +static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size) +{ + XiveSource *xsrc = XIVE_SOURCE(opaque); + uint32_t offset = addr & 0xFFF; + uint32_t srcno = addr >> xsrc->esb_shift; + uint64_t ret = -1; + + /* In a two pages ESB MMIO setting, trigger page should not be read */ + if (xive_source_is_trigger_page(xsrc, addr)) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid load on IRQ %d trigger page at " + "0x%"HWADDR_PRIx"\n", srcno, addr); + return -1; + } + + switch (offset) { + case XIVE_ESB_LOAD_EOI ... XIVE_ESB_LOAD_EOI + 0x7FF: + ret = xive_source_esb_eoi(xsrc, srcno); + + /* Forward the source event notification for routing */ + if (ret) { + xive_source_notify(xsrc, srcno); + } + break; + + case XIVE_ESB_GET ... XIVE_ESB_GET + 0x3FF: + ret = xive_source_esb_get(xsrc, srcno); + break; + + case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF: + case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF: + case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF: + case XIVE_ESB_SET_PQ_11 ... XIVE_ESB_SET_PQ_11 + 0x0FF: + ret = xive_source_esb_set(xsrc, srcno, (offset >> 8) & 0x3); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB load addr %x\n", + offset); + } + + return ret; +} + +/* + * ESB MMIO stores + * Trigger page Management/EOI page + * + * ESB MMIO setting 2 pages 1 or 2 pages + * + * 0x000 .. 0x3FF Trigger Trigger + * 0x400 .. 0x7FF Trigger EOI + * 0x800 .. 0xBFF Trigger undefined + * 0xC00 .. 0xCFF Trigger PQ=00 + * 0xD00 .. 0xDFF Trigger PQ=01 + * 0xE00 .. 0xDFF Trigger PQ=10 + * 0xF00 .. 0xDFF Trigger PQ=11 + */ +static void xive_source_esb_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + XiveSource *xsrc = XIVE_SOURCE(opaque); + uint32_t offset = addr & 0xFFF; + uint32_t srcno = addr >> xsrc->esb_shift; + bool notify = false; + + /* In a two pages ESB MMIO setting, trigger page only triggers */ + if (xive_source_is_trigger_page(xsrc, addr)) { + notify = xive_source_esb_trigger(xsrc, srcno); + goto out; + } + + switch (offset) { + case 0 ... 0x3FF: + notify = xive_source_esb_trigger(xsrc, srcno); + break; + + case XIVE_ESB_STORE_EOI ... XIVE_ESB_STORE_EOI + 0x3FF: + if (!(xsrc->esb_flags & XIVE_SRC_STORE_EOI)) { + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: invalid Store EOI for IRQ %d\n", srcno); + return; + } + + notify = xive_source_esb_eoi(xsrc, srcno); + break; + + case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF: + case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF: + case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF: + case XIVE_ESB_SET_PQ_11 ... XIVE_ESB_SET_PQ_11 + 0x0FF: + xive_source_esb_set(xsrc, srcno, (offset >> 8) & 0x3); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr %x\n", + offset); + return; + } + +out: + /* Forward the source event notification for routing */ + if (notify) { + xive_source_notify(xsrc, srcno); + } +} + +static const MemoryRegionOps xive_source_esb_ops = { + .read = xive_source_esb_read, + .write = xive_source_esb_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +void xive_source_set_irq(void *opaque, int srcno, int val) +{ + XiveSource *xsrc = XIVE_SOURCE(opaque); + bool notify = false; + + if (xive_source_irq_is_lsi(xsrc, srcno)) { + if (val) { + notify = xive_source_lsi_trigger(xsrc, srcno); + } else { + xsrc->status[srcno] &= ~XIVE_STATUS_ASSERTED; + } + } else { + if (val) { + notify = xive_source_esb_trigger(xsrc, srcno); + } + } + + /* Forward the source event notification for routing */ + if (notify) { + xive_source_notify(xsrc, srcno); + } +} + +void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, Monitor *mon) +{ + int i; + + for (i = 0; i < xsrc->nr_irqs; i++) { + uint8_t pq = xive_source_esb_get(xsrc, i); + + if (pq == XIVE_ESB_OFF) { + continue; + } + + monitor_printf(mon, " %08x %s %c%c%c\n", i + offset, + xive_source_irq_is_lsi(xsrc, i) ? "LSI" : "MSI", + pq & XIVE_ESB_VAL_P ? 'P' : '-', + pq & XIVE_ESB_VAL_Q ? 'Q' : '-', + xsrc->status[i] & XIVE_STATUS_ASSERTED ? 'A' : ' '); + } +} + +static void xive_source_reset(void *dev) +{ + XiveSource *xsrc = XIVE_SOURCE(dev); + + /* Do not clear the LSI bitmap */ + + /* PQs are initialized to 0b01 (Q=1) which corresponds to "ints off" */ + memset(xsrc->status, XIVE_ESB_OFF, xsrc->nr_irqs); +} + +static void xive_source_realize(DeviceState *dev, Error **errp) +{ + XiveSource *xsrc = XIVE_SOURCE(dev); + Object *obj; + Error *local_err = NULL; + + obj = object_property_get_link(OBJECT(dev), "xive", &local_err); + if (!obj) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'xive' not found: "); + return; + } + + xsrc->xive = XIVE_NOTIFIER(obj); + + if (!xsrc->nr_irqs) { + error_setg(errp, "Number of interrupt needs to be greater than 0"); + return; + } + + if (xsrc->esb_shift != XIVE_ESB_4K && + xsrc->esb_shift != XIVE_ESB_4K_2PAGE && + xsrc->esb_shift != XIVE_ESB_64K && + xsrc->esb_shift != XIVE_ESB_64K_2PAGE) { + error_setg(errp, "Invalid ESB shift setting"); + return; + } + + xsrc->status = g_malloc0(xsrc->nr_irqs); + xsrc->lsi_map = bitmap_new(xsrc->nr_irqs); + + memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc), + &xive_source_esb_ops, xsrc, "xive.esb", + (1ull << xsrc->esb_shift) * xsrc->nr_irqs); + + qemu_register_reset(xive_source_reset, dev); +} + +static const VMStateDescription vmstate_xive_source = { + .name = TYPE_XIVE_SOURCE, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_EQUAL(nr_irqs, XiveSource, NULL), + VMSTATE_VBUFFER_UINT32(status, XiveSource, 1, NULL, nr_irqs), + VMSTATE_END_OF_LIST() + }, +}; + +/* + * The default XIVE interrupt source setting for the ESB MMIOs is two + * 64k pages without Store EOI, to be in sync with KVM. + */ +static Property xive_source_properties[] = { + DEFINE_PROP_UINT64("flags", XiveSource, esb_flags, 0), + DEFINE_PROP_UINT32("nr-irqs", XiveSource, nr_irqs, 0), + DEFINE_PROP_UINT32("shift", XiveSource, esb_shift, XIVE_ESB_64K_2PAGE), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xive_source_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "XIVE Interrupt Source"; + dc->props = xive_source_properties; + dc->realize = xive_source_realize; + dc->vmsd = &vmstate_xive_source; +} + +static const TypeInfo xive_source_info = { + .name = TYPE_XIVE_SOURCE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XiveSource), + .class_init = xive_source_class_init, +}; + +/* + * XiveEND helpers + */ + +void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon) +{ + uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32 + | be32_to_cpu(end->w3); + uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0); + uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); + uint32_t qentries = 1 << (qsize + 10); + int i; + + /* + * print out the [ (qindex - (width - 1)) .. (qindex + 1)] window + */ + monitor_printf(mon, " [ "); + qindex = (qindex - (width - 1)) & (qentries - 1); + for (i = 0; i < width; i++) { + uint64_t qaddr = qaddr_base + (qindex << 2); + uint32_t qdata = -1; + + if (dma_memory_read(&address_space_memory, qaddr, &qdata, + sizeof(qdata))) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to read EQ @0x%" + HWADDR_PRIx "\n", qaddr); + return; + } + monitor_printf(mon, "%s%08x ", i == width - 1 ? "^" : "", + be32_to_cpu(qdata)); + qindex = (qindex + 1) & (qentries - 1); + } +} + +void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon) +{ + uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32 + | be32_to_cpu(end->w3); + uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); + uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1); + uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0); + uint32_t qentries = 1 << (qsize + 10); + + uint32_t nvt = xive_get_field32(END_W6_NVT_INDEX, end->w6); + uint8_t priority = xive_get_field32(END_W7_F0_PRIORITY, end->w7); + + if (!xive_end_is_valid(end)) { + return; + } + + monitor_printf(mon, " %08x %c%c%c%c%c prio:%d nvt:%04x eq:@%08"PRIx64 + "% 6d/%5d ^%d", end_idx, + xive_end_is_valid(end) ? 'v' : '-', + xive_end_is_enqueue(end) ? 'q' : '-', + xive_end_is_notify(end) ? 'n' : '-', + xive_end_is_backlog(end) ? 'b' : '-', + xive_end_is_escalate(end) ? 'e' : '-', + priority, nvt, qaddr_base, qindex, qentries, qgen); + + xive_end_queue_pic_print_info(end, 6, mon); + monitor_printf(mon, "]\n"); +} + +static void xive_end_enqueue(XiveEND *end, uint32_t data) +{ + uint64_t qaddr_base = (uint64_t) be32_to_cpu(end->w2 & 0x0fffffff) << 32 + | be32_to_cpu(end->w3); + uint32_t qsize = xive_get_field32(END_W0_QSIZE, end->w0); + uint32_t qindex = xive_get_field32(END_W1_PAGE_OFF, end->w1); + uint32_t qgen = xive_get_field32(END_W1_GENERATION, end->w1); + + uint64_t qaddr = qaddr_base + (qindex << 2); + uint32_t qdata = cpu_to_be32((qgen << 31) | (data & 0x7fffffff)); + uint32_t qentries = 1 << (qsize + 10); + + if (dma_memory_write(&address_space_memory, qaddr, &qdata, sizeof(qdata))) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: failed to write END data @0x%" + HWADDR_PRIx "\n", qaddr); + return; + } + + qindex = (qindex + 1) & (qentries - 1); + if (qindex == 0) { + qgen ^= 1; + end->w1 = xive_set_field32(END_W1_GENERATION, end->w1, qgen); + } + end->w1 = xive_set_field32(END_W1_PAGE_OFF, end->w1, qindex); +} + +/* + * XIVE Router (aka. Virtualization Controller or IVRE) + */ + +int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + XiveEAS *eas) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->get_eas(xrtr, eas_blk, eas_idx, eas); +} + +int xive_router_get_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, + XiveEND *end) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->get_end(xrtr, end_blk, end_idx, end); +} + +int xive_router_write_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, + XiveEND *end, uint8_t word_number) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->write_end(xrtr, end_blk, end_idx, end, word_number); +} + +int xive_router_get_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, + XiveNVT *nvt) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->get_nvt(xrtr, nvt_blk, nvt_idx, nvt); +} + +int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, + XiveNVT *nvt, uint8_t word_number) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->write_nvt(xrtr, nvt_blk, nvt_idx, nvt, word_number); +} + +XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs) +{ + XiveRouterClass *xrc = XIVE_ROUTER_GET_CLASS(xrtr); + + return xrc->get_tctx(xrtr, cs); +} + +/* + * By default on P9, the HW CAM line (23bits) is hardwired to : + * + * 0x000||0b1||4Bit chip number||7Bit Thread number. + * + * When the block grouping is enabled, the CAM line is changed to : + * + * 4Bit chip number||0x001||7Bit Thread number. + */ +static uint32_t hw_cam_line(uint8_t chip_id, uint8_t tid) +{ + return 1 << 11 | (chip_id & 0xf) << 7 | (tid & 0x7f); +} + +static bool xive_presenter_tctx_match_hw(XiveTCTX *tctx, + uint8_t nvt_blk, uint32_t nvt_idx) +{ + CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; + uint32_t pir = env->spr_cb[SPR_PIR].default_value; + + return hw_cam_line((pir >> 8) & 0xf, pir & 0x7f) == + hw_cam_line(nvt_blk, nvt_idx); +} + +/* + * The thread context register words are in big-endian format. + */ +static int xive_presenter_tctx_match(XiveTCTX *tctx, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint32_t logic_serv) +{ + uint32_t cam = xive_nvt_cam_line(nvt_blk, nvt_idx); + uint32_t qw3w2 = xive_tctx_word2(&tctx->regs[TM_QW3_HV_PHYS]); + uint32_t qw2w2 = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); + uint32_t qw1w2 = xive_tctx_word2(&tctx->regs[TM_QW1_OS]); + uint32_t qw0w2 = xive_tctx_word2(&tctx->regs[TM_QW0_USER]); + + /* + * TODO (PowerNV): ignore mode. The low order bits of the NVT + * identifier are ignored in the "CAM" match. + */ + + if (format == 0) { + if (cam_ignore == true) { + /* + * F=0 & i=1: Logical server notification (bits ignored at + * the end of the NVT identifier) + */ + qemu_log_mask(LOG_UNIMP, "XIVE: no support for LS NVT %x/%x\n", + nvt_blk, nvt_idx); + return -1; + } + + /* F=0 & i=0: Specific NVT notification */ + + /* PHYS ring */ + if ((be32_to_cpu(qw3w2) & TM_QW3W2_VT) && + xive_presenter_tctx_match_hw(tctx, nvt_blk, nvt_idx)) { + return TM_QW3_HV_PHYS; + } + + /* HV POOL ring */ + if ((be32_to_cpu(qw2w2) & TM_QW2W2_VP) && + cam == xive_get_field32(TM_QW2W2_POOL_CAM, qw2w2)) { + return TM_QW2_HV_POOL; + } + + /* OS ring */ + if ((be32_to_cpu(qw1w2) & TM_QW1W2_VO) && + cam == xive_get_field32(TM_QW1W2_OS_CAM, qw1w2)) { + return TM_QW1_OS; + } + } else { + /* F=1 : User level Event-Based Branch (EBB) notification */ + + /* USER ring */ + if ((be32_to_cpu(qw1w2) & TM_QW1W2_VO) && + (cam == xive_get_field32(TM_QW1W2_OS_CAM, qw1w2)) && + (be32_to_cpu(qw0w2) & TM_QW0W2_VU) && + (logic_serv == xive_get_field32(TM_QW0W2_LOGIC_SERV, qw0w2))) { + return TM_QW0_USER; + } + } + return -1; +} + +typedef struct XiveTCTXMatch { + XiveTCTX *tctx; + uint8_t ring; +} XiveTCTXMatch; + +static bool xive_presenter_match(XiveRouter *xrtr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) +{ + CPUState *cs; + + /* + * TODO (PowerNV): handle chip_id overwrite of block field for + * hardwired CAM compares + */ + + CPU_FOREACH(cs) { + XiveTCTX *tctx = xive_router_get_tctx(xrtr, cs); + int ring; + + /* + * HW checks that the CPU is enabled in the Physical Thread + * Enable Register (PTER). + */ + + /* + * Check the thread context CAM lines and record matches. We + * will handle CPU exception delivery later + */ + ring = xive_presenter_tctx_match(tctx, format, nvt_blk, nvt_idx, + cam_ignore, logic_serv); + /* + * Save the context and follow on to catch duplicates, that we + * don't support yet. + */ + if (ring != -1) { + if (match->tctx) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread " + "context NVT %x/%x\n", nvt_blk, nvt_idx); + return false; + } + + match->ring = ring; + match->tctx = tctx; + } + } + + if (!match->tctx) { + qemu_log_mask(LOG_UNIMP, "XIVE: NVT %x/%x is not dispatched\n", + nvt_blk, nvt_idx); + return false; + } + + return true; +} + +/* + * This is our simple Xive Presenter Engine model. It is merged in the + * Router as it does not require an extra object. + * + * It receives notification requests sent by the IVRE to find one + * matching NVT (or more) dispatched on the processor threads. In case + * of a single NVT notification, the process is abreviated and the + * thread is signaled if a match is found. In case of a logical server + * notification (bits ignored at the end of the NVT identifier), the + * IVPE and IVRE select a winning thread using different filters. This + * involves 2 or 3 exchanges on the PowerBus that the model does not + * support. + * + * The parameters represent what is sent on the PowerBus + */ +static void xive_presenter_notify(XiveRouter *xrtr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool cam_ignore, uint8_t priority, + uint32_t logic_serv) +{ + XiveNVT nvt; + XiveTCTXMatch match = { .tctx = NULL, .ring = 0 }; + bool found; + + /* NVT cache lookup */ + if (xive_router_get_nvt(xrtr, nvt_blk, nvt_idx, &nvt)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no NVT %x/%x\n", + nvt_blk, nvt_idx); + return; + } + + if (!xive_nvt_is_valid(&nvt)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is invalid\n", + nvt_blk, nvt_idx); + return; + } + + found = xive_presenter_match(xrtr, format, nvt_blk, nvt_idx, cam_ignore, + priority, logic_serv, &match); + if (found) { + ipb_update(&match.tctx->regs[match.ring], priority); + xive_tctx_notify(match.tctx, match.ring); + return; + } + + /* Record the IPB in the associated NVT structure */ + ipb_update((uint8_t *) &nvt.w4, priority); + xive_router_write_nvt(xrtr, nvt_blk, nvt_idx, &nvt, 4); + + /* + * If no matching NVT is dispatched on a HW thread : + * - update the NVT structure if backlog is activated + * - escalate (ESe PQ bits and EAS in w4-5) if escalation is + * activated + */ +} + +/* + * An END trigger can come from an event trigger (IPI or HW) or from + * another chip. We don't model the PowerBus but the END trigger + * message has the same parameters than in the function below. + */ +static void xive_router_end_notify(XiveRouter *xrtr, uint8_t end_blk, + uint32_t end_idx, uint32_t end_data) +{ + XiveEND end; + uint8_t priority; + uint8_t format; + + /* END cache lookup */ + if (xive_router_get_end(xrtr, end_blk, end_idx, &end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk, + end_idx); + return; + } + + if (!xive_end_is_valid(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x is invalid\n", + end_blk, end_idx); + return; + } + + if (xive_end_is_enqueue(&end)) { + xive_end_enqueue(&end, end_data); + /* Enqueuing event data modifies the EQ toggle and index */ + xive_router_write_end(xrtr, end_blk, end_idx, &end, 1); + } + + /* + * The W7 format depends on the F bit in W6. It defines the type + * of the notification : + * + * F=0 : single or multiple NVT notification + * F=1 : User level Event-Based Branch (EBB) notification, no + * priority + */ + format = xive_get_field32(END_W6_FORMAT_BIT, end.w6); + priority = xive_get_field32(END_W7_F0_PRIORITY, end.w7); + + /* The END is masked */ + if (format == 0 && priority == 0xff) { + return; + } + + /* + * Check the END ESn (Event State Buffer for notification) for + * even futher coalescing in the Router + */ + if (!xive_end_is_notify(&end)) { + uint8_t pq = xive_get_field32(END_W1_ESn, end.w1); + bool notify = xive_esb_trigger(&pq); + + if (pq != xive_get_field32(END_W1_ESn, end.w1)) { + end.w1 = xive_set_field32(END_W1_ESn, end.w1, pq); + xive_router_write_end(xrtr, end_blk, end_idx, &end, 1); + } + + /* ESn[Q]=1 : end of notification */ + if (!notify) { + return; + } + } + + /* + * Follows IVPE notification + */ + xive_presenter_notify(xrtr, format, + xive_get_field32(END_W6_NVT_BLOCK, end.w6), + xive_get_field32(END_W6_NVT_INDEX, end.w6), + xive_get_field32(END_W7_F0_IGNORE, end.w7), + priority, + xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7)); + + /* TODO: Auto EOI. */ +} + +void xive_router_notify(XiveNotifier *xn, uint32_t lisn) +{ + XiveRouter *xrtr = XIVE_ROUTER(xn); + uint8_t eas_blk = XIVE_SRCNO_BLOCK(lisn); + uint32_t eas_idx = XIVE_SRCNO_INDEX(lisn); + XiveEAS eas; + + /* EAS cache lookup */ + if (xive_router_get_eas(xrtr, eas_blk, eas_idx, &eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Unknown LISN %x\n", lisn); + return; + } + + /* + * The IVRE checks the State Bit Cache at this point. We skip the + * SBC lookup because the state bits of the sources are modeled + * internally in QEMU. + */ + + if (!xive_eas_is_valid(&eas)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid LISN %x\n", lisn); + return; + } + + if (xive_eas_is_masked(&eas)) { + /* Notification completed */ + return; + } + + /* + * The event trigger becomes an END trigger + */ + xive_router_end_notify(xrtr, + xive_get_field64(EAS_END_BLOCK, eas.w), + xive_get_field64(EAS_END_INDEX, eas.w), + xive_get_field64(EAS_END_DATA, eas.w)); +} + +static void xive_router_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); + + dc->desc = "XIVE Router Engine"; + xnc->notify = xive_router_notify; +} + +static const TypeInfo xive_router_info = { + .name = TYPE_XIVE_ROUTER, + .parent = TYPE_SYS_BUS_DEVICE, + .abstract = true, + .class_size = sizeof(XiveRouterClass), + .class_init = xive_router_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_NOTIFIER }, + { } + } +}; + +void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon) +{ + if (!xive_eas_is_valid(eas)) { + return; + } + + monitor_printf(mon, " %08x %s end:%02x/%04x data:%08x\n", + lisn, xive_eas_is_masked(eas) ? "M" : " ", + (uint8_t) xive_get_field64(EAS_END_BLOCK, eas->w), + (uint32_t) xive_get_field64(EAS_END_INDEX, eas->w), + (uint32_t) xive_get_field64(EAS_END_DATA, eas->w)); +} + +/* + * END ESB MMIO loads + */ +static uint64_t xive_end_source_read(void *opaque, hwaddr addr, unsigned size) +{ + XiveENDSource *xsrc = XIVE_END_SOURCE(opaque); + uint32_t offset = addr & 0xFFF; + uint8_t end_blk; + uint32_t end_idx; + XiveEND end; + uint32_t end_esmask; + uint8_t pq; + uint64_t ret = -1; + + end_blk = xsrc->block_id; + end_idx = addr >> (xsrc->esb_shift + 1); + + if (xive_router_get_end(xsrc->xrtr, end_blk, end_idx, &end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No END %x/%x\n", end_blk, + end_idx); + return -1; + } + + if (!xive_end_is_valid(&end)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: END %x/%x is invalid\n", + end_blk, end_idx); + return -1; + } + + end_esmask = addr_is_even(addr, xsrc->esb_shift) ? END_W1_ESn : END_W1_ESe; + pq = xive_get_field32(end_esmask, end.w1); + + switch (offset) { + case XIVE_ESB_LOAD_EOI ... XIVE_ESB_LOAD_EOI + 0x7FF: + ret = xive_esb_eoi(&pq); + + /* Forward the source event notification for routing ?? */ + break; + + case XIVE_ESB_GET ... XIVE_ESB_GET + 0x3FF: + ret = pq; + break; + + case XIVE_ESB_SET_PQ_00 ... XIVE_ESB_SET_PQ_00 + 0x0FF: + case XIVE_ESB_SET_PQ_01 ... XIVE_ESB_SET_PQ_01 + 0x0FF: + case XIVE_ESB_SET_PQ_10 ... XIVE_ESB_SET_PQ_10 + 0x0FF: + case XIVE_ESB_SET_PQ_11 ... XIVE_ESB_SET_PQ_11 + 0x0FF: + ret = xive_esb_set(&pq, (offset >> 8) & 0x3); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid END ESB load addr %d\n", + offset); + return -1; + } + + if (pq != xive_get_field32(end_esmask, end.w1)) { + end.w1 = xive_set_field32(end_esmask, end.w1, pq); + xive_router_write_end(xsrc->xrtr, end_blk, end_idx, &end, 1); + } + + return ret; +} + +/* + * END ESB MMIO stores are invalid + */ +static void xive_end_source_write(void *opaque, hwaddr addr, + uint64_t value, unsigned size) +{ + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid ESB write addr 0x%" + HWADDR_PRIx"\n", addr); +} + +static const MemoryRegionOps xive_end_source_ops = { + .read = xive_end_source_read, + .write = xive_end_source_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static void xive_end_source_realize(DeviceState *dev, Error **errp) +{ + XiveENDSource *xsrc = XIVE_END_SOURCE(dev); + Object *obj; + Error *local_err = NULL; + + obj = object_property_get_link(OBJECT(dev), "xive", &local_err); + if (!obj) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'xive' not found: "); + return; + } + + xsrc->xrtr = XIVE_ROUTER(obj); + + if (!xsrc->nr_ends) { + error_setg(errp, "Number of interrupt needs to be greater than 0"); + return; + } + + if (xsrc->esb_shift != XIVE_ESB_4K && + xsrc->esb_shift != XIVE_ESB_64K) { + error_setg(errp, "Invalid ESB shift setting"); + return; + } + + /* + * Each END is assigned an even/odd pair of MMIO pages, the even page + * manages the ESn field while the odd page manages the ESe field. + */ + memory_region_init_io(&xsrc->esb_mmio, OBJECT(xsrc), + &xive_end_source_ops, xsrc, "xive.end", + (1ull << (xsrc->esb_shift + 1)) * xsrc->nr_ends); +} + +static Property xive_end_source_properties[] = { + DEFINE_PROP_UINT8("block-id", XiveENDSource, block_id, 0), + DEFINE_PROP_UINT32("nr-ends", XiveENDSource, nr_ends, 0), + DEFINE_PROP_UINT32("shift", XiveENDSource, esb_shift, XIVE_ESB_64K), + DEFINE_PROP_END_OF_LIST(), +}; + +static void xive_end_source_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "XIVE END Source"; + dc->props = xive_end_source_properties; + dc->realize = xive_end_source_realize; +} + +static const TypeInfo xive_end_source_info = { + .name = TYPE_XIVE_END_SOURCE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XiveENDSource), + .class_init = xive_end_source_class_init, +}; + +/* + * XIVE Notifier + */ +static const TypeInfo xive_notifier_info = { + .name = TYPE_XIVE_NOTIFIER, + .parent = TYPE_INTERFACE, + .class_size = sizeof(XiveNotifierClass), +}; + +static void xive_register_types(void) +{ + type_register_static(&xive_source_info); + type_register_static(&xive_notifier_info); + type_register_static(&xive_router_info); + type_register_static(&xive_end_source_info); + type_register_static(&xive_tctx_info); +} + +type_init(xive_register_types) diff --git a/hw/ipack/Kconfig b/hw/ipack/Kconfig new file mode 100644 index 0000000000..f8da24a62b --- /dev/null +++ b/hw/ipack/Kconfig @@ -0,0 +1,4 @@ +config IPACK + bool + default y if PCI_DEVICES + depends on PCI diff --git a/hw/ipmi/Kconfig b/hw/ipmi/Kconfig new file mode 100644 index 0000000000..b944fae100 --- /dev/null +++ b/hw/ipmi/Kconfig @@ -0,0 +1,22 @@ +config IPMI + bool + +config IPMI_LOCAL + bool + default y + depends on IPMI + +config IPMI_EXTERN + bool + default y + depends on IPMI + +config ISA_IPMI_KCS + bool + depends on ISA_BUS + select IPMI + +config ISA_IPMI_BT + bool + depends on ISA_BUS + select IPMI diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index e946030e84..8bbb1fa785 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -22,6 +22,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "qapi/error.h" #include "hw/hw.h" #include "hw/ipmi/ipmi.h" @@ -450,22 +451,63 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); } -static const VMStateDescription vmstate_ISAIPMIBTDevice = { - .name = TYPE_IPMI_INTERFACE, +static int ipmi_bt_vmstate_post_load(void *opaque, int version) +{ + IPMIBT *ib = opaque; + + /* Make sure all the values are sane. */ + if (ib->outpos >= MAX_IPMI_MSG_SIZE || ib->outlen >= MAX_IPMI_MSG_SIZE || + ib->outpos >= ib->outlen) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:bt: vmstate transfer received bad out values: %d %d\n", + ib->outpos, ib->outlen); + ib->outpos = 0; + ib->outlen = 0; + } + + if (ib->inlen >= MAX_IPMI_MSG_SIZE) { + qemu_log_mask(LOG_GUEST_ERROR, + "ipmi:bt: vmstate transfer received bad in value: %d\n", + ib->inlen); + ib->inlen = 0; + } + + return 0; +} + +const VMStateDescription vmstate_IPMIBT = { + .name = TYPE_IPMI_INTERFACE_PREFIX "bt", .version_id = 1, .minimum_version_id = 1, + .post_load = ipmi_bt_vmstate_post_load, .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, bt.outlen), - VMSTATE_VBUFFER_UINT32(bt.inmsg, ISAIPMIBTDevice, 1, NULL, 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_BOOL(obf_irq_set, IPMIBT), + VMSTATE_BOOL(atn_irq_set, IPMIBT), + VMSTATE_BOOL(irqs_enabled, IPMIBT), + VMSTATE_UINT32(outpos, IPMIBT), + VMSTATE_UINT32(outlen, IPMIBT), + VMSTATE_UINT8_ARRAY(outmsg, IPMIBT, MAX_IPMI_MSG_SIZE), + VMSTATE_UINT32(inlen, IPMIBT), + VMSTATE_UINT8_ARRAY(inmsg, IPMIBT, MAX_IPMI_MSG_SIZE), + VMSTATE_UINT8(control_reg, IPMIBT), + VMSTATE_UINT8(mask_reg, IPMIBT), + VMSTATE_UINT8(waiting_rsp, IPMIBT), + VMSTATE_UINT8(waiting_seq, IPMIBT), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_ISAIPMIBTDevice = { + .name = TYPE_IPMI_INTERFACE_PREFIX "isa-bt", + .version_id = 2, + .minimum_version_id = 2, + /* + * Version 1 had messed up the array transfer, it's not even usable + * because it used VMSTATE_VBUFFER_UINT32, but it did not transfer + * the buffer length, so random things would happen. + */ + .fields = (VMStateField[]) { + VMSTATE_STRUCT(bt, ISAIPMIBTDevice, 1, vmstate_IPMIBT, IPMIBT), VMSTATE_END_OF_LIST() } }; diff --git a/hw/isa/Kconfig b/hw/isa/Kconfig new file mode 100644 index 0000000000..6db0d7970c --- /dev/null +++ b/hw/isa/Kconfig @@ -0,0 +1,55 @@ +config ISA_BUS + bool + +config APM + bool + +config I82378 + bool + select ISA_BUS + select I8259 + select I8254 + select I82374 + select MC146818RTC + select PCSPK + +config PC87312 + bool + select ISA_BUS + select I8259 + select I8254 + select I8257 + select MC146818RTC + select SERIAL_ISA + select PARALLEL + select FDC + select IDE_ISA + +config PIIX4 + bool + # For historical reasons, SuperIO devices are created in the board + # for PIIX4. + select ISA_BUS + select USB_UHCI + +config VT82C686 + bool + select ISA_BUS + select ACPI_SMBUS + select SERIAL_ISA + select FDC + +config SMC37C669 + bool + select ISA_BUS + select SERIAL_ISA + select PARALLEL + select FDC + +config LPC_ICH9 + bool + # For historical reasons, SuperIO devices are created in the board + # for ICH9. + select ISA_BUS + select ACPI_SMBUS + select ACPI_X86_ICH diff --git a/hw/isa/Makefile.objs b/hw/isa/Makefile.objs index 83e06f6c04..9e106df186 100644 --- a/hw/isa/Makefile.objs +++ b/hw/isa/Makefile.objs @@ -1,9 +1,10 @@ common-obj-$(CONFIG_ISA_BUS) += isa-bus.o -common-obj-$(CONFIG_ISA_BUS) += isa-superio.o smc37c669-superio.o +common-obj-$(CONFIG_ISA_BUS) += isa-superio.o common-obj-$(CONFIG_APM) += apm.o common-obj-$(CONFIG_I82378) += i82378.o common-obj-$(CONFIG_PC87312) += pc87312.o common-obj-$(CONFIG_PIIX4) += piix4.o common-obj-$(CONFIG_VT82C686) += vt82c686.o +common-obj-$(CONFIG_SMC37C669) += smc37c669-superio.o obj-$(CONFIG_LPC_ICH9) += lpc_ich9.o diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 8bc2f69eaa..d54463bf03 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -44,7 +44,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) chr = parallel_hds[i]; if (chr == NULL) { name = g_strdup_printf("discarding-parallel%d", i); - chr = qemu_chr_new(name, "null"); + chr = qemu_chr_new(name, "null", NULL); } else { name = g_strdup_printf("parallel%d", i); } @@ -84,7 +84,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) chr = serial_hd(i); if (chr == NULL) { name = g_strdup_printf("discarding-serial%d", i); - chr = qemu_chr_new(name, "null"); + chr = qemu_chr_new(name, "null", NULL); } else { name = g_strdup_printf("serial%d", i); } diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index e692b9fdc1..ac44aa53be 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -805,6 +805,7 @@ static void ich9_lpc_class_init(ObjectClass *klass, void *data) * pc_q35_init() */ dc->user_creatable = false; + hc->pre_plug = ich9_pm_device_pre_plug_cb; hc->plug = ich9_pm_device_plug_cb; hc->unplug_request = ich9_pm_device_unplug_request_cb; hc->unplug = ich9_pm_device_unplug_cb; diff --git a/hw/isa/trace-events b/hw/isa/trace-events index 80ac6175d6..202f8938e7 100644 --- a/hw/isa/trace-events +++ b/hw/isa/trace-events @@ -1,11 +1,11 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/isa/isa-superio.c +# isa-superio.c superio_create_parallel(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" superio_create_serial(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" superio_create_floppy(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" superio_create_ide(int id, uint16_t base, unsigned int irq) "id=%d, base 0x%03x, irq %u" -# hw/isa/pc87312.c +# pc87312.c pc87312_io_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" pc87312_io_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index cff1946232..85d0532dd5 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -14,7 +14,6 @@ #include "hw/hw.h" #include "hw/isa/vt82c686.h" #include "hw/i2c/i2c.h" -#include "hw/i2c/smbus.h" #include "hw/pci/pci.h" #include "hw/isa/isa.h" #include "hw/isa/superio.h" @@ -370,7 +369,7 @@ static void vt82c686b_pm_realize(PCIDevice *dev, Error **errp) pci_conf[0x90] = s->smb_io_base | 1; pci_conf[0x91] = s->smb_io_base >> 8; pci_conf[0xd2] = 0x90; - pm_smbus_init(&s->dev.qdev, &s->smb); + pm_smbus_init(&s->dev.qdev, &s->smb, false); memory_region_add_subregion(get_system_io(), s->smb_io_base, &s->smb.io); apm_init(dev, &s->apm, NULL, s); diff --git a/hw/lm32/Kconfig b/hw/lm32/Kconfig new file mode 100644 index 0000000000..3d09c2dd6f --- /dev/null +++ b/hw/lm32/Kconfig @@ -0,0 +1,13 @@ +config LM32 + bool + select PTIMER + select PFLASH_CFI02 + +config MILKYMIST + bool + # FIXME: disabling it results in compile-time errors + select MILKYMIST_TMU2 if OPENGL && X11 + select PTIMER + select PFLASH_CFI01 + select FRAMEBUFFER + select SD diff --git a/hw/lm32/Makefile.objs b/hw/lm32/Makefile.objs index ea6418ae59..c3941866c7 100644 --- a/hw/lm32/Makefile.objs +++ b/hw/lm32/Makefile.objs @@ -1,3 +1,3 @@ # LM32 boards -obj-y += lm32_boards.o -obj-y += milkymist.o +obj-$(CONFIG_LM32) += lm32_boards.o +obj-$(CONFIG_MILKYMIST) += milkymist.o diff --git a/hw/lm32/lm32_boards.c b/hw/lm32/lm32_boards.c index fd8eccca14..b820c9114b 100644 --- a/hw/lm32/lm32_boards.c +++ b/hw/lm32/lm32_boards.c @@ -25,7 +25,6 @@ #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/block/flash.h" -#include "hw/devices.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" @@ -114,9 +113,9 @@ static void lm32_evr_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); /* Spansion S29NS128P */ - pflash_cfi02_register(flash_base, NULL, "lm32_evr.flash", flash_size, + pflash_cfi02_register(flash_base, "lm32_evr.flash", flash_size, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - flash_sector_size, flash_size / flash_sector_size, + flash_sector_size, 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); /* create irq lines */ @@ -138,7 +137,8 @@ static void lm32_evr_init(MachineState *machine) uint64_t entry; int kernel_size; - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; @@ -206,9 +206,9 @@ static void lm32_uclinux_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); /* Spansion S29NS128P */ - pflash_cfi02_register(flash_base, NULL, "lm32_uclinux.flash", flash_size, + pflash_cfi02_register(flash_base, "lm32_uclinux.flash", flash_size, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - flash_sector_size, flash_size / flash_sector_size, + flash_sector_size, 1, 2, 0x01, 0x7e, 0x43, 0x00, 0x555, 0x2aa, 1); /* create irq lines */ @@ -231,7 +231,8 @@ static void lm32_uclinux_init(MachineState *machine) uint64_t entry; int kernel_size; - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; diff --git a/hw/lm32/milkymist-hw.h b/hw/lm32/milkymist-hw.h index d3be0cfb3a..976cf9254d 100644 --- a/hw/lm32/milkymist-hw.h +++ b/hw/lm32/milkymist-hw.h @@ -88,69 +88,6 @@ static inline DeviceState *milkymist_pfpu_create(hwaddr base, return dev; } -#ifdef CONFIG_OPENGL -#include -#include -#include -static const int glx_fbconfig_attr[] = { - GLX_GREEN_SIZE, 5, - GLX_GREEN_SIZE, 6, - GLX_BLUE_SIZE, 5, - None -}; -#endif - -static inline DeviceState *milkymist_tmu2_create(hwaddr base, - qemu_irq irq) -{ -#ifdef CONFIG_OPENGL - DeviceState *dev; - Display *d; - GLXFBConfig *configs; - int nelements; - int ver_major, ver_minor; - - /* check that GLX will work */ - d = XOpenDisplay(NULL); - if (d == NULL) { - return NULL; - } - - if (!glXQueryVersion(d, &ver_major, &ver_minor)) { - /* Yeah, sometimes getting the GLX version can fail. - * Isn't X beautiful? */ - XCloseDisplay(d); - return NULL; - } - - if ((ver_major < 1) || ((ver_major == 1) && (ver_minor < 3))) { - printf("Your GLX version is %d.%d," - "but TMU emulation needs at least 1.3. TMU disabled.\n", - ver_major, ver_minor); - XCloseDisplay(d); - return NULL; - } - - configs = glXChooseFBConfig(d, 0, glx_fbconfig_attr, &nelements); - if (configs == NULL) { - XCloseDisplay(d); - return NULL; - } - - XFree(configs); - XCloseDisplay(d); - - dev = qdev_create(NULL, "milkymist-tmu2"); - qdev_init_nofail(dev); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq); - - return dev; -#else - return NULL; -#endif -} - static inline DeviceState *milkymist_ac97_create(hwaddr base, qemu_irq crrequest_irq, qemu_irq crreply_irq, qemu_irq dmar_irq, qemu_irq dmaw_irq) diff --git a/hw/lm32/milkymist.c b/hw/lm32/milkymist.c index 321f184595..689e633199 100644 --- a/hw/lm32/milkymist.c +++ b/hw/lm32/milkymist.c @@ -27,11 +27,11 @@ #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" -#include "hw/devices.h" #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" #include "milkymist-hw.h" +#include "hw/display/milkymist_tmu2.h" #include "lm32.h" #include "exec/address-spaces.h" @@ -120,10 +120,9 @@ milkymist_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); /* Numonyx JS28F256J3F105 */ - pflash_cfi01_register(flash_base, NULL, "milkymist.flash", flash_size, + pflash_cfi01_register(flash_base, "milkymist.flash", flash_size, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - flash_sector_size, flash_size / flash_sector_size, - 2, 0x00, 0x89, 0x00, 0x1d, 1); + flash_sector_size, 2, 0x00, 0x89, 0x00, 0x1d, 1); /* create irq lines */ env->pic_state = lm32_pic_init(qemu_allocate_irq(cpu_irq_handler, cpu, 0)); @@ -138,7 +137,10 @@ milkymist_init(MachineState *machine) bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (bios_filename) { - load_image_targphys(bios_filename, BIOS_OFFSET, BIOS_SIZE); + if (load_image_targphys(bios_filename, BIOS_OFFSET, BIOS_SIZE) < 0) { + error_report("could not load bios '%s'", bios_filename); + exit(1); + } } reset_info->bootstrap_pc = BIOS_OFFSET; @@ -172,7 +174,8 @@ milkymist_init(MachineState *machine) uint64_t entry; /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, 1, EM_LATTICEMICO32, 0, 0); reset_info->bootstrap_pc = entry; diff --git a/hw/m68k/Kconfig b/hw/m68k/Kconfig new file mode 100644 index 0000000000..49ef0b3f6d --- /dev/null +++ b/hw/m68k/Kconfig @@ -0,0 +1,9 @@ +config AN5206 + bool + select COLDFIRE + select PTIMER + +config MCF5208 + bool + select COLDFIRE + select PTIMER diff --git a/hw/m68k/Makefile.objs b/hw/m68k/Makefile.objs index d1f089c08a..482f8477b4 100644 --- a/hw/m68k/Makefile.objs +++ b/hw/m68k/Makefile.objs @@ -1,2 +1,2 @@ -obj-y += an5206.o mcf5208.o -obj-y += mcf5206.o mcf_intc.o +obj-$(CONFIG_AN5206) += an5206.o mcf5206.o +obj-$(CONFIG_MCF5208) += mcf5208.o mcf_intc.o diff --git a/hw/m68k/an5206.c b/hw/m68k/an5206.c index 5e067ea1c3..06e3803258 100644 --- a/hw/m68k/an5206.c +++ b/hw/m68k/an5206.c @@ -66,7 +66,7 @@ static void an5206_init(MachineState *machine) exit(1); } - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { diff --git a/hw/m68k/mcf5208.c b/hw/m68k/mcf5208.c index 0f2245dd81..6f6efae9fc 100644 --- a/hw/m68k/mcf5208.c +++ b/hw/m68k/mcf5208.c @@ -27,6 +27,8 @@ #define SYS_FREQ 166666666 +#define ROM_SIZE 0x200000 + #define PCSR_EN 0x0001 #define PCSR_RLD 0x0002 #define PCSR_PIF 0x0004 @@ -227,6 +229,7 @@ static void mcf5208evb_init(MachineState *machine) hwaddr entry; qemu_irq *pic; MemoryRegion *address_space_mem = get_system_memory(); + MemoryRegion *rom = g_new(MemoryRegion, 1); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *sram = g_new(MemoryRegion, 1); @@ -237,6 +240,10 @@ static void mcf5208evb_init(MachineState *machine) env->vbr = 0; /* TODO: Configure BARs. */ + /* ROM at 0x00000000 */ + memory_region_init_rom(rom, NULL, "mcf5208.rom", ROM_SIZE, &error_fatal); + memory_region_add_subregion(address_space_mem, 0x00000000, rom); + /* DRAM at 0x40000000 */ memory_region_allocate_system_memory(ram, NULL, "mcf5208.ram", ram_size); memory_region_add_subregion(address_space_mem, 0x40000000, ram); @@ -285,16 +292,37 @@ static void mcf5208evb_init(MachineState *machine) /* 0xfc0a4000 GPIO. */ /* 0xfc0a8000 SDRAM controller. */ + /* Load firmware */ + if (bios_name) { + char *fn; + uint8_t *ptr; + + fn = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!fn) { + error_report("Could not find ROM image '%s'", bios_name); + exit(1); + } + if (load_image_targphys(fn, 0x0, ROM_SIZE) < 8) { + error_report("Could not load ROM image '%s'", bios_name); + exit(1); + } + g_free(fn); + /* Initial PC is always at offset 4 in firmware binaries */ + ptr = rom_ptr(0x4, 4); + assert(ptr != NULL); + env->pc = ldl_p(ptr); + } + /* Load kernel. */ if (!kernel_filename) { - if (qtest_enabled()) { + if (qtest_enabled() || bios_name) { return; } error_report("Kernel image must be specified"); exit(1); } - kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, NULL, 1, EM_68K, 0, 0); entry = elf_entry; if (kernel_size < 0) { diff --git a/hw/mem/Kconfig b/hw/mem/Kconfig new file mode 100644 index 0000000000..620fd4cb59 --- /dev/null +++ b/hw/mem/Kconfig @@ -0,0 +1,11 @@ +config DIMM + bool + select MEM_DEVICE + +config MEM_DEVICE + bool + +config NVDIMM + bool + default y + depends on PC diff --git a/hw/mem/Makefile.objs b/hw/mem/Makefile.objs index 10be4df2a2..3e2f7c5ca2 100644 --- a/hw/mem/Makefile.objs +++ b/hw/mem/Makefile.objs @@ -1,3 +1,3 @@ -common-obj-$(CONFIG_MEM_HOTPLUG) += pc-dimm.o -common-obj-$(CONFIG_MEM_HOTPLUG) += memory-device.o +common-obj-$(CONFIG_DIMM) += pc-dimm.o +common-obj-$(CONFIG_MEM_DEVICE) += memory-device.o common-obj-$(CONFIG_NVDIMM) += nvdimm.o diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 6de4f70bb4..5f2c408036 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -17,6 +17,7 @@ #include "qemu/range.h" #include "hw/virtio/vhost.h" #include "sysemu/kvm.h" +#include "trace.h" static gint memory_device_addr_sort(gconstpointer a, gconstpointer b) { @@ -57,10 +58,9 @@ static int memory_device_used_region_size(Object *obj, void *opaque) if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) { const DeviceState *dev = DEVICE(obj); const MemoryDeviceState *md = MEMORY_DEVICE(obj); - const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); if (dev->realized) { - *size += mdc->get_region_size(md); + *size += memory_device_get_region_size(md, &error_abort); } } @@ -85,22 +85,23 @@ static void memory_device_check_addable(MachineState *ms, uint64_t size, /* will we exceed the total amount of memory specified */ memory_device_used_region_size(OBJECT(ms), &used_region_size); - if (used_region_size + size > ms->maxram_size - ms->ram_size) { + if (used_region_size + size < used_region_size || + used_region_size + size > ms->maxram_size - ms->ram_size) { error_setg(errp, "not enough space, currently 0x%" PRIx64 - " in use of total hot pluggable 0x" RAM_ADDR_FMT, + " in use of total space for memory devices 0x" RAM_ADDR_FMT, used_region_size, ms->maxram_size - ms->ram_size); return; } } -uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, - uint64_t align, uint64_t size, - Error **errp) +static uint64_t memory_device_get_free_addr(MachineState *ms, + const uint64_t *hint, + uint64_t align, uint64_t size, + Error **errp) { - uint64_t address_space_start, address_space_end; GSList *list = NULL, *item; - uint64_t new_addr = 0; + Range as, new = range_empty; if (!ms->device_memory) { error_setg(errp, "memory devices (e.g. for memory hotplug) are not " @@ -113,14 +114,12 @@ uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, "enabled, please specify the maxmem option"); return 0; } - address_space_start = ms->device_memory->base; - address_space_end = address_space_start + - memory_region_size(&ms->device_memory->mr); - g_assert(address_space_end >= address_space_start); + range_init_nofail(&as, ms->device_memory->base, + memory_region_size(&ms->device_memory->mr)); - /* address_space_start indicates the maximum alignment we expect */ - if (QEMU_ALIGN_UP(address_space_start, align) != address_space_start) { - error_setg(errp, "the alignment (0%" PRIx64 ") is not supported", + /* start of address space indicates the maximum alignment we expect */ + if (!QEMU_IS_ALIGNED(range_lob(&as), align)) { + error_setg(errp, "the alignment (0x%" PRIx64 ") is not supported", align); return 0; } @@ -130,32 +129,31 @@ uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, return 0; } - if (hint && QEMU_ALIGN_UP(*hint, align) != *hint) { + if (hint && !QEMU_IS_ALIGNED(*hint, align)) { error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes", align); return 0; } - if (QEMU_ALIGN_UP(size, align) != size) { + if (!QEMU_IS_ALIGNED(size, align)) { error_setg(errp, "backend memory size must be multiple of 0x%" PRIx64, align); return 0; } if (hint) { - new_addr = *hint; - if (new_addr < address_space_start) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] at 0x%" PRIx64, new_addr, size, address_space_start); - return 0; - } else if ((new_addr + size) > address_space_end) { - error_setg(errp, "can't add memory [0x%" PRIx64 ":0x%" PRIx64 - "] beyond 0x%" PRIx64, new_addr, size, - address_space_end); + if (range_init(&new, *hint, size) || !range_contains_range(&as, &new)) { + error_setg(errp, "can't add memory device [0x%" PRIx64 ":0x%" PRIx64 + "], usable range for memory devices [0x%" PRIx64 ":0x%" + PRIx64 "]", *hint, size, range_lob(&as), + range_size(&as)); return 0; } } else { - new_addr = address_space_start; + if (range_init(&new, range_lob(&as), size)) { + error_setg(errp, "can't add memory device, device too big"); + return 0; + } } /* find address range that will fit new memory device */ @@ -163,32 +161,36 @@ uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, for (item = list; item; item = g_slist_next(item)) { const MemoryDeviceState *md = item->data; const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(OBJECT(md)); - uint64_t md_size, md_addr; + uint64_t next_addr; + Range tmp; - md_addr = mdc->get_addr(md); - md_size = mdc->get_region_size(md); - if (*errp) { - goto out; - } + range_init_nofail(&tmp, mdc->get_addr(md), + memory_device_get_region_size(md, &error_abort)); - if (ranges_overlap(md_addr, md_size, new_addr, size)) { + if (range_overlaps_range(&tmp, &new)) { if (hint) { const DeviceState *d = DEVICE(md); - error_setg(errp, "address range conflicts with '%s'", d->id); + error_setg(errp, "address range conflicts with memory device" + " id='%s'", d->id ? d->id : "(unnamed)"); goto out; } - new_addr = QEMU_ALIGN_UP(md_addr + md_size, align); + + next_addr = QEMU_ALIGN_UP(range_upb(&tmp) + 1, align); + if (!next_addr || range_init(&new, next_addr, range_size(&new))) { + range_make_empty(&new); + break; + } } } - if (new_addr + size > address_space_end) { + if (!range_contains_range(&as, &new)) { error_setg(errp, "could not find position in guest address space for " "memory device - memory fragmented due to alignments"); goto out; } out: g_slist_free(list); - return new_addr; + return range_lob(&new); } MemoryDeviceInfoList *qmp_memory_device_list(void) @@ -232,7 +234,7 @@ static int memory_device_plugged_size(Object *obj, void *opaque) const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj); if (dev->realized) { - *size += mdc->get_plugged_size(md); + *size += mdc->get_plugged_size(md, &error_abort); } } @@ -249,22 +251,83 @@ uint64_t get_plugged_memory_size(void) return size; } -void memory_device_plug_region(MachineState *ms, MemoryRegion *mr, - uint64_t addr) +void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, + const uint64_t *legacy_align, Error **errp) { - /* we expect a previous call to memory_device_get_free_addr() */ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + Error *local_err = NULL; + uint64_t addr, align; + MemoryRegion *mr; + + mr = mdc->get_memory_region(md, &local_err); + if (local_err) { + goto out; + } + + align = legacy_align ? *legacy_align : memory_region_get_alignment(mr); + addr = mdc->get_addr(md); + addr = memory_device_get_free_addr(ms, !addr ? NULL : &addr, align, + memory_region_size(mr), &local_err); + if (local_err) { + goto out; + } + mdc->set_addr(md, addr, &local_err); + if (!local_err) { + trace_memory_device_pre_plug(DEVICE(md)->id ? DEVICE(md)->id : "", + addr); + } +out: + error_propagate(errp, local_err); +} + +void memory_device_plug(MemoryDeviceState *md, MachineState *ms) +{ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + const uint64_t addr = mdc->get_addr(md); + MemoryRegion *mr; + + /* + * We expect that a previous call to memory_device_pre_plug() succeeded, so + * it can't fail at this point. + */ + mr = mdc->get_memory_region(md, &error_abort); g_assert(ms->device_memory); memory_region_add_subregion(&ms->device_memory->mr, addr - ms->device_memory->base, mr); + trace_memory_device_plug(DEVICE(md)->id ? DEVICE(md)->id : "", addr); } -void memory_device_unplug_region(MachineState *ms, MemoryRegion *mr) +void memory_device_unplug(MemoryDeviceState *md, MachineState *ms) { - /* we expect a previous call to memory_device_get_free_addr() */ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + MemoryRegion *mr; + + /* + * We expect that a previous call to memory_device_pre_plug() succeeded, so + * it can't fail at this point. + */ + mr = mdc->get_memory_region(md, &error_abort); g_assert(ms->device_memory); memory_region_del_subregion(&ms->device_memory->mr, mr); + trace_memory_device_unplug(DEVICE(md)->id ? DEVICE(md)->id : "", + mdc->get_addr(md)); +} + +uint64_t memory_device_get_region_size(const MemoryDeviceState *md, + Error **errp) +{ + const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md); + MemoryRegion *mr; + + /* dropping const here is fine as we don't touch the memory region */ + mr = mdc->get_memory_region((MemoryDeviceState *)md, errp); + if (!mr) { + return 0; + } + + return memory_region_size(mr); } static const TypeInfo memory_device_info = { diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index 021d1c3997..bf2adf5e16 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -23,9 +23,11 @@ */ #include "qemu/osdep.h" +#include "qemu/pmem.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "hw/mem/nvdimm.h" +#include "hw/mem/memory-device.h" static void nvdimm_get_label_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) @@ -114,12 +116,14 @@ static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp) nvdimm->nvdimm_mr = g_new(MemoryRegion, 1); memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm), "nvdimm-memory", mr, 0, pmem_size); + memory_region_set_nonvolatile(nvdimm->nvdimm_mr, true); nvdimm->nvdimm_mr->align = align; } -static MemoryRegion *nvdimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) +static MemoryRegion *nvdimm_md_get_memory_region(MemoryDeviceState *md, + Error **errp) { - NVDIMMDevice *nvdimm = NVDIMM(dimm); + NVDIMMDevice *nvdimm = NVDIMM(md); Error *local_err = NULL; if (!nvdimm->nvdimm_mr) { @@ -164,11 +168,17 @@ static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf, { MemoryRegion *mr; PCDIMMDevice *dimm = PC_DIMM(nvdimm); + bool is_pmem = object_property_get_bool(OBJECT(dimm->hostmem), + "pmem", NULL); uint64_t backend_offset; nvdimm_validate_rw_label_data(nvdimm, size, offset); - memcpy(nvdimm->label_data + offset, buf, size); + if (!is_pmem) { + memcpy(nvdimm->label_data + offset, buf, size); + } else { + pmem_memcpy_persist(nvdimm->label_data + offset, buf, size); + } mr = host_memory_backend_get_memory(dimm->hostmem); backend_offset = memory_region_size(mr) - nvdimm->label_size + offset; @@ -183,11 +193,12 @@ static Property nvdimm_properties[] = { static void nvdimm_class_init(ObjectClass *oc, void *data) { PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); + MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); NVDIMMClass *nvc = NVDIMM_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); ddc->realize = nvdimm_realize; - ddc->get_memory_region = nvdimm_get_memory_region; + mdc->get_memory_region = nvdimm_md_get_memory_region; dc->props = nvdimm_properties; nvc->read_label_data = nvdimm_read_label_data; diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 65843bc52a..152400b1fc 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -29,69 +29,47 @@ static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp); -void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, - Error **errp) +void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, + const uint64_t *legacy_align, Error **errp) { - int slot; - PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, - &error_abort); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); Error *local_err = NULL; - uint64_t addr; - - addr = object_property_get_uint(OBJECT(dimm), - PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out; - } - - addr = memory_device_get_free_addr(machine, !addr ? NULL : &addr, align, - memory_region_size(mr), &local_err); - if (local_err) { - goto out; - } - - object_property_set_uint(OBJECT(dev), addr, PC_DIMM_ADDR_PROP, &local_err); - if (local_err) { - goto out; - } - trace_mhp_pc_dimm_assigned_address(addr); - - slot = object_property_get_int(OBJECT(dev), PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } + int slot; + slot = object_property_get_int(OBJECT(dimm), PC_DIMM_SLOT_PROP, + &error_abort); slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot, machine->ram_slots, &local_err); if (local_err) { goto out; } - object_property_set_int(OBJECT(dev), slot, PC_DIMM_SLOT_PROP, &local_err); - if (local_err) { - goto out; - } + object_property_set_int(OBJECT(dimm), slot, PC_DIMM_SLOT_PROP, + &error_abort); trace_mhp_pc_dimm_assigned_slot(slot); - memory_device_plug_region(machine, mr, addr); - vmstate_register_ram(vmstate_mr, dev); - + memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, legacy_align, + &local_err); out: error_propagate(errp, local_err); } -void pc_dimm_unplug(DeviceState *dev, MachineState *machine) +void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp) { - PCDIMMDevice *dimm = PC_DIMM(dev); PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, &error_abort); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - memory_device_unplug_region(machine, mr); - vmstate_unregister_ram(vmstate_mr, dev); + memory_device_plug(MEMORY_DEVICE(dimm), machine); + vmstate_register_ram(vmstate_mr, DEVICE(dimm)); +} + +void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine) +{ + PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); + MemoryRegion *vmstate_mr = ddc->get_vmstate_memory_region(dimm, + &error_abort); + + memory_device_unplug(MEMORY_DEVICE(dimm), machine); + vmstate_unregister_ram(vmstate_mr, DEVICE(dimm)); } static int pc_dimm_slot2bitmap(Object *obj, void *opaque) @@ -160,16 +138,14 @@ static Property pc_dimm_properties[] = { static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { + Error *local_err = NULL; uint64_t value; - MemoryRegion *mr; - PCDIMMDevice *dimm = PC_DIMM(obj); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(obj); - mr = ddc->get_memory_region(dimm, errp); - if (!mr) { + value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - value = memory_region_size(mr); visit_type_uint64(v, name, &value, errp); } @@ -228,24 +204,19 @@ static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp) static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md) { - const PCDIMMDevice *dimm = PC_DIMM(md); - - return dimm->addr; + return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP, &error_abort); } -static uint64_t pc_dimm_md_get_region_size(const MemoryDeviceState *md) +static void pc_dimm_md_set_addr(MemoryDeviceState *md, uint64_t addr, + Error **errp) { - /* dropping const here is fine as we don't touch the memory region */ - PCDIMMDevice *dimm = PC_DIMM(md); - const PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(md); - MemoryRegion *mr; + object_property_set_uint(OBJECT(md), addr, PC_DIMM_ADDR_PROP, errp); +} - mr = ddc->get_memory_region(dimm, &error_abort); - if (!mr) { - return 0; - } - - return memory_region_size(mr); +static MemoryRegion *pc_dimm_md_get_memory_region(MemoryDeviceState *md, + Error **errp) +{ + return pc_dimm_get_memory_region(PC_DIMM(md), errp); } static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, @@ -289,13 +260,13 @@ static void pc_dimm_class_init(ObjectClass *oc, void *data) dc->props = pc_dimm_properties; dc->desc = "DIMM memory module"; - ddc->get_memory_region = pc_dimm_get_memory_region; ddc->get_vmstate_memory_region = pc_dimm_get_memory_region; mdc->get_addr = pc_dimm_md_get_addr; + mdc->set_addr = pc_dimm_md_set_addr; /* for a dimm plugged_size == region_size */ - mdc->get_plugged_size = pc_dimm_md_get_region_size; - mdc->get_region_size = pc_dimm_md_get_region_size; + mdc->get_plugged_size = memory_device_get_region_size; + mdc->get_memory_region = pc_dimm_md_get_memory_region; mdc->fill_device_info = pc_dimm_md_fill_device_info; } diff --git a/hw/mem/trace-events b/hw/mem/trace-events index e150dcc497..9f6b52acd7 100644 --- a/hw/mem/trace-events +++ b/hw/mem/trace-events @@ -1,5 +1,8 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/mem/pc-dimm.c +# pc-dimm.c mhp_pc_dimm_assigned_slot(int slot) "%d" -mhp_pc_dimm_assigned_address(uint64_t addr) "0x%"PRIx64 +# memory-device.c +memory_device_pre_plug(const char *id, uint64_t addr) "id=%s addr=0x%"PRIx64 +memory_device_plug(const char *id, uint64_t addr) "id=%s addr=0x%"PRIx64 +memory_device_unplug(const char *id, uint64_t addr) "id=%s addr=0x%"PRIx64 diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig new file mode 100644 index 0000000000..c4dc120973 --- /dev/null +++ b/hw/microblaze/Kconfig @@ -0,0 +1,20 @@ +config PETALOGIX_S3ADSP1800 + bool + select PFLASH_CFI01 + select XILINX + select XILINX_AXI + select XILINX_ETHLITE + +config PETALOGIX_ML605 + bool + select PFLASH_CFI01 + select SERIAL + select SSI_M25P80 + select XILINX + select XILINX_AXI + select XILINX_ETHLITE + select XILINX_SPI + +config XLNX_ZYNQMP_PMU + bool + select XLNX_ZYNQMP diff --git a/hw/microblaze/Makefile.objs b/hw/microblaze/Makefile.objs index ae9fd40de7..8595a62f6c 100644 --- a/hw/microblaze/Makefile.objs +++ b/hw/microblaze/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += petalogix_s3adsp1800_mmu.o -obj-y += petalogix_ml605_mmu.o -obj-y += xlnx-zynqmp-pmu.o +obj-$(CONFIG_PETALOGIX_S3ADSP1800) += petalogix_s3adsp1800_mmu.o +obj-$(CONFIG_PETALOGIX_ML605) += petalogix_ml605_mmu.o +obj-$(CONFIG_XLNX_ZYNQMP_PMU) += xlnx-zynqmp-pmu.o obj-y += boot.o diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 35bfeda7aa..a7af4c0704 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -142,13 +142,14 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, #endif /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, &low, &high, big_endian, EM_MICROBLAZE, 0, 0); base32 = entry; if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, + translate_kernel_address, NULL, + &entry, NULL, NULL, big_endian, EM_MICROBLAZE, 0, 0); } /* Always boot into physical ram. */ @@ -156,7 +157,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, hwaddr ddr_base, /* If it wasn't an ELF image, try an u-boot image. */ if (kernel_size < 0) { - hwaddr uentry, loadaddr; + hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, NULL, NULL); diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index c730878d25..a907604116 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -35,7 +35,6 @@ #include "net/net.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" -#include "hw/devices.h" #include "hw/boards.h" #include "hw/char/serial.h" #include "exec/address-spaces.h" @@ -107,11 +106,9 @@ petalogix_ml605_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); /* 5th parameter 2 means bank-width * 10th paremeter 0 means little-endian */ - pflash_cfi01_register(FLASH_BASEADDR, - NULL, "petalogix_ml605.flash", FLASH_SIZE, + pflash_cfi01_register(FLASH_BASEADDR, "petalogix_ml605.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 64 * KiB, FLASH_SIZE >> 16, - 2, 0x89, 0x18, 0x0000, 0x0, 0); + 64 * KiB, 2, 0x89, 0x18, 0x0000, 0x0, 0); dev = qdev_create(NULL, "xlnx.xps-intc"); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 5cf7b84c79..88ce570f9a 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -33,8 +33,8 @@ #include "net/net.h" #include "hw/block/flash.h" #include "sysemu/sysemu.h" -#include "hw/devices.h" #include "hw/boards.h" +#include "hw/misc/unimp.h" #include "exec/address-spaces.h" #include "hw/char/xilinx_uartlite.h" @@ -47,6 +47,7 @@ #define MEMORY_BASEADDR 0x90000000 #define FLASH_BASEADDR 0xa0000000 +#define GPIO_BASEADDR 0x81400000 #define INTC_BASEADDR 0x81800000 #define TIMER_BASEADDR 0x83c00000 #define UARTLITE_BASEADDR 0x84000000 @@ -86,10 +87,9 @@ petalogix_s3adsp1800_init(MachineState *machine) dinfo = drive_get(IF_PFLASH, 0, 0); pflash_cfi01_register(FLASH_BASEADDR, - NULL, "petalogix_s3adsp1800.flash", FLASH_SIZE, + "petalogix_s3adsp1800.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 64 * KiB, FLASH_SIZE >> 16, - 1, 0x89, 0x18, 0x0000, 0x0, 1); + 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); dev = qdev_create(NULL, "xlnx.xps-intc"); qdev_prop_set_uint32(dev, "kind-of-intr", @@ -122,6 +122,8 @@ petalogix_s3adsp1800_init(MachineState *machine) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, ETHLITE_BASEADDR); sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, irq[ETHLITE_IRQ]); + create_unimplemented_device("gpio", GPIO_BASEADDR, 0x10000); + microblaze_load_kernel(cpu, ddr_base, ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig new file mode 100644 index 0000000000..cdc07e59b6 --- /dev/null +++ b/hw/mips/Kconfig @@ -0,0 +1,21 @@ +config R4K + bool + +config MALTA + bool + +config MIPSSIM + bool + +config JAZZ + bool + +config FULONG + bool + +config MIPS_CPS + bool + select PTIMER + +config MIPS_BOSTON + bool diff --git a/hw/mips/Makefile.objs b/hw/mips/Makefile.objs index 17a311aaba..525809af07 100644 --- a/hw/mips/Makefile.objs +++ b/hw/mips/Makefile.objs @@ -1,7 +1,8 @@ -obj-y += mips_r4k.o mips_malta.o mips_mipssim.o obj-y += addr.o mips_int.o +obj-$(CONFIG_R4K) += mips_r4k.o +obj-$(CONFIG_MALTA) += gt64xxx_pci.o mips_malta.o +obj-$(CONFIG_MIPSSIM) += mips_mipssim.o obj-$(CONFIG_JAZZ) += mips_jazz.o obj-$(CONFIG_FULONG) += mips_fulong2e.o -obj-y += gt64xxx_pci.o obj-$(CONFIG_MIPS_CPS) += cps.o obj-$(CONFIG_MIPS_BOSTON) += boston.o diff --git a/hw/mips/boston.c b/hw/mips/boston.c index 6c9c20a93e..e5bab3cadc 100644 --- a/hw/mips/boston.c +++ b/hw/mips/boston.c @@ -512,7 +512,7 @@ static void boston_mach_init(MachineState *machine) memory_region_init_io(lcd, NULL, &boston_lcd_ops, s, "boston-lcd", 0x8); memory_region_add_subregion_overlap(sys_mem, 0x17fff000, lcd, 0); - chr = qemu_chr_new("lcd", "vc:320x240"); + chr = qemu_chr_new("lcd", "vc:320x240", NULL); qemu_chr_fe_init(&s->lcd_display, chr, NULL); qemu_chr_fe_set_handlers(&s->lcd_display, NULL, NULL, boston_lcd_event, NULL, s, NULL, true); diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 4285d1964e..fc97f59af4 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -69,6 +69,7 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) Error *err = NULL; target_ulong gcr_base; bool itu_present = false; + bool saar_present = false; for (i = 0; i < s->num_vp; i++) { cpu = MIPS_CPU(cpu_create(s->cpu_type)); @@ -82,12 +83,14 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) itu_present = true; /* Attach ITC Tag to the VP */ env->itc_tag = mips_itu_get_tag_region(&s->itu); + env->itu = &s->itu; } qemu_register_reset(main_cpu_reset, cpu); } cpu = MIPS_CPU(first_cpu); env = &cpu->env; + saar_present = (bool)env->saarp; /* Inter-Thread Communication Unit */ if (itu_present) { @@ -96,6 +99,11 @@ static void mips_cps_realize(DeviceState *dev, Error **errp) 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), saar_present, "saar-present", + &err); + if (saar_present) { + qdev_prop_set_ptr(DEVICE(&s->itu), "saar", (void *)&env->CP0_SAAR); + } object_property_set_bool(OBJECT(&s->itu), true, "realized", &err); if (err != NULL) { error_propagate(errp, err); diff --git a/hw/mips/gt64xxx_pci.c b/hw/mips/gt64xxx_pci.c index 24ad0ad024..f707e59c7a 100644 --- a/hw/mips/gt64xxx_pci.c +++ b/hw/mips/gt64xxx_pci.c @@ -395,7 +395,7 @@ static void gt64120_writel (void *opaque, hwaddr addr, s->regs[GT_CPU] = val; break; case GT_MULTI: - /* Read-only register as only one GT64xxx is present on the CPU bus */ + /* Read-only register as only one GT64xxx is present on the CPU bus */ break; /* CPU Address Decode */ @@ -457,13 +457,13 @@ static void gt64120_writel (void *opaque, hwaddr addr, case GT_CPUERR_DATALO: case GT_CPUERR_DATAHI: case GT_CPUERR_PARITY: - /* Read-only registers, do nothing */ + /* Read-only registers, do nothing */ break; /* CPU Sync Barrier */ case GT_PCI0SYNC: case GT_PCI1SYNC: - /* Read-only registers, do nothing */ + /* Read-only registers, do nothing */ break; /* SDRAM and Device Address Decode */ @@ -992,9 +992,9 @@ static void gt64120_pci_set_irq(void *opaque, int irq_num, int level) } -static void gt64120_reset(void *opaque) +static void gt64120_reset(DeviceState *dev) { - GT64120State *s = opaque; + GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); /* FIXME: Malta specific hw assumptions ahead */ @@ -1184,16 +1184,6 @@ PCIBus *gt64120_register(qemu_irq *pic) return phb->bus; } -static int gt64120_init(SysBusDevice *dev) -{ - GT64120State *s; - - s = GT64120_PCI_HOST_BRIDGE(dev); - - qemu_register_reset(gt64120_reset, s); - return 0; -} - static void gt64120_pci_realize(PCIDevice *d, Error **errp) { /* FIXME: Malta specific hw assumptions ahead */ @@ -1241,9 +1231,9 @@ static const TypeInfo gt64120_pci_info = { static void gt64120_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); - sdc->init = gt64120_init; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->reset = gt64120_reset; dc->vmsd = &vmstate_gt64120; } diff --git a/hw/mips/mips_fulong2e.c b/hw/mips/mips_fulong2e.c index c1694c8254..9d7480ed31 100644 --- a/hw/mips/mips_fulong2e.c +++ b/hw/mips/mips_fulong2e.c @@ -21,13 +21,14 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "cpu.h" #include "hw/hw.h" #include "hw/i386/pc.h" #include "hw/dma/i8257.h" #include "hw/isa/superio.h" #include "net/net.h" #include "hw/boards.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -35,7 +36,6 @@ #include "audio/audio.h" #include "qemu/log.h" #include "hw/loader.h" -#include "hw/mips/bios.h" #include "hw/ide.h" #include "elf.h" #include "hw/isa/vt82c686.h" @@ -51,6 +51,8 @@ #define ENVP_NB_ENTRIES 16 #define ENVP_ENTRY_SIZE 256 +/* fulong 2e has a 512k flash: Winbond W39L040AP70Z */ +#define BIOS_SIZE (512 * KiB) #define MAX_IDE_BUS 2 /* @@ -104,15 +106,16 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, static int64_t load_kernel (CPUMIPSState *env) { - int64_t kernel_entry, kernel_low, kernel_high; + int64_t kernel_entry, kernel_low, kernel_high, initrd_size; int index = 0; - long kernel_size, initrd_size; + long kernel_size; ram_addr_t initrd_offset; uint32_t *prom_buf; long prom_size; - kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, - NULL, (uint64_t *)&kernel_entry, + kernel_size = load_elf(loaderparams.kernel_filename, NULL, + cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low, (uint64_t *)&kernel_high, 0, EM_MIPS, 1, 0); if (kernel_size < 0) { @@ -150,7 +153,7 @@ static int64_t load_kernel (CPUMIPSState *env) prom_set(prom_buf, index++, "%s", loaderparams.kernel_filename); if (initrd_size > 0) { - prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", + prom_set(prom_buf, index++, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { @@ -211,20 +214,6 @@ static void main_cpu_reset(void *opaque) } } -static const uint8_t eeprom_spd[0x80] = { - 0x80,0x08,0x07,0x0d,0x09,0x02,0x40,0x00,0x04,0x70, - 0x70,0x00,0x82,0x10,0x00,0x01,0x0e,0x04,0x0c,0x01, - 0x02,0x20,0x80,0x75,0x70,0x00,0x00,0x50,0x3c,0x50, - 0x2d,0x20,0xb0,0xb0,0x50,0x50,0x00,0x00,0x00,0x00, - 0x00,0x41,0x48,0x3c,0x32,0x75,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,0x9c,0x7b,0x07,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x48,0x42,0x35,0x34,0x41,0x32, - 0x35,0x36,0x38,0x4b,0x4e,0x2d,0x41,0x37,0x35,0x42, - 0x20,0x30,0x20 -}; - static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, I2CBus **i2c_bus, ISABus **p_isa_bus) { @@ -249,7 +238,7 @@ static void vt82c686b_southbridge_init(PCIBus *pci_bus, int slot, qemu_irq intc, isa_create_simple(isa_bus, TYPE_VT82C686B_SUPERIO); ide_drive_get(hd, ARRAY_SIZE(hd)); - vt82c686b_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1)); + via_ide_init(pci_bus, hd, PCI_DEVFN(slot, 1)); pci_create_simple(pci_bus, PCI_DEVFN(slot, 2), "vt82c686b-usb-uhci"); pci_create_simple(pci_bus, PCI_DEVFN(slot, 3), "vt82c686b-usb-uhci"); @@ -281,7 +270,6 @@ static void network_init (PCIBus *pci_bus) static void mips_fulong2e_init(MachineState *machine) { - ram_addr_t ram_size = machine->ram_size; const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; @@ -289,13 +277,17 @@ static void mips_fulong2e_init(MachineState *machine) MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *ram = g_new(MemoryRegion, 1); MemoryRegion *bios = g_new(MemoryRegion, 1); + ram_addr_t ram_size = machine->ram_size; long bios_size; + uint8_t *spd_data; + Error *err = NULL; int64_t kernel_entry; PCIBus *pci_bus; ISABus *isa_bus; I2CBus *smbus; MIPSCPU *cpu; CPUMIPSState *env; + DeviceState *dev; /* init CPUs */ cpu = MIPS_CPU(cpu_create(machine->cpu_type)); @@ -303,15 +295,12 @@ static void mips_fulong2e_init(MachineState *machine) qemu_register_reset(main_cpu_reset, cpu); - /* fulong 2e has 256M ram. */ + /* TODO: support more than 256M RAM as highmem */ ram_size = 256 * MiB; - /* fulong 2e has a 1M flash.Winbond W39L040AP70Z */ - bios_size = 1 * MiB; - /* allocate RAM */ memory_region_allocate_system_memory(ram, NULL, "fulong2e.ram", ram_size); - memory_region_init_ram(bios, NULL, "fulong2e.bios", bios_size, + memory_region_init_ram(bios, NULL, "fulong2e.bios", BIOS_SIZE, &error_fatal); memory_region_set_readonly(bios, true); @@ -359,8 +348,20 @@ static void mips_fulong2e_init(MachineState *machine) vt82c686b_southbridge_init(pci_bus, FULONG2E_VIA_SLOT, env->irq[5], &smbus, &isa_bus); - /* TODO: Populate SPD eeprom data. */ - smbus_eeprom_init(smbus, 1, eeprom_spd, sizeof(eeprom_spd)); + /* GPU */ + dev = DEVICE(pci_create(pci_bus, -1, "ati-vga")); + qdev_prop_set_uint32(dev, "vgamem_mb", 16); + qdev_prop_set_uint16(dev, "x-device-id", 0x5159); + qdev_init_nofail(dev); + + /* Populate SPD eeprom data */ + spd_data = spd_data_generate(DDR, ram_size, &err); + if (err) { + warn_report_err(err); + } + if (spd_data) { + smbus_eeprom_init_one(smbus, 0x50, spd_data); + } mc146818_rtc_init(isa_bus, 2000, NULL); @@ -374,6 +375,7 @@ static void mips_fulong2e_machine_init(MachineClass *mc) mc->init = mips_fulong2e_init; mc->block_default_type = IF_IDE; mc->default_cpu_type = MIPS_CPU_TYPE_NAME("Loongson-2E"); + mc->default_ram_size = 256 * MiB; } DEFINE_MACHINE("fulong2e", mips_fulong2e_machine_init) diff --git a/hw/mips/mips_int.c b/hw/mips/mips_int.c index 48192d22f3..5ddeb15848 100644 --- a/hw/mips/mips_int.c +++ b/hw/mips/mips_int.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/main-loop.h" #include "hw/hw.h" #include "hw/mips/cpudevs.h" #include "cpu.h" @@ -32,10 +33,17 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) MIPSCPU *cpu = opaque; CPUMIPSState *env = &cpu->env; CPUState *cs = CPU(cpu); + bool locked = false; if (irq < 0 || irq > 7) return; + /* Make sure locking works even if BQL is already held by the caller */ + if (!qemu_mutex_iothread_locked()) { + locked = true; + qemu_mutex_lock_iothread(); + } + if (level) { env->CP0_Cause |= 1 << (irq + CP0Ca_IP); @@ -56,6 +64,10 @@ static void cpu_mips_irq_request(void *opaque, int irq, int level) } else { cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); } + + if (locked) { + qemu_mutex_unlock_iothread(); + } } void cpu_mips_irq_init_cpu(MIPSCPU *cpu) diff --git a/hw/mips/mips_malta.c b/hw/mips/mips_malta.c index 3467451482..439665ab45 100644 --- a/hw/mips/mips_malta.c +++ b/hw/mips/mips_malta.c @@ -33,7 +33,7 @@ #include "hw/char/serial.h" #include "net/net.h" #include "hw/boards.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/block/flash.h" #include "hw/mips/mips.h" #include "hw/mips/cpudevs.h" @@ -58,8 +58,6 @@ #include "exec/semihost.h" #include "hw/mips/cps.h" -//#define DEBUG_BOARD_INIT - #define ENVP_ADDR 0x80002000l #define ENVP_NB_ENTRIES 16 #define ENVP_ENTRY_SIZE 256 @@ -568,7 +566,7 @@ static MaltaFPGAState *malta_fpga_init(MemoryRegion *address_space, memory_region_add_subregion(address_space, base, &s->iomem_lo); memory_region_add_subregion(address_space, base + 0xa00, &s->iomem_hi); - chr = qemu_chr_new("fpga", "vc:320x200"); + chr = qemu_chr_new("fpga", "vc:320x200", NULL); qemu_chr_fe_init(&s->display, chr, NULL); qemu_chr_fe_set_handlers(&s->display, NULL, NULL, malta_fgpa_display_event, NULL, s, NULL, true); @@ -599,6 +597,208 @@ static void network_init(PCIBus *pci_bus) } } +static void write_bootloader_nanomips(uint8_t *base, int64_t run_addr, + int64_t kernel_entry) +{ + uint16_t *p; + + /* Small bootloader */ + p = (uint16_t *)base; + +#define NM_HI1(VAL) (((VAL) >> 16) & 0x1f) +#define NM_HI2(VAL) \ + (((VAL) & 0xf000) | (((VAL) >> 19) & 0xffc) | (((VAL) >> 31) & 0x1)) +#define NM_LO(VAL) ((VAL) & 0xfff) + + stw_p(p++, 0x2800); stw_p(p++, 0x001c); + /* bc to_here */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + + /* to_here: */ + if (semihosting_get_argc()) { + /* Preserve a0 content as arguments have been passed */ + stw_p(p++, 0x8000); stw_p(p++, 0xc000); + /* nop */ + } else { + stw_p(p++, 0x0080); stw_p(p++, 0x0002); + /* li a0,2 */ + } + + stw_p(p++, 0xe3a0 | NM_HI1(ENVP_ADDR - 64)); + + stw_p(p++, NM_HI2(ENVP_ADDR - 64)); + /* lui sp,%hi(ENVP_ADDR - 64) */ + + stw_p(p++, 0x83bd); stw_p(p++, NM_LO(ENVP_ADDR - 64)); + /* ori sp,sp,%lo(ENVP_ADDR - 64) */ + + stw_p(p++, 0xe0a0 | NM_HI1(ENVP_ADDR)); + + stw_p(p++, NM_HI2(ENVP_ADDR)); + /* lui a1,%hi(ENVP_ADDR) */ + + stw_p(p++, 0x80a5); stw_p(p++, NM_LO(ENVP_ADDR)); + /* ori a1,a1,%lo(ENVP_ADDR) */ + + stw_p(p++, 0xe0c0 | NM_HI1(ENVP_ADDR + 8)); + + stw_p(p++, NM_HI2(ENVP_ADDR + 8)); + /* lui a2,%hi(ENVP_ADDR + 8) */ + + stw_p(p++, 0x80c6); stw_p(p++, NM_LO(ENVP_ADDR + 8)); + /* ori a2,a2,%lo(ENVP_ADDR + 8) */ + + stw_p(p++, 0xe0e0 | NM_HI1(loaderparams.ram_low_size)); + + stw_p(p++, NM_HI2(loaderparams.ram_low_size)); + /* lui a3,%hi(loaderparams.ram_low_size) */ + + stw_p(p++, 0x80e7); stw_p(p++, NM_LO(loaderparams.ram_low_size)); + /* ori a3,a3,%lo(loaderparams.ram_low_size) */ + + /* + * Load BAR registers as done by YAMON: + * + * - set up PCI0 I/O BARs from 0x18000000 to 0x181fffff + * - set up PCI0 MEM0 at 0x10000000, size 0x8000000 + * - set up PCI0 MEM1 at 0x18200000, size 0xbe00000 + * + */ + stw_p(p++, 0xe040); stw_p(p++, 0x0681); + /* lui t1, %hi(0xb4000000) */ + +#ifdef TARGET_WORDS_BIGENDIAN + + stw_p(p++, 0xe020); stw_p(p++, 0x0be1); + /* lui t0, %hi(0xdf000000) */ + + /* 0x68 corresponds to GT_ISD (from hw/mips/gt64xxx_pci.c) */ + stw_p(p++, 0x8422); stw_p(p++, 0x9068); + /* sw t0, 0x68(t1) */ + + stw_p(p++, 0xe040); stw_p(p++, 0x077d); + /* lui t1, %hi(0xbbe00000) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0801); + /* lui t0, %hi(0xc0000000) */ + + /* 0x48 corresponds to GT_PCI0IOLD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9048); + /* sw t0, 0x48(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0800); + /* lui t0, %hi(0x40000000) */ + + /* 0x50 corresponds to GT_PCI0IOHD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9050); + /* sw t0, 0x50(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0001); + /* lui t0, %hi(0x80000000) */ + + /* 0x58 corresponds to GT_PCI0M0LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9058); + /* sw t0, 0x58(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x07e0); + /* lui t0, %hi(0x3f000000) */ + + /* 0x60 corresponds to GT_PCI0M0HD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9060); + /* sw t0, 0x60(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0821); + /* lui t0, %hi(0xc1000000) */ + + /* 0x80 corresponds to GT_PCI0M1LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9080); + /* sw t0, 0x80(t1) */ + + stw_p(p++, 0xe020); stw_p(p++, 0x0bc0); + /* lui t0, %hi(0x5e000000) */ + +#else + + stw_p(p++, 0x0020); stw_p(p++, 0x00df); + /* addiu[32] t0, $0, 0xdf */ + + /* 0x68 corresponds to GT_ISD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9068); + /* sw t0, 0x68(t1) */ + + /* Use kseg2 remapped address 0x1be00000 */ + stw_p(p++, 0xe040); stw_p(p++, 0x077d); + /* lui t1, %hi(0xbbe00000) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x00c0); + /* addiu[32] t0, $0, 0xc0 */ + + /* 0x48 corresponds to GT_PCI0IOLD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9048); + /* sw t0, 0x48(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x0040); + /* addiu[32] t0, $0, 0x40 */ + + /* 0x50 corresponds to GT_PCI0IOHD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9050); + /* sw t0, 0x50(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x0080); + /* addiu[32] t0, $0, 0x80 */ + + /* 0x58 corresponds to GT_PCI0M0LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9058); + /* sw t0, 0x58(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x003f); + /* addiu[32] t0, $0, 0x3f */ + + /* 0x60 corresponds to GT_PCI0M0HD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9060); + /* sw t0, 0x60(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x00c1); + /* addiu[32] t0, $0, 0xc1 */ + + /* 0x80 corresponds to GT_PCI0M1LD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9080); + /* sw t0, 0x80(t1) */ + + stw_p(p++, 0x0020); stw_p(p++, 0x005e); + /* addiu[32] t0, $0, 0x5e */ + +#endif + + /* 0x88 corresponds to GT_PCI0M1HD */ + stw_p(p++, 0x8422); stw_p(p++, 0x9088); + /* sw t0, 0x88(t1) */ + + stw_p(p++, 0xe320 | NM_HI1(kernel_entry)); + + stw_p(p++, NM_HI2(kernel_entry)); + /* lui t9,%hi(kernel_entry) */ + + stw_p(p++, 0x8339); stw_p(p++, NM_LO(kernel_entry)); + /* ori t9,t9,%lo(kernel_entry) */ + + stw_p(p++, 0x4bf9); stw_p(p++, 0x0000); + /* jalrc t8 */ +} + /* ROM and pseudo bootloader The following code implements a very very simple bootloader. It first @@ -620,7 +820,6 @@ static void network_init(PCIBus *pci_bus) a2 - 32-bit address of the environment variables table a3 - RAM size in bytes */ - static void write_bootloader(uint8_t *base, int64_t run_addr, int64_t kernel_entry) { @@ -794,8 +993,8 @@ static void GCC_FMT_ATTR(3, 4) prom_set(uint32_t* prom_buf, int index, /* Kernel */ static int64_t load_kernel (void) { - int64_t kernel_entry, kernel_high; - long kernel_size, initrd_size; + int64_t kernel_entry, kernel_high, initrd_size; + long kernel_size; ram_addr_t initrd_offset; int big_endian; uint32_t *prom_buf; @@ -809,8 +1008,9 @@ static int64_t load_kernel (void) big_endian = 0; #endif - kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, - NULL, (uint64_t *)&kernel_entry, NULL, + kernel_size = load_elf(loaderparams.kernel_filename, NULL, + cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high, big_endian, EM_MIPS, 1, 0); if (kernel_size < 0) { error_report("could not load kernel '%s': %s", @@ -869,7 +1069,7 @@ static int64_t load_kernel (void) prom_set(prom_buf, prom_index++, "%s", loaderparams.kernel_filename); if (initrd_size > 0) { - prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s", + prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", xlate_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { @@ -987,13 +1187,12 @@ void mips_malta_init(MachineState *machine) const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; char *filename; - pflash_t *fl; + PFlashCFI01 *fl; MemoryRegion *system_memory = get_system_memory(); MemoryRegion *ram_high = g_new(MemoryRegion, 1); MemoryRegion *ram_low_preio = g_new(MemoryRegion, 1); MemoryRegion *ram_low_postio; MemoryRegion *bios, *bios_copy = g_new(MemoryRegion, 1); - target_long bios_size = FLASH_SIZE; const size_t smbus_eeprom_size = 8 * 256; uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); int64_t kernel_entry, bootloader_run_addr; @@ -1006,7 +1205,6 @@ void mips_malta_init(MachineState *machine) DriveInfo *dinfo; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; int fl_idx = 0; - int fl_sectors = bios_size >> 16; int be; DeviceState *dev = qdev_create(NULL, TYPE_MIPS_MALTA); @@ -1063,18 +1261,10 @@ void mips_malta_init(MachineState *machine) /* Load firmware in flash / BIOS. */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); -#ifdef DEBUG_BOARD_INIT - if (dinfo) { - printf("Register parallel flash %d size " TARGET_FMT_lx " at " - "addr %08llx '%s' %x\n", - fl_idx, bios_size, FLASH_ADDRESS, - blk_name(dinfo->bdrv), fl_sectors); - } -#endif - fl = pflash_cfi01_register(FLASH_ADDRESS, NULL, "mips_malta.bios", - BIOS_SIZE, + fl = pflash_cfi01_register(FLASH_ADDRESS, "mips_malta.bios", + FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 65536, fl_sectors, + 65536, 4, 0x0000, 0x0000, 0x0000, 0x0000, be); bios = pflash_cfi01_get_memory(fl); fl_idx++; @@ -1096,8 +1286,13 @@ void mips_malta_init(MachineState *machine) loaderparams.initrd_filename = initrd_filename; kernel_entry = load_kernel(); - write_bootloader(memory_region_get_ram_ptr(bios), - bootloader_run_addr, kernel_entry); + if (!cpu_supports_isa(machine->cpu_type, ISA_NANOMIPS32)) { + write_bootloader(memory_region_get_ram_ptr(bios), + bootloader_run_addr, kernel_entry); + } else { + write_bootloader_nanomips(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(memory_region_get_ram_ptr(ram_low_preio) + @@ -1105,6 +1300,7 @@ void mips_malta_init(MachineState *machine) bootloader_run_addr, kernel_entry); } } else { + target_long bios_size = FLASH_SIZE; /* The flash region isn't executable from a KVM guest */ if (kvm_enabled()) { error_report("KVM enabled but no -kernel argument was specified. " @@ -1216,23 +1412,10 @@ void mips_malta_init(MachineState *machine) pci_vga_init(pci_bus); } -static int mips_malta_sysbus_device_init(SysBusDevice *sysbusdev) -{ - return 0; -} - -static void mips_malta_class_init(ObjectClass *klass, void *data) -{ - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - - k->init = mips_malta_sysbus_device_init; -} - static const TypeInfo mips_malta_device = { .name = TYPE_MIPS_MALTA, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(MaltaState), - .class_init = mips_malta_class_init, }; static void mips_malta_machine_init(MachineClass *mc) diff --git a/hw/mips/mips_mipssim.c b/hw/mips/mips_mipssim.c index 241faa1d0f..824abda657 100644 --- a/hw/mips/mips_mipssim.c +++ b/hw/mips/mips_mipssim.c @@ -58,9 +58,8 @@ typedef struct ResetData { static int64_t load_kernel(void) { - int64_t entry, kernel_high; + int64_t entry, kernel_high, initrd_size; long kernel_size; - long initrd_size; ram_addr_t initrd_offset; int big_endian; @@ -70,8 +69,9 @@ static int64_t load_kernel(void) big_endian = 0; #endif - kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, - NULL, (uint64_t *)&entry, NULL, + kernel_size = load_elf(loaderparams.kernel_filename, NULL, + cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, big_endian, EM_MIPS, 1, 0); if (kernel_size >= 0) { diff --git a/hw/mips/mips_r4k.c b/hw/mips/mips_r4k.c index d5725d0555..93dbf76bb4 100644 --- a/hw/mips/mips_r4k.c +++ b/hw/mips/mips_r4k.c @@ -81,8 +81,8 @@ typedef struct ResetData { static int64_t load_kernel(void) { const size_t params_size = 264; - int64_t entry, kernel_high; - long kernel_size, initrd_size; + int64_t entry, kernel_high, initrd_size; + long kernel_size; ram_addr_t initrd_offset; uint32_t *params_buf; int big_endian; @@ -92,8 +92,9 @@ static int64_t load_kernel(void) #else big_endian = 0; #endif - kernel_size = load_elf(loaderparams.kernel_filename, cpu_mips_kseg0_to_phys, - NULL, (uint64_t *)&entry, NULL, + kernel_size = load_elf(loaderparams.kernel_filename, NULL, + cpu_mips_kseg0_to_phys, NULL, + (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high, big_endian, EM_MIPS, 1, 0); if (kernel_size >= 0) { @@ -136,7 +137,7 @@ static int64_t load_kernel(void) params_buf[1] = tswap32(0x12345678); if (initrd_size > 0) { - snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s", + snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%" PRId64 " %s", cpu_mips_phys_to_kseg0(NULL, initrd_offset), initrd_size, loaderparams.kernel_cmdline); } else { @@ -234,12 +235,11 @@ void mips_r4k_init(MachineState *machine) load_image_targphys(filename, 0x1fc00000, BIOS_SIZE); } else if ((dinfo = drive_get(IF_PFLASH, 0, 0)) != NULL) { uint32_t mips_rom = 0x00400000; - if (!pflash_cfi01_register(0x1fc00000, NULL, "mips_r4k.bios", mips_rom, + if (!pflash_cfi01_register(0x1fc00000, "mips_r4k.bios", mips_rom, blk_by_legacy_dinfo(dinfo), - sector_len, mips_rom / sector_len, - 4, 0, 0, 0, 0, be)) { + sector_len, 4, 0, 0, 0, 0, be)) { fprintf(stderr, "qemu: Error registering flash memory.\n"); - } + } } else if (!qtest_enabled()) { /* not fatal */ warn_report("could not load MIPS bios '%s'", bios_name); @@ -285,7 +285,7 @@ void mips_r4k_init(MachineState *machine) for(i = 0; i < MAX_IDE_BUS; i++) isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[MAX_IDE_DEVS * i], - hd[MAX_IDE_DEVS * i + 1]); + hd[MAX_IDE_DEVS * i + 1]); isa_create_simple(isa_bus, TYPE_I8042); } diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig new file mode 100644 index 0000000000..5f67d0d6d9 --- /dev/null +++ b/hw/misc/Kconfig @@ -0,0 +1,118 @@ +config APPLESMC + bool + depends on ISA_BUS + +config MAX111X + bool + +config TMP105 + bool + depends on I2C + +config TMP421 + bool + depends on I2C + +config ISA_DEBUG + bool + depends on ISA_BUS + +config SGA + bool + depends on ISA_BUS + +config ISA_TESTDEV + bool + default y if TEST_DEVICES + depends on ISA_BUS + +config PCI_TESTDEV + bool + default y if TEST_DEVICES + depends on PCI + +config EDU + bool + default y if TEST_DEVICES + depends on PCI && MSI_NONBROKEN + +config PCA9552 + bool + depends on I2C + +config PL310 + bool + +config INTEGRATOR_DEBUG + bool + +config A9SCU + bool + +config ARM11SCU + bool + +config MOS6522 + bool + +config MACIO + bool + select CUDA + select ESCC + select IDE_MACIO + select MAC_DBDMA + select MAC_NVRAM + select MOS6522 + +config IVSHMEM_DEVICE + bool + default y if PCI_DEVICES + depends on PCI && LINUX && IVSHMEM && MSI_NONBROKEN + +config ECCMEMCTL + bool + select ECC + +config IMX + bool + select PTIMER + +config STM32F2XX_SYSCFG + bool + +config MIPS_ITU + bool + +config MPS2_FPGAIO + bool + +config MPS2_SCC + bool + +config TZ_MPC + bool + +config TZ_MSC + bool + +config TZ_PPC + bool + +config IOTKIT_SECCTL + bool + +config IOTKIT_SYSCTL + bool + +config IOTKIT_SYSINFO + bool + +config PVPANIC + bool + depends on ISA_BUS + +config AUX + bool + select I2C + +source macio/Kconfig diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs index 9350900845..c71e07ae35 100644 --- a/hw/misc/Makefile.objs +++ b/hw/misc/Makefile.objs @@ -36,6 +36,7 @@ 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_IMX) += imx6ul_ccm.o obj-$(CONFIG_IMX) += imx6_src.o obj-$(CONFIG_IMX) += imx7_ccm.o obj-$(CONFIG_IMX) += imx2_wdt.o @@ -63,12 +64,16 @@ obj-$(CONFIG_MPS2_FPGAIO) += mps2-fpgaio.o obj-$(CONFIG_MPS2_SCC) += mps2-scc.o obj-$(CONFIG_TZ_MPC) += tz-mpc.o +obj-$(CONFIG_TZ_MSC) += tz-msc.o obj-$(CONFIG_TZ_PPC) += tz-ppc.o obj-$(CONFIG_IOTKIT_SECCTL) += iotkit-secctl.o +obj-$(CONFIG_IOTKIT_SYSCTL) += iotkit-sysctl.o +obj-$(CONFIG_IOTKIT_SYSINFO) += iotkit-sysinfo.o +obj-$(CONFIG_ARMSSE_CPUID) += armsse-cpuid.o +obj-$(CONFIG_ARMSSE_MHU) += armsse-mhu.o obj-$(CONFIG_PVPANIC) += pvpanic.o -obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o obj-$(CONFIG_AUX) += auxbus.o obj-$(CONFIG_ASPEED_SOC) += aspeed_scu.o aspeed_sdmc.o -obj-y += mmio_interface.o obj-$(CONFIG_MSF2) += msf2-sysreg.o +obj-$(CONFIG_NRF51_SOC) += nrf51_rng.o diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c new file mode 100644 index 0000000000..7788f6ced6 --- /dev/null +++ b/hw/misc/armsse-cpuid.c @@ -0,0 +1,134 @@ +/* + * ARM SSE-200 CPU_IDENTITY register block + * + * Copyright (c) 2019 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "CPU_IDENTITY" register block which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * + * It consists of one read-only CPUID register (set by QOM property), plus the + * usual ID registers. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/armsse-cpuid.h" + +REG32(CPUID, 0x0) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysinfo_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t armsse_cpuid_read(void *opaque, hwaddr offset, + unsigned size) +{ + ARMSSECPUID *s = ARMSSE_CPUID(opaque); + uint64_t r; + + switch (offset) { + case A_CPUID: + r = s->cpuid; + break; + case A_PID4 ... A_CID3: + r = sysinfo_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE CPU_IDENTITY read: bad offset 0x%x\n", (int)offset); + r = 0; + break; + } + trace_armsse_cpuid_read(offset, r, size); + return r; +} + +static void armsse_cpuid_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + trace_armsse_cpuid_write(offset, value, size); + + qemu_log_mask(LOG_GUEST_ERROR, + "SSE CPU_IDENTITY: write to RO offset 0x%x\n", (int)offset); +} + +static const MemoryRegionOps armsse_cpuid_ops = { + .read = armsse_cpuid_read, + .write = armsse_cpuid_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static Property armsse_cpuid_props[] = { + DEFINE_PROP_UINT32("CPUID", ARMSSECPUID, cpuid, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void armsse_cpuid_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMSSECPUID *s = ARMSSE_CPUID(obj); + + memory_region_init_io(&s->iomem, obj, &armsse_cpuid_ops, + s, "armsse-cpuid", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void armsse_cpuid_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * This device has no guest-modifiable state and so it + * does not need a reset function or VMState. + */ + + dc->props = armsse_cpuid_props; +} + +static const TypeInfo armsse_cpuid_info = { + .name = TYPE_ARMSSE_CPUID, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSECPUID), + .instance_init = armsse_cpuid_init, + .class_init = armsse_cpuid_class_init, +}; + +static void armsse_cpuid_register_types(void) +{ + type_register_static(&armsse_cpuid_info); +} + +type_init(armsse_cpuid_register_types); diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c new file mode 100644 index 0000000000..9ebca32e9a --- /dev/null +++ b/hw/misc/armsse-mhu.c @@ -0,0 +1,198 @@ +/* + * ARM SSE-200 Message Handling Unit (MHU) + * + * Copyright (c) 2019 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the Message Handling Unit (MHU) which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/armsse-mhu.h" + +REG32(CPU0INTR_STAT, 0x0) +REG32(CPU0INTR_SET, 0x4) +REG32(CPU0INTR_CLR, 0x8) +REG32(CPU1INTR_STAT, 0x10) +REG32(CPU1INTR_SET, 0x14) +REG32(CPU1INTR_CLR, 0x18) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* Valid bits in the interrupt registers. If any are set the IRQ is raised */ +#define INTR_MASK 0xf + +/* PID/CID values */ +static const int armsse_mhu_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x56, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static void armsse_mhu_update(ARMSSEMHU *s) +{ + qemu_set_irq(s->cpu0irq, s->cpu0intr != 0); + qemu_set_irq(s->cpu1irq, s->cpu1intr != 0); +} + +static uint64_t armsse_mhu_read(void *opaque, hwaddr offset, unsigned size) +{ + ARMSSEMHU *s = ARMSSE_MHU(opaque); + uint64_t r; + + switch (offset) { + case A_CPU0INTR_STAT: + r = s->cpu0intr; + break; + + case A_CPU1INTR_STAT: + r = s->cpu1intr; + break; + + case A_PID4 ... A_CID3: + r = armsse_mhu_id[(offset - A_PID4) / 4]; + break; + + case A_CPU0INTR_SET: + case A_CPU0INTR_CLR: + case A_CPU1INTR_SET: + case A_CPU1INTR_CLR: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU: read of write-only register at offset 0x%x\n", + (int)offset); + r = 0; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU read: bad offset 0x%x\n", (int)offset); + r = 0; + break; + } + trace_armsse_mhu_read(offset, r, size); + return r; +} + +static void armsse_mhu_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + ARMSSEMHU *s = ARMSSE_MHU(opaque); + + trace_armsse_mhu_write(offset, value, size); + + switch (offset) { + case A_CPU0INTR_SET: + s->cpu0intr |= (value & INTR_MASK); + break; + case A_CPU0INTR_CLR: + s->cpu0intr &= ~(value & INTR_MASK); + break; + case A_CPU1INTR_SET: + s->cpu1intr |= (value & INTR_MASK); + break; + case A_CPU1INTR_CLR: + s->cpu1intr &= ~(value & INTR_MASK); + break; + + case A_CPU0INTR_STAT: + case A_CPU1INTR_STAT: + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU: write to read-only register at offset 0x%x\n", + (int)offset); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "SSE MHU write: bad offset 0x%x\n", (int)offset); + break; + } + + armsse_mhu_update(s); +} + +static const MemoryRegionOps armsse_mhu_ops = { + .read = armsse_mhu_read, + .write = armsse_mhu_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static void armsse_mhu_reset(DeviceState *dev) +{ + ARMSSEMHU *s = ARMSSE_MHU(dev); + + s->cpu0intr = 0; + s->cpu1intr = 0; +} + +static const VMStateDescription armsse_mhu_vmstate = { + .name = "armsse-mhu", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(cpu0intr, ARMSSEMHU), + VMSTATE_UINT32(cpu1intr, ARMSSEMHU), + VMSTATE_END_OF_LIST() + }, +}; + +static void armsse_mhu_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + ARMSSEMHU *s = ARMSSE_MHU(obj); + + memory_region_init_io(&s->iomem, obj, &armsse_mhu_ops, + s, "armsse-mhu", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->cpu0irq); + sysbus_init_irq(sbd, &s->cpu1irq); +} + +static void armsse_mhu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = armsse_mhu_reset; + dc->vmsd = &armsse_mhu_vmstate; +} + +static const TypeInfo armsse_mhu_info = { + .name = TYPE_ARMSSE_MHU, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(ARMSSEMHU), + .instance_init = armsse_mhu_init, + .class_init = armsse_mhu_class_init, +}; + +static void armsse_mhu_register_types(void) +{ + type_register_static(&armsse_mhu_info); +} + +type_init(armsse_mhu_register_types); diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index 0df008e52a..eec77f2435 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -23,6 +23,14 @@ /* Configuration Register */ #define R_CONF (0x04 / 4) +/* Control/Status Register #1 (ast2500) */ +#define R_STATUS1 (0x60 / 4) +#define PHY_BUSY_STATE BIT(0) + +#define R_ECC_TEST_CTRL (0x70 / 4) +#define ECC_TEST_FINISHED BIT(12) +#define ECC_TEST_FAIL BIT(13) + /* * Configuration register Ox4 (for Aspeed AST2400 SOC) * @@ -126,15 +134,33 @@ static void aspeed_sdmc_write(void *opaque, hwaddr addr, uint64_t data, case AST2400_A0_SILICON_REV: case AST2400_A1_SILICON_REV: data &= ~ASPEED_SDMC_READONLY_MASK; + data |= s->fixed_conf; break; case AST2500_A0_SILICON_REV: case AST2500_A1_SILICON_REV: data &= ~ASPEED_SDMC_AST2500_READONLY_MASK; + data |= s->fixed_conf; break; default: g_assert_not_reached(); } } + if (s->silicon_rev == AST2500_A0_SILICON_REV || + s->silicon_rev == AST2500_A1_SILICON_REV) { + switch (addr) { + case R_STATUS1: + /* Will never return 'busy' */ + data &= ~PHY_BUSY_STATE; + break; + case R_ECC_TEST_CTRL: + /* Always done, always happy */ + data |= ECC_TEST_FINISHED; + data &= ~ECC_TEST_FAIL; + break; + default: + break; + } + } s->regs[addr] = data; } @@ -198,25 +224,7 @@ static void aspeed_sdmc_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); /* Set ram size bit and defaults values */ - switch (s->silicon_rev) { - case AST2400_A0_SILICON_REV: - case AST2400_A1_SILICON_REV: - s->regs[R_CONF] |= - ASPEED_SDMC_VGA_COMPAT | - ASPEED_SDMC_DRAM_SIZE(s->ram_bits); - break; - - case AST2500_A0_SILICON_REV: - case AST2500_A1_SILICON_REV: - s->regs[R_CONF] |= - ASPEED_SDMC_HW_VERSION(1) | - ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) | - ASPEED_SDMC_DRAM_SIZE(s->ram_bits); - break; - - default: - g_assert_not_reached(); - } + s->regs[R_CONF] = s->fixed_conf; } static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) @@ -234,10 +242,18 @@ static void aspeed_sdmc_realize(DeviceState *dev, Error **errp) case AST2400_A0_SILICON_REV: case AST2400_A1_SILICON_REV: s->ram_bits = ast2400_rambits(s); + s->max_ram_size = 512 << 20; + s->fixed_conf = ASPEED_SDMC_VGA_COMPAT | + ASPEED_SDMC_DRAM_SIZE(s->ram_bits); break; case AST2500_A0_SILICON_REV: case AST2500_A1_SILICON_REV: s->ram_bits = ast2500_rambits(s); + s->max_ram_size = 1024 << 20; + s->fixed_conf = ASPEED_SDMC_HW_VERSION(1) | + ASPEED_SDMC_VGA_APERTURE(ASPEED_SDMC_VGA_64MB) | + ASPEED_SDMC_CACHE_INITIAL_DONE | + ASPEED_SDMC_DRAM_SIZE(s->ram_bits); break; default: g_assert_not_reached(); @@ -261,6 +277,7 @@ static const VMStateDescription vmstate_aspeed_sdmc = { static Property aspeed_sdmc_properties[] = { DEFINE_PROP_UINT32("silicon-rev", AspeedSDMCState, silicon_rev, 0), DEFINE_PROP_UINT64("ram-size", AspeedSDMCState, ram_size, 0), + DEFINE_PROP_UINT64("max-ram-size", AspeedSDMCState, max_ram_size, 0), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 70eaafd325..145427ae0f 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -21,11 +21,14 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) uint32_t tmp; int n; uint32_t offset, length, color; - uint32_t xres, yres, xoffset, yoffset, bpp, pixo, alpha; - uint32_t tmp_xres, tmp_yres, tmp_xoffset, tmp_yoffset; - uint32_t tmp_bpp, tmp_pixo, tmp_alpha; - uint32_t *newxres = NULL, *newyres = NULL, *newxoffset = NULL, - *newyoffset = NULL, *newbpp = NULL, *newpixo = NULL, *newalpha = NULL; + + /* + * Copy the current state of the framebuffer config; we will update + * this copy as we process tags and then ask the framebuffer to use + * it at the end. + */ + BCM2835FBConfig fbconfig = s->fbdev->config; + bool fbconfig_updated = false; value &= ~0xf; @@ -141,12 +144,9 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) /* Frame buffer */ case 0x00040001: /* Allocate buffer */ - stl_le_phys(&s->dma_as, value + 12, s->fbdev->base); - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; + stl_le_phys(&s->dma_as, value + 12, fbconfig.base); stl_le_phys(&s->dma_as, value + 16, - tmp_xres * tmp_yres * tmp_bpp / 8); + bcm2835_fb_get_size(&fbconfig)); resplen = 8; break; case 0x00048001: /* Release buffer */ @@ -155,86 +155,85 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) case 0x00040002: /* Blank screen */ resplen = 4; break; - case 0x00040003: /* Get display width/height */ - case 0x00040004: - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_yres = newyres != NULL ? *newyres : s->fbdev->yres; - stl_le_phys(&s->dma_as, value + 12, tmp_xres); - stl_le_phys(&s->dma_as, value + 16, tmp_yres); + case 0x00044003: /* Test physical display width/height */ + case 0x00044004: /* Test virtual display width/height */ resplen = 8; break; - case 0x00044003: /* Test display width/height */ - case 0x00044004: + case 0x00048003: /* Set physical display width/height */ + fbconfig.xres = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040003: /* Get physical display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres); 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; + case 0x00048004: /* Set virtual display width/height */ + fbconfig.xres_virtual = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yres_virtual = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040004: /* Get virtual display width/height */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xres_virtual); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yres_virtual); resplen = 8; break; - case 0x00040005: /* Get depth */ - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; - stl_le_phys(&s->dma_as, value + 12, tmp_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 */ - tmp_pixo = newpixo != NULL ? *newpixo : s->fbdev->pixo; - stl_le_phys(&s->dma_as, value + 12, tmp_pixo); + fbconfig.bpp = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040005: /* Get depth */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.bpp); 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 */ - tmp_alpha = newalpha != NULL ? *newalpha : s->fbdev->alpha; - stl_le_phys(&s->dma_as, value + 12, tmp_alpha); + fbconfig.pixo = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040006: /* Get pixel order */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.pixo); 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 = α + fbconfig.alpha = ldl_le_phys(&s->dma_as, value + 12); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040007: /* Get alpha */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.alpha); resplen = 4; break; case 0x00040008: /* Get pitch */ - tmp_xres = newxres != NULL ? *newxres : s->fbdev->xres; - tmp_bpp = newbpp != NULL ? *newbpp : s->fbdev->bpp; - stl_le_phys(&s->dma_as, value + 12, tmp_xres * tmp_bpp / 8); + stl_le_phys(&s->dma_as, value + 12, + bcm2835_fb_get_pitch(&fbconfig)); resplen = 4; break; - case 0x00040009: /* Get virtual offset */ - tmp_xoffset = newxoffset != NULL ? *newxoffset : s->fbdev->xoffset; - tmp_yoffset = newyoffset != NULL ? *newyoffset : s->fbdev->yoffset; - stl_le_phys(&s->dma_as, value + 12, tmp_xoffset); - stl_le_phys(&s->dma_as, value + 16, tmp_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; + fbconfig.xoffset = ldl_le_phys(&s->dma_as, value + 12); + fbconfig.yoffset = ldl_le_phys(&s->dma_as, value + 16); + bcm2835_fb_validate_config(&fbconfig); + fbconfig_updated = true; + /* fall through */ + case 0x00040009: /* Get virtual offset */ + stl_le_phys(&s->dma_as, value + 12, fbconfig.xoffset); + stl_le_phys(&s->dma_as, value + 16, fbconfig.yoffset); resplen = 8; break; case 0x0004000a: /* Get/Test/Set overscan */ @@ -285,10 +284,8 @@ static void bcm2835_property_mbox_push(BCM2835PropertyState *s, uint32_t value) } /* Reconfigure framebuffer if required */ - if (newxres || newyres || newxoffset || newyoffset || newbpp || newpixo - || newalpha) { - bcm2835_fb_reconfigure(s->fbdev, newxres, newyres, newxoffset, - newyoffset, newbpp, newpixo, newalpha); + if (fbconfig_updated) { + bcm2835_fb_reconfigure(s->fbdev, &fbconfig); } /* Buffer response code */ diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index 84fa1a5b9d..bed293247e 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -23,6 +23,11 @@ typedef struct ISADebugExitState { MemoryRegion io; } ISADebugExitState; +static uint64_t debug_exit_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { @@ -30,6 +35,7 @@ static void debug_exit_write(void *opaque, hwaddr addr, uint64_t val, } static const MemoryRegionOps debug_exit_ops = { + .read = debug_exit_read, .write = debug_exit_write, .valid.min_access_size = 1, .valid.max_access_size = 4, diff --git a/hw/misc/edu.c b/hw/misc/edu.c index df26a4d046..91af452c9e 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -30,7 +30,8 @@ #include "qemu/main-loop.h" /* iothread mutex */ #include "qapi/visitor.h" -#define EDU(obj) OBJECT_CHECK(EduState, obj, "edu") +#define TYPE_PCI_EDU_DEVICE "edu" +#define EDU(obj) OBJECT_CHECK(EduState, obj, TYPE_PCI_EDU_DEVICE) #define FACT_IRQ 0x00000001 #define DMA_IRQ 0x00000100 @@ -341,7 +342,7 @@ static void *edu_fact_thread(void *opaque) static void pci_edu_realize(PCIDevice *pdev, Error **errp) { - EduState *edu = DO_UPCAST(EduState, pdev, pdev); + EduState *edu = EDU(pdev); uint8_t *pci_conf = pdev->config; pci_config_set_interrupt_pin(pci_conf, 1); @@ -364,7 +365,7 @@ static void pci_edu_realize(PCIDevice *pdev, Error **errp) static void pci_edu_uninit(PCIDevice *pdev) { - EduState *edu = DO_UPCAST(EduState, pdev, pdev); + EduState *edu = EDU(pdev); qemu_mutex_lock(&edu->thr_mutex); edu->stopping = true; @@ -376,6 +377,7 @@ static void pci_edu_uninit(PCIDevice *pdev) qemu_mutex_destroy(&edu->thr_mutex); timer_del(&edu->dma_timer); + msi_uninit(pdev); } static void edu_obj_uint64(Object *obj, Visitor *v, const char *name, @@ -397,6 +399,7 @@ static void edu_instance_init(Object *obj) static void edu_class_init(ObjectClass *class, void *data) { + DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *k = PCI_DEVICE_CLASS(class); k->realize = pci_edu_realize; @@ -405,6 +408,7 @@ static void edu_class_init(ObjectClass *class, void *data) k->device_id = 0x11e8; k->revision = 0x10; k->class_id = PCI_CLASS_OTHERS; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); } static void pci_edu_register_types(void) @@ -414,7 +418,7 @@ static void pci_edu_register_types(void) { }, }; static const TypeInfo edu_info = { - .name = "edu", + .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EduState), .instance_init = edu_instance_init, diff --git a/hw/misc/hyperv_testdev.c b/hw/misc/hyperv_testdev.c deleted file mode 100644 index bf6bbfa8cf..0000000000 --- a/hw/misc/hyperv_testdev.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * 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 -#include "hw/hw.h" -#include "hw/qdev.h" -#include "hw/isa/isa.h" -#include "sysemu/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 vp_index, - 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->vp_index == vp_index && - sint_route->sint == sint) { - return i; - } - } - return -1; -} - -static void hv_synic_test_dev_control(HypervTestDev *dev, uint32_t ctl, - uint32_t vp_index, 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(vp_index, 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, vp_index, 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, vp_index, 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 vp_index = (data >> 8ULL) & 0xFF; - hv_synic_test_dev_control(dev, ctl, vp_index, 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/hw/misc/imx6ul_ccm.c b/hw/misc/imx6ul_ccm.c new file mode 100644 index 0000000000..90bc374271 --- /dev/null +++ b/hw/misc/imx6ul_ccm.c @@ -0,0 +1,886 @@ +/* + * IMX6UL Clock Control Module + * + * Copyright (c) 2018 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/registerfields.h" +#include "hw/misc/imx6ul_ccm.h" +#include "qemu/log.h" + +#include "trace.h" + +static const char *imx6ul_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 const char *imx6ul_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_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_imx6ul_ccm = { + .name = TYPE_IMX6UL_CCM, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(ccm, IMX6ULCCMState, CCM_MAX), + VMSTATE_UINT32_ARRAY(analog, IMX6ULCCMState, CCM_ANALOG_MAX), + VMSTATE_END_OF_LIST() + }, +}; + +static uint64_t imx6ul_analog_get_osc_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = CKIH_FREQ; + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_analog_get_pll2_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = imx6ul_analog_get_osc_clk(dev); + + if (FIELD_EX32(dev->analog[CCM_ANALOG_PLL_SYS], + ANALOG_PLL_SYS, DIV_SELECT)) { + freq *= 22; + } else { + freq *= 20; + } + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_analog_get_pll3_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = imx6ul_analog_get_osc_clk(dev) * 20; + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_analog_get_pll2_pfd0_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6ul_analog_get_pll2_clk(dev) * 18 + / FIELD_EX32(dev->analog[CCM_ANALOG_PFD_528], + ANALOG_PFD_528, PFD0_FRAC); + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_analog_get_pll2_pfd2_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6ul_analog_get_pll2_clk(dev) * 18 + / FIELD_EX32(dev->analog[CCM_ANALOG_PFD_528], + ANALOG_PFD_528, PFD2_FRAC); + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_analog_pll2_bypass_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_periph_clk2_sel_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + switch (FIELD_EX32(dev->ccm[CCM_CBCMR], CBCMR, PERIPH_CLK2_SEL)) { + case 0: + freq = imx6ul_analog_get_pll3_clk(dev); + break; + case 1: + freq = imx6ul_analog_get_osc_clk(dev); + break; + case 2: + freq = imx6ul_analog_pll2_bypass_clk(dev); + break; + case 3: + /* We should never get there as 3 is a reserved value */ + qemu_log_mask(LOG_GUEST_ERROR, + "[%s]%s: unsupported PERIPH_CLK2_SEL value 3\n", + TYPE_IMX6UL_CCM, __func__); + /* freq is set to 0 as we don't know what it should be */ + break; + default: + g_assert_not_reached(); + } + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_periph_clk_sel_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + switch (FIELD_EX32(dev->ccm[CCM_CBCMR], CBCMR, PRE_PERIPH_CLK_SEL)) { + case 0: + freq = imx6ul_analog_get_pll2_clk(dev); + break; + case 1: + freq = imx6ul_analog_get_pll2_pfd2_clk(dev); + break; + case 2: + freq = imx6ul_analog_get_pll2_pfd0_clk(dev); + break; + case 3: + freq = imx6ul_analog_get_pll2_pfd2_clk(dev) / 2; + break; + default: + g_assert_not_reached(); + } + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_periph_clk2_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6ul_ccm_get_periph_clk2_sel_clk(dev) + / (1 + FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, PERIPH_CLK2_PODF)); + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_periph_sel_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + switch (FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, PERIPH_CLK_SEL)) { + case 0: + freq = imx6ul_ccm_get_periph_clk_sel_clk(dev); + break; + case 1: + freq = imx6ul_ccm_get_periph_clk2_clk(dev); + break; + default: + g_assert_not_reached(); + } + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_ahb_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6ul_ccm_get_periph_sel_clk(dev) + / (1 + FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, AHB_PODF)); + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_ipg_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6ul_ccm_get_ahb_clk(dev) + / (1 + FIELD_EX32(dev->ccm[CCM_CBCDR], CBCDR, IPG_PODF)); + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_per_sel_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + switch (FIELD_EX32(dev->ccm[CCM_CSCMR1], CSCMR1, PERCLK_CLK_SEL)) { + case 0: + freq = imx6ul_ccm_get_ipg_clk(dev); + break; + case 1: + freq = imx6ul_analog_get_osc_clk(dev); + break; + default: + g_assert_not_reached(); + } + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint64_t imx6ul_ccm_get_per_clk(IMX6ULCCMState *dev) +{ + uint64_t freq = 0; + + freq = imx6ul_ccm_get_per_sel_clk(dev) + / (1 + FIELD_EX32(dev->ccm[CCM_CSCMR1], CSCMR1, PERCLK_PODF)); + + trace_ccm_freq((uint32_t)freq); + + return freq; +} + +static uint32_t imx6ul_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) +{ + uint32_t freq = 0; + IMX6ULCCMState *s = IMX6UL_CCM(dev); + + switch (clock) { + case CLK_NONE: + break; + case CLK_IPG: + freq = imx6ul_ccm_get_ipg_clk(s); + break; + case CLK_IPG_HIGH: + freq = imx6ul_ccm_get_per_clk(s); + break; + case CLK_32k: + freq = CKIL_FREQ; + break; + case CLK_HIGH: + freq = CKIH_FREQ; + break; + case CLK_HIGH_DIV: + freq = CKIH_FREQ / 8; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: unsupported clock %d\n", + TYPE_IMX6UL_CCM, __func__, clock); + break; + } + + trace_ccm_clock_freq(clock, freq); + + return freq; +} + +static void imx6ul_ccm_reset(DeviceState *dev) +{ + IMX6ULCCMState *s = IMX6UL_CCM(dev); + + trace_ccm_entry(); + + s->ccm[CCM_CCR] = 0x0401167F; + s->ccm[CCM_CCDR] = 0x00000000; + s->ccm[CCM_CSR] = 0x00000010; + s->ccm[CCM_CCSR] = 0x00000100; + s->ccm[CCM_CACRR] = 0x00000000; + s->ccm[CCM_CBCDR] = 0x00018D00; + s->ccm[CCM_CBCMR] = 0x24860324; + s->ccm[CCM_CSCMR1] = 0x04900080; + s->ccm[CCM_CSCMR2] = 0x03192F06; + s->ccm[CCM_CSCDR1] = 0x00490B00; + s->ccm[CCM_CS1CDR] = 0x0EC102C1; + s->ccm[CCM_CS2CDR] = 0x000336C1; + s->ccm[CCM_CDCDR] = 0x33F71F92; + s->ccm[CCM_CHSCCDR] = 0x000248A4; + s->ccm[CCM_CSCDR2] = 0x00029B48; + 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] = 0x00013063; + 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_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] = 0x00001073; + s->analog[PMU_REG_CORE] = 0x00482012; + s->analog[PMU_MISC0] = 0x04000000; + s->analog[PMU_MISC1] = 0x00000000; + s->analog[PMU_MISC2] = 0x00272727; + s->analog[PMU_LOWPWR_CTRL] = 0x00004009; + + s->analog[USB_ANALOG_USB1_VBUS_DETECT] = 0x01000004; + 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] = 0x01000004; + s->analog[USB_ANALOG_USB2_CHRG_DETECT] = 0x00000000; + s->analog[USB_ANALOG_USB2_MISC] = 0x00000002; + s->analog[USB_ANALOG_DIGPROG] = 0x00640000; + + /* 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_ENET] |= CCM_ANALOG_PLL_LOCK; + + s->analog[TEMPMON_TEMPSENSE0] = 0x00000001; + s->analog[TEMPMON_TEMPSENSE1] = 0x00000001; + s->analog[TEMPMON_TEMPSENSE2] = 0x00000000; +} + +static uint64_t imx6ul_ccm_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value = 0; + uint32_t index = offset >> 2; + IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; + + assert(index < CCM_MAX); + + value = s->ccm[index]; + + trace_ccm_read_reg(imx6ul_ccm_reg_name(index), (uint32_t)value); + + return (uint64_t)value; +} + +static void imx6ul_ccm_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + uint32_t index = offset >> 2; + IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; + + assert(index < CCM_MAX); + + trace_ccm_write_reg(imx6ul_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 imx6ul_analog_read(void *opaque, hwaddr offset, unsigned size) +{ + uint32_t value; + uint32_t index = offset >> 2; + IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; + + assert(index < CCM_ANALOG_MAX); + + 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_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: + case TEMPMON_TEMPSENSE0_SET: + case TEMPMON_TEMPSENSE1_SET: + case TEMPMON_TEMPSENSE2_SET: + /* + * All REG_NAME_SET register access are in fact targeting + * 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_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: + case TEMPMON_TEMPSENSE0_CLR: + case TEMPMON_TEMPSENSE1_CLR: + case TEMPMON_TEMPSENSE2_CLR: + /* + * All REG_NAME_CLR register access are in fact targeting + * 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_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: + case TEMPMON_TEMPSENSE0_TOG: + case TEMPMON_TEMPSENSE1_TOG: + case TEMPMON_TEMPSENSE2_TOG: + /* + * All REG_NAME_TOG register access are in fact targeting + * the REG_NAME register. + */ + value = s->analog[index - 3]; + break; + default: + value = s->analog[index]; + break; + } + + trace_ccm_read_reg(imx6ul_analog_reg_name(index), (uint32_t)value); + + return (uint64_t)value; +} + +static void imx6ul_analog_write(void *opaque, hwaddr offset, uint64_t value, + unsigned size) +{ + uint32_t index = offset >> 2; + IMX6ULCCMState *s = (IMX6ULCCMState *)opaque; + + assert(index < CCM_ANALOG_MAX); + + trace_ccm_write_reg(imx6ul_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_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 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_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 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_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 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 imx6ul_ccm_ops = { + .read = imx6ul_ccm_read, + .write = imx6ul_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 imx6ul_analog_ops = { + .read = imx6ul_analog_read, + .write = imx6ul_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 imx6ul_ccm_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + SysBusDevice *sd = SYS_BUS_DEVICE(obj); + IMX6ULCCMState *s = IMX6UL_CCM(obj); + + /* initialize a container for the all memory range */ + memory_region_init(&s->container, OBJECT(dev), TYPE_IMX6UL_CCM, 0x8000); + + /* We initialize an IO memory region for the CCM part */ + memory_region_init_io(&s->ioccm, OBJECT(dev), &imx6ul_ccm_ops, s, + TYPE_IMX6UL_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), &imx6ul_analog_ops, s, + TYPE_IMX6UL_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 imx6ul_ccm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + IMXCCMClass *ccm = IMX_CCM_CLASS(klass); + + dc->reset = imx6ul_ccm_reset; + dc->vmsd = &vmstate_imx6ul_ccm; + dc->desc = "i.MX6UL Clock Control Module"; + + ccm->get_clock_frequency = imx6ul_ccm_get_clock_frequency; +} + +static const TypeInfo imx6ul_ccm_info = { + .name = TYPE_IMX6UL_CCM, + .parent = TYPE_IMX_CCM, + .instance_size = sizeof(IMX6ULCCMState), + .instance_init = imx6ul_ccm_init, + .class_init = imx6ul_ccm_class_init, +}; + +static void imx6ul_ccm_register_types(void) +{ + type_register_static(&imx6ul_ccm_info); +} + +type_init(imx6ul_ccm_register_types) diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index de4fd8e36d..537601cd53 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -190,12 +190,13 @@ static MemTxResult iotkit_secctl_s_read(void *opaque, hwaddr addr, r = s->apbexp[offset_to_ppc_idx(offset)].sp; break; case A_SECMSCINTSTAT: + r = s->secmscintstat; + break; case A_SECMSCINTEN: + r = s->secmscinten; + break; case A_NSMSCEXP: - qemu_log_mask(LOG_UNIMP, - "IoTKit SecCtl S block read: " - "unimplemented offset 0x%x\n", offset); - r = 0; + r = s->nsmscexp; break; case A_PID4: case A_PID5: @@ -291,6 +292,23 @@ static void iotkit_secctl_ppc_update_irq_enable(IoTKitSecCtlPPC *ppc) qemu_set_irq(ppc->irq_enable, extract32(value, ppc->irq_bit_offset, 1)); } +static void iotkit_secctl_update_mscexp_irqs(qemu_irq *msc_irqs, uint32_t value) +{ + int i; + + for (i = 0; i < IOTS_NUM_EXP_MSC; i++) { + qemu_set_irq(msc_irqs[i], extract32(value, i + 16, 1)); + } +} + +static void iotkit_secctl_update_msc_irq(IoTKitSecCtl *s) +{ + /* Update the combined MSC IRQ, based on S_MSCEXP_STATUS and S_MSCEXP_EN */ + bool level = s->secmscintstat & s->secmscinten; + + qemu_set_irq(s->msc_irq, level); +} + static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, uint64_t value, unsigned size, MemTxAttrs attrs) @@ -370,10 +388,15 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, iotkit_secctl_ppc_sp_write(ppc, value); break; case A_SECMSCINTCLR: + iotkit_secctl_update_mscexp_irqs(s->mscexp_clear, value); + break; case A_SECMSCINTEN: - qemu_log_mask(LOG_UNIMP, - "IoTKit SecCtl S block write: " - "unimplemented offset 0x%x\n", offset); + s->secmscinten = value; + iotkit_secctl_update_msc_irq(s); + break; + case A_NSMSCEXP: + s->nsmscexp = value; + iotkit_secctl_update_mscexp_irqs(s->mscexp_ns, value); break; case A_SECMPCINTSTATUS: case A_SECPPCINTSTAT: @@ -381,7 +404,6 @@ static MemTxResult iotkit_secctl_s_write(void *opaque, hwaddr addr, case A_BRGINTSTAT: case A_AHBNSPPC0: case A_AHBSPPPC0: - case A_NSMSCEXP: case A_PID4: case A_PID5: case A_PID6: @@ -578,7 +600,7 @@ static void iotkit_secctl_mpc_status(void *opaque, int n, int level) { IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); - s->mpcintstatus = deposit32(s->mpcintstatus, 0, 1, !!level); + s->mpcintstatus = deposit32(s->mpcintstatus, n, 1, !!level); } static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) @@ -588,6 +610,14 @@ static void iotkit_secctl_mpcexp_status(void *opaque, int n, int level) s->mpcintstatus = deposit32(s->mpcintstatus, n + 16, 1, !!level); } +static void iotkit_secctl_mscexp_status(void *opaque, int n, int level) +{ + IoTKitSecCtl *s = IOTKIT_SECCTL(opaque); + + s->secmscintstat = deposit32(s->secmscintstat, n + 16, 1, !!level); + iotkit_secctl_update_msc_irq(s); +} + static void iotkit_secctl_ppc_irqstatus(void *opaque, int n, int level) { IoTKitSecCtlPPC *ppc = opaque; @@ -656,10 +686,19 @@ static void iotkit_secctl_init(Object *obj) qdev_init_gpio_out_named(dev, &s->sec_resp_cfg, "sec_resp_cfg", 1); qdev_init_gpio_out_named(dev, &s->nsc_cfg_irq, "nsc_cfg", 1); - qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", 1); + qdev_init_gpio_in_named(dev, iotkit_secctl_mpc_status, "mpc_status", + IOTS_NUM_MPC); qdev_init_gpio_in_named(dev, iotkit_secctl_mpcexp_status, "mpcexp_status", IOTS_NUM_EXP_MPC); + qdev_init_gpio_in_named(dev, iotkit_secctl_mscexp_status, + "mscexp_status", IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, s->mscexp_clear, "mscexp_clear", + IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, s->mscexp_ns, "mscexp_ns", + IOTS_NUM_EXP_MSC); + qdev_init_gpio_out_named(dev, &s->msc_irq, "msc_irq", 1); + memory_region_init_io(&s->s_regs, obj, &iotkit_secctl_s_ops, s, "iotkit-secctl-s-regs", 0x1000); memory_region_init_io(&s->ns_regs, obj, &iotkit_secctl_ns_ops, @@ -690,6 +729,24 @@ static const VMStateDescription iotkit_secctl_mpcintstatus_vmstate = { } }; +static bool needed_always(void *opaque) +{ + return true; +} + +static const VMStateDescription iotkit_secctl_msc_vmstate = { + .name = "iotkit-secctl/msc", + .version_id = 1, + .minimum_version_id = 1, + .needed = needed_always, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secmscintstat, IoTKitSecCtl), + VMSTATE_UINT32(secmscinten, IoTKitSecCtl), + VMSTATE_UINT32(nsmscexp, IoTKitSecCtl), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription iotkit_secctl_vmstate = { .name = "iotkit-secctl", .version_id = 1, @@ -710,6 +767,7 @@ static const VMStateDescription iotkit_secctl_vmstate = { }, .subsections = (const VMStateDescription*[]) { &iotkit_secctl_mpcintstatus_vmstate, + &iotkit_secctl_msc_vmstate, NULL }, }; diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c new file mode 100644 index 0000000000..54064a31ef --- /dev/null +++ b/hw/misc/iotkit-sysctl.c @@ -0,0 +1,525 @@ +/* + * ARM IoTKit system control element + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system control element" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Specifically, it implements the "system control register" blocks. + */ + +#include "qemu/osdep.h" +#include "qemu/bitops.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-sysctl.h" +#include "target/arm/arm-powerctl.h" +#include "target/arm/cpu.h" + +REG32(SECDBGSTAT, 0x0) +REG32(SECDBGSET, 0x4) +REG32(SECDBGCLR, 0x8) +REG32(SCSECCTRL, 0xc) +REG32(FCLK_DIV, 0x10) +REG32(SYSCLK_DIV, 0x14) +REG32(CLOCK_FORCE, 0x18) +REG32(RESET_SYNDROME, 0x100) +REG32(RESET_MASK, 0x104) +REG32(SWRESET, 0x108) + FIELD(SWRESET, SWRESETREQ, 9, 1) +REG32(GRETREG, 0x10c) +REG32(INITSVTOR0, 0x110) +REG32(INITSVTOR1, 0x114) +REG32(CPUWAIT, 0x118) +REG32(NMI_ENABLE, 0x11c) /* BUSWAIT in IoTKit */ +REG32(WICCTRL, 0x120) +REG32(EWCTRL, 0x124) +REG32(PDCM_PD_SYS_SENSE, 0x200) +REG32(PDCM_PD_SRAM0_SENSE, 0x20c) +REG32(PDCM_PD_SRAM1_SENSE, 0x210) +REG32(PDCM_PD_SRAM2_SENSE, 0x214) +REG32(PDCM_PD_SRAM3_SENSE, 0x218) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysctl_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x54, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +/* + * Set the initial secure vector table offset address for the core. + * This will take effect when the CPU next resets. + */ +static void set_init_vtor(uint64_t cpuid, uint32_t vtor) +{ + Object *cpuobj = OBJECT(arm_get_cpu_by_id(cpuid)); + + if (cpuobj) { + if (object_property_find(cpuobj, "init-svtor", NULL)) { + object_property_set_uint(cpuobj, vtor, "init-svtor", &error_abort); + } + } +} + +static uint64_t iotkit_sysctl_read(void *opaque, hwaddr offset, + unsigned size) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + uint64_t r; + + switch (offset) { + case A_SECDBGSTAT: + r = s->secure_debug; + break; + case A_SCSECCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->scsecctrl; + break; + case A_FCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->fclk_div; + break; + case A_SYSCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->sysclk_div; + break; + case A_CLOCK_FORCE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->clock_force; + break; + case A_RESET_SYNDROME: + r = s->reset_syndrome; + break; + case A_RESET_MASK: + r = s->reset_mask; + break; + case A_GRETREG: + r = s->gretreg; + break; + case A_INITSVTOR0: + r = s->initsvtor0; + break; + case A_INITSVTOR1: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->initsvtor1; + break; + case A_CPUWAIT: + r = s->cpuwait; + break; + case A_NMI_ENABLE: + /* In IoTKit this is named BUSWAIT but is marked reserved, R/O, zero */ + if (!s->is_sse200) { + r = 0; + break; + } + r = s->nmi_enable; + break; + case A_WICCTRL: + r = s->wicctrl; + break; + case A_EWCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->ewctrl; + break; + case A_PDCM_PD_SYS_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sys_sense; + break; + case A_PDCM_PD_SRAM0_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram0_sense; + break; + case A_PDCM_PD_SRAM1_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram1_sense; + break; + case A_PDCM_PD_SRAM2_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram2_sense; + break; + case A_PDCM_PD_SRAM3_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + r = s->pdcm_pd_sram3_sense; + break; + case A_PID4 ... A_CID3: + r = sysctl_id[(offset - A_PID4) / 4]; + break; + case A_SECDBGSET: + case A_SECDBGCLR: + case A_SWRESET: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl read: read of WO offset %x\n", + (int)offset); + r = 0; + break; + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_iotkit_sysctl_read(offset, r, size); + return r; +} + +static void iotkit_sysctl_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + + trace_iotkit_sysctl_write(offset, value, size); + + /* + * Most of the state here has to do with control of reset and + * similar kinds of power up -- for instance the guest can ask + * what the reason for the last reset was, or forbid reset for + * some causes (like the non-secure watchdog). Most of this is + * not relevant to QEMU, which doesn't really model anything other + * than a full power-on reset. + * We just model the registers as reads-as-written. + */ + + switch (offset) { + case A_RESET_SYNDROME: + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl RESET_SYNDROME unimplemented\n"); + s->reset_syndrome = value; + break; + case A_RESET_MASK: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl RESET_MASK unimplemented\n"); + s->reset_mask = value; + break; + case A_GRETREG: + /* + * General retention register, which is only reset by a power-on + * reset. Technically this implementation is complete, since + * QEMU only supports power-on resets... + */ + s->gretreg = value; + break; + case A_INITSVTOR0: + s->initsvtor0 = value; + set_init_vtor(0, s->initsvtor0); + break; + case A_CPUWAIT: + if ((s->cpuwait & 1) && !(value & 1)) { + /* Powering up CPU 0 */ + arm_set_cpu_on_and_reset(0); + } + if ((s->cpuwait & 2) && !(value & 2)) { + /* Powering up CPU 1 */ + arm_set_cpu_on_and_reset(1); + } + s->cpuwait = value; + break; + case A_WICCTRL: + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl WICCTRL unimplemented\n"); + s->wicctrl = value; + break; + case A_SECDBGSET: + /* write-1-to-set */ + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SECDBGSET unimplemented\n"); + s->secure_debug |= value; + break; + case A_SECDBGCLR: + /* write-1-to-clear */ + s->secure_debug &= ~value; + break; + case A_SWRESET: + /* One w/o bit to request a reset; all other bits reserved */ + if (value & R_SWRESET_SWRESETREQ_MASK) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + break; + case A_SCSECCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SCSECCTRL unimplemented\n"); + s->scsecctrl = value; + break; + case A_FCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl FCLK_DIV unimplemented\n"); + s->fclk_div = value; + break; + case A_SYSCLK_DIV: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl SYSCLK_DIV unimplemented\n"); + s->sysclk_div = value; + break; + case A_CLOCK_FORCE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl CLOCK_FORCE unimplemented\n"); + s->clock_force = value; + break; + case A_INITSVTOR1: + if (!s->is_sse200) { + goto bad_offset; + } + s->initsvtor1 = value; + set_init_vtor(1, s->initsvtor1); + break; + case A_EWCTRL: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl EWCTRL unimplemented\n"); + s->ewctrl = value; + break; + case A_PDCM_PD_SYS_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SYS_SENSE unimplemented\n"); + s->pdcm_pd_sys_sense = value; + break; + case A_PDCM_PD_SRAM0_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM0_SENSE unimplemented\n"); + s->pdcm_pd_sram0_sense = value; + break; + case A_PDCM_PD_SRAM1_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM1_SENSE unimplemented\n"); + s->pdcm_pd_sram1_sense = value; + break; + case A_PDCM_PD_SRAM2_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM2_SENSE unimplemented\n"); + s->pdcm_pd_sram2_sense = value; + break; + case A_PDCM_PD_SRAM3_SENSE: + if (!s->is_sse200) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "IoTKit SysCtl PDCM_PD_SRAM3_SENSE unimplemented\n"); + s->pdcm_pd_sram3_sense = value; + break; + case A_NMI_ENABLE: + /* In IoTKit this is BUSWAIT: reserved, R/O, zero */ + if (!s->is_sse200) { + goto ro_offset; + } + qemu_log_mask(LOG_UNIMP, "IoTKit SysCtl NMI_ENABLE unimplemented\n"); + s->nmi_enable = value; + break; + case A_SECDBGSTAT: + case A_PID4 ... A_CID3: + ro_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl write: write of RO offset %x\n", + (int)offset); + break; + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysCtl write: bad offset %x\n", (int)offset); + break; + } +} + +static const MemoryRegionOps iotkit_sysctl_ops = { + .read = iotkit_sysctl_read, + .write = iotkit_sysctl_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void iotkit_sysctl_reset(DeviceState *dev) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(dev); + + trace_iotkit_sysctl_reset(); + s->secure_debug = 0; + s->reset_syndrome = 1; + s->reset_mask = 0; + s->gretreg = 0; + s->initsvtor0 = s->initsvtor0_rst; + s->initsvtor1 = s->initsvtor1_rst; + s->cpuwait = s->cpuwait_rst; + s->wicctrl = 0; + s->scsecctrl = 0; + s->fclk_div = 0; + s->sysclk_div = 0; + s->clock_force = 0; + s->nmi_enable = 0; + s->ewctrl = 0; + s->pdcm_pd_sys_sense = 0x7f; + s->pdcm_pd_sram0_sense = 0; + s->pdcm_pd_sram1_sense = 0; + s->pdcm_pd_sram2_sense = 0; + s->pdcm_pd_sram3_sense = 0; +} + +static void iotkit_sysctl_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IoTKitSysCtl *s = IOTKIT_SYSCTL(obj); + + memory_region_init_io(&s->iomem, obj, &iotkit_sysctl_ops, + s, "iotkit-sysctl", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void iotkit_sysctl_realize(DeviceState *dev, Error **errp) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(dev); + + /* The top 4 bits of the SYS_VERSION register tell us if we're an SSE-200 */ + if (extract32(s->sys_version, 28, 4) == 2) { + s->is_sse200 = true; + } +} + +static bool sse200_needed(void *opaque) +{ + IoTKitSysCtl *s = IOTKIT_SYSCTL(opaque); + + return s->is_sse200; +} + +static const VMStateDescription iotkit_sysctl_sse200_vmstate = { + .name = "iotkit-sysctl/sse-200", + .version_id = 1, + .minimum_version_id = 1, + .needed = sse200_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(scsecctrl, IoTKitSysCtl), + VMSTATE_UINT32(fclk_div, IoTKitSysCtl), + VMSTATE_UINT32(sysclk_div, IoTKitSysCtl), + VMSTATE_UINT32(clock_force, IoTKitSysCtl), + VMSTATE_UINT32(initsvtor1, IoTKitSysCtl), + VMSTATE_UINT32(nmi_enable, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sys_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram0_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram1_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram2_sense, IoTKitSysCtl), + VMSTATE_UINT32(pdcm_pd_sram3_sense, IoTKitSysCtl), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription iotkit_sysctl_vmstate = { + .name = "iotkit-sysctl", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(secure_debug, IoTKitSysCtl), + VMSTATE_UINT32(reset_syndrome, IoTKitSysCtl), + VMSTATE_UINT32(reset_mask, IoTKitSysCtl), + VMSTATE_UINT32(gretreg, IoTKitSysCtl), + VMSTATE_UINT32(initsvtor0, IoTKitSysCtl), + VMSTATE_UINT32(cpuwait, IoTKitSysCtl), + VMSTATE_UINT32(wicctrl, IoTKitSysCtl), + VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &iotkit_sysctl_sse200_vmstate, + NULL + } +}; + +static Property iotkit_sysctl_props[] = { + DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysCtl, sys_version, 0), + DEFINE_PROP_UINT32("CPUWAIT_RST", IoTKitSysCtl, cpuwait_rst, 0), + DEFINE_PROP_UINT32("INITSVTOR0_RST", IoTKitSysCtl, initsvtor0_rst, + 0x10000000), + DEFINE_PROP_UINT32("INITSVTOR1_RST", IoTKitSysCtl, initsvtor1_rst, + 0x10000000), + DEFINE_PROP_END_OF_LIST() +}; + +static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->vmsd = &iotkit_sysctl_vmstate; + dc->reset = iotkit_sysctl_reset; + dc->props = iotkit_sysctl_props; + dc->realize = iotkit_sysctl_realize; +} + +static const TypeInfo iotkit_sysctl_info = { + .name = TYPE_IOTKIT_SYSCTL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSysCtl), + .instance_init = iotkit_sysctl_init, + .class_init = iotkit_sysctl_class_init, +}; + +static void iotkit_sysctl_register_types(void) +{ + type_register_static(&iotkit_sysctl_info); +} + +type_init(iotkit_sysctl_register_types); diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c new file mode 100644 index 0000000000..026ba94261 --- /dev/null +++ b/hw/misc/iotkit-sysinfo.c @@ -0,0 +1,139 @@ +/* + * ARM IoTKit system information block + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system information block" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * It consists of 2 read-only version/config registers, plus the + * usual ID registers. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "sysemu/sysemu.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/iotkit-sysinfo.h" + +REG32(SYS_VERSION, 0x0) +REG32(SYS_CONFIG, 0x4) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int sysinfo_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x58, 0xb8, 0x0b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static uint64_t iotkit_sysinfo_read(void *opaque, hwaddr offset, + unsigned size) +{ + IoTKitSysInfo *s = IOTKIT_SYSINFO(opaque); + uint64_t r; + + switch (offset) { + case A_SYS_VERSION: + r = s->sys_version; + break; + + case A_SYS_CONFIG: + r = s->sys_config; + break; + case A_PID4 ... A_CID3: + r = sysinfo_id[(offset - A_PID4) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysInfo read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_iotkit_sysinfo_read(offset, r, size); + return r; +} + +static void iotkit_sysinfo_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + trace_iotkit_sysinfo_write(offset, value, size); + + qemu_log_mask(LOG_GUEST_ERROR, + "IoTKit SysInfo: write to RO offset 0x%x\n", (int)offset); +} + +static const MemoryRegionOps iotkit_sysinfo_ops = { + .read = iotkit_sysinfo_read, + .write = iotkit_sysinfo_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static Property iotkit_sysinfo_props[] = { + DEFINE_PROP_UINT32("SYS_VERSION", IoTKitSysInfo, sys_version, 0), + DEFINE_PROP_UINT32("SYS_CONFIG", IoTKitSysInfo, sys_config, 0), + DEFINE_PROP_END_OF_LIST() +}; + +static void iotkit_sysinfo_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + IoTKitSysInfo *s = IOTKIT_SYSINFO(obj); + + memory_region_init_io(&s->iomem, obj, &iotkit_sysinfo_ops, + s, "iotkit-sysinfo", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); +} + +static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + /* + * This device has no guest-modifiable state and so it + * does not need a reset function or VMState. + */ + + dc->props = iotkit_sysinfo_props; +} + +static const TypeInfo iotkit_sysinfo_info = { + .name = TYPE_IOTKIT_SYSINFO, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(IoTKitSysInfo), + .instance_init = iotkit_sysinfo_init, + .class_init = iotkit_sysinfo_class_init, +}; + +static void iotkit_sysinfo_register_types(void) +{ + type_register_static(&iotkit_sysinfo_info); +} + +type_init(iotkit_sysinfo_register_types); diff --git a/hw/misc/ivshmem.c b/hw/misc/ivshmem.c index 6febbabcaa..c7b6bbc974 100644 --- a/hw/misc/ivshmem.c +++ b/hw/misc/ivshmem.c @@ -112,13 +112,6 @@ typedef struct IVShmemState { /* migration stuff */ OnOffAuto master; Error *migration_blocker; - - /* 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 */ @@ -529,17 +522,6 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) size = buf.st_size; - /* 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; - } - /* mmap the region and map into the BAR2 */ memory_region_init_ram_from_fd(&s->server_bar2, OBJECT(s), "ivshmem.bar2", size, true, fd, &local_err); @@ -882,8 +864,6 @@ 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; Error *local_err = NULL; /* IRQFD requires MSI */ @@ -903,14 +883,11 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) 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 (s->hostmem != NULL) { IVSHMEM_DPRINTF("using hostmem\n"); s->ivshmem_bar2 = host_memory_backend_get_memory(s->hostmem); + host_memory_backend_set_mapped(s->hostmem, true); } else { Chardev *chr = qemu_chr_fe_get_driver(&s->server_chr); assert(chr); @@ -963,7 +940,11 @@ static void ivshmem_common_realize(PCIDevice *dev, Error **errp) } vmstate_register_ram(s->ivshmem_bar2, DEVICE(s)); - pci_register_bar(PCI_DEVICE(s), 2, attr, s->ivshmem_bar2); + pci_register_bar(PCI_DEVICE(s), 2, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + s->ivshmem_bar2); } static void ivshmem_exit(PCIDevice *dev) @@ -993,6 +974,10 @@ static void ivshmem_exit(PCIDevice *dev) vmstate_unregister_ram(s->ivshmem_bar2, DEVICE(dev)); } + if (s->hostmem) { + host_memory_backend_set_mapped(s->hostmem, false); + } + if (s->peers) { for (i = 0; i < s->nb_peers; i++) { close_peer_eventfds(s, i); @@ -1079,13 +1064,6 @@ static Property ivshmem_plain_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static void ivshmem_plain_init(Object *obj) -{ - IVShmemState *s = IVSHMEM_PLAIN(obj); - - s->not_legacy_32bit = 1; -} - static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) { IVShmemState *s = IVSHMEM_COMMON(dev); @@ -1101,14 +1079,6 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) } ivshmem_common_realize(dev, errp); - host_memory_backend_set_mapped(s->hostmem, true); -} - -static void ivshmem_plain_exit(PCIDevice *pci_dev) -{ - IVShmemState *s = IVSHMEM_COMMON(pci_dev); - - host_memory_backend_set_mapped(s->hostmem, false); } static void ivshmem_plain_class_init(ObjectClass *klass, void *data) @@ -1117,7 +1087,6 @@ static void ivshmem_plain_class_init(ObjectClass *klass, void *data) PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); k->realize = ivshmem_plain_realize; - k->exit = ivshmem_plain_exit; dc->props = ivshmem_plain_properties; dc->vmsd = &ivshmem_plain_vmsd; } @@ -1126,7 +1095,6 @@ 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, }; @@ -1159,8 +1127,6 @@ 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 */ - s->not_legacy_32bit = 1; } static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) @@ -1193,180 +1159,11 @@ static const TypeInfo ivshmem_doorbell_info = { .class_init = ivshmem_doorbell_class_init, }; -static int ivshmem_load_old(QEMUFile *f, void *opaque, int version_id) -{ - IVShmemState *s = opaque; - PCIDevice *pdev = PCI_DEVICE(s); - int ret; - - 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); - } - - 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("msi", IVShmemState, features, IVSHMEM_MSI, true), - DEFINE_PROP_STRING("shm", IVShmemState, shmobj), - DEFINE_PROP_STRING("role", IVShmemState, role), - 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 (qemu_chr_fe_backend_connected(&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 * MiB; /* 4 MB default */ - } else { - int ret; - uint64_t size; - - ret = qemu_strtosz_MiB(s->sizearg, NULL, &size); - if (ret < 0 || (size_t)size != size || !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->realize = ivshmem_realize; - k->revision = 0; - dc->desc = "Inter-VM shared memory (legacy)"; - dc->props = ivshmem_properties; - dc->vmsd = &ivshmem_vmsd; -} - -static const TypeInfo ivshmem_info = { - .name = TYPE_IVSHMEM, - .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); } type_init(ivshmem_register_types) diff --git a/hw/misc/macio/Kconfig b/hw/misc/macio/Kconfig new file mode 100644 index 0000000000..c6caeb672f --- /dev/null +++ b/hw/misc/macio/Kconfig @@ -0,0 +1,11 @@ +config CUDA + bool + +config MAC_PMU + bool + +config MAC_DBDMA + bool + +config MACIO_GPIO + bool diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index c4f7a2f39b..3febacdd1e 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -97,17 +97,8 @@ static void cuda_set_sr_int(void *opaque) static void cuda_delay_set_sr_int(CUDAState *s) { - MOS6522CUDAState *mcs = &s->mos6522_cuda; - MOS6522State *ms = MOS6522(mcs); - MOS6522DeviceClass *mdc = MOS6522_DEVICE_GET_CLASS(ms); int64_t expire; - if (ms->dirb == 0xff || s->sr_delay_ns == 0) { - /* Disabled or not in Mac OS, fire the IRQ directly */ - mdc->set_sr_int(ms); - return; - } - trace_cuda_delay_set_sr_int(); expire = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->sr_delay_ns; @@ -542,7 +533,7 @@ static void cuda_realize(DeviceState *dev, Error **errp) s->tick_offset = (uint32_t)mktimegm(&tm) + RTC_OFFSET; s->sr_delay_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_set_sr_int, s); - s->sr_delay_ns = 300 * SCALE_US; + s->sr_delay_ns = 20 * SCALE_US; s->adb_poll_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cuda_adb_poll, s); s->adb_poll_mask = 0xffff; diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index 87ae246d37..e5917d8f2e 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -38,7 +38,6 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" -#include "hw/isa/isa.h" #include "hw/ppc/mac_dbdma.h" #include "qemu/main-loop.h" #include "qemu/log.h" diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 52aa3775f4..94da85c8d7 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -90,6 +90,15 @@ static void macio_bar_setup(MacIOState *s) macio_escc_legacy_setup(s); } +static void macio_init_child_obj(MacIOState *s, const char *childname, + void *child, size_t childsize, + const char *childtype) +{ + object_initialize_child(OBJECT(s), childname, child, childsize, childtype, + &error_abort, NULL); + qdev_set_parent_bus(DEVICE(child), BUS(&s->macio_bus)); +} + static void macio_common_realize(PCIDevice *d, Error **errp) { MacIOState *s = MACIO(d); @@ -210,10 +219,11 @@ static void macio_init_ide(MacIOState *s, MACIOIDEState *ide, size_t ide_size, int index) { gchar *name = g_strdup_printf("ide[%i]", index); + uint32_t addr = 0x1f000 + ((index + 1) * 0x1000); - sysbus_init_child_obj(OBJECT(s), name, ide, ide_size, TYPE_MACIO_IDE); - memory_region_add_subregion(&s->bar, 0x1f000 + ((index + 1) * 0x1000), - &ide->mem); + macio_init_child_obj(s, name, ide, ide_size, TYPE_MACIO_IDE); + qdev_prop_set_uint32(DEVICE(ide), "addr", addr); + memory_region_add_subregion(&s->bar, addr, &ide->mem); g_free(name); } @@ -229,7 +239,7 @@ static void macio_oldworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); - sysbus_init_child_obj(obj, "cuda", &s->cuda, sizeof(s->cuda), TYPE_CUDA); + macio_init_child_obj(s, "cuda", &s->cuda, sizeof(s->cuda), TYPE_CUDA); object_initialize(&os->nvram, sizeof(os->nvram), TYPE_MACIO_NVRAM); dev = DEVICE(&os->nvram); @@ -340,7 +350,7 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) object_property_set_link(OBJECT(&s->pmu), OBJECT(sysbus_dev), "gpio", &error_abort); qdev_prop_set_bit(DEVICE(&s->pmu), "has-adb", ns->has_adb); - qdev_set_parent_bus(DEVICE(&s->pmu), sysbus_get_default()); + qdev_set_parent_bus(DEVICE(&s->pmu), BUS(&s->macio_bus)); object_property_add_child(OBJECT(s), "pmu", OBJECT(&s->pmu), NULL); object_property_set_bool(OBJECT(&s->pmu), true, "realized", &err); @@ -356,7 +366,7 @@ static void macio_newworld_realize(PCIDevice *d, Error **errp) } else { /* CUDA */ object_initialize(&s->cuda, sizeof(s->cuda), TYPE_CUDA); - qdev_set_parent_bus(DEVICE(&s->cuda), sysbus_get_default()); + qdev_set_parent_bus(DEVICE(&s->cuda), BUS(&s->macio_bus)); object_property_add_child(OBJECT(s), "cuda", OBJECT(&s->cuda), NULL); qdev_prop_set_uint64(DEVICE(&s->cuda), "timebase-frequency", s->frequency); @@ -385,8 +395,8 @@ static void macio_newworld_init(Object *obj) qdev_prop_allow_set_link_before_realize, 0, NULL); - sysbus_init_child_obj(obj, "gpio", &ns->gpio, sizeof(ns->gpio), - TYPE_MACIO_GPIO); + macio_init_child_obj(s, "gpio", &ns->gpio, sizeof(ns->gpio), + TYPE_MACIO_GPIO); for (i = 0; i < 2; i++) { macio_init_ide(s, &ns->ide[i], sizeof(ns->ide[i]), i); @@ -399,10 +409,13 @@ static void macio_instance_init(Object *obj) memory_region_init(&s->bar, obj, "macio", 0x80000); - sysbus_init_child_obj(obj, "dbdma", &s->dbdma, sizeof(s->dbdma), - TYPE_MAC_DBDMA); + qbus_create_inplace(&s->macio_bus, sizeof(s->macio_bus), TYPE_MACIO_BUS, + DEVICE(obj), "macio.0"); - sysbus_init_child_obj(obj, "escc", &s->escc, sizeof(s->escc), TYPE_ESCC); + macio_init_child_obj(s, "dbdma", &s->dbdma, sizeof(s->dbdma), + TYPE_MAC_DBDMA); + + macio_init_child_obj(s, "escc", &s->escc, sizeof(s->escc), TYPE_ESCC); } static const VMStateDescription vmstate_macio_oldworld = { @@ -470,6 +483,12 @@ static void macio_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } +static const TypeInfo macio_bus_info = { + .name = TYPE_MACIO_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(MacIOBusState), +}; + static const TypeInfo macio_oldworld_type_info = { .name = TYPE_OLDWORLD_MACIO, .parent = TYPE_MACIO, @@ -501,6 +520,7 @@ static const TypeInfo macio_type_info = { static void macio_register_types(void) { + type_register_static(&macio_bus_info); type_register_static(&macio_type_info); type_register_static(&macio_oldworld_type_info); type_register_static(&macio_newworld_type_info); diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index d25344f888..6e6d96c8c5 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -686,6 +686,7 @@ static const VMStateDescription vmstate_pmu_adb = { VMSTATE_TIMER_PTR(adb_poll_timer, PMUState), VMSTATE_UINT8(adb_reply_size, PMUState), VMSTATE_BUFFER(adb_reply, PMUState), + VMSTATE_END_OF_LIST() } }; diff --git a/hw/misc/macio/trace-events b/hw/misc/macio/trace-events index 05019262fa..e4a1cc0d24 100644 --- a/hw/misc/macio/trace-events +++ b/hw/misc/macio/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/misc/macio/cuda.c +# cuda.c cuda_delay_set_sr_int(void) "" cuda_data_send(uint8_t data) "send: 0x%02x" cuda_data_recv(uint8_t data) "recv: 0x%02x" @@ -10,18 +10,17 @@ cuda_packet_receive_data(int i, const uint8_t data) "[%d] 0x%02x" cuda_packet_send(int len) "length %d" cuda_packet_send_data(int i, const uint8_t data) "[%d] 0x%02x" -# hw/misc/macio/macio.c +# macio.c macio_timer_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 macio_timer_read(uint64_t addr, unsigned len, uint32_t val) "read addr 0x%"PRIx64 " len %d val 0x%"PRIx32 -# hw/misc/macio/gpio.c +# gpio.c macio_set_gpio(int gpio, bool state) "setting GPIO %d to %d" macio_gpio_irq_assert(int gpio) "asserting GPIO %d" macio_gpio_irq_deassert(int gpio) "deasserting GPIO %d" macio_gpio_write(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 -macio_gpio_read(uint64_t addr, uint64_t val) "addr: 0x%"PRIx64" value: 0x%"PRIx64 -# hw/misc/macio/pmu.c +# pmu.c pmu_adb_poll(int olen) "ADB autopoll, olen=%d" pmu_one_sec_timer(void) "PMU one sec..." pmu_cmd_set_int_mask(int intmask) "Setting PMU int mask to 0x%02x" diff --git a/hw/misc/max111x.c b/hw/misc/max111x.c index 6dbdc03677..ac6d35a81d 100644 --- a/hw/misc/max111x.c +++ b/hw/misc/max111x.c @@ -43,9 +43,9 @@ typedef struct { #define CB_START (1 << 7) #define CHANNEL_NUM(v, b0, b1, b2) \ - ((((v) >> (2 + (b0))) & 4) | \ - (((v) >> (3 + (b1))) & 2) | \ - (((v) >> (4 + (b2))) & 1)) + ((((v) >> (2 + (b0))) & 4) | \ + (((v) >> (3 + (b1))) & 2) | \ + (((v) >> (4 + (b2))) & 1)) static uint32_t max111x_read(MAX111xState *s) { diff --git a/hw/misc/milkymist-hpdmc.c b/hw/misc/milkymist-hpdmc.c index e6140eec6b..44dc0698ec 100644 --- a/hw/misc/milkymist-hpdmc.c +++ b/hw/misc/milkymist-hpdmc.c @@ -129,15 +129,13 @@ static void milkymist_hpdmc_reset(DeviceState *d) | IODELAY_PLL2_LOCKED; } -static int milkymist_hpdmc_init(SysBusDevice *dev) +static void milkymist_hpdmc_realize(DeviceState *dev, Error **errp) { MilkymistHpdmcState *s = MILKYMIST_HPDMC(dev); memory_region_init_io(&s->regs_region, OBJECT(dev), &hpdmc_mmio_ops, s, "milkymist-hpdmc", R_MAX * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->regs_region); } static const VMStateDescription vmstate_milkymist_hpdmc = { @@ -153,9 +151,8 @@ static const VMStateDescription vmstate_milkymist_hpdmc = { static void milkymist_hpdmc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_hpdmc_init; + dc->realize = milkymist_hpdmc_realize; dc->reset = milkymist_hpdmc_reset; dc->vmsd = &vmstate_milkymist_hpdmc; } diff --git a/hw/misc/milkymist-pfpu.c b/hw/misc/milkymist-pfpu.c index 86f5e383b0..4a03c7ee63 100644 --- a/hw/misc/milkymist-pfpu.c +++ b/hw/misc/milkymist-pfpu.c @@ -497,17 +497,16 @@ static void milkymist_pfpu_reset(DeviceState *d) } } -static int milkymist_pfpu_init(SysBusDevice *dev) +static void milkymist_pfpu_realize(DeviceState *dev, Error **errp) { MilkymistPFPUState *s = MILKYMIST_PFPU(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); - sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(sbd, &s->irq); memory_region_init_io(&s->regs_region, OBJECT(dev), &pfpu_mmio_ops, s, "milkymist-pfpu", MICROCODE_END * 4); - sysbus_init_mmio(dev, &s->regs_region); - - return 0; + sysbus_init_mmio(sbd, &s->regs_region); } static const VMStateDescription vmstate_milkymist_pfpu = { @@ -527,9 +526,8 @@ static const VMStateDescription vmstate_milkymist_pfpu = { static void milkymist_pfpu_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_pfpu_init; + dc->realize = milkymist_pfpu_realize; dc->reset = milkymist_pfpu_reset; dc->vmsd = &vmstate_milkymist_pfpu; } diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 6d345745f6..712d8423a7 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -30,6 +30,14 @@ static inline uint64_t cpc_vp_run_mask(MIPSCPCState *cpc) return (1ULL << cpc->num_vp) - 1; } +static void mips_cpu_reset_async_work(CPUState *cs, run_on_cpu_data data) +{ + MIPSCPCState *cpc = (MIPSCPCState *) data.host_ptr; + + cpu_reset(cs); + cpc->vp_running |= 1ULL << cs->cpu_index; +} + static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) { CPUState *cs = first_cpu; @@ -37,8 +45,13 @@ static void cpc_run_vp(MIPSCPCState *cpc, uint64_t vp_run) CPU_FOREACH(cs) { uint64_t i = 1ULL << cs->cpu_index; if (i & vp_run & ~cpc->vp_running) { - cpu_reset(cs); - cpc->vp_running |= i; + /* + * To avoid racing with a CPU we are just kicking off. + * We do the final bit of preparation for the work in + * the target CPUs context. + */ + async_safe_run_on_cpu(cs, mips_cpu_reset_async_work, + RUN_ON_CPU_HOST_PTR(cpc)); } } } diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index 43bbec46cf..3afdbe69c6 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -55,9 +55,17 @@ typedef enum ITCView { ITCVIEW_EF_SYNC = 2, ITCVIEW_EF_TRY = 3, ITCVIEW_PV_SYNC = 4, - ITCVIEW_PV_TRY = 5 + ITCVIEW_PV_TRY = 5, + ITCVIEW_PV_ICR0 = 15, } ITCView; +#define ITC_ICR0_CELL_NUM 16 +#define ITC_ICR0_BLK_GRAIN 8 +#define ITC_ICR0_BLK_GRAIN_MASK 0x7 +#define ITC_ICR0_ERR_AXI 2 +#define ITC_ICR0_ERR_PARITY 1 +#define ITC_ICR0_ERR_EXEC 0 + MemoryRegion *mips_itu_get_tag_region(MIPSITUState *itu) { return &itu->tag_io; @@ -76,7 +84,7 @@ static uint64_t itc_tag_read(void *opaque, hwaddr addr, unsigned size) return tag->ITCAddressMap[index]; } -static void itc_reconfigure(MIPSITUState *tag) +void itc_reconfigure(MIPSITUState *tag) { uint64_t *am = &tag->ITCAddressMap[0]; MemoryRegion *mr = &tag->storage_io; @@ -84,6 +92,12 @@ static void itc_reconfigure(MIPSITUState *tag) uint64_t size = (1 * KiB) + (am[1] & ITC_AM1_ADDR_MASK_MASK); bool is_enabled = (am[0] & ITC_AM0_EN_MASK) != 0; + if (tag->saar_present) { + address = ((*(uint64_t *) tag->saar) & 0xFFFFFFFFE000ULL) << 4; + size = 1ULL << ((*(uint64_t *) tag->saar >> 1) & 0x1f); + is_enabled = *(uint64_t *) tag->saar & 1; + } + memory_region_transaction_begin(); if (!(size & (size - 1))) { memory_region_set_size(mr, size); @@ -142,7 +156,12 @@ static inline ITCView get_itc_view(hwaddr addr) 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); + if (s->saar_present) { + return 7 + ((s->icr0 >> ITC_ICR0_BLK_GRAIN) & + ITC_ICR0_BLK_GRAIN_MASK); + } else { + return 7 + (s->ITCAddressMap[1] & ITC_AM1_ENTRY_GRAIN_MASK); + } } static inline ITCStorageCell *get_cell(MIPSITUState *s, @@ -356,6 +375,12 @@ static void view_pv_try_write(ITCStorageCell *c) view_pv_common_write(c); } +static void raise_exception(int excp) +{ + current_cpu->exception_index = excp; + cpu_loop_exit(current_cpu); +} + static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size) { MIPSITUState *s = (MIPSITUState *)opaque; @@ -363,6 +388,14 @@ static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size) ITCView view = get_itc_view(addr); uint64_t ret = -1; + switch (size) { + case 1: + case 2: + s->icr0 |= 1 << ITC_ICR0_ERR_AXI; + raise_exception(EXCP_DBE); + return 0; + } + switch (view) { case ITCVIEW_BYPASS: ret = view_bypass_read(cell); @@ -382,6 +415,9 @@ static uint64_t itc_storage_read(void *opaque, hwaddr addr, unsigned size) case ITCVIEW_PV_TRY: ret = view_pv_try_read(cell); break; + case ITCVIEW_PV_ICR0: + ret = s->icr0; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "itc_storage_read: Bad ITC View %d\n", (int)view); @@ -398,6 +434,14 @@ static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data, ITCStorageCell *cell = get_cell(s, addr); ITCView view = get_itc_view(addr); + switch (size) { + case 1: + case 2: + s->icr0 |= 1 << ITC_ICR0_ERR_AXI; + raise_exception(EXCP_DBE); + return; + } + switch (view) { case ITCVIEW_BYPASS: view_bypass_write(cell, data); @@ -417,6 +461,15 @@ static void itc_storage_write(void *opaque, hwaddr addr, uint64_t data, case ITCVIEW_PV_TRY: view_pv_try_write(cell); break; + case ITCVIEW_PV_ICR0: + if (data & 0x7) { + /* clear ERROR bits */ + s->icr0 &= ~(data & 0x7); + } + /* set BLK_GRAIN */ + s->icr0 &= ~0x700; + s->icr0 |= data & 0x700; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "itc_storage_write: Bad ITC View %d\n", (int)view); @@ -479,10 +532,15 @@ 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); + if (s->saar_present) { + *(uint64_t *) s->saar = 0x11 << 1; + s->icr0 = get_num_cells(s) << ITC_ICR0_CELL_NUM; + } else { + 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); @@ -493,6 +551,7 @@ static Property mips_itu_properties[] = { ITC_FIFO_NUM_MAX), DEFINE_PROP_INT32("num-semaphores", MIPSITUState, num_semaphores, ITC_SEMAPH_NUM_MAX), + DEFINE_PROP_BOOL("saar-present", MIPSITUState, saar_present, false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/misc/mmio_interface.c b/hw/misc/mmio_interface.c deleted file mode 100644 index 3b0e2039a3..0000000000 --- a/hw/misc/mmio_interface.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * mmio_interface.c - * - * Copyright (C) 2017 : GreenSocs - * http://www.greensocs.com/ , email: info@greensocs.com - * - * Developed by : - * Frederic Konrad - * - * 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 "qemu/log.h" -#include "trace.h" -#include "hw/qdev-properties.h" -#include "hw/misc/mmio_interface.h" -#include "qapi/error.h" - -#ifndef DEBUG_MMIO_INTERFACE -#define DEBUG_MMIO_INTERFACE 0 -#endif - -static uint64_t mmio_interface_counter; - -#define DPRINTF(fmt, ...) do { \ - if (DEBUG_MMIO_INTERFACE) { \ - qemu_log("mmio_interface: 0x%" PRIX64 ": " fmt, s->id, ## __VA_ARGS__);\ - } \ -} while (0) - -static void mmio_interface_init(Object *obj) -{ - MMIOInterface *s = MMIO_INTERFACE(obj); - - if (DEBUG_MMIO_INTERFACE) { - s->id = mmio_interface_counter++; - } - - DPRINTF("interface created\n"); - s->host_ptr = 0; - s->subregion = 0; -} - -static void mmio_interface_realize(DeviceState *dev, Error **errp) -{ - MMIOInterface *s = MMIO_INTERFACE(dev); - - DPRINTF("realize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" - " %p\n", s->start, s->end, s->host_ptr); - - if (!s->host_ptr) { - error_setg(errp, "host_ptr property must be set"); - return; - } - - if (!s->subregion) { - error_setg(errp, "subregion property must be set"); - return; - } - - memory_region_init_ram_ptr(&s->ram_mem, OBJECT(s), "ram", - s->end - s->start + 1, s->host_ptr); - memory_region_set_readonly(&s->ram_mem, s->ro); - memory_region_add_subregion(s->subregion, s->start, &s->ram_mem); -} - -static void mmio_interface_unrealize(DeviceState *dev, Error **errp) -{ - MMIOInterface *s = MMIO_INTERFACE(dev); - - DPRINTF("unrealize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" - " %p\n", s->start, s->end, s->host_ptr); - memory_region_del_subregion(s->subregion, &s->ram_mem); -} - -static void mmio_interface_finalize(Object *obj) -{ - MMIOInterface *s = MMIO_INTERFACE(obj); - - DPRINTF("finalize from 0x%" PRIX64 " to 0x%" PRIX64 " map host pointer" - " %p\n", s->start, s->end, s->host_ptr); - object_unparent(OBJECT(&s->ram_mem)); -} - -static Property mmio_interface_properties[] = { - DEFINE_PROP_UINT64("start", MMIOInterface, start, 0), - DEFINE_PROP_UINT64("end", MMIOInterface, end, 0), - DEFINE_PROP_PTR("host_ptr", MMIOInterface, host_ptr), - DEFINE_PROP_BOOL("ro", MMIOInterface, ro, false), - DEFINE_PROP_MEMORY_REGION("subregion", MMIOInterface, subregion), - DEFINE_PROP_END_OF_LIST(), -}; - -static void mmio_interface_class_init(ObjectClass *oc, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(oc); - - dc->realize = mmio_interface_realize; - dc->unrealize = mmio_interface_unrealize; - dc->props = mmio_interface_properties; - /* Reason: pointer property "host_ptr", and this device - * is an implementation detail of the memory subsystem, - * not intended to be created directly by the user. - */ - dc->user_creatable = false; -} - -static const TypeInfo mmio_interface_info = { - .name = TYPE_MMIO_INTERFACE, - .parent = TYPE_DEVICE, - .instance_size = sizeof(MMIOInterface), - .instance_init = mmio_interface_init, - .instance_finalize = mmio_interface_finalize, - .class_init = mmio_interface_class_init, -}; - -static void mmio_interface_register_types(void) -{ - type_register_static(&mmio_interface_info); -} - -type_init(mmio_interface_register_types) diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 7394a057d8..5cf10ebd66 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -22,6 +22,7 @@ #include "hw/sysbus.h" #include "hw/registerfields.h" #include "hw/misc/mps2-fpgaio.h" +#include "qemu/timer.h" REG32(LED0, 0) REG32(BUTTON, 8) @@ -32,10 +33,92 @@ REG32(PRESCALE, 0x1c) REG32(PSCNTR, 0x20) REG32(MISC, 0x4c) +static uint32_t counter_from_tickoff(int64_t now, int64_t tick_offset, int frq) +{ + return muldiv64(now - tick_offset, frq, NANOSECONDS_PER_SECOND); +} + +static int64_t tickoff_from_counter(int64_t now, uint32_t count, int frq) +{ + return now - muldiv64(count, NANOSECONDS_PER_SECOND, frq); +} + +static void resync_counter(MPS2FPGAIO *s) +{ + /* + * Update s->counter and s->pscntr to their true current values + * by calculating how many times PSCNTR has ticked since the + * last time we did a resync. + */ + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + int64_t elapsed = now - s->pscntr_sync_ticks; + + /* + * Round elapsed down to a whole number of PSCNTR ticks, so we don't + * lose time if we do multiple resyncs in a single tick. + */ + uint64_t ticks = muldiv64(elapsed, s->prescale_clk, NANOSECONDS_PER_SECOND); + + /* + * Work out what PSCNTR and COUNTER have moved to. We assume that + * PSCNTR reloads from PRESCALE one tick-period after it hits zero, + * and that COUNTER increments at the same moment. + */ + if (ticks == 0) { + /* We haven't ticked since the last time we were asked */ + return; + } else if (ticks < s->pscntr) { + /* We haven't yet reached zero, just reduce the PSCNTR */ + s->pscntr -= ticks; + } else { + if (s->prescale == 0) { + /* + * If the reload value is zero then the PSCNTR will stick + * at zero once it reaches it, and so we will increment + * COUNTER every tick after that. + */ + s->counter += ticks - s->pscntr; + s->pscntr = 0; + } else { + /* + * This is the complicated bit. This ASCII art diagram gives an + * example with PRESCALE==5 PSCNTR==7: + * + * ticks 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + * PSCNTR 7 6 5 4 3 2 1 0 5 4 3 2 1 0 5 + * cinc 1 2 + * y 0 1 2 3 4 5 6 7 8 9 10 11 12 + * x 0 1 2 3 4 5 0 1 2 3 4 5 0 + * + * where x = y % (s->prescale + 1) + * and so PSCNTR = s->prescale - x + * and COUNTER is incremented by y / (s->prescale + 1) + * + * The case where PSCNTR < PRESCALE works out the same, + * though we must be careful to calculate y as 64-bit unsigned + * for all parts of the expression. + * y < 0 is not possible because that implies ticks < s->pscntr. + */ + uint64_t y = ticks - s->pscntr + s->prescale; + s->pscntr = s->prescale - (y % (s->prescale + 1)); + s->counter += y / (s->prescale + 1); + } + } + + /* + * Only advance the sync time to the timestamp of the last PSCNTR tick, + * not all the way to 'now', so we don't lose time if we do multiple + * resyncs in a single tick. + */ + s->pscntr_sync_ticks += muldiv64(ticks, NANOSECONDS_PER_SECOND, + s->prescale_clk); +} + static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) { MPS2FPGAIO *s = MPS2_FPGAIO(opaque); uint64_t r; + int64_t now; switch (offset) { case A_LED0: @@ -54,12 +137,20 @@ static uint64_t mps2_fpgaio_read(void *opaque, hwaddr offset, unsigned size) r = s->misc; break; case A_CLK1HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r = counter_from_tickoff(now, s->clk1hz_tick_offset, 1); + break; case A_CLK100HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + r = counter_from_tickoff(now, s->clk100hz_tick_offset, 100); + break; case A_COUNTER: + resync_counter(s); + r = s->counter; + break; case A_PSCNTR: - /* These are all upcounters of various frequencies. */ - qemu_log_mask(LOG_UNIMP, "MPS2 FPGAIO: counters unimplemented\n"); - r = 0; + resync_counter(s); + r = s->pscntr; break; default: qemu_log_mask(LOG_GUEST_ERROR, @@ -76,6 +167,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { MPS2FPGAIO *s = MPS2_FPGAIO(opaque); + int64_t now; trace_mps2_fpgaio_write(offset, value, size); @@ -89,6 +181,7 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, s->led0 = value & 0x3; break; case A_PRESCALE: + resync_counter(s); s->prescale = value; break; case A_MISC: @@ -100,6 +193,22 @@ static void mps2_fpgaio_write(void *opaque, hwaddr offset, uint64_t value, "MPS2 FPGAIO: MISC control bits unimplemented\n"); s->misc = value; break; + case A_CLK1HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->clk1hz_tick_offset = tickoff_from_counter(now, value, 1); + break; + case A_CLK100HZ: + now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + s->clk100hz_tick_offset = tickoff_from_counter(now, value, 100); + break; + case A_COUNTER: + resync_counter(s); + s->counter = value; + break; + case A_PSCNTR: + resync_counter(s); + s->pscntr = value; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "MPS2 FPGAIO write: bad offset 0x%x\n", (int) offset); @@ -116,11 +225,17 @@ static const MemoryRegionOps mps2_fpgaio_ops = { static void mps2_fpgaio_reset(DeviceState *dev) { MPS2FPGAIO *s = MPS2_FPGAIO(dev); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); trace_mps2_fpgaio_reset(); s->led0 = 0; s->prescale = 0; s->misc = 0; + s->clk1hz_tick_offset = tickoff_from_counter(now, 0, 1); + s->clk100hz_tick_offset = tickoff_from_counter(now, 0, 100); + s->counter = 0; + s->pscntr = 0; + s->pscntr_sync_ticks = now; } static void mps2_fpgaio_init(Object *obj) @@ -133,6 +248,27 @@ static void mps2_fpgaio_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } +static bool mps2_fpgaio_counters_needed(void *opaque) +{ + /* Currently vmstate.c insists all subsections have a 'needed' function */ + return true; +} + +static const VMStateDescription mps2_fpgaio_counters_vmstate = { + .name = "mps2-fpgaio/counters", + .version_id = 2, + .minimum_version_id = 2, + .needed = mps2_fpgaio_counters_needed, + .fields = (VMStateField[]) { + VMSTATE_INT64(clk1hz_tick_offset, MPS2FPGAIO), + VMSTATE_INT64(clk100hz_tick_offset, MPS2FPGAIO), + VMSTATE_UINT32(counter, MPS2FPGAIO), + VMSTATE_UINT32(pscntr, MPS2FPGAIO), + VMSTATE_INT64(pscntr_sync_ticks, MPS2FPGAIO), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription mps2_fpgaio_vmstate = { .name = "mps2-fpgaio", .version_id = 1, @@ -142,6 +278,10 @@ static const VMStateDescription mps2_fpgaio_vmstate = { VMSTATE_UINT32(prescale, MPS2FPGAIO), VMSTATE_UINT32(misc, MPS2FPGAIO), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription*[]) { + &mps2_fpgaio_counters_vmstate, + NULL } }; diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c new file mode 100644 index 0000000000..d188f044f4 --- /dev/null +++ b/hw/misc/nrf51_rng.c @@ -0,0 +1,262 @@ +/* + * nRF51 Random Number Generator + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf + * + * Copyright 2018 Steffen Görtz + * + * 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 "qemu/log.h" +#include "qapi/error.h" +#include "hw/arm/nrf51.h" +#include "hw/misc/nrf51_rng.h" +#include "crypto/random.h" + +static void update_irq(NRF51RNGState *s) +{ + bool irq = s->interrupt_enabled && s->event_valrdy; + qemu_set_irq(s->irq, irq); +} + +static uint64_t rng_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + uint64_t r = 0; + + switch (offset) { + case NRF51_RNG_EVENT_VALRDY: + r = s->event_valrdy; + break; + case NRF51_RNG_REG_SHORTS: + r = s->shortcut_stop_on_valrdy; + break; + case NRF51_RNG_REG_INTEN: + case NRF51_RNG_REG_INTENSET: + case NRF51_RNG_REG_INTENCLR: + r = s->interrupt_enabled; + break; + case NRF51_RNG_REG_CONFIG: + r = s->filter_enabled; + break; + case NRF51_RNG_REG_VALUE: + r = s->value; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + return r; +} + +static int64_t calc_next_timeout(NRF51RNGState *s) +{ + int64_t timeout = qemu_clock_get_us(QEMU_CLOCK_VIRTUAL); + if (s->filter_enabled) { + timeout += s->period_filtered_us; + } else { + timeout += s->period_unfiltered_us; + } + + return timeout; +} + + +static void rng_update_timer(NRF51RNGState *s) +{ + if (s->active) { + timer_mod(&s->timer, calc_next_timeout(s)); + } else { + timer_del(&s->timer); + } +} + + +static void rng_write(void *opaque, hwaddr offset, + uint64_t value, unsigned int size) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + switch (offset) { + case NRF51_RNG_TASK_START: + if (value == NRF51_TRIGGER_TASK) { + s->active = 1; + rng_update_timer(s); + } + break; + case NRF51_RNG_TASK_STOP: + if (value == NRF51_TRIGGER_TASK) { + s->active = 0; + rng_update_timer(s); + } + break; + case NRF51_RNG_EVENT_VALRDY: + if (value == NRF51_EVENT_CLEAR) { + s->event_valrdy = 0; + } + break; + case NRF51_RNG_REG_SHORTS: + s->shortcut_stop_on_valrdy = + (value & BIT_MASK(NRF51_RNG_REG_SHORTS_VALRDY_STOP)) ? 1 : 0; + break; + case NRF51_RNG_REG_INTEN: + s->interrupt_enabled = + (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) ? 1 : 0; + break; + case NRF51_RNG_REG_INTENSET: + if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { + s->interrupt_enabled = 1; + } + break; + case NRF51_RNG_REG_INTENCLR: + if (value & BIT_MASK(NRF51_RNG_REG_INTEN_VALRDY)) { + s->interrupt_enabled = 0; + } + break; + case NRF51_RNG_REG_CONFIG: + s->filter_enabled = + (value & BIT_MASK(NRF51_RNG_REG_CONFIG_DECEN)) ? 1 : 0; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + update_irq(s); +} + +static const MemoryRegionOps rng_ops = { + .read = rng_read, + .write = rng_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4 +}; + +static void nrf51_rng_timer_expire(void *opaque) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + qcrypto_random_bytes(&s->value, 1, &error_abort); + + s->event_valrdy = 1; + qemu_set_irq(s->eep_valrdy, 1); + + if (s->shortcut_stop_on_valrdy) { + s->active = 0; + } + + rng_update_timer(s); + update_irq(s); +} + +static void nrf51_rng_tep_start(void *opaque, int n, int level) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + if (level) { + s->active = 1; + rng_update_timer(s); + } +} + +static void nrf51_rng_tep_stop(void *opaque, int n, int level) +{ + NRF51RNGState *s = NRF51_RNG(opaque); + + if (level) { + s->active = 0; + rng_update_timer(s); + } +} + + +static void nrf51_rng_init(Object *obj) +{ + NRF51RNGState *s = NRF51_RNG(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mmio, obj, &rng_ops, s, + TYPE_NRF51_RNG, NRF51_RNG_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + timer_init_us(&s->timer, QEMU_CLOCK_VIRTUAL, nrf51_rng_timer_expire, s); + + sysbus_init_irq(sbd, &s->irq); + + /* Tasks */ + qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_start, "tep_start", 1); + qdev_init_gpio_in_named(DEVICE(s), nrf51_rng_tep_stop, "tep_stop", 1); + + /* Events */ + qdev_init_gpio_out_named(DEVICE(s), &s->eep_valrdy, "eep_valrdy", 1); +} + +static void nrf51_rng_reset(DeviceState *dev) +{ + NRF51RNGState *s = NRF51_RNG(dev); + + s->value = 0; + s->active = 0; + s->event_valrdy = 0; + s->shortcut_stop_on_valrdy = 0; + s->interrupt_enabled = 0; + s->filter_enabled = 0; + + rng_update_timer(s); +} + + +static Property nrf51_rng_properties[] = { + DEFINE_PROP_UINT16("period_unfiltered_us", NRF51RNGState, + period_unfiltered_us, 167), + DEFINE_PROP_UINT16("period_filtered_us", NRF51RNGState, + period_filtered_us, 660), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_rng = { + .name = "nrf51_soc.rng", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(active, NRF51RNGState), + VMSTATE_UINT32(event_valrdy, NRF51RNGState), + VMSTATE_UINT32(shortcut_stop_on_valrdy, NRF51RNGState), + VMSTATE_UINT32(interrupt_enabled, NRF51RNGState), + VMSTATE_UINT32(filter_enabled, NRF51RNGState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = nrf51_rng_properties; + dc->vmsd = &vmstate_rng; + dc->reset = nrf51_rng_reset; +} + +static const TypeInfo nrf51_rng_info = { + .name = TYPE_NRF51_RNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51RNGState), + .instance_init = nrf51_rng_init, + .class_init = nrf51_rng_class_init +}; + +static void nrf51_rng_register_types(void) +{ + type_register_static(&nrf51_rng_info); +} + +type_init(nrf51_rng_register_types) diff --git a/hw/misc/omap_l4.c b/hw/misc/omap_l4.c index 96fc057b4e..c217728c78 100644 --- a/hw/misc/omap_l4.c +++ b/hw/misc/omap_l4.c @@ -112,8 +112,8 @@ static const MemoryRegionOps omap_l4ta_ops = { struct omap_target_agent_s *omap_l4ta_get(struct omap_l4_s *bus, const struct omap_l4_region_s *regions, - const struct omap_l4_agent_info_s *agents, - int cs) + const struct omap_l4_agent_info_s *agents, + int cs) { int i; struct omap_target_agent_s *ta = NULL; diff --git a/hw/misc/pc-testdev.c b/hw/misc/pc-testdev.c index b81d820084..0aee04f231 100644 --- a/hw/misc/pc-testdev.c +++ b/hw/misc/pc-testdev.c @@ -32,7 +32,7 @@ * -kernel /home/lmr/Code/virt-test.git/kvm/unittests/msr.flat * * Where msr.flat is one of the KVM unittests, present on a separate repo, - * git://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git + * https://git.kernel.org/pub/scm/virt/kvm/kvm-unit-tests.git */ #include "qemu/osdep.h" @@ -58,7 +58,12 @@ typedef struct PCTestdev { #define TESTDEV(obj) \ OBJECT_CHECK(PCTestdev, (obj), TYPE_TESTDEV) -static void test_irq_line(void *opaque, hwaddr addr, uint64_t data, +static uint64_t test_irq_line_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + +static void test_irq_line_write(void *opaque, hwaddr addr, uint64_t data, unsigned len) { PCTestdev *dev = opaque; @@ -68,7 +73,8 @@ static void test_irq_line(void *opaque, hwaddr addr, uint64_t data, } static const MemoryRegionOps test_irq_ops = { - .write = test_irq_line, + .read = test_irq_line_read, + .write = test_irq_line_write, .valid.min_access_size = 1, .valid.max_access_size = 1, .endianness = DEVICE_LITTLE_ENDIAN, @@ -110,7 +116,12 @@ static const MemoryRegionOps test_ioport_byte_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void test_flush_page(void *opaque, hwaddr addr, uint64_t data, +static uint64_t test_flush_page_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + +static void test_flush_page_write(void *opaque, hwaddr addr, uint64_t data, unsigned len) { hwaddr page = 4096; @@ -126,7 +137,8 @@ static void test_flush_page(void *opaque, hwaddr addr, uint64_t data, } static const MemoryRegionOps test_flush_ops = { - .write = test_flush_page, + .read = test_flush_page_read, + .write = test_flush_page_write, .valid.min_access_size = 4, .valid.max_access_size = 4, .endianness = DEVICE_LITTLE_ENDIAN, diff --git a/hw/misc/pca9552.c b/hw/misc/pca9552.c index 9775d5274a..7325d3f287 100644 --- a/hw/misc/pca9552.c +++ b/hw/misc/pca9552.c @@ -115,7 +115,7 @@ static void pca9552_autoinc(PCA9552State *s) } } -static int pca9552_recv(I2CSlave *i2c) +static uint8_t pca9552_recv(I2CSlave *i2c) { PCA9552State *s = PCA9552(i2c); uint8_t ret; diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 32041f535f..1282d151cb 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -85,6 +85,9 @@ typedef struct PCITestDevState { MemoryRegion portio; IOTest *tests; int current; + + uint64_t membar_size; + MemoryRegion membar; } PCITestDevState; #define TYPE_PCI_TEST_DEV "pci-testdev" @@ -253,6 +256,16 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &d->mmio); pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); + if (d->membar_size) { + memory_region_init(&d->membar, OBJECT(d), "pci-testdev-membar", + d->membar_size); + pci_register_bar(pci_dev, 2, + PCI_BASE_ADDRESS_SPACE_MEMORY | + PCI_BASE_ADDRESS_MEM_PREFETCH | + PCI_BASE_ADDRESS_MEM_TYPE_64, + &d->membar); + } + d->current = -1; d->tests = g_malloc0(IOTEST_MAX * sizeof *d->tests); for (i = 0; i < IOTEST_MAX; ++i) { @@ -305,6 +318,11 @@ static void qdev_pci_testdev_reset(DeviceState *dev) pci_testdev_reset(d); } +static Property pci_testdev_properties[] = { + DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0), + DEFINE_PROP_END_OF_LIST(), +}; + static void pci_testdev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -319,6 +337,7 @@ static void pci_testdev_class_init(ObjectClass *klass, void *data) dc->desc = "PCI Test Device"; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->reset = qdev_pci_testdev_reset; + dc->props = pci_testdev_properties; } static const TypeInfo pci_testdev_info = { diff --git a/hw/misc/puv3_pm.c b/hw/misc/puv3_pm.c index 577cebaac7..afe191fbe1 100644 --- a/hw/misc/puv3_pm.c +++ b/hw/misc/puv3_pm.c @@ -119,7 +119,7 @@ static const MemoryRegionOps puv3_pm_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static int puv3_pm_init(SysBusDevice *dev) +static void puv3_pm_realize(DeviceState *dev, Error **errp) { PUV3PMState *s = PUV3_PM(dev); @@ -127,16 +127,14 @@ static int puv3_pm_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &puv3_pm_ops, s, "puv3_pm", PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } static void puv3_pm_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = puv3_pm_init; + dc->realize = puv3_pm_realize; } static const TypeInfo puv3_pm_info = { diff --git a/hw/misc/pvpanic.c b/hw/misc/pvpanic.c index b26250dec9..9d8961ba0c 100644 --- a/hw/misc/pvpanic.c +++ b/hw/misc/pvpanic.c @@ -99,17 +99,6 @@ static void pvpanic_isa_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(d, &s->io, s->ioport); } -#define PVPANIC_IOPORT_PROP "ioport" - -uint16_t pvpanic_port(void) -{ - Object *o = object_resolve_path_type("", TYPE_PVPANIC, NULL); - if (!o) { - return 0; - } - return object_property_get_uint(o, PVPANIC_IOPORT_PROP, NULL); -} - static Property pvpanic_isa_properties[] = { DEFINE_PROP_UINT16(PVPANIC_IOPORT_PROP, PVPanicState, ioport, 0x505), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/misc/tmp105.c b/hw/misc/tmp105.c index 0918f3a6ea..0c32f6f8b6 100644 --- a/hw/misc/tmp105.c +++ b/hw/misc/tmp105.c @@ -79,7 +79,7 @@ static void tmp105_set_temperature(Object *obj, Visitor *v, const char *name, return; } if (temp >= 128000 || temp < -128000) { - error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range", + error_setg(errp, "value %" PRId64 ".%03" PRIu64 " C is out of range", temp / 1000, temp % 1000); return; } @@ -147,7 +147,7 @@ static void tmp105_write(TMP105State *s) } } -static int tmp105_rx(I2CSlave *i2c) +static uint8_t tmp105_rx(I2CSlave *i2c) { TMP105State *s = TMP105(i2c); diff --git a/hw/misc/tmp421.c b/hw/misc/tmp421.c index c234044305..ce6d40ac9c 100644 --- a/hw/misc/tmp421.c +++ b/hw/misc/tmp421.c @@ -153,7 +153,7 @@ static void tmp421_set_temperature(Object *obj, Visitor *v, const char *name, } if (temp >= maxs[ext_range] || temp < mins[ext_range]) { - error_setg(errp, "value %" PRId64 ".%03" PRIu64 " °C is out of range", + error_setg(errp, "value %" PRId64 ".%03" PRIu64 " C is out of range", temp / 1000, temp % 1000); return; } @@ -249,7 +249,7 @@ static void tmp421_write(TMP421State *s) } } -static int tmp421_rx(I2CSlave *i2c) +static uint8_t tmp421_rx(I2CSlave *i2c) { TMP421State *s = TMP421(i2c); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index c956e1419b..47e1bccf71 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/misc/eccmemctl.c +# eccmemctl.c ecc_mem_writel_mer(uint32_t val) "Write memory enable 0x%08x" ecc_mem_writel_mdr(uint32_t val) "Write memory delay 0x%08x" ecc_mem_writel_mfsr(uint32_t val) "Write memory fault status 0x%08x" @@ -20,7 +20,7 @@ ecc_mem_readl_ecr1(uint32_t ret) "Read event count 2 0x%08x" ecc_diag_mem_writeb(uint64_t addr, uint32_t val) "Write diagnostic %"PRId64" = 0x%02x" ecc_diag_mem_readb(uint64_t addr, uint32_t ret) "Read diagnostic %"PRId64"= 0x%02x" -# hw/misc/slavio_misc.c +# slavio_misc.c slavio_misc_update_irq_raise(void) "Raise IRQ" slavio_misc_update_irq_lower(void) "Lower IRQ" slavio_set_power_fail(int power_failing, uint8_t config) "Power fail: %d, config: %d" @@ -41,20 +41,20 @@ slavio_sysctrl_mem_readl(uint32_t ret) "Read system control 0x%08x" slavio_led_mem_writew(uint32_t val) "Write diagnostic LED 0x%04x" slavio_led_mem_readw(uint32_t ret) "Read diagnostic LED 0x%04x" -# hw/misc/milkymist-hpdmc.c +# milkymist-hpdmc.c milkymist_hpdmc_memory_read(uint32_t addr, uint32_t value) "addr=0x%08x value=0x%08x" milkymist_hpdmc_memory_write(uint32_t addr, uint32_t value) "addr=0x%08x value=0x%08x" -# hw/misc/milkymist-pfpu.c +# milkymist-pfpu.c milkymist_pfpu_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_pfpu_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_pfpu_vectout(uint32_t a, uint32_t b, uint32_t dma_ptr) "a 0x%08x b 0x%08x dma_ptr 0x%08x" milkymist_pfpu_pulse_irq(void) "Pulse IRQ" -# hw/misc/aspeed_scu.c +# aspeed_scu.c aspeed_scu_write(uint64_t offset, unsigned size, uint32_t data) "To 0x%" PRIx64 " of size %u: 0x%" PRIx32 -# hw/misc/mps2_scc.c +# mps2-scc.c mps2_scc_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_scc_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 SCC write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_scc_reset(void) "MPS2 SCC: reset" @@ -62,29 +62,29 @@ mps2_scc_leds(char led7, char led6, char led5, char led4, char led3, char led2, mps2_scc_cfg_write(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config write: function %d device %d data 0x%" PRIx32 mps2_scc_cfg_read(unsigned function, unsigned device, uint32_t value) "MPS2 SCC config read: function %d device %d data 0x%" PRIx32 -# hw/misc/mps2_fpgaio.c +# mps2-fpgaio.c mps2_fpgaio_read(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_fpgaio_write(uint64_t offset, uint64_t data, unsigned size) "MPS2 FPGAIO write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" mps2_fpgaio_reset(void) "MPS2 FPGAIO: reset" mps2_fpgaio_leds(char led1, char led0) "MPS2 FPGAIO LEDs: %c%c" -# hw/misc/msf2-sysreg.c +# msf2-sysreg.c msf2_sysreg_write(uint64_t offset, uint32_t val, uint32_t prev) "msf2-sysreg write: addr 0x%08" PRIx64 " data 0x%" PRIx32 " prev 0x%" PRIx32 msf2_sysreg_read(uint64_t offset, uint32_t val) "msf2-sysreg read: addr 0x%08" PRIx64 " data 0x%08" PRIx32 msf2_sysreg_write_pll_status(void) "Invalid write to read only PLL status register" -#hw/misc/imx7_gpr.c +# imx7_gpr.c imx7_gpr_read(uint64_t offset) "addr 0x%08" PRIx64 imx7_gpr_write(uint64_t offset, uint64_t value) "addr 0x%08" PRIx64 "value 0x%08" PRIx64 -# hw/misc/mos6522.c +# mos6522.c mos6522_set_counter(int index, unsigned int val) "T%d.counter=%d" mos6522_get_next_irq_time(uint16_t latch, int64_t d, int64_t delta) "latch=%d counter=0x%"PRId64 " delta_next=0x%"PRId64 mos6522_set_sr_int(void) "set sr_int" mos6522_write(uint64_t addr, uint64_t val) "reg=0x%"PRIx64 " val=0x%"PRIx64 mos6522_read(uint64_t addr, unsigned val) "reg=0x%"PRIx64 " val=0x%x" -# hw/misc/tz-mpc.c +# tz-mpc.c tz_mpc_reg_read(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs read: offset 0x%x data 0x%" PRIx64 " size %u" tz_mpc_reg_write(uint32_t offset, uint64_t data, unsigned size) "TZ MPC regs write: offset 0x%x data 0x%" PRIx64 " size %u" tz_mpc_mem_blocked_read(uint64_t addr, unsigned size, bool secure) "TZ MPC blocked read: offset 0x%" PRIx64 " size %u secure %d" @@ -92,7 +92,15 @@ tz_mpc_mem_blocked_write(uint64_t addr, uint64_t data, unsigned size, bool secur tz_mpc_translate(uint64_t addr, int flags, const char *idx, const char *res) "TZ MPC translate: addr 0x%" PRIx64 " flags 0x%x iommu_idx %s: %s" tz_mpc_iommu_notify(uint64_t addr) "TZ MPC iommu: notifying UNMAP/MAP for 0x%" PRIx64 -# hw/misc/tz-ppc.c +# tz-msc.c +tz_msc_reset(void) "TZ MSC: reset" +tz_msc_cfg_nonsec(int level) "TZ MSC: cfg_nonsec = %d" +tz_msc_cfg_sec_resp(int level) "TZ MSC: cfg_sec_resp = %d" +tz_msc_irq_clear(int level) "TZ MSC: int_clear = %d" +tz_msc_update_irq(int level) "TZ MSC: setting irq line to %d" +tz_msc_access_blocked(uint64_t offset) "TZ MSC: offset 0x%" PRIx64 " access blocked" + +# tz-ppc.c tz_ppc_reset(void) "TZ PPC: reset" tz_ppc_cfg_nonsec(int n, int level) "TZ PPC: cfg_nonsec[%d] = %d" tz_ppc_cfg_ap(int n, int level) "TZ PPC: cfg_ap[%d] = %d" @@ -103,9 +111,32 @@ tz_ppc_update_irq(int level) "TZ PPC: setting irq line to %d" tz_ppc_read_blocked(int n, uint64_t offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" PRIx64 " read (secure %d user %d) blocked" tz_ppc_write_blocked(int n, uint64_t offset, bool secure, bool user) "TZ PPC: port %d offset 0x%" PRIx64 " write (secure %d user %d) blocked" -# hw/misc/iotkit-secctl.c +# iotkit-secctl.c iotkit_secctl_s_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs read: offset 0x%x data 0x%" PRIx64 " size %u" iotkit_secctl_s_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl S regs write: offset 0x%x data 0x%" PRIx64 " size %u" iotkit_secctl_ns_read(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs read: offset 0x%x data 0x%" PRIx64 " size %u" iotkit_secctl_ns_write(uint32_t offset, uint64_t data, unsigned size) "IoTKit SecCtl NS regs write: offset 0x%x data 0x%" PRIx64 " size %u" -iotkit_secctl_reset(void) "IoTKit SecCtl: reset" + +# imx6ul_ccm.c +ccm_entry(void) "\n" +ccm_freq(uint32_t freq) "freq = %d\n" +ccm_clock_freq(uint32_t clock, uint32_t freq) "(Clock = %d) = %d\n" +ccm_read_reg(const char *reg_name, uint32_t value) "reg[%s] <= 0x%" PRIx32 "\n" +ccm_write_reg(const char *reg_name, uint32_t value) "reg[%s] => 0x%" PRIx32 "\n" + +# iotkit-sysinfo.c +iotkit_sysinfo_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysinfo_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysInfo write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" + +# iotkit-sysctl.c +iotkit_sysctl_read(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_write(uint64_t offset, uint64_t data, unsigned size) "IoTKit SysCtl write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +iotkit_sysctl_reset(void) "IoTKit SysCtl: reset" + +# armsse-cpuid.c +armsse_cpuid_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +armsse_cpuid_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 CPU_IDENTITY write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" + +# armsse-mhu.c +armsse_mhu_read(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +armsse_mhu_write(uint64_t offset, uint64_t data, unsigned size) "SSE-200 MHU write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index e0c58ba37e..9a84be75ed 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -150,7 +150,7 @@ static MemTxResult tz_mpc_reg_read(void *opaque, hwaddr addr, r = s->ctrl; break; case A_BLK_MAX: - r = s->blk_max; + r = s->blk_max - 1; break; case A_BLK_CFG: /* We are never in "init in progress state", so this just indicates @@ -448,7 +448,7 @@ static int tz_mpc_attrs_to_index(IOMMUMemoryRegion *iommu, MemTxAttrs attrs) { /* We treat unspecified attributes like secure. Transactions with * unspecified attributes come from places like - * cpu_physical_memory_write_rom() for initial image load, and we want + * rom_reset() for initial image load, and we want * those to pass through the from-reset "everything is secure" config. * All the real during-emulation transactions from the CPU will * specify attributes. diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c new file mode 100644 index 0000000000..9e352044ea --- /dev/null +++ b/hw/misc/tz-msc.c @@ -0,0 +1,308 @@ +/* + * ARM TrustZone master security controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "qapi/error.h" +#include "trace.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/misc/tz-msc.h" + +static void tz_msc_update_irq(TZMSC *s) +{ + bool level = s->irq_status; + + trace_tz_msc_update_irq(level); + qemu_set_irq(s->irq, level); +} + +static void tz_msc_cfg_nonsec(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_cfg_nonsec(level); + s->cfg_nonsec = level; +} + +static void tz_msc_cfg_sec_resp(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_cfg_sec_resp(level); + s->cfg_sec_resp = level; +} + +static void tz_msc_irq_clear(void *opaque, int n, int level) +{ + TZMSC *s = TZ_MSC(opaque); + + trace_tz_msc_irq_clear(level); + + s->irq_clear = level; + if (level) { + s->irq_status = false; + tz_msc_update_irq(s); + } +} + +/* The MSC may either block a transaction by aborting it, block a + * transaction by making it RAZ/WI, allow it through with + * MemTxAttrs indicating a secure transaction, or allow it with + * MemTxAttrs indicating a non-secure transaction. + */ +typedef enum MSCAction { + MSCBlockAbort, + MSCBlockRAZWI, + MSCAllowSecure, + MSCAllowNonSecure, +} MSCAction; + +static MSCAction tz_msc_check(TZMSC *s, hwaddr addr) +{ + /* + * Check whether to allow an access from the bus master, returning + * an MSCAction indicating the required behaviour. If the transaction + * is blocked, the caller must check cfg_sec_resp to determine + * whether to abort or RAZ/WI the transaction. + */ + IDAUInterfaceClass *iic = IDAU_INTERFACE_GET_CLASS(s->idau); + IDAUInterface *ii = IDAU_INTERFACE(s->idau); + bool idau_exempt = false, idau_ns = true, idau_nsc = true; + int idau_region = IREGION_NOTVALID; + + iic->check(ii, addr, &idau_region, &idau_exempt, &idau_ns, &idau_nsc); + + if (idau_exempt) { + /* + * Uncheck region -- OK, transaction type depends on + * whether bus master is configured as Secure or NonSecure + */ + return s->cfg_nonsec ? MSCAllowNonSecure : MSCAllowSecure; + } + + if (idau_ns) { + /* NonSecure region -- always forward as NS transaction */ + return MSCAllowNonSecure; + } + + if (!s->cfg_nonsec) { + /* Access to Secure region by Secure bus master: OK */ + return MSCAllowSecure; + } + + /* Attempted access to Secure region by NS bus master: block */ + trace_tz_msc_access_blocked(addr); + if (!s->cfg_sec_resp) { + return MSCBlockRAZWI; + } + + /* + * The TRM isn't clear on behaviour if irq_clear is high when a + * transaction is blocked. We assume that the MSC behaves like the + * PPC, where holding irq_clear high suppresses the interrupt. + */ + if (!s->irq_clear) { + s->irq_status = true; + tz_msc_update_irq(s); + } + return MSCBlockAbort; +} + +static MemTxResult tz_msc_read(void *opaque, hwaddr addr, uint64_t *pdata, + unsigned size, MemTxAttrs attrs) +{ + TZMSC *s = opaque; + AddressSpace *as = &s->downstream_as; + uint64_t data; + MemTxResult res; + + switch (tz_msc_check(s, addr)) { + case MSCBlockAbort: + return MEMTX_ERROR; + case MSCBlockRAZWI: + *pdata = 0; + return MEMTX_OK; + case MSCAllowSecure: + attrs.secure = 1; + attrs.unspecified = 0; + break; + case MSCAllowNonSecure: + attrs.secure = 0; + attrs.unspecified = 0; + break; + } + + switch (size) { + case 1: + data = address_space_ldub(as, addr, attrs, &res); + break; + case 2: + data = address_space_lduw_le(as, addr, attrs, &res); + break; + case 4: + data = address_space_ldl_le(as, addr, attrs, &res); + break; + case 8: + data = address_space_ldq_le(as, addr, attrs, &res); + break; + default: + g_assert_not_reached(); + } + *pdata = data; + return res; +} + +static MemTxResult tz_msc_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size, MemTxAttrs attrs) +{ + TZMSC *s = opaque; + AddressSpace *as = &s->downstream_as; + MemTxResult res; + + switch (tz_msc_check(s, addr)) { + case MSCBlockAbort: + return MEMTX_ERROR; + case MSCBlockRAZWI: + return MEMTX_OK; + case MSCAllowSecure: + attrs.secure = 1; + attrs.unspecified = 0; + break; + case MSCAllowNonSecure: + attrs.secure = 0; + attrs.unspecified = 0; + break; + } + + switch (size) { + case 1: + address_space_stb(as, addr, val, attrs, &res); + break; + case 2: + address_space_stw_le(as, addr, val, attrs, &res); + break; + case 4: + address_space_stl_le(as, addr, val, attrs, &res); + break; + case 8: + address_space_stq_le(as, addr, val, attrs, &res); + break; + default: + g_assert_not_reached(); + } + return res; +} + +static const MemoryRegionOps tz_msc_ops = { + .read_with_attrs = tz_msc_read, + .write_with_attrs = tz_msc_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void tz_msc_reset(DeviceState *dev) +{ + TZMSC *s = TZ_MSC(dev); + + trace_tz_msc_reset(); + s->cfg_sec_resp = false; + s->cfg_nonsec = false; + s->irq_clear = 0; + s->irq_status = 0; +} + +static void tz_msc_init(Object *obj) +{ + DeviceState *dev = DEVICE(obj); + TZMSC *s = TZ_MSC(obj); + + qdev_init_gpio_in_named(dev, tz_msc_cfg_nonsec, "cfg_nonsec", 1); + qdev_init_gpio_in_named(dev, tz_msc_cfg_sec_resp, "cfg_sec_resp", 1); + qdev_init_gpio_in_named(dev, tz_msc_irq_clear, "irq_clear", 1); + qdev_init_gpio_out_named(dev, &s->irq, "irq", 1); +} + +static void tz_msc_realize(DeviceState *dev, Error **errp) +{ + Object *obj = OBJECT(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + TZMSC *s = TZ_MSC(dev); + const char *name = "tz-msc-downstream"; + uint64_t size; + + /* + * We can't create the upstream end of the port until realize, + * as we don't know the size of the MR used as the downstream until then. + * We insist on having a downstream, to avoid complicating the + * code with handling the "don't know how big this is" case. It's easy + * enough for the user to create an unimplemented_device as downstream + * if they have nothing else to plug into this. + */ + if (!s->downstream) { + error_setg(errp, "MSC 'downstream' link not set"); + return; + } + if (!s->idau) { + error_setg(errp, "MSC 'idau' link not set"); + return; + } + + size = memory_region_size(s->downstream); + address_space_init(&s->downstream_as, s->downstream, name); + memory_region_init_io(&s->upstream, obj, &tz_msc_ops, s, name, size); + sysbus_init_mmio(sbd, &s->upstream); +} + +static const VMStateDescription tz_msc_vmstate = { + .name = "tz-msc", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_BOOL(cfg_nonsec, TZMSC), + VMSTATE_BOOL(cfg_sec_resp, TZMSC), + VMSTATE_BOOL(irq_clear, TZMSC), + VMSTATE_BOOL(irq_status, TZMSC), + VMSTATE_END_OF_LIST() + } +}; + +static Property tz_msc_properties[] = { + DEFINE_PROP_LINK("downstream", TZMSC, downstream, + TYPE_MEMORY_REGION, MemoryRegion *), + DEFINE_PROP_LINK("idau", TZMSC, idau, + TYPE_IDAU_INTERFACE, IDAUInterface *), + DEFINE_PROP_END_OF_LIST(), +}; + +static void tz_msc_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = tz_msc_realize; + dc->vmsd = &tz_msc_vmstate; + dc->reset = tz_msc_reset; + dc->props = tz_msc_properties; +} + +static const TypeInfo tz_msc_info = { + .name = TYPE_TZ_MSC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(TZMSC), + .instance_init = tz_msc_init, + .class_init = tz_msc_class_init, +}; + +static void tz_msc_register_types(void) +{ + type_register_static(&tz_msc_info); +} + +type_init(tz_msc_register_types); diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 3dd045c15f..2e04837bea 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -181,6 +181,21 @@ static const MemoryRegionOps tz_ppc_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; +static bool tz_ppc_dummy_accepts(void *opaque, hwaddr addr, + unsigned size, bool is_write, + MemTxAttrs attrs) +{ + /* + * Board code should never map the upstream end of an unused port, + * so we should never try to make a memory access to it. + */ + g_assert_not_reached(); +} + +static const MemoryRegionOps tz_ppc_dummy_ops = { + .valid.accepts = tz_ppc_dummy_accepts, +}; + static void tz_ppc_reset(DeviceState *dev) { TZPPC *s = TZ_PPC(dev); @@ -210,16 +225,33 @@ static void tz_ppc_realize(DeviceState *dev, Error **errp) SysBusDevice *sbd = SYS_BUS_DEVICE(dev); TZPPC *s = TZ_PPC(dev); int i; + int max_port = 0; /* We can't create the upstream end of the port until realize, * as we don't know the size of the MR used as the downstream until then. */ for (i = 0; i < TZ_NUM_PORTS; i++) { + if (s->port[i].downstream) { + max_port = i; + } + } + + for (i = 0; i <= max_port; i++) { TZPPCPort *port = &s->port[i]; char *name; uint64_t size; if (!port->downstream) { + /* + * Create dummy sysbus MMIO region so the sysbus region + * numbering doesn't get out of sync with the port numbers. + * The size is entirely arbitrary. + */ + name = g_strdup_printf("tz-ppc-dummy-port[%d]", i); + memory_region_init_io(&port->upstream, obj, &tz_ppc_dummy_ops, + port, name, 0x10000); + sysbus_init_mmio(sbd, &port->upstream); + g_free(name); continue; } diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index a2805527cb..304c6287c7 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -19,7 +19,7 @@ static void fw_cfg_vmci_write(void *dev, off_t offset, size_t len) VMCoreInfoState *s = VMCOREINFO(dev); s->has_vmcoreinfo = offset == 0 && len == sizeof(s->vmcoreinfo) - && s->vmcoreinfo.guest_format != VMCOREINFO_FORMAT_NONE; + && s->vmcoreinfo.guest_format != FW_CFG_VMCOREINFO_FORMAT_NONE; } static void vmcoreinfo_reset(void *dev) @@ -28,7 +28,7 @@ static void vmcoreinfo_reset(void *dev) s->has_vmcoreinfo = false; memset(&s->vmcoreinfo, 0, sizeof(s->vmcoreinfo)); - s->vmcoreinfo.host_format = cpu_to_le16(VMCOREINFO_FORMAT_ELF); + s->vmcoreinfo.host_format = cpu_to_le16(FW_CFG_VMCOREINFO_FORMAT_ELF); } static void vmcoreinfo_realize(DeviceState *dev, Error **errp) @@ -53,7 +53,7 @@ static void vmcoreinfo_realize(DeviceState *dev, Error **errp) return; } - fw_cfg_add_file_callback(fw_cfg, "etc/vmcoreinfo", + fw_cfg_add_file_callback(fw_cfg, FW_CFG_VMCOREINFO_FILENAME, NULL, fw_cfg_vmci_write, s, &s->vmcoreinfo, sizeof(s->vmcoreinfo), false); diff --git a/hw/moxie/Kconfig b/hw/moxie/Kconfig new file mode 100644 index 0000000000..3793ef0372 --- /dev/null +++ b/hw/moxie/Kconfig @@ -0,0 +1,3 @@ +config MOXIESIM + bool + select SERIAL diff --git a/hw/moxie/Makefile.objs b/hw/moxie/Makefile.objs index bfc90012fd..ddbf300f54 100644 --- a/hw/moxie/Makefile.objs +++ b/hw/moxie/Makefile.objs @@ -1,2 +1,2 @@ # moxie boards -obj-y += moxiesim.o +obj-$(CONFIG_MOXIESIM) += moxiesim.o diff --git a/hw/moxie/moxiesim.c b/hw/moxie/moxiesim.c index d41247dbdc..eddeed915d 100644 --- a/hw/moxie/moxiesim.c +++ b/hw/moxie/moxiesim.c @@ -31,7 +31,6 @@ #include "cpu.h" #include "hw/sysbus.h" #include "hw/hw.h" -#include "hw/isa/isa.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -54,11 +53,11 @@ typedef struct { static void load_kernel(MoxieCPU *cpu, LoaderParams *loader_params) { uint64_t entry, kernel_low, kernel_high; + int64_t initrd_size; long kernel_size; - long initrd_size; ram_addr_t initrd_offset; - kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, + kernel_size = load_elf(loader_params->kernel_filename, NULL, NULL, NULL, &entry, &kernel_low, &kernel_high, 1, EM_MOXIE, 0, 0); diff --git a/hw/net/Kconfig b/hw/net/Kconfig new file mode 100644 index 0000000000..7d7bbc5d7c --- /dev/null +++ b/hw/net/Kconfig @@ -0,0 +1,125 @@ +config DP8393X + bool + +config NE2000_PCI + bool + default y if PCI_DEVICES + depends on PCI + +config EEPRO100_PCI + bool + default y if PCI_DEVICES + depends on PCI + +config PCNET_PCI + bool + default y if PCI_DEVICES + depends on PCI + select PCNET_COMMON + +config PCNET_COMMON + bool + +config E1000_PCI + bool + default y if PCI_DEVICES + depends on PCI + +config E1000E_PCI_EXPRESS + bool + default y if PCI_DEVICES + depends on PCI_EXPRESS && MSI_NONBROKEN + +config RTL8139_PCI + bool + default y if PCI_DEVICES + depends on PCI + +config VMXNET3_PCI + bool + default y if PCI_DEVICES + depends on PCI + +config SMC91C111 + bool + +config LAN9118 + bool + select PTIMER + +config NE2000_ISA + bool + default y + depends on ISA_BUS + depends on PCI # for NE2000State + select NE2000_PCI + +config OPENCORES_ETH + bool + +config XGMAC + bool + +config MIPSNET + bool + +config ALLWINNER_EMAC + bool + +config IMX_FEC + bool + +config CADENCE + bool + +config STELLARIS_ENET + bool + +config LANCE + bool + select PCNET_COMMON + +config SUNHME + bool + +config FTGMAC100 + bool + +config SUNGEM + bool + depends on PCI + +config COLDFIRE + bool + +config XILINX_ETHLITE + bool + +config VIRTIO_NET + bool + default y + depends on VIRTIO + +config ETSEC + bool + select PTIMER + +config ROCKER + bool + default y if PCI_DEVICES + depends on PCI && MSI_NONBROKEN + +config CAN_BUS + bool + +config CAN_PCI + bool + default y if PCI_DEVICES + depends on PCI + select CAN_BUS + +config CAN_SJA1000 + bool + default y if PCI_DEVICES + depends on PCI + select CAN_BUS diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index fa461d4463..ea63715780 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -7,8 +7,8 @@ common-obj-$(CONFIG_EEPRO100_PCI) += eepro100.o common-obj-$(CONFIG_PCNET_PCI) += pcnet-pci.o common-obj-$(CONFIG_PCNET_COMMON) += pcnet.o common-obj-$(CONFIG_E1000_PCI) += e1000.o e1000x_common.o -common-obj-$(CONFIG_E1000E_PCI) += net_tx_pkt.o net_rx_pkt.o -common-obj-$(CONFIG_E1000E_PCI) += e1000e.o e1000e_core.o e1000x_common.o +common-obj-$(CONFIG_E1000E_PCI_EXPRESS) += net_tx_pkt.o net_rx_pkt.o +common-obj-$(CONFIG_E1000E_PCI_EXPRESS) += e1000e.o e1000e_core.o e1000x_common.o common-obj-$(CONFIG_RTL8139_PCI) += rtl8139.o common-obj-$(CONFIG_VMXNET3_PCI) += net_tx_pkt.o net_rx_pkt.o common-obj-$(CONFIG_VMXNET3_PCI) += vmxnet3.o @@ -37,7 +37,9 @@ obj-$(CONFIG_PSERIES) += spapr_llan.o obj-$(CONFIG_XILINX_ETHLITE) += xilinx_ethlite.o obj-$(CONFIG_VIRTIO_NET) += virtio-net.o -obj-y += vhost_net.o +common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET)) += vhost_net.o +common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET))) += vhost_net-stub.o +common-obj-$(CONFIG_ALL) += vhost_net-stub.o obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ fsl_etsec/rings.o fsl_etsec/miim.o diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 0fa4b0dc44..7f63411430 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -28,6 +28,7 @@ #include "hw/net/cadence_gem.h" #include "qapi/error.h" #include "qemu/log.h" +#include "sysemu/dma.h" #include "net/checksum.h" #ifdef CADENCE_GEM_ERR_DEBUG @@ -141,6 +142,7 @@ #define GEM_DESCONF4 (0x0000028C/4) #define GEM_DESCONF5 (0x00000290/4) #define GEM_DESCONF6 (0x00000294/4) +#define GEM_DESCONF6_64B_MASK (1U << 23) #define GEM_DESCONF7 (0x00000298/4) #define GEM_INT_Q1_STATUS (0x00000400 / 4) @@ -152,6 +154,9 @@ #define GEM_RECEIVE_Q1_PTR (0x00000480 / 4) #define GEM_RECEIVE_Q7_PTR (GEM_RECEIVE_Q1_PTR + 6) +#define GEM_TBQPH (0x000004C8 / 4) +#define GEM_RBQPH (0x000004D4 / 4) + #define GEM_INT_Q1_ENABLE (0x00000600 / 4) #define GEM_INT_Q7_ENABLE (GEM_INT_Q1_ENABLE + 6) @@ -207,6 +212,9 @@ #define GEM_NWCFG_BCAST_REJ 0x00000020 /* Reject broadcast packets */ #define GEM_NWCFG_PROMISC 0x00000010 /* Accept all packets */ +#define GEM_DMACFG_ADDR_64B (1U << 30) +#define GEM_DMACFG_TX_BD_EXT (1U << 29) +#define GEM_DMACFG_RX_BD_EXT (1U << 28) #define GEM_DMACFG_RBUFSZ_M 0x00FF0000 /* DMA RX Buffer Size mask */ #define GEM_DMACFG_RBUFSZ_S 16 /* DMA RX Buffer Size shift */ #define GEM_DMACFG_RBUFSZ_MUL 64 /* DMA RX Buffer Size multiplier */ @@ -302,42 +310,47 @@ #define GEM_MODID_VALUE 0x00020118 -static inline unsigned tx_desc_get_buffer(unsigned *desc) +static inline uint64_t tx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { - return desc[0]; + uint64_t ret = desc[0]; + + if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + ret |= (uint64_t)desc[2] << 32; + } + return ret; } -static inline unsigned tx_desc_get_used(unsigned *desc) +static inline unsigned tx_desc_get_used(uint32_t *desc) { return (desc[1] & DESC_1_USED) ? 1 : 0; } -static inline void tx_desc_set_used(unsigned *desc) +static inline void tx_desc_set_used(uint32_t *desc) { desc[1] |= DESC_1_USED; } -static inline unsigned tx_desc_get_wrap(unsigned *desc) +static inline unsigned tx_desc_get_wrap(uint32_t *desc) { return (desc[1] & DESC_1_TX_WRAP) ? 1 : 0; } -static inline unsigned tx_desc_get_last(unsigned *desc) +static inline unsigned tx_desc_get_last(uint32_t *desc) { return (desc[1] & DESC_1_TX_LAST) ? 1 : 0; } -static inline void tx_desc_set_last(unsigned *desc) +static inline void tx_desc_set_last(uint32_t *desc) { desc[1] |= DESC_1_TX_LAST; } -static inline unsigned tx_desc_get_length(unsigned *desc) +static inline unsigned tx_desc_get_length(uint32_t *desc) { return desc[1] & DESC_1_LENGTH; } -static inline void print_gem_tx_desc(unsigned *desc, uint8_t queue) +static inline void print_gem_tx_desc(uint32_t *desc, uint8_t queue) { DB_PRINT("TXDESC (queue %" PRId8 "):\n", queue); DB_PRINT("bufaddr: 0x%08x\n", *desc); @@ -347,58 +360,79 @@ static inline void print_gem_tx_desc(unsigned *desc, uint8_t queue) DB_PRINT("length: %d\n", tx_desc_get_length(desc)); } -static inline unsigned rx_desc_get_buffer(unsigned *desc) +static inline uint64_t rx_desc_get_buffer(CadenceGEMState *s, uint32_t *desc) { - return desc[0] & ~0x3UL; + uint64_t ret = desc[0] & ~0x3UL; + + if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + ret |= (uint64_t)desc[2] << 32; + } + return ret; } -static inline unsigned rx_desc_get_wrap(unsigned *desc) +static inline int gem_get_desc_len(CadenceGEMState *s, bool rx_n_tx) +{ + int ret = 2; + + if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + ret += 2; + } + if (s->regs[GEM_DMACFG] & (rx_n_tx ? GEM_DMACFG_RX_BD_EXT + : GEM_DMACFG_TX_BD_EXT)) { + ret += 2; + } + + assert(ret <= DESC_MAX_NUM_WORDS); + return ret; +} + +static inline unsigned rx_desc_get_wrap(uint32_t *desc) { return desc[0] & DESC_0_RX_WRAP ? 1 : 0; } -static inline unsigned rx_desc_get_ownership(unsigned *desc) +static inline unsigned rx_desc_get_ownership(uint32_t *desc) { return desc[0] & DESC_0_RX_OWNERSHIP ? 1 : 0; } -static inline void rx_desc_set_ownership(unsigned *desc) +static inline void rx_desc_set_ownership(uint32_t *desc) { desc[0] |= DESC_0_RX_OWNERSHIP; } -static inline void rx_desc_set_sof(unsigned *desc) +static inline void rx_desc_set_sof(uint32_t *desc) { desc[1] |= DESC_1_RX_SOF; } -static inline void rx_desc_set_eof(unsigned *desc) +static inline void rx_desc_set_eof(uint32_t *desc) { desc[1] |= DESC_1_RX_EOF; } -static inline void rx_desc_set_length(unsigned *desc, unsigned len) +static inline void rx_desc_set_length(uint32_t *desc, unsigned len) { desc[1] &= ~DESC_1_LENGTH; desc[1] |= len; } -static inline void rx_desc_set_broadcast(unsigned *desc) +static inline void rx_desc_set_broadcast(uint32_t *desc) { desc[1] |= R_DESC_1_RX_BROADCAST; } -static inline void rx_desc_set_unicast_hash(unsigned *desc) +static inline void rx_desc_set_unicast_hash(uint32_t *desc) { desc[1] |= R_DESC_1_RX_UNICAST_HASH; } -static inline void rx_desc_set_multicast_hash(unsigned *desc) +static inline void rx_desc_set_multicast_hash(uint32_t *desc) { desc[1] |= R_DESC_1_RX_MULTICAST_HASH; } -static inline void rx_desc_set_sar(unsigned *desc, int sar_idx) +static inline void rx_desc_set_sar(uint32_t *desc, int sar_idx) { desc[1] = deposit32(desc[1], R_DESC_1_RX_SAR_SHIFT, R_DESC_1_RX_SAR_LENGTH, sar_idx); @@ -419,7 +453,7 @@ static void gem_init_register_masks(CadenceGEMState *s) memset(&s->regs_ro[0], 0, sizeof(s->regs_ro)); s->regs_ro[GEM_NWCTRL] = 0xFFF80000; s->regs_ro[GEM_NWSTATUS] = 0xFFFFFFFF; - s->regs_ro[GEM_DMACFG] = 0xFE00F000; + s->regs_ro[GEM_DMACFG] = 0x8E00F000; s->regs_ro[GEM_TXSTATUS] = 0xFFFFFE08; s->regs_ro[GEM_RXQBASE] = 0x00000003; s->regs_ro[GEM_TXQBASE] = 0x00000003; @@ -802,17 +836,42 @@ static int get_queue_from_screen(CadenceGEMState *s, uint8_t *rxbuf_ptr, return 0; } +static hwaddr gem_get_desc_addr(CadenceGEMState *s, bool tx, int q) +{ + hwaddr desc_addr = 0; + + if (s->regs[GEM_DMACFG] & GEM_DMACFG_ADDR_64B) { + desc_addr = s->regs[tx ? GEM_TBQPH : GEM_RBQPH]; + } + desc_addr <<= 32; + desc_addr |= tx ? s->tx_desc_addr[q] : s->rx_desc_addr[q]; + return desc_addr; +} + +static hwaddr gem_get_tx_desc_addr(CadenceGEMState *s, int q) +{ + return gem_get_desc_addr(s, true, q); +} + +static hwaddr gem_get_rx_desc_addr(CadenceGEMState *s, int q) +{ + return gem_get_desc_addr(s, false, q); +} + static void gem_get_rx_desc(CadenceGEMState *s, int q) { - DB_PRINT("read descriptor 0x%x\n", (unsigned)s->rx_desc_addr[q]); + hwaddr desc_addr = gem_get_rx_desc_addr(s, q); + + DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", desc_addr); + /* read current descriptor */ - cpu_physical_memory_read(s->rx_desc_addr[q], - (uint8_t *)s->rx_desc[q], sizeof(s->rx_desc[q])); + address_space_read(&s->dma_as, desc_addr, MEMTXATTRS_UNSPECIFIED, + (uint8_t *)s->rx_desc[q], + sizeof(uint32_t) * gem_get_desc_len(s, true)); /* Descriptor owned by software ? */ if (rx_desc_get_ownership(s->rx_desc[q]) == 1) { - DB_PRINT("descriptor 0x%x owned by sw.\n", - (unsigned)s->rx_desc_addr[q]); + DB_PRINT("descriptor 0x%" HWADDR_PRIx " owned by sw.\n", desc_addr); s->regs[GEM_RXSTATUS] |= GEM_RXSTATUS_NOBUF; s->regs[GEM_ISR] |= GEM_INT_RXUSED & ~(s->regs[GEM_IMR]); /* Handle interrupt consequences */ @@ -916,9 +975,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) q = get_queue_from_screen(s, rxbuf_ptr, rxbufsize); while (bytes_to_copy) { + hwaddr desc_addr; + /* Do nothing if receive is not enabled. */ if (!gem_can_receive(nc)) { - assert(!first_desc); return -1; } @@ -926,9 +986,10 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) rx_desc_get_buffer(s->rx_desc[q])); /* Copy packet data to emulated DMA buffer */ - cpu_physical_memory_write(rx_desc_get_buffer(s->rx_desc[q]) + - rxbuf_offset, - rxbuf_ptr, MIN(bytes_to_copy, rxbufsize)); + address_space_write(&s->dma_as, rx_desc_get_buffer(s, s->rx_desc[q]) + + rxbuf_offset, + MEMTXATTRS_UNSPECIFIED, rxbuf_ptr, + MIN(bytes_to_copy, rxbufsize)); rxbuf_ptr += MIN(bytes_to_copy, rxbufsize); bytes_to_copy -= MIN(bytes_to_copy, rxbufsize); @@ -962,9 +1023,11 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) } /* Descriptor write-back. */ - cpu_physical_memory_write(s->rx_desc_addr[q], - (uint8_t *)s->rx_desc[q], - sizeof(s->rx_desc[q])); + desc_addr = gem_get_rx_desc_addr(s, q); + address_space_write(&s->dma_as, desc_addr, + MEMTXATTRS_UNSPECIFIED, + (uint8_t *)s->rx_desc[q], + sizeof(uint32_t) * gem_get_desc_len(s, true)); /* Next descriptor */ if (rx_desc_get_wrap(s->rx_desc[q])) { @@ -972,7 +1035,7 @@ static ssize_t gem_receive(NetClientState *nc, const uint8_t *buf, size_t size) s->rx_desc_addr[q] = s->regs[GEM_RXQBASE]; } else { DB_PRINT("incrementing RX descriptor list\n"); - s->rx_desc_addr[q] += 8; + s->rx_desc_addr[q] += 4 * gem_get_desc_len(s, true); } gem_get_rx_desc(s, q); @@ -1042,7 +1105,7 @@ static void gem_transmit_updatestats(CadenceGEMState *s, const uint8_t *packet, */ static void gem_transmit(CadenceGEMState *s) { - unsigned desc[2]; + uint32_t desc[DESC_MAX_NUM_WORDS]; hwaddr packet_desc_addr; uint8_t tx_packet[2048]; uint8_t *p; @@ -1065,11 +1128,12 @@ static void gem_transmit(CadenceGEMState *s) for (q = s->num_priority_queues - 1; q >= 0; q--) { /* read current descriptor */ - packet_desc_addr = s->tx_desc_addr[q]; + packet_desc_addr = gem_get_tx_desc_addr(s, q); DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr); - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)desc, sizeof(desc)); + address_space_read(&s->dma_as, packet_desc_addr, + MEMTXATTRS_UNSPECIFIED, (uint8_t *)desc, + sizeof(uint32_t) * gem_get_desc_len(s, false)); /* Handle all descriptors owned by hardware */ while (tx_desc_get_used(desc) == 0) { @@ -1082,7 +1146,7 @@ static void gem_transmit(CadenceGEMState *s) /* The real hardware would eat this (and possibly crash). * For QEMU let's lend a helping hand. */ - if ((tx_desc_get_buffer(desc) == 0) || + if ((tx_desc_get_buffer(s, desc) == 0) || (tx_desc_get_length(desc) == 0)) { DB_PRINT("Invalid TX descriptor @ 0x%x\n", (unsigned)packet_desc_addr); @@ -1101,30 +1165,35 @@ static void gem_transmit(CadenceGEMState *s) /* Gather this fragment of the packet from "dma memory" to our * contig buffer. */ - cpu_physical_memory_read(tx_desc_get_buffer(desc), p, - tx_desc_get_length(desc)); + address_space_read(&s->dma_as, tx_desc_get_buffer(s, desc), + MEMTXATTRS_UNSPECIFIED, + p, tx_desc_get_length(desc)); p += tx_desc_get_length(desc); total_bytes += tx_desc_get_length(desc); /* Last descriptor for this packet; hand the whole thing off */ if (tx_desc_get_last(desc)) { - unsigned desc_first[2]; + uint32_t desc_first[DESC_MAX_NUM_WORDS]; + hwaddr desc_addr = gem_get_tx_desc_addr(s, q); /* Modify the 1st descriptor of this packet to be owned by * the processor. */ - cpu_physical_memory_read(s->tx_desc_addr[q], - (uint8_t *)desc_first, - sizeof(desc_first)); + address_space_read(&s->dma_as, desc_addr, + MEMTXATTRS_UNSPECIFIED, + (uint8_t *)desc_first, + sizeof(desc_first)); tx_desc_set_used(desc_first); - cpu_physical_memory_write(s->tx_desc_addr[q], - (uint8_t *)desc_first, - sizeof(desc_first)); + address_space_write(&s->dma_as, desc_addr, + MEMTXATTRS_UNSPECIFIED, + (uint8_t *)desc_first, + sizeof(desc_first)); /* Advance the hardware current descriptor past this packet */ if (tx_desc_get_wrap(desc)) { s->tx_desc_addr[q] = s->regs[GEM_TXQBASE]; } else { - s->tx_desc_addr[q] = packet_desc_addr + 8; + s->tx_desc_addr[q] = packet_desc_addr + + 4 * gem_get_desc_len(s, false); } DB_PRINT("TX descriptor next: 0x%08x\n", s->tx_desc_addr[q]); @@ -1168,11 +1237,12 @@ static void gem_transmit(CadenceGEMState *s) tx_desc_set_last(desc); packet_desc_addr = s->regs[GEM_TXQBASE]; } else { - packet_desc_addr += 8; + packet_desc_addr += 4 * gem_get_desc_len(s, false); } DB_PRINT("read descriptor 0x%" HWADDR_PRIx "\n", packet_desc_addr); - cpu_physical_memory_read(packet_desc_addr, - (uint8_t *)desc, sizeof(desc)); + address_space_read(&s->dma_as, packet_desc_addr, + MEMTXATTRS_UNSPECIFIED, (uint8_t *)desc, + sizeof(uint32_t) * gem_get_desc_len(s, false)); } if (tx_desc_get_used(desc)) { @@ -1213,6 +1283,7 @@ static void gem_reset(DeviceState *d) int i; CadenceGEMState *s = CADENCE_GEM(d); const uint8_t *a; + uint32_t queues_mask = 0; DB_PRINT("\n"); @@ -1228,8 +1299,13 @@ static void gem_reset(DeviceState *d) s->regs[GEM_MODID] = s->revision; s->regs[GEM_DESCONF] = 0x02500111; s->regs[GEM_DESCONF2] = 0x2ab13fff; - s->regs[GEM_DESCONF5] = 0x002f2145; - s->regs[GEM_DESCONF6] = 0x00000200; + s->regs[GEM_DESCONF5] = 0x002f2045; + s->regs[GEM_DESCONF6] = GEM_DESCONF6_64B_MASK; + + if (s->num_priority_queues > 1) { + queues_mask = MAKE_64BIT_MASK(1, s->num_priority_queues - 1); + s->regs[GEM_DESCONF6] |= queues_mask; + } /* Set MAC address */ a = &s->conf.macaddr.a[0]; @@ -1463,6 +1539,9 @@ static void gem_realize(DeviceState *dev, Error **errp) CadenceGEMState *s = CADENCE_GEM(dev); int i; + address_space_init(&s->dma_as, + s->dma_mr ? s->dma_mr : get_system_memory(), "dma"); + if (s->num_priority_queues == 0 || s->num_priority_queues > MAX_PRIORITY_QUEUES) { error_setg(errp, "Invalid num-priority-queues value: %" PRIx8, @@ -1500,6 +1579,12 @@ static void gem_init(Object *obj) "enet", sizeof(s->regs)); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); + + object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, + (Object **)&s->dma_mr, + qdev_prop_allow_set_link_before_realize, + OBJ_PROP_LINK_STRONG, + &error_abort); } static const VMStateDescription vmstate_cadence_gem = { diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index b53fcaa8bc..98cb4e58c7 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" -#include "hw/devices.h" #include "net/net.h" #include "qapi/error.h" #include "qemu/timer.h" diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 13a9494a8d..9b39bccfb2 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -36,6 +36,7 @@ #include "qemu/range.h" #include "e1000x_common.h" +#include "trace.h" static const uint8_t bcast[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; @@ -119,6 +120,8 @@ typedef struct E1000State_st { bool mit_irq_level; /* Tracks interrupt pin level. */ uint32_t mit_ide; /* Tracks E1000_TXD_CMD_IDE bit. */ + QEMUTimer *flush_queue_timer; + /* Compatibility flags for migration to/from qemu 1.3.0 and older */ #define E1000_FLAG_AUTONEG_BIT 0 #define E1000_FLAG_MIT_BIT 1 @@ -365,6 +368,7 @@ static void e1000_reset(void *opaque) timer_del(d->autoneg_timer); timer_del(d->mit_timer); + timer_del(d->flush_queue_timer); d->mit_timer_on = 0; d->mit_irq_level = 0; d->mit_ide = 0; @@ -390,6 +394,14 @@ set_ctrl(E1000State *s, int index, uint32_t val) s->mac_reg[CTRL] = val & ~E1000_CTRL_RST; } +static void +e1000_flush_queue_timer(void *opaque) +{ + E1000State *s = opaque; + + qemu_flush_queued_packets(qemu_get_queue(s->nic)); +} + static void set_rx_control(E1000State *s, int index, uint32_t val) { @@ -398,7 +410,8 @@ set_rx_control(E1000State *s, int index, uint32_t val) s->rxbuf_min_shift = ((val / E1000_RCTL_RDMTS_QUAT) & 3) + 1; DBGOUT(RX, "RCTL: %d, mac_reg[RCTL] = 0x%x\n", s->mac_reg[RDT], s->mac_reg[RCTL]); - qemu_flush_queued_packets(qemu_get_queue(s->nic)); + timer_mod(s->flush_queue_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + 1000); } static void @@ -836,7 +849,7 @@ e1000_can_receive(NetClientState *nc) E1000State *s = qemu_get_nic_opaque(nc); return e1000x_rx_ready(&s->parent_obj, s->mac_reg) && - e1000_has_rxbufs(s, 1); + e1000_has_rxbufs(s, 1) && !timer_pending(s->flush_queue_timer); } static uint64_t rx_desc_base(E1000State *s) @@ -847,6 +860,15 @@ static uint64_t rx_desc_base(E1000State *s) return (bah << 32) + bal; } +static void +e1000_receiver_overrun(E1000State *s, size_t size) +{ + trace_e1000_receiver_overrun(size, s->mac_reg[RDH], s->mac_reg[RDT]); + e1000x_inc_reg_if_not_full(s->mac_reg, RNBC); + e1000x_inc_reg_if_not_full(s->mac_reg, MPC); + set_ics(s, 0, E1000_ICS_RXO); +} + static ssize_t e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) { @@ -871,6 +893,10 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) return -1; } + if (timer_pending(s->flush_queue_timer)) { + return 0; + } + /* Pad to minimum Ethernet frame length */ if (size < sizeof(min_buf)) { iov_to_buf(iov, iovcnt, 0, min_buf, size); @@ -916,8 +942,8 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) desc_offset = 0; total_size = size + e1000x_fcs_len(s->mac_reg); if (!e1000_has_rxbufs(s, total_size)) { - set_ics(s, 0, E1000_ICS_RXO); - return -1; + e1000_receiver_overrun(s, total_size); + return -1; } do { desc_size = total_size - desc_offset; @@ -969,7 +995,7 @@ e1000_receive_iov(NetClientState *nc, const struct iovec *iov, int iovcnt) 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); + e1000_receiver_overrun(s, total_size); return -1; } } while (desc_offset < total_size); @@ -1627,6 +1653,8 @@ pci_e1000_uninit(PCIDevice *dev) timer_free(d->autoneg_timer); timer_del(d->mit_timer); timer_free(d->mit_timer); + timer_del(d->flush_queue_timer); + timer_free(d->flush_queue_timer); qemu_del_nic(d->nic); } @@ -1690,6 +1718,8 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) d->autoneg_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, e1000_autoneg_timer, d); d->mit_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, e1000_mit_timer, d); + d->flush_queue_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + e1000_flush_queue_timer, d); } static void qdev_e1000_reset(DeviceState *dev) diff --git a/hw/net/etraxfs_eth.c b/hw/net/etraxfs_eth.c index a6932432b1..36855804db 100644 --- a/hw/net/etraxfs_eth.c +++ b/hw/net/etraxfs_eth.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "net/net.h" #include "hw/cris/etraxfs.h" @@ -126,7 +127,7 @@ tdk_write(struct qemu_phy *phy, unsigned int req, unsigned int data) } static void -tdk_init(struct qemu_phy *phy) +tdk_reset(struct qemu_phy *phy) { phy->regs[0] = 0x3100; /* PHY Id. */ @@ -135,9 +136,6 @@ tdk_init(struct qemu_phy *phy) /* Autonegotiation advertisement reg. */ phy->regs[4] = 0x01E1; phy->link = 1; - - phy->read = tdk_read; - phy->write = tdk_write; } struct qemu_mdio @@ -584,14 +582,35 @@ static NetClientInfo net_etraxfs_info = { .link_status_changed = eth_set_link, }; -static int fs_eth_init(SysBusDevice *sbd) +static void etraxfs_eth_reset(DeviceState *dev) { - DeviceState *dev = DEVICE(sbd); + ETRAXFSEthState *s = ETRAX_FS_ETH(dev); + + memset(s->regs, 0, sizeof(s->regs)); + memset(s->macaddr, 0, sizeof(s->macaddr)); + s->duplex_mismatch = 0; + + s->mdio_bus.mdc = 0; + s->mdio_bus.mdio = 0; + s->mdio_bus.state = 0; + s->mdio_bus.drive = 0; + s->mdio_bus.cnt = 0; + s->mdio_bus.addr = 0; + s->mdio_bus.opc = 0; + s->mdio_bus.req = 0; + s->mdio_bus.data = 0; + + tdk_reset(&s->phy); +} + +static void etraxfs_eth_realize(DeviceState *dev, Error **errp) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); ETRAXFSEthState *s = ETRAX_FS_ETH(dev); if (!s->dma_out || !s->dma_in) { - error_report("Unconnected ETRAX-FS Ethernet MAC"); - return -1; + error_setg(errp, "Unconnected ETRAX-FS Ethernet MAC"); + return; } s->dma_out->client.push = eth_tx_push; @@ -608,10 +627,9 @@ static int fs_eth_init(SysBusDevice *sbd) object_get_typename(OBJECT(s)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - tdk_init(&s->phy); + s->phy.read = tdk_read; + s->phy.write = tdk_write; mdio_attach(&s->mdio_bus, &s->phy, s->phyaddr); - return 0; } static Property etraxfs_eth_properties[] = { @@ -625,9 +643,9 @@ static Property etraxfs_eth_properties[] = { static void etraxfs_eth_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = fs_eth_init; + dc->realize = etraxfs_eth_realize; + dc->reset = etraxfs_eth_reset; dc->props = etraxfs_eth_properties; /* Reason: pointer properties "dma_out", "dma_in" */ dc->user_creatable = false; diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 909c1182ee..790430346b 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -89,6 +89,18 @@ #define FTGMAC100_PHYDATA_MIIWDATA(x) ((x) & 0xffff) #define FTGMAC100_PHYDATA_MIIRDATA(x) (((x) >> 16) & 0xffff) +/* + * PHY control register - New MDC/MDIO interface + */ +#define FTGMAC100_PHYCR_NEW_DATA(x) (((x) >> 16) & 0xffff) +#define FTGMAC100_PHYCR_NEW_FIRE (1 << 15) +#define FTGMAC100_PHYCR_NEW_ST_22 (1 << 12) +#define FTGMAC100_PHYCR_NEW_OP(x) (((x) >> 10) & 3) +#define FTGMAC100_PHYCR_NEW_OP_WRITE 0x1 +#define FTGMAC100_PHYCR_NEW_OP_READ 0x2 +#define FTGMAC100_PHYCR_NEW_DEV(x) (((x) >> 5) & 0x1f) +#define FTGMAC100_PHYCR_NEW_REG(x) ((x) & 0x1f) + /* * Feature Register */ @@ -269,9 +281,9 @@ static void phy_reset(FTGMAC100State *s) s->phy_int = 0; } -static uint32_t do_phy_read(FTGMAC100State *s, int reg) +static uint16_t do_phy_read(FTGMAC100State *s, uint8_t reg) { - uint32_t val; + uint16_t val; switch (reg) { case MII_BMCR: /* Basic Control */ @@ -336,7 +348,7 @@ static uint32_t do_phy_read(FTGMAC100State *s, int reg) MII_BMCR_FD | MII_BMCR_CTST) #define MII_ANAR_MASK 0x2d7f -static void do_phy_write(FTGMAC100State *s, int reg, uint32_t val) +static void do_phy_write(FTGMAC100State *s, uint8_t reg, uint16_t val) { switch (reg) { case MII_BMCR: /* Basic Control */ @@ -373,6 +385,55 @@ static void do_phy_write(FTGMAC100State *s, int reg, uint32_t val) } } +static void do_phy_new_ctl(FTGMAC100State *s) +{ + uint8_t reg; + uint16_t data; + + if (!(s->phycr & FTGMAC100_PHYCR_NEW_ST_22)) { + qemu_log_mask(LOG_UNIMP, "%s: unsupported ST code\n", __func__); + return; + } + + /* Nothing to do */ + if (!(s->phycr & FTGMAC100_PHYCR_NEW_FIRE)) { + return; + } + + reg = FTGMAC100_PHYCR_NEW_REG(s->phycr); + data = FTGMAC100_PHYCR_NEW_DATA(s->phycr); + + switch (FTGMAC100_PHYCR_NEW_OP(s->phycr)) { + case FTGMAC100_PHYCR_NEW_OP_WRITE: + do_phy_write(s, reg, data); + break; + case FTGMAC100_PHYCR_NEW_OP_READ: + s->phydata = do_phy_read(s, reg) & 0xffff; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: invalid OP code %08x\n", + __func__, s->phycr); + } + + s->phycr &= ~FTGMAC100_PHYCR_NEW_FIRE; +} + +static void do_phy_ctl(FTGMAC100State *s) +{ + uint8_t reg = FTGMAC100_PHYCR_REG(s->phycr); + + if (s->phycr & FTGMAC100_PHYCR_MIIWR) { + do_phy_write(s, reg, s->phydata & 0xffff); + s->phycr &= ~FTGMAC100_PHYCR_MIIWR; + } else if (s->phycr & FTGMAC100_PHYCR_MIIRD) { + s->phydata = do_phy_read(s, reg) << 16; + s->phycr &= ~FTGMAC100_PHYCR_MIIRD; + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: no OP code %08x\n", + __func__, s->phycr); + } +} + static int ftgmac100_read_bd(FTGMAC100Desc *bd, dma_addr_t addr) { if (dma_memory_read(&address_space_memory, addr, bd, sizeof(*bd))) { @@ -628,7 +689,6 @@ static void ftgmac100_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { FTGMAC100State *s = FTGMAC100(opaque); - int reg; switch (addr & 0xff) { case FTGMAC100_ISR: /* Interrupt status */ @@ -711,14 +771,11 @@ static void ftgmac100_write(void *opaque, hwaddr addr, break; case FTGMAC100_PHYCR: /* PHY Device control */ - reg = FTGMAC100_PHYCR_REG(value); s->phycr = value; - if (value & FTGMAC100_PHYCR_MIIWR) { - do_phy_write(s, reg, s->phydata & 0xffff); - s->phycr &= ~FTGMAC100_PHYCR_MIIWR; + if (s->revr & FTGMAC100_REVR_NEW_MDIO_INTERFACE) { + do_phy_new_ctl(s); } else { - s->phydata = do_phy_read(s, reg) << 16; - s->phycr &= ~FTGMAC100_PHYCR_MIIRD; + do_phy_ctl(s); } break; case FTGMAC100_PHYDATA: @@ -728,8 +785,7 @@ static void ftgmac100_write(void *opaque, hwaddr addr, s->dblac = value; break; case FTGMAC100_REVR: /* Feature Register */ - /* TODO: Only Old MDIO interface is supported */ - s->revr = value & ~FTGMAC100_REVR_NEW_MDIO_INTERFACE; + s->revr = value; break; case FTGMAC100_FEAR1: /* Feature Register 1 */ s->fear1 = value; diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index b9032dac59..a6269d9463 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1320,9 +1320,9 @@ static NetClientInfo net_lan9118_info = { .link_status_changed = lan9118_set_link, }; -static int lan9118_init1(SysBusDevice *sbd) +static void lan9118_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); lan9118_state *s = LAN9118(dev); QEMUBH *bh; int i; @@ -1349,8 +1349,6 @@ static int lan9118_init1(SysBusDevice *sbd) s->timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT); ptimer_set_freq(s->timer, 10000); ptimer_set_limit(s->timer, 0xffff, 1); - - return 0; } static Property lan9118_properties[] = { @@ -1362,12 +1360,11 @@ static Property lan9118_properties[] = { static void lan9118_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = lan9118_init1; dc->reset = lan9118_reset; dc->props = lan9118_properties; dc->vmsd = &vmstate_lan9118; + dc->realize = lan9118_realize; } static const TypeInfo lan9118_info = { diff --git a/hw/net/lance.c b/hw/net/lance.c index a08d5ac6a8..f987b2fd18 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -97,9 +97,9 @@ static const VMStateDescription vmstate_lance = { } }; -static int lance_init(SysBusDevice *sbd) +static void lance_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); SysBusPCNetState *d = SYSBUS_PCNET(dev); PCNetState *s = &d->state; @@ -115,7 +115,6 @@ static int lance_init(SysBusDevice *sbd) s->phys_mem_read = ledma_memory_read; s->phys_mem_write = ledma_memory_write; pcnet_common_init(dev, s, &net_lance_info); - return 0; } static void lance_reset(DeviceState *dev) @@ -144,9 +143,8 @@ static Property lance_properties[] = { static void lance_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = lance_init; + dc->realize = lance_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->fw_name = "ethernet"; dc->reset = lance_reset; diff --git a/hw/net/milkymist-minimac2.c b/hw/net/milkymist-minimac2.c index 3eaa19dfde..85c9fc0b65 100644 --- a/hw/net/milkymist-minimac2.c +++ b/hw/net/milkymist-minimac2.c @@ -30,6 +30,7 @@ #include "hw/sysbus.h" #include "trace.h" #include "net/net.h" +#include "qemu/log.h" #include "qemu/error-report.h" #include @@ -214,7 +215,8 @@ static size_t assemble_frame(uint8_t *buf, size_t size, uint32_t crc; if (size < payload_size + 12) { - error_report("milkymist_minimac2: received too big ethernet frame"); + qemu_log_mask(LOG_GUEST_ERROR, "milkymist_minimac2: frame too big " + "(%zd bytes)\n", payload_size); return 0; } @@ -347,8 +349,9 @@ minimac2_read(void *opaque, hwaddr addr, unsigned size) break; default: - error_report("milkymist_minimac2: read access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + qemu_log_mask(LOG_GUEST_ERROR, + "milkymist_minimac2_rd%d: 0x%" HWADDR_PRIx "\n", + size, addr << 2); break; } @@ -413,8 +416,10 @@ minimac2_write(void *opaque, hwaddr addr, uint64_t value, break; default: - error_report("milkymist_minimac2: write access to unknown register 0x" - TARGET_FMT_plx, addr << 2); + qemu_log_mask(LOG_GUEST_ERROR, + "milkymist_minimac2_wr%d: 0x%" HWADDR_PRIx + " = 0x%" PRIx64 "\n", + size, addr << 2, value); break; } } @@ -452,9 +457,9 @@ static NetClientInfo net_milkymist_minimac2_info = { .receive = minimac2_rx, }; -static int milkymist_minimac2_init(SysBusDevice *sbd) +static void milkymist_minimac2_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); MilkymistMinimac2State *s = MILKYMIST_MINIMAC2(dev); size_t buffers_size = TARGET_PAGE_ALIGN(3 * MINIMAC2_BUFFER_SIZE); @@ -479,8 +484,6 @@ static int milkymist_minimac2_init(SysBusDevice *sbd) s->nic = qemu_new_nic(&net_milkymist_minimac2_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - return 0; } static const VMStateDescription vmstate_milkymist_minimac2_mdio = { @@ -521,9 +524,8 @@ static Property milkymist_minimac2_properties[] = { static void milkymist_minimac2_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = milkymist_minimac2_init; + dc->realize = milkymist_minimac2_realize; dc->reset = milkymist_minimac2_reset; dc->vmsd = &vmstate_milkymist_minimac2; dc->props = milkymist_minimac2_properties; diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index 5a63df7ccb..5ec13105df 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -112,27 +112,27 @@ static uint64_t mipsnet_ioport_read(void *opaque, hwaddr addr, addr &= 0x3f; switch (addr) { case MIPSNET_DEV_ID: - ret = be32_to_cpu(0x4d495053); /* MIPS */ + ret = be32_to_cpu(0x4d495053); /* MIPS */ break; case MIPSNET_DEV_ID + 4: - ret = be32_to_cpu(0x4e455430); /* NET0 */ + ret = be32_to_cpu(0x4e455430); /* NET0 */ break; case MIPSNET_BUSY: - ret = s->busy; + ret = s->busy; break; case MIPSNET_RX_DATA_COUNT: - ret = s->rx_count; + ret = s->rx_count; break; case MIPSNET_TX_DATA_COUNT: - ret = s->tx_count; + ret = s->tx_count; break; case MIPSNET_INT_CTL: - ret = s->intctl; + ret = s->intctl; s->intctl &= ~MIPSNET_INTCTL_TESTBIT; break; case MIPSNET_INTERRUPT_INFO: /* XXX: This seems to be a per-VPE interrupt number. */ - ret = 0; + ret = 0; break; case MIPSNET_RX_DATA_BUFFER: if (s->rx_count) { @@ -161,7 +161,7 @@ static void mipsnet_ioport_write(void *opaque, hwaddr addr, trace_mipsnet_write(addr, val); switch (addr) { case MIPSNET_TX_DATA_COUNT: - s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0; + s->tx_count = (val <= MAX_ETH_FRAME_SIZE) ? val : 0; s->tx_written = 0; break; case MIPSNET_INT_CTL: @@ -236,9 +236,9 @@ static const MemoryRegionOps mipsnet_ioport_ops = { .impl.max_access_size = 4, }; -static int mipsnet_sysbus_init(SysBusDevice *sbd) +static void mipsnet_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); MIPSnetState *s = MIPS_NET(dev); memory_region_init_io(&s->io, OBJECT(dev), &mipsnet_ioport_ops, s, @@ -249,8 +249,6 @@ static int mipsnet_sysbus_init(SysBusDevice *sbd) s->nic = qemu_new_nic(&net_mipsnet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - return 0; } static void mipsnet_sysbus_reset(DeviceState *dev) @@ -267,9 +265,8 @@ static Property mipsnet_properties[] = { static void mipsnet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = mipsnet_sysbus_init; + dc->realize = mipsnet_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "MIPS Simulator network device"; dc->reset = mipsnet_sysbus_reset; diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index 07d79e317f..037afc8052 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -145,7 +145,7 @@ static void ne2000_update_irq(NE2000State *s) isr = (s->isr & s->imr) & 0x7f; #if defined(DEBUG_NE2000) printf("NE2000: Set IRQ to %d (%02x %02x)\n", - isr ? 1 : 0, s->isr, s->imr); + isr ? 1 : 0, s->isr, s->imr); #endif qemu_set_irq(s->irq, (isr != 0)); } @@ -174,7 +174,7 @@ static int ne2000_buffer_full(NE2000State *s) ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { NE2000State *s = qemu_get_nic_opaque(nc); - int size = size_; + size_t size = size_; uint8_t *p; unsigned int total_len, next, avail, len, index, mcast_idx; uint8_t buf1[60]; @@ -182,7 +182,7 @@ ssize_t ne2000_receive(NetClientState *nc, const uint8_t *buf, size_t size_) { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; #if defined(DEBUG_NE2000) - printf("NE2000: received len=%d\n", size); + printf("NE2000: received len=%zu\n", size); #endif if (s->cmd & E8390_STOP || ne2000_buffer_full(s)) @@ -396,12 +396,12 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) case EN0_ISR: ret = s->isr; break; - case EN0_RSARLO: - ret = s->rsar & 0x00ff; - break; - case EN0_RSARHI: - ret = s->rsar >> 8; - break; + case EN0_RSARLO: + ret = s->rsar & 0x00ff; + break; + case EN0_RSARHI: + ret = s->rsar >> 8; + break; case EN1_PHYS ... EN1_PHYS + 5: ret = s->phys[offset - EN1_PHYS]; break; @@ -420,21 +420,21 @@ static uint32_t ne2000_ioport_read(void *opaque, uint32_t addr) case EN2_STOPPG: ret = s->stop >> 8; break; - case EN0_RTL8029ID0: - ret = 0x50; - break; - case EN0_RTL8029ID1: - ret = 0x43; - break; - case EN3_CONFIG0: - ret = 0; /* 10baseT media */ - break; - case EN3_CONFIG2: - ret = 0x40; /* 10baseT active */ - break; - case EN3_CONFIG3: - ret = 0x40; /* Full duplex */ - break; + case EN0_RTL8029ID0: + ret = 0x50; + break; + case EN0_RTL8029ID1: + ret = 0x43; + break; + case EN3_CONFIG0: + ret = 0; /* 10baseT media */ + break; + case EN3_CONFIG2: + ret = 0x40; /* 10baseT active */ + break; + case EN3_CONFIG3: + ret = 0x40; /* Full duplex */ + break; default: ret = 0x00; break; diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index d42b79c08c..d6f54f8d82 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -715,9 +715,9 @@ static const MemoryRegionOps open_eth_desc_ops = { .write = open_eth_desc_write, }; -static int sysbus_open_eth_init(SysBusDevice *sbd) +static void sysbus_open_eth_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); OpenEthState *s = OPEN_ETH(dev); memory_region_init_io(&s->reg_io, OBJECT(dev), &open_eth_reg_ops, s, @@ -732,7 +732,6 @@ static int sysbus_open_eth_init(SysBusDevice *sbd) s->nic = qemu_new_nic(&net_open_eth_info, &s->conf, object_get_typename(OBJECT(s)), dev->id, s); - return 0; } static void qdev_open_eth_reset(DeviceState *dev) @@ -750,9 +749,8 @@ static Property open_eth_properties[] = { static void open_eth_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = sysbus_open_eth_init; + dc->realize = sysbus_open_eth_realize; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); dc->desc = "Opencores 10/100 Mbit Ethernet"; dc->reset = qdev_open_eth_reset; diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 70dc8b3f0c..7c73855783 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -139,94 +139,6 @@ static const MemoryRegionOps pcnet_io_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void pcnet_mmio_writeb(void *opaque, hwaddr addr, uint32_t val) -{ - PCNetState *d = opaque; - - trace_pcnet_mmio_writeb(opaque, addr, val); - if (!(addr & 0x10)) - pcnet_aprom_writeb(d, addr & 0x0f, val); -} - -static uint32_t pcnet_mmio_readb(void *opaque, hwaddr addr) -{ - PCNetState *d = opaque; - uint32_t val = -1; - - if (!(addr & 0x10)) - val = pcnet_aprom_readb(d, addr & 0x0f); - trace_pcnet_mmio_readb(opaque, addr, val); - return val; -} - -static void pcnet_mmio_writew(void *opaque, hwaddr addr, uint32_t val) -{ - PCNetState *d = opaque; - - trace_pcnet_mmio_writew(opaque, addr, val); - if (addr & 0x10) - pcnet_ioport_writew(d, addr & 0x0f, val); - else { - addr &= 0x0f; - pcnet_aprom_writeb(d, addr, val & 0xff); - pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); - } -} - -static uint32_t pcnet_mmio_readw(void *opaque, hwaddr addr) -{ - PCNetState *d = opaque; - uint32_t val = -1; - - if (addr & 0x10) - val = pcnet_ioport_readw(d, addr & 0x0f); - else { - addr &= 0x0f; - val = pcnet_aprom_readb(d, addr+1); - val <<= 8; - val |= pcnet_aprom_readb(d, addr); - } - trace_pcnet_mmio_readw(opaque, addr, val); - return val; -} - -static void pcnet_mmio_writel(void *opaque, hwaddr addr, uint32_t val) -{ - PCNetState *d = opaque; - - trace_pcnet_mmio_writel(opaque, addr, val); - if (addr & 0x10) - pcnet_ioport_writel(d, addr & 0x0f, val); - else { - addr &= 0x0f; - pcnet_aprom_writeb(d, addr, val & 0xff); - pcnet_aprom_writeb(d, addr+1, (val & 0xff00) >> 8); - pcnet_aprom_writeb(d, addr+2, (val & 0xff0000) >> 16); - pcnet_aprom_writeb(d, addr+3, (val & 0xff000000) >> 24); - } -} - -static uint32_t pcnet_mmio_readl(void *opaque, hwaddr addr) -{ - PCNetState *d = opaque; - uint32_t val; - - if (addr & 0x10) - val = pcnet_ioport_readl(d, addr & 0x0f); - else { - addr &= 0x0f; - val = pcnet_aprom_readb(d, addr+3); - val <<= 8; - val |= pcnet_aprom_readb(d, addr+2); - val <<= 8; - val |= pcnet_aprom_readb(d, addr+1); - val <<= 8; - val |= pcnet_aprom_readb(d, addr); - } - trace_pcnet_mmio_readl(opaque, addr, val); - return val; -} - static const VMStateDescription vmstate_pci_pcnet = { .name = "pcnet", .version_id = 3, @@ -241,10 +153,12 @@ static const VMStateDescription vmstate_pci_pcnet = { /* PCI interface */ static const MemoryRegionOps pcnet_mmio_ops = { - .old_mmio = { - .read = { pcnet_mmio_readb, pcnet_mmio_readw, pcnet_mmio_readl }, - .write = { pcnet_mmio_writeb, pcnet_mmio_writew, pcnet_mmio_writel }, - }, + .read = pcnet_ioport_read, + .write = pcnet_ioport_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, .endianness = DEVICE_LITTLE_ENDIAN, }; diff --git a/hw/net/pcnet.c b/hw/net/pcnet.c index 0c44554168..d9ba04bdfc 100644 --- a/hw/net/pcnet.c +++ b/hw/net/pcnet.c @@ -988,14 +988,14 @@ ssize_t pcnet_receive(NetClientState *nc, const uint8_t *buf, size_t size_) uint8_t buf1[60]; int remaining; int crc_err = 0; - int size = size_; + size_t size = size_; if (CSR_DRX(s) || CSR_STOP(s) || CSR_SPND(s) || !size || (CSR_LOOP(s) && !s->looptest)) { return -1; } #ifdef PCNET_DEBUG - printf("pcnet_receive size=%d\n", size); + printf("pcnet_receive size=%zu\n", size); #endif /* if too small buffer, then expand it */ diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index c02cbefece..5266f9b7dd 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -1279,7 +1279,7 @@ static World *rocker_world_type_by_name(Rocker *r, const char *name) for (i = 0; i < ROCKER_WORLD_TYPE_MAX; i++) { if (strcmp(name, world_name(r->worlds[i])) == 0) { return r->worlds[i]; - } + } } return NULL; } diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 46daa16202..2342a095e3 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -817,7 +817,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t RTL8139State *s = qemu_get_nic_opaque(nc); PCIDevice *d = PCI_DEVICE(s); /* size is the length of the buffer passed to the driver */ - int size = size_; + size_t size = size_; const uint8_t *dot1q_buf = NULL; uint32_t packet_header = 0; @@ -826,7 +826,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t static const uint8_t broadcast_macaddr[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; - DPRINTF(">>> received len=%d\n", size); + DPRINTF(">>> received len=%zu\n", size); /* test if board clock is stopped */ if (!s->clock_enabled) @@ -1035,7 +1035,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t if (size+4 > rx_space) { - DPRINTF("C+ Rx mode : descriptor %d size %d received %d + 4\n", + DPRINTF("C+ Rx mode : descriptor %d size %d received %zu + 4\n", descriptor, rx_space, size); s->IntrStatus |= RxOverflow; @@ -1148,7 +1148,7 @@ static ssize_t rtl8139_do_receive(NetClientState *nc, const uint8_t *buf, size_t 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", + "read 0x%04x === available 0x%04x need 0x%04zx\n", s->RxBufferSize, s->RxBufAddr, s->RxBufPtr, avail, size + 8); s->IntrStatus |= RxOverflow; diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index d2fd2040e8..99da2d9297 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -766,9 +766,9 @@ static NetClientInfo net_smc91c111_info = { .receive = smc91c111_receive, }; -static int smc91c111_init1(SysBusDevice *sbd) +static void smc91c111_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); smc91c111_state *s = SMC91C111(dev); memory_region_init_io(&s->mmio, OBJECT(s), &smc91c111_mem_ops, s, @@ -780,7 +780,6 @@ static int smc91c111_init1(SysBusDevice *sbd) object_get_typename(OBJECT(dev)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); /* ??? Save/restore. */ - return 0; } static Property smc91c111_properties[] = { @@ -791,9 +790,8 @@ static Property smc91c111_properties[] = { static void smc91c111_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = smc91c111_init1; + dc->realize = smc91c111_realize; dc->reset = smc91c111_reset; dc->vmsd = &vmstate_smc91c111; dc->props = smc91c111_properties; diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index d239e4bd7d..63ba3929e9 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -84,7 +84,7 @@ typedef uint64_t vlan_bd_t; #define TYPE_VIO_SPAPR_VLAN_DEVICE "spapr-vlan" #define VIO_SPAPR_VLAN_DEVICE(obj) \ - OBJECT_CHECK(VIOsPAPRVLANDevice, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE) + OBJECT_CHECK(SpaprVioVlan, (obj), TYPE_VIO_SPAPR_VLAN_DEVICE) #define RX_POOL_MAX_BDS 4096 #define RX_MAX_POOLS 5 @@ -95,8 +95,8 @@ typedef struct { vlan_bd_t bds[RX_POOL_MAX_BDS]; } RxBufPool; -typedef struct VIOsPAPRVLANDevice { - VIOsPAPRDevice sdev; +typedef struct SpaprVioVlan { + SpaprVioDevice sdev; NICConf nicconf; NICState *nic; MACAddr perm_mac; @@ -107,11 +107,11 @@ typedef struct VIOsPAPRVLANDevice { QEMUTimer *rxp_timer; uint32_t compat_flags; /* Compatibility flags for migration */ RxBufPool *rx_pool[RX_MAX_POOLS]; /* Receive buffer descriptor pools */ -} VIOsPAPRVLANDevice; +} SpaprVioVlan; static int spapr_vlan_can_receive(NetClientState *nc) { - VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); + SpaprVioVlan *dev = qemu_get_nic_opaque(nc); return (dev->isopen && dev->rx_bufs > 0); } @@ -123,7 +123,7 @@ static int spapr_vlan_can_receive(NetClientState *nc) * suitable receive buffer available. This function is used to increase * this counter by one. */ -static void spapr_vlan_record_dropped_rx_frame(VIOsPAPRVLANDevice *dev) +static void spapr_vlan_record_dropped_rx_frame(SpaprVioVlan *dev) { uint64_t cnt; @@ -134,7 +134,7 @@ static void spapr_vlan_record_dropped_rx_frame(VIOsPAPRVLANDevice *dev) /** * Get buffer descriptor from one of our receive buffer pools */ -static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, +static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(SpaprVioVlan *dev, size_t size) { vlan_bd_t bd; @@ -168,7 +168,7 @@ static vlan_bd_t spapr_vlan_get_rx_bd_from_pool(VIOsPAPRVLANDevice *dev, * 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, +static vlan_bd_t spapr_vlan_get_rx_bd_from_page(SpaprVioVlan *dev, size_t size) { int buf_ptr = dev->use_buf_ptr; @@ -203,8 +203,8 @@ static vlan_bd_t spapr_vlan_get_rx_bd_from_page(VIOsPAPRVLANDevice *dev, static ssize_t spapr_vlan_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - VIOsPAPRVLANDevice *dev = qemu_get_nic_opaque(nc); - VIOsPAPRDevice *sdev = VIO_SPAPR_DEVICE(dev); + SpaprVioVlan *dev = qemu_get_nic_opaque(nc); + SpaprVioDevice *sdev = VIO_SPAPR_DEVICE(dev); vlan_bd_t rxq_bd = vio_ldq(sdev, dev->buf_list + VLAN_RXQ_BD_OFF); vlan_bd_t bd; uint64_t handle; @@ -280,7 +280,7 @@ static NetClientInfo net_spapr_vlan_info = { static void spapr_vlan_flush_rx_queue(void *opaque) { - VIOsPAPRVLANDevice *dev = opaque; + SpaprVioVlan *dev = opaque; qemu_flush_queued_packets(qemu_get_queue(dev->nic)); } @@ -296,9 +296,9 @@ static void spapr_vlan_reset_rx_pool(RxBufPool *rxp) memset(rxp->bds, 0, sizeof(rxp->bds)); } -static void spapr_vlan_reset(VIOsPAPRDevice *sdev) +static void spapr_vlan_reset(SpaprVioDevice *sdev) { - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); int i; dev->buf_list = 0; @@ -316,9 +316,9 @@ static void spapr_vlan_reset(VIOsPAPRDevice *sdev) qemu_format_nic_info_str(qemu_get_queue(dev->nic), dev->nicconf.macaddr.a); } -static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) +static void spapr_vlan_realize(SpaprVioDevice *sdev, Error **errp) { - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); qemu_macaddr_default_if_unset(&dev->nicconf.macaddr); @@ -334,7 +334,7 @@ static void spapr_vlan_realize(VIOsPAPRDevice *sdev, Error **errp) static void spapr_vlan_instance_init(Object *obj) { - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(obj); int i; device_add_bootindex_property(obj, &dev->nicconf.bootindex, @@ -351,7 +351,7 @@ static void spapr_vlan_instance_init(Object *obj) static void spapr_vlan_instance_finalize(Object *obj) { - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(obj); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(obj); int i; if (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) { @@ -367,7 +367,7 @@ static void spapr_vlan_instance_finalize(Object *obj) } } -void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) +void spapr_vlan_create(SpaprVioBus *bus, NICInfo *nd) { DeviceState *dev; @@ -378,9 +378,9 @@ void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd) qdev_init_nofail(dev); } -static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) +static int spapr_vlan_devnode(SpaprVioDevice *dev, void *fdt, int node_off) { - VIOsPAPRVLANDevice *vdev = VIO_SPAPR_VLAN_DEVICE(dev); + SpaprVioVlan *vdev = VIO_SPAPR_VLAN_DEVICE(dev); uint8_t padded_mac[8] = {0, 0}; int ret; @@ -415,7 +415,7 @@ static int spapr_vlan_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) return 0; } -static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, +static int check_bd(SpaprVioVlan *dev, vlan_bd_t bd, target_ulong alignment) { if ((VLAN_BD_ADDR(bd) % alignment) @@ -434,7 +434,7 @@ static int check_bd(VIOsPAPRVLANDevice *dev, vlan_bd_t bd, } static target_ulong h_register_logical_lan(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -442,8 +442,8 @@ static target_ulong h_register_logical_lan(PowerPCCPU *cpu, target_ulong buf_list = args[1]; target_ulong rec_queue = args[2]; target_ulong filter_list = args[3]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); vlan_bd_t filter_list_bd; if (!dev) { @@ -500,12 +500,12 @@ static target_ulong h_register_logical_lan(PowerPCCPU *cpu, static target_ulong h_free_logical_lan(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); if (!dev) { return H_PARAMETER; @@ -539,7 +539,7 @@ static int rx_pool_size_compare(const void *p1, const void *p2) * 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) +static int spapr_vlan_get_rx_pool_id(SpaprVioVlan *dev, int size) { int pool; @@ -555,7 +555,7 @@ static int spapr_vlan_get_rx_pool_id(VIOsPAPRVLANDevice *dev, int size) /** * Enqueuing receive buffer by adding it to one of our receive buffer pools */ -static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev, +static target_long spapr_vlan_add_rxbuf_to_pool(SpaprVioVlan *dev, target_ulong buf) { int size = VLAN_BD_LEN(buf); @@ -602,7 +602,7 @@ static target_long spapr_vlan_add_rxbuf_to_pool(VIOsPAPRVLANDevice *dev, * 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, +static target_long spapr_vlan_add_rxbuf_to_page(SpaprVioVlan *dev, target_ulong buf) { vlan_bd_t bd; @@ -628,14 +628,14 @@ static target_long spapr_vlan_add_rxbuf_to_page(VIOsPAPRVLANDevice *dev, } static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong buf = args[1]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); target_long ret; trace_spapr_vlan_h_add_logical_lan_buffer(reg, buf); @@ -678,14 +678,14 @@ static target_ulong h_add_logical_lan_buffer(PowerPCCPU *cpu, } static target_ulong h_send_logical_lan(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong *bufs = args + 1; target_ulong continue_token = args[7]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); unsigned total_len; uint8_t *lbuf, *p; int i, nbufs; @@ -745,11 +745,11 @@ static target_ulong h_send_logical_lan(PowerPCCPU *cpu, return H_SUCCESS; } -static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); if (!dev) { return H_PARAMETER; @@ -759,14 +759,14 @@ static target_ulong h_multicast_ctrl(PowerPCCPU *cpu, sPAPRMachineState *spapr, } static target_ulong h_change_logical_lan_mac(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong macaddr = args[1]; - VIOsPAPRDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRVLANDevice *dev = VIO_SPAPR_VLAN_DEVICE(sdev); + SpaprVioDevice *sdev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioVlan *dev = VIO_SPAPR_VLAN_DEVICE(sdev); int i; for (i = 0; i < ETH_ALEN; i++) { @@ -780,16 +780,16 @@ static target_ulong h_change_logical_lan_mac(PowerPCCPU *cpu, } static Property spapr_vlan_properties[] = { - DEFINE_SPAPR_PROPERTIES(VIOsPAPRVLANDevice, sdev), - DEFINE_NIC_PROPERTIES(VIOsPAPRVLANDevice, nicconf), - DEFINE_PROP_BIT("use-rx-buffer-pools", VIOsPAPRVLANDevice, + DEFINE_SPAPR_PROPERTIES(SpaprVioVlan, sdev), + DEFINE_NIC_PROPERTIES(SpaprVioVlan, nicconf), + DEFINE_PROP_BIT("use-rx-buffer-pools", SpaprVioVlan, 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; + SpaprVioVlan *dev = opaque; return (dev->compat_flags & SPAPRVLAN_FLAG_RX_BUF_POOLS) != 0; } @@ -813,7 +813,7 @@ static const VMStateDescription vmstate_rx_pools = { .minimum_version_id = 1, .needed = spapr_vlan_rx_buffer_pools_needed, .fields = (VMStateField[]) { - VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, VIOsPAPRVLANDevice, + VMSTATE_ARRAY_OF_POINTER_TO_STRUCT(rx_pool, SpaprVioVlan, RX_MAX_POOLS, 1, vmstate_rx_buffer_pool, RxBufPool), VMSTATE_END_OF_LIST() @@ -825,14 +825,14 @@ static const VMStateDescription vmstate_spapr_llan = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_SPAPR_VIO(sdev, VIOsPAPRVLANDevice), + VMSTATE_SPAPR_VIO(sdev, SpaprVioVlan), /* LLAN state */ - VMSTATE_BOOL(isopen, VIOsPAPRVLANDevice), - VMSTATE_UINT64(buf_list, VIOsPAPRVLANDevice), - VMSTATE_UINT32(add_buf_ptr, VIOsPAPRVLANDevice), - VMSTATE_UINT32(use_buf_ptr, VIOsPAPRVLANDevice), - VMSTATE_UINT32(rx_bufs, VIOsPAPRVLANDevice), - VMSTATE_UINT64(rxq_ptr, VIOsPAPRVLANDevice), + VMSTATE_BOOL(isopen, SpaprVioVlan), + VMSTATE_UINT64(buf_list, SpaprVioVlan), + VMSTATE_UINT32(add_buf_ptr, SpaprVioVlan), + VMSTATE_UINT32(use_buf_ptr, SpaprVioVlan), + VMSTATE_UINT32(rx_bufs, SpaprVioVlan), + VMSTATE_UINT64(rxq_ptr, SpaprVioVlan), VMSTATE_END_OF_LIST() }, @@ -845,7 +845,7 @@ static const VMStateDescription vmstate_spapr_llan = { static void spapr_vlan_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); k->realize = spapr_vlan_realize; k->reset = spapr_vlan_reset; @@ -863,7 +863,7 @@ static void spapr_vlan_class_init(ObjectClass *klass, void *data) static const TypeInfo spapr_vlan_info = { .name = TYPE_VIO_SPAPR_VLAN_DEVICE, .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(VIOsPAPRVLANDevice), + .instance_size = sizeof(SpaprVioVlan), .class_init = spapr_vlan_class_init, .instance_init = spapr_vlan_instance_init, .instance_finalize = spapr_vlan_instance_finalize, diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index 165562d788..b3375ebb45 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -457,8 +457,10 @@ static const MemoryRegionOps stellaris_enet_ops = { .endianness = DEVICE_NATIVE_ENDIAN, }; -static void stellaris_enet_reset(stellaris_enet_state *s) +static void stellaris_enet_reset(DeviceState *dev) { + stellaris_enet_state *s = STELLARIS_ENET(dev); + s->mdv = 0x80; s->rctl = SE_RCTL_BADCRC; s->im = SE_INT_PHY | SE_INT_MD | SE_INT_RXER | SE_INT_FOV | SE_INT_TXEMP @@ -473,9 +475,9 @@ static NetClientInfo net_stellaris_enet_info = { .receive = stellaris_enet_receive, }; -static int stellaris_enet_init(SysBusDevice *sbd) +static void stellaris_enet_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); stellaris_enet_state *s = STELLARIS_ENET(dev); memory_region_init_io(&s->mmio, OBJECT(s), &stellaris_enet_ops, s, @@ -487,9 +489,6 @@ static int stellaris_enet_init(SysBusDevice *sbd) s->nic = qemu_new_nic(&net_stellaris_enet_info, &s->conf, object_get_typename(OBJECT(dev)), dev->id, s); qemu_format_nic_info_str(qemu_get_queue(s->nic), s->conf.macaddr.a); - - stellaris_enet_reset(s); - return 0; } static Property stellaris_enet_properties[] = { @@ -500,9 +499,9 @@ static Property stellaris_enet_properties[] = { static void stellaris_enet_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = stellaris_enet_init; + dc->realize = stellaris_enet_realize; + dc->reset = stellaris_enet_reset; dc->props = stellaris_enet_properties; dc->vmsd = &vmstate_stellaris_enet; } diff --git a/hw/net/trace-events b/hw/net/trace-events index 663bea1b74..3cd9e122df 100644 --- a/hw/net/trace-events +++ b/hw/net/trace-events @@ -1,15 +1,15 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/net/etraxfs_eth.c +# etraxfs_eth.c mdio_phy_read(int regnum, uint16_t value) "read phy_reg:%d value:0x%04x" mdio_phy_write(int regnum, uint16_t value) "write phy_reg:%d value:0x%04x" mdio_bitbang(bool mdc, bool mdio, int state, uint16_t cnt, unsigned int drive) "bitbang mdc=%u mdio=%u state=%d cnt=%u drv=%d" -# hw/net/lance.c +# lance.c lance_mem_readw(uint64_t addr, uint32_t ret) "addr=0x%"PRIx64"val=0x%04x" lance_mem_writew(uint64_t addr, uint32_t val) "addr=0x%"PRIx64"val=0x%04x" -# hw/net/milkymist-minimac2.c +# milkymist-minimac2.c milkymist_minimac2_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_minimac2_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_minimac2_mdio_write(uint8_t phy_addr, uint8_t addr, uint16_t value) "phy_addr 0x%02x addr 0x%02x value 0x%04x" @@ -21,20 +21,20 @@ milkymist_minimac2_raise_irq_rx(void) "Raise IRQ RX" milkymist_minimac2_lower_irq_rx(void) "Lower IRQ RX" milkymist_minimac2_pulse_irq_tx(void) "Pulse IRQ TX" -# hw/net/mipsnet.c +# mipsnet.c 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_irq(uint32_t isr, uint32_t intctl) "set irq to %d (0x%02x)" -# hw/net/ne2000.c +# ne2000.c ne2000_read(uint64_t addr, uint64_t val) "read addr=0x%" PRIx64 " val=0x%" PRIx64 ne2000_write(uint64_t addr, uint64_t val) "write addr=0x%" PRIx64 " val=0x%" PRIx64 ne2000_ioport_read(uint64_t addr, uint64_t val) "io read addr=0x%02" PRIx64 " val=0x%02" PRIx64 ne2000_ioport_write(uint64_t addr, uint64_t val) "io write addr=0x%02" PRIx64 " val=0x%02" PRIx64 -# hw/net/opencores_eth.c +# opencores_eth.c open_eth_mii_write(unsigned idx, uint16_t v) "MII[0x%02x] <- 0x%04x" open_eth_mii_read(unsigned idx, uint16_t v) "MII[0x%02x] -> 0x%04x" open_eth_update_irq(uint32_t v) "IRQ <- 0x%x" @@ -48,7 +48,7 @@ open_eth_reg_write(uint32_t addr, uint32_t v) "MAC[0x%02x] <- 0x%08x" open_eth_desc_read(uint32_t addr, uint32_t v) "DESC[0x%04x] -> 0x%08x" open_eth_desc_write(uint32_t addr, uint32_t v) "DESC[0x%04x] <- 0x%08x" -# hw/net/pcnet.c +# pcnet.c pcnet_s_reset(void *s) "s=%p" pcnet_user_int(void *s) "s=%p" pcnet_isr_change(void *s, uint32_t isr, uint32_t isr_old) "s=%p INTA=%d<=%d" @@ -56,19 +56,13 @@ pcnet_init(void *s, uint64_t init_addr) "s=%p init_addr=0x%"PRIx64 pcnet_rlen_tlen(void *s, uint32_t rlen, uint32_t tlen) "s=%p rlen=%d tlen=%d" pcnet_ss32_rdra_tdra(void *s, uint32_t ss32, uint32_t rdra, uint32_t rcvrl, uint32_t tdra, uint32_t xmtrl) "s=%p ss32=%d rdra=0x%08x[%d] tdra=0x%08x[%d]" -# hw/net/pcnet-pci.c +# pcnet-pci.c pcnet_aprom_writeb(void *opaque, uint32_t addr, uint32_t val) "opaque=%p addr=0x%08x val=0x%02x" pcnet_aprom_readb(void *opaque, uint32_t addr, uint32_t val) "opaque=%p addr=0x%08x val=0x%02x" pcnet_ioport_read(void *opaque, uint64_t addr, unsigned size) "opaque=%p addr=0x%"PRIx64" size=%d" pcnet_ioport_write(void *opaque, uint64_t addr, uint64_t data, unsigned size) "opaque=%p addr=0x%"PRIx64" data=0x%"PRIx64" size=%d" -pcnet_mmio_writeb(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=0x%"PRIx64" val=0x%x" -pcnet_mmio_writew(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=0x%"PRIx64" val=0x%x" -pcnet_mmio_writel(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=0x%"PRIx64" val=0x%x" -pcnet_mmio_readb(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=0x%"PRIx64" val=0x%x" -pcnet_mmio_readw(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=0x%"PRIx64" val=0x%x" -pcnet_mmio_readl(void *opaque, uint64_t addr, uint32_t val) "opaque=%p addr=0x%"PRIx64" val=0x%x" -# hw/net/net_rx_pkt.c +# net_rx_pkt.c net_rx_pkt_parsed(bool ip4, bool ip6, bool udp, bool tcp, size_t l3o, size_t l4o, size_t l5o) "RX packet parsed: ip4: %d, ip6: %d, udp: %d, tcp: %d, l3 offset: %zu, l4 offset: %zu, l5 offset: %zu" net_rx_pkt_l4_csum_validate_entry(void) "Starting L4 checksum validation" net_rx_pkt_l4_csum_validate_not_xxp(void) "Not a TCP/UDP packet" @@ -104,7 +98,10 @@ net_rx_pkt_rss_ip6_ex(void) "Calculating IPv6/EX RSS hash" net_rx_pkt_rss_hash(size_t rss_length, uint32_t rss_hash) "RSS hash for %zu bytes: 0x%X" net_rx_pkt_rss_add_chunk(void* ptr, size_t size, size_t input_offset) "Add RSS chunk %p, %zu bytes, RSS input offset %zu bytes" -# hw/net/e1000x_common.c +# e1000.c +e1000_receiver_overrun(size_t s, uint32_t rdh, uint32_t rdt) "Receiver overrun: dropped packet of %zu bytes, RDH=%u, RDT=%u" + +# e1000x_common.c e1000x_rx_can_recv_disabled(bool link_up, bool rx_enabled, bool pci_master) "link_up: %d, rx_enabled %d, pci_master %d" e1000x_vlan_is_vlan_pkt(bool is_vlan_pkt, uint16_t eth_proto, uint16_t vet) "Is VLAN packet: %d, ETH proto: 0x%X, VET: 0x%X" e1000x_rx_flt_ucast_match(uint32_t idx, uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "unicast match[%d]: %02x:%02x:%02x:%02x:%02x:%02x" @@ -117,7 +114,7 @@ e1000x_mac_indicate(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, e1000x_link_negotiation_start(void) "Start link auto negotiation" e1000x_link_negotiation_done(void) "Auto negotiation is completed" -# hw/net/e1000e_core.c +# e1000e_core.c e1000e_core_write(uint64_t index, uint32_t size, uint64_t val) "Write to register 0x%"PRIx64", %d byte(s), value: 0x%"PRIx64 e1000e_core_read(uint64_t index, uint32_t size, uint64_t val) "Read from register 0x%"PRIx64", %d byte(s), value: 0x%"PRIx64 e1000e_core_mdic_read(uint8_t page, uint32_t addr, uint32_t data) "MDIC READ: PHY[%u][%u] = 0x%x" @@ -245,10 +242,12 @@ e1000e_irq_msix_pending_clearing(uint32_t cause, uint32_t int_cfg, uint32_t vec) e1000e_wrn_msix_vec_wrong(uint32_t cause, uint32_t cfg) "Invalid configuration for cause 0x%x: 0x%x" e1000e_wrn_msix_invalid(uint32_t cause, uint32_t cfg) "Invalid entry for cause 0x%x: 0x%x" -e1000e_mac_set_permanent(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Set permanent MAC: %02x:%02x:%02x:%02x:%02x:%02x" e1000e_mac_set_sw(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Set SW MAC: %02x:%02x:%02x:%02x:%02x:%02x" -# hw/net/e1000e.c +e1000e_vm_state_running(void) "VM state is running" +e1000e_vm_state_stopped(void) "VM state is stopped" + +# e1000e.c e1000e_cb_pci_realize(void) "E1000E PCI realize entry" e1000e_cb_pci_uninit(void) "E1000E PCI unit entry" e1000e_cb_qdev_reset(void) "E1000E qdev reset entry" @@ -269,12 +268,10 @@ e1000e_msi_init_fail(int32_t res) "Failed to initialize MSI, error %d" e1000e_msix_init_fail(int32_t res) "Failed to initialize MSI-X, error %d" e1000e_msix_use_vector_fail(uint32_t vec, int32_t res) "Failed to use MSI-X vector %d, error %d" +e1000e_mac_set_permanent(uint8_t b0, uint8_t b1, uint8_t b2, uint8_t b3, uint8_t b4, uint8_t b5) "Set permanent MAC: %02x:%02x:%02x:%02x:%02x:%02x" e1000e_cfg_support_virtio(bool support) "Virtio header supported: %d" -e1000e_vm_state_running(void) "VM state is running" -e1000e_vm_state_stopped(void) "VM state is stopped" - -# hw/net/spapr_llan.c +# spapr_llan.c spapr_vlan_get_rx_bd_from_pool_found(int pool, int32_t count, uint32_t rx_bufs) "pool=%d count=%"PRId32" rxbufs=%"PRIu32 spapr_vlan_get_rx_bd_from_page(int buf_ptr, uint64_t bd) "use_buf_ptr=%d bd=0x%016"PRIx64 spapr_vlan_get_rx_bd_from_page_found(uint32_t use_buf_ptr, uint32_t rx_bufs) "ptr=%"PRIu32" rxbufs=%"PRIu32 @@ -290,7 +287,7 @@ spapr_vlan_h_send_logical_lan_rxbufs(uint32_t rx_bufs) "rxbufs = %"PRIu32 spapr_vlan_h_send_logical_lan_buf_desc(uint64_t buf) " buf desc: 0x%"PRIx64 spapr_vlan_h_send_logical_lan_total(int nbufs, unsigned total_len) "%d buffers, total length 0x%x" -# hw/net/sungem.c +# sungem.c sungem_tx_checksum(uint16_t start, uint16_t off) "TX checksumming from byte %d, inserting at %d" sungem_tx_checksum_oob(void) "TX checksum out of packet bounds" sungem_tx_unfinished(void) "TX packet started without finishing the previous one" @@ -334,7 +331,7 @@ sungem_mmio_mif_read(uint64_t addr, uint64_t val) "MMIO mif read from 0x%"PRIx64 sungem_mmio_pcs_write(uint64_t addr, uint64_t val) "MMIO pcs write to 0x%"PRIx64" val=0x%"PRIx64 sungem_mmio_pcs_read(uint64_t addr, uint64_t val) "MMIO pcs read from 0x%"PRIx64" val=0x%"PRIx64 -# hw/net/sunhme.c +# sunhme.c sunhme_seb_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64 sunhme_seb_read(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64 sunhme_etx_write(uint64_t addr, uint64_t value) "addr 0x%"PRIx64" value 0x%"PRIx64 @@ -362,3 +359,9 @@ sunhme_rx_filter_reject(void) "rejecting incoming frame" sunhme_rx_filter_accept(void) "accepting incoming frame" sunhme_rx_desc(uint32_t addr, int offset, uint32_t status, int len, int cr, int nr) "addr 0x%"PRIx32"(+0x%x) status 0x%"PRIx32 " len %d (ring %d/%d)" sunhme_rx_xsum_calc(uint16_t xsum) "calculated incoming xsum as 0x%x" + +# virtio-net.c +virtio_net_announce_notify(void) "" +virtio_net_announce_timer(int round) "%d" +virtio_net_handle_announce(int round) "%d" +virtio_net_post_load_device(void) diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c new file mode 100644 index 0000000000..aac0e98228 --- /dev/null +++ b/hw/net/vhost_net-stub.c @@ -0,0 +1,92 @@ +/* + * vhost-net support + * + * Copyright Red Hat, Inc. 2010 + * + * 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 "net/net.h" +#include "net/tap.h" +#include "net/vhost-user.h" + +#include "hw/virtio/virtio-net.h" +#include "net/vhost_net.h" +#include "qemu/error-report.h" + + +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"); + return NULL; +} + +int vhost_net_start(VirtIODevice *dev, + NetClientState *ncs, + int total_queues) +{ + return -ENOSYS; +} +void vhost_net_stop(VirtIODevice *dev, + NetClientState *ncs, + int total_queues) +{ +} + +void vhost_net_cleanup(struct vhost_net *net) +{ +} + +uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) +{ + return features; +} + +void vhost_net_ack_features(struct vhost_net *net, uint64_t features) +{ +} + +uint64_t vhost_net_get_acked_features(VHostNetState *net) +{ + return 0; +} + +bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) +{ + return false; +} + +void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, + int idx, bool mask) +{ +} + +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; +} + +int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) +{ + return 0; +} diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index e037db63a3..be3cc88370 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -18,17 +18,13 @@ #include "net/tap.h" #include "net/vhost-user.h" +#include "standard-headers/linux/vhost_types.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" #include "qemu/error-report.h" -#ifdef CONFIG_VHOST_NET -#include #include -#include -#include -#include #include #include @@ -136,7 +132,7 @@ static int vhost_net_get_fd(NetClientState *backend) return tap_get_fd(backend); default: fprintf(stderr, "vhost-net requires tap backend\n"); - return -EBADFD; + return -ENOSYS; } } @@ -194,6 +190,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } /* Set sane init value. Override when guest acks. */ +#ifdef CONFIG_VHOST_NET_USER if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { features = vhost_user_get_acked_features(net->nc); if (~net->dev.features & features) { @@ -203,6 +200,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) goto fail; } } +#endif vhost_net_ack_features(net, features); @@ -414,10 +412,12 @@ VHostNetState *get_vhost_net(NetClientState *nc) case NET_CLIENT_DRIVER_TAP: vhost_net = tap_get_vhost_net(nc); break; +#ifdef CONFIG_VHOST_NET_USER case NET_CLIENT_DRIVER_VHOST_USER: vhost_net = vhost_user_get_vhost_net(nc); assert(vhost_net); break; +#endif default: break; } @@ -449,76 +449,3 @@ int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) return vhost_ops->vhost_net_set_mtu(&net->dev, mtu); } - -#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"); - return NULL; -} - -int vhost_net_start(VirtIODevice *dev, - NetClientState *ncs, - int total_queues) -{ - return -ENOSYS; -} -void vhost_net_stop(VirtIODevice *dev, - NetClientState *ncs, - int total_queues) -{ -} - -void vhost_net_cleanup(struct vhost_net *net) -{ -} - -uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) -{ - return features; -} - -void vhost_net_ack_features(struct vhost_net *net, uint64_t features) -{ -} - -uint64_t vhost_net_get_acked_features(VHostNetState *net) -{ - return 0; -} - -bool vhost_net_virtqueue_pending(VHostNetState *net, int idx) -{ - return false; -} - -void vhost_net_virtqueue_mask(VHostNetState *net, VirtIODevice *dev, - int idx, bool mask) -{ -} - -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; -} - -int vhost_net_set_mtu(struct vhost_net *net, uint16_t mtu) -{ - return 0; -} -#endif diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index f154756e85..ffe0872fff 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -21,12 +21,14 @@ #include "qemu/timer.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" +#include "net/announce.h" #include "hw/virtio/virtio-bus.h" #include "qapi/error.h" #include "qapi/qapi-events-net.h" #include "hw/virtio/virtio-access.h" #include "migration/misc.h" #include "standard-headers/linux/ethtool.h" +#include "trace.h" #define VIRTIO_NET_VM_VERSION 11 @@ -41,29 +43,58 @@ #define VIRTIO_NET_RX_QUEUE_MIN_SIZE VIRTIO_NET_RX_QUEUE_DEFAULT_SIZE #define VIRTIO_NET_TX_QUEUE_MIN_SIZE VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE -/* - * Calculate the number of bytes up to and including the given 'field' of - * 'container'. - */ -#define endof(container, field) \ - (offsetof(container, field) + sizeof_field(container, field)) +#define VIRTIO_NET_IP4_ADDR_SIZE 8 /* ipv4 saddr + daddr */ -typedef struct VirtIOFeature { - uint64_t flags; - size_t end; -} VirtIOFeature; +#define VIRTIO_NET_TCP_FLAG 0x3F +#define VIRTIO_NET_TCP_HDR_LENGTH 0xF000 + +/* IPv4 max payload, 16 bits in the header */ +#define VIRTIO_NET_MAX_IP4_PAYLOAD (65535 - sizeof(struct ip_header)) +#define VIRTIO_NET_MAX_TCP_PAYLOAD 65535 + +/* header length value in ip header without option */ +#define VIRTIO_NET_IP4_HEADER_LENGTH 5 + +#define VIRTIO_NET_IP6_ADDR_SIZE 32 /* ipv6 saddr + daddr */ +#define VIRTIO_NET_MAX_IP6_PAYLOAD VIRTIO_NET_MAX_TCP_PAYLOAD + +/* Purge coalesced packets timer interval, This value affects the performance + a lot, and should be tuned carefully, '300000'(300us) is the recommended + value to pass the WHQL test, '50000' can gain 2x netperf throughput with + tso/gso/gro 'off'. */ +#define VIRTIO_NET_RSC_DEFAULT_INTERVAL 300000 + +/* temporary until standard header include it */ +#if !defined(VIRTIO_NET_HDR_F_RSC_INFO) + +#define VIRTIO_NET_HDR_F_RSC_INFO 4 /* rsc_ext data in csum_ fields */ +#define VIRTIO_NET_F_RSC_EXT 61 + +static inline __virtio16 *virtio_net_rsc_ext_num_packets( + struct virtio_net_hdr *hdr) +{ + return &hdr->csum_start; +} + +static inline __virtio16 *virtio_net_rsc_ext_num_dupacks( + struct virtio_net_hdr *hdr) +{ + return &hdr->csum_offset; +} + +#endif static VirtIOFeature feature_sizes[] = { {.flags = 1ULL << VIRTIO_NET_F_MAC, - .end = endof(struct virtio_net_config, mac)}, + .end = virtio_endof(struct virtio_net_config, mac)}, {.flags = 1ULL << VIRTIO_NET_F_STATUS, - .end = endof(struct virtio_net_config, status)}, + .end = virtio_endof(struct virtio_net_config, status)}, {.flags = 1ULL << VIRTIO_NET_F_MQ, - .end = endof(struct virtio_net_config, max_virtqueue_pairs)}, + .end = virtio_endof(struct virtio_net_config, max_virtqueue_pairs)}, {.flags = 1ULL << VIRTIO_NET_F_MTU, - .end = endof(struct virtio_net_config, mtu)}, + .end = virtio_endof(struct virtio_net_config, mtu)}, {.flags = 1ULL << VIRTIO_NET_F_SPEED_DUPLEX, - .end = endof(struct virtio_net_config, duplex)}, + .end = virtio_endof(struct virtio_net_config, duplex)}, {} }; @@ -119,14 +150,42 @@ static bool virtio_net_started(VirtIONet *n, uint8_t status) (n->status & VIRTIO_NET_S_LINK_UP) && vdev->vm_running; } +static void virtio_net_announce_notify(VirtIONet *net) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(net); + trace_virtio_net_announce_notify(); + + net->status |= VIRTIO_NET_S_ANNOUNCE; + virtio_notify_config(vdev); +} + static void virtio_net_announce_timer(void *opaque) { VirtIONet *n = opaque; + trace_virtio_net_announce_timer(n->announce_timer.round); + + n->announce_timer.round--; + virtio_net_announce_notify(n); +} + +static void virtio_net_announce(NetClientState *nc) +{ + VirtIONet *n = qemu_get_nic_opaque(nc); VirtIODevice *vdev = VIRTIO_DEVICE(n); - n->announce_counter--; - n->status |= VIRTIO_NET_S_ANNOUNCE; - virtio_notify_config(vdev); + /* + * Make sure the virtio migration announcement timer isn't running + * If it is, let it trigger announcement so that we do not cause + * confusion. + */ + if (n->announce_timer.round) { + return; + } + + if (virtio_vdev_has_feature(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE) && + virtio_vdev_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) { + virtio_net_announce_notify(n); + } } static void virtio_net_vhost_status(VirtIONet *n, uint8_t status) @@ -329,7 +388,7 @@ static void rxfilter_notify(NetClientState *nc) if (nc->rxfilter_notify_enabled) { gchar *path = object_get_canonical_path(OBJECT(n->qdev)); qapi_event_send_nic_rx_filter_changed(!!n->netclient_name, - n->netclient_name, path, &error_abort); + n->netclient_name, path); g_free(path); /* disable event notification to avoid events flooding */ @@ -438,8 +497,8 @@ static void virtio_net_reset(VirtIODevice *vdev) n->nobcast = 0; /* multiqueue is disabled by default */ n->curr_queues = 1; - timer_del(n->announce_timer); - n->announce_counter = 0; + timer_del(n->announce_timer.tm); + n->announce_timer.round = 0; n->status &= ~VIRTIO_NET_S_ANNOUNCE; /* Flush any MAC and VLAN filter table state */ @@ -628,6 +687,7 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, if (!get_vhost_net(nc->peer)) { return features; } + features = vhost_net_get_features(get_vhost_net(nc->peer), features); vdev->backend_features = features; @@ -701,6 +761,11 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) virtio_has_feature(features, VIRTIO_F_VERSION_1)); + n->rsc4_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && + virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO4); + n->rsc6_enabled = virtio_has_feature(features, VIRTIO_NET_F_RSC_EXT) && + virtio_has_feature(features, VIRTIO_NET_F_GUEST_TSO6); + if (n->has_vnet_hdr) { n->curr_guest_offloads = virtio_net_guest_offloads_by_features(features); @@ -781,6 +846,12 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, return VIRTIO_NET_ERR; } + n->rsc4_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) && + virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO4); + n->rsc6_enabled = virtio_has_feature(offloads, VIRTIO_NET_F_RSC_EXT) && + virtio_has_feature(offloads, VIRTIO_NET_F_GUEST_TSO6); + virtio_clear_feature(&offloads, VIRTIO_NET_F_RSC_EXT); + supported_offloads = virtio_net_supported_guest_offloads(n); if (offloads & ~supported_offloads) { return VIRTIO_NET_ERR; @@ -923,13 +994,12 @@ static int virtio_net_handle_vlan_table(VirtIONet *n, uint8_t cmd, static int virtio_net_handle_announce(VirtIONet *n, uint8_t cmd, struct iovec *iov, unsigned int iov_cnt) { + trace_virtio_net_handle_announce(n->announce_timer.round); if (cmd == VIRTIO_NET_CTRL_ANNOUNCE_ACK && n->status & VIRTIO_NET_S_ANNOUNCE) { n->status &= ~VIRTIO_NET_S_ANNOUNCE; - if (n->announce_counter) { - timer_mod(n->announce_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + - self_announce_delay(n->announce_counter)); + if (n->announce_timer.round) { + qemu_announce_timer_step(&n->announce_timer); } return VIRTIO_NET_OK; } else { @@ -1292,7 +1362,7 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, return size; } -static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, +static ssize_t virtio_net_do_receive(NetClientState *nc, const uint8_t *buf, size_t size) { ssize_t r; @@ -1303,6 +1373,612 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, return r; } +static void virtio_net_rsc_extract_unit4(VirtioNetRscChain *chain, + const uint8_t *buf, + VirtioNetRscUnit *unit) +{ + uint16_t ip_hdrlen; + struct ip_header *ip; + + ip = (struct ip_header *)(buf + chain->n->guest_hdr_len + + sizeof(struct eth_header)); + unit->ip = (void *)ip; + ip_hdrlen = (ip->ip_ver_len & 0xF) << 2; + unit->ip_plen = &ip->ip_len; + unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip) + ip_hdrlen); + unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; + unit->payload = htons(*unit->ip_plen) - ip_hdrlen - unit->tcp_hdrlen; +} + +static void virtio_net_rsc_extract_unit6(VirtioNetRscChain *chain, + const uint8_t *buf, + VirtioNetRscUnit *unit) +{ + struct ip6_header *ip6; + + ip6 = (struct ip6_header *)(buf + chain->n->guest_hdr_len + + sizeof(struct eth_header)); + unit->ip = ip6; + unit->ip_plen = &(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); + unit->tcp = (struct tcp_header *)(((uint8_t *)unit->ip)\ + + sizeof(struct ip6_header)); + unit->tcp_hdrlen = (htons(unit->tcp->th_offset_flags) & 0xF000) >> 10; + + /* There is a difference between payload lenght in ipv4 and v6, + ip header is excluded in ipv6 */ + unit->payload = htons(*unit->ip_plen) - unit->tcp_hdrlen; +} + +static size_t virtio_net_rsc_drain_seg(VirtioNetRscChain *chain, + VirtioNetRscSeg *seg) +{ + int ret; + struct virtio_net_hdr *h; + + h = (struct virtio_net_hdr *)seg->buf; + h->flags = 0; + h->gso_type = VIRTIO_NET_HDR_GSO_NONE; + + if (seg->is_coalesced) { + *virtio_net_rsc_ext_num_packets(h) = seg->packets; + *virtio_net_rsc_ext_num_dupacks(h) = seg->dup_ack; + h->flags = VIRTIO_NET_HDR_F_RSC_INFO; + if (chain->proto == ETH_P_IP) { + h->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + } else { + h->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + } + } + + ret = virtio_net_do_receive(seg->nc, seg->buf, seg->size); + QTAILQ_REMOVE(&chain->buffers, seg, next); + g_free(seg->buf); + g_free(seg); + + return ret; +} + +static void virtio_net_rsc_purge(void *opq) +{ + VirtioNetRscSeg *seg, *rn; + VirtioNetRscChain *chain = (VirtioNetRscChain *)opq; + + QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn) { + if (virtio_net_rsc_drain_seg(chain, seg) == 0) { + chain->stat.purge_failed++; + continue; + } + } + + chain->stat.timer++; + if (!QTAILQ_EMPTY(&chain->buffers)) { + timer_mod(chain->drain_timer, + qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); + } +} + +static void virtio_net_rsc_cleanup(VirtIONet *n) +{ + VirtioNetRscChain *chain, *rn_chain; + VirtioNetRscSeg *seg, *rn_seg; + + QTAILQ_FOREACH_SAFE(chain, &n->rsc_chains, next, rn_chain) { + QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, rn_seg) { + QTAILQ_REMOVE(&chain->buffers, seg, next); + g_free(seg->buf); + g_free(seg); + } + + timer_del(chain->drain_timer); + timer_free(chain->drain_timer); + QTAILQ_REMOVE(&n->rsc_chains, chain, next); + g_free(chain); + } +} + +static void virtio_net_rsc_cache_buf(VirtioNetRscChain *chain, + NetClientState *nc, + const uint8_t *buf, size_t size) +{ + uint16_t hdr_len; + VirtioNetRscSeg *seg; + + hdr_len = chain->n->guest_hdr_len; + seg = g_malloc(sizeof(VirtioNetRscSeg)); + seg->buf = g_malloc(hdr_len + sizeof(struct eth_header) + + sizeof(struct ip6_header) + VIRTIO_NET_MAX_TCP_PAYLOAD); + memcpy(seg->buf, buf, size); + seg->size = size; + seg->packets = 1; + seg->dup_ack = 0; + seg->is_coalesced = 0; + seg->nc = nc; + + QTAILQ_INSERT_TAIL(&chain->buffers, seg, next); + chain->stat.cache++; + + switch (chain->proto) { + case ETH_P_IP: + virtio_net_rsc_extract_unit4(chain, seg->buf, &seg->unit); + break; + case ETH_P_IPV6: + virtio_net_rsc_extract_unit6(chain, seg->buf, &seg->unit); + break; + default: + g_assert_not_reached(); + } +} + +static int32_t virtio_net_rsc_handle_ack(VirtioNetRscChain *chain, + VirtioNetRscSeg *seg, + const uint8_t *buf, + struct tcp_header *n_tcp, + struct tcp_header *o_tcp) +{ + uint32_t nack, oack; + uint16_t nwin, owin; + + nack = htonl(n_tcp->th_ack); + nwin = htons(n_tcp->th_win); + oack = htonl(o_tcp->th_ack); + owin = htons(o_tcp->th_win); + + if ((nack - oack) >= VIRTIO_NET_MAX_TCP_PAYLOAD) { + chain->stat.ack_out_of_win++; + return RSC_FINAL; + } else if (nack == oack) { + /* duplicated ack or window probe */ + if (nwin == owin) { + /* duplicated ack, add dup ack count due to whql test up to 1 */ + chain->stat.dup_ack++; + return RSC_FINAL; + } else { + /* Coalesce window update */ + o_tcp->th_win = n_tcp->th_win; + chain->stat.win_update++; + return RSC_COALESCE; + } + } else { + /* pure ack, go to 'C', finalize*/ + chain->stat.pure_ack++; + return RSC_FINAL; + } +} + +static int32_t virtio_net_rsc_coalesce_data(VirtioNetRscChain *chain, + VirtioNetRscSeg *seg, + const uint8_t *buf, + VirtioNetRscUnit *n_unit) +{ + void *data; + uint16_t o_ip_len; + uint32_t nseq, oseq; + VirtioNetRscUnit *o_unit; + + o_unit = &seg->unit; + o_ip_len = htons(*o_unit->ip_plen); + nseq = htonl(n_unit->tcp->th_seq); + oseq = htonl(o_unit->tcp->th_seq); + + /* out of order or retransmitted. */ + if ((nseq - oseq) > VIRTIO_NET_MAX_TCP_PAYLOAD) { + chain->stat.data_out_of_win++; + return RSC_FINAL; + } + + data = ((uint8_t *)n_unit->tcp) + n_unit->tcp_hdrlen; + if (nseq == oseq) { + if ((o_unit->payload == 0) && n_unit->payload) { + /* From no payload to payload, normal case, not a dup ack or etc */ + chain->stat.data_after_pure_ack++; + goto coalesce; + } else { + return virtio_net_rsc_handle_ack(chain, seg, buf, + n_unit->tcp, o_unit->tcp); + } + } else if ((nseq - oseq) != o_unit->payload) { + /* Not a consistent packet, out of order */ + chain->stat.data_out_of_order++; + return RSC_FINAL; + } else { +coalesce: + if ((o_ip_len + n_unit->payload) > chain->max_payload) { + chain->stat.over_size++; + return RSC_FINAL; + } + + /* Here comes the right data, the payload length in v4/v6 is different, + so use the field value to update and record the new data len */ + o_unit->payload += n_unit->payload; /* update new data len */ + + /* update field in ip header */ + *o_unit->ip_plen = htons(o_ip_len + n_unit->payload); + + /* Bring 'PUSH' big, the whql test guide says 'PUSH' can be coalesced + for windows guest, while this may change the behavior for linux + guest (only if it uses RSC feature). */ + o_unit->tcp->th_offset_flags = n_unit->tcp->th_offset_flags; + + o_unit->tcp->th_ack = n_unit->tcp->th_ack; + o_unit->tcp->th_win = n_unit->tcp->th_win; + + memmove(seg->buf + seg->size, data, n_unit->payload); + seg->size += n_unit->payload; + seg->packets++; + chain->stat.coalesced++; + return RSC_COALESCE; + } +} + +static int32_t virtio_net_rsc_coalesce4(VirtioNetRscChain *chain, + VirtioNetRscSeg *seg, + const uint8_t *buf, size_t size, + VirtioNetRscUnit *unit) +{ + struct ip_header *ip1, *ip2; + + ip1 = (struct ip_header *)(unit->ip); + ip2 = (struct ip_header *)(seg->unit.ip); + if ((ip1->ip_src ^ ip2->ip_src) || (ip1->ip_dst ^ ip2->ip_dst) + || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport) + || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) { + chain->stat.no_match++; + return RSC_NO_MATCH; + } + + return virtio_net_rsc_coalesce_data(chain, seg, buf, unit); +} + +static int32_t virtio_net_rsc_coalesce6(VirtioNetRscChain *chain, + VirtioNetRscSeg *seg, + const uint8_t *buf, size_t size, + VirtioNetRscUnit *unit) +{ + struct ip6_header *ip1, *ip2; + + ip1 = (struct ip6_header *)(unit->ip); + ip2 = (struct ip6_header *)(seg->unit.ip); + if (memcmp(&ip1->ip6_src, &ip2->ip6_src, sizeof(struct in6_address)) + || memcmp(&ip1->ip6_dst, &ip2->ip6_dst, sizeof(struct in6_address)) + || (unit->tcp->th_sport ^ seg->unit.tcp->th_sport) + || (unit->tcp->th_dport ^ seg->unit.tcp->th_dport)) { + chain->stat.no_match++; + return RSC_NO_MATCH; + } + + return virtio_net_rsc_coalesce_data(chain, seg, buf, unit); +} + +/* Packets with 'SYN' should bypass, other flag should be sent after drain + * to prevent out of order */ +static int virtio_net_rsc_tcp_ctrl_check(VirtioNetRscChain *chain, + struct tcp_header *tcp) +{ + uint16_t tcp_hdr; + uint16_t tcp_flag; + + tcp_flag = htons(tcp->th_offset_flags); + tcp_hdr = (tcp_flag & VIRTIO_NET_TCP_HDR_LENGTH) >> 10; + tcp_flag &= VIRTIO_NET_TCP_FLAG; + tcp_flag = htons(tcp->th_offset_flags) & 0x3F; + if (tcp_flag & TH_SYN) { + chain->stat.tcp_syn++; + return RSC_BYPASS; + } + + if (tcp_flag & (TH_FIN | TH_URG | TH_RST | TH_ECE | TH_CWR)) { + chain->stat.tcp_ctrl_drain++; + return RSC_FINAL; + } + + if (tcp_hdr > sizeof(struct tcp_header)) { + chain->stat.tcp_all_opt++; + return RSC_FINAL; + } + + return RSC_CANDIDATE; +} + +static size_t virtio_net_rsc_do_coalesce(VirtioNetRscChain *chain, + NetClientState *nc, + const uint8_t *buf, size_t size, + VirtioNetRscUnit *unit) +{ + int ret; + VirtioNetRscSeg *seg, *nseg; + + if (QTAILQ_EMPTY(&chain->buffers)) { + chain->stat.empty_cache++; + virtio_net_rsc_cache_buf(chain, nc, buf, size); + timer_mod(chain->drain_timer, + qemu_clock_get_ns(QEMU_CLOCK_HOST) + chain->n->rsc_timeout); + return size; + } + + QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) { + if (chain->proto == ETH_P_IP) { + ret = virtio_net_rsc_coalesce4(chain, seg, buf, size, unit); + } else { + ret = virtio_net_rsc_coalesce6(chain, seg, buf, size, unit); + } + + if (ret == RSC_FINAL) { + if (virtio_net_rsc_drain_seg(chain, seg) == 0) { + /* Send failed */ + chain->stat.final_failed++; + return 0; + } + + /* Send current packet */ + return virtio_net_do_receive(nc, buf, size); + } else if (ret == RSC_NO_MATCH) { + continue; + } else { + /* Coalesced, mark coalesced flag to tell calc cksum for ipv4 */ + seg->is_coalesced = 1; + return size; + } + } + + chain->stat.no_match_cache++; + virtio_net_rsc_cache_buf(chain, nc, buf, size); + return size; +} + +/* Drain a connection data, this is to avoid out of order segments */ +static size_t virtio_net_rsc_drain_flow(VirtioNetRscChain *chain, + NetClientState *nc, + const uint8_t *buf, size_t size, + uint16_t ip_start, uint16_t ip_size, + uint16_t tcp_port) +{ + VirtioNetRscSeg *seg, *nseg; + uint32_t ppair1, ppair2; + + ppair1 = *(uint32_t *)(buf + tcp_port); + QTAILQ_FOREACH_SAFE(seg, &chain->buffers, next, nseg) { + ppair2 = *(uint32_t *)(seg->buf + tcp_port); + if (memcmp(buf + ip_start, seg->buf + ip_start, ip_size) + || (ppair1 != ppair2)) { + continue; + } + if (virtio_net_rsc_drain_seg(chain, seg) == 0) { + chain->stat.drain_failed++; + } + + break; + } + + return virtio_net_do_receive(nc, buf, size); +} + +static int32_t virtio_net_rsc_sanity_check4(VirtioNetRscChain *chain, + struct ip_header *ip, + const uint8_t *buf, size_t size) +{ + uint16_t ip_len; + + /* Not an ipv4 packet */ + if (((ip->ip_ver_len & 0xF0) >> 4) != IP_HEADER_VERSION_4) { + chain->stat.ip_option++; + return RSC_BYPASS; + } + + /* Don't handle packets with ip option */ + if ((ip->ip_ver_len & 0xF) != VIRTIO_NET_IP4_HEADER_LENGTH) { + chain->stat.ip_option++; + return RSC_BYPASS; + } + + if (ip->ip_p != IPPROTO_TCP) { + chain->stat.bypass_not_tcp++; + return RSC_BYPASS; + } + + /* Don't handle packets with ip fragment */ + if (!(htons(ip->ip_off) & IP_DF)) { + chain->stat.ip_frag++; + return RSC_BYPASS; + } + + /* Don't handle packets with ecn flag */ + if (IPTOS_ECN(ip->ip_tos)) { + chain->stat.ip_ecn++; + return RSC_BYPASS; + } + + ip_len = htons(ip->ip_len); + if (ip_len < (sizeof(struct ip_header) + sizeof(struct tcp_header)) + || ip_len > (size - chain->n->guest_hdr_len - + sizeof(struct eth_header))) { + chain->stat.ip_hacked++; + return RSC_BYPASS; + } + + return RSC_CANDIDATE; +} + +static size_t virtio_net_rsc_receive4(VirtioNetRscChain *chain, + NetClientState *nc, + const uint8_t *buf, size_t size) +{ + int32_t ret; + uint16_t hdr_len; + VirtioNetRscUnit unit; + + hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; + + if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header) + + sizeof(struct tcp_header))) { + chain->stat.bypass_not_tcp++; + return virtio_net_do_receive(nc, buf, size); + } + + virtio_net_rsc_extract_unit4(chain, buf, &unit); + if (virtio_net_rsc_sanity_check4(chain, unit.ip, buf, size) + != RSC_CANDIDATE) { + return virtio_net_do_receive(nc, buf, size); + } + + ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp); + if (ret == RSC_BYPASS) { + return virtio_net_do_receive(nc, buf, size); + } else if (ret == RSC_FINAL) { + return virtio_net_rsc_drain_flow(chain, nc, buf, size, + ((hdr_len + sizeof(struct eth_header)) + 12), + VIRTIO_NET_IP4_ADDR_SIZE, + hdr_len + sizeof(struct eth_header) + sizeof(struct ip_header)); + } + + return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit); +} + +static int32_t virtio_net_rsc_sanity_check6(VirtioNetRscChain *chain, + struct ip6_header *ip6, + const uint8_t *buf, size_t size) +{ + uint16_t ip_len; + + if (((ip6->ip6_ctlun.ip6_un1.ip6_un1_flow & 0xF0) >> 4) + != IP_HEADER_VERSION_6) { + return RSC_BYPASS; + } + + /* Both option and protocol is checked in this */ + if (ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt != IPPROTO_TCP) { + chain->stat.bypass_not_tcp++; + return RSC_BYPASS; + } + + ip_len = htons(ip6->ip6_ctlun.ip6_un1.ip6_un1_plen); + if (ip_len < sizeof(struct tcp_header) || + ip_len > (size - chain->n->guest_hdr_len - sizeof(struct eth_header) + - sizeof(struct ip6_header))) { + chain->stat.ip_hacked++; + return RSC_BYPASS; + } + + /* Don't handle packets with ecn flag */ + if (IP6_ECN(ip6->ip6_ctlun.ip6_un3.ip6_un3_ecn)) { + chain->stat.ip_ecn++; + return RSC_BYPASS; + } + + return RSC_CANDIDATE; +} + +static size_t virtio_net_rsc_receive6(void *opq, NetClientState *nc, + const uint8_t *buf, size_t size) +{ + int32_t ret; + uint16_t hdr_len; + VirtioNetRscChain *chain; + VirtioNetRscUnit unit; + + chain = (VirtioNetRscChain *)opq; + hdr_len = ((VirtIONet *)(chain->n))->guest_hdr_len; + + if (size < (hdr_len + sizeof(struct eth_header) + sizeof(struct ip6_header) + + sizeof(tcp_header))) { + return virtio_net_do_receive(nc, buf, size); + } + + virtio_net_rsc_extract_unit6(chain, buf, &unit); + if (RSC_CANDIDATE != virtio_net_rsc_sanity_check6(chain, + unit.ip, buf, size)) { + return virtio_net_do_receive(nc, buf, size); + } + + ret = virtio_net_rsc_tcp_ctrl_check(chain, unit.tcp); + if (ret == RSC_BYPASS) { + return virtio_net_do_receive(nc, buf, size); + } else if (ret == RSC_FINAL) { + return virtio_net_rsc_drain_flow(chain, nc, buf, size, + ((hdr_len + sizeof(struct eth_header)) + 8), + VIRTIO_NET_IP6_ADDR_SIZE, + hdr_len + sizeof(struct eth_header) + + sizeof(struct ip6_header)); + } + + return virtio_net_rsc_do_coalesce(chain, nc, buf, size, &unit); +} + +static VirtioNetRscChain *virtio_net_rsc_lookup_chain(VirtIONet *n, + NetClientState *nc, + uint16_t proto) +{ + VirtioNetRscChain *chain; + + if ((proto != (uint16_t)ETH_P_IP) && (proto != (uint16_t)ETH_P_IPV6)) { + return NULL; + } + + QTAILQ_FOREACH(chain, &n->rsc_chains, next) { + if (chain->proto == proto) { + return chain; + } + } + + chain = g_malloc(sizeof(*chain)); + chain->n = n; + chain->proto = proto; + if (proto == (uint16_t)ETH_P_IP) { + chain->max_payload = VIRTIO_NET_MAX_IP4_PAYLOAD; + chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; + } else { + chain->max_payload = VIRTIO_NET_MAX_IP6_PAYLOAD; + chain->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; + } + chain->drain_timer = timer_new_ns(QEMU_CLOCK_HOST, + virtio_net_rsc_purge, chain); + memset(&chain->stat, 0, sizeof(chain->stat)); + + QTAILQ_INIT(&chain->buffers); + QTAILQ_INSERT_TAIL(&n->rsc_chains, chain, next); + + return chain; +} + +static ssize_t virtio_net_rsc_receive(NetClientState *nc, + const uint8_t *buf, + size_t size) +{ + uint16_t proto; + VirtioNetRscChain *chain; + struct eth_header *eth; + VirtIONet *n; + + n = qemu_get_nic_opaque(nc); + if (size < (n->host_hdr_len + sizeof(struct eth_header))) { + return virtio_net_do_receive(nc, buf, size); + } + + eth = (struct eth_header *)(buf + n->guest_hdr_len); + proto = htons(eth->h_proto); + + chain = virtio_net_rsc_lookup_chain(n, nc, proto); + if (chain) { + chain->stat.received++; + if (proto == (uint16_t)ETH_P_IP && n->rsc4_enabled) { + return virtio_net_rsc_receive4(chain, nc, buf, size); + } else if (proto == (uint16_t)ETH_P_IPV6 && n->rsc6_enabled) { + return virtio_net_rsc_receive6(chain, nc, buf, size); + } + } + return virtio_net_do_receive(nc, buf, size); +} + +static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + VirtIONet *n = qemu_get_nic_opaque(nc); + if ((n->rsc4_enabled || n->rsc6_enabled)) { + return virtio_net_rsc_receive(nc, buf, size); + } else { + return virtio_net_do_receive(nc, buf, size); + } +} + static int32_t virtio_net_flush_tx(VirtIONetQueue *q); static void virtio_net_tx_complete(NetClientState *nc, ssize_t len) @@ -1375,10 +2051,10 @@ static int32_t virtio_net_flush_tx(VirtIONetQueue *q) n->guest_hdr_len, -1); if (out_num == VIRTQUEUE_MAX_SIZE) { goto drop; - } + } out_num += 1; out_sg = sg2; - } + } } /* * If host wants to see the guest header as is, we can @@ -1605,7 +2281,7 @@ static void virtio_net_change_num_queues(VirtIONet *n, int new_max_queues) /* * We always need to remove and add ctrl vq if * old_num_queues != new_num_queues. Remove ctrl_vq first, - * and then we only enter one of the following too loops. + * and then we only enter one of the following two loops. */ virtio_del_queue(vdev, old_num_queues - 1); @@ -1639,6 +2315,7 @@ static int virtio_net_post_load_device(void *opaque, int version_id) VirtIODevice *vdev = VIRTIO_DEVICE(n); int i, link_down; + trace_virtio_net_post_load_device(); virtio_net_set_mrg_rx_bufs(n, n->mergeable_rx_bufs, virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)); @@ -1675,8 +2352,15 @@ static int virtio_net_post_load_device(void *opaque, int version_id) 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)); + qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(), + QEMU_CLOCK_VIRTUAL, + virtio_net_announce_timer, n); + if (n->announce_timer.round) { + timer_mod(n->announce_timer.tm, + qemu_clock_get_ms(n->announce_timer.type)); + } else { + qemu_announce_timer_del(&n->announce_timer); + } } return 0; @@ -1899,6 +2583,7 @@ static NetClientInfo net_virtio_info = { .receive = virtio_net_receive, .link_status_changed = virtio_net_set_link_status, .query_rx_filter = virtio_net_query_rxfilter, + .announce = virtio_net_announce, }; static bool virtio_net_guest_notifier_pending(VirtIODevice *vdev, int idx) @@ -1921,15 +2606,10 @@ static void virtio_net_guest_notifier_mask(VirtIODevice *vdev, int idx, static void virtio_net_set_config_size(VirtIONet *n, uint64_t host_features) { - int i, config_size = 0; virtio_add_feature(&host_features, VIRTIO_NET_F_MAC); - for (i = 0; feature_sizes[i].flags != 0; i++) { - if (host_features & feature_sizes[i].flags) { - config_size = MAX(feature_sizes[i].end, config_size); - } - } - n->config_size = config_size; + n->config_size = virtio_feature_get_config_size(feature_sizes, + host_features); } void virtio_net_set_netclient_name(VirtIONet *n, const char *name, @@ -2020,10 +2700,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) if (n->net_conf.tx && strcmp(n->net_conf.tx, "timer") && strcmp(n->net_conf.tx, "bh")) { - error_report("virtio-net: " - "Unknown option tx=%s, valid options: \"timer\" \"bh\"", - n->net_conf.tx); - error_report("Defaulting to \"bh\""); + warn_report("virtio-net: " + "Unknown option tx=%s, valid options: \"timer\" \"bh\"", + n->net_conf.tx); + error_printf("Defaulting to \"bh\""); } n->net_conf.tx_queue_size = MIN(virtio_net_max_tx_queue_size(n), @@ -2037,8 +2717,10 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) qemu_macaddr_default_if_unset(&n->nic_conf.macaddr); memcpy(&n->mac[0], &n->nic_conf.macaddr, sizeof(n->mac)); n->status = VIRTIO_NET_S_LINK_UP; - n->announce_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - virtio_net_announce_timer, n); + qemu_announce_timer_reset(&n->announce_timer, migrate_announce_params(), + QEMU_CLOCK_VIRTUAL, + virtio_net_announce_timer, n); + n->announce_timer.round = 0; if (n->netclient_type) { /* @@ -2075,6 +2757,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) nc = qemu_get_queue(n->nic); nc->rxfilter_notify_enabled = 1; + QTAILQ_INIT(&n->rsc_chains); n->qdev = dev; } @@ -2100,10 +2783,10 @@ static void virtio_net_device_unrealize(DeviceState *dev, Error **errp) virtio_net_del_queue(n, i); } - timer_del(n->announce_timer); - timer_free(n->announce_timer); + qemu_announce_timer_del(&n->announce_timer); g_free(n->vqs); qemu_del_nic(n->nic); + virtio_net_rsc_cleanup(n); virtio_cleanup(vdev); } @@ -2184,6 +2867,10 @@ static Property virtio_net_properties[] = { DEFINE_PROP_BIT64("ctrl_guest_offloads", VirtIONet, host_features, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, true), DEFINE_PROP_BIT64("mq", VirtIONet, host_features, VIRTIO_NET_F_MQ, false), + DEFINE_PROP_BIT64("guest_rsc_ext", VirtIONet, host_features, + VIRTIO_NET_F_RSC_EXT, false), + DEFINE_PROP_UINT32("rsc_interval", VirtIONet, rsc_timeout, + VIRTIO_NET_RSC_DEFAULT_INTERVAL), DEFINE_NIC_PROPERTIES(VirtIONet, nic_conf), DEFINE_PROP_UINT32("x-txtimer", VirtIONet, net_conf.txtimer, TX_TIMER_INTERVAL), diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 3648630386..4665dc95ad 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/pci/pci.h" -#include "net/net.h" #include "net/tap.h" #include "net/checksum.h" #include "sysemu/sysemu.h" @@ -29,6 +28,7 @@ #include "migration/register.h" #include "vmxnet3.h" +#include "vmxnet3_defs.h" #include "vmxnet_debug.h" #include "vmware_utils.h" #include "net_tx_pkt.h" @@ -131,25 +131,13 @@ typedef struct VMXNET3Class { 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; - uint32_t size; - uint32_t cell_size; - uint32_t next; - uint8_t gen; -} Vmxnet3Ring; - static inline void vmxnet3_ring_init(PCIDevice *d, - Vmxnet3Ring *ring, + Vmxnet3Ring *ring, hwaddr pa, uint32_t size, uint32_t cell_size, @@ -193,13 +181,13 @@ static inline hwaddr vmxnet3_ring_curr_cell_pa(Vmxnet3Ring *ring) } static inline void vmxnet3_ring_read_curr_cell(PCIDevice *d, Vmxnet3Ring *ring, - void *buff) + void *buff) { vmw_shmem_read(d, vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); } static inline void vmxnet3_ring_write_curr_cell(PCIDevice *d, Vmxnet3Ring *ring, - void *buff) + void *buff) { vmw_shmem_write(d, vmxnet3_ring_curr_cell_pa(ring), buff, ring->cell_size); } @@ -245,108 +233,6 @@ vmxnet3_dump_rx_descr(struct Vmxnet3_RxDesc *descr) descr->rsvd, descr->dtype, descr->ext1, descr->btype); } -/* Device state and helper functions */ -#define VMXNET3_RX_RINGS_PER_QUEUE (2) - -typedef struct { - Vmxnet3Ring tx_ring; - Vmxnet3Ring comp_ring; - - uint8_t intr_idx; - hwaddr tx_stats_pa; - struct UPT1_TxStats txq_stats; -} Vmxnet3TxqDescr; - -typedef struct { - Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE]; - Vmxnet3Ring comp_ring; - uint8_t intr_idx; - hwaddr rx_stats_pa; - struct UPT1_RxStats rxq_stats; -} Vmxnet3RxqDescr; - -typedef struct { - bool is_masked; - bool is_pending; - bool is_asserted; -} Vmxnet3IntState; - -typedef struct { - PCIDevice parent_obj; - NICState *nic; - NICConf conf; - MemoryRegion bar0; - MemoryRegion bar1; - MemoryRegion msix_bar; - - Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES]; - Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES]; - - /* Whether MSI-X support was installed successfully */ - bool msix_used; - hwaddr drv_shmem; - hwaddr temp_shared_guest_driver_memory; - - uint8_t txq_num; - - /* This boolean tells whether RX packet being indicated has to */ - /* be split into head and body chunks from different RX rings */ - bool rx_packets_compound; - - bool rx_vlan_stripping; - bool lro_supported; - - uint8_t rxq_num; - - /* Network MTU */ - uint32_t mtu; - - /* Maximum number of fragments for indicated TX packets */ - uint32_t max_tx_frags; - - /* Maximum number of fragments for indicated RX packets */ - uint16_t max_rx_frags; - - /* Index for events interrupt */ - uint8_t event_int_idx; - - /* Whether automatic interrupts masking enabled */ - bool auto_int_masking; - - bool peer_has_vhdr; - - /* TX packets to QEMU interface */ - struct NetTxPkt *tx_pkt; - uint32_t offload_mode; - uint32_t cso_or_gso_size; - uint16_t tci; - bool needs_vlan; - - struct NetRxPkt *rx_pkt; - - bool tx_sop; - bool skip_current_tx_pkt; - - uint32_t device_active; - uint32_t last_command; - - uint32_t link_status_and_speed; - - Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS]; - - uint32_t temp_mac; /* To store the low part first */ - - MACAddr perm_mac; - uint32_t vlan_table[VMXNET3_VFT_SIZE]; - uint32_t rx_mode; - MACAddr *mcast_list; - uint32_t mcast_list_len; - uint32_t mcast_list_buff_size; /* needed for live migration. */ - - /* Compatibility flags for migration */ - uint32_t compat_flags; -} VMXNET3State; - /* Interrupt management */ /* diff --git a/hw/net/vmxnet3_defs.h b/hw/net/vmxnet3_defs.h new file mode 100644 index 0000000000..6c19d29b12 --- /dev/null +++ b/hw/net/vmxnet3_defs.h @@ -0,0 +1,133 @@ +/* + * QEMU VMWARE VMXNET3 paravirtual NIC + * + * Copyright (c) 2012 Ravello Systems LTD (http://ravellosystems.com) + * + * Developed by Daynix Computing LTD (http://www.daynix.com) + * + * Authors: + * Dmitry Fleytman + * Tamir Shomer + * Yan Vugenfirer + * + * This work is licensed under the terms of the GNU GPL, version 2. + * See the COPYING file in the top-level directory. + * + */ + +#include "net/net.h" +#include "hw/net/vmxnet3.h" + +#define TYPE_VMXNET3 "vmxnet3" +#define VMXNET3(obj) OBJECT_CHECK(VMXNET3State, (obj), TYPE_VMXNET3) + +/* Device state and helper functions */ +#define VMXNET3_RX_RINGS_PER_QUEUE (2) + +/* Cyclic ring abstraction */ +typedef struct { + hwaddr pa; + uint32_t size; + uint32_t cell_size; + uint32_t next; + uint8_t gen; +} Vmxnet3Ring; + +typedef struct { + Vmxnet3Ring tx_ring; + Vmxnet3Ring comp_ring; + + uint8_t intr_idx; + hwaddr tx_stats_pa; + struct UPT1_TxStats txq_stats; +} Vmxnet3TxqDescr; + +typedef struct { + Vmxnet3Ring rx_ring[VMXNET3_RX_RINGS_PER_QUEUE]; + Vmxnet3Ring comp_ring; + uint8_t intr_idx; + hwaddr rx_stats_pa; + struct UPT1_RxStats rxq_stats; +} Vmxnet3RxqDescr; + +typedef struct { + bool is_masked; + bool is_pending; + bool is_asserted; +} Vmxnet3IntState; + +typedef struct { + PCIDevice parent_obj; + NICState *nic; + NICConf conf; + MemoryRegion bar0; + MemoryRegion bar1; + MemoryRegion msix_bar; + + Vmxnet3RxqDescr rxq_descr[VMXNET3_DEVICE_MAX_RX_QUEUES]; + Vmxnet3TxqDescr txq_descr[VMXNET3_DEVICE_MAX_TX_QUEUES]; + + /* Whether MSI-X support was installed successfully */ + bool msix_used; + hwaddr drv_shmem; + hwaddr temp_shared_guest_driver_memory; + + uint8_t txq_num; + + /* This boolean tells whether RX packet being indicated has to */ + /* be split into head and body chunks from different RX rings */ + bool rx_packets_compound; + + bool rx_vlan_stripping; + bool lro_supported; + + uint8_t rxq_num; + + /* Network MTU */ + uint32_t mtu; + + /* Maximum number of fragments for indicated TX packets */ + uint32_t max_tx_frags; + + /* Maximum number of fragments for indicated RX packets */ + uint16_t max_rx_frags; + + /* Index for events interrupt */ + uint8_t event_int_idx; + + /* Whether automatic interrupts masking enabled */ + bool auto_int_masking; + + bool peer_has_vhdr; + + /* TX packets to QEMU interface */ + struct NetTxPkt *tx_pkt; + uint32_t offload_mode; + uint32_t cso_or_gso_size; + uint16_t tci; + bool needs_vlan; + + struct NetRxPkt *rx_pkt; + + bool tx_sop; + bool skip_current_tx_pkt; + + uint32_t device_active; + uint32_t last_command; + + uint32_t link_status_and_speed; + + Vmxnet3IntState interrupt_states[VMXNET3_MAX_INTRS]; + + uint32_t temp_mac; /* To store the low part first */ + + MACAddr perm_mac; + uint32_t vlan_table[VMXNET3_VFT_SIZE]; + uint32_t rx_mode; + MACAddr *mcast_list; + uint32_t mcast_list_len; + uint32_t mcast_list_buff_size; /* needed for live migration. */ + + /* Compatibility flags for migration */ + uint32_t compat_flags; +} VMXNET3State; diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index 46a8dbfc90..37cda8e4be 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -28,14 +28,14 @@ #include "net/net.h" #include "net/checksum.h" #include "net/util.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include /* ------------------------------------------------------------- */ struct XenNetDev { - struct XenDevice xendev; /* must be first */ + struct XenLegacyDevice xendev; /* must be first */ char *mac; int tx_work; int tx_ring_ref; @@ -276,7 +276,7 @@ static NetClientInfo net_xen_info = { .receive = net_rx_packet, }; -static int net_init(struct XenDevice *xendev) +static int net_init(struct XenLegacyDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); @@ -308,7 +308,7 @@ static int net_init(struct XenDevice *xendev) return 0; } -static int net_connect(struct XenDevice *xendev) +static int net_connect(struct XenLegacyDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); int rx_copy; @@ -363,7 +363,7 @@ static int net_connect(struct XenDevice *xendev) return 0; } -static void net_disconnect(struct XenDevice *xendev) +static void net_disconnect(struct XenLegacyDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); @@ -379,14 +379,14 @@ static void net_disconnect(struct XenDevice *xendev) } } -static void net_event(struct XenDevice *xendev) +static void net_event(struct XenLegacyDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); net_tx_packets(netdev); qemu_flush_queued_packets(qemu_get_queue(netdev->nic)); } -static int net_free(struct XenDevice *xendev) +static int net_free(struct XenLegacyDevice *xendev) { struct XenNetDev *netdev = container_of(xendev, struct XenNetDev, xendev); diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index fa001563d3..63f5a62ebf 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -374,9 +374,9 @@ static NetClientInfo net_xgmac_enet_info = { .receive = eth_rx, }; -static int xgmac_enet_init(SysBusDevice *sbd) +static void xgmac_enet_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); XgmacState *s = XGMAC(dev); memory_region_init_io(&s->iomem, OBJECT(s), &enet_mem_ops, s, @@ -397,8 +397,6 @@ static int xgmac_enet_init(SysBusDevice *sbd) (s->conf.macaddr.a[2] << 16) | (s->conf.macaddr.a[1] << 8) | s->conf.macaddr.a[0]; - - return 0; } static Property xgmac_properties[] = { @@ -408,10 +406,9 @@ static Property xgmac_properties[] = { static void xgmac_enet_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - sbc->init = xgmac_enet_init; + dc->realize = xgmac_enet_realize; dc->vmsd = &vmstate_xgmac; dc->props = xgmac_properties; } diff --git a/hw/nios2/Kconfig b/hw/nios2/Kconfig new file mode 100644 index 0000000000..ab953e0077 --- /dev/null +++ b/hw/nios2/Kconfig @@ -0,0 +1,8 @@ +config NIOS2_10M50 + bool + select NIOS2 + select SERIAL + select ALTERA_TIMER + +config NIOS2 + bool diff --git a/hw/nios2/Makefile.objs b/hw/nios2/Makefile.objs index 6b5c421760..89a419a9f5 100644 --- a/hw/nios2/Makefile.objs +++ b/hw/nios2/Makefile.objs @@ -1 +1,2 @@ -obj-y = boot.o cpu_pic.o 10m50_devboard.o +obj-y = boot.o cpu_pic.o +obj-$(CONFIG_NIOS2_10M50) += 10m50_devboard.o diff --git a/hw/nios2/boot.c b/hw/nios2/boot.c index 4bb5b601d3..5f0ab2fbb9 100644 --- a/hw/nios2/boot.c +++ b/hw/nios2/boot.c @@ -146,13 +146,14 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, #endif /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, &low, &high, big_endian, EM_ALTERA_NIOS2, 0, 0); base32 = entry; if (base32 == 0xc0000000) { - kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, + translate_kernel_address, NULL, + &entry, NULL, NULL, big_endian, EM_ALTERA_NIOS2, 0, 0); } @@ -161,7 +162,7 @@ void nios2_load_kernel(Nios2CPU *cpu, hwaddr ddr_base, /* If it wasn't an ELF image, try an u-boot image. */ if (kernel_size < 0) { - hwaddr uentry, loadaddr; + hwaddr uentry, loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; kernel_size = load_uimage(kernel_filename, &uentry, &loadaddr, 0, NULL, NULL); diff --git a/hw/nvram/Kconfig b/hw/nvram/Kconfig new file mode 100644 index 0000000000..ebaa749ce9 --- /dev/null +++ b/hw/nvram/Kconfig @@ -0,0 +1,9 @@ +config DS1225Y + bool + +config AT24C + bool + depends on I2C + +config MAC_NVRAM + bool diff --git a/hw/nvram/Makefile.objs b/hw/nvram/Makefile.objs index a912d25391..26f7b4ca35 100644 --- a/hw/nvram/Makefile.objs +++ b/hw/nvram/Makefile.objs @@ -1,7 +1,8 @@ common-obj-$(CONFIG_DS1225Y) += ds1225y.o common-obj-y += eeprom93xx.o -common-obj-$(CONFIG_I2C) += eeprom_at24c.o +common-obj-$(CONFIG_AT24C) += eeprom_at24c.o common-obj-y += fw_cfg.o common-obj-y += chrp_nvram.o common-obj-$(CONFIG_MAC_NVRAM) += mac_nvram.o obj-$(CONFIG_PSERIES) += spapr_nvram.o +obj-$(CONFIG_NRF51_SOC) += nrf51_nvm.o diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index ad7345f288..b6ef463db0 100644 --- a/hw/nvram/ds1225y.c +++ b/hw/nvram/ds1225y.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "trace.h" +#include "qemu/error-report.h" typedef struct { MemoryRegion iomem; @@ -113,7 +114,7 @@ typedef struct { NvRamState nvram; } SysBusNvRamState; -static int nvram_sysbus_initfn(SysBusDevice *dev) +static void nvram_sysbus_realize(DeviceState *dev, Error **errp) { SysBusNvRamState *sys = DS1225Y(dev); NvRamState *s = &sys->nvram; @@ -123,20 +124,18 @@ static int nvram_sysbus_initfn(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &nvram_ops, s, "nvram", s->chip_size); - sysbus_init_mmio(dev, &s->iomem); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); /* Read current file */ file = s->filename ? fopen(s->filename, "rb") : NULL; if (file) { /* Read nvram contents */ if (fread(s->contents, s->chip_size, 1, file) != 1) { - printf("nvram_sysbus_initfn: short read\n"); + error_report("nvram_sysbus_realize: short read"); } fclose(file); } nvram_post_load(s, 0); - - return 0; } static Property nvram_sysbus_properties[] = { @@ -148,9 +147,8 @@ static Property nvram_sysbus_properties[] = { static void nvram_sysbus_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = nvram_sysbus_initfn; + dc->realize = nvram_sysbus_realize; dc->vmsd = &vmstate_nvram; dc->props = nvram_sysbus_properties; } diff --git a/hw/nvram/eeprom93xx.c b/hw/nvram/eeprom93xx.c index 2fd0e3c29f..2db3d7cce6 100644 --- a/hw/nvram/eeprom93xx.c +++ b/hw/nvram/eeprom93xx.c @@ -95,15 +95,15 @@ struct _eeprom_t { */ static int get_uint16_from_uint8(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint16_t *v = pv; *v = qemu_get_ubyte(f); return 0; } -static int put_unused(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_unused(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { fprintf(stderr, "uint16_from_uint8 is used only for backwards compatibility.\n"); fprintf(stderr, "Never should be used to write a new state.\n"); diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index 27cd01e615..d1456dafbd 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -74,10 +74,10 @@ int at24c_eeprom_event(I2CSlave *s, enum i2c_event event) } static -int at24c_eeprom_recv(I2CSlave *s) +uint8_t at24c_eeprom_recv(I2CSlave *s) { EEPROMState *ee = AT24C_EE(s); - int ret; + uint8_t ret; ret = ee->mem[ee->cur]; diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index b23e7f64a8..5c3a46ce6f 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -27,7 +27,6 @@ #include "sysemu/sysemu.h" #include "sysemu/dma.h" #include "hw/boards.h" -#include "hw/isa/isa.h" #include "hw/nvram/fw_cfg.h" #include "hw/sysbus.h" #include "trace.h" @@ -68,15 +67,14 @@ static char *read_splashfile(char *filename, gsize *file_sizep, int *file_typep) { GError *err = NULL; - gboolean res; gchar *content; int file_type; unsigned int filehead; int bmp_bpp; - res = g_file_get_contents(filename, &content, file_sizep, &err); - if (res == FALSE) { - error_report("failed to read splash file '%s'", filename); + if (!g_file_get_contents(filename, &content, file_sizep, &err)) { + error_report("failed to read splash file '%s': %s", + filename, err->message); g_error_free(err); return NULL; } @@ -87,7 +85,7 @@ static char *read_splashfile(char *filename, gsize *file_sizep, } /* check magic ID */ - filehead = ((content[0] & 0xff) + (content[1] << 8)) & 0xffff; + filehead = lduw_le_p(content); if (filehead == 0xd8ff) { file_type = JPG_FILE; } else if (filehead == 0x4d42) { @@ -98,7 +96,7 @@ static char *read_splashfile(char *filename, gsize *file_sizep, /* check BMP bpp */ if (file_type == BMP_FILE) { - bmp_bpp = (content[28] + (content[29] << 8)) & 0xffff; + bmp_bpp = lduw_le_p(&content[28]); if (bmp_bpp != 24) { goto error; } @@ -118,47 +116,40 @@ error: static void fw_cfg_bootsplash(FWCfgState *s) { - int boot_splash_time = -1; const char *boot_splash_filename = NULL; - char *p; + const char *boot_splash_time = NULL; char *filename, *file_data; gsize file_size; int file_type; - const char *temp; /* get user configuration */ QemuOptsList *plist = qemu_find_opts("boot-opts"); QemuOpts *opts = QTAILQ_FIRST(&plist->head); - if (opts != NULL) { - temp = qemu_opt_get(opts, "splash"); - if (temp != NULL) { - boot_splash_filename = temp; - } - temp = qemu_opt_get(opts, "splash-time"); - if (temp != NULL) { - p = (char *)temp; - boot_splash_time = strtol(p, &p, 10); - } - } + boot_splash_filename = qemu_opt_get(opts, "splash"); + boot_splash_time = qemu_opt_get(opts, "splash-time"); /* insert splash time if user configurated */ - if (boot_splash_time >= 0) { + if (boot_splash_time) { + int64_t bst_val = qemu_opt_get_number(opts, "splash-time", -1); + uint16_t bst_le16; + /* validate the input */ - if (boot_splash_time > 0xffff) { - error_report("splash time is big than 65535, force it to 65535."); - boot_splash_time = 0xffff; + if (bst_val < 0 || bst_val > 0xffff) { + error_report("splash-time is invalid," + "it should be a value between 0 and 65535"); + exit(1); } /* use little endian format */ - qemu_extra_params_fw[0] = (uint8_t)(boot_splash_time & 0xff); - qemu_extra_params_fw[1] = (uint8_t)((boot_splash_time >> 8) & 0xff); - fw_cfg_add_file(s, "etc/boot-menu-wait", qemu_extra_params_fw, 2); + bst_le16 = cpu_to_le16(bst_val); + fw_cfg_add_file(s, "etc/boot-menu-wait", + g_memdup(&bst_le16, sizeof bst_le16), sizeof bst_le16); } /* insert splash file if user configurated */ - if (boot_splash_filename != NULL) { + if (boot_splash_filename) { filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, boot_splash_filename); if (filename == NULL) { - error_report("failed to find file '%s'.", boot_splash_filename); + error_report("failed to find file '%s'", boot_splash_filename); return; } @@ -170,15 +161,14 @@ static void fw_cfg_bootsplash(FWCfgState *s) } g_free(boot_splash_filedata); boot_splash_filedata = (uint8_t *)file_data; - boot_splash_filedata_size = file_size; /* insert data */ if (file_type == JPG_FILE) { fw_cfg_add_file(s, "bootsplash.jpg", - boot_splash_filedata, boot_splash_filedata_size); + boot_splash_filedata, file_size); } else { fw_cfg_add_file(s, "bootsplash.bmp", - boot_splash_filedata, boot_splash_filedata_size); + boot_splash_filedata, file_size); } g_free(filename); } @@ -186,26 +176,25 @@ static void fw_cfg_bootsplash(FWCfgState *s) static void fw_cfg_reboot(FWCfgState *s) { - int reboot_timeout = -1; - char *p; - const char *temp; + const char *reboot_timeout = NULL; + int64_t rt_val = -1; /* get user configuration */ QemuOptsList *plist = qemu_find_opts("boot-opts"); QemuOpts *opts = QTAILQ_FIRST(&plist->head); - if (opts != NULL) { - temp = qemu_opt_get(opts, "reboot-timeout"); - if (temp != NULL) { - p = (char *)temp; - reboot_timeout = strtol(p, &p, 10); + reboot_timeout = qemu_opt_get(opts, "reboot-timeout"); + + if (reboot_timeout) { + rt_val = qemu_opt_get_number(opts, "reboot-timeout", -1); + /* validate the input */ + if (rt_val < 0 || rt_val > 0xffff) { + error_report("reboot timeout is invalid," + "it should be a value between 0 and 65535"); + exit(1); } } - /* validate the input */ - if (reboot_timeout > 0xffff) { - error_report("reboot timeout is larger than 65535, force it to 65535."); - reboot_timeout = 0xffff; - } - fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&reboot_timeout, 4), 4); + + fw_cfg_add_file(s, "etc/boot-fail-wait", g_memdup(&rt_val, 4), 4); } static void fw_cfg_write(FWCfgState *s, uint8_t value) @@ -434,6 +423,11 @@ static bool fw_cfg_data_mem_valid(void *opaque, hwaddr addr, return addr == 0; } +static uint64_t fw_cfg_ctl_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + static void fw_cfg_ctl_mem_write(void *opaque, hwaddr addr, uint64_t value, unsigned size) { @@ -468,6 +462,7 @@ static bool fw_cfg_comb_valid(void *opaque, hwaddr addr, } static const MemoryRegionOps fw_cfg_ctl_mem_ops = { + .read = fw_cfg_ctl_mem_read, .write = fw_cfg_ctl_mem_write, .endianness = DEVICE_BIG_ENDIAN, .valid.accepts = fw_cfg_ctl_mem_valid, @@ -514,15 +509,15 @@ static void fw_cfg_reset(DeviceState *d) */ static int get_uint32_as_uint16(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint32_t *v = pv; *v = qemu_get_be16(f); return 0; } -static int put_unused(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_unused(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { fprintf(stderr, "uint32_as_uint16 is only used for backward compatibility.\n"); fprintf(stderr, "This functions shouldn't be called.\n"); @@ -861,7 +856,7 @@ static void fw_cfg_machine_reset(void *opaque) void *ptr; size_t len; FWCfgState *s = opaque; - char *bootindex = get_boot_devices_list(&len, false); + char *bootindex = get_boot_devices_list(&len); ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)bootindex, len); g_free(ptr); @@ -1109,12 +1104,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->ctl_iomem); if (s->data_width > data_ops->valid.max_access_size) { - /* memberwise copy because the "old_mmio" member is const */ - s->wide_data_ops.read = data_ops->read; - s->wide_data_ops.write = data_ops->write; - s->wide_data_ops.endianness = data_ops->endianness; - s->wide_data_ops.valid = data_ops->valid; - s->wide_data_ops.impl = data_ops->impl; + s->wide_data_ops = *data_ops; s->wide_data_ops.valid.max_access_size = s->data_width; s->wide_data_ops.impl.max_access_size = s->data_width; diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c new file mode 100644 index 0000000000..7d94cef1db --- /dev/null +++ b/hw/nvram/nrf51_nvm.c @@ -0,0 +1,388 @@ +/* + * Nordic Semiconductor nRF51 non-volatile memory + * + * It provides an interface to erase regions in flash memory. + * Furthermore it provides the user and factory information registers. + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * + * See nRF51 reference manual and product sheet sections: + * + Non-Volatile Memory Controller (NVMC) + * + Factory Information Configuration Registers (FICR) + * + User Information Configuration Registers (UICR) + * + * Copyright 2018 Steffen Görtz + * + * 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/log.h" +#include "exec/address-spaces.h" +#include "hw/arm/nrf51.h" +#include "hw/nvram/nrf51_nvm.h" + +/* + * FICR Registers Assignments + * CODEPAGESIZE 0x010 + * CODESIZE 0x014 + * CLENR0 0x028 + * PPFC 0x02C + * NUMRAMBLOCK 0x034 + * SIZERAMBLOCKS 0x038 + * SIZERAMBLOCK[0] 0x038 + * SIZERAMBLOCK[1] 0x03C + * SIZERAMBLOCK[2] 0x040 + * SIZERAMBLOCK[3] 0x044 + * CONFIGID 0x05C + * DEVICEID[0] 0x060 + * DEVICEID[1] 0x064 + * ER[0] 0x080 + * ER[1] 0x084 + * ER[2] 0x088 + * ER[3] 0x08C + * IR[0] 0x090 + * IR[1] 0x094 + * IR[2] 0x098 + * IR[3] 0x09C + * DEVICEADDRTYPE 0x0A0 + * DEVICEADDR[0] 0x0A4 + * DEVICEADDR[1] 0x0A8 + * OVERRIDEEN 0x0AC + * NRF_1MBIT[0] 0x0B0 + * NRF_1MBIT[1] 0x0B4 + * NRF_1MBIT[2] 0x0B8 + * NRF_1MBIT[3] 0x0BC + * NRF_1MBIT[4] 0x0C0 + * BLE_1MBIT[0] 0x0EC + * BLE_1MBIT[1] 0x0F0 + * BLE_1MBIT[2] 0x0F4 + * BLE_1MBIT[3] 0x0F8 + * BLE_1MBIT[4] 0x0FC + */ +static const uint32_t ficr_content[64] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000400, + 0x00000100, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000002, 0x00002000, + 0x00002000, 0x00002000, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000003, + 0x12345678, 0x9ABCDEF1, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF +}; + +static uint64_t ficr_read(void *opaque, hwaddr offset, unsigned int size) +{ + assert(offset < sizeof(ficr_content)); + return ficr_content[offset / 4]; +} + +static void ficr_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + /* Intentionally do nothing */ +} + +static const MemoryRegionOps ficr_ops = { + .read = ficr_read, + .write = ficr_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN +}; + +/* + * UICR Registers Assignments + * CLENR0 0x000 + * RBPCONF 0x004 + * XTALFREQ 0x008 + * FWID 0x010 + * BOOTLOADERADDR 0x014 + * NRFFW[0] 0x014 + * NRFFW[1] 0x018 + * NRFFW[2] 0x01C + * NRFFW[3] 0x020 + * NRFFW[4] 0x024 + * NRFFW[5] 0x028 + * NRFFW[6] 0x02C + * NRFFW[7] 0x030 + * NRFFW[8] 0x034 + * NRFFW[9] 0x038 + * NRFFW[10] 0x03C + * NRFFW[11] 0x040 + * NRFFW[12] 0x044 + * NRFFW[13] 0x048 + * NRFFW[14] 0x04C + * NRFHW[0] 0x050 + * NRFHW[1] 0x054 + * NRFHW[2] 0x058 + * NRFHW[3] 0x05C + * NRFHW[4] 0x060 + * NRFHW[5] 0x064 + * NRFHW[6] 0x068 + * NRFHW[7] 0x06C + * NRFHW[8] 0x070 + * NRFHW[9] 0x074 + * NRFHW[10] 0x078 + * NRFHW[11] 0x07C + * CUSTOMER[0] 0x080 + * CUSTOMER[1] 0x084 + * CUSTOMER[2] 0x088 + * CUSTOMER[3] 0x08C + * CUSTOMER[4] 0x090 + * CUSTOMER[5] 0x094 + * CUSTOMER[6] 0x098 + * CUSTOMER[7] 0x09C + * CUSTOMER[8] 0x0A0 + * CUSTOMER[9] 0x0A4 + * CUSTOMER[10] 0x0A8 + * CUSTOMER[11] 0x0AC + * CUSTOMER[12] 0x0B0 + * CUSTOMER[13] 0x0B4 + * CUSTOMER[14] 0x0B8 + * CUSTOMER[15] 0x0BC + * CUSTOMER[16] 0x0C0 + * CUSTOMER[17] 0x0C4 + * CUSTOMER[18] 0x0C8 + * CUSTOMER[19] 0x0CC + * CUSTOMER[20] 0x0D0 + * CUSTOMER[21] 0x0D4 + * CUSTOMER[22] 0x0D8 + * CUSTOMER[23] 0x0DC + * CUSTOMER[24] 0x0E0 + * CUSTOMER[25] 0x0E4 + * CUSTOMER[26] 0x0E8 + * CUSTOMER[27] 0x0EC + * CUSTOMER[28] 0x0F0 + * CUSTOMER[29] 0x0F4 + * CUSTOMER[30] 0x0F8 + * CUSTOMER[31] 0x0FC + */ + +static uint64_t uicr_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + assert(offset < sizeof(s->uicr_content)); + return s->uicr_content[offset / 4]; +} + +static void uicr_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + assert(offset < sizeof(s->uicr_content)); + s->uicr_content[offset / 4] = value; +} + +static const MemoryRegionOps uicr_ops = { + .read = uicr_read, + .write = uicr_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN +}; + + +static uint64_t io_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + uint64_t r = 0; + + switch (offset) { + case NRF51_NVMC_READY: + r = NRF51_NVMC_READY_READY; + break; + case NRF51_NVMC_CONFIG: + r = s->config; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", __func__, offset); + break; + } + + return r; +} + +static void io_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + switch (offset) { + case NRF51_NVMC_CONFIG: + s->config = value & NRF51_NVMC_CONFIG_MASK; + break; + case NRF51_NVMC_ERASEPCR0: + case NRF51_NVMC_ERASEPCR1: + if (s->config & NRF51_NVMC_CONFIG_EEN) { + /* Mask in-page sub address */ + value &= ~(NRF51_PAGE_SIZE - 1); + if (value <= (s->flash_size - NRF51_PAGE_SIZE)) { + memset(s->storage + value, 0xFF, NRF51_PAGE_SIZE); + memory_region_flush_rom_device(&s->flash, value, + NRF51_PAGE_SIZE); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Flash erase at 0x%" HWADDR_PRIx" while flash not erasable.\n", + __func__, offset); + } + break; + case NRF51_NVMC_ERASEALL: + if (value == NRF51_NVMC_ERASE) { + if (s->config & NRF51_NVMC_CONFIG_EEN) { + memset(s->storage, 0xFF, s->flash_size); + memory_region_flush_rom_device(&s->flash, 0, s->flash_size); + memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: Flash not erasable.\n", + __func__); + } + } + break; + case NRF51_NVMC_ERASEUICR: + if (value == NRF51_NVMC_ERASE) { + memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", __func__, offset); + } +} + +static const MemoryRegionOps io_ops = { + .read = io_read, + .write = io_write, + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + + +static void flash_write(void *opaque, hwaddr offset, uint64_t value, + unsigned int size) +{ + NRF51NVMState *s = NRF51_NVM(opaque); + + if (s->config & NRF51_NVMC_CONFIG_WEN) { + uint32_t oldval; + + assert(offset + size <= s->flash_size); + + /* NOR Flash only allows bits to be flipped from 1's to 0's on write */ + oldval = ldl_le_p(s->storage + offset); + oldval &= value; + stl_le_p(s->storage + offset, oldval); + + memory_region_flush_rom_device(&s->flash, offset, size); + } else { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Flash write 0x%" HWADDR_PRIx" while flash not writable.\n", + __func__, offset); + } +} + + + +static const MemoryRegionOps flash_ops = { + .write = flash_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void nrf51_nvm_init(Object *obj) +{ + NRF51NVMState *s = NRF51_NVM(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->mmio, obj, &io_ops, s, "nrf51_soc.nvmc", + NRF51_NVMC_SIZE); + sysbus_init_mmio(sbd, &s->mmio); + + memory_region_init_io(&s->ficr, obj, &ficr_ops, s, "nrf51_soc.ficr", + sizeof(ficr_content)); + sysbus_init_mmio(sbd, &s->ficr); + + memory_region_init_io(&s->uicr, obj, &uicr_ops, s, "nrf51_soc.uicr", + sizeof(s->uicr_content)); + sysbus_init_mmio(sbd, &s->uicr); +} + +static void nrf51_nvm_realize(DeviceState *dev, Error **errp) +{ + NRF51NVMState *s = NRF51_NVM(dev); + Error *err = NULL; + + memory_region_init_rom_device(&s->flash, OBJECT(dev), &flash_ops, s, + "nrf51_soc.flash", s->flash_size, &err); + if (err) { + error_propagate(errp, err); + return; + } + + s->storage = memory_region_get_ram_ptr(&s->flash); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->flash); +} + +static void nrf51_nvm_reset(DeviceState *dev) +{ + NRF51NVMState *s = NRF51_NVM(dev); + + s->config = 0x00; + memset(s->uicr_content, 0xFF, sizeof(s->uicr_content)); +} + +static Property nrf51_nvm_properties[] = { + DEFINE_PROP_UINT32("flash-size", NRF51NVMState, flash_size, 0x40000), + DEFINE_PROP_END_OF_LIST(), +}; + +static const VMStateDescription vmstate_nvm = { + .name = "nrf51_soc.nvm", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32_ARRAY(uicr_content, NRF51NVMState, + NRF51_UICR_FIXTURE_SIZE), + VMSTATE_UINT32(config, NRF51NVMState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_nvm_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = nrf51_nvm_properties; + dc->vmsd = &vmstate_nvm; + dc->realize = nrf51_nvm_realize; + dc->reset = nrf51_nvm_reset; +} + +static const TypeInfo nrf51_nvm_info = { + .name = TYPE_NRF51_NVM, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51NVMState), + .instance_init = nrf51_nvm_init, + .class_init = nrf51_nvm_class_init +}; + +static void nrf51_nvm_register_types(void) +{ + type_register_static(&nrf51_nvm_info); +} + +type_init(nrf51_nvm_register_types) diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index bed1557d83..c98c7576e6 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -36,28 +36,28 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" -typedef struct sPAPRNVRAM { - VIOsPAPRDevice sdev; +typedef struct SpaprNvram { + SpaprVioDevice sdev; uint32_t size; uint8_t *buf; BlockBackend *blk; VMChangeStateEntry *vmstate; -} sPAPRNVRAM; +} SpaprNvram; #define TYPE_VIO_SPAPR_NVRAM "spapr-nvram" #define VIO_SPAPR_NVRAM(obj) \ - OBJECT_CHECK(sPAPRNVRAM, (obj), TYPE_VIO_SPAPR_NVRAM) + OBJECT_CHECK(SpaprNvram, (obj), TYPE_VIO_SPAPR_NVRAM) #define MIN_NVRAM_SIZE (8 * KiB) #define DEFAULT_NVRAM_SIZE (64 * KiB) #define MAX_NVRAM_SIZE (1 * MiB) -static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_nvram_fetch(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRNVRAM *nvram = spapr->nvram; + SpaprNvram *nvram = spapr->nvram; hwaddr offset, buffer, len; void *membuf; @@ -93,12 +93,12 @@ static void rtas_nvram_fetch(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 1, len); } -static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_nvram_store(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRNVRAM *nvram = spapr->nvram; + SpaprNvram *nvram = spapr->nvram; hwaddr offset, buffer, len; int alen; void *membuf; @@ -139,9 +139,9 @@ static void rtas_nvram_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 1, (alen < 0) ? 0 : alen); } -static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) +static void spapr_nvram_realize(SpaprVioDevice *dev, Error **errp) { - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev); + SpaprNvram *nvram = VIO_SPAPR_NVRAM(dev); int ret; if (nvram->blk) { @@ -193,16 +193,16 @@ static void spapr_nvram_realize(VIOsPAPRDevice *dev, Error **errp) spapr_rtas_register(RTAS_NVRAM_STORE, "nvram-store", rtas_nvram_store); } -static int spapr_nvram_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) +static int spapr_nvram_devnode(SpaprVioDevice *dev, void *fdt, int node_off) { - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(dev); + SpaprNvram *nvram = VIO_SPAPR_NVRAM(dev); return fdt_setprop_cell(fdt, node_off, "#bytes", nvram->size); } static int spapr_nvram_pre_load(void *opaque) { - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); + SpaprNvram *nvram = VIO_SPAPR_NVRAM(opaque); g_free(nvram->buf); nvram->buf = NULL; @@ -213,7 +213,7 @@ static int spapr_nvram_pre_load(void *opaque) static void postload_update_cb(void *opaque, int running, RunState state) { - sPAPRNVRAM *nvram = opaque; + SpaprNvram *nvram = opaque; /* This is called after bdrv_invalidate_cache_all. */ @@ -225,7 +225,7 @@ static void postload_update_cb(void *opaque, int running, RunState state) static int spapr_nvram_post_load(void *opaque, int version_id) { - sPAPRNVRAM *nvram = VIO_SPAPR_NVRAM(opaque); + SpaprNvram *nvram = VIO_SPAPR_NVRAM(opaque); if (nvram->blk) { nvram->vmstate = qemu_add_vm_change_state_handler(postload_update_cb, @@ -242,22 +242,22 @@ static const VMStateDescription vmstate_spapr_nvram = { .pre_load = spapr_nvram_pre_load, .post_load = spapr_nvram_post_load, .fields = (VMStateField[]) { - VMSTATE_UINT32(size, sPAPRNVRAM), - VMSTATE_VBUFFER_ALLOC_UINT32(buf, sPAPRNVRAM, 1, NULL, size), + VMSTATE_UINT32(size, SpaprNvram), + VMSTATE_VBUFFER_ALLOC_UINT32(buf, SpaprNvram, 1, NULL, size), VMSTATE_END_OF_LIST() }, }; static Property spapr_nvram_properties[] = { - DEFINE_SPAPR_PROPERTIES(sPAPRNVRAM, sdev), - DEFINE_PROP_DRIVE("drive", sPAPRNVRAM, blk), + DEFINE_SPAPR_PROPERTIES(SpaprNvram, sdev), + DEFINE_PROP_DRIVE("drive", SpaprNvram, blk), DEFINE_PROP_END_OF_LIST(), }; static void spapr_nvram_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); k->realize = spapr_nvram_realize; k->devnode = spapr_nvram_devnode; @@ -274,7 +274,7 @@ static void spapr_nvram_class_init(ObjectClass *klass, void *data) static const TypeInfo spapr_nvram_type_info = { .name = TYPE_VIO_SPAPR_NVRAM, .parent = TYPE_VIO_SPAPR_DEVICE, - .instance_size = sizeof(sPAPRNVRAM), + .instance_size = sizeof(SpaprNvram), .class_init = spapr_nvram_class_init, }; diff --git a/hw/nvram/trace-events b/hw/nvram/trace-events index 6b55ba7a09..e191991e2a 100644 --- a/hw/nvram/trace-events +++ b/hw/nvram/trace-events @@ -1,10 +1,10 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/nvram/ds1225y.c +# ds1225y.c nvram_read(uint32_t addr, uint32_t ret) "read addr %d: 0x%02x" nvram_write(uint32_t addr, uint32_t old, uint32_t val) "write addr %d: 0x%02x -> 0x%02x" -# hw/nvram/fw_cfg.c +# fw_cfg.c fw_cfg_select(void *s, uint16_t key, int ret) "%p key %d = %d" fw_cfg_read(void *s, uint64_t ret) "%p = 0x%"PRIx64 fw_cfg_add_file(void *s, int index, char *name, size_t len) "%p #%d: %s (%zd bytes)" diff --git a/hw/openrisc/Kconfig b/hw/openrisc/Kconfig new file mode 100644 index 0000000000..6c1e86884e --- /dev/null +++ b/hw/openrisc/Kconfig @@ -0,0 +1,5 @@ +config OR1K_SIM + bool + select SERIAL + select OPENCORES_ETH + select OMPIC diff --git a/hw/openrisc/Makefile.objs b/hw/openrisc/Makefile.objs index 61246b149b..aa04de7f5a 100644 --- a/hw/openrisc/Makefile.objs +++ b/hw/openrisc/Makefile.objs @@ -1,2 +1,2 @@ obj-y = pic_cpu.o cputimer.o -obj-y += openrisc_sim.o +obj-$(CONFIG_OR1K_SIM) += openrisc_sim.o diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index a495a84a41..7d3b734d24 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -96,7 +96,7 @@ static void openrisc_load_kernel(ram_addr_t ram_size, hwaddr entry; if (kernel_filename && !qtest_enabled()) { - kernel_size = load_elf(kernel_filename, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, NULL, NULL, 1, EM_OPENRISC, 1, 0); entry = elf_entry; diff --git a/hw/pci-bridge/Kconfig b/hw/pci-bridge/Kconfig new file mode 100644 index 0000000000..a51ec716f5 --- /dev/null +++ b/hw/pci-bridge/Kconfig @@ -0,0 +1,29 @@ +config PCIE_PORT + bool + default y if PCI_DEVICES + depends on PCI_EXPRESS && MSI_NONBROKEN + +config PXB + bool + default y if Q35 + +config XIO3130 + bool + default y if PCI_DEVICES + depends on PCI_EXPRESS && MSI_NONBROKEN + +config IOH3420 + bool + default y if PCI_DEVICES + depends on PCI_EXPRESS && MSI_NONBROKEN + +config I82801B11 + bool + default y if PCI_DEVICES + depends on PCI_EXPRESS + +config DEC_PCI + bool + +config SIMBA + bool diff --git a/hw/pci-bridge/dec.c b/hw/pci-bridge/dec.c index 84492d5e5f..8484bfd434 100644 --- a/hw/pci-bridge/dec.c +++ b/hw/pci-bridge/dec.c @@ -98,9 +98,10 @@ PCIBus *pci_dec_21154_init(PCIBus *parent_bus, int devfn) return pci_bridge_get_sec_bus(br); } -static int pci_dec_21154_device_init(SysBusDevice *dev) +static void pci_dec_21154_device_realize(DeviceState *dev, Error **errp) { PCIHostState *phb; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); phb = PCI_HOST_BRIDGE(dev); @@ -108,9 +109,8 @@ static int pci_dec_21154_device_init(SysBusDevice *dev) dev, "pci-conf-idx", 0x1000); memory_region_init_io(&phb->data_mem, OBJECT(dev), &pci_host_data_le_ops, dev, "pci-data-idx", 0x1000); - sysbus_init_mmio(dev, &phb->conf_mem); - sysbus_init_mmio(dev, &phb->data_mem); - return 0; + sysbus_init_mmio(sbd, &phb->conf_mem); + sysbus_init_mmio(sbd, &phb->data_mem); } static void dec_21154_pci_host_realize(PCIDevice *d, Error **errp) @@ -150,9 +150,9 @@ static const TypeInfo dec_21154_pci_host_info = { static void pci_dec_21154_device_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = pci_dec_21154_device_init; + dc->realize = pci_dec_21154_device_realize; } static const TypeInfo pci_dec_21154_device_info = { diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index d117e20325..26bda73eae 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -20,6 +20,9 @@ OBJECT_CHECK(GenPCIERootPort, (obj), TYPE_GEN_PCIE_ROOT_PORT) #define GEN_PCIE_ROOT_PORT_AER_OFFSET 0x100 +#define GEN_PCIE_ROOT_PORT_ACS_OFFSET \ + (GEN_PCIE_ROOT_PORT_AER_OFFSET + PCI_ERR_SIZEOF) + #define GEN_PCIE_ROOT_PORT_MSIX_NR_VECTOR 1 typedef struct GenPCIERootPort { @@ -29,12 +32,8 @@ typedef struct GenPCIERootPort { bool migrate_msix; - /* additional resources to reserve on firmware init */ - uint32_t bus_reserve; - uint64_t io_reserve; - uint64_t mem_reserve; - uint64_t pref32_reserve; - uint64_t pref64_reserve; + /* additional resources to reserve */ + PCIResReserve res_reserve; } GenPCIERootPort; static uint8_t gen_rp_aer_vector(const PCIDevice *d) @@ -82,16 +81,15 @@ static void gen_rp_realize(DeviceState *dev, Error **errp) return; } - int rc = pci_bridge_qemu_reserve_cap_init(d, 0, grp->bus_reserve, - grp->io_reserve, grp->mem_reserve, grp->pref32_reserve, - grp->pref64_reserve, errp); + int rc = pci_bridge_qemu_reserve_cap_init(d, 0, + grp->res_reserve, errp); if (rc < 0) { rpc->parent_class.exit(d); return; } - if (!grp->io_reserve) { + if (!grp->res_reserve.io) { pci_word_test_and_clear_mask(d->wmask + PCI_COMMAND, PCI_COMMAND_IO); d->wmask[PCI_IO_BASE] = 0; @@ -117,12 +115,22 @@ static const VMStateDescription vmstate_rp_dev = { }; static Property gen_rp_props[] = { - DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, migrate_msix, true), - DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, bus_reserve, -1), - DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort, io_reserve, -1), - DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, mem_reserve, -1), - DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, pref32_reserve, -1), - DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, pref64_reserve, -1), + DEFINE_PROP_BOOL("x-migrate-msix", GenPCIERootPort, + migrate_msix, true), + DEFINE_PROP_UINT32("bus-reserve", GenPCIERootPort, + res_reserve.bus, -1), + DEFINE_PROP_SIZE("io-reserve", GenPCIERootPort, + res_reserve.io, -1), + DEFINE_PROP_SIZE("mem-reserve", GenPCIERootPort, + res_reserve.mem_non_pref, -1), + DEFINE_PROP_SIZE("pref32-reserve", GenPCIERootPort, + res_reserve.mem_pref_32, -1), + DEFINE_PROP_SIZE("pref64-reserve", GenPCIERootPort, + res_reserve.mem_pref_64, -1), + DEFINE_PROP_PCIE_LINK_SPEED("x-speed", PCIESlot, + speed, PCIE_LINK_SPEED_16), + DEFINE_PROP_PCIE_LINK_WIDTH("x-width", PCIESlot, + width, PCIE_LINK_WIDTH_32), DEFINE_PROP_END_OF_LIST() }; @@ -144,6 +152,7 @@ static void gen_rp_dev_class_init(ObjectClass *klass, void *data) rpc->interrupts_init = gen_rp_interrupts_init; rpc->interrupts_uninit = gen_rp_interrupts_uninit; rpc->aer_offset = GEN_PCIE_ROOT_PORT_AER_OFFSET; + rpc->acs_offset = GEN_PCIE_ROOT_PORT_ACS_OFFSET; } static const TypeInfo gen_rp_dev_info = { diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index a451d74ee6..81f2de6f07 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -24,7 +24,7 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" #include "hw/pci/pcie.h" -#include "ioh3420.h" +#include "hw/pci/pcie_port.h" #define PCI_DEVICE_ID_IOH_EPORT 0x3420 /* D0:F0 express mode */ #define PCI_DEVICE_ID_IOH_REV 0x2 diff --git a/hw/pci-bridge/ioh3420.h b/hw/pci-bridge/ioh3420.h deleted file mode 100644 index ea423cb991..0000000000 --- a/hw/pci-bridge/ioh3420.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef QEMU_IOH3420_H -#define QEMU_IOH3420_H - -#include "hw/pci/pcie_port.h" - -#endif /* QEMU_IOH3420_H */ diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index b2d861d216..ff6b8323da 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -46,6 +46,9 @@ struct PCIBridgeDev { uint32_t flags; OnOffAuto msi; + + /* additional resources to reserve */ + PCIResReserve res_reserve; }; typedef struct PCIBridgeDev PCIBridgeDev; @@ -95,6 +98,12 @@ static void pci_bridge_dev_realize(PCIDevice *dev, Error **errp) error_free(local_err); } + err = pci_bridge_qemu_reserve_cap_init(dev, 0, + bridge_dev->res_reserve, errp); + if (err) { + goto cap_error; + } + if (shpc_present(dev)) { /* TODO: spec recommends using 64 bit prefetcheable BAR. * Check whether that works well. */ @@ -103,6 +112,8 @@ static void pci_bridge_dev_realize(PCIDevice *dev, Error **errp) } return; +cap_error: + msi_uninit(dev); msi_error: slotid_cap_cleanup(dev); slotid_error: @@ -116,6 +127,8 @@ shpc_error: static void pci_bridge_dev_exitfn(PCIDevice *dev) { PCIBridgeDev *bridge_dev = PCI_BRIDGE_DEV(dev); + + pci_del_capability(dev, PCI_CAP_ID_VNDR, sizeof(PCIBridgeQemuCap)); if (msi_present(dev)) { msi_uninit(dev); } @@ -162,6 +175,17 @@ static Property pci_bridge_dev_properties[] = { ON_OFF_AUTO_AUTO), DEFINE_PROP_BIT(PCI_BRIDGE_DEV_PROP_SHPC, PCIBridgeDev, flags, PCI_BRIDGE_DEV_F_SHPC_REQ, true), + DEFINE_PROP_UINT32("bus-reserve", PCIBridgeDev, + res_reserve.bus, -1), + DEFINE_PROP_SIZE("io-reserve", PCIBridgeDev, + res_reserve.io, -1), + DEFINE_PROP_SIZE("mem-reserve", PCIBridgeDev, + res_reserve.mem_non_pref, -1), + DEFINE_PROP_SIZE("pref32-reserve", PCIBridgeDev, + res_reserve.mem_pref_32, -1), + DEFINE_PROP_SIZE("pref64-reserve", PCIBridgeDev, + res_reserve.mem_pref_64, -1), + DEFINE_PROP_END_OF_LIST(), }; @@ -182,31 +206,39 @@ static const VMStateDescription pci_bridge_dev_vmstate = { } }; -static void pci_bridge_dev_hotplug_cb(HotplugHandler *hotplug_dev, +void pci_bridge_dev_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + + if (!shpc_present(pci_hotplug_dev)) { + error_setg(errp, "standard hotplug controller has been disabled for " + "this %s", object_get_typename(OBJECT(hotplug_dev))); + return; + } + shpc_device_plug_cb(hotplug_dev, dev, errp); +} + +void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); + + g_assert(shpc_present(pci_hotplug_dev)); + shpc_device_unplug_cb(hotplug_dev, dev, errp); +} + +void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); if (!shpc_present(pci_hotplug_dev)) { error_setg(errp, "standard hotplug controller has been disabled for " - "this %s", TYPE_PCI_BRIDGE_DEV); + "this %s", object_get_typename(OBJECT(hotplug_dev))); return; } - shpc_device_hotplug_cb(hotplug_dev, dev, errp); -} - -static void pci_bridge_dev_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, - Error **errp) -{ - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - - if (!shpc_present(pci_hotplug_dev)) { - error_setg(errp, "standard hotplug controller has been disabled for " - "this %s", TYPE_PCI_BRIDGE_DEV); - return; - } - shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp); + shpc_device_unplug_request_cb(hotplug_dev, dev, errp); } static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) @@ -227,8 +259,9 @@ static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) dc->props = pci_bridge_dev_properties; dc->vmsd = &pci_bridge_dev_vmstate; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - hc->plug = pci_bridge_dev_hotplug_cb; - hc->unplug_request = pci_bridge_dev_hot_unplug_request_cb; + hc->plug = pci_bridge_dev_plug_cb; + hc->unplug = pci_bridge_dev_unplug_cb; + hc->unplug_request = pci_bridge_dev_unplug_request_cb; } static const TypeInfo pci_bridge_dev_info = { diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 04cf5a6a92..d491b40d04 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -137,33 +137,6 @@ static const VMStateDescription pcie_pci_bridge_dev_vmstate = { } }; -static void pcie_pci_bridge_hotplug_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - - if (!shpc_present(pci_hotplug_dev)) { - error_setg(errp, "standard hotplug controller has been disabled for " - "this %s", TYPE_PCIE_PCI_BRIDGE_DEV); - return; - } - shpc_device_hotplug_cb(hotplug_dev, dev, errp); -} - -static void pcie_pci_bridge_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, - Error **errp) -{ - PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); - - if (!shpc_present(pci_hotplug_dev)) { - error_setg(errp, "standard hotplug controller has been disabled for " - "this %s", TYPE_PCIE_PCI_BRIDGE_DEV); - return; - } - shpc_device_hot_unplug_request_cb(hotplug_dev, dev, errp); -} - static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -180,8 +153,9 @@ static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) dc->props = pcie_pci_bridge_dev_properties; dc->reset = &pcie_pci_bridge_reset; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); - hc->plug = pcie_pci_bridge_hotplug_cb; - hc->unplug_request = pcie_pci_bridge_hot_unplug_request_cb; + hc->plug = pci_bridge_dev_plug_cb; + hc->unplug = pci_bridge_dev_unplug_cb; + hc->unplug_request = pci_bridge_dev_unplug_request_cb; } static const TypeInfo pcie_pci_bridge_info = { diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 45f9e8cd4a..e94d918b6d 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -47,6 +47,7 @@ static void rp_reset(DeviceState *qdev) pcie_cap_deverr_reset(d); pcie_cap_slot_reset(d); pcie_cap_arifwd_reset(d); + pcie_acs_reset(d); pcie_aer_root_reset(d); pci_bridge_reset(qdev); pci_bridge_disable_base_limit(d); @@ -106,6 +107,9 @@ static void rp_realize(PCIDevice *d, Error **errp) pcie_aer_root_init(d); rp_aer_vector_update(d); + if (rpc->acs_offset) { + pcie_acs_init(d, rpc->acs_offset); + } return; err: @@ -140,6 +144,19 @@ static Property rp_props[] = { DEFINE_PROP_END_OF_LIST() }; +static void rp_instance_post_init(Object *obj) +{ + PCIESlot *s = PCIE_SLOT(obj); + + if (!s->speed) { + s->speed = QEMU_PCI_EXP_LNK_2_5GT; + } + + if (!s->width) { + s->width = QEMU_PCI_EXP_LNK_X1; + } +} + static void rp_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -157,6 +174,7 @@ static void rp_class_init(ObjectClass *klass, void *data) static const TypeInfo rp_info = { .name = TYPE_PCIE_ROOT_PORT, .parent = TYPE_PCIE_SLOT, + .instance_post_init = rp_instance_post_init, .class_init = rp_class_init, .abstract = true, .class_size = sizeof(PCIERootPortClass), diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index b202657954..467bbabe4c 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -23,7 +23,7 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" #include "hw/pci/pcie.h" -#include "xio3130_downstream.h" +#include "hw/pci/pcie_port.h" #include "qapi/error.h" #define PCI_DEVICE_ID_TI_XIO3130D 0x8233 /* downstream port */ @@ -127,32 +127,6 @@ static void xio3130_downstream_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port, uint8_t chassis, - uint16_t slot) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, - "xio3130-downstream"); - if (!d) { - return NULL; - } - br = PCI_BRIDGE(d); - - qdev = DEVICE(d); - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_prop_set_uint8(qdev, "chassis", chassis); - qdev_prop_set_uint16(qdev, "slot", slot); - qdev_init_nofail(qdev); - - return PCIE_SLOT(d); -} - static Property xio3130_downstream_props[] = { DEFINE_PROP_BIT(COMPAT_PROP_PCP, PCIDevice, cap_present, QEMU_PCIE_SLTCAP_PCP_BITNR, true), diff --git a/hw/pci-bridge/xio3130_downstream.h b/hw/pci-bridge/xio3130_downstream.h deleted file mode 100644 index 8426d9ffa6..0000000000 --- a/hw/pci-bridge/xio3130_downstream.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef QEMU_XIO3130_DOWNSTREAM_H -#define QEMU_XIO3130_DOWNSTREAM_H - -#include "hw/pci/pcie_port.h" - -PCIESlot *xio3130_downstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port, uint8_t chassis, - uint16_t slot); - -#endif /* QEMU_XIO3130_DOWNSTREAM_H */ diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index bca2f9a5ea..b524908cf1 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -23,7 +23,7 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/msi.h" #include "hw/pci/pcie.h" -#include "xio3130_upstream.h" +#include "hw/pci/pcie_port.h" #define PCI_DEVICE_ID_TI_XIO3130U 0x8232 /* upstream port */ #define XIO3130_REVISION 0x2 @@ -108,28 +108,6 @@ static void xio3130_upstream_exitfn(PCIDevice *d) pci_bridge_exitfn(d); } -PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port) -{ - PCIDevice *d; - PCIBridge *br; - DeviceState *qdev; - - d = pci_create_multifunction(bus, devfn, multifunction, "x3130-upstream"); - if (!d) { - return NULL; - } - br = PCI_BRIDGE(d); - - qdev = DEVICE(d); - pci_bridge_map_irq(br, bus_name, map_irq); - qdev_prop_set_uint8(qdev, "port", port); - qdev_init_nofail(qdev); - - return PCIE_PORT(d); -} - static const VMStateDescription vmstate_xio3130_upstream = { .name = "xio3130-express-upstream-port", .priority = MIG_PRI_PCI_BUS, diff --git a/hw/pci-bridge/xio3130_upstream.h b/hw/pci-bridge/xio3130_upstream.h deleted file mode 100644 index d0ab7577e2..0000000000 --- a/hw/pci-bridge/xio3130_upstream.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef QEMU_XIO3130_UPSTREAM_H -#define QEMU_XIO3130_UPSTREAM_H - -#include "hw/pci/pcie_port.h" - -PCIEPort *xio3130_upstream_init(PCIBus *bus, int devfn, bool multifunction, - const char *bus_name, pci_map_irq_fn map_irq, - uint8_t port); - -#endif /* QEMU_XIO3130_UPSTREAM_H */ diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig new file mode 100644 index 0000000000..8c16d96b3f --- /dev/null +++ b/hw/pci-host/Kconfig @@ -0,0 +1,52 @@ +config PAM + bool + +config PREP_PCI + select PCI + bool + +config GRACKLE_PCI + select PCI + bool + +config UNIN_PCI + bool + select PCI + select DEC_PCI + select OPENPIC + +config PPCE500_PCI + select PCI + bool + +config VERSATILE_PCI + select PCI + bool + +config PCI_SABRE + select PCI + bool + +config PCI_PIIX + bool + select PCI + select PAM + select ISA_BUS + +config PCI_EXPRESS_Q35 + bool + select PCI_EXPRESS + select PAM + +config PCI_EXPRESS_GENERIC_BRIDGE + bool + select PCI_EXPRESS + +config PCI_EXPRESS_XILINX + bool + select PCI_EXPRESS + +config PCI_EXPRESS_DESIGNWARE + bool + select PCI_EXPRESS + select MSI_NONBROKEN diff --git a/hw/pci-host/Makefile.objs b/hw/pci-host/Makefile.objs index 6d6597c065..a9cd3e022d 100644 --- a/hw/pci-host/Makefile.objs +++ b/hw/pci-host/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y += pam.o +common-obj-$(CONFIG_PAM) += pam.o # PPC devices common-obj-$(CONFIG_PREP_PCI) += prep.o @@ -14,8 +14,8 @@ common-obj-$(CONFIG_VERSATILE_PCI) += versatile.o common-obj-$(CONFIG_PCI_SABRE) += sabre.o common-obj-$(CONFIG_FULONG) += bonito.o common-obj-$(CONFIG_PCI_PIIX) += piix.o -common-obj-$(CONFIG_PCI_Q35) += q35.o -common-obj-$(CONFIG_PCI_GENERIC) += gpex.o -common-obj-$(CONFIG_PCI_XILINX) += xilinx-pcie.o +common-obj-$(CONFIG_PCI_EXPRESS_Q35) += q35.o +common-obj-$(CONFIG_PCI_EXPRESS_GENERIC_BRIDGE) += gpex.o +common-obj-$(CONFIG_PCI_EXPRESS_XILINX) += xilinx-pcie.o -common-obj-$(CONFIG_PCI_DESIGNWARE) += designware.o +common-obj-$(CONFIG_PCI_EXPRESS_DESIGNWARE) += designware.o diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 2d25e9bf7c..dde4437595 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -217,6 +217,7 @@ struct BonitoState { PCIHostState parent_obj; qemu_irq *pic; PCIBonitoState *pci_dev; + MemoryRegion pci_mem; }; #define TYPE_BONITO_PCI_HOST_BRIDGE "Bonito-pcihost" @@ -460,8 +461,8 @@ static uint32_t bonito_sbridge_pciaddr(void *opaque, hwaddr addr) return pciaddr; } -static void bonito_spciconf_writeb(void *opaque, hwaddr addr, - uint32_t val) +static void bonito_spciconf_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { PCIBonitoState *s = opaque; PCIDevice *d = PCI_DEVICE(s); @@ -469,34 +470,8 @@ static void bonito_spciconf_writeb(void *opaque, hwaddr addr, uint32_t pciaddr; uint16_t status; - DPRINTF("bonito_spciconf_writeb "TARGET_FMT_plx" val %x\n", addr, val); - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val & 0xff, 1); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static void bonito_spciconf_writew(void *opaque, hwaddr addr, - uint32_t val) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_writew "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr & 0x1) == 0); + DPRINTF("bonito_spciconf_write "TARGET_FMT_plx" size %d val %x\n", + addr, size, val); pciaddr = bonito_sbridge_pciaddr(s, addr); @@ -506,7 +481,7 @@ static void bonito_spciconf_writew(void *opaque, hwaddr addr, /* set the pci address in s->config_reg */ phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val, 2); + pci_data_write(phb->bus, phb->config_reg, val, size); /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ status = pci_get_word(d->config + PCI_STATUS); @@ -514,8 +489,7 @@ static void bonito_spciconf_writew(void *opaque, hwaddr addr, pci_set_word(d->config + PCI_STATUS, status); } -static void bonito_spciconf_writel(void *opaque, hwaddr addr, - uint32_t val) +static uint64_t bonito_spciconf_read(void *opaque, hwaddr addr, unsigned size) { PCIBonitoState *s = opaque; PCIDevice *d = PCI_DEVICE(s); @@ -523,38 +497,12 @@ static void bonito_spciconf_writel(void *opaque, hwaddr addr, uint32_t pciaddr; uint16_t status; - DPRINTF("bonito_spciconf_writel "TARGET_FMT_plx" val %x\n", addr, val); - assert((addr & 0x3) == 0); + DPRINTF("bonito_spciconf_read "TARGET_FMT_plx" size %d\n", addr, size); pciaddr = bonito_sbridge_pciaddr(s, addr); if (pciaddr == 0xffffffff) { - return; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - pci_data_write(phb->bus, phb->config_reg, val, 4); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); -} - -static uint32_t bonito_spciconf_readb(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readb "TARGET_FMT_plx"\n", addr); - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xff; + return MAKE_64BIT_MASK(0, size * 8); } /* set the pci address in s->config_reg */ @@ -565,79 +513,17 @@ static uint32_t bonito_spciconf_readb(void *opaque, hwaddr addr) status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); pci_set_word(d->config + PCI_STATUS, status); - return pci_data_read(phb->bus, phb->config_reg, 1); -} - -static uint32_t bonito_spciconf_readw(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readw "TARGET_FMT_plx"\n", addr); - assert((addr & 0x1) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xffff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 2); -} - -static uint32_t bonito_spciconf_readl(void *opaque, hwaddr addr) -{ - PCIBonitoState *s = opaque; - PCIDevice *d = PCI_DEVICE(s); - PCIHostState *phb = PCI_HOST_BRIDGE(s->pcihost); - uint32_t pciaddr; - uint16_t status; - - DPRINTF("bonito_spciconf_readl "TARGET_FMT_plx"\n", addr); - assert((addr & 0x3) == 0); - - pciaddr = bonito_sbridge_pciaddr(s, addr); - - if (pciaddr == 0xffffffff) { - return 0xffffffff; - } - - /* set the pci address in s->config_reg */ - phb->config_reg = (pciaddr) | (1u << 31); - - /* clear PCI_STATUS_REC_MASTER_ABORT and PCI_STATUS_REC_TARGET_ABORT */ - status = pci_get_word(d->config + PCI_STATUS); - status &= ~(PCI_STATUS_REC_MASTER_ABORT | PCI_STATUS_REC_TARGET_ABORT); - pci_set_word(d->config + PCI_STATUS, status); - - return pci_data_read(phb->bus, phb->config_reg, 4); + return pci_data_read(phb->bus, phb->config_reg, size); } /* south bridge PCI configure space. 0x1fe8 0000 - 0x1fef ffff */ static const MemoryRegionOps bonito_spciconf_ops = { - .old_mmio = { - .read = { - bonito_spciconf_readb, - bonito_spciconf_readw, - bonito_spciconf_readl, - }, - .write = { - bonito_spciconf_writeb, - bonito_spciconf_writew, - bonito_spciconf_writel, - }, - }, + .read = bonito_spciconf_read, + .write = bonito_spciconf_write, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .impl.min_access_size = 1, + .impl.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -710,16 +596,18 @@ static const VMStateDescription vmstate_bonito = { } }; -static int bonito_pcihost_initfn(SysBusDevice *dev) +static void bonito_pcihost_realize(DeviceState *dev, Error **errp) { PCIHostState *phb = PCI_HOST_BRIDGE(dev); + BonitoState *bs = BONITO_PCI_HOST_BRIDGE(dev); + memory_region_init(&bs->pci_mem, OBJECT(dev), "pci.mem", BONITO_PCILO_SIZE); phb->bus = pci_register_root_bus(DEVICE(dev), "pci", pci_bonito_set_irq, pci_bonito_map_irq, - dev, get_system_memory(), get_system_io(), + dev, &bs->pci_mem, get_system_io(), 0x28, 32, TYPE_PCI_BUS); - - return 0; + memory_region_add_subregion(get_system_memory(), BONITO_PCILO_BASE, + &bs->pci_mem); } static void bonito_realize(PCIDevice *dev, Error **errp) @@ -799,7 +687,6 @@ PCIBus *bonito_init(qemu_irq *pic) pcihost->pic = pic; qdev_init_nofail(dev); - /* set the pcihost pointer before bonito_initfn is called */ d = pci_create(phb->bus, PCI_DEVFN(0, 0), TYPE_PCI_BONITO); s = PCI_BONITO(d); s->pcihost = pcihost; @@ -841,9 +728,9 @@ static const TypeInfo bonito_info = { static void bonito_pcihost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - k->init = bonito_pcihost_initfn; + dc->realize = bonito_pcihost_realize; } static const TypeInfo bonito_pcihost_info = { diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 4810a4de79..5a151e93e9 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -37,6 +37,7 @@ typedef struct GrackleState { PCIHostState parent_obj; + uint32_t ofw_addr; HeathrowState *pic; qemu_irq irqs[4]; MemoryRegion pci_mmio; @@ -146,12 +147,28 @@ static const TypeInfo grackle_pci_info = { }, }; +static char *grackle_ofw_unit_address(const SysBusDevice *dev) +{ + GrackleState *s = GRACKLE_PCI_HOST_BRIDGE(dev); + + return g_strdup_printf("%x", s->ofw_addr); +} + +static Property grackle_properties[] = { + DEFINE_PROP_UINT32("ofw-addr", GrackleState, ofw_addr, -1), + DEFINE_PROP_END_OF_LIST() +}; + static void grackle_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = grackle_realize; + dc->props = grackle_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->fw_name = "pci"; + sbc->explicit_ofw_unit_address = grackle_ofw_unit_address; } static const TypeInfo grackle_host_info = { diff --git a/hw/pci-host/pam.c b/hw/pci-host/pam.c index e361ecb7ee..aa5ecfd0c2 100644 --- a/hw/pci-host/pam.c +++ b/hw/pci-host/pam.c @@ -52,11 +52,13 @@ void init_pam(DeviceState *dev, MemoryRegion *ram_memory, memory_region_init_alias(&mem->alias[2], OBJECT(dev), "pam-pci", ram_memory, start, size); + memory_region_transaction_begin(); for (i = 0; i < 4; ++i) { memory_region_set_enabled(&mem->alias[i], false); memory_region_add_subregion_overlap(system_memory, start, &mem->alias[i], 1); } + memory_region_transaction_commit(); mem->current = 0; } diff --git a/hw/pci-host/piix.c b/hw/pci-host/piix.c index 0e608347c1..d9c70f7ce6 100644 --- a/hw/pci-host/piix.c +++ b/hw/pci-host/piix.c @@ -40,7 +40,7 @@ /* * I440FX chipset data sheet. - * http://download.intel.com/design/chipsets/datashts/29054901.pdf + * https://wiki.qemu.org/File:29054901.pdf */ #define I440FX_PCI_HOST_BRIDGE(obj) \ @@ -95,6 +95,9 @@ typedef struct PIIX3State { #define I440FX_PCI_DEVICE(obj) \ OBJECT_CHECK(PCII440FXState, (obj), TYPE_I440FX_PCI_DEVICE) +#define TYPE_PIIX3_DEVICE "PIIX3" +#define TYPE_PIIX3_XEN_DEVICE "PIIX3-xen" + struct PCII440FXState { /*< private >*/ PCIDevice parent_obj; @@ -142,9 +145,9 @@ static void i440fx_update_memory_mappings(PCII440FXState *d) PCIDevice *pd = PCI_DEVICE(d); memory_region_transaction_begin(); - for (i = 0; i < 13; i++) { + for (i = 0; i < ARRAY_SIZE(d->pam_regions); i++) { pam_update(&d->pam_regions[i], i, - pd->config[I440FX_PAM + (DIV_ROUND_UP(i, 2))]); + pd->config[I440FX_PAM + DIV_ROUND_UP(i, 2)]); } memory_region_set_enabled(&d->smram_region, !(pd->config[I440FX_SMRAM] & SMRAM_D_OPEN)); @@ -249,9 +252,7 @@ static void i440fx_pcihost_get_pci_hole_end(Object *obj, Visitor *v, * the 64bit PCI hole will start after "over 4G RAM" and the * reserved space for memory hotplug if any. */ -static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, - const char *name, - void *opaque, Error **errp) +static uint64_t i440fx_pcihost_get_pci_hole64_start_value(Object *obj) { PCIHostState *h = PCI_HOST_BRIDGE(obj); I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); @@ -263,7 +264,16 @@ static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, if (!value && s->pci_hole64_fix) { value = pc_pci_hole64_start(); } - visit_type_uint64(v, name, &value, errp); + return value; +} + +static void i440fx_pcihost_get_pci_hole64_start(Object *obj, Visitor *v, + const char *name, + void *opaque, Error **errp) +{ + uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj); + + visit_type_uint64(v, name, &hole64_start, errp); } /* @@ -278,7 +288,7 @@ static void i440fx_pcihost_get_pci_hole64_end(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); I440FXState *s = I440FX_PCI_HOST_BRIDGE(obj); - uint64_t hole64_start = pc_pci_hole64_start(); + uint64_t hole64_start = i440fx_pcihost_get_pci_hole64_start_value(obj); Range w64; uint64_t value, hole64_end; @@ -327,6 +337,10 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) sysbus_add_io(sbd, 0xcfc, &s->data_mem); sysbus_init_ioports(sbd, 0xcfc, 4); + + /* register i440fx 0xcf8 port as coalesced pio */ + memory_region_set_flush_coalesced(&s->data_mem); + memory_region_add_coalescing(&s->conf_mem, 0, 4); } static void i440fx_realize(PCIDevice *dev, Error **errp) @@ -401,7 +415,7 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, &f->pam_regions[0], PAM_BIOS_BASE, PAM_BIOS_SIZE); - for (i = 0; i < 12; ++i) { + for (i = 0; i < ARRAY_SIZE(f->pam_regions) - 1; ++i) { init_pam(dev, f->ram_memory, f->system_memory, f->pci_address_space, &f->pam_regions[i+1], PAM_EXPAN_BASE + i * PAM_EXPAN_SIZE, PAM_EXPAN_SIZE); @@ -413,13 +427,13 @@ PCIBus *i440fx_init(const char *host_type, const char *pci_type, * These additional routes can be discovered through ACPI. */ if (xen_enabled()) { PCIDevice *pci_dev = pci_create_simple_multifunction(b, - -1, true, "PIIX3-xen"); + -1, true, TYPE_PIIX3_XEN_DEVICE); piix3 = PIIX3_PCI_DEVICE(pci_dev); pci_bus_irqs(b, xen_piix3_set_irq, xen_pci_slot_get_pirq, piix3, XEN_PIIX_NUM_PIRQS); } else { PCIDevice *pci_dev = pci_create_simple_multifunction(b, - -1, true, "PIIX3"); + -1, true, TYPE_PIIX3_DEVICE); piix3 = PIIX3_PCI_DEVICE(pci_dev); pci_bus_irqs(b, piix3_set_irq, pci_slot_get_pirq, piix3, PIIX_NUM_PIRQS); @@ -737,7 +751,7 @@ static void piix3_class_init(ObjectClass *klass, void *data) } static const TypeInfo piix3_info = { - .name = "PIIX3", + .name = TYPE_PIIX3_DEVICE, .parent = TYPE_PIIX3_PCI_DEVICE, .class_init = piix3_class_init, }; @@ -750,7 +764,7 @@ static void piix3_xen_class_init(ObjectClass *klass, void *data) }; static const TypeInfo piix3_xen_info = { - .name = "PIIX3-xen", + .name = TYPE_PIIX3_XEN_DEVICE, .parent = TYPE_PIIX3_PCI_DEVICE, .class_init = piix3_xen_class_init, }; diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index eb75e080fc..b8f8c112e6 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -436,8 +436,9 @@ static AddressSpace *e500_pcihost_set_iommu(PCIBus *bus, void *opaque, return &s->bm_as; } -static int e500_pcihost_initfn(SysBusDevice *dev) +static void e500_pcihost_realize(DeviceState *dev, Error **errp) { + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PCIHostState *h; PPCE500PCIState *s; PCIBus *b; @@ -447,7 +448,7 @@ static int e500_pcihost_initfn(SysBusDevice *dev) s = PPC_E500_PCI_HOST_BRIDGE(dev); for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); + sysbus_init_irq(sbd, &s->irq[i]); } for (i = 0; i < PCI_NUM_PINS; i++) { @@ -460,7 +461,7 @@ static int e500_pcihost_initfn(SysBusDevice *dev) /* PIO lives at the bottom of our bus space */ memory_region_add_subregion_overlap(&s->busmem, 0, &s->pio, -2); - b = pci_register_root_bus(DEVICE(dev), NULL, mpc85xx_pci_set_irq, + b = pci_register_root_bus(dev, NULL, mpc85xx_pci_set_irq, mpc85xx_pci_map_irq, s, &s->busmem, &s->pio, PCI_DEVFN(s->first_slot, 0), 4, TYPE_PCI_BUS); h->bus = b; @@ -483,10 +484,8 @@ static int e500_pcihost_initfn(SysBusDevice *dev) memory_region_add_subregion(&s->container, PCIE500_CFGADDR, &h->conf_mem); memory_region_add_subregion(&s->container, PCIE500_CFGDATA, &h->data_mem); memory_region_add_subregion(&s->container, PCIE500_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); + sysbus_init_mmio(sbd, &s->container); pci_bus_set_route_irq_fn(b, e500_route_intx_pin_to_irq); - - return 0; } static void e500_host_bridge_class_init(ObjectClass *klass, void *data) @@ -526,9 +525,8 @@ static Property pcihost_properties[] = { static void e500_pcihost_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = e500_pcihost_initfn; + dc->realize = e500_pcihost_realize; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->props = pcihost_properties; dc->vmsd = &vmstate_ppce500_pci; diff --git a/hw/pci-host/prep.c b/hw/pci-host/prep.c index 88f035c20b..8b9e1fd0d3 100644 --- a/hw/pci-host/prep.c +++ b/hw/pci-host/prep.c @@ -32,6 +32,7 @@ #include "hw/pci/pci_host.h" #include "hw/i386/pc.h" #include "hw/loader.h" +#include "hw/or-irq.h" #include "exec/address-spaces.h" #include "elf.h" @@ -55,7 +56,8 @@ typedef struct RavenPCIState { typedef struct PRePPCIState { PCIHostState parent_obj; - qemu_irq irq[PCI_NUM_PINS]; + qemu_or_irq *or_irq; + qemu_irq pci_irqs[PCI_NUM_PINS]; PCIBus pci_bus; AddressSpace pci_io_as; MemoryRegion pci_io; @@ -69,6 +71,7 @@ typedef struct PRePPCIState { RavenPCIState pci_dev; int contiguous_map; + bool is_legacy_prep; } PREPPCIState; #define BIOS_SIZE (1 * MiB) @@ -194,9 +197,9 @@ static int raven_map_irq(PCIDevice *pci_dev, int irq_num) static void raven_set_irq(void *opaque, int irq_num, int level) { - qemu_irq *pic = opaque; + PREPPCIState *s = opaque; - qemu_set_irq(pic[irq_num] , level); + qemu_set_irq(s->pci_irqs[irq_num], level); } static AddressSpace *raven_pcihost_set_iommu(PCIBus *bus, void *opaque, @@ -222,14 +225,28 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) MemoryRegion *address_space_mem = get_system_memory(); int i; - for (i = 0; i < PCI_NUM_PINS; i++) { - sysbus_init_irq(dev, &s->irq[i]); + if (s->is_legacy_prep) { + for (i = 0; i < PCI_NUM_PINS; i++) { + sysbus_init_irq(dev, &s->pci_irqs[i]); + } + } else { + /* According to PReP specification section 6.1.6 "System Interrupt + * Assignments", all PCI interrupts are routed via IRQ 15 */ + s->or_irq = OR_IRQ(object_new(TYPE_OR_IRQ)); + object_property_set_int(OBJECT(s->or_irq), PCI_NUM_PINS, "num-lines", + &error_fatal); + object_property_set_bool(OBJECT(s->or_irq), true, "realized", + &error_fatal); + sysbus_init_irq(dev, &s->or_irq->out_irq); + + for (i = 0; i < PCI_NUM_PINS; i++) { + s->pci_irqs[i] = qdev_get_gpio_in(DEVICE(s->or_irq), i); + } } qdev_init_gpio_in(d, raven_change_gpio, 1); - pci_bus_irqs(&s->pci_bus, raven_set_irq, raven_map_irq, s->irq, - PCI_NUM_PINS); + pci_bus_irqs(&s->pci_bus, raven_set_irq, raven_map_irq, s, PCI_NUM_PINS); memory_region_init_io(&h->conf_mem, OBJECT(h), &pci_host_conf_le_ops, s, "pci-conf-idx", 4); @@ -314,7 +331,7 @@ static void raven_realize(PCIDevice *d, Error **errp) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bios_name); if (filename) { if (s->elf_machine != EM_NONE) { - bios_size = load_elf(filename, NULL, NULL, NULL, + bios_size = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, 1, s->elf_machine, 0, 0); } if (bios_size < 0) { @@ -383,6 +400,9 @@ static Property raven_pcihost_properties[] = { DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine, EM_NONE), DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name), + /* Temporary workaround until legacy prep machine is removed */ + DEFINE_PROP_BOOL("is-legacy-prep", PREPPCIState, is_legacy_prep, + false), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 02f9576588..7b871b5734 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -51,6 +51,10 @@ static void q35_host_realize(DeviceState *dev, Error **errp) sysbus_add_io(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, &pci->data_mem); sysbus_init_ioports(sbd, MCH_HOST_BRIDGE_CONFIG_DATA, 4); + /* register q35 0xcf8 port as coalesced pio */ + memory_region_set_flush_coalesced(&pci->data_mem); + memory_region_add_coalescing(&pci->conf_mem, 0, 4); + pci->bus = pci_root_bus_new(DEVICE(s), "pcie.0", s->mch.pci_address_space, s->mch.address_space_io, @@ -109,9 +113,7 @@ static void q35_host_get_pci_hole_end(Object *obj, Visitor *v, * the 64bit PCI hole will start after "over 4G RAM" and the * reserved space for memory hotplug if any. */ -static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, - const char *name, void *opaque, - Error **errp) +static uint64_t q35_host_get_pci_hole64_start_value(Object *obj) { PCIHostState *h = PCI_HOST_BRIDGE(obj); Q35PCIHost *s = Q35_HOST_DEVICE(obj); @@ -123,7 +125,16 @@ static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, if (!value && s->pci_hole64_fix) { value = pc_pci_hole64_start(); } - visit_type_uint64(v, name, &value, errp); + return value; +} + +static void q35_host_get_pci_hole64_start(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + uint64_t hole64_start = q35_host_get_pci_hole64_start_value(obj); + + visit_type_uint64(v, name, &hole64_start, errp); } /* @@ -138,7 +149,7 @@ static void q35_host_get_pci_hole64_end(Object *obj, Visitor *v, { PCIHostState *h = PCI_HOST_BRIDGE(obj); Q35PCIHost *s = Q35_HOST_DEVICE(obj); - uint64_t hole64_start = pc_pci_hole64_start(); + uint64_t hole64_start = q35_host_get_pci_hole64_start_value(obj); Range w64; uint64_t value, hole64_end; @@ -352,7 +363,7 @@ static void mch_update_pam(MCHPCIState *mch) memory_region_transaction_begin(); for (i = 0; i < 13; i++) { pam_update(&mch->pam_regions[i], i, - pd->config[MCH_HOST_BRIDGE_PAM0 + (DIV_ROUND_UP(i, 2))]); + pd->config[MCH_HOST_BRIDGE_PAM0 + DIV_ROUND_UP(i, 2)]); } memory_region_transaction_commit(); } @@ -618,7 +629,15 @@ static void mch_class_init(ObjectClass *klass, void *data) dc->desc = "Host bridge"; dc->vmsd = &vmstate_mch; k->vendor_id = PCI_VENDOR_ID_INTEL; - k->device_id = PCI_DEVICE_ID_INTEL_Q35_MCH; + /* + * The 'q35' machine type implements an Intel Series 3 chipset, + * of which there are several variants. The key difference between + * the 82P35 MCH ('p35') and 82Q35 GMCH ('q35') variants is that + * the latter has an integrated graphics adapter. QEMU does not + * implement integrated graphics, so uses the PCI ID for the 82P35 + * chipset. + */ + k->device_id = PCI_DEVICE_ID_INTEL_P35_MCH; k->revision = MCH_HOST_BRIDGE_REVISION_DEFAULT; k->class_id = PCI_CLASS_BRIDGE_HOST; /* diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index e2f4ee480e..e33bd46967 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -496,6 +496,15 @@ static const TypeInfo sabre_pci_info = { }, }; +static char *sabre_ofw_unit_address(const SysBusDevice *dev) +{ + SabreState *s = SABRE_DEVICE(dev); + + return g_strdup_printf("%x,%x", + (uint32_t)((s->special_base >> 32) & 0xffffffff), + (uint32_t)(s->special_base & 0xffffffff)); +} + static Property sabre_properties[] = { DEFINE_PROP_UINT64("special-base", SabreState, special_base, 0), DEFINE_PROP_UINT64("mem-base", SabreState, mem_base, 0), @@ -505,11 +514,14 @@ static Property sabre_properties[] = { static void sabre_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = sabre_realize; dc->reset = sabre_reset; dc->props = sabre_properties; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->fw_name = "pci"; + sbc->explicit_ofw_unit_address = sabre_ofw_unit_address; } static const TypeInfo sabre_info = { diff --git a/hw/pci-host/trace-events b/hw/pci-host/trace-events index dd7a398e96..d19ca9aef6 100644 --- a/hw/pci-host/trace-events +++ b/hw/pci-host/trace-events @@ -1,9 +1,9 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/pci-host/grackle.c +# grackle.c grackle_set_irq(int irq_num, int level) "set_irq num %d level %d" -# hw/pci-host/sabre.c +# sabre.c sabre_set_request(int irq_num) "request irq %d" sabre_clear_request(int irq_num) "clear request irq %d" sabre_config_write(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRIx64 @@ -13,7 +13,7 @@ sabre_pci_config_read(uint64_t addr, uint64_t val) "addr 0x%"PRIx64" val 0x%"PRI sabre_pci_set_irq(int irq_num, int level) "set irq_in %d level %d" sabre_pci_set_obio_irq(int irq_num, int level) "set irq %d level %d" -# hw/pci-host/uninorth.c +# uninorth.c unin_set_irq(int irq_num, int level) "setting INT %d = %d" unin_get_config_reg(uint32_t reg, uint32_t addr, uint32_t retval) "converted config space accessor 0x%"PRIx32 "/0x%"PRIx32 " -> 0x%"PRIx32 unin_data_write(uint64_t addr, unsigned len, uint64_t val) "write addr 0x%"PRIx64 " len %d val 0x%"PRIx64 diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index a843aa7b36..1378c5c7fb 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -118,6 +118,13 @@ static void pci_unin_init_irqs(UNINHostState *s) } } +static char *pci_unin_main_ofw_unit_address(const SysBusDevice *dev) +{ + UNINHostState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); + + return g_strdup_printf("%x", s->ofw_addr); +} + static void pci_unin_main_realize(DeviceState *dev, Error **errp) { UNINHostState *s = UNI_NORTH_PCI_HOST_BRIDGE(dev); @@ -455,12 +462,21 @@ static const TypeInfo unin_internal_pci_host_info = { }, }; +static Property pci_unin_main_pci_host_props[] = { + DEFINE_PROP_UINT32("ofw-addr", UNINHostState, ofw_addr, -1), + DEFINE_PROP_END_OF_LIST() +}; + static void pci_unin_main_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); + SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); dc->realize = pci_unin_main_realize; + dc->props = pci_unin_main_pci_host_props; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); + dc->fw_name = "pci"; + sbc->explicit_ofw_unit_address = pci_unin_main_ofw_unit_address; } static const TypeInfo pci_unin_main_info = { diff --git a/hw/pci/Kconfig b/hw/pci/Kconfig new file mode 100644 index 0000000000..77f8b005ff --- /dev/null +++ b/hw/pci/Kconfig @@ -0,0 +1,15 @@ +config PCI + bool + +config PCI_EXPRESS + bool + select PCI + +config PCI_DEVICES + bool + +config MSI_NONBROKEN + # selected by interrupt controllers that do not support MSI, + # or support it and have a good implementation. See commit + # 47d2b0f33c664533b8dbd5cb17faa8e6a01afe1f. + bool diff --git a/hw/pci/Makefile.objs b/hw/pci/Makefile.objs index 9f905e6344..c78f2fb24b 100644 --- a/hw/pci/Makefile.objs +++ b/hw/pci/Makefile.objs @@ -2,8 +2,13 @@ common-obj-$(CONFIG_PCI) += pci.o pci_bridge.o common-obj-$(CONFIG_PCI) += msix.o msi.o common-obj-$(CONFIG_PCI) += shpc.o common-obj-$(CONFIG_PCI) += slotid_cap.o -common-obj-$(CONFIG_PCI) += pci_host.o pcie_host.o -common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o pcie_port.o +common-obj-$(CONFIG_PCI) += pci_host.o + +# The functions in these modules can be used by devices too. Since we +# allow plugging PCIe devices into PCI buses, include them even if +# CONFIG_PCI_EXPRESS=n. +common-obj-$(CONFIG_PCI) += pcie.o pcie_aer.o +common-obj-$(CONFIG_PCI_EXPRESS) += pcie_port.o pcie_host.o common-obj-$(call lnot,$(CONFIG_PCI)) += pci-stub.o common-obj-$(CONFIG_ALL) += pci-stub.o diff --git a/hw/pci/msi.c b/hw/pci/msi.c index 5e05ce5ec2..47d2b0f33c 100644 --- a/hw/pci/msi.c +++ b/hw/pci/msi.c @@ -286,7 +286,7 @@ void msi_reset(PCIDevice *dev) MSI_DEV_PRINTF(dev, "reset\n"); } -static bool msi_is_masked(const PCIDevice *dev, unsigned int vector) +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, data; diff --git a/hw/pci/msix.c b/hw/pci/msix.c index c944c02135..4e336416a7 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -345,7 +345,7 @@ int msix_init_exclusive_bar(PCIDevice *dev, unsigned short nentries, char *name; uint32_t bar_size = 4096; uint32_t bar_pba_offset = bar_size / 2; - uint32_t bar_pba_size = (nentries / 8 + 1) * 8; + uint32_t bar_pba_size = QEMU_ALIGN_UP(nentries, 64) / 8; /* * Migration compatibility dictates that this remains a 4k @@ -501,7 +501,7 @@ void msix_reset(PCIDevice *dev) } msix_clear_all_vectors(dev); dev->config[dev->msix_cap + MSIX_CONTROL_OFFSET] &= - ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; + ~dev->wmask[dev->msix_cap + MSIX_CONTROL_OFFSET]; memset(dev->msix_table, 0, dev->msix_entries_nr * PCI_MSIX_ENTRY_SIZE); memset(dev->msix_pba, 0, QEMU_ALIGN_UP(dev->msix_entries_nr, 64) / 8); msix_mask_all(dev, dev->msix_entries_nr); @@ -625,7 +625,7 @@ void msix_unset_vector_notifiers(PCIDevice *dev) } static int put_msix_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { msix_save(pv, f); @@ -633,7 +633,7 @@ static int put_msix_state(QEMUFile *f, void *pv, size_t size, } static int get_msix_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { msix_load(pv, f); return 0; diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 80bc45930d..6d13ef877b 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -147,6 +147,11 @@ static uint16_t pcibus_numa_node(PCIBus *bus) return NUMA_NODE_UNASSIGNED; } +static bool pcibus_allows_extended_config_space(PCIBus *bus) +{ + return false; +} + static void pci_bus_class_init(ObjectClass *klass, void *data) { BusClass *k = BUS_CLASS(klass); @@ -162,6 +167,7 @@ static void pci_bus_class_init(ObjectClass *klass, void *data) pbc->is_root = pcibus_is_root; pbc->bus_num = pcibus_num; pbc->numa_node = pcibus_numa_node; + pbc->allows_extended_config_space = pcibus_allows_extended_config_space; } static const TypeInfo pci_bus_info = { @@ -182,9 +188,22 @@ static const TypeInfo conventional_pci_interface_info = { .parent = TYPE_INTERFACE, }; +static bool pciebus_allows_extended_config_space(PCIBus *bus) +{ + return true; +} + +static void pcie_bus_class_init(ObjectClass *klass, void *data) +{ + PCIBusClass *pbc = PCI_BUS_CLASS(klass); + + pbc->allows_extended_config_space = pciebus_allows_extended_config_space; +} + static const TypeInfo pcie_bus_info = { .name = TYPE_PCIE_BUS, .parent = TYPE_PCI_BUS, + .class_init = pcie_bus_class_init, }; static PCIBus *pci_find_bus_nr(PCIBus *bus, int bus_num); @@ -211,13 +230,13 @@ int pci_bar(PCIDevice *d, int reg) static inline int pci_irq_state(PCIDevice *d, int irq_num) { - return (d->irq_state >> irq_num) & 0x1; + return (d->irq_state >> irq_num) & 0x1; } static inline void pci_set_irq_state(PCIDevice *d, int irq_num, int level) { - d->irq_state &= ~(0x1 << irq_num); - d->irq_state |= level << irq_num; + d->irq_state &= ~(0x1 << irq_num); + d->irq_state |= level << irq_num; } static void pci_change_irq_level(PCIDevice *pci_dev, int irq_num, int change) @@ -333,6 +352,13 @@ static void pci_host_bus_register(DeviceState *host) QLIST_INSERT_HEAD(&pci_host_bridges, host_bridge, next); } +static void pci_host_bus_unregister(DeviceState *host) +{ + PCIHostState *host_bridge = PCI_HOST_BRIDGE(host); + + QLIST_REMOVE(host_bridge, next); +} + PCIBus *pci_device_root_bus(const PCIDevice *d) { PCIBus *bus = pci_get_bus(d); @@ -379,6 +405,11 @@ static void pci_root_bus_init(PCIBus *bus, DeviceState *parent, pci_host_bus_register(parent); } +static void pci_bus_uninit(PCIBus *bus) +{ + pci_host_bus_unregister(BUS(bus)->parent); +} + bool pci_bus_is_express(PCIBus *bus) { return object_dynamic_cast(OBJECT(bus), TYPE_PCIE_BUS); @@ -389,6 +420,11 @@ bool pci_bus_is_root(PCIBus *bus) return PCI_BUS_GET_CLASS(bus)->is_root(bus); } +bool pci_bus_allows_extended_config_space(PCIBus *bus) +{ + return PCI_BUS_GET_CLASS(bus)->allows_extended_config_space(bus); +} + void pci_root_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, @@ -413,6 +449,13 @@ PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, return bus; } +void pci_root_bus_cleanup(PCIBus *bus) +{ + pci_bus_uninit(bus); + /* the caller of the unplug hotplug handler will delete this device */ + object_property_set_bool(OBJECT(bus), false, "realized", NULL); +} + void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq) { @@ -423,6 +466,15 @@ void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, bus->irq_count = g_malloc0(nirq * sizeof(bus->irq_count[0])); } +void pci_bus_irqs_cleanup(PCIBus *bus) +{ + bus->set_irq = NULL; + bus->map_irq = NULL; + bus->irq_opaque = NULL; + bus->nirq = 0; + g_free(bus->irq_count); +} + PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, @@ -439,6 +491,12 @@ PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, return bus; } +void pci_unregister_root_bus(PCIBus *bus) +{ + pci_bus_irqs_cleanup(bus); + pci_root_bus_cleanup(bus); +} + int pci_bus_num(PCIBus *s) { return PCI_BUS_GET_CLASS(s)->bus_num(s); @@ -450,7 +508,7 @@ int pci_bus_numa_node(PCIBus *bus) } static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { PCIDevice *s = container_of(pv, PCIDevice, config); PCIDeviceClass *pc = PCI_DEVICE_GET_CLASS(s); @@ -490,7 +548,7 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, /* just put buffer */ static int put_pci_config_device(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { const uint8_t **v = pv; assert(size == pci_config_size(container_of(pv, PCIDevice, config))); @@ -506,7 +564,7 @@ static VMStateInfo vmstate_info_pci_config = { }; static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { PCIDevice *s = container_of(pv, PCIDevice, irq_state); uint32_t irq_state[PCI_NUM_PINS]; @@ -528,7 +586,7 @@ static int get_pci_irq_state(QEMUFile *f, void *pv, size_t size, } static int put_pci_irq_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { int i; PCIDevice *s = container_of(pv, PCIDevice, irq_state); @@ -571,8 +629,8 @@ const VMStateDescription vmstate_pci_device = { 0, vmstate_info_pci_config, PCIE_CONFIG_SPACE_SIZE), VMSTATE_BUFFER_UNSAFE_INFO(irq_state, PCIDevice, 2, - vmstate_info_pci_irq_state, - PCI_NUM_PINS * sizeof(int32_t)), + vmstate_info_pci_irq_state, + PCI_NUM_PINS * sizeof(int32_t)), VMSTATE_END_OF_LIST() } }; @@ -624,21 +682,21 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, p = addr; val = strtoul(p, &e, 16); if (e == p) - return -1; + return -1; if (*e == ':') { - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - if (*e == ':') { - dom = bus; - bus = val; - p = e + 1; - val = strtoul(p, &e, 16); - if (e == p) - return -1; - } + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + if (*e == ':') { + dom = bus; + bus = val; + p = e + 1; + val = strtoul(p, &e, 16); + if (e == p) + return -1; + } } slot = val; @@ -657,10 +715,10 @@ static int pci_parse_devaddr(const char *addr, int *domp, int *busp, /* if funcp == NULL func is 0 */ if (dom > 0xffff || bus > 0xff || slot > 0x1f || func > 7) - return -1; + return -1; if (*e) - return -1; + return -1; *domp = dom; *busp = bus; @@ -1217,7 +1275,7 @@ pcibus_t pci_get_bar_addr(PCIDevice *pci_dev, int region_num) } static pcibus_t pci_bar_address(PCIDevice *d, - int reg, uint8_t type, pcibus_t size) + int reg, uint8_t type, pcibus_t size) { pcibus_t new_addr, last_addr; int bar = pci_bar(d, reg); @@ -1353,6 +1411,10 @@ uint32_t pci_default_read_config(PCIDevice *d, { uint32_t val = 0; + if (pci_is_express_downstream_port(d) && + ranges_overlap(address, len, d->exp.exp_cap + PCI_EXP_LNKSTA, 2)) { + pcie_sync_bridge_lnk(d); + } memcpy(&val, d->config + address, len); return le32_to_cpu(val); } @@ -1749,6 +1811,16 @@ static PciDeviceInfo *qmp_query_pci_device(PCIDevice *dev, PCIBus *bus, if (type == PCI_HEADER_TYPE_BRIDGE) { info->has_pci_bridge = true; info->pci_bridge = qmp_query_pci_bridge(dev, bus, bus_num); + } else if (type == PCI_HEADER_TYPE_NORMAL) { + info->id->has_subsystem = info->id->has_subsystem_vendor = true; + info->id->subsystem = pci_get_word(dev->config + PCI_SUBSYSTEM_ID); + info->id->subsystem_vendor = + pci_get_word(dev->config + PCI_SUBSYSTEM_VENDOR_ID); + } else if (type == PCI_HEADER_TYPE_CARDBUS) { + info->id->has_subsystem = info->id->has_subsystem_vendor = true; + info->id->subsystem = pci_get_word(dev->config + PCI_CB_SUBSYSTEM_ID); + info->id->subsystem_vendor = + pci_get_word(dev->config + PCI_CB_SUBSYSTEM_VENDOR_ID); } return info; @@ -2251,7 +2323,11 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, pdev->has_rom = true; memory_region_init_rom(&pdev->rom, OBJECT(pdev), name, size, &error_fatal); ptr = memory_region_get_ram_ptr(&pdev->rom); - load_image(path, ptr); + if (load_image_size(path, ptr, size) < 0) { + error_setg(errp, "failed to load romfile \"%s\"", pdev->romfile); + g_free(path); + return; + } g_free(path); if (is_default_rom) { diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 40a39f57cb..c6d9ded320 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -241,9 +241,9 @@ void pci_bridge_update_mappings(PCIBridge *br) * while another accesses an unaffected region. */ memory_region_transaction_begin(); pci_bridge_region_del(br, br->windows); + pci_bridge_region_cleanup(br, w); br->windows = pci_bridge_region_init(br); memory_region_transaction_commit(); - pci_bridge_region_cleanup(br, w); } /* default write_config function for PCI-to-PCI bridge */ @@ -369,7 +369,7 @@ void pci_bridge_initfn(PCIDevice *dev, const char *typename) * let users address the bus using the device name. */ if (!br->bus_name && dev->qdev.id && *dev->qdev.id) { - br->bus_name = dev->qdev.id; + br->bus_name = dev->qdev.id; } qbus_create_inplace(sec_bus, sizeof(br->sec_bus), typename, DEVICE(dev), @@ -399,7 +399,7 @@ void pci_bridge_exitfn(PCIDevice *pci_dev) /* * before qdev initialization(qdev_init()), this function sets bus_name and - * map_irq callback which are necessry for pci_bridge_initfn() to + * map_irq callback which are necessary for pci_bridge_initfn() to * initialize bus. */ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, @@ -411,38 +411,34 @@ void pci_bridge_map_irq(PCIBridge *br, const char* bus_name, int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, - uint32_t bus_reserve, uint64_t io_reserve, - uint64_t mem_non_pref_reserve, - uint64_t mem_pref_32_reserve, - uint64_t mem_pref_64_reserve, - Error **errp) + PCIResReserve res_reserve, Error **errp) { - if (mem_pref_32_reserve != (uint64_t)-1 && - mem_pref_64_reserve != (uint64_t)-1) { + if (res_reserve.mem_pref_32 != (uint64_t)-1 && + res_reserve.mem_pref_64 != (uint64_t)-1) { error_setg(errp, "PCI resource reserve cap: PREF32 and PREF64 conflict"); return -EINVAL; } - if (mem_non_pref_reserve != (uint64_t)-1 && - mem_non_pref_reserve >= (1ULL << 32)) { + if (res_reserve.mem_non_pref != (uint64_t)-1 && + res_reserve.mem_non_pref >= (1ULL << 32)) { error_setg(errp, "PCI resource reserve cap: mem-reserve must be less than 4G"); return -EINVAL; } - if (mem_pref_32_reserve != (uint64_t)-1 && - mem_pref_32_reserve >= (1ULL << 32)) { + if (res_reserve.mem_pref_32 != (uint64_t)-1 && + res_reserve.mem_pref_32 >= (1ULL << 32)) { error_setg(errp, "PCI resource reserve cap: pref32-reserve must be less than 4G"); return -EINVAL; } - if (bus_reserve == (uint32_t)-1 && - io_reserve == (uint64_t)-1 && - mem_non_pref_reserve == (uint64_t)-1 && - mem_pref_32_reserve == (uint64_t)-1 && - mem_pref_64_reserve == (uint64_t)-1) { + if (res_reserve.bus == (uint32_t)-1 && + res_reserve.io == (uint64_t)-1 && + res_reserve.mem_non_pref == (uint64_t)-1 && + res_reserve.mem_pref_32 == (uint64_t)-1 && + res_reserve.mem_pref_64 == (uint64_t)-1) { return 0; } @@ -450,11 +446,11 @@ int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, PCIBridgeQemuCap cap = { .len = cap_len, .type = REDHAT_PCI_CAP_RESOURCE_RESERVE, - .bus_res = bus_reserve, - .io = io_reserve, - .mem = mem_non_pref_reserve, - .mem_pref_32 = mem_pref_32_reserve, - .mem_pref_64 = mem_pref_64_reserve + .bus_res = res_reserve.bus, + .io = res_reserve.io, + .mem = res_reserve.mem_non_pref, + .mem_pref_32 = res_reserve.mem_pref_32, + .mem_pref_64 = res_reserve.mem_pref_64 }; int offset = pci_add_capability(dev, PCI_CAP_ID_VNDR, diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 5eaa935cb5..9d64b2e12f 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "hw/pci/pci.h" +#include "hw/pci/pci_bridge.h" #include "hw/pci/pci_host.h" #include "hw/pci/pci_bus.h" #include "trace.h" @@ -50,9 +51,29 @@ static inline PCIDevice *pci_dev_find_by_addr(PCIBus *bus, uint32_t addr) return pci_find_device(bus, bus_num, devfn); } +static void pci_adjust_config_limit(PCIBus *bus, uint32_t *limit) +{ + if (*limit > PCI_CONFIG_SPACE_SIZE) { + if (!pci_bus_allows_extended_config_space(bus)) { + *limit = PCI_CONFIG_SPACE_SIZE; + return; + } + + if (!pci_bus_is_root(bus)) { + PCIDevice *bridge = pci_bridge_get_device(bus); + pci_adjust_config_limit(pci_get_bus(bridge), limit); + } + } +} + void pci_host_config_write_common(PCIDevice *pci_dev, uint32_t addr, uint32_t limit, uint32_t val, uint32_t len) { + pci_adjust_config_limit(pci_get_bus(pci_dev), &limit); + if (limit <= addr) { + return; + } + assert(len <= 4); /* non-zero functions are only exposed when function 0 is present, * allowing direct removal of unexposed functions. @@ -71,6 +92,11 @@ uint32_t pci_host_config_read_common(PCIDevice *pci_dev, uint32_t addr, { uint32_t ret; + pci_adjust_config_limit(pci_get_bus(pci_dev), &limit); + if (limit <= addr) { + return ~0x0; + } + assert(len <= 4); /* non-zero functions are only exposed when function 0 is present, * allowing direct removal of unexposed functions. diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 6c91bd44a0..cf1ca30f93 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -27,6 +27,7 @@ #include "hw/pci/msi.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pcie_regs.h" +#include "hw/pci/pcie_port.h" #include "qemu/range.h" //#define DEBUG_PCIE @@ -68,11 +69,12 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) pci_set_long(exp_cap + PCI_EXP_LNKCAP, (port << PCI_EXP_LNKCAP_PN_SHIFT) | PCI_EXP_LNKCAP_ASPMS_0S | - PCI_EXP_LNK_MLW_1 | - PCI_EXP_LNK_LS_25); + QEMU_PCI_EXP_LNKCAP_MLW(QEMU_PCI_EXP_LNK_X1) | + QEMU_PCI_EXP_LNKCAP_MLS(QEMU_PCI_EXP_LNK_2_5GT)); pci_set_word(exp_cap + PCI_EXP_LNKSTA, - PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25); + QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1) | + QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT)); if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, @@ -86,6 +88,76 @@ pcie_cap_v1_fill(PCIDevice *dev, uint8_t port, uint8_t type, uint8_t version) pci_set_word(cmask + PCI_EXP_LNKSTA, 0); } +static void pcie_cap_fill_slot_lnk(PCIDevice *dev) +{ + PCIESlot *s = (PCIESlot *)object_dynamic_cast(OBJECT(dev), TYPE_PCIE_SLOT); + uint8_t *exp_cap = dev->config + dev->exp.exp_cap; + + /* Skip anything that isn't a PCIESlot */ + if (!s) { + return; + } + + /* Clear and fill LNKCAP from what was configured above */ + pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP, + PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, + QEMU_PCI_EXP_LNKCAP_MLW(s->width) | + QEMU_PCI_EXP_LNKCAP_MLS(s->speed)); + + /* + * Link bandwidth notification is required for all root ports and + * downstream ports supporting links wider than x1 or multiple link + * speeds. + */ + if (s->width > QEMU_PCI_EXP_LNK_X1 || + s->speed > QEMU_PCI_EXP_LNK_2_5GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, + PCI_EXP_LNKCAP_LBNC); + } + + if (s->speed > QEMU_PCI_EXP_LNK_2_5GT) { + /* + * Hot-plug capable downstream ports and downstream ports supporting + * link speeds greater than 5GT/s must hardwire PCI_EXP_LNKCAP_DLLLARC + * to 1b. PCI_EXP_LNKCAP_DLLLARC implies PCI_EXP_LNKSTA_DLLLA, which + * we also hardwire to 1b here. 2.5GT/s hot-plug slots should also + * technically implement this, but it's not done here for compatibility. + */ + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP, + PCI_EXP_LNKCAP_DLLLARC); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA); + + /* + * Target Link Speed defaults to the highest link speed supported by + * the component. 2.5GT/s devices are permitted to hardwire to zero. + */ + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKCTL2, + PCI_EXP_LNKCTL2_TLS); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKCTL2, + QEMU_PCI_EXP_LNKCAP_MLS(s->speed) & + PCI_EXP_LNKCTL2_TLS); + } + + /* + * 2.5 & 5.0GT/s can be fully described by LNKCAP, but 8.0GT/s is + * actually a reference to the highest bit supported in this register. + * We assume the device supports all link speeds. + */ + if (s->speed > QEMU_PCI_EXP_LNK_5GT) { + pci_long_test_and_clear_mask(exp_cap + PCI_EXP_LNKCAP2, ~0U); + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_2_5GB | + PCI_EXP_LNKCAP2_SLS_5_0GB | + PCI_EXP_LNKCAP2_SLS_8_0GB); + if (s->speed > QEMU_PCI_EXP_LNK_8GT) { + pci_long_test_and_set_mask(exp_cap + PCI_EXP_LNKCAP2, + PCI_EXP_LNKCAP2_SLS_16_0GB); + } + } +} + int pcie_cap_init(PCIDevice *dev, uint8_t offset, uint8_t type, uint8_t port, Error **errp) @@ -107,6 +179,9 @@ int pcie_cap_init(PCIDevice *dev, uint8_t offset, /* Filling values common with v1 */ pcie_cap_v1_fill(dev, port, type, PCI_EXP_FLAGS_VER2); + /* Fill link speed and width options */ + pcie_cap_fill_slot_lnk(dev); + /* Filling v2 specific values */ pci_set_long(exp_cap + PCI_EXP_DEVCAP2, PCI_EXP_DEVCAP2_EFF | PCI_EXP_DEVCAP2_EETLPP); @@ -315,12 +390,11 @@ static void pcie_cap_slot_event(PCIDevice *dev, PCIExpressHotPlugEvent event) hotplug_event_notify(dev); } -static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev, - DeviceState *dev, - uint8_t **exp_cap, Error **errp) +static void pcie_cap_slot_plug_common(PCIDevice *hotplug_dev, DeviceState *dev, + Error **errp) { - *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; - uint16_t sltsta = pci_get_word(*exp_cap + PCI_EXP_SLTSTA); + uint8_t *exp_cap = hotplug_dev->config + hotplug_dev->exp.exp_cap; + uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA); PCIE_DEV_PRINTF(PCI_DEVICE(dev), "hotplug state: 0x%x\n", sltsta); if (sltsta & PCI_EXP_SLTSTA_EIS) { @@ -331,13 +405,18 @@ static void pcie_cap_slot_hotplug_common(PCIDevice *hotplug_dev, } } -void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp) +void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - uint8_t *exp_cap; - PCIDevice *pci_dev = PCI_DEVICE(dev); + pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, errp); +} - pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); +void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + PCIDevice *hotplug_pdev = PCI_DEVICE(hotplug_dev); + uint8_t *exp_cap = hotplug_pdev->config + hotplug_pdev->exp.exp_cap; + PCIDevice *pci_dev = PCI_DEVICE(dev); /* Don't send event when device is enabled during qemu machine creation: * it is present on boot, no hotplug event is necessary. We do send an @@ -345,6 +424,10 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, if (!dev->hotplugged) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); + if (pci_dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA); + } return; } @@ -355,24 +438,41 @@ void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, if (pci_get_function_0(pci_dev)) { pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); + if (pci_dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) { + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA); + } pcie_cap_slot_event(PCI_DEVICE(hotplug_dev), PCI_EXP_HP_EV_PDC | PCI_EXP_HP_EV_ABP); } } +void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + object_property_set_bool(OBJECT(dev), false, "realized", NULL); +} + static void pcie_unplug_device(PCIBus *bus, PCIDevice *dev, void *opaque) { + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(dev)); + + hotplug_handler_unplug(hotplug_ctrl, DEVICE(dev), &error_abort); object_unparent(OBJECT(dev)); } -void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { - uint8_t *exp_cap; + Error *local_err = NULL; PCIDevice *pci_dev = PCI_DEVICE(dev); PCIBus *bus = pci_get_bus(pci_dev); - pcie_cap_slot_hotplug_common(PCI_DEVICE(hotplug_dev), dev, &exp_cap, errp); + pcie_cap_slot_plug_common(PCI_DEVICE(hotplug_dev), dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } /* In case user cancel the operation of multi-function hot-add, * remove the function that is unexposed to guest individually, @@ -444,7 +544,7 @@ void pcie_cap_slot_init(PCIDevice *dev, uint16_t slot) dev->exp.hpev_notified = false; qbus_set_hotplug_handler(BUS(pci_bridge_get_sec_bus(PCI_BRIDGE(dev))), - DEVICE(dev), NULL); + OBJECT(dev), NULL); } void pcie_cap_slot_reset(PCIDevice *dev) @@ -531,6 +631,10 @@ void pcie_cap_slot_write_config(PCIDevice *dev, pci_word_test_and_clear_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDS); + if (dev->cap_present & QEMU_PCIE_LNKSTA_DLLLA) { + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_DLLLA); + } pci_word_test_and_set_mask(exp_cap + PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_PDC); } @@ -728,6 +832,52 @@ void pcie_add_capability(PCIDevice *dev, memset(dev->cmask + offset, 0xFF, size); } +/* + * Sync the PCIe Link Status negotiated speed and width of a bridge with the + * downstream device. If downstream device is not present, re-write with the + * Link Capability fields. If downstream device reports invalid width or + * speed, replace with minimum values (LnkSta fields are RsvdZ on VFs but such + * values interfere with PCIe native hotplug detecting new devices). Limit + * width and speed to bridge capabilities for compatibility. Use config_read + * to access the downstream device since it could be an assigned device with + * volatile link information. + */ +void pcie_sync_bridge_lnk(PCIDevice *bridge_dev) +{ + PCIBridge *br = PCI_BRIDGE(bridge_dev); + PCIBus *bus = pci_bridge_get_sec_bus(br); + PCIDevice *target = bus->devices[0]; + uint8_t *exp_cap = bridge_dev->config + bridge_dev->exp.exp_cap; + uint16_t lnksta, lnkcap = pci_get_word(exp_cap + PCI_EXP_LNKCAP); + + if (!target || !target->exp.exp_cap) { + lnksta = lnkcap; + } else { + lnksta = target->config_read(target, + target->exp.exp_cap + PCI_EXP_LNKSTA, + sizeof(lnksta)); + + if ((lnksta & PCI_EXP_LNKSTA_NLW) > (lnkcap & PCI_EXP_LNKCAP_MLW)) { + lnksta &= ~PCI_EXP_LNKSTA_NLW; + lnksta |= lnkcap & PCI_EXP_LNKCAP_MLW; + } else if (!(lnksta & PCI_EXP_LNKSTA_NLW)) { + lnksta |= QEMU_PCI_EXP_LNKSTA_NLW(QEMU_PCI_EXP_LNK_X1); + } + + if ((lnksta & PCI_EXP_LNKSTA_CLS) > (lnkcap & PCI_EXP_LNKCAP_SLS)) { + lnksta &= ~PCI_EXP_LNKSTA_CLS; + lnksta |= lnkcap & PCI_EXP_LNKCAP_SLS; + } else if (!(lnksta & PCI_EXP_LNKSTA_CLS)) { + lnksta |= QEMU_PCI_EXP_LNKSTA_CLS(QEMU_PCI_EXP_LNK_2_5GT); + } + } + + pci_word_test_and_clear_mask(exp_cap + PCI_EXP_LNKSTA, + PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW); + pci_word_test_and_set_mask(exp_cap + PCI_EXP_LNKSTA, lnksta & + (PCI_EXP_LNKSTA_CLS | PCI_EXP_LNKSTA_NLW)); +} + /************************************************************************** * pci express extended capability helper functions */ @@ -764,3 +914,41 @@ void pcie_ats_init(PCIDevice *dev, uint16_t offset) pci_set_word(dev->wmask + dev->exp.ats_cap + PCI_ATS_CTRL, 0x800f); } + +/* ACS (Access Control Services) */ +void pcie_acs_init(PCIDevice *dev, uint16_t offset) +{ + bool is_downstream = pci_is_express_downstream_port(dev); + uint16_t cap_bits = 0; + + /* For endpoints, only multifunction devs may have an ACS capability: */ + assert(is_downstream || + (dev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) || + PCI_FUNC(dev->devfn)); + + pcie_add_capability(dev, PCI_EXT_CAP_ID_ACS, PCI_ACS_VER, offset, + PCI_ACS_SIZEOF); + dev->exp.acs_cap = offset; + + if (is_downstream) { + /* + * Downstream ports must implement SV, TB, RR, CR, UF, and DT (with + * caveats on the latter four that we ignore for simplicity). + * Endpoints may also implement a subset of ACS capabilities, + * but these are optional if the endpoint does not support + * peer-to-peer between functions and thus omitted here. + */ + cap_bits = PCI_ACS_SV | PCI_ACS_TB | PCI_ACS_RR | + PCI_ACS_CR | PCI_ACS_UF | PCI_ACS_DT; + } + + pci_set_word(dev->config + offset + PCI_ACS_CAP, cap_bits); + pci_set_word(dev->wmask + offset + PCI_ACS_CTRL, cap_bits); +} + +void pcie_acs_reset(PCIDevice *dev) +{ + if (dev->exp.acs_cap) { + pci_set_word(dev->config + dev->exp.acs_cap + PCI_ACS_CTRL, 0); + } +} diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 6432b9ac1f..a30291ef54 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -154,8 +154,10 @@ static void pcie_slot_class_init(ObjectClass *oc, void *data) HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); dc->props = pcie_slot_props; - hc->plug = pcie_cap_slot_hotplug_cb; - hc->unplug_request = pcie_cap_slot_hot_unplug_request_cb; + hc->pre_plug = pcie_cap_slot_pre_plug_cb; + hc->plug = pcie_cap_slot_plug_cb; + hc->unplug = pcie_cap_slot_unplug_cb; + hc->unplug_request = pcie_cap_slot_unplug_request_cb; } static const TypeInfo pcie_slot_type_info = { diff --git a/hw/pci/shpc.c b/hw/pci/shpc.c index a8462d48bb..49bbb841bd 100644 --- a/hw/pci/shpc.c +++ b/hw/pci/shpc.c @@ -238,6 +238,7 @@ static void shpc_invalid_command(SHPCDevice *shpc) static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) { + HotplugHandler *hotplug_ctrl; int devfn; int pci_slot = SHPC_IDX_TO_PCI(slot); for (devfn = PCI_DEVFN(pci_slot, 0); @@ -245,6 +246,9 @@ static void shpc_free_devices_in_slot(SHPCDevice *shpc, int slot) ++devfn) { PCIDevice *affected_dev = shpc->sec_bus->devices[devfn]; if (affected_dev) { + hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(affected_dev)); + hotplug_handler_unplug(hotplug_ctrl, DEVICE(affected_dev), + &error_abort); object_unparent(OBJECT(affected_dev)); } } @@ -482,8 +486,8 @@ static const MemoryRegionOps shpc_mmio_ops = { .max_access_size = 4, }, }; -static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot, - SHPCDevice *shpc, Error **errp) +static void shpc_device_plug_common(PCIDevice *affected_dev, int *slot, + SHPCDevice *shpc, Error **errp) { int pci_slot = PCI_SLOT(affected_dev->devfn); *slot = SHPC_PCI_TO_IDX(pci_slot); @@ -497,7 +501,7 @@ static void shpc_device_hotplug_common(PCIDevice *affected_dev, int *slot, } } -void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, +void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { Error *local_err = NULL; @@ -505,7 +509,7 @@ void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, SHPCDevice *shpc = pci_hotplug_dev->shpc; int slot; - shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); + shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -540,8 +544,14 @@ void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, shpc_interrupt_update(pci_hotplug_dev); } -void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + object_property_set_bool(OBJECT(dev), false, "realized", NULL); +} + +void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) { Error *local_err = NULL; PCIDevice *pci_hotplug_dev = PCI_DEVICE(hotplug_dev); @@ -550,7 +560,7 @@ void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, uint8_t led; int slot; - shpc_device_hotplug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); + shpc_device_plug_common(PCI_DEVICE(dev), &slot, shpc, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -639,7 +649,7 @@ int shpc_init(PCIDevice *d, PCIBus *sec_bus, MemoryRegion *bar, shpc_cap_update_dword(d); memory_region_add_subregion(bar, offset, &shpc->mmio); - qbus_set_hotplug_handler(BUS(sec_bus), DEVICE(d), NULL); + qbus_set_hotplug_handler(BUS(sec_bus), OBJECT(d), NULL); d->cap_present |= QEMU_PCI_CAP_SHPC; return 0; @@ -688,8 +698,8 @@ void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int l) shpc_cap_update_dword(d); } -static int shpc_save(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int shpc_save(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { PCIDevice *d = container_of(pv, PCIDevice, shpc); qemu_put_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); @@ -697,7 +707,8 @@ static int shpc_save(QEMUFile *f, void *pv, size_t size, VMStateField *field, return 0; } -static int shpc_load(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int shpc_load(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { PCIDevice *d = container_of(pv, PCIDevice, shpc); int ret = qemu_get_buffer(f, d->shpc->config, SHPC_SIZEOF(d)); diff --git a/hw/pci/trace-events b/hw/pci/trace-events index f68c178afc..def4b3926d 100644 --- a/hw/pci/trace-events +++ b/hw/pci/trace-events @@ -1,12 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/pci/pci.c +# pci.c 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,0x%"PRIx64"+0x%"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,0x%"PRIx64"+0x%"PRIx64 -# hw/pci/pci_host.c +# pci_host.c 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/pci/msix.c +# msix.c msix_write_config(char *name, bool enabled, bool masked) "dev %s enabled %d masked %d" diff --git a/hw/pcmcia/Kconfig b/hw/pcmcia/Kconfig new file mode 100644 index 0000000000..41f2df9136 --- /dev/null +++ b/hw/pcmcia/Kconfig @@ -0,0 +1,2 @@ +config PCMCIA + bool diff --git a/hw/ppc/Kconfig b/hw/ppc/Kconfig new file mode 100644 index 0000000000..a3465155f0 --- /dev/null +++ b/hw/ppc/Kconfig @@ -0,0 +1,124 @@ +config PSERIES + bool + imply PCI_DEVICES + imply TEST_DEVICES + imply VIRTIO_VGA + select DIMM + select PCI + select SPAPR_VSCSI + select VFIO if LINUX # needed by spapr_pci_vfio.c + select XICS_SPAPR + select XIVE_SPAPR + select MSI_NONBROKEN + +config SPAPR_RNG + bool + default y + depends on PSERIES + +config POWERNV + bool + imply PCI_DEVICES + imply TEST_DEVICES + select ISA_IPMI_BT + select IPMI_LOCAL + select ISA_BUS + select MC146818RTC + select XICS + select XIVE + +config PPC405 + bool + select M48T59 + select PFLASH_CFI02 + select PPC4XX + select SERIAL + +config PPC440 + bool + imply PCI_DEVICES + imply TEST_DEVICES + imply E1000_PCI + select PCI_EXPRESS + select PPC4XX + select SERIAL + +config PPC4XX + bool + select BITBANG_I2C + select PCI + +config SAM460EX + bool + select PFLASH_CFI01 + select IDE_SII3112 + select M41T80 + select PPC440 + select SERIAL + select SM501 + select SMBUS_EEPROM + select USB_EHCI_SYSBUS + select USB_OHCI + +config PREP + bool + imply PCI_DEVICES + imply TEST_DEVICES + select CS4231A + select PREP_PCI + select I82378 + select LSI_SCSI_PCI + select M48T59 + select PC87312 + select RS6000_MC + +config RS6000_MC + bool + +config MAC_OLDWORLD + bool + imply PCI_DEVICES + imply SUNGEM + imply TEST_DEVICES + select ADB + select GRACKLE_PCI + select HEATHROW_PIC + select MACIO + +config MAC_NEWWORLD + bool + imply PCI_DEVICES + imply SUNGEM + imply TEST_DEVICES + select ADB + select MACIO + select MACIO_GPIO + select MAC_PMU + select UNIN_PCI + +config E500 + bool + imply AT24C + imply VIRTIO_PCI + select ETSEC + select OPENPIC + select PLATFORM_BUS + select PPCE500_PCI + select SERIAL + +config VIRTEX + bool + select PFLASH_CFI01 + select SERIAL + select XILINX + select XILINX_ETHLITE + +config XIVE + bool + depends on POWERNV || PSERIES + +config XIVE_SPAPR + bool + default y + depends on PSERIES + select XIVE diff --git a/hw/ppc/Makefile.objs b/hw/ppc/Makefile.objs index bcab6323b7..1111b218a0 100644 --- a/hw/ppc/Makefile.objs +++ b/hw/ppc/Makefile.objs @@ -3,8 +3,9 @@ obj-y += ppc.o ppc_booke.o fdt.o # IBM pSeries (sPAPR) obj-$(CONFIG_PSERIES) += spapr.o spapr_caps.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 spapr_rng.o -obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o +obj-$(CONFIG_PSERIES) += spapr_pci.o spapr_rtc.o spapr_drc.o +obj-$(CONFIG_PSERIES) += spapr_cpu_core.o spapr_ovec.o spapr_irq.o +obj-$(CONFIG_SPAPR_RNG) += spapr_rng.o # IBM PowerNV obj-$(CONFIG_POWERNV) += pnv.o pnv_xscom.o pnv_core.o pnv_lpc.o pnv_psi.o pnv_occ.o pnv_bmc.o ifeq ($(CONFIG_PCI)$(CONFIG_PSERIES)$(CONFIG_LINUX), yyy) @@ -12,20 +13,20 @@ obj-y += spapr_pci_vfio.o endif obj-$(CONFIG_PSERIES) += spapr_rtas_ddw.o # PowerPC 4xx boards -obj-y += ppc4xx_devs.o ppc405_uc.o -obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc405_boards.o -obj-$(CONFIG_PPC4XX) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o +obj-$(CONFIG_PPC405) += ppc405_boards.o ppc405_uc.o +obj-$(CONFIG_PPC440) += ppc440_bamboo.o ppc440_pcix.o ppc440_uc.o +obj-$(CONFIG_PPC4XX) += ppc4xx_pci.o ppc4xx_devs.o obj-$(CONFIG_SAM460EX) += sam460ex.o # PReP obj-$(CONFIG_PREP) += prep.o obj-$(CONFIG_PREP) += prep_systemio.o obj-${CONFIG_RS6000_MC} += rs6000_mc.o # OldWorld PowerMac -obj-$(CONFIG_MAC) += mac_oldworld.o +obj-$(CONFIG_MAC_OLDWORLD) += mac_oldworld.o # NewWorld PowerMac -obj-$(CONFIG_MAC) += mac_newworld.o +obj-$(CONFIG_MAC_NEWWORLD) += mac_newworld.o # e500 obj-$(CONFIG_E500) += e500.o mpc8544ds.o e500plat.o obj-$(CONFIG_E500) += mpc8544_guts.o ppce500_spin.o # PowerPC 440 Xilinx ML507 reference board. -obj-$(CONFIG_XILINX) += virtex_ml507.o +obj-$(CONFIG_VIRTEX) += virtex_ml507.o diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 7d19b1498c..beb2efd694 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -42,6 +42,7 @@ #include "qemu/error-report.h" #include "hw/platform-bus.h" #include "hw/net/fsl_etsec/etsec.h" +#include "hw/i2c/i2c.h" #define EPAPR_MAGIC (0x45504150) #define BINARY_DEVICE_TREE_FILE "mpc8544ds.dtb" @@ -63,7 +64,10 @@ #define MPC8544_PCI_REGS_SIZE 0x1000ULL #define MPC8544_UTIL_OFFSET 0xe0000ULL #define MPC8XXX_GPIO_OFFSET 0x000FF000ULL +#define MPC8544_I2C_REGS_OFFSET 0x3000ULL #define MPC8XXX_GPIO_IRQ 47 +#define MPC8544_I2C_IRQ 43 +#define RTC_REGS_OFFSET 0x68 struct boot_info { @@ -161,6 +165,39 @@ static void create_dt_mpc8xxx_gpio(void *fdt, const char *soc, const char *mpic) g_free(poweroff); } +static void dt_rtc_create(void *fdt, const char *i2c, const char *alias) +{ + int offset = RTC_REGS_OFFSET; + + gchar *rtc = g_strdup_printf("%s/rtc@%"PRIx32, i2c, offset); + qemu_fdt_add_subnode(fdt, rtc); + qemu_fdt_setprop_string(fdt, rtc, "compatible", "pericom,pt7c4338"); + qemu_fdt_setprop_cells(fdt, rtc, "reg", offset); + qemu_fdt_setprop_string(fdt, "/aliases", alias, rtc); + + g_free(rtc); +} + +static void dt_i2c_create(void *fdt, const char *soc, const char *mpic, + const char *alias) +{ + hwaddr mmio0 = MPC8544_I2C_REGS_OFFSET; + int irq0 = MPC8544_I2C_IRQ; + + gchar *i2c = g_strdup_printf("%s/i2c@%"PRIx64, soc, mmio0); + qemu_fdt_add_subnode(fdt, i2c); + qemu_fdt_setprop_string(fdt, i2c, "device_type", "i2c"); + qemu_fdt_setprop_string(fdt, i2c, "compatible", "fsl-i2c"); + qemu_fdt_setprop_cells(fdt, i2c, "reg", mmio0, 0x14); + qemu_fdt_setprop_cells(fdt, i2c, "cell-index", 0); + qemu_fdt_setprop_cells(fdt, i2c, "interrupts", irq0, 0x2); + qemu_fdt_setprop_phandle(fdt, i2c, "interrupt-parent", mpic); + qemu_fdt_setprop_string(fdt, "/aliases", alias, i2c); + + g_free(i2c); +} + + typedef struct PlatformDevtreeData { void *fdt; const char *mpic; @@ -464,6 +501,12 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, soc, mpic, "serial0", 0, true); } + /* i2c */ + dt_i2c_create(fdt, soc, mpic, "i2c"); + + dt_rtc_create(fdt, "i2c", "rtc"); + + gutil = g_strdup_printf("%s/global-utilities@%llx", soc, MPC8544_UTIL_OFFSET); qemu_fdt_add_subnode(fdt, gutil); @@ -685,7 +728,7 @@ static void ppce500_cpu_reset(void *opaque) } static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, - qemu_irq **irqs) + IrqLines *irqs) { DeviceState *dev; SysBusDevice *s; @@ -705,7 +748,7 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, k = 0; for (i = 0; i < smp_cpus; i++) { for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { - sysbus_connect_irq(s, k++, irqs[i][j]); + sysbus_connect_irq(s, k++, irqs[i].irq[j]); } } @@ -713,7 +756,7 @@ static DeviceState *ppce500_init_mpic_qemu(PPCE500MachineState *pms, } static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, - qemu_irq **irqs, Error **errp) + IrqLines *irqs, Error **errp) { Error *err = NULL; DeviceState *dev; @@ -742,7 +785,7 @@ static DeviceState *ppce500_init_mpic_kvm(const PPCE500MachineClass *pmc, static DeviceState *ppce500_init_mpic(PPCE500MachineState *pms, MemoryRegion *ccsr, - qemu_irq **irqs) + IrqLines *irqs) { MachineState *machine = MACHINE(pms); const PPCE500MachineClass *pmc = PPCE500_MACHINE_GET_CLASS(pms); @@ -806,15 +849,15 @@ void ppce500_init(MachineState *machine) /* irq num for pin INTA, INTB, INTC and INTD is 1, 2, 3 and * 4 respectively */ unsigned int pci_irq_nrs[PCI_NUM_PINS] = {1, 2, 3, 4}; - qemu_irq **irqs; + IrqLines *irqs; DeviceState *dev, *mpicdev; CPUPPCState *firstenv = NULL; MemoryRegion *ccsr_addr_space; SysBusDevice *s; PPCE500CCSRState *ccsr; + I2CBus *i2c; - irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - irqs[0] = g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + irqs = g_new0(IrqLines, smp_cpus); for (i = 0; i < smp_cpus; i++) { PowerPCCPU *cpu; CPUState *cs; @@ -834,10 +877,9 @@ void ppce500_init(MachineState *machine) firstenv = env; } - irqs[i] = irqs[0] + (i * OPENPIC_OUTPUT_NB); input = (qemu_irq *)env->irq_inputs; - irqs[i][OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; - irqs[i][OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; + irqs[i].irq[OPENPIC_OUTPUT_INT] = input[PPCE500_INPUT_INT]; + irqs[i].irq[OPENPIC_OUTPUT_CINT] = input[PPCE500_INPUT_CINT]; env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; @@ -889,6 +931,16 @@ void ppce500_init(MachineState *machine) 0, qdev_get_gpio_in(mpicdev, 42), 399193, serial_hd(1), DEVICE_BIG_ENDIAN); } + /* I2C */ + dev = qdev_create(NULL, "mpc-i2c"); + s = SYS_BUS_DEVICE(dev); + qdev_init_nofail(dev); + sysbus_connect_irq(s, 0, qdev_get_gpio_in(mpicdev, MPC8544_I2C_IRQ)); + memory_region_add_subregion(ccsr_addr_space, MPC8544_I2C_REGS_OFFSET, + sysbus_mmio_get_region(s, 0)); + i2c = (I2CBus *)qdev_get_child_bus(dev, "i2c"); + i2c_create_slave(i2c, "ds1338", RTC_REGS_OFFSET); + /* General Utility device */ dev = qdev_create(NULL, "mpc8544-guts"); @@ -990,17 +1042,19 @@ void ppce500_init(MachineState *machine) filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, payload_name); - payload_size = load_elf(filename, NULL, NULL, &bios_entry, &loadaddr, NULL, + payload_size = load_elf(filename, NULL, NULL, NULL, + &bios_entry, &loadaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (payload_size < 0) { /* * Hrm. No ELF image? Try a uImage, maybe someone is giving us an * ePAPR compliant kernel */ + loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; payload_size = load_uimage(filename, &bios_entry, &loadaddr, NULL, NULL, NULL); if (payload_size < 0) { - error_report("qemu: could not load firmware '%s'", filename); + error_report("could not load firmware '%s'", filename); exit(1); } } @@ -1056,7 +1110,7 @@ void ppce500_init(MachineState *machine) */ dt_base = (loadaddr + payload_size + DTC_LOAD_PAD) & ~DTC_PAD_MASK; if (dt_base + DTB_MAX_SIZE > ram_size) { - error_report("qemu: not enough memory for device tree"); + error_report("not enough memory for device tree"); exit(1); } diff --git a/hw/ppc/mac.h b/hw/ppc/mac.h index 41fd289e81..a741300ac9 100644 --- a/hw/ppc/mac.h +++ b/hw/ppc/mac.h @@ -86,32 +86,6 @@ typedef struct Core99MachineState { uint8_t via_config; } Core99MachineState; -/* MacIO */ -#define TYPE_MACIO_IDE "macio-ide" -#define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) - -typedef struct MACIOIDEState { - /*< private >*/ - SysBusDevice parent_obj; - /*< public >*/ - uint32_t channel; - qemu_irq real_ide_irq; - qemu_irq real_dma_irq; - qemu_irq ide_irq; - qemu_irq dma_irq; - - MemoryRegion mem; - IDEBus bus; - IDEDMA dma; - void *dbdma; - bool dma_active; - uint32_t timing_reg; - uint32_t irq_reg; -} MACIOIDEState; - -void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); -void macio_ide_register_dma(MACIOIDEState *ide); - /* Grackle PCI */ #define TYPE_GRACKLE_PCI_HOST_BRIDGE "grackle-pcihost" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2ca294664b..02d8559621 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -53,7 +53,6 @@ #include "hw/ppc/mac.h" #include "hw/input/adb.h" #include "hw/ppc/mac_dbdma.h" -#include "hw/timer/m48t59.h" #include "hw/pci/pci.h" #include "net/net.h" #include "sysemu/sysemu.h" @@ -64,6 +63,7 @@ #include "hw/ppc/openpic.h" #include "hw/ide.h" #include "hw/loader.h" +#include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" @@ -114,7 +114,7 @@ static void ppc_core99_init(MachineState *machine) PowerPCCPU *cpu = NULL; CPUPPCState *env = NULL; char *filename; - qemu_irq **openpic_irqs; + IrqLines *openpic_irqs; int linux_boot, i, j, k; MemoryRegion *ram = g_new(MemoryRegion, 1), *bios = g_new(MemoryRegion, 1); hwaddr kernel_base, initrd_base, cmdline_base = 0; @@ -126,8 +126,7 @@ static void ppc_core99_init(MachineState *machine) MACIOIDEState *macio_ide; BusState *adb_bus; MacIONVRAMState *nvr; - int bios_size, ndrv_size; - uint8_t *ndrv_file; + int bios_size; int ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; @@ -165,7 +164,7 @@ static void ppc_core99_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, NULL, NULL, NULL, + bios_size = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); @@ -188,7 +187,8 @@ static void ppc_core99_init(MachineState *machine) #endif kernel_base = KERNEL_LOAD_ADDR; - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + kernel_size = load_elf(kernel_filename, NULL, + translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) @@ -248,41 +248,37 @@ static void ppc_core99_init(MachineState *machine) memory_region_add_subregion(get_system_memory(), 0xf8000000, sysbus_mmio_get_region(s, 0)); - openpic_irqs = g_malloc0(smp_cpus * sizeof(qemu_irq *)); - openpic_irqs[0] = - g_malloc0(smp_cpus * sizeof(qemu_irq) * OPENPIC_OUTPUT_NB); + openpic_irqs = g_new0(IrqLines, smp_cpus); for (i = 0; i < smp_cpus; i++) { /* Mac99 IRQ connection between OpenPIC outputs pins * and PowerPC input pins */ switch (PPC_INPUT(env)) { case PPC_FLAGS_INPUT_6xx: - openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); - openpic_irqs[i][OPENPIC_OUTPUT_INT] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_CINT] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_MCK] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_MCK] = ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_MCP]; /* Not connected ? */ - openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; + openpic_irqs[i].irq[OPENPIC_OUTPUT_DEBUG] = NULL; /* Check this */ - openpic_irqs[i][OPENPIC_OUTPUT_RESET] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_RESET] = ((qemu_irq *)env->irq_inputs)[PPC6xx_INPUT_HRESET]; break; #if defined(TARGET_PPC64) case PPC_FLAGS_INPUT_970: - openpic_irqs[i] = openpic_irqs[0] + (i * OPENPIC_OUTPUT_NB); - openpic_irqs[i][OPENPIC_OUTPUT_INT] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_CINT] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_INT]; - openpic_irqs[i][OPENPIC_OUTPUT_MCK] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_MCK] = ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_MCP]; /* Not connected ? */ - openpic_irqs[i][OPENPIC_OUTPUT_DEBUG] = NULL; + openpic_irqs[i].irq[OPENPIC_OUTPUT_DEBUG] = NULL; /* Check this */ - openpic_irqs[i][OPENPIC_OUTPUT_RESET] = + openpic_irqs[i].irq[OPENPIC_OUTPUT_RESET] = ((qemu_irq *)env->irq_inputs)[PPC970_INPUT_HRESET]; break; #endif /* defined(TARGET_PPC64) */ @@ -299,9 +295,10 @@ static void ppc_core99_init(MachineState *machine) k = 0; for (i = 0; i < smp_cpus; i++) { for (j = 0; j < OPENPIC_OUTPUT_NB; j++) { - sysbus_connect_irq(s, k++, openpic_irqs[i][j]); + sysbus_connect_irq(s, k++, openpic_irqs[i].irq[j]); } } + g_free(openpic_irqs); if (PPC_INPUT(env) == PPC_FLAGS_INPUT_970) { /* 970 gets a U3 bus */ @@ -344,6 +341,7 @@ static void ppc_core99_init(MachineState *machine) /* Uninorth main bus */ dev = qdev_create(NULL, TYPE_UNI_NORTH_PCI_HOST_BRIDGE); + qdev_prop_set_uint32(dev, "ofw-addr", 0xf2000000); object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", &error_abort); qdev_init_nofail(dev); @@ -434,7 +432,7 @@ static void ppc_core99_init(MachineState *machine) } for (i = 0; i < nb_nics; i++) { - pci_nic_init_nofail(&nd_table[i], pci_bus, "ne2k_pci", NULL); + pci_nic_init_nofail(&nd_table[i], pci_bus, "sungem", NULL); } /* The NewWorld NVRAM is not located in the MacIO device */ @@ -454,7 +452,17 @@ static void ppc_core99_init(MachineState *machine) pmac_format_nvram_partition(nvr, 0x2000); /* No PCI init: the BIOS will do it */ - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); + dev = qdev_create(NULL, TYPE_FW_CFG_MEM); + fw_cfg = FW_CFG(dev); + qdev_prop_set_uint32(dev, "data_width", 1); + qdev_prop_set_bit(dev, "dma_enabled", false); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(fw_cfg), NULL); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, CFG_ADDR); + sysbus_mmio_map(s, 1, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); @@ -497,11 +505,10 @@ static void ppc_core99_init(MachineState *machine) /* MacOS NDRV VGA driver */ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, NDRV_VGA_FILENAME); if (filename) { - ndrv_size = get_image_size(filename); - if (ndrv_size != -1) { - ndrv_file = g_malloc(ndrv_size); - ndrv_size = load_image(filename, ndrv_file); + gchar *ndrv_file; + gsize ndrv_size; + if (g_file_get_contents(filename, &ndrv_file, &ndrv_size, NULL)) { fw_cfg_add_file(fw_cfg, "ndrv/qemu_vga.ndrv", ndrv_file, ndrv_size); } g_free(filename); @@ -510,7 +517,54 @@ static void ppc_core99_init(MachineState *machine) qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } -static int core99_kvm_type(const char *arg) +/* + * Implementation of an interface to adjust firmware path + * for the bootindex property handling. + */ +static char *core99_fw_dev_path(FWPathProvider *p, BusState *bus, + DeviceState *dev) +{ + PCIDevice *pci; + IDEBus *ide_bus; + IDEState *ide_s; + MACIOIDEState *macio_ide; + + if (!strcmp(object_get_typename(OBJECT(dev)), "macio-newworld")) { + pci = PCI_DEVICE(dev); + return g_strdup_printf("mac-io@%x", PCI_SLOT(pci->devfn)); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "macio-ide")) { + macio_ide = MACIO_IDE(dev); + return g_strdup_printf("ata-3@%x", macio_ide->addr); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-drive")) { + ide_bus = IDE_BUS(qdev_get_parent_bus(dev)); + ide_s = idebus_active_if(ide_bus); + + if (ide_s->drive_kind == IDE_CD) { + return g_strdup("cdrom"); + } + + return g_strdup("disk"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-hd")) { + return g_strdup("disk"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-cd")) { + return g_strdup("cdrom"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "virtio-blk-device")) { + return g_strdup("disk"); + } + + return NULL; +} +static int core99_kvm_type(MachineState *machine, const char *arg) { /* Always force PR KVM */ return 2; @@ -519,6 +573,7 @@ static int core99_kvm_type(const char *arg) static void core99_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); mc->desc = "Mac99 based PowerMAC"; mc->init = ppc_core99_init; @@ -532,6 +587,8 @@ static void core99_machine_class_init(ObjectClass *oc, void *data) #else mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("7400_v2.9"); #endif + mc->ignore_boot_device_suffixes = true; + fwc->get_dev_path = core99_fw_dev_path; } static char *core99_get_via_config(Object *obj, Error **errp) @@ -588,7 +645,11 @@ static const TypeInfo core99_machine_info = { .parent = TYPE_MACHINE, .class_init = core99_machine_class_init, .instance_init = core99_instance_init, - .instance_size = sizeof(Core99MachineState) + .instance_size = sizeof(Core99MachineState), + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_PATH_PROVIDER }, + { } + }, }; static void mac_machine_register_types(void) diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 064d7eb30a..460cbc7923 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -30,7 +30,6 @@ #include "hw/ppc/ppc.h" #include "mac.h" #include "hw/input/adb.h" -#include "hw/timer/m48t59.h" #include "sysemu/sysemu.h" #include "net/net.h" #include "hw/isa/isa.h" @@ -42,6 +41,7 @@ #include "hw/misc/macio/macio.h" #include "hw/ide.h" #include "hw/loader.h" +#include "hw/fw-path-provider.h" #include "elf.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" @@ -98,8 +98,7 @@ static void ppc_heathrow_init(MachineState *machine) SysBusDevice *s; DeviceState *dev, *pic_dev; BusState *adb_bus; - int bios_size, ndrv_size; - uint8_t *ndrv_file; + int bios_size; uint16_t ppc_boot_device; DriveInfo *hd[MAX_IDE_BUS * MAX_IDE_DEVS]; void *fw_cfg; @@ -140,7 +139,7 @@ static void ppc_heathrow_init(MachineState *machine) /* Load OpenBIOS (ELF) */ if (filename) { - bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL, + bios_size = load_elf(filename, NULL, 0, NULL, NULL, NULL, NULL, 1, PPC_ELF_MACHINE, 0, 0); g_free(filename); } else { @@ -161,7 +160,8 @@ static void ppc_heathrow_init(MachineState *machine) bswap_needed = 0; #endif kernel_base = KERNEL_LOAD_ADDR; - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + kernel_size = load_elf(kernel_filename, NULL, + translate_kernel_address, NULL, NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (kernel_size < 0) @@ -254,6 +254,7 @@ static void ppc_heathrow_init(MachineState *machine) /* Grackle PCI host bridge */ dev = qdev_create(NULL, TYPE_GRACKLE_PCI_HOST_BRIDGE); + qdev_prop_set_uint32(dev, "ofw-addr", 0x80000000); object_property_set_link(OBJECT(dev), OBJECT(pic_dev), "pic", &error_abort); qdev_init_nofail(dev); @@ -309,7 +310,17 @@ static void ppc_heathrow_init(MachineState *machine) /* No PCI init: the BIOS will do it */ - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); + dev = qdev_create(NULL, TYPE_FW_CFG_MEM); + fw_cfg = FW_CFG(dev); + qdev_prop_set_uint32(dev, "data_width", 1); + qdev_prop_set_bit(dev, "dma_enabled", false); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(fw_cfg), NULL); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, CFG_ADDR); + sysbus_mmio_map(s, 1, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); @@ -349,11 +360,10 @@ static void ppc_heathrow_init(MachineState *machine) /* MacOS NDRV VGA driver */ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, NDRV_VGA_FILENAME); if (filename) { - ndrv_size = get_image_size(filename); - if (ndrv_size != -1) { - ndrv_file = g_malloc(ndrv_size); - ndrv_size = load_image(filename, ndrv_file); + gchar *ndrv_file; + gsize ndrv_size; + if (g_file_get_contents(filename, &ndrv_file, &ndrv_size, NULL)) { fw_cfg_add_file(fw_cfg, "ndrv/qemu_vga.ndrv", ndrv_file, ndrv_size); } g_free(filename); @@ -362,7 +372,55 @@ static void ppc_heathrow_init(MachineState *machine) qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } -static int heathrow_kvm_type(const char *arg) +/* + * Implementation of an interface to adjust firmware path + * for the bootindex property handling. + */ +static char *heathrow_fw_dev_path(FWPathProvider *p, BusState *bus, + DeviceState *dev) +{ + PCIDevice *pci; + IDEBus *ide_bus; + IDEState *ide_s; + MACIOIDEState *macio_ide; + + if (!strcmp(object_get_typename(OBJECT(dev)), "macio-oldworld")) { + pci = PCI_DEVICE(dev); + return g_strdup_printf("mac-io@%x", PCI_SLOT(pci->devfn)); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "macio-ide")) { + macio_ide = MACIO_IDE(dev); + return g_strdup_printf("ata-3@%x", macio_ide->addr); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-drive")) { + ide_bus = IDE_BUS(qdev_get_parent_bus(dev)); + ide_s = idebus_active_if(ide_bus); + + if (ide_s->drive_kind == IDE_CD) { + return g_strdup("cdrom"); + } + + return g_strdup("disk"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-hd")) { + return g_strdup("disk"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-cd")) { + return g_strdup("cdrom"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "virtio-blk-device")) { + return g_strdup("disk"); + } + + return NULL; +} + +static int heathrow_kvm_type(MachineState *machine, const char *arg) { /* Always force PR KVM */ return 2; @@ -371,6 +429,7 @@ static int heathrow_kvm_type(const char *arg) static void heathrow_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); mc->desc = "Heathrow based PowerMAC"; mc->init = ppc_heathrow_init; @@ -384,12 +443,18 @@ static void heathrow_class_init(ObjectClass *oc, void *data) mc->kvm_type = heathrow_kvm_type; mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("750_v3.1"); mc->default_display = "std"; + mc->ignore_boot_device_suffixes = true; + fwc->get_dev_path = heathrow_fw_dev_path; } static const TypeInfo ppc_heathrow_machine_info = { .name = MACHINE_TYPE_NAME("g3beige"), .parent = TYPE_MACHINE, - .class_init = heathrow_class_init + .class_init = heathrow_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_PATH_PROVIDER }, + { } + }, }; static void ppc_heathrow_register_types(void) diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 346f5e7aed..dfb4ea5742 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -47,14 +47,16 @@ #include -#define FDT_MAX_SIZE 0x00100000 +#define FDT_MAX_SIZE (1 * MiB) #define FW_FILE_NAME "skiboot.lid" #define FW_LOAD_ADDR 0x0 -#define FW_MAX_SIZE 0x00400000 +#define FW_MAX_SIZE (4 * MiB) #define KERNEL_LOAD_ADDR 0x20000000 +#define KERNEL_MAX_SIZE (256 * MiB) #define INITRD_LOAD_ADDR 0x60000000 +#define INITRD_MAX_SIZE (256 * MiB) static const char *pnv_chip_core_typename(const PnvChip *o) { @@ -265,7 +267,7 @@ static void pnv_dt_icp(PnvChip *chip, void *fdt, uint32_t pir, g_free(reg); } -static void pnv_dt_chip(PnvChip *chip, void *fdt) +static void pnv_chip_power8_dt_populate(PnvChip *chip, void *fdt) { const char *typename = pnv_chip_core_typename(chip); size_t typesize = object_type_get_instance_size(typename); @@ -287,6 +289,27 @@ static void pnv_dt_chip(PnvChip *chip, void *fdt) } } +static void pnv_chip_power9_dt_populate(PnvChip *chip, void *fdt) +{ + const char *typename = pnv_chip_core_typename(chip); + size_t typesize = object_type_get_instance_size(typename); + int i; + + pnv_dt_xscom(chip, fdt, 0); + + for (i = 0; i < chip->nr_cores; i++) { + PnvCore *pnv_core = PNV_CORE(chip->cores + i * typesize); + + pnv_dt_core(chip, pnv_core, fdt); + } + + if (chip->ram_size) { + pnv_dt_memory(fdt, chip->chip_id, chip->ram_start, chip->ram_size); + } + + pnv_dt_lpc(chip, fdt, 0); +} + static void pnv_dt_rtc(ISADevice *d, void *fdt, int lpc_off) { uint32_t io_base = d->ioport_id; @@ -396,24 +419,12 @@ static int pnv_dt_isa_device(DeviceState *dev, void *opaque) return 0; } -static int pnv_chip_isa_offset(PnvChip *chip, void *fdt) -{ - char *name; - int offset; - - name = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", - (uint64_t) PNV_XSCOM_BASE(chip), PNV_XSCOM_LPC_BASE); - offset = fdt_path_offset(fdt, name); - g_free(name); - return offset; -} - /* The default LPC bus of a multichip system is on chip 0. It's * recognized by the firmware (skiboot) using a "primary" property. */ static void pnv_dt_isa(PnvMachineState *pnv, void *fdt) { - int isa_offset = pnv_chip_isa_offset(pnv->chips[0], fdt); + int isa_offset = fdt_path_offset(fdt, pnv->chips[0]->dt_isa_nodename); ForeachPopulateArgs args = { .fdt = fdt, .offset = isa_offset, @@ -427,6 +438,16 @@ static void pnv_dt_isa(PnvMachineState *pnv, void *fdt) &args); } +static void pnv_dt_power_mgt(void *fdt) +{ + int off; + + off = fdt_add_subnode(fdt, 0, "ibm,opal"); + off = fdt_add_subnode(fdt, off, "power-mgt"); + + _FDT(fdt_setprop_cell(fdt, off, "ibm,enabled-stop-levels", 0xc0000000)); +} + static void *pnv_dt_create(MachineState *machine) { const char plat_compat[] = "qemu,powernv\0ibm,powernv"; @@ -472,7 +493,7 @@ static void *pnv_dt_create(MachineState *machine) /* Populate device tree for each chip */ for (i = 0; i < pnv->num_chips; i++) { - pnv_dt_chip(pnv->chips[i], fdt); + PNV_CHIP_GET_CLASS(pnv->chips[i])->dt_populate(pnv->chips[i], fdt); } /* Populate ISA devices on chip 0 */ @@ -482,6 +503,11 @@ static void *pnv_dt_create(MachineState *machine) pnv_dt_bmc_sensors(pnv->bmc, fdt); } + /* Create an extra node for power management on Power9 */ + if (pnv_is_power9(pnv)) { + pnv_dt_power_mgt(fdt); + } + return fdt; } @@ -538,7 +564,8 @@ static ISABus *pnv_chip_power8nvl_isa_create(PnvChip *chip, Error **errp) static ISABus *pnv_chip_power9_isa_create(PnvChip *chip, Error **errp) { - return NULL; + Pnv9Chip *chip9 = PNV9_CHIP(chip); + return pnv_lpc_isa_create(&chip9->lpc, false, errp); } static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) @@ -546,6 +573,21 @@ static ISABus *pnv_isa_create(PnvChip *chip, Error **errp) return PNV_CHIP_GET_CLASS(chip)->isa_create(chip, errp); } +static void pnv_chip_power8_pic_print_info(PnvChip *chip, Monitor *mon) +{ + Pnv8Chip *chip8 = PNV8_CHIP(chip); + + ics_pic_print_info(&chip8->psi.ics, mon); +} + +static void pnv_chip_power9_pic_print_info(PnvChip *chip, Monitor *mon) +{ + Pnv9Chip *chip9 = PNV9_CHIP(chip); + + pnv_xive_pic_print_info(&chip9->xive, mon); + pnv_psi_pic_print_info(&chip9->psi, mon); +} + static void pnv_init(MachineState *machine) { PnvMachineState *pnv = PNV_MACHINE(machine); @@ -588,7 +630,7 @@ static void pnv_init(MachineState *machine) long kernel_size; kernel_size = load_image_targphys(machine->kernel_filename, - KERNEL_LOAD_ADDR, 0x2000000); + KERNEL_LOAD_ADDR, KERNEL_MAX_SIZE); if (kernel_size < 0) { error_report("Could not load kernel '%s'", machine->kernel_filename); @@ -600,7 +642,7 @@ static void pnv_init(MachineState *machine) if (machine->initrd_filename) { pnv->initrd_base = INITRD_LOAD_ADDR; pnv->initrd_size = load_image_targphys(machine->initrd_filename, - pnv->initrd_base, 0x10000000); /* 128MB max */ + pnv->initrd_base, INITRD_MAX_SIZE); if (pnv->initrd_size < 0) { error_report("Could not load initial ram disk '%s'", machine->initrd_filename); @@ -668,11 +710,21 @@ static uint32_t pnv_chip_core_pir_p8(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 7) | (core_id << 3); } -static Object *pnv_chip_power8_intc_create(PnvChip *chip, Object *child, - Error **errp) +static void pnv_chip_power8_intc_create(PnvChip *chip, PowerPCCPU *cpu, + Error **errp) { - return icp_create(child, TYPE_PNV_ICP, XICS_FABRIC(qdev_get_machine()), - errp); + Error *local_err = NULL; + Object *obj; + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + obj = icp_create(OBJECT(cpu), TYPE_PNV_ICP, XICS_FABRIC(qdev_get_machine()), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pnv_cpu->intc = obj; } /* @@ -690,10 +742,26 @@ static uint32_t pnv_chip_core_pir_p9(PnvChip *chip, uint32_t core_id) return (chip->chip_id << 8) | (core_id << 2); } -static Object *pnv_chip_power9_intc_create(PnvChip *chip, Object *child, - Error **errp) +static void pnv_chip_power9_intc_create(PnvChip *chip, PowerPCCPU *cpu, + Error **errp) { - return NULL; + Pnv9Chip *chip9 = PNV9_CHIP(chip); + Error *local_err = NULL; + Object *obj; + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + + /* + * The core creates its interrupt presenter but the XIVE interrupt + * controller object is initialized afterwards. Hopefully, it's + * only used at runtime. + */ + obj = xive_tctx_create(OBJECT(cpu), XIVE_ROUTER(&chip9->xive), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pnv_cpu->intc = obj; } /* Allowed core identifiers on a POWER8 Processor Chip : @@ -726,18 +794,18 @@ static void pnv_chip_power8_instance_init(Object *obj) { Pnv8Chip *chip8 = PNV8_CHIP(obj); - object_initialize(&chip8->psi, sizeof(chip8->psi), TYPE_PNV_PSI); - object_property_add_child(obj, "psi", OBJECT(&chip8->psi), NULL); + object_initialize_child(obj, "psi", &chip8->psi, sizeof(chip8->psi), + TYPE_PNV8_PSI, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip8->psi), "xics", OBJECT(qdev_get_machine()), &error_abort); - object_initialize(&chip8->lpc, sizeof(chip8->lpc), TYPE_PNV_LPC); - object_property_add_child(obj, "lpc", OBJECT(&chip8->lpc), NULL); + object_initialize_child(obj, "lpc", &chip8->lpc, sizeof(chip8->lpc), + TYPE_PNV8_LPC, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip8->lpc), "psi", OBJECT(&chip8->psi), &error_abort); - object_initialize(&chip8->occ, sizeof(chip8->occ), TYPE_PNV_OCC); - object_property_add_child(obj, "occ", OBJECT(&chip8->occ), NULL); + object_initialize_child(obj, "occ", &chip8->occ, sizeof(chip8->occ), + TYPE_PNV8_OCC, &error_abort, NULL); object_property_add_const_link(OBJECT(&chip8->occ), "psi", OBJECT(&chip8->psi), &error_abort); } @@ -779,6 +847,7 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); PnvChip *chip = PNV_CHIP(dev); Pnv8Chip *chip8 = PNV8_CHIP(dev); + Pnv8Psi *psi8 = &chip8->psi; Error *local_err = NULL; pcc->parent_realize(dev, &local_err); @@ -795,13 +864,18 @@ static void pnv_chip_power8_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } - pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, &chip8->psi.xscom_regs); + pnv_xscom_add_subregion(chip, PNV_XSCOM_PSIHB_BASE, + &PNV_PSI(psi8)->xscom_regs); /* Create LPC controller */ object_property_set_bool(OBJECT(&chip8->lpc), true, "realized", &error_fatal); pnv_xscom_add_subregion(chip, PNV_XSCOM_LPC_BASE, &chip8->lpc.xscom_regs); + chip->dt_isa_nodename = g_strdup_printf("/xscom@%" PRIx64 "/isa@%x", + (uint64_t) PNV_XSCOM_BASE(chip), + PNV_XSCOM_LPC_BASE); + /* Interrupt Management Area. This is the memory region holding * all the Interrupt Control Presenter (ICP) registers */ pnv_chip_icp_realize(chip8, &local_err); @@ -830,6 +904,8 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->isa_create = pnv_chip_power8_isa_create; + k->dt_populate = pnv_chip_power8_dt_populate; + k->pic_print_info = pnv_chip_power8_pic_print_info; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8E"; @@ -848,6 +924,8 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->isa_create = pnv_chip_power8_isa_create; + k->dt_populate = pnv_chip_power8_dt_populate; + k->pic_print_info = pnv_chip_power8_pic_print_info; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8"; @@ -866,6 +944,8 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) k->core_pir = pnv_chip_core_pir_p8; k->intc_create = pnv_chip_power8_intc_create; k->isa_create = pnv_chip_power8nvl_isa_create; + k->dt_populate = pnv_chip_power8_dt_populate; + k->pic_print_info = pnv_chip_power8_pic_print_info; k->xscom_base = 0x003fc0000000000ull; dc->desc = "PowerNV Chip POWER8NVL"; @@ -875,11 +955,65 @@ static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) static void pnv_chip_power9_instance_init(Object *obj) { + Pnv9Chip *chip9 = PNV9_CHIP(obj); + + object_initialize_child(obj, "xive", &chip9->xive, sizeof(chip9->xive), + TYPE_PNV_XIVE, &error_abort, NULL); + object_property_add_const_link(OBJECT(&chip9->xive), "chip", obj, + &error_abort); + + object_initialize_child(obj, "psi", &chip9->psi, sizeof(chip9->psi), + TYPE_PNV9_PSI, &error_abort, NULL); + object_property_add_const_link(OBJECT(&chip9->psi), "chip", obj, + &error_abort); + + object_initialize_child(obj, "lpc", &chip9->lpc, sizeof(chip9->lpc), + TYPE_PNV9_LPC, &error_abort, NULL); + object_property_add_const_link(OBJECT(&chip9->lpc), "psi", + OBJECT(&chip9->psi), &error_abort); + + object_initialize_child(obj, "occ", &chip9->occ, sizeof(chip9->occ), + TYPE_PNV9_OCC, &error_abort, NULL); + object_property_add_const_link(OBJECT(&chip9->occ), "psi", + OBJECT(&chip9->psi), &error_abort); +} + +static void pnv_chip_quad_realize(Pnv9Chip *chip9, Error **errp) +{ + PnvChip *chip = PNV_CHIP(chip9); + const char *typename = pnv_chip_core_typename(chip); + size_t typesize = object_type_get_instance_size(typename); + int i; + + chip9->nr_quads = DIV_ROUND_UP(chip->nr_cores, 4); + chip9->quads = g_new0(PnvQuad, chip9->nr_quads); + + for (i = 0; i < chip9->nr_quads; i++) { + char eq_name[32]; + PnvQuad *eq = &chip9->quads[i]; + PnvCore *pnv_core = PNV_CORE(chip->cores + (i * 4) * typesize); + int core_id = CPU_CORE(pnv_core)->core_id; + + object_initialize(eq, sizeof(*eq), TYPE_PNV_QUAD); + snprintf(eq_name, sizeof(eq_name), "eq[%d]", core_id); + + object_property_add_child(OBJECT(chip), eq_name, OBJECT(eq), + &error_fatal); + object_property_set_int(OBJECT(eq), core_id, "id", &error_fatal); + object_property_set_bool(OBJECT(eq), true, "realized", &error_fatal); + object_unref(OBJECT(eq)); + + pnv_xscom_add_subregion(chip, PNV9_XSCOM_EQ_BASE(eq->id), + &eq->xscom_regs); + } } static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) { PnvChipClass *pcc = PNV_CHIP_GET_CLASS(dev); + Pnv9Chip *chip9 = PNV9_CHIP(dev); + PnvChip *chip = PNV_CHIP(dev); + Pnv9Psi *psi9 = &chip9->psi; Error *local_err = NULL; pcc->parent_realize(dev, &local_err); @@ -887,6 +1021,61 @@ static void pnv_chip_power9_realize(DeviceState *dev, Error **errp) error_propagate(errp, local_err); return; } + + pnv_chip_quad_realize(chip9, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* XIVE interrupt controller (POWER9) */ + object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_IC_BASE(chip), + "ic-bar", &error_fatal); + object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_VC_BASE(chip), + "vc-bar", &error_fatal); + object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_PC_BASE(chip), + "pc-bar", &error_fatal); + object_property_set_int(OBJECT(&chip9->xive), PNV9_XIVE_TM_BASE(chip), + "tm-bar", &error_fatal); + object_property_set_bool(OBJECT(&chip9->xive), true, "realized", + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_XIVE_BASE, + &chip9->xive.xscom_regs); + + /* Processor Service Interface (PSI) Host Bridge */ + object_property_set_int(OBJECT(&chip9->psi), PNV9_PSIHB_BASE(chip), + "bar", &error_fatal); + object_property_set_bool(OBJECT(&chip9->psi), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_PSIHB_BASE, + &PNV_PSI(psi9)->xscom_regs); + + /* LPC */ + object_property_set_bool(OBJECT(&chip9->lpc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + memory_region_add_subregion(get_system_memory(), PNV9_LPCM_BASE(chip), + &chip9->lpc.xscom_regs); + + chip->dt_isa_nodename = g_strdup_printf("/lpcm-opb@%" PRIx64 "/lpc@0", + (uint64_t) PNV9_LPCM_BASE(chip)); + + /* Create the simplified OCC model */ + object_property_set_bool(OBJECT(&chip9->occ), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + pnv_xscom_add_subregion(chip, PNV9_XSCOM_OCC_BASE, &chip9->occ.xscom_regs); } static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) @@ -900,6 +1089,8 @@ static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) k->core_pir = pnv_chip_core_pir_p9; k->intc_create = pnv_chip_power9_intc_create; k->isa_create = pnv_chip_power9_isa_create; + k->dt_populate = pnv_chip_power9_dt_populate; + k->pic_print_info = pnv_chip_power9_pic_print_info; k->xscom_base = 0x00603fc00000000ull; dc->desc = "PowerNV Chip POWER9"; @@ -995,7 +1186,7 @@ static void pnv_chip_core_realize(PnvChip *chip, Error **errp) if (!pnv_chip_is_power9(chip)) { xscom_core_base = PNV_XSCOM_EX_BASE(core_hwid); } else { - xscom_core_base = PNV_XSCOM_P9_EC_BASE(core_hwid); + xscom_core_base = PNV9_XSCOM_EC_BASE(core_hwid); } pnv_xscom_add_subregion(chip, xscom_core_base, @@ -1070,27 +1261,11 @@ static void pnv_ics_resend(XICSFabric *xi) } } -static PowerPCCPU *ppc_get_vcpu_by_pir(int pir) -{ - CPUState *cs; - - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - - if (env->spr_cb[SPR_PIR].default_value == pir) { - return cpu; - } - } - - return NULL; -} - static ICPState *pnv_icp_get(XICSFabric *xi, int pir) { PowerPCCPU *cpu = ppc_get_vcpu_by_pir(pir); - return cpu ? ICP(cpu->intc) : NULL; + return cpu ? ICP(pnv_cpu_state(cpu)->intc) : NULL; } static void pnv_pic_print_info(InterruptStatsProvider *obj, @@ -1103,12 +1278,15 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - icp_pic_print_info(ICP(cpu->intc), mon); + if (pnv_chip_is_power9(pnv->chips[0])) { + xive_tctx_pic_print_info(XIVE_TCTX(pnv_cpu_state(cpu)->intc), mon); + } else { + icp_pic_print_info(ICP(pnv_cpu_state(cpu)->intc), mon); + } } for (i = 0; i < pnv->num_chips; i++) { - Pnv8Chip *chip8 = PNV8_CHIP(pnv->chips[i]); - ics_pic_print_info(&chip8->psi.ics, mon); + PNV_CHIP_GET_CLASS(pnv->chips[i])->pic_print_info(pnv->chips[i], mon); } } diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index 9750464bf4..5feeed6bc4 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -60,8 +60,8 @@ static void pnv_cpu_reset(void *opaque) #define PNV_XSCOM_EX_DTS_RESULT0 0x50000 #define PNV_XSCOM_EX_DTS_RESULT1 0x50001 -static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, - unsigned int width) +static uint64_t pnv_core_power8_xscom_read(void *opaque, hwaddr addr, + unsigned int width) { uint32_t offset = addr >> 3; uint64_t val = 0; @@ -82,16 +82,74 @@ static uint64_t pnv_core_xscom_read(void *opaque, hwaddr addr, return val; } -static void pnv_core_xscom_write(void *opaque, hwaddr addr, uint64_t val, - unsigned int width) +static void pnv_core_power8_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int width) { qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n", addr); } -static const MemoryRegionOps pnv_core_xscom_ops = { - .read = pnv_core_xscom_read, - .write = pnv_core_xscom_write, +static const MemoryRegionOps pnv_core_power8_xscom_ops = { + .read = pnv_core_power8_xscom_read, + .write = pnv_core_power8_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + + +/* + * POWER9 core controls + */ +#define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP 0xf010d +#define PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR 0xf010a + +static uint64_t pnv_core_power9_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = 0; + + /* The result should be 38 C */ + switch (offset) { + case PNV_XSCOM_EX_DTS_RESULT0: + val = 0x26f024f023f0000ull; + break; + case PNV_XSCOM_EX_DTS_RESULT1: + val = 0x24f000000000000ull; + break; + case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP: + case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR: + val = 0x0; + break; + default: + qemu_log_mask(LOG_UNIMP, "Warning: reading reg=0x%" HWADDR_PRIx "\n", + addr); + } + + return val; +} + +static void pnv_core_power9_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int width) +{ + uint32_t offset = addr >> 3; + + switch (offset) { + case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_HYP: + case PNV9_XSCOM_EC_PPM_SPECIAL_WKUP_OTR: + break; + default: + qemu_log_mask(LOG_UNIMP, "Warning: writing to reg=0x%" HWADDR_PRIx "\n", + addr); + } +} + +static const MemoryRegionOps pnv_core_power9_xscom_ops = { + .read = pnv_core_power9_xscom_read, + .write = pnv_core_power9_xscom_write, .valid.min_access_size = 8, .valid.max_access_size = 8, .impl.min_access_size = 8, @@ -114,7 +172,7 @@ static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp) return; } - cpu->intc = pcc->intc_create(chip, OBJECT(cpu), &local_err); + pcc->intc_create(chip, cpu, &local_err); if (local_err) { error_propagate(errp, local_err); return; @@ -138,6 +196,7 @@ static void pnv_realize_vcpu(PowerPCCPU *cpu, PnvChip *chip, Error **errp) static void pnv_core_realize(DeviceState *dev, Error **errp) { PnvCore *pc = PNV_CORE(OBJECT(dev)); + PnvCoreClass *pcc = PNV_CORE_GET_CLASS(pc); CPUCore *cc = CPU_CORE(OBJECT(dev)); const char *typename = pnv_core_cpu_typename(pc); Error *local_err = NULL; @@ -148,14 +207,17 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) chip = object_property_get_link(OBJECT(dev), "chip", &local_err); if (!chip) { - error_propagate(errp, local_err); - error_prepend(errp, "required link 'chip' not found: "); + error_propagate_prepend(errp, local_err, + "required link 'chip' not found: "); return; } pc->threads = g_new(PowerPCCPU *, cc->nr_threads); for (i = 0; i < cc->nr_threads; i++) { + PowerPCCPU *cpu; + obj = object_new(typename); + cpu = POWERPC_CPU(obj); pc->threads[i] = POWERPC_CPU(obj); @@ -163,6 +225,9 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) object_property_add_child(OBJECT(pc), name, obj, &error_abort); object_property_add_alias(obj, "core-pir", OBJECT(pc), "pir", &error_abort); + + cpu->machine_data = g_new0(PnvCPUState, 1); + object_unref(obj); } @@ -174,7 +239,7 @@ static void pnv_core_realize(DeviceState *dev, Error **errp) } snprintf(name, sizeof(name), "xscom-core.%d", cc->core_id); - pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), &pnv_core_xscom_ops, + pnv_xscom_region_init(&pc->xscom_regs, OBJECT(dev), pcc->xscom_ops, pc, name, PNV_XSCOM_EX_SIZE); return; @@ -189,9 +254,13 @@ err: static void pnv_unrealize_vcpu(PowerPCCPU *cpu) { + PnvCPUState *pnv_cpu = pnv_cpu_state(cpu); + qemu_unregister_reset(pnv_cpu_reset, cpu); - object_unparent(cpu->intc); + object_unparent(OBJECT(pnv_cpu_state(cpu)->intc)); cpu_remove_sync(CPU(cpu)); + cpu->machine_data = NULL; + g_free(pnv_cpu); object_unparent(OBJECT(cpu)); } @@ -212,6 +281,20 @@ static Property pnv_core_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void pnv_core_power8_class_init(ObjectClass *oc, void *data) +{ + PnvCoreClass *pcc = PNV_CORE_CLASS(oc); + + pcc->xscom_ops = &pnv_core_power8_xscom_ops; +} + +static void pnv_core_power9_class_init(ObjectClass *oc, void *data) +{ + PnvCoreClass *pcc = PNV_CORE_CLASS(oc); + + pcc->xscom_ops = &pnv_core_power9_xscom_ops; +} + static void pnv_core_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -221,10 +304,11 @@ static void pnv_core_class_init(ObjectClass *oc, void *data) dc->props = pnv_core_properties; } -#define DEFINE_PNV_CORE_TYPE(cpu_model) \ +#define DEFINE_PNV_CORE_TYPE(family, cpu_model) \ { \ .parent = TYPE_PNV_CORE, \ .name = PNV_CORE_TYPE_NAME(cpu_model), \ + .class_init = pnv_core_##family##_class_init, \ } static const TypeInfo pnv_core_infos[] = { @@ -236,10 +320,97 @@ static const TypeInfo pnv_core_infos[] = { .class_init = pnv_core_class_init, .abstract = true, }, - DEFINE_PNV_CORE_TYPE("power8e_v2.1"), - DEFINE_PNV_CORE_TYPE("power8_v2.0"), - DEFINE_PNV_CORE_TYPE("power8nvl_v1.0"), - DEFINE_PNV_CORE_TYPE("power9_v2.0"), + DEFINE_PNV_CORE_TYPE(power8, "power8e_v2.1"), + DEFINE_PNV_CORE_TYPE(power8, "power8_v2.0"), + DEFINE_PNV_CORE_TYPE(power8, "power8nvl_v1.0"), + DEFINE_PNV_CORE_TYPE(power9, "power9_v2.0"), }; DEFINE_TYPES(pnv_core_infos) + +/* + * POWER9 Quads + */ + +#define P9X_EX_NCU_SPEC_BAR 0x11010 + +static uint64_t pnv_quad_xscom_read(void *opaque, hwaddr addr, + unsigned int width) +{ + uint32_t offset = addr >> 3; + uint64_t val = -1; + + switch (offset) { + case P9X_EX_NCU_SPEC_BAR: + case P9X_EX_NCU_SPEC_BAR + 0x400: /* Second EX */ + val = 0; + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__, + offset); + } + + return val; +} + +static void pnv_quad_xscom_write(void *opaque, hwaddr addr, uint64_t val, + unsigned int width) +{ + uint32_t offset = addr >> 3; + + switch (offset) { + case P9X_EX_NCU_SPEC_BAR: + case P9X_EX_NCU_SPEC_BAR + 0x400: /* Second EX */ + break; + default: + qemu_log_mask(LOG_UNIMP, "%s: writing @0x%08x\n", __func__, + offset); + } +} + +static const MemoryRegionOps pnv_quad_xscom_ops = { + .read = pnv_quad_xscom_read, + .write = pnv_quad_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_quad_realize(DeviceState *dev, Error **errp) +{ + PnvQuad *eq = PNV_QUAD(dev); + char name[32]; + + snprintf(name, sizeof(name), "xscom-quad.%d", eq->id); + pnv_xscom_region_init(&eq->xscom_regs, OBJECT(dev), &pnv_quad_xscom_ops, + eq, name, PNV9_XSCOM_EQ_SIZE); +} + +static Property pnv_quad_properties[] = { + DEFINE_PROP_UINT32("id", PnvQuad, id, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void pnv_quad_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + + dc->realize = pnv_quad_realize; + dc->props = pnv_quad_properties; +} + +static const TypeInfo pnv_quad_info = { + .name = TYPE_PNV_QUAD, + .parent = TYPE_DEVICE, + .instance_size = sizeof(PnvQuad), + .class_init = pnv_quad_class_init, +}; + +static void pnv_core_register_types(void) +{ + type_register_static(&pnv_quad_info); +} + +type_init(pnv_core_register_types) diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d7721320a2..641e2046db 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -39,6 +39,8 @@ enum { }; /* OPB Master LS registers */ +#define OPB_MASTER_LS_ROUTE0 0x8 +#define OPB_MASTER_LS_ROUTE1 0xC #define OPB_MASTER_LS_IRQ_STAT 0x50 #define OPB_MASTER_IRQ_LPC 0x00000800 #define OPB_MASTER_LS_IRQ_MASK 0x54 @@ -89,10 +91,11 @@ enum { #define LPC_FW_OPB_SIZE 0x10000000 #define LPC_OPB_REGS_OPB_ADDR 0xc0010000 -#define LPC_OPB_REGS_OPB_SIZE 0x00002000 +#define LPC_OPB_REGS_OPB_SIZE 0x00000060 +#define LPC_OPB_REGS_OPBA_ADDR 0xc0011000 +#define LPC_OPB_REGS_OPBA_SIZE 0x00000008 #define LPC_HC_REGS_OPB_ADDR 0xc0012000 -#define LPC_HC_REGS_OPB_SIZE 0x00001000 - +#define LPC_HC_REGS_OPB_SIZE 0x00000100 static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) { @@ -117,6 +120,100 @@ static int pnv_lpc_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) return 0; } +/* POWER9 only */ +int pnv_dt_lpc(PnvChip *chip, void *fdt, int root_offset) +{ + const char compat[] = "ibm,power9-lpcm-opb\0simple-bus"; + const char lpc_compat[] = "ibm,power9-lpc\0ibm,lpc"; + char *name; + int offset, lpcm_offset; + uint64_t lpcm_addr = PNV9_LPCM_BASE(chip); + uint32_t opb_ranges[8] = { 0, + cpu_to_be32(lpcm_addr >> 32), + cpu_to_be32((uint32_t)lpcm_addr), + cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(lpcm_addr >> 32), + cpu_to_be32(PNV9_LPCM_SIZE / 2), + cpu_to_be32(PNV9_LPCM_SIZE / 2), + }; + uint32_t opb_reg[4] = { cpu_to_be32(lpcm_addr >> 32), + cpu_to_be32((uint32_t)lpcm_addr), + cpu_to_be32(PNV9_LPCM_SIZE >> 32), + cpu_to_be32((uint32_t)PNV9_LPCM_SIZE), + }; + uint32_t reg[2]; + + /* + * OPB bus + */ + name = g_strdup_printf("lpcm-opb@%"PRIx64, lpcm_addr); + lpcm_offset = fdt_add_subnode(fdt, root_offset, name); + _FDT(lpcm_offset); + g_free(name); + + _FDT((fdt_setprop(fdt, lpcm_offset, "reg", opb_reg, sizeof(opb_reg)))); + _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#address-cells", 1))); + _FDT((fdt_setprop_cell(fdt, lpcm_offset, "#size-cells", 1))); + _FDT((fdt_setprop(fdt, lpcm_offset, "compatible", compat, sizeof(compat)))); + _FDT((fdt_setprop_cell(fdt, lpcm_offset, "ibm,chip-id", chip->chip_id))); + _FDT((fdt_setprop(fdt, lpcm_offset, "ranges", opb_ranges, + sizeof(opb_ranges)))); + + /* + * OPB Master registers + */ + name = g_strdup_printf("opb-master@%x", LPC_OPB_REGS_OPB_ADDR); + offset = fdt_add_subnode(fdt, lpcm_offset, name); + _FDT(offset); + g_free(name); + + reg[0] = cpu_to_be32(LPC_OPB_REGS_OPB_ADDR); + reg[1] = cpu_to_be32(LPC_OPB_REGS_OPB_SIZE); + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop_string(fdt, offset, "compatible", + "ibm,power9-lpcm-opb-master"))); + + /* + * OPB arbitrer registers + */ + name = g_strdup_printf("opb-arbitrer@%x", LPC_OPB_REGS_OPBA_ADDR); + offset = fdt_add_subnode(fdt, lpcm_offset, name); + _FDT(offset); + g_free(name); + + reg[0] = cpu_to_be32(LPC_OPB_REGS_OPBA_ADDR); + reg[1] = cpu_to_be32(LPC_OPB_REGS_OPBA_SIZE); + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop_string(fdt, offset, "compatible", + "ibm,power9-lpcm-opb-arbiter"))); + + /* + * LPC Host Controller registers + */ + name = g_strdup_printf("lpc-controller@%x", LPC_HC_REGS_OPB_ADDR); + offset = fdt_add_subnode(fdt, lpcm_offset, name); + _FDT(offset); + g_free(name); + + reg[0] = cpu_to_be32(LPC_HC_REGS_OPB_ADDR); + reg[1] = cpu_to_be32(LPC_HC_REGS_OPB_SIZE); + _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); + _FDT((fdt_setprop_string(fdt, offset, "compatible", + "ibm,power9-lpc-controller"))); + + name = g_strdup_printf("lpc@0"); + offset = fdt_add_subnode(fdt, lpcm_offset, name); + _FDT(offset); + g_free(name); + _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); + _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); + _FDT((fdt_setprop(fdt, offset, "compatible", lpc_compat, + sizeof(lpc_compat)))); + + return 0; +} + /* * These read/write handlers of the OPB address space should be common * with the P9 LPC Controller which uses direct MMIOs. @@ -155,9 +252,15 @@ static void pnv_lpc_do_eccb(PnvLpcController *lpc, uint64_t cmd) /* XXX Check for magic bits at the top, addr size etc... */ unsigned int sz = (cmd & ECCB_CTL_SZ_MASK) >> ECCB_CTL_SZ_LSH; uint32_t opb_addr = cmd & ECCB_CTL_ADDR_MASK; - uint8_t data[4]; + uint8_t data[8]; bool success; + if (sz > sizeof(data)) { + qemu_log_mask(LOG_GUEST_ERROR, + "ECCB: invalid operation at @0x%08x size %d\n", opb_addr, sz); + return; + } + if (cmd & ECCB_CTL_READ) { success = opb_read(lpc, opb_addr, data, sz); if (success) { @@ -235,9 +338,78 @@ static const MemoryRegionOps pnv_lpc_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static uint64_t pnv_lpc_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + uint64_t val = 0; + uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK; + MemTxResult result; + + switch (size) { + case 4: + val = address_space_ldl(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED, + &result); + break; + case 1: + val = address_space_ldub(&lpc->opb_as, opb_addr, MEMTXATTRS_UNSPECIFIED, + &result); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%" + HWADDR_PRIx " invalid size %d\n", addr, size); + return 0; + } + + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "OPB read failed at @0x%" + HWADDR_PRIx "\n", addr); + } + + return val; +} + +static void pnv_lpc_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvLpcController *lpc = PNV_LPC(opaque); + uint32_t opb_addr = addr & ECCB_CTL_ADDR_MASK; + MemTxResult result; + + switch (size) { + case 4: + address_space_stl(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED, + &result); + break; + case 1: + address_space_stb(&lpc->opb_as, opb_addr, val, MEMTXATTRS_UNSPECIFIED, + &result); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%" + HWADDR_PRIx " invalid size %d\n", addr, size); + return; + } + + if (result != MEMTX_OK) { + qemu_log_mask(LOG_GUEST_ERROR, "OPB write failed at @0x%" + HWADDR_PRIx "\n", addr); + } +} + +static const MemoryRegionOps pnv_lpc_mmio_ops = { + .read = pnv_lpc_mmio_read, + .write = pnv_lpc_mmio_write, + .impl = { + .min_access_size = 1, + .max_access_size = 4, + }, + .endianness = DEVICE_BIG_ENDIAN, +}; + static void pnv_lpc_eval_irqs(PnvLpcController *lpc) { bool lpc_to_opb_irq = false; + PnvLpcClass *plc = PNV_LPC_GET_CLASS(lpc); /* Update LPC controller to OPB line */ if (lpc->lpc_hc_irqser_ctrl & LPC_HC_IRQSER_EN) { @@ -260,7 +432,7 @@ static void pnv_lpc_eval_irqs(PnvLpcController *lpc) lpc->opb_irq_stat |= lpc->opb_irq_input & lpc->opb_irq_mask; /* Reflect the interrupt */ - pnv_psi_irq_set(lpc->psi, PSIHB_IRQ_LPC_I2C, lpc->opb_irq_stat != 0); + pnv_psi_irq_set(lpc->psi, plc->psi_irq, lpc->opb_irq_stat != 0); } static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) @@ -288,7 +460,7 @@ static uint64_t lpc_hc_read(void *opaque, hwaddr addr, unsigned size) val = lpc->lpc_hc_error_addr; break; default: - qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: 0x%" HWADDR_PRIx "\n", addr); } return val; @@ -326,7 +498,7 @@ static void lpc_hc_write(void *opaque, hwaddr addr, uint64_t val, case LPC_HC_ERROR_ADDRESS: break; default: - qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: Ox%" + qemu_log_mask(LOG_UNIMP, "LPC HC Unimplemented register: 0x%" HWADDR_PRIx "\n", addr); } } @@ -351,6 +523,12 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) uint64_t val = 0xfffffffffffffffful; switch (addr) { + case OPB_MASTER_LS_ROUTE0: /* TODO */ + val = lpc->opb_irq_route0; + break; + case OPB_MASTER_LS_ROUTE1: /* TODO */ + val = lpc->opb_irq_route1; + break; case OPB_MASTER_LS_IRQ_STAT: val = lpc->opb_irq_stat; break; @@ -364,7 +542,7 @@ static uint64_t opb_master_read(void *opaque, hwaddr addr, unsigned size) val = lpc->opb_irq_input; break; default: - qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" + qemu_log_mask(LOG_UNIMP, "OPBM: read on unimplemented register: 0x%" HWADDR_PRIx "\n", addr); } @@ -377,6 +555,12 @@ static void opb_master_write(void *opaque, hwaddr addr, PnvLpcController *lpc = opaque; switch (addr) { + case OPB_MASTER_LS_ROUTE0: /* TODO */ + lpc->opb_irq_route0 = val; + break; + case OPB_MASTER_LS_ROUTE1: /* TODO */ + lpc->opb_irq_route1 = val; + break; case OPB_MASTER_LS_IRQ_STAT: lpc->opb_irq_stat &= ~val; pnv_lpc_eval_irqs(lpc); @@ -393,8 +577,8 @@ static void opb_master_write(void *opaque, hwaddr addr, /* Read only */ break; default: - qemu_log_mask(LOG_UNIMP, "OPB MASTER Unimplemented register: Ox%" - HWADDR_PRIx "\n", addr); + qemu_log_mask(LOG_UNIMP, "OPBM: write on unimplemented register: 0x%" + HWADDR_PRIx " val=0x%08"PRIx64"\n", addr, val); } } @@ -412,11 +596,102 @@ static const MemoryRegionOps opb_master_ops = { }, }; +static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp) +{ + PnvLpcController *lpc = PNV_LPC(dev); + PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); + Error *local_err = NULL; + + plc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* P8 uses a XSCOM region for LPC registers */ + pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(lpc), + &pnv_lpc_xscom_ops, lpc, "xscom-lpc", + PNV_XSCOM_LPC_SIZE); +} + +static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); + PnvLpcClass *plc = PNV_LPC_CLASS(klass); + + dc->desc = "PowerNV LPC Controller POWER8"; + + xdc->dt_xscom = pnv_lpc_dt_xscom; + + plc->psi_irq = PSIHB_IRQ_LPC_I2C; + + device_class_set_parent_realize(dc, pnv_lpc_power8_realize, + &plc->parent_realize); +} + +static const TypeInfo pnv_lpc_power8_info = { + .name = TYPE_PNV8_LPC, + .parent = TYPE_PNV_LPC, + .instance_size = sizeof(PnvLpcController), + .class_init = pnv_lpc_power8_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_PNV_XSCOM_INTERFACE }, + { } + } +}; + +static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) +{ + PnvLpcController *lpc = PNV_LPC(dev); + PnvLpcClass *plc = PNV_LPC_GET_CLASS(dev); + Error *local_err = NULL; + + plc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* P9 uses a MMIO region */ + memory_region_init_io(&lpc->xscom_regs, OBJECT(lpc), &pnv_lpc_mmio_ops, + lpc, "lpcm", PNV9_LPCM_SIZE); +} + +static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvLpcClass *plc = PNV_LPC_CLASS(klass); + + dc->desc = "PowerNV LPC Controller POWER9"; + + plc->psi_irq = PSIHB9_IRQ_LPCHC; + + device_class_set_parent_realize(dc, pnv_lpc_power9_realize, + &plc->parent_realize); +} + +static const TypeInfo pnv_lpc_power9_info = { + .name = TYPE_PNV9_LPC, + .parent = TYPE_PNV_LPC, + .instance_size = sizeof(PnvLpcController), + .class_init = pnv_lpc_power9_class_init, +}; + static void pnv_lpc_realize(DeviceState *dev, Error **errp) { PnvLpcController *lpc = PNV_LPC(dev); Object *obj; - Error *error = NULL; + Error *local_err = NULL; + + obj = object_property_get_link(OBJECT(dev), "psi", &local_err); + if (!obj) { + error_propagate(errp, local_err); + error_prepend(errp, "required link 'psi' not found: "); + return; + } + /* The LPC controller needs PSI to generate interrupts */ + lpc->psi = PNV_PSI(obj); /* Reg inits */ lpc->lpc_hc_fw_rd_acc_size = LPC_HC_FW_RD_4B; @@ -456,46 +731,29 @@ static void pnv_lpc_realize(DeviceState *dev, Error **errp) "lpc-hc", LPC_HC_REGS_OPB_SIZE); memory_region_add_subregion(&lpc->opb_mr, LPC_HC_REGS_OPB_ADDR, &lpc->lpc_hc_regs); - - /* XScom region for LPC registers */ - pnv_xscom_region_init(&lpc->xscom_regs, OBJECT(dev), - &pnv_lpc_xscom_ops, lpc, "xscom-lpc", - PNV_XSCOM_LPC_SIZE); - - /* get PSI object from chip */ - obj = object_property_get_link(OBJECT(dev), "psi", &error); - if (!obj) { - error_setg(errp, "%s: required link 'psi' not found: %s", - __func__, error_get_pretty(error)); - return; - } - lpc->psi = PNV_PSI(obj); } static void pnv_lpc_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); - - xdc->dt_xscom = pnv_lpc_dt_xscom; dc->realize = pnv_lpc_realize; + dc->desc = "PowerNV LPC Controller"; } static const TypeInfo pnv_lpc_info = { .name = TYPE_PNV_LPC, .parent = TYPE_DEVICE, - .instance_size = sizeof(PnvLpcController), .class_init = pnv_lpc_class_init, - .interfaces = (InterfaceInfo[]) { - { TYPE_PNV_XSCOM_INTERFACE }, - { } - } + .class_size = sizeof(PnvLpcClass), + .abstract = true, }; static void pnv_lpc_register_types(void) { type_register_static(&pnv_lpc_info); + type_register_static(&pnv_lpc_power8_info); + type_register_static(&pnv_lpc_power9_info); } type_init(pnv_lpc_register_types) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 04880f26d6..fdd9296e1b 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -34,15 +34,17 @@ static void pnv_occ_set_misc(PnvOCC *occ, uint64_t val) { bool irq_state; + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); val &= 0xffff000000000000ull; occ->occmisc = val; irq_state = !!(val >> 63); - pnv_psi_irq_set(occ->psi, PSIHB_IRQ_OCC, irq_state); + pnv_psi_irq_set(occ->psi, poc->psi_irq, irq_state); } -static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t pnv_occ_power8_xscom_read(void *opaque, hwaddr addr, + unsigned size) { PnvOCC *occ = PNV_OCC(opaque); uint32_t offset = addr >> 3; @@ -54,13 +56,13 @@ static uint64_t pnv_occ_xscom_read(void *opaque, hwaddr addr, unsigned size) break; default: qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" - HWADDR_PRIx "\n", addr); + HWADDR_PRIx "\n", addr >> 3); } return val; } -static void pnv_occ_xscom_write(void *opaque, hwaddr addr, - uint64_t val, unsigned size) +static void pnv_occ_power8_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) { PnvOCC *occ = PNV_OCC(opaque); uint32_t offset = addr >> 3; @@ -77,13 +79,13 @@ static void pnv_occ_xscom_write(void *opaque, hwaddr addr, break; default: qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" - HWADDR_PRIx "\n", addr); + HWADDR_PRIx "\n", addr >> 3); } } -static const MemoryRegionOps pnv_occ_xscom_ops = { - .read = pnv_occ_xscom_read, - .write = pnv_occ_xscom_write, +static const MemoryRegionOps pnv_occ_power8_xscom_ops = { + .read = pnv_occ_power8_xscom_read, + .write = pnv_occ_power8_xscom_write, .valid.min_access_size = 8, .valid.max_access_size = 8, .impl.min_access_size = 8, @@ -91,27 +93,113 @@ static const MemoryRegionOps pnv_occ_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; +static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) +{ + PnvOCCClass *poc = PNV_OCC_CLASS(klass); + + poc->xscom_size = PNV_XSCOM_OCC_SIZE; + poc->xscom_ops = &pnv_occ_power8_xscom_ops; + poc->psi_irq = PSIHB_IRQ_OCC; +} + +static const TypeInfo pnv_occ_power8_type_info = { + .name = TYPE_PNV8_OCC, + .parent = TYPE_PNV_OCC, + .instance_size = sizeof(PnvOCC), + .class_init = pnv_occ_power8_class_init, +}; + +#define P9_OCB_OCI_OCCMISC 0x6080 +#define P9_OCB_OCI_OCCMISC_CLEAR 0x6081 +#define P9_OCB_OCI_OCCMISC_OR 0x6082 + + +static uint64_t pnv_occ_power9_xscom_read(void *opaque, hwaddr addr, + unsigned size) +{ + PnvOCC *occ = PNV_OCC(opaque); + uint32_t offset = addr >> 3; + uint64_t val = 0; + + switch (offset) { + case P9_OCB_OCI_OCCMISC: + val = occ->occmisc; + break; + default: + qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } + return val; +} + +static void pnv_occ_power9_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvOCC *occ = PNV_OCC(opaque); + uint32_t offset = addr >> 3; + + switch (offset) { + case P9_OCB_OCI_OCCMISC_CLEAR: + pnv_occ_set_misc(occ, 0); + break; + case P9_OCB_OCI_OCCMISC_OR: + pnv_occ_set_misc(occ, occ->occmisc | val); + break; + case P9_OCB_OCI_OCCMISC: + pnv_occ_set_misc(occ, val); + break; + default: + qemu_log_mask(LOG_UNIMP, "OCC Unimplemented register: Ox%" + HWADDR_PRIx "\n", addr >> 3); + } +} + +static const MemoryRegionOps pnv_occ_power9_xscom_ops = { + .read = pnv_occ_power9_xscom_read, + .write = pnv_occ_power9_xscom_write, + .valid.min_access_size = 8, + .valid.max_access_size = 8, + .impl.min_access_size = 8, + .impl.max_access_size = 8, + .endianness = DEVICE_BIG_ENDIAN, +}; + +static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) +{ + PnvOCCClass *poc = PNV_OCC_CLASS(klass); + + poc->xscom_size = PNV9_XSCOM_OCC_SIZE; + poc->xscom_ops = &pnv_occ_power9_xscom_ops; + poc->psi_irq = PSIHB9_IRQ_OCC; +} + +static const TypeInfo pnv_occ_power9_type_info = { + .name = TYPE_PNV9_OCC, + .parent = TYPE_PNV_OCC, + .instance_size = sizeof(PnvOCC), + .class_init = pnv_occ_power9_class_init, +}; static void pnv_occ_realize(DeviceState *dev, Error **errp) { PnvOCC *occ = PNV_OCC(dev); + PnvOCCClass *poc = PNV_OCC_GET_CLASS(occ); Object *obj; - Error *error = NULL; + Error *local_err = NULL; occ->occmisc = 0; - /* get PSI object from chip */ - obj = object_property_get_link(OBJECT(dev), "psi", &error); + obj = object_property_get_link(OBJECT(dev), "psi", &local_err); if (!obj) { - error_setg(errp, "%s: required link 'psi' not found: %s", - __func__, error_get_pretty(error)); + error_propagate(errp, local_err); + error_prepend(errp, "required link 'psi' not found: "); return; } occ->psi = PNV_PSI(obj); /* XScom region for OCC registers */ - pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), &pnv_occ_xscom_ops, - occ, "xscom-occ", PNV_XSCOM_OCC_SIZE); + pnv_xscom_region_init(&occ->xscom_regs, OBJECT(dev), poc->xscom_ops, + occ, "xscom-occ", poc->xscom_size); } static void pnv_occ_class_init(ObjectClass *klass, void *data) @@ -119,6 +207,7 @@ static void pnv_occ_class_init(ObjectClass *klass, void *data) DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = pnv_occ_realize; + dc->desc = "PowerNV OCC Controller"; } static const TypeInfo pnv_occ_type_info = { @@ -126,11 +215,15 @@ static const TypeInfo pnv_occ_type_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvOCC), .class_init = pnv_occ_class_init, + .class_size = sizeof(PnvOCCClass), + .abstract = true, }; static void pnv_occ_register_types(void) { type_register_static(&pnv_occ_type_info); + type_register_static(&pnv_occ_power8_type_info); + type_register_static(&pnv_occ_power9_type_info); } -type_init(pnv_occ_register_types) +type_init(pnv_occ_register_types); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 5b969127c3..5345c8389e 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -22,6 +22,7 @@ #include "target/ppc/cpu.h" #include "qemu/log.h" #include "qapi/error.h" +#include "monitor/monitor.h" #include "exec/address-spaces.h" @@ -114,12 +115,18 @@ #define PSIHB_BAR_MASK 0x0003fffffff00000ull #define PSIHB_FSPBAR_MASK 0x0003ffff00000000ull +#define PSIHB9_BAR_MASK 0x00fffffffff00000ull +#define PSIHB9_FSPBAR_MASK 0x00ffffff00000000ull + +#define PSIHB_REG(addr) (((addr) >> 3) + PSIHB_XSCOM_BAR) + static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) { + PnvPsiClass *ppc = PNV_PSI_GET_CLASS(psi); MemoryRegion *sysmem = get_system_memory(); uint64_t old = psi->regs[PSIHB_XSCOM_BAR]; - psi->regs[PSIHB_XSCOM_BAR] = bar & (PSIHB_BAR_MASK | PSIHB_BAR_EN); + psi->regs[PSIHB_XSCOM_BAR] = bar & (ppc->bar_mask | PSIHB_BAR_EN); /* Update MR, always remove it first */ if (old & PSIHB_BAR_EN) { @@ -128,7 +135,7 @@ static void pnv_psi_set_bar(PnvPsi *psi, uint64_t bar) /* Then add it back if needed */ if (bar & PSIHB_BAR_EN) { - uint64_t addr = bar & PSIHB_BAR_MASK; + uint64_t addr = bar & ppc->bar_mask; memory_region_add_subregion(sysmem, addr, &psi->regs_mr); } } @@ -152,7 +159,7 @@ static void pnv_psi_set_cr(PnvPsi *psi, uint64_t cr) static void pnv_psi_set_irsn(PnvPsi *psi, uint64_t val) { - ICSState *ics = &psi->ics; + ICSState *ics = &PNV8_PSI(psi)->ics; /* In this model we ignore the up/down enable bits for now * as SW doesn't use them (other than setting them at boot). @@ -205,9 +212,13 @@ static const uint64_t stat_bits[] = { [PSIHB_IRQ_EXTERNAL] = PSIHB_IRQ_STAT_EXT, }; -void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) +void pnv_psi_irq_set(PnvPsi *psi, int irq, bool state) +{ + PNV_PSI_GET_CLASS(psi)->irq_set(psi, irq, state); +} + +static void pnv_psi_power8_irq_set(PnvPsi *psi, int irq, bool state) { - ICSState *ics = &psi->ics; uint32_t xivr_reg; uint32_t stat_reg; uint32_t src; @@ -227,14 +238,14 @@ void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) /* TODO: optimization, check mask here. That means * re-evaluating when unmasking */ - qemu_irq_raise(ics->qirqs[src]); + qemu_irq_raise(psi->qirqs[src]); } else { psi->regs[stat_reg] &= ~stat_bits[irq]; /* FSP and PSI are muxed so don't lower if either is still set */ if (stat_reg != PSIHB_XSCOM_CR || !(psi->regs[stat_reg] & (PSIHB_CR_PSI_IRQ | PSIHB_CR_FSP_IRQ))) { - qemu_irq_lower(ics->qirqs[src]); + qemu_irq_lower(psi->qirqs[src]); } else { state = true; } @@ -261,7 +272,7 @@ void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state) static void pnv_psi_set_xivr(PnvPsi *psi, uint32_t reg, uint64_t val) { - ICSState *ics = &psi->ics; + ICSState *ics = &PNV8_PSI(psi)->ics; uint16_t server; uint8_t prio; uint8_t src; @@ -324,7 +335,7 @@ static uint64_t pnv_psi_reg_read(PnvPsi *psi, uint32_t offset, bool mmio) val = psi->regs[offset]; break; default: - qemu_log_mask(LOG_UNIMP, "PSI: read at Ox%" PRIx32 "\n", offset); + qemu_log_mask(LOG_UNIMP, "PSI: read at 0x%" PRIx32 "\n", offset); } return val; } @@ -383,7 +394,7 @@ static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val, pnv_psi_set_irsn(psi, val); break; default: - qemu_log_mask(LOG_UNIMP, "PSI: write at Ox%" PRIx32 "\n", offset); + qemu_log_mask(LOG_UNIMP, "PSI: write at 0x%" PRIx32 "\n", offset); } } @@ -393,13 +404,13 @@ static void pnv_psi_reg_write(PnvPsi *psi, uint32_t offset, uint64_t val, */ static uint64_t pnv_psi_mmio_read(void *opaque, hwaddr addr, unsigned size) { - return pnv_psi_reg_read(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, true); + return pnv_psi_reg_read(opaque, PSIHB_REG(addr), true); } static void pnv_psi_mmio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - pnv_psi_reg_write(opaque, (addr >> 3) + PSIHB_XSCOM_BAR, val, true); + pnv_psi_reg_write(opaque, PSIHB_REG(addr), val, true); } static const MemoryRegionOps psi_mmio_ops = { @@ -441,12 +452,21 @@ static const MemoryRegionOps pnv_psi_xscom_ops = { } }; -static void pnv_psi_init(Object *obj) +static void pnv_psi_reset(void *dev) { - PnvPsi *psi = PNV_PSI(obj); + PnvPsi *psi = PNV_PSI(dev); - object_initialize(&psi->ics, sizeof(psi->ics), TYPE_ICS_SIMPLE); - object_property_add_child(obj, "ics-psi", OBJECT(&psi->ics), NULL); + memset(psi->regs, 0x0, sizeof(psi->regs)); + + psi->regs[PSIHB_XSCOM_BAR] = psi->bar | PSIHB_BAR_EN; +} + +static void pnv_psi_power8_instance_init(Object *obj) +{ + Pnv8Psi *psi8 = PNV8_PSI(obj); + + object_initialize_child(obj, "ics-psi", &psi8->ics, sizeof(psi8->ics), + TYPE_ICS_SIMPLE, &error_abort, NULL); } static const uint8_t irq_to_xivr[] = { @@ -458,10 +478,10 @@ static const uint8_t irq_to_xivr[] = { PSIHB_XSCOM_XIVR_EXT, }; -static void pnv_psi_realize(DeviceState *dev, Error **errp) +static void pnv_psi_power8_realize(DeviceState *dev, Error **errp) { PnvPsi *psi = PNV_PSI(dev); - ICSState *ics = &psi->ics; + ICSState *ics = &PNV8_PSI(psi)->ics; Object *obj; Error *err = NULL; unsigned int i; @@ -491,6 +511,8 @@ static void pnv_psi_realize(DeviceState *dev, Error **errp) ics_set_irq_type(ics, i, true); } + psi->qirqs = qemu_allocate_irqs(ics_simple_set_irq, ics, ics->nr_irqs); + /* XSCOM region for PSI registers */ pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_xscom_ops, psi, "xscom-psi", PNV_XSCOM_PSIHB_SIZE); @@ -508,30 +530,38 @@ static void pnv_psi_realize(DeviceState *dev, Error **errp) psi->regs[xivr] = PSIHB_XIVR_PRIO_MSK | ((uint64_t) i << PSIHB_XIVR_SRC_SH); } + + qemu_register_reset(pnv_psi_reset, dev); } +static const char compat_p8[] = "ibm,power8-psihb-x\0ibm,psihb-x"; +static const char compat_p9[] = "ibm,power9-psihb-x\0ibm,psihb-x"; + static int pnv_psi_dt_xscom(PnvXScomInterface *dev, void *fdt, int xscom_offset) { - const char compat[] = "ibm,power8-psihb-x\0ibm,psihb-x"; + PnvPsiClass *ppc = PNV_PSI_GET_CLASS(dev); char *name; int offset; - uint32_t lpc_pcba = PNV_XSCOM_PSIHB_BASE; uint32_t reg[] = { - cpu_to_be32(lpc_pcba), - cpu_to_be32(PNV_XSCOM_PSIHB_SIZE) + cpu_to_be32(ppc->xscom_pcba), + cpu_to_be32(ppc->xscom_size) }; - name = g_strdup_printf("psihb@%x", lpc_pcba); + name = g_strdup_printf("psihb@%x", ppc->xscom_pcba); offset = fdt_add_subnode(fdt, xscom_offset, name); _FDT(offset); g_free(name); - _FDT((fdt_setprop(fdt, offset, "reg", reg, sizeof(reg)))); - - _FDT((fdt_setprop_cell(fdt, offset, "#address-cells", 2))); - _FDT((fdt_setprop_cell(fdt, offset, "#size-cells", 1))); - _FDT((fdt_setprop(fdt, offset, "compatible", compat, - sizeof(compat)))); + _FDT(fdt_setprop(fdt, offset, "reg", reg, sizeof(reg))); + _FDT(fdt_setprop_cell(fdt, offset, "#address-cells", 2)); + _FDT(fdt_setprop_cell(fdt, offset, "#size-cells", 1)); + if (ppc->chip_type == PNV_CHIP_POWER9) { + _FDT(fdt_setprop(fdt, offset, "compatible", compat_p9, + sizeof(compat_p9))); + } else { + _FDT(fdt_setprop(fdt, offset, "compatible", compat_p8, + sizeof(compat_p8))); + } return 0; } @@ -541,6 +571,331 @@ static Property pnv_psi_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + + dc->desc = "PowerNV PSI Controller POWER8"; + dc->realize = pnv_psi_power8_realize; + + ppc->chip_type = PNV_CHIP_POWER8; + ppc->xscom_pcba = PNV_XSCOM_PSIHB_BASE; + ppc->xscom_size = PNV_XSCOM_PSIHB_SIZE; + ppc->bar_mask = PSIHB_BAR_MASK; + ppc->irq_set = pnv_psi_power8_irq_set; +} + +static const TypeInfo pnv_psi_power8_info = { + .name = TYPE_PNV8_PSI, + .parent = TYPE_PNV_PSI, + .instance_size = sizeof(Pnv8Psi), + .instance_init = pnv_psi_power8_instance_init, + .class_init = pnv_psi_power8_class_init, +}; + + +/* Common registers */ + +#define PSIHB9_CR 0x20 +#define PSIHB9_SEMR 0x28 + +/* P9 registers */ + +#define PSIHB9_INTERRUPT_CONTROL 0x58 +#define PSIHB9_IRQ_METHOD PPC_BIT(0) +#define PSIHB9_IRQ_RESET PPC_BIT(1) +#define PSIHB9_ESB_CI_BASE 0x60 +#define PSIHB9_ESB_CI_VALID 1 +#define PSIHB9_ESB_NOTIF_ADDR 0x68 +#define PSIHB9_ESB_NOTIF_VALID 1 +#define PSIHB9_IVT_OFFSET 0x70 +#define PSIHB9_IVT_OFF_SHIFT 32 + +#define PSIHB9_IRQ_LEVEL 0x78 /* assertion */ +#define PSIHB9_IRQ_LEVEL_PSI PPC_BIT(0) +#define PSIHB9_IRQ_LEVEL_OCC PPC_BIT(1) +#define PSIHB9_IRQ_LEVEL_FSI PPC_BIT(2) +#define PSIHB9_IRQ_LEVEL_LPCHC PPC_BIT(3) +#define PSIHB9_IRQ_LEVEL_LOCAL_ERR PPC_BIT(4) +#define PSIHB9_IRQ_LEVEL_GLOBAL_ERR PPC_BIT(5) +#define PSIHB9_IRQ_LEVEL_TPM PPC_BIT(6) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ1 PPC_BIT(7) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ2 PPC_BIT(8) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ3 PPC_BIT(9) +#define PSIHB9_IRQ_LEVEL_LPC_SIRQ4 PPC_BIT(10) +#define PSIHB9_IRQ_LEVEL_SBE_I2C PPC_BIT(11) +#define PSIHB9_IRQ_LEVEL_DIO PPC_BIT(12) +#define PSIHB9_IRQ_LEVEL_PSU PPC_BIT(13) +#define PSIHB9_IRQ_LEVEL_I2C_C PPC_BIT(14) +#define PSIHB9_IRQ_LEVEL_I2C_D PPC_BIT(15) +#define PSIHB9_IRQ_LEVEL_I2C_E PPC_BIT(16) +#define PSIHB9_IRQ_LEVEL_SBE PPC_BIT(19) + +#define PSIHB9_IRQ_STAT 0x80 /* P bit */ +#define PSIHB9_IRQ_STAT_PSI PPC_BIT(0) +#define PSIHB9_IRQ_STAT_OCC PPC_BIT(1) +#define PSIHB9_IRQ_STAT_FSI PPC_BIT(2) +#define PSIHB9_IRQ_STAT_LPCHC PPC_BIT(3) +#define PSIHB9_IRQ_STAT_LOCAL_ERR PPC_BIT(4) +#define PSIHB9_IRQ_STAT_GLOBAL_ERR PPC_BIT(5) +#define PSIHB9_IRQ_STAT_TPM PPC_BIT(6) +#define PSIHB9_IRQ_STAT_LPC_SIRQ1 PPC_BIT(7) +#define PSIHB9_IRQ_STAT_LPC_SIRQ2 PPC_BIT(8) +#define PSIHB9_IRQ_STAT_LPC_SIRQ3 PPC_BIT(9) +#define PSIHB9_IRQ_STAT_LPC_SIRQ4 PPC_BIT(10) +#define PSIHB9_IRQ_STAT_SBE_I2C PPC_BIT(11) +#define PSIHB9_IRQ_STAT_DIO PPC_BIT(12) +#define PSIHB9_IRQ_STAT_PSU PPC_BIT(13) + +static void pnv_psi_notify(XiveNotifier *xf, uint32_t srcno) +{ + PnvPsi *psi = PNV_PSI(xf); + uint64_t notif_port = psi->regs[PSIHB_REG(PSIHB9_ESB_NOTIF_ADDR)]; + bool valid = notif_port & PSIHB9_ESB_NOTIF_VALID; + uint64_t notify_addr = notif_port & ~PSIHB9_ESB_NOTIF_VALID; + + uint32_t offset = + (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); + uint64_t lisn = cpu_to_be64(offset + srcno); + + if (valid) { + cpu_physical_memory_write(notify_addr, &lisn, sizeof(lisn)); + } +} + +static uint64_t pnv_psi_p9_mmio_read(void *opaque, hwaddr addr, unsigned size) +{ + PnvPsi *psi = PNV_PSI(opaque); + uint32_t reg = PSIHB_REG(addr); + uint64_t val = -1; + + switch (addr) { + case PSIHB9_CR: + case PSIHB9_SEMR: + /* FSP stuff */ + case PSIHB9_INTERRUPT_CONTROL: + case PSIHB9_ESB_CI_BASE: + case PSIHB9_ESB_NOTIF_ADDR: + case PSIHB9_IVT_OFFSET: + val = psi->regs[reg]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "PSI: read at 0x%" PRIx64 "\n", addr); + } + + return val; +} + +static void pnv_psi_p9_mmio_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPsi *psi = PNV_PSI(opaque); + Pnv9Psi *psi9 = PNV9_PSI(psi); + uint32_t reg = PSIHB_REG(addr); + MemoryRegion *sysmem = get_system_memory(); + + switch (addr) { + case PSIHB9_CR: + case PSIHB9_SEMR: + /* FSP stuff */ + break; + case PSIHB9_INTERRUPT_CONTROL: + if (val & PSIHB9_IRQ_RESET) { + device_reset(DEVICE(&psi9->source)); + } + psi->regs[reg] = val; + break; + + case PSIHB9_ESB_CI_BASE: + if (!(val & PSIHB9_ESB_CI_VALID)) { + if (psi->regs[reg] & PSIHB9_ESB_CI_VALID) { + memory_region_del_subregion(sysmem, &psi9->source.esb_mmio); + } + } else { + if (!(psi->regs[reg] & PSIHB9_ESB_CI_VALID)) { + memory_region_add_subregion(sysmem, + val & ~PSIHB9_ESB_CI_VALID, + &psi9->source.esb_mmio); + } + } + psi->regs[reg] = val; + break; + + case PSIHB9_ESB_NOTIF_ADDR: + psi->regs[reg] = val; + break; + case PSIHB9_IVT_OFFSET: + psi->regs[reg] = val; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "PSI: write at 0x%" PRIx64 "\n", addr); + } +} + +static const MemoryRegionOps pnv_psi_p9_mmio_ops = { + .read = pnv_psi_p9_mmio_read, + .write = pnv_psi_p9_mmio_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + }, +}; + +static uint64_t pnv_psi_p9_xscom_read(void *opaque, hwaddr addr, unsigned size) +{ + /* No read are expected */ + qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom read at 0x%" PRIx64 "\n", addr); + return -1; +} + +static void pnv_psi_p9_xscom_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + PnvPsi *psi = PNV_PSI(opaque); + + /* XSCOM is only used to set the PSIHB MMIO region */ + switch (addr >> 3) { + case PSIHB_XSCOM_BAR: + pnv_psi_set_bar(psi, val); + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, "PSI: xscom write at 0x%" PRIx64 "\n", + addr); + } +} + +static const MemoryRegionOps pnv_psi_p9_xscom_ops = { + .read = pnv_psi_p9_xscom_read, + .write = pnv_psi_p9_xscom_write, + .endianness = DEVICE_BIG_ENDIAN, + .valid = { + .min_access_size = 8, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 8, + .max_access_size = 8, + } +}; + +static void pnv_psi_power9_irq_set(PnvPsi *psi, int irq, bool state) +{ + uint64_t irq_method = psi->regs[PSIHB_REG(PSIHB9_INTERRUPT_CONTROL)]; + + if (irq > PSIHB9_NUM_IRQS) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: Unsupported irq %d\n", irq); + return; + } + + if (irq_method & PSIHB9_IRQ_METHOD) { + qemu_log_mask(LOG_GUEST_ERROR, "PSI: LSI IRQ method no supported\n"); + return; + } + + /* Update LSI levels */ + if (state) { + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] |= PPC_BIT(irq); + } else { + psi->regs[PSIHB_REG(PSIHB9_IRQ_LEVEL)] &= ~PPC_BIT(irq); + } + + qemu_set_irq(psi->qirqs[irq], state); +} + +static void pnv_psi_power9_reset(void *dev) +{ + Pnv9Psi *psi = PNV9_PSI(dev); + + pnv_psi_reset(dev); + + if (memory_region_is_mapped(&psi->source.esb_mmio)) { + memory_region_del_subregion(get_system_memory(), &psi->source.esb_mmio); + } +} + +static void pnv_psi_power9_instance_init(Object *obj) +{ + Pnv9Psi *psi = PNV9_PSI(obj); + + object_initialize_child(obj, "source", &psi->source, sizeof(psi->source), + TYPE_XIVE_SOURCE, &error_abort, NULL); +} + +static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) +{ + PnvPsi *psi = PNV_PSI(dev); + XiveSource *xsrc = &PNV9_PSI(psi)->source; + Error *local_err = NULL; + int i; + + /* This is the only device with 4k ESB pages */ + object_property_set_int(OBJECT(xsrc), XIVE_ESB_4K, "shift", + &error_fatal); + object_property_set_int(OBJECT(xsrc), PSIHB9_NUM_IRQS, "nr-irqs", + &error_fatal); + object_property_add_const_link(OBJECT(xsrc), "xive", OBJECT(psi), + &error_fatal); + object_property_set_bool(OBJECT(xsrc), true, "realized", &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + for (i = 0; i < xsrc->nr_irqs; i++) { + xive_source_irq_set_lsi(xsrc, i); + } + + psi->qirqs = qemu_allocate_irqs(xive_source_set_irq, xsrc, xsrc->nr_irqs); + + /* XSCOM region for PSI registers */ + pnv_xscom_region_init(&psi->xscom_regs, OBJECT(dev), &pnv_psi_p9_xscom_ops, + psi, "xscom-psi", PNV9_XSCOM_PSIHB_SIZE); + + /* MMIO region for PSI registers */ + memory_region_init_io(&psi->regs_mr, OBJECT(dev), &pnv_psi_p9_mmio_ops, psi, + "psihb", PNV9_PSIHB_SIZE); + + pnv_psi_set_bar(psi, psi->bar | PSIHB_BAR_EN); + + qemu_register_reset(pnv_psi_power9_reset, dev); +} + +static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PnvPsiClass *ppc = PNV_PSI_CLASS(klass); + XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); + + dc->desc = "PowerNV PSI Controller POWER9"; + dc->realize = pnv_psi_power9_realize; + + ppc->chip_type = PNV_CHIP_POWER9; + ppc->xscom_pcba = PNV9_XSCOM_PSIHB_BASE; + ppc->xscom_size = PNV9_XSCOM_PSIHB_SIZE; + ppc->bar_mask = PSIHB9_BAR_MASK; + ppc->irq_set = pnv_psi_power9_irq_set; + + xfc->notify = pnv_psi_notify; +} + +static const TypeInfo pnv_psi_power9_info = { + .name = TYPE_PNV9_PSI, + .parent = TYPE_PNV_PSI, + .instance_size = sizeof(Pnv9Psi), + .instance_init = pnv_psi_power9_instance_init, + .class_init = pnv_psi_power9_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_XIVE_NOTIFIER }, + { }, + }, +}; + static void pnv_psi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -548,7 +903,7 @@ static void pnv_psi_class_init(ObjectClass *klass, void *data) xdc->dt_xscom = pnv_psi_dt_xscom; - dc->realize = pnv_psi_realize; + dc->desc = "PowerNV PSI Controller"; dc->props = pnv_psi_properties; } @@ -556,8 +911,9 @@ static const TypeInfo pnv_psi_info = { .name = TYPE_PNV_PSI, .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PnvPsi), - .instance_init = pnv_psi_init, .class_init = pnv_psi_class_init, + .class_size = sizeof(PnvPsiClass), + .abstract = true, .interfaces = (InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } @@ -567,6 +923,20 @@ static const TypeInfo pnv_psi_info = { static void pnv_psi_register_types(void) { type_register_static(&pnv_psi_info); + type_register_static(&pnv_psi_power8_info); + type_register_static(&pnv_psi_power9_info); } -type_init(pnv_psi_register_types) +type_init(pnv_psi_register_types); + +void pnv_psi_pic_print_info(Pnv9Psi *psi9, Monitor *mon) +{ + PnvPsi *psi = PNV_PSI(psi9); + + uint32_t offset = + (psi->regs[PSIHB_REG(PSIHB9_IVT_OFFSET)] >> PSIHB9_IVT_OFF_SHIFT); + + monitor_printf(mon, "PSIHB Source %08x .. %08x\n", + offset, offset + psi9->source.nr_irqs - 1); + xive_source_pic_print_info(&psi9->source, offset, mon); +} diff --git a/hw/ppc/pnv_xscom.c b/hw/ppc/pnv_xscom.c index 46fae41f32..c285ef514e 100644 --- a/hw/ppc/pnv_xscom.c +++ b/hw/ppc/pnv_xscom.c @@ -64,11 +64,21 @@ static uint64_t xscom_read_default(PnvChip *chip, uint32_t pcba) switch (pcba) { case 0xf000f: return PNV_CHIP_GET_CLASS(chip)->chip_cfam_id; + case 0x18002: /* ECID2 */ + return 0; + case 0x1010c00: /* PIBAM FIR */ case 0x1010c03: /* PIBAM FIR MASK */ - case 0x2020007: /* ADU stuff */ - case 0x2020009: /* ADU stuff */ - case 0x202000f: /* ADU stuff */ + + /* P9 xscom reset */ + case 0x0090018: /* Receive status reg */ + case 0x0090012: /* log register */ + case 0x0090013: /* error register */ + + /* P8 xscom reset */ + case 0x2020007: /* ADU stuff, log register */ + case 0x2020009: /* ADU stuff, error register */ + case 0x202000f: /* ADU stuff, receive status register*/ return 0; case 0x2013f00: /* PBA stuff */ case 0x2013f01: /* PBA stuff */ @@ -100,9 +110,20 @@ static bool xscom_write_default(PnvChip *chip, uint32_t pcba, uint64_t val) case 0x1010c03: /* PIBAM FIR MASK */ case 0x1010c04: /* PIBAM FIR MASK */ case 0x1010c05: /* PIBAM FIR MASK */ - case 0x2020007: /* ADU stuff */ - case 0x2020009: /* ADU stuff */ - case 0x202000f: /* ADU stuff */ + /* P9 xscom reset */ + case 0x0090018: /* Receive status reg */ + case 0x0090012: /* log register */ + case 0x0090013: /* error register */ + + /* P8 xscom reset */ + case 0x2020007: /* ADU stuff, log register */ + case 0x2020009: /* ADU stuff, error register */ + case 0x202000f: /* ADU stuff, receive status register*/ + + case 0x2013028: /* CAPP stuff */ + case 0x201302a: /* CAPP stuff */ + case 0x2013801: /* CAPP stuff */ + case 0x2013802: /* CAPP stuff */ return true; default: return false; diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index ec4be25f49..ad20584f26 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -30,10 +30,8 @@ #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "sysemu/cpus.h" -#include "hw/timer/m48t59.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "hw/loader.h" #include "sysemu/kvm.h" #include "kvm_ppc.h" #include "trace.h" @@ -308,8 +306,106 @@ void ppcPOWER7_irq_init(PowerPCCPU *cpu) env->irq_inputs = (void **)qemu_allocate_irqs(&power7_set_irq, cpu, POWER7_INPUT_NB); } + +/* POWER9 internal IRQ controller */ +static void power9_set_irq(void *opaque, int pin, int level) +{ + PowerPCCPU *cpu = opaque; + CPUPPCState *env = &cpu->env; + + LOG_IRQ("%s: env %p pin %d level %d\n", __func__, + env, pin, level); + + switch (pin) { + case POWER9_INPUT_INT: + /* Level sensitive - active high */ + LOG_IRQ("%s: set the external IRQ state to %d\n", + __func__, level); + ppc_set_irq(cpu, PPC_INTERRUPT_EXT, level); + break; + case POWER9_INPUT_HINT: + /* Level sensitive - active high */ + LOG_IRQ("%s: set the external IRQ state to %d\n", + __func__, level); + ppc_set_irq(cpu, PPC_INTERRUPT_HVIRT, level); + break; + default: + /* Unknown pin - do nothing */ + LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin); + return; + } + if (level) { + env->irq_input_state |= 1 << pin; + } else { + env->irq_input_state &= ~(1 << pin); + } +} + +void ppcPOWER9_irq_init(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + + env->irq_inputs = (void **)qemu_allocate_irqs(&power9_set_irq, cpu, + POWER9_INPUT_NB); +} #endif /* defined(TARGET_PPC64) */ +void ppc40x_core_reset(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + target_ulong dbsr; + + qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC core\n"); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); + dbsr = env->spr[SPR_40x_DBSR]; + dbsr &= ~0x00000300; + dbsr |= 0x00000100; + env->spr[SPR_40x_DBSR] = dbsr; +} + +void ppc40x_chip_reset(PowerPCCPU *cpu) +{ + CPUPPCState *env = &cpu->env; + target_ulong dbsr; + + qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC chip\n"); + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); + /* XXX: TODO reset all internal peripherals */ + dbsr = env->spr[SPR_40x_DBSR]; + dbsr &= ~0x00000300; + dbsr |= 0x00000200; + env->spr[SPR_40x_DBSR] = dbsr; +} + +void ppc40x_system_reset(PowerPCCPU *cpu) +{ + qemu_log_mask(CPU_LOG_RESET, "Reset PowerPC system\n"); + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); +} + +void store_40x_dbcr0(CPUPPCState *env, uint32_t val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + switch ((val >> 28) & 0x3) { + case 0x0: + /* No action */ + break; + case 0x1: + /* Core reset */ + ppc40x_core_reset(cpu); + break; + case 0x2: + /* Chip reset */ + ppc40x_chip_reset(cpu); + break; + case 0x3: + /* System reset */ + ppc40x_system_reset(cpu); + break; + } +} + /* PowerPC 40x internal IRQ controller */ static void ppc40x_set_irq(void *opaque, int pin, int level) { @@ -648,11 +744,10 @@ bool ppc_decr_clear_on_delivery(CPUPPCState *env) return ((tb_env->flags & flags) == PPC_DECR_UNDERFLOW_TRIGGERED); } -static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) +static inline int64_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) { ppc_tb_t *tb_env = env->tb_env; - uint32_t decr; - int64_t diff; + int64_t decr, diff; diff = next - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); if (diff >= 0) { @@ -662,27 +757,49 @@ static inline uint32_t _cpu_ppc_load_decr(CPUPPCState *env, uint64_t next) } else { decr = -muldiv64(-diff, tb_env->decr_freq, NANOSECONDS_PER_SECOND); } - LOG_TB("%s: %08" PRIx32 "\n", __func__, decr); + LOG_TB("%s: %016" PRIx64 "\n", __func__, decr); return decr; } -uint32_t cpu_ppc_load_decr (CPUPPCState *env) +target_ulong cpu_ppc_load_decr(CPUPPCState *env) { ppc_tb_t *tb_env = env->tb_env; + uint64_t decr; if (kvm_enabled()) { return env->spr[SPR_DECR]; } - return _cpu_ppc_load_decr(env, tb_env->decr_next); + decr = _cpu_ppc_load_decr(env, tb_env->decr_next); + + /* + * If large decrementer is enabled then the decrementer is signed extened + * to 64 bits, otherwise it is a 32 bit value. + */ + if (env->spr[SPR_LPCR] & LPCR_LD) { + return decr; + } + return (uint32_t) decr; } -uint32_t cpu_ppc_load_hdecr (CPUPPCState *env) +target_ulong cpu_ppc_load_hdecr(CPUPPCState *env) { + PowerPCCPU *cpu = ppc_env_get_cpu(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); ppc_tb_t *tb_env = env->tb_env; + uint64_t hdecr; - return _cpu_ppc_load_decr(env, tb_env->hdecr_next); + hdecr = _cpu_ppc_load_decr(env, tb_env->hdecr_next); + + /* + * If we have a large decrementer (POWER9 or later) then hdecr is sign + * extended to 64 bits, otherwise it is 32 bits. + */ + if (pcc->lrg_decr_bits > 32) { + return hdecr; + } + return (uint32_t) hdecr; } uint64_t cpu_ppc_load_purr (CPUPPCState *env) @@ -722,7 +839,7 @@ static inline void cpu_ppc_hdecr_excp(PowerPCCPU *cpu) * interrupts in a PM state. Not only they don't cause a * wakeup but they also get effectively discarded. */ - if (!env->in_pm_state) { + if (!env->resume_as_sreset) { ppc_set_irq(cpu, PPC_INTERRUPT_HDECR, 1); } } @@ -736,13 +853,22 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, QEMUTimer *timer, void (*raise_excp)(void *), void (*lower_excp)(PowerPCCPU *), - uint32_t decr, uint32_t value) + target_ulong decr, target_ulong value, + int nr_bits) { CPUPPCState *env = &cpu->env; ppc_tb_t *tb_env = env->tb_env; uint64_t now, next; + bool negative; - LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__, + /* Truncate value to decr_width and sign extend for simplicity */ + value &= ((1ULL << nr_bits) - 1); + negative = !!(value & (1ULL << (nr_bits - 1))); + if (negative) { + value |= (0xFFFFFFFFULL << nr_bits); + } + + LOG_TB("%s: " TARGET_FMT_lx " => " TARGET_FMT_lx "\n", __func__, decr, value); if (kvm_enabled()) { @@ -764,15 +890,15 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, * an edge interrupt, so raise it here too. */ if ((value < 3) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && (value & 0x80000000)) || - ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && (value & 0x80000000) - && !(decr & 0x80000000))) { + ((tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL) && negative) || + ((tb_env->flags & PPC_DECR_UNDERFLOW_TRIGGERED) && negative + && !(decr & (1ULL << (nr_bits - 1))))) { (*raise_excp)(cpu); return; } /* On MSB level based systems a 0 for the MSB stops interrupt delivery */ - if (!(value & 0x80000000) && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { + if (!negative && (tb_env->flags & PPC_DECR_UNDERFLOW_LEVEL)) { (*lower_excp)(cpu); } @@ -785,21 +911,27 @@ static void __cpu_ppc_store_decr(PowerPCCPU *cpu, uint64_t *nextp, timer_mod(timer, next); } -static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, uint32_t decr, - uint32_t value) +static inline void _cpu_ppc_store_decr(PowerPCCPU *cpu, target_ulong decr, + target_ulong value, int nr_bits) { ppc_tb_t *tb_env = cpu->env.tb_env; __cpu_ppc_store_decr(cpu, &tb_env->decr_next, tb_env->decr_timer, tb_env->decr_timer->cb, &cpu_ppc_decr_lower, decr, - value); + value, nr_bits); } -void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value) +void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = ppc_env_get_cpu(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + int nr_bits = 32; - _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value); + if (env->spr[SPR_LPCR] & LPCR_LD) { + nr_bits = pcc->lrg_decr_bits; + } + + _cpu_ppc_store_decr(cpu, cpu_ppc_load_decr(env), value, nr_bits); } static void cpu_ppc_decr_cb(void *opaque) @@ -809,23 +941,25 @@ static void cpu_ppc_decr_cb(void *opaque) cpu_ppc_decr_excp(cpu); } -static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, uint32_t hdecr, - uint32_t value) +static inline void _cpu_ppc_store_hdecr(PowerPCCPU *cpu, target_ulong hdecr, + target_ulong value, int nr_bits) { ppc_tb_t *tb_env = cpu->env.tb_env; if (tb_env->hdecr_timer != NULL) { __cpu_ppc_store_decr(cpu, &tb_env->hdecr_next, tb_env->hdecr_timer, tb_env->hdecr_timer->cb, &cpu_ppc_hdecr_lower, - hdecr, value); + hdecr, value, nr_bits); } } -void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value) +void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value) { PowerPCCPU *cpu = ppc_env_get_cpu(env); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value); + _cpu_ppc_store_hdecr(cpu, cpu_ppc_load_hdecr(env), value, + pcc->lrg_decr_bits); } static void cpu_ppc_hdecr_cb(void *opaque) @@ -855,8 +989,8 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) * if a decrementer exception is pending when it enables msr_ee at startup, * it's not ready to handle it... */ - _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF); - _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF); + _cpu_ppc_store_decr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); + _cpu_ppc_store_hdecr(cpu, 0xFFFFFFFF, 0xFFFFFFFF, 32); cpu_ppc_store_purr(cpu, 0x0000000000000000ULL); } @@ -967,7 +1101,7 @@ clk_setup_cb cpu_ppc_tb_init (CPUPPCState *env, uint32_t freq) tb_env = g_malloc0(sizeof(ppc_tb_t)); env->tb_env = tb_env; tb_env->flags = PPC_DECR_UNDERFLOW_TRIGGERED; - if (env->insns_flags & PPC_SEGMENT_64B) { + if (is_book3s_arch2x(env)) { /* All Book3S 64bit CPUs implement level based DEC logic */ tb_env->flags |= PPC_DECR_UNDERFLOW_LEVEL; } @@ -1358,3 +1492,19 @@ void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val) break; } } + +PowerPCCPU *ppc_get_vcpu_by_pir(int pir) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + + if (env->spr_cb[SPR_PIR].default_value == pir) { + return cpu; + } + } + + return NULL; +} diff --git a/hw/ppc/ppc405_boards.c b/hw/ppc/ppc405_boards.c index 70111075b3..13318a9faf 100644 --- a/hw/ppc/ppc405_boards.c +++ b/hw/ppc/ppc405_boards.c @@ -48,8 +48,6 @@ #define USE_FLASH_BIOS -//#define DEBUG_BOARD_INIT - /*****************************************************************************/ /* PPC405EP reference board (IBM) */ /* Standalone board with: @@ -66,7 +64,7 @@ struct ref405ep_fpga_t { uint8_t reg1; }; -static uint32_t ref405ep_fpga_readb (void *opaque, hwaddr addr) +static uint64_t ref405ep_fpga_readb(void *opaque, hwaddr addr, unsigned size) { ref405ep_fpga_t *fpga; uint32_t ret; @@ -87,8 +85,8 @@ static uint32_t ref405ep_fpga_readb (void *opaque, hwaddr addr) return ret; } -static void ref405ep_fpga_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void ref405ep_fpga_writeb(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { ref405ep_fpga_t *fpga; @@ -105,54 +103,14 @@ static void ref405ep_fpga_writeb (void *opaque, } } -static uint32_t ref405ep_fpga_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = ref405ep_fpga_readb(opaque, addr) << 8; - ret |= ref405ep_fpga_readb(opaque, addr + 1); - - return ret; -} - -static void ref405ep_fpga_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - ref405ep_fpga_writeb(opaque, addr, (value >> 8) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 1, value & 0xFF); -} - -static uint32_t ref405ep_fpga_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - - ret = ref405ep_fpga_readb(opaque, addr) << 24; - ret |= ref405ep_fpga_readb(opaque, addr + 1) << 16; - ret |= ref405ep_fpga_readb(opaque, addr + 2) << 8; - ret |= ref405ep_fpga_readb(opaque, addr + 3); - - return ret; -} - -static void ref405ep_fpga_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - ref405ep_fpga_writeb(opaque, addr, (value >> 24) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 1, (value >> 16) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 2, (value >> 8) & 0xFF); - ref405ep_fpga_writeb(opaque, addr + 3, value & 0xFF); -} - static const MemoryRegionOps ref405ep_fpga_ops = { - .old_mmio = { - .read = { - ref405ep_fpga_readb, ref405ep_fpga_readw, ref405ep_fpga_readl, - }, - .write = { - ref405ep_fpga_writeb, ref405ep_fpga_writew, ref405ep_fpga_writel, - }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, + .read = ref405ep_fpga_readb, + .write = ref405ep_fpga_writeb, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, }; static void ref405ep_fpga_reset (void *opaque) @@ -189,7 +147,7 @@ static void ref405ep_init(MachineState *machine) MemoryRegion *bios; MemoryRegion *sram = g_new(MemoryRegion, 1); ram_addr_t bdloc; - MemoryRegion *ram_memories = g_malloc(2 * sizeof(*ram_memories)); + MemoryRegion *ram_memories = g_new(MemoryRegion, 2); hwaddr ram_bases[2], ram_sizes[2]; target_ulong sram_size; long bios_size; @@ -198,17 +156,10 @@ static void ref405ep_init(MachineState *machine) target_ulong kernel_base, initrd_base; long kernel_size, initrd_size; int linux_boot; - int fl_idx, fl_sectors, len; + int len; DriveInfo *dinfo; MemoryRegion *sysmem = get_system_memory(); -#ifdef TARGET_PPCEMB - if (!qtest_enabled()) { - warn_report("qemu-system-ppcemb is deprecated, " - "please use qemu-system-ppc instead."); - } -#endif - /* XXX: fix this */ memory_region_allocate_system_memory(&ram_memories[0], NULL, "ef405ep.ram", 0x08000000); @@ -218,9 +169,6 @@ static void ref405ep_init(MachineState *machine) ram_bases[1] = 0x00000000; ram_sizes[1] = 0x00000000; ram_size = 128 * MiB; -#ifdef DEBUG_BOARD_INIT - printf("%s: register cpu\n", __func__); -#endif env = ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate SRAM */ @@ -229,35 +177,19 @@ static void ref405ep_init(MachineState *machine) &error_fatal); memory_region_add_subregion(sysmem, 0xFFF00000, sram); /* allocate and load BIOS */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register BIOS\n", __func__); -#endif - fl_idx = 0; #ifdef USE_FLASH_BIOS - dinfo = drive_get(IF_PFLASH, 0, fl_idx); + dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - bios_size = blk_getlength(blk); - fl_sectors = (bios_size + 65535) >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size %lx" - " at addr %lx '%s' %d\n", - fl_idx, bios_size, -bios_size, - blk_name(blk), fl_sectors); -#endif + bios_size = 8 * MiB; pflash_cfi02_register((uint32_t)(-bios_size), - NULL, "ef405ep.bios", bios_size, - blk, 65536, fl_sectors, 1, + "ef405ep.bios", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, 1, 2, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, 1); - fl_idx++; } else #endif { -#ifdef DEBUG_BOARD_INIT - printf("Load BIOS from file\n"); -#endif bios = g_new(MemoryRegion, 1); memory_region_init_ram(bios, NULL, "ef405ep.bios", BIOS_SIZE, &error_fatal); @@ -266,9 +198,11 @@ static void ref405ep_init(MachineState *machine) bios_name = BIOS_FILENAME; filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); + bios_size = load_image_size(filename, + memory_region_get_ram_ptr(bios), + BIOS_SIZE); g_free(filename); - if (bios_size < 0 || bios_size > BIOS_SIZE) { + if (bios_size < 0) { error_report("Could not load PowerPC BIOS '%s'", bios_name); exit(1); } @@ -284,21 +218,12 @@ static void ref405ep_init(MachineState *machine) memory_region_set_readonly(bios, true); } /* Register FPGA */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register FPGA\n", __func__); -#endif ref405ep_fpga_init(sysmem, 0xF0300000); /* Register NVRAM */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register NVRAM\n", __func__); -#endif m48t59_init(NULL, 0xF0000000, 0, 8192, 1968, 8); /* Load kernel */ linux_boot = (kernel_filename != NULL); if (linux_boot) { -#ifdef DEBUG_BOARD_INIT - printf("%s: load kernel\n", __func__); -#endif memset(&bd, 0, sizeof(bd)); bd.bi_memstart = 0x00000000; bd.bi_memsize = ram_size; @@ -370,10 +295,6 @@ static void ref405ep_init(MachineState *machine) initrd_size = 0; bdloc = 0; } -#ifdef DEBUG_BOARD_INIT - printf("bdloc " RAM_ADDR_FMT "\n", bdloc); - printf("%s: Done\n", __func__); -#endif } static void ref405ep_class_init(ObjectClass *oc, void *data) @@ -493,23 +414,16 @@ static void taihu_405ep_init(MachineState *machine) qemu_irq *pic; MemoryRegion *sysmem = get_system_memory(); MemoryRegion *bios; - MemoryRegion *ram_memories = g_malloc(2 * sizeof(*ram_memories)); + MemoryRegion *ram_memories = g_new(MemoryRegion, 2); MemoryRegion *ram = g_malloc0(sizeof(*ram)); hwaddr ram_bases[2], ram_sizes[2]; long bios_size; target_ulong kernel_base, initrd_base; long kernel_size, initrd_size; int linux_boot; - int fl_idx, fl_sectors; + int fl_idx; DriveInfo *dinfo; -#ifdef TARGET_PPCEMB - if (!qtest_enabled()) { - warn_report("qemu-system-ppcemb is deprecated, " - "please use qemu-system-ppc instead."); - } -#endif - /* RAM is soldered to the board so the size cannot be changed */ ram_size = 0x08000000; memory_region_allocate_system_memory(ram, NULL, "taihu_405ep.ram", @@ -525,43 +439,24 @@ static void taihu_405ep_init(MachineState *machine) memory_region_init_alias(&ram_memories[1], NULL, "taihu_405ep.ram-1", ram, ram_bases[1], ram_sizes[1]); -#ifdef DEBUG_BOARD_INIT - printf("%s: register cpu\n", __func__); -#endif ppc405ep_init(sysmem, ram_memories, ram_bases, ram_sizes, 33333333, &pic, kernel_filename == NULL ? 0 : 1); /* allocate and load BIOS */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register BIOS\n", __func__); -#endif fl_idx = 0; #if defined(USE_FLASH_BIOS) dinfo = drive_get(IF_PFLASH, 0, fl_idx); if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - bios_size = blk_getlength(blk); - /* XXX: should check that size is 2MB */ - // bios_size = 2 * 1024 * 1024; - fl_sectors = (bios_size + 65535) >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size %lx" - " at addr %lx '%s' %d\n", - fl_idx, bios_size, -bios_size, - blk_name(blk), fl_sectors); -#endif - pflash_cfi02_register((uint32_t)(-bios_size), - NULL, "taihu_405ep.bios", bios_size, - blk, 65536, fl_sectors, 1, + bios_size = 2 * MiB; + pflash_cfi02_register(0xFFE00000, + "taihu_405ep.bios", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, 1, 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, 1); fl_idx++; } else #endif { -#ifdef DEBUG_BOARD_INIT - printf("Load BIOS from file\n"); -#endif if (bios_name == NULL) bios_name = BIOS_FILENAME; bios = g_new(MemoryRegion, 1); @@ -569,9 +464,11 @@ static void taihu_405ep_init(MachineState *machine) &error_fatal); filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); if (filename) { - bios_size = load_image(filename, memory_region_get_ram_ptr(bios)); + bios_size = load_image_size(filename, + memory_region_get_ram_ptr(bios), + BIOS_SIZE); g_free(filename); - if (bios_size < 0 || bios_size > BIOS_SIZE) { + if (bios_size < 0) { error_report("Could not load PowerPC BIOS '%s'", bios_name); exit(1); } @@ -586,35 +483,19 @@ static void taihu_405ep_init(MachineState *machine) /* Register Linux flash */ dinfo = drive_get(IF_PFLASH, 0, fl_idx); if (dinfo) { - BlockBackend *blk = blk_by_legacy_dinfo(dinfo); - - bios_size = blk_getlength(blk); - /* XXX: should check that size is 32MB */ bios_size = 32 * MiB; - fl_sectors = (bios_size + 65535) >> 16; -#ifdef DEBUG_BOARD_INIT - printf("Register parallel flash %d size %lx" - " at addr " TARGET_FMT_lx " '%s'\n", - fl_idx, bios_size, (target_ulong)0xfc000000, - blk_name(blk)); -#endif - pflash_cfi02_register(0xfc000000, NULL, "taihu_405ep.flash", bios_size, - blk, 65536, fl_sectors, 1, + pflash_cfi02_register(0xfc000000, "taihu_405ep.flash", bios_size, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, 1, 4, 0x0001, 0x22DA, 0x0000, 0x0000, 0x555, 0x2AA, 1); fl_idx++; } /* Register CLPD & LCD display */ -#ifdef DEBUG_BOARD_INIT - printf("%s: register CPLD\n", __func__); -#endif taihu_cpld_init(sysmem, 0x50100000); /* Load kernel */ linux_boot = (kernel_filename != NULL); if (linux_boot) { -#ifdef DEBUG_BOARD_INIT - printf("%s: load kernel\n", __func__); -#endif kernel_base = KERNEL_LOAD_ADDR; /* now we can load the kernel */ kernel_size = load_image_targphys(kernel_filename, kernel_base, @@ -643,9 +524,6 @@ static void taihu_405ep_init(MachineState *machine) initrd_base = 0; initrd_size = 0; } -#ifdef DEBUG_BOARD_INIT - printf("%s: Done\n", __func__); -#endif } static void taihu_class_init(ObjectClass *oc, void *data) diff --git a/hw/ppc/ppc405_uc.c b/hw/ppc/ppc405_uc.c index 4bd9fbcc1e..3ae7f6d4df 100644 --- a/hw/ppc/ppc405_uc.c +++ b/hw/ppc/ppc405_uc.c @@ -283,7 +283,7 @@ struct ppc4xx_opba_t { uint8_t pr; }; -static uint32_t opba_readb (void *opaque, hwaddr addr) +static uint64_t opba_readb(void *opaque, hwaddr addr, unsigned size) { ppc4xx_opba_t *opba; uint32_t ret; @@ -307,8 +307,8 @@ static uint32_t opba_readb (void *opaque, hwaddr addr) return ret; } -static void opba_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void opba_writeb(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { ppc4xx_opba_t *opba; @@ -328,61 +328,14 @@ static void opba_writeb (void *opaque, break; } } - -static uint32_t opba_readw (void *opaque, hwaddr addr) -{ - uint32_t ret; - -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - ret = opba_readb(opaque, addr) << 8; - ret |= opba_readb(opaque, addr + 1); - - return ret; -} - -static void opba_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - opba_writeb(opaque, addr, value >> 8); - opba_writeb(opaque, addr + 1, value); -} - -static uint32_t opba_readl (void *opaque, hwaddr addr) -{ - uint32_t ret; - -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - ret = opba_readb(opaque, addr) << 24; - ret |= opba_readb(opaque, addr + 1) << 16; - - return ret; -} - -static void opba_writel (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_OPBA - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - opba_writeb(opaque, addr, value >> 24); - opba_writeb(opaque, addr + 1, value >> 16); -} - static const MemoryRegionOps opba_ops = { - .old_mmio = { - .read = { opba_readb, opba_readw, opba_readl, }, - .write = { opba_writeb, opba_writew, opba_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, + .read = opba_readb, + .write = opba_writeb, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, }; static void ppc4xx_opba_reset (void *opaque) @@ -750,65 +703,27 @@ struct ppc405_gpio_t { uint32_t isr1l; }; -static uint32_t ppc405_gpio_readb (void *opaque, hwaddr addr) +static uint64_t ppc405_gpio_read(void *opaque, hwaddr addr, unsigned size) { #ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); + printf("%s: addr " TARGET_FMT_plx " size %d\n", __func__, addr, size); #endif return 0; } -static void ppc405_gpio_writeb (void *opaque, - hwaddr addr, uint32_t value) +static void ppc405_gpio_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { #ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif -} - -static uint32_t ppc405_gpio_readw (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - - return 0; -} - -static void ppc405_gpio_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif -} - -static uint32_t ppc405_gpio_readl (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - - return 0; -} - -static void ppc405_gpio_writel (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_GPIO - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); + printf("%s: addr " TARGET_FMT_plx " size %d val %08" PRIx32 "\n", + __func__, addr, size, value); #endif } static const MemoryRegionOps ppc405_gpio_ops = { - .old_mmio = { - .read = { ppc405_gpio_readb, ppc405_gpio_readw, ppc405_gpio_readl, }, - .write = { ppc405_gpio_writeb, ppc405_gpio_writew, ppc405_gpio_writel, }, - }, + .read = ppc405_gpio_read, + .write = ppc405_gpio_write, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -1017,44 +932,6 @@ struct ppc4xx_gpt_t { uint32_t mask[5]; }; -static uint32_t ppc4xx_gpt_readb (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPT - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - /* XXX: generate a bus fault */ - return -1; -} - -static void ppc4xx_gpt_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - /* XXX: generate a bus fault */ -} - -static uint32_t ppc4xx_gpt_readw (void *opaque, hwaddr addr) -{ -#ifdef DEBUG_GPT - printf("%s: addr " TARGET_FMT_plx "\n", __func__, addr); -#endif - /* XXX: generate a bus fault */ - return -1; -} - -static void ppc4xx_gpt_writew (void *opaque, - hwaddr addr, uint32_t value) -{ -#ifdef DEBUG_I2C - printf("%s: addr " TARGET_FMT_plx " val %08" PRIx32 "\n", __func__, addr, - value); -#endif - /* XXX: generate a bus fault */ -} - static int ppc4xx_gpt_compare (ppc4xx_gpt_t *gpt, int n) { /* XXX: TODO */ @@ -1107,7 +984,7 @@ static void ppc4xx_gpt_compute_timer (ppc4xx_gpt_t *gpt) /* XXX: TODO */ } -static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr) +static uint64_t ppc4xx_gpt_read(void *opaque, hwaddr addr, unsigned size) { ppc4xx_gpt_t *gpt; uint32_t ret; @@ -1162,8 +1039,8 @@ static uint32_t ppc4xx_gpt_readl (void *opaque, hwaddr addr) return ret; } -static void ppc4xx_gpt_writel (void *opaque, - hwaddr addr, uint32_t value) +static void ppc4xx_gpt_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { ppc4xx_gpt_t *gpt; int idx; @@ -1225,10 +1102,10 @@ static void ppc4xx_gpt_writel (void *opaque, } static const MemoryRegionOps gpt_ops = { - .old_mmio = { - .read = { ppc4xx_gpt_readb, ppc4xx_gpt_readw, ppc4xx_gpt_readl, }, - .write = { ppc4xx_gpt_writeb, ppc4xx_gpt_writew, ppc4xx_gpt_writel, }, - }, + .read = ppc4xx_gpt_read, + .write = ppc4xx_gpt_write, + .valid.min_access_size = 4, + .valid.max_access_size = 4, .endianness = DEVICE_NATIVE_ENDIAN, }; @@ -1278,64 +1155,6 @@ static void ppc4xx_gpt_init(hwaddr base, qemu_irq irqs[5]) qemu_register_reset(ppc4xx_gpt_reset, gpt); } -/*****************************************************************************/ -/* SPR */ -void ppc40x_core_reset(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - target_ulong dbsr; - - printf("Reset PowerPC core\n"); - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); - dbsr = env->spr[SPR_40x_DBSR]; - dbsr &= ~0x00000300; - dbsr |= 0x00000100; - env->spr[SPR_40x_DBSR] = dbsr; -} - -void ppc40x_chip_reset(PowerPCCPU *cpu) -{ - CPUPPCState *env = &cpu->env; - target_ulong dbsr; - - printf("Reset PowerPC chip\n"); - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_RESET); - /* XXX: TODO reset all internal peripherals */ - dbsr = env->spr[SPR_40x_DBSR]; - dbsr &= ~0x00000300; - dbsr |= 0x00000200; - env->spr[SPR_40x_DBSR] = dbsr; -} - -void ppc40x_system_reset(PowerPCCPU *cpu) -{ - printf("Reset PowerPC system\n"); - qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); -} - -void store_40x_dbcr0 (CPUPPCState *env, uint32_t val) -{ - PowerPCCPU *cpu = ppc_env_get_cpu(env); - - switch ((val >> 28) & 0x3) { - case 0x0: - /* No action */ - break; - case 0x1: - /* Core reset */ - ppc40x_core_reset(cpu); - break; - case 0x2: - /* Chip reset */ - ppc40x_chip_reset(cpu); - break; - case 0x3: - /* System reset */ - ppc40x_system_reset(cpu); - break; - } -} - /*****************************************************************************/ /* PowerPC 405CR */ enum { @@ -1642,7 +1461,7 @@ CPUPPCState *ppc405cr_init(MemoryRegion *address_space_mem, /* OBP arbitrer */ ppc4xx_opba_init(0xef600600); /* Universal interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); + irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; irqs[PPCUIC_OUTPUT_CINT] = @@ -2000,7 +1819,7 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, /* Initialize timers */ ppc_booke_timers_init(cpu, sysclk, 0); /* Universal interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); + irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; irqs[PPCUIC_OUTPUT_CINT] = @@ -2008,7 +1827,7 @@ CPUPPCState *ppc405ep_init(MemoryRegion *address_space_mem, pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); *picp = pic; /* SDRAM controller */ - /* XXX 405EP has no ECC interrupt */ + /* XXX 405EP has no ECC interrupt */ ppc4xx_sdram_init(env, pic[17], 2, ram_memories, ram_bases, ram_sizes, do_init); /* External bus controller */ diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 3d4c43b8cc..b4da099e3e 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -49,7 +49,7 @@ #define PPC440EP_SDRAM_NR_BANKS 4 -static const unsigned int ppc440ep_sdram_bank_sizes[] = { +static const ram_addr_t ppc440ep_sdram_bank_sizes[] = { 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 16 * MiB, 8 * MiB, 0 }; @@ -169,8 +169,7 @@ static void bamboo_init(MachineState *machine) unsigned int pci_irq_nrs[4] = { 28, 27, 26, 25 }; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *isa = g_new(MemoryRegion, 1); - MemoryRegion *ram_memories - = g_malloc(PPC440EP_SDRAM_NR_BANKS * sizeof(*ram_memories)); + MemoryRegion *ram_memories = g_new(MemoryRegion, PPC440EP_SDRAM_NR_BANKS); hwaddr ram_bases[PPC440EP_SDRAM_NR_BANKS]; hwaddr ram_sizes[PPC440EP_SDRAM_NR_BANKS]; qemu_irq *pic; @@ -180,7 +179,7 @@ static void bamboo_init(MachineState *machine) CPUPPCState *env; uint64_t elf_entry; uint64_t elf_lowaddr; - hwaddr loadaddr = 0; + hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; target_long initrd_size = 0; DeviceState *dev; int success; @@ -195,19 +194,12 @@ static void bamboo_init(MachineState *machine) exit(1); } -#ifdef TARGET_PPCEMB - if (!qtest_enabled()) { - warn_report("qemu-system-ppcemb is deprecated, " - "please use qemu-system-ppc instead."); - } -#endif - qemu_register_reset(main_cpu_reset, cpu); ppc_booke_timers_init(cpu, 400000000, 0); ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); + irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; pic = ppcuic_init(env, irqs, 0x0C0, 0, 1); @@ -264,7 +256,7 @@ static void bamboo_init(MachineState *machine) success = load_uimage(kernel_filename, &entry, &loadaddr, NULL, NULL, NULL); if (success < 0) { - success = load_elf(kernel_filename, NULL, NULL, &elf_entry, + success = load_elf(kernel_filename, NULL, NULL, NULL, &elf_entry, &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; diff --git a/hw/ppc/ppc440_pcix.c b/hw/ppc/ppc440_pcix.c index 64ed07afa6..42ef76b6f5 100644 --- a/hw/ppc/ppc440_pcix.c +++ b/hw/ppc/ppc440_pcix.c @@ -466,17 +466,18 @@ const MemoryRegionOps ppc440_pcix_host_data_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static int ppc440_pcix_initfn(SysBusDevice *dev) +static void ppc440_pcix_realize(DeviceState *dev, Error **errp) { + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PPC440PCIXState *s; PCIHostState *h; h = PCI_HOST_BRIDGE(dev); s = PPC440_PCIX_HOST_BRIDGE(dev); - sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(sbd, &s->irq); memory_region_init(&s->busmem, OBJECT(dev), "pci bus memory", UINT64_MAX); - h->bus = pci_register_root_bus(DEVICE(dev), NULL, ppc440_pcix_set_irq, + h->bus = pci_register_root_bus(dev, NULL, ppc440_pcix_set_irq, ppc440_pcix_map_irq, &s->irq, &s->busmem, get_system_io(), PCI_DEVFN(0, 0), 1, TYPE_PCI_BUS); @@ -497,17 +498,14 @@ static int ppc440_pcix_initfn(SysBusDevice *dev) memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); memory_region_add_subregion(&s->container, PPC440_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); - - return 0; + sysbus_init_mmio(sbd, &s->container); } static void ppc440_pcix_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = ppc440_pcix_initfn; + dc->realize = ppc440_pcix_realize; dc->reset = ppc440_pcix_reset; } diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 09ccda548f..9130eb314c 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -2,7 +2,7 @@ * QEMU PowerPC 440 embedded processors emulation * * Copyright (c) 2012 François Revol - * Copyright (c) 2016-2018 BALATON Zoltan + * Copyright (c) 2016-2019 BALATON Zoltan * * This work is licensed under the GNU GPL license version 2 or later. * @@ -481,7 +481,7 @@ void ppc4xx_sdr_init(CPUPPCState *env) /*****************************************************************************/ /* SDRAM controller */ -typedef struct ppc4xx_sdram_t { +typedef struct ppc440_sdram_t { uint32_t addr; int nbanks; MemoryRegion containers[4]; /* used for clipping */ @@ -489,7 +489,7 @@ typedef struct ppc4xx_sdram_t { hwaddr ram_bases[4]; hwaddr ram_sizes[4]; uint32_t bcr[4]; -} ppc4xx_sdram_t; +} ppc440_sdram_t; enum { SDRAM0_CFGADDR = 0x10, @@ -505,10 +505,6 @@ enum { SDRAM_PLBADDUHB = 0x50, }; -/* XXX: TOFIX: some patches have made this code become inconsistent: - * there are type inconsistencies, mixing hwaddr, target_ulong - * and uint32_t - */ static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) { uint32_t bcr; @@ -538,11 +534,17 @@ static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) case (1 * GiB): bcr = 0xe000; break; + case (2 * GiB): + bcr = 0xc000; + break; + case (4 * GiB): + bcr = 0x8000; + break; default: error_report("invalid RAM size " TARGET_FMT_plx, ram_size); return 0; } - bcr |= ram_base & 0xFF800000; + bcr |= ram_base >> 2 & 0xffe00000; bcr |= 1; return bcr; @@ -550,68 +552,60 @@ static uint32_t sdram_bcr(hwaddr ram_base, hwaddr ram_size) static inline hwaddr sdram_base(uint32_t bcr) { - return bcr & 0xFF800000; + return (bcr & 0xffe00000) << 2; } -static target_ulong sdram_size(uint32_t bcr) +static uint64_t sdram_size(uint32_t bcr) { - target_ulong size; + uint64_t size; int sh; sh = 1024 - ((bcr >> 6) & 0x3ff); - if (sh == 0) { - size = -1; - } else { - size = 8 * MiB * sh; - } + size = 8 * MiB * sh; return size; } -static void sdram_set_bcr(ppc4xx_sdram_t *sdram, - uint32_t *bcrp, uint32_t bcr, int enabled) +static void sdram_set_bcr(ppc440_sdram_t *sdram, int i, + uint32_t bcr, int enabled) { - unsigned n = bcrp - sdram->bcr; - - if (*bcrp & 1) { - /* Unmap RAM */ + if (sdram->bcr[i] & 1) { + /* First unmap RAM if enabled */ memory_region_del_subregion(get_system_memory(), - &sdram->containers[n]); - memory_region_del_subregion(&sdram->containers[n], - &sdram->ram_memories[n]); - object_unparent(OBJECT(&sdram->containers[n])); + &sdram->containers[i]); + memory_region_del_subregion(&sdram->containers[i], + &sdram->ram_memories[i]); + object_unparent(OBJECT(&sdram->containers[i])); } - *bcrp = bcr & 0xFFDEE001; + sdram->bcr[i] = bcr & 0xffe0ffc1; if (enabled && (bcr & 1)) { - memory_region_init(&sdram->containers[n], NULL, "sdram-containers", + memory_region_init(&sdram->containers[i], NULL, "sdram-containers", sdram_size(bcr)); - memory_region_add_subregion(&sdram->containers[n], 0, - &sdram->ram_memories[n]); + memory_region_add_subregion(&sdram->containers[i], 0, + &sdram->ram_memories[i]); memory_region_add_subregion(get_system_memory(), sdram_base(bcr), - &sdram->containers[n]); + &sdram->containers[i]); } } -static void sdram_map_bcr(ppc4xx_sdram_t *sdram) +static void sdram_map_bcr(ppc440_sdram_t *sdram) { int i; for (i = 0; i < sdram->nbanks; i++) { if (sdram->ram_sizes[i] != 0) { - sdram_set_bcr(sdram, - &sdram->bcr[i], - sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), - 1); + sdram_set_bcr(sdram, i, sdram_bcr(sdram->ram_bases[i], + sdram->ram_sizes[i]), 1); } else { - sdram_set_bcr(sdram, &sdram->bcr[i], 0, 0); + sdram_set_bcr(sdram, i, 0, 0); } } } static uint32_t dcr_read_sdram(void *opaque, int dcrn) { - ppc4xx_sdram_t *sdram = opaque; + ppc440_sdram_t *sdram = opaque; uint32_t ret = 0; switch (dcrn) { @@ -619,8 +613,10 @@ static uint32_t dcr_read_sdram(void *opaque, int dcrn) case SDRAM_R1BAS: case SDRAM_R2BAS: case SDRAM_R3BAS: - ret = sdram_bcr(sdram->ram_bases[dcrn - SDRAM_R0BAS], - sdram->ram_sizes[dcrn - SDRAM_R0BAS]); + if (sdram->ram_sizes[dcrn - SDRAM_R0BAS]) { + ret = sdram_bcr(sdram->ram_bases[dcrn - SDRAM_R0BAS], + sdram->ram_sizes[dcrn - SDRAM_R0BAS]); + } break; case SDRAM_CONF1HB: case SDRAM_CONF1LL: @@ -662,7 +658,7 @@ static uint32_t dcr_read_sdram(void *opaque, int dcrn) static void dcr_write_sdram(void *opaque, int dcrn, uint32_t val) { - ppc4xx_sdram_t *sdram = opaque; + ppc440_sdram_t *sdram = opaque; switch (dcrn) { case SDRAM_R0BAS: @@ -693,7 +689,7 @@ static void dcr_write_sdram(void *opaque, int dcrn, uint32_t val) static void sdram_reset(void *opaque) { - ppc4xx_sdram_t *sdram = opaque; + ppc440_sdram_t *sdram = opaque; sdram->addr = 0; } @@ -703,7 +699,7 @@ void ppc440_sdram_init(CPUPPCState *env, int nbanks, hwaddr *ram_bases, hwaddr *ram_sizes, int do_init) { - ppc4xx_sdram_t *sdram; + ppc440_sdram_t *sdram; sdram = g_malloc0(sizeof(*sdram)); sdram->nbanks = nbanks; diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 8c6f3c9577..fdfeb67e65 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -32,8 +32,7 @@ #include "exec/address-spaces.h" #include "qemu/error-report.h" -#define DEBUG_UIC - +/*#define DEBUG_UIC*/ #ifdef DEBUG_UIC # define LOG_UIC(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__) @@ -406,36 +405,34 @@ static target_ulong sdram_size (uint32_t bcr) return size; } -static void sdram_set_bcr(ppc4xx_sdram_t *sdram, - uint32_t *bcrp, uint32_t bcr, int enabled) +static void sdram_set_bcr(ppc4xx_sdram_t *sdram, int i, + uint32_t bcr, int enabled) { - unsigned n = bcrp - sdram->bcr; - - if (*bcrp & 0x00000001) { + if (sdram->bcr[i] & 0x00000001) { /* Unmap RAM */ #ifdef DEBUG_SDRAM printf("%s: unmap RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", - __func__, sdram_base(*bcrp), sdram_size(*bcrp)); + __func__, sdram_base(sdram->bcr[i]), sdram_size(sdram->bcr[i])); #endif memory_region_del_subregion(get_system_memory(), - &sdram->containers[n]); - memory_region_del_subregion(&sdram->containers[n], - &sdram->ram_memories[n]); - object_unparent(OBJECT(&sdram->containers[n])); + &sdram->containers[i]); + memory_region_del_subregion(&sdram->containers[i], + &sdram->ram_memories[i]); + object_unparent(OBJECT(&sdram->containers[i])); } - *bcrp = bcr & 0xFFDEE001; + sdram->bcr[i] = bcr & 0xFFDEE001; if (enabled && (bcr & 0x00000001)) { #ifdef DEBUG_SDRAM printf("%s: Map RAM area " TARGET_FMT_plx " " TARGET_FMT_lx "\n", __func__, sdram_base(bcr), sdram_size(bcr)); #endif - memory_region_init(&sdram->containers[n], NULL, "sdram-containers", + memory_region_init(&sdram->containers[i], NULL, "sdram-containers", sdram_size(bcr)); - memory_region_add_subregion(&sdram->containers[n], 0, - &sdram->ram_memories[n]); + memory_region_add_subregion(&sdram->containers[i], 0, + &sdram->ram_memories[i]); memory_region_add_subregion(get_system_memory(), sdram_base(bcr), - &sdram->containers[n]); + &sdram->containers[i]); } } @@ -445,12 +442,10 @@ static void sdram_map_bcr (ppc4xx_sdram_t *sdram) for (i = 0; i < sdram->nbanks; i++) { if (sdram->ram_sizes[i] != 0) { - sdram_set_bcr(sdram, - &sdram->bcr[i], - sdram_bcr(sdram->ram_bases[i], sdram->ram_sizes[i]), - 1); + sdram_set_bcr(sdram, i, sdram_bcr(sdram->ram_bases[i], + sdram->ram_sizes[i]), 1); } else { - sdram_set_bcr(sdram, &sdram->bcr[i], 0x00000000, 0); + sdram_set_bcr(sdram, i, 0x00000000, 0); } } } @@ -590,16 +585,16 @@ static void dcr_write_sdram (void *opaque, int dcrn, uint32_t val) sdram->pmit = (val & 0xF8000000) | 0x07C00000; break; case 0x40: /* SDRAM_B0CR */ - sdram_set_bcr(sdram, &sdram->bcr[0], val, sdram->cfg & 0x80000000); + sdram_set_bcr(sdram, 0, val, sdram->cfg & 0x80000000); break; case 0x44: /* SDRAM_B1CR */ - sdram_set_bcr(sdram, &sdram->bcr[1], val, sdram->cfg & 0x80000000); + sdram_set_bcr(sdram, 1, val, sdram->cfg & 0x80000000); break; case 0x48: /* SDRAM_B2CR */ - sdram_set_bcr(sdram, &sdram->bcr[2], val, sdram->cfg & 0x80000000); + sdram_set_bcr(sdram, 2, val, sdram->cfg & 0x80000000); break; case 0x4C: /* SDRAM_B3CR */ - sdram_set_bcr(sdram, &sdram->bcr[3], val, sdram->cfg & 0x80000000); + sdram_set_bcr(sdram, 3, val, sdram->cfg & 0x80000000); break; case 0x80: /* SDRAM_TR */ sdram->tr = val & 0x018FC01F; @@ -680,12 +675,12 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, MemoryRegion ram_memories[], hwaddr ram_bases[], hwaddr ram_sizes[], - const unsigned int sdram_bank_sizes[]) + const ram_addr_t sdram_bank_sizes[]) { MemoryRegion *ram = g_malloc0(sizeof(*ram)); ram_addr_t size_left = ram_size; ram_addr_t base = 0; - unsigned int bank_size; + ram_addr_t bank_size; int i; int j; diff --git a/hw/ppc/ppc4xx_pci.c b/hw/ppc/ppc4xx_pci.c index b7642bac01..86981be710 100644 --- a/hw/ppc/ppc4xx_pci.c +++ b/hw/ppc/ppc4xx_pci.c @@ -300,8 +300,9 @@ static const VMStateDescription vmstate_ppc4xx_pci = { }; /* XXX Interrupt acknowledge cycles not supported. */ -static int ppc4xx_pcihost_initfn(SysBusDevice *dev) +static void ppc4xx_pcihost_realize(DeviceState *dev, Error **errp) { + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PPC4xxPCIState *s; PCIHostState *h; PCIBus *b; @@ -311,10 +312,10 @@ static int ppc4xx_pcihost_initfn(SysBusDevice *dev) s = PPC4xx_PCI_HOST_BRIDGE(dev); for (i = 0; i < ARRAY_SIZE(s->irq); i++) { - sysbus_init_irq(dev, &s->irq[i]); + sysbus_init_irq(sbd, &s->irq[i]); } - b = pci_register_root_bus(DEVICE(dev), NULL, ppc4xx_pci_set_irq, + b = pci_register_root_bus(dev, NULL, ppc4xx_pci_set_irq, ppc4xx_pci_map_irq, s->irq, get_system_memory(), get_system_io(), 0, 4, TYPE_PCI_BUS); h->bus = b; @@ -332,10 +333,8 @@ static int ppc4xx_pcihost_initfn(SysBusDevice *dev) memory_region_add_subregion(&s->container, PCIC0_CFGADDR, &h->conf_mem); memory_region_add_subregion(&s->container, PCIC0_CFGDATA, &h->data_mem); memory_region_add_subregion(&s->container, PCI_REG_BASE, &s->iomem); - sysbus_init_mmio(dev, &s->container); + sysbus_init_mmio(sbd, &s->container); qemu_register_reset(ppc4xx_pci_reset, s); - - return 0; } static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) @@ -367,10 +366,9 @@ static const TypeInfo ppc4xx_host_bridge_info = { static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); - k->init = ppc4xx_pcihost_initfn; + dc->realize = ppc4xx_pcihost_realize; dc->vmsd = &vmstate_ppc4xx_pci; } diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 23bcf1b138..4f11e00a17 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -28,7 +28,6 @@ #include "hw/ppc/ppc.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" -#include "hw/timer/m48t59.h" #include "qemu/log.h" #include "hw/loader.h" #include "kvm_ppc.h" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 3401570d98..847d320465 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -26,7 +26,6 @@ #include "cpu.h" #include "hw/hw.h" #include "hw/timer/m48t59.h" -#include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/block/fdc.h" #include "net/net.h" @@ -78,94 +77,6 @@ static int ne2000_irq[NE2000_NB_MAX] = { 9, 10, 11, 3, 4, 5 }; /* ISA IO ports bridge */ #define PPC_IO_BASE 0x80000000 -/* PowerPC control and status registers */ -#if 0 // Not used -static struct { - /* IDs */ - uint32_t veni_devi; - uint32_t revi; - /* Control and status */ - uint32_t gcsr; - uint32_t xcfr; - uint32_t ct32; - uint32_t mcsr; - /* General purpose registers */ - uint32_t gprg[6]; - /* Exceptions */ - uint32_t feen; - uint32_t fest; - uint32_t fema; - uint32_t fecl; - uint32_t eeen; - uint32_t eest; - uint32_t eecl; - uint32_t eeint; - uint32_t eemck0; - uint32_t eemck1; - /* Error diagnostic */ -} XCSR; - -static void PPC_XCSR_writeb (void *opaque, - hwaddr addr, uint32_t value) -{ - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, - value); -} - -static void PPC_XCSR_writew (void *opaque, - hwaddr addr, uint32_t value) -{ - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, - value); -} - -static void PPC_XCSR_writel (void *opaque, - hwaddr addr, uint32_t value) -{ - printf("%s: 0x" TARGET_FMT_plx " => 0x%08" PRIx32 "\n", __func__, addr, - value); -} - -static uint32_t PPC_XCSR_readb (void *opaque, hwaddr addr) -{ - uint32_t retval = 0; - - printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, - retval); - - return retval; -} - -static uint32_t PPC_XCSR_readw (void *opaque, hwaddr addr) -{ - uint32_t retval = 0; - - printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, - retval); - - return retval; -} - -static uint32_t PPC_XCSR_readl (void *opaque, hwaddr addr) -{ - uint32_t retval = 0; - - printf("%s: 0x" TARGET_FMT_plx " <= %08" PRIx32 "\n", __func__, addr, - retval); - - return retval; -} - -static const MemoryRegionOps PPC_XCSR_ops = { - .old_mmio = { - .read = { PPC_XCSR_readb, PPC_XCSR_readw, PPC_XCSR_readl, }, - .write = { PPC_XCSR_writeb, PPC_XCSR_writew, PPC_XCSR_writel, }, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -#endif - /* Fake super-io ports for PREP platform (Intel 82378ZB) */ typedef struct sysctrl_t { qemu_irq reset_irq; @@ -590,6 +501,7 @@ static void ppc_prep_init(MachineState *machine) } qdev_prop_set_string(dev, "bios-name", bios_name); qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); + qdev_prop_set_bit(dev, "is-legacy-prep", true); pcihost = PCI_HOST_BRIDGE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev), NULL); qdev_init_nofail(dev); @@ -625,7 +537,7 @@ static void ppc_prep_init(MachineState *machine) nb_nics1 = NE2000_NB_MAX; for(i = 0; i < nb_nics1; i++) { if (nd_table[i].model == NULL) { - nd_table[i].model = g_strdup("ne2k_isa"); + nd_table[i].model = g_strdup("ne2k_isa"); } if (strcmp(nd_table[i].model, "ne2k_isa") == 0) { isa_ne2000_init(isa_bus, ne2000_io[i], ne2000_irq[i], @@ -639,7 +551,7 @@ static void ppc_prep_init(MachineState *machine) for(i = 0; i < MAX_IDE_BUS; i++) { isa_ide_init(isa_bus, ide_iobase[i], ide_iobase2[i], ide_irq[i], hd[2 * i], - hd[2 * i + 1]); + hd[2 * i + 1]); } cpu = POWERPC_CPU(first_cpu); @@ -648,11 +560,10 @@ static void ppc_prep_init(MachineState *machine) portio_list_init(&prep_port_list, NULL, prep_portio_list, sysctrl, "prep"); portio_list_add(&prep_port_list, isa_address_space_io(isa), 0x0); - /* PowerPC control and status register group */ -#if 0 - memory_region_init_io(xcsr, NULL, &PPC_XCSR_ops, NULL, "ppc-xcsr", 0x1000); - memory_region_add_subregion(sysmem, 0xFEFF0000, xcsr); -#endif + /* + * PowerPC control and status register group: unimplemented, + * would be at address 0xFEFF0000. + */ if (machine_usb(machine)) { pci_create_simple(pci_bus, -1, "pci-ohci"); @@ -676,6 +587,7 @@ static void ppc_prep_init(MachineState *machine) static void prep_machine_init(MachineClass *mc) { + mc->deprecation_reason = "use 40p machine type instead"; mc->desc = "PowerPC PREP platform"; mc->init = ppc_prep_init; mc->block_default_type = IF_IDE; @@ -696,6 +608,9 @@ static int prep_set_cmos_checksum(DeviceState *dev, void *opaque) rtc_set_memory(rtc, 0x3e, checksum & 0xff); rtc_set_memory(rtc, 0x2f, checksum >> 8); rtc_set_memory(rtc, 0x3f, checksum >> 8); + + object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(rtc), + "date", NULL); } return 0; } @@ -705,8 +620,8 @@ static void ibm_40p_init(MachineState *machine) CPUPPCState *env = NULL; uint16_t cmos_checksum; PowerPCCPU *cpu; - DeviceState *dev; - SysBusDevice *pcihost; + DeviceState *dev, *i82378_dev; + SysBusDevice *pcihost, *s; Nvram *m48t59 = NULL; PCIBus *pci_bus; ISABus *isa_bus; @@ -736,7 +651,7 @@ static void ibm_40p_init(MachineState *machine) /* PCI host */ dev = qdev_create(NULL, "raven-pcihost"); if (!bios_name) { - bios_name = BIOS_FILENAME; + bios_name = "openbios-ppc"; } qdev_prop_set_string(dev, "bios-name", bios_name); qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); @@ -750,14 +665,11 @@ static void ibm_40p_init(MachineState *machine) } /* PCI -> ISA bridge */ - dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378")); - qdev_connect_gpio_out(dev, 0, + i82378_dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(11, 0), "i82378")); + qdev_connect_gpio_out(i82378_dev, 0, cpu->env.irq_inputs[PPC6xx_INPUT_INT]); - sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(dev, 15)); - sysbus_connect_irq(pcihost, 1, qdev_get_gpio_in(dev, 13)); - sysbus_connect_irq(pcihost, 2, qdev_get_gpio_in(dev, 15)); - sysbus_connect_irq(pcihost, 3, qdev_get_gpio_in(dev, 13)); - isa_bus = ISA_BUS(qdev_get_child_bus(dev, "isa.0")); + sysbus_connect_irq(pcihost, 0, qdev_get_gpio_in(i82378_dev, 15)); + isa_bus = ISA_BUS(qdev_get_child_bus(i82378_dev, "isa.0")); /* Memory controller */ dev = DEVICE(isa_create(isa_bus, "rs6000-mc")); @@ -787,7 +699,10 @@ static void ibm_40p_init(MachineState *machine) qdev_prop_set_uint32(dev, "equipment", 0xc0); qdev_init_nofail(dev); - lsi53c810_create(pci_bus, PCI_DEVFN(1, 0)); + dev = DEVICE(pci_create_simple(pci_bus, PCI_DEVFN(1, 0), + "lsi53c810")); + lsi53c8xx_handle_legacy_cmdline(dev); + qdev_connect_gpio_out(dev, 0, qdev_get_gpio_in(i82378_dev, 13)); /* XXX: s3-trio at PCI_DEVFN(2, 0) */ pci_vga_init(pci_bus); @@ -799,7 +714,16 @@ static void ibm_40p_init(MachineState *machine) } /* Prepare firmware configuration for OpenBIOS */ - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); + dev = qdev_create(NULL, TYPE_FW_CFG_MEM); + fw_cfg = FW_CFG(dev); + qdev_prop_set_uint32(dev, "data_width", 1); + qdev_prop_set_bit(dev, "dma_enabled", false); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(fw_cfg), NULL); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, CFG_ADDR); + sysbus_mmio_map(s, 1, CFG_ADDR + 2); if (machine->kernel_filename) { /* load kernel */ diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 9c77183006..fbcddc5b00 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -2,7 +2,7 @@ * QEMU aCube Sam460ex board emulation * * Copyright (c) 2012 François Revol - * Copyright (c) 2016-2018 BALATON Zoltan + * Copyright (c) 2016-2019 BALATON Zoltan * * This file is derived from hw/ppc440_bamboo.c, * the copyright for that material belongs to the original owners. @@ -34,7 +34,7 @@ #include "hw/sysbus.h" #include "hw/char/serial.h" #include "hw/i2c/ppc4xx_i2c.h" -#include "hw/i2c/smbus.h" +#include "hw/i2c/smbus_eeprom.h" #include "hw/usb/hcd-ehci.h" #include "hw/ppc/fdt.h" @@ -76,9 +76,11 @@ #define UART_FREQ 11059200 #define SDRAM_NR_BANKS 4 -/* FIXME: See u-boot.git 8ac41e, also fix in ppc440_uc.c */ -static const unsigned int ppc460ex_sdram_bank_sizes[] = { - 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, 64 * MiB, 32 * MiB, 0 +/* The SoC could also handle 4 GiB but firmware does not work with that. */ +/* Maybe it overflows a signed 32 bit number somewhere? */ +static const ram_addr_t ppc460ex_sdram_bank_sizes[] = { + 2 * GiB, 1 * GiB, 512 * MiB, 256 * MiB, 128 * MiB, 64 * MiB, + 32 * MiB, 0 }; struct boot_info { @@ -87,163 +89,44 @@ struct boot_info { uint32_t entry; }; -/*****************************************************************************/ -/* SPD eeprom content from mips_malta.c */ - -struct _eeprom24c0x_t { - uint8_t tick; - uint8_t address; - uint8_t command; - uint8_t ack; - uint8_t scl; - uint8_t sda; - uint8_t data; - uint8_t contents[256]; -}; - -typedef struct _eeprom24c0x_t eeprom24c0x_t; - -static eeprom24c0x_t spd_eeprom = { - .contents = { - /* 00000000: */ 0x80, 0x08, 0xFF, 0x0D, 0x0A, 0xFF, 0x40, 0x00, - /* 00000008: */ 0x04, 0x75, 0x54, 0x00, 0x82, 0x08, 0x00, 0x01, - /* 00000010: */ 0x8F, 0x04, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, - /* 00000018: */ 0x00, 0x00, 0x00, 0x14, 0x0F, 0x14, 0x2D, 0xFF, - /* 00000020: */ 0x15, 0x08, 0x15, 0x08, 0x00, 0x00, 0x00, 0x00, - /* 00000028: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000030: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000038: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xD0, - /* 00000040: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000048: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000050: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000058: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000060: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000068: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000070: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - /* 00000078: */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xF4, - }, -}; - -static void generate_eeprom_spd(uint8_t *eeprom, ram_addr_t ram_size) -{ - enum { SDR = 0x4, DDR1 = 0x7, DDR2 = 0x8 } type; - uint8_t *spd = spd_eeprom.contents; - uint8_t nbanks = 0; - uint16_t density = 0; - int i; - - /* work in terms of MB */ - ram_size /= MiB; - - while ((ram_size >= 4) && (nbanks <= 2)) { - int sz_log2 = MIN(31 - clz32(ram_size), 14); - nbanks++; - density |= 1 << (sz_log2 - 2); - ram_size -= 1 << sz_log2; - } - - /* split to 2 banks if possible */ - if ((nbanks == 1) && (density > 1)) { - nbanks++; - density >>= 1; - } - - if (density & 0xff00) { - density = (density & 0xe0) | ((density >> 8) & 0x1f); - type = DDR2; - } else if (!(density & 0x1f)) { - type = DDR2; - } else { - type = SDR; - } - - if (ram_size) { - warn_report("SPD cannot represent final " RAM_ADDR_FMT "MB" - " of SDRAM", ram_size); - } - - /* fill in SPD memory information */ - spd[2] = type; - spd[5] = nbanks; - spd[31] = density; - - /* XXX: this is totally random */ - spd[9] = 0x10; /* CAS tcyc */ - spd[18] = 0x20; /* CAS bit */ - spd[23] = 0x10; /* CAS tcyc */ - spd[25] = 0x10; /* CAS tcyc */ - - /* checksum */ - spd[63] = 0; - for (i = 0; i < 63; i++) { - spd[63] += spd[i]; - } - - /* copy for SMBUS */ - memcpy(eeprom, spd, sizeof(spd_eeprom.contents)); -} - -static void generate_eeprom_serial(uint8_t *eeprom) -{ - int i, pos = 0; - uint8_t mac[6] = { 0x00 }; - uint8_t sn[5] = { 0x01, 0x23, 0x45, 0x67, 0x89 }; - - /* version */ - eeprom[pos++] = 0x01; - - /* count */ - eeprom[pos++] = 0x02; - - /* MAC address */ - eeprom[pos++] = 0x01; /* MAC */ - eeprom[pos++] = 0x06; /* length */ - memcpy(&eeprom[pos], mac, sizeof(mac)); - pos += sizeof(mac); - - /* serial number */ - eeprom[pos++] = 0x02; /* serial */ - eeprom[pos++] = 0x05; /* length */ - memcpy(&eeprom[pos], sn, sizeof(sn)); - pos += sizeof(sn); - - /* checksum */ - eeprom[pos] = 0; - for (i = 0; i < pos; i++) { - eeprom[pos] += eeprom[i]; - } -} - -/*****************************************************************************/ - static int sam460ex_load_uboot(void) { + /* + * This first creates 1MiB of flash memory mapped at the end of + * the 32-bit address space (0xFFF00000..0xFFFFFFFF). + * + * If_PFLASH unit 0 is defined, the flash memory is initialized + * from that block backend. + * + * Else, it's initialized to zero. And then 512KiB of ROM get + * mapped on top of its second half (0xFFF80000..0xFFFFFFFF), + * initialized from u-boot-sam460-20100605.bin. + * + * This doesn't smell right. + * + * The physical hardware appears to have 512KiB flash memory. + * + * TODO Figure out what we really need here, and clean this up. + */ + DriveInfo *dinfo; - BlockBackend *blk = NULL; - hwaddr base = FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32); - long bios_size = FLASH_SIZE; - int fl_sectors; dinfo = drive_get(IF_PFLASH, 0, 0); - if (dinfo) { - blk = blk_by_legacy_dinfo(dinfo); - bios_size = blk_getlength(blk); - } - fl_sectors = (bios_size + 65535) >> 16; - - if (!pflash_cfi01_register(base, NULL, "sam460ex.flash", bios_size, - blk, 64 * KiB, fl_sectors, - 1, 0x89, 0x18, 0x0000, 0x0, 1)) { - error_report("qemu: Error registering flash memory."); + if (!pflash_cfi01_register(FLASH_BASE | ((hwaddr)FLASH_BASE_H << 32), + "sam460ex.flash", FLASH_SIZE, + dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, + 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1)) { + error_report("Error registering flash memory"); /* XXX: return an error instead? */ exit(1); } - if (!blk) { + if (!dinfo) { /*error_report("No flash image given with the 'pflash' parameter," " using default u-boot image");*/ - base = UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32); - rom_add_file_fixed(UBOOT_FILENAME, base, -1); + rom_add_file_fixed(UBOOT_FILENAME, + UBOOT_LOAD_BASE | ((hwaddr)FLASH_BASE_H << 32), + -1); } return 0; @@ -393,24 +276,23 @@ static void sam460ex_init(MachineState *machine) MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *isa = g_new(MemoryRegion, 1); MemoryRegion *ram_memories = g_new(MemoryRegion, SDRAM_NR_BANKS); - hwaddr ram_bases[SDRAM_NR_BANKS]; - hwaddr ram_sizes[SDRAM_NR_BANKS]; + hwaddr ram_bases[SDRAM_NR_BANKS] = {0}; + hwaddr ram_sizes[SDRAM_NR_BANKS] = {0}; MemoryRegion *l2cache_ram = g_new(MemoryRegion, 1); qemu_irq *irqs, *uic[4]; PCIBus *pci_bus; PowerPCCPU *cpu; CPUPPCState *env; - PPC4xxI2CState *i2c[2]; + I2CBus *i2c; hwaddr entry = UBOOT_ENTRY; - hwaddr loadaddr = 0; + hwaddr loadaddr = LOAD_UIMAGE_LOADADDR_INVALID; target_long initrd_size = 0; DeviceState *dev; SysBusDevice *sbdev; - int success; - int i; struct boot_info *boot_info; - const size_t smbus_eeprom_size = 8 * 256; - uint8_t *smbus_eeprom_buf = g_malloc0(smbus_eeprom_size); + uint8_t *spd_data; + Error *err = NULL; + int success; cpu = POWERPC_CPU(cpu_create(machine->cpu_type)); env = &cpu->env; @@ -419,13 +301,6 @@ static void sam460ex_init(MachineState *machine) exit(1); } -#ifdef TARGET_PPCEMB - if (!qtest_enabled()) { - warn_report("qemu-system-ppcemb is deprecated, " - "please use qemu-system-ppc instead."); - } -#endif - qemu_register_reset(main_cpu_reset, cpu); boot_info = g_malloc0(sizeof(*boot_info)); env->load_info = boot_info; @@ -437,7 +312,7 @@ static void sam460ex_init(MachineState *machine) ppc4xx_plb_init(env); /* interrupt controllers */ - irqs = g_malloc0(sizeof(*irqs) * PPCUIC_OUTPUT_NB); + irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; uic[0] = ppcuic_init(env, irqs, 0xc0, 0, 1); @@ -446,8 +321,6 @@ static void sam460ex_init(MachineState *machine) uic[3] = ppcuic_init(env, &uic[0][16], 0xf0, 0, 1); /* SDRAM controller */ - memset(ram_bases, 0, sizeof(ram_bases)); - memset(ram_sizes, 0, sizeof(ram_sizes)); /* put all RAM on first bank because board has one slot * and firmware only checks that */ machine->ram_size = ppc4xx_sdram_adjust(machine->ram_size, 1, @@ -458,23 +331,22 @@ static void sam460ex_init(MachineState *machine) ppc440_sdram_init(env, SDRAM_NR_BANKS, ram_memories, ram_bases, ram_sizes, 1); - /* generate SPD EEPROM data */ - for (i = 0; i < SDRAM_NR_BANKS; i++) { - generate_eeprom_spd(&smbus_eeprom_buf[i * 256], ram_sizes[i]); - } - generate_eeprom_serial(&smbus_eeprom_buf[4 * 256]); - generate_eeprom_serial(&smbus_eeprom_buf[6 * 256]); - - /* IIC controllers */ + /* IIC controllers and devices */ dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600700, uic[0][2]); - i2c[0] = PPC4xx_I2C(dev); - object_property_set_bool(OBJECT(dev), true, "realized", NULL); - smbus_eeprom_init(i2c[0]->bus, 8, smbus_eeprom_buf, smbus_eeprom_size); - g_free(smbus_eeprom_buf); - i2c_create_slave(i2c[0]->bus, "m41t80", 0x68); + i2c = PPC4xx_I2C(dev)->bus; + /* SPD EEPROM on RAM module */ + spd_data = spd_data_generate(DDR2, ram_sizes[0], &err); + if (err) { + warn_report_err(err); + } + if (spd_data) { + spd_data[20] = 4; /* SO-DIMM module */ + smbus_eeprom_init_one(i2c, 0x50, spd_data); + } + /* RTC */ + i2c_create_slave(i2c, "m41t80", 0x68); dev = sysbus_create_simple(TYPE_PPC4xx_I2C, 0x4ef600800, uic[0][3]); - i2c[1] = PPC4xx_I2C(dev); /* External bus controller */ ppc405_ebc_init(env); @@ -552,7 +424,7 @@ static void sam460ex_init(MachineState *machine) if (!machine->kernel_filename) { success = sam460ex_load_uboot(); if (success < 0) { - error_report("qemu: could not load firmware"); + error_report("could not load firmware"); exit(1); } } @@ -564,14 +436,15 @@ static void sam460ex_init(MachineState *machine) if (success < 0) { uint64_t elf_entry, elf_lowaddr; - success = load_elf(machine->kernel_filename, NULL, NULL, &elf_entry, + success = load_elf(machine->kernel_filename, NULL, + NULL, NULL, &elf_entry, &elf_lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); entry = elf_entry; loadaddr = elf_lowaddr; } /* XXX try again as binary */ if (success < 0) { - error_report("qemu: could not load kernel '%s'", + error_report("could not load kernel '%s'", machine->kernel_filename); exit(1); } @@ -583,7 +456,7 @@ static void sam460ex_init(MachineState *machine) RAMDISK_ADDR, machine->ram_size - RAMDISK_ADDR); if (initrd_size < 0) { - error_report("qemu: could not load ram disk '%s' at %x", + error_report("could not load ram disk '%s' at %x", machine->initrd_filename, RAMDISK_ADDR); exit(1); } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 421b2dd09b..b52b82d298 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -29,6 +29,7 @@ #include "qapi/visitor.h" #include "sysemu/sysemu.h" #include "sysemu/numa.h" +#include "sysemu/qtest.h" #include "hw/hw.h" #include "qemu/log.h" #include "hw/fw-path-provider.h" @@ -54,7 +55,6 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" #include "hw/pci-host/spapr.h" -#include "hw/ppc/xics.h" #include "hw/pci/msi.h" #include "hw/pci/pci.h" @@ -71,7 +71,6 @@ #include "hw/nmi.h" #include "hw/intc/intc.h" -#include "hw/compat.h" #include "qemu/cutils.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/mem/memory-device.h" @@ -98,52 +97,25 @@ #define MIN_RMA_SLOF 128UL -#define PHANDLE_XICP 0x00001111 +#define PHANDLE_INTC 0x00001111 /* These two functions implement the VCPU id numbering: one to compute them * all and one to identify thread 0 of a VCORE. Any change to the first one * is likely to have an impact on the second one, so let's keep them close. */ -static int spapr_vcpu_id(sPAPRMachineState *spapr, int cpu_index) +static int spapr_vcpu_id(SpaprMachineState *spapr, int cpu_index) { assert(spapr->vsmt); return (cpu_index / smp_threads) * spapr->vsmt + cpu_index % smp_threads; } -static bool spapr_is_thread0_in_vcore(sPAPRMachineState *spapr, +static bool spapr_is_thread0_in_vcore(SpaprMachineState *spapr, PowerPCCPU *cpu) { assert(spapr->vsmt); return spapr_get_vcpu_id(cpu) % spapr->vsmt == 0; } -static ICSState *spapr_ics_create(sPAPRMachineState *spapr, - const char *type_ics, - int nr_irqs, Error **errp) -{ - Error *local_err = NULL; - Object *obj; - - obj = object_new(type_ics); - object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort); - object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr), - &error_abort); - object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err); - if (local_err) { - goto error; - } - object_property_set_bool(obj, true, "realized", &local_err); - if (local_err) { - goto error; - } - - return ICS_BASE(obj); - -error: - error_propagate(errp, local_err); - return NULL; -} - static bool pre_2_10_vmstate_dummy_icp_needed(void *opaque) { /* Dummy entries correspond to unused ICPState objects in older QEMUs, @@ -178,44 +150,12 @@ static void pre_2_10_vmstate_unregister_dummy_icp(int i) (void *)(uintptr_t) i); } -static int xics_max_server_number(sPAPRMachineState *spapr) +int spapr_max_server_number(SpaprMachineState *spapr) { assert(spapr->vsmt); return DIV_ROUND_UP(max_cpus * spapr->vsmt, smp_threads); } -static void xics_system_init(MachineState *machine, int nr_irqs, Error **errp) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - Error *local_err = NULL; - - if (kvm_enabled()) { - if (machine_kernel_irqchip_allowed(machine) && - !xics_kvm_init(spapr, &local_err)) { - spapr->icp_type = TYPE_KVM_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_KVM, nr_irqs, - &local_err); - } - if (machine_kernel_irqchip_required(machine) && !spapr->ics) { - error_prepend(&local_err, - "kernel_irqchip requested but unavailable: "); - goto error; - } - error_free(local_err); - local_err = NULL; - } - - if (!spapr->ics) { - xics_spapr_init(spapr); - spapr->icp_type = TYPE_ICP; - spapr->ics = spapr_ics_create(spapr, TYPE_ICS_SIMPLE, nr_irqs, - &local_err); - } - -error: - error_propagate(errp, local_err); -} - static int spapr_fixup_cpu_smt_dt(void *fdt, int offset, PowerPCCPU *cpu, int smt_threads) { @@ -265,7 +205,7 @@ static int spapr_fixup_cpu_numa_dt(void *fdt, int offset, PowerPCCPU *cpu) } /* Populate the "ibm,pa-features" property */ -static void spapr_populate_pa_features(sPAPRMachineState *spapr, +static void spapr_populate_pa_features(SpaprMachineState *spapr, PowerPCCPU *cpu, void *fdt, int offset, bool legacy_guest) @@ -344,7 +284,7 @@ static void spapr_populate_pa_features(sPAPRMachineState *spapr, _FDT((fdt_setprop(fdt, offset, "ibm,pa-features", pa_features, pa_size))); } -static int spapr_fixup_cpu_dt(void *fdt, sPAPRMachineState *spapr) +static int spapr_fixup_cpu_dt(void *fdt, SpaprMachineState *spapr) { int ret = 0, offset, cpus_offset; CPUState *cs; @@ -447,7 +387,7 @@ static int spapr_populate_memory_node(void *fdt, int nodeid, hwaddr start, return off; } -static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt) +static int spapr_populate_memory(SpaprMachineState *spapr, void *fdt) { MachineState *machine = MACHINE(spapr); hwaddr mem_start, node_size; @@ -499,7 +439,7 @@ static int spapr_populate_memory(sPAPRMachineState *spapr, void *fdt) } static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, - sPAPRMachineState *spapr) + SpaprMachineState *spapr) { PowerPCCPU *cpu = POWERPC_CPU(cs); CPUPPCState *env = &cpu->env; @@ -515,7 +455,7 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, uint32_t vcpus_per_socket = smp_threads * smp_cores; uint32_t pft_size_prop[] = {0, cpu_to_be32(spapr->htab_shift)}; int compat_smt = MIN(smp_threads, ppc_compat_max_vthreads(cpu)); - sPAPRDRConnector *drc; + SpaprDrc *drc; int drc_index; uint32_t radix_AP_encodings[PPC_PAGE_SIZES_MAX_SZ]; int i; @@ -618,13 +558,24 @@ static void spapr_populate_cpu_dt(CPUState *cs, void *fdt, int offset, pcc->radix_page_info->count * sizeof(radix_AP_encodings[0])))); } + + /* + * We set this property to let the guest know that it can use the large + * decrementer and its width in bits. + */ + if (spapr_get_cap(spapr, SPAPR_CAP_LARGE_DECREMENTER) != SPAPR_CAP_OFF) + _FDT((fdt_setprop_u32(fdt, offset, "ibm,dec-bits", + pcc->lrg_decr_bits))); } -static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) +static void spapr_populate_cpus_dt_node(void *fdt, SpaprMachineState *spapr) { + CPUState **rev; CPUState *cs; + int n_cpus; int cpus_offset; char *nodename; + int i; cpus_offset = fdt_add_subnode(fdt, 0, "cpus"); _FDT(cpus_offset); @@ -635,8 +586,19 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) * We walk the CPUs in reverse order to ensure that CPU DT nodes * created by fdt_add_subnode() end up in the right order in FDT * for the guest kernel the enumerate the CPUs correctly. + * + * The CPU list cannot be traversed in reverse order, so we need + * to do extra work. */ - CPU_FOREACH_REVERSE(cs) { + n_cpus = 0; + rev = NULL; + CPU_FOREACH(cs) { + rev = g_renew(CPUState *, rev, n_cpus + 1); + rev[n_cpus++] = cs; + } + + for (i = n_cpus - 1; i >= 0; i--) { + CPUState *cs = rev[i]; PowerPCCPU *cpu = POWERPC_CPU(cs); int index = spapr_get_vcpu_id(cpu); DeviceClass *dc = DEVICE_GET_CLASS(cs); @@ -653,6 +615,30 @@ static void spapr_populate_cpus_dt_node(void *fdt, sPAPRMachineState *spapr) spapr_populate_cpu_dt(cs, fdt, offset, spapr); } + g_free(rev); +} + +static 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 uint32_t spapr_pc_dimm_node(MemoryDeviceInfoList *list, ram_addr_t addr) @@ -706,19 +692,19 @@ spapr_get_drconf_cell(uint32_t seq_lmbs, uint64_t base_addr, } /* ibm,dynamic-memory-v2 */ -static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, +static int spapr_populate_drmem_v2(SpaprMachineState *spapr, void *fdt, int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); - uint8_t *int_buf, *cur_index, buf_len; + uint8_t *int_buf, *cur_index; int ret; uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; uint64_t addr, cur_addr, size; uint32_t nr_boot_lmbs = (machine->device_memory->base / lmb_size); uint64_t mem_end = machine->device_memory->base + memory_region_size(&machine->device_memory->mr); - uint32_t node, nr_entries = 0; - sPAPRDRConnector *drc; + uint32_t node, buf_len, nr_entries = 0; + SpaprDrc *drc; DrconfCellQueue *elem, *next; MemoryDeviceInfoList *info; QSIMPLEQ_HEAD(, DrconfCellQueue) drconf_queue @@ -791,7 +777,7 @@ static int spapr_populate_drmem_v2(sPAPRMachineState *spapr, void *fdt, } /* ibm,dynamic-memory */ -static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, +static int spapr_populate_drmem_v1(SpaprMachineState *spapr, void *fdt, int offset, MemoryDeviceInfoList *dimms) { MachineState *machine = MACHINE(spapr); @@ -815,7 +801,7 @@ static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, uint32_t *dynamic_memory = cur_index; if (i >= device_lmb_start) { - sPAPRDRConnector *drc; + SpaprDrc *drc; drc = spapr_drc_by_id(TYPE_SPAPR_DRC_LMB, i); g_assert(drc); @@ -860,7 +846,7 @@ static int spapr_populate_drmem_v1(sPAPRMachineState *spapr, void *fdt, * 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) +static int spapr_populate_drconf_memory(SpaprMachineState *spapr, void *fdt) { MachineState *machine = MACHINE(spapr); int ret, i, offset; @@ -911,8 +897,6 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) /* ibm,associativity-lookup-arrays */ buf_len = (nr_nodes * 4 + 2) * sizeof(uint32_t); cur_index = int_buf = g_malloc0(buf_len); - - 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; @@ -933,10 +917,10 @@ static int spapr_populate_drconf_memory(sPAPRMachineState *spapr, void *fdt) return ret; } -static int spapr_dt_cas_updates(sPAPRMachineState *spapr, void *fdt, - sPAPROptionVector *ov5_updates) +static int spapr_dt_cas_updates(SpaprMachineState *spapr, void *fdt, + SpaprOptionVector *ov5_updates) { - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); int ret = 0, offset; /* Generate ibm,dynamic-reconfiguration-memory node if required */ @@ -982,12 +966,12 @@ static bool spapr_hotplugged_dev_before_cas(void) return false; } -int spapr_h_cas_compose_response(sPAPRMachineState *spapr, +int spapr_h_cas_compose_response(SpaprMachineState *spapr, target_ulong addr, target_ulong size, - sPAPROptionVector *ov5_updates) + SpaprOptionVector *ov5_updates) { void *fdt, *fdt_skel; - sPAPRDeviceTreeUpdateHeader hdr = { .version_id = 1 }; + SpaprDeviceTreeUpdateHeader hdr = { .version_id = 1 }; if (spapr_hotplugged_dev_before_cas()) { return 1; @@ -1036,7 +1020,7 @@ int spapr_h_cas_compose_response(sPAPRMachineState *spapr, return 0; } -static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) +static void spapr_dt_rtas(SpaprMachineState *spapr, void *fdt) { int rtas; GString *hypertas = g_string_sized_new(256); @@ -1055,7 +1039,7 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) cpu_to_be32(0), cpu_to_be32(0), cpu_to_be32(0), - cpu_to_be32(nb_numa_nodes ? nb_numa_nodes - 1 : 0), + cpu_to_be32(nb_numa_nodes ? nb_numa_nodes : 1), }; _FDT(rtas = fdt_add_subnode(fdt, 0, "rtas")); @@ -1073,6 +1057,7 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) add_str(hypertas, "hcall-sprg0"); add_str(hypertas, "hcall-copy"); add_str(hypertas, "hcall-debug"); + add_str(hypertas, "hcall-vphn"); add_str(qemu_hypertas, "hcall-memop1"); if (!kvm_enabled() || kvmppc_spapr_use_multitce()) { @@ -1119,15 +1104,18 @@ static void spapr_dt_rtas(sPAPRMachineState *spapr, void *fdt) spapr_dt_rtas_tokens(fdt, rtas); } -/* Prepare ibm,arch-vec-5-platform-support, which indicates the MMU features - * that the guest may request and thus the valid values for bytes 24..26 of - * option vector 5: */ -static void spapr_dt_ov5_platform_support(void *fdt, int chosen) +/* + * Prepare ibm,arch-vec-5-platform-support, which indicates the MMU + * and the XIVE features that the guest may request and thus the valid + * values for bytes 23..26 of option vector 5: + */ +static void spapr_dt_ov5_platform_support(SpaprMachineState *spapr, void *fdt, + int chosen) { PowerPCCPU *first_ppc_cpu = POWERPC_CPU(first_cpu); char val[2 * 4] = { - 23, 0x00, /* Xive mode, filled in below. */ + 23, spapr->irq->ov5, /* Xive mode. */ 24, 0x00, /* Hash/Radix, filled in below. */ 25, 0x00, /* Hash options: Segment Tables == no, GTSE == no. */ 26, 0x40, /* Radix options: GTSE == yes. */ @@ -1135,7 +1123,11 @@ static void spapr_dt_ov5_platform_support(void *fdt, int chosen) if (!ppc_check_compat(first_ppc_cpu, CPU_POWERPC_LOGICAL_3_00, 0, first_ppc_cpu->compat_pvr)) { - /* If we're in a pre POWER9 compat mode then the guest should do hash */ + /* + * If we're in a pre POWER9 compat mode then the guest should + * do hash and use the legacy interrupt mode + */ + val[1] = 0x00; /* XICS */ val[3] = 0x00; /* Hash */ } else if (kvm_enabled()) { if (kvmppc_has_cap_mmu_radix() && kvmppc_has_cap_mmu_hash_v3()) { @@ -1153,14 +1145,14 @@ static void spapr_dt_ov5_platform_support(void *fdt, int chosen) val, sizeof(val))); } -static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) +static void spapr_dt_chosen(SpaprMachineState *spapr, void *fdt) { MachineState *machine = MACHINE(spapr); int chosen; const char *boot_device = machine->boot_order; char *stdout_path = spapr_vio_stdout_path(spapr->vio_bus); size_t cb = 0; - char *bootlist = get_boot_devices_list(&cb, true); + char *bootlist = get_boot_devices_list(&cb); _FDT(chosen = fdt_add_subnode(fdt, 0, "chosen")); @@ -1213,13 +1205,13 @@ static void spapr_dt_chosen(sPAPRMachineState *spapr, void *fdt) _FDT(fdt_setprop_string(fdt, chosen, "stdout-path", stdout_path)); } - spapr_dt_ov5_platform_support(fdt, chosen); + spapr_dt_ov5_platform_support(spapr, fdt, chosen); g_free(stdout_path); g_free(bootlist); } -static void spapr_dt_hypervisor(sPAPRMachineState *spapr, void *fdt) +static void spapr_dt_hypervisor(SpaprMachineState *spapr, void *fdt) { /* The /hypervisor node isn't in PAPR - this is a hack to allow PR * KVM to work under pHyp with some guest co-operation */ @@ -1242,16 +1234,14 @@ static void spapr_dt_hypervisor(sPAPRMachineState *spapr, void *fdt) } } -static void *spapr_build_fdt(sPAPRMachineState *spapr, - hwaddr rtas_addr, - hwaddr rtas_size) +static void *spapr_build_fdt(SpaprMachineState *spapr) { MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); int ret; void *fdt; - sPAPRPHBState *phb; + SpaprPhbState *phb; char *buf; fdt = g_malloc0(FDT_MAX_SIZE); @@ -1262,21 +1252,8 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, _FDT(fdt_setprop_string(fdt, 0, "model", "IBM pSeries (emulated by qemu)")); _FDT(fdt_setprop_string(fdt, 0, "compatible", "qemu,pseries")); - /* - * Add info to guest to indentify which host is it being run on - * and what is the uuid of the guest - */ - if (kvmppc_get_host_model(&buf)) { - _FDT(fdt_setprop_string(fdt, 0, "host-model", buf)); - g_free(buf); - } - if (kvmppc_get_host_serial(&buf)) { - _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); - g_free(buf); - } - + /* Guest UUID & Name*/ buf = qemu_uuid_unparse_strdup(&qemu_uuid); - _FDT(fdt_setprop_string(fdt, 0, "vm,uuid", buf)); if (qemu_uuid_set) { _FDT(fdt_setprop_string(fdt, 0, "system-id", buf)); @@ -1288,11 +1265,27 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, qemu_get_vm_name())); } + /* Host Model & Serial Number */ + if (spapr->host_model) { + _FDT(fdt_setprop_string(fdt, 0, "host-model", spapr->host_model)); + } else if (smc->broken_host_serial_model && kvmppc_get_host_model(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-model", buf)); + g_free(buf); + } + + if (spapr->host_serial) { + _FDT(fdt_setprop_string(fdt, 0, "host-serial", spapr->host_serial)); + } else if (smc->broken_host_serial_model && kvmppc_get_host_serial(&buf)) { + _FDT(fdt_setprop_string(fdt, 0, "host-serial", buf)); + g_free(buf); + } + _FDT(fdt_setprop_cell(fdt, 0, "#address-cells", 2)); _FDT(fdt_setprop_cell(fdt, 0, "#size-cells", 2)); /* /interrupt controller */ - spapr_dt_xics(xics_max_server_number(spapr), fdt, PHANDLE_XICP); + spapr->irq->dt_populate(spapr, spapr_max_server_number(spapr), fdt, + PHANDLE_INTC); ret = spapr_populate_memory(spapr, fdt); if (ret < 0) { @@ -1312,7 +1305,8 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, } QLIST_FOREACH(phb, &spapr->phbs, list) { - ret = spapr_populate_pci_dt(phb, PHANDLE_XICP, fdt); + ret = spapr_populate_pci_dt(phb, PHANDLE_INTC, fdt, + spapr->irq->nr_msis, NULL); if (ret < 0) { error_report("couldn't setup PCI devices in fdt"); exit(1); @@ -1365,6 +1359,14 @@ static void *spapr_build_fdt(sPAPRMachineState *spapr, exit(1); } + if (smc->dr_phb_enabled) { + ret = spapr_drc_populate_dt(fdt, 0, NULL, SPAPR_DR_CONNECTOR_TYPE_PHB); + if (ret < 0) { + error_report("Couldn't set up PHB DR device tree properties"); + exit(1); + } + } + return fdt; } @@ -1389,11 +1391,44 @@ static void emulate_spapr_hypercall(PPCVirtualHypervisor *vhyp, } } -static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); +struct LPCRSyncState { + target_ulong value; + target_ulong mask; +}; - return spapr->patb_entry; +static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg) +{ + struct LPCRSyncState *s = arg.host_ptr; + PowerPCCPU *cpu = POWERPC_CPU(cs); + CPUPPCState *env = &cpu->env; + target_ulong lpcr; + + cpu_synchronize_state(cs); + lpcr = env->spr[SPR_LPCR]; + lpcr &= ~s->mask; + lpcr |= s->value; + ppc_store_lpcr(cpu, lpcr); +} + +void spapr_set_all_lpcrs(target_ulong value, target_ulong mask) +{ + CPUState *cs; + struct LPCRSyncState s = { + .value = value, + .mask = mask + }; + CPU_FOREACH(cs) { + run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s)); + } +} + +static void spapr_get_pate(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(vhyp); + + /* Copy PATE1:GR into PATE0:HR */ + entry->dw0 = spapr->patb_entry & PATE0_HR; + entry->dw1 = spapr->patb_entry; } #define HPTE(_table, _i) (void *)(((uint64_t *)(_table)) + ((_i) * 2)) @@ -1405,7 +1440,7 @@ static uint64_t spapr_get_patbe(PPCVirtualHypervisor *vhyp) /* * Get the fd to access the kernel htab, re-opening it if necessary */ -static int get_htab_fd(sPAPRMachineState *spapr) +static int get_htab_fd(SpaprMachineState *spapr) { Error *local_err = NULL; @@ -1421,7 +1456,7 @@ static int get_htab_fd(sPAPRMachineState *spapr) return spapr->htab_fd; } -void close_htab_fd(sPAPRMachineState *spapr) +void close_htab_fd(SpaprMachineState *spapr) { if (spapr->htab_fd >= 0) { close(spapr->htab_fd); @@ -1431,14 +1466,14 @@ void close_htab_fd(sPAPRMachineState *spapr) static hwaddr spapr_hpt_mask(PPCVirtualHypervisor *vhyp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); + SpaprMachineState *spapr = SPAPR_MACHINE(vhyp); return HTAB_SIZE(spapr) / HASH_PTEG_SIZE_64 - 1; } static target_ulong spapr_encode_hpt_for_kvm_pr(PPCVirtualHypervisor *vhyp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); + SpaprMachineState *spapr = SPAPR_MACHINE(vhyp); assert(kvm_enabled()); @@ -1452,7 +1487,7 @@ static target_ulong spapr_encode_hpt_for_kvm_pr(PPCVirtualHypervisor *vhyp) static const ppc_hash_pte64_t *spapr_map_hptes(PPCVirtualHypervisor *vhyp, hwaddr ptex, int n) { - sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); + SpaprMachineState *spapr = SPAPR_MACHINE(vhyp); hwaddr pte_offset = ptex * HASH_PTE_SIZE_64; if (!spapr->htab) { @@ -1475,7 +1510,7 @@ static void spapr_unmap_hptes(PPCVirtualHypervisor *vhyp, const ppc_hash_pte64_t *hptes, hwaddr ptex, int n) { - sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); + SpaprMachineState *spapr = SPAPR_MACHINE(vhyp); if (!spapr->htab) { g_free((void *)hptes); @@ -1487,14 +1522,31 @@ static void spapr_unmap_hptes(PPCVirtualHypervisor *vhyp, static void spapr_store_hpte(PPCVirtualHypervisor *vhyp, hwaddr ptex, uint64_t pte0, uint64_t pte1) { - sPAPRMachineState *spapr = SPAPR_MACHINE(vhyp); + SpaprMachineState *spapr = SPAPR_MACHINE(vhyp); hwaddr offset = ptex * HASH_PTE_SIZE_64; if (!spapr->htab) { kvmppc_write_hpte(ptex, pte0, pte1); } else { - stq_p(spapr->htab + offset, pte0); - stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1); + if (pte0 & HPTE64_V_VALID) { + stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1); + /* + * When setting valid, we write PTE1 first. This ensures + * proper synchronization with the reading code in + * ppc_hash64_pteg_search() + */ + smp_wmb(); + stq_p(spapr->htab + offset, pte0); + } else { + stq_p(spapr->htab + offset, pte0); + /* + * When clearing it we set PTE0 first. This ensures proper + * synchronization with the reading code in + * ppc_hash64_pteg_search() + */ + smp_wmb(); + stq_p(spapr->htab + offset + HASH_PTE_SIZE_64 / 2, pte1); + } } } @@ -1511,7 +1563,7 @@ int spapr_hpt_shift_for_ramsize(uint64_t ramsize) return shift; } -void spapr_free_hpt(sPAPRMachineState *spapr) +void spapr_free_hpt(SpaprMachineState *spapr) { g_free(spapr->htab); spapr->htab = NULL; @@ -1519,7 +1571,7 @@ void spapr_free_hpt(sPAPRMachineState *spapr) close_htab_fd(spapr); } -void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, +void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp) { long rc; @@ -1566,9 +1618,10 @@ void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, } /* We're setting up a hash table, so that means we're not radix */ spapr->patb_entry = 0; + spapr_set_all_lpcrs(0, LPCR_HR | LPCR_UPRT); } -void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) +void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr) { int hpt_shift; @@ -1592,8 +1645,8 @@ void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr) static int spapr_reset_drcs(Object *child, void *opaque) { - sPAPRDRConnector *drc = - (sPAPRDRConnector *) object_dynamic_cast(child, + SpaprDrc *drc = + (SpaprDrc *) object_dynamic_cast(child, TYPE_SPAPR_DR_CONNECTOR); if (drc) { @@ -1606,7 +1659,7 @@ static int spapr_reset_drcs(Object *child, void *opaque) static void spapr_machine_reset(void) { MachineState *machine = MACHINE(qdev_get_machine()); - sPAPRMachineState *spapr = SPAPR_MACHINE(machine); + SpaprMachineState *spapr = SPAPR_MACHINE(machine); PowerPCCPU *first_ppc_cpu; uint32_t rtas_limit; hwaddr rtas_addr, fdt_addr; @@ -1619,16 +1672,21 @@ static void spapr_machine_reset(void) if (kvm_enabled() && kvmppc_has_cap_mmu_radix() && ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, 0, spapr->max_compat_pvr)) { - /* If using KVM with radix mode available, VCPUs can be started + /* + * If using KVM with radix mode available, VCPUs can be started * without a HPT because KVM will start them in radix mode. - * Set the GR bit in PATB so that we know there is no HPT. */ - spapr->patb_entry = PATBE1_GR; + * Set the GR bit in PATE so that we know there is no HPT. + */ + spapr->patb_entry = PATE1_GR; + spapr_set_all_lpcrs(LPCR_HR | LPCR_UPRT, LPCR_HR | LPCR_UPRT); } else { spapr_setup_hpt_and_vrma(spapr); } - /* if this reset wasn't generated by CAS, we should reset our - * negotiated options and start from scratch */ + /* + * If this reset wasn't generated by CAS, we should reset our + * negotiated options and start from scratch + */ if (!spapr->cas_reboot) { spapr_ovec_cleanup(spapr->ov5_cas); spapr->ov5_cas = spapr_ovec_new(); @@ -1636,8 +1694,28 @@ static void spapr_machine_reset(void) ppc_set_compat(first_ppc_cpu, spapr->max_compat_pvr, &error_fatal); } + if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { + spapr_irq_msi_reset(spapr); + } + qemu_devices_reset(); + /* + * This is fixing some of the default configuration of the XIVE + * devices. To be called after the reset of the machine devices. + */ + spapr_irq_reset(spapr, &error_fatal); + + /* + * There is no CAS under qtest. Simulate one to please the code that + * depends on spapr->ov5_cas. This is especially needed to test device + * unplug, so we do that before resetting the DRCs. + */ + if (qtest_enabled()) { + spapr_ovec_cleanup(spapr->ov5_cas); + spapr->ov5_cas = spapr_ovec_clone(spapr->ov5); + } + /* DRC reset may cause a device to be unplugged. This will cause troubles * if this device is used by another device (eg, a running vhost backend * will crash QEMU if the DIMM holding the vring goes away). To avoid such @@ -1649,14 +1727,14 @@ static void spapr_machine_reset(void) /* * We place the device tree and RTAS just below either the top of the RMA, - * or just below 2GB, whichever is lowere, so that it can be + * or just below 2GB, whichever is lower, so that it can be * processed with 32-bit real mode code if necessary */ rtas_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR); rtas_addr = rtas_limit - RTAS_MAX_SIZE; fdt_addr = rtas_addr - FDT_MAX_SIZE; - fdt = spapr_build_fdt(spapr, rtas_addr, spapr->rtas_size); + fdt = spapr_build_fdt(spapr); spapr_load_rtas(spapr, fdt, rtas_addr); @@ -1674,7 +1752,10 @@ static void spapr_machine_reset(void) /* Load the fdt */ qemu_fdt_dumpdtb(fdt, fdt_totalsize(fdt)); cpu_physical_memory_write(fdt_addr, fdt, fdt_totalsize(fdt)); - g_free(fdt); + g_free(spapr->fdt_blob); + spapr->fdt_size = fdt_totalsize(fdt); + spapr->fdt_initial_size = spapr->fdt_size; + spapr->fdt_blob = fdt; /* Set up the entry state */ spapr_cpu_set_entry_state(first_ppc_cpu, SPAPR_ENTRY_POINT, fdt_addr); @@ -1683,7 +1764,7 @@ static void spapr_machine_reset(void) spapr->cas_reboot = false; } -static void spapr_create_nvram(sPAPRMachineState *spapr) +static void spapr_create_nvram(SpaprMachineState *spapr) { DeviceState *dev = qdev_create(&spapr->vio_bus->bus, "spapr-nvram"); DriveInfo *dinfo = drive_get(IF_PFLASH, 0, 0); @@ -1695,14 +1776,14 @@ static void spapr_create_nvram(sPAPRMachineState *spapr) qdev_init_nofail(dev); - spapr->nvram = (struct sPAPRNVRAM *)dev; + spapr->nvram = (struct SpaprNvram *)dev; } -static void spapr_rtc_create(sPAPRMachineState *spapr) +static void spapr_rtc_create(SpaprMachineState *spapr) { - object_initialize(&spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC); - object_property_add_child(OBJECT(spapr), "rtc", OBJECT(&spapr->rtc), - &error_fatal); + object_initialize_child(OBJECT(spapr), "rtc", + &spapr->rtc, sizeof(spapr->rtc), TYPE_SPAPR_RTC, + &error_fatal, NULL); object_property_set_bool(OBJECT(&spapr->rtc), true, "realized", &error_fatal); object_property_add_alias(OBJECT(spapr), "rtc-time", OBJECT(&spapr->rtc), @@ -1719,6 +1800,7 @@ static bool spapr_vga_init(PCIBus *pci_bus, Error **errp) return true; case VGA_STD: case VGA_VIRTIO: + case VGA_CIRRUS: return pci_vga_init(pci_bus) != NULL; default: error_setg(errp, @@ -1741,7 +1823,7 @@ static int spapr_pre_load(void *opaque) static int spapr_post_load(void *opaque, int version_id) { - sPAPRMachineState *spapr = (sPAPRMachineState *)opaque; + SpaprMachineState *spapr = (SpaprMachineState *)opaque; int err = 0; err = spapr_caps_post_migration(spapr); @@ -1749,27 +1831,31 @@ static int spapr_post_load(void *opaque, int version_id) return err; } - if (!object_dynamic_cast(OBJECT(spapr->ics), TYPE_ICS_KVM)) { - CPUState *cs; - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - icp_resend(ICP(cpu->intc)); - } - } - - /* In earlier versions, there was no separate qdev for the PAPR + /* + * In earlier versions, there was no separate qdev for the PAPR * RTC, so the RTC offset was stored directly in sPAPREnvironment. * So when migrating from those versions, poke the incoming offset - * value into the RTC device */ + * value into the RTC device + */ if (version_id < 3) { err = spapr_rtc_import_offset(&spapr->rtc, spapr->rtc_offset); + if (err) { + return err; + } } if (kvm_enabled() && spapr->patb_entry) { PowerPCCPU *cpu = POWERPC_CPU(first_cpu); - bool radix = !!(spapr->patb_entry & PATBE1_GR); + bool radix = !!(spapr->patb_entry & PATE1_GR); bool gtse = !!(cpu->env.spr[SPR_LPCR] & LPCR_GTSE); + /* + * Update LPCR:HR and UPRT as they may not be set properly in + * the stream + */ + spapr_set_all_lpcrs(radix ? (LPCR_HR | LPCR_UPRT) : 0, + LPCR_HR | LPCR_UPRT); + err = kvmppc_configure_v3_mmu(cpu, radix, gtse, spapr->patb_entry); if (err) { error_report("Process table config unsupported by the host"); @@ -1777,6 +1863,11 @@ static int spapr_post_load(void *opaque, int version_id) } } + err = spapr_irq_post_load(spapr, version_id); + if (err) { + return err; + } + return err; } @@ -1799,7 +1890,7 @@ static bool version_before_3(void *opaque, int version_id) static bool spapr_pending_events_needed(void *opaque) { - sPAPRMachineState *spapr = (sPAPRMachineState *)opaque; + SpaprMachineState *spapr = (SpaprMachineState *)opaque; return !QTAILQ_EMPTY(&spapr->pending_events); } @@ -1808,9 +1899,9 @@ static const VMStateDescription vmstate_spapr_event_entry = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_UINT32(summary, sPAPREventLogEntry), - VMSTATE_UINT32(extended_length, sPAPREventLogEntry), - VMSTATE_VBUFFER_ALLOC_UINT32(extended_log, sPAPREventLogEntry, 0, + VMSTATE_UINT32(summary, SpaprEventLogEntry), + VMSTATE_UINT32(extended_length, SpaprEventLogEntry), + VMSTATE_VBUFFER_ALLOC_UINT32(extended_log, SpaprEventLogEntry, 0, NULL, extended_length), VMSTATE_END_OF_LIST() }, @@ -1822,21 +1913,21 @@ static const VMStateDescription vmstate_spapr_pending_events = { .minimum_version_id = 1, .needed = spapr_pending_events_needed, .fields = (VMStateField[]) { - VMSTATE_QTAILQ_V(pending_events, sPAPRMachineState, 1, - vmstate_spapr_event_entry, sPAPREventLogEntry, next), + VMSTATE_QTAILQ_V(pending_events, SpaprMachineState, 1, + vmstate_spapr_event_entry, SpaprEventLogEntry, next), VMSTATE_END_OF_LIST() }, }; static bool spapr_ov5_cas_needed(void *opaque) { - sPAPRMachineState *spapr = opaque; - sPAPROptionVector *ov5_mask = spapr_ovec_new(); - sPAPROptionVector *ov5_legacy = spapr_ovec_new(); - sPAPROptionVector *ov5_removed = spapr_ovec_new(); + SpaprMachineState *spapr = opaque; + SpaprOptionVector *ov5_mask = spapr_ovec_new(); + SpaprOptionVector *ov5_legacy = spapr_ovec_new(); + SpaprOptionVector *ov5_removed = spapr_ovec_new(); bool cas_needed; - /* Prior to the introduction of sPAPROptionVector, we had two option + /* Prior to the introduction of SpaprOptionVector, we had two option * vectors we dealt with: OV5_FORM1_AFFINITY, and OV5_DRCONF_MEMORY. * Both of these options encode machine topology into the device-tree * in such a way that the now-booted OS should still be able to interact @@ -1886,15 +1977,15 @@ static const VMStateDescription vmstate_spapr_ov5_cas = { .minimum_version_id = 1, .needed = spapr_ov5_cas_needed, .fields = (VMStateField[]) { - VMSTATE_STRUCT_POINTER_V(ov5_cas, sPAPRMachineState, 1, - vmstate_spapr_ovec, sPAPROptionVector), + VMSTATE_STRUCT_POINTER_V(ov5_cas, SpaprMachineState, 1, + vmstate_spapr_ovec, SpaprOptionVector), VMSTATE_END_OF_LIST() }, }; static bool spapr_patb_entry_needed(void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; return !!spapr->patb_entry; } @@ -1905,7 +1996,58 @@ static const VMStateDescription vmstate_spapr_patb_entry = { .minimum_version_id = 1, .needed = spapr_patb_entry_needed, .fields = (VMStateField[]) { - VMSTATE_UINT64(patb_entry, sPAPRMachineState), + VMSTATE_UINT64(patb_entry, SpaprMachineState), + VMSTATE_END_OF_LIST() + }, +}; + +static bool spapr_irq_map_needed(void *opaque) +{ + SpaprMachineState *spapr = opaque; + + return spapr->irq_map && !bitmap_empty(spapr->irq_map, spapr->irq_map_nr); +} + +static const VMStateDescription vmstate_spapr_irq_map = { + .name = "spapr_irq_map", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_irq_map_needed, + .fields = (VMStateField[]) { + VMSTATE_BITMAP(irq_map, SpaprMachineState, 0, irq_map_nr), + VMSTATE_END_OF_LIST() + }, +}; + +static bool spapr_dtb_needed(void *opaque) +{ + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(opaque); + + return smc->update_dt_enabled; +} + +static int spapr_dtb_pre_load(void *opaque) +{ + SpaprMachineState *spapr = (SpaprMachineState *)opaque; + + g_free(spapr->fdt_blob); + spapr->fdt_blob = NULL; + spapr->fdt_size = 0; + + return 0; +} + +static const VMStateDescription vmstate_spapr_dtb = { + .name = "spapr_dtb", + .version_id = 1, + .minimum_version_id = 1, + .needed = spapr_dtb_needed, + .pre_load = spapr_dtb_pre_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(fdt_initial_size, SpaprMachineState), + VMSTATE_UINT32(fdt_size, SpaprMachineState), + VMSTATE_VBUFFER_ALLOC_UINT32(fdt_blob, SpaprMachineState, 0, NULL, + fdt_size), VMSTATE_END_OF_LIST() }, }; @@ -1922,9 +2064,9 @@ static const VMStateDescription vmstate_spapr = { VMSTATE_UNUSED_BUFFER(version_before_3, 0, 4), /* RTC offset */ - VMSTATE_UINT64_TEST(rtc_offset, sPAPRMachineState, version_before_3), + VMSTATE_UINT64_TEST(rtc_offset, SpaprMachineState, version_before_3), - VMSTATE_PPC_TIMEBASE_V(tb, sPAPRMachineState, 2), + VMSTATE_PPC_TIMEBASE_V(tb, SpaprMachineState, 2), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription*[]) { @@ -1937,13 +2079,18 @@ static const VMStateDescription vmstate_spapr = { &vmstate_spapr_cap_cfpc, &vmstate_spapr_cap_sbbc, &vmstate_spapr_cap_ibs, + &vmstate_spapr_irq_map, + &vmstate_spapr_cap_nested_kvm_hv, + &vmstate_spapr_dtb, + &vmstate_spapr_cap_large_decr, + &vmstate_spapr_cap_ccf_assist, NULL } }; static int htab_save_setup(QEMUFile *f, void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; /* "Iteration" header */ if (!spapr->htab_shift) { @@ -1965,7 +2112,7 @@ static int htab_save_setup(QEMUFile *f, void *opaque) return 0; } -static void htab_save_chunk(QEMUFile *f, sPAPRMachineState *spapr, +static void htab_save_chunk(QEMUFile *f, SpaprMachineState *spapr, int chunkstart, int n_valid, int n_invalid) { qemu_put_be32(f, chunkstart); @@ -1982,7 +2129,7 @@ static void htab_save_end_marker(QEMUFile *f) qemu_put_be16(f, 0); } -static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, +static void htab_save_first_pass(QEMUFile *f, SpaprMachineState *spapr, int64_t max_ns) { bool has_timeout = max_ns != -1; @@ -2030,7 +2177,7 @@ static void htab_save_first_pass(QEMUFile *f, sPAPRMachineState *spapr, spapr->htab_save_index = index; } -static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr, +static int htab_save_later_pass(QEMUFile *f, SpaprMachineState *spapr, int64_t max_ns) { bool final = max_ns < 0; @@ -2108,7 +2255,7 @@ static int htab_save_later_pass(QEMUFile *f, sPAPRMachineState *spapr, static int htab_save_iterate(QEMUFile *f, void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; int fd; int rc = 0; @@ -2145,7 +2292,7 @@ static int htab_save_iterate(QEMUFile *f, void *opaque) static int htab_save_complete(QEMUFile *f, void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; int fd; /* Iteration header */ @@ -2185,7 +2332,7 @@ static int htab_save_complete(QEMUFile *f, void *opaque) static int htab_load(QEMUFile *f, void *opaque, int version_id) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; uint32_t section_hdr; int fd = -1; Error *local_err = NULL; @@ -2275,7 +2422,7 @@ static int htab_load(QEMUFile *f, void *opaque, int version_id) static void htab_save_cleanup(void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; close_htab_fd(spapr); } @@ -2295,7 +2442,7 @@ static void spapr_boot_set(void *opaque, const char *boot_device, machine->boot_order = g_strdup(boot_device); } -static void spapr_create_lmb_dr_connectors(sPAPRMachineState *spapr) +static void spapr_create_lmb_dr_connectors(SpaprMachineState *spapr) { MachineState *machine = MACHINE(spapr); uint64_t lmb_size = SPAPR_MEMORY_BLOCK_SIZE; @@ -2362,7 +2509,7 @@ static CPUArchId *spapr_find_cpu_slot(MachineState *ms, uint32_t id, int *idx) return &ms->possible_cpus->cpus[index]; } -static void spapr_set_vsmt_mode(sPAPRMachineState *spapr, Error **errp) +static void spapr_set_vsmt_mode(SpaprMachineState *spapr, Error **errp) { Error *local_err = NULL; bool vsmt_user = !!spapr->vsmt; @@ -2434,11 +2581,11 @@ out: error_propagate(errp, local_err); } -static void spapr_init_cpus(sPAPRMachineState *spapr) +static void spapr_init_cpus(SpaprMachineState *spapr) { MachineState *machine = MACHINE(spapr); MachineClass *mc = MACHINE_GET_CLASS(machine); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *type = spapr_get_cpu_core_type(machine->cpu_type); const CPUArchIdList *possible_cpus; int boot_cores_nr = smp_cpus / smp_threads; @@ -2464,15 +2611,10 @@ static void spapr_init_cpus(sPAPRMachineState *spapr) boot_cores_nr = possible_cpus->len; } - /* VSMT must be set in order to be able to compute VCPU ids, ie to - * call xics_max_server_number() or spapr_vcpu_id(). - */ - spapr_set_vsmt_mode(spapr, &error_fatal); - if (smc->pre_2_10_has_unused_icps) { int i; - for (i = 0; i < xics_max_server_number(spapr); i++) { + for (i = 0; i < spapr_max_server_number(spapr); i++) { /* Dummy entries get deregistered when real ICPState objects * are registered during CPU core hotplug. */ @@ -2502,15 +2644,28 @@ static void spapr_init_cpus(sPAPRMachineState *spapr) object_property_set_int(core, core_id, CPU_CORE_PROP_CORE_ID, &error_fatal); object_property_set_bool(core, true, "realized", &error_fatal); + + object_unref(core); } } } +static PCIHostState *spapr_create_default_phb(void) +{ + DeviceState *dev; + + dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); + qdev_prop_set_uint32(dev, "index", 0); + qdev_init_nofail(dev); + + return PCI_HOST_BRIDGE(dev); +} + /* pSeries LPAR / sPAPR hardware init */ static void spapr_machine_init(MachineState *machine) { - sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); + SpaprMachineState *spapr = SPAPR_MACHINE(machine); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(machine); const char *kernel_filename = machine->kernel_filename; const char *initrd_filename = machine->initrd_filename; PCIHostState *phb; @@ -2589,8 +2744,14 @@ static void spapr_machine_init(MachineState *machine) /* Setup a load limit for the ramdisk leaving room for SLOF and FDT */ load_limit = MIN(spapr->rma_size, RTAS_MAX_ADDR) - FW_OVERHEAD; + /* + * VSMT must be set in order to be able to compute VCPU ids, ie to + * call spapr_max_server_number() or spapr_vcpu_id(). + */ + spapr_set_vsmt_mode(spapr, &error_fatal); + /* Set up Interrupt Controller before we create the VCPUs */ - xics_system_init(machine, XICS_IRQS_SPAPR, &error_fatal); + spapr_irq_init(spapr, &error_fatal); /* Set up containers for ibm,client-architecture-support negotiated options */ @@ -2617,6 +2778,11 @@ static void spapr_machine_init(MachineState *machine) /* advertise support for ibm,dyamic-memory-v2 */ spapr_ovec_set(spapr->ov5, OV5_DRMEM_V2); + /* advertise XIVE on POWER9 machines */ + if (spapr->irq->ov5 & (SPAPR_OV5_XIVE_EXPLOIT | SPAPR_OV5_XIVE_BOTH)) { + spapr_ovec_set(spapr->ov5, OV5_XIVE_EXPLOIT); + } + /* init CPUs */ spapr_init_cpus(spapr); @@ -2635,6 +2801,9 @@ static void spapr_machine_init(MachineState *machine) /* H_CLEAR_MOD/_REF are mandatory in PAPR, but off by default */ kvmppc_enable_clear_ref_mod_hcalls(); + + /* Enable H_PAGE_INIT */ + kvmppc_enable_h_page_init(); } /* allocate RAM */ @@ -2718,10 +2887,23 @@ static void spapr_machine_init(MachineState *machine) /* We always have at least the nvram device on VIO */ spapr_create_nvram(spapr); + /* + * Setup hotplug / dynamic-reconfiguration connectors. top-level + * connectors (described in root DT node's "ibm,drc-types" property) + * are pre-initialized here. additional child connectors (such as + * connectors for a PHBs PCI slots) are added as needed during their + * parent's realization. + */ + if (smc->dr_phb_enabled) { + for (i = 0; i < SPAPR_MAX_PHBS; i++) { + spapr_dr_connector_new(OBJECT(machine), TYPE_SPAPR_DRC_PHB, i); + } + } + /* Set up PCI */ spapr_pci_rtas_init(); - phb = spapr_create_phb(spapr, 0); + phb = spapr_create_default_phb(); for (i = 0; i < nb_nics; i++) { NICInfo *nd = &nd_table[i]; @@ -2773,11 +2955,12 @@ static void spapr_machine_init(MachineState *machine) if (kernel_filename) { uint64_t lowaddr = 0; - spapr->kernel_size = load_elf(kernel_filename, translate_kernel_address, - NULL, NULL, &lowaddr, NULL, 1, + spapr->kernel_size = load_elf(kernel_filename, NULL, + translate_kernel_address, NULL, + NULL, &lowaddr, NULL, 1, PPC_ELF_MACHINE, 0, 0); if (spapr->kernel_size == ELF_LOAD_WRONG_ENDIAN) { - spapr->kernel_size = load_elf(kernel_filename, + spapr->kernel_size = load_elf(kernel_filename, NULL, translate_kernel_address, NULL, NULL, &lowaddr, NULL, 0, PPC_ELF_MACHINE, 0, 0); @@ -2830,6 +3013,9 @@ static void spapr_machine_init(MachineState *machine) register_savevm_live(NULL, "spapr/htab", -1, 1, &savevm_htab_handlers, spapr); + qbus_set_hotplug_handler(sysbus_get_default(), OBJECT(machine), + &error_fatal); + qemu_register_boot_set(spapr_boot_set, spapr); if (kvm_enabled()) { @@ -2841,7 +3027,7 @@ static void spapr_machine_init(MachineState *machine) } } -static int spapr_kvm_type(const char *vm_type) +static int spapr_kvm_type(MachineState *machine, const char *vm_type) { if (!vm_type) { return 0; @@ -2869,7 +3055,7 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, #define CAST(type, obj, name) \ ((type *)object_dynamic_cast(OBJECT(obj), (name))) SCSIDevice *d = CAST(SCSIDevice, dev, TYPE_SCSI_DEVICE); - sPAPRPHBState *phb = CAST(sPAPRPHBState, dev, TYPE_SPAPR_PCI_HOST_BRIDGE); + SpaprPhbState *phb = CAST(SpaprPhbState, dev, TYPE_SPAPR_PCI_HOST_BRIDGE); VHostSCSICommon *vsc = CAST(VHostSCSICommon, dev, TYPE_VHOST_SCSI_COMMON); if (d) { @@ -2880,10 +3066,11 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, if (spapr) { /* * Replace "channel@0/disk@0,0" with "disk@8000000000000000": - * We use SRP luns of the form 8000 | (bus << 8) | (id << 5) | lun - * in the top 16 bits of the 64-bit LUN + * In the top 16 bits of the 64-bit LUN, we use SRP luns of the form + * 0x8000 | (target << 8) | (bus << 5) | lun + * (see the "Logical unit addressing format" table in SAM5) */ - unsigned id = 0x8000 | (d->id << 8) | d->lun; + unsigned id = 0x8000 | (d->id << 8) | (d->channel << 5) | d->lun; return g_strdup_printf("%s@%"PRIX64, qdev_fw_name(dev), (uint64_t)id << 48); } else if (virtio) { @@ -2948,14 +3135,14 @@ static char *spapr_get_fw_dev_path(FWPathProvider *p, BusState *bus, static char *spapr_get_kvm_type(Object *obj, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); return g_strdup(spapr->kvm_type); } static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); g_free(spapr->kvm_type); spapr->kvm_type = g_strdup(value); @@ -2963,7 +3150,7 @@ static void spapr_set_kvm_type(Object *obj, const char *value, Error **errp) static bool spapr_get_modern_hotplug_events(Object *obj, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); return spapr->use_hotplug_event_source; } @@ -2971,7 +3158,7 @@ static bool spapr_get_modern_hotplug_events(Object *obj, Error **errp) static void spapr_set_modern_hotplug_events(Object *obj, bool value, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); spapr->use_hotplug_event_source = value; } @@ -2983,7 +3170,7 @@ static bool spapr_get_msix_emulation(Object *obj, Error **errp) static char *spapr_get_resize_hpt(Object *obj, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); switch (spapr->resize_hpt) { case SPAPR_RESIZE_HPT_DEFAULT: @@ -3000,7 +3187,7 @@ static char *spapr_get_resize_hpt(Object *obj, Error **errp) static void spapr_set_resize_hpt(Object *obj, const char *value, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); if (strcmp(value, "default") == 0) { spapr->resize_hpt = SPAPR_RESIZE_HPT_DEFAULT; @@ -3027,9 +3214,77 @@ static void spapr_set_vsmt(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, (uint32_t *)opaque, errp); } +static char *spapr_get_ic_mode(Object *obj, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + if (spapr->irq == &spapr_irq_xics_legacy) { + return g_strdup("legacy"); + } else if (spapr->irq == &spapr_irq_xics) { + return g_strdup("xics"); + } else if (spapr->irq == &spapr_irq_xive) { + return g_strdup("xive"); + } else if (spapr->irq == &spapr_irq_dual) { + return g_strdup("dual"); + } + g_assert_not_reached(); +} + +static void spapr_set_ic_mode(Object *obj, const char *value, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { + error_setg(errp, "This machine only uses the legacy XICS backend, don't pass ic-mode"); + return; + } + + /* The legacy IRQ backend can not be set */ + if (strcmp(value, "xics") == 0) { + spapr->irq = &spapr_irq_xics; + } else if (strcmp(value, "xive") == 0) { + spapr->irq = &spapr_irq_xive; + } else if (strcmp(value, "dual") == 0) { + spapr->irq = &spapr_irq_dual; + } else { + error_setg(errp, "Bad value for \"ic-mode\" property"); + } +} + +static char *spapr_get_host_model(Object *obj, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + return g_strdup(spapr->host_model); +} + +static void spapr_set_host_model(Object *obj, const char *value, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + g_free(spapr->host_model); + spapr->host_model = g_strdup(value); +} + +static char *spapr_get_host_serial(Object *obj, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + return g_strdup(spapr->host_serial); +} + +static void spapr_set_host_serial(Object *obj, const char *value, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + + g_free(spapr->host_serial); + spapr->host_serial = g_strdup(value); +} + static void spapr_instance_init(Object *obj) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); spapr->htab_fd = -1; spapr->use_hotplug_event_source = true; @@ -3063,11 +3318,30 @@ static void spapr_instance_init(Object *obj) " the host's SMT mode", &error_abort); object_property_add_bool(obj, "vfio-no-msix-emulation", spapr_get_msix_emulation, NULL, NULL); + + /* The machine class defines the default interrupt controller mode */ + spapr->irq = smc->irq; + object_property_add_str(obj, "ic-mode", spapr_get_ic_mode, + spapr_set_ic_mode, NULL); + object_property_set_description(obj, "ic-mode", + "Specifies the interrupt controller mode (xics, xive, dual)", + NULL); + + object_property_add_str(obj, "host-model", + spapr_get_host_model, spapr_set_host_model, + &error_abort); + object_property_set_description(obj, "host-model", + "Host model to advertise in guest device tree", &error_abort); + object_property_add_str(obj, "host-serial", + spapr_get_host_serial, spapr_set_host_serial, + &error_abort); + object_property_set_description(obj, "host-serial", + "Host serial number to advertise in guest device tree", &error_abort); } static void spapr_machine_finalizefn(Object *obj) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprMachineState *spapr = SPAPR_MACHINE(obj); g_free(spapr->kvm_type); } @@ -3087,14 +3361,26 @@ static void spapr_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, - uint32_t node, bool dedicated_hp_event_source, - Error **errp) +int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) { - sPAPRDRConnector *drc; + uint64_t addr; + uint32_t node; + + addr = spapr_drc_index(drc) * SPAPR_MEMORY_BLOCK_SIZE; + node = object_property_get_uint(OBJECT(drc->dev), PC_DIMM_NODE_PROP, + &error_abort); + *fdt_start_offset = spapr_populate_memory_node(fdt, node, addr, + SPAPR_MEMORY_BLOCK_SIZE); + return 0; +} + +static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, + bool dedicated_hp_event_source, Error **errp) +{ + SpaprDrc *drc; uint32_t nr_lmbs = size/SPAPR_MEMORY_BLOCK_SIZE; - int i, fdt_offset, fdt_size; - void *fdt; + int i; uint64_t addr = addr_start; bool hotplugged = spapr_drc_hotplugged(dev); Error *local_err = NULL; @@ -3104,11 +3390,7 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, 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); - - spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err); + spapr_drc_attach(drc, dev, &local_err); if (local_err) { while (addr > addr_start) { addr -= SPAPR_MEMORY_BLOCK_SIZE; @@ -3116,7 +3398,6 @@ static void spapr_add_lmbs(DeviceState *dev, uint64_t addr_start, uint64_t size, addr / SPAPR_MEMORY_BLOCK_SIZE); spapr_drc_detach(drc); } - g_free(fdt); error_propagate(errp, local_err); return; } @@ -3146,17 +3427,13 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { Error *local_err = NULL; - sPAPRMachineState *ms = SPAPR_MACHINE(hotplug_dev); + 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, &error_abort); - uint64_t align, size, addr; - uint32_t node; + uint64_t size, addr; - align = memory_region_get_alignment(mr); - size = memory_region_size(mr); + size = memory_device_get_region_size(MEMORY_DEVICE(dev), &error_abort); - pc_dimm_plug(dev, MACHINE(ms), align, &local_err); + pc_dimm_plug(dimm, MACHINE(ms), &local_err); if (local_err) { goto out; } @@ -3167,10 +3444,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, goto out_unplug; } - node = object_property_get_uint(OBJECT(dev), PC_DIMM_NODE_PROP, - &error_abort); - spapr_add_lmbs(dev, addr, size, node, - spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), + spapr_add_lmbs(dev, addr, size, spapr_ovec_test(ms->ov5_cas, OV5_HP_EVT), &local_err); if (local_err) { goto out_unplug; @@ -3179,7 +3453,7 @@ static void spapr_memory_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; out_unplug: - pc_dimm_unplug(dev, MACHINE(ms)); + pc_dimm_unplug(dimm, MACHINE(ms)); out: error_propagate(errp, local_err); } @@ -3187,11 +3461,10 @@ out: static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - const sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); - sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + const SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(hotplug_dev); + SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr; + Error *local_err = NULL; uint64_t size; Object *memdev; hwaddr pagesize; @@ -3201,11 +3474,11 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, return; } - mr = ddc->get_memory_region(dimm, errp); - if (!mr) { + size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &local_err); + if (local_err) { + error_propagate(errp, local_err); return; } - size = memory_region_size(mr); if (size % SPAPR_MEMORY_BLOCK_SIZE) { error_setg(errp, "Hotplugged memory size must be a multiple of " @@ -3216,19 +3489,25 @@ static void spapr_memory_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, memdev = object_property_get_link(OBJECT(dimm), PC_DIMM_MEMDEV_PROP, &error_abort); pagesize = host_memory_backend_pagesize(MEMORY_BACKEND(memdev)); - spapr_check_pagesize(spapr, pagesize, errp); + spapr_check_pagesize(spapr, pagesize, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + pc_dimm_pre_plug(dimm, MACHINE(hotplug_dev), NULL, errp); } -struct sPAPRDIMMState { +struct SpaprDimmState { PCDIMMDevice *dimm; uint32_t nr_lmbs; - QTAILQ_ENTRY(sPAPRDIMMState) next; + QTAILQ_ENTRY(SpaprDimmState) next; }; -static sPAPRDIMMState *spapr_pending_dimm_unplugs_find(sPAPRMachineState *s, +static SpaprDimmState *spapr_pending_dimm_unplugs_find(SpaprMachineState *s, PCDIMMDevice *dimm) { - sPAPRDIMMState *dimm_state = NULL; + SpaprDimmState *dimm_state = NULL; QTAILQ_FOREACH(dimm_state, &s->pending_dimm_unplugs, next) { if (dimm_state->dimm == dimm) { @@ -3238,11 +3517,11 @@ static sPAPRDIMMState *spapr_pending_dimm_unplugs_find(sPAPRMachineState *s, return dimm_state; } -static sPAPRDIMMState *spapr_pending_dimm_unplugs_add(sPAPRMachineState *spapr, +static SpaprDimmState *spapr_pending_dimm_unplugs_add(SpaprMachineState *spapr, uint32_t nr_lmbs, PCDIMMDevice *dimm) { - sPAPRDIMMState *ds = NULL; + SpaprDimmState *ds = NULL; /* * If this request is for a DIMM whose removal had failed earlier @@ -3252,7 +3531,7 @@ static sPAPRDIMMState *spapr_pending_dimm_unplugs_add(sPAPRMachineState *spapr, */ ds = spapr_pending_dimm_unplugs_find(spapr, dimm); if (!ds) { - ds = g_malloc0(sizeof(sPAPRDIMMState)); + ds = g_malloc0(sizeof(SpaprDimmState)); ds->nr_lmbs = nr_lmbs; ds->dimm = dimm; QTAILQ_INSERT_HEAD(&spapr->pending_dimm_unplugs, ds, next); @@ -3260,20 +3539,19 @@ static sPAPRDIMMState *spapr_pending_dimm_unplugs_add(sPAPRMachineState *spapr, return ds; } -static void spapr_pending_dimm_unplugs_remove(sPAPRMachineState *spapr, - sPAPRDIMMState *dimm_state) +static void spapr_pending_dimm_unplugs_remove(SpaprMachineState *spapr, + SpaprDimmState *dimm_state) { QTAILQ_REMOVE(&spapr->pending_dimm_unplugs, dimm_state, next); g_free(dimm_state); } -static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms, +static SpaprDimmState *spapr_recover_pending_dimm_state(SpaprMachineState *ms, PCDIMMDevice *dimm) { - sPAPRDRConnector *drc; - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); - uint64_t size = memory_region_size(mr); + SpaprDrc *drc; + uint64_t size = memory_device_get_region_size(MEMORY_DEVICE(dimm), + &error_abort); uint32_t nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; uint32_t avail_lmbs = 0; uint64_t addr_start, addr; @@ -3300,8 +3578,8 @@ static sPAPRDIMMState *spapr_recover_pending_dimm_state(sPAPRMachineState *ms, void spapr_lmb_release(DeviceState *dev) { HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); - sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_ctrl); - sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); + SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_ctrl); + SpaprDimmState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); /* This information will get lost if a migration occurs * during the unplug process. In this case recover it. */ @@ -3321,32 +3599,31 @@ void spapr_lmb_release(DeviceState *dev) * unplug handler chain. This can never fail. */ hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); } static void spapr_memory_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) { - sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); - sPAPRDIMMState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); + SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + SpaprDimmState *ds = spapr_pending_dimm_unplugs_find(spapr, PC_DIMM(dev)); - pc_dimm_unplug(dev, MACHINE(hotplug_dev)); - object_unparent(OBJECT(dev)); + pc_dimm_unplug(PC_DIMM(dev), MACHINE(hotplug_dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); spapr_pending_dimm_unplugs_remove(spapr, ds); } static void spapr_memory_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(hotplug_dev); + SpaprMachineState *spapr = SPAPR_MACHINE(hotplug_dev); Error *local_err = NULL; PCDIMMDevice *dimm = PC_DIMM(dev); - PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm); - MemoryRegion *mr = ddc->get_memory_region(dimm, &error_abort); uint32_t nr_lmbs; uint64_t size, addr_start, addr; int i; - sPAPRDRConnector *drc; + SpaprDrc *drc; - size = memory_region_size(mr); + size = memory_device_get_region_size(MEMORY_DEVICE(dimm), &error_abort); nr_lmbs = size / SPAPR_MEMORY_BLOCK_SIZE; addr_start = object_property_get_uint(OBJECT(dimm), PC_DIMM_ADDR_PROP, @@ -3388,27 +3665,6 @@ out: error_propagate(errp, local_err); } -static void *spapr_populate_hotplug_cpu_dt(CPUState *cs, int *fdt_offset, - sPAPRMachineState *spapr) -{ - PowerPCCPU *cpu = POWERPC_CPU(cs); - DeviceClass *dc = DEVICE_GET_CLASS(cs); - int id = spapr_get_vcpu_id(cpu); - void *fdt; - int offset, fdt_size; - char *nodename; - - fdt = create_device_tree(&fdt_size); - nodename = g_strdup_printf("%s@%x", dc->fw_name, id); - offset = fdt_add_subnode(fdt, 0, nodename); - - spapr_populate_cpu_dt(cs, fdt, offset, spapr); - g_free(nodename); - - *fdt_offset = offset; - return fdt; -} - /* Callback to be called during DRC release. */ void spapr_core_release(DeviceState *dev) { @@ -3416,17 +3672,18 @@ void spapr_core_release(DeviceState *dev) /* Call the unplug handler chain. This can never fail. */ hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); } static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) { MachineState *ms = MACHINE(hotplug_dev); - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(ms); CPUCore *cc = CPU_CORE(dev); CPUArchId *core_slot = spapr_find_cpu_slot(ms, cc->core_id, NULL); if (smc->pre_2_10_has_unused_icps) { - sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); + SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); int i; for (i = 0; i < cc->nr_threads; i++) { @@ -3438,16 +3695,16 @@ static void spapr_core_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) assert(core_slot); core_slot->cpu = NULL; - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } static void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); int index; - sPAPRDRConnector *drc; + SpaprDrc *drc; CPUCore *cc = CPU_CORE(dev); if (!spapr_find_cpu_slot(MACHINE(hotplug_dev), cc->core_id, &index)) { @@ -3469,16 +3726,37 @@ void spapr_core_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, spapr_hotplug_req_remove_by_index(drc); } +int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + SpaprCpuCore *core = SPAPR_CPU_CORE(drc->dev); + CPUState *cs = CPU(core->threads[0]); + PowerPCCPU *cpu = POWERPC_CPU(cs); + DeviceClass *dc = DEVICE_GET_CLASS(cs); + int id = spapr_get_vcpu_id(cpu); + char *nodename; + int offset; + + nodename = g_strdup_printf("%s@%x", dc->fw_name, id); + offset = fdt_add_subnode(fdt, 0, nodename); + g_free(nodename); + + spapr_populate_cpu_dt(cs, fdt, offset, spapr); + + *fdt_start_offset = offset; + return 0; +} + static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(spapr); - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); - sPAPRCPUCore *core = SPAPR_CPU_CORE(OBJECT(dev)); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprCpuCore *core = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(dev); - CPUState *cs = CPU(core->threads[0]); - sPAPRDRConnector *drc; + CPUState *cs; + SpaprDrc *drc; Error *local_err = NULL; CPUArchId *core_slot; int index; @@ -3496,14 +3774,8 @@ static void spapr_core_plug(HotplugHandler *hotplug_dev, DeviceState *dev, g_assert(drc || !mc->has_hotpluggable_cpus); if (drc) { - void *fdt; - int fdt_offset; - - fdt = spapr_populate_hotplug_cpu_dt(cs, &fdt_offset, spapr); - - spapr_drc_attach(drc, dev, fdt, fdt_offset, &local_err); + spapr_drc_attach(drc, dev, &local_err); if (local_err) { - g_free(fdt); error_propagate(errp, local_err); return; } @@ -3587,6 +3859,116 @@ out: error_propagate(errp, local_err); } +int spapr_phb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(drc->dev); + int intc_phandle; + + intc_phandle = spapr_irq_get_phandle(spapr, spapr->fdt_blob, errp); + if (intc_phandle <= 0) { + return -1; + } + + if (spapr_populate_pci_dt(sphb, intc_phandle, fdt, spapr->irq->nr_msis, + fdt_start_offset)) { + error_setg(errp, "unable to create FDT node for PHB %d", sphb->index); + return -1; + } + + /* generally SLOF creates these, for hotplug it's up to QEMU */ + _FDT(fdt_setprop_string(fdt, *fdt_start_offset, "name", "pci")); + + return 0; +} + +static void spapr_phb_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + const unsigned windows_supported = spapr_phb_windows_supported(sphb); + + if (dev->hotplugged && !smc->dr_phb_enabled) { + error_setg(errp, "PHB hotplug not supported for this machine"); + return; + } + + if (sphb->index == (uint32_t)-1) { + error_setg(errp, "\"index\" for PAPR PHB is mandatory"); + return; + } + + /* + * This will check that sphb->index doesn't exceed the maximum number of + * PHBs for the current machine type. + */ + smc->phb_placement(spapr, sphb->index, + &sphb->buid, &sphb->io_win_addr, + &sphb->mem_win_addr, &sphb->mem64_win_addr, + windows_supported, sphb->dma_liobn, errp); +} + +static void spapr_phb_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(OBJECT(hotplug_dev)); + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); + SpaprDrc *drc; + bool hotplugged = spapr_drc_hotplugged(dev); + Error *local_err = NULL; + + if (!smc->dr_phb_enabled) { + return; + } + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index); + /* hotplug hooks should check it's enabled before getting this far */ + assert(drc); + + spapr_drc_attach(drc, DEVICE(dev), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (hotplugged) { + spapr_hotplug_req_add_by_index(drc); + } else { + spapr_drc_reset(drc); + } +} + +void spapr_phb_release(DeviceState *dev) +{ + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); + object_unparent(OBJECT(dev)); +} + +static void spapr_phb_unplug(HotplugHandler *hotplug_dev, DeviceState *dev) +{ + object_property_set_bool(OBJECT(dev), false, "realized", NULL); +} + +static void spapr_phb_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(dev); + SpaprDrc *drc; + + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, sphb->index); + assert(drc); + + if (!spapr_drc_unplug_requested(drc)) { + spapr_drc_detach(drc); + spapr_hotplug_req_remove_by_index(drc); + } +} + static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -3594,6 +3976,8 @@ static void spapr_machine_device_plug(HotplugHandler *hotplug_dev, spapr_memory_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + spapr_phb_plug(hotplug_dev, dev, errp); } } @@ -3604,14 +3988,17 @@ static void spapr_machine_device_unplug(HotplugHandler *hotplug_dev, spapr_memory_unplug(hotplug_dev, dev); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_unplug(hotplug_dev, dev); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + spapr_phb_unplug(hotplug_dev, dev); } } static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - sPAPRMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev)); + SpaprMachineState *sms = SPAPR_MACHINE(OBJECT(hotplug_dev)); MachineClass *mc = MACHINE_GET_CLASS(sms); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM)) { if (spapr_ovec_test(sms->ov5_cas, OV5_HP_EVT)) { @@ -3631,6 +4018,12 @@ static void spapr_machine_device_unplug_request(HotplugHandler *hotplug_dev, return; } spapr_core_unplug_request(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + if (!smc->dr_phb_enabled) { + error_setg(errp, "PHB hot unplug not supported on this machine"); + return; + } + spapr_phb_unplug_request(hotplug_dev, dev, errp); } } @@ -3641,6 +4034,8 @@ static void spapr_machine_device_pre_plug(HotplugHandler *hotplug_dev, spapr_memory_pre_plug(hotplug_dev, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { spapr_core_pre_plug(hotplug_dev, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { + spapr_phb_pre_plug(hotplug_dev, dev, errp); } } @@ -3648,7 +4043,8 @@ static HotplugHandler *spapr_get_hotplug_handler(MachineState *machine, DeviceState *dev) { if (object_dynamic_cast(OBJECT(dev), TYPE_PC_DIMM) || - object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE)) { + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_CPU_CORE) || + object_dynamic_cast(OBJECT(dev), TYPE_SPAPR_PCI_HOST_BRIDGE)) { return HOTPLUG_HANDLER(machine); } return NULL; @@ -3709,7 +4105,7 @@ static const CPUArchIdList *spapr_possible_cpu_arch_ids(MachineState *machine) return machine->possible_cpus; } -static void spapr_phb_placement(sPAPRMachineState *spapr, uint32_t index, +static void spapr_phb_placement(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, unsigned n_dma, uint32_t *liobns, Error **errp) @@ -3730,8 +4126,6 @@ static void spapr_phb_placement(sPAPRMachineState *spapr, uint32_t index, * 1TiB 64-bit MMIO windows for each PHB. */ const uint64_t base_buid = 0x800000020000000ULL; -#define SPAPR_MAX_PHBS ((SPAPR_PCI_LIMIT - SPAPR_PCI_BASE) / \ - SPAPR_PCI_MEM64_WIN_SIZE - 1) int i; /* Sanity check natural alignments */ @@ -3763,14 +4157,14 @@ static void spapr_phb_placement(sPAPRMachineState *spapr, uint32_t index, static ICSState *spapr_ics_get(XICSFabric *dev, int irq) { - sPAPRMachineState *spapr = SPAPR_MACHINE(dev); + SpaprMachineState *spapr = SPAPR_MACHINE(dev); return ics_valid_irq(spapr->ics, irq) ? spapr->ics : NULL; } static void spapr_ics_resend(XICSFabric *dev) { - sPAPRMachineState *spapr = SPAPR_MACHINE(dev); + SpaprMachineState *spapr = SPAPR_MACHINE(dev); ics_resend(spapr->ics); } @@ -3779,124 +4173,15 @@ static ICPState *spapr_icp_get(XICSFabric *xi, int vcpu_id) { PowerPCCPU *cpu = spapr_find_cpu(vcpu_id); - return cpu ? ICP(cpu->intc) : NULL; -} - -#define ICS_IRQ_FREE(ics, srcno) \ - (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK))) - -static int ics_find_free_block(ICSState *ics, int num, int alignnum) -{ - int first, i; - - for (first = 0; first < ics->nr_irqs; first += alignnum) { - if (num > (ics->nr_irqs - first)) { - return -1; - } - for (i = first; i < first + num; ++i) { - if (!ICS_IRQ_FREE(ics, i)) { - break; - } - } - if (i == (first + num)) { - return first; - } - } - - return -1; -} - -int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, Error **errp) -{ - ICSState *ics = spapr->ics; - int first = -1; - - assert(ics); - - /* - * MSIMesage::data is used for storing VIRQ so - * it has to be aligned to num to support multiple - * MSI vectors. MSI-X is not affected by this. - * The hint is used for the first IRQ, the rest should - * be allocated continuously. - */ - if (align) { - assert((num == 1) || (num == 2) || (num == 4) || - (num == 8) || (num == 16) || (num == 32)); - first = ics_find_free_block(ics, num, num); - } 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; - } - - return first + ics->offset; -} - -int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp) -{ - ICSState *ics = spapr->ics; - - assert(ics); - - if (!ics_valid_irq(ics, irq)) { - error_setg(errp, "IRQ %d is invalid", irq); - return -1; - } - - if (!ICS_IRQ_FREE(ics, irq - ics->offset)) { - error_setg(errp, "IRQ %d is not free", irq); - return -1; - } - - ics_set_irq_type(ics, irq - ics->offset, lsi); - return 0; -} - -void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num) -{ - ICSState *ics = spapr->ics; - int srcno = irq - ics->offset; - int i; - - if (ics_valid_irq(ics, irq)) { - trace_spapr_irq_free(0, irq, num); - for (i = srcno; i < srcno + num; ++i) { - if (ICS_IRQ_FREE(ics, i)) { - trace_spapr_irq_free_warn(0, i + ics->offset); - } - memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); - } - } -} - -qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq) -{ - ICSState *ics = spapr->ics; - - if (ics_valid_irq(ics, irq)) { - return ics->qirqs[irq - ics->offset]; - } - - return NULL; + return cpu ? spapr_cpu_state(cpu)->icp : NULL; } static void spapr_pic_print_info(InterruptStatsProvider *obj, Monitor *mon) { - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); - CPUState *cs; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); - CPU_FOREACH(cs) { - PowerPCCPU *cpu = POWERPC_CPU(cs); - - icp_pic_print_info(ICP(cpu->intc), mon); - } - - ics_pic_print_info(spapr->ics, mon); + spapr->irq->print_info(spapr, mon); } int spapr_get_vcpu_id(PowerPCCPU *cpu) @@ -3906,7 +4191,7 @@ int spapr_get_vcpu_id(PowerPCCPU *cpu) void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); int vcpu_id; vcpu_id = spapr_vcpu_id(spapr, cpu_index); @@ -3940,7 +4225,7 @@ PowerPCCPU *spapr_find_cpu(int vcpu_id) static void spapr_machine_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); - sPAPRMachineClass *smc = SPAPR_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); @@ -3949,6 +4234,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); mc->desc = "pSeries Logical Partition (PAPR compliant)"; + mc->ignore_boot_device_suffixes = true; /* * We set up the default / latest behaviour here. The class_init @@ -3977,7 +4263,8 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) hc->unplug = spapr_machine_device_unplug; smc->dr_lmb_enabled = true; - mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); + smc->update_dt_enabled = true; + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power9_v2.0"); mc->has_hotpluggable_cpus = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_ENABLED; fwc->get_dev_path = spapr_get_fw_dev_path; @@ -3988,7 +4275,7 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) vhc->map_hptes = spapr_map_hptes; vhc->unmap_hptes = spapr_unmap_hptes; vhc->store_hpte = spapr_store_hpte; - vhc->get_patbe = spapr_get_patbe; + vhc->get_pate = spapr_get_pate; vhc->encode_hpt_for_kvm_pr = spapr_encode_hpt_for_kvm_pr; xic->ics_get = spapr_ics_get; xic->ics_resend = spapr_ics_resend; @@ -4003,21 +4290,26 @@ static void spapr_machine_class_init(ObjectClass *oc, void *data) smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; smc->default_caps.caps[SPAPR_CAP_VSX] = SPAPR_CAP_ON; smc->default_caps.caps[SPAPR_CAP_DFP] = SPAPR_CAP_ON; - smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; - smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; - smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; + smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_WORKAROUND; + smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_WORKAROUND; smc->default_caps.caps[SPAPR_CAP_HPT_MAXPAGESIZE] = 16; /* 64kiB */ + smc->default_caps.caps[SPAPR_CAP_NESTED_KVM_HV] = SPAPR_CAP_OFF; + smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_ON; + smc->default_caps.caps[SPAPR_CAP_CCF_ASSIST] = SPAPR_CAP_OFF; spapr_caps_add_properties(smc, &error_abort); + smc->irq = &spapr_irq_xics; + smc->dr_phb_enabled = true; } static const TypeInfo spapr_machine_info = { .name = TYPE_SPAPR_MACHINE, .parent = TYPE_MACHINE, .abstract = true, - .instance_size = sizeof(sPAPRMachineState), + .instance_size = sizeof(SpaprMachineState), .instance_init = spapr_instance_init, .instance_finalize = spapr_machine_finalizefn, - .class_size = sizeof(sPAPRMachineClass), + .class_size = sizeof(SpaprMachineClass), .class_init = spapr_machine_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, @@ -4041,16 +4333,10 @@ static const TypeInfo spapr_machine_info = { 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) \ { \ @@ -4059,46 +4345,68 @@ static const TypeInfo spapr_machine_info = { type_init(spapr_machine_register_##suffix) /* - * pseries-3.0 + * pseries-4.0 */ -static void spapr_machine_3_0_instance_options(MachineState *machine) -{ -} - -static void spapr_machine_3_0_class_options(MachineClass *mc) +static void spapr_machine_4_0_class_options(MachineClass *mc) { /* Defaults for the latest behaviour inherited from the base class */ } -DEFINE_SPAPR_MACHINE(3_0, "3.0", true); +DEFINE_SPAPR_MACHINE(4_0, "4.0", true); + +/* + * pseries-3.1 + */ +static void spapr_machine_3_1_class_options(MachineClass *mc) +{ + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_4_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); + + mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power8_v2.0"); + smc->update_dt_enabled = false; + smc->dr_phb_enabled = false; + smc->broken_host_serial_model = true; + smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_SBBC] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_IBS] = SPAPR_CAP_BROKEN; + smc->default_caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; +} + +DEFINE_SPAPR_MACHINE(3_1, "3.1", false); + +/* + * pseries-3.0 + */ + +static void spapr_machine_3_0_class_options(MachineClass *mc) +{ + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + + spapr_machine_3_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); + + smc->legacy_irq_allocation = true; + smc->irq = &spapr_irq_xics_legacy; +} + +DEFINE_SPAPR_MACHINE(3_0, "3.0", false); /* * pseries-2.12 */ -#define SPAPR_COMPAT_2_12 \ - HW_COMPAT_2_12 \ - { \ - .driver = TYPE_POWERPC_CPU, \ - .property = "pre-3.0-migration", \ - .value = "on", \ - }, \ - { \ - .driver = TYPE_SPAPR_CPU_CORE, \ - .property = "pre-3.0-migration", \ - .value = "on", \ - }, - -static void spapr_machine_2_12_instance_options(MachineState *machine) -{ - spapr_machine_3_0_instance_options(machine); -} - static void spapr_machine_2_12_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_POWERPC_CPU, "pre-3.0-migration", "on" }, + { TYPE_SPAPR_CPU_CORE, "pre-3.0-migration", "on" }, + }; spapr_machine_3_0_class_options(mc); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_12); + compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); /* We depend on kvm_enabled() to choose a default value for the * hpt-max-page-size capability. Of course we can't do it here @@ -4110,14 +4418,9 @@ static void spapr_machine_2_12_class_options(MachineClass *mc) DEFINE_SPAPR_MACHINE(2_12, "2.12", false); -static void spapr_machine_2_12_sxxm_instance_options(MachineState *machine) -{ - spapr_machine_2_12_instance_options(machine); -} - static void spapr_machine_2_12_sxxm_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); spapr_machine_2_12_class_options(mc); smc->default_caps.caps[SPAPR_CAP_CFPC] = SPAPR_CAP_WORKAROUND; @@ -4130,21 +4433,14 @@ DEFINE_SPAPR_MACHINE(2_12_sxxm, "2.12-sxxm", false); /* * pseries-2.11 */ -#define SPAPR_COMPAT_2_11 \ - HW_COMPAT_2_11 - -static void spapr_machine_2_11_instance_options(MachineState *machine) -{ - spapr_machine_2_12_instance_options(machine); -} static void spapr_machine_2_11_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); spapr_machine_2_12_class_options(mc); smc->default_caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_ON; - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_11); + compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); } DEFINE_SPAPR_MACHINE(2_11, "2.11", false); @@ -4152,18 +4448,11 @@ DEFINE_SPAPR_MACHINE(2_11, "2.11", false); /* * pseries-2.10 */ -#define SPAPR_COMPAT_2_10 \ - HW_COMPAT_2_10 - -static void spapr_machine_2_10_instance_options(MachineState *machine) -{ - spapr_machine_2_11_instance_options(machine); -} static void spapr_machine_2_10_class_options(MachineClass *mc) { spapr_machine_2_11_class_options(mc); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_10); + compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); } DEFINE_SPAPR_MACHINE(2_10, "2.10", false); @@ -4171,25 +4460,17 @@ DEFINE_SPAPR_MACHINE(2_10, "2.10", false); /* * pseries-2.9 */ -#define SPAPR_COMPAT_2_9 \ - HW_COMPAT_2_9 \ - { \ - .driver = TYPE_POWERPC_CPU, \ - .property = "pre-2.10-migration", \ - .value = "on", \ - }, \ - -static void spapr_machine_2_9_instance_options(MachineState *machine) -{ - spapr_machine_2_10_instance_options(machine); -} static void spapr_machine_2_9_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_POWERPC_CPU, "pre-2.10-migration", "on" }, + }; spapr_machine_2_10_class_options(mc); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_9); + compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); mc->numa_auto_assign_ram = numa_legacy_auto_assign_ram; smc->pre_2_10_has_unused_icps = true; smc->resize_hpt_default = SPAPR_RESIZE_HPT_DISABLED; @@ -4200,23 +4481,16 @@ DEFINE_SPAPR_MACHINE(2_9, "2.9", false); /* * pseries-2.8 */ -#define SPAPR_COMPAT_2_8 \ - HW_COMPAT_2_8 \ - { \ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ - .property = "pcie-extended-configuration-space", \ - .value = "off", \ - }, - -static void spapr_machine_2_8_instance_options(MachineState *machine) -{ - spapr_machine_2_9_instance_options(machine); -} static void spapr_machine_2_8_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_SPAPR_PCI_HOST_BRIDGE, "pcie-extended-configuration-space", "off" }, + }; + spapr_machine_2_9_class_options(mc); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_8); + compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); mc->numa_mem_align_shift = 23; } @@ -4225,30 +4499,8 @@ DEFINE_SPAPR_MACHINE(2_8, "2.8", false); /* * pseries-2.7 */ -#define SPAPR_COMPAT_2_7 \ - HW_COMPAT_2_7 \ - { \ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ - .property = "mem_win_size", \ - .value = stringify(SPAPR_PCI_2_7_MMIO_WIN_SIZE),\ - }, \ - { \ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ - .property = "mem64_win_size", \ - .value = "0", \ - }, \ - { \ - .driver = TYPE_POWERPC_CPU, \ - .property = "pre-2.8-migration", \ - .value = "on", \ - }, \ - { \ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE, \ - .property = "pre-2.8-migration", \ - .value = "on", \ - }, -static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, +static void phb_placement_2_7(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, unsigned n_dma, uint32_t *liobns, Error **errp) @@ -4297,21 +4549,21 @@ static void phb_placement_2_7(sPAPRMachineState *spapr, uint32_t index, */ } -static void spapr_machine_2_7_instance_options(MachineState *machine) -{ - sPAPRMachineState *spapr = SPAPR_MACHINE(machine); - - spapr_machine_2_8_instance_options(machine); - spapr->use_hotplug_event_source = false; -} - static void spapr_machine_2_7_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_SPAPR_PCI_HOST_BRIDGE, "mem_win_size", "0xf80000000", }, + { TYPE_SPAPR_PCI_HOST_BRIDGE, "mem64_win_size", "0", }, + { TYPE_POWERPC_CPU, "pre-2.8-migration", "on", }, + { TYPE_SPAPR_PCI_HOST_BRIDGE, "pre-2.8-migration", "on", }, + }; spapr_machine_2_8_class_options(mc); mc->default_cpu_type = POWERPC_CPU_TYPE_NAME("power7_v2.3"); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_7); + mc->default_machine_opts = "modern-hotplug-events=off"; + compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); smc->phb_placement = phb_placement_2_7; } @@ -4320,24 +4572,17 @@ DEFINE_SPAPR_MACHINE(2_7, "2.7", false); /* * pseries-2.6 */ -#define SPAPR_COMPAT_2_6 \ - HW_COMPAT_2_6 \ - { \ - .driver = TYPE_SPAPR_PCI_HOST_BRIDGE,\ - .property = "ddw",\ - .value = stringify(off),\ - }, - -static void spapr_machine_2_6_instance_options(MachineState *machine) -{ - spapr_machine_2_7_instance_options(machine); -} static void spapr_machine_2_6_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_SPAPR_PCI_HOST_BRIDGE, "ddw", "off" }, + }; + spapr_machine_2_7_class_options(mc); mc->has_hotpluggable_cpus = false; - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_6); + compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_SPAPR_MACHINE(2_6, "2.6", false); @@ -4345,26 +4590,18 @@ DEFINE_SPAPR_MACHINE(2_6, "2.6", false); /* * 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_5_instance_options(MachineState *machine) -{ - spapr_machine_2_6_instance_options(machine); -} static void spapr_machine_2_5_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { "spapr-vlan", "use-rx-buffer-pools", "off" }, + }; spapr_machine_2_6_class_options(mc); smc->use_ohci_by_default = true; - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_5); + compat_props_add(mc->compat_props, hw_compat_2_5, hw_compat_2_5_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_SPAPR_MACHINE(2_5, "2.5", false); @@ -4372,21 +4609,14 @@ DEFINE_SPAPR_MACHINE(2_5, "2.5", false); /* * pseries-2.4 */ -#define SPAPR_COMPAT_2_4 \ - HW_COMPAT_2_4 - -static void spapr_machine_2_4_instance_options(MachineState *machine) -{ - spapr_machine_2_5_instance_options(machine); -} static void spapr_machine_2_4_class_options(MachineClass *mc) { - sPAPRMachineClass *smc = SPAPR_MACHINE_CLASS(mc); + SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(mc); spapr_machine_2_5_class_options(mc); smc->dr_lmb_enabled = false; - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_4); + compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); } DEFINE_SPAPR_MACHINE(2_4, "2.4", false); @@ -4394,23 +4624,15 @@ DEFINE_SPAPR_MACHINE(2_4, "2.4", false); /* * pseries-2.3 */ -#define SPAPR_COMPAT_2_3 \ - HW_COMPAT_2_3 \ - {\ - .driver = "spapr-pci-host-bridge",\ - .property = "dynamic-reconfiguration",\ - .value = "off",\ - }, - -static void spapr_machine_2_3_instance_options(MachineState *machine) -{ - spapr_machine_2_4_instance_options(machine); -} static void spapr_machine_2_3_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { "spapr-pci-host-bridge", "dynamic-reconfiguration", "off" }, + }; spapr_machine_2_4_class_options(mc); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_3); + compat_props_add(mc->compat_props, hw_compat_2_3, hw_compat_2_3_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_SPAPR_MACHINE(2_3, "2.3", false); @@ -4418,42 +4640,27 @@ DEFINE_SPAPR_MACHINE(2_3, "2.3", false); * pseries-2.2 */ -#define SPAPR_COMPAT_2_2 \ - 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) -{ - spapr_machine_2_3_instance_options(machine); - machine->suppress_vmdesc = true; -} - static void spapr_machine_2_2_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_SPAPR_PCI_HOST_BRIDGE, "mem_win_size", "0x20000000" }, + }; + spapr_machine_2_3_class_options(mc); - SET_MACHINE_COMPAT(mc, SPAPR_COMPAT_2_2); + compat_props_add(mc->compat_props, hw_compat_2_2, hw_compat_2_2_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); + mc->default_machine_opts = "modern-hotplug-events=off,suppress-vmdesc=on"; } DEFINE_SPAPR_MACHINE(2_2, "2.2", false); /* * pseries-2.1 */ -#define SPAPR_COMPAT_2_1 \ - HW_COMPAT_2_1 - -static void spapr_machine_2_1_instance_options(MachineState *machine) -{ - spapr_machine_2_2_instance_options(machine); -} 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); + compat_props_add(mc->compat_props, hw_compat_2_1, hw_compat_2_1_len); } DEFINE_SPAPR_MACHINE(2_1, "2.1", false); diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index aa605cea91..edc5ed0e0c 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -31,10 +31,11 @@ #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" #include "kvm_ppc.h" +#include "sysemu/qtest.h" #include "hw/ppc/spapr.h" -typedef struct sPAPRCapPossible { +typedef struct SpaprCapPossible { int num; /* size of vals array below */ const char *help; /* help text for vals */ /* @@ -46,9 +47,9 @@ typedef struct sPAPRCapPossible { * point is observed */ const char *vals[]; -} sPAPRCapPossible; +} SpaprCapPossible; -typedef struct sPAPRCapabilityInfo { +typedef struct SpaprCapabilityInfo { const char *name; const char *description; int index; @@ -58,18 +59,18 @@ typedef struct sPAPRCapabilityInfo { ObjectPropertyAccessor *set; const char *type; /* Possible values if this is a custom string type */ - sPAPRCapPossible *possible; + SpaprCapPossible *possible; /* Make sure the virtual hardware can support this capability */ - void (*apply)(sPAPRMachineState *spapr, uint8_t val, Error **errp); - void (*cpu_apply)(sPAPRMachineState *spapr, PowerPCCPU *cpu, + void (*apply)(SpaprMachineState *spapr, uint8_t val, Error **errp); + void (*cpu_apply)(SpaprMachineState *spapr, PowerPCCPU *cpu, uint8_t val, Error **errp); -} sPAPRCapabilityInfo; +} SpaprCapabilityInfo; static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRCapabilityInfo *cap = opaque; - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprCapabilityInfo *cap = opaque; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); bool value = spapr_get_cap(spapr, cap->index) == SPAPR_CAP_ON; visit_type_bool(v, name, &value, errp); @@ -78,8 +79,8 @@ static void spapr_cap_get_bool(Object *obj, Visitor *v, const char *name, static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRCapabilityInfo *cap = opaque; - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprCapabilityInfo *cap = opaque; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); bool value; Error *local_err = NULL; @@ -97,8 +98,8 @@ static void spapr_cap_set_bool(Object *obj, Visitor *v, const char *name, static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRCapabilityInfo *cap = opaque; - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprCapabilityInfo *cap = opaque; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); char *val = NULL; uint8_t value = spapr_get_cap(spapr, cap->index); @@ -116,8 +117,8 @@ static void spapr_cap_get_string(Object *obj, Visitor *v, const char *name, static void spapr_cap_set_string(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRCapabilityInfo *cap = opaque; - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprCapabilityInfo *cap = opaque; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); Error *local_err = NULL; uint8_t i; char *val; @@ -149,8 +150,8 @@ out: static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRCapabilityInfo *cap = opaque; - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprCapabilityInfo *cap = opaque; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); uint8_t val = spapr_get_cap(spapr, cap->index); uint64_t pagesize = (1ULL << val); @@ -160,8 +161,8 @@ static void spapr_cap_get_pagesize(Object *obj, Visitor *v, const char *name, static void spapr_cap_set_pagesize(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRCapabilityInfo *cap = opaque; - sPAPRMachineState *spapr = SPAPR_MACHINE(obj); + SpaprCapabilityInfo *cap = opaque; + SpaprMachineState *spapr = SPAPR_MACHINE(obj); uint64_t pagesize; uint8_t val; Error *local_err = NULL; @@ -182,7 +183,7 @@ static void spapr_cap_set_pagesize(Object *obj, Visitor *v, const char *name, spapr->eff.caps[cap->index] = val; } -static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) +static void cap_htm_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { if (!val) { /* TODO: We don't support disabling htm yet */ @@ -198,7 +199,7 @@ static void cap_htm_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) } } -static void cap_vsx_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) +static void cap_vsx_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(first_cpu); CPUPPCState *env = &cpu->env; @@ -215,7 +216,7 @@ static void cap_vsx_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) } } -static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) +static void cap_dfp_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { PowerPCCPU *cpu = POWERPC_CPU(first_cpu); CPUPPCState *env = &cpu->env; @@ -229,83 +230,97 @@ static void cap_dfp_apply(sPAPRMachineState *spapr, uint8_t val, Error **errp) } } -sPAPRCapPossible cap_cfpc_possible = { +SpaprCapPossible cap_cfpc_possible = { .num = 3, .vals = {"broken", "workaround", "fixed"}, .help = "broken - no protection, workaround - workaround available," " fixed - fixed in hardware", }; -static void cap_safe_cache_apply(sPAPRMachineState *spapr, uint8_t val, +static void cap_safe_cache_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { + Error *local_err = NULL; uint8_t kvm_val = kvmppc_get_cap_safe_cache(); if (tcg_enabled() && val) { - /* TODO - for now only allow broken for TCG */ - error_setg(errp, -"Requested safe cache capability level not supported by tcg, try a different value for cap-cfpc"); + /* TCG only supports broken, allow other values and print a warning */ + error_setg(&local_err, + "TCG doesn't support requested feature, cap-cfpc=%s", + cap_cfpc_possible.vals[val]); } else if (kvm_enabled() && (val > kvm_val)) { error_setg(errp, "Requested safe cache capability level not supported by kvm, try cap-cfpc=%s", cap_cfpc_possible.vals[kvm_val]); } + + if (local_err != NULL) + warn_report_err(local_err); } -sPAPRCapPossible cap_sbbc_possible = { +SpaprCapPossible cap_sbbc_possible = { .num = 3, .vals = {"broken", "workaround", "fixed"}, .help = "broken - no protection, workaround - workaround available," " fixed - fixed in hardware", }; -static void cap_safe_bounds_check_apply(sPAPRMachineState *spapr, uint8_t val, +static void cap_safe_bounds_check_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { + Error *local_err = NULL; uint8_t kvm_val = kvmppc_get_cap_safe_bounds_check(); if (tcg_enabled() && val) { - /* TODO - for now only allow broken for TCG */ - error_setg(errp, -"Requested safe bounds check capability level not supported by tcg, try a different value for cap-sbbc"); + /* TCG only supports broken, allow other values and print a warning */ + error_setg(&local_err, + "TCG doesn't support requested feature, cap-sbbc=%s", + cap_sbbc_possible.vals[val]); } else if (kvm_enabled() && (val > kvm_val)) { error_setg(errp, "Requested safe bounds check capability level not supported by kvm, try cap-sbbc=%s", cap_sbbc_possible.vals[kvm_val]); } + + if (local_err != NULL) + warn_report_err(local_err); } -sPAPRCapPossible cap_ibs_possible = { - .num = 4, +SpaprCapPossible cap_ibs_possible = { + .num = 5, /* Note workaround only maintained for compatibility */ - .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd"}, - .help = "broken - no protection, fixed-ibs - indirect branch serialisation," - " fixed-ccd - cache count disabled", + .vals = {"broken", "workaround", "fixed-ibs", "fixed-ccd", "fixed-na"}, + .help = "broken - no protection, workaround - count cache flush" + ", fixed-ibs - indirect branch serialisation," + " fixed-ccd - cache count disabled," + " fixed-na - fixed in hardware (no longer applicable)", }; -static void cap_safe_indirect_branch_apply(sPAPRMachineState *spapr, +static void cap_safe_indirect_branch_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { + Error *local_err = NULL; uint8_t kvm_val = kvmppc_get_cap_safe_indirect_branch(); - if (val == SPAPR_CAP_WORKAROUND) { /* Can only be Broken or Fixed */ - error_setg(errp, -"Requested safe indirect branch capability level \"workaround\" not valid, try cap-ibs=%s", - cap_ibs_possible.vals[kvm_val]); - } else if (tcg_enabled() && val) { - /* TODO - for now only allow broken for TCG */ - error_setg(errp, -"Requested safe indirect branch capability level not supported by tcg, try a different value for cap-ibs"); - } else if (kvm_enabled() && val && (val != kvm_val)) { + if (tcg_enabled() && val) { + /* TCG only supports broken, allow other values and print a warning */ + error_setg(&local_err, + "TCG doesn't support requested feature, cap-ibs=%s", + cap_ibs_possible.vals[val]); + } else if (kvm_enabled() && (val > kvm_val)) { error_setg(errp, "Requested safe indirect branch capability level not supported by kvm, try cap-ibs=%s", cap_ibs_possible.vals[kvm_val]); } + + if (local_err != NULL) { + warn_report_err(local_err); + } } #define VALUE_DESC_TRISTATE " (broken, workaround, fixed)" -void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, +void spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize, Error **errp) { hwaddr maxpagesize = (1ULL << spapr->eff.caps[SPAPR_CAP_HPT_MAXPAGESIZE]); @@ -322,7 +337,7 @@ void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, } } -static void cap_hpt_maxpagesize_apply(sPAPRMachineState *spapr, +static void cap_hpt_maxpagesize_apply(SpaprMachineState *spapr, uint8_t val, Error **errp) { if (val < 12) { @@ -359,7 +374,7 @@ static bool spapr_pagesize_cb(void *opaque, uint32_t seg_pshift, return true; } -static void cap_hpt_maxpagesize_cpu_apply(sPAPRMachineState *spapr, +static void cap_hpt_maxpagesize_cpu_apply(SpaprMachineState *spapr, PowerPCCPU *cpu, uint8_t val, Error **errp) { @@ -368,7 +383,97 @@ static void cap_hpt_maxpagesize_cpu_apply(sPAPRMachineState *spapr, ppc_hash64_filter_pagesizes(cpu, spapr_pagesize_cb, &maxshift); } -sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { +static void cap_nested_kvm_hv_apply(SpaprMachineState *spapr, + uint8_t val, Error **errp) +{ + if (!val) { + /* capability disabled by default */ + return; + } + + if (tcg_enabled()) { + error_setg(errp, + "No Nested KVM-HV support in tcg, try cap-nested-hv=off"); + } else if (kvm_enabled()) { + if (!kvmppc_has_cap_nested_kvm_hv()) { + error_setg(errp, +"KVM implementation does not support Nested KVM-HV, try cap-nested-hv=off"); + } else if (kvmppc_set_cap_nested_kvm_hv(val) < 0) { + error_setg(errp, +"Error enabling cap-nested-hv with KVM, try cap-nested-hv=off"); + } + } +} + +static void cap_large_decr_apply(SpaprMachineState *spapr, + uint8_t val, Error **errp) +{ + PowerPCCPU *cpu = POWERPC_CPU(first_cpu); + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + + if (!val) { + return; /* Disabled by default */ + } + + if (tcg_enabled()) { + if (!ppc_check_compat(cpu, CPU_POWERPC_LOGICAL_3_00, 0, + spapr->max_compat_pvr)) { + error_setg(errp, + "Large decrementer only supported on POWER9, try -cpu POWER9"); + return; + } + } else if (kvm_enabled()) { + int kvm_nr_bits = kvmppc_get_cap_large_decr(); + + if (!kvm_nr_bits) { + error_setg(errp, + "No large decrementer support, try cap-large-decr=off"); + } else if (pcc->lrg_decr_bits != kvm_nr_bits) { + error_setg(errp, +"KVM large decrementer size (%d) differs to model (%d), try -cap-large-decr=off", + kvm_nr_bits, pcc->lrg_decr_bits); + } + } +} + +static void cap_large_decr_cpu_apply(SpaprMachineState *spapr, + PowerPCCPU *cpu, + uint8_t val, Error **errp) +{ + CPUPPCState *env = &cpu->env; + target_ulong lpcr = env->spr[SPR_LPCR]; + + if (kvm_enabled()) { + if (kvmppc_enable_cap_large_decr(cpu, val)) { + error_setg(errp, + "No large decrementer support, try cap-large-decr=off"); + } + } + + if (val) { + lpcr |= LPCR_LD; + } else { + lpcr &= ~LPCR_LD; + } + ppc_store_lpcr(cpu, lpcr); +} + +static void cap_ccf_assist_apply(SpaprMachineState *spapr, uint8_t val, + Error **errp) +{ + uint8_t kvm_val = kvmppc_get_cap_count_cache_flush_assist(); + + if (tcg_enabled() && val) { + /* TODO - for now only allow broken for TCG */ + error_setg(errp, +"Requested count cache flush assist capability level not supported by tcg, try cap-ccf-assist=off"); + } else if (kvm_enabled() && (val > kvm_val)) { + error_setg(errp, +"Requested count cache flush assist capability level not supported by kvm, try cap-ccf-assist=off"); + } +} + +SpaprCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_HTM] = { .name = "htm", .description = "Allow Hardware Transactional Memory (HTM)", @@ -419,7 +524,8 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { [SPAPR_CAP_IBS] = { .name = "ibs", .description = - "Indirect Branch Speculation (broken, fixed-ibs, fixed-ccd)", + "Indirect Branch Speculation (broken, workaround, fixed-ibs," + "fixed-ccd, fixed-na)", .index = SPAPR_CAP_IBS, .get = spapr_cap_get_string, .set = spapr_cap_set_string, @@ -437,16 +543,49 @@ sPAPRCapabilityInfo capability_table[SPAPR_CAP_NUM] = { .apply = cap_hpt_maxpagesize_apply, .cpu_apply = cap_hpt_maxpagesize_cpu_apply, }, + [SPAPR_CAP_NESTED_KVM_HV] = { + .name = "nested-hv", + .description = "Allow Nested KVM-HV", + .index = SPAPR_CAP_NESTED_KVM_HV, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_nested_kvm_hv_apply, + }, + [SPAPR_CAP_LARGE_DECREMENTER] = { + .name = "large-decr", + .description = "Allow Large Decrementer", + .index = SPAPR_CAP_LARGE_DECREMENTER, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_large_decr_apply, + .cpu_apply = cap_large_decr_cpu_apply, + }, + [SPAPR_CAP_CCF_ASSIST] = { + .name = "ccf-assist", + .description = "Count Cache Flush Assist via HW Instruction", + .index = SPAPR_CAP_CCF_ASSIST, + .get = spapr_cap_get_bool, + .set = spapr_cap_set_bool, + .type = "bool", + .apply = cap_ccf_assist_apply, + }, }; -static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, +static SpaprCapabilities default_caps_with_cpu(SpaprMachineState *spapr, const char *cputype) { - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - sPAPRCapabilities caps; + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + SpaprCapabilities caps; caps = smc->default_caps; + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_3_00, + 0, spapr->max_compat_pvr)) { + caps.caps[SPAPR_CAP_LARGE_DECREMENTER] = SPAPR_CAP_OFF; + } + if (!ppc_type_check_compat(cputype, CPU_POWERPC_LOGICAL_2_07, 0, spapr->max_compat_pvr)) { caps.caps[SPAPR_CAP_HTM] = SPAPR_CAP_OFF; @@ -483,7 +622,7 @@ static sPAPRCapabilities default_caps_with_cpu(sPAPRMachineState *spapr, int spapr_caps_pre_load(void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; /* Set to default so we can tell if this came in with the migration */ spapr->mig = spapr->def; @@ -492,7 +631,7 @@ int spapr_caps_pre_load(void *opaque) int spapr_caps_pre_save(void *opaque) { - sPAPRMachineState *spapr = opaque; + SpaprMachineState *spapr = opaque; spapr->mig = spapr->eff; return 0; @@ -502,12 +641,12 @@ int spapr_caps_pre_save(void *opaque) * caps specific one. Otherwise it wouldn't be called when the source * caps are all defaults, which could still conflict with overridden * caps on the destination */ -int spapr_caps_post_migration(sPAPRMachineState *spapr) +int spapr_caps_post_migration(SpaprMachineState *spapr) { int i; bool ok = true; - sPAPRCapabilities dstcaps = spapr->eff; - sPAPRCapabilities srccaps; + SpaprCapabilities dstcaps = spapr->eff; + SpaprCapabilities srccaps; srccaps = default_caps_with_cpu(spapr, MACHINE(spapr)->cpu_type); for (i = 0; i < SPAPR_CAP_NUM; i++) { @@ -518,7 +657,7 @@ int spapr_caps_post_migration(sPAPRMachineState *spapr) } for (i = 0; i < SPAPR_CAP_NUM; i++) { - sPAPRCapabilityInfo *info = &capability_table[i]; + SpaprCapabilityInfo *info = &capability_table[i]; if (srccaps.caps[i] > dstcaps.caps[i]) { error_report("cap-%s higher level (%d) in incoming stream than on destination (%d)", @@ -539,7 +678,7 @@ int spapr_caps_post_migration(sPAPRMachineState *spapr) #define SPAPR_CAP_MIG_STATE(sname, cap) \ static bool spapr_cap_##sname##_needed(void *opaque) \ { \ - sPAPRMachineState *spapr = opaque; \ + SpaprMachineState *spapr = opaque; \ \ return spapr->cmd_line_caps[cap] && \ (spapr->eff.caps[cap] != \ @@ -553,7 +692,7 @@ const VMStateDescription vmstate_spapr_cap_##sname = { \ .needed = spapr_cap_##sname##_needed, \ .fields = (VMStateField[]) { \ VMSTATE_UINT8(mig.caps[cap], \ - sPAPRMachineState), \ + SpaprMachineState), \ VMSTATE_END_OF_LIST() \ }, \ } @@ -564,10 +703,13 @@ SPAPR_CAP_MIG_STATE(dfp, SPAPR_CAP_DFP); SPAPR_CAP_MIG_STATE(cfpc, SPAPR_CAP_CFPC); SPAPR_CAP_MIG_STATE(sbbc, SPAPR_CAP_SBBC); SPAPR_CAP_MIG_STATE(ibs, SPAPR_CAP_IBS); +SPAPR_CAP_MIG_STATE(nested_kvm_hv, SPAPR_CAP_NESTED_KVM_HV); +SPAPR_CAP_MIG_STATE(large_decr, SPAPR_CAP_LARGE_DECREMENTER); +SPAPR_CAP_MIG_STATE(ccf_assist, SPAPR_CAP_CCF_ASSIST); -void spapr_caps_init(sPAPRMachineState *spapr) +void spapr_caps_init(SpaprMachineState *spapr) { - sPAPRCapabilities default_caps; + SpaprCapabilities default_caps; int i; /* Compute the actual set of caps we should run with */ @@ -583,12 +725,12 @@ void spapr_caps_init(sPAPRMachineState *spapr) } } -void spapr_caps_apply(sPAPRMachineState *spapr) +void spapr_caps_apply(SpaprMachineState *spapr) { int i; for (i = 0; i < SPAPR_CAP_NUM; i++) { - sPAPRCapabilityInfo *info = &capability_table[i]; + SpaprCapabilityInfo *info = &capability_table[i]; /* * If the apply function can't set the desired level and thinks it's @@ -598,12 +740,12 @@ void spapr_caps_apply(sPAPRMachineState *spapr) } } -void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu) +void spapr_caps_cpu_apply(SpaprMachineState *spapr, PowerPCCPU *cpu) { int i; for (i = 0; i < SPAPR_CAP_NUM; i++) { - sPAPRCapabilityInfo *info = &capability_table[i]; + SpaprCapabilityInfo *info = &capability_table[i]; /* * If the apply function can't set the desired level and thinks it's @@ -615,14 +757,14 @@ void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu) } } -void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp) +void spapr_caps_add_properties(SpaprMachineClass *smc, Error **errp) { Error *local_err = NULL; ObjectClass *klass = OBJECT_CLASS(smc); int i; for (i = 0; i < ARRAY_SIZE(capability_table); i++) { - sPAPRCapabilityInfo *cap = &capability_table[i]; + SpaprCapabilityInfo *cap = &capability_table[i]; const char *name = g_strdup_printf("cap-%s", cap->name); char *desc; diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index 993759db47..f04e06cdf6 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -28,21 +28,21 @@ static void spapr_cpu_reset(void *opaque) CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); target_ulong lpcr; cpu_reset(cs); - /* Set compatibility mode to match the boot CPU, which was either set - * by the machine reset code or by CAS. This should never fail. - */ - ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); - /* All CPUs start halted. CPU0 is unhalted from the machine level * reset code and the rest are explicitly started up by the guest * using an RTAS call */ cs->halted = 1; + /* Set compatibility mode to match the boot CPU, which was either set + * by the machine reset code or by CAS. This should never fail. + */ + ppc_set_compat(cpu, POWERPC_CPU(first_cpu)->compat_pvr, &error_abort); + env->spr[SPR_HIOR] = 0; lpcr = env->spr[SPR_LPCR]; @@ -89,6 +89,7 @@ void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r env->nip = nip; env->gpr[3] = r3; + kvmppc_set_reg_ppc_online(cpu, 1); CPU(cpu)->halted = 0; /* Enable Power-saving mode Exit Cause exceptions */ ppc_store_lpcr(cpu, env->spr[SPR_LPCR] | pcc->lpcr_pm); @@ -113,29 +114,9 @@ const char *spapr_get_cpu_core_type(const char *cpu_type) return object_class_get_name(oc); } -static void spapr_unrealize_vcpu(PowerPCCPU *cpu) -{ - qemu_unregister_reset(spapr_cpu_reset, cpu); - object_unparent(cpu->intc); - cpu_remove_sync(CPU(cpu)); - object_unparent(OBJECT(cpu)); -} - -static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) -{ - sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); - CPUCore *cc = CPU_CORE(dev); - int i; - - for (i = 0; i < cc->nr_threads; i++) { - spapr_unrealize_vcpu(sc->threads[i]); - } - g_free(sc->threads); -} - static bool slb_shadow_needed(void *opaque) { - sPAPRCPUState *spapr_cpu = opaque; + SpaprCpuState *spapr_cpu = opaque; return spapr_cpu->slb_shadow_addr != 0; } @@ -146,15 +127,15 @@ static const VMStateDescription vmstate_spapr_cpu_slb_shadow = { .minimum_version_id = 1, .needed = slb_shadow_needed, .fields = (VMStateField[]) { - VMSTATE_UINT64(slb_shadow_addr, sPAPRCPUState), - VMSTATE_UINT64(slb_shadow_size, sPAPRCPUState), + VMSTATE_UINT64(slb_shadow_addr, SpaprCpuState), + VMSTATE_UINT64(slb_shadow_size, SpaprCpuState), VMSTATE_END_OF_LIST() } }; static bool dtl_needed(void *opaque) { - sPAPRCPUState *spapr_cpu = opaque; + SpaprCpuState *spapr_cpu = opaque; return spapr_cpu->dtl_addr != 0; } @@ -165,15 +146,15 @@ static const VMStateDescription vmstate_spapr_cpu_dtl = { .minimum_version_id = 1, .needed = dtl_needed, .fields = (VMStateField[]) { - VMSTATE_UINT64(dtl_addr, sPAPRCPUState), - VMSTATE_UINT64(dtl_size, sPAPRCPUState), + VMSTATE_UINT64(dtl_addr, SpaprCpuState), + VMSTATE_UINT64(dtl_size, SpaprCpuState), VMSTATE_END_OF_LIST() } }; static bool vpa_needed(void *opaque) { - sPAPRCPUState *spapr_cpu = opaque; + SpaprCpuState *spapr_cpu = opaque; return spapr_cpu->vpa_addr != 0; } @@ -184,7 +165,7 @@ static const VMStateDescription vmstate_spapr_cpu_vpa = { .minimum_version_id = 1, .needed = vpa_needed, .fields = (VMStateField[]) { - VMSTATE_UINT64(vpa_addr, sPAPRCPUState), + VMSTATE_UINT64(vpa_addr, SpaprCpuState), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * []) { @@ -207,10 +188,39 @@ static const VMStateDescription vmstate_spapr_cpu_state = { } }; -static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, - Error **errp) +static void spapr_unrealize_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc) +{ + if (!sc->pre_3_0_migration) { + vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); + } + qemu_unregister_reset(spapr_cpu_reset, cpu); + if (spapr_cpu_state(cpu)->icp) { + object_unparent(OBJECT(spapr_cpu_state(cpu)->icp)); + } + if (spapr_cpu_state(cpu)->tctx) { + object_unparent(OBJECT(spapr_cpu_state(cpu)->tctx)); + } + cpu_remove_sync(CPU(cpu)); + object_unparent(OBJECT(cpu)); +} + +static void spapr_cpu_core_unrealize(DeviceState *dev, Error **errp) +{ + SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); + CPUCore *cc = CPU_CORE(dev); + int i; + + for (i = 0; i < cc->nr_threads; i++) { + spapr_unrealize_vcpu(sc->threads[i], sc); + } + g_free(sc->threads); +} + +static void spapr_realize_vcpu(PowerPCCPU *cpu, SpaprMachineState *spapr, + SpaprCpuCore *sc, Error **errp) { CPUPPCState *env = &cpu->env; + CPUState *cs = CPU(cpu); Error *local_err = NULL; object_property_set_bool(OBJECT(cpu), true, "realized", &local_err); @@ -227,12 +237,16 @@ static void spapr_realize_vcpu(PowerPCCPU *cpu, sPAPRMachineState *spapr, qemu_register_reset(spapr_cpu_reset, cpu); spapr_cpu_reset(cpu); - cpu->intc = icp_create(OBJECT(cpu), spapr->icp_type, XICS_FABRIC(spapr), - &local_err); + spapr->irq->cpu_intc_create(spapr, cpu, &local_err); if (local_err) { goto error_unregister; } + if (!sc->pre_3_0_migration) { + vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state, + cpu->machine_data); + } + return; error_unregister: @@ -242,9 +256,9 @@ error: error_propagate(errp, local_err); } -static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) +static PowerPCCPU *spapr_create_vcpu(SpaprCpuCore *sc, int i, Error **errp) { - sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); + SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_GET_CLASS(sc); CPUCore *cc = CPU_CORE(sc); Object *obj; char *id; @@ -271,11 +285,7 @@ static PowerPCCPU *spapr_create_vcpu(sPAPRCPUCore *sc, int i, Error **errp) goto err; } - cpu->machine_data = g_new0(sPAPRCPUState, 1); - if (!sc->pre_3_0_migration) { - vmstate_register(NULL, cs->cpu_index, &vmstate_spapr_cpu_state, - cpu->machine_data); - } + cpu->machine_data = g_new0(SpaprCpuState, 1); object_unref(obj); return cpu; @@ -286,13 +296,10 @@ err: return NULL; } -static void spapr_delete_vcpu(PowerPCCPU *cpu, sPAPRCPUCore *sc) +static void spapr_delete_vcpu(PowerPCCPU *cpu, SpaprCpuCore *sc) { - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); - if (!sc->pre_3_0_migration) { - vmstate_unregister(NULL, &vmstate_spapr_cpu_state, cpu->machine_data); - } cpu->machine_data = NULL; g_free(spapr_cpu); object_unparent(OBJECT(cpu)); @@ -303,10 +310,10 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user * tries to add a sPAPR CPU core to a non-pseries machine. */ - sPAPRMachineState *spapr = - (sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(), + SpaprMachineState *spapr = + (SpaprMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); - sPAPRCPUCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); + SpaprCpuCore *sc = SPAPR_CPU_CORE(OBJECT(dev)); CPUCore *cc = CPU_CORE(OBJECT(dev)); Error *local_err = NULL; int i, j; @@ -325,7 +332,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) } for (j = 0; j < cc->nr_threads; j++) { - spapr_realize_vcpu(sc->threads[j], spapr, &local_err); + spapr_realize_vcpu(sc->threads[j], spapr, sc, &local_err); if (local_err) { goto err_unrealize; } @@ -334,7 +341,7 @@ static void spapr_cpu_core_realize(DeviceState *dev, Error **errp) err_unrealize: while (--j >= 0) { - spapr_unrealize_vcpu(sc->threads[j]); + spapr_unrealize_vcpu(sc->threads[j], sc); } err: while (--i >= 0) { @@ -345,8 +352,8 @@ err: } static Property spapr_cpu_core_properties[] = { - DEFINE_PROP_INT32("node-id", sPAPRCPUCore, node_id, CPU_UNSET_NUMA_NODE_ID), - DEFINE_PROP_BOOL("pre-3.0-migration", sPAPRCPUCore, pre_3_0_migration, + DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID), + DEFINE_PROP_BOOL("pre-3.0-migration", SpaprCpuCore, pre_3_0_migration, false), DEFINE_PROP_END_OF_LIST() }; @@ -354,7 +361,7 @@ static Property spapr_cpu_core_properties[] = { static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - sPAPRCPUCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); + SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); dc->realize = spapr_cpu_core_realize; dc->unrealize = spapr_cpu_core_unrealize; @@ -375,8 +382,8 @@ static const TypeInfo spapr_cpu_core_type_infos[] = { .name = TYPE_SPAPR_CPU_CORE, .parent = TYPE_CPU_CORE, .abstract = true, - .instance_size = sizeof(sPAPRCPUCore), - .class_size = sizeof(sPAPRCPUCoreClass), + .instance_size = sizeof(SpaprCpuCore), + .class_size = sizeof(SpaprCpuCoreClass), }, DEFINE_SPAPR_CPU_CORE_TYPE("970_v2.2"), DEFINE_SPAPR_CPU_CORE_TYPE("970mp_v1.0"), diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 2edb7d1e9c..597f236b9c 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -22,22 +22,23 @@ #include "qemu/error-report.h" #include "hw/ppc/spapr.h" /* for RTAS return codes */ #include "hw/pci-host/spapr.h" /* spapr_phb_remove_pci_device_cb callback */ +#include "sysemu/device_tree.h" #include "trace.h" #define DRC_CONTAINER_PATH "/dr-connector" #define DRC_INDEX_TYPE_SHIFT 28 #define DRC_INDEX_ID_MASK ((1ULL << DRC_INDEX_TYPE_SHIFT) - 1) -sPAPRDRConnectorType spapr_drc_type(sPAPRDRConnector *drc) +SpaprDrcType spapr_drc_type(SpaprDrc *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); return 1 << drck->typeshift; } -uint32_t spapr_drc_index(sPAPRDRConnector *drc) +uint32_t spapr_drc_index(SpaprDrc *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); /* no set format for a drc index: it only needs to be globally * unique. this is how we encode the DRC type on bare-metal @@ -47,7 +48,7 @@ uint32_t spapr_drc_index(sPAPRDRConnector *drc) | (drc->id & DRC_INDEX_ID_MASK); } -static uint32_t drc_isolate_physical(sPAPRDRConnector *drc) +static uint32_t drc_isolate_physical(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_PHYSICAL_POWERON: @@ -71,7 +72,7 @@ static uint32_t drc_isolate_physical(sPAPRDRConnector *drc) return RTAS_OUT_SUCCESS; } -static uint32_t drc_unisolate_physical(sPAPRDRConnector *drc) +static uint32_t drc_unisolate_physical(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_PHYSICAL_UNISOLATE: @@ -98,7 +99,7 @@ static uint32_t drc_unisolate_physical(sPAPRDRConnector *drc) return RTAS_OUT_SUCCESS; } -static uint32_t drc_isolate_logical(sPAPRDRConnector *drc) +static uint32_t drc_isolate_logical(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: @@ -145,7 +146,7 @@ static uint32_t drc_isolate_logical(sPAPRDRConnector *drc) return RTAS_OUT_SUCCESS; } -static uint32_t drc_unisolate_logical(sPAPRDRConnector *drc) +static uint32_t drc_unisolate_logical(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_LOGICAL_UNISOLATE: @@ -169,7 +170,7 @@ static uint32_t drc_unisolate_logical(sPAPRDRConnector *drc) return RTAS_OUT_SUCCESS; } -static uint32_t drc_set_usable(sPAPRDRConnector *drc) +static uint32_t drc_set_usable(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_LOGICAL_AVAILABLE: @@ -201,7 +202,7 @@ static uint32_t drc_set_usable(sPAPRDRConnector *drc) return RTAS_OUT_SUCCESS; } -static uint32_t drc_set_unusable(sPAPRDRConnector *drc) +static uint32_t drc_set_unusable(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: @@ -225,9 +226,9 @@ static uint32_t drc_set_unusable(sPAPRDRConnector *drc) return RTAS_OUT_SUCCESS; } -static const char *spapr_drc_name(sPAPRDRConnector *drc) +static const char *spapr_drc_name(SpaprDrc *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); /* human-readable name for a DRC to encode into the DT * description. this is mainly only used within a guest in place @@ -260,7 +261,7 @@ static const char *spapr_drc_name(sPAPRDRConnector *drc) * based on the current allocation/indicator/power states * for the DR connector. */ -static sPAPRDREntitySense physical_entity_sense(sPAPRDRConnector *drc) +static SpaprDREntitySense physical_entity_sense(SpaprDrc *drc) { /* this assumes all PCI devices are assigned to a 'live insertion' * power domain, where QEMU manages power state automatically as @@ -271,7 +272,7 @@ static sPAPRDREntitySense physical_entity_sense(sPAPRDRConnector *drc) : SPAPR_DR_ENTITY_SENSE_EMPTY; } -static sPAPRDREntitySense logical_entity_sense(sPAPRDRConnector *drc) +static SpaprDREntitySense logical_entity_sense(SpaprDrc *drc) { switch (drc->state) { case SPAPR_DRC_STATE_LOGICAL_UNUSABLE: @@ -289,7 +290,7 @@ static sPAPRDREntitySense logical_entity_sense(sPAPRDRConnector *drc) static void prop_get_index(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(obj); uint32_t value = spapr_drc_index(drc); visit_type_uint32(v, name, &value, errp); } @@ -297,7 +298,7 @@ static void prop_get_index(Object *obj, Visitor *v, const char *name, static void prop_get_fdt(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(obj); QNull *null = NULL; Error *err = NULL; int fdt_offset_next, fdt_offset, fdt_depth; @@ -373,8 +374,7 @@ static void prop_get_fdt(Object *obj, Visitor *v, const char *name, } while (fdt_depth != 0); } -void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, - int fdt_start_offset, Error **errp) +void spapr_drc_attach(SpaprDrc *drc, DeviceState *d, Error **errp) { trace_spapr_drc_attach(spapr_drc_index(drc)); @@ -384,11 +384,8 @@ void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, } g_assert((drc->state == SPAPR_DRC_STATE_LOGICAL_UNUSABLE) || (drc->state == SPAPR_DRC_STATE_PHYSICAL_POWERON)); - g_assert(fdt); drc->dev = d; - drc->fdt = fdt; - drc->fdt_start_offset = fdt_start_offset; object_property_add_link(OBJECT(drc), "device", object_get_typename(OBJECT(drc->dev)), @@ -396,9 +393,9 @@ void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, NULL, 0, NULL); } -static void spapr_drc_release(sPAPRDRConnector *drc) +static void spapr_drc_release(SpaprDrc *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); drck->release(drc->dev); @@ -410,9 +407,9 @@ static void spapr_drc_release(sPAPRDRConnector *drc) drc->dev = NULL; } -void spapr_drc_detach(sPAPRDRConnector *drc) +void spapr_drc_detach(SpaprDrc *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); trace_spapr_drc_detach(spapr_drc_index(drc)); @@ -428,9 +425,9 @@ void spapr_drc_detach(sPAPRDRConnector *drc) spapr_drc_release(drc); } -void spapr_drc_reset(sPAPRDRConnector *drc) +void spapr_drc_reset(SpaprDrc *drc) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); trace_spapr_drc_reset(spapr_drc_index(drc)); @@ -459,8 +456,8 @@ void spapr_drc_reset(sPAPRDRConnector *drc) bool spapr_drc_needed(void *opaque) { - sPAPRDRConnector *drc = (sPAPRDRConnector *)opaque; - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrc *drc = (SpaprDrc *)opaque; + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); /* If no dev is plugged in there is no need to migrate the DRC state */ if (!drc->dev) { @@ -480,14 +477,14 @@ static const VMStateDescription vmstate_spapr_drc = { .minimum_version_id = 1, .needed = spapr_drc_needed, .fields = (VMStateField []) { - VMSTATE_UINT32(state, sPAPRDRConnector), + VMSTATE_UINT32(state, SpaprDrc), VMSTATE_END_OF_LIST() } }; static void realize(DeviceState *d, Error **errp) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); Object *root_container; gchar *link_name; gchar *child_name; @@ -520,7 +517,7 @@ static void realize(DeviceState *d, Error **errp) static void unrealize(DeviceState *d, Error **errp) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(d); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(d); Object *root_container; gchar *name; @@ -532,10 +529,10 @@ static void unrealize(DeviceState *d, Error **errp) g_free(name); } -sPAPRDRConnector *spapr_dr_connector_new(Object *owner, const char *type, +SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, uint32_t id) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(object_new(type)); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(object_new(type)); char *prop_name; drc->id = id; @@ -552,8 +549,8 @@ sPAPRDRConnector *spapr_dr_connector_new(Object *owner, const char *type, static void spapr_dr_connector_instance_init(Object *obj) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(obj); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(obj); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); object_property_add_uint32_ptr(obj, "id", &drc->id, NULL); object_property_add(obj, "index", "uint32", prop_get_index, @@ -577,8 +574,8 @@ static void spapr_dr_connector_class_init(ObjectClass *k, void *data) static bool drc_physical_needed(void *opaque) { - sPAPRDRCPhysical *drcp = (sPAPRDRCPhysical *)opaque; - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(drcp); + SpaprDrcPhysical *drcp = (SpaprDrcPhysical *)opaque; + SpaprDrc *drc = SPAPR_DR_CONNECTOR(drcp); if ((drc->dev && (drcp->dr_indicator == SPAPR_DR_INDICATOR_ACTIVE)) || (!drc->dev && (drcp->dr_indicator == SPAPR_DR_INDICATOR_INACTIVE))) { @@ -593,15 +590,15 @@ static const VMStateDescription vmstate_spapr_drc_physical = { .minimum_version_id = 1, .needed = drc_physical_needed, .fields = (VMStateField []) { - VMSTATE_UINT32(dr_indicator, sPAPRDRCPhysical), + VMSTATE_UINT32(dr_indicator, SpaprDrcPhysical), VMSTATE_END_OF_LIST() } }; static void drc_physical_reset(void *opaque) { - sPAPRDRConnector *drc = SPAPR_DR_CONNECTOR(opaque); - sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(drc); + SpaprDrc *drc = SPAPR_DR_CONNECTOR(opaque); + SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(drc); if (drc->dev) { drcp->dr_indicator = SPAPR_DR_INDICATOR_ACTIVE; @@ -612,7 +609,7 @@ static void drc_physical_reset(void *opaque) static void realize_physical(DeviceState *d, Error **errp) { - sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(d); + SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d); Error *local_err = NULL; realize(d, &local_err); @@ -628,7 +625,7 @@ static void realize_physical(DeviceState *d, Error **errp) static void unrealize_physical(DeviceState *d, Error **errp) { - sPAPRDRCPhysical *drcp = SPAPR_DRC_PHYSICAL(d); + SpaprDrcPhysical *drcp = SPAPR_DRC_PHYSICAL(d); Error *local_err = NULL; unrealize(d, &local_err); @@ -644,7 +641,7 @@ static void unrealize_physical(DeviceState *d, Error **errp) static void spapr_drc_physical_class_init(ObjectClass *k, void *data) { DeviceClass *dk = DEVICE_CLASS(k); - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); dk->realize = realize_physical; dk->unrealize = unrealize_physical; @@ -657,7 +654,7 @@ static void spapr_drc_physical_class_init(ObjectClass *k, void *data) static void spapr_drc_logical_class_init(ObjectClass *k, void *data) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); drck->dr_entity_sense = logical_entity_sense; drck->isolate = drc_isolate_logical; @@ -668,40 +665,54 @@ static void spapr_drc_logical_class_init(ObjectClass *k, void *data) static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_CPU; drck->typename = "CPU"; drck->drc_name_prefix = "CPU "; drck->release = spapr_core_release; + drck->dt_populate = spapr_core_dt_populate; } static void spapr_drc_pci_class_init(ObjectClass *k, void *data) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI; drck->typename = "28"; drck->drc_name_prefix = "C"; drck->release = spapr_phb_remove_pci_device_cb; + drck->dt_populate = spapr_pci_dt_populate; } static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) { - sPAPRDRConnectorClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB; drck->typename = "MEM"; drck->drc_name_prefix = "LMB "; drck->release = spapr_lmb_release; + drck->dt_populate = spapr_lmb_dt_populate; +} + +static void spapr_drc_phb_class_init(ObjectClass *k, void *data) +{ + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); + + drck->typeshift = SPAPR_DR_CONNECTOR_TYPE_SHIFT_PHB; + drck->typename = "PHB"; + drck->drc_name_prefix = "PHB "; + drck->release = spapr_phb_release; + drck->dt_populate = spapr_phb_dt_populate; } static const TypeInfo spapr_dr_connector_info = { .name = TYPE_SPAPR_DR_CONNECTOR, .parent = TYPE_DEVICE, - .instance_size = sizeof(sPAPRDRConnector), + .instance_size = sizeof(SpaprDrc), .instance_init = spapr_dr_connector_instance_init, - .class_size = sizeof(sPAPRDRConnectorClass), + .class_size = sizeof(SpaprDrcClass), .class_init = spapr_dr_connector_class_init, .abstract = true, }; @@ -709,7 +720,7 @@ static const TypeInfo spapr_dr_connector_info = { static const TypeInfo spapr_drc_physical_info = { .name = TYPE_SPAPR_DRC_PHYSICAL, .parent = TYPE_SPAPR_DR_CONNECTOR, - .instance_size = sizeof(sPAPRDRCPhysical), + .instance_size = sizeof(SpaprDrcPhysical), .class_init = spapr_drc_physical_class_init, .abstract = true, }; @@ -739,9 +750,16 @@ static const TypeInfo spapr_drc_lmb_info = { .class_init = spapr_drc_lmb_class_init, }; +static const TypeInfo spapr_drc_phb_info = { + .name = TYPE_SPAPR_DRC_PHB, + .parent = TYPE_SPAPR_DRC_LOGICAL, + .instance_size = sizeof(SpaprDrc), + .class_init = spapr_drc_phb_class_init, +}; + /* helper functions for external users */ -sPAPRDRConnector *spapr_drc_by_index(uint32_t index) +SpaprDrc *spapr_drc_by_index(uint32_t index) { Object *obj; gchar *name; @@ -753,9 +771,9 @@ sPAPRDRConnector *spapr_drc_by_index(uint32_t index) return !obj ? NULL : SPAPR_DR_CONNECTOR(obj); } -sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id) +SpaprDrc *spapr_drc_by_id(const char *type, uint32_t id) { - sPAPRDRConnectorClass *drck + SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(object_class_by_name(type)); return spapr_drc_by_index(drck->typeshift << DRC_INDEX_TYPE_SHIFT @@ -769,7 +787,7 @@ sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id) * @path: path in the DT to generate properties * @owner: parent Object/DeviceState for which to generate DRC * descriptions for - * @drc_type_mask: mask of sPAPRDRConnectorType values corresponding + * @drc_type_mask: mask of SpaprDrcType values corresponding * to the types of DRCs to generate entries for * * generate OF properties to describe DRC topology/indices to guests @@ -808,8 +826,8 @@ int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, object_property_iter_init(&iter, root_container); while ((prop = object_property_iter_next(&iter))) { Object *obj; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; + SpaprDrc *drc; + SpaprDrcClass *drck; uint32_t drc_index, drc_power_domain; if (!strstart(prop->type, "link<", NULL)) { @@ -900,8 +918,8 @@ out: static uint32_t rtas_set_isolation_state(uint32_t idx, uint32_t state) { - sPAPRDRConnector *drc = spapr_drc_by_index(idx); - sPAPRDRConnectorClass *drck; + SpaprDrc *drc = spapr_drc_by_index(idx); + SpaprDrcClass *drck; if (!drc) { return RTAS_OUT_NO_SUCH_INDICATOR; @@ -925,7 +943,7 @@ static uint32_t rtas_set_isolation_state(uint32_t idx, uint32_t state) static uint32_t rtas_set_allocation_state(uint32_t idx, uint32_t state) { - sPAPRDRConnector *drc = spapr_drc_by_index(idx); + SpaprDrc *drc = spapr_drc_by_index(idx); if (!drc || !object_dynamic_cast(OBJECT(drc), TYPE_SPAPR_DRC_LOGICAL)) { return RTAS_OUT_NO_SUCH_INDICATOR; @@ -947,7 +965,7 @@ static uint32_t rtas_set_allocation_state(uint32_t idx, uint32_t state) static uint32_t rtas_set_dr_indicator(uint32_t idx, uint32_t state) { - sPAPRDRConnector *drc = spapr_drc_by_index(idx); + SpaprDrc *drc = spapr_drc_by_index(idx); if (!drc || !object_dynamic_cast(OBJECT(drc), TYPE_SPAPR_DRC_PHYSICAL)) { return RTAS_OUT_NO_SUCH_INDICATOR; @@ -964,7 +982,7 @@ static uint32_t rtas_set_dr_indicator(uint32_t idx, uint32_t state) return RTAS_OUT_SUCCESS; } -static void rtas_set_indicator(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_set_indicator(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -999,7 +1017,7 @@ out: rtas_st(rets, 0, ret); } -static void rtas_get_sensor_state(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_get_sensor_state(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -1007,8 +1025,8 @@ 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; + SpaprDrc *drc; + SpaprDrcClass *drck; uint32_t ret = RTAS_OUT_SUCCESS; if (nargs != 2 || nret != 2) { @@ -1061,7 +1079,7 @@ static void configure_connector_st(target_ulong addr, target_ulong offset, } static void rtas_ibm_configure_connector(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -1069,9 +1087,9 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, uint64_t wa_addr; uint64_t wa_offset; uint32_t drc_index; - sPAPRDRConnector *drc; - sPAPRDRConnectorClass *drck; - sPAPRDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; + SpaprDrc *drc; + SpaprDrcClass *drck; + SpaprDRCCResponse resp = SPAPR_DR_CC_RESPONSE_CONTINUE; int rc; if (nargs != 2 || nret != 1) { @@ -1102,10 +1120,28 @@ static void rtas_ibm_configure_connector(PowerPCCPU *cpu, goto out; } - g_assert(drc->fdt); - drck = SPAPR_DR_CONNECTOR_GET_CLASS(drc); + if (!drc->fdt) { + Error *local_err = NULL; + void *fdt; + int fdt_size; + + fdt = create_device_tree(&fdt_size); + + if (drck->dt_populate(drc, spapr, fdt, &drc->fdt_start_offset, + &local_err)) { + g_free(fdt); + error_free(local_err); + rc = SPAPR_DR_CC_RESPONSE_ERROR; + goto out; + } + + drc->fdt = fdt; + drc->ccs_offset = drc->fdt_start_offset; + drc->ccs_depth = 0; + } + do { uint32_t tag; const char *name; @@ -1189,6 +1225,7 @@ static void spapr_drc_register_types(void) type_register_static(&spapr_drc_cpu_info); type_register_static(&spapr_drc_pci_info); type_register_static(&spapr_drc_lmb_info); + type_register_static(&spapr_drc_phb_info); spapr_rtas_register(RTAS_SET_INDICATOR, "set-indicator", rtas_set_indicator); diff --git a/hw/ppc/spapr_events.c b/hw/ppc/spapr_events.c index e4f5946a21..ae0f093f59 100644 --- a/hw/ppc/spapr_events.c +++ b/hw/ppc/spapr_events.c @@ -229,18 +229,18 @@ static const char * const event_names[EVENT_CLASS_MAX] = { [EVENT_CLASS_IO] = "ibm,io-events", }; -struct sPAPREventSource { +struct SpaprEventSource { int irq; uint32_t mask; bool enabled; }; -static sPAPREventSource *spapr_event_sources_new(void) +static SpaprEventSource *spapr_event_sources_new(void) { - return g_new0(sPAPREventSource, EVENT_CLASS_MAX); + return g_new0(SpaprEventSource, EVENT_CLASS_MAX); } -static void spapr_event_sources_register(sPAPREventSource *event_sources, +static void spapr_event_sources_register(SpaprEventSource *event_sources, EventClassIndex index, int irq) { /* we only support 1 irq per event class at the moment */ @@ -251,8 +251,8 @@ static void spapr_event_sources_register(sPAPREventSource *event_sources, event_sources[index].enabled = true; } -static const sPAPREventSource * -spapr_event_sources_get_source(sPAPREventSource *event_sources, +static const SpaprEventSource * +spapr_event_sources_get_source(SpaprEventSource *event_sources, EventClassIndex index) { g_assert(index < EVENT_CLASS_MAX); @@ -261,11 +261,11 @@ spapr_event_sources_get_source(sPAPREventSource *event_sources, return &event_sources[index]; } -void spapr_dt_events(sPAPRMachineState *spapr, void *fdt) +void spapr_dt_events(SpaprMachineState *spapr, void *fdt) { uint32_t irq_ranges[EVENT_CLASS_MAX * 2]; int i, count = 0, event_sources; - sPAPREventSource *events = spapr->event_sources; + SpaprEventSource *events = spapr->event_sources; g_assert(events); @@ -274,7 +274,7 @@ void spapr_dt_events(sPAPRMachineState *spapr, void *fdt) for (i = 0, count = 0; i < EVENT_CLASS_MAX; i++) { int node_offset; uint32_t interrupts[2]; - const sPAPREventSource *source = + const SpaprEventSource *source = spapr_event_sources_get_source(events, i); const char *source_name = event_names[i]; @@ -282,7 +282,7 @@ void spapr_dt_events(sPAPRMachineState *spapr, void *fdt) continue; } - spapr_dt_xics_irq(interrupts, source->irq, false); + spapr_dt_irq(interrupts, source->irq, false); _FDT(node_offset = fdt_add_subnode(fdt, event_sources, source_name)); _FDT(fdt_setprop(fdt, node_offset, "interrupts", interrupts, @@ -298,10 +298,10 @@ void spapr_dt_events(sPAPRMachineState *spapr, void *fdt) irq_ranges, count * sizeof(uint32_t)))); } -static const sPAPREventSource * -rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type) +static const SpaprEventSource * +rtas_event_log_to_source(SpaprMachineState *spapr, int log_type) { - const sPAPREventSource *source; + const SpaprEventSource *source; g_assert(spapr->event_sources); @@ -325,9 +325,9 @@ rtas_event_log_to_source(sPAPRMachineState *spapr, int log_type) return source; } -static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type) +static int rtas_event_log_to_irq(SpaprMachineState *spapr, int log_type) { - const sPAPREventSource *source; + const SpaprEventSource *source; source = rtas_event_log_to_source(spapr, log_type); g_assert(source); @@ -336,24 +336,24 @@ static int rtas_event_log_to_irq(sPAPRMachineState *spapr, int log_type) return source->irq; } -static uint32_t spapr_event_log_entry_type(sPAPREventLogEntry *entry) +static uint32_t spapr_event_log_entry_type(SpaprEventLogEntry *entry) { return entry->summary & RTAS_LOG_TYPE_MASK; } -static void rtas_event_log_queue(sPAPRMachineState *spapr, - sPAPREventLogEntry *entry) +static void rtas_event_log_queue(SpaprMachineState *spapr, + SpaprEventLogEntry *entry) { QTAILQ_INSERT_TAIL(&spapr->pending_events, entry, next); } -static sPAPREventLogEntry *rtas_event_log_dequeue(sPAPRMachineState *spapr, +static SpaprEventLogEntry *rtas_event_log_dequeue(SpaprMachineState *spapr, uint32_t event_mask) { - sPAPREventLogEntry *entry = NULL; + SpaprEventLogEntry *entry = NULL; QTAILQ_FOREACH(entry, &spapr->pending_events, next) { - const sPAPREventSource *source = + const SpaprEventSource *source = rtas_event_log_to_source(spapr, spapr_event_log_entry_type(entry)); @@ -371,11 +371,11 @@ static sPAPREventLogEntry *rtas_event_log_dequeue(sPAPRMachineState *spapr, static bool rtas_event_log_contains(uint32_t event_mask) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry = NULL; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprEventLogEntry *entry = NULL; QTAILQ_FOREACH(entry, &spapr->pending_events, next) { - const sPAPREventSource *source = + const SpaprEventSource *source = rtas_event_log_to_source(spapr, spapr_event_log_entry_type(entry)); @@ -401,7 +401,7 @@ static void spapr_init_v6hdr(struct rtas_event_log_v6 *v6hdr) static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, int section_count) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); struct tm tm; int year; @@ -424,15 +424,15 @@ static void spapr_init_maina(struct rtas_event_log_v6_maina *maina, static void spapr_powerdown_req(Notifier *n, void *opaque) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprEventLogEntry *entry; struct rtas_event_log_v6 *v6hdr; struct rtas_event_log_v6_maina *maina; struct rtas_event_log_v6_mainb *mainb; struct rtas_event_log_v6_epow *epow; struct epow_extended_log *new_epow; - entry = g_new(sPAPREventLogEntry, 1); + entry = g_new(SpaprEventLogEntry, 1); new_epow = g_malloc0(sizeof(*new_epow)); entry->extended_log = new_epow; @@ -473,18 +473,18 @@ static void spapr_powerdown_req(Notifier *n, void *opaque) } static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, - sPAPRDRConnectorType drc_type, + SpaprDrcType drc_type, union drc_identifier *drc_id) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPREventLogEntry *entry; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprEventLogEntry *entry; struct hp_extended_log *new_hp; struct rtas_event_log_v6 *v6hdr; struct rtas_event_log_v6_maina *maina; struct rtas_event_log_v6_mainb *mainb; struct rtas_event_log_v6_hp *hp; - entry = g_new(sPAPREventLogEntry, 1); + entry = g_new(SpaprEventLogEntry, 1); new_hp = g_malloc0(sizeof(struct hp_extended_log)); entry->extended_log = new_hp; @@ -526,6 +526,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, case SPAPR_DR_CONNECTOR_TYPE_CPU: hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_CPU; break; + case SPAPR_DR_CONNECTOR_TYPE_PHB: + hp->hotplug_type = RTAS_LOG_V6_HP_TYPE_PHB; + break; default: /* we shouldn't be signaling hotplug events for resources * that don't support them @@ -555,9 +558,9 @@ static void spapr_hotplug_req_event(uint8_t hp_id, uint8_t hp_action, rtas_event_log_to_irq(spapr, RTAS_LOG_TYPE_HOTPLUG))); } -void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) +void spapr_hotplug_req_add_by_index(SpaprDrc *drc) { - sPAPRDRConnectorType drc_type = spapr_drc_type(drc); + SpaprDrcType drc_type = spapr_drc_type(drc); union drc_identifier drc_id; drc_id.index = spapr_drc_index(drc); @@ -565,9 +568,9 @@ void spapr_hotplug_req_add_by_index(sPAPRDRConnector *drc) RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } -void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) +void spapr_hotplug_req_remove_by_index(SpaprDrc *drc) { - sPAPRDRConnectorType drc_type = spapr_drc_type(drc); + SpaprDrcType drc_type = spapr_drc_type(drc); union drc_identifier drc_id; drc_id.index = spapr_drc_index(drc); @@ -575,7 +578,7 @@ void spapr_hotplug_req_remove_by_index(sPAPRDRConnector *drc) RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } -void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_add_by_count(SpaprDrcType drc_type, uint32_t count) { union drc_identifier drc_id; @@ -585,7 +588,7 @@ void spapr_hotplug_req_add_by_count(sPAPRDRConnectorType drc_type, RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } -void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_remove_by_count(SpaprDrcType drc_type, uint32_t count) { union drc_identifier drc_id; @@ -595,7 +598,7 @@ void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } -void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_add_by_count_indexed(SpaprDrcType drc_type, uint32_t count, uint32_t index) { union drc_identifier drc_id; @@ -606,7 +609,7 @@ void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, RTAS_LOG_V6_HP_ACTION_ADD, drc_type, &drc_id); } -void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type, uint32_t count, uint32_t index) { union drc_identifier drc_id; @@ -617,14 +620,14 @@ void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, RTAS_LOG_V6_HP_ACTION_REMOVE, drc_type, &drc_id); } -static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void check_exception(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { uint32_t mask, buf, len, event_len; uint64_t xinfo; - sPAPREventLogEntry *event; + SpaprEventLogEntry *event; struct rtas_error_log header; int i; @@ -668,7 +671,7 @@ static void check_exception(PowerPCCPU *cpu, sPAPRMachineState *spapr, */ for (i = 0; i < EVENT_CLASS_MAX; i++) { if (rtas_event_log_contains(EVENT_CLASS_MASK(i))) { - const sPAPREventSource *source = + const SpaprEventSource *source = spapr_event_sources_get_source(spapr->event_sources, i); g_assert(source->enabled); @@ -682,7 +685,7 @@ out_no_events: rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } -static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void event_scan(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -694,9 +697,9 @@ static void event_scan(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_NO_ERRORS_FOUND); } -void spapr_clear_pending_events(sPAPRMachineState *spapr) +void spapr_clear_pending_events(SpaprMachineState *spapr) { - sPAPREventLogEntry *entry = NULL, *next_entry; + SpaprEventLogEntry *entry = NULL, *next_entry; QTAILQ_FOREACH_SAFE(entry, &spapr->pending_events, next, next_entry) { QTAILQ_REMOVE(&spapr->pending_events, entry, next); @@ -705,11 +708,13 @@ void spapr_clear_pending_events(sPAPRMachineState *spapr) } } -void spapr_events_init(sPAPRMachineState *spapr) +void spapr_events_init(SpaprMachineState *spapr) { - int epow_irq; + int epow_irq = SPAPR_IRQ_EPOW; - epow_irq = spapr_irq_findone(spapr, &error_fatal); + if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { + epow_irq = spapr_irq_findone(spapr, &error_fatal); + } spapr_irq_claim(spapr, epow_irq, false, &error_fatal); @@ -729,9 +734,11 @@ void spapr_events_init(sPAPRMachineState *spapr) * checking that it's enabled. */ if (spapr->use_hotplug_event_source) { - int hp_irq; + int hp_irq = SPAPR_IRQ_HOTPLUG; - hp_irq = spapr_irq_findone(spapr, &error_fatal); + if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { + hp_irq = spapr_irq_findone(spapr, &error_fatal); + } spapr_irq_claim(spapr, hp_irq, false, &error_fatal); diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index ae913d070f..8a736797b9 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -17,37 +17,6 @@ #include "mmu-book3s-v3.h" #include "hw/mem/memory-device.h" -struct LPCRSyncState { - target_ulong value; - target_ulong mask; -}; - -static void do_lpcr_sync(CPUState *cs, run_on_cpu_data arg) -{ - struct LPCRSyncState *s = arg.host_ptr; - PowerPCCPU *cpu = POWERPC_CPU(cs); - CPUPPCState *env = &cpu->env; - target_ulong lpcr; - - cpu_synchronize_state(cs); - lpcr = env->spr[SPR_LPCR]; - lpcr &= ~s->mask; - lpcr |= s->value; - ppc_store_lpcr(cpu, lpcr); -} - -static void set_all_lpcrs(target_ulong value, target_ulong mask) -{ - CPUState *cs; - struct LPCRSyncState s = { - .value = value, - .mask = mask - }; - CPU_FOREACH(cs) { - run_on_cpu(cs, do_lpcr_sync, RUN_ON_CPU_HOST_PTR(&s)); - } -} - static bool has_spr(PowerPCCPU *cpu, int spr) { /* We can test whether the SPR is defined by checking for a valid name */ @@ -65,7 +34,7 @@ static inline bool valid_ptex(PowerPCCPU *cpu, target_ulong ptex) return true; } -static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) +static bool is_ram_address(SpaprMachineState *spapr, hwaddr addr) { MachineState *machine = MACHINE(spapr); DeviceMemoryState *dms = machine->device_memory; @@ -81,7 +50,7 @@ static bool is_ram_address(sPAPRMachineState *spapr, hwaddr addr) return false; } -static target_ulong h_enter(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_enter(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -191,7 +160,7 @@ static RemoveResult remove_hpte(PowerPCCPU *cpu, target_ulong ptex, return REMOVE_SUCCESS; } -static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_remove(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUPPCState *env = &cpu->env; @@ -239,7 +208,7 @@ static target_ulong h_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, #define H_BULK_REMOVE_MAX_BATCH 4 -static target_ulong h_bulk_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; @@ -291,7 +260,7 @@ static target_ulong h_bulk_remove(PowerPCCPU *cpu, sPAPRMachineState *spapr, return rc; } -static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_protect(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUPPCState *env = &cpu->env; @@ -330,7 +299,7 @@ static target_ulong h_protect(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_read(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -359,7 +328,7 @@ static target_ulong h_read(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -struct sPAPRPendingHPT { +struct SpaprPendingHpt { /* These fields are read-only after initialization */ int shift; QemuThread thread; @@ -373,7 +342,7 @@ struct sPAPRPendingHPT { void *hpt; }; -static void free_pending_hpt(sPAPRPendingHPT *pending) +static void free_pending_hpt(SpaprPendingHpt *pending) { if (pending->hpt) { qemu_vfree(pending->hpt); @@ -384,7 +353,7 @@ static void free_pending_hpt(sPAPRPendingHPT *pending) static void *hpt_prepare_thread(void *opaque) { - sPAPRPendingHPT *pending = opaque; + SpaprPendingHpt *pending = opaque; size_t size = 1ULL << pending->shift; pending->hpt = qemu_memalign(size, size); @@ -410,9 +379,9 @@ static void *hpt_prepare_thread(void *opaque) } /* Must be called with BQL held */ -static void cancel_hpt_prepare(sPAPRMachineState *spapr) +static void cancel_hpt_prepare(SpaprMachineState *spapr) { - sPAPRPendingHPT *pending = spapr->pending_hpt; + SpaprPendingHpt *pending = spapr->pending_hpt; /* Let the thread know it's cancelled */ spapr->pending_hpt = NULL; @@ -469,13 +438,13 @@ static target_ulong resize_hpt_convert_rc(int ret) } static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; int shift = args[1]; - sPAPRPendingHPT *pending = spapr->pending_hpt; + SpaprPendingHpt *pending = spapr->pending_hpt; uint64_t current_ram_size; int rc; @@ -534,7 +503,7 @@ static target_ulong h_resize_hpt_prepare(PowerPCCPU *cpu, /* start new prepare */ - pending = g_new0(sPAPRPendingHPT, 1); + pending = g_new0(SpaprPendingHpt, 1); pending->shift = shift; pending->ret = H_HARDWARE; @@ -703,7 +672,7 @@ static void do_push_sregs_to_kvm_pr(CPUState *cs, run_on_cpu_data data) } } -static void push_sregs_to_kvm_pr(sPAPRMachineState *spapr) +static void push_sregs_to_kvm_pr(SpaprMachineState *spapr) { CPUState *cs; @@ -722,13 +691,13 @@ static void push_sregs_to_kvm_pr(sPAPRMachineState *spapr) } static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; target_ulong shift = args[1]; - sPAPRPendingHPT *pending = spapr->pending_hpt; + SpaprPendingHpt *pending = spapr->pending_hpt; int rc; size_t newsize; @@ -790,7 +759,7 @@ static target_ulong h_resize_hpt_commit(PowerPCCPU *cpu, return rc; } -static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_set_sprg0(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { cpu_synchronize_state(CPU(cpu)); @@ -799,7 +768,7 @@ static target_ulong h_set_sprg0(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_set_dabr(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { if (!has_spr(cpu, SPR_DABR)) { @@ -817,7 +786,7 @@ static target_ulong h_set_dabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_set_xdabr(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong dabrx = args[1]; @@ -838,7 +807,7 @@ static target_ulong h_set_xdabr(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_page_init(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_page_init(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -913,7 +882,7 @@ static target_ulong register_vpa(PowerPCCPU *cpu, target_ulong vpa) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); uint16_t size; uint8_t tmp; @@ -949,7 +918,7 @@ static target_ulong register_vpa(PowerPCCPU *cpu, target_ulong vpa) static target_ulong deregister_vpa(PowerPCCPU *cpu, target_ulong vpa) { - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); if (spapr_cpu->slb_shadow_addr) { return H_RESOURCE; @@ -965,7 +934,7 @@ static target_ulong deregister_vpa(PowerPCCPU *cpu, target_ulong vpa) static target_ulong register_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -994,7 +963,7 @@ static target_ulong register_slb_shadow(PowerPCCPU *cpu, target_ulong addr) static target_ulong deregister_slb_shadow(PowerPCCPU *cpu, target_ulong addr) { - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); spapr_cpu->slb_shadow_addr = 0; spapr_cpu->slb_shadow_size = 0; @@ -1003,7 +972,7 @@ static target_ulong deregister_slb_shadow(PowerPCCPU *cpu, target_ulong addr) static target_ulong register_dtl(PowerPCCPU *cpu, target_ulong addr) { - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); uint32_t size; if (addr == 0) { @@ -1029,7 +998,7 @@ static target_ulong register_dtl(PowerPCCPU *cpu, target_ulong addr) static target_ulong deregister_dtl(PowerPCCPU *cpu, target_ulong addr) { - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); spapr_cpu->dtl_addr = 0; spapr_cpu->dtl_size = 0; @@ -1037,7 +1006,7 @@ static target_ulong deregister_dtl(PowerPCCPU *cpu, target_ulong addr) return H_SUCCESS; } -static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_register_vpa(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong flags = args[0]; @@ -1080,7 +1049,7 @@ static target_ulong h_register_vpa(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -static target_ulong h_cede(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_cede(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUPPCState *env = &cpu->env; @@ -1096,7 +1065,7 @@ static target_ulong h_cede(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_rtas(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_rtas(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong rtas_r3 = args[0]; @@ -1108,7 +1077,7 @@ static target_ulong h_rtas(PowerPCCPU *cpu, sPAPRMachineState *spapr, nret, rtas_r3 + 12 + 4*nargs); } -static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_logical_load(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); @@ -1132,7 +1101,7 @@ static target_ulong h_logical_load(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } -static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_logical_store(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); @@ -1158,7 +1127,7 @@ static target_ulong h_logical_store(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_PARAMETER; } -static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_logical_memop(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { CPUState *cs = CPU(cpu); @@ -1227,14 +1196,14 @@ static target_ulong h_logical_memop(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong h_logical_icbi(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_logical_icbi(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { /* Nothing to do on emulation, KVM will trap this in the kernel */ return H_SUCCESS; } -static target_ulong h_logical_dcbf(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_logical_dcbf(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { /* Nothing to do on emulation, KVM will trap this in the kernel */ @@ -1255,12 +1224,12 @@ static target_ulong h_set_mode_resource_le(PowerPCCPU *cpu, switch (mflags) { case H_SET_MODE_ENDIAN_BIG: - set_all_lpcrs(0, LPCR_ILE); + spapr_set_all_lpcrs(0, LPCR_ILE); spapr_pci_switch_vga(true); return H_SUCCESS; case H_SET_MODE_ENDIAN_LITTLE: - set_all_lpcrs(LPCR_ILE, LPCR_ILE); + spapr_set_all_lpcrs(LPCR_ILE, LPCR_ILE); spapr_pci_switch_vga(false); return H_SUCCESS; } @@ -1289,12 +1258,12 @@ static target_ulong h_set_mode_resource_addr_trans_mode(PowerPCCPU *cpu, return H_UNSUPPORTED_FLAG; } - set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); + spapr_set_all_lpcrs(mflags << LPCR_AIL_SHIFT, LPCR_AIL); return H_SUCCESS; } -static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_set_mode(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong resource = args[1]; @@ -1313,7 +1282,7 @@ static target_ulong h_set_mode(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -static target_ulong h_clean_slb(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_clean_slb(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", @@ -1321,7 +1290,7 @@ static target_ulong h_clean_slb(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_FUNCTION; } -static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_invalidate_pid(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { qemu_log_mask(LOG_UNIMP, "Unimplemented SPAPR hcall 0x"TARGET_FMT_lx"%s\n", @@ -1329,7 +1298,7 @@ static target_ulong h_invalidate_pid(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_FUNCTION; } -static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, +static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, uint64_t patbe_old, uint64_t patbe_new) { /* @@ -1342,12 +1311,12 @@ static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, * later and so assumed radix and now it's called H_REG_PROC_TBL */ - if ((patbe_old & PATBE1_GR) == (patbe_new & PATBE1_GR)) { + if ((patbe_old & PATE1_GR) == (patbe_new & PATE1_GR)) { /* We assume RADIX, so this catches all the "Do Nothing" cases */ - } else if (!(patbe_old & PATBE1_GR)) { + } else if (!(patbe_old & PATE1_GR)) { /* HASH->RADIX : Free HPT */ spapr_free_hpt(spapr); - } else if (!(patbe_new & PATBE1_GR)) { + } else if (!(patbe_new & PATE1_GR)) { /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ spapr_setup_hpt_and_vrma(spapr); } @@ -1362,7 +1331,7 @@ static void spapr_check_setup_free_hpt(sPAPRMachineState *spapr, #define FLAG_GTSE 0x01 static target_ulong h_register_process_table(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -1370,6 +1339,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, target_ulong proc_tbl = args[1]; target_ulong page_size = args[2]; target_ulong table_size = args[3]; + target_ulong update_lpcr = 0; uint64_t cproc; if (flags & ~FLAGS_MASK) { /* Check no reserved bits are set */ @@ -1385,7 +1355,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, } else if (table_size > 24) { return H_P4; } - cproc = PATBE1_GR | proc_tbl | table_size; + cproc = PATE1_GR | proc_tbl | table_size; } else { /* Register new HPT process table */ if (flags & FLAG_HASH_PROC_TBL) { /* Hash with Segment Tables */ /* TODO - Not Supported */ @@ -1404,13 +1374,15 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, } } else { /* Deregister current process table */ - /* Set to benign value: (current GR) | 0. This allows - * deregistration in KVM to succeed even if the radix bit in flags - * doesn't match the radix bit in the old PATB. */ - cproc = spapr->patb_entry & PATBE1_GR; + /* + * Set to benign value: (current GR) | 0. This allows + * deregistration in KVM to succeed even if the radix bit + * in flags doesn't match the radix bit in the old PATE. + */ + cproc = spapr->patb_entry & PATE1_GR; } } else { /* Maintain current registration */ - if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATBE1_GR)) { + if (!(flags & FLAG_RADIX) != !(spapr->patb_entry & PATE1_GR)) { /* Technically caused by flag bits => H_PARAMETER */ return H_PARAMETER; /* Existing Process Table Mismatch */ } @@ -1422,10 +1394,15 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, spapr->patb_entry = cproc; /* Save new process table */ - /* Update the UPRT and GTSE bits in the LPCR for all cpus */ - set_all_lpcrs(((flags & (FLAG_RADIX | FLAG_HASH_PROC_TBL)) ? LPCR_UPRT : 0) | - ((flags & FLAG_GTSE) ? LPCR_GTSE : 0), - LPCR_UPRT | LPCR_GTSE); + /* Update the UPRT, HR and GTSE bits in the LPCR for all cpus */ + if (flags & FLAG_RADIX) /* Radix must use process tables, also set HR */ + update_lpcr |= (LPCR_UPRT | LPCR_HR); + else if (flags & FLAG_HASH_PROC_TBL) /* Hash with process tables */ + update_lpcr |= LPCR_UPRT; + if (flags & FLAG_GTSE) /* Guest translation shootdown enable */ + update_lpcr |= LPCR_GTSE; + + spapr_set_all_lpcrs(update_lpcr, LPCR_UPRT | LPCR_HR | LPCR_GTSE); if (kvm_enabled()) { return kvmppc_configure_v3_mmu(cpu, flags & FLAG_RADIX, @@ -1438,7 +1415,7 @@ static target_ulong h_register_process_table(PowerPCCPU *cpu, #define H_SIGNAL_SYS_RESET_ALLBUTSELF -2 static target_ulong h_signal_sys_reset(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_long target = args[0]; @@ -1473,7 +1450,7 @@ static target_ulong h_signal_sys_reset(PowerPCCPU *cpu, } } -static uint32_t cas_check_pvr(sPAPRMachineState *spapr, PowerPCCPU *cpu, +static uint32_t cas_check_pvr(SpaprMachineState *spapr, PowerPCCPU *cpu, target_ulong *addr, bool *raw_mode_supported, Error **errp) { @@ -1524,7 +1501,7 @@ static uint32_t cas_check_pvr(sPAPRMachineState *spapr, PowerPCCPU *cpu, } static target_ulong h_client_architecture_support(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -1532,7 +1509,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, target_ulong addr = ppc64_phys_to_real(args[0]); target_ulong ov_table; uint32_t cas_pvr; - sPAPROptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; + SpaprOptionVector *ov1_guest, *ov5_guest, *ov5_cas_old, *ov5_updates; bool guest_radix; Error *local_err = NULL; bool raw_mode_supported = false; @@ -1646,7 +1623,7 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, if (!spapr->cas_reboot) { /* If spapr_machine_reset() did not set up a HPT but one is necessary * (because the guest isn't going to use radix) then set it up here. */ - if ((spapr->patb_entry & PATBE1_GR) && !guest_radix) { + if ((spapr->patb_entry & PATE1_GR) && !guest_radix) { /* legacy hash or new hash: */ spapr_setup_hpt_and_vrma(spapr); } @@ -1654,6 +1631,17 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, (spapr_h_cas_compose_response(spapr, args[1], args[2], ov5_updates) != 0); } + + /* + * Generate a machine reset when we have an update of the + * interrupt mode. Only required when the machine supports both + * modes. + */ + if (!spapr->cas_reboot) { + spapr->cas_reboot = spapr_ovec_test(ov5_updates, OV5_XIVE_EXPLOIT) + && spapr->irq->ov5 & SPAPR_OV5_XIVE_BOTH; + } + spapr_ovec_cleanup(ov5_updates); if (spapr->cas_reboot) { @@ -1663,8 +1651,44 @@ static target_ulong h_client_architecture_support(PowerPCCPU *cpu, return H_SUCCESS; } +static target_ulong h_home_node_associativity(PowerPCCPU *cpu, + SpaprMachineState *spapr, + target_ulong opcode, + target_ulong *args) +{ + target_ulong flags = args[0]; + target_ulong procno = args[1]; + PowerPCCPU *tcpu; + int idx; + + /* only support procno from H_REGISTER_VPA */ + if (flags != 0x1) { + return H_FUNCTION; + } + + tcpu = spapr_find_cpu(procno); + if (tcpu == NULL) { + return H_P2; + } + + /* sequence is the same as in the "ibm,associativity" property */ + + idx = 0; +#define ASSOCIATIVITY(a, b) (((uint64_t)(a) << 32) | \ + ((uint64_t)(b) & 0xffffffff)) + args[idx++] = ASSOCIATIVITY(0, 0); + args[idx++] = ASSOCIATIVITY(0, tcpu->node_id); + args[idx++] = ASSOCIATIVITY(procno, -1); + for ( ; idx < 6; idx++) { + args[idx] = -1; + } +#undef ASSOCIATIVITY + + return H_SUCCESS; +} + static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { @@ -1674,6 +1698,8 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, uint8_t safe_cache = spapr_get_cap(spapr, SPAPR_CAP_CFPC); uint8_t safe_bounds_check = spapr_get_cap(spapr, SPAPR_CAP_SBBC); uint8_t safe_indirect_branch = spapr_get_cap(spapr, SPAPR_CAP_IBS); + uint8_t count_cache_flush_assist = spapr_get_cap(spapr, + SPAPR_CAP_CCF_ASSIST); switch (safe_cache) { case SPAPR_CAP_WORKAROUND: @@ -1704,12 +1730,20 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, } switch (safe_indirect_branch) { + case SPAPR_CAP_FIXED_NA: + break; case SPAPR_CAP_FIXED_CCD: characteristics |= H_CPU_CHAR_CACHE_COUNT_DIS; break; case SPAPR_CAP_FIXED_IBS: characteristics |= H_CPU_CHAR_BCCTRL_SERIALISED; break; + case SPAPR_CAP_WORKAROUND: + behaviour |= H_CPU_BEHAV_FLUSH_COUNT_CACHE; + if (count_cache_flush_assist) { + characteristics |= H_CPU_CHAR_BCCTR_FLUSH_ASSIST; + } + break; default: /* broken */ assert(safe_indirect_branch == SPAPR_CAP_BROKEN); break; @@ -1717,6 +1751,46 @@ static target_ulong h_get_cpu_characteristics(PowerPCCPU *cpu, args[0] = characteristics; args[1] = behaviour; + return H_SUCCESS; +} + +static target_ulong h_update_dt(PowerPCCPU *cpu, SpaprMachineState *spapr, + target_ulong opcode, target_ulong *args) +{ + target_ulong dt = ppc64_phys_to_real(args[0]); + struct fdt_header hdr = { 0 }; + unsigned cb; + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); + void *fdt; + + cpu_physical_memory_read(dt, &hdr, sizeof(hdr)); + cb = fdt32_to_cpu(hdr.totalsize); + + if (!smc->update_dt_enabled) { + return H_SUCCESS; + } + + /* Check that the fdt did not grow out of proportion */ + if (cb > spapr->fdt_initial_size * 2) { + trace_spapr_update_dt_failed_size(spapr->fdt_initial_size, cb, + fdt32_to_cpu(hdr.magic)); + return H_PARAMETER; + } + + fdt = g_malloc0(cb); + cpu_physical_memory_read(dt, fdt, cb); + + /* Check the fdt consistency */ + if (fdt_check_full(fdt, cb)) { + trace_spapr_update_dt_failed_check(spapr->fdt_initial_size, cb, + fdt32_to_cpu(hdr.magic)); + return H_PARAMETER; + } + + g_free(spapr->fdt_blob); + spapr->fdt_size = cb; + spapr->fdt_blob = fdt; + trace_spapr_update_dt(cb); return H_SUCCESS; } @@ -1745,7 +1819,7 @@ void spapr_register_hypercall(target_ulong opcode, spapr_hcall_fn fn) target_ulong spapr_hypercall(PowerPCCPU *cpu, target_ulong opcode, target_ulong *args) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); if ((opcode <= MAX_HCALL_OPCODE) && ((opcode & 0x3) == 0)) { @@ -1822,6 +1896,12 @@ static void hypercall_register_types(void) /* ibm,client-architecture-support support */ spapr_register_hypercall(KVMPPC_H_CAS, h_client_architecture_support); + + spapr_register_hypercall(KVMPPC_H_UPDATE_DT, h_update_dt); + + /* Virtual Processor Home Node */ + spapr_register_hypercall(H_HOME_NODE_ASSOCIATIVITY, + h_home_node_associativity); } type_init(hypercall_register_types) diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index 1b0880ac9e..5aff4d5a05 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -32,7 +32,7 @@ #include -enum sPAPRTCEAccess { +enum SpaprTceAccess { SPAPR_TCE_FAULT = 0, SPAPR_TCE_RO = 1, SPAPR_TCE_WO = 2, @@ -42,11 +42,11 @@ enum sPAPRTCEAccess { #define IOMMU_PAGE_SIZE(shift) (1ULL << (shift)) #define IOMMU_PAGE_MASK(shift) (~(IOMMU_PAGE_SIZE(shift) - 1)) -static QLIST_HEAD(spapr_tce_tables, sPAPRTCETable) spapr_tce_tables; +static QLIST_HEAD(, SpaprTceTable) spapr_tce_tables; -sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn) +SpaprTceTable *spapr_tce_find_by_liobn(target_ulong liobn) { - sPAPRTCETable *tcet; + SpaprTceTable *tcet; if (liobn & 0xFFFFFFFF00000000ULL) { hcall_dprintf("Request for out-of-bounds LIOBN 0x" TARGET_FMT_lx "\n", @@ -93,7 +93,7 @@ static uint64_t *spapr_tce_alloc_table(uint32_t liobn, if (!table) { *fd = -1; - table = g_malloc0(nb_table * sizeof(uint64_t)); + table = g_new0(uint64_t, nb_table); } trace_spapr_iommu_new_table(liobn, table, *fd); @@ -115,7 +115,7 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, IOMMUAccessFlags flag, int iommu_idx) { - sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); + SpaprTceTable *tcet = container_of(iommu, SpaprTceTable, iommu); uint64_t tce; IOMMUTLBEntry ret = { .target_as = &address_space_memory, @@ -141,9 +141,39 @@ static IOMMUTLBEntry spapr_tce_translate_iommu(IOMMUMemoryRegion *iommu, return ret; } +static void spapr_tce_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) +{ + MemoryRegion *mr = MEMORY_REGION(iommu_mr); + IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_GET_CLASS(iommu_mr); + hwaddr addr, granularity; + IOMMUTLBEntry iotlb; + SpaprTceTable *tcet = container_of(iommu_mr, SpaprTceTable, iommu); + + if (tcet->skipping_replay) { + return; + } + + granularity = memory_region_iommu_get_min_page_size(iommu_mr); + + for (addr = 0; addr < memory_region_size(mr); addr += granularity) { + iotlb = imrc->translate(iommu_mr, addr, IOMMU_NONE, n->iommu_idx); + 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; + } + } +} + static int spapr_tce_table_pre_save(void *opaque) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); + SpaprTceTable *tcet = SPAPR_TCE_TABLE(opaque); tcet->mig_table = tcet->table; tcet->mig_nb_table = tcet->nb_table; @@ -156,7 +186,7 @@ static int spapr_tce_table_pre_save(void *opaque) static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu) { - sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); + SpaprTceTable *tcet = container_of(iommu, SpaprTceTable, iommu); return 1ULL << tcet->page_shift; } @@ -164,7 +194,7 @@ static uint64_t spapr_tce_get_min_page_size(IOMMUMemoryRegion *iommu) static int spapr_tce_get_attr(IOMMUMemoryRegion *iommu, enum IOMMUMemoryRegionAttr attr, void *data) { - sPAPRTCETable *tcet = container_of(iommu, sPAPRTCETable, iommu); + SpaprTceTable *tcet = container_of(iommu, SpaprTceTable, iommu); if (attr == IOMMU_ATTR_SPAPR_TCE_FD && kvmppc_has_cap_spapr_vfio()) { *(int *) data = tcet->fd; @@ -178,7 +208,7 @@ static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu, IOMMUNotifierFlag old, IOMMUNotifierFlag new) { - struct sPAPRTCETable *tbl = container_of(iommu, sPAPRTCETable, iommu); + struct SpaprTceTable *tbl = container_of(iommu, SpaprTceTable, iommu); if (old == IOMMU_NOTIFIER_NONE && new != IOMMU_NOTIFIER_NONE) { spapr_tce_set_need_vfio(tbl, true); @@ -189,7 +219,7 @@ static void spapr_tce_notify_flag_changed(IOMMUMemoryRegion *iommu, static int spapr_tce_table_post_load(void *opaque, int version_id) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(opaque); + SpaprTceTable *tcet = SPAPR_TCE_TABLE(opaque); uint32_t old_nb_table = tcet->nb_table; uint64_t old_bus_offset = tcet->bus_offset; uint32_t old_page_shift = tcet->page_shift; @@ -223,7 +253,7 @@ static int spapr_tce_table_post_load(void *opaque, int version_id) static bool spapr_tce_table_ex_needed(void *opaque) { - sPAPRTCETable *tcet = opaque; + SpaprTceTable *tcet = opaque; return tcet->bus_offset || tcet->page_shift != 0xC; } @@ -234,8 +264,8 @@ static const VMStateDescription vmstate_spapr_tce_table_ex = { .minimum_version_id = 1, .needed = spapr_tce_table_ex_needed, .fields = (VMStateField[]) { - VMSTATE_UINT64(bus_offset, sPAPRTCETable), - VMSTATE_UINT32(page_shift, sPAPRTCETable), + VMSTATE_UINT64(bus_offset, SpaprTceTable), + VMSTATE_UINT32(page_shift, SpaprTceTable), VMSTATE_END_OF_LIST() }, }; @@ -248,12 +278,12 @@ static const VMStateDescription vmstate_spapr_tce_table = { .post_load = spapr_tce_table_post_load, .fields = (VMStateField []) { /* Sanity check */ - VMSTATE_UINT32_EQUAL(liobn, sPAPRTCETable, NULL), + VMSTATE_UINT32_EQUAL(liobn, SpaprTceTable, NULL), /* IOMMU state */ - VMSTATE_UINT32(mig_nb_table, sPAPRTCETable), - VMSTATE_BOOL(bypass, sPAPRTCETable), - VMSTATE_VARRAY_UINT32_ALLOC(mig_table, sPAPRTCETable, mig_nb_table, 0, + VMSTATE_UINT32(mig_nb_table, SpaprTceTable), + VMSTATE_BOOL(bypass, SpaprTceTable), + VMSTATE_VARRAY_UINT32_ALLOC(mig_table, SpaprTceTable, mig_nb_table, 0, vmstate_info_uint64, uint64_t), VMSTATE_END_OF_LIST() @@ -266,7 +296,7 @@ static const VMStateDescription vmstate_spapr_tce_table = { static void spapr_tce_table_realize(DeviceState *dev, Error **errp) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); + SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev); Object *tcetobj = OBJECT(tcet); gchar *tmp; @@ -288,7 +318,7 @@ static void spapr_tce_table_realize(DeviceState *dev, Error **errp) tcet); } -void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) +void spapr_tce_set_need_vfio(SpaprTceTable *tcet, bool need_vfio) { size_t table_size = tcet->nb_table * sizeof(uint64_t); uint64_t *oldtable; @@ -317,9 +347,9 @@ void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio) tcet->fd = newfd; } -sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn) +SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn) { - sPAPRTCETable *tcet; + SpaprTceTable *tcet; gchar *tmp; if (spapr_tce_find_by_liobn(liobn)) { @@ -341,7 +371,7 @@ sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn) return tcet; } -void spapr_tce_table_enable(sPAPRTCETable *tcet, +void spapr_tce_table_enable(SpaprTceTable *tcet, uint32_t page_shift, uint64_t bus_offset, uint32_t nb_table) { @@ -366,7 +396,7 @@ void spapr_tce_table_enable(sPAPRTCETable *tcet, MEMORY_REGION(&tcet->iommu)); } -void spapr_tce_table_disable(sPAPRTCETable *tcet) +void spapr_tce_table_disable(SpaprTceTable *tcet) { if (!tcet->nb_table) { return; @@ -385,7 +415,7 @@ void spapr_tce_table_disable(sPAPRTCETable *tcet) static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); + SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev); vmstate_unregister(DEVICE(tcet), &vmstate_spapr_tce_table, tcet); @@ -394,14 +424,14 @@ static void spapr_tce_table_unrealize(DeviceState *dev, Error **errp) spapr_tce_table_disable(tcet); } -MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet) +MemoryRegion *spapr_tce_get_iommu(SpaprTceTable *tcet) { return &tcet->root; } static void spapr_tce_reset(DeviceState *dev) { - sPAPRTCETable *tcet = SPAPR_TCE_TABLE(dev); + SpaprTceTable *tcet = SPAPR_TCE_TABLE(dev); size_t table_size = tcet->nb_table * sizeof(uint64_t); if (tcet->nb_table) { @@ -409,7 +439,7 @@ static void spapr_tce_reset(DeviceState *dev) } } -static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, +static target_ulong put_tce_emu(SpaprTceTable *tcet, target_ulong ioba, target_ulong tce) { IOMMUTLBEntry entry; @@ -435,7 +465,7 @@ static target_ulong put_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, } static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { int i; @@ -445,7 +475,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, target_ulong tce_list = args[2]; target_ulong npages = args[3]; target_ulong ret = H_PARAMETER, tce = 0; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn); CPUState *cs = CPU(cpu); hwaddr page_mask, page_size; @@ -480,7 +510,7 @@ static target_ulong h_put_tce_indirect(PowerPCCPU *cpu, return ret; } -static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_stuff_tce(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { int i; @@ -489,7 +519,7 @@ static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, target_ulong tce_value = args[2]; target_ulong npages = args[3]; target_ulong ret = H_PARAMETER; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn); hwaddr page_mask, page_size; if (!tcet) { @@ -519,14 +549,14 @@ static target_ulong h_stuff_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_put_tce(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong liobn = args[0]; target_ulong ioba = args[1]; target_ulong tce = args[2]; target_ulong ret = H_PARAMETER; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn); if (tcet) { hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); @@ -544,7 +574,7 @@ static target_ulong h_put_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, return ret; } -static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, +static target_ulong get_tce_emu(SpaprTceTable *tcet, target_ulong ioba, target_ulong *tce) { unsigned long index = (ioba - tcet->bus_offset) >> tcet->page_shift; @@ -560,14 +590,14 @@ static target_ulong get_tce_emu(sPAPRTCETable *tcet, target_ulong ioba, return H_SUCCESS; } -static target_ulong h_get_tce(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_get_tce(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong liobn = args[0]; target_ulong ioba = args[1]; target_ulong tce = 0; target_ulong ret = H_PARAMETER; - sPAPRTCETable *tcet = spapr_tce_find_by_liobn(liobn); + SpaprTceTable *tcet = spapr_tce_find_by_liobn(liobn); if (tcet) { hwaddr page_mask = IOMMU_PAGE_MASK(tcet->page_shift); @@ -619,7 +649,7 @@ int spapr_dma_dt(void *fdt, int node_off, const char *propname, } int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, - sPAPRTCETable *tcet) + SpaprTceTable *tcet) { if (!tcet) { return 0; @@ -650,7 +680,7 @@ static void spapr_tce_table_class_init(ObjectClass *klass, void *data) static TypeInfo spapr_tce_table_info = { .name = TYPE_SPAPR_TCE_TABLE, .parent = TYPE_DEVICE, - .instance_size = sizeof(sPAPRTCETable), + .instance_size = sizeof(SpaprTceTable), .class_init = spapr_tce_table_class_init, }; @@ -659,6 +689,7 @@ static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); imrc->translate = spapr_tce_translate_iommu; + imrc->replay = spapr_tce_replay; imrc->get_min_page_size = spapr_tce_get_min_page_size; imrc->notify_flag_changed = spapr_tce_notify_flag_changed; imrc->get_attr = spapr_tce_get_attr; diff --git a/hw/ppc/spapr_irq.c b/hw/ppc/spapr_irq.c new file mode 100644 index 0000000000..0a84e4cf63 --- /dev/null +++ b/hw/ppc/spapr_irq.c @@ -0,0 +1,784 @@ +/* + * QEMU PowerPC sPAPR IRQ interface + * + * Copyright (c) 2018, IBM Corporation. + * + * 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 "qemu/log.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/ppc/spapr.h" +#include "hw/ppc/spapr_cpu_core.h" +#include "hw/ppc/spapr_xive.h" +#include "hw/ppc/xics.h" +#include "hw/ppc/xics_spapr.h" +#include "cpu-models.h" +#include "sysemu/kvm.h" + +#include "trace.h" + +void spapr_irq_msi_init(SpaprMachineState *spapr, uint32_t nr_msis) +{ + spapr->irq_map_nr = nr_msis; + spapr->irq_map = bitmap_new(spapr->irq_map_nr); +} + +int spapr_irq_msi_alloc(SpaprMachineState *spapr, uint32_t num, bool align, + Error **errp) +{ + int irq; + + /* + * The 'align_mask' parameter of bitmap_find_next_zero_area() + * should be one less than a power of 2; 0 means no + * alignment. Adapt the 'align' value of the former allocator + * to fit the requirements of bitmap_find_next_zero_area() + */ + align -= 1; + + irq = bitmap_find_next_zero_area(spapr->irq_map, spapr->irq_map_nr, 0, num, + align); + if (irq == spapr->irq_map_nr) { + error_setg(errp, "can't find a free %d-IRQ block", num); + return -1; + } + + bitmap_set(spapr->irq_map, irq, num); + + return irq + SPAPR_IRQ_MSI; +} + +void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num) +{ + bitmap_clear(spapr->irq_map, irq - SPAPR_IRQ_MSI, num); +} + +void spapr_irq_msi_reset(SpaprMachineState *spapr) +{ + bitmap_clear(spapr->irq_map, 0, spapr->irq_map_nr); +} + + +/* + * XICS IRQ backend. + */ + +static ICSState *spapr_ics_create(SpaprMachineState *spapr, + int nr_irqs, Error **errp) +{ + Error *local_err = NULL; + Object *obj; + + obj = object_new(TYPE_ICS_SIMPLE); + object_property_add_child(OBJECT(spapr), "ics", obj, &error_abort); + object_property_add_const_link(obj, ICS_PROP_XICS, OBJECT(spapr), + &error_abort); + object_property_set_int(obj, nr_irqs, "nr-irqs", &local_err); + if (local_err) { + goto error; + } + object_property_set_bool(obj, true, "realized", &local_err); + if (local_err) { + goto error; + } + + return ICS_BASE(obj); + +error: + error_propagate(errp, local_err); + return NULL; +} + +static void spapr_irq_init_xics(SpaprMachineState *spapr, int nr_irqs, + Error **errp) +{ + MachineState *machine = MACHINE(spapr); + Error *local_err = NULL; + bool xics_kvm = false; + + if (kvm_enabled()) { + if (machine_kernel_irqchip_allowed(machine) && + !xics_kvm_init(spapr, &local_err)) { + xics_kvm = true; + } + if (machine_kernel_irqchip_required(machine) && !xics_kvm) { + error_prepend(&local_err, + "kernel_irqchip requested but unavailable: "); + goto error; + } + error_free(local_err); + local_err = NULL; + } + + if (!xics_kvm) { + xics_spapr_init(spapr); + } + + spapr->ics = spapr_ics_create(spapr, nr_irqs, &local_err); + +error: + error_propagate(errp, local_err); +} + +#define ICS_IRQ_FREE(ics, srcno) \ + (!((ics)->irqs[(srcno)].flags & (XICS_FLAGS_IRQ_MASK))) + +static int spapr_irq_claim_xics(SpaprMachineState *spapr, int irq, bool lsi, + Error **errp) +{ + ICSState *ics = spapr->ics; + + assert(ics); + + if (!ics_valid_irq(ics, irq)) { + error_setg(errp, "IRQ %d is invalid", irq); + return -1; + } + + if (!ICS_IRQ_FREE(ics, irq - ics->offset)) { + error_setg(errp, "IRQ %d is not free", irq); + return -1; + } + + ics_set_irq_type(ics, irq - ics->offset, lsi); + return 0; +} + +static void spapr_irq_free_xics(SpaprMachineState *spapr, int irq, int num) +{ + ICSState *ics = spapr->ics; + uint32_t srcno = irq - ics->offset; + int i; + + if (ics_valid_irq(ics, irq)) { + trace_spapr_irq_free(0, irq, num); + for (i = srcno; i < srcno + num; ++i) { + if (ICS_IRQ_FREE(ics, i)) { + trace_spapr_irq_free_warn(0, i); + } + memset(&ics->irqs[i], 0, sizeof(ICSIRQState)); + } + } +} + +static qemu_irq spapr_qirq_xics(SpaprMachineState *spapr, int irq) +{ + ICSState *ics = spapr->ics; + uint32_t srcno = irq - ics->offset; + + if (ics_valid_irq(ics, irq)) { + return spapr->qirqs[srcno]; + } + + return NULL; +} + +static void spapr_irq_print_info_xics(SpaprMachineState *spapr, Monitor *mon) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + icp_pic_print_info(spapr_cpu_state(cpu)->icp, mon); + } + + ics_pic_print_info(spapr->ics, mon); +} + +static void spapr_irq_cpu_intc_create_xics(SpaprMachineState *spapr, + PowerPCCPU *cpu, Error **errp) +{ + Error *local_err = NULL; + Object *obj; + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + + obj = icp_create(OBJECT(cpu), TYPE_ICP, XICS_FABRIC(spapr), + &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + spapr_cpu->icp = ICP(obj); +} + +static int spapr_irq_post_load_xics(SpaprMachineState *spapr, int version_id) +{ + if (!kvm_irqchip_in_kernel()) { + CPUState *cs; + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + icp_resend(spapr_cpu_state(cpu)->icp); + } + } + return 0; +} + +static void spapr_irq_set_irq_xics(void *opaque, int srcno, int val) +{ + SpaprMachineState *spapr = opaque; + + ics_simple_set_irq(spapr->ics, srcno, val); +} + +static void spapr_irq_reset_xics(SpaprMachineState *spapr, Error **errp) +{ + /* TODO: create the KVM XICS device */ +} + +static const char *spapr_irq_get_nodename_xics(SpaprMachineState *spapr) +{ + return XICS_NODENAME; +} + +#define SPAPR_IRQ_XICS_NR_IRQS 0x1000 +#define SPAPR_IRQ_XICS_NR_MSIS \ + (XICS_IRQ_BASE + SPAPR_IRQ_XICS_NR_IRQS - SPAPR_IRQ_MSI) + +SpaprIrq spapr_irq_xics = { + .nr_irqs = SPAPR_IRQ_XICS_NR_IRQS, + .nr_msis = SPAPR_IRQ_XICS_NR_MSIS, + .ov5 = SPAPR_OV5_XIVE_LEGACY, + + .init = spapr_irq_init_xics, + .claim = spapr_irq_claim_xics, + .free = spapr_irq_free_xics, + .qirq = spapr_qirq_xics, + .print_info = spapr_irq_print_info_xics, + .dt_populate = spapr_dt_xics, + .cpu_intc_create = spapr_irq_cpu_intc_create_xics, + .post_load = spapr_irq_post_load_xics, + .reset = spapr_irq_reset_xics, + .set_irq = spapr_irq_set_irq_xics, + .get_nodename = spapr_irq_get_nodename_xics, +}; + +/* + * XIVE IRQ backend. + */ +static void spapr_irq_init_xive(SpaprMachineState *spapr, int nr_irqs, + Error **errp) +{ + MachineState *machine = MACHINE(spapr); + uint32_t nr_servers = spapr_max_server_number(spapr); + DeviceState *dev; + int i; + + /* KVM XIVE device not yet available */ + if (kvm_enabled()) { + if (machine_kernel_irqchip_required(machine)) { + error_setg(errp, "kernel_irqchip requested. no KVM XIVE support"); + return; + } + } + + dev = qdev_create(NULL, TYPE_SPAPR_XIVE); + qdev_prop_set_uint32(dev, "nr-irqs", nr_irqs); + /* + * 8 XIVE END structures per CPU. One for each available priority + */ + qdev_prop_set_uint32(dev, "nr-ends", nr_servers << 3); + qdev_init_nofail(dev); + + spapr->xive = SPAPR_XIVE(dev); + + /* Enable the CPU IPIs */ + for (i = 0; i < nr_servers; ++i) { + spapr_xive_irq_claim(spapr->xive, SPAPR_IRQ_IPI + i, false); + } + + spapr_xive_hcall_init(spapr); +} + +static int spapr_irq_claim_xive(SpaprMachineState *spapr, int irq, bool lsi, + Error **errp) +{ + if (!spapr_xive_irq_claim(spapr->xive, irq, lsi)) { + error_setg(errp, "IRQ %d is invalid", irq); + return -1; + } + return 0; +} + +static void spapr_irq_free_xive(SpaprMachineState *spapr, int irq, int num) +{ + int i; + + for (i = irq; i < irq + num; ++i) { + spapr_xive_irq_free(spapr->xive, i); + } +} + +static qemu_irq spapr_qirq_xive(SpaprMachineState *spapr, int irq) +{ + SpaprXive *xive = spapr->xive; + + if (irq >= xive->nr_irqs) { + return NULL; + } + + /* The sPAPR machine/device should have claimed the IRQ before */ + assert(xive_eas_is_valid(&xive->eat[irq])); + + return spapr->qirqs[irq]; +} + +static void spapr_irq_print_info_xive(SpaprMachineState *spapr, + Monitor *mon) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + xive_tctx_pic_print_info(spapr_cpu_state(cpu)->tctx, mon); + } + + spapr_xive_pic_print_info(spapr->xive, mon); +} + +static void spapr_irq_cpu_intc_create_xive(SpaprMachineState *spapr, + PowerPCCPU *cpu, Error **errp) +{ + Error *local_err = NULL; + Object *obj; + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); + + obj = xive_tctx_create(OBJECT(cpu), XIVE_ROUTER(spapr->xive), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + spapr_cpu->tctx = XIVE_TCTX(obj); + + /* + * (TCG) Early setting the OS CAM line for hotplugged CPUs as they + * don't beneficiate from the reset of the XIVE IRQ backend + */ + spapr_xive_set_tctx_os_cam(spapr_cpu->tctx); +} + +static int spapr_irq_post_load_xive(SpaprMachineState *spapr, int version_id) +{ + return 0; +} + +static void spapr_irq_reset_xive(SpaprMachineState *spapr, Error **errp) +{ + CPUState *cs; + + CPU_FOREACH(cs) { + PowerPCCPU *cpu = POWERPC_CPU(cs); + + /* (TCG) Set the OS CAM line of the thread interrupt context. */ + spapr_xive_set_tctx_os_cam(spapr_cpu_state(cpu)->tctx); + } + + /* Activate the XIVE MMIOs */ + spapr_xive_mmio_set_enabled(spapr->xive, true); +} + +static void spapr_irq_set_irq_xive(void *opaque, int srcno, int val) +{ + SpaprMachineState *spapr = opaque; + + xive_source_set_irq(&spapr->xive->source, srcno, val); +} + +static const char *spapr_irq_get_nodename_xive(SpaprMachineState *spapr) +{ + return spapr->xive->nodename; +} + +/* + * XIVE uses the full IRQ number space. Set it to 8K to be compatible + * with XICS. + */ + +#define SPAPR_IRQ_XIVE_NR_IRQS 0x2000 +#define SPAPR_IRQ_XIVE_NR_MSIS (SPAPR_IRQ_XIVE_NR_IRQS - SPAPR_IRQ_MSI) + +SpaprIrq spapr_irq_xive = { + .nr_irqs = SPAPR_IRQ_XIVE_NR_IRQS, + .nr_msis = SPAPR_IRQ_XIVE_NR_MSIS, + .ov5 = SPAPR_OV5_XIVE_EXPLOIT, + + .init = spapr_irq_init_xive, + .claim = spapr_irq_claim_xive, + .free = spapr_irq_free_xive, + .qirq = spapr_qirq_xive, + .print_info = spapr_irq_print_info_xive, + .dt_populate = spapr_dt_xive, + .cpu_intc_create = spapr_irq_cpu_intc_create_xive, + .post_load = spapr_irq_post_load_xive, + .reset = spapr_irq_reset_xive, + .set_irq = spapr_irq_set_irq_xive, + .get_nodename = spapr_irq_get_nodename_xive, +}; + +/* + * Dual XIVE and XICS IRQ backend. + * + * Both interrupt mode, XIVE and XICS, objects are created but the + * machine starts in legacy interrupt mode (XICS). It can be changed + * by the CAS negotiation process and, in that case, the new mode is + * activated after an extra machine reset. + */ + +/* + * Returns the sPAPR IRQ backend negotiated by CAS. XICS is the + * default. + */ +static SpaprIrq *spapr_irq_current(SpaprMachineState *spapr) +{ + return spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT) ? + &spapr_irq_xive : &spapr_irq_xics; +} + +static void spapr_irq_init_dual(SpaprMachineState *spapr, int nr_irqs, + Error **errp) +{ + MachineState *machine = MACHINE(spapr); + Error *local_err = NULL; + + if (kvm_enabled() && machine_kernel_irqchip_allowed(machine)) { + error_setg(errp, "No KVM support for the 'dual' machine"); + return; + } + + spapr_irq_xics.init(spapr, spapr_irq_xics.nr_irqs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + spapr_irq_xive.init(spapr, spapr_irq_xive.nr_irqs, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } +} + +static int spapr_irq_claim_dual(SpaprMachineState *spapr, int irq, bool lsi, + Error **errp) +{ + Error *local_err = NULL; + int ret; + + ret = spapr_irq_xics.claim(spapr, irq, lsi, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return ret; + } + + ret = spapr_irq_xive.claim(spapr, irq, lsi, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return ret; + } + + return ret; +} + +static void spapr_irq_free_dual(SpaprMachineState *spapr, int irq, int num) +{ + spapr_irq_xics.free(spapr, irq, num); + spapr_irq_xive.free(spapr, irq, num); +} + +static qemu_irq spapr_qirq_dual(SpaprMachineState *spapr, int irq) +{ + return spapr_irq_current(spapr)->qirq(spapr, irq); +} + +static void spapr_irq_print_info_dual(SpaprMachineState *spapr, Monitor *mon) +{ + spapr_irq_current(spapr)->print_info(spapr, mon); +} + +static void spapr_irq_dt_populate_dual(SpaprMachineState *spapr, + uint32_t nr_servers, void *fdt, + uint32_t phandle) +{ + spapr_irq_current(spapr)->dt_populate(spapr, nr_servers, fdt, phandle); +} + +static void spapr_irq_cpu_intc_create_dual(SpaprMachineState *spapr, + PowerPCCPU *cpu, Error **errp) +{ + Error *local_err = NULL; + + spapr_irq_xive.cpu_intc_create(spapr, cpu, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + spapr_irq_xics.cpu_intc_create(spapr, cpu, errp); +} + +static int spapr_irq_post_load_dual(SpaprMachineState *spapr, int version_id) +{ + /* + * Force a reset of the XIVE backend after migration. The machine + * defaults to XICS at startup. + */ + if (spapr_ovec_test(spapr->ov5_cas, OV5_XIVE_EXPLOIT)) { + spapr_irq_xive.reset(spapr, &error_fatal); + } + + return spapr_irq_current(spapr)->post_load(spapr, version_id); +} + +static void spapr_irq_reset_dual(SpaprMachineState *spapr, Error **errp) +{ + /* + * Deactivate the XIVE MMIOs. The XIVE backend will reenable them + * if selected. + */ + spapr_xive_mmio_set_enabled(spapr->xive, false); + + spapr_irq_current(spapr)->reset(spapr, errp); +} + +static void spapr_irq_set_irq_dual(void *opaque, int srcno, int val) +{ + SpaprMachineState *spapr = opaque; + + spapr_irq_current(spapr)->set_irq(spapr, srcno, val); +} + +static const char *spapr_irq_get_nodename_dual(SpaprMachineState *spapr) +{ + return spapr_irq_current(spapr)->get_nodename(spapr); +} + +/* + * Define values in sync with the XIVE and XICS backend + */ +#define SPAPR_IRQ_DUAL_NR_IRQS 0x2000 +#define SPAPR_IRQ_DUAL_NR_MSIS (SPAPR_IRQ_DUAL_NR_IRQS - SPAPR_IRQ_MSI) + +SpaprIrq spapr_irq_dual = { + .nr_irqs = SPAPR_IRQ_DUAL_NR_IRQS, + .nr_msis = SPAPR_IRQ_DUAL_NR_MSIS, + .ov5 = SPAPR_OV5_XIVE_BOTH, + + .init = spapr_irq_init_dual, + .claim = spapr_irq_claim_dual, + .free = spapr_irq_free_dual, + .qirq = spapr_qirq_dual, + .print_info = spapr_irq_print_info_dual, + .dt_populate = spapr_irq_dt_populate_dual, + .cpu_intc_create = spapr_irq_cpu_intc_create_dual, + .post_load = spapr_irq_post_load_dual, + .reset = spapr_irq_reset_dual, + .set_irq = spapr_irq_set_irq_dual, + .get_nodename = spapr_irq_get_nodename_dual, +}; + + +static void spapr_irq_check(SpaprMachineState *spapr, Error **errp) +{ + MachineState *machine = MACHINE(spapr); + + /* + * Sanity checks on non-P9 machines. On these, XIVE is not + * advertised, see spapr_dt_ov5_platform_support() + */ + if (!ppc_type_check_compat(machine->cpu_type, CPU_POWERPC_LOGICAL_3_00, + 0, spapr->max_compat_pvr)) { + /* + * If the 'dual' interrupt mode is selected, force XICS as CAS + * negotiation is useless. + */ + if (spapr->irq == &spapr_irq_dual) { + spapr->irq = &spapr_irq_xics; + return; + } + + /* + * Non-P9 machines using only XIVE is a bogus setup. We have two + * scenarios to take into account because of the compat mode: + * + * 1. POWER7/8 machines should fail to init later on when creating + * the XIVE interrupt presenters because a POWER9 exception + * model is required. + + * 2. POWER9 machines using the POWER8 compat mode won't fail and + * will let the OS boot with a partial XIVE setup : DT + * properties but no hcalls. + * + * To cover both and not confuse the OS, add an early failure in + * QEMU. + */ + if (spapr->irq == &spapr_irq_xive) { + error_setg(errp, "XIVE-only machines require a POWER9 CPU"); + return; + } + } +} + +/* + * sPAPR IRQ frontend routines for devices + */ +void spapr_irq_init(SpaprMachineState *spapr, Error **errp) +{ + MachineState *machine = MACHINE(spapr); + Error *local_err = NULL; + + if (machine_kernel_irqchip_split(machine)) { + error_setg(errp, "kernel_irqchip split mode not supported on pseries"); + return; + } + + if (!kvm_enabled() && machine_kernel_irqchip_required(machine)) { + error_setg(errp, + "kernel_irqchip requested but only available with KVM"); + return; + } + + spapr_irq_check(spapr, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* Initialize the MSI IRQ allocator. */ + if (!SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { + spapr_irq_msi_init(spapr, spapr->irq->nr_msis); + } + + spapr->irq->init(spapr, spapr->irq->nr_irqs, errp); + + spapr->qirqs = qemu_allocate_irqs(spapr->irq->set_irq, spapr, + spapr->irq->nr_irqs); +} + +int spapr_irq_claim(SpaprMachineState *spapr, int irq, bool lsi, Error **errp) +{ + return spapr->irq->claim(spapr, irq, lsi, errp); +} + +void spapr_irq_free(SpaprMachineState *spapr, int irq, int num) +{ + spapr->irq->free(spapr, irq, num); +} + +qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq) +{ + return spapr->irq->qirq(spapr, irq); +} + +int spapr_irq_post_load(SpaprMachineState *spapr, int version_id) +{ + return spapr->irq->post_load(spapr, version_id); +} + +void spapr_irq_reset(SpaprMachineState *spapr, Error **errp) +{ + if (spapr->irq->reset) { + spapr->irq->reset(spapr, errp); + } +} + +int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp) +{ + const char *nodename = spapr->irq->get_nodename(spapr); + int offset, phandle; + + offset = fdt_subnode_offset(fdt, 0, nodename); + if (offset < 0) { + error_setg(errp, "Can't find node \"%s\": %s", nodename, + fdt_strerror(offset)); + return -1; + } + + phandle = fdt_get_phandle(fdt, offset); + if (!phandle) { + error_setg(errp, "Can't get phandle of node \"%s\"", nodename); + return -1; + } + + return phandle; +} + +/* + * XICS legacy routines - to deprecate one day + */ + +static int ics_find_free_block(ICSState *ics, int num, int alignnum) +{ + int first, i; + + for (first = 0; first < ics->nr_irqs; first += alignnum) { + if (num > (ics->nr_irqs - first)) { + return -1; + } + for (i = first; i < first + num; ++i) { + if (!ICS_IRQ_FREE(ics, i)) { + break; + } + } + if (i == (first + num)) { + return first; + } + } + + return -1; +} + +int spapr_irq_find(SpaprMachineState *spapr, int num, bool align, Error **errp) +{ + ICSState *ics = spapr->ics; + int first = -1; + + assert(ics); + + /* + * MSIMesage::data is used for storing VIRQ so + * it has to be aligned to num to support multiple + * MSI vectors. MSI-X is not affected by this. + * The hint is used for the first IRQ, the rest should + * be allocated continuously. + */ + if (align) { + assert((num == 1) || (num == 2) || (num == 4) || + (num == 8) || (num == 16) || (num == 32)); + first = ics_find_free_block(ics, num, num); + } 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; + } + + return first + ics->offset; +} + +#define SPAPR_IRQ_XICS_LEGACY_NR_IRQS 0x400 + +SpaprIrq spapr_irq_xics_legacy = { + .nr_irqs = SPAPR_IRQ_XICS_LEGACY_NR_IRQS, + .nr_msis = SPAPR_IRQ_XICS_LEGACY_NR_IRQS, + .ov5 = SPAPR_OV5_XIVE_LEGACY, + + .init = spapr_irq_init_xics, + .claim = spapr_irq_claim_xics, + .free = spapr_irq_free_xics, + .qirq = spapr_qirq_xics, + .print_info = spapr_irq_print_info_xics, + .dt_populate = spapr_dt_xics, + .cpu_intc_create = spapr_irq_cpu_intc_create_xics, + .post_load = spapr_irq_post_load_xics, + .set_irq = spapr_irq_set_irq_xics, + .get_nodename = spapr_irq_get_nodename_xics, +}; diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 318bf33de4..a65b7c7da9 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -26,7 +26,7 @@ * allows us to more safely make assumptions about the bitmap size and * simplify the calling code somewhat */ -struct sPAPROptionVector { +struct SpaprOptionVector { unsigned long *bitmap; int32_t bitmap_size; /* only used for migration */ }; @@ -36,25 +36,25 @@ const VMStateDescription vmstate_spapr_ovec = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_BITMAP(bitmap, sPAPROptionVector, 1, bitmap_size), + VMSTATE_BITMAP(bitmap, SpaprOptionVector, 1, bitmap_size), VMSTATE_END_OF_LIST() } }; -sPAPROptionVector *spapr_ovec_new(void) +SpaprOptionVector *spapr_ovec_new(void) { - sPAPROptionVector *ov; + SpaprOptionVector *ov; - ov = g_new0(sPAPROptionVector, 1); + ov = g_new0(SpaprOptionVector, 1); ov->bitmap = bitmap_new(OV_MAXBITS); ov->bitmap_size = OV_MAXBITS; return ov; } -sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig) +SpaprOptionVector *spapr_ovec_clone(SpaprOptionVector *ov_orig) { - sPAPROptionVector *ov; + SpaprOptionVector *ov; g_assert(ov_orig); @@ -64,9 +64,9 @@ sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig) return ov; } -void spapr_ovec_intersect(sPAPROptionVector *ov, - sPAPROptionVector *ov1, - sPAPROptionVector *ov2) +void spapr_ovec_intersect(SpaprOptionVector *ov, + SpaprOptionVector *ov1, + SpaprOptionVector *ov2) { g_assert(ov); g_assert(ov1); @@ -76,9 +76,9 @@ void spapr_ovec_intersect(sPAPROptionVector *ov, } /* returns true if options bits were removed, false otherwise */ -bool spapr_ovec_diff(sPAPROptionVector *ov, - sPAPROptionVector *ov_old, - sPAPROptionVector *ov_new) +bool spapr_ovec_diff(SpaprOptionVector *ov, + SpaprOptionVector *ov_old, + SpaprOptionVector *ov_new) { unsigned long *change_mask = bitmap_new(OV_MAXBITS); unsigned long *removed_bits = bitmap_new(OV_MAXBITS); @@ -102,7 +102,7 @@ bool spapr_ovec_diff(sPAPROptionVector *ov, return bits_were_removed; } -void spapr_ovec_cleanup(sPAPROptionVector *ov) +void spapr_ovec_cleanup(SpaprOptionVector *ov) { if (ov) { g_free(ov->bitmap); @@ -110,7 +110,7 @@ void spapr_ovec_cleanup(sPAPROptionVector *ov) } } -void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) +void spapr_ovec_set(SpaprOptionVector *ov, long bitnr) { g_assert(ov); g_assert(bitnr < OV_MAXBITS); @@ -118,7 +118,7 @@ void spapr_ovec_set(sPAPROptionVector *ov, long bitnr) set_bit(bitnr, ov->bitmap); } -void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) +void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr) { g_assert(ov); g_assert(bitnr < OV_MAXBITS); @@ -126,7 +126,7 @@ void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr) clear_bit(bitnr, ov->bitmap); } -bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr) +bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr) { g_assert(ov); g_assert(bitnr < OV_MAXBITS); @@ -178,9 +178,9 @@ static target_ulong vector_addr(target_ulong table_addr, int vector) return table_addr; } -sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) +SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) { - sPAPROptionVector *ov; + SpaprOptionVector *ov; target_ulong addr; uint16_t vector_len; int i; @@ -210,7 +210,7 @@ sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector) } int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - sPAPROptionVector *ov, const char *name) + SpaprOptionVector *ov, const char *name) { uint8_t vec[OV_MAXBYTES + 1]; uint16_t vec_len; diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 497b896c7d..f62e6833b8 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -61,9 +61,9 @@ #define RTAS_TYPE_MSI 1 #define RTAS_TYPE_MSIX 2 -sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid) +SpaprPhbState *spapr_pci_find_phb(SpaprMachineState *spapr, uint64_t buid) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; QLIST_FOREACH(sphb, &spapr->phbs, list) { if (sphb->buid != buid) { @@ -75,10 +75,10 @@ sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid) return NULL; } -PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid, +PCIDevice *spapr_pci_find_dev(SpaprMachineState *spapr, uint64_t buid, uint32_t config_addr) { - sPAPRPHBState *sphb = spapr_pci_find_phb(spapr, buid); + SpaprPhbState *sphb = spapr_pci_find_phb(spapr, buid); PCIHostState *phb = PCI_HOST_BRIDGE(sphb); int bus_num = (config_addr >> 16) & 0xFF; int devfn = (config_addr >> 8) & 0xFF; @@ -96,7 +96,7 @@ static uint32_t rtas_pci_cfgaddr(uint32_t arg) return ((arg >> 20) & 0xf00) | (arg & 0xff); } -static void finish_read_pci_config(sPAPRMachineState *spapr, uint64_t buid, +static void finish_read_pci_config(SpaprMachineState *spapr, uint64_t buid, uint32_t addr, uint32_t size, target_ulong rets) { @@ -126,7 +126,7 @@ static void finish_read_pci_config(sPAPRMachineState *spapr, uint64_t buid, rtas_st(rets, 1, val); } -static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -146,7 +146,7 @@ static void rtas_ibm_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, finish_read_pci_config(spapr, buid, addr, size, rets); } -static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_read_pci_config(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -164,7 +164,7 @@ static void rtas_read_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, finish_read_pci_config(spapr, 0, addr, size, rets); } -static void finish_write_pci_config(sPAPRMachineState *spapr, uint64_t buid, +static void finish_write_pci_config(SpaprMachineState *spapr, uint64_t buid, uint32_t addr, uint32_t size, uint32_t val, target_ulong rets) { @@ -192,7 +192,7 @@ static void finish_write_pci_config(sPAPRMachineState *spapr, uint64_t buid, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -213,7 +213,7 @@ static void rtas_ibm_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, finish_write_pci_config(spapr, buid, addr, size, val, rets); } -static void rtas_write_pci_config(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_write_pci_config(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -262,11 +262,12 @@ static void spapr_msi_setmsg(PCIDevice *pdev, hwaddr addr, bool msix, } } -static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_ibm_change_msi(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { + SpaprMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); uint32_t config_addr = rtas_ld(args, 0); uint64_t buid = rtas_ldq(args, 1); unsigned int func = rtas_ld(args, 3); @@ -274,14 +275,14 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, unsigned int seq_num = rtas_ld(args, 5); unsigned int ret_intr_type; unsigned int irq, max_irqs = 0; - sPAPRPHBState *phb = NULL; + SpaprPhbState *phb = NULL; PCIDevice *pdev = NULL; spapr_pci_msi *msi; int *config_addr_key; Error *err = NULL; int i; - /* Fins sPAPRPHBState */ + /* Fins SpaprPhbState */ phb = spapr_pci_find_phb(spapr, buid); if (phb) { pdev = spapr_pci_find_dev(spapr, buid, config_addr); @@ -334,6 +335,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, return; } + if (!smc->legacy_irq_allocation) { + spapr_irq_msi_free(spapr, msi->first_irq, msi->num); + } spapr_irq_free(spapr, msi->first_irq, msi->num); if (msi_present(pdev)) { spapr_msi_setmsg(pdev, 0, false, 0, 0); @@ -372,7 +376,13 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, } /* Allocate MSIs */ - irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, &err); + if (smc->legacy_irq_allocation) { + irq = spapr_irq_find(spapr, req_num, ret_intr_type == RTAS_TYPE_MSI, + &err); + } else { + irq = spapr_irq_msi_alloc(spapr, req_num, + ret_intr_type == RTAS_TYPE_MSI, &err); + } if (err) { error_reportf_err(err, "Can't allocate MSIs for device %x: ", config_addr); @@ -383,6 +393,12 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, for (i = 0; i < req_num; i++) { spapr_irq_claim(spapr, irq + i, false, &err); if (err) { + if (i) { + spapr_irq_free(spapr, irq, i); + } + if (!smc->legacy_irq_allocation) { + spapr_irq_msi_free(spapr, irq, req_num); + } error_reportf_err(err, "Can't allocate MSIs for device %x: ", config_addr); rtas_st(rets, 0, RTAS_OUT_HW_ERROR); @@ -392,6 +408,9 @@ static void rtas_ibm_change_msi(PowerPCCPU *cpu, sPAPRMachineState *spapr, /* Release previous MSIs */ if (msi) { + if (!smc->legacy_irq_allocation) { + spapr_irq_msi_free(spapr, msi->first_irq, msi->num); + } spapr_irq_free(spapr, msi->first_irq, msi->num); g_hash_table_remove(phb->msi, &config_addr); } @@ -420,7 +439,7 @@ out: } static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, @@ -430,11 +449,11 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, uint32_t config_addr = rtas_ld(args, 0); uint64_t buid = rtas_ldq(args, 1); unsigned int intr_src_num = -1, ioa_intr_num = rtas_ld(args, 3); - sPAPRPHBState *phb = NULL; + SpaprPhbState *phb = NULL; PCIDevice *pdev = NULL; spapr_pci_msi *msi; - /* Find sPAPRPHBState */ + /* Find SpaprPhbState */ phb = spapr_pci_find_phb(spapr, buid); if (phb) { pdev = spapr_pci_find_dev(spapr, buid, config_addr); @@ -461,12 +480,12 @@ static void rtas_ibm_query_interrupt_source_number(PowerPCCPU *cpu, } static void rtas_ibm_set_eeh_option(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; uint32_t addr, option; uint64_t buid; int ret; @@ -497,12 +516,12 @@ param_error_exit: } static void rtas_ibm_get_config_addr_info2(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; PCIDevice *pdev; uint32_t addr, option; uint64_t buid; @@ -551,12 +570,12 @@ param_error_exit: } static void rtas_ibm_read_slot_reset_state2(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; uint64_t buid; int state, ret; @@ -593,12 +612,12 @@ param_error_exit: } static void rtas_ibm_set_slot_reset(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; uint32_t option; uint64_t buid; int ret; @@ -627,12 +646,12 @@ param_error_exit: } static void rtas_ibm_configure_pe(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; uint64_t buid; int ret; @@ -660,12 +679,12 @@ param_error_exit: /* To support it later */ static void rtas_ibm_slot_error_detail(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; int option; uint64_t buid; @@ -722,7 +741,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) * Here we use the number returned by pci_spapr_map_irq to find a * corresponding qemu_irq. */ - sPAPRPHBState *phb = opaque; + SpaprPhbState *phb = opaque; trace_spapr_pci_lsi_set(phb->dtbusname, irq_num, phb->lsi_table[irq_num].irq); qemu_set_irq(spapr_phb_lsi_qirq(phb, irq_num), level); @@ -730,7 +749,7 @@ static void pci_spapr_set_irq(void *opaque, int irq_num, int level) static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin) { - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(opaque); PCIINTxRoute route; route.mode = PCI_INTX_ENABLED; @@ -747,7 +766,7 @@ static PCIINTxRoute spapr_route_intx_pin_to_irq(void *opaque, int pin) static void spapr_msi_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); uint32_t irq = data; trace_spapr_pci_msi_write(addr, data, irq); @@ -767,12 +786,12 @@ static const MemoryRegionOps spapr_msi_ops = { */ static AddressSpace *spapr_pci_dma_iommu(PCIBus *bus, void *opaque, int devfn) { - sPAPRPHBState *phb = opaque; + SpaprPhbState *phb = opaque; return &phb->iommu_as; } -static char *spapr_phb_vfio_get_loc_code(sPAPRPHBState *sphb, PCIDevice *pdev) +static char *spapr_phb_vfio_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) { char *path = NULL, *buf = NULL, *host = NULL; @@ -803,7 +822,7 @@ err_out: return NULL; } -static char *spapr_phb_get_loc_code(sPAPRPHBState *sphb, PCIDevice *pdev) +static char *spapr_phb_get_loc_code(SpaprPhbState *sphb, PCIDevice *pdev) { char *buf; const char *devtype = "qemu"; @@ -951,7 +970,7 @@ static void populate_resource_props(PCIDevice *d, ResourceProps *rp) } assigned = &rp->assigned[assigned_idx++]; - assigned->phys_hi = cpu_to_be32(reg->phys_hi | b_n(1)); + assigned->phys_hi = cpu_to_be32(be32_to_cpu(reg->phys_hi) | b_n(1)); assigned->phys_mid = cpu_to_be32(d->io_regions[i].addr >> 32); assigned->phys_lo = cpu_to_be32(d->io_regions[i].addr); assigned->size_hi = reg->size_hi; @@ -1230,11 +1249,11 @@ static gchar *pci_get_node_name(PCIDevice *dev) } } -static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, +static uint32_t spapr_phb_get_pci_drc_index(SpaprPhbState *phb, PCIDevice *pdev); static void spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, - sPAPRPHBState *sphb) + SpaprPhbState *sphb) { ResourceProps rp; bool is_bridge = false; @@ -1339,7 +1358,7 @@ static void spapr_populate_pci_child_dt(PCIDevice *dev, void *fdt, int offset, } /* create OF node for pci device and required OF DT properties */ -static int spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev, +static int spapr_create_pci_child_dt(SpaprPhbState *phb, PCIDevice *dev, void *fdt, int node_offset) { int offset; @@ -1357,21 +1376,13 @@ static int spapr_create_pci_child_dt(sPAPRPHBState *phb, PCIDevice *dev, /* Callback to be called during DRC release. */ void spapr_phb_remove_pci_device_cb(DeviceState *dev) { - /* some version guests do not wait for completion of a device - * cleanup (generally done asynchronously by the kernel) before - * signaling to QEMU that the device is safe, but instead sleep - * for some 'safe' period of time. unfortunately on a busy host - * this sleep isn't guaranteed to be long enough, resulting in - * bad things like IRQ lines being left asserted during final - * device removal. to deal with this we call reset just prior - * to finalizing the device, which will put the device back into - * an 'idle' state, as the device cleanup code expects. - */ - pci_device_reset(PCI_DEVICE(dev)); + HotplugHandler *hotplug_ctrl = qdev_get_hotplug_handler(dev); + + hotplug_handler_unplug(hotplug_ctrl, dev, &error_abort); object_unparent(OBJECT(dev)); } -static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, +static SpaprDrc *spapr_phb_get_pci_func_drc(SpaprPhbState *phb, uint32_t busnr, int32_t devfn) { @@ -1379,17 +1390,17 @@ static sPAPRDRConnector *spapr_phb_get_pci_func_drc(sPAPRPHBState *phb, (phb->index << 16) | (busnr << 8) | devfn); } -static sPAPRDRConnector *spapr_phb_get_pci_drc(sPAPRPHBState *phb, +static SpaprDrc *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, +static uint32_t spapr_phb_get_pci_drc_index(SpaprPhbState *phb, PCIDevice *pdev) { - sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); + SpaprDrc *drc = spapr_phb_get_pci_drc(phb, pdev); if (!drc) { return 0; @@ -1398,17 +1409,26 @@ static uint32_t spapr_phb_get_pci_drc_index(sPAPRPHBState *phb, return spapr_drc_index(drc); } +int spapr_pci_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp) +{ + HotplugHandler *plug_handler = qdev_get_hotplug_handler(drc->dev); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(plug_handler); + PCIDevice *pdev = PCI_DEVICE(drc->dev); + + *fdt_start_offset = spapr_create_pci_child_dt(sphb, pdev, fdt, 0); + return 0; +} + static void spapr_pci_plug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); + SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); PCIDevice *pdev = PCI_DEVICE(plugged_dev); - sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); + SpaprDrc *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); - void *fdt = NULL; - int fdt_start_offset, fdt_size; /* if DR is disabled we don't need to do anything in the case of * hotplug or coldplug callbacks @@ -1438,10 +1458,7 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, goto out; } - fdt = create_device_tree(&fdt_size); - fdt_start_offset = spapr_create_pci_child_dt(phb, pdev, fdt, 0); - - spapr_drc_attach(drc, DEVICE(pdev), fdt, fdt_start_offset, &local_err); + spapr_drc_attach(drc, DEVICE(pdev), &local_err); if (local_err) { goto out; } @@ -1455,9 +1472,9 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, int i; for (i = 0; i < 8; i++) { - sPAPRDRConnector *func_drc; - sPAPRDRConnectorClass *func_drck; - sPAPRDREntitySense state; + SpaprDrc *func_drc; + SpaprDrcClass *func_drck; + SpaprDREntitySense state; func_drc = spapr_phb_get_pci_func_drc(phb, pci_bus_num(bus), PCI_DEVFN(slotnr, i)); @@ -1471,18 +1488,32 @@ static void spapr_pci_plug(HotplugHandler *plug_handler, } out: - if (local_err) { - error_propagate(errp, local_err); - g_free(fdt); - } + error_propagate(errp, local_err); +} + +static void spapr_pci_unplug(HotplugHandler *plug_handler, + DeviceState *plugged_dev, Error **errp) +{ + /* some version guests do not wait for completion of a device + * cleanup (generally done asynchronously by the kernel) before + * signaling to QEMU that the device is safe, but instead sleep + * for some 'safe' period of time. unfortunately on a busy host + * this sleep isn't guaranteed to be long enough, resulting in + * bad things like IRQ lines being left asserted during final + * device removal. to deal with this we call reset just prior + * to finalizing the device, which will put the device back into + * an 'idle' state, as the device cleanup code expects. + */ + pci_device_reset(PCI_DEVICE(plugged_dev)); + object_property_set_bool(OBJECT(plugged_dev), false, "realized", NULL); } static void spapr_pci_unplug_request(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp) { - sPAPRPHBState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); + SpaprPhbState *phb = SPAPR_PCI_HOST_BRIDGE(DEVICE(plug_handler)); PCIDevice *pdev = PCI_DEVICE(plugged_dev); - sPAPRDRConnector *drc = spapr_phb_get_pci_drc(phb, pdev); + SpaprDrc *drc = spapr_phb_get_pci_drc(phb, pdev); if (!phb->dr_enabled) { error_setg(errp, QERR_BUS_NO_HOTPLUG, @@ -1496,9 +1527,9 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, if (!spapr_drc_unplug_requested(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; + SpaprDrc *func_drc; + SpaprDrcClass *func_drck; + SpaprDREntitySense state; int i; /* ensure any other present functions are pending unplug */ @@ -1538,46 +1569,122 @@ static void spapr_pci_unplug_request(HotplugHandler *plug_handler, } } +static void spapr_phb_finalizefn(Object *obj) +{ + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(obj); + + g_free(sphb->dtbusname); + sphb->dtbusname = NULL; +} + +static void spapr_phb_unrealize(DeviceState *dev, Error **errp) +{ + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SysBusDevice *s = SYS_BUS_DEVICE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(s); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(phb); + SpaprTceTable *tcet; + int i; + const unsigned windows_supported = spapr_phb_windows_supported(sphb); + + if (sphb->msi) { + g_hash_table_unref(sphb->msi); + sphb->msi = NULL; + } + + /* + * Remove IO/MMIO subregions and aliases, rest should get cleaned + * via PHB's unrealize->object_finalize + */ + for (i = windows_supported - 1; i >= 0; i--) { + tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]); + if (tcet) { + memory_region_del_subregion(&sphb->iommu_root, + spapr_tce_get_iommu(tcet)); + } + } + + if (sphb->dr_enabled) { + for (i = PCI_SLOT_MAX * 8 - 1; i >= 0; i--) { + SpaprDrc *drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PCI, + (sphb->index << 16) | i); + + if (drc) { + object_unparent(OBJECT(drc)); + } + } + } + + for (i = PCI_NUM_PINS - 1; i >= 0; i--) { + if (sphb->lsi_table[i].irq) { + spapr_irq_free(spapr, sphb->lsi_table[i].irq, 1); + sphb->lsi_table[i].irq = 0; + } + } + + QLIST_REMOVE(sphb, list); + + memory_region_del_subregion(&sphb->iommu_root, &sphb->msiwindow); + + address_space_destroy(&sphb->iommu_as); + + qbus_set_hotplug_handler(BUS(phb->bus), NULL, &error_abort); + pci_unregister_root_bus(phb->bus); + + memory_region_del_subregion(get_system_memory(), &sphb->iowindow); + if (sphb->mem64_win_pciaddr != (hwaddr)-1) { + memory_region_del_subregion(get_system_memory(), &sphb->mem64window); + } + memory_region_del_subregion(get_system_memory(), &sphb->mem32window); +} + +static bool spapr_phb_allows_extended_config_space(PCIBus *bus) +{ + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(BUS(bus)->parent); + + return sphb->pcie_ecs; +} + +static void spapr_phb_root_bus_class_init(ObjectClass *klass, void *data) +{ + PCIBusClass *pbc = PCI_BUS_CLASS(klass); + + pbc->allows_extended_config_space = spapr_phb_allows_extended_config_space; +} + +#define TYPE_SPAPR_PHB_ROOT_BUS "pci" + +static const TypeInfo spapr_phb_root_bus_info = { + .name = TYPE_SPAPR_PHB_ROOT_BUS, + .parent = TYPE_PCI_BUS, + .class_init = spapr_phb_root_bus_class_init, +}; + static void spapr_phb_realize(DeviceState *dev, Error **errp) { /* We don't use SPAPR_MACHINE() in order to exit gracefully if the user * tries to add a sPAPR PHB to a non-pseries machine. */ - sPAPRMachineState *spapr = - (sPAPRMachineState *) object_dynamic_cast(qdev_get_machine(), + SpaprMachineState *spapr = + (SpaprMachineState *) object_dynamic_cast(qdev_get_machine(), TYPE_SPAPR_MACHINE); + SpaprMachineClass *smc = spapr ? SPAPR_MACHINE_GET_CLASS(spapr) : NULL; SysBusDevice *s = SYS_BUS_DEVICE(dev); - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(s); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(s); PCIHostState *phb = PCI_HOST_BRIDGE(s); char *namebuf; int i; PCIBus *bus; uint64_t msi_window_size = 4096; - sPAPRTCETable *tcet; - const unsigned windows_supported = - sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1; + SpaprTceTable *tcet; + const unsigned windows_supported = spapr_phb_windows_supported(sphb); if (!spapr) { error_setg(errp, TYPE_SPAPR_PCI_HOST_BRIDGE " needs a pseries machine"); return; } - if (sphb->index != (uint32_t)-1) { - sPAPRMachineClass *smc = SPAPR_MACHINE_GET_CLASS(spapr); - Error *local_err = NULL; - - smc->phb_placement(spapr, sphb->index, - &sphb->buid, &sphb->io_win_addr, - &sphb->mem_win_addr, &sphb->mem64_win_addr, - windows_supported, sphb->dma_liobn, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - } else { - error_setg(errp, "\"index\" for PAPR PHB is mandatory"); - return; - } + assert(sphb->index != (uint32_t)-1); /* checked in spapr_phb_pre_plug() */ if (sphb->mem64_win_size != 0) { if (sphb->mem_win_size > SPAPR_PCI_MEM32_WIN_SIZE) { @@ -1657,9 +1764,10 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) bus = pci_register_root_bus(dev, NULL, pci_spapr_set_irq, pci_spapr_map_irq, sphb, &sphb->memspace, &sphb->iospace, - PCI_DEVFN(0, 0), PCI_NUM_PINS, TYPE_PCI_BUS); + PCI_DEVFN(0, 0), PCI_NUM_PINS, + TYPE_SPAPR_PHB_ROOT_BUS); phb->bus = bus; - qbus_set_hotplug_handler(BUS(phb->bus), DEVICE(sphb), NULL); + qbus_set_hotplug_handler(BUS(phb->bus), OBJECT(sphb), NULL); /* * Initialize PHB address space. @@ -1705,21 +1813,26 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) /* Initialize the LSI table */ for (i = 0; i < PCI_NUM_PINS; i++) { - uint32_t irq; + uint32_t irq = SPAPR_IRQ_PCI_LSI + sphb->index * PCI_NUM_PINS + i; Error *local_err = NULL; - irq = spapr_irq_findone(spapr, &local_err); - if (local_err) { - error_propagate(errp, local_err); - error_prepend(errp, "can't allocate LSIs: "); - return; + if (smc->legacy_irq_allocation) { + irq = spapr_irq_findone(spapr, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "can't allocate LSIs: "); + /* + * Older machines will never support PHB hotplug, ie, this is an + * init only path and QEMU will terminate. No need to rollback. + */ + return; + } } spapr_irq_claim(spapr, irq, true, &local_err); if (local_err) { - error_propagate(errp, local_err); - error_prepend(errp, "can't allocate LSIs: "); - return; + error_propagate_prepend(errp, local_err, "can't allocate LSIs: "); + goto unrealize; } sphb->lsi_table[i].irq = irq; @@ -1739,13 +1852,17 @@ static void spapr_phb_realize(DeviceState *dev, Error **errp) if (!tcet) { error_setg(errp, "Creating window#%d failed for %s", i, sphb->dtbusname); - return; + goto unrealize; } memory_region_add_subregion(&sphb->iommu_root, 0, spapr_tce_get_iommu(tcet)); } sphb->msi = g_hash_table_new_full(g_int_hash, g_int_equal, g_free, g_free); + return; + +unrealize: + spapr_phb_unrealize(dev, NULL); } static int spapr_phb_children_reset(Object *child, void *opaque) @@ -1759,10 +1876,10 @@ static int spapr_phb_children_reset(Object *child, void *opaque) return 0; } -void spapr_phb_dma_reset(sPAPRPHBState *sphb) +void spapr_phb_dma_reset(SpaprPhbState *sphb) { int i; - sPAPRTCETable *tcet; + SpaprTceTable *tcet; for (i = 0; i < SPAPR_PCI_DMA_MAX_WINDOWS; ++i) { tcet = spapr_tce_find_by_liobn(sphb->dma_liobn[i]); @@ -1780,7 +1897,7 @@ void spapr_phb_dma_reset(sPAPRPHBState *sphb) static void spapr_phb_reset(DeviceState *qdev) { - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(qdev); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(qdev); spapr_phb_dma_reset(sphb); @@ -1793,27 +1910,27 @@ static void spapr_phb_reset(DeviceState *qdev) } static Property spapr_phb_properties[] = { - DEFINE_PROP_UINT32("index", sPAPRPHBState, index, -1), - DEFINE_PROP_UINT64("mem_win_size", sPAPRPHBState, mem_win_size, + DEFINE_PROP_UINT32("index", SpaprPhbState, index, -1), + DEFINE_PROP_UINT64("mem_win_size", SpaprPhbState, mem_win_size, SPAPR_PCI_MEM32_WIN_SIZE), - DEFINE_PROP_UINT64("mem64_win_size", sPAPRPHBState, mem64_win_size, + DEFINE_PROP_UINT64("mem64_win_size", SpaprPhbState, mem64_win_size, SPAPR_PCI_MEM64_WIN_SIZE), - DEFINE_PROP_UINT64("io_win_size", sPAPRPHBState, io_win_size, + DEFINE_PROP_UINT64("io_win_size", SpaprPhbState, io_win_size, SPAPR_PCI_IO_WIN_SIZE), - DEFINE_PROP_BOOL("dynamic-reconfiguration", sPAPRPHBState, dr_enabled, + 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_UINT64("dma64_win_addr", sPAPRPHBState, dma64_win_addr, + DEFINE_PROP_UINT64("dma_win_addr", SpaprPhbState, dma_win_addr, 0), + DEFINE_PROP_UINT64("dma_win_size", SpaprPhbState, dma_win_size, 0x40000000), + DEFINE_PROP_UINT64("dma64_win_addr", SpaprPhbState, dma64_win_addr, 0x800000000000000ULL), - DEFINE_PROP_BOOL("ddw", sPAPRPHBState, ddw_enabled, true), - DEFINE_PROP_UINT64("pgsz", sPAPRPHBState, page_size_mask, + DEFINE_PROP_BOOL("ddw", SpaprPhbState, ddw_enabled, true), + DEFINE_PROP_UINT64("pgsz", SpaprPhbState, page_size_mask, (1ULL << 12) | (1ULL << 16)), - DEFINE_PROP_UINT32("numa_node", sPAPRPHBState, numa_node, -1), - DEFINE_PROP_BOOL("pre-2.8-migration", sPAPRPHBState, + DEFINE_PROP_UINT32("numa_node", SpaprPhbState, numa_node, -1), + DEFINE_PROP_BOOL("pre-2.8-migration", SpaprPhbState, pre_2_8_migration, false), - DEFINE_PROP_BOOL("pcie-extended-configuration-space", sPAPRPHBState, + DEFINE_PROP_BOOL("pcie-extended-configuration-space", SpaprPhbState, pcie_ecs, true), DEFINE_PROP_END_OF_LIST(), }; @@ -1843,7 +1960,7 @@ static const VMStateDescription vmstate_spapr_pci_msi = { static int spapr_pci_pre_save(void *opaque) { - sPAPRPHBState *sphb = opaque; + SpaprPhbState *sphb = opaque; GHashTableIter iter; gpointer key, value; int i; @@ -1868,7 +1985,7 @@ static int spapr_pci_pre_save(void *opaque) if (!sphb->msi_devs_num) { return 0; } - sphb->msi_devs = g_malloc(sphb->msi_devs_num * sizeof(spapr_pci_msi_mig)); + sphb->msi_devs = g_new(spapr_pci_msi_mig, sphb->msi_devs_num); g_hash_table_iter_init(&iter, sphb->msi); for (i = 0; g_hash_table_iter_next(&iter, &key, &value); ++i) { @@ -1881,7 +1998,7 @@ static int spapr_pci_pre_save(void *opaque) static int spapr_pci_post_load(void *opaque, int version_id) { - sPAPRPHBState *sphb = opaque; + SpaprPhbState *sphb = opaque; gpointer key, value; int i; @@ -1901,7 +2018,7 @@ static int spapr_pci_post_load(void *opaque, int version_id) static bool pre_2_8_migration(void *opaque, int version_id) { - sPAPRPHBState *sphb = opaque; + SpaprPhbState *sphb = opaque; return sphb->pre_2_8_migration; } @@ -1913,16 +2030,16 @@ static const VMStateDescription vmstate_spapr_pci = { .pre_save = spapr_pci_pre_save, .post_load = spapr_pci_post_load, .fields = (VMStateField[]) { - VMSTATE_UINT64_EQUAL(buid, sPAPRPHBState, NULL), - VMSTATE_UINT32_TEST(mig_liobn, sPAPRPHBState, pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_mem_win_addr, sPAPRPHBState, pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_mem_win_size, sPAPRPHBState, pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_io_win_addr, sPAPRPHBState, pre_2_8_migration), - VMSTATE_UINT64_TEST(mig_io_win_size, sPAPRPHBState, pre_2_8_migration), - VMSTATE_STRUCT_ARRAY(lsi_table, sPAPRPHBState, PCI_NUM_PINS, 0, + VMSTATE_UINT64_EQUAL(buid, SpaprPhbState, NULL), + VMSTATE_UINT32_TEST(mig_liobn, SpaprPhbState, pre_2_8_migration), + VMSTATE_UINT64_TEST(mig_mem_win_addr, SpaprPhbState, pre_2_8_migration), + VMSTATE_UINT64_TEST(mig_mem_win_size, SpaprPhbState, pre_2_8_migration), + VMSTATE_UINT64_TEST(mig_io_win_addr, SpaprPhbState, pre_2_8_migration), + VMSTATE_UINT64_TEST(mig_io_win_size, SpaprPhbState, pre_2_8_migration), + VMSTATE_STRUCT_ARRAY(lsi_table, SpaprPhbState, PCI_NUM_PINS, 0, vmstate_spapr_pci_lsi, struct spapr_pci_lsi), - VMSTATE_INT32(msi_devs_num, sPAPRPHBState), - VMSTATE_STRUCT_VARRAY_ALLOC(msi_devs, sPAPRPHBState, msi_devs_num, 0, + VMSTATE_INT32(msi_devs_num, SpaprPhbState), + VMSTATE_STRUCT_VARRAY_ALLOC(msi_devs, SpaprPhbState, msi_devs_num, 0, vmstate_spapr_pci_msi, spapr_pci_msi_mig), VMSTATE_END_OF_LIST() }, @@ -1931,7 +2048,7 @@ static const VMStateDescription vmstate_spapr_pci = { static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) { - sPAPRPHBState *sphb = SPAPR_PCI_HOST_BRIDGE(host_bridge); + SpaprPhbState *sphb = SPAPR_PCI_HOST_BRIDGE(host_bridge); return sphb->dtbusname; } @@ -1944,6 +2061,7 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) hc->root_bus_path = spapr_phb_root_bus_path; dc->realize = spapr_phb_realize; + dc->unrealize = spapr_phb_unrealize; dc->props = spapr_phb_properties; dc->reset = spapr_phb_reset; dc->vmsd = &vmstate_spapr_pci; @@ -1951,13 +2069,15 @@ static void spapr_phb_class_init(ObjectClass *klass, void *data) dc->user_creatable = true; set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); hp->plug = spapr_pci_plug; + hp->unplug = spapr_pci_unplug; hp->unplug_request = spapr_pci_unplug_request; } static const TypeInfo spapr_phb_info = { .name = TYPE_SPAPR_PCI_HOST_BRIDGE, .parent = TYPE_PCI_HOST_BRIDGE, - .instance_size = sizeof(sPAPRPHBState), + .instance_size = sizeof(SpaprPhbState), + .instance_finalize = spapr_phb_finalizefn, .class_init = spapr_phb_class_init, .interfaces = (InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, @@ -1965,30 +2085,19 @@ static const TypeInfo spapr_phb_info = { } }; -PCIHostState *spapr_create_phb(sPAPRMachineState *spapr, int index) -{ - DeviceState *dev; - - dev = qdev_create(NULL, TYPE_SPAPR_PCI_HOST_BRIDGE); - qdev_prop_set_uint32(dev, "index", index); - qdev_init_nofail(dev); - - return PCI_HOST_BRIDGE(dev); -} - -typedef struct sPAPRFDT { +typedef struct SpaprFdt { void *fdt; int node_off; - sPAPRPHBState *sphb; -} sPAPRFDT; + SpaprPhbState *sphb; +} SpaprFdt; static void spapr_populate_pci_devices_dt(PCIBus *bus, PCIDevice *pdev, void *opaque) { PCIBus *sec_bus; - sPAPRFDT *p = opaque; + SpaprFdt *p = opaque; int offset; - sPAPRFDT s_fdt; + SpaprFdt s_fdt; offset = spapr_create_pci_child_dt(p->sphb, pdev, p->fdt, p->node_off); if (!offset) { @@ -2018,8 +2127,6 @@ static void spapr_phb_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, void *opaque) { unsigned int *bus_no = opaque; - unsigned int primary = *bus_no; - unsigned int subordinate = 0xff; PCIBus *sec_bus = NULL; if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != @@ -2028,7 +2135,7 @@ static void spapr_phb_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, } (*bus_no)++; - pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1); + pci_default_write_config(pdev, PCI_PRIMARY_BUS, pci_dev_bus_num(pdev), 1); pci_default_write_config(pdev, PCI_SECONDARY_BUS, *bus_no, 1); pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1); @@ -2037,13 +2144,12 @@ static void spapr_phb_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, return; } - pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1); pci_for_each_device(sec_bus, pci_bus_num(sec_bus), spapr_phb_pci_enumerate_bridge, bus_no); pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, *bus_no, 1); } -static void spapr_phb_pci_enumerate(sPAPRPHBState *phb) +static void spapr_phb_pci_enumerate(SpaprPhbState *phb) { PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; unsigned int bus_no = 0; @@ -2054,9 +2160,8 @@ static void spapr_phb_pci_enumerate(sPAPRPHBState *phb) } -int spapr_populate_pci_dt(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt) +int spapr_populate_pci_dt(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt, + uint32_t nr_msis, int *node_offset) { int bus_off, i, j, ret; gchar *nodename; @@ -2103,14 +2208,18 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, cpu_to_be32(0x0), cpu_to_be32(0x0), cpu_to_be32(phb->numa_node)}; - sPAPRTCETable *tcet; + SpaprTceTable *tcet; PCIBus *bus = PCI_HOST_BRIDGE(phb)->bus; - sPAPRFDT s_fdt; + SpaprFdt s_fdt; + SpaprDrc *drc; /* Start populating the FDT */ nodename = g_strdup_printf("pci@%" PRIx64, phb->buid); _FDT(bus_off = fdt_add_subnode(fdt, 0, nodename)); g_free(nodename); + if (node_offset) { + *node_offset = bus_off; + } /* Write PHB properties */ _FDT(fdt_setprop_string(fdt, bus_off, "device_type", "pci")); @@ -2123,7 +2232,7 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, _FDT(fdt_setprop(fdt, bus_off, "ranges", &ranges, sizeof_ranges)); _FDT(fdt_setprop(fdt, bus_off, "reg", &bus_reg, sizeof(bus_reg))); _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pci-config-space-type", 0x1)); - _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", XICS_IRQS_SPAPR)); + _FDT(fdt_setprop_cell(fdt, bus_off, "ibm,pe-total-#msi", nr_msis)); /* Dynamic DMA window */ if (phb->ddw_enabled) { @@ -2153,8 +2262,8 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, irqmap[1] = 0; irqmap[2] = 0; irqmap[3] = cpu_to_be32(j+1); - irqmap[4] = cpu_to_be32(xics_phandle); - spapr_dt_xics_irq(&irqmap[5], phb->lsi_table[lsi_num].irq, true); + irqmap[4] = cpu_to_be32(intc_phandle); + spapr_dt_irq(&irqmap[5], phb->lsi_table[lsi_num].irq, true); } } /* Write interrupt map */ @@ -2169,6 +2278,14 @@ int spapr_populate_pci_dt(sPAPRPHBState *phb, tcet->liobn, tcet->bus_offset, tcet->nb_table << tcet->page_shift); + drc = spapr_drc_by_id(TYPE_SPAPR_DRC_PHB, phb->index); + if (drc) { + uint32_t drc_index = cpu_to_be32(spapr_drc_index(drc)); + + _FDT(fdt_setprop(fdt, bus_off, "ibm,my-drc-index", &drc_index, + sizeof(drc_index))); + } + /* Walk the bridges and program the bus numbers*/ spapr_phb_pci_enumerate(phb); _FDT(fdt_setprop_cell(fdt, bus_off, "qemu,phb-enumerated", 0x1)); @@ -2231,6 +2348,7 @@ void spapr_pci_rtas_init(void) static void spapr_pci_register_types(void) { type_register_static(&spapr_phb_info); + type_register_static(&spapr_phb_root_bus_info); } type_init(spapr_pci_register_types) @@ -2249,8 +2367,8 @@ static int spapr_switch_one_vga(DeviceState *dev, void *opaque) void spapr_pci_switch_vga(bool big_endian) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - sPAPRPHBState *sphb; + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprPhbState *sphb; /* * For backward compatibility with existing guests, we switch diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 71491dbd28..5f5dde567d 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -28,12 +28,12 @@ #include "qemu/error-report.h" #include "sysemu/qtest.h" -bool spapr_phb_eeh_available(sPAPRPHBState *sphb) +bool spapr_phb_eeh_available(SpaprPhbState *sphb) { return vfio_eeh_as_ok(&sphb->iommu_as); } -static void spapr_phb_vfio_eeh_reenable(sPAPRPHBState *sphb) +static void spapr_phb_vfio_eeh_reenable(SpaprPhbState *sphb) { vfio_eeh_as_op(&sphb->iommu_as, VFIO_EEH_PE_ENABLE); } @@ -49,7 +49,7 @@ void spapr_phb_vfio_reset(DeviceState *qdev) spapr_phb_vfio_eeh_reenable(SPAPR_PCI_HOST_BRIDGE(qdev)); } -int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, +int spapr_phb_vfio_eeh_set_option(SpaprPhbState *sphb, unsigned int addr, int option) { uint32_t op; @@ -96,7 +96,7 @@ int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, return RTAS_OUT_SUCCESS; } -int spapr_phb_vfio_eeh_get_state(sPAPRPHBState *sphb, int *state) +int spapr_phb_vfio_eeh_get_state(SpaprPhbState *sphb, int *state) { int ret; @@ -145,14 +145,14 @@ static void spapr_phb_vfio_eeh_clear_bus_msix(PCIBus *bus, void *opaque) spapr_phb_vfio_eeh_clear_dev_msix, NULL); } -static void spapr_phb_vfio_eeh_pre_reset(sPAPRPHBState *sphb) +static void spapr_phb_vfio_eeh_pre_reset(SpaprPhbState *sphb) { PCIHostState *phb = PCI_HOST_BRIDGE(sphb); pci_for_each_bus(phb->bus, spapr_phb_vfio_eeh_clear_bus_msix, NULL); } -int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) +int spapr_phb_vfio_eeh_reset(SpaprPhbState *sphb, int option) { uint32_t op; int ret; @@ -181,7 +181,7 @@ int spapr_phb_vfio_eeh_reset(sPAPRPHBState *sphb, int option) return RTAS_OUT_SUCCESS; } -int spapr_phb_vfio_eeh_configure(sPAPRPHBState *sphb) +int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb) { int ret; diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index d2acd61a15..4060987590 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -29,15 +29,15 @@ #include "kvm_ppc.h" #define SPAPR_RNG(obj) \ - OBJECT_CHECK(sPAPRRngState, (obj), TYPE_SPAPR_RNG) + OBJECT_CHECK(SpaprRngState, (obj), TYPE_SPAPR_RNG) -struct sPAPRRngState { +struct SpaprRngState { /*< private >*/ DeviceState ds; RngBackend *backend; bool use_kvm; }; -typedef struct sPAPRRngState sPAPRRngState; +typedef struct SpaprRngState SpaprRngState; struct HRandomData { QemuSemaphore sem; @@ -64,10 +64,10 @@ static void random_recv(void *dest, const void *src, size_t size) } /* Handler for the H_RANDOM hypercall */ -static target_ulong h_random(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_random(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { - sPAPRRngState *rngstate; + SpaprRngState *rngstate; HRandomData hrdata; rngstate = SPAPR_RNG(object_resolve_path_type("", TYPE_SPAPR_RNG, NULL)); @@ -109,7 +109,7 @@ static void spapr_rng_instance_init(Object *obj) static void spapr_rng_realize(DeviceState *dev, Error **errp) { - sPAPRRngState *rngstate = SPAPR_RNG(dev); + SpaprRngState *rngstate = SPAPR_RNG(dev); if (rngstate->use_kvm) { if (kvmppc_enable_hwrng() == 0) { @@ -132,32 +132,9 @@ static void spapr_rng_realize(DeviceState *dev, Error **errp) } } -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_LINK("rng", sPAPRRngState, backend, TYPE_RNG_BACKEND, + DEFINE_PROP_BOOL("use-kvm", SpaprRngState, use_kvm, false), + DEFINE_PROP_LINK("rng", SpaprRngState, backend, TYPE_RNG_BACKEND, RngBackend *), DEFINE_PROP_END_OF_LIST(), }; @@ -175,7 +152,7 @@ static void spapr_rng_class_init(ObjectClass *oc, void *data) static const TypeInfo spapr_rng_info = { .name = TYPE_SPAPR_RNG, .parent = TYPE_DEVICE, - .instance_size = sizeof(sPAPRRngState), + .instance_size = sizeof(SpaprRngState), .instance_init = spapr_rng_instance_init, .class_init = spapr_rng_class_init, }; diff --git a/hw/ppc/spapr_rtas.c b/hw/ppc/spapr_rtas.c index 4ac96bc94b..24c45b12d4 100644 --- a/hw/ppc/spapr_rtas.c +++ b/hw/ppc/spapr_rtas.c @@ -33,6 +33,7 @@ #include "sysemu/device_tree.h" #include "sysemu/cpus.h" #include "sysemu/hw_accel.h" +#include "kvm_ppc.h" #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" @@ -49,13 +50,13 @@ #include "target/ppc/mmu-hash64.h" #include "target/ppc/mmu-book3s-v3.h" -static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_display_character(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { uint8_t c = rtas_ld(args, 0); - VIOsPAPRDevice *sdev = vty_lookup(spapr, 0); + SpaprVioDevice *sdev = vty_lookup(spapr, 0); if (!sdev) { rtas_st(rets, 0, RTAS_OUT_HW_ERROR); @@ -65,7 +66,7 @@ static void rtas_display_character(PowerPCCPU *cpu, sPAPRMachineState *spapr, } } -static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_power_off(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { @@ -78,7 +79,7 @@ static void rtas_power_off(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_system_reboot(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_system_reboot(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -92,7 +93,7 @@ static void rtas_system_reboot(PowerPCCPU *cpu, sPAPRMachineState *spapr, } static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -122,7 +123,7 @@ static void rtas_query_cpu_stopped_state(PowerPCCPU *cpu_, rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); } -static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, +static void rtas_start_cpu(PowerPCCPU *callcpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -171,10 +172,10 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, * New cpus are expected to start in the same radix/hash mode * as the existing CPUs */ - if (ppc64_radix_guest(callcpu)) { - lpcr |= LPCR_UPRT | LPCR_GTSE; + if (ppc64_v3_radix(callcpu)) { + lpcr |= LPCR_UPRT | LPCR_GTSE | LPCR_HR; } else { - lpcr &= ~(LPCR_UPRT | LPCR_GTSE); + lpcr &= ~(LPCR_UPRT | LPCR_GTSE | LPCR_HR); } } ppc_store_lpcr(newcpu, lpcr); @@ -193,7 +194,7 @@ static void rtas_start_cpu(PowerPCCPU *callcpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_stop_self(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -207,6 +208,7 @@ static void rtas_stop_self(PowerPCCPU *cpu, sPAPRMachineState *spapr, * guest */ ppc_store_lpcr(cpu, env->spr[SPR_LPCR] & ~pcc->lpcr_pm); cs->halted = 1; + kvmppc_set_reg_ppc_online(cpu, 0); qemu_cpu_kick(cs); } @@ -224,7 +226,7 @@ static inline int sysparm_st(target_ulong addr, target_ulong len, } static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -266,7 +268,7 @@ static void rtas_ibm_get_system_parameter(PowerPCCPU *cpu, } static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -286,7 +288,7 @@ static void rtas_ibm_set_system_parameter(PowerPCCPU *cpu, } static void rtas_ibm_os_term(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -296,7 +298,7 @@ static void rtas_ibm_os_term(PowerPCCPU *cpu, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_set_power_level(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -321,7 +323,7 @@ static void rtas_set_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 1, 100); } -static void rtas_get_power_level(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_get_power_level(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -351,7 +353,7 @@ static struct rtas_call { spapr_rtas_fn fn; } rtas_table[RTAS_TOKEN_MAX - RTAS_TOKEN_BASE]; -target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *spapr, +target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { @@ -385,7 +387,7 @@ uint64_t qtest_rtas_call(char *cmd, uint32_t nargs, uint64_t args, for (token = 0; token < RTAS_TOKEN_MAX - RTAS_TOKEN_BASE; token++) { if (strcmp(cmd, rtas_table[token].name) == 0) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); PowerPCCPU *cpu = POWERPC_CPU(first_cpu); rtas_table[token].fn(cpu, spapr, token + RTAS_TOKEN_BASE, @@ -423,7 +425,7 @@ void spapr_dt_rtas_tokens(void *fdt, int rtas) } } -void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr) +void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr) { int rtas_node; int ret; diff --git a/hw/ppc/spapr_rtas_ddw.c b/hw/ppc/spapr_rtas_ddw.c index 329feb148f..f6538189f4 100644 --- a/hw/ppc/spapr_rtas_ddw.c +++ b/hw/ppc/spapr_rtas_ddw.c @@ -26,16 +26,16 @@ static int spapr_phb_get_active_win_num_cb(Object *child, void *opaque) { - sPAPRTCETable *tcet; + SpaprTceTable *tcet; - tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); + tcet = (SpaprTceTable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); if (tcet && tcet->nb_table) { ++*(unsigned *)opaque; } return 0; } -static unsigned spapr_phb_get_active_win_num(sPAPRPHBState *sphb) +static unsigned spapr_phb_get_active_win_num(SpaprPhbState *sphb) { unsigned ret = 0; @@ -46,9 +46,9 @@ static unsigned spapr_phb_get_active_win_num(sPAPRPHBState *sphb) static int spapr_phb_get_free_liobn_cb(Object *child, void *opaque) { - sPAPRTCETable *tcet; + SpaprTceTable *tcet; - tcet = (sPAPRTCETable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); + tcet = (SpaprTceTable *) object_dynamic_cast(child, TYPE_SPAPR_TCE_TABLE); if (tcet && !tcet->nb_table) { *(uint32_t *)opaque = tcet->liobn; return 1; @@ -56,7 +56,7 @@ static int spapr_phb_get_free_liobn_cb(Object *child, void *opaque) return 0; } -static unsigned spapr_phb_get_free_liobn(sPAPRPHBState *sphb) +static unsigned spapr_phb_get_free_liobn(SpaprPhbState *sphb) { uint32_t liobn = 0; @@ -90,15 +90,14 @@ static uint32_t spapr_page_mask_to_query_mask(uint64_t page_mask) } static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; - uint64_t buid, max_window_size; + SpaprPhbState *sphb; + uint64_t buid; uint32_t avail, addr, pgmask = 0; - MachineState *machine = MACHINE(spapr); if ((nargs != 3) || (nret != 5)) { goto param_error_exit; @@ -114,27 +113,15 @@ static void rtas_ibm_query_pe_dma_window(PowerPCCPU *cpu, /* Translate page mask to LoPAPR format */ pgmask = spapr_page_mask_to_query_mask(sphb->page_size_mask); - /* - * This is "Largest contiguous block of TCEs allocated specifically - * for (that is, are reserved for) this PE". - * Return the maximum number as maximum supported RAM size was in 4K pages. - */ - if (machine->ram_size == machine->maxram_size) { - max_window_size = machine->ram_size; - } else { - max_window_size = machine->device_memory->base + - memory_region_size(&machine->device_memory->mr); - } - avail = SPAPR_PCI_DMA_MAX_WINDOWS - spapr_phb_get_active_win_num(sphb); rtas_st(rets, 0, RTAS_OUT_SUCCESS); rtas_st(rets, 1, avail); - rtas_st(rets, 2, max_window_size >> SPAPR_TCE_PAGE_SHIFT); + rtas_st(rets, 2, 0x80000000); /* The largest window we can possibly have */ rtas_st(rets, 3, pgmask); rtas_st(rets, 4, 0); /* DMA migration mask, not supported */ - trace_spapr_iommu_ddw_query(buid, addr, avail, max_window_size, pgmask); + trace_spapr_iommu_ddw_query(buid, addr, avail, 0x80000000, pgmask); return; param_error_exit: @@ -142,13 +129,13 @@ param_error_exit: } static void rtas_ibm_create_pe_dma_window(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; - sPAPRTCETable *tcet = NULL; + SpaprPhbState *sphb; + SpaprTceTable *tcet = NULL; uint32_t addr, page_shift, window_shift, liobn; uint64_t buid, win_addr; int windows; @@ -184,8 +171,18 @@ static void rtas_ibm_create_pe_dma_window(PowerPCCPU *cpu, } win_addr = (windows == 0) ? sphb->dma_win_addr : sphb->dma64_win_addr; + /* + * We have just created a window, we know for the fact that it is empty, + * use a hack to avoid iterating over the table as it is quite possible + * to have billions of TCEs, all empty. + * Note that we cannot delay this to the first H_PUT_TCE as this hcall is + * mostly likely to be handled in KVM so QEMU just does not know if it + * happened. + */ + tcet->skipping_replay = true; spapr_tce_table_enable(tcet, page_shift, win_addr, 1ULL << (window_shift - page_shift)); + tcet->skipping_replay = false; if (!tcet->nb_table) { goto hw_error_exit; } @@ -209,13 +206,13 @@ param_error_exit: } static void rtas_ibm_remove_pe_dma_window(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; - sPAPRTCETable *tcet; + SpaprPhbState *sphb; + SpaprTceTable *tcet; uint32_t liobn; if ((nargs != 1) || (nret != 1)) { @@ -244,12 +241,12 @@ param_error_exit: } static void rtas_ibm_reset_pe_dma_window(PowerPCCPU *cpu, - sPAPRMachineState *spapr, + SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRPHBState *sphb; + SpaprPhbState *sphb; uint64_t buid; uint32_t addr; diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index a37360537e..d732a3ea95 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -31,10 +31,10 @@ #include "sysemu/sysemu.h" #include "hw/ppc/spapr.h" #include "qapi/error.h" -#include "qapi/qapi-events-misc.h" +#include "qapi/qapi-events-target.h" #include "qemu/cutils.h" -void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns) +void spapr_rtc_read(SpaprRtcState *rtc, struct tm *tm, uint32_t *ns) { int64_t host_ns = qemu_clock_get_ns(rtc_clock); int64_t guest_ns; @@ -53,7 +53,7 @@ void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns) } } -int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset) +int spapr_rtc_import_offset(SpaprRtcState *rtc, int64_t legacy_offset) { if (!rtc) { return -ENODEV; @@ -64,7 +64,7 @@ int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset) return 0; } -static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_get_time_of_day(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) @@ -89,12 +89,12 @@ static void rtas_get_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 7, ns); } -static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_set_time_of_day(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - sPAPRRTCState *rtc = &spapr->rtc; + SpaprRtcState *rtc = &spapr->rtc; struct tm tm; time_t new_s; int64_t host_ns; @@ -118,7 +118,7 @@ static void rtas_set_time_of_day(PowerPCCPU *cpu, sPAPRMachineState *spapr, } /* Generate a monitor event for the change */ - qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); host_ns = qemu_clock_get_ns(rtc_clock); @@ -134,7 +134,7 @@ static void spapr_rtc_qom_date(Object *obj, struct tm *current_tm, Error **errp) static void spapr_rtc_realize(DeviceState *dev, Error **errp) { - sPAPRRTCState *rtc = SPAPR_RTC(dev); + SpaprRtcState *rtc = SPAPR_RTC(dev); struct tm tm; time_t host_s; int64_t rtc_ns; @@ -154,7 +154,7 @@ static const VMStateDescription vmstate_spapr_rtc = { .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { - VMSTATE_INT64(ns_offset, sPAPRRTCState), + VMSTATE_INT64(ns_offset, SpaprRtcState), VMSTATE_END_OF_LIST() }, }; @@ -177,7 +177,7 @@ static void spapr_rtc_class_init(ObjectClass *oc, void *data) static const TypeInfo spapr_rtc_info = { .name = TYPE_SPAPR_RTC, .parent = TYPE_DEVICE, - .instance_size = sizeof(sPAPRRTCState), + .instance_size = sizeof(SpaprRtcState), .class_init = spapr_rtc_class_init, }; diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index be9af71437..583c13deda 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -37,48 +37,17 @@ #include "hw/ppc/spapr.h" #include "hw/ppc/spapr_vio.h" -#include "hw/ppc/xics.h" #include "hw/ppc/fdt.h" #include "trace.h" #include -static void spapr_vio_get_irq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - Property *prop = opaque; - uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); - - visit_type_uint32(v, name, ptr, errp); -} - -static void spapr_vio_set_irq(Object *obj, Visitor *v, const char *name, - void *opaque, Error **errp) -{ - Property *prop = opaque; - uint32_t *ptr = qdev_get_prop_ptr(DEVICE(obj), prop); - - if (!qtest_enabled()) { - warn_report(TYPE_VIO_SPAPR_DEVICE " '%s' property is deprecated", name); - } - visit_type_uint32(v, name, ptr, errp); -} - -static const PropertyInfo spapr_vio_irq_propinfo = { - .name = "irq", - .get = spapr_vio_get_irq, - .set = spapr_vio_set_irq, -}; - -static Property spapr_vio_props[] = { - DEFINE_PROP("irq", VIOsPAPRDevice, irq, spapr_vio_irq_propinfo, uint32_t), - DEFINE_PROP_END_OF_LIST(), -}; +#define SPAPR_VIO_REG_BASE 0x71000000 static char *spapr_vio_get_dev_name(DeviceState *qdev) { - VIOsPAPRDevice *dev = VIO_SPAPR_DEVICE(qdev); - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + SpaprVioDevice *dev = VIO_SPAPR_DEVICE(qdev); + SpaprVioDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); /* Device tree style name device@reg */ return g_strdup_printf("%s@%x", pc->dt_name, dev->reg); @@ -96,16 +65,16 @@ static const TypeInfo spapr_vio_bus_info = { .name = TYPE_SPAPR_VIO_BUS, .parent = TYPE_BUS, .class_init = spapr_vio_bus_class_init, - .instance_size = sizeof(VIOsPAPRBus), + .instance_size = sizeof(SpaprVioBus), }; -VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) +SpaprVioDevice *spapr_vio_find_by_reg(SpaprVioBus *bus, uint32_t reg) { BusChild *kid; - VIOsPAPRDevice *dev = NULL; + SpaprVioDevice *dev = NULL; QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - dev = (VIOsPAPRDevice *)kid->child; + dev = (SpaprVioDevice *)kid->child; if (dev->reg == reg) { return dev; } @@ -114,10 +83,10 @@ VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg) return NULL; } -static int vio_make_devnode(VIOsPAPRDevice *dev, +static int vio_make_devnode(SpaprVioDevice *dev, void *fdt) { - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + SpaprVioDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); int vdevice_off, node_off, ret; char *dt_name; @@ -157,7 +126,7 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, if (dev->irq) { uint32_t ints_prop[2]; - spapr_dt_xics_irq(ints_prop, dev->irq, false); + spapr_dt_irq(ints_prop, dev->irq, false); ret = fdt_setprop(fdt, node_off, "interrupts", ints_prop, sizeof(ints_prop)); if (ret < 0) { @@ -183,13 +152,13 @@ static int vio_make_devnode(VIOsPAPRDevice *dev, /* * CRQ handling */ -static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_reg_crq(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong queue_addr = args[1]; target_ulong queue_len = args[2]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); if (!dev) { hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); @@ -228,7 +197,7 @@ static target_ulong h_reg_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -static target_ulong free_crq(VIOsPAPRDevice *dev) +static target_ulong free_crq(SpaprVioDevice *dev) { dev->crq.qladdr = 0; dev->crq.qsize = 0; @@ -239,11 +208,11 @@ static target_ulong free_crq(VIOsPAPRDevice *dev) return H_SUCCESS; } -static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_free_crq(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); if (!dev) { hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); @@ -253,13 +222,13 @@ static target_ulong h_free_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, return free_crq(dev); } -static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_send_crq(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong msg_hi = args[1]; target_ulong msg_lo = args[2]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); uint64_t crq_mangle[2]; if (!dev) { @@ -276,11 +245,11 @@ static target_ulong h_send_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_HARDWARE; } -static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_enable_crq(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); if (!dev) { hcall_dprintf("Unit 0x" TARGET_FMT_lx " does not exist\n", reg); @@ -291,7 +260,7 @@ static target_ulong h_enable_crq(PowerPCCPU *cpu, sPAPRMachineState *spapr, } /* Returns negative error, 0 success, or positive: queue full */ -int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) +int spapr_vio_send_crq(SpaprVioDevice *dev, uint8_t *crq) { int rc; uint8_t byte; @@ -334,7 +303,7 @@ int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq) /* "quiesce" handling */ -static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) +static void spapr_vio_quiesce_one(SpaprVioDevice *dev) { if (dev->tcet) { device_reset(DEVICE(dev->tcet)); @@ -342,7 +311,7 @@ static void spapr_vio_quiesce_one(VIOsPAPRDevice *dev) free_crq(dev); } -void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass) +void spapr_vio_set_bypass(SpaprVioDevice *dev, bool bypass) { if (!dev->tcet) { return; @@ -354,13 +323,13 @@ void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass) dev->tcet->bypass = bypass; } -static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_set_tce_bypass(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - VIOsPAPRBus *bus = spapr->vio_bus; - VIOsPAPRDevice *dev; + SpaprVioBus *bus = spapr->vio_bus; + SpaprVioDevice *dev; uint32_t unit, enable; if (nargs != 2) { @@ -385,14 +354,14 @@ static void rtas_set_tce_bypass(PowerPCCPU *cpu, sPAPRMachineState *spapr, rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static void rtas_quiesce(PowerPCCPU *cpu, SpaprMachineState *spapr, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets) { - VIOsPAPRBus *bus = spapr->vio_bus; + SpaprVioBus *bus = spapr->vio_bus; BusChild *kid; - VIOsPAPRDevice *dev = NULL; + SpaprVioDevice *dev = NULL; if (nargs != 0) { rtas_st(rets, 0, RTAS_OUT_PARAM_ERROR); @@ -400,18 +369,18 @@ static void rtas_quiesce(PowerPCCPU *cpu, sPAPRMachineState *spapr, } QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { - dev = (VIOsPAPRDevice *)kid->child; + dev = (SpaprVioDevice *)kid->child; spapr_vio_quiesce_one(dev); } rtas_st(rets, 0, RTAS_OUT_SUCCESS); } -static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) +static SpaprVioDevice *reg_conflict(SpaprVioDevice *dev) { - VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); + SpaprVioBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); BusChild *kid; - VIOsPAPRDevice *other; + SpaprVioDevice *other; /* * Check for a device other than the given one which is already @@ -431,8 +400,8 @@ static VIOsPAPRDevice *reg_conflict(VIOsPAPRDevice *dev) static void spapr_vio_busdev_reset(DeviceState *qdev) { - VIOsPAPRDevice *dev = VIO_SPAPR_DEVICE(qdev); - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + SpaprVioDevice *dev = VIO_SPAPR_DEVICE(qdev); + SpaprVioDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); /* Shut down the request queue and TCEs if necessary */ spapr_vio_quiesce_one(dev); @@ -445,11 +414,60 @@ static void spapr_vio_busdev_reset(DeviceState *qdev) } } +/* + * The register property of a VIO device is defined in livirt using + * 0x1000 as a base register number plus a 0x1000 increment. For the + * VIO tty device, the base number is changed to 0x30000000. QEMU uses + * a base register number of 0x71000000 and then a simple increment. + * + * The formula below tries to compute a unique index number from the + * register value that will be used to define the IRQ number of the + * VIO device. + * + * A maximum of 256 VIO devices is covered. Collisions are possible + * but they will be detected when the IRQ is claimed. + */ +static inline uint32_t spapr_vio_reg_to_irq(uint32_t reg) +{ + uint32_t irq; + + if (reg >= SPAPR_VIO_REG_BASE) { + /* + * VIO device register values when allocated by QEMU. For + * these, we simply mask the high bits to fit the overall + * range: [0x00 - 0xff]. + * + * The nvram VIO device (reg=0x71000000) is a static device of + * the pseries machine and so is always allocated by QEMU. Its + * IRQ number is 0x0. + */ + irq = reg & 0xff; + + } else if (reg >= 0x30000000) { + /* + * VIO tty devices register values, when allocated by livirt, + * are mapped in range [0xf0 - 0xff], gives us a maximum of 16 + * vtys. + */ + irq = 0xf0 | ((reg >> 12) & 0xf); + + } else { + /* + * Other VIO devices register values, when allocated by + * livirt, should be mapped in range [0x00 - 0xef]. Conflicts + * will be detected when IRQ is claimed. + */ + irq = (reg >> 12) & 0xff; + } + + return SPAPR_IRQ_VIO | irq; +} + static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); - VIOsPAPRDevice *dev = (VIOsPAPRDevice *)qdev; - VIOsPAPRDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprVioDevice *dev = (SpaprVioDevice *)qdev; + SpaprVioDeviceClass *pc = VIO_SPAPR_DEVICE_GET_CLASS(dev); char *id; Error *local_err = NULL; @@ -460,7 +478,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) * rather than using spapr_vio_find_by_reg() because sdev * itself is already in the list. */ - VIOsPAPRDevice *other = reg_conflict(dev); + SpaprVioDevice *other = reg_conflict(dev); if (other) { error_setg(errp, "%s and %s devices conflict at address %#x", @@ -471,7 +489,7 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) } } else { /* Need to assign an address */ - VIOsPAPRBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); + SpaprVioBus *bus = SPAPR_VIO_BUS(dev->qdev.parent_bus); do { dev->reg = bus->next_reg++; @@ -484,7 +502,9 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) dev->qdev.id = id; } - if (!dev->irq) { + dev->irq = spapr_vio_reg_to_irq(dev->reg); + + if (SPAPR_MACHINE_GET_CLASS(spapr)->legacy_irq_allocation) { dev->irq = spapr_irq_findone(spapr, &local_err); if (local_err) { error_propagate(errp, local_err); @@ -520,14 +540,14 @@ static void spapr_vio_busdev_realize(DeviceState *qdev, Error **errp) pc->realize(dev, errp); } -static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPRMachineState *spapr, +static target_ulong h_vio_signal(PowerPCCPU *cpu, SpaprMachineState *spapr, target_ulong opcode, target_ulong *args) { target_ulong reg = args[0]; target_ulong mode = args[1]; - VIOsPAPRDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); - VIOsPAPRDeviceClass *pc; + SpaprVioDevice *dev = spapr_vio_find_by_reg(spapr->vio_bus, reg); + SpaprVioDeviceClass *pc; if (!dev) { return H_PARAMETER; @@ -544,9 +564,9 @@ static target_ulong h_vio_signal(PowerPCCPU *cpu, sPAPRMachineState *spapr, return H_SUCCESS; } -VIOsPAPRBus *spapr_vio_bus_init(void) +SpaprVioBus *spapr_vio_bus_init(void) { - VIOsPAPRBus *bus; + SpaprVioBus *bus; BusState *qbus; DeviceState *dev; @@ -557,7 +577,7 @@ VIOsPAPRBus *spapr_vio_bus_init(void) /* Create bus on bridge device */ qbus = qbus_create(TYPE_SPAPR_VIO_BUS, dev, "spapr-vio"); bus = SPAPR_VIO_BUS(qbus); - bus->next_reg = 0x71000000; + bus->next_reg = SPAPR_VIO_REG_BASE; /* hcall-vio */ spapr_register_hypercall(H_VIO_SIGNAL, h_vio_signal); @@ -595,14 +615,14 @@ const VMStateDescription vmstate_spapr_vio = { .minimum_version_id = 1, .fields = (VMStateField[]) { /* Sanity check */ - VMSTATE_UINT32_EQUAL(reg, VIOsPAPRDevice, NULL), - VMSTATE_UINT32_EQUAL(irq, VIOsPAPRDevice, NULL), + VMSTATE_UINT32_EQUAL(reg, SpaprVioDevice, NULL), + VMSTATE_UINT32_EQUAL(irq, SpaprVioDevice, NULL), /* General VIO device state */ - VMSTATE_UINT64(signal_state, VIOsPAPRDevice), - VMSTATE_UINT64(crq.qladdr, VIOsPAPRDevice), - VMSTATE_UINT32(crq.qsize, VIOsPAPRDevice), - VMSTATE_UINT32(crq.qnext, VIOsPAPRDevice), + VMSTATE_UINT64(signal_state, SpaprVioDevice), + VMSTATE_UINT64(crq.qladdr, SpaprVioDevice), + VMSTATE_UINT32(crq.qsize, SpaprVioDevice), + VMSTATE_UINT32(crq.qnext, SpaprVioDevice), VMSTATE_END_OF_LIST() }, @@ -614,15 +634,14 @@ static void vio_spapr_device_class_init(ObjectClass *klass, void *data) k->realize = spapr_vio_busdev_realize; k->reset = spapr_vio_busdev_reset; k->bus_type = TYPE_SPAPR_VIO_BUS; - k->props = spapr_vio_props; } static const TypeInfo spapr_vio_type_info = { .name = TYPE_VIO_SPAPR_DEVICE, .parent = TYPE_DEVICE, - .instance_size = sizeof(VIOsPAPRDevice), + .instance_size = sizeof(SpaprVioDevice), .abstract = true, - .class_size = sizeof(VIOsPAPRDeviceClass), + .class_size = sizeof(SpaprVioDeviceClass), .class_init = vio_spapr_device_class_init, }; @@ -637,10 +656,10 @@ type_init(spapr_vio_register_types) static int compare_reg(const void *p1, const void *p2) { - VIOsPAPRDevice const *dev1, *dev2; + SpaprVioDevice const *dev1, *dev2; - dev1 = (VIOsPAPRDevice *)*(DeviceState **)p1; - dev2 = (VIOsPAPRDevice *)*(DeviceState **)p2; + dev1 = (SpaprVioDevice *)*(DeviceState **)p1; + dev2 = (SpaprVioDevice *)*(DeviceState **)p2; if (dev1->reg < dev2->reg) { return -1; @@ -653,7 +672,7 @@ static int compare_reg(const void *p1, const void *p2) return 1; } -void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt) +void spapr_dt_vdevice(SpaprVioBus *bus, void *fdt) { DeviceState *qdev, **qdevs; BusChild *kid; @@ -676,7 +695,7 @@ void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt) } /* Copy out into an array of pointers */ - qdevs = g_malloc(sizeof(qdev) * num); + qdevs = g_new(DeviceState *, num); num = 0; QTAILQ_FOREACH(kid, &bus->bus.children, sibling) { qdevs[num++] = kid->child; @@ -688,8 +707,8 @@ void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt) /* Hack alert. Give the devices to libfdt in reverse order, we happen * to know that will mean they are in forward order in the tree. */ for (i = num - 1; i >= 0; i--) { - VIOsPAPRDevice *dev = (VIOsPAPRDevice *)(qdevs[i]); - VIOsPAPRDeviceClass *vdc = VIO_SPAPR_DEVICE_GET_CLASS(dev); + SpaprVioDevice *dev = (SpaprVioDevice *)(qdevs[i]); + SpaprVioDeviceClass *vdc = VIO_SPAPR_DEVICE_GET_CLASS(dev); ret = vio_make_devnode(dev, fdt); if (ret < 0) { @@ -702,9 +721,9 @@ void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt) g_free(qdevs); } -gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus) +gchar *spapr_vio_stdout_path(SpaprVioBus *bus) { - VIOsPAPRDevice *dev; + SpaprVioDevice *dev; char *name, *path; dev = spapr_vty_get_default(bus); diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events index dc5e65aee9..f76448f532 100644 --- a/hw/ppc/trace-events +++ b/hw/ppc/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/ppc/spapr_pci.c +# spapr_pci.c spapr_pci_msi(const char *msg, uint32_t ca) "%s (cfg=0x%x)" spapr_pci_msi_setup(const char *name, unsigned vector, uint64_t addr) "dev\"%s\" vector %u, addr=0x%"PRIx64 spapr_pci_rtas_ibm_change_msi(unsigned cfg, unsigned func, unsigned req, unsigned first) "cfgaddr 0x%x func %u, requested %u, first irq %u" @@ -9,21 +9,23 @@ spapr_pci_msi_write(uint64_t addr, uint64_t data, uint32_t dt_irq) "@0x%"PRIx64" 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 0x%x asked %u, have only %u" -# hw/ppc/spapr.c +# spapr.c spapr_cas_failed(unsigned long n) "DT diff buffer is too small: %ld bytes" spapr_cas_continue(unsigned long n) "Copy changes to the guest: %ld bytes" -spapr_irq_alloc(int irq) "irq %d" -spapr_irq_alloc_block(int first, int num, bool lsi, int align) "first irq %d, %d irqs, lsi=%d, alignnum %d" + +# spapr_irq.c spapr_irq_free(int src, int irq, int num) "Source#%d, first irq %d, %d irqs" spapr_irq_free_warn(int src, int irq) "Source#%d, irq %d is already free" -# hw/ppc/spapr_hcall.c -spapr_cas_pvr_try(uint32_t pvr) "0x%x" +# spapr_hcall.c spapr_cas_pvr(uint32_t cur_pvr, bool explicit_match, uint32_t new_pvr) "current=0x%x, explicit_match=%u, new=0x%x" spapr_h_resize_hpt_prepare(uint64_t flags, uint64_t shift) "flags=0x%"PRIx64", shift=%"PRIu64 spapr_h_resize_hpt_commit(uint64_t flags, uint64_t shift) "flags=0x%"PRIx64", shift=%"PRIu64 +spapr_update_dt(unsigned cb) "New blob %u bytes" +spapr_update_dt_failed_size(unsigned cbold, unsigned cbnew, unsigned magic) "Old blob %u bytes, new blob %u bytes, magic 0x%x" +spapr_update_dt_failed_check(unsigned cbold, unsigned cbnew, unsigned magic) "Old blob %u bytes, new blob %u bytes, magic 0x%x" -# hw/ppc/spapr_iommu.c +# spapr_iommu.c spapr_iommu_put(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t ret) "liobn=0x%"PRIx64" ioba=0x%"PRIx64" tce=0x%"PRIx64" ret=%"PRId64 spapr_iommu_get(uint64_t liobn, uint64_t ioba, uint64_t ret, uint64_t tce) "liobn=0x%"PRIx64" ioba=0x%"PRIx64" ret=%"PRId64" tce=0x%"PRIx64 spapr_iommu_indirect(uint64_t liobn, uint64_t ioba, uint64_t tce, uint64_t iobaN, uint64_t tceN, uint64_t ret) "liobn=0x%"PRIx64" ioba=0x%"PRIx64" tcelist=0x%"PRIx64" iobaN=0x%"PRIx64" tceN=0x%"PRIx64" ret=%"PRId64 @@ -36,70 +38,67 @@ spapr_iommu_xlate(uint64_t liobn, uint64_t ioba, uint64_t tce, unsigned perm, un spapr_iommu_new_table(uint64_t liobn, void *table, int fd) "liobn=0x%"PRIx64" table=%p fd=%d" spapr_iommu_pre_save(uint64_t liobn, uint32_t nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" bus_offset=0x%"PRIx64" ps=%"PRIu32 spapr_iommu_post_load(uint64_t liobn, uint32_t pre_nb, uint32_t post_nb, uint64_t offs, uint32_t ps) "liobn=%"PRIx64" %"PRIx32" => 0x%"PRIx32" bus_offset=0x%"PRIx64" ps=%"PRIu32 + +# spapr_rtas_ddw.c spapr_iommu_ddw_query(uint64_t buid, uint32_t cfgaddr, unsigned wa, uint64_t win_size, uint32_t pgmask) "buid=0x%"PRIx64" addr=0x%"PRIx32", %u windows available, max window size=0x%"PRIx64", mask=0x%"PRIx32 spapr_iommu_ddw_create(uint64_t buid, uint32_t cfgaddr, uint64_t pg_size, uint64_t req_size, uint64_t start, uint32_t liobn) "buid=0x%"PRIx64" addr=0x%"PRIx32", page size=0x%"PRIx64", requested=0x%"PRIx64", start addr=0x%"PRIx64", liobn=0x%"PRIx32 spapr_iommu_ddw_remove(uint32_t liobn) "liobn=0x%"PRIx32 spapr_iommu_ddw_reset(uint64_t buid, uint32_t cfgaddr) "buid=0x%"PRIx64" addr=0x%"PRIx32 -# hw/ppc/spapr_drc.c +# spapr_drc.c spapr_drc_set_isolation_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%"PRIx32 spapr_drc_set_isolation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_set_isolation_state_deferring(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_set_dr_indicator(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x" spapr_drc_set_allocation_state(uint32_t index, int state) "drc: 0x%"PRIx32", state: 0x%x" spapr_drc_set_allocation_state_finalizing(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_set_configured(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_set_configured_skipping(uint32_t index) "drc: 0x%"PRIx32", isolated device" spapr_drc_attach(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_detach(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_awaiting_quiesce(uint32_t index) "drc: 0x%"PRIx32 -spapr_drc_awaiting_allocation(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_reset(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_realize(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_realize_child(uint32_t index, char *childname) "drc: 0x%"PRIx32", child name: %s" spapr_drc_realize_complete(uint32_t index) "drc: 0x%"PRIx32 spapr_drc_unrealize(uint32_t index) "drc: 0x%"PRIx32 -# hw/ppc/spapr_ovec.c +# spapr_ovec.c spapr_ovec_parse_vector(int vector, int byte, uint16_t vec_len, uint8_t entry) "read guest vector %2d, byte %3d / %3d: 0x%.2x" spapr_ovec_populate_dt(int byte, uint16_t vec_len, uint8_t entry) "encoding guest vector byte %3d / %3d: 0x%.2x" -# hw/ppc/spapr_rtas.c +# spapr_drc.c spapr_rtas_get_sensor_state_not_supported(uint32_t index, uint32_t type) "sensor index: 0x%"PRIx32", type: %"PRIu32 spapr_rtas_get_sensor_state_invalid(uint32_t index) "sensor index: 0x%"PRIx32 spapr_rtas_ibm_configure_connector_invalid(uint32_t index) "DRC index: 0x%"PRIx32 -spapr_rtas_ibm_configure_connector_missing_fdt(uint32_t index) "DRC index: 0x%"PRIx32 -# hw/ppc/spapr_vio.c +# spapr_vio.c spapr_vio_h_reg_crq(uint64_t reg, uint64_t queue_addr, uint64_t queue_len) "CRQ for dev 0x%" PRIx64 " registered at 0x%" PRIx64 "/0x%" PRIx64 spapr_vio_free_crq(uint32_t reg) "CRQ for dev 0x%" PRIx32 " freed" -# hw/ppc/ppc.c +# 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.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 -# hw/ppc/prep_systemio.c +# prep_systemio.c prep_systemio_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" prep_systemio_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" -# hw/ppc/rs6000_mc.c +# rs6000_mc.c rs6000mc_id_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" rs6000mc_presence_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" rs6000mc_size_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" rs6000mc_size_write(uint32_t addr, uint32_t val) "write addr=0x%x val=0x%x" rs6000mc_parity_read(uint32_t addr, uint32_t val) "read addr=0x%x val=0x%x" -# hw/ppc/ppc4xx_pci.c +# ppc4xx_pci.c ppc4xx_pci_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" ppc4xx_pci_set_irq(int irq_num) "PCI irq %d" -# hw/ppc/ppc440_pcix.c +# ppc440_pcix.c ppc440_pcix_map_irq(int32_t devfn, int irq_num, int slot) "devfn 0x%x irq %d -> %d" ppc440_pcix_set_irq(int irq_num) "PCI irq %d" ppc440_pcix_update_pim(int idx, uint64_t size, uint64_t la) "Added window %d of size=0x%" PRIx64 " to CPU=0x%" PRIx64 ppc440_pcix_update_pom(int idx, uint32_t size, uint64_t la, uint64_t pcia) "Added window %d of size=0x%x from CPU=0x%" PRIx64 " to PCI=0x%" PRIx64 ppc440_pcix_reg_read(uint64_t addr, uint32_t val) "addr 0x%" PRIx64 " = 0x%" PRIx32 -ppc440_pcix_reg_write(uint64_t addr, uint64_t val) "addr 0x%" PRIx64 " = 0x%" PRIx64 diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 7891464cd9..0e4c7409e0 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -31,7 +31,6 @@ #include "hw/block/flash.h" #include "sysemu/sysemu.h" #include "sysemu/qtest.h" -#include "hw/devices.h" #include "hw/boards.h" #include "sysemu/device_tree.h" #include "hw/loader.h" @@ -105,7 +104,7 @@ static PowerPCCPU *ppc440_init_xilinx(ram_addr_t *ram_size, ppc_dcr_init(env, NULL, NULL); /* interrupt controller */ - irqs = g_malloc0(sizeof(qemu_irq) * PPCUIC_OUTPUT_NB); + irqs = g_new0(qemu_irq, PPCUIC_OUTPUT_NB); irqs[PPCUIC_OUTPUT_INT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_INT]; irqs[PPCUIC_OUTPUT_CINT] = ((qemu_irq *)env->irq_inputs)[PPC40x_INPUT_CINT]; ppcuic_init(env, irqs, 0x0C0, 0, 1); @@ -211,13 +210,6 @@ static void virtex_init(MachineState *machine) int kernel_size; int i; -#ifdef TARGET_PPCEMB - if (!qtest_enabled()) { - warn_report("qemu-system-ppcemb is deprecated, " - "please use qemu-system-ppc instead."); - } -#endif - /* init CPUs */ cpu = ppc440_init_xilinx(&ram_size, 1, machine->cpu_type, 400000000); env = &cpu->env; @@ -234,10 +226,9 @@ static void virtex_init(MachineState *machine) memory_region_add_subregion(address_space_mem, ram_base, phys_ram); dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi01_register(PFLASH_BASEADDR, NULL, "virtex.flash", FLASH_SIZE, + pflash_cfi01_register(PFLASH_BASEADDR, "virtex.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 64 * KiB, FLASH_SIZE >> 16, - 1, 0x89, 0x18, 0x0000, 0x0, 1); + 64 * KiB, 1, 0x89, 0x18, 0x0000, 0x0, 1); cpu_irq = (qemu_irq *) &env->irq_inputs[PPC40x_INPUT_INT]; dev = qdev_create(NULL, "xlnx.xps-intc"); @@ -265,7 +256,7 @@ static void virtex_init(MachineState *machine) hwaddr boot_offset; /* Boots a kernel elf binary. */ - kernel_size = load_elf(kernel_filename, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, &entry, &low, &high, 1, PPC_ELF_MACHINE, 0, 0); boot_info.bootstrap_pc = entry & 0x00ffffff; diff --git a/hw/rdma/Kconfig b/hw/rdma/Kconfig new file mode 100644 index 0000000000..8e2211288f --- /dev/null +++ b/hw/rdma/Kconfig @@ -0,0 +1,3 @@ +config VMW_PVRDMA + default y if PCI_DEVICES + depends on PVRDMA && PCI && MSI_NONBROKEN diff --git a/hw/rdma/Makefile.objs b/hw/rdma/Makefile.objs index 3504c39d21..819bb12a35 100644 --- a/hw/rdma/Makefile.objs +++ b/hw/rdma/Makefile.objs @@ -1,5 +1,3 @@ -ifeq ($(CONFIG_RDMA),y) -obj-$(CONFIG_PCI) += rdma_utils.o rdma_backend.o rdma_rm.o -obj-$(CONFIG_PCI) += vmw/pvrdma_dev_ring.o vmw/pvrdma_cmd.o \ +obj-$(CONFIG_VMW_PVRDMA) += rdma_utils.o rdma_backend.o rdma_rm.o rdma.o +obj-$(CONFIG_VMW_PVRDMA) += vmw/pvrdma_dev_ring.o vmw/pvrdma_cmd.o \ vmw/pvrdma_qp_ops.o vmw/pvrdma_main.o -endif diff --git a/hw/rdma/rdma.c b/hw/rdma/rdma.c new file mode 100644 index 0000000000..7bec0d0d2c --- /dev/null +++ b/hw/rdma/rdma.c @@ -0,0 +1,30 @@ +/* + * RDMA device interface + * + * Copyright (C) 2018 Oracle + * Copyright (C) 2018 Red Hat Inc + * + * Authors: + * Yuval Shaia + * + * 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/rdma/rdma.h" +#include "qemu/module.h" + +static const TypeInfo rdma_hmp_info = { + .name = INTERFACE_RDMA_PROVIDER, + .parent = TYPE_INTERFACE, + .class_size = sizeof(RdmaProviderClass), +}; + +static void rdma_register_types(void) +{ + type_register_static(&rdma_hmp_info); +} + +type_init(rdma_register_types) diff --git a/hw/rdma/rdma_backend.c b/hw/rdma/rdma_backend.c index e9ced6f9ef..d1660b6474 100644 --- a/hw/rdma/rdma_backend.c +++ b/hw/rdma/rdma_backend.c @@ -14,75 +14,125 @@ */ #include "qemu/osdep.h" -#include "qemu/error-report.h" +#include "sysemu/sysemu.h" #include "qapi/error.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qapi-events-rdma.h" #include +#include +#include +#include +#include "contrib/rdmacm-mux/rdmacm-mux.h" #include "trace.h" #include "rdma_utils.h" #include "rdma_rm.h" #include "rdma_backend.h" -/* Vendor Errors */ -#define VENDOR_ERR_FAIL_BACKEND 0x201 -#define VENDOR_ERR_TOO_MANY_SGES 0x202 -#define VENDOR_ERR_NOMEM 0x203 -#define VENDOR_ERR_QP0 0x204 -#define VENDOR_ERR_NO_SGE 0x205 -#define VENDOR_ERR_MAD_SEND 0x206 -#define VENDOR_ERR_INVLKEY 0x207 -#define VENDOR_ERR_MR_SMALL 0x208 - #define THR_NAME_LEN 16 +#define THR_POLL_TO 5000 + +#define MAD_HDR_SIZE sizeof(struct ibv_grh) typedef struct BackendCtx { - uint64_t req_id; void *up_ctx; - bool is_tx_req; + struct ibv_sge sge; /* Used to save MAD recv buffer */ + RdmaBackendQP *backend_qp; /* To maintain recv buffers */ } BackendCtx; -static void (*comp_handler)(int status, unsigned int vendor_err, void *ctx); +struct backend_umad { + struct ib_user_mad hdr; + char mad[RDMA_MAX_PRIVATE_DATA]; +}; -static void dummy_comp_handler(int status, unsigned int vendor_err, void *ctx) +static void (*comp_handler)(void *ctx, struct ibv_wc *wc); + +static void dummy_comp_handler(void *ctx, struct ibv_wc *wc) { - pr_err("No completion handler is registered\n"); + rdma_error_report("No completion handler is registered"); } -static void poll_cq(RdmaDeviceResources *rdma_dev_res, struct ibv_cq *ibcq) +static inline void complete_work(enum ibv_wc_status status, uint32_t vendor_err, + void *ctx) { - int i, ne; + struct ibv_wc wc = {}; + + wc.status = status; + wc.vendor_err = vendor_err; + + comp_handler(ctx, &wc); +} + +static void free_cqe_ctx(gpointer data, gpointer user_data) +{ + BackendCtx *bctx; + RdmaDeviceResources *rdma_dev_res = user_data; + unsigned long cqe_ctx_id = GPOINTER_TO_INT(data); + + bctx = rdma_rm_get_cqe_ctx(rdma_dev_res, cqe_ctx_id); + if (bctx) { + rdma_rm_dealloc_cqe_ctx(rdma_dev_res, cqe_ctx_id); + atomic_dec(&rdma_dev_res->stats.missing_cqe); + } + g_free(bctx); +} + +static void clean_recv_mads(RdmaBackendDev *backend_dev) +{ + unsigned long cqe_ctx_id; + + do { + cqe_ctx_id = rdma_protected_qlist_pop_int64(&backend_dev-> + recv_mads_list); + if (cqe_ctx_id != -ENOENT) { + atomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); + free_cqe_ctx(GINT_TO_POINTER(cqe_ctx_id), + backend_dev->rdma_dev_res); + } + } while (cqe_ctx_id != -ENOENT); +} + +static int rdma_poll_cq(RdmaDeviceResources *rdma_dev_res, struct ibv_cq *ibcq) +{ + int i, ne, total_ne = 0; BackendCtx *bctx; struct ibv_wc wc[2]; - pr_dbg("Entering poll_cq loop on cq %p\n", ibcq); + qemu_mutex_lock(&rdma_dev_res->lock); do { ne = ibv_poll_cq(ibcq, ARRAY_SIZE(wc), wc); - pr_dbg("Got %d completion(s) from cq %p\n", ne, ibcq); + trace_rdma_poll_cq(ne, ibcq); for (i = 0; i < ne; i++) { - pr_dbg("wr_id=0x%" PRIx64 "\n", wc[i].wr_id); - pr_dbg("status=%d\n", wc[i].status); - bctx = rdma_rm_get_cqe_ctx(rdma_dev_res, wc[i].wr_id); if (unlikely(!bctx)) { - pr_dbg("Error: Failed to find ctx for req %" PRId64 "\n", - wc[i].wr_id); + rdma_error_report("No matching ctx for req %"PRId64, + wc[i].wr_id); continue; } - pr_dbg("Processing %s CQE\n", bctx->is_tx_req ? "send" : "recv"); - comp_handler(wc[i].status, wc[i].vendor_err, bctx->up_ctx); + comp_handler(bctx->up_ctx, &wc[i]); + rdma_protected_gslist_remove_int32(&bctx->backend_qp->cqe_ctx_list, + wc[i].wr_id); rdma_rm_dealloc_cqe_ctx(rdma_dev_res, wc[i].wr_id); g_free(bctx); } + total_ne += ne; } while (ne > 0); + atomic_sub(&rdma_dev_res->stats.missing_cqe, total_ne); + qemu_mutex_unlock(&rdma_dev_res->lock); if (ne < 0) { - pr_dbg("Got error %d from ibv_poll_cq\n", ne); + rdma_error_report("ibv_poll_cq fail, rc=%d, errno=%d", ne, errno); } + + rdma_dev_res->stats.completions += total_ne; + + return total_ne; } static void *comp_handler_thread(void *arg) @@ -91,37 +141,151 @@ static void *comp_handler_thread(void *arg) int rc; struct ibv_cq *ev_cq; void *ev_ctx; + int flags; + GPollFD pfds[1]; - pr_dbg("Starting\n"); - - while (backend_dev->comp_thread.run) { - pr_dbg("Waiting for completion on channel %p\n", backend_dev->channel); - rc = ibv_get_cq_event(backend_dev->channel, &ev_cq, &ev_ctx); - pr_dbg("ibv_get_cq_event=%d\n", rc); - if (unlikely(rc)) { - pr_dbg("---> ibv_get_cq_event (%d)\n", rc); - continue; - } - - rc = ibv_req_notify_cq(ev_cq, 0); - if (unlikely(rc)) { - pr_dbg("Error %d from ibv_req_notify_cq\n", rc); - } - - poll_cq(backend_dev->rdma_dev_res, ev_cq); - - ibv_ack_cq_events(ev_cq, 1); + /* Change to non-blocking mode */ + flags = fcntl(backend_dev->channel->fd, F_GETFL); + rc = fcntl(backend_dev->channel->fd, F_SETFL, flags | O_NONBLOCK); + if (rc < 0) { + rdma_error_report("Failed to change backend channel FD to non-blocking"); + return NULL; } - pr_dbg("Going down\n"); + pfds[0].fd = backend_dev->channel->fd; + pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; - /* TODO: Post cqe for all remaining buffs that were posted */ + backend_dev->comp_thread.is_running = true; + + while (backend_dev->comp_thread.run) { + do { + rc = qemu_poll_ns(pfds, 1, THR_POLL_TO * (int64_t)SCALE_MS); + if (!rc) { + backend_dev->rdma_dev_res->stats.poll_cq_ppoll_to++; + } + } while (!rc && backend_dev->comp_thread.run); + + if (backend_dev->comp_thread.run) { + rc = ibv_get_cq_event(backend_dev->channel, &ev_cq, &ev_ctx); + if (unlikely(rc)) { + rdma_error_report("ibv_get_cq_event fail, rc=%d, errno=%d", rc, + errno); + continue; + } + + rc = ibv_req_notify_cq(ev_cq, 0); + if (unlikely(rc)) { + rdma_error_report("ibv_req_notify_cq fail, rc=%d, errno=%d", rc, + errno); + } + + backend_dev->rdma_dev_res->stats.poll_cq_from_bk++; + rdma_poll_cq(backend_dev->rdma_dev_res, ev_cq); + + ibv_ack_cq_events(ev_cq, 1); + } + } + + backend_dev->comp_thread.is_running = false; + + qemu_thread_exit(0); return NULL; } -void rdma_backend_register_comp_handler(void (*handler)(int status, - unsigned int vendor_err, void *ctx)) +static inline void disable_rdmacm_mux_async(RdmaBackendDev *backend_dev) +{ + atomic_set(&backend_dev->rdmacm_mux.can_receive, 0); +} + +static inline void enable_rdmacm_mux_async(RdmaBackendDev *backend_dev) +{ + atomic_set(&backend_dev->rdmacm_mux.can_receive, sizeof(RdmaCmMuxMsg)); +} + +static inline int rdmacm_mux_can_process_async(RdmaBackendDev *backend_dev) +{ + return atomic_read(&backend_dev->rdmacm_mux.can_receive); +} + +static int rdmacm_mux_check_op_status(CharBackend *mad_chr_be) +{ + RdmaCmMuxMsg msg = {}; + int ret; + + ret = qemu_chr_fe_read_all(mad_chr_be, (uint8_t *)&msg, sizeof(msg)); + if (ret != sizeof(msg)) { + rdma_error_report("Got invalid message from mux: size %d, expecting %d", + ret, (int)sizeof(msg)); + return -EIO; + } + + trace_rdmacm_mux_check_op_status(msg.hdr.msg_type, msg.hdr.op_code, + msg.hdr.err_code); + + if (msg.hdr.msg_type != RDMACM_MUX_MSG_TYPE_RESP) { + rdma_error_report("Got invalid message type %d", msg.hdr.msg_type); + return -EIO; + } + + if (msg.hdr.err_code != RDMACM_MUX_ERR_CODE_OK) { + rdma_error_report("Operation failed in mux, error code %d", + msg.hdr.err_code); + return -EIO; + } + + return 0; +} + +static int rdmacm_mux_send(RdmaBackendDev *backend_dev, RdmaCmMuxMsg *msg) +{ + int rc = 0; + + msg->hdr.msg_type = RDMACM_MUX_MSG_TYPE_REQ; + trace_rdmacm_mux("send", msg->hdr.msg_type, msg->hdr.op_code); + disable_rdmacm_mux_async(backend_dev); + rc = qemu_chr_fe_write(backend_dev->rdmacm_mux.chr_be, + (const uint8_t *)msg, sizeof(*msg)); + if (rc != sizeof(*msg)) { + enable_rdmacm_mux_async(backend_dev); + rdma_error_report("Failed to send request to rdmacm_mux (rc=%d)", rc); + return -EIO; + } + + rc = rdmacm_mux_check_op_status(backend_dev->rdmacm_mux.chr_be); + if (rc) { + rdma_error_report("Failed to execute rdmacm_mux request %d (rc=%d)", + msg->hdr.op_code, rc); + } + + enable_rdmacm_mux_async(backend_dev); + + return 0; +} + +static void stop_backend_thread(RdmaBackendThread *thread) +{ + thread->run = false; + while (thread->is_running) { + sleep(THR_POLL_TO / SCALE_US / 2); + } +} + +static void start_comp_thread(RdmaBackendDev *backend_dev) +{ + char thread_name[THR_NAME_LEN] = {}; + + stop_backend_thread(&backend_dev->comp_thread); + + snprintf(thread_name, sizeof(thread_name), "rdma_comp_%s", + ibv_get_device_name(backend_dev->ib_dev)); + backend_dev->comp_thread.run = true; + qemu_thread_create(&backend_dev->comp_thread.thread, thread_name, + comp_handler_thread, backend_dev, QEMU_THREAD_DETACHED); +} + +void rdma_backend_register_comp_handler(void (*handler)(void *ctx, + struct ibv_wc *wc)) { comp_handler = handler; } @@ -138,7 +302,7 @@ int rdma_backend_query_port(RdmaBackendDev *backend_dev, rc = ibv_query_port(backend_dev->context, backend_dev->port_num, port_attr); if (rc) { - pr_dbg("Error %d from ibv_query_port\n", rc); + rdma_error_report("ibv_query_port fail, rc=%d, errno=%d", rc, errno); return -EIO; } @@ -147,7 +311,13 @@ int rdma_backend_query_port(RdmaBackendDev *backend_dev, void rdma_backend_poll_cq(RdmaDeviceResources *rdma_dev_res, RdmaBackendCQ *cq) { - poll_cq(rdma_dev_res, cq->ibcq); + int polled; + + rdma_dev_res->stats.poll_cq_from_guest++; + polled = rdma_poll_cq(rdma_dev_res, cq->ibcq); + if (!polled) { + rdma_dev_res->stats.poll_cq_from_guest_empty++; + } } static GHashTable *ah_hash; @@ -159,8 +329,8 @@ static struct ibv_ah *create_ah(RdmaBackendDev *backend_dev, struct ibv_pd *pd, struct ibv_ah *ah = g_hash_table_lookup(ah_hash, ah_key); if (ah) { - trace_create_ah_cache_hit(be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); + trace_rdma_create_ah_cache_hit(be64_to_cpu(dgid->global.subnet_prefix), + be64_to_cpu(dgid->global.interface_id)); g_bytes_unref(ah_key); } else { struct ibv_ah_attr ah_attr = { @@ -177,13 +347,13 @@ static struct ibv_ah *create_ah(RdmaBackendDev *backend_dev, struct ibv_pd *pd, g_hash_table_insert(ah_hash, ah_key, ah); } else { g_bytes_unref(ah_key); - pr_dbg("Fail to create AH for gid <0x%" PRIx64 ", 0x%" PRIx64 ">\n", - be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); + rdma_error_report("Failed to create AH for gid <0x%" PRIx64", 0x%"PRIx64">", + be64_to_cpu(dgid->global.subnet_prefix), + be64_to_cpu(dgid->global.interface_id)); } - trace_create_ah_cache_miss(be64_to_cpu(dgid->global.subnet_prefix), - be64_to_cpu(dgid->global.interface_id)); + trace_rdma_create_ah_cache_miss(be64_to_cpu(dgid->global.subnet_prefix), + be64_to_cpu(dgid->global.interface_id)); } return ah; @@ -209,29 +379,23 @@ static void ah_cache_init(void) static int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, struct ibv_sge *dsge, struct ibv_sge *ssge, - uint8_t num_sge) + uint8_t num_sge, uint64_t *total_length) { RdmaRmMR *mr; int ssge_idx; - pr_dbg("num_sge=%d\n", num_sge); - for (ssge_idx = 0; ssge_idx < num_sge; ssge_idx++) { mr = rdma_rm_get_mr(rdma_dev_res, ssge[ssge_idx].lkey); if (unlikely(!mr)) { - pr_dbg("Invalid lkey 0x%x\n", ssge[ssge_idx].lkey); + rdma_error_report("Invalid lkey 0x%x", ssge[ssge_idx].lkey); return VENDOR_ERR_INVLKEY | ssge[ssge_idx].lkey; } - dsge->addr = (uintptr_t)mr->user_mr.host_virt + ssge[ssge_idx].addr - - mr->user_mr.guest_start; + dsge->addr = (uintptr_t)mr->virt + ssge[ssge_idx].addr - mr->start; dsge->length = ssge[ssge_idx].length; dsge->lkey = rdma_backend_mr_lkey(&mr->backend_mr); - pr_dbg("ssge->addr=0x%" PRIx64 "\n", ssge[ssge_idx].addr); - pr_dbg("dsge->addr=0x%" PRIx64 "\n", dsge->addr); - pr_dbg("dsge->length=%d\n", dsge->length); - pr_dbg("dsge->lkey=0x%x\n", dsge->lkey); + *total_length += dsge->length; dsge++; } @@ -239,58 +403,130 @@ static int build_host_sge_array(RdmaDeviceResources *rdma_dev_res, return 0; } +static void trace_mad_message(const char *title, char *buf, int len) +{ + int i; + char *b = g_malloc0(len * 3 + 1); + char b1[4]; + + for (i = 0; i < len; i++) { + sprintf(b1, "%.2X ", buf[i] & 0x000000FF); + strcat(b, b1); + } + + trace_rdma_mad_message(title, len, b); + + g_free(b); +} + +static int mad_send(RdmaBackendDev *backend_dev, uint8_t sgid_idx, + union ibv_gid *sgid, struct ibv_sge *sge, uint32_t num_sge) +{ + RdmaCmMuxMsg msg = {}; + char *hdr, *data; + int ret; + + if (num_sge != 2) { + return -EINVAL; + } + + msg.hdr.op_code = RDMACM_MUX_OP_CODE_MAD; + memcpy(msg.hdr.sgid.raw, sgid->raw, sizeof(msg.hdr.sgid)); + + msg.umad_len = sge[0].length + sge[1].length; + + if (msg.umad_len > sizeof(msg.umad.mad)) { + return -ENOMEM; + } + + msg.umad.hdr.addr.qpn = htobe32(1); + msg.umad.hdr.addr.grh_present = 1; + msg.umad.hdr.addr.gid_index = sgid_idx; + memcpy(msg.umad.hdr.addr.gid, sgid->raw, sizeof(msg.umad.hdr.addr.gid)); + msg.umad.hdr.addr.hop_limit = 0xFF; + + hdr = rdma_pci_dma_map(backend_dev->dev, sge[0].addr, sge[0].length); + if (!hdr) { + return -ENOMEM; + } + data = rdma_pci_dma_map(backend_dev->dev, sge[1].addr, sge[1].length); + if (!data) { + rdma_pci_dma_unmap(backend_dev->dev, hdr, sge[0].length); + return -ENOMEM; + } + + memcpy(&msg.umad.mad[0], hdr, sge[0].length); + memcpy(&msg.umad.mad[sge[0].length], data, sge[1].length); + + rdma_pci_dma_unmap(backend_dev->dev, data, sge[1].length); + rdma_pci_dma_unmap(backend_dev->dev, hdr, sge[0].length); + + trace_mad_message("send", msg.umad.mad, msg.umad_len); + + ret = rdmacm_mux_send(backend_dev, &msg); + if (ret) { + rdma_error_report("Failed to send MAD to rdma_umadmux (%d)", ret); + return -EIO; + } + + return 0; +} + void rdma_backend_post_send(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, uint8_t qp_type, struct ibv_sge *sge, uint32_t num_sge, - union ibv_gid *dgid, uint32_t dqpn, - uint32_t dqkey, void *ctx) + uint8_t sgid_idx, union ibv_gid *sgid, + union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey, + void *ctx) { BackendCtx *bctx; struct ibv_sge new_sge[MAX_SGE]; uint32_t bctx_id; int rc; - struct ibv_send_wr wr = {0}, *bad_wr; + struct ibv_send_wr wr = {}, *bad_wr; - if (!qp->ibqp) { /* This field does not get initialized for QP0 and QP1 */ + if (!qp->ibqp) { /* This field is not initialized for QP0 and QP1 */ if (qp_type == IBV_QPT_SMI) { - pr_dbg("QP0 unsupported\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); + rdma_error_report("Got QP0 request"); + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); } else if (qp_type == IBV_QPT_GSI) { - pr_dbg("QP1\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_MAD_SEND, ctx); + rc = mad_send(backend_dev, sgid_idx, sgid, sge, num_sge); + if (rc) { + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_MAD_SEND, ctx); + backend_dev->rdma_dev_res->stats.mad_tx_err++; + } else { + complete_work(IBV_WC_SUCCESS, 0, ctx); + backend_dev->rdma_dev_res->stats.mad_tx++; + } } - pr_dbg("qp->ibqp is NULL for qp_type %d!!!\n", qp_type); - return; - } - - pr_dbg("num_sge=%d\n", num_sge); - if (!num_sge) { - pr_dbg("num_sge=0\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_NO_SGE, ctx); return; } bctx = g_malloc0(sizeof(*bctx)); bctx->up_ctx = ctx; - bctx->is_tx_req = 1; + bctx->backend_qp = qp; rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); if (unlikely(rc)) { - pr_dbg("Failed to allocate cqe_ctx\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto out_free_bctx; + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); + goto err_free_bctx; } - rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge); + rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); + + rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge, + &backend_dev->rdma_dev_res->stats.tx_len); if (rc) { - pr_dbg("Error: Failed to build host SGE array\n"); - comp_handler(IBV_WC_GENERAL_ERR, rc, ctx); - goto out_dealloc_cqe_ctx; + complete_work(IBV_WC_GENERAL_ERR, rc, ctx); + goto err_dealloc_cqe_ctx; } if (qp_type == IBV_QPT_UD) { - wr.wr.ud.ah = create_ah(backend_dev, qp->ibpd, - backend_dev->backend_gid_idx, dgid); + wr.wr.ud.ah = create_ah(backend_dev, qp->ibpd, sgid_idx, dgid); + if (!wr.wr.ud.ah) { + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); + goto err_dealloc_cqe_ctx; + } wr.wr.ud.remote_qpn = dqpn; wr.wr.ud.remote_qkey = dqkey; } @@ -302,25 +538,61 @@ void rdma_backend_post_send(RdmaBackendDev *backend_dev, wr.wr_id = bctx_id; rc = ibv_post_send(qp->ibqp, &wr, &bad_wr); - pr_dbg("ibv_post_send=%d\n", rc); if (rc) { - pr_dbg("Fail (%d, %d) to post send WQE to qpn %d\n", rc, errno, - qp->ibqp->qp_num); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto out_dealloc_cqe_ctx; + rdma_error_report("ibv_post_send fail, qpn=0x%x, rc=%d, errno=%d", + qp->ibqp->qp_num, rc, errno); + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); + goto err_dealloc_cqe_ctx; } + atomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); + backend_dev->rdma_dev_res->stats.tx++; + return; -out_dealloc_cqe_ctx: +err_dealloc_cqe_ctx: + backend_dev->rdma_dev_res->stats.tx_err++; rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); -out_free_bctx: +err_free_bctx: g_free(bctx); } +static unsigned int save_mad_recv_buffer(RdmaBackendDev *backend_dev, + struct ibv_sge *sge, uint32_t num_sge, + void *ctx) +{ + BackendCtx *bctx; + int rc; + uint32_t bctx_id; + + if (num_sge != 1) { + rdma_error_report("Invalid num_sge (%d), expecting 1", num_sge); + return VENDOR_ERR_INV_NUM_SGE; + } + + if (sge[0].length < RDMA_MAX_PRIVATE_DATA + sizeof(struct ibv_grh)) { + rdma_error_report("Too small buffer for MAD"); + return VENDOR_ERR_INV_MAD_BUFF; + } + + bctx = g_malloc0(sizeof(*bctx)); + + rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); + if (unlikely(rc)) { + g_free(bctx); + return VENDOR_ERR_NOMEM; + } + + bctx->up_ctx = ctx; + bctx->sge = *sge; + + rdma_protected_qlist_append_int64(&backend_dev->recv_mads_list, bctx_id); + + return 0; +} + void rdma_backend_post_recv(RdmaBackendDev *backend_dev, - RdmaDeviceResources *rdma_dev_res, RdmaBackendQP *qp, uint8_t qp_type, struct ibv_sge *sge, uint32_t num_sge, void *ctx) { @@ -328,63 +600,65 @@ void rdma_backend_post_recv(RdmaBackendDev *backend_dev, struct ibv_sge new_sge[MAX_SGE]; uint32_t bctx_id; int rc; - struct ibv_recv_wr wr = {0}, *bad_wr; + struct ibv_recv_wr wr = {}, *bad_wr; if (!qp->ibqp) { /* This field does not get initialized for QP0 and QP1 */ if (qp_type == IBV_QPT_SMI) { - pr_dbg("QP0 unsupported\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); + rdma_error_report("Got QP0 request"); + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_QP0, ctx); } if (qp_type == IBV_QPT_GSI) { - pr_dbg("QP1\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_MAD_SEND, ctx); + rc = save_mad_recv_buffer(backend_dev, sge, num_sge, ctx); + if (rc) { + complete_work(IBV_WC_GENERAL_ERR, rc, ctx); + backend_dev->rdma_dev_res->stats.mad_rx_bufs_err++; + } else { + backend_dev->rdma_dev_res->stats.mad_rx_bufs++; + } } return; } - pr_dbg("num_sge=%d\n", num_sge); - if (!num_sge) { - pr_dbg("num_sge=0\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_NO_SGE, ctx); - return; - } - bctx = g_malloc0(sizeof(*bctx)); bctx->up_ctx = ctx; - bctx->is_tx_req = 0; + bctx->backend_qp = qp; - rc = rdma_rm_alloc_cqe_ctx(rdma_dev_res, &bctx_id, bctx); + rc = rdma_rm_alloc_cqe_ctx(backend_dev->rdma_dev_res, &bctx_id, bctx); if (unlikely(rc)) { - pr_dbg("Failed to allocate cqe_ctx\n"); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); - goto out_free_bctx; + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_NOMEM, ctx); + goto err_free_bctx; } - rc = build_host_sge_array(rdma_dev_res, new_sge, sge, num_sge); + rdma_protected_gslist_append_int32(&qp->cqe_ctx_list, bctx_id); + + rc = build_host_sge_array(backend_dev->rdma_dev_res, new_sge, sge, num_sge, + &backend_dev->rdma_dev_res->stats.rx_bufs_len); if (rc) { - pr_dbg("Error: Failed to build host SGE array\n"); - comp_handler(IBV_WC_GENERAL_ERR, rc, ctx); - goto out_dealloc_cqe_ctx; + complete_work(IBV_WC_GENERAL_ERR, rc, ctx); + goto err_dealloc_cqe_ctx; } wr.num_sge = num_sge; wr.sg_list = new_sge; wr.wr_id = bctx_id; rc = ibv_post_recv(qp->ibqp, &wr, &bad_wr); - pr_dbg("ibv_post_recv=%d\n", rc); if (rc) { - pr_dbg("Fail (%d, %d) to post recv WQE to qpn %d\n", rc, errno, - qp->ibqp->qp_num); - comp_handler(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); - goto out_dealloc_cqe_ctx; + rdma_error_report("ibv_post_recv fail, qpn=0x%x, rc=%d, errno=%d", + qp->ibqp->qp_num, rc, errno); + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_FAIL_BACKEND, ctx); + goto err_dealloc_cqe_ctx; } + atomic_inc(&backend_dev->rdma_dev_res->stats.missing_cqe); + backend_dev->rdma_dev_res->stats.rx_bufs++; + return; -out_dealloc_cqe_ctx: - rdma_rm_dealloc_cqe_ctx(rdma_dev_res, bctx_id); +err_dealloc_cqe_ctx: + backend_dev->rdma_dev_res->stats.rx_bufs_err++; + rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, bctx_id); -out_free_bctx: +err_free_bctx: g_free(bctx); } @@ -392,7 +666,12 @@ int rdma_backend_create_pd(RdmaBackendDev *backend_dev, RdmaBackendPD *pd) { pd->ibpd = ibv_alloc_pd(backend_dev->context); - return pd->ibpd ? 0 : -EIO; + if (!pd->ibpd) { + rdma_error_report("ibv_alloc_pd fail, errno=%d", errno); + return -EIO; + } + + return 0; } void rdma_backend_destroy_pd(RdmaBackendPD *pd) @@ -405,16 +684,15 @@ void rdma_backend_destroy_pd(RdmaBackendPD *pd) int rdma_backend_create_mr(RdmaBackendMR *mr, RdmaBackendPD *pd, void *addr, size_t length, int access) { - pr_dbg("addr=0x%p\n", addr); - pr_dbg("len=%zu\n", length); mr->ibmr = ibv_reg_mr(pd->ibpd, addr, length, access); - if (mr->ibmr) { - pr_dbg("lkey=0x%x\n", mr->ibmr->lkey); - pr_dbg("rkey=0x%x\n", mr->ibmr->rkey); - mr->ibpd = pd->ibpd; + if (!mr->ibmr) { + rdma_error_report("ibv_reg_mr fail, errno=%d", errno); + return -EIO; } - return mr->ibmr ? 0 : -EIO; + mr->ibpd = pd->ibpd; + + return 0; } void rdma_backend_destroy_mr(RdmaBackendMR *mr) @@ -429,21 +707,21 @@ int rdma_backend_create_cq(RdmaBackendDev *backend_dev, RdmaBackendCQ *cq, { int rc; - pr_dbg("cqe=%d\n", cqe); - - pr_dbg("dev->channel=%p\n", backend_dev->channel); cq->ibcq = ibv_create_cq(backend_dev->context, cqe + 1, NULL, backend_dev->channel, 0); - - if (cq->ibcq) { - rc = ibv_req_notify_cq(cq->ibcq, 0); - if (rc) { - pr_dbg("Error %d from ibv_req_notify_cq\n", rc); - } - cq->backend_dev = backend_dev; + if (!cq->ibcq) { + rdma_error_report("ibv_create_cq fail, errno=%d", errno); + return -EIO; } - return cq->ibcq ? 0 : -EIO; + rc = ibv_req_notify_cq(cq->ibcq, 0); + if (rc) { + rdma_warn_report("ibv_req_notify_cq fail, rc=%d, errno=%d", rc, errno); + } + + cq->backend_dev = backend_dev; + + return 0; } void rdma_backend_destroy_cq(RdmaBackendCQ *cq) @@ -459,14 +737,12 @@ int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, uint32_t max_recv_wr, uint32_t max_send_sge, uint32_t max_recv_sge) { - struct ibv_qp_init_attr attr = {0}; + struct ibv_qp_init_attr attr = {}; qp->ibqp = 0; - pr_dbg("qp_type=%d\n", qp_type); switch (qp_type) { case IBV_QPT_GSI: - pr_dbg("QP1 unsupported\n"); return 0; case IBV_QPT_RC: @@ -476,7 +752,7 @@ int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, break; default: - pr_dbg("Unsupported QP type %d\n", qp_type); + rdma_error_report("Unsupported QP type %d", qp_type); return -EIO; } @@ -488,35 +764,27 @@ int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, attr.cap.max_send_sge = max_send_sge; attr.cap.max_recv_sge = max_recv_sge; - pr_dbg("max_send_wr=%d\n", max_send_wr); - pr_dbg("max_recv_wr=%d\n", max_recv_wr); - pr_dbg("max_send_sge=%d\n", max_send_sge); - pr_dbg("max_recv_sge=%d\n", max_recv_sge); - qp->ibqp = ibv_create_qp(pd->ibpd, &attr); - if (likely(!qp->ibqp)) { - pr_dbg("Error from ibv_create_qp\n"); + if (!qp->ibqp) { + rdma_error_report("ibv_create_qp fail, errno=%d", errno); return -EIO; } + rdma_protected_gslist_init(&qp->cqe_ctx_list); + qp->ibpd = pd->ibpd; /* TODO: Query QP to get max_inline_data and save it to be used in send */ - pr_dbg("qpn=0x%x\n", qp->ibqp->qp_num); - return 0; } int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, uint8_t qp_type, uint32_t qkey) { - struct ibv_qp_attr attr = {0}; + struct ibv_qp_attr attr = {}; int rc, attr_mask; - pr_dbg("qpn=0x%x\n", qp->ibqp->qp_num); - pr_dbg("sport_num=%d\n", backend_dev->port_num); - attr_mask = IBV_QP_STATE | IBV_QP_PKEY_INDEX | IBV_QP_PORT; attr.qp_state = IBV_QPS_INIT; attr.pkey_index = 0; @@ -525,21 +793,23 @@ int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, switch (qp_type) { case IBV_QPT_RC: attr_mask |= IBV_QP_ACCESS_FLAGS; + trace_rdma_backend_rc_qp_state_init(qp->ibqp->qp_num); break; case IBV_QPT_UD: attr.qkey = qkey; attr_mask |= IBV_QP_QKEY; + trace_rdma_backend_ud_qp_state_init(qp->ibqp->qp_num, qkey); break; default: - pr_dbg("Unsupported QP type %d\n", qp_type); + rdma_error_report("Unsupported QP type %d", qp_type); return -EIO; } rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); if (rc) { - pr_dbg("Error %d from ibv_modify_qp\n", rc); + rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); return -EIO; } @@ -547,11 +817,11 @@ int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, } int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, union ibv_gid *dgid, - uint32_t dqpn, uint32_t rq_psn, uint32_t qkey, - bool use_qkey) + uint8_t qp_type, uint8_t sgid_idx, + union ibv_gid *dgid, uint32_t dqpn, + uint32_t rq_psn, uint32_t qkey, bool use_qkey) { - struct ibv_qp_attr attr = {0}; + struct ibv_qp_attr attr = {}; union ibv_gid ibv_gid = { .global.interface_id = dgid->global.interface_id, .global.subnet_prefix = dgid->global.subnet_prefix @@ -561,16 +831,10 @@ int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, attr.qp_state = IBV_QPS_RTR; attr_mask = IBV_QP_STATE; + qp->sgid_idx = sgid_idx; + switch (qp_type) { case IBV_QPT_RC: - pr_dbg("dgid=0x%" PRIx64 ",%" PRIx64 "\n", - be64_to_cpu(ibv_gid.global.subnet_prefix), - be64_to_cpu(ibv_gid.global.interface_id)); - pr_dbg("dqpn=0x%x\n", dqpn); - pr_dbg("sgid_idx=%d\n", backend_dev->backend_gid_idx); - pr_dbg("sport_num=%d\n", backend_dev->port_num); - pr_dbg("rq_psn=0x%x\n", rq_psn); - attr.path_mtu = IBV_MTU_1024; attr.dest_qp_num = dqpn; attr.max_dest_rd_atomic = 1; @@ -579,26 +843,34 @@ int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, attr.ah_attr.is_global = 1; attr.ah_attr.grh.hop_limit = 1; attr.ah_attr.grh.dgid = ibv_gid; - attr.ah_attr.grh.sgid_index = backend_dev->backend_gid_idx; + attr.ah_attr.grh.sgid_index = qp->sgid_idx; attr.rq_psn = rq_psn; attr_mask |= IBV_QP_AV | IBV_QP_PATH_MTU | IBV_QP_DEST_QPN | IBV_QP_RQ_PSN | IBV_QP_MAX_DEST_RD_ATOMIC | IBV_QP_MIN_RNR_TIMER; + + trace_rdma_backend_rc_qp_state_rtr(qp->ibqp->qp_num, + be64_to_cpu(ibv_gid.global. + subnet_prefix), + be64_to_cpu(ibv_gid.global. + interface_id), + qp->sgid_idx, dqpn, rq_psn); break; case IBV_QPT_UD: if (use_qkey) { - pr_dbg("qkey=0x%x\n", qkey); attr.qkey = qkey; attr_mask |= IBV_QP_QKEY; } + trace_rdma_backend_ud_qp_state_rtr(qp->ibqp->qp_num, use_qkey ? qkey : + 0); break; } rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); if (rc) { - pr_dbg("Error %d from ibv_modify_qp\n", rc); + rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); return -EIO; } @@ -608,12 +880,9 @@ int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, uint32_t sq_psn, uint32_t qkey, bool use_qkey) { - struct ibv_qp_attr attr = {0}; + struct ibv_qp_attr attr = {}; int rc, attr_mask; - pr_dbg("qpn=0x%x\n", qp->ibqp->qp_num); - pr_dbg("sq_psn=0x%x\n", sq_psn); - attr.qp_state = IBV_QPS_RTS; attr.sq_psn = sq_psn; attr_mask = IBV_QP_STATE | IBV_QP_SQ_PSN; @@ -627,20 +896,22 @@ int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, attr_mask |= IBV_QP_TIMEOUT | IBV_QP_RETRY_CNT | IBV_QP_RNR_RETRY | IBV_QP_MAX_QP_RD_ATOMIC; + trace_rdma_backend_rc_qp_state_rts(qp->ibqp->qp_num, sq_psn); break; case IBV_QPT_UD: if (use_qkey) { - pr_dbg("qkey=0x%x\n", qkey); attr.qkey = qkey; attr_mask |= IBV_QP_QKEY; } + trace_rdma_backend_ud_qp_state_rts(qp->ibqp->qp_num, sq_psn, + use_qkey ? qkey : 0); break; } rc = ibv_modify_qp(qp->ibqp, &attr, attr_mask); if (rc) { - pr_dbg("Error %d from ibv_modify_qp\n", rc); + rdma_error_report("ibv_modify_qp fail, rc=%d, errno=%d", rc, errno); return -EIO; } @@ -651,7 +922,6 @@ int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, int attr_mask, struct ibv_qp_init_attr *init_attr) { if (!qp->ibqp) { - pr_dbg("QP1\n"); attr->qp_state = IBV_QPS_RTS; return 0; } @@ -659,58 +929,249 @@ int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, return ibv_query_qp(qp->ibqp, attr, attr_mask, init_attr); } -void rdma_backend_destroy_qp(RdmaBackendQP *qp) +void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res) { if (qp->ibqp) { ibv_destroy_qp(qp->ibqp); } + g_slist_foreach(qp->cqe_ctx_list.list, free_cqe_ctx, dev_res); + rdma_protected_gslist_destroy(&qp->cqe_ctx_list); } #define CHK_ATTR(req, dev, member, fmt) ({ \ - pr_dbg("%s="fmt","fmt"\n", #member, dev.member, req->member); \ + trace_rdma_check_dev_attr(#member, dev.member, req->member); \ if (req->member > dev.member) { \ - warn_report("%s = "fmt" is higher than host device capability "fmt, \ - #member, req->member, dev.member); \ + rdma_warn_report("%s = "fmt" is higher than host device capability "fmt, \ + #member, req->member, dev.member); \ req->member = dev.member; \ } \ - pr_dbg("%s="fmt"\n", #member, req->member); }) +}) static int init_device_caps(RdmaBackendDev *backend_dev, struct ibv_device_attr *dev_attr) { - if (ibv_query_device(backend_dev->context, &backend_dev->dev_attr)) { + struct ibv_device_attr bk_dev_attr; + int rc; + + rc = ibv_query_device(backend_dev->context, &bk_dev_attr); + if (rc) { + rdma_error_report("ibv_query_device fail, rc=%d, errno=%d", rc, errno); return -EIO; } - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_mr_size, "%" PRId64); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_sge, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp_wr, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_cq, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_cqe, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_mr, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_pd, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp_rd_atom, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_qp_init_rd_atom, "%d"); - CHK_ATTR(dev_attr, backend_dev->dev_attr, max_ah, "%d"); + dev_attr->max_sge = MAX_SGE; + + CHK_ATTR(dev_attr, bk_dev_attr, max_mr_size, "%" PRId64); + CHK_ATTR(dev_attr, bk_dev_attr, max_qp, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_sge, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_cq, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_mr, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_pd, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_qp_rd_atom, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_qp_init_rd_atom, "%d"); + CHK_ATTR(dev_attr, bk_dev_attr, max_ah, "%d"); return 0; } -int rdma_backend_init(RdmaBackendDev *backend_dev, +static inline void build_mad_hdr(struct ibv_grh *grh, union ibv_gid *sgid, + union ibv_gid *my_gid, int paylen) +{ + grh->paylen = htons(paylen); + grh->sgid = *sgid; + grh->dgid = *my_gid; +} + +static void process_incoming_mad_req(RdmaBackendDev *backend_dev, + RdmaCmMuxMsg *msg) +{ + unsigned long cqe_ctx_id; + BackendCtx *bctx; + char *mad; + + trace_mad_message("recv", msg->umad.mad, msg->umad_len); + + cqe_ctx_id = rdma_protected_qlist_pop_int64(&backend_dev->recv_mads_list); + if (cqe_ctx_id == -ENOENT) { + rdma_warn_report("No more free MADs buffers, waiting for a while"); + sleep(THR_POLL_TO); + return; + } + + bctx = rdma_rm_get_cqe_ctx(backend_dev->rdma_dev_res, cqe_ctx_id); + if (unlikely(!bctx)) { + rdma_error_report("No matching ctx for req %ld", cqe_ctx_id); + backend_dev->rdma_dev_res->stats.mad_rx_err++; + return; + } + + mad = rdma_pci_dma_map(backend_dev->dev, bctx->sge.addr, + bctx->sge.length); + if (!mad || bctx->sge.length < msg->umad_len + MAD_HDR_SIZE) { + backend_dev->rdma_dev_res->stats.mad_rx_err++; + complete_work(IBV_WC_GENERAL_ERR, VENDOR_ERR_INV_MAD_BUFF, + bctx->up_ctx); + } else { + struct ibv_wc wc = {}; + memset(mad, 0, bctx->sge.length); + build_mad_hdr((struct ibv_grh *)mad, + (union ibv_gid *)&msg->umad.hdr.addr.gid, &msg->hdr.sgid, + msg->umad_len); + memcpy(&mad[MAD_HDR_SIZE], msg->umad.mad, msg->umad_len); + rdma_pci_dma_unmap(backend_dev->dev, mad, bctx->sge.length); + + wc.byte_len = msg->umad_len; + wc.status = IBV_WC_SUCCESS; + wc.wc_flags = IBV_WC_GRH; + backend_dev->rdma_dev_res->stats.mad_rx++; + comp_handler(bctx->up_ctx, &wc); + } + + g_free(bctx); + rdma_rm_dealloc_cqe_ctx(backend_dev->rdma_dev_res, cqe_ctx_id); +} + +static inline int rdmacm_mux_can_receive(void *opaque) +{ + RdmaBackendDev *backend_dev = (RdmaBackendDev *)opaque; + + return rdmacm_mux_can_process_async(backend_dev); +} + +static void rdmacm_mux_read(void *opaque, const uint8_t *buf, int size) +{ + RdmaBackendDev *backend_dev = (RdmaBackendDev *)opaque; + RdmaCmMuxMsg *msg = (RdmaCmMuxMsg *)buf; + + trace_rdmacm_mux("read", msg->hdr.msg_type, msg->hdr.op_code); + + if (msg->hdr.msg_type != RDMACM_MUX_MSG_TYPE_REQ && + msg->hdr.op_code != RDMACM_MUX_OP_CODE_MAD) { + rdma_error_report("Error: Not a MAD request, skipping"); + return; + } + process_incoming_mad_req(backend_dev, msg); +} + +static int mad_init(RdmaBackendDev *backend_dev, CharBackend *mad_chr_be) +{ + int ret; + + backend_dev->rdmacm_mux.chr_be = mad_chr_be; + + ret = qemu_chr_fe_backend_connected(backend_dev->rdmacm_mux.chr_be); + if (!ret) { + rdma_error_report("Missing chardev for MAD multiplexer"); + return -EIO; + } + + rdma_protected_qlist_init(&backend_dev->recv_mads_list); + + enable_rdmacm_mux_async(backend_dev); + + qemu_chr_fe_set_handlers(backend_dev->rdmacm_mux.chr_be, + rdmacm_mux_can_receive, rdmacm_mux_read, NULL, + NULL, backend_dev, NULL, true); + + return 0; +} + +static void mad_stop(RdmaBackendDev *backend_dev) +{ + clean_recv_mads(backend_dev); +} + +static void mad_fini(RdmaBackendDev *backend_dev) +{ + disable_rdmacm_mux_async(backend_dev); + qemu_chr_fe_disconnect(backend_dev->rdmacm_mux.chr_be); + rdma_protected_qlist_destroy(&backend_dev->recv_mads_list); +} + +int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev, + union ibv_gid *gid) +{ + union ibv_gid sgid; + int ret; + int i = 0; + + do { + ret = ibv_query_gid(backend_dev->context, backend_dev->port_num, i, + &sgid); + i++; + } while (!ret && (memcmp(&sgid, gid, sizeof(*gid)))); + + trace_rdma_backend_get_gid_index(be64_to_cpu(gid->global.subnet_prefix), + be64_to_cpu(gid->global.interface_id), + i - 1); + + return ret ? ret : i - 1; +} + +int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname, + union ibv_gid *gid) +{ + RdmaCmMuxMsg msg = {}; + int ret; + + trace_rdma_backend_gid_change("add", be64_to_cpu(gid->global.subnet_prefix), + be64_to_cpu(gid->global.interface_id)); + + msg.hdr.op_code = RDMACM_MUX_OP_CODE_REG; + memcpy(msg.hdr.sgid.raw, gid->raw, sizeof(msg.hdr.sgid)); + + ret = rdmacm_mux_send(backend_dev, &msg); + if (ret) { + rdma_error_report("Failed to register GID to rdma_umadmux (%d)", ret); + return -EIO; + } + + qapi_event_send_rdma_gid_status_changed(ifname, true, + gid->global.subnet_prefix, + gid->global.interface_id); + + return ret; +} + +int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname, + union ibv_gid *gid) +{ + RdmaCmMuxMsg msg = {}; + int ret; + + trace_rdma_backend_gid_change("del", be64_to_cpu(gid->global.subnet_prefix), + be64_to_cpu(gid->global.interface_id)); + + msg.hdr.op_code = RDMACM_MUX_OP_CODE_UNREG; + memcpy(msg.hdr.sgid.raw, gid->raw, sizeof(msg.hdr.sgid)); + + ret = rdmacm_mux_send(backend_dev, &msg); + if (ret) { + rdma_error_report("Failed to unregister GID from rdma_umadmux (%d)", + ret); + return -EIO; + } + + qapi_event_send_rdma_gid_status_changed(ifname, false, + gid->global.subnet_prefix, + gid->global.interface_id); + + return 0; +} + +int rdma_backend_init(RdmaBackendDev *backend_dev, PCIDevice *pdev, RdmaDeviceResources *rdma_dev_res, const char *backend_device_name, uint8_t port_num, - uint8_t backend_gid_idx, struct ibv_device_attr *dev_attr, - Error **errp) + struct ibv_device_attr *dev_attr, CharBackend *mad_chr_be) { int i; int ret = 0; int num_ibv_devices; - char thread_name[THR_NAME_LEN] = {0}; struct ibv_device **dev_list; - struct ibv_port_attr port_attr; - backend_dev->backend_gid_idx = backend_gid_idx; + memset(backend_dev, 0, sizeof(*backend_dev)); + + backend_dev->dev = pdev; backend_dev->port_num = port_num; backend_dev->rdma_dev_res = rdma_dev_res; @@ -718,12 +1179,12 @@ int rdma_backend_init(RdmaBackendDev *backend_dev, dev_list = ibv_get_device_list(&num_ibv_devices); if (!dev_list) { - error_setg(errp, "Failed to get IB devices list"); + rdma_error_report("Failed to get IB devices list"); return -EIO; } if (num_ibv_devices == 0) { - error_setg(errp, "No IB devices were found"); + rdma_error_report("No IB devices were found"); ret = -ENXIO; goto out_free_dev_list; } @@ -738,8 +1199,8 @@ int rdma_backend_init(RdmaBackendDev *backend_dev, backend_dev->ib_dev = dev_list[i]; if (!backend_dev->ib_dev) { - error_setg(errp, "Failed to find IB device %s", - backend_device_name); + rdma_error_report("Failed to find IB device %s", + backend_device_name); ret = -EIO; goto out_free_dev_list; } @@ -747,64 +1208,40 @@ int rdma_backend_init(RdmaBackendDev *backend_dev, backend_dev->ib_dev = *dev_list; } - pr_dbg("Using backend device %s, port %d, gid_idx %d\n", - ibv_get_device_name(backend_dev->ib_dev), - backend_dev->port_num, backend_dev->backend_gid_idx); + rdma_info_report("uverb device %s", backend_dev->ib_dev->dev_name); backend_dev->context = ibv_open_device(backend_dev->ib_dev); if (!backend_dev->context) { - error_setg(errp, "Failed to open IB device"); + rdma_error_report("Failed to open IB device %s", + ibv_get_device_name(backend_dev->ib_dev)); ret = -EIO; goto out; } backend_dev->channel = ibv_create_comp_channel(backend_dev->context); if (!backend_dev->channel) { - error_setg(errp, "Failed to create IB communication channel"); + rdma_error_report("Failed to create IB communication channel"); ret = -EIO; goto out_close_device; } - pr_dbg("dev->backend_dev.channel=%p\n", backend_dev->channel); - - ret = ibv_query_port(backend_dev->context, backend_dev->port_num, - &port_attr); - if (ret) { - error_setg(errp, "Error %d from ibv_query_port", ret); - ret = -EIO; - goto out_destroy_comm_channel; - } - - if (backend_dev->backend_gid_idx >= port_attr.gid_tbl_len) { - error_setg(errp, "Invalid backend_gid_idx, should be less than %d", - port_attr.gid_tbl_len); - goto out_destroy_comm_channel; - } ret = init_device_caps(backend_dev, dev_attr); if (ret) { - error_setg(errp, "Failed to initialize device capabilities"); + rdma_error_report("Failed to initialize device capabilities"); ret = -EIO; goto out_destroy_comm_channel; } - ret = ibv_query_gid(backend_dev->context, backend_dev->port_num, - backend_dev->backend_gid_idx, &backend_dev->gid); + + ret = mad_init(backend_dev, mad_chr_be); if (ret) { - error_setg(errp, "Failed to query gid %d", - backend_dev->backend_gid_idx); + rdma_error_report("Failed to initialize mad"); ret = -EIO; goto out_destroy_comm_channel; } - pr_dbg("subnet_prefix=0x%" PRIx64 "\n", - be64_to_cpu(backend_dev->gid.global.subnet_prefix)); - pr_dbg("interface_id=0x%" PRIx64 "\n", - be64_to_cpu(backend_dev->gid.global.interface_id)); - snprintf(thread_name, sizeof(thread_name), "rdma_comp_%s", - ibv_get_device_name(backend_dev->ib_dev)); - backend_dev->comp_thread.run = true; - qemu_thread_create(&backend_dev->comp_thread.thread, thread_name, - comp_handler_thread, backend_dev, QEMU_THREAD_DETACHED); + backend_dev->comp_thread.run = false; + backend_dev->comp_thread.is_running = false; ah_cache_init(); @@ -823,8 +1260,21 @@ out: return ret; } + +void rdma_backend_start(RdmaBackendDev *backend_dev) +{ + start_comp_thread(backend_dev); +} + +void rdma_backend_stop(RdmaBackendDev *backend_dev) +{ + mad_stop(backend_dev); + stop_backend_thread(&backend_dev->comp_thread); +} + void rdma_backend_fini(RdmaBackendDev *backend_dev) { + mad_fini(backend_dev); g_hash_table_destroy(ah_hash); ibv_destroy_comp_channel(backend_dev->channel); ibv_close_device(backend_dev->context); diff --git a/hw/rdma/rdma_backend.h b/hw/rdma/rdma_backend.h index 3cd636dd88..38056d97c7 100644 --- a/hw/rdma/rdma_backend.h +++ b/hw/rdma/rdma_backend.h @@ -17,23 +17,32 @@ #define RDMA_BACKEND_H #include "qapi/error.h" +#include "chardev/char-fe.h" + #include "rdma_rm_defs.h" #include "rdma_backend_defs.h" +/* Vendor Errors */ +#define VENDOR_ERR_FAIL_BACKEND 0x201 +#define VENDOR_ERR_TOO_MANY_SGES 0x202 +#define VENDOR_ERR_NOMEM 0x203 +#define VENDOR_ERR_QP0 0x204 +#define VENDOR_ERR_INV_NUM_SGE 0x205 +#define VENDOR_ERR_MAD_SEND 0x206 +#define VENDOR_ERR_INVLKEY 0x207 +#define VENDOR_ERR_MR_SMALL 0x208 +#define VENDOR_ERR_INV_MAD_BUFF 0x209 +#define VENDOR_ERR_INV_GID_IDX 0x210 + /* Add definition for QP0 and QP1 as there is no userspace enums for them */ enum ibv_special_qp_type { IBV_QPT_SMI = 0, IBV_QPT_GSI = 1, }; -static inline union ibv_gid *rdma_backend_gid(RdmaBackendDev *dev) -{ - return &dev->gid; -} - static inline uint32_t rdma_backend_qpn(const RdmaBackendQP *qp) { - return qp->ibqp ? qp->ibqp->qp_num : 0; + return qp->ibqp ? qp->ibqp->qp_num : 1; } static inline uint32_t rdma_backend_mr_lkey(const RdmaBackendMR *mr) @@ -46,14 +55,22 @@ static inline uint32_t rdma_backend_mr_rkey(const RdmaBackendMR *mr) return mr->ibmr ? mr->ibmr->rkey : 0; } -int rdma_backend_init(RdmaBackendDev *backend_dev, +int rdma_backend_init(RdmaBackendDev *backend_dev, PCIDevice *pdev, RdmaDeviceResources *rdma_dev_res, const char *backend_device_name, uint8_t port_num, - uint8_t backend_gid_idx, struct ibv_device_attr *dev_attr, - Error **errp); + struct ibv_device_attr *dev_attr, + CharBackend *mad_chr_be); void rdma_backend_fini(RdmaBackendDev *backend_dev); -void rdma_backend_register_comp_handler(void (*handler)(int status, - unsigned int vendor_err, void *ctx)); +int rdma_backend_add_gid(RdmaBackendDev *backend_dev, const char *ifname, + union ibv_gid *gid); +int rdma_backend_del_gid(RdmaBackendDev *backend_dev, const char *ifname, + union ibv_gid *gid); +int rdma_backend_get_gid_index(RdmaBackendDev *backend_dev, + union ibv_gid *gid); +void rdma_backend_start(RdmaBackendDev *backend_dev); +void rdma_backend_stop(RdmaBackendDev *backend_dev); +void rdma_backend_register_comp_handler(void (*handler)(void *ctx, + struct ibv_wc *wc)); void rdma_backend_unregister_comp_handler(void); int rdma_backend_query_port(RdmaBackendDev *backend_dev, @@ -78,22 +95,22 @@ int rdma_backend_create_qp(RdmaBackendQP *qp, uint8_t qp_type, int rdma_backend_qp_state_init(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, uint8_t qp_type, uint32_t qkey); int rdma_backend_qp_state_rtr(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, - uint8_t qp_type, union ibv_gid *dgid, - uint32_t dqpn, uint32_t rq_psn, uint32_t qkey, - bool use_qkey); + uint8_t qp_type, uint8_t sgid_idx, + union ibv_gid *dgid, uint32_t dqpn, + uint32_t rq_psn, uint32_t qkey, bool use_qkey); int rdma_backend_qp_state_rts(RdmaBackendQP *qp, uint8_t qp_type, uint32_t sq_psn, uint32_t qkey, bool use_qkey); int rdma_backend_query_qp(RdmaBackendQP *qp, struct ibv_qp_attr *attr, int attr_mask, struct ibv_qp_init_attr *init_attr); -void rdma_backend_destroy_qp(RdmaBackendQP *qp); +void rdma_backend_destroy_qp(RdmaBackendQP *qp, RdmaDeviceResources *dev_res); void rdma_backend_post_send(RdmaBackendDev *backend_dev, RdmaBackendQP *qp, uint8_t qp_type, struct ibv_sge *sge, uint32_t num_sge, + uint8_t sgid_idx, union ibv_gid *sgid, union ibv_gid *dgid, uint32_t dqpn, uint32_t dqkey, void *ctx); void rdma_backend_post_recv(RdmaBackendDev *backend_dev, - RdmaDeviceResources *rdma_dev_res, RdmaBackendQP *qp, uint8_t qp_type, struct ibv_sge *sge, uint32_t num_sge, void *ctx); diff --git a/hw/rdma/rdma_backend_defs.h b/hw/rdma/rdma_backend_defs.h index ff5cfc26eb..817153dc8c 100644 --- a/hw/rdma/rdma_backend_defs.h +++ b/hw/rdma/rdma_backend_defs.h @@ -16,28 +16,35 @@ #ifndef RDMA_BACKEND_DEFS_H #define RDMA_BACKEND_DEFS_H -#include #include "qemu/thread.h" +#include "chardev/char-fe.h" +#include +#include "contrib/rdmacm-mux/rdmacm-mux.h" +#include "rdma_utils.h" typedef struct RdmaDeviceResources RdmaDeviceResources; typedef struct RdmaBackendThread { QemuThread thread; - QemuMutex mutex; - bool run; + bool run; /* Set by thread manager to let thread know it should exit */ + bool is_running; /* Set by the thread to report its status */ } RdmaBackendThread; +typedef struct RdmaCmMux { + CharBackend *chr_be; + int can_receive; +} RdmaCmMux; + typedef struct RdmaBackendDev { - struct ibv_device_attr dev_attr; RdmaBackendThread comp_thread; - union ibv_gid gid; PCIDevice *dev; RdmaDeviceResources *rdma_dev_res; struct ibv_device *ib_dev; struct ibv_context *context; struct ibv_comp_channel *channel; uint8_t port_num; - uint8_t backend_gid_idx; + RdmaProtectedQList recv_mads_list; + RdmaCmMux rdmacm_mux; } RdmaBackendDev; typedef struct RdmaBackendPD { @@ -57,6 +64,8 @@ typedef struct RdmaBackendCQ { typedef struct RdmaBackendQP { struct ibv_pd *ibpd; struct ibv_qp *ibqp; + uint8_t sgid_idx; + RdmaProtectedGSList cqe_ctx_list; } RdmaBackendQP; #endif diff --git a/hw/rdma/rdma_rm.c b/hw/rdma/rdma_rm.c index 415da15efe..bac3b2f4a6 100644 --- a/hw/rdma/rdma_rm.c +++ b/hw/rdma/rdma_rm.c @@ -16,7 +16,9 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" +#include "monitor/monitor.h" +#include "trace.h" #include "rdma_utils.h" #include "rdma_backend.h" #include "rdma_rm.h" @@ -25,6 +27,58 @@ #define PG_DIR_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } #define PG_TBL_SZ { TARGET_PAGE_SIZE / sizeof(__u64) } +void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res) +{ + monitor_printf(mon, "\ttx : %" PRId64 "\n", + dev_res->stats.tx); + monitor_printf(mon, "\ttx_len : %" PRId64 "\n", + dev_res->stats.tx_len); + monitor_printf(mon, "\ttx_err : %" PRId64 "\n", + dev_res->stats.tx_err); + monitor_printf(mon, "\trx_bufs : %" PRId64 "\n", + dev_res->stats.rx_bufs); + monitor_printf(mon, "\trx_bufs_len : %" PRId64 "\n", + dev_res->stats.rx_bufs_len); + monitor_printf(mon, "\trx_bufs_err : %" PRId64 "\n", + dev_res->stats.rx_bufs_err); + monitor_printf(mon, "\tcomps : %" PRId64 "\n", + dev_res->stats.completions); + monitor_printf(mon, "\tmissing_comps : %" PRId32 "\n", + dev_res->stats.missing_cqe); + monitor_printf(mon, "\tpoll_cq (bk) : %" PRId64 "\n", + dev_res->stats.poll_cq_from_bk); + monitor_printf(mon, "\tpoll_cq_ppoll_to : %" PRId64 "\n", + dev_res->stats.poll_cq_ppoll_to); + monitor_printf(mon, "\tpoll_cq (fe) : %" PRId64 "\n", + dev_res->stats.poll_cq_from_guest); + monitor_printf(mon, "\tpoll_cq_empty : %" PRId64 "\n", + dev_res->stats.poll_cq_from_guest_empty); + monitor_printf(mon, "\tmad_tx : %" PRId64 "\n", + dev_res->stats.mad_tx); + monitor_printf(mon, "\tmad_tx_err : %" PRId64 "\n", + dev_res->stats.mad_tx_err); + monitor_printf(mon, "\tmad_rx : %" PRId64 "\n", + dev_res->stats.mad_rx); + monitor_printf(mon, "\tmad_rx_err : %" PRId64 "\n", + dev_res->stats.mad_rx_err); + monitor_printf(mon, "\tmad_rx_bufs : %" PRId64 "\n", + dev_res->stats.mad_rx_bufs); + monitor_printf(mon, "\tmad_rx_bufs_err : %" PRId64 "\n", + dev_res->stats.mad_rx_bufs_err); + monitor_printf(mon, "\tPDs : %" PRId32 "\n", + dev_res->pd_tbl.used); + monitor_printf(mon, "\tMRs : %" PRId32 "\n", + dev_res->mr_tbl.used); + monitor_printf(mon, "\tUCs : %" PRId32 "\n", + dev_res->uc_tbl.used); + monitor_printf(mon, "\tQPs : %" PRId32 "\n", + dev_res->qp_tbl.used); + monitor_printf(mon, "\tCQs : %" PRId32 "\n", + dev_res->cq_tbl.used); + monitor_printf(mon, "\tCEQ_CTXs : %" PRId32 "\n", + dev_res->cqe_ctx_tbl.used); +} + static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl, uint32_t tbl_sz, uint32_t res_sz) { @@ -36,58 +90,66 @@ static inline void res_tbl_init(const char *name, RdmaRmResTbl *tbl, tbl->bitmap = bitmap_new(tbl_sz); tbl->tbl_sz = tbl_sz; tbl->res_sz = res_sz; + tbl->used = 0; qemu_mutex_init(&tbl->lock); } static inline void res_tbl_free(RdmaRmResTbl *tbl) { + if (!tbl->bitmap) { + return; + } qemu_mutex_destroy(&tbl->lock); g_free(tbl->tbl); - bitmap_zero_extend(tbl->bitmap, tbl->tbl_sz, 0); + g_free(tbl->bitmap); } -static inline void *res_tbl_get(RdmaRmResTbl *tbl, uint32_t handle) +static inline void *rdma_res_tbl_get(RdmaRmResTbl *tbl, uint32_t handle) { - pr_dbg("%s, handle=%d\n", tbl->name, handle); + trace_rdma_res_tbl_get(tbl->name, handle); if ((handle < tbl->tbl_sz) && (test_bit(handle, tbl->bitmap))) { return tbl->tbl + handle * tbl->res_sz; } else { - pr_dbg("Invalid handle %d\n", handle); + rdma_error_report("Table %s, invalid handle %d", tbl->name, handle); return NULL; } } -static inline void *res_tbl_alloc(RdmaRmResTbl *tbl, uint32_t *handle) +static inline void *rdma_res_tbl_alloc(RdmaRmResTbl *tbl, uint32_t *handle) { qemu_mutex_lock(&tbl->lock); *handle = find_first_zero_bit(tbl->bitmap, tbl->tbl_sz); if (*handle > tbl->tbl_sz) { - pr_dbg("Failed to alloc, bitmap is full\n"); + rdma_error_report("Table %s, failed to allocate, bitmap is full", + tbl->name); qemu_mutex_unlock(&tbl->lock); return NULL; } set_bit(*handle, tbl->bitmap); + tbl->used++; + qemu_mutex_unlock(&tbl->lock); memset(tbl->tbl + *handle * tbl->res_sz, 0, tbl->res_sz); - pr_dbg("%s, handle=%d\n", tbl->name, *handle); + trace_rdma_res_tbl_alloc(tbl->name, *handle); return tbl->tbl + *handle * tbl->res_sz; } -static inline void res_tbl_dealloc(RdmaRmResTbl *tbl, uint32_t handle) +static inline void rdma_res_tbl_dealloc(RdmaRmResTbl *tbl, uint32_t handle) { - pr_dbg("%s, handle=%d\n", tbl->name, handle); + trace_rdma_res_tbl_dealloc(tbl->name, handle); qemu_mutex_lock(&tbl->lock); if (handle < tbl->tbl_sz) { clear_bit(handle, tbl->bitmap); + tbl->used--; } qemu_mutex_unlock(&tbl->lock); @@ -99,7 +161,7 @@ int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, RdmaRmPD *pd; int ret = -ENOMEM; - pd = res_tbl_alloc(&dev_res->pd_tbl, pd_handle); + pd = rdma_res_tbl_alloc(&dev_res->pd_tbl, pd_handle); if (!pd) { goto out; } @@ -115,7 +177,7 @@ int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, return 0; out_tbl_dealloc: - res_tbl_dealloc(&dev_res->pd_tbl, *pd_handle); + rdma_res_tbl_dealloc(&dev_res->pd_tbl, *pd_handle); out: return ret; @@ -123,7 +185,7 @@ out: RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) { - return res_tbl_get(&dev_res->pd_tbl, pd_handle); + return rdma_res_tbl_get(&dev_res->pd_tbl, pd_handle); } void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) @@ -132,80 +194,62 @@ void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle) if (pd) { rdma_backend_destroy_pd(&pd->backend_pd); - res_tbl_dealloc(&dev_res->pd_tbl, pd_handle); + rdma_res_tbl_dealloc(&dev_res->pd_tbl, pd_handle); } } int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint64_t guest_start, size_t guest_length, void *host_virt, - int access_flags, uint32_t *mr_handle, uint32_t *lkey, - uint32_t *rkey) + uint64_t guest_start, uint64_t guest_length, + void *host_virt, int access_flags, uint32_t *mr_handle, + uint32_t *lkey, uint32_t *rkey) { RdmaRmMR *mr; int ret = 0; RdmaRmPD *pd; - void *addr; - size_t length; pd = rdma_rm_get_pd(dev_res, pd_handle); if (!pd) { - pr_dbg("Invalid PD\n"); return -EINVAL; } - mr = res_tbl_alloc(&dev_res->mr_tbl, mr_handle); + mr = rdma_res_tbl_alloc(&dev_res->mr_tbl, mr_handle); if (!mr) { - pr_dbg("Failed to allocate obj in table\n"); return -ENOMEM; } + trace_rdma_rm_alloc_mr(*mr_handle, host_virt, guest_start, guest_length, + access_flags); - if (!host_virt) { - /* TODO: This is my guess but not so sure that this needs to be - * done */ - length = TARGET_PAGE_SIZE; - addr = g_malloc(length); - } else { - mr->user_mr.host_virt = host_virt; - pr_dbg("host_virt=0x%p\n", mr->user_mr.host_virt); - mr->user_mr.length = guest_length; - pr_dbg("length=%zu\n", guest_length); - mr->user_mr.guest_start = guest_start; - pr_dbg("guest_start=0x%" PRIx64 "\n", mr->user_mr.guest_start); + if (host_virt) { + mr->virt = host_virt; + mr->start = guest_start; + mr->length = guest_length; + mr->virt += (mr->start & (TARGET_PAGE_SIZE - 1)); - length = mr->user_mr.length; - addr = mr->user_mr.host_virt; + ret = rdma_backend_create_mr(&mr->backend_mr, &pd->backend_pd, mr->virt, + mr->length, access_flags); + if (ret) { + ret = -EIO; + goto out_dealloc_mr; + } } - ret = rdma_backend_create_mr(&mr->backend_mr, &pd->backend_pd, addr, length, - access_flags); - if (ret) { - pr_dbg("Fail in rdma_backend_create_mr, err=%d\n", ret); - ret = -EIO; - goto out_dealloc_mr; - } - - if (!host_virt) { - *lkey = mr->lkey = rdma_backend_mr_lkey(&mr->backend_mr); - *rkey = mr->rkey = rdma_backend_mr_rkey(&mr->backend_mr); - } else { - /* We keep mr_handle in lkey so send and recv get get mr ptr */ - *lkey = *mr_handle; - *rkey = -1; - } + /* We keep mr_handle in lkey so send and recv get get mr ptr */ + *lkey = *mr_handle; + *rkey = -1; mr->pd_handle = pd_handle; return 0; out_dealloc_mr: - res_tbl_dealloc(&dev_res->mr_tbl, *mr_handle); + rdma_res_tbl_dealloc(&dev_res->mr_tbl, *mr_handle); return ret; } RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) { - return res_tbl_get(&dev_res->mr_tbl, mr_handle); + return rdma_res_tbl_get(&dev_res->mr_tbl, mr_handle); } void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) @@ -214,8 +258,12 @@ void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle) if (mr) { rdma_backend_destroy_mr(&mr->backend_mr); - munmap(mr->user_mr.host_virt, mr->user_mr.length); - res_tbl_dealloc(&dev_res->mr_tbl, mr_handle); + trace_rdma_rm_dealloc_mr(mr_handle, mr->start); + if (mr->start) { + mr->virt -= (mr->start & (TARGET_PAGE_SIZE - 1)); + munmap(mr->virt, mr->length); + } + rdma_res_tbl_dealloc(&dev_res->mr_tbl, mr_handle); } } @@ -227,12 +275,13 @@ int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn, /* TODO: Need to make sure pfn is between bar start address and * bsd+RDMA_BAR2_UAR_SIZE if (pfn > RDMA_BAR2_UAR_SIZE) { - pr_err("pfn out of range (%d > %d)\n", pfn, RDMA_BAR2_UAR_SIZE); + rdma_error_report("pfn out of range (%d > %d)", pfn, + RDMA_BAR2_UAR_SIZE); return -ENOMEM; } */ - uc = res_tbl_alloc(&dev_res->uc_tbl, uc_handle); + uc = rdma_res_tbl_alloc(&dev_res->uc_tbl, uc_handle); if (!uc) { return -ENOMEM; } @@ -242,7 +291,7 @@ int rdma_rm_alloc_uc(RdmaDeviceResources *dev_res, uint32_t pfn, RdmaRmUC *rdma_rm_get_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) { - return res_tbl_get(&dev_res->uc_tbl, uc_handle); + return rdma_res_tbl_get(&dev_res->uc_tbl, uc_handle); } void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) @@ -250,13 +299,13 @@ void rdma_rm_dealloc_uc(RdmaDeviceResources *dev_res, uint32_t uc_handle) RdmaRmUC *uc = rdma_rm_get_uc(dev_res, uc_handle); if (uc) { - res_tbl_dealloc(&dev_res->uc_tbl, uc_handle); + rdma_res_tbl_dealloc(&dev_res->uc_tbl, uc_handle); } } RdmaRmCQ *rdma_rm_get_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) { - return res_tbl_get(&dev_res->cq_tbl, cq_handle); + return rdma_res_tbl_get(&dev_res->cq_tbl, cq_handle); } int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, @@ -265,13 +314,13 @@ int rdma_rm_alloc_cq(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, int rc; RdmaRmCQ *cq; - cq = res_tbl_alloc(&dev_res->cq_tbl, cq_handle); + cq = rdma_res_tbl_alloc(&dev_res->cq_tbl, cq_handle); if (!cq) { return -ENOMEM; } cq->opaque = opaque; - cq->notify = false; + cq->notify = CNT_CLEAR; rc = rdma_backend_create_cq(backend_dev, &cq->backend_cq, cqe); if (rc) { @@ -292,15 +341,14 @@ void rdma_rm_req_notify_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle, { RdmaRmCQ *cq; - pr_dbg("cq_handle=%d, notify=0x%x\n", cq_handle, notify); - cq = rdma_rm_get_cq(dev_res, cq_handle); if (!cq) { return; } - cq->notify = notify; - pr_dbg("notify=%d\n", cq->notify); + if (cq->notify != CNT_SET) { + cq->notify = notify ? CNT_ARM : CNT_CLEAR; + } } void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) @@ -314,7 +362,7 @@ void rdma_rm_dealloc_cq(RdmaDeviceResources *dev_res, uint32_t cq_handle) rdma_backend_destroy_cq(&cq->backend_cq); - res_tbl_dealloc(&dev_res->cq_tbl, cq_handle); + rdma_res_tbl_dealloc(&dev_res->cq_tbl, cq_handle); } RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn) @@ -325,6 +373,10 @@ RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn) g_bytes_unref(key); + if (!qp) { + rdma_error_report("Invalid QP handle %d", qpn); + } + return qp; } @@ -340,11 +392,8 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, RdmaRmPD *pd; uint32_t rm_qpn; - pr_dbg("qp_type=%d\n", qp_type); - pd = rdma_rm_get_pd(dev_res, pd_handle); if (!pd) { - pr_err("Invalid pd handle (%d)\n", pd_handle); return -EINVAL; } @@ -352,16 +401,20 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, rcq = rdma_rm_get_cq(dev_res, recv_cq_handle); if (!scq || !rcq) { - pr_err("Invalid send_cqn or recv_cqn (%d, %d)\n", - send_cq_handle, recv_cq_handle); + rdma_error_report("Invalid send_cqn or recv_cqn (%d, %d)", + send_cq_handle, recv_cq_handle); return -EINVAL; } - qp = res_tbl_alloc(&dev_res->qp_tbl, &rm_qpn); + if (qp_type == IBV_QPT_GSI) { + scq->notify = CNT_SET; + rcq->notify = CNT_SET; + } + + qp = rdma_res_tbl_alloc(&dev_res->qp_tbl, &rm_qpn); if (!qp) { return -ENOMEM; } - pr_dbg("rm_qpn=%d\n", rm_qpn); qp->qpn = rm_qpn; qp->qp_state = IBV_QPS_RESET; @@ -379,19 +432,19 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, } *qpn = rdma_backend_qpn(&qp->backend_qp); - pr_dbg("rm_qpn=%d, backend_qpn=0x%x\n", rm_qpn, *qpn); + trace_rdma_rm_alloc_qp(rm_qpn, *qpn, qp_type); g_hash_table_insert(dev_res->qp_hash, g_bytes_new(qpn, sizeof(*qpn)), qp); return 0; out_dealloc_qp: - res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); + rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); return rc; } int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, uint32_t attr_mask, + uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, union ibv_gid *dgid, uint32_t dqpn, enum ibv_qp_state qp_state, uint32_t qkey, uint32_t rq_psn, uint32_t sq_psn) @@ -399,27 +452,22 @@ int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, RdmaRmQP *qp; int ret; - pr_dbg("qpn=%d\n", qp_handle); - qp = rdma_rm_get_qp(dev_res, qp_handle); if (!qp) { return -EINVAL; } - pr_dbg("qp_type=%d\n", qp->qp_type); - pr_dbg("attr_mask=0x%x\n", attr_mask); - if (qp->qp_type == IBV_QPT_SMI) { - pr_dbg("QP0 unsupported\n"); + rdma_error_report("Got QP0 request"); return -EPERM; } else if (qp->qp_type == IBV_QPT_GSI) { - pr_dbg("QP1\n"); return 0; } + trace_rdma_rm_modify_qp(qp_handle, attr_mask, qp_state, sgid_idx); + if (attr_mask & IBV_QP_STATE) { qp->qp_state = qp_state; - pr_dbg("qp_state=%d\n", qp->qp_state); if (qp->qp_state == IBV_QPS_INIT) { ret = rdma_backend_qp_state_init(backend_dev, &qp->backend_qp, @@ -430,9 +478,19 @@ int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, } if (qp->qp_state == IBV_QPS_RTR) { + /* Get backend gid index */ + sgid_idx = rdma_rm_get_backend_gid_index(dev_res, backend_dev, + sgid_idx); + if (sgid_idx <= 0) { /* TODO check also less than bk.max_sgid */ + rdma_error_report("Failed to get bk sgid_idx for sgid_idx %d", + sgid_idx); + return -EIO; + } + ret = rdma_backend_qp_state_rtr(backend_dev, &qp->backend_qp, - qp->qp_type, dgid, dqpn, rq_psn, - qkey, attr_mask & IBV_QP_QKEY); + qp->qp_type, sgid_idx, dgid, dqpn, + rq_psn, qkey, + attr_mask & IBV_QP_QKEY); if (ret) { return -EIO; } @@ -457,15 +515,11 @@ int rdma_rm_query_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, { RdmaRmQP *qp; - pr_dbg("qpn=%d\n", qp_handle); - qp = rdma_rm_get_qp(dev_res, qp_handle); if (!qp) { return -EINVAL; } - pr_dbg("qp_type=%d\n", qp->qp_type); - return rdma_backend_query_qp(&qp->backend_qp, attr, attr_mask, init_attr); } @@ -483,22 +537,20 @@ void rdma_rm_dealloc_qp(RdmaDeviceResources *dev_res, uint32_t qp_handle) return; } - rdma_backend_destroy_qp(&qp->backend_qp); + rdma_backend_destroy_qp(&qp->backend_qp, dev_res); - res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); + rdma_res_tbl_dealloc(&dev_res->qp_tbl, qp->qpn); } void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) { void **cqe_ctx; - cqe_ctx = res_tbl_get(&dev_res->cqe_ctx_tbl, cqe_ctx_id); + cqe_ctx = rdma_res_tbl_get(&dev_res->cqe_ctx_tbl, cqe_ctx_id); if (!cqe_ctx) { return NULL; } - pr_dbg("ctx=%p\n", *cqe_ctx); - return *cqe_ctx; } @@ -507,12 +559,11 @@ int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, { void **cqe_ctx; - cqe_ctx = res_tbl_alloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); + cqe_ctx = rdma_res_tbl_alloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); if (!cqe_ctx) { return -ENOMEM; } - pr_dbg("ctx=%p\n", ctx); *cqe_ctx = ctx; return 0; @@ -520,7 +571,61 @@ int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id) { - res_tbl_dealloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); + rdma_res_tbl_dealloc(&dev_res->cqe_ctx_tbl, cqe_ctx_id); +} + +int rdma_rm_add_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, + const char *ifname, union ibv_gid *gid, int gid_idx) +{ + int rc; + + rc = rdma_backend_add_gid(backend_dev, ifname, gid); + if (rc) { + return -EINVAL; + } + + memcpy(&dev_res->port.gid_tbl[gid_idx].gid, gid, sizeof(*gid)); + + return 0; +} + +int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, + const char *ifname, int gid_idx) +{ + int rc; + + if (!dev_res->port.gid_tbl[gid_idx].gid.global.interface_id) { + return 0; + } + + rc = rdma_backend_del_gid(backend_dev, ifname, + &dev_res->port.gid_tbl[gid_idx].gid); + if (rc) { + return -EINVAL; + } + + memset(dev_res->port.gid_tbl[gid_idx].gid.raw, 0, + sizeof(dev_res->port.gid_tbl[gid_idx].gid)); + dev_res->port.gid_tbl[gid_idx].backend_gid_index = -1; + + return 0; +} + +int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res, + RdmaBackendDev *backend_dev, int sgid_idx) +{ + if (unlikely(sgid_idx < 0 || sgid_idx >= MAX_PORT_GIDS)) { + rdma_error_report("Got invalid sgid_idx %d", sgid_idx); + return -EINVAL; + } + + if (unlikely(dev_res->port.gid_tbl[sgid_idx].backend_gid_index == -1)) { + dev_res->port.gid_tbl[sgid_idx].backend_gid_index = + rdma_backend_get_gid_index(backend_dev, + &dev_res->port.gid_tbl[sgid_idx].gid); + } + + return dev_res->port.gid_tbl[sgid_idx].backend_gid_index; } static void destroy_qp_hash_key(gpointer data) @@ -528,8 +633,30 @@ static void destroy_qp_hash_key(gpointer data) g_bytes_unref(data); } -int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr, - Error **errp) +static void init_ports(RdmaDeviceResources *dev_res) +{ + int i; + + memset(&dev_res->port, 0, sizeof(dev_res->port)); + + dev_res->port.state = IBV_PORT_DOWN; + for (i = 0; i < MAX_PORT_GIDS; i++) { + dev_res->port.gid_tbl[i].backend_gid_index = -1; + } +} + +static void fini_ports(RdmaDeviceResources *dev_res, + RdmaBackendDev *backend_dev, const char *ifname) +{ + int i; + + dev_res->port.state = IBV_PORT_DOWN; + for (i = 0; i < MAX_PORT_GIDS; i++) { + rdma_rm_del_gid(dev_res, backend_dev, ifname, i); + } +} + +int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr) { dev_res->qp_hash = g_hash_table_new_full(g_bytes_hash, g_bytes_equal, destroy_qp_hash_key, NULL); @@ -545,16 +672,31 @@ int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr, dev_attr->max_qp_wr, sizeof(void *)); res_tbl_init("UC", &dev_res->uc_tbl, MAX_UCS, sizeof(RdmaRmUC)); + init_ports(dev_res); + + qemu_mutex_init(&dev_res->lock); + + memset(&dev_res->stats, 0, sizeof(dev_res->stats)); + atomic_set(&dev_res->stats.missing_cqe, 0); + return 0; } -void rdma_rm_fini(RdmaDeviceResources *dev_res) +void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, + const char *ifname) { + qemu_mutex_destroy(&dev_res->lock); + + fini_ports(dev_res, backend_dev, ifname); + res_tbl_free(&dev_res->uc_tbl); res_tbl_free(&dev_res->cqe_ctx_tbl); res_tbl_free(&dev_res->qp_tbl); - res_tbl_free(&dev_res->cq_tbl); res_tbl_free(&dev_res->mr_tbl); + res_tbl_free(&dev_res->cq_tbl); res_tbl_free(&dev_res->pd_tbl); - g_hash_table_destroy(dev_res->qp_hash); + + if (dev_res->qp_hash) { + g_hash_table_destroy(dev_res->qp_hash); + } } diff --git a/hw/rdma/rdma_rm.h b/hw/rdma/rdma_rm.h index b4e04cc7b4..4f03f9b8c5 100644 --- a/hw/rdma/rdma_rm.h +++ b/hw/rdma/rdma_rm.h @@ -20,9 +20,10 @@ #include "rdma_backend_defs.h" #include "rdma_rm_defs.h" -int rdma_rm_init(RdmaDeviceResources *dev_res, struct ibv_device_attr *dev_attr, - Error **errp); -void rdma_rm_fini(RdmaDeviceResources *dev_res); +int rdma_rm_init(RdmaDeviceResources *dev_res, + struct ibv_device_attr *dev_attr); +void rdma_rm_fini(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, + const char *ifname); int rdma_rm_alloc_pd(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, uint32_t *pd_handle, uint32_t ctx_handle); @@ -30,9 +31,9 @@ RdmaRmPD *rdma_rm_get_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle); void rdma_rm_dealloc_pd(RdmaDeviceResources *dev_res, uint32_t pd_handle); int rdma_rm_alloc_mr(RdmaDeviceResources *dev_res, uint32_t pd_handle, - uint64_t guest_start, size_t guest_length, void *host_virt, - int access_flags, uint32_t *mr_handle, uint32_t *lkey, - uint32_t *rkey); + uint64_t guest_start, uint64_t guest_length, + void *host_virt, int access_flags, uint32_t *mr_handle, + uint32_t *lkey, uint32_t *rkey); RdmaRmMR *rdma_rm_get_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle); void rdma_rm_dealloc_mr(RdmaDeviceResources *dev_res, uint32_t mr_handle); @@ -55,7 +56,7 @@ int rdma_rm_alloc_qp(RdmaDeviceResources *dev_res, uint32_t pd_handle, uint32_t recv_cq_handle, void *opaque, uint32_t *qpn); RdmaRmQP *rdma_rm_get_qp(RdmaDeviceResources *dev_res, uint32_t qpn); int rdma_rm_modify_qp(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, - uint32_t qp_handle, uint32_t attr_mask, + uint32_t qp_handle, uint32_t attr_mask, uint8_t sgid_idx, union ibv_gid *dgid, uint32_t dqpn, enum ibv_qp_state qp_state, uint32_t qkey, uint32_t rq_psn, uint32_t sq_psn); @@ -69,4 +70,17 @@ int rdma_rm_alloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t *cqe_ctx_id, void *rdma_rm_get_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); void rdma_rm_dealloc_cqe_ctx(RdmaDeviceResources *dev_res, uint32_t cqe_ctx_id); +int rdma_rm_add_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, + const char *ifname, union ibv_gid *gid, int gid_idx); +int rdma_rm_del_gid(RdmaDeviceResources *dev_res, RdmaBackendDev *backend_dev, + const char *ifname, int gid_idx); +int rdma_rm_get_backend_gid_index(RdmaDeviceResources *dev_res, + RdmaBackendDev *backend_dev, int sgid_idx); +static inline union ibv_gid *rdma_rm_get_gid(RdmaDeviceResources *dev_res, + int sgid_idx) +{ + return &dev_res->port.gid_tbl[sgid_idx].gid; +} +void rdma_dump_device_counters(Monitor *mon, RdmaDeviceResources *dev_res); + #endif diff --git a/hw/rdma/rdma_rm_defs.h b/hw/rdma/rdma_rm_defs.h index 226011176d..c200d311de 100644 --- a/hw/rdma/rdma_rm_defs.h +++ b/hw/rdma/rdma_rm_defs.h @@ -18,8 +18,8 @@ #include "rdma_backend_defs.h" -#define MAX_PORTS 1 -#define MAX_PORT_GIDS 1 +#define MAX_PORTS 1 /* Do not change - we support only one port */ +#define MAX_PORT_GIDS 255 #define MAX_GIDS MAX_PORT_GIDS #define MAX_PORT_PKEYS 1 #define MAX_PKEYS MAX_PORT_PKEYS @@ -34,7 +34,9 @@ #define MAX_QP_INIT_RD_ATOM 16 #define MAX_AH 64 -#define MAX_RM_TBL_NAME 16 +#define MAX_RM_TBL_NAME 16 +#define MAX_CONSEQ_EMPTY_POLL_CQ 4096 /* considered as error above this */ + typedef struct RdmaRmResTbl { char name[MAX_RM_TBL_NAME]; QemuMutex lock; @@ -42,6 +44,7 @@ typedef struct RdmaRmResTbl { size_t tbl_sz; size_t res_sz; void *tbl; + uint32_t used; /* number of used entries in the table */ } RdmaRmResTbl; typedef struct RdmaRmPD { @@ -49,22 +52,24 @@ typedef struct RdmaRmPD { uint32_t ctx_handle; } RdmaRmPD; +typedef enum CQNotificationType { + CNT_CLEAR, + CNT_ARM, + CNT_SET, +} CQNotificationType; + typedef struct RdmaRmCQ { RdmaBackendCQ backend_cq; void *opaque; - bool notify; + CQNotificationType notify; } RdmaRmCQ; -typedef struct RdmaRmUserMR { - void *host_virt; - uint64_t guest_start; - size_t length; -} RdmaRmUserMR; - /* MR (DMA region) */ typedef struct RdmaRmMR { RdmaBackendMR backend_mr; - RdmaRmUserMR user_mr; + void *virt; + uint64_t start; + size_t length; uint32_t pd_handle; uint32_t lkey; uint32_t rkey; @@ -84,13 +89,39 @@ typedef struct RdmaRmQP { enum ibv_qp_state qp_state; } RdmaRmQP; +typedef struct RdmaRmGid { + union ibv_gid gid; + int backend_gid_index; +} RdmaRmGid; + typedef struct RdmaRmPort { - union ibv_gid gid_tbl[MAX_PORT_GIDS]; + RdmaRmGid gid_tbl[MAX_PORT_GIDS]; enum ibv_port_state state; } RdmaRmPort; -typedef struct RdmaDeviceResources { - RdmaRmPort ports[MAX_PORTS]; +typedef struct RdmaRmStats { + uint64_t tx; + uint64_t tx_len; + uint64_t tx_err; + uint64_t rx_bufs; + uint64_t rx_bufs_len; + uint64_t rx_bufs_err; + uint64_t completions; + uint64_t mad_tx; + uint64_t mad_tx_err; + uint64_t mad_rx; + uint64_t mad_rx_err; + uint64_t mad_rx_bufs; + uint64_t mad_rx_bufs_err; + uint64_t poll_cq_from_bk; + uint64_t poll_cq_from_guest; + uint64_t poll_cq_from_guest_empty; + uint64_t poll_cq_ppoll_to; + uint32_t missing_cqe; +} RdmaRmStats; + +struct RdmaDeviceResources { + RdmaRmPort port; RdmaRmResTbl pd_tbl; RdmaRmResTbl mr_tbl; RdmaRmResTbl uc_tbl; @@ -98,6 +129,8 @@ typedef struct RdmaDeviceResources { RdmaRmResTbl cq_tbl; RdmaRmResTbl cqe_ctx_tbl; GHashTable *qp_hash; /* Keeps mapping between real and emulated */ -} RdmaDeviceResources; + QemuMutex lock; + RdmaRmStats stats; +}; #endif diff --git a/hw/rdma/rdma_utils.c b/hw/rdma/rdma_utils.c index d713f635f1..73f279104c 100644 --- a/hw/rdma/rdma_utils.c +++ b/hw/rdma/rdma_utils.c @@ -13,6 +13,10 @@ * */ +#include "qemu/osdep.h" +#include "qapi/qmp/qlist.h" +#include "qapi/qmp/qnum.h" +#include "trace.h" #include "rdma_utils.h" void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen) @@ -21,14 +25,14 @@ void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen) hwaddr len = plen; if (!addr) { - pr_dbg("addr is NULL\n"); + rdma_error_report("addr is NULL"); return NULL; } p = pci_dma_map(dev, addr, &len, DMA_DIRECTION_TO_DEVICE); if (!p) { - pr_dbg("Fail in pci_dma_map, addr=0x%" PRIx64 ", len=%" PRId64 "\n", - addr, len); + rdma_error_report("pci_dma_map fail, addr=0x%"PRIx64", len=%"PRId64, + addr, len); return NULL; } @@ -37,15 +41,81 @@ void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen) return NULL; } - pr_dbg("0x%" PRIx64 " -> %p (len=% " PRId64 ")\n", addr, p, len); + trace_rdma_pci_dma_map(addr, p, len); return p; } void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len) { - pr_dbg("%p\n", buffer); + trace_rdma_pci_dma_unmap(buffer); if (buffer) { pci_dma_unmap(dev, buffer, len, DMA_DIRECTION_TO_DEVICE, 0); } } + +void rdma_protected_qlist_init(RdmaProtectedQList *list) +{ + qemu_mutex_init(&list->lock); + list->list = qlist_new(); +} + +void rdma_protected_qlist_destroy(RdmaProtectedQList *list) +{ + if (list->list) { + qlist_destroy_obj(QOBJECT(list->list)); + qemu_mutex_destroy(&list->lock); + list->list = NULL; + } +} + +void rdma_protected_qlist_append_int64(RdmaProtectedQList *list, int64_t value) +{ + qemu_mutex_lock(&list->lock); + qlist_append_int(list->list, value); + qemu_mutex_unlock(&list->lock); +} + +int64_t rdma_protected_qlist_pop_int64(RdmaProtectedQList *list) +{ + QObject *obj; + + qemu_mutex_lock(&list->lock); + obj = qlist_pop(list->list); + qemu_mutex_unlock(&list->lock); + + if (!obj) { + return -ENOENT; + } + + return qnum_get_uint(qobject_to(QNum, obj)); +} + +void rdma_protected_gslist_init(RdmaProtectedGSList *list) +{ + qemu_mutex_init(&list->lock); +} + +void rdma_protected_gslist_destroy(RdmaProtectedGSList *list) +{ + if (list->list) { + g_slist_free(list->list); + list->list = NULL; + } +} + +void rdma_protected_gslist_append_int32(RdmaProtectedGSList *list, + int32_t value) +{ + qemu_mutex_lock(&list->lock); + list->list = g_slist_prepend(list->list, GINT_TO_POINTER(value)); + qemu_mutex_unlock(&list->lock); +} + +void rdma_protected_gslist_remove_int32(RdmaProtectedGSList *list, + int32_t value) +{ + qemu_mutex_lock(&list->lock); + list->list = g_slist_remove(list->list, GINT_TO_POINTER(value)); + qemu_mutex_unlock(&list->lock); +} diff --git a/hw/rdma/rdma_utils.h b/hw/rdma/rdma_utils.h index 3dc07891bc..2d42249691 100644 --- a/hw/rdma/rdma_utils.h +++ b/hw/rdma/rdma_utils.h @@ -17,27 +17,48 @@ #ifndef RDMA_UTILS_H #define RDMA_UTILS_H -#include "qemu/osdep.h" +#include "qemu/error-report.h" #include "hw/pci/pci.h" #include "sysemu/dma.h" +#include "stdio.h" -#define pr_info(fmt, ...) \ - fprintf(stdout, "%s: %-20s (%3d): " fmt, "pvrdma", __func__, __LINE__,\ - ## __VA_ARGS__) +#define rdma_error_report(fmt, ...) \ + error_report("%s: " fmt, "rdma", ## __VA_ARGS__) +#define rdma_warn_report(fmt, ...) \ + warn_report("%s: " fmt, "rdma", ## __VA_ARGS__) +#define rdma_info_report(fmt, ...) \ + info_report("%s: " fmt, "rdma", ## __VA_ARGS__) -#define pr_err(fmt, ...) \ - fprintf(stderr, "%s: Error at %-20s (%3d): " fmt, "pvrdma", __func__, \ - __LINE__, ## __VA_ARGS__) +typedef struct RdmaProtectedQList { + QemuMutex lock; + QList *list; +} RdmaProtectedQList; -#ifdef PVRDMA_DEBUG -#define pr_dbg(fmt, ...) \ - fprintf(stdout, "%s: %-20s (%3d): " fmt, "pvrdma", __func__, __LINE__,\ - ## __VA_ARGS__) -#else -#define pr_dbg(fmt, ...) -#endif +typedef struct RdmaProtectedGSList { + QemuMutex lock; + GSList *list; +} RdmaProtectedGSList; void *rdma_pci_dma_map(PCIDevice *dev, dma_addr_t addr, dma_addr_t plen); void rdma_pci_dma_unmap(PCIDevice *dev, void *buffer, dma_addr_t len); +void rdma_protected_qlist_init(RdmaProtectedQList *list); +void rdma_protected_qlist_destroy(RdmaProtectedQList *list); +void rdma_protected_qlist_append_int64(RdmaProtectedQList *list, int64_t value); +int64_t rdma_protected_qlist_pop_int64(RdmaProtectedQList *list); +void rdma_protected_gslist_init(RdmaProtectedGSList *list); +void rdma_protected_gslist_destroy(RdmaProtectedGSList *list); +void rdma_protected_gslist_append_int32(RdmaProtectedGSList *list, + int32_t value); +void rdma_protected_gslist_remove_int32(RdmaProtectedGSList *list, + int32_t value); + +static inline void addrconf_addr_eui48(uint8_t *eui, const char *addr) +{ + memcpy(eui, addr, 3); + eui[3] = 0xFF; + eui[4] = 0xFE; + memcpy(eui + 5, addr + 3, 3); + eui[0] ^= 2; +} #endif diff --git a/hw/rdma/trace-events b/hw/rdma/trace-events index c4c202e647..2022a820cb 100644 --- a/hw/rdma/trace-events +++ b/hw/rdma/trace-events @@ -1,5 +1,31 @@ -# See docs/tracing.txt for syntax documentation. +# See docs/devel/tracing.txt for syntax documentation. -#hw/rdma/rdma_backend.c -create_ah_cache_hit(uint64_t subnet, uint64_t net_id) "subnet = 0x%"PRIx64" net_id = 0x%"PRIx64 -create_ah_cache_miss(uint64_t subnet, uint64_t net_id) "subnet = 0x%"PRIx64" net_id = 0x%"PRIx64 +# rdma_backend.c +rdma_check_dev_attr(const char *name, int max_bk, int max_fe) "%s: be=%d, fe=%d" +rdma_create_ah_cache_hit(uint64_t subnet, uint64_t if_id) "subnet=0x%"PRIx64",if_id=0x%"PRIx64 +rdma_create_ah_cache_miss(uint64_t subnet, uint64_t if_id) "subnet=0x%"PRIx64",if_id=0x%"PRIx64 +rdma_poll_cq(int ne, void *ibcq) "Got %d completion(s) from cq %p" +rdmacm_mux(const char *title, int msg_type, int op_code) "%s: msg_type=%d, op_code=%d" +rdmacm_mux_check_op_status(int msg_type, int op_code, int err_code) "resp: msg_type=%d, op_code=%d, err_code=%d" +rdma_mad_message(const char *title, int len, char *data) "mad %s (%d): %s" +rdma_backend_rc_qp_state_init(uint32_t qpn) "RC QP 0x%x switch to INIT" +rdma_backend_ud_qp_state_init(uint32_t qpn, uint32_t qkey) "UD QP 0x%x switch to INIT, qkey=0x%x" +rdma_backend_rc_qp_state_rtr(uint32_t qpn, uint64_t subnet, uint64_t ifid, uint8_t sgid_idx, uint32_t dqpn, uint32_t rq_psn) "RC QP 0x%x switch to RTR, subnet = 0x%"PRIx64", ifid = 0x%"PRIx64 ", sgid_idx=%d, dqpn=0x%x, rq_psn=0x%x" +rdma_backend_ud_qp_state_rtr(uint32_t qpn, uint32_t qkey) "UD QP 0x%x switch to RTR, qkey=0x%x" +rdma_backend_rc_qp_state_rts(uint32_t qpn, uint32_t sq_psn) "RC QP 0x%x switch to RTS, sq_psn=0x%x, " +rdma_backend_ud_qp_state_rts(uint32_t qpn, uint32_t sq_psn, uint32_t qkey) "UD QP 0x%x switch to RTS, sq_psn=0x%x, qkey=0x%x" +rdma_backend_get_gid_index(uint64_t subnet, uint64_t ifid, int gid_idx) "subnet=0x%"PRIx64", ifid=0x%"PRIx64 ", gid_idx=%d" +rdma_backend_gid_change(const char *op, uint64_t subnet, uint64_t ifid) "%s subnet=0x%"PRIx64", ifid=0x%"PRIx64 + +# rdma_rm.c +rdma_res_tbl_get(char *name, uint32_t handle) "tbl %s, handle %d" +rdma_res_tbl_alloc(char *name, uint32_t handle) "tbl %s, handle %d" +rdma_res_tbl_dealloc(char *name, uint32_t handle) "tbl %s, handle %d" +rdma_rm_alloc_mr(uint32_t mr_handle, void *host_virt, uint64_t guest_start, uint64_t guest_length, int access_flags) "mr_handle=%d, host_virt=%p, guest_start=0x%"PRIx64", length=%" PRId64", access_flags=0x%x" +rdma_rm_dealloc_mr(uint32_t mr_handle, uint64_t guest_start) "mr_handle=%d, guest_start=0x%"PRIx64 +rdma_rm_alloc_qp(uint32_t rm_qpn, uint32_t backend_qpn, uint8_t qp_type) "rm_qpn=%d, backend_qpn=0x%x, qp_type=%d" +rdma_rm_modify_qp(uint32_t qpn, uint32_t attr_mask, int qp_state, uint8_t sgid_idx) "qpn=0x%x, attr_mask=0x%x, qp_state=%d, sgid_idx=%d" + +# rdma_utils.c +rdma_pci_dma_map(uint64_t addr, void *vaddr, uint64_t len) "0x%"PRIx64" -> %p (len=%" PRId64")" +rdma_pci_dma_unmap(void *vaddr) "%p" diff --git a/hw/rdma/vmw/pvrdma.h b/hw/rdma/vmw/pvrdma.h index 81e0e0e99c..a8a04a253c 100644 --- a/hw/rdma/vmw/pvrdma.h +++ b/hw/rdma/vmw/pvrdma.h @@ -17,8 +17,11 @@ #define PVRDMA_PVRDMA_H #include "qemu/units.h" +#include "qemu/notify.h" #include "hw/pci/pci.h" #include "hw/pci/msix.h" +#include "chardev/char-fe.h" +#include "hw/net/vmxnet3_defs.h" #include "../rdma_backend_defs.h" #include "../rdma_rm_defs.h" @@ -50,6 +53,9 @@ #define PVRDMA_HW_VERSION 17 #define PVRDMA_FW_VERSION 14 +/* Some defaults */ +#define PVRDMA_PKEY 0xFFFF + typedef struct DSRInfo { dma_addr_t dma; struct pvrdma_device_shared_region *dsr; @@ -64,6 +70,14 @@ typedef struct DSRInfo { PvrdmaRing cq; } DSRInfo; +typedef struct PVRDMADevStats { + uint64_t commands; + uint64_t regs_reads; + uint64_t regs_writes; + uint64_t uar_writes; + uint64_t interrupts; +} PVRDMADevStats; + typedef struct PVRDMADev { PCIDevice parent_obj; MemoryRegion msix; @@ -75,11 +89,15 @@ typedef struct PVRDMADev { int interrupt_mask; struct ibv_device_attr dev_attr; uint64_t node_guid; + char *backend_eth_device_name; char *backend_device_name; - uint8_t backend_gid_idx; uint8_t backend_port_num; RdmaBackendDev backend_dev; RdmaDeviceResources rdma_dev_res; + CharBackend mad_chr; + VMXNET3State *func0; + Notifier shutdown_notifier; + PVRDMADevStats stats; } PVRDMADev; #define PVRDMA_DEV(dev) OBJECT_CHECK(PVRDMADev, (dev), PVRDMA_HW_NAME) @@ -114,10 +132,11 @@ static inline void post_interrupt(PVRDMADev *dev, unsigned vector) PCIDevice *pci_dev = PCI_DEVICE(dev); if (likely(!dev->interrupt_mask)) { + dev->stats.interrupts++; msix_notify(pci_dev, vector); } } -int execute_command(PVRDMADev *dev); +int pvrdma_exec_cmd(PVRDMADev *dev); #endif diff --git a/hw/rdma/vmw/pvrdma_cmd.c b/hw/rdma/vmw/pvrdma_cmd.c index 14255d609f..4afcd2037d 100644 --- a/hw/rdma/vmw/pvrdma_cmd.c +++ b/hw/rdma/vmw/pvrdma_cmd.c @@ -14,9 +14,7 @@ */ #include "qemu/osdep.h" -#include "qemu/error-report.h" #include "cpu.h" -#include #include "hw/hw.h" #include "hw/pci/pci.h" #include "hw/pci/pci_ids.h" @@ -25,6 +23,7 @@ #include "../rdma_rm.h" #include "../rdma_utils.h" +#include "trace.h" #include "pvrdma.h" #include "standard-headers/rdma/vmw_pvrdma-abi.h" @@ -36,39 +35,38 @@ static void *pvrdma_map_to_pdir(PCIDevice *pdev, uint64_t pdir_dma, void *host_virt = NULL, *curr_page; if (!nchunks) { - pr_dbg("nchunks=0\n"); + rdma_error_report("Got nchunks=0"); return NULL; } dir = rdma_pci_dma_map(pdev, pdir_dma, TARGET_PAGE_SIZE); if (!dir) { - error_report("PVRDMA: Failed to map to page directory"); + rdma_error_report("Failed to map to page directory"); return NULL; } tbl = rdma_pci_dma_map(pdev, dir[0], TARGET_PAGE_SIZE); if (!tbl) { - error_report("PVRDMA: Failed to map to page table 0"); + rdma_error_report("Failed to map to page table 0"); goto out_unmap_dir; } curr_page = rdma_pci_dma_map(pdev, (dma_addr_t)tbl[0], TARGET_PAGE_SIZE); if (!curr_page) { - error_report("PVRDMA: Failed to map the first page"); + rdma_error_report("Failed to map the page 0"); goto out_unmap_tbl; } host_virt = mremap(curr_page, 0, length, MREMAP_MAYMOVE); if (host_virt == MAP_FAILED) { host_virt = NULL; - error_report("PVRDMA: Failed to remap memory for host_virt"); + rdma_error_report("Failed to remap memory for host_virt"); goto out_unmap_tbl; } + trace_pvrdma_map_to_pdir_host_virt(curr_page, host_virt); rdma_pci_dma_unmap(pdev, curr_page, TARGET_PAGE_SIZE); - pr_dbg("host_virt=%p\n", host_virt); - dir_idx = 0; tbl_idx = 1; addr_idx = 1; @@ -76,28 +74,28 @@ static void *pvrdma_map_to_pdir(PCIDevice *pdev, uint64_t pdir_dma, if (tbl_idx == TARGET_PAGE_SIZE / sizeof(uint64_t)) { tbl_idx = 0; dir_idx++; - pr_dbg("Mapping to table %d\n", dir_idx); rdma_pci_dma_unmap(pdev, tbl, TARGET_PAGE_SIZE); tbl = rdma_pci_dma_map(pdev, dir[dir_idx], TARGET_PAGE_SIZE); if (!tbl) { - error_report("PVRDMA: Failed to map to page table %d", dir_idx); + rdma_error_report("Failed to map to page table %d", dir_idx); goto out_unmap_host_virt; } } - pr_dbg("guest_dma[%d]=0x%" PRIx64 "\n", addr_idx, tbl[tbl_idx]); - curr_page = rdma_pci_dma_map(pdev, (dma_addr_t)tbl[tbl_idx], TARGET_PAGE_SIZE); if (!curr_page) { - error_report("PVRDMA: Failed to map to page %d, dir %d", tbl_idx, - dir_idx); + rdma_error_report("Failed to map to page %d, dir %d", tbl_idx, + dir_idx); goto out_unmap_host_virt; } mremap(curr_page, 0, TARGET_PAGE_SIZE, MREMAP_MAYMOVE | MREMAP_FIXED, host_virt + TARGET_PAGE_SIZE * addr_idx); + trace_pvrdma_map_to_pdir_next_page(addr_idx, curr_page, host_virt + + TARGET_PAGE_SIZE * addr_idx); + rdma_pci_dma_unmap(pdev, curr_page, TARGET_PAGE_SIZE); addr_idx++; @@ -125,9 +123,11 @@ static int query_port(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_query_port *cmd = &req->query_port; struct pvrdma_cmd_query_port_resp *resp = &rsp->query_port_resp; - struct pvrdma_port_attr attrs = {0}; + struct pvrdma_port_attr attrs = {}; - pr_dbg("port=%d\n", cmd->port_num); + if (cmd->port_num > MAX_PORTS) { + return -EINVAL; + } if (rdma_backend_query_port(&dev->backend_dev, (struct ibv_port_attr *)&attrs)) { @@ -135,11 +135,9 @@ static int query_port(PVRDMADev *dev, union pvrdma_cmd_req *req, } memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_QUERY_PORT_RESP; - resp->hdr.err = 0; - resp->attrs.state = attrs.state; + resp->attrs.state = dev->func0->device_active ? attrs.state : + PVRDMA_PORT_DOWN; resp->attrs.max_mtu = attrs.max_mtu; resp->attrs.active_mtu = attrs.active_mtu; resp->attrs.phys_state = attrs.phys_state; @@ -158,16 +156,17 @@ static int query_pkey(PVRDMADev *dev, union pvrdma_cmd_req *req, struct pvrdma_cmd_query_pkey *cmd = &req->query_pkey; struct pvrdma_cmd_query_pkey_resp *resp = &rsp->query_pkey_resp; - pr_dbg("port=%d\n", cmd->port_num); - pr_dbg("index=%d\n", cmd->index); + if (cmd->port_num > MAX_PORTS) { + return -EINVAL; + } + + if (cmd->index > MAX_PKEYS) { + return -EINVAL; + } memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_QUERY_PKEY_RESP; - resp->hdr.err = 0; - resp->pkey = 0x7FFF; - pr_dbg("pkey=0x%x\n", resp->pkey); + resp->pkey = PVRDMA_PKEY; return 0; } @@ -177,17 +176,13 @@ static int create_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_create_pd *cmd = &req->create_pd; struct pvrdma_cmd_create_pd_resp *resp = &rsp->create_pd_resp; - - pr_dbg("context=0x%x\n", cmd->ctx_handle ? cmd->ctx_handle : 0); + int rc; memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_CREATE_PD_RESP; - resp->hdr.err = rdma_rm_alloc_pd(&dev->rdma_dev_res, &dev->backend_dev, - &resp->pd_handle, cmd->ctx_handle); + rc = rdma_rm_alloc_pd(&dev->rdma_dev_res, &dev->backend_dev, + &resp->pd_handle, cmd->ctx_handle); - pr_dbg("ret=%d\n", resp->hdr.err); - return resp->hdr.err; + return rc; } static int destroy_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -195,8 +190,6 @@ static int destroy_pd(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_destroy_pd *cmd = &req->destroy_pd; - pr_dbg("pd_handle=%d\n", cmd->pd_handle); - rdma_rm_dealloc_pd(&dev->rdma_dev_res, cmd->pd_handle); return 0; @@ -209,36 +202,27 @@ static int create_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, struct pvrdma_cmd_create_mr_resp *resp = &rsp->create_mr_resp; PCIDevice *pci_dev = PCI_DEVICE(dev); void *host_virt = NULL; + int rc = 0; memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_CREATE_MR_RESP; - - pr_dbg("pd_handle=%d\n", cmd->pd_handle); - pr_dbg("access_flags=0x%x\n", cmd->access_flags); - pr_dbg("flags=0x%x\n", cmd->flags); if (!(cmd->flags & PVRDMA_MR_FLAG_DMA)) { host_virt = pvrdma_map_to_pdir(pci_dev, cmd->pdir_dma, cmd->nchunks, cmd->length); if (!host_virt) { - pr_dbg("Failed to map to pdir\n"); - resp->hdr.err = -EINVAL; - goto out; + rdma_error_report("Failed to map to pdir"); + return -EINVAL; } } - resp->hdr.err = rdma_rm_alloc_mr(&dev->rdma_dev_res, cmd->pd_handle, - cmd->start, cmd->length, host_virt, - cmd->access_flags, &resp->mr_handle, - &resp->lkey, &resp->rkey); - if (host_virt && !resp->hdr.err) { + rc = rdma_rm_alloc_mr(&dev->rdma_dev_res, cmd->pd_handle, cmd->start, + cmd->length, host_virt, cmd->access_flags, + &resp->mr_handle, &resp->lkey, &resp->rkey); + if (rc && host_virt) { munmap(host_virt, cmd->length); } -out: - pr_dbg("ret=%d\n", resp->hdr.err); - return resp->hdr.err; + return rc; } static int destroy_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -246,8 +230,6 @@ static int destroy_mr(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_destroy_mr *cmd = &req->destroy_mr; - pr_dbg("mr_handle=%d\n", cmd->mr_handle); - rdma_rm_dealloc_mr(&dev->rdma_dev_res, cmd->mr_handle); return 0; @@ -261,16 +243,20 @@ static int create_cq_ring(PCIDevice *pci_dev , PvrdmaRing **ring, int rc = -EINVAL; char ring_name[MAX_RING_NAME_SZ]; - pr_dbg("pdir_dma=0x%llx\n", (long long unsigned int)pdir_dma); + if (!nchunks || nchunks > PVRDMA_MAX_FAST_REG_PAGES) { + rdma_error_report("Got invalid nchunks: %d", nchunks); + return rc; + } + dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); if (!dir) { - pr_dbg("Failed to map to CQ page directory\n"); + rdma_error_report("Failed to map to CQ page directory"); goto out; } tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); if (!tbl) { - pr_dbg("Failed to map to CQ page table\n"); + rdma_error_report("Failed to map to CQ page table"); goto out; } @@ -281,7 +267,7 @@ static int create_cq_ring(PCIDevice *pci_dev , PvrdmaRing **ring, rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!r->ring_state) { - pr_dbg("Failed to map to CQ ring state\n"); + rdma_error_report("Failed to map to CQ ring state"); goto out_free_ring; } @@ -310,34 +296,41 @@ out: return rc; } +static void destroy_cq_ring(PvrdmaRing *ring) +{ + pvrdma_ring_free(ring); + /* ring_state was in slot 1, not 0 so need to jump back */ + rdma_pci_dma_unmap(ring->dev, --ring->ring_state, TARGET_PAGE_SIZE); + g_free(ring); +} + static int create_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { struct pvrdma_cmd_create_cq *cmd = &req->create_cq; struct pvrdma_cmd_create_cq_resp *resp = &rsp->create_cq_resp; PvrdmaRing *ring = NULL; + int rc; memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_CREATE_CQ_RESP; resp->cqe = cmd->cqe; - resp->hdr.err = create_cq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, - cmd->nchunks, cmd->cqe); - if (resp->hdr.err) { - goto out; + rc = create_cq_ring(PCI_DEVICE(dev), &ring, cmd->pdir_dma, cmd->nchunks, + cmd->cqe); + if (rc) { + return rc; } - pr_dbg("ring=%p\n", ring); + rc = rdma_rm_alloc_cq(&dev->rdma_dev_res, &dev->backend_dev, cmd->cqe, + &resp->cq_handle, ring); + if (rc) { + destroy_cq_ring(ring); + } - resp->hdr.err = rdma_rm_alloc_cq(&dev->rdma_dev_res, &dev->backend_dev, - cmd->cqe, &resp->cq_handle, ring); resp->cqe = cmd->cqe; -out: - pr_dbg("ret=%d\n", resp->hdr.err); - return resp->hdr.err; + return rc; } static int destroy_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -347,19 +340,14 @@ static int destroy_cq(PVRDMADev *dev, union pvrdma_cmd_req *req, RdmaRmCQ *cq; PvrdmaRing *ring; - pr_dbg("cq_handle=%d\n", cmd->cq_handle); - cq = rdma_rm_get_cq(&dev->rdma_dev_res, cmd->cq_handle); if (!cq) { - pr_dbg("Invalid CQ handle\n"); + rdma_error_report("Got invalid CQ handle"); return -EINVAL; } ring = (PvrdmaRing *)cq->opaque; - pvrdma_ring_free(ring); - /* ring_state was in slot 1, not 0 so need to jump back */ - rdma_pci_dma_unmap(PCI_DEVICE(dev), --ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); + destroy_cq_ring(ring); rdma_rm_dealloc_cq(&dev->rdma_dev_res, cmd->cq_handle); @@ -377,38 +365,35 @@ static int create_qp_rings(PCIDevice *pci_dev, uint64_t pdir_dma, char ring_name[MAX_RING_NAME_SZ]; uint32_t wqe_sz; - pr_dbg("pdir_dma=0x%llx\n", (long long unsigned int)pdir_dma); + if (!spages || spages > PVRDMA_MAX_FAST_REG_PAGES + || !rpages || rpages > PVRDMA_MAX_FAST_REG_PAGES) { + rdma_error_report("Got invalid page count for QP ring: %d, %d", spages, + rpages); + return rc; + } + dir = rdma_pci_dma_map(pci_dev, pdir_dma, TARGET_PAGE_SIZE); if (!dir) { - pr_dbg("Failed to map to CQ page directory\n"); + rdma_error_report("Failed to map to QP page directory"); goto out; } tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); if (!tbl) { - pr_dbg("Failed to map to CQ page table\n"); + rdma_error_report("Failed to map to QP page table"); goto out; } sr = g_malloc(2 * sizeof(*rr)); rr = &sr[1]; - pr_dbg("sring=%p\n", sr); - pr_dbg("rring=%p\n", rr); *rings = sr; - pr_dbg("scqe=%d\n", scqe); - pr_dbg("smax_sge=%d\n", smax_sge); - pr_dbg("spages=%d\n", spages); - pr_dbg("rcqe=%d\n", rcqe); - pr_dbg("rmax_sge=%d\n", rmax_sge); - pr_dbg("rpages=%d\n", rpages); - /* Create send ring */ sr->ring_state = (struct pvrdma_ring *) rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!sr->ring_state) { - pr_dbg("Failed to map to CQ ring state\n"); + rdma_error_report("Failed to map to QP ring state"); goto out_free_sr_mem; } @@ -451,36 +436,42 @@ out: return rc; } +static void destroy_qp_rings(PvrdmaRing *ring) +{ + pvrdma_ring_free(&ring[0]); + pvrdma_ring_free(&ring[1]); + + rdma_pci_dma_unmap(ring->dev, ring->ring_state, TARGET_PAGE_SIZE); + g_free(ring); +} + static int create_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { struct pvrdma_cmd_create_qp *cmd = &req->create_qp; struct pvrdma_cmd_create_qp_resp *resp = &rsp->create_qp_resp; PvrdmaRing *rings = NULL; + int rc; memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_CREATE_QP_RESP; - pr_dbg("total_chunks=%d\n", cmd->total_chunks); - pr_dbg("send_chunks=%d\n", cmd->send_chunks); - - resp->hdr.err = create_qp_rings(PCI_DEVICE(dev), cmd->pdir_dma, &rings, - cmd->max_send_wr, cmd->max_send_sge, - cmd->send_chunks, cmd->max_recv_wr, - cmd->max_recv_sge, cmd->total_chunks - - cmd->send_chunks - 1); - if (resp->hdr.err) { - goto out; + rc = create_qp_rings(PCI_DEVICE(dev), cmd->pdir_dma, &rings, + cmd->max_send_wr, cmd->max_send_sge, cmd->send_chunks, + cmd->max_recv_wr, cmd->max_recv_sge, + cmd->total_chunks - cmd->send_chunks - 1); + if (rc) { + return rc; } - pr_dbg("rings=%p\n", rings); - - resp->hdr.err = rdma_rm_alloc_qp(&dev->rdma_dev_res, cmd->pd_handle, - cmd->qp_type, cmd->max_send_wr, - cmd->max_send_sge, cmd->send_cq_handle, - cmd->max_recv_wr, cmd->max_recv_sge, - cmd->recv_cq_handle, rings, &resp->qpn); + rc = rdma_rm_alloc_qp(&dev->rdma_dev_res, cmd->pd_handle, cmd->qp_type, + cmd->max_send_wr, cmd->max_send_sge, + cmd->send_cq_handle, cmd->max_recv_wr, + cmd->max_recv_sge, cmd->recv_cq_handle, rings, + &resp->qpn); + if (rc) { + destroy_qp_rings(rings); + return rc; + } resp->max_send_wr = cmd->max_send_wr; resp->max_recv_wr = cmd->max_recv_wr; @@ -488,32 +479,27 @@ static int create_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, resp->max_recv_sge = cmd->max_recv_sge; resp->max_inline_data = cmd->max_inline_data; -out: - pr_dbg("ret=%d\n", resp->hdr.err); - return resp->hdr.err; + return 0; } static int modify_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { struct pvrdma_cmd_modify_qp *cmd = &req->modify_qp; + int rc; - pr_dbg("qp_handle=%d\n", cmd->qp_handle); + /* No need to verify sgid_index since it is u8 */ - memset(rsp, 0, sizeof(*rsp)); - rsp->hdr.response = cmd->hdr.response; - rsp->hdr.ack = PVRDMA_CMD_MODIFY_QP_RESP; + rc = rdma_rm_modify_qp(&dev->rdma_dev_res, &dev->backend_dev, + cmd->qp_handle, cmd->attr_mask, + cmd->attrs.ah_attr.grh.sgid_index, + (union ibv_gid *)&cmd->attrs.ah_attr.grh.dgid, + cmd->attrs.dest_qp_num, + (enum ibv_qp_state)cmd->attrs.qp_state, + cmd->attrs.qkey, cmd->attrs.rq_psn, + cmd->attrs.sq_psn); - rsp->hdr.err = rdma_rm_modify_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, cmd->attr_mask, - (union ibv_gid *)&cmd->attrs.ah_attr.grh.dgid, - cmd->attrs.dest_qp_num, - (enum ibv_qp_state)cmd->attrs.qp_state, - cmd->attrs.qkey, cmd->attrs.rq_psn, - cmd->attrs.sq_psn); - - pr_dbg("ret=%d\n", rsp->hdr.err); - return rsp->hdr.err; + return rc; } static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -522,20 +508,15 @@ static int query_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, struct pvrdma_cmd_query_qp *cmd = &req->query_qp; struct pvrdma_cmd_query_qp_resp *resp = &rsp->query_qp_resp; struct ibv_qp_init_attr init_attr; + int rc; - pr_dbg("qp_handle=%d\n", cmd->qp_handle); + memset(resp, 0, sizeof(*resp)); - memset(rsp, 0, sizeof(*rsp)); - rsp->hdr.response = cmd->hdr.response; - rsp->hdr.ack = PVRDMA_CMD_QUERY_QP_RESP; + rc = rdma_rm_query_qp(&dev->rdma_dev_res, &dev->backend_dev, cmd->qp_handle, + (struct ibv_qp_attr *)&resp->attrs, cmd->attr_mask, + &init_attr); - rsp->hdr.err = rdma_rm_query_qp(&dev->rdma_dev_res, &dev->backend_dev, - cmd->qp_handle, - (struct ibv_qp_attr *)&resp->attrs, -1, - &init_attr); - - pr_dbg("ret=%d\n", rsp->hdr.err); - return rsp->hdr.err; + return rc; } static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -547,20 +528,13 @@ static int destroy_qp(PVRDMADev *dev, union pvrdma_cmd_req *req, qp = rdma_rm_get_qp(&dev->rdma_dev_res, cmd->qp_handle); if (!qp) { - pr_dbg("Invalid QP handle\n"); return -EINVAL; } rdma_rm_dealloc_qp(&dev->rdma_dev_res, cmd->qp_handle); ring = (PvrdmaRing *)qp->opaque; - pr_dbg("sring=%p\n", &ring[0]); - pvrdma_ring_free(&ring[0]); - pr_dbg("rring=%p\n", &ring[1]); - pvrdma_ring_free(&ring[1]); - - rdma_pci_dma_unmap(PCI_DEVICE(dev), ring->ring_state, TARGET_PAGE_SIZE); - g_free(ring); + destroy_qp_rings(ring); return 0; } @@ -569,50 +543,34 @@ static int create_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { struct pvrdma_cmd_create_bind *cmd = &req->create_bind; -#ifdef PVRDMA_DEBUG - __be64 *subnet = (__be64 *)&cmd->new_gid[0]; - __be64 *if_id = (__be64 *)&cmd->new_gid[8]; -#endif - - pr_dbg("index=%d\n", cmd->index); + int rc; + union ibv_gid *gid = (union ibv_gid *)&cmd->new_gid; if (cmd->index >= MAX_PORT_GIDS) { return -EINVAL; } - pr_dbg("gid[%d]=0x%llx,0x%llx\n", cmd->index, - (long long unsigned int)be64_to_cpu(*subnet), - (long long unsigned int)be64_to_cpu(*if_id)); + rc = rdma_rm_add_gid(&dev->rdma_dev_res, &dev->backend_dev, + dev->backend_eth_device_name, gid, cmd->index); - /* Driver forces to one port only */ - memcpy(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw, &cmd->new_gid, - sizeof(cmd->new_gid)); - - /* TODO: Since drivers stores node_guid at load_dsr phase then this - * assignment is not relevant, i need to figure out a way how to - * retrieve MAC of our netdev */ - dev->node_guid = dev->rdma_dev_res.ports[0].gid_tbl[0].global.interface_id; - pr_dbg("dev->node_guid=0x%llx\n", - (long long unsigned int)be64_to_cpu(dev->node_guid)); - - return 0; + return rc; } static int destroy_bind(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp) { - struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; + int rc; - pr_dbg("index=%d\n", cmd->index); + struct pvrdma_cmd_destroy_bind *cmd = &req->destroy_bind; if (cmd->index >= MAX_PORT_GIDS) { return -EINVAL; } - memset(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw, 0, - sizeof(dev->rdma_dev_res.ports[0].gid_tbl[cmd->index].raw)); + rc = rdma_rm_del_gid(&dev->rdma_dev_res, &dev->backend_dev, + dev->backend_eth_device_name, cmd->index); - return 0; + return rc; } static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -620,18 +578,12 @@ static int create_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_create_uc *cmd = &req->create_uc; struct pvrdma_cmd_create_uc_resp *resp = &rsp->create_uc_resp; - - pr_dbg("pfn=%d\n", cmd->pfn); + int rc; memset(resp, 0, sizeof(*resp)); - resp->hdr.response = cmd->hdr.response; - resp->hdr.ack = PVRDMA_CMD_CREATE_UC_RESP; - resp->hdr.err = rdma_rm_alloc_uc(&dev->rdma_dev_res, cmd->pfn, - &resp->ctx_handle); + rc = rdma_rm_alloc_uc(&dev->rdma_dev_res, cmd->pfn, &resp->ctx_handle); - pr_dbg("ret=%d\n", resp->hdr.err); - - return 0; + return rc; } static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, @@ -639,59 +591,66 @@ static int destroy_uc(PVRDMADev *dev, union pvrdma_cmd_req *req, { struct pvrdma_cmd_destroy_uc *cmd = &req->destroy_uc; - pr_dbg("ctx_handle=%d\n", cmd->ctx_handle); - rdma_rm_dealloc_uc(&dev->rdma_dev_res, cmd->ctx_handle); return 0; } + struct cmd_handler { uint32_t cmd; + uint32_t ack; int (*exec)(PVRDMADev *dev, union pvrdma_cmd_req *req, union pvrdma_cmd_resp *rsp); }; static struct cmd_handler cmd_handlers[] = { - {PVRDMA_CMD_QUERY_PORT, query_port}, - {PVRDMA_CMD_QUERY_PKEY, query_pkey}, - {PVRDMA_CMD_CREATE_PD, create_pd}, - {PVRDMA_CMD_DESTROY_PD, destroy_pd}, - {PVRDMA_CMD_CREATE_MR, create_mr}, - {PVRDMA_CMD_DESTROY_MR, destroy_mr}, - {PVRDMA_CMD_CREATE_CQ, create_cq}, - {PVRDMA_CMD_RESIZE_CQ, NULL}, - {PVRDMA_CMD_DESTROY_CQ, destroy_cq}, - {PVRDMA_CMD_CREATE_QP, create_qp}, - {PVRDMA_CMD_MODIFY_QP, modify_qp}, - {PVRDMA_CMD_QUERY_QP, query_qp}, - {PVRDMA_CMD_DESTROY_QP, destroy_qp}, - {PVRDMA_CMD_CREATE_UC, create_uc}, - {PVRDMA_CMD_DESTROY_UC, destroy_uc}, - {PVRDMA_CMD_CREATE_BIND, create_bind}, - {PVRDMA_CMD_DESTROY_BIND, destroy_bind}, + {PVRDMA_CMD_QUERY_PORT, PVRDMA_CMD_QUERY_PORT_RESP, query_port}, + {PVRDMA_CMD_QUERY_PKEY, PVRDMA_CMD_QUERY_PKEY_RESP, query_pkey}, + {PVRDMA_CMD_CREATE_PD, PVRDMA_CMD_CREATE_PD_RESP, create_pd}, + {PVRDMA_CMD_DESTROY_PD, PVRDMA_CMD_DESTROY_PD_RESP_NOOP, destroy_pd}, + {PVRDMA_CMD_CREATE_MR, PVRDMA_CMD_CREATE_MR_RESP, create_mr}, + {PVRDMA_CMD_DESTROY_MR, PVRDMA_CMD_DESTROY_MR_RESP_NOOP, destroy_mr}, + {PVRDMA_CMD_CREATE_CQ, PVRDMA_CMD_CREATE_CQ_RESP, create_cq}, + {PVRDMA_CMD_RESIZE_CQ, PVRDMA_CMD_RESIZE_CQ_RESP, NULL}, + {PVRDMA_CMD_DESTROY_CQ, PVRDMA_CMD_DESTROY_CQ_RESP_NOOP, destroy_cq}, + {PVRDMA_CMD_CREATE_QP, PVRDMA_CMD_CREATE_QP_RESP, create_qp}, + {PVRDMA_CMD_MODIFY_QP, PVRDMA_CMD_MODIFY_QP_RESP, modify_qp}, + {PVRDMA_CMD_QUERY_QP, PVRDMA_CMD_QUERY_QP_RESP, query_qp}, + {PVRDMA_CMD_DESTROY_QP, PVRDMA_CMD_DESTROY_QP_RESP, destroy_qp}, + {PVRDMA_CMD_CREATE_UC, PVRDMA_CMD_CREATE_UC_RESP, create_uc}, + {PVRDMA_CMD_DESTROY_UC, PVRDMA_CMD_DESTROY_UC_RESP_NOOP, destroy_uc}, + {PVRDMA_CMD_CREATE_BIND, PVRDMA_CMD_CREATE_BIND_RESP_NOOP, create_bind}, + {PVRDMA_CMD_DESTROY_BIND, PVRDMA_CMD_DESTROY_BIND_RESP_NOOP, destroy_bind}, }; -int execute_command(PVRDMADev *dev) +int pvrdma_exec_cmd(PVRDMADev *dev) { int err = 0xFFFF; DSRInfo *dsr_info; dsr_info = &dev->dsr_info; - pr_dbg("cmd=%d\n", dsr_info->req->hdr.cmd); if (dsr_info->req->hdr.cmd >= sizeof(cmd_handlers) / sizeof(struct cmd_handler)) { - pr_dbg("Unsupported command\n"); + rdma_error_report("Unsupported command"); goto out; } if (!cmd_handlers[dsr_info->req->hdr.cmd].exec) { - pr_dbg("Unsupported command (not implemented yet)\n"); + rdma_error_report("Unsupported command (not implemented yet)"); goto out; } err = cmd_handlers[dsr_info->req->hdr.cmd].exec(dev, dsr_info->req, - dsr_info->rsp); + dsr_info->rsp); + dsr_info->rsp->hdr.response = dsr_info->req->hdr.response; + dsr_info->rsp->hdr.ack = cmd_handlers[dsr_info->req->hdr.cmd].ack; + dsr_info->rsp->hdr.err = err < 0 ? -err : 0; + + trace_pvrdma_exec_cmd(dsr_info->req->hdr.cmd, dsr_info->rsp->hdr.err); + + dev->stats.commands++; + out: set_reg_val(dev, PVRDMA_REG_ERR, err); post_interrupt(dev, INTR_VEC_CMD_RING); diff --git a/hw/rdma/vmw/pvrdma_dev_ring.c b/hw/rdma/vmw/pvrdma_dev_ring.c index 01247fc041..d7bc7f5ccc 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.c +++ b/hw/rdma/vmw/pvrdma_dev_ring.c @@ -17,6 +17,8 @@ #include "hw/pci/pci.h" #include "cpu.h" +#include "trace.h" + #include "../rdma_utils.h" #include "standard-headers/drivers/infiniband/hw/vmw_pvrdma/pvrdma_ring.h" #include "pvrdma_dev_ring.h" @@ -30,13 +32,10 @@ int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, strncpy(ring->name, name, MAX_RING_NAME_SZ); ring->name[MAX_RING_NAME_SZ - 1] = 0; - pr_dbg("Initializing %s ring\n", ring->name); ring->dev = dev; ring->ring_state = ring_state; ring->max_elems = max_elems; ring->elem_sz = elem_sz; - pr_dbg("ring->elem_sz=%zu\n", ring->elem_sz); - pr_dbg("npages=%d\n", npages); /* TODO: Give a moment to think if we want to redo driver settings atomic_set(&ring->ring_state->prod_tail, 0); atomic_set(&ring->ring_state->cons_head, 0); @@ -46,14 +45,14 @@ int pvrdma_ring_init(PvrdmaRing *ring, const char *name, PCIDevice *dev, for (i = 0; i < npages; i++) { if (!tbl[i]) { - pr_err("npages=%ld but tbl[%d] is NULL\n", (long)npages, i); + rdma_error_report("npages=%d but tbl[%d] is NULL", npages, i); continue; } ring->pages[i] = rdma_pci_dma_map(dev, tbl[i], TARGET_PAGE_SIZE); if (!ring->pages[i]) { rc = -ENOMEM; - pr_dbg("Failed to map to page %d\n", i); + rdma_error_report("Failed to map to page %d in ring %s", i, name); goto out_free; } memset(ring->pages[i], 0, TARGET_PAGE_SIZE); @@ -73,52 +72,40 @@ out: void *pvrdma_ring_next_elem_read(PvrdmaRing *ring) { + int e; unsigned int idx = 0, offset; - /* - pr_dbg("%s: t=%d, h=%d\n", ring->name, ring->ring_state->prod_tail, - ring->ring_state->cons_head); - */ - - if (!pvrdma_idx_ring_has_data(ring->ring_state, ring->max_elems, &idx)) { - pr_dbg("No more data in ring\n"); + e = pvrdma_idx_ring_has_data(ring->ring_state, ring->max_elems, &idx); + if (e <= 0) { + trace_pvrdma_ring_next_elem_read_no_data(ring->name); return NULL; } offset = idx * ring->elem_sz; - /* - pr_dbg("idx=%d\n", idx); - pr_dbg("offset=%d\n", offset); - */ return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); } void pvrdma_ring_read_inc(PvrdmaRing *ring) { pvrdma_idx_ring_inc(&ring->ring_state->cons_head, ring->max_elems); - /* - pr_dbg("%s: t=%d, h=%d, m=%ld\n", ring->name, - ring->ring_state->prod_tail, ring->ring_state->cons_head, - ring->max_elems); - */ } void *pvrdma_ring_next_elem_write(PvrdmaRing *ring) { - unsigned int idx, offset, tail; + int idx; + unsigned int offset, tail; - /* - pr_dbg("%s: t=%d, h=%d\n", ring->name, ring->ring_state->prod_tail, - ring->ring_state->cons_head); - */ - - if (!pvrdma_idx_ring_has_space(ring->ring_state, ring->max_elems, &tail)) { - pr_dbg("CQ is full\n"); + idx = pvrdma_idx_ring_has_space(ring->ring_state, ring->max_elems, &tail); + if (idx <= 0) { + rdma_error_report("CQ is full"); return NULL; } idx = pvrdma_idx(&ring->ring_state->prod_tail, ring->max_elems); - /* TODO: tail == idx */ + if (idx < 0 || tail != idx) { + rdma_error_report("Invalid idx %d", idx); + return NULL; + } offset = idx * ring->elem_sz; return ring->pages[offset / TARGET_PAGE_SIZE] + (offset % TARGET_PAGE_SIZE); @@ -127,11 +114,6 @@ void *pvrdma_ring_next_elem_write(PvrdmaRing *ring) void pvrdma_ring_write_inc(PvrdmaRing *ring) { pvrdma_idx_ring_inc(&ring->ring_state->prod_tail, ring->max_elems); - /* - pr_dbg("%s: t=%d, h=%d, m=%ld\n", ring->name, - ring->ring_state->prod_tail, ring->ring_state->cons_head, - ring->max_elems); - */ } void pvrdma_ring_free(PvrdmaRing *ring) @@ -144,7 +126,6 @@ void pvrdma_ring_free(PvrdmaRing *ring) return; } - pr_dbg("ring->npages=%d\n", ring->npages); while (ring->npages--) { rdma_pci_dma_unmap(ring->dev, ring->pages[ring->npages], TARGET_PAGE_SIZE); diff --git a/hw/rdma/vmw/pvrdma_dev_ring.h b/hw/rdma/vmw/pvrdma_dev_ring.h index 411d244603..5f2a0cf9b9 100644 --- a/hw/rdma/vmw/pvrdma_dev_ring.h +++ b/hw/rdma/vmw/pvrdma_dev_ring.h @@ -16,7 +16,6 @@ #ifndef PVRDMA_DEV_RING_H #define PVRDMA_DEV_RING_H -#include "qemu/typedefs.h" #define MAX_RING_NAME_SZ 32 diff --git a/hw/rdma/vmw/pvrdma_main.c b/hw/rdma/vmw/pvrdma_main.c index 3ed7409763..0b46561bad 100644 --- a/hw/rdma/vmw/pvrdma_main.c +++ b/hw/rdma/vmw/pvrdma_main.c @@ -24,6 +24,9 @@ #include "hw/qdev-properties.h" #include "cpu.h" #include "trace.h" +#include "sysemu/sysemu.h" +#include "monitor/monitor.h" +#include "hw/rdma/rdma.h" #include "../rdma_rm.h" #include "../rdma_backend.h" @@ -36,13 +39,12 @@ #include "pvrdma_qp_ops.h" static Property pvrdma_dev_properties[] = { - DEFINE_PROP_STRING("backend-dev", PVRDMADev, backend_device_name), - DEFINE_PROP_UINT8("backend-port", PVRDMADev, backend_port_num, 1), - DEFINE_PROP_UINT8("backend-gid-idx", PVRDMADev, backend_gid_idx, 0), + DEFINE_PROP_STRING("netdev", PVRDMADev, backend_eth_device_name), + DEFINE_PROP_STRING("ibdev", PVRDMADev, backend_device_name), + DEFINE_PROP_UINT8("ibport", PVRDMADev, backend_port_num, 1), DEFINE_PROP_UINT64("dev-caps-max-mr-size", PVRDMADev, dev_attr.max_mr_size, MAX_MR_SIZE), DEFINE_PROP_INT32("dev-caps-max-qp", PVRDMADev, dev_attr.max_qp, MAX_QP), - DEFINE_PROP_INT32("dev-caps-max-sge", PVRDMADev, dev_attr.max_sge, MAX_SGE), DEFINE_PROP_INT32("dev-caps-max-cq", PVRDMADev, dev_attr.max_cq, MAX_CQ), DEFINE_PROP_INT32("dev-caps-max-mr", PVRDMADev, dev_attr.max_mr, MAX_MR), DEFINE_PROP_INT32("dev-caps-max-pd", PVRDMADev, dev_attr.max_pd, MAX_PD), @@ -51,9 +53,30 @@ static Property pvrdma_dev_properties[] = { DEFINE_PROP_INT32("dev-caps-max-qp-init-rd-atom", PVRDMADev, dev_attr.max_qp_init_rd_atom, MAX_QP_INIT_RD_ATOM), DEFINE_PROP_INT32("dev-caps-max-ah", PVRDMADev, dev_attr.max_ah, MAX_AH), + DEFINE_PROP_CHR("mad-chardev", PVRDMADev, mad_chr), DEFINE_PROP_END_OF_LIST(), }; +static void pvrdma_print_statistics(Monitor *mon, RdmaProvider *obj) +{ + PVRDMADev *dev = PVRDMA_DEV(obj); + PCIDevice *pdev = PCI_DEVICE(dev); + + monitor_printf(mon, "%s, %x.%x\n", pdev->name, PCI_SLOT(pdev->devfn), + PCI_FUNC(pdev->devfn)); + monitor_printf(mon, "\tcommands : %" PRId64 "\n", + dev->stats.commands); + monitor_printf(mon, "\tregs_reads : %" PRId64 "\n", + dev->stats.regs_reads); + monitor_printf(mon, "\tregs_writes : %" PRId64 "\n", + dev->stats.regs_writes); + monitor_printf(mon, "\tuar_writes : %" PRId64 "\n", + dev->stats.uar_writes); + monitor_printf(mon, "\tinterrupts : %" PRId64 "\n", + dev->stats.interrupts); + rdma_dump_device_counters(mon, &dev->rdma_dev_res); +} + static void free_dev_ring(PCIDevice *pci_dev, PvrdmaRing *ring, void *ring_state) { @@ -68,25 +91,22 @@ static int init_dev_ring(PvrdmaRing *ring, struct pvrdma_ring **ring_state, uint64_t *dir, *tbl; int rc = 0; - pr_dbg("Initializing device ring %s\n", name); - pr_dbg("pdir_dma=0x%llx\n", (long long unsigned int)dir_addr); - pr_dbg("num_pages=%d\n", num_pages); dir = rdma_pci_dma_map(pci_dev, dir_addr, TARGET_PAGE_SIZE); if (!dir) { - pr_err("Failed to map to page directory\n"); + rdma_error_report("Failed to map to page directory (ring %s)", name); rc = -ENOMEM; goto out; } tbl = rdma_pci_dma_map(pci_dev, dir[0], TARGET_PAGE_SIZE); if (!tbl) { - pr_err("Failed to map to page table\n"); + rdma_error_report("Failed to map to page table (ring %s)", name); rc = -ENOMEM; goto out_free_dir; } *ring_state = rdma_pci_dma_map(pci_dev, tbl[0], TARGET_PAGE_SIZE); if (!*ring_state) { - pr_err("Failed to map to ring state\n"); + rdma_error_report("Failed to map to ring state (ring %s)", name); rc = -ENOMEM; goto out_free_tbl; } @@ -99,7 +119,6 @@ static int init_dev_ring(PvrdmaRing *ring, struct pvrdma_ring **ring_state, sizeof(struct pvrdma_cqne), (dma_addr_t *)&tbl[1], (dma_addr_t)num_pages - 1); if (rc) { - pr_err("Failed to initialize ring\n"); rc = -ENOMEM; goto out_free_ring_state; } @@ -154,11 +173,10 @@ static int load_dsr(PVRDMADev *dev) free_dsr(dev); /* Map to DSR */ - pr_dbg("dsr_dma=0x%llx\n", (long long unsigned int)dev->dsr_info.dma); dev->dsr_info.dsr = rdma_pci_dma_map(pci_dev, dev->dsr_info.dma, sizeof(struct pvrdma_device_shared_region)); if (!dev->dsr_info.dsr) { - pr_err("Failed to map to DSR\n"); + rdma_error_report("Failed to map to DSR"); rc = -ENOMEM; goto out; } @@ -168,21 +186,19 @@ static int load_dsr(PVRDMADev *dev) dsr = dsr_info->dsr; /* Map to command slot */ - pr_dbg("cmd_dma=0x%llx\n", (long long unsigned int)dsr->cmd_slot_dma); dsr_info->req = rdma_pci_dma_map(pci_dev, dsr->cmd_slot_dma, sizeof(union pvrdma_cmd_req)); if (!dsr_info->req) { - pr_err("Failed to map to command slot address\n"); + rdma_error_report("Failed to map to command slot address"); rc = -ENOMEM; goto out_free_dsr; } /* Map to response slot */ - pr_dbg("rsp_dma=0x%llx\n", (long long unsigned int)dsr->resp_slot_dma); dsr_info->rsp = rdma_pci_dma_map(pci_dev, dsr->resp_slot_dma, sizeof(union pvrdma_cmd_resp)); if (!dsr_info->rsp) { - pr_err("Failed to map to response slot address\n"); + rdma_error_report("Failed to map to response slot address"); rc = -ENOMEM; goto out_free_req; } @@ -192,7 +208,6 @@ static int load_dsr(PVRDMADev *dev) pci_dev, dsr->cq_ring_pages.pdir_dma, dsr->cq_ring_pages.num_pages); if (rc) { - pr_err("Failed to map to initialize CQ ring\n"); rc = -ENOMEM; goto out_free_rsp; } @@ -202,7 +217,6 @@ static int load_dsr(PVRDMADev *dev) "dev_async", pci_dev, dsr->async_ring_pages.pdir_dma, dsr->async_ring_pages.num_pages); if (rc) { - pr_err("Failed to map to initialize event ring\n"); rc = -ENOMEM; goto out_free_rsp; } @@ -229,24 +243,15 @@ static void init_dsr_dev_caps(PVRDMADev *dev) struct pvrdma_device_shared_region *dsr; if (dev->dsr_info.dsr == NULL) { - pr_err("Can't initialized DSR\n"); + rdma_error_report("Can't initialized DSR"); return; } dsr = dev->dsr_info.dsr; - dsr->caps.fw_ver = PVRDMA_FW_VERSION; - pr_dbg("fw_ver=0x%" PRIx64 "\n", dsr->caps.fw_ver); - dsr->caps.mode = PVRDMA_DEVICE_MODE_ROCE; - pr_dbg("mode=%d\n", dsr->caps.mode); - dsr->caps.gid_types |= PVRDMA_GID_TYPE_FLAG_ROCE_V1; - pr_dbg("gid_types=0x%x\n", dsr->caps.gid_types); - dsr->caps.max_uar = RDMA_BAR2_UAR_SIZE; - pr_dbg("max_uar=%d\n", dsr->caps.max_uar); - dsr->caps.max_mr_size = dev->dev_attr.max_mr_size; dsr->caps.max_qp = dev->dev_attr.max_qp; dsr->caps.max_qp_wr = dev->dev_attr.max_qp_wr; @@ -256,64 +261,113 @@ static void init_dsr_dev_caps(PVRDMADev *dev) dsr->caps.max_mr = dev->dev_attr.max_mr; dsr->caps.max_pd = dev->dev_attr.max_pd; dsr->caps.max_ah = dev->dev_attr.max_ah; - dsr->caps.gid_tbl_len = MAX_GIDS; - pr_dbg("gid_tbl_len=%d\n", dsr->caps.gid_tbl_len); - dsr->caps.sys_image_guid = 0; - pr_dbg("sys_image_guid=%" PRIx64 "\n", dsr->caps.sys_image_guid); - - dsr->caps.node_guid = cpu_to_be64(dev->node_guid); - pr_dbg("node_guid=%" PRIx64 "\n", be64_to_cpu(dsr->caps.node_guid)); - + dsr->caps.node_guid = dev->node_guid; dsr->caps.phys_port_cnt = MAX_PORTS; - pr_dbg("phys_port_cnt=%d\n", dsr->caps.phys_port_cnt); - dsr->caps.max_pkeys = MAX_PKEYS; - pr_dbg("max_pkeys=%d\n", dsr->caps.max_pkeys); - - pr_dbg("Initialized\n"); } -static void init_ports(PVRDMADev *dev, Error **errp) +static void uninit_msix(PCIDevice *pdev, int used_vectors) { + PVRDMADev *dev = PVRDMA_DEV(pdev); int i; - memset(dev->rdma_dev_res.ports, 0, sizeof(dev->rdma_dev_res.ports)); - - for (i = 0; i < MAX_PORTS; i++) { - dev->rdma_dev_res.ports[i].state = IBV_PORT_DOWN; + for (i = 0; i < used_vectors; i++) { + msix_vector_unuse(pdev, i); } + + msix_uninit(pdev, &dev->msix, &dev->msix); +} + +static int init_msix(PCIDevice *pdev) +{ + PVRDMADev *dev = PVRDMA_DEV(pdev); + int i; + int rc; + + rc = msix_init(pdev, RDMA_MAX_INTRS, &dev->msix, RDMA_MSIX_BAR_IDX, + RDMA_MSIX_TABLE, &dev->msix, RDMA_MSIX_BAR_IDX, + RDMA_MSIX_PBA, 0, NULL); + + if (rc < 0) { + rdma_error_report("Failed to initialize MSI-X"); + return rc; + } + + for (i = 0; i < RDMA_MAX_INTRS; i++) { + rc = msix_vector_use(PCI_DEVICE(dev), i); + if (rc < 0) { + rdma_error_report("Fail mark MSI-X vector %d", i); + uninit_msix(pdev, i); + return rc; + } + } + + return 0; +} + +static void pvrdma_fini(PCIDevice *pdev) +{ + PVRDMADev *dev = PVRDMA_DEV(pdev); + + notifier_remove(&dev->shutdown_notifier); + + pvrdma_qp_ops_fini(); + + rdma_backend_stop(&dev->backend_dev); + + rdma_rm_fini(&dev->rdma_dev_res, &dev->backend_dev, + dev->backend_eth_device_name); + + rdma_backend_fini(&dev->backend_dev); + + free_dsr(dev); + + if (msix_enabled(pdev)) { + uninit_msix(pdev, RDMA_MAX_INTRS); + } + + rdma_info_report("Device %s %x.%x is down", pdev->name, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); +} + +static void pvrdma_stop(PVRDMADev *dev) +{ + rdma_backend_stop(&dev->backend_dev); +} + +static void pvrdma_start(PVRDMADev *dev) +{ + rdma_backend_start(&dev->backend_dev); } static void activate_device(PVRDMADev *dev) { + pvrdma_start(dev); set_reg_val(dev, PVRDMA_REG_ERR, 0); - pr_dbg("Device activated\n"); } static int unquiesce_device(PVRDMADev *dev) { - pr_dbg("Device unquiesced\n"); return 0; } -static int reset_device(PVRDMADev *dev) +static void reset_device(PVRDMADev *dev) { - pr_dbg("Device reset complete\n"); - return 0; + pvrdma_stop(dev); } -static uint64_t regs_read(void *opaque, hwaddr addr, unsigned size) +static uint64_t pvrdma_regs_read(void *opaque, hwaddr addr, unsigned size) { PVRDMADev *dev = opaque; uint32_t val; - /* pr_dbg("addr=0x%lx, size=%d\n", addr, size); */ + dev->stats.regs_reads++; if (get_reg_val(dev, addr, &val)) { - pr_dbg("Error trying to read REG value from address 0x%x\n", - (uint32_t)addr); + rdma_error_report("Failed to read REG value from address 0x%x", + (uint32_t)addr); return -EINVAL; } @@ -322,25 +376,26 @@ static uint64_t regs_read(void *opaque, hwaddr addr, unsigned size) return val; } -static void regs_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +static void pvrdma_regs_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { PVRDMADev *dev = opaque; - /* pr_dbg("addr=0x%lx, val=0x%x, size=%d\n", addr, (uint32_t)val, size); */ + dev->stats.regs_writes++; if (set_reg_val(dev, addr, val)) { - pr_err("Fail to set REG value, addr=0x%" PRIx64 ", val=0x%" PRIx64 "\n", - addr, val); + rdma_error_report("Failed to set REG value, addr=0x%"PRIx64 ", val=0x%"PRIx64, + addr, val); return; } - trace_pvrdma_regs_write(addr, val); - switch (addr) { case PVRDMA_REG_DSRLOW: + trace_pvrdma_regs_write(addr, val, "DSRLOW", ""); dev->dsr_info.dma = val; break; case PVRDMA_REG_DSRHIGH: + trace_pvrdma_regs_write(addr, val, "DSRHIGH", ""); dev->dsr_info.dma |= val << 32; load_dsr(dev); init_dsr_dev_caps(dev); @@ -348,33 +403,37 @@ static void regs_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) case PVRDMA_REG_CTL: switch (val) { case PVRDMA_DEVICE_CTL_ACTIVATE: + trace_pvrdma_regs_write(addr, val, "CTL", "ACTIVATE"); activate_device(dev); break; case PVRDMA_DEVICE_CTL_UNQUIESCE: + trace_pvrdma_regs_write(addr, val, "CTL", "UNQUIESCE"); unquiesce_device(dev); break; case PVRDMA_DEVICE_CTL_RESET: + trace_pvrdma_regs_write(addr, val, "CTL", "URESET"); reset_device(dev); break; } - break; + break; case PVRDMA_REG_IMR: - pr_dbg("Interrupt mask=0x%" PRIx64 "\n", val); + trace_pvrdma_regs_write(addr, val, "INTR_MASK", ""); dev->interrupt_mask = val; break; case PVRDMA_REG_REQUEST: if (val == 0) { - execute_command(dev); + trace_pvrdma_regs_write(addr, val, "REQUEST", ""); + pvrdma_exec_cmd(dev); } - break; + break; default: break; } } static const MemoryRegionOps regs_ops = { - .read = regs_read, - .write = regs_write, + .read = pvrdma_regs_read, + .write = pvrdma_regs_write, .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = sizeof(uint32_t), @@ -382,48 +441,60 @@ static const MemoryRegionOps regs_ops = { }, }; -static void uar_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) +static uint64_t pvrdma_uar_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0xffffffff; +} + +static void pvrdma_uar_write(void *opaque, hwaddr addr, uint64_t val, + unsigned size) { PVRDMADev *dev = opaque; - /* pr_dbg("addr=0x%lx, val=0x%x, size=%d\n", addr, (uint32_t)val, size); */ + dev->stats.uar_writes++; switch (addr & 0xFFF) { /* Mask with 0xFFF as each UC gets page */ case PVRDMA_UAR_QP_OFFSET: - pr_dbg("UAR QP command, addr=0x%" PRIx64 ", val=0x%" PRIx64 "\n", - (uint64_t)addr, val); if (val & PVRDMA_UAR_QP_SEND) { + trace_pvrdma_uar_write(addr, val, "QP", "SEND", + val & PVRDMA_UAR_HANDLE_MASK, 0); pvrdma_qp_send(dev, val & PVRDMA_UAR_HANDLE_MASK); } if (val & PVRDMA_UAR_QP_RECV) { + trace_pvrdma_uar_write(addr, val, "QP", "RECV", + val & PVRDMA_UAR_HANDLE_MASK, 0); pvrdma_qp_recv(dev, val & PVRDMA_UAR_HANDLE_MASK); } break; case PVRDMA_UAR_CQ_OFFSET: - /* pr_dbg("UAR CQ cmd, addr=0x%x, val=0x%lx\n", (uint32_t)addr, val); */ if (val & PVRDMA_UAR_CQ_ARM) { + trace_pvrdma_uar_write(addr, val, "CQ", "ARM", + val & PVRDMA_UAR_HANDLE_MASK, + !!(val & PVRDMA_UAR_CQ_ARM_SOL)); rdma_rm_req_notify_cq(&dev->rdma_dev_res, val & PVRDMA_UAR_HANDLE_MASK, !!(val & PVRDMA_UAR_CQ_ARM_SOL)); } if (val & PVRDMA_UAR_CQ_ARM_SOL) { - pr_dbg("UAR_CQ_ARM_SOL (%" PRIx64 ")\n", - val & PVRDMA_UAR_HANDLE_MASK); + trace_pvrdma_uar_write(addr, val, "CQ", "ARMSOL - not supported", 0, + 0); } if (val & PVRDMA_UAR_CQ_POLL) { - pr_dbg("UAR_CQ_POLL (%" PRIx64 ")\n", val & PVRDMA_UAR_HANDLE_MASK); + trace_pvrdma_uar_write(addr, val, "CQ", "POLL", + val & PVRDMA_UAR_HANDLE_MASK, 0); pvrdma_cq_poll(&dev->rdma_dev_res, val & PVRDMA_UAR_HANDLE_MASK); } break; default: - pr_err("Unsupported command, addr=0x%" PRIx64 ", val=0x%" PRIx64 "\n", - addr, val); + rdma_error_report("Unsupported command, addr=0x%"PRIx64", val=0x%"PRIx64, + addr, val); break; } } static const MemoryRegionOps uar_ops = { - .write = uar_write, + .read = pvrdma_uar_read, + .write = pvrdma_uar_write, .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = sizeof(uint32_t), @@ -469,45 +540,6 @@ static void init_regs(PCIDevice *pdev) set_reg_val(dev, PVRDMA_REG_ERR, 0xFFFF); } -static void uninit_msix(PCIDevice *pdev, int used_vectors) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - int i; - - for (i = 0; i < used_vectors; i++) { - msix_vector_unuse(pdev, i); - } - - msix_uninit(pdev, &dev->msix, &dev->msix); -} - -static int init_msix(PCIDevice *pdev, Error **errp) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - int i; - int rc; - - rc = msix_init(pdev, RDMA_MAX_INTRS, &dev->msix, RDMA_MSIX_BAR_IDX, - RDMA_MSIX_TABLE, &dev->msix, RDMA_MSIX_BAR_IDX, - RDMA_MSIX_PBA, 0, NULL); - - if (rc < 0) { - error_setg(errp, "Failed to initialize MSI-X"); - return rc; - } - - for (i = 0; i < RDMA_MAX_INTRS; i++) { - rc = msix_vector_use(PCI_DEVICE(dev), i); - if (rc < 0) { - error_setg(errp, "Fail mark MSI-X vercor %d", i); - uninit_msix(pdev, i); - return rc; - } - } - - return 0; -} - static void init_dev_caps(PVRDMADev *dev) { size_t pg_tbl_bytes = TARGET_PAGE_SIZE * @@ -516,13 +548,12 @@ static void init_dev_caps(PVRDMADev *dev) sizeof(struct pvrdma_rq_wqe_hdr)); dev->dev_attr.max_qp_wr = pg_tbl_bytes / - (wr_sz + sizeof(struct pvrdma_sge) * MAX_SGE) - - TARGET_PAGE_SIZE; /* First page is ring state */ - pr_dbg("max_qp_wr=%d\n", dev->dev_attr.max_qp_wr); + (wr_sz + sizeof(struct pvrdma_sge) * + dev->dev_attr.max_sge) - TARGET_PAGE_SIZE; + /* First page is ring state ^^^^ */ dev->dev_attr.max_cqe = pg_tbl_bytes / sizeof(struct pvrdma_cqe) - TARGET_PAGE_SIZE; /* First page is ring state */ - pr_dbg("max_cqe=%d\n", dev->dev_attr.max_cqe); } static int pvrdma_check_ram_shared(Object *obj, void *opaque) @@ -536,21 +567,42 @@ static int pvrdma_check_ram_shared(Object *obj, void *opaque) return 0; } +static void pvrdma_shutdown_notifier(Notifier *n, void *opaque) +{ + PVRDMADev *dev = container_of(n, PVRDMADev, shutdown_notifier); + PCIDevice *pci_dev = PCI_DEVICE(dev); + + pvrdma_fini(pci_dev); +} + static void pvrdma_realize(PCIDevice *pdev, Error **errp) { - int rc; + int rc = 0; PVRDMADev *dev = PVRDMA_DEV(pdev); Object *memdev_root; bool ram_shared = false; + PCIDevice *func0; - pr_dbg("Initializing device %s %x.%x\n", pdev->name, - PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); + rdma_info_report("Initializing device %s %x.%x", pdev->name, + PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn)); if (TARGET_PAGE_SIZE != getpagesize()) { error_setg(errp, "Target page size must be the same as host page size"); return; } + func0 = pci_get_function_0(pdev); + /* Break if not vmxnet3 device in slot 0 */ + if (strcmp(object_get_typename(OBJECT(func0)), TYPE_VMXNET3)) { + error_setg(errp, "Device on %x.0 must be %s", PCI_SLOT(pdev->devfn), + TYPE_VMXNET3); + return; + } + dev->func0 = VMXNET3(func0); + + addrconf_addr_eui48((unsigned char *)&dev->node_guid, + (const char *)&dev->func0->conf.macaddr.a); + memdev_root = object_resolve_path("/objects", NULL); if (memdev_root) { object_child_foreach(memdev_root, pvrdma_check_ram_shared, &ram_shared); @@ -568,55 +620,39 @@ static void pvrdma_realize(PCIDevice *pdev, Error **errp) init_regs(pdev); + rc = init_msix(pdev); + if (rc) { + goto out; + } + + rc = rdma_backend_init(&dev->backend_dev, pdev, &dev->rdma_dev_res, + dev->backend_device_name, dev->backend_port_num, + &dev->dev_attr, &dev->mad_chr); + if (rc) { + goto out; + } + init_dev_caps(dev); - rc = init_msix(pdev, errp); + rc = rdma_rm_init(&dev->rdma_dev_res, &dev->dev_attr); if (rc) { goto out; } - rc = rdma_backend_init(&dev->backend_dev, &dev->rdma_dev_res, - dev->backend_device_name, dev->backend_port_num, - dev->backend_gid_idx, &dev->dev_attr, errp); - if (rc) { - goto out; - } - - rc = rdma_rm_init(&dev->rdma_dev_res, &dev->dev_attr, errp); - if (rc) { - goto out; - } - - init_ports(dev, errp); - rc = pvrdma_qp_ops_init(); if (rc) { goto out; } + memset(&dev->stats, 0, sizeof(dev->stats)); + + dev->shutdown_notifier.notify = pvrdma_shutdown_notifier; + qemu_register_shutdown_notifier(&dev->shutdown_notifier); + out: if (rc) { - error_append_hint(errp, "Device fail to load\n"); - } -} - -static void pvrdma_exit(PCIDevice *pdev) -{ - PVRDMADev *dev = PVRDMA_DEV(pdev); - - pr_dbg("Closing device %s %x.%x\n", pdev->name, PCI_SLOT(pdev->devfn), - PCI_FUNC(pdev->devfn)); - - pvrdma_qp_ops_fini(); - - rdma_rm_fini(&dev->rdma_dev_res); - - rdma_backend_fini(&dev->backend_dev); - - free_dsr(dev); - - if (msix_enabled(pdev)) { - uninit_msix(pdev, RDMA_MAX_INTRS); + pvrdma_fini(pdev); + error_append_hint(errp, "Device failed to load\n"); } } @@ -624,9 +660,9 @@ static void pvrdma_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + RdmaProviderClass *ir = INTERFACE_RDMA_PROVIDER_CLASS(klass); k->realize = pvrdma_realize; - k->exit = pvrdma_exit; k->vendor_id = PCI_VENDOR_ID_VMWARE; k->device_id = PCI_DEVICE_ID_VMWARE_PVRDMA; k->revision = 0x00; @@ -635,6 +671,8 @@ static void pvrdma_class_init(ObjectClass *klass, void *data) dc->desc = "RDMA Device"; dc->props = pvrdma_dev_properties; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + + ir->print_statistics = pvrdma_print_statistics; } static const TypeInfo pvrdma_info = { @@ -644,6 +682,7 @@ static const TypeInfo pvrdma_info = { .class_init = pvrdma_class_init, .interfaces = (InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { INTERFACE_RDMA_PROVIDER }, { } } }; diff --git a/hw/rdma/vmw/pvrdma_qp_ops.c b/hw/rdma/vmw/pvrdma_qp_ops.c index 99bb51111e..5b9786efbe 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.c +++ b/hw/rdma/vmw/pvrdma_qp_ops.c @@ -19,6 +19,8 @@ #include "../rdma_rm.h" #include "../rdma_backend.h" +#include "trace.h" + #include "pvrdma.h" #include "standard-headers/rdma/vmw_pvrdma-abi.h" #include "pvrdma_qp_ops.h" @@ -47,7 +49,7 @@ typedef struct PvrdmaRqWqe { * 3. Interrupt host */ static int pvrdma_post_cqe(PVRDMADev *dev, uint32_t cq_handle, - struct pvrdma_cqe *cqe) + struct pvrdma_cqe *cqe, struct ibv_wc *wc) { struct pvrdma_cqe *cqe1; struct pvrdma_cqne *cqne; @@ -55,30 +57,34 @@ static int pvrdma_post_cqe(PVRDMADev *dev, uint32_t cq_handle, RdmaRmCQ *cq = rdma_rm_get_cq(&dev->rdma_dev_res, cq_handle); if (unlikely(!cq)) { - pr_dbg("Invalid cqn %d\n", cq_handle); return -EINVAL; } ring = (PvrdmaRing *)cq->opaque; - pr_dbg("ring=%p\n", ring); /* Step #1: Put CQE on CQ ring */ - pr_dbg("Writing CQE\n"); cqe1 = pvrdma_ring_next_elem_write(ring); if (unlikely(!cqe1)) { return -EINVAL; } + memset(cqe1, 0, sizeof(*cqe1)); cqe1->wr_id = cqe->wr_id; cqe1->qp = cqe->qp; cqe1->opcode = cqe->opcode; - cqe1->status = cqe->status; - cqe1->vendor_err = cqe->vendor_err; + cqe1->status = wc->status; + cqe1->byte_len = wc->byte_len; + cqe1->src_qp = wc->src_qp; + cqe1->wc_flags = wc->wc_flags; + cqe1->vendor_err = wc->vendor_err; + + trace_pvrdma_post_cqe(cq_handle, cq->notify, cqe1->wr_id, cqe1->qp, + cqe1->opcode, cqe1->status, cqe1->byte_len, + cqe1->src_qp, cqe1->wc_flags, cqe1->vendor_err); pvrdma_ring_write_inc(ring); /* Step #2: Put CQ number on dsr completion ring */ - pr_dbg("Writing CQNE\n"); cqne = pvrdma_ring_next_elem_write(&dev->dsr_info.cq); if (unlikely(!cqne)) { return -EINVAL; @@ -87,30 +93,35 @@ static int pvrdma_post_cqe(PVRDMADev *dev, uint32_t cq_handle, cqne->info = cq_handle; pvrdma_ring_write_inc(&dev->dsr_info.cq); - pr_dbg("cq->notify=%d\n", cq->notify); - if (cq->notify) { - cq->notify = false; + if (cq->notify != CNT_CLEAR) { + if (cq->notify == CNT_ARM) { + cq->notify = CNT_CLEAR; + } post_interrupt(dev, INTR_VEC_CMD_COMPLETION_Q); } return 0; } -static void pvrdma_qp_ops_comp_handler(int status, unsigned int vendor_err, - void *ctx) +static void pvrdma_qp_ops_comp_handler(void *ctx, struct ibv_wc *wc) { CompHandlerCtx *comp_ctx = (CompHandlerCtx *)ctx; - pr_dbg("cq_handle=%d\n", comp_ctx->cq_handle); - pr_dbg("wr_id=%" PRIx64 "\n", comp_ctx->cqe.wr_id); - pr_dbg("status=%d\n", status); - pr_dbg("vendor_err=0x%x\n", vendor_err); - comp_ctx->cqe.status = status; - comp_ctx->cqe.vendor_err = vendor_err; - pvrdma_post_cqe(comp_ctx->dev, comp_ctx->cq_handle, &comp_ctx->cqe); + pvrdma_post_cqe(comp_ctx->dev, comp_ctx->cq_handle, &comp_ctx->cqe, wc); + g_free(ctx); } +static void complete_with_error(uint32_t vendor_err, void *ctx) +{ + struct ibv_wc wc = {}; + + wc.status = IBV_WC_GENERAL_ERR; + wc.vendor_err = vendor_err; + + pvrdma_qp_ops_comp_handler(ctx, &wc); +} + void pvrdma_qp_ops_fini(void) { rdma_backend_unregister_comp_handler(); @@ -123,38 +134,61 @@ int pvrdma_qp_ops_init(void) return 0; } -int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) +void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) { RdmaRmQP *qp; PvrdmaSqWqe *wqe; PvrdmaRing *ring; - - pr_dbg("qp_handle=%d\n", qp_handle); + int sgid_idx; + union ibv_gid *sgid; qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle); if (unlikely(!qp)) { - return -EINVAL; + return; } ring = (PvrdmaRing *)qp->opaque; - pr_dbg("sring=%p\n", ring); wqe = (struct PvrdmaSqWqe *)pvrdma_ring_next_elem_read(ring); while (wqe) { CompHandlerCtx *comp_ctx; - pr_dbg("wr_id=%" PRIx64 "\n", wqe->hdr.wr_id); - /* Prepare CQE */ comp_ctx = g_malloc(sizeof(CompHandlerCtx)); comp_ctx->dev = dev; comp_ctx->cq_handle = qp->send_cq_handle; comp_ctx->cqe.wr_id = wqe->hdr.wr_id; comp_ctx->cqe.qp = qp_handle; - comp_ctx->cqe.opcode = wqe->hdr.opcode; + comp_ctx->cqe.opcode = IBV_WC_SEND; + + sgid = rdma_rm_get_gid(&dev->rdma_dev_res, wqe->hdr.wr.ud.av.gid_index); + if (!sgid) { + rdma_error_report("Failed to get gid for idx %d", + wqe->hdr.wr.ud.av.gid_index); + complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx); + continue; + } + + sgid_idx = rdma_rm_get_backend_gid_index(&dev->rdma_dev_res, + &dev->backend_dev, + wqe->hdr.wr.ud.av.gid_index); + if (sgid_idx <= 0) { + rdma_error_report("Failed to get bk sgid_idx for sgid_idx %d", + wqe->hdr.wr.ud.av.gid_index); + complete_with_error(VENDOR_ERR_INV_GID_IDX, comp_ctx); + continue; + } + + if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { + rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, + dev->dev_attr.max_sge); + complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); + continue; + } rdma_backend_post_send(&dev->backend_dev, &qp->backend_qp, qp->qp_type, (struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge, + sgid_idx, sgid, (union ibv_gid *)wqe->hdr.wr.ud.av.dgid, wqe->hdr.wr.ud.remote_qpn, wqe->hdr.wr.ud.remote_qkey, comp_ctx); @@ -163,41 +197,41 @@ int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle) wqe = pvrdma_ring_next_elem_read(ring); } - - return 0; } -int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) +void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) { RdmaRmQP *qp; PvrdmaRqWqe *wqe; PvrdmaRing *ring; - pr_dbg("qp_handle=%d\n", qp_handle); - qp = rdma_rm_get_qp(&dev->rdma_dev_res, qp_handle); if (unlikely(!qp)) { - return -EINVAL; + return; } ring = &((PvrdmaRing *)qp->opaque)[1]; - pr_dbg("rring=%p\n", ring); wqe = (struct PvrdmaRqWqe *)pvrdma_ring_next_elem_read(ring); while (wqe) { CompHandlerCtx *comp_ctx; - pr_dbg("wr_id=%" PRIx64 "\n", wqe->hdr.wr_id); - /* Prepare CQE */ comp_ctx = g_malloc(sizeof(CompHandlerCtx)); comp_ctx->dev = dev; comp_ctx->cq_handle = qp->recv_cq_handle; - comp_ctx->cqe.qp = qp_handle; comp_ctx->cqe.wr_id = wqe->hdr.wr_id; + comp_ctx->cqe.qp = qp_handle; + comp_ctx->cqe.opcode = IBV_WC_RECV; - rdma_backend_post_recv(&dev->backend_dev, &dev->rdma_dev_res, - &qp->backend_qp, qp->qp_type, + if (wqe->hdr.num_sge > dev->dev_attr.max_sge) { + rdma_error_report("Invalid num_sge=%d (max %d)", wqe->hdr.num_sge, + dev->dev_attr.max_sge); + complete_with_error(VENDOR_ERR_INV_NUM_SGE, comp_ctx); + continue; + } + + rdma_backend_post_recv(&dev->backend_dev, &qp->backend_qp, qp->qp_type, (struct ibv_sge *)&wqe->sge[0], wqe->hdr.num_sge, comp_ctx); @@ -205,8 +239,6 @@ int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle) wqe = pvrdma_ring_next_elem_read(ring); } - - return 0; } void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) @@ -215,7 +247,6 @@ void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle) cq = rdma_rm_get_cq(dev_res, cq_handle); if (!cq) { - pr_dbg("Invalid CQ# %d\n", cq_handle); return; } diff --git a/hw/rdma/vmw/pvrdma_qp_ops.h b/hw/rdma/vmw/pvrdma_qp_ops.h index ac46bf7fdf..31cb48ba29 100644 --- a/hw/rdma/vmw/pvrdma_qp_ops.h +++ b/hw/rdma/vmw/pvrdma_qp_ops.h @@ -20,8 +20,8 @@ int pvrdma_qp_ops_init(void); void pvrdma_qp_ops_fini(void); -int pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle); -int pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle); +void pvrdma_qp_send(PVRDMADev *dev, uint32_t qp_handle); +void pvrdma_qp_recv(PVRDMADev *dev, uint32_t qp_handle); void pvrdma_cq_poll(RdmaDeviceResources *dev_res, uint32_t cq_handle); #endif diff --git a/hw/rdma/vmw/trace-events b/hw/rdma/vmw/trace-events index b3f9e2b19f..323fca8456 100644 --- a/hw/rdma/vmw/trace-events +++ b/hw/rdma/vmw/trace-events @@ -1,5 +1,17 @@ -# See docs/tracing.txt for syntax documentation. +# See docs/devel/tracing.txt for syntax documentation. -# hw/rdma/vmw/pvrdma_main.c -pvrdma_regs_read(uint64_t addr, uint64_t val) "regs[0x%"PRIx64"] = 0x%"PRIx64 -pvrdma_regs_write(uint64_t addr, uint64_t val) "regs[0x%"PRIx64"] = 0x%"PRIx64 +# pvrdma_main.c +pvrdma_regs_read(uint64_t addr, uint64_t val) "pvrdma.regs[0x%"PRIx64"]=0x%"PRIx64 +pvrdma_regs_write(uint64_t addr, uint64_t val, const char *reg_name, const char *val_name) "pvrdma.regs[0x%"PRIx64"]=0x%"PRIx64" (%s %s)" +pvrdma_uar_write(uint64_t addr, uint64_t val, const char *reg_name, const char *val_name, int val1, int val2) "uar[0x%"PRIx64"]=0x%"PRIx64" (cls=%s, op=%s, obj=%d, val=%d)" + +# pvrdma_cmd.c +pvrdma_map_to_pdir_host_virt(void *vfirst, void *vremaped) "mremap %p -> %p" +pvrdma_map_to_pdir_next_page(int page_idx, void *vnext, void *vremaped) "mremap [%d] %p -> %p" +pvrdma_exec_cmd(int cmd, int err) "cmd=%d, err=%d" + +# pvrdma_dev_ring.c +pvrdma_ring_next_elem_read_no_data(char *ring_name) "pvrdma_ring %s is empty" + +# pvrdma_qp_ops.c +pvrdma_post_cqe(uint32_t cq_handle, int notify, uint64_t wr_id, uint64_t qpn, uint32_t op_code, uint32_t status, uint32_t byte_len, uint32_t src_qp, uint32_t wc_flags, uint32_t vendor_err) "cq_handle=%d, notify=%d, wr_id=0x%"PRIx64", qpn=0x%"PRIx64", opcode=%d, status=%d, byte_len=%d, src_qp=%d, wc_flags=%d, vendor_err=%d" diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig new file mode 100644 index 0000000000..8674211085 --- /dev/null +++ b/hw/riscv/Kconfig @@ -0,0 +1,37 @@ +config HTIF + bool + +config HART + bool + +config SIFIVE + bool + select MSI_NONBROKEN + +config SIFIVE_E + bool + select HART + select SIFIVE + +config SIFIVE_U + bool + select CADENCE + select HART + select SIFIVE + +config SPIKE + bool + select HART + select HTIF + select SIFIVE + +config RISCV_VIRT + bool + imply PCI_DEVICES + imply TEST_DEVICES + select PCI + select HART + select SERIAL + select VIRTIO_MMIO + select PCI_EXPRESS_GENERIC_BRIDGE + select SIFIVE diff --git a/hw/riscv/Makefile.objs b/hw/riscv/Makefile.objs index 1dde01d39d..79bfb3abf9 100644 --- a/hw/riscv/Makefile.objs +++ b/hw/riscv/Makefile.objs @@ -1,11 +1,11 @@ -obj-y += riscv_htif.o -obj-y += riscv_hart.o -obj-y += sifive_e.o -obj-y += sifive_clint.o -obj-y += sifive_prci.o -obj-y += sifive_plic.o -obj-y += sifive_test.o -obj-y += sifive_u.o -obj-y += sifive_uart.o -obj-y += spike.o -obj-y += virt.o +obj-$(CONFIG_SPIKE) += riscv_htif.o +obj-$(CONFIG_HART) += riscv_hart.o +obj-$(CONFIG_SIFIVE_E) += sifive_e.o +obj-$(CONFIG_SIFIVE) += sifive_clint.o +obj-$(CONFIG_SIFIVE) += sifive_prci.o +obj-$(CONFIG_SIFIVE) += sifive_plic.o +obj-$(CONFIG_SIFIVE) += sifive_test.o +obj-$(CONFIG_SIFIVE_U) += sifive_u.o +obj-$(CONFIG_SIFIVE) += sifive_uart.o +obj-$(CONFIG_SPIKE) += spike.o +obj-$(CONFIG_RISCV_VIRT) += virt.o diff --git a/hw/riscv/sifive_clint.c b/hw/riscv/sifive_clint.c index 7cc606e065..d4c159e937 100644 --- a/hw/riscv/sifive_clint.c +++ b/hw/riscv/sifive_clint.c @@ -47,12 +47,12 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) if (cpu->env.timecmp <= rtc_r) { /* if we're setting an MTIMECMP value in the "past", immediately raise the timer interrupt */ - riscv_set_local_interrupt(cpu, MIP_MTIP, 1); + riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); return; } /* otherwise, set up the future timer interrupt */ - riscv_set_local_interrupt(cpu, MIP_MTIP, 0); + riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(0)); diff = cpu->env.timecmp - rtc_r; /* back to ns (note args switched in muldiv64) */ next = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + @@ -67,7 +67,7 @@ static void sifive_clint_write_timecmp(RISCVCPU *cpu, uint64_t value) static void sifive_clint_timer_cb(void *opaque) { RISCVCPU *cpu = opaque; - riscv_set_local_interrupt(cpu, MIP_MTIP, 1); + riscv_cpu_update_mip(cpu, MIP_MTIP, BOOL_TO_MASK(1)); } /* CPU wants to read rtc or timecmp register */ @@ -132,7 +132,7 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, if (!env) { error_report("clint: invalid timecmp hartid: %zu", hartid); } else if ((addr & 0x3) == 0) { - riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MSIP, value != 0); + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MSIP, BOOL_TO_MASK(value)); } else { error_report("clint: invalid sip write: %08x", (uint32_t)addr); } @@ -146,15 +146,15 @@ static void sifive_clint_write(void *opaque, hwaddr addr, uint64_t value, error_report("clint: invalid timecmp hartid: %zu", hartid); } else if ((addr & 0x7) == 0) { /* timecmp_lo */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp_hi = env->timecmp >> 32; sifive_clint_write_timecmp(RISCV_CPU(cpu), - timecmp << 32 | (value & 0xFFFFFFFF)); + timecmp_hi << 32 | (value & 0xFFFFFFFF)); return; } else if ((addr & 0x7) == 4) { /* timecmp_hi */ - uint64_t timecmp = env->timecmp; + uint64_t timecmp_lo = env->timecmp; sifive_clint_write_timecmp(RISCV_CPU(cpu), - value << 32 | (timecmp & 0xFFFFFFFF)); + value << 32 | (timecmp_lo & 0xFFFFFFFF)); } else { error_report("clint: invalid timecmp write: %08x", (uint32_t)addr); } diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 4577d72037..b1cd11363c 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -74,14 +74,14 @@ static const struct MemmapEntry { [SIFIVE_E_DTIM] = { 0x80000000, 0x4000 } }; -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, NULL, NULL, + if (load_elf(kernel_filename, NULL, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) < 0) { - error_report("qemu: could not load kernel '%s'", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } return kernel_entry; @@ -192,9 +192,8 @@ static void riscv_sifive_e_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_E_QSPI0].base, memmap[SIFIVE_E_QSPI0].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm0", memmap[SIFIVE_E_PWM0].base, memmap[SIFIVE_E_PWM0].size); - /* sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, - serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), - SIFIVE_E_UART1_IRQ)); */ + sifive_uart_create(sys_mem, memmap[SIFIVE_E_UART1].base, + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_E_UART1_IRQ)); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.qspi1", memmap[SIFIVE_E_QSPI1].base, memmap[SIFIVE_E_QSPI1].size); sifive_mmio_emulate(sys_mem, "riscv.sifive.e.pwm1", diff --git a/hw/riscv/sifive_plic.c b/hw/riscv/sifive_plic.c index a91aeb97ab..07a032d93d 100644 --- a/hw/riscv/sifive_plic.c +++ b/hw/riscv/sifive_plic.c @@ -22,7 +22,9 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "hw/sysbus.h" +#include "hw/pci/msi.h" #include "target/riscv/cpu.h" +#include "sysemu/sysemu.h" #include "hw/riscv/sifive_plic.h" #define RISCV_DEBUG_PLIC 0 @@ -81,36 +83,32 @@ static void sifive_plic_print_state(SiFivePLICState *plic) } } -static -void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool pending) +static uint32_t atomic_set_masked(uint32_t *a, uint32_t mask, uint32_t value) { - qemu_mutex_lock(&plic->lock); - uint32_t word = irq >> 5; - if (pending) { - plic->pending[word] |= (1 << (irq & 31)); - } else { - plic->pending[word] &= ~(1 << (irq & 31)); - } - qemu_mutex_unlock(&plic->lock); + uint32_t old, new, cmp = atomic_read(a); + + do { + old = cmp; + new = (old & ~mask) | (value & mask); + cmp = atomic_cmpxchg(a, old, new); + } while (old != cmp); + + return old; } -static -void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool claimed) +static void sifive_plic_set_pending(SiFivePLICState *plic, int irq, bool level) { - qemu_mutex_lock(&plic->lock); - uint32_t word = irq >> 5; - if (claimed) { - plic->claimed[word] |= (1 << (irq & 31)); - } else { - plic->claimed[word] &= ~(1 << (irq & 31)); - } - qemu_mutex_unlock(&plic->lock); + atomic_set_masked(&plic->pending[irq >> 5], 1 << (irq & 31), -!!level); } -static -int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) +static void sifive_plic_set_claimed(SiFivePLICState *plic, int irq, bool level) { - int i, j, count = 0; + atomic_set_masked(&plic->claimed[irq >> 5], 1 << (irq & 31), -!!level); +} + +static int sifive_plic_irqs_pending(SiFivePLICState *plic, uint32_t addrid) +{ + int i, j; for (i = 0; i < plic->bitfield_words; i++) { uint32_t pending_enabled_not_claimed = (plic->pending[i] & ~plic->claimed[i]) & @@ -123,11 +121,11 @@ int sifive_plic_num_irqs_pending(SiFivePLICState *plic, uint32_t addrid) uint32_t prio = plic->source_priority[irq]; int enabled = pending_enabled_not_claimed & (1 << j); if (enabled && prio > plic->target_priority[addrid]) { - count++; + return 1; } } } - return count; + return 0; } static void sifive_plic_update(SiFivePLICState *plic) @@ -143,13 +141,13 @@ static void sifive_plic_update(SiFivePLICState *plic) if (!env) { continue; } - int level = sifive_plic_num_irqs_pending(plic, addrid) > 0; + int level = sifive_plic_irqs_pending(plic, addrid); switch (mode) { case PLICMode_M: - riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_MEIP, level); + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_MEIP, BOOL_TO_MASK(level)); break; case PLICMode_S: - riscv_set_local_interrupt(RISCV_CPU(cpu), MIP_SEIP, level); + riscv_cpu_update_mip(RISCV_CPU(cpu), MIP_SEIP, BOOL_TO_MASK(level)); break; default: break; @@ -209,7 +207,7 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) if (addr >= plic->priority_base && /* 4 bytes per source */ addr < plic->priority_base + (plic->num_sources << 2)) { - uint32_t irq = (addr - plic->priority_base) >> 2; + uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; if (RISCV_DEBUG_PLIC) { qemu_log("plic: read priority: irq=%d priority=%d\n", irq, plic->source_priority[irq]); @@ -218,7 +216,7 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) } else if (addr >= plic->pending_base && /* 1 bit per source */ addr < plic->pending_base + (plic->num_sources >> 3)) { - uint32_t word = (addr - plic->priority_base) >> 2; + uint32_t word = (addr - plic->pending_base) >> 2; if (RISCV_DEBUG_PLIC) { qemu_log("plic: read pending: word=%d value=%d\n", word, plic->pending[word]); @@ -265,7 +263,9 @@ static uint64_t sifive_plic_read(void *opaque, hwaddr addr, unsigned size) } err: - error_report("plic: invalid register read: %08x", (uint32_t)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register read 0x%" HWADDR_PRIx "\n", + __func__, addr); return 0; } @@ -282,7 +282,7 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, if (addr >= plic->priority_base && /* 4 bytes per source */ addr < plic->priority_base + (plic->num_sources << 2)) { - uint32_t irq = (addr - plic->priority_base) >> 2; + uint32_t irq = ((addr - plic->priority_base) >> 2) + 1; plic->source_priority[irq] = value & 7; if (RISCV_DEBUG_PLIC) { qemu_log("plic: write priority: irq=%d priority=%d\n", @@ -292,7 +292,9 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, } else if (addr >= plic->pending_base && /* 1 bit per source */ addr < plic->pending_base + (plic->num_sources >> 3)) { - error_report("plic: invalid pending write: %08x", (uint32_t)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: invalid pending write: 0x%" HWADDR_PRIx "", + __func__, addr); return; } else if (addr >= plic->enable_base && /* 1 bit per source */ addr < plic->enable_base + plic->num_addrs * plic->enable_stride) @@ -342,7 +344,9 @@ static void sifive_plic_write(void *opaque, hwaddr addr, uint64_t value, } err: - error_report("plic: invalid register write: %08x", (uint32_t)addr); + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Invalid register write 0x%" HWADDR_PRIx "\n", + __func__, addr); } static const MemoryRegionOps sifive_plic_ops = { @@ -387,7 +391,7 @@ static void parse_hart_config(SiFivePLICState *plic) p = plic->hart_config; while ((c = *p++)) { if (c == ',') { - addrid += __builtin_popcount(modes); + addrid += ctpop8(modes); modes = 0; hartid++; } else { @@ -401,7 +405,7 @@ static void parse_hart_config(SiFivePLICState *plic) } } if (modes) { - addrid += __builtin_popcount(modes); + addrid += ctpop8(modes); } hartid++; @@ -435,11 +439,11 @@ static void sifive_plic_irq_request(void *opaque, int irq, int level) static void sifive_plic_realize(DeviceState *dev, Error **errp) { SiFivePLICState *plic = SIFIVE_PLIC(dev); + int i; memory_region_init_io(&plic->mmio, OBJECT(dev), &sifive_plic_ops, plic, TYPE_SIFIVE_PLIC, plic->aperture_size); parse_hart_config(plic); - qemu_mutex_init(&plic->lock); plic->bitfield_words = (plic->num_sources + 31) >> 5; plic->source_priority = g_new0(uint32_t, plic->num_sources); plic->target_priority = g_new(uint32_t, plic->num_addrs); @@ -448,6 +452,21 @@ static void sifive_plic_realize(DeviceState *dev, Error **errp) plic->enable = g_new0(uint32_t, plic->bitfield_words * plic->num_addrs); sysbus_init_mmio(SYS_BUS_DEVICE(dev), &plic->mmio); qdev_init_gpio_in(dev, sifive_plic_irq_request, plic->num_sources); + + /* We can't allow the supervisor to control SEIP as this would allow the + * supervisor to clear a pending external interrupt which will result in + * lost a interrupt in the case a PLIC is attached. The SEIP bit must be + * hardware controlled when a PLIC is attached. + */ + for (i = 0; i < smp_cpus; i++) { + RISCVCPU *cpu = RISCV_CPU(qemu_get_cpu(i)); + if (riscv_cpu_claim_interrupts(cpu, MIP_SEIP) < 0) { + error_report("SEIP already claimed"); + exit(1); + } + } + + msi_nonbroken = true; } static void sifive_plic_class_init(ObjectClass *klass, void *data) diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 59ae1ce24a..5ecc47cea3 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -65,14 +65,14 @@ static const struct MemmapEntry { #define GEM_REVISION 0x10070109 -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, NULL, NULL, + if (load_elf(kernel_filename, NULL, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) < 0) { - error_report("qemu: could not load kernel '%s'", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } return kernel_entry; @@ -85,7 +85,8 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, int cpu; uint32_t *cells; char *nodename; - uint32_t plic_phandle; + char ethclk_names[] = "pclk\0hclk\0tx_clk"; + uint32_t plic_phandle, ethclk_phandle; fdt = s->fdt = create_device_tree(&s->fdt_size); if (!fdt) { @@ -197,6 +198,17 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); + nodename = g_strdup_printf("/soc/ethclk"); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_string(fdt, nodename, "compatible", "fixed-clock"); + qemu_fdt_setprop_cell(fdt, nodename, "#clock-cells", 0x0); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + SIFIVE_U_GEM_CLOCK_FREQ); + qemu_fdt_setprop_cell(fdt, nodename, "phandle", 3); + qemu_fdt_setprop_cell(fdt, nodename, "linux,phandle", 3); + ethclk_phandle = qemu_fdt_get_phandle(fdt, nodename); + g_free(nodename); + nodename = g_strdup_printf("/soc/ethernet@%lx", (long)memmap[SIFIVE_U_GEM].base); qemu_fdt_add_subnode(fdt, nodename); @@ -208,6 +220,10 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_string(fdt, nodename, "phy-mode", "gmii"); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_GEM_IRQ); + qemu_fdt_setprop_cells(fdt, nodename, "clocks", + ethclk_phandle, ethclk_phandle, ethclk_phandle); + qemu_fdt_setprop(fdt, nodename, "clocks-names", ethclk_names, + sizeof(ethclk_names)); qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", 1); qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0); g_free(nodename); @@ -225,12 +241,16 @@ static void create_fdt(SiFiveUState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[SIFIVE_U_UART0].base, 0x0, memmap[SIFIVE_U_UART0].size); + qemu_fdt_setprop_cell(fdt, nodename, "clock-frequency", + SIFIVE_U_CLOCK_FREQ / 2); qemu_fdt_setprop_cells(fdt, nodename, "interrupt-parent", plic_phandle); - qemu_fdt_setprop_cells(fdt, nodename, "interrupts", 1); + qemu_fdt_setprop_cells(fdt, nodename, "interrupts", SIFIVE_U_UART0_IRQ); qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } g_free(nodename); } @@ -348,9 +368,8 @@ static void riscv_sifive_u_soc_realize(DeviceState *dev, Error **errp) memmap[SIFIVE_U_PLIC].size); sifive_uart_create(system_memory, memmap[SIFIVE_U_UART0].base, serial_hd(0), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART0_IRQ)); - /* sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, - serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), - SIFIVE_U_UART1_IRQ)); */ + sifive_uart_create(system_memory, memmap[SIFIVE_U_UART1].base, + serial_hd(1), qdev_get_gpio_in(DEVICE(s->plic), SIFIVE_U_UART1_IRQ)); sifive_clint_create(memmap[SIFIVE_U_CLINT].base, memmap[SIFIVE_U_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); @@ -379,7 +398,10 @@ static void riscv_sifive_u_machine_init(MachineClass *mc) { mc->desc = "RISC-V Board compatible with SiFive U SDK"; mc->init = riscv_sifive_u_init; - mc->max_cpus = 1; + /* The real hardware has 5 CPUs, but one of them is a small embedded power + * management CPU. + */ + mc->max_cpus = 4; } DEFINE_MACHINE("sifive_u", riscv_sifive_u_machine_init) diff --git a/hw/riscv/sifive_uart.c b/hw/riscv/sifive_uart.c index b0c3798cf2..3b3f94f51d 100644 --- a/hw/riscv/sifive_uart.c +++ b/hw/riscv/sifive_uart.c @@ -28,16 +28,31 @@ * Not yet implemented: * * Transmit FIFO using "qemu/fifo8.h" - * SIFIVE_UART_IE_TXWM interrupts - * SIFIVE_UART_IE_RXWM interrupts must honor fifo watermark - * Rx FIFO watermark interrupt trigger threshold - * Tx FIFO watermark interrupt trigger threshold. */ +/* Returns the state of the IP (interrupt pending) register */ +static uint64_t uart_ip(SiFiveUARTState *s) +{ + uint64_t ret = 0; + + uint64_t txcnt = SIFIVE_UART_GET_TXCNT(s->txctrl); + uint64_t rxcnt = SIFIVE_UART_GET_RXCNT(s->rxctrl); + + if (txcnt != 0) { + ret |= SIFIVE_UART_IP_TXWM; + } + if (s->rx_fifo_len > rxcnt) { + ret |= SIFIVE_UART_IP_RXWM; + } + + return ret; +} + static void update_irq(SiFiveUARTState *s) { int cond = 0; - if ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len) { + if ((s->ie & SIFIVE_UART_IE_TXWM) || + ((s->ie & SIFIVE_UART_IE_RXWM) && s->rx_fifo_len)) { cond = 1; } if (cond) { @@ -69,7 +84,7 @@ uart_read(void *opaque, hwaddr addr, unsigned int size) case SIFIVE_UART_IE: return s->ie; case SIFIVE_UART_IP: - return s->rx_fifo_len ? SIFIVE_UART_IP_RXWM : 0; + return uart_ip(s); case SIFIVE_UART_TXCTRL: return s->txctrl; case SIFIVE_UART_RXCTRL: @@ -94,6 +109,7 @@ uart_write(void *opaque, hwaddr addr, switch (addr) { case SIFIVE_UART_TXFIFO: qemu_chr_fe_write(&s->chr, &ch, 1); + update_irq(s); return; case SIFIVE_UART_IE: s->ie = val64; diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index c8c056c50b..2a000a5800 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -53,14 +53,14 @@ static const struct MemmapEntry { [SPIKE_DRAM] = { 0x80000000, 0x0 }, }; -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf_ram_sym(kernel_filename, NULL, NULL, + if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0, NULL, true, htif_symbol_callback) < 0) { - error_report("qemu: could not load kernel '%s'", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } return kernel_entry; @@ -90,7 +90,7 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/soc"); qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(fdt, "/soc", "compatible", "ucbbar,spike-bare-soc"); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); @@ -156,8 +156,10 @@ static void create_fdt(SpikeState *s, const struct MemmapEntry *memmap, g_free(cells); g_free(nodename); - qemu_fdt_add_subnode(fdt, "/chosen"); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + if (cmdline) { + qemu_fdt_add_subnode(fdt, "/chosen"); + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } } static void spike_v1_10_0_board_init(MachineState *machine) @@ -314,9 +316,7 @@ static void spike_v1_09_1_board_init(MachineState *machine) /* build config string with supplied memory size */ char *isa = riscv_isa_string(&s->soc.harts[0]); - size_t config_string_size = strlen(config_string_tmpl) + 48; - char *config_string = malloc(config_string_size); - snprintf(config_string, config_string_size, config_string_tmpl, + char *config_string = g_strdup_printf(config_string_tmpl, (uint64_t)memmap[SPIKE_CLINT].base + SIFIVE_TIME_BASE, (uint64_t)memmap[SPIKE_DRAM].base, (uint64_t)ram_size, isa, @@ -343,6 +343,8 @@ static void spike_v1_09_1_board_init(MachineState *machine) /* Core Local Interruptor (timer and IPI) */ sifive_clint_create(memmap[SPIKE_CLINT].base, memmap[SPIKE_CLINT].size, smp_cpus, SIFIVE_SIP_BASE, SIFIVE_TIMECMP_BASE, SIFIVE_TIME_BASE); + + g_free(config_string); } static void spike_v1_09_1_machine_init(MachineClass *mc) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 248bbdffd3..fc4c6b306e 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -39,6 +39,8 @@ #include "sysemu/arch_init.h" #include "sysemu/device_tree.h" #include "exec/address-spaces.h" +#include "hw/pci/pci.h" +#include "hw/pci-host/gpex.h" #include "elf.h" #include @@ -47,24 +49,27 @@ static const struct MemmapEntry { hwaddr base; hwaddr size; } virt_memmap[] = { - [VIRT_DEBUG] = { 0x0, 0x100 }, - [VIRT_MROM] = { 0x1000, 0x11000 }, - [VIRT_TEST] = { 0x100000, 0x1000 }, - [VIRT_CLINT] = { 0x2000000, 0x10000 }, - [VIRT_PLIC] = { 0xc000000, 0x4000000 }, - [VIRT_UART0] = { 0x10000000, 0x100 }, - [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, - [VIRT_DRAM] = { 0x80000000, 0x0 }, + [VIRT_DEBUG] = { 0x0, 0x100 }, + [VIRT_MROM] = { 0x1000, 0x11000 }, + [VIRT_TEST] = { 0x100000, 0x1000 }, + [VIRT_CLINT] = { 0x2000000, 0x10000 }, + [VIRT_PLIC] = { 0xc000000, 0x4000000 }, + [VIRT_UART0] = { 0x10000000, 0x100 }, + [VIRT_VIRTIO] = { 0x10001000, 0x1000 }, + [VIRT_DRAM] = { 0x80000000, 0x0 }, + [VIRT_PCIE_MMIO] = { 0x40000000, 0x40000000 }, + [VIRT_PCIE_PIO] = { 0x03000000, 0x00010000 }, + [VIRT_PCIE_ECAM] = { 0x30000000, 0x10000000 }, }; -static uint64_t load_kernel(const char *kernel_filename) +static target_ulong load_kernel(const char *kernel_filename) { uint64_t kernel_entry, kernel_high; - if (load_elf(kernel_filename, NULL, NULL, + if (load_elf(kernel_filename, NULL, NULL, NULL, &kernel_entry, NULL, &kernel_high, 0, EM_RISCV, 1, 0) < 0) { - error_report("qemu: could not load kernel '%s'", kernel_filename); + error_report("could not load kernel '%s'", kernel_filename); exit(1); } return kernel_entry; @@ -91,13 +96,58 @@ static hwaddr load_initrd(const char *filename, uint64_t mem_size, if (size == -1) { size = load_image_targphys(filename, *start, mem_size - *start); if (size == -1) { - error_report("qemu: could not load ramdisk '%s'", filename); + error_report("could not load ramdisk '%s'", filename); exit(1); } } return *start + size; } +static void create_pcie_irq_map(void *fdt, char *nodename, + uint32_t plic_phandle) +{ + int pin, dev; + uint32_t + full_irq_map[GPEX_NUM_IRQS * GPEX_NUM_IRQS * FDT_INT_MAP_WIDTH] = {}; + uint32_t *irq_map = full_irq_map; + + /* This code creates a standard swizzle of interrupts such that + * each device's first interrupt is based on it's PCI_SLOT number. + * (See pci_swizzle_map_irq_fn()) + * + * We only need one entry per interrupt in the table (not one per + * possible slot) seeing the interrupt-map-mask will allow the table + * to wrap to any number of devices. + */ + for (dev = 0; dev < GPEX_NUM_IRQS; dev++) { + int devfn = dev * 0x8; + + for (pin = 0; pin < GPEX_NUM_IRQS; pin++) { + int irq_nr = PCIE_IRQ + ((pin + PCI_SLOT(devfn)) % GPEX_NUM_IRQS); + int i = 0; + + irq_map[i] = cpu_to_be32(devfn << 8); + + i += FDT_PCI_ADDR_CELLS; + irq_map[i] = cpu_to_be32(pin + 1); + + i += FDT_PCI_INT_CELLS; + irq_map[i++] = cpu_to_be32(plic_phandle); + + i += FDT_PLIC_ADDR_CELLS; + irq_map[i] = cpu_to_be32(irq_nr); + + irq_map += FDT_INT_MAP_WIDTH; + } + } + + qemu_fdt_setprop(fdt, nodename, "interrupt-map", + full_irq_map, sizeof(full_irq_map)); + + qemu_fdt_setprop_cells(fdt, nodename, "interrupt-map-mask", + 0x1800, 0, 0, 0x7); +} + static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, uint64_t mem_size, const char *cmdline) { @@ -121,7 +171,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/soc"); qemu_fdt_setprop(fdt, "/soc", "ranges", NULL, 0); - qemu_fdt_setprop_string(fdt, "/soc", "compatible", "riscv-virtio-soc"); + qemu_fdt_setprop_string(fdt, "/soc", "compatible", "simple-bus"); qemu_fdt_setprop_cell(fdt, "/soc", "#size-cells", 0x2); qemu_fdt_setprop_cell(fdt, "/soc", "#address-cells", 0x2); @@ -203,7 +253,10 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, nodename = g_strdup_printf("/soc/interrupt-controller@%lx", (long)memmap[VIRT_PLIC].base); qemu_fdt_add_subnode(fdt, nodename); - qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", 1); + qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", + FDT_PLIC_ADDR_CELLS); + qemu_fdt_setprop_cell(fdt, nodename, "#interrupt-cells", + FDT_PLIC_INT_CELLS); qemu_fdt_setprop_string(fdt, nodename, "compatible", "riscv,plic0"); qemu_fdt_setprop(fdt, nodename, "interrupt-controller", NULL, 0); qemu_fdt_setprop(fdt, nodename, "interrupts-extended", @@ -233,6 +286,33 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, g_free(nodename); } + nodename = g_strdup_printf("/soc/pci@%lx", + (long) memmap[VIRT_PCIE_ECAM].base); + qemu_fdt_add_subnode(fdt, nodename); + qemu_fdt_setprop_cells(fdt, nodename, "#address-cells", + FDT_PCI_ADDR_CELLS); + qemu_fdt_setprop_cells(fdt, nodename, "#interrupt-cells", + FDT_PCI_INT_CELLS); + qemu_fdt_setprop_cells(fdt, nodename, "#size-cells", 0x2); + qemu_fdt_setprop_string(fdt, nodename, "compatible", + "pci-host-ecam-generic"); + qemu_fdt_setprop_string(fdt, nodename, "device_type", "pci"); + qemu_fdt_setprop_cell(fdt, nodename, "linux,pci-domain", 0); + qemu_fdt_setprop_cells(fdt, nodename, "bus-range", 0, + memmap[VIRT_PCIE_ECAM].base / + PCIE_MMCFG_SIZE_MIN - 1); + qemu_fdt_setprop(fdt, nodename, "dma-coherent", NULL, 0); + qemu_fdt_setprop_cells(fdt, nodename, "reg", 0, memmap[VIRT_PCIE_ECAM].base, + 0, memmap[VIRT_PCIE_ECAM].size); + qemu_fdt_setprop_sized_cells(fdt, nodename, "ranges", + 1, FDT_PCI_RANGE_IOPORT, 2, 0, + 2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size, + 1, FDT_PCI_RANGE_MMIO, + 2, memmap[VIRT_PCIE_MMIO].base, + 2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size); + create_pcie_irq_map(fdt, nodename, plic_phandle); + g_free(nodename); + nodename = g_strdup_printf("/test@%lx", (long)memmap[VIRT_TEST].base); qemu_fdt_add_subnode(fdt, nodename); @@ -240,6 +320,7 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_setprop_cells(fdt, nodename, "reg", 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); + g_free(nodename); nodename = g_strdup_printf("/uart@%lx", (long)memmap[VIRT_UART0].base); @@ -254,12 +335,55 @@ static void *create_fdt(RISCVVirtState *s, const struct MemmapEntry *memmap, qemu_fdt_add_subnode(fdt, "/chosen"); qemu_fdt_setprop_string(fdt, "/chosen", "stdout-path", nodename); - qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + if (cmdline) { + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", cmdline); + } g_free(nodename); return fdt; } + +static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, + hwaddr ecam_base, hwaddr ecam_size, + hwaddr mmio_base, hwaddr mmio_size, + hwaddr pio_base, + DeviceState *plic, bool link_up) +{ + DeviceState *dev; + MemoryRegion *ecam_alias, *ecam_reg; + MemoryRegion *mmio_alias, *mmio_reg; + qemu_irq irq; + int i; + + dev = qdev_create(NULL, TYPE_GPEX_HOST); + + qdev_init_nofail(dev); + + ecam_alias = g_new0(MemoryRegion, 1); + ecam_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 0); + memory_region_init_alias(ecam_alias, OBJECT(dev), "pcie-ecam", + ecam_reg, 0, ecam_size); + memory_region_add_subregion(get_system_memory(), ecam_base, ecam_alias); + + mmio_alias = g_new0(MemoryRegion, 1); + mmio_reg = sysbus_mmio_get_region(SYS_BUS_DEVICE(dev), 1); + memory_region_init_alias(mmio_alias, OBJECT(dev), "pcie-mmio", + mmio_reg, mmio_base, mmio_size); + memory_region_add_subregion(get_system_memory(), mmio_base, mmio_alias); + + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 2, pio_base); + + for (i = 0; i < GPEX_NUM_IRQS; i++) { + irq = qdev_get_gpio_in(plic, PCIE_IRQ + i); + + sysbus_connect_irq(SYS_BUS_DEVICE(dev), i, irq); + gpex_set_irq_num(GPEX_HOST(dev), i, PCIE_IRQ + i); + } + + return dev; +} + static void riscv_virt_board_init(MachineState *machine) { const struct MemmapEntry *memmap = virt_memmap; @@ -382,9 +506,19 @@ static void riscv_virt_board_init(MachineState *machine) qdev_get_gpio_in(DEVICE(s->plic), VIRTIO_IRQ + i)); } + gpex_pcie_init(system_memory, + memmap[VIRT_PCIE_ECAM].base, + memmap[VIRT_PCIE_ECAM].size, + memmap[VIRT_PCIE_MMIO].base, + memmap[VIRT_PCIE_MMIO].size, + memmap[VIRT_PCIE_PIO].base, + DEVICE(s->plic), true); + serial_mm_init(system_memory, memmap[VIRT_UART0].base, 0, qdev_get_gpio_in(DEVICE(s->plic), UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + g_free(plic_hart_config); } static void riscv_virt_board_machine_init(MachineClass *mc) diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index 3af13ea027..14882242c3 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -78,13 +78,13 @@ static int emulated_ccw_3270_cb(SubchDev *sch, CCW1 ccw) if (rc == -EIO) { /* I/O error, specific devices generate specific conditions */ - SCSW *s = &sch->curr_status.scsw; + SCHIB *schib = &sch->curr_status; sch->curr_status.scsw.dstat = SCSW_DSTAT_UNIT_CHECK; sch->sense_data[0] = 0x40; /* intervention-req */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; } @@ -98,13 +98,10 @@ static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) EmulatedCcw3270Class *ck = EMULATED_CCW_3270_GET_CLASS(dev); CcwDevice *cdev = CCW_DEVICE(ds); CCWDeviceClass *cdk = CCW_DEVICE_GET_CLASS(cdev); - DeviceState *parent = DEVICE(cdev); - BusState *qbus = qdev_get_parent_bus(parent); - VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); SubchDev *sch; Error *err = NULL; - sch = css_create_sch(cdev->devno, cbus->squash_mcss, errp); + sch = css_create_sch(cdev->devno, errp); if (!sch) { return; } diff --git a/hw/s390x/Kconfig b/hw/s390x/Kconfig new file mode 100644 index 0000000000..5e7d8a2bae --- /dev/null +++ b/hw/s390x/Kconfig @@ -0,0 +1,12 @@ +config S390_CCW_VIRTIO + bool + imply VIRTIO_PCI + imply TERMINAL3270 + imply VFIO_AP + imply VFIO_CCW + imply WDT_DIAG288 + select PCI + select S390_FLIC + select SCLPCONSOLE + select VIRTIO_CCW + select MSI_NONBROKEN diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 93282f7c59..e02ed80b68 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -5,18 +5,32 @@ obj-y += sclpquiesce.o obj-y += sclpcpu.o obj-y += ipl.o obj-y += css.o -obj-y += s390-virtio-ccw.o -obj-y += 3270-ccw.o +obj-$(CONFIG_S390_CCW_VIRTIO) += s390-virtio-ccw.o +obj-$(CONFIG_TERMINAL3270) += 3270-ccw.o +ifeq ($(CONFIG_VIRTIO_CCW),y) obj-y += virtio-ccw.o +obj-$(CONFIG_VIRTIO_SERIAL) += virtio-ccw-serial.o +obj-$(CONFIG_VIRTIO_BALLOON) += virtio-ccw-balloon.o +obj-$(CONFIG_VIRTIO_SCSI) += virtio-ccw-scsi.o +obj-$(CONFIG_VIRTIO_RNG) += virtio-ccw-rng.o +obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-ccw-crypto.o +obj-$(CONFIG_VIRTIO_GPU) += virtio-ccw-gpu.o +obj-$(CONFIG_VIRTIO_INPUT) += virtio-ccw-input.o +obj-$(CONFIG_VIRTIO_NET) += virtio-ccw-net.o +obj-$(CONFIG_VIRTIO_BLK) += virtio-ccw-blk.o +obj-$(call land,$(CONFIG_VIRTIO_9P),$(CONFIG_VIRTFS)) += virtio-ccw-9p.o +obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-ccw.o +endif obj-y += css-bridge.o obj-y += ccw-device.o -obj-$(CONFIG_PCI) += s390-pci-bus.o s390-pci-inst.o -obj-$(call lnot,$(CONFIG_PCI)) += s390-pci-stub.o +obj-y += s390-pci-bus.o s390-pci-inst.o obj-y += s390-skeys.o obj-y += s390-stattrib.o obj-y += tod.o +obj-y += tod-qemu.o obj-$(CONFIG_KVM) += tod-kvm.o -obj-$(CONFIG_TCG) += tod-qemu.o obj-$(CONFIG_KVM) += s390-skeys-kvm.o obj-$(CONFIG_KVM) += s390-stattrib-kvm.o obj-y += s390-ccw.o +obj-y += ap-device.o +obj-y += ap-bridge.o diff --git a/hw/s390x/ap-bridge.c b/hw/s390x/ap-bridge.c new file mode 100644 index 0000000000..75ad0a66b9 --- /dev/null +++ b/hw/s390x/ap-bridge.c @@ -0,0 +1,88 @@ +/* + * ap bridge + * + * Copyright 2018 IBM Corp. + * + * 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 "hw/sysbus.h" +#include "qemu/bitops.h" +#include "hw/s390x/ap-bridge.h" +#include "cpu.h" + +static char *ap_bus_get_dev_path(DeviceState *dev) +{ + /* at most one */ + return g_strdup_printf("/1"); +} + +static void ap_bus_class_init(ObjectClass *oc, void *data) +{ + BusClass *k = BUS_CLASS(oc); + + k->get_dev_path = ap_bus_get_dev_path; + /* More than one ap device does not make sense */ + k->max_dev = 1; +} + +static const TypeInfo ap_bus_info = { + .name = TYPE_AP_BUS, + .parent = TYPE_BUS, + .instance_size = 0, + .class_init = ap_bus_class_init, +}; + +void s390_init_ap(void) +{ + DeviceState *dev; + BusState *bus; + + /* If no AP instructions then no need for AP bridge */ + if (!s390_has_feat(S390_FEAT_AP)) { + return; + } + + /* Create bridge device */ + dev = qdev_create(NULL, TYPE_AP_BRIDGE); + object_property_add_child(qdev_get_machine(), TYPE_AP_BRIDGE, + OBJECT(dev), NULL); + qdev_init_nofail(dev); + + /* Create bus on bridge device */ + bus = qbus_create(TYPE_AP_BUS, dev, TYPE_AP_BUS); + + /* Enable hotplugging */ + qbus_set_hotplug_handler(bus, OBJECT(dev), &error_abort); + } + +static void ap_bridge_class_init(ObjectClass *oc, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(oc); + HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); + + hc->unplug = qdev_simple_device_unplug_cb; + set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); +} + +static const TypeInfo ap_bridge_info = { + .name = TYPE_AP_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = 0, + .class_init = ap_bridge_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + } +}; + +static void ap_register(void) +{ + type_register_static(&ap_bridge_info); + type_register_static(&ap_bus_info); +} + +type_init(ap_register) diff --git a/hw/s390x/ap-device.c b/hw/s390x/ap-device.c new file mode 100644 index 0000000000..f5ac8db968 --- /dev/null +++ b/hw/s390x/ap-device.c @@ -0,0 +1,38 @@ +/* + * Adjunct Processor (AP) matrix device + * + * Copyright 2018 IBM Corp. + * + * 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/module.h" +#include "qapi/error.h" +#include "hw/qdev.h" +#include "hw/s390x/ap-device.h" + +static void ap_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "AP device class"; + dc->hotpluggable = false; +} + +static const TypeInfo ap_device_info = { + .name = AP_DEVICE_TYPE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(APDevice), + .class_size = sizeof(DeviceClass), + .class_init = ap_class_init, + .abstract = true, +}; + +static void ap_device_register(void) +{ + type_register_static(&ap_device_info); +} + +type_init(ap_device_register) diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index a02d708239..e04d51b191 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -51,7 +51,7 @@ static void ccw_device_unplug(HotplugHandler *hotplug_dev, css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, 1, 0); - object_unparent(OBJECT(dev)); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); } static void virtual_css_bus_reset(BusState *qbus) @@ -106,10 +106,9 @@ VirtualCssBus *virtual_css_bus_init(void) /* Create bus on bridge device */ bus = qbus_create(TYPE_VIRTUAL_CSS_BUS, dev, "virtual-css"); cbus = VIRTUAL_CSS_BUS(bus); - cbus->squash_mcss = s390_get_squash_mcss(); /* Enable hotplugging */ - qbus_set_hotplug_handler(bus, dev, &error_abort); + qbus_set_hotplug_handler(bus, OBJECT(dev), &error_abort); css_register_io_adapters(CSS_IO_ADAPTER_VIRTIO, true, false, 0, &error_abort); diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 5424ea4562..8fc9e35ba5 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -695,35 +695,32 @@ void css_adapter_interrupt(CssIoAdapterType type, uint8_t isc) static void sch_handle_clear_func(SubchDev *sch) { - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; + SCHIB *schib = &sch->curr_status; int path; /* Path management: In our simple css, we always choose the only path. */ path = 0x80; /* Reset values prior to 'issuing the clear signal'. */ - p->lpum = 0; - p->pom = 0xff; - s->flags &= ~SCSW_FLAGS_MASK_PNO; + schib->pmcw.lpum = 0; + schib->pmcw.pom = 0xff; + schib->scsw.flags &= ~SCSW_FLAGS_MASK_PNO; /* We always 'attempt to issue the clear signal', and we always succeed. */ sch->channel_prog = 0x0; sch->last_cmd_valid = false; - s->ctrl &= ~SCSW_ACTL_CLEAR_PEND; - s->ctrl |= SCSW_STCTL_STATUS_PEND; + schib->scsw.ctrl &= ~SCSW_ACTL_CLEAR_PEND; + schib->scsw.ctrl |= SCSW_STCTL_STATUS_PEND; - s->dstat = 0; - s->cstat = 0; - p->lpum = path; + schib->scsw.dstat = 0; + schib->scsw.cstat = 0; + schib->pmcw.lpum = path; } static void sch_handle_halt_func(SubchDev *sch) { - - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; + SCHIB *schib = &sch->curr_status; hwaddr curr_ccw = sch->channel_prog; int path; @@ -733,37 +730,44 @@ static void sch_handle_halt_func(SubchDev *sch) /* We always 'attempt to issue the halt signal', and we always succeed. */ sch->channel_prog = 0x0; sch->last_cmd_valid = false; - s->ctrl &= ~SCSW_ACTL_HALT_PEND; - s->ctrl |= SCSW_STCTL_STATUS_PEND; + schib->scsw.ctrl &= ~SCSW_ACTL_HALT_PEND; + schib->scsw.ctrl |= SCSW_STCTL_STATUS_PEND; - if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || - !((s->ctrl & SCSW_ACTL_START_PEND) || - (s->ctrl & SCSW_ACTL_SUSP))) { - s->dstat = SCSW_DSTAT_DEVICE_END; + if ((schib->scsw.ctrl & (SCSW_ACTL_SUBCH_ACTIVE | + SCSW_ACTL_DEVICE_ACTIVE)) || + !((schib->scsw.ctrl & SCSW_ACTL_START_PEND) || + (schib->scsw.ctrl & SCSW_ACTL_SUSP))) { + schib->scsw.dstat = SCSW_DSTAT_DEVICE_END; } - if ((s->ctrl & (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) || - (s->ctrl & SCSW_ACTL_SUSP)) { - s->cpa = curr_ccw + 8; + if ((schib->scsw.ctrl & (SCSW_ACTL_SUBCH_ACTIVE | + SCSW_ACTL_DEVICE_ACTIVE)) || + (schib->scsw.ctrl & SCSW_ACTL_SUSP)) { + schib->scsw.cpa = curr_ccw + 8; } - s->cstat = 0; - p->lpum = path; + schib->scsw.cstat = 0; + schib->pmcw.lpum = path; } -static void copy_sense_id_to_guest(SenseId *dest, SenseId *src) +/* + * As the SenseId struct cannot be packed (would cause unaligned accesses), we + * have to copy the individual fields to an unstructured area using the correct + * layout (see SA22-7204-01 "Common I/O-Device Commands"). + */ +static void copy_sense_id_to_guest(uint8_t *dest, SenseId *src) { int i; - dest->reserved = src->reserved; - dest->cu_type = cpu_to_be16(src->cu_type); - dest->cu_model = src->cu_model; - dest->dev_type = cpu_to_be16(src->dev_type); - dest->dev_model = src->dev_model; - dest->unused = src->unused; - for (i = 0; i < ARRAY_SIZE(dest->ciw); i++) { - dest->ciw[i].type = src->ciw[i].type; - dest->ciw[i].command = src->ciw[i].command; - dest->ciw[i].count = cpu_to_be16(src->ciw[i].count); + dest[0] = src->reserved; + stw_be_p(dest + 1, src->cu_type); + dest[3] = src->cu_model; + stw_be_p(dest + 4, src->dev_type); + dest[6] = src->dev_model; + dest[7] = src->unused; + for (i = 0; i < ARRAY_SIZE(src->ciw); i++) { + dest[8 + i * 4] = src->ciw[i].type; + dest[9 + i * 4] = src->ciw[i].command; + stw_be_p(dest + 10 + i * 4, src->ciw[i].count); } } @@ -1044,9 +1048,10 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr, break; case CCW_CMD_SENSE_ID: { - SenseId sense_id; + /* According to SA22-7204-01, Sense-ID can store up to 256 bytes */ + uint8_t sense_id[256]; - copy_sense_id_to_guest(&sense_id, &sch->id); + copy_sense_id_to_guest(sense_id, &sch->id); /* Sense ID information is device specific. */ if (check_len) { if (ccw.count != sizeof(sense_id)) { @@ -1060,11 +1065,11 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr, * have enough place to store at least bytes 0-3. */ if (len >= 4) { - sense_id.reserved = 0xff; + sense_id[0] = 0xff; } else { - sense_id.reserved = 0; + sense_id[0] = 0; } - ccw_dstream_write_buf(&sch->cds, &sense_id, len); + ccw_dstream_write_buf(&sch->cds, sense_id, len); sch->curr_status.scsw.count = ccw_dstream_residual_count(&sch->cds); ret = 0; break; @@ -1105,9 +1110,7 @@ static int css_interpret_ccw(SubchDev *sch, hwaddr ccw_addr, static void sch_handle_start_func_virtual(SubchDev *sch) { - - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; + SCHIB *schib = &sch->curr_status; int path; int ret; bool suspend_allowed; @@ -1115,27 +1118,27 @@ static void sch_handle_start_func_virtual(SubchDev *sch) /* Path management: In our simple css, we always choose the only path. */ path = 0x80; - if (!(s->ctrl & SCSW_ACTL_SUSP)) { + if (!(schib->scsw.ctrl & SCSW_ACTL_SUSP)) { /* Start Function triggered via ssch, i.e. we have an ORB */ ORB *orb = &sch->orb; - s->cstat = 0; - s->dstat = 0; + schib->scsw.cstat = 0; + schib->scsw.dstat = 0; /* Look at the orb and try to execute the channel program. */ - p->intparm = orb->intparm; + schib->pmcw.intparm = orb->intparm; if (!(orb->lpm & path)) { /* Generate a deferred cc 3 condition. */ - s->flags |= SCSW_FLAGS_MASK_CC; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); + schib->scsw.flags |= SCSW_FLAGS_MASK_CC; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); return; } sch->ccw_fmt_1 = !!(orb->ctrl0 & ORB_CTRL0_MASK_FMT); - s->flags |= (sch->ccw_fmt_1) ? SCSW_FLAGS_MASK_FMT : 0; + schib->scsw.flags |= (sch->ccw_fmt_1) ? SCSW_FLAGS_MASK_FMT : 0; sch->ccw_no_data_cnt = 0; suspend_allowed = !!(orb->ctrl0 & ORB_CTRL0_MASK_SPND); } else { /* Start Function resumed via rsch */ - s->ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); + schib->scsw.ctrl &= ~(SCSW_ACTL_SUSP | SCSW_ACTL_RESUME_PEND); /* The channel program had been suspended before. */ suspend_allowed = true; } @@ -1148,40 +1151,40 @@ static void sch_handle_start_func_virtual(SubchDev *sch) break; case 0: /* success */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_STATUS_PEND; - s->dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; - s->cpa = sch->channel_prog + 8; + schib->scsw.dstat = SCSW_DSTAT_CHANNEL_END | SCSW_DSTAT_DEVICE_END; + schib->scsw.cpa = sch->channel_prog + 8; break; case -EIO: /* I/O errors, status depends on specific devices */ break; case -ENOSYS: /* unsupported command, generate unit check (command reject) */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->dstat = SCSW_DSTAT_UNIT_CHECK; + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.dstat = SCSW_DSTAT_UNIT_CHECK; /* Set sense bit 0 in ecw0. */ sch->sense_data[0] = 0x80; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - s->cpa = sch->channel_prog + 8; + schib->scsw.cpa = sch->channel_prog + 8; break; case -EINPROGRESS: /* channel program has been suspended */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->ctrl |= SCSW_ACTL_SUSP; + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.ctrl |= SCSW_ACTL_SUSP; break; default: /* error, generate channel program check */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->cstat = SCSW_CSTAT_PROG_CHECK; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.cstat = SCSW_CSTAT_PROG_CHECK; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; - s->cpa = sch->channel_prog + 8; + schib->scsw.cpa = sch->channel_prog + 8; break; } } while (ret == -EAGAIN); @@ -1190,14 +1193,11 @@ static void sch_handle_start_func_virtual(SubchDev *sch) static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch) { - - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; - + SCHIB *schib = &sch->curr_status; ORB *orb = &sch->orb; - if (!(s->ctrl & SCSW_ACTL_SUSP)) { + if (!(schib->scsw.ctrl & SCSW_ACTL_SUSP)) { assert(orb != NULL); - p->intparm = orb->intparm; + schib->pmcw.intparm = orb->intparm; } return s390_ccw_cmd_request(sch); } @@ -1210,14 +1210,13 @@ static IOInstEnding sch_handle_start_func_passthrough(SubchDev *sch) */ IOInstEnding do_subchannel_work_virtual(SubchDev *sch) { + SCHIB *schib = &sch->curr_status; - SCSW *s = &sch->curr_status.scsw; - - if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { + if (schib->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) { sch_handle_clear_func(sch); - } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { + } else if (schib->scsw.ctrl & SCSW_FCTL_HALT_FUNC) { sch_handle_halt_func(sch); - } else if (s->ctrl & SCSW_FCTL_START_FUNC) { + } else if (schib->scsw.ctrl & SCSW_FCTL_START_FUNC) { /* Triggered by both ssch and rsch. */ sch_handle_start_func_virtual(sch); } @@ -1228,15 +1227,15 @@ IOInstEnding do_subchannel_work_virtual(SubchDev *sch) IOInstEnding do_subchannel_work_passthrough(SubchDev *sch) { - SCSW *s = &sch->curr_status.scsw; + SCHIB *schib = &sch->curr_status; - if (s->ctrl & SCSW_FCTL_CLEAR_FUNC) { + if (schib->scsw.ctrl & SCSW_FCTL_CLEAR_FUNC) { /* TODO: Clear handling */ sch_handle_clear_func(sch); - } else if (s->ctrl & SCSW_FCTL_HALT_FUNC) { + } else if (schib->scsw.ctrl & SCSW_FCTL_HALT_FUNC) { /* TODO: Halt handling */ sch_handle_halt_func(sch); - } else if (s->ctrl & SCSW_FCTL_START_FUNC) { + } else if (schib->scsw.ctrl & SCSW_FCTL_START_FUNC) { return sch_handle_start_func_passthrough(sch); } return IOINST_CC_EXPECTED; @@ -1284,9 +1283,19 @@ void copy_scsw_to_guest(SCSW *dest, const SCSW *src) static void copy_schib_to_guest(SCHIB *dest, const SCHIB *src) { int i; + /* + * We copy the PMCW and SCSW in and out of local variables to + * avoid taking the address of members of a packed struct. + */ + PMCW src_pmcw, dest_pmcw; + SCSW src_scsw, dest_scsw; - copy_pmcw_to_guest(&dest->pmcw, &src->pmcw); - copy_scsw_to_guest(&dest->scsw, &src->scsw); + src_pmcw = src->pmcw; + copy_pmcw_to_guest(&dest_pmcw, &src_pmcw); + dest->pmcw = dest_pmcw; + src_scsw = src->scsw; + copy_scsw_to_guest(&dest_scsw, &src_scsw); + dest->scsw = dest_scsw; dest->mba = cpu_to_be64(src->mba); for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { dest->mda[i] = src->mda[i]; @@ -1333,9 +1342,19 @@ static void copy_scsw_from_guest(SCSW *dest, const SCSW *src) static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) { int i; + /* + * We copy the PMCW and SCSW in and out of local variables to + * avoid taking the address of members of a packed struct. + */ + PMCW src_pmcw, dest_pmcw; + SCSW src_scsw, dest_scsw; - copy_pmcw_from_guest(&dest->pmcw, &src->pmcw); - copy_scsw_from_guest(&dest->scsw, &src->scsw); + src_pmcw = src->pmcw; + copy_pmcw_from_guest(&dest_pmcw, &src_pmcw); + dest->pmcw = dest_pmcw; + src_scsw = src->scsw; + copy_scsw_from_guest(&dest_scsw, &src_scsw); + dest->scsw = dest_scsw; dest->mba = be64_to_cpu(src->mba); for (i = 0; i < ARRAY_SIZE(dest->mda); i++) { dest->mda[i] = src->mda[i]; @@ -1344,46 +1363,45 @@ static void copy_schib_from_guest(SCHIB *dest, const SCHIB *src) IOInstEnding css_do_msch(SubchDev *sch, const SCHIB *orig_schib) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; uint16_t oldflags; - SCHIB schib; + SCHIB schib_copy; - if (!(sch->curr_status.pmcw.flags & PMCW_FLAGS_MASK_DNV)) { + if (!(schib->pmcw.flags & PMCW_FLAGS_MASK_DNV)) { return IOINST_CC_EXPECTED; } - if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + if (schib->scsw.ctrl & SCSW_STCTL_STATUS_PEND) { return IOINST_CC_STATUS_PRESENT; } - if (s->ctrl & + if (schib->scsw.ctrl & (SCSW_FCTL_START_FUNC|SCSW_FCTL_HALT_FUNC|SCSW_FCTL_CLEAR_FUNC)) { return IOINST_CC_BUSY; } - copy_schib_from_guest(&schib, orig_schib); + copy_schib_from_guest(&schib_copy, orig_schib); /* Only update the program-modifiable fields. */ - p->intparm = schib.pmcw.intparm; - oldflags = p->flags; - p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + schib->pmcw.intparm = schib_copy.pmcw.intparm; + oldflags = schib->pmcw.flags; + schib->pmcw.flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | PMCW_FLAGS_MASK_MP); - p->flags |= schib.pmcw.flags & + schib->pmcw.flags |= schib_copy.pmcw.flags & (PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | PMCW_FLAGS_MASK_MP); - p->lpm = schib.pmcw.lpm; - p->mbi = schib.pmcw.mbi; - p->pom = schib.pmcw.pom; - p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); - p->chars |= schib.pmcw.chars & + schib->pmcw.lpm = schib_copy.pmcw.lpm; + schib->pmcw.mbi = schib_copy.pmcw.mbi; + schib->pmcw.pom = schib_copy.pmcw.pom; + schib->pmcw.chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); + schib->pmcw.chars |= schib_copy.pmcw.chars & (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); - sch->curr_status.mba = schib.mba; + schib->mba = schib_copy.mba; /* Has the channel been disabled? */ if (sch->disable_cb && (oldflags & PMCW_FLAGS_MASK_ENA) != 0 - && (p->flags & PMCW_FLAGS_MASK_ENA) == 0) { + && (schib->pmcw.flags & PMCW_FLAGS_MASK_ENA) == 0) { sch->disable_cb(sch); } return IOINST_CC_EXPECTED; @@ -1391,82 +1409,80 @@ IOInstEnding css_do_msch(SubchDev *sch, const SCHIB *orig_schib) IOInstEnding css_do_xsch(SubchDev *sch) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; - if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { + if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; } - if (s->ctrl & SCSW_CTRL_MASK_STCTL) { + if (schib->scsw.ctrl & SCSW_CTRL_MASK_STCTL) { return IOINST_CC_STATUS_PRESENT; } - if (!(s->ctrl & SCSW_CTRL_MASK_FCTL) || - ((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || - (!(s->ctrl & + if (!(schib->scsw.ctrl & SCSW_CTRL_MASK_FCTL) || + ((schib->scsw.ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || + (!(schib->scsw.ctrl & (SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP))) || - (s->ctrl & SCSW_ACTL_SUBCH_ACTIVE)) { + (schib->scsw.ctrl & SCSW_ACTL_SUBCH_ACTIVE)) { return IOINST_CC_BUSY; } /* Cancel the current operation. */ - s->ctrl &= ~(SCSW_FCTL_START_FUNC | + schib->scsw.ctrl &= ~(SCSW_FCTL_START_FUNC | SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_SUSP); sch->channel_prog = 0x0; sch->last_cmd_valid = false; - s->dstat = 0; - s->cstat = 0; + schib->scsw.dstat = 0; + schib->scsw.cstat = 0; return IOINST_CC_EXPECTED; } IOInstEnding css_do_csch(SubchDev *sch) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; - if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { + if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; } /* Trigger the clear function. */ - s->ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); - s->ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND; + schib->scsw.ctrl &= ~(SCSW_CTRL_MASK_FCTL | SCSW_CTRL_MASK_ACTL); + schib->scsw.ctrl |= SCSW_FCTL_CLEAR_FUNC | SCSW_ACTL_CLEAR_PEND; return do_subchannel_work(sch); } IOInstEnding css_do_hsch(SubchDev *sch) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; - if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { + if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; } - if (((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_STATUS_PEND) || - (s->ctrl & (SCSW_STCTL_PRIMARY | + if (((schib->scsw.ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_STATUS_PEND) || + (schib->scsw.ctrl & (SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT))) { return IOINST_CC_STATUS_PRESENT; } - if (s->ctrl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { + if (schib->scsw.ctrl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { return IOINST_CC_BUSY; } /* Trigger the halt function. */ - s->ctrl |= SCSW_FCTL_HALT_FUNC; - s->ctrl &= ~SCSW_FCTL_START_FUNC; - if (((s->ctrl & SCSW_CTRL_MASK_ACTL) == + schib->scsw.ctrl |= SCSW_FCTL_HALT_FUNC; + schib->scsw.ctrl &= ~SCSW_FCTL_START_FUNC; + if (((schib->scsw.ctrl & SCSW_CTRL_MASK_ACTL) == (SCSW_ACTL_SUBCH_ACTIVE | SCSW_ACTL_DEVICE_ACTIVE)) && - ((s->ctrl & SCSW_CTRL_MASK_STCTL) == SCSW_STCTL_INTERMEDIATE)) { - s->ctrl &= ~SCSW_STCTL_STATUS_PEND; + ((schib->scsw.ctrl & SCSW_CTRL_MASK_STCTL) == + SCSW_STCTL_INTERMEDIATE)) { + schib->scsw.ctrl &= ~SCSW_STCTL_STATUS_PEND; } - s->ctrl |= SCSW_ACTL_HALT_PEND; + schib->scsw.ctrl |= SCSW_ACTL_HALT_PEND; return do_subchannel_work(sch); } @@ -1508,18 +1524,17 @@ static void css_update_chnmon(SubchDev *sch) IOInstEnding css_do_ssch(SubchDev *sch, ORB *orb) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; - if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { + if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; } - if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + if (schib->scsw.ctrl & SCSW_STCTL_STATUS_PEND) { return IOINST_CC_STATUS_PRESENT; } - if (s->ctrl & (SCSW_FCTL_START_FUNC | + if (schib->scsw.ctrl & (SCSW_FCTL_START_FUNC | SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) { return IOINST_CC_BUSY; @@ -1532,13 +1547,13 @@ IOInstEnding css_do_ssch(SubchDev *sch, ORB *orb) sch->orb = *orb; sch->channel_prog = orb->cpa; /* Trigger the start function. */ - s->ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); - s->flags &= ~SCSW_FLAGS_MASK_PNO; + schib->scsw.ctrl |= (SCSW_FCTL_START_FUNC | SCSW_ACTL_START_PEND); + schib->scsw.flags &= ~SCSW_FLAGS_MASK_PNO; return do_subchannel_work(sch); } -static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw, +static void copy_irb_to_guest(IRB *dest, const IRB *src, const PMCW *pmcw, int *irb_len) { int i; @@ -1577,24 +1592,24 @@ static void copy_irb_to_guest(IRB *dest, const IRB *src, PMCW *pmcw, int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; + PMCW p; uint16_t stctl; IRB irb; - if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { + if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return 3; } - stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; + stctl = schib->scsw.ctrl & SCSW_CTRL_MASK_STCTL; /* Prepare the irb for the guest. */ memset(&irb, 0, sizeof(IRB)); /* Copy scsw from current status. */ - memcpy(&irb.scsw, s, sizeof(SCSW)); + irb.scsw = schib->scsw; if (stctl & SCSW_STCTL_STATUS_PEND) { - if (s->cstat & (SCSW_CSTAT_DATA_CHECK | + if (schib->scsw.cstat & (SCSW_CSTAT_DATA_CHECK | SCSW_CSTAT_CHN_CTRL_CHK | SCSW_CSTAT_INTF_CTRL_CHK)) { irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF; @@ -1603,8 +1618,8 @@ int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len) irb.esw[0] = 0x00800000; } /* If a unit check is pending, copy sense data. */ - if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && - (p->chars & PMCW_CHARS_MASK_CSENSE)) { + if ((schib->scsw.dstat & SCSW_DSTAT_UNIT_CHECK) && + (schib->pmcw.chars & PMCW_CHARS_MASK_CSENSE)) { int i; irb.scsw.flags |= SCSW_FLAGS_MASK_ESWF | SCSW_FLAGS_MASK_ECTL; @@ -1617,34 +1632,34 @@ int css_do_tsch_get_irb(SubchDev *sch, IRB *target_irb, int *irb_len) } } /* Store the irb to the guest. */ - copy_irb_to_guest(target_irb, &irb, p, irb_len); + p = schib->pmcw; + copy_irb_to_guest(target_irb, &irb, &p, irb_len); return ((stctl & SCSW_STCTL_STATUS_PEND) == 0); } void css_do_tsch_update_subch(SubchDev *sch) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; uint16_t stctl; uint16_t fctl; uint16_t actl; - stctl = s->ctrl & SCSW_CTRL_MASK_STCTL; - fctl = s->ctrl & SCSW_CTRL_MASK_FCTL; - actl = s->ctrl & SCSW_CTRL_MASK_ACTL; + stctl = schib->scsw.ctrl & SCSW_CTRL_MASK_STCTL; + fctl = schib->scsw.ctrl & SCSW_CTRL_MASK_FCTL; + actl = schib->scsw.ctrl & SCSW_CTRL_MASK_ACTL; /* Clear conditions on subchannel, if applicable. */ if (stctl & SCSW_STCTL_STATUS_PEND) { - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; if ((stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) || ((fctl & SCSW_FCTL_HALT_FUNC) && (actl & SCSW_ACTL_SUSP))) { - s->ctrl &= ~SCSW_CTRL_MASK_FCTL; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_FCTL; } if (stctl != (SCSW_STCTL_INTERMEDIATE | SCSW_STCTL_STATUS_PEND)) { - s->flags &= ~SCSW_FLAGS_MASK_PNO; - s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | + schib->scsw.flags &= ~SCSW_FLAGS_MASK_PNO; + schib->scsw.ctrl &= ~(SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_HALT_PEND | SCSW_ACTL_CLEAR_PEND | @@ -1652,20 +1667,20 @@ void css_do_tsch_update_subch(SubchDev *sch) } else { if ((actl & SCSW_ACTL_SUSP) && (fctl & SCSW_FCTL_START_FUNC)) { - s->flags &= ~SCSW_FLAGS_MASK_PNO; + schib->scsw.flags &= ~SCSW_FLAGS_MASK_PNO; if (fctl & SCSW_FCTL_HALT_FUNC) { - s->ctrl &= ~(SCSW_ACTL_RESUME_PEND | + schib->scsw.ctrl &= ~(SCSW_ACTL_RESUME_PEND | SCSW_ACTL_START_PEND | SCSW_ACTL_HALT_PEND | SCSW_ACTL_CLEAR_PEND | SCSW_ACTL_SUSP); } else { - s->ctrl &= ~SCSW_ACTL_RESUME_PEND; + schib->scsw.ctrl &= ~SCSW_ACTL_RESUME_PEND; } } } /* Clear pending sense data. */ - if (p->chars & PMCW_CHARS_MASK_CSENSE) { + if (schib->pmcw.chars & PMCW_CHARS_MASK_CSENSE) { memset(sch->sense_data, 0 , sizeof(sch->sense_data)); } } @@ -1778,20 +1793,19 @@ void css_do_schm(uint8_t mbk, int update, int dct, uint64_t mbo) IOInstEnding css_do_rsch(SubchDev *sch) { - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; - if (~(p->flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { + if (~(schib->pmcw.flags) & (PMCW_FLAGS_MASK_DNV | PMCW_FLAGS_MASK_ENA)) { return IOINST_CC_NOT_OPERATIONAL; } - if (s->ctrl & SCSW_STCTL_STATUS_PEND) { + if (schib->scsw.ctrl & SCSW_STCTL_STATUS_PEND) { return IOINST_CC_STATUS_PRESENT; } - if (((s->ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || - (s->ctrl & SCSW_ACTL_RESUME_PEND) || - (!(s->ctrl & SCSW_ACTL_SUSP))) { + if (((schib->scsw.ctrl & SCSW_CTRL_MASK_FCTL) != SCSW_FCTL_START_FUNC) || + (schib->scsw.ctrl & SCSW_ACTL_RESUME_PEND) || + (!(schib->scsw.ctrl & SCSW_ACTL_SUSP))) { return IOINST_CC_BUSY; } @@ -1800,7 +1814,7 @@ IOInstEnding css_do_rsch(SubchDev *sch) css_update_chnmon(sch); } - s->ctrl |= SCSW_ACTL_RESUME_PEND; + schib->scsw.ctrl |= SCSW_ACTL_RESUME_PEND; return do_subchannel_work(sch); } @@ -1901,28 +1915,27 @@ static int css_add_chpid(uint8_t cssid, uint8_t chpid, uint8_t type, 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; + SCHIB *schib = &sch->curr_status; int i; CssImage *css = channel_subsys.css[sch->cssid]; assert(css != NULL); - memset(p, 0, sizeof(PMCW)); - p->flags |= PMCW_FLAGS_MASK_DNV; - p->devno = sch->devno; + memset(&schib->pmcw, 0, sizeof(PMCW)); + schib->pmcw.flags |= PMCW_FLAGS_MASK_DNV; + schib->pmcw.devno = sch->devno; /* single path */ - p->pim = 0x80; - p->pom = 0xff; - p->pam = 0x80; - p->chpid[0] = chpid; + schib->pmcw.pim = 0x80; + schib->pmcw.pom = 0xff; + schib->pmcw.pam = 0x80; + schib->pmcw.chpid[0] = chpid; if (!css->chpids[chpid].in_use) { css_add_chpid(sch->cssid, chpid, type, true); } - memset(s, 0, sizeof(SCSW)); - sch->curr_status.mba = 0; - for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { - sch->curr_status.mda[i] = 0; + memset(&schib->scsw, 0, sizeof(SCSW)); + schib->mba = 0; + for (i = 0; i < ARRAY_SIZE(schib->mda); i++) { + schib->mda[i] = 0; } } @@ -2220,30 +2233,30 @@ int css_enable_mss(void) void css_reset_sch(SubchDev *sch) { - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; - if ((p->flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) { + if ((schib->pmcw.flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) { sch->disable_cb(sch); } - p->intparm = 0; - p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | + schib->pmcw.intparm = 0; + schib->pmcw.flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | PMCW_FLAGS_MASK_MP | PMCW_FLAGS_MASK_TF); - p->flags |= PMCW_FLAGS_MASK_DNV; - p->devno = sch->devno; - p->pim = 0x80; - p->lpm = p->pim; - p->pnom = 0; - p->lpum = 0; - p->mbi = 0; - p->pom = 0xff; - p->pam = 0x80; - p->chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_XMWME | + schib->pmcw.flags |= PMCW_FLAGS_MASK_DNV; + schib->pmcw.devno = sch->devno; + schib->pmcw.pim = 0x80; + schib->pmcw.lpm = schib->pmcw.pim; + schib->pmcw.pnom = 0; + schib->pmcw.lpum = 0; + schib->pmcw.mbi = 0; + schib->pmcw.pom = 0xff; + schib->pmcw.pam = 0x80; + schib->pmcw.chars &= ~(PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_XMWME | PMCW_CHARS_MASK_CSENSE); - memset(&sch->curr_status.scsw, 0, sizeof(sch->curr_status.scsw)); - sch->curr_status.mba = 0; + memset(&schib->scsw, 0, sizeof(schib->scsw)); + schib->mba = 0; sch->channel_prog = 0x0; sch->last_cmd_valid = false; @@ -2359,15 +2372,13 @@ const PropertyInfo css_devid_ro_propinfo = { .get = get_css_devid, }; -SubchDev *css_create_sch(CssDevId bus_id, bool squash_mcss, Error **errp) +SubchDev *css_create_sch(CssDevId bus_id, Error **errp) { uint16_t schid = 0; SubchDev *sch; if (bus_id.valid) { - if (squash_mcss) { - bus_id.cssid = channel_subsys.default_cssid; - } else if (!channel_subsys.css[bus_id.cssid]) { + if (!channel_subsys.css[bus_id.cssid]) { css_create_css_image(bus_id.cssid, false); } @@ -2409,7 +2420,7 @@ static int css_sch_get_chpids(SubchDev *sch, CssDevId *dev_id) FILE *fd; uint32_t chpid[8]; int i; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; fid_path = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/chpids", dev_id->cssid, dev_id->ssid, dev_id->devid); @@ -2428,8 +2439,8 @@ static int css_sch_get_chpids(SubchDev *sch, CssDevId *dev_id) return -EINVAL; } - for (i = 0; i < ARRAY_SIZE(p->chpid); i++) { - p->chpid[i] = chpid[i]; + for (i = 0; i < ARRAY_SIZE(schib->pmcw.chpid); i++) { + schib->pmcw.chpid[i] = chpid[i]; } fclose(fd); @@ -2443,7 +2454,7 @@ static int css_sch_get_path_masks(SubchDev *sch, CssDevId *dev_id) char *fid_path; FILE *fd; uint32_t pim, pam, pom; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; fid_path = g_strdup_printf("/sys/bus/css/devices/%x.%x.%04x/pimpampom", dev_id->cssid, dev_id->ssid, dev_id->devid); @@ -2460,9 +2471,9 @@ static int css_sch_get_path_masks(SubchDev *sch, CssDevId *dev_id) return -EINVAL; } - p->pim = pim; - p->pam = pam; - p->pom = pom; + schib->pmcw.pim = pim; + schib->pmcw.pam = pam; + schib->pmcw.pom = pom; fclose(fd); g_free(fid_path); @@ -2504,16 +2515,15 @@ static int css_sch_get_chpid_type(uint8_t chpid, uint32_t *type, int css_sch_build_schib(SubchDev *sch, CssDevId *dev_id) { CssImage *css = channel_subsys.css[sch->cssid]; - PMCW *p = &sch->curr_status.pmcw; - SCSW *s = &sch->curr_status.scsw; + SCHIB *schib = &sch->curr_status; uint32_t type; int i, ret; assert(css != NULL); - memset(p, 0, sizeof(PMCW)); - p->flags |= PMCW_FLAGS_MASK_DNV; + memset(&schib->pmcw, 0, sizeof(PMCW)); + schib->pmcw.flags |= PMCW_FLAGS_MASK_DNV; /* We are dealing with I/O subchannels only. */ - p->devno = sch->devno; + schib->pmcw.devno = sch->devno; /* Grab path mask from sysfs. */ ret = css_sch_get_path_masks(sch, dev_id); @@ -2528,20 +2538,20 @@ int css_sch_build_schib(SubchDev *sch, CssDevId *dev_id) } /* Build chpid type. */ - for (i = 0; i < ARRAY_SIZE(p->chpid); i++) { - if (p->chpid[i] && !css->chpids[p->chpid[i]].in_use) { - ret = css_sch_get_chpid_type(p->chpid[i], &type, dev_id); + for (i = 0; i < ARRAY_SIZE(schib->pmcw.chpid); i++) { + if (schib->pmcw.chpid[i] && !css->chpids[schib->pmcw.chpid[i]].in_use) { + ret = css_sch_get_chpid_type(schib->pmcw.chpid[i], &type, dev_id); if (ret) { return ret; } - css_add_chpid(sch->cssid, p->chpid[i], type, false); + css_add_chpid(sch->cssid, schib->pmcw.chpid[i], type, false); } } - memset(s, 0, sizeof(SCSW)); - sch->curr_status.mba = 0; - for (i = 0; i < ARRAY_SIZE(sch->curr_status.mda); i++) { - sch->curr_status.mda[i] = 0; + memset(&schib->scsw, 0, sizeof(SCSW)); + schib->mba = 0; + for (i = 0; i < ARRAY_SIZE(schib->mda); i++) { + schib->mda[i] = 0; } return 0; diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 21f64ad26a..51b272e190 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -131,7 +131,8 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) goto error; } - bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase, + bios_size = load_elf(bios_filename, NULL, + bios_translate_addr, &fwbase, &ipl->bios_start_addr, NULL, NULL, 1, EM_S390, 0, 0); if (bios_size > 0) { @@ -155,7 +156,8 @@ static void s390_ipl_realize(DeviceState *dev, Error **errp) } if (ipl->kernel) { - kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL, + kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, + &pentry, NULL, NULL, 1, EM_S390, 0, 0); if (kernel_size < 0) { kernel_size = load_image_targphys(ipl->kernel, 0, ram_size); @@ -250,8 +252,6 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) { QemuOptsList *plist = qemu_find_opts("boot-opts"); QemuOpts *opts = QTAILQ_FIRST(&plist->head); - uint8_t *flags = &ipl->qipl.qipl_flags; - uint32_t *timeout = &ipl->qipl.boot_menu_timeout; const char *tmp; unsigned long splash_time = 0; @@ -267,7 +267,7 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) case S390_IPL_TYPE_CCW: /* In the absence of -boot menu, use zipl parameters */ if (!qemu_opt_get(opts, "menu")) { - *flags |= QIPL_FLAG_BM_OPTS_ZIPL; + ipl->qipl.qipl_flags |= QIPL_FLAG_BM_OPTS_ZIPL; return; } break; @@ -284,23 +284,23 @@ static void s390_ipl_set_boot_menu(S390IPLState *ipl) return; } - *flags |= QIPL_FLAG_BM_OPTS_CMD; + ipl->qipl.qipl_flags |= QIPL_FLAG_BM_OPTS_CMD; tmp = qemu_opt_get(opts, "splash-time"); if (tmp && qemu_strtoul(tmp, NULL, 10, &splash_time)) { error_report("splash-time is invalid, forcing it to 0"); - *timeout = 0; + ipl->qipl.boot_menu_timeout = 0; return; } if (splash_time > 0xffffffff) { error_report("splash-time is too large, forcing it to max value"); - *timeout = 0xffffffff; + ipl->qipl.boot_menu_timeout = 0xffffffff; return; } - *timeout = cpu_to_be32(splash_time); + ipl->qipl.boot_menu_timeout = cpu_to_be32(splash_time); } static CcwDevice *s390_get_ccw_device(DeviceState *dev_st) @@ -436,7 +436,8 @@ static int load_netboot_image(Error **errp) goto unref_mr; } - img_size = load_elf_ram(netboot_filename, NULL, NULL, &ipl->start_addr, + img_size = load_elf_ram(netboot_filename, NULL, NULL, NULL, + &ipl->start_addr, NULL, NULL, 1, EM_S390, 0, 0, NULL, false); if (img_size < 0) { diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 4e87b89418..b3a07a12d8 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -132,15 +132,15 @@ typedef struct QemuIplParameters QemuIplParameters; struct S390IPLState { /*< private >*/ DeviceState parent_obj; + IplParameterBlock iplb; + QemuIplParameters qipl; uint64_t start_addr; uint64_t compat_start_addr; uint64_t bios_start_addr; uint64_t compat_bios_start_addr; bool enforce_bios; - IplParameterBlock iplb; bool iplb_valid; bool netboot; - QemuIplParameters qipl; /* reset related properties don't have to be migrated or reset */ enum s390_reset reset_type; int reset_cpu_index; @@ -157,6 +157,7 @@ struct S390IPLState { bool iplbext_migration; }; typedef struct S390IPLState S390IPLState; +QEMU_BUILD_BUG_MSG(offsetof(S390IPLState, iplb) & 3, "alignment of iplb wrong"); #define S390_IPL_TYPE_FCP 0x00 #define S390_IPL_TYPE_CCW 0x02 diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 214c940593..cad91ee626 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -67,8 +67,6 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) CcwDevice *ccw_dev = CCW_DEVICE(cdev); CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); DeviceState *parent = DEVICE(ccw_dev); - BusState *qbus = qdev_get_parent_bus(parent); - VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); SubchDev *sch; int ret; Error *err = NULL; @@ -78,7 +76,7 @@ static void s390_ccw_realize(S390CCWDevice *cdev, char *sysfsdev, Error **errp) goto out_err_propagate; } - sch = css_create_sch(ccw_dev->devno, cbus->squash_mcss, &err); + sch = css_create_sch(ccw_dev->devno, &err); if (!sch) { goto out_mdevid_free; } diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index e3e0ebb7f6..2d0a28d544 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -148,6 +148,25 @@ out: psccb->header.response_code = cpu_to_be16(rc); } +static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev) +{ + HotplugHandler *hotplug_ctrl; + + /* Unplug the PCI device */ + if (pbdev->pdev) { + DeviceState *pdev = DEVICE(pbdev->pdev); + + hotplug_ctrl = qdev_get_hotplug_handler(pdev); + hotplug_handler_unplug(hotplug_ctrl, pdev, &error_abort); + object_unparent(OBJECT(pdev)); + } + + /* Unplug the zPCI device */ + hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev)); + hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev), &error_abort); + object_unparent(OBJECT(pbdev)); +} + void s390_pci_sclp_deconfigure(SCCB *sccb) { IoaCfgSccb *psccb = (IoaCfgSccb *)sccb; @@ -178,8 +197,8 @@ void s390_pci_sclp_deconfigure(SCCB *sccb) pbdev->state = ZPCI_FS_STANDBY; rc = SCLP_RC_NORMAL_COMPLETION; - if (pbdev->release_timer) { - qdev_unplug(DEVICE(pbdev->pdev), NULL); + if (pbdev->unplug_requested) { + s390_pci_perform_unplug(pbdev); } } out: @@ -217,6 +236,24 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s, return NULL; } +static S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s, + PCIDevice *pci_dev) +{ + S390PCIBusDevice *pbdev; + + if (!pci_dev) { + return NULL; + } + + QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) { + if (pbdev->pdev == pci_dev) { + return pbdev; + } + } + + return NULL; +} + S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx) { return g_hash_table_lookup(s->zpci_table, &idx); @@ -660,7 +697,7 @@ void s390_pci_iommu_enable(S390PCIIOMMU *iommu) char *name = g_strdup_printf("iommu-s390-%04x", iommu->pbdev->uid); memory_region_init_iommu(&iommu->iommu_mr, sizeof(iommu->iommu_mr), TYPE_S390_IOMMU_MEMORY_REGION, OBJECT(&iommu->mr), - name, iommu->pal + 1); + name, iommu->pal - iommu->pba + 1); iommu->enabled = true; memory_region_add_subregion(&iommu->mr, 0, MEMORY_REGION(&iommu->iommu_mr)); g_free(name); @@ -692,27 +729,35 @@ static void s390_pci_iommu_free(S390pciState *s, PCIBus *bus, int32_t devfn) object_unref(OBJECT(iommu)); } -static int s390_pcihost_init(SysBusDevice *dev) +static void s390_pcihost_realize(DeviceState *dev, Error **errp) { PCIBus *b; BusState *bus; PCIHostState *phb = PCI_HOST_BRIDGE(dev); S390pciState *s = S390_PCI_HOST_BRIDGE(dev); + Error *local_err = NULL; DPRINTF("host_init\n"); - b = pci_register_root_bus(DEVICE(dev), NULL, - s390_pci_set_irq, s390_pci_map_irq, NULL, - get_system_memory(), get_system_io(), 0, 64, - TYPE_PCI_BUS); + b = pci_register_root_bus(dev, NULL, s390_pci_set_irq, s390_pci_map_irq, + NULL, get_system_memory(), get_system_io(), 0, + 64, TYPE_PCI_BUS); pci_setup_iommu(b, s390_pci_dma_iommu, s); bus = BUS(b); - qbus_set_hotplug_handler(bus, DEVICE(dev), NULL); + qbus_set_hotplug_handler(bus, OBJECT(dev), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } phb->bus = b; - s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, DEVICE(s), NULL)); - qbus_set_hotplug_handler(BUS(s->bus), DEVICE(s), NULL); + s->bus = S390_PCI_BUS(qbus_create(TYPE_S390_PCI_BUS, dev, NULL)); + qbus_set_hotplug_handler(BUS(s->bus), OBJECT(dev), &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } s->iommu_table = g_hash_table_new_full(g_int64_hash, g_int64_equal, NULL, g_free); @@ -722,9 +767,8 @@ static int s390_pcihost_init(SysBusDevice *dev) QTAILQ_INIT(&s->zpci_devs); css_register_io_adapters(CSS_IO_ADAPTER_PCI, true, false, - S390_ADAPTER_SUPPRESSIBLE, &error_abort); - - return 0; + S390_ADAPTER_SUPPRESSIBLE, &local_err); + error_propagate(errp, local_err); } static int s390_pci_msix_init(S390PCIBusDevice *pbdev) @@ -736,7 +780,6 @@ static int s390_pci_msix_init(S390PCIBusDevice *pbdev) pos = pci_find_capability(pbdev->pdev, PCI_CAP_ID_MSIX); if (!pos) { - pbdev->msix.available = false; return -1; } @@ -752,7 +795,6 @@ static int s390_pci_msix_init(S390PCIBusDevice *pbdev) pbdev->msix.pba_bar = pba & PCI_MSIX_FLAGS_BIRMASK; pbdev->msix.pba_offset = pba & ~PCI_MSIX_FLAGS_BIRMASK; pbdev->msix.entries = (ctrl & PCI_MSIX_FLAGS_QSIZE) + 1; - pbdev->msix.available = true; name = g_strdup_printf("msix-s390-%04x", pbdev->uid); memory_region_init_io(&pbdev->msix_notify_mr, OBJECT(pbdev), @@ -771,17 +813,31 @@ static void s390_pci_msix_free(S390PCIBusDevice *pbdev) } static S390PCIBusDevice *s390_pci_device_new(S390pciState *s, - const char *target) + const char *target, Error **errp) { - DeviceState *dev = NULL; + Error *local_err = NULL; + DeviceState *dev; dev = qdev_try_create(BUS(s->bus), TYPE_S390_PCI_DEVICE); if (!dev) { + error_setg(errp, "zPCI device could not be created"); return NULL; } - qdev_prop_set_string(dev, "target", target); - qdev_init_nofail(dev); + object_property_set_str(OBJECT(dev), target, "target", &local_err); + if (local_err) { + object_unparent(OBJECT(dev)); + error_propagate_prepend(errp, local_err, + "zPCI device could not be created: "); + return NULL; + } + object_property_set_bool(OBJECT(dev), true, "realized", &local_err); + if (local_err) { + object_unparent(OBJECT(dev)); + error_propagate_prepend(errp, local_err, + "zPCI device could not be created: "); + return NULL; + } return S390_PCI_DEVICE(dev); } @@ -799,52 +855,79 @@ static bool s390_pci_alloc_idx(S390pciState *s, S390PCIBusDevice *pbdev) } pbdev->idx = idx; - s->next_idx = (idx + 1) & FH_MASK_INDEX; - return true; } -static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) +static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - PCIDevice *pdev = NULL; - S390PCIBusDevice *pbdev = NULL; - S390pciState *s = s390_get_phb(); + S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); - if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { - BusState *bus; - PCIBridge *pb = PCI_BRIDGE(dev); + if (!s390_has_feat(S390_FEAT_ZPCI)) { + warn_report("Plugging a PCI/zPCI device without the 'zpci' CPU " + "feature enabled; the guest will not be able to see/use " + "this device"); + } + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { PCIDevice *pdev = PCI_DEVICE(dev); if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { error_setg(errp, "multifunction not supported in s390"); return; } + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + S390PCIBusDevice *pbdev = S390_PCI_DEVICE(dev); + if (!s390_pci_alloc_idx(s, pbdev)) { + error_setg(errp, "no slot for plugging zpci device"); + return; + } + } +} + +static void s390_pci_update_subordinate(PCIDevice *dev, uint32_t nr) +{ + uint32_t old_nr; + + pci_default_write_config(dev, PCI_SUBORDINATE_BUS, nr, 1); + while (!pci_bus_is_root(pci_get_bus(dev))) { + dev = pci_get_bus(dev)->parent_dev; + + old_nr = pci_default_read_config(dev, PCI_SUBORDINATE_BUS, 1); + if (old_nr < nr) { + pci_default_write_config(dev, PCI_SUBORDINATE_BUS, nr, 1); + } + } +} + +static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) +{ + S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); + PCIDevice *pdev = NULL; + S390PCIBusDevice *pbdev = NULL; + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { + PCIBridge *pb = PCI_BRIDGE(dev); + + pdev = PCI_DEVICE(dev); pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq); pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s); - bus = BUS(&pb->sec_bus); - qbus_set_hotplug_handler(bus, DEVICE(s), errp); + qbus_set_hotplug_handler(BUS(&pb->sec_bus), OBJECT(s), errp); if (dev->hotplugged) { - pci_default_write_config(pdev, PCI_PRIMARY_BUS, s->bus_no, 1); + pci_default_write_config(pdev, PCI_PRIMARY_BUS, + pci_dev_bus_num(pdev), 1); s->bus_no += 1; pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1); - do { - pdev = pci_get_bus(pdev)->parent_dev; - pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, - s->bus_no, 1); - } while (pci_get_bus(pdev) && pci_dev_bus_num(pdev)); + + s390_pci_update_subordinate(pdev, s->bus_no); } } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { pdev = PCI_DEVICE(dev); - if (pdev->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - error_setg(errp, "multifunction not supported in s390"); - return; - } - if (!dev->id) { /* In the case the PCI device does not define an id */ /* we generate one based on the PCI address */ @@ -856,9 +939,8 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, pbdev = s390_pci_find_dev_by_target(s, dev->id); if (!pbdev) { - pbdev = s390_pci_device_new(s, dev->id); + pbdev = s390_pci_device_new(s, dev->id, errp); if (!pbdev) { - error_setg(errp, "create zpci device failed"); return; } } @@ -881,109 +963,112 @@ static void s390_pcihost_hot_plug(HotplugHandler *hotplug_dev, } if (dev->hotplugged) { - s390_pci_generate_plug_event(HP_EVENT_RESERVED_TO_STANDBY, + s390_pci_generate_plug_event(HP_EVENT_TO_CONFIGURED , pbdev->fh, pbdev->fid); } } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { pbdev = S390_PCI_DEVICE(dev); - if (!s390_pci_alloc_idx(s, pbdev)) { - error_setg(errp, "no slot for plugging zpci device"); - return; - } + /* the allocated idx is actually getting used */ + s->next_idx = (pbdev->idx + 1) & FH_MASK_INDEX; pbdev->fh = pbdev->idx; QTAILQ_INSERT_TAIL(&s->zpci_devs, pbdev, link); g_hash_table_insert(s->zpci_table, &pbdev->idx, pbdev); + } else { + g_assert_not_reached(); } } -static void s390_pcihost_timer_cb(void *opaque) +static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp) { - S390PCIBusDevice *pbdev = opaque; - - if (pbdev->summary_ind) { - pci_dereg_irqs(pbdev); - } - if (pbdev->iommu->enabled) { - pci_dereg_ioat(pbdev->iommu); - } - - pbdev->state = ZPCI_FS_STANDBY; - s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES, - pbdev->fh, pbdev->fid); - qdev_unplug(DEVICE(pbdev), NULL); -} - -static void s390_pcihost_hot_unplug(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp) -{ - PCIDevice *pci_dev = NULL; - PCIBus *bus; - int32_t devfn; + S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); S390PCIBusDevice *pbdev = NULL; - S390pciState *s = s390_get_phb(); + + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + PCIDevice *pci_dev = PCI_DEVICE(dev); + PCIBus *bus; + int32_t devfn; + + pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev)); + g_assert(pbdev); + + s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, + pbdev->fh, pbdev->fid); + bus = pci_get_bus(pci_dev); + devfn = pci_dev->devfn; + object_property_set_bool(OBJECT(dev), false, "realized", NULL); + + s390_pci_msix_free(pbdev); + s390_pci_iommu_free(s, bus, devfn); + pbdev->pdev = NULL; + pbdev->state = ZPCI_FS_RESERVED; + } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { + pbdev = S390_PCI_DEVICE(dev); + pbdev->fid = 0; + QTAILQ_REMOVE(&s->zpci_devs, pbdev, link); + g_hash_table_remove(s->zpci_table, &pbdev->idx); + object_property_set_bool(OBJECT(dev), false, "realized", NULL); + } +} + +static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev, + DeviceState *dev, + Error **errp) +{ + S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev); + S390PCIBusDevice *pbdev; if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) { error_setg(errp, "PCI bridge hot unplug currently not supported"); - return; } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { - pci_dev = PCI_DEVICE(dev); - - QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) { - if (pbdev->pdev == pci_dev) { - break; - } - } - assert(pbdev != NULL); + /* + * Redirect the unplug request to the zPCI device and remember that + * we've checked the PCI device already (to prevent endless recursion). + */ + pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev)); + g_assert(pbdev); + pbdev->pci_unplug_request_processed = true; + qdev_unplug(DEVICE(pbdev), errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) { pbdev = S390_PCI_DEVICE(dev); - pci_dev = pbdev->pdev; - } - switch (pbdev->state) { - case ZPCI_FS_RESERVED: - goto out; - case ZPCI_FS_STANDBY: - break; - default: - s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST, - pbdev->fh, pbdev->fid); - pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, - s390_pcihost_timer_cb, - pbdev); - timer_mod(pbdev->release_timer, - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT); - return; - } + /* + * If unplug was initially requested for the zPCI device, we + * first have to redirect to the PCI device, which will in return + * redirect back to us after performing its checks (if the request + * is not blocked, e.g. because it's a PCI bridge). + */ + if (pbdev->pdev && !pbdev->pci_unplug_request_processed) { + qdev_unplug(DEVICE(pbdev->pdev), errp); + return; + } + pbdev->pci_unplug_request_processed = false; - if (pbdev->release_timer && timer_pending(pbdev->release_timer)) { - timer_del(pbdev->release_timer); - timer_free(pbdev->release_timer); - pbdev->release_timer = NULL; + switch (pbdev->state) { + case ZPCI_FS_STANDBY: + case ZPCI_FS_RESERVED: + s390_pci_perform_unplug(pbdev); + break; + default: + /* + * Allow to send multiple requests, e.g. if the guest crashed + * before releasing the device, we would not be able to send + * another request to the same VM (e.g. fresh OS). + */ + pbdev->unplug_requested = true; + s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST, + pbdev->fh, pbdev->fid); + } + } else { + g_assert_not_reached(); } - - s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED, - pbdev->fh, pbdev->fid); - bus = pci_get_bus(pci_dev); - devfn = pci_dev->devfn; - object_unparent(OBJECT(pci_dev)); - s390_pci_msix_free(pbdev); - s390_pci_iommu_free(s, bus, devfn); - pbdev->pdev = NULL; - pbdev->state = ZPCI_FS_RESERVED; -out: - pbdev->fid = 0; - QTAILQ_REMOVE(&s->zpci_devs, pbdev, link); - g_hash_table_remove(s->zpci_table, &pbdev->idx); - object_unparent(OBJECT(pbdev)); } static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, void *opaque) { S390pciState *s = opaque; - unsigned int primary = s->bus_no; - unsigned int subordinate = 0xff; PCIBus *sec_bus = NULL; if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) != @@ -992,7 +1077,7 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, } (s->bus_no)++; - pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1); + pci_default_write_config(pdev, PCI_PRIMARY_BUS, pci_dev_bus_num(pdev), 1); pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1); pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); @@ -1001,7 +1086,7 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev, return; } - pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1); + /* Assign numbers to all child bridges. The last is the highest number. */ pci_for_each_device(sec_bus, pci_bus_num(sec_bus), s390_pci_enumerate_bridge, s); pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1); @@ -1011,21 +1096,41 @@ static void s390_pcihost_reset(DeviceState *dev) { S390pciState *s = S390_PCI_HOST_BRIDGE(dev); PCIBus *bus = s->parent_obj.bus; + S390PCIBusDevice *pbdev, *next; + /* Process all pending unplug requests */ + QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) { + if (pbdev->unplug_requested) { + if (pbdev->summary_ind) { + pci_dereg_irqs(pbdev); + } + if (pbdev->iommu->enabled) { + pci_dereg_ioat(pbdev->iommu); + } + pbdev->state = ZPCI_FS_STANDBY; + s390_pci_perform_unplug(pbdev); + } + } + + /* + * When resetting a PCI bridge, the assigned numbers are set to 0. So + * on every system reset, we also have to reassign numbers. + */ s->bus_no = 0; pci_for_each_device(bus, pci_bus_num(bus), s390_pci_enumerate_bridge, s); } static void s390_pcihost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); dc->reset = s390_pcihost_reset; - k->init = s390_pcihost_init; - hc->plug = s390_pcihost_hot_plug; - hc->unplug = s390_pcihost_hot_unplug; + dc->realize = s390_pcihost_realize; + hc->pre_plug = s390_pcihost_pre_plug; + hc->plug = s390_pcihost_plug; + hc->unplug_request = s390_pcihost_unplug_request; + hc->unplug = s390_pcihost_unplug; msi_nonbroken = true; } @@ -1115,6 +1220,7 @@ static void s390_pci_device_realize(DeviceState *dev, Error **errp) } zpci->state = ZPCI_FS_RESERVED; + zpci->fmb.format = ZPCI_FMB_FORMAT; } static void s390_pci_device_reset(DeviceState *dev) @@ -1139,7 +1245,7 @@ static void s390_pci_device_reset(DeviceState *dev) pci_dereg_ioat(pbdev->iommu); } - pbdev->fmb_addr = 0; + fmb_timer_free(pbdev); } static void s390_pci_get_fid(Object *obj, Visitor *v, const char *name, @@ -1184,6 +1290,15 @@ static Property s390_pci_device_properties[] = { DEFINE_PROP_END_OF_LIST(), }; +static const VMStateDescription s390_pci_device_vmstate = { + .name = TYPE_S390_PCI_DEVICE, + /* + * TODO: add state handling here, so migration works at least with + * emulated pci devices on s390x + */ + .unmigratable = 1, +}; + static void s390_pci_device_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1194,6 +1309,7 @@ static void s390_pci_device_class_init(ObjectClass *klass, void *data) dc->bus_type = TYPE_S390_PCI_BUS; dc->realize = s390_pci_device_realize; dc->props = s390_pci_device_properties; + dc->vmsd = &s390_pci_device_vmstate; } static const TypeInfo s390_pci_device_info = { diff --git a/hw/s390x/s390-pci-bus.h b/hw/s390x/s390-pci-bus.h index 1f7f9b5814..550f3cc5e9 100644 --- a/hw/s390x/s390-pci-bus.h +++ b/hw/s390x/s390-pci-bus.h @@ -35,7 +35,6 @@ #define ZPCI_MAX_UID 0xffff #define UID_UNDEFINED 0 #define UID_CHECKING_ENABLED 0x01 -#define HOT_UNPLUG_TIMEOUT (NANOSECONDS_PER_SECOND * 60 * 5) #define S390_PCI_HOST_BRIDGE(obj) \ OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE) @@ -252,7 +251,6 @@ typedef struct ChscSeiNt2Res { } QEMU_PACKED ChscSeiNt2Res; typedef struct S390MsixInfo { - bool available; uint8_t table_bar; uint8_t pba_bar; uint16_t entries; @@ -286,6 +284,33 @@ typedef struct S390PCIIOMMUTable { S390PCIIOMMU *iommu[PCI_SLOT_MAX]; } S390PCIIOMMUTable; +/* Function Measurement Block */ +#define DEFAULT_MUI 4000 +#define UPDATE_U_BIT 0x1ULL +#define FMBK_MASK 0xfULL + +typedef struct ZpciFmbFmt0 { + uint64_t dma_rbytes; + uint64_t dma_wbytes; +} ZpciFmbFmt0; + +#define ZPCI_FMB_CNT_LD 0 +#define ZPCI_FMB_CNT_ST 1 +#define ZPCI_FMB_CNT_STB 2 +#define ZPCI_FMB_CNT_RPCIT 3 +#define ZPCI_FMB_CNT_MAX 4 + +#define ZPCI_FMB_FORMAT 0 + +typedef struct ZpciFmb { + uint32_t format; + uint32_t sample; + uint64_t last_update; + uint64_t counter[ZPCI_FMB_CNT_MAX]; + ZpciFmbFmt0 fmt0; +} ZpciFmb; +QEMU_BUILD_BUG_MSG(offsetof(ZpciFmb, fmt0) != 48, "padding in ZpciFmb"); + struct S390PCIBusDevice { DeviceState qdev; PCIDevice *pdev; @@ -297,6 +322,8 @@ struct S390PCIBusDevice { uint32_t fid; bool fid_defined; uint64_t fmb_addr; + ZpciFmb fmb; + QEMUTimer *fmb_timer; uint8_t isc; uint16_t noi; uint16_t maxstbl; @@ -307,7 +334,8 @@ struct S390PCIBusDevice { MemoryRegion msix_notify_mr; IndAddr *summary_ind; IndAddr *indicator; - QEMUTimer *release_timer; + bool pci_unplug_request_processed; + bool unplug_requested; QTAILQ_ENTRY(S390PCIBusDevice) link; }; diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 7b61367ee3..be2896232d 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -19,6 +19,7 @@ #include "exec/memory-internal.h" #include "qemu/error-report.h" #include "sysemu/hw_accel.h" +#include "hw/s390x/tod.h" #ifndef DEBUG_S390PCI_INST #define DEBUG_S390PCI_INST 0 @@ -293,7 +294,7 @@ int clp_service_call(S390CPU *cpu, uint8_t r2, uintptr_t ra) resgrp->fr = 1; stq_p(&resgrp->dasm, 0); stq_p(&resgrp->msia, ZPCI_MSI_ADDR); - stw_p(&resgrp->mui, 0); + stw_p(&resgrp->mui, DEFAULT_MUI); stw_p(&resgrp->i, 128); stw_p(&resgrp->maxstbl, 128); resgrp->version = 0; @@ -456,6 +457,8 @@ int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) return 0; } + pbdev->fmb.counter[ZPCI_FMB_CNT_LD]++; + env->regs[r1] = data; setcc(cpu, ZPCI_PCI_LS_OK); return 0; @@ -561,6 +564,8 @@ int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2, uintptr_t ra) return 0; } + pbdev->fmb.counter[ZPCI_FMB_CNT_ST]++; + setcc(cpu, ZPCI_PCI_LS_OK); return 0; } @@ -681,6 +686,7 @@ err: s390_set_status_code(env, r1, ZPCI_PCI_ST_FUNC_IN_ERR); s390_pci_generate_error_event(error, pbdev->fh, pbdev->fid, start, 0); } else { + pbdev->fmb.counter[ZPCI_FMB_CNT_RPCIT]++; setcc(cpu, ZPCI_PCI_LS_OK); } return 0; @@ -783,6 +789,8 @@ int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, } } + pbdev->fmb.counter[ZPCI_FMB_CNT_STB]++; + setcc(cpu, ZPCI_PCI_LS_OK); return 0; @@ -889,6 +897,99 @@ void pci_dereg_ioat(S390PCIIOMMU *iommu) iommu->g_iota = 0; } +void fmb_timer_free(S390PCIBusDevice *pbdev) +{ + if (pbdev->fmb_timer) { + timer_del(pbdev->fmb_timer); + timer_free(pbdev->fmb_timer); + pbdev->fmb_timer = NULL; + } + pbdev->fmb_addr = 0; + memset(&pbdev->fmb, 0, sizeof(ZpciFmb)); +} + +static int fmb_do_update(S390PCIBusDevice *pbdev, int offset, uint64_t val, + int len) +{ + MemTxResult ret; + uint64_t dst = pbdev->fmb_addr + offset; + + switch (len) { + case 8: + address_space_stq_be(&address_space_memory, dst, val, + MEMTXATTRS_UNSPECIFIED, + &ret); + break; + case 4: + address_space_stl_be(&address_space_memory, dst, val, + MEMTXATTRS_UNSPECIFIED, + &ret); + break; + case 2: + address_space_stw_be(&address_space_memory, dst, val, + MEMTXATTRS_UNSPECIFIED, + &ret); + break; + case 1: + address_space_stb(&address_space_memory, dst, val, + MEMTXATTRS_UNSPECIFIED, + &ret); + break; + default: + ret = MEMTX_ERROR; + break; + } + if (ret != MEMTX_OK) { + s390_pci_generate_error_event(ERR_EVENT_FMBA, pbdev->fh, pbdev->fid, + pbdev->fmb_addr, 0); + fmb_timer_free(pbdev); + } + + return ret; +} + +static void fmb_update(void *opaque) +{ + S390PCIBusDevice *pbdev = opaque; + int64_t t = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + int i; + + /* Update U bit */ + pbdev->fmb.last_update *= 2; + pbdev->fmb.last_update |= UPDATE_U_BIT; + if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update), + pbdev->fmb.last_update, + sizeof(pbdev->fmb.last_update))) { + return; + } + + /* Update FMB sample count */ + if (fmb_do_update(pbdev, offsetof(ZpciFmb, sample), + pbdev->fmb.sample++, + sizeof(pbdev->fmb.sample))) { + return; + } + + /* Update FMB counters */ + for (i = 0; i < ZPCI_FMB_CNT_MAX; i++) { + if (fmb_do_update(pbdev, offsetof(ZpciFmb, counter[i]), + pbdev->fmb.counter[i], + sizeof(pbdev->fmb.counter[0]))) { + return; + } + } + + /* Clear U bit and update the time */ + pbdev->fmb.last_update = time2tod(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL)); + pbdev->fmb.last_update *= 2; + if (fmb_do_update(pbdev, offsetof(ZpciFmb, last_update), + pbdev->fmb.last_update, + sizeof(pbdev->fmb.last_update))) { + return; + } + timer_mod(pbdev->fmb_timer, t + DEFAULT_MUI); +} + int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uintptr_t ra) { @@ -1018,9 +1119,35 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, s390_set_status_code(env, r1, ZPCI_MOD_ST_SEQUENCE); } break; - case ZPCI_MOD_FC_SET_MEASURE: - pbdev->fmb_addr = ldq_p(&fib.fmb_addr); + case ZPCI_MOD_FC_SET_MEASURE: { + uint64_t fmb_addr = ldq_p(&fib.fmb_addr); + + if (fmb_addr & FMBK_MASK) { + cc = ZPCI_PCI_LS_ERR; + s390_pci_generate_error_event(ERR_EVENT_FMBPRO, pbdev->fh, + pbdev->fid, fmb_addr, 0); + fmb_timer_free(pbdev); + break; + } + + if (!fmb_addr) { + /* Stop updating FMB. */ + fmb_timer_free(pbdev); + break; + } + + if (!pbdev->fmb_timer) { + pbdev->fmb_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + fmb_update, pbdev); + } else if (timer_pending(pbdev->fmb_timer)) { + /* Remove pending timer to update FMB address. */ + timer_del(pbdev->fmb_timer); + } + pbdev->fmb_addr = fmb_addr; + timer_mod(pbdev->fmb_timer, + qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + DEFAULT_MUI); break; + } default: s390_program_interrupt(&cpu->env, PGM_OPERAND, 6, ra); cc = ZPCI_PCI_LS_ERR; diff --git a/hw/s390x/s390-pci-inst.h b/hw/s390x/s390-pci-inst.h index 91c3d61f2a..fa3bf8b5aa 100644 --- a/hw/s390x/s390-pci-inst.h +++ b/hw/s390x/s390-pci-inst.h @@ -303,6 +303,7 @@ int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uintptr_t ra); int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar, uintptr_t ra); +void fmb_timer_free(S390PCIBusDevice *pbdev); #define ZPCI_IO_BAR_MIN 0 #define ZPCI_IO_BAR_MAX 5 diff --git a/hw/s390x/s390-pci-stub.c b/hw/s390x/s390-pci-stub.c deleted file mode 100644 index ad4c5a7719..0000000000 --- a/hw/s390x/s390-pci-stub.c +++ /dev/null @@ -1,77 +0,0 @@ -/* stubs for non-pci builds */ - -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "cpu.h" -#include "s390-pci-inst.h" -#include "s390-pci-bus.h" - -/* target/s390x/ioinst.c */ -int pci_chsc_sei_nt2_get_event(void *res) -{ - return 1; -} - -int pci_chsc_sei_nt2_have_event(void) -{ - return 0; -} - -/* hw/s390x/sclp.c */ -void s390_pci_sclp_configure(SCCB *sccb) -{ - sccb->h.response_code = cpu_to_be16(SCLP_RC_ADAPTER_TYPE_NOT_RECOGNIZED); -} - -void s390_pci_sclp_deconfigure(SCCB *sccb) -{ - sccb->h.response_code = cpu_to_be16(SCLP_RC_ADAPTER_TYPE_NOT_RECOGNIZED); -} - -/* target/s390x/kvm.c */ -int clp_service_call(S390CPU *cpu, uint8_t r2) -{ - return -1; -} - -int pcilg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) -{ - return -1; -} - -int pcistg_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) -{ - return -1; -} - -int stpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) -{ - return -1; -} - -int rpcit_service_call(S390CPU *cpu, uint8_t r1, uint8_t r2) -{ - return -1; -} - -int pcistb_service_call(S390CPU *cpu, uint8_t r1, uint8_t r3, uint64_t gaddr, - uint8_t ar) -{ - return -1; -} - -int mpcifc_service_call(S390CPU *cpu, uint8_t r1, uint64_t fiba, uint8_t ar) -{ - return -1; -} - -S390pciState *s390_get_phb(void) -{ - return NULL; -} - -S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s, - const char *target) -{ - return NULL; -} diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 15f7ab0e53..daac936698 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -14,7 +14,7 @@ #include "hw/boards.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-target.h" #include "qapi/qmp/qdict.h" #include "qemu/error-report.h" #include "sysemu/kvm.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 7983185d04..d11069b860 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -28,10 +28,10 @@ #include "hw/s390x/storage-keys.h" #include "hw/s390x/storage-attributes.h" #include "hw/s390x/event-facility.h" -#include "hw/compat.h" #include "ipl.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/css-bridge.h" +#include "hw/s390x/ap-bridge.h" #include "migration/register.h" #include "cpu_models.h" #include "hw/nmi.h" @@ -263,16 +263,15 @@ static void ccw_init(MachineState *machine) /* init the SIGP facility */ s390_init_sigp(); + /* create AP bridge and bus(es) */ + s390_init_ap(); + /* get a BUS */ css_bus = virtual_css_bus_init(); s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline, machine->initrd_filename, "s390-ccw.img", "s390-netboot.img", true); - /* - * We cannot easily make the pci host bridge conditional as older QEMUs - * always created it. Doing so would break migration across QEMU versions. - */ dev = qdev_create(NULL, TYPE_S390_PCI_HOST_BRIDGE); object_property_add_child(qdev_get_machine(), TYPE_S390_PCI_HOST_BRIDGE, OBJECT(dev), NULL); @@ -282,19 +281,8 @@ static void ccw_init(MachineState *machine) virtio_ccw_register_hcalls(); s390_enable_css_support(s390_cpu_addr2state(0)); - /* - * Non mcss-e enabled guests only see the devices from the default - * css, which is determined by the value of the squash_mcss property. - */ - if (css_bus->squash_mcss) { - ret = css_create_css_image(0, true); - } else { - ret = css_create_css_image(VIRTUAL_CSSID, true); - } - if (qemu_opt_get(qemu_get_machine_opts(), "s390-squash-mcss")) { - warn_report("The machine property 's390-squash-mcss' is deprecated" - " (obsoleted by lifting the cssid restrictions)."); - } + + ret = css_create_css_image(VIRTUAL_CSSID, true); assert(ret == 0); if (css_migration_enabled()) { @@ -467,6 +455,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) s390mc->ri_allowed = true; s390mc->cpu_model_allowed = true; s390mc->css_migration_enabled = true; + s390mc->hpage_1m_allowed = true; mc->init = ccw_init; mc->reset = s390_machine_reset; mc->hot_add_cpu = s390_hot_add_cpu; @@ -546,6 +535,12 @@ bool cpu_model_allowed(void) return get_machine_class()->cpu_model_allowed; } +bool hpage_1m_allowed(void) +{ + /* for "none" machine this results in true */ + return get_machine_class()->hpage_1m_allowed; +} + static char *machine_get_loadparm(Object *obj, Error **errp) { S390CcwMachineState *ms = S390_CCW_MACHINE(obj); @@ -575,21 +570,6 @@ static void machine_set_loadparm(Object *obj, const char *val, Error **errp) ms->loadparm[i] = ' '; /* pad right with spaces */ } } -static inline bool machine_get_squash_mcss(Object *obj, Error **errp) -{ - S390CcwMachineState *ms = S390_CCW_MACHINE(obj); - - return ms->s390_squash_mcss; -} - -static inline void machine_set_squash_mcss(Object *obj, bool value, - Error **errp) -{ - S390CcwMachineState *ms = S390_CCW_MACHINE(obj); - - ms->s390_squash_mcss = value; -} - static inline void s390_machine_initfn(Object *obj) { object_property_add_bool(obj, "aes-key-wrap", @@ -614,13 +594,6 @@ static inline void s390_machine_initfn(Object *obj) " to upper case) to pass to machine loader, boot manager," " and guest kernel", NULL); - object_property_add_bool(obj, "s390-squash-mcss", - machine_get_squash_mcss, - machine_set_squash_mcss, NULL); - object_property_set_description(obj, "s390-squash-mcss", "(deprecated) " - "enable/disable squashing subchannels into the default css", - NULL); - object_property_set_bool(obj, false, "s390-squash-mcss", NULL); } static const TypeInfo ccw_machine_info = { @@ -673,102 +646,45 @@ bool css_migration_enabled(void) } \ type_init(ccw_machine_register_##suffix) -#define CCW_COMPAT_2_12 \ - HW_COMPAT_2_12 +static void ccw_machine_4_0_instance_options(MachineState *machine) +{ +} -#define CCW_COMPAT_2_11 \ - HW_COMPAT_2_11 \ - {\ - .driver = TYPE_SCLP_EVENT_FACILITY,\ - .property = "allow_all_mask_sizes",\ - .value = "off",\ - }, +static void ccw_machine_4_0_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE(4_0, "4.0", true); -#define CCW_COMPAT_2_10 \ - HW_COMPAT_2_10 +static void ccw_machine_3_1_instance_options(MachineState *machine) +{ + static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V3_1 }; + ccw_machine_4_0_instance_options(machine); + s390_cpudef_featoff_greater(14, 1, S390_FEAT_MULTIPLE_EPOCH); + s390_cpudef_group_featoff_greater(14, 1, S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF); + s390_set_qemu_cpu_model(0x2827, 12, 2, qemu_cpu_feat); +} -#define CCW_COMPAT_2_9 \ - HW_COMPAT_2_9 \ - {\ - .driver = TYPE_S390_STATTRIB,\ - .property = "migration-enabled",\ - .value = "off",\ - }, - -#define CCW_COMPAT_2_8 \ - HW_COMPAT_2_8 \ - {\ - .driver = TYPE_S390_FLIC_COMMON,\ - .property = "adapter_routes_max_batch",\ - .value = "64",\ - }, - -#define CCW_COMPAT_2_7 \ - HW_COMPAT_2_7 - -#define CCW_COMPAT_2_6 \ - HW_COMPAT_2_6 \ - {\ - .driver = TYPE_S390_IPL,\ - .property = "iplbext_migration",\ - .value = "off",\ - }, {\ - .driver = TYPE_VIRTUAL_CSS_BRIDGE,\ - .property = "css_dev_path",\ - .value = "off",\ - }, - -#define CCW_COMPAT_2_5 \ - HW_COMPAT_2_5 - -#define CCW_COMPAT_2_4 \ - 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_3_1_class_options(MachineClass *mc) +{ + ccw_machine_4_0_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_3_1, hw_compat_3_1_len); +} +DEFINE_CCW_MACHINE(3_1, "3.1", false); static void ccw_machine_3_0_instance_options(MachineState *machine) { + ccw_machine_3_1_instance_options(machine); } static void ccw_machine_3_0_class_options(MachineClass *mc) { + S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + + s390mc->hpage_1m_allowed = false; + ccw_machine_3_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_3_0, hw_compat_3_0_len); } -DEFINE_CCW_MACHINE(3_0, "3.0", true); +DEFINE_CCW_MACHINE(3_0, "3.0", false); static void ccw_machine_2_12_instance_options(MachineState *machine) { @@ -780,7 +696,7 @@ static void ccw_machine_2_12_instance_options(MachineState *machine) static void ccw_machine_2_12_class_options(MachineClass *mc) { ccw_machine_3_0_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_12); + compat_props_add(mc->compat_props, hw_compat_2_12, hw_compat_2_12_len); } DEFINE_CCW_MACHINE(2_12, "2.12", false); @@ -795,8 +711,13 @@ static void ccw_machine_2_11_instance_options(MachineState *machine) static void ccw_machine_2_11_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_SCLP_EVENT_FACILITY, "allow_all_mask_sizes", "off", }, + }; + ccw_machine_2_12_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_11); + compat_props_add(mc->compat_props, hw_compat_2_11, hw_compat_2_11_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_CCW_MACHINE(2_11, "2.11", false); @@ -808,7 +729,7 @@ static void ccw_machine_2_10_instance_options(MachineState *machine) static void ccw_machine_2_10_class_options(MachineClass *mc) { ccw_machine_2_11_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_10); + compat_props_add(mc->compat_props, hw_compat_2_10, hw_compat_2_10_len); } DEFINE_CCW_MACHINE(2_10, "2.10", false); @@ -825,9 +746,13 @@ static void ccw_machine_2_9_instance_options(MachineState *machine) static void ccw_machine_2_9_class_options(MachineClass *mc) { S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_S390_STATTRIB, "migration-enabled", "off", }, + }; ccw_machine_2_10_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_9); + compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); s390mc->css_migration_enabled = false; } DEFINE_CCW_MACHINE(2_9, "2.9", false); @@ -839,8 +764,13 @@ static void ccw_machine_2_8_instance_options(MachineState *machine) static void ccw_machine_2_8_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_S390_FLIC_COMMON, "adapter_routes_max_batch", "64", }, + }; + ccw_machine_2_9_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_8); + compat_props_add(mc->compat_props, hw_compat_2_8, hw_compat_2_8_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_CCW_MACHINE(2_8, "2.8", false); @@ -855,7 +785,7 @@ static void ccw_machine_2_7_class_options(MachineClass *mc) s390mc->cpu_model_allowed = false; ccw_machine_2_8_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_7); + compat_props_add(mc->compat_props, hw_compat_2_7, hw_compat_2_7_len); } DEFINE_CCW_MACHINE(2_7, "2.7", false); @@ -867,10 +797,15 @@ static void ccw_machine_2_6_instance_options(MachineState *machine) static void ccw_machine_2_6_class_options(MachineClass *mc) { S390CcwMachineClass *s390mc = S390_MACHINE_CLASS(mc); + static GlobalProperty compat[] = { + { TYPE_S390_IPL, "iplbext_migration", "off", }, + { TYPE_VIRTUAL_CSS_BRIDGE, "css_dev_path", "off", }, + }; s390mc->ri_allowed = false; ccw_machine_2_7_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_6); + compat_props_add(mc->compat_props, hw_compat_2_6, hw_compat_2_6_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_CCW_MACHINE(2_6, "2.6", false); @@ -882,7 +817,7 @@ static void ccw_machine_2_5_instance_options(MachineState *machine) static void ccw_machine_2_5_class_options(MachineClass *mc) { ccw_machine_2_6_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_5); + compat_props_add(mc->compat_props, hw_compat_2_5, hw_compat_2_5_len); } DEFINE_CCW_MACHINE(2_5, "2.5", false); @@ -893,8 +828,21 @@ static void ccw_machine_2_4_instance_options(MachineState *machine) static void ccw_machine_2_4_class_options(MachineClass *mc) { + static GlobalProperty compat[] = { + { TYPE_S390_SKEYS, "migration-enabled", "off", }, + { "virtio-blk-ccw", "max_revision", "0", }, + { "virtio-balloon-ccw", "max_revision", "0", }, + { "virtio-serial-ccw", "max_revision", "0", }, + { "virtio-9p-ccw", "max_revision", "0", }, + { "virtio-rng-ccw", "max_revision", "0", }, + { "virtio-net-ccw", "max_revision", "0", }, + { "virtio-scsi-ccw", "max_revision", "0", }, + { "vhost-scsi-ccw", "max_revision", "0", }, + }; + ccw_machine_2_5_class_options(mc); - SET_MACHINE_COMPAT(mc, CCW_COMPAT_2_4); + compat_props_add(mc->compat_props, hw_compat_2_4, hw_compat_2_4_len); + compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); } DEFINE_CCW_MACHINE(2_4, "2.4", false); diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index df564ab89c..2456bf7b24 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -10,10 +10,11 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "sysemu/sysemu.h" #include "hw/s390x/tod.h" #include "kvm_s390x.h" -static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) +static void kvm_s390_get_tod_raw(S390TOD *tod, Error **errp) { int r; @@ -27,7 +28,17 @@ static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) } } -static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) +static void kvm_s390_tod_get(const S390TODState *td, S390TOD *tod, Error **errp) +{ + if (td->stopped) { + *tod = td->base; + return; + } + + kvm_s390_get_tod_raw(tod, errp); +} + +static void kvm_s390_set_tod_raw(const S390TOD *tod, Error **errp) { int r; @@ -41,18 +52,105 @@ static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) } } +static void kvm_s390_tod_set(S390TODState *td, const S390TOD *tod, Error **errp) +{ + Error *local_err = NULL; + + /* + * Somebody (e.g. migration) set the TOD. We'll store it into KVM to + * properly detect errors now but take a look at the runstate to decide + * whether really to keep the tod running. E.g. during migration, this + * is the point where we want to stop the initially running TOD to fire + * it back up when actually starting the migrated guest. + */ + kvm_s390_set_tod_raw(tod, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + if (runstate_is_running()) { + td->stopped = false; + } else { + td->stopped = true; + td->base = *tod; + } +} + +static void kvm_s390_tod_vm_state_change(void *opaque, int running, + RunState state) +{ + S390TODState *td = opaque; + Error *local_err = NULL; + + if (running && td->stopped) { + /* Set the old TOD when running the VM - start the TOD clock. */ + kvm_s390_set_tod_raw(&td->base, &local_err); + if (local_err) { + warn_report_err(local_err); + } + /* Treat errors like the TOD was running all the time. */ + td->stopped = false; + } else if (!running && !td->stopped) { + /* Store the TOD when stopping the VM - stop the TOD clock. */ + kvm_s390_get_tod_raw(&td->base, &local_err); + if (local_err) { + /* Keep the TOD running in case we could not back it up. */ + warn_report_err(local_err); + } else { + td->stopped = true; + } + } +} + +static void kvm_s390_tod_realize(DeviceState *dev, Error **errp) +{ + S390TODState *td = S390_TOD(dev); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + Error *local_err = NULL; + + tdc->parent_realize(dev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + /* + * We need to know when the VM gets started/stopped to start/stop the TOD. + * As we can never have more than one TOD instance (and that will never be + * removed), registering here and never unregistering is good enough. + */ + qemu_add_vm_change_state_handler(kvm_s390_tod_vm_state_change, td); +} + static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) { S390TODClass *tdc = S390_TOD_CLASS(oc); + device_class_set_parent_realize(DEVICE_CLASS(oc), kvm_s390_tod_realize, + &tdc->parent_realize); tdc->get = kvm_s390_tod_get; tdc->set = kvm_s390_tod_set; } +static void kvm_s390_tod_init(Object *obj) +{ + S390TODState *td = S390_TOD(obj); + + /* + * The TOD is initially running (value stored in KVM). Avoid needless + * loading/storing of the TOD when starting a simple VM, so let it + * run although the (never started) VM is stopped. For migration, we + * will properly set the TOD later. + */ + td->stopped = false; +} + static TypeInfo kvm_s390_tod_info = { .name = TYPE_KVM_S390_TOD, .parent = TYPE_S390_TOD, .instance_size = sizeof(S390TODState), + .instance_init = kvm_s390_tod_init, .class_init = kvm_s390_tod_class_init, .class_size = sizeof(S390TODClass), }; diff --git a/hw/s390x/trace-events b/hw/s390x/trace-events index 0d3622ec6f..0dc5b818c4 100644 --- a/hw/s390x/trace-events +++ b/hw/s390x/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/s390x/css.c +# css.c css_enable_facility(const char *facility) "CSS: enable %s" css_crw(uint8_t rsc, uint8_t erc, uint16_t rsid, const char *chained) "CSS: queueing crw: rsc=0x%x, erc=0x%x, rsid=0x%x %s" css_chpid_add(uint8_t cssid, uint8_t chpid, uint8_t type) "CSS: add chpid %x.%02x (type 0x%02x)" @@ -10,7 +10,7 @@ css_io_interrupt(int cssid, int ssid, int schid, uint32_t intparm, uint8_t isc, css_adapter_interrupt(uint8_t isc) "CSS: adapter I/O interrupt (isc 0x%x)" css_do_sic(uint16_t mode, uint8_t isc) "CSS: set interruption mode 0x%x on isc 0x%x" -# hw/s390x/virtio-ccw.c +# virtio-ccw.c virtio_ccw_interpret_ccw(int cssid, int ssid, int schid, int cmd_code) "VIRTIO-CCW: %x.%x.%04x: interpret command 0x%x" virtio_ccw_new_device(int cssid, int ssid, int schid, int devno, const char *devno_mode) "VIRTIO-CCW: add subchannel %x.%x.%04x, devno 0x%04x (%s)" virtio_ccw_set_ind(uint64_t ind_loc, uint8_t ind_old, uint8_t ind_new) "VIRTIO-CCW: indicator at %" PRIu64 ": 0x%x->0x%x" diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c new file mode 100644 index 0000000000..cddc5cf652 --- /dev/null +++ b/hw/s390x/vhost-vsock-ccw.c @@ -0,0 +1,60 @@ +/* + * vhost vsock ccw implementation + * + * 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/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static Property vhost_vsock_ccw_properties[] = { + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VHostVSockCCWState *dev = VHOST_VSOCK_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = vhost_vsock_ccw_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = vhost_vsock_ccw_properties; +} + +static void vhost_vsock_ccw_instance_init(Object *obj) +{ + VHostVSockCCWState *dev = VHOST_VSOCK_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_VSOCK); +} + +static const TypeInfo vhost_vsock_ccw_info = { + .name = TYPE_VHOST_VSOCK_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VHostVSockCCWState), + .instance_init = vhost_vsock_ccw_instance_init, + .class_init = vhost_vsock_ccw_class_init, +}; + +static void vhost_vsock_ccw_register(void) +{ + type_register_static(&vhost_vsock_ccw_info); +} + +type_init(vhost_vsock_ccw_register) diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c new file mode 100644 index 0000000000..d6be172596 --- /dev/null +++ b/hw/s390x/virtio-ccw-9p.c @@ -0,0 +1,65 @@ +/* + * virtio ccw 9p implementation + * + * Copyright 2012, 2015 IBM Corp. + * Author(s): Pierre Morel + * + * 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/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_ccw_9p_instance_init(Object *obj) +{ + V9fsCCWState *dev = VIRTIO_9P_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_9P); +} + +static Property virtio_ccw_9p_properties[] = { + 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(), +}; + +static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_9p_realize; + dc->props = virtio_ccw_9p_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo virtio_ccw_9p_info = { + .name = TYPE_VIRTIO_9P_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(V9fsCCWState), + .instance_init = virtio_ccw_9p_instance_init, + .class_init = virtio_ccw_9p_class_init, +}; + +static void virtio_ccw_9p_register(void) +{ + type_register_static(&virtio_ccw_9p_info); +} + +type_init(virtio_ccw_9p_register) diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c new file mode 100644 index 0000000000..28d171ac0c --- /dev/null +++ b/hw/s390x/virtio-ccw-balloon.c @@ -0,0 +1,70 @@ +/* + * virtio ccw balloon implementation + * + * Copyright 2012, 2015 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. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_ccw_balloon_instance_init(Object *obj) +{ + VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_BALLOON); + object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev), + "guest-stats", &error_abort); + object_property_add_alias(obj, "guest-stats-polling-interval", + OBJECT(&dev->vdev), + "guest-stats-polling-interval", &error_abort); +} + +static Property virtio_ccw_balloon_properties[] = { + 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(), +}; + +static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_balloon_realize; + dc->props = virtio_ccw_balloon_properties; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo virtio_ccw_balloon = { + .name = TYPE_VIRTIO_BALLOON_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOBalloonCcw), + .instance_init = virtio_ccw_balloon_instance_init, + .class_init = virtio_ccw_balloon_class_init, +}; + +static void virtio_ccw_balloon_register(void) +{ + type_register_static(&virtio_ccw_balloon); +} + +type_init(virtio_ccw_balloon_register) diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c new file mode 100644 index 0000000000..1f3d09a75a --- /dev/null +++ b/hw/s390x/virtio-ccw-blk.c @@ -0,0 +1,67 @@ +/* + * virtio ccw block implementation + * + * Copyright 2012, 2015 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. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_ccw_blk_instance_init(Object *obj) +{ + VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_BLK); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static Property virtio_ccw_blk_properties[] = { + 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(), +}; + +static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_blk_realize; + dc->props = virtio_ccw_blk_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo virtio_ccw_blk = { + .name = TYPE_VIRTIO_BLK_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOBlkCcw), + .instance_init = virtio_ccw_blk_instance_init, + .class_init = virtio_ccw_blk_class_init, +}; + +static void virtio_ccw_blk_register(void) +{ + type_register_static(&virtio_ccw_blk); +} + +type_init(virtio_ccw_blk_register) diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c new file mode 100644 index 0000000000..aab6a958f2 --- /dev/null +++ b/hw/s390x/virtio-ccw-crypto.c @@ -0,0 +1,75 @@ +/* + * virtio ccw crypto implementation + * + * Copyright 2012, 2015 IBM Corp. + * + * 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/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_crypto_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOCryptoCcw *dev = VIRTIO_CRYPTO_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_link(OBJECT(vdev), + OBJECT(dev->vdev.conf.cryptodev), "cryptodev", + NULL); +} + +static void virtio_ccw_crypto_instance_init(Object *obj) +{ + VirtIOCryptoCcw *dev = VIRTIO_CRYPTO_CCW(obj); + VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); + + ccw_dev->force_revision_1 = true; + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_CRYPTO); +} + +static Property virtio_ccw_crypto_properties[] = { + 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(), +}; + +static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_crypto_realize; + dc->props = virtio_ccw_crypto_properties; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo virtio_ccw_crypto = { + .name = TYPE_VIRTIO_CRYPTO_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOCryptoCcw), + .instance_init = virtio_ccw_crypto_instance_init, + .class_init = virtio_ccw_crypto_class_init, +}; + +static void virtio_ccw_crypto_register(void) +{ + type_register_static(&virtio_ccw_crypto); +} + +type_init(virtio_ccw_crypto_register) diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c new file mode 100644 index 0000000000..71869b7fbd --- /dev/null +++ b/hw/s390x/virtio-ccw-gpu.c @@ -0,0 +1,67 @@ +/* + * virtio ccw gpu implementation + * + * Copyright 2012, 2015 IBM Corp. + * + * 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/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_gpu_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOGPUCcw *dev = VIRTIO_GPU_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_ccw_gpu_instance_init(Object *obj) +{ + VirtIOGPUCcw *dev = VIRTIO_GPU_CCW(obj); + VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); + + ccw_dev->force_revision_1 = true; + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_GPU); +} + +static Property virtio_ccw_gpu_properties[] = { + 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(), +}; + +static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_gpu_realize; + dc->props = virtio_ccw_gpu_properties; + dc->hotpluggable = false; + set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); +} + +static const TypeInfo virtio_ccw_gpu = { + .name = TYPE_VIRTIO_GPU_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOGPUCcw), + .instance_init = virtio_ccw_gpu_instance_init, + .class_init = virtio_ccw_gpu_class_init, +}; + +static void virtio_ccw_gpu_register(void) +{ + type_register_static(&virtio_ccw_gpu); +} + +type_init(virtio_ccw_gpu_register) diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c new file mode 100644 index 0000000000..79c87cb3f2 --- /dev/null +++ b/hw/s390x/virtio-ccw-input.c @@ -0,0 +1,118 @@ +/* + * virtio ccw scsi implementation + * + * Copyright 2012, 2015 IBM Corp. + * + * 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/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_input_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOInputCcw *dev = VIRTIO_INPUT_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static Property virtio_ccw_input_properties[] = { + 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(), +}; + +static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_input_realize; + dc->props = virtio_ccw_input_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static void virtio_ccw_keyboard_instance_init(Object *obj) +{ + VirtIOInputHIDCcw *dev = VIRTIO_INPUT_HID_CCW(obj); + VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); + + ccw_dev->force_revision_1 = true; + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_KEYBOARD); +} + +static void virtio_ccw_mouse_instance_init(Object *obj) +{ + VirtIOInputHIDCcw *dev = VIRTIO_INPUT_HID_CCW(obj); + VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); + + ccw_dev->force_revision_1 = true; + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_MOUSE); +} + +static void virtio_ccw_tablet_instance_init(Object *obj) +{ + VirtIOInputHIDCcw *dev = VIRTIO_INPUT_HID_CCW(obj); + VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); + + ccw_dev->force_revision_1 = true; + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_TABLET); +} + +static const TypeInfo virtio_ccw_input = { + .name = TYPE_VIRTIO_INPUT_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOInputCcw), + .class_init = virtio_ccw_input_class_init, + .abstract = true, +}; + +static const TypeInfo virtio_ccw_input_hid = { + .name = TYPE_VIRTIO_INPUT_HID_CCW, + .parent = TYPE_VIRTIO_INPUT_CCW, + .instance_size = sizeof(VirtIOInputHIDCcw), + .abstract = true, +}; + +static const TypeInfo virtio_ccw_keyboard = { + .name = TYPE_VIRTIO_KEYBOARD_CCW, + .parent = TYPE_VIRTIO_INPUT_HID_CCW, + .instance_size = sizeof(VirtIOInputHIDCcw), + .instance_init = virtio_ccw_keyboard_instance_init, +}; + +static const TypeInfo virtio_ccw_mouse = { + .name = TYPE_VIRTIO_MOUSE_CCW, + .parent = TYPE_VIRTIO_INPUT_HID_CCW, + .instance_size = sizeof(VirtIOInputHIDCcw), + .instance_init = virtio_ccw_mouse_instance_init, +}; + +static const TypeInfo virtio_ccw_tablet = { + .name = TYPE_VIRTIO_TABLET_CCW, + .parent = TYPE_VIRTIO_INPUT_HID_CCW, + .instance_size = sizeof(VirtIOInputHIDCcw), + .instance_init = virtio_ccw_tablet_instance_init, +}; + +static void virtio_ccw_input_register(void) +{ + type_register_static(&virtio_ccw_input); + type_register_static(&virtio_ccw_input_hid); + type_register_static(&virtio_ccw_keyboard); + type_register_static(&virtio_ccw_mouse); + type_register_static(&virtio_ccw_tablet); +} + +type_init(virtio_ccw_input_register) diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c new file mode 100644 index 0000000000..0c0410c643 --- /dev/null +++ b/hw/s390x/virtio-ccw-net.c @@ -0,0 +1,70 @@ +/* + * virtio ccw net implementation + * + * Copyright 2012, 2015 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. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + DeviceState *qdev = DEVICE(ccw_dev); + VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + virtio_net_set_netclient_name(&dev->vdev, qdev->id, + object_get_typename(OBJECT(qdev))); + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_ccw_net_instance_init(Object *obj) +{ + VirtIONetCcw *dev = VIRTIO_NET_CCW(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 Property virtio_ccw_net_properties[] = { + 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(), +}; + +static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_net_realize; + dc->props = virtio_ccw_net_properties; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); +} + +static const TypeInfo virtio_ccw_net = { + .name = TYPE_VIRTIO_NET_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIONetCcw), + .instance_init = virtio_ccw_net_instance_init, + .class_init = virtio_ccw_net_class_init, +}; + +static void virtio_ccw_net_register(void) +{ + type_register_static(&virtio_ccw_net); +} + +type_init(virtio_ccw_net_register) diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c new file mode 100644 index 0000000000..3f6abccef8 --- /dev/null +++ b/hw/s390x/virtio-ccw-rng.c @@ -0,0 +1,74 @@ +/* + * virtio ccw random number generator implementation + * + * Copyright 2012, 2015 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. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIORNGCcw *dev = VIRTIO_RNG_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + Error *err = NULL; + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_link(OBJECT(dev), + OBJECT(dev->vdev.conf.rng), "rng", + NULL); +} + +static void virtio_ccw_rng_instance_init(Object *obj) +{ + VirtIORNGCcw *dev = VIRTIO_RNG_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_RNG); +} + +static Property virtio_ccw_rng_properties[] = { + 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(), +}; + +static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_rng_realize; + dc->props = virtio_ccw_rng_properties; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); +} + +static const TypeInfo virtio_ccw_rng = { + .name = TYPE_VIRTIO_RNG_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIORNGCcw), + .instance_init = virtio_ccw_rng_instance_init, + .class_init = virtio_ccw_rng_class_init, +}; + +static void virtio_ccw_rng_register(void) +{ + type_register_static(&virtio_ccw_rng); +} + +type_init(virtio_ccw_rng_register) diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c new file mode 100644 index 0000000000..c9a804fa25 --- /dev/null +++ b/hw/s390x/virtio-ccw-scsi.c @@ -0,0 +1,125 @@ +/* + * virtio ccw scsi implementation + * + * Copyright 2012, 2015 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. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "qapi/error.h" +#include "virtio-ccw.h" + +static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + DeviceState *qdev = DEVICE(ccw_dev); + 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(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_ccw_scsi_instance_init(Object *obj) +{ + VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_SCSI); +} + +static Property virtio_ccw_scsi_properties[] = { + 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(), +}; + +static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_scsi_realize; + dc->props = virtio_ccw_scsi_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo virtio_ccw_scsi = { + .name = TYPE_VIRTIO_SCSI_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtIOSCSICcw), + .instance_init = virtio_ccw_scsi_instance_init, + .class_init = virtio_ccw_scsi_class_init, +}; + +#ifdef CONFIG_VHOST_SCSI + +static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_ccw_scsi_instance_init(Object *obj) +{ + VHostSCSICcw *dev = VHOST_SCSI_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_SCSI); +} + +static Property vhost_ccw_scsi_properties[] = { + DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, + VIRTIO_CCW_MAX_REV), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = vhost_ccw_scsi_realize; + dc->props = vhost_ccw_scsi_properties; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); +} + +static const TypeInfo vhost_ccw_scsi = { + .name = TYPE_VHOST_SCSI_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VHostSCSICcw), + .instance_init = vhost_ccw_scsi_instance_init, + .class_init = vhost_ccw_scsi_class_init, +}; + +#endif + +static void virtio_ccw_scsi_register(void) +{ + type_register_static(&virtio_ccw_scsi); +#ifdef CONFIG_VHOST_SCSI + type_register_static(&vhost_ccw_scsi); +#endif +} + +type_init(virtio_ccw_scsi_register) diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c new file mode 100644 index 0000000000..3851fc9c9b --- /dev/null +++ b/hw/s390x/virtio-ccw-serial.c @@ -0,0 +1,78 @@ +/* + * virtio ccw serial implementation + * + * Copyright 2012, 2015 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. + */ + +#include "qemu/osdep.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-serial.h" +#include "virtio-ccw.h" + +static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) +{ + VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + DeviceState *proxy = DEVICE(ccw_dev); + char *bus_name; + + /* + * For command line compatibility, this sets the virtio-serial-device bus + * name as before. + */ + if (proxy->id) { + bus_name = g_strdup_printf("%s.0", proxy->id); + virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); + g_free(bus_name); + } + + qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + + +static void virtio_ccw_serial_instance_init(Object *obj) +{ + VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_SERIAL); +} + +static Property virtio_ccw_serial_properties[] = { + 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(), +}; + +static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); + + k->realize = virtio_ccw_serial_realize; + dc->props = virtio_ccw_serial_properties; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); +} + +static const TypeInfo virtio_ccw_serial = { + .name = TYPE_VIRTIO_SERIAL_CCW, + .parent = TYPE_VIRTIO_CCW_DEVICE, + .instance_size = sizeof(VirtioSerialCcw), + .instance_init = virtio_ccw_serial_instance_init, + .class_init = virtio_ccw_serial_class_init, +}; + +static void virtio_ccw_serial_register(void) +{ + type_register_static(&virtio_ccw_serial); +} + +type_init(virtio_ccw_serial_register) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 7ddb378d52..c2b78c8e9b 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -17,7 +17,6 @@ #include "sysemu/kvm.h" #include "net/net.h" #include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-serial.h" #include "hw/virtio/virtio-net.h" #include "hw/sysbus.h" #include "qemu/bitops.h" @@ -288,18 +287,18 @@ static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, } if (is_legacy) { ccw_dstream_read(&sch->cds, linfo); - be64_to_cpus(&linfo.queue); - be32_to_cpus(&linfo.align); - be16_to_cpus(&linfo.index); - be16_to_cpus(&linfo.num); + linfo.queue = be64_to_cpu(linfo.queue); + linfo.align = be32_to_cpu(linfo.align); + linfo.index = be16_to_cpu(linfo.index); + linfo.num = be16_to_cpu(linfo.num); ret = virtio_ccw_set_vqs(sch, NULL, &linfo); } else { ccw_dstream_read(&sch->cds, info); - be64_to_cpus(&info.desc); - be16_to_cpus(&info.index); - be16_to_cpus(&info.num); - be64_to_cpus(&info.avail); - be64_to_cpus(&info.used); + info.desc = be64_to_cpu(info.desc); + info.index = be16_to_cpu(info.index); + info.num = be16_to_cpu(info.num); + info.avail = be64_to_cpu(info.avail); + info.used = be64_to_cpu(info.used); ret = virtio_ccw_set_vqs(sch, &info, NULL); } sch->curr_status.scsw.count = 0; @@ -383,7 +382,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) features.features = 0; } ccw_dstream_rewind(&sch->cds); - cpu_to_le32s(&features.features); + features.features = cpu_to_le32(features.features); ccw_dstream_write(&sch->cds, features.features); sch->curr_status.scsw.count = ccw.count - sizeof(features); ret = 0; @@ -404,7 +403,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EFAULT; } else { ccw_dstream_read(&sch->cds, features); - le32_to_cpus(&features.features); + features.features = le32_to_cpu(features.features); if (features.index == 0) { virtio_set_features(vdev, (vdev->guest_features & 0xffffffff00000000ULL) | @@ -547,7 +546,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EFAULT; } else { ccw_dstream_read(&sch->cds, indicators); - be64_to_cpus(&indicators); + indicators = be64_to_cpu(indicators); dev->indicators = get_indicator(indicators, sizeof(uint64_t)); sch->curr_status.scsw.count = ccw.count - sizeof(indicators); ret = 0; @@ -568,7 +567,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EFAULT; } else { ccw_dstream_read(&sch->cds, indicators); - be64_to_cpus(&indicators); + indicators = be64_to_cpu(indicators); dev->indicators2 = get_indicator(indicators, sizeof(uint64_t)); sch->curr_status.scsw.count = ccw.count - sizeof(indicators); ret = 0; @@ -589,14 +588,14 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ret = -EFAULT; } else { ccw_dstream_read(&sch->cds, vq_config.index); - be16_to_cpus(&vq_config.index); + vq_config.index = be16_to_cpu(vq_config.index); if (vq_config.index >= VIRTIO_QUEUE_MAX) { ret = -EINVAL; break; } vq_config.num_max = virtio_queue_get_num(vdev, vq_config.index); - cpu_to_be16s(&vq_config.num_max); + vq_config.num_max = cpu_to_be16(vq_config.num_max); ccw_dstream_write(&sch->cds, vq_config.num_max); sch->curr_status.scsw.count = ccw.count - sizeof(vq_config); ret = 0; @@ -622,9 +621,11 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) if (ccw_dstream_read(&sch->cds, thinint)) { ret = -EFAULT; } else { - be64_to_cpus(&thinint.ind_bit); - be64_to_cpus(&thinint.summary_indicator); - be64_to_cpus(&thinint.device_indicator); + thinint.ind_bit = be64_to_cpu(thinint.ind_bit); + thinint.summary_indicator = + be64_to_cpu(thinint.summary_indicator); + thinint.device_indicator = + be64_to_cpu(thinint.device_indicator); dev->summary_indicator = get_indicator(thinint.summary_indicator, sizeof(uint8_t)); @@ -655,8 +656,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) break; } ccw_dstream_read_buf(&sch->cds, &revinfo, 4); - be16_to_cpus(&revinfo.revision); - be16_to_cpus(&revinfo.length); + revinfo.revision = be16_to_cpu(revinfo.revision); + revinfo.length = be16_to_cpu(revinfo.length); if (ccw.count < len + revinfo.length || (check_len && ccw.count > len + revinfo.length)) { ret = -EINVAL; @@ -694,13 +695,10 @@ static void virtio_ccw_device_realize(VirtioCcwDevice *dev, Error **errp) VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_GET_CLASS(dev); CcwDevice *ccw_dev = CCW_DEVICE(dev); CCWDeviceClass *ck = CCW_DEVICE_GET_CLASS(ccw_dev); - DeviceState *parent = DEVICE(ccw_dev); - BusState *qbus = qdev_get_parent_bus(parent); - VirtualCssBus *cbus = VIRTUAL_CSS_BUS(qbus); SubchDev *sch; Error *err = NULL; - sch = css_create_sch(ccw_dev->devno, cbus->squash_mcss, errp); + sch = css_create_sch(ccw_dev->devno, errp); if (!sch) { return; } @@ -750,11 +748,16 @@ out_err: g_free(sch); } -static void virtio_ccw_unrealize(VirtioCcwDevice *dev, Error **errp) +static void virtio_ccw_device_unrealize(VirtioCcwDevice *dev, Error **errp) { + VirtIOCCWDeviceClass *dc = VIRTIO_CCW_DEVICE_GET_CLASS(dev); CcwDevice *ccw_dev = CCW_DEVICE(dev); SubchDev *sch = ccw_dev->sch; + if (dc->unrealize) { + dc->unrealize(dev, errp); + } + if (sch) { css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); g_free(sch); @@ -766,201 +769,6 @@ static void virtio_ccw_unrealize(VirtioCcwDevice *dev, Error **errp) } } -static void virtio_ccw_net_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - DeviceState *qdev = DEVICE(ccw_dev); - VirtIONetCcw *dev = VIRTIO_NET_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - virtio_net_set_netclient_name(&dev->vdev, qdev->id, - object_get_typename(OBJECT(qdev))); - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_ccw_net_instance_init(Object *obj) -{ - VirtIONetCcw *dev = VIRTIO_NET_CCW(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 virtio_ccw_blk_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_ccw_blk_instance_init(Object *obj) -{ - VirtIOBlkCcw *dev = VIRTIO_BLK_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static void virtio_ccw_serial_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *proxy = DEVICE(ccw_dev); - char *bus_name; - - /* - * For command line compatibility, this sets the virtio-serial-device bus - * name as before. - */ - if (proxy->id) { - bus_name = g_strdup_printf("%s.0", proxy->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - - -static void virtio_ccw_serial_instance_init(Object *obj) -{ - VirtioSerialCcw *dev = VIRTIO_SERIAL_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SERIAL); -} - -static void virtio_ccw_balloon_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_ccw_balloon_instance_init(Object *obj) -{ - VirtIOBalloonCcw *dev = VIRTIO_BALLOON_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BALLOON); - object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev), - "guest-stats", &error_abort); - object_property_add_alias(obj, "guest-stats-polling-interval", - OBJECT(&dev->vdev), - "guest-stats-polling-interval", &error_abort); -} - -static void virtio_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *qdev = DEVICE(ccw_dev); - 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(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_ccw_scsi_instance_init(Object *obj) -{ - VirtIOSCSICcw *dev = VIRTIO_SCSI_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SCSI); -} - -#ifdef CONFIG_VHOST_SCSI -static void vhost_ccw_scsi_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VHostSCSICcw *dev = VHOST_SCSI_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void vhost_ccw_scsi_instance_init(Object *obj) -{ - VHostSCSICcw *dev = VHOST_SCSI_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_SCSI); -} -#endif - -static void virtio_ccw_rng_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIORNGCcw *dev = VIRTIO_RNG_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_link(OBJECT(dev), - OBJECT(dev->vdev.conf.rng), "rng", - NULL); -} - -static void virtio_ccw_crypto_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOCryptoCcw *dev = VIRTIO_CRYPTO_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_link(OBJECT(vdev), - OBJECT(dev->vdev.conf.cryptodev), "cryptodev", - NULL); -} - -static void virtio_ccw_gpu_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOGPUCcw *dev = VIRTIO_GPU_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_ccw_input_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VirtIOInputCcw *dev = VIRTIO_INPUT_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - /* DeviceState to VirtioCcwDevice. Note: used on datapath, * be careful and test performance if you change this. */ @@ -1335,363 +1143,6 @@ static void virtio_ccw_device_unplugged(DeviceState *d) } /**************** Virtio-ccw Bus Device Descriptions *******************/ -static Property virtio_ccw_net_properties[] = { - 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(), -}; - -static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_net_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_net_properties; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); -} - -static const TypeInfo virtio_ccw_net = { - .name = TYPE_VIRTIO_NET_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIONetCcw), - .instance_init = virtio_ccw_net_instance_init, - .class_init = virtio_ccw_net_class_init, -}; - -static Property virtio_ccw_blk_properties[] = { - 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(), -}; - -static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_blk_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_blk_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo virtio_ccw_blk = { - .name = TYPE_VIRTIO_BLK_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOBlkCcw), - .instance_init = virtio_ccw_blk_instance_init, - .class_init = virtio_ccw_blk_class_init, -}; - -static Property virtio_ccw_serial_properties[] = { - 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(), -}; - -static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_serial_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_serial_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static const TypeInfo virtio_ccw_serial = { - .name = TYPE_VIRTIO_SERIAL_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtioSerialCcw), - .instance_init = virtio_ccw_serial_instance_init, - .class_init = virtio_ccw_serial_class_init, -}; - -static Property virtio_ccw_balloon_properties[] = { - 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(), -}; - -static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_balloon_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_balloon_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo virtio_ccw_balloon = { - .name = TYPE_VIRTIO_BALLOON_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOBalloonCcw), - .instance_init = virtio_ccw_balloon_instance_init, - .class_init = virtio_ccw_balloon_class_init, -}; - -static Property virtio_ccw_scsi_properties[] = { - 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(), -}; - -static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_scsi_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo virtio_ccw_scsi = { - .name = TYPE_VIRTIO_SCSI_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOSCSICcw), - .instance_init = virtio_ccw_scsi_instance_init, - .class_init = virtio_ccw_scsi_class_init, -}; - -#ifdef CONFIG_VHOST_SCSI -static Property vhost_ccw_scsi_properties[] = { - DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, - VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = vhost_ccw_scsi_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = vhost_ccw_scsi_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo vhost_ccw_scsi = { - .name = TYPE_VHOST_SCSI_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VHostSCSICcw), - .instance_init = vhost_ccw_scsi_instance_init, - .class_init = vhost_ccw_scsi_class_init, -}; -#endif - -static void virtio_ccw_rng_instance_init(Object *obj) -{ - VirtIORNGCcw *dev = VIRTIO_RNG_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_RNG); -} - -static Property virtio_ccw_rng_properties[] = { - 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(), -}; - -static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_rng_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_rng_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo virtio_ccw_rng = { - .name = TYPE_VIRTIO_RNG_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIORNGCcw), - .instance_init = virtio_ccw_rng_instance_init, - .class_init = virtio_ccw_rng_class_init, -}; - -static Property virtio_ccw_crypto_properties[] = { - 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(), -}; - -static void virtio_ccw_crypto_instance_init(Object *obj) -{ - VirtIOCryptoCcw *dev = VIRTIO_CRYPTO_CCW(obj); - VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); - - ccw_dev->force_revision_1 = true; - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_CRYPTO); -} - -static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_crypto_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_crypto_properties; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); -} - -static const TypeInfo virtio_ccw_crypto = { - .name = TYPE_VIRTIO_CRYPTO_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOCryptoCcw), - .instance_init = virtio_ccw_crypto_instance_init, - .class_init = virtio_ccw_crypto_class_init, -}; - -static Property virtio_ccw_gpu_properties[] = { - 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(), -}; - -static void virtio_ccw_gpu_instance_init(Object *obj) -{ - VirtIOGPUCcw *dev = VIRTIO_GPU_CCW(obj); - VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); - - ccw_dev->force_revision_1 = true; - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_GPU); -} - -static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_gpu_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_gpu_properties; - dc->hotpluggable = false; - set_bit(DEVICE_CATEGORY_DISPLAY, dc->categories); -} - -static const TypeInfo virtio_ccw_gpu = { - .name = TYPE_VIRTIO_GPU_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOGPUCcw), - .instance_init = virtio_ccw_gpu_instance_init, - .class_init = virtio_ccw_gpu_class_init, -}; - -static Property virtio_ccw_input_properties[] = { - 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(), -}; - -static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = virtio_ccw_input_realize; - k->unrealize = virtio_ccw_unrealize; - dc->props = virtio_ccw_input_properties; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); -} - -static void virtio_ccw_keyboard_instance_init(Object *obj) -{ - VirtIOInputHIDCcw *dev = VIRTIO_INPUT_HID_CCW(obj); - VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); - - ccw_dev->force_revision_1 = true; - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_KEYBOARD); -} - -static void virtio_ccw_mouse_instance_init(Object *obj) -{ - VirtIOInputHIDCcw *dev = VIRTIO_INPUT_HID_CCW(obj); - VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); - - ccw_dev->force_revision_1 = true; - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_MOUSE); -} - -static void virtio_ccw_tablet_instance_init(Object *obj) -{ - VirtIOInputHIDCcw *dev = VIRTIO_INPUT_HID_CCW(obj); - VirtioCcwDevice *ccw_dev = VIRTIO_CCW_DEVICE(obj); - - ccw_dev->force_revision_1 = true; - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_TABLET); -} - -static const TypeInfo virtio_ccw_input = { - .name = TYPE_VIRTIO_INPUT_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VirtIOInputCcw), - .class_init = virtio_ccw_input_class_init, - .abstract = true, -}; - -static const TypeInfo virtio_ccw_input_hid = { - .name = TYPE_VIRTIO_INPUT_HID_CCW, - .parent = TYPE_VIRTIO_INPUT_CCW, - .instance_size = sizeof(VirtIOInputHIDCcw), - .abstract = true, -}; - -static const TypeInfo virtio_ccw_keyboard = { - .name = TYPE_VIRTIO_KEYBOARD_CCW, - .parent = TYPE_VIRTIO_INPUT_HID_CCW, - .instance_size = sizeof(VirtIOInputHIDCcw), - .instance_init = virtio_ccw_keyboard_instance_init, -}; - -static const TypeInfo virtio_ccw_mouse = { - .name = TYPE_VIRTIO_MOUSE_CCW, - .parent = TYPE_VIRTIO_INPUT_HID_CCW, - .instance_size = sizeof(VirtIOInputHIDCcw), - .instance_init = virtio_ccw_mouse_instance_init, -}; - -static const TypeInfo virtio_ccw_tablet = { - .name = TYPE_VIRTIO_TABLET_CCW, - .parent = TYPE_VIRTIO_INPUT_HID_CCW, - .instance_size = sizeof(VirtIOInputHIDCcw), - .instance_init = virtio_ccw_tablet_instance_init, -}; - static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; @@ -1703,9 +1154,8 @@ static void virtio_ccw_busdev_realize(DeviceState *dev, Error **errp) static void virtio_ccw_busdev_unrealize(DeviceState *dev, Error **errp) { VirtioCcwDevice *_dev = (VirtioCcwDevice *)dev; - VirtIOCCWDeviceClass *_info = VIRTIO_CCW_DEVICE_GET_CLASS(dev); - _info->unrealize(_dev, errp); + virtio_ccw_device_unrealize(_dev, errp); } static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, @@ -1778,123 +1228,10 @@ static const TypeInfo virtio_ccw_bus_info = { .class_init = virtio_ccw_bus_class_init, }; -#ifdef CONFIG_VIRTFS -static Property virtio_ccw_9p_properties[] = { - DEFINE_PROP_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(), -}; - -static void virtio_ccw_9p_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - V9fsCCWState *dev = VIRTIO_9P_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->unrealize = virtio_ccw_unrealize; - k->realize = virtio_ccw_9p_realize; - dc->props = virtio_ccw_9p_properties; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static void virtio_ccw_9p_instance_init(Object *obj) -{ - V9fsCCWState *dev = VIRTIO_9P_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_9P); -} - -static const TypeInfo virtio_ccw_9p_info = { - .name = TYPE_VIRTIO_9P_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(V9fsCCWState), - .instance_init = virtio_ccw_9p_instance_init, - .class_init = virtio_ccw_9p_class_init, -}; -#endif - -#ifdef CONFIG_VHOST_VSOCK - -static Property vhost_vsock_ccw_properties[] = { - DEFINE_PROP_UINT32("max_revision", VirtioCcwDevice, max_rev, - VIRTIO_CCW_MAX_REV), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) -{ - VHostVSockCCWState *dev = VHOST_VSOCK_CCW(ccw_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&ccw_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); - - k->realize = vhost_vsock_ccw_realize; - k->unrealize = virtio_ccw_unrealize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = vhost_vsock_ccw_properties; -} - -static void vhost_vsock_ccw_instance_init(Object *obj) -{ - VHostVSockCCWState *dev = VHOST_VSOCK_CCW(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_VSOCK); -} - -static const TypeInfo vhost_vsock_ccw_info = { - .name = TYPE_VHOST_VSOCK_CCW, - .parent = TYPE_VIRTIO_CCW_DEVICE, - .instance_size = sizeof(VHostVSockCCWState), - .instance_init = vhost_vsock_ccw_instance_init, - .class_init = vhost_vsock_ccw_class_init, -}; -#endif - static void virtio_ccw_register(void) { type_register_static(&virtio_ccw_bus_info); type_register_static(&virtio_ccw_device_info); - type_register_static(&virtio_ccw_serial); - type_register_static(&virtio_ccw_blk); - type_register_static(&virtio_ccw_net); - type_register_static(&virtio_ccw_balloon); - type_register_static(&virtio_ccw_scsi); -#ifdef CONFIG_VHOST_SCSI - type_register_static(&vhost_ccw_scsi); -#endif - type_register_static(&virtio_ccw_rng); -#ifdef CONFIG_VIRTFS - type_register_static(&virtio_ccw_9p_info); -#endif -#ifdef CONFIG_VHOST_VSOCK - type_register_static(&vhost_vsock_ccw_info); -#endif - type_register_static(&virtio_ccw_crypto); - type_register_static(&virtio_ccw_gpu); - type_register_static(&virtio_ccw_input); - type_register_static(&virtio_ccw_input_hid); - type_register_static(&virtio_ccw_keyboard); - type_register_static(&virtio_ccw_mouse); - type_register_static(&virtio_ccw_tablet); } type_init(virtio_ccw_register) diff --git a/hw/scsi/Kconfig b/hw/scsi/Kconfig new file mode 100644 index 0000000000..b3ba540c17 --- /dev/null +++ b/hw/scsi/Kconfig @@ -0,0 +1,54 @@ +config SCSI + bool + +config LSI_SCSI_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SCSI + +config MPTSAS_SCSI_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SCSI + +config MEGASAS_SCSI_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SCSI + +config VMW_PVSCSI_SCSI_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SCSI + +config ESP + bool + select SCSI + +config ESP_PCI + bool + default y if PCI_DEVICES + depends on PCI + select ESP + +config SPAPR_VSCSI + bool + default y + depends on PSERIES + select SCSI + +config VIRTIO_SCSI + bool + default y + depends on VIRTIO + select SCSI + +config VHOST_USER_SCSI + bool + # Only PCI devices are provided for now + default y if VIRTIO_PCI + depends on VIRTIO && VHOST_USER && LINUX diff --git a/hw/scsi/Makefile.objs b/hw/scsi/Makefile.objs index 718b4c2a68..54b36ed8b1 100644 --- a/hw/scsi/Makefile.objs +++ b/hw/scsi/Makefile.objs @@ -1,4 +1,4 @@ -common-obj-y += scsi-disk.o +common-obj-y += scsi-disk.o emulation.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 @@ -6,7 +6,7 @@ common-obj-$(CONFIG_MEGASAS_SCSI_PCI) += megasas.o common-obj-$(CONFIG_VMW_PVSCSI_SCSI_PCI) += vmw_pvscsi.o common-obj-$(CONFIG_ESP) += esp.o common-obj-$(CONFIG_ESP_PCI) += esp-pci.o -obj-$(CONFIG_PSERIES) += spapr_vscsi.o +obj-$(CONFIG_SPAPR_VSCSI) += spapr_vscsi.o ifeq ($(CONFIG_VIRTIO_SCSI),y) obj-y += virtio-scsi.o virtio-scsi-dataplane.o diff --git a/hw/scsi/emulation.c b/hw/scsi/emulation.c new file mode 100644 index 0000000000..06d62f3c38 --- /dev/null +++ b/hw/scsi/emulation.c @@ -0,0 +1,42 @@ +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qemu/bswap.h" +#include "hw/scsi/emulation.h" + +int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl) +{ + /* required VPD size with unmap support */ + memset(outbuf, 0, 0x3c); + + outbuf[0] = bl->wsnz; /* wsnz */ + + if (bl->max_io_sectors) { + /* optimal transfer length granularity. This field and the optimal + * transfer length can't be greater than maximum transfer length. + */ + stw_be_p(outbuf + 2, MIN(bl->min_io_size, bl->max_io_sectors)); + + /* maximum transfer length */ + stl_be_p(outbuf + 4, bl->max_io_sectors); + + /* optimal transfer length */ + stl_be_p(outbuf + 8, MIN(bl->opt_io_size, bl->max_io_sectors)); + } else { + stw_be_p(outbuf + 2, bl->min_io_size); + stl_be_p(outbuf + 8, bl->opt_io_size); + } + + /* max unmap LBA count */ + stl_be_p(outbuf + 16, bl->max_unmap_sectors); + + /* max unmap descriptors */ + stl_be_p(outbuf + 20, bl->max_unmap_descr); + + /* optimal unmap granularity; alignment is zero */ + stl_be_p(outbuf + 24, bl->unmap_sectors); + + /* max write same size, make it the same as maximum transfer length */ + stl_be_p(outbuf + 36, bl->max_io_sectors); + + return 0x3c; +} diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 419fc668ac..6b0bbb9b7f 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -59,7 +59,7 @@ #define DMA_STAT_SCSIINT 0x10 #define DMA_STAT_BCMBLT 0x20 -#define SBAC_STATUS 0x1000 +#define SBAC_STATUS (1 << 24) typedef struct PCIESPState { /*< private >*/ @@ -136,7 +136,7 @@ static void esp_pci_dma_write(PCIESPState *pci, uint32_t saddr, uint32_t val) pci->dma_regs[saddr] = val; break; case DMA_STAT: - if (!(pci->sbac & SBAC_STATUS)) { + if (pci->sbac & SBAC_STATUS) { /* clear some bits on write */ uint32_t mask = DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE; pci->dma_regs[DMA_STAT] &= ~(val & mask); @@ -157,7 +157,7 @@ static uint32_t esp_pci_dma_read(PCIESPState *pci, uint32_t saddr) if (pci->esp.rregs[ESP_RSTAT] & STAT_INT) { val |= DMA_STAT_SCSIINT; } - if (pci->sbac & SBAC_STATUS) { + if (!(pci->sbac & SBAC_STATUS)) { pci->dma_regs[DMA_STAT] &= ~(DMA_STAT_ERROR | DMA_STAT_ABORT | DMA_STAT_DONE); } @@ -313,8 +313,8 @@ static void esp_pci_hard_reset(DeviceState *dev) static const VMStateDescription vmstate_esp_pci_scsi = { .name = "pciespscsi", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PCIESPState), VMSTATE_BUFFER_UNSAFE(dma_regs, PCIESPState, 0, 8 * sizeof(uint32_t)), diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 630d923623..ca8b36c0c5 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -286,11 +286,8 @@ static void esp_do_dma(ESPState *s) esp_dma_done(s); } -void esp_command_complete(SCSIRequest *req, uint32_t status, - size_t resid) +static void esp_report_command_complete(ESPState *s, uint32_t status) { - ESPState *s = req->hba_private; - trace_esp_command_complete(); if (s->ti_size != 0) { trace_esp_command_complete_unexpected(); @@ -311,6 +308,23 @@ void esp_command_complete(SCSIRequest *req, uint32_t status, } } +void esp_command_complete(SCSIRequest *req, uint32_t status, + size_t resid) +{ + ESPState *s = req->hba_private; + + if (s->rregs[ESP_RSTAT] & STAT_INT) { + /* Defer handling command complete until the previous + * interrupt has been handled. + */ + trace_esp_command_complete_deferred(); + s->deferred_status = status; + s->deferred_complete = true; + return; + } + esp_report_command_complete(s, status); +} + void esp_transfer_data(SCSIRequest *req, uint32_t len) { ESPState *s = req->hba_private; @@ -422,7 +436,10 @@ uint64_t esp_reg_read(ESPState *s, uint32_t saddr) s->rregs[ESP_RSTAT] &= ~STAT_TC; s->rregs[ESP_RSEQ] = SEQ_CD; esp_lower_irq(s); - + if (s->deferred_complete) { + esp_report_command_complete(s, s->deferred_status); + s->deferred_complete = false; + } return old_val; case ESP_TCHI: /* Return the unique id if the value has never been written */ @@ -582,6 +599,8 @@ const VMStateDescription vmstate_esp = { VMSTATE_UINT32(ti_wptr, ESPState), VMSTATE_BUFFER(ti_buf, ESPState), VMSTATE_UINT32(status, ESPState), + VMSTATE_UINT32(deferred_status, ESPState), + VMSTATE_BOOL(deferred_complete, ESPState), VMSTATE_UINT32(dma, ESPState), VMSTATE_PARTIAL_BUFFER(cmdbuf, ESPState, 16), VMSTATE_BUFFER_START_MIDDLE_V(cmdbuf, ESPState, 16, 4), @@ -671,8 +690,8 @@ static void sysbus_esp_hard_reset(DeviceState *dev) static const VMStateDescription vmstate_sysbus_esp_scsi = { .name = "sysbusespscsi", - .version_id = 0, - .minimum_version_id = 0, + .version_id = 1, + .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_STRUCT(esp, SysBusESPState, 0, vmstate_esp, ESPState), VMSTATE_END_OF_LIST() diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 160657f4b9..da7239d94f 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -20,20 +20,7 @@ #include "hw/scsi/scsi.h" #include "sysemu/dma.h" #include "qemu/log.h" - -//#define DEBUG_LSI -//#define DEBUG_LSI_REG - -#ifdef DEBUG_LSI -#define DPRINTF(fmt, ...) \ -do { printf("lsi_scsi: " fmt , ## __VA_ARGS__); } while (0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__); exit(1);} while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#define BADF(fmt, ...) \ -do { fprintf(stderr, "lsi_scsi: error: " fmt , ## __VA_ARGS__);} while (0) -#endif +#include "trace.h" static const char *names[] = { "SCNTL0", "SCNTL1", "SCNTL2", "SCNTL3", "SCID", "SXFER", "SDID", "GPREG", @@ -173,6 +160,11 @@ static const char *names[] = { #define LSI_CCNTL1_DDAC 0x08 #define LSI_CCNTL1_ZMOD 0x80 +#define LSI_SBCL_ATN 0x08 +#define LSI_SBCL_BSY 0x20 +#define LSI_SBCL_ACK 0x40 +#define LSI_SBCL_REQ 0x80 + /* Enable Response to Reselection */ #define LSI_SCID_RRE 0x60 @@ -202,11 +194,26 @@ typedef struct lsi_request { QTAILQ_ENTRY(lsi_request) next; } lsi_request; +enum { + LSI_NOWAIT, /* SCRIPTS are running or stopped */ + LSI_WAIT_RESELECT, /* Wait Reselect instruction has been issued */ + LSI_DMA_SCRIPTS, /* processing DMA from lsi_execute_script */ + LSI_DMA_IN_PROGRESS, /* DMA operation is in progress */ +}; + +enum { + LSI_MSG_ACTION_COMMAND = 0, + LSI_MSG_ACTION_DISCONNECT = 1, + LSI_MSG_ACTION_DOUT = 2, + LSI_MSG_ACTION_DIN = 3, +}; + typedef struct { /*< private >*/ PCIDevice parent_obj; /*< public >*/ + qemu_irq ext_irq; MemoryRegion mmio_io; MemoryRegion ram_io; MemoryRegion io_io; @@ -214,15 +221,9 @@ typedef struct { int carry; /* ??? Should this be an a visible register somewhere? */ int status; - /* Action to take at the end of a MSG IN phase. - 0 = COMMAND, 1 = disconnect, 2 = DATA OUT, 3 = DATA IN. */ int msg_action; int msg_len; uint8_t msg[LSI_MAX_MSGIN_LEN]; - /* 0 if SCRIPTS are running or stopped. - * 1 if a Wait Reselect instruction has been issued. - * 2 if processing DMA from lsi_execute_script. - * 3 if a DMA operation is in progress. */ int waiting; SCSIBus bus; int current_lun; @@ -270,6 +271,7 @@ typedef struct { uint8_t sdid; uint8_t ssid; uint8_t sfbr; + uint8_t sbcl; uint8_t stest1; uint8_t stest2; uint8_t stest3; @@ -295,8 +297,7 @@ typedef struct { uint8_t sbr; uint32_t adder; - /* Script ram is stored as 32-bit words in host byteorder. */ - uint32_t script_ram[2048]; + uint8_t script_ram[2048 * sizeof(uint32_t)]; } LSIState; #define TYPE_LSI53C810 "lsi53c810" @@ -305,19 +306,47 @@ typedef struct { #define LSI53C895A(obj) \ OBJECT_CHECK(LSIState, (obj), TYPE_LSI53C895A) +static const char *scsi_phases[] = { + "DOUT", + "DIN", + "CMD", + "STATUS", + "RSVOUT", + "RSVIN", + "MSGOUT", + "MSGIN" +}; + +static const char *scsi_phase_name(int phase) +{ + return scsi_phases[phase & PHASE_MASK]; +} + static inline int lsi_irq_on_rsl(LSIState *s) { return (s->sien0 & LSI_SIST0_RSL) && (s->scid & LSI_SCID_RRE); } +static lsi_request *get_pending_req(LSIState *s) +{ + lsi_request *p; + + QTAILQ_FOREACH(p, &s->queue, next) { + if (p->pending) { + return p; + } + } + return NULL; +} + static void lsi_soft_reset(LSIState *s) { - DPRINTF("Reset\n"); + trace_lsi_reset(); s->carry = 0; - s->msg_action = 0; + s->msg_action = LSI_MSG_ACTION_COMMAND; s->msg_len = 0; - s->waiting = 0; + s->waiting = LSI_NOWAIT; s->dsa = 0; s->dnad = 0; s->dbc = 0; @@ -356,6 +385,7 @@ static void lsi_soft_reset(LSIState *s) s->socl = 0; s->sdid = 0; s->ssid = 0; + s->sbcl = 0; s->stest1 = 0; s->stest2 = 0; s->stest3 = 0; @@ -443,12 +473,21 @@ static void lsi_stop_script(LSIState *s) s->istat1 &= ~LSI_ISTAT1_SRUN; } -static void lsi_update_irq(LSIState *s) +static void lsi_set_irq(LSIState *s, int level) { PCIDevice *d = PCI_DEVICE(s); + + if (s->ext_irq) { + qemu_set_irq(s->ext_irq, level); + } else { + pci_set_irq(d, level); + } +} + +static void lsi_update_irq(LSIState *s) +{ int level; static int last_level; - lsi_request *p; /* It's unclear whether the DIP/SIP bits should be cleared when the Interrupt Status Registers are cleared or when istat0 is read. @@ -473,20 +512,18 @@ static void lsi_update_irq(LSIState *s) level = 1; if (level != last_level) { - DPRINTF("Update IRQ level %d dstat %02x sist %02x%02x\n", - level, s->dstat, s->sist1, s->sist0); + trace_lsi_update_irq(level, s->dstat, s->sist1, s->sist0); last_level = level; } - pci_set_irq(d, level); + lsi_set_irq(s, level); - if (!level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { - DPRINTF("Handled IRQs & disconnected, looking for pending " - "processes\n"); - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } + if (!s->current && !level && lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON)) { + lsi_request *p; + + trace_lsi_update_irq_disconnected(); + p = get_pending_req(s); + if (p) { + lsi_reselect(s, p); } } } @@ -497,8 +534,7 @@ static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1) uint32_t mask0; uint32_t mask1; - DPRINTF("SCSI Interrupt 0x%02x%02x prev 0x%02x%02x\n", - stat1, stat0, s->sist1, s->sist0); + trace_lsi_script_scsi_interrupt(stat1, stat0, s->sist1, s->sist0); s->sist0 |= stat0; s->sist1 |= stat1; /* Stop processor on fatal or unmasked interrupt. As a special hack @@ -516,7 +552,7 @@ static void lsi_script_scsi_interrupt(LSIState *s, int stat0, int stat1) /* Stop SCRIPTS execution and raise a DMA interrupt. */ static void lsi_script_dma_interrupt(LSIState *s, int stat) { - DPRINTF("DMA Interrupt 0x%x prev 0x%x\n", stat, s->dstat); + trace_lsi_script_dma_interrupt(stat, s->dstat); s->dstat |= stat; lsi_update_irq(s); lsi_stop_script(s); @@ -524,6 +560,8 @@ static void lsi_script_dma_interrupt(LSIState *s, int stat) static inline void lsi_set_phase(LSIState *s, int phase) { + s->sbcl &= ~PHASE_MASK; + s->sbcl |= phase | LSI_SBCL_REQ; s->sstat1 = (s->sstat1 & ~PHASE_MASK) | phase; } @@ -536,9 +574,9 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) } else { s->dsp = (s->scntl2 & LSI_SCNTL2_WSR ? s->pmjad2 : s->pmjad1); } - DPRINTF("Data phase mismatch jump to %08x\n", s->dsp); + trace_lsi_bad_phase_jump(s->dsp); } else { - DPRINTF("Phase mismatch interrupt\n"); + trace_lsi_bad_phase_interrupt(); lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); lsi_stop_script(s); } @@ -550,10 +588,10 @@ static void lsi_bad_phase(LSIState *s, int out, int new_phase) static void lsi_resume_script(LSIState *s) { if (s->waiting != 2) { - s->waiting = 0; + s->waiting = LSI_NOWAIT; lsi_execute_script(s); } else { - s->waiting = 0; + s->waiting = LSI_NOWAIT; } } @@ -561,11 +599,12 @@ static void lsi_disconnect(LSIState *s) { s->scntl1 &= ~LSI_SCNTL1_CON; s->sstat1 &= ~PHASE_MASK; + s->sbcl = 0; } static void lsi_bad_selection(LSIState *s, uint32_t id) { - DPRINTF("Selected absent target %d\n", id); + trace_lsi_bad_selection(id); lsi_script_scsi_interrupt(s, 0, LSI_SIST1_STO); lsi_disconnect(s); } @@ -580,7 +619,7 @@ static void lsi_do_dma(LSIState *s, int out) assert(s->current); if (!s->current->dma_len) { /* Wait until data is available. */ - DPRINTF("DMA no data available\n"); + trace_lsi_do_dma_unavailable(); return; } @@ -600,7 +639,7 @@ static void lsi_do_dma(LSIState *s, int out) else if (s->sbms) addr |= ((uint64_t)s->sbms << 32); - DPRINTF("DMA addr=0x" DMA_ADDR_FMT " len=%d\n", addr, count); + trace_lsi_do_dma(addr, count); s->csbc += count; s->dnad += count; s->dbc -= count; @@ -629,7 +668,7 @@ static void lsi_queue_command(LSIState *s) { lsi_request *p = s->current; - DPRINTF("Queueing tag=0x%x\n", p->tag); + trace_lsi_queue_command(p->tag); assert(s->current != NULL); assert(s->current->dma_len == 0); QTAILQ_INSERT_TAIL(&s->queue, s->current, next); @@ -643,9 +682,9 @@ static void lsi_queue_command(LSIState *s) static void lsi_add_msg_byte(LSIState *s, uint8_t data) { if (s->msg_len >= LSI_MAX_MSGIN_LEN) { - BADF("MSG IN data too long\n"); + trace_lsi_add_msg_byte_error(); } else { - DPRINTF("MSG IN 0x%02x\n", data); + trace_lsi_add_msg_byte(data); s->msg[s->msg_len++] = data; } } @@ -665,10 +704,10 @@ static void lsi_reselect(LSIState *s, lsi_request *p) if (!(s->dcntl & LSI_DCNTL_COM)) { s->sfbr = 1 << (id & 0x7); } - DPRINTF("Reselected target %d\n", id); + trace_lsi_reselect(id); s->scntl1 |= LSI_SCNTL1_CON; lsi_set_phase(s, PHASE_MI); - s->msg_action = p->out ? 2 : 3; + s->msg_action = p->out ? LSI_MSG_ACTION_DOUT : LSI_MSG_ACTION_DIN; s->current->dma_len = p->pending; lsi_add_msg_byte(s, 0x80); if (s->current->tag & LSI_TAG_VALID) { @@ -721,7 +760,7 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) lsi_request *p = req->hba_private; if (p->pending) { - BADF("Multiple IO pending for request %p\n", p); + trace_lsi_queue_req_error(p); } p->pending = len; /* Reselect if waiting for it, or if reselection triggers an IRQ @@ -729,14 +768,14 @@ static int lsi_queue_req(LSIState *s, SCSIRequest *req, uint32_t len) Since no interrupt stacking is implemented in the emulation, it is also required that there are no pending interrupts waiting for service from the device driver. */ - if (s->waiting == 1 || + if (s->waiting == LSI_WAIT_RESELECT || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON) && !(s->istat0 & (LSI_ISTAT0_SIP | LSI_ISTAT0_DIP)))) { /* Reselect device. */ lsi_reselect(s, p); return 0; } else { - DPRINTF("Queueing IO tag=0x%x\n", p->tag); + trace_lsi_queue_req(p->tag); p->pending = len; return 1; } @@ -749,7 +788,7 @@ static void lsi_command_complete(SCSIRequest *req, uint32_t status, size_t resid int out; out = (s->sstat1 & PHASE_MASK) == PHASE_DO; - DPRINTF("Command complete status=%d\n", (int)status); + trace_lsi_command_complete(status); s->status = status; s->command_complete = 2; if (s->waiting && s->dbc != 0) { @@ -774,7 +813,7 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len) int out; assert(req->hba_private); - if (s->waiting == 1 || req->hba_private != s->current || + if (s->waiting == LSI_WAIT_RESELECT || req->hba_private != s->current || (lsi_irq_on_rsl(s) && !(s->scntl1 & LSI_SCNTL1_CON))) { if (lsi_queue_req(s, req, len)) { return; @@ -784,11 +823,11 @@ static void lsi_transfer_data(SCSIRequest *req, uint32_t len) out = (s->sstat1 & PHASE_MASK) == PHASE_DO; /* host adapter (re)connected */ - DPRINTF("Data ready tag=0x%x len=%d\n", req->tag, len); + trace_lsi_transfer_data(req->tag, len); s->current->dma_len = len; s->command_complete = 1; if (s->waiting) { - if (s->waiting == 1 || s->dbc == 0) { + if (s->waiting == LSI_WAIT_RESELECT || s->dbc == 0) { lsi_resume_script(s); } else { lsi_do_dma(s, out); @@ -803,7 +842,7 @@ static void lsi_do_command(LSIState *s) uint32_t id; int n; - DPRINTF("Send command len=%d\n", s->dbc); + trace_lsi_do_command(s->dbc); if (s->dbc > 16) s->dbc = 16; pci_dma_read(PCI_DEVICE(s), s->dnad, buf, s->dbc); @@ -839,7 +878,7 @@ static void lsi_do_command(LSIState *s) lsi_add_msg_byte(s, 4); /* DISCONNECT */ /* wait data */ lsi_set_phase(s, PHASE_MI); - s->msg_action = 1; + s->msg_action = LSI_MSG_ACTION_DISCONNECT; lsi_queue_command(s); } else { /* wait command complete */ @@ -851,24 +890,26 @@ static void lsi_do_command(LSIState *s) static void lsi_do_status(LSIState *s) { uint8_t status; - DPRINTF("Get status len=%d status=%d\n", s->dbc, s->status); - if (s->dbc != 1) - BADF("Bad Status move\n"); + trace_lsi_do_status(s->dbc, s->status); + if (s->dbc != 1) { + trace_lsi_do_status_error(); + } s->dbc = 1; status = s->status; s->sfbr = status; pci_dma_write(PCI_DEVICE(s), s->dnad, &status, 1); lsi_set_phase(s, PHASE_MI); - s->msg_action = 1; + s->msg_action = LSI_MSG_ACTION_DISCONNECT; lsi_add_msg_byte(s, 0); /* COMMAND COMPLETE */ } static void lsi_do_msgin(LSIState *s) { - int len; - DPRINTF("Message in len=%d/%d\n", s->dbc, s->msg_len); + uint8_t len; + trace_lsi_do_msgin(s->dbc, s->msg_len); s->sfbr = s->msg[0]; len = s->msg_len; + assert(len > 0 && len <= LSI_MAX_MSGIN_LEN); if (len > s->dbc) len = s->dbc; pci_dma_write(PCI_DEVICE(s), s->dnad, s->msg, len); @@ -881,16 +922,16 @@ static void lsi_do_msgin(LSIState *s) /* ??? Check if ATN (not yet implemented) is asserted and maybe switch to PHASE_MO. */ switch (s->msg_action) { - case 0: + case LSI_MSG_ACTION_COMMAND: lsi_set_phase(s, PHASE_CMD); break; - case 1: + case LSI_MSG_ACTION_DISCONNECT: lsi_disconnect(s); break; - case 2: + case LSI_MSG_ACTION_DOUT: lsi_set_phase(s, PHASE_DO); break; - case 3: + case LSI_MSG_ACTION_DIN: lsi_set_phase(s, PHASE_DI); break; default: @@ -931,53 +972,58 @@ static void lsi_do_msgout(LSIState *s) current_req = lsi_find_by_tag(s, current_tag); } - DPRINTF("MSG out len=%d\n", s->dbc); + trace_lsi_do_msgout(s->dbc); while (s->dbc) { msg = lsi_get_msgbyte(s); s->sfbr = msg; switch (msg) { case 0x04: - DPRINTF("MSG: Disconnect\n"); + trace_lsi_do_msgout_disconnect(); lsi_disconnect(s); break; case 0x08: - DPRINTF("MSG: No Operation\n"); + trace_lsi_do_msgout_noop(); lsi_set_phase(s, PHASE_CMD); break; case 0x01: len = lsi_get_msgbyte(s); msg = lsi_get_msgbyte(s); (void)len; /* avoid a warning about unused variable*/ - DPRINTF("Extended message 0x%x (len %d)\n", msg, len); + trace_lsi_do_msgout_extended(msg, len); switch (msg) { case 1: - DPRINTF("SDTR (ignored)\n"); + trace_lsi_do_msgout_ignored("SDTR"); lsi_skip_msgbytes(s, 2); break; case 3: - DPRINTF("WDTR (ignored)\n"); + trace_lsi_do_msgout_ignored("WDTR"); lsi_skip_msgbytes(s, 1); break; + case 4: + trace_lsi_do_msgout_ignored("PPR"); + lsi_skip_msgbytes(s, 5); + break; default: goto bad; } break; case 0x20: /* SIMPLE queue */ s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; - DPRINTF("SIMPLE queue tag=0x%x\n", s->select_tag & 0xff); + trace_lsi_do_msgout_simplequeue(s->select_tag & 0xff); break; case 0x21: /* HEAD of queue */ - BADF("HEAD queue not implemented\n"); + qemu_log_mask(LOG_UNIMP, "lsi_scsi: HEAD queue not implemented\n"); s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; break; case 0x22: /* ORDERED queue */ - BADF("ORDERED queue not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: ORDERED queue not implemented\n"); s->select_tag |= lsi_get_msgbyte(s) | LSI_TAG_VALID; break; case 0x0d: /* The ABORT TAG message clears the current I/O process only. */ - DPRINTF("MSG: ABORT TAG tag=0x%x\n", current_tag); + trace_lsi_do_msgout_abort(current_tag); if (current_req) { scsi_req_cancel(current_req->req); } @@ -989,17 +1035,17 @@ static void lsi_do_msgout(LSIState *s) /* The ABORT message clears all I/O processes for the selecting initiator on the specified logical unit of the target. */ if (msg == 0x06) { - DPRINTF("MSG: ABORT tag=0x%x\n", current_tag); + trace_lsi_do_msgout_abort(current_tag); } /* The CLEAR QUEUE message clears all I/O processes for all initiators on the specified logical unit of the target. */ if (msg == 0x0e) { - DPRINTF("MSG: CLEAR QUEUE tag=0x%x\n", current_tag); + trace_lsi_do_msgout_clearqueue(current_tag); } /* The BUS DEVICE RESET message clears all I/O processes for all initiators on all logical units of the target. */ if (msg == 0x0c) { - DPRINTF("MSG: BUS DEVICE RESET tag=0x%x\n", current_tag); + trace_lsi_do_msgout_busdevicereset(current_tag); } /* clear the current I/O process */ @@ -1027,17 +1073,17 @@ static void lsi_do_msgout(LSIState *s) goto bad; } s->current_lun = msg & 7; - DPRINTF("Select LUN %d\n", s->current_lun); + trace_lsi_do_msgout_select(s->current_lun); lsi_set_phase(s, PHASE_CMD); break; } } return; bad: - BADF("Unimplemented message 0x%02x\n", msg); + qemu_log_mask(LOG_UNIMP, "Unimplemented message 0x%02x\n", msg); lsi_set_phase(s, PHASE_MI); lsi_add_msg_byte(s, 7); /* MESSAGE REJECT */ - s->msg_action = 0; + s->msg_action = LSI_MSG_ACTION_COMMAND; } #define LSI_BUF_SIZE 4096 @@ -1046,7 +1092,7 @@ static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) int n; uint8_t buf[LSI_BUF_SIZE]; - DPRINTF("memcpy dest 0x%08x src 0x%08x count %d\n", dest, src, count); + trace_lsi_memcpy(dest, src, count); while (count) { n = (count > LSI_BUF_SIZE) ? LSI_BUF_SIZE : count; lsi_mem_read(s, src, buf, n); @@ -1061,16 +1107,17 @@ static void lsi_wait_reselect(LSIState *s) { lsi_request *p; - DPRINTF("Wait Reselect\n"); + trace_lsi_wait_reselect(); - QTAILQ_FOREACH(p, &s->queue, next) { - if (p->pending) { - lsi_reselect(s, p); - break; - } + if (s->current) { + return; + } + p = get_pending_req(s); + if (p) { + lsi_reselect(s, p); } if (s->current == NULL) { - s->waiting = 1; + s->waiting = LSI_WAIT_RESELECT; } } @@ -1094,14 +1141,14 @@ again: } addr = read_dword(s, s->dsp + 4); addr_high = 0; - DPRINTF("SCRIPTS dsp=%08x opcode %08x arg %08x\n", s->dsp, insn, addr); + trace_lsi_execute_script(s->dsp, insn, addr); s->dsps = addr; s->dcmd = insn >> 24; s->dsp += 8; switch (insn >> 30) { case 0: /* Block move. */ if (s->sist1 & LSI_SIST1_STO) { - DPRINTF("Delayed select timeout\n"); + trace_lsi_execute_script_blockmove_delayed(); lsi_stop_script(s); break; } @@ -1156,8 +1203,9 @@ again: addr_high = s->dbms; break; default: - BADF("Illegal selector specified (0x%x > 0x15)" - " for 64-bit DMA block move", selector); + qemu_log_mask(LOG_GUEST_ERROR, + "lsi_scsi: Illegal selector specified (0x%x > 0x15) " + "for 64-bit DMA block move", selector); break; } } @@ -1169,8 +1217,9 @@ again: s->ia = s->dsp - 12; } if ((s->sstat1 & PHASE_MASK) != ((insn >> 24) & 7)) { - DPRINTF("Wrong phase got %d expected %d\n", - s->sstat1 & PHASE_MASK, (insn >> 24) & 7); + trace_lsi_execute_script_blockmove_badphase( + scsi_phase_name(s->sstat1), + scsi_phase_name(insn >> 24)); lsi_script_scsi_interrupt(s, LSI_SIST0_MA, 0); break; } @@ -1178,16 +1227,16 @@ again: s->dnad64 = addr_high; switch (s->sstat1 & 0x7) { case PHASE_DO: - s->waiting = 2; + s->waiting = LSI_DMA_SCRIPTS; lsi_do_dma(s, 1); if (s->waiting) - s->waiting = 3; + s->waiting = LSI_DMA_IN_PROGRESS; break; case PHASE_DI: - s->waiting = 2; + s->waiting = LSI_DMA_SCRIPTS; lsi_do_dma(s, 0); if (s->waiting) - s->waiting = 3; + s->waiting = LSI_DMA_IN_PROGRESS; break; case PHASE_CMD: lsi_do_command(s); @@ -1202,8 +1251,8 @@ again: lsi_do_msgin(s); break; default: - BADF("Unimplemented phase %d\n", s->sstat1 & PHASE_MASK); - exit(1); + qemu_log_mask(LOG_UNIMP, "lsi_scsi: Unimplemented phase %s\n", + scsi_phase_name(s->sstat1)); } s->dfifo = s->dbc & 0xff; s->ctest5 = (s->ctest5 & 0xfc) | ((s->dbc >> 8) & 3); @@ -1231,7 +1280,7 @@ again: case 0: /* Select */ s->sdid = id; if (s->scntl1 & LSI_SCNTL1_CON) { - DPRINTF("Already reselected, jumping to alternative address\n"); + trace_lsi_execute_script_io_alreadyreselected(); s->dsp = s->dnad; break; } @@ -1241,8 +1290,8 @@ again: lsi_bad_selection(s, id); break; } - DPRINTF("Selected target %d%s\n", - id, insn & (1 << 3) ? " ATN" : ""); + trace_lsi_execute_script_io_selected(id, + insn & (1 << 3) ? " ATN" : ""); /* ??? Linux drivers compain when this is set. Maybe it only applies in low-level mode (unimplemented). lsi_script_scsi_interrupt(s, LSI_SIST0_CMP, 0); */ @@ -1250,44 +1299,73 @@ again: s->scntl1 |= LSI_SCNTL1_CON; if (insn & (1 << 3)) { s->socl |= LSI_SOCL_ATN; + s->sbcl |= LSI_SBCL_ATN; } + s->sbcl |= LSI_SBCL_BSY; lsi_set_phase(s, PHASE_MO); + s->waiting = LSI_NOWAIT; break; case 1: /* Disconnect */ - DPRINTF("Wait Disconnect\n"); + trace_lsi_execute_script_io_disconnect(); s->scntl1 &= ~LSI_SCNTL1_CON; + /* FIXME: this is not entirely correct; the target need not ask + * for reselection until it has to send data, while here we force a + * reselection as soon as the bus is free. The correct flow would + * reselect before lsi_transfer_data and disconnect as soon as + * DMA ends. + */ + if (!s->current) { + lsi_request *p = get_pending_req(s); + if (p) { + lsi_reselect(s, p); + } + } break; case 2: /* Wait Reselect */ - if (!lsi_irq_on_rsl(s)) { - lsi_wait_reselect(s); + if (s->istat0 & LSI_ISTAT0_SIGP) { + s->dsp = s->dnad; + } else if (!lsi_irq_on_rsl(s)) { + lsi_wait_reselect(s); } break; case 3: /* Set */ - DPRINTF("Set%s%s%s%s\n", + trace_lsi_execute_script_io_set( insn & (1 << 3) ? " ATN" : "", insn & (1 << 6) ? " ACK" : "", insn & (1 << 9) ? " TM" : "", insn & (1 << 10) ? " CC" : ""); if (insn & (1 << 3)) { s->socl |= LSI_SOCL_ATN; + s->sbcl |= LSI_SBCL_ATN; lsi_set_phase(s, PHASE_MO); } + + if (insn & (1 << 6)) { + s->sbcl |= LSI_SBCL_ACK; + } + if (insn & (1 << 9)) { - BADF("Target mode not implemented\n"); - exit(1); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: Target mode not implemented\n"); } if (insn & (1 << 10)) s->carry = 1; break; case 4: /* Clear */ - DPRINTF("Clear%s%s%s%s\n", + trace_lsi_execute_script_io_clear( insn & (1 << 3) ? " ATN" : "", insn & (1 << 6) ? " ACK" : "", insn & (1 << 9) ? " TM" : "", insn & (1 << 10) ? " CC" : ""); if (insn & (1 << 3)) { s->socl &= ~LSI_SOCL_ATN; + s->sbcl &= ~LSI_SBCL_ATN; } + + if (insn & (1 << 6)) { + s->sbcl &= ~LSI_SBCL_ACK; + } + if (insn & (1 << 10)) s->carry = 0; break; @@ -1298,18 +1376,17 @@ again: uint8_t data8; int reg; int operator; -#ifdef DEBUG_LSI + static const char *opcode_names[3] = {"Write", "Read", "Read-Modify-Write"}; static const char *operator_names[8] = {"MOV", "SHL", "OR", "XOR", "AND", "SHR", "ADD", "ADC"}; -#endif reg = ((insn >> 16) & 0x7f) | (insn & 0x80); data8 = (insn >> 8) & 0xff; opcode = (insn >> 27) & 7; operator = (insn >> 24) & 7; - DPRINTF("%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s\n", + trace_lsi_execute_script_io_opcode( opcode_names[opcode - 5], reg, operator_names[operator], data8, s->sfbr, (insn & (1 << 23)) ? " SFBR" : ""); @@ -1389,31 +1466,29 @@ again: int jmp; if ((insn & 0x002e0000) == 0) { - DPRINTF("NOP\n"); + trace_lsi_execute_script_tc_nop(); break; } if (s->sist1 & LSI_SIST1_STO) { - DPRINTF("Delayed select timeout\n"); + trace_lsi_execute_script_tc_delayedselect_timeout(); lsi_stop_script(s); break; } cond = jmp = (insn & (1 << 19)) != 0; if (cond == jmp && (insn & (1 << 21))) { - DPRINTF("Compare carry %d\n", s->carry == jmp); + trace_lsi_execute_script_tc_compc(s->carry == jmp); cond = s->carry != 0; } if (cond == jmp && (insn & (1 << 17))) { - DPRINTF("Compare phase %d %c= %d\n", - (s->sstat1 & PHASE_MASK), - jmp ? '=' : '!', - ((insn >> 24) & 7)); + trace_lsi_execute_script_tc_compp(scsi_phase_name(s->sstat1), + jmp ? '=' : '!', scsi_phase_name(insn >> 24)); cond = (s->sstat1 & PHASE_MASK) == ((insn >> 24) & 7); } if (cond == jmp && (insn & (1 << 18))) { uint8_t mask; mask = (~insn >> 8) & 0xff; - DPRINTF("Compare data 0x%x & 0x%x %c= 0x%x\n", + trace_lsi_execute_script_tc_compd( s->sfbr, mask, jmp ? '=' : '!', insn & mask); cond = (s->sfbr & mask) == (insn & mask); } @@ -1424,21 +1499,21 @@ again: } switch ((insn >> 27) & 7) { case 0: /* Jump */ - DPRINTF("Jump to 0x%08x\n", addr); + trace_lsi_execute_script_tc_jump(addr); s->adder = addr; s->dsp = addr; break; case 1: /* Call */ - DPRINTF("Call 0x%08x\n", addr); + trace_lsi_execute_script_tc_call(addr); s->temp = s->dsp; s->dsp = addr; break; case 2: /* Return */ - DPRINTF("Return to 0x%08x\n", s->temp); + trace_lsi_execute_script_tc_return(s->temp); s->dsp = s->temp; break; case 3: /* Interrupt */ - DPRINTF("Interrupt 0x%08x\n", s->dsps); + trace_lsi_execute_script_tc_interrupt(s->dsps); if ((insn & (1 << 20)) != 0) { s->istat0 |= LSI_ISTAT0_INTF; lsi_update_irq(s); @@ -1447,12 +1522,12 @@ again: } break; default: - DPRINTF("Illegal transfer control\n"); + trace_lsi_execute_script_tc_illegal(); lsi_script_dma_interrupt(s, LSI_DSTAT_IID); break; } } else { - DPRINTF("Control condition failed\n"); + trace_lsi_execute_script_tc_cc_failed(); } } break; @@ -1480,13 +1555,12 @@ again: reg = (insn >> 16) & 0xff; if (insn & (1 << 24)) { pci_dma_read(pci_dev, addr, data, n); - DPRINTF("Load reg 0x%x size %d addr 0x%08x = %08x\n", reg, n, - addr, *(int *)data); + trace_lsi_execute_script_mm_load(reg, n, addr, *(int *)data); for (i = 0; i < n; i++) { lsi_reg_writeb(s, reg + i, data[i]); } } else { - DPRINTF("Store reg 0x%x size %d addr 0x%08x\n", reg, n, addr); + trace_lsi_execute_script_mm_store(reg, n, addr); for (i = 0; i < n; i++) { data[i] = lsi_reg_readb(s, reg + i); } @@ -1494,24 +1568,26 @@ again: } } } - if (insn_processed > 10000 && !s->waiting) { + if (insn_processed > 10000 && s->waiting == LSI_NOWAIT) { /* Some windows drivers make the device spin waiting for a memory location to change. If we have been executed a lot of code then assume this is the case and force an unexpected device disconnect. This is apparently sufficient to beat the drivers into submission. */ - if (!(s->sien0 & LSI_SIST0_UDC)) - fprintf(stderr, "inf. loop with UDC masked\n"); + if (!(s->sien0 & LSI_SIST0_UDC)) { + qemu_log_mask(LOG_GUEST_ERROR, + "lsi_scsi: inf. loop with UDC masked"); + } lsi_script_scsi_interrupt(s, LSI_SIST0_UDC, 0); lsi_disconnect(s); - } else if (s->istat1 & LSI_ISTAT1_SRUN && !s->waiting) { + } else if (s->istat1 & LSI_ISTAT1_SRUN && s->waiting == LSI_NOWAIT) { if (s->dcntl & LSI_DCNTL_SSM) { lsi_script_dma_interrupt(s, LSI_DSTAT_SSI); } else { goto again; } } - DPRINTF("SCRIPTS execution stopped\n"); + trace_lsi_execute_script_stop(); } static uint8_t lsi_reg_readb(LSIState *s, int offset) @@ -1564,9 +1640,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) ret = s->ssid; break; case 0xb: /* SBCL */ - /* ??? This is not correct. However it's (hopefully) only - used for diagnostics, so should be ok. */ - ret = 0; + ret = s->sbcl; break; case 0xc: /* DSTAT */ ret = s->dstat | LSI_DSTAT_DFE; @@ -1614,7 +1688,7 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) break; CASE_GET_REG32(temp, 0x1c) case 0x20: /* DFIFO */ - ret = 0; + ret = s->dfifo; break; case 0x21: /* CTEST4 */ ret = s->ctest4; @@ -1703,8 +1777,10 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) break; case 0x58: /* SBDL */ /* Some drivers peek at the data bus during the MSG IN phase. */ - if ((s->sstat1 & PHASE_MASK) == PHASE_MI) + if ((s->sstat1 & PHASE_MASK) == PHASE_MI) { + assert(s->msg_len > 0); return s->msg[0]; + } ret = 0; break; case 0x59: /* SBDL high */ @@ -1746,10 +1822,8 @@ static uint8_t lsi_reg_readb(LSIState *s, int offset) #undef CASE_GET_REG24 #undef CASE_GET_REG32 -#ifdef DEBUG_LSI_REG - DPRINTF("Read reg %s %x = %02x\n", - offset < ARRAY_SIZE(names) ? names[offset] : "???", offset, ret); -#endif + trace_lsi_reg_read(offset < ARRAY_SIZE(names) ? names[offset] : "???", + offset, ret); return ret; } @@ -1767,21 +1841,22 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) case addr + 2: s->name &= 0xff00ffff; s->name |= val << 16; break; \ case addr + 3: s->name &= 0x00ffffff; s->name |= val << 24; break; -#ifdef DEBUG_LSI_REG - DPRINTF("Write reg %s %x = %02x\n", - offset < ARRAY_SIZE(names) ? names[offset] : "???", offset, val); -#endif + trace_lsi_reg_write(offset < ARRAY_SIZE(names) ? names[offset] : "???", + offset, val); + switch (offset) { case 0x00: /* SCNTL0 */ s->scntl0 = val; if (val & LSI_SCNTL0_START) { - BADF("Start sequence not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: Start sequence not implemented\n"); } break; case 0x01: /* SCNTL1 */ s->scntl1 = val & ~LSI_SCNTL1_SST; if (val & LSI_SCNTL1_IARB) { - BADF("Immediate Arbritration not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: Immediate Arbritration not implemented\n"); } if (val & LSI_SCNTL1_RST) { if (!(s->sstat0 & LSI_SSTAT0_RST)) { @@ -1808,7 +1883,8 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) break; case 0x06: /* SDID */ if ((s->ssid & 0x80) && (val & 0xf) != (s->ssid & 0xf)) { - BADF("Destination ID does not match SSID\n"); + qemu_log_mask(LOG_GUEST_ERROR, + "lsi_scsi: Destination ID does not match SSID\n"); } s->sdid = val & 0xf; break; @@ -1821,7 +1897,7 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) break; case 0x0a: case 0x0b: /* Openserver writes to these readonly registers on startup */ - return; + return; case 0x0c: case 0x0d: case 0x0e: case 0x0f: /* Linux writes to these readonly registers on startup. */ return; @@ -1835,9 +1911,9 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) s->istat0 &= ~LSI_ISTAT0_INTF; lsi_update_irq(s); } - if (s->waiting == 1 && val & LSI_ISTAT0_SIGP) { - DPRINTF("Woken by SIGP\n"); - s->waiting = 0; + if (s->waiting == LSI_WAIT_RESELECT && val & LSI_ISTAT0_SIGP) { + trace_lsi_awoken(); + s->waiting = LSI_NOWAIT; s->dsp = s->dnad; lsi_execute_script(s); } @@ -1855,21 +1931,23 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) /* nothing to do */ break; case 0x1a: /* CTEST2 */ - s->ctest2 = val & LSI_CTEST2_PCICIE; - break; + s->ctest2 = val & LSI_CTEST2_PCICIE; + break; case 0x1b: /* CTEST3 */ s->ctest3 = val & 0x0f; break; CASE_SET_REG32(temp, 0x1c) case 0x21: /* CTEST4 */ if (val & 7) { - BADF("Unimplemented CTEST4-FBL 0x%x\n", val); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: Unimplemented CTEST4-FBL 0x%x\n", val); } s->ctest4 = val; break; case 0x22: /* CTEST5 */ if (val & (LSI_CTEST5_ADCK | LSI_CTEST5_BBCK)) { - BADF("CTEST5 DMA increment not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: CTEST5 DMA increment not implemented\n"); } s->ctest5 = val; break; @@ -1926,7 +2004,8 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) break; case 0x49: /* STIME1 */ if (val & 0xf) { - DPRINTF("General purpose timer not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: General purpose timer not implemented\n"); /* ??? Raising the interrupt immediately seems to be sufficient to keep the FreeBSD driver happy. */ lsi_script_scsi_interrupt(s, 0, LSI_SIST1_GEN); @@ -1943,13 +2022,15 @@ static void lsi_reg_writeb(LSIState *s, int offset, uint8_t val) break; case 0x4e: /* STEST2 */ if (val & 1) { - BADF("Low level mode not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: Low level mode not implemented\n"); } s->stest2 = val; break; case 0x4f: /* STEST3 */ if (val & 0x41) { - BADF("SCSI FIFO test mode not implemented\n"); + qemu_log_mask(LOG_UNIMP, + "lsi_scsi: SCSI FIFO test mode not implemented\n"); } s->stest3 = val; break; @@ -2003,14 +2084,13 @@ static uint64_t lsi_mmio_read(void *opaque, hwaddr addr, unsigned size) { LSIState *s = opaque; - return lsi_reg_readb(s, addr & 0xff); } static const MemoryRegionOps lsi_mmio_ops = { .read = lsi_mmio_read, .write = lsi_mmio_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, @@ -2021,35 +2101,20 @@ static void lsi_ram_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { LSIState *s = opaque; - uint32_t newval; - uint32_t mask; - int shift; - - newval = s->script_ram[addr >> 2]; - shift = (addr & 3) * 8; - mask = ((uint64_t)1 << (size * 8)) - 1; - newval &= ~(mask << shift); - newval |= val << shift; - s->script_ram[addr >> 2] = newval; + stn_le_p(s->script_ram + addr, size, val); } static uint64_t lsi_ram_read(void *opaque, hwaddr addr, unsigned size) { LSIState *s = opaque; - uint32_t val; - uint32_t mask; - - val = s->script_ram[addr >> 2]; - mask = ((uint64_t)1 << (size * 8)) - 1; - val >>= (addr & 3) * 8; - return val & mask; + return ldn_le_p(s->script_ram + addr, size); } static const MemoryRegionOps lsi_ram_ops = { .read = lsi_ram_read, .write = lsi_ram_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, }; static uint64_t lsi_io_read(void *opaque, hwaddr addr, @@ -2069,7 +2134,7 @@ static void lsi_io_write(void *opaque, hwaddr addr, static const MemoryRegionOps lsi_io_ops = { .read = lsi_io_read, .write = lsi_io_write, - .endianness = DEVICE_NATIVE_ENDIAN, + .endianness = DEVICE_LITTLE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, @@ -2096,11 +2161,23 @@ static int lsi_pre_save(void *opaque) return 0; } +static int lsi_post_load(void *opaque, int version_id) +{ + LSIState *s = opaque; + + if (s->msg_len < 0 || s->msg_len > LSI_MAX_MSGIN_LEN) { + return -EINVAL; + } + + return 0; +} + static const VMStateDescription vmstate_lsi_scsi = { .name = "lsiscsi", - .version_id = 0, + .version_id = 1, .minimum_version_id = 0, .pre_save = lsi_pre_save, + .post_load = lsi_post_load, .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, LSIState), @@ -2156,6 +2233,7 @@ static const VMStateDescription vmstate_lsi_scsi = { VMSTATE_UINT8(stime0, LSIState), VMSTATE_UINT8(respid0, LSIState), VMSTATE_UINT8(respid1, LSIState), + VMSTATE_UINT8_V(sbcl, LSIState, 1), VMSTATE_UINT32(mmrs, LSIState), VMSTATE_UINT32(mmws, LSIState), VMSTATE_UINT32(sfs, LSIState), @@ -2173,7 +2251,7 @@ static const VMStateDescription vmstate_lsi_scsi = { VMSTATE_BUFFER_UNSAFE(scratch, LSIState, 0, 18 * sizeof(uint32_t)), VMSTATE_UINT8(sbr, LSIState), - VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 2048 * sizeof(uint32_t)), + VMSTATE_BUFFER_UNSAFE(script_ram, LSIState, 0, 8192), VMSTATE_END_OF_LIST() } }; @@ -2209,6 +2287,7 @@ static void lsi_scsi_realize(PCIDevice *dev, Error **errp) "lsi-io", 256); address_space_init(&s->pci_io_as, pci_address_space_io(dev), "lsi-pci-io"); + qdev_init_gpio_out(d, &s->ext_irq, 1); pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &s->io_io); pci_register_bar(dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->mmio_io); @@ -2273,16 +2352,9 @@ static void lsi53c895a_register_types(void) type_init(lsi53c895a_register_types) -void lsi53c895a_create(PCIBus *bus) +void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev) { - LSIState *s = LSI53C895A(pci_create_simple(bus, -1, "lsi53c895a")); - - scsi_bus_legacy_handle_cmdline(&s->bus); -} - -void lsi53c810_create(PCIBus *bus, int devfn) -{ - LSIState *s = LSI53C895A(pci_create_simple(bus, devfn, "lsi53c810")); + LSIState *s = LSI53C895A(lsi_dev); scsi_bus_legacy_handle_cmdline(&s->bus); } diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index ba1afa3c1e..a56317e026 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -464,6 +464,7 @@ static void megasas_unmap_frame(MegasasState *s, MegasasCmd *cmd) cmd->frame = NULL; cmd->pa = 0; cmd->pa_size = 0; + qemu_sglist_destroy(&cmd->qsg); clear_bit(cmd->index, s->frame_map); } @@ -580,7 +581,6 @@ static void megasas_complete_frame(MegasasState *s, uint64_t context) static void megasas_complete_command(MegasasCmd *cmd) { - qemu_sglist_destroy(&cmd->qsg); cmd->iov_size = 0; cmd->iov_offset = 0; diff --git a/hw/scsi/mptendian.c b/hw/scsi/mptendian.c index 8ae39a76f4..79f99734d2 100644 --- a/hw/scsi/mptendian.c +++ b/hw/scsi/mptendian.c @@ -35,152 +35,155 @@ static void mptsas_fix_sgentry_endianness(MPISGEntry *sge) { - le32_to_cpus(&sge->FlagsLength); + sge->FlagsLength = le32_to_cpu(sge->FlagsLength); if (sge->FlagsLength & MPI_SGE_FLAGS_64_BIT_ADDRESSING) { - le64_to_cpus(&sge->u.Address64); + sge->u.Address64 = le64_to_cpu(sge->u.Address64); } else { - le32_to_cpus(&sge->u.Address32); + sge->u.Address32 = le32_to_cpu(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); + sge->u.Address64 = cpu_to_le64(sge->u.Address64); } else { - cpu_to_le32s(&sge->u.Address32); + sge->u.Address32 = cpu_to_le32(sge->u.Address32); } - cpu_to_le32s(&sge->FlagsLength); + sge->FlagsLength = cpu_to_le32(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); + req->MsgContext = le32_to_cpu(req->MsgContext); + req->Control = le32_to_cpu(req->Control); + req->DataLength = le32_to_cpu(req->DataLength); + req->SenseBufferLowAddr = le32_to_cpu(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); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); + reply->TransferCount = cpu_to_le32(reply->TransferCount); + reply->SenseCount = cpu_to_le32(reply->SenseCount); + reply->ResponseInfo = cpu_to_le32(reply->ResponseInfo); + reply->TaskTag = cpu_to_le16(reply->TaskTag); } void mptsas_fix_scsi_task_mgmt_endianness(MPIMsgSCSITaskMgmt *req) { - le32_to_cpus(&req->MsgContext); - le32_to_cpus(&req->TaskMsgContext); + req->MsgContext = le32_to_cpu(req->MsgContext); + req->TaskMsgContext = le32_to_cpu(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); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); + reply->TerminationCount = cpu_to_le32(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); + req->MsgContext = le32_to_cpu(req->MsgContext); + req->ReplyFrameSize = le16_to_cpu(req->ReplyFrameSize); + req->HostMfaHighAddr = le32_to_cpu(req->HostMfaHighAddr); + req->SenseBufferHighAddr = le32_to_cpu(req->SenseBufferHighAddr); + req->ReplyFifoHostSignalingAddr = + le32_to_cpu(req->ReplyFifoHostSignalingAddr); mptsas_fix_sgentry_endianness(&req->HostPageBufferSGE); - le16_to_cpus(&req->MsgVersion); - le16_to_cpus(&req->HeaderVersion); + req->MsgVersion = le16_to_cpu(req->MsgVersion); + req->HeaderVersion = le16_to_cpu(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); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); } void mptsas_fix_ioc_facts_endianness(MPIMsgIOCFacts *req) { - le32_to_cpus(&req->MsgContext); + req->MsgContext = le32_to_cpu(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); + reply->MsgVersion = cpu_to_le16(reply->MsgVersion); + reply->HeaderVersion = cpu_to_le16(reply->HeaderVersion); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCExceptions = cpu_to_le16(reply->IOCExceptions); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); + reply->ReplyQueueDepth = cpu_to_le16(reply->ReplyQueueDepth); + reply->RequestFrameSize = cpu_to_le16(reply->RequestFrameSize); + reply->ProductID = cpu_to_le16(reply->ProductID); + reply->CurrentHostMfaHighAddr = cpu_to_le32(reply->CurrentHostMfaHighAddr); + reply->GlobalCredits = cpu_to_le16(reply->GlobalCredits); + reply->CurrentSenseBufferHighAddr = + cpu_to_le32(reply->CurrentSenseBufferHighAddr); + reply->CurReplyFrameSize = cpu_to_le16(reply->CurReplyFrameSize); + reply->FWImageSize = cpu_to_le32(reply->FWImageSize); + reply->IOCCapabilities = cpu_to_le32(reply->IOCCapabilities); + reply->HighPriorityQueueDepth = cpu_to_le16(reply->HighPriorityQueueDepth); mptsas_fix_sgentry_endianness_reply(&reply->HostPageBufferSGE); - cpu_to_le32s(&reply->ReplyFifoHostSignalingAddr); + reply->ReplyFifoHostSignalingAddr = + cpu_to_le32(reply->ReplyFifoHostSignalingAddr); } void mptsas_fix_config_endianness(MPIMsgConfig *req) { - le16_to_cpus(&req->ExtPageLength); - le32_to_cpus(&req->MsgContext); - le32_to_cpus(&req->PageAddress); + req->ExtPageLength = le16_to_cpu(req->ExtPageLength); + req->MsgContext = le32_to_cpu(req->MsgContext); + req->PageAddress = le32_to_cpu(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); + reply->ExtPageLength = cpu_to_le16(reply->ExtPageLength); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); } void mptsas_fix_port_facts_endianness(MPIMsgPortFacts *req) { - le32_to_cpus(&req->MsgContext); + req->MsgContext = le32_to_cpu(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); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); + reply->MaxDevices = cpu_to_le16(reply->MaxDevices); + reply->PortSCSIID = cpu_to_le16(reply->PortSCSIID); + reply->ProtocolFlags = cpu_to_le16(reply->ProtocolFlags); + reply->MaxPostedCmdBuffers = cpu_to_le16(reply->MaxPostedCmdBuffers); + reply->MaxPersistentIDs = cpu_to_le16(reply->MaxPersistentIDs); + reply->MaxLanBuckets = cpu_to_le16(reply->MaxLanBuckets); } void mptsas_fix_port_enable_endianness(MPIMsgPortEnable *req) { - le32_to_cpus(&req->MsgContext); + req->MsgContext = le32_to_cpu(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); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); } void mptsas_fix_event_notification_endianness(MPIMsgEventNotify *req) { - le32_to_cpus(&req->MsgContext); + req->MsgContext = le32_to_cpu(req->MsgContext); } void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *reply) @@ -188,16 +191,16 @@ void mptsas_fix_event_notification_reply_endianness(MPIMsgEventNotifyReply *repl 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); + reply->EventDataLength = cpu_to_le16(reply->EventDataLength); + reply->MsgContext = cpu_to_le32(reply->MsgContext); + reply->IOCStatus = cpu_to_le16(reply->IOCStatus); + reply->IOCLogInfo = cpu_to_le32(reply->IOCLogInfo); + reply->Event = cpu_to_le32(reply->Event); + reply->EventContext = cpu_to_le32(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]); + reply->Data[i] = cpu_to_le32(reply->Data[i]); } } diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 4176e871e1..929404fb48 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1431,6 +1431,7 @@ static void mptsas1068_class_init(ObjectClass *oc, void *data) dc->reset = mptsas_reset; dc->vmsd = &vmstate_mptsas; dc->desc = "LSI SAS 1068"; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } static const TypeInfo mptsas_info = { diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 5905f6bf29..c480553083 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -1554,7 +1554,7 @@ SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) BusChild *kid; SCSIDevice *target_dev = NULL; - QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, ChildrenHead, sibling) { + QTAILQ_FOREACH_REVERSE(kid, &bus->qbus.children, sibling) { DeviceState *qdev = kid->child; SCSIDevice *dev = SCSI_DEVICE(qdev); @@ -1571,7 +1571,7 @@ SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int id, int lun) /* SCSI request list. For simplicity, pv points to the whole device */ static int put_scsi_requests(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { SCSIDevice *s = pv; SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); @@ -1599,7 +1599,7 @@ static int put_scsi_requests(QEMUFile *f, void *pv, size_t size, } static int get_scsi_requests(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { SCSIDevice *s = pv; SCSIBus *bus = DO_UPCAST(SCSIBus, qbus, s->qdev.parent_bus); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 5bb390773b..e7e865ab3b 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -19,20 +19,12 @@ * the host adapter emulator. */ -//#define DEBUG_SCSI - -#ifdef DEBUG_SCSI -#define DPRINTF(fmt, ...) \ -do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" +#include "hw/scsi/emulation.h" #include "scsi/constants.h" #include "sysemu/sysemu.h" #include "sysemu/block-backend.h" @@ -40,6 +32,7 @@ do { printf("scsi-disk: " fmt , ## __VA_ARGS__); } while (0) #include "hw/block/block.h" #include "sysemu/dma.h" #include "qemu/cutils.h" +#include "trace.h" #ifdef __linux #include @@ -103,6 +96,7 @@ typedef struct SCSIDiskState char *serial; char *vendor; char *product; + char *device_id; bool tray_open; bool tray_locked; /* @@ -127,8 +121,8 @@ static void scsi_free_request(SCSIRequest *req) /* Helper function for command completion with sense. */ static void scsi_check_condition(SCSIDiskReq *r, SCSISense sense) { - DPRINTF("Command complete tag=0x%x sense=%d/%d/%d\n", - r->req.tag, sense.key, sense.asc, sense.ascq); + trace_scsi_disk_check_condition(r->req.tag, sense.key, sense.asc, + sense.ascq); scsi_req_build_sense(&r->req, sense); scsi_req_complete(&r->req, CHECK_CONDITION); } @@ -302,22 +296,15 @@ static void scsi_dma_complete(void *opaque, int ret) aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } -static void scsi_read_complete(void * opaque, int ret) +static void scsi_read_complete_noio(SCSIDiskReq *r, int ret) { - SCSIDiskReq *r = (SCSIDiskReq *)opaque; - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - int n; + uint32_t n; - assert(r->req.aiocb != NULL); - r->req.aiocb = NULL; - aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); - if (scsi_disk_req_check_error(r, ret, true)) { + assert(r->req.aiocb == NULL); + if (scsi_disk_req_check_error(r, ret, false)) { 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; r->sector += n; r->sector_count -= n; @@ -325,6 +312,24 @@ static void scsi_read_complete(void * opaque, int ret) done: scsi_req_unref(&r->req); +} + +static void scsi_read_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; + + aio_context_acquire(blk_get_aio_context(s->qdev.conf.blk)); + 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); + trace_scsi_disk_read_complete(r->req.tag, r->qiov.size); + } + scsi_read_complete_noio(r, ret); aio_context_release(blk_get_aio_context(s->qdev.conf.blk)); } @@ -387,7 +392,7 @@ static void scsi_read_data(SCSIRequest *req) SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); bool first; - DPRINTF("Read sector_count=%d\n", r->sector_count); + trace_scsi_disk_read_data_count(r->sector_count); if (r->sector_count == 0) { /* This also clears the sense buffer for REQUEST SENSE. */ scsi_req_complete(&r->req, GOOD); @@ -400,13 +405,13 @@ 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->req.cmd.mode == SCSI_XFER_TO_DEV) { - DPRINTF("Data transfer direction invalid\n"); - scsi_read_complete(r, -EINVAL); + trace_scsi_disk_read_data_invalid(); + scsi_read_complete_noio(r, -EINVAL); return; } if (!blk_is_available(req->dev->conf.blk)) { - scsi_read_complete(r, -ENOMEDIUM); + scsi_read_complete_noio(r, -ENOMEDIUM); return; } @@ -441,9 +446,18 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed) } switch (error) { case 0: - /* The command has run, no need to fake sense. */ + /* A passthrough command has run and has produced sense data; check + * whether the error has to be handled by the guest or should rather + * pause the host. + */ assert(r->status && *r->status); - scsi_req_complete(&r->req, *r->status); + error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); + if (error == ECANCELED || error == EAGAIN || error == ENOTCONN || + error == 0) { + /* These errors are handled by guest. */ + scsi_req_complete(&r->req, *r->status); + return true; + } break; case ENOMEDIUM: scsi_check_condition(r, SENSE_CODE(NO_MEDIUM)); @@ -462,23 +476,17 @@ static bool scsi_handle_rw_error(SCSIDiskReq *r, int error, bool acct_failed) break; } } - if (!error) { - assert(r->status && *r->status); - error = scsi_sense_buf_to_errno(r->req.sense, sizeof(r->req.sense)); - - if (error == ECANCELED || error == EAGAIN || error == ENOTCONN || - error == 0) { - /* These errors are handled by guest. */ - scsi_req_complete(&r->req, *r->status); - return true; - } - } blk_error_action(s->qdev.conf.blk, action, is_read, error); + if (action == BLOCK_ERROR_ACTION_IGNORE) { + scsi_req_complete(&r->req, 0); + return true; + } + if (action == BLOCK_ERROR_ACTION_STOP) { scsi_req_retry(&r->req); } - return action != BLOCK_ERROR_ACTION_IGNORE; + return true; } static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) @@ -498,7 +506,7 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) return; } else { scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); - DPRINTF("Write complete tag=0x%x more=%zd\n", r->req.tag, r->qiov.size); + trace_scsi_disk_write_complete_noio(r->req.tag, r->qiov.size); scsi_req_data(&r->req, r->qiov.size); } @@ -536,7 +544,7 @@ static void scsi_write_data(SCSIRequest *req) /* The request is used as the AIO opaque value, so add a ref. */ scsi_req_ref(&r->req); if (r->req.cmd.mode != SCSI_XFER_TO_DEV) { - DPRINTF("Data transfer direction invalid\n"); + trace_scsi_disk_write_data_invalid(); scsi_write_complete_noio(r, -EINVAL); return; } @@ -586,7 +594,7 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return (uint8_t *)r->iov.iov_base; } -int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) +static int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, req->dev); uint8_t page_code = req->cmd.buf[2]; @@ -601,8 +609,7 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) switch (page_code) { case 0x00: /* Supported page codes, mandatory */ { - DPRINTF("Inquiry EVPD[Supported pages] " - "buffer size %zd\n", req->cmd.xfer); + trace_scsi_disk_emulate_vpd_page_00(req->cmd.xfer); outbuf[buflen++] = 0x00; /* list of supported pages (this page) */ if (s->serial) { outbuf[buflen++] = 0x80; /* unit serial number */ @@ -620,7 +627,7 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) int l; if (!s->serial) { - DPRINTF("Inquiry (EVPD[Serial number] not supported\n"); + trace_scsi_disk_emulate_vpd_page_80_not_supported(); return -1; } @@ -629,8 +636,7 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) l = 36; } - DPRINTF("Inquiry EVPD[Serial number] " - "buffer size %zd\n", req->cmd.xfer); + trace_scsi_disk_emulate_vpd_page_80(req->cmd.xfer); memcpy(outbuf + buflen, s->serial, l); buflen += l; break; @@ -638,22 +644,18 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) case 0x83: /* Device identification page, mandatory */ { - const char *str = s->serial ?: blk_name(s->qdev.conf.blk); - int max_len = s->serial ? 20 : 255 - 8; - int id_len = strlen(str); + int id_len = s->device_id ? MIN(strlen(s->device_id), 255 - 8) : 0; - if (id_len > max_len) { - id_len = max_len; + trace_scsi_disk_emulate_vpd_page_83(req->cmd.xfer); + + if (id_len) { + outbuf[buflen++] = 0x2; /* ASCII */ + outbuf[buflen++] = 0; /* not officially assigned */ + outbuf[buflen++] = 0; /* reserved */ + outbuf[buflen++] = id_len; /* length of data following */ + memcpy(outbuf + buflen, s->device_id, id_len); + buflen += id_len; } - DPRINTF("Inquiry EVPD[Device identification] " - "buffer size %zd\n", req->cmd.xfer); - - outbuf[buflen++] = 0x2; /* ASCII */ - outbuf[buflen++] = 0; /* not officially assigned */ - outbuf[buflen++] = 0; /* reserved */ - outbuf[buflen++] = id_len; /* length of data following */ - memcpy(outbuf + buflen, str, id_len); - buflen += id_len; if (s->qdev.wwn) { outbuf[buflen++] = 0x1; /* Binary */ @@ -688,89 +690,35 @@ int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf) } case 0xb0: /* block limits */ { - unsigned int unmap_sectors = - s->qdev.conf.discard_granularity / s->qdev.blocksize; - unsigned int min_io_size = - s->qdev.conf.min_io_size / s->qdev.blocksize; - unsigned int opt_io_size = - s->qdev.conf.opt_io_size / s->qdev.blocksize; - unsigned int max_unmap_sectors = - s->max_unmap_size / s->qdev.blocksize; - unsigned int max_io_sectors = - s->max_io_size / s->qdev.blocksize; + SCSIBlockLimits bl = {}; if (s->qdev.type == TYPE_ROM) { - DPRINTF("Inquiry (EVPD[%02X] not supported for CDROM\n", - page_code); + trace_scsi_disk_emulate_vpd_page_b0_not_supported(); return -1; } + bl.wsnz = 1; + bl.unmap_sectors = + s->qdev.conf.discard_granularity / s->qdev.blocksize; + bl.min_io_size = + s->qdev.conf.min_io_size / s->qdev.blocksize; + bl.opt_io_size = + s->qdev.conf.opt_io_size / s->qdev.blocksize; + bl.max_unmap_sectors = + s->max_unmap_size / s->qdev.blocksize; + bl.max_io_sectors = + s->max_io_size / s->qdev.blocksize; + /* 255 descriptors fit in 4 KiB with an 8-byte header */ + bl.max_unmap_descr = 255; + if (s->qdev.type == TYPE_DISK) { int max_transfer_blk = blk_get_max_transfer(s->qdev.conf.blk); int max_io_sectors_blk = max_transfer_blk / s->qdev.blocksize; - max_io_sectors = - MIN_NON_ZERO(max_io_sectors_blk, max_io_sectors); - - /* min_io_size and opt_io_size can't be greater than - * max_io_sectors */ - if (min_io_size) { - min_io_size = MIN(min_io_size, max_io_sectors); - } - if (opt_io_size) { - opt_io_size = MIN(opt_io_size, max_io_sectors); - } + bl.max_io_sectors = + MIN_NON_ZERO(max_io_sectors_blk, bl.max_io_sectors); } - /* required VPD size with unmap support */ - buflen = 0x40; - memset(outbuf + 4, 0, buflen - 4); - - outbuf[4] = 0x1; /* wsnz */ - - /* optimal transfer length granularity */ - outbuf[6] = (min_io_size >> 8) & 0xff; - outbuf[7] = min_io_size & 0xff; - - /* maximum transfer length */ - outbuf[8] = (max_io_sectors >> 24) & 0xff; - outbuf[9] = (max_io_sectors >> 16) & 0xff; - outbuf[10] = (max_io_sectors >> 8) & 0xff; - outbuf[11] = max_io_sectors & 0xff; - - /* optimal transfer length */ - outbuf[12] = (opt_io_size >> 24) & 0xff; - outbuf[13] = (opt_io_size >> 16) & 0xff; - outbuf[14] = (opt_io_size >> 8) & 0xff; - outbuf[15] = opt_io_size & 0xff; - - /* max unmap LBA count, default is 1GB */ - outbuf[20] = (max_unmap_sectors >> 24) & 0xff; - outbuf[21] = (max_unmap_sectors >> 16) & 0xff; - outbuf[22] = (max_unmap_sectors >> 8) & 0xff; - outbuf[23] = max_unmap_sectors & 0xff; - - /* max unmap descriptors, 255 fit in 4 kb with an 8-byte header */ - outbuf[24] = 0; - outbuf[25] = 0; - outbuf[26] = 0; - outbuf[27] = 255; - - /* optimal unmap granularity */ - outbuf[28] = (unmap_sectors >> 24) & 0xff; - outbuf[29] = (unmap_sectors >> 16) & 0xff; - outbuf[30] = (unmap_sectors >> 8) & 0xff; - outbuf[31] = unmap_sectors & 0xff; - - /* max write same size */ - outbuf[36] = 0; - outbuf[37] = 0; - outbuf[38] = 0; - outbuf[39] = 0; - - outbuf[40] = (max_io_sectors >> 24) & 0xff; - outbuf[41] = (max_io_sectors >> 16) & 0xff; - outbuf[42] = (max_io_sectors >> 8) & 0xff; - outbuf[43] = max_io_sectors & 0xff; + buflen += scsi_emulate_block_limits(outbuf + buflen, &bl); break; } case 0xb1: /* block device characteristics */ @@ -1292,8 +1240,9 @@ static int scsi_disk_emulate_mode_sense(SCSIDiskReq *r, uint8_t *outbuf) dbd = (r->req.cmd.buf[1] & 0x8) != 0; page = r->req.cmd.buf[2] & 0x3f; page_control = (r->req.cmd.buf[2] & 0xc0) >> 6; - DPRINTF("Mode Sense(%d) (page %d, xfer %zd, page_control %d)\n", - (r->req.cmd.buf[0] == MODE_SENSE) ? 6 : 10, page, r->req.cmd.xfer, page_control); + + trace_scsi_disk_emulate_mode_sense((r->req.cmd.buf[0] == MODE_SENSE) ? 6 : + 10, page, r->req.cmd.xfer, page_control); memset(outbuf, 0, r->req.cmd.xfer); p = outbuf; @@ -1385,7 +1334,7 @@ static int scsi_disk_emulate_read_toc(SCSIRequest *req, uint8_t *outbuf) format = req->cmd.buf[2] & 0xf; start_track = req->cmd.buf[6]; blk_get_geometry(s->qdev.conf.blk, &nb_sectors); - DPRINTF("Read TOC (track %d format %d msf %d)\n", start_track, format, msf >> 1); + trace_scsi_disk_emulate_read_toc(start_track, format, msf >> 1); nb_sectors /= s->qdev.blocksize / 512; switch (format) { case 0: @@ -1444,7 +1393,7 @@ static void scsi_disk_emulate_read_data(SCSIRequest *req) int buflen = r->iov.iov_len; if (buflen) { - DPRINTF("Read buf_len=%d\n", buflen); + trace_scsi_disk_emulate_read_data(buflen); r->iov.iov_len = 0; r->started = true; scsi_req_data(&r->req, buflen); @@ -1863,7 +1812,7 @@ static void scsi_disk_emulate_write_data(SCSIRequest *req) if (r->iov.iov_len) { int buflen = r->iov.iov_len; - DPRINTF("Write buf_len=%d\n", buflen); + trace_scsi_disk_emulate_write_data(buflen); r->iov.iov_len = 0; scsi_req_data(&r->req, buflen); return; @@ -2072,7 +2021,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) case SERVICE_ACTION_IN_16: /* Service Action In subcommands. */ if ((req->cmd.buf[1] & 31) == SAI_READ_CAPACITY_16) { - DPRINTF("SAI READ CAPACITY(16)\n"); + trace_scsi_disk_emulate_command_SAI_16(); memset(outbuf, 0, req->cmd.xfer); blk_get_geometry(s->qdev.conf.blk, &nb_sectors); if (!nb_sectors) { @@ -2110,7 +2059,7 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) /* Protection, exponent and lowest lba field left blank. */ break; } - DPRINTF("Unsupported Service Action In\n"); + trace_scsi_disk_emulate_command_SAI_unsupported(); goto illegal_request; case SYNCHRONIZE_CACHE: /* The request is used as the AIO opaque value, so add a ref. */ @@ -2120,37 +2069,36 @@ static int32_t scsi_disk_emulate_command(SCSIRequest *req, uint8_t *buf) r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); return 0; case SEEK_10: - DPRINTF("Seek(10) (sector %" PRId64 ")\n", r->req.cmd.lba); + trace_scsi_disk_emulate_command_SEEK_10(r->req.cmd.lba); if (r->req.cmd.lba > s->qdev.max_lba) { goto illegal_lba; } break; case MODE_SELECT: - DPRINTF("Mode Select(6) (len %lu)\n", (unsigned long)r->req.cmd.xfer); + trace_scsi_disk_emulate_command_MODE_SELECT(r->req.cmd.xfer); break; case MODE_SELECT_10: - DPRINTF("Mode Select(10) (len %lu)\n", (unsigned long)r->req.cmd.xfer); + trace_scsi_disk_emulate_command_MODE_SELECT_10(r->req.cmd.xfer); break; case UNMAP: - DPRINTF("Unmap (len %lu)\n", (unsigned long)r->req.cmd.xfer); + trace_scsi_disk_emulate_command_UNMAP(r->req.cmd.xfer); break; case VERIFY_10: case VERIFY_12: case VERIFY_16: - DPRINTF("Verify (bytchk %d)\n", (req->cmd.buf[1] >> 1) & 3); + trace_scsi_disk_emulate_command_VERIFY((req->cmd.buf[1] >> 1) & 3); if (req->cmd.buf[1] & 6) { goto illegal_request; } break; case WRITE_SAME_10: case WRITE_SAME_16: - DPRINTF("WRITE SAME %d (len %lu)\n", - req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, - (unsigned long)r->req.cmd.xfer); + trace_scsi_disk_emulate_command_WRITE_SAME( + req->cmd.buf[0] == WRITE_SAME_10 ? 10 : 16, r->req.cmd.xfer); break; default: - DPRINTF("Unknown SCSI command (%2.2x=%s)\n", buf[0], - scsi_command_name(buf[0])); + trace_scsi_disk_emulate_command_UNKNOWN(buf[0], + scsi_command_name(buf[0])); scsi_check_condition(r, SENSE_CODE(INVALID_OPCODE)); return 0; } @@ -2203,7 +2151,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) case READ_10: case READ_12: case READ_16: - DPRINTF("Read (sector %" PRId64 ", count %u)\n", r->req.cmd.lba, len); + trace_scsi_disk_dma_command_READ(r->req.cmd.lba, len); /* Protection information is not supported. For SCSI versions 2 and * older (as determined by snooping the guest's INQUIRY commands), * there is no RD/WR/VRPROTECT, so skip this check in these versions. @@ -2228,7 +2176,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(WRITE_PROTECTED)); return 0; } - DPRINTF("Write %s(sector %" PRId64 ", count %u)\n", + trace_scsi_disk_dma_command_WRITE( (command & 0xe) == 0xe ? "And Verify " : "", r->req.cmd.lba, len); /* fall through */ @@ -2379,7 +2327,6 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) return; } - blkconf_serial(&s->qdev.conf, &s->serial); blkconf_blocksizes(&s->qdev.conf); if (s->qdev.conf.logical_block_size > @@ -2411,6 +2358,16 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) if (!s->vendor) { s->vendor = g_strdup("QEMU"); } + if (!s->device_id) { + if (s->serial) { + s->device_id = g_strdup_printf("%.20s", s->serial); + } else { + const char *str = blk_name(s->qdev.conf.blk); + if (str && *str) { + s->device_id = g_strdup(str); + } + } + } if (blk_is_sg(s->qdev.conf.blk)) { error_setg(errp, "unwanted /dev/sg*"); @@ -2431,10 +2388,13 @@ static void scsi_realize(SCSIDevice *dev, Error **errp) static void scsi_hd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + AioContext *ctx = NULL; /* can happen for devices without drive. The error message for missing * backend will be issued in scsi_realize */ if (s->qdev.conf.blk) { + ctx = blk_get_aio_context(s->qdev.conf.blk); + aio_context_acquire(ctx); blkconf_blocksizes(&s->qdev.conf); } s->qdev.blocksize = s->qdev.conf.logical_block_size; @@ -2443,11 +2403,15 @@ static void scsi_hd_realize(SCSIDevice *dev, Error **errp) s->product = g_strdup("QEMU HARDDISK"); } scsi_realize(&s->qdev, errp); + if (ctx) { + aio_context_release(ctx); + } } static void scsi_cd_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + AioContext *ctx; int ret; if (!dev->conf.blk) { @@ -2458,6 +2422,8 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) assert(ret == 0); } + ctx = blk_get_aio_context(dev->conf.blk); + aio_context_acquire(ctx); s->qdev.blocksize = 2048; s->qdev.type = TYPE_ROM; s->features |= 1 << SCSI_DISK_F_REMOVABLE; @@ -2465,6 +2431,7 @@ static void scsi_cd_realize(SCSIDevice *dev, Error **errp) s->product = g_strdup("QEMU CD-ROM"); } scsi_realize(&s->qdev, errp); + aio_context_release(ctx); } static void scsi_disk_realize(SCSIDevice *dev, Error **errp) @@ -2547,6 +2514,22 @@ static const SCSIReqOps *const scsi_disk_reqops_dispatch[256] = { [WRITE_VERIFY_16] = &scsi_disk_dma_reqops, }; +static void scsi_disk_new_request_dump(uint32_t lun, uint32_t tag, uint8_t *buf) +{ + int i; + int len = scsi_cdb_length(buf); + char *line_buffer, *p; + + line_buffer = g_malloc(len * 5 + 1); + + for (i = 0, p = line_buffer; i < len; i++) { + p += sprintf(p, " 0x%02x", buf[i]); + } + trace_scsi_disk_new_request(lun, tag, line_buffer); + + g_free(line_buffer); +} + static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, uint8_t *buf, void *hba_private) { @@ -2562,16 +2545,9 @@ static SCSIRequest *scsi_new_request(SCSIDevice *d, uint32_t tag, uint32_t lun, } req = scsi_req_alloc(ops, &s->qdev, tag, lun, hba_private); -#ifdef DEBUG_SCSI - DPRINTF("Command: lun=%d tag=0x%x data=0x%02x", lun, tag, buf[0]); - { - int i; - for (i = 1; i < scsi_cdb_length(buf); i++) { - printf(" 0x%02x", buf[i]); - } - printf("\n"); + if (trace_event_get_state_backends(TRACE_SCSI_DISK_NEW_REQUEST)) { + scsi_disk_new_request_dump(lun, tag, buf); } -#endif return req; } @@ -2603,6 +2579,7 @@ static int get_device_type(SCSIDiskState *s) static void scsi_block_realize(SCSIDevice *dev, Error **errp) { SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, dev); + AioContext *ctx; int sg_version; int rc; @@ -2611,6 +2588,15 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) return; } + if (s->rotation_rate) { + error_report_once("rotation_rate is specified for scsi-block but is " + "not implemented. This option is deprecated and will " + "be removed in a future version"); + } + + ctx = blk_get_aio_context(s->qdev.conf.blk); + aio_context_acquire(ctx); + /* check we are using a driver managing SG_IO (version 3 and after) */ rc = blk_ioctl(s->qdev.conf.blk, SG_GET_VERSION_NUM, &sg_version); if (rc < 0) { @@ -2618,18 +2604,18 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) if (rc != -EPERM) { error_append_hint(errp, "Is this a SCSI device?\n"); } - return; + goto out; } if (sg_version < 30000) { error_setg(errp, "scsi generic interface too old"); - return; + goto out; } /* get device type from INQUIRY data */ rc = get_device_type(s); if (rc < 0) { error_setg(errp, "INQUIRY failed"); - return; + goto out; } /* Make a guess for the block size, we'll fix it when the guest sends. @@ -2649,6 +2635,9 @@ static void scsi_block_realize(SCSIDevice *dev, Error **errp) scsi_realize(&s->qdev, errp); scsi_generic_read_device_inquiry(&s->qdev); + +out: + aio_context_release(ctx); } typedef struct SCSIBlockReq { @@ -2946,7 +2935,9 @@ static const TypeInfo scsi_disk_base_info = { DEFINE_PROP_STRING("ver", SCSIDiskState, version), \ DEFINE_PROP_STRING("serial", SCSIDiskState, serial), \ DEFINE_PROP_STRING("vendor", SCSIDiskState, vendor), \ - DEFINE_PROP_STRING("product", SCSIDiskState, product) + DEFINE_PROP_STRING("product", SCSIDiskState, product), \ + DEFINE_PROP_STRING("device_id", SCSIDiskState, device_id) + static Property scsi_hd_properties[] = { DEFINE_SCSI_DISK_PROPERTIES(), diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index d60c4d0fcf..d82b462be4 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -16,22 +16,12 @@ #include "qemu-common.h" #include "qemu/error-report.h" #include "hw/scsi/scsi.h" +#include "hw/scsi/emulation.h" #include "sysemu/block-backend.h" +#include "trace.h" #ifdef __linux__ -//#define DEBUG_SCSI - -#ifdef DEBUG_SCSI -#define DPRINTF(fmt, ...) \ -do { printf("scsi-generic: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif - -#define BADF(fmt, ...) \ -do { fprintf(stderr, "scsi-generic: " fmt , ## __VA_ARGS__); } while (0) - #include #include "scsi/constants.h" @@ -97,8 +87,7 @@ static void scsi_command_complete_noio(SCSIGenericReq *r, int ret) } } - DPRINTF("Command complete 0x%p tag=0x%x status=%d\n", - r, r->req.tag, status); + trace_scsi_generic_command_complete_noio(r, r->req.tag, status); scsi_req_complete(&r->req, status); done: @@ -144,7 +133,7 @@ static int execute_command(BlockBackend *blk, static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) { - uint8_t page, page_len; + uint8_t page, page_idx; /* * EVPD set to zero returns the standard INQUIRY data. @@ -181,7 +170,7 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) /* Also take care of the opt xfer len. */ stl_be_p(&r->buf[12], MIN_NON_ZERO(max_transfer, ldl_be_p(&r->buf[12]))); - } else if (page == 0x00 && s->needs_vpd_bl_emulation) { + } else if (s->needs_vpd_bl_emulation && page == 0x00 && r->buflen >= 4) { /* * Now we're capable of supplying the VPD Block Limits * response if the hardware can't. Add it in the INQUIRY @@ -190,17 +179,45 @@ static void scsi_handle_inquiry_reply(SCSIGenericReq *r, SCSIDevice *s) * * This way, the guest kernel will be aware of the support * and will use it to proper setup the SCSI device. + * + * VPD page numbers must be sorted, so insert 0xb0 at the + * right place with an in-place insert. When the while loop + * begins the device response is at r[0] to r[page_idx - 1]. */ - page_len = r->buf[3]; - r->buf[page_len + 4] = 0xb0; - r->buf[3] = ++page_len; + page_idx = lduw_be_p(r->buf + 2) + 4; + page_idx = MIN(page_idx, r->buflen); + while (page_idx > 4 && r->buf[page_idx - 1] >= 0xb0) { + if (page_idx < r->buflen) { + r->buf[page_idx] = r->buf[page_idx - 1]; + } + page_idx--; + } + if (page_idx < r->buflen) { + r->buf[page_idx] = 0xb0; + } + stw_be_p(r->buf + 2, lduw_be_p(r->buf + 2) + 1); } } } -static int scsi_emulate_block_limits(SCSIGenericReq *r) +static int scsi_generic_emulate_block_limits(SCSIGenericReq *r, SCSIDevice *s) { - r->buflen = scsi_disk_emulate_vpd_page(&r->req, r->buf); + int len; + uint8_t buf[64]; + + SCSIBlockLimits bl = { + .max_io_sectors = blk_get_max_transfer(s->conf.blk) / s->blocksize + }; + + memset(r->buf, 0, r->buflen); + stb_p(buf, s->type); + stb_p(buf + 1, 0xb0); + len = scsi_emulate_block_limits(buf + 4, &bl); + assert(len <= sizeof(buf) - 4); + stw_be_p(buf + 2, len); + + memcpy(r->buf, buf, MIN(r->buflen, len + 4)); + r->io_header.sb_len_wr = 0; /* @@ -219,7 +236,6 @@ static void scsi_read_complete(void * opaque, int ret) { SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIDevice *s = r->req.dev; - SCSISense sense; int len; assert(r->req.aiocb != NULL); @@ -233,7 +249,7 @@ static void scsi_read_complete(void * opaque, int ret) } len = r->io_header.dxfer_len - r->io_header.resid; - DPRINTF("Data ready tag=0x%x len=%d\n", r->req.tag, len); + trace_scsi_generic_read_complete(r->req.tag, len); r->len = -1; @@ -242,13 +258,15 @@ static void scsi_read_complete(void * opaque, int ret) * resulted in sense error but would need emulation. * In this case, emulate a valid VPD response. */ - if (s->needs_vpd_bl_emulation) { - int is_vpd_bl = r->req.cmd.buf[0] == INQUIRY && - r->req.cmd.buf[1] & 0x01 && - r->req.cmd.buf[2] == 0xb0; - - if (is_vpd_bl && sg_io_sense_from_errno(-ret, &r->io_header, &sense)) { - len = scsi_emulate_block_limits(r); + if (s->needs_vpd_bl_emulation && ret == 0 && + (r->io_header.driver_status & SG_ERR_DRIVER_SENSE) && + r->req.cmd.buf[0] == INQUIRY && + (r->req.cmd.buf[1] & 0x01) && + r->req.cmd.buf[2] == 0xb0) { + SCSISense sense = + scsi_parse_sense_buf(r->req.sense, r->io_header.sb_len_wr); + if (sense.key == ILLEGAL_REQUEST) { + len = scsi_generic_emulate_block_limits(r, s); /* * No need to let scsi_read_complete go on and handle an * INQUIRY VPD BL request we created manually. @@ -307,7 +325,7 @@ static void scsi_read_data(SCSIRequest *req) SCSIDevice *s = r->req.dev; int ret; - DPRINTF("scsi_read_data tag=0x%x\n", req->tag); + trace_scsi_generic_read_data(req->tag); /* The request is used as the AIO opaque value, so add a ref. */ scsi_req_ref(&r->req); @@ -328,7 +346,7 @@ static void scsi_write_complete(void * opaque, int ret) SCSIGenericReq *r = (SCSIGenericReq *)opaque; SCSIDevice *s = r->req.dev; - DPRINTF("scsi_write_complete() ret = %d\n", ret); + trace_scsi_generic_write_complete(ret); assert(r->req.aiocb != NULL); r->req.aiocb = NULL; @@ -343,7 +361,7 @@ static void scsi_write_complete(void * opaque, int ret) if (r->req.cmd.buf[0] == MODE_SELECT && r->req.cmd.buf[4] == 12 && s->type == TYPE_TAPE) { s->blocksize = (r->buf[9] << 16) | (r->buf[10] << 8) | r->buf[11]; - DPRINTF("block size %d\n", s->blocksize); + trace_scsi_generic_write_complete_blocksize(s->blocksize); } scsi_command_complete_noio(r, ret); @@ -360,7 +378,7 @@ static void scsi_write_data(SCSIRequest *req) SCSIDevice *s = r->req.dev; int ret; - DPRINTF("scsi_write_data tag=0x%x\n", req->tag); + trace_scsi_generic_write_data(req->tag); if (r->len == 0) { r->len = r->buflen; scsi_req_data(&r->req, r->len); @@ -383,6 +401,21 @@ static uint8_t *scsi_get_buf(SCSIRequest *req) return r->buf; } +static void scsi_generic_command_dump(uint8_t *cmd, int len) +{ + int i; + char *line_buffer, *p; + + line_buffer = g_malloc(len * 5 + 1); + + for (i = 0, p = line_buffer; i < len; i++) { + p += sprintf(p, " 0x%02x", cmd[i]); + } + trace_scsi_generic_send_command(line_buffer); + + g_free(line_buffer); +} + /* Execute a scsi command. Returns the length of the data expected by the command. This will be Positive for data transfers from the device (eg. disk reads), negative for transfers to the device (eg. disk writes), @@ -394,16 +427,9 @@ static int32_t scsi_send_command(SCSIRequest *req, uint8_t *cmd) SCSIDevice *s = r->req.dev; int ret; -#ifdef DEBUG_SCSI - DPRINTF("Command: data=0x%02x", cmd[0]); - { - int i; - for (i = 1; i < r->req.cmd.len; i++) { - printf(" 0x%02x", cmd[i]); - } - printf("\n"); + if (trace_event_get_state_backends(TRACE_SCSI_GENERIC_SEND_COMMAND)) { + scsi_generic_command_dump(cmd, r->req.cmd.len); } -#endif if (r->req.cmd.xfer == 0) { g_free(r->buf); @@ -527,7 +553,7 @@ static void scsi_generic_set_vpd_bl_emulation(SCSIDevice *s) } page_len = buf[3]; - for (i = 4; i < page_len + 4; i++) { + for (i = 4; i < MIN(sizeof(buf), page_len + 4); i++) { if (buf[i] == 0xb0) { s->needs_vpd_bl_emulation = false; return; @@ -665,7 +691,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) /* define device state */ s->type = scsiid.scsi_type; - DPRINTF("device type %d\n", s->type); + trace_scsi_generic_realize_type(s->type); switch (s->type) { case TYPE_TAPE: @@ -688,7 +714,7 @@ static void scsi_generic_realize(SCSIDevice *s, Error **errp) break; } - DPRINTF("block size %d\n", s->blocksize); + trace_scsi_generic_realize_blocksize(s->blocksize); /* Only used by scsi-block, but initialize it nevertheless to be clean. */ s->default_scsi_version = -1; diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index a9e49c7cb5..26dfc0340f 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -91,7 +91,7 @@ typedef struct vscsi_req { OBJECT_CHECK(VSCSIState, (obj), TYPE_VIO_SPAPR_VSCSI_DEVICE) typedef struct { - VIOsPAPRDevice vdev; + SpaprVioDevice vdev; SCSIBus bus; vscsi_req reqs[VSCSI_REQ_LIMIT]; } VSCSIState; @@ -1115,7 +1115,7 @@ static void vscsi_got_payload(VSCSIState *s, vscsi_crq *crq) } -static int vscsi_do_crq(struct VIOsPAPRDevice *dev, uint8_t *crq_data) +static int vscsi_do_crq(struct SpaprVioDevice *dev, uint8_t *crq_data) { VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); vscsi_crq crq; @@ -1187,7 +1187,7 @@ static const struct SCSIBusInfo vscsi_scsi_info = { .load_request = vscsi_load_request, }; -static void spapr_vscsi_reset(VIOsPAPRDevice *dev) +static void spapr_vscsi_reset(SpaprVioDevice *dev) { VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); int i; @@ -1198,7 +1198,7 @@ static void spapr_vscsi_reset(VIOsPAPRDevice *dev) } } -static void spapr_vscsi_realize(VIOsPAPRDevice *dev, Error **errp) +static void spapr_vscsi_realize(SpaprVioDevice *dev, Error **errp) { VSCSIState *s = VIO_SPAPR_VSCSI_DEVICE(dev); @@ -1208,7 +1208,7 @@ static void spapr_vscsi_realize(VIOsPAPRDevice *dev, Error **errp) &vscsi_scsi_info, NULL); } -void spapr_vscsi_create(VIOsPAPRBus *bus) +void spapr_vscsi_create(SpaprVioBus *bus) { DeviceState *dev; @@ -1218,7 +1218,7 @@ void spapr_vscsi_create(VIOsPAPRBus *bus) scsi_bus_legacy_handle_cmdline(&VIO_SPAPR_VSCSI_DEVICE(dev)->bus); } -static int spapr_vscsi_devnode(VIOsPAPRDevice *dev, void *fdt, int node_off) +static int spapr_vscsi_devnode(SpaprVioDevice *dev, void *fdt, int node_off) { int ret; @@ -1256,7 +1256,7 @@ static const VMStateDescription vmstate_spapr_vscsi = { static void spapr_vscsi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VIOsPAPRDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); + SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); k->realize = spapr_vscsi_realize; k->reset = spapr_vscsi_reset; diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index 6e299d0338..452b5994e6 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/scsi/scsi-bus.c +# scsi-bus.c scsi_req_alloc(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_cancel(int target, int lun, int tag) "target %d lun %d tag %d" scsi_req_data(int target, int lun, int tag, int len) "target %d lun %d tag %d len %d" @@ -18,7 +18,7 @@ scsi_inquiry(int target, int lun, int tag, int cdb1, int cdb2) "target %d lun %d scsi_test_unit_ready(int target, int lun, int tag) "target %d lun %d tag %d" scsi_request_sense(int target, int lun, int tag) "target %d lun %d tag %d" -# hw/scsi/mptsas.c +# mptsas.c mptsas_command_complete(void *dev, uint32_t ctx, uint32_t status, uint32_t resid) "dev %p context 0x%08x status 0x%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" @@ -36,11 +36,11 @@ mptsas_sgl_overflow(void *dev, uint32_t ctx, uint64_t req, uint64_t found) "dev mptsas_unhandled_cmd(void *dev, uint32_t ctx, uint8_t msg_cmd) "dev %p context 0x%08x: Unhandled cmd 0x%x" mptsas_unhandled_doorbell_cmd(void *dev, int cmd) "dev %p value 0x%08x" -# hw/scsi/mptconfig.c +# 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.c megasas_init_firmware(uint64_t pa) "pa 0x%" PRIx64 " " megasas_init_queue(uint64_t queue_pa, int queue_len, uint64_t head, uint64_t tail, uint32_t flags) "queue at 0x%" PRIx64 " len %d head 0x%" PRIx64 " tail 0x%" PRIx64 " flags 0x%x" megasas_initq_map_failed(int frame) "scmd %d: failed to map queue" @@ -118,7 +118,7 @@ megasas_mmio_invalid_readl(unsigned long addr) "addr 0x%lx" megasas_mmio_writel(const char *reg, uint32_t val) "reg %s: 0x%x" megasas_mmio_invalid_writel(uint32_t addr, uint32_t val) "addr 0x%x: 0x%x" -# hw/scsi/vmw_pvscsi.c +# 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 @@ -153,7 +153,7 @@ pvscsi_state(const char* state) "starting %s ..." pvscsi_tx_rings_ppn(const char* label, uint64_t ppn) "%s page: 0x%"PRIx64 pvscsi_tx_rings_num_pages(const char* label, uint32_t num) "Number of %s pages: %u" -# hw/scsi/esp.c +# esp.c esp_error_fifo_overrun(void) "FIFO overrun" esp_error_unhandled_command(uint32_t val) "unhandled command (0x%2.2x)" esp_error_invalid_write(uint32_t val, uint32_t addr) "invalid write of 0x%02x at [0x%x]" @@ -167,6 +167,7 @@ esp_handle_satn_stop(uint32_t cmdlen) "cmdlen %d" esp_write_response(uint32_t status) "Transfer status (status=%d)" esp_do_dma(uint32_t cmdlen, uint32_t len) "command len %d + %d" esp_command_complete(void) "SCSI Command complete" +esp_command_complete_deferred(void) "SCSI Command complete deferred" esp_command_complete_unexpected(void) "SCSI command completed unexpectedly" esp_command_complete_fail(void) "Command failed" esp_transfer_data(uint32_t dma_left, int32_t ti_size) "transfer %d/%d" @@ -189,7 +190,7 @@ esp_mem_writeb_cmd_selatns(uint32_t val) "Select with ATN & stop (0x%2.2x)" esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (0x%2.2x)" esp_mem_writeb_cmd_dissel(uint32_t val) "Disable selection (0x%2.2x)" -# hw/scsi/esp-pci.c +# esp-pci.c esp_pci_error_invalid_dma_direction(void) "invalid DMA transfer direction" esp_pci_error_invalid_read(uint32_t reg) "read access outside bounds (reg 0x%x)" esp_pci_error_invalid_write(uint32_t reg) "write access outside bounds (reg 0x%x)" @@ -203,7 +204,7 @@ esp_pci_dma_start(uint32_t val) "START (0x%.8x)" esp_pci_sbac_read(uint32_t reg) "sbac: 0x%8.8x" esp_pci_sbac_write(uint32_t reg, uint32_t val) "sbac: 0x%8.8x -> 0x%8.8x" -# hw/scsi/spapr_vscsi.c +# spapr_vscsi.c spapr_vscsi_send_rsp(uint8_t status, int32_t res_in, int32_t res_out) "status: 0x%x, res_in: %"PRId32", res_out: %"PRId32 spapr_vscsi_fetch_desc_no_data(void) "no data descriptor" spapr_vscsi_fetch_desc_direct(void) "direct segment" @@ -229,3 +230,105 @@ spapr_vscsi_process_login(void) "Got login, sending response !" spapr_vscsi_queue_cmd_no_drive(uint64_t lun) "Command for lun 0x%08" PRIx64 " with no drive" spapr_vscsi_queue_cmd(uint32_t qtag, unsigned cdb, const char *cmd, int lun, int ret) "Queued command tag 0x%"PRIx32" CMD 0x%x=%s LUN %d ret: %d" spapr_vscsi_do_crq(unsigned c0, unsigned c1) "crq: %02x %02x ..." + +# lsi53c895a.c +lsi_reset(void) "Reset" +lsi_update_irq(int level, uint8_t dstat, uint8_t sist1, uint8_t sist0) "Update IRQ level %d dstat 0x%02x sist 0x%02x0x%02x" +lsi_update_irq_disconnected(void) "Handled IRQs & disconnected, looking for pending processes" +lsi_script_scsi_interrupt(uint8_t stat1, uint8_t stat0, uint8_t sist1, uint8_t sist0) "SCSI Interrupt 0x%02x0x%02x prev 0x%02x0x%02x" +lsi_script_dma_interrupt(uint8_t stat, uint8_t dstat) "DMA Interrupt 0x%x prev 0x%x" +lsi_bad_phase_jump(uint32_t dsp) "Data phase mismatch jump to 0x%"PRIX32 +lsi_bad_phase_interrupt(void) "Phase mismatch interrupt" +lsi_bad_selection(uint32_t id) "Selected absent target %"PRIu32 +lsi_do_dma_unavailable(void) "DMA no data available" +lsi_do_dma(uint64_t addr, int len) "DMA addr=0x%"PRIx64" len=%d" +lsi_queue_command(uint32_t tag) "Queueing tag=0x%"PRId32 +lsi_add_msg_byte_error(void) "MSG IN data too long" +lsi_add_msg_byte(uint8_t data) "MSG IN 0x%02x" +lsi_reselect(int id) "Reselected target %d" +lsi_queue_req_error(void *p) "Multiple IO pending for request %p" +lsi_queue_req(uint32_t tag) "Queueing IO tag=0x%"PRIx32 +lsi_command_complete(uint32_t status) "Command complete status=%"PRId32 +lsi_transfer_data(uint32_t tag, uint32_t len) "Data ready tag=0x%"PRIx32" len=%"PRId32 +lsi_do_command(uint32_t dbc) "Send command len=%"PRId32 +lsi_do_status(uint32_t dbc, uint8_t status) "Get status len=%"PRId32" status=%d" +lsi_do_status_error(void) "Bad Status move" +lsi_do_msgin(uint32_t dbc, int len) "Message in len=%"PRId32" %d" +lsi_do_msgout(uint32_t dbc) "MSG out len=%"PRId32 +lsi_do_msgout_disconnect(void) "MSG: Disconnect" +lsi_do_msgout_noop(void) "MSG: No Operation" +lsi_do_msgout_extended(uint8_t msg, uint8_t len) "Extended message 0x%x (len %d)" +lsi_do_msgout_ignored(const char *msg) "%s (ignored)" +lsi_do_msgout_simplequeue(uint8_t select_tag) "SIMPLE queue tag=0x%x" +lsi_do_msgout_abort(uint32_t tag) "MSG: ABORT TAG tag=0x%"PRId32 +lsi_do_msgout_clearqueue(uint32_t tag) "MSG: CLEAR QUEUE tag=0x%"PRIx32 +lsi_do_msgout_busdevicereset(uint32_t tag) "MSG: BUS DEVICE RESET tag=0x%"PRIx32 +lsi_do_msgout_select(int id) "Select LUN %d" +lsi_memcpy(uint32_t dest, uint32_t src, int count) "memcpy dest 0x%"PRIx32" src 0x%"PRIx32" count %d" +lsi_wait_reselect(void) "Wait Reselect" +lsi_execute_script(uint32_t dsp, uint32_t insn, uint32_t addr) "SCRIPTS dsp=0x%"PRIx32" opcode 0x%"PRIx32" arg 0x%"PRIx32 +lsi_execute_script_blockmove_delayed(void) "Delayed select timeout" +lsi_execute_script_blockmove_badphase(const char *phase, const char *expected) "Wrong phase got %s expected %s" +lsi_execute_script_io_alreadyreselected(void) "Already reselected, jumping to alternative address" +lsi_execute_script_io_selected(uint8_t id, const char *atn) "Selected target %d%s" +lsi_execute_script_io_disconnect(void) "Wait Disconnect" +lsi_execute_script_io_set(const char *atn, const char *ack, const char *tm, const char *cc) "Set%s%s%s%s" +lsi_execute_script_io_clear(const char *atn, const char *ack, const char *tm, const char *cc) "Clear%s%s%s%s" +lsi_execute_script_io_opcode(const char *opcode, int reg, const char *opname, uint8_t data8, uint32_t sfbr, const char *ssfbr) "%s reg 0x%x %s data8=0x%02x sfbr=0x%02x%s" +lsi_execute_script_tc_nop(void) "NOP" +lsi_execute_script_tc_delayedselect_timeout(void) "Delayed select timeout" +lsi_execute_script_tc_compc(int result) "Compare carry %d" +lsi_execute_script_tc_compp(const char *phase, char op, const char *insn_phase) "Compare phase %s %c= %s" +lsi_execute_script_tc_compd(uint32_t sfbr, uint8_t mask, char op, int result) "Compare data 0x%"PRIx32" & 0x%x %c= 0x%x" +lsi_execute_script_tc_jump(uint32_t addr) "Jump to 0x%"PRIx32 +lsi_execute_script_tc_call(uint32_t addr) "Call 0x%"PRIx32 +lsi_execute_script_tc_return(uint32_t addr) "Return to 0x%"PRIx32 +lsi_execute_script_tc_interrupt(uint32_t addr) "Interrupt 0x%"PRIx32 +lsi_execute_script_tc_illegal(void) "Illegal transfer control" +lsi_execute_script_tc_cc_failed(void) "Control condition failed" +lsi_execute_script_mm_load(int reg, int n, uint32_t addr, int data) "Load reg 0x%x size %d addr 0x%"PRIx32" = 0x%08x" +lsi_execute_script_mm_store(int reg, int n, uint32_t addr) "Store reg 0x%x size %d addr 0x%"PRIx32 +lsi_execute_script_stop(void) "SCRIPTS execution stopped" +lsi_awoken(void) "Woken by SIGP" +lsi_reg_read(const char *name, int offset, uint8_t ret) "Read reg %s 0x%x = 0x%02x" +lsi_reg_write(const char *name, int offset, uint8_t val) "Write reg %s 0x%x = 0x%02x" + +# scsi-disk.c +scsi_disk_check_condition(uint32_t tag, uint8_t key, uint8_t asc, uint8_t ascq) "Command complete tag=0x%x sense=%d/%d/%d" +scsi_disk_read_complete(uint32_t tag, size_t size) "Data ready tag=0x%x len=%zd" +scsi_disk_read_data_count(uint32_t sector_count) "Read sector_count=%d" +scsi_disk_read_data_invalid(void) "Data transfer direction invalid" +scsi_disk_write_complete_noio(uint32_t tag, size_t size) "Write complete tag=0x%x more=%zd" +scsi_disk_write_data_invalid(void) "Data transfer direction invalid" +scsi_disk_emulate_vpd_page_00(size_t xfer) "Inquiry EVPD[Supported pages] buffer size %zd" +scsi_disk_emulate_vpd_page_80_not_supported(void) "Inquiry (EVPD[Serial number] not supported" +scsi_disk_emulate_vpd_page_80(size_t xfer) "Inquiry EVPD[Serial number] buffer size %zd" +scsi_disk_emulate_vpd_page_83(size_t xfer) "Inquiry EVPD[Device identification] buffer size %zd" +scsi_disk_emulate_vpd_page_b0_not_supported(void) "Inquiry (EVPD[Block limits] not supported for CDROM" +scsi_disk_emulate_mode_sense(int cmd, int page, size_t xfer, int control) "Mode Sense(%d) (page %d, xfer %zd, page_control %d)" +scsi_disk_emulate_read_toc(int start_track, int format, int msf) "Read TOC (track %d format %d msf %d)" +scsi_disk_emulate_read_data(int buflen) "Read buf_len=%d" +scsi_disk_emulate_write_data(int buflen) "Write buf_len=%d" +scsi_disk_emulate_command_SAI_16(void) "SAI READ CAPACITY(16)" +scsi_disk_emulate_command_SAI_unsupported(void) "Unsupported Service Action In" +scsi_disk_emulate_command_SEEK_10(uint64_t lba) "Seek(10) (sector %" PRId64 ")" +scsi_disk_emulate_command_MODE_SELECT(size_t xfer) "Mode Select(6) (len %zd)" +scsi_disk_emulate_command_MODE_SELECT_10(size_t xfer) "Mode Select(10) (len %zd)" +scsi_disk_emulate_command_UNMAP(size_t xfer) "Unmap (len %zd)" +scsi_disk_emulate_command_VERIFY(int bytchk) "Verify (bytchk %d)" +scsi_disk_emulate_command_WRITE_SAME(int cmd, size_t xfer) "WRITE SAME %d (len %zd)" +scsi_disk_emulate_command_UNKNOWN(int cmd, const char *name) "Unknown SCSI command (0x%2.2x=%s)" +scsi_disk_dma_command_READ(uint64_t lba, uint32_t len) "Read (sector %" PRId64 ", count %u)" +scsi_disk_dma_command_WRITE(const char *cmd, uint64_t lba, int len) "Write %s(sector %" PRId64 ", count %u)" +scsi_disk_new_request(uint32_t lun, uint32_t tag, const char *line) "Command: lun=%d tag=0x%x data=%s" + +# scsi-generic.c +scsi_generic_command_complete_noio(void *req, uint32_t tag, int statuc) "Command complete %p tag=0x%x status=%d" +scsi_generic_read_complete(uint32_t tag, int len) "Data ready tag=0x%x len=%d" +scsi_generic_read_data(uint32_t tag) "scsi_read_data tag=0x%x" +scsi_generic_write_complete(int ret) "scsi_write_complete() ret = %d" +scsi_generic_write_complete_blocksize(int blocksize) "block size %d" +scsi_generic_write_data(uint32_t tag) "scsi_write_data tag=0x%x" +scsi_generic_send_command(const char *line) "Command: data=%s" +scsi_generic_realize_type(int type) "device type %d" +scsi_generic_realize_blocksize(int blocksize) "block size %d" diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index e2a5828af1..b7fbab65dd 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -96,6 +96,9 @@ uint64_t vhost_scsi_common_get_features(VirtIODevice *vdev, uint64_t features, { VHostSCSICommon *vsc = VHOST_SCSI_COMMON(vdev); + /* Turn on predefined features supported by this device */ + features |= vsc->host_features; + return vhost_get_features(&vsc->dev, vsc->feature_bits, features); } diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 9c1bea8ff3..61e2e57da9 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -183,7 +183,7 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) } vsc->dev.nvqs = VHOST_SCSI_VQ_NUM_FIXED + vs->conf.num_queues; - vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); + vsc->dev.vqs = g_new0(struct vhost_virtqueue, vsc->dev.nvqs); vsc->dev.vq_index = 0; vsc->dev.backend_features = 0; @@ -215,6 +215,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) { VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(dev); + struct vhost_virtqueue *vqs = vsc->dev.vqs; migrate_del_blocker(vsc->migration_blocker); error_free(vsc->migration_blocker); @@ -223,7 +224,7 @@ static void vhost_scsi_unrealize(DeviceState *dev, Error **errp) vhost_scsi_set_status(vdev, 0); vhost_dev_cleanup(&vsc->dev); - g_free(vsc->dev.vqs); + g_free(vqs); virtio_scsi_common_unrealize(dev, errp); } @@ -238,6 +239,9 @@ static Property vhost_scsi_properties[] = { DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, 0xFFFF), DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), + DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_T10_PI, + false), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 9355cfdf07..8b1e6876db 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -69,7 +69,6 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); - VhostUserState *user; Error *err = NULL; int ret; @@ -86,30 +85,24 @@ static void vhost_user_scsi_realize(DeviceState *dev, Error **errp) return; } - user = vhost_user_init(); - if (!user) { - error_setg(errp, "vhost-user-scsi: failed to init vhost_user"); + if (!vhost_user_init(&s->vhost_user, &vs->conf.chardev, errp)) { return; } - user->chr = &vs->conf.chardev; vsc->dev.nvqs = 2 + vs->conf.num_queues; vsc->dev.vqs = g_new(struct vhost_virtqueue, vsc->dev.nvqs); vsc->dev.vq_index = 0; vsc->dev.backend_features = 0; - ret = vhost_dev_init(&vsc->dev, user, + ret = vhost_dev_init(&vsc->dev, &s->vhost_user, VHOST_BACKEND_TYPE_USER, 0); if (ret < 0) { error_setg(errp, "vhost-user-scsi: vhost initialization failed: %s", strerror(-ret)); - vhost_user_cleanup(user); - g_free(user); + vhost_user_cleanup(&s->vhost_user); return; } - s->vhost_user = user; - /* Channel and lun both are 0 for bootable vhost-user-scsi disk */ vsc->channel = 0; vsc->lun = 0; @@ -121,31 +114,16 @@ static void vhost_user_scsi_unrealize(DeviceState *dev, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VHostUserSCSI *s = VHOST_USER_SCSI(dev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); + struct vhost_virtqueue *vqs = vsc->dev.vqs; /* This will stop the vhost backend. */ vhost_user_scsi_set_status(vdev, 0); vhost_dev_cleanup(&vsc->dev); - g_free(vsc->dev.vqs); + g_free(vqs); virtio_scsi_common_unrealize(dev, errp); - - if (s->vhost_user) { - vhost_user_cleanup(s->vhost_user); - g_free(s->vhost_user); - s->vhost_user = NULL; - } -} - -static uint64_t vhost_user_scsi_get_features(VirtIODevice *vdev, - uint64_t features, Error **errp) -{ - VHostUserSCSI *s = VHOST_USER_SCSI(vdev); - - /* Turn on predefined features supported by this device */ - features |= s->host_features; - - return vhost_scsi_common_get_features(vdev, features, errp); + vhost_user_cleanup(&s->vhost_user); } static Property vhost_user_scsi_properties[] = { @@ -157,12 +135,15 @@ static Property vhost_user_scsi_properties[] = { DEFINE_PROP_UINT32("max_sectors", VirtIOSCSICommon, conf.max_sectors, 0xFFFF), DEFINE_PROP_UINT32("cmd_per_lun", VirtIOSCSICommon, conf.cmd_per_lun, 128), - DEFINE_PROP_BIT64("hotplug", VHostUserSCSI, host_features, - VIRTIO_SCSI_F_HOTPLUG, - true), - DEFINE_PROP_BIT64("param_change", VHostUserSCSI, host_features, - VIRTIO_SCSI_F_CHANGE, - true), + DEFINE_PROP_BIT64("hotplug", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_HOTPLUG, + true), + DEFINE_PROP_BIT64("param_change", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_CHANGE, + true), + DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_T10_PI, + false), DEFINE_PROP_END_OF_LIST(), }; @@ -187,7 +168,7 @@ static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); vdc->realize = vhost_user_scsi_realize; vdc->unrealize = vhost_user_scsi_unrealize; - vdc->get_features = vhost_user_scsi_get_features; + vdc->get_features = vhost_scsi_common_get_features; vdc->set_config = vhost_scsi_common_set_config; vdc->set_status = vhost_user_scsi_set_status; fwc->get_dev_path = vhost_scsi_common_get_fw_dev_path; diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 5a3057d1f8..839f120256 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -262,7 +262,13 @@ static int virtio_scsi_do_tmf(VirtIOSCSI *s, VirtIOSCSIReq *req) /* Here VIRTIO_SCSI_S_OK means "FUNCTION COMPLETE". */ req->resp.tmf.response = VIRTIO_SCSI_S_OK; - virtio_tswap32s(VIRTIO_DEVICE(s), &req->req.tmf.subtype); + /* + * req->req.tmf has the QEMU_PACKED attribute. Don't use virtio_tswap32s() + * to avoid compiler errors. + */ + req->req.tmf.subtype = + virtio_tswap32(VIRTIO_DEVICE(s), req->req.tmf.subtype); + switch (req->req.tmf.subtype) { case VIRTIO_SCSI_T_TMF_ABORT_TASK: case VIRTIO_SCSI_T_TMF_QUERY_TASK: @@ -791,22 +797,21 @@ static void virtio_scsi_hotplug(HotplugHandler *hotplug_dev, DeviceState *dev, SCSIDevice *sd = SCSI_DEVICE(dev); if (s->ctx && !s->dataplane_fenced) { + AioContext *ctx; if (blk_op_is_blocked(sd->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { return; } + ctx = blk_get_aio_context(sd->conf.blk); + if (ctx != s->ctx && ctx != qemu_get_aio_context()) { + error_setg(errp, "Cannot attach a blockdev that is using " + "a different iothread"); + return; + } virtio_scsi_acquire(s); blk_set_aio_context(sd->conf.blk, s->ctx); virtio_scsi_release(s); - } -} -/* Announce the new device after it has been plugged */ -static void virtio_scsi_post_hotplug(HotplugHandler *hotplug_dev, - DeviceState *dev) -{ - VirtIODevice *vdev = VIRTIO_DEVICE(hotplug_dev); - VirtIOSCSI *s = VIRTIO_SCSI(vdev); - SCSIDevice *sd = SCSI_DEVICE(dev); + } if (virtio_vdev_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) { virtio_scsi_acquire(s); @@ -832,6 +837,12 @@ static void virtio_scsi_hotunplug(HotplugHandler *hotplug_dev, DeviceState *dev, virtio_scsi_release(s); } + if (s->ctx) { + virtio_scsi_acquire(s); + blk_set_aio_context(sd->conf.blk, qemu_get_aio_context()); + virtio_scsi_release(s); + } + qdev_simple_device_unplug_cb(hotplug_dev, dev, errp); } @@ -901,7 +912,7 @@ static void virtio_scsi_device_realize(DeviceState *dev, Error **errp) scsi_bus_new(&s->bus, sizeof(s->bus), dev, &virtio_scsi_scsi_info, vdev->bus_name); /* override default SCSI bus hotplug-handler, with virtio-scsi's one */ - qbus_set_hotplug_handler(BUS(&s->bus), dev, &error_abort); + qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(dev), &error_abort); virtio_scsi_dataplane_setup(s, errp); } @@ -976,7 +987,6 @@ static void virtio_scsi_class_init(ObjectClass *klass, void *data) vdc->start_ioeventfd = virtio_scsi_dataplane_start; vdc->stop_ioeventfd = virtio_scsi_dataplane_stop; hc->plug = virtio_scsi_hotplug; - hc->post_plug = virtio_scsi_post_hotplug; hc->unplug = virtio_scsi_hotunplug; } diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index a3a019e30a..584b4be07e 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1142,7 +1142,7 @@ pvscsi_realizefn(PCIDevice *pci_dev, Error **errp) scsi_bus_new(&s->bus, sizeof(s->bus), DEVICE(pci_dev), &pvscsi_scsi_info, NULL); /* override default SCSI bus hotplug-handler, with pvscsi's one */ - qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(s), &error_abort); + qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(s), &error_abort); pvscsi_reset_state(s); } diff --git a/hw/sd/Kconfig b/hw/sd/Kconfig new file mode 100644 index 0000000000..c5e1e5581c --- /dev/null +++ b/hw/sd/Kconfig @@ -0,0 +1,21 @@ +config PL181 + bool + select SD + +config SSI_SD + bool + depends on SSI + select SD + +config SD + bool + +config SDHCI + bool + select SD + +config SDHCI_PCI + bool + default y if PCI_DEVICES + depends on PCI + select SDHCI diff --git a/hw/sd/Makefile.objs b/hw/sd/Makefile.objs index a99d9fbb04..06657279d1 100644 --- a/hw/sd/Makefile.objs +++ b/hw/sd/Makefile.objs @@ -2,6 +2,7 @@ common-obj-$(CONFIG_PL181) += pl181.o common-obj-$(CONFIG_SSI_SD) += ssi-sd.o common-obj-$(CONFIG_SD) += sd.o core.o sdmmc-internal.o common-obj-$(CONFIG_SDHCI) += sdhci.o +common-obj-$(CONFIG_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MILKYMIST) += milkymist-memcard.o obj-$(CONFIG_OMAP) += omap_mmc.o diff --git a/hw/sd/sd.c b/hw/sd/sd.c index d4356e9b73..aaab15f386 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2121,6 +2121,7 @@ static void sd_class_init(ObjectClass *klass, void *data) dc->vmsd = &sd_vmstate; dc->reset = sd_reset; dc->bus_type = TYPE_SD_BUS; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); sc->set_voltage = sd_set_voltage; sc->get_dat_lines = sd_get_dat_lines; diff --git a/hw/sd/sdhci-internal.h b/hw/sd/sdhci-internal.h index 756ef3f3c2..34141400f8 100644 --- a/hw/sd/sdhci-internal.h +++ b/hw/sd/sdhci-internal.h @@ -302,4 +302,40 @@ extern const VMStateDescription sdhci_vmstate; #define ESDHC_CTRL_4BITBUS (0x1 << 1) #define ESDHC_CTRL_8BITBUS (0x2 << 1) +#define ESDHC_PRNSTS_SDSTB (1 << 3) + +/* + * Default SD/MMC host controller features information, which will be + * presented in CAPABILITIES register of generic SD host controller at reset. + * + * support: + * - 3.3v and 1.8v voltages + * - SDMA/ADMA1/ADMA2 + * - high-speed + * max host controller R/W buffers size: 512B + * max clock frequency for SDclock: 52 MHz + * timeout clock frequency: 52 MHz + * + * does not support: + * - 3.0v voltage + * - 64-bit system bus + * - suspend/resume + */ +#define SDHC_CAPAB_REG_DEFAULT 0x057834b4 + +#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \ + DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \ + DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \ + \ + /* Capabilities registers provide information on supported + * features of this specific host controller implementation */ \ + DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \ + DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0) + +void sdhci_initfn(SDHCIState *s); +void sdhci_uninitfn(SDHCIState *s); +void sdhci_common_realize(SDHCIState *s, Error **errp); +void sdhci_common_unrealize(SDHCIState *s, Error **errp); +void sdhci_common_class_init(ObjectClass *klass, void *data); + #endif diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c new file mode 100644 index 0000000000..f884661862 --- /dev/null +++ b/hw/sd/sdhci-pci.c @@ -0,0 +1,87 @@ +/* + * SDHCI device on PCI + * + * 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/hw.h" +#include "hw/sd/sdhci.h" +#include "sdhci-internal.h" + +static Property sdhci_pci_properties[] = { + DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), + DEFINE_PROP_END_OF_LIST(), +}; + +static void sdhci_pci_realize(PCIDevice *dev, Error **errp) +{ + SDHCIState *s = PCI_SDHCI(dev); + Error *local_err = NULL; + + sdhci_initfn(s); + sdhci_common_realize(s, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + + dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */ + dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ + s->irq = pci_allocate_irq(dev); + s->dma_as = pci_get_address_space(dev); + pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem); +} + +static void sdhci_pci_exit(PCIDevice *dev) +{ + SDHCIState *s = PCI_SDHCI(dev); + + sdhci_common_unrealize(s, &error_abort); + sdhci_uninitfn(s); +} + +static void sdhci_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + + k->realize = sdhci_pci_realize; + k->exit = sdhci_pci_exit; + k->vendor_id = PCI_VENDOR_ID_REDHAT; + k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI; + k->class_id = PCI_CLASS_SYSTEM_SDHCI; + dc->props = sdhci_pci_properties; + + sdhci_common_class_init(klass, data); +} + +static const TypeInfo sdhci_pci_info = { + .name = TYPE_PCI_SDHCI, + .parent = TYPE_PCI_DEVICE, + .instance_size = sizeof(SDHCIState), + .class_init = sdhci_pci_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, +}; + +static void sdhci_pci_register_type(void) +{ + type_register_static(&sdhci_pci_info); +} + +type_init(sdhci_pci_register_type) diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 8f58c31265..17ad5465a7 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -40,24 +40,6 @@ #define MASKED_WRITE(reg, mask, val) (reg = (reg & (mask)) | (val)) -/* Default SD/MMC host controller features information, which will be - * presented in CAPABILITIES register of generic SD host controller at reset. - * - * support: - * - 3.3v and 1.8v voltages - * - SDMA/ADMA1/ADMA2 - * - high-speed - * max host controller R/W buffers size: 512B - * max clock frequency for SDclock: 52 MHz - * timeout clock frequency: 52 MHz - * - * does not support: - * - 3.0v voltage - * - 64-bit system bus - * - suspend/resume - */ -#define SDHC_CAPAB_REG_DEFAULT 0x057834b4 - static inline unsigned int sdhci_get_fifolen(SDHCIState *s) { return 1 << (9 + FIELD_EX32(s->capareg, SDHC_CAPAB, MAXBLOCKLENGTH)); @@ -1328,16 +1310,7 @@ static void sdhci_init_readonly_registers(SDHCIState *s, Error **errp) /* --- qdev common --- */ -#define DEFINE_SDHCI_COMMON_PROPERTIES(_state) \ - DEFINE_PROP_UINT8("sd-spec-version", _state, sd_spec_version, 2), \ - DEFINE_PROP_UINT8("uhs", _state, uhs_mode, UHS_NOT_SUPPORTED), \ - \ - /* Capabilities registers provide information on supported - * features of this specific host controller implementation */ \ - DEFINE_PROP_UINT64("capareg", _state, capareg, SDHC_CAPAB_REG_DEFAULT), \ - DEFINE_PROP_UINT64("maxcurr", _state, maxcurr, 0) - -static void sdhci_initfn(SDHCIState *s) +void sdhci_initfn(SDHCIState *s) { qbus_create_inplace(&s->sdbus, sizeof(s->sdbus), TYPE_SDHCI_BUS, DEVICE(s), "sd-bus"); @@ -1348,7 +1321,7 @@ static void sdhci_initfn(SDHCIState *s) s->io_ops = &sdhci_mmio_ops; } -static void sdhci_uninitfn(SDHCIState *s) +void sdhci_uninitfn(SDHCIState *s) { timer_del(s->insert_timer); timer_free(s->insert_timer); @@ -1359,7 +1332,7 @@ static void sdhci_uninitfn(SDHCIState *s) s->fifo_buffer = NULL; } -static void sdhci_common_realize(SDHCIState *s, Error **errp) +void sdhci_common_realize(SDHCIState *s, Error **errp) { Error *local_err = NULL; @@ -1371,11 +1344,11 @@ static void sdhci_common_realize(SDHCIState *s, Error **errp) s->buf_maxsz = sdhci_get_fifolen(s); s->fifo_buffer = g_malloc0(s->buf_maxsz); - memory_region_init_io(&s->iomem, OBJECT(s), &sdhci_mmio_ops, s, "sdhci", + memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci", SDHC_REGISTERS_MAP_SIZE); } -static void sdhci_common_unrealize(SDHCIState *s, Error **errp) +void sdhci_common_unrealize(SDHCIState *s, Error **errp) { /* This function is expected to be called only once for each class: * - SysBus: via DeviceClass->unrealize(), @@ -1445,7 +1418,7 @@ const VMStateDescription sdhci_vmstate = { }, }; -static void sdhci_common_class_init(ObjectClass *klass, void *data) +void sdhci_common_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1454,66 +1427,6 @@ static void sdhci_common_class_init(ObjectClass *klass, void *data) dc->reset = sdhci_poweron_reset; } -/* --- qdev PCI --- */ - -static Property sdhci_pci_properties[] = { - DEFINE_SDHCI_COMMON_PROPERTIES(SDHCIState), - DEFINE_PROP_END_OF_LIST(), -}; - -static void sdhci_pci_realize(PCIDevice *dev, Error **errp) -{ - SDHCIState *s = PCI_SDHCI(dev); - Error *local_err = NULL; - - sdhci_initfn(s); - sdhci_common_realize(s, &local_err); - if (local_err) { - error_propagate(errp, local_err); - return; - } - - dev->config[PCI_CLASS_PROG] = 0x01; /* Standard Host supported DMA */ - dev->config[PCI_INTERRUPT_PIN] = 0x01; /* interrupt pin A */ - s->irq = pci_allocate_irq(dev); - s->dma_as = pci_get_address_space(dev); - pci_register_bar(dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->iomem); -} - -static void sdhci_pci_exit(PCIDevice *dev) -{ - SDHCIState *s = PCI_SDHCI(dev); - - sdhci_common_unrealize(s, &error_abort); - sdhci_uninitfn(s); -} - -static void sdhci_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - - k->realize = sdhci_pci_realize; - k->exit = sdhci_pci_exit; - k->vendor_id = PCI_VENDOR_ID_REDHAT; - k->device_id = PCI_DEVICE_ID_REDHAT_SDHCI; - k->class_id = PCI_CLASS_SYSTEM_SDHCI; - dc->props = sdhci_pci_properties; - - sdhci_common_class_init(klass, data); -} - -static const TypeInfo sdhci_pci_info = { - .name = TYPE_PCI_SDHCI, - .parent = TYPE_PCI_DEVICE, - .instance_size = sizeof(SDHCIState), - .class_init = sdhci_pci_class_init, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }, -}; - /* --- qdev SysBus --- */ static Property sdhci_sysbus_properties[] = { @@ -1565,9 +1478,6 @@ static void sdhci_sysbus_realize(DeviceState *dev, Error ** errp) sysbus_init_irq(sbd, &s->irq); - memory_region_init_io(&s->iomem, OBJECT(s), s->io_ops, s, "sdhci", - SDHC_REGISTERS_MAP_SIZE); - sysbus_init_mmio(sbd, &s->iomem); } @@ -1651,6 +1561,14 @@ static uint64_t usdhc_read(void *opaque, hwaddr offset, unsigned size) break; + case SDHC_PRNSTS: + /* Add SDSTB (SD Clock Stable) bit to PRNSTS */ + ret = sdhci_read(opaque, offset, size) & ~ESDHC_PRNSTS_SDSTB; + if (s->clkcon & SDHC_CLOCK_INT_STABLE) { + ret |= ESDHC_PRNSTS_SDSTB; + } + break; + case ESDHC_DLL_CTRL: case ESDHC_TUNE_CTRL_STATUS: case ESDHC_UNDOCUMENTED_REG27: @@ -1841,7 +1759,6 @@ static const TypeInfo imx_usdhc_info = { 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_register_static(&imx_usdhc_info); diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 95a143bfba..623d0333e8 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -284,6 +284,8 @@ static void ssi_sd_class_init(ObjectClass *klass, void *data) k->cs_polarity = SSI_CS_LOW; dc->vmsd = &vmstate_ssi_sd; dc->reset = ssi_sd_reset; + /* Reason: init() method uses drive_get_next() */ + dc->user_creatable = false; } static const TypeInfo ssi_sd_info = { diff --git a/hw/sd/trace-events b/hw/sd/trace-events index fb0615cd3c..52971dc033 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -1,12 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/sd/bcm2835_sdhost.c +# bcm2835_sdhost.c bcm2835_sdhost_read(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" bcm2835_sdhost_write(uint64_t offset, uint64_t data, unsigned size) "offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" bcm2835_sdhost_edm_change(const char *why, uint32_t edm) "(%s) EDM now 0x%x" bcm2835_sdhost_update_irq(uint32_t irq) "IRQ bits 0x%x\n" -# hw/sd/core.c +# core.c sdbus_command(const char *bus_name, uint8_t cmd, uint32_t arg) "@%s CMD%02d arg 0x%08x" sdbus_read(const char *bus_name, uint8_t value) "@%s value 0x%02x" sdbus_write(const char *bus_name, uint8_t value) "@%s value 0x%02x" @@ -14,7 +14,7 @@ sdbus_set_voltage(const char *bus_name, uint16_t millivolts) "@%s %u (mV)" sdbus_get_dat_lines(const char *bus_name, uint8_t dat_lines) "@%s dat_lines: %u" sdbus_get_cmd_line(const char *bus_name, bool cmd_line) "@%s cmd_line: %u" -# hw/sd/sdhci.c +# sdhci.c sdhci_set_inserted(const char *level) "card state changed: %s" sdhci_send_command(uint8_t cmd, uint32_t arg) "CMD%02u ARG[0x%08x]" sdhci_error(const char *msg) "%s" @@ -29,13 +29,12 @@ sdhci_read_dataport(uint16_t data_count) "all %u bytes of data have been read fr sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of data" sdhci_capareg(const char *desc, uint16_t val) "%s: %u" -# hw/sd/sd.c +# sd.c sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)" sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)" sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" -sdcard_set_enable(bool current_state, bool new_state) "%u -> %u" sdcard_reset(void) "" sdcard_set_blocklen(uint16_t length) "0x%03x" sdcard_inserted(bool readonly) "read_only: %u" @@ -49,10 +48,10 @@ sdcard_write_data(const char *proto, const char *cmd_desc, uint8_t cmd, uint8_t sdcard_read_data(const char *proto, const char *cmd_desc, uint8_t cmd, int length) "%s %20s/ CMD%02d len %d" sdcard_set_voltage(uint16_t millivolts) "%u mV" -# hw/sd/milkymist-memcard.c +# milkymist-memcard.c milkymist_memcard_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_memcard_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" -# hw/sd/pxa2xx_mmci.c +# pxa2xx_mmci.c pxa2xx_mmci_read(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x" pxa2xx_mmci_write(uint8_t size, uint32_t addr, uint32_t value) "size %d addr 0x%02x value 0x%08x" diff --git a/hw/sh4/Kconfig b/hw/sh4/Kconfig new file mode 100644 index 0000000000..593662d28a --- /dev/null +++ b/hw/sh4/Kconfig @@ -0,0 +1,24 @@ +config R2D + bool + imply PCI_DEVICES + imply TEST_DEVICES + imply RTL8139_PCI + select I82378 if TEST_DEVICES + select IDE_MMIO + select PFLASH_CFI02 + select USB_OHCI + select PCI + select SM501 + select SH4 + +config SHIX + bool + select SH7750 + select SH4 + +config SH7750 + bool + +config SH4 + bool + select PTIMER diff --git a/hw/sh4/Makefile.objs b/hw/sh4/Makefile.objs index 2393702c57..2a707f9473 100644 --- a/hw/sh4/Makefile.objs +++ b/hw/sh4/Makefile.objs @@ -1,4 +1,4 @@ -obj-y += shix.o r2d.o - obj-y += sh7750.o sh7750_regnames.o obj-y += sh_pci.o +obj-$(CONFIG_R2D) += r2d.o +obj-$(CONFIG_SHIX) += shix.o diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 6a5fc46a47..0bcb769c85 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -31,7 +31,6 @@ #include "hw/sysbus.h" #include "hw/hw.h" #include "hw/sh4/sh.h" -#include "hw/devices.h" #include "sysemu/sysemu.h" #include "hw/boards.h" #include "hw/pci/pci.h" @@ -44,7 +43,7 @@ #include "exec/address-spaces.h" #define FLASH_BASE 0x00000000 -#define FLASH_SIZE 0x02000000 +#define FLASH_SIZE (16 * MiB) #define SDRAM_BASE 0x0c000000 /* Physical location of SDRAM: Area 3 */ #define SDRAM_SIZE 0x04000000 @@ -139,11 +138,11 @@ static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size) case PA_IRLMSK: return s->irlmsk; case PA_OUTPORT: - return s->outport; + return s->outport; case PA_POWOFF: - return 0x00; + return 0x00; case PA_VERREG: - return 0x10; + return 0x10; } return 0; @@ -158,18 +157,18 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) case PA_IRLMSK: s->irlmsk = value; update_irl(s); - break; + break; case PA_OUTPORT: - s->outport = value; - break; + s->outport = value; + break; case PA_POWOFF: if (value & 1) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } break; case PA_VERREG: - /* Discard writes */ - break; + /* Discard writes */ + break; } } @@ -220,7 +219,7 @@ static struct QEMU_PACKED char pad[232]; - char kernel_cmdline[256]; + char kernel_cmdline[256] QEMU_NONSTRING; } boot_params; static void r2d_init(MachineState *machine) @@ -288,12 +287,19 @@ static void r2d_init(MachineState *machine) sysbus_mmio_map(busdev, 1, 0x1400080c); mmio_ide_init_drives(dev, dinfo, NULL); - /* onboard flash memory */ + /* + * Onboard flash memory + * According to the old board user document in Japanese (under + * NDA) what is referred to as FROM (Area0) is connected via a + * 32-bit bus and CS0 to CN8. The docs mention a Cypress + * S29PL127J60TFI130 chipsset. Per the 'S29PL-J 002-00615 + * Rev. *E' datasheet, it is a 128Mbit NOR parallel flash + * addressable in words of 16bit. + */ dinfo = drive_get(IF_PFLASH, 0, 0); - pflash_cfi02_register(0x0, NULL, "r2d.flash", FLASH_SIZE, + pflash_cfi02_register(0x0, "r2d.flash", FLASH_SIZE, dinfo ? blk_by_legacy_dinfo(dinfo) : NULL, - 16 * KiB, FLASH_SIZE >> 16, - 1, 4, 0x0000, 0x0000, 0x0000, 0x0000, + 64 * KiB, 1, 2, 0x0001, 0x227e, 0x2220, 0x2200, 0x555, 0x2aa, 0); /* NIC: rtl8139 on-board, and 2 slots. */ diff --git a/hw/sh4/sh_pci.c b/hw/sh4/sh_pci.c index 4ec2e35500..379d0685ed 100644 --- a/hw/sh4/sh_pci.c +++ b/hw/sh4/sh_pci.c @@ -120,16 +120,15 @@ static void sh_pci_set_irq(void *opaque, int irq_num, int level) qemu_set_irq(pic[irq_num], level); } -static int sh_pci_device_init(SysBusDevice *dev) +static void sh_pci_device_realize(DeviceState *dev, Error **errp) { - PCIHostState *phb; - SHPCIState *s; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + SHPCIState *s = SH_PCI_HOST_BRIDGE(dev); + PCIHostState *phb = PCI_HOST_BRIDGE(s); int i; - s = SH_PCI_HOST_BRIDGE(dev); - phb = PCI_HOST_BRIDGE(s); for (i = 0; i < 4; i++) { - sysbus_init_irq(dev, &s->irq[i]); + sysbus_init_irq(sbd, &s->irq[i]); } phb->bus = pci_register_root_bus(DEVICE(dev), "pci", sh_pci_set_irq, sh_pci_map_irq, @@ -143,13 +142,12 @@ static int sh_pci_device_init(SysBusDevice *dev) &s->memconfig_p4, 0, 0x224); memory_region_init_alias(&s->isa, OBJECT(s), "sh_pci.isa", get_system_io(), 0, 0x40000); - sysbus_init_mmio(dev, &s->memconfig_p4); - sysbus_init_mmio(dev, &s->memconfig_a7); + sysbus_init_mmio(sbd, &s->memconfig_p4); + sysbus_init_mmio(sbd, &s->memconfig_a7); s->iobr = 0xfe240000; memory_region_add_subregion(get_system_memory(), s->iobr, &s->isa); s->dev = pci_create_simple(phb->bus, PCI_DEVFN(0, 0), "sh_pci_host"); - return 0; } static void sh_pci_host_realize(PCIDevice *d, Error **errp) @@ -187,9 +185,9 @@ static const TypeInfo sh_pci_host_info = { static void sh_pci_device_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = sh_pci_device_init; + dc->realize = sh_pci_device_realize; } static const TypeInfo sh_pci_device_info = { diff --git a/hw/smbios/Kconfig b/hw/smbios/Kconfig new file mode 100644 index 0000000000..553adf4bfc --- /dev/null +++ b/hw/smbios/Kconfig @@ -0,0 +1,2 @@ +config SMBIOS + bool diff --git a/hw/smbios/smbios-stub.c b/hw/smbios/smbios-stub.c index d3a385441a..64e5ba93ec 100644 --- a/hw/smbios/smbios-stub.c +++ b/hw/smbios/smbios-stub.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qmp/qerror.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" void smbios_entry_add(QemuOpts *opts, Error **errp) { diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index a27e54b2fa..47be9071fa 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -24,11 +24,10 @@ #include "sysemu/sysemu.h" #include "qemu/uuid.h" #include "sysemu/cpus.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "hw/loader.h" #include "exec/cpu-common.h" #include "smbios_build.h" -#include "hw/smbios/ipmi.h" /* legacy structures and constants for <= 2.0 machines */ struct smbios_header { @@ -564,6 +563,7 @@ static void smbios_build_type_3_table(void) t->height = 0; t->number_of_power_cords = 0; t->contained_element_count = 0; + t->contained_element_record_length = 0; SMBIOS_TABLE_SET_STR(3, sku_number_str, type3.sku); SMBIOS_BUILD_TABLE_POST; @@ -950,6 +950,7 @@ static void save_opt_list(size_t *ndest, const char ***dest, void smbios_entry_add(QemuOpts *opts, Error **errp) { + Error *err = NULL; const char *val; assert(!smbios_immutable); @@ -960,12 +961,16 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) int size; struct smbios_table *table; /* legacy mode only */ - qemu_opts_validate(opts, qemu_smbios_file_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_file_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } size = get_image_size(val); if (size == -1 || size < sizeof(struct smbios_structure_header)) { - error_report("Cannot read SMBIOS file %s", val); - exit(1); + error_setg(errp, "Cannot read SMBIOS file %s", val); + return; } /* @@ -977,15 +982,16 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) 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 (load_image_size(val, (uint8_t *)header, size) != size) { + error_setg(errp, "Failed to load SMBIOS file %s", val); + return; } if (test_bit(header->type, have_fields_bitmap)) { - error_report("can't load type %d struct, fields already specified!", - header->type); - exit(1); + error_setg(errp, + "can't load type %d struct, fields already specified!", + header->type); + return; } set_bit(header->type, have_binfile_bitmap); @@ -1030,19 +1036,23 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) unsigned long type = strtoul(val, NULL, 0); if (type > SMBIOS_MAX_TYPE) { - error_report("out of range!"); - exit(1); + error_setg(errp, "out of range!"); + return; } if (test_bit(type, have_binfile_bitmap)) { - error_report("can't add fields, binary file already loaded!"); - exit(1); + error_setg(errp, "can't add fields, binary file already loaded!"); + return; } set_bit(type, have_fields_bitmap); switch (type) { case 0: - qemu_opts_validate(opts, qemu_smbios_type0_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type0_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt(&type0.vendor, opts, "vendor"); save_opt(&type0.version, opts, "version"); save_opt(&type0.date, opts, "date"); @@ -1051,14 +1061,18 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) val = qemu_opt_get(opts, "release"); if (val) { if (sscanf(val, "%hhu.%hhu", &type0.major, &type0.minor) != 2) { - error_report("Invalid release"); - exit(1); + error_setg(errp, "Invalid release"); + return; } type0.have_major_minor = true; } return; case 1: - qemu_opts_validate(opts, qemu_smbios_type1_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type1_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt(&type1.manufacturer, opts, "manufacturer"); save_opt(&type1.product, opts, "product"); save_opt(&type1.version, opts, "version"); @@ -1069,14 +1083,18 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) val = qemu_opt_get(opts, "uuid"); if (val) { if (qemu_uuid_parse(val, &qemu_uuid) != 0) { - error_report("Invalid UUID"); - exit(1); + error_setg(errp, "Invalid UUID"); + return; } qemu_uuid_set = true; } return; case 2: - qemu_opts_validate(opts, qemu_smbios_type2_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type2_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt(&type2.manufacturer, opts, "manufacturer"); save_opt(&type2.product, opts, "product"); save_opt(&type2.version, opts, "version"); @@ -1085,7 +1103,11 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) save_opt(&type2.location, opts, "location"); return; case 3: - qemu_opts_validate(opts, qemu_smbios_type3_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type3_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt(&type3.manufacturer, opts, "manufacturer"); save_opt(&type3.version, opts, "version"); save_opt(&type3.serial, opts, "serial"); @@ -1093,7 +1115,11 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) save_opt(&type3.sku, opts, "sku"); return; case 4: - qemu_opts_validate(opts, qemu_smbios_type4_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type4_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt(&type4.sock_pfx, opts, "sock_pfx"); save_opt(&type4.manufacturer, opts, "manufacturer"); save_opt(&type4.version, opts, "version"); @@ -1102,11 +1128,19 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) save_opt(&type4.part, opts, "part"); return; case 11: - qemu_opts_validate(opts, qemu_smbios_type11_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type11_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt_list(&type11.nvalues, &type11.values, opts, "value"); return; case 17: - qemu_opts_validate(opts, qemu_smbios_type17_opts, &error_fatal); + qemu_opts_validate(opts, qemu_smbios_type17_opts, &err); + if (err) { + error_propagate(errp, err); + return; + } save_opt(&type17.loc_pfx, opts, "loc_pfx"); save_opt(&type17.bank, opts, "bank"); save_opt(&type17.manufacturer, opts, "manufacturer"); @@ -1116,12 +1150,12 @@ void smbios_entry_add(QemuOpts *opts, Error **errp) 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_setg(errp, + "Don't know how to build fields for SMBIOS type %ld", + type); + return; } } - error_report("Must specify type= or file="); - exit(1); + error_setg(errp, "Must specify type= or file="); } diff --git a/hw/smbios/smbios_build.h b/hw/smbios/smbios_build.h index 93b360d520..56b5a1e3f3 100644 --- a/hw/smbios/smbios_build.h +++ b/hw/smbios/smbios_build.h @@ -3,6 +3,7 @@ * * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. * Copyright (C) 2013 Red Hat, Inc. + * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC * * Authors: * Alex Williamson @@ -96,4 +97,7 @@ extern unsigned smbios_table_cnt; smbios_table_cnt++; \ } while (0) +/* IPMI SMBIOS firmware handling */ +void smbios_build_type_38_table(void); + #endif /* QEMU_SMBIOS_BUILD_H */ diff --git a/hw/smbios/smbios_type_38-stub.c b/hw/smbios/smbios_type_38-stub.c index 5b83c9b1f1..14b53d004b 100644 --- a/hw/smbios/smbios_type_38-stub.c +++ b/hw/smbios/smbios_type_38-stub.c @@ -8,7 +8,7 @@ */ #include "qemu/osdep.h" -#include "hw/smbios/ipmi.h" +#include "smbios_build.h" void smbios_build_type_38_table(void) { diff --git a/hw/smbios/smbios_type_38.c b/hw/smbios/smbios_type_38.c index 56e8609c00..0c08f282de 100644 --- a/hw/smbios/smbios_type_38.c +++ b/hw/smbios/smbios_type_38.c @@ -9,8 +9,7 @@ #include "qemu/osdep.h" #include "hw/ipmi/ipmi.h" -#include "hw/smbios/ipmi.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "qemu/error-report.h" #include "smbios_build.h" diff --git a/hw/sparc/Kconfig b/hw/sparc/Kconfig new file mode 100644 index 0000000000..2a83a8010e --- /dev/null +++ b/hw/sparc/Kconfig @@ -0,0 +1,26 @@ +config SUN4M + bool + imply TCX + imply CG3 + select CS4231 + select ECCMEMCTL + select EMPTY_SLOT + select ESCC + select ESP + select FDC + select SLAVIO + select LANCE + select M48T59 + select STP2000 + +config LEON3 + bool + select GRLIB + +config GRLIB + bool + select PTIMER + +config SLAVIO + bool + select PTIMER diff --git a/hw/sparc/Makefile.objs b/hw/sparc/Makefile.objs index e2d0828c39..d57e33f83e 100644 --- a/hw/sparc/Makefile.objs +++ b/hw/sparc/Makefile.objs @@ -1 +1,3 @@ -obj-y += sun4m_iommu.o sun4m.o leon3.o +obj-$(CONFIG_SUN4M) += sun4m_iommu.o +obj-$(CONFIG_SUN4M) += sun4m.o +obj-$(CONFIG_LEON3) += leon3.o diff --git a/hw/sparc/leon3.c b/hw/sparc/leon3.c index fa98ab8177..774639af33 100644 --- a/hw/sparc/leon3.c +++ b/hw/sparc/leon3.c @@ -190,7 +190,8 @@ static void leon3_generic_hw_init(MachineState *machine) long kernel_size; uint64_t entry; - kernel_size = load_elf(kernel_filename, NULL, NULL, &entry, NULL, NULL, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, + &entry, NULL, NULL, 1 /* big endian */, EM_SPARC, 0, 0); if (kernel_size < 0) { error_report("could not load kernel '%s'", kernel_filename); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index d981de1841..ca1e3825d5 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -37,7 +37,6 @@ #include "net/net.h" #include "hw/boards.h" #include "hw/scsi/esp.h" -#include "hw/isa/isa.h" #include "hw/nvram/sun_nvram.h" #include "hw/nvram/chrp_nvram.h" #include "hw/nvram/fw_cfg.h" @@ -225,11 +224,12 @@ static uint64_t translate_kernel_address(void *opaque, uint64_t addr) static unsigned long sun4m_load_kernel(const char *kernel_filename, const char *initrd_filename, - ram_addr_t RAM_size) + ram_addr_t RAM_size, + uint32_t *initrd_size) { int linux_boot; unsigned int i; - long initrd_size, kernel_size; + long kernel_size; uint8_t *ptr; linux_boot = (kernel_filename != NULL); @@ -243,7 +243,8 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, #else bswap_needed = 0; #endif - kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL, + kernel_size = load_elf(kernel_filename, NULL, + translate_kernel_address, NULL, NULL, NULL, NULL, 1, EM_SPARC, 0, 0); if (kernel_size < 0) kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR, @@ -259,23 +260,23 @@ static unsigned long sun4m_load_kernel(const char *kernel_filename, } /* load initrd */ - initrd_size = 0; + *initrd_size = 0; if (initrd_filename) { - initrd_size = load_image_targphys(initrd_filename, - INITRD_LOAD_ADDR, - RAM_size - INITRD_LOAD_ADDR); - if (initrd_size < 0) { + *initrd_size = load_image_targphys(initrd_filename, + INITRD_LOAD_ADDR, + RAM_size - INITRD_LOAD_ADDR); + if ((int)*initrd_size < 0) { error_report("could not load initial ram disk '%s'", initrd_filename); exit(1); } } - if (initrd_size > 0) { + if (*initrd_size > 0) { for (i = 0; i < 64 * TARGET_PAGE_SIZE; i += TARGET_PAGE_SIZE) { ptr = rom_ptr(KERNEL_LOAD_ADDR + i, 24); if (ptr && ldl_p(ptr) == 0x48647253) { /* HdrS */ stl_p(ptr + 16, INITRD_LOAD_ADDR); - stl_p(ptr + 20, initrd_size); + stl_p(ptr + 20, *initrd_size); break; } } @@ -559,8 +560,9 @@ static void idreg_init(hwaddr addr) s = SYS_BUS_DEVICE(dev); sysbus_mmio_map(s, 0, addr); - cpu_physical_memory_write_rom(&address_space_memory, - addr, idreg_data, sizeof(idreg_data)); + address_space_write_rom(&address_space_memory, addr, + MEMTXATTRS_UNSPECIFIED, + idreg_data, sizeof(idreg_data)); } #define MACIO_ID_REGISTER(obj) \ @@ -692,7 +694,8 @@ 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, + ret = load_elf(filename, NULL, + translate_prom_address, &addr, NULL, NULL, NULL, 1, EM_SPARC, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); @@ -844,6 +847,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, qemu_irq *cpu_irqs[MAX_CPUS], slavio_irq[32], slavio_cpu_irq[MAX_CPUS]; qemu_irq fdc_tc; unsigned long kernel_size; + uint32_t initrd_size; DriveInfo *fd[MAX_FD]; FWCfgState *fw_cfg; unsigned int num_vsimms; @@ -1022,9 +1026,10 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, empty_slot_init(hwdef->bpp_base, 0x20); } + initrd_size = 0; kernel_size = sun4m_load_kernel(machine->kernel_filename, machine->initrd_filename, - machine->ram_size); + machine->ram_size, &initrd_size); nvram_init(nvram, (uint8_t *)&nd_table[0].macaddr, machine->kernel_cmdline, machine->boot_order, machine->ram_size, kernel_size, @@ -1035,7 +1040,17 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, ecc_init(hwdef->ecc_base, slavio_irq[28], hwdef->ecc_version); - fw_cfg = fw_cfg_init_mem(CFG_ADDR, CFG_ADDR + 2); + dev = qdev_create(NULL, TYPE_FW_CFG_MEM); + fw_cfg = FW_CFG(dev); + qdev_prop_set_uint32(dev, "data_width", 1); + qdev_prop_set_bit(dev, "dma_enabled", false); + object_property_add_child(OBJECT(qdev_get_machine()), TYPE_FW_CFG, + OBJECT(fw_cfg), NULL); + qdev_init_nofail(dev); + s = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(s, 0, CFG_ADDR); + sysbus_mmio_map(s, 1, CFG_ADDR + 2); + fw_cfg_add_i16(fw_cfg, FW_CFG_NB_CPUS, (uint16_t)smp_cpus); fw_cfg_add_i16(fw_cfg, FW_CFG_MAX_CPUS, (uint16_t)max_cpus); fw_cfg_add_i64(fw_cfg, FW_CFG_RAM_SIZE, (uint64_t)ram_size); @@ -1057,7 +1072,7 @@ static void sun4m_hw_init(const struct sun4m_hwdef *hwdef, fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE, 0); } fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_ADDR, INITRD_LOAD_ADDR); - fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, 0); // not used + fw_cfg_add_i32(fw_cfg, FW_CFG_INITRD_SIZE, initrd_size); fw_cfg_add_i16(fw_cfg, FW_CFG_BOOT_DEVICE, machine->boot_order[0]); qemu_register_boot_set(fw_cfg_boot_set, fw_cfg); } diff --git a/hw/sparc/trace-events b/hw/sparc/trace-events index 6e7259f8f8..355b07ae05 100644 --- a/hw/sparc/trace-events +++ b/hw/sparc/trace-events @@ -1,12 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/sparc/sun4m.c +# sun4m.c sun4m_cpu_interrupt(unsigned int level) "Set CPU IRQ %d" sun4m_cpu_reset_interrupt(unsigned int level) "Reset CPU IRQ %d" sun4m_cpu_set_irq_raise(int level) "Raise CPU IRQ %d" sun4m_cpu_set_irq_lower(int level) "Lower CPU IRQ %d" -# hw/sparc/sun4m_iommu.c +# sun4m_iommu.c sun4m_iommu_mem_readl(uint64_t addr, uint32_t ret) "read reg[0x%"PRIx64"] = 0x%x" sun4m_iommu_mem_writel(uint64_t addr, uint32_t val) "write reg[0x%"PRIx64"] = 0x%x" sun4m_iommu_mem_writel_ctrl(uint64_t iostart) "iostart = 0x%"PRIx64 @@ -16,6 +16,6 @@ sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "get flags sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva 0x%"PRIx64" => pa 0x%"PRIx64" iopte = 0x%x" sun4m_iommu_bad_addr(uint64_t addr) "bad addr 0x%"PRIx64 -# hw/sparc/leon3.c +# leon3.c leon3_set_irq(int intno) "Set CPU IRQ %d" leon3_reset_irq(int intno) "Reset CPU IRQ %d" diff --git a/hw/sparc64/Kconfig b/hw/sparc64/Kconfig new file mode 100644 index 0000000000..d4d76a89be --- /dev/null +++ b/hw/sparc64/Kconfig @@ -0,0 +1,19 @@ +config SUN4U + bool + imply PCI_DEVICES + imply SUNHME + imply TEST_DEVICES + imply PARALLEL + select M48T59 + select ISA_BUS + select FDC + select SERIAL_ISA + select PCI_SABRE + select IDE_CMD646 + select PCKBD + select SIMBA + +config NIAGARA + bool + select EMPTY_SLOT + select SUN4V_RTC diff --git a/hw/sparc64/Makefile.objs b/hw/sparc64/Makefile.objs index 117e0ff27d..af0525c1a2 100644 --- a/hw/sparc64/Makefile.objs +++ b/hw/sparc64/Makefile.objs @@ -1,4 +1,4 @@ obj-y += sparc64.o -obj-y += sun4u_iommu.o -obj-y += sun4u.o -obj-y += niagara.o \ No newline at end of file +obj-$(CONFIG_SUN4U) += sun4u_iommu.o +obj-$(CONFIG_SUN4U) += sun4u.o +obj-$(CONFIG_NIAGARA) += niagara.o diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 4fa8cb2904..f8a856f611 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -29,7 +29,7 @@ #include "hw/hw.h" #include "hw/boards.h" #include "hw/char/serial.h" -#include "hw/empty_slot.h" +#include "hw/misc/unimp.h" #include "hw/loader.h" #include "hw/sparc/sparc64.h" #include "hw/timer/sun4v-rtc.h" @@ -161,7 +161,7 @@ static void niagara_init(MachineState *machine) serial_mm_init(sysmem, NIAGARA_UART_BASE, 0, NULL, 115200, serial_hd(0), DEVICE_BIG_ENDIAN); } - empty_slot_init(NIAGARA_IOBBASE, NIAGARA_IOBSIZE); + create_unimplemented_device("sun4v-iob", NIAGARA_IOBBASE, NIAGARA_IOBSIZE); sun4v_rtc_init(NIAGARA_RTC_BASE); } diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 74b748497e..399f2d73c8 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -33,7 +33,6 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pci_host.h" #include "hw/pci-host/sabre.h" -#include "hw/i386/pc.h" #include "hw/char/serial.h" #include "hw/char/parallel.h" #include "hw/timer/m48t59.h" @@ -51,6 +50,7 @@ #include "hw/ide.h" #include "hw/ide/pci.h" #include "hw/loader.h" +#include "hw/fw-path-provider.h" #include "elf.h" #include "trace.h" @@ -139,7 +139,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, unsigned int i; long kernel_size; uint8_t *ptr; - uint64_t kernel_top; + uint64_t kernel_top = 0; linux_boot = (kernel_filename != NULL); @@ -152,7 +152,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, #else bswap_needed = 0; #endif - kernel_size = load_elf(kernel_filename, NULL, NULL, kernel_entry, + kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, kernel_entry, kernel_addr, &kernel_top, 1, EM_SPARCV9, 0, 0); if (kernel_size < 0) { *kernel_addr = KERNEL_LOAD_ADDR; @@ -172,7 +172,7 @@ static uint64_t sun4u_load_kernel(const char *kernel_filename, } /* load initrd above kernel */ *initrd_size = 0; - if (initrd_filename) { + if (initrd_filename && kernel_top) { *initrd_addr = TARGET_PAGE_ALIGN(kernel_top); *initrd_size = load_image_targphys(initrd_filename, @@ -213,6 +213,11 @@ typedef struct PowerDevice { } PowerDevice; /* Power */ +static uint64_t power_mem_read(void *opaque, hwaddr addr, unsigned size) +{ + return 0; +} + static void power_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { @@ -223,6 +228,7 @@ static void power_mem_write(void *opaque, hwaddr addr, } static const MemoryRegionOps power_mem_ops = { + .read = power_mem_read, .write = power_mem_write, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { @@ -410,7 +416,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, + ret = load_elf(filename, NULL, translate_prom_address, &addr, NULL, NULL, NULL, 1, EM_SPARCV9, 0, 0); if (ret < 0 || ret > PROM_SIZE_MAX) { ret = load_image_targphys(filename, addr, PROM_SIZE_MAX); @@ -595,7 +601,15 @@ static void sun4uv_init(MemoryRegion *address_space_mem, qdev_connect_gpio_out_named(DEVICE(ebus), "isa-irq", 4, qdev_get_gpio_in_named(DEVICE(sabre), "pbm-irq", OBIO_SER_IRQ)); - pci_dev = pci_create_simple(pci_busA, PCI_DEVFN(2, 0), "VGA"); + switch (vga_interface_type) { + case VGA_STD: + pci_create_simple(pci_busA, PCI_DEVFN(2, 0), "VGA"); + break; + case VGA_NONE: + break; + default: + abort(); /* Should not happen - types are checked in vl.c already */ + } memset(&macaddr, 0, sizeof(MACAddr)); onboard_nic = false; @@ -693,6 +707,56 @@ enum { sun4v_id = 64, }; +/* + * Implementation of an interface to adjust firmware path + * for the bootindex property handling. + */ +static char *sun4u_fw_dev_path(FWPathProvider *p, BusState *bus, + DeviceState *dev) +{ + PCIDevice *pci; + IDEBus *ide_bus; + IDEState *ide_s; + int bus_id; + + if (!strcmp(object_get_typename(OBJECT(dev)), "pbm-bridge")) { + pci = PCI_DEVICE(dev); + + if (PCI_FUNC(pci->devfn)) { + return g_strdup_printf("pci@%x,%x", PCI_SLOT(pci->devfn), + PCI_FUNC(pci->devfn)); + } else { + return g_strdup_printf("pci@%x", PCI_SLOT(pci->devfn)); + } + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-drive")) { + ide_bus = IDE_BUS(qdev_get_parent_bus(dev)); + ide_s = idebus_active_if(ide_bus); + bus_id = ide_bus->bus_id; + + if (ide_s->drive_kind == IDE_CD) { + return g_strdup_printf("ide@%x/cdrom", bus_id); + } + + return g_strdup_printf("ide@%x/disk", bus_id); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-hd")) { + return g_strdup("disk"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "ide-cd")) { + return g_strdup("cdrom"); + } + + if (!strcmp(object_get_typename(OBJECT(dev)), "virtio-blk-device")) { + return g_strdup("disk"); + } + + return NULL; +} + static const struct hwdef hwdefs[] = { /* Sun4u generic PC-like machine */ { @@ -723,6 +787,7 @@ static void sun4v_init(MachineState *machine) static void sun4u_class_init(ObjectClass *oc, void *data) { MachineClass *mc = MACHINE_CLASS(oc); + FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); mc->desc = "Sun4u platform"; mc->init = sun4u_init; @@ -731,12 +796,19 @@ static void sun4u_class_init(ObjectClass *oc, void *data) mc->is_default = 1; mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("TI-UltraSparc-IIi"); + mc->ignore_boot_device_suffixes = true; + mc->default_display = "std"; + fwc->get_dev_path = sun4u_fw_dev_path; } static const TypeInfo sun4u_type = { .name = MACHINE_TYPE_NAME("sun4u"), .parent = TYPE_MACHINE, .class_init = sun4u_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_FW_PATH_PROVIDER }, + { } + }, }; static void sun4v_class_init(ObjectClass *oc, void *data) @@ -749,6 +821,7 @@ static void sun4v_class_init(ObjectClass *oc, void *data) mc->max_cpus = 1; /* XXX for now */ mc->default_boot_order = "c"; mc->default_cpu_type = SPARC_CPU_TYPE_NAME("Sun-UltraSparc-T1"); + mc->default_display = "std"; } static const TypeInfo sun4v_type = { diff --git a/hw/sparc64/trace-events b/hw/sparc64/trace-events index ce597a6e9d..a0b29987d2 100644 --- a/hw/sparc64/trace-events +++ b/hw/sparc64/trace-events @@ -1,14 +1,14 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/sparc64/sun4u.c +# sun4u.c ebus_isa_irq_handler(int n, int level) "Set ISA IRQ %d level %d" -# hw/sparc64/sun4u_iommu.c +# sun4u_iommu.c sun4u_iommu_mem_read(uint64_t addr, uint64_t val, int size) "addr: 0x%"PRIx64" val: 0x%"PRIx64" size: %d" sun4u_iommu_mem_write(uint64_t addr, uint64_t val, int size) "addr: 0x%"PRIx64" val: 0x%"PRIx64" size: %d" sun4u_iommu_translate(uint64_t addr, uint64_t trans_addr, uint64_t tte) "xlate 0x%"PRIx64" => pa 0x%"PRIx64" tte: 0x%"PRIx64 -# hw/sparc64/sparc64.c +# sparc64.c sparc64_cpu_check_irqs_reset_irq(int intno) "Reset CPU IRQ (current interrupt 0x%x)" sparc64_cpu_check_irqs_noset_irq(uint32_t tl, uint32_t tt, int intno) "Not setting CPU IRQ: TL=%d current 0x%x >= pending 0x%x" sparc64_cpu_check_irqs_set_irq(unsigned int i, int old, int new) "Set CPU IRQ %d old=0x%x new=0x%x" diff --git a/hw/ssi/Kconfig b/hw/ssi/Kconfig new file mode 100644 index 0000000000..9e54a0c8dd --- /dev/null +++ b/hw/ssi/Kconfig @@ -0,0 +1,18 @@ +config PL022 + bool + select SSI + +config SSI + bool + +config XILINX_SPI + bool + select SSI + +config XILINX_SPIPS + bool + select SSI + +config STM32F2XX_SPI + bool + select SSI diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index b29bfd3124..f1e66870d7 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -98,8 +98,8 @@ /* Misc Control Register #1 */ #define R_MISC_CTRL1 (0x50 / 4) -/* Misc Control Register #2 */ -#define R_MISC_CTRL2 (0x54 / 4) +/* SPI dummy cycle data */ +#define R_DUMMY_DATA (0x54 / 4) /* DMA Control/Status Register */ #define R_DMA_CTRL (0x80 / 4) @@ -145,6 +145,9 @@ /* Flash opcodes. */ #define SPI_OP_READ 0x03 /* Read data bytes (low frequency) */ +#define SNOOP_OFF 0xFF +#define SNOOP_START 0x0 + /* * Default segments mapping addresses and size for each slave per * controller. These can be changed when board is initialized with the @@ -388,8 +391,8 @@ static uint64_t aspeed_smc_flash_default_read(void *opaque, hwaddr addr, static void aspeed_smc_flash_default_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%" - PRIx64 "\n", __func__, addr, size, data); + qemu_log_mask(LOG_GUEST_ERROR, "%s: To 0x%" HWADDR_PRIx " of size %u: 0x%" + PRIx64 "\n", __func__, addr, size, data); } static const MemoryRegionOps aspeed_smc_flash_default_ops = { @@ -529,7 +532,7 @@ static void aspeed_smc_flash_setup(AspeedSMCFlash *fl, uint32_t addr) */ if (aspeed_smc_flash_mode(fl) == CTRL_FREADMODE) { for (i = 0; i < aspeed_smc_flash_dummies(fl); i++) { - ssi_transfer(fl->controller->spi, 0xFF); + ssi_transfer(fl->controller->spi, s->regs[R_DUMMY_DATA] & 0xff); } } } @@ -566,8 +569,103 @@ static uint64_t aspeed_smc_flash_read(void *opaque, hwaddr addr, unsigned size) return ret; } +/* + * TODO (clg@kaod.org): stolen from xilinx_spips.c. Should move to a + * common include header. + */ +typedef enum { + READ = 0x3, READ_4 = 0x13, + FAST_READ = 0xb, FAST_READ_4 = 0x0c, + DOR = 0x3b, DOR_4 = 0x3c, + QOR = 0x6b, QOR_4 = 0x6c, + DIOR = 0xbb, DIOR_4 = 0xbc, + QIOR = 0xeb, QIOR_4 = 0xec, + + PP = 0x2, PP_4 = 0x12, + DPP = 0xa2, + QPP = 0x32, QPP_4 = 0x34, +} FlashCMD; + +static int aspeed_smc_num_dummies(uint8_t command) +{ + switch (command) { /* check for dummies */ + case READ: /* no dummy bytes/cycles */ + case PP: + case DPP: + case QPP: + case READ_4: + case PP_4: + case QPP_4: + return 0; + case FAST_READ: + case DOR: + case QOR: + case DOR_4: + case QOR_4: + return 1; + case DIOR: + case FAST_READ_4: + case DIOR_4: + return 2; + case QIOR: + case QIOR_4: + return 4; + default: + return -1; + } +} + +static bool aspeed_smc_do_snoop(AspeedSMCFlash *fl, uint64_t data, + unsigned size) +{ + AspeedSMCState *s = fl->controller; + uint8_t addr_width = aspeed_smc_flash_is_4byte(fl) ? 4 : 3; + + if (s->snoop_index == SNOOP_OFF) { + return false; /* Do nothing */ + + } else if (s->snoop_index == SNOOP_START) { + uint8_t cmd = data & 0xff; + int ndummies = aspeed_smc_num_dummies(cmd); + + /* + * No dummy cycles are expected with the current command. Turn + * off snooping and let the transfer proceed normally. + */ + if (ndummies <= 0) { + s->snoop_index = SNOOP_OFF; + return false; + } + + s->snoop_dummies = ndummies * 8; + + } else if (s->snoop_index >= addr_width + 1) { + + /* The SPI transfer has reached the dummy cycles sequence */ + for (; s->snoop_dummies; s->snoop_dummies--) { + ssi_transfer(s->spi, s->regs[R_DUMMY_DATA] & 0xff); + } + + /* If no more dummy cycles are expected, turn off snooping */ + if (!s->snoop_dummies) { + s->snoop_index = SNOOP_OFF; + } else { + s->snoop_index += size; + } + + /* + * Dummy cycles have been faked already. Ignore the current + * SPI transfer + */ + return true; + } + + s->snoop_index += size; + return false; +} + static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, - unsigned size) + unsigned size) { AspeedSMCFlash *fl = opaque; AspeedSMCState *s = fl->controller; @@ -581,6 +679,10 @@ static void aspeed_smc_flash_write(void *opaque, hwaddr addr, uint64_t data, switch (aspeed_smc_flash_mode(fl)) { case CTRL_USERMODE: + if (aspeed_smc_do_snoop(fl, data, size)) { + break; + } + for (i = 0; i < size; i++) { ssi_transfer(s->spi, (data >> (8 * i)) & 0xff); } @@ -613,7 +715,9 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { static void aspeed_smc_flash_update_cs(AspeedSMCFlash *fl) { - const AspeedSMCState *s = fl->controller; + AspeedSMCState *s = fl->controller; + + s->snoop_index = aspeed_smc_is_ce_stop_active(fl) ? SNOOP_OFF : SNOOP_START; qemu_set_irq(s->cs_lines[fl->id], aspeed_smc_is_ce_stop_active(fl)); } @@ -652,6 +756,9 @@ static void aspeed_smc_reset(DeviceState *d) if (s->ctrl->segments == aspeed_segments_fmc) { s->regs[s->r_conf] |= (CONF_FLASH_TYPE_SPI << CONF_FLASH_TYPE0); } + + s->snoop_index = SNOOP_OFF; + s->snoop_dummies = 0; } static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) @@ -664,13 +771,14 @@ static uint64_t aspeed_smc_read(void *opaque, hwaddr addr, unsigned int size) addr == s->r_timings || addr == s->r_ce_ctrl || addr == R_INTR_CTRL || + addr == R_DUMMY_DATA || (addr >= R_SEG_ADDR0 && addr < R_SEG_ADDR0 + s->ctrl->max_slaves) || - (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->num_cs)) { + (addr >= s->r_ctrl0 && addr < s->r_ctrl0 + s->ctrl->max_slaves)) { return s->regs[addr]; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); - return 0; + return -1; } } @@ -697,6 +805,8 @@ static void aspeed_smc_write(void *opaque, hwaddr addr, uint64_t data, if (value != s->regs[R_SEG_ADDR0 + cs]) { aspeed_smc_flash_set_segment(s, cs, value); } + } else if (addr == R_DUMMY_DATA) { + s->regs[addr] = value & 0xff; } else { qemu_log_mask(LOG_UNIMP, "%s: not implemented: 0x%" HWADDR_PRIx "\n", __func__, addr); @@ -790,10 +900,12 @@ static void aspeed_smc_realize(DeviceState *dev, Error **errp) static const VMStateDescription vmstate_aspeed_smc = { .name = "aspeed.smc", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedSMCState, ASPEED_SMC_R_MAX), + VMSTATE_UINT8(snoop_index, AspeedSMCState), + VMSTATE_UINT8(snoop_dummies, AspeedSMCState), VMSTATE_END_OF_LIST() } }; diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index b66505ca49..02c38c9e47 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -208,8 +208,6 @@ static void imx_spi_flush_txfifo(IMXSPIState *s) } if (s->burst_length <= 0) { - s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH; - if (!imx_spi_is_multiple_master_burst(s)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; break; @@ -219,6 +217,7 @@ static void imx_spi_flush_txfifo(IMXSPIState *s) if (fifo32_is_empty(&s->tx_fifo)) { s->regs[ECSPI_STATREG] |= ECSPI_STATREG_TC; + s->regs[ECSPI_CONREG] &= ~ECSPI_CONREG_XCH; } /* TODO: We should also use TDR and RDR bits */ diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index c1368018ee..e58247554c 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" +#include "hw/ssi/pl022.h" #include "hw/ssi/ssi.h" #include "qemu/log.h" @@ -37,35 +38,10 @@ do { fprintf(stderr, "pl022: error: " fmt , ## __VA_ARGS__);} while (0) #define PL022_SR_BSY 0x10 #define PL022_INT_ROR 0x01 -#define PL022_INT_RT 0x04 +#define PL022_INT_RT 0x02 #define PL022_INT_RX 0x04 #define PL022_INT_TX 0x08 -#define TYPE_PL022 "pl022" -#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) - -typedef struct PL022State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - uint32_t cr0; - uint32_t cr1; - uint32_t bitmask; - uint32_t sr; - uint32_t cpsr; - uint32_t is; - uint32_t im; - /* The FIFO head points to the next empty entry. */ - int tx_fifo_head; - int rx_fifo_head; - int tx_fifo_len; - int rx_fifo_len; - uint16_t tx_fifo[8]; - uint16_t rx_fifo[8]; - qemu_irq irq; - SSIBus *ssi; -} PL022State; - static const unsigned char pl022_id[8] = { 0x22, 0x10, 0x04, 0x00, 0x0d, 0xf0, 0x05, 0xb1 }; @@ -170,7 +146,7 @@ static uint64_t pl022_read(void *opaque, hwaddr offset, return s->is; case 0x1c: /* MIS */ return s->im & s->is; - case 0x20: /* DMACR */ + case 0x24: /* DMACR */ /* Not implemented. */ return 0; default: @@ -216,7 +192,15 @@ static void pl022_write(void *opaque, hwaddr offset, s->im = value; pl022_update(s); break; - case 0x20: /* DMACR */ + case 0x20: /* ICR */ + /* + * write-1-to-clear: bit 0 clears ROR, bit 1 clears RT; + * RX and TX interrupts cannot be cleared this way. + */ + value &= PL022_INT_ROR | PL022_INT_RT; + s->is &= ~value; + break; + case 0x24: /* DMACR */ if (value) { qemu_log_mask(LOG_UNIMP, "pl022: DMA not implemented\n"); } @@ -227,8 +211,10 @@ static void pl022_write(void *opaque, hwaddr offset, } } -static void pl022_reset(PL022State *s) +static void pl022_reset(DeviceState *dev) { + PL022State *s = PL022(dev); + s->rx_fifo_len = 0; s->tx_fifo_len = 0; s->im = 0; @@ -292,25 +278,24 @@ static const VMStateDescription vmstate_pl022 = { } }; -static int pl022_init(SysBusDevice *sbd) +static void pl022_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); PL022State *s = PL022(dev); memory_region_init_io(&s->iomem, OBJECT(s), &pl022_ops, s, "pl022", 0x1000); sysbus_init_mmio(sbd, &s->iomem); sysbus_init_irq(sbd, &s->irq); s->ssi = ssi_create_bus(dev, "ssi"); - pl022_reset(s); - vmstate_register(dev, -1, &vmstate_pl022, s); - return 0; } static void pl022_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = pl022_init; + dc->reset = pl022_reset; + dc->vmsd = &vmstate_pl022; + dc->realize = pl022_realize; } static const TypeInfo pl022_info = { diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index 83585bc8b2..3dae303d5b 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -319,9 +319,9 @@ static const MemoryRegionOps spi_ops = { } }; -static int xilinx_spi_init(SysBusDevice *sbd) +static void xilinx_spi_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); XilinxSPI *s = XILINX_SPI(dev); int i; @@ -344,8 +344,6 @@ static int xilinx_spi_init(SysBusDevice *sbd) fifo8_create(&s->tx_fifo, FIFO_CAPACITY); fifo8_create(&s->rx_fifo, FIFO_CAPACITY); - - return 0; } static const VMStateDescription vmstate_xilinx_spi = { @@ -368,9 +366,8 @@ static Property xilinx_spi_properties[] = { static void xilinx_spi_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = xilinx_spi_init; + dc->realize = xilinx_spi_realize; dc->reset = xlx_spi_reset; dc->props = xilinx_spi_properties; dc->vmsd = &vmstate_xilinx_spi; diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index c052bfc4b3..16f88f7402 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1031,14 +1031,6 @@ static const MemoryRegionOps spips_ops = { static void xilinx_qspips_invalidate_mmio_ptr(XilinxQSPIPS *q) { - XilinxSPIPS *s = &q->parent_obj; - - if ((q->mmio_execution_enabled) && (q->lqspi_cached_addr != ~0ULL)) { - /* Invalidate the current mapped mmio */ - memory_region_invalidate_mmio_ptr(&s->mmlqspi, q->lqspi_cached_addr, - LQSPI_CACHE_SIZE); - } - q->lqspi_cached_addr = ~0ULL; } @@ -1207,23 +1199,6 @@ static void lqspi_load_cache(void *opaque, hwaddr addr) } } -static void *lqspi_request_mmio_ptr(void *opaque, hwaddr addr, unsigned *size, - unsigned *offset) -{ - XilinxQSPIPS *q = opaque; - hwaddr offset_within_the_region; - - if (!q->mmio_execution_enabled) { - return NULL; - } - - offset_within_the_region = addr & ~(LQSPI_CACHE_SIZE - 1); - lqspi_load_cache(opaque, offset_within_the_region); - *size = LQSPI_CACHE_SIZE; - *offset = offset_within_the_region; - return q->lqspi_buf; -} - static uint64_t lqspi_read(void *opaque, hwaddr addr, unsigned int size) { @@ -1245,7 +1220,6 @@ lqspi_read(void *opaque, hwaddr addr, unsigned int size) static const MemoryRegionOps lqspi_ops = { .read = lqspi_read, - .request_ptr = lqspi_request_mmio_ptr, .endianness = DEVICE_NATIVE_ENDIAN, .valid = { .min_access_size = 1, @@ -1322,15 +1296,6 @@ static void xilinx_qspips_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->mmlqspi); q->lqspi_cached_addr = ~0ULL; - - /* mmio_execution breaks migration better aborting than having strange - * bugs. - */ - if (q->mmio_execution_enabled) { - error_setg(&q->migration_blocker, - "enabling mmio_execution breaks migration"); - migrate_add_blocker(q->migration_blocker, &error_fatal); - } } static void xlnx_zynqmp_qspips_realize(DeviceState *dev, Error **errp) @@ -1427,16 +1392,6 @@ static Property xilinx_zynqmp_qspips_properties[] = { DEFINE_PROP_END_OF_LIST(), }; -static Property xilinx_qspips_properties[] = { - /* We had to turn this off for 2.10 as it is not compatible with migration. - * It can be enabled but will prevent the device to be migrated. - * This will go aways when a fix will be released. - */ - DEFINE_PROP_BOOL("x-mmio-exec", XilinxQSPIPS, mmio_execution_enabled, - false), - DEFINE_PROP_END_OF_LIST(), -}; - static Property xilinx_spips_properties[] = { DEFINE_PROP_UINT8("num-busses", XilinxSPIPS, num_busses, 1), DEFINE_PROP_UINT8("num-ss-bits", XilinxSPIPS, num_cs, 4), @@ -1450,7 +1405,6 @@ static void xilinx_qspips_class_init(ObjectClass *klass, void * data) XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); dc->realize = xilinx_qspips_realize; - dc->props = xilinx_qspips_properties; xsc->reg_ops = &qspips_ops; xsc->rx_fifo_size = RXFF_A_Q; xsc->tx_fifo_size = TXFF_A_Q; diff --git a/hw/timer/Kconfig b/hw/timer/Kconfig new file mode 100644 index 0000000000..51921eb63f --- /dev/null +++ b/hw/timer/Kconfig @@ -0,0 +1,63 @@ +config ARM_TIMER + bool + select PTIMER + +config ARM_MPTIMER + bool + select PTIMER + +config A9_GTIMER + bool + +config DS1338 + bool + depends on I2C + +config HPET + bool + default y if PC + +config I8254 + bool + +config M41T80 + bool + depends on I2C + +config M48T59 + bool + +config PL031 + bool + +config TWL92230 + bool + depends on I2C + +config XLNX_ZYNQMP + bool + +config ALTERA_TIMER + bool + select PTIMER + +config MC146818RTC + bool + +config ALLWINNER_A10_PIT + bool + select PTIMER + +config STM32F2XX_TIMER + bool + +config SUN4V_RTC + bool + +config CMSDK_APB_TIMER + bool + select PTIMER + +config CMSDK_APB_DUALTIMER + bool + select PTIMER diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs index e16b2b913c..0e9a4530f8 100644 --- a/hw/timer/Makefile.objs +++ b/hw/timer/Makefile.objs @@ -23,6 +23,7 @@ common-obj-$(CONFIG_IMX) += imx_gpt.o common-obj-$(CONFIG_LM32) += lm32_timer.o common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o common-obj-$(CONFIG_XLNX_ZYNQMP) += xlnx-zynqmp-rtc.o +common-obj-$(CONFIG_NRF51_SOC) += nrf51_timer.o obj-$(CONFIG_ALTERA_TIMER) += altera_timer.o obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o @@ -44,4 +45,5 @@ common-obj-$(CONFIG_ASPEED_SOC) += aspeed_timer.o common-obj-$(CONFIG_SUN4V_RTC) += sun4v-rtc.o common-obj-$(CONFIG_CMSDK_APB_TIMER) += cmsdk-apb-timer.o +common-obj-$(CONFIG_CMSDK_APB_DUALTIMER) += cmsdk-apb-dualtimer.o common-obj-$(CONFIG_MSF2) += mss-timer.o diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index 5e3f51b66b..5c786e5128 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -13,7 +13,6 @@ #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/timer/aspeed_timer.h" -#include "hw/misc/aspeed_scu.h" #include "qemu-common.h" #include "qemu/bitops.h" #include "qemu/timer.h" @@ -455,8 +454,7 @@ static void aspeed_timer_realize(DeviceState *dev, Error **errp) obj = object_property_get_link(OBJECT(dev), "scu", &err); if (!obj) { - error_propagate(errp, err); - error_prepend(errp, "required link 'scu' not found: "); + error_propagate_prepend(errp, err, "required link 'scu' not found: "); return; } s->scu = ASPEED_SCU(obj); diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c new file mode 100644 index 0000000000..30245990f3 --- /dev/null +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -0,0 +1,517 @@ +/* + * ARM CMSDK APB dual-timer emulation + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB dual-input timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" + +REG32(TIMER1LOAD, 0x0) +REG32(TIMER1VALUE, 0x4) +REG32(TIMER1CONTROL, 0x8) + FIELD(CONTROL, ONESHOT, 0, 1) + FIELD(CONTROL, SIZE, 1, 1) + FIELD(CONTROL, PRESCALE, 2, 2) + FIELD(CONTROL, INTEN, 5, 1) + FIELD(CONTROL, MODE, 6, 1) + FIELD(CONTROL, ENABLE, 7, 1) +#define R_CONTROL_VALID_MASK (R_CONTROL_ONESHOT_MASK | R_CONTROL_SIZE_MASK | \ + R_CONTROL_PRESCALE_MASK | R_CONTROL_INTEN_MASK | \ + R_CONTROL_MODE_MASK | R_CONTROL_ENABLE_MASK) +REG32(TIMER1INTCLR, 0xc) +REG32(TIMER1RIS, 0x10) +REG32(TIMER1MIS, 0x14) +REG32(TIMER1BGLOAD, 0x18) +REG32(TIMER2LOAD, 0x20) +REG32(TIMER2VALUE, 0x24) +REG32(TIMER2CONTROL, 0x28) +REG32(TIMER2INTCLR, 0x2c) +REG32(TIMER2RIS, 0x30) +REG32(TIMER2MIS, 0x34) +REG32(TIMER2BGLOAD, 0x38) +REG32(TIMERITCR, 0xf00) + FIELD(TIMERITCR, ENABLE, 0, 1) +#define R_TIMERITCR_VALID_MASK R_TIMERITCR_ENABLE_MASK +REG32(TIMERITOP, 0xf04) + FIELD(TIMERITOP, TIMINT1, 0, 1) + FIELD(TIMERITOP, TIMINT2, 1, 1) +#define R_TIMERITOP_VALID_MASK (R_TIMERITOP_TIMINT1_MASK | \ + R_TIMERITOP_TIMINT2_MASK) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const int timer_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x23, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static bool cmsdk_dualtimermod_intstatus(CMSDKAPBDualTimerModule *m) +{ + /* Return masked interrupt status for the timer module */ + return m->intstatus && (m->control & R_CONTROL_INTEN_MASK); +} + +static void cmsdk_apb_dualtimer_update(CMSDKAPBDualTimer *s) +{ + bool timint1, timint2, timintc; + + if (s->timeritcr) { + /* Integration test mode: outputs driven directly from TIMERITOP bits */ + timint1 = s->timeritop & R_TIMERITOP_TIMINT1_MASK; + timint2 = s->timeritop & R_TIMERITOP_TIMINT2_MASK; + } else { + timint1 = cmsdk_dualtimermod_intstatus(&s->timermod[0]); + timint2 = cmsdk_dualtimermod_intstatus(&s->timermod[1]); + } + + timintc = timint1 || timint2; + + qemu_set_irq(s->timermod[0].timerint, timint1); + qemu_set_irq(s->timermod[1].timerint, timint2); + qemu_set_irq(s->timerintc, timintc); +} + +static void cmsdk_dualtimermod_write_control(CMSDKAPBDualTimerModule *m, + uint32_t newctrl) +{ + /* Handle a write to the CONTROL register */ + uint32_t changed; + + newctrl &= R_CONTROL_VALID_MASK; + + changed = m->control ^ newctrl; + + if (changed & ~newctrl & R_CONTROL_ENABLE_MASK) { + /* ENABLE cleared, stop timer before any further changes */ + ptimer_stop(m->timer); + } + + if (changed & R_CONTROL_PRESCALE_MASK) { + int divisor; + + switch (FIELD_EX32(newctrl, CONTROL, PRESCALE)) { + case 0: + divisor = 1; + break; + case 1: + divisor = 16; + break; + case 2: + divisor = 256; + break; + case 3: + /* UNDEFINED; complain, and arbitrarily treat like 2 */ + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer: CONTROL.PRESCALE==0b11" + " is undefined behaviour\n"); + divisor = 256; + break; + default: + g_assert_not_reached(); + } + ptimer_set_freq(m->timer, m->parent->pclk_frq / divisor); + } + + if (changed & R_CONTROL_MODE_MASK) { + uint32_t load; + if (newctrl & R_CONTROL_MODE_MASK) { + /* Periodic: the limit is the LOAD register value */ + load = m->load; + } else { + /* Free-running: counter wraps around */ + load = ptimer_get_limit(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + load = deposit32(m->load, 0, 16, load); + } + m->load = load; + load = 0xffffffff; + } + if (!(m->control & R_CONTROL_SIZE_MASK)) { + load &= 0xffff; + } + ptimer_set_limit(m->timer, load, 0); + } + + if (changed & R_CONTROL_SIZE_MASK) { + /* Timer switched between 16 and 32 bit count */ + uint32_t value, load; + + value = ptimer_get_count(m->timer); + load = ptimer_get_limit(m->timer); + if (newctrl & R_CONTROL_SIZE_MASK) { + /* 16 -> 32, top half of VALUE is in struct field */ + value = deposit32(m->value, 0, 16, value); + } else { + /* 32 -> 16: save top half to struct field and truncate */ + m->value = value; + value &= 0xffff; + } + + if (newctrl & R_CONTROL_MODE_MASK) { + /* Periodic, timer limit has LOAD value */ + if (newctrl & R_CONTROL_SIZE_MASK) { + load = deposit32(m->load, 0, 16, load); + } else { + m->load = load; + load &= 0xffff; + } + } else { + /* Free-running, timer limit is set to give wraparound */ + if (newctrl & R_CONTROL_SIZE_MASK) { + load = 0xffffffff; + } else { + load = 0xffff; + } + } + ptimer_set_count(m->timer, value); + ptimer_set_limit(m->timer, load, 0); + } + + if (newctrl & R_CONTROL_ENABLE_MASK) { + /* + * ENABLE is set; start the timer after all other changes. + * We start it even if the ENABLE bit didn't actually change, + * in case the timer was an expired one-shot timer that has + * now been changed into a free-running or periodic timer. + */ + ptimer_run(m->timer, !!(newctrl & R_CONTROL_ONESHOT_MASK)); + } + + m->control = newctrl; +} + +static uint64_t cmsdk_apb_dualtimer_read(void *opaque, hwaddr offset, + unsigned size) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); + uint64_t r; + + if (offset >= A_TIMERITCR) { + switch (offset) { + case A_TIMERITCR: + r = s->timeritcr; + break; + case A_PID4 ... A_CID3: + r = timer_id[(offset - A_PID4) / 4]; + break; + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer read: bad offset %x\n", + (int) offset); + r = 0; + break; + } + } else { + int timer = offset >> 5; + CMSDKAPBDualTimerModule *m; + + if (timer >= ARRAY_SIZE(s->timermod)) { + goto bad_offset; + } + + m = &s->timermod[timer]; + + switch (offset & 0x1F) { + case A_TIMER1LOAD: + case A_TIMER1BGLOAD: + if (m->control & R_CONTROL_MODE_MASK) { + /* + * Periodic: the ptimer limit is the LOAD register value, (or + * just the low 16 bits of it if the timer is in 16-bit mode) + */ + r = ptimer_get_limit(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + r = deposit32(m->load, 0, 16, r); + } + } else { + /* Free-running: LOAD register value is just in m->load */ + r = m->load; + } + break; + case A_TIMER1VALUE: + r = ptimer_get_count(m->timer); + if (!(m->control & R_CONTROL_SIZE_MASK)) { + r = deposit32(m->value, 0, 16, r); + } + break; + case A_TIMER1CONTROL: + r = m->control; + break; + case A_TIMER1RIS: + r = m->intstatus; + break; + case A_TIMER1MIS: + r = cmsdk_dualtimermod_intstatus(m); + break; + default: + goto bad_offset; + } + } + + trace_cmsdk_apb_dualtimer_read(offset, r, size); + return r; +} + +static void cmsdk_apb_dualtimer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(opaque); + + trace_cmsdk_apb_dualtimer_write(offset, value, size); + + if (offset >= A_TIMERITCR) { + switch (offset) { + case A_TIMERITCR: + s->timeritcr = value & R_TIMERITCR_VALID_MASK; + cmsdk_apb_dualtimer_update(s); + break; + case A_TIMERITOP: + s->timeritop = value & R_TIMERITOP_VALID_MASK; + cmsdk_apb_dualtimer_update(s); + break; + default: + bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB dual-timer write: bad offset %x\n", + (int) offset); + break; + } + } else { + int timer = offset >> 5; + CMSDKAPBDualTimerModule *m; + + if (timer >= ARRAY_SIZE(s->timermod)) { + goto bad_offset; + } + + m = &s->timermod[timer]; + + switch (offset & 0x1F) { + case A_TIMER1LOAD: + /* Set the limit, and immediately reload the count from it */ + m->load = value; + m->value = value; + if (!(m->control & R_CONTROL_SIZE_MASK)) { + value &= 0xffff; + } + if (!(m->control & R_CONTROL_MODE_MASK)) { + /* + * In free-running mode this won't set the limit but will + * still change the current count value. + */ + ptimer_set_count(m->timer, value); + } else { + if (!value) { + ptimer_stop(m->timer); + } + ptimer_set_limit(m->timer, value, 1); + if (value && (m->control & R_CONTROL_ENABLE_MASK)) { + /* Force possibly-expired oneshot timer to restart */ + ptimer_run(m->timer, 1); + } + } + break; + case A_TIMER1BGLOAD: + /* Set the limit, but not the current count */ + m->load = value; + if (!(m->control & R_CONTROL_MODE_MASK)) { + /* In free-running mode there is no limit */ + break; + } + if (!(m->control & R_CONTROL_SIZE_MASK)) { + value &= 0xffff; + } + ptimer_set_limit(m->timer, value, 0); + break; + case A_TIMER1CONTROL: + cmsdk_dualtimermod_write_control(m, value); + cmsdk_apb_dualtimer_update(s); + break; + case A_TIMER1INTCLR: + m->intstatus = 0; + cmsdk_apb_dualtimer_update(s); + break; + default: + goto bad_offset; + } + } +} + +static const MemoryRegionOps cmsdk_apb_dualtimer_ops = { + .read = cmsdk_apb_dualtimer_read, + .write = cmsdk_apb_dualtimer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void cmsdk_dualtimermod_tick(void *opaque) +{ + CMSDKAPBDualTimerModule *m = opaque; + + m->intstatus = 1; + cmsdk_apb_dualtimer_update(m->parent); +} + +static void cmsdk_dualtimermod_reset(CMSDKAPBDualTimerModule *m) +{ + m->control = R_CONTROL_INTEN_MASK; + m->intstatus = 0; + m->load = 0; + m->value = 0xffffffff; + ptimer_stop(m->timer); + /* + * We start in free-running mode, with VALUE at 0xffffffff, and + * in 16-bit counter mode. This means that the ptimer count and + * limit must both be set to 0xffff, so we wrap at 16 bits. + */ + ptimer_set_limit(m->timer, 0xffff, 1); + ptimer_set_freq(m->timer, m->parent->pclk_frq); +} + +static void cmsdk_apb_dualtimer_reset(DeviceState *dev) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); + int i; + + trace_cmsdk_apb_dualtimer_reset(); + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + cmsdk_dualtimermod_reset(&s->timermod[i]); + } + s->timeritcr = 0; + s->timeritop = 0; +} + +static void cmsdk_apb_dualtimer_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(obj); + int i; + + memory_region_init_io(&s->iomem, obj, &cmsdk_apb_dualtimer_ops, + s, "cmsdk-apb-dualtimer", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->timerintc); + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + sysbus_init_irq(sbd, &s->timermod[i].timerint); + } +} + +static void cmsdk_apb_dualtimer_realize(DeviceState *dev, Error **errp) +{ + CMSDKAPBDualTimer *s = CMSDK_APB_DUALTIMER(dev); + int i; + + if (s->pclk_frq == 0) { + error_setg(errp, "CMSDK APB timer: pclk-frq property must be set"); + return; + } + + for (i = 0; i < ARRAY_SIZE(s->timermod); i++) { + CMSDKAPBDualTimerModule *m = &s->timermod[i]; + QEMUBH *bh = qemu_bh_new(cmsdk_dualtimermod_tick, m); + + m->parent = s; + m->timer = ptimer_init(bh, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + } +} + +static const VMStateDescription cmsdk_dualtimermod_vmstate = { + .name = "cmsdk-apb-dualtimer-module", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(timer, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(load, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(value, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(control, CMSDKAPBDualTimerModule), + VMSTATE_UINT32(intstatus, CMSDKAPBDualTimerModule), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { + .name = "cmsdk-apb-dualtimer", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_STRUCT_ARRAY(timermod, CMSDKAPBDualTimer, + CMSDK_APB_DUALTIMER_NUM_MODULES, + 1, cmsdk_dualtimermod_vmstate, + CMSDKAPBDualTimerModule), + VMSTATE_UINT32(timeritcr, CMSDKAPBDualTimer), + VMSTATE_UINT32(timeritop, CMSDKAPBDualTimer), + VMSTATE_END_OF_LIST() + } +}; + +static Property cmsdk_apb_dualtimer_properties[] = { + DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBDualTimer, pclk_frq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cmsdk_apb_dualtimer_realize; + dc->vmsd = &cmsdk_apb_dualtimer_vmstate; + dc->reset = cmsdk_apb_dualtimer_reset; + dc->props = cmsdk_apb_dualtimer_properties; +} + +static const TypeInfo cmsdk_apb_dualtimer_info = { + .name = TYPE_CMSDK_APB_DUALTIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBDualTimer), + .instance_init = cmsdk_apb_dualtimer_init, + .class_init = cmsdk_apb_dualtimer_class_init, +}; + +static void cmsdk_apb_dualtimer_register_types(void) +{ + type_register_static(&cmsdk_apb_dualtimer_info); +} + +type_init(cmsdk_apb_dualtimer_register_types); diff --git a/hw/timer/ds1338.c b/hw/timer/ds1338.c index 3849b74a68..03da75486b 100644 --- a/hw/timer/ds1338.c +++ b/hw/timer/ds1338.c @@ -117,7 +117,7 @@ static int ds1338_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static int ds1338_recv(I2CSlave *i2c) +static uint8_t ds1338_recv(I2CSlave *i2c) { DS1338State *s = DS1338(i2c); uint8_t res; diff --git a/hw/timer/etraxfs_timer.c b/hw/timer/etraxfs_timer.c index d13bc30b2d..2280914b1d 100644 --- a/hw/timer/etraxfs_timer.c +++ b/hw/timer/etraxfs_timer.c @@ -315,9 +315,10 @@ static void etraxfs_timer_reset(void *opaque) qemu_irq_lower(t->irq); } -static int etraxfs_timer_init(SysBusDevice *dev) +static void etraxfs_timer_realize(DeviceState *dev, Error **errp) { ETRAXTimerState *t = ETRAX_TIMER(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); t->bh_t0 = qemu_bh_new(timer0_hit, t); t->bh_t1 = qemu_bh_new(timer1_hit, t); @@ -326,21 +327,20 @@ static int etraxfs_timer_init(SysBusDevice *dev) t->ptimer_t1 = ptimer_init(t->bh_t1, PTIMER_POLICY_DEFAULT); t->ptimer_wd = ptimer_init(t->bh_wd, PTIMER_POLICY_DEFAULT); - sysbus_init_irq(dev, &t->irq); - sysbus_init_irq(dev, &t->nmi); + sysbus_init_irq(sbd, &t->irq); + sysbus_init_irq(sbd, &t->nmi); memory_region_init_io(&t->mmio, OBJECT(t), &timer_ops, t, "etraxfs-timer", 0x5c); - sysbus_init_mmio(dev, &t->mmio); + sysbus_init_mmio(sbd, &t->mmio); qemu_register_reset(etraxfs_timer_reset, t); - return 0; } static void etraxfs_timer_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = etraxfs_timer_init; + dc->realize = etraxfs_timer_realize; } static const TypeInfo etraxfs_timer_info = { diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index 4ed96e970a..183eddc073 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -347,10 +347,11 @@ static void grlib_gptimer_reset(DeviceState *d) } } -static int grlib_gptimer_init(SysBusDevice *dev) +static void grlib_gptimer_realize(DeviceState *dev, Error **errp) { GPTimerUnit *unit = GRLIB_GPTIMER(dev); unsigned int i; + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); assert(unit->nr_timers > 0); assert(unit->nr_timers <= GPTIMER_MAX_TIMERS); @@ -366,7 +367,7 @@ static int grlib_gptimer_init(SysBusDevice *dev) timer->id = i; /* One IRQ line for each timer */ - sysbus_init_irq(dev, &timer->irq); + sysbus_init_irq(sbd, &timer->irq); ptimer_set_freq(timer->ptimer, unit->freq_hz); } @@ -375,8 +376,7 @@ static int grlib_gptimer_init(SysBusDevice *dev) unit, "gptimer", UNIT_REG_SIZE + GPTIMER_REG_SIZE * unit->nr_timers); - sysbus_init_mmio(dev, &unit->iomem); - return 0; + sysbus_init_mmio(sbd, &unit->iomem); } static Property grlib_gptimer_properties[] = { @@ -389,9 +389,8 @@ static Property grlib_gptimer_properties[] = { static void grlib_gptimer_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = grlib_gptimer_init; + dc->realize = grlib_gptimer_realize; dc->reset = grlib_gptimer_reset; dc->props = grlib_gptimer_properties; } diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index 1057850808..20f834e7da 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -23,7 +23,6 @@ */ #include "qemu/osdep.h" #include "hw/hw.h" -#include "hw/isa/isa.h" #include "qemu/timer.h" #include "hw/timer/i8254.h" #include "hw/timer/i8254_internal.h" diff --git a/hw/timer/m41t80.c b/hw/timer/m41t80.c index 734d7d95fc..c45b9297d8 100644 --- a/hw/timer/m41t80.c +++ b/hw/timer/m41t80.c @@ -40,7 +40,7 @@ static int m41t80_send(I2CSlave *i2c, uint8_t data) return 0; } -static int m41t80_recv(I2CSlave *i2c) +static uint8_t m41t80_recv(I2CSlave *i2c) { M41t80State *s = M41T80(i2c); struct tm now; diff --git a/hw/timer/m48t59.c b/hw/timer/m48t59.c index f2991762ab..ca3ed445de 100644 --- a/hw/timer/m48t59.c +++ b/hw/timer/m48t59.c @@ -493,66 +493,29 @@ static uint64_t NVRAM_readb(void *opaque, hwaddr addr, unsigned size) return retval; } -static void nvram_writeb (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, value & 0xff); -} - -static void nvram_writew (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, (value >> 8) & 0xff); - m48t59_write(NVRAM, addr + 1, value & 0xff); -} - -static void nvram_writel (void *opaque, hwaddr addr, uint32_t value) -{ - M48t59State *NVRAM = opaque; - - m48t59_write(NVRAM, addr, (value >> 24) & 0xff); - m48t59_write(NVRAM, addr + 1, (value >> 16) & 0xff); - m48t59_write(NVRAM, addr + 2, (value >> 8) & 0xff); - m48t59_write(NVRAM, addr + 3, value & 0xff); -} - -static uint32_t nvram_readb (void *opaque, hwaddr addr) +static uint64_t nvram_read(void *opaque, hwaddr addr, unsigned size) { M48t59State *NVRAM = opaque; return m48t59_read(NVRAM, addr); } -static uint32_t nvram_readw (void *opaque, hwaddr addr) +static void nvram_write(void *opaque, hwaddr addr, uint64_t value, + unsigned size) { M48t59State *NVRAM = opaque; - uint32_t retval; - retval = m48t59_read(NVRAM, addr) << 8; - retval |= m48t59_read(NVRAM, addr + 1); - return retval; -} - -static uint32_t nvram_readl (void *opaque, hwaddr addr) -{ - M48t59State *NVRAM = opaque; - uint32_t retval; - - retval = m48t59_read(NVRAM, addr) << 24; - retval |= m48t59_read(NVRAM, addr + 1) << 16; - retval |= m48t59_read(NVRAM, addr + 2) << 8; - retval |= m48t59_read(NVRAM, addr + 3); - return retval; + return m48t59_write(NVRAM, addr, value); } static const MemoryRegionOps nvram_ops = { - .old_mmio = { - .read = { nvram_readb, nvram_readw, nvram_readl, }, - .write = { nvram_writeb, nvram_writew, nvram_writel, }, - }, - .endianness = DEVICE_NATIVE_ENDIAN, + .read = nvram_read, + .write = nvram_write, + .impl.min_access_size = 1, + .impl.max_access_size = 1, + .valid.min_access_size = 1, + .valid.max_access_size = 4, + .endianness = DEVICE_BIG_ENDIAN, }; static const VMStateDescription vmstate_m48t59 = { diff --git a/hw/timer/mc146818rtc.c b/hw/timer/mc146818rtc.c index ae9d03ed02..a87265d605 100644 --- a/hw/timer/mc146818rtc.c +++ b/hw/timer/mc146818rtc.c @@ -31,9 +31,10 @@ #include "sysemu/replay.h" #include "hw/timer/mc146818rtc.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc.h" -#include "qapi/qapi-events-misc.h" +#include "qapi/qapi-commands-target.h" +#include "qapi/qapi-events-target.h" #include "qapi/visitor.h" +#include "exec/address-spaces.h" #ifdef TARGET_I386 #include "hw/i386/apic.h" @@ -70,6 +71,7 @@ typedef struct RTCState { ISADevice parent_obj; MemoryRegion io; + MemoryRegion coalesced_io; uint8_t cmos_data[256]; uint8_t cmos_index; int32_t base_year; @@ -120,7 +122,7 @@ static void rtc_coalesced_timer_update(RTCState *s) timer_del(s->coalesced_timer); } else { /* divide each RTC interval to 2 - 8 smaller intervals */ - int c = MIN(s->irq_coalesced, 7) + 1; + int c = MIN(s->irq_coalesced, 7) + 1; int64_t next_clock = qemu_clock_get_ns(rtc_clock) + periodic_clock_to_ns(s->period / c); timer_mod(s->coalesced_timer, next_clock); @@ -453,7 +455,7 @@ static void rtc_update_timer(void *opaque) if (qemu_clock_get_ns(rtc_clock) >= s->next_alarm_time) { irqs |= REG_C_AF; if (s->cmos_data[RTC_REG_B] & REG_B_AIE) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_RTC, NULL); } } @@ -645,7 +647,7 @@ static void rtc_set_time(RTCState *s) s->base_rtc = mktimegm(&tm); s->last_update = qemu_clock_get_ns(rtc_clock); - qapi_event_send_rtc_change(qemu_timedate_diff(&tm), &error_abort); + qapi_event_send_rtc_change(qemu_timedate_diff(&tm)); } static void rtc_set_cmos(RTCState *s, const struct tm *tm) @@ -933,7 +935,7 @@ static void rtc_reset(void *opaque) if (s->lost_tick_policy == LOST_TICK_POLICY_SLEW) { s->irq_coalesced = 0; - s->irq_reinject_on_ack_count = 0; + s->irq_reinject_on_ack_count = 0; } } @@ -1008,14 +1010,18 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) memory_region_init_io(&s->io, OBJECT(s), &cmos_ops, s, "rtc", 4); isa_register_ioport(isadev, &s->io, base); + /* register rtc 0x70 port for coalesced_pio */ + memory_region_set_flush_coalesced(&s->io); + memory_region_init_io(&s->coalesced_io, OBJECT(s), &cmos_ops, + s, "rtc-index", 1); + memory_region_add_subregion(&s->io, 0, &s->coalesced_io); + memory_region_add_coalescing(&s->coalesced_io, 0, 1); + qdev_set_legacy_instance_id(dev, base, 3); qemu_register_reset(rtc_reset, s); object_property_add_tm(OBJECT(s), "date", rtc_get_date, NULL); - object_property_add_alias(qdev_get_machine(), "rtc-time", - OBJECT(s), "date", NULL); - qdev_init_gpio_out(dev, &s->irq, 1); } @@ -1037,6 +1043,9 @@ ISADevice *mc146818_rtc_init(ISABus *bus, int base_year, qemu_irq intercept_irq) } QLIST_INSERT_HEAD(&rtc_devices, s, link); + object_property_add_alias(qdev_get_machine(), "rtc-time", OBJECT(s), + "date", NULL); + return isadev; } @@ -1070,17 +1079,11 @@ static void rtc_class_initfn(ObjectClass *klass, void *data) dc->user_creatable = false; } -static void rtc_finalize(Object *obj) -{ - object_property_del(qdev_get_machine(), "rtc", NULL); -} - static const TypeInfo mc146818rtc_info = { .name = TYPE_MC146818_RTC, .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(RTCState), .class_init = rtc_class_initfn, - .instance_finalize = rtc_finalize, }; static void mc146818rtc_register_types(void) diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c new file mode 100644 index 0000000000..0c90662896 --- /dev/null +++ b/hw/timer/nrf51_timer.c @@ -0,0 +1,393 @@ +/* + * nRF51 System-on-Chip Timer peripheral + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Copyright 2018 Steffen Görtz + * Copyright (c) 2019 Red Hat, Inc. + * + * 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 "qemu/log.h" +#include "hw/arm/nrf51.h" +#include "hw/timer/nrf51_timer.h" +#include "trace.h" + +#define TIMER_CLK_FREQ 16000000UL + +static uint32_t const bitwidths[] = {16, 8, 24, 32}; + +static uint32_t ns_to_ticks(NRF51TimerState *s, int64_t ns) +{ + uint32_t freq = TIMER_CLK_FREQ >> s->prescaler; + + return muldiv64(ns, freq, NANOSECONDS_PER_SECOND); +} + +static int64_t ticks_to_ns(NRF51TimerState *s, uint32_t ticks) +{ + uint32_t freq = TIMER_CLK_FREQ >> s->prescaler; + + return muldiv64(ticks, NANOSECONDS_PER_SECOND, freq); +} + +/* Returns number of ticks since last call */ +static uint32_t update_counter(NRF51TimerState *s, int64_t now) +{ + uint32_t ticks = ns_to_ticks(s, now - s->update_counter_ns); + + s->counter = (s->counter + ticks) % BIT(bitwidths[s->bitmode]); + s->update_counter_ns = now; + return ticks; +} + +/* Assumes s->counter is up-to-date */ +static void rearm_timer(NRF51TimerState *s, int64_t now) +{ + int64_t min_ns = INT64_MAX; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + int64_t delta_ns; + + if (s->events_compare[i]) { + continue; /* already expired, ignore it for now */ + } + + if (s->cc[i] <= s->counter) { + delta_ns = ticks_to_ns(s, BIT(bitwidths[s->bitmode]) - + s->counter + s->cc[i]); + } else { + delta_ns = ticks_to_ns(s, s->cc[i] - s->counter); + } + + if (delta_ns < min_ns) { + min_ns = delta_ns; + } + } + + if (min_ns != INT64_MAX) { + timer_mod_ns(&s->timer, now + min_ns); + } +} + +static void update_irq(NRF51TimerState *s) +{ + bool flag = false; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + flag |= s->events_compare[i] && extract32(s->inten, 16 + i, 1); + } + qemu_set_irq(s->irq, flag); +} + +static void timer_expire(void *opaque) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + uint32_t cc_remaining[NRF51_TIMER_REG_COUNT]; + bool should_stop = false; + uint32_t ticks; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + if (s->cc[i] > s->counter) { + cc_remaining[i] = s->cc[i] - s->counter; + } else { + cc_remaining[i] = BIT(bitwidths[s->bitmode]) - + s->counter + s->cc[i]; + } + } + + ticks = update_counter(s, now); + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + if (cc_remaining[i] <= ticks) { + s->events_compare[i] = 1; + + if (s->shorts & BIT(i)) { + s->timer_start_ns = now; + s->update_counter_ns = s->timer_start_ns; + s->counter = 0; + } + + should_stop |= s->shorts & BIT(i + 8); + } + } + + update_irq(s); + + if (should_stop) { + s->running = false; + timer_del(&s->timer); + } else { + rearm_timer(s, now); + } +} + +static void counter_compare(NRF51TimerState *s) +{ + uint32_t counter = s->counter; + size_t i; + + for (i = 0; i < NRF51_TIMER_REG_COUNT; i++) { + if (counter == s->cc[i]) { + s->events_compare[i] = 1; + + if (s->shorts & BIT(i)) { + s->counter = 0; + } + } + } +} + +static uint64_t nrf51_timer_read(void *opaque, hwaddr offset, unsigned int size) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + uint64_t r = 0; + + switch (offset) { + case NRF51_TIMER_EVENT_COMPARE_0 ... NRF51_TIMER_EVENT_COMPARE_3: + r = s->events_compare[(offset - NRF51_TIMER_EVENT_COMPARE_0) / 4]; + break; + case NRF51_TIMER_REG_SHORTS: + r = s->shorts; + break; + case NRF51_TIMER_REG_INTENSET: + r = s->inten; + break; + case NRF51_TIMER_REG_INTENCLR: + r = s->inten; + break; + case NRF51_TIMER_REG_MODE: + r = s->mode; + break; + case NRF51_TIMER_REG_BITMODE: + r = s->bitmode; + break; + case NRF51_TIMER_REG_PRESCALER: + r = s->prescaler; + break; + case NRF51_TIMER_REG_CC0 ... NRF51_TIMER_REG_CC3: + r = s->cc[(offset - NRF51_TIMER_REG_CC0) / 4]; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad read offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + trace_nrf51_timer_read(offset, r, size); + + return r; +} + +static void nrf51_timer_write(void *opaque, hwaddr offset, + uint64_t value, unsigned int size) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + size_t idx; + + trace_nrf51_timer_write(offset, value, size); + + switch (offset) { + case NRF51_TIMER_TASK_START: + if (value == NRF51_TRIGGER_TASK && s->mode == NRF51_TIMER_TIMER) { + s->running = true; + s->timer_start_ns = now - ticks_to_ns(s, s->counter); + s->update_counter_ns = s->timer_start_ns; + rearm_timer(s, now); + } + break; + case NRF51_TIMER_TASK_STOP: + case NRF51_TIMER_TASK_SHUTDOWN: + if (value == NRF51_TRIGGER_TASK) { + s->running = false; + timer_del(&s->timer); + } + break; + case NRF51_TIMER_TASK_COUNT: + if (value == NRF51_TRIGGER_TASK && s->mode == NRF51_TIMER_COUNTER) { + s->counter = (s->counter + 1) % BIT(bitwidths[s->bitmode]); + counter_compare(s); + } + break; + case NRF51_TIMER_TASK_CLEAR: + if (value == NRF51_TRIGGER_TASK) { + s->timer_start_ns = now; + s->update_counter_ns = s->timer_start_ns; + s->counter = 0; + if (s->running) { + rearm_timer(s, now); + } + } + break; + case NRF51_TIMER_TASK_CAPTURE_0 ... NRF51_TIMER_TASK_CAPTURE_3: + if (value == NRF51_TRIGGER_TASK) { + if (s->running) { + timer_expire(s); /* update counter and all state */ + } + + idx = (offset - NRF51_TIMER_TASK_CAPTURE_0) / 4; + s->cc[idx] = s->counter; + } + break; + case NRF51_TIMER_EVENT_COMPARE_0 ... NRF51_TIMER_EVENT_COMPARE_3: + if (value == NRF51_EVENT_CLEAR) { + s->events_compare[(offset - NRF51_TIMER_EVENT_COMPARE_0) / 4] = 0; + + if (s->running) { + timer_expire(s); /* update counter and all state */ + } + } + break; + case NRF51_TIMER_REG_SHORTS: + s->shorts = value & NRF51_TIMER_REG_SHORTS_MASK; + break; + case NRF51_TIMER_REG_INTENSET: + s->inten |= value & NRF51_TIMER_REG_INTEN_MASK; + break; + case NRF51_TIMER_REG_INTENCLR: + s->inten &= ~(value & NRF51_TIMER_REG_INTEN_MASK); + break; + case NRF51_TIMER_REG_MODE: + s->mode = value; + break; + case NRF51_TIMER_REG_BITMODE: + if (s->mode == NRF51_TIMER_TIMER && s->running) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: erroneous change of BITMODE while timer is running\n", + __func__); + } + s->bitmode = value & NRF51_TIMER_REG_BITMODE_MASK; + break; + case NRF51_TIMER_REG_PRESCALER: + if (s->mode == NRF51_TIMER_TIMER && s->running) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: erroneous change of PRESCALER while timer is running\n", + __func__); + } + s->prescaler = value & NRF51_TIMER_REG_PRESCALER_MASK; + break; + case NRF51_TIMER_REG_CC0 ... NRF51_TIMER_REG_CC3: + if (s->running) { + timer_expire(s); /* update counter */ + } + + idx = (offset - NRF51_TIMER_REG_CC0) / 4; + s->cc[idx] = value % BIT(bitwidths[s->bitmode]); + + if (s->running) { + rearm_timer(s, now); + } + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: bad write offset 0x%" HWADDR_PRIx "\n", + __func__, offset); + } + + update_irq(s); +} + +static const MemoryRegionOps rng_ops = { + .read = nrf51_timer_read, + .write = nrf51_timer_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, + .impl.max_access_size = 4, +}; + +static void nrf51_timer_init(Object *obj) +{ + NRF51TimerState *s = NRF51_TIMER(obj); + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + + memory_region_init_io(&s->iomem, obj, &rng_ops, s, + TYPE_NRF51_TIMER, NRF51_TIMER_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->irq); + + timer_init_ns(&s->timer, QEMU_CLOCK_VIRTUAL, timer_expire, s); +} + +static void nrf51_timer_reset(DeviceState *dev) +{ + NRF51TimerState *s = NRF51_TIMER(dev); + + timer_del(&s->timer); + s->timer_start_ns = 0x00; + s->update_counter_ns = 0x00; + s->counter = 0x00; + s->running = false; + + memset(s->events_compare, 0x00, sizeof(s->events_compare)); + memset(s->cc, 0x00, sizeof(s->cc)); + + s->shorts = 0x00; + s->inten = 0x00; + s->mode = 0x00; + s->bitmode = 0x00; + s->prescaler = 0x00; +} + +static int nrf51_timer_post_load(void *opaque, int version_id) +{ + NRF51TimerState *s = NRF51_TIMER(opaque); + + if (s->running && s->mode == NRF51_TIMER_TIMER) { + timer_expire(s); + } + return 0; +} + +static const VMStateDescription vmstate_nrf51_timer = { + .name = TYPE_NRF51_TIMER, + .version_id = 1, + .post_load = nrf51_timer_post_load, + .fields = (VMStateField[]) { + VMSTATE_TIMER(timer, NRF51TimerState), + VMSTATE_INT64(timer_start_ns, NRF51TimerState), + VMSTATE_INT64(update_counter_ns, NRF51TimerState), + VMSTATE_UINT32(counter, NRF51TimerState), + VMSTATE_BOOL(running, NRF51TimerState), + VMSTATE_UINT8_ARRAY(events_compare, NRF51TimerState, + NRF51_TIMER_REG_COUNT), + VMSTATE_UINT32_ARRAY(cc, NRF51TimerState, NRF51_TIMER_REG_COUNT), + VMSTATE_UINT32(shorts, NRF51TimerState), + VMSTATE_UINT32(inten, NRF51TimerState), + VMSTATE_UINT32(mode, NRF51TimerState), + VMSTATE_UINT32(bitmode, NRF51TimerState), + VMSTATE_UINT32(prescaler, NRF51TimerState), + VMSTATE_END_OF_LIST() + } +}; + +static void nrf51_timer_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->reset = nrf51_timer_reset; + dc->vmsd = &vmstate_nrf51_timer; +} + +static const TypeInfo nrf51_timer_info = { + .name = TYPE_NRF51_TIMER, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(NRF51TimerState), + .instance_init = nrf51_timer_init, + .class_init = nrf51_timer_class_init +}; + +static void nrf51_timer_register_types(void) +{ + type_register_static(&nrf51_timer_info); +} + +type_init(nrf51_timer_register_types) diff --git a/hw/timer/pl031.c b/hw/timer/pl031.c index d3aacce80d..274ad47a33 100644 --- a/hw/timer/pl031.c +++ b/hw/timer/pl031.c @@ -12,20 +12,13 @@ */ #include "qemu/osdep.h" +#include "hw/timer/pl031.h" #include "hw/sysbus.h" #include "qemu/timer.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "qemu/log.h" - -//#define DEBUG_PL031 - -#ifdef DEBUG_PL031 -#define DPRINTF(fmt, ...) \ -do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while(0) -#endif +#include "trace.h" #define RTC_DR 0x00 /* Data read register */ #define RTC_MR 0x04 /* Match register */ @@ -36,30 +29,6 @@ do { printf("pl031: " fmt , ## __VA_ARGS__); } while (0) #define RTC_MIS 0x18 /* Masked interrupt status register */ #define RTC_ICR 0x1c /* Interrupt clear register */ -#define TYPE_PL031 "pl031" -#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031) - -typedef struct PL031State { - SysBusDevice parent_obj; - - MemoryRegion iomem; - QEMUTimer *timer; - qemu_irq irq; - - /* Needed to preserve the tick_count across migration, even if the - * absolute value of the rtc_clock is different on the source and - * destination. - */ - uint32_t tick_offset_vmstate; - uint32_t tick_offset; - - uint32_t mr; - uint32_t lr; - uint32_t cr; - uint32_t im; - uint32_t is; -} PL031State; - static const unsigned char pl031_id[] = { 0x31, 0x10, 0x14, 0x00, /* Device ID */ 0x0d, 0xf0, 0x05, 0xb1 /* Cell ID */ @@ -67,7 +36,10 @@ static const unsigned char pl031_id[] = { static void pl031_update(PL031State *s) { - qemu_set_irq(s->irq, s->is & s->im); + uint32_t flags = s->is & s->im; + + trace_pl031_irq_state(flags); + qemu_set_irq(s->irq, flags); } static void pl031_interrupt(void * opaque) @@ -75,7 +47,7 @@ static void pl031_interrupt(void * opaque) PL031State *s = (PL031State *)opaque; s->is = 1; - DPRINTF("Alarm raised\n"); + trace_pl031_alarm_raised(); pl031_update(s); } @@ -92,7 +64,7 @@ static void pl031_set_alarm(PL031State *s) /* The timer wraps around. This subtraction also wraps in the same way, and gives correct results when alarm < now_ticks. */ ticks = s->mr - pl031_get_count(s); - DPRINTF("Alarm set in %ud ticks\n", ticks); + trace_pl031_set_alarm(ticks); if (ticks == 0) { timer_del(s->timer); pl031_interrupt(s); @@ -106,38 +78,49 @@ static uint64_t pl031_read(void *opaque, hwaddr offset, unsigned size) { PL031State *s = (PL031State *)opaque; - - if (offset >= 0xfe0 && offset < 0x1000) - return pl031_id[(offset - 0xfe0) >> 2]; + uint64_t r; switch (offset) { case RTC_DR: - return pl031_get_count(s); + r = pl031_get_count(s); + break; case RTC_MR: - return s->mr; + r = s->mr; + break; case RTC_IMSC: - return s->im; + r = s->im; + break; case RTC_RIS: - return s->is; + r = s->is; + break; case RTC_LR: - return s->lr; + r = s->lr; + break; case RTC_CR: /* RTC is permanently enabled. */ - return 1; + r = 1; + break; case RTC_MIS: - return s->is & s->im; + r = s->is & s->im; + break; + case 0xfe0 ... 0xfff: + r = pl031_id[(offset - 0xfe0) >> 2]; + break; case RTC_ICR: qemu_log_mask(LOG_GUEST_ERROR, "pl031: read of write-only register at offset 0x%x\n", (int)offset); + r = 0; break; default: qemu_log_mask(LOG_GUEST_ERROR, "pl031_read: Bad offset 0x%x\n", (int)offset); + r = 0; break; } - return 0; + trace_pl031_read(offset, r); + return r; } static void pl031_write(void * opaque, hwaddr offset, @@ -145,6 +128,7 @@ static void pl031_write(void * opaque, hwaddr offset, { PL031State *s = (PL031State *)opaque; + trace_pl031_write(offset, value); switch (offset) { case RTC_LR: @@ -157,7 +141,6 @@ static void pl031_write(void * opaque, hwaddr offset, break; case RTC_IMSC: s->im = value & 1; - DPRINTF("Interrupt mask %d\n", s->im); pl031_update(s); break; case RTC_ICR: @@ -165,7 +148,6 @@ static void pl031_write(void * opaque, hwaddr offset, cleared when bit 0 of the written value is set. However the arm926e documentation (DDI0287B) states that the interrupt is cleared when any value is written. */ - DPRINTF("Interrupt cleared"); s->is = 0; pl031_update(s); break; diff --git a/hw/timer/puv3_ost.c b/hw/timer/puv3_ost.c index 0b3d717e60..3be58c7fdd 100644 --- a/hw/timer/puv3_ost.c +++ b/hw/timer/puv3_ost.c @@ -113,16 +113,17 @@ static void puv3_ost_tick(void *opaque) } } -static int puv3_ost_init(SysBusDevice *dev) +static void puv3_ost_realize(DeviceState *dev, Error **errp) { PUV3OSTState *s = PUV3_OST(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); s->reg_OIER = 0; s->reg_OSSR = 0; s->reg_OSMR0 = 0; s->reg_OSCR = 0; - sysbus_init_irq(dev, &s->irq); + sysbus_init_irq(sbd, &s->irq); s->bh = qemu_bh_new(puv3_ost_tick, s); s->ptimer = ptimer_init(s->bh, PTIMER_POLICY_DEFAULT); @@ -130,16 +131,14 @@ static int puv3_ost_init(SysBusDevice *dev) memory_region_init_io(&s->iomem, OBJECT(s), &puv3_ost_ops, s, "puv3_ost", PUV3_REGS_OFFSET); - sysbus_init_mmio(dev, &s->iomem); - - return 0; + sysbus_init_mmio(sbd, &s->iomem); } static void puv3_ost_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - sdc->init = puv3_ost_init; + dc->realize = puv3_ost_realize; } static const TypeInfo puv3_ost_info = { diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 5f8736cf10..91b18ba312 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -74,6 +74,7 @@ static uint32_t sh_timer_read(void *opaque, hwaddr offset) case OFFSET_TCPR: if (s->feat & TIMER_FEAT_CAPT) return s->tcpr; + /* fall through */ default: hw_error("sh_timer_read: Bad offset %x\n", (int)offset); return 0; diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 58fc7b1188..ae744d1642 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -308,7 +308,7 @@ static void stm32f2xx_timer_init(Object *obj) sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); memory_region_init_io(&s->iomem, obj, &stm32f2xx_timer_ops, s, - "stm32f2xx_timer", 0x4000); + "stm32f2xx_timer", 0x400); sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->iomem); s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); diff --git a/hw/timer/sun4v-rtc.c b/hw/timer/sun4v-rtc.c index 310523225f..b93cbd6a81 100644 --- a/hw/timer/sun4v-rtc.c +++ b/hw/timer/sun4v-rtc.c @@ -14,15 +14,8 @@ #include "hw/sysbus.h" #include "qemu/timer.h" #include "hw/timer/sun4v-rtc.h" +#include "trace.h" -//#define DEBUG_SUN4V_RTC - -#ifdef DEBUG_SUN4V_RTC -#define DPRINTF(fmt, ...) \ - do { printf("sun4v_rtc: " fmt , ## __VA_ARGS__); } while (0) -#else -#define DPRINTF(fmt, ...) do {} while (0) -#endif #define TYPE_SUN4V_RTC "sun4v_rtc" #define SUN4V_RTC(obj) OBJECT_CHECK(Sun4vRtc, (obj), TYPE_SUN4V_RTC) @@ -41,14 +34,14 @@ static uint64_t sun4v_rtc_read(void *opaque, hwaddr addr, /* accessing the high 32 bits */ val >>= 32; } - DPRINTF("read from " TARGET_FMT_plx " val %lx\n", addr, val); + trace_sun4v_rtc_read(addr, val); return val; } static void sun4v_rtc_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - DPRINTF("write 0x%x to " TARGET_FMT_plx "\n", (unsigned)val, addr); + trace_sun4v_rtc_write(addr, val); } static const MemoryRegionOps sun4v_rtc_ops = { @@ -70,21 +63,21 @@ void sun4v_rtc_init(hwaddr addr) sysbus_mmio_map(s, 0, addr); } -static int sun4v_rtc_init1(SysBusDevice *dev) +static void sun4v_rtc_realize(DeviceState *dev, Error **errp) { + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); Sun4vRtc *s = SUN4V_RTC(dev); memory_region_init_io(&s->iomem, OBJECT(s), &sun4v_rtc_ops, s, "sun4v-rtc", 0x08ULL); - sysbus_init_mmio(dev, &s->iomem); - return 0; + sysbus_init_mmio(sbd, &s->iomem); } static void sun4v_rtc_class_init(ObjectClass *klass, void *data) { - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); - k->init = sun4v_rtc_init1; + dc->realize = sun4v_rtc_realize; } static const TypeInfo sun4v_rtc_info = { diff --git a/hw/timer/trace-events b/hw/timer/trace-events index e6e042fddb..dcaf3d6da6 100644 --- a/hw/timer/trace-events +++ b/hw/timer/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/timer/slavio_timer.c +# slavio_timer.c slavio_timer_get_out(uint64_t limit, uint32_t counthigh, uint32_t count) "limit 0x%"PRIx64" count 0x%x0x%08x" slavio_timer_irq(uint32_t counthigh, uint32_t count) "callback: count 0x%x0x%08x" slavio_timer_mem_readl_invalid(uint64_t addr) "invalid read address 0x%"PRIx64 @@ -15,7 +15,7 @@ slavio_timer_mem_writel_mode_counter(unsigned int timer_index) "processor %d cha slavio_timer_mem_writel_mode_invalid(void) "not system timer" slavio_timer_mem_writel_invalid(uint64_t addr) "invalid write address 0x%"PRIx64 -# hw/timer/grlib_gptimer.c +# grlib_gptimer.c grlib_gptimer_enable(int id, uint32_t count) "timer:%d set count 0x%x and run" grlib_gptimer_disabled(int id, uint32_t config) "timer:%d Timer disable config 0x%x" grlib_gptimer_restart(int id, uint32_t reload) "timer:%d reload val: 0x%x" @@ -24,13 +24,13 @@ grlib_gptimer_hit(int id) "timer:%d HIT" grlib_gptimer_readl(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" grlib_gptimer_writel(int id, uint64_t addr, uint32_t val) "timer:%d addr 0x%"PRIx64" 0x%x" -# hw/timer/lm32_timer.c +# lm32_timer.c lm32_timer_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" lm32_timer_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" lm32_timer_hit(void) "timer hit" lm32_timer_irq_state(int level) "irq state %d" -# hw/timer/milkymist-sysctl.c +# milkymist-sysctl.c milkymist_sysctl_memory_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_sysctl_memory_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" milkymist_sysctl_icap_write(uint32_t value) "value 0x%08x" @@ -41,7 +41,7 @@ milkymist_sysctl_stop_timer1(void) "Stop timer1" milkymist_sysctl_pulse_irq_timer0(void) "Pulse IRQ Timer0" milkymist_sysctl_pulse_irq_timer1(void) "Pulse IRQ Timer1" -# hw/timer/aspeed_timer.c +# 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" @@ -50,16 +50,36 @@ 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/timer/armv7m_systick.c +# armv7m_systick.c systick_reload(void) "systick reload" systick_timer_tick(void) "systick reload" systick_read(uint64_t addr, uint32_t value, unsigned size) "systick read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" systick_write(uint64_t addr, uint32_t value, unsigned size) "systick write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" -# hw/char/cmsdk_apb_timer.c +# cmsdk-apb-timer.c cmsdk_apb_timer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_timer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB timer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" cmsdk_apb_timer_reset(void) "CMSDK APB timer: reset" -# hw/timer/xlnx-zynqmp-rtc.c +# cmsdk-apb-dualtimer.c +cmsdk_apb_dualtimer_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_dualtimer_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB dualtimer write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_dualtimer_reset(void) "CMSDK APB dualtimer: reset" + +# sun4v-rtc.c +sun4v_rtc_read(uint64_t addr, uint64_t value) "read: addr 0x%" PRIx64 " value 0x%" PRIx64 +sun4v_rtc_write(uint64_t addr, uint64_t value) "write: addr 0x%" PRIx64 " value 0x%" PRIx64 + +# xlnx-zynqmp-rtc.c xlnx_zynqmp_rtc_gettime(int year, int month, int day, int hour, int min, int sec) "Get time from host: %d-%d-%d %2d:%02d:%02d" + +# nrf51_timer.c +nrf51_timer_read(uint64_t addr, uint32_t value, unsigned size) "read addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" +nrf51_timer_write(uint64_t addr, uint32_t value, unsigned size) "write addr 0x%" PRIx64 " data 0x%" PRIx32 " size %u" + +# pl031.c +pl031_irq_state(int level) "irq state %d" +pl031_read(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +pl031_write(uint32_t addr, uint32_t value) "addr 0x%08x value 0x%08x" +pl031_alarm_raised(void) "alarm raised" +pl031_set_alarm(uint32_t ticks) "alarm set for %u ticks" diff --git a/hw/timer/twl92230.c b/hw/timer/twl92230.c index 3b43b46199..c83d803dd8 100644 --- a/hw/timer/twl92230.c +++ b/hw/timer/twl92230.c @@ -737,7 +737,7 @@ static int menelaus_tx(I2CSlave *i2c, uint8_t data) return 0; } -static int menelaus_rx(I2CSlave *i2c) +static uint8_t menelaus_rx(I2CSlave *i2c) { MenelausState *s = TWL92230(i2c); @@ -750,7 +750,7 @@ static int menelaus_rx(I2CSlave *i2c) */ static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { int *v = pv; *v = qemu_get_be16(f); @@ -758,7 +758,7 @@ static int get_int32_as_uint16(QEMUFile *f, void *pv, size_t size, } static int put_int32_as_uint16(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { int *v = pv; qemu_put_be16(f, *v); diff --git a/hw/tpm/Kconfig b/hw/tpm/Kconfig new file mode 100644 index 0000000000..4c8ee87d67 --- /dev/null +++ b/hw/tpm/Kconfig @@ -0,0 +1,24 @@ +config TPMDEV + bool + depends on TPM + +config TPM_TIS + bool + depends on TPM && ISA_BUS + select TPMDEV + +config TPM_CRB + bool + depends on TPM && PC + select TPMDEV + +config TPM_PASSTHROUGH + bool + default y + # FIXME: should check for x86 host as well + depends on TPMDEV && LINUX + +config TPM_EMULATOR + bool + default y + depends on TPMDEV diff --git a/hw/tpm/Makefile.objs b/hw/tpm/Makefile.objs index 1dc9f8bf2c..700c878622 100644 --- a/hw/tpm/Makefile.objs +++ b/hw/tpm/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += tpm_util.o +obj-y += tpm_ppi.o common-obj-$(CONFIG_TPM_TIS) += tpm_tis.o common-obj-$(CONFIG_TPM_CRB) += tpm_crb.o common-obj-$(CONFIG_TPM_PASSTHROUGH) += tpm_passthrough.o diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index a92dd50437..3087acc4ab 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -29,6 +29,7 @@ #include "sysemu/reset.h" #include "tpm_int.h" #include "tpm_util.h" +#include "tpm_ppi.h" #include "trace.h" typedef struct CRBState { @@ -41,6 +42,9 @@ typedef struct CRBState { MemoryRegion cmdmem; size_t be_buffer_size; + + bool ppi_enabled; + TPMPPI ppi; } CRBState; #define CRB(obj) OBJECT_CHECK(CRBState, (obj), TYPE_TPM_CRB) @@ -221,6 +225,7 @@ static const VMStateDescription vmstate_tpm_crb = { static Property tpm_crb_properties[] = { DEFINE_PROP_TPMBE("tpmdev", CRBState, tpmbe), + DEFINE_PROP_BOOL("ppi", CRBState, ppi_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -228,6 +233,9 @@ static void tpm_crb_reset(void *dev) { CRBState *s = CRB(dev); + if (s->ppi_enabled) { + tpm_ppi_reset(&s->ppi); + } tpm_backend_reset(s->tpmbe); memset(s->regs, 0, sizeof(s->regs)); @@ -291,6 +299,11 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(get_system_memory(), TPM_CRB_ADDR_BASE + sizeof(s->regs), &s->cmdmem); + if (s->ppi_enabled) { + tpm_ppi_init(&s->ppi, get_system_memory(), + TPM_PPI_ADDR_BASE, OBJECT(s)); + } + qemu_register_reset(tpm_crb_reset, dev); } diff --git a/hw/tpm/tpm_emulator.c b/hw/tpm/tpm_emulator.c index 10bc20dbec..70f4b10284 100644 --- a/hw/tpm/tpm_emulator.c +++ b/hw/tpm/tpm_emulator.c @@ -166,6 +166,7 @@ static int tpm_emulator_set_locality(TPMEmulator *tpm_emu, uint8_t locty_number, trace_tpm_emulator_set_locality(locty_number); + memset(&loc, 0, sizeof(loc)); loc.u.req.loc = locty_number; if (tpm_emulator_ctrlcmd(tpm_emu, CMD_SET_LOCALITY, &loc, sizeof(loc), sizeof(loc)) < 0) { diff --git a/hw/tpm/tpm_ppi.c b/hw/tpm/tpm_ppi.c new file mode 100644 index 0000000000..cd8205f212 --- /dev/null +++ b/hw/tpm/tpm_ppi.c @@ -0,0 +1,53 @@ +/* + * tpm_ppi.c - TPM Physical Presence Interface + * + * Copyright (C) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * 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 "cpu.h" +#include "sysemu/memory_mapping.h" +#include "sysemu/reset.h" +#include "migration/vmstate.h" +#include "tpm_ppi.h" +#include "trace.h" + +void tpm_ppi_reset(TPMPPI *tpmppi) +{ + if (tpmppi->buf[0x15a /* movv, docs/specs/tpm.txt */] & 0x1) { + GuestPhysBlockList guest_phys_blocks; + GuestPhysBlock *block; + + guest_phys_blocks_init(&guest_phys_blocks); + guest_phys_blocks_append(&guest_phys_blocks); + QTAILQ_FOREACH(block, &guest_phys_blocks.head, next) { + trace_tpm_ppi_memset(block->host_addr, + block->target_end - block->target_start); + memset(block->host_addr, 0, + block->target_end - block->target_start); + memory_region_set_dirty(block->mr, 0, + block->target_end - block->target_start); + } + guest_phys_blocks_free(&guest_phys_blocks); + } +} + +void tpm_ppi_init(TPMPPI *tpmppi, struct MemoryRegion *m, + hwaddr addr, Object *obj) +{ + tpmppi->buf = g_malloc0(HOST_PAGE_ALIGN(TPM_PPI_ADDR_SIZE)); + memory_region_init_ram_device_ptr(&tpmppi->ram, obj, "tpm-ppi", + TPM_PPI_ADDR_SIZE, tpmppi->buf); + vmstate_register_ram(&tpmppi->ram, DEVICE(obj)); + + memory_region_add_subregion(m, addr, &tpmppi->ram); +} diff --git a/hw/tpm/tpm_ppi.h b/hw/tpm/tpm_ppi.h new file mode 100644 index 0000000000..d33ef27de6 --- /dev/null +++ b/hw/tpm/tpm_ppi.h @@ -0,0 +1,46 @@ +/* + * TPM Physical Presence Interface + * + * Copyright (C) 2018 IBM Corporation + * + * Authors: + * Stefan Berger + * + * 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 TPM_TPM_PPI_H +#define TPM_TPM_PPI_H + +#include "hw/acpi/tpm.h" +#include "exec/address-spaces.h" + +typedef struct TPMPPI { + MemoryRegion ram; + uint8_t *buf; +} TPMPPI; + +/** + * tpm_ppi_init: + * @tpmppi: a TPMPPI + * @m: the address-space / MemoryRegion to use + * @addr: the address of the PPI region + * @obj: the owner object + * + * Register the TPM PPI memory region at @addr on the given address + * space for the object @obj. + **/ +void tpm_ppi_init(TPMPPI *tpmppi, struct MemoryRegion *m, + hwaddr addr, Object *obj); + +/** + * tpm_ppi_reset: + * @tpmppi: a TPMPPI + * + * Function to call on machine reset. It will check if the "Memory + * overwrite" variable is set, and perform a memory clear on volatile + * memory if requested. + **/ +void tpm_ppi_reset(TPMPPI *tpmppi); + +#endif /* TPM_TPM_PPI_H */ diff --git a/hw/tpm/tpm_tis.c b/hw/tpm/tpm_tis.c index 12f5c9a759..fd183e8deb 100644 --- a/hw/tpm/tpm_tis.c +++ b/hw/tpm/tpm_tis.c @@ -31,6 +31,7 @@ #include "sysemu/tpm_backend.h" #include "tpm_int.h" #include "tpm_util.h" +#include "tpm_ppi.h" #include "trace.h" #define TPM_TIS_NUM_LOCALITIES 5 /* per spec */ @@ -81,6 +82,9 @@ typedef struct TPMState { TPMVersion be_tpm_version; size_t be_buffer_size; + + bool ppi_enabled; + TPMPPI ppi; } TPMState; #define TPM(obj) OBJECT_CHECK(TPMState, (obj), TYPE_TPM_TIS) @@ -102,17 +106,26 @@ static uint8_t tpm_tis_locality_from_addr(hwaddr addr) static void tpm_tis_show_buffer(const unsigned char *buffer, size_t buffer_size, const char *string) { - uint32_t len, i; + size_t len, i; + char *line_buffer, *p; len = MIN(tpm_cmd_get_size(buffer), buffer_size); - printf("tpm_tis: %s length = %d\n", string, len); - for (i = 0; i < len; i++) { + + /* + * allocate enough room for 3 chars per buffer entry plus a + * newline after every 16 chars and a final null terminator. + */ + line_buffer = g_malloc(len * 3 + (len / 16) + 1); + + for (i = 0, p = line_buffer; i < len; i++) { if (i && !(i % 16)) { - printf("\n"); + p += sprintf(p, "\n"); } - printf("%.2X ", buffer[i]); + p += sprintf(p, "%.2X ", buffer[i]); } - printf("\n"); + trace_tpm_tis_show_buffer(string, len, line_buffer); + + g_free(line_buffer); } /* @@ -139,9 +152,8 @@ static void tpm_tis_sts_set(TPMLocality *l, uint32_t flags) */ static void tpm_tis_tpm_send(TPMState *s, uint8_t locty) { - if (DEBUG_TIS) { - tpm_tis_show_buffer(s->buffer, s->be_buffer_size, - "tpm_tis: To TPM"); + if (trace_event_get_state_backends(TRACE_TPM_TIS_SHOW_BUFFER)) { + tpm_tis_show_buffer(s->buffer, s->be_buffer_size, "To TPM"); } /* @@ -233,7 +245,7 @@ static void tpm_tis_new_active_locality(TPMState *s, uint8_t new_active_locty) } /* abort -- this function switches the locality */ -static void tpm_tis_abort(TPMState *s, uint8_t locty) +static void tpm_tis_abort(TPMState *s) { s->rw_offset = 0; @@ -263,7 +275,9 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) { uint8_t busy_locty; - s->aborting_locty = locty; + assert(TPM_TIS_IS_VALID_LOCTY(newlocty)); + + s->aborting_locty = locty; /* may also be TPM_TIS_NO_LOCALITY */ s->next_locty = newlocty; /* locality after successful abort */ /* @@ -281,7 +295,7 @@ static void tpm_tis_prep_abort(TPMState *s, uint8_t locty, uint8_t newlocty) } } - tpm_tis_abort(s, locty); + tpm_tis_abort(s); } /* @@ -293,9 +307,11 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret) uint8_t locty = s->cmd.locty; uint8_t l; + assert(TPM_TIS_IS_VALID_LOCTY(locty)); + if (s->cmd.selftest_done) { for (l = 0; l < TPM_TIS_NUM_LOCALITIES; l++) { - s->loc[locty].sts |= TPM_TIS_STS_SELFTEST_DONE; + s->loc[l].sts |= TPM_TIS_STS_SELFTEST_DONE; } } @@ -305,13 +321,12 @@ static void tpm_tis_request_completed(TPMIf *ti, int ret) s->loc[locty].state = TPM_TIS_STATE_COMPLETION; s->rw_offset = 0; - if (DEBUG_TIS) { - tpm_tis_show_buffer(s->buffer, s->be_buffer_size, - "tpm_tis: From TPM"); + if (trace_event_get_state_backends(TRACE_TPM_TIS_SHOW_BUFFER)) { + tpm_tis_show_buffer(s->buffer, s->be_buffer_size, "From TPM"); } if (TPM_TIS_IS_VALID_LOCTY(s->next_locty)) { - tpm_tis_abort(s, locty); + tpm_tis_abort(s); } tpm_tis_raise_irq(s, locty, @@ -616,7 +631,7 @@ static void tpm_tis_mmio_write(void *opaque, hwaddr addr, } /* cancel any seize by a lower locality */ - for (l = 0; l < locty - 1; l++) { + for (l = 0; l < locty; l++) { s->loc[l].access &= ~TPM_TIS_ACCESS_SEIZE; } @@ -864,6 +879,9 @@ static void tpm_tis_reset(DeviceState *dev) s->be_buffer_size = MIN(tpm_backend_get_buffer_size(s->be_driver), TPM_TIS_BUFFER_MAX); + if (s->ppi_enabled) { + tpm_ppi_reset(&s->ppi); + } tpm_backend_reset(s->be_driver); s->active_locty = TPM_TIS_NO_LOCALITY; @@ -950,6 +968,7 @@ static const VMStateDescription vmstate_tpm_tis = { static Property tpm_tis_properties[] = { DEFINE_PROP_UINT32("irq", TPMState, irq_num, TPM_TIS_IRQ), DEFINE_PROP_TPMBE("tpmdev", TPMState, be_driver), + DEFINE_PROP_BOOL("ppi", TPMState, ppi_enabled, true), DEFINE_PROP_END_OF_LIST(), }; @@ -976,6 +995,11 @@ static void tpm_tis_realizefn(DeviceState *dev, Error **errp) memory_region_add_subregion(isa_address_space(ISA_DEVICE(dev)), TPM_TIS_ADDR_BASE, &s->mmio); + + if (s->ppi_enabled) { + tpm_ppi_init(&s->ppi, isa_address_space(ISA_DEVICE(dev)), + TPM_PPI_ADDR_BASE, OBJECT(s)); + } } static void tpm_tis_initfn(Object *obj) diff --git a/hw/tpm/trace-events b/hw/tpm/trace-events index 25bee0cecf..0b94aa1526 100644 --- a/hw/tpm/trace-events +++ b/hw/tpm/trace-events @@ -1,21 +1,21 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/tpm/tpm_crb.c +# tpm_crb.c tpm_crb_mmio_read(uint64_t addr, unsigned size, uint32_t val) "CRB read 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 tpm_crb_mmio_write(uint64_t addr, unsigned size, uint32_t val) "CRB write 0x" TARGET_FMT_plx " len:%u val: 0x%" PRIx32 -# hw/tpm/tpm_passthrough.c +# tpm_passthrough.c tpm_passthrough_handle_request(void *cmd) "processing command %p" tpm_passthrough_reset(void) "reset" -# hw/tpm/tpm_util.c +# tpm_util.c tpm_util_get_buffer_size_hdr_len(uint32_t len, size_t expected) "tpm_resp->hdr.len = %u, expected = %zu" tpm_util_get_buffer_size_len(uint32_t len, size_t expected) "tpm_resp->len = %u, expected = %zu" tpm_util_get_buffer_size_hdr_len2(uint32_t len, size_t expected) "tpm2_resp->hdr.len = %u, expected = %zu" tpm_util_get_buffer_size_len2(uint32_t len, size_t expected) "tpm2_resp->len = %u, expected = %zu" tpm_util_get_buffer_size(size_t len) "buffersize of device: %zu" -# hw/tpm/tpm_emulator.c +# tpm_emulator.c tpm_emulator_set_locality(uint8_t locty) "setting locality to %d" tpm_emulator_handle_request(void) "processing TPM command" tpm_emulator_probe_caps(uint64_t caps) "capabilities: 0x%"PRIx64 @@ -35,7 +35,8 @@ tpm_emulator_set_state_blobs_done(void) "Done setting state blobs" tpm_emulator_pre_save(void) "" tpm_emulator_inst_init(void) "" -# hw/tpm/tpm_tis.c +# tpm_tis.c +tpm_tis_show_buffer(const char *direction, size_t len, const char *buf) "direction: %s len: %zu\nbuf: %s" tpm_tis_raise_irq(uint32_t irqmask) "Raising IRQ for flag 0x%08x" tpm_tis_new_active_locality(uint8_t locty) "Active locality is now %d" tpm_tis_abort(uint8_t locty) "New active locality is %d" @@ -51,3 +52,6 @@ tpm_tis_mmio_write_init_abort(void) "Initiating abort" tpm_tis_mmio_write_lowering_irq(void) "Lowering IRQ" tpm_tis_mmio_write_data2send(uint32_t value, unsigned size) "Data to send to TPM: 0x%08x (size=%d)" tpm_tis_pre_save(uint8_t locty, uint32_t rw_offset) "locty: %d, rw_offset = %u" + +# tpm_ppi.c +tpm_ppi_memset(uint8_t *ptr, size_t size) "memset: %p %zu" diff --git a/hw/tricore/Kconfig b/hw/tricore/Kconfig new file mode 100644 index 0000000000..9313409309 --- /dev/null +++ b/hw/tricore/Kconfig @@ -0,0 +1,2 @@ +config TRICORE + bool diff --git a/hw/tricore/Makefile.objs b/hw/tricore/Makefile.objs index 435e095cff..5501f6c1a8 100644 --- a/hw/tricore/Makefile.objs +++ b/hw/tricore/Makefile.objs @@ -1 +1 @@ -obj-y += tricore_testboard.o +obj-$(CONFIG_TRICORE) += tricore_testboard.o diff --git a/hw/tricore/tricore_testboard.c b/hw/tricore/tricore_testboard.c index a58096f05e..b40cc997d0 100644 --- a/hw/tricore/tricore_testboard.c +++ b/hw/tricore/tricore_testboard.c @@ -24,7 +24,6 @@ #include "qemu-common.h" #include "cpu.h" #include "hw/hw.h" -#include "hw/devices.h" #include "net/net.h" #include "sysemu/sysemu.h" #include "hw/boards.h" @@ -45,7 +44,7 @@ static void tricore_load_kernel(CPUTriCoreState *env) long kernel_size; kernel_size = load_elf(tricoretb_binfo.kernel_filename, NULL, - NULL, &entry, NULL, + NULL, NULL, &entry, NULL, NULL, 0, EM_TRICORE, 1, 0); if (kernel_size <= 0) { diff --git a/hw/unicore32/Kconfig b/hw/unicore32/Kconfig new file mode 100644 index 0000000000..4443a29dd2 --- /dev/null +++ b/hw/unicore32/Kconfig @@ -0,0 +1,5 @@ +config PUV3 + bool + select ISA_BUS + select PCKBD + select PTIMER diff --git a/hw/unicore32/puv3.c b/hw/unicore32/puv3.c index 830fe3face..b42e600f74 100644 --- a/hw/unicore32/puv3.c +++ b/hw/unicore32/puv3.c @@ -15,7 +15,6 @@ #include "ui/console.h" #include "hw/boards.h" #include "hw/loader.h" -#include "hw/i386/pc.h" #include "sysemu/qtest.h" #undef DEBUG_PUV3 diff --git a/hw/usb/Kconfig b/hw/usb/Kconfig new file mode 100644 index 0000000000..a1b7acb12a --- /dev/null +++ b/hw/usb/Kconfig @@ -0,0 +1,91 @@ +config USB + bool + +config USB_UHCI + bool + default y if PCI_DEVICES + depends on PCI + select USB + +config USB_OHCI + bool + default y if PCI_DEVICES + depends on PCI + select USB + +config USB_EHCI + bool + default y if PCI_DEVICES + depends on PCI + select USB + +config USB_EHCI_SYSBUS + bool + select USB + +config USB_XHCI + bool + default y if PCI_DEVICES + depends on PCI + select USB + +config USB_XHCI_NEC + bool + default y if PCI_DEVICES + depends on PCI + select USB + +config USB_MUSB + bool + select USB + +config TUSB6010 + bool + select USB_MUSB + +config USB_TABLET_WACOM + bool + default y + depends on USB + +config USB_STORAGE_BOT + bool + default y + depends on USB + select SCSI + +config USB_STORAGE_UAS + bool + default y + depends on USB + select SCSI + +config USB_AUDIO + bool + default y + depends on USB + +config USB_SERIAL + bool + default y + depends on USB + +config USB_NETWORK + bool + default y + depends on USB + +config USB_BLUETOOTH + bool + default y + depends on USB + +config USB_SMARTCARD + bool + default y + depends on USB + +config USB_STORAGE_MTP + bool + default y + depends on USB diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs index 41be700812..2b929649ac 100644 --- a/hw/usb/Makefile.objs +++ b/hw/usb/Makefile.objs @@ -6,7 +6,7 @@ common-obj-$(CONFIG_USB) += desc.o desc-msos.o common-obj-$(CONFIG_USB_UHCI) += hcd-uhci.o common-obj-$(CONFIG_USB_OHCI) += hcd-ohci.o common-obj-$(CONFIG_USB_EHCI) += hcd-ehci.o hcd-ehci-pci.o -common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci-sysbus.o +common-obj-$(CONFIG_USB_EHCI_SYSBUS) += hcd-ehci.o hcd-ehci-sysbus.o common-obj-$(CONFIG_USB_XHCI) += hcd-xhci.o common-obj-$(CONFIG_USB_XHCI_NEC) += hcd-xhci-nec.o common-obj-$(CONFIG_USB_MUSB) += hcd-musb.o diff --git a/hw/usb/bus.c b/hw/usb/bus.c index 11f7720d71..9a74dc9560 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -59,12 +59,6 @@ static int usb_device_post_load(void *opaque, int version_id) } else { dev->attached = true; } - if (dev->setup_index < 0 || - dev->setup_len < 0 || - dev->setup_index > dev->setup_len || - dev->setup_len > sizeof(dev->data_buf)) { - return -EINVAL; - } return 0; } @@ -340,8 +334,9 @@ static USBDevice *usb_try_create_simple(USBBus *bus, const char *name, } object_property_set_bool(OBJECT(dev), true, "realized", &err); if (err) { - error_propagate(errp, err); - error_prepend(errp, "Failed to initialize USB device '%s': ", name); + error_propagate_prepend(errp, err, + "Failed to initialize USB device '%s': ", + name); return NULL; } return dev; @@ -505,6 +500,10 @@ static void usb_mask_to_str(char *dest, size_t size, speeds[i].name); } } + + if (pos == 0) { + snprintf(dest, size, "unknown"); + } } void usb_check_attach(USBDevice *dev, Error **errp) diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index 5c8b3c9907..963373ba95 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -119,11 +119,11 @@ struct EmulatedState { char *db; uint8_t atr[MAX_ATR_SIZE]; uint8_t atr_length; - QSIMPLEQ_HEAD(event_list, EmulEvent) event_list; + QSIMPLEQ_HEAD(, EmulEvent) event_list; QemuMutex event_list_mutex; QemuThread event_thread_id; VReader *reader; - QSIMPLEQ_HEAD(guest_apdu_list, EmulEvent) guest_apdu_list; + QSIMPLEQ_HEAD(, EmulEvent) guest_apdu_list; QemuMutex vreader_mutex; /* and guest_apdu_list mutex */ QemuMutex handle_apdu_mutex; QemuCond handle_apdu_cond; @@ -409,6 +409,12 @@ static int init_event_notifier(EmulatedState *card, Error **errp) return 0; } +static void clean_event_notifier(EmulatedState *card) +{ + event_notifier_set_handler(&card->notifier, NULL); + event_notifier_cleanup(&card->notifier); +} + #define CERTIFICATES_DEFAULT_DB "/etc/pki/nssdb" #define CERTIFICATES_ARGS_TEMPLATE\ "db=\"%s\" use_hw=no soft=(,Virtual Reader,CAC,,%s,%s,%s)" @@ -493,7 +499,7 @@ static void emulated_realize(CCIDCardState *base, Error **errp) card->reader = NULL; card->quit_apdu_thread = 0; if (init_event_notifier(card, errp) < 0) { - return; + goto out1; } card->backend = 0; @@ -507,7 +513,7 @@ static void emulated_realize(CCIDCardState *base, Error **errp) for (ptable = backend_enum_table; ptable->name != NULL; ++ptable) { error_append_hint(errp, "%s\n", ptable->name); } - return; + goto out2; } /* TODO: a passthru backened that works on local machine. third card type?*/ @@ -517,31 +523,41 @@ static void emulated_realize(CCIDCardState *base, Error **errp) } else { error_setg(errp, "%s: you must provide all three certs for" " certificates backend", TYPE_EMULATED_CCID); - return; + goto out2; } } else { if (card->backend != BACKEND_NSS_EMULATED) { error_setg(errp, "%s: bad backend specified. The options are:%s" " (default), %s.", TYPE_EMULATED_CCID, BACKEND_NSS_EMULATED_NAME, BACKEND_CERTIFICATES_NAME); - return; + goto out2; } if (card->cert1 != NULL || card->cert2 != NULL || card->cert3 != NULL) { error_setg(errp, "%s: unexpected cert parameters to nss emulated " "backend", TYPE_EMULATED_CCID); - return; + goto out2; } /* default to mirroring the local hardware readers */ ret = wrap_vcard_emul_init(NULL); } if (ret != VCARD_EMUL_OK) { error_setg(errp, "%s: failed to initialize vcard", TYPE_EMULATED_CCID); - return; + goto out2; } qemu_thread_create(&card->event_thread_id, "ccid/event", event_thread, card, QEMU_THREAD_JOINABLE); qemu_thread_create(&card->apdu_thread_id, "ccid/apdu", handle_apdu_thread, card, QEMU_THREAD_JOINABLE); + + return; + +out2: + clean_event_notifier(card); +out1: + qemu_cond_destroy(&card->handle_apdu_cond); + qemu_mutex_destroy(&card->handle_apdu_mutex); + qemu_mutex_destroy(&card->vreader_mutex); + qemu_mutex_destroy(&card->event_list_mutex); } static void emulated_unrealize(CCIDCardState *base, Error **errp) @@ -556,6 +572,7 @@ static void emulated_unrealize(CCIDCardState *base, Error **errp) qemu_cond_signal(&card->handle_apdu_cond); qemu_thread_join(&card->apdu_thread_id); + clean_event_notifier(card); /* threads exited, can destroy all condvars/mutexes */ qemu_cond_destroy(&card->handle_apdu_cond); qemu_mutex_destroy(&card->handle_apdu_mutex); diff --git a/hw/usb/combined-packet.c b/hw/usb/combined-packet.c index 01a7ed0848..37b23e20ef 100644 --- a/hw/usb/combined-packet.c +++ b/hw/usb/combined-packet.c @@ -9,14 +9,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ #include "qemu/osdep.h" @@ -64,7 +64,7 @@ void usb_combined_input_packet_complete(USBDevice *dev, USBPacket *p) status = combined->first->status; actual_length = combined->first->actual_length; - short_not_ok = QTAILQ_LAST(&combined->packets, packets_head)->short_not_ok; + short_not_ok = QTAILQ_LAST(&combined->packets)->short_not_ok; QTAILQ_FOREACH_SAFE(p, &combined->packets, combined_entry, next) { if (!done) { diff --git a/hw/usb/core.c b/hw/usb/core.c index 241ae66b15..8fbd9c7d57 100644 --- a/hw/usb/core.c +++ b/hw/usb/core.c @@ -717,15 +717,13 @@ struct USBEndpoint *usb_ep_get(USBDevice *dev, int pid, int ep) { struct USBEndpoint *eps; - if (dev == NULL) { - return NULL; - } - eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; + assert(dev != NULL); if (ep == 0) { return &dev->ep_ctl; } assert(pid == USB_TOKEN_IN || pid == USB_TOKEN_OUT); assert(ep > 0 && ep <= USB_MAX_ENDPOINTS); + eps = (pid == USB_TOKEN_IN) ? dev->ep_in : dev->ep_out; return eps + ep - 1; } diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index ee43e4914d..c46d5eeb79 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -321,6 +321,9 @@ static int streambuf_put(struct streambuf *buf, USBPacket *p) if (!free) { return 0; } + if (p->iov.size != USBAUDIO_PACKET_SIZE) { + return 0; + } assert(free >= USBAUDIO_PACKET_SIZE); usb_packet_copy(p, buf->data + (buf->prod % buf->size), USBAUDIO_PACKET_SIZE); @@ -647,7 +650,7 @@ static void usb_audio_realize(USBDevice *dev, Error **errp) s->out.vol[1] = 240; /* 0 dB */ s->out.as.freq = USBAUDIO_SAMPLE_RATE; s->out.as.nchannels = 2; - s->out.as.fmt = AUD_FMT_S16; + s->out.as.fmt = AUDIO_FORMAT_S16; s->out.as.endianness = 0; streambuf_init(&s->out.buf, s->buffer); diff --git a/hw/usb/dev-bluetooth.c b/hw/usb/dev-bluetooth.c index eac7365b0a..c539a1afc6 100644 --- a/hw/usb/dev-bluetooth.c +++ b/hw/usb/dev-bluetooth.c @@ -46,7 +46,7 @@ struct USBBtState { struct usb_hci_out_fifo_s { uint8_t data[4096]; - int len; + int len; } outcmd, outacl, outsco; }; diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 62d18290dc..f9ea3033a1 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -61,10 +61,13 @@ enum { STR_PRODUCT_MOUSE, STR_PRODUCT_TABLET, STR_PRODUCT_KEYBOARD, - STR_SERIALNUMBER, + STR_SERIAL_COMPAT, STR_CONFIG_MOUSE, STR_CONFIG_TABLET, STR_CONFIG_KEYBOARD, + STR_SERIAL_MOUSE, + STR_SERIAL_TABLET, + STR_SERIAL_KEYBOARD, }; static const USBDescStrings desc_strings = { @@ -72,10 +75,13 @@ static const USBDescStrings desc_strings = { [STR_PRODUCT_MOUSE] = "QEMU USB Mouse", [STR_PRODUCT_TABLET] = "QEMU USB Tablet", [STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard", - [STR_SERIALNUMBER] = "42", /* == remote wakeup works */ + [STR_SERIAL_COMPAT] = "42", [STR_CONFIG_MOUSE] = "HID Mouse", [STR_CONFIG_TABLET] = "HID Tablet", [STR_CONFIG_KEYBOARD] = "HID Keyboard", + [STR_SERIAL_MOUSE] = "89126", + [STR_SERIAL_TABLET] = "28754", + [STR_SERIAL_KEYBOARD] = "68284", }; static const USBDescIface desc_iface_mouse = { @@ -375,7 +381,7 @@ static const USBDesc desc_mouse = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_MOUSE, }, .full = &desc_device_mouse, .str = desc_strings, @@ -389,7 +395,7 @@ static const USBDesc desc_mouse2 = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_MOUSE, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_MOUSE, }, .full = &desc_device_mouse, .high = &desc_device_mouse2, @@ -404,7 +410,7 @@ static const USBDesc desc_tablet = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_TABLET, }, .full = &desc_device_tablet, .str = desc_strings, @@ -418,7 +424,7 @@ static const USBDesc desc_tablet2 = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_TABLET, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_TABLET, }, .full = &desc_device_tablet, .high = &desc_device_tablet2, @@ -433,7 +439,7 @@ static const USBDesc desc_keyboard = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_KEYBOARD, }, .full = &desc_device_keyboard, .str = desc_strings, @@ -447,7 +453,7 @@ static const USBDesc desc_keyboard2 = { .bcdDevice = 0, .iManufacturer = STR_MANUFACTURER, .iProduct = STR_PRODUCT_KEYBOARD, - .iSerialNumber = STR_SERIALNUMBER, + .iSerialNumber = STR_SERIAL_KEYBOARD, }, .full = &desc_device_keyboard, .high = &desc_device_keyboard2, @@ -592,12 +598,12 @@ static void usb_hid_handle_control(USBDevice *dev, USBPacket *p, switch (value >> 8) { case 0x22: if (hs->kind == HID_MOUSE) { - memcpy(data, qemu_mouse_hid_report_descriptor, - sizeof(qemu_mouse_hid_report_descriptor)); + memcpy(data, qemu_mouse_hid_report_descriptor, + sizeof(qemu_mouse_hid_report_descriptor)); p->actual_length = sizeof(qemu_mouse_hid_report_descriptor); } else if (hs->kind == HID_TABLET) { memcpy(data, qemu_tablet_hid_report_descriptor, - sizeof(qemu_tablet_hid_report_descriptor)); + sizeof(qemu_tablet_hid_report_descriptor)); p->actual_length = sizeof(qemu_tablet_hid_report_descriptor); } else if (hs->kind == HID_KEYBOARD) { memcpy(data, qemu_keyboard_hid_report_descriptor, @@ -718,9 +724,7 @@ static void usb_hid_initfn(USBDevice *dev, int kind, return; } - if (dev->serial) { - usb_desc_set_string(dev, STR_SERIALNUMBER, dev->serial); - } + usb_desc_create_serial(dev); usb_desc_init(dev); us->intr = usb_ep_get(dev, USB_TOKEN_IN, 1); hid_init(&us->hid, kind, usb_hid_changed); diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index b651406705..ed30cfb72d 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -147,13 +147,13 @@ static const USBDesc desc_hub = { static const uint8_t qemu_hub_hub_descriptor[] = { - 0x00, /* u8 bLength; patched in later */ - 0x29, /* u8 bDescriptorType; Hub-descriptor */ - 0x00, /* u8 bNbrPorts; (patched later) */ - 0x0a, /* u16 wHubCharacteristics; */ - 0x00, /* (per-port OC, no power switching) */ - 0x01, /* u8 bPwrOn2pwrGood; 2ms */ - 0x00 /* u8 bHubContrCurrent; 0 mA */ + 0x00, /* u8 bLength; patched in later */ + 0x29, /* u8 bDescriptorType; Hub-descriptor */ + 0x00, /* u8 bNbrPorts; (patched later) */ + 0x0a, /* u16 wHubCharacteristics; */ + 0x00, /* (per-port OC, no power switching) */ + 0x01, /* u8 bPwrOn2pwrGood; 2ms */ + 0x00 /* u8 bHubContrCurrent; 0 mA */ /* DeviceRemovable and PortPwrCtrlMask patched in later */ }; @@ -191,6 +191,10 @@ static void usb_hub_detach(USBPort *port1) port->wPortStatus &= ~PORT_STAT_ENABLE; port->wPortChange |= PORT_STAT_C_ENABLE; } + if (port->wPortStatus & PORT_STAT_SUSPEND) { + port->wPortStatus &= ~PORT_STAT_SUSPEND; + port->wPortChange |= PORT_STAT_C_SUSPEND; + } usb_wakeup(s->intr, 0); } diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 1ded7ac9a3..99548b012d 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -11,20 +11,20 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include #include #include -#ifdef CONFIG_INOTIFY1 -#include -#include "qemu/main-loop.h" -#endif + #include "qemu-common.h" #include "qemu/iov.h" +#include "qemu/filemonitor.h" #include "trace.h" #include "hw/usb.h" #include "desc.h" +#include "qemu/units.h" /* ----------------------------------------------------------------------- */ @@ -35,6 +35,13 @@ enum mtp_container_type { TYPE_EVENT = 4, }; +/* MTP write stage, for internal use only */ +enum mtp_write_status { + WRITE_START = 1, + WRITE_CONTINUE = 2, + WRITE_END = 3, +}; + enum mtp_code { /* command codes */ CMD_GET_DEVICE_INFO = 0x1001, @@ -82,6 +89,7 @@ enum mtp_code { FMT_ASSOCIATION = 0x3001, /* event codes */ + EVT_CANCEL_TRANSACTION = 0x4001, EVT_OBJ_ADDED = 0x4002, EVT_OBJ_REMOVED = 0x4003, EVT_OBJ_INFO_CHANGED = 0x4007, @@ -123,7 +131,6 @@ enum { EP_EVENT, }; -#ifdef CONFIG_INOTIFY1 typedef struct MTPMonEntry MTPMonEntry; struct MTPMonEntry { @@ -132,7 +139,6 @@ struct MTPMonEntry { QTAILQ_ENTRY(MTPMonEntry) next; }; -#endif struct MTPControl { uint16_t code; @@ -146,10 +152,15 @@ struct MTPData { uint32_t trans; uint64_t offset; uint64_t length; - uint32_t alloc; + uint64_t alloc; uint8_t *data; bool first; + /* Used for >4G file sizes */ + bool pending; int fd; + uint8_t write_status; + /* Internal pointer per every MTP_WRITE_BUF_SZ */ + uint64_t data_offset; }; struct MTPObject { @@ -158,10 +169,8 @@ struct MTPObject { char *name; char *path; struct stat stat; -#ifdef CONFIG_INOTIFY1 - /* inotify watch cookie */ - int watchfd; -#endif + /* file monitor watch id */ + int64_t watchid; MTPObject *parent; uint32_t nchildren; QLIST_HEAD(, MTPObject) children; @@ -184,11 +193,8 @@ struct MTPState { bool readonly; QTAILQ_HEAD(, MTPObject) objects; -#ifdef CONFIG_INOTIFY1 - /* inotify descriptor */ - int inotifyfd; - QTAILQ_HEAD(events, MTPMonEntry) events; -#endif + QFileMonitor *file_monitor; + QTAILQ_HEAD(, MTPMonEntry) events; /* Responder is expecting a write operation */ bool write_pending; struct { @@ -240,6 +246,7 @@ typedef struct { #define MTP_MANUFACTURER "QEMU" #define MTP_PRODUCT "QEMU filesharing" +#define MTP_WRITE_BUF_SZ (512 * KiB) enum { STR_MANUFACTURER = 1, @@ -368,7 +375,7 @@ static const USBDesc desc = { /* ----------------------------------------------------------------------- */ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, - MTPObject *parent, char *name) + MTPObject *parent, const char *name) { MTPObject *o = g_new0(MTPObject, 1); @@ -376,6 +383,7 @@ static MTPObject *usb_mtp_object_alloc(MTPState *s, uint32_t handle, goto ignore; } + o->watchid = -1; o->handle = handle; o->parent = parent; o->name = g_strdup(name); @@ -422,6 +430,10 @@ static void usb_mtp_object_free(MTPState *s, MTPObject *o) trace_usb_mtp_object_free(s->dev.addr, o->handle, o->path); + if (o->watchid != -1 && s->file_monitor) { + qemu_file_monitor_remove_watch(s->file_monitor, o->path, o->watchid); + } + QTAILQ_REMOVE(&s->objects, o, next); if (o->parent) { QLIST_REMOVE(o, list); @@ -450,7 +462,7 @@ static MTPObject *usb_mtp_object_lookup(MTPState *s, uint32_t handle) } static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, - char *name) + const char *name) { MTPObject *child = usb_mtp_object_alloc(s, s->next_handle++, o, name); @@ -469,10 +481,14 @@ static MTPObject *usb_mtp_add_child(MTPState *s, MTPObject *o, } static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, - char *name, int len) + const char *name, int len) { MTPObject *iter; + if (len == -1) { + len = strlen(name); + } + QLIST_FOREACH(iter, &parent->children, list) { if (strncmp(iter->name, name, len) == 0) { return iter; @@ -482,13 +498,12 @@ static MTPObject *usb_mtp_object_lookup_name(MTPObject *parent, return NULL; } -#ifdef CONFIG_INOTIFY1 -static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) +static MTPObject *usb_mtp_object_lookup_id(MTPState *s, int64_t id) { MTPObject *iter; QTAILQ_FOREACH(iter, &s->objects, next) { - if (iter->watchfd == wd) { + if (iter->watchid == id) { return iter; } } @@ -496,179 +511,133 @@ static MTPObject *usb_mtp_object_lookup_wd(MTPState *s, int wd) return NULL; } -static void inotify_watchfn(void *arg) +static void file_monitor_event(int64_t id, + QFileMonitorEvent ev, + const char *name, + void *opaque) { - 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]; + MTPState *s = opaque; + MTPObject *parent = usb_mtp_object_lookup_id(s, id); + MTPMonEntry *entry = NULL; + MTPObject *o; - 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: - trace_usb_mtp_inotify_event(s->dev.addr, parent->path, - event->mask, "Obj parent dir 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) { + if (!parent) { return; } - qemu_set_fd_handler(s->inotifyfd, NULL, NULL, s); - close(s->inotifyfd); + switch (ev) { + case QFILE_MONITOR_EVENT_CREATED: + if (usb_mtp_object_lookup_name(parent, name, -1)) { + /* Duplicate create event */ + return; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = s->next_handle; + entry->event = EVT_OBJ_ADDED; + o = usb_mtp_add_child(s, parent, name); + if (!o) { + g_free(entry); + return; + } + trace_usb_mtp_file_monitor_event(s->dev.addr, name, "Obj Added"); + break; + + case QFILE_MONITOR_EVENT_DELETED: + /* + * 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, name, -1); + if (!o) { + return; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_REMOVED; + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Deleted"); + usb_mtp_object_free(s, o); + break; + + case QFILE_MONITOR_EVENT_MODIFIED: + o = usb_mtp_object_lookup_name(parent, name, -1); + if (!o) { + return; + } + entry = g_new0(MTPMonEntry, 1); + entry->handle = o->handle; + entry->event = EVT_OBJ_INFO_CHANGED; + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, "Obj Modified"); + break; + + case QFILE_MONITOR_EVENT_IGNORED: + trace_usb_mtp_file_monitor_event(s->dev.addr, parent->path, + "Obj parent dir ignored"); + break; + + case QFILE_MONITOR_EVENT_ATTRIBUTES: + break; + + default: + g_assert_not_reached(); + } + + if (entry) { + QTAILQ_INSERT_HEAD(&s->events, entry, next); + } +} + +static void usb_mtp_file_monitor_cleanup(MTPState *s) +{ + MTPMonEntry *e, *p; QTAILQ_FOREACH_SAFE(e, &s->events, next, p) { QTAILQ_REMOVE(&s->events, e, next); g_free(e); } + + qemu_file_monitor_free(s->file_monitor); + s->file_monitor = NULL; } -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; DIR *dir; + int fd; + Error *err = NULL; if (o->have_children) { return; } o->have_children = true; - dir = opendir(o->path); - if (!dir) { + fd = open(o->path, O_DIRECTORY | O_CLOEXEC | O_NOFOLLOW); + if (fd < 0) { 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; + dir = fdopendir(fd); + if (!dir) { + close(fd); + return; } -#endif + + if (s->file_monitor) { + int64_t id = qemu_file_monitor_add_watch(s->file_monitor, o->path, NULL, + file_monitor_event, s, &err); + if (id == -1) { + error_report("usb-mtp: failed to add watch for %s: %s", o->path, + error_get_pretty(err)); + error_free(err); + } else { + trace_usb_mtp_file_monitor_event(s->dev.addr, o->path, + "Watch Added"); + o->watchid = id; + } + } + while ((entry = readdir(dir)) != NULL) { usb_mtp_add_child(s, o, entry->d_name); } @@ -1003,7 +972,7 @@ static MTPData *usb_mtp_get_object(MTPState *s, MTPControl *c, trace_usb_mtp_op_get_object(s->dev.addr, o->handle, o->path); - d->fd = open(o->path, O_RDONLY); + d->fd = open(o->path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); if (d->fd == -1) { usb_mtp_data_free(d); return NULL; @@ -1027,7 +996,7 @@ static MTPData *usb_mtp_get_partial_object(MTPState *s, MTPControl *c, c->argv[1], c->argv[2]); d = usb_mtp_data_alloc(c); - d->fd = open(o->path, O_RDONLY); + d->fd = open(o->path, O_RDONLY | O_CLOEXEC | O_NOFOLLOW); if (d->fd == -1) { usb_mtp_data_free(d); return NULL; @@ -1166,30 +1135,25 @@ static MTPData *usb_mtp_get_object_prop_value(MTPState *s, MTPControl *c, return d; } -/* Return correct return code for a delete event */ +/* + * Return values when object @o is deleted. + * If at least one of the deletions succeeded, + * DELETE_SUCCESS is set and if at least one + * of the deletions failed, DELETE_FAILURE is + * set. Both bits being set (DELETE_PARTIAL) + * signifies a RES_PARTIAL_DELETE being sent + * back to the initiator. + */ enum { - ALL_DELETE, - PARTIAL_DELETE, - READ_ONLY, + DELETE_SUCCESS = (1 << 0), + DELETE_FAILURE = (1 << 1), + DELETE_PARTIAL = (DELETE_FAILURE | DELETE_SUCCESS), }; -/* Assumes that children, if any, have been already freed */ -static void usb_mtp_object_free_one(MTPState *s, MTPObject *o) -{ -#ifndef CONFIG_INOTIFY1 - assert(o->nchildren == 0); - QTAILQ_REMOVE(&s->objects, o, next); - g_free(o->name); - g_free(o->path); - g_free(o); -#endif -} - static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) { MTPObject *iter, *iter2; - bool partial_delete = false; - bool success = false; + int ret = 0; /* * TODO: Add support for Protection Status @@ -1198,36 +1162,28 @@ static int usb_mtp_deletefn(MTPState *s, MTPObject *o, uint32_t trans) QLIST_FOREACH(iter, &o->children, list) { if (iter->format == FMT_ASSOCIATION) { QLIST_FOREACH(iter2, &iter->children, list) { - usb_mtp_deletefn(s, iter2, trans); + ret |= usb_mtp_deletefn(s, iter2, trans); } } } if (o->format == FMT_UNDEFINED_OBJECT) { if (remove(o->path)) { - partial_delete = true; + ret |= DELETE_FAILURE; } else { - usb_mtp_object_free_one(s, o); - success = true; + usb_mtp_object_free(s, o); + ret |= DELETE_SUCCESS; } - } - - if (o->format == FMT_ASSOCIATION) { + } else if (o->format == FMT_ASSOCIATION) { if (rmdir(o->path)) { - partial_delete = true; + ret |= DELETE_FAILURE; } else { - usb_mtp_object_free_one(s, o); - success = true; + usb_mtp_object_free(s, o); + ret |= DELETE_SUCCESS; } } - if (success && partial_delete) { - return PARTIAL_DELETE; - } - if (!success && partial_delete) { - return READ_ONLY; - } - return ALL_DELETE; + return ret; } static void usb_mtp_object_delete(MTPState *s, uint32_t handle, @@ -1261,19 +1217,24 @@ static void usb_mtp_object_delete(MTPState *s, uint32_t handle, } ret = usb_mtp_deletefn(s, o, trans); - if (ret == PARTIAL_DELETE) { - usb_mtp_queue_result(s, RES_PARTIAL_DELETE, - trans, 0, 0, 0, 0); - return; - } else if (ret == READ_ONLY) { - usb_mtp_queue_result(s, RES_STORE_READ_ONLY, trans, - 0, 0, 0, 0); - return; - } else { + switch (ret) { + case DELETE_SUCCESS: usb_mtp_queue_result(s, RES_OK, trans, 0, 0, 0, 0); - return; + break; + case DELETE_FAILURE: + usb_mtp_queue_result(s, RES_PARTIAL_DELETE, + trans, 0, 0, 0, 0); + break; + case DELETE_PARTIAL: + usb_mtp_queue_result(s, RES_PARTIAL_DELETE, + trans, 0, 0, 0, 0); + break; + default: + g_assert_not_reached(); } + + return; } static void usb_mtp_command(MTPState *s, MTPControl *c) @@ -1281,6 +1242,7 @@ static void usb_mtp_command(MTPState *s, MTPControl *c) MTPData *data_in = NULL; MTPObject *o = NULL; uint32_t nres = 0, res0 = 0; + Error *err = NULL; /* sanity checks */ if (c->code >= CMD_CLOSE_SESSION && s->session == 0) { @@ -1308,19 +1270,21 @@ 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"); + + s->file_monitor = qemu_file_monitor_new(&err); + if (err) { + error_report("usb-mtp: file monitoring init failed: %s", + error_get_pretty(err)); + error_free(err); + } else { + QTAILQ_INIT(&s->events); } -#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_file_monitor_cleanup(s); usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); assert(QTAILQ_EMPTY(&s->objects)); break; @@ -1533,9 +1497,7 @@ 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_file_monitor_cleanup(s); usb_mtp_object_free(s, QTAILQ_FIRST(&s->objects)); s->session = 0; usb_mtp_data_free(s->data_in); @@ -1551,14 +1513,36 @@ static void usb_mtp_handle_control(USBDevice *dev, USBPacket *p, int length, uint8_t *data) { int ret; + MTPState *s = USB_MTP(dev); + uint16_t *event = (uint16_t *)data; - ret = usb_desc_handle_control(dev, p, request, value, index, length, data); - if (ret >= 0) { - return; + switch (request) { + case ClassInterfaceOutRequest | 0x64: + if (*event == EVT_CANCEL_TRANSACTION) { + g_free(s->result); + s->result = NULL; + usb_mtp_data_free(s->data_in); + s->data_in = NULL; + if (s->write_pending) { + g_free(s->dataset.filename); + s->write_pending = false; + s->dataset.size = 0; + } + usb_mtp_data_free(s->data_out); + s->data_out = NULL; + } else { + p->status = USB_RET_STALL; + } + break; + default: + ret = usb_desc_handle_control(dev, p, request, + value, index, length, data); + if (ret >= 0) { + return; + } } trace_usb_mtp_stall(dev->addr, "unknown control request"); - p->status = USB_RET_STALL; } static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) @@ -1567,72 +1551,123 @@ static void usb_mtp_cancel_packet(USBDevice *dev, USBPacket *p) fprintf(stderr, "%s\n", __func__); } -static void utf16_to_str(uint8_t len, uint16_t *arr, char *name) +static char *utf16_to_str(uint8_t len, uint16_t *arr) { - int count; - wchar_t *wstr = g_new0(wchar_t, len); + wchar_t *wstr = g_new0(wchar_t, len + 1); + int count, dlen; + char *dest; for (count = 0; count < len; count++) { + /* FIXME: not working for surrogate pairs */ wstr[count] = (wchar_t)arr[count]; } + wstr[count] = 0; - wcstombs(name, wstr, len); + dlen = wcstombs(NULL, wstr, 0) + 1; + dest = g_malloc(dlen); + wcstombs(dest, wstr, dlen); g_free(wstr); + return dest; } -static void usb_mtp_write_data(MTPState *s) +/* Wrapper around write, returns 0 on failure */ +static uint64_t write_retry(int fd, void *buf, uint64_t size, off_t offset) +{ + uint64_t ret = 0; + + if (lseek(fd, offset, SEEK_SET) < 0) { + goto done; + } + + ret = qemu_write_full(fd, buf, size); + +done: + return ret; +} + +static int usb_mtp_update_object(MTPObject *parent, char *name) +{ + int ret = -1; + + MTPObject *o = + usb_mtp_object_lookup_name(parent, name, strlen(name)); + + if (o) { + ret = lstat(o->path, &o->stat); + } + + return ret; +} + +static int usb_mtp_write_data(MTPState *s) { MTPData *d = s->data_out; MTPObject *parent = usb_mtp_object_lookup(s, s->dataset.parent_handle); char *path = NULL; - int rc = -1; + uint64_t rc; mode_t mask = 0644; + int ret = 0; assert(d != NULL); - if (parent == NULL || !s->write_pending) { - usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, - 0, 0, 0, 0); - return; - } - - if (s->dataset.filename) { - path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); - if (s->dataset.format == FMT_ASSOCIATION) { - d->fd = mkdir(path, mask); - goto free; - } - if (s->dataset.size < d->length) { - usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, - 0, 0, 0, 0); - goto done; - } - d->fd = open(path, O_CREAT | O_WRONLY, mask); - if (d->fd == -1) { - usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, - 0, 0, 0, 0); - goto done; + switch (d->write_status) { + case WRITE_START: + if (!parent || !s->write_pending) { + usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, d->trans, + 0, 0, 0, 0); + return 1; } - /* - * Return success if initiator sent 0 sized data - */ - if (!s->dataset.size) { - goto success; - } - - rc = write(d->fd, d->data, s->dataset.size); - if (rc == -1) { - usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, - 0, 0, 0, 0); - goto done; + if (s->dataset.filename) { + path = g_strdup_printf("%s/%s", parent->path, s->dataset.filename); + if (s->dataset.format == FMT_ASSOCIATION) { + ret = mkdir(path, mask); + goto free; } - if (rc != s->dataset.size) { - usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, + d->fd = open(path, O_CREAT | O_WRONLY | + O_CLOEXEC | O_NOFOLLOW, mask); + if (d->fd == -1) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, + 0, 0, 0, 0); + goto done; + } + + /* Return success if initiator sent 0 sized data */ + if (!s->dataset.size) { + goto success; + } + if (d->length != MTP_WRITE_BUF_SZ && !d->pending) { + d->write_status = WRITE_END; + } + } + /* fall through */ + case WRITE_CONTINUE: + case WRITE_END: + rc = write_retry(d->fd, d->data, d->data_offset, + d->offset - d->data_offset); + if (rc != d->data_offset) { + usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); goto done; } + if (d->write_status != WRITE_END) { + g_free(path); + return ret; + } else { + /* + * Return an incomplete transfer if file size doesn't match + * for < 4G file or if lstat fails which will result in an incorrect + * file size + */ + if ((s->dataset.size != 0xFFFFFFFF && + d->offset != s->dataset.size) || + usb_mtp_update_object(parent, s->dataset.filename)) { + usb_mtp_queue_result(s, RES_INCOMPLETE_TRANSFER, d->trans, + 0, 0, 0, 0); + goto done; + } + } } success: @@ -1646,26 +1681,45 @@ done: */ if (d->fd != -1) { close(d->fd); + d->fd = -1; } free: g_free(s->dataset.filename); + s->dataset.size = 0; g_free(path); s->write_pending = false; + return ret; } -static void usb_mtp_write_metadata(MTPState *s) +static void usb_mtp_write_metadata(MTPState *s, uint64_t dlen) { MTPData *d = s->data_out; ObjectInfo *dataset = (ObjectInfo *)d->data; - char *filename = g_new0(char, dataset->length); + char *filename; MTPObject *o; MTPObject *p = usb_mtp_object_lookup(s, s->dataset.parent_handle); uint32_t next_handle = s->next_handle; + size_t filename_chars = dlen - offsetof(ObjectInfo, filename); + + /* + * filename is utf-16. We're intentionally doing + * integer division to truncate if malicious guest + * sent an odd number of bytes. + */ + filename_chars /= 2; assert(!s->write_pending); assert(p != NULL); - utf16_to_str(dataset->length, dataset->filename, filename); + filename = utf16_to_str(MIN(dataset->length, filename_chars), + dataset->filename); + + if (strchr(filename, '/')) { + usb_mtp_queue_result(s, RES_PARAMETER_NOT_SUPPORTED, d->trans, + 0, 0, 0, 0); + g_free(filename); + return; + } o = usb_mtp_object_lookup_name(p, filename, dataset->length); if (o != NULL) { @@ -1675,18 +1729,15 @@ static void usb_mtp_write_metadata(MTPState *s) s->dataset.filename = filename; s->dataset.format = dataset->format; s->dataset.size = dataset->size; - s->dataset.filename = filename; s->write_pending = true; if (s->dataset.format == FMT_ASSOCIATION) { - usb_mtp_write_data(s); - /* next_handle will be allocated to the newly created dir */ - if (d->fd == -1) { + if (usb_mtp_write_data(s)) { + /* next_handle will be allocated to the newly created dir */ usb_mtp_queue_result(s, RES_STORE_FULL, d->trans, 0, 0, 0, 0); return; } - d->fd = -1; } usb_mtp_queue_result(s, RES_OK, d->trans, 3, QEMU_STORAGE_ID, @@ -1699,6 +1750,7 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, MTPData *d = s->data_out; uint64_t dlen; uint32_t data_len = p->iov.size; + uint64_t total_len; if (!d) { usb_mtp_queue_result(s, RES_INVALID_OBJECTINFO, 0, @@ -1707,28 +1759,49 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, } if (d->first) { /* Total length of incoming data */ - d->length = cpu_to_le32(container->length) - sizeof(mtp_container); + total_len = cpu_to_le32(container->length) - sizeof(mtp_container); /* Length of data in this packet */ data_len -= sizeof(mtp_container); - usb_mtp_realloc(d, d->length); + if (total_len < MTP_WRITE_BUF_SZ) { + usb_mtp_realloc(d, total_len); + d->length += total_len; + } else { + usb_mtp_realloc(d, MTP_WRITE_BUF_SZ - sizeof(mtp_container)); + d->length += MTP_WRITE_BUF_SZ - sizeof(mtp_container); + } d->offset = 0; d->first = false; + d->pending = false; + d->data_offset = 0; + d->write_status = WRITE_START; } - if (d->length - d->offset > data_len) { + if (d->pending) { + memset(d->data, 0, d->length); + if (d->length != MTP_WRITE_BUF_SZ) { + usb_mtp_realloc(d, MTP_WRITE_BUF_SZ - d->length); + d->length += (MTP_WRITE_BUF_SZ - d->length); + } + d->pending = false; + d->write_status = WRITE_CONTINUE; + d->data_offset = 0; + } + + if (d->length - d->data_offset > data_len) { dlen = data_len; } else { - dlen = d->length - d->offset; + dlen = d->length - d->data_offset; } switch (d->code) { case CMD_SEND_OBJECT_INFO: - usb_packet_copy(p, d->data + d->offset, dlen); + usb_packet_copy(p, d->data + d->data_offset, dlen); d->offset += dlen; - if (d->offset == d->length) { + d->data_offset += dlen; + if (d->data_offset == d->length) { /* The operation might have already failed */ if (!s->result) { - usb_mtp_write_metadata(s); + usb_mtp_write_metadata(s, dlen); } usb_mtp_data_free(s->data_out); s->data_out = NULL; @@ -1736,14 +1809,27 @@ static void usb_mtp_get_data(MTPState *s, mtp_container *container, } break; case CMD_SEND_OBJECT: - usb_packet_copy(p, d->data + d->offset, dlen); + usb_packet_copy(p, d->data + d->data_offset, dlen); d->offset += dlen; - if (d->offset == d->length) { + d->data_offset += dlen; + if ((p->iov.size % 64) || !p->iov.size) { + assert((s->dataset.size == 0xFFFFFFFF) || + (s->dataset.size == d->offset)); + + if (d->length == MTP_WRITE_BUF_SZ) { + d->write_status = WRITE_END; + } else { + d->write_status = WRITE_START; + } usb_mtp_write_data(s); usb_mtp_data_free(s->data_out); s->data_out = NULL; return; } + if (d->data_offset == d->length) { + d->pending = true; + usb_mtp_write_data(s); + } break; default: p->status = USB_RET_STALL; @@ -1900,9 +1986,8 @@ 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); + struct MTPMonEntry *e = QTAILQ_LAST(&s->events); uint32_t handle; int len = sizeof(container) + sizeof(uint32_t); @@ -1924,7 +2009,6 @@ static void usb_mtp_handle_data(USBDevice *dev, USBPacket *p) g_free(e); return; } -#endif p->status = USB_RET_NAK; return; default: @@ -1953,7 +2037,7 @@ static void usb_mtp_realize(USBDevice *dev, Error **errp) QTAILQ_INIT(&s->objects); if (s->desc == NULL) { if (s->root == NULL) { - error_setg(errp, "usb-mtp: x-root property must be configured"); + error_setg(errp, "usb-mtp: rootdir property must be configured"); return; } s->desc = strrchr(s->root, '/'); @@ -1982,7 +2066,7 @@ static const VMStateDescription vmstate_usb_mtp = { }; static Property mtp_properties[] = { - DEFINE_PROP_STRING("x-root", MTPState, root), + DEFINE_PROP_STRING("rootdir", MTPState, root), DEFINE_PROP_STRING("desc", MTPState, desc), DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), DEFINE_PROP_END_OF_LIST(), diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index 385e090336..ffab3fabee 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -648,7 +648,7 @@ typedef struct USBNetState { char usbstring_mac[13]; NICState *nic; NICConf conf; - QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp; + QTAILQ_HEAD(, rndis_response) rndis_resp; } USBNetState; #define TYPE_USB_NET "usb-net" diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 98d1ca3c91..03c3bcd240 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -514,7 +514,7 @@ static USBDevice *usb_braille_init(USBBus *bus, const char *unused) USBDevice *dev; Chardev *cdrv; - cdrv = qemu_chr_new("braille", "braille"); + cdrv = qemu_chr_new("braille", "braille", NULL); if (!cdrv) return NULL; diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 8f716fc165..6b0137bb76 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1322,7 +1322,7 @@ static void ccid_realize(USBDevice *dev, Error **errp) usb_desc_init(dev); qbus_create_inplace(&s->bus, sizeof(s->bus), TYPE_CCID_BUS, DEVICE(dev), NULL); - qbus_set_hotplug_handler(BUS(&s->bus), DEVICE(dev), &error_abort); + qbus_set_hotplug_handler(BUS(&s->bus), OBJECT(dev), &error_abort); s->intr = usb_ep_get(dev, USB_TOKEN_IN, CCID_INT_IN_EP); s->bulk = usb_ep_get(dev, USB_TOKEN_IN, CCID_BULK_IN_EP); s->card = NULL; diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 45a9487cdb..cd5551d94f 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -599,7 +599,6 @@ static void usb_msd_storage_realize(USBDevice *dev, Error **errp) return; } - blkconf_serial(&s->conf, &dev->serial); blkconf_blocksizes(&s->conf); if (!blkconf_apply_backend_options(&s->conf, blk_is_read_only(blk), true, errp)) { diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 8c0fc53a26..38b24b160a 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -4,14 +4,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ @@ -230,56 +230,3 @@ static void ehci_pci_register_types(void) } type_init(ehci_pci_register_types) - -struct ehci_companions { - const char *name; - int func; - int port; -}; - -static const struct ehci_companions ich9_1d[] = { - { .name = "ich9-usb-uhci1", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci2", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci3", .func = 2, .port = 4 }, -}; - -static const struct ehci_companions ich9_1a[] = { - { .name = "ich9-usb-uhci4", .func = 0, .port = 0 }, - { .name = "ich9-usb-uhci5", .func = 1, .port = 2 }, - { .name = "ich9-usb-uhci6", .func = 2, .port = 4 }, -}; - -int ehci_create_ich9_with_companions(PCIBus *bus, int slot) -{ - const struct ehci_companions *comp; - PCIDevice *ehci, *uhci; - BusState *usbbus; - const char *name; - int i; - - switch (slot) { - case 0x1d: - name = "ich9-usb-ehci1"; - comp = ich9_1d; - break; - case 0x1a: - name = "ich9-usb-ehci2"; - comp = ich9_1a; - break; - default: - return -1; - } - - ehci = pci_create_multifunction(bus, PCI_DEVFN(slot, 7), true, name); - qdev_init_nofail(&ehci->qdev); - usbbus = QLIST_FIRST(&ehci->qdev.child_bus); - - for (i = 0; i < 3; i++) { - uhci = pci_create_multifunction(bus, PCI_DEVFN(slot, comp[i].func), - true, comp[i].name); - qdev_prop_set_string(&uhci->qdev, "masterbus", usbbus->name); - qdev_prop_set_uint32(&uhci->qdev, "firstport", comp[i].port); - qdev_init_nofail(&uhci->qdev); - } - return 0; -} diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 3b83beb140..9f7f128f19 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -4,14 +4,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ @@ -94,6 +94,22 @@ static const TypeInfo ehci_type_info = { .class_size = sizeof(SysBusEHCIClass), }; +static void ehci_platform_class_init(ObjectClass *oc, void *data) +{ + SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); + DeviceClass *dc = DEVICE_CLASS(oc); + + sec->capsbase = 0x0; + sec->opregbase = 0x20; + set_bit(DEVICE_CATEGORY_USB, dc->categories); +} + +static const TypeInfo ehci_platform_type_info = { + .name = TYPE_PLATFORM_EHCI, + .parent = TYPE_SYS_BUS_EHCI, + .class_init = ehci_platform_class_init, +}; + static void ehci_xlnx_class_init(ObjectClass *oc, void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); @@ -245,6 +261,7 @@ static const TypeInfo ehci_fusbh200_type_info = { static void ehci_sysbus_register_types(void) { type_register_static(&ehci_type_info); + type_register_static(&ehci_platform_type_info); type_register_static(&ehci_xlnx_type_info); type_register_static(&ehci_exynos4210_type_info); type_register_static(&ehci_tegra2_type_info); diff --git a/hw/usb/hcd-ehci.c b/hw/usb/hcd-ehci.c index e5acfc5ba5..62dab0592f 100644 --- a/hw/usb/hcd-ehci.c +++ b/hw/usb/hcd-ehci.c @@ -12,18 +12,17 @@ * Niels de Vos. David S. Ahern continued working on it. Kevin Wolf, * Jan Kiszka and Vincent Palatin contributed bugfixes. * - * * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ @@ -1440,9 +1439,12 @@ static int ehci_process_itd(EHCIState *ehci, qemu_sglist_add(&ehci->isgl, ptr1 + off, len); } - pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; - dev = ehci_find_device(ehci, devaddr); + if (dev == NULL) { + ehci_trace_guest_bug(ehci, "no device found"); + return -1; + } + pid = dir ? USB_TOKEN_IN : USB_TOKEN_OUT; ep = usb_ep_get(dev, pid, endp); if (ep && ep->type == USB_ENDPOINT_XFER_ISOC) { usb_packet_setup(&ehci->ipacket, pid, ep, 0, addr, false, @@ -1783,9 +1785,17 @@ static int ehci_state_fetchqtd(EHCIQueue *q) EHCIqtd qtd; EHCIPacket *p; int again = 1; + uint32_t addr; - if (get_dwords(q->ehci, NLPTR_GET(q->qtdaddr), (uint32_t *) &qtd, - sizeof(EHCIqtd) >> 2) < 0) { + addr = NLPTR_GET(q->qtdaddr); + if (get_dwords(q->ehci, addr + 8, &qtd.token, 1) < 0) { + return 0; + } + barrier(); + if (get_dwords(q->ehci, addr + 0, &qtd.next, 1) < 0 || + get_dwords(q->ehci, addr + 4, &qtd.altnext, 1) < 0 || + get_dwords(q->ehci, addr + 12, qtd.bufptr, + ARRAY_SIZE(qtd.bufptr)) < 0) { return 0; } ehci_trace_qtd(q, NLPTR_GET(q->qtdaddr), &qtd); @@ -1815,7 +1825,7 @@ static int ehci_state_fetchqtd(EHCIQueue *q) break; case EHCI_ASYNC_INFLIGHT: /* Check if the guest has added new tds to the queue */ - again = ehci_fill_queue(QTAILQ_LAST(&q->packets, pkts_head)); + again = ehci_fill_queue(QTAILQ_LAST(&q->packets)); /* Unfinished async handled packet, go horizontal */ ehci_set_state(q->ehci, q->async, EST_HORIZONTALQH); break; diff --git a/hw/usb/hcd-ehci.h b/hw/usb/hcd-ehci.h index 0bc364b286..fedf82c611 100644 --- a/hw/usb/hcd-ehci.h +++ b/hw/usb/hcd-ehci.h @@ -4,14 +4,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . */ @@ -247,7 +247,7 @@ struct EHCIQueue { uint32_t qtdaddr; /* address QTD read from */ int last_pid; /* pid of last packet executed */ USBDevice *dev; - QTAILQ_HEAD(pkts_head, EHCIPacket) packets; + QTAILQ_HEAD(, EHCIPacket) packets; }; typedef QTAILQ_HEAD(EHCIQueueHead, EHCIQueue) EHCIQueueHead; @@ -342,6 +342,7 @@ typedef struct EHCIPCIState { #define TYPE_SYS_BUS_EHCI "sysbus-ehci-usb" +#define TYPE_PLATFORM_EHCI "platform-ehci-usb" #define TYPE_EXYNOS4210_EHCI "exynos4210-ehci-usb" #define TYPE_TEGRA2_EHCI "tegra2-ehci-usb" #define TYPE_PPC4xx_EHCI "ppc4xx-ehci-usb" diff --git a/hw/usb/hcd-musb.c b/hw/usb/hcd-musb.c index d70a91a58c..85d7796554 100644 --- a/hw/usb/hcd-musb.c +++ b/hw/usb/hcd-musb.c @@ -628,11 +628,11 @@ static void musb_packet(MUSBState *s, MUSBEndPoint *ep, /* A wild guess on the FADDR semantics... */ dev = usb_find_device(&s->port, ep->faddr[idx]); - uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); - id = pid; - if (uep) { - id |= (dev->addr << 16) | (uep->nr << 8); + if (dev == NULL) { + return; } + uep = usb_ep_get(dev, pid, ep->type[idx] & 0xf); + id = pid | (dev->addr << 16) | (uep->nr << 8); usb_packet_setup(&ep->packey[dir].p, pid, uep, 0, id, false, true); usb_packet_addbuf(&ep->packey[dir].p, ep->buf[idx], len); ep->packey[dir].ep = ep; diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index fa07bd452c..d149b2a8a1 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -57,7 +57,7 @@ typedef struct { qemu_irq irq; MemoryRegion mem; AddressSpace *as; - int num_ports; + uint32_t num_ports; const char *name; QEMUTimer *eof_timer; @@ -848,6 +848,10 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed, bool int_req = relative_frame_number == frame_count && OHCI_BM(iso_td.flags, TD_DI) == 0; dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) { + trace_usb_ohci_td_dev_error(); + return 1; + } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, false, int_req); usb_packet_addbuf(&ohci->usb_packet, ohci->usb_buf, len); @@ -1071,6 +1075,10 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) return 1; } dev = ohci_find_device(ohci, OHCI_BM(ed->flags, ED_FA)); + if (dev == NULL) { + trace_usb_ohci_td_dev_error(); + return 1; + } ep = usb_ep_get(dev, pid, OHCI_BM(ed->flags, ED_EN)); usb_packet_setup(&ohci->usb_packet, pid, ep, 0, addr, !flag_r, OHCI_BM(td.flags, TD_DI) == 0); @@ -1158,6 +1166,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed) OHCI_SET_BM(td.flags, TD_EC, 3); break; } + /* An error occured so we have to clear the interrupt counter. See + * spec at 6.4.4 on page 104 */ + ohci->done_count = 0; } ed->head |= OHCI_ED_H; } @@ -1193,7 +1204,7 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) if (head == 0) return 0; - for (cur = head; cur; cur = next_ed) { + for (cur = head; cur && link_cnt++ < ED_LINK_LIMIT; cur = next_ed) { if (ohci_read_ed(ohci, cur, &ed)) { trace_usb_ohci_ed_read_error(cur); ohci_die(ohci); @@ -1202,11 +1213,6 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) next_ed = ed.next & OHCI_DPTR_MASK; - if (++link_cnt > ED_LINK_LIMIT) { - ohci_die(ohci); - return 0; - } - if ((ed.head & OHCI_ED_H) || (ed.flags & OHCI_ED_K)) { uint32_t addr; /* Cancel pending packets for ED that have been paused. */ @@ -1254,12 +1260,12 @@ static int ohci_service_ed_list(OHCIState *ohci, uint32_t head, int completion) /* 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->sof_time += usb_frame_time; ohci_eof_timer(ohci); ohci_set_interrupt(ohci, OHCI_INTR_SF); } @@ -1363,6 +1369,7 @@ static int ohci_bus_start(OHCIState *ohci) * can meet some race conditions */ + ohci->sof_time = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); ohci_eof_timer(ohci); return 1; @@ -1477,6 +1484,9 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci) * set already. */ tks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - ohci->sof_time; + if (tks < 0) { + tks = 0; + } /* avoid muldiv if possible */ if (tks >= usb_frame_time) @@ -1847,7 +1857,7 @@ static USBBusOps ohci_bus_ops = { }; static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, - int num_ports, dma_addr_t localmem_base, + uint32_t num_ports, dma_addr_t localmem_base, char *masterbus, uint32_t firstport, AddressSpace *as, Error **errp) { @@ -1857,7 +1867,7 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev, ohci->as = as; if (num_ports > OHCI_MAX_PORTS) { - error_setg(errp, "OHCI num-ports=%d is too big (limit is %d ports)", + error_setg(errp, "OHCI num-ports=%u is too big (limit is %u ports)", num_ports, OHCI_MAX_PORTS); return; } diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 836b11f177..09df29ff9c 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -99,7 +99,7 @@ struct UHCIQueue { UHCIState *uhci; USBEndpoint *ep; QTAILQ_ENTRY(UHCIQueue) next; - QTAILQ_HEAD(asyncs_head, UHCIAsync) asyncs; + QTAILQ_HEAD(, UHCIAsync) asyncs; int8_t valid; }; @@ -837,7 +837,7 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, } if (!async->done) { UHCI_TD last_td; - UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs, asyncs_head); + UHCIAsync *last = QTAILQ_LAST(&async->queue->asyncs); /* * While we are waiting for the current td to complete, the guest * may have added more tds to the queue. Note we re-read the td @@ -858,13 +858,15 @@ static int uhci_handle_td(UHCIState *s, UHCIQueue *q, uint32_t qh_addr, /* Allocate new packet */ if (q == NULL) { - USBDevice *dev = uhci_find_device(s, (td->token >> 8) & 0x7f); - USBEndpoint *ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); + USBDevice *dev; + USBEndpoint *ep; - if (ep == NULL) { + dev = uhci_find_device(s, (td->token >> 8) & 0x7f); + if (dev == NULL) { return uhci_handle_td_error(s, td, td_addr, USB_RET_NODEV, int_mask); } + ep = usb_ep_get(dev, pid, (td->token >> 15) & 0xf); q = uhci_queue_new(s, qh_addr, td, ep); } async = uhci_async_alloc(q, td_addr); @@ -1056,8 +1058,8 @@ static void uhci_process_frame(UHCIState *s) link = qh.link; } else { /* QH with elements */ - curr_qh = link; - link = qh.el_link; + curr_qh = link; + link = qh.el_link; } continue; } diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 8f1a01a405..ec28bee319 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1571,6 +1571,11 @@ static void xhci_stall_ep(XHCITransfer *xfer) uint32_t err; XHCIStreamContext *sctx; + if (epctx->type == ET_ISO_IN || epctx->type == ET_ISO_OUT) { + /* never halt isoch endpoints, 4.10.2 */ + return; + } + if (epctx->nr_pstreams) { sctx = xhci_find_stream(epctx, xfer->streamid, &err); if (sctx == NULL) { @@ -1944,6 +1949,16 @@ static void xhci_kick_epctx(XHCIEPContext *epctx, unsigned int streamid) while (1) { length = xhci_ring_chain_length(xhci, ring); if (length <= 0) { + if (epctx->type == ET_ISO_OUT || epctx->type == ET_ISO_IN) { + /* 4.10.3.1 */ + XHCIEvent ev = { ER_TRANSFER }; + ev.ccode = epctx->type == ET_ISO_IN ? + CC_RING_OVERRUN : CC_RING_UNDERRUN; + ev.slotid = epctx->slotid; + ev.epid = epctx->epid; + ev.ptr = epctx->ring.dequeue; + xhci_event(xhci, &ev, xhci->slots[epctx->slotid-1].intr); + } break; } xfer = xhci_ep_alloc_xfer(epctx, length); @@ -2023,6 +2038,7 @@ static TRBCCode xhci_disable_slot(XHCIState *xhci, unsigned int slotid) xhci->slots[slotid-1].enabled = 0; xhci->slots[slotid-1].addressed = 0; xhci->slots[slotid-1].uport = NULL; + xhci->slots[slotid-1].intr = 0; return CC_SUCCESS; } @@ -2122,6 +2138,7 @@ static TRBCCode xhci_address_slot(XHCIState *xhci, unsigned int slotid, slot = &xhci->slots[slotid-1]; slot->uport = uport; slot->ctx = octx; + slot->intr = get_field(slot_ctx[2], TRB_INTR); /* Make sure device is in USB_STATE_DEFAULT state */ usb_device_reset(dev); @@ -2295,8 +2312,9 @@ static TRBCCode xhci_evaluate_slot(XHCIState *xhci, unsigned int slotid, slot_ctx[1] &= ~0xFFFF; /* max exit latency */ slot_ctx[1] |= islot_ctx[1] & 0xFFFF; - slot_ctx[2] &= ~0xFF00000; /* interrupter target */ - slot_ctx[2] |= islot_ctx[2] & 0xFF000000; + /* update interrupter target field */ + xhci->slots[slotid-1].intr = get_field(islot_ctx[2], TRB_INTR); + set_field(&slot_ctx[2], xhci->slots[slotid-1].intr, TRB_INTR); DPRINTF("xhci: output slot context: %08x %08x %08x %08x\n", slot_ctx[0], slot_ctx[1], slot_ctx[2], slot_ctx[3]); @@ -2589,6 +2607,7 @@ static void xhci_port_update(XHCIPort *port, int is_detach) { uint32_t pls = PLS_RX_DETECT; + assert(port); port->portsc = PORTSC_PP; if (!is_detach && xhci_port_have_device(port)) { port->portsc |= PORTSC_CCS; @@ -3197,6 +3216,7 @@ static void xhci_wakeup(USBPort *usbport) XHCIState *xhci = usbport->opaque; XHCIPort *port = xhci_lookup_port(xhci, usbport); + assert(port); if (get_field(port->portsc, PORTSC_PLS) != PLS_U3) { return; } @@ -3256,10 +3276,10 @@ static USBEndpoint *xhci_epid_to_usbep(XHCIEPContext *epctx) return NULL; } uport = epctx->xhci->slots[epctx->slotid - 1].uport; - token = (epctx->epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT; - if (!uport) { + if (!uport || !uport->dev) { return NULL; } + token = (epctx->epid & 1) ? USB_TOKEN_IN : USB_TOKEN_OUT; return usb_ep_get(uport->dev, token, epctx->epid >> 1); } diff --git a/hw/usb/hcd-xhci.h b/hw/usb/hcd-xhci.h index fc36a4c787..240caa4e51 100644 --- a/hw/usb/hcd-xhci.h +++ b/hw/usb/hcd-xhci.h @@ -140,6 +140,7 @@ typedef struct XHCIPort { typedef struct XHCISlot { bool enabled; bool addressed; + uint16_t intr; dma_addr_t ctx; USBPort *uport; XHCIEPContext *eps[31]; diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index f31e9cbbb8..67b7465915 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -82,7 +82,7 @@ struct USBHostDevice { uint32_t options; uint32_t loglevel; bool needs_autoscan; - + bool allow_guest_reset; /* state */ QTAILQ_ENTRY(USBHostDevice) next; int seen, errcount; @@ -988,7 +988,9 @@ static void usb_host_exit_notifier(struct Notifier *n, void *data) if (s->dh) { usb_host_release_interfaces(s); + libusb_reset_device(s->dh); usb_host_attach_kernel(s); + libusb_close(s->dh); } } @@ -1120,6 +1122,9 @@ static void usb_host_detach_kernel(USBHostDevice *s) rc = libusb_kernel_driver_active(s->dh, i); usb_host_libusb_error("libusb_kernel_driver_active", rc); if (rc != 1) { + if (rc == 0) { + s->ifs[i].detached = true; + } continue; } trace_usb_host_detach_kernel(s->bus_num, s->addr, i); @@ -1451,6 +1456,10 @@ static void usb_host_handle_reset(USBDevice *udev) USBHostDevice *s = USB_HOST_DEVICE(udev); int rc; + if (!s->allow_guest_reset) { + return; + } + trace_usb_host_reset(s->bus_num, s->addr); rc = libusb_reset_device(s->dh); @@ -1568,6 +1577,7 @@ static Property usb_host_dev_properties[] = { DEFINE_PROP_UINT32("productid", USBHostDevice, match.product_id, 0), DEFINE_PROP_UINT32("isobufs", USBHostDevice, iso_urb_count, 4), DEFINE_PROP_UINT32("isobsize", USBHostDevice, iso_urb_frames, 32), + DEFINE_PROP_BOOL("guest-reset", USBHostDevice, allow_guest_reset, true), DEFINE_PROP_UINT32("loglevel", USBHostDevice, loglevel, LIBUSB_LOG_LEVEL_WARNING), DEFINE_PROP_BIT("pipeline", USBHostDevice, options, diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index 99094a721e..7cb6b120d4 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -1728,6 +1728,7 @@ static void usbredir_ep_info(void *priv, USBRedirDevice *dev = priv; int i; + assert(dev != NULL); for (i = 0; i < MAX_ENDPOINTS; i++) { dev->endpoint[i].type = ep_info->type[i]; dev->endpoint[i].interval = ep_info->interval[i]; @@ -2125,7 +2126,7 @@ static int usbredir_post_load(void *priv, int version_id) { USBRedirDevice *dev = priv; - if (dev->parser == NULL) { + if (dev == NULL || dev->parser == NULL) { return 0; } @@ -2155,7 +2156,7 @@ static int usbredir_post_load(void *priv, int version_id) /* For usbredirparser migration */ static int usbredir_put_parser(QEMUFile *f, void *priv, size_t unused, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { USBRedirDevice *dev = priv; uint8_t *data; @@ -2178,7 +2179,7 @@ static int usbredir_put_parser(QEMUFile *f, void *priv, size_t unused, } static int usbredir_get_parser(QEMUFile *f, void *priv, size_t unused, - VMStateField *field) + const VMStateField *field) { USBRedirDevice *dev = priv; uint8_t *data; @@ -2222,7 +2223,7 @@ static const VMStateInfo usbredir_parser_vmstate_info = { /* For buffered packets (iso/irq) queue migration */ static int usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { struct endp_data *endp = priv; USBRedirDevice *dev = endp->dev; @@ -2245,7 +2246,7 @@ static int usbredir_put_bufpq(QEMUFile *f, void *priv, size_t unused, } static int usbredir_get_bufpq(QEMUFile *f, void *priv, size_t unused, - VMStateField *field) + const VMStateField *field) { struct endp_data *endp = priv; USBRedirDevice *dev = endp->dev; @@ -2349,7 +2350,7 @@ static const VMStateDescription usbredir_ep_vmstate = { /* For PacketIdQueue migration */ static int usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { struct PacketIdQueue *q = priv; USBRedirDevice *dev = q->dev; @@ -2368,7 +2369,7 @@ static int usbredir_put_packet_id_q(QEMUFile *f, void *priv, size_t unused, } static int usbredir_get_packet_id_q(QEMUFile *f, void *priv, size_t unused, - VMStateField *field) + const VMStateField *field) { struct PacketIdQueue *q = priv; USBRedirDevice *dev = q->dev; diff --git a/hw/usb/trace-events b/hw/usb/trace-events index 2c18770ca5..2d3713351c 100644 --- a/hw/usb/trace-events +++ b/hw/usb/trace-events @@ -1,16 +1,16 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/usb/core.c +# core.c usb_packet_state_change(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s -> %s" usb_packet_state_fault(int bus, const char *port, int ep, void *p, const char *o, const char *n) "bus %d, port %s, ep %d, packet %p, state %s, expected %s" -# hw/usb/bus.c +# bus.c usb_port_claim(int bus, const char *port) "bus %d, port %s" usb_port_attach(int bus, const char *port, const char *devspeed, const char *portspeed) "bus %d, port %s, devspeed %s, portspeed %s" usb_port_detach(int bus, const char *port) "bus %d, port %s" usb_port_release(int bus, const char *port) "bus %d, port %s" -# hw/usb/hcd-ohci.c +# hcd-ohci.c usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at 0x%x" usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count 0x%.8x relative %d" usb_ohci_iso_td_head_offset(uint32_t o0, uint32_t o1, uint32_t o2, uint32_t o3, uint32_t o4, uint32_t o5, uint32_t o6, uint32_t o7) "0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x 0x%.8x" @@ -67,7 +67,7 @@ usb_ohci_init_time(int64_t frametime, int64_t bittime) "usb_bit_time=%" PRId64 " usb_ohci_die(void) "" usb_ohci_async_complete(void) "" -# hw/usb/hcd-ehci.c +# hcd-ehci.c usb_ehci_reset(void) "=== RESET ===" usb_ehci_unrealize(void) "=== UNREALIZE ===" usb_ehci_opreg_read(uint32_t addr, const char *str, uint32_t val) "rd mmio 0x%04x [%s] = 0x%x" @@ -100,7 +100,7 @@ usb_ehci_doorbell_ring(void) "" usb_ehci_doorbell_ack(void) "" usb_ehci_dma_error(void) "" -# hw/usb/hcd-uhci.c +# hcd-uhci.c usb_uhci_reset(void) "=== RESET ===" usb_uhci_exit(void) "=== EXIT ===" usb_uhci_schedule_start(void) "" @@ -130,7 +130,7 @@ usb_uhci_td_nextqh(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" usb_uhci_td_async(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" usb_uhci_td_complete(uint32_t qh, uint32_t td) "qh 0x%x, td 0x%x" -# hw/usb/hcd-xhci.c +# hcd-xhci.c usb_xhci_reset(void) "=== RESET ===" usb_xhci_exit(void) "=== EXIT ===" usb_xhci_run(void) "" @@ -176,7 +176,7 @@ usb_xhci_xfer_error(void *xfer, uint32_t ret) "%p: ret %d" usb_xhci_unimplemented(const char *item, int nr) "%s (0x%x)" usb_xhci_enforced_limit(const char *item) "%s" -# hw/usb/desc.c +# desc.c usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d" usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d" usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d" @@ -190,7 +190,7 @@ usb_set_interface(int addr, int iface, int alt, int ret) "dev %d, interface %d, usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d" -# hw/usb/dev-hub.c +# dev-hub.c usb_hub_reset(int addr) "dev %d" usb_hub_control(int addr, int request, int value, int index, int length) "dev %d, req 0x%x, value %d, index %d, langth %d" usb_hub_get_port_status(int addr, int nr, int status, int changed) "dev %d, port %d, status 0x%x, changed 0x%x" @@ -200,7 +200,7 @@ usb_hub_attach(int addr, int nr) "dev %d, port %d" usb_hub_detach(int addr, int nr) "dev %d, port %d" usb_hub_status_report(int addr, int status) "dev %d, status 0x%x" -# hw/usb/dev-uas.c +# dev-uas.c usb_uas_reset(int addr) "dev %d" usb_uas_command(int addr, uint16_t tag, int lun, uint32_t lun64_1, uint32_t lun64_2) "dev %d, tag 0x%x, lun %d, lun64 0x%08x-0x%08x" usb_uas_response(int addr, uint16_t tag, uint8_t code) "dev %d, tag 0x%x, code 0x%x" @@ -214,7 +214,7 @@ usb_uas_tmf_abort_task(int addr, uint16_t tag, uint16_t task_tag) "dev %d, tag 0 usb_uas_tmf_logical_unit_reset(int addr, uint16_t tag, int lun) "dev %d, tag 0x%x, lun %d" usb_uas_tmf_unsupported(int addr, uint16_t tag, uint32_t function) "dev %d, tag 0x%x, function 0x%x" -# hw/usb/dev-mtp.c +# dev-mtp.c usb_mtp_reset(int addr) "dev %d" usb_mtp_command(int dev, uint16_t code, uint32_t trans, uint32_t arg0, uint32_t arg1, uint32_t arg2, uint32_t arg3, uint32_t arg4) "dev %d, code 0x%x, trans 0x%x, args 0x%x, 0x%x, 0x%x, 0x%x, 0x%x" usb_mtp_success(int dev, uint32_t trans, uint32_t arg0, uint32_t arg1) "dev %d, trans 0x%x, args 0x%x, 0x%x" @@ -237,9 +237,9 @@ 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" +usb_mtp_file_monitor_event(int dev, const char *path, const char *s) "dev %d, path %s event %s" -# hw/usb/host-libusb.c +# host-libusb.c usb_host_open_started(int bus, int addr) "dev %d:%d" usb_host_open_success(int bus, int addr) "dev %d:%d" usb_host_open_failure(int bus, int addr) "dev %d:%d" diff --git a/hw/usb/tusb6010.c b/hw/usb/tusb6010.c index a2128024c1..f76b59afe8 100644 --- a/hw/usb/tusb6010.c +++ b/hw/usb/tusb6010.c @@ -24,7 +24,6 @@ #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" @@ -808,10 +807,10 @@ static void tusb6010_reset(DeviceState *dev) musb_reset(s->musb); } -static int tusb6010_init(SysBusDevice *sbd) +static void tusb6010_realize(DeviceState *dev, Error **errp) { - DeviceState *dev = DEVICE(sbd); TUSBState *s = TUSB(dev); + SysBusDevice *sbd = SYS_BUS_DEVICE(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); @@ -822,15 +821,13 @@ static int tusb6010_init(SysBusDevice *sbd) 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->realize = tusb6010_realize; dc->reset = tusb6010_reset; } diff --git a/hw/usb/xen-usb.c b/hw/usb/xen-usb.c index 5b2e21ed18..b20d0cfadf 100644 --- a/hw/usb/xen-usb.c +++ b/hw/usb/xen-usb.c @@ -27,7 +27,7 @@ #include "qemu/option.h" #include "hw/sysbus.h" #include "hw/usb.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "monitor/qdev.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qstring.h" @@ -72,7 +72,7 @@ struct usbback_stub { USBPort port; unsigned int speed; bool attached; - QTAILQ_HEAD(submit_q_head, usbback_req) submit_q; + QTAILQ_HEAD(, usbback_req) submit_q; }; struct usbback_req { @@ -99,7 +99,7 @@ struct usbback_hotplug { }; struct usbback_info { - struct XenDevice xendev; /* must be first */ + struct XenLegacyDevice xendev; /* must be first */ USBBus bus; void *urb_sring; void *conn_sring; @@ -108,8 +108,8 @@ struct usbback_info { int num_ports; int usb_ver; bool ring_error; - QTAILQ_HEAD(req_free_q_head, usbback_req) req_free_q; - QSIMPLEQ_HEAD(hotplug_q_head, usbback_hotplug) hotplug_q; + QTAILQ_HEAD(, usbback_req) req_free_q; + QSIMPLEQ_HEAD(, usbback_hotplug) hotplug_q; struct usbback_stub ports[USBBACK_MAXPORTS]; struct usbback_stub *addr_table[USB_DEV_ADDR_SIZE]; QEMUBH *bh; @@ -142,7 +142,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) unsigned int nr_segs, i, prot; uint32_t ref[USBIF_MAX_SEGMENTS_PER_REQUEST]; struct usbback_info *usbif = usbback_req->usbif; - struct XenDevice *xendev = &usbif->xendev; + struct XenLegacyDevice *xendev = &usbif->xendev; struct usbif_request_segment *seg; void *addr; @@ -220,7 +220,7 @@ static int usbback_gnttab_map(struct usbback_req *usbback_req) static int usbback_init_packet(struct usbback_req *usbback_req) { - struct XenDevice *xendev = &usbback_req->usbif->xendev; + struct XenLegacyDevice *xendev = &usbback_req->usbif->xendev; USBPacket *packet = &usbback_req->packet; USBDevice *dev = usbback_req->stub->dev; USBEndpoint *ep; @@ -279,7 +279,7 @@ static void usbback_do_response(struct usbback_req *usbback_req, int32_t status, { struct usbback_info *usbif; struct usbif_urb_response *res; - struct XenDevice *xendev; + struct XenLegacyDevice *xendev; unsigned int notify; usbif = usbback_req->usbif; @@ -824,7 +824,7 @@ static void usbback_process_port(struct usbback_info *usbif, unsigned port) g_free(busid); } -static void usbback_disconnect(struct XenDevice *xendev) +static void usbback_disconnect(struct XenLegacyDevice *xendev) { struct usbback_info *usbif; unsigned int i; @@ -853,17 +853,21 @@ static void usbback_disconnect(struct XenDevice *xendev) TR_BUS(xendev, "finished\n"); } -static int usbback_connect(struct XenDevice *xendev) +static int usbback_connect(struct XenLegacyDevice *xendev) { struct usbback_info *usbif; struct usbif_urb_sring *urb_sring; struct usbif_conn_sring *conn_sring; int urb_ring_ref; int conn_ring_ref; - unsigned int i; + unsigned int i, max_grants; TR_BUS(xendev, "start\n"); + /* max_grants: for each request and for the rings (request and connect). */ + max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; + xen_be_set_max_grant_refs(xendev, max_grants); + usbif = container_of(xendev, struct usbback_info, xendev); if (xenstore_read_fe_int(xendev, "urb-ring-ref", &urb_ring_ref)) { @@ -909,7 +913,8 @@ static int usbback_connect(struct XenDevice *xendev) return 0; } -static void usbback_backend_changed(struct XenDevice *xendev, const char *node) +static void usbback_backend_changed(struct XenLegacyDevice *xendev, + const char *node) { struct usbback_info *usbif; unsigned int i; @@ -922,7 +927,7 @@ static void usbback_backend_changed(struct XenDevice *xendev, const char *node) } } -static int usbback_init(struct XenDevice *xendev) +static int usbback_init(struct XenLegacyDevice *xendev) { struct usbback_info *usbif; @@ -1001,11 +1006,11 @@ static USBPortOps xen_usb_port_ops = { static USBBusOps xen_usb_bus_ops = { }; -static void usbback_alloc(struct XenDevice *xendev) +static void usbback_alloc(struct XenLegacyDevice *xendev) { struct usbback_info *usbif; USBPort *p; - unsigned int i, max_grants; + unsigned int i; usbif = container_of(xendev, struct usbback_info, xendev); @@ -1021,13 +1026,9 @@ static void usbback_alloc(struct XenDevice *xendev) QTAILQ_INIT(&usbif->req_free_q); QSIMPLEQ_INIT(&usbif->hotplug_q); usbif->bh = qemu_bh_new(usbback_bh, usbif); - - /* max_grants: for each request and for the rings (request and connect). */ - max_grants = USBIF_MAX_SEGMENTS_PER_REQUEST * USB_URB_RING_SIZE + 2; - xen_be_set_max_grant_refs(xendev, max_grants); } -static int usbback_free(struct XenDevice *xendev) +static int usbback_free(struct XenLegacyDevice *xendev) { struct usbback_info *usbif; struct usbback_req *usbback_req; @@ -1066,7 +1067,7 @@ static int usbback_free(struct XenDevice *xendev) return 0; } -static void usbback_event(struct XenDevice *xendev) +static void usbback_event(struct XenLegacyDevice *xendev) { struct usbback_info *usbif; diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig new file mode 100644 index 0000000000..34da2a3cfd --- /dev/null +++ b/hw/vfio/Kconfig @@ -0,0 +1,37 @@ +config VFIO + bool + depends on LINUX + +config VFIO_PCI + bool + default y + select VFIO + depends on LINUX && PCI + +config VFIO_CCW + bool + default y + select VFIO + depends on LINUX && S390_CCW_VIRTIO + +config VFIO_PLATFORM + bool + default y + select VFIO + depends on LINUX && PLATFORM_BUS + +config VFIO_XGMAC + bool + default y + depends on VFIO_PLATFORM + +config VFIO_AMD_XGBE + bool + default y + depends on VFIO_PLATFORM + +config VFIO_AP + bool + default y + select VFIO + depends on LINUX && S390_CCW_VIRTIO diff --git a/hw/vfio/Makefile.objs b/hw/vfio/Makefile.objs index a2e7a0a7cf..abad8b818c 100644 --- a/hw/vfio/Makefile.objs +++ b/hw/vfio/Makefile.objs @@ -1,9 +1,7 @@ -ifeq ($(CONFIG_LINUX), y) -obj-$(CONFIG_SOFTMMU) += common.o -obj-$(CONFIG_PCI) += pci.o pci-quirks.o display.o +obj-y += common.o spapr.o +obj-$(CONFIG_VFIO_PCI) += pci.o pci-quirks.o display.o obj-$(CONFIG_VFIO_CCW) += ccw.o -obj-$(CONFIG_SOFTMMU) += platform.o +obj-$(CONFIG_VFIO_PLATFORM) += platform.o obj-$(CONFIG_VFIO_XGMAC) += calxeda-xgmac.o obj-$(CONFIG_VFIO_AMD_XGBE) += amd-xgbe.o -obj-$(CONFIG_SOFTMMU) += spapr.o -endif +obj-$(CONFIG_VFIO_AP) += ap.o diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index 0c4ec4ba25..ee64a3b4a2 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -20,6 +20,7 @@ static void amd_xgbe_realize(DeviceState *dev, Error **errp) VFIOAmdXgbeDeviceClass *k = VFIO_AMD_XGBE_DEVICE_GET_CLASS(dev); vdev->compat = g_strdup("amd,xgbe-seattle-v1a"); + vdev->num_compat = 1; k->parent_realize(dev, errp); } diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c new file mode 100644 index 0000000000..d8b79ebe53 --- /dev/null +++ b/hw/vfio/ap.c @@ -0,0 +1,189 @@ +/* + * VFIO based AP matrix device assignment + * + * Copyright 2018 IBM Corp. + * Author(s): Tony Krowiak + * Halil Pasic + * + * 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 "qapi/error.h" +#include "hw/sysbus.h" +#include "hw/vfio/vfio.h" +#include "hw/vfio/vfio-common.h" +#include "hw/s390x/ap-device.h" +#include "qemu/error-report.h" +#include "qemu/queue.h" +#include "qemu/option.h" +#include "qemu/config-file.h" +#include "cpu.h" +#include "kvm_s390x.h" +#include "sysemu/sysemu.h" +#include "hw/s390x/ap-bridge.h" +#include "exec/address-spaces.h" + +#define VFIO_AP_DEVICE_TYPE "vfio-ap" + +typedef struct VFIOAPDevice { + APDevice apdev; + VFIODevice vdev; +} VFIOAPDevice; + +#define VFIO_AP_DEVICE(obj) \ + OBJECT_CHECK(VFIOAPDevice, (obj), VFIO_AP_DEVICE_TYPE) + +static void vfio_ap_compute_needs_reset(VFIODevice *vdev) +{ + vdev->needs_reset = false; +} + +/* + * We don't need vfio_hot_reset_multi and vfio_eoi operations for + * vfio-ap device now. + */ +struct VFIODeviceOps vfio_ap_ops = { + .vfio_compute_needs_reset = vfio_ap_compute_needs_reset, +}; + +static void vfio_ap_put_device(VFIOAPDevice *vapdev) +{ + g_free(vapdev->vdev.name); + vfio_put_base_device(&vapdev->vdev); +} + +static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp) +{ + GError *gerror = NULL; + char *symlink, *group_path; + int groupid; + + symlink = g_strdup_printf("%s/iommu_group", vapdev->vdev.sysfsdev); + group_path = g_file_read_link(symlink, &gerror); + g_free(symlink); + + if (!group_path) { + error_setg(errp, "%s: no iommu_group found for %s: %s", + VFIO_AP_DEVICE_TYPE, vapdev->vdev.sysfsdev, gerror->message); + return NULL; + } + + if (sscanf(basename(group_path), "%d", &groupid) != 1) { + error_setg(errp, "vfio: failed to read %s", group_path); + g_free(group_path); + return NULL; + } + + g_free(group_path); + + return vfio_get_group(groupid, &address_space_memory, errp); +} + +static void vfio_ap_realize(DeviceState *dev, Error **errp) +{ + int ret; + char *mdevid; + Error *local_err = NULL; + VFIOGroup *vfio_group; + APDevice *apdev = AP_DEVICE(dev); + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + + vfio_group = vfio_ap_get_group(vapdev, &local_err); + if (!vfio_group) { + goto out_err; + } + + vapdev->vdev.ops = &vfio_ap_ops; + vapdev->vdev.type = VFIO_DEVICE_TYPE_AP; + mdevid = basename(vapdev->vdev.sysfsdev); + vapdev->vdev.name = g_strdup_printf("%s", mdevid); + vapdev->vdev.dev = dev; + + /* + * vfio-ap devices operate in a way compatible with + * memory ballooning, as no pages are pinned in the host. + * This needs to be set before vfio_get_device() for vfio common to + * handle the balloon inhibitor. + */ + vapdev->vdev.balloon_allowed = true; + + ret = vfio_get_device(vfio_group, mdevid, &vapdev->vdev, &local_err); + if (ret) { + goto out_get_dev_err; + } + + return; + +out_get_dev_err: + vfio_ap_put_device(vapdev); + vfio_put_group(vfio_group); +out_err: + error_propagate(errp, local_err); +} + +static void vfio_ap_unrealize(DeviceState *dev, Error **errp) +{ + APDevice *apdev = AP_DEVICE(dev); + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + VFIOGroup *group = vapdev->vdev.group; + + vfio_ap_put_device(vapdev); + vfio_put_group(group); +} + +static Property vfio_ap_properties[] = { + DEFINE_PROP_STRING("sysfsdev", VFIOAPDevice, vdev.sysfsdev), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vfio_ap_reset(DeviceState *dev) +{ + int ret; + APDevice *apdev = AP_DEVICE(dev); + VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev); + + ret = ioctl(vapdev->vdev.fd, VFIO_DEVICE_RESET); + if (ret) { + error_report("%s: failed to reset %s device: %s", __func__, + vapdev->vdev.name, strerror(errno)); + } +} + +static const VMStateDescription vfio_ap_vmstate = { + .name = VFIO_AP_DEVICE_TYPE, + .unmigratable = 1, +}; + +static void vfio_ap_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = vfio_ap_properties; + dc->vmsd = &vfio_ap_vmstate; + dc->desc = "VFIO-based AP device assignment"; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->realize = vfio_ap_realize; + dc->unrealize = vfio_ap_unrealize; + dc->hotpluggable = true; + dc->reset = vfio_ap_reset; + dc->bus_type = TYPE_AP_BUS; +} + +static const TypeInfo vfio_ap_info = { + .name = VFIO_AP_DEVICE_TYPE, + .parent = AP_DEVICE_TYPE, + .instance_size = sizeof(VFIOAPDevice), + .class_init = vfio_ap_class_init, +}; + +static void vfio_ap_type_init(void) +{ + type_register_static(&vfio_ap_info); +} + +type_init(vfio_ap_type_init) diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index 24cee6d065..e7767c4b02 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -20,6 +20,7 @@ static void calxeda_xgmac_realize(DeviceState *dev, Error **errp) VFIOCalxedaXgmacDeviceClass *k = VFIO_CALXEDA_XGMAC_DEVICE_GET_CLASS(dev); vdev->compat = g_strdup("calxeda,hb-xgmac"); + vdev->num_compat = 1; k->parent_realize(dev, errp); } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 351b305e1a..c44d13cc50 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -37,24 +37,12 @@ typedef struct VFIOCCWDevice { bool warned_orb_pfch; } VFIOCCWDevice; -static inline void warn_once(bool *warned, const char *fmt, ...) -{ - va_list ap; - - if (!warned || *warned) { - return; - } - *warned = true; - va_start(ap, fmt); - warn_vreport(fmt, ap); - va_end(ap); -} - static inline void warn_once_pfch(VFIOCCWDevice *vcdev, SubchDev *sch, const char *msg) { - warn_once(&vcdev->warned_orb_pfch, "vfio-ccw (devno %x.%x.%04x): %s", - sch->cssid, sch->ssid, sch->devno, msg); + warn_report_once_cond(&vcdev->warned_orb_pfch, + "vfio-ccw (devno %x.%x.%04x): %s", + sch->cssid, sch->ssid, sch->devno, msg); } static void vfio_ccw_compute_needs_reset(VFIODevice *vdev) @@ -142,8 +130,8 @@ static void vfio_ccw_io_notifier_handler(void *opaque) S390CCWDevice *cdev = S390_CCW_DEVICE(vcdev); CcwDevice *ccw_dev = CCW_DEVICE(cdev); SubchDev *sch = ccw_dev->sch; - SCSW *s = &sch->curr_status.scsw; - PMCW *p = &sch->curr_status.pmcw; + SCHIB *schib = &sch->curr_status; + SCSW s; IRB irb; int size; @@ -157,33 +145,33 @@ static void vfio_ccw_io_notifier_handler(void *opaque) switch (errno) { case ENODEV: /* Generate a deferred cc 3 condition. */ - s->flags |= SCSW_FLAGS_MASK_CC; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); + schib->scsw.flags |= SCSW_FLAGS_MASK_CC; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= (SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND); goto read_err; case EFAULT: /* Memory problem, generate channel data check. */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->cstat = SCSW_CSTAT_DATA_CHECK; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.cstat = SCSW_CSTAT_DATA_CHECK; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; goto read_err; default: /* Error, generate channel program check. */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->cstat = SCSW_CSTAT_PROG_CHECK; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.cstat = SCSW_CSTAT_PROG_CHECK; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; goto read_err; } } else if (size != vcdev->io_region_size) { /* Information transfer error, generate channel-control check. */ - s->ctrl &= ~SCSW_ACTL_START_PEND; - s->cstat = SCSW_CSTAT_CHN_CTRL_CHK; - s->ctrl &= ~SCSW_CTRL_MASK_STCTL; - s->ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | + schib->scsw.ctrl &= ~SCSW_ACTL_START_PEND; + schib->scsw.cstat = SCSW_CSTAT_CHN_CTRL_CHK; + schib->scsw.ctrl &= ~SCSW_CTRL_MASK_STCTL; + schib->scsw.ctrl |= SCSW_STCTL_PRIMARY | SCSW_STCTL_SECONDARY | SCSW_STCTL_ALERT | SCSW_STCTL_STATUS_PEND; goto read_err; } @@ -191,11 +179,13 @@ static void vfio_ccw_io_notifier_handler(void *opaque) memcpy(&irb, region->irb_area, sizeof(IRB)); /* Update control block via irb. */ - copy_scsw_to_guest(s, &irb.scsw); + s = schib->scsw; + copy_scsw_to_guest(&s, &irb.scsw); + schib->scsw = s; /* If a uint check is pending, copy sense data. */ - if ((s->dstat & SCSW_DSTAT_UNIT_CHECK) && - (p->chars & PMCW_CHARS_MASK_CSENSE)) { + if ((schib->scsw.dstat & SCSW_DSTAT_UNIT_CHECK) && + (schib->pmcw.chars & PMCW_CHARS_MASK_CSENSE)) { memcpy(sch->sense_data, irb.ecw, sizeof(irb.ecw)); } @@ -349,6 +339,15 @@ static void vfio_ccw_get_device(VFIOGroup *group, VFIOCCWDevice *vcdev, } } + /* + * All vfio-ccw devices are believed to operate in a way compatible with + * memory ballooning, ie. pages pinned in the host are in the current + * working set of the guest driver and therefore never overlap with pages + * available to the guest balloon driver. This needs to be set before + * vfio_get_device() for vfio common to handle the balloon inhibitor. + */ + vcdev->vdev.balloon_allowed = true; + if (vfio_get_device(group, vcdev->cdev.mdevid, &vcdev->vdev, errp)) { goto out_err; } diff --git a/hw/vfio/common.c b/hw/vfio/common.c index fb396cf00a..4374cc6176 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -32,13 +32,14 @@ #include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/range.h" +#include "sysemu/balloon.h" #include "sysemu/kvm.h" #include "trace.h" #include "qapi/error.h" -struct vfio_group_head vfio_group_list = +VFIOGroupList vfio_group_list = QLIST_HEAD_INITIALIZER(vfio_group_list); -struct vfio_as_head vfio_address_spaces = +static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = QLIST_HEAD_INITIALIZER(vfio_address_spaces); #ifdef CONFIG_KVM @@ -219,7 +220,25 @@ static int vfio_dma_unmap(VFIOContainer *container, .size = size, }; - if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + while (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + /* + * The type1 backend has an off-by-one bug in the kernel (71a7d3d78e3c + * v4.15) where an overflow in its wrap-around check prevents us from + * unmapping the last page of the address space. Test for the error + * condition and re-try the unmap excluding the last page. The + * expectation is that we've never mapped the last page anyway and this + * unmap request comes via vIOMMU support which also makes it unlikely + * that this page is used. This bug was introduced well after type1 v2 + * support was introduced, so we shouldn't need to test for v1. A fix + * is queued for kernel v5.0 so this workaround can be removed once + * affected kernels are sufficiently deprecated. + */ + if (errno == EINVAL && unmap.size && !(unmap.iova + unmap.size) && + container->iommu_type == VFIO_TYPE1v2_IOMMU) { + trace_vfio_dma_unmap_overflow_workaround(); + unmap.size -= 1ULL << ctz64(container->pgsizes); + continue; + } error_report("VFIO_UNMAP_DMA: %d", -errno); return -errno; } @@ -710,7 +729,7 @@ static void vfio_listener_release(VFIOContainer *container) } } -static struct vfio_info_cap_header * +struct vfio_info_cap_header * vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id) { struct vfio_info_cap_header *hdr; @@ -1035,6 +1054,60 @@ static void vfio_put_address_space(VFIOAddressSpace *space) } } +/* + * vfio_get_iommu_type - selects the richest iommu_type (v2 first) + */ +static int vfio_get_iommu_type(VFIOContainer *container, + Error **errp) +{ + int iommu_types[] = { VFIO_TYPE1v2_IOMMU, VFIO_TYPE1_IOMMU, + VFIO_SPAPR_TCE_v2_IOMMU, VFIO_SPAPR_TCE_IOMMU }; + int i; + + for (i = 0; i < ARRAY_SIZE(iommu_types); i++) { + if (ioctl(container->fd, VFIO_CHECK_EXTENSION, iommu_types[i])) { + return iommu_types[i]; + } + } + error_setg(errp, "No available IOMMU models"); + return -EINVAL; +} + +static int vfio_init_container(VFIOContainer *container, int group_fd, + Error **errp) +{ + int iommu_type, ret; + + iommu_type = vfio_get_iommu_type(container, errp); + if (iommu_type < 0) { + return iommu_type; + } + + ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER, &container->fd); + if (ret) { + error_setg_errno(errp, errno, "Failed to set group container"); + return -errno; + } + + while (ioctl(container->fd, VFIO_SET_IOMMU, iommu_type)) { + if (iommu_type == VFIO_SPAPR_TCE_v2_IOMMU) { + /* + * On sPAPR, despite the IOMMU subdriver always advertises v1 and + * v2, the running platform may not support v2 and there is no + * way to guess it until an IOMMU group gets added to the container. + * So in case it fails with v2, try v1 as a fallback. + */ + iommu_type = VFIO_SPAPR_TCE_IOMMU; + continue; + } + error_setg_errno(errp, errno, "Failed to set iommu for container"); + return -errno; + } + + container->iommu_type = iommu_type; + return 0; +} + static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, Error **errp) { @@ -1044,6 +1117,33 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, space = vfio_get_address_space(as); + /* + * VFIO is currently incompatible with memory ballooning insofar as the + * madvise to purge (zap) the page from QEMU's address space does not + * interact with the memory API and therefore leaves stale virtual to + * physical mappings in the IOMMU if the page was previously pinned. We + * therefore add a balloon inhibit for each group added to a container, + * whether the container is used individually or shared. This provides + * us with options to allow devices within a group to opt-in and allow + * ballooning, so long as it is done consistently for a group (for instance + * if the device is an mdev device where it is known that the host vendor + * driver will never pin pages outside of the working set of the guest + * driver, which would thus not be ballooning candidates). + * + * The first opportunity to induce pinning occurs here where we attempt to + * attach the group to existing containers within the AddressSpace. If any + * pages are already zapped from the virtual address space, such as from a + * previous ballooning opt-in, new pinning will cause valid mappings to be + * re-established. Likewise, when the overall MemoryListener for a new + * container is registered, a replay of mappings within the AddressSpace + * will occur, re-establishing any previously zapped pages as well. + * + * NB. Balloon inhibiting does not currently block operation of the + * balloon driver or revoke previously pinned pages, it only prevents + * calling madvise to modify the virtual mapping of ballooned pages. + */ + qemu_balloon_inhibit(true); + QLIST_FOREACH(container, &space->containers, next) { if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { group->container = container; @@ -1073,26 +1173,18 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, container->fd = fd; QLIST_INIT(&container->giommu_list); QLIST_INIT(&container->hostwin_list); - 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); + + ret = vfio_init_container(container, group->fd, errp); + if (ret) { + goto free_container_exit; + } + + switch (container->iommu_type) { + case VFIO_TYPE1v2_IOMMU: + case VFIO_TYPE1_IOMMU: + { struct vfio_iommu_type1_info info; - ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); - if (ret) { - error_setg_errno(errp, errno, "failed to set group container"); - ret = -errno; - goto free_container_exit; - } - - container->iommu_type = v2 ? VFIO_TYPE1v2_IOMMU : VFIO_TYPE1_IOMMU; - ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); - if (ret) { - error_setg_errno(errp, errno, "failed to set iommu for container"); - ret = -errno; - goto free_container_exit; - } - /* * FIXME: This assumes that a Type1 IOMMU can map any 64-bit * IOVA whatsoever. That's not actually true, but the current @@ -1108,30 +1200,14 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, info.iova_pgsizes = 4096; } vfio_host_win_add(container, 0, (hwaddr)-1, info.iova_pgsizes); - } else if (ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_IOMMU) || - ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU)) { + container->pgsizes = info.iova_pgsizes; + break; + } + case VFIO_SPAPR_TCE_v2_IOMMU: + case VFIO_SPAPR_TCE_IOMMU: + { struct vfio_iommu_spapr_tce_info info; - bool v2 = !!ioctl(fd, VFIO_CHECK_EXTENSION, VFIO_SPAPR_TCE_v2_IOMMU); - - ret = ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &fd); - if (ret) { - error_setg_errno(errp, errno, "failed to set group container"); - ret = -errno; - goto free_container_exit; - } - container->iommu_type = - v2 ? VFIO_SPAPR_TCE_v2_IOMMU : VFIO_SPAPR_TCE_IOMMU; - ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); - if (ret) { - container->iommu_type = VFIO_SPAPR_TCE_IOMMU; - v2 = false; - ret = ioctl(fd, VFIO_SET_IOMMU, container->iommu_type); - } - if (ret) { - error_setg_errno(errp, errno, "failed to set iommu for container"); - ret = -errno; - goto free_container_exit; - } + bool v2 = container->iommu_type == VFIO_SPAPR_TCE_v2_IOMMU; /* * The host kernel code implementing VFIO_IOMMU_DISABLE is called @@ -1172,6 +1248,7 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, } if (v2) { + container->pgsizes = info.ddw.pgsizes; /* * There is a default window in just created container. * To make region_add/del simpler, we better remove this @@ -1186,15 +1263,13 @@ static int vfio_connect_container(VFIOGroup *group, AddressSpace *as, } } else { /* The default table uses 4K pages */ + container->pgsizes = 0x1000; vfio_host_win_add(container, info.dma32_window_start, info.dma32_window_start + info.dma32_window_size - 1, 0x1000); } - } else { - error_setg(errp, "No available IOMMU models"); - ret = -EINVAL; - goto free_container_exit; + } } vfio_kvm_device_add_group(group); @@ -1232,6 +1307,7 @@ close_fd_exit: close(fd); put_space_exit: + qemu_balloon_inhibit(false); vfio_put_address_space(space); return ret; @@ -1352,6 +1428,9 @@ void vfio_put_group(VFIOGroup *group) return; } + if (!group->balloon_allowed) { + qemu_balloon_inhibit(false); + } vfio_kvm_device_del_group(group); vfio_disconnect_container(group); QLIST_REMOVE(group, next); @@ -1387,6 +1466,26 @@ int vfio_get_device(VFIOGroup *group, const char *name, return ret; } + /* + * Clear the balloon inhibitor for this group if the driver knows the + * device operates compatibly with ballooning. Setting must be consistent + * per group, but since compatibility is really only possible with mdev + * currently, we expect singleton groups. + */ + if (vbasedev->balloon_allowed != group->balloon_allowed) { + if (!QLIST_EMPTY(&group->device_list)) { + error_setg(errp, + "Inconsistent device balloon setting within group"); + close(fd); + return -1; + } + + if (!group->balloon_allowed) { + group->balloon_allowed = true; + qemu_balloon_inhibit(false); + } + } + vbasedev->fd = fd; vbasedev->group = group; QLIST_INSERT_HEAD(&group->device_list, vbasedev, next); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 59c0e5d1d7..a3d9c8f5be 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -15,15 +15,181 @@ #include #include "sysemu/sysemu.h" +#include "hw/display/edid.h" #include "ui/console.h" #include "qapi/error.h" #include "pci.h" +#include "trace.h" #ifndef DRM_PLANE_TYPE_PRIMARY # define DRM_PLANE_TYPE_PRIMARY 1 # define DRM_PLANE_TYPE_CURSOR 2 #endif +#define pread_field(_fd, _reg, _ptr, _fld) \ + (sizeof(_ptr->_fld) != \ + pread(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ + _reg->offset + offsetof(typeof(*_ptr), _fld))) + +#define pwrite_field(_fd, _reg, _ptr, _fld) \ + (sizeof(_ptr->_fld) != \ + pwrite(_fd, &(_ptr->_fld), sizeof(_ptr->_fld), \ + _reg->offset + offsetof(typeof(*_ptr), _fld))) + + +static void vfio_display_edid_link_up(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + VFIODisplay *dpy = vdev->dpy; + int fd = vdev->vbasedev.fd; + + dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_UP; + if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { + goto err; + } + trace_vfio_display_edid_link_up(); + return; + +err: + trace_vfio_display_edid_write_error(); +} + +static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled, + int prefx, int prefy) +{ + VFIODisplay *dpy = vdev->dpy; + int fd = vdev->vbasedev.fd; + qemu_edid_info edid = { + .maxx = dpy->edid_regs->max_xres, + .maxy = dpy->edid_regs->max_yres, + .prefx = prefx ?: vdev->display_xres, + .prefy = prefy ?: vdev->display_yres, + }; + + timer_del(dpy->edid_link_timer); + dpy->edid_regs->link_state = VFIO_DEVICE_GFX_LINK_STATE_DOWN; + if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, link_state)) { + goto err; + } + trace_vfio_display_edid_link_down(); + + if (!enabled) { + return; + } + + if (edid.maxx && edid.prefx > edid.maxx) { + edid.prefx = edid.maxx; + } + if (edid.maxy && edid.prefy > edid.maxy) { + edid.prefy = edid.maxy; + } + qemu_edid_generate(dpy->edid_blob, + dpy->edid_regs->edid_max_size, + &edid); + trace_vfio_display_edid_update(edid.prefx, edid.prefy); + + dpy->edid_regs->edid_size = qemu_edid_size(dpy->edid_blob); + if (pwrite_field(fd, dpy->edid_info, dpy->edid_regs, edid_size)) { + goto err; + } + if (pwrite(fd, dpy->edid_blob, dpy->edid_regs->edid_size, + dpy->edid_info->offset + dpy->edid_regs->edid_offset) + != dpy->edid_regs->edid_size) { + goto err; + } + + timer_mod(dpy->edid_link_timer, + qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 100); + return; + +err: + trace_vfio_display_edid_write_error(); + return; +} + +static int vfio_display_edid_ui_info(void *opaque, uint32_t idx, + QemuUIInfo *info) +{ + VFIOPCIDevice *vdev = opaque; + VFIODisplay *dpy = vdev->dpy; + + if (!dpy->edid_regs) { + return 0; + } + + if (info->width && info->height) { + vfio_display_edid_update(vdev, true, info->width, info->height); + } else { + vfio_display_edid_update(vdev, false, 0, 0); + } + + return 0; +} + +static void vfio_display_edid_init(VFIOPCIDevice *vdev) +{ + VFIODisplay *dpy = vdev->dpy; + int fd = vdev->vbasedev.fd; + int ret; + + ret = vfio_get_dev_region_info(&vdev->vbasedev, + VFIO_REGION_TYPE_GFX, + VFIO_REGION_SUBTYPE_GFX_EDID, + &dpy->edid_info); + if (ret) { + return; + } + + trace_vfio_display_edid_available(); + dpy->edid_regs = g_new0(struct vfio_region_gfx_edid, 1); + if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_offset)) { + goto err; + } + if (pread_field(fd, dpy->edid_info, dpy->edid_regs, edid_max_size)) { + goto err; + } + if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_xres)) { + goto err; + } + if (pread_field(fd, dpy->edid_info, dpy->edid_regs, max_yres)) { + goto err; + } + + dpy->edid_blob = g_malloc0(dpy->edid_regs->edid_max_size); + + /* if xres + yres properties are unset use the maximum resolution */ + if (!vdev->display_xres) { + vdev->display_xres = dpy->edid_regs->max_xres; + } + if (!vdev->display_yres) { + vdev->display_yres = dpy->edid_regs->max_yres; + } + + dpy->edid_link_timer = timer_new_ms(QEMU_CLOCK_REALTIME, + vfio_display_edid_link_up, vdev); + + vfio_display_edid_update(vdev, true, 0, 0); + return; + +err: + trace_vfio_display_edid_write_error(); + g_free(dpy->edid_regs); + dpy->edid_regs = NULL; + return; +} + +static void vfio_display_edid_exit(VFIODisplay *dpy) +{ + if (!dpy->edid_regs) { + return; + } + + g_free(dpy->edid_regs); + g_free(dpy->edid_blob); + timer_del(dpy->edid_link_timer); + timer_free(dpy->edid_link_timer); +} + static void vfio_display_update_cursor(VFIODMABuf *dmabuf, struct vfio_device_gfx_plane_info *plane) { @@ -124,6 +290,9 @@ static void vfio_display_dmabuf_update(void *opaque) primary = vfio_display_get_dmabuf(vdev, DRM_PLANE_TYPE_PRIMARY); if (primary == NULL) { + if (dpy->ramfb) { + ramfb_display_update(dpy->con, dpy->ramfb); + } return; } @@ -168,6 +337,7 @@ static void vfio_display_dmabuf_update(void *opaque) static const GraphicHwOps vfio_display_dmabuf_ops = { .gfx_update = vfio_display_dmabuf_update, + .ui_info = vfio_display_edid_ui_info, }; static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) @@ -181,6 +351,10 @@ static int vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, &vfio_display_dmabuf_ops, vdev); + if (vdev->enable_ramfb) { + vdev->dpy->ramfb = ramfb_setup(errp); + } + vfio_display_edid_init(vdev); return 0; } @@ -228,6 +402,9 @@ static void vfio_display_region_update(void *opaque) return; } if (!plane.drm_format || !plane.size) { + if (dpy->ramfb) { + ramfb_display_update(dpy->con, dpy->ramfb); + } return; } format = qemu_drm_format_to_pixman(plane.drm_format); @@ -300,6 +477,9 @@ static int vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) vdev->dpy->con = graphic_console_init(DEVICE(vdev), 0, &vfio_display_region_ops, vdev); + if (vdev->enable_ramfb) { + vdev->dpy->ramfb = ramfb_setup(errp); + } return 0; } @@ -354,5 +534,6 @@ void vfio_display_finalize(VFIOPCIDevice *vdev) graphic_console_close(vdev->dpy->con); vfio_display_dmabuf_exit(vdev->dpy); vfio_display_region_exit(vdev->dpy); + vfio_display_edid_exit(vdev->dpy); g_free(vdev->dpy); } diff --git a/hw/vfio/pci-quirks.c b/hw/vfio/pci-quirks.c index 481fd08df7..40a12001f5 100644 --- a/hw/vfio/pci-quirks.c +++ b/hw/vfio/pci-quirks.c @@ -526,8 +526,6 @@ static void vfio_probe_ati_bar2_quirk(VFIOPCIDevice *vdev, int nr) * 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: @@ -1670,7 +1668,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) * but also no point in us enabling VGA if disabled in hardware. */ if (!(gmch & 0x2) && !vdev->vga && vfio_populate_vga(vdev, &err)) { - error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name); + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); error_report("IGD device %s failed to enable VGA access, " "legacy mode disabled", vdev->vbasedev.name); goto out; @@ -1696,7 +1694,7 @@ static void vfio_probe_igd_bar4_quirk(VFIOPCIDevice *vdev, int nr) ret = vfio_pci_igd_opregion_init(vdev, opregion, &err); if (ret) { error_append_hint(&err, "IGD legacy mode disabled\n"); - error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name); + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); goto out; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 6cbb8fa054..504019c458 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -37,6 +37,9 @@ #define MSIX_CAP_LENGTH 12 +#define TYPE_VFIO_PCI "vfio-pci" +#define PCI_VFIO(obj) OBJECT_CHECK(VFIOPCIDevice, obj, TYPE_VFIO_PCI) + static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); @@ -222,7 +225,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) static void vfio_intx_update(PCIDevice *pdev) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); PCIINTxRoute route; Error *err = NULL; @@ -249,7 +252,7 @@ static void vfio_intx_update(PCIDevice *pdev) vfio_intx_enable_kvm(vdev, &err); if (err) { - error_reportf_err(err, WARN_PREFIX, vdev->vbasedev.name); + warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } /* Re-enable the interrupt in cased we missed an EOI */ @@ -314,7 +317,7 @@ static int vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) vfio_intx_enable_kvm(vdev, &err); if (err) { - error_reportf_err(err, WARN_PREFIX, vdev->vbasedev.name); + warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } vdev->interrupt = VFIO_INT_INTx; @@ -477,7 +480,7 @@ static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, MSIMessage *msg, IOHandler *handler) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); VFIOMSIVector *vector; int ret; @@ -574,7 +577,7 @@ static int vfio_msix_vector_use(PCIDevice *pdev, static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); VFIOMSIVector *vector = &vdev->msi_vectors[nr]; trace_vfio_msix_vector_release(vdev->vbasedev.name, nr); @@ -742,7 +745,7 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) vfio_intx_enable(vdev, &err); if (err) { - error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name); + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } } @@ -1086,7 +1089,7 @@ static const MemoryRegionOps vfio_vga_ops = { */ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); VFIORegion *region = &vdev->bars[bar].region; MemoryRegion *mmap_mr, *region_mr, *base_mr; PCIIORegion *r; @@ -1132,7 +1135,7 @@ static void vfio_sub_page_bar_update_mapping(PCIDevice *pdev, int bar) */ uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); uint32_t emu_bits = 0, emu_val = 0, phys_val = 0, val; memcpy(&emu_bits, vdev->emulated_config_bits + addr, len); @@ -1165,7 +1168,7 @@ 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) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); uint32_t val_le = cpu_to_le32(val); trace_vfio_pci_write_config(vdev->vbasedev.name, addr, val, len); @@ -1280,8 +1283,7 @@ static int vfio_msi_setup(VFIOPCIDevice *vdev, int pos, Error **errp) if (ret == -ENOTSUP) { return 0; } - error_prepend(&err, "msi_init failed: "); - error_propagate(errp, err); + error_propagate_prepend(errp, err, "msi_init failed: "); return ret; } vdev->msi_cap_size = 0xa + (msi_maskbit ? 0xa : 0) + (msi_64bit ? 0x4 : 0); @@ -1555,7 +1557,7 @@ static int vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) &err); if (ret < 0) { if (ret == -ENOTSUP) { - error_report_err(err); + warn_report_err(err); return 0; } @@ -1895,15 +1897,10 @@ static int vfio_setup_pcie_cap(VFIOPCIDevice *vdev, int pos, uint8_t size, PCI_EXP_TYPE_ENDPOINT << 4, PCI_EXP_FLAGS_TYPE); vfio_add_emulated_long(vdev, pos + PCI_EXP_LNKCAP, - PCI_EXP_LNK_MLW_1 | PCI_EXP_LNK_LS_25, ~0); + QEMU_PCI_EXP_LNKCAP_MLW(QEMU_PCI_EXP_LNK_X1) | + QEMU_PCI_EXP_LNKCAP_MLS(QEMU_PCI_EXP_LNK_2_5GT), ~0); vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKCTL, 0, ~0); } - - /* Mark the Link Status bits as emulated to allow virtual negotiation */ - vfio_add_emulated_word(vdev, pos + PCI_EXP_LNKSTA, - pci_get_word(vdev->pdev.config + pos + - PCI_EXP_LNKSTA), - PCI_EXP_LNKCAP_MLW | PCI_EXP_LNKCAP_SLS); } /* @@ -2194,7 +2191,7 @@ static void vfio_pci_post_reset(VFIOPCIDevice *vdev) vfio_intx_enable(vdev, &err); if (err) { - error_reportf_err(err, ERR_PREFIX, vdev->vbasedev.name); + error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } for (nr = 0; nr < PCI_NUM_REGIONS - 1; ++nr) { @@ -2584,13 +2581,13 @@ static void vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) ret = ioctl(vdev->vbasedev.fd, VFIO_DEVICE_GET_IRQ_INFO, &irq_info); if (ret) { /* This can fail for an old kernel or legacy PCI dev */ - trace_vfio_populate_device_get_irq_info_failure(); + trace_vfio_populate_device_get_irq_info_failure(strerror(errno)); } else if (irq_info.count == 1) { vdev->pci_aer = true; } else { - error_report(WARN_PREFIX - "Could not enable error recovery for the device", - vbasedev->name); + warn_report(VFIO_MSG_PREFIX + "Could not enable error recovery for the device", + vbasedev->name); } } @@ -2715,7 +2712,7 @@ static void vfio_req_notifier_handler(void *opaque) qdev_unplug(&vdev->pdev.qdev, &err); if (err) { - error_reportf_err(err, WARN_PREFIX, vdev->vbasedev.name); + warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } } @@ -2801,15 +2798,16 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) static void vfio_realize(PCIDevice *pdev, Error **errp) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); VFIODevice *vbasedev_iter; VFIOGroup *group; - char *tmp, group_path[PATH_MAX], *group_name; + char *tmp, *subsys, group_path[PATH_MAX], *group_name; Error *err = NULL; ssize_t len; struct stat st; int groupid; int i, ret; + bool is_mdev; if (!vdev->vbasedev.sysfsdev) { if (!(~vdev->host.domain || ~vdev->host.bus || @@ -2827,7 +2825,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) if (stat(vdev->vbasedev.sysfsdev, &st) < 0) { error_setg_errno(errp, errno, "no such host device"); - error_prepend(errp, ERR_PREFIX, vdev->vbasedev.sysfsdev); + error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.sysfsdev); return; } @@ -2869,6 +2867,27 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) } } + /* + * Mediated devices *might* operate compatibly with memory ballooning, but + * we cannot know for certain, it depends on whether the mdev vendor driver + * stays in sync with the active working set of the guest driver. Prevent + * the x-balloon-allowed option unless this is minimally an mdev device. + */ + tmp = g_strdup_printf("%s/subsystem", vdev->vbasedev.sysfsdev); + subsys = realpath(tmp, NULL); + g_free(tmp); + is_mdev = subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); + free(subsys); + + trace_vfio_mdev(vdev->vbasedev.name, is_mdev); + + if (vdev->vbasedev.balloon_allowed && !is_mdev) { + error_setg(errp, "x-balloon-allowed only potentially compatible " + "with mdev devices"); + vfio_put_group(group); + goto error; + } + ret = vfio_get_device(group, vdev->vbasedev.name, &vdev->vbasedev, errp); if (ret) { vfio_put_group(group); @@ -3045,6 +3064,20 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto out_teardown; } } + if (vdev->enable_ramfb && vdev->dpy == NULL) { + error_setg(errp, "ramfb=on requires display=on"); + goto out_teardown; + } + if (vdev->display_xres || vdev->display_yres) { + if (vdev->dpy == NULL) { + error_setg(errp, "xres and yres properties require display=on"); + goto out_teardown; + } + if (vdev->dpy->edid_regs == NULL) { + error_setg(errp, "xres and yres properties need edid support"); + goto out_teardown; + } + } vfio_register_err_notifier(vdev); vfio_register_req_notifier(vdev); @@ -3057,13 +3090,12 @@ out_teardown: vfio_teardown_msi(vdev); vfio_bars_exit(vdev); error: - error_prepend(errp, ERR_PREFIX, vdev->vbasedev.name); + error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); } static void vfio_instance_finalize(Object *obj) { - PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pci_dev); + VFIOPCIDevice *vdev = PCI_VFIO(obj); VFIOGroup *group = vdev->vbasedev.group; vfio_display_finalize(vdev); @@ -3083,7 +3115,7 @@ static void vfio_instance_finalize(Object *obj) static void vfio_exitfn(PCIDevice *pdev) { - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(pdev); vfio_unregister_req_notifier(vdev); vfio_unregister_err_notifier(vdev); @@ -3098,8 +3130,7 @@ static void vfio_exitfn(PCIDevice *pdev) static void vfio_pci_reset(DeviceState *dev) { - PCIDevice *pdev = DO_UPCAST(PCIDevice, qdev, dev); - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, pdev); + VFIOPCIDevice *vdev = PCI_VFIO(dev); trace_vfio_pci_reset(vdev->vbasedev.name); @@ -3139,7 +3170,7 @@ post_reset: static void vfio_instance_init(Object *obj) { PCIDevice *pci_dev = PCI_DEVICE(obj); - VFIOPCIDevice *vdev = DO_UPCAST(VFIOPCIDevice, pdev, PCI_DEVICE(obj)); + VFIOPCIDevice *vdev = PCI_VFIO(obj); device_add_bootindex_property(obj, &vdev->bootindex, "bootindex", NULL, @@ -3161,6 +3192,8 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_STRING("sysfsdev", VFIOPCIDevice, vbasedev.sysfsdev), DEFINE_PROP_ON_OFF_AUTO("display", VFIOPCIDevice, display, ON_OFF_AUTO_OFF), + DEFINE_PROP_UINT32("xres", VFIOPCIDevice, display_xres, 0), + DEFINE_PROP_UINT32("yres", VFIOPCIDevice, display_yres, 0), DEFINE_PROP_UINT32("x-intx-mmap-timeout-ms", VFIOPCIDevice, intx.mmap_timeout, 1100), DEFINE_PROP_BIT("x-vga", VFIOPCIDevice, features, @@ -3170,6 +3203,8 @@ static Property vfio_pci_dev_properties[] = { DEFINE_PROP_BIT("x-igd-opregion", VFIOPCIDevice, features, VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), + DEFINE_PROP_BOOL("x-balloon-allowed", VFIOPCIDevice, + vbasedev.balloon_allowed, 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), @@ -3221,7 +3256,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) } static const TypeInfo vfio_pci_dev_info = { - .name = "vfio-pci", + .name = TYPE_VFIO_PCI, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(VFIOPCIDevice), .class_init = vfio_pci_dev_class_init, @@ -3234,9 +3269,30 @@ static const TypeInfo vfio_pci_dev_info = { }, }; +static Property vfio_pci_dev_nohotplug_properties[] = { + DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->props = vfio_pci_dev_nohotplug_properties; + dc->hotpluggable = false; +} + +static const TypeInfo vfio_pci_nohotplug_dev_info = { + .name = "vfio-pci-nohotplug", + .parent = "vfio-pci", + .instance_size = sizeof(VFIOPCIDevice), + .class_init = vfio_pci_nohotplug_dev_class_init, +}; + static void register_vfio_pci_dev_type(void) { type_register_static(&vfio_pci_dev_info); + type_register_static(&vfio_pci_nohotplug_dev_info); } type_init(register_vfio_pci_dev_type) diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 52b065421a..c11c3f1670 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -149,6 +149,8 @@ typedef struct VFIOPCIDevice { #define VFIO_FEATURE_ENABLE_IGD_OPREGION \ (1 << VFIO_FEATURE_ENABLE_IGD_OPREGION_BIT) OnOffAuto display; + uint32_t display_xres; + uint32_t display_yres; int32_t bootindex; uint32_t igd_gms; OffAutoPCIBAR msix_relo; @@ -165,6 +167,7 @@ typedef struct VFIOPCIDevice { bool no_geforce_quirks; bool no_kvm_ioeventfd; bool no_vfio_ioeventfd; + bool enable_ramfb; VFIODisplay *dpy; } VFIOPCIDevice; diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 57c4a0ee2b..398db38f14 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -655,10 +655,32 @@ static void vfio_platform_realize(DeviceState *dev, Error **errp) goto out; } + if (!vdev->compat) { + GError *gerr = NULL; + gchar *contents; + gsize length; + char *path; + + path = g_strdup_printf("%s/of_node/compatible", vbasedev->sysfsdev); + if (!g_file_get_contents(path, &contents, &length, &gerr)) { + error_setg(errp, "%s", gerr->message); + g_error_free(gerr); + g_free(path); + return; + } + g_free(path); + vdev->compat = contents; + for (vdev->num_compat = 0; length; vdev->num_compat++) { + size_t skip = strlen(contents) + 1; + contents += skip; + length -= skip; + } + } + for (i = 0; i < vbasedev->num_regions; i++) { if (vfio_region_mmap(vdev->regions[i])) { - error_report("%s mmap unsupported. Performance may be slow", - memory_region_name(vdev->regions[i]->mem)); + warn_report("%s mmap unsupported, performance may be slow", + memory_region_name(vdev->regions[i]->mem)); } sysbus_init_mmio(sbdev, vdev->regions[i]->mem); } @@ -668,7 +690,7 @@ out: } if (vdev->vbasedev.name) { - error_prepend(errp, ERR_PREFIX, vdev->vbasedev.name); + error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); } else { error_prepend(errp, "vfio error: "); } @@ -700,6 +722,8 @@ static void vfio_platform_class_init(ObjectClass *klass, void *data) dc->desc = "VFIO-based platform device assignment"; sbc->connect_irq_notifier = vfio_start_irqfd_injection; set_bit(DEVICE_CATEGORY_MISC, dc->categories); + /* Supported by TYPE_VIRT_MACHINE */ + dc->user_creatable = true; } static const TypeInfo vfio_platform_dev_info = { @@ -708,7 +732,6 @@ static const TypeInfo vfio_platform_dev_info = { .instance_size = sizeof(VFIOPlatformDevice), .class_init = vfio_platform_class_init, .class_size = sizeof(VFIOPlatformDeviceClass), - .abstract = true, }; static void register_vfio_platform_dev_type(void) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 259397c002..57fe758e54 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -15,6 +15,7 @@ #include "hw/vfio/vfio-common.h" #include "hw/hw.h" +#include "exec/ram_addr.h" #include "qemu/error-report.h" #include "trace.h" @@ -142,11 +143,29 @@ int vfio_spapr_create_window(VFIOContainer *container, MemoryRegionSection *section, hwaddr *pgsize) { - int ret; + int ret = 0; IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); - unsigned pagesize = memory_region_iommu_get_min_page_size(iommu_mr); - unsigned entries, pages; + uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr); + unsigned entries, bits_total, bits_per_level, max_levels; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; + long rampagesize = qemu_getrampagesize(); + + /* + * The host might not support the guest supported IOMMU page size, + * so we will use smaller physical IOMMU pages to back them. + */ + if (pagesize > rampagesize) { + pagesize = rampagesize; + } + pagesize = 1ULL << (63 - clz64(container->pgsizes & + (pagesize | (pagesize - 1)))); + if (!pagesize) { + error_report("Host doesn't support page size 0x%"PRIx64 + ", the supported mask is 0x%lx", + memory_region_iommu_get_min_page_size(iommu_mr), + container->pgsizes); + return -EINVAL; + } /* * FIXME: For VFIO iommu types which have KVM acceleration to @@ -157,16 +176,38 @@ int vfio_spapr_create_window(VFIOContainer *container, create.window_size = int128_get64(section->size); create.page_shift = ctz64(pagesize); /* - * SPAPR host supports multilevel TCE tables, there is some - * heuristic to decide how many levels we want for our table: - * 0..64 = 1; 65..4096 = 2; 4097..262144 = 3; 262145.. = 4 + * SPAPR host supports multilevel TCE tables. We try to guess optimal + * levels number and if this fails (for example due to the host memory + * fragmentation), we increase levels. The DMA address structure is: + * rrrrrrrr rxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx iiiiiiii + * where: + * r = reserved (bits >= 55 are reserved in the existing hardware) + * i = IOMMU page offset (64K in this example) + * x = bits to index a TCE which can be split to equal chunks to index + * within the level. + * The aim is to split "x" to smaller possible number of levels. */ entries = create.window_size >> create.page_shift; - pages = MAX((entries * sizeof(uint64_t)) / getpagesize(), 1); - pages = MAX(pow2ceil(pages), 1); /* Round up */ - create.levels = ctz64(pages) / 6 + 1; - - ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); + /* bits_total is number of "x" needed */ + bits_total = ctz64(entries * sizeof(uint64_t)); + /* + * bits_per_level is a safe guess of how much we can allocate per level: + * 8 is the current minimum for CONFIG_FORCE_MAX_ZONEORDER and MAX_ORDER + * is usually bigger than that. + * Below we look at getpagesize() as TCEs are allocated from system pages. + */ + bits_per_level = ctz64(getpagesize()) + 8; + create.levels = bits_total / bits_per_level; + if (bits_total % bits_per_level) { + ++create.levels; + } + max_levels = (64 - create.page_shift) / ctz64(getpagesize()); + for ( ; create.levels <= max_levels; ++create.levels) { + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); + if (!ret) { + break; + } + } if (ret) { error_report("Failed to create a window, ret = %d (%m)", ret); return -errno; @@ -181,6 +222,7 @@ int vfio_spapr_create_window(VFIOContainer *container, return -EINVAL; } trace_vfio_spapr_create_window(create.page_shift, + create.levels, create.window_size, create.start_addr); *pgsize = pagesize; diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index d2a74952e3..eb589930a5 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/vfio/pci.c +# pci.c vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" vfio_intx_eoi(const char *name) " (%s) EOI" vfio_intx_enable_kvm(const char *name) " (%s) KVM INTx accel enabled" @@ -16,7 +16,6 @@ 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_msix_relo_cost(const char *name, int bar, uint64_t cost) " (%s) BAR %d cost 0x%"PRIx64"" vfio_msix_relo(const char *name, int bar, uint64_t offset) " (%s) BAR %d offset 0x%"PRIx64"" vfio_msi_enable(const char *name, int nr_vectors) " (%s) Enabled %d MSI vectors" vfio_msi_disable(const char *name) " (%s)" @@ -37,8 +36,9 @@ vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent de 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_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_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" vfio_realize(const char *name, int group_id) " (%s) group %d" +vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d" vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x" vfio_pci_reset(const char *name) " (%s)" vfio_pci_reset_flr(const char *name) "%s FLR/VFIO_DEVICE_RESET" @@ -48,7 +48,7 @@ vfio_pci_emulated_device_id(const char *name, uint16_t val) "%s 0x%04x" vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s 0x%04x" vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s 0x%04x" -# hw/vfio/pci-quirks.c +# pci-quirks.c 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 @@ -86,11 +86,12 @@ vfio_pci_igd_opregion_enabled(const char *name) "%s" vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" -# hw/vfio/common.c +# 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)" vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_add_skip(uint64_t start, uint64_t end) "SKIPPING region_add 0x%"PRIx64" - 0x%"PRIx64 +vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" vfio_listener_region_add_iommu(uint64_t start, uint64_t end) "region_add [iommu] 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_add_ram(uint64_t iova_start, uint64_t iova_end, void *vaddr) "region_add [ram] 0x%"PRIx64" - 0x%"PRIx64" [%p]" vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t size, uint64_t page_size) "Region \"%s\" 0x%"PRIx64" size=0x%"PRIx64" is not aligned to 0x%"PRIx64" and cannot be mapped for DMA" @@ -109,8 +110,9 @@ vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps e vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%0x8" +vfio_dma_unmap_overflow_workaround(void) "" -# hw/vfio/platform.c +# platform.c 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)" @@ -122,11 +124,17 @@ vfio_intp_interrupt_set_pending(int index) "irq %d is set PENDING" 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/vfio/spapr.c +# spapr.c vfio_prereg_listener_region_add_skip(uint64_t start, uint64_t end) "0x%"PRIx64" - 0x%"PRIx64 vfio_prereg_listener_region_del_skip(uint64_t start, uint64_t end) "0x%"PRIx64" - 0x%"PRIx64 vfio_prereg_register(uint64_t va, uint64_t size, int ret) "va=0x%"PRIx64" size=0x%"PRIx64" ret=%d" vfio_prereg_unregister(uint64_t va, uint64_t size, int ret) "va=0x%"PRIx64" size=0x%"PRIx64" ret=%d" -vfio_spapr_create_window(int ps, uint64_t ws, uint64_t off) "pageshift=0x%x winsize=0x%"PRIx64" offset=0x%"PRIx64 +vfio_spapr_create_window(int ps, unsigned int levels, uint64_t ws, uint64_t off) "pageshift=0x%x levels=%u winsize=0x%"PRIx64" offset=0x%"PRIx64 vfio_spapr_remove_window(uint64_t off) "offset=0x%"PRIx64 -vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" + +# display.c +vfio_display_edid_available(void) "" +vfio_display_edid_link_up(void) "" +vfio_display_edid_link_down(void) "" +vfio_display_edid_update(uint32_t prefx, uint32_t prefy) "%ux%u" +vfio_display_edid_write_error(void) "" diff --git a/hw/virtio/Kconfig b/hw/virtio/Kconfig new file mode 100644 index 0000000000..e0452de4ba --- /dev/null +++ b/hw/virtio/Kconfig @@ -0,0 +1,31 @@ +config VIRTIO + bool + +config VIRTIO_RNG + bool + default y + depends on VIRTIO + +config VIRTIO_PCI + bool + default y if PCI_DEVICES + depends on PCI + select VIRTIO + +config VIRTIO_MMIO + bool + select VIRTIO + +config VIRTIO_CCW + bool + select VIRTIO + +config VIRTIO_BALLOON + bool + default y + depends on VIRTIO + +config VIRTIO_CRYPTO + bool + default y + depends on VIRTIO diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index 1b2799cfd8..f2ab667a21 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -2,16 +2,35 @@ ifeq ($(CONFIG_VIRTIO),y) common-obj-y += virtio-bus.o obj-y += virtio.o +obj-$(call lor,$(CONFIG_VHOST_USER),$(CONFIG_VHOST_KERNEL)) += vhost.o vhost-backend.o +common-obj-$(call lnot,$(call lor,$(CONFIG_VHOST_USER),$(CONFIG_VHOST_KERNEL))) += vhost-stub.o +obj-$(CONFIG_VHOST_USER) += vhost-user.o + common-obj-$(CONFIG_VIRTIO_RNG) += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-$(CONFIG_VIRTIO_MMIO) += virtio-mmio.o obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon.o obj-$(CONFIG_VIRTIO_CRYPTO) += virtio-crypto.o obj-$(call land,$(CONFIG_VIRTIO_CRYPTO),$(CONFIG_VIRTIO_PCI)) += virtio-crypto-pci.o - -obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock.o + +ifeq ($(CONFIG_VIRTIO_PCI),y) +obj-$(CONFIG_VHOST_VSOCK) += vhost-vsock-pci.o +obj-$(CONFIG_VHOST_USER_BLK) += vhost-user-blk-pci.o +obj-$(CONFIG_VHOST_USER_SCSI) += vhost-user-scsi-pci.o +obj-$(CONFIG_VHOST_SCSI) += vhost-scsi-pci.o +obj-$(CONFIG_VIRTIO_INPUT_HOST) += virtio-input-host-pci.o +obj-$(CONFIG_VIRTIO_INPUT) += virtio-input-pci.o +obj-$(CONFIG_VIRTIO_RNG) += virtio-rng-pci.o +obj-$(CONFIG_VIRTIO_BALLOON) += virtio-balloon-pci.o +obj-$(CONFIG_VIRTIO_9P) += virtio-9p-pci.o +obj-$(CONFIG_VIRTIO_SCSI) += virtio-scsi-pci.o +obj-$(CONFIG_VIRTIO_BLK) += virtio-blk-pci.o +obj-$(CONFIG_VIRTIO_NET) += virtio-net-pci.o +obj-$(CONFIG_VIRTIO_SERIAL) += virtio-serial-pci.o +endif +else +common-obj-y += vhost-stub.o endif -common-obj-$(call lnot,$(call land,$(CONFIG_VIRTIO),$(CONFIG_LINUX))) += vhost-stub.o common-obj-$(CONFIG_ALL) += vhost-stub.o diff --git a/hw/virtio/trace-events b/hw/virtio/trace-events index 07bcbe9e85..60c649c4bc 100644 --- a/hw/virtio/trace-events +++ b/hw/virtio/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# hw/virtio/vhost.c +# vhost.c vhost_commit(bool started, bool changed) "Started: %d Changed: %d" vhost_region_add_section(const char *name, uint64_t gpa, uint64_t size, uint64_t host) "%s: 0x%"PRIx64"+0x%"PRIx64" @ 0x%"PRIx64 vhost_region_add_section_merge(const char *name, uint64_t new_size, uint64_t gpa, uint64_t owr) "%s: size: 0x%"PRIx64 " gpa: 0x%"PRIx64 " owr: 0x%"PRIx64 @@ -8,7 +8,7 @@ vhost_region_add_section_aligned(const char *name, uint64_t gpa, uint64_t size, vhost_section(const char *name, int r) "%s:%d" vhost_iotlb_miss(void *dev, int step) "%p step %d" -# hw/virtio/vhost-user.c +# vhost-user.c vhost_user_postcopy_end_entry(void) "" vhost_user_postcopy_end_exit(void) "" vhost_user_postcopy_fault_handler(const char *name, uint64_t fault_address, int nregions) "%s: @0x%"PRIx64" nregions:%d" @@ -21,7 +21,7 @@ vhost_user_postcopy_waker(const char *rb, uint64_t rb_offset) "%s + 0x%"PRIx64 vhost_user_postcopy_waker_found(uint64_t client_addr) "0x%"PRIx64 vhost_user_postcopy_waker_nomatch(const char *rb, uint64_t rb_offset) "%s + 0x%"PRIx64 -# hw/virtio/virtio.c +# virtio.c virtqueue_alloc_element(void *elem, size_t sz, unsigned in_num, unsigned out_num) "elem %p size %zd in_num %u out_num %u" virtqueue_fill(void *vq, const void *elem, unsigned int len, unsigned int idx) "vq %p elem %p len %u idx %u" virtqueue_flush(void *vq, unsigned int count) "vq %p count %u" @@ -31,7 +31,7 @@ virtio_notify_irqfd(void *vdev, void *vq) "vdev %p vq %p" virtio_notify(void *vdev, void *vq) "vdev %p vq %p" virtio_set_status(void *vdev, uint8_t val) "vdev %p val %u" -# hw/virtio/virtio-rng.c +# virtio-rng.c virtio_rng_guest_not_ready(void *rng) "rng %p: guest not ready" virtio_rng_cpu_is_stopped(void *rng, int size) "rng %p: cpu is stopped, dropping %d bytes" virtio_rng_popped(void *rng) "rng %p: elem popped" @@ -39,7 +39,7 @@ virtio_rng_pushed(void *rng, size_t len) "rng %p: %zd bytes pushed" virtio_rng_request(void *rng, size_t size, unsigned quota) "rng %p: %zd bytes requested, %u bytes quota left" virtio_rng_vm_state_change(void *rng, int running, int state) "rng %p: state change to running %d state %d" -# hw/virtio/virtio-balloon.c +# virtio-balloon.c # virtio_balloon_bad_addr(uint64_t gpa) "0x%"PRIx64 virtio_balloon_handle_output(const char *name, uint64_t gpa) "section name: %s gpa: 0x%"PRIx64 diff --git a/hw/virtio/vhost-backend.c b/hw/virtio/vhost-backend.c index 7f09efab8b..96b8d3c95d 100644 --- a/hw/virtio/vhost-backend.c +++ b/hw/virtio/vhost-backend.c @@ -9,11 +9,14 @@ */ #include "qemu/osdep.h" -#include -#include #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-backend.h" #include "qemu/error-report.h" +#include "standard-headers/linux/vhost_types.h" + +#ifdef CONFIG_VHOST_KERNEL +#include +#include static int vhost_kernel_call(struct vhost_dev *dev, unsigned long int request, void *arg) @@ -265,18 +268,23 @@ static const VhostOps kernel_ops = { .vhost_set_iotlb_callback = vhost_kernel_set_iotlb_callback, .vhost_send_device_iotlb_msg = vhost_kernel_send_device_iotlb_msg, }; +#endif int vhost_set_backend_type(struct vhost_dev *dev, VhostBackendType backend_type) { int r = 0; switch (backend_type) { +#ifdef CONFIG_VHOST_KERNEL case VHOST_BACKEND_TYPE_KERNEL: dev->vhost_ops = &kernel_ops; break; +#endif +#ifdef CONFIG_VHOST_USER case VHOST_BACKEND_TYPE_USER: dev->vhost_ops = &user_ops; break; +#endif default: error_report("Unknown vhost backend type"); r = -1; diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c new file mode 100644 index 0000000000..523f7cb3ce --- /dev/null +++ b/hw/virtio/vhost-scsi-pci.c @@ -0,0 +1,97 @@ +/* + * Vhost scsi PCI bindings + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Stefan Hajnoczi + * + * Changes for QEMU mainline + tcm_vhost kernel upstream: + * Nicholas Bellinger + * + * 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 "standard-headers/linux/virtio_pci.h" +#include "hw/virtio/vhost-scsi.h" +#include "qapi/error.h" +#include "virtio-pci.h" + +typedef struct VHostSCSIPCI VHostSCSIPCI; + +/* + * vhost-scsi-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VHOST_SCSI_PCI "vhost-scsi-pci-base" +#define VHOST_SCSI_PCI(obj) \ + OBJECT_CHECK(VHostSCSIPCI, (obj), TYPE_VHOST_SCSI_PCI) + +struct VHostSCSIPCI { + VirtIOPCIProxy parent_obj; + VHostSCSI vdev; +}; + +static Property vhost_scsi_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = vs->conf.num_queues + 3; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_scsi_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = vhost_scsi_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vhost_scsi_pci_instance_init(Object *obj) +{ + VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_SCSI); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_scsi_pci_info = { + .base_name = TYPE_VHOST_SCSI_PCI, + .generic_name = "vhost-scsi-pci", + .transitional_name = "vhost-scsi-pci-transitional", + .non_transitional_name = "vhost-scsi-pci-non-transitional", + .instance_size = sizeof(VHostSCSIPCI), + .instance_init = vhost_scsi_pci_instance_init, + .class_init = vhost_scsi_pci_class_init, +}; + +static void vhost_scsi_pci_register(void) +{ + virtio_pci_types_register(&vhost_scsi_pci_info); +} + +type_init(vhost_scsi_pci_register) diff --git a/hw/virtio/vhost-stub.c b/hw/virtio/vhost-stub.c index 049089b5e2..c175148fce 100644 --- a/hw/virtio/vhost-stub.c +++ b/hw/virtio/vhost-stub.c @@ -7,9 +7,9 @@ bool vhost_has_free_slot(void) return true; } -VhostUserState *vhost_user_init(void) +bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp) { - return NULL; + return false; } void vhost_user_cleanup(VhostUserState *user) diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c new file mode 100644 index 0000000000..ca66c217a7 --- /dev/null +++ b/hw/virtio/vhost-user-blk-pci.c @@ -0,0 +1,103 @@ +/* + * Vhost user blk PCI Bindings + * + * Copyright(C) 2017 Intel Corporation. + * + * Authors: + * Changpeng Liu + * + * Largely based on the "vhost-user-scsi.c" and "vhost-scsi.c" implemented by: + * Felipe Franciosi + * Stefan Hajnoczi + * Nicholas Bellinger + * + * 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 "standard-headers/linux/virtio_pci.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/vhost-user-blk.h" +#include "hw/pci/pci.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "virtio-pci.h" + +typedef struct VHostUserBlkPCI VHostUserBlkPCI; + +/* + * vhost-user-blk-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VHOST_USER_BLK_PCI "vhost-user-blk-pci-base" +#define VHOST_USER_BLK_PCI(obj) \ + OBJECT_CHECK(VHostUserBlkPCI, (obj), TYPE_VHOST_USER_BLK_PCI) + +struct VHostUserBlkPCI { + VirtIOPCIProxy parent_obj; + VHostUserBlk vdev; +}; + +static Property vhost_user_blk_pci_properties[] = { + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = dev->vdev.num_queues + 1; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = vhost_user_blk_pci_properties; + k->realize = vhost_user_blk_pci_realize; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vhost_user_blk_pci_instance_init(Object *obj) +{ + VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_BLK); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_blk_pci_info = { + .base_name = TYPE_VHOST_USER_BLK_PCI, + .generic_name = "vhost-user-blk-pci", + .transitional_name = "vhost-user-blk-pci-transitional", + .non_transitional_name = "vhost-user-blk-pci-non-transitional", + .instance_size = sizeof(VHostUserBlkPCI), + .instance_init = vhost_user_blk_pci_instance_init, + .class_init = vhost_user_blk_pci_class_init, +}; + +static void vhost_user_blk_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_blk_pci_info); +} + +type_init(vhost_user_blk_pci_register) diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c new file mode 100644 index 0000000000..46f7193cc7 --- /dev/null +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -0,0 +1,103 @@ +/* + * Vhost user scsi PCI Bindings + * + * Copyright (c) 2016 Nutanix Inc. All rights reserved. + * + * Author: + * Felipe Franciosi + * + * This work is largely based on the "vhost-scsi" implementation by: + * Stefan Hajnoczi + * Nicholas Bellinger + * + * 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 "standard-headers/linux/virtio_pci.h" +#include "hw/virtio/vhost-user-scsi.h" +#include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-scsi.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" +#include "hw/loader.h" +#include "sysemu/kvm.h" +#include "virtio-pci.h" + +typedef struct VHostUserSCSIPCI VHostUserSCSIPCI; + +#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci-base" +#define VHOST_USER_SCSI_PCI(obj) \ + OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI) + +struct VHostUserSCSIPCI { + VirtIOPCIProxy parent_obj; + VHostUserSCSI vdev; +}; + +static Property vhost_user_scsi_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = vs->conf.num_queues + 3; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_user_scsi_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = vhost_user_scsi_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void vhost_user_scsi_pci_instance_init(Object *obj) +{ + VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_USER_SCSI); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static const VirtioPCIDeviceTypeInfo vhost_user_scsi_pci_info = { + .base_name = TYPE_VHOST_USER_SCSI_PCI, + .generic_name = "vhost-user-scsi-pci", + .transitional_name = "vhost-user-scsi-pci-transitional", + .non_transitional_name = "vhost-user-scsi-pci-non-transitional", + .instance_size = sizeof(VHostUserSCSIPCI), + .instance_init = vhost_user_scsi_pci_instance_init, + .class_init = vhost_user_scsi_pci_class_init, +}; + +static void vhost_user_scsi_pci_register(void) +{ + virtio_pci_types_register(&vhost_user_scsi_pci_info); +} + +type_init(vhost_user_scsi_pci_register) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index b041343632..553319c7ac 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -27,8 +27,12 @@ #include #include #include -#include + +#include "standard-headers/linux/vhost_types.h" + +#ifdef CONFIG_LINUX #include +#endif #define VHOST_MEMORY_MAX_NREGIONS 8 #define VHOST_USER_F_PROTOCOL_FEATURES 30 @@ -52,6 +56,7 @@ enum VhostUserProtocolFeature { VHOST_USER_PROTOCOL_F_CONFIG = 9, VHOST_USER_PROTOCOL_F_SLAVE_SEND_FD = 10, VHOST_USER_PROTOCOL_F_HOST_NOTIFIER = 11, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD = 12, VHOST_USER_PROTOCOL_F_MAX }; @@ -89,6 +94,8 @@ typedef enum VhostUserRequest { VHOST_USER_POSTCOPY_ADVISE = 28, VHOST_USER_POSTCOPY_LISTEN = 29, VHOST_USER_POSTCOPY_END = 30, + VHOST_USER_GET_INFLIGHT_FD = 31, + VHOST_USER_SET_INFLIGHT_FD = 32, VHOST_USER_MAX } VhostUserRequest; @@ -147,6 +154,13 @@ typedef struct VhostUserVringArea { uint64_t offset; } VhostUserVringArea; +typedef struct VhostUserInflight { + uint64_t mmap_size; + uint64_t mmap_offset; + uint16_t num_queues; + uint16_t queue_size; +} VhostUserInflight; + typedef struct { VhostUserRequest request; @@ -169,6 +183,7 @@ typedef union { VhostUserConfig config; VhostUserCryptoSession session; VhostUserVringArea area; + VhostUserInflight inflight; } VhostUserPayload; typedef struct VhostUserMsg { @@ -207,10 +222,10 @@ struct vhost_user { static bool ioeventfd_enabled(void) { - return kvm_enabled() && kvm_eventfds_enabled(); + return !kvm_enabled() || kvm_eventfds_enabled(); } -static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) +static int vhost_user_read_header(struct vhost_dev *dev, VhostUserMsg *msg) { struct vhost_user *u = dev->opaque; CharBackend *chr = u->user->chr; @@ -221,7 +236,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) if (r != size) { error_report("Failed to read msg header. Read %d instead of %d." " Original request %d.", r, size, msg->hdr.request); - goto fail; + return -1; } /* validate received flags */ @@ -229,7 +244,21 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) error_report("Failed to read msg header." " Flags 0x%x instead of 0x%x.", msg->hdr.flags, VHOST_USER_REPLY_MASK | VHOST_USER_VERSION); - goto fail; + return -1; + } + + return 0; +} + +static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) +{ + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->user->chr; + uint8_t *p = (uint8_t *) msg; + int r, size; + + if (vhost_user_read_header(dev, msg) < 0) { + return -1; } /* validate message size is sane */ @@ -237,7 +266,7 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) error_report("Failed to read msg header." " Size %d exceeds the maximum %zu.", msg->hdr.size, VHOST_USER_PAYLOAD_SIZE); - goto fail; + return -1; } if (msg->hdr.size) { @@ -247,14 +276,11 @@ static int vhost_user_read(struct vhost_dev *dev, VhostUserMsg *msg) if (r != size) { error_report("Failed to read msg payload." " Read %d instead of %d.", r, msg->hdr.size); - goto fail; + return -1; } } return 0; - -fail: - return -1; } static int process_message_reply(struct vhost_dev *dev, @@ -374,8 +400,6 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev, int fds[VHOST_MEMORY_MAX_NREGIONS]; int i, fd; size_t fd_num = 0; - bool reply_supported = virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_REPLY_ACK); VhostUserMsg msg_reply; int region_i, msg_i; @@ -384,10 +408,6 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev, .hdr.flags = VHOST_USER_VERSION, }; - if (reply_supported) { - msg.hdr.flags |= VHOST_USER_NEED_REPLY_MASK; - } - if (u->region_rb_len < dev->mem->nregions) { u->region_rb = g_renew(RAMBlock*, u->region_rb, dev->mem->nregions); u->region_rb_offset = g_renew(ram_addr_t, u->region_rb_offset, @@ -503,10 +523,6 @@ static int vhost_user_set_mem_table_postcopy(struct vhost_dev *dev, return -1; } - if (reply_supported) { - return process_message_reply(dev, &msg); - } - return 0; } @@ -519,8 +535,7 @@ static int vhost_user_set_mem_table(struct vhost_dev *dev, size_t fd_num = 0; bool do_postcopy = u->postcopy_listen && u->postcopy_fd.handler; bool reply_supported = virtio_has_feature(dev->protocol_features, - VHOST_USER_PROTOCOL_F_REPLY_ACK) && - !do_postcopy; + VHOST_USER_PROTOCOL_F_REPLY_ACK); if (do_postcopy) { /* Postcopy has enough differences that it's best done in it's own @@ -975,7 +990,10 @@ static void slave_read(void *opaque) iov.iov_base = &hdr; iov.iov_len = VHOST_USER_HDR_SIZE; - size = recvmsg(u->slave_fd, &msgh, 0); + do { + size = recvmsg(u->slave_fd, &msgh, 0); + } while (size < 0 && (errno == EINTR || errno == EAGAIN)); + if (size != VHOST_USER_HDR_SIZE) { error_report("Failed to read from slave."); goto err; @@ -1004,7 +1022,10 @@ static void slave_read(void *opaque) } /* Read payload */ - size = read(u->slave_fd, &payload, hdr.size); + do { + size = read(u->slave_fd, &payload, hdr.size); + } while (size < 0 && (errno == EINTR || errno == EAGAIN)); + if (size != hdr.size) { error_report("Failed to read payload from slave."); goto err; @@ -1052,7 +1073,10 @@ static void slave_read(void *opaque) iovec[1].iov_base = &payload; iovec[1].iov_len = hdr.size; - size = writev(u->slave_fd, iovec, ARRAY_SIZE(iovec)); + do { + size = writev(u->slave_fd, iovec, ARRAY_SIZE(iovec)); + } while (size < 0 && (errno == EINTR || errno == EAGAIN)); + if (size != VHOST_USER_HDR_SIZE + hdr.size) { error_report("Failed to send msg reply to slave."); goto err; @@ -1121,6 +1145,7 @@ out: return ret; } +#ifdef CONFIG_LINUX /* * Called back from the postcopy fault thread when a fault is received on our * ufd. @@ -1188,6 +1213,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb, trace_vhost_user_postcopy_waker_nomatch(qemu_ram_get_idstr(rb), offset); return 0; } +#endif /* * Called at the start of an inbound postcopy on reception of the @@ -1195,6 +1221,7 @@ static int vhost_user_postcopy_waker(struct PostCopyFD *pcfd, RAMBlock *rb, */ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) { +#ifdef CONFIG_LINUX struct vhost_user *u = dev->opaque; CharBackend *chr = u->user->chr; int ufd; @@ -1238,6 +1265,10 @@ static int vhost_user_postcopy_advise(struct vhost_dev *dev, Error **errp) u->postcopy_fd.idstr = "vhost-user"; /* Need to find unique name */ postcopy_register_shared_ufd(&u->postcopy_fd); return 0; +#else + error_setg(errp, "Postcopy not supported on non-Linux systems"); + return -1; +#endif } /* @@ -1291,6 +1322,7 @@ static int vhost_user_postcopy_end(struct vhost_dev *dev, Error **errp) return ret; } postcopy_unregister_shared_ufd(&u->postcopy_fd); + close(u->postcopy_fd.fd); u->postcopy_fd.handler = NULL; trace_vhost_user_postcopy_end_exit(); @@ -1430,6 +1462,12 @@ static int vhost_user_backend_cleanup(struct vhost_dev *dev) postcopy_remove_notifier(&u->postcopy_notifier); u->postcopy_notifier.notify = NULL; } + u->postcopy_listen = false; + if (u->postcopy_fd.handler) { + postcopy_unregister_shared_ufd(&u->postcopy_fd); + close(u->postcopy_fd.fd); + u->postcopy_fd.handler = NULL; + } if (u->slave_fd >= 0) { qemu_set_fd_handler(u->slave_fd, NULL, NULL, NULL); close(u->slave_fd); @@ -1743,17 +1781,118 @@ static bool vhost_user_mem_section_filter(struct vhost_dev *dev, return result; } -VhostUserState *vhost_user_init(void) +static int vhost_user_get_inflight_fd(struct vhost_dev *dev, + uint16_t queue_size, + struct vhost_inflight *inflight) { - VhostUserState *user = g_new0(struct VhostUserState, 1); + void *addr; + int fd; + struct vhost_user *u = dev->opaque; + CharBackend *chr = u->user->chr; + VhostUserMsg msg = { + .hdr.request = VHOST_USER_GET_INFLIGHT_FD, + .hdr.flags = VHOST_USER_VERSION, + .payload.inflight.num_queues = dev->nvqs, + .payload.inflight.queue_size = queue_size, + .hdr.size = sizeof(msg.payload.inflight), + }; - return user; + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + if (vhost_user_write(dev, &msg, NULL, 0) < 0) { + return -1; + } + + if (vhost_user_read(dev, &msg) < 0) { + return -1; + } + + if (msg.hdr.request != VHOST_USER_GET_INFLIGHT_FD) { + error_report("Received unexpected msg type. " + "Expected %d received %d", + VHOST_USER_GET_INFLIGHT_FD, msg.hdr.request); + return -1; + } + + if (msg.hdr.size != sizeof(msg.payload.inflight)) { + error_report("Received bad msg size."); + return -1; + } + + if (!msg.payload.inflight.mmap_size) { + return 0; + } + + fd = qemu_chr_fe_get_msgfd(chr); + if (fd < 0) { + error_report("Failed to get mem fd"); + return -1; + } + + addr = mmap(0, msg.payload.inflight.mmap_size, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, msg.payload.inflight.mmap_offset); + + if (addr == MAP_FAILED) { + error_report("Failed to mmap mem fd"); + close(fd); + return -1; + } + + inflight->addr = addr; + inflight->fd = fd; + inflight->size = msg.payload.inflight.mmap_size; + inflight->offset = msg.payload.inflight.mmap_offset; + inflight->queue_size = queue_size; + + return 0; +} + +static int vhost_user_set_inflight_fd(struct vhost_dev *dev, + struct vhost_inflight *inflight) +{ + VhostUserMsg msg = { + .hdr.request = VHOST_USER_SET_INFLIGHT_FD, + .hdr.flags = VHOST_USER_VERSION, + .payload.inflight.mmap_size = inflight->size, + .payload.inflight.mmap_offset = inflight->offset, + .payload.inflight.num_queues = dev->nvqs, + .payload.inflight.queue_size = inflight->queue_size, + .hdr.size = sizeof(msg.payload.inflight), + }; + + if (!virtio_has_feature(dev->protocol_features, + VHOST_USER_PROTOCOL_F_INFLIGHT_SHMFD)) { + return 0; + } + + if (vhost_user_write(dev, &msg, &inflight->fd, 1) < 0) { + return -1; + } + + return 0; +} + +bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp) +{ + if (user->chr) { + error_setg(errp, "Cannot initialize vhost-user state"); + return false; + } + user->chr = chr; + return true; } void vhost_user_cleanup(VhostUserState *user) { int i; + if (!user->chr) { + return; + } + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { if (user->notifier[i].addr) { object_unparent(OBJECT(&user->notifier[i].mr)); @@ -1761,6 +1900,7 @@ void vhost_user_cleanup(VhostUserState *user) user->notifier[i].addr = NULL; } } + user->chr = NULL; } const VhostOps user_ops = { @@ -1794,4 +1934,6 @@ const VhostOps user_ops = { .vhost_crypto_create_session = vhost_user_crypto_create_session, .vhost_crypto_close_session = vhost_user_crypto_close_session, .vhost_backend_mem_section_filter = vhost_user_mem_section_filter, + .vhost_get_inflight_fd = vhost_user_get_inflight_fd, + .vhost_set_inflight_fd = vhost_user_set_inflight_fd, }; diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c new file mode 100644 index 0000000000..6f43ca35fb --- /dev/null +++ b/hw/virtio/vhost-vsock-pci.c @@ -0,0 +1,86 @@ +/* + * Vhost vsock PCI Bindings + * + * Copyright 2015 Red Hat, Inc. + * + * Authors: + * Stefan Hajnoczi + * + * 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 "virtio-pci.h" +#include "hw/virtio/vhost-vsock.h" + +typedef struct VHostVSockPCI VHostVSockPCI; + +/* + * vhost-vsock-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VHOST_VSOCK_PCI "vhost-vsock-pci-base" +#define VHOST_VSOCK_PCI(obj) \ + OBJECT_CHECK(VHostVSockPCI, (obj), TYPE_VHOST_VSOCK_PCI) + +struct VHostVSockPCI { + VirtIOPCIProxy parent_obj; + VHostVSock vdev; +}; + +/* vhost-vsock-pci */ + +static Property vhost_vsock_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), + DEFINE_PROP_END_OF_LIST(), +}; + +static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VHostVSockPCI *dev = VHOST_VSOCK_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = vhost_vsock_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = vhost_vsock_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_VSOCK; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; +} + +static void vhost_vsock_pci_instance_init(Object *obj) +{ + VHostVSockPCI *dev = VHOST_VSOCK_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VHOST_VSOCK); +} + +static const VirtioPCIDeviceTypeInfo vhost_vsock_pci_info = { + .base_name = TYPE_VHOST_VSOCK_PCI, + .generic_name = "vhost-vsock-pci", + .transitional_name = "vhost-vsock-pci-transitional", + .non_transitional_name = "vhost-vsock-pci-non-transitional", + .instance_size = sizeof(VHostVSockPCI), + .instance_init = vhost_vsock_pci_instance_init, + .class_init = vhost_vsock_pci_class_init, +}; + +static void virtio_pci_vhost_register(void) +{ + virtio_pci_types_register(&vhost_vsock_pci_info); +} + +type_init(virtio_pci_vhost_register) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index d4cb5894a8..7f61018f2a 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -21,7 +21,7 @@ #include "qemu/range.h" #include "qemu/error-report.h" #include "qemu/memfd.h" -#include +#include "standard-headers/linux/vhost_types.h" #include "exec/address-spaces.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio-access.h" @@ -1073,10 +1073,8 @@ static void vhost_virtqueue_stop(struct vhost_dev *dev, .index = vhost_vq_index, }; int r; - int a; - a = virtio_queue_get_desc_addr(vdev, idx); - if (a == 0) { + if (virtio_queue_get_desc_addr(vdev, idx) == 0) { /* Don't stop the virtqueue which might have not been started */ return; } @@ -1483,6 +1481,102 @@ void vhost_dev_set_config_notifier(struct vhost_dev *hdev, hdev->config_ops = ops; } +void vhost_dev_free_inflight(struct vhost_inflight *inflight) +{ + if (inflight->addr) { + qemu_memfd_free(inflight->addr, inflight->size, inflight->fd); + inflight->addr = NULL; + inflight->fd = -1; + } +} + +static int vhost_dev_resize_inflight(struct vhost_inflight *inflight, + uint64_t new_size) +{ + Error *err = NULL; + int fd = -1; + void *addr = qemu_memfd_alloc("vhost-inflight", new_size, + F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL, + &fd, &err); + + if (err) { + error_report_err(err); + return -1; + } + + vhost_dev_free_inflight(inflight); + inflight->offset = 0; + inflight->addr = addr; + inflight->fd = fd; + inflight->size = new_size; + + return 0; +} + +void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f) +{ + if (inflight->addr) { + qemu_put_be64(f, inflight->size); + qemu_put_be16(f, inflight->queue_size); + qemu_put_buffer(f, inflight->addr, inflight->size); + } else { + qemu_put_be64(f, 0); + } +} + +int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f) +{ + uint64_t size; + + size = qemu_get_be64(f); + if (!size) { + return 0; + } + + if (inflight->size != size) { + if (vhost_dev_resize_inflight(inflight, size)) { + return -1; + } + } + inflight->queue_size = qemu_get_be16(f); + + qemu_get_buffer(f, inflight->addr, size); + + return 0; +} + +int vhost_dev_set_inflight(struct vhost_dev *dev, + struct vhost_inflight *inflight) +{ + int r; + + if (dev->vhost_ops->vhost_set_inflight_fd && inflight->addr) { + r = dev->vhost_ops->vhost_set_inflight_fd(dev, inflight); + if (r) { + VHOST_OPS_DEBUG("vhost_set_inflight_fd failed"); + return -errno; + } + } + + return 0; +} + +int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, + struct vhost_inflight *inflight) +{ + int r; + + if (dev->vhost_ops->vhost_get_inflight_fd) { + r = dev->vhost_ops->vhost_get_inflight_fd(dev, queue_size, inflight); + if (r) { + VHOST_OPS_DEBUG("vhost_get_inflight_fd failed"); + return -errno; + } + } + + return 0; +} + /* Host notifiers must be enabled at this point. */ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev) { diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c new file mode 100644 index 0000000000..7bf1130966 --- /dev/null +++ b/hw/virtio/virtio-9p-pci.c @@ -0,0 +1,88 @@ +/* + * Virtio 9p PCI Bindings + * + * 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. + * + * 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 "virtio-pci.h" +#include "hw/9pfs/virtio-9p.h" + +/* + * virtio-9p-pci: This extends VirtioPCIProxy. + */ + +#define TYPE_VIRTIO_9P_PCI "virtio-9p-pci-base" +#define VIRTIO_9P_PCI(obj) \ + OBJECT_CHECK(V9fsPCIState, (obj), TYPE_VIRTIO_9P_PCI) + +typedef struct V9fsPCIState { + VirtIOPCIProxy parent_obj; + V9fsVirtioState vdev; +} V9fsPCIState; + +static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + V9fsPCIState *dev = VIRTIO_9P_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static Property virtio_9p_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + + k->realize = virtio_9p_pci_realize; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_9P; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = 0x2; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = virtio_9p_pci_properties; +} + +static void virtio_9p_pci_instance_init(Object *obj) +{ + V9fsPCIState *dev = VIRTIO_9P_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_9P); +} + +static const VirtioPCIDeviceTypeInfo virtio_9p_pci_info = { + .base_name = TYPE_VIRTIO_9P_PCI, + .generic_name = "virtio-9p-pci", + .transitional_name = "virtio-9p-pci-transitional", + .non_transitional_name = "virtio-9p-pci-non-transitional", + .instance_size = sizeof(V9fsPCIState), + .instance_init = virtio_9p_pci_instance_init, + .class_init = virtio_9p_pci_class_init, +}; + +static void virtio_9p_pci_register(void) +{ + virtio_pci_types_register(&virtio_9p_pci_info); +} + +type_init(virtio_9p_pci_register) diff --git a/hw/virtio/virtio-balloon-pci.c b/hw/virtio/virtio-balloon-pci.c new file mode 100644 index 0000000000..2a213bbb38 --- /dev/null +++ b/hw/virtio/virtio-balloon-pci.c @@ -0,0 +1,95 @@ +/* + * Virtio balloon PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori + * Paul Brook + * + * 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 "virtio-pci.h" +#include "hw/virtio/virtio-balloon.h" +#include "qapi/error.h" + +typedef struct VirtIOBalloonPCI VirtIOBalloonPCI; + +/* + * virtio-balloon-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_BALLOON_PCI "virtio-balloon-pci-base" +#define VIRTIO_BALLOON_PCI(obj) \ + OBJECT_CHECK(VirtIOBalloonPCI, (obj), TYPE_VIRTIO_BALLOON_PCI) + +struct VirtIOBalloonPCI { + VirtIOPCIProxy parent_obj; + VirtIOBalloon vdev; +}; +static Property virtio_balloon_pci_properties[] = { + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->class_code != PCI_CLASS_OTHERS && + vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */ + vpci_dev->class_code = PCI_CLASS_OTHERS; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = virtio_balloon_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + dc->props = virtio_balloon_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_OTHERS; +} + +static void virtio_balloon_pci_instance_init(Object *obj) +{ + VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_BALLOON); + object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev), + "guest-stats", &error_abort); + object_property_add_alias(obj, "guest-stats-polling-interval", + OBJECT(&dev->vdev), + "guest-stats-polling-interval", &error_abort); +} + +static const VirtioPCIDeviceTypeInfo virtio_balloon_pci_info = { + .base_name = TYPE_VIRTIO_BALLOON_PCI, + .generic_name = "virtio-balloon-pci", + .transitional_name = "virtio-balloon-pci-transitional", + .non_transitional_name = "virtio-balloon-pci-non-transitional", + .instance_size = sizeof(VirtIOBalloonPCI), + .instance_init = virtio_balloon_pci_instance_init, + .class_init = virtio_balloon_pci_class_init, +}; + +static void virtio_balloon_pci_register(void) +{ + virtio_pci_types_register(&virtio_balloon_pci_info); +} + +type_init(virtio_balloon_pci_register) diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 1f7a87f094..2112874055 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -21,25 +21,144 @@ #include "hw/mem/pc-dimm.h" #include "sysemu/balloon.h" #include "hw/virtio/virtio-balloon.h" -#include "sysemu/kvm.h" #include "exec/address-spaces.h" #include "qapi/error.h" #include "qapi/qapi-events-misc.h" #include "qapi/visitor.h" #include "trace.h" #include "qemu/error-report.h" +#include "migration/misc.h" #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) +struct PartiallyBalloonedPage { + RAMBlock *rb; + ram_addr_t base; + unsigned long bitmap[]; +}; + +static void balloon_inflate_page(VirtIOBalloon *balloon, + MemoryRegion *mr, hwaddr offset) { - if (!qemu_balloon_is_inhibited() && (!kvm_enabled() || - kvm_has_sync_mmu())) { - qemu_madvise(addr, BALLOON_PAGE_SIZE, - deflate ? QEMU_MADV_WILLNEED : QEMU_MADV_DONTNEED); + void *addr = memory_region_get_ram_ptr(mr) + offset; + RAMBlock *rb; + size_t rb_page_size; + int subpages; + ram_addr_t ram_offset, host_page_base; + + /* XXX is there a better way to get to the RAMBlock than via a + * host address? */ + rb = qemu_ram_block_from_host(addr, false, &ram_offset); + rb_page_size = qemu_ram_pagesize(rb); + host_page_base = ram_offset & ~(rb_page_size - 1); + + if (rb_page_size == BALLOON_PAGE_SIZE) { + /* Easy case */ + + ram_block_discard_range(rb, ram_offset, rb_page_size); + /* We ignore errors from ram_block_discard_range(), because it + * has already reported them, and failing to discard a balloon + * page is not fatal */ + return; + } + + /* Hard case + * + * We've put a piece of a larger host page into the balloon - we + * need to keep track until we have a whole host page to + * discard + */ + warn_report_once( +"Balloon used with backing page size > 4kiB, this may not be reliable"); + + subpages = rb_page_size / BALLOON_PAGE_SIZE; + + if (balloon->pbp + && (rb != balloon->pbp->rb + || host_page_base != balloon->pbp->base)) { + /* We've partially ballooned part of a host page, but now + * we're trying to balloon part of a different one. Too hard, + * give up on the old partial page */ + g_free(balloon->pbp); + balloon->pbp = NULL; + } + + if (!balloon->pbp) { + /* Starting on a new host page */ + size_t bitlen = BITS_TO_LONGS(subpages) * sizeof(unsigned long); + balloon->pbp = g_malloc0(sizeof(PartiallyBalloonedPage) + bitlen); + balloon->pbp->rb = rb; + balloon->pbp->base = host_page_base; + } + + bitmap_set(balloon->pbp->bitmap, + (ram_offset - balloon->pbp->base) / BALLOON_PAGE_SIZE, + subpages); + + if (bitmap_full(balloon->pbp->bitmap, subpages)) { + /* We've accumulated a full host page, we can actually discard + * it now */ + + ram_block_discard_range(rb, balloon->pbp->base, rb_page_size); + /* We ignore errors from ram_block_discard_range(), because it + * has already reported them, and failing to discard a balloon + * page is not fatal */ + + g_free(balloon->pbp); + balloon->pbp = NULL; + } +} + +static void balloon_deflate_page(VirtIOBalloon *balloon, + MemoryRegion *mr, hwaddr offset) +{ + void *addr = memory_region_get_ram_ptr(mr) + offset; + RAMBlock *rb; + size_t rb_page_size; + ram_addr_t ram_offset, host_page_base; + void *host_addr; + int ret; + + /* XXX is there a better way to get to the RAMBlock than via a + * host address? */ + rb = qemu_ram_block_from_host(addr, false, &ram_offset); + rb_page_size = qemu_ram_pagesize(rb); + host_page_base = ram_offset & ~(rb_page_size - 1); + + if (balloon->pbp + && rb == balloon->pbp->rb + && host_page_base == balloon->pbp->base) { + int subpages = rb_page_size / BALLOON_PAGE_SIZE; + + /* + * This means the guest has asked to discard some of the 4kiB + * subpages of a host page, but then changed its mind and + * asked to keep them after all. It's exceedingly unlikely + * for a guest to do this in practice, but handle it anyway, + * since getting it wrong could mean discarding memory the + * guest is still using. */ + bitmap_clear(balloon->pbp->bitmap, + (ram_offset - balloon->pbp->base) / BALLOON_PAGE_SIZE, + subpages); + + if (bitmap_empty(balloon->pbp->bitmap, subpages)) { + g_free(balloon->pbp); + balloon->pbp = NULL; + } + } + + host_addr = (void *)((uintptr_t)addr & ~(rb_page_size - 1)); + + /* When a page is deflated, we hint the whole host page it lives + * on, since we can't do anything smaller */ + ret = qemu_madvise(host_addr, rb_page_size, QEMU_MADV_WILLNEED); + if (ret != 0) { + warn_report("Couldn't MADV_WILLNEED on balloon deflate: %s", + strerror(errno)); + /* Otherwise ignore, failing to page hint shouldn't be fatal */ } } @@ -224,17 +343,19 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) } while (iov_to_buf(elem->out_sg, elem->out_num, offset, &pfn, 4) == 4) { - ram_addr_t pa; - ram_addr_t addr; + hwaddr pa; int p = virtio_ldl_p(vdev, &pfn); - pa = (ram_addr_t) p << VIRTIO_BALLOON_PFN_SHIFT; + pa = (hwaddr) p << VIRTIO_BALLOON_PFN_SHIFT; offset += 4; - /* FIXME: remove get_system_memory(), but how? */ - section = memory_region_find(get_system_memory(), pa, 1); - if (!int128_nz(section.size) || - !memory_region_is_ram(section.mr) || + section = memory_region_find(get_system_memory(), pa, + BALLOON_PAGE_SIZE); + if (!section.mr) { + trace_virtio_balloon_bad_addr(pa); + continue; + } + if (!memory_region_is_ram(section.mr) || memory_region_is_rom(section.mr) || memory_region_is_romd(section.mr)) { trace_virtio_balloon_bad_addr(pa); @@ -244,11 +365,16 @@ static void virtio_balloon_handle_output(VirtIODevice *vdev, VirtQueue *vq) trace_virtio_balloon_handle_output(memory_region_name(section.mr), pa); - /* Using memory_region_get_ram_ptr is bending the rules a bit, but - should be OK because we only want a single page. */ - addr = section.offset_within_region; - balloon_page(memory_region_get_ram_ptr(section.mr) + addr, - !!(vq == s->dvq)); + if (!qemu_balloon_is_inhibited()) { + if (vq == s->ivq) { + balloon_inflate_page(s, section.mr, + section.offset_within_region); + } else if (vq == s->dvq) { + balloon_deflate_page(s, section.mr, section.offset_within_region); + } else { + g_assert_not_reached(); + } + } memory_region_unref(section.mr); } @@ -310,14 +436,204 @@ out: } } +static void virtio_balloon_handle_free_page_vq(VirtIODevice *vdev, + VirtQueue *vq) +{ + VirtIOBalloon *s = VIRTIO_BALLOON(vdev); + qemu_bh_schedule(s->free_page_bh); +} + +static bool get_free_page_hints(VirtIOBalloon *dev) +{ + VirtQueueElement *elem; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtQueue *vq = dev->free_page_vq; + bool ret = true; + + while (dev->block_iothread) { + qemu_cond_wait(&dev->free_page_cond, &dev->free_page_lock); + } + + elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); + if (!elem) { + return false; + } + + if (elem->out_num) { + uint32_t id; + size_t size = iov_to_buf(elem->out_sg, elem->out_num, 0, + &id, sizeof(id)); + + virtio_tswap32s(vdev, &id); + if (unlikely(size != sizeof(id))) { + virtio_error(vdev, "received an incorrect cmd id"); + ret = false; + goto out; + } + if (id == dev->free_page_report_cmd_id) { + dev->free_page_report_status = FREE_PAGE_REPORT_S_START; + } else { + /* + * Stop the optimization only when it has started. This + * avoids a stale stop sign for the previous command. + */ + if (dev->free_page_report_status == FREE_PAGE_REPORT_S_START) { + dev->free_page_report_status = FREE_PAGE_REPORT_S_STOP; + } + } + } + + if (elem->in_num) { + if (dev->free_page_report_status == FREE_PAGE_REPORT_S_START) { + qemu_guest_free_page_hint(elem->in_sg[0].iov_base, + elem->in_sg[0].iov_len); + } + } + +out: + virtqueue_push(vq, elem, 1); + g_free(elem); + return ret; +} + +static void virtio_ballloon_get_free_page_hints(void *opaque) +{ + VirtIOBalloon *dev = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + VirtQueue *vq = dev->free_page_vq; + bool continue_to_get_hints; + + do { + qemu_mutex_lock(&dev->free_page_lock); + virtio_queue_set_notification(vq, 0); + continue_to_get_hints = get_free_page_hints(dev); + qemu_mutex_unlock(&dev->free_page_lock); + virtio_notify(vdev, vq); + /* + * Start to poll the vq once the reporting started. Otherwise, continue + * only when there are entries on the vq, which need to be given back. + */ + } while (continue_to_get_hints || + dev->free_page_report_status == FREE_PAGE_REPORT_S_START); + virtio_queue_set_notification(vq, 1); +} + +static bool virtio_balloon_free_page_support(void *opaque) +{ + VirtIOBalloon *s = opaque; + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + return virtio_vdev_has_feature(vdev, VIRTIO_BALLOON_F_FREE_PAGE_HINT); +} + +static void virtio_balloon_free_page_start(VirtIOBalloon *s) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + /* For the stop and copy phase, we don't need to start the optimization */ + if (!vdev->vm_running) { + return; + } + + if (s->free_page_report_cmd_id == UINT_MAX) { + s->free_page_report_cmd_id = + VIRTIO_BALLOON_FREE_PAGE_REPORT_CMD_ID_MIN; + } else { + s->free_page_report_cmd_id++; + } + + s->free_page_report_status = FREE_PAGE_REPORT_S_REQUESTED; + virtio_notify_config(vdev); +} + +static void virtio_balloon_free_page_stop(VirtIOBalloon *s) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + if (s->free_page_report_status != FREE_PAGE_REPORT_S_STOP) { + /* + * The lock also guarantees us that the + * virtio_ballloon_get_free_page_hints exits after the + * free_page_report_status is set to S_STOP. + */ + qemu_mutex_lock(&s->free_page_lock); + /* + * The guest hasn't done the reporting, so host sends a notification + * to the guest to actively stop the reporting. + */ + s->free_page_report_status = FREE_PAGE_REPORT_S_STOP; + qemu_mutex_unlock(&s->free_page_lock); + virtio_notify_config(vdev); + } +} + +static void virtio_balloon_free_page_done(VirtIOBalloon *s) +{ + VirtIODevice *vdev = VIRTIO_DEVICE(s); + + s->free_page_report_status = FREE_PAGE_REPORT_S_DONE; + virtio_notify_config(vdev); +} + +static int +virtio_balloon_free_page_report_notify(NotifierWithReturn *n, void *data) +{ + VirtIOBalloon *dev = container_of(n, VirtIOBalloon, + free_page_report_notify); + VirtIODevice *vdev = VIRTIO_DEVICE(dev); + PrecopyNotifyData *pnd = data; + + if (!virtio_balloon_free_page_support(dev)) { + /* + * This is an optimization provided to migration, so just return 0 to + * have the normal migration process not affected when this feature is + * not supported. + */ + return 0; + } + + switch (pnd->reason) { + case PRECOPY_NOTIFY_SETUP: + precopy_enable_free_page_optimization(); + break; + case PRECOPY_NOTIFY_COMPLETE: + case PRECOPY_NOTIFY_CLEANUP: + case PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC: + virtio_balloon_free_page_stop(dev); + break; + case PRECOPY_NOTIFY_AFTER_BITMAP_SYNC: + if (vdev->vm_running) { + virtio_balloon_free_page_start(dev); + } else { + virtio_balloon_free_page_done(dev); + } + break; + default: + virtio_error(vdev, "%s: %d reason unknown", __func__, pnd->reason); + } + + return 0; +} + static void virtio_balloon_get_config(VirtIODevice *vdev, uint8_t *config_data) { VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); - struct virtio_balloon_config config; + struct virtio_balloon_config config = {}; config.num_pages = cpu_to_le32(dev->num_pages); config.actual = cpu_to_le32(dev->actual); + if (dev->free_page_report_status == FREE_PAGE_REPORT_S_REQUESTED) { + config.free_page_report_cmd_id = + cpu_to_le32(dev->free_page_report_cmd_id); + } else if (dev->free_page_report_status == FREE_PAGE_REPORT_S_STOP) { + config.free_page_report_cmd_id = + cpu_to_le32(VIRTIO_BALLOON_CMD_ID_STOP); + } else if (dev->free_page_report_status == FREE_PAGE_REPORT_S_DONE) { + config.free_page_report_cmd_id = + cpu_to_le32(VIRTIO_BALLOON_CMD_ID_DONE); + } + trace_virtio_balloon_get_config(config.num_pages, config.actual); memcpy(config_data, &config, sizeof(struct virtio_balloon_config)); } @@ -367,8 +683,7 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, dev->actual = le32_to_cpu(config.actual); if (dev->actual != oldactual) { qapi_event_send_balloon_change(vm_ram_size - - ((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT), - &error_abort); + ((ram_addr_t) dev->actual << VIRTIO_BALLOON_PFN_SHIFT)); } trace_virtio_balloon_set_config(dev->actual, oldactual); } @@ -379,6 +694,7 @@ static uint64_t virtio_balloon_get_features(VirtIODevice *vdev, uint64_t f, VirtIOBalloon *dev = VIRTIO_BALLOON(vdev); f |= dev->host_features; virtio_add_feature(&f, VIRTIO_BALLOON_F_STATS_VQ); + return f; } @@ -415,6 +731,18 @@ static int virtio_balloon_post_load_device(void *opaque, int version_id) return 0; } +static const VMStateDescription vmstate_virtio_balloon_free_page_report = { + .name = "virtio-balloon-device/free-page-report", + .version_id = 1, + .minimum_version_id = 1, + .needed = virtio_balloon_free_page_support, + .fields = (VMStateField[]) { + VMSTATE_UINT32(free_page_report_cmd_id, VirtIOBalloon), + VMSTATE_UINT32(free_page_report_status, VirtIOBalloon), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_virtio_balloon_device = { .name = "virtio-balloon-device", .version_id = 1, @@ -425,6 +753,10 @@ static const VMStateDescription vmstate_virtio_balloon_device = { VMSTATE_UINT32(actual, VirtIOBalloon), VMSTATE_END_OF_LIST() }, + .subsections = (const VMStateDescription * []) { + &vmstate_virtio_balloon_free_page_report, + NULL + } }; static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) @@ -449,6 +781,29 @@ static void virtio_balloon_device_realize(DeviceState *dev, Error **errp) s->dvq = virtio_add_queue(vdev, 128, virtio_balloon_handle_output); s->svq = virtio_add_queue(vdev, 128, virtio_balloon_receive_stats); + if (virtio_has_feature(s->host_features, + VIRTIO_BALLOON_F_FREE_PAGE_HINT)) { + s->free_page_vq = virtio_add_queue(vdev, VIRTQUEUE_MAX_SIZE, + virtio_balloon_handle_free_page_vq); + s->free_page_report_status = FREE_PAGE_REPORT_S_STOP; + s->free_page_report_cmd_id = + VIRTIO_BALLOON_FREE_PAGE_REPORT_CMD_ID_MIN; + s->free_page_report_notify.notify = + virtio_balloon_free_page_report_notify; + precopy_add_notifier(&s->free_page_report_notify); + if (s->iothread) { + object_ref(OBJECT(s->iothread)); + s->free_page_bh = aio_bh_new(iothread_get_aio_context(s->iothread), + virtio_ballloon_get_free_page_hints, s); + qemu_mutex_init(&s->free_page_lock); + qemu_cond_init(&s->free_page_cond); + s->block_iothread = false; + } else { + /* Simply disable this feature if the iothread wasn't created. */ + s->host_features &= ~(1 << VIRTIO_BALLOON_F_FREE_PAGE_HINT); + virtio_error(vdev, "iothread is missing"); + } + } reset_stats(s); } @@ -457,6 +812,11 @@ static void virtio_balloon_device_unrealize(DeviceState *dev, Error **errp) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOBalloon *s = VIRTIO_BALLOON(dev); + if (virtio_balloon_free_page_support(s)) { + qemu_bh_delete(s->free_page_bh); + virtio_balloon_free_page_stop(s); + precopy_remove_notifier(&s->free_page_report_notify); + } balloon_stats_destroy_timer(s); qemu_remove_balloon_handler(s); virtio_cleanup(vdev); @@ -466,6 +826,10 @@ static void virtio_balloon_device_reset(VirtIODevice *vdev) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); + if (virtio_balloon_free_page_support(s)) { + virtio_balloon_free_page_stop(s); + } + if (s->stats_vq_elem != NULL) { virtqueue_unpop(s->svq, s->stats_vq_elem, 0); g_free(s->stats_vq_elem); @@ -483,6 +847,26 @@ static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) * was stopped */ virtio_balloon_receive_stats(vdev, s->svq); } + + if (virtio_balloon_free_page_support(s)) { + /* + * The VM is woken up and the iothread was blocked, so signal it to + * continue. + */ + if (vdev->vm_running && s->block_iothread) { + qemu_mutex_lock(&s->free_page_lock); + s->block_iothread = false; + qemu_cond_signal(&s->free_page_cond); + qemu_mutex_unlock(&s->free_page_lock); + } + + /* The VM is stopped, block the iothread. */ + if (!vdev->vm_running) { + qemu_mutex_lock(&s->free_page_lock); + s->block_iothread = true; + qemu_mutex_unlock(&s->free_page_lock); + } + } } static void virtio_balloon_instance_init(Object *obj) @@ -511,6 +895,10 @@ static const VMStateDescription vmstate_virtio_balloon = { static Property virtio_balloon_properties[] = { DEFINE_PROP_BIT("deflate-on-oom", VirtIOBalloon, host_features, VIRTIO_BALLOON_F_DEFLATE_ON_OOM, false), + DEFINE_PROP_BIT("free-page-hint", VirtIOBalloon, host_features, + VIRTIO_BALLOON_F_FREE_PAGE_HINT, false), + DEFINE_PROP_LINK("iothread", VirtIOBalloon, iothread, TYPE_IOTHREAD, + IOThread *), DEFINE_PROP_END_OF_LIST(), }; diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c new file mode 100644 index 0000000000..60c9185c39 --- /dev/null +++ b/hw/virtio/virtio-blk-pci.c @@ -0,0 +1,100 @@ +/* + * Virtio blk PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori + * Paul Brook + * + * 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 "hw/virtio/virtio-blk.h" +#include "virtio-pci.h" +#include "qapi/error.h" + +typedef struct VirtIOBlkPCI VirtIOBlkPCI; + +/* + * virtio-blk-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci-base" +#define VIRTIO_BLK_PCI(obj) \ + OBJECT_CHECK(VirtIOBlkPCI, (obj), TYPE_VIRTIO_BLK_PCI) + +struct VirtIOBlkPCI { + VirtIOPCIProxy parent_obj; + VirtIOBlock vdev; +}; + +static Property virtio_blk_pci_properties[] = { + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = dev->vdev.conf.num_queues + 1; + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = virtio_blk_pci_properties; + k->realize = virtio_blk_pci_realize; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void virtio_blk_pci_instance_init(Object *obj) +{ + VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_BLK); + object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), + "bootindex", &error_abort); +} + +static const VirtioPCIDeviceTypeInfo virtio_blk_pci_info = { + .base_name = TYPE_VIRTIO_BLK_PCI, + .generic_name = "virtio-blk-pci", + .transitional_name = "virtio-blk-pci-transitional", + .non_transitional_name = "virtio-blk-pci-non-transitional", + .instance_size = sizeof(VirtIOBlkPCI), + .instance_init = virtio_blk_pci_instance_init, + .class_init = virtio_blk_pci_class_init, +}; + +static void virtio_blk_pci_register(void) +{ + virtio_pci_types_register(&virtio_blk_pci_info); +} + +type_init(virtio_blk_pci_register) diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index bf64996e48..90a6e0dc2e 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -19,6 +19,20 @@ #include "hw/virtio/virtio-crypto.h" #include "qapi/error.h" +typedef struct VirtIOCryptoPCI VirtIOCryptoPCI; + +/* + * virtio-crypto-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_CRYPTO_PCI "virtio-crypto-pci" +#define VIRTIO_CRYPTO_PCI(obj) \ + OBJECT_CHECK(VirtIOCryptoPCI, (obj), TYPE_VIRTIO_CRYPTO_PCI) + +struct VirtIOCryptoPCI { + VirtIOPCIProxy parent_obj; + VirtIOCrypto vdev; +}; + static Property virtio_crypto_pci_properties[] = { DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), @@ -64,9 +78,8 @@ static void virtio_crypto_initfn(Object *obj) TYPE_VIRTIO_CRYPTO); } -static const TypeInfo virtio_crypto_pci_info = { - .name = TYPE_VIRTIO_CRYPTO_PCI, - .parent = TYPE_VIRTIO_PCI, +static const VirtioPCIDeviceTypeInfo virtio_crypto_pci_info = { + .generic_name = TYPE_VIRTIO_CRYPTO_PCI, .instance_size = sizeof(VirtIOCryptoPCI), .instance_init = virtio_crypto_initfn, .class_init = virtio_crypto_pci_class_init, @@ -74,6 +87,6 @@ static const TypeInfo virtio_crypto_pci_info = { static void virtio_crypto_pci_register_types(void) { - type_register_static(&virtio_crypto_pci_info); + virtio_pci_types_register(&virtio_crypto_pci_info); } type_init(virtio_crypto_pci_register_types) diff --git a/hw/virtio/virtio-input-host-pci.c b/hw/virtio/virtio-input-host-pci.c new file mode 100644 index 0000000000..725a51ad30 --- /dev/null +++ b/hw/virtio/virtio-input-host-pci.c @@ -0,0 +1,48 @@ +/* + * Virtio input host PCI Bindings + * + * 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 "virtio-pci.h" +#include "hw/virtio/virtio-input.h" + +typedef struct VirtIOInputHostPCI VirtIOInputHostPCI; + +#define TYPE_VIRTIO_INPUT_HOST_PCI "virtio-input-host-pci-base" +#define VIRTIO_INPUT_HOST_PCI(obj) \ + OBJECT_CHECK(VirtIOInputHostPCI, (obj), TYPE_VIRTIO_INPUT_HOST_PCI) + +struct VirtIOInputHostPCI { + VirtIOPCIProxy parent_obj; + VirtIOInputHost vdev; +}; + +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 VirtioPCIDeviceTypeInfo virtio_input_host_pci_info = { + .base_name = TYPE_VIRTIO_INPUT_HOST_PCI, + .generic_name = "virtio-input-host-pci", + .transitional_name = "virtio-input-host-pci-transitional", + .non_transitional_name = "virtio-input-host-pci-non-transitional", + .parent = TYPE_VIRTIO_INPUT_PCI, + .instance_size = sizeof(VirtIOInputHostPCI), + .instance_init = virtio_host_initfn, +}; + +static void virtio_input_host_pci_register(void) +{ + virtio_pci_types_register(&virtio_input_host_pci_info); +} + +type_init(virtio_input_host_pci_register) diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c new file mode 100644 index 0000000000..2c1397842b --- /dev/null +++ b/hw/virtio/virtio-input-pci.c @@ -0,0 +1,157 @@ +/* + * Virtio input PCI Bindings + * + * 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 "virtio-pci.h" +#include "hw/virtio/virtio-input.h" + +typedef struct VirtIOInputPCI VirtIOInputPCI; +typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; + +/* + * virtio-input-pci: This extends VirtioPCIProxy. + */ +#define VIRTIO_INPUT_PCI(obj) \ + OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI) + +struct VirtIOInputPCI { + VirtIOPCIProxy parent_obj; + VirtIOInput vdev; +}; + +#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci" +#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci" +#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci" +#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci" +#define VIRTIO_INPUT_HID_PCI(obj) \ + OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI) + +struct VirtIOInputHIDPCI { + VirtIOPCIProxy parent_obj; + VirtIOInputHID vdev; +}; + +static Property virtio_input_pci_properties[] = { + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&vinput->vdev); + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + virtio_pci_force_virtio_1(vpci_dev); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_input_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + dc->props = virtio_input_pci_properties; + k->realize = virtio_input_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + + pcidev_k->class_id = PCI_CLASS_INPUT_OTHER; +} + +static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) +{ + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD; +} + +static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass, + void *data) +{ + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE; +} + +static void virtio_keyboard_initfn(Object *obj) +{ + VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_KEYBOARD); +} + +static void virtio_mouse_initfn(Object *obj) +{ + VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_MOUSE); +} + +static void virtio_tablet_initfn(Object *obj) +{ + VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_TABLET); +} + +static const TypeInfo virtio_input_pci_info = { + .name = TYPE_VIRTIO_INPUT_PCI, + .parent = TYPE_VIRTIO_PCI, + .instance_size = sizeof(VirtIOInputPCI), + .class_init = virtio_input_pci_class_init, + .abstract = true, +}; + +static const TypeInfo virtio_input_hid_pci_info = { + .name = TYPE_VIRTIO_INPUT_HID_PCI, + .parent = TYPE_VIRTIO_INPUT_PCI, + .instance_size = sizeof(VirtIOInputHIDPCI), + .abstract = true, +}; + +static const VirtioPCIDeviceTypeInfo virtio_keyboard_pci_info = { + .generic_name = TYPE_VIRTIO_KEYBOARD_PCI, + .parent = TYPE_VIRTIO_INPUT_HID_PCI, + .class_init = virtio_input_hid_kbd_pci_class_init, + .instance_size = sizeof(VirtIOInputHIDPCI), + .instance_init = virtio_keyboard_initfn, +}; + +static const VirtioPCIDeviceTypeInfo virtio_mouse_pci_info = { + .generic_name = TYPE_VIRTIO_MOUSE_PCI, + .parent = TYPE_VIRTIO_INPUT_HID_PCI, + .class_init = virtio_input_hid_mouse_pci_class_init, + .instance_size = sizeof(VirtIOInputHIDPCI), + .instance_init = virtio_mouse_initfn, +}; + +static const VirtioPCIDeviceTypeInfo virtio_tablet_pci_info = { + .generic_name = TYPE_VIRTIO_TABLET_PCI, + .parent = TYPE_VIRTIO_INPUT_HID_PCI, + .instance_size = sizeof(VirtIOInputHIDPCI), + .instance_init = virtio_tablet_initfn, +}; + +static void virtio_pci_input_register(void) +{ + /* Base types: */ + type_register_static(&virtio_input_pci_info); + type_register_static(&virtio_input_hid_pci_info); + + /* Implementations: */ + virtio_pci_types_register(&virtio_keyboard_pci_info); + virtio_pci_types_register(&virtio_mouse_pci_info); + virtio_pci_types_register(&virtio_tablet_pci_info); +} + +type_init(virtio_pci_input_register) diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c new file mode 100644 index 0000000000..db07ab9e21 --- /dev/null +++ b/hw/virtio/virtio-net-pci.c @@ -0,0 +1,98 @@ +/* + * Virtio net PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori + * Paul Brook + * + * 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 "hw/virtio/virtio-net.h" +#include "virtio-pci.h" +#include "qapi/error.h" + +typedef struct VirtIONetPCI VirtIONetPCI; + +/* + * virtio-net-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_NET_PCI "virtio-net-pci-base" +#define VIRTIO_NET_PCI(obj) \ + OBJECT_CHECK(VirtIONetPCI, (obj), TYPE_VIRTIO_NET_PCI) + +struct VirtIONetPCI { + VirtIOPCIProxy parent_obj; + VirtIONet vdev; +}; + +static Property virtio_net_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + DeviceState *qdev = DEVICE(vpci_dev); + VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + + virtio_net_set_netclient_name(&dev->vdev, qdev->id, + object_get_typename(OBJECT(qdev))); + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_net_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); + VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); + + k->romfile = "efi-virtio.rom"; + k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + k->device_id = PCI_DEVICE_ID_VIRTIO_NET; + k->revision = VIRTIO_PCI_ABI_VERSION; + k->class_id = PCI_CLASS_NETWORK_ETHERNET; + set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); + dc->props = virtio_net_properties; + vpciklass->realize = virtio_net_pci_realize; +} + +static void virtio_net_pci_instance_init(Object *obj) +{ + VirtIONetPCI *dev = VIRTIO_NET_PCI(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 const VirtioPCIDeviceTypeInfo virtio_net_pci_info = { + .base_name = TYPE_VIRTIO_NET_PCI, + .generic_name = "virtio-net-pci", + .transitional_name = "virtio-net-pci-transitional", + .non_transitional_name = "virtio-net-pci-non-transitional", + .instance_size = sizeof(VirtIONetPCI), + .instance_init = virtio_net_pci_instance_init, + .class_init = virtio_net_pci_class_init, +}; + +static void virtio_net_pci_register(void) +{ + virtio_pci_types_register(&virtio_net_pci_info); +} + +type_init(virtio_net_pci_register) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 3a01fe90f0..cb44e19b67 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -19,12 +19,6 @@ #include "standard-headers/linux/virtio_pci.h" #include "hw/virtio/virtio.h" -#include "hw/virtio/virtio-blk.h" -#include "hw/virtio/virtio-net.h" -#include "hw/virtio/virtio-serial.h" -#include "hw/virtio/virtio-scsi.h" -#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" @@ -597,7 +591,7 @@ virtio_address_space_read(VirtIOPCIProxy *proxy, hwaddr addr, static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, uint32_t val, int len) { - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus); struct virtio_pci_cfg_cap *cfg; @@ -630,7 +624,7 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, static uint32_t virtio_read_config(PCIDevice *pci_dev, uint32_t address, int len) { - VirtIOPCIProxy *proxy = DO_UPCAST(VirtIOPCIProxy, pci_dev, pci_dev); + VirtIOPCIProxy *proxy = VIRTIO_PCI(pci_dev); struct virtio_pci_cfg_cap *cfg; if (proxy->config_cap && @@ -1079,55 +1073,6 @@ static void virtio_pci_vmstate_change(DeviceState *d, bool running) } } -#ifdef CONFIG_VIRTFS -static void virtio_9p_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - V9fsPCIState *dev = VIRTIO_9P_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static Property virtio_9p_pci_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - - k->realize = virtio_9p_pci_realize; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_9P; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = 0x2; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_9p_pci_properties; -} - -static void virtio_9p_pci_instance_init(Object *obj) -{ - V9fsPCIState *dev = VIRTIO_9P_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_9P); -} - -static const TypeInfo virtio_9p_pci_info = { - .name = TYPE_VIRTIO_9P_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(V9fsPCIState), - .instance_init = virtio_9p_pci_instance_init, - .class_init = virtio_9p_pci_class_init, -}; -#endif /* CONFIG_VIRTFS */ - /* * virtio-pci: This is the PCIDevice which has a virtio-pci-bus. */ @@ -1683,8 +1628,8 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) 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); + warn_report("unable to init msix vectors to %" PRIu32, + proxy->nvectors); } proxy->nvectors = 0; } @@ -1877,9 +1822,6 @@ static void virtio_pci_reset(DeviceState *qdev) static Property virtio_pci_properties[] = { DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), - DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy, - ON_OFF_AUTO_AUTO), - DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true), DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags, @@ -1939,712 +1881,124 @@ static const TypeInfo virtio_pci_info = { .class_init = virtio_pci_class_init, .class_size = sizeof(VirtioPCIClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { - { INTERFACE_PCIE_DEVICE }, - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { } - }, }; -/* virtio-blk-pci */ - -static Property virtio_blk_pci_properties[] = { - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), +static Property virtio_pci_generic_properties[] = { + DEFINE_PROP_ON_OFF_AUTO("disable-legacy", VirtIOPCIProxy, disable_legacy, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), DEFINE_PROP_END_OF_LIST(), }; -static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +static void virtio_pci_base_class_init(ObjectClass *klass, void *data) { - VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = dev->vdev.conf.num_queues + 1; + const VirtioPCIDeviceTypeInfo *t = data; + if (t->class_init) { + t->class_init(klass, NULL); } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); } -static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) +static void virtio_pci_generic_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_blk_pci_properties; - k->realize = virtio_blk_pci_realize; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; + dc->props = virtio_pci_generic_properties; } -static void virtio_blk_pci_instance_init(Object *obj) +/* Used when the generic type and the base type is the same */ +static void virtio_pci_generic_base_class_init(ObjectClass *klass, void *data) { - VirtIOBlkPCI *dev = VIRTIO_BLK_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BLK); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); + virtio_pci_base_class_init(klass, data); + virtio_pci_generic_class_init(klass, NULL); } -static const TypeInfo virtio_blk_pci_info = { - .name = TYPE_VIRTIO_BLK_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOBlkPCI), - .instance_init = virtio_blk_pci_instance_init, - .class_init = virtio_blk_pci_class_init, -}; - -#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) -/* vhost-user-blk */ - -static Property vhost_user_blk_pci_properties[] = { - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +static void virtio_pci_transitional_instance_init(Object *obj) { - VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOPCIProxy *proxy = VIRTIO_PCI(obj); - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = dev->vdev.num_queues + 1; + proxy->disable_legacy = ON_OFF_AUTO_OFF; + proxy->disable_modern = false; +} + +static void virtio_pci_non_transitional_instance_init(Object *obj) +{ + VirtIOPCIProxy *proxy = VIRTIO_PCI(obj); + + proxy->disable_legacy = ON_OFF_AUTO_ON; + proxy->disable_modern = false; +} + +void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) +{ + TypeInfo base_type_info = { + .name = t->base_name, + .parent = t->parent ? t->parent : TYPE_VIRTIO_PCI, + .instance_size = t->instance_size, + .instance_init = t->instance_init, + .class_size = t->class_size, + .class_init = virtio_pci_base_class_init, + .class_data = (void *)t, + .abstract = true, + }; + TypeInfo generic_type_info = { + .name = t->generic_name, + .parent = base_type_info.name, + .class_init = virtio_pci_generic_class_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, + }; + + if (!base_type_info.name) { + /* No base type -> register a single generic device type */ + base_type_info.name = t->generic_name; + base_type_info.class_init = virtio_pci_generic_base_class_init; + base_type_info.interfaces = generic_type_info.interfaces; + base_type_info.abstract = false; + generic_type_info.name = NULL; + assert(!t->non_transitional_name); + assert(!t->transitional_name); } - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_user_blk_pci_properties; - k->realize = vhost_user_blk_pci_realize; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BLOCK; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void vhost_user_blk_pci_instance_init(Object *obj) -{ - VHostUserBlkPCI *dev = VHOST_USER_BLK_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_USER_BLK); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static const TypeInfo vhost_user_blk_pci_info = { - .name = TYPE_VHOST_USER_BLK_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VHostUserBlkPCI), - .instance_init = vhost_user_blk_pci_instance_init, - .class_init = vhost_user_blk_pci_class_init, -}; -#endif - -/* virtio-scsi-pci */ - -static Property virtio_scsi_pci_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - DeviceState *proxy = DEVICE(vpci_dev); - char *bus_name; - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = vs->conf.num_queues + 3; + type_register(&base_type_info); + if (generic_type_info.name) { + type_register(&generic_type_info); } - /* - * For command line compatibility, this sets the virtio-scsi-device bus - * name as before. - */ - if (proxy->id) { - bus_name = g_strdup_printf("%s.0", proxy->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); + if (t->non_transitional_name) { + const TypeInfo non_transitional_type_info = { + .name = t->non_transitional_name, + .parent = base_type_info.name, + .instance_init = virtio_pci_non_transitional_instance_init, + .interfaces = (InterfaceInfo[]) { + { INTERFACE_PCIE_DEVICE }, + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, + }; + type_register(&non_transitional_type_info); } - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - k->realize = virtio_scsi_pci_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = virtio_scsi_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - pcidev_k->revision = 0x00; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void virtio_scsi_pci_instance_init(Object *obj) -{ - VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SCSI); -} - -static const TypeInfo virtio_scsi_pci_info = { - .name = TYPE_VIRTIO_SCSI_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOSCSIPCI), - .instance_init = virtio_scsi_pci_instance_init, - .class_init = virtio_scsi_pci_class_init, -}; - -/* vhost-scsi-pci */ - -#ifdef CONFIG_VHOST_SCSI -static Property vhost_scsi_pci_properties[] = { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VHostSCSIPCI *dev = VHOST_SCSI_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = vs->conf.num_queues + 3; + if (t->transitional_name) { + const TypeInfo transitional_type_info = { + .name = t->transitional_name, + .parent = base_type_info.name, + .instance_init = virtio_pci_transitional_instance_init, + .interfaces = (InterfaceInfo[]) { + /* + * Transitional virtio devices work only as Conventional PCI + * devices because they require PIO ports. + */ + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { } + }, + }; + type_register(&transitional_type_info); } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); } -static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = vhost_scsi_pci_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_scsi_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - pcidev_k->revision = 0x00; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void vhost_scsi_pci_instance_init(Object *obj) -{ - VHostSCSIPCI *dev = VHOST_SCSI_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_SCSI); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static const TypeInfo vhost_scsi_pci_info = { - .name = TYPE_VHOST_SCSI_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VHostSCSIPCI), - .instance_init = vhost_scsi_pci_instance_init, - .class_init = vhost_scsi_pci_class_init, -}; -#endif - -#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) -/* vhost-user-scsi-pci */ -static Property vhost_user_scsi_pci_properties[] = { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, - DEV_NVECTORS_UNSPECIFIED), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); - - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = vs->conf.num_queues + 3; - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = vhost_user_scsi_pci_realize; - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); - dc->props = vhost_user_scsi_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; - pcidev_k->revision = 0x00; - pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; -} - -static void vhost_user_scsi_pci_instance_init(Object *obj) -{ - VHostUserSCSIPCI *dev = VHOST_USER_SCSI_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_USER_SCSI); - object_property_add_alias(obj, "bootindex", OBJECT(&dev->vdev), - "bootindex", &error_abort); -} - -static const TypeInfo vhost_user_scsi_pci_info = { - .name = TYPE_VHOST_USER_SCSI_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VHostUserSCSIPCI), - .instance_init = vhost_user_scsi_pci_instance_init, - .class_init = vhost_user_scsi_pci_class_init, -}; -#endif - -/* vhost-vsock-pci */ - -#ifdef CONFIG_VHOST_VSOCK -static Property vhost_vsock_pci_properties[] = { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), -}; - -static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VHostVSockPCI *dev = VHOST_VSOCK_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = vhost_vsock_pci_realize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = vhost_vsock_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_VSOCK; - pcidev_k->revision = 0x00; - pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; -} - -static void vhost_vsock_pci_instance_init(Object *obj) -{ - VHostVSockPCI *dev = VHOST_VSOCK_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VHOST_VSOCK); -} - -static const TypeInfo vhost_vsock_pci_info = { - .name = TYPE_VHOST_VSOCK_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VHostVSockPCI), - .instance_init = vhost_vsock_pci_instance_init, - .class_init = vhost_vsock_pci_class_init, -}; -#endif - -/* virtio-balloon-pci */ - -static Property virtio_balloon_pci_properties[] = { - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - if (vpci_dev->class_code != PCI_CLASS_OTHERS && - vpci_dev->class_code != PCI_CLASS_MEMORY_RAM) { /* qemu < 1.1 */ - vpci_dev->class_code = PCI_CLASS_OTHERS; - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = virtio_balloon_pci_realize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - dc->props = virtio_balloon_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_BALLOON; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_OTHERS; -} - -static void virtio_balloon_pci_instance_init(Object *obj) -{ - VirtIOBalloonPCI *dev = VIRTIO_BALLOON_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_BALLOON); - object_property_add_alias(obj, "guest-stats", OBJECT(&dev->vdev), - "guest-stats", &error_abort); - object_property_add_alias(obj, "guest-stats-polling-interval", - OBJECT(&dev->vdev), - "guest-stats-polling-interval", &error_abort); -} - -static const TypeInfo virtio_balloon_pci_info = { - .name = TYPE_VIRTIO_BALLOON_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOBalloonPCI), - .instance_init = virtio_balloon_pci_instance_init, - .class_init = virtio_balloon_pci_class_init, -}; - -/* virtio-serial-pci */ - -static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - DeviceState *proxy = DEVICE(vpci_dev); - char *bus_name; - - if (vpci_dev->class_code != PCI_CLASS_COMMUNICATION_OTHER && - vpci_dev->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */ - vpci_dev->class_code != PCI_CLASS_OTHERS) { /* qemu-kvm */ - vpci_dev->class_code = PCI_CLASS_COMMUNICATION_OTHER; - } - - /* backwards-compatibility with machines that were created with - DEV_NVECTORS_UNSPECIFIED */ - if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { - vpci_dev->nvectors = dev->vdev.serial.max_virtserial_ports + 1; - } - - /* - * For command line compatibility, this sets the virtio-serial-device bus - * name as before. - */ - if (proxy->id) { - bus_name = g_strdup_printf("%s.0", proxy->id); - virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); - g_free(bus_name); - } - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static Property virtio_serial_pci_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - k->realize = virtio_serial_pci_realize; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - dc->props = virtio_serial_pci_properties; - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; -} - -static void virtio_serial_pci_instance_init(Object *obj) -{ - VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_SERIAL); -} - -static const TypeInfo virtio_serial_pci_info = { - .name = TYPE_VIRTIO_SERIAL_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOSerialPCI), - .instance_init = virtio_serial_pci_instance_init, - .class_init = virtio_serial_pci_class_init, -}; - -/* virtio-net-pci */ - -static Property virtio_net_properties[] = { - DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 3), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - DeviceState *qdev = DEVICE(vpci_dev); - VirtIONetPCI *dev = VIRTIO_NET_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&dev->vdev); - - virtio_net_set_netclient_name(&dev->vdev, qdev->id, - object_get_typename(OBJECT(qdev))); - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_net_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); - - k->romfile = "efi-virtio.rom"; - k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - k->device_id = PCI_DEVICE_ID_VIRTIO_NET; - k->revision = VIRTIO_PCI_ABI_VERSION; - k->class_id = PCI_CLASS_NETWORK_ETHERNET; - set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); - dc->props = virtio_net_properties; - vpciklass->realize = virtio_net_pci_realize; -} - -static void virtio_net_pci_instance_init(Object *obj) -{ - VirtIONetPCI *dev = VIRTIO_NET_PCI(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 const TypeInfo virtio_net_pci_info = { - .name = TYPE_VIRTIO_NET_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIONetPCI), - .instance_init = virtio_net_pci_instance_init, - .class_init = virtio_net_pci_class_init, -}; - -/* virtio-rng-pci */ - -static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&vrng->vdev); - Error *err = NULL; - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - object_property_set_bool(OBJECT(vdev), true, "realized", &err); - if (err) { - error_propagate(errp, err); - return; - } - - object_property_set_link(OBJECT(vrng), - OBJECT(vrng->vdev.conf.rng), "rng", - NULL); -} - -static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - k->realize = virtio_rng_pci_realize; - set_bit(DEVICE_CATEGORY_MISC, dc->categories); - - pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; - pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; - pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; - pcidev_k->class_id = PCI_CLASS_OTHERS; -} - -static void virtio_rng_initfn(Object *obj) -{ - VirtIORngPCI *dev = VIRTIO_RNG_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_RNG); -} - -static const TypeInfo virtio_rng_pci_info = { - .name = TYPE_VIRTIO_RNG_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIORngPCI), - .instance_init = virtio_rng_initfn, - .class_init = virtio_rng_pci_class_init, -}; - -/* virtio-input-pci */ - -static Property virtio_input_pci_properties[] = { - DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), - DEFINE_PROP_END_OF_LIST(), -}; - -static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) -{ - VirtIOInputPCI *vinput = VIRTIO_INPUT_PCI(vpci_dev); - DeviceState *vdev = DEVICE(&vinput->vdev); - - qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); - virtio_pci_force_virtio_1(vpci_dev); - object_property_set_bool(OBJECT(vdev), true, "realized", errp); -} - -static void virtio_input_pci_class_init(ObjectClass *klass, void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - dc->props = virtio_input_pci_properties; - k->realize = virtio_input_pci_realize; - set_bit(DEVICE_CATEGORY_INPUT, dc->categories); - - pcidev_k->class_id = PCI_CLASS_INPUT_OTHER; -} - -static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) -{ - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - pcidev_k->class_id = PCI_CLASS_INPUT_KEYBOARD; -} - -static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass, - void *data) -{ - PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); - - pcidev_k->class_id = PCI_CLASS_INPUT_MOUSE; -} - -static void virtio_keyboard_initfn(Object *obj) -{ - VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_KEYBOARD); -} - -static void virtio_mouse_initfn(Object *obj) -{ - VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_MOUSE); -} - -static void virtio_tablet_initfn(Object *obj) -{ - VirtIOInputHIDPCI *dev = VIRTIO_INPUT_HID_PCI(obj); - - virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), - TYPE_VIRTIO_TABLET); -} - -static const TypeInfo virtio_input_pci_info = { - .name = TYPE_VIRTIO_INPUT_PCI, - .parent = TYPE_VIRTIO_PCI, - .instance_size = sizeof(VirtIOInputPCI), - .class_init = virtio_input_pci_class_init, - .abstract = true, -}; - -static const TypeInfo virtio_input_hid_pci_info = { - .name = TYPE_VIRTIO_INPUT_HID_PCI, - .parent = TYPE_VIRTIO_INPUT_PCI, - .instance_size = sizeof(VirtIOInputHIDPCI), - .abstract = true, -}; - -static const TypeInfo virtio_keyboard_pci_info = { - .name = TYPE_VIRTIO_KEYBOARD_PCI, - .parent = TYPE_VIRTIO_INPUT_HID_PCI, - .class_init = virtio_input_hid_kbd_pci_class_init, - .instance_size = sizeof(VirtIOInputHIDPCI), - .instance_init = virtio_keyboard_initfn, -}; - -static const TypeInfo virtio_mouse_pci_info = { - .name = TYPE_VIRTIO_MOUSE_PCI, - .parent = TYPE_VIRTIO_INPUT_HID_PCI, - .class_init = virtio_input_hid_mouse_pci_class_init, - .instance_size = sizeof(VirtIOInputHIDPCI), - .instance_init = virtio_mouse_initfn, -}; - -static const TypeInfo virtio_tablet_pci_info = { - .name = TYPE_VIRTIO_TABLET_PCI, - .parent = TYPE_VIRTIO_INPUT_HID_PCI, - .instance_size = sizeof(VirtIOInputHIDPCI), - .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 */ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, @@ -2692,37 +2046,10 @@ static const TypeInfo virtio_pci_bus_info = { static void virtio_pci_register_types(void) { - type_register_static(&virtio_rng_pci_info); - type_register_static(&virtio_input_pci_info); - type_register_static(&virtio_input_hid_pci_info); - 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 + /* Base types: */ type_register_static(&virtio_pci_bus_info); type_register_static(&virtio_pci_info); -#ifdef CONFIG_VIRTFS - type_register_static(&virtio_9p_pci_info); -#endif - type_register_static(&virtio_blk_pci_info); -#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) - type_register_static(&vhost_user_blk_pci_info); -#endif - type_register_static(&virtio_scsi_pci_info); - type_register_static(&virtio_balloon_pci_info); - type_register_static(&virtio_serial_pci_info); - type_register_static(&virtio_net_pci_info); -#ifdef CONFIG_VHOST_SCSI - type_register_static(&vhost_scsi_pci_info); -#endif -#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) - type_register_static(&vhost_user_scsi_pci_info); -#endif -#ifdef CONFIG_VHOST_VSOCK - type_register_static(&vhost_vsock_pci_info); -#endif } type_init(virtio_pci_register_types) + diff --git a/hw/virtio/virtio-pci.h b/hw/virtio/virtio-pci.h index 813082b0d7..18581854ca 100644 --- a/hw/virtio/virtio-pci.h +++ b/hw/virtio/virtio-pci.h @@ -16,47 +16,9 @@ #define QEMU_VIRTIO_PCI_H #include "hw/pci/msi.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-balloon.h" #include "hw/virtio/virtio-bus.h" -#include "hw/virtio/virtio-input.h" -#include "hw/virtio/virtio-gpu.h" -#include "hw/virtio/virtio-crypto.h" -#include "hw/virtio/vhost-user-scsi.h" -#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) -#include "hw/virtio/vhost-user-blk.h" -#endif - -#ifdef CONFIG_VIRTFS -#include "hw/9pfs/virtio-9p.h" -#endif -#ifdef CONFIG_VHOST_SCSI -#include "hw/virtio/vhost-scsi.h" -#endif -#ifdef CONFIG_VHOST_VSOCK -#include "hw/virtio/vhost-vsock.h" -#endif typedef struct VirtIOPCIProxy VirtIOPCIProxy; -typedef struct VirtIOBlkPCI VirtIOBlkPCI; -typedef struct VirtIOSCSIPCI VirtIOSCSIPCI; -typedef struct VirtIOBalloonPCI VirtIOBalloonPCI; -typedef struct VirtIOSerialPCI VirtIOSerialPCI; -typedef struct VirtIONetPCI VirtIONetPCI; -typedef struct VHostSCSIPCI VHostSCSIPCI; -typedef struct VHostUserSCSIPCI VHostUserSCSIPCI; -typedef struct VHostUserBlkPCI VHostUserBlkPCI; -typedef struct VirtIORngPCI VirtIORngPCI; -typedef struct VirtIOInputPCI VirtIOInputPCI; -typedef struct VirtIOInputHIDPCI VirtIOInputHIDPCI; -typedef struct VirtIOInputHostPCI VirtIOInputHostPCI; -typedef struct VirtIOGPUPCI VirtIOGPUPCI; -typedef struct VHostVSockPCI VHostVSockPCI; -typedef struct VirtIOCryptoPCI VirtIOCryptoPCI; /* virtio-pci-bus */ @@ -213,208 +175,67 @@ static inline void virtio_pci_disable_modern(VirtIOPCIProxy *proxy) proxy->disable_modern = true; } -/* - * virtio-scsi-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_SCSI_PCI "virtio-scsi-pci" -#define VIRTIO_SCSI_PCI(obj) \ - OBJECT_CHECK(VirtIOSCSIPCI, (obj), TYPE_VIRTIO_SCSI_PCI) - -struct VirtIOSCSIPCI { - VirtIOPCIProxy parent_obj; - VirtIOSCSI vdev; -}; - -#ifdef CONFIG_VHOST_SCSI -/* - * vhost-scsi-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VHOST_SCSI_PCI "vhost-scsi-pci" -#define VHOST_SCSI_PCI(obj) \ - OBJECT_CHECK(VHostSCSIPCI, (obj), TYPE_VHOST_SCSI_PCI) - -struct VHostSCSIPCI { - VirtIOPCIProxy parent_obj; - VHostSCSI vdev; -}; -#endif - -#define TYPE_VHOST_USER_SCSI_PCI "vhost-user-scsi-pci" -#define VHOST_USER_SCSI_PCI(obj) \ - OBJECT_CHECK(VHostUserSCSIPCI, (obj), TYPE_VHOST_USER_SCSI_PCI) - -struct VHostUserSCSIPCI { - VirtIOPCIProxy parent_obj; - VHostUserSCSI vdev; -}; - -#if defined(CONFIG_VHOST_USER) && defined(CONFIG_LINUX) -/* - * vhost-user-blk-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VHOST_USER_BLK_PCI "vhost-user-blk-pci" -#define VHOST_USER_BLK_PCI(obj) \ - OBJECT_CHECK(VHostUserBlkPCI, (obj), TYPE_VHOST_USER_BLK_PCI) - -struct VHostUserBlkPCI { - VirtIOPCIProxy parent_obj; - VHostUserBlk vdev; -}; -#endif - -/* - * virtio-blk-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_BLK_PCI "virtio-blk-pci" -#define VIRTIO_BLK_PCI(obj) \ - OBJECT_CHECK(VirtIOBlkPCI, (obj), TYPE_VIRTIO_BLK_PCI) - -struct VirtIOBlkPCI { - VirtIOPCIProxy parent_obj; - VirtIOBlock vdev; -}; - -/* - * virtio-balloon-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_BALLOON_PCI "virtio-balloon-pci" -#define VIRTIO_BALLOON_PCI(obj) \ - OBJECT_CHECK(VirtIOBalloonPCI, (obj), TYPE_VIRTIO_BALLOON_PCI) - -struct VirtIOBalloonPCI { - VirtIOPCIProxy parent_obj; - VirtIOBalloon vdev; -}; - -/* - * virtio-serial-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_SERIAL_PCI "virtio-serial-pci" -#define VIRTIO_SERIAL_PCI(obj) \ - OBJECT_CHECK(VirtIOSerialPCI, (obj), TYPE_VIRTIO_SERIAL_PCI) - -struct VirtIOSerialPCI { - VirtIOPCIProxy parent_obj; - VirtIOSerial vdev; -}; - -/* - * virtio-net-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_NET_PCI "virtio-net-pci" -#define VIRTIO_NET_PCI(obj) \ - OBJECT_CHECK(VirtIONetPCI, (obj), TYPE_VIRTIO_NET_PCI) - -struct VirtIONetPCI { - VirtIOPCIProxy parent_obj; - VirtIONet vdev; -}; - -/* - * virtio-9p-pci: This extends VirtioPCIProxy. - */ - -#ifdef CONFIG_VIRTFS - -#define TYPE_VIRTIO_9P_PCI "virtio-9p-pci" -#define VIRTIO_9P_PCI(obj) \ - OBJECT_CHECK(V9fsPCIState, (obj), TYPE_VIRTIO_9P_PCI) - -typedef struct V9fsPCIState { - VirtIOPCIProxy parent_obj; - V9fsVirtioState vdev; -} V9fsPCIState; - -#endif - -/* - * virtio-rng-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_RNG_PCI "virtio-rng-pci" -#define VIRTIO_RNG_PCI(obj) \ - OBJECT_CHECK(VirtIORngPCI, (obj), TYPE_VIRTIO_RNG_PCI) - -struct VirtIORngPCI { - VirtIOPCIProxy parent_obj; - VirtIORNG vdev; -}; - /* * virtio-input-pci: This extends VirtioPCIProxy. */ #define TYPE_VIRTIO_INPUT_PCI "virtio-input-pci" -#define VIRTIO_INPUT_PCI(obj) \ - OBJECT_CHECK(VirtIOInputPCI, (obj), TYPE_VIRTIO_INPUT_PCI) - -struct VirtIOInputPCI { - VirtIOPCIProxy parent_obj; - VirtIOInput vdev; -}; - -#define TYPE_VIRTIO_INPUT_HID_PCI "virtio-input-hid-pci" -#define TYPE_VIRTIO_KEYBOARD_PCI "virtio-keyboard-pci" -#define TYPE_VIRTIO_MOUSE_PCI "virtio-mouse-pci" -#define TYPE_VIRTIO_TABLET_PCI "virtio-tablet-pci" -#define VIRTIO_INPUT_HID_PCI(obj) \ - OBJECT_CHECK(VirtIOInputHIDPCI, (obj), TYPE_VIRTIO_INPUT_HID_PCI) - -struct VirtIOInputHIDPCI { - VirtIOPCIProxy parent_obj; - 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) - -struct VirtIOInputHostPCI { - VirtIOPCIProxy parent_obj; - VirtIOInputHost vdev; -}; - -#endif - -/* - * virtio-gpu-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_GPU_PCI "virtio-gpu-pci" -#define VIRTIO_GPU_PCI(obj) \ - OBJECT_CHECK(VirtIOGPUPCI, (obj), TYPE_VIRTIO_GPU_PCI) - -struct VirtIOGPUPCI { - VirtIOPCIProxy parent_obj; - VirtIOGPU vdev; -}; - -#ifdef CONFIG_VHOST_VSOCK -/* - * vhost-vsock-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VHOST_VSOCK_PCI "vhost-vsock-pci" -#define VHOST_VSOCK_PCI(obj) \ - OBJECT_CHECK(VHostVSockPCI, (obj), TYPE_VHOST_VSOCK_PCI) - -struct VHostVSockPCI { - VirtIOPCIProxy parent_obj; - VHostVSock vdev; -}; -#endif - -/* - * virtio-crypto-pci: This extends VirtioPCIProxy. - */ -#define TYPE_VIRTIO_CRYPTO_PCI "virtio-crypto-pci" -#define VIRTIO_CRYPTO_PCI(obj) \ - OBJECT_CHECK(VirtIOCryptoPCI, (obj), TYPE_VIRTIO_CRYPTO_PCI) - -struct VirtIOCryptoPCI { - VirtIOPCIProxy parent_obj; - VirtIOCrypto vdev; -}; /* Virtio ABI version, if we increment this, we break the guest driver. */ #define VIRTIO_PCI_ABI_VERSION 0 +/* Input for virtio_pci_types_register() */ +typedef struct VirtioPCIDeviceTypeInfo { + /* + * Common base class for the subclasses below. + * + * Required only if transitional_name or non_transitional_name is set. + * + * We need a separate base type instead of making all types + * inherit from generic_name for two reasons: + * 1) generic_name implements INTERFACE_PCIE_DEVICE, but + * transitional_name does not. + * 2) generic_name has the "disable-legacy" and "disable-modern" + * properties, transitional_name and non_transitional name don't. + */ + const char *base_name; + /* + * Generic device type. Optional. + * + * Supports both transitional and non-transitional modes, + * using the disable-legacy and disable-modern properties. + * If disable-legacy=auto, (non-)transitional mode is selected + * depending on the bus where the device is plugged. + * + * Implements both INTERFACE_PCIE_DEVICE and INTERFACE_CONVENTIONAL_PCI_DEVICE, + * but PCI Express is supported only in non-transitional mode. + * + * The only type implemented by QEMU 3.1 and older. + */ + const char *generic_name; + /* + * The transitional device type. Optional. + * + * Implements both INTERFACE_PCIE_DEVICE and INTERFACE_CONVENTIONAL_PCI_DEVICE. + */ + const char *transitional_name; + /* + * The non-transitional device type. Optional. + * + * Implements INTERFACE_CONVENTIONAL_PCI_DEVICE only. + */ + const char *non_transitional_name; + + /* Parent type. If NULL, TYPE_VIRTIO_PCI is used */ + const char *parent; + + /* Same as TypeInfo fields: */ + size_t instance_size; + size_t class_size; + void (*instance_init)(Object *obj); + void (*class_init)(ObjectClass *klass, void *data); +} VirtioPCIDeviceTypeInfo; + +/* Register virtio-pci type(s). @t must be static. */ +void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t); + #endif diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c new file mode 100644 index 0000000000..6cc6374289 --- /dev/null +++ b/hw/virtio/virtio-rng-pci.c @@ -0,0 +1,88 @@ +/* + * Virtio rng PCI Bindings + * + * Copyright 2012 Red Hat, Inc. + * Copyright 2012 Amit Shah + * + * 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 "virtio-pci.h" +#include "hw/virtio/virtio-rng.h" +#include "qapi/error.h" + +typedef struct VirtIORngPCI VirtIORngPCI; + +/* + * virtio-rng-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_RNG_PCI "virtio-rng-pci-base" +#define VIRTIO_RNG_PCI(obj) \ + OBJECT_CHECK(VirtIORngPCI, (obj), TYPE_VIRTIO_RNG_PCI) + +struct VirtIORngPCI { + VirtIOPCIProxy parent_obj; + VirtIORNG vdev; +}; + +static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIORngPCI *vrng = VIRTIO_RNG_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&vrng->vdev); + Error *err = NULL; + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", &err); + if (err) { + error_propagate(errp, err); + return; + } + + object_property_set_link(OBJECT(vrng), + OBJECT(vrng->vdev.conf.rng), "rng", + NULL); +} + +static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + k->realize = virtio_rng_pci_realize; + set_bit(DEVICE_CATEGORY_MISC, dc->categories); + + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_RNG; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_OTHERS; +} + +static void virtio_rng_initfn(Object *obj) +{ + VirtIORngPCI *dev = VIRTIO_RNG_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_RNG); +} + +static const VirtioPCIDeviceTypeInfo virtio_rng_pci_info = { + .base_name = TYPE_VIRTIO_RNG_PCI, + .generic_name = "virtio-rng-pci", + .transitional_name = "virtio-rng-pci-transitional", + .non_transitional_name = "virtio-rng-pci-non-transitional", + .instance_size = sizeof(VirtIORngPCI), + .instance_init = virtio_rng_initfn, + .class_init = virtio_rng_pci_class_init, +}; + +static void virtio_rng_pci_register(void) +{ + virtio_pci_types_register(&virtio_rng_pci_info); +} + +type_init(virtio_rng_pci_register) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 855f1b41d1..30493a2586 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -191,7 +191,7 @@ static void virtio_rng_device_realize(DeviceState *dev, Error **errp) if (vrng->conf.rng == NULL) { vrng->conf.default_backend = RNG_RANDOM(object_new(TYPE_RNG_RANDOM)); - user_creatable_complete(OBJECT(vrng->conf.default_backend), + user_creatable_complete(USER_CREATABLE(vrng->conf.default_backend), &local_err); if (local_err) { error_propagate(errp, local_err); diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c new file mode 100644 index 0000000000..2830849729 --- /dev/null +++ b/hw/virtio/virtio-scsi-pci.c @@ -0,0 +1,107 @@ +/* + * Virtio scsi PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori + * Paul Brook + * + * 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/virtio/virtio-scsi.h" +#include "virtio-pci.h" + +typedef struct VirtIOSCSIPCI VirtIOSCSIPCI; + +/* + * virtio-scsi-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_SCSI_PCI "virtio-scsi-pci-base" +#define VIRTIO_SCSI_PCI(obj) \ + OBJECT_CHECK(VirtIOSCSIPCI, (obj), TYPE_VIRTIO_SCSI_PCI) + +struct VirtIOSCSIPCI { + VirtIOPCIProxy parent_obj; + VirtIOSCSI vdev; +}; + +static Property virtio_scsi_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, + DEV_NVECTORS_UNSPECIFIED), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); + DeviceState *proxy = DEVICE(vpci_dev); + char *bus_name; + + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = vs->conf.num_queues + 3; + } + + /* + * For command line compatibility, this sets the virtio-scsi-device bus + * name as before. + */ + if (proxy->id) { + bus_name = g_strdup_printf("%s.0", proxy->id); + virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); + g_free(bus_name); + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + + k->realize = virtio_scsi_pci_realize; + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); + dc->props = virtio_scsi_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_SCSI; + pcidev_k->revision = 0x00; + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; +} + +static void virtio_scsi_pci_instance_init(Object *obj) +{ + VirtIOSCSIPCI *dev = VIRTIO_SCSI_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_SCSI); +} + +static const VirtioPCIDeviceTypeInfo virtio_scsi_pci_info = { + .base_name = TYPE_VIRTIO_SCSI_PCI, + .generic_name = "virtio-scsi-pci", + .transitional_name = "virtio-scsi-pci-transitional", + .non_transitional_name = "virtio-scsi-pci-non-transitional", + .instance_size = sizeof(VirtIOSCSIPCI), + .instance_init = virtio_scsi_pci_instance_init, + .class_init = virtio_scsi_pci_class_init, +}; + +static void virtio_scsi_pci_register(void) +{ + virtio_pci_types_register(&virtio_scsi_pci_info); +} + +type_init(virtio_scsi_pci_register) diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c new file mode 100644 index 0000000000..971b2eb8d8 --- /dev/null +++ b/hw/virtio/virtio-serial-pci.c @@ -0,0 +1,115 @@ +/* + * Virtio serial PCI Bindings + * + * Copyright IBM, Corp. 2007 + * Copyright (c) 2009 CodeSourcery + * + * Authors: + * Anthony Liguori + * Paul Brook + * + * 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 "hw/virtio/virtio-serial.h" +#include "virtio-pci.h" + +typedef struct VirtIOSerialPCI VirtIOSerialPCI; + +/* + * virtio-serial-pci: This extends VirtioPCIProxy. + */ +#define TYPE_VIRTIO_SERIAL_PCI "virtio-serial-pci-base" +#define VIRTIO_SERIAL_PCI(obj) \ + OBJECT_CHECK(VirtIOSerialPCI, (obj), TYPE_VIRTIO_SERIAL_PCI) + +struct VirtIOSerialPCI { + VirtIOPCIProxy parent_obj; + VirtIOSerial vdev; +}; + +static void virtio_serial_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) +{ + VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(vpci_dev); + DeviceState *vdev = DEVICE(&dev->vdev); + DeviceState *proxy = DEVICE(vpci_dev); + char *bus_name; + + if (vpci_dev->class_code != PCI_CLASS_COMMUNICATION_OTHER && + vpci_dev->class_code != PCI_CLASS_DISPLAY_OTHER && /* qemu 0.10 */ + vpci_dev->class_code != PCI_CLASS_OTHERS) { /* qemu-kvm */ + vpci_dev->class_code = PCI_CLASS_COMMUNICATION_OTHER; + } + + /* backwards-compatibility with machines that were created with + DEV_NVECTORS_UNSPECIFIED */ + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { + vpci_dev->nvectors = dev->vdev.serial.max_virtserial_ports + 1; + } + + /* + * For command line compatibility, this sets the virtio-serial-device bus + * name as before. + */ + if (proxy->id) { + bus_name = g_strdup_printf("%s.0", proxy->id); + virtio_device_set_child_bus_name(VIRTIO_DEVICE(vdev), bus_name); + g_free(bus_name); + } + + qdev_set_parent_bus(vdev, BUS(&vpci_dev->bus)); + object_property_set_bool(OBJECT(vdev), true, "realized", errp); +} + +static Property virtio_serial_pci_properties[] = { + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); + k->realize = virtio_serial_pci_realize; + set_bit(DEVICE_CATEGORY_INPUT, dc->categories); + dc->props = virtio_serial_pci_properties; + pcidev_k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET; + pcidev_k->device_id = PCI_DEVICE_ID_VIRTIO_CONSOLE; + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; + pcidev_k->class_id = PCI_CLASS_COMMUNICATION_OTHER; +} + +static void virtio_serial_pci_instance_init(Object *obj) +{ + VirtIOSerialPCI *dev = VIRTIO_SERIAL_PCI(obj); + + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), + TYPE_VIRTIO_SERIAL); +} + +static const VirtioPCIDeviceTypeInfo virtio_serial_pci_info = { + .base_name = TYPE_VIRTIO_SERIAL_PCI, + .generic_name = "virtio-serial-pci", + .transitional_name = "virtio-serial-pci-transitional", + .non_transitional_name = "virtio-serial-pci-non-transitional", + .instance_size = sizeof(VirtIOSerialPCI), + .instance_init = virtio_serial_pci_instance_init, + .class_init = virtio_serial_pci_class_init, +}; + +static void virtio_serial_pci_register(void) +{ + virtio_pci_types_register(&virtio_serial_pci_info); +} + +type_init(virtio_serial_pci_register) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index d4e4d98b59..2626a895cb 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -358,6 +358,10 @@ int virtio_queue_ready(VirtQueue *vq) * Called within rcu_read_lock(). */ static int virtio_queue_empty_rcu(VirtQueue *vq) { + if (unlikely(vq->vdev->broken)) { + return 1; + } + if (unlikely(!vq->vring.avail)) { return 1; } @@ -373,6 +377,10 @@ int virtio_queue_empty(VirtQueue *vq) { bool empty; + if (unlikely(vq->vdev->broken)) { + return 1; + } + if (unlikely(!vq->vring.avail)) { return 1; } @@ -638,7 +646,7 @@ void virtqueue_get_avail_bytes(VirtQueue *vq, unsigned int *in_bytes, vring_desc_read(vdev, &desc, desc_cache, i); if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingDesc)) { + if (!desc.len || (desc.len % sizeof(VRingDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto err; } @@ -788,13 +796,13 @@ static void virtqueue_undo_map_desc(unsigned int out_num, unsigned int in_num, } static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, - hwaddr *addr, unsigned int *num_sg, + hwaddr *addr, unsigned int num_sg, int is_write) { unsigned int i; hwaddr len; - for (i = 0; i < *num_sg; i++) { + for (i = 0; i < num_sg; i++) { len = sg[i].iov_len; sg[i].iov_base = dma_memory_map(vdev->dma_as, addr[i], &len, is_write ? @@ -813,8 +821,8 @@ static void virtqueue_map_iovec(VirtIODevice *vdev, struct iovec *sg, void virtqueue_map(VirtIODevice *vdev, VirtQueueElement *elem) { - virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, &elem->in_num, 1); - virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, &elem->out_num, 0); + virtqueue_map_iovec(vdev, elem->in_sg, elem->in_addr, elem->in_num, 1); + virtqueue_map_iovec(vdev, elem->out_sg, elem->out_addr, elem->out_num, 0); } static void *virtqueue_alloc_element(size_t sz, unsigned out_num, unsigned in_num) @@ -894,7 +902,7 @@ void *virtqueue_pop(VirtQueue *vq, size_t sz) desc_cache = &caches->desc; vring_desc_read(vdev, &desc, desc_cache, i); if (desc.flags & VRING_DESC_F_INDIRECT) { - if (desc.len % sizeof(VRingDesc)) { + if (!desc.len || (desc.len % sizeof(VRingDesc))) { virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } @@ -1161,7 +1169,6 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) return 0; } -bool target_words_bigendian(void); static enum virtio_device_endian virtio_default_endian(void) { if (target_words_bigendian()) { @@ -1604,6 +1611,8 @@ void virtio_del_queue(VirtIODevice *vdev, int n) vdev->vq[n].vring.num = 0; vdev->vq[n].vring.num_default = 0; + vdev->vq[n].handle_output = NULL; + vdev->vq[n].handle_aio_output = NULL; } static void virtio_set_isr(VirtIODevice *vdev, int value) @@ -1807,7 +1816,7 @@ static const VMStateDescription vmstate_virtio_ringsize = { }; static int get_extra_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { VirtIODevice *vdev = pv; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -1821,7 +1830,7 @@ static int get_extra_state(QEMUFile *f, void *pv, size_t size, } static int put_extra_state(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { VirtIODevice *vdev = pv; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -1970,14 +1979,14 @@ int virtio_save(VirtIODevice *vdev, QEMUFile *f) /* A wrapper for use as a VMState .put function */ static int virtio_device_put(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { return virtio_save(VIRTIO_DEVICE(opaque), f); } /* A wrapper for use as a VMState .get function */ static int virtio_device_get(QEMUFile *f, void *opaque, size_t size, - VMStateField *field) + const VMStateField *field) { VirtIODevice *vdev = VIRTIO_DEVICE(opaque); DeviceClass *dc = DEVICE_CLASS(VIRTIO_DEVICE_GET_CLASS(vdev)); @@ -2006,14 +2015,40 @@ static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val) int virtio_set_features(VirtIODevice *vdev, uint64_t val) { - /* + int ret; + /* * The driver must not attempt to set features after feature negotiation * has finished. */ if (vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) { return -EINVAL; } - return virtio_set_features_nocheck(vdev, val); + ret = virtio_set_features_nocheck(vdev, val); + if (!ret && virtio_vdev_has_feature(vdev, VIRTIO_RING_F_EVENT_IDX)) { + /* VIRTIO_RING_F_EVENT_IDX changes the size of the caches. */ + int i; + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + if (vdev->vq[i].vring.num != 0) { + virtio_init_region_cache(vdev, i); + } + } + } + return ret; +} + +size_t virtio_feature_get_config_size(VirtIOFeature *feature_sizes, + uint64_t host_features) +{ + size_t config_size = 0; + int i; + + for (i = 0; feature_sizes[i].flags != 0; i++) { + if (host_features & feature_sizes[i].flags) { + config_size = MAX(feature_sizes[i].end, config_size); + } + } + + return config_size; } int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) diff --git a/hw/watchdog/Kconfig b/hw/watchdog/Kconfig new file mode 100644 index 0000000000..2118d897c9 --- /dev/null +++ b/hw/watchdog/Kconfig @@ -0,0 +1,16 @@ +config CMSDK_APB_WATCHDOG + bool + select PTIMER + +config WDT_IB6300ESB + bool + default y if PCI_DEVICES + depends on PCI + +config WDT_IB700 + bool + default y + depends on ISA_BUS + +config WDT_DIAG288 + bool diff --git a/hw/watchdog/Makefile.objs b/hw/watchdog/Makefile.objs index 9589bed63a..3f536d1cad 100644 --- a/hw/watchdog/Makefile.objs +++ b/hw/watchdog/Makefile.objs @@ -1,4 +1,5 @@ common-obj-y += watchdog.o +common-obj-$(CONFIG_CMSDK_APB_WATCHDOG) += cmsdk-apb-watchdog.o common-obj-$(CONFIG_WDT_IB6300ESB) += wdt_i6300esb.o common-obj-$(CONFIG_WDT_IB700) += wdt_ib700.o common-obj-$(CONFIG_WDT_DIAG288) += wdt_diag288.o diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c new file mode 100644 index 0000000000..9c865bce67 --- /dev/null +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -0,0 +1,396 @@ +/* + * ARM CMSDK APB watchdog emulation + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB watchdog" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * + * We also support the variant of this device found in the TI + * Stellaris/Luminary boards and documented in: + * http://www.ti.com/lit/ds/symlink/lm3s6965.pdf + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/main-loop.h" +#include "sysemu/watchdog.h" +#include "hw/sysbus.h" +#include "hw/registerfields.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" + +REG32(WDOGLOAD, 0x0) +REG32(WDOGVALUE, 0x4) +REG32(WDOGCONTROL, 0x8) + FIELD(WDOGCONTROL, INTEN, 0, 1) + FIELD(WDOGCONTROL, RESEN, 1, 1) +#define R_WDOGCONTROL_VALID_MASK (R_WDOGCONTROL_INTEN_MASK | \ + R_WDOGCONTROL_RESEN_MASK) +REG32(WDOGINTCLR, 0xc) +REG32(WDOGRIS, 0x10) + FIELD(WDOGRIS, INT, 0, 1) +REG32(WDOGMIS, 0x14) +REG32(WDOGTEST, 0x418) /* only in Stellaris/Luminary version of the device */ +REG32(WDOGLOCK, 0xc00) +#define WDOG_UNLOCK_VALUE 0x1ACCE551 +REG32(WDOGITCR, 0xf00) + FIELD(WDOGITCR, ENABLE, 0, 1) +#define R_WDOGITCR_VALID_MASK R_WDOGITCR_ENABLE_MASK +REG32(WDOGITOP, 0xf04) + FIELD(WDOGITOP, WDOGRES, 0, 1) + FIELD(WDOGITOP, WDOGINT, 1, 1) +#define R_WDOGITOP_VALID_MASK (R_WDOGITOP_WDOGRES_MASK | \ + R_WDOGITOP_WDOGINT_MASK) +REG32(PID4, 0xfd0) +REG32(PID5, 0xfd4) +REG32(PID6, 0xfd8) +REG32(PID7, 0xfdc) +REG32(PID0, 0xfe0) +REG32(PID1, 0xfe4) +REG32(PID2, 0xfe8) +REG32(PID3, 0xfec) +REG32(CID0, 0xff0) +REG32(CID1, 0xff4) +REG32(CID2, 0xff8) +REG32(CID3, 0xffc) + +/* PID/CID values */ +static const uint32_t cmsdk_apb_watchdog_id[] = { + 0x04, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x24, 0xb8, 0x1b, 0x00, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static const uint32_t luminary_watchdog_id[] = { + 0x00, 0x00, 0x00, 0x00, /* PID4..PID7 */ + 0x05, 0x18, 0x18, 0x01, /* PID0..PID3 */ + 0x0d, 0xf0, 0x05, 0xb1, /* CID0..CID3 */ +}; + +static bool cmsdk_apb_watchdog_intstatus(CMSDKAPBWatchdog *s) +{ + /* Return masked interrupt status */ + return s->intstatus && (s->control & R_WDOGCONTROL_INTEN_MASK); +} + +static bool cmsdk_apb_watchdog_resetstatus(CMSDKAPBWatchdog *s) +{ + /* Return masked reset status */ + return s->resetstatus && (s->control & R_WDOGCONTROL_RESEN_MASK); +} + +static void cmsdk_apb_watchdog_update(CMSDKAPBWatchdog *s) +{ + bool wdogint; + bool wdogres; + + if (s->itcr) { + /* + * Not checking that !s->is_luminary since s->itcr can't be written + * when s->is_luminary in the first place. + */ + wdogint = s->itop & R_WDOGITOP_WDOGINT_MASK; + wdogres = s->itop & R_WDOGITOP_WDOGRES_MASK; + } else { + wdogint = cmsdk_apb_watchdog_intstatus(s); + wdogres = cmsdk_apb_watchdog_resetstatus(s); + } + + qemu_set_irq(s->wdogint, wdogint); + if (wdogres) { + watchdog_perform_action(); + } +} + +static uint64_t cmsdk_apb_watchdog_read(void *opaque, hwaddr offset, + unsigned size) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque); + uint64_t r; + + switch (offset) { + case A_WDOGLOAD: + r = ptimer_get_limit(s->timer); + break; + case A_WDOGVALUE: + r = ptimer_get_count(s->timer); + break; + case A_WDOGCONTROL: + r = s->control; + break; + case A_WDOGRIS: + r = s->intstatus; + break; + case A_WDOGMIS: + r = cmsdk_apb_watchdog_intstatus(s); + break; + case A_WDOGLOCK: + r = s->lock; + break; + case A_WDOGITCR: + if (s->is_luminary) { + goto bad_offset; + } + r = s->itcr; + break; + case A_PID4 ... A_CID3: + r = s->id[(offset - A_PID4) / 4]; + break; + case A_WDOGINTCLR: + case A_WDOGITOP: + if (s->is_luminary) { + goto bad_offset; + } + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog read: read of WO offset %x\n", + (int)offset); + r = 0; + break; + case A_WDOGTEST: + if (!s->is_luminary) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "Luminary watchdog read: stall not implemented\n"); + r = 0; + break; + default: +bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog read: bad offset %x\n", (int)offset); + r = 0; + break; + } + trace_cmsdk_apb_watchdog_read(offset, r, size); + return r; +} + +static void cmsdk_apb_watchdog_write(void *opaque, hwaddr offset, + uint64_t value, unsigned size) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque); + + trace_cmsdk_apb_watchdog_write(offset, value, size); + + if (s->lock && offset != A_WDOGLOCK) { + /* Write access is disabled via WDOGLOCK */ + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog write: write to locked watchdog\n"); + return; + } + + switch (offset) { + case A_WDOGLOAD: + /* + * Reset the load value and the current count, and make sure + * we're counting. + */ + ptimer_set_limit(s->timer, value, 1); + ptimer_run(s->timer, 0); + break; + case A_WDOGCONTROL: + if (s->is_luminary && 0 != (R_WDOGCONTROL_INTEN_MASK & s->control)) { + /* + * The Luminary version of this device ignores writes to + * this register after the guest has enabled interrupts + * (so they can only be disabled again via reset). + */ + break; + } + s->control = value & R_WDOGCONTROL_VALID_MASK; + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGINTCLR: + s->intstatus = 0; + ptimer_set_count(s->timer, ptimer_get_limit(s->timer)); + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGLOCK: + s->lock = (value != WDOG_UNLOCK_VALUE); + break; + case A_WDOGITCR: + if (s->is_luminary) { + goto bad_offset; + } + s->itcr = value & R_WDOGITCR_VALID_MASK; + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGITOP: + if (s->is_luminary) { + goto bad_offset; + } + s->itop = value & R_WDOGITOP_VALID_MASK; + cmsdk_apb_watchdog_update(s); + break; + case A_WDOGVALUE: + case A_WDOGRIS: + case A_WDOGMIS: + case A_PID4 ... A_CID3: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog write: write to RO offset 0x%x\n", + (int)offset); + break; + case A_WDOGTEST: + if (!s->is_luminary) { + goto bad_offset; + } + qemu_log_mask(LOG_UNIMP, + "Luminary watchdog write: stall not implemented\n"); + break; + default: +bad_offset: + qemu_log_mask(LOG_GUEST_ERROR, + "CMSDK APB watchdog write: bad offset 0x%x\n", + (int)offset); + break; + } +} + +static const MemoryRegionOps cmsdk_apb_watchdog_ops = { + .read = cmsdk_apb_watchdog_read, + .write = cmsdk_apb_watchdog_write, + .endianness = DEVICE_LITTLE_ENDIAN, + /* byte/halfword accesses are just zero-padded on reads and writes */ + .impl.min_access_size = 4, + .impl.max_access_size = 4, + .valid.min_access_size = 1, + .valid.max_access_size = 4, +}; + +static void cmsdk_apb_watchdog_tick(void *opaque) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(opaque); + + if (!s->intstatus) { + /* Count expired for the first time: raise interrupt */ + s->intstatus = R_WDOGRIS_INT_MASK; + } else { + /* Count expired for the second time: raise reset and stop clock */ + s->resetstatus = 1; + ptimer_stop(s->timer); + } + cmsdk_apb_watchdog_update(s); +} + +static void cmsdk_apb_watchdog_reset(DeviceState *dev) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev); + + trace_cmsdk_apb_watchdog_reset(); + s->control = 0; + s->intstatus = 0; + s->lock = 0; + s->itcr = 0; + s->itop = 0; + s->resetstatus = 0; + /* Set the limit and the count */ + ptimer_set_limit(s->timer, 0xffffffff, 1); + ptimer_run(s->timer, 0); +} + +static void cmsdk_apb_watchdog_init(Object *obj) +{ + SysBusDevice *sbd = SYS_BUS_DEVICE(obj); + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj); + + memory_region_init_io(&s->iomem, obj, &cmsdk_apb_watchdog_ops, + s, "cmsdk-apb-watchdog", 0x1000); + sysbus_init_mmio(sbd, &s->iomem); + sysbus_init_irq(sbd, &s->wdogint); + + s->is_luminary = false; + s->id = cmsdk_apb_watchdog_id; +} + +static void cmsdk_apb_watchdog_realize(DeviceState *dev, Error **errp) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(dev); + QEMUBH *bh; + + if (s->wdogclk_frq == 0) { + error_setg(errp, + "CMSDK APB watchdog: wdogclk-frq property must be set"); + return; + } + + bh = qemu_bh_new(cmsdk_apb_watchdog_tick, s); + s->timer = ptimer_init(bh, + PTIMER_POLICY_WRAP_AFTER_ONE_PERIOD | + PTIMER_POLICY_TRIGGER_ONLY_ON_DECREMENT | + PTIMER_POLICY_NO_IMMEDIATE_RELOAD | + PTIMER_POLICY_NO_COUNTER_ROUND_DOWN); + + ptimer_set_freq(s->timer, s->wdogclk_frq); +} + +static const VMStateDescription cmsdk_apb_watchdog_vmstate = { + .name = "cmsdk-apb-watchdog", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_PTIMER(timer, CMSDKAPBWatchdog), + VMSTATE_UINT32(control, CMSDKAPBWatchdog), + VMSTATE_UINT32(intstatus, CMSDKAPBWatchdog), + VMSTATE_UINT32(lock, CMSDKAPBWatchdog), + VMSTATE_UINT32(itcr, CMSDKAPBWatchdog), + VMSTATE_UINT32(itop, CMSDKAPBWatchdog), + VMSTATE_UINT32(resetstatus, CMSDKAPBWatchdog), + VMSTATE_END_OF_LIST() + } +}; + +static Property cmsdk_apb_watchdog_properties[] = { + DEFINE_PROP_UINT32("wdogclk-frq", CMSDKAPBWatchdog, wdogclk_frq, 0), + DEFINE_PROP_END_OF_LIST(), +}; + +static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = cmsdk_apb_watchdog_realize; + dc->vmsd = &cmsdk_apb_watchdog_vmstate; + dc->reset = cmsdk_apb_watchdog_reset; + dc->props = cmsdk_apb_watchdog_properties; +} + +static const TypeInfo cmsdk_apb_watchdog_info = { + .name = TYPE_CMSDK_APB_WATCHDOG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CMSDKAPBWatchdog), + .instance_init = cmsdk_apb_watchdog_init, + .class_init = cmsdk_apb_watchdog_class_init, +}; + +static void luminary_watchdog_init(Object *obj) +{ + CMSDKAPBWatchdog *s = CMSDK_APB_WATCHDOG(obj); + + s->is_luminary = true; + s->id = luminary_watchdog_id; +} + +static const TypeInfo luminary_watchdog_info = { + .name = TYPE_LUMINARY_WATCHDOG, + .parent = TYPE_CMSDK_APB_WATCHDOG, + .instance_init = luminary_watchdog_init +}; + +static void cmsdk_apb_watchdog_register_types(void) +{ + type_register_static(&cmsdk_apb_watchdog_info); + type_register_static(&luminary_watchdog_info); +} + +type_init(cmsdk_apb_watchdog_register_types); diff --git a/hw/watchdog/trace-events b/hw/watchdog/trace-events new file mode 100644 index 0000000000..ab94d7df50 --- /dev/null +++ b/hw/watchdog/trace-events @@ -0,0 +1,6 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# cmsdk-apb-watchdog.c +cmsdk_apb_watchdog_read(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog read: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_watchdog_write(uint64_t offset, uint64_t data, unsigned size) "CMSDK APB watchdog write: offset 0x%" PRIx64 " data 0x%" PRIx64 " size %u" +cmsdk_apb_watchdog_reset(void) "CMSDK APB watchdog: reset" diff --git a/hw/watchdog/watchdog.c b/hw/watchdog/watchdog.c index 6e8ba061d8..dce7c1db14 100644 --- a/hw/watchdog/watchdog.c +++ b/hw/watchdog/watchdog.c @@ -32,7 +32,7 @@ #include "qemu/help_option.h" static WatchdogAction watchdog_action = WATCHDOG_ACTION_RESET; -static QLIST_HEAD(watchdog_list, WatchdogTimerModel) watchdog_list; +static QLIST_HEAD(, WatchdogTimerModel) watchdog_list; void watchdog_add_model(WatchdogTimerModel *model) { @@ -102,17 +102,17 @@ void watchdog_perform_action(void) { switch (watchdog_action) { case WATCHDOG_ACTION_RESET: /* same as 'system_reset' in monitor */ - qapi_event_send_watchdog(WATCHDOG_ACTION_RESET, &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_RESET); qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); break; case WATCHDOG_ACTION_SHUTDOWN: /* same as 'system_powerdown' in monitor */ - qapi_event_send_watchdog(WATCHDOG_ACTION_SHUTDOWN, &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_SHUTDOWN); qemu_system_powerdown_request(); break; case WATCHDOG_ACTION_POWEROFF: /* same as 'quit' command in monitor */ - qapi_event_send_watchdog(WATCHDOG_ACTION_POWEROFF, &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_POWEROFF); exit(0); case WATCHDOG_ACTION_PAUSE: /* same as 'stop' command in monitor */ @@ -120,22 +120,21 @@ void watchdog_perform_action(void) * you would get a deadlock. Bypass the problem. */ qemu_system_vmstop_request_prepare(); - qapi_event_send_watchdog(WATCHDOG_ACTION_PAUSE, &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_PAUSE); qemu_system_vmstop_request(RUN_STATE_WATCHDOG); break; case WATCHDOG_ACTION_DEBUG: - qapi_event_send_watchdog(WATCHDOG_ACTION_DEBUG, &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_DEBUG); fprintf(stderr, "watchdog: timer fired\n"); break; case WATCHDOG_ACTION_NONE: - qapi_event_send_watchdog(WATCHDOG_ACTION_NONE, &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_NONE); break; case WATCHDOG_ACTION_INJECT_NMI: - qapi_event_send_watchdog(WATCHDOG_ACTION_INJECT_NMI, - &error_abort); + qapi_event_send_watchdog(WATCHDOG_ACTION_INJECT_NMI); nmi_monitor_handle(0, NULL); break; diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 7b59469888..1c6eddf86a 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -449,7 +449,6 @@ static void i6300esb_realize(PCIDevice *dev, Error **errp) memory_region_init_io(&d->io_mem, OBJECT(d), &i6300esb_ops, d, "i6300esb", 0x10); pci_register_bar(&d->dev, 0, 0, &d->io_mem); - /* qemu_register_coalesced_mmio (addr, 0x10); ? */ } static void i6300esb_exit(PCIDevice *dev) diff --git a/hw/xen/Makefile.objs b/hw/xen/Makefile.objs index 9ea5c73423..84df60a928 100644 --- a/hw/xen/Makefile.objs +++ b/hw/xen/Makefile.objs @@ -1,5 +1,5 @@ # xen backend driver support -common-obj-$(CONFIG_XEN) += xen_backend.o xen_devconfig.o xen_pvdev.o xen-common.o +common-obj-$(CONFIG_XEN) += xen-legacy-backend.o xen_devconfig.o xen_pvdev.o xen-common.o xen-bus.o xen-bus-helper.o xen-backend.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_graphics.o xen_pt_msi.o diff --git a/hw/xen/trace-events b/hw/xen/trace-events index c7e7a3b523..bc82ecb1a5 100644 --- a/hw/xen/trace-events +++ b/hw/xen/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# include/hw/xen/xen_common.h +# ../../include/hw/xen/xen_common.h xen_default_ioreq_server(void) "" xen_ioreq_server_create(uint32_t id) "id: %u" xen_ioreq_server_destroy(uint32_t id) "id: %u" @@ -12,3 +12,29 @@ xen_unmap_portio_range(uint32_t id, uint64_t start_addr, uint64_t end_addr) "id: 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" xen_domid_restrict(int err) "err: %u" + +# xen-bus.c +xen_bus_realize(void) "" +xen_bus_unrealize(void) "" +xen_bus_enumerate(void) "" +xen_bus_type_enumerate(const char *type) "type: %s" +xen_bus_backend_create(const char *type, const char *path) "type: %s path: %s" +xen_bus_add_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s" +xen_bus_remove_watch(const char *node, const char *key, char *token) "node: %s key: %s token: %s" +xen_bus_watch(const char *token) "token: %s" +xen_device_realize(const char *type, char *name) "type: %s name: %s" +xen_device_unrealize(const char *type, char *name) "type: %s name: %s" +xen_device_backend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s" +xen_device_backend_online(const char *type, char *name, bool online) "type: %s name: %s -> %u" +xen_device_backend_changed(const char *type, char *name) "type: %s name: %s" +xen_device_frontend_state(const char *type, char *name, const char *state) "type: %s name: %s -> %s" +xen_device_frontend_changed(const char *type, char *name) "type: %s name: %s" +xen_device_unplug(const char *type, char *name) "type: %s name: %s" + +# xen-bus-helper.c +xs_node_create(const char *node) "%s" +xs_node_destroy(const char *node) "%s" +xs_node_vprintf(char *path, char *value) "%s %s" +xs_node_vscanf(char *path, char *value) "%s %s" +xs_node_watch(char *path) "%s" +xs_node_unwatch(char *path) "%s" diff --git a/hw/xen/xen-backend.c b/hw/xen/xen-backend.c new file mode 100644 index 0000000000..da065f81b7 --- /dev/null +++ b/hw/xen/xen-backend.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 "qemu/error-report.h" +#include "qapi/error.h" +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus.h" + +typedef struct XenBackendImpl { + const char *type; + XenBackendDeviceCreate create; + XenBackendDeviceDestroy destroy; +} XenBackendImpl; + +struct XenBackendInstance { + QLIST_ENTRY(XenBackendInstance) entry; + const XenBackendImpl *impl; + XenBus *xenbus; + char *name; + XenDevice *xendev; +}; + +static GHashTable *xen_backend_table_get(void) +{ + static GHashTable *table; + + if (table == NULL) { + table = g_hash_table_new(g_str_hash, g_str_equal); + } + + return table; +} + +static void xen_backend_table_add(XenBackendImpl *impl) +{ + g_hash_table_insert(xen_backend_table_get(), (void *)impl->type, impl); +} + +static const XenBackendImpl *xen_backend_table_lookup(const char *type) +{ + return g_hash_table_lookup(xen_backend_table_get(), type); +} + +void xen_backend_register(const XenBackendInfo *info) +{ + XenBackendImpl *impl = g_new0(XenBackendImpl, 1); + + g_assert(info->type); + + if (xen_backend_table_lookup(info->type)) { + error_report("attempt to register duplicate Xen backend type '%s'", + info->type); + abort(); + } + + if (!info->create) { + error_report("backend type '%s' has no creator", info->type); + abort(); + } + + impl->type = info->type; + impl->create = info->create; + impl->destroy = info->destroy; + + xen_backend_table_add(impl); +} + +static QLIST_HEAD(, XenBackendInstance) backend_list; + +static void xen_backend_list_add(XenBackendInstance *backend) +{ + QLIST_INSERT_HEAD(&backend_list, backend, entry); +} + +static XenBackendInstance *xen_backend_list_find(XenDevice *xendev) +{ + XenBackendInstance *backend; + + QLIST_FOREACH(backend, &backend_list, entry) { + if (backend->xendev == xendev) { + return backend; + } + } + + return NULL; +} + +static void xen_backend_list_remove(XenBackendInstance *backend) +{ + QLIST_REMOVE(backend, entry); +} + +void xen_backend_device_create(XenBus *xenbus, const char *type, + const char *name, QDict *opts, Error **errp) +{ + const XenBackendImpl *impl = xen_backend_table_lookup(type); + XenBackendInstance *backend; + Error *local_error = NULL; + + if (!impl) { + return; + } + + backend = g_new0(XenBackendInstance, 1); + backend->xenbus = xenbus; + backend->name = g_strdup(name); + + impl->create(backend, opts, &local_error); + if (local_error) { + error_propagate(errp, local_error); + g_free(backend->name); + g_free(backend); + return; + } + + backend->impl = impl; + xen_backend_list_add(backend); +} + +XenBus *xen_backend_get_bus(XenBackendInstance *backend) +{ + return backend->xenbus; +} + +const char *xen_backend_get_name(XenBackendInstance *backend) +{ + return backend->name; +} + +void xen_backend_set_device(XenBackendInstance *backend, + XenDevice *xendev) +{ + g_assert(!backend->xendev); + backend->xendev = xendev; +} + +XenDevice *xen_backend_get_device(XenBackendInstance *backend) +{ + return backend->xendev; +} + + +bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp) +{ + XenBackendInstance *backend = xen_backend_list_find(xendev); + const XenBackendImpl *impl; + + if (!backend) { + return false; + } + + impl = backend->impl; + impl->destroy(backend, errp); + + xen_backend_list_remove(backend); + g_free(backend->name); + g_free(backend); + + return true; +} diff --git a/hw/xen/xen-bus-helper.c b/hw/xen/xen-bus-helper.c new file mode 100644 index 0000000000..5f7a4b2612 --- /dev/null +++ b/hw/xen/xen-bus-helper.c @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/xen/xen-bus.h" +#include "hw/xen/xen-bus-helper.h" +#include "qapi/error.h" + +#include + +struct xs_state { + enum xenbus_state statenum; + const char *statestr; +}; +#define XS_STATE(state) { state, #state } + +static struct xs_state xs_state[] = { + XS_STATE(XenbusStateUnknown), + XS_STATE(XenbusStateInitialising), + XS_STATE(XenbusStateInitWait), + XS_STATE(XenbusStateInitialised), + XS_STATE(XenbusStateConnected), + XS_STATE(XenbusStateClosing), + XS_STATE(XenbusStateClosed), + XS_STATE(XenbusStateReconfiguring), + XS_STATE(XenbusStateReconfigured), +}; + +#undef XS_STATE + +const char *xs_strstate(enum xenbus_state state) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(xs_state); i++) { + if (xs_state[i].statenum == state) { + return xs_state[i].statestr; + } + } + + return "INVALID"; +} + +void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, struct xs_permissions perms[], + unsigned int nr_perms, Error **errp) +{ + trace_xs_node_create(node); + + if (!xs_write(xsh, tid, node, "", 0)) { + error_setg_errno(errp, errno, "failed to create node '%s'", node); + return; + } + + if (!xs_set_permissions(xsh, tid, node, perms, nr_perms)) { + error_setg_errno(errp, errno, "failed to set node '%s' permissions", + node); + } +} + +void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, Error **errp) +{ + trace_xs_node_destroy(node); + + if (!xs_rm(xsh, tid, node)) { + error_setg_errno(errp, errno, "failed to destroy node '%s'", node); + } +} + +void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, va_list ap) +{ + char *path, *value; + int len; + + path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : + g_strdup(key); + len = g_vasprintf(&value, fmt, ap); + + trace_xs_node_vprintf(path, value); + + if (!xs_write(xsh, tid, path, value, len)) { + error_setg_errno(errp, errno, "failed to write '%s' to '%s'", + value, path); + } + + g_free(value); + g_free(path); +} + +void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + xs_node_vprintf(xsh, tid, node, key, errp, fmt, ap); + va_end(ap); +} + +int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, va_list ap) +{ + char *path, *value; + int rc; + + path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : + g_strdup(key); + value = xs_read(xsh, tid, path, NULL); + + trace_xs_node_vscanf(path, value); + + if (value) { + rc = vsscanf(value, fmt, ap); + } else { + error_setg_errno(errp, errno, "failed to read from '%s'", + path); + rc = EOF; + } + + free(value); + g_free(path); + + return rc; +} + +int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, ...) +{ + va_list ap; + int rc; + + va_start(ap, fmt); + rc = xs_node_vscanf(xsh, tid, node, key, errp, fmt, ap); + va_end(ap); + + return rc; +} + +void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key, + char *token, Error **errp) +{ + char *path; + + path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : + g_strdup(key); + + trace_xs_node_watch(path); + + if (!xs_watch(xsh, path, token)) { + error_setg_errno(errp, errno, "failed to watch node '%s'", path); + } + + g_free(path); +} + +void xs_node_unwatch(struct xs_handle *xsh, const char *node, + const char *key, const char *token, Error **errp) +{ + char *path; + + path = (strlen(node) != 0) ? g_strdup_printf("%s/%s", node, key) : + g_strdup(key); + + trace_xs_node_unwatch(path); + + if (!xs_unwatch(xsh, path, token)) { + error_setg_errno(errp, errno, "failed to unwatch node '%s'", path); + } + + g_free(path); +} diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c new file mode 100644 index 0000000000..49a725e8c7 --- /dev/null +++ b/hw/xen/xen-bus.c @@ -0,0 +1,1205 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 "qemu/main-loop.h" +#include "qemu/uuid.h" +#include "hw/hw.h" +#include "hw/sysbus.h" +#include "hw/xen/xen.h" +#include "hw/xen/xen-backend.h" +#include "hw/xen/xen-bus.h" +#include "hw/xen/xen-bus-helper.h" +#include "monitor/monitor.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" +#include "sysemu/sysemu.h" +#include "trace.h" + +static char *xen_device_get_backend_path(XenDevice *xendev) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); + const char *type = object_get_typename(OBJECT(xendev)); + const char *backend = xendev_class->backend; + + if (!backend) { + backend = type; + } + + return g_strdup_printf("/local/domain/%u/backend/%s/%u/%s", + xenbus->backend_id, backend, xendev->frontend_id, + xendev->name); +} + +static char *xen_device_get_frontend_path(XenDevice *xendev) +{ + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); + const char *type = object_get_typename(OBJECT(xendev)); + const char *device = xendev_class->device; + + if (!device) { + device = type; + } + + return g_strdup_printf("/local/domain/%u/device/%s/%s", + xendev->frontend_id, device, xendev->name); +} + +static void xen_device_unplug(XenDevice *xendev, Error **errp) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + const char *type = object_get_typename(OBJECT(xendev)); + Error *local_err = NULL; + xs_transaction_t tid; + + trace_xen_device_unplug(type, xendev->name); + + /* Mimic the way the Xen toolstack does an unplug */ +again: + tid = xs_transaction_start(xenbus->xsh); + if (tid == XBT_NULL) { + error_setg_errno(errp, errno, "failed xs_transaction_start"); + return; + } + + xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "online", + &local_err, "%u", 0); + if (local_err) { + goto abort; + } + + xs_node_printf(xenbus->xsh, tid, xendev->backend_path, "state", + &local_err, "%u", XenbusStateClosing); + if (local_err) { + goto abort; + } + + if (!xs_transaction_end(xenbus->xsh, tid, false)) { + if (errno == EAGAIN) { + goto again; + } + + error_setg_errno(errp, errno, "failed xs_transaction_end"); + } + + return; + +abort: + /* + * We only abort if there is already a failure so ignore any error + * from ending the transaction. + */ + xs_transaction_end(xenbus->xsh, tid, true); + error_propagate(errp, local_err); +} + +static void xen_bus_print_dev(Monitor *mon, DeviceState *dev, int indent) +{ + XenDevice *xendev = XEN_DEVICE(dev); + + monitor_printf(mon, "%*sname = '%s' frontend_id = %u\n", + indent, "", xendev->name, xendev->frontend_id); +} + +static char *xen_bus_get_dev_path(DeviceState *dev) +{ + return xen_device_get_backend_path(XEN_DEVICE(dev)); +} + +struct XenWatch { + char *node, *key; + char *token; + XenWatchHandler handler; + void *opaque; + Notifier notifier; +}; + +static void watch_notify(Notifier *n, void *data) +{ + XenWatch *watch = container_of(n, XenWatch, notifier); + const char *token = data; + + if (!strcmp(watch->token, token)) { + watch->handler(watch->opaque); + } +} + +static XenWatch *new_watch(const char *node, const char *key, + XenWatchHandler handler, void *opaque) +{ + XenWatch *watch = g_new0(XenWatch, 1); + QemuUUID uuid; + + qemu_uuid_generate(&uuid); + + watch->token = qemu_uuid_unparse_strdup(&uuid); + watch->node = g_strdup(node); + watch->key = g_strdup(key); + watch->handler = handler; + watch->opaque = opaque; + watch->notifier.notify = watch_notify; + + return watch; +} + +static void free_watch(XenWatch *watch) +{ + g_free(watch->token); + g_free(watch->key); + g_free(watch->node); + + g_free(watch); +} + +static XenWatch *xen_bus_add_watch(XenBus *xenbus, const char *node, + const char *key, XenWatchHandler handler, + void *opaque, Error **errp) +{ + XenWatch *watch = new_watch(node, key, handler, opaque); + Error *local_err = NULL; + + trace_xen_bus_add_watch(watch->node, watch->key, watch->token); + + notifier_list_add(&xenbus->watch_notifiers, &watch->notifier); + + xs_node_watch(xenbus->xsh, node, key, watch->token, &local_err); + if (local_err) { + error_propagate(errp, local_err); + + notifier_remove(&watch->notifier); + free_watch(watch); + + return NULL; + } + + return watch; +} + +static void xen_bus_remove_watch(XenBus *xenbus, XenWatch *watch, + Error **errp) +{ + trace_xen_bus_remove_watch(watch->node, watch->key, watch->token); + + xs_node_unwatch(xenbus->xsh, watch->node, watch->key, watch->token, + errp); + + notifier_remove(&watch->notifier); + free_watch(watch); +} + +static void xen_bus_backend_create(XenBus *xenbus, const char *type, + const char *name, char *path, + Error **errp) +{ + xs_transaction_t tid; + char **key; + QDict *opts; + unsigned int i, n; + Error *local_err = NULL; + + trace_xen_bus_backend_create(type, path); + +again: + tid = xs_transaction_start(xenbus->xsh); + if (tid == XBT_NULL) { + error_setg(errp, "failed xs_transaction_start"); + return; + } + + key = xs_directory(xenbus->xsh, tid, path, &n); + if (!key) { + if (!xs_transaction_end(xenbus->xsh, tid, true)) { + error_setg_errno(errp, errno, "failed xs_transaction_end"); + } + return; + } + + opts = qdict_new(); + for (i = 0; i < n; i++) { + char *val; + + /* + * Assume anything found in the xenstore backend area, other than + * the keys created for a generic XenDevice, are parameters + * to be used to configure the backend. + */ + if (!strcmp(key[i], "state") || + !strcmp(key[i], "online") || + !strcmp(key[i], "frontend") || + !strcmp(key[i], "frontend-id") || + !strcmp(key[i], "hotplug-status")) + continue; + + if (xs_node_scanf(xenbus->xsh, tid, path, key[i], NULL, "%ms", + &val) == 1) { + qdict_put_str(opts, key[i], val); + free(val); + } + } + + free(key); + + if (!xs_transaction_end(xenbus->xsh, tid, false)) { + qobject_unref(opts); + + if (errno == EAGAIN) { + goto again; + } + + error_setg_errno(errp, errno, "failed xs_transaction_end"); + return; + } + + xen_backend_device_create(xenbus, type, name, opts, &local_err); + qobject_unref(opts); + + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to create '%s' device '%s': ", + type, name); + } +} + +static void xen_bus_type_enumerate(XenBus *xenbus, const char *type) +{ + char *domain_path = g_strdup_printf("backend/%s/%u", type, xen_domid); + char **backend; + unsigned int i, n; + + trace_xen_bus_type_enumerate(type); + + backend = xs_directory(xenbus->xsh, XBT_NULL, domain_path, &n); + if (!backend) { + goto out; + } + + for (i = 0; i < n; i++) { + char *backend_path = g_strdup_printf("%s/%s", domain_path, + backend[i]); + enum xenbus_state backend_state; + + if (xs_node_scanf(xenbus->xsh, XBT_NULL, backend_path, "state", + NULL, "%u", &backend_state) != 1) + backend_state = XenbusStateUnknown; + + if (backend_state == XenbusStateInitialising) { + Error *local_err = NULL; + + xen_bus_backend_create(xenbus, type, backend[i], backend_path, + &local_err); + if (local_err) { + error_report_err(local_err); + } + } + + g_free(backend_path); + } + + free(backend); + +out: + g_free(domain_path); +} + +static void xen_bus_enumerate(void *opaque) +{ + XenBus *xenbus = opaque; + char **type; + unsigned int i, n; + + trace_xen_bus_enumerate(); + + type = xs_directory(xenbus->xsh, XBT_NULL, "backend", &n); + if (!type) { + return; + } + + for (i = 0; i < n; i++) { + xen_bus_type_enumerate(xenbus, type[i]); + } + + free(type); +} + +static void xen_bus_unrealize(BusState *bus, Error **errp) +{ + XenBus *xenbus = XEN_BUS(bus); + + trace_xen_bus_unrealize(); + + if (xenbus->backend_watch) { + xen_bus_remove_watch(xenbus, xenbus->backend_watch, NULL); + xenbus->backend_watch = NULL; + } + + if (!xenbus->xsh) { + return; + } + + qemu_set_fd_handler(xs_fileno(xenbus->xsh), NULL, NULL, NULL); + + xs_close(xenbus->xsh); +} + +static void xen_bus_watch(void *opaque) +{ + XenBus *xenbus = opaque; + char **v; + const char *token; + + g_assert(xenbus->xsh); + + v = xs_check_watch(xenbus->xsh); + if (!v) { + return; + } + + token = v[XS_WATCH_TOKEN]; + + trace_xen_bus_watch(token); + + notifier_list_notify(&xenbus->watch_notifiers, (void *)token); + + free(v); +} + +static void xen_bus_realize(BusState *bus, Error **errp) +{ + XenBus *xenbus = XEN_BUS(bus); + unsigned int domid; + Error *local_err = NULL; + + trace_xen_bus_realize(); + + xenbus->xsh = xs_open(0); + if (!xenbus->xsh) { + error_setg_errno(errp, errno, "failed xs_open"); + goto fail; + } + + if (xs_node_scanf(xenbus->xsh, XBT_NULL, "", /* domain root node */ + "domid", NULL, "%u", &domid) == 1) { + xenbus->backend_id = domid; + } else { + xenbus->backend_id = 0; /* Assume lack of node means dom0 */ + } + + notifier_list_init(&xenbus->watch_notifiers); + qemu_set_fd_handler(xs_fileno(xenbus->xsh), xen_bus_watch, NULL, + xenbus); + + module_call_init(MODULE_INIT_XEN_BACKEND); + + xenbus->backend_watch = + xen_bus_add_watch(xenbus, "", /* domain root node */ + "backend", xen_bus_enumerate, xenbus, &local_err); + if (local_err) { + /* This need not be treated as a hard error so don't propagate */ + error_reportf_err(local_err, + "failed to set up enumeration watch: "); + } + + return; + +fail: + xen_bus_unrealize(bus, &error_abort); +} + +static void xen_bus_unplug_request(HotplugHandler *hotplug, + DeviceState *dev, + Error **errp) +{ + XenDevice *xendev = XEN_DEVICE(dev); + + xen_device_unplug(xendev, errp); +} + +static void xen_bus_class_init(ObjectClass *class, void *data) +{ + BusClass *bus_class = BUS_CLASS(class); + HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); + + bus_class->print_dev = xen_bus_print_dev; + bus_class->get_dev_path = xen_bus_get_dev_path; + bus_class->realize = xen_bus_realize; + bus_class->unrealize = xen_bus_unrealize; + + hotplug_class->unplug_request = xen_bus_unplug_request; +} + +static const TypeInfo xen_bus_type_info = { + .name = TYPE_XEN_BUS, + .parent = TYPE_BUS, + .instance_size = sizeof(XenBus), + .class_size = sizeof(XenBusClass), + .class_init = xen_bus_class_init, + .interfaces = (InterfaceInfo[]) { + { TYPE_HOTPLUG_HANDLER }, + { } + }, +}; + +void xen_device_backend_printf(XenDevice *xendev, const char *key, + const char *fmt, ...) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + Error *local_err = NULL; + va_list ap; + + g_assert(xenbus->xsh); + + va_start(ap, fmt); + xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, + &local_err, fmt, ap); + va_end(ap); + + if (local_err) { + error_report_err(local_err); + } +} + +static int xen_device_backend_scanf(XenDevice *xendev, const char *key, + const char *fmt, ...) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + va_list ap; + int rc; + + g_assert(xenbus->xsh); + + va_start(ap, fmt); + rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->backend_path, key, + NULL, fmt, ap); + va_end(ap); + + return rc; +} + +void xen_device_backend_set_state(XenDevice *xendev, + enum xenbus_state state) +{ + const char *type = object_get_typename(OBJECT(xendev)); + + if (xendev->backend_state == state) { + return; + } + + trace_xen_device_backend_state(type, xendev->name, + xs_strstate(state)); + + xendev->backend_state = state; + xen_device_backend_printf(xendev, "state", "%u", state); +} + +enum xenbus_state xen_device_backend_get_state(XenDevice *xendev) +{ + return xendev->backend_state; +} + +static void xen_device_backend_set_online(XenDevice *xendev, bool online) +{ + const char *type = object_get_typename(OBJECT(xendev)); + + if (xendev->backend_online == online) { + return; + } + + trace_xen_device_backend_online(type, xendev->name, online); + + xendev->backend_online = online; + xen_device_backend_printf(xendev, "online", "%u", online); +} + +static void xen_device_backend_changed(void *opaque) +{ + XenDevice *xendev = opaque; + const char *type = object_get_typename(OBJECT(xendev)); + enum xenbus_state state; + unsigned int online; + + trace_xen_device_backend_changed(type, xendev->name); + + if (xen_device_backend_scanf(xendev, "state", "%u", &state) != 1) { + state = XenbusStateUnknown; + } + + xen_device_backend_set_state(xendev, state); + + if (xen_device_backend_scanf(xendev, "online", "%u", &online) != 1) { + online = 0; + } + + xen_device_backend_set_online(xendev, !!online); + + /* + * If the toolstack (or unplug request callback) has set the backend + * state to Closing, but there is no active frontend (i.e. the + * state is not Connected) then set the backend state to Closed. + */ + if (xendev->backend_state == XenbusStateClosing && + xendev->frontend_state != XenbusStateConnected) { + xen_device_backend_set_state(xendev, XenbusStateClosed); + } + + /* + * If a backend is still 'online' then we should leave it alone but, + * if a backend is not 'online', then the device should be destroyed + * once the state is Closed. + */ + if (!xendev->backend_online && + (xendev->backend_state == XenbusStateClosed || + xendev->backend_state == XenbusStateInitialising || + xendev->backend_state == XenbusStateInitWait || + xendev->backend_state == XenbusStateUnknown)) { + Error *local_err = NULL; + + if (!xen_backend_try_device_destroy(xendev, &local_err)) { + object_unparent(OBJECT(xendev)); + } + + if (local_err) { + error_report_err(local_err); + } + } +} + +static void xen_device_backend_create(XenDevice *xendev, Error **errp) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + struct xs_permissions perms[2]; + Error *local_err = NULL; + + xendev->backend_path = xen_device_get_backend_path(xendev); + + perms[0].id = xenbus->backend_id; + perms[0].perms = XS_PERM_NONE; + perms[1].id = xendev->frontend_id; + perms[1].perms = XS_PERM_READ; + + g_assert(xenbus->xsh); + + xs_node_create(xenbus->xsh, XBT_NULL, xendev->backend_path, perms, + ARRAY_SIZE(perms), &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to create backend: "); + return; + } + + xendev->backend_state_watch = + xen_bus_add_watch(xenbus, xendev->backend_path, + "state", xen_device_backend_changed, + xendev, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to watch backend state: "); + return; + } + + xendev->backend_online_watch = + xen_bus_add_watch(xenbus, xendev->backend_path, + "online", xen_device_backend_changed, + xendev, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to watch backend online: "); + return; + } +} + +static void xen_device_backend_destroy(XenDevice *xendev) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + Error *local_err = NULL; + + if (xendev->backend_online_watch) { + xen_bus_remove_watch(xenbus, xendev->backend_online_watch, NULL); + xendev->backend_online_watch = NULL; + } + + if (xendev->backend_state_watch) { + xen_bus_remove_watch(xenbus, xendev->backend_state_watch, NULL); + xendev->backend_state_watch = NULL; + } + + if (!xendev->backend_path) { + return; + } + + g_assert(xenbus->xsh); + + xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->backend_path, + &local_err); + g_free(xendev->backend_path); + xendev->backend_path = NULL; + + if (local_err) { + error_report_err(local_err); + } +} + +void xen_device_frontend_printf(XenDevice *xendev, const char *key, + const char *fmt, ...) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + Error *local_err = NULL; + va_list ap; + + g_assert(xenbus->xsh); + + va_start(ap, fmt); + xs_node_vprintf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, + &local_err, fmt, ap); + va_end(ap); + + if (local_err) { + error_report_err(local_err); + } +} + +int xen_device_frontend_scanf(XenDevice *xendev, const char *key, + const char *fmt, ...) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + va_list ap; + int rc; + + g_assert(xenbus->xsh); + + va_start(ap, fmt); + rc = xs_node_vscanf(xenbus->xsh, XBT_NULL, xendev->frontend_path, key, + NULL, fmt, ap); + va_end(ap); + + return rc; +} + +static void xen_device_frontend_set_state(XenDevice *xendev, + enum xenbus_state state) +{ + const char *type = object_get_typename(OBJECT(xendev)); + + if (xendev->frontend_state == state) { + return; + } + + trace_xen_device_frontend_state(type, xendev->name, + xs_strstate(state)); + + xendev->frontend_state = state; + xen_device_frontend_printf(xendev, "state", "%u", state); +} + +static void xen_device_frontend_changed(void *opaque) +{ + XenDevice *xendev = opaque; + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); + const char *type = object_get_typename(OBJECT(xendev)); + enum xenbus_state state; + + trace_xen_device_frontend_changed(type, xendev->name); + + if (xen_device_frontend_scanf(xendev, "state", "%u", &state) != 1) { + state = XenbusStateUnknown; + } + + xen_device_frontend_set_state(xendev, state); + + if (state == XenbusStateInitialising && + xendev->backend_state == XenbusStateClosed && + xendev->backend_online) { + /* + * The frontend is re-initializing so switch back to + * InitWait. + */ + xen_device_backend_set_state(xendev, XenbusStateInitWait); + return; + } + + if (xendev_class->frontend_changed) { + Error *local_err = NULL; + + xendev_class->frontend_changed(xendev, state, &local_err); + + if (local_err) { + error_reportf_err(local_err, "frontend change error: "); + } + } +} + +static void xen_device_frontend_create(XenDevice *xendev, Error **errp) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + struct xs_permissions perms[2]; + Error *local_err = NULL; + + xendev->frontend_path = xen_device_get_frontend_path(xendev); + + perms[0].id = xendev->frontend_id; + perms[0].perms = XS_PERM_NONE; + perms[1].id = xenbus->backend_id; + perms[1].perms = XS_PERM_READ | XS_PERM_WRITE; + + g_assert(xenbus->xsh); + + xs_node_create(xenbus->xsh, XBT_NULL, xendev->frontend_path, perms, + ARRAY_SIZE(perms), &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to create frontend: "); + return; + } + + xendev->frontend_state_watch = + xen_bus_add_watch(xenbus, xendev->frontend_path, "state", + xen_device_frontend_changed, xendev, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to watch frontend state: "); + } +} + +static void xen_device_frontend_destroy(XenDevice *xendev) +{ + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + Error *local_err = NULL; + + if (xendev->frontend_state_watch) { + xen_bus_remove_watch(xenbus, xendev->frontend_state_watch, NULL); + xendev->frontend_state_watch = NULL; + } + + if (!xendev->frontend_path) { + return; + } + + g_assert(xenbus->xsh); + + xs_node_destroy(xenbus->xsh, XBT_NULL, xendev->frontend_path, + &local_err); + g_free(xendev->frontend_path); + xendev->frontend_path = NULL; + + if (local_err) { + error_report_err(local_err); + } +} + +void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, + Error **errp) +{ + if (xengnttab_set_max_grants(xendev->xgth, nr_refs)) { + error_setg_errno(errp, errno, "xengnttab_set_max_grants failed"); + } +} + +void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot, + Error **errp) +{ + void *map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_refs, + xendev->frontend_id, refs, + prot); + + if (!map) { + error_setg_errno(errp, errno, + "xengnttab_map_domain_grant_refs failed"); + } + + return map; +} + +void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, + unsigned int nr_refs, Error **errp) +{ + if (xengnttab_unmap(xendev->xgth, map, nr_refs)) { + error_setg_errno(errp, errno, "xengnttab_unmap failed"); + } +} + +static void compat_copy_grant_refs(XenDevice *xendev, bool to_domain, + XenDeviceGrantCopySegment segs[], + unsigned int nr_segs, Error **errp) +{ + uint32_t *refs = g_new(uint32_t, nr_segs); + int prot = to_domain ? PROT_WRITE : PROT_READ; + void *map; + unsigned int i; + + for (i = 0; i < nr_segs; i++) { + XenDeviceGrantCopySegment *seg = &segs[i]; + + refs[i] = to_domain ? seg->dest.foreign.ref : + seg->source.foreign.ref; + } + + map = xengnttab_map_domain_grant_refs(xendev->xgth, nr_segs, + xendev->frontend_id, refs, + prot); + if (!map) { + error_setg_errno(errp, errno, + "xengnttab_map_domain_grant_refs failed"); + goto done; + } + + for (i = 0; i < nr_segs; i++) { + XenDeviceGrantCopySegment *seg = &segs[i]; + void *page = map + (i * XC_PAGE_SIZE); + + if (to_domain) { + memcpy(page + seg->dest.foreign.offset, seg->source.virt, + seg->len); + } else { + memcpy(seg->dest.virt, page + seg->source.foreign.offset, + seg->len); + } + } + + if (xengnttab_unmap(xendev->xgth, map, nr_segs)) { + error_setg_errno(errp, errno, "xengnttab_unmap failed"); + } + +done: + g_free(refs); +} + +void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, + XenDeviceGrantCopySegment segs[], + unsigned int nr_segs, Error **errp) +{ + xengnttab_grant_copy_segment_t *xengnttab_segs; + unsigned int i; + + if (!xendev->feature_grant_copy) { + compat_copy_grant_refs(xendev, to_domain, segs, nr_segs, errp); + return; + } + + xengnttab_segs = g_new0(xengnttab_grant_copy_segment_t, nr_segs); + + for (i = 0; i < nr_segs; i++) { + XenDeviceGrantCopySegment *seg = &segs[i]; + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (to_domain) { + xengnttab_seg->flags = GNTCOPY_dest_gref; + xengnttab_seg->dest.foreign.domid = xendev->frontend_id; + xengnttab_seg->dest.foreign.ref = seg->dest.foreign.ref; + xengnttab_seg->dest.foreign.offset = seg->dest.foreign.offset; + xengnttab_seg->source.virt = seg->source.virt; + } else { + xengnttab_seg->flags = GNTCOPY_source_gref; + xengnttab_seg->source.foreign.domid = xendev->frontend_id; + xengnttab_seg->source.foreign.ref = seg->source.foreign.ref; + xengnttab_seg->source.foreign.offset = + seg->source.foreign.offset; + xengnttab_seg->dest.virt = seg->dest.virt; + } + + xengnttab_seg->len = seg->len; + } + + if (xengnttab_grant_copy(xendev->xgth, nr_segs, xengnttab_segs)) { + error_setg_errno(errp, errno, "xengnttab_grant_copy failed"); + goto done; + } + + for (i = 0; i < nr_segs; i++) { + xengnttab_grant_copy_segment_t *xengnttab_seg = &xengnttab_segs[i]; + + if (xengnttab_seg->status != GNTST_okay) { + error_setg(errp, "xengnttab_grant_copy seg[%u] failed", i); + break; + } + } + +done: + g_free(xengnttab_segs); +} + +struct XenEventChannel { + evtchn_port_t local_port; + XenEventHandler handler; + void *opaque; + Notifier notifier; +}; + +static void event_notify(Notifier *n, void *data) +{ + XenEventChannel *channel = container_of(n, XenEventChannel, notifier); + unsigned long port = (unsigned long)data; + + if (port == channel->local_port) { + channel->handler(channel->opaque); + } +} + +XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, + unsigned int port, + XenEventHandler handler, + void *opaque, Error **errp) +{ + XenEventChannel *channel = g_new0(XenEventChannel, 1); + xenevtchn_port_or_error_t local_port; + + local_port = xenevtchn_bind_interdomain(xendev->xeh, + xendev->frontend_id, + port); + if (local_port < 0) { + error_setg_errno(errp, errno, "xenevtchn_bind_interdomain failed"); + + g_free(channel); + return NULL; + } + + channel->local_port = local_port; + channel->handler = handler; + channel->opaque = opaque; + channel->notifier.notify = event_notify; + + notifier_list_add(&xendev->event_notifiers, &channel->notifier); + + return channel; +} + +void xen_device_notify_event_channel(XenDevice *xendev, + XenEventChannel *channel, + Error **errp) +{ + if (!channel) { + error_setg(errp, "bad channel"); + return; + } + + if (xenevtchn_notify(xendev->xeh, channel->local_port) < 0) { + error_setg_errno(errp, errno, "xenevtchn_notify failed"); + } +} + +void xen_device_unbind_event_channel(XenDevice *xendev, + XenEventChannel *channel, + Error **errp) +{ + if (!channel) { + error_setg(errp, "bad channel"); + return; + } + + notifier_remove(&channel->notifier); + + if (xenevtchn_unbind(xendev->xeh, channel->local_port) < 0) { + error_setg_errno(errp, errno, "xenevtchn_unbind failed"); + } + + g_free(channel); +} + +static void xen_device_unrealize(DeviceState *dev, Error **errp) +{ + XenDevice *xendev = XEN_DEVICE(dev); + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); + const char *type = object_get_typename(OBJECT(xendev)); + + if (!xendev->name) { + return; + } + + trace_xen_device_unrealize(type, xendev->name); + + if (xendev->exit.notify) { + qemu_remove_exit_notifier(&xendev->exit); + xendev->exit.notify = NULL; + } + + if (xendev_class->unrealize) { + xendev_class->unrealize(xendev, errp); + } + + xen_device_frontend_destroy(xendev); + xen_device_backend_destroy(xendev); + + if (xendev->xeh) { + qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), NULL, NULL, NULL); + xenevtchn_close(xendev->xeh); + xendev->xeh = NULL; + } + + if (xendev->xgth) { + xengnttab_close(xendev->xgth); + xendev->xgth = NULL; + } + + g_free(xendev->name); + xendev->name = NULL; +} + +static void xen_device_exit(Notifier *n, void *data) +{ + XenDevice *xendev = container_of(n, XenDevice, exit); + + xen_device_unrealize(DEVICE(xendev), &error_abort); +} + +static void xen_device_event(void *opaque) +{ + XenDevice *xendev = opaque; + unsigned long port = xenevtchn_pending(xendev->xeh); + + notifier_list_notify(&xendev->event_notifiers, (void *)port); + + xenevtchn_unmask(xendev->xeh, port); +} + +static void xen_device_realize(DeviceState *dev, Error **errp) +{ + XenDevice *xendev = XEN_DEVICE(dev); + XenDeviceClass *xendev_class = XEN_DEVICE_GET_CLASS(xendev); + XenBus *xenbus = XEN_BUS(qdev_get_parent_bus(DEVICE(xendev))); + const char *type = object_get_typename(OBJECT(xendev)); + Error *local_err = NULL; + + if (xendev->frontend_id == DOMID_INVALID) { + xendev->frontend_id = xen_domid; + } + + if (xendev->frontend_id >= DOMID_FIRST_RESERVED) { + error_setg(errp, "invalid frontend-id"); + goto unrealize; + } + + if (!xendev_class->get_name) { + error_setg(errp, "get_name method not implemented"); + goto unrealize; + } + + xendev->name = xendev_class->get_name(xendev, &local_err); + if (local_err) { + error_propagate_prepend(errp, local_err, + "failed to get device name: "); + goto unrealize; + } + + trace_xen_device_realize(type, xendev->name); + + xendev->xgth = xengnttab_open(NULL, 0); + if (!xendev->xgth) { + error_setg_errno(errp, errno, "failed xengnttab_open"); + goto unrealize; + } + + xendev->feature_grant_copy = + (xengnttab_grant_copy(xendev->xgth, 0, NULL) == 0); + + xendev->xeh = xenevtchn_open(NULL, 0); + if (!xendev->xeh) { + error_setg_errno(errp, errno, "failed xenevtchn_open"); + goto unrealize; + } + + notifier_list_init(&xendev->event_notifiers); + qemu_set_fd_handler(xenevtchn_fd(xendev->xeh), xen_device_event, NULL, + xendev); + + xen_device_backend_create(xendev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto unrealize; + } + + xen_device_frontend_create(xendev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto unrealize; + } + + if (xendev_class->realize) { + xendev_class->realize(xendev, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto unrealize; + } + } + + xen_device_backend_printf(xendev, "frontend", "%s", + xendev->frontend_path); + xen_device_backend_printf(xendev, "frontend-id", "%u", + xendev->frontend_id); + xen_device_backend_printf(xendev, "hotplug-status", "connected"); + + xen_device_backend_set_online(xendev, true); + xen_device_backend_set_state(xendev, XenbusStateInitWait); + + xen_device_frontend_printf(xendev, "backend", "%s", + xendev->backend_path); + xen_device_frontend_printf(xendev, "backend-id", "%u", + xenbus->backend_id); + + xen_device_frontend_set_state(xendev, XenbusStateInitialising); + + xendev->exit.notify = xen_device_exit; + qemu_add_exit_notifier(&xendev->exit); + return; + +unrealize: + xen_device_unrealize(dev, &error_abort); +} + +static Property xen_device_props[] = { + DEFINE_PROP_UINT16("frontend-id", XenDevice, frontend_id, + DOMID_INVALID), + DEFINE_PROP_END_OF_LIST() +}; + +static void xen_device_class_init(ObjectClass *class, void *data) +{ + DeviceClass *dev_class = DEVICE_CLASS(class); + + dev_class->realize = xen_device_realize; + dev_class->unrealize = xen_device_unrealize; + dev_class->props = xen_device_props; + dev_class->bus_type = TYPE_XEN_BUS; +} + +static const TypeInfo xen_device_type_info = { + .name = TYPE_XEN_DEVICE, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XenDevice), + .abstract = true, + .class_size = sizeof(XenDeviceClass), + .class_init = xen_device_class_init, +}; + +typedef struct XenBridge { + SysBusDevice busdev; +} XenBridge; + +#define TYPE_XEN_BRIDGE "xen-bridge" + +static const TypeInfo xen_bridge_type_info = { + .name = TYPE_XEN_BRIDGE, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(XenBridge), +}; + +static void xen_register_types(void) +{ + type_register_static(&xen_bridge_type_info); + type_register_static(&xen_bus_type_info); + type_register_static(&xen_device_type_info); +} + +type_init(xen_register_types) + +void xen_bus_init(void) +{ + DeviceState *dev = qdev_create(NULL, TYPE_XEN_BRIDGE); + BusState *bus = qbus_create(TYPE_XEN_BUS, dev, NULL); + + qdev_init_nofail(dev); + qbus_set_bus_hotplug_handler(bus, &error_abort); +} diff --git a/hw/xen/xen-common.c b/hw/xen/xen-common.c index 6ec14c73ca..0e9e58f04d 100644 --- a/hw/xen/xen-common.c +++ b/hw/xen/xen-common.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "chardev/char.h" #include "sysemu/accel.h" #include "migration/misc.h" @@ -159,33 +159,22 @@ static int xen_init(MachineState *ms) return 0; } -static GlobalProperty xen_compat_props[] = { - { - .driver = "migration", - .property = "store-global-state", - .value = "off", - }, - { - .driver = "migration", - .property = "send-configuration", - .value = "off", - }, - { - .driver = "migration", - .property = "send-section-footer", - .value = "off", - }, - { /* end of list */ }, -}; - static void xen_accel_class_init(ObjectClass *oc, void *data) { AccelClass *ac = ACCEL_CLASS(oc); + static GlobalProperty compat[] = { + { "migration", "store-global-state", "off" }, + { "migration", "send-configuration", "off" }, + { "migration", "send-section-footer", "off" }, + }; + ac->name = "Xen"; ac->init_machine = xen_init; ac->setup_post = xen_setup_post; ac->allowed = &xen_allowed; - ac->global_props = xen_compat_props; + ac->compat_props = g_ptr_array_new(); + + compat_props_add(ac->compat_props, compat, G_N_ELEMENTS(compat)); } #define TYPE_XEN_ACCEL ACCEL_CLASS_NAME("xen") diff --git a/hw/xen/xen_backend.c b/hw/xen/xen-legacy-backend.c similarity index 89% rename from hw/xen/xen_backend.c rename to hw/xen/xen-legacy-backend.c index 9a8e8771ec..36fd1e9b09 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -30,7 +30,7 @@ #include "hw/boards.h" #include "qemu/log.h" #include "qapi/error.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen_pvdev.h" #include "monitor/qdev.h" @@ -42,49 +42,54 @@ BusState *xen_sysbus; /* ------------------------------------------------------------- */ /* public */ -struct xs_handle *xenstore = NULL; +struct xs_handle *xenstore; const char *xen_protocol; /* private */ static bool xen_feature_grant_copy; static int debug; -int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val) +int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node, + const char *val) { return xenstore_write_str(xendev->be, node, val); } -int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival) +int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node, + int ival) { return xenstore_write_int(xendev->be, node, ival); } -int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival) +int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node, + int64_t ival) { return xenstore_write_int64(xendev->be, node, ival); } -char *xenstore_read_be_str(struct XenDevice *xendev, const char *node) +char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node) { return xenstore_read_str(xendev->be, node); } -int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival) +int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node, + int *ival) { return xenstore_read_int(xendev->be, node, ival); } -char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node) +char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node) { return xenstore_read_str(xendev->fe, node); } -int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival) +int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node, + int *ival) { return xenstore_read_int(xendev->fe, node, ival); } -int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, +int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node, uint64_t *uval) { return xenstore_read_uint64(xendev->fe, node, uval); @@ -92,7 +97,7 @@ int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, /* ------------------------------------------------------------- */ -int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) +int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state) { int rc; @@ -106,7 +111,7 @@ int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state) return 0; } -void xen_be_set_max_grant_refs(struct XenDevice *xendev, +void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, unsigned int nr_refs) { assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); @@ -117,7 +122,7 @@ void xen_be_set_max_grant_refs(struct XenDevice *xendev, } } -void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, +void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, unsigned int nr_refs, int prot) { void *ptr; @@ -135,7 +140,7 @@ void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, return ptr; } -void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, +void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, unsigned int nr_refs) { assert(xendev->ops->flags & DEVOPS_FLAG_NEED_GNTDEV); @@ -146,7 +151,7 @@ void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, } } -static int compat_copy_grant_refs(struct XenDevice *xendev, +static int compat_copy_grant_refs(struct XenLegacyDevice *xendev, bool to_domain, XenGrantCopySegment segs[], unsigned int nr_segs) @@ -195,7 +200,7 @@ static int compat_copy_grant_refs(struct XenDevice *xendev, return 0; } -int xen_be_copy_grant_refs(struct XenDevice *xendev, +int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, bool to_domain, XenGrantCopySegment segs[], unsigned int nr_segs) @@ -259,10 +264,11 @@ int xen_be_copy_grant_refs(struct XenDevice *xendev, /* * get xen backend device, allocate a new one if it doesn't exist. */ -static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, - struct XenDevOps *ops) +static struct XenLegacyDevice *xen_be_get_xendev(const char *type, int dom, + int dev, + struct XenDevOps *ops) { - struct XenDevice *xendev; + struct XenLegacyDevice *xendev; xendev = xen_pv_find_xendev(type, dom, dev); if (xendev) { @@ -314,7 +320,8 @@ static struct XenDevice *xen_be_get_xendev(const char *type, int dom, int dev, * Node specifies the changed field. node = NULL means * update all fields (used for initialization). */ -static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) +static void xen_be_backend_changed(struct XenLegacyDevice *xendev, + const char *node) { if (node == NULL || strcmp(node, "online") == 0) { if (xenstore_read_be_int(xendev, "online", &xendev->online) == -1) { @@ -330,7 +337,8 @@ static void xen_be_backend_changed(struct XenDevice *xendev, const char *node) } } -static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) +static void xen_be_frontend_changed(struct XenLegacyDevice *xendev, + const char *node) { int fe_state; @@ -373,7 +381,7 @@ static void xen_be_frontend_changed(struct XenDevice *xendev, const char *node) * only affects the xendev->be_state variable as xenbus should * already be put into that state by xend. */ -static int xen_be_try_setup(struct XenDevice *xendev) +static int xen_be_try_setup(struct XenLegacyDevice *xendev) { char token[XEN_BUFSIZE]; int be_state; @@ -417,7 +425,7 @@ static int xen_be_try_setup(struct XenDevice *xendev) * * Goes to InitWait on success. */ -static int xen_be_try_init(struct XenDevice *xendev) +static int xen_be_try_init(struct XenLegacyDevice *xendev) { int rc = 0; @@ -446,7 +454,7 @@ static int xen_be_try_init(struct XenDevice *xendev) * * Goes to Connected on success. */ -static int xen_be_try_initialise(struct XenDevice *xendev) +static int xen_be_try_initialise(struct XenLegacyDevice *xendev) { int rc = 0; @@ -487,7 +495,7 @@ static int xen_be_try_initialise(struct XenDevice *xendev) * frontend being Connected. Note that this may be called more * than once since the backend state is not modified. */ -static void xen_be_try_connected(struct XenDevice *xendev) +static void xen_be_try_connected(struct XenLegacyDevice *xendev) { if (!xendev->ops->connected) { return; @@ -510,7 +518,8 @@ static void xen_be_try_connected(struct XenDevice *xendev) * * Goes to Closed when done. */ -static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) +static void xen_be_disconnect(struct XenLegacyDevice *xendev, + enum xenbus_state state) { if (xendev->be_state != XenbusStateClosing && xendev->be_state != XenbusStateClosed && @@ -529,7 +538,7 @@ static void xen_be_disconnect(struct XenDevice *xendev, enum xenbus_state state) /* * Try to reset xendev, for reconnection by another frontend instance. */ -static int xen_be_try_reset(struct XenDevice *xendev) +static int xen_be_try_reset(struct XenLegacyDevice *xendev) { if (xendev->fe_state != XenbusStateInitialising) { return -1; @@ -543,7 +552,7 @@ static int xen_be_try_reset(struct XenDevice *xendev) /* * state change dispatcher function */ -void xen_be_check_state(struct XenDevice *xendev) +void xen_be_check_state(struct XenLegacyDevice *xendev) { int rc = 0; @@ -587,7 +596,7 @@ void xen_be_check_state(struct XenDevice *xendev) static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) { - struct XenDevice *xendev; + struct XenLegacyDevice *xendev; char path[XEN_BUFSIZE], token[XEN_BUFSIZE]; char **dev = NULL; unsigned int cdev, j; @@ -620,7 +629,7 @@ static int xenstore_scan(const char *type, int dom, struct XenDevOps *ops) void xenstore_update_be(char *watch, char *type, int dom, struct XenDevOps *ops) { - struct XenDevice *xendev; + struct XenLegacyDevice *xendev; char path[XEN_BUFSIZE], *bepath; unsigned int len, dev; @@ -628,9 +637,9 @@ void xenstore_update_be(char *watch, char *type, int dom, if (strncmp(path, watch, len) != 0) { return; } - if (sscanf(watch+len, "/%u/%255s", &dev, path) != 2) { + if (sscanf(watch + len, "/%u/%255s", &dev, path) != 2) { strcpy(path, ""); - if (sscanf(watch+len, "/%u", &dev) != 1) { + if (sscanf(watch + len, "/%u", &dev) != 1) { dev = -1; } } @@ -651,7 +660,7 @@ void xenstore_update_be(char *watch, char *type, int dom, } } -void xenstore_update_fe(char *watch, struct XenDevice *xendev) +void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev) { char *node; unsigned int len; @@ -744,7 +753,6 @@ void xen_be_register_common(void) xen_be_register("console", &xen_console_ops); xen_be_register("vkbd", &xen_kbdmouse_ops); - xen_be_register("qdisk", &xen_blkdev_ops); #ifdef CONFIG_VIRTFS xen_be_register("9pfs", &xen_9pfs_ops); #endif @@ -753,7 +761,7 @@ void xen_be_register_common(void) #endif } -int xen_be_bind_evtchn(struct XenDevice *xendev) +int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) { if (xendev->local_port != -1) { return 0; @@ -789,7 +797,7 @@ static const TypeInfo xendev_type_info = { .name = TYPE_XENBACKEND, .parent = TYPE_XENSYSDEV, .class_init = xendev_class_init, - .instance_size = sizeof(struct XenDevice), + .instance_size = sizeof(struct XenLegacyDevice), }; static void xen_sysbus_class_init(ObjectClass *klass, void *data) @@ -809,11 +817,6 @@ static const TypeInfo xensysbus_info = { } }; -static int xen_sysdev_init(SysBusDevice *dev) -{ - return 0; -} - static Property xen_sysdev_properties[] = { {/* end of property list */}, }; @@ -821,9 +824,7 @@ static Property xen_sysdev_properties[] = { static void xen_sysdev_class_init(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); - SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); - k->init = xen_sysdev_init; dc->props = xen_sysdev_properties; dc->bus_type = TYPE_XENSYSBUS; } diff --git a/hw/xen/xen_devconfig.c b/hw/xen/xen_devconfig.c index aebc19bd71..315dbc9c51 100644 --- a/hw/xen/xen_devconfig.c +++ b/hw/xen/xen_devconfig.c @@ -1,12 +1,12 @@ #include "qemu/osdep.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "qemu/option.h" #include "sysemu/blockdev.h" /* ------------------------------------------------------------- */ static int xen_config_dev_dirs(const char *ftype, const char *btype, int vdev, - char *fe, char *be, int len) + char *fe, char *be, int len) { char *dom; diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index e5a6eff44f..5539d56c3a 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -59,7 +59,7 @@ #include "hw/pci/pci.h" #include "hw/xen/xen.h" #include "hw/i386/pc.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "xen_pt.h" #include "qemu/range.h" #include "exec/address-spaces.h" @@ -830,7 +830,7 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) xen_pt_config_init(s, &err); if (err) { error_append_hint(&err, "PCI Config space initialisation failed"); - error_report_err(err); + error_propagate(errp, err); rc = -1; goto err_out; } @@ -847,6 +847,12 @@ static void xen_pt_realize(PCIDevice *d, Error **errp) } machine_irq = s->real_device.irq; + if (machine_irq == 0) { + XEN_PT_LOG(d, "machine irq is 0\n"); + cmd |= PCI_COMMAND_INTX_DISABLE; + goto out; + } + rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); if (rc < 0) { error_setg_errno(errp, errno, "Mapping machine irq %u to" diff --git a/hw/xen/xen_pt_config_init.c b/hw/xen/xen_pt_config_init.c index aee31c62bb..31ec5add1d 100644 --- a/hw/xen/xen_pt_config_init.c +++ b/hw/xen/xen_pt_config_init.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/timer.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "xen_pt.h" #define XEN_PT_MERGE_VALUE(value, data, val_mask) \ @@ -300,7 +300,9 @@ static int xen_pt_irqpin_reg_init(XenPCIPassthroughState *s, XenPTRegInfo *reg, uint32_t real_offset, uint32_t *data) { - *data = xen_pt_pci_read_intx(s); + if (s->real_device.irq) { + *data = xen_pt_pci_read_intx(s); + } return 0; } @@ -358,7 +360,7 @@ static uint64_t xen_pt_get_bar_size(PCIIORegion *r) static XenPTBarFlag xen_pt_bar_reg_parse(XenPCIPassthroughState *s, int index) { - PCIDevice *d = &s->dev; + PCIDevice *d = PCI_DEVICE(s); XenPTRegion *region = NULL; PCIIORegion *r; @@ -469,7 +471,7 @@ static int xen_pt_bar_reg_write(XenPCIPassthroughState *s, XenPTReg *cfg_entry, { XenPTRegInfo *reg = cfg_entry->reg; XenPTRegion *base = NULL; - PCIDevice *d = &s->dev; + PCIDevice *d = PCI_DEVICE(s); const PCIIORegion *r; uint32_t writable_mask = 0; uint32_t bar_emu_mask = 0; @@ -543,7 +545,7 @@ static int xen_pt_exp_rom_bar_reg_write(XenPCIPassthroughState *s, { XenPTRegInfo *reg = cfg_entry->reg; XenPTRegion *base = NULL; - PCIDevice *d = (PCIDevice *)&s->dev; + PCIDevice *d = PCI_DEVICE(s); uint32_t writable_mask = 0; uint32_t throughable_mask = get_throughable_mask(s, reg, valid_mask); pcibus_t r_size = 0; @@ -1587,7 +1589,7 @@ static int xen_pt_pcie_size_init(XenPCIPassthroughState *s, const XenPTRegGroupInfo *grp_reg, uint32_t base_offset, uint8_t *size) { - PCIDevice *d = &s->dev; + PCIDevice *d = PCI_DEVICE(s); uint8_t version = get_capability_version(s, base_offset); uint8_t type = get_device_type(s, base_offset); uint8_t pcie_size = 0; diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 135c8df1e7..b69732729b 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -5,7 +5,7 @@ #include "qapi/error.h" #include "xen_pt.h" #include "xen-host-pci-device.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" static unsigned long igd_guest_opregion; static unsigned long igd_host_opregion; @@ -185,8 +185,19 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, return; } + if (bios_size < sizeof(struct rom_header)) { + error_setg(errp, "VGA: VBIOS image corrupt (too small)"); + return; + } + /* Currently we fixed this address as a primary. */ rom = (struct rom_header *)bios; + + if (rom->pcioffset + sizeof(struct pci_data) > bios_size) { + error_setg(errp, "VGA: VBIOS image corrupt (bad pcioffset field)"); + return; + } + pd = (void *)(bios + (unsigned char)rom->pcioffset); /* We may need to fixup Device Identification. */ @@ -194,6 +205,11 @@ void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, pd->device = s->real_device.device_id; len = rom->size * 512; + if (len > bios_size) { + error_setg(errp, "VGA: VBIOS image corrupt (bad size field)"); + return; + } + /* Then adjust the bios checksum */ for (c = (char *)bios; c < ((char *)bios + len); c++) { checksum += *c; diff --git a/hw/xen/xen_pt_msi.c b/hw/xen/xen_pt_msi.c index cc514f9157..fb4b887b92 100644 --- a/hw/xen/xen_pt_msi.c +++ b/hw/xen/xen_pt_msi.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "xen_pt.h" #include "hw/i386/apic-msidef.h" diff --git a/hw/xen/xen_pvdev.c b/hw/xen/xen_pvdev.c index aed783e844..6ef09cbf9d 100644 --- a/hw/xen/xen_pvdev.c +++ b/hw/xen/xen_pvdev.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "hw/qdev-core.h" -#include "hw/xen/xen_backend.h" +#include "hw/xen/xen-legacy-backend.h" #include "hw/xen/xen_pvdev.h" /* private */ @@ -31,10 +31,10 @@ struct xs_dirs { QTAILQ_ENTRY(xs_dirs) list; }; -static QTAILQ_HEAD(xs_dirs_head, xs_dirs) xs_cleanup = +static QTAILQ_HEAD(, xs_dirs) xs_cleanup = QTAILQ_HEAD_INITIALIZER(xs_cleanup); -static QTAILQ_HEAD(XenDeviceHead, XenDevice) xendevs = +static QTAILQ_HEAD(, XenLegacyDevice) xendevs = QTAILQ_HEAD_INITIALIZER(xendevs); /* ------------------------------------------------------------- */ @@ -195,7 +195,7 @@ const char *xenbus_strstate(enum xenbus_state state) * 2 == noisy debug messages (logfile only). * 3 == will flood your log (logfile only). */ -void xen_pv_printf(struct XenDevice *xendev, int msg_level, +void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level, const char *fmt, ...) { va_list args; @@ -230,7 +230,7 @@ void xen_pv_printf(struct XenDevice *xendev, int msg_level, void xen_pv_evtchn_event(void *opaque) { - struct XenDevice *xendev = opaque; + struct XenLegacyDevice *xendev = opaque; evtchn_port_t port; port = xenevtchn_pending(xendev->evtchndev); @@ -247,7 +247,7 @@ void xen_pv_evtchn_event(void *opaque) } } -void xen_pv_unbind_evtchn(struct XenDevice *xendev) +void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev) { if (xendev->local_port == -1) { return; @@ -258,16 +258,16 @@ void xen_pv_unbind_evtchn(struct XenDevice *xendev) xendev->local_port = -1; } -int xen_pv_send_notify(struct XenDevice *xendev) +int xen_pv_send_notify(struct XenLegacyDevice *xendev) { return xenevtchn_notify(xendev->evtchndev, xendev->local_port); } /* ------------------------------------------------------------- */ -struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev) +struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev) { - struct XenDevice *xendev; + struct XenLegacyDevice *xendev; QTAILQ_FOREACH(xendev, &xendevs, next) { if (xendev->dom != dom) { @@ -287,7 +287,7 @@ struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev) /* * release xen backend device. */ -void xen_pv_del_xendev(struct XenDevice *xendev) +void xen_pv_del_xendev(struct XenLegacyDevice *xendev) { if (xendev->ops->free) { xendev->ops->free(xendev); @@ -312,7 +312,7 @@ void xen_pv_del_xendev(struct XenDevice *xendev) qdev_unplug(&xendev->qdev, NULL); } -void xen_pv_insert_xendev(struct XenDevice *xendev) +void xen_pv_insert_xendev(struct XenLegacyDevice *xendev) { QTAILQ_INSERT_TAIL(&xendevs, xendev, next); } diff --git a/hw/xenpv/Makefile.objs b/hw/xenpv/Makefile.objs index bbf5873fd1..8bfa4586ab 100644 --- a/hw/xenpv/Makefile.objs +++ b/hw/xenpv/Makefile.objs @@ -1,4 +1,2 @@ # Xen PV machine support obj-$(CONFIG_XEN) += xen_machine_pv.o -# Xen PV machine builder support -obj-$(CONFIG_XEN_PV_DOMAIN_BUILD) += xen_domainbuild.o diff --git a/hw/xenpv/xen_domainbuild.c b/hw/xenpv/xen_domainbuild.c deleted file mode 100644 index 188acaca16..0000000000 --- a/hw/xenpv/xen_domainbuild.c +++ /dev/null @@ -1,299 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu/units.h" -#include "hw/xen/xen_backend.h" -#include "xen_domainbuild.h" -#include "qemu/timer.h" -#include "qemu/log.h" - -#include - -static int xenstore_domain_mkdir(char *path) -{ - struct xs_permissions perms_ro[] = {{ - .id = 0, /* set owner: dom0 */ - },{ - .id = xen_domid, - .perms = XS_PERM_READ, - }}; - struct xs_permissions perms_rw[] = {{ - .id = 0, /* set owner: dom0 */ - },{ - .id = xen_domid, - .perms = XS_PERM_READ | XS_PERM_WRITE, - }}; - const char *writable[] = { "device", "control", "error", NULL }; - char subpath[256]; - int i; - - if (!xs_mkdir(xenstore, 0, path)) { - fprintf(stderr, "%s: xs_mkdir %s: failed\n", __func__, path); - return -1; - } - if (!xs_set_permissions(xenstore, 0, path, perms_ro, 2)) { - fprintf(stderr, "%s: xs_set_permissions failed\n", __func__); - return -1; - } - - for (i = 0; writable[i]; i++) { - snprintf(subpath, sizeof(subpath), "%s/%s", path, writable[i]); - if (!xs_mkdir(xenstore, 0, subpath)) { - fprintf(stderr, "%s: xs_mkdir %s: failed\n", __func__, subpath); - return -1; - } - if (!xs_set_permissions(xenstore, 0, subpath, perms_rw, 2)) { - fprintf(stderr, "%s: xs_set_permissions failed\n", __func__); - return -1; - } - } - return 0; -} - -int xenstore_domain_init1(const char *kernel, const char *ramdisk, - const char *cmdline) -{ - char *dom, uuid_string[42], vm[256], path[256]; - int i; - - qemu_uuid_unparse(&qemu_uuid, uuid_string); - dom = xs_get_domain_path(xenstore, xen_domid); - snprintf(vm, sizeof(vm), "/vm/%s", uuid_string); - - xenstore_domain_mkdir(dom); - - xenstore_write_str(vm, "image/ostype", "linux"); - if (kernel) - xenstore_write_str(vm, "image/kernel", kernel); - if (ramdisk) - xenstore_write_str(vm, "image/ramdisk", ramdisk); - if (cmdline) - xenstore_write_str(vm, "image/cmdline", cmdline); - - /* name + id */ - xenstore_write_str(vm, "name", qemu_name ? qemu_name : "no-name"); - xenstore_write_str(vm, "uuid", uuid_string); - xenstore_write_str(dom, "name", qemu_name ? qemu_name : "no-name"); - xenstore_write_int(dom, "domid", xen_domid); - xenstore_write_str(dom, "vm", vm); - - /* memory */ - xenstore_write_int(dom, "memory/target", ram_size / KiB); - xenstore_write_int(vm, "memory", ram_size / MiB); - xenstore_write_int(vm, "maxmem", ram_size / MiB); - - /* cpus */ - for (i = 0; i < smp_cpus; i++) { - snprintf(path, sizeof(path), "cpu/%d/availability",i); - xenstore_write_str(dom, path, "online"); - } - xenstore_write_int(vm, "vcpu_avail", smp_cpus); - xenstore_write_int(vm, "vcpus", smp_cpus); - - /* vnc password */ - xenstore_write_str(vm, "vncpassword", "" /* FIXME */); - - free(dom); - return 0; -} - -int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, - int console_port, int console_mfn) -{ - char *dom; - - dom = xs_get_domain_path(xenstore, xen_domid); - - /* signal new domain */ - xs_introduce_domain(xenstore, - xen_domid, - xenstore_mfn, - xenstore_port); - - /* xenstore */ - xenstore_write_int(dom, "store/ring-ref", xenstore_mfn); - xenstore_write_int(dom, "store/port", xenstore_port); - - /* console */ - xenstore_write_str(dom, "console/type", "ioemu"); - xenstore_write_int(dom, "console/limit", 128 * KiB); - xenstore_write_int(dom, "console/ring-ref", console_mfn); - xenstore_write_int(dom, "console/port", console_port); - xen_config_dev_console(0); - - free(dom); - return 0; -} - -/* ------------------------------------------------------------- */ - -static QEMUTimer *xen_poll; - -/* check domain state once per second */ -static void xen_domain_poll(void *opaque) -{ - struct xc_dominfo info; - int rc; - - rc = xc_domain_getinfo(xen_xc, xen_domid, 1, &info); - if ((rc != 1) || (info.domid != xen_domid)) { - qemu_log("xen: domain %d is gone\n", xen_domid); - goto quit; - } - if (info.dying) { - qemu_log("xen: domain %d is dying (%s%s)\n", xen_domid, - info.crashed ? "crashed" : "", - info.shutdown ? "shutdown" : ""); - goto quit; - } - - timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); - return; - -quit: - qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); -} - -static int xen_domain_watcher(void) -{ - int qemu_running = 1; - int fd[2], i, n, rc; - char byte; - - if (pipe(fd) != 0) { - qemu_log("%s: Huh? pipe error: %s\n", __func__, strerror(errno)); - return -1; - } - if (fork() != 0) - return 0; /* not child */ - - /* close all file handles, except stdio/out/err, - * our watch pipe and the xen interface handle */ - n = getdtablesize(); - for (i = 3; i < n; i++) { - if (i == fd[0]) - 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); - - /* wait for qemu exiting */ - while (qemu_running) { - rc = read(fd[0], &byte, 1); - switch (rc) { - case -1: - if (errno == EINTR) - continue; - qemu_log("%s: Huh? read error: %s\n", __func__, strerror(errno)); - qemu_running = 0; - break; - case 0: - /* EOF -> qemu exited */ - qemu_running = 0; - break; - default: - qemu_log("%s: Huh? data on the watch pipe?\n", __func__); - break; - } - } - - /* cleanup */ - qemu_log("%s: destroy domain %d\n", __func__, xen_domid); - xc_domain_destroy(xen_xc, xen_domid); - _exit(0); -} - -/* normal cleanup */ -static void xen_domain_cleanup(void) -{ - char *dom; - - dom = xs_get_domain_path(xenstore, xen_domid); - if (dom) { - xs_rm(xenstore, 0, dom); - free(dom); - } - xs_release_domain(xenstore, xen_domid); -} - -int xen_domain_build_pv(const char *kernel, const char *ramdisk, - const char *cmdline) -{ - uint32_t ssidref = 0; - uint32_t flags = 0; - xen_domain_handle_t uuid; - unsigned int xenstore_port = 0, console_port = 0; - unsigned long xenstore_mfn = 0, console_mfn = 0; - int rc; - - memcpy(uuid, &qemu_uuid, sizeof(uuid)); - rc = xen_domain_create(xen_xc, ssidref, uuid, flags, &xen_domid); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_create() failed\n"); - goto err; - } - qemu_log("xen: created domain %d\n", xen_domid); - atexit(xen_domain_cleanup); - if (xen_domain_watcher() == -1) { - goto err; - } - - xenstore_domain_init1(kernel, ramdisk, cmdline); - - rc = xc_domain_max_vcpus(xen_xc, xen_domid, smp_cpus); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_max_vcpus() failed\n"); - goto err; - } - -#if 0 - rc = xc_domain_setcpuweight(xen_xc, xen_domid, 256); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_setcpuweight() failed\n"); - goto err; - } -#endif - - rc = xc_domain_setmaxmem(xen_xc, xen_domid, ram_size / KiB); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_setmaxmem() failed\n"); - goto err; - } - - xenstore_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); - console_port = xc_evtchn_alloc_unbound(xen_xc, xen_domid, 0); - - rc = xc_linux_build(xen_xc, xen_domid, ram_size / MiB, - kernel, ramdisk, cmdline, - 0, flags, - xenstore_port, &xenstore_mfn, - console_port, &console_mfn); - if (rc < 0) { - fprintf(stderr, "xen: xc_linux_build() failed\n"); - goto err; - } - - xenstore_domain_init2(xenstore_port, xenstore_mfn, - console_port, console_mfn); - - qemu_log("xen: unpausing domain %d\n", xen_domid); - rc = xc_domain_unpause(xen_xc, xen_domid); - if (rc < 0) { - fprintf(stderr, "xen: xc_domain_unpause() failed\n"); - goto err; - } - - xen_poll = timer_new_ms(QEMU_CLOCK_REALTIME, xen_domain_poll, NULL); - timer_mod(xen_poll, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + 1000); - return 0; - -err: - return -1; -} diff --git a/hw/xenpv/xen_domainbuild.h b/hw/xenpv/xen_domainbuild.h deleted file mode 100644 index 652d9b410f..0000000000 --- a/hw/xenpv/xen_domainbuild.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef QEMU_HW_XEN_DOMAINBUILD_H -#define QEMU_HW_XEN_DOMAINBUILD_H - -#include "hw/xen/xen_common.h" - -int xenstore_domain_init1(const char *kernel, const char *ramdisk, - const char *cmdline); -int xenstore_domain_init2(int xenstore_port, int xenstore_mfn, - int console_port, int console_mfn); -int xen_domain_build_pv(const char *kernel, const char *ramdisk, - const char *cmdline); - -#endif /* QEMU_HW_XEN_DOMAINBUILD_H */ diff --git a/hw/xenpv/xen_machine_pv.c b/hw/xenpv/xen_machine_pv.c index 44d67b87c4..dcaf2a01a3 100644 --- a/hw/xenpv/xen_machine_pv.c +++ b/hw/xenpv/xen_machine_pv.c @@ -26,8 +26,8 @@ #include "qemu/error-report.h" #include "hw/hw.h" #include "hw/boards.h" -#include "hw/xen/xen_backend.h" -#include "xen_domainbuild.h" +#include "hw/xen/xen-legacy-backend.h" +#include "hw/xen/xen-bus.h" #include "sysemu/block-backend.h" static void xen_init_pv(MachineState *machine) @@ -43,21 +43,8 @@ static void xen_init_pv(MachineState *machine) switch (xen_mode) { case XEN_ATTACH: - /* nothing to do, xend handles everything */ + /* nothing to do, libxl handles everything */ break; -#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) { - error_report("xen pv domain creation failed"); - exit(1); - } - break; - } -#endif case XEN_EMULATE: error_report("xen emulation not implemented (yet)"); exit(1); @@ -93,6 +80,8 @@ static void xen_init_pv(MachineState *machine) xen_config_dev_nic(nd_table + i); } + xen_bus_init(); + /* config cleanup hook */ atexit(xen_config_cleanup); } diff --git a/hw/xtensa/Kconfig b/hw/xtensa/Kconfig new file mode 100644 index 0000000000..d72817d012 --- /dev/null +++ b/hw/xtensa/Kconfig @@ -0,0 +1,8 @@ +config XTENSA_SIM + bool + +config XTENSA_XTFPGA + bool + select OPENCORES_ETH + select PFLASH_CFI01 + select SERIAL diff --git a/hw/xtensa/Makefile.objs b/hw/xtensa/Makefile.objs index cb4998d2bf..0bbfccd6de 100644 --- a/hw/xtensa/Makefile.objs +++ b/hw/xtensa/Makefile.objs @@ -1,4 +1,5 @@ +obj-y += mx_pic.o obj-y += pic_cpu.o -obj-y += sim.o obj-y += xtensa_memory.o -obj-y += xtfpga.o +obj-$(CONFIG_XTENSA_SIM) += sim.o +obj-$(CONFIG_XTENSA_XTFPGA) += xtfpga.o diff --git a/hw/xtensa/mx_pic.c b/hw/xtensa/mx_pic.c new file mode 100644 index 0000000000..7075db9d4b --- /dev/null +++ b/hw/xtensa/mx_pic.c @@ -0,0 +1,354 @@ +/* + * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "hw/hw.h" +#include "hw/xtensa/mx_pic.h" +#include "qemu/log.h" + +#define MX_MAX_CPU 32 +#define MX_MAX_IRQ 32 + +#define MIROUT 0x0 +#define MIPICAUSE 0x100 +#define MIPISET 0x140 +#define MIENG 0x180 +#define MIENGSET 0x184 +#define MIASG 0x188 +#define MIASGSET 0x18c +#define MIPIPART 0x190 +#define SYSCFGID 0x1a0 +#define MPSCORE 0x200 +#define CCON 0x220 + +struct XtensaMxPic { + unsigned n_cpu; + unsigned n_irq; + + uint32_t ext_irq_state; + uint32_t mieng; + uint32_t miasg; + uint32_t mirout[MX_MAX_IRQ]; + uint32_t mipipart; + uint32_t runstall; + + qemu_irq *irq_inputs; + struct XtensaMxPicCpu { + XtensaMxPic *mx; + qemu_irq *irq; + qemu_irq runstall; + uint32_t mipicause; + uint32_t mirout_cache; + uint32_t irq_state_cache; + uint32_t ccon; + MemoryRegion reg; + } cpu[MX_MAX_CPU]; +}; + +static uint64_t xtensa_mx_pic_ext_reg_read(void *opaque, hwaddr offset, + unsigned size) +{ + struct XtensaMxPicCpu *mx_cpu = opaque; + struct XtensaMxPic *mx = mx_cpu->mx; + + if (offset < MIROUT + MX_MAX_IRQ) { + return mx->mirout[offset - MIROUT]; + } else if (offset >= MIPICAUSE && offset < MIPICAUSE + MX_MAX_CPU) { + return mx->cpu[offset - MIPICAUSE].mipicause; + } else { + switch (offset) { + case MIENG: + return mx->mieng; + + case MIASG: + return mx->miasg; + + case MIPIPART: + return mx->mipipart; + + case SYSCFGID: + return ((mx->n_cpu - 1) << 18) | (mx_cpu - mx->cpu); + + case MPSCORE: + return mx->runstall; + + case CCON: + return mx_cpu->ccon; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "unknown RER in MX PIC range: 0x%08x\n", + (uint32_t)offset); + return 0; + } + } +} + +static uint32_t xtensa_mx_pic_get_ipi_for_cpu(const XtensaMxPic *mx, + unsigned cpu) +{ + uint32_t mipicause = mx->cpu[cpu].mipicause; + uint32_t mipipart = mx->mipipart; + + return (((mipicause & 1) << (mipipart & 3)) | + ((mipicause & 0x000e) != 0) << ((mipipart >> 2) & 3) | + ((mipicause & 0x00f0) != 0) << ((mipipart >> 4) & 3) | + ((mipicause & 0xff00) != 0) << ((mipipart >> 6) & 3)) & 0x7; +} + +static uint32_t xtensa_mx_pic_get_ext_irq_for_cpu(const XtensaMxPic *mx, + unsigned cpu) +{ + return ((((mx->ext_irq_state & mx->mieng) | mx->miasg) & + mx->cpu[cpu].mirout_cache) << 2) | + xtensa_mx_pic_get_ipi_for_cpu(mx, cpu); +} + +static void xtensa_mx_pic_update_cpu(XtensaMxPic *mx, unsigned cpu) +{ + uint32_t irq = xtensa_mx_pic_get_ext_irq_for_cpu(mx, cpu); + uint32_t changed_irq = mx->cpu[cpu].irq_state_cache ^ irq; + unsigned i; + + qemu_log_mask(CPU_LOG_INT, "%s: CPU %d, irq: %08x, changed_irq: %08x\n", + __func__, cpu, irq, changed_irq); + mx->cpu[cpu].irq_state_cache = irq; + for (i = 0; changed_irq; ++i) { + uint32_t mask = 1u << i; + + if (changed_irq & mask) { + changed_irq ^= mask; + qemu_set_irq(mx->cpu[cpu].irq[i], irq & mask); + } + } +} + +static void xtensa_mx_pic_update_all(XtensaMxPic *mx) +{ + unsigned cpu; + + for (cpu = 0; cpu < mx->n_cpu; ++cpu) { + xtensa_mx_pic_update_cpu(mx, cpu); + } +} + +static void xtensa_mx_pic_ext_reg_write(void *opaque, hwaddr offset, + uint64_t v, unsigned size) +{ + struct XtensaMxPicCpu *mx_cpu = opaque; + struct XtensaMxPic *mx = mx_cpu->mx; + unsigned cpu; + + if (offset < MIROUT + mx->n_irq) { + mx->mirout[offset - MIROUT] = v; + for (cpu = 0; cpu < mx->n_cpu; ++cpu) { + uint32_t mask = 1u << (offset - MIROUT); + + if (!(mx->cpu[cpu].mirout_cache & mask) != !(v & (1u << cpu))) { + mx->cpu[cpu].mirout_cache ^= mask; + xtensa_mx_pic_update_cpu(mx, cpu); + } + } + } else if (offset >= MIPICAUSE && offset < MIPICAUSE + mx->n_cpu) { + cpu = offset - MIPICAUSE; + mx->cpu[cpu].mipicause &= ~v; + xtensa_mx_pic_update_cpu(mx, cpu); + } else if (offset >= MIPISET && offset < MIPISET + 16) { + for (cpu = 0; cpu < mx->n_cpu; ++cpu) { + if (v & (1u << cpu)) { + mx->cpu[cpu].mipicause |= 1u << (offset - MIPISET); + xtensa_mx_pic_update_cpu(mx, cpu); + } + } + } else { + uint32_t change = 0; + uint32_t oldv, newv; + const char *name = "???"; + + switch (offset) { + case MIENG: + change = mx->mieng & v; + oldv = mx->mieng; + mx->mieng &= ~v; + newv = mx->mieng; + name = "MIENG"; + break; + + case MIENGSET: + change = ~mx->mieng & v; + oldv = mx->mieng; + mx->mieng |= v; + newv = mx->mieng; + name = "MIENG"; + break; + + case MIASG: + change = mx->miasg & v; + oldv = mx->miasg; + mx->miasg &= ~v; + newv = mx->miasg; + name = "MIASG"; + break; + + case MIASGSET: + change = ~mx->miasg & v; + oldv = mx->miasg; + mx->miasg |= v; + newv = mx->miasg; + name = "MIASG"; + break; + + case MIPIPART: + change = mx->mipipart ^ v; + oldv = mx->mipipart; + mx->mipipart = v; + newv = mx->mipipart; + name = "MIPIPART"; + break; + + case MPSCORE: + change = mx->runstall ^ v; + oldv = mx->runstall; + mx->runstall = v; + newv = mx->runstall; + name = "RUNSTALL"; + for (cpu = 0; cpu < mx->n_cpu; ++cpu) { + if (change & (1u << cpu)) { + qemu_set_irq(mx->cpu[cpu].runstall, v & (1u << cpu)); + } + } + break; + + case CCON: + mx_cpu->ccon = v & 0x1; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "unknown WER in MX PIC range: 0x%08x = 0x%08x\n", + (uint32_t)offset, (uint32_t)v); + break; + } + if (change) { + qemu_log_mask(CPU_LOG_INT, + "%s: %s changed by CPU %d: %08x -> %08x\n", + __func__, name, (int)(mx_cpu - mx->cpu), + oldv, newv); + xtensa_mx_pic_update_all(mx); + } + } +} + +static const MemoryRegionOps xtensa_mx_pic_ops = { + .read = xtensa_mx_pic_ext_reg_read, + .write = xtensa_mx_pic_ext_reg_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid = { + .unaligned = true, + }, +}; + +MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx, + qemu_irq *irq, + qemu_irq runstall) +{ + struct XtensaMxPicCpu *mx_cpu = mx->cpu + mx->n_cpu; + + mx_cpu->mx = mx; + mx_cpu->irq = irq; + mx_cpu->runstall = runstall; + + memory_region_init_io(&mx_cpu->reg, NULL, &xtensa_mx_pic_ops, mx_cpu, + "mx_pic", 0x280); + + ++mx->n_cpu; + return &mx_cpu->reg; +} + +static void xtensa_mx_pic_set_irq(void *opaque, int irq, int active) +{ + XtensaMxPic *mx = opaque; + + if (irq < mx->n_irq) { + uint32_t old_irq_state = mx->ext_irq_state; + + if (active) { + mx->ext_irq_state |= 1u << irq; + } else { + mx->ext_irq_state &= ~(1u << irq); + } + if (old_irq_state != mx->ext_irq_state) { + qemu_log_mask(CPU_LOG_INT, + "%s: IRQ %d, active: %d, ext_irq_state: %08x -> %08x\n", + __func__, irq, active, + old_irq_state, mx->ext_irq_state); + xtensa_mx_pic_update_all(mx); + } + } else { + qemu_log_mask(LOG_GUEST_ERROR, "%s: IRQ %d out of range\n", + __func__, irq); + } +} + +XtensaMxPic *xtensa_mx_pic_init(unsigned n_irq) +{ + XtensaMxPic *mx = calloc(1, sizeof(XtensaMxPic)); + + mx->n_irq = n_irq + 1; + mx->irq_inputs = qemu_allocate_irqs(xtensa_mx_pic_set_irq, mx, + mx->n_irq); + return mx; +} + +void xtensa_mx_pic_reset(void *opaque) +{ + XtensaMxPic *mx = opaque; + unsigned i; + + mx->ext_irq_state = 0; + mx->mieng = mx->n_irq < 32 ? (1u << mx->n_irq) - 1 : ~0u; + mx->miasg = 0; + mx->mipipart = 0; + for (i = 0; i < mx->n_irq; ++i) { + mx->mirout[i] = 1; + } + for (i = 0; i < mx->n_cpu; ++i) { + mx->cpu[i].mipicause = 0; + mx->cpu[i].mirout_cache = i ? 0 : mx->mieng; + mx->cpu[i].irq_state_cache = 0; + mx->cpu[i].ccon = 0; + } + mx->runstall = (1u << mx->n_cpu) - 2; + for (i = 0; i < mx->n_cpu; ++i) { + qemu_set_irq(mx->cpu[i].runstall, i > 0); + } +} + +qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx) +{ + return mx->irq_inputs + 1; +} diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c index 0e812d7f06..a8939f5e58 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -68,36 +68,37 @@ static void xtensa_set_irq(void *opaque, int irq, int active) uint32_t irq_bit = 1 << irq; if (active) { - env->sregs[INTSET] |= irq_bit; + atomic_or(&env->sregs[INTSET], irq_bit); } else if (env->config->interrupt[irq].inttype == INTTYPE_LEVEL) { - env->sregs[INTSET] &= ~irq_bit; + atomic_and(&env->sregs[INTSET], ~irq_bit); } check_interrupts(env); } } -void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active) -{ - qemu_set_irq(env->irq_inputs[env->config->timerint[id]], active); -} - static void xtensa_ccompare_cb(void *opaque) { XtensaCcompareTimer *ccompare = opaque; CPUXtensaState *env = ccompare->env; unsigned i = ccompare - env->ccompare; - xtensa_timer_irq(env, i, 1); + qemu_set_irq(env->irq_inputs[env->config->timerint[i]], 1); +} + +static void xtensa_set_runstall(void *opaque, int irq, int active) +{ + CPUXtensaState *env = opaque; + xtensa_runstall(env, active); } void xtensa_irq_init(CPUXtensaState *env) { - env->irq_inputs = (void **)qemu_allocate_irqs( - xtensa_set_irq, env, env->config->ninterrupt); - if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { - unsigned i; + unsigned i; + env->irq_inputs = qemu_allocate_irqs(xtensa_set_irq, env, + env->config->ninterrupt); + if (xtensa_option_enabled(env->config, XTENSA_OPTION_TIMER_INTERRUPT)) { env->time_base = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); env->ccount_base = env->sregs[CCOUNT]; for (i = 0; i < env->config->nccompare; ++i) { @@ -106,16 +107,20 @@ void xtensa_irq_init(CPUXtensaState *env) xtensa_ccompare_cb, env->ccompare + i); } } + for (i = 0; i < env->config->nextint; ++i) { + unsigned irq = env->config->extint[i]; + + env->ext_irq_inputs[i] = env->irq_inputs[irq]; + } + env->runstall_irq = qemu_allocate_irq(xtensa_set_runstall, env, 0); } -void *xtensa_get_extint(CPUXtensaState *env, unsigned extint) +qemu_irq *xtensa_get_extints(CPUXtensaState *env) { - if (extint < env->config->nextint) { - unsigned irq = env->config->extint[extint]; - return env->irq_inputs[irq]; - } else { - qemu_log("%s: trying to acquire invalid external interrupt %d\n", - __func__, extint); - return NULL; - } + return env->ext_irq_inputs; +} + +qemu_irq xtensa_get_runstall(CPUXtensaState *env) +{ + return env->runstall_irq; } diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index b6ccb3cd4a..12c7437398 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -97,11 +97,15 @@ static void xtensa_sim_init(MachineState *machine) uint64_t elf_entry; uint64_t elf_lowaddr; #ifdef TARGET_WORDS_BIGENDIAN - int success = load_elf(kernel_filename, translate_phys_addr, cpu, - &elf_entry, &elf_lowaddr, NULL, 1, EM_XTENSA, 0, 0); + int success = load_elf(kernel_filename, NULL, + translate_phys_addr, cpu, + &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, EM_XTENSA, 0, 0); + int success = load_elf(kernel_filename, NULL, + translate_phys_addr, cpu, + &elf_entry, &elf_lowaddr, + NULL, 0, EM_XTENSA, 0, 0); #endif if (success > 0) { env->pc = elf_entry; diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index b3161de320..e05ef75a75 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -45,6 +45,7 @@ #include "qemu/option.h" #include "bootparam.h" #include "xtensa_memory.h" +#include "hw/xtensa/mx_pic.h" typedef struct XtfpgaFlashDesc { hwaddr base; @@ -61,6 +62,7 @@ typedef struct XtfpgaBoardDesc { typedef struct XtfpgaFpgaState { MemoryRegion iomem; + uint32_t freq; uint32_t leds; uint32_t switches; } XtfpgaFpgaState; @@ -83,7 +85,7 @@ static uint64_t xtfpga_fpga_read(void *opaque, hwaddr addr, return 0x09272011; case 0x4: /*processor clock frequency, Hz*/ - return 10000000; + return s->freq; case 0x8: /*LEDs (off = 0, on = 1)*/ return s->leds; @@ -119,13 +121,14 @@ static const MemoryRegionOps xtfpga_fpga_ops = { }; static XtfpgaFpgaState *xtfpga_fpga_init(MemoryRegion *address_space, - hwaddr base) + hwaddr base, uint32_t freq) { XtfpgaFpgaState *s = g_malloc(sizeof(XtfpgaFpgaState)); memory_region_init_io(&s->iomem, NULL, &xtfpga_fpga_ops, s, - "xtfpga.fpga", 0x10000); + "xtfpga.fpga", 0x10000); memory_region_add_subregion(address_space, base, &s->iomem); + s->freq = freq; xtfpga_fpga_reset(s); qemu_register_reset(xtfpga_fpga_reset, s); return s; @@ -159,12 +162,12 @@ static void xtfpga_net_init(MemoryRegion *address_space, memory_region_add_subregion(address_space, buffers, ram); } -static pflash_t *xtfpga_flash_init(MemoryRegion *address_space, - const XtfpgaBoardDesc *board, - DriveInfo *dinfo, int be) +static PFlashCFI01 *xtfpga_flash_init(MemoryRegion *address_space, + const XtfpgaBoardDesc *board, + DriveInfo *dinfo, int be) { SysBusDevice *s; - DeviceState *dev = qdev_create(NULL, "cfi.pflash01"); + DeviceState *dev = qdev_create(NULL, TYPE_PFLASH_CFI01); qdev_prop_set_drive(dev, "drive", blk_by_legacy_dinfo(dinfo), &error_abort); @@ -178,7 +181,7 @@ static pflash_t *xtfpga_flash_init(MemoryRegion *address_space, 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"); + return PFLASH_CFI01(dev); } static uint64_t translate_phys_addr(void *opaque, uint64_t addr) @@ -223,27 +226,55 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) XtensaCPU *cpu = NULL; CPUXtensaState *env = NULL; MemoryRegion *system_io; + XtensaMxPic *mx_pic = NULL; + qemu_irq *extints; DriveInfo *dinfo; - pflash_t *flash = NULL; + PFlashCFI01 *flash = NULL; QemuOpts *machine_opts = qemu_get_machine_opts(); const char *kernel_filename = qemu_opt_get(machine_opts, "kernel"); const char *kernel_cmdline = qemu_opt_get(machine_opts, "append"); const char *dtb_filename = qemu_opt_get(machine_opts, "dtb"); const char *initrd_filename = qemu_opt_get(machine_opts, "initrd"); const unsigned system_io_size = 224 * MiB; + uint32_t freq = 10000000; int n; + if (smp_cpus > 1) { + mx_pic = xtensa_mx_pic_init(31); + qemu_register_reset(xtensa_mx_pic_reset, mx_pic); + } for (n = 0; n < smp_cpus; n++) { - cpu = XTENSA_CPU(cpu_create(machine->cpu_type)); - env = &cpu->env; + CPUXtensaState *cenv = NULL; - env->sregs[PRID] = n; + cpu = XTENSA_CPU(cpu_create(machine->cpu_type)); + cenv = &cpu->env; + if (!env) { + env = cenv; + freq = env->config->clock_freq_khz * 1000; + } + + if (mx_pic) { + MemoryRegion *mx_eri; + + mx_eri = xtensa_mx_pic_register_cpu(mx_pic, + xtensa_get_extints(cenv), + xtensa_get_runstall(cenv)); + memory_region_add_subregion(xtensa_get_er_region(cenv), + 0, mx_eri); + } + cenv->sregs[PRID] = n; + xtensa_select_static_vectors(cenv, n != 0); qemu_register_reset(xtfpga_reset, cpu); /* Need MMU initialized prior to ELF loading, * so that ELF gets loaded into virtual addresses */ cpu_reset(CPU(cpu)); } + if (smp_cpus > 1) { + extints = xtensa_mx_pic_get_extints(mx_pic); + } else { + extints = xtensa_get_extints(env); + } if (env) { XtensaMemory sysram = env->config->sysram; @@ -272,14 +303,14 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) system_io, 0, system_io_size); memory_region_add_subregion(system_memory, board->io[1], io); } - xtfpga_fpga_init(system_io, 0x0d020000); + xtfpga_fpga_init(system_io, 0x0d020000, freq); if (nd_table[0].used) { xtfpga_net_init(system_io, 0x0d030000, 0x0d030400, 0x0d800000, - xtensa_get_extint(env, 1), nd_table); + extints[1], nd_table); } - serial_mm_init(system_io, 0x0d050020, 2, xtensa_get_extint(env, 0), - 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); + serial_mm_init(system_io, 0x0d050020, 2, extints[0], + 115200, serial_hd(0), DEVICE_NATIVE_ENDIAN); dinfo = drive_get(IF_PFLASH, 0, 0); if (dinfo) { @@ -378,7 +409,7 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) uint64_t elf_entry; uint64_t elf_lowaddr; - int success = load_elf(kernel_filename, translate_phys_addr, cpu, + int success = load_elf(kernel_filename, NULL, translate_phys_addr, cpu, &elf_entry, &elf_lowaddr, NULL, be, EM_XTENSA, 0, 0); if (success > 0) { entry_point = elf_entry; @@ -446,6 +477,8 @@ static void xtfpga_init(const XtfpgaBoardDesc *board, MachineState *machine) } } +#define XTFPGA_MMU_RESERVED_MEMORY_SIZE (128 * MiB) + static const hwaddr xtfpga_mmu_io[2] = { 0xf0000000, }; @@ -566,8 +599,9 @@ static void xtfpga_lx60_class_init(ObjectClass *oc, void *data) mc->desc = "lx60 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; mc->init = xtfpga_lx60_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; + mc->default_ram_size = 64 * MiB; } static const TypeInfo xtfpga_lx60_type = { @@ -582,8 +616,9 @@ static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data) mc->desc = "lx60 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")"; mc->init = xtfpga_lx60_nommu_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; + mc->default_ram_size = 64 * MiB; } static const TypeInfo xtfpga_lx60_nommu_type = { @@ -598,8 +633,9 @@ static void xtfpga_lx200_class_init(ObjectClass *oc, void *data) mc->desc = "lx200 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; mc->init = xtfpga_lx200_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; + mc->default_ram_size = 96 * MiB; } static const TypeInfo xtfpga_lx200_type = { @@ -614,8 +650,9 @@ static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data) mc->desc = "lx200 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")"; mc->init = xtfpga_lx200_nommu_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; + mc->default_ram_size = 96 * MiB; } static const TypeInfo xtfpga_lx200_nommu_type = { @@ -630,8 +667,9 @@ static void xtfpga_ml605_class_init(ObjectClass *oc, void *data) mc->desc = "ml605 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; mc->init = xtfpga_ml605_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; + mc->default_ram_size = 512 * MiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; } static const TypeInfo xtfpga_ml605_type = { @@ -646,8 +684,9 @@ static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data) mc->desc = "ml605 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")"; mc->init = xtfpga_ml605_nommu_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; + mc->default_ram_size = 256 * MiB; } static const TypeInfo xtfpga_ml605_nommu_type = { @@ -662,8 +701,9 @@ static void xtfpga_kc705_class_init(ObjectClass *oc, void *data) mc->desc = "kc705 EVB (" XTENSA_DEFAULT_CPU_MODEL ")"; mc->init = xtfpga_kc705_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_TYPE; + mc->default_ram_size = 1 * GiB - XTFPGA_MMU_RESERVED_MEMORY_SIZE; } static const TypeInfo xtfpga_kc705_type = { @@ -678,8 +718,9 @@ static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data) mc->desc = "kc705 noMMU EVB (" XTENSA_DEFAULT_CPU_NOMMU_MODEL ")"; mc->init = xtfpga_kc705_nommu_init; - mc->max_cpus = 4; + mc->max_cpus = 32; mc->default_cpu_type = XTENSA_DEFAULT_CPU_NOMMU_TYPE; + mc->default_ram_size = 256 * MiB; } static const TypeInfo xtfpga_kc705_nommu_type = { diff --git a/include/authz/base.h b/include/authz/base.h new file mode 100644 index 0000000000..55ac9581ad --- /dev/null +++ b/include/authz/base.h @@ -0,0 +1,112 @@ +/* + * QEMU authorization framework base class + * + * Copyright (c) 2018 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 QAUTHZ_BASE_H__ +#define QAUTHZ_BASE_H__ + +#include "qemu-common.h" +#include "qapi/error.h" +#include "qom/object.h" + + +#define TYPE_QAUTHZ "authz" + +#define QAUTHZ_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZClass, (klass), \ + TYPE_QAUTHZ) +#define QAUTHZ_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZClass, (obj), \ + TYPE_QAUTHZ) +#define QAUTHZ(obj) \ + OBJECT_CHECK(QAuthZ, (obj), \ + TYPE_QAUTHZ) + +typedef struct QAuthZ QAuthZ; +typedef struct QAuthZClass QAuthZClass; + +/** + * QAuthZ: + * + * The QAuthZ class defines an API contract to be used + * for providing an authorization driver for services + * with user identities. + */ + +struct QAuthZ { + Object parent_obj; +}; + + +struct QAuthZClass { + ObjectClass parent_class; + + bool (*is_allowed)(QAuthZ *authz, + const char *identity, + Error **errp); +}; + + +/** + * qauthz_is_allowed: + * @authz: the authorization object + * @identity: the user identity to authorize + * @errp: pointer to a NULL initialized error object + * + * Check if a user @identity is authorized. If an error + * occurs this method will return false to indicate + * denial, as well as setting @errp to contain the details. + * Callers are recommended to treat the denial and error + * scenarios identically. Specifically the error info in + * @errp should never be fed back to the user being + * authorized, it is merely for benefit of administrator + * debugging. + * + * Returns: true if @identity is authorized, false if denied or if + * an error occurred. + */ +bool qauthz_is_allowed(QAuthZ *authz, + const char *identity, + Error **errp); + + +/** + * qauthz_is_allowed_by_id: + * @authzid: ID of the authorization object + * @identity: the user identity to authorize + * @errp: pointer to a NULL initialized error object + * + * Check if a user @identity is authorized. If an error + * occurs this method will return false to indicate + * denial, as well as setting @errp to contain the details. + * Callers are recommended to treat the denial and error + * scenarios identically. Specifically the error info in + * @errp should never be fed back to the user being + * authorized, it is merely for benefit of administrator + * debugging. + * + * Returns: true if @identity is authorized, false if denied or if + * an error occurred. + */ +bool qauthz_is_allowed_by_id(const char *authzid, + const char *identity, + Error **errp); + +#endif /* QAUTHZ_BASE_H__ */ + diff --git a/include/authz/list.h b/include/authz/list.h new file mode 100644 index 0000000000..138ae7047c --- /dev/null +++ b/include/authz/list.h @@ -0,0 +1,106 @@ +/* + * QEMU list authorization driver + * + * Copyright (c) 2018 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 QAUTHZ_LIST_H__ +#define QAUTHZ_LIST_H__ + +#include "authz/base.h" +#include "qapi/qapi-types-authz.h" + +#define TYPE_QAUTHZ_LIST "authz-list" + +#define QAUTHZ_LIST_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZListClass, (klass), \ + TYPE_QAUTHZ_LIST) +#define QAUTHZ_LIST_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZListClass, (obj), \ + TYPE_QAUTHZ_LIST) +#define QAUTHZ_LIST(obj) \ + OBJECT_CHECK(QAuthZList, (obj), \ + TYPE_QAUTHZ_LIST) + +typedef struct QAuthZList QAuthZList; +typedef struct QAuthZListClass QAuthZListClass; + + +/** + * QAuthZList: + * + * This authorization driver provides a list mechanism + * for granting access by matching user names against a + * list of globs. Each match rule has an associated policy + * and a catch all policy applies if no rule matches + * + * To create an instance of this class via QMP: + * + * { + * "execute": "object-add", + * "arguments": { + * "qom-type": "authz-list", + * "id": "authz0", + * "props": { + * "rules": [ + * { "match": "fred", "policy": "allow", "format": "exact" }, + * { "match": "bob", "policy": "allow", "format": "exact" }, + * { "match": "danb", "policy": "deny", "format": "exact" }, + * { "match": "dan*", "policy": "allow", "format": "glob" } + * ], + * "policy": "deny" + * } + * } + * } + * + */ +struct QAuthZList { + QAuthZ parent_obj; + + QAuthZListPolicy policy; + QAuthZListRuleList *rules; +}; + + +struct QAuthZListClass { + QAuthZClass parent_class; +}; + + +QAuthZList *qauthz_list_new(const char *id, + QAuthZListPolicy policy, + Error **errp); + +ssize_t qauthz_list_append_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + Error **errp); + +ssize_t qauthz_list_insert_rule(QAuthZList *auth, + const char *match, + QAuthZListPolicy policy, + QAuthZListFormat format, + size_t index, + Error **errp); + +ssize_t qauthz_list_delete_rule(QAuthZList *auth, + const char *match); + + +#endif /* QAUTHZ_LIST_H__ */ + diff --git a/include/authz/listfile.h b/include/authz/listfile.h new file mode 100644 index 0000000000..0d618c903c --- /dev/null +++ b/include/authz/listfile.h @@ -0,0 +1,111 @@ +/* + * QEMU list file authorization driver + * + * Copyright (c) 2018 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 QAUTHZ_LIST_FILE_H__ +#define QAUTHZ_LIST_FILE_H__ + +#include "authz/list.h" +#include "qapi/qapi-types-authz.h" +#include "qemu/filemonitor.h" + +#define TYPE_QAUTHZ_LIST_FILE "authz-list-file" + +#define QAUTHZ_LIST_FILE_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZListFileClass, (klass), \ + TYPE_QAUTHZ_LIST_FILE) +#define QAUTHZ_LIST_FILE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZListFileClass, (obj), \ + TYPE_QAUTHZ_LIST_FILE) +#define QAUTHZ_LIST_FILE(obj) \ + OBJECT_CHECK(QAuthZListFile, (obj), \ + TYPE_QAUTHZ_LIST_FILE) + +typedef struct QAuthZListFile QAuthZListFile; +typedef struct QAuthZListFileClass QAuthZListFileClass; + + +/** + * QAuthZListFile: + * + * This authorization driver provides a file mechanism + * for granting access by matching user names against a + * file of globs. Each match rule has an associated policy + * and a catch all policy applies if no rule matches + * + * To create an instance of this class via QMP: + * + * { + * "execute": "object-add", + * "arguments": { + * "qom-type": "authz-list-file", + * "id": "authz0", + * "props": { + * "filename": "/etc/qemu/myvm-vnc.acl", + * "refresh": true + * } + * } + * } + * + * If 'refresh' is 'yes', inotify is used to monitor for changes + * to the file and auto-reload the rules. + * + * The myvm-vnc.acl file should contain the parameters for + * the QAuthZList object in JSON format: + * + * { + * "rules": [ + * { "match": "fred", "policy": "allow", "format": "exact" }, + * { "match": "bob", "policy": "allow", "format": "exact" }, + * { "match": "danb", "policy": "deny", "format": "exact" }, + * { "match": "dan*", "policy": "allow", "format": "glob" } + * ], + * "policy": "deny" + * } + * + * The object can be created on the command line using + * + * -object authz-list-file,id=authz0,\ + * filename=/etc/qemu/myvm-vnc.acl,refresh=yes + * + */ +struct QAuthZListFile { + QAuthZ parent_obj; + + QAuthZ *list; + char *filename; + bool refresh; + QFileMonitor *file_monitor; + int64_t file_watch; +}; + + +struct QAuthZListFileClass { + QAuthZClass parent_class; +}; + + +QAuthZListFile *qauthz_list_file_new(const char *id, + const char *filename, + bool refresh, + Error **errp); + + +#endif /* QAUTHZ_LIST_FILE_H__ */ + diff --git a/include/authz/pamacct.h b/include/authz/pamacct.h new file mode 100644 index 0000000000..cad5b11d47 --- /dev/null +++ b/include/authz/pamacct.h @@ -0,0 +1,100 @@ +/* + * QEMU PAM authorization driver + * + * Copyright (c) 2018 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 QAUTHZ_PAM_H__ +#define QAUTHZ_PAM_H__ + +#include "authz/base.h" + + +#define TYPE_QAUTHZ_PAM "authz-pam" + +#define QAUTHZ_PAM_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZPAMClass, (klass), \ + TYPE_QAUTHZ_PAM) +#define QAUTHZ_PAM_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZPAMClass, (obj), \ + TYPE_QAUTHZ_PAM) +#define QAUTHZ_PAM(obj) \ + OBJECT_CHECK(QAuthZPAM, (obj), \ + TYPE_QAUTHZ_PAM) + +typedef struct QAuthZPAM QAuthZPAM; +typedef struct QAuthZPAMClass QAuthZPAMClass; + + +/** + * QAuthZPAM: + * + * This authorization driver provides a PAM mechanism + * for granting access by matching user names against a + * list of globs. Each match rule has an associated policy + * and a catch all policy applies if no rule matches + * + * To create an instance of this class via QMP: + * + * { + * "execute": "object-add", + * "arguments": { + * "qom-type": "authz-pam", + * "id": "authz0", + * "parameters": { + * "service": "qemu-vnc-tls" + * } + * } + * } + * + * The driver only uses the PAM "account" verification + * subsystem. The above config would require a config + * file /etc/pam.d/qemu-vnc-tls. For a simple file + * lookup it would contain + * + * account requisite pam_listfile.so item=user sense=allow \ + * file=/etc/qemu/vnc.allow + * + * The external file would then contain a list of usernames. + * If x509 cert was being used as the username, a suitable + * entry would match the distinguish name: + * + * CN=laptop.berrange.com,O=Berrange Home,L=London,ST=London,C=GB + * + * On the command line it can be created using + * + * -object authz-pam,id=authz0,service=qemu-vnc-tls + * + */ +struct QAuthZPAM { + QAuthZ parent_obj; + + char *service; +}; + + +struct QAuthZPAMClass { + QAuthZClass parent_class; +}; + + +QAuthZPAM *qauthz_pam_new(const char *id, + const char *service, + Error **errp); + + +#endif /* QAUTHZ_PAM_H__ */ diff --git a/include/authz/simple.h b/include/authz/simple.h new file mode 100644 index 0000000000..30b932dfeb --- /dev/null +++ b/include/authz/simple.h @@ -0,0 +1,84 @@ +/* + * QEMU simple authorization driver + * + * Copyright (c) 2018 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 QAUTHZ_SIMPLE_H__ +#define QAUTHZ_SIMPLE_H__ + +#include "authz/base.h" + +#define TYPE_QAUTHZ_SIMPLE "authz-simple" + +#define QAUTHZ_SIMPLE_CLASS(klass) \ + OBJECT_CLASS_CHECK(QAuthZSimpleClass, (klass), \ + TYPE_QAUTHZ_SIMPLE) +#define QAUTHZ_SIMPLE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(QAuthZSimpleClass, (obj), \ + TYPE_QAUTHZ_SIMPLE) +#define QAUTHZ_SIMPLE(obj) \ + OBJECT_CHECK(QAuthZSimple, (obj), \ + TYPE_QAUTHZ_SIMPLE) + +typedef struct QAuthZSimple QAuthZSimple; +typedef struct QAuthZSimpleClass QAuthZSimpleClass; + + +/** + * QAuthZSimple: + * + * This authorization driver provides a simple mechanism + * for granting access based on an exact matched username. + * + * To create an instance of this class via QMP: + * + * { + * "execute": "object-add", + * "arguments": { + * "qom-type": "authz-simple", + * "id": "authz0", + * "props": { + * "identity": "fred" + * } + * } + * } + * + * Or via the command line + * + * -object authz-simple,id=authz0,identity=fred + * + */ +struct QAuthZSimple { + QAuthZ parent_obj; + + char *identity; +}; + + +struct QAuthZSimpleClass { + QAuthZClass parent_class; +}; + + +QAuthZSimple *qauthz_simple_new(const char *id, + const char *identity, + Error **errp); + + +#endif /* QAUTHZ_SIMPLE_H__ */ + diff --git a/include/block/aio-wait.h b/include/block/aio-wait.h index c85a62f798..afd0ff7eb8 100644 --- a/include/block/aio-wait.h +++ b/include/block/aio-wait.h @@ -30,14 +30,15 @@ /** * AioWait: * - * An object that facilitates synchronous waiting on a condition. The main - * loop can wait on an operation running in an IOThread as follows: + * An object that facilitates synchronous waiting on a condition. A single + * global AioWait object (global_aio_wait) is used internally. + * + * The main loop can wait on an operation running in an IOThread as follows: * - * AioWait *wait = ...; * AioContext *ctx = ...; * MyWork work = { .done = false }; * schedule_my_work_in_iothread(ctx, &work); - * AIO_WAIT_WHILE(wait, ctx, !work.done); + * AIO_WAIT_WHILE(ctx, !work.done); * * The IOThread must call aio_wait_kick() to notify the main loop when * work.done changes: @@ -46,7 +47,7 @@ * { * ... * work.done = true; - * aio_wait_kick(wait); + * aio_wait_kick(); * } */ typedef struct { @@ -54,9 +55,10 @@ typedef struct { unsigned num_waiters; } AioWait; +extern AioWait global_aio_wait; + /** * AIO_WAIT_WHILE: - * @wait: the aio wait object * @ctx: the aio context, or NULL if multiple aio contexts (for which the * caller does not hold a lock) are involved in the polling condition. * @cond: wait while this conditional expression is true @@ -72,10 +74,12 @@ typedef struct { * wait on conditions between two IOThreads since that could lead to deadlock, * go via the main loop instead. */ -#define AIO_WAIT_WHILE(wait, ctx, cond) ({ \ +#define AIO_WAIT_WHILE(ctx, cond) ({ \ bool waited_ = false; \ - AioWait *wait_ = (wait); \ + AioWait *wait_ = &global_aio_wait; \ AioContext *ctx_ = (ctx); \ + /* Increment wait_->num_waiters before evaluating cond. */ \ + atomic_inc(&wait_->num_waiters); \ if (ctx_ && in_aio_context_home_thread(ctx_)) { \ while ((cond)) { \ aio_poll(ctx_, true); \ @@ -84,8 +88,6 @@ typedef struct { } else { \ assert(qemu_get_current_aio_context() == \ qemu_get_aio_context()); \ - /* Increment wait_->num_waiters before evaluating cond. */ \ - atomic_inc(&wait_->num_waiters); \ while ((cond)) { \ if (ctx_) { \ aio_context_release(ctx_); \ @@ -96,20 +98,18 @@ typedef struct { } \ waited_ = true; \ } \ - atomic_dec(&wait_->num_waiters); \ } \ + atomic_dec(&wait_->num_waiters); \ waited_; }) /** * aio_wait_kick: - * @wait: the aio wait object that should re-evaluate its condition - * * Wake up the main thread if it is waiting on AIO_WAIT_WHILE(). During * synchronous operations performed in an IOThread, the main thread lets the * IOThread's event loop run, waiting for the operation to complete. A * aio_wait_kick() call will wake up the main thread. */ -void aio_wait_kick(AioWait *wait); +void aio_wait_kick(void); /** * aio_wait_bh_oneshot: diff --git a/include/block/aio.h b/include/block/aio.h index f08630c6e5..0ca25dfec6 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -387,6 +387,32 @@ struct LinuxAioState *aio_setup_linux_aio(AioContext *ctx, Error **errp); /* Return the LinuxAioState bound to this AioContext */ struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); +/** + * aio_timer_new_with_attrs: + * @ctx: the aio context + * @type: the clock type + * @scale: the scale + * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_ values + * to assign + * @cb: the callback to call on timer expiry + * @opaque: the opaque pointer to pass to the callback + * + * Allocate a new timer (with attributes) attached to the context @ctx. + * The function is responsible for memory allocation. + * + * The preferred interface is aio_timer_init or aio_timer_init_with_attrs. + * Use that unless you really need dynamic memory allocation. + * + * Returns: a pointer to the new timer + */ +static inline QEMUTimer *aio_timer_new_with_attrs(AioContext *ctx, + QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque) +{ + return timer_new_full(&ctx->tlg, type, scale, attributes, cb, opaque); +} + /** * aio_timer_new: * @ctx: the aio context @@ -396,10 +422,7 @@ struct LinuxAioState *aio_get_linux_aio(AioContext *ctx); * @opaque: the opaque pointer to pass to the callback * * Allocate a new timer attached to the context @ctx. - * The function is responsible for memory allocation. - * - * The preferred interface is aio_timer_init. Use that - * unless you really need dynamic memory allocation. + * See aio_timer_new_with_attrs for details. * * Returns: a pointer to the new timer */ @@ -407,7 +430,29 @@ static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type, int scale, QEMUTimerCB *cb, void *opaque) { - return timer_new_tl(ctx->tlg.tl[type], scale, cb, opaque); + return timer_new_full(&ctx->tlg, type, scale, 0, cb, opaque); +} + +/** + * aio_timer_init_with_attrs: + * @ctx: the aio context + * @ts: the timer + * @type: the clock type + * @scale: the scale + * @attributes: 0, or one to multiple OR'ed QEMU_TIMER_ATTR_ values + * to assign + * @cb: the callback to call on timer expiry + * @opaque: the opaque pointer to pass to the callback + * + * Initialise a new timer (with attributes) attached to the context @ctx. + * The caller is responsible for memory allocation. + */ +static inline void aio_timer_init_with_attrs(AioContext *ctx, + QEMUTimer *ts, QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque) +{ + timer_init_full(ts, &ctx->tlg, type, scale, attributes, cb, opaque); } /** @@ -420,14 +465,14 @@ static inline QEMUTimer *aio_timer_new(AioContext *ctx, QEMUClockType type, * @opaque: the opaque pointer to pass to the callback * * Initialise a new timer attached to the context @ctx. - * The caller is responsible for memory allocation. + * See aio_timer_init_with_attrs for details. */ static inline void aio_timer_init(AioContext *ctx, QEMUTimer *ts, QEMUClockType type, int scale, QEMUTimerCB *cb, void *opaque) { - timer_init_tl(ts, ctx->tlg.tl[type], scale, cb, opaque); + timer_init_full(ts, &ctx->tlg, type, scale, 0, cb, opaque); } /** diff --git a/include/block/block.h b/include/block/block.h index 4e0871aaf9..c7a26199aa 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -83,8 +83,13 @@ typedef enum { */ BDRV_REQ_SERIALISING = 0x80, + /* Execute the request only if the operation can be offloaded or otherwise + * be executed efficiently, but return an error instead of using a slow + * fallback. */ + BDRV_REQ_NO_FALLBACK = 0x100, + /* Mask of valid flags */ - BDRV_REQ_MASK = 0xff, + BDRV_REQ_MASK = 0x1ff, } BdrvRequestFlags; typedef struct BlockSizes { @@ -115,6 +120,7 @@ typedef struct HDGeometry { select an appropriate protocol driver, ignoring the format layer */ #define BDRV_O_NO_IO 0x10000 /* don't initialize for I/O */ +#define BDRV_O_AUTO_RDONLY 0x20000 /* degrade to read-only if opening read-write fails */ #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_NO_FLUSH) @@ -125,6 +131,7 @@ typedef struct HDGeometry { #define BDRV_OPT_CACHE_DIRECT "cache.direct" #define BDRV_OPT_CACHE_NO_FLUSH "cache.no-flush" #define BDRV_OPT_READ_ONLY "read-only" +#define BDRV_OPT_AUTO_READ_ONLY "auto-read-only" #define BDRV_OPT_DISCARD "discard" #define BDRV_OPT_FORCE_SHARE "force-share" @@ -184,6 +191,10 @@ typedef QSIMPLEQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; typedef struct BDRVReopenState { BlockDriverState *bs; int flags; + BlockdevDetectZeroesOptions detect_zeroes; + bool backing_missing; + bool replace_backing_bs; /* new_backing_bs is ignored if this is false */ + BlockDriverState *new_backing_bs; /* If NULL then detach the current bs */ uint64_t perm, shared_perm; QDict *options; QDict *explicit_options; @@ -296,10 +307,11 @@ BlockDriverState *bdrv_open(const char *filename, const char *reference, BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, int flags, Error **errp); BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, - QDict *options, int flags); -int bdrv_reopen_multiple(AioContext *ctx, BlockReopenQueue *bs_queue, Error **errp); -int bdrv_reopen(BlockDriverState *bs, int bdrv_flags, Error **errp); + BlockDriverState *bs, QDict *options, + bool keep_old_opts); +int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); +int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, + Error **errp); int bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, Error **errp); void bdrv_reopen_commit(BDRVReopenState *reopen_state); @@ -350,6 +362,11 @@ int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, BlockDriverState *bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); BlockDriverState *bdrv_find_base(BlockDriverState *bs); +bool bdrv_is_backing_chain_frozen(BlockDriverState *bs, BlockDriverState *base, + Error **errp); +int bdrv_freeze_backing_chain(BlockDriverState *bs, BlockDriverState *base, + Error **errp); +void bdrv_unfreeze_backing_chain(BlockDriverState *bs, BlockDriverState *base); typedef struct BdrvCheckResult { @@ -410,13 +427,9 @@ void bdrv_drain_all_begin(void); void bdrv_drain_all_end(void); void bdrv_drain_all(void); -/* Returns NULL when bs == NULL */ -AioWait *bdrv_get_aio_wait(BlockDriverState *bs); - #define BDRV_POLL_WHILE(bs, cond) ({ \ BlockDriverState *bs_ = (bs); \ - AIO_WAIT_WHILE(bdrv_get_aio_wait(bs_), \ - bdrv_get_aio_context(bs_), \ + AIO_WAIT_WHILE(bdrv_get_aio_context(bs_), \ cond); }) int bdrv_pdiscard(BdrvChild *child, int64_t offset, int bytes); @@ -439,7 +452,8 @@ int bdrv_is_allocated_above(BlockDriverState *top, BlockDriverState *base, bool bdrv_is_read_only(BlockDriverState *bs); int bdrv_can_set_read_only(BlockDriverState *bs, bool read_only, bool ignore_allow_rdw, Error **errp); -int bdrv_set_read_only(BlockDriverState *bs, bool read_only, Error **errp); +int bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, + Error **errp); bool bdrv_is_writable(BlockDriverState *bs); bool bdrv_is_sg(BlockDriverState *bs); bool bdrv_is_inserted(BlockDriverState *bs); @@ -448,6 +462,7 @@ void bdrv_eject(BlockDriverState *bs, bool eject_flag); const char *bdrv_get_format_name(BlockDriverState *bs); BlockDriverState *bdrv_find_node(const char *node_name); BlockDeviceInfoList *bdrv_named_nodes_list(Error **errp); +XDbgBlockGraph *bdrv_get_xdbg_block_graph(Error **errp); BlockDriverState *bdrv_lookup_bs(const char *device, const char *node_name, Error **errp); @@ -471,33 +486,30 @@ void bdrv_next_cleanup(BdrvNextIterator *it); BlockDriverState *bdrv_next_monitor_owned(BlockDriverState *bs); bool bdrv_is_encrypted(BlockDriverState *bs); void bdrv_iterate_format(void (*it)(void *opaque, const char *name), - void *opaque); + void *opaque, bool read_only); const char *bdrv_get_node_name(const BlockDriverState *bs); const char *bdrv_get_device_name(const BlockDriverState *bs); const char *bdrv_get_device_or_node_name(const BlockDriverState *bs); int bdrv_get_flags(BlockDriverState *bs); int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi); -ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs); +ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs, + Error **errp); void bdrv_round_to_clusters(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *cluster_offset, int64_t *cluster_bytes); -const char *bdrv_get_encrypted_filename(BlockDriverState *bs); void bdrv_get_backing_filename(BlockDriverState *bs, char *filename, int filename_size); -void bdrv_get_full_backing_filename(BlockDriverState *bs, - char *dest, size_t sz, Error **errp); -void bdrv_get_full_backing_filename_from_filename(const char *backed, - const char *backing, - char *dest, size_t sz, - Error **errp); +char *bdrv_get_full_backing_filename(BlockDriverState *bs, Error **errp); +char *bdrv_get_full_backing_filename_from_filename(const char *backed, + const char *backing, + Error **errp); +char *bdrv_dirname(BlockDriverState *bs, Error **errp); int path_has_protocol(const char *path); int path_is_absolute(const char *path); -void path_combine(char *dest, int dest_size, - const char *base_path, - const char *filename); +char *path_combine(const char *base_path, const char *filename); int bdrv_readv_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); int bdrv_writev_vmstate(BlockDriverState *bs, QEMUIOVector *qiov, int64_t pos); diff --git a/include/block/block_backup.h b/include/block/block_backup.h index 994a3bd2ec..157596c296 100644 --- a/include/block/block_backup.h +++ b/include/block/block_backup.h @@ -20,19 +20,6 @@ #include "block/block_int.h" -typedef struct CowRequest { - int64_t start_byte; - int64_t end_byte; - QLIST_ENTRY(CowRequest) list; - CoQueue wait_queue; /* coroutines blocked on this request */ -} CowRequest; - -void backup_wait_for_overlapping_requests(BlockJob *job, int64_t offset, - uint64_t bytes); -void backup_cow_request_begin(CowRequest *req, BlockJob *job, - int64_t offset, uint64_t bytes); -void backup_cow_request_end(CowRequest *req); - void backup_do_checkpoint(BlockJob *job, Error **errp); #endif diff --git a/include/block/block_int.h b/include/block/block_int.h index 903b9c1034..01e855a066 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -56,6 +56,8 @@ #define BLOCK_OPT_NOCOW "nocow" #define BLOCK_OPT_OBJECT_SIZE "object_size" #define BLOCK_OPT_REFCOUNT_BITS "refcount_bits" +#define BLOCK_OPT_DATA_FILE "data_file" +#define BLOCK_OPT_DATA_FILE_RAW "data_file_raw" #define BLOCK_PROBE_BUF_SIZE 512 @@ -139,7 +141,42 @@ struct BlockDriver { Error **errp); int (*bdrv_make_empty)(BlockDriverState *bs); - void (*bdrv_refresh_filename)(BlockDriverState *bs, QDict *options); + /* + * Refreshes the bs->exact_filename field. If that is impossible, + * bs->exact_filename has to be left empty. + */ + void (*bdrv_refresh_filename)(BlockDriverState *bs); + + /* + * Gathers the open options for all children into @target. + * A simple format driver (without backing file support) might + * implement this function like this: + * + * QINCREF(bs->file->bs->full_open_options); + * qdict_put(target, "file", bs->file->bs->full_open_options); + * + * If not specified, the generic implementation will simply put + * all children's options under their respective name. + * + * @backing_overridden is true when bs->backing seems not to be + * the child that would result from opening bs->backing_file. + * Therefore, if it is true, the backing child's options should be + * gathered; otherwise, there is no need since the backing child + * is the one implied by the image header. + * + * Note that ideally this function would not be needed. Every + * block driver which implements it is probably doing something + * shady regarding its runtime option structure. + */ + void (*bdrv_gather_child_options)(BlockDriverState *bs, QDict *target, + bool backing_overridden); + + /* + * Returns an allocated string which is the directory name of this BDS: It + * will be used to make relative filenames absolute by prepending this + * function's return value to them. + */ + char *(*bdrv_dirname)(BlockDriverState *bs, Error **errp); /* aio */ BlockAIOCB *(*bdrv_aio_preadv)(BlockDriverState *bs, @@ -319,7 +356,8 @@ struct BlockDriver { const char *name, Error **errp); int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi); - ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs); + ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs, + Error **errp); int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs, QEMUIOVector *qiov, @@ -345,6 +383,14 @@ struct BlockDriver { /* List of options for creating images, terminated by name == NULL */ QemuOptsList *create_opts; + /* + * If this driver supports reopening images this contains a + * NULL-terminated list of the runtime options that can be + * modified. If an option in this list is unspecified during + * reopen then it _must_ be reset to its default value or return + * an error. + */ + const char *const *mutable_opts; /* * Returns 0 for completed check, -errno for internal errors. @@ -509,6 +555,13 @@ struct BlockDriver { void (*bdrv_register_buf)(BlockDriverState *bs, void *host, size_t size); void (*bdrv_unregister_buf)(BlockDriverState *bs, void *host); QLIST_ENTRY(BlockDriver) list; + + /* Pointer to a NULL-terminated array of names of strong options + * that can be specified for bdrv_open(). A strong option is one + * that changes the data of a BDS. + * If this pointer is NULL, the array is considered empty. + * "filename" and "driver" are always considered strong. */ + const char *const *strong_runtime_opts; }; typedef struct BlockLimits { @@ -661,6 +714,17 @@ struct BdrvChild { */ uint64_t shared_perm; + /* backup of permissions during permission update procedure */ + bool has_backup_perm; + uint64_t backup_perm; + uint64_t backup_shared_perm; + + /* + * This link is frozen: the child can neither be replaced nor + * detached from the parent. + */ + bool frozen; + QLIST_ENTRY(BdrvChild) next; QLIST_ENTRY(BdrvChild) next_parent; }; @@ -696,6 +760,10 @@ struct BlockDriverState { char filename[PATH_MAX]; char backing_file[PATH_MAX]; /* if non zero, the image is a diff of this file image */ + /* The backing filename indicated by the image header; if we ever + * open this file, then this is replaced by the resulting BDS's + * filename (i.e. after a bdrv_refresh_filename() run). */ + char auto_backing_file[PATH_MAX]; char backing_format[16]; /* if non-zero and backing_file exists */ QDict *full_open_options; @@ -794,9 +862,6 @@ struct BlockDriverState { unsigned int in_flight; unsigned int serialising_in_flight; - /* Kicked to signal main loop when a request completes. */ - AioWait wait; - /* counter for nested bdrv_io_plug. * Accessed with atomic ops. */ @@ -958,6 +1023,8 @@ int is_windows_drive(const char *filename); * flatten the whole backing file chain onto @bs. * @backing_file_str: The file name that will be written to @bs as the * the new backing file if the job completes. Ignored if @base is %NULL. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags * @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @on_error: The action to take upon error. * @errp: Error object. @@ -971,7 +1038,8 @@ int is_windows_drive(const char *filename); */ void stream_start(const char *job_id, BlockDriverState *bs, BlockDriverState *base, const char *backing_file_str, - int64_t speed, BlockdevOnError on_error, Error **errp); + int creation_flags, int64_t speed, + BlockdevOnError on_error, Error **errp); /** * commit_start: @@ -980,6 +1048,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, * @bs: Active block device. * @top: Top block device to be committed. * @base: Block device that will be written into, and become the new top. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags * @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @on_error: The action to take upon error. * @backing_file_str: String to use as the backing file in @top's overlay @@ -990,7 +1060,8 @@ void stream_start(const char *job_id, BlockDriverState *bs, * */ void commit_start(const char *job_id, BlockDriverState *bs, - BlockDriverState *base, BlockDriverState *top, int64_t speed, + BlockDriverState *base, BlockDriverState *top, + int creation_flags, int64_t speed, BlockdevOnError on_error, const char *backing_file_str, const char *filter_node_name, Error **errp); /** @@ -1026,6 +1097,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, * @target: Block device to write to. * @replaces: Block graph node name to replace once the mirror is done. Can * only be used when full mirroring is selected. + * @creation_flags: Flags that control the behavior of the Job lifetime. + * See @BlockJobCreateFlags * @speed: The maximum speed, in bytes per second, or 0 for unlimited. * @granularity: The chosen granularity for the dirty bitmap. * @buf_size: The amount of data that can be in flight at one time. @@ -1047,7 +1120,8 @@ void commit_active_start(const char *job_id, BlockDriverState *bs, */ void mirror_start(const char *job_id, BlockDriverState *bs, BlockDriverState *target, const char *replaces, - int64_t speed, uint32_t granularity, int64_t buf_size, + int creation_flags, int64_t speed, + uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, BlockdevOnError on_source_error, BlockdevOnError on_target_error, @@ -1149,7 +1223,7 @@ bool blk_dev_is_medium_locked(BlockBackend *blk); void bdrv_set_dirty(BlockDriverState *bs, int64_t offset, int64_t bytes); void bdrv_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap **out); -void bdrv_undo_clear_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *in); +void bdrv_restore_dirty_bitmap(BdrvDirtyBitmap *bitmap, HBitmap *backup); void bdrv_inc_in_flight(BlockDriverState *bs); void bdrv_dec_in_flight(BlockDriverState *bs); diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 32c00b7dc0..ede0bd8dcb 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -70,6 +70,9 @@ typedef struct BlockJob { /** Called when the job transitions to READY */ Notifier ready_notifier; + /** Called when the job coroutine yields or terminates */ + Notifier idle_notifier; + /** BlockDriverStates that are involved in this block job */ GSList *nodes; } BlockJob; diff --git a/include/block/dirty-bitmap.h b/include/block/dirty-bitmap.h index 259bd27c40..8044ace63e 100644 --- a/include/block/dirty-bitmap.h +++ b/include/block/dirty-bitmap.h @@ -5,6 +5,16 @@ #include "qapi/qapi-types-block-core.h" #include "qemu/hbitmap.h" +typedef enum BitmapCheckFlags { + BDRV_BITMAP_BUSY = 1, + BDRV_BITMAP_RO = 2, + BDRV_BITMAP_INCONSISTENT = 4, +} BitmapCheckFlags; + +#define BDRV_BITMAP_DEFAULT (BDRV_BITMAP_BUSY | BDRV_BITMAP_RO | \ + BDRV_BITMAP_INCONSISTENT) +#define BDRV_BITMAP_ALLOW_RO (BDRV_BITMAP_BUSY | BDRV_BITMAP_INCONSISTENT) + BdrvDirtyBitmap *bdrv_create_dirty_bitmap(BlockDriverState *bs, uint32_t granularity, const char *name, @@ -24,9 +34,10 @@ BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap(BlockDriverState *bs, void bdrv_dirty_bitmap_enable_successor(BdrvDirtyBitmap *bitmap); BdrvDirtyBitmap *bdrv_find_dirty_bitmap(BlockDriverState *bs, const char *name); +int bdrv_dirty_bitmap_check(const BdrvDirtyBitmap *bitmap, uint32_t flags, + Error **errp); void bdrv_release_dirty_bitmap(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); void bdrv_release_named_dirty_bitmaps(BlockDriverState *bs); -void bdrv_release_persistent_dirty_bitmaps(BlockDriverState *bs); void bdrv_remove_persistent_dirty_bitmap(BlockDriverState *bs, const char *name, Error **errp); @@ -37,7 +48,7 @@ BlockDirtyInfoList *bdrv_query_dirty_bitmaps(BlockDriverState *bs); uint32_t bdrv_get_default_bitmap_granularity(BlockDriverState *bs); uint32_t bdrv_dirty_bitmap_granularity(const BdrvDirtyBitmap *bitmap); bool bdrv_dirty_bitmap_enabled(BdrvDirtyBitmap *bitmap); -bool bdrv_dirty_bitmap_frozen(BdrvDirtyBitmap *bitmap); +bool bdrv_dirty_bitmap_has_successor(BdrvDirtyBitmap *bitmap); const char *bdrv_dirty_bitmap_name(const BdrvDirtyBitmap *bitmap); int64_t bdrv_dirty_bitmap_size(const BdrvDirtyBitmap *bitmap); DirtyBitmapStatus bdrv_dirty_bitmap_status(BdrvDirtyBitmap *bitmap); @@ -67,11 +78,13 @@ void bdrv_dirty_bitmap_deserialize_ones(BdrvDirtyBitmap *bitmap, void bdrv_dirty_bitmap_deserialize_finish(BdrvDirtyBitmap *bitmap); void bdrv_dirty_bitmap_set_readonly(BdrvDirtyBitmap *bitmap, bool value); -void bdrv_dirty_bitmap_set_persistance(BdrvDirtyBitmap *bitmap, +void bdrv_dirty_bitmap_set_persistence(BdrvDirtyBitmap *bitmap, bool persistent); -void bdrv_dirty_bitmap_set_qmp_locked(BdrvDirtyBitmap *bitmap, bool qmp_locked); +void bdrv_dirty_bitmap_set_inconsistent(BdrvDirtyBitmap *bitmap); +void bdrv_dirty_bitmap_set_busy(BdrvDirtyBitmap *bitmap, bool busy); void bdrv_merge_dirty_bitmap(BdrvDirtyBitmap *dest, const BdrvDirtyBitmap *src, - Error **errp); + HBitmap **backup, Error **errp); +void bdrv_dirty_bitmap_set_migration(BdrvDirtyBitmap *bitmap, bool migration); /* Functions that require manual locking. */ void bdrv_dirty_bitmap_lock(BdrvDirtyBitmap *bitmap); @@ -83,8 +96,6 @@ void bdrv_set_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, void bdrv_reset_dirty_bitmap_locked(BdrvDirtyBitmap *bitmap, int64_t offset, int64_t bytes); int64_t bdrv_dirty_iter_next(BdrvDirtyBitmapIter *iter); -bool bdrv_dirty_iter_next_area(BdrvDirtyBitmapIter *iter, uint64_t max_offset, - uint64_t *offset, int *bytes); void bdrv_set_dirty_iter(BdrvDirtyBitmapIter *hbi, int64_t offset); int64_t bdrv_get_dirty_count(BdrvDirtyBitmap *bitmap); int64_t bdrv_get_meta_dirty_count(BdrvDirtyBitmap *bitmap); @@ -92,13 +103,16 @@ void bdrv_dirty_bitmap_truncate(BlockDriverState *bs, int64_t bytes); bool bdrv_dirty_bitmap_readonly(const BdrvDirtyBitmap *bitmap); bool bdrv_has_readonly_bitmaps(BlockDriverState *bs); bool bdrv_dirty_bitmap_get_autoload(const BdrvDirtyBitmap *bitmap); -bool bdrv_dirty_bitmap_get_persistance(BdrvDirtyBitmap *bitmap); -bool bdrv_dirty_bitmap_qmp_locked(BdrvDirtyBitmap *bitmap); +bool bdrv_dirty_bitmap_get_persistence(BdrvDirtyBitmap *bitmap); +bool bdrv_dirty_bitmap_inconsistent(const BdrvDirtyBitmap *bitmap); bool bdrv_has_changed_persistent_bitmaps(BlockDriverState *bs); BdrvDirtyBitmap *bdrv_dirty_bitmap_next(BlockDriverState *bs, BdrvDirtyBitmap *bitmap); char *bdrv_dirty_bitmap_sha256(const BdrvDirtyBitmap *bitmap, Error **errp); -int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t start); +int64_t bdrv_dirty_bitmap_next_zero(BdrvDirtyBitmap *bitmap, uint64_t offset, + uint64_t bytes); +bool bdrv_dirty_bitmap_next_dirty_area(BdrvDirtyBitmap *bitmap, + uint64_t *offset, uint64_t *bytes); BdrvDirtyBitmap *bdrv_reclaim_dirty_bitmap_locked(BlockDriverState *bs, BdrvDirtyBitmap *bitmap, Error **errp); diff --git a/include/block/nbd.h b/include/block/nbd.h index 4638c839f5..6d05983a55 100644 --- a/include/block/nbd.h +++ b/include/block/nbd.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2016-2017 Red Hat, Inc. + * Copyright (C) 2016-2019 Red Hat, Inc. * Copyright (C) 2005 Anthony Liguori * * Network Block Device @@ -23,6 +23,7 @@ #include "qapi/qapi-types-block.h" #include "io/channel-socket.h" #include "crypto/tlscreds.h" +#include "qapi/error.h" /* Handshake phase structs - this struct is passed on the wire */ @@ -135,7 +136,9 @@ typedef struct NBDExtent { #define NBD_FLAG_SEND_TRIM (1 << 5) /* Send TRIM (discard) */ #define NBD_FLAG_SEND_WRITE_ZEROES (1 << 6) /* Send WRITE_ZEROES */ #define NBD_FLAG_SEND_DF (1 << 7) /* Send DF (Do not Fragment) */ -#define NBD_FLAG_SEND_CACHE (1 << 8) /* Send CACHE (prefetch) */ +#define NBD_FLAG_CAN_MULTI_CONN (1 << 8) /* Multi-client cache consistent */ +#define NBD_FLAG_SEND_RESIZE (1 << 9) /* Send resize */ +#define NBD_FLAG_SEND_CACHE (1 << 10) /* Send CACHE (prefetch) */ /* New-style handshake (global) flags, sent from server to client, and control what will happen during handshake phase. */ @@ -261,30 +264,44 @@ struct NBDExportInfo { bool request_sizes; char *x_dirty_bitmap; + /* Set by client before nbd_receive_negotiate(), or by server results + * during nbd_receive_export_list() */ + char *name; /* must be non-NULL */ + /* In-out fields, set by client before nbd_receive_negotiate() and * updated by server results during nbd_receive_negotiate() */ bool structured_reply; bool base_allocation; /* base:allocation context for NBD_CMD_BLOCK_STATUS */ - /* Set by server results during nbd_receive_negotiate() */ + /* Set by server results during nbd_receive_negotiate() and + * nbd_receive_export_list() */ uint64_t size; uint16_t flags; uint32_t min_block; uint32_t opt_block; uint32_t max_block; - uint32_t meta_base_allocation_id; + uint32_t context_id; + + /* Set by server results during nbd_receive_export_list() */ + char *description; + int n_contexts; + char **contexts; }; typedef struct NBDExportInfo NBDExportInfo; -int nbd_receive_negotiate(QIOChannel *ioc, const char *name, - QCryptoTLSCreds *tlscreds, const char *hostname, - QIOChannel **outioc, NBDExportInfo *info, - Error **errp); +int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + const char *hostname, QIOChannel **outioc, + NBDExportInfo *info, Error **errp); +void nbd_free_export_list(NBDExportInfo *info, int count); +int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + const char *hostname, NBDExportInfo **info, + Error **errp); int nbd_init(int fd, QIOChannelSocket *sioc, NBDExportInfo *info, Error **errp); int nbd_send_request(QIOChannel *ioc, NBDRequest *request); -int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp); +int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, + NBDReply *reply, Error **errp); int nbd_client(int fd); int nbd_disconnect(int fd); int nbd_errno_to_system_errno(int err); @@ -292,10 +309,11 @@ int nbd_errno_to_system_errno(int err); typedef struct NBDExport NBDExport; typedef struct NBDClient NBDClient; -NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, - uint16_t nbdflags, void (*close)(NBDExport *), - bool writethrough, BlockBackend *on_eject_blk, - Error **errp); +NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + uint64_t size, const char *name, const char *desc, + const char *bitmap, uint16_t nbdflags, + void (*close)(NBDExport *), bool writethrough, + BlockBackend *on_eject_blk, Error **errp); void nbd_export_close(NBDExport *exp); void nbd_export_remove(NBDExport *exp, NbdServerRemoveMode mode, Error **errp); void nbd_export_get(NBDExport *exp); @@ -304,33 +322,54 @@ void nbd_export_put(NBDExport *exp); BlockBackend *nbd_export_get_blockdev(NBDExport *exp); NBDExport *nbd_export_find(const char *name); -void nbd_export_set_name(NBDExport *exp, const char *name); -void nbd_export_set_description(NBDExport *exp, const char *description); void nbd_export_close_all(void); -void nbd_client_new(NBDExport *exp, - QIOChannelSocket *sioc, +void nbd_client_new(QIOChannelSocket *sioc, QCryptoTLSCreds *tlscreds, - const char *tlsaclname, + const char *tlsauthz, void (*close_fn)(NBDClient *, bool)); void nbd_client_get(NBDClient *client); void nbd_client_put(NBDClient *client); void nbd_server_start(SocketAddress *addr, const char *tls_creds, - Error **errp); - -void nbd_export_bitmap(NBDExport *exp, const char *bitmap, - const char *bitmap_export_name, Error **errp); + const char *tls_authz, Error **errp); /* nbd_read * Reads @size bytes from @ioc. Returns 0 on success. */ static inline int nbd_read(QIOChannel *ioc, void *buffer, size_t size, - Error **errp) + const char *desc, Error **errp) { - return qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; + int ret = qio_channel_read_all(ioc, buffer, size, errp) < 0 ? -EIO : 0; + + if (ret < 0) { + if (desc) { + error_prepend(errp, "Failed to read %s: ", desc); + } + return -1; + } + + return 0; } +#define DEF_NBD_READ_N(bits) \ +static inline int nbd_read##bits(QIOChannel *ioc, \ + uint##bits##_t *val, \ + const char *desc, Error **errp) \ +{ \ + if (nbd_read(ioc, val, sizeof(*val), desc, errp) < 0) { \ + return -1; \ + } \ + *val = be##bits##_to_cpu(*val); \ + return 0; \ +} + +DEF_NBD_READ_N(16) /* Defines nbd_read16(). */ +DEF_NBD_READ_N(32) /* Defines nbd_read32(). */ +DEF_NBD_READ_N(64) /* Defines nbd_read64(). */ + +#undef DEF_NBD_READ_N + static inline bool nbd_reply_is_simple(NBDReply *reply) { return reply->magic == NBD_SIMPLE_REPLY_MAGIC; @@ -342,5 +381,10 @@ static inline bool nbd_reply_is_structured(NBDReply *reply) } const char *nbd_reply_type_lookup(uint16_t type); +const char *nbd_opt_lookup(uint32_t opt); +const char *nbd_rep_lookup(uint32_t rep); +const char *nbd_info_lookup(uint16_t info); +const char *nbd_cmd_lookup(uint16_t info); +const char *nbd_err_lookup(int err); #endif diff --git a/include/block/raw-aio.h b/include/block/raw-aio.h index 6799614e56..ba223dd1f1 100644 --- a/include/block/raw-aio.h +++ b/include/block/raw-aio.h @@ -40,6 +40,7 @@ /* AIO flags */ #define QEMU_AIO_MISALIGNED 0x1000 #define QEMU_AIO_BLKDEV 0x2000 +#define QEMU_AIO_NO_FALLBACK 0x4000 /* linux-aio.c - Linux native implementation */ diff --git a/include/block/snapshot.h b/include/block/snapshot.h index f73d1094af..b5d5084a12 100644 --- a/include/block/snapshot.h +++ b/include/block/snapshot.h @@ -61,9 +61,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs, const char *snapshot_id, const char *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, diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h index e2fd0513c4..712a8e64b4 100644 --- a/include/block/throttle-groups.h +++ b/include/block/throttle-groups.h @@ -43,6 +43,11 @@ typedef struct ThrottleGroupMember { */ unsigned int io_limits_disabled; + /* Number of pending throttle_group_restart_queue_entry() coroutines. + * Accessed with atomic operations. + */ + unsigned int restart_pending; + /* The following fields are protected by the ThrottleGroup lock. * See the ThrottleGroup documentation for details. * throttle_state tells us if I/O limits are configured. */ diff --git a/include/chardev/char-fe.h b/include/chardev/char-fe.h index 71cd069478..aa1b864ccd 100644 --- a/include/chardev/char-fe.h +++ b/include/chardev/char-fe.h @@ -20,7 +20,7 @@ struct CharBackend { }; /** - * @qemu_chr_fe_init: + * qemu_chr_fe_init: * * Initializes a front end for the given CharBackend and * Chardev. Call qemu_chr_fe_deinit() to remove the association and @@ -31,7 +31,7 @@ struct CharBackend { bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp); /** - * @qemu_chr_fe_deinit: + * qemu_chr_fe_deinit: * @b: a CharBackend * @del: if true, delete the chardev backend * @@ -42,9 +42,9 @@ bool qemu_chr_fe_init(CharBackend *b, Chardev *s, Error **errp); void qemu_chr_fe_deinit(CharBackend *b, bool del); /** - * @qemu_chr_fe_get_driver: + * qemu_chr_fe_get_driver: * - * Returns the driver associated with a CharBackend or NULL if no + * Returns: the driver associated with a CharBackend or NULL if no * associated Chardev. * Note: avoid this function as the driver should never be accessed directly, * especially by the frontends that support chardevice hotswap. @@ -53,21 +53,21 @@ void qemu_chr_fe_deinit(CharBackend *b, bool del); Chardev *qemu_chr_fe_get_driver(CharBackend *be); /** - * @qemu_chr_fe_backend_connected: + * qemu_chr_fe_backend_connected: * - * Returns true if there is a chardevice associated with @be. + * Returns: true if there is a chardevice associated with @be. */ bool qemu_chr_fe_backend_connected(CharBackend *be); /** - * @qemu_chr_fe_backend_open: + * qemu_chr_fe_backend_open: * - * Returns true if chardevice associated with @be is open. + * Returns: true if chardevice associated with @be is open. */ bool qemu_chr_fe_backend_open(CharBackend *be); /** - * @qemu_chr_fe_set_handlers: + * qemu_chr_fe_set_handlers_full: * @b: a CharBackend * @fd_can_read: callback to get the amount of data the frontend may * receive @@ -79,12 +79,28 @@ bool qemu_chr_fe_backend_open(CharBackend *be); * @context: a main loop context or NULL for the default * @set_open: whether to call qemu_chr_fe_set_open() implicitely when * any of the handler is non-NULL + * @sync_state: whether to issue event callback with updated state * * Set the front end char handlers. The front end takes the focus if * any of the handler is non-NULL. * * Without associated Chardev, nothing is changed. */ +void qemu_chr_fe_set_handlers_full(CharBackend *b, + IOCanReadHandler *fd_can_read, + IOReadHandler *fd_read, + IOEventHandler *fd_event, + BackendChangeHandler *be_change, + void *opaque, + GMainContext *context, + bool set_open, + bool sync_state); + +/** + * qemu_chr_fe_set_handlers: + * + * Version of qemu_chr_fe_set_handlers_full() with sync_state = true. + */ void qemu_chr_fe_set_handlers(CharBackend *b, IOCanReadHandler *fd_can_read, IOReadHandler *fd_read, @@ -95,7 +111,7 @@ void qemu_chr_fe_set_handlers(CharBackend *b, bool set_open); /** - * @qemu_chr_fe_take_focus: + * qemu_chr_fe_take_focus: * * Take the focus (if the front end is muxed). * @@ -104,42 +120,41 @@ void qemu_chr_fe_set_handlers(CharBackend *b, void qemu_chr_fe_take_focus(CharBackend *b); /** - * @qemu_chr_fe_accept_input: + * qemu_chr_fe_accept_input: * * Notify that the frontend is ready to receive data */ void qemu_chr_fe_accept_input(CharBackend *be); /** - * @qemu_chr_fe_disconnect: + * qemu_chr_fe_disconnect: * - * Close a fd accpeted by character backend. + * Close a fd accepted by character backend. * Without associated Chardev, do nothing. */ void qemu_chr_fe_disconnect(CharBackend *be); /** - * @qemu_chr_fe_wait_connected: + * qemu_chr_fe_wait_connected: * * Wait for characted backend to be connected, return < 0 on error or - * if no assicated Chardev. + * if no associated Chardev. */ int qemu_chr_fe_wait_connected(CharBackend *be, Error **errp); /** - * @qemu_chr_fe_set_echo: + * qemu_chr_fe_set_echo: + * @echo: true to enable echo, false to disable echo * * Ask the backend to override its normal echo setting. This only really * applies to the stdio backend and is used by the QMP server such that you * can see what you type if you try to type QMP commands. * Without associated Chardev, do nothing. - * - * @echo true to enable echo, false to disable echo */ void qemu_chr_fe_set_echo(CharBackend *be, bool echo); /** - * @qemu_chr_fe_set_open: + * qemu_chr_fe_set_open: * * Set character frontend open status. This is an indication that the * front end is ready (or not) to begin doing I/O. @@ -148,28 +163,29 @@ void qemu_chr_fe_set_echo(CharBackend *be, bool echo); void qemu_chr_fe_set_open(CharBackend *be, int fe_open); /** - * @qemu_chr_fe_printf: + * qemu_chr_fe_printf: + * @fmt: see #printf * * Write to a character backend using a printf style interface. This * function is thread-safe. It does nothing without associated * Chardev. - * - * @fmt see #printf */ void qemu_chr_fe_printf(CharBackend *be, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** - * @qemu_chr_fe_add_watch: + * qemu_chr_fe_add_watch: + * @cond: the condition to poll for + * @func: the function to call when the condition happens + * @user_data: the opaque pointer to pass to @func * * If the backend is connected, create and add a #GSource that fires * when the given condition (typically G_IO_OUT|G_IO_HUP or G_IO_HUP) * is active; return the #GSource's tag. If it is disconnected, * or without associated Chardev, return 0. * - * @cond the condition to poll for - * @func the function to call when the condition happens - * @user_data the opaque pointer to pass to @func + * Note that you are responsible to update the front-end sources if + * you are switching the main context with qemu_chr_fe_set_handlers(). * * Returns: the source tag */ @@ -177,54 +193,50 @@ guint qemu_chr_fe_add_watch(CharBackend *be, GIOCondition cond, GIOFunc func, void *user_data); /** - * @qemu_chr_fe_write: + * qemu_chr_fe_write: + * @buf: the data + * @len: the number of bytes to send * * Write data to a character backend from the front end. This function * will send data from the front end to the back end. This function * is thread-safe. * - * @buf the data - * @len the number of bytes to send - * - * Returns: the number of bytes consumed (0 if no assicated Chardev) + * Returns: the number of bytes consumed (0 if no associated Chardev) */ int qemu_chr_fe_write(CharBackend *be, const uint8_t *buf, int len); /** - * @qemu_chr_fe_write_all: + * qemu_chr_fe_write_all: + * @buf: the data + * @len: the number of bytes to send * * Write data to a character backend from the front end. This function will * send data from the front end to the back end. Unlike @qemu_chr_fe_write, * this function will block if the back end cannot consume all of the data * attempted to be written. This function is thread-safe. * - * @buf the data - * @len the number of bytes to send - * - * Returns: the number of bytes consumed (0 if no assicated Chardev) + * Returns: the number of bytes consumed (0 if no associated Chardev) */ int qemu_chr_fe_write_all(CharBackend *be, const uint8_t *buf, int len); /** - * @qemu_chr_fe_read_all: + * qemu_chr_fe_read_all: + * @buf: the data buffer + * @len: the number of bytes to read * * Read data to a buffer from the back end. * - * @buf the data buffer - * @len the number of bytes to read - * - * Returns: the number of bytes read (0 if no assicated Chardev) + * Returns: the number of bytes read (0 if no associated Chardev) */ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len); /** - * @qemu_chr_fe_ioctl: + * qemu_chr_fe_ioctl: + * @cmd: see CHR_IOCTL_* + * @arg: the data associated with @cmd * * Issue a device specific ioctl to a backend. This function is thread-safe. * - * @cmd see CHR_IOCTL_* - * @arg the data associated with @cmd - * * Returns: if @cmd is not supported by the backend or there is no * associated Chardev, -ENOTSUP, otherwise the return * value depends on the semantics of @cmd @@ -232,7 +244,7 @@ int qemu_chr_fe_read_all(CharBackend *be, uint8_t *buf, int len); int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg); /** - * @qemu_chr_fe_get_msgfd: + * qemu_chr_fe_get_msgfd: * * For backends capable of fd passing, return the latest file descriptor passed * by a client. @@ -245,7 +257,7 @@ int qemu_chr_fe_ioctl(CharBackend *be, int cmd, void *arg); int qemu_chr_fe_get_msgfd(CharBackend *be); /** - * @qemu_chr_fe_get_msgfds: + * qemu_chr_fe_get_msgfds: * * For backends capable of fd passing, return the number of file received * descriptors and fills the fds array up to num elements @@ -258,7 +270,7 @@ int qemu_chr_fe_get_msgfd(CharBackend *be); int qemu_chr_fe_get_msgfds(CharBackend *be, int *fds, int num); /** - * @qemu_chr_fe_set_msgfds: + * qemu_chr_fe_set_msgfds: * * For backends capable of fd passing, set an array of fds to be passed with * the next send operation. diff --git a/include/chardev/char-mux.h b/include/chardev/char-mux.h index 1e13187767..572cefd517 100644 --- a/include/chardev/char-mux.h +++ b/include/chardev/char-mux.h @@ -55,7 +55,6 @@ typedef struct MuxChardev { #define CHARDEV_IS_MUX(chr) \ object_dynamic_cast(OBJECT(chr), TYPE_CHARDEV_MUX) -void mux_chr_set_handlers(Chardev *chr, GMainContext *context); void mux_set_focus(Chardev *chr, int focus); void mux_chr_send_all_event(Chardev *chr, int event); diff --git a/include/chardev/char.h b/include/chardev/char.h index 6f0576e214..c0b57f7685 100644 --- a/include/chardev/char.h +++ b/include/chardev/char.h @@ -47,6 +47,9 @@ typedef enum { QEMU_CHAR_FEATURE_FD_PASS, /* Whether replay or record mode is enabled */ QEMU_CHAR_FEATURE_REPLAY, + /* Whether the gcontext can be changed after calling + * qemu_chr_be_update_read_handlers() */ + QEMU_CHAR_FEATURE_GCONTEXT, QEMU_CHAR_FEATURE_LAST, } ChardevFeature; @@ -68,31 +71,31 @@ struct Chardev { }; /** - * @qemu_chr_new_from_opts: + * qemu_chr_new_from_opts: + * @opts: see qemu-config.c for a list of valid options + * @context: the #GMainContext to be used at initialization time * * Create a new character backend from a QemuOpts list. * - * @opts see qemu-config.c for a list of valid options - * * Returns: on success: a new character backend * otherwise: NULL; @errp specifies the error * or left untouched in case of help option */ Chardev *qemu_chr_new_from_opts(QemuOpts *opts, + GMainContext *context, Error **errp); /** - * @qemu_chr_parse_common: + * qemu_chr_parse_common: + * @opts: the options that still need parsing + * @backend: a new backend * * 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_parse_opts: + * qemu_chr_parse_opts: * * Parse the options to the ChardevBackend struct. * @@ -102,49 +105,66 @@ ChardevBackend *qemu_chr_parse_opts(QemuOpts *opts, Error **errp); /** - * @qemu_chr_new: + * qemu_chr_new: + * @label: the name of the backend + * @filename: the URI + * @context: the #GMainContext to be used at initialization time * * Create a new character backend from a URI. - * - * @label the name of the backend - * @filename the URI + * Do not implicitly initialize a monitor if the chardev is muxed. * * Returns: a new character backend */ -Chardev *qemu_chr_new(const char *label, const char *filename); +Chardev *qemu_chr_new(const char *label, const char *filename, + GMainContext *context); /** - * @qemu_chr_change: + * qemu_chr_new_mux_mon: + * @label: the name of the backend + * @filename: the URI + * @context: the #GMainContext to be used at initialization time + * + * Create a new character backend from a URI. + * Implicitly initialize a monitor if the chardev is muxed. + * + * Returns: a new character backend + */ +Chardev *qemu_chr_new_mux_mon(const char *label, const char *filename, + GMainContext *context); + +/** +* qemu_chr_change: +* @opts: the new backend options * * Change an existing character backend - * - * @opts the new backend options */ void qemu_chr_change(QemuOpts *opts, Error **errp); /** - * @qemu_chr_cleanup: + * qemu_chr_cleanup: * * Delete all chardevs (when leaving qemu) */ void qemu_chr_cleanup(void); /** - * @qemu_chr_new_noreplay: + * qemu_chr_new_noreplay: + * @label: the name of the backend + * @filename: the URI + * @permit_mux_mon: if chardev is muxed, initialize a monitor + * @context: the #GMainContext to be used at initialization time * * 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 - * * Returns: a new character backend */ -Chardev *qemu_chr_new_noreplay(const char *label, const char *filename); +Chardev *qemu_chr_new_noreplay(const char *label, const char *filename, + bool permit_mux_mon, GMainContext *context); /** - * @qemu_chr_be_can_write: + * qemu_chr_be_can_write: * * Determine how much data the front end can currently accept. This function * returns the number of bytes the front end can accept. If it returns 0, the @@ -156,43 +176,39 @@ Chardev *qemu_chr_new_noreplay(const char *label, const char *filename); int qemu_chr_be_can_write(Chardev *s); /** - * @qemu_chr_be_write: + * qemu_chr_be_write: + * @buf: a buffer to receive data from the front end + * @len: the number of bytes to receive from the front end * * Write data from the back end to the front end. Before issuing this call, * the caller should call @qemu_chr_be_can_write to determine how much data * the front end can currently accept. - * - * @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(Chardev *s, uint8_t *buf, int len); /** - * @qemu_chr_be_write_impl: + * qemu_chr_be_write_impl: + * @buf: a buffer to receive data from the front end + * @len: the number of bytes to receive from the front end * * 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(Chardev *s, uint8_t *buf, int len); /** - * @qemu_chr_be_update_read_handlers: + * qemu_chr_be_update_read_handlers: + * @context: the gcontext that will be used to attach the watch sources * * Invoked when frontend read handlers are setup - * - * @context the gcontext that will be used to attach the watch sources */ void qemu_chr_be_update_read_handlers(Chardev *s, GMainContext *context); /** - * @qemu_chr_be_event: + * qemu_chr_be_event: + * @event: the event to send * * Send an event from the back end to the front end. - * - * @event the event to send */ void qemu_chr_be_event(Chardev *s, int event); @@ -203,7 +219,8 @@ bool qemu_chr_has_feature(Chardev *chr, ChardevFeature feature); void qemu_chr_set_feature(Chardev *chr, ChardevFeature feature); -QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename); +QemuOpts *qemu_chr_parse_compat(const char *label, const char *filename, + bool permit_mux_mon); int qemu_chr_write(Chardev *s, const uint8_t *buf, int len, bool write_all); #define qemu_chr_write_all(s, buf, len) qemu_chr_write(s, buf, len, true) int qemu_chr_wait_connected(Chardev *chr, Error **errp); @@ -262,7 +279,8 @@ typedef struct ChardevClass { } ChardevClass; Chardev *qemu_chardev_new(const char *id, const char *typename, - ChardevBackend *backend, Error **errp); + ChardevBackend *backend, GMainContext *context, + Error **errp); extern int term_escape_char; diff --git a/include/chardev/spice.h b/include/chardev/spice.h new file mode 100644 index 0000000000..6431da3205 --- /dev/null +++ b/include/chardev/spice.h @@ -0,0 +1,27 @@ +#ifndef CHARDEV_SPICE_H_ +#define CHARDEV_SPICE_H_ + +#include +#include "chardev/char-fe.h" + +typedef struct SpiceChardev { + Chardev parent; + + SpiceCharDeviceInstance sin; + bool active; + bool blocked; + const uint8_t *datapos; + int datalen; + QLIST_ENTRY(SpiceChardev) next; +} SpiceChardev; + +#define TYPE_CHARDEV_SPICE "chardev-spice" +#define TYPE_CHARDEV_SPICEVMC "chardev-spicevmc" +#define TYPE_CHARDEV_SPICEPORT "chardev-spiceport" + +#define SPICE_CHARDEV(obj) OBJECT_CHECK(SpiceChardev, (obj), TYPE_CHARDEV_SPICE) + +void qemu_chr_open_spice_port(Chardev *chr, ChardevBackend *backend, + bool *be_opened, Error **errp); + +#endif diff --git a/include/crypto/afsplit.h b/include/crypto/afsplit.h index 7dd21f0a67..4894d64330 100644 --- a/include/crypto/afsplit.h +++ b/include/crypto/afsplit.h @@ -3,19 +3,18 @@ * * Copyright (c) 2015-2016 Red Hat, Inc. * - * This library is free software; you can redistribute it and/or + * 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 library is distributed in the hope that it will be useful, + * 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 - * 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 . + * 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 QCRYPTO_AFSPLIT_H diff --git a/include/crypto/block.h b/include/crypto/block.h index cd18f46d56..e729d5bd66 100644 --- a/include/crypto/block.h +++ b/include/crypto/block.h @@ -75,6 +75,7 @@ typedef enum { * @readfunc: callback for reading data from the volume * @opaque: data to pass to @readfunc * @flags: bitmask of QCryptoBlockOpenFlags values + * @n_threads: allow concurrent I/O from up to @n_threads threads * @errp: pointer to a NULL-initialized error object * * Create a new block encryption object for an existing @@ -107,6 +108,7 @@ QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options, QCryptoBlockReadFunc readfunc, void *opaque, unsigned int flags, + size_t n_threads, Error **errp); /** diff --git a/include/disas/bfd.h b/include/disas/bfd.h index 1f69a6e9d3..41b61c85f9 100644 --- a/include/disas/bfd.h +++ b/include/disas/bfd.h @@ -387,6 +387,7 @@ typedef int (*disassembler_ftype) (bfd_vma, disassemble_info *); int print_insn_tci(bfd_vma, disassemble_info*); int print_insn_big_mips (bfd_vma, disassemble_info*); int print_insn_little_mips (bfd_vma, disassemble_info*); +int print_insn_nanomips (bfd_vma, disassemble_info*); int print_insn_i386 (bfd_vma, disassemble_info*); int print_insn_m68k (bfd_vma, disassemble_info*); int print_insn_z8001 (bfd_vma, disassemble_info*); diff --git a/include/elf.h b/include/elf.h index 934dbbd6b3..ea7708a4ea 100644 --- a/include/elf.h +++ b/include/elf.h @@ -28,8 +28,11 @@ typedef int64_t Elf64_Sxword; #define PT_PHDR 6 #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7fffffff -#define PT_MIPS_REGINFO 0x70000000 -#define PT_MIPS_OPTIONS 0x70000001 + +#define PT_MIPS_REGINFO 0x70000000 +#define PT_MIPS_RTPROC 0x70000001 +#define PT_MIPS_OPTIONS 0x70000002 +#define PT_MIPS_ABIFLAGS 0x70000003 /* Flags in the e_flags field of the header */ /* MIPS architecture level. */ @@ -61,7 +64,55 @@ typedef int64_t Elf64_Sxword; #define EF_MIPS_ABI 0x0000f000 #define EF_MIPS_FP64 0x00000200 #define EF_MIPS_NAN2008 0x00000400 -#define EF_MIPS_ARCH 0xf0000000 + +/* MIPS machine variant */ +#define EF_MIPS_MACH_NONE 0x00000000 /* A standard MIPS implementation */ +#define EF_MIPS_MACH_3900 0x00810000 /* Toshiba R3900 */ +#define EF_MIPS_MACH_4010 0x00820000 /* LSI R4010 */ +#define EF_MIPS_MACH_4100 0x00830000 /* NEC VR4100 */ +#define EF_MIPS_MACH_4650 0x00850000 /* MIPS R4650 */ +#define EF_MIPS_MACH_4120 0x00870000 /* NEC VR4120 */ +#define EF_MIPS_MACH_4111 0x00880000 /* NEC VR4111/VR4181 */ +#define EF_MIPS_MACH_SB1 0x008a0000 /* Broadcom SB-1 */ +#define EF_MIPS_MACH_OCTEON 0x008b0000 /* Cavium Networks Octeon */ +#define EF_MIPS_MACH_XLR 0x008c0000 /* RMI Xlr */ +#define EF_MIPS_MACH_OCTEON2 0x008d0000 /* Cavium Networks Octeon2 */ +#define EF_MIPS_MACH_OCTEON3 0x008e0000 /* Cavium Networks Octeon3 */ +#define EF_MIPS_MACH_5400 0x00910000 /* NEC VR5400 */ +#define EF_MIPS_MACH_5900 0x00920000 /* Toshiba/Sony R5900 */ +#define EF_MIPS_MACH_5500 0x00980000 /* NEC VR5500 */ +#define EF_MIPS_MACH_9000 0x00990000 /* PMC-Sierra RM9000 */ +#define EF_MIPS_MACH_LS2E 0x00a00000 /* ST Microelectronics Loongson 2E */ +#define EF_MIPS_MACH_LS2F 0x00a10000 /* ST Microelectronics Loongson 2F */ +#define EF_MIPS_MACH_LS3A 0x00a20000 /* ST Microelectronics Loongson 3A */ +#define EF_MIPS_MACH 0x00ff0000 /* EF_MIPS_MACH_xxx selection mask */ + +#define MIPS_ABI_FP_UNKNOWN (-1) /* Unknown FP ABI (internal) */ + +#define MIPS_ABI_FP_ANY 0x0 /* FP ABI doesn't matter */ +#define MIPS_ABI_FP_DOUBLE 0x1 /* -mdouble-float */ +#define MIPS_ABI_FP_SINGLE 0x2 /* -msingle-float */ +#define MIPS_ABI_FP_SOFT 0x3 /* -msoft-float */ +#define MIPS_ABI_FP_OLD_64 0x4 /* -mips32r2 -mfp64 */ +#define MIPS_ABI_FP_XX 0x5 /* -mfpxx */ +#define MIPS_ABI_FP_64 0x6 /* -mips32r2 -mfp64 */ +#define MIPS_ABI_FP_64A 0x7 /* -mips32r2 -mfp64 -mno-odd-spreg */ + +typedef struct mips_elf_abiflags_v0 { + uint16_t version; /* Version of flags structure */ + uint8_t isa_level; /* The level of the ISA: 1-5, 32, 64 */ + uint8_t isa_rev; /* The revision of ISA: */ + /* - 0 for MIPS V and below, */ + /* - 1-n otherwise. */ + uint8_t gpr_size; /* The size of general purpose registers */ + uint8_t cpr1_size; /* The size of co-processor 1 registers */ + uint8_t cpr2_size; /* The size of co-processor 2 registers */ + uint8_t fp_abi; /* The floating-point ABI */ + uint32_t isa_ext; /* Mask of processor-specific extensions */ + uint32_t ases; /* Mask of ASEs used */ + uint32_t flags1; /* Mask of general flags */ + uint32_t flags2; +} Mips_elf_abiflags_v0; /* These constants define the different elf file types */ #define ET_NONE 0 @@ -121,6 +172,8 @@ typedef int64_t Elf64_Sxword; #define EM_RISCV 243 /* RISC-V */ +#define EM_NANOMIPS 249 /* Wave Computing nanoMIPS */ + /* * This is an interim value that we will use until the committee comes * up with a final number. @@ -729,11 +782,11 @@ typedef struct { /* ARM-specific values for sh_flags */ #define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ #define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined - in the input to a link step */ + in the input to a link step */ /* ARM-specific program header flags */ #define PF_ARM_SB 0x10000000 /* Segment contains the location - addressed by the static base */ + addressed by the static base */ /* ARM relocs. */ #define R_ARM_NONE 0 /* No reloc */ @@ -994,7 +1047,7 @@ typedef struct { #define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ #define R_X86_64_RELATIVE 8 /* Adjust by program base */ #define R_X86_64_GOTPCREL 9 /* 32 bit signed pc relative - offset to GOT */ + offset to GOT */ #define R_X86_64_32 10 /* Direct 32 bit zero extended */ #define R_X86_64_32S 11 /* Direct 32 bit sign extended */ #define R_X86_64_16 12 /* Direct 16 bit zero extended */ @@ -1017,7 +1070,7 @@ typedef struct { #define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ #define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ #define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch - prediction. */ + prediction. */ #define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ #define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ @@ -1030,7 +1083,7 @@ typedef struct { /* Additional section indeces. */ #define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared - symbols in ANSI C. */ + symbols in ANSI C. */ #define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ /* Legal values for sh_type field of Elf32_Shdr. */ @@ -1285,6 +1338,71 @@ typedef struct { #define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ #define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ +/* RISC-V relocations. */ +#define R_RISCV_NONE 0 +#define R_RISCV_32 1 +#define R_RISCV_64 2 +#define R_RISCV_RELATIVE 3 +#define R_RISCV_COPY 4 +#define R_RISCV_JUMP_SLOT 5 +#define R_RISCV_TLS_DTPMOD32 6 +#define R_RISCV_TLS_DTPMOD64 7 +#define R_RISCV_TLS_DTPREL32 8 +#define R_RISCV_TLS_DTPREL64 9 +#define R_RISCV_TLS_TPREL32 10 +#define R_RISCV_TLS_TPREL64 11 +#define R_RISCV_BRANCH 16 +#define R_RISCV_JAL 17 +#define R_RISCV_CALL 18 +#define R_RISCV_CALL_PLT 19 +#define R_RISCV_GOT_HI20 20 +#define R_RISCV_TLS_GOT_HI20 21 +#define R_RISCV_TLS_GD_HI20 22 +#define R_RISCV_PCREL_HI20 23 +#define R_RISCV_PCREL_LO12_I 24 +#define R_RISCV_PCREL_LO12_S 25 +#define R_RISCV_HI20 26 +#define R_RISCV_LO12_I 27 +#define R_RISCV_LO12_S 28 +#define R_RISCV_TPREL_HI20 29 +#define R_RISCV_TPREL_LO12_I 30 +#define R_RISCV_TPREL_LO12_S 31 +#define R_RISCV_TPREL_ADD 32 +#define R_RISCV_ADD8 33 +#define R_RISCV_ADD16 34 +#define R_RISCV_ADD32 35 +#define R_RISCV_ADD64 36 +#define R_RISCV_SUB8 37 +#define R_RISCV_SUB16 38 +#define R_RISCV_SUB32 39 +#define R_RISCV_SUB64 40 +#define R_RISCV_GNU_VTINHERIT 41 +#define R_RISCV_GNU_VTENTRY 42 +#define R_RISCV_ALIGN 43 +#define R_RISCV_RVC_BRANCH 44 +#define R_RISCV_RVC_JUMP 45 +#define R_RISCV_RVC_LUI 46 +#define R_RISCV_GPREL_I 47 +#define R_RISCV_GPREL_S 48 +#define R_RISCV_TPREL_I 49 +#define R_RISCV_TPREL_S 50 +#define R_RISCV_RELAX 51 +#define R_RISCV_SUB6 52 +#define R_RISCV_SET6 53 +#define R_RISCV_SET8 54 +#define R_RISCV_SET16 55 +#define R_RISCV_SET32 56 + +/* RISC-V ELF Flags. */ +#define EF_RISCV_RVC 0x0001 +#define EF_RISCV_FLOAT_ABI 0x0006 +#define EF_RISCV_FLOAT_ABI_SOFT 0x0000 +#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002 +#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004 +#define EF_RISCV_FLOAT_ABI_QUAD 0x0006 +#define EF_RISCV_RVE 0x0008 +#define EF_RISCV_TSO 0x0010 + typedef struct elf32_rel { Elf32_Addr r_offset; Elf32_Word r_info; @@ -1532,6 +1650,16 @@ typedef struct elf64_shdr { #define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ #define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ +/* + * Physical entry point into the kernel. + * + * 32bit entry point into the kernel. When requested to launch the + * guest kernel, use this entry point to launch the guest in 32-bit + * protected mode with paging disabled. + * + * [ Corresponding definition in Linux kernel: include/xen/interface/elfnote.h ] + */ +#define XEN_ELFNOTE_PHYS32_ENTRY 18 /* 0x12 */ /* Note header in a PT_NOTE section */ typedef struct elf32_note { diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 117d2fbbca..b16c9ec513 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -367,7 +367,7 @@ void dump_opcount_info(FILE *f, fprintf_function cpu_fprintf); #endif /* !CONFIG_USER_ONLY */ int cpu_memory_rw_debug(CPUState *cpu, target_ulong addr, - uint8_t *buf, int len, int is_write); + uint8_t *buf, target_ulong len, int is_write); int cpu_exec(CPUState *cpu); diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 18b40d6145..cef8b88a2a 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -72,6 +72,9 @@ ram_addr_t qemu_ram_block_host_offset(RAMBlock *rb, void *host); void qemu_ram_set_idstr(RAMBlock *block, const char *name, DeviceState *dev); void qemu_ram_unset_idstr(RAMBlock *block); const char *qemu_ram_get_idstr(RAMBlock *rb); +void *qemu_ram_get_host_addr(RAMBlock *rb); +ram_addr_t qemu_ram_get_offset(RAMBlock *rb); +ram_addr_t qemu_ram_get_used_length(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); bool qemu_ram_is_uf_zeroable(RAMBlock *rb); void qemu_ram_set_uf_zeroable(RAMBlock *rb); @@ -83,14 +86,14 @@ size_t qemu_ram_pagesize(RAMBlock *block); size_t qemu_ram_pagesize_largest(void); void cpu_physical_memory_rw(hwaddr addr, uint8_t *buf, - int len, int is_write); + hwaddr len, int is_write); static inline void cpu_physical_memory_read(hwaddr addr, - void *buf, int len) + void *buf, hwaddr len) { cpu_physical_memory_rw(addr, buf, len, 0); } static inline void cpu_physical_memory_write(hwaddr addr, - const void *buf, int len) + const void *buf, hwaddr len) { cpu_physical_memory_rw(addr, (void *)buf, len, 1); } @@ -111,18 +114,14 @@ bool cpu_physical_memory_is_io(hwaddr phys_addr); */ void qemu_flush_coalesced_mmio_buffer(void); -void cpu_physical_memory_write_rom(AddressSpace *as, hwaddr addr, - const uint8_t *buf, int len); -void cpu_flush_icache_range(hwaddr start, int len); +void cpu_flush_icache_range(hwaddr start, hwaddr len); extern struct MemoryRegion io_mem_rom; extern struct MemoryRegion io_mem_notdirty; -typedef int (RAMBlockIterFunc)(const char *block_name, void *host_addr, - ram_addr_t offset, ram_addr_t length, void *opaque); +typedef int (RAMBlockIterFunc)(RAMBlock *rb, void *opaque); int qemu_ram_foreach_block(RAMBlockIterFunc func, void *opaque); -int qemu_ram_foreach_migratable_block(RAMBlockIterFunc func, void *opaque); int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); #endif diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index a171ffc1a4..8f2a848bf5 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -24,6 +24,7 @@ #endif #include "qemu/host-utils.h" +#include "qemu/thread.h" #include "qemu/queue.h" #ifdef CONFIG_TCG #include "tcg-target.h" @@ -66,37 +67,23 @@ typedef uint64_t target_ulong; #define CPU_TLB_ENTRY_BITS 5 #endif -/* TCG_TARGET_TLB_DISPLACEMENT_BITS is used in CPU_TLB_BITS to ensure that - * the TLB is not unnecessarily small, but still small enough for the - * TLB lookup instruction sequence used by the TCG target. - * - * TCG will have to generate an operand as large as the distance between - * env and the tlb_table[NB_MMU_MODES - 1][0].addend. For simplicity, - * the TCG targets just round everything up to the next power of two, and - * count bits. This works because: 1) the size of each TLB is a largish - * power of two, 2) and because the limit of the displacement is really close - * to a power of two, 3) the offset of tlb_table[0][0] inside env is smaller - * than the size of a TLB. - * - * For example, the maximum displacement 0xFFF0 on PPC and MIPS, but TCG - * just says "the displacement is 16 bits". TCG_TARGET_TLB_DISPLACEMENT_BITS - * then ensures that tlb_table at least 0x8000 bytes large ("not unnecessarily - * small": 2^15). The operand then will come up smaller than 0xFFF0 without - * any particular care, because the TLB for a single MMU mode is larger than - * 0x10000-0xFFF0=16 bytes. In the end, the maximum value of the operand - * could be something like 0xC000 (the offset of the last TLB table) plus - * 0x18 (the offset of the addend field in each TLB entry) plus the offset - * of tlb_table inside env (which is non-trivial but not huge). - */ -#define CPU_TLB_BITS \ - MIN(8, \ - TCG_TARGET_TLB_DISPLACEMENT_BITS - CPU_TLB_ENTRY_BITS - \ - (NB_MMU_MODES <= 1 ? 0 : \ - NB_MMU_MODES <= 2 ? 1 : \ - NB_MMU_MODES <= 4 ? 2 : \ - NB_MMU_MODES <= 8 ? 3 : 4)) +#define CPU_TLB_DYN_MIN_BITS 6 +#define CPU_TLB_DYN_DEFAULT_BITS 8 -#define CPU_TLB_SIZE (1 << CPU_TLB_BITS) +# if HOST_LONG_BITS == 32 +/* Make sure we do not require a double-word shift for the TLB load */ +# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) +# else /* HOST_LONG_BITS == 64 */ +/* + * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == + * 2**34 == 16G of address space. This is roughly what one would expect a + * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel + * Skylake's Level-2 STLB has 16 1G entries. + * Also, make sure we do not size the TLB past the guest's address space. + */ +# define CPU_TLB_DYN_MAX_BITS \ + MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) +# endif typedef struct CPUTLBEntry { /* bit TARGET_LONG_BITS to TARGET_PAGE_BITS : virtual address @@ -140,16 +127,74 @@ typedef struct CPUIOTLBEntry { MemTxAttrs attrs; } CPUIOTLBEntry; +/** + * struct CPUTLBWindow + * @begin_ns: host time (in ns) at the beginning of the time window + * @max_entries: maximum number of entries observed in the window + * + * See also: tlb_mmu_resize_locked() + */ +typedef struct CPUTLBWindow { + int64_t begin_ns; + size_t max_entries; +} CPUTLBWindow; + +typedef struct CPUTLBDesc { + /* + * Describe a region covering all of the large pages allocated + * into the tlb. When any page within this region is flushed, + * we must flush the entire tlb. The region is matched if + * (addr & large_page_mask) == large_page_addr. + */ + target_ulong large_page_addr; + target_ulong large_page_mask; + /* The next index to use in the tlb victim table. */ + size_t vindex; + CPUTLBWindow window; + size_t n_used_entries; +} CPUTLBDesc; + +/* + * Data elements that are shared between all MMU modes. + */ +typedef struct CPUTLBCommon { + /* Serialize updates to tlb_table and tlb_v_table, and others as noted. */ + QemuSpin lock; + /* + * Within dirty, for each bit N, modifications have been made to + * mmu_idx N since the last time that mmu_idx was flushed. + * Protected by tlb_c.lock. + */ + uint16_t dirty; + /* + * Statistics. These are not lock protected, but are read and + * written atomically. This allows the monitor to print a snapshot + * of the stats without interfering with the cpu. + */ + size_t full_flush_count; + size_t part_flush_count; + size_t elide_flush_count; +} CPUTLBCommon; + +# define CPU_TLB \ + /* tlb_mask[i] contains (n_entries - 1) << CPU_TLB_ENTRY_BITS */ \ + uintptr_t tlb_mask[NB_MMU_MODES]; \ + CPUTLBEntry *tlb_table[NB_MMU_MODES]; +# define CPU_IOTLB \ + CPUIOTLBEntry *iotlb[NB_MMU_MODES]; + +/* + * The meaning of each of the MMU modes is defined in the target code. + * Note that NB_MMU_MODES is not yet defined; we can only reference it + * within preprocessor defines that will be expanded later. + */ #define CPU_COMMON_TLB \ - /* The meaning of the MMU modes is defined in the target code. */ \ - CPUTLBEntry tlb_table[NB_MMU_MODES][CPU_TLB_SIZE]; \ + CPUTLBCommon tlb_c; \ + CPUTLBDesc tlb_d[NB_MMU_MODES]; \ + CPU_TLB \ CPUTLBEntry tlb_v_table[NB_MMU_MODES][CPU_VTLB_SIZE]; \ - CPUIOTLBEntry iotlb[NB_MMU_MODES][CPU_TLB_SIZE]; \ - CPUIOTLBEntry iotlb_v[NB_MMU_MODES][CPU_VTLB_SIZE]; \ - size_t tlb_flush_count; \ - target_ulong tlb_flush_addr; \ - target_ulong tlb_flush_mask; \ - target_ulong vtlb_index; \ + CPU_IOTLB \ + CPUIOTLBEntry iotlb_v[NB_MMU_MODES][CPU_VTLB_SIZE]; #else diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 0f2cb717b1..d78041d7a0 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -48,8 +48,19 @@ #define CPU_LDST_H #if defined(CONFIG_USER_ONLY) +/* sparc32plus has 64bit long but 32bit space address + * this can make bad result with g2h() and h2g() + */ +#if TARGET_VIRT_ADDR_SPACE_BITS <= 32 +typedef uint32_t abi_ptr; +#define TARGET_ABI_FMT_ptr "%x" +#else +typedef uint64_t abi_ptr; +#define TARGET_ABI_FMT_ptr "%"PRIx64 +#endif + /* 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)(abi_ptr)(x) + guest_base)) #define guest_addr_valid(x) ((x) <= GUEST_ADDR_MAX) #define h2g_valid(x) guest_addr_valid((unsigned long)(x) - guest_base) @@ -61,7 +72,7 @@ static inline int guest_range_valid(unsigned long start, unsigned long len) #define h2g_nocheck(x) ({ \ unsigned long __ret = (unsigned long)(x) - guest_base; \ - (abi_ulong)__ret; \ + (abi_ptr)__ret; \ }) #define h2g(x) ({ \ @@ -69,7 +80,9 @@ static inline int guest_range_valid(unsigned long start, unsigned long len) assert(h2g_valid(x)); \ h2g_nocheck(x); \ }) - +#else +typedef target_ulong abi_ptr; +#define TARGET_ABI_FMT_ptr TARGET_ABI_FMT_lx #endif #if defined(CONFIG_USER_ONLY) @@ -113,6 +126,36 @@ extern __thread uintptr_t helper_retaddr; /* The memory helpers for tcg-generated code need tcg_target_long etc. */ #include "tcg.h" +static inline target_ulong tlb_addr_write(const CPUTLBEntry *entry) +{ +#if TCG_OVERSIZED_GUEST + return entry->addr_write; +#else + return atomic_read(&entry->addr_write); +#endif +} + +/* Find the TLB index corresponding to the mmu_idx + address pair. */ +static inline uintptr_t tlb_index(CPUArchState *env, uintptr_t mmu_idx, + target_ulong addr) +{ + uintptr_t size_mask = env->tlb_mask[mmu_idx] >> CPU_TLB_ENTRY_BITS; + + return (addr >> TARGET_PAGE_BITS) & size_mask; +} + +static inline size_t tlb_n_entries(CPUArchState *env, uintptr_t mmu_idx) +{ + return (env->tlb_mask[mmu_idx] >> CPU_TLB_ENTRY_BITS) + 1; +} + +/* Find the TLB entry corresponding to the mmu_idx + address pair. */ +static inline CPUTLBEntry *tlb_entry(CPUArchState *env, uintptr_t mmu_idx, + target_ulong addr) +{ + return &env->tlb_table[mmu_idx][tlb_index(env, mmu_idx, addr)]; +} + #ifdef MMU_MODE0_SUFFIX #define CPU_MMU_INDEX 0 #define MEMSUFFIX MMU_MODE0_SUFFIX @@ -397,15 +440,14 @@ extern __thread uintptr_t helper_retaddr; * This is the equivalent of the initial fast-path code used by * TCG backends for guest load and store accesses. */ -static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr, +static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, int access_type, int mmu_idx) { #if defined(CONFIG_USER_ONLY) return g2h(addr); #else - int index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - CPUTLBEntry *tlbentry = &env->tlb_table[mmu_idx][index]; - target_ulong tlb_addr; + CPUTLBEntry *tlbentry = tlb_entry(env, mmu_idx, addr); + abi_ptr tlb_addr; uintptr_t haddr; switch (access_type) { @@ -413,7 +455,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr, tlb_addr = tlbentry->addr_read; break; case 1: - tlb_addr = tlbentry->addr_write; + tlb_addr = tlb_addr_write(tlbentry); break; case 2: tlb_addr = tlbentry->addr_code; @@ -432,7 +474,7 @@ static inline void *tlb_vaddr_to_host(CPUArchState *env, target_ulong addr, return NULL; } - haddr = addr + env->tlb_table[mmu_idx][index].addend; + haddr = addr + tlbentry->addend; return (void *)haddr; #endif /* defined(CONFIG_USER_ONLY) */ } diff --git a/include/exec/cpu_ldst_template.h b/include/exec/cpu_ldst_template.h index 4db2302962..0f061d47ef 100644 --- a/include/exec/cpu_ldst_template.h +++ b/include/exec/cpu_ldst_template.h @@ -81,7 +81,7 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) { - int page_index; + CPUTLBEntry *entry; RES_TYPE res; target_ulong addr; int mmu_idx; @@ -94,15 +94,15 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #endif 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 != + entry = tlb_entry(env, mmu_idx, addr); + if (unlikely(entry->ADDR_READ != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { 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; + uintptr_t hostaddr = addr + entry->addend; res = glue(glue(ld, USUFFIX), _p)((uint8_t *)hostaddr); } return res; @@ -120,7 +120,8 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, target_ulong ptr, uintptr_t retaddr) { - int res, page_index; + CPUTLBEntry *entry; + int res; target_ulong addr; int mmu_idx; TCGMemOpIdx oi; @@ -132,15 +133,15 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #endif 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 != + entry = tlb_entry(env, mmu_idx, addr); + if (unlikely(entry->ADDR_READ != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { 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; + uintptr_t hostaddr = addr + entry->addend; res = glue(glue(lds, SUFFIX), _p)((uint8_t *)hostaddr); } return res; @@ -162,7 +163,7 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, target_ulong ptr, RES_TYPE v, uintptr_t retaddr) { - int page_index; + CPUTLBEntry *entry; target_ulong addr; int mmu_idx; TCGMemOpIdx oi; @@ -174,15 +175,15 @@ glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #endif 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 != + entry = tlb_entry(env, mmu_idx, addr); + if (unlikely(tlb_addr_write(entry) != (addr & (TARGET_PAGE_MASK | (DATA_SIZE - 1))))) { 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; + uintptr_t hostaddr = addr + entry->addend; glue(glue(st, SUFFIX), _p)((uint8_t *)hostaddr, v); } } diff --git a/include/exec/cpu_ldst_useronly_template.h b/include/exec/cpu_ldst_useronly_template.h index e30e58ed4a..0fd6019af0 100644 --- a/include/exec/cpu_ldst_useronly_template.h +++ b/include/exec/cpu_ldst_useronly_template.h @@ -62,7 +62,7 @@ #endif static inline RES_TYPE -glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr) { #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( @@ -74,7 +74,7 @@ glue(glue(cpu_ld, USUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) static inline RES_TYPE glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - target_ulong ptr, + abi_ptr ptr, uintptr_t retaddr) { RES_TYPE ret; @@ -86,7 +86,7 @@ glue(glue(glue(cpu_ld, USUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #if DATA_SIZE <= 2 static inline int -glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) +glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr) { #if !defined(CODE_ACCESS) trace_guest_mem_before_exec( @@ -98,7 +98,7 @@ glue(glue(cpu_lds, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr) static inline int glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - target_ulong ptr, + abi_ptr ptr, uintptr_t retaddr) { int ret; @@ -111,7 +111,7 @@ glue(glue(glue(cpu_lds, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, #ifndef CODE_ACCESS static inline void -glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, +glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, abi_ptr ptr, RES_TYPE v) { #if !defined(CODE_ACCESS) @@ -124,7 +124,7 @@ glue(glue(cpu_st, SUFFIX), MEMSUFFIX)(CPUArchState *env, target_ulong ptr, static inline void glue(glue(glue(cpu_st, SUFFIX), MEMSUFFIX), _ra)(CPUArchState *env, - target_ulong ptr, + abi_ptr ptr, RES_TYPE v, uintptr_t retaddr) { diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index c91db211bc..5373188be3 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -23,6 +23,6 @@ /* cputlb.c */ void tlb_protect_code(ram_addr_t ram_addr); void tlb_unprotect_code(ram_addr_t ram_addr); -size_t tlb_flush_count(void); +void tlb_flush_counts(size_t *full, size_t *part, size_t *elide); #endif #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index da73e3bfed..97b90cb0db 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -99,6 +99,11 @@ void cpu_address_space_init(CPUState *cpu, int asidx, #if !defined(CONFIG_USER_ONLY) && defined(CONFIG_TCG) /* cputlb.c */ +/** + * tlb_init - initialize a CPU's TLB + * @cpu: CPU whose TLB should be initialized + */ +void tlb_init(CPUState *cpu); /** * tlb_flush_page: * @cpu: CPU whose TLB should be flushed @@ -258,6 +263,9 @@ void tlb_set_page(CPUState *cpu, target_ulong vaddr, void probe_write(CPUArchState *env, target_ulong addr, int size, int mmu_idx, uintptr_t retaddr); #else +static inline void tlb_init(CPUState *cpu) +{ +} static inline void tlb_flush_page(CPUState *cpu, target_ulong addr) { } @@ -343,9 +351,11 @@ struct TranslationBlock { #define CF_USE_ICOUNT 0x00020000 #define CF_INVALID 0x00040000 /* TB is stale. Set with @jmp_lock held */ #define CF_PARALLEL 0x00080000 /* Generate code for a parallel context */ +#define CF_CLUSTER_MASK 0xff000000 /* Top 8 bits are cluster ID */ +#define CF_CLUSTER_SHIFT 24 /* cflags' mask for hashing/comparison */ #define CF_HASH_MASK \ - (CF_COUNT_MASK | CF_LAST_IO | CF_USE_ICOUNT | CF_PARALLEL) + (CF_COUNT_MASK | CF_LAST_IO | CF_USE_ICOUNT | CF_PARALLEL | CF_CLUSTER_MASK) /* Per-vCPU dynamic tracing state used to generate this TB */ uint32_t trace_vcpu_dstate; @@ -465,6 +475,11 @@ static inline void assert_no_pages_locked(void) struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, hwaddr index, MemTxAttrs attrs); +/* + * Note: tlb_fill() can trigger a resize of the TLB. This means that all of the + * caller's prior references to the TLB table (e.g. CPUTLBEntry pointers) must + * be discarded and looked up again (e.g. via tlb_entry()). + */ void tlb_fill(CPUState *cpu, target_ulong addr, int size, MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); @@ -502,8 +517,6 @@ hwaddr memory_region_section_get_iotlb(CPUState *cpu, hwaddr paddr, hwaddr xlat, int prot, target_ulong *address); -bool memory_region_is_unassigned(MemoryRegion *mr); - #endif /* vl.c */ diff --git a/include/exec/helper-head.h b/include/exec/helper-head.h index 276dd5afce..f2519c9741 100644 --- a/include/exec/helper-head.h +++ b/include/exec/helper-head.h @@ -30,6 +30,7 @@ #define dh_alias_f32 i32 #define dh_alias_f64 i64 #define dh_alias_ptr ptr +#define dh_alias_cptr ptr #define dh_alias_void void #define dh_alias_noreturn noreturn #define dh_alias(t) glue(dh_alias_, t) @@ -43,6 +44,7 @@ #define dh_ctype_f32 float32 #define dh_ctype_f64 float64 #define dh_ctype_ptr void * +#define dh_ctype_cptr const void * #define dh_ctype_void void #define dh_ctype_noreturn void QEMU_NORETURN #define dh_ctype(t) dh_ctype_##t @@ -88,6 +90,7 @@ #define dh_is_64bit_i32 0 #define dh_is_64bit_i64 1 #define dh_is_64bit_ptr (sizeof(void *) == 8) +#define dh_is_64bit_cptr dh_is_64bit_ptr #define dh_is_64bit(t) glue(dh_is_64bit_, dh_alias(t)) #define dh_is_signed_void 0 @@ -105,9 +108,24 @@ extension instructions that may be required, e.g. ia64's addp4. But for now we don't support any 64-bit targets with 32-bit pointers. */ #define dh_is_signed_ptr 0 +#define dh_is_signed_cptr dh_is_signed_ptr #define dh_is_signed_env dh_is_signed_ptr #define dh_is_signed(t) dh_is_signed_##t +#define dh_callflag_i32 0 +#define dh_callflag_s32 0 +#define dh_callflag_int 0 +#define dh_callflag_i64 0 +#define dh_callflag_s64 0 +#define dh_callflag_f16 0 +#define dh_callflag_f32 0 +#define dh_callflag_f64 0 +#define dh_callflag_ptr 0 +#define dh_callflag_cptr dh_callflag_ptr +#define dh_callflag_void 0 +#define dh_callflag_noreturn TCG_CALL_NO_RETURN +#define dh_callflag(t) glue(dh_callflag_, dh_alias(t)) + #define dh_sizemask(t, n) \ ((dh_is_64bit(t) << (n*2)) | (dh_is_signed(t) << (n*2+1))) diff --git a/include/exec/helper-tcg.h b/include/exec/helper-tcg.h index b3bdb0c399..268e0f804b 100644 --- a/include/exec/helper-tcg.h +++ b/include/exec/helper-tcg.h @@ -11,36 +11,43 @@ #define str(s) #s #define DEF_HELPER_FLAGS_0(NAME, FLAGS, ret) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) }, #define DEF_HELPER_FLAGS_1(NAME, FLAGS, ret, t1) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) }, #define DEF_HELPER_FLAGS_2(NAME, FLAGS, ret, t1, t2) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \ | dh_sizemask(t2, 2) }, #define DEF_HELPER_FLAGS_3(NAME, FLAGS, ret, t1, t2, t3) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) }, #define DEF_HELPER_FLAGS_4(NAME, FLAGS, ret, t1, t2, t3, t4) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) }, #define DEF_HELPER_FLAGS_5(NAME, FLAGS, ret, t1, t2, t3, t4, t5) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \ | dh_sizemask(t5, 5) }, #define DEF_HELPER_FLAGS_6(NAME, FLAGS, ret, t1, t2, t3, t4, t5, t6) \ - { .func = HELPER(NAME), .name = str(NAME), .flags = FLAGS, \ + { .func = HELPER(NAME), .name = str(NAME), \ + .flags = FLAGS | dh_callflag(ret), \ .sizemask = dh_sizemask(ret, 0) | dh_sizemask(t1, 1) \ | dh_sizemask(t2, 2) | dh_sizemask(t3, 3) | dh_sizemask(t4, 4) \ | dh_sizemask(t5, 5) | dh_sizemask(t6, 6) }, diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index d4a1642098..d4a3477d71 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -37,6 +37,16 @@ typedef struct MemTxAttrs { unsigned int user:1; /* Requester ID (for MSI for example) */ unsigned int requester_id:16; + /* + * The following are target-specific page-table bits. These are not + * related to actual memory transactions at all. However, this structure + * is part of the tlb_fill interface, cached in the cputlb structure, + * and has unused bits. These fields will be read by target-specific + * helpers using env->iotlb[mmu_idx][tlb_index()].attrs.target_tlb_bitN. + */ + unsigned int target_tlb_bit0 : 1; + unsigned int target_tlb_bit1 : 1; + unsigned int target_tlb_bit2 : 1; } MemTxAttrs; /* Bus masters which don't specify any attributes will get this, diff --git a/include/exec/memory.h b/include/exec/memory.h index c5ffa98069..c75090c12c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -103,6 +103,29 @@ struct IOMMUNotifier { }; typedef struct IOMMUNotifier IOMMUNotifier; +/* RAM is pre-allocated and passed into qemu_ram_alloc_from_ptr */ +#define RAM_PREALLOC (1 << 0) + +/* RAM is mmap-ed with MAP_SHARED */ +#define RAM_SHARED (1 << 1) + +/* Only a portion of RAM (used_length) is actually used, and migrated. + * This used_length size can change across reboots. + */ +#define RAM_RESIZEABLE (1 << 2) + +/* UFFDIO_ZEROPAGE is available on this RAMBlock to atomically + * zero the page and wake waiting processes. + * (Set during postcopy) + */ +#define RAM_UF_ZEROPAGE (1 << 3) + +/* RAM can be migrated */ +#define RAM_MIGRATABLE (1 << 4) + +/* RAM is a persistent kind memory */ +#define RAM_PMEM (1 << 5) + static inline void iommu_notifier_init(IOMMUNotifier *n, IOMMUNotify fn, IOMMUNotifierFlag flags, hwaddr start, hwaddr end, @@ -141,15 +164,6 @@ struct MemoryRegionOps { uint64_t data, unsigned size, MemTxAttrs attrs); - /* Instruction execution pre-callback: - * @addr is the address of the access relative to the @mr. - * @size is the size of the area returned by the callback. - * @offset is the location of the pointer inside @mr. - * - * Returns a pointer to a location which contains guest code. - */ - void *(*request_ptr)(void *opaque, hwaddr addr, unsigned *size, - unsigned *offset); enum device_endian endianness; /* Guest-visible constraints: */ @@ -187,11 +201,6 @@ struct MemoryRegionOps { */ bool unaligned; } impl; - - /* If .read and .write are not present, old_mmio may be used for - * backwards compatibility with old mmio registration - */ - const MemoryRegionMmio old_mmio; }; enum IOMMUMemoryRegionAttr { @@ -346,6 +355,7 @@ struct MemoryRegion { bool ram; bool subpage; bool readonly; /* For RAM regions */ + bool nonvolatile; bool rom_device; bool flush_coalesced_mmio; bool global_locking; @@ -369,9 +379,9 @@ struct MemoryRegion { MemoryRegion *alias; hwaddr alias_offset; int32_t priority; - QTAILQ_HEAD(subregions, MemoryRegion) subregions; + QTAILQ_HEAD(, MemoryRegion) subregions; QTAILQ_ENTRY(MemoryRegion) subregions_link; - QTAILQ_HEAD(coalesced_ranges, CoalescedMemoryRange) coalesced; + QTAILQ_HEAD(, CoalescedMemoryRange) coalesced; const char *name; unsigned ioeventfd_nb; MemoryRegionIoeventfd *ioeventfds; @@ -410,9 +420,9 @@ struct MemoryListener { bool match_data, uint64_t data, EventNotifier *e); void (*eventfd_del)(MemoryListener *listener, MemoryRegionSection *section, bool match_data, uint64_t data, EventNotifier *e); - void (*coalesced_mmio_add)(MemoryListener *listener, MemoryRegionSection *section, + void (*coalesced_io_add)(MemoryListener *listener, MemoryRegionSection *section, hwaddr addr, hwaddr len); - void (*coalesced_mmio_del)(MemoryListener *listener, MemoryRegionSection *section, + void (*coalesced_io_del)(MemoryListener *listener, MemoryRegionSection *section, hwaddr addr, hwaddr len); /* Lower = earlier (during add), later (during del) */ unsigned priority; @@ -435,7 +445,7 @@ struct AddressSpace { int ioeventfd_nb; struct MemoryRegionIoeventfd *ioeventfds; - QTAILQ_HEAD(memory_listeners_as, MemoryListener) listeners; + QTAILQ_HEAD(, MemoryListener) listeners; QTAILQ_ENTRY(AddressSpace) address_spaces_link; }; @@ -471,6 +481,7 @@ static inline FlatView *address_space_to_flatview(AddressSpace *as) * @offset_within_address_space: the address of the first byte of the section * relative to the region's address space * @readonly: writes to this section are ignored + * @nonvolatile: this section is non-volatile */ struct MemoryRegionSection { MemoryRegion *mr; @@ -479,6 +490,7 @@ struct MemoryRegionSection { Int128 size; hwaddr offset_within_address_space; bool readonly; + bool nonvolatile; }; /** @@ -619,7 +631,8 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, uint64_t length, void *host), Error **errp); -#ifdef __linux__ +#ifdef CONFIG_POSIX + /** * memory_region_init_ram_from_file: Initialize RAM memory region with a * mmap-ed backend. @@ -631,7 +644,10 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, * @size: size of the region. * @align: alignment of the region base address; if 0, the default alignment * (getpagesize()) will be used. - * @share: %true if memory must be mmaped with the MAP_SHARED flag + * @ram_flags: Memory region features: + * - RAM_SHARED: memory must be mmaped with the MAP_SHARED flag + * - RAM_PMEM: the memory is persistent memory + * Other bits are ignored now. * @path: the path in which to allocate the RAM. * @errp: pointer to Error*, to store an error if it happens. * @@ -643,7 +659,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr, const char *name, uint64_t size, uint64_t align, - bool share, + uint32_t ram_flags, const char *path, Error **errp); @@ -922,7 +938,7 @@ uint64_t memory_region_size(MemoryRegion *mr); /** * memory_region_is_ram: check whether a memory region is random access * - * Returns %true is a memory region is random access. + * Returns %true if a memory region is random access. * * @mr: the memory region being queried */ @@ -934,7 +950,7 @@ static inline bool memory_region_is_ram(MemoryRegion *mr) /** * memory_region_is_ram_device: check whether a memory region is a ram device * - * Returns %true is a memory region is a device backed ram region + * Returns %true if a memory region is a device backed ram region * * @mr: the memory region being queried */ @@ -1148,7 +1164,7 @@ uint8_t memory_region_get_dirty_log_mask(MemoryRegion *mr); /** * memory_region_is_rom: check whether a memory region is ROM * - * Returns %true is a memory region is read-only memory. + * Returns %true if a memory region is read-only memory. * * @mr: the memory region being queried */ @@ -1157,6 +1173,17 @@ static inline bool memory_region_is_rom(MemoryRegion *mr) return mr->ram && mr->readonly; } +/** + * memory_region_is_nonvolatile: check whether a memory region is non-volatile + * + * Returns %true is a memory region is non-volatile memory. + * + * @mr: the memory region being queried + */ +static inline bool memory_region_is_nonvolatile(MemoryRegion *mr) +{ + return mr->nonvolatile; +} /** * memory_region_get_fd: Get a file descriptor backing a RAM memory region. @@ -1336,6 +1363,24 @@ bool memory_region_snapshot_get_dirty(MemoryRegion *mr, void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, hwaddr size, unsigned client); +/** + * memory_region_flush_rom_device: Mark a range of pages dirty and invalidate + * TBs (for self-modifying code). + * + * The MemoryRegionOps->write() callback of a ROM device must use this function + * to mark byte ranges that have been modified internally, such as by directly + * accessing the memory returned by memory_region_get_ram_ptr(). + * + * This function marks the range dirty and invalidates TBs so that TCG can + * detect self-modifying code. + * + * @mr: the region being flushed. + * @addr: the start, relative to the start of the region, of the range being + * flushed. + * @size: the size, in bytes, of the range being flushed. + */ +void memory_region_flush_rom_device(MemoryRegion *mr, hwaddr addr, hwaddr size); + /** * memory_region_set_readonly: Turn a memory region read-only (or read-write) * @@ -1347,6 +1392,17 @@ void memory_region_reset_dirty(MemoryRegion *mr, hwaddr addr, */ void memory_region_set_readonly(MemoryRegion *mr, bool readonly); +/** + * memory_region_set_nonvolatile: Turn a memory region non-volatile + * + * Allows a memory region to be marked as non-volatile. + * only useful on RAM regions. + * + * @mr: the region being updated. + * @nonvolatile: whether rhe region is to be non-volatile. + */ +void memory_region_set_nonvolatile(MemoryRegion *mr, bool nonvolatile); + /** * memory_region_rom_device_set_romd: enable/disable ROMD mode * @@ -1686,32 +1742,6 @@ void memory_global_dirty_log_stop(void); void mtree_info(fprintf_function mon_printf, void *f, bool flatview, bool dispatch_tree, bool owner); -/** - * memory_region_request_mmio_ptr: request a pointer to an mmio - * MemoryRegion. If it is possible map a RAM MemoryRegion with this pointer. - * When the device wants to invalidate the pointer it will call - * memory_region_invalidate_mmio_ptr. - * - * @mr: #MemoryRegion to check - * @addr: address within that region - * - * Returns true on success, false otherwise. - */ -bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr); - -/** - * memory_region_invalidate_mmio_ptr: invalidate the pointer to an mmio - * previously requested. - * In the end that means that if something wants to execute from this area it - * will need to request the pointer again. - * - * @mr: #MemoryRegion associated to the pointer. - * @offset: offset within the memory region - * @size: size of that area. - */ -void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset, - unsigned size); - /** * memory_region_dispatch_read: perform a read directly to the specified * MemoryRegion. @@ -1780,7 +1810,7 @@ void address_space_destroy(AddressSpace *as); */ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, uint8_t *buf, - int len, bool is_write); + hwaddr len, bool is_write); /** * address_space_write: write to address space. @@ -1797,7 +1827,33 @@ MemTxResult address_space_rw(AddressSpace *as, hwaddr addr, */ MemTxResult address_space_write(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, - const uint8_t *buf, int len); + const uint8_t *buf, hwaddr len); + +/** + * address_space_write_rom: write to address space, including ROM. + * + * This function writes to the specified address space, but will + * write data to both ROM and RAM. This is used for non-guest + * writes like writes from the gdb debug stub or initial loading + * of ROM contents. + * + * Note that portions of the write which attempt to write data to + * a device will be silently ignored -- only real RAM and ROM will + * be written to. + * + * 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 + * @len: the number of bytes to write + */ +MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, + MemTxAttrs attrs, + const uint8_t *buf, hwaddr len); /* address_space_ld*: load from an address space * address_space_st*: store to an address space @@ -1998,7 +2054,7 @@ static inline MemoryRegion *address_space_translate(AddressSpace *as, * @is_write: indicates the transfer direction * @attrs: memory attributes */ -bool address_space_access_valid(AddressSpace *as, hwaddr addr, int len, +bool address_space_access_valid(AddressSpace *as, hwaddr addr, hwaddr len, bool is_write, MemTxAttrs attrs); /* address_space_map: map a physical memory region into a host virtual address @@ -2035,19 +2091,19 @@ void address_space_unmap(AddressSpace *as, void *buffer, hwaddr len, /* Internal functions, part of the implementation of address_space_read. */ MemTxResult address_space_read_full(AddressSpace *as, hwaddr addr, - MemTxAttrs attrs, uint8_t *buf, int len); + MemTxAttrs attrs, uint8_t *buf, hwaddr len); MemTxResult flatview_read_continue(FlatView *fv, hwaddr addr, MemTxAttrs attrs, uint8_t *buf, - int len, hwaddr addr1, hwaddr l, + hwaddr len, hwaddr addr1, hwaddr l, MemoryRegion *mr); void *qemu_map_ram_ptr(RAMBlock *ram_block, ram_addr_t addr); /* Internal functions, part of the implementation of address_space_read_cached * and address_space_write_cached. */ void address_space_read_cached_slow(MemoryRegionCache *cache, - hwaddr addr, void *buf, int len); + hwaddr addr, void *buf, hwaddr len); void address_space_write_cached_slow(MemoryRegionCache *cache, - hwaddr addr, const void *buf, int len); + hwaddr addr, const void *buf, hwaddr len); static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) { @@ -2075,7 +2131,7 @@ static inline bool memory_access_is_direct(MemoryRegion *mr, bool is_write) static inline __attribute__((__always_inline__)) MemTxResult address_space_read(AddressSpace *as, hwaddr addr, MemTxAttrs attrs, uint8_t *buf, - int len) + hwaddr len) { MemTxResult result = MEMTX_OK; hwaddr l, addr1; @@ -2114,7 +2170,7 @@ MemTxResult address_space_read(AddressSpace *as, hwaddr addr, */ static inline void address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, - void *buf, int len) + void *buf, hwaddr len) { assert(addr < cache->len && len <= cache->len - addr); if (likely(cache->ptr)) { @@ -2134,7 +2190,7 @@ address_space_read_cached(MemoryRegionCache *cache, hwaddr addr, */ static inline void address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, - void *buf, int len) + void *buf, hwaddr len) { assert(addr < cache->len && len <= cache->len - addr); if (likely(cache->ptr)) { diff --git a/include/exec/poison.h b/include/exec/poison.h index 41cd2eb1d8..1a7a57baae 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -24,7 +24,6 @@ #pragma GCC poison TARGET_NIOS2 #pragma GCC poison TARGET_OPENRISC #pragma GCC poison TARGET_PPC -#pragma GCC poison TARGET_PPCEMB #pragma GCC poison TARGET_PPC64 #pragma GCC poison TARGET_ABI32 #pragma GCC poison TARGET_S390X @@ -76,16 +75,17 @@ #pragma GCC poison CONFIG_M68K_DIS #pragma GCC poison CONFIG_MICROBLAZE_DIS #pragma GCC poison CONFIG_MIPS_DIS +#pragma GCC poison CONFIG_NANOMIPS_DIS #pragma GCC poison CONFIG_MOXIE_DIS #pragma GCC poison CONFIG_NIOS2_DIS #pragma GCC poison CONFIG_PPC_DIS +#pragma GCC poison CONFIG_RISCV_DIS #pragma GCC poison CONFIG_S390_DIS #pragma GCC poison CONFIG_SH4_DIS #pragma GCC poison CONFIG_SPARC_DIS #pragma GCC poison CONFIG_XTENSA_DIS #pragma GCC poison CONFIG_LINUX_USER -#pragma GCC poison CONFIG_VHOST_NET #pragma GCC poison CONFIG_KVM #pragma GCC poison CONFIG_SOFTMMU diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 65b6522237..20d3a522fc 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -27,6 +27,7 @@ struct RAMBlock { struct rcu_head rcu; struct MemoryRegion *mr; uint8_t *host; + uint8_t *colo_cache; /* For colo, VM's ram cache */ ram_addr_t offset; ram_addr_t used_length; ram_addr_t max_length; @@ -70,13 +71,37 @@ static inline unsigned long int ramblock_recv_bitmap_offset(void *host_addr, return host_addr_offset >> TARGET_PAGE_BITS; } +bool ramblock_is_pmem(RAMBlock *rb); + long qemu_getrampagesize(void); + +/** + * qemu_ram_alloc_from_file, + * qemu_ram_alloc_from_fd: Allocate a ram block from the specified backing + * file or device + * + * Parameters: + * @size: the size in bytes of the ram block + * @mr: the memory region where the ram block is + * @ram_flags: specify the properties of the ram block, which can be one + * or bit-or of following values + * - RAM_SHARED: mmap the backing file or device with MAP_SHARED + * - RAM_PMEM: the backend @mem_path or @fd is persistent memory + * Other bits are ignored. + * @mem_path or @fd: specify the backing file or device + * @errp: pointer to Error*, to store an error if it happens + * + * Return: + * On success, return a pointer to the ram block. + * On failure, return NULL. + */ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr, - bool share, const char *mem_path, + uint32_t ram_flags, const char *mem_path, Error **errp); RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr, - bool share, int fd, + uint32_t ram_flags, int fd, 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, bool share, MemoryRegion *mr, diff --git a/include/exec/tb-hash.h b/include/exec/tb-hash.h index 0526c4f678..4f3a37d927 100644 --- a/include/exec/tb-hash.h +++ b/include/exec/tb-hash.h @@ -20,7 +20,7 @@ #ifndef EXEC_TB_HASH_H #define EXEC_TB_HASH_H -#include "exec/tb-hash-xx.h" +#include "qemu/xxhash.h" #ifdef CONFIG_SOFTMMU @@ -61,7 +61,7 @@ static inline uint32_t tb_hash_func(tb_page_addr_t phys_pc, target_ulong pc, uint32_t flags, uint32_t cf_mask, uint32_t trace_vcpu_dstate) { - return tb_hash_func7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate); + return qemu_xxhash7(phys_pc, pc, flags, cf_mask, trace_vcpu_dstate); } #endif diff --git a/include/exec/tb-lookup.h b/include/exec/tb-lookup.h index 492cb68289..26921b6daf 100644 --- a/include/exec/tb-lookup.h +++ b/include/exec/tb-lookup.h @@ -28,6 +28,10 @@ tb_lookup__cpu_state(CPUState *cpu, target_ulong *pc, target_ulong *cs_base, cpu_get_tb_cpu_state(env, pc, cs_base, flags); hash = tb_jmp_cache_hash_func(*pc); tb = atomic_rcu_read(&cpu->tb_jmp_cache[hash]); + + cf_mask &= ~CF_CLUSTER_MASK; + cf_mask |= cpu->cluster_index << CF_CLUSTER_SHIFT; + if (likely(tb && tb->pc == *pc && tb->cs_base == *cs_base && diff --git a/include/fpu/softfloat-macros.h b/include/fpu/softfloat-macros.h index 35e1603a5e..bd5b6418e3 100644 --- a/include/fpu/softfloat-macros.h +++ b/include/fpu/softfloat-macros.h @@ -79,17 +79,6 @@ this code that are retained. * version 2 or later. See the COPYING file in the top-level directory. */ -/*---------------------------------------------------------------------------- -| This macro tests for minimum version of the GNU C compiler. -*----------------------------------------------------------------------------*/ -#if defined(__GNUC__) && defined(__GNUC_MINOR__) -# define SOFTFLOAT_GNUC_PREREQ(maj, min) \ - ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min)) -#else -# define SOFTFLOAT_GNUC_PREREQ(maj, min) 0 -#endif - - /*---------------------------------------------------------------------------- | Shifts `a' right by the number of bits given in `count'. If any nonzero | bits are shifted off, they are ``jammed'' into the least significant bit of @@ -340,15 +329,30 @@ static inline void | pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. *----------------------------------------------------------------------------*/ -static inline void - shortShift128Left( - uint64_t a0, uint64_t a1, int count, uint64_t *z0Ptr, uint64_t *z1Ptr) +static inline void shortShift128Left(uint64_t a0, uint64_t a1, int count, + uint64_t *z0Ptr, uint64_t *z1Ptr) { + *z1Ptr = a1 << count; + *z0Ptr = count == 0 ? a0 : (a0 << count) | (a1 >> (-count & 63)); +} - *z1Ptr = a1<>( ( - count ) & 63 ) ); +/*---------------------------------------------------------------------------- +| Shifts the 128-bit value formed by concatenating `a0' and `a1' left by the +| number of bits given in `count'. Any bits shifted off are lost. The value +| of `count' may be greater than 64. The result is broken into two 64-bit +| pieces which are stored at the locations pointed to by `z0Ptr' and `z1Ptr'. +*----------------------------------------------------------------------------*/ +static inline void shift128Left(uint64_t a0, uint64_t a1, int count, + uint64_t *z0Ptr, uint64_t *z1Ptr) +{ + if (count < 64) { + *z1Ptr = a1 << count; + *z0Ptr = count == 0 ? a0 : (a0 << count) | (a1 >> (-count & 63)); + } else { + *z1Ptr = 0; + *z0Ptr = a1 << (count - 64); + } } /*---------------------------------------------------------------------------- @@ -630,8 +634,36 @@ static inline uint64_t estimateDiv128To64(uint64_t a0, uint64_t a1, uint64_t b) * * Licensed under the GPLv2/LGPLv3 */ -static inline uint64_t div128To64(uint64_t n0, uint64_t n1, uint64_t d) +static inline uint64_t udiv_qrnnd(uint64_t *r, uint64_t n1, + uint64_t n0, uint64_t d) { +#if defined(__x86_64__) + uint64_t q; + asm("divq %4" : "=a"(q), "=d"(*r) : "0"(n0), "1"(n1), "rm"(d)); + return q; +#elif defined(__s390x__) && !defined(__clang__) + /* Need to use a TImode type to get an even register pair for DLGR. */ + unsigned __int128 n = (unsigned __int128)n1 << 64 | n0; + asm("dlgr %0, %1" : "+r"(n) : "r"(d)); + *r = n >> 64; + return n; +#elif defined(_ARCH_PPC64) && defined(_ARCH_PWR7) + /* From Power ISA 2.06, programming note for divdeu. */ + uint64_t q1, q2, Q, r1, r2, R; + asm("divdeu %0,%2,%4; divdu %1,%3,%4" + : "=&r"(q1), "=r"(q2) + : "r"(n1), "r"(n0), "r"(d)); + r1 = -(q1 * d); /* low part of (n1<<64) - (q1 * d) */ + r2 = n0 - (q2 * d); + Q = q1 + q2; + R = r1 + r2; + if (R >= d || R < r2) { /* overflow implies R > d */ + Q += 1; + R -= d; + } + *r = R; + return Q; +#else uint64_t d0, d1, q0, q1, r1, r0, m; d0 = (uint32_t)d; @@ -669,8 +701,9 @@ static inline uint64_t div128To64(uint64_t n0, uint64_t n1, uint64_t d) } r0 -= m; - /* Return remainder in LSB */ - return (q1 << 32) | q0 | (r0 != 0); + *r = r0; + return (q1 << 32) | q0; +#endif } /*---------------------------------------------------------------------------- @@ -712,82 +745,6 @@ static inline uint32_t estimateSqrt32(int aExp, uint32_t a) } -/*---------------------------------------------------------------------------- -| Returns the number of leading 0 bits before the most-significant 1 bit of -| `a'. If `a' is zero, 32 is returned. -*----------------------------------------------------------------------------*/ - -static inline int8_t countLeadingZeros32(uint32_t a) -{ -#if SOFTFLOAT_GNUC_PREREQ(3, 4) - if (a) { - return __builtin_clz(a); - } else { - return 32; - } -#else - 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, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 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, - 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, - 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, - 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_t shiftCount; - - shiftCount = 0; - if ( a < 0x10000 ) { - shiftCount += 16; - a <<= 16; - } - if ( a < 0x1000000 ) { - shiftCount += 8; - a <<= 8; - } - shiftCount += countLeadingZerosHigh[ a>>24 ]; - return shiftCount; -#endif -} - -/*---------------------------------------------------------------------------- -| Returns the number of leading 0 bits before the most-significant 1 bit of -| `a'. If `a' is zero, 64 is returned. -*----------------------------------------------------------------------------*/ - -static inline int8_t countLeadingZeros64(uint64_t a) -{ -#if SOFTFLOAT_GNUC_PREREQ(3, 4) - if (a) { - return __builtin_clzll(a); - } else { - return 64; - } -#else - int8_t shiftCount; - - shiftCount = 0; - if ( a < ( (uint64_t) 1 )<<32 ) { - shiftCount += 32; - } - else { - a >>= 32; - } - shiftCount += countLeadingZeros32( a ); - return shiftCount; -#endif -} - /*---------------------------------------------------------------------------- | Returns 1 if the 128-bit value formed by concatenating `a0' and `a1' | is equal to the 128-bit value formed by concatenating `b0' and `b1'. diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index 69f4dbc4db..3ff3fa5224 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -190,43 +190,14 @@ enum { /*---------------------------------------------------------------------------- | Software IEC/IEEE integer-to-floating-point conversion routines. *----------------------------------------------------------------------------*/ -float32 int16_to_float32(int16_t, float_status *status); -float32 int32_to_float32(int32_t, float_status *status); -float64 int16_to_float64(int16_t, float_status *status); -float64 int32_to_float64(int32_t, float_status *status); -float32 uint16_to_float32(uint16_t, float_status *status); -float32 uint32_to_float32(uint32_t, float_status *status); -float64 uint16_to_float64(uint16_t, float_status *status); -float64 uint32_to_float64(uint32_t, float_status *status); -floatx80 int32_to_floatx80(int32_t, float_status *status); -float128 int32_to_float128(int32_t, float_status *status); -float32 int64_to_float32(int64_t, float_status *status); -float64 int64_to_float64(int64_t, float_status *status); -floatx80 int64_to_floatx80(int64_t, float_status *status); -float128 int64_to_float128(int64_t, float_status *status); -float32 uint64_to_float32(uint64_t, float_status *status); -float64 uint64_to_float64(uint64_t, float_status *status); -float128 uint64_to_float128(uint64_t, float_status *status); -/*---------------------------------------------------------------------------- -| Software half-precision conversion routines. -*----------------------------------------------------------------------------*/ -float16 float32_to_float16(float32, bool ieee, float_status *status); -float32 float16_to_float32(float16, bool ieee, float_status *status); -float16 float64_to_float16(float64 a, bool ieee, float_status *status); -float64 float16_to_float64(float16 a, bool ieee, float_status *status); -int16_t float16_to_int16(float16, float_status *status); -uint16_t float16_to_uint16(float16 a, float_status *status); -int16_t float16_to_int16_round_to_zero(float16, float_status *status); -uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *status); -int32_t float16_to_int32(float16, float_status *status); -uint32_t float16_to_uint32(float16 a, float_status *status); -int32_t float16_to_int32_round_to_zero(float16, float_status *status); -uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *status); -int64_t float16_to_int64(float16, float_status *status); -uint64_t float16_to_uint64(float16 a, float_status *status); -int64_t float16_to_int64_round_to_zero(float16, float_status *status); -uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *status); +float16 int16_to_float16_scalbn(int16_t a, int, float_status *status); +float16 int32_to_float16_scalbn(int32_t a, int, float_status *status); +float16 int64_to_float16_scalbn(int64_t a, int, float_status *status); +float16 uint16_to_float16_scalbn(uint16_t a, int, float_status *status); +float16 uint32_to_float16_scalbn(uint32_t a, int, float_status *status); +float16 uint64_to_float16_scalbn(uint64_t a, int, float_status *status); + float16 int16_to_float16(int16_t a, float_status *status); float16 int32_to_float16(int32_t a, float_status *status); float16 int64_to_float16(int64_t a, float_status *status); @@ -234,6 +205,74 @@ float16 uint16_to_float16(uint16_t a, float_status *status); float16 uint32_to_float16(uint32_t a, float_status *status); float16 uint64_to_float16(uint64_t a, float_status *status); +float32 int16_to_float32_scalbn(int16_t, int, float_status *status); +float32 int32_to_float32_scalbn(int32_t, int, float_status *status); +float32 int64_to_float32_scalbn(int64_t, int, float_status *status); +float32 uint16_to_float32_scalbn(uint16_t, int, float_status *status); +float32 uint32_to_float32_scalbn(uint32_t, int, float_status *status); +float32 uint64_to_float32_scalbn(uint64_t, int, float_status *status); + +float32 int16_to_float32(int16_t, float_status *status); +float32 int32_to_float32(int32_t, float_status *status); +float32 int64_to_float32(int64_t, float_status *status); +float32 uint16_to_float32(uint16_t, float_status *status); +float32 uint32_to_float32(uint32_t, float_status *status); +float32 uint64_to_float32(uint64_t, float_status *status); + +float64 int16_to_float64_scalbn(int16_t, int, float_status *status); +float64 int32_to_float64_scalbn(int32_t, int, float_status *status); +float64 int64_to_float64_scalbn(int64_t, int, float_status *status); +float64 uint16_to_float64_scalbn(uint16_t, int, float_status *status); +float64 uint32_to_float64_scalbn(uint32_t, int, float_status *status); +float64 uint64_to_float64_scalbn(uint64_t, int, float_status *status); + +float64 int16_to_float64(int16_t, float_status *status); +float64 int32_to_float64(int32_t, float_status *status); +float64 int64_to_float64(int64_t, float_status *status); +float64 uint16_to_float64(uint16_t, float_status *status); +float64 uint32_to_float64(uint32_t, float_status *status); +float64 uint64_to_float64(uint64_t, float_status *status); + +floatx80 int32_to_floatx80(int32_t, float_status *status); +floatx80 int64_to_floatx80(int64_t, float_status *status); + +float128 int32_to_float128(int32_t, float_status *status); +float128 int64_to_float128(int64_t, float_status *status); +float128 uint64_to_float128(uint64_t, float_status *status); + +/*---------------------------------------------------------------------------- +| Software half-precision conversion routines. +*----------------------------------------------------------------------------*/ + +float16 float32_to_float16(float32, bool ieee, float_status *status); +float32 float16_to_float32(float16, bool ieee, float_status *status); +float16 float64_to_float16(float64 a, bool ieee, float_status *status); +float64 float16_to_float64(float16 a, bool ieee, float_status *status); + +int16_t float16_to_int16_scalbn(float16, int, int, float_status *status); +int32_t float16_to_int32_scalbn(float16, int, int, float_status *status); +int64_t float16_to_int64_scalbn(float16, int, int, float_status *status); + +int16_t float16_to_int16(float16, float_status *status); +int32_t float16_to_int32(float16, float_status *status); +int64_t float16_to_int64(float16, float_status *status); + +int16_t float16_to_int16_round_to_zero(float16, float_status *status); +int32_t float16_to_int32_round_to_zero(float16, float_status *status); +int64_t float16_to_int64_round_to_zero(float16, float_status *status); + +uint16_t float16_to_uint16_scalbn(float16 a, int, int, float_status *status); +uint32_t float16_to_uint32_scalbn(float16 a, int, int, float_status *status); +uint64_t float16_to_uint64_scalbn(float16 a, int, int, float_status *status); + +uint16_t float16_to_uint16(float16 a, float_status *status); +uint32_t float16_to_uint32(float16 a, float_status *status); +uint64_t float16_to_uint64(float16 a, float_status *status); + +uint16_t float16_to_uint16_round_to_zero(float16 a, float_status *status); +uint32_t float16_to_uint32_round_to_zero(float16 a, float_status *status); +uint64_t float16_to_uint64_round_to_zero(float16 a, float_status *status); + /*---------------------------------------------------------------------------- | Software half-precision operations. *----------------------------------------------------------------------------*/ @@ -321,18 +360,31 @@ float16 float16_default_nan(float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. *----------------------------------------------------------------------------*/ + +int16_t float32_to_int16_scalbn(float32, int, int, float_status *status); +int32_t float32_to_int32_scalbn(float32, int, int, float_status *status); +int64_t float32_to_int64_scalbn(float32, int, int, 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); + +int16_t float32_to_int16_round_to_zero(float32, float_status *status); +int32_t float32_to_int32_round_to_zero(float32, float_status *status); int64_t float32_to_int64_round_to_zero(float32, float_status *status); + +uint16_t float32_to_uint16_scalbn(float32, int, int, float_status *status); +uint32_t float32_to_uint32_scalbn(float32, int, int, float_status *status); +uint64_t float32_to_uint64_scalbn(float32, int, int, float_status *status); + +uint16_t float32_to_uint16(float32, float_status *status); +uint32_t float32_to_uint32(float32, float_status *status); +uint64_t float32_to_uint64(float32, float_status *status); + +uint16_t float32_to_uint16_round_to_zero(float32, float_status *status); +uint32_t float32_to_uint32_round_to_zero(float32, float_status *status); +uint64_t float32_to_uint64_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); @@ -412,6 +464,21 @@ static inline int float32_is_zero_or_denormal(float32 a) return (float32_val(a) & 0x7f800000) == 0; } +static inline bool float32_is_normal(float32 a) +{ + return (((float32_val(a) >> 23) + 1) & 0xff) >= 2; +} + +static inline bool float32_is_denormal(float32 a) +{ + return float32_is_zero_or_denormal(a) && !float32_is_zero(a); +} + +static inline bool float32_is_zero_or_normal(float32 a) +{ + return float32_is_normal(a) || float32_is_zero(a); +} + static inline float32 float32_set_sign(float32 a, int sign) { return make_float32((float32_val(a) & 0x7fffffff) | (sign << 31)); @@ -450,18 +517,31 @@ float32 float32_default_nan(float_status *status); /*---------------------------------------------------------------------------- | Software IEC/IEEE double-precision conversion routines. *----------------------------------------------------------------------------*/ + +int16_t float64_to_int16_scalbn(float64, int, int, float_status *status); +int32_t float64_to_int32_scalbn(float64, int, int, float_status *status); +int64_t float64_to_int64_scalbn(float64, int, int, 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); + +int16_t float64_to_int16_round_to_zero(float64, float_status *status); +int32_t float64_to_int32_round_to_zero(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); + +uint16_t float64_to_uint16_scalbn(float64, int, int, float_status *status); +uint32_t float64_to_uint32_scalbn(float64, int, int, float_status *status); +uint64_t float64_to_uint64_scalbn(float64, int, int, float_status *status); + +uint16_t float64_to_uint16(float64, float_status *status); +uint32_t float64_to_uint32(float64, float_status *status); +uint64_t float64_to_uint64(float64, float_status *status); + +uint16_t float64_to_uint16_round_to_zero(float64, float_status *status); +uint32_t float64_to_uint32_round_to_zero(float64, float_status *status); +uint64_t float64_to_uint64_round_to_zero(float64, 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); @@ -470,7 +550,6 @@ float128 float64_to_float128(float64, float_status *status); | Software IEC/IEEE double-precision operations. *----------------------------------------------------------------------------*/ float64 float64_round_to_int(float64, float_status *status); -float64 float64_trunc_to_int(float64, float_status *status); float64 float64_add(float64, float64, float_status *status); float64 float64_sub(float64, float64, float_status *status); float64 float64_mul(float64, float64, float_status *status); @@ -541,6 +620,21 @@ static inline int float64_is_zero_or_denormal(float64 a) return (float64_val(a) & 0x7ff0000000000000LL) == 0; } +static inline bool float64_is_normal(float64 a) +{ + return (((float64_val(a) >> 52) + 1) & 0x7ff) >= 2; +} + +static inline bool float64_is_denormal(float64 a) +{ + return float64_is_zero_or_denormal(a) && !float64_is_zero(a); +} + +static inline bool float64_is_zero_or_normal(float64 a) +{ + return float64_is_normal(a) || float64_is_zero(a); +} + static inline float64 float64_set_sign(float64 a, int sign) { return make_float64((float64_val(a) & 0x7fffffffffffffffULL) @@ -784,6 +878,7 @@ int64_t float128_to_int64(float128, float_status *status); int64_t float128_to_int64_round_to_zero(float128, float_status *status); uint64_t float128_to_uint64(float128, float_status *status); uint64_t float128_to_uint64_round_to_zero(float128, float_status *status); +uint32_t float128_to_uint32(float128, float_status *status); uint32_t float128_to_uint32_round_to_zero(float128, float_status *status); float32 float128_to_float32(float128, float_status *status); float64 float128_to_float64(float128, float_status *status); @@ -846,6 +941,16 @@ static inline int float128_is_zero_or_denormal(float128 a) return (a.high & 0x7fff000000000000LL) == 0; } +static inline bool float128_is_normal(float128 a) +{ + return (((a.high >> 48) + 1) & 0x7fff) >= 2; +} + +static inline bool float128_is_denormal(float128 a) +{ + return float128_is_zero_or_denormal(a) && !float128_is_zero(a); +} + static inline int float128_is_any_nan(float128 a) { return ((a.high >> 48) & 0x7fff) == 0x7fff && diff --git a/include/glib-compat.h b/include/glib-compat.h index fdf95a255d..1291628e09 100644 --- a/include/glib-compat.h +++ b/include/glib-compat.h @@ -83,7 +83,6 @@ static inline gboolean g_strv_contains_qemu(const gchar *const *strv, } #define g_strv_contains(a, b) g_strv_contains_qemu(a, b) - #if defined(_WIN32) && !GLIB_CHECK_VERSION(2, 50, 0) /* * g_poll has a problem on Windows when using diff --git a/include/hw/acpi/acpi-defs.h b/include/hw/acpi/acpi-defs.h index af8e023968..f9aa4bd398 100644 --- a/include/hw/acpi/acpi-defs.h +++ b/include/hw/acpi/acpi-defs.h @@ -40,18 +40,13 @@ enum { ACPI_FADT_F_LOW_POWER_S0_IDLE_CAPABLE, }; -struct AcpiRsdpDescriptor { /* Root System Descriptor Pointer */ - uint64_t signature; /* ACPI signature, contains "RSD PTR " */ - uint8_t checksum; /* To make sum of struct == 0 */ - uint8_t oem_id [6]; /* OEM identification */ - uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */ - uint32_t rsdt_physical_address; /* 32-bit physical address of RSDT */ - uint32_t length; /* XSDT Length in bytes including hdr */ - uint64_t xsdt_physical_address; /* 64-bit physical address of XSDT */ - uint8_t extended_checksum; /* Checksum of entire table */ - uint8_t reserved [3]; /* Reserved field must be 0 */ -} QEMU_PACKED; -typedef struct AcpiRsdpDescriptor AcpiRsdpDescriptor; +typedef struct AcpiRsdpData { + uint8_t oem_id[6] QEMU_NONSTRING; /* OEM identification */ + uint8_t revision; /* Must be 0 for 1.0, 2 for 2.0 */ + + unsigned *rsdt_tbl_offset; + unsigned *xsdt_tbl_offset; +} AcpiRsdpData; /* Table structure from Linux kernel (the ACPI tables are under the BSD license) */ @@ -62,10 +57,13 @@ typedef struct AcpiRsdpDescriptor AcpiRsdpDescriptor; uint32_t length; /* Length of table, in bytes, including header */ \ uint8_t revision; /* ACPI Specification minor version # */ \ uint8_t checksum; /* To make sum of entire table == 0 */ \ - uint8_t oem_id [6]; /* OEM identification */ \ - uint8_t oem_table_id [8]; /* OEM table identification */ \ + uint8_t oem_id[6] \ + QEMU_NONSTRING; /* OEM identification */ \ + uint8_t oem_table_id[8] \ + QEMU_NONSTRING; /* OEM table identification */ \ uint32_t oem_revision; /* OEM revision number */ \ - uint8_t asl_compiler_id [4]; /* ASL compiler vendor ID */ \ + uint8_t asl_compiler_id[4] \ + QEMU_NONSTRING; /* ASL compiler vendor ID */ \ uint32_t asl_compiler_revision; /* ASL compiler revision number */ @@ -628,6 +626,8 @@ struct AcpiIortItsGroup { } QEMU_PACKED; typedef struct AcpiIortItsGroup AcpiIortItsGroup; +#define ACPI_IORT_SMMU_V3_COHACC_OVERRIDE 1 + struct AcpiIortSmmu3 { ACPI_IORT_NODE_HEADER_DEF uint64_t base_address; diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index 58c5765d98..f68310a124 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -73,13 +73,13 @@ #define ACPI_BITMASK_WAKE_STATUS 0x8000 #define ACPI_BITMASK_ALL_FIXED_STATUS (\ - ACPI_BITMASK_TIMER_STATUS | \ - ACPI_BITMASK_BUS_MASTER_STATUS | \ - ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ - ACPI_BITMASK_POWER_BUTTON_STATUS | \ - ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ - ACPI_BITMASK_RT_CLOCK_STATUS | \ - ACPI_BITMASK_WAKE_STATUS) + ACPI_BITMASK_TIMER_STATUS | \ + ACPI_BITMASK_BUS_MASTER_STATUS | \ + ACPI_BITMASK_GLOBAL_LOCK_STATUS | \ + ACPI_BITMASK_POWER_BUTTON_STATUS | \ + ACPI_BITMASK_SLEEP_BUTTON_STATUS | \ + ACPI_BITMASK_RT_CLOCK_STATUS | \ + ACPI_BITMASK_WAKE_STATUS) /* PM1x_EN */ #define ACPI_BITMASK_TIMER_ENABLE 0x0001 @@ -194,7 +194,6 @@ uint8_t *acpi_table_first(void); uint8_t *acpi_table_next(uint8_t *current); 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 { diff --git a/include/hw/acpi/acpi_dev_interface.h b/include/hw/acpi/acpi_dev_interface.h index dabf4c4fc9..43ff119179 100644 --- a/include/hw/acpi/acpi_dev_interface.h +++ b/include/hw/acpi/acpi_dev_interface.h @@ -25,11 +25,7 @@ typedef enum { INTERFACE_CHECK(AcpiDeviceIf, (obj), \ TYPE_ACPI_DEVICE_IF) - -typedef struct AcpiDeviceIf { - /* */ - Object Parent; -} AcpiDeviceIf; +typedef struct AcpiDeviceIf AcpiDeviceIf; void acpi_send_event(DeviceState *dev, AcpiEventStatusBits event); diff --git a/include/hw/acpi/aml-build.h b/include/hw/acpi/aml-build.h index 6c36903c0a..1a563ad756 100644 --- a/include/hw/acpi/aml-build.h +++ b/include/hw/acpi/aml-build.h @@ -388,6 +388,8 @@ 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_rsdp(GArray *tbl, BIOSLinker *linker, AcpiRsdpData *rsdp_data); +void build_rsdt(GArray *table_data, BIOSLinker *linker, GArray *table_offsets, const char *oem_id, const char *oem_table_id); void diff --git a/include/hw/acpi/cpu.h b/include/hw/acpi/cpu.h index 89ce172941..62f0278ba2 100644 --- a/include/hw/acpi/cpu.h +++ b/include/hw/acpi/cpu.h @@ -48,7 +48,7 @@ void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, CPUHotplugState *state, hwaddr base_addr); typedef struct CPUHotplugFeatures { - bool apci_1_compatible; + bool acpi_1_compatible; bool has_legacy_cphp; } CPUHotplugFeatures; diff --git a/include/hw/acpi/ich9.h b/include/hw/acpi/ich9.h index 59aeb06393..41568d1837 100644 --- a/include/hw/acpi/ich9.h +++ b/include/hw/acpi/ich9.h @@ -74,6 +74,8 @@ extern const VMStateDescription vmstate_ich9_pm; void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm, Error **errp); +void ich9_pm_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); void ich9_pm_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void ich9_pm_device_unplug_request_cb(HotplugHandler *hotplug_dev, diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 8a65f99fc8..8bc4a4c01d 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -56,10 +56,15 @@ typedef struct AcpiPciHpState { void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root, MemoryRegion *address_space_io, bool bridges_enabled); +void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); +void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, + AcpiPciHpState *s, DeviceState *dev, + Error **errp); /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); diff --git a/include/hw/acpi/tpm.h b/include/hw/acpi/tpm.h index 3580ffd50c..1a2a57a21f 100644 --- a/include/hw/acpi/tpm.h +++ b/include/hw/acpi/tpm.h @@ -18,6 +18,8 @@ #include "qemu/units.h" #include "hw/registerfields.h" +#include "hw/acpi/aml-build.h" +#include "sysemu/tpm.h" #define TPM_TIS_ADDR_BASE 0xFED40000 #define TPM_TIS_ADDR_SIZE 0x5000 @@ -188,4 +190,23 @@ REG32(CRB_DATA_BUFFER, 0x80) #define TPM2_START_METHOD_MMIO 6 #define TPM2_START_METHOD_CRB 7 +/* + * Physical Presence Interface + */ +#define TPM_PPI_ADDR_SIZE 0x400 +#define TPM_PPI_ADDR_BASE 0xFED45000 + +#define TPM_PPI_VERSION_NONE 0 +#define TPM_PPI_VERSION_1_30 1 + +/* whether function is blocked by BIOS settings; bits 0, 1, 2 */ +#define TPM_PPI_FUNC_NOT_IMPLEMENTED (0 << 0) +#define TPM_PPI_FUNC_BIOS_ONLY (1 << 0) +#define TPM_PPI_FUNC_BLOCKED (2 << 0) +#define TPM_PPI_FUNC_ALLOWED_USR_REQ (3 << 0) +#define TPM_PPI_FUNC_ALLOWED_USR_NOT_REQ (4 << 0) +#define TPM_PPI_FUNC_MASK (7 << 0) + +void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev); + #endif /* HW_ACPI_TPM_H */ diff --git a/include/hw/arm/allwinner-a10.h b/include/hw/arm/allwinner-a10.h index efb8fc8123..389e128d0f 100644 --- a/include/hw/arm/allwinner-a10.h +++ b/include/hw/arm/allwinner-a10.h @@ -35,6 +35,7 @@ typedef struct AwA10State { AwA10PICState intc; AwEmacState emac; AllwinnerAHCIState sata; + MemoryRegion sram_a; } AwA10State; #define ALLWINNER_H_ diff --git a/include/hw/arm/armsse.h b/include/hw/arm/armsse.h new file mode 100644 index 0000000000..81e082cccf --- /dev/null +++ b/include/hw/arm/armsse.h @@ -0,0 +1,225 @@ +/* + * ARM SSE (Subsystems for Embedded): IoTKit, SSE-200 + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the Arm "Subsystems for Embedded" family of + * hardware, which include the IoT Kit and the SSE-050, SSE-100 and + * SSE-200. Currently we model: + * - the Arm IoT Kit which is documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * - the SSE-200 which is documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * + * The IoTKit contains: + * a Cortex-M33 + * the IDAU + * some timers and watchdogs + * two peripheral protection controllers + * a memory protection controller + * a security controller + * a bus fabric which arranges that some parts of the address + * space are secure and non-secure aliases of each other + * The SSE-200 additionally contains: + * a second Cortex-M33 + * two Message Handling Units (MHUs) + * an optional CryptoCell (which we do not model) + * more SRAM banks with associated MPCs + * multiple Power Policy Units (PPUs) + * a control interface for an icache for each CPU + * per-CPU identity and control register blocks + * + * QEMU interface: + * + QOM property "memory" is a MemoryRegion containing the devices provided + * by the board model. + * + QOM property "MAINCLK" is the frequency of the main system clock + * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts. + * (In hardware, the SSE-200 permits the number of expansion interrupts + * for the two CPUs to be configured separately, but we restrict it to + * being the same for both, to avoid having to have separate Property + * lists for different variants. This restriction can be relaxed later + * if necessary.) + * + QOM property "SRAM_ADDR_WIDTH" sets the number of bits used for the + * address of each SRAM bank (and thus the total amount of internal SRAM) + * + QOM property "init-svtor" sets the initial value of the CPU SVTOR register + * (where it expects to load the PC and SP from the vector table on reset) + * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts for CPU 0, + * which are wired to its NVIC lines 32 .. n+32 + * + Named GPIO inputs "EXP_CPU1_IRQ" 0..n are the expansion interrupts for + * CPU 1, which are wired to its NVIC lines 32 .. n+32 + * + sysbus MMIO region 0 is the "AHB Slave Expansion" which allows + * bus master devices in the board model to make transactions into + * all the devices and memory areas in the IoTKit + * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit + * might provide: + * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs apb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 4 expansion AHB PPCs which a system using the IoTKit + * might provide: + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_nonsec[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_ap[0..15] + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable + * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear + * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status + * Controlling each of the 16 expansion MPCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] + */ + +#ifndef ARMSSE_H +#define ARMSSE_H + +#include "hw/sysbus.h" +#include "hw/arm/armv7m.h" +#include "hw/misc/iotkit-secctl.h" +#include "hw/misc/tz-ppc.h" +#include "hw/misc/tz-mpc.h" +#include "hw/timer/cmsdk-apb-timer.h" +#include "hw/timer/cmsdk-apb-dualtimer.h" +#include "hw/watchdog/cmsdk-apb-watchdog.h" +#include "hw/misc/iotkit-sysctl.h" +#include "hw/misc/iotkit-sysinfo.h" +#include "hw/misc/armsse-cpuid.h" +#include "hw/misc/armsse-mhu.h" +#include "hw/misc/unimp.h" +#include "hw/or-irq.h" +#include "hw/core/split-irq.h" +#include "hw/cpu/cluster.h" + +#define TYPE_ARMSSE "arm-sse" +#define ARMSSE(obj) OBJECT_CHECK(ARMSSE, (obj), TYPE_ARMSSE) + +/* + * These type names are for specific IoTKit subsystems; other than + * instantiating them, code using these devices should always handle + * them via the ARMSSE base class, so they have no IOTKIT() etc macros. + */ +#define TYPE_IOTKIT "iotkit" +#define TYPE_SSE200 "sse-200" + +/* We have an IRQ splitter and an OR gate input for each external PPC + * and the 2 internal PPCs + */ +#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) +#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) + +#define MAX_SRAM_BANKS 4 +#if MAX_SRAM_BANKS > IOTS_NUM_MPC +#error Too many SRAM banks +#endif + +#define SSE_MAX_CPUS 2 + +/* These define what each PPU in the ppu[] index is for */ +#define CPU0CORE_PPU 0 +#define CPU1CORE_PPU 1 +#define DBG_PPU 2 +#define RAM0_PPU 3 +#define RAM1_PPU 4 +#define RAM2_PPU 5 +#define RAM3_PPU 6 +#define NUM_PPUS 7 + +typedef struct ARMSSE { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + ARMv7MState armv7m[SSE_MAX_CPUS]; + CPUClusterState cluster[SSE_MAX_CPUS]; + IoTKitSecCtl secctl; + TZPPC apb_ppc0; + TZPPC apb_ppc1; + TZMPC mpc[IOTS_NUM_MPC]; + CMSDKAPBTIMER timer0; + CMSDKAPBTIMER timer1; + CMSDKAPBTIMER s32ktimer; + qemu_or_irq ppc_irq_orgate; + SplitIRQ sec_resp_splitter; + SplitIRQ ppc_irq_splitter[NUM_PPCS]; + SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; + qemu_or_irq mpc_irq_orgate; + qemu_or_irq nmi_orgate; + + SplitIRQ cpu_irq_splitter[32]; + + CMSDKAPBDualTimer dualtimer; + + CMSDKAPBWatchdog s32kwatchdog; + CMSDKAPBWatchdog nswatchdog; + CMSDKAPBWatchdog swatchdog; + + IoTKitSysCtl sysctl; + IoTKitSysCtl sysinfo; + + ARMSSEMHU mhu[2]; + UnimplementedDeviceState ppu[NUM_PPUS]; + UnimplementedDeviceState cachectrl[SSE_MAX_CPUS]; + UnimplementedDeviceState cpusecctrl[SSE_MAX_CPUS]; + + ARMSSECPUID cpuid[SSE_MAX_CPUS]; + + /* + * 'container' holds all devices seen by all CPUs. + * 'cpu_container[i]' is the view that CPU i has: this has the + * per-CPU devices of that CPU, plus as the background 'container' + * (or an alias of it, since we can only use it directly once). + * container_alias[i] is the alias of 'container' used by CPU i+1; + * CPU 0 can use 'container' directly. + */ + MemoryRegion container; + MemoryRegion container_alias[SSE_MAX_CPUS - 1]; + MemoryRegion cpu_container[SSE_MAX_CPUS]; + MemoryRegion alias1; + MemoryRegion alias2; + MemoryRegion alias3[SSE_MAX_CPUS]; + MemoryRegion sram[MAX_SRAM_BANKS]; + + qemu_irq *exp_irqs[SSE_MAX_CPUS]; + qemu_irq ppc0_irq; + qemu_irq ppc1_irq; + qemu_irq sec_resp_cfg; + qemu_irq sec_resp_cfg_in; + qemu_irq nsc_cfg_in; + + qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; + qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; + + uint32_t nsccfg; + + /* Properties */ + MemoryRegion *board_memory; + uint32_t exp_numirq; + uint32_t mainclk_frq; + uint32_t sram_addr_width; + uint32_t init_svtor; +} ARMSSE; + +typedef struct ARMSSEInfo ARMSSEInfo; + +typedef struct ARMSSEClass { + DeviceClass parent_class; + const ARMSSEInfo *info; +} ARMSSEClass; + +#define ARMSSE_CLASS(klass) \ + OBJECT_CLASS_CHECK(ARMSSEClass, (klass), TYPE_ARMSSE) +#define ARMSSE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(ARMSSEClass, (obj), TYPE_ARMSSE) + +#endif diff --git a/include/hw/arm/armv7m.h b/include/hw/arm/armv7m.h index 78308d1484..e96a98f809 100644 --- a/include/hw/arm/armv7m.h +++ b/include/hw/arm/armv7m.h @@ -43,6 +43,7 @@ typedef struct { * devices will be automatically layered on top of this view.) * + Property "idau": IDAU interface (forwarded to CPU object) * + Property "init-svtor": secure VTOR reset value (forwarded to CPU object) + * + Property "enable-bitband": expose bitbanded IO */ typedef struct ARMv7MState { /*< private >*/ @@ -63,6 +64,8 @@ typedef struct ARMv7MState { MemoryRegion *board_memory; Object *idau; uint32_t init_svtor; + bool enable_bitband; + bool start_powered_off; } ARMv7MState; #endif diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h new file mode 100644 index 0000000000..325c091d09 --- /dev/null +++ b/include/hw/arm/aspeed.h @@ -0,0 +1,46 @@ +/* + * Aspeed Machines + * + * Copyright 2018 IBM Corp. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ +#ifndef ARM_ASPEED_H +#define ARM_ASPEED_H + +#include "hw/boards.h" + +typedef struct AspeedBoardState AspeedBoardState; + +typedef struct AspeedBoardConfig { + const char *name; + const char *desc; + const char *soc_name; + uint32_t hw_strap1; + const char *fmc_model; + const char *spi_model; + uint32_t num_cs; + void (*i2c_init)(AspeedBoardState *bmc); +} AspeedBoardConfig; + +#define TYPE_ASPEED_MACHINE MACHINE_TYPE_NAME("aspeed") +#define ASPEED_MACHINE(obj) \ + OBJECT_CHECK(AspeedMachine, (obj), TYPE_ASPEED_MACHINE) + +typedef struct AspeedMachine { + MachineState parent_obj; +} AspeedMachine; + +#define ASPEED_MACHINE_CLASS(klass) \ + OBJECT_CLASS_CHECK(AspeedMachineClass, (klass), TYPE_ASPEED_MACHINE) +#define ASPEED_MACHINE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AspeedMachineClass, (obj), TYPE_ASPEED_MACHINE) + +typedef struct AspeedMachineClass { + MachineClass parent_obj; + const AspeedBoardConfig *board; +} AspeedMachineClass; + + +#endif diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h new file mode 100644 index 0000000000..5897217194 --- /dev/null +++ b/include/hw/arm/fsl-imx6ul.h @@ -0,0 +1,339 @@ +/* + * Copyright (c) 2018 Jean-Christophe Dubois + * + * i.MX6ul SoC definitions + * + * 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_IMX6UL_H +#define FSL_IMX6UL_H + +#include "hw/arm/arm.h" +#include "hw/cpu/a15mpcore.h" +#include "hw/misc/imx6ul_ccm.h" +#include "hw/misc/imx6_src.h" +#include "hw/misc/imx7_snvs.h" +#include "hw/misc/imx7_gpr.h" +#include "hw/intc/imx_gpcv2.h" +#include "hw/misc/imx2_wdt.h" +#include "hw/gpio/imx_gpio.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 "hw/sd/sdhci.h" +#include "hw/ssi/imx_spi.h" +#include "hw/net/imx_fec.h" +#include "exec/memory.h" +#include "cpu.h" + +#define TYPE_FSL_IMX6UL "fsl,imx6ul" +#define FSL_IMX6UL(obj) OBJECT_CHECK(FslIMX6ULState, (obj), TYPE_FSL_IMX6UL) + +enum FslIMX6ULConfiguration { + FSL_IMX6UL_NUM_CPUS = 1, + FSL_IMX6UL_NUM_UARTS = 8, + FSL_IMX6UL_NUM_ETHS = 2, + FSL_IMX6UL_ETH_NUM_TX_RINGS = 2, + FSL_IMX6UL_NUM_USDHCS = 2, + FSL_IMX6UL_NUM_WDTS = 3, + FSL_IMX6UL_NUM_GPTS = 2, + FSL_IMX6UL_NUM_EPITS = 2, + FSL_IMX6UL_NUM_IOMUXCS = 2, + FSL_IMX6UL_NUM_GPIOS = 5, + FSL_IMX6UL_NUM_I2CS = 4, + FSL_IMX6UL_NUM_ECSPIS = 4, + FSL_IMX6UL_NUM_ADCS = 2, +}; + +typedef struct FslIMX6ULState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + ARMCPU cpu[FSL_IMX6UL_NUM_CPUS]; + A15MPPrivState a7mpcore; + IMXGPTState gpt[FSL_IMX6UL_NUM_GPTS]; + IMXEPITState epit[FSL_IMX6UL_NUM_EPITS]; + IMXGPIOState gpio[FSL_IMX6UL_NUM_GPIOS]; + IMX6ULCCMState ccm; + IMX6SRCState src; + IMX7SNVSState snvs; + IMXGPCv2State gpcv2; + IMX7GPRState gpr; + IMXSPIState spi[FSL_IMX6UL_NUM_ECSPIS]; + IMXI2CState i2c[FSL_IMX6UL_NUM_I2CS]; + IMXSerialState uart[FSL_IMX6UL_NUM_UARTS]; + IMXFECState eth[FSL_IMX6UL_NUM_ETHS]; + SDHCIState usdhc[FSL_IMX6UL_NUM_USDHCS]; + IMX2WdtState wdt[FSL_IMX6UL_NUM_WDTS]; + MemoryRegion rom; + MemoryRegion caam; + MemoryRegion ocram; + MemoryRegion ocram_alias; +} FslIMX6ULState; + +enum FslIMX6ULMemoryMap { + FSL_IMX6UL_MMDC_ADDR = 0x80000000, + FSL_IMX6UL_MMDC_SIZE = 2 * 1024 * 1024 * 1024UL, + + FSL_IMX6UL_QSPI1_MEM_ADDR = 0x60000000, + FSL_IMX6UL_EIM_ALIAS_ADDR = 0x58000000, + FSL_IMX6UL_EIM_CS_ADDR = 0x50000000, + FSL_IMX6UL_AES_ENCRYPT_ADDR = 0x10000000, + FSL_IMX6UL_QSPI1_RX_ADDR = 0x0C000000, + + /* AIPS-2 */ + FSL_IMX6UL_UART6_ADDR = 0x021FC000, + FSL_IMX6UL_I2C4_ADDR = 0x021F8000, + FSL_IMX6UL_UART5_ADDR = 0x021F4000, + FSL_IMX6UL_UART4_ADDR = 0x021F0000, + FSL_IMX6UL_UART3_ADDR = 0x021EC000, + FSL_IMX6UL_UART2_ADDR = 0x021E8000, + FSL_IMX6UL_WDOG3_ADDR = 0x021E4000, + FSL_IMX6UL_QSPI_ADDR = 0x021E0000, + FSL_IMX6UL_SYS_CNT_CTRL_ADDR = 0x021DC000, + FSL_IMX6UL_SYS_CNT_CMP_ADDR = 0x021D8000, + FSL_IMX6UL_SYS_CNT_RD_ADDR = 0x021D4000, + FSL_IMX6UL_TZASC_ADDR = 0x021D0000, + FSL_IMX6UL_PXP_ADDR = 0x021CC000, + FSL_IMX6UL_LCDIF_ADDR = 0x021C8000, + FSL_IMX6UL_CSI_ADDR = 0x021C4000, + FSL_IMX6UL_CSU_ADDR = 0x021C0000, + FSL_IMX6UL_OCOTP_CTRL_ADDR = 0x021BC000, + FSL_IMX6UL_EIM_ADDR = 0x021B8000, + FSL_IMX6UL_SIM2_ADDR = 0x021B4000, + FSL_IMX6UL_MMDC_CFG_ADDR = 0x021B0000, + FSL_IMX6UL_ROMCP_ADDR = 0x021AC000, + FSL_IMX6UL_I2C3_ADDR = 0x021A8000, + FSL_IMX6UL_I2C2_ADDR = 0x021A4000, + FSL_IMX6UL_I2C1_ADDR = 0x021A0000, + FSL_IMX6UL_ADC2_ADDR = 0x0219C000, + FSL_IMX6UL_ADC1_ADDR = 0x02198000, + FSL_IMX6UL_USDHC2_ADDR = 0x02194000, + FSL_IMX6UL_USDHC1_ADDR = 0x02190000, + FSL_IMX6UL_SIM1_ADDR = 0x0218C000, + FSL_IMX6UL_ENET1_ADDR = 0x02188000, + FSL_IMX6UL_USBO2_USBMISC_ADDR = 0x02184800, + FSL_IMX6UL_USBO2_USB_ADDR = 0x02184000, + FSL_IMX6UL_USBO2_PL301_ADDR = 0x02180000, + FSL_IMX6UL_AIPS2_CFG_ADDR = 0x0217C000, + FSL_IMX6UL_CAAM_ADDR = 0x02140000, + FSL_IMX6UL_A7MPCORE_DAP_ADDR = 0x02100000, + + /* AIPS-1 */ + FSL_IMX6UL_PWM8_ADDR = 0x020FC000, + FSL_IMX6UL_PWM7_ADDR = 0x020F8000, + FSL_IMX6UL_PWM6_ADDR = 0x020F4000, + FSL_IMX6UL_PWM5_ADDR = 0x020F0000, + FSL_IMX6UL_SDMA_ADDR = 0x020EC000, + FSL_IMX6UL_GPT2_ADDR = 0x020E8000, + FSL_IMX6UL_IOMUXC_GPR_ADDR = 0x020E4000, + FSL_IMX6UL_IOMUXC_ADDR = 0x020E0000, + FSL_IMX6UL_GPC_ADDR = 0x020DC000, + FSL_IMX6UL_SRC_ADDR = 0x020D8000, + FSL_IMX6UL_EPIT2_ADDR = 0x020D4000, + FSL_IMX6UL_EPIT1_ADDR = 0x020D0000, + FSL_IMX6UL_SNVS_HP_ADDR = 0x020CC000, + FSL_IMX6UL_ANALOG_ADDR = 0x020C8000, + FSL_IMX6UL_CCM_ADDR = 0x020C4000, + FSL_IMX6UL_WDOG2_ADDR = 0x020C0000, + FSL_IMX6UL_WDOG1_ADDR = 0x020BC000, + FSL_IMX6UL_KPP_ADDR = 0x020B8000, + FSL_IMX6UL_ENET2_ADDR = 0x020B4000, + FSL_IMX6UL_SNVS_LP_ADDR = 0x020B0000, + FSL_IMX6UL_GPIO5_ADDR = 0x020AC000, + FSL_IMX6UL_GPIO4_ADDR = 0x020A8000, + FSL_IMX6UL_GPIO3_ADDR = 0x020A4000, + FSL_IMX6UL_GPIO2_ADDR = 0x020A0000, + FSL_IMX6UL_GPIO1_ADDR = 0x0209C000, + FSL_IMX6UL_GPT1_ADDR = 0x02098000, + FSL_IMX6UL_CAN2_ADDR = 0x02094000, + FSL_IMX6UL_CAN1_ADDR = 0x02090000, + FSL_IMX6UL_PWM4_ADDR = 0x0208C000, + FSL_IMX6UL_PWM3_ADDR = 0x02088000, + FSL_IMX6UL_PWM2_ADDR = 0x02084000, + FSL_IMX6UL_PWM1_ADDR = 0x02080000, + FSL_IMX6UL_AIPS1_CFG_ADDR = 0x0207C000, + FSL_IMX6UL_BEE_ADDR = 0x02044000, + FSL_IMX6UL_TOUCH_CTRL_ADDR = 0x02040000, + FSL_IMX6UL_SPBA_ADDR = 0x0203C000, + FSL_IMX6UL_ASRC_ADDR = 0x02034000, + FSL_IMX6UL_SAI3_ADDR = 0x02030000, + FSL_IMX6UL_SAI2_ADDR = 0x0202C000, + FSL_IMX6UL_SAI1_ADDR = 0x02028000, + FSL_IMX6UL_UART8_ADDR = 0x02024000, + FSL_IMX6UL_UART1_ADDR = 0x02020000, + FSL_IMX6UL_UART7_ADDR = 0x02018000, + FSL_IMX6UL_ECSPI4_ADDR = 0x02014000, + FSL_IMX6UL_ECSPI3_ADDR = 0x02010000, + FSL_IMX6UL_ECSPI2_ADDR = 0x0200C000, + FSL_IMX6UL_ECSPI1_ADDR = 0x02008000, + FSL_IMX6UL_SPDIF_ADDR = 0x02004000, + + FSL_IMX6UL_APBH_DMA_ADDR = 0x01804000, + FSL_IMX6UL_APBH_DMA_SIZE = (32 * 1024), + + FSL_IMX6UL_A7MPCORE_ADDR = 0x00A00000, + + FSL_IMX6UL_OCRAM_ALIAS_ADDR = 0x00920000, + FSL_IMX6UL_OCRAM_ALIAS_SIZE = 0x00060000, + FSL_IMX6UL_OCRAM_MEM_ADDR = 0x00900000, + FSL_IMX6UL_OCRAM_MEM_SIZE = 0x00020000, + FSL_IMX6UL_CAAM_MEM_ADDR = 0x00100000, + FSL_IMX6UL_CAAM_MEM_SIZE = 0x00008000, + FSL_IMX6UL_ROM_ADDR = 0x00000000, + FSL_IMX6UL_ROM_SIZE = 0x00018000, +}; + +enum FslIMX6ULIRQs { + FSL_IMX6UL_IOMUXC_IRQ = 0, + FSL_IMX6UL_DAP_IRQ = 1, + FSL_IMX6UL_SDMA_IRQ = 2, + FSL_IMX6UL_TSC_IRQ = 3, + FSL_IMX6UL_SNVS_IRQ = 4, + FSL_IMX6UL_LCDIF_IRQ = 5, + FSL_IMX6UL_BEE_IRQ = 6, + FSL_IMX6UL_CSI_IRQ = 7, + FSL_IMX6UL_PXP_IRQ = 8, + FSL_IMX6UL_SCTR1_IRQ = 9, + FSL_IMX6UL_SCTR2_IRQ = 10, + FSL_IMX6UL_WDOG3_IRQ = 11, + FSL_IMX6UL_APBH_DMA_IRQ = 13, + FSL_IMX6UL_WEIM_IRQ = 14, + FSL_IMX6UL_RAWNAND1_IRQ = 15, + FSL_IMX6UL_RAWNAND2_IRQ = 16, + FSL_IMX6UL_UART6_IRQ = 17, + FSL_IMX6UL_SRTC_IRQ = 19, + FSL_IMX6UL_SRTC_SEC_IRQ = 20, + FSL_IMX6UL_CSU_IRQ = 21, + FSL_IMX6UL_USDHC1_IRQ = 22, + FSL_IMX6UL_USDHC2_IRQ = 23, + FSL_IMX6UL_SAI3_IRQ = 24, + FSL_IMX6UL_SAI32_IRQ = 25, + + FSL_IMX6UL_UART1_IRQ = 26, + FSL_IMX6UL_UART2_IRQ = 27, + FSL_IMX6UL_UART3_IRQ = 28, + FSL_IMX6UL_UART4_IRQ = 29, + FSL_IMX6UL_UART5_IRQ = 30, + + FSL_IMX6UL_ECSPI1_IRQ = 31, + FSL_IMX6UL_ECSPI2_IRQ = 32, + FSL_IMX6UL_ECSPI3_IRQ = 33, + FSL_IMX6UL_ECSPI4_IRQ = 34, + + FSL_IMX6UL_I2C4_IRQ = 35, + FSL_IMX6UL_I2C1_IRQ = 36, + FSL_IMX6UL_I2C2_IRQ = 37, + FSL_IMX6UL_I2C3_IRQ = 38, + + FSL_IMX6UL_UART7_IRQ = 39, + FSL_IMX6UL_UART8_IRQ = 40, + + FSL_IMX6UL_USB1_IRQ = 42, + FSL_IMX6UL_USB2_IRQ = 43, + FSL_IMX6UL_USB_PHY1_IRQ = 44, + FSL_IMX6UL_USB_PHY2_IRQ = 44, + + FSL_IMX6UL_CAAM_JQ2_IRQ = 46, + FSL_IMX6UL_CAAM_ERR_IRQ = 47, + FSL_IMX6UL_CAAM_RTIC_IRQ = 48, + FSL_IMX6UL_TEMP_IRQ = 49, + FSL_IMX6UL_ASRC_IRQ = 50, + FSL_IMX6UL_SPDIF_IRQ = 52, + FSL_IMX6UL_PMU_REG_IRQ = 54, + FSL_IMX6UL_GPT1_IRQ = 55, + + FSL_IMX6UL_EPIT1_IRQ = 56, + FSL_IMX6UL_EPIT2_IRQ = 57, + + FSL_IMX6UL_GPIO1_INT7_IRQ = 58, + FSL_IMX6UL_GPIO1_INT6_IRQ = 59, + FSL_IMX6UL_GPIO1_INT5_IRQ = 60, + FSL_IMX6UL_GPIO1_INT4_IRQ = 61, + FSL_IMX6UL_GPIO1_INT3_IRQ = 62, + FSL_IMX6UL_GPIO1_INT2_IRQ = 63, + FSL_IMX6UL_GPIO1_INT1_IRQ = 64, + FSL_IMX6UL_GPIO1_INT0_IRQ = 65, + FSL_IMX6UL_GPIO1_LOW_IRQ = 66, + FSL_IMX6UL_GPIO1_HIGH_IRQ = 67, + FSL_IMX6UL_GPIO2_LOW_IRQ = 68, + FSL_IMX6UL_GPIO2_HIGH_IRQ = 69, + FSL_IMX6UL_GPIO3_LOW_IRQ = 70, + FSL_IMX6UL_GPIO3_HIGH_IRQ = 71, + FSL_IMX6UL_GPIO4_LOW_IRQ = 72, + FSL_IMX6UL_GPIO4_HIGH_IRQ = 73, + FSL_IMX6UL_GPIO5_LOW_IRQ = 74, + FSL_IMX6UL_GPIO5_HIGH_IRQ = 75, + + FSL_IMX6UL_WDOG1_IRQ = 80, + FSL_IMX6UL_WDOG2_IRQ = 81, + + FSL_IMX6UL_KPP_IRQ = 82, + + FSL_IMX6UL_PWM1_IRQ = 83, + FSL_IMX6UL_PWM2_IRQ = 84, + FSL_IMX6UL_PWM3_IRQ = 85, + FSL_IMX6UL_PWM4_IRQ = 86, + + FSL_IMX6UL_CCM1_IRQ = 87, + FSL_IMX6UL_CCM2_IRQ = 88, + + FSL_IMX6UL_GPC_IRQ = 89, + + FSL_IMX6UL_SRC_IRQ = 91, + + FSL_IMX6UL_CPU_PERF_IRQ = 94, + FSL_IMX6UL_CPU_CTI_IRQ = 95, + + FSL_IMX6UL_SRC_WDOG_IRQ = 96, + + FSL_IMX6UL_SAI1_IRQ = 97, + FSL_IMX6UL_SAI2_IRQ = 98, + + FSL_IMX6UL_ADC1_IRQ = 100, + FSL_IMX6UL_ADC2_IRQ = 101, + + FSL_IMX6UL_SJC_IRQ = 104, + + FSL_IMX6UL_CAAM_RING0_IRQ = 105, + FSL_IMX6UL_CAAM_RING1_IRQ = 106, + + FSL_IMX6UL_QSPI_IRQ = 107, + + FSL_IMX6UL_TZASC_IRQ = 108, + + FSL_IMX6UL_GPT2_IRQ = 109, + + FSL_IMX6UL_CAN1_IRQ = 110, + FSL_IMX6UL_CAN2_IRQ = 111, + + FSL_IMX6UL_SIM1_IRQ = 112, + FSL_IMX6UL_SIM2_IRQ = 113, + + FSL_IMX6UL_PWM5_IRQ = 114, + FSL_IMX6UL_PWM6_IRQ = 115, + FSL_IMX6UL_PWM7_IRQ = 116, + FSL_IMX6UL_PWM8_IRQ = 117, + + FSL_IMX6UL_ENET1_IRQ = 118, + FSL_IMX6UL_ENET1_TIMER_IRQ = 119, + FSL_IMX6UL_ENET2_IRQ = 120, + FSL_IMX6UL_ENET2_TIMER_IRQ = 121, + + FSL_IMX6UL_PMU_CORE_IRQ = 127, + FSL_IMX6UL_MAX_IRQ = 128, +}; + +#endif /* FSL_IMX6UL_H */ diff --git a/include/hw/arm/iotkit.h b/include/hw/arm/iotkit.h deleted file mode 100644 index 2cddde55dd..0000000000 --- a/include/hw/arm/iotkit.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * ARM IoT Kit - * - * Copyright (c) 2018 Linaro Limited - * Written 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 version 2 or - * (at your option) any later version. - */ - -/* This is a model of the Arm IoT Kit which is documented in - * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html - * It contains: - * a Cortex-M33 - * the IDAU - * some timers and watchdogs - * two peripheral protection controllers - * a memory protection controller - * a security controller - * a bus fabric which arranges that some parts of the address - * space are secure and non-secure aliases of each other - * - * QEMU interface: - * + QOM property "memory" is a MemoryRegion containing the devices provided - * by the board model. - * + QOM property "MAINCLK" is the frequency of the main system clock - * + QOM property "EXP_NUMIRQ" sets the number of expansion interrupts - * + Named GPIO inputs "EXP_IRQ" 0..n are the expansion interrupts, which - * are wired to the NVIC lines 32 .. n+32 - * Controlling up to 4 AHB expansion PPBs which a system using the IoTKit - * might provide: - * + named GPIO outputs apb_ppcexp{0,1,2,3}_nonsec[0..15] - * + named GPIO outputs apb_ppcexp{0,1,2,3}_ap[0..15] - * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_enable - * + named GPIO outputs apb_ppcexp{0,1,2,3}_irq_clear - * + named GPIO inputs apb_ppcexp{0,1,2,3}_irq_status - * Controlling each of the 4 expansion AHB PPCs which a system using the IoTKit - * might provide: - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_nonsec[0..15] - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_ap[0..15] - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable - * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear - * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status - * Controlling each of the 16 expansion MPCs which a system using the IoTKit - * might provide: - * + named GPIO inputs mpcexp_status[0..15] - */ - -#ifndef IOTKIT_H -#define IOTKIT_H - -#include "hw/sysbus.h" -#include "hw/arm/armv7m.h" -#include "hw/misc/iotkit-secctl.h" -#include "hw/misc/tz-ppc.h" -#include "hw/misc/tz-mpc.h" -#include "hw/timer/cmsdk-apb-timer.h" -#include "hw/misc/unimp.h" -#include "hw/or-irq.h" -#include "hw/core/split-irq.h" - -#define TYPE_IOTKIT "iotkit" -#define IOTKIT(obj) OBJECT_CHECK(IoTKit, (obj), TYPE_IOTKIT) - -/* We have an IRQ splitter and an OR gate input for each external PPC - * and the 2 internal PPCs - */ -#define NUM_EXTERNAL_PPCS (IOTS_NUM_AHB_EXP_PPC + IOTS_NUM_APB_EXP_PPC) -#define NUM_PPCS (NUM_EXTERNAL_PPCS + 2) - -typedef struct IoTKit { - /*< private >*/ - SysBusDevice parent_obj; - - /*< public >*/ - ARMv7MState armv7m; - IoTKitSecCtl secctl; - TZPPC apb_ppc0; - TZPPC apb_ppc1; - TZMPC mpc; - CMSDKAPBTIMER timer0; - CMSDKAPBTIMER timer1; - qemu_or_irq ppc_irq_orgate; - SplitIRQ sec_resp_splitter; - SplitIRQ ppc_irq_splitter[NUM_PPCS]; - SplitIRQ mpc_irq_splitter[IOTS_NUM_EXP_MPC + IOTS_NUM_MPC]; - qemu_or_irq mpc_irq_orgate; - - UnimplementedDeviceState dualtimer; - UnimplementedDeviceState s32ktimer; - - MemoryRegion container; - MemoryRegion alias1; - MemoryRegion alias2; - MemoryRegion alias3; - MemoryRegion sram0; - - qemu_irq *exp_irqs; - qemu_irq ppc0_irq; - qemu_irq ppc1_irq; - qemu_irq sec_resp_cfg; - qemu_irq sec_resp_cfg_in; - qemu_irq nsc_cfg_in; - - qemu_irq irq_status_in[NUM_EXTERNAL_PPCS]; - qemu_irq mpcexp_status_in[IOTS_NUM_EXP_MPC]; - - uint32_t nsccfg; - - /* Properties */ - MemoryRegion *board_memory; - uint32_t exp_numirq; - uint32_t mainclk_frq; -} IoTKit; - -#endif diff --git a/include/hw/arm/linux-boot-if.h b/include/hw/arm/linux-boot-if.h index aba4479a14..7bbdfd1cc6 100644 --- a/include/hw/arm/linux-boot-if.h +++ b/include/hw/arm/linux-boot-if.h @@ -16,10 +16,7 @@ #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 ARMLinuxBootIf ARMLinuxBootIf; typedef struct ARMLinuxBootIfClass { /*< private >*/ diff --git a/include/hw/arm/nrf51.h b/include/hw/arm/nrf51.h new file mode 100644 index 0000000000..1008fee6c9 --- /dev/null +++ b/include/hw/arm/nrf51.h @@ -0,0 +1,47 @@ +/* + * Nordic Semiconductor nRF51 Series SOC Common Defines + * + * This file hosts generic defines used in various nRF51 peripheral devices. + * + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef NRF51_H +#define NRF51_H + +#define NRF51_FLASH_BASE 0x00000000 +#define NRF51_FICR_BASE 0x10000000 +#define NRF51_FICR_SIZE 0x00000100 +#define NRF51_UICR_BASE 0x10001000 +#define NRF51_SRAM_BASE 0x20000000 + +#define NRF51_IOMEM_BASE 0x40000000 +#define NRF51_IOMEM_SIZE 0x20000000 + +#define NRF51_UART_BASE 0x40002000 +#define NRF51_TWI_BASE 0x40003000 +#define NRF51_TWI_SIZE 0x00001000 +#define NRF51_TIMER_BASE 0x40008000 +#define NRF51_TIMER_SIZE 0x00001000 +#define NRF51_RNG_BASE 0x4000D000 +#define NRF51_NVMC_BASE 0x4001E000 +#define NRF51_GPIO_BASE 0x50000000 + +#define NRF51_PRIVATE_BASE 0xF0000000 +#define NRF51_PRIVATE_SIZE 0x10000000 + +#define NRF51_PAGE_SIZE 1024 + +/* Trigger */ +#define NRF51_TRIGGER_TASK 0x01 + +/* Events */ +#define NRF51_EVENT_CLEAR 0x00 + +#endif diff --git a/include/hw/arm/nrf51_soc.h b/include/hw/arm/nrf51_soc.h new file mode 100644 index 0000000000..fd7fcc71a5 --- /dev/null +++ b/include/hw/arm/nrf51_soc.h @@ -0,0 +1,56 @@ +/* + * Nordic Semiconductor nRF51 SoC + * + * Copyright 2018 Joel Stanley + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef NRF51_SOC_H +#define NRF51_SOC_H + +#include "hw/sysbus.h" +#include "hw/arm/armv7m.h" +#include "hw/char/nrf51_uart.h" +#include "hw/misc/nrf51_rng.h" +#include "hw/gpio/nrf51_gpio.h" +#include "hw/nvram/nrf51_nvm.h" +#include "hw/timer/nrf51_timer.h" + +#define TYPE_NRF51_SOC "nrf51-soc" +#define NRF51_SOC(obj) \ + OBJECT_CHECK(NRF51State, (obj), TYPE_NRF51_SOC) + +#define NRF51_NUM_TIMERS 3 + +typedef struct NRF51State { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + ARMv7MState cpu; + + NRF51UARTState uart; + NRF51RNGState rng; + NRF51NVMState nvm; + NRF51GPIOState gpio; + NRF51TimerState timer[NRF51_NUM_TIMERS]; + + MemoryRegion iomem; + MemoryRegion sram; + MemoryRegion flash; + MemoryRegion clock; + MemoryRegion twi; + + uint32_t sram_size; + uint32_t flash_size; + + MemoryRegion *board_memory; + + MemoryRegion container; + +} NRF51State; + +#endif + diff --git a/include/hw/arm/pxa.h b/include/hw/arm/pxa.h index 0df1199caa..f6dfb5c0cf 100644 --- a/include/hw/arm/pxa.h +++ b/include/hw/arm/pxa.h @@ -12,6 +12,7 @@ #include "exec/memory.h" #include "target/arm/cpu-qom.h" +#include "hw/pcmcia.h" /* Interrupt numbers */ # define PXA2XX_PIC_SSP3 0 diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 9a870ccb6a..507517c603 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -42,7 +42,7 @@ #define NUM_VIRTIO_TRANSPORTS 32 #define NUM_SMMU_IRQS 4 -#define ARCH_GICV3_MAINT_IRQ 9 +#define ARCH_GIC_MAINT_IRQ 9 #define ARCH_TIMER_VIRT_IRQ 11 #define ARCH_TIMER_S_EL1_IRQ 13 @@ -60,9 +60,10 @@ enum { VIRT_GIC_DIST, VIRT_GIC_CPU, VIRT_GIC_V2M, + VIRT_GIC_HYP, + VIRT_GIC_VCPU, VIRT_GIC_ITS, VIRT_GIC_REDIST, - VIRT_GIC_REDIST2, VIRT_SMMU, VIRT_UART, VIRT_MMIO, @@ -72,12 +73,18 @@ enum { VIRT_PCIE_MMIO, VIRT_PCIE_PIO, VIRT_PCIE_ECAM, - VIRT_PCIE_ECAM_HIGH, VIRT_PLATFORM_BUS, - VIRT_PCIE_MMIO_HIGH, VIRT_GPIO, VIRT_SECURE_UART, VIRT_SECURE_MEM, + VIRT_LOWMEMMAP_LAST, +}; + +/* indices of IO regions located after the RAM */ +enum { + VIRT_HIGH_GIC_REDIST2 = VIRT_LOWMEMMAP_LAST, + VIRT_HIGH_PCIE_ECAM, + VIRT_HIGH_PCIE_MMIO, }; typedef enum VirtIOMMUType { @@ -114,7 +121,7 @@ typedef struct { int32_t gic_version; VirtIOMMUType iommu; struct arm_boot_info bootinfo; - const MemMapEntry *memmap; + MemMapEntry *memmap; const int *irqmap; int smp_cpus; void *fdt; @@ -124,9 +131,10 @@ typedef struct { uint32_t msi_phandle; uint32_t iommu_phandle; int psci_conduit; + hwaddr highest_gpa; } VirtMachineState; -#define VIRT_ECAM_ID(high) (high ? VIRT_PCIE_ECAM_HIGH : VIRT_PCIE_ECAM) +#define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) #define TYPE_VIRT_MACHINE MACHINE_TYPE_NAME("virt") #define VIRT_MACHINE(obj) \ diff --git a/include/hw/arm/xlnx-versal.h b/include/hw/arm/xlnx-versal.h new file mode 100644 index 0000000000..ec7c859d08 --- /dev/null +++ b/include/hw/arm/xlnx-versal.h @@ -0,0 +1,122 @@ +/* + * Model of the Xilinx Versal + * + * Copyright (c) 2018 Xilinx Inc. + * Written by Edgar E. Iglesias + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#ifndef XLNX_VERSAL_H +#define XLNX_VERSAL_H + +#include "hw/sysbus.h" +#include "hw/arm/arm.h" +#include "hw/intc/arm_gicv3.h" + +#define TYPE_XLNX_VERSAL "xlnx-versal" +#define XLNX_VERSAL(obj) OBJECT_CHECK(Versal, (obj), TYPE_XLNX_VERSAL) + +#define XLNX_VERSAL_NR_ACPUS 2 +#define XLNX_VERSAL_NR_UARTS 2 +#define XLNX_VERSAL_NR_GEMS 2 +#define XLNX_VERSAL_NR_IRQS 192 + +typedef struct Versal { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + struct { + struct { + MemoryRegion mr; + ARMCPU *cpu[XLNX_VERSAL_NR_ACPUS]; + GICv3State gic; + } apu; + } fpd; + + MemoryRegion mr_ps; + + struct { + /* 4 ranges to access DDR. */ + MemoryRegion mr_ddr_ranges[4]; + } noc; + + struct { + MemoryRegion mr_ocm; + + struct { + SysBusDevice *uart[XLNX_VERSAL_NR_UARTS]; + SysBusDevice *gem[XLNX_VERSAL_NR_GEMS]; + } iou; + } lpd; + + struct { + MemoryRegion *mr_ddr; + uint32_t psci_conduit; + } cfg; +} Versal; + +/* Memory-map and IRQ definitions. Copied a subset from + * auto-generated files. */ + +#define VERSAL_GIC_MAINT_IRQ 9 +#define VERSAL_TIMER_VIRT_IRQ 11 +#define VERSAL_TIMER_S_EL1_IRQ 13 +#define VERSAL_TIMER_NS_EL1_IRQ 14 +#define VERSAL_TIMER_NS_EL2_IRQ 10 + +#define VERSAL_UART0_IRQ_0 18 +#define VERSAL_UART1_IRQ_0 19 +#define VERSAL_GEM0_IRQ_0 56 +#define VERSAL_GEM0_WAKE_IRQ_0 57 +#define VERSAL_GEM1_IRQ_0 58 +#define VERSAL_GEM1_WAKE_IRQ_0 59 + +/* Architecturally reserved IRQs suitable for virtualization. */ +#define VERSAL_RSVD_IRQ_FIRST 111 +#define VERSAL_RSVD_IRQ_LAST 118 + +#define MM_TOP_RSVD 0xa0000000U +#define MM_TOP_RSVD_SIZE 0x4000000 +#define MM_GIC_APU_DIST_MAIN 0xf9000000U +#define MM_GIC_APU_DIST_MAIN_SIZE 0x10000 +#define MM_GIC_APU_REDIST_0 0xf9080000U +#define MM_GIC_APU_REDIST_0_SIZE 0x80000 + +#define MM_UART0 0xff000000U +#define MM_UART0_SIZE 0x10000 +#define MM_UART1 0xff010000U +#define MM_UART1_SIZE 0x10000 + +#define MM_GEM0 0xff0c0000U +#define MM_GEM0_SIZE 0x10000 +#define MM_GEM1 0xff0d0000U +#define MM_GEM1_SIZE 0x10000 + +#define MM_OCM 0xfffc0000U +#define MM_OCM_SIZE 0x40000 + +#define MM_TOP_DDR 0x0 +#define MM_TOP_DDR_SIZE 0x80000000U +#define MM_TOP_DDR_2 0x800000000ULL +#define MM_TOP_DDR_2_SIZE 0x800000000ULL +#define MM_TOP_DDR_3 0xc000000000ULL +#define MM_TOP_DDR_3_SIZE 0x4000000000ULL +#define MM_TOP_DDR_4 0x10000000000ULL +#define MM_TOP_DDR_4_SIZE 0xb780000000ULL + +#define MM_PSM_START 0xffc80000U +#define MM_PSM_END 0xffcf0000U + +#define MM_CRL 0xff5e0000U +#define MM_CRL_SIZE 0x300000 +#define MM_IOU_SCNTR 0xff130000U +#define MM_IOU_SCNTR_SIZE 0x10000 +#define MM_IOU_SCNTRS 0xff140000U +#define MM_IOU_SCNTRS_SIZE 0x10000 +#define MM_FPD_CRF 0xfd1a0000U +#define MM_FPD_CRF_SIZE 0x140000 +#endif diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h index 82b6ec2486..591515c760 100644 --- a/include/hw/arm/xlnx-zynqmp.h +++ b/include/hw/arm/xlnx-zynqmp.h @@ -31,6 +31,7 @@ #include "hw/display/xlnx_dp.h" #include "hw/intc/xlnx-zynqmp-ipi.h" #include "hw/timer/xlnx-zynqmp-rtc.h" +#include "hw/cpu/cluster.h" #define TYPE_XLNX_ZYNQMP "xlnx,zynqmp" #define XLNX_ZYNQMP(obj) OBJECT_CHECK(XlnxZynqMPState, (obj), \ @@ -53,7 +54,7 @@ #define XLNX_ZYNQMP_OCM_RAM_0_ADDRESS 0xFFFC0000 #define XLNX_ZYNQMP_OCM_RAM_SIZE 0x10000 -#define XLNX_ZYNQMP_GIC_REGIONS 2 +#define XLNX_ZYNQMP_GIC_REGIONS 6 /* ZynqMP maps the ARM GIC regions (GICC, GICD ...) at consecutive 64k offsets * and under-decodes the 64k region. This mirrors the 4k regions to every 4k @@ -62,7 +63,7 @@ */ #define XLNX_ZYNQMP_GIC_REGION_SIZE 0x1000 -#define XLNX_ZYNQMP_GIC_ALIASES (0x10000 / XLNX_ZYNQMP_GIC_REGION_SIZE - 1) +#define XLNX_ZYNQMP_GIC_ALIASES (0x10000 / XLNX_ZYNQMP_GIC_REGION_SIZE) #define XLNX_ZYNQMP_MAX_LOW_RAM_SIZE 0x80000000ull @@ -77,6 +78,8 @@ typedef struct XlnxZynqMPState { DeviceState parent_obj; /*< public >*/ + CPUClusterState apu_cluster; + CPUClusterState rpu_cluster; ARMCPU apu_cpu[XLNX_ZYNQMP_NUM_APU_CPUS]; ARMCPU rpu_cpu[XLNX_ZYNQMP_NUM_RPU_CPUS]; GICState gic; diff --git a/include/hw/audio/wm8750.h b/include/hw/audio/wm8750.h index 84e7a119bb..e12cb886d1 100644 --- a/include/hw/audio/wm8750.h +++ b/include/hw/audio/wm8750.h @@ -17,6 +17,7 @@ #include "hw/hw.h" #define TYPE_WM8750 "wm8750" +#define TYPE_MV88W8618_AUDIO "mv88w8618_audio" typedef void data_req_cb(void *opaque, int free_out, int free_in); diff --git a/include/hw/block/block.h b/include/hw/block/block.h index ac51280d86..1bfdcebce0 100644 --- a/include/hw/block/block.h +++ b/include/hw/block/block.h @@ -11,7 +11,7 @@ #ifndef HW_BLOCK_H #define HW_BLOCK_H -#include "qemu-common.h" +#include "exec/hwaddr.h" #include "qapi/qapi-types-block-core.h" /* Configuration */ @@ -70,10 +70,14 @@ static inline unsigned int get_physical_block_exp(BlockConf *conf) DEFINE_PROP_BLOCKDEV_ON_ERROR("werror", _state, _conf.werror, \ BLOCKDEV_ON_ERROR_AUTO) +/* Backend access helpers */ + +bool blk_check_size_and_read_all(BlockBackend *blk, void *buf, hwaddr size, + Error **errp); + /* Configuration helpers */ void blkconf_locked(BlockConf *conf, bool *locked); -void blkconf_serial(BlockConf *conf, char **serial); bool blkconf_geometry(BlockConf *conf, int *trans, unsigned cyls_max, unsigned heads_max, unsigned secs_max, Error **errp); diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h index 67c3aa329e..a0f488732a 100644 --- a/include/hw/block/flash.h +++ b/include/hw/block/flash.h @@ -5,32 +5,46 @@ #include "exec/memory.h" -#define TYPE_CFI_PFLASH01 "cfi.pflash01" -#define TYPE_CFI_PFLASH02 "cfi.pflash02" - -typedef struct pflash_t pflash_t; - /* pflash_cfi01.c */ -pflash_t *pflash_cfi01_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, - uint32_t sector_len, int nb_blocs, int width, - uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, int be); + +#define TYPE_PFLASH_CFI01 "cfi.pflash01" +#define PFLASH_CFI01(obj) \ + OBJECT_CHECK(PFlashCFI01, (obj), TYPE_PFLASH_CFI01) + +typedef struct PFlashCFI01 PFlashCFI01; + +PFlashCFI01 *pflash_cfi01_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, + int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, + int be); +BlockBackend *pflash_cfi01_get_blk(PFlashCFI01 *fl); +MemoryRegion *pflash_cfi01_get_memory(PFlashCFI01 *fl); /* pflash_cfi02.c */ -pflash_t *pflash_cfi02_register(hwaddr base, - DeviceState *qdev, const char *name, - hwaddr size, - BlockBackend *blk, uint32_t sector_len, - int nb_blocs, int nb_mappings, int width, - uint16_t id0, uint16_t id1, - uint16_t id2, uint16_t id3, - uint16_t unlock_addr0, uint16_t unlock_addr1, - int be); -MemoryRegion *pflash_cfi01_get_memory(pflash_t *fl); +#define TYPE_PFLASH_CFI02 "cfi.pflash02" +#define PFLASH_CFI02(obj) \ + OBJECT_CHECK(PFlashCFI02, (obj), TYPE_PFLASH_CFI02) + +typedef struct PFlashCFI02 PFlashCFI02; + +PFlashCFI02 *pflash_cfi02_register(hwaddr base, + const char *name, + hwaddr size, + BlockBackend *blk, + uint32_t sector_len, + int nb_mappings, + int width, + uint16_t id0, uint16_t id1, + uint16_t id2, uint16_t id3, + uint16_t unlock_addr0, + uint16_t unlock_addr1, + int be); /* nand.c */ DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id); diff --git a/include/hw/boards.h b/include/hw/boards.h index d139a431a6..e231860666 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -69,7 +69,6 @@ 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); -void machine_register_compat_props(MachineState *machine); HotpluggableCPUList *machine_query_hotpluggable_cpus(MachineState *machine); void machine_set_cpu_numa_node(MachineState *machine, const CpuInstanceProperties *props, @@ -157,6 +156,9 @@ typedef struct { * should instead use "unimplemented-device" for all memory ranges where * the guest will attempt to probe for a device that QEMU doesn't * implement and a stub device is required. + * @kvm_type: + * Return the type of KVM corresponding to the kvm-type string option or + * computed based on other criteria such as the host kernel capabilities. */ struct MachineClass { /*< private >*/ @@ -172,7 +174,7 @@ struct MachineClass { void (*init)(MachineState *state); void (*reset)(void); void (*hot_add_cpu)(const int64_t id, Error **errp); - int (*kvm_type)(const char *arg); + int (*kvm_type)(MachineState *machine, const char *arg); BlockInterfaceType block_default_type; int units_per_default_bus; @@ -181,7 +183,6 @@ struct MachineClass { int default_cpus; unsigned int no_serial:1, no_parallel:1, - use_virtcon:1, no_floppy:1, no_cdrom:1, no_sdcard:1, @@ -191,10 +192,11 @@ struct MachineClass { const char *default_machine_opts; const char *default_boot_order; const char *default_display; - GArray *compat_props; + GPtrArray *compat_props; const char *hw_version; ram_addr_t default_ram_size; const char *default_cpu_type; + bool default_kernel_irqchip_split; bool option_rom_has_mr; bool rom_file_has_mr; int minimum_page_bits; @@ -206,6 +208,9 @@ struct MachineClass { bool auto_enable_numa_with_memhp; void (*numa_auto_assign_ram)(MachineClass *mc, NodeInfo *nodes, int nb_nodes, ram_addr_t size); + bool ignore_boot_device_suffixes; + bool smbus_no_migration_support; + bool nvdimm_supported; HotplugHandler *(*get_hotplug_handler)(MachineState *machine, DeviceState *dev); @@ -268,6 +273,7 @@ struct MachineState { const char *cpu_type; AccelState *accelerator; CPUArchIdList *possible_cpus; + struct NVDIMMState *nvdimms_state; }; #define DEFINE_MACHINE(namestr, machine_initfn) \ @@ -287,20 +293,46 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) -#define SET_MACHINE_COMPAT(m, COMPAT) \ - do { \ - int i; \ - static GlobalProperty props[] = { \ - COMPAT \ - { /* end of list */ } \ - }; \ - if (!m->compat_props) { \ - m->compat_props = g_array_new(false, false, sizeof(void *)); \ - } \ - for (i = 0; props[i].driver != NULL; i++) { \ - GlobalProperty *prop = &props[i]; \ - g_array_append_val(m->compat_props, prop); \ - } \ - } while (0) +extern GlobalProperty hw_compat_3_1[]; +extern const size_t hw_compat_3_1_len; + +extern GlobalProperty hw_compat_3_0[]; +extern const size_t hw_compat_3_0_len; + +extern GlobalProperty hw_compat_2_12[]; +extern const size_t hw_compat_2_12_len; + +extern GlobalProperty hw_compat_2_11[]; +extern const size_t hw_compat_2_11_len; + +extern GlobalProperty hw_compat_2_10[]; +extern const size_t hw_compat_2_10_len; + +extern GlobalProperty hw_compat_2_9[]; +extern const size_t hw_compat_2_9_len; + +extern GlobalProperty hw_compat_2_8[]; +extern const size_t hw_compat_2_8_len; + +extern GlobalProperty hw_compat_2_7[]; +extern const size_t hw_compat_2_7_len; + +extern GlobalProperty hw_compat_2_6[]; +extern const size_t hw_compat_2_6_len; + +extern GlobalProperty hw_compat_2_5[]; +extern const size_t hw_compat_2_5_len; + +extern GlobalProperty hw_compat_2_4[]; +extern const size_t hw_compat_2_4_len; + +extern GlobalProperty hw_compat_2_3[]; +extern const size_t hw_compat_2_3_len; + +extern GlobalProperty hw_compat_2_2[]; +extern const size_t hw_compat_2_2_len; + +extern GlobalProperty hw_compat_2_1[]; +extern const size_t hw_compat_2_1_len; #endif diff --git a/include/hw/char/imx_serial.h b/include/hw/char/imx_serial.h index ee80da12e6..c8b74284f8 100644 --- a/include/hw/char/imx_serial.h +++ b/include/hw/char/imx_serial.h @@ -68,6 +68,7 @@ #define UCR2_RXEN (1<<1) /* Receiver enable */ #define UCR2_SRST (1<<0) /* Reset complete */ +#define UCR4_DREN BIT(0) /* Receive Data Ready interrupt enable */ #define UCR4_TCEN BIT(3) /* TX complete interrupt enable */ #define UTS1_TXEMPTY (1<<6) diff --git a/include/hw/char/nrf51_uart.h b/include/hw/char/nrf51_uart.h new file mode 100644 index 0000000000..eb1c15b490 --- /dev/null +++ b/include/hw/char/nrf51_uart.h @@ -0,0 +1,77 @@ +/* + * nRF51 SoC UART emulation + * + * Copyright (c) 2018 Julia Suvorova + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +#ifndef NRF51_UART_H +#define NRF51_UART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "hw/registerfields.h" + +#define UART_FIFO_LENGTH 6 +#define UART_SIZE 0x1000 + +#define TYPE_NRF51_UART "nrf51_soc.uart" +#define NRF51_UART(obj) OBJECT_CHECK(NRF51UARTState, (obj), TYPE_NRF51_UART) + +REG32(UART_STARTRX, 0x000) +REG32(UART_STOPRX, 0x004) +REG32(UART_STARTTX, 0x008) +REG32(UART_STOPTX, 0x00C) +REG32(UART_SUSPEND, 0x01C) + +REG32(UART_CTS, 0x100) +REG32(UART_NCTS, 0x104) +REG32(UART_RXDRDY, 0x108) +REG32(UART_TXDRDY, 0x11C) +REG32(UART_ERROR, 0x124) +REG32(UART_RXTO, 0x144) + +REG32(UART_INTEN, 0x300) + FIELD(UART_INTEN, CTS, 0, 1) + FIELD(UART_INTEN, NCTS, 1, 1) + FIELD(UART_INTEN, RXDRDY, 2, 1) + FIELD(UART_INTEN, TXDRDY, 7, 1) + FIELD(UART_INTEN, ERROR, 9, 1) + FIELD(UART_INTEN, RXTO, 17, 1) +REG32(UART_INTENSET, 0x304) +REG32(UART_INTENCLR, 0x308) +REG32(UART_ERRORSRC, 0x480) +REG32(UART_ENABLE, 0x500) +REG32(UART_PSELRTS, 0x508) +REG32(UART_PSELTXD, 0x50C) +REG32(UART_PSELCTS, 0x510) +REG32(UART_PSELRXD, 0x514) +REG32(UART_RXD, 0x518) +REG32(UART_TXD, 0x51C) +REG32(UART_BAUDRATE, 0x524) +REG32(UART_CONFIG, 0x56C) + +typedef struct NRF51UARTState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + CharBackend chr; + qemu_irq irq; + guint watch_tag; + + uint8_t rx_fifo[UART_FIFO_LENGTH]; + unsigned int rx_fifo_pos; + unsigned int rx_fifo_len; + + uint32_t reg[0x56C]; + + bool rx_started; + bool tx_started; + bool pending_tx_byte; + bool enabled; +} NRF51UARTState; + +#endif diff --git a/include/hw/char/pl011.h b/include/hw/char/pl011.h index 83649324b6..dad3cf2912 100644 --- a/include/hw/char/pl011.h +++ b/include/hw/char/pl011.h @@ -15,6 +15,40 @@ #ifndef HW_PL011_H #define HW_PL011_H +#include "hw/sysbus.h" +#include "chardev/char-fe.h" + +#define TYPE_PL011 "pl011" +#define PL011(obj) OBJECT_CHECK(PL011State, (obj), TYPE_PL011) + +/* This shares the same struct (and cast macro) as the base pl011 device */ +#define TYPE_PL011_LUMINARY "pl011_luminary" + +typedef struct PL011State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t readbuff; + uint32_t flags; + uint32_t lcr; + uint32_t rsr; + uint32_t cr; + uint32_t dmacr; + uint32_t int_enabled; + uint32_t int_level; + uint32_t read_fifo[16]; + uint32_t ilpr; + uint32_t ibrd; + uint32_t fbrd; + uint32_t ifl; + int read_pos; + int read_count; + int read_trigger; + CharBackend chr; + qemu_irq irq[6]; + const unsigned char *id; +} PL011State; + static inline DeviceState *pl011_create(hwaddr addr, qemu_irq irq, Chardev *chr) diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index 0acfbbc382..abd5cf71a9 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -35,7 +35,7 @@ #define UART_FIFO_LENGTH 16 /* 16550A Fifo Length */ -struct SerialState { +typedef struct SerialState { uint16_t divider; uint8_t rbr; /* receive register */ uint8_t thr; /* transmit holding register */ @@ -77,7 +77,7 @@ struct SerialState { QEMUTimer *modem_status_poll; MemoryRegion io; -}; +} SerialState; extern const VMStateDescription vmstate_serial; extern const MemoryRegionOps serial_io_ops; diff --git a/include/hw/compat.h b/include/hw/compat.h deleted file mode 100644 index c08f4040bb..0000000000 --- a/include/hw/compat.h +++ /dev/null @@ -1,285 +0,0 @@ -#ifndef HW_COMPAT_H -#define HW_COMPAT_H - -#define HW_COMPAT_2_12 \ - {\ - .driver = "migration",\ - .property = "decompress-error-check",\ - .value = "off",\ - },{\ - .driver = "hda-audio",\ - .property = "use-timer",\ - .value = "false",\ - },{\ - .driver = "cirrus-vga",\ - .property = "global-vmstate",\ - .value = "true",\ - },{\ - .driver = "VGA",\ - .property = "global-vmstate",\ - .value = "true",\ - },{\ - .driver = "vmware-svga",\ - .property = "global-vmstate",\ - .value = "true",\ - },{\ - .driver = "qxl-vga",\ - .property = "global-vmstate",\ - .value = "true",\ - }, - -#define HW_COMPAT_2_11 \ - {\ - .driver = "hpet",\ - .property = "hpet-offset-saved",\ - .value = "false",\ - },{\ - .driver = "virtio-blk-pci",\ - .property = "vectors",\ - .value = "2",\ - },{\ - .driver = "vhost-user-blk-pci",\ - .property = "vectors",\ - .value = "2",\ - },{\ - .driver = "e1000",\ - .property = "migrate_tso_props",\ - .value = "off",\ - }, - -#define HW_COMPAT_2_10 \ - {\ - .driver = "virtio-mouse-device",\ - .property = "wheel-axis",\ - .value = "false",\ - },{\ - .driver = "virtio-tablet-device",\ - .property = "wheel-axis",\ - .value = "false",\ - }, - -#define HW_COMPAT_2_9 \ - {\ - .driver = "pci-bridge",\ - .property = "shpc",\ - .value = "off",\ - },{\ - .driver = "intel-iommu",\ - .property = "pt",\ - .value = "off",\ - },{\ - .driver = "virtio-net-device",\ - .property = "x-mtu-bypass-backend",\ - .value = "off",\ - },{\ - .driver = "pcie-root-port",\ - .property = "x-migrate-msix",\ - .value = "false",\ - }, - -#define HW_COMPAT_2_8 \ - {\ - .driver = "fw_cfg_mem",\ - .property = "x-file-slots",\ - .value = stringify(0x10),\ - },{\ - .driver = "fw_cfg_io",\ - .property = "x-file-slots",\ - .value = stringify(0x10),\ - },{\ - .driver = "pflash_cfi01",\ - .property = "old-multiple-chip-handling",\ - .value = "on",\ - },{\ - .driver = "pci-bridge",\ - .property = "shpc",\ - .value = "on",\ - },{\ - .driver = TYPE_PCI_DEVICE,\ - .property = "x-pcie-extcap-init",\ - .value = "off",\ - },{\ - .driver = "virtio-pci",\ - .property = "x-pcie-deverr-init",\ - .value = "off",\ - },{\ - .driver = "virtio-pci",\ - .property = "x-pcie-lnkctl-init",\ - .value = "off",\ - },{\ - .driver = "virtio-pci",\ - .property = "x-pcie-pm-init",\ - .value = "off",\ - },{\ - .driver = "cirrus-vga",\ - .property = "vgamem_mb",\ - .value = "8",\ - },{\ - .driver = "isa-cirrus-vga",\ - .property = "vgamem_mb",\ - .value = "8",\ - }, - -#define HW_COMPAT_2_7 \ - {\ - .driver = "virtio-pci",\ - .property = "page-per-vq",\ - .value = "on",\ - },{\ - .driver = "virtio-serial-device",\ - .property = "emergency-write",\ - .value = "off",\ - },{\ - .driver = "ioapic",\ - .property = "version",\ - .value = "0x11",\ - },{\ - .driver = "intel-iommu",\ - .property = "x-buggy-eim",\ - .value = "true",\ - },{\ - .driver = "virtio-pci",\ - .property = "x-ignore-backend-features",\ - .value = "on",\ - }, - -#define HW_COMPAT_2_6 \ - {\ - .driver = "virtio-mmio",\ - .property = "format_transport_address",\ - .value = "off",\ - },{\ - .driver = "virtio-pci",\ - .property = "disable-modern",\ - .value = "on",\ - },{\ - .driver = "virtio-pci",\ - .property = "disable-legacy",\ - .value = "off",\ - }, - -#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 = TYPE_PCI_DEVICE,\ - .property = "x-pcie-lnksta-dllla",\ - .value = "off",\ - },{\ - .driver = "migration",\ - .property = "send-configuration",\ - .value = "off",\ - },{\ - .driver = "migration",\ - .property = "send-section-footer",\ - .value = "off",\ - },{\ - .driver = "migration",\ - .property = "store-global-state",\ - .value = "off",\ - }, - -#define HW_COMPAT_2_2 \ - /* 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",\ - }, - -#endif /* HW_COMPAT_H */ diff --git a/include/hw/cpu/cluster.h b/include/hw/cpu/cluster.h new file mode 100644 index 0000000000..549c2d31d4 --- /dev/null +++ b/include/hw/cpu/cluster.h @@ -0,0 +1,82 @@ +/* + * QEMU CPU cluster + * + * Copyright (c) 2018 GreenSocs SAS + * + * 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_CPU_CLUSTER_H +#define HW_CPU_CLUSTER_H + +#include "qemu/osdep.h" +#include "hw/qdev.h" + +/* + * CPU Cluster type + * + * A cluster is a group of CPUs which are all identical and have the same view + * of the rest of the system. It is mainly an internal QEMU representation and + * does not necessarily match with the notion of clusters on the real hardware. + * + * If CPUs are not identical (for example, Cortex-A53 and Cortex-A57 CPUs in an + * Arm big.LITTLE system) they should be in different clusters. If the CPUs do + * not have the same view of memory (for example the main CPU and a management + * controller processor) they should be in different clusters. + * + * A cluster is created by creating an object of TYPE_CPU_CLUSTER, and then + * adding the CPUs to it as QOM child objects (e.g. using the + * object_initialize_child() or object_property_add_child() functions). + * The CPUs may be either direct children of the cluster object, or indirect + * children (e.g. children of children of the cluster object). + * + * All CPUs must be added as children before the cluster is realized. + * (Regrettably QOM provides no way to prevent adding children to a realized + * object and no way for the parent to be notified when a new child is added + * to it, so this restriction is not checked for, but the system will not + * behave correctly if it is not adhered to. The cluster will assert that + * it contains at least one CPU, which should catch most inadvertent + * violations of this constraint.) + * + * A CPU which is not put into any cluster will be considered implicitly + * to be in a cluster with all the other "loose" CPUs, so all CPUs that are + * not assigned to clusters must be identical. + */ + +#define TYPE_CPU_CLUSTER "cpu-cluster" +#define CPU_CLUSTER(obj) \ + OBJECT_CHECK(CPUClusterState, (obj), TYPE_CPU_CLUSTER) + +/* + * This limit is imposed by TCG, which puts the cluster ID into an + * 8 bit field (and uses all-1s for the default "not in any cluster"). + */ +#define MAX_CLUSTERS 255 + +/** + * CPUClusterState: + * @cluster_id: The cluster ID. This value is for internal use only and should + * not be exposed directly to the user or to the guest. + * + * State of a CPU cluster. + */ +typedef struct CPUClusterState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + uint32_t cluster_id; +} CPUClusterState; + +#endif diff --git a/include/hw/devices.h b/include/hw/devices.h index 0e27feb0c2..1ed5be3296 100644 --- a/include/hw/devices.h +++ b/include/hw/devices.h @@ -4,6 +4,7 @@ /* Devices that have nowhere better to go. */ #include "hw/hw.h" +#include "ui/console.h" /* smc91c111.c */ void smc91c111_init(NICInfo *, uint32_t, qemu_irq); @@ -51,7 +52,6 @@ void retu_key_event(void *retu, int state); /* tc6393xb.c */ typedef struct TC6393xbState TC6393xbState; -#define TC6393XB_RAM 0x110000 /* amount of ram for Video and USB */ TC6393xbState *tc6393xb_init(struct MemoryRegion *sysmem, uint32_t base, qemu_irq irq); void tc6393xb_gpio_out_set(TC6393xbState *s, int line, diff --git a/include/hw/display/bcm2835_fb.h b/include/hw/display/bcm2835_fb.h index ae0a3807f2..228988ba05 100644 --- a/include/hw/display/bcm2835_fb.h +++ b/include/hw/display/bcm2835_fb.h @@ -17,6 +17,20 @@ #define TYPE_BCM2835_FB "bcm2835-fb" #define BCM2835_FB(obj) OBJECT_CHECK(BCM2835FBState, (obj), TYPE_BCM2835_FB) +/* + * Configuration information about the fb which the guest can program + * via the mailbox property interface. + */ +typedef struct { + uint32_t xres, yres; + uint32_t xres_virtual, yres_virtual; + uint32_t xoffset, yoffset; + uint32_t bpp; + uint32_t base; + uint32_t pixo; + uint32_t alpha; +} BCM2835FBConfig; + typedef struct { /*< private >*/ SysBusDevice busdev; @@ -31,16 +45,43 @@ typedef struct { 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; + + BCM2835FBConfig config; + BCM2835FBConfig initial_config; } 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); +void bcm2835_fb_reconfigure(BCM2835FBState *s, BCM2835FBConfig *newconfig); + +/** + * bcm2835_fb_get_pitch: return number of bytes per line of the framebuffer + * @config: configuration info for the framebuffer + * + * Return the number of bytes per line of the framebuffer, ie the number + * that must be added to a pixel address to get the address of the pixel + * directly below it on screen. + */ +static inline uint32_t bcm2835_fb_get_pitch(BCM2835FBConfig *config) +{ + uint32_t xres = MAX(config->xres, config->xres_virtual); + return xres * (config->bpp >> 3); +} + +/** + * bcm2835_fb_get_size: return total size of framebuffer in bytes + * @config: configuration info for the framebuffer + */ +static inline uint32_t bcm2835_fb_get_size(BCM2835FBConfig *config) +{ + uint32_t yres = MAX(config->yres, config->yres_virtual); + return yres * bcm2835_fb_get_pitch(config); +} + +/** + * bcm2835_fb_validate_config: check provided config + * + * Validates the configuration information provided by the guest and + * adjusts it if necessary. + */ +void bcm2835_fb_validate_config(BCM2835FBConfig *config); #endif diff --git a/include/hw/display/edid.h b/include/hw/display/edid.h new file mode 100644 index 0000000000..bacf170889 --- /dev/null +++ b/include/hw/display/edid.h @@ -0,0 +1,27 @@ +#ifndef EDID_H +#define EDID_H + +#include "hw/hw.h" + +typedef struct qemu_edid_info { + const char *vendor; /* http://www.uefi.org/pnp_id_list */ + const char *name; + const char *serial; + uint32_t dpi; + uint32_t prefx; + uint32_t prefy; + uint32_t maxx; + uint32_t maxy; +} qemu_edid_info; + +void qemu_edid_generate(uint8_t *edid, size_t size, + qemu_edid_info *info); +size_t qemu_edid_size(uint8_t *edid); +void qemu_edid_region_io(MemoryRegion *region, Object *owner, + uint8_t *edid, size_t size); + +#define DEFINE_EDID_PROPERTIES(_state, _edid_info) \ + DEFINE_PROP_UINT32("xres", _state, _edid_info.prefx, 0), \ + DEFINE_PROP_UINT32("yres", _state, _edid_info.prefy, 0) + +#endif /* EDID_H */ diff --git a/include/hw/display/milkymist_tmu2.h b/include/hw/display/milkymist_tmu2.h new file mode 100644 index 0000000000..148a119a1d --- /dev/null +++ b/include/hw/display/milkymist_tmu2.h @@ -0,0 +1,41 @@ +/* + * QEMU model of the Milkymist texture mapping unit. + * + * Copyright (c) 2010 Michael Walle + * Copyright (c) 2010 Sebastien Bourdeauducq + * + * + * 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 . + * + * + * Specification available at: + * http://milkymist.walle.cc/socdoc/tmu2.pdf + * + */ + +#ifndef HW_DISPLAY_MILKYMIST_TMU2_H +#define HW_DISPLAY_MILKYMIST_TMU2_H + +#include "hw/qdev.h" + +#if defined(CONFIG_X11) && defined(CONFIG_OPENGL) +DeviceState *milkymist_tmu2_create(hwaddr base, qemu_irq irq); +#else +static inline DeviceState *milkymist_tmu2_create(hwaddr base, qemu_irq irq) +{ + return NULL; +} +#endif + +#endif /* HW_DISPLAY_MILKYMIST_TMU2_H */ diff --git a/include/hw/dma/pl080.h b/include/hw/dma/pl080.h new file mode 100644 index 0000000000..9d4b3df143 --- /dev/null +++ b/include/hw/dma/pl080.h @@ -0,0 +1,71 @@ +/* + * ARM PrimeCell PL080/PL081 DMA controller + * + * Copyright (c) 2006 CodeSourcery. + * Copyright (c) 2018 Linaro Limited + * Written by Paul Brook, Peter Maydell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the Arm PrimeCell PL080/PL081 DMA controller: + * The PL080 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0196g/DDI0196.pdf + * and the PL081 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0218e/DDI0218.pdf + * + * QEMU interface: + * + sysbus IRQ 0: DMACINTR combined interrupt line + * + sysbus IRQ 1: DMACINTERR error interrupt request + * + sysbus IRQ 2: DMACINTTC count interrupt request + * + sysbus MMIO region 0: MemoryRegion for the device's registers + * + QOM property "downstream": MemoryRegion defining where DMA + * bus master transactions are made + */ + +#ifndef HW_DMA_PL080_H +#define HW_DMA_PL080_H + +#include "hw/sysbus.h" + +#define PL080_MAX_CHANNELS 8 + +typedef struct { + uint32_t src; + uint32_t dest; + uint32_t lli; + uint32_t ctrl; + uint32_t conf; +} pl080_channel; + +#define TYPE_PL080 "pl080" +#define TYPE_PL081 "pl081" +#define PL080(obj) OBJECT_CHECK(PL080State, (obj), TYPE_PL080) + +typedef struct PL080State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint8_t tc_int; + uint8_t tc_mask; + uint8_t err_int; + uint8_t err_mask; + uint32_t conf; + uint32_t sync; + uint32_t req_single; + uint32_t req_burst; + pl080_channel chan[PL080_MAX_CHANNELS]; + int nchannels; + /* Flag to avoid recursive DMA invocations. */ + int running; + qemu_irq irq; + qemu_irq interr; + qemu_irq inttc; + + MemoryRegion *downstream; + AddressSpace downstream_as; +} PL080State; + +#endif diff --git a/include/hw/elf_ops.h b/include/hw/elf_ops.h index b6e19e35d0..690f9238c8 100644 --- a/include/hw/elf_ops.h +++ b/include/hw/elf_ops.h @@ -265,7 +265,53 @@ fail: return ret; } +/* + * Given 'nhdr', a pointer to a range of ELF Notes, search through them + * for a note matching type 'elf_note_type' and return a pointer to + * the matching ELF note. + */ +static struct elf_note *glue(get_elf_note_type, SZ)(struct elf_note *nhdr, + elf_word note_size, + elf_word phdr_align, + elf_word elf_note_type) +{ + elf_word nhdr_size = sizeof(struct elf_note); + elf_word elf_note_entry_offset = 0; + elf_word note_type; + elf_word nhdr_namesz; + elf_word nhdr_descsz; + + if (nhdr == NULL) { + return NULL; + } + + note_type = nhdr->n_type; + while (note_type != elf_note_type) { + nhdr_namesz = nhdr->n_namesz; + nhdr_descsz = nhdr->n_descsz; + + elf_note_entry_offset = nhdr_size + + QEMU_ALIGN_UP(nhdr_namesz, phdr_align) + + QEMU_ALIGN_UP(nhdr_descsz, phdr_align); + + /* + * If the offset calculated in this iteration exceeds the + * supplied size, we are done and no matching note was found. + */ + if (elf_note_entry_offset > note_size) { + return NULL; + } + + /* skip to the next ELF Note entry */ + nhdr = (void *)nhdr + elf_note_entry_offset; + note_type = nhdr->n_type; + } + + return nhdr; +} + static int glue(load_elf, SZ)(const char *name, int fd, + uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, int must_swab, uint64_t *pentry, @@ -327,6 +373,14 @@ static int glue(load_elf, SZ)(const char *name, int fd, } } break; + case EM_MIPS: + case EM_NANOMIPS: + if ((ehdr.e_machine != EM_MIPS) && + (ehdr.e_machine != EM_NANOMIPS)) { + ret = ELF_LOAD_WRONG_ARCH; + goto fail; + } + break; default: if (elf_machine != ehdr.e_machine) { ret = ELF_LOAD_WRONG_ARCH; @@ -335,7 +389,7 @@ static int glue(load_elf, SZ)(const char *name, int fd, } if (pentry) - *pentry = (uint64_t)(elf_sword)ehdr.e_entry; + *pentry = (uint64_t)(elf_sword)ehdr.e_entry; glue(load_symbols, SZ)(&ehdr, fd, must_swab, clear_lsb, sym_cb); @@ -474,7 +528,9 @@ static int glue(load_elf, SZ)(const char *name, int fd, rom_add_elf_program(label, data, file_size, mem_size, addr, as); } else { - cpu_physical_memory_write(addr, data, file_size); + address_space_write(as ? as : &address_space_memory, + addr, MEMTXATTRS_UNSPECIFIED, + data, file_size); g_free(data); } } @@ -486,8 +542,39 @@ static int glue(load_elf, SZ)(const char *name, int fd, high = addr + mem_size; data = NULL; + + } else if (ph->p_type == PT_NOTE && elf_note_fn) { + struct elf_note *nhdr = NULL; + + file_size = ph->p_filesz; /* Size of the range of ELF notes */ + data = g_malloc0(file_size); + if (ph->p_filesz > 0) { + if (lseek(fd, ph->p_offset, SEEK_SET) < 0) { + goto fail; + } + if (read(fd, data, file_size) != file_size) { + goto fail; + } + } + + /* + * Search the ELF notes to find one with a type matching the + * value passed in via 'translate_opaque' + */ + nhdr = (struct elf_note *)data; + assert(translate_opaque != NULL); + nhdr = glue(get_elf_note_type, SZ)(nhdr, file_size, ph->p_align, + *(uint64_t *)translate_opaque); + if (nhdr != NULL) { + bool is64 = + sizeof(struct elf_note) == sizeof(struct elf64_note); + elf_note_fn((void *)nhdr, (void *)&ph->p_align, is64); + } + g_free(data); + data = NULL; } } + g_free(phdr); if (lowaddr) *lowaddr = (uint64_t)(elf_sword)low; diff --git a/include/hw/smbios/smbios.h b/include/hw/firmware/smbios.h similarity index 99% rename from include/hw/smbios/smbios.h rename to include/hw/firmware/smbios.h index eeb5a4d7b6..6fef32a3c9 100644 --- a/include/hw/smbios/smbios.h +++ b/include/hw/firmware/smbios.h @@ -162,6 +162,7 @@ struct smbios_type_3 { uint8_t height; uint8_t number_of_power_cords; uint8_t contained_element_count; + uint8_t contained_element_record_length; uint8_t sku_number_str; /* contained elements follow */ } QEMU_PACKED; diff --git a/include/hw/fw-path-provider.h b/include/hw/fw-path-provider.h index 050cb05d92..5df893a3d8 100644 --- a/include/hw/fw-path-provider.h +++ b/include/hw/fw-path-provider.h @@ -30,9 +30,7 @@ #define FW_PATH_PROVIDER(obj) \ INTERFACE_CHECK(FWPathProvider, (obj), TYPE_FW_PATH_PROVIDER) -typedef struct FWPathProvider { - Object parent_obj; -} FWPathProvider; +typedef struct FWPathProvider FWPathProvider; typedef struct FWPathProviderClass { InterfaceClass parent_class; diff --git a/include/hw/gpio/nrf51_gpio.h b/include/hw/gpio/nrf51_gpio.h new file mode 100644 index 0000000000..337ee534bb --- /dev/null +++ b/include/hw/gpio/nrf51_gpio.h @@ -0,0 +1,69 @@ +/* + * nRF51 System-on-Chip general purpose input/output register definition + * + * QEMU interface: + * + sysbus MMIO regions 0: GPIO registers + * + Unnamed GPIO inputs 0-31: Set tri-state input level for GPIO pin. + * Level -1: Externally Disconnected/Floating; Pull-up/down will be regarded + * Level 0: Input externally driven LOW + * Level 1: Input externally driven HIGH + * + Unnamed GPIO outputs 0-31: + * Level -1: Disconnected/Floating + * Level 0: Driven LOW + * Level 1: Driven HIGH + * + * Accuracy of the peripheral model: + * + The nRF51 GPIO output driver supports two modes, standard and high-current + * mode. These different drive modes are not modeled and handled the same. + * + Pin SENSEing is not modeled/implemented. + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ +#ifndef NRF51_GPIO_H +#define NRF51_GPIO_H + +#include "hw/sysbus.h" +#define TYPE_NRF51_GPIO "nrf51_soc.gpio" +#define NRF51_GPIO(obj) OBJECT_CHECK(NRF51GPIOState, (obj), TYPE_NRF51_GPIO) + +#define NRF51_GPIO_PINS 32 + +#define NRF51_GPIO_SIZE 0x1000 + +#define NRF51_GPIO_REG_OUT 0x504 +#define NRF51_GPIO_REG_OUTSET 0x508 +#define NRF51_GPIO_REG_OUTCLR 0x50C +#define NRF51_GPIO_REG_IN 0x510 +#define NRF51_GPIO_REG_DIR 0x514 +#define NRF51_GPIO_REG_DIRSET 0x518 +#define NRF51_GPIO_REG_DIRCLR 0x51C +#define NRF51_GPIO_REG_CNF_START 0x700 +#define NRF51_GPIO_REG_CNF_END 0x77F + +#define NRF51_GPIO_PULLDOWN 1 +#define NRF51_GPIO_PULLUP 3 + +typedef struct NRF51GPIOState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq; + + uint32_t out; + uint32_t in; + uint32_t in_mask; + uint32_t dir; + uint32_t cnf[NRF51_GPIO_PINS]; + + uint32_t old_out; + uint32_t old_out_connected; + + qemu_irq output[NRF51_GPIO_PINS]; +} NRF51GPIOState; + + +#endif diff --git a/include/hw/hotplug.h b/include/hw/hotplug.h index 51541d63e1..6321e292fd 100644 --- a/include/hw/hotplug.h +++ b/include/hw/hotplug.h @@ -23,11 +23,7 @@ #define HOTPLUG_HANDLER(obj) \ INTERFACE_CHECK(HotplugHandler, (obj), TYPE_HOTPLUG_HANDLER) - -typedef struct HotplugHandler { - /* */ - Object Parent; -} HotplugHandler; +typedef struct HotplugHandler HotplugHandler; /** * hotplug_fn: @@ -47,8 +43,6 @@ typedef void (*hotplug_fn)(HotplugHandler *plug_handler, * @parent: Opaque parent interface. * @pre_plug: pre plug callback called at start of device.realize(true) * @plug: plug callback called at end of device.realize(true). - * @post_plug: post plug callback called after device.realize(true) and device - * reset * @unplug_request: unplug request callback. * Used as a means to initiate device unplug for devices that * require asynchronous unplug handling. @@ -63,7 +57,6 @@ typedef struct HotplugHandlerClass { /* */ hotplug_fn pre_plug; hotplug_fn plug; - void (*post_plug)(HotplugHandler *plug_handler, DeviceState *plugged_dev); hotplug_fn unplug_request; hotplug_fn unplug; } HotplugHandlerClass; @@ -86,14 +79,6 @@ void hotplug_handler_pre_plug(HotplugHandler *plug_handler, DeviceState *plugged_dev, Error **errp); -/** - * hotplug_handler_post_plug: - * - * Call #HotplugHandlerClass.post_plug callback of @plug_handler. - */ -void hotplug_handler_post_plug(HotplugHandler *plug_handler, - DeviceState *plugged_dev); - /** * hotplug_handler_unplug_request: * diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h new file mode 100644 index 0000000000..21dc28aee9 --- /dev/null +++ b/include/hw/hyperv/hyperv-proto.h @@ -0,0 +1,130 @@ +/* + * Definitions for Hyper-V guest/hypervisor interaction + * + * Copyright (c) 2017-2018 Virtuozzo International GmbH. + * + * 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 HW_HYPERV_HYPERV_PROTO_H +#define HW_HYPERV_HYPERV_PROTO_H + +#include "qemu/bitmap.h" + +/* + * 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_INVALID_PARAMETER 5 +#define HV_STATUS_INSUFFICIENT_MEMORY 11 +#define HV_STATUS_INVALID_PORT_ID 17 +#define HV_STATUS_INVALID_CONNECTION_ID 18 +#define HV_STATUS_INSUFFICIENT_BUFFERS 19 + +/* + * Hypercall numbers + */ +#define HV_POST_MESSAGE 0x005c +#define HV_SIGNAL_EVENT 0x005d +#define HV_HYPERCALL_FAST (1u << 16) + +/* + * Message size + */ +#define HV_MESSAGE_PAYLOAD_SIZE 240 + +/* + * Message types + */ +#define HV_MESSAGE_NONE 0x00000000 +#define HV_MESSAGE_VMBUS 0x00000001 +#define HV_MESSAGE_UNMAPPED_GPA 0x80000000 +#define HV_MESSAGE_GPA_INTERCEPT 0x80000001 +#define HV_MESSAGE_TIMER_EXPIRED 0x80000010 +#define HV_MESSAGE_INVALID_VP_REGISTER_VALUE 0x80000020 +#define HV_MESSAGE_UNRECOVERABLE_EXCEPTION 0x80000021 +#define HV_MESSAGE_UNSUPPORTED_FEATURE 0x80000022 +#define HV_MESSAGE_EVENTLOG_BUFFERCOMPLETE 0x80000040 +#define HV_MESSAGE_X64_IOPORT_INTERCEPT 0x80010000 +#define HV_MESSAGE_X64_MSR_INTERCEPT 0x80010001 +#define HV_MESSAGE_X64_CPUID_INTERCEPT 0x80010002 +#define HV_MESSAGE_X64_EXCEPTION_INTERCEPT 0x80010003 +#define HV_MESSAGE_X64_APIC_EOI 0x80010004 +#define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005 + +/* + * Message flags + */ +#define HV_MESSAGE_FLAG_PENDING 0x1 + +/* + * Number of synthetic interrupts + */ +#define HV_SINT_COUNT 16 + +/* + * Event flags number per SINT + */ +#define HV_EVENT_FLAGS_COUNT (256 * 8) + +/* + * Connection id valid bits + */ +#define HV_CONNECTION_ID_MASK 0x00ffffff + +/* + * Input structure for POST_MESSAGE hypercall + */ +struct hyperv_post_message_input { + uint32_t connection_id; + uint32_t _reserved; + uint32_t message_type; + uint32_t payload_size; + uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE]; +}; + +/* + * Input structure for SIGNAL_EVENT hypercall + */ +struct hyperv_signal_event_input { + uint32_t connection_id; + uint16_t flag_number; + uint16_t _reserved_zero; +}; + +/* + * SynIC message structures + */ +struct hyperv_message_header { + uint32_t message_type; + uint8_t payload_size; + uint8_t message_flags; /* HV_MESSAGE_FLAG_XX */ + uint8_t _reserved[2]; + uint64_t sender; +}; + +struct hyperv_message { + struct hyperv_message_header header; + uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE]; +}; + +struct hyperv_message_page { + struct hyperv_message slot[HV_SINT_COUNT]; +}; + +/* + * SynIC event flags structures + */ +struct hyperv_event_flags { + DECLARE_BITMAP(flags, HV_EVENT_FLAGS_COUNT); +}; + +struct hyperv_event_flags_page { + struct hyperv_event_flags slot[HV_SINT_COUNT]; +}; + +#endif diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h new file mode 100644 index 0000000000..597381cb01 --- /dev/null +++ b/include/hw/hyperv/hyperv.h @@ -0,0 +1,83 @@ +/* + * Hyper-V guest/hypervisor interaction + * + * Copyright (c) 2015-2018 Virtuozzo International GmbH. + * + * 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 HW_HYPERV_HYPERV_H +#define HW_HYPERV_HYPERV_H + +#include "cpu-qom.h" +#include "hw/hyperv/hyperv-proto.h" + +typedef struct HvSintRoute HvSintRoute; + +/* + * Callback executed in a bottom-half when the status of posting the message + * becomes known, before unblocking the connection for further messages + */ +typedef void (*HvSintMsgCb)(void *data, int status); + +HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, + HvSintMsgCb cb, void *cb_data); +void hyperv_sint_route_ref(HvSintRoute *sint_route); +void hyperv_sint_route_unref(HvSintRoute *sint_route); + +int hyperv_sint_route_set_sint(HvSintRoute *sint_route); + +/* + * Submit a message to be posted in vcpu context. If the submission succeeds, + * the status of posting the message is reported via the callback associated + * with the @sint_route; until then no more messages are accepted. + */ +int hyperv_post_msg(HvSintRoute *sint_route, struct hyperv_message *msg); +/* + * Set event flag @eventno, and signal the SINT if the flag has changed. + */ +int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno); + +/* + * Handler for messages arriving from the guest via HV_POST_MESSAGE hypercall. + * Executed in vcpu context. + */ +typedef uint16_t (*HvMsgHandler)(const struct hyperv_post_message_input *msg, + void *data); +/* + * Associate @handler with the message connection @conn_id, such that @handler + * is called with @data when the guest executes HV_POST_MESSAGE hypercall on + * @conn_id. If @handler is NULL clear the association. + */ +int hyperv_set_msg_handler(uint32_t conn_id, HvMsgHandler handler, void *data); +/* + * Associate @notifier with the event connection @conn_id, such that @notifier + * is signaled when the guest executes HV_SIGNAL_EVENT hypercall on @conn_id. + * If @notifier is NULL clear the association. + */ +int hyperv_set_event_flag_handler(uint32_t conn_id, EventNotifier *notifier); + +/* + * Process HV_POST_MESSAGE hypercall: parse the data in the guest memory as + * specified in @param, and call the HvMsgHandler associated with the + * connection on the message contained therein. + */ +uint16_t hyperv_hcall_post_message(uint64_t param, bool fast); +/* + * Process HV_SIGNAL_EVENT hypercall: signal the EventNotifier associated with + * the connection as specified in @param. + */ +uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast); + +static inline uint32_t hyperv_vp_index(CPUState *cs) +{ + return cs->cpu_index; +} + +void hyperv_synic_add(CPUState *cs); +void hyperv_synic_reset(CPUState *cs); +void hyperv_synic_update(CPUState *cs, bool enable, + hwaddr msg_page_addr, hwaddr event_page_addr); + +#endif diff --git a/include/hw/i2c/i2c-ddc.h b/include/hw/i2c/i2c-ddc.h index d9b5f33f58..c29443c5af 100644 --- a/include/hw/i2c/i2c-ddc.h +++ b/include/hw/i2c/i2c-ddc.h @@ -19,14 +19,16 @@ #ifndef I2C_DDC_H #define I2C_DDC_H -/* A simple I2C slave which just returns the contents of its EDID blob. */ +#include "hw/display/edid.h" +/* A simple I2C slave which just returns the contents of its EDID blob. */ struct I2CDDCState { /*< private >*/ I2CSlave i2c; /*< public >*/ bool firstbyte; uint8_t reg; + qemu_edid_info edid_info; uint8_t edid_blob[128]; }; diff --git a/include/hw/i2c/i2c.h b/include/hw/i2c/i2c.h index 5dc166158b..8e236f7bb4 100644 --- a/include/hw/i2c/i2c.h +++ b/include/hw/i2c/i2c.h @@ -33,10 +33,9 @@ typedef struct I2CSlaveClass { /* * Slave to master. This cannot fail, the device should always - * return something here. Negative values probably result in 0xff - * and a possible log from the driver, and shouldn't be used. + * return something here. */ - int (*recv)(I2CSlave *s); + uint8_t (*recv)(I2CSlave *s); /* * Notify the slave of a bus state change. For start event, @@ -78,10 +77,12 @@ void i2c_end_transfer(I2CBus *bus); void i2c_nack(I2CBus *bus); int i2c_send_recv(I2CBus *bus, uint8_t *data, bool send); int i2c_send(I2CBus *bus, uint8_t data); -int i2c_recv(I2CBus *bus); +uint8_t i2c_recv(I2CBus *bus); DeviceState *i2c_create_slave(I2CBus *bus, const char *name, uint8_t addr); +typedef struct bitbang_i2c_interface bitbang_i2c_interface; + /* lm832x.c */ void lm832x_key_event(DeviceState *dev, int key, int state); diff --git a/include/hw/i2c/microbit_i2c.h b/include/hw/i2c/microbit_i2c.h new file mode 100644 index 0000000000..aad636127e --- /dev/null +++ b/include/hw/i2c/microbit_i2c.h @@ -0,0 +1,42 @@ +/* + * Microbit stub for Nordic Semiconductor nRF51 SoC Two-Wire Interface + * http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.1.pdf + * + * Copyright 2019 Red Hat, Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ + +#ifndef MICROBIT_I2C_H +#define MICROBIT_I2C_H + +#include "hw/sysbus.h" +#include "hw/arm/nrf51.h" + +#define NRF51_TWI_TASK_STARTRX 0x000 +#define NRF51_TWI_TASK_STARTTX 0x008 +#define NRF51_TWI_TASK_STOP 0x014 +#define NRF51_TWI_EVENT_STOPPED 0x104 +#define NRF51_TWI_EVENT_RXDREADY 0x108 +#define NRF51_TWI_EVENT_TXDSENT 0x11c +#define NRF51_TWI_REG_ENABLE 0x500 +#define NRF51_TWI_REG_RXD 0x518 +#define NRF51_TWI_REG_TXD 0x51c +#define NRF51_TWI_REG_ADDRESS 0x588 + +#define TYPE_MICROBIT_I2C "microbit.i2c" +#define MICROBIT_I2C(obj) \ + OBJECT_CHECK(MicrobitI2CState, (obj), TYPE_MICROBIT_I2C) + +#define MICROBIT_I2C_NREGS (NRF51_TWI_SIZE / sizeof(uint32_t)) + +typedef struct { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t regs[MICROBIT_I2C_NREGS]; + uint32_t read_idx; +} MicrobitI2CState; + +#endif /* MICROBIT_I2C_H */ diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 2a837afdcb..fb55c44444 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -1,6 +1,10 @@ #ifndef PM_SMBUS_H #define PM_SMBUS_H +#include "hw/i2c/smbus_master.h" + +#define PM_SMBUS_MAX_MSG_SIZE 32 + typedef struct PMSMBus { I2CBus *smbus; MemoryRegion io; @@ -11,10 +15,41 @@ typedef struct PMSMBus { uint8_t smb_addr; uint8_t smb_data0; uint8_t smb_data1; - uint8_t smb_data[32]; - uint8_t smb_index; + uint8_t smb_data[PM_SMBUS_MAX_MSG_SIZE]; + uint8_t smb_blkdata; + uint8_t smb_auxctl; + uint32_t smb_index; + + /* Set by pm_smbus.c */ + void (*reset)(struct PMSMBus *s); + + /* Set by the user. */ + bool i2c_enable; + void (*set_irq)(struct PMSMBus *s, bool enabled); + void *opaque; + + /* Internally used by pm_smbus. */ + + /* Set on block transfers after the last byte has been read, so the + INTR bit can be set at the right time. */ + bool op_done; + + /* Set during an I2C block read, so we know how to handle data. */ + bool in_i2c_block_read; + + /* Used to work around a bug in AMIBIOS, see smb_transaction_start() */ + bool start_transaction_on_status_read; } PMSMBus; -void pm_smbus_init(DeviceState *parent, PMSMBus *smb); +void pm_smbus_init(DeviceState *parent, PMSMBus *smb, bool force_aux_blk); + +/* + * For backwards compatibility on migration, older versions don't have + * working migration for pm_smbus, this lets us ignore the migrations + * for older machine versions. + */ +bool pm_smbus_vmstate_needed(void); + +extern const VMStateDescription pmsmb_vmstate; #endif /* PM_SMBUS_H */ diff --git a/include/hw/i2c/ppc4xx_i2c.h b/include/hw/i2c/ppc4xx_i2c.h index 0891a9c948..b3450bacf7 100644 --- a/include/hw/i2c/ppc4xx_i2c.h +++ b/include/hw/i2c/ppc4xx_i2c.h @@ -31,9 +31,6 @@ #include "hw/sysbus.h" #include "hw/i2c/i2c.h" -/* from hw/i2c/bitbang_i2c.h */ -typedef struct bitbang_i2c_interface bitbang_i2c_interface; - #define TYPE_PPC4xx_I2C "ppc4xx-i2c" #define PPC4xx_I2C(obj) OBJECT_CHECK(PPC4xxI2CState, (obj), TYPE_PPC4xx_I2C) diff --git a/include/hw/i2c/smbus_eeprom.h b/include/hw/i2c/smbus_eeprom.h new file mode 100644 index 0000000000..0f96836bab --- /dev/null +++ b/include/hw/i2c/smbus_eeprom.h @@ -0,0 +1,35 @@ +/* + * QEMU SMBus EEPROM API + * + * 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_SMBUS_EEPROM_H +#define HW_SMBUS_EEPROM_H + +#include "hw/i2c/i2c.h" + +void smbus_eeprom_init_one(I2CBus *bus, uint8_t address, uint8_t *eeprom_buf); +void smbus_eeprom_init(I2CBus *bus, int nb_eeprom, + const uint8_t *eeprom_spd, int size); + +enum sdram_type { SDR = 0x4, DDR = 0x7, DDR2 = 0x8 }; +uint8_t *spd_data_generate(enum sdram_type type, ram_addr_t size, Error **errp); + +#endif diff --git a/include/hw/i2c/smbus.h b/include/hw/i2c/smbus_master.h similarity index 50% rename from include/hw/i2c/smbus.h rename to include/hw/i2c/smbus_master.h index 4fdba022c1..bb13bc423c 100644 --- a/include/hw/i2c/smbus.h +++ b/include/hw/i2c/smbus_master.h @@ -1,8 +1,5 @@ -#ifndef QEMU_SMBUS_H -#define QEMU_SMBUS_H - /* - * QEMU SMBus API + * QEMU SMBus host (master) API * * Copyright (c) 2007 Arastra, Inc. * @@ -25,45 +22,11 @@ * THE SOFTWARE. */ +#ifndef HW_SMBUS_MASTER_H +#define HW_SMBUS_MASTER_H + #include "hw/i2c/i2c.h" -#define TYPE_SMBUS_DEVICE "smbus-device" -#define SMBUS_DEVICE(obj) \ - OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE) -#define SMBUS_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE) -#define SMBUS_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE) - -typedef struct SMBusDeviceClass -{ - I2CSlaveClass parent_class; - void (*quick_cmd)(SMBusDevice *dev, uint8_t read); - void (*send_byte)(SMBusDevice *dev, uint8_t val); - uint8_t (*receive_byte)(SMBusDevice *dev); - /* We can't distinguish between a word write and a block write with - length 1, so pass the whole data block including the length byte - (if present). The device is responsible figuring out what type of - command this is. */ - void (*write_data)(SMBusDevice *dev, uint8_t cmd, uint8_t *buf, int len); - /* Likewise we can't distinguish between different reads, or even know - the length of the read until the read is complete, so read data a - byte at a time. The device is responsible for adding the length - byte on block reads. */ - uint8_t (*read_data)(SMBusDevice *dev, uint8_t cmd, int n); -} SMBusDeviceClass; - -struct SMBusDevice { - /* The SMBus protocol is implemented on top of I2C. */ - I2CSlave i2c; - - /* Remaining fields for internal use only. */ - int mode; - int data_len; - uint8_t data_buf[34]; /* command + len + 32 bytes of data. */ - uint8_t command; -}; - /* Master device commands. */ int smbus_quick_command(I2CBus *bus, uint8_t addr, int read); int smbus_receive_byte(I2CBus *bus, uint8_t addr); @@ -72,12 +35,21 @@ int smbus_read_byte(I2CBus *bus, uint8_t addr, uint8_t command); int smbus_write_byte(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t data); int smbus_read_word(I2CBus *bus, uint8_t addr, uint8_t command); int smbus_write_word(I2CBus *bus, uint8_t addr, uint8_t command, uint16_t data); -int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data); -int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, - int len); -void smbus_eeprom_init_one(I2CBus *smbus, uint8_t address, uint8_t *eeprom_buf); -void smbus_eeprom_init(I2CBus *smbus, int nb_eeprom, - const uint8_t *eeprom_spd, int size); +/* + * Do a block transfer from an I2C device. If recv_len is set, then the + * first received byte is a length field and is used to know how much data + * to receive. Otherwise receive "len" bytes. If send_cmd is set, send + * the command byte first before receiving the data. + */ +int smbus_read_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool recv_len, bool send_cmd); + +/* + * Do a block transfer to an I2C device. If send_len is set, send the + * "len" value before the data. + */ +int smbus_write_block(I2CBus *bus, uint8_t addr, uint8_t command, uint8_t *data, + int len, bool send_len); #endif diff --git a/include/hw/i2c/smbus_slave.h b/include/hw/i2c/smbus_slave.h new file mode 100644 index 0000000000..ebe068304e --- /dev/null +++ b/include/hw/i2c/smbus_slave.h @@ -0,0 +1,100 @@ +/* + * QEMU SMBus device (slave) API + * + * Copyright (c) 2007 Arastra, 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 HW_SMBUS_SLAVE_H +#define HW_SMBUS_SLAVE_H + +#include "hw/i2c/i2c.h" + +#define TYPE_SMBUS_DEVICE "smbus-device" +#define SMBUS_DEVICE(obj) \ + OBJECT_CHECK(SMBusDevice, (obj), TYPE_SMBUS_DEVICE) +#define SMBUS_DEVICE_CLASS(klass) \ + OBJECT_CLASS_CHECK(SMBusDeviceClass, (klass), TYPE_SMBUS_DEVICE) +#define SMBUS_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SMBusDeviceClass, (obj), TYPE_SMBUS_DEVICE) + +typedef struct SMBusDevice SMBusDevice; + +typedef struct SMBusDeviceClass +{ + I2CSlaveClass parent_class; + + /* + * An operation with no data, special in SMBus. + * This may be NULL, quick commands are ignore in that case. + */ + void (*quick_cmd)(SMBusDevice *dev, uint8_t read); + + /* + * We can't distinguish between a word write and a block write with + * length 1, so pass the whole data block including the length byte + * (if present). The device is responsible figuring out what type of + * command this is. + * This may be NULL if no data is written to the device. Writes + * will be ignore in that case. + */ + int (*write_data)(SMBusDevice *dev, uint8_t *buf, uint8_t len); + + /* + * Likewise we can't distinguish between different reads, or even know + * the length of the read until the read is complete, so read data a + * byte at a time. The device is responsible for adding the length + * byte on block reads. This call cannot fail, it should return + * something, preferably 0xff if nothing is available. + * This may be NULL if no data is read from the device. Reads will + * return 0xff in that case. + */ + uint8_t (*receive_byte)(SMBusDevice *dev); +} SMBusDeviceClass; + +#define SMBUS_DATA_MAX_LEN 34 /* command + len + 32 bytes of data. */ + +struct SMBusDevice { + /* The SMBus protocol is implemented on top of I2C. */ + I2CSlave i2c; + + /* Remaining fields for internal use only. */ + int32_t mode; + int32_t data_len; + uint8_t data_buf[SMBUS_DATA_MAX_LEN]; +}; + +extern const VMStateDescription vmstate_smbus_device; + +#define VMSTATE_SMBUS_DEVICE(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(SMBusDevice), \ + .vmsd = &vmstate_smbus_device, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, SMBusDevice), \ +} + +/* + * Users should call this in their .needed functions to know if the + * SMBus slave data needs to be transferred. + */ +bool smbus_vmstate_needed(SMBusDevice *dev); + +#endif diff --git a/include/hw/i386/intel_iommu.h b/include/hw/i386/intel_iommu.h index fbfedcb1c0..12f3d266e2 100644 --- a/include/hw/i386/intel_iommu.h +++ b/include/hw/i386/intel_iommu.h @@ -66,13 +66,20 @@ typedef struct VTDIOTLBEntry VTDIOTLBEntry; typedef struct VTDBus VTDBus; typedef union VTD_IR_TableEntry VTD_IR_TableEntry; typedef union VTD_IR_MSIAddress VTD_IR_MSIAddress; -typedef struct VTDIrq VTDIrq; -typedef struct VTD_MSIMessage VTD_MSIMessage; +typedef struct VTDPASIDDirEntry VTDPASIDDirEntry; +typedef struct VTDPASIDEntry VTDPASIDEntry; /* Context-Entry */ struct VTDContextEntry { - uint64_t lo; - uint64_t hi; + union { + struct { + uint64_t lo; + uint64_t hi; + }; + struct { + uint64_t val[4]; + }; + }; }; struct VTDContextCacheEntry { @@ -83,13 +90,23 @@ struct VTDContextCacheEntry { struct VTDContextEntry context_entry; }; +/* PASID Directory Entry */ +struct VTDPASIDDirEntry { + uint64_t val; +}; + +/* PASID Table Entry */ +struct VTDPASIDEntry { + uint64_t val[8]; +}; + struct VTDAddressSpace { PCIBus *bus; uint8_t devfn; AddressSpace as; IOMMUMemoryRegion iommu; - MemoryRegion root; - MemoryRegion sys_alias; + MemoryRegion root; /* The root container of the device */ + MemoryRegion nodmar; /* The alias of shared nodmar MR */ MemoryRegion iommu_ir; /* Interrupt region: 0xfeeXXXXX */ IntelIOMMUState *iommu_state; VTDContextCacheEntry context_cache_entry; @@ -197,63 +214,6 @@ union VTD_IR_MSIAddress { uint32_t data; }; -/* Generic IRQ entry information */ -struct VTDIrq { - /* Used by both IOAPIC/MSI interrupt remapping */ - uint8_t trigger_mode; - uint8_t vector; - uint8_t delivery_mode; - uint32_t dest; - uint8_t dest_mode; - - /* only used by MSI interrupt remapping */ - uint8_t redir_hint; - uint8_t msi_addr_last_bits; -}; - -struct VTD_MSIMessage { - union { - struct { -#ifdef HOST_WORDS_BIGENDIAN - uint32_t __addr_head:12; /* 0xfee */ - uint32_t dest:8; - uint32_t __reserved:8; - uint32_t redir_hint:1; - uint32_t dest_mode:1; - uint32_t __not_used:2; -#else - uint32_t __not_used:2; - uint32_t dest_mode:1; - uint32_t redir_hint:1; - uint32_t __reserved:8; - uint32_t dest:8; - uint32_t __addr_head:12; /* 0xfee */ -#endif - uint32_t __addr_hi; - } QEMU_PACKED; - uint64_t msi_addr; - }; - union { - struct { -#ifdef HOST_WORDS_BIGENDIAN - uint16_t trigger_mode:1; - uint16_t level:1; - uint16_t __resved:3; - uint16_t delivery_mode:3; - uint16_t vector:8; -#else - uint16_t vector:8; - uint16_t delivery_mode:3; - uint16_t __resved:3; - uint16_t level:1; - uint16_t trigger_mode:1; -#endif - uint16_t __resved1; - } QEMU_PACKED; - uint32_t msi_data; - }; -}; - /* When IR is enabled, all MSI/MSI-X data bits should be zero */ #define VTD_IR_MSI_DATA (0) @@ -261,22 +221,27 @@ struct VTD_MSIMessage { struct IntelIOMMUState { X86IOMMUState x86_iommu; MemoryRegion csrmem; + MemoryRegion mr_nodmar; + MemoryRegion mr_ir; + MemoryRegion mr_sys_alias; uint8_t csr[DMAR_REG_SIZE]; /* register values */ uint8_t wmask[DMAR_REG_SIZE]; /* R/W bytes */ uint8_t w1cmask[DMAR_REG_SIZE]; /* RW1C(Write 1 to Clear) bytes */ uint8_t womask[DMAR_REG_SIZE]; /* WO (write only - read returns 0) */ uint32_t version; - bool caching_mode; /* RO - is cap CM enabled? */ + bool caching_mode; /* RO - is cap CM enabled? */ + bool scalable_mode; /* RO - is Scalable Mode supported? */ dma_addr_t root; /* Current root table pointer */ - bool root_extended; /* Type of root table (extended or not) */ + bool root_scalable; /* Type of root table (scalable or not) */ bool dmar_enabled; /* Set if DMA remapping is enabled */ uint16_t iq_head; /* Current invalidation queue head */ uint16_t iq_tail; /* Current invalidation queue tail */ dma_addr_t iq; /* Current invalidation queue pointer */ uint16_t iq_size; /* IQ Size in number of entries */ + bool iq_dw; /* IQ descriptor width 256bit or not */ bool qi_enabled; /* Set if the QI is enabled */ uint8_t iq_last_desc_type; /* The type of last completed descriptor */ @@ -304,6 +269,7 @@ struct IntelIOMMUState { OnOffAuto intr_eim; /* Toggle for EIM cabability */ bool buggy_eim; /* Force buggy EIM unless eim=off */ uint8_t aw_bits; /* Host/IOVA address width (in bits) */ + bool dma_drain; /* Whether DMA r/w draining enabled */ /* * Protects IOMMU states in general. Currently it protects the diff --git a/include/hw/i386/ioapic.h b/include/hw/i386/ioapic.h index 9c8816f11f..59fcb158a7 100644 --- a/include/hw/i386/ioapic.h +++ b/include/hw/i386/ioapic.h @@ -23,6 +23,9 @@ #define IOAPIC_NUM_PINS 24 #define IO_APIC_DEFAULT_ADDRESS 0xfec00000 +#define TYPE_KVM_IOAPIC "kvm-ioapic" +#define TYPE_IOAPIC "ioapic" + void ioapic_eoi_broadcast(int vector); #endif /* HW_IOAPIC_H */ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 6894f37df1..ca65ef18af 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -6,6 +6,7 @@ #include "hw/boards.h" #include "hw/isa/isa.h" #include "hw/block/fdc.h" +#include "hw/block/flash.h" #include "net/net.h" #include "hw/i386/ioapic.h" @@ -13,7 +14,6 @@ #include "qemu/bitmap.h" #include "sysemu/sysemu.h" #include "hw/pci/pci.h" -#include "hw/compat.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" #include "hw/acpi/acpi_dev_interface.h" @@ -40,18 +40,17 @@ struct PCMachineState { PCIBus *bus; FWCfgState *fw_cfg; qemu_irq *gsi; + PFlashCFI01 *flash[2]; /* Configuration options: */ uint64_t max_ram_below_4g; OnOffAuto vmport; OnOffAuto smm; - AcpiNVDIMMState acpi_nvdimm_state; - bool acpi_build_enabled; - bool smbus; - bool sata; - bool pit; + bool smbus_enabled; + bool sata_enabled; + bool pit_enabled; /* RAM information (sizes, addresses, configuration): */ ram_addr_t below_4g_mem_size, above_4g_mem_size; @@ -75,8 +74,6 @@ 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_NVDIMM "nvdimm" -#define PC_MACHINE_NVDIMM_PERSIST "nvdimm-persistence" #define PC_MACHINE_SMBUS "smbus" #define PC_MACHINE_SATA "sata" #define PC_MACHINE_PIT "pit" @@ -97,7 +94,7 @@ struct PCMachineState { * way we can use 1GByte pages in the host. * */ -struct PCMachineClass { +typedef struct PCMachineClass { /*< private >*/ MachineClass parent_class; @@ -134,7 +131,10 @@ struct PCMachineClass { /* use DMA capable linuxboot option rom */ bool linuxboot_dma_enabled; -}; + + /* use PVH to load kernels that support this feature */ + bool pvh_enabled; +} PCMachineClass; #define TYPE_PC_MACHINE "generic-pc-machine" #define PC_MACHINE(obj) \ @@ -187,7 +187,6 @@ void pc_acpi_smi_interrupt(void *opaque, int irq, int level); 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); void pc_guest_info_init(PCMachineState *pcms); @@ -276,8 +275,8 @@ extern PCIDevice *piix4_dev; int piix4_init(PCIBus *bus, ISABus **isa_bus, int devfn); /* pc_sysfw.c */ -void pc_system_firmware_init(MemoryRegion *rom_memory, - bool isapc_ram_fw); +void pc_system_flash_create(PCMachineState *pcms); +void pc_system_firmware_init(PCMachineState *pcms, MemoryRegion *rom_memory); /* acpi-build.c */ void pc_madt_cpu_entry(AcpiDeviceIf *adev, int uid, @@ -294,652 +293,70 @@ 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_12 \ - HW_COMPAT_2_12 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "legacy-cache",\ - .value = "on",\ - },{\ - .driver = TYPE_X86_CPU,\ - .property = "topoext",\ - .value = "off",\ - },{\ - .driver = "EPYC-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "EPYC-IBPB-" TYPE_X86_CPU,\ - .property = "xlevel",\ - .value = stringify(0x8000000a),\ - }, +extern GlobalProperty pc_compat_3_1[]; +extern const size_t pc_compat_3_1_len; -#define PC_COMPAT_2_11 \ - HW_COMPAT_2_11 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "x-migrate-smi-count",\ - .value = "off",\ - },{\ - .driver = "Skylake-Server" "-" TYPE_X86_CPU,\ - .property = "clflushopt",\ - .value = "off",\ - }, +extern GlobalProperty pc_compat_3_0[]; +extern const size_t pc_compat_3_0_len; -#define PC_COMPAT_2_10 \ - HW_COMPAT_2_10 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "x-hv-max-vps",\ - .value = "0x40",\ - },{\ - .driver = "i440FX-pcihost",\ - .property = "x-pci-hole64-fix",\ - .value = "off",\ - },{\ - .driver = "q35-pcihost",\ - .property = "x-pci-hole64-fix",\ - .value = "off",\ - }, +extern GlobalProperty pc_compat_2_12[]; +extern const size_t pc_compat_2_12_len; -#define PC_COMPAT_2_9 \ - HW_COMPAT_2_9 \ - {\ - .driver = "mch",\ - .property = "extended-tseg-mbytes",\ - .value = stringify(0),\ - },\ +extern GlobalProperty pc_compat_2_11[]; +extern const size_t pc_compat_2_11_len; -#define PC_COMPAT_2_8 \ - HW_COMPAT_2_8 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "tcg-cpuid",\ - .value = "off",\ - },\ - {\ - .driver = "kvmclock",\ - .property = "x-mach-use-reliable-get-clock",\ - .value = "off",\ - },\ - {\ - .driver = "ICH9-LPC",\ - .property = "x-smi-broadcast",\ - .value = "off",\ - },\ - {\ - .driver = TYPE_X86_CPU,\ - .property = "vmware-cpuid-freq",\ - .value = "off",\ - },\ - {\ - .driver = "Haswell-" TYPE_X86_CPU,\ - .property = "stepping",\ - .value = "1",\ - }, +extern GlobalProperty pc_compat_2_10[]; +extern const size_t pc_compat_2_10_len; -#define PC_COMPAT_2_7 \ - HW_COMPAT_2_7 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "l3-cache",\ - .value = "off",\ - },\ - {\ - .driver = TYPE_X86_CPU,\ - .property = "full-cpuid-auto-level",\ - .value = "off",\ - },\ - {\ - .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ - .property = "family",\ - .value = "15",\ - },\ - {\ - .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ - .property = "model",\ - .value = "6",\ - },\ - {\ - .driver = "Opteron_G3" "-" TYPE_X86_CPU,\ - .property = "stepping",\ - .value = "1",\ - },\ - {\ - .driver = "isa-pcspk",\ - .property = "migrate",\ - .value = "off",\ - }, +extern GlobalProperty pc_compat_2_9[]; +extern const size_t pc_compat_2_9_len; -#define PC_COMPAT_2_6 \ - HW_COMPAT_2_6 \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "cpuid-0xb",\ - .value = "off",\ - },{\ - .driver = "vmxnet3",\ - .property = "romfile",\ - .value = "",\ - },\ - {\ - .driver = TYPE_X86_CPU,\ - .property = "fill-mtrr-mask",\ - .value = "off",\ - },\ - {\ - .driver = "apic-common",\ - .property = "legacy-instance-id",\ - .value = "on",\ - }, +extern GlobalProperty pc_compat_2_8[]; +extern const size_t pc_compat_2_8_len; -#define PC_COMPAT_2_5 \ - HW_COMPAT_2_5 +extern GlobalProperty pc_compat_2_7[]; +extern const size_t pc_compat_2_7_len; + +extern GlobalProperty pc_compat_2_6[]; +extern const size_t pc_compat_2_6_len; + +extern GlobalProperty pc_compat_2_5[]; +extern const size_t pc_compat_2_5_len; + +extern GlobalProperty pc_compat_2_4[]; +extern const size_t pc_compat_2_4_len; + +extern GlobalProperty pc_compat_2_3[]; +extern const size_t pc_compat_2_3_len; + +extern GlobalProperty pc_compat_2_2[]; +extern const size_t pc_compat_2_2_len; + +extern GlobalProperty pc_compat_2_1[]; +extern const size_t pc_compat_2_1_len; + +extern GlobalProperty pc_compat_2_0[]; +extern const size_t pc_compat_2_0_len; + +extern GlobalProperty pc_compat_1_7[]; +extern const size_t pc_compat_1_7_len; + +extern GlobalProperty pc_compat_1_6[]; +extern const size_t pc_compat_1_6_len; + +extern GlobalProperty pc_compat_1_5[]; +extern const size_t pc_compat_1_5_len; + +extern GlobalProperty pc_compat_1_4[]; +extern const size_t pc_compat_1_4_len; /* Helper for setting model-id for CPU models that changed model-id * depending on QEMU versions up to QEMU 2.4. */ #define PC_CPU_MODEL_IDS(v) \ - {\ - .driver = "qemu32-" TYPE_X86_CPU,\ - .property = "model-id",\ - .value = "QEMU Virtual CPU version " v,\ - },\ - {\ - .driver = "qemu64-" TYPE_X86_CPU,\ - .property = "model-id",\ - .value = "QEMU Virtual CPU version " v,\ - },\ - {\ - .driver = "athlon-" TYPE_X86_CPU,\ - .property = "model-id",\ - .value = "QEMU Virtual CPU version " v,\ - }, - -#define PC_COMPAT_2_4 \ - HW_COMPAT_2_4 \ - PC_CPU_MODEL_IDS("2.4.0") \ - {\ - .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 \ - PC_CPU_MODEL_IDS("2.3.0") \ - {\ - .driver = TYPE_X86_CPU,\ - .property = "arat",\ - .value = "off",\ - },{\ - .driver = "qemu64" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(4),\ - },{\ - .driver = "kvm64" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(5),\ - },{\ - .driver = "pentium3" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(2),\ - },{\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(5),\ - },{\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(4),\ - },{\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(4),\ - },{\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(4),\ - },{\ - .driver = "n270" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Penryn" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Conroe" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Nehalem" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Westmere" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "SandyBridge" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "IvyBridge" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Haswell" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Haswell-noTSX" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Broadwell" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = "Broadwell-noTSX" "-" TYPE_X86_CPU,\ - .property = "min-xlevel",\ - .value = stringify(0x8000000a),\ - },{\ - .driver = TYPE_X86_CPU,\ - .property = "kvm-no-smi-migration",\ - .value = "on",\ - }, - -#define PC_COMPAT_2_2 \ - HW_COMPAT_2_2 \ - PC_CPU_MODEL_IDS("2.2.0") \ - {\ - .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 \ - HW_COMPAT_2_1 \ - PC_CPU_MODEL_IDS("2.1.0") \ - {\ - .driver = "coreduo" "-" TYPE_X86_CPU,\ - .property = "vmx",\ - .value = "on",\ - },\ - {\ - .driver = "core2duo" "-" TYPE_X86_CPU,\ - .property = "vmx",\ - .value = "on",\ - }, - -#define PC_COMPAT_2_0 \ - PC_CPU_MODEL_IDS("2.0.0") \ - {\ - .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_CPU_MODEL_IDS("1.7.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_CPU_MODEL_IDS("1.6.0") \ - {\ - .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_CPU_MODEL_IDS("1.5.0") \ - {\ - .driver = "Conroe-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Conroe-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(2),\ - },{\ - .driver = "Penryn-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Penryn-" TYPE_X86_CPU,\ - .property = "min-level",\ - .value = stringify(2),\ - },{\ - .driver = "Nehalem-" TYPE_X86_CPU,\ - .property = "model",\ - .value = stringify(2),\ - },{\ - .driver = "Nehalem-" TYPE_X86_CPU,\ - .property = "min-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_CPU_MODEL_IDS("1.4.0") \ - {\ - .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",\ - }, + { "qemu32-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ + { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ + { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ diff --git a/include/hw/i386/x86-iommu.h b/include/hw/i386/x86-iommu.h index 7c71fc7470..dcd9719a2c 100644 --- a/include/hw/i386/x86-iommu.h +++ b/include/hw/i386/x86-iommu.h @@ -22,6 +22,7 @@ #include "hw/sysbus.h" #include "hw/pci/pci.h" +#include "hw/pci/msi.h" #define TYPE_X86_IOMMU_DEVICE ("x86-iommu") #define X86_IOMMU_DEVICE(obj) \ @@ -35,6 +36,8 @@ typedef struct X86IOMMUState X86IOMMUState; typedef struct X86IOMMUClass X86IOMMUClass; +typedef struct X86IOMMUIrq X86IOMMUIrq; +typedef struct X86IOMMU_MSIMessage X86IOMMU_MSIMessage; typedef enum IommuType { TYPE_INTEL, @@ -71,13 +74,72 @@ typedef struct IEC_Notifier IEC_Notifier; struct X86IOMMUState { SysBusDevice busdev; - bool intr_supported; /* Whether vIOMMU supports IR */ + OnOffAuto intr_supported; /* Whether vIOMMU supports IR */ bool dt_supported; /* Whether vIOMMU supports DT */ bool pt_supported; /* Whether vIOMMU supports pass-through */ IommuType type; /* IOMMU type - AMD/Intel */ QLIST_HEAD(, IEC_Notifier) iec_notifiers; /* IEC notify list */ }; +bool x86_iommu_ir_supported(X86IOMMUState *s); + +/* Generic IRQ entry information when interrupt remapping is enabled */ +struct X86IOMMUIrq { + /* Used by both IOAPIC/MSI interrupt remapping */ + uint8_t trigger_mode; + uint8_t vector; + uint8_t delivery_mode; + uint32_t dest; + uint8_t dest_mode; + + /* only used by MSI interrupt remapping */ + uint8_t redir_hint; + uint8_t msi_addr_last_bits; +}; + +struct X86IOMMU_MSIMessage { + union { + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint32_t __addr_head:12; /* 0xfee */ + uint32_t dest:8; + uint32_t __reserved:8; + uint32_t redir_hint:1; + uint32_t dest_mode:1; + uint32_t __not_used:2; +#else + uint32_t __not_used:2; + uint32_t dest_mode:1; + uint32_t redir_hint:1; + uint32_t __reserved:8; + uint32_t dest:8; + uint32_t __addr_head:12; /* 0xfee */ +#endif + uint32_t __addr_hi; + } QEMU_PACKED; + uint64_t msi_addr; + }; + union { + struct { +#ifdef HOST_WORDS_BIGENDIAN + uint16_t trigger_mode:1; + uint16_t level:1; + uint16_t __resved:3; + uint16_t delivery_mode:3; + uint16_t vector:8; +#else + uint16_t vector:8; + uint16_t delivery_mode:3; + uint16_t __resved:3; + uint16_t level:1; + uint16_t trigger_mode:1; +#endif + uint16_t __resved1; + } QEMU_PACKED; + uint32_t msi_data; + }; +}; + /** * x86_iommu_get_default - get default IOMMU device * @return: pointer to default IOMMU device @@ -110,4 +172,10 @@ void x86_iommu_iec_register_notifier(X86IOMMUState *iommu, void x86_iommu_iec_notify_all(X86IOMMUState *iommu, bool global, uint32_t index, uint32_t mask); +/** + * x86_iommu_irq_to_msi_message - Populate one MSIMessage from X86IOMMUIrq + * @X86IOMMUIrq: The IRQ information + * @out: Output MSI message + */ +void x86_iommu_irq_to_msi_message(X86IOMMUIrq *irq, MSIMessage *out); #endif diff --git a/include/hw/ide.h b/include/hw/ide.h index 3ae087c572..28d8a06439 100644 --- a/include/hw/ide.h +++ b/include/hw/ide.h @@ -18,7 +18,7 @@ PCIDevice *pci_piix3_xen_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix3_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); PCIDevice *pci_piix4_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); int pci_piix3_xen_ide_unplug(DeviceState *dev, bool aux); -void vt82c686b_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); +void via_ide_init(PCIBus *bus, DriveInfo **hd_table, int devfn); /* ide-mmio.c */ void mmio_ide_init_drives(DeviceState *dev, DriveInfo *hd0, DriveInfo *hd1); diff --git a/include/hw/ide/ahci.h b/include/hw/ide/ahci.h index b7bb2b02d6..b44e3000cf 100644 --- a/include/hw/ide/ahci.h +++ b/include/hw/ide/ahci.h @@ -73,13 +73,13 @@ typedef struct SysbusAHCIState { #define ALLWINNER_AHCI_MMIO_OFF 0x80 #define ALLWINNER_AHCI_MMIO_SIZE 0x80 -struct AllwinnerAHCIState { +typedef struct AllwinnerAHCIState { /*< private >*/ SysbusAHCIState parent_obj; /*< public >*/ MemoryRegion mmio; uint32_t regs[ALLWINNER_AHCI_MMIO_SIZE/4]; -}; +} AllwinnerAHCIState; #endif /* HW_IDE_AHCI_H */ diff --git a/include/hw/ide/internal.h b/include/hw/ide/internal.h index 9a73bf8b77..97139827ac 100644 --- a/include/hw/ide/internal.h +++ b/include/hw/ide/internal.h @@ -342,11 +342,10 @@ enum ide_dma_cmd { extern const char *IDE_DMA_CMD_lookup[IDE_DMA__COUNT]; #define ide_cmd_is_read(s) \ - ((s)->dma_cmd == IDE_DMA_READ) + ((s)->dma_cmd == IDE_DMA_READ) typedef struct IDEBufferedRequest { QLIST_ENTRY(IDEBufferedRequest) list; - struct iovec iov; QEMUIOVector qiov; QEMUIOVector *original_qiov; BlockCompletionFunc *original_cb; @@ -405,7 +404,6 @@ struct IDEState { int atapi_dma; /* true if dma is requested for the packet cmd */ BlockAcctCookie acct; BlockAIOCB *pio_aiocb; - struct iovec iov; QEMUIOVector qiov; QLIST_HEAD(, IDEBufferedRequest) buffered_requests; /* ATA DMA state */ @@ -457,7 +455,6 @@ struct IDEDMAOps { struct IDEDMA { const struct IDEDMAOps *ops; - struct iovec iov; QEMUIOVector qiov; BlockAIOCB *aiocb; }; diff --git a/include/hw/ide/pci.h b/include/hw/ide/pci.h index dbc6a0383d..a9f2c33e68 100644 --- a/include/hw/ide/pci.h +++ b/include/hw/ide/pci.h @@ -37,13 +37,6 @@ typedef struct BMDMAState { struct PCIIDEState *pci_dev; } BMDMAState; -typedef struct CMD646BAR { - MemoryRegion cmd; - MemoryRegion data; - IDEBus *bus; - struct PCIIDEState *pci_dev; -} CMD646BAR; - #define TYPE_PCI_IDE "pci-ide" #define PCI_IDE(obj) OBJECT_CHECK(PCIIDEState, (obj), TYPE_PCI_IDE) @@ -56,21 +49,22 @@ typedef struct PCIIDEState { BMDMAState bmdma[2]; uint32_t secondary; /* used only for cmd646 */ MemoryRegion bmdma_bar; - CMD646BAR cmd646_bar[2]; /* used only for cmd646 */ + MemoryRegion cmd_bar[2]; + MemoryRegion data_bar[2]; } PCIIDEState; - static inline IDEState *bmdma_active_if(BMDMAState *bmdma) { assert(bmdma->bus->retry_unit != (uint8_t)-1); return bmdma->bus->ifs + bmdma->bus->retry_unit; } - void bmdma_init(IDEBus *bus, BMDMAState *bm, PCIIDEState *d); void bmdma_cmd_writeb(BMDMAState *bm, uint32_t val); extern MemoryRegionOps bmdma_addr_ioport_ops; void pci_ide_create_devs(PCIDevice *dev, DriveInfo **hd_table); extern const VMStateDescription vmstate_ide_pci; +extern const MemoryRegionOps pci_ide_cmd_le_ops; +extern const MemoryRegionOps pci_ide_data_le_ops; #endif diff --git a/include/hw/input/ps2.h b/include/hw/input/ps2.h index 213aa16aa3..b60455d4f6 100644 --- a/include/hw/input/ps2.h +++ b/include/hw/input/ps2.h @@ -31,6 +31,8 @@ #define PS2_MOUSE_BUTTON_SIDE 0x08 #define PS2_MOUSE_BUTTON_EXTRA 0x10 +typedef struct PS2State PS2State; + /* ps2.c */ void *ps2_kbd_init(void (*update_irq)(void *, int), void *update_arg); void *ps2_mouse_init(void (*update_irq)(void *, int), void *update_arg); diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h index 42bb535fd4..ed703a1720 100644 --- a/include/hw/intc/arm_gic.h +++ b/include/hw/intc/arm_gic.h @@ -18,6 +18,49 @@ * with this program; if not, see . */ +/* + * QEMU interface: + * + QOM property "num-cpu": number of CPUs to support + * + QOM property "num-irq": number of IRQs (including both SPIs and PPIs) + * + QOM property "revision": GIC version (1 or 2), or 0 for the 11MPCore GIC + * + QOM property "has-security-extensions": set true if the GIC should + * implement the security extensions + * + QOM property "has-virtualization-extensions": set true if the GIC should + * implement the virtualization extensions + * + unnamed GPIO inputs: (where P is number of SPIs, i.e. num-irq - 32) + * [0..P-1] SPIs + * [P..P+31] PPIs for CPU 0 + * [P+32..P+63] PPIs for CPU 1 + * ... + * + sysbus IRQs: (in order; number will vary depending on number of cores) + * - IRQ for CPU 0 + * - IRQ for CPU 1 + * ... + * - FIQ for CPU 0 + * - FIQ for CPU 1 + * ... + * - VIRQ for CPU 0 (exists even if virt extensions not present) + * - VIRQ for CPU 1 (exists even if virt extensions not present) + * ... + * - VFIQ for CPU 0 (exists even if virt extensions not present) + * - VFIQ for CPU 1 (exists even if virt extensions not present) + * ... + * - maintenance IRQ for CPU i/f 0 (only if virt extensions present) + * - maintenance IRQ for CPU i/f 1 (only if virt extensions present) + * + sysbus MMIO regions: (in order; numbers will vary depending on + * whether virtualization extensions are present and on number of cores) + * - distributor registers (GICD*) + * - CPU interface for the accessing core (GICC*) + * - virtual interface control registers (GICH*) (only if virt extns present) + * - virtual CPU interface for the accessing core (GICV*) (only if virt) + * - CPU 0 CPU interface registers + * - CPU 1 CPU interface registers + * ... + * - CPU 0 virtual interface control registers (only if virt extns present) + * - CPU 1 virtual interface control registers (only if virt extns present) + * ... + */ + #ifndef HW_ARM_GIC_H #define HW_ARM_GIC_H diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index af3ca18e2f..b5585fec45 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -30,6 +30,8 @@ #define GIC_NR_SGIS 16 /* Maximum number of possible CPU interfaces, determined by GIC architecture */ #define GIC_NCPU 8 +/* Maximum number of possible CPU interfaces with their respective vCPU */ +#define GIC_NCPU_VCPU (GIC_NCPU * 2) #define MAX_NR_GROUP_PRIO 128 #define GIC_NR_APRS (MAX_NR_GROUP_PRIO / 32) @@ -37,6 +39,17 @@ #define GIC_MIN_BPR 0 #define GIC_MIN_ABPR (GIC_MIN_BPR + 1) +/* Architectural maximum number of list registers in the virtual interface */ +#define GIC_MAX_LR 64 + +/* Only 32 priority levels and 32 preemption levels in the vCPU interfaces */ +#define GIC_VIRT_MAX_GROUP_PRIO_BITS 5 +#define GIC_VIRT_MAX_NR_GROUP_PRIO (1 << GIC_VIRT_MAX_GROUP_PRIO_BITS) +#define GIC_VIRT_NR_APRS (GIC_VIRT_MAX_NR_GROUP_PRIO / 32) + +#define GIC_VIRT_MIN_BPR 2 +#define GIC_VIRT_MIN_ABPR (GIC_VIRT_MIN_BPR + 1) + typedef struct gic_irq_state { /* The enable bits are only banked for per-cpu interrupts. */ uint8_t enabled; @@ -57,6 +70,8 @@ typedef struct GICState { qemu_irq parent_fiq[GIC_NCPU]; qemu_irq parent_virq[GIC_NCPU]; qemu_irq parent_vfiq[GIC_NCPU]; + qemu_irq maintenance_irq[GIC_NCPU]; + /* GICD_CTLR; for a GIC with the security extensions the NS banked version * of this register is just an alias of bit 1 of the S banked version. */ @@ -64,7 +79,7 @@ typedef struct GICState { /* GICC_CTLR; again, the NS banked version is just aliases of bits of * the S banked register, so our state only needs to store the S version. */ - uint32_t cpu_ctlr[GIC_NCPU]; + uint32_t cpu_ctlr[GIC_NCPU_VCPU]; gic_irq_state irq_state[GIC_MAXIRQ]; uint8_t irq_target[GIC_MAXIRQ]; @@ -78,9 +93,9 @@ typedef struct GICState { */ uint8_t sgi_pending[GIC_NR_SGIS][GIC_NCPU]; - uint16_t priority_mask[GIC_NCPU]; - uint16_t running_priority[GIC_NCPU]; - uint16_t current_pending[GIC_NCPU]; + uint16_t priority_mask[GIC_NCPU_VCPU]; + uint16_t running_priority[GIC_NCPU_VCPU]; + uint16_t current_pending[GIC_NCPU_VCPU]; /* If we present the GICv2 without security extensions to a guest, * the guest can configure the GICC_CTLR to configure group 1 binary point @@ -88,8 +103,8 @@ typedef struct GICState { * For a GIC with Security Extensions we use use bpr for the * secure copy and abpr as storage for the non-secure copy of the register. */ - uint8_t bpr[GIC_NCPU]; - uint8_t abpr[GIC_NCPU]; + uint8_t bpr[GIC_NCPU_VCPU]; + uint8_t abpr[GIC_NCPU_VCPU]; /* The APR is implementation defined, so we choose a layout identical to * the KVM ABI layout for QEMU's implementation of the gic: @@ -100,6 +115,15 @@ typedef struct GICState { uint32_t apr[GIC_NR_APRS][GIC_NCPU]; uint32_t nsapr[GIC_NR_APRS][GIC_NCPU]; + /* Virtual interface control registers */ + uint32_t h_hcr[GIC_NCPU]; + uint32_t h_misr[GIC_NCPU]; + uint32_t h_lr[GIC_MAX_LR][GIC_NCPU]; + uint32_t h_apr[GIC_NCPU]; + + /* Number of LRs implemented in this GIC instance */ + uint32_t num_lrs; + uint32_t num_cpu; MemoryRegion iomem; /* Distributor */ @@ -108,9 +132,13 @@ typedef struct GICState { */ struct GICState *backref[GIC_NCPU]; MemoryRegion cpuiomem[GIC_NCPU + 1]; /* CPU interfaces */ + MemoryRegion vifaceiomem[GIC_NCPU + 1]; /* Virtual interfaces */ + MemoryRegion vcpuiomem; /* vCPU interface */ + uint32_t num_irq; uint32_t revision; bool security_extn; + bool virt_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; @@ -134,6 +162,7 @@ typedef struct ARMGICCommonClass { } ARMGICCommonClass; void gic_init_irqs_and_mmio(GICState *s, qemu_irq_handler handler, - const MemoryRegionOps *ops); + const MemoryRegionOps *ops, + const MemoryRegionOps *virt_ops); #endif diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index b798486ecf..31ec9a1ae4 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -62,7 +62,7 @@ * avoids bugs where we forget to subtract GIC_INTERNAL from an * interrupt number. */ -#define GICV3_BMP_SIZE (DIV_ROUND_UP(GICV3_MAXIRQ, 32)) +#define GICV3_BMP_SIZE DIV_ROUND_UP(GICV3_MAXIRQ, 32) #define GIC_DECLARE_BITMAP(name) \ uint32_t name[GICV3_BMP_SIZE] diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h index 8bc29112e3..a472c9b8f0 100644 --- a/include/hw/intc/armv7m_nvic.h +++ b/include/hw/intc/armv7m_nvic.h @@ -57,6 +57,7 @@ typedef struct NVICState { VecInfo sec_vectors[NVIC_INTERNAL_VECTORS]; /* The PRIGROUP field in AIRCR is banked */ uint32_t prigroup[M_REG_NUM_BANKS]; + uint8_t num_prio_bits; /* v8M NVIC_ITNS state (stored as a bool per bit) */ bool itns[NVIC_MAX_VECTORS]; diff --git a/include/hw/intc/bcm2836_control.h b/include/hw/intc/bcm2836_control.h index 613f3c4186..de061b8929 100644 --- a/include/hw/intc/bcm2836_control.h +++ b/include/hw/intc/bcm2836_control.h @@ -5,6 +5,9 @@ * Rasperry Pi 2 emulation and refactoring Copyright (c) 2015, Microsoft * Written by Andrew Baumann * + * ARM Local Timer IRQ Copyright (c) 2019. Zoltán Baldaszti + * Added basic IRQ_TIMER interrupt support + * * This code is licensed under the GNU GPLv2 and later. */ @@ -12,6 +15,7 @@ #define BCM2836_CONTROL_H #include "hw/sysbus.h" +#include "qemu/timer.h" /* 4 mailboxes per core, for 16 total */ #define BCM2836_NCORES 4 @@ -39,6 +43,11 @@ typedef struct BCM2836ControlState { bool gpu_irq, gpu_fiq; uint8_t timerirqs[BCM2836_NCORES]; + /* local timer */ + QEMUTimer timer; + uint32_t local_timer_control; + uint8_t route_localtimer; + /* interrupt source registers, post-routing (also input-derived; visible) */ uint32_t irqsrc[BCM2836_NCORES]; uint32_t fiqsrc[BCM2836_NCORES]; diff --git a/include/hw/intc/intc.h b/include/hw/intc/intc.h index 27d9828943..fb3e8e621f 100644 --- a/include/hw/intc/intc.h +++ b/include/hw/intc/intc.h @@ -15,9 +15,7 @@ INTERFACE_CHECK(InterruptStatsProvider, (obj), \ TYPE_INTERRUPT_STATS_PROVIDER) -typedef struct InterruptStatsProvider { - Object parent; -} InterruptStatsProvider; +typedef struct InterruptStatsProvider InterruptStatsProvider; typedef struct InterruptStatsProviderClass { InterfaceClass parent; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 0affe5a4d8..99661d2bf0 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -114,9 +114,7 @@ uint32_t ipmi_next_uuid(void); #define IPMI_INTERFACE_GET_CLASS(class) \ OBJECT_GET_CLASS(IPMIInterfaceClass, (class), TYPE_IPMI_INTERFACE) -typedef struct IPMIInterface { - Object parent; -} IPMIInterface; +typedef struct IPMIInterface IPMIInterface; typedef struct IPMIInterfaceClass { InterfaceClass parent; diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index b9dbab24b4..e62ac91c19 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -43,10 +43,6 @@ static inline uint16_t applesmc_port(void) #define ISADMA(obj) \ INTERFACE_CHECK(IsaDma, (obj), TYPE_ISADMA) -struct IsaDma { - Object parent; -}; - typedef enum { ISADMA_TRANSFER_VERIFY, ISADMA_TRANSFER_READ, diff --git a/include/hw/loader.h b/include/hw/loader.h index e98b84b8f9..3e1b3a4566 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -10,8 +10,23 @@ * Returns the size of the image file on success, -1 otherwise. * On error, errno is also set as appropriate. */ -int get_image_size(const char *filename); -int load_image(const char *filename, uint8_t *addr); /* deprecated */ +int64_t get_image_size(const char *filename); +/** + * load_image_size: load an image file into specified buffer + * @filename: Path to the image file + * @addr: Buffer to load image into + * @size: Size of buffer in bytes + * + * Load an image file from disk into the specified buffer. + * If the image is larger than the specified buffer, only + * @size bytes are read (this is not considered an error). + * + * Prefer to use the GLib function g_file_get_contents() rather + * than a "get_image_size()/g_malloc()/load_image_size()" sequence. + * + * Returns the number of bytes read, or -1 on error. On error, + * errno is also set as appropriate. + */ ssize_t load_image_size(const char *filename, void *addr, size_t size); /**load_image_targphys_as: @@ -28,6 +43,18 @@ ssize_t load_image_size(const char *filename, void *addr, size_t size); int load_image_targphys_as(const char *filename, hwaddr addr, uint64_t max_sz, AddressSpace *as); +/**load_targphys_hex_as: + * @filename: Path to the .hex file + * @entry: Store the entry point given by the .hex file + * @as: The AddressSpace to load the .hex file to. The value of + * address_space_memory is used if nothing is supplied here. + * + * Load a fixed .hex file into memory. + * + * Returns the size of the loaded .hex file on success, -1 otherwise. + */ +int load_targphys_hex_as(const char *filename, hwaddr *entry, AddressSpace *as); + /** load_image_targphys: * Same as load_image_targphys_as(), but doesn't allow the caller to specify * an AddressSpace. @@ -66,6 +93,8 @@ const char *load_elf_strerror(int error); /** load_elf_ram_sym: * @filename: Path of ELF file + * @elf_note_fn: optional function to parse ELF Note type + * passed via @translate_opaque * @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. @@ -98,6 +127,7 @@ typedef void (*symbol_fn_t)(const char *st_name, int st_info, uint64_t st_value, uint64_t st_size); int load_elf_ram_sym(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), uint64_t (*translate_fn)(void *, uint64_t), void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr, int big_endian, @@ -109,6 +139,7 @@ int load_elf_ram_sym(const char *filename, * symbol callback function */ int load_elf_ram(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), 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, @@ -119,6 +150,7 @@ int load_elf_ram(const char *filename, * Same as load_elf_ram(), but always loads the elf as ROM */ int load_elf_as(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), 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, @@ -128,7 +160,9 @@ int load_elf_as(const char *filename, * Same as load_elf_as(), but doesn't allow the caller to specify an * AddressSpace. */ -int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t), +int load_elf(const char *filename, + uint64_t (*elf_note_fn)(void *, void *, bool), + 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 data_swab); @@ -148,10 +182,15 @@ 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); +#define LOAD_UIMAGE_LOADADDR_INVALID (-1) + /** load_uimage_as: * @filename: Path of uimage file * @ep: Populated with program entry point. Ignored if NULL. - * @loadaddr: Populated with the load address. Ignored if NULL. + * @loadaddr: load address if none specified in the image or when loading a + * ramdisk. Populated with the load address. Ignored if NULL or + * LOAD_UIMAGE_LOADADDR_INVALID (images which do not specify a load + * address will not be loadable). * @is_linux: Is set to true if the image loaded is Linux. Ignored if NULL. * @translate_fn: optional function to translate load addresses * @translate_opaque: opaque data passed to @translate_fn @@ -225,6 +264,25 @@ 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); + +/** + * rom_transaction_begin: + * + * Call this before of a series of rom_add_*() calls. Call + * rom_transaction_end() afterwards to commit or abort. These functions are + * useful for undoing a series of rom_add_*() calls if image file loading fails + * partway through. + */ +void rom_transaction_begin(void); + +/** + * rom_transaction_end: + * @commit: true to commit added roms, false to drop added roms + * + * Call this after a series of rom_add_*() calls. See rom_transaction_begin(). + */ +void rom_transaction_end(bool commit); + int rom_copy(uint8_t *dest, hwaddr addr, size_t size); void *rom_ptr(hwaddr addr, size_t size); void hmp_info_roms(Monitor *mon, const QDict *qdict); diff --git a/include/hw/mem/memory-device.h b/include/hw/mem/memory-device.h index 2853b084b5..0293a96abb 100644 --- a/include/hw/mem/memory-device.h +++ b/include/hw/mem/memory-device.h @@ -25,27 +25,83 @@ #define MEMORY_DEVICE(obj) \ INTERFACE_CHECK(MemoryDeviceState, (obj), TYPE_MEMORY_DEVICE) -typedef struct MemoryDeviceState { - Object parent_obj; -} MemoryDeviceState; +typedef struct MemoryDeviceState MemoryDeviceState; +/** + * MemoryDeviceClass: + * + * All memory devices need to implement TYPE_MEMORY_DEVICE as an interface. + * + * A memory device is a device that owns a memory region which is + * mapped into guest physical address space at a certain address. The + * address in guest physical memory can either be specified explicitly + * or get assigned automatically. + * + * Conceptually, memory devices only span one memory region. If multiple + * successive memory regions are used, a covering memory region has to + * be provided. Scattered memory regions are not supported for single + * devices. + */ typedef struct MemoryDeviceClass { + /* private */ InterfaceClass parent_class; + /* + * Return the address of the memory device in guest physical memory. + * + * Called when (un)plugging a memory device or when iterating over + * all memory devices mapped into guest physical address space. + * + * If "0" is returned, no address has been specified by the user and + * no address has been assigned to this memory device yet. + */ uint64_t (*get_addr)(const MemoryDeviceState *md); - uint64_t (*get_plugged_size)(const MemoryDeviceState *md); - uint64_t (*get_region_size)(const MemoryDeviceState *md); + + /* + * Set the address of the memory device in guest physical memory. + * + * Called when plugging the memory device to configure the determined + * address in guest physical memory. + */ + void (*set_addr)(MemoryDeviceState *md, uint64_t addr, Error **errp); + + /* + * Return the amount of memory provided by the memory device currently + * usable ("plugged") by the VM. + * + * Called when calculating the total amount of ram available to the + * VM (e.g. to report memory stats to the user). + * + * This is helpful for devices that dynamically manage the amount of + * memory accessible by the guest via the reserved memory region. For + * most devices, this corresponds to the size of the memory region. + */ + uint64_t (*get_plugged_size)(const MemoryDeviceState *md, Error **errp); + + /* + * Return the memory region of the memory device. + * + * Called when (un)plugging the memory device, to (un)map the + * memory region in guest physical memory, but also to detect the + * required alignment during address assignment or when the size of the + * memory region is required. + */ + MemoryRegion *(*get_memory_region)(MemoryDeviceState *md, Error **errp); + + /* + * Translate the memory device into #MemoryDeviceInfo. + */ void (*fill_device_info)(const MemoryDeviceState *md, MemoryDeviceInfo *info); } MemoryDeviceClass; MemoryDeviceInfoList *qmp_memory_device_list(void); uint64_t get_plugged_memory_size(void); -uint64_t memory_device_get_free_addr(MachineState *ms, const uint64_t *hint, - uint64_t align, uint64_t size, - Error **errp); -void memory_device_plug_region(MachineState *ms, MemoryRegion *mr, - uint64_t addr); -void memory_device_unplug_region(MachineState *ms, MemoryRegion *mr); +void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms, + const uint64_t *legacy_align, Error **errp); +void memory_device_plug(MemoryDeviceState *md, MachineState *ms); +void memory_device_unplug(MemoryDeviceState *md, MachineState *ms); +uint64_t memory_device_get_region_size(const MemoryDeviceState *md, + Error **errp); #endif diff --git a/include/hw/mem/nvdimm.h b/include/hw/mem/nvdimm.h index c5c9b3c7f8..523a9b3d4a 100644 --- a/include/hw/mem/nvdimm.h +++ b/include/hw/mem/nvdimm.h @@ -123,7 +123,7 @@ struct NvdimmFitBuffer { }; typedef struct NvdimmFitBuffer NvdimmFitBuffer; -struct AcpiNVDIMMState { +struct NVDIMMState { /* detect if NVDIMM support is enabled. */ bool is_enabled; @@ -141,13 +141,13 @@ struct AcpiNVDIMMState { int32_t persistence; char *persistence_string; }; -typedef struct AcpiNVDIMMState AcpiNVDIMMState; +typedef struct NVDIMMState NVDIMMState; -void nvdimm_init_acpi_state(AcpiNVDIMMState *state, MemoryRegion *io, +void nvdimm_init_acpi_state(NVDIMMState *state, MemoryRegion *io, FWCfgState *fw_cfg, Object *owner); void nvdimm_build_acpi(GArray *table_offsets, GArray *table_data, - BIOSLinker *linker, AcpiNVDIMMState *state, + BIOSLinker *linker, NVDIMMState *state, uint32_t ram_slots); -void nvdimm_plug(AcpiNVDIMMState *state); +void nvdimm_plug(NVDIMMState *state); void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev); #endif diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index 26ebb7d5e9..01436b9f50 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -61,9 +61,6 @@ typedef struct PCDIMMDevice { * PCDIMMDeviceClass: * @realize: called after common dimm is realized so that the dimm based * devices get the chance to do specified operations. - * @get_memory_region: returns #MemoryRegion associated with @dimm which - * is directly mapped into the physical address space of guest. Will not - * fail after the device was realized. * @get_vmstate_memory_region: returns #MemoryRegion which indicates the * memory of @dimm should be kept during live migration. Will not fail * after the device was realized. @@ -74,12 +71,12 @@ typedef struct PCDIMMDeviceClass { /* public */ void (*realize)(PCDIMMDevice *dimm, Error **errp); - MemoryRegion *(*get_memory_region)(PCDIMMDevice *dimm, Error **errp); MemoryRegion *(*get_vmstate_memory_region)(PCDIMMDevice *dimm, Error **errp); } PCDIMMDeviceClass; -void pc_dimm_plug(DeviceState *dev, MachineState *machine, uint64_t align, - Error **errp); -void pc_dimm_unplug(DeviceState *dev, MachineState *machine); +void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, + const uint64_t *legacy_align, Error **errp); +void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp); +void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine); #endif diff --git a/include/hw/misc/armsse-cpuid.h b/include/hw/misc/armsse-cpuid.h new file mode 100644 index 0000000000..0ef33fcaba --- /dev/null +++ b/include/hw/misc/armsse-cpuid.h @@ -0,0 +1,41 @@ +/* + * ARM SSE-200 CPU_IDENTITY register block + * + * Copyright (c) 2019 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "CPU_IDENTITY" register block which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * + * QEMU interface: + * + QOM property "CPUID": the value to use for the CPUID register + * + sysbus MMIO region 0: the system information register bank + */ + +#ifndef HW_MISC_ARMSSE_CPUID_H +#define HW_MISC_ARMSSE_CPUID_H + +#include "hw/sysbus.h" + +#define TYPE_ARMSSE_CPUID "armsse-cpuid" +#define ARMSSE_CPUID(obj) OBJECT_CHECK(ARMSSECPUID, (obj), TYPE_ARMSSE_CPUID) + +typedef struct ARMSSECPUID { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + /* Properties */ + uint32_t cpuid; +} ARMSSECPUID; + +#endif diff --git a/include/hw/misc/armsse-mhu.h b/include/hw/misc/armsse-mhu.h new file mode 100644 index 0000000000..e57eafc252 --- /dev/null +++ b/include/hw/misc/armsse-mhu.h @@ -0,0 +1,44 @@ +/* + * ARM SSE-200 Message Handling Unit (MHU) + * + * Copyright (c) 2019 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the Message Handling Unit (MHU) which is part of the + * Arm SSE-200 and documented in + * http://infocenter.arm.com/help/topic/com.arm.doc.101104_0100_00_en/corelink_sse200_subsystem_for_embedded_technical_reference_manual_101104_0100_00_en.pdf + * + * QEMU interface: + * + sysbus MMIO region 0: the system information register bank + * + sysbus IRQ 0: interrupt for CPU 0 + * + sysbus IRQ 1: interrupt for CPU 1 + */ + +#ifndef HW_MISC_SSE_MHU_H +#define HW_MISC_SSE_MHU_H + +#include "hw/sysbus.h" + +#define TYPE_ARMSSE_MHU "armsse-mhu" +#define ARMSSE_MHU(obj) OBJECT_CHECK(ARMSSEMHU, (obj), TYPE_ARMSSE_MHU) + +typedef struct ARMSSEMHU { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq cpu0irq; + qemu_irq cpu1irq; + + uint32_t cpu0intr; + uint32_t cpu1intr; +} ARMSSEMHU; + +#endif diff --git a/include/hw/misc/aspeed_sdmc.h b/include/hw/misc/aspeed_sdmc.h index 551c8afdf4..b3c926acae 100644 --- a/include/hw/misc/aspeed_sdmc.h +++ b/include/hw/misc/aspeed_sdmc.h @@ -14,7 +14,7 @@ #define TYPE_ASPEED_SDMC "aspeed.sdmc" #define ASPEED_SDMC(obj) OBJECT_CHECK(AspeedSDMCState, (obj), TYPE_ASPEED_SDMC) -#define ASPEED_SDMC_NR_REGS (0x8 >> 2) +#define ASPEED_SDMC_NR_REGS (0x174 >> 2) typedef struct AspeedSDMCState { /*< private >*/ @@ -27,6 +27,8 @@ typedef struct AspeedSDMCState { uint32_t silicon_rev; uint32_t ram_bits; uint64_t ram_size; + uint64_t max_ram_size; + uint32_t fixed_conf; } AspeedSDMCState; diff --git a/include/hw/misc/imx6ul_ccm.h b/include/hw/misc/imx6ul_ccm.h new file mode 100644 index 0000000000..377ddca244 --- /dev/null +++ b/include/hw/misc/imx6ul_ccm.h @@ -0,0 +1,226 @@ +/* + * IMX6UL Clock Control Module + * + * Copyright (C) 2018 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 IMX6UL_CCM_H +#define IMX6UL_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_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 CCM_ANALOG_MISC0 +#define CCM_ANALOG_MISC0_SET 85 +#define PMU_MISC0_SET CCM_ANALOG_MISC0_SET +#define CCM_ANALOG_MISC0_CLR 86 +#define PMU_MISC0_CLR CCM_ANALOG_MISC0_CLR +#define CCM_ANALOG_MISC0_TOG 87 +#define PMU_MISC0_TOG CCM_ANALOG_MISC0_TOG + +#define CCM_ANALOG_MISC1 88 +#define PMU_MISC1 CCM_ANALOG_MISC1 +#define CCM_ANALOG_MISC1_SET 89 +#define PMU_MISC1_SET CCM_ANALOG_MISC1_SET +#define CCM_ANALOG_MISC1_CLR 90 +#define PMU_MISC1_CLR CCM_ANALOG_MISC1_CLR +#define CCM_ANALOG_MISC1_TOG 91 +#define PMU_MISC1_TOG CCM_ANALOG_MISC1_TOG + +#define CCM_ANALOG_MISC2 92 +#define PMU_MISC2 CCM_ANALOG_MISC2 +#define CCM_ANALOG_MISC2_SET 93 +#define PMU_MISC2_SET CCM_ANALOG_MISC2_SET +#define CCM_ANALOG_MISC2_CLR 94 +#define PMU_MISC2_CLR CCM_ANALOG_MISC2_CLR +#define CCM_ANALOG_MISC2_TOG 95 +#define PMU_MISC2_TOG CCM_ANALOG_MISC2_TOG + +#define TEMPMON_TEMPSENSE0 96 +#define TEMPMON_TEMPSENSE0_SET 97 +#define TEMPMON_TEMPSENSE0_CLR 98 +#define TEMPMON_TEMPSENSE0_TOG 99 +#define TEMPMON_TEMPSENSE1 100 +#define TEMPMON_TEMPSENSE1_SET 101 +#define TEMPMON_TEMPSENSE1_CLR 102 +#define TEMPMON_TEMPSENSE1_TOG 103 +#define TEMPMON_TEMPSENSE2 164 +#define TEMPMON_TEMPSENSE2_SET 165 +#define TEMPMON_TEMPSENSE2_CLR 166 +#define TEMPMON_TEMPSENSE2_TOG 167 + +#define PMU_LOWPWR_CTRL 155 +#define PMU_LOWPWR_CTRL_SET 156 +#define PMU_LOWPWR_CTRL_CLR 157 +#define PMU_LOWPWR_CTRL_TOG 158 + +#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 4096 + +/* CCM_CBCMR */ +#define R_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT (18) +#define R_CBCMR_PRE_PERIPH_CLK_SEL_LENGTH (2) +#define R_CBCMR_PERIPH_CLK2_SEL_SHIFT (12) +#define R_CBCMR_PERIPH_CLK2_SEL_LENGTH (2) + +/* CCM_CBCDR */ +#define R_CBCDR_AHB_PODF_SHIFT (10) +#define R_CBCDR_AHB_PODF_LENGTH (3) +#define R_CBCDR_IPG_PODF_SHIFT (8) +#define R_CBCDR_IPG_PODF_LENGTH (2) +#define R_CBCDR_PERIPH_CLK_SEL_SHIFT (25) +#define R_CBCDR_PERIPH_CLK_SEL_LENGTH (1) +#define R_CBCDR_PERIPH_CLK2_PODF_SHIFT (27) +#define R_CBCDR_PERIPH_CLK2_PODF_LENGTH (3) + +/* CCM_CSCMR1 */ +#define R_CSCMR1_PERCLK_PODF_SHIFT (0) +#define R_CSCMR1_PERCLK_PODF_LENGTH (6) +#define R_CSCMR1_PERCLK_CLK_SEL_SHIFT (6) +#define R_CSCMR1_PERCLK_CLK_SEL_LENGTH (1) + +/* CCM_ANALOG_PFD_528 */ +#define R_ANALOG_PFD_528_PFD0_FRAC_SHIFT (0) +#define R_ANALOG_PFD_528_PFD0_FRAC_LENGTH (6) +#define R_ANALOG_PFD_528_PFD2_FRAC_SHIFT (16) +#define R_ANALOG_PFD_528_PFD2_FRAC_LENGTH (6) + +/* CCM_ANALOG_PLL_SYS */ +#define R_ANALOG_PLL_SYS_DIV_SELECT_SHIFT (0) +#define R_ANALOG_PLL_SYS_DIV_SELECT_LENGTH (1) + +#define CCM_ANALOG_PLL_LOCK (1 << 31); + +#define TYPE_IMX6UL_CCM "imx6ul.ccm" +#define IMX6UL_CCM(obj) OBJECT_CHECK(IMX6ULCCMState, (obj), TYPE_IMX6UL_CCM) + +typedef struct IMX6ULCCMState { + /* */ + IMXCCMState parent_obj; + + /* */ + MemoryRegion container; + MemoryRegion ioccm; + MemoryRegion ioanalog; + + uint32_t ccm[CCM_MAX]; + uint32_t analog[CCM_ANALOG_MAX]; + +} IMX6ULCCMState; + +#endif /* IMX6UL_CCM_H */ diff --git a/include/hw/misc/iotkit-secctl.h b/include/hw/misc/iotkit-secctl.h index 082c14c925..bcb0437be5 100644 --- a/include/hw/misc/iotkit-secctl.h +++ b/include/hw/misc/iotkit-secctl.h @@ -19,6 +19,7 @@ * + named GPIO output "sec_resp_cfg" indicating whether blocked accesses * should RAZ/WI or bus error * + named GPIO output "nsc_cfg" whose value tracks the NSCCFG register value + * + named GPIO output "msc_irq" for the combined IRQ line from the MSCs * Controlling the 2 APB PPCs in the IoTKit: * + named GPIO outputs apb_ppc0_nonsec[0..2] and apb_ppc1_nonsec * + named GPIO outputs apb_ppc0_ap[0..2] and apb_ppc1_ap @@ -39,11 +40,16 @@ * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_enable * + named GPIO outputs ahb_ppcexp{0,1,2,3}_irq_clear * + named GPIO inputs ahb_ppcexp{0,1,2,3}_irq_status - * Controlling the MPC in the IoTKit: - * + named GPIO input mpc_status + * Controlling the (up to) 4 MPCs in the IoTKit/SSE: + * + named GPIO inputs mpc_status[0..3] * Controlling each of the 16 expansion MPCs which a system using the IoTKit * might provide: * + named GPIO inputs mpcexp_status[0..15] + * Controlling each of the 16 expansion MSCs which a system using the IoTKit + * might provide: + * + named GPIO inputs mscexp_status[0..15] + * + named GPIO outputs mscexp_clear[0..15] + * + named GPIO outputs mscexp_ns[0..15] */ #ifndef IOTKIT_SECCTL_H @@ -61,7 +67,8 @@ #define IOTS_NUM_APB_EXP_PPC 4 #define IOTS_NUM_AHB_EXP_PPC 4 #define IOTS_NUM_EXP_MPC 16 -#define IOTS_NUM_MPC 1 +#define IOTS_NUM_MPC 4 +#define IOTS_NUM_EXP_MSC 16 typedef struct IoTKitSecCtl IoTKitSecCtl; @@ -103,6 +110,13 @@ struct IoTKitSecCtl { uint32_t brginten; uint32_t mpcintstatus; + uint32_t secmscintstat; + uint32_t secmscinten; + uint32_t nsmscexp; + qemu_irq mscexp_clear[IOTS_NUM_EXP_MSC]; + qemu_irq mscexp_ns[IOTS_NUM_EXP_MSC]; + qemu_irq msc_irq; + IoTKitSecCtlPPC apb[IOTS_NUM_APB_PPC]; IoTKitSecCtlPPC apbexp[IOTS_NUM_APB_EXP_PPC]; IoTKitSecCtlPPC ahbexp[IOTS_NUM_APB_EXP_PPC]; diff --git a/include/hw/misc/iotkit-sysctl.h b/include/hw/misc/iotkit-sysctl.h new file mode 100644 index 0000000000..601c8ecc0d --- /dev/null +++ b/include/hw/misc/iotkit-sysctl.h @@ -0,0 +1,72 @@ +/* + * ARM IoTKit system control element + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system control element" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * Specifically, it implements the "system information block" and + * "system control register" blocks. + * + * QEMU interface: + * + QOM property "SYS_VERSION": value of the SYS_VERSION register of the + * system information block of the SSE + * (used to identify whether to provide SSE-200-only registers) + * + sysbus MMIO region 0: the system information register bank + * + sysbus MMIO region 1: the system control register bank + */ + +#ifndef HW_MISC_IOTKIT_SYSCTL_H +#define HW_MISC_IOTKIT_SYSCTL_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SYSCTL "iotkit-sysctl" +#define IOTKIT_SYSCTL(obj) OBJECT_CHECK(IoTKitSysCtl, (obj), \ + TYPE_IOTKIT_SYSCTL) + +typedef struct IoTKitSysCtl { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + uint32_t secure_debug; + uint32_t reset_syndrome; + uint32_t reset_mask; + uint32_t gretreg; + uint32_t initsvtor0; + uint32_t cpuwait; + uint32_t wicctrl; + uint32_t scsecctrl; + uint32_t fclk_div; + uint32_t sysclk_div; + uint32_t clock_force; + uint32_t initsvtor1; + uint32_t nmi_enable; + uint32_t ewctrl; + uint32_t pdcm_pd_sys_sense; + uint32_t pdcm_pd_sram0_sense; + uint32_t pdcm_pd_sram1_sense; + uint32_t pdcm_pd_sram2_sense; + uint32_t pdcm_pd_sram3_sense; + + /* Properties */ + uint32_t sys_version; + uint32_t cpuwait_rst; + uint32_t initsvtor0_rst; + uint32_t initsvtor1_rst; + + bool is_sse200; +} IoTKitSysCtl; + +#endif diff --git a/include/hw/misc/iotkit-sysinfo.h b/include/hw/misc/iotkit-sysinfo.h new file mode 100644 index 0000000000..d84eb203b9 --- /dev/null +++ b/include/hw/misc/iotkit-sysinfo.h @@ -0,0 +1,43 @@ +/* + * ARM IoTKit system information block + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "system information block" which is part of the + * Arm IoTKit and documented in + * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ecm0601256/index.html + * QEMU interface: + * + QOM property "SYS_VERSION": value to use for SYS_VERSION register + * + QOM property "SYS_CONFIG": value to use for SYS_CONFIG register + * + sysbus MMIO region 0: the system information register bank + */ + +#ifndef HW_MISC_IOTKIT_SYSINFO_H +#define HW_MISC_IOTKIT_SYSINFO_H + +#include "hw/sysbus.h" + +#define TYPE_IOTKIT_SYSINFO "iotkit-sysinfo" +#define IOTKIT_SYSINFO(obj) OBJECT_CHECK(IoTKitSysInfo, (obj), \ + TYPE_IOTKIT_SYSINFO) + +typedef struct IoTKitSysInfo { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + + /* Properties */ + uint32_t sys_version; + uint32_t sys_config; +} IoTKitSysInfo; + +#endif diff --git a/include/hw/misc/macio/macio.h b/include/hw/misc/macio/macio.h index cfaa145500..970058b6ed 100644 --- a/include/hw/misc/macio/macio.h +++ b/include/hw/misc/macio/macio.h @@ -34,6 +34,42 @@ #include "hw/ppc/mac_dbdma.h" #include "hw/ppc/openpic.h" +/* MacIO virtual bus */ +#define TYPE_MACIO_BUS "macio-bus" +#define MACIO_BUS(obj) OBJECT_CHECK(MacIOBusState, (obj), TYPE_MACIO_BUS) + +typedef struct MacIOBusState { + /*< private >*/ + BusState parent_obj; +} MacIOBusState; + +/* MacIO IDE */ +#define TYPE_MACIO_IDE "macio-ide" +#define MACIO_IDE(obj) OBJECT_CHECK(MACIOIDEState, (obj), TYPE_MACIO_IDE) + +typedef struct MACIOIDEState { + /*< private >*/ + SysBusDevice parent_obj; + /*< public >*/ + uint32_t addr; + uint32_t channel; + qemu_irq real_ide_irq; + qemu_irq real_dma_irq; + qemu_irq ide_irq; + qemu_irq dma_irq; + + MemoryRegion mem; + IDEBus bus; + IDEDMA dma; + void *dbdma; + bool dma_active; + uint32_t timing_reg; + uint32_t irq_reg; +} MACIOIDEState; + +void macio_ide_init_drives(MACIOIDEState *ide, DriveInfo **hd_table); +void macio_ide_register_dma(MACIOIDEState *ide); + #define TYPE_MACIO "macio" #define MACIO(obj) OBJECT_CHECK(MacIOState, (obj), TYPE_MACIO) @@ -42,6 +78,7 @@ typedef struct MacIOState { PCIDevice parent; /*< public >*/ + MacIOBusState macio_bus; MemoryRegion bar; CUDAState cuda; PMUState pmu; diff --git a/include/hw/misc/mips_itu.h b/include/hw/misc/mips_itu.h index 030eb4ac62..c44e7672b6 100644 --- a/include/hw/misc/mips_itu.h +++ b/include/hw/misc/mips_itu.h @@ -66,6 +66,14 @@ typedef struct MIPSITUState { /* ITC Configuration Tags */ uint64_t ITCAddressMap[ITC_ADDRESSMAP_NUM]; MemoryRegion tag_io; + + /* ITU Control Register */ + uint64_t icr0; + + /* SAAR */ + bool saar_present; + void *saar; + } MIPSITUState; /* Get ITC Configuration Tag memory region. */ diff --git a/include/hw/misc/mmio_interface.h b/include/hw/misc/mmio_interface.h deleted file mode 100644 index 90d34fb228..0000000000 --- a/include/hw/misc/mmio_interface.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * mmio_interface.h - * - * Copyright (C) 2017 : GreenSocs - * http://www.greensocs.com/ , email: info@greensocs.com - * - * Developed by : - * Frederic Konrad - * - * 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 MMIO_INTERFACE_H -#define MMIO_INTERFACE_H - -#include "exec/memory.h" - -#define TYPE_MMIO_INTERFACE "mmio_interface" -#define MMIO_INTERFACE(obj) OBJECT_CHECK(MMIOInterface, (obj), \ - TYPE_MMIO_INTERFACE) - -typedef struct MMIOInterface { - DeviceState parent_obj; - - MemoryRegion *subregion; - MemoryRegion ram_mem; - uint64_t start; - uint64_t end; - bool ro; - uint64_t id; - void *host_ptr; -} MMIOInterface; - -void mmio_interface_map(MMIOInterface *s); -void mmio_interface_unmap(MMIOInterface *s); - -#endif /* MMIO_INTERFACE_H */ diff --git a/include/hw/misc/mps2-fpgaio.h b/include/hw/misc/mps2-fpgaio.h index eedf17ebc6..69e265cd4b 100644 --- a/include/hw/misc/mps2-fpgaio.h +++ b/include/hw/misc/mps2-fpgaio.h @@ -37,7 +37,17 @@ typedef struct { uint32_t prescale; uint32_t misc; + /* QEMU_CLOCK_VIRTUAL time at which counter and pscntr were last synced */ + int64_t pscntr_sync_ticks; + /* Values of COUNTER and PSCNTR at time pscntr_sync_ticks */ + uint32_t counter; + uint32_t pscntr; + uint32_t prescale_clk; + + /* These hold the CLOCK_VIRTUAL ns tick when the CLK1HZ/CLK100HZ was zero */ + int64_t clk1hz_tick_offset; + int64_t clk100hz_tick_offset; } MPS2FPGAIO; #endif diff --git a/include/hw/misc/nrf51_rng.h b/include/hw/misc/nrf51_rng.h new file mode 100644 index 0000000000..3d6bf79997 --- /dev/null +++ b/include/hw/misc/nrf51_rng.h @@ -0,0 +1,83 @@ +/* + * nRF51 Random Number Generator + * + * QEMU interface: + * + Property "period_unfiltered_us": Time between two biased values in + * microseconds. + * + Property "period_filtered_us": Time between two unbiased values in + * microseconds. + * + sysbus MMIO regions 0: Memory Region with tasks, events and registers + * to be mapped to the peripherals instance address by the SOC. + * + Named GPIO output "irq": Interrupt line of the peripheral. Must be + * connected to the associated peripheral interrupt line of the NVIC. + * + Named GPIO output "eep_valrdy": Event set when new random value is ready + * to be read. + * + Named GPIO input "tep_start": Task that triggers start of continuous + * generation of random values. + * + Named GPIO input "tep_stop": Task that ends continuous generation of + * random values. + * + * Accuracy of the peripheral model: + * + Stochastic properties of different configurations of the random source + * are not modeled. + * + Generation of unfiltered and filtered random values take at least the + * average generation time stated in the production specification; + * non-deterministic generation times are not modeled. + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ +#ifndef NRF51_RNG_H +#define NRF51_RNG_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#define TYPE_NRF51_RNG "nrf51_soc.rng" +#define NRF51_RNG(obj) OBJECT_CHECK(NRF51RNGState, (obj), TYPE_NRF51_RNG) + +#define NRF51_RNG_SIZE 0x1000 + +#define NRF51_RNG_TASK_START 0x000 +#define NRF51_RNG_TASK_STOP 0x004 +#define NRF51_RNG_EVENT_VALRDY 0x100 +#define NRF51_RNG_REG_SHORTS 0x200 +#define NRF51_RNG_REG_SHORTS_VALRDY_STOP 0 +#define NRF51_RNG_REG_INTEN 0x300 +#define NRF51_RNG_REG_INTEN_VALRDY 0 +#define NRF51_RNG_REG_INTENSET 0x304 +#define NRF51_RNG_REG_INTENCLR 0x308 +#define NRF51_RNG_REG_CONFIG 0x504 +#define NRF51_RNG_REG_CONFIG_DECEN 0 +#define NRF51_RNG_REG_VALUE 0x508 + +typedef struct { + SysBusDevice parent_obj; + + MemoryRegion mmio; + qemu_irq irq; + + /* Event End Points */ + qemu_irq eep_valrdy; + + QEMUTimer timer; + + /* Time between generation of successive unfiltered values in us */ + uint16_t period_unfiltered_us; + /* Time between generation of successive filtered values in us */ + uint16_t period_filtered_us; + + uint8_t value; + + uint32_t active; + uint32_t event_valrdy; + uint32_t shortcut_stop_on_valrdy; + uint32_t interrupt_enabled; + uint32_t filter_enabled; + +} NRF51RNGState; + + +#endif /* NRF51_RNG_H_ */ diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index 36a54e270c..1ee071a703 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -16,6 +16,15 @@ #define TYPE_PVPANIC "pvpanic" -uint16_t pvpanic_port(void); +#define PVPANIC_IOPORT_PROP "ioport" + +static inline uint16_t pvpanic_port(void) +{ + Object *o = object_resolve_path_type("", TYPE_PVPANIC, NULL); + if (!o) { + return 0; + } + return object_property_get_uint(o, PVPANIC_IOPORT_PROP, NULL); +} #endif diff --git a/include/hw/misc/tz-msc.h b/include/hw/misc/tz-msc.h new file mode 100644 index 0000000000..116b96ae9b --- /dev/null +++ b/include/hw/misc/tz-msc.h @@ -0,0 +1,79 @@ +/* + * ARM TrustZone master security controller emulation + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the TrustZone master security controller (MSC). + * It is documented in the ARM CoreLink SIE-200 System IP for Embedded TRM + * (DDI 0571G): + * https://developer.arm.com/products/architecture/m-profile/docs/ddi0571/g + * + * The MSC sits in front of a device which can be a bus master (such as + * a DMA controller) and allows secure software to configure it to either + * pass through or reject transactions made by that bus master. + * Rejected transactions may be configured to either be aborted, or to + * behave as RAZ/WI. An interrupt can be signalled for a rejected transaction. + * + * The MSC has no register interface -- it is configured purely by a + * collection of input signals from other hardware in the system. Typically + * they are either hardwired or exposed in an ad-hoc register interface by + * the SoC that uses the MSC. + * + * We don't currently implement the irq_enable GPIO input, because on + * the MPS2 FPGA images it is always tied high, which is awkward to + * implement in QEMU. + * + * QEMU interface: + * + Named GPIO input "cfg_nonsec": set to 1 if the bus master should be + * treated as nonsecure, or 0 for secure + * + Named GPIO input "cfg_sec_resp": set to 1 if a rejected transaction should + * result in a transaction error, or 0 for the transaction to RAZ/WI + * + Named GPIO input "irq_clear": set to 1 to clear a pending interrupt + * + Named GPIO output "irq": set for a transaction-failed interrupt + * + Property "downstream": MemoryRegion defining where bus master transactions + * are made if they are not blocked + * + Property "idau": an object implementing IDAUInterface, which defines which + * addresses should be treated as secure and which as non-secure. + * This need not be the same IDAU as the one used by the CPU. + * + sysbus MMIO region 0: MemoryRegion defining the upstream end of the MSC; + * this should be passed to the bus master device as the region it should + * make memory transactions to + */ + +#ifndef TZ_MSC_H +#define TZ_MSC_H + +#include "hw/sysbus.h" +#include "target/arm/idau.h" + +#define TYPE_TZ_MSC "tz-msc" +#define TZ_MSC(obj) OBJECT_CHECK(TZMSC, (obj), TYPE_TZ_MSC) + +typedef struct TZMSC { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + + /* State: these just track the values of our input signals */ + bool cfg_nonsec; + bool cfg_sec_resp; + bool irq_clear; + /* State: are we asserting irq ? */ + bool irq_status; + + qemu_irq irq; + MemoryRegion *downstream; + AddressSpace downstream_as; + MemoryRegion upstream; + IDAUInterface *idau; +} TZMSC; + +#endif diff --git a/include/hw/misc/tz-ppc.h b/include/hw/misc/tz-ppc.h index fc8b806e4d..080d6e2ec1 100644 --- a/include/hw/misc/tz-ppc.h +++ b/include/hw/misc/tz-ppc.h @@ -38,7 +38,13 @@ * * QEMU interface: * + sysbus MMIO regions 0..15: MemoryRegions defining the upstream end - * of each of the 16 ports of the PPC + * of each of the 16 ports of the PPC. When a port is unused (i.e. no + * downstream MemoryRegion is connected to it) at the end of the 0..15 + * range then no sysbus MMIO region is created for its upstream. When an + * unused port lies in the middle of the range with other used ports at + * higher port numbers, a dummy MMIO region is created to ensure that + * port N's upstream is always sysbus MMIO region N. Dummy regions should + * not be mapped, and will assert if any access is made to them. * + Property "port[0..15]": MemoryRegion defining the downstream device(s) * for each of the 16 ports of the PPC * + Named GPIO inputs "cfg_nonsec[0..15]": set to 1 if the port should be diff --git a/include/hw/misc/vmcoreinfo.h b/include/hw/misc/vmcoreinfo.h index c3aa856545..0d11578059 100644 --- a/include/hw/misc/vmcoreinfo.h +++ b/include/hw/misc/vmcoreinfo.h @@ -13,20 +13,12 @@ #define VMCOREINFO_H #include "hw/qdev.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #define VMCOREINFO_DEVICE "vmcoreinfo" #define VMCOREINFO(obj) OBJECT_CHECK(VMCoreInfoState, (obj), VMCOREINFO_DEVICE) -#define VMCOREINFO_FORMAT_NONE 0x0 -#define VMCOREINFO_FORMAT_ELF 0x1 - -/* all fields are little-endian */ -typedef struct FWCfgVMCoreInfo { - uint16_t host_format; /* set on reset */ - uint16_t guest_format; - uint32_t size; - uint64_t paddr; -} QEMU_PACKED FWCfgVMCoreInfo; +typedef struct fw_cfg_vmcoreinfo FWCfgVMCoreInfo; typedef struct VMCoreInfoState { DeviceClass parent_obj; diff --git a/include/hw/net/cadence_gem.h b/include/hw/net/cadence_gem.h index 35de622063..5426961d91 100644 --- a/include/hw/net/cadence_gem.h +++ b/include/hw/net/cadence_gem.h @@ -32,6 +32,9 @@ #define CADENCE_GEM_MAXREG (0x00000800 / 4) /* Last valid GEM address */ +/* Max number of words in a DMA descriptor. */ +#define DESC_MAX_NUM_WORDS 6 + #define MAX_PRIORITY_QUEUES 8 #define MAX_TYPE1_SCREENERS 16 #define MAX_TYPE2_SCREENERS 16 @@ -42,6 +45,8 @@ typedef struct CadenceGEMState { /*< public >*/ MemoryRegion iomem; + MemoryRegion *dma_mr; + AddressSpace dma_as; NICState *nic; NICConf conf; qemu_irq irq[MAX_PRIORITY_QUEUES]; @@ -74,7 +79,7 @@ typedef struct CadenceGEMState { uint8_t can_rx_state; /* Debug only */ - unsigned rx_desc[MAX_PRIORITY_QUEUES][2]; + uint32_t rx_desc[MAX_PRIORITY_QUEUES][DESC_MAX_NUM_WORDS]; bool sar_active[4]; } CadenceGEMState; diff --git a/include/hw/nmi.h b/include/hw/nmi.h index d092c684a1..ad857f3832 100644 --- a/include/hw/nmi.h +++ b/include/hw/nmi.h @@ -34,9 +34,7 @@ #define NMI(obj) \ INTERFACE_CHECK(NMI, (obj), TYPE_NMI) -typedef struct NMIState { - Object parent_obj; -} NMIState; +typedef struct NMIState NMIState; typedef struct NMIClass { InterfaceClass parent_class; diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index b2259cc4a3..f5a6895a74 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -2,7 +2,7 @@ #define FW_CFG_H #include "exec/hwaddr.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #include "hw/sysbus.h" #include "sysemu/dma.h" @@ -14,12 +14,7 @@ #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) -typedef struct FWCfgFile { - uint32_t size; /* file size */ - uint16_t select; /* write this to 0x510 to read it */ - uint16_t reserved; - char name[FW_CFG_MAX_FILE_PATH]; -} FWCfgFile; +typedef struct fw_cfg_file FWCfgFile; #define FW_CFG_ORDER_OVERRIDE_VGA 70 #define FW_CFG_ORDER_OVERRIDE_NIC 80 @@ -34,14 +29,7 @@ typedef struct FWCfgFiles { FWCfgFile f[]; } FWCfgFiles; -/* 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 struct fw_cfg_dma_access FWCfgDmaAccess; typedef void (*FWCfgCallback)(void *opaque); typedef void (*FWCfgWriteCallback)(void *opaque, off_t start, size_t len); diff --git a/include/hw/nvram/fw_cfg_keys.h b/include/hw/nvram/fw_cfg_keys.h deleted file mode 100644 index b6919451f5..0000000000 --- a/include/hw/nvram/fw_cfg_keys.h +++ /dev/null @@ -1,45 +0,0 @@ -#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_MIN 0x10 - -#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/include/hw/nvram/nrf51_nvm.h b/include/hw/nvram/nrf51_nvm.h new file mode 100644 index 0000000000..3792e4a9fe --- /dev/null +++ b/include/hw/nvram/nrf51_nvm.h @@ -0,0 +1,64 @@ +/* + * Nordic Semiconductor nRF51 non-volatile memory + * + * It provides an interface to erase regions in flash memory. + * Furthermore it provides the user and factory information registers. + * + * QEMU interface: + * + sysbus MMIO regions 0: NVMC peripheral registers + * + sysbus MMIO regions 1: FICR peripheral registers + * + sysbus MMIO regions 2: UICR peripheral registers + * + flash-size property: flash size in bytes. + * + * Accuracy of the peripheral model: + * + Code regions (MPU configuration) are disregarded. + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + */ +#ifndef NRF51_NVM_H +#define NRF51_NVM_H + +#include "hw/sysbus.h" +#define TYPE_NRF51_NVM "nrf51_soc.nvm" +#define NRF51_NVM(obj) OBJECT_CHECK(NRF51NVMState, (obj), TYPE_NRF51_NVM) + +#define NRF51_UICR_FIXTURE_SIZE 64 + +#define NRF51_NVMC_SIZE 0x1000 + +#define NRF51_NVMC_READY 0x400 +#define NRF51_NVMC_READY_READY 0x01 +#define NRF51_NVMC_CONFIG 0x504 +#define NRF51_NVMC_CONFIG_MASK 0x03 +#define NRF51_NVMC_CONFIG_WEN 0x01 +#define NRF51_NVMC_CONFIG_EEN 0x02 +#define NRF51_NVMC_ERASEPCR1 0x508 +#define NRF51_NVMC_ERASEPCR0 0x510 +#define NRF51_NVMC_ERASEALL 0x50C +#define NRF51_NVMC_ERASEUICR 0x514 +#define NRF51_NVMC_ERASE 0x01 + +#define NRF51_UICR_SIZE 0x100 + +typedef struct NRF51NVMState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + MemoryRegion ficr; + MemoryRegion uicr; + MemoryRegion flash; + + uint32_t uicr_content[NRF51_UICR_FIXTURE_SIZE]; + uint32_t flash_size; + uint8_t *storage; + + uint32_t config; + +} NRF51NVMState; + + +#endif diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h index aef38b881b..2af1c4a37e 100644 --- a/include/hw/pci-host/gpex.h +++ b/include/hw/pci-host/gpex.h @@ -13,8 +13,8 @@ * 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 + * You should have received a copy of the GNU General Public License + * along with this program; if not, see */ #ifndef HW_GPEX_H diff --git a/include/hw/pci-host/q35.h b/include/hw/pci-host/q35.h index 8f4ddde393..5ed77facd0 100644 --- a/include/hw/pci-host/q35.h +++ b/include/hw/pci-host/q35.h @@ -15,8 +15,8 @@ * 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 + * You should have received a copy of the GNU General Public License + * along with this program; if not, see */ #ifndef HW_Q35_H diff --git a/include/hw/pci-host/spapr.h b/include/hw/pci-host/spapr.h index 0fae4fc6a4..b4aad26798 100644 --- a/include/hw/pci-host/spapr.h +++ b/include/hw/pci-host/spapr.h @@ -28,11 +28,11 @@ #define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge" #define SPAPR_PCI_HOST_BRIDGE(obj) \ - OBJECT_CHECK(sPAPRPHBState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) + OBJECT_CHECK(SpaprPhbState, (obj), TYPE_SPAPR_PCI_HOST_BRIDGE) #define SPAPR_PCI_DMA_MAX_WINDOWS 2 -typedef struct sPAPRPHBState sPAPRPHBState; +typedef struct SpaprPhbState SpaprPhbState; typedef struct spapr_pci_msi { uint32_t first_irq; @@ -44,7 +44,7 @@ typedef struct spapr_pci_msi_mig { spapr_pci_msi value; } spapr_pci_msi_mig; -struct sPAPRPHBState { +struct SpaprPhbState { PCIHostState parent_obj; uint32_t index; @@ -72,7 +72,7 @@ struct sPAPRPHBState { int32_t msi_devs_num; spapr_pci_msi_mig *msi_devs; - QLIST_ENTRY(sPAPRPHBState) list; + QLIST_ENTRY(SpaprPhbState) list; bool ddw_enabled; uint64_t page_size_mask; @@ -94,67 +94,67 @@ struct sPAPRPHBState { ((1ULL << 32) - SPAPR_PCI_MEM_WIN_BUS_OFFSET) #define SPAPR_PCI_MEM64_WIN_SIZE 0x10000000000ULL /* 1 TiB */ -/* Without manual configuration, all PCI outbound windows will be - * within this range */ +/* All PCI outbound windows will be within this range */ #define SPAPR_PCI_BASE (1ULL << 45) /* 32 TiB */ #define SPAPR_PCI_LIMIT (1ULL << 46) /* 64 TiB */ -#define SPAPR_PCI_2_7_MMIO_WIN_SIZE 0xf80000000 +#define SPAPR_MAX_PHBS ((SPAPR_PCI_LIMIT - SPAPR_PCI_BASE) / \ + SPAPR_PCI_MEM64_WIN_SIZE - 1) + #define SPAPR_PCI_IO_WIN_SIZE 0x10000 #define SPAPR_PCI_MSI_WINDOW 0x40000000000ULL -static inline qemu_irq spapr_phb_lsi_qirq(struct sPAPRPHBState *phb, int pin) +static inline qemu_irq spapr_phb_lsi_qirq(struct SpaprPhbState *phb, int pin) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); return spapr_qirq(spapr, phb->lsi_table[pin].irq); } -PCIHostState *spapr_create_phb(sPAPRMachineState *spapr, int index); - -int spapr_populate_pci_dt(sPAPRPHBState *phb, - uint32_t xics_phandle, - void *fdt); +int spapr_populate_pci_dt(SpaprPhbState *phb, uint32_t intc_phandle, void *fdt, + uint32_t nr_msis, int *node_offset); void spapr_pci_rtas_init(void); -sPAPRPHBState *spapr_pci_find_phb(sPAPRMachineState *spapr, uint64_t buid); -PCIDevice *spapr_pci_find_dev(sPAPRMachineState *spapr, uint64_t buid, +SpaprPhbState *spapr_pci_find_phb(SpaprMachineState *spapr, uint64_t buid); +PCIDevice *spapr_pci_find_dev(SpaprMachineState *spapr, uint64_t buid, uint32_t config_addr); -/* PCI release callback. */ +/* DRC callbacks */ void spapr_phb_remove_pci_device_cb(DeviceState *dev); +int spapr_pci_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp); /* VFIO EEH hooks */ #ifdef CONFIG_LINUX -bool spapr_phb_eeh_available(sPAPRPHBState *sphb); -int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, +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); +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) +static inline bool spapr_phb_eeh_available(SpaprPhbState *sphb) { return false; } -static inline int spapr_phb_vfio_eeh_set_option(sPAPRPHBState *sphb, +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, +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) +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) +static inline int spapr_phb_vfio_eeh_configure(SpaprPhbState *sphb) { return RTAS_OUT_HW_ERROR; } @@ -163,6 +163,11 @@ static inline void spapr_phb_vfio_reset(DeviceState *qdev) } #endif -void spapr_phb_dma_reset(sPAPRPHBState *sphb); +void spapr_phb_dma_reset(SpaprPhbState *sphb); + +static inline unsigned spapr_phb_windows_supported(SpaprPhbState *sphb) +{ + return sphb->ddw_enabled ? SPAPR_PCI_DMA_MAX_WINDOWS : 1; +} #endif /* PCI_HOST_SPAPR_H */ diff --git a/include/hw/pci-host/uninorth.h b/include/hw/pci-host/uninorth.h index 2a1cf9f284..060324536a 100644 --- a/include/hw/pci-host/uninorth.h +++ b/include/hw/pci-host/uninorth.h @@ -49,6 +49,7 @@ typedef struct UNINHostState { PCIHostState parent_obj; + uint32_t ofw_addr; OpenPICState *pic; qemu_irq irqs[4]; MemoryRegion pci_mmio; diff --git a/include/hw/pci/msi.h b/include/hw/pci/msi.h index 4837bcf490..8440eaee11 100644 --- a/include/hw/pci/msi.h +++ b/include/hw/pci/msi.h @@ -39,6 +39,7 @@ int msi_init(struct PCIDevice *dev, uint8_t offset, bool msi_per_vector_mask, Error **errp); void msi_uninit(struct PCIDevice *dev); void msi_reset(PCIDevice *dev); +bool msi_is_masked(const PCIDevice *dev, unsigned int vector); void msi_notify(PCIDevice *dev, unsigned int vector); void msi_send_message(PCIDevice *dev, MSIMessage msg); void msi_write_config(PCIDevice *dev, uint32_t addr, uint32_t val, int len); diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 990d6fcbde..0abb06b357 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -396,6 +396,8 @@ typedef PCIINTxRoute (*pci_route_irq_fn)(void *opaque, int pin); bool pci_bus_is_express(PCIBus *bus); bool pci_bus_is_root(PCIBus *bus); +bool pci_bus_allows_extended_config_space(PCIBus *bus); + void pci_root_bus_new_inplace(PCIBus *bus, size_t bus_size, DeviceState *parent, const char *name, MemoryRegion *address_space_mem, @@ -405,8 +407,10 @@ PCIBus *pci_root_bus_new(DeviceState *parent, const char *name, MemoryRegion *address_space_mem, MemoryRegion *address_space_io, uint8_t devfn_min, const char *typename); +void pci_root_bus_cleanup(PCIBus *bus); void pci_bus_irqs(PCIBus *bus, pci_set_irq_fn set_irq, pci_map_irq_fn map_irq, void *irq_opaque, int nirq); +void pci_bus_irqs_cleanup(PCIBus *bus); int pci_bus_get_irq_level(PCIBus *bus, int irq_num); /* 0 <= pin <= 3 0 = INTA, 1 = INTB, 2 = INTC, 3 = INTD */ int pci_swizzle_map_irq_fn(PCIDevice *pci_dev, int pin); @@ -417,6 +421,7 @@ PCIBus *pci_register_root_bus(DeviceState *parent, const char *name, MemoryRegion *address_space_io, uint8_t devfn_min, int nirq, const char *typename); +void pci_unregister_root_bus(PCIBus *bus); void pci_bus_set_route_irq_fn(PCIBus *, pci_route_irq_fn); PCIINTxRoute pci_device_route_intx_to_irq(PCIDevice *dev, int pin); bool pci_intx_route_changed(PCIINTxRoute *old, PCIINTxRoute *new); @@ -707,8 +712,7 @@ PCIDevice *pci_create_simple_multifunction(PCIBus *bus, int devfn, PCIDevice *pci_create(PCIBus *bus, int devfn, const char *name); PCIDevice *pci_create_simple(PCIBus *bus, int devfn, const char *name); -void lsi53c895a_create(PCIBus *bus); -void lsi53c810_create(PCIBus *bus, int devfn); +void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev); qemu_irq pci_allocate_irq(PCIDevice *pci_dev); void pci_set_irq(PCIDevice *pci_dev, int level); @@ -738,6 +742,19 @@ static inline int pci_is_express(const PCIDevice *d) return d->cap_present & QEMU_PCI_CAP_EXPRESS; } +static inline int pci_is_express_downstream_port(const PCIDevice *d) +{ + uint8_t type; + + if (!pci_is_express(d) || !d->exp.exp_cap) { + return 0; + } + + type = pcie_cap_get_type(d); + + return type == PCI_EXP_TYPE_DOWNSTREAM || type == PCI_EXP_TYPE_ROOT_PORT; +} + static inline uint32_t pci_config_size(const PCIDevice *d) { return pci_is_express(d) ? PCIE_CONFIG_SPACE_SIZE : PCI_CONFIG_SPACE_SIZE; diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index 0347da52d2..ba488818d2 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -99,6 +99,12 @@ void pci_bridge_reset(DeviceState *qdev); void pci_bridge_initfn(PCIDevice *pci_dev, const char *typename); void pci_bridge_exitfn(PCIDevice *pci_dev); +void pci_bridge_dev_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void pci_bridge_dev_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); /* * before qdev initialization(qdev_init()), this function sets bus_name and @@ -133,11 +139,19 @@ typedef struct PCIBridgeQemuCap { #define REDHAT_PCI_CAP_RESOURCE_RESERVE 1 +/* + * PCI BUS/IO/MEM/PREFMEM additional resources recorded as a + * capability in PCI configuration space to reserve on firmware init. + */ +typedef struct PCIResReserve { + uint32_t bus; + uint64_t io; + uint64_t mem_non_pref; + uint64_t mem_pref_32; + uint64_t mem_pref_64; +} PCIResReserve; + int pci_bridge_qemu_reserve_cap_init(PCIDevice *dev, int cap_offset, - uint32_t bus_reserve, uint64_t io_reserve, - uint64_t mem_non_pref_reserve, - uint64_t mem_pref_32_reserve, - uint64_t mem_pref_64_reserve, - Error **errp); + PCIResReserve res_reserve, Error **errp); #endif /* QEMU_PCI_BRIDGE_H */ diff --git a/include/hw/pci/pci_bus.h b/include/hw/pci/pci_bus.h index b7da8f555b..f6df834170 100644 --- a/include/hw/pci/pci_bus.h +++ b/include/hw/pci/pci_bus.h @@ -1,6 +1,8 @@ #ifndef QEMU_PCI_BUS_H #define QEMU_PCI_BUS_H +#include "hw/pci/pci.h" + /* * PCI Bus datastructures. * @@ -16,6 +18,7 @@ typedef struct PCIBusClass { bool (*is_root)(PCIBus *bus); int (*bus_num)(PCIBus *bus); uint16_t (*numa_node)(PCIBus *bus); + bool (*allows_extended_config_space)(PCIBus *bus); } PCIBusClass; struct PCIBus { diff --git a/include/hw/pci/pci_ids.h b/include/hw/pci/pci_ids.h index 1a01d52473..f800aa2f08 100644 --- a/include/hw/pci/pci_ids.h +++ b/include/hw/pci/pci_ids.h @@ -255,7 +255,7 @@ #define PCI_DEVICE_ID_INTEL_82801I_EHCI2 0x293c #define PCI_DEVICE_ID_INTEL_82599_SFP_VF 0x10ed -#define PCI_DEVICE_ID_INTEL_Q35_MCH 0x29c0 +#define PCI_DEVICE_ID_INTEL_P35_MCH 0x29c0 #define PCI_VENDOR_ID_XEN 0x5853 #define PCI_DEVICE_ID_XEN_PLATFORM 0x0001 @@ -271,8 +271,9 @@ #define PCI_VENDOR_ID_SYNOPSYS 0x16C3 +#define PCI_VENDOR_ID_NVIDIA 0x10de + // XBOX -#define PCI_VENDOR_ID_NVIDIA 0x10de #define PCI_DEVICE_ID_NVIDIA_MCPX_APU 0x01b0 #define PCI_DEVICE_ID_NVIDIA_MCPX_ACI 0x01b1 #define PCI_DEVICE_ID_NVIDIA_NFORCE_LPC 0x01b2 diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index b71e369703..e30334d74d 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -79,6 +79,9 @@ struct PCIExpressDevice { /* Offset of ATS capability in config space */ uint16_t ats_cap; + + /* ACS */ + uint16_t acs_cap; }; #define COMPAT_PROP_PCP "power_controller_present" @@ -126,13 +129,21 @@ uint16_t pcie_find_capability(PCIDevice *dev, uint16_t cap_id); void pcie_add_capability(PCIDevice *dev, uint16_t cap_id, uint8_t cap_ver, uint16_t offset, uint16_t size); +void pcie_sync_bridge_lnk(PCIDevice *dev); + +void pcie_acs_init(PCIDevice *dev, uint16_t offset); +void pcie_acs_reset(PCIDevice *dev); void pcie_ari_init(PCIDevice *dev, uint16_t offset, uint16_t nextfn); void pcie_dev_ser_num_init(PCIDevice *dev, uint16_t offset, uint64_t ser_num); void pcie_ats_init(PCIDevice *dev, uint16_t offset); -void pcie_cap_slot_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp); -void pcie_cap_slot_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp); +void pcie_cap_slot_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void pcie_cap_slot_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/pcie_port.h b/include/hw/pci/pcie_port.h index 0736014bfd..09586f4641 100644 --- a/include/hw/pci/pcie_port.h +++ b/include/hw/pci/pcie_port.h @@ -49,6 +49,10 @@ struct PCIESlot { /* pci express switch port with slot */ uint8_t chassis; uint16_t slot; + + PCIExpLinkSpeed speed; + PCIExpLinkWidth width; + QLIST_ENTRY(PCIESlot) next; }; @@ -74,6 +78,7 @@ typedef struct PCIERootPortClass { int exp_offset; int aer_offset; int ssvid_offset; + int acs_offset; /* If nonzero, optional ACS capability offset */ int ssid; } PCIERootPortClass; diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index a95522a13b..1db86b0ec4 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -34,10 +34,29 @@ /* PCI_EXP_LINK{CAP, STA} */ /* link speed */ -#define PCI_EXP_LNK_LS_25 1 +typedef enum PCIExpLinkSpeed { + QEMU_PCI_EXP_LNK_2_5GT = 1, + QEMU_PCI_EXP_LNK_5GT, + QEMU_PCI_EXP_LNK_8GT, + QEMU_PCI_EXP_LNK_16GT, +} PCIExpLinkSpeed; + +#define QEMU_PCI_EXP_LNKCAP_MLS(speed) (speed) +#define QEMU_PCI_EXP_LNKSTA_CLS QEMU_PCI_EXP_LNKCAP_MLS + +typedef enum PCIExpLinkWidth { + QEMU_PCI_EXP_LNK_X1 = 1, + QEMU_PCI_EXP_LNK_X2 = 2, + QEMU_PCI_EXP_LNK_X4 = 4, + QEMU_PCI_EXP_LNK_X8 = 8, + QEMU_PCI_EXP_LNK_X12 = 12, + QEMU_PCI_EXP_LNK_X16 = 16, + QEMU_PCI_EXP_LNK_X32 = 32, +} PCIExpLinkWidth; #define PCI_EXP_LNK_MLW_SHIFT ctz32(PCI_EXP_LNKCAP_MLW) -#define PCI_EXP_LNK_MLW_1 (1 << PCI_EXP_LNK_MLW_SHIFT) +#define QEMU_PCI_EXP_LNKCAP_MLW(width) (width << PCI_EXP_LNK_MLW_SHIFT) +#define QEMU_PCI_EXP_LNKSTA_NLW QEMU_PCI_EXP_LNKCAP_MLW /* PCI_EXP_LINKCAP */ #define PCI_EXP_LNKCAP_ASPMS_SHIFT ctz32(PCI_EXP_LNKCAP_ASPMS) @@ -156,4 +175,8 @@ PCI_ERR_COR_INTERNAL | \ PCI_ERR_COR_HL_OVERFLOW) +/* ACS */ +#define PCI_ACS_VER 0x1 +#define PCI_ACS_SIZEOF 8 + #endif /* QEMU_PCIE_REGS_H */ diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index ee19fecf61..18f6ec1cd5 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -45,10 +45,12 @@ void shpc_free(PCIDevice *dev); void shpc_cap_write_config(PCIDevice *d, uint32_t addr, uint32_t val, int len); -void shpc_device_hotplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, - Error **errp); -void shpc_device_hot_unplug_request_cb(HotplugHandler *hotplug_dev, - DeviceState *dev, Error **errp); +void shpc_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void shpc_device_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, + Error **errp); +void shpc_device_unplug_request_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp); extern VMStateInfo shpc_vmstate_info; #define SHPC_VMSTATE(_field, _type, _test) \ diff --git a/include/hw/pcmcia.h b/include/hw/pcmcia.h index 79cac9c761..1b4080764f 100644 --- a/include/hw/pcmcia.h +++ b/include/hw/pcmcia.h @@ -18,13 +18,13 @@ typedef struct PCMCIASocket { #define PCMCIA_CARD_CLASS(cls) \ OBJECT_CLASS_CHECK(PCMCIACardClass, cls, TYPE_PCMCIA_CARD) -struct PCMCIACardState { +typedef struct PCMCIACardState { /*< private >*/ DeviceState parent_obj; /*< public >*/ PCMCIASocket *slot; -}; +} PCMCIACardState; typedef struct PCMCIACardClass { /*< private >*/ diff --git a/include/hw/ppc/openpic.h b/include/hw/ppc/openpic.h index 5eb982197d..dad08fe9be 100644 --- a/include/hw/ppc/openpic.h +++ b/include/hw/ppc/openpic.h @@ -20,6 +20,8 @@ enum { OPENPIC_OUTPUT_NB, }; +typedef struct IrqLines { qemu_irq irq[OPENPIC_OUTPUT_NB]; } IrqLines; + #define OPENPIC_MODEL_RAVEN 0 #define OPENPIC_MODEL_FSL_MPIC_20 1 #define OPENPIC_MODEL_FSL_MPIC_42 2 diff --git a/include/hw/ppc/pnv.h b/include/hw/ppc/pnv.h index 86d5f54e54..e5b00d373e 100644 --- a/include/hw/ppc/pnv.h +++ b/include/hw/ppc/pnv.h @@ -25,6 +25,8 @@ #include "hw/ppc/pnv_lpc.h" #include "hw/ppc/pnv_psi.h" #include "hw/ppc/pnv_occ.h" +#include "hw/ppc/pnv_xive.h" +#include "hw/ppc/pnv_core.h" #define TYPE_PNV_CHIP "pnv-chip" #define PNV_CHIP(obj) OBJECT_CHECK(PnvChip, (obj), TYPE_PNV_CHIP) @@ -57,6 +59,8 @@ typedef struct PnvChip { MemoryRegion xscom_mmio; MemoryRegion xscom; AddressSpace xscom_as; + + gchar *dt_isa_nodename; } PnvChip; #define TYPE_PNV8_CHIP "pnv8-chip" @@ -70,7 +74,7 @@ typedef struct Pnv8Chip { MemoryRegion icp_mmio; PnvLpcController lpc; - PnvPsi psi; + Pnv8Psi psi; PnvOCC occ; } Pnv8Chip; @@ -82,6 +86,13 @@ typedef struct Pnv9Chip { PnvChip parent_obj; /*< public >*/ + PnvXive xive; + Pnv9Psi psi; + PnvLpcController lpc; + PnvOCC occ; + + uint32_t nr_quads; + PnvQuad *quads; } Pnv9Chip; typedef struct PnvChipClass { @@ -98,8 +109,10 @@ typedef struct PnvChipClass { DeviceRealize parent_realize; uint32_t (*core_pir)(PnvChip *chip, uint32_t core_id); - Object *(*intc_create)(PnvChip *chip, Object *child, Error **errp); + void (*intc_create)(PnvChip *chip, PowerPCCPU *cpu, Error **errp); ISABus *(*isa_create)(PnvChip *chip, Error **errp); + void (*dt_populate)(PnvChip *chip, void *fdt); + void (*pic_print_info)(PnvChip *chip, Monitor *mon); } PnvChipClass; #define PNV_CHIP_TYPE_SUFFIX "-" TYPE_PNV_CHIP @@ -215,4 +228,31 @@ void pnv_bmc_powerdown(IPMIBmc *bmc); (0x0003ffe000000000ull + (uint64_t)PNV_CHIP_INDEX(chip) * \ PNV_PSIHB_FSP_SIZE) +/* + * POWER9 MMIO base addresses + */ +#define PNV9_CHIP_BASE(chip, base) \ + ((base) + ((uint64_t) (chip)->chip_id << 42)) + +#define PNV9_XIVE_VC_SIZE 0x0000008000000000ull +#define PNV9_XIVE_VC_BASE(chip) PNV9_CHIP_BASE(chip, 0x0006010000000000ull) + +#define PNV9_XIVE_PC_SIZE 0x0000001000000000ull +#define PNV9_XIVE_PC_BASE(chip) PNV9_CHIP_BASE(chip, 0x0006018000000000ull) + +#define PNV9_LPCM_SIZE 0x0000000100000000ull +#define PNV9_LPCM_BASE(chip) PNV9_CHIP_BASE(chip, 0x0006030000000000ull) + +#define PNV9_PSIHB_SIZE 0x0000000000100000ull +#define PNV9_PSIHB_BASE(chip) PNV9_CHIP_BASE(chip, 0x0006030203000000ull) + +#define PNV9_XIVE_IC_SIZE 0x0000000000080000ull +#define PNV9_XIVE_IC_BASE(chip) PNV9_CHIP_BASE(chip, 0x0006030203100000ull) + +#define PNV9_XIVE_TM_SIZE 0x0000000000040000ull +#define PNV9_XIVE_TM_BASE(chip) PNV9_CHIP_BASE(chip, 0x0006030203180000ull) + +#define PNV9_PSIHB_ESB_SIZE 0x0000000000010000ull +#define PNV9_PSIHB_ESB_BASE(chip) PNV9_CHIP_BASE(chip, 0x00060302031c0000ull) + #endif /* _PPC_PNV_H */ diff --git a/include/hw/ppc/pnv_core.h b/include/hw/ppc/pnv_core.h index 447ae761f7..50cdb2b358 100644 --- a/include/hw/ppc/pnv_core.h +++ b/include/hw/ppc/pnv_core.h @@ -42,9 +42,30 @@ typedef struct PnvCore { typedef struct PnvCoreClass { DeviceClass parent_class; + + const MemoryRegionOps *xscom_ops; } PnvCoreClass; #define PNV_CORE_TYPE_SUFFIX "-" TYPE_PNV_CORE #define PNV_CORE_TYPE_NAME(cpu_model) cpu_model PNV_CORE_TYPE_SUFFIX +typedef struct PnvCPUState { + Object *intc; +} PnvCPUState; + +static inline PnvCPUState *pnv_cpu_state(PowerPCCPU *cpu) +{ + return (PnvCPUState *)cpu->machine_data; +} + +#define TYPE_PNV_QUAD "powernv-cpu-quad" +#define PNV_QUAD(obj) \ + OBJECT_CHECK(PnvQuad, (obj), TYPE_PNV_QUAD) + +typedef struct PnvQuad { + DeviceState parent_obj; + + uint32_t id; + MemoryRegion xscom_regs; +} PnvQuad; #endif /* _PPC_PNV_CORE_H */ diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index d657489b07..413579792e 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -24,6 +24,11 @@ #define TYPE_PNV_LPC "pnv-lpc" #define PNV_LPC(obj) \ OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV_LPC) +#define TYPE_PNV8_LPC TYPE_PNV_LPC "-POWER8" +#define PNV8_LPC(obj) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV8_LPC) + +#define TYPE_PNV9_LPC TYPE_PNV_LPC "-POWER9" +#define PNV9_LPC(obj) OBJECT_CHECK(PnvLpcController, (obj), TYPE_PNV9_LPC) typedef struct PnvLpcController { DeviceState parent; @@ -50,6 +55,8 @@ typedef struct PnvLpcController { MemoryRegion opb_master_regs; /* OPB Master LS registers */ + uint32_t opb_irq_route0; + uint32_t opb_irq_route1; uint32_t opb_irq_stat; uint32_t opb_irq_mask; uint32_t opb_irq_pol; @@ -70,6 +77,25 @@ typedef struct PnvLpcController { PnvPsi *psi; } PnvLpcController; +#define PNV_LPC_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvLpcClass, (klass), TYPE_PNV_LPC) +#define PNV_LPC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvLpcClass, (obj), TYPE_PNV_LPC) + +typedef struct PnvLpcClass { + DeviceClass parent_class; + + int psi_irq; + + DeviceRealize parent_realize; +} PnvLpcClass; + +/* + * Old compilers error on typdef forward declarations. Keep them happy. + */ +struct PnvChip; + ISABus *pnv_lpc_isa_create(PnvLpcController *lpc, bool use_cpld, Error **errp); +int pnv_dt_lpc(struct PnvChip *chip, void *fdt, int root_offset); #endif /* _PPC_PNV_LPC_H */ diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index 82f299dc76..d22b65a71a 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -23,6 +23,10 @@ #define TYPE_PNV_OCC "pnv-occ" #define PNV_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV_OCC) +#define TYPE_PNV8_OCC TYPE_PNV_OCC "-POWER8" +#define PNV8_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV8_OCC) +#define TYPE_PNV9_OCC TYPE_PNV_OCC "-POWER9" +#define PNV9_OCC(obj) OBJECT_CHECK(PnvOCC, (obj), TYPE_PNV9_OCC) typedef struct PnvOCC { DeviceState xd; @@ -35,4 +39,17 @@ typedef struct PnvOCC { MemoryRegion xscom_regs; } PnvOCC; +#define PNV_OCC_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvOCCClass, (klass), TYPE_PNV_OCC) +#define PNV_OCC_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvOCCClass, (obj), TYPE_PNV_OCC) + +typedef struct PnvOCCClass { + DeviceClass parent_class; + + int xscom_size; + const MemoryRegionOps *xscom_ops; + int psi_irq; +} PnvOCCClass; + #endif /* _PPC_PNV_OCC_H */ diff --git a/include/hw/ppc/pnv_psi.h b/include/hw/ppc/pnv_psi.h index f6af5eae1f..2c1b27e865 100644 --- a/include/hw/ppc/pnv_psi.h +++ b/include/hw/ppc/pnv_psi.h @@ -21,6 +21,7 @@ #include "hw/sysbus.h" #include "hw/ppc/xics.h" +#include "hw/ppc/xive.h" #define TYPE_PNV_PSI "pnv-psi" #define PNV_PSI(obj) \ @@ -39,7 +40,7 @@ typedef struct PnvPsi { uint64_t fsp_bar; /* Interrupt generation */ - ICSState ics; + qemu_irq *qirqs; /* Registers */ uint64_t regs[PSIHB_XSCOM_MAX]; @@ -47,6 +48,42 @@ typedef struct PnvPsi { MemoryRegion xscom_regs; } PnvPsi; +#define TYPE_PNV8_PSI TYPE_PNV_PSI "-POWER8" +#define PNV8_PSI(obj) \ + OBJECT_CHECK(Pnv8Psi, (obj), TYPE_PNV8_PSI) + +typedef struct Pnv8Psi { + PnvPsi parent; + + ICSState ics; +} Pnv8Psi; + +#define TYPE_PNV9_PSI TYPE_PNV_PSI "-POWER9" +#define PNV9_PSI(obj) \ + OBJECT_CHECK(Pnv9Psi, (obj), TYPE_PNV9_PSI) + +typedef struct Pnv9Psi { + PnvPsi parent; + + XiveSource source; +} Pnv9Psi; + +#define PNV_PSI_CLASS(klass) \ + OBJECT_CLASS_CHECK(PnvPsiClass, (klass), TYPE_PNV_PSI) +#define PNV_PSI_GET_CLASS(obj) \ + OBJECT_GET_CLASS(PnvPsiClass, (obj), TYPE_PNV_PSI) + +typedef struct PnvPsiClass { + SysBusDeviceClass parent_class; + + int chip_type; + uint32_t xscom_pcba; + uint32_t xscom_size; + uint64_t bar_mask; + + void (*irq_set)(PnvPsi *psi, int, bool state); +} PnvPsiClass; + /* The PSI and FSP interrupts are muxed on the same IRQ number */ typedef enum PnvPsiIrq { PSIHB_IRQ_PSI, /* internal use only */ @@ -60,6 +97,25 @@ typedef enum PnvPsiIrq { #define PSI_NUM_INTERRUPTS 6 -extern void pnv_psi_irq_set(PnvPsi *psi, PnvPsiIrq irq, bool state); +void pnv_psi_irq_set(PnvPsi *psi, int irq, bool state); + +/* P9 PSI Interrupts */ +#define PSIHB9_IRQ_PSI 0 +#define PSIHB9_IRQ_OCC 1 +#define PSIHB9_IRQ_FSI 2 +#define PSIHB9_IRQ_LPCHC 3 +#define PSIHB9_IRQ_LOCAL_ERR 4 +#define PSIHB9_IRQ_GLOBAL_ERR 5 +#define PSIHB9_IRQ_TPM 6 +#define PSIHB9_IRQ_LPC_SIRQ0 7 +#define PSIHB9_IRQ_LPC_SIRQ1 8 +#define PSIHB9_IRQ_LPC_SIRQ2 9 +#define PSIHB9_IRQ_LPC_SIRQ3 10 +#define PSIHB9_IRQ_SBE_I2C 11 +#define PSIHB9_IRQ_DIO 12 +#define PSIHB9_IRQ_PSU 13 +#define PSIHB9_NUM_IRQS 14 + +void pnv_psi_pic_print_info(Pnv9Psi *psi, Monitor *mon); #endif /* _PPC_PNV_PSI_H */ diff --git a/include/hw/ppc/pnv_xive.h b/include/hw/ppc/pnv_xive.h new file mode 100644 index 0000000000..4fdaa9247d --- /dev/null +++ b/include/hw/ppc/pnv_xive.h @@ -0,0 +1,93 @@ +/* + * QEMU PowerPC XIVE interrupt controller model + * + * Copyright (c) 2017-2019, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_PNV_XIVE_H +#define PPC_PNV_XIVE_H + +#include "hw/ppc/xive.h" + +struct PnvChip; + +#define TYPE_PNV_XIVE "pnv-xive" +#define PNV_XIVE(obj) OBJECT_CHECK(PnvXive, (obj), TYPE_PNV_XIVE) + +#define XIVE_BLOCK_MAX 16 + +#define XIVE_TABLE_BLK_MAX 16 /* Block Scope Table (0-15) */ +#define XIVE_TABLE_MIG_MAX 16 /* Migration Register Table (1-15) */ +#define XIVE_TABLE_VDT_MAX 16 /* VDT Domain Table (0-15) */ +#define XIVE_TABLE_EDT_MAX 64 /* EDT Domain Table (0-63) */ + +typedef struct PnvXive { + XiveRouter parent_obj; + + /* Owning chip */ + struct PnvChip *chip; + + /* XSCOM addresses giving access to the controller registers */ + MemoryRegion xscom_regs; + + /* Main MMIO regions that can be configured by FW */ + MemoryRegion ic_mmio; + MemoryRegion ic_reg_mmio; + MemoryRegion ic_notify_mmio; + MemoryRegion ic_lsi_mmio; + MemoryRegion tm_indirect_mmio; + MemoryRegion vc_mmio; + MemoryRegion pc_mmio; + MemoryRegion tm_mmio; + + /* + * IPI and END address spaces modeling the EDT segmentation in the + * VC region + */ + AddressSpace ipi_as; + MemoryRegion ipi_mmio; + MemoryRegion ipi_edt_mmio; + + AddressSpace end_as; + MemoryRegion end_mmio; + MemoryRegion end_edt_mmio; + + /* Shortcut values for the Main MMIO regions */ + hwaddr ic_base; + uint32_t ic_shift; + hwaddr vc_base; + uint32_t vc_shift; + hwaddr pc_base; + uint32_t pc_shift; + hwaddr tm_base; + uint32_t tm_shift; + + /* Our XIVE source objects for IPIs and ENDs */ + XiveSource ipi_source; + XiveENDSource end_source; + + /* Interrupt controller registers */ + uint64_t regs[0x300]; + + /* Can be configured by FW */ + uint32_t tctx_chipid; + + /* + * Virtual Structure Descriptor tables : EAT, SBE, ENDT, NVTT, IRQ + * These are in a SRAM protected by ECC. + */ + uint64_t vsds[5][XIVE_BLOCK_MAX]; + + /* Translation tables */ + uint64_t blk[XIVE_TABLE_BLK_MAX]; + uint64_t mig[XIVE_TABLE_MIG_MAX]; + uint64_t vdt[XIVE_TABLE_VDT_MAX]; + uint64_t edt[XIVE_TABLE_EDT_MAX]; +} PnvXive; + +void pnv_xive_pic_print_info(PnvXive *xive, Monitor *mon); + +#endif /* PPC_PNV_XIVE_H */ diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index 255b26a5aa..68dfae0dfe 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -60,10 +60,6 @@ typedef struct PnvXScomInterfaceClass { (PNV_XSCOM_EX_CORE_BASE | ((uint64_t)(core) << 24)) #define PNV_XSCOM_EX_SIZE 0x100000 -#define PNV_XSCOM_P9_EC_BASE(core) \ - ((uint64_t)(((core) & 0x1F) + 0x20) << 24) -#define PNV_XSCOM_P9_EC_SIZE 0x100000 - #define PNV_XSCOM_LPC_BASE 0xb0020 #define PNV_XSCOM_LPC_SIZE 0x4 @@ -73,6 +69,23 @@ typedef struct PnvXScomInterfaceClass { #define PNV_XSCOM_OCC_BASE 0x0066000 #define PNV_XSCOM_OCC_SIZE 0x6000 +#define PNV9_XSCOM_EC_BASE(core) \ + ((uint64_t)(((core) & 0x1F) + 0x20) << 24) +#define PNV9_XSCOM_EC_SIZE 0x100000 + +#define PNV9_XSCOM_EQ_BASE(core) \ + ((uint64_t)(((core) & 0x1C) + 0x40) << 22) +#define PNV9_XSCOM_EQ_SIZE 0x100000 + +#define PNV9_XSCOM_OCC_BASE PNV_XSCOM_OCC_BASE +#define PNV9_XSCOM_OCC_SIZE 0x8000 + +#define PNV9_XSCOM_PSIHB_BASE 0x5012900 +#define PNV9_XSCOM_PSIHB_SIZE 0x100 + +#define PNV9_XSCOM_XIVE_BASE 0x5013000 +#define PNV9_XSCOM_XIVE_SIZE 0x300 + extern void pnv_xscom_realize(PnvChip *chip, Error **errp); extern int pnv_dt_xscom(PnvChip *chip, void *fdt, int offset); diff --git a/include/hw/ppc/ppc.h b/include/hw/ppc/ppc.h index 298ec354a8..4bdcb8bacd 100644 --- a/include/hw/ppc/ppc.h +++ b/include/hw/ppc/ppc.h @@ -4,6 +4,7 @@ #include "target/ppc/cpu-qom.h" void ppc_set_irq(PowerPCCPU *cpu, int n_IRQ, int level); +PowerPCCPU *ppc_get_vcpu_by_pir(int pir); /* PowerPC hardware exceptions management helpers */ typedef void (*clk_setup_cb)(void *opaque, uint32_t freq); @@ -73,6 +74,7 @@ static inline void ppc40x_irq_init(PowerPCCPU *cpu) {} static inline void ppc6xx_irq_init(PowerPCCPU *cpu) {} static inline void ppc970_irq_init(PowerPCCPU *cpu) {} static inline void ppcPOWER7_irq_init(PowerPCCPU *cpu) {} +static inline void ppcPOWER9_irq_init(PowerPCCPU *cpu) {} static inline void ppce500_irq_init(PowerPCCPU *cpu) {} #else void ppc40x_irq_init(PowerPCCPU *cpu); @@ -80,6 +82,7 @@ void ppce500_irq_init(PowerPCCPU *cpu); void ppc6xx_irq_init(PowerPCCPU *cpu); void ppc970_irq_init(PowerPCCPU *cpu); void ppcPOWER7_irq_init(PowerPCCPU *cpu); +void ppcPOWER9_irq_init(PowerPCCPU *cpu); #endif /* PPC machines for OpenBIOS */ diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index 3a2a04c8ce..39a7ba1ce6 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -43,7 +43,7 @@ ram_addr_t ppc4xx_sdram_adjust(ram_addr_t ram_size, int nr_banks, MemoryRegion ram_memories[], hwaddr ram_bases[], hwaddr ram_sizes[], - const unsigned int sdram_bank_sizes[]); + const ram_addr_t sdram_bank_sizes[]); void ppc4xx_sdram_init (CPUPPCState *env, qemu_irq irq, int nbanks, MemoryRegion ram_memories[], diff --git a/include/hw/ppc/spapr.h b/include/hw/ppc/spapr.h index 7e5de1a6fd..5ea8081041 100644 --- a/include/hw/ppc/spapr.h +++ b/include/hw/ppc/spapr.h @@ -4,17 +4,20 @@ #include "qemu/units.h" #include "sysemu/dma.h" #include "hw/boards.h" -#include "hw/ppc/xics.h" #include "hw/ppc/spapr_drc.h" #include "hw/mem/pc-dimm.h" #include "hw/ppc/spapr_ovec.h" +#include "hw/ppc/spapr_irq.h" +#include "hw/ppc/spapr_xive.h" /* For SpaprXive */ +#include "hw/ppc/xics.h" /* For ICSState */ -struct VIOsPAPRBus; -struct sPAPRPHBState; -struct sPAPRNVRAM; -typedef struct sPAPREventLogEntry sPAPREventLogEntry; -typedef struct sPAPREventSource sPAPREventSource; -typedef struct sPAPRPendingHPT sPAPRPendingHPT; +struct SpaprVioBus; +struct SpaprPhbState; +struct SpaprNvram; + +typedef struct SpaprEventLogEntry SpaprEventLogEntry; +typedef struct SpaprEventSource SpaprEventSource; +typedef struct SpaprPendingHpt SpaprPendingHpt; #define HPTE64_V_HPTE_DIRTY 0x0000000000000040ULL #define SPAPR_ENTRY_POINT 0x100 @@ -24,32 +27,32 @@ typedef struct sPAPRPendingHPT sPAPRPendingHPT; #define TYPE_SPAPR_RTC "spapr-rtc" #define SPAPR_RTC(obj) \ - OBJECT_CHECK(sPAPRRTCState, (obj), TYPE_SPAPR_RTC) + OBJECT_CHECK(SpaprRtcState, (obj), TYPE_SPAPR_RTC) -typedef struct sPAPRRTCState sPAPRRTCState; -struct sPAPRRTCState { +typedef struct SpaprRtcState SpaprRtcState; +struct SpaprRtcState { /*< private >*/ DeviceState parent_obj; int64_t ns_offset; }; -typedef struct sPAPRDIMMState sPAPRDIMMState; -typedef struct sPAPRMachineClass sPAPRMachineClass; +typedef struct SpaprDimmState SpaprDimmState; +typedef struct SpaprMachineClass SpaprMachineClass; #define TYPE_SPAPR_MACHINE "spapr-machine" #define SPAPR_MACHINE(obj) \ - OBJECT_CHECK(sPAPRMachineState, (obj), TYPE_SPAPR_MACHINE) + OBJECT_CHECK(SpaprMachineState, (obj), TYPE_SPAPR_MACHINE) #define SPAPR_MACHINE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRMachineClass, obj, TYPE_SPAPR_MACHINE) + OBJECT_GET_CLASS(SpaprMachineClass, obj, TYPE_SPAPR_MACHINE) #define SPAPR_MACHINE_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRMachineClass, klass, TYPE_SPAPR_MACHINE) + OBJECT_CLASS_CHECK(SpaprMachineClass, klass, TYPE_SPAPR_MACHINE) typedef enum { SPAPR_RESIZE_HPT_DEFAULT = 0, SPAPR_RESIZE_HPT_DISABLED, SPAPR_RESIZE_HPT_ENABLED, SPAPR_RESIZE_HPT_REQUIRED, -} sPAPRResizeHPT; +} SpaprResizeHpt; /** * Capabilities @@ -69,8 +72,14 @@ typedef enum { #define SPAPR_CAP_IBS 0x05 /* HPT Maximum Page Size (encoded as a shift) */ #define SPAPR_CAP_HPT_MAXPAGESIZE 0x06 +/* Nested KVM-HV */ +#define SPAPR_CAP_NESTED_KVM_HV 0x07 +/* Large Decrementer */ +#define SPAPR_CAP_LARGE_DECREMENTER 0x08 +/* Count Cache Flush Assist HW Instruction */ +#define SPAPR_CAP_CCF_ASSIST 0x09 /* Num Caps */ -#define SPAPR_CAP_NUM (SPAPR_CAP_HPT_MAXPAGESIZE + 1) +#define SPAPR_CAP_NUM (SPAPR_CAP_CCF_ASSIST + 1) /* * Capability Values @@ -78,60 +87,74 @@ typedef enum { /* Bool Caps */ #define SPAPR_CAP_OFF 0x00 #define SPAPR_CAP_ON 0x01 + /* Custom Caps */ + +/* Generic */ #define SPAPR_CAP_BROKEN 0x00 #define SPAPR_CAP_WORKAROUND 0x01 #define SPAPR_CAP_FIXED 0x02 +/* SPAPR_CAP_IBS (cap-ibs) */ #define SPAPR_CAP_FIXED_IBS 0x02 #define SPAPR_CAP_FIXED_CCD 0x03 +#define SPAPR_CAP_FIXED_NA 0x10 /* Lets leave a bit of a gap... */ -typedef struct sPAPRCapabilities sPAPRCapabilities; -struct sPAPRCapabilities { +typedef struct SpaprCapabilities SpaprCapabilities; +struct SpaprCapabilities { uint8_t caps[SPAPR_CAP_NUM]; }; /** - * sPAPRMachineClass: + * SpaprMachineClass: */ -struct sPAPRMachineClass { +struct SpaprMachineClass { /*< private >*/ MachineClass parent_class; /*< public >*/ bool dr_lmb_enabled; /* enable dynamic-reconfig/hotplug of LMBs */ + bool dr_phb_enabled; /* enable dynamic-reconfig/hotplug of PHBs */ + bool update_dt_enabled; /* enable KVMPPC_H_UPDATE_DT */ bool use_ohci_by_default; /* use USB-OHCI instead of XHCI */ bool pre_2_10_has_unused_icps; - void (*phb_placement)(sPAPRMachineState *spapr, uint32_t index, + bool legacy_irq_allocation; + bool broken_host_serial_model; /* present real host info to the guest */ + + void (*phb_placement)(SpaprMachineState *spapr, uint32_t index, uint64_t *buid, hwaddr *pio, hwaddr *mmio32, hwaddr *mmio64, unsigned n_dma, uint32_t *liobns, Error **errp); - sPAPRResizeHPT resize_hpt_default; - sPAPRCapabilities default_caps; + SpaprResizeHpt resize_hpt_default; + SpaprCapabilities default_caps; + SpaprIrq *irq; }; /** - * sPAPRMachineState: + * SpaprMachineState: */ -struct sPAPRMachineState { +struct SpaprMachineState { /*< private >*/ MachineState parent_obj; - struct VIOsPAPRBus *vio_bus; - QLIST_HEAD(, sPAPRPHBState) phbs; - struct sPAPRNVRAM *nvram; + struct SpaprVioBus *vio_bus; + QLIST_HEAD(, SpaprPhbState) phbs; + struct SpaprNvram *nvram; ICSState *ics; - sPAPRRTCState rtc; + SpaprRtcState rtc; - sPAPRResizeHPT resize_hpt; + SpaprResizeHpt resize_hpt; void *htab; uint32_t htab_shift; uint64_t patb_entry; /* Process tbl registed in H_REGISTER_PROCESS_TABLE */ - sPAPRPendingHPT *pending_hpt; /* in-progress resize */ + SpaprPendingHpt *pending_hpt; /* in-progress resize */ hwaddr rma_size; int vrma_adjust; ssize_t rtas_size; void *rtas_blob; + uint32_t fdt_size; + uint32_t fdt_initial_size; + void *fdt_blob; long kernel_size; bool kernel_le; uint32_t initrd_base; @@ -142,15 +165,15 @@ struct sPAPRMachineState { uint32_t vsmt; /* Virtual SMT mode (KVM's "core stride") */ Notifier epow_notifier; - QTAILQ_HEAD(, sPAPREventLogEntry) pending_events; + QTAILQ_HEAD(, SpaprEventLogEntry) pending_events; bool use_hotplug_event_source; - sPAPREventSource *event_sources; + SpaprEventSource *event_sources; /* ibm,client-architecture-support option negotiation */ bool cas_reboot; bool cas_legacy_guest_workaround; - sPAPROptionVector *ov5; /* QEMU-supported option vectors */ - sPAPROptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ + SpaprOptionVector *ov5; /* QEMU-supported option vectors */ + SpaprOptionVector *ov5_cas; /* negotiated (via CAS) option vectors */ uint32_t max_compat_pvr; /* Migration state */ @@ -161,15 +184,21 @@ struct sPAPRMachineState { /* Pending DIMM unplug cache. It is populated when a LMB * unplug starts. It can be regenerated if a migration * occurs during the unplug process. */ - QTAILQ_HEAD(, sPAPRDIMMState) pending_dimm_unplugs; + QTAILQ_HEAD(, SpaprDimmState) pending_dimm_unplugs; /*< public >*/ char *kvm_type; + char *host_model; + char *host_serial; - const char *icp_type; + int32_t irq_map_nr; + unsigned long *irq_map; + SpaprXive *xive; + SpaprIrq *irq; + qemu_irq *qirqs; bool cmd_line_caps[SPAPR_CAP_NUM]; - sPAPRCapabilities def, eff, mig; + SpaprCapabilities def, eff, mig; }; #define H_SUCCESS 0 @@ -318,9 +347,11 @@ struct sPAPRMachineState { #define H_CPU_CHAR_HON_BRANCH_HINTS PPC_BIT(5) #define H_CPU_CHAR_THR_RECONF_TRIG PPC_BIT(6) #define H_CPU_CHAR_CACHE_COUNT_DIS PPC_BIT(7) +#define H_CPU_CHAR_BCCTR_FLUSH_ASSIST PPC_BIT(9) #define H_CPU_BEHAV_FAVOUR_SECURITY PPC_BIT(0) #define H_CPU_BEHAV_L1D_FLUSH_PR PPC_BIT(1) #define H_CPU_BEHAV_BNDS_CHK_SPEC_BAR PPC_BIT(2) +#define H_CPU_BEHAV_FLUSH_COUNT_CACHE PPC_BIT(5) /* Each control block has to be on a 4K boundary */ #define H_CB_ALIGNMENT 4096 @@ -433,6 +464,7 @@ struct sPAPRMachineState { #define H_GET_EM_PARMS 0x2B8 #define H_SET_MPP 0x2D0 #define H_GET_MPP 0x2D4 +#define H_HOME_NODE_ASSOCIATIVITY 0x2EC #define H_XIRR_X 0x2FC #define H_RANDOM 0x300 #define H_SET_MODE 0x31C @@ -442,7 +474,20 @@ struct sPAPRMachineState { #define H_INVALIDATE_PID 0x378 #define H_REGISTER_PROC_TBL 0x37C #define H_SIGNAL_SYS_RESET 0x380 -#define MAX_HCALL_OPCODE H_SIGNAL_SYS_RESET + +#define H_INT_GET_SOURCE_INFO 0x3A8 +#define H_INT_SET_SOURCE_CONFIG 0x3AC +#define H_INT_GET_SOURCE_CONFIG 0x3B0 +#define H_INT_GET_QUEUE_INFO 0x3B4 +#define H_INT_SET_QUEUE_CONFIG 0x3B8 +#define H_INT_GET_QUEUE_CONFIG 0x3BC +#define H_INT_SET_OS_REPORTING_LINE 0x3C0 +#define H_INT_GET_OS_REPORTING_LINE 0x3C4 +#define H_INT_ESB 0x3C8 +#define H_INT_SYNC 0x3CC +#define H_INT_RESET 0x3D0 + +#define MAX_HCALL_OPCODE H_INT_RESET /* The hcalls above are standardized in PAPR and implemented by pHyp * as well. @@ -456,18 +501,19 @@ struct sPAPRMachineState { #define KVMPPC_H_LOGICAL_MEMOP (KVMPPC_HCALL_BASE + 0x1) /* Client Architecture support */ #define KVMPPC_H_CAS (KVMPPC_HCALL_BASE + 0x2) -#define KVMPPC_HCALL_MAX KVMPPC_H_CAS +#define KVMPPC_H_UPDATE_DT (KVMPPC_HCALL_BASE + 0x3) +#define KVMPPC_HCALL_MAX KVMPPC_H_UPDATE_DT -typedef struct sPAPRDeviceTreeUpdateHeader { +typedef struct SpaprDeviceTreeUpdateHeader { uint32_t version_id; -} sPAPRDeviceTreeUpdateHeader; +} SpaprDeviceTreeUpdateHeader; #define hcall_dprintf(fmt, ...) \ do { \ qemu_log_mask(LOG_GUEST_ERROR, "%s: " fmt, __func__, ## __VA_ARGS__); \ } while (0) -typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, sPAPRMachineState *sm, +typedef target_ulong (*spapr_hcall_fn)(PowerPCCPU *cpu, SpaprMachineState *sm, target_ulong opcode, target_ulong *args); @@ -621,16 +667,16 @@ 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); } -typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, sPAPRMachineState *sm, +typedef void (*spapr_rtas_fn)(PowerPCCPU *cpu, SpaprMachineState *sm, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets); void spapr_rtas_register(int token, const char *name, spapr_rtas_fn fn); -target_ulong spapr_rtas_call(PowerPCCPU *cpu, sPAPRMachineState *sm, +target_ulong spapr_rtas_call(PowerPCCPU *cpu, SpaprMachineState *sm, uint32_t token, uint32_t nargs, target_ulong args, uint32_t nret, target_ulong rets); void spapr_dt_rtas_tokens(void *fdt, int rtas); -void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr); +void spapr_load_rtas(SpaprMachineState *spapr, void *fdt, hwaddr addr); #define SPAPR_TCE_PAGE_SHIFT 12 #define SPAPR_TCE_PAGE_SIZE (1ULL << SPAPR_TCE_PAGE_SHIFT) @@ -651,23 +697,23 @@ void spapr_load_rtas(sPAPRMachineState *spapr, void *fdt, hwaddr addr); * "interrupt-controller" node has its "#interrupt-cells" property set to 2 (ie, * VIO devices, RTAS event sources and PHBs). */ -static inline void spapr_dt_xics_irq(uint32_t *intspec, int irq, bool is_lsi) +static inline void spapr_dt_irq(uint32_t *intspec, int irq, bool is_lsi) { intspec[0] = cpu_to_be32(irq); intspec[1] = is_lsi ? cpu_to_be32(1) : 0; } -typedef struct sPAPRTCETable sPAPRTCETable; +typedef struct SpaprTceTable SpaprTceTable; #define TYPE_SPAPR_TCE_TABLE "spapr-tce-table" #define SPAPR_TCE_TABLE(obj) \ - OBJECT_CHECK(sPAPRTCETable, (obj), TYPE_SPAPR_TCE_TABLE) + OBJECT_CHECK(SpaprTceTable, (obj), TYPE_SPAPR_TCE_TABLE) #define TYPE_SPAPR_IOMMU_MEMORY_REGION "spapr-iommu-memory-region" #define SPAPR_IOMMU_MEMORY_REGION(obj) \ OBJECT_CHECK(IOMMUMemoryRegion, (obj), TYPE_SPAPR_IOMMU_MEMORY_REGION) -struct sPAPRTCETable { +struct SpaprTceTable { DeviceState parent; uint32_t liobn; uint32_t nb_table; @@ -678,70 +724,77 @@ struct sPAPRTCETable { uint64_t *mig_table; bool bypass; bool need_vfio; + bool skipping_replay; int fd; MemoryRegion root; IOMMUMemoryRegion iommu; - struct VIOsPAPRDevice *vdev; /* for @bypass migration compatibility only */ - QLIST_ENTRY(sPAPRTCETable) list; + struct SpaprVioDevice *vdev; /* for @bypass migration compatibility only */ + QLIST_ENTRY(SpaprTceTable) list; }; -sPAPRTCETable *spapr_tce_find_by_liobn(target_ulong liobn); +SpaprTceTable *spapr_tce_find_by_liobn(target_ulong liobn); -struct sPAPREventLogEntry { +struct SpaprEventLogEntry { uint32_t summary; uint32_t extended_length; void *extended_log; - QTAILQ_ENTRY(sPAPREventLogEntry) next; + QTAILQ_ENTRY(SpaprEventLogEntry) next; }; -void spapr_events_init(sPAPRMachineState *sm); -void spapr_dt_events(sPAPRMachineState *sm, void *fdt); -int spapr_h_cas_compose_response(sPAPRMachineState *sm, +void spapr_events_init(SpaprMachineState *sm); +void spapr_dt_events(SpaprMachineState *sm, void *fdt); +int spapr_h_cas_compose_response(SpaprMachineState *sm, target_ulong addr, target_ulong size, - sPAPROptionVector *ov5_updates); -void close_htab_fd(sPAPRMachineState *spapr); -void spapr_setup_hpt_and_vrma(sPAPRMachineState *spapr); -void spapr_free_hpt(sPAPRMachineState *spapr); -sPAPRTCETable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); -void spapr_tce_table_enable(sPAPRTCETable *tcet, + SpaprOptionVector *ov5_updates); +void close_htab_fd(SpaprMachineState *spapr); +void spapr_setup_hpt_and_vrma(SpaprMachineState *spapr); +void spapr_free_hpt(SpaprMachineState *spapr); +SpaprTceTable *spapr_tce_new_table(DeviceState *owner, uint32_t liobn); +void spapr_tce_table_enable(SpaprTceTable *tcet, uint32_t page_shift, uint64_t bus_offset, uint32_t nb_table); -void spapr_tce_table_disable(sPAPRTCETable *tcet); -void spapr_tce_set_need_vfio(sPAPRTCETable *tcet, bool need_vfio); +void spapr_tce_table_disable(SpaprTceTable *tcet); +void spapr_tce_set_need_vfio(SpaprTceTable *tcet, bool need_vfio); -MemoryRegion *spapr_tce_get_iommu(sPAPRTCETable *tcet); +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); + SpaprTceTable *tcet); void spapr_pci_switch_vga(bool big_endian); -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, +void spapr_hotplug_req_add_by_index(SpaprDrc *drc); +void spapr_hotplug_req_remove_by_index(SpaprDrc *drc); +void spapr_hotplug_req_add_by_count(SpaprDrcType drc_type, uint32_t count); -void spapr_hotplug_req_remove_by_count(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_remove_by_count(SpaprDrcType drc_type, uint32_t count); -void spapr_hotplug_req_add_by_count_indexed(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_add_by_count_indexed(SpaprDrcType drc_type, uint32_t count, uint32_t index); -void spapr_hotplug_req_remove_by_count_indexed(sPAPRDRConnectorType drc_type, +void spapr_hotplug_req_remove_by_count_indexed(SpaprDrcType drc_type, uint32_t count, uint32_t index); int spapr_hpt_shift_for_ramsize(uint64_t ramsize); -void spapr_reallocate_hpt(sPAPRMachineState *spapr, int shift, +void spapr_reallocate_hpt(SpaprMachineState *spapr, int shift, Error **errp); -void spapr_clear_pending_events(sPAPRMachineState *spapr); +void spapr_clear_pending_events(SpaprMachineState *spapr); +int spapr_max_server_number(SpaprMachineState *spapr); -/* CPU and LMB DRC release callbacks. */ +/* DRC callbacks. */ void spapr_core_release(DeviceState *dev); +int spapr_core_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp); void spapr_lmb_release(DeviceState *dev); +int spapr_lmb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp); +void spapr_phb_release(DeviceState *dev); +int spapr_phb_dt_populate(SpaprDrc *drc, SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp); -void spapr_rtc_read(sPAPRRTCState *rtc, struct tm *tm, uint32_t *ns); -int spapr_rtc_import_offset(sPAPRRTCState *rtc, int64_t legacy_offset); +void spapr_rtc_read(SpaprRtcState *rtc, struct tm *tm, uint32_t *ns); +int spapr_rtc_import_offset(SpaprRtcState *rtc, int64_t legacy_offset); #define TYPE_SPAPR_RNG "spapr-rng" -int spapr_rng_populate_dt(void *fdt); - -#define SPAPR_MEMORY_BLOCK_SIZE (1 << 28) /* 256MB */ +#define SPAPR_MEMORY_BLOCK_SIZE ((hwaddr)1 << 28) /* 256MB */ /* * This defines the maximum number of DIMM slots we can have for sPAPR @@ -775,14 +828,6 @@ int spapr_get_vcpu_id(PowerPCCPU *cpu); void spapr_set_vcpu_id(PowerPCCPU *cpu, int cpu_index, Error **errp); PowerPCCPU *spapr_find_cpu(int vcpu_id); -int spapr_irq_find(sPAPRMachineState *spapr, int num, bool align, - Error **errp); -#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp) -int spapr_irq_claim(sPAPRMachineState *spapr, int irq, bool lsi, Error **errp); -void spapr_irq_free(sPAPRMachineState *spapr, int irq, int num); -qemu_irq spapr_qirq(sPAPRMachineState *spapr, int irq); - - int spapr_caps_pre_load(void *opaque); int spapr_caps_pre_save(void *opaque); @@ -795,19 +840,29 @@ extern const VMStateDescription vmstate_spapr_cap_dfp; extern const VMStateDescription vmstate_spapr_cap_cfpc; extern const VMStateDescription vmstate_spapr_cap_sbbc; extern const VMStateDescription vmstate_spapr_cap_ibs; +extern const VMStateDescription vmstate_spapr_cap_nested_kvm_hv; +extern const VMStateDescription vmstate_spapr_cap_large_decr; +extern const VMStateDescription vmstate_spapr_cap_ccf_assist; -static inline uint8_t spapr_get_cap(sPAPRMachineState *spapr, int cap) +static inline uint8_t spapr_get_cap(SpaprMachineState *spapr, int cap) { return spapr->eff.caps[cap]; } -void spapr_caps_init(sPAPRMachineState *spapr); -void spapr_caps_apply(sPAPRMachineState *spapr); -void spapr_caps_cpu_apply(sPAPRMachineState *spapr, PowerPCCPU *cpu); -void spapr_caps_add_properties(sPAPRMachineClass *smc, Error **errp); -int spapr_caps_post_migration(sPAPRMachineState *spapr); +void spapr_caps_init(SpaprMachineState *spapr); +void spapr_caps_apply(SpaprMachineState *spapr); +void spapr_caps_cpu_apply(SpaprMachineState *spapr, PowerPCCPU *cpu); +void spapr_caps_add_properties(SpaprMachineClass *smc, Error **errp); +int spapr_caps_post_migration(SpaprMachineState *spapr); -void spapr_check_pagesize(sPAPRMachineState *spapr, hwaddr pagesize, +void spapr_check_pagesize(SpaprMachineState *spapr, hwaddr pagesize, Error **errp); +/* + * XIVE definitions + */ +#define SPAPR_OV5_XIVE_LEGACY 0x0 +#define SPAPR_OV5_XIVE_EXPLOIT 0x40 +#define SPAPR_OV5_XIVE_BOTH 0x80 /* Only to advertise on the platform */ +void spapr_set_all_lpcrs(target_ulong value, target_ulong mask); #endif /* HW_SPAPR_H */ diff --git a/include/hw/ppc/spapr_cpu_core.h b/include/hw/ppc/spapr_cpu_core.h index 9e2821e4b3..f9645a7290 100644 --- a/include/hw/ppc/spapr_cpu_core.h +++ b/include/hw/ppc/spapr_cpu_core.h @@ -16,41 +16,43 @@ #define TYPE_SPAPR_CPU_CORE "spapr-cpu-core" #define SPAPR_CPU_CORE(obj) \ - OBJECT_CHECK(sPAPRCPUCore, (obj), TYPE_SPAPR_CPU_CORE) + OBJECT_CHECK(SpaprCpuCore, (obj), TYPE_SPAPR_CPU_CORE) #define SPAPR_CPU_CORE_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRCPUCoreClass, (klass), TYPE_SPAPR_CPU_CORE) + OBJECT_CLASS_CHECK(SpaprCpuCoreClass, (klass), TYPE_SPAPR_CPU_CORE) #define SPAPR_CPU_CORE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRCPUCoreClass, (obj), TYPE_SPAPR_CPU_CORE) + OBJECT_GET_CLASS(SpaprCpuCoreClass, (obj), TYPE_SPAPR_CPU_CORE) #define SPAPR_CPU_CORE_TYPE_NAME(model) model "-" TYPE_SPAPR_CPU_CORE -typedef struct sPAPRCPUCore { +typedef struct SpaprCpuCore { /*< private >*/ CPUCore parent_obj; /*< public >*/ PowerPCCPU **threads; int node_id; - bool pre_3_0_migration; /* older machine don't know about sPAPRCPUState */ -} sPAPRCPUCore; + bool pre_3_0_migration; /* older machine don't know about SpaprCpuState */ +} SpaprCpuCore; -typedef struct sPAPRCPUCoreClass { +typedef struct SpaprCpuCoreClass { DeviceClass parent_class; const char *cpu_type; -} sPAPRCPUCoreClass; +} SpaprCpuCoreClass; const char *spapr_get_cpu_core_type(const char *cpu_type); void spapr_cpu_set_entry_state(PowerPCCPU *cpu, target_ulong nip, target_ulong r3); -typedef struct sPAPRCPUState { +typedef struct SpaprCpuState { uint64_t vpa_addr; uint64_t slb_shadow_addr, slb_shadow_size; uint64_t dtl_addr, dtl_size; -} sPAPRCPUState; + struct ICPState *icp; + struct XiveTCTX *tctx; +} SpaprCpuState; -static inline sPAPRCPUState *spapr_cpu_state(PowerPCCPU *cpu) +static inline SpaprCpuState *spapr_cpu_state(PowerPCCPU *cpu) { - return (sPAPRCPUState *)cpu->machine_data; + return (SpaprCpuState *)cpu->machine_data; } #endif diff --git a/include/hw/ppc/spapr_drc.h b/include/hw/ppc/spapr_drc.h index f6ff32e7e2..fad0a887f9 100644 --- a/include/hw/ppc/spapr_drc.h +++ b/include/hw/ppc/spapr_drc.h @@ -18,60 +18,69 @@ #include "qom/object.h" #include "sysemu/sysemu.h" #include "hw/qdev.h" +#include "qapi/error.h" #define TYPE_SPAPR_DR_CONNECTOR "spapr-dr-connector" #define SPAPR_DR_CONNECTOR_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DR_CONNECTOR) + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DR_CONNECTOR) #define SPAPR_DR_CONNECTOR_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, \ TYPE_SPAPR_DR_CONNECTOR) -#define SPAPR_DR_CONNECTOR(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ +#define SPAPR_DR_CONNECTOR(obj) OBJECT_CHECK(SpaprDrc, (obj), \ TYPE_SPAPR_DR_CONNECTOR) #define TYPE_SPAPR_DRC_PHYSICAL "spapr-drc-physical" #define SPAPR_DRC_PHYSICAL_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_PHYSICAL) + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_PHYSICAL) #define SPAPR_DRC_PHYSICAL_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, \ TYPE_SPAPR_DRC_PHYSICAL) -#define SPAPR_DRC_PHYSICAL(obj) OBJECT_CHECK(sPAPRDRCPhysical, (obj), \ +#define SPAPR_DRC_PHYSICAL(obj) OBJECT_CHECK(SpaprDrcPhysical, (obj), \ TYPE_SPAPR_DRC_PHYSICAL) #define TYPE_SPAPR_DRC_LOGICAL "spapr-drc-logical" #define SPAPR_DRC_LOGICAL_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_LOGICAL) + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_LOGICAL) #define SPAPR_DRC_LOGICAL_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, \ TYPE_SPAPR_DRC_LOGICAL) -#define SPAPR_DRC_LOGICAL(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ +#define SPAPR_DRC_LOGICAL(obj) OBJECT_CHECK(SpaprDrc, (obj), \ TYPE_SPAPR_DRC_LOGICAL) #define TYPE_SPAPR_DRC_CPU "spapr-drc-cpu" #define SPAPR_DRC_CPU_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_CPU) + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_CPU) #define SPAPR_DRC_CPU_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, TYPE_SPAPR_DRC_CPU) -#define SPAPR_DRC_CPU(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, TYPE_SPAPR_DRC_CPU) +#define SPAPR_DRC_CPU(obj) OBJECT_CHECK(SpaprDrc, (obj), \ TYPE_SPAPR_DRC_CPU) #define TYPE_SPAPR_DRC_PCI "spapr-drc-pci" #define SPAPR_DRC_PCI_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_PCI) + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_PCI) #define SPAPR_DRC_PCI_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, TYPE_SPAPR_DRC_PCI) -#define SPAPR_DRC_PCI(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, TYPE_SPAPR_DRC_PCI) +#define SPAPR_DRC_PCI(obj) OBJECT_CHECK(SpaprDrc, (obj), \ TYPE_SPAPR_DRC_PCI) #define TYPE_SPAPR_DRC_LMB "spapr-drc-lmb" #define SPAPR_DRC_LMB_GET_CLASS(obj) \ - OBJECT_GET_CLASS(sPAPRDRConnectorClass, obj, TYPE_SPAPR_DRC_LMB) + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_LMB) #define SPAPR_DRC_LMB_CLASS(klass) \ - OBJECT_CLASS_CHECK(sPAPRDRConnectorClass, klass, TYPE_SPAPR_DRC_LMB) -#define SPAPR_DRC_LMB(obj) OBJECT_CHECK(sPAPRDRConnector, (obj), \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, TYPE_SPAPR_DRC_LMB) +#define SPAPR_DRC_LMB(obj) OBJECT_CHECK(SpaprDrc, (obj), \ TYPE_SPAPR_DRC_LMB) +#define TYPE_SPAPR_DRC_PHB "spapr-drc-phb" +#define SPAPR_DRC_PHB_GET_CLASS(obj) \ + OBJECT_GET_CLASS(SpaprDrcClass, obj, TYPE_SPAPR_DRC_PHB) +#define SPAPR_DRC_PHB_CLASS(klass) \ + OBJECT_CLASS_CHECK(SpaprDrcClass, klass, TYPE_SPAPR_DRC_PHB) +#define SPAPR_DRC_PHB(obj) OBJECT_CHECK(SpaprDrc, (obj), \ + TYPE_SPAPR_DRC_PHB) + /* - * Various hotplug types managed by sPAPRDRConnector + * Various hotplug types managed by SpaprDrc * * these are somewhat arbitrary, but to make things easier * when generating DRC indexes later we've aligned the bit @@ -87,7 +96,7 @@ typedef enum { SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO = 3, SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI = 4, SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB = 8, -} sPAPRDRConnectorTypeShift; +} SpaprDrcTypeShift; typedef enum { SPAPR_DR_CONNECTOR_TYPE_ANY = ~0, @@ -96,7 +105,7 @@ typedef enum { SPAPR_DR_CONNECTOR_TYPE_VIO = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_VIO, SPAPR_DR_CONNECTOR_TYPE_PCI = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_PCI, SPAPR_DR_CONNECTOR_TYPE_LMB = 1 << SPAPR_DR_CONNECTOR_TYPE_SHIFT_LMB, -} sPAPRDRConnectorType; +} SpaprDrcType; /* * set via set-indicator RTAS calls @@ -108,7 +117,7 @@ typedef enum { typedef enum { SPAPR_DR_ISOLATION_STATE_ISOLATED = 0, SPAPR_DR_ISOLATION_STATE_UNISOLATED = 1 -} sPAPRDRIsolationState; +} SpaprDRIsolationState; /* * set via set-indicator RTAS calls @@ -124,7 +133,7 @@ typedef enum { SPAPR_DR_ALLOCATION_STATE_USABLE = 1, SPAPR_DR_ALLOCATION_STATE_EXCHANGE = 2, SPAPR_DR_ALLOCATION_STATE_RECOVER = 3 -} sPAPRDRAllocationState; +} SpaprDRAllocationState; /* * DR-indicator (LED/visual indicator) @@ -143,7 +152,7 @@ typedef enum { SPAPR_DR_INDICATOR_ACTIVE = 1, SPAPR_DR_INDICATOR_IDENTIFY = 2, SPAPR_DR_INDICATOR_ACTION = 3, -} sPAPRDRIndicatorState; +} SpaprDRIndicatorState; /* * returned via get-sensor-state RTAS calls @@ -161,7 +170,7 @@ typedef enum { SPAPR_DR_ENTITY_SENSE_UNUSABLE = 2, SPAPR_DR_ENTITY_SENSE_EXCHANGE = 3, SPAPR_DR_ENTITY_SENSE_RECOVER = 4, -} sPAPRDREntitySense; +} SpaprDREntitySense; typedef enum { SPAPR_DR_CC_RESPONSE_NEXT_SIB = 1, /* currently unused */ @@ -172,7 +181,7 @@ typedef enum { SPAPR_DR_CC_RESPONSE_ERROR = -1, SPAPR_DR_CC_RESPONSE_CONTINUE = -2, SPAPR_DR_CC_RESPONSE_NOT_CONFIGURABLE = -9003, -} sPAPRDRCCResponse; +} SpaprDRCCResponse; typedef enum { /* @@ -190,9 +199,9 @@ typedef enum { SPAPR_DRC_STATE_PHYSICAL_POWERON = 6, SPAPR_DRC_STATE_PHYSICAL_UNISOLATE = 7, SPAPR_DRC_STATE_PHYSICAL_CONFIGURED = 8, -} sPAPRDRCState; +} SpaprDrcState; -typedef struct sPAPRDRConnector { +typedef struct SpaprDrc { /*< private >*/ DeviceState parent; @@ -211,56 +220,60 @@ typedef struct sPAPRDRConnector { bool unplug_requested; void *fdt; int fdt_start_offset; -} sPAPRDRConnector; +} SpaprDrc; -typedef struct sPAPRDRConnectorClass { +struct SpaprMachineState; + +typedef struct SpaprDrcClass { /*< private >*/ DeviceClass parent; - sPAPRDRCState empty_state; - sPAPRDRCState ready_state; + SpaprDrcState empty_state; + SpaprDrcState ready_state; /*< public >*/ - sPAPRDRConnectorTypeShift typeshift; + SpaprDrcTypeShift typeshift; const char *typename; /* used in device tree, PAPR 13.5.2.6 & C.6.1 */ const char *drc_name_prefix; /* used other places in device tree */ - sPAPRDREntitySense (*dr_entity_sense)(sPAPRDRConnector *drc); - uint32_t (*isolate)(sPAPRDRConnector *drc); - uint32_t (*unisolate)(sPAPRDRConnector *drc); + SpaprDREntitySense (*dr_entity_sense)(SpaprDrc *drc); + uint32_t (*isolate)(SpaprDrc *drc); + uint32_t (*unisolate)(SpaprDrc *drc); void (*release)(DeviceState *dev); -} sPAPRDRConnectorClass; -typedef struct sPAPRDRCPhysical { + int (*dt_populate)(SpaprDrc *drc, struct SpaprMachineState *spapr, + void *fdt, int *fdt_start_offset, Error **errp); +} SpaprDrcClass; + +typedef struct SpaprDrcPhysical { /*< private >*/ - sPAPRDRConnector parent; + SpaprDrc parent; /* DR-indicator */ uint32_t dr_indicator; -} sPAPRDRCPhysical; +} SpaprDrcPhysical; static inline bool spapr_drc_hotplugged(DeviceState *dev) { return dev->hotplugged && !runstate_check(RUN_STATE_INMIGRATE); } -void spapr_drc_reset(sPAPRDRConnector *drc); +void spapr_drc_reset(SpaprDrc *drc); -uint32_t spapr_drc_index(sPAPRDRConnector *drc); -sPAPRDRConnectorType spapr_drc_type(sPAPRDRConnector *drc); +uint32_t spapr_drc_index(SpaprDrc *drc); +SpaprDrcType spapr_drc_type(SpaprDrc *drc); -sPAPRDRConnector *spapr_dr_connector_new(Object *owner, const char *type, +SpaprDrc *spapr_dr_connector_new(Object *owner, const char *type, uint32_t id); -sPAPRDRConnector *spapr_drc_by_index(uint32_t index); -sPAPRDRConnector *spapr_drc_by_id(const char *type, uint32_t id); +SpaprDrc *spapr_drc_by_index(uint32_t index); +SpaprDrc *spapr_drc_by_id(const char *type, uint32_t id); int spapr_drc_populate_dt(void *fdt, int fdt_offset, Object *owner, uint32_t drc_type_mask); -void spapr_drc_attach(sPAPRDRConnector *drc, DeviceState *d, void *fdt, - int fdt_start_offset, Error **errp); -void spapr_drc_detach(sPAPRDRConnector *drc); +void spapr_drc_attach(SpaprDrc *drc, DeviceState *d, Error **errp); +void spapr_drc_detach(SpaprDrc *drc); bool spapr_drc_needed(void *opaque); -static inline bool spapr_drc_unplug_requested(sPAPRDRConnector *drc) +static inline bool spapr_drc_unplug_requested(SpaprDrc *drc) { return drc->unplug_requested; } diff --git a/include/hw/ppc/spapr_irq.h b/include/hw/ppc/spapr_irq.h new file mode 100644 index 0000000000..b855f74e44 --- /dev/null +++ b/include/hw/ppc/spapr_irq.h @@ -0,0 +1,72 @@ +/* + * QEMU PowerPC sPAPR IRQ backend definitions + * + * Copyright (c) 2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef HW_SPAPR_IRQ_H +#define HW_SPAPR_IRQ_H + +/* + * IRQ range offsets per device type + */ +#define SPAPR_IRQ_IPI 0x0 +#define SPAPR_IRQ_EPOW 0x1000 /* XICS_IRQ_BASE offset */ +#define SPAPR_IRQ_HOTPLUG 0x1001 +#define SPAPR_IRQ_VIO 0x1100 /* 256 VIO devices */ +#define SPAPR_IRQ_PCI_LSI 0x1200 /* 32+ PHBs devices */ + +#define SPAPR_IRQ_MSI 0x1300 /* Offset of the dynamic range covered + * by the bitmap allocator */ + +typedef struct SpaprMachineState SpaprMachineState; + +void spapr_irq_msi_init(SpaprMachineState *spapr, uint32_t nr_msis); +int spapr_irq_msi_alloc(SpaprMachineState *spapr, uint32_t num, bool align, + Error **errp); +void spapr_irq_msi_free(SpaprMachineState *spapr, int irq, uint32_t num); +void spapr_irq_msi_reset(SpaprMachineState *spapr); + +typedef struct SpaprIrq { + uint32_t nr_irqs; + uint32_t nr_msis; + uint8_t ov5; + + void (*init)(SpaprMachineState *spapr, int nr_irqs, Error **errp); + int (*claim)(SpaprMachineState *spapr, int irq, bool lsi, Error **errp); + void (*free)(SpaprMachineState *spapr, int irq, int num); + qemu_irq (*qirq)(SpaprMachineState *spapr, int irq); + void (*print_info)(SpaprMachineState *spapr, Monitor *mon); + void (*dt_populate)(SpaprMachineState *spapr, uint32_t nr_servers, + void *fdt, uint32_t phandle); + void (*cpu_intc_create)(SpaprMachineState *spapr, PowerPCCPU *cpu, + Error **errp); + int (*post_load)(SpaprMachineState *spapr, int version_id); + void (*reset)(SpaprMachineState *spapr, Error **errp); + void (*set_irq)(void *opaque, int srcno, int val); + const char *(*get_nodename)(SpaprMachineState *spapr); +} SpaprIrq; + +extern SpaprIrq spapr_irq_xics; +extern SpaprIrq spapr_irq_xics_legacy; +extern SpaprIrq spapr_irq_xive; +extern SpaprIrq spapr_irq_dual; + +void spapr_irq_init(SpaprMachineState *spapr, Error **errp); +int spapr_irq_claim(SpaprMachineState *spapr, int irq, bool lsi, Error **errp); +void spapr_irq_free(SpaprMachineState *spapr, int irq, int num); +qemu_irq spapr_qirq(SpaprMachineState *spapr, int irq); +int spapr_irq_post_load(SpaprMachineState *spapr, int version_id); +void spapr_irq_reset(SpaprMachineState *spapr, Error **errp); +int spapr_irq_get_phandle(SpaprMachineState *spapr, void *fdt, Error **errp); + +/* + * XICS legacy routines + */ +int spapr_irq_find(SpaprMachineState *spapr, int num, bool align, Error **errp); +#define spapr_irq_findone(spapr, errp) spapr_irq_find(spapr, 1, false, errp) + +#endif diff --git a/include/hw/ppc/spapr_ovec.h b/include/hw/ppc/spapr_ovec.h index 0f2d8d715d..188a9367e2 100644 --- a/include/hw/ppc/spapr_ovec.h +++ b/include/hw/ppc/spapr_ovec.h @@ -39,7 +39,7 @@ #include "cpu.h" #include "migration/vmstate.h" -typedef struct sPAPROptionVector sPAPROptionVector; +typedef struct SpaprOptionVector SpaprOptionVector; #define OV_BIT(byte, bit) ((byte - 1) * BITS_PER_BYTE + bit) @@ -61,21 +61,21 @@ typedef struct sPAPROptionVector sPAPROptionVector; #define OV5_MMU_RADIX_GTSE OV_BIT(26, 1) /* Radix GTSE */ /* interfaces */ -sPAPROptionVector *spapr_ovec_new(void); -sPAPROptionVector *spapr_ovec_clone(sPAPROptionVector *ov_orig); -void spapr_ovec_intersect(sPAPROptionVector *ov, - sPAPROptionVector *ov1, - sPAPROptionVector *ov2); -bool spapr_ovec_diff(sPAPROptionVector *ov, - sPAPROptionVector *ov_old, - sPAPROptionVector *ov_new); -void spapr_ovec_cleanup(sPAPROptionVector *ov); -void spapr_ovec_set(sPAPROptionVector *ov, long bitnr); -void spapr_ovec_clear(sPAPROptionVector *ov, long bitnr); -bool spapr_ovec_test(sPAPROptionVector *ov, long bitnr); -sPAPROptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); +SpaprOptionVector *spapr_ovec_new(void); +SpaprOptionVector *spapr_ovec_clone(SpaprOptionVector *ov_orig); +void spapr_ovec_intersect(SpaprOptionVector *ov, + SpaprOptionVector *ov1, + SpaprOptionVector *ov2); +bool spapr_ovec_diff(SpaprOptionVector *ov, + SpaprOptionVector *ov_old, + SpaprOptionVector *ov_new); +void spapr_ovec_cleanup(SpaprOptionVector *ov); +void spapr_ovec_set(SpaprOptionVector *ov, long bitnr); +void spapr_ovec_clear(SpaprOptionVector *ov, long bitnr); +bool spapr_ovec_test(SpaprOptionVector *ov, long bitnr); +SpaprOptionVector *spapr_ovec_parse_vector(target_ulong table_addr, int vector); int spapr_ovec_populate_dt(void *fdt, int fdt_offset, - sPAPROptionVector *ov, const char *name); + SpaprOptionVector *ov, const char *name); /* migration */ extern const VMStateDescription vmstate_spapr_ovec; diff --git a/include/hw/ppc/spapr_vio.h b/include/hw/ppc/spapr_vio.h index e8b006d18f..04609f214e 100644 --- a/include/hw/ppc/spapr_vio.h +++ b/include/hw/ppc/spapr_vio.h @@ -26,91 +26,91 @@ #define TYPE_VIO_SPAPR_DEVICE "vio-spapr-device" #define VIO_SPAPR_DEVICE(obj) \ - OBJECT_CHECK(VIOsPAPRDevice, (obj), TYPE_VIO_SPAPR_DEVICE) + OBJECT_CHECK(SpaprVioDevice, (obj), TYPE_VIO_SPAPR_DEVICE) #define VIO_SPAPR_DEVICE_CLASS(klass) \ - OBJECT_CLASS_CHECK(VIOsPAPRDeviceClass, (klass), TYPE_VIO_SPAPR_DEVICE) + OBJECT_CLASS_CHECK(SpaprVioDeviceClass, (klass), TYPE_VIO_SPAPR_DEVICE) #define VIO_SPAPR_DEVICE_GET_CLASS(obj) \ - OBJECT_GET_CLASS(VIOsPAPRDeviceClass, (obj), TYPE_VIO_SPAPR_DEVICE) + OBJECT_GET_CLASS(SpaprVioDeviceClass, (obj), TYPE_VIO_SPAPR_DEVICE) #define TYPE_SPAPR_VIO_BUS "spapr-vio-bus" -#define SPAPR_VIO_BUS(obj) OBJECT_CHECK(VIOsPAPRBus, (obj), TYPE_SPAPR_VIO_BUS) +#define SPAPR_VIO_BUS(obj) OBJECT_CHECK(SpaprVioBus, (obj), TYPE_SPAPR_VIO_BUS) #define TYPE_SPAPR_VIO_BRIDGE "spapr-vio-bridge" -typedef struct VIOsPAPR_CRQ { +typedef struct SpaprVioCrq { uint64_t qladdr; uint32_t qsize; uint32_t qnext; - int(*SendFunc)(struct VIOsPAPRDevice *vdev, uint8_t *crq); -} VIOsPAPR_CRQ; + int(*SendFunc)(struct SpaprVioDevice *vdev, uint8_t *crq); +} SpaprVioCrq; -typedef struct VIOsPAPRDevice VIOsPAPRDevice; -typedef struct VIOsPAPRBus VIOsPAPRBus; +typedef struct SpaprVioDevice SpaprVioDevice; +typedef struct SpaprVioBus SpaprVioBus; -typedef struct VIOsPAPRDeviceClass { +typedef struct SpaprVioDeviceClass { DeviceClass parent_class; const char *dt_name, *dt_type, *dt_compatible; target_ulong signal_mask; uint32_t rtce_window_size; - void (*realize)(VIOsPAPRDevice *dev, Error **errp); - void (*reset)(VIOsPAPRDevice *dev); - int (*devnode)(VIOsPAPRDevice *dev, void *fdt, int node_off); -} VIOsPAPRDeviceClass; + void (*realize)(SpaprVioDevice *dev, Error **errp); + void (*reset)(SpaprVioDevice *dev); + int (*devnode)(SpaprVioDevice *dev, void *fdt, int node_off); +} SpaprVioDeviceClass; -struct VIOsPAPRDevice { +struct SpaprVioDevice { DeviceState qdev; uint32_t reg; uint32_t irq; uint64_t signal_state; - VIOsPAPR_CRQ crq; + SpaprVioCrq crq; AddressSpace as; MemoryRegion mrroot; MemoryRegion mrbypass; - sPAPRTCETable *tcet; + SpaprTceTable *tcet; }; #define DEFINE_SPAPR_PROPERTIES(type, field) \ DEFINE_PROP_UINT32("reg", type, field.reg, -1) -struct VIOsPAPRBus { +struct SpaprVioBus { BusState bus; uint32_t next_reg; }; -extern VIOsPAPRBus *spapr_vio_bus_init(void); -extern VIOsPAPRDevice *spapr_vio_find_by_reg(VIOsPAPRBus *bus, uint32_t reg); -void spapr_dt_vdevice(VIOsPAPRBus *bus, void *fdt); -extern gchar *spapr_vio_stdout_path(VIOsPAPRBus *bus); +extern SpaprVioBus *spapr_vio_bus_init(void); +extern SpaprVioDevice *spapr_vio_find_by_reg(SpaprVioBus *bus, uint32_t reg); +void spapr_dt_vdevice(SpaprVioBus *bus, void *fdt); +extern gchar *spapr_vio_stdout_path(SpaprVioBus *bus); -static inline qemu_irq spapr_vio_qirq(VIOsPAPRDevice *dev) +static inline qemu_irq spapr_vio_qirq(SpaprVioDevice *dev) { - sPAPRMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); + SpaprMachineState *spapr = SPAPR_MACHINE(qdev_get_machine()); return spapr_qirq(spapr, dev->irq); } -static inline bool spapr_vio_dma_valid(VIOsPAPRDevice *dev, uint64_t taddr, +static inline bool spapr_vio_dma_valid(SpaprVioDevice *dev, uint64_t taddr, uint32_t size, DMADirection dir) { return dma_memory_valid(&dev->as, taddr, size, dir); } -static inline int spapr_vio_dma_read(VIOsPAPRDevice *dev, uint64_t taddr, +static inline int spapr_vio_dma_read(SpaprVioDevice *dev, uint64_t taddr, void *buf, uint32_t size) { return (dma_memory_read(&dev->as, taddr, buf, size) != 0) ? H_DEST_PARM : H_SUCCESS; } -static inline int spapr_vio_dma_write(VIOsPAPRDevice *dev, uint64_t taddr, +static inline int spapr_vio_dma_write(SpaprVioDevice *dev, uint64_t taddr, const void *buf, uint32_t size) { return (dma_memory_write(&dev->as, taddr, buf, size) != 0) ? H_DEST_PARM : H_SUCCESS; } -static inline int spapr_vio_dma_set(VIOsPAPRDevice *dev, uint64_t taddr, +static inline int spapr_vio_dma_set(SpaprVioDevice *dev, uint64_t taddr, uint8_t c, uint32_t size) { return (dma_memory_set(&dev->as, taddr, c, size) != 0) ? @@ -123,21 +123,21 @@ static inline int spapr_vio_dma_set(VIOsPAPRDevice *dev, uint64_t taddr, #define vio_stq(_dev, _addr, _val) (stq_be_dma(&(_dev)->as, (_addr), (_val))) #define vio_ldq(_dev, _addr) (ldq_be_dma(&(_dev)->as, (_addr))) -int spapr_vio_send_crq(VIOsPAPRDevice *dev, uint8_t *crq); +int spapr_vio_send_crq(SpaprVioDevice *dev, uint8_t *crq); -VIOsPAPRDevice *vty_lookup(sPAPRMachineState *spapr, target_ulong reg); -void vty_putchars(VIOsPAPRDevice *sdev, uint8_t *buf, int len); -void spapr_vty_create(VIOsPAPRBus *bus, Chardev *chardev); -void spapr_vlan_create(VIOsPAPRBus *bus, NICInfo *nd); -void spapr_vscsi_create(VIOsPAPRBus *bus); +SpaprVioDevice *vty_lookup(SpaprMachineState *spapr, target_ulong reg); +void vty_putchars(SpaprVioDevice *sdev, uint8_t *buf, int len); +void spapr_vty_create(SpaprVioBus *bus, Chardev *chardev); +void spapr_vlan_create(SpaprVioBus *bus, NICInfo *nd); +void spapr_vscsi_create(SpaprVioBus *bus); -VIOsPAPRDevice *spapr_vty_get_default(VIOsPAPRBus *bus); +SpaprVioDevice *spapr_vty_get_default(SpaprVioBus *bus); extern const VMStateDescription vmstate_spapr_vio; #define VMSTATE_SPAPR_VIO(_f, _s) \ - VMSTATE_STRUCT(_f, _s, 0, vmstate_spapr_vio, VIOsPAPRDevice) + VMSTATE_STRUCT(_f, _s, 0, vmstate_spapr_vio, SpaprVioDevice) -void spapr_vio_set_bypass(VIOsPAPRDevice *dev, bool bypass); +void spapr_vio_set_bypass(SpaprVioDevice *dev, bool bypass); #endif /* HW_SPAPR_VIO_H */ diff --git a/include/hw/ppc/spapr_xive.h b/include/hw/ppc/spapr_xive.h new file mode 100644 index 0000000000..fc3e9652f9 --- /dev/null +++ b/include/hw/ppc/spapr_xive.h @@ -0,0 +1,53 @@ +/* + * QEMU PowerPC sPAPR XIVE interrupt controller model + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_SPAPR_XIVE_H +#define PPC_SPAPR_XIVE_H + +#include "hw/ppc/xive.h" + +#define TYPE_SPAPR_XIVE "spapr-xive" +#define SPAPR_XIVE(obj) OBJECT_CHECK(SpaprXive, (obj), TYPE_SPAPR_XIVE) + +typedef struct SpaprXive { + XiveRouter parent; + + /* Internal interrupt source for IPIs and virtual devices */ + XiveSource source; + hwaddr vc_base; + + /* END ESB MMIOs */ + XiveENDSource end_source; + hwaddr end_base; + + /* DT */ + gchar *nodename; + + /* Routing table */ + XiveEAS *eat; + uint32_t nr_irqs; + XiveEND *endt; + uint32_t nr_ends; + + /* TIMA mapping address */ + hwaddr tm_base; + MemoryRegion tm_mmio; +} SpaprXive; + +bool spapr_xive_irq_claim(SpaprXive *xive, uint32_t lisn, bool lsi); +bool spapr_xive_irq_free(SpaprXive *xive, uint32_t lisn); +void spapr_xive_pic_print_info(SpaprXive *xive, Monitor *mon); + +void spapr_xive_hcall_init(SpaprMachineState *spapr); +void spapr_dt_xive(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt, + uint32_t phandle); +void spapr_xive_set_tctx_os_cam(XiveTCTX *tctx); +void spapr_xive_mmio_set_enabled(SpaprXive *xive, bool enable); + +#endif /* PPC_SPAPR_XIVE_H */ diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index 6ac8a9392d..eb65ad7e43 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -29,7 +29,6 @@ #define XICS_H #include "hw/qdev.h" -#include "target/ppc/cpu-qom.h" #define XICS_IPI 0x2 #define XICS_BUID 0x1 @@ -51,9 +50,6 @@ typedef struct XICSFabric XICSFabric; #define TYPE_ICP "icp" #define ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_ICP) -#define TYPE_KVM_ICP "icp-kvm" -#define KVM_ICP(obj) OBJECT_CHECK(ICPState, (obj), TYPE_KVM_ICP) - #define TYPE_PNV_ICP "pnv-icp" #define PNV_ICP(obj) OBJECT_CHECK(PnvICPState, (obj), TYPE_PNV_ICP) @@ -66,11 +62,6 @@ struct ICPStateClass { DeviceClass parent_class; DeviceRealize parent_realize; - DeviceReset parent_reset; - - void (*pre_save)(ICPState *icp); - int (*post_load)(ICPState *icp, int version_id); - void (*synchronize_state)(ICPState *icp); }; struct ICPState { @@ -104,9 +95,6 @@ struct PnvICPState { #define TYPE_ICS_SIMPLE "ics" #define ICS_SIMPLE(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_SIMPLE) -#define TYPE_ICS_KVM "icskvm" -#define ICS_KVM(obj) OBJECT_CHECK(ICSState, (obj), TYPE_ICS_KVM) - #define ICS_BASE_CLASS(klass) \ OBJECT_CLASS_CHECK(ICSStateClass, (klass), TYPE_ICS_BASE) #define ICS_BASE_GET_CLASS(obj) \ @@ -118,12 +106,9 @@ struct ICSStateClass { DeviceRealize parent_realize; DeviceReset parent_reset; - void (*pre_save)(ICSState *s); - int (*post_load)(ICSState *s, int version_id); void (*reject)(ICSState *s, uint32_t irq); void (*resend)(ICSState *s); void (*eoi)(ICSState *s, uint32_t irq); - void (*synchronize_state)(ICSState *s); }; struct ICSState { @@ -132,7 +117,6 @@ struct ICSState { /*< public >*/ uint32_t nr_irqs; uint32_t offset; - qemu_irq *qirqs; ICSIRQState *irqs; XICSFabric *xics; }; @@ -141,8 +125,7 @@ struct ICSState { static inline bool ics_valid_irq(ICSState *ics, uint32_t nr) { - return (ics->offset != 0) && (nr >= ics->offset) - && (nr < (ics->offset + ics->nr_irqs)); + return (nr >= ics->offset) && (nr < (ics->offset + ics->nr_irqs)); } struct ICSIRQState { @@ -182,10 +165,6 @@ typedef struct XICSFabricClass { ICPState *(*icp_get)(XICSFabric *xi, int server); } XICSFabricClass; -#define XICS_IRQS_SPAPR 1024 - -void spapr_dt_xics(int nr_servers, void *fdt, uint32_t phandle); - ICPState *xics_icp_get(XICSFabric *xi, int server); /* Internal XICS interfaces */ @@ -197,6 +176,7 @@ void icp_eoi(ICPState *icp, uint32_t xirr); void ics_simple_write_xive(ICSState *ics, int nr, int server, uint8_t priority, uint8_t saved_priority); +void ics_simple_set_irq(void *opaque, int srcno, int val); void ics_set_irq_type(ICSState *ics, int srcno, bool lsi); void icp_pic_print_info(ICPState *icp, Monitor *mon); @@ -205,12 +185,19 @@ void ics_pic_print_info(ICSState *ics, Monitor *mon); void ics_resend(ICSState *ics); void icp_resend(ICPState *ss); -typedef struct sPAPRMachineState sPAPRMachineState; - -int xics_kvm_init(sPAPRMachineState *spapr, Error **errp); -void xics_spapr_init(sPAPRMachineState *spapr); - Object *icp_create(Object *cpu, const char *type, XICSFabric *xi, Error **errp); +/* KVM */ +void icp_get_kvm_state(ICPState *icp); +int icp_set_kvm_state(ICPState *icp); +void icp_synchronize_state(ICPState *icp); +void icp_kvm_realize(DeviceState *dev, Error **errp); + +void ics_get_kvm_state(ICSState *ics); +int ics_set_kvm_state_one(ICSState *ics, int srcno); +int ics_set_kvm_state(ICSState *ics); +void ics_synchronize_state(ICSState *ics); +void ics_kvm_set_irq(ICSState *ics, int srcno, int val); + #endif /* XICS_H */ diff --git a/include/qemu/acl.h b/include/hw/ppc/xics_spapr.h similarity index 54% rename from include/qemu/acl.h rename to include/hw/ppc/xics_spapr.h index 7c44119a47..15a8dcff66 100644 --- a/include/qemu/acl.h +++ b/include/hw/ppc/xics_spapr.h @@ -1,7 +1,9 @@ /* - * QEMU access control list management + * QEMU PowerPC pSeries Logical Partition (aka sPAPR) hardware System Emulator * - * Copyright (C) 2009 Red Hat, Inc + * PAPR Virtualized Interrupt System, aka ICS/ICP aka xics + * + * Copyright (c) 2010, 2011 David Gibson, IBM Corporation. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -22,45 +24,16 @@ * THE SOFTWARE. */ -#ifndef QEMU_ACL_H -#define QEMU_ACL_H +#ifndef XICS_SPAPR_H +#define XICS_SPAPR_H -#include "qemu/queue.h" +#include "hw/ppc/spapr.h" -typedef struct qemu_acl_entry qemu_acl_entry; -typedef struct qemu_acl qemu_acl; +#define XICS_NODENAME "interrupt-controller" -struct qemu_acl_entry { - char *match; - int deny; +void spapr_dt_xics(SpaprMachineState *spapr, uint32_t nr_servers, void *fdt, + uint32_t phandle); +int xics_kvm_init(SpaprMachineState *spapr, Error **errp); +void xics_spapr_init(SpaprMachineState *spapr); - QTAILQ_ENTRY(qemu_acl_entry) next; -}; - -struct qemu_acl { - char *aclname; - unsigned int nentries; - QTAILQ_HEAD(,qemu_acl_entry) entries; - int defaultDeny; -}; - -qemu_acl *qemu_acl_init(const char *aclname); - -qemu_acl *qemu_acl_find(const char *aclname); - -int qemu_acl_party_is_allowed(qemu_acl *acl, - const char *party); - -void qemu_acl_reset(qemu_acl *acl); - -int qemu_acl_append(qemu_acl *acl, - int deny, - const char *match); -int qemu_acl_insert(qemu_acl *acl, - int deny, - const char *match, - int index); -int qemu_acl_remove(qemu_acl *acl, - const char *match); - -#endif /* QEMU_ACL_H */ +#endif /* XICS_SPAPR_H */ diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h new file mode 100644 index 0000000000..c4f27742ca --- /dev/null +++ b/include/hw/ppc/xive.h @@ -0,0 +1,426 @@ +/* + * QEMU PowerPC XIVE interrupt controller model + * + * + * The POWER9 processor comes with a new interrupt controller, called + * XIVE as "eXternal Interrupt Virtualization Engine". + * + * = Overall architecture + * + * + * XIVE Interrupt Controller + * +------------------------------------+ IPIs + * | +---------+ +---------+ +--------+ | +-------+ + * | |VC | |CQ | |PC |----> | CORES | + * | | esb | | | | |----> | | + * | | eas | | Bridge | | tctx |----> | | + * | |SC end | | | | nvt | | | | + * +------+ | +---------+ +----+----+ +--------+ | +-+-+-+-+ + * | RAM | +------------------|-----------------+ | | | + * | | | | | | + * | | | | | | + * | | +--------------------v------------------------v-v-v--+ other + * | <--+ Power Bus +--> chips + * | esb | +---------+-----------------------+------------------+ + * | eas | | | + * | end | +--|------+ | + * | nvt | +----+----+ | +----+----+ + * +------+ |SC | | |SC | + * | | | | | + * | PQ-bits | | | PQ-bits | + * | local |-+ | in VC | + * +---------+ +---------+ + * PCIe NX,NPU,CAPI + * + * SC: Source Controller (aka. IVSE) + * VC: Virtualization Controller (aka. IVRE) + * PC: Presentation Controller (aka. IVPE) + * CQ: Common Queue (Bridge) + * + * PQ-bits: 2 bits source state machine (P:pending Q:queued) + * esb: Event State Buffer (Array of PQ bits in an IVSE) + * eas: Event Assignment Structure + * end: Event Notification Descriptor + * nvt: Notification Virtual Target + * tctx: Thread interrupt Context + * + * + * The XIVE IC is composed of three sub-engines : + * + * - Interrupt Virtualization Source Engine (IVSE), or Source + * Controller (SC). These are found in PCI PHBs, in the PSI host + * bridge controller, but also inside the main controller for the + * core IPIs and other sub-chips (NX, CAP, NPU) of the + * chip/processor. They are configured to feed the IVRE with events. + * + * - Interrupt Virtualization Routing Engine (IVRE) or Virtualization + * Controller (VC). Its job is to match an event source with an + * Event Notification Descriptor (END). + * + * - Interrupt Virtualization Presentation Engine (IVPE) or + * Presentation Controller (PC). It maintains the interrupt context + * state of each thread and handles the delivery of the external + * exception to the thread. + * + * In XIVE 1.0, the sub-engines used to be referred as: + * + * SC Source Controller + * VC Virtualization Controller + * PC Presentation Controller + * CQ Common Queue (PowerBUS Bridge) + * + * + * = XIVE internal tables + * + * Each of the sub-engines uses a set of tables to redirect exceptions + * from event sources to CPU threads. + * + * +-------+ + * User or OS | EQ | + * or +------>|entries| + * Hypervisor | | .. | + * Memory | +-------+ + * | ^ + * | | + * +-------------------------------------------------+ + * | | + * Hypervisor +------+ +---+--+ +---+--+ +------+ + * Memory | ESB | | EAT | | ENDT | | NVTT | + * (skiboot) +----+-+ +----+-+ +----+-+ +------+ + * ^ | ^ | ^ | ^ + * | | | | | | | + * +-------------------------------------------------+ + * | | | | | | | + * | | | | | | | + * +----|--|--------|--|--------|--|-+ +-|-----+ +------+ + * | | | | | | | | | | tctx| |Thread| + * IPI or --> | + v + v + v |---| + .. |-----> | + * HW events --> | | | | | | + * IVSE | IVRE | | IVPE | +------+ + * +---------------------------------+ +-------+ + * + * + * + * The IVSE have a 2-bits state machine, P for pending and Q for queued, + * for each source that allows events to be triggered. They are stored in + * an Event State Buffer (ESB) array and can be controlled by MMIOs. + * + * If the event is let through, the IVRE looks up in the Event Assignment + * Structure (EAS) table for an Event Notification Descriptor (END) + * configured for the source. Each Event Notification Descriptor defines + * a notification path to a CPU and an in-memory Event Queue, in which + * will be enqueued an EQ data for the OS to pull. + * + * The IVPE determines if a Notification Virtual Target (NVT) can + * handle the event by scanning the thread contexts of the VCPUs + * dispatched on the processor HW threads. It maintains the state of + * the thread interrupt context (TCTX) of each thread in a NVT table. + * + * = Acronyms + * + * Description In XIVE 1.0, used to be referred as + * + * EAS Event Assignment Structure IVE Interrupt Virt. Entry + * EAT Event Assignment Table IVT Interrupt Virt. Table + * ENDT Event Notif. Descriptor Table EQDT Event Queue Desc. Table + * EQ Event Queue same + * ESB Event State Buffer SBE State Bit Entry + * NVT Notif. Virtual Target VPD Virtual Processor Desc. + * NVTT Notif. Virtual Target Table VPDT Virtual Processor Desc. Table + * TCTX Thread interrupt Context + * + * + * Copyright (c) 2017-2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + * + */ + +#ifndef PPC_XIVE_H +#define PPC_XIVE_H + +#include "hw/qdev-core.h" +#include "hw/sysbus.h" +#include "hw/ppc/xive_regs.h" + +/* + * XIVE Notifier (Interface between Source and Router) + */ + +typedef struct XiveNotifier { + Object parent; +} XiveNotifier; + +#define TYPE_XIVE_NOTIFIER "xive-notifier" +#define XIVE_NOTIFIER(obj) \ + OBJECT_CHECK(XiveNotifier, (obj), TYPE_XIVE_NOTIFIER) +#define XIVE_NOTIFIER_CLASS(klass) \ + OBJECT_CLASS_CHECK(XiveNotifierClass, (klass), TYPE_XIVE_NOTIFIER) +#define XIVE_NOTIFIER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XiveNotifierClass, (obj), TYPE_XIVE_NOTIFIER) + +typedef struct XiveNotifierClass { + InterfaceClass parent; + void (*notify)(XiveNotifier *xn, uint32_t lisn); +} XiveNotifierClass; + +/* + * XIVE Interrupt Source + */ + +#define TYPE_XIVE_SOURCE "xive-source" +#define XIVE_SOURCE(obj) OBJECT_CHECK(XiveSource, (obj), TYPE_XIVE_SOURCE) + +/* + * XIVE Interrupt Source characteristics, which define how the ESB are + * controlled. + */ +#define XIVE_SRC_H_INT_ESB 0x1 /* ESB managed with hcall H_INT_ESB */ +#define XIVE_SRC_STORE_EOI 0x2 /* Store EOI supported */ + +typedef struct XiveSource { + DeviceState parent; + + /* IRQs */ + uint32_t nr_irqs; + unsigned long *lsi_map; + + /* PQ bits and LSI assertion bit */ + uint8_t *status; + + /* ESB memory region */ + uint64_t esb_flags; + uint32_t esb_shift; + MemoryRegion esb_mmio; + + XiveNotifier *xive; +} XiveSource; + +/* + * ESB MMIO setting. Can be one page, for both source triggering and + * source management, or two different pages. See below for magic + * values. + */ +#define XIVE_ESB_4K 12 /* PSI HB only */ +#define XIVE_ESB_4K_2PAGE 13 +#define XIVE_ESB_64K 16 +#define XIVE_ESB_64K_2PAGE 17 + +static inline bool xive_source_esb_has_2page(XiveSource *xsrc) +{ + return xsrc->esb_shift == XIVE_ESB_64K_2PAGE || + xsrc->esb_shift == XIVE_ESB_4K_2PAGE; +} + +/* The trigger page is always the first/even page */ +static inline hwaddr xive_source_esb_page(XiveSource *xsrc, uint32_t srcno) +{ + assert(srcno < xsrc->nr_irqs); + return (1ull << xsrc->esb_shift) * srcno; +} + +/* In a two pages ESB MMIO setting, the odd page is for management */ +static inline hwaddr xive_source_esb_mgmt(XiveSource *xsrc, int srcno) +{ + hwaddr addr = xive_source_esb_page(xsrc, srcno); + + if (xive_source_esb_has_2page(xsrc)) { + addr += (1 << (xsrc->esb_shift - 1)); + } + + return addr; +} + +/* + * Each interrupt source has a 2-bit state machine which can be + * controlled by MMIO. P indicates that an interrupt is pending (has + * been sent to a queue and is waiting for an EOI). Q indicates that + * the interrupt has been triggered while pending. + * + * This acts as a coalescing mechanism in order to guarantee that a + * given interrupt only occurs at most once in a queue. + * + * When doing an EOI, the Q bit will indicate if the interrupt + * needs to be re-triggered. + */ +#define XIVE_STATUS_ASSERTED 0x4 /* Extra bit for LSI */ +#define XIVE_ESB_VAL_P 0x2 +#define XIVE_ESB_VAL_Q 0x1 + +#define XIVE_ESB_RESET 0x0 +#define XIVE_ESB_PENDING XIVE_ESB_VAL_P +#define XIVE_ESB_QUEUED (XIVE_ESB_VAL_P | XIVE_ESB_VAL_Q) +#define XIVE_ESB_OFF XIVE_ESB_VAL_Q + +/* + * "magic" Event State Buffer (ESB) MMIO offsets. + * + * The following offsets into the ESB MMIO allow to read or manipulate + * the PQ bits. They must be used with an 8-byte load instruction. + * They all return the previous state of the interrupt (atomically). + * + * Additionally, some ESB pages support doing an EOI via a store and + * some ESBs support doing a trigger via a separate trigger page. + */ +#define XIVE_ESB_STORE_EOI 0x400 /* Store */ +#define XIVE_ESB_LOAD_EOI 0x000 /* Load */ +#define XIVE_ESB_GET 0x800 /* Load */ +#define XIVE_ESB_SET_PQ_00 0xc00 /* Load */ +#define XIVE_ESB_SET_PQ_01 0xd00 /* Load */ +#define XIVE_ESB_SET_PQ_10 0xe00 /* Load */ +#define XIVE_ESB_SET_PQ_11 0xf00 /* Load */ + +uint8_t xive_source_esb_get(XiveSource *xsrc, uint32_t srcno); +uint8_t xive_source_esb_set(XiveSource *xsrc, uint32_t srcno, uint8_t pq); + +void xive_source_pic_print_info(XiveSource *xsrc, uint32_t offset, + Monitor *mon); + +static inline bool xive_source_irq_is_lsi(XiveSource *xsrc, uint32_t srcno) +{ + assert(srcno < xsrc->nr_irqs); + return test_bit(srcno, xsrc->lsi_map); +} + +static inline void xive_source_irq_set_lsi(XiveSource *xsrc, uint32_t srcno) +{ + assert(srcno < xsrc->nr_irqs); + bitmap_set(xsrc->lsi_map, srcno, 1); +} + +void xive_source_set_irq(void *opaque, int srcno, int val); + +/* + * XIVE Thread interrupt Management (TM) context + */ + +#define TYPE_XIVE_TCTX "xive-tctx" +#define XIVE_TCTX(obj) OBJECT_CHECK(XiveTCTX, (obj), TYPE_XIVE_TCTX) + +/* + * XIVE Thread interrupt Management register rings : + * + * QW-0 User event-based exception state + * QW-1 O/S OS context for priority management, interrupt acks + * QW-2 Pool hypervisor pool context for virtual processors dispatched + * QW-3 Physical physical thread context and security context + */ +#define XIVE_TM_RING_COUNT 4 +#define XIVE_TM_RING_SIZE 0x10 + +typedef struct XiveTCTX { + DeviceState parent_obj; + + CPUState *cs; + qemu_irq output; + + uint8_t regs[XIVE_TM_RING_COUNT * XIVE_TM_RING_SIZE]; +} XiveTCTX; + +/* + * XIVE Router + */ + +typedef struct XiveRouter { + SysBusDevice parent; +} XiveRouter; + +#define TYPE_XIVE_ROUTER "xive-router" +#define XIVE_ROUTER(obj) \ + OBJECT_CHECK(XiveRouter, (obj), TYPE_XIVE_ROUTER) +#define XIVE_ROUTER_CLASS(klass) \ + OBJECT_CLASS_CHECK(XiveRouterClass, (klass), TYPE_XIVE_ROUTER) +#define XIVE_ROUTER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XiveRouterClass, (obj), TYPE_XIVE_ROUTER) + +typedef struct XiveRouterClass { + SysBusDeviceClass parent; + + /* XIVE table accessors */ + int (*get_eas)(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + XiveEAS *eas); + int (*get_end)(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, + XiveEND *end); + int (*write_end)(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, + XiveEND *end, uint8_t word_number); + int (*get_nvt)(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, + XiveNVT *nvt); + int (*write_nvt)(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, + XiveNVT *nvt, uint8_t word_number); + XiveTCTX *(*get_tctx)(XiveRouter *xrtr, CPUState *cs); +} XiveRouterClass; + +void xive_eas_pic_print_info(XiveEAS *eas, uint32_t lisn, Monitor *mon); + +int xive_router_get_eas(XiveRouter *xrtr, uint8_t eas_blk, uint32_t eas_idx, + XiveEAS *eas); +int xive_router_get_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, + XiveEND *end); +int xive_router_write_end(XiveRouter *xrtr, uint8_t end_blk, uint32_t end_idx, + XiveEND *end, uint8_t word_number); +int xive_router_get_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, + XiveNVT *nvt); +int xive_router_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, uint32_t nvt_idx, + XiveNVT *nvt, uint8_t word_number); +XiveTCTX *xive_router_get_tctx(XiveRouter *xrtr, CPUState *cs); +void xive_router_notify(XiveNotifier *xn, uint32_t lisn); + +/* + * XIVE END ESBs + */ + +#define TYPE_XIVE_END_SOURCE "xive-end-source" +#define XIVE_END_SOURCE(obj) \ + OBJECT_CHECK(XiveENDSource, (obj), TYPE_XIVE_END_SOURCE) + +typedef struct XiveENDSource { + DeviceState parent; + + uint32_t nr_ends; + uint8_t block_id; + + /* ESB memory region */ + uint32_t esb_shift; + MemoryRegion esb_mmio; + + XiveRouter *xrtr; +} XiveENDSource; + +/* + * For legacy compatibility, the exceptions define up to 256 different + * priorities. P9 implements only 9 levels : 8 active levels [0 - 7] + * and the least favored level 0xFF. + */ +#define XIVE_PRIORITY_MAX 7 + +void xive_end_pic_print_info(XiveEND *end, uint32_t end_idx, Monitor *mon); +void xive_end_queue_pic_print_info(XiveEND *end, uint32_t width, Monitor *mon); + +/* + * XIVE Thread Interrupt Management Aera (TIMA) + * + * This region gives access to the registers of the thread interrupt + * management context. It is four page wide, each page providing a + * different view of the registers. The page with the lower offset is + * the most privileged and gives access to the entire context. + */ +#define XIVE_TM_HW_PAGE 0x0 +#define XIVE_TM_HV_PAGE 0x1 +#define XIVE_TM_OS_PAGE 0x2 +#define XIVE_TM_USER_PAGE 0x3 + +extern const MemoryRegionOps xive_tm_ops; +void xive_tctx_tm_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, + unsigned size); +uint64_t xive_tctx_tm_read(XiveTCTX *tctx, hwaddr offset, unsigned size); + +void xive_tctx_pic_print_info(XiveTCTX *tctx, Monitor *mon); +Object *xive_tctx_create(Object *cpu, XiveRouter *xrtr, Error **errp); + +static inline uint32_t xive_nvt_cam_line(uint8_t nvt_blk, uint32_t nvt_idx) +{ + return (nvt_blk << 19) | nvt_idx; +} + +#endif /* PPC_XIVE_H */ diff --git a/include/hw/ppc/xive_regs.h b/include/hw/ppc/xive_regs.h new file mode 100644 index 0000000000..bf36678a24 --- /dev/null +++ b/include/hw/ppc/xive_regs.h @@ -0,0 +1,235 @@ +/* + * QEMU PowerPC XIVE internal structure definitions + * + * + * The XIVE structures are accessed by the HW and their format is + * architected to be big-endian. Some macros are provided to ease + * access to the different fields. + * + * + * Copyright (c) 2016-2018, IBM Corporation. + * + * This code is licensed under the GPL version 2 or later. See the + * COPYING file in the top-level directory. + */ + +#ifndef PPC_XIVE_REGS_H +#define PPC_XIVE_REGS_H + +/* + * Interrupt source number encoding on PowerBUS + */ +#define XIVE_SRCNO_BLOCK(srcno) (((srcno) >> 28) & 0xf) +#define XIVE_SRCNO_INDEX(srcno) ((srcno) & 0x0fffffff) +#define XIVE_SRCNO(blk, idx) ((uint32_t)(blk) << 28 | (idx)) + +#define TM_SHIFT 16 + +/* TM register offsets */ +#define TM_QW0_USER 0x000 /* All rings */ +#define TM_QW1_OS 0x010 /* Ring 0..2 */ +#define TM_QW2_HV_POOL 0x020 /* Ring 0..1 */ +#define TM_QW3_HV_PHYS 0x030 /* Ring 0..1 */ + +/* Byte offsets inside a QW QW0 QW1 QW2 QW3 */ +#define TM_NSR 0x0 /* + + - + */ +#define TM_CPPR 0x1 /* - + - + */ +#define TM_IPB 0x2 /* - + + + */ +#define TM_LSMFB 0x3 /* - + + + */ +#define TM_ACK_CNT 0x4 /* - + - - */ +#define TM_INC 0x5 /* - + - + */ +#define TM_AGE 0x6 /* - + - + */ +#define TM_PIPR 0x7 /* - + - + */ + +#define TM_WORD0 0x0 +#define TM_WORD1 0x4 + +/* + * QW word 2 contains the valid bit at the top and other fields + * depending on the QW. + */ +#define TM_WORD2 0x8 +#define TM_QW0W2_VU PPC_BIT32(0) +#define TM_QW0W2_LOGIC_SERV PPC_BITMASK32(1, 31) /* XX 2,31 ? */ +#define TM_QW1W2_VO PPC_BIT32(0) +#define TM_QW1W2_OS_CAM PPC_BITMASK32(8, 31) +#define TM_QW2W2_VP PPC_BIT32(0) +#define TM_QW2W2_POOL_CAM PPC_BITMASK32(8, 31) +#define TM_QW3W2_VT PPC_BIT32(0) +#define TM_QW3W2_LP PPC_BIT32(6) +#define TM_QW3W2_LE PPC_BIT32(7) +#define TM_QW3W2_T PPC_BIT32(31) + +/* + * In addition to normal loads to "peek" and writes (only when invalid) + * using 4 and 8 bytes accesses, the above registers support these + * "special" byte operations: + * + * - Byte load from QW0[NSR] - User level NSR (EBB) + * - Byte store to QW0[NSR] - User level NSR (EBB) + * - Byte load/store to QW1[CPPR] and QW3[CPPR] - CPPR access + * - Byte load from QW3[TM_WORD2] - Read VT||00000||LP||LE on thrd 0 + * otherwise VT||0000000 + * - Byte store to QW3[TM_WORD2] - Set VT bit (and LP/LE if present) + * + * Then we have all these "special" CI ops at these offset that trigger + * all sorts of side effects: + */ +#define TM_SPC_ACK_EBB 0x800 /* Load8 ack EBB to reg*/ +#define TM_SPC_ACK_OS_REG 0x810 /* Load16 ack OS irq to reg */ +#define TM_SPC_PUSH_USR_CTX 0x808 /* Store32 Push/Validate user context */ +#define TM_SPC_PULL_USR_CTX 0x808 /* Load32 Pull/Invalidate user + * context */ +#define TM_SPC_SET_OS_PENDING 0x812 /* Store8 Set OS irq pending bit */ +#define TM_SPC_PULL_OS_CTX 0x818 /* Load32/Load64 Pull/Invalidate OS + * context to reg */ +#define TM_SPC_PULL_POOL_CTX 0x828 /* Load32/Load64 Pull/Invalidate Pool + * context to reg*/ +#define TM_SPC_ACK_HV_REG 0x830 /* Load16 ack HV irq to reg */ +#define TM_SPC_PULL_USR_CTX_OL 0xc08 /* Store8 Pull/Inval usr ctx to odd + * line */ +#define TM_SPC_ACK_OS_EL 0xc10 /* Store8 ack OS irq to even line */ +#define TM_SPC_ACK_HV_POOL_EL 0xc20 /* Store8 ack HV evt pool to even + * line */ +#define TM_SPC_ACK_HV_EL 0xc30 /* Store8 ack HV irq to even line */ +/* XXX more... */ + +/* NSR fields for the various QW ack types */ +#define TM_QW0_NSR_EB PPC_BIT8(0) +#define TM_QW1_NSR_EO PPC_BIT8(0) +#define TM_QW3_NSR_HE PPC_BITMASK8(0, 1) +#define TM_QW3_NSR_HE_NONE 0 +#define TM_QW3_NSR_HE_POOL 1 +#define TM_QW3_NSR_HE_PHYS 2 +#define TM_QW3_NSR_HE_LSI 3 +#define TM_QW3_NSR_I PPC_BIT8(2) +#define TM_QW3_NSR_GRP_LVL PPC_BIT8(3, 7) + +/* + * EAS (Event Assignment Structure) + * + * One per interrupt source. Targets an interrupt to a given Event + * Notification Descriptor (END) and provides the corresponding + * logical interrupt number (END data) + */ +typedef struct XiveEAS { + /* + * Use a single 64-bit definition to make it easier to perform + * atomic updates + */ + uint64_t w; +#define EAS_VALID PPC_BIT(0) +#define EAS_END_BLOCK PPC_BITMASK(4, 7) /* Destination END block# */ +#define EAS_END_INDEX PPC_BITMASK(8, 31) /* Destination END index */ +#define EAS_MASKED PPC_BIT(32) /* Masked */ +#define EAS_END_DATA PPC_BITMASK(33, 63) /* Data written to the END */ +} XiveEAS; + +#define xive_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS_VALID) +#define xive_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS_MASKED) + +static inline uint64_t xive_get_field64(uint64_t mask, uint64_t word) +{ + return (be64_to_cpu(word) & mask) >> ctz64(mask); +} + +static inline uint64_t xive_set_field64(uint64_t mask, uint64_t word, + uint64_t value) +{ + uint64_t tmp = + (be64_to_cpu(word) & ~mask) | ((value << ctz64(mask)) & mask); + return cpu_to_be64(tmp); +} + +static inline uint32_t xive_get_field32(uint32_t mask, uint32_t word) +{ + return (be32_to_cpu(word) & mask) >> ctz32(mask); +} + +static inline uint32_t xive_set_field32(uint32_t mask, uint32_t word, + uint32_t value) +{ + uint32_t tmp = + (be32_to_cpu(word) & ~mask) | ((value << ctz32(mask)) & mask); + return cpu_to_be32(tmp); +} + +/* Event Notification Descriptor (END) */ +typedef struct XiveEND { + uint32_t w0; +#define END_W0_VALID PPC_BIT32(0) /* "v" bit */ +#define END_W0_ENQUEUE PPC_BIT32(1) /* "q" bit */ +#define END_W0_UCOND_NOTIFY PPC_BIT32(2) /* "n" bit */ +#define END_W0_BACKLOG PPC_BIT32(3) /* "b" bit */ +#define END_W0_PRECL_ESC_CTL PPC_BIT32(4) /* "p" bit */ +#define END_W0_ESCALATE_CTL PPC_BIT32(5) /* "e" bit */ +#define END_W0_UNCOND_ESCALATE PPC_BIT32(6) /* "u" bit - DD2.0 */ +#define END_W0_SILENT_ESCALATE PPC_BIT32(7) /* "s" bit - DD2.0 */ +#define END_W0_QSIZE PPC_BITMASK32(12, 15) +#define END_W0_SW0 PPC_BIT32(16) +#define END_W0_FIRMWARE END_W0_SW0 /* Owned by FW */ +#define END_QSIZE_4K 0 +#define END_QSIZE_64K 4 +#define END_W0_HWDEP PPC_BITMASK32(24, 31) + uint32_t w1; +#define END_W1_ESn PPC_BITMASK32(0, 1) +#define END_W1_ESn_P PPC_BIT32(0) +#define END_W1_ESn_Q PPC_BIT32(1) +#define END_W1_ESe PPC_BITMASK32(2, 3) +#define END_W1_ESe_P PPC_BIT32(2) +#define END_W1_ESe_Q PPC_BIT32(3) +#define END_W1_GENERATION PPC_BIT32(9) +#define END_W1_PAGE_OFF PPC_BITMASK32(10, 31) + uint32_t w2; +#define END_W2_MIGRATION_REG PPC_BITMASK32(0, 3) +#define END_W2_OP_DESC_HI PPC_BITMASK32(4, 31) + uint32_t w3; +#define END_W3_OP_DESC_LO PPC_BITMASK32(0, 31) + uint32_t w4; +#define END_W4_ESC_END_BLOCK PPC_BITMASK32(4, 7) +#define END_W4_ESC_END_INDEX PPC_BITMASK32(8, 31) + uint32_t w5; +#define END_W5_ESC_END_DATA PPC_BITMASK32(1, 31) + uint32_t w6; +#define END_W6_FORMAT_BIT PPC_BIT32(8) +#define END_W6_NVT_BLOCK PPC_BITMASK32(9, 12) +#define END_W6_NVT_INDEX PPC_BITMASK32(13, 31) + uint32_t w7; +#define END_W7_F0_IGNORE PPC_BIT32(0) +#define END_W7_F0_BLK_GROUPING PPC_BIT32(1) +#define END_W7_F0_PRIORITY PPC_BITMASK32(8, 15) +#define END_W7_F1_WAKEZ PPC_BIT32(0) +#define END_W7_F1_LOG_SERVER_ID PPC_BITMASK32(1, 31) +} XiveEND; + +#define xive_end_is_valid(end) (be32_to_cpu((end)->w0) & END_W0_VALID) +#define xive_end_is_enqueue(end) (be32_to_cpu((end)->w0) & END_W0_ENQUEUE) +#define xive_end_is_notify(end) (be32_to_cpu((end)->w0) & END_W0_UCOND_NOTIFY) +#define xive_end_is_backlog(end) (be32_to_cpu((end)->w0) & END_W0_BACKLOG) +#define xive_end_is_escalate(end) (be32_to_cpu((end)->w0) & END_W0_ESCALATE_CTL) + +/* Notification Virtual Target (NVT) */ +typedef struct XiveNVT { + uint32_t w0; +#define NVT_W0_VALID PPC_BIT32(0) + uint32_t w1; + uint32_t w2; + uint32_t w3; + uint32_t w4; + uint32_t w5; + uint32_t w6; + uint32_t w7; + uint32_t w8; +#define NVT_W8_GRP_VALID PPC_BIT32(0) + uint32_t w9; + uint32_t wa; + uint32_t wb; + uint32_t wc; + uint32_t wd; + uint32_t we; + uint32_t wf; +} XiveNVT; + +#define xive_nvt_is_valid(nvt) (be32_to_cpu((nvt)->w0) & NVT_W0_VALID) + +#endif /* PPC_XIVE_REGS_H */ diff --git a/include/hw/qdev-core.h b/include/hw/qdev-core.h index f1fd0f8736..33ed3b8dde 100644 --- a/include/hw/qdev-core.h +++ b/include/hw/qdev-core.h @@ -51,8 +51,9 @@ struct VMStateDescription; * Devices are constructed in two stages, * 1) object instantiation via object_initialize() and * 2) device realization via #DeviceState:realized property. - * The former may not fail (it might assert or exit), the latter may return - * error information to the caller and must be re-entrant. + * The former may not fail (and must not abort or exit, since it is called + * during device introspection already), and the latter may return error + * information to the caller and must be re-entrant. * Trivial field initializations should go into #TypeInfo.instance_init. * Operations depending on @props static properties should go into @realize. * After successful realization, setting static properties will fail. @@ -196,7 +197,7 @@ typedef struct BusChild { /** * BusState: - * @hotplug_device: link to a hotplug device associated with bus. + * @hotplug_handler: link to a hotplug handler associated with bus. */ struct BusState { Object obj; @@ -205,7 +206,8 @@ struct BusState { HotplugHandler *hotplug_handler; int max_index; bool realized; - QTAILQ_HEAD(ChildrenHead, BusChild) children; + int num_children; + QTAILQ_HEAD(, BusChild) children; QLIST_ENTRY(BusState) sibling; }; @@ -248,24 +250,30 @@ struct PropertyInfo { /** * GlobalProperty: - * @user_provided: Set to true if property comes from user-provided config - * (command-line or config file). * @used: Set to true if property was used when initializing a device. - * @errp: Error destination, used like first argument of error_setg() - * in case property setting fails later. If @errp is NULL, we - * print warnings instead of ignoring errors silently. For - * hotplugged devices, errp is always ignored and warnings are - * printed instead. + * @optional: If set to true, GlobalProperty will be skipped without errors + * if the property doesn't exist. + * + * An error is fatal for non-hotplugged devices, when the global is applied. */ typedef struct GlobalProperty { const char *driver; const char *property; const char *value; - bool user_provided; bool used; - Error **errp; + bool optional; } GlobalProperty; +static inline void +compat_props_add(GPtrArray *arr, + GlobalProperty props[], size_t nelem) +{ + int i; + for (i = 0; i < nelem; i++) { + g_ptr_array_add(arr, (void *)&props[i]); + } +} + /*** Board API. This should go away once we have a machine config file. ***/ DeviceState *qdev_create(BusState *bus, const char *name); @@ -273,7 +281,19 @@ DeviceState *qdev_try_create(BusState *bus, const char *name); void qdev_init_nofail(DeviceState *dev); void qdev_set_legacy_instance_id(DeviceState *dev, int alias_id, int required_for_version); +HotplugHandler *qdev_get_bus_hotplug_handler(DeviceState *dev); HotplugHandler *qdev_get_machine_hotplug_handler(DeviceState *dev); +/** + * qdev_get_hotplug_handler: Get handler responsible for device wiring + * + * Find HOTPLUG_HANDLER for @dev that provides [pre|un]plug callbacks for it. + * + * Note: in case @dev has a parent bus, it will be returned as handler unless + * machine handler overrides it. + * + * Returns: pointer to object that implements TYPE_HOTPLUG_HANDLER interface + * or NULL if there aren't any. + */ HotplugHandler *qdev_get_hotplug_handler(DeviceState *dev); void qdev_unplug(DeviceState *dev, Error **errp); void qdev_simple_device_unplug_cb(HotplugHandler *hotplug_dev, @@ -421,8 +441,7 @@ char *qdev_get_dev_path(DeviceState *dev); GSList *qdev_build_hotpluggable_device_list(Object *peripheral); -void qbus_set_hotplug_handler(BusState *bus, DeviceState *handler, - Error **errp); +void qbus_set_hotplug_handler(BusState *bus, Object *handler, Error **errp); void qbus_set_bus_hotplug_handler(BusState *bus, Error **errp); diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 4f60cc88f3..b6758c852e 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -36,6 +36,8 @@ extern const PropertyInfo qdev_prop_uuid; extern const PropertyInfo qdev_prop_arraylen; extern const PropertyInfo qdev_prop_link; extern const PropertyInfo qdev_prop_off_auto_pcibar; +extern const PropertyInfo qdev_prop_pcie_link_speed; +extern const PropertyInfo qdev_prop_pcie_link_width; #define DEFINE_PROP(_name, _state, _field, _prop, _type) { \ .name = (_name), \ @@ -217,6 +219,12 @@ extern const PropertyInfo qdev_prop_off_auto_pcibar; #define DEFINE_PROP_OFF_AUTO_PCIBAR(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_off_auto_pcibar, \ OffAutoPCIBAR) +#define DEFINE_PROP_PCIE_LINK_SPEED(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pcie_link_speed, \ + PCIExpLinkSpeed) +#define DEFINE_PROP_PCIE_LINK_WIDTH(_n, _s, _f, _d) \ + DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pcie_link_width, \ + PCIExpLinkWidth) #define DEFINE_PROP_UUID(_name, _state, _field) { \ .name = (_name), \ @@ -249,41 +257,11 @@ void qdev_prop_set_enum(DeviceState *dev, const char *name, int value); void qdev_prop_set_ptr(DeviceState *dev, const char *name, void *value); void qdev_prop_register_global(GlobalProperty *prop); -void qdev_prop_register_global_list(GlobalProperty *props); int qdev_prop_check_globals(void); void qdev_prop_set_globals(DeviceState *dev); void error_set_from_qdev_prop_error(Error **errp, int ret, DeviceState *dev, Property *prop, const char *value); -/** - * register_compat_prop: - * - * Register internal (not user-provided) global property, changing the - * default value of a given property in a device type. This can be used - * for enabling machine-type compatibility or for enabling - * accelerator-specific defaults in devices. - * - * The property values set using this function must be always valid and - * never report setter errors, as the property will have - * GlobalProperty::errp set to &error_abort. - * - * User-provided global properties should override internal global - * properties, so callers of this function should ensure that it is - * called before user-provided global properties are registered. - * - * @driver: Device type to be affected - * @property: Property whose default value is going to be changed - * @value: New default value for the property - */ -void register_compat_prop(const char *driver, const char *property, - const char *value); -/* - * register_compat_props_array(): using register_compat_prop(), which - * only registers internal global properties (which has lower priority - * than user-provided global properties) - */ -void register_compat_props_array(GlobalProperty *prop); - /** * qdev_property_add_static: * @dev: Device to add the property to. diff --git a/include/hw/rdma/rdma.h b/include/hw/rdma/rdma.h new file mode 100644 index 0000000000..68290fb58c --- /dev/null +++ b/include/hw/rdma/rdma.h @@ -0,0 +1,40 @@ +/* + * RDMA device interface + * + * Copyright (C) 2019 Oracle + * Copyright (C) 2019 Red Hat Inc + * + * Authors: + * Yuval Shaia + * + * 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 RDMA_H +#define RDMA_H + +#include "qom/object.h" + +#define INTERFACE_RDMA_PROVIDER "rdma" + +#define INTERFACE_RDMA_PROVIDER_CLASS(klass) \ + OBJECT_CLASS_CHECK(RdmaProviderClass, (klass), \ + INTERFACE_RDMA_PROVIDER) +#define RDMA_PROVIDER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(RdmaProviderClass, (obj), \ + INTERFACE_RDMA_PROVIDER) +#define RDMA_PROVIDER(obj) \ + INTERFACE_CHECK(RdmaProvider, (obj), \ + INTERFACE_RDMA_PROVIDER) + +typedef struct RdmaProvider RdmaProvider; + +typedef struct RdmaProviderClass { + InterfaceClass parent; + + void (*print_statistics)(Monitor *mon, RdmaProvider *obj); +} RdmaProviderClass; + +#endif diff --git a/include/hw/riscv/sifive_e.h b/include/hw/riscv/sifive_e.h index 7b6d8aed96..f715f8606f 100644 --- a/include/hw/riscv/sifive_e.h +++ b/include/hw/riscv/sifive_e.h @@ -70,7 +70,7 @@ enum { #define SIFIVE_E_PLIC_HART_CONFIG "M" #define SIFIVE_E_PLIC_NUM_SOURCES 127 #define SIFIVE_E_PLIC_NUM_PRIORITIES 7 -#define SIFIVE_E_PLIC_PRIORITY_BASE 0x0 +#define SIFIVE_E_PLIC_PRIORITY_BASE 0x04 #define SIFIVE_E_PLIC_PENDING_BASE 0x1000 #define SIFIVE_E_PLIC_ENABLE_BASE 0x2000 #define SIFIVE_E_PLIC_ENABLE_STRIDE 0x80 diff --git a/include/hw/riscv/sifive_plic.h b/include/hw/riscv/sifive_plic.h index 2f2af7e686..688cd97f82 100644 --- a/include/hw/riscv/sifive_plic.h +++ b/include/hw/riscv/sifive_plic.h @@ -55,7 +55,6 @@ typedef struct SiFivePLICState { uint32_t *pending; uint32_t *claimed; uint32_t *enable; - QemuMutex lock; /* config */ char *hart_config; diff --git a/include/hw/riscv/sifive_u.h b/include/hw/riscv/sifive_u.h index e8b4d9ffa3..892f0eee21 100644 --- a/include/hw/riscv/sifive_u.h +++ b/include/hw/riscv/sifive_u.h @@ -63,13 +63,14 @@ enum { }; enum { - SIFIVE_U_CLOCK_FREQ = 1000000000 + SIFIVE_U_CLOCK_FREQ = 1000000000, + SIFIVE_U_GEM_CLOCK_FREQ = 125000000 }; #define SIFIVE_U_PLIC_HART_CONFIG "MS" -#define SIFIVE_U_PLIC_NUM_SOURCES 127 +#define SIFIVE_U_PLIC_NUM_SOURCES 54 #define SIFIVE_U_PLIC_NUM_PRIORITIES 7 -#define SIFIVE_U_PLIC_PRIORITY_BASE 0x0 +#define SIFIVE_U_PLIC_PRIORITY_BASE 0x04 #define SIFIVE_U_PLIC_PENDING_BASE 0x1000 #define SIFIVE_U_PLIC_ENABLE_BASE 0x2000 #define SIFIVE_U_PLIC_ENABLE_STRIDE 0x80 diff --git a/include/hw/riscv/sifive_uart.h b/include/hw/riscv/sifive_uart.h index 504f18a60f..c8dc1c57fd 100644 --- a/include/hw/riscv/sifive_uart.h +++ b/include/hw/riscv/sifive_uart.h @@ -43,6 +43,9 @@ enum { SIFIVE_UART_IP_RXWM = 2 /* Receive watermark interrupt pending */ }; +#define SIFIVE_UART_GET_TXCNT(txctrl) ((txctrl >> 16) & 0x7) +#define SIFIVE_UART_GET_RXCNT(rxctrl) ((rxctrl >> 16) & 0x7) + #define TYPE_SIFIVE_UART "riscv.sifive.uart" #define SIFIVE_UART(obj) \ diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 91163d6cbf..568764b570 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -38,14 +38,18 @@ enum { VIRT_PLIC, VIRT_UART0, VIRT_VIRTIO, - VIRT_DRAM + VIRT_DRAM, + VIRT_PCIE_MMIO, + VIRT_PCIE_PIO, + VIRT_PCIE_ECAM }; enum { UART0_IRQ = 10, VIRTIO_IRQ = 1, /* 1 to 8 */ VIRTIO_COUNT = 8, - VIRTIO_NDEV = 10 + PCIE_IRQ = 0x20, /* 32 to 35 */ + VIRTIO_NDEV = 0x35 /* Arbitrary maximum number of interrupts */ }; enum { @@ -55,13 +59,20 @@ enum { #define VIRT_PLIC_HART_CONFIG "MS" #define VIRT_PLIC_NUM_SOURCES 127 #define VIRT_PLIC_NUM_PRIORITIES 7 -#define VIRT_PLIC_PRIORITY_BASE 0x0 +#define VIRT_PLIC_PRIORITY_BASE 0x04 #define VIRT_PLIC_PENDING_BASE 0x1000 #define VIRT_PLIC_ENABLE_BASE 0x2000 #define VIRT_PLIC_ENABLE_STRIDE 0x80 #define VIRT_PLIC_CONTEXT_BASE 0x200000 #define VIRT_PLIC_CONTEXT_STRIDE 0x1000 +#define FDT_PCI_ADDR_CELLS 3 +#define FDT_PCI_INT_CELLS 1 +#define FDT_PLIC_ADDR_CELLS 0 +#define FDT_PLIC_INT_CELLS 1 +#define FDT_INT_MAP_WIDTH (FDT_PCI_ADDR_CELLS + FDT_PCI_INT_CELLS + 1 + \ + FDT_PLIC_ADDR_CELLS + FDT_PLIC_INT_CELLS) + #if defined(TARGET_RISCV32) #define VIRT_CPU TYPE_RISCV_CPU_RV32GCSU_V1_10_0 #elif defined(TARGET_RISCV64) diff --git a/include/hw/s390x/ap-bridge.h b/include/hw/s390x/ap-bridge.h new file mode 100644 index 0000000000..470e439a98 --- /dev/null +++ b/include/hw/s390x/ap-bridge.h @@ -0,0 +1,19 @@ +/* + * ap bridge + * + * Copyright 2018 IBM Corp. + * + * 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_AP_BRIDGE_H +#define HW_S390X_AP_BRIDGE_H + +#define TYPE_AP_BRIDGE "ap-bridge" +#define TYPE_AP_BUS "ap-bus" + +void s390_init_ap(void); + +#endif diff --git a/include/hw/s390x/ap-device.h b/include/hw/s390x/ap-device.h new file mode 100644 index 0000000000..765e9082a3 --- /dev/null +++ b/include/hw/s390x/ap-device.h @@ -0,0 +1,22 @@ +/* + * Adjunct Processor (AP) matrix device interfaces + * + * Copyright 2018 IBM Corp. + * + * 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_AP_DEVICE_H +#define HW_S390X_AP_DEVICE_H + +#define AP_DEVICE_TYPE "ap-device" + +typedef struct APDevice { + DeviceState parent_obj; +} APDevice; + +#define AP_DEVICE(obj) \ + OBJECT_CHECK(APDevice, (obj), AP_DEVICE_TYPE) + +#endif /* HW_S390X_AP_DEVICE_H */ diff --git a/include/hw/s390x/css-bridge.h b/include/hw/s390x/css-bridge.h index cf0860432a..5a0203be5f 100644 --- a/include/hw/s390x/css-bridge.h +++ b/include/hw/s390x/css-bridge.h @@ -28,7 +28,6 @@ typedef struct VirtualCssBridge { /* virtual css bus type */ typedef struct VirtualCssBus { BusState parent_obj; - bool squash_mcss; } VirtualCssBus; #define TYPE_VIRTUAL_CSS_BUS "virtual-css-bus" diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index 35facb47d2..aae19c4272 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -48,7 +48,7 @@ typedef struct SenseId { uint8_t unused; /* padding byte */ /* extended part */ CIW ciw[MAX_CIWS]; /* variable # of CIWs */ -} QEMU_PACKED SenseId; +} SenseId; /* Note: No QEMU_PACKED due to unaligned members */ /* Channel measurements, from linux/drivers/s390/cio/cmf.c. */ typedef struct CMB { @@ -118,11 +118,12 @@ typedef enum IOInstEnding { typedef struct SubchDev SubchDev; struct SubchDev { /* channel-subsystem related things: */ + SCHIB curr_status; /* Needs alignment and thus must come first */ + ORB orb; uint8_t cssid; uint8_t ssid; uint16_t schid; uint16_t devno; - SCHIB curr_status; uint8_t sense_data[32]; hwaddr channel_prog; CCW1 last_cmd; @@ -131,7 +132,6 @@ struct SubchDev { bool thinint_active; uint8_t ccw_no_data_cnt; uint16_t migrated_schid; /* used for missmatch detection */ - ORB orb; CcwDataStream cds; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); @@ -266,11 +266,8 @@ extern const PropertyInfo css_devid_ro_propinfo; /** * Create a subchannel for the given bus id. * - * If @p bus_id is valid, and @p squash_mcss is true, verify that it is - * not already in use in the default css, and find a free devno from the - * default css image for it. - * If @p bus_id is valid, and @p squash_mcss is false, verify that it is - * not already in use, and find a free devno for it. + * If @p bus_id is valid, verify that it is not already in use, and find a + * free devno for it. * If @p bus_id is not valid find a free subchannel id and device number * across all subchannel sets and all css images starting from the default * css image. @@ -282,7 +279,7 @@ extern const PropertyInfo css_devid_ro_propinfo; * The caller becomes owner of the returned subchannel structure and * is responsible for unregistering and freeing it. */ -SubchDev *css_create_sch(CssDevId bus_id, bool squash_mcss, Error **errp); +SubchDev *css_create_sch(CssDevId bus_id, Error **errp); /** Turn on css migration */ void css_register_vmstate(void); diff --git a/include/hw/s390x/ioinst.h b/include/hw/s390x/ioinst.h index 5f2db6949d..c6737a30d4 100644 --- a/include/hw/s390x/ioinst.h +++ b/include/hw/s390x/ioinst.h @@ -25,7 +25,8 @@ typedef struct SCSW { uint8_t dstat; uint8_t cstat; uint16_t count; -} QEMU_PACKED SCSW; +} SCSW; +QEMU_BUILD_BUG_MSG(sizeof(SCSW) != 12, "size of SCSW is wrong"); #define SCSW_FLAGS_MASK_KEY 0xf000 #define SCSW_FLAGS_MASK_SCTL 0x0800 @@ -94,7 +95,8 @@ typedef struct PMCW { uint8_t pam; uint8_t chpid[8]; uint32_t chars; -} QEMU_PACKED PMCW; +} PMCW; +QEMU_BUILD_BUG_MSG(sizeof(PMCW) != 28, "size of PMCW is wrong"); #define PMCW_FLAGS_MASK_QF 0x8000 #define PMCW_FLAGS_MASK_W 0x4000 @@ -127,7 +129,8 @@ typedef struct IRB { uint32_t esw[5]; uint32_t ecw[8]; uint32_t emw[8]; -} QEMU_PACKED IRB; +} IRB; +QEMU_BUILD_BUG_MSG(sizeof(IRB) != 96, "size of IRB is wrong"); /* operation request block */ typedef struct ORB { @@ -136,7 +139,8 @@ typedef struct ORB { uint8_t lpm; uint8_t ctrl1; uint32_t cpa; -} QEMU_PACKED ORB; +} ORB; +QEMU_BUILD_BUG_MSG(sizeof(ORB) != 12, "size of ORB is wrong"); #define ORB_CTRL0_MASK_KEY 0xf000 #define ORB_CTRL0_MASK_SPND 0x0800 @@ -165,7 +169,8 @@ typedef struct CCW0 { uint8_t flags; uint8_t reserved; uint16_t count; -} QEMU_PACKED CCW0; +} CCW0; +QEMU_BUILD_BUG_MSG(sizeof(CCW0) != 8, "size of CCW0 is wrong"); /* channel command word (type 1) */ typedef struct CCW1 { @@ -173,7 +178,8 @@ typedef struct CCW1 { uint8_t flags; uint16_t count; uint32_t cda; -} QEMU_PACKED CCW1; +} CCW1; +QEMU_BUILD_BUG_MSG(sizeof(CCW1) != 8, "size of CCW1 is wrong"); #define CCW_FLAG_DC 0x80 #define CCW_FLAG_CC 0x40 @@ -192,7 +198,8 @@ typedef struct CCW1 { typedef struct CRW { uint16_t flags; uint16_t rsid; -} QEMU_PACKED CRW; +} CRW; +QEMU_BUILD_BUG_MSG(sizeof(CRW) != 4, "size of CRW is wrong"); #define CRW_FLAGS_MASK_S 0x4000 #define CRW_FLAGS_MASK_R 0x2000 diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index ab88d49d10..8aa27199c9 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -29,7 +29,6 @@ typedef struct S390CcwMachineState { bool aes_key_wrap; bool dea_key_wrap; uint8_t loadparm[8]; - bool s390_squash_mcss; } S390CcwMachineState; typedef struct S390CcwMachineClass { @@ -40,12 +39,15 @@ typedef struct S390CcwMachineClass { bool ri_allowed; bool cpu_model_allowed; bool css_migration_enabled; + bool hpage_1m_allowed; } S390CcwMachineClass; /* runtime-instrumentation allowed by the machine */ bool ri_allowed(void); /* cpu model allowed by the machine */ bool cpu_model_allowed(void); +/* 1M huge page mappings allowed by the machine */ +bool hpage_1m_allowed(void); /** * Returns true if (vmstate based) migration of the channel subsystem diff --git a/include/hw/s390x/tod.h b/include/hw/s390x/tod.h index 413c0d7c02..9c4a6000c3 100644 --- a/include/hw/s390x/tod.h +++ b/include/hw/s390x/tod.h @@ -12,6 +12,7 @@ #define HW_S390_TOD_H #include "hw/qdev.h" +#include "s390-tod.h" typedef struct S390TOD { uint8_t high; @@ -31,34 +32,25 @@ typedef struct S390TODState { /* private */ DeviceState parent_obj; - /* unused by KVM implementation */ + /* + * Used by TCG to remember the time base. Used by KVM to backup the TOD + * while the TOD is stopped. + */ S390TOD base; + /* Used by KVM to remember if the TOD is stopped and base is valid. */ + bool stopped; } S390TODState; typedef struct S390TODClass { /* private */ DeviceClass parent_class; + void (*parent_realize)(DeviceState *dev, Error **errp); /* public */ void (*get)(const S390TODState *td, S390TOD *tod, Error **errp); void (*set)(S390TODState *td, const S390TOD *tod, Error **errp); } S390TODClass; -/* The value of the TOD clock for 1.1.1970. */ -#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL - -/* Converts ns to s390's clock format */ -static inline uint64_t time2tod(uint64_t ns) -{ - return (ns << 9) / 125 + (((ns & 0xff10000000000000ull) / 125) << 9); -} - -/* Converts s390's clock format to ns */ -static inline uint64_t tod2time(uint64_t t) -{ - return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); -} - void s390_init_tod(void); S390TODState *s390_get_todstate(void); diff --git a/include/hw/scsi/emulation.h b/include/hw/scsi/emulation.h new file mode 100644 index 0000000000..09fba1ff39 --- /dev/null +++ b/include/hw/scsi/emulation.h @@ -0,0 +1,16 @@ +#ifndef HW_SCSI_EMULATION_H +#define HW_SCSI_EMULATION_H 1 + +typedef struct SCSIBlockLimits { + bool wsnz; + uint16_t min_io_size; + uint32_t max_unmap_descr; + uint32_t opt_io_size; + uint32_t max_unmap_sectors; + uint32_t unmap_sectors; + uint32_t max_io_sectors; +} SCSIBlockLimits; + +int scsi_emulate_block_limits(uint8_t *outbuf, const SCSIBlockLimits *bl); + +#endif diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 682a0d2de0..adab63d1c9 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -23,6 +23,8 @@ struct ESPState { int32_t ti_size; uint32_t ti_rptr, ti_wptr; uint32_t status; + uint32_t deferred_status; + bool deferred_complete; uint32_t dma; uint8_t ti_buf[TI_BUFSZ]; SCSIBus bus; diff --git a/include/hw/scsi/scsi.h b/include/hw/scsi/scsi.h index ee3a4118fb..acef25faa4 100644 --- a/include/hw/scsi/scsi.h +++ b/include/hw/scsi/scsi.h @@ -189,7 +189,6 @@ void scsi_device_report_change(SCSIDevice *dev, SCSISense sense); void scsi_device_unit_attention_reported(SCSIDevice *dev); void scsi_generic_read_device_inquiry(SCSIDevice *dev); int scsi_device_get_sense(SCSIDevice *dev, uint8_t *buf, int len, bool fixed); -int scsi_disk_emulate_vpd_page(SCSIRequest *req, uint8_t *outbuf); int scsi_SG_IO_FROM_DEV(BlockBackend *blk, uint8_t *cmd, uint8_t cmd_size, uint8_t *buf, uint8_t buf_size); SCSIDevice *scsi_device_find(SCSIBus *bus, int channel, int target, int lun); diff --git a/include/hw/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h index fbcee94ed7..adfedb2efc 100644 --- a/include/hw/sh4/sh_intc.h +++ b/include/hw/sh4/sh_intc.h @@ -61,21 +61,21 @@ struct intc_desc { int sh_intc_get_pending_vector(struct intc_desc *desc, int imask); struct intc_source *sh_intc_source(struct intc_desc *desc, intc_enum id); void sh_intc_toggle_source(struct intc_source *source, - int enable_adj, int assert_adj); + int enable_adj, int assert_adj); void sh_intc_register_sources(struct intc_desc *desc, - struct intc_vect *vectors, - int nr_vectors, - struct intc_group *groups, - int nr_groups); + struct intc_vect *vectors, + int nr_vectors, + struct intc_group *groups, + int nr_groups); int sh_intc_init(MemoryRegion *sysmem, struct intc_desc *desc, - int nr_sources, - struct intc_mask_reg *mask_regs, - int nr_mask_regs, - struct intc_prio_reg *prio_regs, - int nr_prio_regs); + int nr_sources, + struct intc_mask_reg *mask_regs, + int nr_mask_regs, + struct intc_prio_reg *prio_regs, + int nr_prio_regs); void sh_intc_set_irl(void *opaque, int n, int level); diff --git a/include/hw/smbios/ipmi.h b/include/hw/smbios/ipmi.h deleted file mode 100644 index 1c9aae38f2..0000000000 --- a/include/hw/smbios/ipmi.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * IPMI SMBIOS firmware handling - * - * Copyright (c) 2015,2016 Corey Minyard, MontaVista Software, LLC - * - * 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_SMBIOS_IPMI_H -#define QEMU_SMBIOS_IPMI_H - -void smbios_build_type_38_table(void); - -#endif /* QEMU_SMBIOS_IPMI_H */ diff --git a/include/hw/ssi/aspeed_smc.h b/include/hw/ssi/aspeed_smc.h index 1f557313fa..3b1e7fce6c 100644 --- a/include/hw/ssi/aspeed_smc.h +++ b/include/hw/ssi/aspeed_smc.h @@ -98,6 +98,9 @@ typedef struct AspeedSMCState { uint8_t conf_enable_w0; AspeedSMCFlash *flashes; + + uint8_t snoop_index; + uint8_t snoop_dummies; } AspeedSMCState; #endif /* ASPEED_SMC_H */ diff --git a/include/hw/ssi/pl022.h b/include/hw/ssi/pl022.h new file mode 100644 index 0000000000..a080519366 --- /dev/null +++ b/include/hw/ssi/pl022.h @@ -0,0 +1,51 @@ +/* + * ARM PrimeCell PL022 Synchronous Serial Port + * + * Copyright (c) 2007 CodeSourcery. + * Written by Paul Brook + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 or + * (at your option) any later version. + */ + +/* This is a model of the Arm PrimeCell PL022 synchronous serial port. + * The PL022 TRM is: + * http://infocenter.arm.com/help/topic/com.arm.doc.ddi0194h/DDI0194H_ssp_pl022_trm.pdf + * + * QEMU interface: + * + sysbus IRQ: SSPINTR combined interrupt line + * + sysbus MMIO region 0: MemoryRegion for the device's registers + */ + +#ifndef HW_SSI_PL022_H +#define HW_SSI_PL022_H + +#include "hw/sysbus.h" + +#define TYPE_PL022 "pl022" +#define PL022(obj) OBJECT_CHECK(PL022State, (obj), TYPE_PL022) + +typedef struct PL022State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + uint32_t cr0; + uint32_t cr1; + uint32_t bitmask; + uint32_t sr; + uint32_t cpsr; + uint32_t is; + uint32_t im; + /* The FIFO head points to the next empty entry. */ + int tx_fifo_head; + int rx_fifo_head; + int tx_fifo_len; + int rx_fifo_len; + uint16_t tx_fifo[8]; + uint16_t rx_fifo[8]; + qemu_irq irq; + SSIBus *ssi; +} PL022State; + +#endif diff --git a/include/hw/stream.h b/include/hw/stream.h index c370ba0c66..15774f07ab 100644 --- a/include/hw/stream.h +++ b/include/hw/stream.h @@ -14,9 +14,7 @@ #define STREAM_SLAVE(obj) \ INTERFACE_CHECK(StreamSlave, (obj), TYPE_STREAM_SLAVE) -typedef struct StreamSlave { - Object Parent; -} StreamSlave; +typedef struct StreamSlave StreamSlave; typedef void (*StreamCanPushNotifyFn)(void *opaque); diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 0b59a3b8d6..1aedcf05c9 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -38,9 +38,6 @@ typedef struct SysBusDevice SysBusDevice; typedef struct SysBusDeviceClass { /*< private >*/ DeviceClass parent_class; - /*< public >*/ - - int (*init)(SysBusDevice *dev); /* * Let the sysbus device format its own non-PIO, non-MMIO unit address. diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index 040a088734..1fb949e167 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -23,8 +23,7 @@ #define ASPEED_TIMER_H #include "qemu/timer.h" - -typedef struct AspeedSCUState AspeedSCUState; +#include "hw/misc/aspeed_scu.h" #define ASPEED_TIMER(obj) \ OBJECT_CHECK(AspeedTimerCtrlState, (obj), TYPE_ASPEED_TIMER); diff --git a/include/hw/timer/cmsdk-apb-dualtimer.h b/include/hw/timer/cmsdk-apb-dualtimer.h new file mode 100644 index 0000000000..9843a9dbb1 --- /dev/null +++ b/include/hw/timer/cmsdk-apb-dualtimer.h @@ -0,0 +1,72 @@ +/* + * ARM CMSDK APB dual-timer emulation + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB dual-input timer" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * + * QEMU interface: + * + QOM property "pclk-frq": frequency at which the timer is clocked + * + sysbus MMIO region 0: the register bank + * + sysbus IRQ 0: combined timer interrupt TIMINTC + * + sysbus IRO 1: timer block 1 interrupt TIMINT1 + * + sysbus IRQ 2: timer block 2 interrupt TIMINT2 + */ + +#ifndef CMSDK_APB_DUALTIMER_H +#define CMSDK_APB_DUALTIMER_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" + +#define TYPE_CMSDK_APB_DUALTIMER "cmsdk-apb-dualtimer" +#define CMSDK_APB_DUALTIMER(obj) OBJECT_CHECK(CMSDKAPBDualTimer, (obj), \ + TYPE_CMSDK_APB_DUALTIMER) + +typedef struct CMSDKAPBDualTimer CMSDKAPBDualTimer; + +/* One of the two identical timer modules in the dual-timer module */ +typedef struct CMSDKAPBDualTimerModule { + CMSDKAPBDualTimer *parent; + struct ptimer_state *timer; + qemu_irq timerint; + /* + * We must track the guest LOAD and VALUE register state by hand + * rather than leaving this state only in the ptimer limit/count, + * because if CONTROL.SIZE is 0 then only the low 16 bits of the + * counter actually counts, but the high half is still guest + * accessible. + */ + uint32_t load; + uint32_t value; + uint32_t control; + uint32_t intstatus; +} CMSDKAPBDualTimerModule; + +#define CMSDK_APB_DUALTIMER_NUM_MODULES 2 + +struct CMSDKAPBDualTimer { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq timerintc; + uint32_t pclk_frq; + + CMSDKAPBDualTimerModule timermod[CMSDK_APB_DUALTIMER_NUM_MODULES]; + uint32_t timeritcr; + uint32_t timeritop; +}; + +#endif diff --git a/include/hw/timer/m48t59.h b/include/hw/timer/m48t59.h index db5e43a8da..6f8db04fce 100644 --- a/include/hw/timer/m48t59.h +++ b/include/hw/timer/m48t59.h @@ -13,9 +13,7 @@ #define NVRAM(obj) \ INTERFACE_CHECK(Nvram, (obj), TYPE_NVRAM) -typedef struct Nvram { - Object parent; -} Nvram; +typedef struct Nvram Nvram; typedef struct NvramClass { InterfaceClass parent; diff --git a/include/hw/timer/nrf51_timer.h b/include/hw/timer/nrf51_timer.h new file mode 100644 index 0000000000..85cad2300d --- /dev/null +++ b/include/hw/timer/nrf51_timer.h @@ -0,0 +1,80 @@ +/* + * nRF51 System-on-Chip Timer peripheral + * + * QEMU interface: + * + sysbus MMIO regions 0: GPIO registers + * + sysbus irq + * + * Copyright 2018 Steffen Görtz + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + */ +#ifndef NRF51_TIMER_H +#define NRF51_TIMER_H + +#include "hw/sysbus.h" +#include "qemu/timer.h" +#define TYPE_NRF51_TIMER "nrf51_soc.timer" +#define NRF51_TIMER(obj) OBJECT_CHECK(NRF51TimerState, (obj), TYPE_NRF51_TIMER) + +#define NRF51_TIMER_REG_COUNT 4 + +#define NRF51_TIMER_TASK_START 0x000 +#define NRF51_TIMER_TASK_STOP 0x004 +#define NRF51_TIMER_TASK_COUNT 0x008 +#define NRF51_TIMER_TASK_CLEAR 0x00C +#define NRF51_TIMER_TASK_SHUTDOWN 0x010 +#define NRF51_TIMER_TASK_CAPTURE_0 0x040 +#define NRF51_TIMER_TASK_CAPTURE_3 0x04C + +#define NRF51_TIMER_EVENT_COMPARE_0 0x140 +#define NRF51_TIMER_EVENT_COMPARE_1 0x144 +#define NRF51_TIMER_EVENT_COMPARE_2 0x148 +#define NRF51_TIMER_EVENT_COMPARE_3 0x14C + +#define NRF51_TIMER_REG_SHORTS 0x200 +#define NRF51_TIMER_REG_SHORTS_MASK 0xf0f +#define NRF51_TIMER_REG_INTENSET 0x304 +#define NRF51_TIMER_REG_INTENCLR 0x308 +#define NRF51_TIMER_REG_INTEN_MASK 0xf0000 +#define NRF51_TIMER_REG_MODE 0x504 +#define NRF51_TIMER_REG_MODE_MASK 0x01 +#define NRF51_TIMER_TIMER 0 +#define NRF51_TIMER_COUNTER 1 +#define NRF51_TIMER_REG_BITMODE 0x508 +#define NRF51_TIMER_REG_BITMODE_MASK 0x03 +#define NRF51_TIMER_WIDTH_16 0 +#define NRF51_TIMER_WIDTH_8 1 +#define NRF51_TIMER_WIDTH_24 2 +#define NRF51_TIMER_WIDTH_32 3 +#define NRF51_TIMER_REG_PRESCALER 0x510 +#define NRF51_TIMER_REG_PRESCALER_MASK 0x0F +#define NRF51_TIMER_REG_CC0 0x540 +#define NRF51_TIMER_REG_CC3 0x54C + +typedef struct NRF51TimerState { + SysBusDevice parent_obj; + + MemoryRegion iomem; + qemu_irq irq; + + QEMUTimer timer; + int64_t timer_start_ns; + int64_t update_counter_ns; + uint32_t counter; + + bool running; + + uint8_t events_compare[NRF51_TIMER_REG_COUNT]; + uint32_t cc[NRF51_TIMER_REG_COUNT]; + uint32_t shorts; + uint32_t inten; + uint32_t mode; + uint32_t bitmode; + uint32_t prescaler; + +} NRF51TimerState; + + +#endif diff --git a/include/hw/timer/pl031.h b/include/hw/timer/pl031.h new file mode 100644 index 0000000000..99416d8ba5 --- /dev/null +++ b/include/hw/timer/pl031.h @@ -0,0 +1,44 @@ +/* + * ARM AMBA PrimeCell PL031 RTC + * + * Copyright (c) 2007 CodeSourcery + * + * This file 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. + * + * Contributions after 2012-01-13 are licensed under the terms of the + * GNU GPL, version 2 or (at your option) any later version. + */ + +#ifndef HW_TIMER_PL031 +#define HW_TIMER_PL031 + +#include "hw/sysbus.h" + +#define TYPE_PL031 "pl031" +#define PL031(obj) OBJECT_CHECK(PL031State, (obj), TYPE_PL031) + +typedef struct PL031State { + SysBusDevice parent_obj; + + MemoryRegion iomem; + QEMUTimer *timer; + qemu_irq irq; + + /* + * Needed to preserve the tick_count across migration, even if the + * absolute value of the rtc_clock is different on the source and + * destination. + */ + uint32_t tick_offset_vmstate; + uint32_t tick_offset; + + uint32_t mr; + uint32_t lr; + uint32_t cr; + uint32_t im; + uint32_t is; +} PL031State; + +#endif diff --git a/include/hw/usb.h b/include/hw/usb.h index a5080adecc..c21f41c8a9 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -408,7 +408,7 @@ struct USBPacket { struct USBCombinedPacket { USBPacket *first; - QTAILQ_HEAD(packets_head, USBPacket) packets; + QTAILQ_HEAD(, USBPacket) packets; QEMUIOVector iov; }; @@ -593,8 +593,6 @@ const char *usb_device_get_product_desc(USBDevice *dev); const USBDesc *usb_device_get_usb_desc(USBDevice *dev); -int ehci_create_ich9_with_companions(PCIBus *bus, int slot); - /* quirks.c */ /* In bulk endpoints are streaming data sources (iow behave like isoc eps) */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index a9036929b2..1155b79678 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -26,17 +26,18 @@ #include "qemu/queue.h" #include "qemu/notify.h" #include "ui/console.h" +#include "hw/display/ramfb.h" #ifdef CONFIG_LINUX #include #endif -#define ERR_PREFIX "vfio error: %s: " -#define WARN_PREFIX "vfio warning: %s: " +#define VFIO_MSG_PREFIX "vfio %s: " enum { VFIO_DEVICE_TYPE_PCI = 0, VFIO_DEVICE_TYPE_PLATFORM = 1, VFIO_DEVICE_TYPE_CCW = 2, + VFIO_DEVICE_TYPE_AP = 3, }; typedef struct VFIOMmap { @@ -73,6 +74,7 @@ typedef struct VFIOContainer { unsigned iommu_type; int error; bool initialized; + unsigned long pgsizes; /* * This assumes the host IOMMU can support only a single * contiguous IOVA window. We may need to generalize that in @@ -112,6 +114,7 @@ typedef struct VFIODevice { bool reset_works; bool needs_reset; bool no_mmap; + bool balloon_allowed; VFIODeviceOps *ops; unsigned int num_irqs; unsigned int num_regions; @@ -131,6 +134,7 @@ typedef struct VFIOGroup { QLIST_HEAD(, VFIODevice) device_list; QLIST_ENTRY(VFIOGroup) next; QLIST_ENTRY(VFIOGroup) container_next; + bool balloon_allowed; } VFIOGroup; typedef struct VFIODMABuf { @@ -143,6 +147,11 @@ typedef struct VFIODMABuf { typedef struct VFIODisplay { QemuConsole *con; + RAMFBState *ramfb; + struct vfio_region_info *edid_info; + struct vfio_region_gfx_edid *edid_regs; + uint8_t *edid_blob; + QEMUTimer *edid_link_timer; struct { VFIORegion buffer; DisplaySurface *surface; @@ -175,8 +184,8 @@ int vfio_get_device(VFIOGroup *group, const char *name, VFIODevice *vbasedev, Error **errp); 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; +typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; +extern VFIOGroupList vfio_group_list; #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, @@ -184,6 +193,8 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info); bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); +struct vfio_info_cap_header * +vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id); #endif extern const MemoryListener vfio_prereg_listener; diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h index 9baaa2db09..30d3c28d3b 100644 --- a/include/hw/vfio/vfio-platform.h +++ b/include/hw/vfio/vfio-platform.h @@ -53,8 +53,9 @@ typedef struct VFIOPlatformDevice { VFIORegion **regions; QLIST_HEAD(, VFIOINTp) intp_list; /* list of IRQs */ /* queue of pending IRQs */ - QSIMPLEQ_HEAD(pending_intp_queue, VFIOINTp) pending_intp_queue; - char *compat; /* compatibility string */ + QSIMPLEQ_HEAD(, VFIOINTp) pending_intp_queue; + char *compat; /* DT compatible values, separated by NUL */ + unsigned int num_compat; /* number of compatible values */ uint32_t mmap_timeout; /* delay to re-enable mmaps after interrupt */ QEMUTimer *mmap_timer; /* allows fast-path resume after IRQ hit */ QemuMutex intp_mutex; /* protect the intp_list IRQ state */ diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 81283ec50f..d6632a18e6 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -25,6 +25,7 @@ typedef enum VhostSetConfigType { VHOST_SET_CONFIG_TYPE_MIGRATION = 1, } VhostSetConfigType; +struct vhost_inflight; struct vhost_dev; struct vhost_log; struct vhost_memory; @@ -104,6 +105,13 @@ typedef int (*vhost_crypto_close_session_op)(struct vhost_dev *dev, typedef bool (*vhost_backend_mem_section_filter_op)(struct vhost_dev *dev, MemoryRegionSection *section); +typedef int (*vhost_get_inflight_fd_op)(struct vhost_dev *dev, + uint16_t queue_size, + struct vhost_inflight *inflight); + +typedef int (*vhost_set_inflight_fd_op)(struct vhost_dev *dev, + struct vhost_inflight *inflight); + typedef struct VhostOps { VhostBackendType backend_type; vhost_backend_init vhost_backend_init; @@ -142,6 +150,8 @@ typedef struct VhostOps { vhost_crypto_create_session_op vhost_crypto_create_session; vhost_crypto_close_session_op vhost_crypto_close_session; vhost_backend_mem_section_filter_op vhost_backend_mem_section_filter; + vhost_get_inflight_fd_op vhost_get_inflight_fd; + vhost_set_inflight_fd_op vhost_set_inflight_fd; } VhostOps; extern const VhostOps user_ops; diff --git a/include/hw/virtio/vhost-scsi-common.h b/include/hw/virtio/vhost-scsi-common.h index 4553be4bc3..57fb1d87b5 100644 --- a/include/hw/virtio/vhost-scsi-common.h +++ b/include/hw/virtio/vhost-scsi-common.h @@ -35,6 +35,7 @@ typedef struct VHostSCSICommon { int channel; int target; int lun; + uint64_t host_features; } VHostSCSICommon; int vhost_scsi_common_start(VHostSCSICommon *vsc); diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index d52944aeeb..68634bee61 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -36,7 +36,8 @@ typedef struct VHostUserBlk { uint32_t queue_size; uint32_t config_wce; struct vhost_dev dev; - VhostUserState *vhost_user; + struct vhost_inflight *inflight; + VhostUserState vhost_user; } VHostUserBlk; #endif diff --git a/include/hw/virtio/vhost-user-scsi.h b/include/hw/virtio/vhost-user-scsi.h index 3ec34ae867..738f9288bd 100644 --- a/include/hw/virtio/vhost-user-scsi.h +++ b/include/hw/virtio/vhost-user-scsi.h @@ -30,8 +30,7 @@ typedef struct VHostUserSCSI { VHostSCSICommon parent_obj; - uint64_t host_features; - VhostUserState *vhost_user; + VhostUserState vhost_user; } VHostUserSCSI; #endif /* VHOST_USER_SCSI_H */ diff --git a/include/hw/virtio/vhost-user.h b/include/hw/virtio/vhost-user.h index fd660393a0..811e325f42 100644 --- a/include/hw/virtio/vhost-user.h +++ b/include/hw/virtio/vhost-user.h @@ -22,7 +22,7 @@ typedef struct VhostUserState { VhostUserHostNotifier notifier[VIRTIO_QUEUE_MAX]; } VhostUserState; -VhostUserState *vhost_user_init(void); +bool vhost_user_init(VhostUserState *user, CharBackend *chr, Error **errp); void vhost_user_cleanup(VhostUserState *user); #endif diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index a7f449fa87..619498c8f4 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -7,6 +7,15 @@ #include "exec/memory.h" /* Generic structures common for any vhost based device. */ + +struct vhost_inflight { + int fd; + void *addr; + uint64_t size; + uint64_t offset; + uint16_t queue_size; +}; + struct vhost_virtqueue { int kick; int call; @@ -120,4 +129,13 @@ int vhost_dev_set_config(struct vhost_dev *dev, const uint8_t *data, */ void vhost_dev_set_config_notifier(struct vhost_dev *dev, const VhostDevConfigOps *ops); + +void vhost_dev_reset_inflight(struct vhost_inflight *inflight); +void vhost_dev_free_inflight(struct vhost_inflight *inflight); +void vhost_dev_save_inflight(struct vhost_inflight *inflight, QEMUFile *f); +int vhost_dev_load_inflight(struct vhost_inflight *inflight, QEMUFile *f); +int vhost_dev_set_inflight(struct vhost_dev *dev, + struct vhost_inflight *inflight); +int vhost_dev_get_inflight(struct vhost_dev *dev, uint16_t queue_size, + struct vhost_inflight *inflight); #endif diff --git a/include/hw/virtio/virtio-balloon.h b/include/hw/virtio/virtio-balloon.h index e0df3528c8..1afafb12f6 100644 --- a/include/hw/virtio/virtio-balloon.h +++ b/include/hw/virtio/virtio-balloon.h @@ -17,11 +17,14 @@ #include "standard-headers/linux/virtio_balloon.h" #include "hw/virtio/virtio.h" +#include "sysemu/iothread.h" #define TYPE_VIRTIO_BALLOON "virtio-balloon-device" #define VIRTIO_BALLOON(obj) \ OBJECT_CHECK(VirtIOBalloon, (obj), TYPE_VIRTIO_BALLOON) +#define VIRTIO_BALLOON_FREE_PAGE_REPORT_CMD_ID_MIN 0x80000000 + typedef struct virtio_balloon_stat VirtIOBalloonStat; typedef struct virtio_balloon_stat_modern { @@ -30,18 +33,44 @@ typedef struct virtio_balloon_stat_modern { uint64_t val; } VirtIOBalloonStatModern; +typedef struct PartiallyBalloonedPage PartiallyBalloonedPage; + +enum virtio_balloon_free_page_report_status { + FREE_PAGE_REPORT_S_STOP = 0, + FREE_PAGE_REPORT_S_REQUESTED = 1, + FREE_PAGE_REPORT_S_START = 2, + FREE_PAGE_REPORT_S_DONE = 3, +}; + typedef struct VirtIOBalloon { VirtIODevice parent_obj; - VirtQueue *ivq, *dvq, *svq; + VirtQueue *ivq, *dvq, *svq, *free_page_vq; + uint32_t free_page_report_status; uint32_t num_pages; uint32_t actual; + uint32_t free_page_report_cmd_id; uint64_t stats[VIRTIO_BALLOON_S_NR]; VirtQueueElement *stats_vq_elem; size_t stats_vq_offset; QEMUTimer *stats_timer; + IOThread *iothread; + QEMUBH *free_page_bh; + /* + * Lock to synchronize threads to access the free page reporting related + * fields (e.g. free_page_report_status). + */ + QemuMutex free_page_lock; + QemuCond free_page_cond; + /* + * Set to block iothread to continue reading free page hints as the VM is + * stopped. + */ + bool block_iothread; + NotifierWithReturn free_page_report_notify; int64_t stats_last_update; int64_t stats_poll_interval; uint32_t host_features; + PartiallyBalloonedPage *pbp; } VirtIOBalloon; #endif diff --git a/include/hw/virtio/virtio-blk.h b/include/hw/virtio/virtio-blk.h index 5117431d96..cddcfbebe9 100644 --- a/include/hw/virtio/virtio-blk.h +++ b/include/hw/virtio/virtio-blk.h @@ -35,11 +35,11 @@ struct VirtIOBlkConf BlockConf conf; IOThread *iothread; char *serial; - uint32_t scsi; - uint32_t config_wce; uint32_t request_merging; uint16_t num_queues; uint16_t queue_size; + uint32_t max_discard_sectors; + uint32_t max_write_zeroes_sectors; }; struct VirtIOBlockDataPlane; @@ -57,6 +57,8 @@ typedef struct VirtIOBlock { bool dataplane_disabled; bool dataplane_started; struct VirtIOBlockDataPlane *dataplane; + uint64_t host_features; + size_t config_size; } VirtIOBlock; typedef struct VirtIOBlockReq { diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index d0321672f4..60425c5d58 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -61,12 +61,15 @@ struct virtio_gpu_requested_state { enum virtio_gpu_conf_flags { VIRTIO_GPU_FLAG_VIRGL_ENABLED = 1, VIRTIO_GPU_FLAG_STATS_ENABLED, + VIRTIO_GPU_FLAG_EDID_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)) +#define virtio_gpu_edid_enabled(_cfg) \ + (_cfg.flags & (1 << VIRTIO_GPU_FLAG_EDID_ENABLED)) struct virtio_gpu_conf { uint64_t max_hostmem; @@ -81,7 +84,6 @@ 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; }; @@ -96,9 +98,6 @@ typedef struct VirtIOGPU { int enable; - int config_size; - DeviceState *qdev; - QTAILQ_HEAD(, virtio_gpu_simple_resource) reslist; QTAILQ_HEAD(, virtio_gpu_ctrl_command) cmdq; QTAILQ_HEAD(, virtio_gpu_ctrl_command) fenceq; @@ -114,6 +113,7 @@ typedef struct VirtIOGPU { bool use_virgl_renderer; bool renderer_inited; int renderer_blocked; + bool renderer_reset; QEMUTimer *fence_poll; QEMUTimer *print_stats; @@ -125,7 +125,6 @@ typedef struct VirtIOGPU { uint32_t bytes_3d; } stats; - void (*disable_scanout)(struct VirtIOGPU *g, int scanout_id); Error *migration_blocker; } VirtIOGPU; @@ -159,10 +158,14 @@ void virtio_gpu_ctrl_response_nodata(VirtIOGPU *g, enum virtio_gpu_ctrl_type type); void virtio_gpu_get_display_info(VirtIOGPU *g, struct virtio_gpu_ctrl_command *cmd); -int virtio_gpu_create_mapping_iov(struct virtio_gpu_resource_attach_backing *ab, +void virtio_gpu_get_edid(VirtIOGPU *g, + struct virtio_gpu_ctrl_command *cmd); +int virtio_gpu_create_mapping_iov(VirtIOGPU *g, + struct virtio_gpu_resource_attach_backing *ab, struct virtio_gpu_ctrl_command *cmd, uint64_t **addr, struct iovec **iov); -void virtio_gpu_cleanup_mapping_iov(struct iovec *iov, uint32_t count); +void virtio_gpu_cleanup_mapping_iov(VirtIOGPU *g, + struct iovec *iov, uint32_t count); void virtio_gpu_process_cmdq(VirtIOGPU *g); /* virtio-gpu-3d.c */ @@ -170,7 +173,6 @@ 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); -void virtio_gpu_gl_block(void *opaque, bool block); int virtio_gpu_virgl_init(VirtIOGPU *g); int virtio_gpu_virgl_get_num_capsets(VirtIOGPU *g); #endif diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index 4d7f3c82ca..b96f0c643f 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -17,6 +17,7 @@ #include "qemu/units.h" #include "standard-headers/linux/virtio_net.h" #include "hw/virtio/virtio.h" +#include "net/announce.h" #define TYPE_VIRTIO_NET "virtio-net-device" #define VIRTIO_NET(obj) \ @@ -44,6 +45,82 @@ typedef struct virtio_net_conf uint8_t duplex; } virtio_net_conf; +/* Coalesced packets type & status */ +typedef enum { + RSC_COALESCE, /* Data been coalesced */ + RSC_FINAL, /* Will terminate current connection */ + RSC_NO_MATCH, /* No matched in the buffer pool */ + RSC_BYPASS, /* Packet to be bypass, not tcp, tcp ctrl, etc */ + RSC_CANDIDATE /* Data want to be coalesced */ +} CoalesceStatus; + +typedef struct VirtioNetRscStat { + uint32_t received; + uint32_t coalesced; + uint32_t over_size; + uint32_t cache; + uint32_t empty_cache; + uint32_t no_match_cache; + uint32_t win_update; + uint32_t no_match; + uint32_t tcp_syn; + uint32_t tcp_ctrl_drain; + uint32_t dup_ack; + uint32_t dup_ack1; + uint32_t dup_ack2; + uint32_t pure_ack; + uint32_t ack_out_of_win; + uint32_t data_out_of_win; + uint32_t data_out_of_order; + uint32_t data_after_pure_ack; + uint32_t bypass_not_tcp; + uint32_t tcp_option; + uint32_t tcp_all_opt; + uint32_t ip_frag; + uint32_t ip_ecn; + uint32_t ip_hacked; + uint32_t ip_option; + uint32_t purge_failed; + uint32_t drain_failed; + uint32_t final_failed; + int64_t timer; +} VirtioNetRscStat; + +/* Rsc unit general info used to checking if can coalescing */ +typedef struct VirtioNetRscUnit { + void *ip; /* ip header */ + uint16_t *ip_plen; /* data len pointer in ip header field */ + struct tcp_header *tcp; /* tcp header */ + uint16_t tcp_hdrlen; /* tcp header len */ + uint16_t payload; /* pure payload without virtio/eth/ip/tcp */ +} VirtioNetRscUnit; + +/* Coalesced segment */ +typedef struct VirtioNetRscSeg { + QTAILQ_ENTRY(VirtioNetRscSeg) next; + void *buf; + size_t size; + uint16_t packets; + uint16_t dup_ack; + bool is_coalesced; /* need recal ipv4 header checksum, mark here */ + VirtioNetRscUnit unit; + NetClientState *nc; +} VirtioNetRscSeg; + +typedef struct VirtIONet VirtIONet; + +/* Chain is divided by protocol(ipv4/v6) and NetClientInfo */ +typedef struct VirtioNetRscChain { + QTAILQ_ENTRY(VirtioNetRscChain) next; + VirtIONet *n; /* VirtIONet */ + uint16_t proto; + uint8_t gso_type; + uint16_t max_payload; + QEMUTimer *drain_timer; + QTAILQ_HEAD(, VirtioNetRscSeg) buffers; + VirtioNetRscStat stat; +} VirtioNetRscChain; + /* Maximum packet size we can receive from tap device: header + 64k */ #define VIRTIO_NET_MAX_BUFSIZE (sizeof(struct virtio_net_hdr) + (64 * KiB)) @@ -59,19 +136,25 @@ typedef struct VirtIONetQueue { struct VirtIONet *n; } VirtIONetQueue; -typedef struct VirtIONet { +struct VirtIONet { VirtIODevice parent_obj; uint8_t mac[ETH_ALEN]; uint16_t status; VirtIONetQueue *vqs; VirtQueue *ctrl_vq; NICState *nic; + /* RSC Chains - temporary storage of coalesced data, + all these data are lost in case of migration */ + QTAILQ_HEAD(, VirtioNetRscChain) rsc_chains; uint32_t tx_timeout; int32_t tx_burst; uint32_t has_vnet_hdr; size_t host_hdr_len; size_t guest_hdr_len; uint64_t host_features; + uint32_t rsc_timeout; + uint8_t rsc4_enabled; + uint8_t rsc6_enabled; uint8_t has_ufo; uint32_t mergeable_rx_bufs; uint8_t promisc; @@ -99,11 +182,10 @@ typedef struct VirtIONet { char *netclient_name; char *netclient_type; uint64_t curr_guest_offloads; - QEMUTimer *announce_timer; - int announce_counter; + AnnounceTimer announce_timer; bool needs_vnet_hdr_swap; bool mtu_bypass_backend; -} VirtIONet; +}; void virtio_net_set_netclient_name(VirtIONet *n, const char *name, const char *type); diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 9c1fa07d6d..ce9516236a 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -37,6 +37,21 @@ static inline hwaddr vring_align(hwaddr addr, return QEMU_ALIGN_UP(addr, align); } +/* + * Calculate the number of bytes up to and including the given 'field' of + * 'container'. + */ +#define virtio_endof(container, field) \ + (offsetof(container, field) + sizeof_field(container, field)) + +typedef struct VirtIOFeature { + uint64_t flags; + size_t end; +} VirtIOFeature; + +size_t virtio_feature_get_config_size(VirtIOFeature *features, + uint64_t host_features); + typedef struct VirtQueue VirtQueue; #define VIRTQUEUE_MAX_SIZE 1024 diff --git a/include/hw/watchdog/cmsdk-apb-watchdog.h b/include/hw/watchdog/cmsdk-apb-watchdog.h new file mode 100644 index 0000000000..6ae9531370 --- /dev/null +++ b/include/hw/watchdog/cmsdk-apb-watchdog.h @@ -0,0 +1,67 @@ +/* + * ARM CMSDK APB watchdog emulation + * + * Copyright (c) 2018 Linaro Limited + * Written 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 version 2 or + * (at your option) any later version. + */ + +/* + * This is a model of the "APB watchdog" which is part of the Cortex-M + * System Design Kit (CMSDK) and documented in the Cortex-M System + * Design Kit Technical Reference Manual (ARM DDI0479C): + * https://developer.arm.com/products/system-design/system-design-kits/cortex-m-system-design-kit + * + * QEMU interface: + * + QOM property "wdogclk-frq": frequency at which the watchdog is clocked + * + sysbus MMIO region 0: the register bank + * + sysbus IRQ 0: watchdog interrupt + * + * In real hardware the watchdog's reset output is just a GPIO line + * which can then be masked by the board or treated as a simple interrupt. + * (For instance the IoTKit does this with the non-secure watchdog, so that + * secure code can control whether non-secure code can perform a system + * reset via its watchdog.) In QEMU, we just wire up the watchdog reset + * to watchdog_perform_action(), at least for the moment. + */ + +#ifndef CMSDK_APB_WATCHDOG_H +#define CMSDK_APB_WATCHDOG_H + +#include "hw/sysbus.h" +#include "hw/ptimer.h" + +#define TYPE_CMSDK_APB_WATCHDOG "cmsdk-apb-watchdog" +#define CMSDK_APB_WATCHDOG(obj) OBJECT_CHECK(CMSDKAPBWatchdog, (obj), \ + TYPE_CMSDK_APB_WATCHDOG) + +/* + * This shares the same struct (and cast macro) as the base + * cmsdk-apb-watchdog device. + */ +#define TYPE_LUMINARY_WATCHDOG "luminary-watchdog" + +typedef struct CMSDKAPBWatchdog { + /*< private >*/ + SysBusDevice parent_obj; + + /*< public >*/ + MemoryRegion iomem; + qemu_irq wdogint; + uint32_t wdogclk_frq; + bool is_luminary; + struct ptimer_state *timer; + + uint32_t control; + uint32_t intstatus; + uint32_t lock; + uint32_t itcr; + uint32_t itop; + uint32_t resetstatus; + const uint32_t *id; +} CMSDKAPBWatchdog; + +#endif diff --git a/include/hw/xen/io/ring.h b/include/hw/xen/io/ring.h index ffa3ebadc8..1adacf09f9 100644 --- a/include/hw/xen/io/ring.h +++ b/include/hw/xen/io/ring.h @@ -235,8 +235,8 @@ typedef struct __name##_back_ring __name##_back_ring_t * to be ineffective where _req is a struct which consists of only bitfields. */ #define RING_COPY_REQUEST(_r, _idx, _req) do { \ - /* Use volatile to force the copy into _req. */ \ - *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \ + /* Use volatile to force the copy into _req. */ \ + *(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx); \ } while (0) #define RING_GET_RESPONSE(_r, _idx) \ diff --git a/include/hw/xen/start_info.h b/include/hw/xen/start_info.h new file mode 100644 index 0000000000..348779eb10 --- /dev/null +++ b/include/hw/xen/start_info.h @@ -0,0 +1,146 @@ +/* + * 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. + * + * Copyright (c) 2016, Citrix Systems, Inc. + */ + +#ifndef __XEN_PUBLIC_ARCH_X86_HVM_START_INFO_H__ +#define __XEN_PUBLIC_ARCH_X86_HVM_START_INFO_H__ + +/* + * Start of day structure passed to PVH guests and to HVM guests in %ebx. + * + * NOTE: nothing will be loaded at physical address 0, so a 0 value in any + * of the address fields should be treated as not present. + * + * 0 +----------------+ + * | magic | Contains the magic value XEN_HVM_START_MAGIC_VALUE + * | | ("xEn3" with the 0x80 bit of the "E" set). + * 4 +----------------+ + * | version | Version of this structure. Current version is 1. New + * | | versions are guaranteed to be backwards-compatible. + * 8 +----------------+ + * | flags | SIF_xxx flags. + * 12 +----------------+ + * | nr_modules | Number of modules passed to the kernel. + * 16 +----------------+ + * | modlist_paddr | Physical address of an array of modules + * | | (layout of the structure below). + * 24 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 32 +----------------+ + * | rsdp_paddr | Physical address of the RSDP ACPI data structure. + * 40 +----------------+ + * | memmap_paddr | Physical address of the (optional) memory map. Only + * | | present in version 1 and newer of the structure. + * 48 +----------------+ + * | memmap_entries | Number of entries in the memory map table. Only + * | | present in version 1 and newer of the structure. + * | | Zero if there is no memory map being provided. + * 52 +----------------+ + * | reserved | Version 1 and newer only. + * 56 +----------------+ + * + * The layout of each entry in the module structure is the following: + * + * 0 +----------------+ + * | paddr | Physical address of the module. + * 8 +----------------+ + * | size | Size of the module in bytes. + * 16 +----------------+ + * | cmdline_paddr | Physical address of the command line, + * | | a zero-terminated ASCII string. + * 24 +----------------+ + * | reserved | + * 32 +----------------+ + * + * The layout of each entry in the memory map table is as follows: + * + * 0 +----------------+ + * | addr | Base address + * 8 +----------------+ + * | size | Size of mapping in bytes + * 16 +----------------+ + * | type | Type of mapping as defined between the hypervisor + * | | and guest it's starting. E820_TYPE_xxx, for example. + * 20 +----------------| + * | reserved | + * 24 +----------------+ + * + * The address and sizes are always a 64bit little endian unsigned integer. + * + * NB: Xen on x86 will always try to place all the data below the 4GiB + * boundary. + * + * Version numbers of the hvm_start_info structure have evolved like this: + * + * Version 0: + * + * Version 1: Added the memmap_paddr/memmap_entries fields (plus 4 bytes of + * padding) to the end of the hvm_start_info struct. These new + * fields can be used to pass a memory map to the guest. The + * memory map is optional and so guests that understand version 1 + * of the structure must check that memmap_entries is non-zero + * before trying to read the memory map. + */ +#define XEN_HVM_START_MAGIC_VALUE 0x336ec578 + +/* + * C representation of the x86/HVM start info layout. + * + * The canonical definition of this layout is above, this is just a way to + * represent the layout described there using C types. + */ +struct hvm_start_info { + uint32_t magic; /* Contains the magic value 0x336ec578 */ + /* ("xEn3" with the 0x80 bit of the "E" set).*/ + uint32_t version; /* Version of this structure. */ + uint32_t flags; /* SIF_xxx flags. */ + uint32_t nr_modules; /* Number of modules passed to the kernel. */ + uint64_t modlist_paddr; /* Physical address of an array of */ + /* hvm_modlist_entry. */ + uint64_t cmdline_paddr; /* Physical address of the command line. */ + uint64_t rsdp_paddr; /* Physical address of the RSDP ACPI data */ + /* structure. */ + uint64_t memmap_paddr; /* Physical address of an array of */ + /* hvm_memmap_table_entry. Only present in */ + /* version 1 and newer of the structure */ + uint32_t memmap_entries; /* Number of entries in the memmap table. */ + /* Only present in version 1 and newer of */ + /* the structure. Value will be zero if */ + /* there is no memory map being provided. */ + uint32_t reserved; +}; + +struct hvm_modlist_entry { + uint64_t paddr; /* Physical address of the module. */ + uint64_t size; /* Size of the module in bytes. */ + uint64_t cmdline_paddr; /* Physical address of the command line. */ + uint64_t reserved; +}; + +struct hvm_memmap_table_entry { + uint64_t addr; /* Base address of the memory region */ + uint64_t size; /* Size of the memory region in bytes */ + uint32_t type; /* Mapping type */ + uint32_t reserved; +}; + +#endif /* __XEN_PUBLIC_ARCH_X86_HVM_START_INFO_H__ */ diff --git a/include/hw/xen/xen-backend.h b/include/hw/xen/xen-backend.h new file mode 100644 index 0000000000..010d712638 --- /dev/null +++ b/include/hw/xen/xen-backend.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 HW_XEN_BACKEND_H +#define HW_XEN_BACKEND_H + +#include "hw/xen/xen-bus.h" + +typedef struct XenBackendInstance XenBackendInstance; + +typedef void (*XenBackendDeviceCreate)(XenBackendInstance *backend, + QDict *opts, Error **errp); +typedef void (*XenBackendDeviceDestroy)(XenBackendInstance *backend, + Error **errp); + +typedef struct XenBackendInfo { + const char *type; + XenBackendDeviceCreate create; + XenBackendDeviceDestroy destroy; +} XenBackendInfo; + +XenBus *xen_backend_get_bus(XenBackendInstance *backend); +const char *xen_backend_get_name(XenBackendInstance *backend); + +void xen_backend_set_device(XenBackendInstance *backend, + XenDevice *xendevice); +XenDevice *xen_backend_get_device(XenBackendInstance *backend); + +void xen_backend_register(const XenBackendInfo *info); + +void xen_backend_device_create(XenBus *xenbus, const char *type, + const char *name, QDict *opts, Error **errp); +bool xen_backend_try_device_destroy(XenDevice *xendev, Error **errp); + +#endif /* HW_XEN_BACKEND_H */ diff --git a/include/hw/xen/xen-block.h b/include/hw/xen/xen-block.h new file mode 100644 index 0000000000..11d351b4b3 --- /dev/null +++ b/include/hw/xen/xen-block.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 HW_XEN_BLOCK_H +#define HW_XEN_BLOCK_H + +#include "hw/xen/xen-bus.h" +#include "hw/block/block.h" +#include "hw/block/dataplane/xen-block.h" +#include "sysemu/iothread.h" + +typedef enum XenBlockVdevType { + XEN_BLOCK_VDEV_TYPE_INVALID, + XEN_BLOCK_VDEV_TYPE_DP, + XEN_BLOCK_VDEV_TYPE_XVD, + XEN_BLOCK_VDEV_TYPE_HD, + XEN_BLOCK_VDEV_TYPE_SD, + XEN_BLOCK_VDEV_TYPE__MAX +} XenBlockVdevType; + +typedef struct XenBlockVdev { + XenBlockVdevType type; + unsigned long disk; + unsigned long partition; + unsigned long number; +} XenBlockVdev; + + +typedef struct XenBlockProperties { + XenBlockVdev vdev; + BlockConf conf; + unsigned int max_ring_page_order; + IOThread *iothread; +} XenBlockProperties; + +typedef struct XenBlockDrive { + char *id; + char *node_name; +} XenBlockDrive; + +typedef struct XenBlockIOThread { + char *id; +} XenBlockIOThread; + +typedef struct XenBlockDevice { + XenDevice xendev; + XenBlockProperties props; + const char *device_type; + unsigned int info; + XenBlockDataPlane *dataplane; + XenBlockDrive *drive; + XenBlockIOThread *iothread; +} XenBlockDevice; + +typedef void (*XenBlockDeviceRealize)(XenBlockDevice *blockdev, Error **errp); +typedef void (*XenBlockDeviceUnrealize)(XenBlockDevice *blockdev, Error **errp); + +typedef struct XenBlockDeviceClass { + /*< private >*/ + XenDeviceClass parent_class; + /*< public >*/ + XenBlockDeviceRealize realize; + XenBlockDeviceUnrealize unrealize; +} XenBlockDeviceClass; + +#define TYPE_XEN_BLOCK_DEVICE "xen-block" +#define XEN_BLOCK_DEVICE(obj) \ + OBJECT_CHECK(XenBlockDevice, (obj), TYPE_XEN_BLOCK_DEVICE) +#define XEN_BLOCK_DEVICE_CLASS(class) \ + OBJECT_CLASS_CHECK(XenBlockDeviceClass, (class), TYPE_XEN_BLOCK_DEVICE) +#define XEN_BLOCK_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XenBlockDeviceClass, (obj), TYPE_XEN_BLOCK_DEVICE) + +typedef struct XenDiskDevice { + XenBlockDevice blockdev; +} XenDiskDevice; + +#define TYPE_XEN_DISK_DEVICE "xen-disk" +#define XEN_DISK_DEVICE(obj) \ + OBJECT_CHECK(XenDiskDevice, (obj), TYPE_XEN_DISK_DEVICE) + +typedef struct XenCDRomDevice { + XenBlockDevice blockdev; +} XenCDRomDevice; + +#define TYPE_XEN_CDROM_DEVICE "xen-cdrom" +#define XEN_CDROM_DEVICE(obj) \ + OBJECT_CHECK(XenCDRomDevice, (obj), TYPE_XEN_CDROM_DEVICE) + +#endif /* HW_XEN_BLOCK_H */ diff --git a/include/hw/xen/xen-bus-helper.h b/include/hw/xen/xen-bus-helper.h new file mode 100644 index 0000000000..4c0f747445 --- /dev/null +++ b/include/hw/xen/xen-bus-helper.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 HW_XEN_BUS_HELPER_H +#define HW_XEN_BUS_HELPER_H + +#include "hw/xen/xen_common.h" + +const char *xs_strstate(enum xenbus_state state); + +void xs_node_create(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, struct xs_permissions perms[], + unsigned int nr_perms, Error **errp); +void xs_node_destroy(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, Error **errp); + +/* Write to node/key unless node is empty, in which case write to key */ +void xs_node_vprintf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, va_list ap) + GCC_FMT_ATTR(6, 0); +void xs_node_printf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, ...) + GCC_FMT_ATTR(6, 7); + +/* Read from node/key unless node is empty, in which case read from key */ +int xs_node_vscanf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, va_list ap); +int xs_node_scanf(struct xs_handle *xsh, xs_transaction_t tid, + const char *node, const char *key, Error **errp, + const char *fmt, ...); + +/* Watch node/key unless node is empty, in which case watch key */ +void xs_node_watch(struct xs_handle *xsh, const char *node, const char *key, + char *token, Error **errp); +void xs_node_unwatch(struct xs_handle *xsh, const char *node, const char *key, + const char *token, Error **errp); + +#endif /* HW_XEN_BUS_HELPER_H */ diff --git a/include/hw/xen/xen-bus.h b/include/hw/xen/xen-bus.h new file mode 100644 index 0000000000..3183f10e3c --- /dev/null +++ b/include/hw/xen/xen-bus.h @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018 Citrix Systems 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 HW_XEN_BUS_H +#define HW_XEN_BUS_H + +#include "hw/xen/xen_common.h" +#include "hw/sysbus.h" +#include "qemu/notify.h" + +typedef void (*XenWatchHandler)(void *opaque); + +typedef struct XenWatch XenWatch; + +typedef struct XenDevice { + DeviceState qdev; + domid_t frontend_id; + char *name; + char *backend_path, *frontend_path; + enum xenbus_state backend_state, frontend_state; + Notifier exit; + XenWatch *backend_state_watch, *frontend_state_watch; + bool backend_online; + XenWatch *backend_online_watch; + xengnttab_handle *xgth; + bool feature_grant_copy; + xenevtchn_handle *xeh; + NotifierList event_notifiers; +} XenDevice; + +typedef char *(*XenDeviceGetName)(XenDevice *xendev, Error **errp); +typedef void (*XenDeviceRealize)(XenDevice *xendev, Error **errp); +typedef void (*XenDeviceFrontendChanged)(XenDevice *xendev, + enum xenbus_state frontend_state, + Error **errp); +typedef void (*XenDeviceUnrealize)(XenDevice *xendev, Error **errp); + +typedef struct XenDeviceClass { + /*< private >*/ + DeviceClass parent_class; + /*< public >*/ + const char *backend; + const char *device; + XenDeviceGetName get_name; + XenDeviceRealize realize; + XenDeviceFrontendChanged frontend_changed; + XenDeviceUnrealize unrealize; +} XenDeviceClass; + +#define TYPE_XEN_DEVICE "xen-device" +#define XEN_DEVICE(obj) \ + OBJECT_CHECK(XenDevice, (obj), TYPE_XEN_DEVICE) +#define XEN_DEVICE_CLASS(class) \ + OBJECT_CLASS_CHECK(XenDeviceClass, (class), TYPE_XEN_DEVICE) +#define XEN_DEVICE_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XenDeviceClass, (obj), TYPE_XEN_DEVICE) + +typedef struct XenBus { + BusState qbus; + domid_t backend_id; + struct xs_handle *xsh; + NotifierList watch_notifiers; + XenWatch *backend_watch; +} XenBus; + +typedef struct XenBusClass { + /*< private >*/ + BusClass parent_class; +} XenBusClass; + +#define TYPE_XEN_BUS "xen-bus" +#define XEN_BUS(obj) \ + OBJECT_CHECK(XenBus, (obj), TYPE_XEN_BUS) +#define XEN_BUS_CLASS(class) \ + OBJECT_CLASS_CHECK(XenBusClass, (class), TYPE_XEN_BUS) +#define XEN_BUS_GET_CLASS(obj) \ + OBJECT_GET_CLASS(XenBusClass, (obj), TYPE_XEN_BUS) + +void xen_bus_init(void); + +void xen_device_backend_set_state(XenDevice *xendev, + enum xenbus_state state); +enum xenbus_state xen_device_backend_get_state(XenDevice *xendev); + +void xen_device_backend_printf(XenDevice *xendev, const char *key, + const char *fmt, ...) + GCC_FMT_ATTR(3, 4); +void xen_device_frontend_printf(XenDevice *xendev, const char *key, + const char *fmt, ...) + GCC_FMT_ATTR(3, 4); + +int xen_device_frontend_scanf(XenDevice *xendev, const char *key, + const char *fmt, ...); + +void xen_device_set_max_grant_refs(XenDevice *xendev, unsigned int nr_refs, + Error **errp); +void *xen_device_map_grant_refs(XenDevice *xendev, uint32_t *refs, + unsigned int nr_refs, int prot, + Error **errp); +void xen_device_unmap_grant_refs(XenDevice *xendev, void *map, + unsigned int nr_refs, Error **errp); + +typedef struct XenDeviceGrantCopySegment { + union { + void *virt; + struct { + uint32_t ref; + off_t offset; + } foreign; + } source, dest; + size_t len; +} XenDeviceGrantCopySegment; + +void xen_device_copy_grant_refs(XenDevice *xendev, bool to_domain, + XenDeviceGrantCopySegment segs[], + unsigned int nr_segs, Error **errp); + +typedef struct XenEventChannel XenEventChannel; + +typedef void (*XenEventHandler)(void *opaque); + +XenEventChannel *xen_device_bind_event_channel(XenDevice *xendev, + unsigned int port, + XenEventHandler handler, + void *opaque, Error **errp); +void xen_device_notify_event_channel(XenDevice *xendev, + XenEventChannel *channel, + Error **errp); +void xen_device_unbind_event_channel(XenDevice *xendev, + XenEventChannel *channel, + Error **errp); + +#endif /* HW_XEN_BUS_H */ diff --git a/include/hw/xen/xen_backend.h b/include/hw/xen/xen-legacy-backend.h similarity index 61% rename from include/hw/xen/xen_backend.h rename to include/hw/xen/xen-legacy-backend.h index 9c17fdd85d..20cb47b5bf 100644 --- a/include/hw/xen/xen_backend.h +++ b/include/hw/xen/xen-legacy-backend.h @@ -11,7 +11,7 @@ #define TYPE_XENBACKEND "xen-backend" #define XENBACKEND_DEVICE(obj) \ - OBJECT_CHECK(XenDevice, (obj), TYPE_XENBACKEND) + OBJECT_CHECK(XenLegacyDevice, (obj), TYPE_XENBACKEND) /* variables */ extern struct xs_handle *xenstore; @@ -20,32 +20,37 @@ extern DeviceState *xen_sysdev; extern BusState *xen_sysbus; int xenstore_mkdir(char *path, int p); -int xenstore_write_be_str(struct XenDevice *xendev, const char *node, const char *val); -int xenstore_write_be_int(struct XenDevice *xendev, const char *node, int ival); -int xenstore_write_be_int64(struct XenDevice *xendev, const char *node, int64_t ival); -char *xenstore_read_be_str(struct XenDevice *xendev, const char *node); -int xenstore_read_be_int(struct XenDevice *xendev, const char *node, int *ival); -void xenstore_update_fe(char *watch, struct XenDevice *xendev); +int xenstore_write_be_str(struct XenLegacyDevice *xendev, const char *node, + const char *val); +int xenstore_write_be_int(struct XenLegacyDevice *xendev, const char *node, + int ival); +int xenstore_write_be_int64(struct XenLegacyDevice *xendev, const char *node, + int64_t ival); +char *xenstore_read_be_str(struct XenLegacyDevice *xendev, const char *node); +int xenstore_read_be_int(struct XenLegacyDevice *xendev, const char *node, + int *ival); +void xenstore_update_fe(char *watch, struct XenLegacyDevice *xendev); void xenstore_update_be(char *watch, char *type, int dom, struct XenDevOps *ops); -char *xenstore_read_fe_str(struct XenDevice *xendev, const char *node); -int xenstore_read_fe_int(struct XenDevice *xendev, const char *node, int *ival); -int xenstore_read_fe_uint64(struct XenDevice *xendev, const char *node, +char *xenstore_read_fe_str(struct XenLegacyDevice *xendev, const char *node); +int xenstore_read_fe_int(struct XenLegacyDevice *xendev, const char *node, + int *ival); +int xenstore_read_fe_uint64(struct XenLegacyDevice *xendev, const char *node, uint64_t *uval); -void xen_be_check_state(struct XenDevice *xendev); +void xen_be_check_state(struct XenLegacyDevice *xendev); /* xen backend driver bits */ int xen_be_init(void); void xen_be_register_common(void); int xen_be_register(const char *type, struct XenDevOps *ops); -int xen_be_set_state(struct XenDevice *xendev, enum xenbus_state state); -int xen_be_bind_evtchn(struct XenDevice *xendev); -void xen_be_set_max_grant_refs(struct XenDevice *xendev, +int xen_be_set_state(struct XenLegacyDevice *xendev, enum xenbus_state state); +int xen_be_bind_evtchn(struct XenLegacyDevice *xendev); +void xen_be_set_max_grant_refs(struct XenLegacyDevice *xendev, unsigned int nr_refs); -void *xen_be_map_grant_refs(struct XenDevice *xendev, uint32_t *refs, +void *xen_be_map_grant_refs(struct XenLegacyDevice *xendev, uint32_t *refs, unsigned int nr_refs, int prot); -void xen_be_unmap_grant_refs(struct XenDevice *xendev, void *ptr, +void xen_be_unmap_grant_refs(struct XenLegacyDevice *xendev, void *ptr, unsigned int nr_refs); typedef struct XenGrantCopySegment { @@ -59,17 +64,17 @@ typedef struct XenGrantCopySegment { size_t len; } XenGrantCopySegment; -int xen_be_copy_grant_refs(struct XenDevice *xendev, +int xen_be_copy_grant_refs(struct XenLegacyDevice *xendev, bool to_domain, XenGrantCopySegment segs[], unsigned int nr_segs); -static inline void *xen_be_map_grant_ref(struct XenDevice *xendev, +static inline void *xen_be_map_grant_ref(struct XenLegacyDevice *xendev, uint32_t ref, int prot) { return xen_be_map_grant_refs(xendev, &ref, 1, prot); } -static inline void xen_be_unmap_grant_ref(struct XenDevice *xendev, +static inline void xen_be_unmap_grant_ref(struct XenLegacyDevice *xendev, void *ptr) { return xen_be_unmap_grant_refs(xendev, ptr, 1); diff --git a/include/hw/xen/xen.h b/include/hw/xen/xen.h index 7efcdaa8fe..ba039c146d 100644 --- a/include/hw/xen/xen.h +++ b/include/hw/xen/xen.h @@ -15,8 +15,7 @@ /* xen-machine.c */ enum xen_mode { XEN_EMULATE = 0, // xen emulation, using xenner (default) - XEN_CREATE, // create xen domain - XEN_ATTACH // attach to xen domain created by xend + XEN_ATTACH // attach to xen domain created by libxl }; extern uint32_t xen_domid; diff --git a/include/hw/xen/xen_common.h b/include/hw/xen/xen_common.h index 93f631e5bf..9a8155e172 100644 --- a/include/hw/xen/xen_common.h +++ b/include/hw/xen/xen_common.h @@ -32,6 +32,7 @@ extern xc_interface *xen_xc; typedef xc_interface xenforeignmemory_handle; typedef xc_evtchn xenevtchn_handle; typedef xc_gnttab xengnttab_handle; +typedef evtchn_port_or_error_t xenevtchn_port_or_error_t; #define xenevtchn_open(l, f) xc_evtchn_open(l, f); #define xenevtchn_close(h) xc_evtchn_close(h) @@ -661,24 +662,6 @@ static inline int xen_set_ioreq_server_state(domid_t dom, #endif -#ifdef CONFIG_XEN_PV_DOMAIN_BUILD -#if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40700 -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 - /* Xen before 4.8 */ #if CONFIG_XEN_CTRL_INTERFACE_VERSION < 40800 diff --git a/include/hw/xen/xen_pvdev.h b/include/hw/xen/xen_pvdev.h index d473e9b34d..83e5174d90 100644 --- a/include/hw/xen/xen_pvdev.h +++ b/include/hw/xen/xen_pvdev.h @@ -6,7 +6,7 @@ #define XEN_BUFSIZE 1024 -struct XenDevice; +struct XenLegacyDevice; /* driver uses grant tables -> open gntdev device (xendev->gnttabdev) */ #define DEVOPS_FLAG_NEED_GNTDEV 1 @@ -16,19 +16,21 @@ struct XenDevice; struct XenDevOps { size_t size; uint32_t flags; - void (*alloc)(struct XenDevice *xendev); - int (*init)(struct XenDevice *xendev); - int (*initialise)(struct XenDevice *xendev); - void (*connected)(struct XenDevice *xendev); - void (*event)(struct XenDevice *xendev); - void (*disconnect)(struct XenDevice *xendev); - int (*free)(struct XenDevice *xendev); - void (*backend_changed)(struct XenDevice *xendev, const char *node); - void (*frontend_changed)(struct XenDevice *xendev, const char *node); + void (*alloc)(struct XenLegacyDevice *xendev); + int (*init)(struct XenLegacyDevice *xendev); + int (*initialise)(struct XenLegacyDevice *xendev); + void (*connected)(struct XenLegacyDevice *xendev); + void (*event)(struct XenLegacyDevice *xendev); + void (*disconnect)(struct XenLegacyDevice *xendev); + int (*free)(struct XenLegacyDevice *xendev); + void (*backend_changed)(struct XenLegacyDevice *xendev, + const char *node); + void (*frontend_changed)(struct XenLegacyDevice *xendev, + const char *node); int (*backend_register)(void); }; -struct XenDevice { +struct XenLegacyDevice { DeviceState qdev; const char *type; int dom; @@ -49,7 +51,7 @@ struct XenDevice { xengnttab_handle *gnttabdev; struct XenDevOps *ops; - QTAILQ_ENTRY(XenDevice) next; + QTAILQ_ENTRY(XenLegacyDevice) next; }; /* ------------------------------------------------------------- */ @@ -66,14 +68,14 @@ void xenstore_update(void *unused); const char *xenbus_strstate(enum xenbus_state state); void xen_pv_evtchn_event(void *opaque); -void xen_pv_insert_xendev(struct XenDevice *xendev); -void xen_pv_del_xendev(struct XenDevice *xendev); -struct XenDevice *xen_pv_find_xendev(const char *type, int dom, int dev); +void xen_pv_insert_xendev(struct XenLegacyDevice *xendev); +void xen_pv_del_xendev(struct XenLegacyDevice *xendev); +struct XenLegacyDevice *xen_pv_find_xendev(const char *type, int dom, int dev); -void xen_pv_unbind_evtchn(struct XenDevice *xendev); -int xen_pv_send_notify(struct XenDevice *xendev); +void xen_pv_unbind_evtchn(struct XenLegacyDevice *xendev); +int xen_pv_send_notify(struct XenLegacyDevice *xendev); -void xen_pv_printf(struct XenDevice *xendev, int msg_level, +void xen_pv_printf(struct XenLegacyDevice *xendev, int msg_level, const char *fmt, ...) GCC_FMT_ATTR(3, 4); #endif /* QEMU_HW_XEN_PVDEV_H */ diff --git a/include/hw/xtensa/mx_pic.h b/include/hw/xtensa/mx_pic.h new file mode 100644 index 0000000000..e6cd8cf016 --- /dev/null +++ b/include/hw/xtensa/mx_pic.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2013 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 _XTENSA_MX_PIC_H +#define _XTENSA_MX_PIC_H + +#include "exec/memory.h" +#include "hw/irq.h" + +struct XtensaMxPic; +typedef struct XtensaMxPic XtensaMxPic; + +XtensaMxPic *xtensa_mx_pic_init(unsigned n_extint); +void xtensa_mx_pic_reset(void *opaque); +MemoryRegion *xtensa_mx_pic_register_cpu(XtensaMxPic *mx, + qemu_irq *irq, + qemu_irq runstall); +qemu_irq *xtensa_mx_pic_get_extints(XtensaMxPic *mx); + +#endif diff --git a/include/io/channel-tls.h b/include/io/channel-tls.h index 87fcaf9146..fdbdf12feb 100644 --- a/include/io/channel-tls.h +++ b/include/io/channel-tls.h @@ -48,6 +48,7 @@ struct QIOChannelTLS { QIOChannel parent; QIOChannel *master; QCryptoTLSSession *session; + QIOChannelShutdown shutdown; }; /** diff --git a/include/io/channel.h b/include/io/channel.h index e8cdadb0b0..59460cb1ec 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -51,9 +51,9 @@ enum QIOChannelFeature { typedef enum QIOChannelShutdown QIOChannelShutdown; enum QIOChannelShutdown { - QIO_CHANNEL_SHUTDOWN_BOTH, - QIO_CHANNEL_SHUTDOWN_READ, - QIO_CHANNEL_SHUTDOWN_WRITE, + QIO_CHANNEL_SHUTDOWN_READ = 1, + QIO_CHANNEL_SHUTDOWN_WRITE = 2, + QIO_CHANNEL_SHUTDOWN_BOTH = 3, }; typedef gboolean (*QIOChannelFunc)(QIOChannel *ioc, @@ -739,10 +739,13 @@ void qio_channel_detach_aio_context(QIOChannel *ioc); * addition, no two coroutine can be waiting on the same condition * and channel at the same time. * - * This must only be called from coroutine context + * This must only be called from coroutine context. It is safe to + * reenter the coroutine externally while it is waiting; in this + * case the function will return even if @condition is not yet + * available. */ -void qio_channel_yield(QIOChannel *ioc, - GIOCondition condition); +void coroutine_fn qio_channel_yield(QIOChannel *ioc, + GIOCondition condition); /** * qio_channel_wait: diff --git a/include/io/task.h b/include/io/task.h index 9e09b95b2e..57d8ba835e 100644 --- a/include/io/task.h +++ b/include/io/task.h @@ -232,7 +232,8 @@ QIOTask *qio_task_new(Object *source, * * Run a task in a background thread. When @worker * returns it will call qio_task_complete() in - * the event thread context that provided. + * the thread that is running the main loop associated + * with @context. */ void qio_task_run_in_thread(QIOTask *task, QIOTaskWorker worker, @@ -240,6 +241,32 @@ void qio_task_run_in_thread(QIOTask *task, GDestroyNotify destroy, GMainContext *context); + +/** + * qio_task_wait_thread: + * @task: the task struct + * + * Wait for completion of a task that was previously + * invoked using qio_task_run_in_thread. This MUST + * ONLY be invoked if the task has not already + * completed, since after the completion callback + * is invoked, @task will have been freed. + * + * To avoid racing with execution of the completion + * callback provided with qio_task_new, this method + * MUST ONLY be invoked from the thread that is + * running the main loop associated with @context + * parameter to qio_task_run_in_thread. + * + * When the thread has completed, the completion + * callback provided to qio_task_new will be invoked. + * When that callback returns @task will be freed, + * so @task must not be referenced after this + * method completes. + */ +void qio_task_wait_thread(QIOTask *task); + + /** * qio_task_complete: * @task: the task struct diff --git a/include/migration/colo.h b/include/migration/colo.h index 2fe48ad353..99ce17aca7 100644 --- a/include/migration/colo.h +++ b/include/migration/colo.h @@ -16,14 +16,21 @@ #include "qemu-common.h" #include "qapi/qapi-types-migration.h" +enum colo_event { + COLO_EVENT_NONE, + COLO_EVENT_CHECKPOINT, + COLO_EVENT_FAILOVER, +}; + void colo_info_init(void); void migrate_start_colo_process(MigrationState *s); bool migration_in_colo_state(void); /* loadvm */ -bool migration_incoming_enable_colo(void); -void migration_incoming_exit_colo(void); +void migration_incoming_enable_colo(void); +void migration_incoming_disable_colo(void); +bool migration_incoming_colo_enabled(void); void *colo_process_incoming_thread(void *opaque); bool migration_incoming_in_colo_state(void); diff --git a/include/migration/misc.h b/include/migration/misc.h index 4ebf24c6c2..5cdbabd094 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -14,11 +14,34 @@ #ifndef MIGRATION_MISC_H #define MIGRATION_MISC_H +#include "exec/cpu-common.h" #include "qemu/notify.h" +#include "qapi/qapi-types-net.h" /* migration/ram.c */ +typedef enum PrecopyNotifyReason { + PRECOPY_NOTIFY_SETUP = 0, + PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC = 1, + PRECOPY_NOTIFY_AFTER_BITMAP_SYNC = 2, + PRECOPY_NOTIFY_COMPLETE = 3, + PRECOPY_NOTIFY_CLEANUP = 4, + PRECOPY_NOTIFY_MAX = 5, +} PrecopyNotifyReason; + +typedef struct PrecopyNotifyData { + enum PrecopyNotifyReason reason; + Error **errp; +} PrecopyNotifyData; + +void precopy_infrastructure_init(void); +void precopy_add_notifier(NotifierWithReturn *n); +void precopy_remove_notifier(NotifierWithReturn *n); +int precopy_notify(PrecopyNotifyReason reason, Error **errp); +void precopy_enable_free_page_optimization(void); + void ram_mig_init(void); +void qemu_guest_free_page_hint(void *addr, size_t len); /* migration/block.c */ @@ -28,23 +51,14 @@ void blk_mig_init(void); static inline void blk_mig_init(void) {} #endif -#define SELF_ANNOUNCE_ROUNDS 5 - -static inline -int64_t self_announce_delay(int round) -{ - assert(round < SELF_ANNOUNCE_ROUNDS && round > 0); - /* delay 50ms, 150ms, 250ms, ... */ - return 50 + (SELF_ANNOUNCE_ROUNDS - round - 1) * 100; -} - +AnnounceParameters *migrate_announce_params(void); /* migration/savevm.c */ void dump_vmstate_json_to_file(FILE *out_fp); /* migration/migration.c */ void migration_object_init(void); -void migration_object_finalize(void); +void migration_shutdown(void); void qemu_start_incoming_migration(const char *uri, Error **errp); bool migration_is_idle(void); void add_migration_state_change_notifier(Notifier *notify); diff --git a/include/migration/qemu-file-types.h b/include/migration/qemu-file-types.h index bd6d7dd7f9..bbe04d4484 100644 --- a/include/migration/qemu-file-types.h +++ b/include/migration/qemu-file-types.h @@ -25,6 +25,8 @@ #ifndef QEMU_FILE_H #define QEMU_FILE_H +int qemu_file_get_error(QEMUFile *f); + void qemu_put_buffer(QEMUFile *f, const uint8_t *buf, size_t size); void qemu_put_byte(QEMUFile *f, int v); diff --git a/include/migration/register.h b/include/migration/register.h index d287f4c317..3d0b9833c6 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -72,7 +72,7 @@ int register_savevm_live(DeviceState *dev, const char *idstr, int instance_id, int version_id, - SaveVMHandlers *ops, + const SaveVMHandlers *ops, void *opaque); void unregister_savevm(DeviceState *dev, const char *idstr, void *opaque); diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index 42b946ce90..a668ec75b8 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -40,8 +40,8 @@ typedef struct VMStateField VMStateField; */ struct VMStateInfo { const char *name; - int (*get)(QEMUFile *f, void *pv, size_t size, VMStateField *field); - int (*put)(QEMUFile *f, void *pv, size_t size, VMStateField *field, + int (*get)(QEMUFile *f, void *pv, size_t size, const VMStateField *field); + int (*put)(QEMUFile *f, void *pv, size_t size, const VMStateField *field, QJSON *vmdesc); }; @@ -185,8 +185,9 @@ struct VMStateDescription { int (*pre_load)(void *opaque); int (*post_load)(void *opaque, int version_id); int (*pre_save)(void *opaque); + int (*post_save)(void *opaque); bool (*needed)(void *opaque); - VMStateField *fields; + const VMStateField *fields; const VMStateDescription **subsections; }; @@ -850,6 +851,9 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_INT32_POSITIVE_LE(_f, _s) \ VMSTATE_SINGLE(_f, _s, 0, vmstate_info_int32_le, int32_t) +#define VMSTATE_BOOL_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_bool, bool) + #define VMSTATE_INT8_TEST(_f, _s, _t) \ VMSTATE_SINGLE_TEST(_f, _s, _t, 0, vmstate_info_int8, int8_t) @@ -923,6 +927,9 @@ extern const VMStateInfo vmstate_info_qtailq; #define VMSTATE_UINT16_ARRAY(_f, _s, _n) \ VMSTATE_UINT16_ARRAY_V(_f, _s, _n, 0) +#define VMSTATE_UINT16_SUB_ARRAY(_f, _s, _start, _num) \ + VMSTATE_SUB_ARRAY(_f, _s, _start, _num, 0, vmstate_info_uint16, uint16_t) + #define VMSTATE_UINT16_2DARRAY(_f, _s, _n1, _n2) \ VMSTATE_UINT16_2DARRAY_V(_f, _s, _n1, _n2, 0) diff --git a/include/monitor/monitor.h b/include/monitor/monitor.h index 2ef5e04b37..c1b40a9cac 100644 --- a/include/monitor/monitor.h +++ b/include/monitor/monitor.h @@ -13,7 +13,8 @@ extern __thread Monitor *cur_mon; #define MONITOR_USE_READLINE 0x02 #define MONITOR_USE_CONTROL 0x04 #define MONITOR_USE_PRETTY 0x08 -#define MONITOR_USE_OOB 0x10 + +#define QMP_REQ_QUEUE_LEN_MAX 8 bool monitor_cur_is_qmp(void); @@ -47,4 +48,7 @@ int monitor_fdset_dup_fd_add(int64_t fdset_id, int dup_fd); void monitor_fdset_dup_fd_remove(int dup_fd); int monitor_fdset_dup_fd_find(int dup_fd); +void monitor_vfprintf(FILE *stream, + const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); + #endif /* MONITOR_H */ diff --git a/include/net/announce.h b/include/net/announce.h new file mode 100644 index 0000000000..892d302b65 --- /dev/null +++ b/include/net/announce.h @@ -0,0 +1,41 @@ +/* + * Self-announce facility + * (c) 2017-2019 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 QEMU_NET_ANNOUNCE_H +#define QEMU_NET_ANNOUNCE_H + +#include "qemu-common.h" +#include "qapi/qapi-types-net.h" +#include "qemu/timer.h" + +struct AnnounceTimer { + QEMUTimer *tm; + AnnounceParameters params; + QEMUClockType type; + int round; +}; + +/* Returns: update the timer to the next time point */ +int64_t qemu_announce_timer_step(AnnounceTimer *timer); + +/* Delete the underlying timer */ +void qemu_announce_timer_del(AnnounceTimer *timer); + +/* + * Under BQL/main thread + * Reset the timer to the given parameters/type/notifier. + */ +void qemu_announce_timer_reset(AnnounceTimer *timer, + AnnounceParameters *params, + QEMUClockType type, + QEMUTimerCB *cb, + void *opaque); + +void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params); + +#endif diff --git a/include/net/eth.h b/include/net/eth.h index e6dc8a7ba0..7f45c678e7 100644 --- a/include/net/eth.h +++ b/include/net/eth.h @@ -177,6 +177,8 @@ struct tcp_hdr { #define TH_PUSH 0x08 #define TH_ACK 0x10 #define TH_URG 0x20 +#define TH_ECE 0x40 +#define TH_CWR 0x80 u_short th_win; /* window */ u_short th_sum; /* checksum */ u_short th_urp; /* urgent pointer */ diff --git a/include/net/filter.h b/include/net/filter.h index 435acd6f82..49da666ac0 100644 --- a/include/net/filter.h +++ b/include/net/filter.h @@ -38,6 +38,8 @@ typedef ssize_t (FilterReceiveIOV)(NetFilterState *nc, typedef void (FilterStatusChanged) (NetFilterState *nf, Error **errp); +typedef void (FilterHandleEvent) (NetFilterState *nf, int event, Error **errp); + typedef struct NetFilterClass { ObjectClass parent_class; @@ -45,6 +47,7 @@ typedef struct NetFilterClass { FilterSetup *setup; FilterCleanup *cleanup; FilterStatusChanged *status_changed; + FilterHandleEvent *handle_event; /* mandatory */ FilterReceiveIOV *receive_iov; } NetFilterClass; @@ -77,4 +80,6 @@ ssize_t qemu_netfilter_pass_to_next(NetClientState *sender, int iovcnt, void *opaque); +void colo_notify_filters_event(int event, Error **errp); + #endif /* QEMU_NET_FILTER_H */ diff --git a/include/net/net.h b/include/net/net.h index 1425960f76..acf0451fc4 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -60,6 +60,7 @@ typedef int (SetVnetLE)(NetClientState *, bool); typedef int (SetVnetBE)(NetClientState *, bool); typedef struct SocketReadState SocketReadState; typedef void (SocketReadStateFinalize)(SocketReadState *rs); +typedef void (NetAnnounce)(NetClientState *); typedef struct NetClientInfo { NetClientDriver type; @@ -80,6 +81,7 @@ typedef struct NetClientInfo { SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; SetVnetBE *set_vnet_be; + NetAnnounce *announce; } NetClientInfo; struct NetClientState { @@ -97,7 +99,7 @@ struct NetClientState { unsigned rxfilter_notify_enabled:1; int vring_enable; int vnet_hdr_len; - QTAILQ_HEAD(NetFilterHead, NetFilterState) filters; + QTAILQ_HEAD(, NetFilterState) filters; }; typedef struct NICState { @@ -146,7 +148,7 @@ ssize_t qemu_sendv_packet(NetClientState *nc, const struct iovec *iov, int iovcnt); ssize_t qemu_sendv_packet_async(NetClientState *nc, const struct iovec *iov, int iovcnt, NetPacketSent *sent_cb); -void qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size); +ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size); ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size); ssize_t qemu_send_packet_async(NetClientState *nc, const uint8_t *buf, int size, NetPacketSent *sent_cb); @@ -169,12 +171,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_iov(NetClientState *sender, - unsigned flags, - const struct iovec *iov, - int iovcnt, - void *opaque); - void print_net_client(Monitor *mon, NetClientState *nc); void hmp_info_network(Monitor *mon, const QDict *qdict); void net_socket_rs_init(SocketReadState *rs, @@ -201,9 +197,6 @@ extern NICInfo nd_table[MAX_NICS]; extern const char *host_net_devices[]; /* from net.c */ -extern const char *legacy_tftp_prefix; -extern const char *legacy_bootp_filename; - int net_client_parse(QemuOptsList *opts_list, const char *str); int net_init_clients(Error **errp); void net_check_clients(void); diff --git a/include/net/slirp.h b/include/net/slirp.h index 4d63d74da4..bad3e1e241 100644 --- a/include/net/slirp.h +++ b/include/net/slirp.h @@ -30,10 +30,6 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict); void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict); -int net_slirp_redir(const char *redir_str); - -int net_slirp_smb(const char *exported_dir); - void hmp_info_usernet(Monitor *mon, const QDict *qdict); #endif diff --git a/include/qapi/error.h b/include/qapi/error.h index bcb86a79f5..51b63dd4b5 100644 --- a/include/qapi/error.h +++ b/include/qapi/error.h @@ -52,8 +52,12 @@ * where Error **errp is a parameter, by convention the last one. * * Pass an existing error to the caller with the message modified: + * error_propagate_prepend(errp, err); + * + * Avoid * error_propagate(errp, err); * error_prepend(errp, "Could not frobnicate '%s': ", name); + * because this fails to prepend when @errp is &error_fatal. * * Create a new error and pass it to the caller: * error_setg(errp, "situation normal, all fouled up"); @@ -215,6 +219,16 @@ void error_setg_win32_internal(Error **errp, */ void error_propagate(Error **dst_errp, Error *local_err); + +/* + * Propagate error object (if any) with some text prepended. + * Behaves like + * error_prepend(&local_err, fmt, ...); + * error_propagate(dst_errp, local_err); + */ +void error_propagate_prepend(Error **dst_errp, Error *local_err, + const char *fmt, ...); + /* * Prepend some text to @errp's human-readable error message. * The text is made by formatting @fmt, @ap like vprintf(). diff --git a/include/qapi/qmp-event.h b/include/qapi/qmp-event.h index 0c87ad833e..b60f1d3a89 100644 --- a/include/qapi/qmp-event.h +++ b/include/qapi/qmp-event.h @@ -14,12 +14,5 @@ #ifndef QMP_EVENT_H #define QMP_EVENT_H - -typedef void (*QMPEventFuncEmit)(unsigned event, QDict *dict, Error **errp); - -void qmp_event_set_func_emit(QMPEventFuncEmit emit); - -QMPEventFuncEmit qmp_event_get_func_emit(void); - QDict *qmp_event_build_dict(const char *event_name); #endif diff --git a/include/qapi/qmp/dispatch.h b/include/qapi/qmp/dispatch.h index 4e2e749faf..9aa426a398 100644 --- a/include/qapi/qmp/dispatch.h +++ b/include/qapi/qmp/dispatch.h @@ -39,7 +39,6 @@ typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList; void qmp_register_command(QmpCommandList *cmds, const char *name, QmpCommandFunc *fn, QmpCommandOptions options); -void qmp_unregister_command(QmpCommandList *cmds, const char *name); QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name); void qmp_disable_command(QmpCommandList *cmds, const char *name); void qmp_enable_command(QmpCommandList *cmds, const char *name); @@ -50,7 +49,7 @@ bool qmp_has_success_response(const QmpCommand *cmd); QDict *qmp_error_response(Error *err); QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, bool allow_oob); -bool qmp_is_oob(QDict *dict); +bool qmp_is_oob(const QDict *dict); typedef void (*qmp_cmd_callback_fn)(QmpCommand *cmd, void *opaque); diff --git a/include/qapi/qmp/json-lexer.h b/include/qapi/qmp/json-lexer.h deleted file mode 100644 index afee7828cd..0000000000 --- a/include/qapi/qmp/json-lexer.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * JSON lexer - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * 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 QEMU_JSON_LEXER_H -#define QEMU_JSON_LEXER_H - - -typedef enum json_token_type { - JSON_MIN = 100, - JSON_LCURLY = JSON_MIN, - JSON_RCURLY, - JSON_LSQUARE, - JSON_RSQUARE, - JSON_COLON, - JSON_COMMA, - JSON_INTEGER, - JSON_FLOAT, - JSON_KEYWORD, - JSON_STRING, - JSON_ESCAPE, - JSON_SKIP, - JSON_ERROR, -} JSONTokenType; - -typedef struct JSONLexer JSONLexer; - -typedef void (JSONLexerEmitter)(JSONLexer *, GString *, - JSONTokenType, int x, int y); - -struct JSONLexer -{ - JSONLexerEmitter *emit; - int state; - GString *token; - int x, y; -}; - -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func); - -int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); - -int json_lexer_flush(JSONLexer *lexer); - -void json_lexer_destroy(JSONLexer *lexer); - -#endif diff --git a/include/qapi/qmp/json-parser.h b/include/qapi/qmp/json-parser.h index 102f5c0068..7345a9bd5c 100644 --- a/include/qapi/qmp/json-parser.h +++ b/include/qapi/qmp/json-parser.h @@ -1,5 +1,5 @@ /* - * JSON Parser + * JSON Parser * * Copyright IBM, Corp. 2009 * @@ -11,12 +11,36 @@ * */ -#ifndef QEMU_JSON_PARSER_H -#define QEMU_JSON_PARSER_H +#ifndef QAPI_QMP_JSON_PARSER_H +#define QAPI_QMP_JSON_PARSER_H -#include "qemu-common.h" +typedef struct JSONLexer { + int start_state, state; + GString *token; + int x, y; +} JSONLexer; -QObject *json_parser_parse(GQueue *tokens, va_list *ap); -QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp); +typedef struct JSONMessageParser { + void (*emit)(void *opaque, QObject *json, Error *err); + void *opaque; + va_list *ap; + JSONLexer lexer; + int brace_count; + int bracket_count; + GQueue tokens; + uint64_t token_size; +} JSONMessageParser; + +void json_message_parser_init(JSONMessageParser *parser, + void (*emit)(void *opaque, QObject *json, + Error *err), + void *opaque, va_list *ap); + +void json_message_parser_feed(JSONMessageParser *parser, + const char *buffer, size_t size); + +void json_message_parser_flush(JSONMessageParser *parser); + +void json_message_parser_destroy(JSONMessageParser *parser); #endif diff --git a/include/qapi/qmp/json-streamer.h b/include/qapi/qmp/json-streamer.h deleted file mode 100644 index 00d8a23af8..0000000000 --- a/include/qapi/qmp/json-streamer.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * JSON streaming support - * - * Copyright IBM, Corp. 2009 - * - * Authors: - * Anthony Liguori - * - * 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 QEMU_JSON_STREAMER_H -#define QEMU_JSON_STREAMER_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, GQueue *tokens); - JSONLexer lexer; - int brace_count; - int bracket_count; - GQueue *tokens; - uint64_t token_size; -} JSONMessageParser; - -void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, GQueue *)); - -int json_message_parser_feed(JSONMessageParser *parser, - const char *buffer, size_t size); - -int json_message_parser_flush(JSONMessageParser *parser); - -void json_message_parser_destroy(JSONMessageParser *parser); - -#endif diff --git a/include/qapi/qmp/qerror.h b/include/qapi/qmp/qerror.h index c82360f429..7c76e24aa7 100644 --- a/include/qapi/qmp/qerror.h +++ b/include/qapi/qmp/qerror.h @@ -61,9 +61,6 @@ #define QERR_IO_ERROR \ "An IO error has occurred" -#define QERR_JSON_PARSING \ - "Invalid JSON syntax" - #define QERR_MIGRATION_ACTIVE \ "There's a migration process in progress" @@ -82,6 +79,9 @@ #define QERR_QGA_COMMAND_FAILED \ "Guest agent command failed, error was '%s'" +#define QERR_REPLAY_NOT_SUPPORTED \ + "Record/replay feature is not supported for '%s'" + #define QERR_SET_PASSWD_FAILED \ "Could not set password" @@ -91,7 +91,4 @@ #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/include/qapi/qmp/qjson.h b/include/qapi/qmp/qjson.h index 43b2ce2f33..5ebbe5a118 100644 --- a/include/qapi/qmp/qjson.h +++ b/include/qapi/qmp/qjson.h @@ -15,11 +15,15 @@ #define QJSON_H QObject *qobject_from_json(const char *string, Error **errp); -QObject *qobject_from_jsonf(const char *string, ...) GCC_FMT_ATTR(1, 2); -QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp) - GCC_FMT_ATTR(1, 0); -QDict *qdict_from_jsonf_nofail(const char *string, ...) GCC_FMT_ATTR(1, 2); +QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) + GCC_FMT_ATTR(1, 0); +QObject *qobject_from_jsonf_nofail(const char *string, ...) + GCC_FMT_ATTR(1, 2); +QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) + GCC_FMT_ATTR(1, 0); +QDict *qdict_from_jsonf_nofail(const char *string, ...) + GCC_FMT_ATTR(1, 2); QString *qobject_to_json(const QObject *obj); QString *qobject_to_json_pretty(const QObject *obj); diff --git a/include/qapi/qmp/qnum.h b/include/qapi/qmp/qnum.h index 45bf02a036..bbae0a5ec8 100644 --- a/include/qapi/qmp/qnum.h +++ b/include/qapi/qmp/qnum.h @@ -25,7 +25,7 @@ typedef enum { /* * QNum encapsulates how our dialect of JSON fills in the blanks left - * by the JSON specification (RFC 7159) regarding numbers. + * by the JSON specification (RFC 8259) regarding numbers. * * Conceptually, we treat number as an abstract type with three * concrete subtypes: floating-point, signed integer, unsigned diff --git a/include/qapi/string-input-visitor.h b/include/qapi/string-input-visitor.h index 33551340e3..921f3875b9 100644 --- a/include/qapi/string-input-visitor.h +++ b/include/qapi/string-input-visitor.h @@ -19,8 +19,8 @@ typedef struct StringInputVisitor StringInputVisitor; /* * The string input visitor does not implement support for visiting - * QAPI structs, alternates, null, or arbitrary QTypes. It also - * requires a non-null list argument to visit_start_list(). + * QAPI structs, alternates, null, or arbitrary QTypes. Only flat lists + * of integers (except type "size") are supported. */ Visitor *string_input_visitor_new(const char *str); diff --git a/include/qemu-common.h b/include/qemu-common.h index 85f4749aef..a102245519 100644 --- a/include/qemu-common.h +++ b/include/qemu-common.h @@ -17,7 +17,7 @@ #define TFR(expr) do { if ((expr) != -1) break; } while (errno == EINTR) /* Copyright string for -version arguments, About dialogs, etc */ -#define QEMU_COPYRIGHT "Copyright (c) 2003-2017 " \ +#define QEMU_COPYRIGHT "Copyright (c) 2003-2019 " \ "Fabrice Bellard and the QEMU Project developers" /* Bug reporting information for --help arguments, About dialogs, etc */ diff --git a/include/qemu/atomic.h b/include/qemu/atomic.h index 9ed39effd3..a6ac188188 100644 --- a/include/qemu/atomic.h +++ b/include/qemu/atomic.h @@ -98,10 +98,11 @@ * We'd prefer not want to pull in everything else TCG related, so handle * those few cases by hand. * - * Note that x32 is fully detected with __x64_64__ + _ILP32, and that for - * Sparc we always force the use of sparcv9 in configure. + * Note that x32 is fully detected with __x86_64__ + _ILP32, and that for + * Sparc we always force the use of sparcv9 in configure. MIPS n32 (ILP32) & + * n64 (LP64) ABIs are both detected using __mips64. */ -#if defined(__x86_64__) || defined(__sparc__) +#if defined(__x86_64__) || defined(__sparc__) || defined(__mips64) # define ATOMIC_REG_SIZE 8 #else # define ATOMIC_REG_SIZE sizeof(void *) @@ -450,4 +451,38 @@ _oldn; \ }) +/* Abstractions to access atomically (i.e. "once") i64/u64 variables */ +#ifdef CONFIG_ATOMIC64 +static inline int64_t atomic_read_i64(const int64_t *ptr) +{ + /* use __nocheck because sizeof(void *) might be < sizeof(u64) */ + return atomic_read__nocheck(ptr); +} + +static inline uint64_t atomic_read_u64(const uint64_t *ptr) +{ + return atomic_read__nocheck(ptr); +} + +static inline void atomic_set_i64(int64_t *ptr, int64_t val) +{ + atomic_set__nocheck(ptr, val); +} + +static inline void atomic_set_u64(uint64_t *ptr, uint64_t val) +{ + atomic_set__nocheck(ptr, val); +} + +static inline void atomic64_init(void) +{ +} +#else /* !CONFIG_ATOMIC64 */ +int64_t atomic_read_i64(const int64_t *ptr); +uint64_t atomic_read_u64(const uint64_t *ptr); +void atomic_set_i64(int64_t *ptr, int64_t val); +void atomic_set_u64(uint64_t *ptr, uint64_t val); +void atomic64_init(void); +#endif /* !CONFIG_ATOMIC64 */ + #endif /* QEMU_ATOMIC_H */ diff --git a/include/qemu/atomic128.h b/include/qemu/atomic128.h new file mode 100644 index 0000000000..ddd0d55d31 --- /dev/null +++ b/include/qemu/atomic128.h @@ -0,0 +1,153 @@ +/* + * Simple interface for 128-bit atomic operations. + * + * Copyright (C) 2018 Linaro, Ltd. + * + * 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/devel/atomics.txt for discussion about the guarantees each + * atomic primitive is meant to provide. + */ + +#ifndef QEMU_ATOMIC128_H +#define QEMU_ATOMIC128_H + +/* + * GCC is a house divided about supporting large atomic operations. + * + * For hosts that only have large compare-and-swap, a legalistic reading + * of the C++ standard means that one cannot implement __atomic_read on + * read-only memory, and thus all atomic operations must synchronize + * through libatomic. + * + * See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=80878 + * + * This interpretation is not especially helpful for QEMU. + * For softmmu, all RAM is always read/write from the hypervisor. + * For user-only, if the guest doesn't implement such an __atomic_read + * then the host need not worry about it either. + * + * Moreover, using libatomic is not an option, because its interface is + * built for std::atomic, and requires that *all* accesses to such an + * object go through the library. In our case we do not have an object + * in the C/C++ sense, but a view of memory as seen by the guest. + * The guest may issue a large atomic operation and then access those + * pieces using word-sized accesses. From the hypervisor, we have no + * way to connect those two actions. + * + * Therefore, special case each platform. + */ + +#if defined(CONFIG_ATOMIC128) +static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + return atomic_cmpxchg__nocheck(ptr, cmp, new); +} +# define HAVE_CMPXCHG128 1 +#elif defined(CONFIG_CMPXCHG128) +static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + return __sync_val_compare_and_swap_16(ptr, cmp, new); +} +# define HAVE_CMPXCHG128 1 +#elif defined(__aarch64__) +/* Through gcc 8, aarch64 has no support for 128-bit at all. */ +static inline Int128 atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new) +{ + uint64_t cmpl = int128_getlo(cmp), cmph = int128_gethi(cmp); + uint64_t newl = int128_getlo(new), newh = int128_gethi(new); + uint64_t oldl, oldh; + uint32_t tmp; + + asm("0: ldaxp %[oldl], %[oldh], %[mem]\n\t" + "cmp %[oldl], %[cmpl]\n\t" + "ccmp %[oldh], %[cmph], #0, eq\n\t" + "b.ne 1f\n\t" + "stlxp %w[tmp], %[newl], %[newh], %[mem]\n\t" + "cbnz %w[tmp], 0b\n" + "1:" + : [mem] "+m"(*ptr), [tmp] "=&r"(tmp), + [oldl] "=&r"(oldl), [oldh] "=&r"(oldh) + : [cmpl] "r"(cmpl), [cmph] "r"(cmph), + [newl] "r"(newl), [newh] "r"(newh) + : "memory", "cc"); + + return int128_make128(oldl, oldh); +} +# define HAVE_CMPXCHG128 1 +#else +/* Fallback definition that must be optimized away, or error. */ +Int128 QEMU_ERROR("unsupported atomic") + atomic16_cmpxchg(Int128 *ptr, Int128 cmp, Int128 new); +# define HAVE_CMPXCHG128 0 +#endif /* Some definition for HAVE_CMPXCHG128 */ + + +#if defined(CONFIG_ATOMIC128) +static inline Int128 atomic16_read(Int128 *ptr) +{ + return atomic_read__nocheck(ptr); +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + atomic_set__nocheck(ptr, val); +} + +# define HAVE_ATOMIC128 1 +#elif !defined(CONFIG_USER_ONLY) && defined(__aarch64__) +/* We can do better than cmpxchg for AArch64. */ +static inline Int128 atomic16_read(Int128 *ptr) +{ + uint64_t l, h; + uint32_t tmp; + + /* The load must be paired with the store to guarantee not tearing. */ + asm("0: ldxp %[l], %[h], %[mem]\n\t" + "stxp %w[tmp], %[l], %[h], %[mem]\n\t" + "cbnz %w[tmp], 0b" + : [mem] "+m"(*ptr), [tmp] "=r"(tmp), [l] "=r"(l), [h] "=r"(h)); + + return int128_make128(l, h); +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + uint64_t l = int128_getlo(val), h = int128_gethi(val); + uint64_t t1, t2; + + /* Load into temporaries to acquire the exclusive access lock. */ + asm("0: ldxp %[t1], %[t2], %[mem]\n\t" + "stxp %w[t1], %[l], %[h], %[mem]\n\t" + "cbnz %w[t1], 0b" + : [mem] "+m"(*ptr), [t1] "=&r"(t1), [t2] "=&r"(t2) + : [l] "r"(l), [h] "r"(h)); +} + +# define HAVE_ATOMIC128 1 +#elif !defined(CONFIG_USER_ONLY) && HAVE_CMPXCHG128 +static inline Int128 atomic16_read(Int128 *ptr) +{ + /* Maybe replace 0 with 0, returning the old value. */ + return atomic16_cmpxchg(ptr, 0, 0); +} + +static inline void atomic16_set(Int128 *ptr, Int128 val) +{ + Int128 old = *ptr, cmp; + do { + cmp = old; + old = atomic16_cmpxchg(ptr, cmp, val); + } while (old != cmp); +} + +# define HAVE_ATOMIC128 1 +#else +/* Fallback definitions that must be optimized away, or error. */ +Int128 QEMU_ERROR("unsupported atomic") atomic16_read(Int128 *ptr); +void QEMU_ERROR("unsupported atomic") atomic16_set(Int128 *ptr, Int128 val); +# define HAVE_ATOMIC128 0 +#endif /* Some definition for HAVE_ATOMIC128 */ + +#endif /* QEMU_ATOMIC128_H */ diff --git a/include/qemu/bitmap.h b/include/qemu/bitmap.h index 509eeddece..5c313346b9 100644 --- a/include/qemu/bitmap.h +++ b/include/qemu/bitmap.h @@ -221,6 +221,10 @@ static inline int bitmap_intersects(const unsigned long *src1, static inline long bitmap_count_one(const unsigned long *bitmap, long nbits) { + if (unlikely(!nbits)) { + return 0; + } + if (small_nbits(nbits)) { return ctpopl(*bitmap & BITMAP_LAST_WORD_MASK(nbits)); } else { @@ -228,6 +232,19 @@ static inline long bitmap_count_one(const unsigned long *bitmap, long nbits) } } +static inline long bitmap_count_one_with_offset(const unsigned long *bitmap, + long offset, long nbits) +{ + long aligned_offset = QEMU_ALIGN_DOWN(offset, BITS_PER_LONG); + long redundant_bits = offset - aligned_offset; + long bits_to_count = nbits + redundant_bits; + const unsigned long *bitmap_start = bitmap + + aligned_offset / BITS_PER_LONG; + + return bitmap_count_one(bitmap_start, bits_to_count) - + bitmap_count_one(bitmap_start, redundant_bits); +} + void bitmap_set(unsigned long *map, long i, long len); void bitmap_set_atomic(unsigned long *map, long i, long len); void bitmap_clear(unsigned long *map, long start, long nr); diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index a684c1a7a2..2a9f3fe783 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -255,9 +255,9 @@ typedef union { /* * the generic syntax is: * - * load: ld{type}{sign}{size}{endian}_p(ptr) + * load: ld{type}{sign}{size}_{endian}_p(ptr) * - * store: st{type}{size}{endian}_p(ptr, val) + * store: st{type}{size}_{endian}_p(ptr, val) * * Note there are small differences with the softmmu access API! * @@ -293,10 +293,10 @@ typedef union { * * For cases where the size to be used is not fixed at compile time, * there are - * stn{endian}_p(ptr, sz, val) + * stn_{endian}_p(ptr, sz, val) * which stores @val to @ptr as an @endian-order number @sz bytes in size * and - * ldn{endian}_p(ptr, sz) + * ldn_{endian}_p(ptr, sz) * which loads @sz bytes from @ptr as an unsigned @endian-order number * and returns it in a uint64_t. */ @@ -316,51 +316,57 @@ static inline void stb_p(void *ptr, uint8_t v) *(uint8_t *)ptr = v; } -/* Any compiler worth its salt will turn these memcpy into native unaligned - operations. Thus we don't need to play games with packed attributes, or - inline byte-by-byte stores. */ +/* + * Any compiler worth its salt will turn these memcpy into native unaligned + * operations. Thus we don't need to play games with packed attributes, or + * inline byte-by-byte stores. + * Some compilation environments (eg some fortify-source implementations) + * may intercept memcpy() in a way that defeats the compiler optimization, + * though, so we use __builtin_memcpy() to give ourselves the best chance + * of good performance. + */ static inline int lduw_he_p(const void *ptr) { uint16_t r; - memcpy(&r, ptr, sizeof(r)); + __builtin_memcpy(&r, ptr, sizeof(r)); return r; } static inline int ldsw_he_p(const void *ptr) { int16_t r; - memcpy(&r, ptr, sizeof(r)); + __builtin_memcpy(&r, ptr, sizeof(r)); return r; } static inline void stw_he_p(void *ptr, uint16_t v) { - memcpy(ptr, &v, sizeof(v)); + __builtin_memcpy(ptr, &v, sizeof(v)); } static inline int ldl_he_p(const void *ptr) { int32_t r; - memcpy(&r, ptr, sizeof(r)); + __builtin_memcpy(&r, ptr, sizeof(r)); return r; } static inline void stl_he_p(void *ptr, uint32_t v) { - memcpy(ptr, &v, sizeof(v)); + __builtin_memcpy(ptr, &v, sizeof(v)); } static inline uint64_t ldq_he_p(const void *ptr) { uint64_t r; - memcpy(&r, ptr, sizeof(r)); + __builtin_memcpy(&r, ptr, sizeof(r)); return r; } static inline void stq_he_p(void *ptr, uint64_t v) { - memcpy(ptr, &v, sizeof(v)); + __builtin_memcpy(ptr, &v, sizeof(v)); } static inline int lduw_le_p(const void *ptr) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 5843812710..296b2fd572 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -28,12 +28,6 @@ #define QEMU_SENTINEL __attribute__((sentinel)) -#if QEMU_GNUC_PREREQ(4, 3) -#define QEMU_ARTIFICIAL __attribute__((always_inline, artificial)) -#else -#define QEMU_ARTIFICIAL -#endif - #if defined(_WIN32) # define QEMU_PACKED __attribute__((gcc_struct, packed)) #else @@ -119,9 +113,63 @@ #define GCC_FMT_ATTR(n, m) #endif +#ifndef __has_warning +#define __has_warning(x) 0 /* compatibility with non-clang compilers */ +#endif + #ifndef __has_feature #define __has_feature(x) 0 /* compatibility with non-clang compilers */ #endif + +#ifndef __has_builtin +#define __has_builtin(x) 0 /* compatibility with non-clang compilers */ +#endif + +#if __has_builtin(__builtin_assume_aligned) || !defined(__clang__) +#define HAS_ASSUME_ALIGNED +#endif + +#ifndef __has_attribute +#define __has_attribute(x) 0 /* compatibility with older GCC */ +#endif + +/* + * GCC doesn't provide __has_attribute() until GCC 5, but we know all the GCC + * versions we support have the "flatten" attribute. Clang may not have the + * "flatten" attribute but always has __has_attribute() to check for it. + */ +#if __has_attribute(flatten) || !defined(__clang__) +# define QEMU_FLATTEN __attribute__((flatten)) +#else +# define QEMU_FLATTEN +#endif + +/* + * If __attribute__((error)) is present, use it to produce an error at + * compile time. Otherwise, one must wait for the linker to diagnose + * the missing symbol. + */ +#if __has_attribute(error) +# define QEMU_ERROR(X) __attribute__((error(X))) +#else +# define QEMU_ERROR(X) +#endif + +/* + * The nonstring variable attribute specifies that an object or member + * declaration with type array of char or pointer to char is intended + * to store character arrays that do not necessarily contain a terminating + * NUL character. This is useful in detecting uses of such arrays or pointers + * with functions that expect NUL-terminated strings, and to avoid warnings + * when such an array or pointer is used as an argument to a bounded string + * manipulation function such as strncpy. + */ +#if __has_attribute(nonstring) +# define QEMU_NONSTRING __attribute__((nonstring)) +#else +# define QEMU_NONSTRING +#endif + /* Implement C11 _Generic via GCC builtins. Example: * * QEMU_GENERIC(x, (float, sinf), (long double, sinl), sin) (x) diff --git a/include/qemu/coroutine.h b/include/qemu/coroutine.h index 6f8a487041..9801e7f5a4 100644 --- a/include/qemu/coroutine.h +++ b/include/qemu/coroutine.h @@ -89,6 +89,11 @@ void qemu_aio_coroutine_enter(AioContext *ctx, Coroutine *co); */ void coroutine_fn qemu_coroutine_yield(void); +/** + * Get the AioContext of the given coroutine + */ +AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co); + /** * Get the currently executing coroutine */ diff --git a/include/qemu/cutils.h b/include/qemu/cutils.h index 47aaa3b0b9..d2dad3057c 100644 --- a/include/qemu/cutils.h +++ b/include/qemu/cutils.h @@ -146,14 +146,16 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, int64_t *result); int qemu_strtou64(const char *nptr, const char **endptr, int base, uint64_t *result); +int qemu_strtod(const char *nptr, const char **endptr, double *result); +int qemu_strtod_finite(const char *nptr, const char **endptr, double *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); -int qemu_strtosz(const char *nptr, char **end, uint64_t *result); -int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result); -int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result); +int qemu_strtosz(const char *nptr, const char **end, uint64_t *result); +int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result); +int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result); /* used to print char* safely */ #define STR_OR_NULL(str) ((str) ? (str) : "null") @@ -169,4 +171,16 @@ bool test_buffer_is_zero_next_accel(void); int uleb128_encode_small(uint8_t *out, uint32_t n); int uleb128_decode_small(const uint8_t *in, uint32_t *n); +/** + * qemu_pstrcmp0: + * @str1: a non-NULL pointer to a C string (*str1 can be NULL) + * @str2: a non-NULL pointer to a C string (*str2 can be NULL) + * + * Compares *str1 and *str2 with g_strcmp0(). + * + * Returns: an integer less than, equal to, or greater than zero, if + * *str1 is <, == or > than *str2. + */ +int qemu_pstrcmp0(const char **str1, const char **str2); + #endif diff --git a/include/qemu/drm.h b/include/qemu/drm.h new file mode 100644 index 0000000000..4c3e622f5c --- /dev/null +++ b/include/qemu/drm.h @@ -0,0 +1,6 @@ +#ifndef QEMU_DRM_H_ +#define QEMU_DRM_H_ + +int qemu_drm_rendernode_open(const char *rendernode); + +#endif diff --git a/include/qemu/error-report.h b/include/qemu/error-report.h index e1c8ae1a52..0a8d9cc9ea 100644 --- a/include/qemu/error-report.h +++ b/include/qemu/error-report.h @@ -44,6 +44,33 @@ void error_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void warn_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); void info_report(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +bool error_report_once_cond(bool *printed, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); +bool warn_report_once_cond(bool *printed, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); + +/* + * Similar to error_report(), except it prints the message just once. + * Return true when it prints, false otherwise. + */ +#define error_report_once(fmt, ...) \ + ({ \ + static bool print_once_; \ + error_report_once_cond(&print_once_, \ + fmt, ##__VA_ARGS__); \ + }) + +/* + * Similar to warn_report(), except it prints the message just once. + * Return true when it prints, false otherwise. + */ +#define warn_report_once(fmt, ...) \ + ({ \ + static bool print_once_; \ + warn_report_once_cond(&print_once_, \ + fmt, ##__VA_ARGS__); \ + }) + const char *error_get_progname(void); extern bool enable_timestamp_msg; diff --git a/include/qemu/filemonitor.h b/include/qemu/filemonitor.h new file mode 100644 index 0000000000..64267d09b2 --- /dev/null +++ b/include/qemu/filemonitor.h @@ -0,0 +1,128 @@ +/* + * QEMU file monitor helper + * + * Copyright (c) 2018 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_FILE_MONITOR_H +#define QEMU_FILE_MONITOR_H + +#include "qemu-common.h" + + +typedef struct QFileMonitor QFileMonitor; + +typedef enum { + /* File has been created in a dir */ + QFILE_MONITOR_EVENT_CREATED, + /* File has been modified in a dir */ + QFILE_MONITOR_EVENT_MODIFIED, + /* File has been deleted in a dir */ + QFILE_MONITOR_EVENT_DELETED, + /* File has attributes changed */ + QFILE_MONITOR_EVENT_ATTRIBUTES, + /* Dir is no longer being monitored (due to deletion) */ + QFILE_MONITOR_EVENT_IGNORED, +} QFileMonitorEvent; + + +/** + * QFileMonitorHandler: + * @id: id from qemu_file_monitor_add_watch() + * @event: the file change that occurred + * @filename: the name of the file affected + * @opaque: opaque data provided to qemu_file_monitor_add_watch() + * + * Invoked whenever a file changes. If @event is + * QFILE_MONITOR_EVENT_IGNORED, @filename will be + * empty. + * + */ +typedef void (*QFileMonitorHandler)(int64_t id, + QFileMonitorEvent event, + const char *filename, + void *opaque); + +/** + * qemu_file_monitor_new: + * @errp: pointer to a NULL-initialized error object + * + * Create a handle for a file monitoring object. + * + * This object does locking internally to enable it to be + * safe to use from multiple threads + * + * If the platform does not support file monitoring, an + * error will be reported. Likewise if file monitoring + * is supported, but cannot be initialized + * + * Currently this is implemented on Linux platforms with + * the inotify subsystem. + * + * Returns: the new monitoring object, or NULL on error + */ +QFileMonitor *qemu_file_monitor_new(Error **errp); + +/** + * qemu_file_monitor_free: + * @mon: the file monitor context + * + * Free resources associated with the file monitor, + * including any currently registered watches. + */ +void qemu_file_monitor_free(QFileMonitor *mon); + +/** + * qemu_file_monitor_add_watch: + * @mon: the file monitor context + * @dirpath: the directory whose contents to watch + * @filename: optional filename to filter on + * @cb: the function to invoke when @dirpath has changes + * @opaque: data to pass to @cb + * @errp: pointer to a NULL-initialized error object + * + * Register to receive notifications of changes + * in the directory @dirpath. All files in the + * directory will be monitored. If the caller is + * only interested in one specific file, @filename + * can be used to filter events. + * + * Returns: a positive integer watch ID, or -1 on error + */ +int64_t qemu_file_monitor_add_watch(QFileMonitor *mon, + const char *dirpath, + const char *filename, + QFileMonitorHandler cb, + void *opaque, + Error **errp); + +/** + * qemu_file_monitor_remove_watch: + * @mon: the file monitor context + * @dirpath: the directory whose contents to unwatch + * @id: id of the watch to remove + * + * Removes the file monitoring watch @id, associated + * with the directory @dirpath. This must never be + * called from a QFileMonitorHandler callback, or a + * deadlock will result. + */ +void qemu_file_monitor_remove_watch(QFileMonitor *mon, + const char *dirpath, + int64_t id); + +#endif /* QEMU_FILE_MONITOR_H */ diff --git a/include/qemu/hbitmap.h b/include/qemu/hbitmap.h index ddca52c48e..4afbe6292e 100644 --- a/include/qemu/hbitmap.h +++ b/include/qemu/hbitmap.h @@ -73,16 +73,23 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size); /** * hbitmap_merge: - * @a: The bitmap to store the result in. - * @b: The bitmap to merge into @a. - * @return true if the merge was successful, - * false if it was not attempted. * - * Merge two bitmaps together. - * A := A (BITOR) B. - * B is left unmodified. + * Store result of merging @a and @b into @result. + * @result is allowed to be equal to @a or @b. + * + * Return true if the merge was successful, + * false if it was not attempted. */ -bool hbitmap_merge(HBitmap *a, const HBitmap *b); +bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result); + +/** + * hbitmap_can_merge: + * + * hbitmap_can_merge(a, b) && hbitmap_can_merge(a, result) is sufficient and + * necessary for hbitmap_merge will not fail. + * + */ +bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b); /** * hbitmap_empty: @@ -293,12 +300,32 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first); unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi); /* hbitmap_next_zero: + * + * Find next not dirty bit within selected range. If not found, return -1. + * * @hb: The HBitmap to operate on * @start: The bit to start from. - * - * Find next not dirty bit. + * @count: Number of bits to proceed. If @start+@count > bitmap size, the whole + * bitmap is looked through. You can use UINT64_MAX as @count to search up to + * the bitmap end. */ -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start); +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count); + +/* hbitmap_next_dirty_area: + * @hb: The HBitmap to operate on + * @start: in-out parameter. + * in: the offset to start from + * out: (if area found) start of found area + * @count: in-out parameter. + * in: length of requested region + * out: length of found area + * + * If dirty area found within [@start, @start + @count), returns true and sets + * @offset and @bytes appropriately. Otherwise returns false and leaves @offset + * and @bytes unchanged. + */ +bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, + uint64_t *count); /* hbitmap_create_meta: * Create a "meta" hbitmap to track dirtiness of the bits in this HBitmap. @@ -324,14 +351,11 @@ void hbitmap_free_meta(HBitmap *hb); /** * hbitmap_iter_next: * @hbi: HBitmapIter to operate on. - * @advance: If true, advance the iterator. Otherwise, the next call - * of this function will return the same result (if that - * position is still dirty). * * Return the next bit that is set in @hbi's associated HBitmap, * or -1 if all remaining bits are zero. */ -int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance); +int64_t hbitmap_iter_next(HBitmapIter *hbi); /** * hbitmap_iter_next_word: diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 38da849be9..4cd170e6cd 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -207,7 +207,7 @@ static inline int cto64(uint64_t val) */ static inline int clrsb32(uint32_t val) { -#if QEMU_GNUC_PREREQ(4, 7) +#if __has_builtin(__builtin_clrsb) || !defined(__clang__) return __builtin_clrsb(val); #else return clz32(val ^ ((int32_t)val >> 1)) - 1; @@ -223,7 +223,7 @@ static inline int clrsb32(uint32_t val) */ static inline int clrsb64(uint64_t val) { -#if QEMU_GNUC_PREREQ(4, 7) +#if __has_builtin(__builtin_clrsbll) || !defined(__clang__) return __builtin_clrsbll(val); #else return clz64(val ^ ((int64_t)val >> 1)) - 1; diff --git a/include/qemu/iov.h b/include/qemu/iov.h index 72d4c559b4..48b45987b7 100644 --- a/include/qemu/iov.h +++ b/include/qemu/iov.h @@ -35,7 +35,7 @@ size_t iov_size(const struct iovec *iov, const unsigned int iov_cnt); 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); + size_t offset, void *buf, size_t bytes); static inline size_t iov_from_buf(const struct iovec *iov, unsigned int iov_cnt, @@ -133,10 +133,70 @@ size_t iov_discard_back(struct iovec *iov, unsigned int *iov_cnt, typedef struct QEMUIOVector { struct iovec *iov; int niov; - int nalloc; - size_t size; + + /* + * For external @iov (qemu_iovec_init_external()) or allocated @iov + * (qemu_iovec_init()), @size is the cumulative size of iovecs and + * @local_iov is invalid and unused. + * + * For embedded @iov (QEMU_IOVEC_INIT_BUF() or qemu_iovec_init_buf()), + * @iov is equal to &@local_iov, and @size is valid, as it has same + * offset and type as @local_iov.iov_len, which is guaranteed by + * static assertion below. + * + * @nalloc is always valid and is -1 both for embedded and external + * cases. It is included in the union only to ensure the padding prior + * to the @size field will not result in a 0-length array. + */ + union { + struct { + int nalloc; + struct iovec local_iov; + }; + struct { + char __pad[sizeof(int) + offsetof(struct iovec, iov_len)]; + size_t size; + }; + }; } QEMUIOVector; +QEMU_BUILD_BUG_ON(offsetof(QEMUIOVector, size) != + offsetof(QEMUIOVector, local_iov.iov_len)); + +#define QEMU_IOVEC_INIT_BUF(self, buf, len) \ +{ \ + .iov = &(self).local_iov, \ + .niov = 1, \ + .nalloc = -1, \ + .local_iov = { \ + .iov_base = (void *)(buf), /* cast away const */ \ + .iov_len = (len), \ + }, \ +} + +/* + * qemu_iovec_init_buf + * + * Initialize embedded QEMUIOVector. + * + * Note: "const" is used over @buf pointer to make it simple to pass + * const pointers, appearing in read functions. Then this "const" is + * cast away by QEMU_IOVEC_INIT_BUF(). + */ +static inline void qemu_iovec_init_buf(QEMUIOVector *qiov, + const void *buf, size_t len) +{ + *qiov = (QEMUIOVector) QEMU_IOVEC_INIT_BUF(*qiov, buf, len); +} + +static inline void *qemu_iovec_buf(QEMUIOVector *qiov) +{ + /* Only supports embedded iov */ + assert(qiov->nalloc == -1 && qiov->iov == &qiov->local_iov); + + return qiov->local_iov.iov_base; +} + 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); diff --git a/include/qemu/job.h b/include/qemu/job.h index 18c9223e31..9e7cd1e4a0 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -76,6 +76,9 @@ typedef struct Job { * Set to false by the job while the coroutine has yielded and may be * re-entered by job_enter(). There may still be I/O or event loop activity * pending. Accessed under block_job_mutex (in blockjob.c). + * + * When the job is deferred to the main loop, busy is true as long as the + * bottom half is still pending. */ bool busy; @@ -124,12 +127,20 @@ typedef struct Job { /** Estimated progress_current value at the completion of the job */ int64_t progress_total; - /** Error string for a failed job (NULL if, and only if, job->ret == 0) */ - char *error; - - /** ret code passed to job_completed. */ + /** + * Return code from @run and/or @prepare callback(s). + * Not final until the job has reached the CONCLUDED status. + * 0 on success, -errno on failure. + */ int ret; + /** + * Error object for a failed job. + * If job->ret is nonzero and an error object was not set, it will be set + * to strerror(-job->ret) during job_completed. + */ + Error *err; + /** The completion function that will be called when the job completes. */ BlockCompletionFunc *cb; @@ -148,6 +159,9 @@ typedef struct Job { /** Notifiers called when the job transitions to READY */ NotifierList on_ready; + /** Notifiers called when the job coroutine yields or terminates */ + NotifierList on_idle; + /** Element of the list of jobs */ QLIST_ENTRY(Job) job_list; @@ -168,8 +182,17 @@ struct JobDriver { /** Enum describing the operation */ JobType job_type; - /** Mandatory: Entrypoint for the Coroutine. */ - CoroutineEntry *start; + /** + * Mandatory: Entrypoint for the Coroutine. + * + * This callback will be invoked when moving from CREATED to RUNNING. + * + * If this callback returns nonzero, the job transaction it is part of is + * aborted. If it returns zero, the job moves into the WAITING state. If it + * is the last job to complete in its transaction, all jobs in the + * transaction move from WAITING to PENDING. + */ + int coroutine_fn (*run)(Job *job, Error **errp); /** * If the callback is not NULL, it will be invoked when the job transitions @@ -481,19 +504,6 @@ void job_early_fail(Job *job); /** Moves the @job from RUNNING to READY */ void job_transition_to_ready(Job *job); -/** - * @job: The job being completed. - * @ret: The status code. - * @error: The error message for a failing job (only with @ret < 0). If @ret is - * negative, but NULL is given for @error, strerror() is used. - * - * Marks @job as completed. If @ret is non-zero, the job transaction it is part - * of is aborted. If @ret is zero, the job moves into the WAITING state. If it - * is the last job to complete in its transaction, all jobs in the transaction - * move from WAITING to PENDING. - */ -void job_completed(Job *job, int ret, Error *error); - /** Asynchronously complete the specified @job. */ void job_complete(Job *job, Error **errp); @@ -517,6 +527,8 @@ void job_user_cancel(Job *job, bool force, Error **errp); * * Returns the return value from the job if the job actually completed * during the call, or -ECANCELED if it was canceled. + * + * Callers must hold the AioContext lock of job->aio_context. */ int job_cancel_sync(Job *job); @@ -534,6 +546,8 @@ void job_cancel_sync_all(void); * function). * * Returns the return value from the job. + * + * Callers must hold the AioContext lock of job->aio_context. */ int job_complete_sync(Job *job, Error **errp); @@ -553,29 +567,14 @@ void job_finalize(Job *job, Error **errp); */ void job_dismiss(Job **job, Error **errp); -typedef void JobDeferToMainLoopFn(Job *job, void *opaque); - -/** - * @job: The job - * @fn: The function to run in the main loop - * @opaque: The opaque value that is passed to @fn - * - * This function must be called by the main job coroutine just before it - * returns. @fn is executed in the main loop with the job AioContext acquired. - * - * Block jobs must call bdrv_unref(), bdrv_close(), and anything that uses - * bdrv_drain_all() in the main loop. - * - * The @job AioContext is held while @fn executes. - */ -void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque); - /** * Synchronously finishes the given @job. If @finish is given, it is called to * trigger completion or cancellation of the job. * * Returns 0 if the job is successfully completed, -ECANCELED if the job was * cancelled before completing, and -errno in other error cases. + * + * Callers must hold the AioContext lock of job->aio_context. */ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp); diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 721aa2416a..f6ba78ea73 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -276,7 +276,9 @@ bool qemu_mutex_iothread_locked(void); * NOTE: tools currently are single-threaded and qemu_mutex_lock_iothread * is a no-op there. */ -void qemu_mutex_lock_iothread(void); +#define qemu_mutex_lock_iothread() \ + qemu_mutex_lock_iothread_impl(__FILE__, __LINE__) +void qemu_mutex_lock_iothread_impl(const char *file, int line); /** * qemu_mutex_unlock_iothread: Unlock the main loop mutex. @@ -300,4 +302,19 @@ void qemu_fd_register(int fd); QEMUBH *qemu_bh_new(QEMUBHFunc *cb, void *opaque); void qemu_bh_schedule_idle(QEMUBH *bh); +enum { + MAIN_LOOP_POLL_FILL, + MAIN_LOOP_POLL_ERR, + MAIN_LOOP_POLL_OK, +}; + +typedef struct MainLoopPoll { + int state; + uint32_t timeout; + GArray *pollfds; +} MainLoopPoll; + +void main_loop_poll_add_notifier(Notifier *notify); +void main_loop_poll_remove_notifier(Notifier *notify); + #endif diff --git a/include/qemu/memfd.h b/include/qemu/memfd.h index 49e79634da..d551c28b68 100644 --- a/include/qemu/memfd.h +++ b/include/qemu/memfd.h @@ -16,12 +16,28 @@ #define F_SEAL_WRITE 0x0008 /* prevent writes */ #endif +#ifndef MFD_CLOEXEC +#define MFD_CLOEXEC 0x0001U +#endif + +#ifndef MFD_ALLOW_SEALING +#define MFD_ALLOW_SEALING 0x0002U +#endif + +#ifndef MFD_HUGETLB +#define MFD_HUGETLB 0x0004U +#endif + +#ifndef MFD_HUGE_SHIFT +#define MFD_HUGE_SHIFT 26 +#endif + int qemu_memfd_create(const char *name, size_t size, bool hugetlb, uint64_t hugetlbsize, unsigned int seals, Error **errp); bool qemu_memfd_alloc_check(void); void *qemu_memfd_alloc(const char *name, size_t size, unsigned int seals, int *fd, Error **errp); void qemu_memfd_free(void *ptr, size_t size, int fd); -bool qemu_memfd_check(void); +bool qemu_memfd_check(unsigned int flags); #endif /* QEMU_MEMFD_H */ diff --git a/include/qemu/mmap-alloc.h b/include/qemu/mmap-alloc.h index 50385e3f81..ef04f0ed5b 100644 --- a/include/qemu/mmap-alloc.h +++ b/include/qemu/mmap-alloc.h @@ -9,6 +9,6 @@ size_t qemu_mempath_getpagesize(const char *mem_path); void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared); -void qemu_ram_munmap(void *ptr, size_t size); +void qemu_ram_munmap(int fd, void *ptr, size_t size); #endif diff --git a/include/qemu/module.h b/include/qemu/module.h index 54300ab6e5..db3065381d 100644 --- a/include/qemu/module.h +++ b/include/qemu/module.h @@ -44,6 +44,8 @@ typedef enum { MODULE_INIT_OPTS, MODULE_INIT_QOM, MODULE_INIT_TRACE, + MODULE_INIT_XEN_BACKEND, + MODULE_INIT_LIBQOS, MODULE_INIT_MAX } module_init_type; @@ -51,6 +53,9 @@ typedef enum { #define opts_init(function) module_init(function, MODULE_INIT_OPTS) #define type_init(function) module_init(function, MODULE_INIT_QOM) #define trace_init(function) module_init(function, MODULE_INIT_TRACE) +#define xen_backend_init(function) module_init(function, \ + MODULE_INIT_XEN_BACKEND) +#define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS) #define block_module_load_one(lib) module_load_one("block-", lib) #define ui_module_load_one(lib) module_load_one("ui-", lib) diff --git a/include/qemu/option.h b/include/qemu/option.h index 3dfb4493cc..844587cab3 100644 --- a/include/qemu/option.h +++ b/include/qemu/option.h @@ -132,7 +132,7 @@ typedef int (*qemu_opts_loopfunc)(void *opaque, QemuOpts *opts, Error **errp); int qemu_opts_foreach(QemuOptsList *list, qemu_opts_loopfunc func, void *opaque, Error **errp); void qemu_opts_print(QemuOpts *opts, const char *sep); -void qemu_opts_print_help(QemuOptsList *list); +void qemu_opts_print_help(QemuOptsList *list, bool print_caption); void qemu_opts_free(QemuOptsList *list); QemuOptsList *qemu_opts_append(QemuOptsList *dst, QemuOptsList *list); diff --git a/include/qemu/option_int.h b/include/qemu/option_int.h index 26b1d9e4d6..5dd9a5162d 100644 --- a/include/qemu/option_int.h +++ b/include/qemu/option_int.h @@ -47,7 +47,7 @@ struct QemuOpts { char *id; QemuOptsList *list; Location loc; - QTAILQ_HEAD(QemuOptHead, QemuOpt) head; + QTAILQ_HEAD(, QemuOpt) head; QTAILQ_ENTRY(QemuOpts) next; }; diff --git a/include/qemu/osdep.h b/include/qemu/osdep.h index a91068df0e..303d315c5d 100644 --- a/include/qemu/osdep.h +++ b/include/qemu/osdep.h @@ -74,13 +74,30 @@ typedef __float128 _Float128; extern int daemon(int, int); #endif +#ifdef _WIN32 +/* as defined in sdkddkver.h */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* Vista */ +#endif +/* reduces the number of implicitly included headers */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#endif + #include #include #include #include #include #include + +/* enable C99/POSIX format strings (needs mingw32-runtime 3.15 or later) */ +#ifdef __MINGW32__ +#define __USE_MINGW_ANSI_STDIO 1 +#endif #include + #include #include #include @@ -92,6 +109,7 @@ extern int daemon(int, int); #include #include #include +#include #include #include #include @@ -122,6 +140,18 @@ extern int daemon(int, int); #include "glib-compat.h" #include "qemu/typedefs.h" +/* + * For mingw, as of v6.0.0, the function implementing the assert macro is + * not marked as noreturn, so the compiler cannot delete code following an + * assert(false) as unused. We rely on this within the code base to delete + * code that is unreachable when features are disabled. + * All supported versions of Glib's g_assert() satisfy this requirement. + */ +#ifdef __MINGW32__ +#undef assert +#define assert(x) g_assert(x) +#endif + /* * According to waitpid man page: * WCOREDUMP @@ -448,7 +478,8 @@ bool qemu_has_ofd_lock(void); #define FMT_pid "%d" #endif -int qemu_create_pidfile(const char *filename); +bool qemu_write_pidfile(const char *pidfile, Error **errp); + int qemu_get_thread_id(void); #ifndef CONFIG_IOVEC @@ -539,6 +570,19 @@ void qemu_set_tty_echo(int fd, bool echo); void os_mem_prealloc(int fd, char *area, size_t sz, int smp_cpus, Error **errp); +/** + * qemu_get_pmem_size: + * @filename: path to a pmem file + * @errp: pointer to a NULL-initialized error object + * + * Determine the size of a persistent memory file. Besides supporting files on + * DAX file systems, this function also supports Linux devdax character + * devices. + * + * Returns: the size or 0 on failure + */ +uint64_t qemu_get_pmem_size(const char *filename, Error **errp); + /** * qemu_get_pid_name: * @pid: pid of a process @@ -570,6 +614,23 @@ extern uintptr_t qemu_real_host_page_size; extern intptr_t qemu_real_host_page_mask; extern int qemu_icache_linesize; +extern int qemu_icache_linesize_log; extern int qemu_dcache_linesize; +extern int qemu_dcache_linesize_log; + +/* + * After using getopt or getopt_long, if you need to parse another set + * of options, then you must reset optind. Unfortunately the way to + * do this varies between implementations of getopt. + */ +static inline void qemu_reset_optind(void) +{ +#ifdef HAVE_OPTRESET + optind = 1; + optreset = 1; +#else + optind = 0; +#endif +} #endif diff --git a/include/qemu/pmem.h b/include/qemu/pmem.h new file mode 100644 index 0000000000..dfb6d0da62 --- /dev/null +++ b/include/qemu/pmem.h @@ -0,0 +1,36 @@ +/* + * QEMU header file for libpmem. + * + * Copyright (c) 2018 Intel Corporation. + * + * Author: Haozhong Zhang + * + * 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_PMEM_H +#define QEMU_PMEM_H + +#ifdef CONFIG_LIBPMEM +#include +#else /* !CONFIG_LIBPMEM */ + +static inline void * +pmem_memcpy_persist(void *pmemdest, const void *src, size_t len) +{ + /* If 'pmem' option is 'on', we should always have libpmem support, + or qemu will report a error and exit, never come here. */ + g_assert_not_reached(); + return NULL; +} + +static inline void +pmem_persist(const void *addr, size_t len) +{ + g_assert_not_reached(); +} + +#endif /* CONFIG_LIBPMEM */ + +#endif /* !QEMU_PMEM_H */ diff --git a/include/qemu/qht.h b/include/qemu/qht.h index 1fb9116fa0..758c7ac6c8 100644 --- a/include/qemu/qht.h +++ b/include/qemu/qht.h @@ -43,9 +43,11 @@ struct qht_stats { }; typedef bool (*qht_lookup_func_t)(const void *obj, const void *userp); -typedef void (*qht_iter_func_t)(struct qht *ht, void *p, uint32_t h, void *up); +typedef void (*qht_iter_func_t)(void *p, uint32_t h, void *up); +typedef bool (*qht_iter_bool_func_t)(void *p, uint32_t h, void *up); #define QHT_MODE_AUTO_RESIZE 0x1 /* auto-resize when heavily loaded */ +#define QHT_MODE_RAW_MUTEXES 0x2 /* bypass the profiler (QSP) */ /** * qht_init - Initialize a QHT @@ -102,7 +104,7 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing); * Returns the corresponding pointer when a match is found. * Returns NULL otherwise. */ -void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, +void *qht_lookup_custom(const struct qht *ht, const void *userp, uint32_t hash, qht_lookup_func_t func); /** @@ -113,7 +115,7 @@ void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, * * Calls qht_lookup_custom() using @ht's default comparison function. */ -void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash); +void *qht_lookup(const struct qht *ht, const void *userp, uint32_t hash); /** * qht_remove - remove a pointer from the hash table @@ -178,9 +180,26 @@ bool qht_resize(struct qht *ht, size_t n_elems); * * Each time it is called, user-provided @func is passed a pointer-hash pair, * plus @userp. + * + * Note: @ht cannot be accessed from @func + * See also: qht_iter_remove() */ void qht_iter(struct qht *ht, qht_iter_func_t func, void *userp); +/** + * qht_iter_remove - Iterate over a QHT, optionally removing entries + * @ht: QHT to be iterated over + * @func: function to be called for each entry in QHT + * @userp: additional pointer to be passed to @func + * + * Each time it is called, user-provided @func is passed a pointer-hash pair, + * plus @userp. If @func returns true, the pointer-hash pair is removed. + * + * Note: @ht cannot be accessed from @func + * See also: qht_iter() + */ +void qht_iter_remove(struct qht *ht, qht_iter_bool_func_t func, void *userp); + /** * qht_statistics_init - Gather statistics from a QHT * @ht: QHT to gather statistics from @@ -192,7 +211,7 @@ void qht_iter(struct qht *ht, qht_iter_func_t func, void *userp); * When done with @stats, pass the struct to qht_statistics_destroy(). * Failing to do this will leak memory. */ -void qht_statistics_init(struct qht *ht, struct qht_stats *stats); +void qht_statistics_init(const struct qht *ht, struct qht_stats *stats); /** * qht_statistics_destroy - Destroy a &struct qht_stats diff --git a/include/qemu/qsp.h b/include/qemu/qsp.h new file mode 100644 index 0000000000..a94c464f90 --- /dev/null +++ b/include/qemu/qsp.h @@ -0,0 +1,29 @@ +/* + * qsp.c - QEMU Synchronization Profiler + * + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * Note: this header file can *only* be included from thread.h. + */ +#ifndef QEMU_QSP_H +#define QEMU_QSP_H + +#include "qemu/fprintf-fn.h" + +enum QSPSortBy { + QSP_SORT_BY_TOTAL_WAIT_TIME, + QSP_SORT_BY_AVG_WAIT_TIME, +}; + +void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max, + enum QSPSortBy sort_by, bool callsite_coalesce); + +bool qsp_is_enabled(void); +void qsp_enable(void); +void qsp_disable(void); +void qsp_reset(void); + +#endif /* QEMU_QSP_H */ diff --git a/include/qemu/queue.h b/include/qemu/queue.h index 59fd1203a1..0379bd8fdb 100644 --- a/include/qemu/queue.h +++ b/include/qemu/queue.h @@ -341,81 +341,85 @@ struct { \ /* * Simple queue access methods. */ +#define QSIMPLEQ_EMPTY_ATOMIC(head) (atomic_read(&((head)->sqh_first)) == NULL) #define QSIMPLEQ_EMPTY(head) ((head)->sqh_first == NULL) #define QSIMPLEQ_FIRST(head) ((head)->sqh_first) #define QSIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) +typedef struct QTailQLink { + void *tql_next; + struct QTailQLink *tql_prev; +} QTailQLink; /* - * Tail queue definitions. + * Tail queue definitions. The union acts as a poor man template, as if + * it were QTailQLink. */ -#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) \ +union name { \ + struct type *tqh_first; /* first element */ \ + QTailQLink tqh_circ; /* link for circular backwards list */ \ } -#define QTAILQ_HEAD(name, type) Q_TAILQ_HEAD(name, struct type,) #define QTAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } + { .tqh_circ = { NULL, &(head).tqh_circ } } -#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) \ +union { \ + struct type *tqe_next; /* next element */ \ + QTailQLink tqe_circ; /* link for circular backwards list */ \ } -#define QTAILQ_ENTRY(type) Q_TAILQ_ENTRY(struct type,) /* * Tail queue functions. */ #define QTAILQ_INIT(head) do { \ (head)->tqh_first = NULL; \ - (head)->tqh_last = &(head)->tqh_first; \ + (head)->tqh_circ.tql_prev = &(head)->tqh_circ; \ } while (/*CONSTCOND*/0) #define QTAILQ_INSERT_HEAD(head, elm, field) do { \ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ - (head)->tqh_first->field.tqe_prev = \ - &(elm)->field.tqe_next; \ + (head)->tqh_first->field.tqe_circ.tql_prev = \ + &(elm)->field.tqe_circ; \ else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ (head)->tqh_first = (elm); \ - (elm)->field.tqe_prev = &(head)->tqh_first; \ + (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ } while (/*CONSTCOND*/0) #define QTAILQ_INSERT_TAIL(head, elm, field) do { \ (elm)->field.tqe_next = NULL; \ - (elm)->field.tqe_prev = (head)->tqh_last; \ - *(head)->tqh_last = (elm); \ - (head)->tqh_last = &(elm)->field.tqe_next; \ + (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ + (head)->tqh_circ.tql_prev->tql_next = (elm); \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ } while (/*CONSTCOND*/0) #define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ - (elm)->field.tqe_next->field.tqe_prev = \ - &(elm)->field.tqe_next; \ + (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ + &(elm)->field.tqe_circ; \ else \ - (head)->tqh_last = &(elm)->field.tqe_next; \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ (listelm)->field.tqe_next = (elm); \ - (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ + (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ } while (/*CONSTCOND*/0) -#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \ - (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ - (elm)->field.tqe_next = (listelm); \ - *(listelm)->field.tqe_prev = (elm); \ - (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ +#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ + (elm)->field.tqe_next = (listelm); \ + (listelm)->field.tqe_circ.tql_prev->tql_next = (elm); \ + (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ } while (/*CONSTCOND*/0) #define QTAILQ_REMOVE(head, elm, field) do { \ if (((elm)->field.tqe_next) != NULL) \ - (elm)->field.tqe_next->field.tqe_prev = \ - (elm)->field.tqe_prev; \ + (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ + (elm)->field.tqe_circ.tql_prev; \ else \ - (head)->tqh_last = (elm)->field.tqe_prev; \ - *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ - (elm)->field.tqe_prev = NULL; \ + (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ + (elm)->field.tqe_circ.tql_prev->tql_next = (elm)->field.tqe_next; \ + (elm)->field.tqe_circ.tql_prev = NULL; \ } while (/*CONSTCOND*/0) #define QTAILQ_FOREACH(var, head, field) \ @@ -428,14 +432,14 @@ struct { \ (var) && ((next_var) = ((var)->field.tqe_next), 1); \ (var) = (next_var)) -#define QTAILQ_FOREACH_REVERSE(var, head, headname, field) \ - for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ +#define QTAILQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = QTAILQ_LAST(head); \ (var); \ - (var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last))) + (var) = QTAILQ_PREV(var, field)) -#define QTAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, prev_var) \ - for ((var) = (*(((struct headname *)((head)->tqh_last))->tqh_last)); \ - (var) && ((prev_var) = (*(((struct headname *)((var)->field.tqe_prev))->tqh_last)), 1); \ +#define QTAILQ_FOREACH_REVERSE_SAFE(var, head, field, prev_var) \ + for ((var) = QTAILQ_LAST(head); \ + (var) && ((prev_var) = QTAILQ_PREV(var, field), 1); \ (var) = (prev_var)) /* @@ -444,71 +448,49 @@ struct { \ #define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL) #define QTAILQ_FIRST(head) ((head)->tqh_first) #define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next) -#define QTAILQ_IN_USE(elm, field) ((elm)->field.tqe_prev != NULL) +#define QTAILQ_IN_USE(elm, field) ((elm)->field.tqe_circ.tql_prev != NULL) -#define QTAILQ_LAST(head, headname) \ - (*(((struct headname *)((head)->tqh_last))->tqh_last)) -#define QTAILQ_PREV(elm, headname, field) \ - (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) +#define QTAILQ_LINK_PREV(link) \ + ((link).tql_prev->tql_prev->tql_next) +#define QTAILQ_LAST(head) \ + ((typeof((head)->tqh_first)) QTAILQ_LINK_PREV((head)->tqh_circ)) +#define QTAILQ_PREV(elm, field) \ + ((typeof((elm)->field.tqe_next)) QTAILQ_LINK_PREV((elm)->field.tqe_circ)) #define field_at_offset(base, offset, type) \ - ((type) (((char *) (base)) + (offset))) - -typedef struct DUMMY_Q_ENTRY DUMMY_Q_ENTRY; -typedef struct DUMMY_Q DUMMY_Q; - -struct DUMMY_Q_ENTRY { - QTAILQ_ENTRY(DUMMY_Q_ENTRY) next; -}; - -struct DUMMY_Q { - QTAILQ_HEAD(DUMMY_Q_HEAD, DUMMY_Q_ENTRY) head; -}; - -#define dummy_q ((DUMMY_Q *) 0) -#define dummy_qe ((DUMMY_Q_ENTRY *) 0) + ((type *) (((char *) (base)) + (offset))) /* - * Offsets of layout of a tail queue head. - */ -#define QTAILQ_FIRST_OFFSET (offsetof(typeof(dummy_q->head), tqh_first)) -#define QTAILQ_LAST_OFFSET (offsetof(typeof(dummy_q->head), tqh_last)) -/* - * Raw access of elements of a tail queue + * Raw access of elements of a tail queue head. Offsets are all zero + * because it's a union. */ #define QTAILQ_RAW_FIRST(head) \ - (*field_at_offset(head, QTAILQ_FIRST_OFFSET, void **)) -#define QTAILQ_RAW_TQH_LAST(head) \ - (*field_at_offset(head, QTAILQ_LAST_OFFSET, void ***)) - -/* - * Offsets of layout of a tail queue element. - */ -#define QTAILQ_NEXT_OFFSET (offsetof(typeof(dummy_qe->next), tqe_next)) -#define QTAILQ_PREV_OFFSET (offsetof(typeof(dummy_qe->next), tqe_prev)) + field_at_offset(head, 0, void *) +#define QTAILQ_RAW_TQH_CIRC(head) \ + field_at_offset(head, 0, QTailQLink) /* * Raw access of elements of a tail entry */ #define QTAILQ_RAW_NEXT(elm, entry) \ - (*field_at_offset(elm, entry + QTAILQ_NEXT_OFFSET, void **)) -#define QTAILQ_RAW_TQE_PREV(elm, entry) \ - (*field_at_offset(elm, entry + QTAILQ_PREV_OFFSET, void ***)) + field_at_offset(elm, entry, void *) +#define QTAILQ_RAW_TQE_CIRC(elm, entry) \ + field_at_offset(elm, entry, QTailQLink) /* - * Tail queue tranversal using pointer arithmetic. + * Tail queue traversal using pointer arithmetic. */ #define QTAILQ_RAW_FOREACH(elm, head, entry) \ - for ((elm) = QTAILQ_RAW_FIRST(head); \ + for ((elm) = *QTAILQ_RAW_FIRST(head); \ (elm); \ - (elm) = QTAILQ_RAW_NEXT(elm, entry)) + (elm) = *QTAILQ_RAW_NEXT(elm, entry)) /* * Tail queue insertion using pointer arithmetic. */ -#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do { \ - QTAILQ_RAW_NEXT(elm, entry) = NULL; \ - QTAILQ_RAW_TQE_PREV(elm, entry) = QTAILQ_RAW_TQH_LAST(head); \ - *QTAILQ_RAW_TQH_LAST(head) = (elm); \ - QTAILQ_RAW_TQH_LAST(head) = &QTAILQ_RAW_NEXT(elm, entry); \ +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do { \ + *QTAILQ_RAW_NEXT(elm, entry) = NULL; \ + QTAILQ_RAW_TQE_CIRC(elm, entry)->tql_prev = QTAILQ_RAW_TQH_CIRC(head)->tql_prev; \ + QTAILQ_RAW_TQH_CIRC(head)->tql_prev->tql_next = (elm); \ + QTAILQ_RAW_TQH_CIRC(head)->tql_prev = QTAILQ_RAW_TQE_CIRC(elm, entry); \ } while (/*CONSTCOND*/0) #endif /* QEMU_SYS_QUEUE_H */ diff --git a/include/qemu/range.h b/include/qemu/range.h index f28f0c1825..71b8b215c6 100644 --- a/include/qemu/range.h +++ b/include/qemu/range.h @@ -3,19 +3,18 @@ * * Copyright (c) 2015-2016 Red Hat, Inc. * - * This library is free software; you can redistribute it and/or + * 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this library; if not, see . + * 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_RANGE_H @@ -39,7 +38,7 @@ struct Range { uint64_t upb; /* inclusive upper bound */ }; -static inline void range_invariant(Range *range) +static inline void range_invariant(const Range *range) { assert(range->lob <= range->upb || range->lob == range->upb + 1); } @@ -48,14 +47,14 @@ static inline void range_invariant(Range *range) #define range_empty ((Range){ .lob = 1, .upb = 0 }) /* Is @range empty? */ -static inline bool range_is_empty(Range *range) +static inline bool range_is_empty(const Range *range) { range_invariant(range); return range->lob > range->upb; } /* Does @range contain @val? */ -static inline bool range_contains(Range *range, uint64_t val) +static inline bool range_contains(const Range *range, uint64_t val) { return val >= range->lob && val <= range->upb; } @@ -112,6 +111,68 @@ static inline uint64_t range_upb(Range *range) return range->upb; } +/* + * Initialize @range to span the interval [@lob,@lob + @size - 1]. + * @size may be 0. If the range would overflow, returns -ERANGE, otherwise + * 0. + */ +static inline int QEMU_WARN_UNUSED_RESULT range_init(Range *range, uint64_t lob, + uint64_t size) +{ + if (lob + size < lob) { + return -ERANGE; + } + range->lob = lob; + range->upb = lob + size - 1; + range_invariant(range); + return 0; +} + +/* + * Initialize @range to span the interval [@lob,@lob + @size - 1]. + * @size may be 0. Range must not overflow. + */ +static inline void range_init_nofail(Range *range, uint64_t lob, uint64_t size) +{ + range->lob = lob; + range->upb = lob + size - 1; + range_invariant(range); +} + +/* + * Get the size of @range. + */ +static inline uint64_t range_size(const Range *range) +{ + return range->upb - range->lob + 1; +} + +/* + * Check if @range1 overlaps with @range2. If one of the ranges is empty, + * the result is always "false". + */ +static inline bool range_overlaps_range(const Range *range1, + const Range *range2) +{ + if (range_is_empty(range1) || range_is_empty(range2)) { + return false; + } + return !(range2->upb < range1->lob || range1->upb < range2->lob); +} + +/* + * Check if @range1 contains @range2. If one of the ranges is empty, + * the result is always "false". + */ +static inline bool range_contains_range(const Range *range1, + const Range *range2) +{ + if (range_is_empty(range1) || range_is_empty(range2)) { + return false; + } + return range1->lob <= range2->lob && range1->upb >= range2->upb; +} + /* * Extend @range to the smallest interval that includes @extend_by, too. */ diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h index 01be77407b..2d386f303e 100644 --- a/include/qemu/rcu_queue.h +++ b/include/qemu/rcu_queue.h @@ -36,7 +36,7 @@ extern "C" { /* * List access methods. */ -#define QLIST_EMPTY_RCU(head) (atomic_rcu_read(&(head)->lh_first) == NULL) +#define QLIST_EMPTY_RCU(head) (atomic_read(&(head)->lh_first) == NULL) #define QLIST_FIRST_RCU(head) (atomic_rcu_read(&(head)->lh_first)) #define QLIST_NEXT_RCU(elm, field) (atomic_rcu_read(&(elm)->field.le_next)) @@ -112,7 +112,7 @@ extern "C" { (elm)->field.le_next->field.le_prev = \ (elm)->field.le_prev; \ } \ - *(elm)->field.le_prev = (elm)->field.le_next; \ + atomic_set((elm)->field.le_prev, (elm)->field.le_next); \ } while (/*CONSTCOND*/0) /* List traversal must occur within an RCU critical section. */ @@ -128,6 +128,140 @@ extern "C" { ((next_var) = atomic_rcu_read(&(var)->field.le_next), 1); \ (var) = (next_var)) +/* + * RCU simple queue + */ + +/* Simple queue access methods */ +#define QSIMPLEQ_EMPTY_RCU(head) (atomic_read(&(head)->sqh_first) == NULL) +#define QSIMPLEQ_FIRST_RCU(head) atomic_rcu_read(&(head)->sqh_first) +#define QSIMPLEQ_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.sqe_next) + +/* Simple queue functions */ +#define QSIMPLEQ_INSERT_HEAD_RCU(head, elm, field) do { \ + (elm)->field.sqe_next = (head)->sqh_first; \ + if ((elm)->field.sqe_next == NULL) { \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + } \ + atomic_rcu_set(&(head)->sqh_first, (elm)); \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_TAIL_RCU(head, elm, field) do { \ + (elm)->field.sqe_next = NULL; \ + atomic_rcu_set((head)->sqh_last, (elm)); \ + (head)->sqh_last = &(elm)->field.sqe_next; \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ + (elm)->field.sqe_next = (listelm)->field.sqe_next; \ + if ((elm)->field.sqe_next == NULL) { \ + (head)->sqh_last = &(elm)->field.sqe_next; \ + } \ + atomic_rcu_set(&(listelm)->field.sqe_next, (elm)); \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_REMOVE_HEAD_RCU(head, field) do { \ + atomic_set(&(head)->sqh_first, (head)->sqh_first->field.sqe_next); \ + if ((head)->sqh_first == NULL) { \ + (head)->sqh_last = &(head)->sqh_first; \ + } \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_REMOVE_RCU(head, elm, type, field) do { \ + if ((head)->sqh_first == (elm)) { \ + QSIMPLEQ_REMOVE_HEAD_RCU((head), field); \ + } else { \ + struct type *curr = (head)->sqh_first; \ + while (curr->field.sqe_next != (elm)) { \ + curr = curr->field.sqe_next; \ + } \ + atomic_set(&curr->field.sqe_next, \ + curr->field.sqe_next->field.sqe_next); \ + if (curr->field.sqe_next == NULL) { \ + (head)->sqh_last = &(curr)->field.sqe_next; \ + } \ + } \ +} while (/*CONSTCOND*/0) + +#define QSIMPLEQ_FOREACH_RCU(var, head, field) \ + for ((var) = atomic_rcu_read(&(head)->sqh_first); \ + (var); \ + (var) = atomic_rcu_read(&(var)->field.sqe_next)) + +#define QSIMPLEQ_FOREACH_SAFE_RCU(var, head, field, next) \ + for ((var) = atomic_rcu_read(&(head)->sqh_first); \ + (var) && ((next) = atomic_rcu_read(&(var)->field.sqe_next), 1); \ + (var) = (next)) + +/* + * RCU tail queue + */ + +/* Tail queue access methods */ +#define QTAILQ_EMPTY_RCU(head) (atomic_read(&(head)->tqh_first) == NULL) +#define QTAILQ_FIRST_RCU(head) atomic_rcu_read(&(head)->tqh_first) +#define QTAILQ_NEXT_RCU(elm, field) atomic_rcu_read(&(elm)->field.tqe_next) + +/* Tail queue functions */ +#define QTAILQ_INSERT_HEAD_RCU(head, elm, field) do { \ + (elm)->field.tqe_next = (head)->tqh_first; \ + if ((elm)->field.tqe_next != NULL) { \ + (head)->tqh_first->field.tqe_circ.tql_prev = \ + &(elm)->field.tqe_circ; \ + } else { \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ + } \ + atomic_rcu_set(&(head)->tqh_first, (elm)); \ + (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_TAIL_RCU(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ + atomic_rcu_set(&(head)->tqh_circ.tql_prev->tql_next, (elm)); \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_AFTER_RCU(head, listelm, elm, field) do { \ + (elm)->field.tqe_next = (listelm)->field.tqe_next; \ + if ((elm)->field.tqe_next != NULL) { \ + (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ + &(elm)->field.tqe_circ; \ + } else { \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ + } \ + atomic_rcu_set(&(listelm)->field.tqe_next, (elm)); \ + (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_BEFORE_RCU(listelm, elm, field) do { \ + (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ + (elm)->field.tqe_next = (listelm); \ + atomic_rcu_set(&(listelm)->field.tqe_circ.tql_prev->tql_next, (elm)); \ + (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_REMOVE_RCU(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) { \ + (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ + (elm)->field.tqe_circ.tql_prev; \ + } else { \ + (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ + } \ + atomic_set(&(elm)->field.tqe_circ.tql_prev->tql_next, (elm)->field.tqe_next); \ + (elm)->field.tqe_circ.tql_prev = NULL; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_FOREACH_RCU(var, head, field) \ + for ((var) = atomic_rcu_read(&(head)->tqh_first); \ + (var); \ + (var) = atomic_rcu_read(&(var)->field.tqe_next)) + +#define QTAILQ_FOREACH_SAFE_RCU(var, head, field, next) \ + for ((var) = atomic_rcu_read(&(head)->tqh_first); \ + (var) && ((next) = atomic_rcu_read(&(var)->field.tqe_next), 1); \ + (var) = (next)) + #ifdef __cplusplus } #endif diff --git a/include/qemu/seqlock.h b/include/qemu/seqlock.h index 8dee11d101..fd408b7ec5 100644 --- a/include/qemu/seqlock.h +++ b/include/qemu/seqlock.h @@ -16,6 +16,7 @@ #include "qemu/atomic.h" #include "qemu/thread.h" +#include "qemu/lockable.h" typedef struct QemuSeqLock QemuSeqLock; @@ -45,7 +46,26 @@ static inline void seqlock_write_end(QemuSeqLock *sl) atomic_set(&sl->sequence, sl->sequence + 1); } -static inline unsigned seqlock_read_begin(QemuSeqLock *sl) +/* Lock out other writers and update the count. */ +static inline void seqlock_write_lock_impl(QemuSeqLock *sl, QemuLockable *lock) +{ + qemu_lockable_lock(lock); + seqlock_write_begin(sl); +} +#define seqlock_write_lock(sl, lock) \ + seqlock_write_lock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) + +/* Lock out other writers and update the count. */ +static inline void seqlock_write_unlock_impl(QemuSeqLock *sl, QemuLockable *lock) +{ + qemu_lockable_unlock(lock); + seqlock_write_begin(sl); +} +#define seqlock_write_unlock(sl, lock) \ + seqlock_write_unlock_impl(sl, QEMU_MAKE_LOCKABLE(lock)) + + +static inline unsigned seqlock_read_begin(const QemuSeqLock *sl) { /* Always fail if a write is in progress. */ unsigned ret = atomic_read(&sl->sequence); diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index fd27b34128..c903525062 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -6,8 +6,8 @@ typedef QemuMutex QemuRecMutex; #define qemu_rec_mutex_destroy qemu_mutex_destroy -#define qemu_rec_mutex_lock qemu_mutex_lock -#define qemu_rec_mutex_trylock qemu_mutex_trylock +#define qemu_rec_mutex_lock_impl qemu_mutex_lock_impl +#define qemu_rec_mutex_trylock_impl qemu_mutex_trylock_impl #define qemu_rec_mutex_unlock qemu_mutex_unlock struct QemuMutex { diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index d668d789b4..50af5dd7ab 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -19,8 +19,9 @@ struct QemuRecMutex { }; void qemu_rec_mutex_destroy(QemuRecMutex *mutex); -void qemu_rec_mutex_lock(QemuRecMutex *mutex); -int qemu_rec_mutex_trylock(QemuRecMutex *mutex); +void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line); +int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, + int line); void qemu_rec_mutex_unlock(QemuRecMutex *mutex); struct QemuCond { diff --git a/include/qemu/thread.h b/include/qemu/thread.h index ef7bd16123..55d83a907c 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -16,6 +16,9 @@ typedef struct QemuThread QemuThread; #include "qemu/thread-posix.h" #endif +/* include QSP header once QemuMutex, QemuCond etc. are defined */ +#include "qemu/qsp.h" + #define QEMU_THREAD_JOINABLE 0 #define QEMU_THREAD_DETACHED 1 @@ -25,10 +28,69 @@ int qemu_mutex_trylock_impl(QemuMutex *mutex, const char *file, const int line); void qemu_mutex_lock_impl(QemuMutex *mutex, const char *file, const int line); void qemu_mutex_unlock_impl(QemuMutex *mutex, const char *file, const int line); -#define qemu_mutex_lock(mutex) \ - qemu_mutex_lock_impl(mutex, __FILE__, __LINE__) -#define qemu_mutex_trylock(mutex) \ - qemu_mutex_trylock_impl(mutex, __FILE__, __LINE__) +typedef void (*QemuMutexLockFunc)(QemuMutex *m, const char *f, int l); +typedef int (*QemuMutexTrylockFunc)(QemuMutex *m, const char *f, int l); +typedef void (*QemuRecMutexLockFunc)(QemuRecMutex *m, const char *f, int l); +typedef int (*QemuRecMutexTrylockFunc)(QemuRecMutex *m, const char *f, int l); +typedef void (*QemuCondWaitFunc)(QemuCond *c, QemuMutex *m, const char *f, + int l); + +extern QemuMutexLockFunc qemu_bql_mutex_lock_func; +extern QemuMutexLockFunc qemu_mutex_lock_func; +extern QemuMutexTrylockFunc qemu_mutex_trylock_func; +extern QemuRecMutexLockFunc qemu_rec_mutex_lock_func; +extern QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func; +extern QemuCondWaitFunc qemu_cond_wait_func; + +/* convenience macros to bypass the profiler */ +#define qemu_mutex_lock__raw(m) \ + qemu_mutex_lock_impl(m, __FILE__, __LINE__) +#define qemu_mutex_trylock__raw(m) \ + qemu_mutex_trylock_impl(m, __FILE__, __LINE__) + +#ifdef __COVERITY__ +/* + * Coverity is severely confused by the indirect function calls, + * hide them. + */ +#define qemu_mutex_lock(m) \ + qemu_mutex_lock_impl(m, __FILE__, __LINE__); +#define qemu_mutex_trylock(m) \ + qemu_mutex_trylock_impl(m, __FILE__, __LINE__); +#define qemu_rec_mutex_lock(m) \ + qemu_rec_mutex_lock_impl(m, __FILE__, __LINE__); +#define qemu_rec_mutex_trylock(m) \ + qemu_rec_mutex_trylock_impl(m, __FILE__, __LINE__); +#define qemu_cond_wait(c, m) \ + qemu_cond_wait_impl(c, m, __FILE__, __LINE__); +#else +#define qemu_mutex_lock(m) ({ \ + QemuMutexLockFunc _f = atomic_read(&qemu_mutex_lock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_mutex_trylock(m) ({ \ + QemuMutexTrylockFunc _f = atomic_read(&qemu_mutex_trylock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_rec_mutex_lock(m) ({ \ + QemuRecMutexLockFunc _f = atomic_read(&qemu_rec_mutex_lock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_rec_mutex_trylock(m) ({ \ + QemuRecMutexTrylockFunc _f; \ + _f = atomic_read(&qemu_rec_mutex_trylock_func); \ + _f(m, __FILE__, __LINE__); \ + }) + +#define qemu_cond_wait(c, m) ({ \ + QemuCondWaitFunc _f = atomic_read(&qemu_cond_wait_func); \ + _f(c, m, __FILE__, __LINE__); \ + }) +#endif + #define qemu_mutex_unlock(mutex) \ qemu_mutex_unlock_impl(mutex, __FILE__, __LINE__) @@ -47,6 +109,16 @@ static inline void (qemu_mutex_unlock)(QemuMutex *mutex) qemu_mutex_unlock(mutex); } +static inline void (qemu_rec_mutex_lock)(QemuRecMutex *mutex) +{ + qemu_rec_mutex_lock(mutex); +} + +static inline int (qemu_rec_mutex_trylock)(QemuRecMutex *mutex) +{ + return qemu_rec_mutex_trylock(mutex); +} + /* Prototypes for other functions are in thread-posix.h/thread-win32.h. */ void qemu_rec_mutex_init(QemuRecMutex *mutex); @@ -63,9 +135,6 @@ void qemu_cond_broadcast(QemuCond *cond); void qemu_cond_wait_impl(QemuCond *cond, QemuMutex *mutex, const char *file, const int line); -#define qemu_cond_wait(cond, mutex) \ - qemu_cond_wait_impl(cond, mutex, __FILE__, __LINE__) - static inline void (qemu_cond_wait)(QemuCond *cond, QemuMutex *mutex) { qemu_cond_wait(cond, mutex); @@ -93,7 +162,29 @@ void qemu_thread_exit(void *retval); void qemu_thread_naming(bool enable); struct Notifier; +/** + * qemu_thread_atexit_add: + * @notifier: Notifier to add + * + * Add the specified notifier to a list which will be run via + * notifier_list_notify() when this thread exits (either by calling + * qemu_thread_exit() or by returning from its start_routine). + * The usual usage is that the caller passes a Notifier which is + * a per-thread variable; it can then use the callback to free + * other per-thread data. + * + * If the thread exits as part of the entire process exiting, + * it is unspecified whether notifiers are called or not. + */ void qemu_thread_atexit_add(struct Notifier *notifier); +/** + * qemu_thread_atexit_remove: + * @notifier: Notifier to remove + * + * Remove the specified notifier from the thread-exit notification + * list. It is not valid to try to remove a notifier which is not + * on the list. + */ void qemu_thread_atexit_remove(struct Notifier *notifier); struct QemuSpin { diff --git a/include/qemu/timer.h b/include/qemu/timer.h index 39ea907e65..a86330c987 100644 --- a/include/qemu/timer.h +++ b/include/qemu/timer.h @@ -2,6 +2,7 @@ #define QEMU_TIMER_H #include "qemu-common.h" +#include "qemu/bitops.h" #include "qemu/notify.h" #include "qemu/host-utils.h" @@ -52,6 +53,24 @@ typedef enum { QEMU_CLOCK_MAX } QEMUClockType; +/** + * QEMU Timer attributes: + * + * An individual timer may be given one or multiple attributes when initialized. + * Each attribute corresponds to one bit. Attributes modify the processing + * of timers when they fire. + * + * The following attributes are available: + * + * QEMU_TIMER_ATTR_EXTERNAL: drives external subsystem + * + * Timers with this attribute do not recorded in rr mode, therefore it could be + * used for the subsystems that operate outside the guest core. Applicable only + * with virtual clock type. + */ + +#define QEMU_TIMER_ATTR_EXTERNAL BIT(0) + typedef struct QEMUTimerList QEMUTimerList; struct QEMUTimerListGroup { @@ -67,6 +86,7 @@ struct QEMUTimer { QEMUTimerCB *cb; void *opaque; QEMUTimer *next; + int attributes; int scale; }; @@ -418,22 +438,27 @@ int64_t timerlistgroup_deadline_ns(QEMUTimerListGroup *tlg); */ /** - * timer_init_tl: + * timer_init_full: * @ts: the timer to be initialised - * @timer_list: the timer list to attach the timer to + * @timer_list_group: (optional) the timer list group to attach the timer to + * @type: the clock type to use * @scale: the scale value for the timer + * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_ values * @cb: the callback to be called when the timer expires * @opaque: the opaque pointer to be passed to the callback * - * Initialise a new timer and associate it with @timer_list. + * Initialise a timer with the given scale and attributes, + * and associate it with timer list for given clock @type in @timer_list_group + * (or default timer list group, if NULL). * The caller is responsible for allocating the memory. * * You need not call an explicit deinit call. Simply make * sure it is not on a list with timer_del. */ -void timer_init_tl(QEMUTimer *ts, - QEMUTimerList *timer_list, int scale, - QEMUTimerCB *cb, void *opaque); +void timer_init_full(QEMUTimer *ts, + QEMUTimerListGroup *timer_list_group, QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque); /** * timer_init: @@ -445,14 +470,12 @@ void timer_init_tl(QEMUTimer *ts, * * Initialize a timer with the given scale on the default timer list * associated with the clock. - * - * You need not call an explicit deinit call. Simply make - * sure it is not on a list with timer_del. + * See timer_init_full for details. */ static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale, QEMUTimerCB *cb, void *opaque) { - timer_init_tl(ts, main_loop_tlg.tl[type], scale, cb, opaque); + timer_init_full(ts, NULL, type, scale, 0, cb, opaque); } /** @@ -464,9 +487,7 @@ static inline void timer_init(QEMUTimer *ts, QEMUClockType type, int scale, * * Initialize a timer with nanosecond scale on the default timer list * associated with the clock. - * - * You need not call an explicit deinit call. Simply make - * sure it is not on a list with timer_del. + * See timer_init_full for details. */ static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type, QEMUTimerCB *cb, void *opaque) @@ -483,9 +504,7 @@ static inline void timer_init_ns(QEMUTimer *ts, QEMUClockType type, * * Initialize a timer with microsecond scale on the default timer list * associated with the clock. - * - * You need not call an explicit deinit call. Simply make - * sure it is not on a list with timer_del. + * See timer_init_full for details. */ static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type, QEMUTimerCB *cb, void *opaque) @@ -502,9 +521,7 @@ static inline void timer_init_us(QEMUTimer *ts, QEMUClockType type, * * Initialize a timer with millisecond scale on the default timer list * associated with the clock. - * - * You need not call an explicit deinit call. Simply make - * sure it is not on a list with timer_del. + * See timer_init_full for details. */ static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, QEMUTimerCB *cb, void *opaque) @@ -513,27 +530,37 @@ static inline void timer_init_ms(QEMUTimer *ts, QEMUClockType type, } /** - * timer_new_tl: - * @timer_list: the timer list to attach the timer to + * timer_new_full: + * @timer_list_group: (optional) the timer list group to attach the timer to + * @type: the clock type to use * @scale: the scale value for the timer + * @attributes: 0, or one or more OR'ed QEMU_TIMER_ATTR_ values * @cb: the callback to be called when the timer expires * @opaque: the opaque pointer to be passed to the callback * - * Create a new timer and associate it with @timer_list. + * Create a new timer with the given scale and attributes, + * and associate it with timer list for given clock @type in @timer_list_group + * (or default timer list group, if NULL). * The memory is allocated by the function. * * This is not the preferred interface unless you know you - * are going to call timer_free. Use timer_init instead. + * are going to call timer_free. Use timer_init or timer_init_full instead. + * + * The default timer list has one special feature: in icount mode, + * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is + * not true of other timer lists, which are typically associated + * with an AioContext---each of them runs its timer callbacks in its own + * AioContext thread. * * Returns: a pointer to the timer */ -static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list, - int scale, - QEMUTimerCB *cb, - void *opaque) +static inline QEMUTimer *timer_new_full(QEMUTimerListGroup *timer_list_group, + QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque) { QEMUTimer *ts = g_malloc0(sizeof(QEMUTimer)); - timer_init_tl(ts, timer_list, scale, cb, opaque); + timer_init_full(ts, timer_list_group, type, scale, attributes, cb, opaque); return ts; } @@ -544,21 +571,16 @@ static inline QEMUTimer *timer_new_tl(QEMUTimerList *timer_list, * @cb: the callback to be called when the timer expires * @opaque: the opaque pointer to be passed to the callback * - * Create a new timer and associate it with the default - * timer list for the clock type @type. - * - * The default timer list has one special feature: in icount mode, - * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is - * not true of other timer lists, which are typically associated - * with an AioContext---each of them runs its timer callbacks in its own - * AioContext thread. + * Create a new timer with the given scale, + * and associate it with the default timer list for the clock type @type. + * See timer_new_full for details. * * Returns: a pointer to the timer */ static inline QEMUTimer *timer_new(QEMUClockType type, int scale, QEMUTimerCB *cb, void *opaque) { - return timer_new_tl(main_loop_tlg.tl[type], scale, cb, opaque); + return timer_new_full(NULL, type, scale, 0, cb, opaque); } /** @@ -569,12 +591,7 @@ static inline QEMUTimer *timer_new(QEMUClockType type, int scale, * * Create a new timer with nanosecond scale on the default timer list * associated with the clock. - * - * The default timer list has one special feature: in icount mode, - * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is - * not true of other timer lists, which are typically associated - * with an AioContext---each of them runs its timer callbacks in its own - * AioContext thread. + * See timer_new_full for details. * * Returns: a pointer to the newly created timer */ @@ -590,14 +607,9 @@ static inline QEMUTimer *timer_new_ns(QEMUClockType type, QEMUTimerCB *cb, * @cb: the callback to call when the timer expires * @opaque: the opaque pointer to pass to the callback * - * The default timer list has one special feature: in icount mode, - * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is - * not true of other timer lists, which are typically associated - * with an AioContext---each of them runs its timer callbacks in its own - * AioContext thread. - * * Create a new timer with microsecond scale on the default timer list * associated with the clock. + * See timer_new_full for details. * * Returns: a pointer to the newly created timer */ @@ -613,14 +625,9 @@ static inline QEMUTimer *timer_new_us(QEMUClockType type, QEMUTimerCB *cb, * @cb: the callback to call when the timer expires * @opaque: the opaque pointer to pass to the callback * - * The default timer list has one special feature: in icount mode, - * %QEMU_CLOCK_VIRTUAL timers are run in the vCPU thread. This is - * not true of other timer lists, which are typically associated - * with an AioContext---each of them runs its timer callbacks in its own - * AioContext thread. - * * Create a new timer with millisecond scale on the default timer list * associated with the clock. + * See timer_new_full for details. * * Returns: a pointer to the newly created timer */ @@ -1037,7 +1044,6 @@ static inline int64_t profile_getclock(void) return get_clock(); } -extern int64_t tcg_time; extern int64_t dev_time; #endif diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 3ec0e13a96..e4a0a656d1 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -4,12 +4,11 @@ /* A load of opaque types so that device init declarations don't have to pull in all the real definitions. */ -/* Please keep this list in alphabetical order */ +/* Please keep this list in case-insensitive alphabetical order */ typedef struct AdapterInfo AdapterInfo; typedef struct AddressSpace AddressSpace; typedef struct AioContext AioContext; -typedef struct AllwinnerAHCIState AllwinnerAHCIState; -typedef struct AudioState AudioState; +typedef struct AnnounceTimer AnnounceTimer; typedef struct BdrvDirtyBitmap BdrvDirtyBitmap; typedef struct BdrvDirtyBitmapIter BdrvDirtyBitmapIter; typedef struct BlockBackend BlockBackend; @@ -26,8 +25,6 @@ typedef struct DeviceListener DeviceListener; typedef struct DeviceState DeviceState; typedef struct DirtyBitmapSnapshot DirtyBitmapSnapshot; typedef struct DisplayChangeListener DisplayChangeListener; -typedef struct DisplayState DisplayState; -typedef struct DisplaySurface DisplaySurface; typedef struct DriveInfo DriveInfo; typedef struct Error Error; typedef struct EventNotifier EventNotifier; @@ -36,10 +33,10 @@ typedef struct FWCfgEntry FWCfgEntry; typedef struct FWCfgIoState FWCfgIoState; typedef struct FWCfgMemState FWCfgMemState; typedef struct FWCfgState FWCfgState; -typedef struct HCIInfo HCIInfo; typedef struct HVFX86EmulatorState HVFX86EmulatorState; typedef struct I2CBus I2CBus; typedef struct I2SCodec I2SCodec; +typedef struct IOMMUMemoryRegion IOMMUMemoryRegion; typedef struct ISABus ISABus; typedef struct ISADevice ISADevice; typedef struct IsaDma IsaDma; @@ -49,19 +46,19 @@ typedef struct MachineState MachineState; typedef struct MemoryListener MemoryListener; typedef struct MemoryMappingList MemoryMappingList; typedef struct MemoryRegion MemoryRegion; -typedef struct IOMMUMemoryRegion IOMMUMemoryRegion; typedef struct MemoryRegionCache MemoryRegionCache; typedef struct MemoryRegionSection MemoryRegionSection; typedef struct MigrationIncomingState MigrationIncomingState; 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 NodeInfo NodeInfo; typedef struct NumaNodeMem NumaNodeMem; +typedef struct ObjectClass ObjectClass; typedef struct PCIBridge PCIBridge; typedef struct PCIBus PCIBus; typedef struct PCIDevice PCIDevice; @@ -74,29 +71,24 @@ 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 PostcopyDiscardState PostcopyDiscardState; typedef struct Property Property; typedef struct PropertyInfo PropertyInfo; -typedef struct PS2State PS2State; +typedef struct QBool QBool; +typedef struct QDict QDict; typedef struct QEMUBH QEMUBH; typedef struct QemuConsole QemuConsole; -typedef struct QemuDmaBuf QemuDmaBuf; typedef struct QEMUFile QEMUFile; typedef struct QemuLockable QemuLockable; typedef struct QemuMutex QemuMutex; typedef struct QemuOpt QemuOpt; typedef struct QemuOpts QemuOpts; typedef struct QemuOptsList QemuOptsList; -typedef struct QemuSpin QemuSpin; typedef struct QEMUSGList QEMUSGList; +typedef struct QemuSpin QemuSpin; typedef struct QEMUTimer QEMUTimer; typedef struct QEMUTimerListGroup QEMUTimerListGroup; -typedef struct QBool QBool; -typedef struct QDict QDict; typedef struct QJSON QJSON; typedef struct QList QList; typedef struct QNull QNull; @@ -105,14 +97,11 @@ typedef struct QObject QObject; typedef struct QString QString; typedef struct RAMBlock RAMBlock; typedef struct Range Range; -typedef struct SerialState SerialState; typedef struct SHPCDevice SHPCDevice; -typedef struct SMBusDevice SMBusDevice; typedef struct SSIBus SSIBus; typedef struct uWireSlave uWireSlave; typedef struct VirtIODevice VirtIODevice; typedef struct Visitor Visitor; -typedef struct node_info NodeInfo; typedef void SaveStateHandler(QEMUFile *f, void *opaque); typedef int LoadStateHandler(QEMUFile *f, void *opaque, int version_id); diff --git a/include/qemu/unicode.h b/include/qemu/unicode.h index 71c72db461..7fa10b8e60 100644 --- a/include/qemu/unicode.h +++ b/include/qemu/unicode.h @@ -2,5 +2,6 @@ #define QEMU_UNICODE_H int mod_utf8_codepoint(const char *s, size_t n, char **end); +ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint); #endif diff --git a/include/qemu/uuid.h b/include/qemu/uuid.h index 09489ce5c5..037357d990 100644 --- a/include/qemu/uuid.h +++ b/include/qemu/uuid.h @@ -56,6 +56,6 @@ char *qemu_uuid_unparse_strdup(const QemuUUID *uuid); int qemu_uuid_parse(const char *str, QemuUUID *uuid); -void qemu_uuid_bswap(QemuUUID *uuid); +QemuUUID qemu_uuid_bswap(QemuUUID uuid); #endif diff --git a/include/qemu/vfio-helpers.h b/include/qemu/vfio-helpers.h index ce7e7b057f..1f057c2b9e 100644 --- a/include/qemu/vfio-helpers.h +++ b/include/qemu/vfio-helpers.h @@ -12,7 +12,6 @@ #ifndef QEMU_VFIO_HELPERS_H #define QEMU_VFIO_HELPERS_H -#include "qemu/typedefs.h" typedef struct QEMUVFIOState QEMUVFIOState; diff --git a/include/qemu/win_dump_defs.h b/include/qemu/win_dump_defs.h new file mode 100644 index 0000000000..145096e8ee --- /dev/null +++ b/include/qemu/win_dump_defs.h @@ -0,0 +1,179 @@ +/* + * Windows crashdump definitions + * + * Copyright (c) 2018 Virtuozzo International GmbH + * + * 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_WIN_DUMP_DEFS_H +#define QEMU_WIN_DUMP_DEFS_H + +typedef struct WinDumpPhyMemRun64 { + uint64_t BasePage; + uint64_t PageCount; +} QEMU_PACKED WinDumpPhyMemRun64; + +typedef struct WinDumpPhyMemDesc64 { + uint32_t NumberOfRuns; + uint32_t unused; + uint64_t NumberOfPages; + WinDumpPhyMemRun64 Run[43]; +} QEMU_PACKED WinDumpPhyMemDesc64; + +typedef struct WinDumpExceptionRecord { + uint32_t ExceptionCode; + uint32_t ExceptionFlags; + uint64_t ExceptionRecord; + uint64_t ExceptionAddress; + uint32_t NumberParameters; + uint32_t unused; + uint64_t ExceptionInformation[15]; +} QEMU_PACKED WinDumpExceptionRecord; + +typedef struct WinDumpHeader64 { + char Signature[4]; + char ValidDump[4]; + uint32_t MajorVersion; + uint32_t MinorVersion; + uint64_t DirectoryTableBase; + uint64_t PfnDatabase; + uint64_t PsLoadedModuleList; + uint64_t PsActiveProcessHead; + uint32_t MachineImageType; + uint32_t NumberProcessors; + union { + struct { + uint32_t BugcheckCode; + uint32_t unused0; + uint64_t BugcheckParameter1; + uint64_t BugcheckParameter2; + uint64_t BugcheckParameter3; + uint64_t BugcheckParameter4; + }; + uint8_t BugcheckData[40]; + }; + uint8_t VersionUser[32]; + uint64_t KdDebuggerDataBlock; + union { + WinDumpPhyMemDesc64 PhysicalMemoryBlock; + uint8_t PhysicalMemoryBlockBuffer[704]; + }; + union { + uint8_t ContextBuffer[3000]; + }; + WinDumpExceptionRecord Exception; + uint32_t DumpType; + uint32_t unused1; + uint64_t RequiredDumpSpace; + uint64_t SystemTime; + char Comment[128]; + uint64_t SystemUpTime; + uint32_t MiniDumpFields; + uint32_t SecondaryDataState; + uint32_t ProductType; + uint32_t SuiteMask; + uint32_t WriterStatus; + uint8_t unused2; + uint8_t KdSecondaryVersion; + uint8_t reserved[4018]; +} QEMU_PACKED WinDumpHeader64; + +#define KDBG_OWNER_TAG_OFFSET64 0x10 +#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 +#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 +#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218 +#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338 + +#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24 + +#define WIN_CTX_X64 0x00100000L + +#define WIN_CTX_CTL 0x00000001L +#define WIN_CTX_INT 0x00000002L +#define WIN_CTX_SEG 0x00000004L +#define WIN_CTX_FP 0x00000008L +#define WIN_CTX_DBG 0x00000010L + +#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP) +#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG) + +#define LIVE_SYSTEM_DUMP 0x00000161 + +typedef struct WinM128A { + uint64_t low; + int64_t high; +} QEMU_ALIGNED(16) WinM128A; + +typedef struct WinContext { + uint64_t PHome[6]; + + uint32_t ContextFlags; + uint32_t MxCsr; + + uint16_t SegCs; + uint16_t SegDs; + uint16_t SegEs; + uint16_t SegFs; + uint16_t SegGs; + uint16_t SegSs; + uint32_t EFlags; + + uint64_t Dr0; + uint64_t Dr1; + uint64_t Dr2; + uint64_t Dr3; + uint64_t Dr6; + uint64_t Dr7; + + uint64_t Rax; + uint64_t Rcx; + uint64_t Rdx; + uint64_t Rbx; + uint64_t Rsp; + uint64_t Rbp; + uint64_t Rsi; + uint64_t Rdi; + uint64_t R8; + uint64_t R9; + uint64_t R10; + uint64_t R11; + uint64_t R12; + uint64_t R13; + uint64_t R14; + uint64_t R15; + + uint64_t Rip; + + struct { + uint16_t ControlWord; + uint16_t StatusWord; + uint8_t TagWord; + uint8_t Reserved1; + uint16_t ErrorOpcode; + uint32_t ErrorOffset; + uint16_t ErrorSelector; + uint16_t Reserved2; + uint32_t DataOffset; + uint16_t DataSelector; + uint16_t Reserved3; + uint32_t MxCsr; + uint32_t MxCsr_Mask; + WinM128A FloatRegisters[8]; + WinM128A XmmRegisters[16]; + uint8_t Reserved4[96]; + } FltSave; + + WinM128A VectorRegister[26]; + uint64_t VectorControl; + + uint64_t DebugControl; + uint64_t LastBranchToRip; + uint64_t LastBranchFromRip; + uint64_t LastExceptionToRip; + uint64_t LastExceptionFromRip; +} QEMU_ALIGNED(16) WinContext; + +#endif /* QEMU_WIN_DUMP_DEFS_H */ diff --git a/include/exec/tb-hash-xx.h b/include/qemu/xxhash.h similarity index 73% rename from include/exec/tb-hash-xx.h rename to include/qemu/xxhash.h index 747a9a612c..076f1f6054 100644 --- a/include/exec/tb-hash-xx.h +++ b/include/qemu/xxhash.h @@ -31,8 +31,8 @@ * - xxHash source repository : https://github.com/Cyan4973/xxHash */ -#ifndef EXEC_TB_HASH_XX_H -#define EXEC_TB_HASH_XX_H +#ifndef QEMU_XXHASH_H +#define QEMU_XXHASH_H #include "qemu/bitops.h" @@ -42,23 +42,23 @@ #define PRIME32_4 668265263U #define PRIME32_5 374761393U -#define TB_HASH_XX_SEED 1 +#define QEMU_XXHASH_SEED 1 /* * xxhash32, customized for input variables that are not guaranteed to be * contiguous in memory. */ static inline uint32_t -tb_hash_func7(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g) +qemu_xxhash7(uint64_t ab, uint64_t cd, uint32_t e, uint32_t f, uint32_t g) { - uint32_t v1 = TB_HASH_XX_SEED + PRIME32_1 + PRIME32_2; - uint32_t v2 = TB_HASH_XX_SEED + PRIME32_2; - uint32_t v3 = TB_HASH_XX_SEED + 0; - uint32_t v4 = TB_HASH_XX_SEED - PRIME32_1; - uint32_t a = a0 >> 32; - uint32_t b = a0; - uint32_t c = b0 >> 32; - uint32_t d = b0; + uint32_t v1 = QEMU_XXHASH_SEED + PRIME32_1 + PRIME32_2; + uint32_t v2 = QEMU_XXHASH_SEED + PRIME32_2; + uint32_t v3 = QEMU_XXHASH_SEED + 0; + uint32_t v4 = QEMU_XXHASH_SEED - PRIME32_1; + uint32_t a = ab; + uint32_t b = ab >> 32; + uint32_t c = cd; + uint32_t d = cd >> 32; uint32_t h32; v1 += a * PRIME32_2; @@ -98,4 +98,25 @@ tb_hash_func7(uint64_t a0, uint64_t b0, uint32_t e, uint32_t f, uint32_t g) return h32; } -#endif /* EXEC_TB_HASH_XX_H */ +static inline uint32_t qemu_xxhash2(uint64_t ab) +{ + return qemu_xxhash7(ab, 0, 0, 0, 0); +} + +static inline uint32_t qemu_xxhash4(uint64_t ab, uint64_t cd) +{ + return qemu_xxhash7(ab, cd, 0, 0, 0); +} + +static inline uint32_t qemu_xxhash5(uint64_t ab, uint64_t cd, uint32_t e) +{ + return qemu_xxhash7(ab, cd, e, 0, 0); +} + +static inline uint32_t qemu_xxhash6(uint64_t ab, uint64_t cd, uint32_t e, + uint32_t f) +{ + return qemu_xxhash7(ab, cd, e, f, 0); +} + +#endif /* QEMU_XXHASH_H */ diff --git a/include/qom/cpu.h b/include/qom/cpu.h index bd796579ee..1d6099e5d4 100644 --- a/include/qom/cpu.h +++ b/include/qom/cpu.h @@ -26,6 +26,8 @@ #include "exec/memattrs.h" #include "qapi/qapi-types-run-state.h" #include "qemu/bitmap.h" +#include "qemu/fprintf-fn.h" +#include "qemu/rcu_queue.h" #include "qemu/queue.h" #include "qemu/thread.h" @@ -101,9 +103,21 @@ struct TranslationBlock; * @get_arch_id: Callback for getting architecture-dependent CPU ID. * @get_paging_enabled: Callback for inquiring whether paging is enabled. * @get_memory_mapping: Callback for obtaining the memory mappings. - * @set_pc: Callback for setting the Program Counter register. + * @set_pc: Callback for setting the Program Counter register. This + * should have the semantics used by the target architecture when + * setting the PC from a source such as an ELF file entry point; + * for example on Arm it will also set the Thumb mode bit based + * on the least significant bit of the new PC value. + * If the target behaviour here is anything other than "set + * the PC register to the value passed in" then the target must + * also implement the synchronize_from_tb hook. * @synchronize_from_tb: Callback for synchronizing state from a TCG - * #TranslationBlock. + * #TranslationBlock. This is called when we abandon execution + * of a TB before starting it, and must set all parts of the CPU + * state which the previous TB in the chain may not have updated. + * This always includes at least the program counter; some targets + * will need to do more. If this hook is not implemented then the + * default is to call @set_pc(tb->pc). * @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 @@ -278,6 +292,11 @@ struct qemu_work_item; /** * CPUState: * @cpu_index: CPU index (informative). + * @cluster_index: Identifies which cluster this CPU is in. + * For boards which don't define clusters or for "loose" CPUs not assigned + * to a cluster this will be UNASSIGNED_CLUSTER_INDEX; otherwise it will + * be the same as the cluster-id property of the CPU object's TYPE_CPU_CLUSTER + * QOM parent. * @nr_cores: Number of cores within this CPU package. * @nr_threads: Number of threads within this CPU. * @running: #true if CPU is currently running (lockless). @@ -374,9 +393,9 @@ struct CPUState { QTAILQ_ENTRY(CPUState) node; /* ice debug support */ - QTAILQ_HEAD(breakpoints_head, CPUBreakpoint) breakpoints; + QTAILQ_HEAD(, CPUBreakpoint) breakpoints; - QTAILQ_HEAD(watchpoints_head, CPUWatchpoint) watchpoints; + QTAILQ_HEAD(, CPUWatchpoint) watchpoints; CPUWatchpoint *watchpoint_hit; void *opaque; @@ -386,6 +405,12 @@ struct CPUState { */ uintptr_t mem_io_pc; vaddr mem_io_vaddr; + /* + * This is only needed for the legacy cpu_unassigned_access() hook; + * when all targets using it have been converted to use + * cpu_transaction_failed() instead it can be removed. + */ + MMUAccessType mem_io_access_type; int kvm_fd; struct KVMState *kvm_state; @@ -397,6 +422,7 @@ struct CPUState { /* TODO Move common fields from CPUArchState here. */ int cpu_index; + int cluster_index; uint32_t halted; uint32_t can_do_io; int32_t exception_index; @@ -422,27 +448,20 @@ struct CPUState { struct hax_vcpu_state *hax_vcpu; - /* The pending_tlb_flush flag is set and cleared atomically to - * avoid potential races. The aim of the flag is to avoid - * unnecessary flushes. - */ - uint16_t pending_tlb_flush; - int hvf_fd; /* track IOMMUs whose translations we've cached in the TCG TLB */ GArray *iommu_notifiers; }; -QTAILQ_HEAD(CPUTailQ, CPUState); -extern struct CPUTailQ cpus; -#define CPU_NEXT(cpu) QTAILQ_NEXT(cpu, node) -#define CPU_FOREACH(cpu) QTAILQ_FOREACH(cpu, &cpus, node) +typedef QTAILQ_HEAD(CPUTailQ, CPUState) CPUTailQ; +extern CPUTailQ cpus; + +#define first_cpu QTAILQ_FIRST_RCU(&cpus) +#define CPU_NEXT(cpu) QTAILQ_NEXT_RCU(cpu, node) +#define CPU_FOREACH(cpu) QTAILQ_FOREACH_RCU(cpu, &cpus, node) #define CPU_FOREACH_SAFE(cpu, next_cpu) \ - QTAILQ_FOREACH_SAFE(cpu, &cpus, node, next_cpu) -#define CPU_FOREACH_REVERSE(cpu) \ - QTAILQ_FOREACH_REVERSE(cpu, &cpus, CPUTailQ, node) -#define first_cpu QTAILQ_FIRST(&cpus) + QTAILQ_FOREACH_SAFE_RCU(cpu, &cpus, node, next_cpu) extern __thread CPUState *current_cpu; @@ -847,7 +866,7 @@ extern CPUInterruptHandler cpu_interrupt_handler; /** * cpu_interrupt: * @cpu: The CPU to set an interrupt on. - * @mask: The interupts to set. + * @mask: The interrupts to set. * * Invokes the interrupt handler. */ @@ -1080,6 +1099,17 @@ void cpu_exec_initfn(CPUState *cpu); void cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); +/** + * target_words_bigendian: + * Returns true if the (default) endianness of the target is big endian, + * false otherwise. Note that in target-specific code, you can use + * TARGET_WORDS_BIGENDIAN directly instead. On the other hand, common + * code should normally never need to know about the endianness of the + * target, so please do *not* use this function unless you know very well + * what you are doing! + */ +bool target_words_bigendian(void); + #ifdef NEED_CPU_H #ifdef CONFIG_SOFTMMU @@ -1099,5 +1129,6 @@ extern const struct VMStateDescription vmstate_cpu_common; #endif /* NEED_CPU_H */ #define UNASSIGNED_CPU_INDEX -1 +#define UNASSIGNED_CLUSTER_INDEX -1 #endif diff --git a/include/qom/object.h b/include/qom/object.h index f0b0bf39cc..288cdddf44 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -20,7 +20,6 @@ struct TypeImpl; typedef struct TypeImpl *Type; -typedef struct ObjectClass ObjectClass; typedef struct Object Object; typedef struct TypeInfo TypeInfo; @@ -455,10 +454,8 @@ struct Object * parent class initialization has occurred, but before the class itself * is initialized. This is the function to use to undo the effects of * memcpy from the parent class to the descendants. - * @class_finalize: This function is called during class destruction and is - * meant to release and dynamic parameters allocated by @class_init. - * @class_data: Data to pass to the @class_init, @class_base_init and - * @class_finalize functions. This can be useful when building dynamic + * @class_data: Data to pass to the @class_init, + * @class_base_init. This can be useful when building dynamic * classes. * @interfaces: The list of interfaces associated with this type. This * should point to a static array that's terminated with a zero filled @@ -479,7 +476,6 @@ struct TypeInfo void (*class_init)(ObjectClass *klass, void *data); void (*class_base_init)(ObjectClass *klass, void *data); - void (*class_finalize)(ObjectClass *klass, void *data); void *class_data; InterfaceInfo *interfaces; @@ -679,6 +675,12 @@ Object *object_new_with_propv(const char *typename, Error **errp, va_list vargs); +void object_apply_global_props(Object *obj, const GPtrArray *props, + Error **errp); +void object_set_machine_compat_props(GPtrArray *compat_props); +void object_set_accelerator_compat_props(GPtrArray *compat_props); +void object_apply_compat_props(Object *obj); + /** * object_set_props: * @obj: the object instance to set properties on diff --git a/include/qom/object_interfaces.h b/include/qom/object_interfaces.h index 4d513fb329..682ba1d9b0 100644 --- a/include/qom/object_interfaces.h +++ b/include/qom/object_interfaces.h @@ -16,11 +16,7 @@ INTERFACE_CHECK(UserCreatable, (obj), \ TYPE_USER_CREATABLE) - -typedef struct UserCreatable { - /* */ - Object Parent; -} UserCreatable; +typedef struct UserCreatable UserCreatable; /** * UserCreatableClass: @@ -55,14 +51,14 @@ typedef struct UserCreatableClass { /** * user_creatable_complete: - * @obj: the object whose complete() method is called if defined + * @uc: the user-creatable object whose complete() method is called if defined * @errp: if an error occurs, a pointer to an area to store the error * * Wrapper to call complete() method if one of types it's inherited * from implements USER_CREATABLE interface, otherwise the call does * nothing. */ -void user_creatable_complete(Object *obj, Error **errp); +void user_creatable_complete(UserCreatable *uc, Error **errp); /** * user_creatable_can_be_deleted: diff --git a/include/scsi/constants.h b/include/scsi/constants.h index 083a8e887a..0dc550732d 100644 --- a/include/scsi/constants.h +++ b/include/scsi/constants.h @@ -212,7 +212,7 @@ #define TYPE_ROM 0x05 #define TYPE_SCANNER 0x06 #define TYPE_MOD 0x07 /* Magneto-optical disk - - * - treated as TYPE_DISK */ + * - treated as TYPE_DISK */ #define TYPE_MEDIUM_CHANGER 0x08 #define TYPE_STORAGE_ARRAY 0x0c /* Storage array device */ #define TYPE_ENCLOSURE 0x0d /* Enclosure Services Device */ diff --git a/include/scsi/pr-manager.h b/include/scsi/pr-manager.h index 50a77b08fc..6ad5fd1ff7 100644 --- a/include/scsi/pr-manager.h +++ b/include/scsi/pr-manager.h @@ -5,6 +5,7 @@ #include "qapi/visitor.h" #include "qom/object_interfaces.h" #include "block/aio.h" +#include "qemu/coroutine.h" #define TYPE_PR_MANAGER "pr-manager" @@ -37,11 +38,8 @@ typedef struct PRManagerClass { } PRManagerClass; bool pr_manager_is_connected(PRManager *pr_mgr); -BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, - AioContext *ctx, int fd, - struct sg_io_hdr *hdr, - BlockCompletionFunc *complete, - void *opaque); +int coroutine_fn pr_manager_execute(PRManager *pr_mgr, AioContext *ctx, int fd, + struct sg_io_hdr *hdr); PRManager *pr_manager_lookup(const char *id, Error **errp); diff --git a/include/standard-headers/asm-x86/bootparam.h b/include/standard-headers/asm-x86/bootparam.h new file mode 100644 index 0000000000..67d4f0119f --- /dev/null +++ b/include/standard-headers/asm-x86/bootparam.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _ASM_X86_BOOTPARAM_H +#define _ASM_X86_BOOTPARAM_H + +/* setup_data types */ +#define SETUP_NONE 0 +#define SETUP_E820_EXT 1 +#define SETUP_DTB 2 +#define SETUP_PCI 3 +#define SETUP_EFI 4 +#define SETUP_APPLE_PROPERTIES 5 +#define SETUP_JAILHOUSE 6 + +/* ram_size flags */ +#define RAMDISK_IMAGE_START_MASK 0x07FF +#define RAMDISK_PROMPT_FLAG 0x8000 +#define RAMDISK_LOAD_FLAG 0x4000 + +/* loadflags */ +#define LOADED_HIGH (1<<0) +#define KASLR_FLAG (1<<1) +#define QUIET_FLAG (1<<5) +#define KEEP_SEGMENTS (1<<6) +#define CAN_USE_HEAP (1<<7) + +/* xloadflags */ +#define XLF_KERNEL_64 (1<<0) +#define XLF_CAN_BE_LOADED_ABOVE_4G (1<<1) +#define XLF_EFI_HANDOVER_32 (1<<2) +#define XLF_EFI_HANDOVER_64 (1<<3) +#define XLF_EFI_KEXEC (1<<4) + + +#endif /* _ASM_X86_BOOTPARAM_H */ diff --git a/include/standard-headers/asm-x86/kvm_para.h b/include/standard-headers/asm-x86/kvm_para.h index 1617c84b0d..35cd8d651f 100644 --- a/include/standard-headers/asm-x86/kvm_para.h +++ b/include/standard-headers/asm-x86/kvm_para.h @@ -28,6 +28,7 @@ #define KVM_FEATURE_PV_UNHALT 7 #define KVM_FEATURE_PV_TLB_FLUSH 9 #define KVM_FEATURE_ASYNC_PF_VMEXIT 10 +#define KVM_FEATURE_PV_SEND_IPI 11 #define KVM_HINTS_REALTIME 0 diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index 11912fde24..44490607f9 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -29,11 +29,50 @@ extern "C" { #endif +/** + * DOC: overview + * + * In the DRM subsystem, framebuffer pixel formats are described using the + * fourcc codes defined in `include/uapi/drm/drm_fourcc.h`. In addition to the + * fourcc code, a Format Modifier may optionally be provided, in order to + * further describe the buffer's format - for example tiling or compression. + * + * Format Modifiers + * ---------------- + * + * Format modifiers are used in conjunction with a fourcc code, forming a + * unique fourcc:modifier pair. This format:modifier pair must fully define the + * format and data layout of the buffer, and should be the only way to describe + * that particular buffer. + * + * Having multiple fourcc:modifier pairs which describe the same layout should + * be avoided, as such aliases run the risk of different drivers exposing + * different names for the same data format, forcing userspace to understand + * that they are aliases. + * + * Format modifiers may change any property of the buffer, including the number + * of planes and/or the required allocation size. Format modifiers are + * vendor-namespaced, and as such the relationship between a fourcc code and a + * modifier is specific to the modifer being used. For example, some modifiers + * may preserve meaning - such as number of planes - from the fourcc code, + * whereas others may not. + * + * Vendors should document their modifier usage in as much detail as + * possible, to ensure maximum compatibility across devices, drivers and + * applications. + * + * The authoritative list of format modifier codes is found in + * `include/uapi/drm/drm_fourcc.h` + */ + #define fourcc_code(a, b, c, d) ((uint32_t)(a) | ((uint32_t)(b) << 8) | \ ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24)) #define DRM_FORMAT_BIG_ENDIAN (1<<31) /* format is big endian instead of little endian */ +/* Reserve 0 for the invalid format specifier */ +#define DRM_FORMAT_INVALID 0 + /* color index */ #define DRM_FORMAT_C8 fourcc_code('C', '8', ' ', ' ') /* [7:0] C */ @@ -111,6 +150,21 @@ extern "C" { #define DRM_FORMAT_VYUY fourcc_code('V', 'Y', 'U', 'Y') /* [31:0] Y1:Cb0:Y0:Cr0 8:8:8:8 little endian */ #define DRM_FORMAT_AYUV fourcc_code('A', 'Y', 'U', 'V') /* [31:0] A:Y:Cb:Cr 8:8:8:8 little endian */ +#define DRM_FORMAT_XYUV8888 fourcc_code('X', 'Y', 'U', 'V') /* [31:0] X:Y:Cb:Cr 8:8:8:8 little endian */ + +/* + * packed YCbCr420 2x2 tiled formats + * first 64 bits will contain Y,Cb,Cr components for a 2x2 tile + */ +/* [63:0] A3:A2:Y3:0:Cr0:0:Y2:0:A1:A0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_Y0L0 fourcc_code('Y', '0', 'L', '0') +/* [63:0] X3:X2:Y3:0:Cr0:0:Y2:0:X1:X0:Y1:0:Cb0:0:Y0:0 1:1:8:2:8:2:8:2:1:1:8:2:8:2:8:2 little endian */ +#define DRM_FORMAT_X0L0 fourcc_code('X', '0', 'L', '0') + +/* [63:0] A3:A2:Y3:Cr0:Y2:A1:A0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_Y0L2 fourcc_code('Y', '0', 'L', '2') +/* [63:0] X3:X2:Y3:Cr0:Y2:X1:X0:Y1:Cb0:Y0 1:1:10:10:10:1:1:10:10:10 little endian */ +#define DRM_FORMAT_X0L2 fourcc_code('X', '0', 'L', '2') /* * 2 plane RGB + A @@ -182,6 +236,7 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_QCOM 0x05 #define DRM_FORMAT_MOD_VENDOR_VIVANTE 0x06 #define DRM_FORMAT_MOD_VENDOR_BROADCOM 0x07 +#define DRM_FORMAT_MOD_VENDOR_ARM 0x08 /* add more to the end as needed */ #define DRM_FORMAT_RESERVED ((1ULL << 56) - 1) @@ -297,6 +352,28 @@ extern "C" { */ #define DRM_FORMAT_MOD_SAMSUNG_64_32_TILE fourcc_mod_code(SAMSUNG, 1) +/* + * Tiled, 16 (pixels) x 16 (lines) - sized macroblocks + * + * This is a simple tiled layout using tiles of 16x16 pixels in a row-major + * layout. For YCbCr formats Cb/Cr components are taken in such a way that + * they correspond to their 16x16 luma block. + */ +#define DRM_FORMAT_MOD_SAMSUNG_16_16_TILE fourcc_mod_code(SAMSUNG, 2) + +/* + * Qualcomm Compressed Format + * + * Refers to a compressed variant of the base format that is compressed. + * Implementation may be platform and base-format specific. + * + * Each macrotile consists of m x n (mostly 4 x 4) tiles. + * Pixel data pitch/stride is aligned with macrotile width. + * Pixel data height is aligned with macrotile height. + * Entire pixel data buffer is aligned with 4k(bytes). + */ +#define DRM_FORMAT_MOD_QCOM_COMPRESSED fourcc_mod_code(QCOM, 1) + /* Vivante framebuffer modifiers */ /* @@ -383,6 +460,23 @@ extern "C" { #define DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK_THIRTYTWO_GOB \ fourcc_mod_code(NVIDIA, 0x15) +/* + * Some Broadcom modifiers take parameters, for example the number of + * vertical lines in the image. Reserve the lower 32 bits for modifier + * type, and the next 24 bits for parameters. Top 8 bits are the + * vendor code. + */ +#define __fourcc_mod_broadcom_param_shift 8 +#define __fourcc_mod_broadcom_param_bits 48 +#define fourcc_mod_broadcom_code(val, params) \ + fourcc_mod_code(BROADCOM, ((((uint64_t)params) << __fourcc_mod_broadcom_param_shift) | val)) +#define fourcc_mod_broadcom_param(m) \ + ((int)(((m) >> __fourcc_mod_broadcom_param_shift) & \ + ((1ULL << __fourcc_mod_broadcom_param_bits) - 1))) +#define fourcc_mod_broadcom_mod(m) \ + ((m) & ~(((1ULL << __fourcc_mod_broadcom_param_bits) - 1) << \ + __fourcc_mod_broadcom_param_shift)) + /* * Broadcom VC4 "T" format * @@ -404,6 +498,151 @@ extern "C" { */ #define DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED fourcc_mod_code(BROADCOM, 1) +/* + * Broadcom SAND format + * + * This is the native format that the H.264 codec block uses. For VC4 + * HVS, it is only valid for H.264 (NV12/21) and RGBA modes. + * + * The image can be considered to be split into columns, and the + * columns are placed consecutively into memory. The width of those + * columns can be either 32, 64, 128, or 256 pixels, but in practice + * only 128 pixel columns are used. + * + * The pitch between the start of each column is set to optimally + * switch between SDRAM banks. This is passed as the number of lines + * of column width in the modifier (we can't use the stride value due + * to various core checks that look at it , so you should set the + * stride to width*cpp). + * + * Note that the column height for this format modifier is the same + * for all of the planes, assuming that each column contains both Y + * and UV. Some SAND-using hardware stores UV in a separate tiled + * image from Y to reduce the column height, which is not supported + * with these modifiers. + */ + +#define DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(2, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(3, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(4, v) +#define DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(v) \ + fourcc_mod_broadcom_code(5, v) + +#define DRM_FORMAT_MOD_BROADCOM_SAND32 \ + DRM_FORMAT_MOD_BROADCOM_SAND32_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND64 \ + DRM_FORMAT_MOD_BROADCOM_SAND64_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND128 \ + DRM_FORMAT_MOD_BROADCOM_SAND128_COL_HEIGHT(0) +#define DRM_FORMAT_MOD_BROADCOM_SAND256 \ + DRM_FORMAT_MOD_BROADCOM_SAND256_COL_HEIGHT(0) + +/* Broadcom UIF format + * + * This is the common format for the current Broadcom multimedia + * blocks, including V3D 3.x and newer, newer video codecs, and + * displays. + * + * The image consists of utiles (64b blocks), UIF blocks (2x2 utiles), + * and macroblocks (4x4 UIF blocks). Those 4x4 UIF block groups are + * stored in columns, with padding between the columns to ensure that + * moving from one column to the next doesn't hit the same SDRAM page + * bank. + * + * To calculate the padding, it is assumed that each hardware block + * and the software driving it knows the platform's SDRAM page size, + * number of banks, and XOR address, and that it's identical between + * all blocks using the format. This tiling modifier will use XOR as + * necessary to reduce the padding. If a hardware block can't do XOR, + * the assumption is that a no-XOR tiling modifier will be created. + */ +#define DRM_FORMAT_MOD_BROADCOM_UIF fourcc_mod_code(BROADCOM, 6) + +/* + * Arm Framebuffer Compression (AFBC) modifiers + * + * AFBC is a proprietary lossless image compression protocol and format. + * It provides fine-grained random access and minimizes the amount of data + * transferred between IP blocks. + * + * AFBC has several features which may be supported and/or used, which are + * represented using bits in the modifier. Not all combinations are valid, + * and different devices or use-cases may support different combinations. + */ +#define DRM_FORMAT_MOD_ARM_AFBC(__afbc_mode) fourcc_mod_code(ARM, __afbc_mode) + +/* + * AFBC superblock size + * + * Indicates the superblock size(s) used for the AFBC buffer. The buffer + * size (in pixels) must be aligned to a multiple of the superblock size. + * Four lowest significant bits(LSBs) are reserved for block size. + */ +#define AFBC_FORMAT_MOD_BLOCK_SIZE_MASK 0xf +#define AFBC_FORMAT_MOD_BLOCK_SIZE_16x16 (1ULL) +#define AFBC_FORMAT_MOD_BLOCK_SIZE_32x8 (2ULL) + +/* + * AFBC lossless colorspace transform + * + * Indicates that the buffer makes use of the AFBC lossless colorspace + * transform. + */ +#define AFBC_FORMAT_MOD_YTR (1ULL << 4) + +/* + * AFBC block-split + * + * Indicates that the payload of each superblock is split. The second + * half of the payload is positioned at a predefined offset from the start + * of the superblock payload. + */ +#define AFBC_FORMAT_MOD_SPLIT (1ULL << 5) + +/* + * AFBC sparse layout + * + * This flag indicates that the payload of each superblock must be stored at a + * predefined position relative to the other superblocks in the same AFBC + * buffer. This order is the same order used by the header buffer. In this mode + * each superblock is given the same amount of space as an uncompressed + * superblock of the particular format would require, rounding up to the next + * multiple of 128 bytes in size. + */ +#define AFBC_FORMAT_MOD_SPARSE (1ULL << 6) + +/* + * AFBC copy-block restrict + * + * Buffers with this flag must obey the copy-block restriction. The restriction + * is such that there are no copy-blocks referring across the border of 8x8 + * blocks. For the subsampled data the 8x8 limitation is also subsampled. + */ +#define AFBC_FORMAT_MOD_CBR (1ULL << 7) + +/* + * AFBC tiled layout + * + * The tiled layout groups superblocks in 8x8 or 4x4 tiles, where all + * superblocks inside a tile are stored together in memory. 8x8 tiles are used + * for pixel formats up to and including 32 bpp while 4x4 tiles are used for + * larger bpp formats. The order between the tiles is scan line. + * When the tiled layout is used, the buffer size (in pixels) must be aligned + * to the tile size. + */ +#define AFBC_FORMAT_MOD_TILED (1ULL << 8) + +/* + * AFBC solid color blocks + * + * Indicates that the buffer makes use of solid-color blocks, whereby bandwidth + * can be reduced if a whole superblock is a single color. + */ +#define AFBC_FORMAT_MOD_SC (1ULL << 9) + #if defined(__cplusplus) } #endif diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index eb10c075e4..063c814278 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -91,10 +91,6 @@ * %ETHTOOL_GSET to get the current values before making specific * changes and then applying them with %ETHTOOL_SSET. * - * Drivers that implement set_settings() should validate all fields - * other than @cmd that are not described as read-only or deprecated, - * and must ignore all fields described as read-only. - * * Deprecated fields should be ignored by both users and drivers. */ struct ethtool_cmd { @@ -226,7 +222,7 @@ enum tunable_id { ETHTOOL_TX_COPYBREAK, ETHTOOL_PFC_PREVENTION_TOUT, /* timeout in msecs */ /* - * Add your fresh new tubale attribute above and remember to update + * Add your fresh new tunable attribute above and remember to update * tunable_strings[] in net/core/ethtool.c */ __ETHTOOL_TUNABLE_COUNT, @@ -870,7 +866,8 @@ struct ethtool_flow_ext { * includes the %FLOW_EXT or %FLOW_MAC_EXT flag * (see &struct ethtool_flow_ext description). * @ring_cookie: RX ring/queue index to deliver to, or %RX_CLS_FLOW_DISC - * if packets should be discarded + * if packets should be discarded, or %RX_CLS_FLOW_WAKE if the + * packets should be used for Wake-on-LAN with %WAKE_FILTER * @location: Location of rule in the table. Locations must be * numbered such that a flow matching multiple rules will be * classified according to the first (lowest numbered) rule. @@ -885,7 +882,7 @@ struct ethtool_rx_flow_spec { uint32_t location; }; -/* How rings are layed out when accessing virtual functions or +/* How rings are laid out when accessing virtual functions or * offloaded queues is device specific. To allow users to do flow * steering and specify these queues the ring cookie is partitioned * into a 32bit queue index with an 8 bit virtual function id. @@ -894,7 +891,7 @@ struct ethtool_rx_flow_spec { * devices start supporting PCIe w/ARI. However at the moment I * do not know of any devices that support this so I do not reserve * space for this at this time. If a future patch consumes the next - * byte it should be aware of this possiblity. + * byte it should be aware of this possibility. */ #define ETHTOOL_RX_FLOW_SPEC_RING 0x00000000FFFFFFFFLL #define ETHTOOL_RX_FLOW_SPEC_RING_VF 0x000000FF00000000LL @@ -902,13 +899,13 @@ struct ethtool_rx_flow_spec { static inline uint64_t ethtool_get_flow_spec_ring(uint64_t ring_cookie) { return ETHTOOL_RX_FLOW_SPEC_RING & ring_cookie; -}; +} static inline uint64_t ethtool_get_flow_spec_ring_vf(uint64_t ring_cookie) { return (ETHTOOL_RX_FLOW_SPEC_RING_VF & ring_cookie) >> ETHTOOL_RX_FLOW_SPEC_RING_VF_OFF; -}; +} /** * struct ethtool_rxnfc - command to get or set RX flow classification rules @@ -1634,6 +1631,7 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define WAKE_ARP (1 << 4) #define WAKE_MAGIC (1 << 5) #define WAKE_MAGICSECURE (1 << 6) /* only meaningful if WAKE_MAGIC */ +#define WAKE_FILTER (1 << 7) /* L2-L4 network traffic flow types */ #define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ @@ -1671,6 +1669,7 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define RXH_DISCARD (1 << 31) #define RX_CLS_FLOW_DISC 0xffffffffffffffffULL +#define RX_CLS_FLOW_WAKE 0xfffffffffffffffeULL /* Special RX classification rule insert location values */ #define RX_CLS_LOC_SPECIAL 0x80000000 /* flag */ @@ -1797,14 +1796,9 @@ enum ethtool_reset_flags { * rejected. * * Deprecated %ethtool_cmd fields transceiver, maxtxpkt and maxrxpkt - * are not available in %ethtool_link_settings. Until all drivers are - * converted to ignore them or to the new %ethtool_link_settings API, - * for both queries and changes, users should always try - * %ETHTOOL_GLINKSETTINGS first, and if it fails with -ENOTSUPP stick - * only to %ETHTOOL_GSET and %ETHTOOL_SSET consistently. If it - * succeeds, then users should stick to %ETHTOOL_GLINKSETTINGS and - * %ETHTOOL_SLINKSETTINGS (which would support drivers implementing - * either %ethtool_cmd or %ethtool_link_settings). + * are not available in %ethtool_link_settings. These fields will be + * always set to zero in %ETHTOOL_GSET reply and %ETHTOOL_SSET will + * fail if any of them is set to non-zero value. * * Users should assume that all fields not marked read-only are * writable and subject to validation by the driver. They should use diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index 9e6a8ba4ce..871ac933eb 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -708,6 +708,14 @@ #define REL_DIAL 0x07 #define REL_WHEEL 0x08 #define REL_MISC 0x09 +/* + * 0x0a is reserved and should not be used in input drivers. + * It was used by HID as REL_MISC+1 and userspace needs to detect if + * the next REL_* event is correct or is just REL_MISC + n. + * We define here REL_RESERVED so userspace can rely on it and detect + * the situation described above. + */ +#define REL_RESERVED 0x0a #define REL_MAX 0x0f #define REL_CNT (REL_MAX+1) @@ -744,6 +752,15 @@ #define ABS_MISC 0x28 +/* + * 0x2e is reserved and should not be used in input drivers. + * It was used by HID as ABS_MISC+6 and userspace needs to detect if + * the next ABS_* event is correct or is just ABS_MISC + n. + * We define here ABS_RESERVED so userspace can rely on it and detect + * the situation described above. + */ +#define ABS_RESERVED 0x2e + #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) */ diff --git a/include/standard-headers/linux/input.h b/include/standard-headers/linux/input.h index 6d6128c081..c0ad9fc2c3 100644 --- a/include/standard-headers/linux/input.h +++ b/include/standard-headers/linux/input.h @@ -267,10 +267,11 @@ struct input_mask { /* * MT_TOOL types */ -#define MT_TOOL_FINGER 0 -#define MT_TOOL_PEN 1 -#define MT_TOOL_PALM 2 -#define MT_TOOL_MAX 2 +#define MT_TOOL_FINGER 0x00 +#define MT_TOOL_PEN 0x01 +#define MT_TOOL_PALM 0x02 +#define MT_TOOL_DIAL 0x0a +#define MT_TOOL_MAX 0x0f /* * Values describing the status of a force-feedback effect diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index 4da87e2ef8..e1e9888c85 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -52,6 +52,7 @@ #define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ #define PCI_STATUS 0x06 /* 16 bits */ +#define PCI_STATUS_IMM_READY 0x01 /* Immediate Readiness */ #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 */ @@ -636,6 +637,7 @@ #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_DEVCAP2_EE_PREFIX 0x00200000 /* End-End TLP Prefix */ #define PCI_EXP_DEVCTL2 40 /* Device Control 2 */ #define PCI_EXP_DEVCTL2_COMP_TIMEOUT 0x000f /* Completion Timeout Value */ #define PCI_EXP_DEVCTL2_COMP_TMOUT_DIS 0x0010 /* Completion Timeout Disable */ @@ -960,8 +962,9 @@ #define PCI_REBAR_CTRL 8 /* control register */ #define PCI_REBAR_CTRL_BAR_IDX 0x00000007 /* BAR index */ #define PCI_REBAR_CTRL_NBAR_MASK 0x000000E0 /* # of resizable BARs */ -#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */ +#define PCI_REBAR_CTRL_NBAR_SHIFT 5 /* shift for # of BARs */ #define PCI_REBAR_CTRL_BAR_SIZE 0x00001F00 /* BAR size */ +#define PCI_REBAR_CTRL_BAR_SHIFT 8 /* shift for BAR size */ /* Dynamic Power Allocation */ #define PCI_DPA_CAP 4 /* capability register */ diff --git a/include/standard-headers/linux/qemu_fw_cfg.h b/include/standard-headers/linux/qemu_fw_cfg.h new file mode 100644 index 0000000000..cb93f6678d --- /dev/null +++ b/include/standard-headers/linux/qemu_fw_cfg.h @@ -0,0 +1,97 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef _LINUX_FW_CFG_H +#define _LINUX_FW_CFG_H + +#include "standard-headers/linux/types.h" + +#define FW_CFG_ACPI_DEVICE_ID "QEMU0002" + +/* selector key values for "well-known" fw_cfg entries */ +#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_MIN 0x10 + +#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 + +/* fw_cfg "file name" is up to 56 characters (including terminating nul) */ +#define FW_CFG_MAX_FILE_PATH 56 + +/* size in bytes of fw_cfg signature */ +#define FW_CFG_SIG_SIZE 4 + +/* FW_CFG_ID bits */ +#define FW_CFG_VERSION 0x01 +#define FW_CFG_VERSION_DMA 0x02 + +/* fw_cfg file directory entry type */ +struct fw_cfg_file { + uint32_t size; + uint16_t select; + uint16_t reserved; + char name[FW_CFG_MAX_FILE_PATH]; +}; + +/* 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_CTL_WRITE 0x10 + +#define FW_CFG_DMA_SIGNATURE 0x51454d5520434647ULL /* "QEMU CFG" */ + +/* Control as first field allows for different structures selected by this + * field, which might be useful in the future + */ +struct fw_cfg_dma_access { + uint32_t control; + uint32_t length; + uint64_t address; +}; + +#define FW_CFG_VMCOREINFO_FILENAME "etc/vmcoreinfo" + +#define FW_CFG_VMCOREINFO_FORMAT_NONE 0x0 +#define FW_CFG_VMCOREINFO_FORMAT_ELF 0x1 + +struct fw_cfg_vmcoreinfo { + uint16_t host_format; + uint16_t guest_format; + uint32_t size; + uint64_t paddr; +}; + +#endif diff --git a/include/standard-headers/linux/vhost_types.h b/include/standard-headers/linux/vhost_types.h new file mode 100644 index 0000000000..5351fe172d --- /dev/null +++ b/include/standard-headers/linux/vhost_types.h @@ -0,0 +1,128 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +#ifndef _LINUX_VHOST_TYPES_H +#define _LINUX_VHOST_TYPES_H +/* Userspace interface for in-kernel virtio accelerators. */ + +/* vhost is used to reduce the number of system calls involved in virtio. + * + * Existing virtio net code is used in the guest without modification. + * + * This header includes interface used by userspace hypervisor for + * device configuration. + */ + +#include "standard-headers/linux/types.h" + +#include "standard-headers/linux/virtio_config.h" +#include "standard-headers/linux/virtio_ring.h" + +struct vhost_vring_state { + unsigned int index; + unsigned int num; +}; + +struct vhost_vring_file { + unsigned int index; + int fd; /* Pass -1 to unbind from file. */ + +}; + +struct vhost_vring_addr { + unsigned int index; + /* Option flags. */ + unsigned int flags; + /* Flag values: */ + /* Whether log address is valid. If set enables logging. */ +#define VHOST_VRING_F_LOG 0 + + /* Start of array of descriptors (virtually contiguous) */ + uint64_t desc_user_addr; + /* Used structure address. Must be 32 bit aligned */ + uint64_t used_user_addr; + /* Available structure address. Must be 16 bit aligned */ + uint64_t avail_user_addr; + /* Logging support. */ + /* Log writes to used structure, at offset calculated from specified + * address. Address must be 32 bit aligned. */ + uint64_t log_guest_addr; +}; + +/* no alignment requirement */ +struct vhost_iotlb_msg { + uint64_t iova; + uint64_t size; + uint64_t uaddr; +#define VHOST_ACCESS_RO 0x1 +#define VHOST_ACCESS_WO 0x2 +#define VHOST_ACCESS_RW 0x3 + uint8_t perm; +#define VHOST_IOTLB_MISS 1 +#define VHOST_IOTLB_UPDATE 2 +#define VHOST_IOTLB_INVALIDATE 3 +#define VHOST_IOTLB_ACCESS_FAIL 4 + uint8_t type; +}; + +#define VHOST_IOTLB_MSG 0x1 +#define VHOST_IOTLB_MSG_V2 0x2 + +struct vhost_msg { + int type; + union { + struct vhost_iotlb_msg iotlb; + uint8_t padding[64]; + }; +}; + +struct vhost_msg_v2 { + uint32_t type; + uint32_t reserved; + union { + struct vhost_iotlb_msg iotlb; + uint8_t padding[64]; + }; +}; + +struct vhost_memory_region { + uint64_t guest_phys_addr; + uint64_t memory_size; /* bytes */ + uint64_t userspace_addr; + uint64_t flags_padding; /* No flags are currently specified. */ +}; + +/* All region addresses and sizes must be 4K aligned. */ +#define VHOST_PAGE_SIZE 0x1000 + +struct vhost_memory { + uint32_t nregions; + uint32_t padding; + struct vhost_memory_region regions[0]; +}; + +/* VHOST_SCSI specific definitions */ + +/* + * 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 field in struct vhost_scsi_target. + * All the targets under vhost_wwpn can be seen and used by guset. + */ + +#define VHOST_SCSI_ABI_VERSION 1 + +struct vhost_scsi_target { + int abi_version; + char vhost_wwpn[224]; /* TRANSPORT_IQN_LEN */ + unsigned short vhost_tpgt; + unsigned short reserved; +}; + +/* Feature bits */ +/* Log all write descriptors. Can be changed while device is active. */ +#define VHOST_F_LOG_ALL 26 +/* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */ +#define VHOST_NET_F_VIRTIO_NET_HDR 27 + +#endif diff --git a/include/standard-headers/linux/virtio_balloon.h b/include/standard-headers/linux/virtio_balloon.h index 4dbb7dc6c0..9375ca2a70 100644 --- a/include/standard-headers/linux/virtio_balloon.h +++ b/include/standard-headers/linux/virtio_balloon.h @@ -34,15 +34,23 @@ #define VIRTIO_BALLOON_F_MUST_TELL_HOST 0 /* Tell before reclaiming pages */ #define VIRTIO_BALLOON_F_STATS_VQ 1 /* Memory Stats virtqueue */ #define VIRTIO_BALLOON_F_DEFLATE_ON_OOM 2 /* Deflate balloon on OOM */ +#define VIRTIO_BALLOON_F_FREE_PAGE_HINT 3 /* VQ to report free pages */ +#define VIRTIO_BALLOON_F_PAGE_POISON 4 /* Guest is using page poisoning */ /* Size of a PFN in the balloon interface. */ #define VIRTIO_BALLOON_PFN_SHIFT 12 +#define VIRTIO_BALLOON_CMD_ID_STOP 0 +#define VIRTIO_BALLOON_CMD_ID_DONE 1 struct virtio_balloon_config { /* Number of pages host wants Guest to give up. */ uint32_t num_pages; /* Number of pages we've actually got in balloon. */ uint32_t actual; + /* Free page report command id, readonly by guest */ + uint32_t free_page_report_cmd_id; + /* Stores PAGE_POISON if page poisoning is in use */ + uint32_t poison_val; }; #define VIRTIO_BALLOON_S_SWAP_IN 0 /* Amount of memory swapped in */ diff --git a/include/standard-headers/linux/virtio_blk.h b/include/standard-headers/linux/virtio_blk.h index ab16ec5fd2..0229b0fbe4 100644 --- a/include/standard-headers/linux/virtio_blk.h +++ b/include/standard-headers/linux/virtio_blk.h @@ -38,6 +38,8 @@ #define VIRTIO_BLK_F_BLK_SIZE 6 /* Block size of disk is available*/ #define VIRTIO_BLK_F_TOPOLOGY 10 /* Topology information is available */ #define VIRTIO_BLK_F_MQ 12 /* support more than one vq */ +#define VIRTIO_BLK_F_DISCARD 13 /* DISCARD is supported */ +#define VIRTIO_BLK_F_WRITE_ZEROES 14 /* WRITE ZEROES is supported */ /* Legacy feature bits */ #ifndef VIRTIO_BLK_NO_LEGACY @@ -84,6 +86,39 @@ struct virtio_blk_config { /* number of vqs, only available when VIRTIO_BLK_F_MQ is set */ uint16_t num_queues; + + /* the next 3 entries are guarded by VIRTIO_BLK_F_DISCARD */ + /* + * The maximum discard sectors (in 512-byte sectors) for + * one segment. + */ + uint32_t max_discard_sectors; + /* + * The maximum number of discard segments in a + * discard command. + */ + uint32_t max_discard_seg; + /* Discard commands must be aligned to this number of sectors. */ + uint32_t discard_sector_alignment; + + /* the next 3 entries are guarded by VIRTIO_BLK_F_WRITE_ZEROES */ + /* + * The maximum number of write zeroes sectors (in 512-byte sectors) in + * one segment. + */ + uint32_t max_write_zeroes_sectors; + /* + * The maximum number of segments in a write zeroes + * command. + */ + uint32_t max_write_zeroes_seg; + /* + * Set if a VIRTIO_BLK_T_WRITE_ZEROES request may result in the + * deallocation of one or more of the sectors. + */ + uint8_t write_zeroes_may_unmap; + + uint8_t unused1[3]; } QEMU_PACKED; /* @@ -112,6 +147,12 @@ struct virtio_blk_config { /* Get device ID command */ #define VIRTIO_BLK_T_GET_ID 8 +/* Discard command */ +#define VIRTIO_BLK_T_DISCARD 11 + +/* Write zeroes command */ +#define VIRTIO_BLK_T_WRITE_ZEROES 13 + #ifndef VIRTIO_BLK_NO_LEGACY /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000 @@ -131,6 +172,19 @@ struct virtio_blk_outhdr { __virtio64 sector; }; +/* Unmap this range (only valid for write zeroes command) */ +#define VIRTIO_BLK_WRITE_ZEROES_FLAG_UNMAP 0x00000001 + +/* Discard/write zeroes range for each request. */ +struct virtio_blk_discard_write_zeroes { + /* discard/write zeroes start sector */ + uint64_t sector; + /* number of discard/write zeroes sectors */ + uint32_t num_sectors; + /* flags for this range */ + uint32_t flags; +}; + #ifndef VIRTIO_BLK_NO_LEGACY struct virtio_scsi_inhdr { __virtio32 errors; diff --git a/include/standard-headers/linux/virtio_config.h b/include/standard-headers/linux/virtio_config.h index b777069699..24e30af5ec 100644 --- a/include/standard-headers/linux/virtio_config.h +++ b/include/standard-headers/linux/virtio_config.h @@ -45,11 +45,14 @@ /* We've given up on this device. */ #define VIRTIO_CONFIG_S_FAILED 0x80 -/* Some virtio feature bits (currently bits 28 through 32) are reserved for the - * transport being used (eg. virtio_ring), the rest are per-device feature - * bits. */ +/* + * Virtio feature bits VIRTIO_TRANSPORT_F_START through + * VIRTIO_TRANSPORT_F_END are reserved for the transport + * being used (e.g. virtio_ring, virtio_pci etc.), the + * rest are per-device feature bits. + */ #define VIRTIO_TRANSPORT_F_START 28 -#define VIRTIO_TRANSPORT_F_END 34 +#define VIRTIO_TRANSPORT_F_END 38 #ifndef VIRTIO_CONFIG_NO_LEGACY /* Do we get callbacks when the ring is completely used, even if we've @@ -71,4 +74,12 @@ * this is for compatibility with legacy systems. */ #define VIRTIO_F_IOMMU_PLATFORM 33 + +/* This feature indicates support for the packed virtqueue layout. */ +#define VIRTIO_F_RING_PACKED 34 + +/* + * Does the device support Single Root I/O Virtualization? + */ +#define VIRTIO_F_SR_IOV 37 #endif /* _LINUX_VIRTIO_CONFIG_H */ diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index 52a830dcf8..27bb5111f9 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -41,6 +41,7 @@ #include "standard-headers/linux/types.h" #define VIRTIO_GPU_F_VIRGL 0 +#define VIRTIO_GPU_F_EDID 1 enum virtio_gpu_ctrl_type { VIRTIO_GPU_UNDEFINED = 0, @@ -56,6 +57,7 @@ enum virtio_gpu_ctrl_type { VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, VIRTIO_GPU_CMD_GET_CAPSET_INFO, VIRTIO_GPU_CMD_GET_CAPSET, + VIRTIO_GPU_CMD_GET_EDID, /* 3d commands */ VIRTIO_GPU_CMD_CTX_CREATE = 0x0200, @@ -76,6 +78,7 @@ enum virtio_gpu_ctrl_type { VIRTIO_GPU_RESP_OK_DISPLAY_INFO, VIRTIO_GPU_RESP_OK_CAPSET_INFO, VIRTIO_GPU_RESP_OK_CAPSET, + VIRTIO_GPU_RESP_OK_EDID, /* error responses */ VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, @@ -291,6 +294,21 @@ struct virtio_gpu_resp_capset { uint8_t capset_data[]; }; +/* VIRTIO_GPU_CMD_GET_EDID */ +struct virtio_gpu_cmd_get_edid { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t scanout; + uint32_t padding; +}; + +/* VIRTIO_GPU_RESP_OK_EDID */ +struct virtio_gpu_resp_edid { + struct virtio_gpu_ctrl_hdr hdr; + uint32_t size; + uint32_t padding; + uint8_t edid[1024]; +}; + #define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) struct virtio_gpu_config { diff --git a/include/standard-headers/linux/virtio_ring.h b/include/standard-headers/linux/virtio_ring.h index d26e72bc6b..e89931f634 100644 --- a/include/standard-headers/linux/virtio_ring.h +++ b/include/standard-headers/linux/virtio_ring.h @@ -42,6 +42,13 @@ /* This means the buffer contains a list of buffer descriptors. */ #define VRING_DESC_F_INDIRECT 4 +/* + * Mark a descriptor as available or used in packed ring. + * Notice: they are defined as shifts instead of shifted values. + */ +#define VRING_PACKED_DESC_F_AVAIL 7 +#define VRING_PACKED_DESC_F_USED 15 + /* The Host uses this in used->flags to advise the Guest: don't kick me when * you add a buffer. It's unreliable, so it's simply an optimization. Guest * will still kick if it's out of buffers. */ @@ -51,6 +58,23 @@ * optimization. */ #define VRING_AVAIL_F_NO_INTERRUPT 1 +/* Enable events in packed ring. */ +#define VRING_PACKED_EVENT_FLAG_ENABLE 0x0 +/* Disable events in packed ring. */ +#define VRING_PACKED_EVENT_FLAG_DISABLE 0x1 +/* + * Enable events for a specific descriptor in packed ring. + * (as specified by Descriptor Ring Change Event Offset/Wrap Counter). + * Only valid if VIRTIO_RING_F_EVENT_IDX has been negotiated. + */ +#define VRING_PACKED_EVENT_FLAG_DESC 0x2 + +/* + * Wrap counter bit shift in event suppression structure + * of packed ring. + */ +#define VRING_PACKED_EVENT_F_WRAP_CTR 15 + /* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 @@ -169,4 +193,32 @@ static inline int vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_ return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old); } +struct vring_packed_desc_event { + /* Descriptor Ring Change Event Offset/Wrap Counter. */ + uint16_t off_wrap; + /* Descriptor Ring Change Event Flags. */ + uint16_t flags; +}; + +struct vring_packed_desc { + /* Buffer Address. */ + uint64_t addr; + /* Buffer Length. */ + uint32_t len; + /* Buffer ID. */ + uint16_t id; + /* The flags depending on descriptor type. */ + uint16_t flags; +}; + +struct vring_packed { + unsigned int num; + + struct vring_packed_desc *desc; + + struct vring_packed_desc_event *driver; + + struct vring_packed_desc_event *device; +}; + #endif /* _LINUX_VIRTIO_RING_H */ diff --git a/include/sysemu/accel.h b/include/sysemu/accel.h index 637358f430..5565e00a96 100644 --- a/include/sysemu/accel.h +++ b/include/sysemu/accel.h @@ -49,7 +49,7 @@ typedef struct AccelClass { * global properties may be overridden by machine-type * compat_props or user-provided global properties. */ - GlobalProperty *global_props; + GPtrArray *compat_props; } AccelClass; #define TYPE_ACCEL "accel" @@ -66,9 +66,7 @@ typedef struct AccelClass { extern unsigned long tcg_tb_size; -void configure_accelerator(MachineState *ms); -/* Register accelerator specific global properties */ -void accel_register_compat_props(AccelState *accel); +void configure_accelerator(MachineState *ms, const char *progname); /* Called just before os_setup_post (ie just before drop OS privs) */ void accel_setup_post(MachineState *ms); diff --git a/include/sysemu/arch_init.h b/include/sysemu/arch_init.h index 32abdfe6a1..10cbafe970 100644 --- a/include/sysemu/arch_init.h +++ b/include/sysemu/arch_init.h @@ -32,15 +32,4 @@ extern const uint32_t arch_type; int kvm_available(void); int xen_available(void); -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp); -CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *mode, - Error **errp); -CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *modela, - CpuModelInfo *modelb, - Error **errp); -CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *modela, - CpuModelInfo *modelb, - Error **errp); - #endif diff --git a/include/sysemu/balloon.h b/include/sysemu/balloon.h index 66543ae8f4..c8f6145257 100644 --- a/include/sysemu/balloon.h +++ b/include/sysemu/balloon.h @@ -20,7 +20,7 @@ typedef void (QEMUBalloonEvent)(void *opaque, ram_addr_t target); typedef void (QEMUBalloonStatus)(void *opaque, BalloonInfo *info); int qemu_add_balloon_handler(QEMUBalloonEvent *event_func, - QEMUBalloonStatus *stat_func, void *opaque); + QEMUBalloonStatus *stat_func, void *opaque); void qemu_remove_balloon_handler(void *opaque); bool qemu_balloon_is_inhibited(void); void qemu_balloon_inhibit(bool state); diff --git a/include/sysemu/block-backend.h b/include/sysemu/block-backend.h index 830d873f24..3be05c2d68 100644 --- a/include/sysemu/block-backend.h +++ b/include/sysemu/block-backend.h @@ -110,9 +110,8 @@ 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, DeviceState *dev); -void blk_attach_dev_legacy(BlockBackend *blk, void *dev); -void blk_detach_dev(BlockBackend *blk, void *dev); -void *blk_get_attached_dev(BlockBackend *blk); +void blk_detach_dev(BlockBackend *blk, DeviceState *dev); +DeviceState *blk_get_attached_dev(BlockBackend *blk); char *blk_get_attached_dev_id(BlockBackend *blk); BlockBackend *blk_by_dev(void *dev); BlockBackend *blk_by_qdev_id(const char *id, Error **errp); @@ -157,6 +156,8 @@ int blk_co_pdiscard(BlockBackend *blk, int64_t offset, int bytes); int blk_co_flush(BlockBackend *blk); int blk_flush(BlockBackend *blk); int blk_commit_all(void); +void blk_inc_in_flight(BlockBackend *blk); +void blk_dec_in_flight(BlockBackend *blk); void blk_drain(BlockBackend *blk); void blk_drain_all(void); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, @@ -166,9 +167,9 @@ BlockErrorAction blk_get_error_action(BlockBackend *blk, bool is_read, int error); void blk_error_action(BlockBackend *blk, BlockErrorAction action, bool is_read, int error); -int blk_is_read_only(BlockBackend *blk); -int blk_is_sg(BlockBackend *blk); -int blk_enable_write_cache(BlockBackend *blk); +bool blk_is_read_only(BlockBackend *blk); +bool blk_is_sg(BlockBackend *blk); +bool blk_enable_write_cache(BlockBackend *blk); void blk_set_enable_write_cache(BlockBackend *blk, bool wce); void blk_invalidate_cache(BlockBackend *blk, Error **errp); bool blk_is_inserted(BlockBackend *blk); @@ -176,6 +177,7 @@ 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); +uint32_t blk_get_request_alignment(BlockBackend *blk); uint32_t blk_get_max_transfer(BlockBackend *blk); int blk_get_max_iov(BlockBackend *blk); void blk_set_guest_block_size(BlockBackend *blk, int align); @@ -237,4 +239,6 @@ int coroutine_fn blk_co_copy_range(BlockBackend *blk_in, int64_t off_in, int bytes, BdrvRequestFlags read_flags, BdrvRequestFlags write_flags); +const BdrvChild *blk_root(BlockBackend *blk); + #endif diff --git a/include/sysemu/blockdev.h b/include/sysemu/blockdev.h index 73b6439055..900c0de956 100644 --- a/include/sysemu/blockdev.h +++ b/include/sysemu/blockdev.h @@ -28,16 +28,13 @@ typedef enum { } BlockInterfaceType; struct DriveInfo { - const char *devaddr; BlockInterfaceType type; int bus; int unit; int auto_del; /* see blockdev_mark_auto_del() */ bool is_default; /* Added by default_drive() ? */ int media_cd; - int cyls, heads, secs, trans; QemuOpts *opts; - char *serial; bool locked; QTAILQ_ENTRY(DriveInfo) next; }; @@ -58,7 +55,8 @@ DriveInfo *drive_get_next(BlockInterfaceType type); QemuOpts *drive_def(const char *optstr); QemuOpts *drive_add(BlockInterfaceType type, int index, const char *file, const char *optstr); -DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type); +DriveInfo *drive_new(QemuOpts *arg, BlockInterfaceType block_default_type, + Error **errp); /* device-hotplug */ diff --git a/include/sysemu/bt.h b/include/sysemu/bt.h index ddb05cd109..2fd8c0f14b 100644 --- a/include/sysemu/bt.h +++ b/include/sysemu/bt.h @@ -3,7 +3,7 @@ /* BT HCI info */ -struct HCIInfo { +typedef struct HCIInfo { int (*bdaddr_set)(struct HCIInfo *hci, const uint8_t *bd_addr); void (*cmd_send)(struct HCIInfo *hci, const uint8_t *data, int len); void (*sco_send)(struct HCIInfo *hci, const uint8_t *data, int len); @@ -11,7 +11,7 @@ struct HCIInfo { void *opaque; void (*evt_recv)(void *opaque, const uint8_t *data, int len); void (*acl_recv)(void *opaque, const uint8_t *data, int len); -}; +} HCIInfo; /* bt-host.c */ struct HCIInfo *bt_host_hci(const char *id); diff --git a/include/sysemu/hostmem.h b/include/sysemu/hostmem.h index 6e6bd2c1cb..a023b372a4 100644 --- a/include/sysemu/hostmem.h +++ b/include/sysemu/hostmem.h @@ -53,7 +53,7 @@ struct HostMemoryBackend { /* protected */ uint64_t size; - bool merge, dump; + bool merge, dump, use_canonical_path; bool prealloc, force_prealloc, is_mapped, share; DECLARE_BITMAP(host_nodes, MAX_NODES + 1); HostMemPolicy policy; @@ -67,5 +67,6 @@ MemoryRegion *host_memory_backend_get_memory(HostMemoryBackend *backend); void host_memory_backend_set_mapped(HostMemoryBackend *backend, bool mapped); bool host_memory_backend_is_mapped(HostMemoryBackend *backend); size_t host_memory_backend_pagesize(HostMemoryBackend *memdev); +char *host_memory_backend_get_name(HostMemoryBackend *backend); #endif diff --git a/include/sysemu/hvf.h b/include/sysemu/hvf.h index 2878ec08c5..8c292a5251 100644 --- a/include/sysemu/hvf.h +++ b/include/sysemu/hvf.h @@ -17,11 +17,11 @@ #include "exec/memory.h" #include "sysemu/accel.h" -extern int hvf_disabled; +extern bool hvf_allowed; #ifdef CONFIG_HVF uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); -#define hvf_enabled() !hvf_disabled +#define hvf_enabled() (hvf_allowed) #else #define hvf_enabled() 0 #define hvf_get_supported_cpuid(func, idx, reg) 0 diff --git a/include/sysemu/iothread.h b/include/sysemu/iothread.h index 8a7ac2c528..5f6240d5cb 100644 --- a/include/sysemu/iothread.h +++ b/include/sysemu/iothread.h @@ -24,11 +24,10 @@ typedef struct { QemuThread thread; AioContext *ctx; + bool run_gcontext; /* whether we should run gcontext */ GMainContext *worker_context; GMainLoop *main_loop; - GOnce once; - QemuMutex init_done_lock; - QemuCond init_done_cond; /* is thread initialization done? */ + QemuSemaphore init_done_sem; /* is thread init done? */ bool stopping; /* has iothread_stop() been called? */ bool running; /* should iothread_run() continue? */ int thread_id; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index 0b64b8e067..a6d1cd190f 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -412,8 +412,6 @@ struct kvm_sw_breakpoint { QTAILQ_ENTRY(kvm_sw_breakpoint) entry; }; -QTAILQ_HEAD(kvm_sw_breakpoint_head, kvm_sw_breakpoint); - struct kvm_sw_breakpoint *kvm_find_sw_breakpoint(CPUState *cpu, target_ulong pc); @@ -463,6 +461,8 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension); uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, uint32_t index, int reg); +uint32_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); + void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); diff --git a/include/sysemu/memory_mapping.h b/include/sysemu/memory_mapping.h index 706152d533..58452457ce 100644 --- a/include/sysemu/memory_mapping.h +++ b/include/sysemu/memory_mapping.h @@ -36,7 +36,7 @@ typedef struct GuestPhysBlock { /* point-in-time snapshot of guest-visible physical mappings */ typedef struct GuestPhysBlockList { unsigned num; - QTAILQ_HEAD(GuestPhysBlockHead, GuestPhysBlock) head; + QTAILQ_HEAD(, GuestPhysBlock) head; } GuestPhysBlockList; /* The physical and virtual address in the memory mapping are contiguous. */ diff --git a/include/sysemu/numa.h b/include/sysemu/numa.h index 7a0ae751aa..b6ac7de43e 100644 --- a/include/sysemu/numa.h +++ b/include/sysemu/numa.h @@ -9,7 +9,7 @@ extern int nb_numa_nodes; /* Number of NUMA nodes */ extern bool have_numa_distance; -struct node_info { +struct NodeInfo { uint64_t node_mem; struct HostMemoryBackend *node_memdev; bool present; @@ -22,7 +22,6 @@ struct NumaNodeMem { }; extern NodeInfo numa_info[MAX_NODES]; -int parse_numa(void *opaque, QemuOpts *opts, Error **errp); void parse_numa_opts(MachineState *ms); void numa_complete_configuration(MachineState *ms); void query_numa_node_mem(NumaNodeMem node_mem[]); diff --git a/include/sysemu/replay.h b/include/sysemu/replay.h index 3ced6bc231..3a7c58e423 100644 --- a/include/sysemu/replay.h +++ b/include/sysemu/replay.h @@ -100,14 +100,20 @@ 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); +int64_t replay_save_clock(ReplayClockKind kind, int64_t clock, + int64_t raw_icount); /*! 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)) \ + ? replay_save_clock((clock), (value), cpu_get_icount_raw()) \ + : (value)) +#define REPLAY_CLOCK_LOCKED(clock, value) \ + (replay_mode == REPLAY_MODE_PLAY ? replay_read_clock((clock)) \ + : replay_mode == REPLAY_MODE_RECORD \ + ? replay_save_clock((clock), (value), cpu_get_icount_raw_locked()) \ : (value)) /* Events */ @@ -120,6 +126,9 @@ void replay_shutdown_request(ShutdownCause cause); Returns 0 in PLAY mode if checkpoint was not found. Returns 1 in all other cases. */ bool replay_checkpoint(ReplayCheckpoint checkpoint); +/*! Used to determine that checkpoint is pending. + Does not proceed to the next event in the log. */ +bool replay_has_checkpoint(void); /* Asynchronous events queue */ diff --git a/include/sysemu/rng.h b/include/sysemu/rng.h index 45629c4c53..27b37da05d 100644 --- a/include/sysemu/rng.h +++ b/include/sysemu/rng.h @@ -57,7 +57,7 @@ struct RngBackend /*< protected >*/ bool opened; - QSIMPLEQ_HEAD(requests, RngRequest) requests; + QSIMPLEQ_HEAD(, RngRequest) requests; }; diff --git a/include/sysemu/sysemu.h b/include/sysemu/sysemu.h index 76ef6196a7..5f133cae83 100644 --- a/include/sysemu/sysemu.h +++ b/include/sysemu/sysemu.h @@ -14,6 +14,7 @@ /* vl.c */ extern const char *bios_name; +extern int only_migratable; extern const char *qemu_name; extern QemuUUID qemu_uuid; extern bool qemu_uuid_set; @@ -31,26 +32,6 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); void vm_state_notify(int running, RunState state); -/* Enumeration of various causes for shutdown. */ -typedef enum ShutdownCause { - SHUTDOWN_CAUSE_NONE, /* No shutdown request pending */ - SHUTDOWN_CAUSE_HOST_ERROR, /* An error prevents further use of guest */ - SHUTDOWN_CAUSE_HOST_QMP, /* Reaction to a QMP command, like 'quit' */ - SHUTDOWN_CAUSE_HOST_SIGNAL, /* Reaction to a signal, such as SIGINT */ - SHUTDOWN_CAUSE_HOST_UI, /* Reaction to UI event, like window close */ - SHUTDOWN_CAUSE_GUEST_SHUTDOWN,/* Guest shutdown/suspend request, via - ACPI or other hardware-specific means */ - SHUTDOWN_CAUSE_GUEST_RESET, /* Guest reset request, and command line - turns that into a shutdown */ - SHUTDOWN_CAUSE_GUEST_PANIC, /* Guest panicked, and command line turns - that into a shutdown */ - SHUTDOWN_CAUSE_SUBSYSTEM_RESET,/* Partial guest reset that does not trigger - QMP events and ignores --no-reboot. This - is useful for sanitize hypercalls on s390 - that are used during kexec/kdump/boot */ - SHUTDOWN_CAUSE__MAX, -} ShutdownCause; - static inline bool shutdown_caused_by_guest(ShutdownCause cause) { return cause >= SHUTDOWN_CAUSE_GUEST_SHUTDOWN; @@ -74,12 +55,15 @@ void qemu_exit_preconfig_request(void); void qemu_system_reset_request(ShutdownCause reason); void qemu_system_suspend_request(void); void qemu_register_suspend_notifier(Notifier *notifier); -void qemu_system_wakeup_request(WakeupReason reason); +bool qemu_wakeup_suspend_enabled(void); +void qemu_system_wakeup_request(WakeupReason reason, Error **errp); void qemu_system_wakeup_enable(WakeupReason reason, bool enabled); void qemu_register_wakeup_notifier(Notifier *notifier); +void qemu_register_wakeup_support(void); void qemu_system_shutdown_request(ShutdownCause reason); void qemu_system_powerdown_request(void); void qemu_register_powerdown_notifier(Notifier *notifier); +void qemu_register_shutdown_notifier(Notifier *notifier); void qemu_system_debug_request(void); void qemu_system_vmstop_request(RunState reason); void qemu_system_vmstop_request_prepare(void); @@ -98,8 +82,6 @@ extern bool machine_init_done; void qemu_add_machine_init_done_notifier(Notifier *notify); void qemu_remove_machine_init_done_notifier(Notifier *notify); -void qemu_announce_self(void); - extern int autostart; typedef enum { @@ -119,7 +101,6 @@ extern const char *keyboard_layout; extern int win2k_install_hack; extern int alt_grab; extern int ctrl_grab; -extern int no_frame; extern int smp_cpus; extern unsigned int max_cpus; extern int cursor_hide; @@ -130,10 +111,8 @@ extern int old_param; 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 bool enable_cpu_pm; -extern uint8_t qemu_extra_params_fw[2]; extern QEMUClockType rtc_clock; extern const char *mem_path; extern int mem_prealloc; @@ -182,7 +161,7 @@ void hmp_info_usb(Monitor *mon, const QDict *qdict); void add_boot_device_path(int32_t bootindex, DeviceState *dev, const char *suffix); -char *get_boot_devices_list(size_t *size, bool ignore_suffixes); +char *get_boot_devices_list(size_t *size); DeviceState *get_boot_device(uint32_t position); void check_boot_index(int32_t bootindex, Error **errp); diff --git a/include/sysemu/tpm.h b/include/sysemu/tpm.h index 9ae1ab6da3..5b541a71c8 100644 --- a/include/sysemu/tpm.h +++ b/include/sysemu/tpm.h @@ -16,7 +16,7 @@ #include "qom/object.h" int tpm_config_parse(QemuOptsList *opts_list, const char *optarg); -int tpm_init(void); +void tpm_init(void); void tpm_cleanup(void); typedef enum TPMVersion { @@ -33,9 +33,7 @@ typedef enum TPMVersion { #define TPM_IF(obj) \ INTERFACE_CHECK(TPMIf, (obj), TYPE_TPM_IF) -typedef struct TPMIf { - Object parent_obj; -} TPMIf; +typedef struct TPMIf TPMIf; typedef struct TPMIfClass { InterfaceClass parent_class; diff --git a/include/sysemu/whpx.h b/include/sysemu/whpx.h index 89592ae4fa..d200ee01d0 100644 --- a/include/sysemu/whpx.h +++ b/include/sysemu/whpx.h @@ -13,7 +13,6 @@ #ifndef QEMU_WHPX_H #define QEMU_WHPX_H -#include "config-host.h" #include "qemu-common.h" int whpx_init_vcpu(CPUState *cpu); diff --git a/include/ui/console.h b/include/ui/console.h index ef2e26e409..ff26e23110 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -65,13 +65,13 @@ void qemu_remove_led_event_handler(QEMUPutLEDEntry *entry); void kbd_put_ledstate(int ledstate); -struct MouseTransformInfo { +typedef struct MouseTransformInfo { /* Touchscreen resolution */ int x; int y; /* Calibration values as used/generated by tslib */ int a[7]; -}; +} MouseTransformInfo; void hmp_mouse_set(Monitor *mon, const QDict *qdict); @@ -121,17 +121,7 @@ struct QemuConsoleClass { #define QEMU_ALLOCATED_FLAG 0x01 -struct PixelFormat { - uint8_t bits_per_pixel; - uint8_t bytes_per_pixel; - uint8_t depth; /* color depth in bits */ - uint32_t rmask, gmask, bmask, amask; - uint8_t rshift, gshift, bshift, ashift; - uint8_t rmax, gmax, bmax, amax; - uint8_t rbits, gbits, bbits, abits; -}; - -struct DisplaySurface { +typedef struct DisplaySurface { pixman_format_code_t format; pixman_image_t *image; uint8_t flags; @@ -140,7 +130,7 @@ struct DisplaySurface { GLenum gltype; GLuint texture; #endif -}; +} DisplaySurface; typedef struct QemuUIInfo { /* geometry */ @@ -179,14 +169,17 @@ struct QEMUGLParams { int minor_ver; }; -struct QemuDmaBuf { +typedef struct QemuDmaBuf { int fd; uint32_t width; uint32_t height; uint32_t stride; uint32_t fourcc; uint32_t texture; -}; + bool y0_top; +} QemuDmaBuf; + +typedef struct DisplayState DisplayState; typedef struct DisplayChangeListenerOps { const char *dpy_name; @@ -256,10 +249,6 @@ 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, - uint64_t addr); DisplaySurface *qemu_create_message_surface(int w, int h, const char *msg); PixelFormat qemu_default_pixelformat(int bpp); @@ -452,7 +441,7 @@ void qemu_display_early_init(DisplayOptions *opts); void qemu_display_init(DisplayState *ds, DisplayOptions *opts); /* vnc.c */ -void vnc_display_init(const char *id); +void vnc_display_init(const char *id, Error **errp); void vnc_display_open(const char *id, Error **errp); void vnc_display_add_client(const char *id, int csock, bool skipauth); int vnc_display_password(const char *id, const char *password); diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 9db7293bdb..b976cb8728 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -27,7 +27,7 @@ void egl_fb_read(void *dst, egl_fb *src); void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip); void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, - int x, int y); + int x, int y, double scale_x, double scale_y); #ifdef CONFIG_OPENGL_DMABUF @@ -43,7 +43,7 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf); #endif -EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win); +EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win); int qemu_egl_init_dpy_x11(EGLNativeDisplayType dpy, DisplayGLMode mode); int qemu_egl_init_dpy_mesa(EGLNativeDisplayType dpy, DisplayGLMode mode); diff --git a/include/ui/gtk.h b/include/ui/gtk.h index a79780afc7..d9eedad976 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -22,25 +22,18 @@ #include #endif +#include "ui/kbd-state.h" #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 */ -#if GTK_CHECK_VERSION(3, 0, 0) -static inline void gdk_drawable_get_size(GdkWindow *w, gint *ww, gint *wh) -{ - *ww = gdk_window_get_width(w); - *wh = gdk_window_get_height(w); -} -#endif - typedef struct GtkDisplayState GtkDisplayState; typedef struct VirtualGfxConsole { GtkWidget *drawing_area; DisplayChangeListener dcl; + QKbdState *kbd; DisplaySurface *ds; pixman_image_t *convert; cairo_surface_t *surface; diff --git a/include/ui/input.h b/include/ui/input.h index 34ebc67c5a..8c8ccb999f 100644 --- a/include/ui/input.h +++ b/include/ui/input.h @@ -49,7 +49,6 @@ int qemu_input_key_value_to_scancode(const KeyValue *value, bool down, int *codes); int qemu_input_linux_to_qcode(unsigned int lnx); -InputEvent *qemu_input_event_new_btn(InputButton btn, bool down); void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down); void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, uint32_t button_old, uint32_t button_new); @@ -58,8 +57,6 @@ bool qemu_input_is_absolute(void); int qemu_input_scale_axis(int value, int min_in, int max_in, int min_out, int max_out); -InputEvent *qemu_input_event_new_move(InputEventKind kind, - InputAxis axis, int value); void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value); void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int min_in, int max_in); diff --git a/include/ui/kbd-state.h b/include/ui/kbd-state.h new file mode 100644 index 0000000000..d87833553a --- /dev/null +++ b/include/ui/kbd-state.h @@ -0,0 +1,101 @@ +/* + * 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 QEMU_UI_KBD_STATE_H +#define QEMU_UI_KBD_STATE_H 1 + +#include "qapi/qapi-types-ui.h" + +typedef enum QKbdModifier QKbdModifier; + +enum QKbdModifier { + QKBD_MOD_NONE = 0, + + QKBD_MOD_SHIFT, + QKBD_MOD_CTRL, + QKBD_MOD_ALT, + QKBD_MOD_ALTGR, + + QKBD_MOD_NUMLOCK, + QKBD_MOD_CAPSLOCK, + + QKBD_MOD__MAX +}; + +typedef struct QKbdState QKbdState; + +/** + * qkbd_state_init: init keyboard state tracker. + * + * Allocates and initializes keyboard state struct. + * + * @con: QemuConsole for this state tracker. Gets passed down to + * qemu_input_*() functions when sending key events to the guest. + */ +QKbdState *qkbd_state_init(QemuConsole *con); + +/** + * qkbd_state_free: free keyboard tracker state. + * + * @kbd: state tracker state. + */ +void qkbd_state_free(QKbdState *kbd); + +/** + * qkbd_state_key_event: process key event. + * + * Update keyboard state, send event to the guest. + * + * This function takes care to not send suspious events (keyup event + * for a key not pressed for example). + * + * @kbd: state tracker state. + * @qcode: the key pressed or released. + * @down: true for key down events, false otherwise. + */ +void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down); + +/** + * qkbd_state_set_delay: set key press delay. + * + * When set the specified delay will be added after each key event, + * using qemu_input_event_send_key_delay(). + * + * @kbd: state tracker state. + * @delay_ms: the delay in miliseconds. + */ +void qkbd_state_set_delay(QKbdState *kbd, int delay_ms); + +/** + * qkbd_state_key_get: get key state. + * + * Returns true when the key is down. + * + * @kbd: state tracker state. + * @qcode: the key to query. + */ +bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode); + +/** + * qkbd_state_modifier_get: get modifier state. + * + * Returns true when the modifier is active. + * + * @kbd: state tracker state. + * @mod: the modifier to query. + */ +bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod); + +/** + * qkbd_state_lift_all_keys: lift all pressed keys. + * + * This sends key up events to the guest for all keys which are in + * down state. + * + * @kbd: state tracker state. + */ +void qkbd_state_lift_all_keys(QKbdState *kbd); + +#endif /* QEMU_UI_KBD_STATE_H */ diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index b7c82d17fc..0668109305 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -53,6 +53,16 @@ /* -------------------------------------------------------------------- */ +typedef struct PixelFormat { + uint8_t bits_per_pixel; + uint8_t bytes_per_pixel; + uint8_t depth; /* color depth in bits */ + uint32_t rmask, gmask, bmask, amask; + uint8_t rshift, gshift, bshift, ashift; + uint8_t rmax, gmax, bmax, amax; + uint8_t rbits, gbits, bbits, abits; +} PixelFormat; + PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format); pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian); pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format); diff --git a/include/ui/qemu-spice.h b/include/ui/qemu-spice.h index c6d50eb87a..8c23dfe717 100644 --- a/include/ui/qemu-spice.h +++ b/include/ui/qemu-spice.h @@ -46,13 +46,7 @@ int qemu_spice_migrate_info(const char *hostname, int port, int tls_port, #else #define SPICE_NEEDS_SET_MM_TIME 0 #endif - -#if SPICE_SERVER_VERSION >= 0x000c02 void qemu_spice_register_ports(void); -#else -static inline Chardev *qemu_chr_open_spice_port(const char *name) -{ return NULL; } -#endif #else /* CONFIG_SPICE */ diff --git a/include/ui/sdl2.h b/include/ui/sdl2.h index f43eecdbd6..0875b8d56b 100644 --- a/include/ui/sdl2.h +++ b/include/ui/sdl2.h @@ -6,7 +6,11 @@ #include #include +#ifdef CONFIG_SDL_IMAGE +# include +#endif +#include "ui/kbd-state.h" #ifdef CONFIG_OPENGL # include "ui/egl-helpers.h" #endif @@ -27,6 +31,7 @@ struct sdl2_console { int idle_counter; int ignore_hotkeys; SDL_GLContext winctx; + QKbdState *kbd; #ifdef CONFIG_OPENGL QemuGLShader *gls; egl_fb guest_fb; @@ -41,7 +46,6 @@ void sdl2_window_destroy(struct sdl2_console *scon); void sdl2_window_resize(struct sdl2_console *scon); void sdl2_poll_events(struct sdl2_console *scon); -void sdl2_reset_keys(struct sdl2_console *scon); void sdl2_process_key(struct sdl2_console *scon, SDL_KeyboardEvent *ev); diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 87a84a59d4..53c3612c32 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -179,3 +179,7 @@ void qemu_spice_wakeup(SimpleSpiceDisplay *ssd); void qemu_spice_display_start(void); void qemu_spice_display_stop(void); int qemu_spice_display_is_running(SimpleSpiceDisplay *ssd); + +bool qemu_spice_fill_device_address(QemuConsole *con, + char *device_address, + size_t size); diff --git a/io/channel-socket.c b/io/channel-socket.c index b50e63a053..bc5f80e780 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -688,10 +688,13 @@ qio_channel_socket_close(QIOChannel *ioc, int rc = 0; if (sioc->fd != -1) { - SocketAddress *addr = socket_local_address(sioc->fd, errp); #ifdef WIN32 WSAEventSelect(sioc->fd, NULL, 0); #endif + if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_LISTEN)) { + socket_listen_cleanup(sioc->fd, errp); + } + if (closesocket(sioc->fd) < 0) { sioc->fd = -1; error_setg_errno(errp, errno, @@ -699,20 +702,6 @@ qio_channel_socket_close(QIOChannel *ioc, return -1; } sioc->fd = -1; - - if (addr && addr->type == SOCKET_ADDRESS_TYPE_UNIX - && addr->u.q_unix.path) { - if (unlink(addr->u.q_unix.path) < 0 && errno != ENOENT) { - error_setg_errno(errp, errno, - "Failed to unlink socket %s", - addr->u.q_unix.path); - rc = -1; - } - } - - if (addr) { - qapi_free_SocketAddress(addr); - } } return rc; } diff --git a/io/channel-tls.c b/io/channel-tls.c index 9628e6fa47..c98ead21b0 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -275,6 +275,9 @@ static ssize_t qio_channel_tls_readv(QIOChannel *ioc, } else { return QIO_CHANNEL_ERR_BLOCK; } + } else if (errno == ECONNABORTED && + (tioc->shutdown & QIO_CHANNEL_SHUTDOWN_READ)) { + return 0; } error_setg_errno(errp, errno, @@ -357,6 +360,8 @@ static int qio_channel_tls_shutdown(QIOChannel *ioc, { QIOChannelTLS *tioc = QIO_CHANNEL_TLS(ioc); + tioc->shutdown |= how; + return qio_channel_shutdown(tioc->master, how, errp); } diff --git a/io/channel-websock.c b/io/channel-websock.c index e6608b969d..77d30f0e4a 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -163,6 +163,7 @@ qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc, responselen = strlen(response); buffer_reserve(&ioc->encoutput, responselen); buffer_append(&ioc->encoutput, response, responselen); + g_free(response); va_end(vargs); } @@ -1224,12 +1225,18 @@ qio_channel_websock_source_check(GSource *source) QIOChannelWebsockSource *wsource = (QIOChannelWebsockSource *)source; GIOCondition cond = 0; - if (wsource->wioc->rawinput.offset || wsource->wioc->io_eof) { + if (wsource->wioc->rawinput.offset) { cond |= G_IO_IN; } if (wsource->wioc->encoutput.offset < QIO_CHANNEL_WEBSOCK_MAX_BUFFER) { cond |= G_IO_OUT; } + if (wsource->wioc->io_eof) { + cond |= G_IO_HUP; + } + if (wsource->wioc->io_err) { + cond |= G_IO_ERR; + } return cond & wsource->condition; } diff --git a/io/channel.c b/io/channel.c index 8dd0684f5d..2a26c2a2c0 100644 --- a/io/channel.c +++ b/io/channel.c @@ -400,15 +400,14 @@ off_t qio_channel_io_seek(QIOChannel *ioc, } -static void qio_channel_set_aio_fd_handlers(QIOChannel *ioc); - static void qio_channel_restart_read(void *opaque) { QIOChannel *ioc = opaque; Coroutine *co = ioc->read_coroutine; - ioc->read_coroutine = NULL; - qio_channel_set_aio_fd_handlers(ioc); + /* Assert that aio_co_wake() reenters the coroutine directly */ + assert(qemu_get_current_aio_context() == + qemu_coroutine_get_aio_context(co)); aio_co_wake(co); } @@ -417,8 +416,9 @@ static void qio_channel_restart_write(void *opaque) QIOChannel *ioc = opaque; Coroutine *co = ioc->write_coroutine; - ioc->write_coroutine = NULL; - qio_channel_set_aio_fd_handlers(ioc); + /* Assert that aio_co_wake() reenters the coroutine directly */ + assert(qemu_get_current_aio_context() == + qemu_coroutine_get_aio_context(co)); aio_co_wake(co); } @@ -469,6 +469,16 @@ void coroutine_fn qio_channel_yield(QIOChannel *ioc, } qio_channel_set_aio_fd_handlers(ioc); qemu_coroutine_yield(); + + /* Allow interrupting the operation by reentering the coroutine other than + * through the aio_fd_handlers. */ + if (condition == G_IO_IN && ioc->read_coroutine) { + ioc->read_coroutine = NULL; + qio_channel_set_aio_fd_handlers(ioc); + } else if (condition == G_IO_OUT && ioc->write_coroutine) { + ioc->write_coroutine = NULL; + qio_channel_set_aio_fd_handlers(ioc); + } } diff --git a/io/task.c b/io/task.c index 2886a2c1bc..64c4c7126a 100644 --- a/io/task.c +++ b/io/task.c @@ -24,6 +24,15 @@ #include "qemu/thread.h" #include "trace.h" +struct QIOTaskThreadData { + QIOTaskWorker worker; + gpointer opaque; + GDestroyNotify destroy; + GMainContext *context; + GSource *completion; +}; + + struct QIOTask { Object *source; QIOTaskFunc func; @@ -32,6 +41,9 @@ struct QIOTask { Error *err; gpointer result; GDestroyNotify destroyResult; + QemuMutex thread_lock; + QemuCond thread_cond; + struct QIOTaskThreadData *thread; }; @@ -49,6 +61,8 @@ QIOTask *qio_task_new(Object *source, task->func = func; task->opaque = opaque; task->destroy = destroy; + qemu_mutex_init(&task->thread_lock); + qemu_cond_init(&task->thread_cond); trace_qio_task_new(task, source, func, opaque); @@ -57,6 +71,19 @@ QIOTask *qio_task_new(Object *source, static void qio_task_free(QIOTask *task) { + qemu_mutex_lock(&task->thread_lock); + if (task->thread) { + if (task->thread->destroy) { + task->thread->destroy(task->thread->opaque); + } + + if (task->thread->context) { + g_main_context_unref(task->thread->context); + } + + g_free(task->thread); + } + if (task->destroy) { task->destroy(task->opaque); } @@ -68,35 +95,20 @@ static void qio_task_free(QIOTask *task) } object_unref(task->source); + qemu_mutex_unlock(&task->thread_lock); + qemu_mutex_destroy(&task->thread_lock); + qemu_cond_destroy(&task->thread_cond); + g_free(task); } -struct QIOTaskThreadData { - QIOTask *task; - QIOTaskWorker worker; - gpointer opaque; - GDestroyNotify destroy; - GMainContext *context; -}; - - static gboolean qio_task_thread_result(gpointer opaque) { - struct QIOTaskThreadData *data = opaque; + QIOTask *task = opaque; - trace_qio_task_thread_result(data->task); - qio_task_complete(data->task); - - if (data->destroy) { - data->destroy(data->opaque); - } - - if (data->context) { - g_main_context_unref(data->context); - } - - g_free(data); + trace_qio_task_thread_result(task); + qio_task_complete(task); return FALSE; } @@ -104,22 +116,30 @@ static gboolean qio_task_thread_result(gpointer opaque) static gpointer qio_task_thread_worker(gpointer opaque) { - struct QIOTaskThreadData *data = opaque; - GSource *idle; + QIOTask *task = opaque; - trace_qio_task_thread_run(data->task); - data->worker(data->task, data->opaque); + trace_qio_task_thread_run(task); + + task->thread->worker(task, task->thread->opaque); /* 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); + trace_qio_task_thread_exit(task); - idle = g_idle_source_new(); - g_source_set_callback(idle, qio_task_thread_result, data, NULL); - g_source_attach(idle, data->context); + qemu_mutex_lock(&task->thread_lock); + + task->thread->completion = g_idle_source_new(); + g_source_set_callback(task->thread->completion, + qio_task_thread_result, task, NULL); + g_source_attach(task->thread->completion, + task->thread->context); + trace_qio_task_thread_source_attach(task, task->thread->completion); + + qemu_cond_signal(&task->thread_cond); + qemu_mutex_unlock(&task->thread_lock); return NULL; } @@ -138,21 +158,38 @@ void qio_task_run_in_thread(QIOTask *task, g_main_context_ref(context); } - data->task = task; data->worker = worker; data->opaque = opaque; data->destroy = destroy; data->context = context; + task->thread = data; + trace_qio_task_thread_start(task, worker, opaque); qemu_thread_create(&thread, "io-task-worker", qio_task_thread_worker, - data, + task, QEMU_THREAD_DETACHED); } +void qio_task_wait_thread(QIOTask *task) +{ + qemu_mutex_lock(&task->thread_lock); + g_assert(task->thread != NULL); + while (task->thread->completion == NULL) { + qemu_cond_wait(&task->thread_cond, &task->thread_lock); + } + + trace_qio_task_thread_source_cancel(task, task->thread->completion); + g_source_destroy(task->thread->completion); + qemu_mutex_unlock(&task->thread_lock); + + qio_task_thread_result(task); +} + + void qio_task_complete(QIOTask *task) { task->func(task, task->opaque); diff --git a/io/trace-events b/io/trace-events index f70bad7cbe..378390521e 100644 --- a/io/trace-events +++ b/io/trace-events @@ -1,14 +1,16 @@ # See docs/devel/tracing.txt for syntax documentation. -# io/task.c +# 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_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" +qio_task_thread_source_attach(void *task, void *source) "Task thread source attach task=%p source=%p" +qio_task_thread_source_cancel(void *task, void *source) "Task thread source cancel task=%p source=%p" -# io/channel-socket.c +# 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" @@ -27,11 +29,11 @@ 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 +# 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 +# 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" @@ -41,7 +43,7 @@ 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 +# 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" @@ -56,7 +58,7 @@ qio_channel_websock_payload_decode(void *ioc, uint8_t opcode, size_t payload_rem qio_channel_websock_encode(void *ioc, uint8_t opcode, size_t payloadlen, size_t headerlen) "Websocket encoded ioc=%p opcode=0x%x header-len=%zu payload-len=%zu" qio_channel_websock_close(void *ioc) "Websocket close ioc=%p" -# io/channel-command.c +# 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" diff --git a/iothread.c b/iothread.c index aff1281257..7130be58e3 100644 --- a/iothread.c +++ b/iothread.c @@ -53,32 +53,37 @@ static void *iothread_run(void *opaque) IOThread *iothread = opaque; rcu_register_thread(); - + /* + * g_main_context_push_thread_default() must be called before anything + * in this new thread uses glib. + */ + g_main_context_push_thread_default(iothread->worker_context); my_iothread = iothread; - qemu_mutex_lock(&iothread->init_done_lock); iothread->thread_id = qemu_get_thread_id(); - qemu_cond_signal(&iothread->init_done_cond); - qemu_mutex_unlock(&iothread->init_done_lock); + qemu_sem_post(&iothread->init_done_sem); while (iothread->running) { + /* + * Note: from functional-wise the g_main_loop_run() below can + * already cover the aio_poll() events, but we can't run the + * main loop unconditionally because explicit aio_poll() here + * is faster than g_main_loop_run() when we do not need the + * gcontext at all (e.g., pure block layer iothreads). In + * other words, when we want to run the gcontext with the + * iothread we need to pay some performance for functionality. + */ aio_poll(iothread->ctx, true); - if (atomic_read(&iothread->worker_context)) { - GMainLoop *loop; - - g_main_context_push_thread_default(iothread->worker_context); - iothread->main_loop = - g_main_loop_new(iothread->worker_context, TRUE); - loop = iothread->main_loop; - + /* + * We must check the running state again in case it was + * changed in previous aio_poll() + */ + if (iothread->running && atomic_read(&iothread->run_gcontext)) { g_main_loop_run(iothread->main_loop); - iothread->main_loop = NULL; - g_main_loop_unref(loop); - - g_main_context_pop_thread_default(iothread->worker_context); } } + g_main_context_pop_thread_default(iothread->worker_context); rcu_unregister_thread(); return NULL; } @@ -110,6 +115,10 @@ static void iothread_instance_init(Object *obj) IOThread *iothread = IOTHREAD(obj); iothread->poll_max_ns = IOTHREAD_POLL_MAX_NS_DEFAULT; + iothread->thread_id = -1; + qemu_sem_init(&iothread->init_done_sem, 0); + /* By default, we don't run gcontext */ + atomic_set(&iothread->run_gcontext, 0); } static void iothread_instance_finalize(Object *obj) @@ -117,6 +126,7 @@ static void iothread_instance_finalize(Object *obj) IOThread *iothread = IOTHREAD(obj); iothread_stop(iothread); + /* * Before glib2 2.33.10, there is a glib2 bug that GSource context * pointer may not be cleared even if the context has already been @@ -134,9 +144,21 @@ static void iothread_instance_finalize(Object *obj) if (iothread->worker_context) { g_main_context_unref(iothread->worker_context); iothread->worker_context = NULL; + g_main_loop_unref(iothread->main_loop); + iothread->main_loop = NULL; } - qemu_cond_destroy(&iothread->init_done_cond); - qemu_mutex_destroy(&iothread->init_done_lock); + qemu_sem_destroy(&iothread->init_done_sem); +} + +static void iothread_init_gcontext(IOThread *iothread) +{ + GSource *source; + + iothread->worker_context = g_main_context_new(); + source = aio_get_g_source(iothread_get_aio_context(iothread)); + g_source_attach(source, iothread->worker_context); + g_source_unref(source); + iothread->main_loop = g_main_loop_new(iothread->worker_context, TRUE); } static void iothread_complete(UserCreatable *obj, Error **errp) @@ -147,13 +169,18 @@ static void iothread_complete(UserCreatable *obj, Error **errp) iothread->stopping = false; iothread->running = true; - iothread->thread_id = -1; iothread->ctx = aio_context_new(&local_error); if (!iothread->ctx) { error_propagate(errp, local_error); return; } + /* + * Init one GMainContext for the iothread unconditionally, even if + * it's not used + */ + iothread_init_gcontext(iothread); + aio_context_set_poll_params(iothread->ctx, iothread->poll_max_ns, iothread->poll_grow, @@ -166,10 +193,6 @@ static void iothread_complete(UserCreatable *obj, Error **errp) return; } - qemu_mutex_init(&iothread->init_done_lock); - qemu_cond_init(&iothread->init_done_cond); - iothread->once = (GOnce) G_ONCE_INIT; - /* This assumes we are called from a thread with useful CPU affinity for us * to inherit. */ @@ -181,12 +204,9 @@ static void iothread_complete(UserCreatable *obj, Error **errp) g_free(name); /* Wait for initialization to complete */ - qemu_mutex_lock(&iothread->init_done_lock); while (iothread->thread_id == -1) { - qemu_cond_wait(&iothread->init_done_cond, - &iothread->init_done_lock); + qemu_sem_wait(&iothread->init_done_sem); } - qemu_mutex_unlock(&iothread->init_done_lock); } typedef struct { @@ -335,27 +355,10 @@ IOThreadInfoList *qmp_query_iothreads(Error **errp) return head; } -static gpointer iothread_g_main_context_init(gpointer opaque) -{ - AioContext *ctx; - IOThread *iothread = opaque; - GSource *source; - - iothread->worker_context = g_main_context_new(); - - ctx = iothread_get_aio_context(iothread); - source = aio_get_g_source(ctx); - g_source_attach(source, iothread->worker_context); - g_source_unref(source); - - aio_notify(iothread->ctx); - return NULL; -} - GMainContext *iothread_get_g_main_context(IOThread *iothread) { - g_once(&iothread->once, iothread_g_main_context_init, iothread); - + atomic_set(&iothread->run_gcontext, 1); + aio_notify(iothread->ctx); return iothread->worker_context; } diff --git a/job-qmp.c b/job-qmp.c index 410775df61..a969b2bbf0 100644 --- a/job-qmp.c +++ b/job-qmp.c @@ -146,8 +146,9 @@ static JobInfo *job_query_single(Job *job, Error **errp) .status = job->status, .current_progress = job->progress_current, .total_progress = job->progress_total, - .has_error = !!job->error, - .error = g_strdup(job->error), + .has_error = !!job->err, + .error = job->err ? \ + g_strdup(error_get_pretty(job->err)) : NULL, }; return info; diff --git a/job.c b/job.c index fa671b431a..da8e4b7bf2 100644 --- a/job.c +++ b/job.c @@ -29,6 +29,7 @@ #include "qemu/job.h" #include "qemu/id.h" #include "qemu/main-loop.h" +#include "block/aio-wait.h" #include "trace-root.h" #include "qapi/qapi-events-job.h" @@ -136,21 +137,13 @@ static void job_txn_del_job(Job *job) } } -static int job_txn_apply(JobTxn *txn, int fn(Job *), bool lock) +static int job_txn_apply(JobTxn *txn, int fn(Job *)) { - AioContext *ctx; Job *job, *next; int rc = 0; QLIST_FOREACH_SAFE(job, &txn->jobs, txn_list, next) { - if (lock) { - ctx = job->aio_context; - aio_context_acquire(ctx); - } rc = fn(job); - if (lock) { - aio_context_release(ctx); - } if (rc) { break; } @@ -166,7 +159,7 @@ bool job_is_internal(Job *job) static void job_state_transition(Job *job, JobStatus s1) { JobStatus s0 = job->status; - assert(s1 >= 0 && s1 <= JOB_STATUS__MAX); + assert(s1 >= 0 && s1 < JOB_STATUS__MAX); trace_job_state_transition(job, job->ret, JobSTT[s0][s1] ? "allowed" : "disallowed", JobStatus_str(s0), JobStatus_str(s1)); @@ -174,14 +167,14 @@ static void job_state_transition(Job *job, JobStatus s1) job->status = s1; if (!job_is_internal(job) && s1 != s0) { - qapi_event_send_job_status_change(job->id, job->status, &error_abort); + qapi_event_send_job_status_change(job->id, job->status); } } int job_apply_verb(Job *job, JobVerb verb, Error **errp) { JobStatus s0 = job->status; - assert(verb >= 0 && verb <= JOB_VERB__MAX); + assert(verb >= 0 && verb < JOB_VERB__MAX); trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb), JobVerbTable[verb][s0] ? "allowed" : "prohibited"); if (JobVerbTable[verb][s0]) { @@ -369,7 +362,7 @@ void job_unref(Job *job) QLIST_REMOVE(job, job_list); - g_free(job->error); + error_free(job->err); g_free(job->id); g_free(job); } @@ -410,6 +403,11 @@ static void job_event_ready(Job *job) notifier_list_notify(&job->on_ready, job); } +static void job_event_idle(Job *job) +{ + notifier_list_notify(&job->on_idle, job); +} + void job_enter_cond(Job *job, bool(*fn)(Job *job)) { if (!job_started(job)) { @@ -455,6 +453,7 @@ static void coroutine_fn job_do_yield(Job *job, uint64_t ns) timer_mod(&job->sleep_timer, ns); } job->busy = false; + job_event_idle(job); job_unlock(); qemu_coroutine_yield(); @@ -535,33 +534,6 @@ void job_drain(Job *job) } } - -/** - * All jobs must allow a pause point before entering their job proper. This - * ensures that jobs can be paused prior to being started, then resumed later. - */ -static void coroutine_fn job_co_entry(void *opaque) -{ - Job *job = opaque; - - assert(job && job->driver && job->driver->start); - job_pause_point(job); - job->driver->start(job); -} - - -void job_start(Job *job) -{ - assert(job && !job_started(job) && job->paused && - job->driver && job->driver->start); - job->co = qemu_coroutine_create(job_co_entry, job); - job->pause_count--; - job->busy = true; - job->paused = false; - job_state_transition(job, JOB_STATUS_RUNNING); - aio_co_enter(job->aio_context, job->co); -} - /* Assumes the block_job_mutex is held */ static bool job_timer_not_pending(Job *job) { @@ -666,8 +638,8 @@ static void job_update_rc(Job *job) job->ret = -ECANCELED; } if (job->ret) { - if (!job->error) { - job->error = g_strdup(strerror(-job->ret)); + if (!job->err) { + error_setg(&job->err, "%s", strerror(-job->ret)); } job_state_transition(job, JOB_STATUS_ABORTING); } @@ -732,10 +704,10 @@ static void job_cancel_async(Job *job, bool force) { if (job->user_paused) { /* Do not call job_enter here, the caller will handle it. */ - job->user_paused = false; if (job->driver->user_resume) { job->driver->user_resume(job); } + job->user_paused = false; assert(job->pause_count > 0); job->pause_count--; } @@ -746,6 +718,7 @@ static void job_cancel_async(Job *job, bool force) static void job_completed_txn_abort(Job *job) { + AioContext *outer_ctx = job->aio_context; AioContext *ctx; JobTxn *txn = job->txn; Job *other_job; @@ -759,23 +732,26 @@ static void job_completed_txn_abort(Job *job) txn->aborting = true; job_txn_ref(txn); - /* We are the first failed job. Cancel other jobs. */ - QLIST_FOREACH(other_job, &txn->jobs, txn_list) { - ctx = other_job->aio_context; - aio_context_acquire(ctx); - } + /* We can only hold the single job's AioContext lock while calling + * job_finalize_single() because the finalization callbacks can involve + * calls of AIO_WAIT_WHILE(), which could deadlock otherwise. */ + aio_context_release(outer_ctx); /* 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. */ QLIST_FOREACH(other_job, &txn->jobs, txn_list) { if (other_job != job) { + ctx = other_job->aio_context; + aio_context_acquire(ctx); job_cancel_async(other_job, false); + aio_context_release(ctx); } } while (!QLIST_EMPTY(&txn->jobs)) { other_job = QLIST_FIRST(&txn->jobs); ctx = other_job->aio_context; + aio_context_acquire(ctx); if (!job_is_completed(other_job)) { assert(job_is_cancelled(other_job)); job_finish_sync(other_job, NULL, NULL); @@ -784,6 +760,8 @@ static void job_completed_txn_abort(Job *job) aio_context_release(ctx); } + aio_context_acquire(outer_ctx); + job_txn_unref(txn); } @@ -807,11 +785,11 @@ static void job_do_finalize(Job *job) assert(job && job->txn); /* prepare the transaction to complete */ - rc = job_txn_apply(job->txn, job_prepare, true); + rc = job_txn_apply(job->txn, job_prepare); if (rc) { job_completed_txn_abort(job); } else { - job_txn_apply(job->txn, job_finalize_single, true); + job_txn_apply(job->txn, job_finalize_single); } } @@ -857,27 +835,20 @@ static void job_completed_txn_success(Job *job) assert(other_job->ret == 0); } - job_txn_apply(txn, job_transition_to_pending, false); + job_txn_apply(txn, job_transition_to_pending); /* If no jobs need manual finalization, automatically do so */ - if (job_txn_apply(txn, job_needs_finalize, false) == 0) { + if (job_txn_apply(txn, job_needs_finalize) == 0) { job_do_finalize(job); } } -void job_completed(Job *job, int ret, Error *error) +static void job_completed(Job *job) { assert(job && job->txn && !job_is_completed(job)); - job->ret = ret; - if (error) { - assert(job->ret < 0); - job->error = g_strdup(error_get_pretty(error)); - error_free(error); - } - job_update_rc(job); - trace_job_completed(job, ret, job->ret); + trace_job_completed(job, job->ret); if (job->ret) { job_completed_txn_abort(job); } else { @@ -885,6 +856,54 @@ void job_completed(Job *job, int ret, Error *error) } } +/** Useful only as a type shim for aio_bh_schedule_oneshot. */ +static void job_exit(void *opaque) +{ + Job *job = (Job *)opaque; + AioContext *ctx = job->aio_context; + + aio_context_acquire(ctx); + + /* This is a lie, we're not quiescent, but still doing the completion + * callbacks. However, completion callbacks tend to involve operations that + * drain block nodes, and if .drained_poll still returned true, we would + * deadlock. */ + job->busy = false; + job_event_idle(job); + + job_completed(job); + + aio_context_release(ctx); +} + +/** + * All jobs must allow a pause point before entering their job proper. This + * ensures that jobs can be paused prior to being started, then resumed later. + */ +static void coroutine_fn job_co_entry(void *opaque) +{ + Job *job = opaque; + + assert(job && job->driver && job->driver->run); + job_pause_point(job); + job->ret = job->driver->run(job, &job->err); + job->deferred_to_main_loop = true; + job->busy = true; + aio_bh_schedule_oneshot(qemu_get_aio_context(), job_exit, job); +} + +void job_start(Job *job) +{ + assert(job && !job_started(job) && job->paused && + job->driver && job->driver->run); + job->co = qemu_coroutine_create(job_co_entry, job); + job->pause_count--; + job->busy = true; + job->paused = false; + job_state_transition(job, JOB_STATUS_RUNNING); + aio_co_enter(job->aio_context, job->co); +} + void job_cancel(Job *job, bool force) { if (job->status == JOB_STATUS_CONCLUDED) { @@ -893,7 +912,7 @@ void job_cancel(Job *job, bool force) } job_cancel_async(job, force); if (!job_started(job)) { - job_completed(job, -ECANCELED, NULL); + job_completed(job); } else if (job->deferred_to_main_loop) { job_completed_txn_abort(job); } else { @@ -956,38 +975,6 @@ void job_complete(Job *job, Error **errp) job->driver->complete(job, errp); } - -typedef struct { - Job *job; - JobDeferToMainLoopFn *fn; - void *opaque; -} JobDeferToMainLoopData; - -static void job_defer_to_main_loop_bh(void *opaque) -{ - JobDeferToMainLoopData *data = opaque; - Job *job = data->job; - AioContext *aio_context = job->aio_context; - - aio_context_acquire(aio_context); - data->fn(data->job, data->opaque); - aio_context_release(aio_context); - - g_free(data); -} - -void job_defer_to_main_loop(Job *job, JobDeferToMainLoopFn *fn, void *opaque) -{ - JobDeferToMainLoopData *data = g_malloc(sizeof(*data)); - data->job = job; - data->fn = fn; - data->opaque = opaque; - job->deferred_to_main_loop = true; - - aio_bh_schedule_oneshot(qemu_get_aio_context(), - job_defer_to_main_loop_bh, data); -} - int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) { Error *local_err = NULL; @@ -1003,14 +990,10 @@ int job_finish_sync(Job *job, void (*finish)(Job *, Error **errp), Error **errp) job_unref(job); return -EBUSY; } - /* job_drain calls job_enter, and it should be enough to induce progress - * until the job completes or moves to the main thread. */ - while (!job->deferred_to_main_loop && !job_is_completed(job)) { - job_drain(job); - } - while (!job_is_completed(job)) { - aio_poll(qemu_get_aio_context(), true); - } + + AIO_WAIT_WHILE(job->aio_context, + (job_drain(job), !job_is_completed(job))); + ret = (job_is_cancelled(job) && job->ret == 0) ? -ECANCELED : job->ret; job_unref(job); return ret; diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h index 72aa226e6c..e1f8b74558 100644 --- a/linux-headers/asm-arm/kvm.h +++ b/linux-headers/asm-arm/kvm.h @@ -27,6 +27,7 @@ #define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_READONLY_MEM +#define __KVM_HAVE_VCPU_EVENTS #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -125,6 +126,18 @@ struct kvm_sync_regs { struct kvm_arch_memory_slot { }; +/* for KVM_GET/SET_VCPU_EVENTS */ +struct kvm_vcpu_events { + struct { + __u8 serror_pending; + __u8 serror_has_esr; + /* Align it to 8 bytes */ + __u8 pad[6]; + __u64 serror_esr; + } exception; + __u32 reserved[12]; +}; + /* If you need to interpret the index values, here is the key: */ #define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 #define KVM_REG_ARM_COPROC_SHIFT 16 diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h index 60c2d931d0..8c84bcf10f 100644 --- a/linux-headers/asm-arm/unistd-common.h +++ b/linux-headers/asm-arm/unistd-common.h @@ -355,5 +355,6 @@ #define __NR_pkey_free (__NR_SYSCALL_BASE + 396) #define __NR_statx (__NR_SYSCALL_BASE + 397) #define __NR_rseq (__NR_SYSCALL_BASE + 398) +#define __NR_io_pgetevents (__NR_SYSCALL_BASE + 399) #endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 99cb9ad14a..e6a98c14c8 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -39,6 +39,7 @@ #define __KVM_HAVE_GUEST_DEBUG #define __KVM_HAVE_IRQ_LINE #define __KVM_HAVE_READONLY_MEM +#define __KVM_HAVE_VCPU_EVENTS #define KVM_COALESCED_MMIO_PAGE_OFFSET 1 @@ -154,6 +155,18 @@ struct kvm_sync_regs { struct kvm_arch_memory_slot { }; +/* for KVM_GET/SET_VCPU_EVENTS */ +struct kvm_vcpu_events { + struct { + __u8 serror_pending; + __u8 serror_has_esr; + /* Align it to 8 bytes */ + __u8 pad[6]; + __u64 serror_esr; + } exception; + __u32 reserved[12]; +}; + /* If you need to interpret the index values, here is the key: */ #define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 #define KVM_REG_ARM_COPROC_SHIFT 16 diff --git a/linux-headers/asm-arm64/unistd.h b/linux-headers/asm-arm64/unistd.h index 5072cbd15c..dae1584cf0 100644 --- a/linux-headers/asm-arm64/unistd.h +++ b/linux-headers/asm-arm64/unistd.h @@ -16,5 +16,6 @@ */ #define __ARCH_WANT_RENAMEAT +#define __ARCH_WANT_NEW_STAT #include diff --git a/linux-headers/asm-generic/unistd.h b/linux-headers/asm-generic/unistd.h index 42990676a5..d90127298f 100644 --- a/linux-headers/asm-generic/unistd.h +++ b/linux-headers/asm-generic/unistd.h @@ -242,10 +242,12 @@ __SYSCALL(__NR_tee, sys_tee) /* fs/stat.c */ #define __NR_readlinkat 78 __SYSCALL(__NR_readlinkat, sys_readlinkat) +#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64) #define __NR3264_fstatat 79 __SC_3264(__NR3264_fstatat, sys_fstatat64, sys_newfstatat) #define __NR3264_fstat 80 __SC_3264(__NR3264_fstat, sys_fstat64, sys_newfstat) +#endif /* fs/sync.c */ #define __NR_sync 81 @@ -734,9 +736,13 @@ __SYSCALL(__NR_pkey_free, sys_pkey_free) __SYSCALL(__NR_statx, sys_statx) #define __NR_io_pgetevents 292 __SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents) +#define __NR_rseq 293 +__SYSCALL(__NR_rseq, sys_rseq) +#define __NR_kexec_file_load 294 +__SYSCALL(__NR_kexec_file_load, sys_kexec_file_load) #undef __NR_syscalls -#define __NR_syscalls 293 +#define __NR_syscalls 295 /* * 32 bit systems traditionally used different @@ -756,8 +762,10 @@ __SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents) #define __NR_ftruncate __NR3264_ftruncate #define __NR_lseek __NR3264_lseek #define __NR_sendfile __NR3264_sendfile +#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64) #define __NR_newfstatat __NR3264_fstatat #define __NR_fstat __NR3264_fstat +#endif #define __NR_mmap __NR3264_mmap #define __NR_fadvise64 __NR3264_fadvise64 #ifdef __NR3264_stat @@ -772,8 +780,10 @@ __SC_COMP(__NR_io_pgetevents, sys_io_pgetevents, compat_sys_io_pgetevents) #define __NR_ftruncate64 __NR3264_ftruncate #define __NR_llseek __NR3264_lseek #define __NR_sendfile64 __NR3264_sendfile +#if defined(__ARCH_WANT_NEW_STAT) || defined(__ARCH_WANT_STAT64) #define __NR_fstatat64 __NR3264_fstatat #define __NR_fstat64 __NR3264_fstat +#endif #define __NR_mmap2 __NR3264_mmap #define __NR_fadvise64_64 __NR3264_fadvise64 #ifdef __NR3264_stat diff --git a/linux-headers/asm-mips/sgidefs.h b/linux-headers/asm-mips/sgidefs.h index 26143e3b7c..69c3de90c5 100644 --- a/linux-headers/asm-mips/sgidefs.h +++ b/linux-headers/asm-mips/sgidefs.h @@ -11,14 +11,6 @@ #ifndef __ASM_SGIDEFS_H #define __ASM_SGIDEFS_H -/* - * Using a Linux compiler for building Linux seems logic but not to - * everybody. - */ -#ifndef __linux__ -#error Use a Linux compiler or give up. -#endif - /* * Definitions for the ISA levels * diff --git a/linux-headers/asm-mips/unistd.h b/linux-headers/asm-mips/unistd.h index 9bfef7f764..62b86b865c 100644 --- a/linux-headers/asm-mips/unistd.h +++ b/linux-headers/asm-mips/unistd.h @@ -17,1079 +17,23 @@ #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) -#define __NR_userfaultfd (__NR_Linux + 357) -#define __NR_membarrier (__NR_Linux + 358) -#define __NR_mlock2 (__NR_Linux + 359) -#define __NR_copy_file_range (__NR_Linux + 360) -#define __NR_preadv2 (__NR_Linux + 361) -#define __NR_pwritev2 (__NR_Linux + 362) -#define __NR_pkey_mprotect (__NR_Linux + 363) -#define __NR_pkey_alloc (__NR_Linux + 364) -#define __NR_pkey_free (__NR_Linux + 365) -#define __NR_statx (__NR_Linux + 366) - - -/* - * Offset of the last Linux o32 flavoured syscall - */ -#define __NR_Linux_syscalls 366 +#define __NR_Linux 4000 +#include #endif /* _MIPS_SIM == _MIPS_SIM_ABI32 */ -#define __NR_O32_Linux 4000 -#define __NR_O32_Linux_syscalls 366 - #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) -#define __NR_userfaultfd (__NR_Linux + 317) -#define __NR_membarrier (__NR_Linux + 318) -#define __NR_mlock2 (__NR_Linux + 319) -#define __NR_copy_file_range (__NR_Linux + 320) -#define __NR_preadv2 (__NR_Linux + 321) -#define __NR_pwritev2 (__NR_Linux + 322) -#define __NR_pkey_mprotect (__NR_Linux + 323) -#define __NR_pkey_alloc (__NR_Linux + 324) -#define __NR_pkey_free (__NR_Linux + 325) -#define __NR_statx (__NR_Linux + 326) - -/* - * Offset of the last Linux 64-bit flavoured syscall - */ -#define __NR_Linux_syscalls 326 +#define __NR_Linux 5000 +#include #endif /* _MIPS_SIM == _MIPS_SIM_ABI64 */ -#define __NR_64_Linux 5000 -#define __NR_64_Linux_syscalls 326 - #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) -#define __NR_userfaultfd (__NR_Linux + 321) -#define __NR_membarrier (__NR_Linux + 322) -#define __NR_mlock2 (__NR_Linux + 323) -#define __NR_copy_file_range (__NR_Linux + 324) -#define __NR_preadv2 (__NR_Linux + 325) -#define __NR_pwritev2 (__NR_Linux + 326) -#define __NR_pkey_mprotect (__NR_Linux + 327) -#define __NR_pkey_alloc (__NR_Linux + 328) -#define __NR_pkey_free (__NR_Linux + 329) -#define __NR_statx (__NR_Linux + 330) - -/* - * Offset of the last N32 flavoured syscall - */ -#define __NR_Linux_syscalls 330 +#define __NR_Linux 6000 +#include #endif /* _MIPS_SIM == _MIPS_SIM_NABI32 */ -#define __NR_N32_Linux 6000 -#define __NR_N32_Linux_syscalls 330 - #endif /* _ASM_UNISTD_H */ diff --git a/linux-headers/asm-mips/unistd_n32.h b/linux-headers/asm-mips/unistd_n32.h new file mode 100644 index 0000000000..b744f4d520 --- /dev/null +++ b/linux-headers/asm-mips/unistd_n32.h @@ -0,0 +1,338 @@ +#ifndef _ASM_MIPS_UNISTD_N32_H +#define _ASM_MIPS_UNISTD_N32_H + +#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_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) +#define __NR_userfaultfd (__NR_Linux + 321) +#define __NR_membarrier (__NR_Linux + 322) +#define __NR_mlock2 (__NR_Linux + 323) +#define __NR_copy_file_range (__NR_Linux + 324) +#define __NR_preadv2 (__NR_Linux + 325) +#define __NR_pwritev2 (__NR_Linux + 326) +#define __NR_pkey_mprotect (__NR_Linux + 327) +#define __NR_pkey_alloc (__NR_Linux + 328) +#define __NR_pkey_free (__NR_Linux + 329) +#define __NR_statx (__NR_Linux + 330) +#define __NR_rseq (__NR_Linux + 331) +#define __NR_io_pgetevents (__NR_Linux + 332) + + +#endif /* _ASM_MIPS_UNISTD_N32_H */ diff --git a/linux-headers/asm-mips/unistd_n64.h b/linux-headers/asm-mips/unistd_n64.h new file mode 100644 index 0000000000..8083de1f25 --- /dev/null +++ b/linux-headers/asm-mips/unistd_n64.h @@ -0,0 +1,334 @@ +#ifndef _ASM_MIPS_UNISTD_N64_H +#define _ASM_MIPS_UNISTD_N64_H + +#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_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) +#define __NR_userfaultfd (__NR_Linux + 317) +#define __NR_membarrier (__NR_Linux + 318) +#define __NR_mlock2 (__NR_Linux + 319) +#define __NR_copy_file_range (__NR_Linux + 320) +#define __NR_preadv2 (__NR_Linux + 321) +#define __NR_pwritev2 (__NR_Linux + 322) +#define __NR_pkey_mprotect (__NR_Linux + 323) +#define __NR_pkey_alloc (__NR_Linux + 324) +#define __NR_pkey_free (__NR_Linux + 325) +#define __NR_statx (__NR_Linux + 326) +#define __NR_rseq (__NR_Linux + 327) +#define __NR_io_pgetevents (__NR_Linux + 328) + + +#endif /* _ASM_MIPS_UNISTD_N64_H */ diff --git a/linux-headers/asm-mips/unistd_o32.h b/linux-headers/asm-mips/unistd_o32.h new file mode 100644 index 0000000000..b03835b286 --- /dev/null +++ b/linux-headers/asm-mips/unistd_o32.h @@ -0,0 +1,374 @@ +#ifndef _ASM_MIPS_UNISTD_O32_H +#define _ASM_MIPS_UNISTD_O32_H + +#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) +#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_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) +#define __NR_userfaultfd (__NR_Linux + 357) +#define __NR_membarrier (__NR_Linux + 358) +#define __NR_mlock2 (__NR_Linux + 359) +#define __NR_copy_file_range (__NR_Linux + 360) +#define __NR_preadv2 (__NR_Linux + 361) +#define __NR_pwritev2 (__NR_Linux + 362) +#define __NR_pkey_mprotect (__NR_Linux + 363) +#define __NR_pkey_alloc (__NR_Linux + 364) +#define __NR_pkey_free (__NR_Linux + 365) +#define __NR_statx (__NR_Linux + 366) +#define __NR_rseq (__NR_Linux + 367) +#define __NR_io_pgetevents (__NR_Linux + 368) + + +#endif /* _ASM_MIPS_UNISTD_O32_H */ diff --git a/linux-headers/asm-powerpc/kvm.h b/linux-headers/asm-powerpc/kvm.h index 833ed9a16a..8c876c166e 100644 --- a/linux-headers/asm-powerpc/kvm.h +++ b/linux-headers/asm-powerpc/kvm.h @@ -633,6 +633,8 @@ struct kvm_ppc_cpu_char { #define KVM_REG_PPC_PSSCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbd) #define KVM_REG_PPC_DEC_EXPIRY (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xbe) +#define KVM_REG_PPC_ONLINE (KVM_REG_PPC | KVM_REG_SIZE_U32 | 0xbf) +#define KVM_REG_PPC_PTCR (KVM_REG_PPC | KVM_REG_SIZE_U64 | 0xc0) /* Transactional Memory checkpointed state: * This is all GPRs, all VSX regs and a subset of SPRs diff --git a/linux-headers/asm-powerpc/unistd.h b/linux-headers/asm-powerpc/unistd.h index 3629858142..2b29bd8096 100644 --- a/linux-headers/asm-powerpc/unistd.h +++ b/linux-headers/asm-powerpc/unistd.h @@ -10,394 +10,10 @@ #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 +#include #else -#define __NR_fstatat64 291 +#include #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 -#define __NR_preadv2 380 -#define __NR_pwritev2 381 -#define __NR_kexec_file_load 382 -#define __NR_statx 383 -#define __NR_pkey_alloc 384 -#define __NR_pkey_free 385 -#define __NR_pkey_mprotect 386 -#define __NR_rseq 387 #endif /* _ASM_POWERPC_UNISTD_H_ */ diff --git a/linux-headers/asm-powerpc/unistd_32.h b/linux-headers/asm-powerpc/unistd_32.h new file mode 100644 index 0000000000..b8403d700d --- /dev/null +++ b/linux-headers/asm-powerpc/unistd_32.h @@ -0,0 +1,381 @@ +#ifndef _ASM_POWERPC_UNISTD_32_H +#define _ASM_POWERPC_UNISTD_32_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 +#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 +#define __NR_putpmsg 188 +#define __NR_vfork 189 +#define __NR_ugetrlimit 190 +#define __NR_readahead 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_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 +#define __NR_fcntl64 204 +#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 +#define __NR_tuxcall 225 +#define __NR_sendfile64 226 +#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 +#define __NR_fadvise64_64 254 +#define __NR_rtas 255 +#define __NR_sys_debug_setcontext 256 +#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 +#define __NR_fstatat64 291 +#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 +#define __NR_preadv2 380 +#define __NR_pwritev2 381 +#define __NR_kexec_file_load 382 +#define __NR_statx 383 +#define __NR_pkey_alloc 384 +#define __NR_pkey_free 385 +#define __NR_pkey_mprotect 386 +#define __NR_rseq 387 +#define __NR_io_pgetevents 388 + + +#endif /* _ASM_POWERPC_UNISTD_32_H */ diff --git a/linux-headers/asm-powerpc/unistd_64.h b/linux-headers/asm-powerpc/unistd_64.h new file mode 100644 index 0000000000..f6a25fbbdd --- /dev/null +++ b/linux-headers/asm-powerpc/unistd_64.h @@ -0,0 +1,372 @@ +#ifndef _ASM_POWERPC_UNISTD_64_H +#define _ASM_POWERPC_UNISTD_64_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 +#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 +#define __NR_putpmsg 188 +#define __NR_vfork 189 +#define __NR_ugetrlimit 190 +#define __NR_readahead 191 +#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 +#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 +#define __NR_tuxcall 225 +#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 +#define __NR_rtas 255 +#define __NR_sys_debug_setcontext 256 +#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 +#define __NR_newfstatat 291 +#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 +#define __NR_preadv2 380 +#define __NR_pwritev2 381 +#define __NR_kexec_file_load 382 +#define __NR_statx 383 +#define __NR_pkey_alloc 384 +#define __NR_pkey_free 385 +#define __NR_pkey_mprotect 386 +#define __NR_rseq 387 +#define __NR_io_pgetevents 388 + + +#endif /* _ASM_POWERPC_UNISTD_64_H */ diff --git a/linux-headers/asm-s390/kvm.h b/linux-headers/asm-s390/kvm.h index 11def14301..0265482f8f 100644 --- a/linux-headers/asm-s390/kvm.h +++ b/linux-headers/asm-s390/kvm.h @@ -4,7 +4,7 @@ /* * KVM s390 specific structures and definitions * - * Copyright IBM Corp. 2008 + * Copyright IBM Corp. 2008, 2018 * * Author(s): Carsten Otte * Christian Borntraeger @@ -160,6 +160,8 @@ struct kvm_s390_vm_cpu_subfunc { #define KVM_S390_VM_CRYPTO_ENABLE_DEA_KW 1 #define KVM_S390_VM_CRYPTO_DISABLE_AES_KW 2 #define KVM_S390_VM_CRYPTO_DISABLE_DEA_KW 3 +#define KVM_S390_VM_CRYPTO_ENABLE_APIE 4 +#define KVM_S390_VM_CRYPTO_DISABLE_APIE 5 /* kvm attributes for migration mode */ #define KVM_S390_VM_MIGRATION_STOP 0 @@ -225,6 +227,7 @@ struct kvm_guest_debug_arch { #define KVM_SYNC_FPRS (1UL << 8) #define KVM_SYNC_GSCB (1UL << 9) #define KVM_SYNC_BPBC (1UL << 10) +#define KVM_SYNC_ETOKEN (1UL << 11) /* length and alignment of the sdnx as a power of two */ #define SDNXC 8 #define SDNXL (1UL << SDNXC) @@ -258,6 +261,8 @@ struct kvm_sync_regs { struct { __u64 reserved1[2]; __u64 gscb[4]; + __u64 etoken; + __u64 etoken_extension; }; }; }; diff --git a/linux-headers/asm-s390/unistd_32.h b/linux-headers/asm-s390/unistd_32.h index d0f97cd0a4..514e302ba1 100644 --- a/linux-headers/asm-s390/unistd_32.h +++ b/linux-headers/asm-s390/unistd_32.h @@ -361,5 +361,7 @@ #define __NR_statx 379 #define __NR_s390_sthyi 380 #define __NR_kexec_file_load 381 +#define __NR_io_pgetevents 382 +#define __NR_rseq 383 #endif /* _ASM_S390_UNISTD_32_H */ diff --git a/linux-headers/asm-s390/unistd_64.h b/linux-headers/asm-s390/unistd_64.h index 23ffb97746..d2b73de0ed 100644 --- a/linux-headers/asm-s390/unistd_64.h +++ b/linux-headers/asm-s390/unistd_64.h @@ -328,5 +328,7 @@ #define __NR_statx 379 #define __NR_s390_sthyi 380 #define __NR_kexec_file_load 381 +#define __NR_io_pgetevents 382 +#define __NR_rseq 383 #endif /* _ASM_S390_UNISTD_64_H */ diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index c535c2fdea..dabfcf7c39 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -288,6 +288,7 @@ struct kvm_reinject_control { #define KVM_VCPUEVENT_VALID_SIPI_VECTOR 0x00000002 #define KVM_VCPUEVENT_VALID_SHADOW 0x00000004 #define KVM_VCPUEVENT_VALID_SMM 0x00000008 +#define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 /* Interrupt shadow states */ #define KVM_X86_SHADOW_INT_MOV_SS 0x01 @@ -299,7 +300,7 @@ struct kvm_vcpu_events { __u8 injected; __u8 nr; __u8 has_error_code; - __u8 pad; + __u8 pending; __u32 error_code; } exception; struct { @@ -322,7 +323,9 @@ struct kvm_vcpu_events { __u8 smm_inside_nmi; __u8 latched_init; } smi; - __u32 reserved[9]; + __u8 reserved[27]; + __u8 exception_has_payload; + __u64 exception_payload; }; /* for KVM_GET/SET_DEBUGREGS */ @@ -377,5 +380,44 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_LINT0_REENABLED (1 << 0) #define KVM_X86_QUIRK_CD_NW_CLEARED (1 << 1) +#define KVM_X86_QUIRK_LAPIC_MMIO_HOLE (1 << 2) + +#define KVM_STATE_NESTED_GUEST_MODE 0x00000001 +#define KVM_STATE_NESTED_RUN_PENDING 0x00000002 +#define KVM_STATE_NESTED_EVMCS 0x00000004 + +#define KVM_STATE_NESTED_SMM_GUEST_MODE 0x00000001 +#define KVM_STATE_NESTED_SMM_VMXON 0x00000002 + +struct kvm_vmx_nested_state { + __u64 vmxon_pa; + __u64 vmcs_pa; + + struct { + __u16 flags; + } smm; +}; + +/* for KVM_CAP_NESTED_STATE */ +struct kvm_nested_state { + /* KVM_STATE_* flags */ + __u16 flags; + + /* 0 for VMX, 1 for SVM. */ + __u16 format; + + /* 128 for SVM, 128 + VMCS size for VMX. */ + __u32 size; + + union { + /* VMXON, VMCS */ + struct kvm_vmx_nested_state vmx; + + /* Pad the header to 128 bytes. */ + __u8 pad[120]; + }; + + __u8 data[0]; +}; #endif /* _ASM_X86_KVM_H */ diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 98f389a5a3..b53ee59748 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -420,13 +420,19 @@ struct kvm_run { struct kvm_coalesced_mmio_zone { __u64 addr; __u32 size; - __u32 pad; + union { + __u32 pad; + __u32 pio; + }; }; struct kvm_coalesced_mmio { __u64 phys_addr; __u32 len; - __u32 pad; + union { + __u32 pad; + __u32 pio; + }; __u8 data[8]; }; @@ -486,6 +492,17 @@ struct kvm_dirty_log { }; }; +/* for KVM_CLEAR_DIRTY_LOG */ +struct kvm_clear_dirty_log { + __u32 slot; + __u32 num_pages; + __u64 first_page; + union { + void *dirty_bitmap; /* one bit per page */ + __u64 padding2; + }; +}; + /* for KVM_SET_SIGNAL_MASK */ struct kvm_signal_mask { __u32 len; @@ -719,6 +736,7 @@ struct kvm_ppc_one_seg_page_size { #define KVM_PPC_PAGE_SIZES_REAL 0x00000001 #define KVM_PPC_1T_SEGMENTS 0x00000002 +#define KVM_PPC_NO_HASH 0x00000004 struct kvm_ppc_smmu_info { __u64 flags; @@ -750,6 +768,15 @@ struct kvm_ppc_resize_hpt { #define KVM_S390_SIE_PAGE_OFFSET 1 +/* + * On arm64, machine type can be used to request the physical + * address size for the VM. Bits[7-0] are reserved for the guest + * PA size shift (i.e, log2(PA_Size)). For backward compatibility, + * value 0 implies the default IPA size, 40bits. + */ +#define KVM_VM_TYPE_ARM_IPA_SIZE_MASK 0xffULL +#define KVM_VM_TYPE_ARM_IPA_SIZE(x) \ + ((x) & KVM_VM_TYPE_ARM_IPA_SIZE_MASK) /* * ioctls for /dev/kvm fds: */ @@ -949,6 +976,18 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_GET_MSR_FEATURES 153 #define KVM_CAP_HYPERV_EVENTFD 154 #define KVM_CAP_HYPERV_TLBFLUSH 155 +#define KVM_CAP_S390_HPAGE_1M 156 +#define KVM_CAP_NESTED_STATE 157 +#define KVM_CAP_ARM_INJECT_SERROR_ESR 158 +#define KVM_CAP_MSR_PLATFORM_INFO 159 +#define KVM_CAP_PPC_NESTED_HV 160 +#define KVM_CAP_HYPERV_SEND_IPI 161 +#define KVM_CAP_COALESCED_PIO 162 +#define KVM_CAP_HYPERV_ENLIGHTENED_VMCS 163 +#define KVM_CAP_EXCEPTION_PAYLOAD 164 +#define KVM_CAP_ARM_VM_IPA_SIZE 165 +#define KVM_CAP_MANUAL_DIRTY_LOG_PROTECT 166 +#define KVM_CAP_HYPERV_CPUID 167 #ifdef KVM_CAP_IRQ_ROUTING @@ -1391,6 +1430,15 @@ struct kvm_enc_region { /* Available with KVM_CAP_HYPERV_EVENTFD */ #define KVM_HYPERV_EVENTFD _IOW(KVMIO, 0xbd, struct kvm_hyperv_eventfd) +/* Available with KVM_CAP_NESTED_STATE */ +#define KVM_GET_NESTED_STATE _IOWR(KVMIO, 0xbe, struct kvm_nested_state) +#define KVM_SET_NESTED_STATE _IOW(KVMIO, 0xbf, struct kvm_nested_state) + +/* Available with KVM_CAP_MANUAL_DIRTY_LOG_PROTECT */ +#define KVM_CLEAR_DIRTY_LOG _IOWR(KVMIO, 0xc0, struct kvm_clear_dirty_log) + +/* Available with KVM_CAP_HYPERV_CPUID */ +#define KVM_GET_SUPPORTED_HV_CPUID _IOWR(KVMIO, 0xc1, struct kvm_cpuid2) /* Secure Encrypted Virtualization command */ enum sev_cmd_id { diff --git a/linux-headers/linux/vfio.h b/linux-headers/linux/vfio.h index 3615a269d3..12a7b1dc53 100644 --- a/linux-headers/linux/vfio.h +++ b/linux-headers/linux/vfio.h @@ -200,6 +200,7 @@ struct vfio_device_info { #define VFIO_DEVICE_FLAGS_PLATFORM (1 << 2) /* vfio-platform device */ #define VFIO_DEVICE_FLAGS_AMBA (1 << 3) /* vfio-amba device */ #define VFIO_DEVICE_FLAGS_CCW (1 << 4) /* vfio-ccw device */ +#define VFIO_DEVICE_FLAGS_AP (1 << 5) /* vfio-ap device */ __u32 num_regions; /* Max region index + 1 */ __u32 num_irqs; /* Max IRQ index + 1 */ }; @@ -215,6 +216,7 @@ struct vfio_device_info { #define VFIO_DEVICE_API_PLATFORM_STRING "vfio-platform" #define VFIO_DEVICE_API_AMBA_STRING "vfio-amba" #define VFIO_DEVICE_API_CCW_STRING "vfio-ccw" +#define VFIO_DEVICE_API_AP_STRING "vfio-ap" /** * VFIO_DEVICE_GET_REGION_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 8, @@ -301,6 +303,71 @@ struct vfio_region_info_cap_type { #define VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG (2) #define VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG (3) +#define VFIO_REGION_TYPE_GFX (1) +#define VFIO_REGION_SUBTYPE_GFX_EDID (1) + +/** + * struct vfio_region_gfx_edid - EDID region layout. + * + * Set display link state and EDID blob. + * + * The EDID blob has monitor information such as brand, name, serial + * number, physical size, supported video modes and more. + * + * This special region allows userspace (typically qemu) set a virtual + * EDID for the virtual monitor, which allows a flexible display + * configuration. + * + * For the edid blob spec look here: + * https://en.wikipedia.org/wiki/Extended_Display_Identification_Data + * + * On linux systems you can find the EDID blob in sysfs: + * /sys/class/drm/${card}/${connector}/edid + * + * You can use the edid-decode ulility (comes with xorg-x11-utils) to + * decode the EDID blob. + * + * @edid_offset: location of the edid blob, relative to the + * start of the region (readonly). + * @edid_max_size: max size of the edid blob (readonly). + * @edid_size: actual edid size (read/write). + * @link_state: display link state (read/write). + * VFIO_DEVICE_GFX_LINK_STATE_UP: Monitor is turned on. + * VFIO_DEVICE_GFX_LINK_STATE_DOWN: Monitor is turned off. + * @max_xres: max display width (0 == no limitation, readonly). + * @max_yres: max display height (0 == no limitation, readonly). + * + * EDID update protocol: + * (1) set link-state to down. + * (2) update edid blob and size. + * (3) set link-state to up. + */ +struct vfio_region_gfx_edid { + __u32 edid_offset; + __u32 edid_max_size; + __u32 edid_size; + __u32 max_xres; + __u32 max_yres; + __u32 link_state; +#define VFIO_DEVICE_GFX_LINK_STATE_UP 1 +#define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2 +}; + +/* + * 10de vendor sub-type + * + * NVIDIA GPU NVlink2 RAM is coherent RAM mapped onto the host address space. + */ +#define VFIO_REGION_SUBTYPE_NVIDIA_NVLINK2_RAM (1) + +/* + * 1014 vendor sub-type + * + * IBM NPU NVlink2 ATSD (Address Translation Shootdown) register of NPU + * to do TLB invalidation on a GPU. + */ +#define VFIO_REGION_SUBTYPE_IBM_NVLINK2_ATSD (1) + /* * The MSIX mappable capability informs that MSIX data of a BAR can be mmapped * which allows direct access to non-MSIX registers which happened to be within @@ -311,6 +378,33 @@ struct vfio_region_info_cap_type { */ #define VFIO_REGION_INFO_CAP_MSIX_MAPPABLE 3 +/* + * Capability with compressed real address (aka SSA - small system address) + * where GPU RAM is mapped on a system bus. Used by a GPU for DMA routing + * and by the userspace to associate a NVLink bridge with a GPU. + */ +#define VFIO_REGION_INFO_CAP_NVLINK2_SSATGT 4 + +struct vfio_region_info_cap_nvlink2_ssatgt { + struct vfio_info_cap_header header; + __u64 tgt; +}; + +/* + * Capability with an NVLink link speed. The value is read by + * the NVlink2 bridge driver from the bridge's "ibm,nvlink-speed" + * property in the device tree. The value is fixed in the hardware + * and failing to provide the correct value results in the link + * not working with no indication from the driver why. + */ +#define VFIO_REGION_INFO_CAP_NVLINK2_LNKSPD 5 + +struct vfio_region_info_cap_nvlink2_lnkspd { + struct vfio_info_cap_header header; + __u32 link_speed; + __u32 __pad; +}; + /** * VFIO_DEVICE_GET_IRQ_INFO - _IOWR(VFIO_TYPE, VFIO_BASE + 9, * struct vfio_irq_info) diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index e336395d67..40d028eed6 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -11,84 +11,9 @@ * device configuration. */ +#include #include - #include -#include -#include - -struct vhost_vring_state { - unsigned int index; - unsigned int num; -}; - -struct vhost_vring_file { - unsigned int index; - int fd; /* Pass -1 to unbind from file. */ - -}; - -struct vhost_vring_addr { - unsigned int index; - /* Option flags. */ - unsigned int flags; - /* Flag values: */ - /* Whether log address is valid. If set enables logging. */ -#define VHOST_VRING_F_LOG 0 - - /* Start of array of descriptors (virtually contiguous) */ - __u64 desc_user_addr; - /* Used structure address. Must be 32 bit aligned */ - __u64 used_user_addr; - /* Available structure address. Must be 16 bit aligned */ - __u64 avail_user_addr; - /* Logging support. */ - /* Log writes to used structure, at offset calculated from specified - * address. Address must be 32 bit aligned. */ - __u64 log_guest_addr; -}; - -/* no alignment requirement */ -struct vhost_iotlb_msg { - __u64 iova; - __u64 size; - __u64 uaddr; -#define VHOST_ACCESS_RO 0x1 -#define VHOST_ACCESS_WO 0x2 -#define VHOST_ACCESS_RW 0x3 - __u8 perm; -#define VHOST_IOTLB_MISS 1 -#define VHOST_IOTLB_UPDATE 2 -#define VHOST_IOTLB_INVALIDATE 3 -#define VHOST_IOTLB_ACCESS_FAIL 4 - __u8 type; -}; - -#define VHOST_IOTLB_MSG 0x1 - -struct vhost_msg { - int type; - union { - struct vhost_iotlb_msg iotlb; - __u8 padding[64]; - }; -}; - -struct vhost_memory_region { - __u64 guest_phys_addr; - __u64 memory_size; /* bytes */ - __u64 userspace_addr; - __u64 flags_padding; /* No flags are currently specified. */ -}; - -/* All region addresses and sizes must be 4K aligned. */ -#define VHOST_PAGE_SIZE 0x1000 - -struct vhost_memory { - __u32 nregions; - __u32 padding; - struct vhost_memory_region regions[0]; -}; /* ioctls */ @@ -160,6 +85,14 @@ struct vhost_memory { #define VHOST_GET_VRING_BUSYLOOP_TIMEOUT _IOW(VHOST_VIRTIO, 0x24, \ struct vhost_vring_state) +/* Set or get vhost backend capability */ + +/* Use message type V2 */ +#define VHOST_BACKEND_F_IOTLB_MSG_V2 0x1 + +#define VHOST_SET_BACKEND_FEATURES _IOW(VHOST_VIRTIO, 0x25, __u64) +#define VHOST_GET_BACKEND_FEATURES _IOR(VHOST_VIRTIO, 0x26, __u64) + /* VHOST_NET specific defines */ /* Attach virtio net ring to a raw socket, or tap device. @@ -168,31 +101,7 @@ struct vhost_memory { * device. This can be used to stop the ring (e.g. for migration). */ #define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file) -/* Feature bits */ -/* Log all write descriptors. Can be changed while device is active. */ -#define VHOST_F_LOG_ALL 26 -/* vhost-net should add virtio_net_hdr for RX, and strip for TX packets. */ -#define VHOST_NET_F_VIRTIO_NET_HDR 27 - -/* VHOST_SCSI specific definitions */ - -/* - * 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 guset. - */ - -#define VHOST_SCSI_ABI_VERSION 1 - -struct vhost_scsi_target { - int abi_version; - char vhost_wwpn[224]; /* TRANSPORT_IQN_LEN */ - unsigned short vhost_tpgt; - unsigned short reserved; -}; +/* VHOST_SCSI specific defines */ #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) diff --git a/linux-headers/linux/vhost_types.h b/linux-headers/linux/vhost_types.h new file mode 100644 index 0000000000..473e3c0d81 --- /dev/null +++ b/linux-headers/linux/vhost_types.h @@ -0,0 +1 @@ +#include "standard-headers/linux/vhost_types.h" diff --git a/linux-user/Makefile.objs b/linux-user/Makefile.objs index b5dfb71f25..769b8d8336 100644 --- a/linux-user/Makefile.objs +++ b/linux-user/Makefile.objs @@ -1,7 +1,7 @@ obj-y = main.o syscall.o strace.o mmap.o signal.o \ elfload.o linuxload.o uaccess.o uname.o \ safe-syscall.o $(TARGET_ABI_DIR)/signal.o \ - $(TARGET_ABI_DIR)/cpu_loop.o exit.o + $(TARGET_ABI_DIR)/cpu_loop.o exit.o fd-trans.o obj-$(TARGET_HAS_BFLT) += flatload.o obj-$(TARGET_I386) += vm86.o diff --git a/linux-user/aarch64/cpu_loop.c b/linux-user/aarch64/cpu_loop.c index c97a646546..d75fd9d3e2 100644 --- a/linux-user/aarch64/cpu_loop.c +++ b/linux-user/aarch64/cpu_loop.c @@ -73,7 +73,7 @@ void cpu_loop(CPUARMState *env) { CPUState *cs = CPU(arm_env_get_cpu(env)); - int trapnr, sig; + int trapnr; abi_long ret; target_siginfo_t info; @@ -121,13 +121,10 @@ void cpu_loop(CPUARMState *env) break; case EXCP_DEBUG: case EXCP_BKPT: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_SEMIHOST: env->xregs[0] = do_arm_semihosting(env); @@ -150,10 +147,29 @@ void cpu_loop(CPUARMState *env) } } +static uint64_t arm_rand64(void) +{ + int shift = 64 - clz64(RAND_MAX); + int i, n = 64 / shift + (64 % shift != 0); + uint64_t ret = 0; + + for (i = 0; i < n; i++) { + ret = (ret << shift) | rand(); + } + return ret; +} + +void arm_init_pauth_key(ARMPACKey *key) +{ + key->lo = arm_rand64(); + key->hi = arm_rand64(); +} + void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { - CPUState *cpu = ENV_GET_CPU(env); - TaskState *ts = cpu->opaque; + ARMCPU *cpu = arm_env_get_cpu(env); + CPUState *cs = CPU(cpu); + TaskState *ts = cs->opaque; struct image_info *info = ts->info; int i; @@ -175,6 +191,14 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) } #endif + if (cpu_isar_feature(aa64_pauth, cpu)) { + arm_init_pauth_key(&env->apia_key); + arm_init_pauth_key(&env->apib_key); + arm_init_pauth_key(&env->apda_key); + arm_init_pauth_key(&env->apdb_key); + arm_init_pauth_key(&env->apga_key); + } + ts->stack_base = info->start_stack; ts->heap_base = info->brk; /* This will be filled in on the first SYS_HEAPINFO call. */ diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 07fedfc33c..f84a9cf28a 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -314,7 +314,7 @@ static int target_restore_sigframe(CPUARMState *env, break; case TARGET_SVE_MAGIC: - if (arm_feature(env, ARM_FEATURE_SVE)) { + if (cpu_isar_feature(aa64_sve, arm_env_get_cpu(env))) { vq = (env->vfp.zcr_el[1] & 0xf) + 1; sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); if (!sve && size == sve_size) { @@ -433,7 +433,7 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, &layout); /* SVE state needs saving only if it exists. */ - if (arm_feature(env, ARM_FEATURE_SVE)) { + if (cpu_isar_feature(aa64_sve, arm_env_get_cpu(env))) { vq = (env->vfp.zcr_el[1] & 0xf) + 1; sve_size = QEMU_ALIGN_UP(TARGET_SVE_SIG_CONTEXT_SIZE(vq), 16); sve_ofs = alloc_sigframe_space(sve_size, &layout); diff --git a/linux-user/aarch64/target_syscall.h b/linux-user/aarch64/target_syscall.h index 205265e619..b595e5da82 100644 --- a/linux-user/aarch64/target_syscall.h +++ b/linux-user/aarch64/target_syscall.h @@ -22,4 +22,13 @@ struct target_pt_regs { #define TARGET_PR_SVE_SET_VL 50 #define TARGET_PR_SVE_GET_VL 51 +#define TARGET_PR_PAC_RESET_KEYS 54 +# define TARGET_PR_PAC_APIAKEY (1 << 0) +# define TARGET_PR_PAC_APIBKEY (1 << 1) +# define TARGET_PR_PAC_APDAKEY (1 << 2) +# define TARGET_PR_PAC_APDBKEY (1 << 3) +# define TARGET_PR_PAC_APGAKEY (1 << 4) + +void arm_init_pauth_key(ARMPACKey *key); + #endif /* AARCH64_TARGET_SYSCALL_H */ diff --git a/linux-user/alpha/cpu_loop.c b/linux-user/alpha/cpu_loop.c index c1a98c8cbf..824b6d6658 100644 --- a/linux-user/alpha/cpu_loop.c +++ b/linux-user/alpha/cpu_loop.c @@ -179,14 +179,10 @@ void cpu_loop(CPUAlphaState *env) } break; case EXCP_DEBUG: - info.si_signo = gdb_handlesig(cs, TARGET_SIGTRAP); - if (info.si_signo) { - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_INTERRUPT: /* Just indicate that signals should be handled asap. */ diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 26928fbbb2..ee68aa60bf 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -397,18 +397,10 @@ void cpu_loop(CPUARMState *env) break; case EXCP_DEBUG: excp_debug: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_KERNEL_TRAP: if (do_kernel_trap(env)) diff --git a/linux-user/cris/cpu_loop.c b/linux-user/cris/cpu_loop.c index 37bdcfa8cc..dacf604c7d 100644 --- a/linux-user/cris/cpu_loop.c +++ b/linux-user/cris/cpu_loop.c @@ -64,18 +64,10 @@ void cpu_loop(CPUCRISState *env) } break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index df07055361..c1a26021f8 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -458,6 +458,10 @@ static uint32_t get_elf_hwcap(void) /* probe for the extra features */ #define GET_FEATURE(feat, hwcap) \ do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) + +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + /* EDSP is in v5TE and above, but all our v5 CPUs are v5TE */ GET_FEATURE(ARM_FEATURE_V5, ARM_HWCAP_ARM_EDSP); GET_FEATURE(ARM_FEATURE_VFP, ARM_HWCAP_ARM_VFP); @@ -467,8 +471,8 @@ static uint32_t get_elf_hwcap(void) GET_FEATURE(ARM_FEATURE_VFP3, ARM_HWCAP_ARM_VFPv3); GET_FEATURE(ARM_FEATURE_V6K, ARM_HWCAP_ARM_TLS); GET_FEATURE(ARM_FEATURE_VFP4, ARM_HWCAP_ARM_VFPv4); - GET_FEATURE(ARM_FEATURE_ARM_DIV, ARM_HWCAP_ARM_IDIVA); - GET_FEATURE(ARM_FEATURE_THUMB_DIV, ARM_HWCAP_ARM_IDIVT); + GET_FEATURE_ID(arm_div, ARM_HWCAP_ARM_IDIVA); + GET_FEATURE_ID(thumb_div, ARM_HWCAP_ARM_IDIVT); /* All QEMU's VFPv3 CPUs have 32 registers, see VFP_DREG in translate.c. * Note that the ARM_HWCAP_ARM_VFPv3D16 bit is always the inverse of * ARM_HWCAP_ARM_VFPD32 (and so always clear for QEMU); it is unrelated @@ -485,15 +489,47 @@ static uint32_t get_elf_hwcap2(void) ARMCPU *cpu = ARM_CPU(thread_cpu); uint32_t hwcaps = 0; - GET_FEATURE(ARM_FEATURE_V8_AES, ARM_HWCAP2_ARM_AES); - GET_FEATURE(ARM_FEATURE_V8_PMULL, ARM_HWCAP2_ARM_PMULL); - GET_FEATURE(ARM_FEATURE_V8_SHA1, ARM_HWCAP2_ARM_SHA1); - GET_FEATURE(ARM_FEATURE_V8_SHA256, ARM_HWCAP2_ARM_SHA2); - GET_FEATURE(ARM_FEATURE_CRC, ARM_HWCAP2_ARM_CRC32); + GET_FEATURE_ID(aa32_aes, ARM_HWCAP2_ARM_AES); + GET_FEATURE_ID(aa32_pmull, ARM_HWCAP2_ARM_PMULL); + GET_FEATURE_ID(aa32_sha1, ARM_HWCAP2_ARM_SHA1); + GET_FEATURE_ID(aa32_sha2, ARM_HWCAP2_ARM_SHA2); + GET_FEATURE_ID(aa32_crc32, ARM_HWCAP2_ARM_CRC32); return hwcaps; } #undef GET_FEATURE +#undef GET_FEATURE_ID + +#define ELF_PLATFORM get_elf_platform() + +static const char *get_elf_platform(void) +{ + CPUARMState *env = thread_cpu->env_ptr; + +#ifdef TARGET_WORDS_BIGENDIAN +# define END "b" +#else +# define END "l" +#endif + + if (arm_feature(env, ARM_FEATURE_V8)) { + return "v8" END; + } else if (arm_feature(env, ARM_FEATURE_V7)) { + if (arm_feature(env, ARM_FEATURE_M)) { + return "v7m" END; + } else { + return "v7" END; + } + } else if (arm_feature(env, ARM_FEATURE_V6)) { + return "v6" END; + } else if (arm_feature(env, ARM_FEATURE_V5)) { + return "v5" END; + } else { + return "v4" END; + } + +#undef END +} #else /* 64 bit ARM definitions */ @@ -501,7 +537,11 @@ static uint32_t get_elf_hwcap2(void) #define ELF_ARCH EM_AARCH64 #define ELF_CLASS ELFCLASS64 -#define ELF_PLATFORM "aarch64" +#ifdef TARGET_WORDS_BIGENDIAN +# define ELF_PLATFORM "aarch64_be" +#else +# define ELF_PLATFORM "aarch64" +#endif static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) @@ -555,6 +595,15 @@ enum { ARM_HWCAP_A64_ASIMDDP = 1 << 20, ARM_HWCAP_A64_SHA512 = 1 << 21, ARM_HWCAP_A64_SVE = 1 << 22, + ARM_HWCAP_A64_ASIMDFHM = 1 << 23, + ARM_HWCAP_A64_DIT = 1 << 24, + ARM_HWCAP_A64_USCAT = 1 << 25, + ARM_HWCAP_A64_ILRCPC = 1 << 26, + ARM_HWCAP_A64_FLAGM = 1 << 27, + ARM_HWCAP_A64_SSBS = 1 << 28, + ARM_HWCAP_A64_SB = 1 << 29, + ARM_HWCAP_A64_PACA = 1 << 30, + ARM_HWCAP_A64_PACG = 1UL << 31, }; #define ELF_HWCAP get_elf_hwcap() @@ -566,27 +615,34 @@ static uint32_t get_elf_hwcap(void) hwcaps |= ARM_HWCAP_A64_FP; hwcaps |= ARM_HWCAP_A64_ASIMD; + hwcaps |= ARM_HWCAP_A64_CPUID; /* probe for the extra features */ -#define GET_FEATURE(feat, hwcap) \ - do { if (arm_feature(&cpu->env, feat)) { hwcaps |= hwcap; } } while (0) - GET_FEATURE(ARM_FEATURE_V8_AES, ARM_HWCAP_A64_AES); - GET_FEATURE(ARM_FEATURE_V8_PMULL, ARM_HWCAP_A64_PMULL); - GET_FEATURE(ARM_FEATURE_V8_SHA1, ARM_HWCAP_A64_SHA1); - GET_FEATURE(ARM_FEATURE_V8_SHA256, ARM_HWCAP_A64_SHA2); - GET_FEATURE(ARM_FEATURE_CRC, ARM_HWCAP_A64_CRC32); - GET_FEATURE(ARM_FEATURE_V8_SHA3, ARM_HWCAP_A64_SHA3); - GET_FEATURE(ARM_FEATURE_V8_SM3, ARM_HWCAP_A64_SM3); - GET_FEATURE(ARM_FEATURE_V8_SM4, ARM_HWCAP_A64_SM4); - GET_FEATURE(ARM_FEATURE_V8_SHA512, ARM_HWCAP_A64_SHA512); - GET_FEATURE(ARM_FEATURE_V8_FP16, - ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); - GET_FEATURE(ARM_FEATURE_V8_ATOMICS, ARM_HWCAP_A64_ATOMICS); - GET_FEATURE(ARM_FEATURE_V8_RDM, ARM_HWCAP_A64_ASIMDRDM); - GET_FEATURE(ARM_FEATURE_V8_DOTPROD, ARM_HWCAP_A64_ASIMDDP); - GET_FEATURE(ARM_FEATURE_V8_FCMA, ARM_HWCAP_A64_FCMA); - GET_FEATURE(ARM_FEATURE_SVE, ARM_HWCAP_A64_SVE); -#undef GET_FEATURE +#define GET_FEATURE_ID(feat, hwcap) \ + do { if (cpu_isar_feature(feat, cpu)) { hwcaps |= hwcap; } } while (0) + + GET_FEATURE_ID(aa64_aes, ARM_HWCAP_A64_AES); + GET_FEATURE_ID(aa64_pmull, ARM_HWCAP_A64_PMULL); + GET_FEATURE_ID(aa64_sha1, ARM_HWCAP_A64_SHA1); + GET_FEATURE_ID(aa64_sha256, ARM_HWCAP_A64_SHA2); + GET_FEATURE_ID(aa64_sha512, ARM_HWCAP_A64_SHA512); + GET_FEATURE_ID(aa64_crc32, ARM_HWCAP_A64_CRC32); + GET_FEATURE_ID(aa64_sha3, ARM_HWCAP_A64_SHA3); + GET_FEATURE_ID(aa64_sm3, ARM_HWCAP_A64_SM3); + GET_FEATURE_ID(aa64_sm4, ARM_HWCAP_A64_SM4); + GET_FEATURE_ID(aa64_fp16, ARM_HWCAP_A64_FPHP | ARM_HWCAP_A64_ASIMDHP); + GET_FEATURE_ID(aa64_atomics, ARM_HWCAP_A64_ATOMICS); + GET_FEATURE_ID(aa64_rdm, ARM_HWCAP_A64_ASIMDRDM); + GET_FEATURE_ID(aa64_dp, ARM_HWCAP_A64_ASIMDDP); + GET_FEATURE_ID(aa64_fcma, ARM_HWCAP_A64_FCMA); + GET_FEATURE_ID(aa64_sve, ARM_HWCAP_A64_SVE); + GET_FEATURE_ID(aa64_pauth, ARM_HWCAP_A64_PACA | ARM_HWCAP_A64_PACG); + GET_FEATURE_ID(aa64_fhm, ARM_HWCAP_A64_ASIMDFHM); + GET_FEATURE_ID(aa64_jscvt, ARM_HWCAP_A64_JSCVT); + GET_FEATURE_ID(aa64_sb, ARM_HWCAP_A64_SB); + GET_FEATURE_ID(aa64_condm_4, ARM_HWCAP_A64_FLAGM); + +#undef GET_FEATURE_ID return hwcaps; } @@ -710,6 +766,7 @@ enum { QEMU_PPC_FEATURE2_HAS_EBB = 0x10000000, /* Event Base Branching */ QEMU_PPC_FEATURE2_HAS_ISEL = 0x08000000, /* Integer Select */ QEMU_PPC_FEATURE2_HAS_TAR = 0x04000000, /* Target Address Register */ + QEMU_PPC_FEATURE2_ARCH_3_00 = 0x00800000, /* ISA 3.00 */ }; #define ELF_HWCAP get_elf_hwcap() @@ -764,6 +821,7 @@ static uint32_t get_elf_hwcap2(void) GET_FEATURE2(PPC2_BCTAR_ISA207, QEMU_PPC_FEATURE2_HAS_TAR); GET_FEATURE2((PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA207S), QEMU_PPC_FEATURE2_ARCH_2_07); + GET_FEATURE2(PPC2_ISA300, QEMU_PPC_FEATURE2_ARCH_3_00); #undef GET_FEATURE #undef GET_FEATURE2 @@ -853,6 +911,8 @@ static void elf_core_copy_regs(target_elf_gregset_t *regs, const CPUPPCState *en #endif #define ELF_ARCH EM_MIPS +#define elf_check_arch(x) ((x) == EM_MIPS || (x) == EM_NANOMIPS) + static inline void init_thread(struct target_pt_regs *regs, struct image_info *infop) { @@ -1435,7 +1495,10 @@ struct exec #define QMAGIC 0314 /* Necessary parameters */ -#define TARGET_ELF_EXEC_PAGESIZE TARGET_PAGE_SIZE +#define TARGET_ELF_EXEC_PAGESIZE \ + (((eppnt->p_align & ~qemu_host_page_mask) != 0) ? \ + TARGET_PAGE_SIZE : MAX(qemu_host_page_size, TARGET_PAGE_SIZE)) +#define TARGET_ELF_PAGELENGTH(_v) ROUND_UP((_v), TARGET_ELF_EXEC_PAGESIZE) #define TARGET_ELF_PAGESTART(_v) ((_v) & \ ~(abi_ulong)(TARGET_ELF_EXEC_PAGESIZE-1)) #define TARGET_ELF_PAGEOFFSET(_v) ((_v) & (TARGET_ELF_EXEC_PAGESIZE-1)) @@ -1504,11 +1567,25 @@ static void bswap_sym(struct elf_sym *sym) bswaptls(&sym->st_size); bswap16s(&sym->st_shndx); } + +#ifdef TARGET_MIPS +static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) +{ + bswap16s(&abiflags->version); + bswap32s(&abiflags->ases); + bswap32s(&abiflags->isa_ext); + bswap32s(&abiflags->flags1); + bswap32s(&abiflags->flags2); +} +#endif #else static inline void bswap_ehdr(struct elfhdr *ehdr) { } static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { } static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { } static inline void bswap_sym(struct elf_sym *sym) { } +#ifdef TARGET_MIPS +static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { } +#endif #endif #ifdef USE_ELF_CORE_DUMP @@ -2277,7 +2354,7 @@ static void load_elf_image(const char *image_name, int image_fd, for (i = 0; i < ehdr->e_phnum; i++) { struct elf_phdr *eppnt = phdr + i; if (eppnt->p_type == PT_LOAD) { - abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em; + abi_ulong vaddr, vaddr_po, vaddr_ps, vaddr_ef, vaddr_em, vaddr_len; int elf_prot = 0; if (eppnt->p_flags & PF_R) elf_prot = PROT_READ; @@ -2287,8 +2364,9 @@ static void load_elf_image(const char *image_name, int image_fd, vaddr = load_bias + eppnt->p_vaddr; vaddr_po = TARGET_ELF_PAGEOFFSET(vaddr); vaddr_ps = TARGET_ELF_PAGESTART(vaddr); + vaddr_len = TARGET_ELF_PAGELENGTH(eppnt->p_filesz + vaddr_po); - error = target_mmap(vaddr_ps, eppnt->p_filesz + vaddr_po, + error = target_mmap(vaddr_ps, vaddr_len, elf_prot, MAP_PRIVATE | MAP_FIXED, image_fd, eppnt->p_offset - vaddr_po); if (error == -1) { @@ -2350,6 +2428,26 @@ static void load_elf_image(const char *image_name, int image_fd, goto exit_errmsg; } *pinterp_name = interp_name; +#ifdef TARGET_MIPS + } else if (eppnt->p_type == PT_MIPS_ABIFLAGS) { + Mips_elf_abiflags_v0 abiflags; + if (eppnt->p_filesz < sizeof(Mips_elf_abiflags_v0)) { + errmsg = "Invalid PT_MIPS_ABIFLAGS entry"; + goto exit_errmsg; + } + if (eppnt->p_offset + eppnt->p_filesz <= BPRM_BUF_SIZE) { + memcpy(&abiflags, bprm_buf + eppnt->p_offset, + sizeof(Mips_elf_abiflags_v0)); + } else { + retval = pread(image_fd, &abiflags, sizeof(Mips_elf_abiflags_v0), + eppnt->p_offset); + if (retval != sizeof(Mips_elf_abiflags_v0)) { + goto exit_perror; + } + } + bswap_mips_abiflags(&abiflags); + info->fp_abi = abiflags.fp_abi; +#endif } } @@ -2661,6 +2759,9 @@ int load_elf_binary(struct linux_binprm *bprm, struct image_info *info) target_mmap(0, qemu_host_page_size, PROT_READ | PROT_EXEC, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); } +#ifdef TARGET_MIPS + info->interp_fp_abi = interp_info.fp_abi; +#endif } bprm->p = create_elf_tables(bprm->p, bprm->argc, bprm->envc, &elf_ex, @@ -2793,7 +2894,7 @@ struct elf_note_info { struct target_elf_prstatus *prstatus; /* NT_PRSTATUS */ struct target_elf_prpsinfo *psinfo; /* NT_PRPSINFO */ - QTAILQ_HEAD(thread_list_head, elf_thread_status) thread_list; + QTAILQ_HEAD(, elf_thread_status) thread_list; #if 0 /* * Current version of ELF coredump doesn't support diff --git a/linux-user/fd-trans.c b/linux-user/fd-trans.c new file mode 100644 index 0000000000..612819c1b1 --- /dev/null +++ b/linux-user/fd-trans.c @@ -0,0 +1,1426 @@ +/* + * 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 +#include +#include +#ifdef CONFIG_INOTIFY +#include +#endif +#include +#ifdef CONFIG_RTNETLINK +#include +#include +#endif +#include "qemu.h" +#include "fd-trans.h" + +enum { + QEMU_IFLA_BR_UNSPEC, + QEMU_IFLA_BR_FORWARD_DELAY, + QEMU_IFLA_BR_HELLO_TIME, + QEMU_IFLA_BR_MAX_AGE, + QEMU_IFLA_BR_AGEING_TIME, + QEMU_IFLA_BR_STP_STATE, + QEMU_IFLA_BR_PRIORITY, + QEMU_IFLA_BR_VLAN_FILTERING, + QEMU_IFLA_BR_VLAN_PROTOCOL, + QEMU_IFLA_BR_GROUP_FWD_MASK, + QEMU_IFLA_BR_ROOT_ID, + QEMU_IFLA_BR_BRIDGE_ID, + QEMU_IFLA_BR_ROOT_PORT, + QEMU_IFLA_BR_ROOT_PATH_COST, + QEMU_IFLA_BR_TOPOLOGY_CHANGE, + QEMU_IFLA_BR_TOPOLOGY_CHANGE_DETECTED, + QEMU_IFLA_BR_HELLO_TIMER, + QEMU_IFLA_BR_TCN_TIMER, + QEMU_IFLA_BR_TOPOLOGY_CHANGE_TIMER, + QEMU_IFLA_BR_GC_TIMER, + QEMU_IFLA_BR_GROUP_ADDR, + QEMU_IFLA_BR_FDB_FLUSH, + QEMU_IFLA_BR_MCAST_ROUTER, + QEMU_IFLA_BR_MCAST_SNOOPING, + QEMU_IFLA_BR_MCAST_QUERY_USE_IFADDR, + QEMU_IFLA_BR_MCAST_QUERIER, + QEMU_IFLA_BR_MCAST_HASH_ELASTICITY, + QEMU_IFLA_BR_MCAST_HASH_MAX, + QEMU_IFLA_BR_MCAST_LAST_MEMBER_CNT, + QEMU_IFLA_BR_MCAST_STARTUP_QUERY_CNT, + QEMU_IFLA_BR_MCAST_LAST_MEMBER_INTVL, + QEMU_IFLA_BR_MCAST_MEMBERSHIP_INTVL, + QEMU_IFLA_BR_MCAST_QUERIER_INTVL, + QEMU_IFLA_BR_MCAST_QUERY_INTVL, + QEMU_IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, + QEMU_IFLA_BR_MCAST_STARTUP_QUERY_INTVL, + QEMU_IFLA_BR_NF_CALL_IPTABLES, + QEMU_IFLA_BR_NF_CALL_IP6TABLES, + QEMU_IFLA_BR_NF_CALL_ARPTABLES, + QEMU_IFLA_BR_VLAN_DEFAULT_PVID, + QEMU_IFLA_BR_PAD, + QEMU_IFLA_BR_VLAN_STATS_ENABLED, + QEMU_IFLA_BR_MCAST_STATS_ENABLED, + QEMU_IFLA_BR_MCAST_IGMP_VERSION, + QEMU_IFLA_BR_MCAST_MLD_VERSION, + QEMU_IFLA_BR_VLAN_STATS_PER_PORT, + QEMU_IFLA_BR_MULTI_BOOLOPT, + QEMU___IFLA_BR_MAX, +}; + +enum { + QEMU_IFLA_UNSPEC, + QEMU_IFLA_ADDRESS, + QEMU_IFLA_BROADCAST, + QEMU_IFLA_IFNAME, + QEMU_IFLA_MTU, + QEMU_IFLA_LINK, + QEMU_IFLA_QDISC, + QEMU_IFLA_STATS, + QEMU_IFLA_COST, + QEMU_IFLA_PRIORITY, + QEMU_IFLA_MASTER, + QEMU_IFLA_WIRELESS, + QEMU_IFLA_PROTINFO, + QEMU_IFLA_TXQLEN, + QEMU_IFLA_MAP, + QEMU_IFLA_WEIGHT, + QEMU_IFLA_OPERSTATE, + QEMU_IFLA_LINKMODE, + QEMU_IFLA_LINKINFO, + QEMU_IFLA_NET_NS_PID, + QEMU_IFLA_IFALIAS, + QEMU_IFLA_NUM_VF, + QEMU_IFLA_VFINFO_LIST, + QEMU_IFLA_STATS64, + QEMU_IFLA_VF_PORTS, + QEMU_IFLA_PORT_SELF, + QEMU_IFLA_AF_SPEC, + QEMU_IFLA_GROUP, + QEMU_IFLA_NET_NS_FD, + QEMU_IFLA_EXT_MASK, + QEMU_IFLA_PROMISCUITY, + QEMU_IFLA_NUM_TX_QUEUES, + QEMU_IFLA_NUM_RX_QUEUES, + QEMU_IFLA_CARRIER, + QEMU_IFLA_PHYS_PORT_ID, + QEMU_IFLA_CARRIER_CHANGES, + QEMU_IFLA_PHYS_SWITCH_ID, + QEMU_IFLA_LINK_NETNSID, + QEMU_IFLA_PHYS_PORT_NAME, + QEMU_IFLA_PROTO_DOWN, + QEMU_IFLA_GSO_MAX_SEGS, + QEMU_IFLA_GSO_MAX_SIZE, + QEMU_IFLA_PAD, + QEMU_IFLA_XDP, + QEMU_IFLA_EVENT, + QEMU_IFLA_NEW_NETNSID, + QEMU_IFLA_IF_NETNSID, + QEMU_IFLA_CARRIER_UP_COUNT, + QEMU_IFLA_CARRIER_DOWN_COUNT, + QEMU_IFLA_NEW_IFINDEX, + QEMU_IFLA_MIN_MTU, + QEMU_IFLA_MAX_MTU, + QEMU___IFLA_MAX +}; + +enum { + QEMU_IFLA_BRPORT_UNSPEC, + QEMU_IFLA_BRPORT_STATE, + QEMU_IFLA_BRPORT_PRIORITY, + QEMU_IFLA_BRPORT_COST, + QEMU_IFLA_BRPORT_MODE, + QEMU_IFLA_BRPORT_GUARD, + QEMU_IFLA_BRPORT_PROTECT, + QEMU_IFLA_BRPORT_FAST_LEAVE, + QEMU_IFLA_BRPORT_LEARNING, + QEMU_IFLA_BRPORT_UNICAST_FLOOD, + QEMU_IFLA_BRPORT_PROXYARP, + QEMU_IFLA_BRPORT_LEARNING_SYNC, + QEMU_IFLA_BRPORT_PROXYARP_WIFI, + QEMU_IFLA_BRPORT_ROOT_ID, + QEMU_IFLA_BRPORT_BRIDGE_ID, + QEMU_IFLA_BRPORT_DESIGNATED_PORT, + QEMU_IFLA_BRPORT_DESIGNATED_COST, + QEMU_IFLA_BRPORT_ID, + QEMU_IFLA_BRPORT_NO, + QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, + QEMU_IFLA_BRPORT_CONFIG_PENDING, + QEMU_IFLA_BRPORT_MESSAGE_AGE_TIMER, + QEMU_IFLA_BRPORT_FORWARD_DELAY_TIMER, + QEMU_IFLA_BRPORT_HOLD_TIMER, + QEMU_IFLA_BRPORT_FLUSH, + QEMU_IFLA_BRPORT_MULTICAST_ROUTER, + QEMU_IFLA_BRPORT_PAD, + QEMU_IFLA_BRPORT_MCAST_FLOOD, + QEMU_IFLA_BRPORT_MCAST_TO_UCAST, + QEMU_IFLA_BRPORT_VLAN_TUNNEL, + QEMU_IFLA_BRPORT_BCAST_FLOOD, + QEMU_IFLA_BRPORT_GROUP_FWD_MASK, + QEMU_IFLA_BRPORT_NEIGH_SUPPRESS, + QEMU_IFLA_BRPORT_ISOLATED, + QEMU_IFLA_BRPORT_BACKUP_PORT, + QEMU___IFLA_BRPORT_MAX +}; + +enum { + QEMU_IFLA_TUN_UNSPEC, + QEMU_IFLA_TUN_OWNER, + QEMU_IFLA_TUN_GROUP, + QEMU_IFLA_TUN_TYPE, + QEMU_IFLA_TUN_PI, + QEMU_IFLA_TUN_VNET_HDR, + QEMU_IFLA_TUN_PERSIST, + QEMU_IFLA_TUN_MULTI_QUEUE, + QEMU_IFLA_TUN_NUM_QUEUES, + QEMU_IFLA_TUN_NUM_DISABLED_QUEUES, + QEMU___IFLA_TUN_MAX, +}; + +enum { + QEMU_IFLA_INFO_UNSPEC, + QEMU_IFLA_INFO_KIND, + QEMU_IFLA_INFO_DATA, + QEMU_IFLA_INFO_XSTATS, + QEMU_IFLA_INFO_SLAVE_KIND, + QEMU_IFLA_INFO_SLAVE_DATA, + QEMU___IFLA_INFO_MAX, +}; + +enum { + QEMU_IFLA_INET_UNSPEC, + QEMU_IFLA_INET_CONF, + QEMU___IFLA_INET_MAX, +}; + +enum { + QEMU_IFLA_INET6_UNSPEC, + QEMU_IFLA_INET6_FLAGS, + QEMU_IFLA_INET6_CONF, + QEMU_IFLA_INET6_STATS, + QEMU_IFLA_INET6_MCAST, + QEMU_IFLA_INET6_CACHEINFO, + QEMU_IFLA_INET6_ICMP6STATS, + QEMU_IFLA_INET6_TOKEN, + QEMU_IFLA_INET6_ADDR_GEN_MODE, + QEMU___IFLA_INET6_MAX +}; + +enum { + QEMU_IFLA_XDP_UNSPEC, + QEMU_IFLA_XDP_FD, + QEMU_IFLA_XDP_ATTACHED, + QEMU_IFLA_XDP_FLAGS, + QEMU_IFLA_XDP_PROG_ID, + QEMU___IFLA_XDP_MAX, +}; + +enum { + QEMU_RTA_UNSPEC, + QEMU_RTA_DST, + QEMU_RTA_SRC, + QEMU_RTA_IIF, + QEMU_RTA_OIF, + QEMU_RTA_GATEWAY, + QEMU_RTA_PRIORITY, + QEMU_RTA_PREFSRC, + QEMU_RTA_METRICS, + QEMU_RTA_MULTIPATH, + QEMU_RTA_PROTOINFO, /* no longer used */ + QEMU_RTA_FLOW, + QEMU_RTA_CACHEINFO, + QEMU_RTA_SESSION, /* no longer used */ + QEMU_RTA_MP_ALGO, /* no longer used */ + QEMU_RTA_TABLE, + QEMU_RTA_MARK, + QEMU_RTA_MFC_STATS, + QEMU_RTA_VIA, + QEMU_RTA_NEWDST, + QEMU_RTA_PREF, + QEMU_RTA_ENCAP_TYPE, + QEMU_RTA_ENCAP, + QEMU_RTA_EXPIRES, + QEMU_RTA_PAD, + QEMU_RTA_UID, + QEMU_RTA_TTL_PROPAGATE, + QEMU_RTA_IP_PROTO, + QEMU_RTA_SPORT, + QEMU_RTA_DPORT, + QEMU___RTA_MAX +}; + +TargetFdTrans **target_fd_trans; +unsigned int target_fd_max; + +static void tswap_nlmsghdr(struct nlmsghdr *nlh) +{ + nlh->nlmsg_len = tswap32(nlh->nlmsg_len); + nlh->nlmsg_type = tswap16(nlh->nlmsg_type); + nlh->nlmsg_flags = tswap16(nlh->nlmsg_flags); + nlh->nlmsg_seq = tswap32(nlh->nlmsg_seq); + nlh->nlmsg_pid = tswap32(nlh->nlmsg_pid); +} + +static abi_long host_to_target_for_each_nlmsg(struct nlmsghdr *nlh, + size_t len, + abi_long (*host_to_target_nlmsg) + (struct nlmsghdr *)) +{ + uint32_t nlmsg_len; + abi_long ret; + + while (len > sizeof(struct nlmsghdr)) { + + nlmsg_len = nlh->nlmsg_len; + if (nlmsg_len < sizeof(struct nlmsghdr) || + nlmsg_len > len) { + break; + } + + switch (nlh->nlmsg_type) { + case NLMSG_DONE: + tswap_nlmsghdr(nlh); + return 0; + case NLMSG_NOOP: + break; + case NLMSG_ERROR: + { + struct nlmsgerr *e = NLMSG_DATA(nlh); + e->error = tswap32(e->error); + tswap_nlmsghdr(&e->msg); + tswap_nlmsghdr(nlh); + return 0; + } + default: + ret = host_to_target_nlmsg(nlh); + if (ret < 0) { + tswap_nlmsghdr(nlh); + return ret; + } + break; + } + tswap_nlmsghdr(nlh); + len -= NLMSG_ALIGN(nlmsg_len); + nlh = (struct nlmsghdr *)(((char*)nlh) + NLMSG_ALIGN(nlmsg_len)); + } + return 0; +} + +static abi_long target_to_host_for_each_nlmsg(struct nlmsghdr *nlh, + size_t len, + abi_long (*target_to_host_nlmsg) + (struct nlmsghdr *)) +{ + int ret; + + while (len > sizeof(struct nlmsghdr)) { + if (tswap32(nlh->nlmsg_len) < sizeof(struct nlmsghdr) || + tswap32(nlh->nlmsg_len) > len) { + break; + } + tswap_nlmsghdr(nlh); + switch (nlh->nlmsg_type) { + case NLMSG_DONE: + return 0; + case NLMSG_NOOP: + break; + case NLMSG_ERROR: + { + struct nlmsgerr *e = NLMSG_DATA(nlh); + e->error = tswap32(e->error); + tswap_nlmsghdr(&e->msg); + return 0; + } + default: + ret = target_to_host_nlmsg(nlh); + if (ret < 0) { + return ret; + } + } + len -= NLMSG_ALIGN(nlh->nlmsg_len); + nlh = (struct nlmsghdr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); + } + return 0; +} + +#ifdef CONFIG_RTNETLINK +static abi_long host_to_target_for_each_nlattr(struct nlattr *nlattr, + size_t len, void *context, + abi_long (*host_to_target_nlattr) + (struct nlattr *, + void *context)) +{ + unsigned short nla_len; + abi_long ret; + + while (len > sizeof(struct nlattr)) { + nla_len = nlattr->nla_len; + if (nla_len < sizeof(struct nlattr) || + nla_len > len) { + break; + } + ret = host_to_target_nlattr(nlattr, context); + nlattr->nla_len = tswap16(nlattr->nla_len); + nlattr->nla_type = tswap16(nlattr->nla_type); + if (ret < 0) { + return ret; + } + len -= NLA_ALIGN(nla_len); + nlattr = (struct nlattr *)(((char *)nlattr) + NLA_ALIGN(nla_len)); + } + return 0; +} + +static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr, + size_t len, + abi_long (*host_to_target_rtattr) + (struct rtattr *)) +{ + unsigned short rta_len; + abi_long ret; + + while (len > sizeof(struct rtattr)) { + rta_len = rtattr->rta_len; + if (rta_len < sizeof(struct rtattr) || + rta_len > len) { + break; + } + ret = host_to_target_rtattr(rtattr); + rtattr->rta_len = tswap16(rtattr->rta_len); + rtattr->rta_type = tswap16(rtattr->rta_type); + if (ret < 0) { + return ret; + } + len -= RTA_ALIGN(rta_len); + rtattr = (struct rtattr *)(((char *)rtattr) + RTA_ALIGN(rta_len)); + } + return 0; +} + +#define NLA_DATA(nla) ((void *)((char *)(nla)) + NLA_HDRLEN) + +static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, + void *context) +{ + uint16_t *u16; + uint32_t *u32; + uint64_t *u64; + + switch (nlattr->nla_type) { + /* no data */ + case QEMU_IFLA_BR_FDB_FLUSH: + break; + /* binary */ + case QEMU_IFLA_BR_GROUP_ADDR: + break; + /* uint8_t */ + case QEMU_IFLA_BR_VLAN_FILTERING: + case QEMU_IFLA_BR_TOPOLOGY_CHANGE: + case QEMU_IFLA_BR_TOPOLOGY_CHANGE_DETECTED: + case QEMU_IFLA_BR_MCAST_ROUTER: + case QEMU_IFLA_BR_MCAST_SNOOPING: + case QEMU_IFLA_BR_MCAST_QUERY_USE_IFADDR: + case QEMU_IFLA_BR_MCAST_QUERIER: + case QEMU_IFLA_BR_NF_CALL_IPTABLES: + case QEMU_IFLA_BR_NF_CALL_IP6TABLES: + case QEMU_IFLA_BR_NF_CALL_ARPTABLES: + case QEMU_IFLA_BR_VLAN_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_STATS_ENABLED: + case QEMU_IFLA_BR_MCAST_IGMP_VERSION: + case QEMU_IFLA_BR_MCAST_MLD_VERSION: + case QEMU_IFLA_BR_VLAN_STATS_PER_PORT: + break; + /* uint16_t */ + case QEMU_IFLA_BR_PRIORITY: + case QEMU_IFLA_BR_VLAN_PROTOCOL: + case QEMU_IFLA_BR_GROUP_FWD_MASK: + case QEMU_IFLA_BR_ROOT_PORT: + case QEMU_IFLA_BR_VLAN_DEFAULT_PVID: + u16 = NLA_DATA(nlattr); + *u16 = tswap16(*u16); + break; + /* uint32_t */ + case QEMU_IFLA_BR_FORWARD_DELAY: + case QEMU_IFLA_BR_HELLO_TIME: + case QEMU_IFLA_BR_MAX_AGE: + case QEMU_IFLA_BR_AGEING_TIME: + case QEMU_IFLA_BR_STP_STATE: + case QEMU_IFLA_BR_ROOT_PATH_COST: + case QEMU_IFLA_BR_MCAST_HASH_ELASTICITY: + case QEMU_IFLA_BR_MCAST_HASH_MAX: + case QEMU_IFLA_BR_MCAST_LAST_MEMBER_CNT: + case QEMU_IFLA_BR_MCAST_STARTUP_QUERY_CNT: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + /* uint64_t */ + case QEMU_IFLA_BR_HELLO_TIMER: + case QEMU_IFLA_BR_TCN_TIMER: + case QEMU_IFLA_BR_GC_TIMER: + case QEMU_IFLA_BR_TOPOLOGY_CHANGE_TIMER: + case QEMU_IFLA_BR_MCAST_LAST_MEMBER_INTVL: + case QEMU_IFLA_BR_MCAST_MEMBERSHIP_INTVL: + case QEMU_IFLA_BR_MCAST_QUERIER_INTVL: + case QEMU_IFLA_BR_MCAST_QUERY_INTVL: + case QEMU_IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: + case QEMU_IFLA_BR_MCAST_STARTUP_QUERY_INTVL: + u64 = NLA_DATA(nlattr); + *u64 = tswap64(*u64); + break; + /* ifla_bridge_id: uin8_t[] */ + case QEMU_IFLA_BR_ROOT_ID: + case QEMU_IFLA_BR_BRIDGE_ID: + break; + default: + gemu_log("Unknown QEMU_IFLA_BR type %d\n", nlattr->nla_type); + break; + } + return 0; +} + +static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, + void *context) +{ + uint16_t *u16; + uint32_t *u32; + uint64_t *u64; + + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_BRPORT_STATE: + case QEMU_IFLA_BRPORT_MODE: + case QEMU_IFLA_BRPORT_GUARD: + case QEMU_IFLA_BRPORT_PROTECT: + case QEMU_IFLA_BRPORT_FAST_LEAVE: + case QEMU_IFLA_BRPORT_LEARNING: + case QEMU_IFLA_BRPORT_UNICAST_FLOOD: + case QEMU_IFLA_BRPORT_PROXYARP: + case QEMU_IFLA_BRPORT_LEARNING_SYNC: + case QEMU_IFLA_BRPORT_PROXYARP_WIFI: + case QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: + case QEMU_IFLA_BRPORT_CONFIG_PENDING: + case QEMU_IFLA_BRPORT_MULTICAST_ROUTER: + case QEMU_IFLA_BRPORT_MCAST_FLOOD: + case QEMU_IFLA_BRPORT_MCAST_TO_UCAST: + case QEMU_IFLA_BRPORT_VLAN_TUNNEL: + case QEMU_IFLA_BRPORT_BCAST_FLOOD: + case QEMU_IFLA_BRPORT_NEIGH_SUPPRESS: + case QEMU_IFLA_BRPORT_ISOLATED: + break; + /* uint16_t */ + case QEMU_IFLA_BRPORT_PRIORITY: + case QEMU_IFLA_BRPORT_DESIGNATED_PORT: + case QEMU_IFLA_BRPORT_DESIGNATED_COST: + case QEMU_IFLA_BRPORT_ID: + case QEMU_IFLA_BRPORT_NO: + case QEMU_IFLA_BRPORT_GROUP_FWD_MASK: + u16 = NLA_DATA(nlattr); + *u16 = tswap16(*u16); + break; + /* uin32_t */ + case QEMU_IFLA_BRPORT_COST: + case QEMU_IFLA_BRPORT_BACKUP_PORT: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + /* uint64_t */ + case QEMU_IFLA_BRPORT_MESSAGE_AGE_TIMER: + case QEMU_IFLA_BRPORT_FORWARD_DELAY_TIMER: + case QEMU_IFLA_BRPORT_HOLD_TIMER: + u64 = NLA_DATA(nlattr); + *u64 = tswap64(*u64); + break; + /* ifla_bridge_id: uint8_t[] */ + case QEMU_IFLA_BRPORT_ROOT_ID: + case QEMU_IFLA_BRPORT_BRIDGE_ID: + break; + /* br_boolopt_multi { uint32_t, uint32_t } */ + case QEMU_IFLA_BR_MULTI_BOOLOPT: + u32 = NLA_DATA(nlattr); + u32[0] = tswap32(u32[0]); /* optval */ + u32[1] = tswap32(u32[1]); /* optmask */ + break; + default: + gemu_log("Unknown QEMU_IFLA_BRPORT type %d\n", nlattr->nla_type); + break; + } + return 0; +} + +static abi_long host_to_target_data_tun_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_TUN_TYPE: + case QEMU_IFLA_TUN_PI: + case QEMU_IFLA_TUN_VNET_HDR: + case QEMU_IFLA_TUN_PERSIST: + case QEMU_IFLA_TUN_MULTI_QUEUE: + break; + /* uint32_t */ + case QEMU_IFLA_TUN_NUM_QUEUES: + case QEMU_IFLA_TUN_NUM_DISABLED_QUEUES: + case QEMU_IFLA_TUN_OWNER: + case QEMU_IFLA_TUN_GROUP: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + default: + gemu_log("Unknown QEMU_IFLA_TUN type %d\n", nlattr->nla_type); + break; + } + return 0; +} + +struct linkinfo_context { + int len; + char *name; + int slave_len; + char *slave_name; +}; + +static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr, + void *context) +{ + struct linkinfo_context *li_context = context; + + switch (nlattr->nla_type) { + /* string */ + case QEMU_IFLA_INFO_KIND: + li_context->name = NLA_DATA(nlattr); + li_context->len = nlattr->nla_len - NLA_HDRLEN; + break; + case QEMU_IFLA_INFO_SLAVE_KIND: + li_context->slave_name = NLA_DATA(nlattr); + li_context->slave_len = nlattr->nla_len - NLA_HDRLEN; + break; + /* stats */ + case QEMU_IFLA_INFO_XSTATS: + /* FIXME: only used by CAN */ + break; + /* nested */ + case QEMU_IFLA_INFO_DATA: + if (strncmp(li_context->name, "bridge", + li_context->len) == 0) { + return host_to_target_for_each_nlattr(NLA_DATA(nlattr), + nlattr->nla_len, + NULL, + host_to_target_data_bridge_nlattr); + } else if (strncmp(li_context->name, "tun", + li_context->len) == 0) { + return host_to_target_for_each_nlattr(NLA_DATA(nlattr), + nlattr->nla_len, + NULL, + host_to_target_data_tun_nlattr); + } else { + gemu_log("Unknown QEMU_IFLA_INFO_KIND %s\n", li_context->name); + } + break; + case QEMU_IFLA_INFO_SLAVE_DATA: + if (strncmp(li_context->slave_name, "bridge", + li_context->slave_len) == 0) { + return host_to_target_for_each_nlattr(NLA_DATA(nlattr), + nlattr->nla_len, + NULL, + host_to_target_slave_data_bridge_nlattr); + } else { + gemu_log("Unknown QEMU_IFLA_INFO_SLAVE_KIND %s\n", + li_context->slave_name); + } + break; + default: + gemu_log("Unknown host QEMU_IFLA_INFO type: %d\n", nlattr->nla_type); + break; + } + + return 0; +} + +static abi_long host_to_target_data_inet_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + int i; + + switch (nlattr->nla_type) { + case QEMU_IFLA_INET_CONF: + u32 = NLA_DATA(nlattr); + for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32); + i++) { + u32[i] = tswap32(u32[i]); + } + break; + default: + gemu_log("Unknown host AF_INET type: %d\n", nlattr->nla_type); + } + return 0; +} + +static abi_long host_to_target_data_inet6_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + uint64_t *u64; + struct ifla_cacheinfo *ci; + int i; + + switch (nlattr->nla_type) { + /* binaries */ + case QEMU_IFLA_INET6_TOKEN: + break; + /* uint8_t */ + case QEMU_IFLA_INET6_ADDR_GEN_MODE: + break; + /* uint32_t */ + case QEMU_IFLA_INET6_FLAGS: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + /* uint32_t[] */ + case QEMU_IFLA_INET6_CONF: + u32 = NLA_DATA(nlattr); + for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32); + i++) { + u32[i] = tswap32(u32[i]); + } + break; + /* ifla_cacheinfo */ + case QEMU_IFLA_INET6_CACHEINFO: + ci = NLA_DATA(nlattr); + ci->max_reasm_len = tswap32(ci->max_reasm_len); + ci->tstamp = tswap32(ci->tstamp); + ci->reachable_time = tswap32(ci->reachable_time); + ci->retrans_time = tswap32(ci->retrans_time); + break; + /* uint64_t[] */ + case QEMU_IFLA_INET6_STATS: + case QEMU_IFLA_INET6_ICMP6STATS: + u64 = NLA_DATA(nlattr); + for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u64); + i++) { + u64[i] = tswap64(u64[i]); + } + break; + default: + gemu_log("Unknown host AF_INET6 type: %d\n", nlattr->nla_type); + } + return 0; +} + +static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr, + void *context) +{ + switch (nlattr->nla_type) { + case AF_INET: + return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len, + NULL, + host_to_target_data_inet_nlattr); + case AF_INET6: + return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len, + NULL, + host_to_target_data_inet6_nlattr); + default: + gemu_log("Unknown host AF_SPEC type: %d\n", nlattr->nla_type); + break; + } + return 0; +} + +static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr, + void *context) +{ + uint32_t *u32; + + switch (nlattr->nla_type) { + /* uint8_t */ + case QEMU_IFLA_XDP_ATTACHED: + break; + /* uint32_t */ + case QEMU_IFLA_XDP_PROG_ID: + u32 = NLA_DATA(nlattr); + *u32 = tswap32(*u32); + break; + default: + gemu_log("Unknown host XDP type: %d\n", nlattr->nla_type); + break; + } + return 0; +} + +static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) +{ + uint32_t *u32; + struct rtnl_link_stats *st; + struct rtnl_link_stats64 *st64; + struct rtnl_link_ifmap *map; + struct linkinfo_context li_context; + + switch (rtattr->rta_type) { + /* binary stream */ + case QEMU_IFLA_ADDRESS: + case QEMU_IFLA_BROADCAST: + /* string */ + case QEMU_IFLA_IFNAME: + case QEMU_IFLA_QDISC: + break; + /* uin8_t */ + case QEMU_IFLA_OPERSTATE: + case QEMU_IFLA_LINKMODE: + case QEMU_IFLA_CARRIER: + case QEMU_IFLA_PROTO_DOWN: + break; + /* uint32_t */ + case QEMU_IFLA_MTU: + case QEMU_IFLA_LINK: + case QEMU_IFLA_WEIGHT: + case QEMU_IFLA_TXQLEN: + case QEMU_IFLA_CARRIER_CHANGES: + case QEMU_IFLA_NUM_RX_QUEUES: + case QEMU_IFLA_NUM_TX_QUEUES: + case QEMU_IFLA_PROMISCUITY: + case QEMU_IFLA_EXT_MASK: + case QEMU_IFLA_LINK_NETNSID: + case QEMU_IFLA_GROUP: + case QEMU_IFLA_MASTER: + case QEMU_IFLA_NUM_VF: + case QEMU_IFLA_GSO_MAX_SEGS: + case QEMU_IFLA_GSO_MAX_SIZE: + case QEMU_IFLA_CARRIER_UP_COUNT: + case QEMU_IFLA_CARRIER_DOWN_COUNT: + case QEMU_IFLA_MIN_MTU: + case QEMU_IFLA_MAX_MTU: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + /* struct rtnl_link_stats */ + case QEMU_IFLA_STATS: + st = RTA_DATA(rtattr); + st->rx_packets = tswap32(st->rx_packets); + st->tx_packets = tswap32(st->tx_packets); + st->rx_bytes = tswap32(st->rx_bytes); + st->tx_bytes = tswap32(st->tx_bytes); + st->rx_errors = tswap32(st->rx_errors); + st->tx_errors = tswap32(st->tx_errors); + st->rx_dropped = tswap32(st->rx_dropped); + st->tx_dropped = tswap32(st->tx_dropped); + st->multicast = tswap32(st->multicast); + st->collisions = tswap32(st->collisions); + + /* detailed rx_errors: */ + st->rx_length_errors = tswap32(st->rx_length_errors); + st->rx_over_errors = tswap32(st->rx_over_errors); + st->rx_crc_errors = tswap32(st->rx_crc_errors); + st->rx_frame_errors = tswap32(st->rx_frame_errors); + st->rx_fifo_errors = tswap32(st->rx_fifo_errors); + st->rx_missed_errors = tswap32(st->rx_missed_errors); + + /* detailed tx_errors */ + st->tx_aborted_errors = tswap32(st->tx_aborted_errors); + st->tx_carrier_errors = tswap32(st->tx_carrier_errors); + st->tx_fifo_errors = tswap32(st->tx_fifo_errors); + st->tx_heartbeat_errors = tswap32(st->tx_heartbeat_errors); + st->tx_window_errors = tswap32(st->tx_window_errors); + + /* for cslip etc */ + st->rx_compressed = tswap32(st->rx_compressed); + st->tx_compressed = tswap32(st->tx_compressed); + break; + /* struct rtnl_link_stats64 */ + case QEMU_IFLA_STATS64: + st64 = RTA_DATA(rtattr); + st64->rx_packets = tswap64(st64->rx_packets); + st64->tx_packets = tswap64(st64->tx_packets); + st64->rx_bytes = tswap64(st64->rx_bytes); + st64->tx_bytes = tswap64(st64->tx_bytes); + st64->rx_errors = tswap64(st64->rx_errors); + st64->tx_errors = tswap64(st64->tx_errors); + st64->rx_dropped = tswap64(st64->rx_dropped); + st64->tx_dropped = tswap64(st64->tx_dropped); + st64->multicast = tswap64(st64->multicast); + st64->collisions = tswap64(st64->collisions); + + /* detailed rx_errors: */ + st64->rx_length_errors = tswap64(st64->rx_length_errors); + st64->rx_over_errors = tswap64(st64->rx_over_errors); + st64->rx_crc_errors = tswap64(st64->rx_crc_errors); + st64->rx_frame_errors = tswap64(st64->rx_frame_errors); + st64->rx_fifo_errors = tswap64(st64->rx_fifo_errors); + st64->rx_missed_errors = tswap64(st64->rx_missed_errors); + + /* detailed tx_errors */ + st64->tx_aborted_errors = tswap64(st64->tx_aborted_errors); + st64->tx_carrier_errors = tswap64(st64->tx_carrier_errors); + st64->tx_fifo_errors = tswap64(st64->tx_fifo_errors); + st64->tx_heartbeat_errors = tswap64(st64->tx_heartbeat_errors); + st64->tx_window_errors = tswap64(st64->tx_window_errors); + + /* for cslip etc */ + st64->rx_compressed = tswap64(st64->rx_compressed); + st64->tx_compressed = tswap64(st64->tx_compressed); + break; + /* struct rtnl_link_ifmap */ + case QEMU_IFLA_MAP: + map = RTA_DATA(rtattr); + map->mem_start = tswap64(map->mem_start); + map->mem_end = tswap64(map->mem_end); + map->base_addr = tswap64(map->base_addr); + map->irq = tswap16(map->irq); + break; + /* nested */ + case QEMU_IFLA_LINKINFO: + memset(&li_context, 0, sizeof(li_context)); + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + &li_context, + host_to_target_data_linkinfo_nlattr); + case QEMU_IFLA_AF_SPEC: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_spec_nlattr); + case QEMU_IFLA_XDP: + return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, + NULL, + host_to_target_data_xdp_nlattr); + default: + gemu_log("Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); + break; + } + return 0; +} + +static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr) +{ + uint32_t *u32; + struct ifa_cacheinfo *ci; + + switch (rtattr->rta_type) { + /* binary: depends on family type */ + case IFA_ADDRESS: + case IFA_LOCAL: + break; + /* string */ + case IFA_LABEL: + break; + /* u32 */ + case IFA_FLAGS: + case IFA_BROADCAST: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + /* struct ifa_cacheinfo */ + case IFA_CACHEINFO: + ci = RTA_DATA(rtattr); + ci->ifa_prefered = tswap32(ci->ifa_prefered); + ci->ifa_valid = tswap32(ci->ifa_valid); + ci->cstamp = tswap32(ci->cstamp); + ci->tstamp = tswap32(ci->tstamp); + break; + default: + gemu_log("Unknown host IFA type: %d\n", rtattr->rta_type); + break; + } + return 0; +} + +static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr) +{ + uint32_t *u32; + struct rta_cacheinfo *ci; + + switch (rtattr->rta_type) { + /* binary: depends on family type */ + case QEMU_RTA_GATEWAY: + case QEMU_RTA_DST: + case QEMU_RTA_PREFSRC: + break; + /* u8 */ + case QEMU_RTA_PREF: + break; + /* u32 */ + case QEMU_RTA_PRIORITY: + case QEMU_RTA_TABLE: + case QEMU_RTA_OIF: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + /* struct rta_cacheinfo */ + case QEMU_RTA_CACHEINFO: + ci = RTA_DATA(rtattr); + ci->rta_clntref = tswap32(ci->rta_clntref); + ci->rta_lastuse = tswap32(ci->rta_lastuse); + ci->rta_expires = tswap32(ci->rta_expires); + ci->rta_error = tswap32(ci->rta_error); + ci->rta_used = tswap32(ci->rta_used); +#if defined(RTNETLINK_HAVE_PEERINFO) + ci->rta_id = tswap32(ci->rta_id); + ci->rta_ts = tswap32(ci->rta_ts); + ci->rta_tsage = tswap32(ci->rta_tsage); +#endif + break; + default: + gemu_log("Unknown host RTA type: %d\n", rtattr->rta_type); + break; + } + return 0; +} + +static abi_long host_to_target_link_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + return host_to_target_for_each_rtattr(rtattr, rtattr_len, + host_to_target_data_link_rtattr); +} + +static abi_long host_to_target_addr_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + return host_to_target_for_each_rtattr(rtattr, rtattr_len, + host_to_target_data_addr_rtattr); +} + +static abi_long host_to_target_route_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + return host_to_target_for_each_rtattr(rtattr, rtattr_len, + host_to_target_data_route_rtattr); +} + +static abi_long host_to_target_data_route(struct nlmsghdr *nlh) +{ + uint32_t nlmsg_len; + struct ifinfomsg *ifi; + struct ifaddrmsg *ifa; + struct rtmsg *rtm; + + nlmsg_len = nlh->nlmsg_len; + switch (nlh->nlmsg_type) { + case RTM_NEWLINK: + case RTM_DELLINK: + case RTM_GETLINK: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) { + ifi = NLMSG_DATA(nlh); + ifi->ifi_type = tswap16(ifi->ifi_type); + ifi->ifi_index = tswap32(ifi->ifi_index); + ifi->ifi_flags = tswap32(ifi->ifi_flags); + ifi->ifi_change = tswap32(ifi->ifi_change); + host_to_target_link_rtattr(IFLA_RTA(ifi), + nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); + } + break; + case RTM_NEWADDR: + case RTM_DELADDR: + case RTM_GETADDR: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifa))) { + ifa = NLMSG_DATA(nlh); + ifa->ifa_index = tswap32(ifa->ifa_index); + host_to_target_addr_rtattr(IFA_RTA(ifa), + nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); + } + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + case RTM_GETROUTE: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*rtm))) { + rtm = NLMSG_DATA(nlh); + rtm->rtm_flags = tswap32(rtm->rtm_flags); + host_to_target_route_rtattr(RTM_RTA(rtm), + nlmsg_len - NLMSG_LENGTH(sizeof(*rtm))); + } + break; + default: + return -TARGET_EINVAL; + } + return 0; +} + +static inline abi_long host_to_target_nlmsg_route(struct nlmsghdr *nlh, + size_t len) +{ + return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_route); +} + +static abi_long target_to_host_for_each_rtattr(struct rtattr *rtattr, + size_t len, + abi_long (*target_to_host_rtattr) + (struct rtattr *)) +{ + abi_long ret; + + while (len >= sizeof(struct rtattr)) { + if (tswap16(rtattr->rta_len) < sizeof(struct rtattr) || + tswap16(rtattr->rta_len) > len) { + break; + } + rtattr->rta_len = tswap16(rtattr->rta_len); + rtattr->rta_type = tswap16(rtattr->rta_type); + ret = target_to_host_rtattr(rtattr); + if (ret < 0) { + return ret; + } + len -= RTA_ALIGN(rtattr->rta_len); + rtattr = (struct rtattr *)(((char *)rtattr) + + RTA_ALIGN(rtattr->rta_len)); + } + return 0; +} + +static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr) +{ + switch (rtattr->rta_type) { + default: + gemu_log("Unknown target QEMU_IFLA type: %d\n", rtattr->rta_type); + break; + } + return 0; +} + +static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) +{ + switch (rtattr->rta_type) { + /* binary: depends on family type */ + case IFA_LOCAL: + case IFA_ADDRESS: + break; + default: + gemu_log("Unknown target IFA type: %d\n", rtattr->rta_type); + break; + } + return 0; +} + +static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr) +{ + uint32_t *u32; + switch (rtattr->rta_type) { + /* binary: depends on family type */ + case QEMU_RTA_DST: + case QEMU_RTA_SRC: + case QEMU_RTA_GATEWAY: + break; + /* u32 */ + case QEMU_RTA_PRIORITY: + case QEMU_RTA_OIF: + u32 = RTA_DATA(rtattr); + *u32 = tswap32(*u32); + break; + default: + gemu_log("Unknown target RTA type: %d\n", rtattr->rta_type); + break; + } + return 0; +} + +static void target_to_host_link_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + target_to_host_for_each_rtattr(rtattr, rtattr_len, + target_to_host_data_link_rtattr); +} + +static void target_to_host_addr_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + target_to_host_for_each_rtattr(rtattr, rtattr_len, + target_to_host_data_addr_rtattr); +} + +static void target_to_host_route_rtattr(struct rtattr *rtattr, + uint32_t rtattr_len) +{ + target_to_host_for_each_rtattr(rtattr, rtattr_len, + target_to_host_data_route_rtattr); +} + +static abi_long target_to_host_data_route(struct nlmsghdr *nlh) +{ + struct ifinfomsg *ifi; + struct ifaddrmsg *ifa; + struct rtmsg *rtm; + + switch (nlh->nlmsg_type) { + case RTM_GETLINK: + break; + case RTM_NEWLINK: + case RTM_DELLINK: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) { + ifi = NLMSG_DATA(nlh); + ifi->ifi_type = tswap16(ifi->ifi_type); + ifi->ifi_index = tswap32(ifi->ifi_index); + ifi->ifi_flags = tswap32(ifi->ifi_flags); + ifi->ifi_change = tswap32(ifi->ifi_change); + target_to_host_link_rtattr(IFLA_RTA(ifi), nlh->nlmsg_len - + NLMSG_LENGTH(sizeof(*ifi))); + } + break; + case RTM_GETADDR: + case RTM_NEWADDR: + case RTM_DELADDR: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifa))) { + ifa = NLMSG_DATA(nlh); + ifa->ifa_index = tswap32(ifa->ifa_index); + target_to_host_addr_rtattr(IFA_RTA(ifa), nlh->nlmsg_len - + NLMSG_LENGTH(sizeof(*ifa))); + } + break; + case RTM_GETROUTE: + break; + case RTM_NEWROUTE: + case RTM_DELROUTE: + if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*rtm))) { + rtm = NLMSG_DATA(nlh); + rtm->rtm_flags = tswap32(rtm->rtm_flags); + target_to_host_route_rtattr(RTM_RTA(rtm), nlh->nlmsg_len - + NLMSG_LENGTH(sizeof(*rtm))); + } + break; + default: + return -TARGET_EOPNOTSUPP; + } + return 0; +} + +static abi_long target_to_host_nlmsg_route(struct nlmsghdr *nlh, size_t len) +{ + return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_route); +} +#endif /* CONFIG_RTNETLINK */ + +static abi_long host_to_target_data_audit(struct nlmsghdr *nlh) +{ + switch (nlh->nlmsg_type) { + default: + gemu_log("Unknown host audit message type %d\n", + nlh->nlmsg_type); + return -TARGET_EINVAL; + } + return 0; +} + +static inline abi_long host_to_target_nlmsg_audit(struct nlmsghdr *nlh, + size_t len) +{ + return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_audit); +} + +static abi_long target_to_host_data_audit(struct nlmsghdr *nlh) +{ + switch (nlh->nlmsg_type) { + case AUDIT_USER: + case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG: + case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2: + break; + default: + gemu_log("Unknown target audit message type %d\n", + nlh->nlmsg_type); + return -TARGET_EINVAL; + } + + return 0; +} + +static abi_long target_to_host_nlmsg_audit(struct nlmsghdr *nlh, size_t len) +{ + return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_audit); +} + +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; +} + +TargetFdTrans target_packet_trans = { + .target_to_host_addr = packet_target_to_host_sockaddr, +}; + +#ifdef CONFIG_RTNETLINK +static abi_long netlink_route_target_to_host(void *buf, size_t len) +{ + abi_long ret; + + ret = target_to_host_nlmsg_route(buf, len); + if (ret < 0) { + return ret; + } + + return len; +} + +static abi_long netlink_route_host_to_target(void *buf, size_t len) +{ + abi_long ret; + + ret = host_to_target_nlmsg_route(buf, len); + if (ret < 0) { + return ret; + } + + return len; +} + +TargetFdTrans target_netlink_route_trans = { + .target_to_host_data = netlink_route_target_to_host, + .host_to_target_data = netlink_route_host_to_target, +}; +#endif /* CONFIG_RTNETLINK */ + +static abi_long netlink_audit_target_to_host(void *buf, size_t len) +{ + abi_long ret; + + ret = target_to_host_nlmsg_audit(buf, len); + if (ret < 0) { + return ret; + } + + return len; +} + +static abi_long netlink_audit_host_to_target(void *buf, size_t len) +{ + abi_long ret; + + ret = host_to_target_nlmsg_audit(buf, len); + if (ret < 0) { + return ret; + } + + return len; +} + +TargetFdTrans target_netlink_audit_trans = { + .target_to_host_data = netlink_audit_target_to_host, + .host_to_target_data = netlink_audit_host_to_target, +}; + +/* 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; +} + +TargetFdTrans target_signalfd_trans = { + .host_to_target_data = host_to_target_data_signalfd, +}; + +static abi_long swap_data_eventfd(void *buf, size_t len) +{ + uint64_t *counter = buf; + int i; + + if (len < sizeof(uint64_t)) { + return -EINVAL; + } + + for (i = 0; i < len; i += sizeof(uint64_t)) { + *counter = tswap64(*counter); + counter++; + } + + return len; +} + +TargetFdTrans target_eventfd_trans = { + .host_to_target_data = swap_data_eventfd, + .target_to_host_data = swap_data_eventfd, +}; + +#if (defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)) || \ + (defined(CONFIG_INOTIFY1) && defined(TARGET_NR_inotify_init1) && \ + defined(__NR_inotify_init1)) +static abi_long host_to_target_data_inotify(void *buf, size_t len) +{ + struct inotify_event *ev; + int i; + uint32_t name_len; + + for (i = 0; i < len; i += sizeof(struct inotify_event) + name_len) { + ev = (struct inotify_event *)((char *)buf + i); + name_len = ev->len; + + ev->wd = tswap32(ev->wd); + ev->mask = tswap32(ev->mask); + ev->cookie = tswap32(ev->cookie); + ev->len = tswap32(name_len); + } + + return len; +} + +TargetFdTrans target_inotify_trans = { + .host_to_target_data = host_to_target_data_inotify, +}; +#endif diff --git a/linux-user/fd-trans.h b/linux-user/fd-trans.h new file mode 100644 index 0000000000..a3fcdaabc7 --- /dev/null +++ b/linux-user/fd-trans.h @@ -0,0 +1,97 @@ +/* + * 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 FD_TRANS_H +#define FD_TRANS_H + +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; + +extern TargetFdTrans **target_fd_trans; + +extern unsigned int target_fd_max; + +static inline TargetFdDataFunc fd_trans_target_to_host_data(int fd) +{ + if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) { + return target_fd_trans[fd]->target_to_host_data; + } + return NULL; +} + +static inline 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 inline 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 inline 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 inline void fd_trans_unregister(int fd) +{ + if (fd >= 0 && fd < target_fd_max) { + target_fd_trans[fd] = NULL; + } +} + +static inline 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]); + } +} + +extern TargetFdTrans target_packet_trans; +#ifdef CONFIG_RTNETLINK +extern TargetFdTrans target_netlink_route_trans; +#endif +extern TargetFdTrans target_netlink_audit_trans; +extern TargetFdTrans target_signalfd_trans; +extern TargetFdTrans target_eventfd_trans; +#if (defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)) || \ + (defined(CONFIG_INOTIFY1) && defined(TARGET_NR_inotify_init1) && \ + defined(__NR_inotify_init1)) +extern TargetFdTrans target_inotify_trans; +#endif +#endif diff --git a/linux-user/flatload.c b/linux-user/flatload.c index 10c529910f..0122ab3afe 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -37,7 +37,7 @@ #include "qemu.h" #include "flat.h" -#include "target_flat.h" +#include //#define DEBUG @@ -771,10 +771,10 @@ int load_flt_binary(struct linux_binprm *bprm, struct image_info *info) /* Enforce final stack alignment of 16 bytes. This is sufficient for all current targets, and excess alignment is harmless. */ stack_len = bprm->envc + bprm->argc + 2; - stack_len += 3; /* argc, arvg, argp */ + stack_len += flat_argvp_envp_on_stack() ? 2 : 0; /* arvg, argp */ + stack_len += 1; /* argc */ stack_len *= sizeof(abi_ulong); - if ((sp + stack_len) & 15) - sp -= 16 - ((sp + stack_len) & 15); + sp -= (sp - stack_len) & 15; sp = loader_build_argptr(bprm->envc, bprm->argc, sp, p, flat_argvp_envp_on_stack()); diff --git a/linux-user/host/riscv32/hostdep.h b/linux-user/host/riscv32/hostdep.h new file mode 100644 index 0000000000..adf9edbf2d --- /dev/null +++ b/linux-user/host/riscv32/hostdep.h @@ -0,0 +1,11 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * 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 RISCV32_HOSTDEP_H +#define RISCV32_HOSTDEP_H + +#endif diff --git a/linux-user/host/riscv64/hostdep.h b/linux-user/host/riscv64/hostdep.h new file mode 100644 index 0000000000..865f0fb9ff --- /dev/null +++ b/linux-user/host/riscv64/hostdep.h @@ -0,0 +1,34 @@ +/* + * hostdep.h : things which are dependent on the host architecture + * + * 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 RISCV64_HOSTDEP_H +#define RISCV64_HOSTDEP_H + +/* We have a safe-syscall.inc.S */ +#define HAVE_SAFE_SYSCALL + +#ifndef __ASSEMBLER__ + +/* These are defined by the safe-syscall.inc.S file */ +extern char safe_syscall_start[]; +extern char safe_syscall_end[]; + +/* Adjust the signal context to rewind out of safe-syscall if we're in it */ +static inline void rewind_if_in_safe_syscall(void *puc) +{ + ucontext_t *uc = puc; + unsigned long *pcreg = &uc->uc_mcontext.__gregs[REG_PC]; + + if (*pcreg > (uintptr_t)safe_syscall_start + && *pcreg < (uintptr_t)safe_syscall_end) { + *pcreg = (uintptr_t)safe_syscall_start; + } +} + +#endif /* __ASSEMBLER__ */ + +#endif diff --git a/linux-user/host/riscv64/safe-syscall.inc.S b/linux-user/host/riscv64/safe-syscall.inc.S new file mode 100644 index 0000000000..9ca3fbfd1e --- /dev/null +++ b/linux-user/host/riscv64/safe-syscall.inc.S @@ -0,0 +1,77 @@ +/* + * safe-syscall.inc.S : host-specific assembly fragment + * to handle signals occurring at the same time as system calls. + * This is intended to be included by linux-user/safe-syscall.S + * + * Written by Richard Henderson + * Copyright (C) 2018 Linaro, 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. + */ + + .global safe_syscall_base + .global safe_syscall_start + .global safe_syscall_end + .type safe_syscall_base, @function + .type safe_syscall_start, @function + .type safe_syscall_end, @function + + /* + * This is the entry point for making a system call. The calling + * convention here is that of a C varargs function with the + * first argument an 'int *' to the signal_pending flag, the + * second one the system call number (as a 'long'), and all further + * arguments being syscall arguments (also 'long'). + * We return a long which is the syscall's return value, which + * may be negative-errno on failure. Conversion to the + * -1-and-errno-set convention is done by the calling wrapper. + */ +safe_syscall_base: + .cfi_startproc + /* + * The syscall calling convention is nearly the same as C: + * we enter with a0 == *signal_pending + * a1 == syscall number + * a2 ... a7 == syscall arguments + * and return the result in a0 + * and the syscall instruction needs + * a7 == syscall number + * a0 ... a5 == syscall arguments + * and returns the result in a0 + * Shuffle everything around appropriately. + */ + mv t0, a0 /* signal_pending pointer */ + mv t1, a1 /* syscall number */ + mv a0, a2 /* syscall arguments */ + mv a1, a3 + mv a2, a4 + mv a3, a5 + mv a4, a6 + mv a5, a7 + mv a7, t1 + + /* + * This next sequence of code works in conjunction with the + * rewind_if_safe_syscall_function(). If a signal is taken + * and the interrupted PC is anywhere between 'safe_syscall_start' + * and 'safe_syscall_end' then we rewind it to 'safe_syscall_start'. + * The code sequence must therefore be able to cope with this, and + * the syscall instruction must be the final one in the sequence. + */ +safe_syscall_start: + /* If signal_pending is non-zero, don't do the call */ + lw t1, 0(t0) + bnez t1, 0f + scall +safe_syscall_end: + /* code path for having successfully executed the syscall */ + ret + +0: + /* code path when we didn't execute the syscall */ + li a0, -TARGET_ERESTARTSYS + ret + .cfi_endproc + + .size safe_syscall_base, .-safe_syscall_base diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 0301c766c6..880955fdef 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -182,13 +182,10 @@ void cpu_loop(CPUHPPAState *env) queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, trapnr, QEMU_SI_FAULT, &info); - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ diff --git a/linux-user/i386/cpu_loop.c b/linux-user/i386/cpu_loop.c index 2374abfd0b..51cfa006c9 100644 --- a/linux-user/i386/cpu_loop.c +++ b/linux-user/i386/cpu_loop.c @@ -225,18 +225,10 @@ void cpu_loop(CPUX86State *env) /* just indicate that signals should be handled asap */ break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/ioctls.h b/linux-user/ioctls.h index 586c794639..ae8951625f 100644 --- a/linux-user/ioctls.h +++ b/linux-user/ioctls.h @@ -131,6 +131,52 @@ IOCTL(FS_IOC_GETFLAGS, IOC_R, MK_PTR(TYPE_INT)) IOCTL(FS_IOC_SETFLAGS, IOC_W, MK_PTR(TYPE_INT)) +#ifdef CONFIG_USBFS + /* USB ioctls */ + IOCTL(USBDEVFS_CONTROL, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ctrltransfer))) + IOCTL(USBDEVFS_BULK, IOC_RW, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_bulktransfer))) + IOCTL(USBDEVFS_RESETEP, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_SETINTERFACE, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_setinterface))) + IOCTL(USBDEVFS_SETCONFIGURATION, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_GETDRIVER, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_getdriver))) + IOCTL_SPECIAL(USBDEVFS_SUBMITURB, IOC_W, do_ioctl_usbdevfs_submiturb, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb))) + IOCTL_SPECIAL(USBDEVFS_DISCARDURB, IOC_RW, do_ioctl_usbdevfs_discardurb, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_urb))) + IOCTL_SPECIAL(USBDEVFS_REAPURB, IOC_R, do_ioctl_usbdevfs_reapurb, + MK_PTR(TYPE_PTRVOID)) + IOCTL_SPECIAL(USBDEVFS_REAPURBNDELAY, IOC_R, do_ioctl_usbdevfs_reapurb, + MK_PTR(TYPE_PTRVOID)) + IOCTL(USBDEVFS_DISCSIGNAL, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnectsignal))) + IOCTL(USBDEVFS_CLAIMINTERFACE, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_RELEASEINTERFACE, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_CONNECTINFO, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_connectinfo))) + IOCTL(USBDEVFS_IOCTL, IOC_RW, MK_PTR(MK_STRUCT(STRUCT_usbdevfs_ioctl))) + IOCTL(USBDEVFS_HUB_PORTINFO, IOC_R, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_hub_portinfo))) + IOCTL(USBDEVFS_RESET, 0, TYPE_NULL) + IOCTL(USBDEVFS_CLEAR_HALT, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_DISCONNECT, 0, TYPE_NULL) + IOCTL(USBDEVFS_CONNECT, 0, TYPE_NULL) + IOCTL(USBDEVFS_CLAIM_PORT, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_RELEASE_PORT, IOC_W, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_GET_CAPABILITIES, IOC_R, MK_PTR(TYPE_INT)) + IOCTL(USBDEVFS_DISCONNECT_CLAIM, IOC_W, + MK_PTR(MK_STRUCT(STRUCT_usbdevfs_disconnect_claim))) +#ifdef USBDEVFS_DROP_PRIVILEGES + IOCTL(USBDEVFS_DROP_PRIVILEGES, IOC_W, MK_PTR(TYPE_INT)) +#endif +#ifdef USBDEVFS_GET_SPEED + IOCTL(USBDEVFS_GET_SPEED, 0, TYPE_NULL) +#endif +#endif /* CONFIG_USBFS */ + IOCTL(SIOCATMARK, IOC_R, MK_PTR(TYPE_INT)) IOCTL(SIOCGIFNAME, IOC_RW, MK_PTR(TYPE_INT)) IOCTL(SIOCGIFFLAGS, IOC_W | IOC_R, MK_PTR(MK_STRUCT(STRUCT_short_ifreq))) diff --git a/linux-user/linuxload.c b/linux-user/linuxload.c index 6f0d6054ce..a27e1d0d8b 100644 --- a/linux-user/linuxload.c +++ b/linux-user/linuxload.c @@ -38,15 +38,15 @@ static int prepare_binprm(struct linux_binprm *bprm) int retval; if(fstat(bprm->fd, &st) < 0) { - return(-errno); + return(-errno); } mode = st.st_mode; if(!S_ISREG(mode)) { /* Must be regular file */ - return(-EACCES); + return(-EACCES); } if(!(mode & 0111)) { /* Must have at least one execute bit set */ - return(-EACCES); + return(-EACCES); } bprm->e_uid = geteuid(); @@ -54,7 +54,7 @@ static int prepare_binprm(struct linux_binprm *bprm) /* Set-uid? */ if(mode & S_ISUID) { - bprm->e_uid = st.st_uid; + bprm->e_uid = st.st_uid; } /* Set-gid? */ @@ -64,13 +64,13 @@ static int prepare_binprm(struct linux_binprm *bprm) * executable. */ if ((mode & (S_ISGID | S_IXGRP)) == (S_ISGID | S_IXGRP)) { - bprm->e_gid = st.st_gid; + bprm->e_gid = st.st_gid; } retval = read(bprm->fd, bprm->buf, BPRM_BUF_SIZE); if (retval < 0) { - perror("prepare_binprm"); - exit(-1); + perror("prepare_binprm"); + exit(-1); } if (retval < BPRM_BUF_SIZE) { /* Make sure the rest of the loader won't read garbage. */ diff --git a/linux-user/m68k/cpu_loop.c b/linux-user/m68k/cpu_loop.c index b4d3d8af3d..bfb41bbcc5 100644 --- a/linux-user/m68k/cpu_loop.c +++ b/linux-user/m68k/cpu_loop.c @@ -55,7 +55,6 @@ void cpu_loop(CPUM68KState *env) break; case EXCP_LINEA: case EXCP_LINEF: - case EXCP_UNSUPPORTED: do_sigill: info.si_signo = TARGET_SIGILL; info.si_errno = 0; @@ -113,18 +112,10 @@ void cpu_loop(CPUM68KState *env) } break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/m68k/signal.c b/linux-user/m68k/signal.c index 38bd77ec16..49ff87c77b 100644 --- a/linux-user/m68k/signal.c +++ b/linux-user/m68k/signal.c @@ -334,9 +334,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, (uint32_t *)(frame->retcode + 0)); __put_user(0x4e40, (uint16_t *)(frame->retcode + 4)); - if (err) - goto give_sigsegv; - /* Set up to return from userspace */ env->aregs[7] = frame_addr; diff --git a/linux-user/m68k/target_cpu.h b/linux-user/m68k/target_cpu.h index 611df065ca..7a26f3c3fc 100644 --- a/linux-user/m68k/target_cpu.h +++ b/linux-user/m68k/target_cpu.h @@ -7,12 +7,12 @@ * 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. + * 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 - * General Public License for more details. + * 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 . diff --git a/linux-user/m68k/target_structs.h b/linux-user/m68k/target_structs.h index a003676548..e373d481e1 100644 --- a/linux-user/m68k/target_structs.h +++ b/linux-user/m68k/target_structs.h @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/linux-user/main.c b/linux-user/main.c index ea00dd9057..a0aba9cb1e 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -126,7 +126,7 @@ void fork_end(int child) Discard information about the parent threads. */ CPU_FOREACH_SAFE(cpu, next_cpu) { if (cpu != thread_cpu) { - QTAILQ_REMOVE(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus, cpu, node); } } qemu_init_cpu_list(); @@ -740,8 +740,8 @@ int main(int argc, char **argv, char **envp) target_argc = argc - optind; target_argv = calloc(target_argc + 1, sizeof (char *)); if (target_argv == NULL) { - (void) fprintf(stderr, "Unable to allocate memory for target_argv\n"); - exit(EXIT_FAILURE); + (void) fprintf(stderr, "Unable to allocate memory for target_argv\n"); + exit(EXIT_FAILURE); } /* diff --git a/linux-user/microblaze/cpu_loop.c b/linux-user/microblaze/cpu_loop.c index 2af93eb39a..c2190e15fd 100644 --- a/linux-user/microblaze/cpu_loop.c +++ b/linux-user/microblaze/cpu_loop.c @@ -113,18 +113,10 @@ void cpu_loop(CPUMBState *env) } break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/mips/cpu_loop.c b/linux-user/mips/cpu_loop.c index 084ad6a041..61dc90d51c 100644 --- a/linux-user/mips/cpu_loop.c +++ b/linux-user/mips/cpu_loop.c @@ -392,55 +392,6 @@ static const uint8_t mips_syscall_args[] = { # undef MIPS_SYS # endif /* O32 */ -static int do_store_exclusive(CPUMIPSState *env) -{ - target_ulong addr; - target_ulong page_addr; - target_ulong val; - int flags; - int segv = 0; - int reg; - int d; - - addr = env->lladdr; - page_addr = addr & TARGET_PAGE_MASK; - start_exclusive(); - mmap_lock(); - flags = page_get_flags(page_addr); - if ((flags & PAGE_READ) == 0) { - segv = 1; - } else { - reg = env->llreg & 0x1f; - d = (env->llreg & 0x20) != 0; - if (d) { - segv = get_user_s64(val, addr); - } else { - segv = get_user_s32(val, addr); - } - if (!segv) { - if (val != env->llval) { - env->active_tc.gpr[reg] = 0; - } else { - if (d) { - segv = put_user_u64(env->llnewval, addr); - } else { - segv = put_user_u32(env->llnewval, addr); - } - if (!segv) { - env->active_tc.gpr[reg] = 1; - } - } - } - } - env->lladdr = -1; - if (!segv) { - env->active_tc.PC += 4; - } - mmap_unlock(); - end_exclusive(); - return segv; -} - /* Break codes */ enum { BRK_OVERFLOW = 6, @@ -577,27 +528,10 @@ done_syscall: /* just indicate that signals should be handled asap */ break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } - break; - case EXCP_SC: - if (do_store_exclusive(env)) { - info.si_signo = TARGET_SIGSEGV; - info.si_errno = 0; - info.si_code = TARGET_SEGV_MAPERR; - info._sifields._sigfault._addr = env->active_tc.PC; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_DSPDIS: info.si_signo = TARGET_SIGILL; @@ -725,6 +659,34 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) struct image_info *info = ts->info; int i; + struct mode_req { + bool single; + bool soft; + bool fr1; + bool frdefault; + bool fre; + }; + + static const struct mode_req fpu_reqs[] = { + [MIPS_ABI_FP_ANY] = { true, true, true, true, true }, + [MIPS_ABI_FP_DOUBLE] = { false, false, false, true, true }, + [MIPS_ABI_FP_SINGLE] = { true, false, false, false, false }, + [MIPS_ABI_FP_SOFT] = { false, true, false, false, false }, + [MIPS_ABI_FP_OLD_64] = { false, false, false, false, false }, + [MIPS_ABI_FP_XX] = { false, false, true, true, true }, + [MIPS_ABI_FP_64] = { false, false, true, false, false }, + [MIPS_ABI_FP_64A] = { false, false, true, false, true } + }; + + /* + * Mode requirements when .MIPS.abiflags is not present in the ELF. + * Not present means that everything is acceptable except FR1. + */ + static struct mode_req none_req = { true, true, false, true, true }; + + struct mode_req prog_req; + struct mode_req interp_req; + for(i = 0; i < 32; i++) { env->active_tc.gpr[i] = regs->regs[i]; } @@ -732,6 +694,56 @@ void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) if (regs->cp0_epc & 1) { env->hflags |= MIPS_HFLAG_M16; } + +#ifdef TARGET_ABI_MIPSO32 +# define MAX_FP_ABI MIPS_ABI_FP_64A +#else +# define MAX_FP_ABI MIPS_ABI_FP_SOFT +#endif + if ((info->fp_abi > MAX_FP_ABI && info->fp_abi != MIPS_ABI_FP_UNKNOWN) + || (info->interp_fp_abi > MAX_FP_ABI && + info->interp_fp_abi != MIPS_ABI_FP_UNKNOWN)) { + fprintf(stderr, "qemu: Unexpected FPU mode\n"); + exit(1); + } + + prog_req = (info->fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req + : fpu_reqs[info->fp_abi]; + interp_req = (info->interp_fp_abi == MIPS_ABI_FP_UNKNOWN) ? none_req + : fpu_reqs[info->interp_fp_abi]; + + prog_req.single &= interp_req.single; + prog_req.soft &= interp_req.soft; + prog_req.fr1 &= interp_req.fr1; + prog_req.frdefault &= interp_req.frdefault; + prog_req.fre &= interp_req.fre; + + bool cpu_has_mips_r2_r6 = env->insn_flags & ISA_MIPS32R2 || + env->insn_flags & ISA_MIPS64R2 || + env->insn_flags & ISA_MIPS32R6 || + env->insn_flags & ISA_MIPS64R6; + + if (prog_req.fre && !prog_req.frdefault && !prog_req.fr1) { + env->CP0_Config5 |= (1 << CP0C5_FRE); + if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { + env->hflags |= MIPS_HFLAG_FRE; + } + } else if ((prog_req.fr1 && prog_req.frdefault) || + (prog_req.single && !prog_req.frdefault)) { + if ((env->active_fpu.fcr0 & (1 << FCR0_F64) + && cpu_has_mips_r2_r6) || prog_req.fr1) { + env->CP0_Status |= (1 << CP0St_FR); + env->hflags |= MIPS_HFLAG_F64; + } + } else if (!prog_req.fre && !prog_req.frdefault && + !prog_req.fr1 && !prog_req.single && !prog_req.soft) { + fprintf(stderr, "qemu: Can't find a matching FPU mode\n"); + exit(1); + } + + if (env->insn_flags & ISA_NANOMIPS32) { + return; + } if (((info->elf_flags & EF_MIPS_NAN2008) != 0) != ((env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) != 0)) { if ((env->active_fpu.fcr31_rw_bitmask & diff --git a/linux-user/mips/syscall_nr.h b/linux-user/mips/syscall_nr.h index ced32806ea..e70adfc2fe 100644 --- a/linux-user/mips/syscall_nr.h +++ b/linux-user/mips/syscall_nr.h @@ -363,3 +363,12 @@ #define TARGET_NR_userfaultfd (TARGET_NR_Linux + 357) #define TARGET_NR_membarrier (TARGET_NR_Linux + 358) #define TARGET_NR_mlock2 (TARGET_NR_Linux + 359) +#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 360) +#define TARGET_NR_preadv2 (TARGET_NR_Linux + 361) +#define TARGET_NR_pwritev2 (TARGET_NR_Linux + 362) +#define TARGET_NR_pkey_mprotect (TARGET_NR_Linux + 363) +#define TARGET_NR_pkey_alloc (TARGET_NR_Linux + 364) +#define TARGET_NR_pkey_free (TARGET_NR_Linux + 365) +#define TARGET_NR_statx (TARGET_NR_Linux + 366) +#define TARGET_NR_rseq (TARGET_NR_Linux + 367) +#define TARGET_NR_io_pgetevents (TARGET_NR_Linux + 368) diff --git a/linux-user/mips/target_elf.h b/linux-user/mips/target_elf.h index fa5d30bf99..a98c9bd6ad 100644 --- a/linux-user/mips/target_elf.h +++ b/linux-user/mips/target_elf.h @@ -12,6 +12,9 @@ static inline const char *cpu_get_model(uint32_t eflags) if ((eflags & EF_MIPS_ARCH) == EF_MIPS_ARCH_32R6) { return "mips32r6-generic"; } + if ((eflags & EF_MIPS_MACH) == EF_MIPS_MACH_5900) { + return "R5900"; + } return "24Kf"; } #endif diff --git a/linux-user/mips/target_syscall.h b/linux-user/mips/target_syscall.h index 2fca1c6bf9..d5509a34a7 100644 --- a/linux-user/mips/target_syscall.h +++ b/linux-user/mips/target_syscall.h @@ -244,4 +244,10 @@ static inline abi_ulong target_shmlba(CPUMIPSState *env) return 0x40000; } +/* MIPS-specific prctl() options */ +#define TARGET_PR_SET_FP_MODE 45 +#define TARGET_PR_GET_FP_MODE 46 +#define TARGET_PR_FP_MODE_FR (1 << 0) +#define TARGET_PR_FP_MODE_FRE (1 << 1) + #endif /* MIPS_TARGET_SYSCALL_H */ diff --git a/linux-user/mips64/syscall_nr.h b/linux-user/mips64/syscall_nr.h index 746cc267e9..ff218a9bf2 100644 --- a/linux-user/mips64/syscall_nr.h +++ b/linux-user/mips64/syscall_nr.h @@ -327,6 +327,15 @@ #define TARGET_NR_userfaultfd (TARGET_NR_Linux + 321) #define TARGET_NR_membarrier (TARGET_NR_Linux + 322) #define TARGET_NR_mlock2 (TARGET_NR_Linux + 323) +#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 324) +#define TARGET_NR_preadv2 (TARGET_NR_Linux + 325) +#define TARGET_NR_pwritev2 (TARGET_NR_Linux + 326) +#define TARGET_NR_pkey_mprotect (TARGET_NR_Linux + 327) +#define TARGET_NR_pkey_alloc (TARGET_NR_Linux + 328) +#define TARGET_NR_pkey_free (TARGET_NR_Linux + 329) +#define TARGET_NR_statx (TARGET_NR_Linux + 330) +#define TARGET_NR_rseq (TARGET_NR_Linux + 331) +#define TARGET_NR_io_pgetevents (TARGET_NR_Linux + 332) #else /* @@ -653,4 +662,13 @@ #define TARGET_NR_userfaultfd (TARGET_NR_Linux + 317) #define TARGET_NR_membarrier (TARGET_NR_Linux + 318) #define TARGET_NR_mlock2 (TARGET_NR_Linux + 319) +#define TARGET_NR_copy_file_range (TARGET_NR_Linux + 320) +#define TARGET_NR_preadv2 (TARGET_NR_Linux + 321) +#define TARGET_NR_pwritev2 (TARGET_NR_Linux + 322) +#define TARGET_NR_pkey_mprotect (TARGET_NR_Linux + 323) +#define TARGET_NR_pkey_alloc (TARGET_NR_Linux + 324) +#define TARGET_NR_pkey_free (TARGET_NR_Linux + 325) +#define TARGET_NR_statx (TARGET_NR_Linux + 326) +#define TARGET_NR_rseq (TARGET_NR_Linux + 327) +#define TARGET_NR_io_pgetevents (TARGET_NR_Linux + 328) #endif diff --git a/linux-user/mips64/target_syscall.h b/linux-user/mips64/target_syscall.h index 078437d765..8ccc46822c 100644 --- a/linux-user/mips64/target_syscall.h +++ b/linux-user/mips64/target_syscall.h @@ -241,4 +241,10 @@ static inline abi_ulong target_shmlba(CPUMIPSState *env) return 0x40000; } +/* MIPS-specific prctl() options */ +#define TARGET_PR_SET_FP_MODE 45 +#define TARGET_PR_GET_FP_MODE 46 +#define TARGET_PR_FP_MODE_FR (1 << 0) +#define TARGET_PR_FP_MODE_FRE (1 << 1) + #endif /* MIPS64_TARGET_SYSCALL_H */ diff --git a/linux-user/mmap.c b/linux-user/mmap.c index 41e0983ce8..e0249efe4f 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -485,11 +485,11 @@ abi_long target_mmap(abi_ulong start, abi_ulong len, int prot, end = start + len; real_end = HOST_PAGE_ALIGN(end); - /* - * Test if requested memory area fits target address space - * It can fail only on 64-bit host with 32-bit target. - * On any other target/host host mmap() handles this error correctly. - */ + /* + * Test if requested memory area fits target address space + * It can fail only on 64-bit host with 32-bit target. + * On any other target/host host mmap() handles this error correctly. + */ if (!guest_range_valid(start, len)) { errno = ENOMEM; goto fail; diff --git a/linux-user/nios2/cpu_loop.c b/linux-user/nios2/cpu_loop.c index dac7a06181..5aa1eca740 100644 --- a/linux-user/nios2/cpu_loop.c +++ b/linux-user/nios2/cpu_loop.c @@ -26,13 +26,12 @@ void cpu_loop(CPUNios2State *env) CPUState *cs = ENV_GET_CPU(env); Nios2CPU *cpu = NIOS2_CPU(cs); target_siginfo_t info; - int trapnr, gdbsig, ret; + int trapnr, ret; for (;;) { cpu_exec_start(cs); trapnr = cpu_exec(cs); cpu_exec_end(cs); - gdbsig = 0; switch (trapnr) { case EXCP_INTERRUPT: @@ -68,9 +67,18 @@ void cpu_loop(CPUNios2State *env) env->regs[R_EA] = env->regs[R_PC] + 4; env->regs[R_PC] = cpu->exception_addr; - gdbsig = TARGET_SIGTRAP; + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; } + case EXCP_DEBUG: + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); + break; case 0xaa: switch (env->regs[R_PC]) { /*case 0x1000:*/ /* TODO:__kuser_helper_version */ @@ -106,14 +114,7 @@ kuser_fail: default: EXCP_DUMP(env, "\nqemu: unhandled CPU exception %#x - aborting\n", trapnr); - gdbsig = TARGET_SIGILL; - break; - } - if (gdbsig) { - gdb_handlesig(cs, gdbsig); - if (gdbsig != TARGET_SIGTRAP) { - exit(EXIT_FAILURE); - } + abort(); } process_pending_signals(env); diff --git a/linux-user/openrisc/cpu_loop.c b/linux-user/openrisc/cpu_loop.c index 6c6ea871e1..f496e4b48a 100644 --- a/linux-user/openrisc/cpu_loop.c +++ b/linux-user/openrisc/cpu_loop.c @@ -85,13 +85,10 @@ void cpu_loop(CPUOpenRISCState *env) /* We processed the pending cpu work above. */ break; case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/ppc/cpu_loop.c b/linux-user/ppc/cpu_loop.c index 133a87f349..801f5ace29 100644 --- a/linux-user/ppc/cpu_loop.c +++ b/linux-user/ppc/cpu_loop.c @@ -69,7 +69,7 @@ void cpu_loop(CPUPPCState *env) { CPUState *cs = CPU(ppc_env_get_cpu(env)); target_siginfo_t info; - int trapnr, sig; + int trapnr; target_ulong ret; for(;;) { @@ -449,15 +449,10 @@ void cpu_loop(CPUPPCState *env) env->gpr[3] = ret; break; case EXCP_DEBUG: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ diff --git a/linux-user/ppc/signal.c b/linux-user/ppc/signal.c index 2ae120a2bc..619a56950d 100644 --- a/linux-user/ppc/signal.c +++ b/linux-user/ppc/signal.c @@ -258,8 +258,8 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) /* Save Altivec registers if necessary. */ if (env->insns_flags & PPC_ALTIVEC) { uint32_t *vrsave; - for (i = 0; i < ARRAY_SIZE(env->avr); i++) { - ppc_avr_t *avr = &env->avr[i]; + for (i = 0; i < 32; i++) { + ppc_avr_t *avr = cpu_avr_ptr(env, i); ppc_avr_t *vreg = (ppc_avr_t *)&frame->mc_vregs.altivec[i]; __put_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); @@ -281,15 +281,17 @@ static void save_user_regs(CPUPPCState *env, struct target_mcontext *frame) /* Save VSX second halves */ if (env->insns_flags2 & PPC2_VSX) { uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; - for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { - __put_user(env->vsr[i], &vsregs[i]); + for (i = 0; i < 32; i++) { + uint64_t *vsrl = cpu_vsrl_ptr(env, i); + __put_user(*vsrl, &vsregs[i]); } } /* Save floating point registers. */ if (env->insns_flags & PPC_FLOAT) { - for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { - __put_user(env->fpr[i], &frame->mc_fregs[i]); + for (i = 0; i < 32; i++) { + uint64_t *fpr = cpu_fpr_ptr(env, i); + __put_user(*fpr, &frame->mc_fregs[i]); } __put_user((uint64_t) env->fpscr, &frame->mc_fregs[32]); } @@ -373,8 +375,8 @@ static void restore_user_regs(CPUPPCState *env, #else v_regs = (ppc_avr_t *)frame->mc_vregs.altivec; #endif - for (i = 0; i < ARRAY_SIZE(env->avr); i++) { - ppc_avr_t *avr = &env->avr[i]; + for (i = 0; i < 32; i++) { + ppc_avr_t *avr = cpu_avr_ptr(env, i); ppc_avr_t *vreg = &v_regs[i]; __get_user(avr->u64[PPC_VEC_HI], &vreg->u64[0]); @@ -393,16 +395,18 @@ static void restore_user_regs(CPUPPCState *env, /* Restore VSX second halves */ if (env->insns_flags2 & PPC2_VSX) { uint64_t *vsregs = (uint64_t *)&frame->mc_vregs.altivec[34]; - for (i = 0; i < ARRAY_SIZE(env->vsr); i++) { - __get_user(env->vsr[i], &vsregs[i]); + for (i = 0; i < 32; i++) { + uint64_t *vsrl = cpu_vsrl_ptr(env, i); + __get_user(*vsrl, &vsregs[i]); } } /* Restore floating point registers. */ if (env->insns_flags & PPC_FLOAT) { uint64_t fpscr; - for (i = 0; i < ARRAY_SIZE(env->fpr); i++) { - __get_user(env->fpr[i], &frame->mc_fregs[i]); + for (i = 0; i < 32; i++) { + uint64_t *fpr = cpu_fpr_ptr(env, i); + __get_user(*fpr, &frame->mc_fregs[i]); } __get_user(fpscr, &frame->mc_fregs[32]); env->fpscr = (uint32_t) fpscr; diff --git a/linux-user/qemu.h b/linux-user/qemu.h index b4959e41c6..ef400cb78a 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -50,7 +50,7 @@ struct image_info { abi_ulong env_strings; abi_ulong file_string; uint32_t elf_flags; - int personality; + int personality; abi_ulong alignment; /* The fields below are used in FDPIC mode. */ @@ -61,6 +61,10 @@ struct image_info { abi_ulong interpreter_loadmap_addr; abi_ulong interpreter_pt_dynamic_addr; struct image_info *other_info; +#ifdef TARGET_MIPS + int fp_abi; + int interp_fp_abi; +#endif }; #ifdef TARGET_I386 @@ -143,7 +147,7 @@ typedef struct TaskState { /* Nonzero if process_pending_signals() needs to do something (either * handle a pending signal or unblock signals). * This flag is written from a signal handler so should be accessed via - * the atomic_read() and atomic_write() functions. (It is not accessed + * the atomic_read() and atomic_set() functions. (It is not accessed * from multiple threads.) */ int signal_pending; @@ -170,7 +174,7 @@ extern unsigned long mmap_min_addr; struct linux_binprm { char buf[BPRM_BUF_SIZE] __attribute__((aligned)); abi_ulong p; - int fd; + int fd; int e_uid, e_gid; int argc, envc; char **argv; @@ -461,27 +465,55 @@ static inline int access_ok(int type, abi_ulong addr, abi_ulong size) These are usually used to access struct data members once the struct has been locked - usually with lock_user_struct. */ -/* Tricky points: - - Use __builtin_choose_expr to avoid type promotion from ?:, - - Invalid sizes result in a compile time error stemming from - the fact that abort has no parameters. - - It's easier to use the endian-specific unaligned load/store - functions than host-endian unaligned load/store plus tswapN. */ +/* + * Tricky points: + * - Use __builtin_choose_expr to avoid type promotion from ?:, + * - Invalid sizes result in a compile time error stemming from + * the fact that abort has no parameters. + * - It's easier to use the endian-specific unaligned load/store + * functions than host-endian unaligned load/store plus tswapN. + * - The pragmas are necessary only to silence a clang false-positive + * warning: see https://bugs.llvm.org/show_bug.cgi?id=39113 . + * - gcc has bugs in its _Pragma() support in some versions, eg + * https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83256 -- so we only + * include the warning-suppression pragmas for clang + */ +#if defined(__clang__) && __has_warning("-Waddress-of-packed-member") +#define PRAGMA_DISABLE_PACKED_WARNING \ + _Pragma("GCC diagnostic push"); \ + _Pragma("GCC diagnostic ignored \"-Waddress-of-packed-member\"") -#define __put_user_e(x, hptr, e) \ - (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \ - ((hptr), (x)), (void)0) +#define PRAGMA_REENABLE_PACKED_WARNING \ + _Pragma("GCC diagnostic pop") + +#else +#define PRAGMA_DISABLE_PACKED_WARNING +#define PRAGMA_REENABLE_PACKED_WARNING +#endif + +#define __put_user_e(x, hptr, e) \ + do { \ + PRAGMA_DISABLE_PACKED_WARNING; \ + (__builtin_choose_expr(sizeof(*(hptr)) == 1, stb_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, stw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, stl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, stq_##e##_p, abort)))) \ + ((hptr), (x)), (void)0); \ + PRAGMA_REENABLE_PACKED_WARNING; \ + } while (0) + +#define __get_user_e(x, hptr, e) \ + do { \ + PRAGMA_DISABLE_PACKED_WARNING; \ + ((x) = (typeof(*hptr))( \ + __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ + __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ + (hptr)), (void)0); \ + PRAGMA_REENABLE_PACKED_WARNING; \ + } while (0) -#define __get_user_e(x, hptr, e) \ - ((x) = (typeof(*hptr))( \ - __builtin_choose_expr(sizeof(*(hptr)) == 1, ldub_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 2, lduw_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 4, ldl_##e##_p, \ - __builtin_choose_expr(sizeof(*(hptr)) == 8, ldq_##e##_p, abort)))) \ - (hptr)), (void)0) #ifdef TARGET_WORDS_BIGENDIAN # define __put_user(x, hptr) __put_user_e(x, hptr, be) diff --git a/linux-user/riscv/cpu_loop.c b/linux-user/riscv/cpu_loop.c index f137d39d7e..a9bac4ca79 100644 --- a/linux-user/riscv/cpu_loop.c +++ b/linux-user/riscv/cpu_loop.c @@ -18,8 +18,10 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu.h" #include "cpu_loop-common.h" +#include "elf.h" void cpu_loop(CPURISCVState *env) { @@ -53,7 +55,8 @@ void cpu_loop(CPURISCVState *env) ret = 0; } else { ret = do_syscall(env, - env->gpr[xA7], + env->gpr[(env->elf_flags & EF_RISCV_RVE) + ? xT0 : xA7], env->gpr[xA0], env->gpr[xA1], env->gpr[xA2], @@ -88,7 +91,7 @@ void cpu_loop(CPURISCVState *env) break; case EXCP_DEBUG: gdbstep: - signum = gdb_handlesig(cs, TARGET_SIGTRAP); + signum = TARGET_SIGTRAP; sigcode = TARGET_TRAP_BRKPT; break; default: @@ -113,6 +116,16 @@ void cpu_loop(CPURISCVState *env) void target_cpu_copy_regs(CPUArchState *env, struct target_pt_regs *regs) { + CPUState *cpu = ENV_GET_CPU(env); + TaskState *ts = cpu->opaque; + struct image_info *info = ts->info; + env->pc = regs->sepc; env->gpr[xSP] = regs->sp; + env->elf_flags = info->elf_flags; + + if ((env->misa & RVE) && !(env->elf_flags & EF_RISCV_RVE)) { + error_report("Incompatible ELF: RVE cpu requires RVE ABI binary"); + exit(EXIT_FAILURE); + } } diff --git a/linux-user/riscv/signal.c b/linux-user/riscv/signal.c index f598d41891..83ecc6f799 100644 --- a/linux-user/riscv/signal.c +++ b/linux-user/riscv/signal.c @@ -83,7 +83,7 @@ static void setup_sigcontext(struct target_sigcontext *sc, CPURISCVState *env) __put_user(env->fpr[i], &sc->fpr[i]); } - uint32_t fcsr = csr_read_helper(env, CSR_FCSR); /*riscv_get_fcsr(env);*/ + uint32_t fcsr = riscv_csr_read(env, CSR_FCSR); __put_user(fcsr, &sc->fcsr); } @@ -159,7 +159,7 @@ static void restore_sigcontext(CPURISCVState *env, struct target_sigcontext *sc) uint32_t fcsr; __get_user(fcsr, &sc->fcsr); - csr_write_helper(env, fcsr, CSR_FCSR); + riscv_csr_write(env, CSR_FCSR, fcsr); } static void restore_ucontext(CPURISCVState *env, struct target_ucontext *uc) diff --git a/linux-user/s390x/cpu_loop.c b/linux-user/s390x/cpu_loop.c index 99f5f1594f..51b5412ea2 100644 --- a/linux-user/s390x/cpu_loop.c +++ b/linux-user/s390x/cpu_loop.c @@ -61,12 +61,9 @@ void cpu_loop(CPUS390XState *env) break; case EXCP_DEBUG: - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - n = TARGET_TRAP_BRKPT; - goto do_signal_pc; - } - break; + sig = TARGET_SIGTRAP; + n = TARGET_TRAP_BRKPT; + goto do_signal_pc; case EXCP_PGM: n = env->int_pgm_code; switch (n) { diff --git a/linux-user/s390x/target_cpu.h b/linux-user/s390x/target_cpu.h index 66ef8aa8c2..aa181ceaee 100644 --- a/linux-user/s390x/target_cpu.h +++ b/linux-user/s390x/target_cpu.h @@ -3,21 +3,18 @@ * * Copyright (c) 2009 Ulrich Hecht * - * 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 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * Contributions after 2012-10-29 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - * - * You should have received a copy of the GNU (Lesser) General Public - * License along with this library; if not, see . + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . */ #ifndef S390X_TARGET_CPU_H #define S390X_TARGET_CPU_H diff --git a/linux-user/sh4/cpu_loop.c b/linux-user/sh4/cpu_loop.c index fdd348170b..47e54b9b61 100644 --- a/linux-user/sh4/cpu_loop.c +++ b/linux-user/sh4/cpu_loop.c @@ -57,19 +57,10 @@ void cpu_loop(CPUSH4State *env) /* just indicate that signals should be handled asap */ break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } else { - arch_interrupt = false; - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case 0xa0: case 0xc0: diff --git a/linux-user/sh4/signal.c b/linux-user/sh4/signal.c index c6752baa7e..cc89a48ff8 100644 --- a/linux-user/sh4/signal.c +++ b/linux-user/sh4/signal.c @@ -279,7 +279,6 @@ long do_sigreturn(CPUSH4State *regs) sigset_t blocked; target_sigset_t target_set; int i; - int err = 0; frame_addr = regs->gregs[15]; trace_user_do_sigreturn(regs, frame_addr); @@ -292,9 +291,6 @@ long do_sigreturn(CPUSH4State *regs) __get_user(target_set.sig[i], &frame->extramask[i - 1]); } - if (err) - goto badframe; - target_to_host_sigset_internal(&blocked, &target_set); set_sigmask(&blocked); diff --git a/linux-user/signal.c b/linux-user/signal.c index 602b631b92..e2c0b37173 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -727,7 +727,7 @@ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp) } #endif - ret = -TARGET_EFAULT; + ret = -TARGET_EFAULT; if (!lock_user_struct(VERIFY_READ, uss, uss_addr, 1)) { goto out; } @@ -736,25 +736,25 @@ abi_long do_sigaltstack(abi_ulong uss_addr, abi_ulong uoss_addr, abi_ulong sp) __get_user(ss.ss_flags, &uss->ss_flags); unlock_user_struct(uss, uss_addr, 0); - ret = -TARGET_EPERM; - if (on_sig_stack(sp)) + ret = -TARGET_EPERM; + if (on_sig_stack(sp)) goto out; - ret = -TARGET_EINVAL; - if (ss.ss_flags != TARGET_SS_DISABLE + ret = -TARGET_EINVAL; + if (ss.ss_flags != TARGET_SS_DISABLE && ss.ss_flags != TARGET_SS_ONSTACK && ss.ss_flags != 0) goto out; - if (ss.ss_flags == TARGET_SS_DISABLE) { + if (ss.ss_flags == TARGET_SS_DISABLE) { ss.ss_size = 0; ss.ss_sp = 0; - } else { + } else { ret = -TARGET_ENOMEM; if (ss.ss_size < minstacksize) { goto out; } - } + } target_sigaltstack_used.ss_sp = ss.ss_sp; target_sigaltstack_used.ss_size = ss.ss_size; diff --git a/linux-user/sparc/cpu_loop.c b/linux-user/sparc/cpu_loop.c index 91f714afc6..7d5b337b97 100644 --- a/linux-user/sparc/cpu_loop.c +++ b/linux-user/sparc/cpu_loop.c @@ -268,18 +268,10 @@ void cpu_loop (CPUSPARCState *env) } break; case EXCP_DEBUG: - { - int sig; - - sig = gdb_handlesig(cs, TARGET_SIGTRAP); - if (sig) - { - info.si_signo = sig; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); - } - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXCP_ATOMIC: cpu_exec_step_atomic(cs); diff --git a/linux-user/sparc/signal.c b/linux-user/sparc/signal.c index b4c60aa446..ead169fbaa 100644 --- a/linux-user/sparc/signal.c +++ b/linux-user/sparc/signal.c @@ -256,12 +256,6 @@ void setup_frame(int sig, struct target_sigaction *ka, /* t 0x10 */ val32 = 0x91d02010; __put_user(val32, &sf->insns[1]); - if (err) - goto sigsegv; - - /* Flush instruction space. */ - // flush_sig_insns(current->mm, (unsigned long) &(sf->insns[0])); - // tb_flush(env); } unlock_user(sf, sf_addr, sizeof(struct target_signal_frame)); return; @@ -288,7 +282,7 @@ long do_sigreturn(CPUSPARCState *env) uint32_t up_psr, pc, npc; target_sigset_t set; sigset_t host_set; - int err=0, i; + int i; sf_addr = env->regwptr[UREG_FP]; trace_user_do_sigreturn(env, sf_addr); @@ -326,10 +320,13 @@ long do_sigreturn(CPUSPARCState *env) } /* FIXME: implement FPU save/restore: - * __get_user(fpu_save, &sf->fpu_save); - * if (fpu_save) - * err |= restore_fpu_state(env, fpu_save); - */ + * __get_user(fpu_save, &sf->fpu_save); + * if (fpu_save) { + * if (restore_fpu_state(env, fpu_save)) { + * goto segv_and_exit; + * } + * } + */ /* This is pretty much atomic, no amount locking would prevent * the races which exist anyways. @@ -342,9 +339,6 @@ long do_sigreturn(CPUSPARCState *env) target_to_host_sigset_internal(&host_set, &set); set_sigmask(&host_set); - if (err) { - goto segv_and_exit; - } unlock_user_struct(sf, sf_addr, 0); return -TARGET_QEMU_ESIGRETURN; diff --git a/linux-user/strace.c b/linux-user/strace.c index bd897a3f20..6f72a74c09 100644 --- a/linux-user/strace.c +++ b/linux-user/strace.c @@ -647,11 +647,11 @@ print_execve(const struct syscallname *name, for (arg_ptr_addr = arg2; ; arg_ptr_addr += sizeof(abi_ulong)) { abi_ulong *arg_ptr, arg_addr; - arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1); + arg_ptr = lock_user(VERIFY_READ, arg_ptr_addr, sizeof(abi_ulong), 1); if (!arg_ptr) return; arg_addr = tswapal(*arg_ptr); - unlock_user(arg_ptr, arg_ptr_addr, 0); + unlock_user(arg_ptr, arg_ptr_addr, 0); if (!arg_addr) break; if ((s = lock_user_string(arg_addr))) { @@ -1235,6 +1235,18 @@ print_chdir(const struct syscallname *name, } #endif +#ifdef TARGET_NR_chroot +static void +print_chroot(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 1); + print_syscall_epilogue(name); +} +#endif + #ifdef TARGET_NR_chmod static void print_chmod(const struct syscallname *name, @@ -1742,6 +1754,9 @@ print_optint: case TARGET_SO_REUSEADDR: gemu_log("SO_REUSEADDR,"); goto print_optint; + case TARGET_SO_REUSEPORT: + gemu_log("SO_REUSEPORT,"); + goto print_optint; case TARGET_SO_TYPE: gemu_log("SO_TYPE,"); goto print_optint; @@ -2304,7 +2319,19 @@ print_statfs(const struct syscallname *name, print_pointer(arg1, 1); print_syscall_epilogue(name); } -#define print_statfs64 print_statfs +#endif + +#ifdef TARGET_NR_statfs64 +static void +print_statfs64(const struct syscallname *name, + abi_long arg0, abi_long arg1, abi_long arg2, + abi_long arg3, abi_long arg4, abi_long arg5) +{ + print_syscall_prologue(name); + print_string(arg0, 0); + print_pointer(arg1, 1); + print_syscall_epilogue(name); +} #endif #ifdef TARGET_NR_symlink diff --git a/linux-user/strace.list b/linux-user/strace.list index ff8bb19f5f..db21ce4177 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -77,7 +77,7 @@ { TARGET_NR_chown32, "chown32" , NULL, NULL, NULL }, #endif #ifdef TARGET_NR_chroot -{ TARGET_NR_chroot, "chroot" , NULL, NULL, NULL }, +{ TARGET_NR_chroot, "chroot" , NULL, print_chroot, NULL }, #endif #ifdef TARGET_NR_clock_adjtime { TARGET_NR_clock_adjtime, "clock_adjtime" , NULL, print_clock_adjtime, NULL }, diff --git a/linux-user/syscall.c b/linux-user/syscall.c index dfc851cc35..96cd4bf86d 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -94,6 +94,10 @@ #include #endif #include +#if defined(CONFIG_USBFS) +#include +#include +#endif #include #include #include @@ -102,15 +106,11 @@ #include #include #include -#ifdef CONFIG_RTNETLINK -#include -#include -#endif -#include #include "linux_loop.h" #include "uname.h" #include "qemu.h" +#include "fd-trans.h" #ifndef CLONE_IO #define CLONE_IO 0x80000000 /* Clone io context */ @@ -163,7 +163,6 @@ * (The one remaining unallocated bit is 0x1000 which used to be CLONE_PID.) */ -//#define DEBUG /* Define DEBUG_ERESTARTSYS to force every syscall to be restarted * once. This exercises the codepaths for restart. */ @@ -250,15 +249,8 @@ static type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, \ #define TARGET_NR__llseek TARGET_NR_llseek #endif -#ifdef __NR_gettid -_syscall0(int, gettid) -#else -/* This is a replacement for the host gettid() and must return a host - errno. */ -static int gettid(void) { - return -ENOSYS; -} -#endif +#define __NR_sys_gettid __NR_gettid +_syscall0(int, sys_gettid) /* For the 64-bit guest on 32-bit host case we must emulate * getdents using getdents64, because otherwise the host @@ -361,250 +353,6 @@ static bitmask_transtbl fcntl_flags_tbl[] = { { 0, 0, 0, 0 } }; -enum { - QEMU_IFLA_BR_UNSPEC, - QEMU_IFLA_BR_FORWARD_DELAY, - QEMU_IFLA_BR_HELLO_TIME, - QEMU_IFLA_BR_MAX_AGE, - QEMU_IFLA_BR_AGEING_TIME, - QEMU_IFLA_BR_STP_STATE, - QEMU_IFLA_BR_PRIORITY, - QEMU_IFLA_BR_VLAN_FILTERING, - QEMU_IFLA_BR_VLAN_PROTOCOL, - QEMU_IFLA_BR_GROUP_FWD_MASK, - QEMU_IFLA_BR_ROOT_ID, - QEMU_IFLA_BR_BRIDGE_ID, - QEMU_IFLA_BR_ROOT_PORT, - QEMU_IFLA_BR_ROOT_PATH_COST, - QEMU_IFLA_BR_TOPOLOGY_CHANGE, - QEMU_IFLA_BR_TOPOLOGY_CHANGE_DETECTED, - QEMU_IFLA_BR_HELLO_TIMER, - QEMU_IFLA_BR_TCN_TIMER, - QEMU_IFLA_BR_TOPOLOGY_CHANGE_TIMER, - QEMU_IFLA_BR_GC_TIMER, - QEMU_IFLA_BR_GROUP_ADDR, - QEMU_IFLA_BR_FDB_FLUSH, - QEMU_IFLA_BR_MCAST_ROUTER, - QEMU_IFLA_BR_MCAST_SNOOPING, - QEMU_IFLA_BR_MCAST_QUERY_USE_IFADDR, - QEMU_IFLA_BR_MCAST_QUERIER, - QEMU_IFLA_BR_MCAST_HASH_ELASTICITY, - QEMU_IFLA_BR_MCAST_HASH_MAX, - QEMU_IFLA_BR_MCAST_LAST_MEMBER_CNT, - QEMU_IFLA_BR_MCAST_STARTUP_QUERY_CNT, - QEMU_IFLA_BR_MCAST_LAST_MEMBER_INTVL, - QEMU_IFLA_BR_MCAST_MEMBERSHIP_INTVL, - QEMU_IFLA_BR_MCAST_QUERIER_INTVL, - QEMU_IFLA_BR_MCAST_QUERY_INTVL, - QEMU_IFLA_BR_MCAST_QUERY_RESPONSE_INTVL, - QEMU_IFLA_BR_MCAST_STARTUP_QUERY_INTVL, - QEMU_IFLA_BR_NF_CALL_IPTABLES, - QEMU_IFLA_BR_NF_CALL_IP6TABLES, - QEMU_IFLA_BR_NF_CALL_ARPTABLES, - QEMU_IFLA_BR_VLAN_DEFAULT_PVID, - QEMU_IFLA_BR_PAD, - QEMU_IFLA_BR_VLAN_STATS_ENABLED, - QEMU_IFLA_BR_MCAST_STATS_ENABLED, - QEMU_IFLA_BR_MCAST_IGMP_VERSION, - QEMU_IFLA_BR_MCAST_MLD_VERSION, - QEMU___IFLA_BR_MAX, -}; - -enum { - QEMU_IFLA_UNSPEC, - QEMU_IFLA_ADDRESS, - QEMU_IFLA_BROADCAST, - QEMU_IFLA_IFNAME, - QEMU_IFLA_MTU, - QEMU_IFLA_LINK, - QEMU_IFLA_QDISC, - QEMU_IFLA_STATS, - QEMU_IFLA_COST, - QEMU_IFLA_PRIORITY, - QEMU_IFLA_MASTER, - QEMU_IFLA_WIRELESS, - QEMU_IFLA_PROTINFO, - QEMU_IFLA_TXQLEN, - QEMU_IFLA_MAP, - QEMU_IFLA_WEIGHT, - QEMU_IFLA_OPERSTATE, - QEMU_IFLA_LINKMODE, - QEMU_IFLA_LINKINFO, - QEMU_IFLA_NET_NS_PID, - QEMU_IFLA_IFALIAS, - QEMU_IFLA_NUM_VF, - QEMU_IFLA_VFINFO_LIST, - QEMU_IFLA_STATS64, - QEMU_IFLA_VF_PORTS, - QEMU_IFLA_PORT_SELF, - QEMU_IFLA_AF_SPEC, - QEMU_IFLA_GROUP, - QEMU_IFLA_NET_NS_FD, - QEMU_IFLA_EXT_MASK, - QEMU_IFLA_PROMISCUITY, - QEMU_IFLA_NUM_TX_QUEUES, - QEMU_IFLA_NUM_RX_QUEUES, - QEMU_IFLA_CARRIER, - QEMU_IFLA_PHYS_PORT_ID, - QEMU_IFLA_CARRIER_CHANGES, - QEMU_IFLA_PHYS_SWITCH_ID, - QEMU_IFLA_LINK_NETNSID, - QEMU_IFLA_PHYS_PORT_NAME, - QEMU_IFLA_PROTO_DOWN, - QEMU_IFLA_GSO_MAX_SEGS, - QEMU_IFLA_GSO_MAX_SIZE, - QEMU_IFLA_PAD, - QEMU_IFLA_XDP, - QEMU_IFLA_EVENT, - QEMU_IFLA_NEW_NETNSID, - QEMU_IFLA_IF_NETNSID, - QEMU_IFLA_CARRIER_UP_COUNT, - QEMU_IFLA_CARRIER_DOWN_COUNT, - QEMU_IFLA_NEW_IFINDEX, - QEMU___IFLA_MAX -}; - -enum { - QEMU_IFLA_BRPORT_UNSPEC, - QEMU_IFLA_BRPORT_STATE, - QEMU_IFLA_BRPORT_PRIORITY, - QEMU_IFLA_BRPORT_COST, - QEMU_IFLA_BRPORT_MODE, - QEMU_IFLA_BRPORT_GUARD, - QEMU_IFLA_BRPORT_PROTECT, - QEMU_IFLA_BRPORT_FAST_LEAVE, - QEMU_IFLA_BRPORT_LEARNING, - QEMU_IFLA_BRPORT_UNICAST_FLOOD, - QEMU_IFLA_BRPORT_PROXYARP, - QEMU_IFLA_BRPORT_LEARNING_SYNC, - QEMU_IFLA_BRPORT_PROXYARP_WIFI, - QEMU_IFLA_BRPORT_ROOT_ID, - QEMU_IFLA_BRPORT_BRIDGE_ID, - QEMU_IFLA_BRPORT_DESIGNATED_PORT, - QEMU_IFLA_BRPORT_DESIGNATED_COST, - QEMU_IFLA_BRPORT_ID, - QEMU_IFLA_BRPORT_NO, - QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK, - QEMU_IFLA_BRPORT_CONFIG_PENDING, - QEMU_IFLA_BRPORT_MESSAGE_AGE_TIMER, - QEMU_IFLA_BRPORT_FORWARD_DELAY_TIMER, - QEMU_IFLA_BRPORT_HOLD_TIMER, - QEMU_IFLA_BRPORT_FLUSH, - QEMU_IFLA_BRPORT_MULTICAST_ROUTER, - QEMU_IFLA_BRPORT_PAD, - QEMU_IFLA_BRPORT_MCAST_FLOOD, - QEMU_IFLA_BRPORT_MCAST_TO_UCAST, - QEMU_IFLA_BRPORT_VLAN_TUNNEL, - QEMU_IFLA_BRPORT_BCAST_FLOOD, - QEMU_IFLA_BRPORT_GROUP_FWD_MASK, - QEMU_IFLA_BRPORT_NEIGH_SUPPRESS, - QEMU___IFLA_BRPORT_MAX -}; - -enum { - QEMU_IFLA_INFO_UNSPEC, - QEMU_IFLA_INFO_KIND, - QEMU_IFLA_INFO_DATA, - QEMU_IFLA_INFO_XSTATS, - QEMU_IFLA_INFO_SLAVE_KIND, - QEMU_IFLA_INFO_SLAVE_DATA, - QEMU___IFLA_INFO_MAX, -}; - -enum { - QEMU_IFLA_INET_UNSPEC, - QEMU_IFLA_INET_CONF, - QEMU___IFLA_INET_MAX, -}; - -enum { - QEMU_IFLA_INET6_UNSPEC, - QEMU_IFLA_INET6_FLAGS, - QEMU_IFLA_INET6_CONF, - QEMU_IFLA_INET6_STATS, - QEMU_IFLA_INET6_MCAST, - QEMU_IFLA_INET6_CACHEINFO, - QEMU_IFLA_INET6_ICMP6STATS, - QEMU_IFLA_INET6_TOKEN, - QEMU_IFLA_INET6_ADDR_GEN_MODE, - QEMU___IFLA_INET6_MAX -}; - -enum { - QEMU_IFLA_XDP_UNSPEC, - QEMU_IFLA_XDP_FD, - QEMU_IFLA_XDP_ATTACHED, - QEMU_IFLA_XDP_FLAGS, - QEMU_IFLA_XDP_PROG_ID, - QEMU___IFLA_XDP_MAX, -}; - -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_target_to_host_data(int fd) -{ - if (fd >= 0 && fd < target_fd_max && target_fd_trans[fd]) { - return target_fd_trans[fd]->target_to_host_data; - } - return NULL; -} - -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) { @@ -1147,7 +895,7 @@ abi_long do_brk(abi_ulong new_brk) } target_brk = new_brk; DEBUGF_BRK(TARGET_ABI_FMT_lx " (new_brk <= brk_page)\n", target_brk); - return target_brk; + return target_brk; } /* We need to allocate more memory after the brk... Note that @@ -2029,915 +1777,6 @@ static inline abi_long host_to_target_cmsg(struct target_msghdr *target_msgh, return 0; } -static void tswap_nlmsghdr(struct nlmsghdr *nlh) -{ - nlh->nlmsg_len = tswap32(nlh->nlmsg_len); - nlh->nlmsg_type = tswap16(nlh->nlmsg_type); - nlh->nlmsg_flags = tswap16(nlh->nlmsg_flags); - nlh->nlmsg_seq = tswap32(nlh->nlmsg_seq); - nlh->nlmsg_pid = tswap32(nlh->nlmsg_pid); -} - -static abi_long host_to_target_for_each_nlmsg(struct nlmsghdr *nlh, - size_t len, - abi_long (*host_to_target_nlmsg) - (struct nlmsghdr *)) -{ - uint32_t nlmsg_len; - abi_long ret; - - while (len > sizeof(struct nlmsghdr)) { - - nlmsg_len = nlh->nlmsg_len; - if (nlmsg_len < sizeof(struct nlmsghdr) || - nlmsg_len > len) { - break; - } - - switch (nlh->nlmsg_type) { - case NLMSG_DONE: - tswap_nlmsghdr(nlh); - return 0; - case NLMSG_NOOP: - break; - case NLMSG_ERROR: - { - struct nlmsgerr *e = NLMSG_DATA(nlh); - e->error = tswap32(e->error); - tswap_nlmsghdr(&e->msg); - tswap_nlmsghdr(nlh); - return 0; - } - default: - ret = host_to_target_nlmsg(nlh); - if (ret < 0) { - tswap_nlmsghdr(nlh); - return ret; - } - break; - } - tswap_nlmsghdr(nlh); - len -= NLMSG_ALIGN(nlmsg_len); - nlh = (struct nlmsghdr *)(((char*)nlh) + NLMSG_ALIGN(nlmsg_len)); - } - return 0; -} - -static abi_long target_to_host_for_each_nlmsg(struct nlmsghdr *nlh, - size_t len, - abi_long (*target_to_host_nlmsg) - (struct nlmsghdr *)) -{ - int ret; - - while (len > sizeof(struct nlmsghdr)) { - if (tswap32(nlh->nlmsg_len) < sizeof(struct nlmsghdr) || - tswap32(nlh->nlmsg_len) > len) { - break; - } - tswap_nlmsghdr(nlh); - switch (nlh->nlmsg_type) { - case NLMSG_DONE: - return 0; - case NLMSG_NOOP: - break; - case NLMSG_ERROR: - { - struct nlmsgerr *e = NLMSG_DATA(nlh); - e->error = tswap32(e->error); - tswap_nlmsghdr(&e->msg); - return 0; - } - default: - ret = target_to_host_nlmsg(nlh); - if (ret < 0) { - return ret; - } - } - len -= NLMSG_ALIGN(nlh->nlmsg_len); - nlh = (struct nlmsghdr *)(((char *)nlh) + NLMSG_ALIGN(nlh->nlmsg_len)); - } - return 0; -} - -#ifdef CONFIG_RTNETLINK -static abi_long host_to_target_for_each_nlattr(struct nlattr *nlattr, - size_t len, void *context, - abi_long (*host_to_target_nlattr) - (struct nlattr *, - void *context)) -{ - unsigned short nla_len; - abi_long ret; - - while (len > sizeof(struct nlattr)) { - nla_len = nlattr->nla_len; - if (nla_len < sizeof(struct nlattr) || - nla_len > len) { - break; - } - ret = host_to_target_nlattr(nlattr, context); - nlattr->nla_len = tswap16(nlattr->nla_len); - nlattr->nla_type = tswap16(nlattr->nla_type); - if (ret < 0) { - return ret; - } - len -= NLA_ALIGN(nla_len); - nlattr = (struct nlattr *)(((char *)nlattr) + NLA_ALIGN(nla_len)); - } - return 0; -} - -static abi_long host_to_target_for_each_rtattr(struct rtattr *rtattr, - size_t len, - abi_long (*host_to_target_rtattr) - (struct rtattr *)) -{ - unsigned short rta_len; - abi_long ret; - - while (len > sizeof(struct rtattr)) { - rta_len = rtattr->rta_len; - if (rta_len < sizeof(struct rtattr) || - rta_len > len) { - break; - } - ret = host_to_target_rtattr(rtattr); - rtattr->rta_len = tswap16(rtattr->rta_len); - rtattr->rta_type = tswap16(rtattr->rta_type); - if (ret < 0) { - return ret; - } - len -= RTA_ALIGN(rta_len); - rtattr = (struct rtattr *)(((char *)rtattr) + RTA_ALIGN(rta_len)); - } - return 0; -} - -#define NLA_DATA(nla) ((void *)((char *)(nla)) + NLA_HDRLEN) - -static abi_long host_to_target_data_bridge_nlattr(struct nlattr *nlattr, - void *context) -{ - uint16_t *u16; - uint32_t *u32; - uint64_t *u64; - - switch (nlattr->nla_type) { - /* no data */ - case QEMU_IFLA_BR_FDB_FLUSH: - break; - /* binary */ - case QEMU_IFLA_BR_GROUP_ADDR: - break; - /* uint8_t */ - case QEMU_IFLA_BR_VLAN_FILTERING: - case QEMU_IFLA_BR_TOPOLOGY_CHANGE: - case QEMU_IFLA_BR_TOPOLOGY_CHANGE_DETECTED: - case QEMU_IFLA_BR_MCAST_ROUTER: - case QEMU_IFLA_BR_MCAST_SNOOPING: - case QEMU_IFLA_BR_MCAST_QUERY_USE_IFADDR: - case QEMU_IFLA_BR_MCAST_QUERIER: - case QEMU_IFLA_BR_NF_CALL_IPTABLES: - case QEMU_IFLA_BR_NF_CALL_IP6TABLES: - case QEMU_IFLA_BR_NF_CALL_ARPTABLES: - case QEMU_IFLA_BR_VLAN_STATS_ENABLED: - case QEMU_IFLA_BR_MCAST_STATS_ENABLED: - case QEMU_IFLA_BR_MCAST_IGMP_VERSION: - case QEMU_IFLA_BR_MCAST_MLD_VERSION: - break; - /* uint16_t */ - case QEMU_IFLA_BR_PRIORITY: - case QEMU_IFLA_BR_VLAN_PROTOCOL: - case QEMU_IFLA_BR_GROUP_FWD_MASK: - case QEMU_IFLA_BR_ROOT_PORT: - case QEMU_IFLA_BR_VLAN_DEFAULT_PVID: - u16 = NLA_DATA(nlattr); - *u16 = tswap16(*u16); - break; - /* uint32_t */ - case QEMU_IFLA_BR_FORWARD_DELAY: - case QEMU_IFLA_BR_HELLO_TIME: - case QEMU_IFLA_BR_MAX_AGE: - case QEMU_IFLA_BR_AGEING_TIME: - case QEMU_IFLA_BR_STP_STATE: - case QEMU_IFLA_BR_ROOT_PATH_COST: - case QEMU_IFLA_BR_MCAST_HASH_ELASTICITY: - case QEMU_IFLA_BR_MCAST_HASH_MAX: - case QEMU_IFLA_BR_MCAST_LAST_MEMBER_CNT: - case QEMU_IFLA_BR_MCAST_STARTUP_QUERY_CNT: - u32 = NLA_DATA(nlattr); - *u32 = tswap32(*u32); - break; - /* uint64_t */ - case QEMU_IFLA_BR_HELLO_TIMER: - case QEMU_IFLA_BR_TCN_TIMER: - case QEMU_IFLA_BR_GC_TIMER: - case QEMU_IFLA_BR_TOPOLOGY_CHANGE_TIMER: - case QEMU_IFLA_BR_MCAST_LAST_MEMBER_INTVL: - case QEMU_IFLA_BR_MCAST_MEMBERSHIP_INTVL: - case QEMU_IFLA_BR_MCAST_QUERIER_INTVL: - case QEMU_IFLA_BR_MCAST_QUERY_INTVL: - case QEMU_IFLA_BR_MCAST_QUERY_RESPONSE_INTVL: - case QEMU_IFLA_BR_MCAST_STARTUP_QUERY_INTVL: - u64 = NLA_DATA(nlattr); - *u64 = tswap64(*u64); - break; - /* ifla_bridge_id: uin8_t[] */ - case QEMU_IFLA_BR_ROOT_ID: - case QEMU_IFLA_BR_BRIDGE_ID: - break; - default: - gemu_log("Unknown QEMU_IFLA_BR type %d\n", nlattr->nla_type); - break; - } - return 0; -} - -static abi_long host_to_target_slave_data_bridge_nlattr(struct nlattr *nlattr, - void *context) -{ - uint16_t *u16; - uint32_t *u32; - uint64_t *u64; - - switch (nlattr->nla_type) { - /* uint8_t */ - case QEMU_IFLA_BRPORT_STATE: - case QEMU_IFLA_BRPORT_MODE: - case QEMU_IFLA_BRPORT_GUARD: - case QEMU_IFLA_BRPORT_PROTECT: - case QEMU_IFLA_BRPORT_FAST_LEAVE: - case QEMU_IFLA_BRPORT_LEARNING: - case QEMU_IFLA_BRPORT_UNICAST_FLOOD: - case QEMU_IFLA_BRPORT_PROXYARP: - case QEMU_IFLA_BRPORT_LEARNING_SYNC: - case QEMU_IFLA_BRPORT_PROXYARP_WIFI: - case QEMU_IFLA_BRPORT_TOPOLOGY_CHANGE_ACK: - case QEMU_IFLA_BRPORT_CONFIG_PENDING: - case QEMU_IFLA_BRPORT_MULTICAST_ROUTER: - case QEMU_IFLA_BRPORT_MCAST_FLOOD: - case QEMU_IFLA_BRPORT_MCAST_TO_UCAST: - case QEMU_IFLA_BRPORT_VLAN_TUNNEL: - case QEMU_IFLA_BRPORT_BCAST_FLOOD: - case QEMU_IFLA_BRPORT_NEIGH_SUPPRESS: - break; - /* uint16_t */ - case QEMU_IFLA_BRPORT_PRIORITY: - case QEMU_IFLA_BRPORT_DESIGNATED_PORT: - case QEMU_IFLA_BRPORT_DESIGNATED_COST: - case QEMU_IFLA_BRPORT_ID: - case QEMU_IFLA_BRPORT_NO: - case QEMU_IFLA_BRPORT_GROUP_FWD_MASK: - u16 = NLA_DATA(nlattr); - *u16 = tswap16(*u16); - break; - /* uin32_t */ - case QEMU_IFLA_BRPORT_COST: - u32 = NLA_DATA(nlattr); - *u32 = tswap32(*u32); - break; - /* uint64_t */ - case QEMU_IFLA_BRPORT_MESSAGE_AGE_TIMER: - case QEMU_IFLA_BRPORT_FORWARD_DELAY_TIMER: - case QEMU_IFLA_BRPORT_HOLD_TIMER: - u64 = NLA_DATA(nlattr); - *u64 = tswap64(*u64); - break; - /* ifla_bridge_id: uint8_t[] */ - case QEMU_IFLA_BRPORT_ROOT_ID: - case QEMU_IFLA_BRPORT_BRIDGE_ID: - break; - default: - gemu_log("Unknown QEMU_IFLA_BRPORT type %d\n", nlattr->nla_type); - break; - } - return 0; -} - -struct linkinfo_context { - int len; - char *name; - int slave_len; - char *slave_name; -}; - -static abi_long host_to_target_data_linkinfo_nlattr(struct nlattr *nlattr, - void *context) -{ - struct linkinfo_context *li_context = context; - - switch (nlattr->nla_type) { - /* string */ - case QEMU_IFLA_INFO_KIND: - li_context->name = NLA_DATA(nlattr); - li_context->len = nlattr->nla_len - NLA_HDRLEN; - break; - case QEMU_IFLA_INFO_SLAVE_KIND: - li_context->slave_name = NLA_DATA(nlattr); - li_context->slave_len = nlattr->nla_len - NLA_HDRLEN; - break; - /* stats */ - case QEMU_IFLA_INFO_XSTATS: - /* FIXME: only used by CAN */ - break; - /* nested */ - case QEMU_IFLA_INFO_DATA: - if (strncmp(li_context->name, "bridge", - li_context->len) == 0) { - return host_to_target_for_each_nlattr(NLA_DATA(nlattr), - nlattr->nla_len, - NULL, - host_to_target_data_bridge_nlattr); - } else { - gemu_log("Unknown QEMU_IFLA_INFO_KIND %s\n", li_context->name); - } - break; - case QEMU_IFLA_INFO_SLAVE_DATA: - if (strncmp(li_context->slave_name, "bridge", - li_context->slave_len) == 0) { - return host_to_target_for_each_nlattr(NLA_DATA(nlattr), - nlattr->nla_len, - NULL, - host_to_target_slave_data_bridge_nlattr); - } else { - gemu_log("Unknown QEMU_IFLA_INFO_SLAVE_KIND %s\n", - li_context->slave_name); - } - break; - default: - gemu_log("Unknown host QEMU_IFLA_INFO type: %d\n", nlattr->nla_type); - break; - } - - return 0; -} - -static abi_long host_to_target_data_inet_nlattr(struct nlattr *nlattr, - void *context) -{ - uint32_t *u32; - int i; - - switch (nlattr->nla_type) { - case QEMU_IFLA_INET_CONF: - u32 = NLA_DATA(nlattr); - for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32); - i++) { - u32[i] = tswap32(u32[i]); - } - break; - default: - gemu_log("Unknown host AF_INET type: %d\n", nlattr->nla_type); - } - return 0; -} - -static abi_long host_to_target_data_inet6_nlattr(struct nlattr *nlattr, - void *context) -{ - uint32_t *u32; - uint64_t *u64; - struct ifla_cacheinfo *ci; - int i; - - switch (nlattr->nla_type) { - /* binaries */ - case QEMU_IFLA_INET6_TOKEN: - break; - /* uint8_t */ - case QEMU_IFLA_INET6_ADDR_GEN_MODE: - break; - /* uint32_t */ - case QEMU_IFLA_INET6_FLAGS: - u32 = NLA_DATA(nlattr); - *u32 = tswap32(*u32); - break; - /* uint32_t[] */ - case QEMU_IFLA_INET6_CONF: - u32 = NLA_DATA(nlattr); - for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u32); - i++) { - u32[i] = tswap32(u32[i]); - } - break; - /* ifla_cacheinfo */ - case QEMU_IFLA_INET6_CACHEINFO: - ci = NLA_DATA(nlattr); - ci->max_reasm_len = tswap32(ci->max_reasm_len); - ci->tstamp = tswap32(ci->tstamp); - ci->reachable_time = tswap32(ci->reachable_time); - ci->retrans_time = tswap32(ci->retrans_time); - break; - /* uint64_t[] */ - case QEMU_IFLA_INET6_STATS: - case QEMU_IFLA_INET6_ICMP6STATS: - u64 = NLA_DATA(nlattr); - for (i = 0; i < (nlattr->nla_len - NLA_HDRLEN) / sizeof(*u64); - i++) { - u64[i] = tswap64(u64[i]); - } - break; - default: - gemu_log("Unknown host AF_INET6 type: %d\n", nlattr->nla_type); - } - return 0; -} - -static abi_long host_to_target_data_spec_nlattr(struct nlattr *nlattr, - void *context) -{ - switch (nlattr->nla_type) { - case AF_INET: - return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len, - NULL, - host_to_target_data_inet_nlattr); - case AF_INET6: - return host_to_target_for_each_nlattr(NLA_DATA(nlattr), nlattr->nla_len, - NULL, - host_to_target_data_inet6_nlattr); - default: - gemu_log("Unknown host AF_SPEC type: %d\n", nlattr->nla_type); - break; - } - return 0; -} - -static abi_long host_to_target_data_xdp_nlattr(struct nlattr *nlattr, - void *context) -{ - uint32_t *u32; - - switch (nlattr->nla_type) { - /* uint8_t */ - case QEMU_IFLA_XDP_ATTACHED: - break; - /* uint32_t */ - case QEMU_IFLA_XDP_PROG_ID: - u32 = NLA_DATA(nlattr); - *u32 = tswap32(*u32); - break; - default: - gemu_log("Unknown host XDP type: %d\n", nlattr->nla_type); - break; - } - return 0; -} - -static abi_long host_to_target_data_link_rtattr(struct rtattr *rtattr) -{ - uint32_t *u32; - struct rtnl_link_stats *st; - struct rtnl_link_stats64 *st64; - struct rtnl_link_ifmap *map; - struct linkinfo_context li_context; - - switch (rtattr->rta_type) { - /* binary stream */ - case QEMU_IFLA_ADDRESS: - case QEMU_IFLA_BROADCAST: - /* string */ - case QEMU_IFLA_IFNAME: - case QEMU_IFLA_QDISC: - break; - /* uin8_t */ - case QEMU_IFLA_OPERSTATE: - case QEMU_IFLA_LINKMODE: - case QEMU_IFLA_CARRIER: - case QEMU_IFLA_PROTO_DOWN: - break; - /* uint32_t */ - case QEMU_IFLA_MTU: - case QEMU_IFLA_LINK: - case QEMU_IFLA_WEIGHT: - case QEMU_IFLA_TXQLEN: - case QEMU_IFLA_CARRIER_CHANGES: - case QEMU_IFLA_NUM_RX_QUEUES: - case QEMU_IFLA_NUM_TX_QUEUES: - case QEMU_IFLA_PROMISCUITY: - case QEMU_IFLA_EXT_MASK: - case QEMU_IFLA_LINK_NETNSID: - case QEMU_IFLA_GROUP: - case QEMU_IFLA_MASTER: - case QEMU_IFLA_NUM_VF: - case QEMU_IFLA_GSO_MAX_SEGS: - case QEMU_IFLA_GSO_MAX_SIZE: - case QEMU_IFLA_CARRIER_UP_COUNT: - case QEMU_IFLA_CARRIER_DOWN_COUNT: - u32 = RTA_DATA(rtattr); - *u32 = tswap32(*u32); - break; - /* struct rtnl_link_stats */ - case QEMU_IFLA_STATS: - st = RTA_DATA(rtattr); - st->rx_packets = tswap32(st->rx_packets); - st->tx_packets = tswap32(st->tx_packets); - st->rx_bytes = tswap32(st->rx_bytes); - st->tx_bytes = tswap32(st->tx_bytes); - st->rx_errors = tswap32(st->rx_errors); - st->tx_errors = tswap32(st->tx_errors); - st->rx_dropped = tswap32(st->rx_dropped); - st->tx_dropped = tswap32(st->tx_dropped); - st->multicast = tswap32(st->multicast); - st->collisions = tswap32(st->collisions); - - /* detailed rx_errors: */ - st->rx_length_errors = tswap32(st->rx_length_errors); - st->rx_over_errors = tswap32(st->rx_over_errors); - st->rx_crc_errors = tswap32(st->rx_crc_errors); - st->rx_frame_errors = tswap32(st->rx_frame_errors); - st->rx_fifo_errors = tswap32(st->rx_fifo_errors); - st->rx_missed_errors = tswap32(st->rx_missed_errors); - - /* detailed tx_errors */ - st->tx_aborted_errors = tswap32(st->tx_aborted_errors); - st->tx_carrier_errors = tswap32(st->tx_carrier_errors); - st->tx_fifo_errors = tswap32(st->tx_fifo_errors); - st->tx_heartbeat_errors = tswap32(st->tx_heartbeat_errors); - st->tx_window_errors = tswap32(st->tx_window_errors); - - /* for cslip etc */ - st->rx_compressed = tswap32(st->rx_compressed); - st->tx_compressed = tswap32(st->tx_compressed); - break; - /* struct rtnl_link_stats64 */ - case QEMU_IFLA_STATS64: - st64 = RTA_DATA(rtattr); - st64->rx_packets = tswap64(st64->rx_packets); - st64->tx_packets = tswap64(st64->tx_packets); - st64->rx_bytes = tswap64(st64->rx_bytes); - st64->tx_bytes = tswap64(st64->tx_bytes); - st64->rx_errors = tswap64(st64->rx_errors); - st64->tx_errors = tswap64(st64->tx_errors); - st64->rx_dropped = tswap64(st64->rx_dropped); - st64->tx_dropped = tswap64(st64->tx_dropped); - st64->multicast = tswap64(st64->multicast); - st64->collisions = tswap64(st64->collisions); - - /* detailed rx_errors: */ - st64->rx_length_errors = tswap64(st64->rx_length_errors); - st64->rx_over_errors = tswap64(st64->rx_over_errors); - st64->rx_crc_errors = tswap64(st64->rx_crc_errors); - st64->rx_frame_errors = tswap64(st64->rx_frame_errors); - st64->rx_fifo_errors = tswap64(st64->rx_fifo_errors); - st64->rx_missed_errors = tswap64(st64->rx_missed_errors); - - /* detailed tx_errors */ - st64->tx_aborted_errors = tswap64(st64->tx_aborted_errors); - st64->tx_carrier_errors = tswap64(st64->tx_carrier_errors); - st64->tx_fifo_errors = tswap64(st64->tx_fifo_errors); - st64->tx_heartbeat_errors = tswap64(st64->tx_heartbeat_errors); - st64->tx_window_errors = tswap64(st64->tx_window_errors); - - /* for cslip etc */ - st64->rx_compressed = tswap64(st64->rx_compressed); - st64->tx_compressed = tswap64(st64->tx_compressed); - break; - /* struct rtnl_link_ifmap */ - case QEMU_IFLA_MAP: - map = RTA_DATA(rtattr); - map->mem_start = tswap64(map->mem_start); - map->mem_end = tswap64(map->mem_end); - map->base_addr = tswap64(map->base_addr); - map->irq = tswap16(map->irq); - break; - /* nested */ - case QEMU_IFLA_LINKINFO: - memset(&li_context, 0, sizeof(li_context)); - return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, - &li_context, - host_to_target_data_linkinfo_nlattr); - case QEMU_IFLA_AF_SPEC: - return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, - NULL, - host_to_target_data_spec_nlattr); - case QEMU_IFLA_XDP: - return host_to_target_for_each_nlattr(RTA_DATA(rtattr), rtattr->rta_len, - NULL, - host_to_target_data_xdp_nlattr); - default: - gemu_log("Unknown host QEMU_IFLA type: %d\n", rtattr->rta_type); - break; - } - return 0; -} - -static abi_long host_to_target_data_addr_rtattr(struct rtattr *rtattr) -{ - uint32_t *u32; - struct ifa_cacheinfo *ci; - - switch (rtattr->rta_type) { - /* binary: depends on family type */ - case IFA_ADDRESS: - case IFA_LOCAL: - break; - /* string */ - case IFA_LABEL: - break; - /* u32 */ - case IFA_FLAGS: - case IFA_BROADCAST: - u32 = RTA_DATA(rtattr); - *u32 = tswap32(*u32); - break; - /* struct ifa_cacheinfo */ - case IFA_CACHEINFO: - ci = RTA_DATA(rtattr); - ci->ifa_prefered = tswap32(ci->ifa_prefered); - ci->ifa_valid = tswap32(ci->ifa_valid); - ci->cstamp = tswap32(ci->cstamp); - ci->tstamp = tswap32(ci->tstamp); - break; - default: - gemu_log("Unknown host IFA type: %d\n", rtattr->rta_type); - break; - } - return 0; -} - -static abi_long host_to_target_data_route_rtattr(struct rtattr *rtattr) -{ - uint32_t *u32; - switch (rtattr->rta_type) { - /* binary: depends on family type */ - case RTA_GATEWAY: - case RTA_DST: - case RTA_PREFSRC: - break; - /* u32 */ - case RTA_PRIORITY: - case RTA_TABLE: - case RTA_OIF: - u32 = RTA_DATA(rtattr); - *u32 = tswap32(*u32); - break; - default: - gemu_log("Unknown host RTA type: %d\n", rtattr->rta_type); - break; - } - return 0; -} - -static abi_long host_to_target_link_rtattr(struct rtattr *rtattr, - uint32_t rtattr_len) -{ - return host_to_target_for_each_rtattr(rtattr, rtattr_len, - host_to_target_data_link_rtattr); -} - -static abi_long host_to_target_addr_rtattr(struct rtattr *rtattr, - uint32_t rtattr_len) -{ - return host_to_target_for_each_rtattr(rtattr, rtattr_len, - host_to_target_data_addr_rtattr); -} - -static abi_long host_to_target_route_rtattr(struct rtattr *rtattr, - uint32_t rtattr_len) -{ - return host_to_target_for_each_rtattr(rtattr, rtattr_len, - host_to_target_data_route_rtattr); -} - -static abi_long host_to_target_data_route(struct nlmsghdr *nlh) -{ - uint32_t nlmsg_len; - struct ifinfomsg *ifi; - struct ifaddrmsg *ifa; - struct rtmsg *rtm; - - nlmsg_len = nlh->nlmsg_len; - switch (nlh->nlmsg_type) { - case RTM_NEWLINK: - case RTM_DELLINK: - case RTM_GETLINK: - if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) { - ifi = NLMSG_DATA(nlh); - ifi->ifi_type = tswap16(ifi->ifi_type); - ifi->ifi_index = tswap32(ifi->ifi_index); - ifi->ifi_flags = tswap32(ifi->ifi_flags); - ifi->ifi_change = tswap32(ifi->ifi_change); - host_to_target_link_rtattr(IFLA_RTA(ifi), - nlmsg_len - NLMSG_LENGTH(sizeof(*ifi))); - } - break; - case RTM_NEWADDR: - case RTM_DELADDR: - case RTM_GETADDR: - if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifa))) { - ifa = NLMSG_DATA(nlh); - ifa->ifa_index = tswap32(ifa->ifa_index); - host_to_target_addr_rtattr(IFA_RTA(ifa), - nlmsg_len - NLMSG_LENGTH(sizeof(*ifa))); - } - break; - case RTM_NEWROUTE: - case RTM_DELROUTE: - case RTM_GETROUTE: - if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*rtm))) { - rtm = NLMSG_DATA(nlh); - rtm->rtm_flags = tswap32(rtm->rtm_flags); - host_to_target_route_rtattr(RTM_RTA(rtm), - nlmsg_len - NLMSG_LENGTH(sizeof(*rtm))); - } - break; - default: - return -TARGET_EINVAL; - } - return 0; -} - -static inline abi_long host_to_target_nlmsg_route(struct nlmsghdr *nlh, - size_t len) -{ - return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_route); -} - -static abi_long target_to_host_for_each_rtattr(struct rtattr *rtattr, - size_t len, - abi_long (*target_to_host_rtattr) - (struct rtattr *)) -{ - abi_long ret; - - while (len >= sizeof(struct rtattr)) { - if (tswap16(rtattr->rta_len) < sizeof(struct rtattr) || - tswap16(rtattr->rta_len) > len) { - break; - } - rtattr->rta_len = tswap16(rtattr->rta_len); - rtattr->rta_type = tswap16(rtattr->rta_type); - ret = target_to_host_rtattr(rtattr); - if (ret < 0) { - return ret; - } - len -= RTA_ALIGN(rtattr->rta_len); - rtattr = (struct rtattr *)(((char *)rtattr) + - RTA_ALIGN(rtattr->rta_len)); - } - return 0; -} - -static abi_long target_to_host_data_link_rtattr(struct rtattr *rtattr) -{ - switch (rtattr->rta_type) { - default: - gemu_log("Unknown target QEMU_IFLA type: %d\n", rtattr->rta_type); - break; - } - return 0; -} - -static abi_long target_to_host_data_addr_rtattr(struct rtattr *rtattr) -{ - switch (rtattr->rta_type) { - /* binary: depends on family type */ - case IFA_LOCAL: - case IFA_ADDRESS: - break; - default: - gemu_log("Unknown target IFA type: %d\n", rtattr->rta_type); - break; - } - return 0; -} - -static abi_long target_to_host_data_route_rtattr(struct rtattr *rtattr) -{ - uint32_t *u32; - switch (rtattr->rta_type) { - /* binary: depends on family type */ - case RTA_DST: - case RTA_SRC: - case RTA_GATEWAY: - break; - /* u32 */ - case RTA_PRIORITY: - case RTA_OIF: - u32 = RTA_DATA(rtattr); - *u32 = tswap32(*u32); - break; - default: - gemu_log("Unknown target RTA type: %d\n", rtattr->rta_type); - break; - } - return 0; -} - -static void target_to_host_link_rtattr(struct rtattr *rtattr, - uint32_t rtattr_len) -{ - target_to_host_for_each_rtattr(rtattr, rtattr_len, - target_to_host_data_link_rtattr); -} - -static void target_to_host_addr_rtattr(struct rtattr *rtattr, - uint32_t rtattr_len) -{ - target_to_host_for_each_rtattr(rtattr, rtattr_len, - target_to_host_data_addr_rtattr); -} - -static void target_to_host_route_rtattr(struct rtattr *rtattr, - uint32_t rtattr_len) -{ - target_to_host_for_each_rtattr(rtattr, rtattr_len, - target_to_host_data_route_rtattr); -} - -static abi_long target_to_host_data_route(struct nlmsghdr *nlh) -{ - struct ifinfomsg *ifi; - struct ifaddrmsg *ifa; - struct rtmsg *rtm; - - switch (nlh->nlmsg_type) { - case RTM_GETLINK: - break; - case RTM_NEWLINK: - case RTM_DELLINK: - if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifi))) { - ifi = NLMSG_DATA(nlh); - ifi->ifi_type = tswap16(ifi->ifi_type); - ifi->ifi_index = tswap32(ifi->ifi_index); - ifi->ifi_flags = tswap32(ifi->ifi_flags); - ifi->ifi_change = tswap32(ifi->ifi_change); - target_to_host_link_rtattr(IFLA_RTA(ifi), nlh->nlmsg_len - - NLMSG_LENGTH(sizeof(*ifi))); - } - break; - case RTM_GETADDR: - case RTM_NEWADDR: - case RTM_DELADDR: - if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*ifa))) { - ifa = NLMSG_DATA(nlh); - ifa->ifa_index = tswap32(ifa->ifa_index); - target_to_host_addr_rtattr(IFA_RTA(ifa), nlh->nlmsg_len - - NLMSG_LENGTH(sizeof(*ifa))); - } - break; - case RTM_GETROUTE: - break; - case RTM_NEWROUTE: - case RTM_DELROUTE: - if (nlh->nlmsg_len >= NLMSG_LENGTH(sizeof(*rtm))) { - rtm = NLMSG_DATA(nlh); - rtm->rtm_flags = tswap32(rtm->rtm_flags); - target_to_host_route_rtattr(RTM_RTA(rtm), nlh->nlmsg_len - - NLMSG_LENGTH(sizeof(*rtm))); - } - break; - default: - return -TARGET_EOPNOTSUPP; - } - return 0; -} - -static abi_long target_to_host_nlmsg_route(struct nlmsghdr *nlh, size_t len) -{ - return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_route); -} -#endif /* CONFIG_RTNETLINK */ - -static abi_long host_to_target_data_audit(struct nlmsghdr *nlh) -{ - switch (nlh->nlmsg_type) { - default: - gemu_log("Unknown host audit message type %d\n", - nlh->nlmsg_type); - return -TARGET_EINVAL; - } - return 0; -} - -static inline abi_long host_to_target_nlmsg_audit(struct nlmsghdr *nlh, - size_t len) -{ - return host_to_target_for_each_nlmsg(nlh, len, host_to_target_data_audit); -} - -static abi_long target_to_host_data_audit(struct nlmsghdr *nlh) -{ - switch (nlh->nlmsg_type) { - case AUDIT_USER: - case AUDIT_FIRST_USER_MSG ... AUDIT_LAST_USER_MSG: - case AUDIT_FIRST_USER_MSG2 ... AUDIT_LAST_USER_MSG2: - break; - default: - gemu_log("Unknown target audit message type %d\n", - nlh->nlmsg_type); - return -TARGET_EINVAL; - } - - return 0; -} - -static abi_long target_to_host_nlmsg_audit(struct nlmsghdr *nlh, size_t len) -{ - return target_to_host_for_each_nlmsg(nlh, len, target_to_host_data_audit); -} - /* do_setsockopt() Must return target values and target errnos. */ static abi_long do_setsockopt(int sockfd, int level, int optname, abi_ulong optval_addr, socklen_t optlen) @@ -3190,6 +2029,24 @@ set_timeout: unlock_user (dev_ifname, optval_addr, 0); return ret; } + case TARGET_SO_LINGER: + { + struct linger lg; + struct target_linger *tlg; + + if (optlen != sizeof(struct target_linger)) { + return -TARGET_EINVAL; + } + if (!lock_user_struct(VERIFY_READ, tlg, optval_addr, 1)) { + return -TARGET_EFAULT; + } + __get_user(lg.l_onoff, &tlg->l_onoff); + __get_user(lg.l_linger, &tlg->l_linger); + ret = get_errno(setsockopt(sockfd, SOL_SOCKET, SO_LINGER, + &lg, sizeof(lg))); + unlock_user_struct(tlg, optval_addr, 0); + return ret; + } /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -3197,6 +2054,11 @@ set_timeout: case TARGET_SO_REUSEADDR: optname = SO_REUSEADDR; break; +#ifdef SO_REUSEPORT + case TARGET_SO_REUSEPORT: + optname = SO_REUSEPORT; + break; +#endif case TARGET_SO_TYPE: optname = SO_TYPE; break; @@ -3281,7 +2143,6 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, level = SOL_SOCKET; switch (optname) { /* These don't just return a single integer */ - case TARGET_SO_LINGER: case TARGET_SO_RCVTIMEO: case TARGET_SO_SNDTIMEO: case TARGET_SO_PEERNAME: @@ -3319,6 +2180,39 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, } break; } + case TARGET_SO_LINGER: + { + struct linger lg; + socklen_t lglen; + struct target_linger *tlg; + + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len < 0) { + return -TARGET_EINVAL; + } + + lglen = sizeof(lg); + ret = get_errno(getsockopt(sockfd, level, SO_LINGER, + &lg, &lglen)); + if (ret < 0) { + return ret; + } + if (len > lglen) { + len = lglen; + } + if (!lock_user_struct(VERIFY_WRITE, tlg, optval_addr, 0)) { + return -TARGET_EFAULT; + } + __put_user(lg.l_onoff, &tlg->l_onoff); + __put_user(lg.l_linger, &tlg->l_linger); + unlock_user_struct(tlg, optval_addr, 1); + if (put_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + break; + } /* Options with 'int' argument. */ case TARGET_SO_DEBUG: optname = SO_DEBUG; @@ -3326,6 +2220,11 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, case TARGET_SO_REUSEADDR: optname = SO_REUSEADDR; goto int_case; +#ifdef SO_REUSEPORT + case TARGET_SO_REUSEPORT: + optname = SO_REUSEPORT; + goto int_case; +#endif case TARGET_SO_TYPE: optname = SO_TYPE; goto int_case; @@ -3446,6 +2345,45 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, break; } break; + case SOL_IPV6: + switch (optname) { + case IPV6_MTU_DISCOVER: + case IPV6_MTU: + case IPV6_V6ONLY: + case IPV6_RECVPKTINFO: + case IPV6_UNICAST_HOPS: + case IPV6_MULTICAST_HOPS: + case IPV6_MULTICAST_LOOP: + case IPV6_RECVERR: + case IPV6_RECVHOPLIMIT: + case IPV6_2292HOPLIMIT: + case IPV6_CHECKSUM: + if (get_user_u32(len, optlen)) + return -TARGET_EFAULT; + if (len < 0) + return -TARGET_EINVAL; + lv = sizeof(lv); + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); + if (ret < 0) + return ret; + if (len < sizeof(int) && len > 0 && val >= 0 && val < 255) { + len = 1; + if (put_user_u32(len, optlen) + || put_user_u8(val, optval_addr)) + return -TARGET_EFAULT; + } else { + if (len > sizeof(int)) + len = sizeof(int); + if (put_user_u32(len, optlen) + || put_user_u32(val, optval_addr)) + return -TARGET_EFAULT; + } + break; + default: + ret = -TARGET_ENOPROTOOPT; + break; + } + break; default: unimplemented: gemu_log("getsockopt level=%d optname=%d not yet supported\n", @@ -3633,90 +2571,6 @@ 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, -}; - -#ifdef CONFIG_RTNETLINK -static abi_long netlink_route_target_to_host(void *buf, size_t len) -{ - abi_long ret; - - ret = target_to_host_nlmsg_route(buf, len); - if (ret < 0) { - return ret; - } - - return len; -} - -static abi_long netlink_route_host_to_target(void *buf, size_t len) -{ - abi_long ret; - - ret = host_to_target_nlmsg_route(buf, len); - if (ret < 0) { - return ret; - } - - return len; -} - -static TargetFdTrans target_netlink_route_trans = { - .target_to_host_data = netlink_route_target_to_host, - .host_to_target_data = netlink_route_host_to_target, -}; -#endif /* CONFIG_RTNETLINK */ - -static abi_long netlink_audit_target_to_host(void *buf, size_t len) -{ - abi_long ret; - - ret = target_to_host_nlmsg_audit(buf, len); - if (ret < 0) { - return ret; - } - - return len; -} - -static abi_long netlink_audit_host_to_target(void *buf, size_t len) -{ - abi_long ret; - - ret = host_to_target_nlmsg_audit(buf, len); - if (ret < 0) { - return ret; - } - - return len; -} - -static TargetFdTrans target_netlink_audit_trans = { - .target_to_host_data = netlink_audit_target_to_host, - .host_to_target_data = netlink_audit_host_to_target, -}; - /* do_socket() Must return target values and target errnos. */ static abi_long do_socket(int domain, int type, int protocol) { @@ -3892,12 +2746,13 @@ static abi_long do_sendrecvmsg_locked(int fd, struct target_msghdr *msgp, len = ret; if (fd_trans_host_to_target_data(fd)) { ret = fd_trans_host_to_target_data(fd)(msg.msg_iov->iov_base, - len); + MIN(msg.msg_iov->iov_len, len)); } else { ret = host_to_target_cmsg(msgp, &msg); } if (!is_error(ret)) { msgp->msg_namelen = tswap32(msg.msg_namelen); + msgp->msg_flags = tswap32(msg.msg_flags); if (msg.msg_name != NULL && msg.msg_name != (void *)-1) { ret = host_to_target_sockaddr(tswapal(msgp->msg_name), msg.msg_name, msg.msg_namelen); @@ -3985,7 +2840,7 @@ static abi_long do_sendrecvmmsg(int fd, abi_ulong target_msgvec, static abi_long do_accept4(int fd, abi_ulong target_addr, abi_ulong target_addrlen_addr, int flags) { - socklen_t addrlen; + socklen_t addrlen, ret_addrlen; void *addr; abi_long ret; int host_flags; @@ -4009,11 +2864,13 @@ static abi_long do_accept4(int fd, abi_ulong target_addr, addr = alloca(addrlen); - ret = get_errno(safe_accept4(fd, addr, &addrlen, host_flags)); + ret_addrlen = addrlen; + ret = get_errno(safe_accept4(fd, addr, &ret_addrlen, host_flags)); if (!is_error(ret)) { - host_to_target_sockaddr(target_addr, addr, addrlen); - if (put_user_u32(addrlen, target_addrlen_addr)) + host_to_target_sockaddr(target_addr, addr, MIN(addrlen, ret_addrlen)); + if (put_user_u32(ret_addrlen, target_addrlen_addr)) { ret = -TARGET_EFAULT; + } } return ret; } @@ -4022,7 +2879,7 @@ static abi_long do_accept4(int fd, abi_ulong target_addr, static abi_long do_getpeername(int fd, abi_ulong target_addr, abi_ulong target_addrlen_addr) { - socklen_t addrlen; + socklen_t addrlen, ret_addrlen; void *addr; abi_long ret; @@ -4038,11 +2895,13 @@ static abi_long do_getpeername(int fd, abi_ulong target_addr, addr = alloca(addrlen); - ret = get_errno(getpeername(fd, addr, &addrlen)); + ret_addrlen = addrlen; + ret = get_errno(getpeername(fd, addr, &ret_addrlen)); if (!is_error(ret)) { - host_to_target_sockaddr(target_addr, addr, addrlen); - if (put_user_u32(addrlen, target_addrlen_addr)) + host_to_target_sockaddr(target_addr, addr, MIN(addrlen, ret_addrlen)); + if (put_user_u32(ret_addrlen, target_addrlen_addr)) { ret = -TARGET_EFAULT; + } } return ret; } @@ -4051,7 +2910,7 @@ static abi_long do_getpeername(int fd, abi_ulong target_addr, static abi_long do_getsockname(int fd, abi_ulong target_addr, abi_ulong target_addrlen_addr) { - socklen_t addrlen; + socklen_t addrlen, ret_addrlen; void *addr; abi_long ret; @@ -4067,11 +2926,13 @@ static abi_long do_getsockname(int fd, abi_ulong target_addr, addr = alloca(addrlen); - ret = get_errno(getsockname(fd, addr, &addrlen)); + ret_addrlen = addrlen; + ret = get_errno(getsockname(fd, addr, &ret_addrlen)); if (!is_error(ret)) { - host_to_target_sockaddr(target_addr, addr, addrlen); - if (put_user_u32(addrlen, target_addrlen_addr)) + host_to_target_sockaddr(target_addr, addr, MIN(addrlen, ret_addrlen)); + if (put_user_u32(ret_addrlen, target_addrlen_addr)) { ret = -TARGET_EFAULT; + } } return ret; } @@ -4143,7 +3004,7 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags, abi_ulong target_addr, abi_ulong target_addrlen) { - socklen_t addrlen; + socklen_t addrlen, ret_addrlen; void *addr; void *host_msg; abi_long ret; @@ -4161,19 +3022,27 @@ static abi_long do_recvfrom(int fd, abi_ulong msg, size_t len, int flags, goto fail; } addr = alloca(addrlen); + ret_addrlen = addrlen; ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, - addr, &addrlen)); + addr, &ret_addrlen)); } else { addr = NULL; /* To keep compiler quiet. */ + addrlen = 0; /* To keep compiler quiet. */ ret = get_errno(safe_recvfrom(fd, host_msg, len, flags, NULL, 0)); } if (!is_error(ret)) { if (fd_trans_host_to_target_data(fd)) { - ret = fd_trans_host_to_target_data(fd)(host_msg, ret); + abi_long trans; + trans = fd_trans_host_to_target_data(fd)(host_msg, MIN(ret, len)); + if (is_error(trans)) { + ret = trans; + goto fail; + } } if (target_addr) { - host_to_target_sockaddr(target_addr, addr, addrlen); - if (put_user_u32(addrlen, target_addrlen)) { + host_to_target_sockaddr(target_addr, addr, + MIN(addrlen, ret_addrlen)); + if (put_user_u32(ret_addrlen, target_addrlen)) { ret = -TARGET_EFAULT; goto fail; } @@ -5321,28 +4190,33 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, unlock_user(argptr, arg, 0); host_ifconf = (struct ifconf *)(unsigned long)buf_temp; - target_ifc_len = host_ifconf->ifc_len; target_ifc_buf = (abi_long)(unsigned long)host_ifconf->ifc_buf; - target_ifreq_size = thunk_type_size(ifreq_arg_type, 0); - nb_ifreq = target_ifc_len / target_ifreq_size; - host_ifc_len = nb_ifreq * sizeof(struct ifreq); - outbufsz = sizeof(*host_ifconf) + host_ifc_len; - if (outbufsz > MAX_STRUCT_SIZE) { - /* We can't fit all the extents into the fixed size buffer. - * Allocate one that is large enough and use it instead. - */ - host_ifconf = malloc(outbufsz); - if (!host_ifconf) { - return -TARGET_ENOMEM; + if (target_ifc_buf != 0) { + target_ifc_len = host_ifconf->ifc_len; + nb_ifreq = target_ifc_len / target_ifreq_size; + host_ifc_len = nb_ifreq * sizeof(struct ifreq); + + outbufsz = sizeof(*host_ifconf) + host_ifc_len; + if (outbufsz > MAX_STRUCT_SIZE) { + /* + * We can't fit all the extents into the fixed size buffer. + * Allocate one that is large enough and use it instead. + */ + host_ifconf = malloc(outbufsz); + if (!host_ifconf) { + return -TARGET_ENOMEM; + } + memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf)); + free_buf = 1; } - memcpy(host_ifconf, buf_temp, sizeof(*host_ifconf)); - free_buf = 1; - } - host_ifc_buf = (char*)host_ifconf + sizeof(*host_ifconf); + host_ifc_buf = (char *)host_ifconf + sizeof(*host_ifconf); - host_ifconf->ifc_len = host_ifc_len; + host_ifconf->ifc_len = host_ifc_len; + } else { + host_ifc_buf = NULL; + } host_ifconf->ifc_buf = host_ifc_buf; ret = get_errno(safe_ioctl(fd, ie->host_cmd, host_ifconf)); @@ -5365,15 +4239,16 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, thunk_convert(argptr, host_ifconf, arg_type, THUNK_TARGET); unlock_user(argptr, arg, target_size); - /* copy ifreq[] to target user */ - - argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0); - for (i = 0; i < nb_ifreq ; i++) { - thunk_convert(argptr + i * target_ifreq_size, - host_ifc_buf + i * sizeof(struct ifreq), - ifreq_arg_type, THUNK_TARGET); + if (target_ifc_buf != 0) { + /* copy ifreq[] to target user */ + argptr = lock_user(VERIFY_WRITE, target_ifc_buf, target_ifc_len, 0); + for (i = 0; i < nb_ifreq ; i++) { + thunk_convert(argptr + i * target_ifreq_size, + host_ifc_buf + i * sizeof(struct ifreq), + ifreq_arg_type, THUNK_TARGET); + } + unlock_user(argptr, target_ifc_buf, target_ifc_len); } - unlock_user(argptr, target_ifc_buf, target_ifc_len); } if (free_buf) { @@ -5383,6 +4258,182 @@ static abi_long do_ioctl_ifconf(const IOCTLEntry *ie, uint8_t *buf_temp, return ret; } +#if defined(CONFIG_USBFS) +#if HOST_LONG_BITS > 64 +#error USBDEVFS thunks do not support >64 bit hosts yet. +#endif +struct live_urb { + uint64_t target_urb_adr; + uint64_t target_buf_adr; + char *target_buf_ptr; + struct usbdevfs_urb host_urb; +}; + +static GHashTable *usbdevfs_urb_hashtable(void) +{ + static GHashTable *urb_hashtable; + + if (!urb_hashtable) { + urb_hashtable = g_hash_table_new(g_int64_hash, g_int64_equal); + } + return urb_hashtable; +} + +static void urb_hashtable_insert(struct live_urb *urb) +{ + GHashTable *urb_hashtable = usbdevfs_urb_hashtable(); + g_hash_table_insert(urb_hashtable, urb, urb); +} + +static struct live_urb *urb_hashtable_lookup(uint64_t target_urb_adr) +{ + GHashTable *urb_hashtable = usbdevfs_urb_hashtable(); + return g_hash_table_lookup(urb_hashtable, &target_urb_adr); +} + +static void urb_hashtable_remove(struct live_urb *urb) +{ + GHashTable *urb_hashtable = usbdevfs_urb_hashtable(); + g_hash_table_remove(urb_hashtable, urb); +} + +static abi_long +do_ioctl_usbdevfs_reapurb(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, int cmd, abi_long arg) +{ + const argtype usbfsurb_arg_type[] = { MK_STRUCT(STRUCT_usbdevfs_urb) }; + const argtype ptrvoid_arg_type[] = { TYPE_PTRVOID, 0, 0 }; + struct live_urb *lurb; + void *argptr; + uint64_t hurb; + int target_size; + uintptr_t target_urb_adr; + abi_long ret; + + target_size = thunk_type_size(usbfsurb_arg_type, THUNK_TARGET); + + memset(buf_temp, 0, sizeof(uint64_t)); + ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp)); + if (is_error(ret)) { + return ret; + } + + memcpy(&hurb, buf_temp, sizeof(uint64_t)); + lurb = (void *)((uintptr_t)hurb - offsetof(struct live_urb, host_urb)); + if (!lurb->target_urb_adr) { + return -TARGET_EFAULT; + } + urb_hashtable_remove(lurb); + unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, + lurb->host_urb.buffer_length); + lurb->target_buf_ptr = NULL; + + /* restore the guest buffer pointer */ + lurb->host_urb.buffer = (void *)(uintptr_t)lurb->target_buf_adr; + + /* update the guest urb struct */ + argptr = lock_user(VERIFY_WRITE, lurb->target_urb_adr, target_size, 0); + if (!argptr) { + g_free(lurb); + return -TARGET_EFAULT; + } + thunk_convert(argptr, &lurb->host_urb, usbfsurb_arg_type, THUNK_TARGET); + unlock_user(argptr, lurb->target_urb_adr, target_size); + + target_size = thunk_type_size(ptrvoid_arg_type, THUNK_TARGET); + /* write back the urb handle */ + argptr = lock_user(VERIFY_WRITE, arg, target_size, 0); + if (!argptr) { + g_free(lurb); + return -TARGET_EFAULT; + } + + /* GHashTable uses 64-bit keys but thunk_convert expects uintptr_t */ + target_urb_adr = lurb->target_urb_adr; + thunk_convert(argptr, &target_urb_adr, ptrvoid_arg_type, THUNK_TARGET); + unlock_user(argptr, arg, target_size); + + g_free(lurb); + return ret; +} + +static abi_long +do_ioctl_usbdevfs_discardurb(const IOCTLEntry *ie, + uint8_t *buf_temp __attribute__((unused)), + int fd, int cmd, abi_long arg) +{ + struct live_urb *lurb; + + /* map target address back to host URB with metadata. */ + lurb = urb_hashtable_lookup(arg); + if (!lurb) { + return -TARGET_EFAULT; + } + return get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb)); +} + +static abi_long +do_ioctl_usbdevfs_submiturb(const IOCTLEntry *ie, uint8_t *buf_temp, + int fd, int cmd, abi_long arg) +{ + const argtype *arg_type = ie->arg_type; + int target_size; + abi_long ret; + void *argptr; + int rw_dir; + struct live_urb *lurb; + + /* + * each submitted URB needs to map to a unique ID for the + * kernel, and that unique ID needs to be a pointer to + * host memory. hence, we need to malloc for each URB. + * isochronous transfers have a variable length struct. + */ + arg_type++; + target_size = thunk_type_size(arg_type, THUNK_TARGET); + + /* construct host copy of urb and metadata */ + lurb = g_try_malloc0(sizeof(struct live_urb)); + if (!lurb) { + return -TARGET_ENOMEM; + } + + argptr = lock_user(VERIFY_READ, arg, target_size, 1); + if (!argptr) { + g_free(lurb); + return -TARGET_EFAULT; + } + thunk_convert(&lurb->host_urb, argptr, arg_type, THUNK_HOST); + unlock_user(argptr, arg, 0); + + lurb->target_urb_adr = arg; + lurb->target_buf_adr = (uintptr_t)lurb->host_urb.buffer; + + /* buffer space used depends on endpoint type so lock the entire buffer */ + /* control type urbs should check the buffer contents for true direction */ + rw_dir = lurb->host_urb.endpoint & USB_DIR_IN ? VERIFY_WRITE : VERIFY_READ; + lurb->target_buf_ptr = lock_user(rw_dir, lurb->target_buf_adr, + lurb->host_urb.buffer_length, 1); + if (lurb->target_buf_ptr == NULL) { + g_free(lurb); + return -TARGET_EFAULT; + } + + /* update buffer pointer in host copy */ + lurb->host_urb.buffer = lurb->target_buf_ptr; + + ret = get_errno(safe_ioctl(fd, ie->host_cmd, &lurb->host_urb)); + if (is_error(ret)) { + unlock_user(lurb->target_buf_ptr, lurb->target_buf_adr, 0); + g_free(lurb); + } else { + urb_hashtable_insert(lurb); + } + + return ret; +} +#endif /* CONFIG_USBFS */ + static abi_long do_ioctl_dm(const IOCTLEntry *ie, uint8_t *buf_temp, int fd, int cmd, abi_long arg) { @@ -5675,8 +4726,8 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp, const int *dst_offsets, *src_offsets; int target_size; void *argptr; - abi_ulong *target_rt_dev_ptr; - unsigned long *host_rt_dev_ptr; + abi_ulong *target_rt_dev_ptr = NULL; + unsigned long *host_rt_dev_ptr = NULL; abi_long ret; int i; @@ -5722,6 +4773,9 @@ static abi_long do_ioctl_rt(const IOCTLEntry *ie, uint8_t *buf_temp, unlock_user(argptr, arg, 0); ret = get_errno(safe_ioctl(fd, ie->host_cmd, buf_temp)); + + assert(host_rt_dev_ptr != NULL); + assert(target_rt_dev_ptr != NULL); if (*host_rt_dev_ptr != 0) { unlock_user((void *)*host_rt_dev_ptr, *target_rt_dev_ptr, 0); @@ -5778,9 +4832,6 @@ static abi_long do_ioctl(int fd, int cmd, abi_long arg) ie++; } arg_type = ie->arg_type; -#if defined(DEBUG) - gemu_log("ioctl: cmd=0x%04lx (%s)\n", (long)cmd, ie->name); -#endif if (ie->do_ioctl) { return ie->do_ioctl(ie, buf_temp, fd, cmd, arg); } else if (!ie->host_cmd) { @@ -6384,7 +5435,7 @@ static void *clone_func(void *arg) cpu = ENV_GET_CPU(env); thread_cpu = cpu; ts = (TaskState *)cpu->opaque; - info->tid = gettid(); + info->tid = sys_gettid(); task_settid(ts); if (info->child_tidptr) put_user_u32(info->tid, info->child_tidptr); @@ -6529,9 +5580,9 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, mapping. We can't repeat the spinlock hack used above because the child process gets its own copy of the lock. */ if (flags & CLONE_CHILD_SETTID) - put_user_u32(gettid(), child_tidptr); + put_user_u32(sys_gettid(), child_tidptr); if (flags & CLONE_PARENT_SETTID) - put_user_u32(gettid(), parent_tidptr); + put_user_u32(sys_gettid(), parent_tidptr); ts = (TaskState *)cpu->opaque; if (flags & CLONE_SETTLS) cpu_set_tls (env, newtls); @@ -7286,6 +6337,9 @@ static inline int target_to_host_mlockall_arg(int arg) } #endif +#if (defined(TARGET_NR_stat64) || defined(TARGET_NR_lstat64) || \ + defined(TARGET_NR_fstat64) || defined(TARGET_NR_fstatat64) || \ + defined(TARGET_NR_newfstatat)) static inline abi_long host_to_target_stat64(void *cpu_env, abi_ulong target_addr, struct stat *host_st) @@ -7348,6 +6402,7 @@ 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 @@ -7490,61 +6545,6 @@ static abi_long do_open_by_handle_at(abi_long mount_fd, abi_long handle, #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; @@ -7640,7 +6640,7 @@ static int open_self_maps(void *cpu_env, int fd) if (h2g(min) == ts->info->stack_limit) { pstrcpy(path, sizeof(path), " [stack]"); } - dprintf(fd, TARGET_ABI_FMT_lx "-" TARGET_ABI_FMT_lx + dprintf(fd, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr " %c%c%c%c %08" PRIx64 " %02x:%02x %d %s%s\n", h2g(min), h2g(max - 1) + 1, flag_r, flag_w, flag_x, flag_p, offset, dev_maj, dev_min, inode, @@ -7774,9 +6774,15 @@ static int open_net_route(void *cpu_env, int fd) char iface[16]; uint32_t dest, gw, mask; unsigned int flags, refcnt, use, metric, mtu, window, irtt; - sscanf(line, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", - iface, &dest, &gw, &flags, &refcnt, &use, &metric, - &mask, &mtu, &window, &irtt); + int fields; + + fields = sscanf(line, + "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", + iface, &dest, &gw, &flags, &refcnt, &use, &metric, + &mask, &mtu, &window, &irtt); + if (fields != 11) { + continue; + } dprintf(fd, "%s\t%08x\t%08x\t%04x\t%d\t%d\t%d\t%08x\t%d\t%u\t%u\n", iface, tswap32(dest), tswap32(gw), flags, refcnt, use, metric, tswap32(mask), mtu, window, irtt); @@ -7870,55 +6876,6 @@ static target_timer_t get_timer_id(abi_long arg) return timerid; } -static abi_long swap_data_eventfd(void *buf, size_t len) -{ - uint64_t *counter = buf; - int i; - - if (len < sizeof(uint64_t)) { - return -EINVAL; - } - - for (i = 0; i < len; i += sizeof(uint64_t)) { - *counter = tswap64(*counter); - counter++; - } - - return len; -} - -static TargetFdTrans target_eventfd_trans = { - .host_to_target_data = swap_data_eventfd, - .target_to_host_data = swap_data_eventfd, -}; - -#if (defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init)) || \ - (defined(CONFIG_INOTIFY1) && defined(TARGET_NR_inotify_init1) && \ - defined(__NR_inotify_init1)) -static abi_long host_to_target_data_inotify(void *buf, size_t len) -{ - struct inotify_event *ev; - int i; - uint32_t name_len; - - for (i = 0; i < len; i += sizeof(struct inotify_event) + name_len) { - ev = (struct inotify_event *)((char *)buf + i); - name_len = ev->len; - - ev->wd = tswap32(ev->wd); - ev->mask = tswap32(ev->mask); - ev->cookie = tswap32(ev->cookie); - ev->len = tswap32(name_len); - } - - return len; -} - -static TargetFdTrans target_inotify_trans = { - .host_to_target_data = host_to_target_data_inotify, -}; -#endif - static int target_to_host_cpu_mask(unsigned long *host_mask, size_t host_size, abi_ulong target_addr, @@ -7986,42 +6943,29 @@ static int host_to_target_cpu_mask(const unsigned long *host_mask, return 0; } -/* do_syscall() should always have a single exit point at the end so - that actions, such as logging of syscall results, can be performed. - All errnos that do_syscall() returns must be -TARGET_. */ -abi_long do_syscall(void *cpu_env, int num, abi_long arg1, - abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6, abi_long arg7, - abi_long arg8) +/* This is an internal helper for do_syscall so that it is easier + * to have a single return point, so that actions, such as logging + * of syscall results, can be performed. + * All errnos that do_syscall() returns must be -TARGET_. + */ +static abi_long do_syscall1(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) { CPUState *cpu = ENV_GET_CPU(cpu_env); abi_long ret; +#if defined(TARGET_NR_stat) || defined(TARGET_NR_stat64) \ + || defined(TARGET_NR_lstat) || defined(TARGET_NR_lstat64) \ + || defined(TARGET_NR_fstat) || defined(TARGET_NR_fstat64) struct stat st; +#endif +#if defined(TARGET_NR_statfs) || defined(TARGET_NR_statfs64) \ + || defined(TARGET_NR_fstatfs) struct statfs stfs; +#endif void *p; -#if defined(DEBUG_ERESTARTSYS) - /* Debug-only code for exercising the syscall-restart code paths - * in the per-architecture cpu main loops: restart every syscall - * the guest makes once before letting it through. - */ - { - static int flag; - - flag = !flag; - if (flag) { - return -TARGET_ERESTARTSYS; - } - } -#endif - -#ifdef DEBUG - gemu_log("syscall %d", num); -#endif - trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8); - if(do_strace) - print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); - switch(num) { case TARGET_NR_exit: /* In old applications this may be used to implement _exit(2). @@ -8030,8 +6974,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, Do thread termination if we have more then one thread. */ if (block_signals()) { - ret = -TARGET_ERESTARTSYS; - break; + return -TARGET_ERESTARTSYS; } cpu_list_lock(); @@ -8040,7 +6983,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, TaskState *ts; /* Remove the CPU from the list. */ - QTAILQ_REMOVE(&cpus, cpu, node); + QTAILQ_REMOVE_RCU(&cpus, cpu, node); cpu_list_unlock(); @@ -8060,14 +7003,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, cpu_list_unlock(); preexit_cleanup(cpu_env, arg1); _exit(arg1); - ret = 0; /* avoid warning */ - break; + return 0; /* avoid warning */ case TARGET_NR_read: - if (arg3 == 0) - ret = 0; - else { + if (arg2 == 0 && arg3 == 0) { + return get_errno(safe_read(arg1, 0, 0)); + } else { if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(safe_read(arg1, p, arg3)); if (ret >= 0 && fd_trans_host_to_target_data(arg1)) { @@ -8075,10 +7017,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user(p, arg2, ret); } - break; + return ret; case TARGET_NR_write: + if (arg2 == 0 && arg3 == 0) { + return get_errno(safe_write(arg1, 0, 0)); + } if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; + return -TARGET_EFAULT; if (fd_trans_target_to_host_data(arg1)) { void *copy = g_malloc(arg3); memcpy(copy, p, arg3); @@ -8091,49 +7036,48 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(safe_write(arg1, p, arg3)); } unlock_user(p, arg2, 0); - break; + return ret; + #ifdef TARGET_NR_open case TARGET_NR_open: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_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; + return ret; #endif case TARGET_NR_openat: if (!(p = lock_user_string(arg2))) - goto efault; + return -TARGET_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; + return ret; #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; + return ret; #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; + return ret; #endif case TARGET_NR_close: fd_trans_unregister(arg1); - ret = get_errno(close(arg1)); - break; + return get_errno(close(arg1)); + case TARGET_NR_brk: - ret = do_brk(arg1); - break; + return do_brk(arg1); #ifdef TARGET_NR_fork case TARGET_NR_fork: - ret = get_errno(do_fork(cpu_env, TARGET_SIGCHLD, 0, 0, 0, 0)); - break; + return get_errno(do_fork(cpu_env, TARGET_SIGCHLD, 0, 0, 0, 0)); #endif #ifdef TARGET_NR_waitpid case TARGET_NR_waitpid: @@ -8142,9 +7086,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(safe_wait4(arg1, &status, arg3, 0)); if (!is_error(ret) && arg2 && ret && put_user_s32(host_to_target_waitstatus(status), arg2)) - goto efault; + return -TARGET_EFAULT; } - break; + return ret; #endif #ifdef TARGET_NR_waitid case TARGET_NR_waitid: @@ -8154,21 +7098,21 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(safe_waitid(arg1, arg2, &info, arg4, NULL)); if (!is_error(ret) && arg3 && info.si_pid != 0) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_siginfo_t), 0))) - goto efault; + return -TARGET_EFAULT; host_to_target_siginfo(p, &info); unlock_user(p, arg3, sizeof(target_siginfo_t)); } } - break; + return ret; #endif #ifdef TARGET_NR_creat /* not on alpha */ case TARGET_NR_creat: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(creat(p, arg2)); fd_trans_unregister(ret); unlock_user(p, arg1, 0); - break; + return ret; #endif #ifdef TARGET_NR_link case TARGET_NR_link: @@ -8183,14 +7127,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg2, 0); unlock_user(p, arg1, 0); } - break; + return ret; #endif #if defined(TARGET_NR_linkat) case TARGET_NR_linkat: { void * p2 = NULL; if (!arg2 || !arg4) - goto efault; + return -TARGET_EFAULT; p = lock_user_string(arg2); p2 = lock_user_string(arg4); if (!p || !p2) @@ -8200,23 +7144,23 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg2, 0); unlock_user(p2, arg4, 0); } - break; + return ret; #endif #ifdef TARGET_NR_unlink case TARGET_NR_unlink: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(unlink(p)); unlock_user(p, arg1, 0); - break; + return ret; #endif #if defined(TARGET_NR_unlinkat) case TARGET_NR_unlinkat: if (!(p = lock_user_string(arg2))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(unlinkat(arg1, p, arg3)); unlock_user(p, arg2, 0); - break; + return ret; #endif case TARGET_NR_execve: { @@ -8233,7 +7177,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, guest_argp = arg2; for (gp = guest_argp; gp; gp += sizeof(abi_ulong)) { if (get_user_ual(addr, gp)) - goto efault; + return -TARGET_EFAULT; if (!addr) break; argc++; @@ -8242,7 +7186,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, guest_envp = arg3; for (gp = guest_envp; gp; gp += sizeof(abi_ulong)) { if (get_user_ual(addr, gp)) - goto efault; + return -TARGET_EFAULT; if (!addr) break; envc++; @@ -8314,13 +7258,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, g_free(argp); g_free(envp); } - break; + return ret; case TARGET_NR_chdir: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(chdir(p)); unlock_user(p, arg1, 0); - break; + return ret; #ifdef TARGET_NR_time case TARGET_NR_time: { @@ -8329,56 +7273,47 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret) && arg1 && put_user_sal(host_time, arg1)) - goto efault; + return -TARGET_EFAULT; } - break; + return ret; #endif #ifdef TARGET_NR_mknod case TARGET_NR_mknod: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(mknod(p, arg2, arg3)); unlock_user(p, arg1, 0); - break; + return ret; #endif #if defined(TARGET_NR_mknodat) case TARGET_NR_mknodat: if (!(p = lock_user_string(arg2))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(mknodat(arg1, p, arg3, arg4)); unlock_user(p, arg2, 0); - break; + return ret; #endif #ifdef TARGET_NR_chmod case TARGET_NR_chmod: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(chmod(p, arg2)); unlock_user(p, arg1, 0); - break; -#endif -#ifdef TARGET_NR_break - case TARGET_NR_break: - goto unimplemented; -#endif -#ifdef TARGET_NR_oldstat - case TARGET_NR_oldstat: - goto unimplemented; + return ret; #endif +#ifdef TARGET_NR_lseek case TARGET_NR_lseek: - ret = get_errno(lseek(arg1, arg2, arg3)); - break; + return get_errno(lseek(arg1, arg2, arg3)); +#endif #if defined(TARGET_NR_getxpid) && defined(TARGET_ALPHA) /* Alpha specific */ case TARGET_NR_getxpid: ((CPUAlphaState *)cpu_env)->ir[IR_A4] = getppid(); - ret = get_errno(getpid()); - break; + return get_errno(getpid()); #endif #ifdef TARGET_NR_getpid case TARGET_NR_getpid: - ret = get_errno(getpid()); - break; + return get_errno(getpid()); #endif case TARGET_NR_mount: { @@ -8388,7 +7323,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg1) { p = lock_user_string(arg1); if (!p) { - goto efault; + return -TARGET_EFAULT; } } else { p = NULL; @@ -8399,7 +7334,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg1) { unlock_user(p, arg1, 0); } - goto efault; + return -TARGET_EFAULT; } if (arg3) { @@ -8409,7 +7344,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); } unlock_user(p2, arg2, 0); - goto efault; + return -TARGET_EFAULT; } } else { p3 = NULL; @@ -8434,43 +7369,34 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p3, arg3, 0); } } - break; + return ret; #ifdef TARGET_NR_umount case TARGET_NR_umount: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(umount(p)); unlock_user(p, arg1, 0); - break; + return ret; #endif #ifdef TARGET_NR_stime /* not on alpha */ case TARGET_NR_stime: { time_t host_time; if (get_user_sal(host_time, arg1)) - goto efault; - ret = get_errno(stime(&host_time)); + return -TARGET_EFAULT; + return get_errno(stime(&host_time)); } - break; #endif - case TARGET_NR_ptrace: - goto unimplemented; #ifdef TARGET_NR_alarm /* not on alpha */ case TARGET_NR_alarm: - ret = alarm(arg1); - break; -#endif -#ifdef TARGET_NR_oldfstat - case TARGET_NR_oldfstat: - goto unimplemented; + return alarm(arg1); #endif #ifdef TARGET_NR_pause /* not on alpha */ case TARGET_NR_pause: if (!block_signals()) { sigsuspend(&((TaskState *)cpu->opaque)->signal_mask); } - ret = -TARGET_EINTR; - break; + return -TARGET_EINTR; #endif #ifdef TARGET_NR_utime case TARGET_NR_utime: @@ -8479,7 +7405,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_utimbuf *target_tbuf; if (arg2) { if (!lock_user_struct(VERIFY_READ, target_tbuf, arg2, 1)) - goto efault; + return -TARGET_EFAULT; tbuf.actime = tswapal(target_tbuf->actime); tbuf.modtime = tswapal(target_tbuf->modtime); unlock_user_struct(target_tbuf, arg2, 0); @@ -8488,11 +7414,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_tbuf = NULL; } if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(utime(p, host_tbuf)); unlock_user(p, arg1, 0); } - break; + return ret; #endif #ifdef TARGET_NR_utimes case TARGET_NR_utimes: @@ -8502,17 +7428,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (copy_from_user_timeval(&tv[0], arg2) || copy_from_user_timeval(&tv[1], arg2 + sizeof(struct target_timeval))) - goto efault; + return -TARGET_EFAULT; tvp = tv; } else { tvp = NULL; } if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(utimes(p, tvp)); unlock_user(p, arg1, 0); } - break; + return ret; #endif #if defined(TARGET_NR_futimesat) case TARGET_NR_futimesat: @@ -8522,63 +7448,50 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (copy_from_user_timeval(&tv[0], arg3) || copy_from_user_timeval(&tv[1], arg3 + sizeof(struct target_timeval))) - goto efault; + return -TARGET_EFAULT; tvp = tv; } else { tvp = NULL; } - if (!(p = lock_user_string(arg2))) - goto efault; + if (!(p = lock_user_string(arg2))) { + return -TARGET_EFAULT; + } ret = get_errno(futimesat(arg1, path(p), tvp)); unlock_user(p, arg2, 0); } - break; -#endif -#ifdef TARGET_NR_stty - case TARGET_NR_stty: - goto unimplemented; -#endif -#ifdef TARGET_NR_gtty - case TARGET_NR_gtty: - goto unimplemented; + return ret; #endif #ifdef TARGET_NR_access case TARGET_NR_access: - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(access(path(p), arg2)); unlock_user(p, arg1, 0); - break; + return ret; #endif #if defined(TARGET_NR_faccessat) && defined(__NR_faccessat) case TARGET_NR_faccessat: - if (!(p = lock_user_string(arg2))) - goto efault; + if (!(p = lock_user_string(arg2))) { + return -TARGET_EFAULT; + } ret = get_errno(faccessat(arg1, p, arg3, 0)); unlock_user(p, arg2, 0); - break; + return ret; #endif #ifdef TARGET_NR_nice /* not on alpha */ case TARGET_NR_nice: - ret = get_errno(nice(arg1)); - break; -#endif -#ifdef TARGET_NR_ftime - case TARGET_NR_ftime: - goto unimplemented; + return get_errno(nice(arg1)); #endif case TARGET_NR_sync: sync(); - ret = 0; - break; + return 0; #if defined(TARGET_NR_syncfs) && defined(CONFIG_SYNCFS) case TARGET_NR_syncfs: - ret = get_errno(syncfs(arg1)); - break; + return get_errno(syncfs(arg1)); #endif case TARGET_NR_kill: - ret = get_errno(safe_kill(arg1, target_to_host_signal(arg2))); - break; + return get_errno(safe_kill(arg1, target_to_host_signal(arg2))); #ifdef TARGET_NR_rename case TARGET_NR_rename: { @@ -8592,7 +7505,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg2, 0); unlock_user(p, arg1, 0); } - break; + return ret; #endif #if defined(TARGET_NR_renameat) case TARGET_NR_renameat: @@ -8607,7 +7520,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg4, 0); unlock_user(p, arg2, 0); } - break; + return ret; #endif #if defined(TARGET_NR_renameat2) case TARGET_NR_renameat2: @@ -8623,48 +7536,46 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg4, 0); unlock_user(p, arg2, 0); } - break; + return ret; #endif #ifdef TARGET_NR_mkdir case TARGET_NR_mkdir: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(mkdir(p, arg2)); unlock_user(p, arg1, 0); - break; + return ret; #endif #if defined(TARGET_NR_mkdirat) case TARGET_NR_mkdirat: if (!(p = lock_user_string(arg2))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(mkdirat(arg1, p, arg3)); unlock_user(p, arg2, 0); - break; + return ret; #endif #ifdef TARGET_NR_rmdir case TARGET_NR_rmdir: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(rmdir(p)); unlock_user(p, arg1, 0); - break; + return ret; #endif case TARGET_NR_dup: ret = get_errno(dup(arg1)); if (ret >= 0) { fd_trans_dup(arg1, ret); } - break; + return ret; #ifdef TARGET_NR_pipe case TARGET_NR_pipe: - ret = do_pipe(cpu_env, arg1, 0, 0); - break; + return do_pipe(cpu_env, arg1, 0, 0); #endif #ifdef TARGET_NR_pipe2 case TARGET_NR_pipe2: - ret = do_pipe(cpu_env, arg1, - target_to_host_bitmask(arg2, fcntl_flags_tbl), 1); - break; + return do_pipe(cpu_env, arg1, + target_to_host_bitmask(arg2, fcntl_flags_tbl), 1); #endif case TARGET_NR_times: { @@ -8674,7 +7585,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg1) { tmsp = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_tms), 0); if (!tmsp) - goto efault; + return -TARGET_EFAULT; tmsp->tms_utime = tswapal(host_to_target_clock_t(tms.tms_utime)); tmsp->tms_stime = tswapal(host_to_target_clock_t(tms.tms_stime)); tmsp->tms_cutime = tswapal(host_to_target_clock_t(tms.tms_cutime)); @@ -8683,80 +7594,49 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) ret = host_to_target_clock_t(ret); } - break; -#ifdef TARGET_NR_prof - case TARGET_NR_prof: - goto unimplemented; -#endif -#ifdef TARGET_NR_signal - case TARGET_NR_signal: - goto unimplemented; -#endif + return ret; case TARGET_NR_acct: if (arg1 == 0) { ret = get_errno(acct(NULL)); } else { - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(acct(path(p))); unlock_user(p, arg1, 0); } - break; + return ret; #ifdef TARGET_NR_umount2 case TARGET_NR_umount2: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(umount2(p, arg2)); unlock_user(p, arg1, 0); - break; -#endif -#ifdef TARGET_NR_lock - case TARGET_NR_lock: - goto unimplemented; + return ret; #endif case TARGET_NR_ioctl: - ret = do_ioctl(arg1, arg2, arg3); - break; + return do_ioctl(arg1, arg2, arg3); #ifdef TARGET_NR_fcntl case TARGET_NR_fcntl: - ret = do_fcntl(arg1, arg2, arg3); - break; -#endif -#ifdef TARGET_NR_mpx - case TARGET_NR_mpx: - goto unimplemented; + return do_fcntl(arg1, arg2, arg3); #endif case TARGET_NR_setpgid: - ret = get_errno(setpgid(arg1, arg2)); - break; -#ifdef TARGET_NR_ulimit - case TARGET_NR_ulimit: - goto unimplemented; -#endif -#ifdef TARGET_NR_oldolduname - case TARGET_NR_oldolduname: - goto unimplemented; -#endif + return get_errno(setpgid(arg1, arg2)); case TARGET_NR_umask: - ret = get_errno(umask(arg1)); - break; + return get_errno(umask(arg1)); case TARGET_NR_chroot: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(chroot(p)); unlock_user(p, arg1, 0); - break; -#ifdef TARGET_NR_ustat - case TARGET_NR_ustat: - goto unimplemented; -#endif + return ret; #ifdef TARGET_NR_dup2 case TARGET_NR_dup2: ret = get_errno(dup2(arg1, arg2)); if (ret >= 0) { fd_trans_dup(arg1, arg2); } - break; + return ret; #endif #if defined(CONFIG_DUP3) && defined(TARGET_NR_dup3) case TARGET_NR_dup3: @@ -8771,22 +7651,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret >= 0) { fd_trans_dup(arg1, arg2); } - break; + return ret; } #endif #ifdef TARGET_NR_getppid /* not on alpha */ case TARGET_NR_getppid: - ret = get_errno(getppid()); - break; + return get_errno(getppid()); #endif #ifdef TARGET_NR_getpgrp case TARGET_NR_getpgrp: - ret = get_errno(getpgrp()); - break; + return get_errno(getpgrp()); #endif case TARGET_NR_setsid: - ret = get_errno(setsid()); - break; + return get_errno(setsid()); #ifdef TARGET_NR_sigaction case TARGET_NR_sigaction: { @@ -8795,7 +7672,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_old_sigaction *old_act; if (arg2) { if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1)) - goto efault; + return -TARGET_EFAULT; act._sa_handler = old_act->_sa_handler; target_siginitset(&act.sa_mask, old_act->sa_mask); act.sa_flags = old_act->sa_flags; @@ -8806,7 +7683,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_sigaction(arg1, pact, &oact)); if (!is_error(ret) && arg3) { if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0)) - goto efault; + return -TARGET_EFAULT; old_act->_sa_handler = oact._sa_handler; old_act->sa_mask = oact.sa_mask.sig[0]; old_act->sa_flags = oact.sa_flags; @@ -8817,7 +7694,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg2) { if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1)) - goto efault; + return -TARGET_EFAULT; act._sa_handler = old_act->_sa_handler; target_siginitset(&act.sa_mask, old_act->sa_mask.sig[0]); act.sa_flags = old_act->sa_flags; @@ -8831,7 +7708,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret) && arg3) { if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0)) - goto efault; + return -TARGET_EFAULT; old_act->_sa_handler = oact._sa_handler; old_act->sa_flags = oact.sa_flags; old_act->sa_mask.sig[0] = oact.sa_mask.sig[0]; @@ -8845,7 +7722,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_sigaction act, oact, *pact; if (arg2) { if (!lock_user_struct(VERIFY_READ, old_act, arg2, 1)) - goto efault; + return -TARGET_EFAULT; act._sa_handler = old_act->_sa_handler; target_siginitset(&act.sa_mask, old_act->sa_mask); act.sa_flags = old_act->sa_flags; @@ -8861,7 +7738,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_sigaction(arg1, pact, &oact)); if (!is_error(ret) && arg3) { if (!lock_user_struct(VERIFY_WRITE, old_act, arg3, 0)) - goto efault; + return -TARGET_EFAULT; old_act->_sa_handler = oact._sa_handler; old_act->sa_mask = oact.sa_mask.sig[0]; old_act->sa_flags = oact.sa_flags; @@ -8870,7 +7747,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } #endif } - break; + return ret; #endif case TARGET_NR_rt_sigaction: { @@ -8887,12 +7764,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_sigaction act, oact, *pact = 0; if (arg4 != sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } if (arg2) { if (!lock_user_struct(VERIFY_READ, rt_act, arg2, 1)) - goto efault; + return -TARGET_EFAULT; act._sa_handler = rt_act->_sa_handler; act.sa_mask = rt_act->sa_mask; act.sa_flags = rt_act->sa_flags; @@ -8903,7 +7779,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(do_sigaction(arg1, pact, &oact)); if (!is_error(ret) && arg3) { if (!lock_user_struct(VERIFY_WRITE, rt_act, arg3, 0)) - goto efault; + return -TARGET_EFAULT; rt_act->_sa_handler = oact._sa_handler; rt_act->sa_mask = oact.sa_mask; rt_act->sa_flags = oact.sa_flags; @@ -8920,12 +7796,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_sigaction *oact; if (sigsetsize != sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } if (arg2) { if (!lock_user_struct(VERIFY_READ, act, arg2, 1)) { - goto efault; + return -TARGET_EFAULT; } #ifdef TARGET_ARCH_HAS_KA_RESTORER act->ka_restorer = restorer; @@ -8948,7 +7823,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user_struct(oact, arg3, 1); #endif } - break; + return ret; #ifdef TARGET_NR_sgetmask /* not on alpha */ case TARGET_NR_sgetmask: { @@ -8960,7 +7835,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = target_set; } } - break; + return ret; #endif #ifdef TARGET_NR_ssetmask /* not on alpha */ case TARGET_NR_ssetmask: @@ -8974,7 +7849,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = target_set; } } - break; + return ret; #endif #ifdef TARGET_NR_sigprocmask case TARGET_NR_sigprocmask: @@ -8995,8 +7870,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, how = SIG_SETMASK; break; default: - ret = -TARGET_EINVAL; - goto fail; + return -TARGET_EINVAL; } mask = arg2; target_to_host_old_sigset(&set, &mask); @@ -9023,11 +7897,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, how = SIG_SETMASK; break; default: - ret = -TARGET_EINVAL; - goto fail; + return -TARGET_EINVAL; } if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1))) - goto efault; + return -TARGET_EFAULT; target_to_host_old_sigset(&set, p); unlock_user(p, arg2, 0); set_ptr = &set; @@ -9038,13 +7911,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_sigprocmask(how, set_ptr, &oldset); if (!is_error(ret) && arg3) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0))) - goto efault; + return -TARGET_EFAULT; host_to_target_old_sigset(p, &oldset); unlock_user(p, arg3, sizeof(target_sigset_t)); } #endif } - break; + return ret; #endif case TARGET_NR_rt_sigprocmask: { @@ -9052,8 +7925,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, sigset_t set, oldset, *set_ptr; if (arg4 != sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } if (arg2) { @@ -9068,11 +7940,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, how = SIG_SETMASK; break; default: - ret = -TARGET_EINVAL; - goto fail; + return -TARGET_EINVAL; } if (!(p = lock_user(VERIFY_READ, arg2, sizeof(target_sigset_t), 1))) - goto efault; + return -TARGET_EFAULT; target_to_host_sigset(&set, p); unlock_user(p, arg2, 0); set_ptr = &set; @@ -9083,12 +7954,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_sigprocmask(how, set_ptr, &oldset); if (!is_error(ret) && arg3) { if (!(p = lock_user(VERIFY_WRITE, arg3, sizeof(target_sigset_t), 0))) - goto efault; + return -TARGET_EFAULT; host_to_target_sigset(p, &oldset); unlock_user(p, arg3, sizeof(target_sigset_t)); } } - break; + return ret; #ifdef TARGET_NR_sigpending case TARGET_NR_sigpending: { @@ -9096,12 +7967,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(sigpending(&set)); if (!is_error(ret)) { if (!(p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0))) - goto efault; + return -TARGET_EFAULT; host_to_target_old_sigset(p, &set); unlock_user(p, arg1, sizeof(target_sigset_t)); } } - break; + return ret; #endif case TARGET_NR_rt_sigpending: { @@ -9113,19 +7984,18 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, * the old_sigset_t is smaller in size. */ if (arg2 > sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } ret = get_errno(sigpending(&set)); if (!is_error(ret)) { if (!(p = lock_user(VERIFY_WRITE, arg1, sizeof(target_sigset_t), 0))) - goto efault; + return -TARGET_EFAULT; host_to_target_sigset(p, &set); unlock_user(p, arg1, sizeof(target_sigset_t)); } } - break; + return ret; #ifdef TARGET_NR_sigsuspend case TARGET_NR_sigsuspend: { @@ -9135,7 +8005,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_to_host_old_sigset(&ts->sigsuspend_mask, &mask); #else if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) - goto efault; + return -TARGET_EFAULT; target_to_host_old_sigset(&ts->sigsuspend_mask, p); unlock_user(p, arg1, 0); #endif @@ -9145,18 +8015,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ts->in_sigsuspend = 1; } } - break; + return ret; #endif case TARGET_NR_rt_sigsuspend: { TaskState *ts = cpu->opaque; if (arg2 != sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) - goto efault; + return -TARGET_EFAULT; target_to_host_sigset(&ts->sigsuspend_mask, p); unlock_user(p, arg1, 0); ret = get_errno(safe_rt_sigsuspend(&ts->sigsuspend_mask, @@ -9165,7 +8034,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ts->in_sigsuspend = 1; } } - break; + return ret; case TARGET_NR_rt_sigtimedwait: { sigset_t set; @@ -9173,12 +8042,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, siginfo_t uinfo; if (arg4 != sizeof(target_sigset_t)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } if (!(p = lock_user(VERIFY_READ, arg1, sizeof(target_sigset_t), 1))) - goto efault; + return -TARGET_EFAULT; target_to_host_sigset(&set, p); unlock_user(p, arg1, 0); if (arg3) { @@ -9194,7 +8062,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, p = lock_user(VERIFY_WRITE, arg2, sizeof(target_siginfo_t), 0); if (!p) { - goto efault; + return -TARGET_EFAULT; } host_to_target_siginfo(p, &uinfo); unlock_user(p, arg2, sizeof(target_siginfo_t)); @@ -9202,68 +8070,80 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = host_to_target_signal(ret); } } - break; + return ret; case TARGET_NR_rt_sigqueueinfo: { siginfo_t uinfo; p = lock_user(VERIFY_READ, arg3, sizeof(target_siginfo_t), 1); if (!p) { - goto efault; + return -TARGET_EFAULT; } target_to_host_siginfo(&uinfo, p); unlock_user(p, arg3, 0); ret = get_errno(sys_rt_sigqueueinfo(arg1, arg2, &uinfo)); } - break; + return ret; case TARGET_NR_rt_tgsigqueueinfo: { siginfo_t uinfo; p = lock_user(VERIFY_READ, arg4, sizeof(target_siginfo_t), 1); if (!p) { - goto efault; + return -TARGET_EFAULT; } target_to_host_siginfo(&uinfo, p); unlock_user(p, arg4, 0); ret = get_errno(sys_rt_tgsigqueueinfo(arg1, arg2, arg3, &uinfo)); } - break; + return ret; #ifdef TARGET_NR_sigreturn case TARGET_NR_sigreturn: if (block_signals()) { - ret = -TARGET_ERESTARTSYS; - } else { - ret = do_sigreturn(cpu_env); + return -TARGET_ERESTARTSYS; } - break; + return do_sigreturn(cpu_env); #endif case TARGET_NR_rt_sigreturn: if (block_signals()) { - ret = -TARGET_ERESTARTSYS; - } else { - ret = do_rt_sigreturn(cpu_env); + return -TARGET_ERESTARTSYS; } - break; + return do_rt_sigreturn(cpu_env); case TARGET_NR_sethostname: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(sethostname(p, arg2)); unlock_user(p, arg1, 0); - break; + return ret; +#ifdef TARGET_NR_setrlimit case TARGET_NR_setrlimit: { int resource = target_to_host_resource(arg1); struct target_rlimit *target_rlim; struct rlimit rlim; if (!lock_user_struct(VERIFY_READ, target_rlim, arg2, 1)) - goto efault; + return -TARGET_EFAULT; rlim.rlim_cur = target_to_host_rlim(target_rlim->rlim_cur); rlim.rlim_max = target_to_host_rlim(target_rlim->rlim_max); unlock_user_struct(target_rlim, arg2, 0); - ret = get_errno(setrlimit(resource, &rlim)); + /* + * If we just passed through resource limit settings for memory then + * they would also apply to QEMU's own allocations, and QEMU will + * crash or hang or die if its allocations fail. Ideally we would + * track the guest allocations in QEMU and apply the limits ourselves. + * For now, just tell the guest the call succeeded but don't actually + * limit anything. + */ + if (resource != RLIMIT_AS && + resource != RLIMIT_DATA && + resource != RLIMIT_STACK) { + return get_errno(setrlimit(resource, &rlim)); + } else { + return 0; + } } - break; +#endif +#ifdef TARGET_NR_getrlimit case TARGET_NR_getrlimit: { int resource = target_to_host_resource(arg1); @@ -9273,13 +8153,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(getrlimit(resource, &rlim)); if (!is_error(ret)) { if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) - goto efault; + return -TARGET_EFAULT; target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur); target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max); unlock_user_struct(target_rlim, arg2, 1); } } - break; + return ret; +#endif case TARGET_NR_getrusage: { struct rusage rusage; @@ -9288,17 +8169,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = host_to_target_rusage(arg2, &rusage); } } - break; + return ret; case TARGET_NR_gettimeofday: { struct timeval tv; ret = get_errno(gettimeofday(&tv, NULL)); if (!is_error(ret)) { if (copy_to_user_timeval(arg1, &tv)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; case TARGET_NR_settimeofday: { struct timeval tv, *ptv = NULL; @@ -9306,21 +8187,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg1) { if (copy_from_user_timeval(&tv, arg1)) { - goto efault; + return -TARGET_EFAULT; } ptv = &tv; } if (arg2) { if (copy_from_user_timezone(&tz, arg2)) { - goto efault; + return -TARGET_EFAULT; } ptz = &tz; } - ret = get_errno(settimeofday(ptv, ptz)); + return get_errno(settimeofday(ptv, ptz)); } - break; #if defined(TARGET_NR_select) case TARGET_NR_select: #if defined(TARGET_WANT_NI_OLD_SELECT) @@ -9333,7 +8213,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else ret = do_select(arg1, arg2, arg3, arg4, arg5); #endif - break; + return ret; #endif #ifdef TARGET_NR_pselect6 case TARGET_NR_pselect6: @@ -9364,15 +8244,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = copy_from_user_fdset_ptr(&rfds, &rfds_ptr, rfd_addr, n); if (ret) { - goto fail; + return ret; } ret = copy_from_user_fdset_ptr(&wfds, &wfds_ptr, wfd_addr, n); if (ret) { - goto fail; + return ret; } ret = copy_from_user_fdset_ptr(&efds, &efds_ptr, efd_addr, n); if (ret) { - goto fail; + return ret; } /* @@ -9381,7 +8261,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, */ if (ts_addr) { if (target_to_host_timespec(&ts, ts_addr)) { - goto efault; + return -TARGET_EFAULT; } ts_ptr = &ts; } else { @@ -9395,7 +8275,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg7 = lock_user(VERIFY_READ, arg6, sizeof(*arg7) * 2, 1); if (!arg7) { - goto efault; + return -TARGET_EFAULT; } arg_sigset = tswapal(arg7[0]); arg_sigsize = tswapal(arg7[1]); @@ -9405,13 +8285,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, sig.set = &set; if (arg_sigsize != sizeof(*target_sigset)) { /* Like the kernel, we enforce correct size sigsets */ - ret = -TARGET_EINVAL; - goto fail; + return -TARGET_EINVAL; } target_sigset = lock_user(VERIFY_READ, arg_sigset, sizeof(*target_sigset), 1); if (!target_sigset) { - goto efault; + return -TARGET_EFAULT; } target_to_host_sigset(&set, target_sigset); unlock_user(target_sigset, arg_sigset, 0); @@ -9427,17 +8306,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { if (rfd_addr && copy_to_user_fdset(rfd_addr, &rfds, n)) - goto efault; + return -TARGET_EFAULT; if (wfd_addr && copy_to_user_fdset(wfd_addr, &wfds, n)) - goto efault; + return -TARGET_EFAULT; if (efd_addr && copy_to_user_fdset(efd_addr, &efds, n)) - goto efault; + return -TARGET_EFAULT; if (ts_addr && host_to_target_timespec(ts_addr, &ts)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #ifdef TARGET_NR_symlink case TARGET_NR_symlink: @@ -9452,7 +8331,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg2, 0); unlock_user(p, arg1, 0); } - break; + return ret; #endif #if defined(TARGET_NR_symlinkat) case TARGET_NR_symlinkat: @@ -9467,11 +8346,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg3, 0); unlock_user(p, arg1, 0); } - break; -#endif -#ifdef TARGET_NR_oldlstat - case TARGET_NR_oldlstat: - goto unimplemented; + return ret; #endif #ifdef TARGET_NR_readlink case TARGET_NR_readlink: @@ -9503,7 +8378,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg2, ret); unlock_user(p, arg1, 0); } - break; + return ret; #endif #if defined(TARGET_NR_readlinkat) case TARGET_NR_readlinkat: @@ -9524,37 +8399,29 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p2, arg3, ret); unlock_user(p, arg2, 0); } - break; -#endif -#ifdef TARGET_NR_uselib - case TARGET_NR_uselib: - goto unimplemented; + return ret; #endif #ifdef TARGET_NR_swapon case TARGET_NR_swapon: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(swapon(p, arg2)); unlock_user(p, arg1, 0); - break; + return ret; #endif case TARGET_NR_reboot: if (arg3 == LINUX_REBOOT_CMD_RESTART2) { /* arg4 must be ignored in all other cases */ p = lock_user_string(arg4); if (!p) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(reboot(arg1, arg2, arg3, p)); unlock_user(p, arg4, 0); } else { ret = get_errno(reboot(arg1, arg2, arg3, NULL)); } - break; -#ifdef TARGET_NR_readdir - case TARGET_NR_readdir: - goto unimplemented; -#endif + return ret; #ifdef TARGET_NR_mmap case TARGET_NR_mmap: #if (defined(TARGET_I386) && defined(TARGET_ABI32)) || \ @@ -9565,7 +8432,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_ulong *v; abi_ulong v1, v2, v3, v4, v5, v6; if (!(v = lock_user(VERIFY_READ, arg1, 6 * sizeof(abi_ulong), 1))) - goto efault; + return -TARGET_EFAULT; v1 = tswapal(v[0]); v2 = tswapal(v[1]); v3 = tswapal(v[2]); @@ -9583,22 +8450,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg5, arg6)); #endif - break; + return ret; #endif #ifdef TARGET_NR_mmap2 case TARGET_NR_mmap2: #ifndef MMAP_SHIFT #define MMAP_SHIFT 12 #endif - ret = get_errno(target_mmap(arg1, arg2, arg3, - target_to_host_bitmask(arg4, mmap_flags_tbl), - arg5, - arg6 << MMAP_SHIFT)); - break; + ret = target_mmap(arg1, arg2, arg3, + target_to_host_bitmask(arg4, mmap_flags_tbl), + arg5, arg6 << MMAP_SHIFT); + return get_errno(ret); #endif case TARGET_NR_munmap: - ret = get_errno(target_munmap(arg1, arg2)); - break; + return get_errno(target_munmap(arg1, arg2)); case TARGET_NR_mprotect: { TaskState *ts = cpu->opaque; @@ -9611,58 +8476,53 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg1 = ts->info->stack_limit; } } - ret = get_errno(target_mprotect(arg1, arg2, arg3)); - break; + return get_errno(target_mprotect(arg1, arg2, arg3)); #ifdef TARGET_NR_mremap case TARGET_NR_mremap: - ret = get_errno(target_mremap(arg1, arg2, arg3, arg4, arg5)); - break; + return get_errno(target_mremap(arg1, arg2, arg3, arg4, arg5)); #endif /* ??? msync/mlock/munlock are broken for softmmu. */ #ifdef TARGET_NR_msync case TARGET_NR_msync: - ret = get_errno(msync(g2h(arg1), arg2, arg3)); - break; + return get_errno(msync(g2h(arg1), arg2, arg3)); #endif #ifdef TARGET_NR_mlock case TARGET_NR_mlock: - ret = get_errno(mlock(g2h(arg1), arg2)); - break; + return get_errno(mlock(g2h(arg1), arg2)); #endif #ifdef TARGET_NR_munlock case TARGET_NR_munlock: - ret = get_errno(munlock(g2h(arg1), arg2)); - break; + return get_errno(munlock(g2h(arg1), arg2)); #endif #ifdef TARGET_NR_mlockall case TARGET_NR_mlockall: - ret = get_errno(mlockall(target_to_host_mlockall_arg(arg1))); - break; + return get_errno(mlockall(target_to_host_mlockall_arg(arg1))); #endif #ifdef TARGET_NR_munlockall case TARGET_NR_munlockall: - ret = get_errno(munlockall()); - break; + return get_errno(munlockall()); #endif +#ifdef TARGET_NR_truncate case TARGET_NR_truncate: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(truncate(p, arg2)); unlock_user(p, arg1, 0); - break; + return ret; +#endif +#ifdef TARGET_NR_ftruncate case TARGET_NR_ftruncate: - ret = get_errno(ftruncate(arg1, arg2)); - break; + return get_errno(ftruncate(arg1, arg2)); +#endif case TARGET_NR_fchmod: - ret = get_errno(fchmod(arg1, arg2)); - break; + return get_errno(fchmod(arg1, arg2)); #if defined(TARGET_NR_fchmodat) case TARGET_NR_fchmodat: if (!(p = lock_user_string(arg2))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(fchmodat(arg1, p, arg3, 0)); unlock_user(p, arg2, 0); - break; + return ret; #endif case TARGET_NR_getpriority: /* Note that negative values are valid for getpriority, so we must @@ -9670,8 +8530,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, errno = 0; ret = getpriority(arg1, arg2); if (ret == -1 && errno != 0) { - ret = -host_to_target_errno(errno); - break; + return -host_to_target_errno(errno); } #ifdef TARGET_ALPHA /* Return value is the unbiased priority. Signal no error. */ @@ -9680,17 +8539,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, /* Return value is a biased priority to avoid negative numbers. */ ret = 20 - ret; #endif - break; + return ret; case TARGET_NR_setpriority: - ret = get_errno(setpriority(arg1, arg2, arg3)); - break; -#ifdef TARGET_NR_profil - case TARGET_NR_profil: - goto unimplemented; -#endif + return get_errno(setpriority(arg1, arg2, arg3)); +#ifdef TARGET_NR_statfs case TARGET_NR_statfs: - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(statfs(path(p), &stfs)); unlock_user(p, arg1, 0); convert_statfs: @@ -9698,7 +8554,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_statfs *target_stfs; if (!lock_user_struct(VERIFY_WRITE, target_stfs, arg2, 0)) - goto efault; + return -TARGET_EFAULT; __put_user(stfs.f_type, &target_stfs->f_type); __put_user(stfs.f_bsize, &target_stfs->f_bsize); __put_user(stfs.f_blocks, &target_stfs->f_blocks); @@ -9718,14 +8574,18 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare)); unlock_user_struct(target_stfs, arg2, 1); } - break; + return ret; +#endif +#ifdef TARGET_NR_fstatfs case TARGET_NR_fstatfs: ret = get_errno(fstatfs(arg1, &stfs)); goto convert_statfs; +#endif #ifdef TARGET_NR_statfs64 case TARGET_NR_statfs64: - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(statfs(path(p), &stfs)); unlock_user(p, arg1, 0); convert_statfs64: @@ -9733,7 +8593,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_statfs64 *target_stfs; if (!lock_user_struct(VERIFY_WRITE, target_stfs, arg3, 0)) - goto efault; + return -TARGET_EFAULT; __put_user(stfs.f_type, &target_stfs->f_type); __put_user(stfs.f_bsize, &target_stfs->f_bsize); __put_user(stfs.f_blocks, &target_stfs->f_blocks); @@ -9748,127 +8608,102 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, memset(target_stfs->f_spare, 0, sizeof(target_stfs->f_spare)); unlock_user_struct(target_stfs, arg3, 1); } - break; + return ret; case TARGET_NR_fstatfs64: ret = get_errno(fstatfs(arg1, &stfs)); goto convert_statfs64; #endif -#ifdef TARGET_NR_ioperm - case TARGET_NR_ioperm: - goto unimplemented; -#endif #ifdef TARGET_NR_socketcall case TARGET_NR_socketcall: - ret = do_socketcall(arg1, arg2); - break; + return do_socketcall(arg1, arg2); #endif #ifdef TARGET_NR_accept case TARGET_NR_accept: - ret = do_accept4(arg1, arg2, arg3, 0); - break; + return do_accept4(arg1, arg2, arg3, 0); #endif #ifdef TARGET_NR_accept4 case TARGET_NR_accept4: - ret = do_accept4(arg1, arg2, arg3, arg4); - break; + return do_accept4(arg1, arg2, arg3, arg4); #endif #ifdef TARGET_NR_bind case TARGET_NR_bind: - ret = do_bind(arg1, arg2, arg3); - break; + return do_bind(arg1, arg2, arg3); #endif #ifdef TARGET_NR_connect case TARGET_NR_connect: - ret = do_connect(arg1, arg2, arg3); - break; + return do_connect(arg1, arg2, arg3); #endif #ifdef TARGET_NR_getpeername case TARGET_NR_getpeername: - ret = do_getpeername(arg1, arg2, arg3); - break; + return do_getpeername(arg1, arg2, arg3); #endif #ifdef TARGET_NR_getsockname case TARGET_NR_getsockname: - ret = do_getsockname(arg1, arg2, arg3); - break; + return do_getsockname(arg1, arg2, arg3); #endif #ifdef TARGET_NR_getsockopt case TARGET_NR_getsockopt: - ret = do_getsockopt(arg1, arg2, arg3, arg4, arg5); - break; + return do_getsockopt(arg1, arg2, arg3, arg4, arg5); #endif #ifdef TARGET_NR_listen case TARGET_NR_listen: - ret = get_errno(listen(arg1, arg2)); - break; + return get_errno(listen(arg1, arg2)); #endif #ifdef TARGET_NR_recv case TARGET_NR_recv: - ret = do_recvfrom(arg1, arg2, arg3, arg4, 0, 0); - break; + return do_recvfrom(arg1, arg2, arg3, arg4, 0, 0); #endif #ifdef TARGET_NR_recvfrom case TARGET_NR_recvfrom: - ret = do_recvfrom(arg1, arg2, arg3, arg4, arg5, arg6); - break; + return do_recvfrom(arg1, arg2, arg3, arg4, arg5, arg6); #endif #ifdef TARGET_NR_recvmsg case TARGET_NR_recvmsg: - ret = do_sendrecvmsg(arg1, arg2, arg3, 0); - break; + return do_sendrecvmsg(arg1, arg2, arg3, 0); #endif #ifdef TARGET_NR_send case TARGET_NR_send: - ret = do_sendto(arg1, arg2, arg3, arg4, 0, 0); - break; + return do_sendto(arg1, arg2, arg3, arg4, 0, 0); #endif #ifdef TARGET_NR_sendmsg case TARGET_NR_sendmsg: - ret = do_sendrecvmsg(arg1, arg2, arg3, 1); - break; + return do_sendrecvmsg(arg1, arg2, arg3, 1); #endif #ifdef TARGET_NR_sendmmsg case TARGET_NR_sendmmsg: - ret = do_sendrecvmmsg(arg1, arg2, arg3, arg4, 1); - break; + return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 1); case TARGET_NR_recvmmsg: - ret = do_sendrecvmmsg(arg1, arg2, arg3, arg4, 0); - break; + return do_sendrecvmmsg(arg1, arg2, arg3, arg4, 0); #endif #ifdef TARGET_NR_sendto case TARGET_NR_sendto: - ret = do_sendto(arg1, arg2, arg3, arg4, arg5, arg6); - break; + return do_sendto(arg1, arg2, arg3, arg4, arg5, arg6); #endif #ifdef TARGET_NR_shutdown case TARGET_NR_shutdown: - ret = get_errno(shutdown(arg1, arg2)); - break; + return get_errno(shutdown(arg1, arg2)); #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; + return -TARGET_EFAULT; } ret = get_errno(getrandom(p, arg2, arg3)); unlock_user(p, arg1, ret); - break; + return ret; #endif #ifdef TARGET_NR_socket case TARGET_NR_socket: - ret = do_socket(arg1, arg2, arg3); - break; + return do_socket(arg1, arg2, arg3); #endif #ifdef TARGET_NR_socketpair case TARGET_NR_socketpair: - ret = do_socketpair(arg1, arg2, arg3, arg4); - break; + return do_socketpair(arg1, arg2, arg3, arg4); #endif #ifdef TARGET_NR_setsockopt case TARGET_NR_setsockopt: - ret = do_setsockopt(arg1, arg2, arg3, arg4, (socklen_t) arg5); - break; + return do_setsockopt(arg1, arg2, arg3, arg4, (socklen_t) arg5); #endif #if defined(TARGET_NR_syslog) case TARGET_NR_syslog: @@ -9884,34 +8719,27 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_SYSLOG_ACTION_CONSOLE_LEVEL: /* Set messages level */ case TARGET_SYSLOG_ACTION_SIZE_UNREAD: /* Number of chars */ case TARGET_SYSLOG_ACTION_SIZE_BUFFER: /* Size of the buffer */ - { - ret = get_errno(sys_syslog((int)arg1, NULL, (int)arg3)); - } - break; + return get_errno(sys_syslog((int)arg1, NULL, (int)arg3)); case TARGET_SYSLOG_ACTION_READ: /* Read from log */ case TARGET_SYSLOG_ACTION_READ_CLEAR: /* Read/clear msgs */ case TARGET_SYSLOG_ACTION_READ_ALL: /* Read last messages */ { - ret = -TARGET_EINVAL; if (len < 0) { - goto fail; + return -TARGET_EINVAL; } - ret = 0; if (len == 0) { - break; + return 0; } p = lock_user(VERIFY_WRITE, arg2, arg3, 0); if (!p) { - ret = -TARGET_EFAULT; - goto fail; + return -TARGET_EFAULT; } ret = get_errno(sys_syslog((int)arg1, p, (int)arg3)); unlock_user(p, arg2, arg3); } - break; + return ret; default: - ret = -EINVAL; - break; + return -TARGET_EINVAL; } } break; @@ -9925,7 +8753,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (copy_from_user_timeval(&pvalue->it_interval, arg2) || copy_from_user_timeval(&pvalue->it_value, arg2 + sizeof(struct target_timeval))) - goto efault; + return -TARGET_EFAULT; } else { pvalue = NULL; } @@ -9935,10 +8763,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, &ovalue.it_interval) || copy_to_user_timeval(arg3 + sizeof(struct target_timeval), &ovalue.it_value)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; case TARGET_NR_getitimer: { struct itimerval value; @@ -9949,26 +8777,29 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, &value.it_interval) || copy_to_user_timeval(arg2 + sizeof(struct target_timeval), &value.it_value)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #ifdef TARGET_NR_stat case TARGET_NR_stat: - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_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; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(lstat(path(p), &st)); unlock_user(p, arg1, 0); goto do_stat; #endif +#ifdef TARGET_NR_fstat case TARGET_NR_fstat: { ret = get_errno(fstat(arg1, &st)); @@ -9979,7 +8810,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct target_stat *target_st; if (!lock_user_struct(VERIFY_WRITE, target_st, arg2, 0)) - goto efault; + return -TARGET_EFAULT; memset(target_st, 0, sizeof(*target_st)); __put_user(st.st_dev, &target_st->st_dev); __put_user(st.st_ino, &target_st->st_ino); @@ -9997,27 +8828,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user_struct(target_st, arg2, 1); } } - break; -#ifdef TARGET_NR_olduname - case TARGET_NR_olduname: - goto unimplemented; -#endif -#ifdef TARGET_NR_iopl - case TARGET_NR_iopl: - goto unimplemented; + return ret; #endif case TARGET_NR_vhangup: - ret = get_errno(vhangup()); - break; -#ifdef TARGET_NR_idle - case TARGET_NR_idle: - goto unimplemented; -#endif + return get_errno(vhangup()); #ifdef TARGET_NR_syscall case TARGET_NR_syscall: - ret = do_syscall(cpu_env, arg1 & 0xffff, arg2, arg3, arg4, arg5, - arg6, arg7, arg8, 0); - break; + return do_syscall(cpu_env, arg1 & 0xffff, arg2, arg3, arg4, arg5, + arg6, arg7, arg8, 0); #endif case TARGET_NR_wait4: { @@ -10035,7 +8853,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (status_ptr && ret) { status = host_to_target_waitstatus(status); if (put_user_s32(status, status_ptr)) - goto efault; + return -TARGET_EFAULT; } if (target_rusage) { rusage_err = host_to_target_rusage(target_rusage, &rusage); @@ -10045,14 +8863,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } } } - break; + return ret; #ifdef TARGET_NR_swapoff case TARGET_NR_swapoff: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(swapoff(p)); unlock_user(p, arg1, 0); - break; + return ret; #endif case TARGET_NR_sysinfo: { @@ -10062,7 +8880,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret) && arg1) { if (!lock_user_struct(VERIFY_WRITE, target_value, arg1, 0)) - goto efault; + return -TARGET_EFAULT; __put_user(value.uptime, &target_value->uptime); __put_user(value.loads[0], &target_value->loads[0]); __put_user(value.loads[1], &target_value->loads[1]); @@ -10080,70 +8898,57 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user_struct(target_value, arg1, 1); } } - break; + return ret; #ifdef TARGET_NR_ipc case TARGET_NR_ipc: - ret = do_ipc(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); - break; + return do_ipc(cpu_env, arg1, arg2, arg3, arg4, arg5, arg6); #endif #ifdef TARGET_NR_semget case TARGET_NR_semget: - ret = get_errno(semget(arg1, arg2, arg3)); - break; + return get_errno(semget(arg1, arg2, arg3)); #endif #ifdef TARGET_NR_semop case TARGET_NR_semop: - ret = do_semop(arg1, arg2, arg3); - break; + return do_semop(arg1, arg2, arg3); #endif #ifdef TARGET_NR_semctl case TARGET_NR_semctl: - ret = do_semctl(arg1, arg2, arg3, arg4); - break; + return do_semctl(arg1, arg2, arg3, arg4); #endif #ifdef TARGET_NR_msgctl case TARGET_NR_msgctl: - ret = do_msgctl(arg1, arg2, arg3); - break; + return do_msgctl(arg1, arg2, arg3); #endif #ifdef TARGET_NR_msgget case TARGET_NR_msgget: - ret = get_errno(msgget(arg1, arg2)); - break; + return get_errno(msgget(arg1, arg2)); #endif #ifdef TARGET_NR_msgrcv case TARGET_NR_msgrcv: - ret = do_msgrcv(arg1, arg2, arg3, arg4, arg5); - break; + return do_msgrcv(arg1, arg2, arg3, arg4, arg5); #endif #ifdef TARGET_NR_msgsnd case TARGET_NR_msgsnd: - ret = do_msgsnd(arg1, arg2, arg3, arg4); - break; + return do_msgsnd(arg1, arg2, arg3, arg4); #endif #ifdef TARGET_NR_shmget case TARGET_NR_shmget: - ret = get_errno(shmget(arg1, arg2, arg3)); - break; + return get_errno(shmget(arg1, arg2, arg3)); #endif #ifdef TARGET_NR_shmctl case TARGET_NR_shmctl: - ret = do_shmctl(arg1, arg2, arg3); - break; + return do_shmctl(arg1, arg2, arg3); #endif #ifdef TARGET_NR_shmat case TARGET_NR_shmat: - ret = do_shmat(cpu_env, arg1, arg2, arg3); - break; + return do_shmat(cpu_env, arg1, arg2, arg3); #endif #ifdef TARGET_NR_shmdt case TARGET_NR_shmdt: - ret = do_shmdt(arg1); - break; + return do_shmdt(arg1); #endif case TARGET_NR_fsync: - ret = get_errno(fsync(arg1)); - break; + return get_errno(fsync(arg1)); case TARGET_NR_clone: /* Linux manages to have three different orderings for its * arguments to clone(); the BACKWARDS and BACKWARDS2 defines @@ -10160,27 +8965,26 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else ret = get_errno(do_fork(cpu_env, arg1, arg2, arg3, arg5, arg4)); #endif - break; + return ret; #ifdef __NR_exit_group /* new thread calls */ case TARGET_NR_exit_group: preexit_cleanup(cpu_env, arg1); - ret = get_errno(exit_group(arg1)); - break; + return get_errno(exit_group(arg1)); #endif case TARGET_NR_setdomainname: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(setdomainname(p, arg2)); unlock_user(p, arg1, 0); - break; + return ret; case TARGET_NR_uname: /* no need to transcode because we use the linux syscall */ { struct new_utsname * buf; if (!lock_user_struct(VERIFY_WRITE, buf, arg1, 0)) - goto efault; + return -TARGET_EFAULT; ret = get_errno(sys_uname(buf)); if (!is_error(ret)) { /* Overwrite the native machine name with whatever is being @@ -10195,17 +8999,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user_struct(buf, arg1, 1); } - break; + return ret; #ifdef TARGET_I386 case TARGET_NR_modify_ldt: - ret = do_modify_ldt(cpu_env, arg1, arg2, arg3); - break; + return do_modify_ldt(cpu_env, arg1, arg2, arg3); #if !defined(TARGET_X86_64) - case TARGET_NR_vm86old: - goto unimplemented; case TARGET_NR_vm86: - ret = do_vm86(cpu_env, arg1, arg2); - break; + return do_vm86(cpu_env, arg1, arg2); #endif #endif case TARGET_NR_adjtimex: @@ -10213,65 +9013,39 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct timex host_buf; if (target_to_host_timex(&host_buf, arg1) != 0) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(adjtimex(&host_buf)); if (!is_error(ret)) { if (host_to_target_timex(arg1, &host_buf) != 0) { - goto efault; + return -TARGET_EFAULT; } } } - break; + return ret; #if defined(TARGET_NR_clock_adjtime) && defined(CONFIG_CLOCK_ADJTIME) case TARGET_NR_clock_adjtime: { struct timex htx, *phtx = &htx; if (target_to_host_timex(phtx, arg2) != 0) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(clock_adjtime(arg1, phtx)); if (!is_error(ret) && phtx) { if (host_to_target_timex(arg2, phtx) != 0) { - goto efault; + return -TARGET_EFAULT; } } } - break; + return ret; #endif -#ifdef TARGET_NR_create_module - case TARGET_NR_create_module: -#endif - case TARGET_NR_init_module: - case TARGET_NR_delete_module: -#ifdef TARGET_NR_get_kernel_syms - case TARGET_NR_get_kernel_syms: -#endif - goto unimplemented; - case TARGET_NR_quotactl: - goto unimplemented; case TARGET_NR_getpgid: - ret = get_errno(getpgid(arg1)); - break; + return get_errno(getpgid(arg1)); case TARGET_NR_fchdir: - ret = get_errno(fchdir(arg1)); - break; -#ifdef TARGET_NR_bdflush /* not on x86_64 */ - case TARGET_NR_bdflush: - goto unimplemented; -#endif -#ifdef TARGET_NR_sysfs - case TARGET_NR_sysfs: - goto unimplemented; -#endif + return get_errno(fchdir(arg1)); case TARGET_NR_personality: - ret = get_errno(personality(arg1)); - break; -#ifdef TARGET_NR_afs_syscall - case TARGET_NR_afs_syscall: - goto unimplemented; -#endif + return get_errno(personality(arg1)); #ifdef TARGET_NR__llseek /* Not on alpha */ case TARGET_NR__llseek: { @@ -10287,10 +9061,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(_llseek(arg1, arg2, arg3, &res, arg5)); #endif if ((ret == 0) && put_user_s64(res, arg4)) { - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #ifdef TARGET_NR_getdents case TARGET_NR_getdents: @@ -10303,8 +9077,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, dirp = g_try_malloc(count); if (!dirp) { - ret = -TARGET_ENOMEM; - goto fail; + return -TARGET_ENOMEM; } ret = get_errno(sys_getdents(arg1, dirp, count)); @@ -10318,7 +9091,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, count1 = 0; de = dirp; if (!(target_dirp = lock_user(VERIFY_WRITE, arg2, count, 0))) - goto efault; + return -TARGET_EFAULT; tde = target_dirp; while (len > 0) { reclen = de->d_reclen; @@ -10346,7 +9119,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, abi_long count = arg3; if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(sys_getdents(arg1, dirp, count)); if (!is_error(ret)) { struct linux_dirent *de; @@ -10375,7 +9148,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, dirp = lock_user(VERIFY_WRITE, arg2, count, 0); if (!dirp) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(sys_getdents64(arg1, dirp, count)); if (!is_error(ret)) { @@ -10422,7 +9195,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(dirp, arg2, ret); } #endif - break; + return ret; #endif /* TARGET_NR_getdents */ #if defined(TARGET_NR_getdents64) && defined(__NR_getdents64) case TARGET_NR_getdents64: @@ -10430,7 +9203,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct linux_dirent64 *dirp; abi_long count = arg3; if (!(dirp = lock_user(VERIFY_WRITE, arg2, count, 0))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(sys_getdents64(arg1, dirp, count)); if (!is_error(ret)) { struct linux_dirent64 *de; @@ -10450,12 +9223,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user(dirp, arg2, ret); } - break; + return ret; #endif /* TARGET_NR_getdents64 */ #if defined(TARGET_NR__newselect) case TARGET_NR__newselect: - ret = do_select(arg1, arg2, arg3, arg4, arg5); - break; + return do_select(arg1, arg2, arg3, arg4, arg5); #endif #if defined(TARGET_NR_poll) || defined(TARGET_NR_ppoll) # ifdef TARGET_NR_poll @@ -10474,14 +9246,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, target_pfd = NULL; if (nfds) { if (nfds > (INT_MAX / sizeof(struct target_pollfd))) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } target_pfd = lock_user(VERIFY_WRITE, arg1, sizeof(struct target_pollfd) * nfds, 1); if (!target_pfd) { - goto efault; + return -TARGET_EFAULT; } pfd = alloca(sizeof(struct pollfd) * nfds); @@ -10502,7 +9273,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg3) { if (target_to_host_timespec(timeout_ts, arg3)) { unlock_user(target_pfd, arg1, 0); - goto efault; + return -TARGET_EFAULT; } } else { timeout_ts = NULL; @@ -10511,14 +9282,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg4) { if (arg5 != sizeof(target_sigset_t)) { unlock_user(target_pfd, arg1, 0); - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } target_set = lock_user(VERIFY_READ, arg4, sizeof(target_sigset_t), 1); if (!target_set) { unlock_user(target_pfd, arg1, 0); - goto efault; + return -TARGET_EFAULT; } target_to_host_sigset(set, target_set); } else { @@ -10566,13 +9336,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user(target_pfd, arg1, sizeof(struct target_pollfd) * nfds); } - break; + return ret; #endif case TARGET_NR_flock: /* NOTE: the flock constant seems to be the same for every Linux platform */ - ret = get_errno(safe_flock(arg1, arg2)); - break; + return get_errno(safe_flock(arg1, arg2)); case TARGET_NR_readv: { struct iovec *vec = lock_iovec(VERIFY_WRITE, arg2, arg3, 0); @@ -10583,7 +9352,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -host_to_target_errno(errno); } } - break; + return ret; case TARGET_NR_writev: { struct iovec *vec = lock_iovec(VERIFY_READ, arg2, arg3, 1); @@ -10594,7 +9363,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -host_to_target_errno(errno); } } - break; + return ret; #if defined(TARGET_NR_preadv) case TARGET_NR_preadv: { @@ -10609,7 +9378,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -host_to_target_errno(errno); } } - break; + return ret; #endif #if defined(TARGET_NR_pwritev) case TARGET_NR_pwritev: @@ -10625,22 +9394,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -host_to_target_errno(errno); } } - break; + return ret; #endif case TARGET_NR_getsid: - ret = get_errno(getsid(arg1)); - break; + return get_errno(getsid(arg1)); #if defined(TARGET_NR_fdatasync) /* Not on alpha (osf_datasync ?) */ case TARGET_NR_fdatasync: - ret = get_errno(fdatasync(arg1)); - break; + return get_errno(fdatasync(arg1)); #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; + return -TARGET_ENOTDIR; #endif case TARGET_NR_sched_getaffinity: { @@ -10652,8 +9418,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, * care of mismatches between target ulong and host ulong sizes. */ if (arg2 & (sizeof(abi_ulong) - 1)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); @@ -10672,18 +9437,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, */ int numcpus = sysconf(_SC_NPROCESSORS_CONF); if (numcpus > arg2 * 8) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } ret = arg2; } if (host_to_target_cpu_mask(mask, mask_size, arg3, ret)) { - goto efault; + return -TARGET_EFAULT; } } } - break; + return ret; case TARGET_NR_sched_setaffinity: { unsigned int mask_size; @@ -10694,20 +9458,18 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, * care of mismatches between target ulong and host ulong sizes. */ if (arg2 & (sizeof(abi_ulong) - 1)) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } mask_size = (arg2 + (sizeof(*mask) - 1)) & ~(sizeof(*mask) - 1); mask = alloca(mask_size); ret = target_to_host_cpu_mask(mask, mask_size, arg3, arg2); if (ret) { - break; + return ret; } - ret = get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); + return get_errno(sys_sched_setaffinity(arg1, mask_size, mask)); } - break; case TARGET_NR_getcpu: { unsigned cpu, node; @@ -10715,16 +9477,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg2 ? &node : NULL, NULL)); if (is_error(ret)) { - goto fail; + return ret; } if (arg1 && put_user_u32(cpu, arg1)) { - goto efault; + return -TARGET_EFAULT; } if (arg2 && put_user_u32(node, arg2)) { - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; case TARGET_NR_sched_setparam: { struct sched_param *target_schp; @@ -10734,12 +9496,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, return -TARGET_EINVAL; } if (!lock_user_struct(VERIFY_READ, target_schp, arg2, 1)) - goto efault; + return -TARGET_EFAULT; schp.sched_priority = tswap32(target_schp->sched_priority); unlock_user_struct(target_schp, arg2, 0); - ret = get_errno(sched_setparam(arg1, &schp)); + return get_errno(sched_setparam(arg1, &schp)); } - break; case TARGET_NR_sched_getparam: { struct sched_param *target_schp; @@ -10751,12 +9512,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(sched_getparam(arg1, &schp)); if (!is_error(ret)) { if (!lock_user_struct(VERIFY_WRITE, target_schp, arg2, 0)) - goto efault; + return -TARGET_EFAULT; target_schp->sched_priority = tswap32(schp.sched_priority); unlock_user_struct(target_schp, arg2, 1); } } - break; + return ret; case TARGET_NR_sched_setscheduler: { struct sched_param *target_schp; @@ -10765,24 +9526,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, return -TARGET_EINVAL; } if (!lock_user_struct(VERIFY_READ, target_schp, arg3, 1)) - goto efault; + return -TARGET_EFAULT; schp.sched_priority = tswap32(target_schp->sched_priority); unlock_user_struct(target_schp, arg3, 0); - ret = get_errno(sched_setscheduler(arg1, arg2, &schp)); + return get_errno(sched_setscheduler(arg1, arg2, &schp)); } - break; case TARGET_NR_sched_getscheduler: - ret = get_errno(sched_getscheduler(arg1)); - break; + return get_errno(sched_getscheduler(arg1)); case TARGET_NR_sched_yield: - ret = get_errno(sched_yield()); - break; + return get_errno(sched_yield()); case TARGET_NR_sched_get_priority_max: - ret = get_errno(sched_get_priority_max(arg1)); - break; + return get_errno(sched_get_priority_max(arg1)); case TARGET_NR_sched_get_priority_min: - ret = get_errno(sched_get_priority_min(arg1)); - break; + return get_errno(sched_get_priority_min(arg1)); case TARGET_NR_sched_rr_get_interval: { struct timespec ts; @@ -10791,7 +9547,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = host_to_target_timespec(arg2, &ts); } } - break; + return ret; case TARGET_NR_nanosleep: { struct timespec req, rem; @@ -10801,15 +9557,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, host_to_target_timespec(arg2, &rem); } } - break; -#ifdef TARGET_NR_query_module - case TARGET_NR_query_module: - goto unimplemented; -#endif -#ifdef TARGET_NR_nfsservctl - case TARGET_NR_nfsservctl: - goto unimplemented; -#endif + return ret; case TARGET_NR_prctl: switch (arg1) { case PR_GET_PDEATHSIG: @@ -10818,45 +9566,132 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(prctl(arg1, &deathsig, arg3, arg4, arg5)); if (!is_error(ret) && arg2 && put_user_ual(deathsig, arg2)) { - goto efault; + return -TARGET_EFAULT; } - break; + return ret; } #ifdef PR_GET_NAME case PR_GET_NAME: { void *name = lock_user(VERIFY_WRITE, arg2, 16, 1); if (!name) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(prctl(arg1, (unsigned long)name, arg3, arg4, arg5)); unlock_user(name, arg2, 16); - break; + return ret; } case PR_SET_NAME: { void *name = lock_user(VERIFY_READ, arg2, 16, 1); if (!name) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(prctl(arg1, (unsigned long)name, arg3, arg4, arg5)); unlock_user(name, arg2, 0); - break; + return ret; } #endif +#ifdef TARGET_MIPS + case TARGET_PR_GET_FP_MODE: + { + CPUMIPSState *env = ((CPUMIPSState *)cpu_env); + ret = 0; + if (env->CP0_Status & (1 << CP0St_FR)) { + ret |= TARGET_PR_FP_MODE_FR; + } + if (env->CP0_Config5 & (1 << CP0C5_FRE)) { + ret |= TARGET_PR_FP_MODE_FRE; + } + return ret; + } + case TARGET_PR_SET_FP_MODE: + { + CPUMIPSState *env = ((CPUMIPSState *)cpu_env); + bool old_fr = env->CP0_Status & (1 << CP0St_FR); + bool old_fre = env->CP0_Config5 & (1 << CP0C5_FRE); + bool new_fr = arg2 & TARGET_PR_FP_MODE_FR; + bool new_fre = arg2 & TARGET_PR_FP_MODE_FRE; + + const unsigned int known_bits = TARGET_PR_FP_MODE_FR | + TARGET_PR_FP_MODE_FRE; + + /* If nothing to change, return right away, successfully. */ + if (old_fr == new_fr && old_fre == new_fre) { + return 0; + } + /* Check the value is valid */ + if (arg2 & ~known_bits) { + return -TARGET_EOPNOTSUPP; + } + /* Setting FRE without FR is not supported. */ + if (new_fre && !new_fr) { + return -TARGET_EOPNOTSUPP; + } + if (new_fr && !(env->active_fpu.fcr0 & (1 << FCR0_F64))) { + /* FR1 is not supported */ + return -TARGET_EOPNOTSUPP; + } + if (!new_fr && (env->active_fpu.fcr0 & (1 << FCR0_F64)) + && !(env->CP0_Status_rw_bitmask & (1 << CP0St_FR))) { + /* cannot set FR=0 */ + return -TARGET_EOPNOTSUPP; + } + if (new_fre && !(env->active_fpu.fcr0 & (1 << FCR0_FREP))) { + /* Cannot set FRE=1 */ + return -TARGET_EOPNOTSUPP; + } + + int i; + fpr_t *fpr = env->active_fpu.fpr; + for (i = 0; i < 32 ; i += 2) { + if (!old_fr && new_fr) { + fpr[i].w[!FP_ENDIAN_IDX] = fpr[i + 1].w[FP_ENDIAN_IDX]; + } else if (old_fr && !new_fr) { + fpr[i + 1].w[FP_ENDIAN_IDX] = fpr[i].w[!FP_ENDIAN_IDX]; + } + } + + if (new_fr) { + env->CP0_Status |= (1 << CP0St_FR); + env->hflags |= MIPS_HFLAG_F64; + } else { + env->CP0_Status &= ~(1 << CP0St_FR); + env->hflags &= ~MIPS_HFLAG_F64; + } + if (new_fre) { + env->CP0_Config5 |= (1 << CP0C5_FRE); + if (env->active_fpu.fcr0 & (1 << FCR0_FREP)) { + env->hflags |= MIPS_HFLAG_FRE; + } + } else { + env->CP0_Config5 &= ~(1 << CP0C5_FRE); + env->hflags &= ~MIPS_HFLAG_FRE; + } + + return 0; + } +#endif /* MIPS */ #ifdef TARGET_AARCH64 case TARGET_PR_SVE_SET_VL: - /* We cannot support either PR_SVE_SET_VL_ONEXEC - or PR_SVE_VL_INHERIT. Therefore, anything above - ARM_MAX_VQ results in EINVAL. */ + /* + * We cannot support either PR_SVE_SET_VL_ONEXEC or + * PR_SVE_VL_INHERIT. Note the kernel definition + * of sve_vl_valid allows for VQ=512, i.e. VL=8192, + * even though the current architectural maximum is VQ=16. + */ ret = -TARGET_EINVAL; - if (arm_feature(cpu_env, ARM_FEATURE_SVE) - && arg2 >= 0 && arg2 <= ARM_MAX_VQ * 16 && !(arg2 & 15)) { + if (cpu_isar_feature(aa64_sve, arm_env_get_cpu(cpu_env)) + && arg2 >= 0 && arg2 <= 512 * 16 && !(arg2 & 15)) { CPUARMState *env = cpu_env; - int old_vq = (env->vfp.zcr_el[1] & 0xf) + 1; - int vq = MAX(arg2 / 16, 1); + ARMCPU *cpu = arm_env_get_cpu(env); + uint32_t vq, old_vq; + + old_vq = (env->vfp.zcr_el[1] & 0xf) + 1; + vq = MAX(arg2 / 16, 1); + vq = MIN(vq, cpu->sve_max_vq); if (vq < old_vq) { aarch64_sve_narrow_vq(env, vq); @@ -10864,34 +9699,69 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, env->vfp.zcr_el[1] = vq - 1; ret = vq * 16; } - break; + return ret; case TARGET_PR_SVE_GET_VL: ret = -TARGET_EINVAL; - if (arm_feature(cpu_env, ARM_FEATURE_SVE)) { - CPUARMState *env = cpu_env; - ret = ((env->vfp.zcr_el[1] & 0xf) + 1) * 16; + { + ARMCPU *cpu = arm_env_get_cpu(cpu_env); + if (cpu_isar_feature(aa64_sve, cpu)) { + ret = ((cpu->env.vfp.zcr_el[1] & 0xf) + 1) * 16; + } } - break; + return ret; + case TARGET_PR_PAC_RESET_KEYS: + { + CPUARMState *env = cpu_env; + ARMCPU *cpu = arm_env_get_cpu(env); + + if (arg3 || arg4 || arg5) { + return -TARGET_EINVAL; + } + if (cpu_isar_feature(aa64_pauth, cpu)) { + int all = (TARGET_PR_PAC_APIAKEY | TARGET_PR_PAC_APIBKEY | + TARGET_PR_PAC_APDAKEY | TARGET_PR_PAC_APDBKEY | + TARGET_PR_PAC_APGAKEY); + if (arg2 == 0) { + arg2 = all; + } else if (arg2 & ~all) { + return -TARGET_EINVAL; + } + if (arg2 & TARGET_PR_PAC_APIAKEY) { + arm_init_pauth_key(&env->apia_key); + } + if (arg2 & TARGET_PR_PAC_APIBKEY) { + arm_init_pauth_key(&env->apib_key); + } + if (arg2 & TARGET_PR_PAC_APDAKEY) { + arm_init_pauth_key(&env->apda_key); + } + if (arg2 & TARGET_PR_PAC_APDBKEY) { + arm_init_pauth_key(&env->apdb_key); + } + if (arg2 & TARGET_PR_PAC_APGAKEY) { + arm_init_pauth_key(&env->apga_key); + } + return 0; + } + } + return -TARGET_EINVAL; #endif /* AARCH64 */ case PR_GET_SECCOMP: case PR_SET_SECCOMP: /* Disable seccomp to prevent the target disabling syscalls we * need. */ - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; default: /* Most prctl options have no pointer arguments */ - ret = get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); - break; + return get_errno(prctl(arg1, arg2, arg3, arg4, arg5)); } break; #ifdef TARGET_NR_arch_prctl case TARGET_NR_arch_prctl: #if defined(TARGET_I386) && !defined(TARGET_ABI32) - ret = do_arch_prctl(cpu_env, arg1, arg2); - break; + return do_arch_prctl(cpu_env, arg1, arg2); #else - goto unimplemented; +#error unreachable #endif #endif #ifdef TARGET_NR_pread64 @@ -10900,28 +9770,42 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg4 = arg5; arg5 = arg6; } - if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0))) - goto efault; + if (arg2 == 0 && arg3 == 0) { + /* Special-case NULL buffer and zero length, which should succeed */ + p = 0; + } else { + p = lock_user(VERIFY_WRITE, arg2, arg3, 0); + if (!p) { + return -TARGET_EFAULT; + } + } ret = get_errno(pread64(arg1, p, arg3, target_offset64(arg4, arg5))); unlock_user(p, arg2, ret); - break; + return ret; case TARGET_NR_pwrite64: if (regpairs_aligned(cpu_env, num)) { arg4 = arg5; arg5 = arg6; } - if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1))) - goto efault; + if (arg2 == 0 && arg3 == 0) { + /* Special-case NULL buffer and zero length, which should succeed */ + p = 0; + } else { + p = lock_user(VERIFY_READ, arg2, arg3, 1); + if (!p) { + return -TARGET_EFAULT; + } + } ret = get_errno(pwrite64(arg1, p, arg3, target_offset64(arg4, arg5))); unlock_user(p, arg2, 0); - break; + return ret; #endif case TARGET_NR_getcwd: if (!(p = lock_user(VERIFY_WRITE, arg1, arg2, 0))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(sys_getcwd1(p, arg2)); unlock_user(p, arg1, ret); - break; + return ret; case TARGET_NR_capget: case TARGET_NR_capset: { @@ -10934,7 +9818,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, int data_items = 1; if (!lock_user_struct(VERIFY_WRITE, target_header, arg1, 1)) { - goto efault; + return -TARGET_EFAULT; } header.version = tswap32(target_header->version); header.pid = tswap32(target_header->pid); @@ -10954,7 +9838,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } if (!target_data) { unlock_user_struct(target_header, arg1, 0); - goto efault; + return -TARGET_EFAULT; } if (num == TARGET_NR_capset) { @@ -10990,13 +9874,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(target_data, arg2, 0); } } - break; + return ret; } case TARGET_NR_sigaltstack: - ret = do_sigaltstack(arg1, arg2, get_sp_from_cpustate((CPUArchState *)cpu_env)); - break; + return do_sigaltstack(arg1, arg2, + get_sp_from_cpustate((CPUArchState *)cpu_env)); #ifdef CONFIG_SENDFILE +#ifdef TARGET_NR_sendfile case TARGET_NR_sendfile: { off_t *offp = NULL; @@ -11004,7 +9889,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg3) { ret = get_user_sal(off, arg3); if (is_error(ret)) { - break; + return ret; } offp = &off; } @@ -11015,8 +9900,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = ret2; } } - break; + return ret; } +#endif #ifdef TARGET_NR_sendfile64 case TARGET_NR_sendfile64: { @@ -11025,7 +9911,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg3) { ret = get_user_s64(off, arg3); if (is_error(ret)) { - break; + return ret; } offp = &off; } @@ -11036,31 +9922,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = ret2; } } - break; + return ret; } #endif -#else - case TARGET_NR_sendfile: -#ifdef TARGET_NR_sendfile64 - case TARGET_NR_sendfile64: -#endif - goto unimplemented; -#endif - -#ifdef TARGET_NR_getpmsg - case TARGET_NR_getpmsg: - goto unimplemented; -#endif -#ifdef TARGET_NR_putpmsg - case TARGET_NR_putpmsg: - goto unimplemented; #endif #ifdef TARGET_NR_vfork case TARGET_NR_vfork: - ret = get_errno(do_fork(cpu_env, - CLONE_VFORK | CLONE_VM | TARGET_SIGCHLD, - 0, 0, 0, 0)); - break; + return get_errno(do_fork(cpu_env, + CLONE_VFORK | CLONE_VM | TARGET_SIGCHLD, + 0, 0, 0, 0)); #endif #ifdef TARGET_NR_ugetrlimit case TARGET_NR_ugetrlimit: @@ -11071,53 +9941,54 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { struct target_rlimit *target_rlim; if (!lock_user_struct(VERIFY_WRITE, target_rlim, arg2, 0)) - goto efault; + return -TARGET_EFAULT; target_rlim->rlim_cur = host_to_target_rlim(rlim.rlim_cur); target_rlim->rlim_max = host_to_target_rlim(rlim.rlim_max); unlock_user_struct(target_rlim, arg2, 1); } - break; + return ret; } #endif #ifdef TARGET_NR_truncate64 case TARGET_NR_truncate64: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = target_truncate64(cpu_env, p, arg2, arg3, arg4); unlock_user(p, arg1, 0); - break; + return ret; #endif #ifdef TARGET_NR_ftruncate64 case TARGET_NR_ftruncate64: - ret = target_ftruncate64(cpu_env, arg1, arg2, arg3, arg4); - break; + return target_ftruncate64(cpu_env, arg1, arg2, arg3, arg4); #endif #ifdef TARGET_NR_stat64 case TARGET_NR_stat64: - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(stat(path(p), &st)); unlock_user(p, arg1, 0); if (!is_error(ret)) ret = host_to_target_stat64(cpu_env, arg2, &st); - break; + return ret; #endif #ifdef TARGET_NR_lstat64 case TARGET_NR_lstat64: - if (!(p = lock_user_string(arg1))) - goto efault; + if (!(p = lock_user_string(arg1))) { + return -TARGET_EFAULT; + } ret = get_errno(lstat(path(p), &st)); unlock_user(p, arg1, 0); if (!is_error(ret)) ret = host_to_target_stat64(cpu_env, arg2, &st); - break; + return ret; #endif #ifdef TARGET_NR_fstat64 case TARGET_NR_fstat64: ret = get_errno(fstat(arg1, &st)); if (!is_error(ret)) ret = host_to_target_stat64(cpu_env, arg2, &st); - break; + return ret; #endif #if (defined(TARGET_NR_fstatat64) || defined(TARGET_NR_newfstatat)) #ifdef TARGET_NR_fstatat64 @@ -11126,47 +9997,43 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #ifdef TARGET_NR_newfstatat case TARGET_NR_newfstatat: #endif - if (!(p = lock_user_string(arg2))) - goto efault; + if (!(p = lock_user_string(arg2))) { + return -TARGET_EFAULT; + } ret = get_errno(fstatat(arg1, path(p), &st, arg4)); + unlock_user(p, arg2, 0); if (!is_error(ret)) ret = host_to_target_stat64(cpu_env, arg3, &st); - break; + return ret; #endif #ifdef TARGET_NR_lchown case TARGET_NR_lchown: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(lchown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); - break; + return ret; #endif #ifdef TARGET_NR_getuid case TARGET_NR_getuid: - ret = get_errno(high2lowuid(getuid())); - break; + return get_errno(high2lowuid(getuid())); #endif #ifdef TARGET_NR_getgid case TARGET_NR_getgid: - ret = get_errno(high2lowgid(getgid())); - break; + return get_errno(high2lowgid(getgid())); #endif #ifdef TARGET_NR_geteuid case TARGET_NR_geteuid: - ret = get_errno(high2lowuid(geteuid())); - break; + return get_errno(high2lowuid(geteuid())); #endif #ifdef TARGET_NR_getegid case TARGET_NR_getegid: - ret = get_errno(high2lowgid(getegid())); - break; + return get_errno(high2lowgid(getegid())); #endif case TARGET_NR_setreuid: - ret = get_errno(setreuid(low2highuid(arg1), low2highuid(arg2))); - break; + return get_errno(setreuid(low2highuid(arg1), low2highuid(arg2))); case TARGET_NR_setregid: - ret = get_errno(setregid(low2highgid(arg1), low2highgid(arg2))); - break; + return get_errno(setregid(low2highgid(arg1), low2highgid(arg2))); case TARGET_NR_getgroups: { int gidsetsize = arg1; @@ -11177,17 +10044,17 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, grouplist = alloca(gidsetsize * sizeof(gid_t)); ret = get_errno(getgroups(gidsetsize, grouplist)); if (gidsetsize == 0) - break; + return ret; if (!is_error(ret)) { target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * sizeof(target_id), 0); if (!target_grouplist) - goto efault; + return -TARGET_EFAULT; for(i = 0;i < ret; i++) target_grouplist[i] = tswapid(high2lowgid(grouplist[i])); unlock_user(target_grouplist, arg2, gidsetsize * sizeof(target_id)); } } - break; + return ret; case TARGET_NR_setgroups: { int gidsetsize = arg1; @@ -11198,35 +10065,31 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, grouplist = alloca(gidsetsize * sizeof(gid_t)); target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * sizeof(target_id), 1); if (!target_grouplist) { - ret = -TARGET_EFAULT; - goto fail; + return -TARGET_EFAULT; } for (i = 0; i < gidsetsize; i++) { grouplist[i] = low2highgid(tswapid(target_grouplist[i])); } unlock_user(target_grouplist, arg2, 0); } - ret = get_errno(setgroups(gidsetsize, grouplist)); + return get_errno(setgroups(gidsetsize, grouplist)); } - break; case TARGET_NR_fchown: - ret = get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); - break; + return get_errno(fchown(arg1, low2highuid(arg2), low2highgid(arg3))); #if defined(TARGET_NR_fchownat) case TARGET_NR_fchownat: if (!(p = lock_user_string(arg2))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(fchownat(arg1, p, low2highuid(arg3), low2highgid(arg4), arg5)); unlock_user(p, arg2, 0); - break; + return ret; #endif #ifdef TARGET_NR_setresuid case TARGET_NR_setresuid: - ret = get_errno(sys_setresuid(low2highuid(arg1), - low2highuid(arg2), - low2highuid(arg3))); - break; + return get_errno(sys_setresuid(low2highuid(arg1), + low2highuid(arg2), + low2highuid(arg3))); #endif #ifdef TARGET_NR_getresuid case TARGET_NR_getresuid: @@ -11237,17 +10100,16 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (put_user_id(high2lowuid(ruid), arg1) || put_user_id(high2lowuid(euid), arg2) || put_user_id(high2lowuid(suid), arg3)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #ifdef TARGET_NR_getresgid case TARGET_NR_setresgid: - ret = get_errno(sys_setresgid(low2highgid(arg1), - low2highgid(arg2), - low2highgid(arg3))); - break; + return get_errno(sys_setresgid(low2highgid(arg1), + low2highgid(arg2), + low2highgid(arg3))); #endif #ifdef TARGET_NR_getresgid case TARGET_NR_getresgid: @@ -11258,44 +10120,39 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (put_user_id(high2lowgid(rgid), arg1) || put_user_id(high2lowgid(egid), arg2) || put_user_id(high2lowgid(sgid), arg3)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #ifdef TARGET_NR_chown case TARGET_NR_chown: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(chown(p, low2highuid(arg2), low2highgid(arg3))); unlock_user(p, arg1, 0); - break; + return ret; #endif case TARGET_NR_setuid: - ret = get_errno(sys_setuid(low2highuid(arg1))); - break; + return get_errno(sys_setuid(low2highuid(arg1))); case TARGET_NR_setgid: - ret = get_errno(sys_setgid(low2highgid(arg1))); - break; + return get_errno(sys_setgid(low2highgid(arg1))); case TARGET_NR_setfsuid: - ret = get_errno(setfsuid(arg1)); - break; + return get_errno(setfsuid(arg1)); case TARGET_NR_setfsgid: - ret = get_errno(setfsgid(arg1)); - break; + return get_errno(setfsgid(arg1)); #ifdef TARGET_NR_lchown32 case TARGET_NR_lchown32: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(lchown(p, arg2, arg3)); unlock_user(p, arg1, 0); - break; + return ret; #endif #ifdef TARGET_NR_getuid32 case TARGET_NR_getuid32: - ret = get_errno(getuid()); - break; + return get_errno(getuid()); #endif #if defined(TARGET_NR_getxuid) && defined(TARGET_ALPHA) @@ -11306,8 +10163,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, euid=geteuid(); ((CPUAlphaState *)cpu_env)->ir[IR_A4]=euid; } - ret = get_errno(getuid()); - break; + return get_errno(getuid()); #endif #if defined(TARGET_NR_getxgid) && defined(TARGET_ALPHA) /* Alpha specific */ @@ -11317,8 +10173,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, egid=getegid(); ((CPUAlphaState *)cpu_env)->ir[IR_A4]=egid; } - ret = get_errno(getgid()); - break; + return get_errno(getgid()); #endif #if defined(TARGET_NR_osf_getsysinfo) && defined(TARGET_ALPHA) /* Alpha specific */ @@ -11341,7 +10196,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, swcr |= (~fpcr >> 41) & SWCR_TRAP_ENABLE_DNO; if (put_user_u64 (swcr, arg2)) - goto efault; + return -TARGET_EFAULT; ret = 0; } break; @@ -11356,7 +10211,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, -- Grabs a copy of the HWRPB; surely not used. */ } - break; + return ret; #endif #if defined(TARGET_NR_osf_setsysinfo) && defined(TARGET_ALPHA) /* Alpha specific */ @@ -11368,7 +10223,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, uint64_t swcr, fpcr, orig_fpcr; if (get_user_u64 (swcr, arg2)) { - goto efault; + return -TARGET_EFAULT; } orig_fpcr = cpu_alpha_load_fpcr(cpu_env); fpcr = orig_fpcr & FPCR_DYN_MASK; @@ -11395,7 +10250,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, int si_code; if (get_user_u64(exc, arg2)) { - goto efault; + return -TARGET_EFAULT; } orig_fpcr = cpu_alpha_load_fpcr(cpu_env); @@ -11447,7 +10302,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, -- Not implemented in linux kernel */ } - break; + return ret; #endif #ifdef TARGET_NR_osf_sigprocmask /* Alpha specific. */ @@ -11468,8 +10323,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, how = SIG_SETMASK; break; default: - ret = -TARGET_EINVAL; - goto fail; + return -TARGET_EINVAL; } mask = arg2; target_to_host_old_sigset(&set, &mask); @@ -11479,33 +10333,28 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = mask; } } - break; + return ret; #endif #ifdef TARGET_NR_getgid32 case TARGET_NR_getgid32: - ret = get_errno(getgid()); - break; + return get_errno(getgid()); #endif #ifdef TARGET_NR_geteuid32 case TARGET_NR_geteuid32: - ret = get_errno(geteuid()); - break; + return get_errno(geteuid()); #endif #ifdef TARGET_NR_getegid32 case TARGET_NR_getegid32: - ret = get_errno(getegid()); - break; + return get_errno(getegid()); #endif #ifdef TARGET_NR_setreuid32 case TARGET_NR_setreuid32: - ret = get_errno(setreuid(arg1, arg2)); - break; + return get_errno(setreuid(arg1, arg2)); #endif #ifdef TARGET_NR_setregid32 case TARGET_NR_setregid32: - ret = get_errno(setregid(arg1, arg2)); - break; + return get_errno(setregid(arg1, arg2)); #endif #ifdef TARGET_NR_getgroups32 case TARGET_NR_getgroups32: @@ -11518,19 +10367,18 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, grouplist = alloca(gidsetsize * sizeof(gid_t)); ret = get_errno(getgroups(gidsetsize, grouplist)); if (gidsetsize == 0) - break; + return ret; if (!is_error(ret)) { target_grouplist = lock_user(VERIFY_WRITE, arg2, gidsetsize * 4, 0); if (!target_grouplist) { - ret = -TARGET_EFAULT; - goto fail; + return -TARGET_EFAULT; } for(i = 0;i < ret; i++) target_grouplist[i] = tswap32(grouplist[i]); unlock_user(target_grouplist, arg2, gidsetsize * 4); } } - break; + return ret; #endif #ifdef TARGET_NR_setgroups32 case TARGET_NR_setgroups32: @@ -11543,25 +10391,21 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, grouplist = alloca(gidsetsize * sizeof(gid_t)); target_grouplist = lock_user(VERIFY_READ, arg2, gidsetsize * 4, 1); if (!target_grouplist) { - ret = -TARGET_EFAULT; - goto fail; + return -TARGET_EFAULT; } for(i = 0;i < gidsetsize; i++) grouplist[i] = tswap32(target_grouplist[i]); unlock_user(target_grouplist, arg2, 0); - ret = get_errno(setgroups(gidsetsize, grouplist)); + return get_errno(setgroups(gidsetsize, grouplist)); } - break; #endif #ifdef TARGET_NR_fchown32 case TARGET_NR_fchown32: - ret = get_errno(fchown(arg1, arg2, arg3)); - break; + return get_errno(fchown(arg1, arg2, arg3)); #endif #ifdef TARGET_NR_setresuid32 case TARGET_NR_setresuid32: - ret = get_errno(sys_setresuid(arg1, arg2, arg3)); - break; + return get_errno(sys_setresuid(arg1, arg2, arg3)); #endif #ifdef TARGET_NR_getresuid32 case TARGET_NR_getresuid32: @@ -11572,15 +10416,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (put_user_u32(ruid, arg1) || put_user_u32(euid, arg2) || put_user_u32(suid, arg3)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #ifdef TARGET_NR_setresgid32 case TARGET_NR_setresgid32: - ret = get_errno(sys_setresgid(arg1, arg2, arg3)); - break; + return get_errno(sys_setresgid(arg1, arg2, arg3)); #endif #ifdef TARGET_NR_getresgid32 case TARGET_NR_getresgid32: @@ -11591,62 +10434,52 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (put_user_u32(rgid, arg1) || put_user_u32(egid, arg2) || put_user_u32(sgid, arg3)) - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #ifdef TARGET_NR_chown32 case TARGET_NR_chown32: if (!(p = lock_user_string(arg1))) - goto efault; + return -TARGET_EFAULT; ret = get_errno(chown(p, arg2, arg3)); unlock_user(p, arg1, 0); - break; + return ret; #endif #ifdef TARGET_NR_setuid32 case TARGET_NR_setuid32: - ret = get_errno(sys_setuid(arg1)); - break; + return get_errno(sys_setuid(arg1)); #endif #ifdef TARGET_NR_setgid32 case TARGET_NR_setgid32: - ret = get_errno(sys_setgid(arg1)); - break; + return get_errno(sys_setgid(arg1)); #endif #ifdef TARGET_NR_setfsuid32 case TARGET_NR_setfsuid32: - ret = get_errno(setfsuid(arg1)); - break; + return get_errno(setfsuid(arg1)); #endif #ifdef TARGET_NR_setfsgid32 case TARGET_NR_setfsgid32: - ret = get_errno(setfsgid(arg1)); - break; + return get_errno(setfsgid(arg1)); #endif - - case TARGET_NR_pivot_root: - goto unimplemented; #ifdef TARGET_NR_mincore case TARGET_NR_mincore: { - void *a; - ret = -TARGET_ENOMEM; - a = lock_user(VERIFY_READ, arg1, arg2, 0); + void *a = lock_user(VERIFY_READ, arg1, arg2, 0); if (!a) { - goto fail; + return -TARGET_ENOMEM; } - ret = -TARGET_EFAULT; p = lock_user_string(arg3); if (!p) { - goto mincore_fail; + ret = -TARGET_EFAULT; + } else { + ret = get_errno(mincore(a, arg2, p)); + unlock_user(p, arg3, ret); } - ret = get_errno(mincore(a, arg2, p)); - unlock_user(p, arg3, ret); - mincore_fail: unlock_user(a, arg1, 0); } - break; + return ret; #endif #ifdef TARGET_NR_arm_fadvise64_64 case TARGET_NR_arm_fadvise64_64: @@ -11658,8 +10491,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, */ ret = posix_fadvise(arg1, target_offset64(arg3, arg4), target_offset64(arg5, arg6), arg2); - ret = -host_to_target_errno(ret); - break; + return -host_to_target_errno(ret); #endif #if TARGET_ABI_BITS == 32 @@ -11685,11 +10517,9 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg6 = arg7; } #endif - ret = -host_to_target_errno(posix_fadvise(arg1, - target_offset64(arg2, arg3), - target_offset64(arg4, arg5), - arg6)); - break; + ret = posix_fadvise(arg1, target_offset64(arg2, arg3), + target_offset64(arg4, arg5), arg6); + return -host_to_target_errno(ret); #endif #ifdef TARGET_NR_fadvise64 @@ -11702,10 +10532,8 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, arg4 = arg5; arg5 = arg6; } - ret = -host_to_target_errno(posix_fadvise(arg1, - target_offset64(arg2, arg3), - arg4, arg5)); - break; + ret = posix_fadvise(arg1, target_offset64(arg2, arg3), arg4, arg5); + return -host_to_target_errno(ret); #endif #else /* not a 32-bit ABI */ @@ -11725,8 +10553,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, default: break; } #endif - ret = -host_to_target_errno(posix_fadvise(arg1, arg2, arg3, arg4)); - break; + return -host_to_target_errno(posix_fadvise(arg1, arg2, arg3, arg4)); #endif #endif /* end of 64-bit ABI fadvise handling */ @@ -11736,8 +10563,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, turns private file-backed mappings into anonymous mappings. This will break MADV_DONTNEED. This is a hint, so ignoring and returning success is ok. */ - ret = get_errno(0); - break; + return 0; #endif #if TARGET_ABI_BITS == 32 case TARGET_NR_fcntl64: @@ -11756,8 +10582,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, cmd = target_to_host_fcntl_cmd(arg2); if (cmd == -TARGET_EINVAL) { - ret = cmd; - break; + return cmd; } switch(arg2) { @@ -11784,27 +10609,20 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = do_fcntl(arg1, arg2, arg3); break; } - break; + return ret; } #endif #ifdef TARGET_NR_cacheflush case TARGET_NR_cacheflush: /* self-modifying code is handled automatically, so nothing needed */ - ret = 0; - break; -#endif -#ifdef TARGET_NR_security - case TARGET_NR_security: - goto unimplemented; + return 0; #endif #ifdef TARGET_NR_getpagesize case TARGET_NR_getpagesize: - ret = TARGET_PAGE_SIZE; - break; + return TARGET_PAGE_SIZE; #endif case TARGET_NR_gettid: - ret = get_errno(gettid()); - break; + return get_errno(sys_gettid()); #ifdef TARGET_NR_readahead case TARGET_NR_readahead: #if TARGET_ABI_BITS == 32 @@ -11817,7 +10635,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else ret = get_errno(readahead(arg1, arg2, arg3)); #endif - break; + return ret; #endif #ifdef CONFIG_ATTR #ifdef TARGET_NR_setxattr @@ -11828,8 +10646,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg2) { b = lock_user(VERIFY_WRITE, arg2, arg3, 0); if (!b) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } } p = lock_user_string(arg1); @@ -11844,7 +10661,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user(p, arg1, 0); unlock_user(b, arg2, arg3); - break; + return ret; } case TARGET_NR_flistxattr: { @@ -11852,13 +10669,12 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg2) { b = lock_user(VERIFY_WRITE, arg2, arg3, 0); if (!b) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } } ret = get_errno(flistxattr(arg1, b, arg3)); unlock_user(b, arg2, arg3); - break; + return ret; } case TARGET_NR_setxattr: case TARGET_NR_lsetxattr: @@ -11867,8 +10683,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg3) { v = lock_user(VERIFY_READ, arg3, arg4, 1); if (!v) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } } p = lock_user_string(arg1); @@ -11886,15 +10701,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(n, arg2, 0); unlock_user(v, arg3, 0); } - break; + return ret; case TARGET_NR_fsetxattr: { void *n, *v = 0; if (arg3) { v = lock_user(VERIFY_READ, arg3, arg4, 1); if (!v) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } } n = lock_user_string(arg2); @@ -11906,7 +10720,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(n, arg2, 0); unlock_user(v, arg3, 0); } - break; + return ret; case TARGET_NR_getxattr: case TARGET_NR_lgetxattr: { @@ -11914,8 +10728,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg3) { v = lock_user(VERIFY_WRITE, arg3, arg4, 0); if (!v) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } } p = lock_user_string(arg1); @@ -11933,15 +10746,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(n, arg2, 0); unlock_user(v, arg3, arg4); } - break; + return ret; case TARGET_NR_fgetxattr: { void *n, *v = 0; if (arg3) { v = lock_user(VERIFY_WRITE, arg3, arg4, 0); if (!v) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } } n = lock_user_string(arg2); @@ -11953,7 +10765,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(n, arg2, 0); unlock_user(v, arg3, arg4); } - break; + return ret; case TARGET_NR_removexattr: case TARGET_NR_lremovexattr: { @@ -11972,7 +10784,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(p, arg1, 0); unlock_user(n, arg2, 0); } - break; + return ret; case TARGET_NR_fremovexattr: { void *n; @@ -11984,15 +10796,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user(n, arg2, 0); } - break; + return ret; #endif #endif /* CONFIG_ATTR */ #ifdef TARGET_NR_set_thread_area case TARGET_NR_set_thread_area: #if defined(TARGET_MIPS) ((CPUMIPSState *) cpu_env)->active_tc.CP0_UserLocal = arg1; - ret = 0; - break; + return 0; #elif defined(TARGET_CRIS) if (arg1 & 0xff) ret = -TARGET_EINVAL; @@ -12000,39 +10811,35 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ((CPUCRISState *) cpu_env)->pregs[PR_PID] = arg1; ret = 0; } - break; + return ret; #elif defined(TARGET_I386) && defined(TARGET_ABI32) - ret = do_set_thread_area(cpu_env, arg1); - break; + return do_set_thread_area(cpu_env, arg1); #elif defined(TARGET_M68K) { TaskState *ts = cpu->opaque; ts->tp_value = arg1; - ret = 0; - break; + return 0; } #else - goto unimplemented_nowarn; + return -TARGET_ENOSYS; #endif #endif #ifdef TARGET_NR_get_thread_area case TARGET_NR_get_thread_area: #if defined(TARGET_I386) && defined(TARGET_ABI32) - ret = do_get_thread_area(cpu_env, arg1); - break; + return do_get_thread_area(cpu_env, arg1); #elif defined(TARGET_M68K) { TaskState *ts = cpu->opaque; - ret = ts->tp_value; - break; + return ts->tp_value; } #else - goto unimplemented_nowarn; + return -TARGET_ENOSYS; #endif #endif #ifdef TARGET_NR_getdomainname case TARGET_NR_getdomainname: - goto unimplemented_nowarn; + return -TARGET_ENOSYS; #endif #ifdef TARGET_NR_clock_settime @@ -12044,7 +10851,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { ret = get_errno(clock_settime(arg1, &ts)); } - break; + return ret; } #endif #ifdef TARGET_NR_clock_gettime @@ -12055,7 +10862,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { ret = host_to_target_timespec(arg2, &ts); } - break; + return ret; } #endif #ifdef TARGET_NR_clock_getres @@ -12066,7 +10873,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (!is_error(ret)) { host_to_target_timespec(arg2, &ts); } - break; + return ret; } #endif #ifdef TARGET_NR_clock_nanosleep @@ -12086,24 +10893,21 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ((CPUPPCState *)cpu_env)->crf[0] |= 1; } #endif - break; + return ret; } #endif #if defined(TARGET_NR_set_tid_address) && defined(__NR_set_tid_address) case TARGET_NR_set_tid_address: - ret = get_errno(set_tid_address((int *)g2h(arg1))); - break; + return get_errno(set_tid_address((int *)g2h(arg1))); #endif case TARGET_NR_tkill: - ret = get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2))); - break; + return get_errno(safe_tkill((int)arg1, target_to_host_signal(arg2))); case TARGET_NR_tgkill: - ret = get_errno(safe_tgkill((int)arg1, (int)arg2, - target_to_host_signal(arg3))); - break; + return get_errno(safe_tgkill((int)arg1, (int)arg2, + target_to_host_signal(arg3))); #ifdef TARGET_NR_set_robust_list case TARGET_NR_set_robust_list: @@ -12120,7 +10924,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, * holding a mutex that is shared with another process via * shared memory). */ - goto unimplemented_nowarn; + return -TARGET_ENOSYS; #endif #if defined(TARGET_NR_utimensat) @@ -12138,25 +10942,23 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(sys_utimensat(arg1, NULL, tsp, arg4)); else { if (!(p = lock_user_string(arg2))) { - ret = -TARGET_EFAULT; - goto fail; + return -TARGET_EFAULT; } ret = get_errno(sys_utimensat(arg1, path(p), tsp, arg4)); unlock_user(p, arg2, 0); } } - break; + return ret; #endif case TARGET_NR_futex: - ret = do_futex(arg1, arg2, arg3, arg4, arg5, arg6); - break; + return do_futex(arg1, arg2, arg3, arg4, arg5, arg6); #if defined(TARGET_NR_inotify_init) && defined(__NR_inotify_init) case TARGET_NR_inotify_init: ret = get_errno(sys_inotify_init()); if (ret >= 0) { fd_trans_register(ret, &target_inotify_trans); } - break; + return ret; #endif #ifdef CONFIG_INOTIFY1 #if defined(TARGET_NR_inotify_init1) && defined(__NR_inotify_init1) @@ -12166,7 +10968,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret >= 0) { fd_trans_register(ret, &target_inotify_trans); } - break; + return ret; #endif #endif #if defined(TARGET_NR_inotify_add_watch) && defined(__NR_inotify_add_watch) @@ -12174,12 +10976,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, p = lock_user_string(arg2); ret = get_errno(sys_inotify_add_watch(arg1, path(p), arg3)); unlock_user(p, arg2, 0); - break; + return ret; #endif #if defined(TARGET_NR_inotify_rm_watch) && defined(__NR_inotify_rm_watch) case TARGET_NR_inotify_rm_watch: - ret = get_errno(sys_inotify_rm_watch(arg1, arg2)); - break; + return get_errno(sys_inotify_rm_watch(arg1, arg2)); #endif #if defined(TARGET_NR_mq_open) && defined(__NR_mq_open) @@ -12193,28 +10994,27 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, pposix_mq_attr = NULL; if (arg4) { if (copy_from_user_mq_attr(&posix_mq_attr, arg4) != 0) { - goto efault; + return -TARGET_EFAULT; } pposix_mq_attr = &posix_mq_attr; } p = lock_user_string(arg1 - 1); if (!p) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno(mq_open(p, host_flags, arg3, pposix_mq_attr)); unlock_user (p, arg1, 0); } - break; + return ret; case TARGET_NR_mq_unlink: p = lock_user_string(arg1 - 1); if (!p) { - ret = -TARGET_EFAULT; - break; + return -TARGET_EFAULT; } ret = get_errno(mq_unlink(p)); unlock_user (p, arg1, 0); - break; + return ret; case TARGET_NR_mq_timedsend: { @@ -12230,7 +11030,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } unlock_user (p, arg2, arg3); } - break; + return ret; case TARGET_NR_mq_timedreceive: { @@ -12251,7 +11051,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg4 != 0) put_user_u32(prio, arg4); } - break; + return ret; /* Not implemented for now... */ /* case TARGET_NR_mq_notify: */ @@ -12272,7 +11072,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, copy_to_user_mq_attr(arg3, &posix_mq_attr_out); } } - break; + return ret; #endif #ifdef CONFIG_SPLICE @@ -12281,7 +11081,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, { ret = get_errno(tee(arg1,arg2,arg3,arg4)); } - break; + return ret; #endif #ifdef TARGET_NR_splice case TARGET_NR_splice: @@ -12290,29 +11090,29 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, loff_t *ploff_in = NULL, *ploff_out = NULL; if (arg2) { if (get_user_u64(loff_in, arg2)) { - goto efault; + return -TARGET_EFAULT; } ploff_in = &loff_in; } if (arg4) { if (get_user_u64(loff_out, arg4)) { - goto efault; + return -TARGET_EFAULT; } ploff_out = &loff_out; } ret = get_errno(splice(arg1, ploff_in, arg3, ploff_out, arg5, arg6)); if (arg2) { if (put_user_u64(loff_in, arg2)) { - goto efault; + return -TARGET_EFAULT; } } if (arg4) { if (put_user_u64(loff_out, arg4)) { - goto efault; + return -TARGET_EFAULT; } } } - break; + return ret; #endif #ifdef TARGET_NR_vmsplice case TARGET_NR_vmsplice: @@ -12325,7 +11125,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -host_to_target_errno(errno); } } - break; + return ret; #endif #endif /* CONFIG_SPLICE */ #ifdef CONFIG_EVENTFD @@ -12335,7 +11135,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret >= 0) { fd_trans_register(ret, &target_eventfd_trans); } - break; + return ret; #endif #if defined(TARGET_NR_eventfd2) case TARGET_NR_eventfd2: @@ -12351,7 +11151,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (ret >= 0) { fd_trans_register(ret, &target_eventfd_trans); } - break; + return ret; } #endif #endif /* CONFIG_EVENTFD */ @@ -12363,7 +11163,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else ret = get_errno(fallocate(arg1, arg2, arg3, arg4)); #endif - break; + return ret; #endif #if defined(CONFIG_SYNC_FILE_RANGE) #if defined(TARGET_NR_sync_file_range) @@ -12379,7 +11179,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else ret = get_errno(sync_file_range(arg1, arg2, arg3, arg4)); #endif - break; + return ret; #endif #if defined(TARGET_NR_sync_file_range2) case TARGET_NR_sync_file_range2: @@ -12390,29 +11190,25 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, #else ret = get_errno(sync_file_range(arg1, arg3, arg4, arg2)); #endif - break; + return ret; #endif #endif #if defined(TARGET_NR_signalfd4) case TARGET_NR_signalfd4: - ret = do_signalfd4(arg1, arg2, arg4); - break; + return do_signalfd4(arg1, arg2, arg4); #endif #if defined(TARGET_NR_signalfd) case TARGET_NR_signalfd: - ret = do_signalfd4(arg1, arg2, 0); - break; + return do_signalfd4(arg1, arg2, 0); #endif #if defined(CONFIG_EPOLL) #if defined(TARGET_NR_epoll_create) case TARGET_NR_epoll_create: - ret = get_errno(epoll_create(arg1)); - break; + return get_errno(epoll_create(arg1)); #endif #if defined(TARGET_NR_epoll_create1) && defined(CONFIG_EPOLL_CREATE1) case TARGET_NR_epoll_create1: - ret = get_errno(epoll_create1(arg1)); - break; + return get_errno(epoll_create1(arg1)); #endif #if defined(TARGET_NR_epoll_ctl) case TARGET_NR_epoll_ctl: @@ -12422,7 +11218,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg4) { struct target_epoll_event *target_ep; if (!lock_user_struct(VERIFY_READ, target_ep, arg4, 1)) { - goto efault; + return -TARGET_EFAULT; } ep.events = tswap32(target_ep->events); /* The epoll_data_t union is just opaque data to the kernel, @@ -12433,8 +11229,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user_struct(target_ep, arg4, 0); epp = &ep; } - ret = get_errno(epoll_ctl(arg1, arg2, arg3, epp)); - break; + return get_errno(epoll_ctl(arg1, arg2, arg3, epp)); } #endif @@ -12453,21 +11248,19 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, int timeout = arg4; if (maxevents <= 0 || maxevents > TARGET_EP_MAX_EVENTS) { - ret = -TARGET_EINVAL; - break; + return -TARGET_EINVAL; } target_ep = lock_user(VERIFY_WRITE, arg2, maxevents * sizeof(struct target_epoll_event), 1); if (!target_ep) { - goto efault; + return -TARGET_EFAULT; } ep = g_try_new(struct epoll_event, maxevents); if (!ep) { unlock_user(target_ep, arg2, 0); - ret = -TARGET_ENOMEM; - break; + return -TARGET_ENOMEM; } switch (num) { @@ -12521,7 +11314,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, unlock_user(target_ep, arg2, 0); } g_free(ep); - break; + return ret; } #endif #endif @@ -12534,7 +11327,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, int resource = target_to_host_resource(arg2); if (arg3) { if (!lock_user_struct(VERIFY_READ, target_rnew, arg3, 1)) { - goto efault; + return -TARGET_EFAULT; } rnew.rlim_cur = tswap64(target_rnew->rlim_cur); rnew.rlim_max = tswap64(target_rnew->rlim_max); @@ -12545,13 +11338,13 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(sys_prlimit64(arg1, resource, rnewp, arg4 ? &rold : 0)); if (!is_error(ret) && arg4) { if (!lock_user_struct(VERIFY_WRITE, target_rold, arg4, 1)) { - goto efault; + return -TARGET_EFAULT; } target_rold->rlim_cur = tswap64(rold.rlim_cur); target_rold->rlim_max = tswap64(rold.rlim_max); unlock_user_struct(target_rold, arg4, 1); } - break; + return ret; } #endif #ifdef TARGET_NR_gethostname @@ -12564,7 +11357,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } else { ret = -TARGET_EFAULT; } - break; + return ret; } #endif #ifdef TARGET_NR_atomic_cmpxchg_32 @@ -12585,17 +11378,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, } if (mem_value == arg2) put_user_u32(arg1, arg6); - ret = mem_value; - break; + return mem_value; } #endif #ifdef TARGET_NR_atomic_barrier case TARGET_NR_atomic_barrier: - { - /* Like the kernel implementation and the qemu arm barrier, no-op this? */ - ret = 0; - break; - } + /* Like the kernel implementation and the + qemu arm barrier, no-op this? */ + return 0; #endif #ifdef TARGET_NR_timer_create @@ -12617,7 +11407,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, phost_sevp = &host_sevp; ret = target_to_host_sigevent(phost_sevp, arg2); if (ret != 0) { - break; + return ret; } } @@ -12626,11 +11416,11 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, phtimer = NULL; } else { if (put_user(TIMER_MAGIC | timer_index, arg3, target_timer_t)) { - goto efault; + return -TARGET_EFAULT; } } } - break; + return ret; } #endif @@ -12650,15 +11440,15 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, struct itimerspec hspec_new = {{0},}, hspec_old = {{0},}; if (target_to_host_itimerspec(&hspec_new, arg3)) { - goto efault; + return -TARGET_EFAULT; } ret = get_errno( timer_settime(htimer, arg2, &hspec_new, &hspec_old)); if (arg4 && host_to_target_itimerspec(arg4, &hspec_old)) { - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; } #endif @@ -12681,7 +11471,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = -TARGET_EFAULT; } } - break; + return ret; } #endif @@ -12698,7 +11488,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(timer_getoverrun(htimer)); } fd_trans_unregister(ret); - break; + return ret; } #endif @@ -12715,15 +11505,14 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(timer_delete(htimer)); g_posix_timers[timerid] = 0; } - break; + return ret; } #endif #if defined(TARGET_NR_timerfd_create) && defined(CONFIG_TIMERFD) case TARGET_NR_timerfd_create: - ret = get_errno(timerfd_create(arg1, - target_to_host_bitmask(arg2, fcntl_flags_tbl))); - break; + return get_errno(timerfd_create(arg1, + target_to_host_bitmask(arg2, fcntl_flags_tbl))); #endif #if defined(TARGET_NR_timerfd_gettime) && defined(CONFIG_TIMERFD) @@ -12734,10 +11523,10 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(timerfd_gettime(arg1, &its_curr)); if (arg2 && host_to_target_itimerspec(arg2, &its_curr)) { - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #if defined(TARGET_NR_timerfd_settime) && defined(CONFIG_TIMERFD) @@ -12747,7 +11536,7 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, if (arg3) { if (target_to_host_itimerspec(&its_new, arg3)) { - goto efault; + return -TARGET_EFAULT; } p_new = &its_new; } else { @@ -12757,64 +11546,82 @@ abi_long do_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(timerfd_settime(arg1, arg2, p_new, &its_old)); if (arg4 && host_to_target_itimerspec(arg4, &its_old)) { - goto efault; + return -TARGET_EFAULT; } } - break; + return ret; #endif #if defined(TARGET_NR_ioprio_get) && defined(__NR_ioprio_get) case TARGET_NR_ioprio_get: - ret = get_errno(ioprio_get(arg1, arg2)); - break; + return get_errno(ioprio_get(arg1, arg2)); #endif #if defined(TARGET_NR_ioprio_set) && defined(__NR_ioprio_set) case TARGET_NR_ioprio_set: - ret = get_errno(ioprio_set(arg1, arg2, arg3)); - break; + return get_errno(ioprio_set(arg1, arg2, arg3)); #endif #if defined(TARGET_NR_setns) && defined(CONFIG_SETNS) case TARGET_NR_setns: - ret = get_errno(setns(arg1, arg2)); - break; + return get_errno(setns(arg1, arg2)); #endif #if defined(TARGET_NR_unshare) && defined(CONFIG_SETNS) case TARGET_NR_unshare: - ret = get_errno(unshare(arg1)); - break; + return get_errno(unshare(arg1)); #endif #if defined(TARGET_NR_kcmp) && defined(__NR_kcmp) case TARGET_NR_kcmp: - ret = get_errno(kcmp(arg1, arg2, arg3, arg4, arg5)); - break; + return get_errno(kcmp(arg1, arg2, arg3, arg4, arg5)); #endif #ifdef TARGET_NR_swapcontext case TARGET_NR_swapcontext: /* PowerPC specific. */ - ret = do_swapcontext(cpu_env, arg1, arg2, arg3); - break; + return do_swapcontext(cpu_env, arg1, arg2, arg3); #endif default: - unimplemented: qemu_log_mask(LOG_UNIMP, "Unsupported syscall: %d\n", num); -#if defined(TARGET_NR_setxattr) || defined(TARGET_NR_get_thread_area) || defined(TARGET_NR_getdomainname) || defined(TARGET_NR_set_robust_list) - unimplemented_nowarn: -#endif - ret = -TARGET_ENOSYS; - break; + return -TARGET_ENOSYS; + } + return ret; +} + +abi_long do_syscall(void *cpu_env, int num, abi_long arg1, + abi_long arg2, abi_long arg3, abi_long arg4, + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) +{ + CPUState *cpu = ENV_GET_CPU(cpu_env); + abi_long ret; + +#ifdef DEBUG_ERESTARTSYS + /* Debug-only code for exercising the syscall-restart code paths + * in the per-architecture cpu main loops: restart every syscall + * the guest makes once before letting it through. + */ + { + static bool flag; + flag = !flag; + if (flag) { + return -TARGET_ERESTARTSYS; + } } -fail: -#ifdef DEBUG - gemu_log(" = " TARGET_ABI_FMT_ld "\n", ret); #endif - if(do_strace) + + trace_guest_user_syscall(cpu, num, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8); + + if (unlikely(do_strace)) { + print_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8); print_syscall_ret(num, ret); + } else { + ret = do_syscall1(cpu_env, num, arg1, arg2, arg3, arg4, + arg5, arg6, arg7, arg8); + } + trace_guest_user_syscall_ret(cpu, num, ret); return ret; -efault: - ret = -TARGET_EFAULT; - goto fail; } diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 40bb60ef4c..12c8407144 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -203,6 +203,11 @@ struct target_ip_mreq_source { uint32_t imr_sourceaddr; }; +struct target_linger { + abi_int l_onoff; /* Linger active */ + abi_int l_linger; /* How long to linger for */ +}; + struct target_timeval { abi_long tv_sec; abi_long tv_usec; @@ -858,6 +863,34 @@ struct target_pollfd { #define TARGET_FS_IOC_FIEMAP TARGET_IOWR('f',11,struct fiemap) +/* usb ioctls */ +#define TARGET_USBDEVFS_CONTROL TARGET_IOWRU('U', 0) +#define TARGET_USBDEVFS_BULK TARGET_IOWRU('U', 2) +#define TARGET_USBDEVFS_RESETEP TARGET_IORU('U', 3) +#define TARGET_USBDEVFS_SETINTERFACE TARGET_IORU('U', 4) +#define TARGET_USBDEVFS_SETCONFIGURATION TARGET_IORU('U', 5) +#define TARGET_USBDEVFS_GETDRIVER TARGET_IOWU('U', 8) +#define TARGET_USBDEVFS_SUBMITURB TARGET_IORU('U', 10) +#define TARGET_USBDEVFS_DISCARDURB TARGET_IO('U', 11) +#define TARGET_USBDEVFS_REAPURB TARGET_IOWU('U', 12) +#define TARGET_USBDEVFS_REAPURBNDELAY TARGET_IOWU('U', 13) +#define TARGET_USBDEVFS_DISCSIGNAL TARGET_IORU('U', 14) +#define TARGET_USBDEVFS_CLAIMINTERFACE TARGET_IORU('U', 15) +#define TARGET_USBDEVFS_RELEASEINTERFACE TARGET_IORU('U', 16) +#define TARGET_USBDEVFS_CONNECTINFO TARGET_IOWU('U', 17) +#define TARGET_USBDEVFS_IOCTL TARGET_IOWRU('U', 18) +#define TARGET_USBDEVFS_HUB_PORTINFO TARGET_IORU('U', 19) +#define TARGET_USBDEVFS_RESET TARGET_IO('U', 20) +#define TARGET_USBDEVFS_CLEAR_HALT TARGET_IORU('U', 21) +#define TARGET_USBDEVFS_DISCONNECT TARGET_IO('U', 22) +#define TARGET_USBDEVFS_CONNECT TARGET_IO('U', 23) +#define TARGET_USBDEVFS_CLAIM_PORT TARGET_IORU('U', 24) +#define TARGET_USBDEVFS_RELEASE_PORT TARGET_IORU('U', 25) +#define TARGET_USBDEVFS_GET_CAPABILITIES TARGET_IORU('U', 26) +#define TARGET_USBDEVFS_DISCONNECT_CLAIM TARGET_IORU('U', 27) +#define TARGET_USBDEVFS_DROP_PRIVILEGES TARGET_IOWU('U', 30) +#define TARGET_USBDEVFS_GET_SPEED TARGET_IO('U', 31) + /* cdrom commands */ #define TARGET_CDROMPAUSE 0x5301 /* Pause Audio Operation */ #define TARGET_CDROMRESUME 0x5302 /* Resume paused Audio Operation */ @@ -1774,7 +1807,7 @@ struct target_stat { abi_ulong st_rdev; abi_long st_size; abi_long st_blksize; - abi_long st_blocks; /* Number 512-byte blocks allocated. */ + abi_long st_blocks; /* Number 512-byte blocks allocated. */ abi_ulong target_st_atime; abi_ulong target_st_atime_nsec; @@ -1783,7 +1816,7 @@ struct target_stat { abi_ulong target_st_ctime; abi_ulong target_st_ctime_nsec; - abi_long __unused[3]; + abi_long __unused[3]; }; #elif defined(TARGET_S390X) struct target_stat { diff --git a/linux-user/syscall_types.h b/linux-user/syscall_types.h index 24631b09be..b98a23b0f1 100644 --- a/linux-user/syscall_types.h +++ b/linux-user/syscall_types.h @@ -266,3 +266,71 @@ STRUCT(blkpg_ioctl_arg, TYPE_INT, /* flags */ TYPE_INT, /* datalen */ TYPE_PTRVOID) /* data */ + +#if defined(CONFIG_USBFS) +/* usb device ioctls */ +STRUCT(usbdevfs_ctrltransfer, + TYPE_CHAR, /* bRequestType */ + TYPE_CHAR, /* bRequest */ + TYPE_SHORT, /* wValue */ + TYPE_SHORT, /* wIndex */ + TYPE_SHORT, /* wLength */ + TYPE_INT, /* timeout */ + TYPE_PTRVOID) /* data */ + +STRUCT(usbdevfs_bulktransfer, + TYPE_INT, /* ep */ + TYPE_INT, /* len */ + TYPE_INT, /* timeout */ + TYPE_PTRVOID) /* data */ + +STRUCT(usbdevfs_setinterface, + TYPE_INT, /* interface */ + TYPE_INT) /* altsetting */ + +STRUCT(usbdevfs_disconnectsignal, + TYPE_INT, /* signr */ + TYPE_PTRVOID) /* context */ + +STRUCT(usbdevfs_getdriver, + TYPE_INT, /* interface */ + MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */ + +STRUCT(usbdevfs_connectinfo, + TYPE_INT, /* devnum */ + TYPE_CHAR) /* slow */ + +STRUCT(usbdevfs_iso_packet_desc, + TYPE_INT, /* length */ + TYPE_INT, /* actual_length */ + TYPE_INT) /* status */ + +STRUCT(usbdevfs_urb, + TYPE_CHAR, /* type */ + TYPE_CHAR, /* endpoint */ + TYPE_INT, /* status */ + TYPE_INT, /* flags */ + TYPE_PTRVOID, /* buffer */ + TYPE_INT, /* buffer_length */ + TYPE_INT, /* actual_length */ + TYPE_INT, /* start_frame */ + TYPE_INT, /* union number_of_packets stream_id */ + TYPE_INT, /* error_count */ + TYPE_INT, /* signr */ + TYPE_PTRVOID, /* usercontext */ + MK_ARRAY(MK_STRUCT(STRUCT_usbdevfs_iso_packet_desc), 0)) /* desc */ + +STRUCT(usbdevfs_ioctl, + TYPE_INT, /* ifno */ + TYPE_INT, /* ioctl_code */ + TYPE_PTRVOID) /* data */ + +STRUCT(usbdevfs_hub_portinfo, + TYPE_CHAR, /* nports */ + MK_ARRAY(TYPE_CHAR, 127)) /* port */ + +STRUCT(usbdevfs_disconnect_claim, + TYPE_INT, /* interface */ + TYPE_INT, /* flags */ + MK_ARRAY(TYPE_CHAR, USBDEVFS_MAXDRIVERNAME + 1)) /* driver */ +#endif /* CONFIG_USBFS */ diff --git a/linux-user/trace-events b/linux-user/trace-events index 68f36ac8fd..6df234bbb6 100644 --- a/linux-user/trace-events +++ b/linux-user/trace-events @@ -1,6 +1,7 @@ # See docs/devel/tracing.txt for syntax documentation. -# linux-user/signal.c +# signal.c +# */signal.c user_setup_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 user_setup_rt_frame(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 user_do_rt_sigreturn(void *env, uint64_t frame_addr) "env=%p frame_addr=0x%"PRIx64 diff --git a/linux-user/uaccess.c b/linux-user/uaccess.c index 0a5c0b0b29..e215ecc2a6 100644 --- a/linux-user/uaccess.c +++ b/linux-user/uaccess.c @@ -30,7 +30,7 @@ abi_long copy_to_user(abi_ulong gaddr, void *hptr, size_t len) if ((ghptr = lock_user(VERIFY_WRITE, gaddr, len, 0))) { memcpy(ghptr, hptr, len); - unlock_user(ghptr, gaddr, len); + unlock_user(ghptr, gaddr, len); } else ret = -TARGET_EFAULT; diff --git a/linux-user/vm86.c b/linux-user/vm86.c index 3829b9a677..9c393df424 100644 --- a/linux-user/vm86.c +++ b/linux-user/vm86.c @@ -257,7 +257,7 @@ void handle_vm86_trap(CPUX86State *env, int trapno) #define CHECK_IF_IN_TRAP() \ if ((ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_active) && \ (ts->vm86plus.vm86plus.flags & TARGET_vm86dbg_TFpendig)) \ - newflags |= TF_MASK + newflags |= TF_MASK #define VM86_FAULT_RETURN \ if ((ts->vm86plus.vm86plus.flags & TARGET_force_return_for_pic) && \ diff --git a/linux-user/x86_64/target_syscall.h b/linux-user/x86_64/target_syscall.h index 983fb23d9b..5e221e1d9d 100644 --- a/linux-user/x86_64/target_syscall.h +++ b/linux-user/x86_64/target_syscall.h @@ -12,7 +12,7 @@ struct target_pt_regs { abi_ulong rbp; abi_ulong rbx; /* arguments: non interrupts/non tracing syscalls only save up to here */ - abi_ulong r11; + abi_ulong r11; abi_ulong r10; abi_ulong r9; abi_ulong r8; diff --git a/linux-user/xtensa/cpu_loop.c b/linux-user/xtensa/cpu_loop.c index d142988ebe..bee78edb8a 100644 --- a/linux-user/xtensa/cpu_loop.c +++ b/linux-user/xtensa/cpu_loop.c @@ -239,13 +239,10 @@ void cpu_loop(CPUXtensaState *env) } break; case EXCP_DEBUG: - trapnr = gdb_handlesig(cs, TARGET_SIGTRAP); - if (trapnr) { - info.si_signo = trapnr; - info.si_errno = 0; - info.si_code = TARGET_TRAP_BRKPT; - queue_signal(env, trapnr, QEMU_SI_FAULT, &info); - } + info.si_signo = TARGET_SIGTRAP; + info.si_errno = 0; + info.si_code = TARGET_TRAP_BRKPT; + queue_signal(env, info.si_signo, QEMU_SI_FAULT, &info); break; case EXC_DEBUG: default: diff --git a/linux-user/xtensa/target_flat.h b/linux-user/xtensa/target_flat.h new file mode 100644 index 0000000000..732adddb0d --- /dev/null +++ b/linux-user/xtensa/target_flat.h @@ -0,0 +1,10 @@ +/* If your arch needs to do custom stuff, create your own target_flat.h + * header file in linux-user// + */ +#define flat_argvp_envp_on_stack() 0 +#define flat_reloc_valid(reloc, size) ((reloc) <= (size)) +#define flat_old_ram_flag(flag) (flag) +#define flat_get_relocate_addr(relval) (relval) +#define flat_get_addr_from_rp(rp, relval, flags, persistent) (rp) +#define flat_set_persistent(relval, persistent) (*persistent) +#define flat_put_addr_at_rp(rp, addr, relval) put_user_ual(addr, rp) diff --git a/memory.c b/memory.c index 316b37ac05..05df77a5d8 100644 --- a/memory.c +++ b/memory.c @@ -29,7 +29,6 @@ #include "exec/ram_addr.h" #include "sysemu/kvm.h" #include "sysemu/sysemu.h" -#include "hw/misc/mmio_interface.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" @@ -40,7 +39,7 @@ static bool memory_region_update_pending; static bool ioeventfd_update_pending; static bool global_dirty_log = false; -static QTAILQ_HEAD(memory_listeners, MemoryListener) memory_listeners +static QTAILQ_HEAD(, MemoryListener) memory_listeners = QTAILQ_HEAD_INITIALIZER(memory_listeners); static QTAILQ_HEAD(, AddressSpace) address_spaces @@ -114,8 +113,7 @@ enum ListenerDirection { Forward, Reverse }; } \ break; \ case Reverse: \ - QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, \ - memory_listeners, link) { \ + QTAILQ_FOREACH_REVERSE(_listener, &memory_listeners, link) { \ if (_listener->_callback) { \ _listener->_callback(_listener, ##_args); \ } \ @@ -129,19 +127,17 @@ enum ListenerDirection { Forward, Reverse }; #define MEMORY_LISTENER_CALL(_as, _callback, _direction, _section, _args...) \ do { \ MemoryListener *_listener; \ - struct memory_listeners_as *list = &(_as)->listeners; \ \ switch (_direction) { \ case Forward: \ - QTAILQ_FOREACH(_listener, list, link_as) { \ + QTAILQ_FOREACH(_listener, &(_as)->listeners, link_as) { \ if (_listener->_callback) { \ _listener->_callback(_listener, _section, ##_args); \ } \ } \ break; \ case Reverse: \ - QTAILQ_FOREACH_REVERSE(_listener, list, memory_listeners_as, \ - link_as) { \ + QTAILQ_FOREACH_REVERSE(_listener, &(_as)->listeners, link_as) { \ if (_listener->_callback) { \ _listener->_callback(_listener, _section, ##_args); \ } \ @@ -217,6 +213,8 @@ struct FlatRange { uint8_t dirty_log_mask; bool romd_mode; bool readonly; + bool nonvolatile; + int has_coalesced_range; }; #define FOR_EACH_FLAT_RANGE(var, view) \ @@ -232,6 +230,7 @@ section_from_flat_range(FlatRange *fr, FlatView *fv) .size = fr->addr.size, .offset_within_address_space = int128_get64(fr->addr.start), .readonly = fr->readonly, + .nonvolatile = fr->nonvolatile, }; } @@ -241,7 +240,8 @@ static bool flatrange_equal(FlatRange *a, FlatRange *b) && addrrange_equal(a->addr, b->addr) && a->offset_in_region == b->offset_in_region && a->romd_mode == b->romd_mode - && a->readonly == b->readonly; + && a->readonly == b->readonly + && a->nonvolatile == b->nonvolatile; } static FlatView *flatview_new(MemoryRegion *mr_root) @@ -313,7 +313,8 @@ static bool can_merge(FlatRange *r1, FlatRange *r2) int128_make64(r2->offset_in_region)) && r1->dirty_log_mask == r2->dirty_log_mask && r1->romd_mode == r2->romd_mode - && r1->readonly == r2->readonly; + && r1->readonly == r2->readonly + && r1->nonvolatile == r2->nonvolatile; } /* Attempt to simplify a view by merging adjacent ranges */ @@ -375,6 +376,33 @@ static void adjust_endianness(MemoryRegion *mr, uint64_t *data, unsigned size) } } +static inline void memory_region_shift_read_access(uint64_t *value, + signed shift, + uint64_t mask, + uint64_t tmp) +{ + if (shift >= 0) { + *value |= (tmp & mask) << shift; + } else { + *value |= (tmp & mask) >> -shift; + } +} + +static inline uint64_t memory_region_shift_write_access(uint64_t *value, + signed shift, + uint64_t mask) +{ + uint64_t tmp; + + if (shift >= 0) { + tmp = (*value >> shift) & mask; + } else { + tmp = (*value << -shift) & mask; + } + + return tmp; +} + static hwaddr memory_region_to_absolute_addr(MemoryRegion *mr, hwaddr offset) { MemoryRegion *root; @@ -397,37 +425,11 @@ static int get_cpu_index(void) return -1; } -static MemTxResult memory_region_oldmmio_read_accessor(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask, - MemTxAttrs attrs) -{ - uint64_t tmp; - - tmp = mr->ops->old_mmio.read[ctz32(size)](mr->opaque, addr); - 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; -} - static MemTxResult memory_region_read_accessor(MemoryRegion *mr, hwaddr addr, uint64_t *value, unsigned size, - unsigned shift, + signed shift, uint64_t mask, MemTxAttrs attrs) { @@ -445,7 +447,7 @@ static MemTxResult memory_region_read_accessor(MemoryRegion *mr, 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; + memory_region_shift_read_access(value, shift, mask, tmp); return MEMTX_OK; } @@ -453,7 +455,7 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr, hwaddr addr, uint64_t *value, unsigned size, - unsigned shift, + signed shift, uint64_t mask, MemTxAttrs attrs) { @@ -472,47 +474,20 @@ static MemTxResult memory_region_read_with_attrs_accessor(MemoryRegion *mr, 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; + memory_region_shift_read_access(value, shift, mask, tmp); return r; } -static MemTxResult memory_region_oldmmio_write_accessor(MemoryRegion *mr, - hwaddr addr, - uint64_t *value, - unsigned size, - unsigned shift, - uint64_t mask, - MemTxAttrs attrs) -{ - uint64_t tmp; - - tmp = (*value >> shift) & mask; - 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; -} - static MemTxResult memory_region_write_accessor(MemoryRegion *mr, hwaddr addr, uint64_t *value, unsigned size, - unsigned shift, + signed shift, uint64_t mask, MemTxAttrs attrs) { - uint64_t tmp; + uint64_t tmp = memory_region_shift_write_access(value, shift, mask); - tmp = (*value >> shift) & mask; if (mr->subpage) { trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); } else if (mr == &io_mem_notdirty) { @@ -532,13 +507,12 @@ static MemTxResult memory_region_write_with_attrs_accessor(MemoryRegion *mr, hwaddr addr, uint64_t *value, unsigned size, - unsigned shift, + signed shift, uint64_t mask, MemTxAttrs attrs) { - uint64_t tmp; + uint64_t tmp = memory_region_shift_write_access(value, shift, mask); - tmp = (*value >> shift) & mask; if (mr->subpage) { trace_memory_region_subpage_write(get_cpu_index(), mr, addr, tmp, size); } else if (mr == &io_mem_notdirty) { @@ -563,7 +537,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, hwaddr addr, uint64_t *value, unsigned size, - unsigned shift, + signed shift, uint64_t mask, MemTxAttrs attrs), MemoryRegion *mr, @@ -583,7 +557,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, /* FIXME: support unaligned access? */ access_size = MAX(MIN(size, access_size_max), access_size_min); - access_mask = -1ULL >> (64 - access_size * 8); + access_mask = MAKE_64BIT_MASK(0, access_size * 8); if (memory_region_big_endian(mr)) { for (i = 0; i < size; i += access_size) { r |= access_fn(mr, addr + i, value, access_size, @@ -620,7 +594,8 @@ static void render_memory_region(FlatView *view, MemoryRegion *mr, Int128 base, AddrRange clip, - bool readonly) + bool readonly, + bool nonvolatile) { MemoryRegion *subregion; unsigned i; @@ -636,6 +611,7 @@ static void render_memory_region(FlatView *view, int128_addto(&base, int128_make64(mr->addr)); readonly |= mr->readonly; + nonvolatile |= mr->nonvolatile; tmp = addrrange_make(base, mr->size); @@ -648,13 +624,15 @@ static void render_memory_region(FlatView *view, if (mr->alias) { int128_subfrom(&base, int128_make64(mr->alias->addr)); int128_subfrom(&base, int128_make64(mr->alias_offset)); - render_memory_region(view, mr->alias, base, clip, readonly); + render_memory_region(view, mr->alias, base, clip, + readonly, nonvolatile); return; } /* Render subregions in priority order. */ QTAILQ_FOREACH(subregion, &mr->subregions, subregions_link) { - render_memory_region(view, subregion, base, clip, readonly); + render_memory_region(view, subregion, base, clip, + readonly, nonvolatile); } if (!mr->terminates) { @@ -669,6 +647,8 @@ static void render_memory_region(FlatView *view, fr.dirty_log_mask = memory_region_get_dirty_log_mask(mr); fr.romd_mode = mr->romd_mode; fr.readonly = readonly; + fr.nonvolatile = nonvolatile; + fr.has_coalesced_range = 0; /* Render the region itself into any gaps left by the current view. */ for (i = 0; i < view->nr && int128_nz(remain); ++i) { @@ -754,7 +734,8 @@ static FlatView *generate_memory_topology(MemoryRegion *mr) if (mr) { render_memory_region(view, mr, int128_zero(), - addrrange_make(int128_zero(), int128_2_64()), false); + addrrange_make(int128_zero(), int128_2_64()), + false, false); } flatview_simplify(view); @@ -868,6 +849,49 @@ static void address_space_update_ioeventfds(AddressSpace *as) flatview_unref(view); } +static void flat_range_coalesced_io_del(FlatRange *fr, AddressSpace *as) +{ + if (!fr->has_coalesced_range) { + return; + } + + if (--fr->has_coalesced_range > 0) { + return; + } + + MEMORY_LISTENER_UPDATE_REGION(fr, as, Reverse, coalesced_io_del, + int128_get64(fr->addr.start), + int128_get64(fr->addr.size)); +} + +static void flat_range_coalesced_io_add(FlatRange *fr, AddressSpace *as) +{ + MemoryRegion *mr = fr->mr; + CoalescedMemoryRange *cmr; + AddrRange tmp; + + if (QTAILQ_EMPTY(&mr->coalesced)) { + return; + } + + if (fr->has_coalesced_range++) { + return; + } + + QTAILQ_FOREACH(cmr, &mr->coalesced, link) { + tmp = addrrange_shift(cmr->addr, + int128_sub(fr->addr.start, + int128_make64(fr->offset_in_region))); + if (!addrrange_intersects(tmp, fr->addr)) { + continue; + } + tmp = addrrange_intersection(tmp, fr->addr); + MEMORY_LISTENER_UPDATE_REGION(fr, as, Forward, coalesced_io_add, + int128_get64(tmp.start), + int128_get64(tmp.size)); + } +} + static void address_space_update_topology_pass(AddressSpace *as, const FlatView *old_view, const FlatView *new_view, @@ -900,6 +924,7 @@ static void address_space_update_topology_pass(AddressSpace *as, /* In old but not in new, or in both but attributes changed. */ if (!adding) { + flat_range_coalesced_io_del(frold, as); MEMORY_LISTENER_UPDATE_REGION(frold, as, Reverse, region_del); } @@ -928,6 +953,7 @@ static void address_space_update_topology_pass(AddressSpace *as, if (adding) { MEMORY_LISTENER_UPDATE_REGION(frnew, as, Forward, region_add); + flat_range_coalesced_io_add(frnew, as); } ++inew; @@ -1249,7 +1275,8 @@ static uint64_t unassigned_mem_read(void *opaque, hwaddr addr, printf("Unassigned mem read " TARGET_FMT_plx "\n", addr); #endif if (current_cpu != NULL) { - cpu_unassigned_access(current_cpu, addr, false, false, 0, size); + bool is_exec = current_cpu->mem_io_access_type == MMU_INST_FETCH; + cpu_unassigned_access(current_cpu, addr, false, is_exec, 0, size); } return 0; } @@ -1394,16 +1421,12 @@ static MemTxResult memory_region_dispatch_read1(MemoryRegion *mr, mr->ops->impl.max_access_size, memory_region_read_accessor, mr, attrs); - } else if (mr->ops->read_with_attrs) { + } else { return access_with_adjusted_size(addr, pval, size, mr->ops->impl.min_access_size, mr->ops->impl.max_access_size, memory_region_read_with_attrs_accessor, mr, attrs); - } else { - return access_with_adjusted_size(addr, pval, size, 1, 4, - memory_region_oldmmio_read_accessor, - mr, attrs); } } @@ -1475,17 +1498,13 @@ MemTxResult memory_region_dispatch_write(MemoryRegion *mr, mr->ops->impl.max_access_size, memory_region_write_accessor, mr, attrs); - } else if (mr->ops->write_with_attrs) { + } else { return access_with_adjusted_size(addr, &data, size, mr->ops->impl.min_access_size, mr->ops->impl.max_access_size, memory_region_write_with_attrs_accessor, mr, attrs); - } else { - return access_with_adjusted_size(addr, &data, size, 1, 4, - memory_region_oldmmio_write_accessor, - mr, attrs); } } @@ -1518,12 +1537,18 @@ void memory_region_init_ram_shared_nomigrate(MemoryRegion *mr, bool share, Error **errp) { + Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, share, mr, errp); + mr->ram_block = qemu_ram_alloc(size, share, mr, &err); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + if (err) { + mr->size = int128_zero(); + object_unparent(OBJECT(mr)); + error_propagate(errp, err); + } } void memory_region_init_resizeable_ram(MemoryRegion *mr, @@ -1536,32 +1561,44 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr, void *host), Error **errp) { + Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->ram_block = qemu_ram_alloc_resizeable(size, max_size, resized, - mr, errp); + mr, &err); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + if (err) { + mr->size = int128_zero(); + object_unparent(OBJECT(mr)); + error_propagate(errp, err); + } } -#ifdef __linux__ +#ifdef CONFIG_POSIX void memory_region_init_ram_from_file(MemoryRegion *mr, struct Object *owner, const char *name, uint64_t size, uint64_t align, - bool share, + uint32_t ram_flags, const char *path, Error **errp) { + Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; mr->align = align; - mr->ram_block = qemu_ram_alloc_from_file(size, mr, share, path, errp); + mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path, &err); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + if (err) { + mr->size = int128_zero(); + object_unparent(OBJECT(mr)); + error_propagate(errp, err); + } } void memory_region_init_ram_from_fd(MemoryRegion *mr, @@ -1572,12 +1609,20 @@ void memory_region_init_ram_from_fd(MemoryRegion *mr, int fd, Error **errp) { + Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc_from_fd(size, mr, share, fd, errp); + mr->ram_block = qemu_ram_alloc_from_fd(size, mr, + share ? RAM_SHARED : 0, + fd, &err); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + if (err) { + mr->size = int128_zero(); + object_unparent(OBJECT(mr)); + error_propagate(errp, err); + } } #endif @@ -1604,10 +1649,17 @@ void memory_region_init_ram_device_ptr(MemoryRegion *mr, uint64_t size, void *ptr) { - memory_region_init_ram_ptr(mr, owner, name, size, ptr); + memory_region_init(mr, owner, name, size); + mr->ram = true; + mr->terminates = true; mr->ram_device = true; mr->ops = &ram_device_mem_ops; mr->opaque = mr; + 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_block = qemu_ram_alloc_from_ptr(size, ptr, mr, &error_fatal); } void memory_region_init_alias(MemoryRegion *mr, @@ -1628,13 +1680,19 @@ void memory_region_init_rom_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp) { + Error *err = NULL; memory_region_init(mr, owner, name, size); mr->ram = true; mr->readonly = true; mr->terminates = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, false, mr, errp); + mr->ram_block = qemu_ram_alloc(size, false, mr, &err); mr->dirty_log_mask = tcg_enabled() ? (1 << DIRTY_MEMORY_CODE) : 0; + if (err) { + mr->size = int128_zero(); + object_unparent(OBJECT(mr)); + error_propagate(errp, err); + } } void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, @@ -1645,6 +1703,7 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, uint64_t size, Error **errp) { + Error *err = NULL; assert(ops); memory_region_init(mr, owner, name, size); mr->ops = ops; @@ -1652,7 +1711,12 @@ void memory_region_init_rom_device_nomigrate(MemoryRegion *mr, mr->terminates = true; mr->rom_device = true; mr->destructor = memory_region_destructor_ram; - mr->ram_block = qemu_ram_alloc(size, false, mr, errp); + mr->ram_block = qemu_ram_alloc(size, false, mr, &err); + if (err) { + mr->size = int128_zero(); + object_unparent(OBJECT(mr)); + error_propagate(errp, err); + } } void memory_region_init_iommu(void *_iommu_mr, @@ -2083,6 +2147,16 @@ void memory_region_set_readonly(MemoryRegion *mr, bool readonly) } } +void memory_region_set_nonvolatile(MemoryRegion *mr, bool nonvolatile) +{ + if (mr->nonvolatile != nonvolatile) { + memory_region_transaction_begin(); + mr->nonvolatile = nonvolatile; + memory_region_update_pending |= mr->enabled; + memory_region_transaction_commit(); + } +} + void memory_region_rom_device_set_romd(MemoryRegion *mr, bool romd_mode) { if (mr->romd_mode != romd_mode) { @@ -2169,34 +2243,12 @@ static void memory_region_update_coalesced_range_as(MemoryRegion *mr, AddressSpa { FlatView *view; FlatRange *fr; - CoalescedMemoryRange *cmr; - AddrRange tmp; - MemoryRegionSection section; view = address_space_get_flatview(as); FOR_EACH_FLAT_RANGE(fr, view) { if (fr->mr == mr) { - section = (MemoryRegionSection) { - .fv = view, - .offset_within_address_space = int128_get64(fr->addr.start), - .size = fr->addr.size, - }; - - MEMORY_LISTENER_CALL(as, coalesced_mmio_del, Reverse, §ion, - int128_get64(fr->addr.start), - int128_get64(fr->addr.size)); - QTAILQ_FOREACH(cmr, &mr->coalesced, link) { - tmp = addrrange_shift(cmr->addr, - int128_sub(fr->addr.start, - int128_make64(fr->offset_in_region))); - if (!addrrange_intersects(tmp, fr->addr)) { - continue; - } - tmp = addrrange_intersection(tmp, fr->addr); - MEMORY_LISTENER_CALL(as, coalesced_mmio_add, Forward, §ion, - int128_get64(tmp.start), - int128_get64(tmp.size)); - } + flat_range_coalesced_io_del(fr, as); + flat_range_coalesced_io_add(fr, as); } } flatview_unref(view); @@ -2542,6 +2594,7 @@ static MemoryRegionSection memory_region_find_rcu(MemoryRegion *mr, ret.size = range.size; ret.offset_within_address_space = int128_get64(range.start); ret.readonly = fr->readonly; + ret.nonvolatile = fr->nonvolatile; return ret; } @@ -2695,8 +2748,7 @@ void memory_listener_register(MemoryListener *listener, AddressSpace *as) listener->address_space = as; if (QTAILQ_EMPTY(&memory_listeners) - || listener->priority >= QTAILQ_LAST(&memory_listeners, - memory_listeners)->priority) { + || listener->priority >= QTAILQ_LAST(&memory_listeners)->priority) { QTAILQ_INSERT_TAIL(&memory_listeners, listener, link); } else { QTAILQ_FOREACH(other, &memory_listeners, link) { @@ -2708,8 +2760,7 @@ void memory_listener_register(MemoryListener *listener, AddressSpace *as) } if (QTAILQ_EMPTY(&as->listeners) - || listener->priority >= QTAILQ_LAST(&as->listeners, - memory_listeners)->priority) { + || listener->priority >= QTAILQ_LAST(&as->listeners)->priority) { QTAILQ_INSERT_TAIL(&as->listeners, listener, link_as); } else { QTAILQ_FOREACH(other, &as->listeners, link_as) { @@ -2735,115 +2786,6 @@ void memory_listener_unregister(MemoryListener *listener) listener->address_space = NULL; } -bool memory_region_request_mmio_ptr(MemoryRegion *mr, hwaddr addr) -{ - void *host; - unsigned size = 0; - unsigned offset = 0; - Object *new_interface; - - if (!mr || !mr->ops->request_ptr) { - return false; - } - - /* - * Avoid an update if the request_ptr call - * memory_region_invalidate_mmio_ptr which seems to be likely when we use - * a cache. - */ - memory_region_transaction_begin(); - - host = mr->ops->request_ptr(mr->opaque, addr - mr->addr, &size, &offset); - - if (!host || !size) { - memory_region_transaction_commit(); - return false; - } - - new_interface = object_new("mmio_interface"); - qdev_prop_set_uint64(DEVICE(new_interface), "start", offset); - qdev_prop_set_uint64(DEVICE(new_interface), "end", offset + size - 1); - qdev_prop_set_bit(DEVICE(new_interface), "ro", true); - qdev_prop_set_ptr(DEVICE(new_interface), "host_ptr", host); - qdev_prop_set_ptr(DEVICE(new_interface), "subregion", mr); - object_property_set_bool(OBJECT(new_interface), true, "realized", NULL); - - memory_region_transaction_commit(); - return true; -} - -typedef struct MMIOPtrInvalidate { - MemoryRegion *mr; - hwaddr offset; - unsigned size; - int busy; - int allocated; -} MMIOPtrInvalidate; - -#define MAX_MMIO_INVALIDATE 10 -static MMIOPtrInvalidate mmio_ptr_invalidate_list[MAX_MMIO_INVALIDATE]; - -static void memory_region_do_invalidate_mmio_ptr(CPUState *cpu, - run_on_cpu_data data) -{ - MMIOPtrInvalidate *invalidate_data = (MMIOPtrInvalidate *)data.host_ptr; - MemoryRegion *mr = invalidate_data->mr; - hwaddr offset = invalidate_data->offset; - unsigned size = invalidate_data->size; - MemoryRegionSection section = memory_region_find(mr, offset, size); - - qemu_mutex_lock_iothread(); - - /* Reset dirty so this doesn't happen later. */ - cpu_physical_memory_test_and_clear_dirty(offset, size, 1); - - if (section.mr != mr) { - /* memory_region_find add a ref on section.mr */ - memory_region_unref(section.mr); - if (MMIO_INTERFACE(section.mr->owner)) { - /* We found the interface just drop it. */ - object_property_set_bool(section.mr->owner, false, "realized", - NULL); - object_unref(section.mr->owner); - object_unparent(section.mr->owner); - } - } - - qemu_mutex_unlock_iothread(); - - if (invalidate_data->allocated) { - g_free(invalidate_data); - } else { - invalidate_data->busy = 0; - } -} - -void memory_region_invalidate_mmio_ptr(MemoryRegion *mr, hwaddr offset, - unsigned size) -{ - size_t i; - MMIOPtrInvalidate *invalidate_data = NULL; - - for (i = 0; i < MAX_MMIO_INVALIDATE; i++) { - if (atomic_cmpxchg(&(mmio_ptr_invalidate_list[i].busy), 0, 1) == 0) { - invalidate_data = &mmio_ptr_invalidate_list[i]; - break; - } - } - - if (!invalidate_data) { - invalidate_data = g_malloc0(sizeof(MMIOPtrInvalidate)); - invalidate_data->allocated = 1; - } - - invalidate_data->mr = mr; - invalidate_data->offset = offset; - invalidate_data->size = size; - - async_safe_run_on_cpu(first_cpu, memory_region_do_invalidate_mmio_ptr, - RUN_ON_CPU_HOST_PTR(invalidate_data)); -} - void address_space_init(AddressSpace *as, MemoryRegion *root, const char *name) { memory_region_ref(root); @@ -2908,7 +2850,7 @@ struct MemoryRegionList { QTAILQ_ENTRY(MemoryRegionList) mrqueue; }; -typedef QTAILQ_HEAD(mrqueue, MemoryRegionList) MemoryRegionListHead; +typedef QTAILQ_HEAD(, MemoryRegionList) MemoryRegionListHead; #define MR_SIZE(size) (int128_nz(size) ? (hwaddr)int128_get64( \ int128_sub((size), int128_one())) : 0) @@ -3001,10 +2943,11 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, QTAILQ_INSERT_TAIL(alias_print_queue, ml, mrqueue); } mon_printf(f, TARGET_FMT_plx "-" TARGET_FMT_plx - " (prio %d, %s): alias %s @%s " TARGET_FMT_plx + " (prio %d, %s%s): alias %s @%s " TARGET_FMT_plx "-" TARGET_FMT_plx "%s", cur_start, cur_end, mr->priority, + mr->nonvolatile ? "nv-" : "", memory_region_type((MemoryRegion *)mr), memory_region_name(mr), memory_region_name(mr->alias), @@ -3016,9 +2959,10 @@ static void mtree_print_mr(fprintf_function mon_printf, void *f, } } else { mon_printf(f, - TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s): %s%s", + TARGET_FMT_plx "-" TARGET_FMT_plx " (prio %d, %s%s): %s%s", cur_start, cur_end, mr->priority, + mr->nonvolatile ? "nv-" : "", memory_region_type((MemoryRegion *)mr), memory_region_name(mr), mr->enabled ? "" : " [disabled]"); @@ -3103,19 +3047,21 @@ static void mtree_print_flatview(gpointer key, gpointer value, mr = range->mr; if (range->offset_in_region) { p(f, MTREE_INDENT TARGET_FMT_plx "-" - TARGET_FMT_plx " (prio %d, %s): %s @" TARGET_FMT_plx, + TARGET_FMT_plx " (prio %d, %s%s): %s @" TARGET_FMT_plx, int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), mr->priority, + range->nonvolatile ? "nv-" : "", range->readonly ? "rom" : memory_region_type(mr), memory_region_name(mr), range->offset_in_region); } else { p(f, MTREE_INDENT TARGET_FMT_plx "-" - TARGET_FMT_plx " (prio %d, %s): %s", + TARGET_FMT_plx " (prio %d, %s%s): %s", int128_get64(range->addr.start), int128_get64(range->addr.start) + MR_SIZE(range->addr.size), mr->priority, + range->nonvolatile ? "nv-" : "", range->readonly ? "rom" : memory_region_type(mr), memory_region_name(mr)); } diff --git a/memory_mapping.c b/memory_mapping.c index 775466f3a8..e3ec70624f 100644 --- a/memory_mapping.c +++ b/memory_mapping.c @@ -206,7 +206,8 @@ static void guest_phys_blocks_region_add(MemoryListener *listener, /* we only care about RAM */ if (!memory_region_is_ram(section->mr) || - memory_region_is_ram_device(section->mr)) { + memory_region_is_ram_device(section->mr) || + memory_region_is_nonvolatile(section->mr)) { return; } @@ -222,7 +223,7 @@ static void guest_phys_blocks_region_add(MemoryListener *listener, if (!QTAILQ_EMPTY(&g->list->head)) { hwaddr predecessor_size; - predecessor = QTAILQ_LAST(&g->list->head, GuestPhysBlockHead); + predecessor = QTAILQ_LAST(&g->list->head); predecessor_size = predecessor->target_end - predecessor->target_start; /* the memory API guarantees monotonically increasing traversal */ diff --git a/migration/Makefile.objs b/migration/Makefile.objs index c83ec47ba8..a4f3bafd86 100644 --- a/migration/Makefile.objs +++ b/migration/Makefile.objs @@ -1,6 +1,6 @@ common-obj-y += migration.o socket.o fd.o exec.o common-obj-y += tls.o channel.o savevm.o -common-obj-y += colo-comm.o colo.o colo-failover.o +common-obj-y += colo.o colo-failover.o common-obj-y += vmstate.o vmstate-types.o page_cache.o common-obj-y += qemu-file.o global_state.o common-obj-y += qemu-file-channel.o diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 477826330c..d1bb863cb6 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -116,7 +116,7 @@ typedef struct DirtyBitmapMigBitmapState { } DirtyBitmapMigBitmapState; typedef struct DirtyBitmapMigState { - QSIMPLEQ_HEAD(dbms_list, DirtyBitmapMigBitmapState) dbms_list; + QSIMPLEQ_HEAD(, DirtyBitmapMigBitmapState) dbms_list; bool bulk_completed; bool no_bitmaps; @@ -261,7 +261,7 @@ static void dirty_bitmap_mig_cleanup(void) while ((dbms = QSIMPLEQ_FIRST(&dirty_bitmap_mig_state.dbms_list)) != NULL) { QSIMPLEQ_REMOVE_HEAD(&dirty_bitmap_mig_state.dbms_list, entry); - bdrv_dirty_bitmap_set_qmp_locked(dbms->bitmap, false); + bdrv_dirty_bitmap_set_busy(dbms->bitmap, false); bdrv_unref(dbms->bs); g_free(dbms); } @@ -274,6 +274,7 @@ static int init_dirty_bitmap_migration(void) BdrvDirtyBitmap *bitmap; DirtyBitmapMigBitmapState *dbms; BdrvNextIterator it; + Error *local_err = NULL; dirty_bitmap_mig_state.bulk_completed = false; dirty_bitmap_mig_state.prev_bs = NULL; @@ -301,20 +302,14 @@ static int init_dirty_bitmap_migration(void) goto fail; } - if (bdrv_dirty_bitmap_frozen(bitmap)) { - error_report("Can't migrate frozen dirty bitmap: '%s", - bdrv_dirty_bitmap_name(bitmap)); - goto fail; - } - - if (bdrv_dirty_bitmap_qmp_locked(bitmap)) { - error_report("Can't migrate locked dirty bitmap: '%s", - bdrv_dirty_bitmap_name(bitmap)); + if (bdrv_dirty_bitmap_check(bitmap, BDRV_BITMAP_DEFAULT, + &local_err)) { + error_report_err(local_err); goto fail; } bdrv_ref(bs); - bdrv_dirty_bitmap_set_qmp_locked(bitmap, true); + bdrv_dirty_bitmap_set_busy(bitmap, true); dbms = g_new0(DirtyBitmapMigBitmapState, 1); dbms->bs = bs; @@ -326,7 +321,7 @@ static int init_dirty_bitmap_migration(void) if (bdrv_dirty_bitmap_enabled(bitmap)) { dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_ENABLED; } - if (bdrv_dirty_bitmap_get_persistance(bitmap)) { + if (bdrv_dirty_bitmap_get_persistence(bitmap)) { dbms->flags |= DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT; } @@ -335,9 +330,9 @@ static int init_dirty_bitmap_migration(void) } } - /* unset persistance here, to not roll back it */ + /* unset migration flags here, to not roll back it */ QSIMPLEQ_FOREACH(dbms, &dirty_bitmap_mig_state.dbms_list, entry) { - bdrv_dirty_bitmap_set_persistance(dbms->bitmap, false); + bdrv_dirty_bitmap_set_migration(dbms->bitmap, true); } if (QSIMPLEQ_EMPTY(&dirty_bitmap_mig_state.dbms_list)) { @@ -478,7 +473,7 @@ static int dirty_bitmap_load_start(QEMUFile *f, DirtyBitmapLoadState *s) } if (flags & DIRTY_BITMAP_MIG_START_FLAG_PERSISTENT) { - bdrv_dirty_bitmap_set_persistance(s->bitmap, true); + bdrv_dirty_bitmap_set_persistence(s->bitmap, true); } bdrv_disable_dirty_bitmap(s->bitmap); @@ -542,7 +537,7 @@ static void dirty_bitmap_load_complete(QEMUFile *f, DirtyBitmapLoadState *s) } } - if (bdrv_dirty_bitmap_frozen(s->bitmap)) { + if (bdrv_dirty_bitmap_has_successor(s->bitmap)) { bdrv_dirty_bitmap_lock(s->bitmap); if (enabled_bitmaps == NULL) { /* in postcopy */ diff --git a/migration/block.c b/migration/block.c index 4c04d937b1..83c633fb3f 100644 --- a/migration/block.c +++ b/migration/block.c @@ -83,7 +83,6 @@ typedef struct BlkMigBlock { BlkMigDevState *bmds; int64_t sector; int nr_sectors; - struct iovec iov; QEMUIOVector qiov; BlockAIOCB *aiocb; @@ -93,12 +92,12 @@ typedef struct BlkMigBlock { } BlkMigBlock; typedef struct BlkMigState { - QSIMPLEQ_HEAD(bmds_list, BlkMigDevState) bmds_list; + QSIMPLEQ_HEAD(, BlkMigDevState) bmds_list; int64_t total_sector_sum; bool zero_blocks; /* Protected by lock. */ - QSIMPLEQ_HEAD(blk_list, BlkMigBlock) blk_list; + QSIMPLEQ_HEAD(, BlkMigBlock) blk_list; int submitted; int read_done; @@ -314,9 +313,7 @@ static int mig_save_device_bulk(QEMUFile *f, BlkMigDevState *bmds) blk->sector = cur_sector; blk->nr_sectors = nr_sectors; - blk->iov.iov_base = blk->buf; - blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); + qemu_iovec_init_buf(&blk->qiov, blk->buf, nr_sectors * BDRV_SECTOR_SIZE); blk_mig_lock(); block_mig_state.submitted++; @@ -556,9 +553,8 @@ static int mig_save_device_dirty(QEMUFile *f, BlkMigDevState *bmds, blk->nr_sectors = nr_sectors; if (is_async) { - blk->iov.iov_base = blk->buf; - blk->iov.iov_len = nr_sectors * BDRV_SECTOR_SIZE; - qemu_iovec_init_external(&blk->qiov, &blk->iov, 1); + qemu_iovec_init_buf(&blk->qiov, blk->buf, + nr_sectors * BDRV_SECTOR_SIZE); blk->aiocb = blk_aio_preadv(bmds->blk, sector * BDRV_SECTOR_SIZE, diff --git a/migration/channel.c b/migration/channel.c index 33e0e9b82f..20e4c8e2dc 100644 --- a/migration/channel.c +++ b/migration/channel.c @@ -30,6 +30,7 @@ void migration_channel_process_incoming(QIOChannel *ioc) { MigrationState *s = migrate_get_current(); + Error *local_err = NULL; trace_migration_set_incoming_channel( ioc, object_get_typename(OBJECT(ioc))); @@ -38,13 +39,13 @@ void migration_channel_process_incoming(QIOChannel *ioc) *s->parameters.tls_creds && !object_dynamic_cast(OBJECT(ioc), TYPE_QIO_CHANNEL_TLS)) { - Error *local_err = NULL; migration_tls_channel_process_incoming(s, ioc, &local_err); - if (local_err) { - error_report_err(local_err); - } } else { - migration_ioc_process_incoming(ioc); + migration_ioc_process_incoming(ioc, &local_err); + } + + if (local_err) { + error_report_err(local_err); } } diff --git a/migration/colo-comm.c b/migration/colo-comm.c deleted file mode 100644 index df26e4dfe7..0000000000 --- a/migration/colo-comm.c +++ /dev/null @@ -1,76 +0,0 @@ -/* - * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) - * (a.k.a. Fault Tolerance or Continuous Replication) - * - * Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD. - * Copyright (c) 2016 FUJITSU LIMITED - * Copyright (c) 2016 Intel Corporation - * - * 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 "migration.h" -#include "migration/colo.h" -#include "migration/vmstate.h" -#include "trace.h" - -typedef struct { - bool colo_requested; -} COLOInfo; - -static COLOInfo colo_info; - -COLOMode get_colo_mode(void) -{ - if (migration_in_colo_state()) { - return COLO_MODE_PRIMARY; - } else if (migration_incoming_in_colo_state()) { - return COLO_MODE_SECONDARY; - } else { - return COLO_MODE_UNKNOWN; - } -} - -static int colo_info_pre_save(void *opaque) -{ - COLOInfo *s = opaque; - - s->colo_requested = migrate_colo_enabled(); - - return 0; -} - -static bool colo_info_need(void *opaque) -{ - return migrate_colo_enabled(); -} - -static const VMStateDescription colo_state = { - .name = "COLOState", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = colo_info_pre_save, - .needed = colo_info_need, - .fields = (VMStateField[]) { - VMSTATE_BOOL(colo_requested, COLOInfo), - VMSTATE_END_OF_LIST() - }, -}; - -void colo_info_init(void) -{ - vmstate_register(NULL, 0, &colo_state, &colo_info); -} - -bool migration_incoming_enable_colo(void) -{ - return colo_info.colo_requested; -} - -void migration_incoming_exit_colo(void) -{ - colo_info.colo_requested = false; -} diff --git a/migration/colo-failover.c b/migration/colo-failover.c index 0ae0c41221..4854a96c92 100644 --- a/migration/colo-failover.c +++ b/migration/colo-failover.c @@ -77,7 +77,7 @@ FailoverStatus failover_get_state(void) void qmp_x_colo_lost_heartbeat(Error **errp) { - if (get_colo_mode() == COLO_MODE_UNKNOWN) { + if (get_colo_mode() == COLO_MODE_NONE) { error_setg(errp, QERR_FEATURE_DISABLED, "colo"); return; } diff --git a/migration/colo.c b/migration/colo.c index 4381067ed4..238a6d62c7 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -24,9 +24,22 @@ #include "trace.h" #include "qemu/error-report.h" #include "migration/failover.h" +#ifdef CONFIG_REPLICATION #include "replication.h" +#endif +#include "net/colo-compare.h" +#include "net/colo.h" +#include "block/block.h" +#include "qapi/qapi-events-migration.h" +#include "qapi/qmp/qerror.h" +#include "sysemu/cpus.h" +#include "net/filter.h" static bool vmstate_loading; +static Notifier packets_compare_notifier; + +/* User need to know colo mode after COLO failover */ +static COLOMode last_colo_mode; #define COLO_BUFFER_BASE_SIZE (4 * 1024 * 1024) @@ -51,8 +64,11 @@ static bool colo_runstate_is_stopped(void) static void secondary_vm_do_failover(void) { +/* COLO needs enable block-replication */ +#ifdef CONFIG_REPLICATION int old_state; MigrationIncomingState *mis = migration_incoming_get_current(); + Error *local_err = NULL; /* Can not do failover during the process of VM's loading VMstate, Or * it will break the secondary VM. @@ -70,6 +86,17 @@ static void secondary_vm_do_failover(void) migrate_set_state(&mis->state, MIGRATION_STATUS_COLO, MIGRATION_STATUS_COMPLETED); + replication_stop_all(true, &local_err); + if (local_err) { + error_report_err(local_err); + } + + /* Notify all filters of all NIC to do checkpoint */ + colo_notify_filters_event(COLO_EVENT_FAILOVER, &local_err); + if (local_err) { + error_report_err(local_err); + } + if (!autostart) { error_report("\"-S\" qemu option will be ignored in secondary side"); /* recover runstate to normal migration finish state */ @@ -97,19 +124,30 @@ static void secondary_vm_do_failover(void) } /* Notify COLO incoming thread that failover work is finished */ qemu_sem_post(&mis->colo_incoming_sem); + /* For Secondary VM, jump to incoming co */ if (mis->migration_incoming_co) { qemu_coroutine_enter(mis->migration_incoming_co); } +#else + abort(); +#endif } static void primary_vm_do_failover(void) { +#ifdef CONFIG_REPLICATION MigrationState *s = migrate_get_current(); int old_state; + Error *local_err = NULL; migrate_set_state(&s->state, MIGRATION_STATUS_COLO, MIGRATION_STATUS_COMPLETED); + /* + * kick COLO thread which might wait at + * qemu_sem_wait(&s->colo_checkpoint_sem). + */ + colo_checkpoint_notify(migrate_get_current()); /* * Wake up COLO thread which may blocked in recv() or send(), @@ -130,8 +168,29 @@ static void primary_vm_do_failover(void) FailoverStatus_str(old_state)); return; } + + replication_stop_all(true, &local_err); + if (local_err) { + error_report_err(local_err); + local_err = NULL; + } + /* Notify COLO thread that failover work is finished */ qemu_sem_post(&s->colo_exit_sem); +#else + abort(); +#endif +} + +COLOMode get_colo_mode(void) +{ + if (migration_in_colo_state()) { + return COLO_MODE_PRIMARY; + } else if (migration_incoming_in_colo_state()) { + return COLO_MODE_SECONDARY; + } else { + return COLO_MODE_NONE; + } } void colo_do_failover(MigrationState *s) @@ -141,18 +200,24 @@ void colo_do_failover(MigrationState *s) vm_stop_force_state(RUN_STATE_COLO); } - if (get_colo_mode() == COLO_MODE_PRIMARY) { + switch (get_colo_mode()) { + case COLO_MODE_PRIMARY: primary_vm_do_failover(); - } else { + break; + case COLO_MODE_SECONDARY: secondary_vm_do_failover(); + break; + default: + error_report("colo_do_failover failed because the colo mode" + " could not be obtained"); } } +#ifdef CONFIG_REPLICATION void qmp_xen_set_replication(bool enable, bool primary, bool has_failover, bool failover, Error **errp) { -#ifdef CONFIG_REPLICATION ReplicationMode mode = primary ? REPLICATION_MODE_PRIMARY : REPLICATION_MODE_SECONDARY; @@ -171,14 +236,10 @@ void qmp_xen_set_replication(bool enable, bool primary, } replication_stop_all(failover, failover ? NULL : errp); } -#else - abort(); -#endif } ReplicationStatus *qmp_query_xen_replication_status(Error **errp) { -#ifdef CONFIG_REPLICATION Error *err = NULL; ReplicationStatus *s = g_new0(ReplicationStatus, 1); @@ -193,18 +254,37 @@ ReplicationStatus *qmp_query_xen_replication_status(Error **errp) error_free(err); return s; -#else - abort(); -#endif } void qmp_xen_colo_do_checkpoint(Error **errp) { -#ifdef CONFIG_REPLICATION replication_do_checkpoint_all(errp); -#else - abort(); +} #endif + +COLOStatus *qmp_query_colo_status(Error **errp) +{ + COLOStatus *s = g_new0(COLOStatus, 1); + + s->mode = get_colo_mode(); + s->last_mode = last_colo_mode; + + switch (failover_get_state()) { + case FAILOVER_STATUS_NONE: + s->reason = COLO_EXIT_REASON_NONE; + break; + case FAILOVER_STATUS_COMPLETED: + s->reason = COLO_EXIT_REASON_REQUEST; + break; + default: + if (migration_in_colo_state()) { + s->reason = COLO_EXIT_REASON_PROCESSING; + } else { + s->reason = COLO_EXIT_REASON_ERROR; + } + } + + return s; } static void colo_send_message(QEMUFile *f, COLOMessage msg, @@ -343,20 +423,46 @@ static int colo_do_checkpoint_transaction(MigrationState *s, goto out; } - /* Disable block migration */ - migrate_set_block_enabled(false, &local_err); - qemu_savevm_state_header(fb); - qemu_savevm_state_setup(fb); - qemu_mutex_lock_iothread(); - qemu_savevm_state_complete_precopy(fb, false, false); - qemu_mutex_unlock_iothread(); - - qemu_fflush(fb); - - colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err); + colo_notify_compares_event(NULL, COLO_EVENT_CHECKPOINT, &local_err); if (local_err) { goto out; } + + /* Disable block migration */ + migrate_set_block_enabled(false, &local_err); + qemu_mutex_lock_iothread(); + +#ifdef CONFIG_REPLICATION + replication_do_checkpoint_all(&local_err); + if (local_err) { + qemu_mutex_unlock_iothread(); + goto out; + } +#else + abort(); +#endif + + colo_send_message(s->to_dst_file, COLO_MESSAGE_VMSTATE_SEND, &local_err); + if (local_err) { + qemu_mutex_unlock_iothread(); + goto out; + } + /* Note: device state is saved into buffer */ + ret = qemu_save_device_state(fb); + + qemu_mutex_unlock_iothread(); + if (ret < 0) { + goto out; + } + /* + * Only save VM's live state, which not including device state. + * TODO: We may need a timeout mechanism to prevent COLO process + * to be blocked here. + */ + qemu_savevm_live_state(s->to_dst_file); + + qemu_fflush(fb); + /* * We need the size of the VMstate data in Secondary side, * With which we can decide how much data should be read. @@ -400,6 +506,11 @@ out: return ret; } +static void colo_compare_notify_checkpoint(Notifier *notifier, void *data) +{ + colo_checkpoint_notify(data); +} + static void colo_process_checkpoint(MigrationState *s) { QIOChannelBuffer *bioc; @@ -408,6 +519,12 @@ static void colo_process_checkpoint(MigrationState *s) Error *local_err = NULL; int ret; + last_colo_mode = get_colo_mode(); + if (last_colo_mode != COLO_MODE_PRIMARY) { + error_report("COLO mode must be COLO_MODE_PRIMARY"); + return; + } + failover_init_state(); s->rp_state.from_dst_file = qemu_file_get_return_path(s->to_dst_file); @@ -416,6 +533,9 @@ static void colo_process_checkpoint(MigrationState *s) goto out; } + packets_compare_notifier.notify = colo_compare_notify_checkpoint; + colo_compare_register_notifier(&packets_compare_notifier); + /* * Wait for Secondary finish loading VM states and enter COLO * restore. @@ -430,6 +550,16 @@ static void colo_process_checkpoint(MigrationState *s) object_unref(OBJECT(bioc)); qemu_mutex_lock_iothread(); +#ifdef CONFIG_REPLICATION + replication_start_all(REPLICATION_MODE_PRIMARY, &local_err); + if (local_err) { + qemu_mutex_unlock_iothread(); + goto out; + } +#else + abort(); +#endif + vm_start(); qemu_mutex_unlock_iothread(); trace_colo_vm_state_change("stop", "run"); @@ -445,6 +575,9 @@ static void colo_process_checkpoint(MigrationState *s) qemu_sem_wait(&s->colo_checkpoint_sem); + if (s->state != MIGRATION_STATUS_COLO) { + goto out; + } ret = colo_do_checkpoint_transaction(s, bioc, fb); if (ret < 0) { goto out; @@ -461,11 +594,35 @@ out: qemu_fclose(fb); } - timer_del(s->colo_delay_timer); + /* + * There are only two reasons we can get here, some error happened + * or the user triggered failover. + */ + switch (failover_get_state()) { + case FAILOVER_STATUS_COMPLETED: + qapi_event_send_colo_exit(COLO_MODE_PRIMARY, + COLO_EXIT_REASON_REQUEST); + break; + default: + qapi_event_send_colo_exit(COLO_MODE_PRIMARY, + COLO_EXIT_REASON_ERROR); + } /* Hope this not to be too long to wait here */ qemu_sem_wait(&s->colo_exit_sem); qemu_sem_destroy(&s->colo_exit_sem); + + /* + * It is safe to unregister notifier after failover finished. + * Besides, colo_delay_timer and colo_checkpoint_sem can't be + * released befor unregister notifier, or there will be use-after-free + * error. + */ + colo_compare_unregister_notifier(&packets_compare_notifier); + timer_del(s->colo_delay_timer); + timer_free(s->colo_delay_timer); + qemu_sem_destroy(&s->colo_checkpoint_sem); + /* * Must be called after failover BH is completed, * Or the failover BH may shutdown the wrong fd that @@ -533,12 +690,20 @@ void *colo_process_incoming_thread(void *opaque) uint64_t total_size; uint64_t value; Error *local_err = NULL; + int ret; + rcu_register_thread(); qemu_sem_init(&mis->colo_incoming_sem, 0); migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COLO); + last_colo_mode = get_colo_mode(); + if (last_colo_mode != COLO_MODE_SECONDARY) { + error_report("COLO mode must be COLO_MODE_SECONDARY"); + return NULL; + } + failover_init_state(); mis->to_src_file = qemu_file_get_return_path(mis->from_src_file); @@ -558,6 +723,20 @@ void *colo_process_incoming_thread(void *opaque) fb = qemu_fopen_channel_input(QIO_CHANNEL(bioc)); object_unref(OBJECT(bioc)); + qemu_mutex_lock_iothread(); +#ifdef CONFIG_REPLICATION + replication_start_all(REPLICATION_MODE_SECONDARY, &local_err); + if (local_err) { + qemu_mutex_unlock_iothread(); + goto out; + } +#else + abort(); +#endif + vm_start(); + trace_colo_vm_state_change("stop", "run"); + qemu_mutex_unlock_iothread(); + colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_READY, &local_err); if (local_err) { @@ -577,6 +756,11 @@ void *colo_process_incoming_thread(void *opaque) goto out; } + qemu_mutex_lock_iothread(); + vm_stop_force_state(RUN_STATE_COLO); + trace_colo_vm_state_change("run", "stop"); + qemu_mutex_unlock_iothread(); + /* FIXME: This is unnecessary for periodic checkpoint mode */ colo_send_message(mis->to_src_file, COLO_MESSAGE_CHECKPOINT_REPLY, &local_err); @@ -590,6 +774,16 @@ void *colo_process_incoming_thread(void *opaque) goto out; } + qemu_mutex_lock_iothread(); + cpu_synchronize_all_pre_loadvm(); + ret = qemu_loadvm_state_main(mis->from_src_file, mis); + qemu_mutex_unlock_iothread(); + + if (ret < 0) { + error_report("Load VM's live state (ram) error"); + goto out; + } + value = colo_receive_message_value(mis->from_src_file, COLO_MESSAGE_VMSTATE_SIZE, &local_err); if (local_err) { @@ -621,15 +815,41 @@ void *colo_process_incoming_thread(void *opaque) } qemu_mutex_lock_iothread(); - qemu_system_reset(SHUTDOWN_CAUSE_NONE); vmstate_loading = true; - if (qemu_loadvm_state(fb) < 0) { - error_report("COLO: loadvm failed"); + ret = qemu_load_device_state(fb); + if (ret < 0) { + error_report("COLO: load device state failed"); + qemu_mutex_unlock_iothread(); + goto out; + } + +#ifdef CONFIG_REPLICATION + replication_get_error_all(&local_err); + if (local_err) { + qemu_mutex_unlock_iothread(); + goto out; + } + + /* discard colo disk buffer */ + replication_do_checkpoint_all(&local_err); + if (local_err) { + qemu_mutex_unlock_iothread(); + goto out; + } +#else + abort(); +#endif + /* Notify all filters of all NIC to do checkpoint */ + colo_notify_filters_event(COLO_EVENT_CHECKPOINT, &local_err); + + if (local_err) { qemu_mutex_unlock_iothread(); goto out; } vmstate_loading = false; + vm_start(); + trace_colo_vm_state_change("stop", "run"); qemu_mutex_unlock_iothread(); if (failover_get_state() == FAILOVER_STATUS_RELAUNCH) { @@ -653,6 +873,20 @@ out: error_report_err(local_err); } + /* + * There are only two reasons we can get here, some error happened + * or the user triggered failover. + */ + switch (failover_get_state()) { + case FAILOVER_STATUS_COMPLETED: + qapi_event_send_colo_exit(COLO_MODE_SECONDARY, + COLO_EXIT_REASON_REQUEST); + break; + default: + qapi_event_send_colo_exit(COLO_MODE_SECONDARY, + COLO_EXIT_REASON_ERROR); + } + if (fb) { qemu_fclose(fb); } @@ -663,8 +897,9 @@ out: /* Must be called after failover BH is completed */ if (mis->to_src_file) { qemu_fclose(mis->to_src_file); + mis->to_src_file = NULL; } - migration_incoming_exit_colo(); + rcu_unregister_thread(); return NULL; } diff --git a/migration/global_state.c b/migration/global_state.c index 8e8ab5c51e..2c8c447239 100644 --- a/migration/global_state.c +++ b/migration/global_state.c @@ -42,6 +42,7 @@ int global_state_store(void) void global_state_store_running(void) { const char *state = RunState_str(RUN_STATE_RUNNING); + assert(strlen(state) < sizeof(global_state.runstate)); strncpy((char *)global_state.runstate, state, sizeof(global_state.runstate)); } @@ -88,6 +89,17 @@ static int global_state_post_load(void *opaque, int version_id) s->received = true; trace_migrate_global_state_post_load(runstate); + if (strnlen((char *)s->runstate, + sizeof(s->runstate)) == sizeof(s->runstate)) { + /* + * This condition should never happen during migration, because + * all runstate names are shorter than 100 bytes (the size of + * s->runstate). However, a malicious stream could overflow + * the qapi_enum_parse() call, so we force the last character + * to a NUL byte. + */ + s->runstate[sizeof(s->runstate) - 1] = '\0'; + } r = qapi_enum_parse(&RunState_lookup, runstate, -1, &local_err); if (r == -1) { @@ -106,7 +118,8 @@ static int global_state_pre_save(void *opaque) GlobalState *s = opaque; trace_migrate_global_state_pre_save((char *)s->runstate); - s->size = strlen((char *)s->runstate) + 1; + s->size = strnlen((char *)s->runstate, sizeof(s->runstate)) + 1; + assert(s->size <= sizeof(s->runstate)); return 0; } diff --git a/migration/migration.c b/migration/migration.c index b7d9854bda..609e0df5d0 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -31,6 +31,8 @@ #include "migration/vmstate.h" #include "block/block.h" #include "qapi/error.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-sockets.h" #include "qapi/qapi-commands-migration.h" #include "qapi/qapi-events-migration.h" #include "qapi/qmp/qerror.h" @@ -45,6 +47,7 @@ #include "migration/colo.h" #include "hw/boards.h" #include "monitor/monitor.h" +#include "net/announce.h" #define MAX_THROTTLE (32 << 20) /* Migration transfer speed throttling */ @@ -71,22 +74,29 @@ /* Define default autoconverge cpu throttle migration parameters */ #define DEFAULT_MIGRATE_CPU_THROTTLE_INITIAL 20 #define DEFAULT_MIGRATE_CPU_THROTTLE_INCREMENT 10 +#define DEFAULT_MIGRATE_MAX_CPU_THROTTLE 99 /* Migration XBZRLE default cache size */ #define DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE (64 * 1024 * 1024) -/* The delay time (in ms) between two COLO checkpoints - * Note: Please change this default value to 10000 when we support hybrid mode. - */ -#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY 200 +/* The delay time (in ms) between two COLO checkpoints */ +#define DEFAULT_MIGRATE_X_CHECKPOINT_DELAY (200 * 100) #define DEFAULT_MIGRATE_MULTIFD_CHANNELS 2 -#define DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT 16 /* Background transfer rate for postcopy, 0 means unlimited, note * that page requests can still exceed this limit. */ #define DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH 0 +/* + * Parameters for self_announce_delay giving a stream of RARP/ARP + * packets after migration. + */ +#define DEFAULT_MIGRATE_ANNOUNCE_INITIAL 50 +#define DEFAULT_MIGRATE_ANNOUNCE_MAX 550 +#define DEFAULT_MIGRATE_ANNOUNCE_ROUNDS 5 +#define DEFAULT_MIGRATE_ANNOUNCE_STEP 100 + static NotifierList migration_state_notifiers = NOTIFIER_LIST_INITIALIZER(migration_state_notifiers); @@ -117,6 +127,7 @@ static bool migration_object_check(MigrationState *ms, Error **errp); static int migration_maybe_pause(MigrationState *s, int *current_active_state, int new_state); +static void migrate_fd_cancel(MigrationState *s); void migration_object_init(void) { @@ -158,8 +169,13 @@ void migration_object_init(void) } } -void migration_object_finalize(void) +void migration_shutdown(void) { + /* + * Cancel the current migration - that will (eventually) + * stop the migration using this structure + */ + migrate_fd_cancel(current_migration); object_unref(OBJECT(current_migration)); } @@ -198,12 +214,17 @@ void migration_incoming_state_destroy(void) } qemu_event_reset(&mis->main_thread_load_event); + + if (mis->socket_address_list) { + qapi_free_SocketAddressList(mis->socket_address_list); + mis->socket_address_list = NULL; + } } static void migrate_generate_event(int new_state) { if (migrate_use_events()) { - qapi_event_send_migration(new_state, &error_abort); + qapi_event_send_migration(new_state); } } @@ -297,11 +318,38 @@ int migrate_send_rp_req_pages(MigrationIncomingState *mis, const char *rbname, return migrate_send_rp_message(mis, msg_type, msglen, bufc); } +static bool migration_colo_enabled; +bool migration_incoming_colo_enabled(void) +{ + return migration_colo_enabled; +} + +void migration_incoming_disable_colo(void) +{ + migration_colo_enabled = false; +} + +void migration_incoming_enable_colo(void) +{ + migration_colo_enabled = true; +} + +void migrate_add_address(SocketAddress *address) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + SocketAddressList *addrs; + + addrs = g_new0(SocketAddressList, 1); + addrs->next = mis->socket_address_list; + mis->socket_address_list = addrs; + addrs->value = QAPI_CLONE(SocketAddress, address); +} + void qemu_start_incoming_migration(const char *uri, Error **errp) { const char *p; - qapi_event_send_migration(MIGRATION_STATUS_SETUP, &error_abort); + qapi_event_send_migration(MIGRATION_STATUS_SETUP); if (!strcmp(uri, "defer")) { deferred_incoming_migration(errp); } else if (strstart(uri, "tcp:", &p)) { @@ -349,7 +397,7 @@ static void process_incoming_migration_bh(void *opaque) * 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(); + qemu_announce_self(&mis->announce_timer, migrate_announce_params()); if (multifd_load_cleanup(&local_err) != 0) { error_report_err(local_err); @@ -368,6 +416,9 @@ static void process_incoming_migration_bh(void *opaque) } else { runstate_set(RUN_STATE_PAUSED); } + } else if (migration_incoming_colo_enabled()) { + migration_incoming_disable_colo(); + vm_start(); } else { runstate_set(global_state_get_runstate()); } @@ -387,8 +438,10 @@ static void process_incoming_migration_co(void *opaque) MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyState ps; int ret; + Error *local_err = NULL; assert(mis->from_src_file); + mis->migration_incoming_co = qemu_coroutine_self(); mis->largest_page_size = qemu_ram_pagesize_largest(); postcopy_state_set(POSTCOPY_INCOMING_NONE); migrate_set_state(&mis->state, MIGRATION_STATUS_NONE, @@ -417,8 +470,19 @@ static void process_incoming_migration_co(void *opaque) } /* we get COLO info, and know if we are in COLO mode */ - if (!ret && migration_incoming_enable_colo()) { - mis->migration_incoming_co = qemu_coroutine_self(); + if (!ret && migration_incoming_colo_enabled()) { + /* Make sure all file formats flush their mutable metadata */ + bdrv_invalidate_cache_all(&local_err); + if (local_err) { + error_report_err(local_err); + goto fail; + } + + if (colo_init_ram_cache() < 0) { + error_report("Init ram cache failed"); + goto fail; + } + qemu_thread_create(&mis->colo_incoming_thread, "COLO incoming", colo_process_incoming_thread, mis, QEMU_THREAD_JOINABLE); mis->have_colo_incoming_thread = true; @@ -426,22 +490,27 @@ static void process_incoming_migration_co(void *opaque) /* Wait checkpoint incoming thread exit before free resource */ qemu_thread_join(&mis->colo_incoming_thread); + /* We hold the global iothread lock, so it is safe here */ + colo_release_ram_cache(); } if (ret < 0) { - Error *local_err = NULL; - - migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, - MIGRATION_STATUS_FAILED); error_report("load of migration failed: %s", strerror(-ret)); - qemu_fclose(mis->from_src_file); - if (multifd_load_cleanup(&local_err) != 0) { - error_report_err(local_err); - } - exit(EXIT_FAILURE); + goto fail; } mis->bh = qemu_bh_new(process_incoming_migration_bh, mis); qemu_bh_schedule(mis->bh); + mis->migration_incoming_co = NULL; + return; +fail: + local_err = NULL; + migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, + MIGRATION_STATUS_FAILED); + qemu_fclose(mis->from_src_file); + if (multifd_load_cleanup(&local_err) != 0) { + error_report_err(local_err); + } + exit(EXIT_FAILURE); } static void migration_incoming_setup(QEMUFile *f) @@ -508,7 +577,7 @@ void migration_fd_process_incoming(QEMUFile *f) migration_incoming_process(); } -void migration_ioc_process_incoming(QIOChannel *ioc) +void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) { MigrationIncomingState *mis = migration_incoming_get_current(); bool start_migration; @@ -530,9 +599,14 @@ void migration_ioc_process_incoming(QIOChannel *ioc) */ start_migration = !migrate_use_multifd(); } else { + Error *local_err = NULL; /* Multiple connections */ assert(migrate_use_multifd()); - start_migration = multifd_recv_new_channel(ioc); + start_migration = multifd_recv_new_channel(ioc, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } } if (start_migration) { @@ -671,6 +745,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->compress_level = s->parameters.compress_level; params->has_compress_threads = true; params->compress_threads = s->parameters.compress_threads; + params->has_compress_wait_thread = true; + params->compress_wait_thread = s->parameters.compress_wait_thread; params->has_decompress_threads = true; params->decompress_threads = s->parameters.decompress_threads; params->has_cpu_throttle_initial = true; @@ -681,6 +757,8 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->tls_creds = g_strdup(s->parameters.tls_creds); params->has_tls_hostname = true; params->tls_hostname = g_strdup(s->parameters.tls_hostname); + params->has_tls_authz = true; + params->tls_authz = g_strdup(s->parameters.tls_authz); params->has_max_bandwidth = true; params->max_bandwidth = s->parameters.max_bandwidth; params->has_downtime_limit = true; @@ -689,23 +767,45 @@ MigrationParameters *qmp_query_migrate_parameters(Error **errp) params->x_checkpoint_delay = s->parameters.x_checkpoint_delay; params->has_block_incremental = true; params->block_incremental = s->parameters.block_incremental; - params->has_x_multifd_channels = true; - params->x_multifd_channels = s->parameters.x_multifd_channels; - params->has_x_multifd_page_count = true; - params->x_multifd_page_count = s->parameters.x_multifd_page_count; + params->has_multifd_channels = true; + params->multifd_channels = s->parameters.multifd_channels; params->has_xbzrle_cache_size = true; params->xbzrle_cache_size = s->parameters.xbzrle_cache_size; params->has_max_postcopy_bandwidth = true; params->max_postcopy_bandwidth = s->parameters.max_postcopy_bandwidth; + params->has_max_cpu_throttle = true; + params->max_cpu_throttle = s->parameters.max_cpu_throttle; + params->has_announce_initial = true; + params->announce_initial = s->parameters.announce_initial; + params->has_announce_max = true; + params->announce_max = s->parameters.announce_max; + params->has_announce_rounds = true; + params->announce_rounds = s->parameters.announce_rounds; + params->has_announce_step = true; + params->announce_step = s->parameters.announce_step; return params; } +AnnounceParameters *migrate_announce_params(void) +{ + static AnnounceParameters ap; + + MigrationState *s = migrate_get_current(); + + ap.initial = s->parameters.announce_initial; + ap.max = s->parameters.announce_max; + ap.rounds = s->parameters.announce_rounds; + ap.step = s->parameters.announce_step; + + return ≈ +} + /* * 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) +bool migration_is_setup_or_active(int state) { switch (state) { case MIGRATION_STATUS_ACTIVE: @@ -740,6 +840,7 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->ram->postcopy_requests = ram_counters.postcopy_requests; info->ram->page_size = qemu_target_page_size(); info->ram->multifd_bytes = ram_counters.multifd_bytes; + info->ram->pages_per_second = s->pages_per_second; if (migrate_use_xbzrle()) { info->has_xbzrle_cache = true; @@ -752,6 +853,18 @@ static void populate_ram_info(MigrationInfo *info, MigrationState *s) info->xbzrle_cache->overflow = xbzrle_counters.overflow; } + if (migrate_use_compression()) { + info->has_compression = true; + info->compression = g_malloc0(sizeof(*info->compression)); + info->compression->pages = compression_counters.pages; + info->compression->busy = compression_counters.busy; + info->compression->busy_rate = compression_counters.busy_rate; + info->compression->compressed_size = + compression_counters.compressed_size; + info->compression->compression_rate = + compression_counters.compression_rate; + } + if (cpu_throttle_active()) { info->has_cpu_throttle_percentage = true; info->cpu_throttle_percentage = cpu_throttle_get_percentage(); @@ -869,6 +982,15 @@ static bool migrate_caps_check(bool *cap_list, } #endif +#ifndef CONFIG_REPLICATION + if (cap_list[MIGRATION_CAPABILITY_X_COLO]) { + error_setg(errp, "QEMU compiled without replication module" + " can't enable COLO"); + error_append_hint(errp, "Please enable replication before COLO.\n"); + return false; + } +#endif + if (cap_list[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { if (cap_list[MIGRATION_CAPABILITY_COMPRESS]) { /* The decompression threads asynchronously write into RAM @@ -893,6 +1015,11 @@ static bool migrate_caps_check(bool *cap_list, error_setg(errp, "Postcopy is not supported"); return false; } + + if (cap_list[MIGRATION_CAPABILITY_X_IGNORE_SHARED]) { + error_setg(errp, "Postcopy is not compatible with ignore-shared"); + return false; + } } return true; @@ -902,6 +1029,12 @@ static void fill_destination_migration_info(MigrationInfo *info) { MigrationIncomingState *mis = migration_incoming_get_current(); + if (mis->socket_address_list) { + info->has_socket_address = true; + info->socket_address = + QAPI_CLONE(SocketAddressList, mis->socket_address_list); + } + switch (mis->state) { case MIGRATION_STATUS_NONE: return; @@ -1018,20 +1151,12 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) /* x_checkpoint_delay is now always positive */ - if (params->has_x_multifd_channels && (params->x_multifd_channels < 1)) { + if (params->has_multifd_channels && (params->multifd_channels < 1)) { error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "multifd_channels", "is invalid, it should be in the range of 1 to 255"); return false; } - if (params->has_x_multifd_page_count && - (params->x_multifd_page_count < 1 || - params->x_multifd_page_count > 10000)) { - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, - "multifd_page_count", - "is invalid, it should be in the range of 1 to 10000"); - return false; - } if (params->has_xbzrle_cache_size && (params->xbzrle_cache_size < qemu_target_page_size() || @@ -1043,6 +1168,44 @@ static bool migrate_params_check(MigrationParameters *params, Error **errp) return false; } + if (params->has_max_cpu_throttle && + (params->max_cpu_throttle < params->cpu_throttle_initial || + params->max_cpu_throttle > 99)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "max_cpu_throttle", + "an integer in the range of cpu_throttle_initial to 99"); + return false; + } + + if (params->has_announce_initial && + params->announce_initial > 100000) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_initial", + "is invalid, it must be less than 100000 ms"); + return false; + } + if (params->has_announce_max && + params->announce_max > 100000) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_max", + "is invalid, it must be less than 100000 ms"); + return false; + } + if (params->has_announce_rounds && + params->announce_rounds > 1000) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_rounds", + "is invalid, it must be in the range of 0 to 1000"); + return false; + } + if (params->has_announce_step && + (params->announce_step < 1 || + params->announce_step > 10000)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + "announce_step", + "is invalid, it must be in the range of 1 to 10000 ms"); + return false; + } return true; } @@ -1061,6 +1224,10 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->compress_threads = params->compress_threads; } + if (params->has_compress_wait_thread) { + dest->compress_wait_thread = params->compress_wait_thread; + } + if (params->has_decompress_threads) { dest->decompress_threads = params->decompress_threads; } @@ -1098,11 +1265,8 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_block_incremental) { dest->block_incremental = params->block_incremental; } - if (params->has_x_multifd_channels) { - dest->x_multifd_channels = params->x_multifd_channels; - } - if (params->has_x_multifd_page_count) { - dest->x_multifd_page_count = params->x_multifd_page_count; + if (params->has_multifd_channels) { + dest->multifd_channels = params->multifd_channels; } if (params->has_xbzrle_cache_size) { dest->xbzrle_cache_size = params->xbzrle_cache_size; @@ -1110,6 +1274,21 @@ static void migrate_params_test_apply(MigrateSetParameters *params, if (params->has_max_postcopy_bandwidth) { dest->max_postcopy_bandwidth = params->max_postcopy_bandwidth; } + if (params->has_max_cpu_throttle) { + dest->max_cpu_throttle = params->max_cpu_throttle; + } + if (params->has_announce_initial) { + dest->announce_initial = params->announce_initial; + } + if (params->has_announce_max) { + dest->announce_max = params->announce_max; + } + if (params->has_announce_rounds) { + dest->announce_rounds = params->announce_rounds; + } + if (params->has_announce_step) { + dest->announce_step = params->announce_step; + } } static void migrate_params_apply(MigrateSetParameters *params, Error **errp) @@ -1126,6 +1305,10 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.compress_threads = params->compress_threads; } + if (params->has_compress_wait_thread) { + s->parameters.compress_wait_thread = params->compress_wait_thread; + } + if (params->has_decompress_threads) { s->parameters.decompress_threads = params->decompress_threads; } @@ -1150,9 +1333,15 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) s->parameters.tls_hostname = g_strdup(params->tls_hostname->u.s); } + if (params->has_tls_authz) { + g_free(s->parameters.tls_authz); + assert(params->tls_authz->type == QTYPE_QSTRING); + s->parameters.tls_authz = g_strdup(params->tls_authz->u.s); + } + if (params->has_max_bandwidth) { s->parameters.max_bandwidth = params->max_bandwidth; - if (s->to_dst_file) { + if (s->to_dst_file && !migration_in_postcopy()) { qemu_file_set_rate_limit(s->to_dst_file, s->parameters.max_bandwidth / XFER_LIMIT_RATIO); } @@ -1172,11 +1361,8 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) if (params->has_block_incremental) { s->parameters.block_incremental = params->block_incremental; } - if (params->has_x_multifd_channels) { - s->parameters.x_multifd_channels = params->x_multifd_channels; - } - if (params->has_x_multifd_page_count) { - s->parameters.x_multifd_page_count = params->x_multifd_page_count; + if (params->has_multifd_channels) { + s->parameters.multifd_channels = params->multifd_channels; } if (params->has_xbzrle_cache_size) { s->parameters.xbzrle_cache_size = params->xbzrle_cache_size; @@ -1184,6 +1370,25 @@ static void migrate_params_apply(MigrateSetParameters *params, Error **errp) } if (params->has_max_postcopy_bandwidth) { s->parameters.max_postcopy_bandwidth = params->max_postcopy_bandwidth; + if (s->to_dst_file && migration_in_postcopy()) { + qemu_file_set_rate_limit(s->to_dst_file, + s->parameters.max_postcopy_bandwidth / XFER_LIMIT_RATIO); + } + } + if (params->has_max_cpu_throttle) { + s->parameters.max_cpu_throttle = params->max_cpu_throttle; + } + if (params->has_announce_initial) { + s->parameters.announce_initial = params->announce_initial; + } + if (params->has_announce_max) { + s->parameters.announce_max = params->announce_max; + } + if (params->has_announce_rounds) { + s->parameters.announce_rounds = params->announce_rounds; + } + if (params->has_announce_step) { + s->parameters.announce_step = params->announce_step; } } @@ -1300,7 +1505,6 @@ static void migrate_fd_cleanup(void *opaque) qemu_savevm_state_cleanup(); if (s->to_dst_file) { - Error *local_err = NULL; QEMUFile *tmp; trace_migrate_fd_cleanup(); @@ -1311,9 +1515,7 @@ static void migrate_fd_cleanup(void *opaque) } qemu_mutex_lock_iothread(); - if (multifd_save_cleanup(&local_err) != 0) { - error_report_err(local_err); - } + multifd_save_cleanup(); qemu_mutex_lock(&s->qemu_file_lock); tmp = s->to_dst_file; s->to_dst_file = NULL; @@ -1444,7 +1646,11 @@ bool migration_in_postcopy_after_devices(MigrationState *s) bool migration_is_idle(void) { - MigrationState *s = migrate_get_current(); + MigrationState *s = current_migration; + + if (!s) { + return true; + } switch (s->state) { case MIGRATION_STATUS_NONE: @@ -1482,6 +1688,7 @@ void migrate_init(MigrationState *s) s->rp_state.from_dst_file = NULL; s->rp_state.error = false; s->mbps = 0.0; + s->pages_per_second = 0.0; s->downtime = 0; s->expected_downtime = 0; s->setup_time = 0; @@ -1504,10 +1711,10 @@ static GSList *migration_blockers; int migrate_add_blocker(Error *reason, Error **errp) { - if (migrate_get_current()->only_migratable) { - error_propagate(errp, error_copy(reason)); - error_prepend(errp, "disallowing migration blocker " - "(--only_migratable) for: "); + if (only_migratable) { + error_propagate_prepend(errp, error_copy(reason), + "disallowing migration blocker " + "(--only_migratable) for: "); return -EACCES; } @@ -1516,9 +1723,9 @@ int migrate_add_blocker(Error *reason, Error **errp) return 0; } - error_propagate(errp, error_copy(reason)); - error_prepend(errp, "disallowing migration blocker (migration in " - "progress) for: "); + error_propagate_prepend(errp, error_copy(reason), + "disallowing migration blocker " + "(migration in progress) for: "); return -EBUSY; } @@ -1871,6 +2078,15 @@ int migrate_compress_threads(void) return s->parameters.compress_threads; } +int migrate_compress_wait_thread(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->parameters.compress_wait_thread; +} + int migrate_decompress_threads(void) { MigrationState *s; @@ -1889,6 +2105,15 @@ bool migrate_dirty_bitmaps(void) return s->enabled_capabilities[MIGRATION_CAPABILITY_DIRTY_BITMAPS]; } +bool migrate_ignore_shared(void) +{ + MigrationState *s; + + s = migrate_get_current(); + + return s->enabled_capabilities[MIGRATION_CAPABILITY_X_IGNORE_SHARED]; +} + bool migrate_use_events(void) { MigrationState *s; @@ -1904,7 +2129,7 @@ bool migrate_use_multifd(void) s = migrate_get_current(); - return s->enabled_capabilities[MIGRATION_CAPABILITY_X_MULTIFD]; + return s->enabled_capabilities[MIGRATION_CAPABILITY_MULTIFD]; } bool migrate_pause_before_switchover(void) @@ -1923,16 +2148,7 @@ int migrate_multifd_channels(void) s = migrate_get_current(); - return s->parameters.x_multifd_channels; -} - -int migrate_multifd_page_count(void) -{ - MigrationState *s; - - s = migrate_get_current(); - - return s->parameters.x_multifd_page_count; + return s->parameters.multifd_channels; } int migrate_use_xbzrle(void) @@ -1962,7 +2178,6 @@ static int64_t migrate_max_postcopy_bandwidth(void) return s->parameters.max_postcopy_bandwidth; } - bool migrate_use_block(void) { MigrationState *s; @@ -2104,6 +2319,7 @@ static void *source_return_path_thread(void *opaque) int res; trace_source_return_path_thread_entry(); + rcu_register_thread(); retry: while (!ms->rp_state.error && !qemu_file_get_error(rp) && @@ -2230,7 +2446,10 @@ out: */ if (postcopy_pause_return_path_thread(ms)) { /* Reload rp, reset the rest */ - rp = ms->rp_state.from_dst_file; + if (rp != ms->rp_state.from_dst_file) { + qemu_fclose(rp); + rp = ms->rp_state.from_dst_file; + } ms->rp_state.error = false; goto retry; } @@ -2243,6 +2462,7 @@ out: trace_source_return_path_thread_end(); ms->rp_state.from_dst_file = NULL; qemu_fclose(rp); + rcu_unregister_thread(); return NULL; } @@ -2314,7 +2534,7 @@ static int postcopy_start(MigrationState *ms) qemu_mutex_lock_iothread(); trace_postcopy_start_set_run(); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); global_state_store(); ret = vm_stop_force_state(RUN_STATE_FINISH_MIGRATE); if (ret < 0) { @@ -2518,7 +2738,7 @@ static void migration_completion(MigrationState *s) if (s->state == MIGRATION_STATUS_ACTIVE) { qemu_mutex_lock_iothread(); s->downtime_start = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, NULL); s->vm_was_running = runstate_is_running(); ret = global_state_store(); @@ -2728,6 +2948,13 @@ static MigThrError postcopy_pause(MigrationState *s) static MigThrError migration_detect_error(MigrationState *s) { int ret; + int state = s->state; + + if (state == MIGRATION_STATUS_CANCELLING || + state == MIGRATION_STATUS_CANCELLED) { + /* End the migration, but don't set the state to failed */ + return MIG_THR_ERR_FATAL; + } /* Try to detect any file errors */ ret = qemu_file_get_error(s->to_dst_file); @@ -2737,7 +2964,7 @@ static MigThrError migration_detect_error(MigrationState *s) return MIG_THR_ERR_NONE; } - if (s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) { + if (state == MIGRATION_STATUS_POSTCOPY_ACTIVE && ret == -EIO) { /* * For postcopy, we allow the network to be down for a * while. After that, it can be continued by a @@ -2749,7 +2976,7 @@ static MigThrError migration_detect_error(MigrationState *s) * For precopy (or postcopy with error outside IO), we fail * with no time. */ - migrate_set_state(&s->state, s->state, MIGRATION_STATUS_FAILED); + migrate_set_state(&s->state, state, MIGRATION_STATUS_FAILED); trace_migration_thread_file_err(); /* Time to stop the migration, now. */ @@ -2787,7 +3014,7 @@ static void migration_calculate_complete(MigrationState *s) static void migration_update_counters(MigrationState *s, int64_t current_time) { - uint64_t transferred, time_spent; + uint64_t transferred, transferred_pages, time_spent; uint64_t current_bytes; /* bytes transferred since the beginning */ double bandwidth; @@ -2804,6 +3031,11 @@ static void migration_update_counters(MigrationState *s, s->mbps = (((double) transferred * 8.0) / ((double) time_spent / 1000.0)) / 1000.0 / 1000.0; + transferred_pages = ram_get_total_transferred_pages() - + s->iteration_initial_pages; + s->pages_per_second = (double) transferred_pages / + (((double) time_spent / 1000.0)); + /* * if we haven't sent anything, we don't want to * recalculate. 10000 is a small enough number for our purposes @@ -2816,6 +3048,7 @@ static void migration_update_counters(MigrationState *s, s->iteration_start_time = current_time; s->iteration_initial_bytes = current_bytes; + s->iteration_initial_pages = ram_get_total_transferred_pages(); trace_migrate_transferred(transferred, time_spent, bandwidth, s->threshold_size); @@ -2938,6 +3171,7 @@ static void *migration_thread(void *opaque) rcu_register_thread(); + object_ref(OBJECT(s)); s->iteration_start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); qemu_savevm_state_header(s->to_dst_file); @@ -2963,6 +3197,11 @@ static void *migration_thread(void *opaque) qemu_savevm_send_postcopy_advise(s->to_dst_file); } + if (migrate_colo_enabled()) { + /* Notify migration destination that we enable COLO */ + qemu_savevm_send_colo_enable(s->to_dst_file); + } + qemu_savevm_state_setup(s->to_dst_file); s->setup_time = qemu_clock_get_ms(QEMU_CLOCK_HOST) - setup_start; @@ -3029,6 +3268,7 @@ static void *migration_thread(void *opaque) trace_migration_thread_after_loop(); migration_iteration_finish(s); + object_unref(OBJECT(s)); rcu_unregister_thread(); return NULL; } @@ -3101,7 +3341,7 @@ void migration_global_dump(Monitor *mon) monitor_printf(mon, "store-global-state: %s\n", ms->store_global_state ? "on" : "off"); monitor_printf(mon, "only-migratable: %s\n", - ms->only_migratable ? "on" : "off"); + only_migratable ? "on" : "off"); monitor_printf(mon, "send-configuration: %s\n", ms->send_configuration ? "on" : "off"); monitor_printf(mon, "send-section-footer: %s\n", @@ -3116,7 +3356,6 @@ void migration_global_dump(Monitor *mon) static Property migration_properties[] = { DEFINE_PROP_BOOL("store-global-state", MigrationState, store_global_state, true), - DEFINE_PROP_BOOL("only-migratable", MigrationState, only_migratable, false), DEFINE_PROP_BOOL("send-configuration", MigrationState, send_configuration, true), DEFINE_PROP_BOOL("send-section-footer", MigrationState, @@ -3131,6 +3370,8 @@ static Property migration_properties[] = { DEFINE_PROP_UINT8("x-compress-threads", MigrationState, parameters.compress_threads, DEFAULT_MIGRATE_COMPRESS_THREAD_COUNT), + DEFINE_PROP_BOOL("x-compress-wait-thread", MigrationState, + parameters.compress_wait_thread, true), DEFINE_PROP_UINT8("x-decompress-threads", MigrationState, parameters.decompress_threads, DEFAULT_MIGRATE_DECOMPRESS_THREAD_COUNT), @@ -3148,18 +3389,30 @@ static Property migration_properties[] = { DEFINE_PROP_UINT32("x-checkpoint-delay", MigrationState, parameters.x_checkpoint_delay, DEFAULT_MIGRATE_X_CHECKPOINT_DELAY), - DEFINE_PROP_UINT8("x-multifd-channels", MigrationState, - parameters.x_multifd_channels, + DEFINE_PROP_UINT8("multifd-channels", MigrationState, + parameters.multifd_channels, DEFAULT_MIGRATE_MULTIFD_CHANNELS), - DEFINE_PROP_UINT32("x-multifd-page-count", MigrationState, - parameters.x_multifd_page_count, - DEFAULT_MIGRATE_MULTIFD_PAGE_COUNT), DEFINE_PROP_SIZE("xbzrle-cache-size", MigrationState, parameters.xbzrle_cache_size, DEFAULT_MIGRATE_XBZRLE_CACHE_SIZE), DEFINE_PROP_SIZE("max-postcopy-bandwidth", MigrationState, parameters.max_postcopy_bandwidth, DEFAULT_MIGRATE_MAX_POSTCOPY_BANDWIDTH), + DEFINE_PROP_UINT8("max-cpu-throttle", MigrationState, + parameters.max_cpu_throttle, + DEFAULT_MIGRATE_MAX_CPU_THROTTLE), + DEFINE_PROP_SIZE("announce-initial", MigrationState, + parameters.announce_initial, + DEFAULT_MIGRATE_ANNOUNCE_INITIAL), + DEFINE_PROP_SIZE("announce-max", MigrationState, + parameters.announce_max, + DEFAULT_MIGRATE_ANNOUNCE_MAX), + DEFINE_PROP_SIZE("announce-rounds", MigrationState, + parameters.announce_rounds, + DEFAULT_MIGRATE_ANNOUNCE_ROUNDS), + DEFINE_PROP_SIZE("announce-step", MigrationState, + parameters.announce_step, + DEFAULT_MIGRATE_ANNOUNCE_STEP), /* Migration capabilities */ DEFINE_PROP_MIG_CAP("x-xbzrle", MIGRATION_CAPABILITY_XBZRLE), @@ -3173,7 +3426,7 @@ static Property migration_properties[] = { DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM), DEFINE_PROP_MIG_CAP("x-block", MIGRATION_CAPABILITY_BLOCK), DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH), - DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_X_MULTIFD), + DEFINE_PROP_MIG_CAP("x-multifd", MIGRATION_CAPABILITY_MULTIFD), DEFINE_PROP_END_OF_LIST(), }; @@ -3210,6 +3463,7 @@ static void migration_instance_init(Object *obj) ms->state = MIGRATION_STATUS_NONE; ms->mbps = -1; + ms->pages_per_second = -1; qemu_sem_init(&ms->pause_sem, 0); qemu_mutex_init(&ms->error_mutex); @@ -3226,10 +3480,14 @@ static void migration_instance_init(Object *obj) params->has_downtime_limit = true; params->has_x_checkpoint_delay = true; params->has_block_incremental = true; - params->has_x_multifd_channels = true; - params->has_x_multifd_page_count = true; + params->has_multifd_channels = true; params->has_xbzrle_cache_size = true; params->has_max_postcopy_bandwidth = true; + params->has_max_cpu_throttle = true; + params->has_announce_initial = true; + params->has_announce_max = true; + params->has_announce_rounds = true; + params->has_announce_step = true; qemu_sem_init(&ms->postcopy_pause_sem, 0); qemu_sem_init(&ms->postcopy_pause_rp_sem, 0); diff --git a/migration/migration.h b/migration/migration.h index 64a7b33735..438f17edad 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -21,6 +21,7 @@ #include "qemu/coroutine_int.h" #include "hw/qdev.h" #include "io/channel.h" +#include "net/announce.h" struct PostcopyBlocktimeContext; @@ -36,6 +37,9 @@ struct MigrationIncomingState { */ QemuEvent main_thread_load_event; + /* For network announces */ + AnnounceTimer announce_timer; + size_t largest_page_size; bool have_fault_thread; QemuThread fault_thread; @@ -80,6 +84,9 @@ struct MigrationIncomingState { bool postcopy_recover_triggered; QemuSemaphore postcopy_pause_sem_dst; QemuSemaphore postcopy_pause_sem_fault; + + /* List of listening socket addresses */ + SocketAddressList *socket_address_list; }; MigrationIncomingState *migration_incoming_get_current(void); @@ -126,7 +133,13 @@ struct MigrationState */ QemuSemaphore rate_limit_sem; - /* bytes already send at the beggining of current interation */ + /* pages already send at the beginning of current iteration */ + uint64_t iteration_initial_pages; + + /* pages transferred per second */ + double pages_per_second; + + /* bytes already send at the beginning of current iteration */ uint64_t iteration_initial_bytes; /* time at the start of current iteration */ int64_t iteration_start_time; @@ -206,9 +219,6 @@ struct MigrationState */ bool store_global_state; - /* Whether the VM is only allowing for migratable devices */ - bool only_migratable; - /* Whether we send QEMU_VM_CONFIGURATION during migration */ bool send_configuration; /* Whether we send section footer during migration */ @@ -229,7 +239,7 @@ struct MigrationState void migrate_set_state(int *state, int old_state, int new_state); void migration_fd_process_incoming(QEMUFile *f); -void migration_ioc_process_incoming(QIOChannel *ioc); +void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp); void migration_incoming_process(void); bool migration_has_all_channels(void); @@ -241,6 +251,8 @@ void migrate_fd_error(MigrationState *s, const Error *error); void migrate_fd_connect(MigrationState *s, Error *error_in); +bool migration_is_setup_or_active(int state); + void migrate_init(MigrationState *s); bool migration_is_blocked(Error **errp); /* True if outgoing migration has entered postcopy phase */ @@ -253,12 +265,12 @@ bool migrate_release_ram(void); bool migrate_postcopy_ram(void); bool migrate_zero_blocks(void); bool migrate_dirty_bitmaps(void); +bool migrate_ignore_shared(void); bool migrate_auto_converge(void); bool migrate_use_multifd(void); bool migrate_pause_before_switchover(void); int migrate_multifd_channels(void); -int migrate_multifd_page_count(void); int migrate_use_xbzrle(void); int64_t migrate_xbzrle_cache_size(void); @@ -266,11 +278,15 @@ bool migrate_colo_enabled(void); bool migrate_use_block(void); bool migrate_use_block_incremental(void); +int migrate_max_cpu_throttle(void); bool migrate_use_return_path(void); +uint64_t ram_get_total_transferred_pages(void); + bool migrate_use_compression(void); int migrate_compress_level(void); int migrate_compress_threads(void); +int migrate_compress_wait_thread(void); int migrate_decompress_threads(void); bool migrate_use_events(void); bool migrate_postcopy_blocktime(void); @@ -288,9 +304,12 @@ void migrate_send_rp_resume_ack(MigrationIncomingState *mis, uint32_t value); void dirty_bitmap_mig_before_vm_start(void); void init_dirty_bitmap_incoming_migration(void); +void migrate_add_address(SocketAddress *address); + +int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque); #define qemu_ram_foreach_block \ - #warning "Use qemu_ram_foreach_block_migratable in migration code" + #warning "Use foreach_not_ignored_block in migration code" void migration_make_urgent_request(void); void migration_consume_urgent_request(void); diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 932f188949..e2aa57a701 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -319,10 +319,10 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis) /* Callback from postcopy_ram_supported_by_host block iterator. */ -static int test_ramblock_postcopiable(const char *block_name, void *host_addr, - ram_addr_t offset, ram_addr_t length, void *opaque) +static int test_ramblock_postcopiable(RAMBlock *rb, void *opaque) { - RAMBlock *rb = qemu_ram_block_by_name(block_name); + const char *block_name = qemu_ram_get_idstr(rb); + ram_addr_t length = qemu_ram_get_used_length(rb); size_t pagesize = qemu_ram_pagesize(rb); if (length % pagesize) { @@ -374,7 +374,7 @@ bool postcopy_ram_supported_by_host(MigrationIncomingState *mis) } /* We don't support postcopy with shared RAM yet */ - if (qemu_ram_foreach_migratable_block(test_ramblock_postcopiable, NULL)) { + if (foreach_not_ignored_block(test_ramblock_postcopiable, NULL)) { goto out; } @@ -443,9 +443,12 @@ out: * 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) +static int init_range(RAMBlock *rb, void *opaque) { + const char *block_name = qemu_ram_get_idstr(rb); + void *host_addr = qemu_ram_get_host_addr(rb); + ram_addr_t offset = qemu_ram_get_offset(rb); + ram_addr_t length = qemu_ram_get_used_length(rb); trace_postcopy_init_range(block_name, host_addr, offset, length); /* @@ -465,9 +468,12 @@ static int init_range(const char *block_name, void *host_addr, * 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) +static int cleanup_range(RAMBlock *rb, void *opaque) { + const char *block_name = qemu_ram_get_idstr(rb); + void *host_addr = qemu_ram_get_host_addr(rb); + ram_addr_t offset = qemu_ram_get_offset(rb); + ram_addr_t length = qemu_ram_get_used_length(rb); MigrationIncomingState *mis = opaque; struct uffdio_range range_struct; trace_postcopy_cleanup_range(block_name, host_addr, offset, length); @@ -502,13 +508,27 @@ static int cleanup_range(const char *block_name, void *host_addr, */ int postcopy_ram_incoming_init(MigrationIncomingState *mis) { - if (qemu_ram_foreach_migratable_block(init_range, NULL)) { + if (foreach_not_ignored_block(init_range, NULL)) { return -1; } return 0; } +/* + * Manage a single vote to the QEMU balloon inhibitor for all postcopy usage, + * last caller wins. + */ +static void postcopy_balloon_inhibit(bool state) +{ + static bool cur_state = false; + + if (state != cur_state) { + qemu_balloon_inhibit(state); + cur_state = state; + } +} + /* * At the end of a migration where postcopy_ram_incoming_init was called. */ @@ -519,27 +539,28 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) if (mis->have_fault_thread) { Error *local_err = NULL; - if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_END, &local_err)) { - error_report_err(local_err); - return -1; - } - - if (qemu_ram_foreach_migratable_block(cleanup_range, mis)) { - return -1; - } /* Let the fault thread quit */ atomic_set(&mis->fault_thread_quit, 1); postcopy_fault_thread_notify(mis); trace_postcopy_ram_incoming_cleanup_join(); qemu_thread_join(&mis->fault_thread); + if (postcopy_notify(POSTCOPY_NOTIFY_INBOUND_END, &local_err)) { + error_report_err(local_err); + return -1; + } + + if (foreach_not_ignored_block(cleanup_range, mis)) { + return -1; + } + trace_postcopy_ram_incoming_cleanup_closeuf(); close(mis->userfault_fd); close(mis->userfault_event_fd); mis->have_fault_thread = false; } - qemu_balloon_inhibit(false); + postcopy_balloon_inhibit(false); if (enable_mlock) { if (os_mlock() < 0) { @@ -571,9 +592,12 @@ int postcopy_ram_incoming_cleanup(MigrationIncomingState *mis) /* * 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) +static int nhp_range(RAMBlock *rb, void *opaque) { + const char *block_name = qemu_ram_get_idstr(rb); + void *host_addr = qemu_ram_get_host_addr(rb); + ram_addr_t offset = qemu_ram_get_offset(rb); + ram_addr_t length = qemu_ram_get_used_length(rb); trace_postcopy_nhp_range(block_name, host_addr, offset, length); /* @@ -593,7 +617,7 @@ static int nhp_range(const char *block_name, void *host_addr, */ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) { - if (qemu_ram_foreach_migratable_block(nhp_range, mis)) { + if (foreach_not_ignored_block(nhp_range, mis)) { return -1; } @@ -604,22 +628,20 @@ int postcopy_ram_prepare_discard(MigrationIncomingState *mis) /* * Mark the given area of RAM as requiring notification to unwritten areas - * Used as a callback on qemu_ram_foreach_migratable_block. + * Used as a callback on foreach_not_ignored_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) +static int ram_block_enable_notify(RAMBlock *rb, 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.range.start = (uintptr_t)qemu_ram_get_host_addr(rb); + reg_struct.range.len = qemu_ram_get_used_length(rb); reg_struct.mode = UFFDIO_REGISTER_MODE_MISSING; /* Now tell our userfault_fd that it's responsible for this area */ @@ -632,7 +654,6 @@ static int ram_block_enable_notify(const char *block_name, void *host_addr, return -1; } if (reg_struct.ioctls & ((__u64)1 << _UFFDIO_ZEROPAGE)) { - RAMBlock *rb = qemu_ram_block_by_name(block_name); qemu_ram_set_uf_zeroable(rb); } @@ -853,6 +874,7 @@ static void *postcopy_ram_fault_thread(void *opaque) RAMBlock *rb = NULL; trace_postcopy_ram_fault_thread_entry(); + rcu_register_thread(); mis->last_rb = NULL; /* last RAMBlock we sent part of */ qemu_sem_post(&mis->fault_thread_sem); @@ -1059,6 +1081,7 @@ retry: } } } + rcu_unregister_thread(); trace_postcopy_ram_fault_thread_exit(); g_free(pfd); return NULL; @@ -1099,7 +1122,8 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) mis->have_fault_thread = true; /* Mark so that we get notified of accesses to unwritten areas */ - if (qemu_ram_foreach_migratable_block(ram_block_enable_notify, mis)) { + if (foreach_not_ignored_block(ram_block_enable_notify, mis)) { + error_report("ram_block_enable_notify failed"); return -1; } @@ -1107,7 +1131,7 @@ int postcopy_ram_enable_notify(MigrationIncomingState *mis) * Ballooning can mark pages as absent while we're postcopying * that would cause false userfaults. */ - qemu_balloon_inhibit(true); + postcopy_balloon_inhibit(true); trace_postcopy_ram_enable_notify(); diff --git a/migration/qemu-file-channel.c b/migration/qemu-file-channel.c index e202d73834..8e639eb496 100644 --- a/migration/qemu-file-channel.c +++ b/migration/qemu-file-channel.c @@ -49,7 +49,11 @@ static ssize_t channel_writev_buffer(void *opaque, ssize_t len; len = qio_channel_writev(ioc, local_iov, nlocal_iov, NULL); if (len == QIO_CHANNEL_ERR_BLOCK) { - qio_channel_wait(ioc, G_IO_OUT); + if (qemu_in_coroutine()) { + qio_channel_yield(ioc, G_IO_OUT); + } else { + qio_channel_wait(ioc, G_IO_OUT); + } continue; } if (len < 0) { @@ -80,7 +84,11 @@ static ssize_t channel_get_buffer(void *opaque, ret = qio_channel_read(ioc, (char *)buf, size, NULL); if (ret < 0) { if (ret == QIO_CHANNEL_ERR_BLOCK) { - qio_channel_yield(ioc, G_IO_IN); + if (qemu_in_coroutine()) { + qio_channel_yield(ioc, G_IO_IN); + } else { + qio_channel_wait(ioc, G_IO_IN); + } } else { /* XXX handle Error * object */ return -EIO; diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 0463f4c321..977b9ae07c 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -253,8 +253,12 @@ size_t ram_control_save_page(QEMUFile *f, ram_addr_t block_offset, if (f->hooks && f->hooks->save_page) { int ret = f->hooks->save_page(f, f->opaque, block_offset, offset, size, bytes_sent); - f->bytes_xfer += size; - if (ret != RAM_SAVE_CONTROL_DELAYED) { + if (ret != RAM_SAVE_CONTROL_NOT_SUPP) { + f->bytes_xfer += size; + } + + if (ret != RAM_SAVE_CONTROL_DELAYED && + ret != RAM_SAVE_CONTROL_NOT_SUPP) { if (bytes_sent && *bytes_sent > 0) { qemu_update_position(f, *bytes_sent); } else if (ret < 0) { diff --git a/migration/qemu-file.h b/migration/qemu-file.h index 2ccfcfb2a8..13baf896bd 100644 --- a/migration/qemu-file.h +++ b/migration/qemu-file.h @@ -149,7 +149,6 @@ void qemu_update_position(QEMUFile *f, size_t size); void qemu_file_reset_rate_limit(QEMUFile *f); void qemu_file_set_rate_limit(QEMUFile *f, int64_t new_rate); int64_t qemu_file_get_rate_limit(QEMUFile *f); -int qemu_file_get_error(QEMUFile *f); void qemu_file_set_error(QEMUFile *f, int ret); int qemu_file_shutdown(QEMUFile *f); QEMUFile *qemu_file_get_return_path(QEMUFile *f); diff --git a/migration/ram.c b/migration/ram.c index 24dea2730c..1ca9ba77b6 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -33,6 +33,7 @@ #include "qemu/bitops.h" #include "qemu/bitmap.h" #include "qemu/main-loop.h" +#include "qemu/pmem.h" #include "xbzrle.h" #include "ram.h" #include "migration.h" @@ -158,18 +159,44 @@ out: return ret; } +static bool ramblock_is_ignored(RAMBlock *block) +{ + return !qemu_ram_is_migratable(block) || + (migrate_ignore_shared() && qemu_ram_is_shared(block)); +} + /* Should be holding either ram_list.mutex, or the RCU lock. */ +#define RAMBLOCK_FOREACH_NOT_IGNORED(block) \ + INTERNAL_RAMBLOCK_FOREACH(block) \ + if (ramblock_is_ignored(block)) {} else + #define RAMBLOCK_FOREACH_MIGRATABLE(block) \ INTERNAL_RAMBLOCK_FOREACH(block) \ if (!qemu_ram_is_migratable(block)) {} else #undef RAMBLOCK_FOREACH +int foreach_not_ignored_block(RAMBlockIterFunc func, void *opaque) +{ + RAMBlock *block; + int ret = 0; + + rcu_read_lock(); + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + ret = func(block, opaque); + if (ret) { + break; + } + } + rcu_read_unlock(); + return ret; +} + static void ramblock_recv_map_init(void) { RAMBlock *rb; - RAMBLOCK_FOREACH_MIGRATABLE(rb) { + RAMBLOCK_FOREACH_NOT_IGNORED(rb) { assert(!rb->receivedmap); rb->receivedmap = bitmap_new(rb->max_length >> qemu_target_page_bits()); } @@ -289,6 +316,8 @@ struct RAMState { uint32_t last_version; /* We are in the first round */ bool ram_bulk_stage; + /* The free page optimization is enabled */ + bool fpo_enabled; /* How many times we have dirty too many pages */ int dirty_rate_high_cnt; /* these variables are used for bitmap sync */ @@ -300,24 +329,68 @@ struct RAMState { uint64_t num_dirty_pages_period; /* xbzrle misses since the beginning of the period */ uint64_t xbzrle_cache_miss_prev; - /* number of iterations at the beginning of period */ - uint64_t iterations_prev; - /* Iterations since start */ - uint64_t iterations; + + /* compression statistics since the beginning of the period */ + /* amount of count that no free thread to compress data */ + uint64_t compress_thread_busy_prev; + /* amount bytes after compression */ + uint64_t compressed_size_prev; + /* amount of compressed pages */ + uint64_t compress_pages_prev; + + /* total handled target pages at the beginning of period */ + uint64_t target_page_count_prev; + /* total handled target pages since start */ + uint64_t target_page_count; /* number of dirty bits in the bitmap */ uint64_t migration_dirty_pages; - /* protects modification of the bitmap */ + /* Protects modification of the bitmap and migration dirty pages */ QemuMutex bitmap_mutex; /* The RAMBlock used in the last src_page_requests */ RAMBlock *last_req_rb; /* Queue of outstanding page requests from the destination */ QemuMutex src_page_req_mutex; - QSIMPLEQ_HEAD(src_page_requests, RAMSrcPageRequest) src_page_requests; + QSIMPLEQ_HEAD(, RAMSrcPageRequest) src_page_requests; }; typedef struct RAMState RAMState; static RAMState *ram_state; +static NotifierWithReturnList precopy_notifier_list; + +void precopy_infrastructure_init(void) +{ + notifier_with_return_list_init(&precopy_notifier_list); +} + +void precopy_add_notifier(NotifierWithReturn *n) +{ + notifier_with_return_list_add(&precopy_notifier_list, n); +} + +void precopy_remove_notifier(NotifierWithReturn *n) +{ + notifier_with_return_remove(n); +} + +int precopy_notify(PrecopyNotifyReason reason, Error **errp) +{ + PrecopyNotifyData pnd; + pnd.reason = reason; + pnd.errp = errp; + + return notifier_with_return_list_notify(&precopy_notifier_list, &pnd); +} + +void precopy_enable_free_page_optimization(void) +{ + if (!ram_state) { + return; + } + + ram_state->fpo_enabled = true; +} + uint64_t ram_bytes_remaining(void) { return ram_state ? (ram_state->migration_dirty_pages * TARGET_PAGE_SIZE) : @@ -337,9 +410,12 @@ struct PageSearchStatus { }; typedef struct PageSearchStatus PageSearchStatus; +CompressionStats compression_counters; + struct CompressParam { bool done; bool quit; + bool zero_page; QEMUFile *file; QemuMutex mutex; QemuCond cond; @@ -381,14 +457,15 @@ static QemuThread *decompress_threads; static QemuMutex decomp_done_lock; static QemuCond decomp_done_cond; -static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf); +static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset, uint8_t *source_buf); static void *do_data_compress(void *opaque) { CompressParam *param = opaque; RAMBlock *block; ram_addr_t offset; + bool zero_page; qemu_mutex_lock(¶m->mutex); while (!param->quit) { @@ -398,11 +475,12 @@ static void *do_data_compress(void *opaque) param->block = NULL; qemu_mutex_unlock(¶m->mutex); - do_compress_ram_page(param->file, ¶m->stream, block, offset, - param->originbuf); + zero_page = do_compress_ram_page(param->file, ¶m->stream, + block, offset, param->originbuf); qemu_mutex_lock(&comp_done_lock); param->done = true; + param->zero_page = zero_page; qemu_cond_signal(&comp_done_cond); qemu_mutex_unlock(&comp_done_lock); @@ -416,28 +494,14 @@ static void *do_data_compress(void *opaque) return NULL; } -static inline void terminate_compression_threads(void) -{ - int idx, thread_count; - - thread_count = migrate_compress_threads(); - - for (idx = 0; idx < thread_count; idx++) { - qemu_mutex_lock(&comp_param[idx].mutex); - comp_param[idx].quit = true; - qemu_cond_signal(&comp_param[idx].cond); - qemu_mutex_unlock(&comp_param[idx].mutex); - } -} - static void compress_threads_save_cleanup(void) { int i, thread_count; - if (!migrate_use_compression()) { + if (!migrate_use_compression() || !comp_param) { return; } - terminate_compression_threads(); + thread_count = migrate_compress_threads(); for (i = 0; i < thread_count; i++) { /* @@ -447,6 +511,12 @@ static void compress_threads_save_cleanup(void) if (!comp_param[i].file) { break; } + + qemu_mutex_lock(&comp_param[i].mutex); + comp_param[i].quit = true; + qemu_cond_signal(&comp_param[i].cond); + qemu_mutex_unlock(&comp_param[i].mutex); + qemu_thread_join(compress_threads + i); qemu_mutex_destroy(&comp_param[i].mutex); qemu_cond_destroy(&comp_param[i].cond); @@ -513,20 +583,29 @@ exit: #define MULTIFD_FLAG_SYNC (1 << 0) +/* This value needs to be a multiple of qemu_target_page_size() */ +#define MULTIFD_PACKET_SIZE (512 * 1024) + typedef struct { uint32_t magic; uint32_t version; unsigned char uuid[16]; /* QemuUUID */ uint8_t id; + uint8_t unused1[7]; /* Reserved for future use */ + uint64_t unused2[4]; /* Reserved for future use */ } __attribute__((packed)) MultiFDInit_t; typedef struct { uint32_t magic; uint32_t version; uint32_t flags; - uint32_t size; - uint32_t used; + /* maximum number of allocated pages */ + uint32_t pages_alloc; + uint32_t pages_used; + /* size of the next packet that contains pages */ + uint32_t next_packet_size; uint64_t packet_num; + uint64_t unused[4]; /* Reserved for future use */ char ramblock[256]; uint64_t offset[]; } __attribute__((packed)) MultiFDPacket_t; @@ -573,6 +652,8 @@ typedef struct { MultiFDPacket_t *packet; /* multifd flags for each packet */ uint32_t flags; + /* size of the next packet that contains pages */ + uint32_t next_packet_size; /* global number of generated multifd packets */ uint64_t packet_num; /* thread local variables */ @@ -609,6 +690,8 @@ typedef struct { /* global number of generated multifd packets */ uint64_t packet_num; /* thread local variables */ + /* size of the next packet that contains pages */ + uint32_t next_packet_size; /* packets sent through this channel */ uint64_t num_packets; /* pages sent through this channel */ @@ -644,8 +727,8 @@ static int multifd_recv_initial_packet(QIOChannel *c, Error **errp) return -1; } - be32_to_cpus(&msg.magic); - be32_to_cpus(&msg.version); + msg.magic = be32_to_cpu(msg.magic); + msg.version = be32_to_cpu(msg.version); if (msg.magic != MULTIFD_MAGIC) { error_setg(errp, "multifd: received packet magic %x " @@ -706,13 +789,15 @@ static void multifd_pages_clear(MultiFDPages_t *pages) static void multifd_send_fill_packet(MultiFDSendParams *p) { MultiFDPacket_t *packet = p->packet; + uint32_t page_max = MULTIFD_PACKET_SIZE / qemu_target_page_size(); int i; packet->magic = cpu_to_be32(MULTIFD_MAGIC); packet->version = cpu_to_be32(MULTIFD_VERSION); packet->flags = cpu_to_be32(p->flags); - packet->size = cpu_to_be32(migrate_multifd_page_count()); - packet->used = cpu_to_be32(p->pages->used); + packet->pages_alloc = cpu_to_be32(page_max); + packet->pages_used = cpu_to_be32(p->pages->used); + packet->next_packet_size = cpu_to_be32(p->next_packet_size); packet->packet_num = cpu_to_be64(p->packet_num); if (p->pages->block) { @@ -727,10 +812,11 @@ static void multifd_send_fill_packet(MultiFDSendParams *p) static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) { MultiFDPacket_t *packet = p->packet; + uint32_t pages_max = MULTIFD_PACKET_SIZE / qemu_target_page_size(); RAMBlock *block; int i; - be32_to_cpus(&packet->magic); + packet->magic = be32_to_cpu(packet->magic); if (packet->magic != MULTIFD_MAGIC) { error_setg(errp, "multifd: received packet " "magic %x and expected magic %x", @@ -738,7 +824,7 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) return -1; } - be32_to_cpus(&packet->version); + packet->version = be32_to_cpu(packet->version); if (packet->version != MULTIFD_VERSION) { error_setg(errp, "multifd: received packet " "version %d and expected version %d", @@ -748,22 +834,35 @@ static int multifd_recv_unfill_packet(MultiFDRecvParams *p, Error **errp) p->flags = be32_to_cpu(packet->flags); - be32_to_cpus(&packet->size); - if (packet->size > migrate_multifd_page_count()) { + packet->pages_alloc = be32_to_cpu(packet->pages_alloc); + /* + * If we recevied a packet that is 100 times bigger than expected + * just stop migration. It is a magic number. + */ + if (packet->pages_alloc > pages_max * 100) { error_setg(errp, "multifd: received packet " - "with size %d and expected maximum size %d", - packet->size, migrate_multifd_page_count()) ; - return -1; - } - - p->pages->used = be32_to_cpu(packet->used); - if (p->pages->used > packet->size) { - error_setg(errp, "multifd: received packet " - "with size %d and expected maximum size %d", - p->pages->used, packet->size) ; + "with size %d and expected a maximum size of %d", + packet->pages_alloc, pages_max * 100) ; + return -1; + } + /* + * We received a packet that is bigger than expected but inside + * reasonable limits (see previous comment). Just reallocate. + */ + if (packet->pages_alloc > p->pages->allocated) { + multifd_pages_clear(p->pages); + p->pages = multifd_pages_init(packet->pages_alloc); + } + + p->pages->used = be32_to_cpu(packet->pages_used); + if (p->pages->used > packet->pages_alloc) { + error_setg(errp, "multifd: received packet " + "with %d pages and expected maximum pages are %d", + p->pages->used, packet->pages_alloc) ; return -1; } + p->next_packet_size = be32_to_cpu(packet->next_packet_size); p->packet_num = be64_to_cpu(packet->packet_num); if (p->pages->used) { @@ -910,13 +1009,12 @@ static void multifd_send_terminate_threads(Error *err) } } -int multifd_save_cleanup(Error **errp) +void multifd_save_cleanup(void) { int i; - int ret = 0; if (!migrate_use_multifd()) { - return 0; + return; } multifd_send_terminate_threads(NULL); for (i = 0; i < migrate_multifd_channels(); i++) { @@ -946,7 +1044,6 @@ int multifd_save_cleanup(Error **errp) multifd_send_state->pages = NULL; g_free(multifd_send_state); multifd_send_state = NULL; - return ret; } static void multifd_send_sync_main(void) @@ -988,6 +1085,7 @@ static void *multifd_send_thread(void *opaque) int ret; trace_multifd_send_thread_start(p->id); + rcu_register_thread(); if (multifd_send_initial_packet(p, &local_err) < 0) { goto out; @@ -1004,6 +1102,7 @@ static void *multifd_send_thread(void *opaque) uint64_t packet_num = p->packet_num; uint32_t flags = p->flags; + p->next_packet_size = used * qemu_target_page_size(); multifd_send_fill_packet(p); p->flags = 0; p->num_packets++; @@ -1011,7 +1110,8 @@ static void *multifd_send_thread(void *opaque) p->pages->used = 0; qemu_mutex_unlock(&p->mutex); - trace_multifd_send(p->id, packet_num, used, flags); + trace_multifd_send(p->id, packet_num, used, flags, + p->next_packet_size); ret = qio_channel_write_all(p->c, (void *)p->packet, p->packet_len, &local_err); @@ -1019,9 +1119,12 @@ static void *multifd_send_thread(void *opaque) break; } - ret = qio_channel_writev_all(p->c, p->pages->iov, used, &local_err); - if (ret != 0) { - break; + if (used) { + ret = qio_channel_writev_all(p->c, p->pages->iov, + used, &local_err); + if (ret != 0) { + break; + } } qemu_mutex_lock(&p->mutex); @@ -1050,6 +1153,7 @@ out: p->running = false; qemu_mutex_unlock(&p->mutex); + rcu_unregister_thread(); trace_multifd_send_thread_end(p->id, p->num_packets, p->num_pages); return NULL; @@ -1062,9 +1166,8 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) Error *local_err = NULL; if (qio_task_propagate_error(task, &local_err)) { - if (multifd_save_cleanup(&local_err) != 0) { - migrate_set_error(migrate_get_current(), local_err); - } + migrate_set_error(migrate_get_current(), local_err); + multifd_save_cleanup(); } else { p->c = QIO_CHANNEL(sioc); qio_channel_set_delay(p->c, false); @@ -1079,7 +1182,7 @@ static void multifd_new_send_channel_async(QIOTask *task, gpointer opaque) int multifd_save_setup(void) { int thread_count; - uint32_t page_count = migrate_multifd_page_count(); + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); uint8_t i; if (!migrate_use_multifd()) { @@ -1219,6 +1322,7 @@ static void *multifd_recv_thread(void *opaque) int ret; trace_multifd_recv_thread_start(p->id); + rcu_register_thread(); while (true) { uint32_t used; @@ -1242,14 +1346,18 @@ static void *multifd_recv_thread(void *opaque) used = p->pages->used; flags = p->flags; - trace_multifd_recv(p->id, p->packet_num, used, flags); + trace_multifd_recv(p->id, p->packet_num, used, flags, + p->next_packet_size); p->num_packets++; p->num_pages += used; qemu_mutex_unlock(&p->mutex); - ret = qio_channel_readv_all(p->c, p->pages->iov, used, &local_err); - if (ret != 0) { - break; + if (used) { + ret = qio_channel_readv_all(p->c, p->pages->iov, + used, &local_err); + if (ret != 0) { + break; + } } if (flags & MULTIFD_FLAG_SYNC) { @@ -1265,6 +1373,7 @@ static void *multifd_recv_thread(void *opaque) p->running = false; qemu_mutex_unlock(&p->mutex); + rcu_unregister_thread(); trace_multifd_recv_thread_end(p->id, p->num_packets, p->num_pages); return NULL; @@ -1273,7 +1382,7 @@ static void *multifd_recv_thread(void *opaque) int multifd_load_setup(void) { int thread_count; - uint32_t page_count = migrate_multifd_page_count(); + uint32_t page_count = MULTIFD_PACKET_SIZE / qemu_target_page_size(); uint8_t i; if (!migrate_use_multifd()) { @@ -1311,8 +1420,13 @@ bool multifd_recv_all_channels_created(void) return thread_count == atomic_read(&multifd_recv_state->count); } -/* Return true if multifd is ready for the migration, otherwise false */ -bool multifd_recv_new_channel(QIOChannel *ioc) +/* + * Try to receive all multifd channels to get ready for the migration. + * - Return true and do not set @errp when correctly receving all channels; + * - Return false and do not set @errp when correctly receiving the current one; + * - Return false and set @errp when failing to receive the current channel. + */ +bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp) { MultiFDRecvParams *p; Error *local_err = NULL; @@ -1321,6 +1435,10 @@ bool multifd_recv_new_channel(QIOChannel *ioc) id = multifd_recv_initial_packet(ioc, &local_err); if (id < 0) { multifd_recv_terminate_threads(local_err); + error_propagate_prepend(errp, local_err, + "failed to receive packet" + " via multifd channel %d: ", + atomic_read(&multifd_recv_state->count)); return false; } @@ -1329,6 +1447,7 @@ bool multifd_recv_new_channel(QIOChannel *ioc) error_setg(&local_err, "multifd: received id '%d' already setup'", id); multifd_recv_terminate_threads(local_err); + error_propagate(errp, local_err); return false; } p->c = ioc; @@ -1340,7 +1459,8 @@ bool multifd_recv_new_channel(QIOChannel *ioc) qemu_thread_create(&p->thread, p->name, multifd_recv_thread, p, QEMU_THREAD_JOINABLE); atomic_inc(&multifd_recv_state->count); - return multifd_recv_state->count == migrate_multifd_channels(); + return atomic_read(&multifd_recv_state->count) == + migrate_multifd_channels(); } /** @@ -1390,13 +1510,15 @@ static void mig_throttle_guest_down(void) MigrationState *s = migrate_get_current(); uint64_t pct_initial = s->parameters.cpu_throttle_initial; uint64_t pct_icrement = s->parameters.cpu_throttle_increment; + int pct_max = s->parameters.max_cpu_throttle; /* 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); + cpu_throttle_set(MIN(cpu_throttle_get_percentage() + pct_icrement, + pct_max)); } } @@ -1524,11 +1646,15 @@ unsigned long migration_bitmap_find_dirty(RAMState *rs, RAMBlock *rb, unsigned long *bitmap = rb->bmap; unsigned long next; - if (!qemu_ram_is_migratable(rb)) { + if (ramblock_is_ignored(rb)) { return size; } - if (rs->ram_bulk_stage && start > 0) { + /* + * When the free page optimization is enabled, we need to check the bitmap + * to send the non-free pages rather than all the pages in the bulk stage. + */ + if (!rs->fpo_enabled && rs->ram_bulk_stage && start > 0) { next = start + 1; } else { next = find_next_bit(bitmap, size, start); @@ -1543,11 +1669,14 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, { bool ret; + qemu_mutex_lock(&rs->bitmap_mutex); ret = test_and_clear_bit(page, rb->bmap); if (ret) { rs->migration_dirty_pages--; } + qemu_mutex_unlock(&rs->bitmap_mutex); + return ret; } @@ -1573,30 +1702,57 @@ uint64_t ram_pagesize_summary(void) RAMBlock *block; uint64_t summary = 0; - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { summary |= block->page_size; } return summary; } +uint64_t ram_get_total_transferred_pages(void) +{ + return ram_counters.normal + ram_counters.duplicate + + compression_counters.pages + xbzrle_counters.pages; +} + static void migration_update_rates(RAMState *rs, int64_t end_time) { - uint64_t iter_count = rs->iterations - rs->iterations_prev; + uint64_t page_count = rs->target_page_count - rs->target_page_count_prev; + double compressed_size; /* calculate period counters */ ram_counters.dirty_pages_rate = rs->num_dirty_pages_period * 1000 / (end_time - rs->time_last_bitmap_sync); - if (!iter_count) { + if (!page_count) { return; } if (migrate_use_xbzrle()) { xbzrle_counters.cache_miss_rate = (double)(xbzrle_counters.cache_miss - - rs->xbzrle_cache_miss_prev) / iter_count; + rs->xbzrle_cache_miss_prev) / page_count; rs->xbzrle_cache_miss_prev = xbzrle_counters.cache_miss; } + + if (migrate_use_compression()) { + compression_counters.busy_rate = (double)(compression_counters.busy - + rs->compress_thread_busy_prev) / page_count; + rs->compress_thread_busy_prev = compression_counters.busy; + + compressed_size = compression_counters.compressed_size - + rs->compressed_size_prev; + if (compressed_size) { + double uncompressed_size = (compression_counters.pages - + rs->compress_pages_prev) * TARGET_PAGE_SIZE; + + /* Compression-Ratio = Uncompressed-size / Compressed-size */ + compression_counters.compression_rate = + uncompressed_size / compressed_size; + + rs->compress_pages_prev = compression_counters.pages; + rs->compressed_size_prev = compression_counters.compressed_size; + } + } } static void migration_bitmap_sync(RAMState *rs) @@ -1616,7 +1772,7 @@ static void migration_bitmap_sync(RAMState *rs) qemu_mutex_lock(&rs->bitmap_mutex); rcu_read_lock(); - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { migration_bitmap_sync_range(rs, block, 0, block->used_length); } ram_counters.remaining = ram_bytes_remaining(); @@ -1652,7 +1808,7 @@ static void migration_bitmap_sync(RAMState *rs) migration_update_rates(rs, end_time); - rs->iterations_prev = rs->iterations; + rs->target_page_count_prev = rs->target_page_count; /* reset period counters */ rs->time_last_bitmap_sync = end_time; @@ -1660,10 +1816,54 @@ static void migration_bitmap_sync(RAMState *rs) rs->bytes_xfer_prev = bytes_xfer_now; } if (migrate_use_events()) { - qapi_event_send_migration_pass(ram_counters.dirty_sync_count, NULL); + qapi_event_send_migration_pass(ram_counters.dirty_sync_count); } } +static void migration_bitmap_sync_precopy(RAMState *rs) +{ + Error *local_err = NULL; + + /* + * The current notifier usage is just an optimization to migration, so we + * don't stop the normal migration process in the error case. + */ + if (precopy_notify(PRECOPY_NOTIFY_BEFORE_BITMAP_SYNC, &local_err)) { + error_report_err(local_err); + } + + migration_bitmap_sync(rs); + + if (precopy_notify(PRECOPY_NOTIFY_AFTER_BITMAP_SYNC, &local_err)) { + error_report_err(local_err); + } +} + +/** + * save_zero_page_to_file: send the zero page to the file + * + * Returns the size of data written to the file, 0 means the page is not + * a zero page + * + * @rs: current RAM state + * @file: the file where the data is saved + * @block: block that contains the page we want to send + * @offset: offset inside the block for the page + */ +static int save_zero_page_to_file(RAMState *rs, QEMUFile *file, + RAMBlock *block, ram_addr_t offset) +{ + uint8_t *p = block->host + offset; + int len = 0; + + if (is_zero_range(p, TARGET_PAGE_SIZE)) { + len += save_page_header(rs, file, block, offset | RAM_SAVE_FLAG_ZERO); + qemu_put_byte(file, 0); + len += 1; + } + return len; +} + /** * save_zero_page: send the zero page to the stream * @@ -1675,19 +1875,14 @@ static void migration_bitmap_sync(RAMState *rs) */ static int save_zero_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) { - uint8_t *p = block->host + offset; - int pages = -1; + int len = save_zero_page_to_file(rs, rs->f, block, offset); - if (is_zero_range(p, TARGET_PAGE_SIZE)) { + if (len) { ram_counters.duplicate++; - ram_counters.transferred += - save_page_header(rs, rs->f, block, offset | RAM_SAVE_FLAG_ZERO); - qemu_put_byte(rs->f, 0); - ram_counters.transferred += 1; - pages = 1; + ram_counters.transferred += len; + return 1; } - - return pages; + return -1; } static void ram_release_pages(const char *rbname, uint64_t offset, int pages) @@ -1822,15 +2017,20 @@ static int ram_save_multifd_page(RAMState *rs, RAMBlock *block, return 1; } -static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, - ram_addr_t offset, uint8_t *source_buf) +static bool do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, + ram_addr_t offset, uint8_t *source_buf) { RAMState *rs = ram_state; - int bytes_sent, blen; uint8_t *p = block->host + (offset & TARGET_PAGE_MASK); + bool zero_page = false; + int ret; - bytes_sent = save_page_header(rs, f, block, offset | - RAM_SAVE_FLAG_COMPRESS_PAGE); + if (save_zero_page_to_file(rs, f, block, offset)) { + zero_page = true; + goto exit; + } + + save_page_header(rs, f, block, offset | RAM_SAVE_FLAG_COMPRESS_PAGE); /* * copy it to a internal buffer to avoid it being modified by VM @@ -1838,24 +2038,40 @@ static int do_compress_ram_page(QEMUFile *f, z_stream *stream, RAMBlock *block, * decompression */ memcpy(source_buf, p, TARGET_PAGE_SIZE); - blen = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); - if (blen < 0) { - bytes_sent = 0; - qemu_file_set_error(migrate_get_current()->to_dst_file, blen); + ret = qemu_put_compression_data(f, stream, source_buf, TARGET_PAGE_SIZE); + if (ret < 0) { + qemu_file_set_error(migrate_get_current()->to_dst_file, ret); error_report("compressed data failed!"); - } else { - bytes_sent += blen; - ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1); + return false; } - return bytes_sent; +exit: + ram_release_pages(block->idstr, offset & TARGET_PAGE_MASK, 1); + return zero_page; } +static void +update_compress_thread_counts(const CompressParam *param, int bytes_xmit) +{ + ram_counters.transferred += bytes_xmit; + + if (param->zero_page) { + ram_counters.duplicate++; + return; + } + + /* 8 means a header with RAM_SAVE_FLAG_CONTINUE. */ + compression_counters.compressed_size += bytes_xmit - 8; + compression_counters.pages++; +} + +static bool save_page_use_compression(RAMState *rs); + static void flush_compressed_data(RAMState *rs) { int idx, len, thread_count; - if (!migrate_use_compression()) { + if (!save_page_use_compression(rs)) { return; } thread_count = migrate_compress_threads(); @@ -1872,7 +2088,12 @@ static void flush_compressed_data(RAMState *rs) qemu_mutex_lock(&comp_param[idx].mutex); if (!comp_param[idx].quit) { len = qemu_put_qemu_file(rs->f, comp_param[idx].file); - ram_counters.transferred += len; + /* + * it's safe to fetch zero_page without holding comp_done_lock + * as there is no further request submitted to the thread, + * i.e, the thread should be waiting for a request at this point. + */ + update_compress_thread_counts(&comp_param[idx], len); } qemu_mutex_unlock(&comp_param[idx].mutex); } @@ -1889,30 +2110,33 @@ static int compress_page_with_multi_thread(RAMState *rs, RAMBlock *block, ram_addr_t offset) { int idx, thread_count, bytes_xmit = -1, pages = -1; + bool wait = migrate_compress_wait_thread(); thread_count = migrate_compress_threads(); qemu_mutex_lock(&comp_done_lock); - while (true) { - for (idx = 0; idx < thread_count; idx++) { - if (comp_param[idx].done) { - comp_param[idx].done = false; - bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file); - qemu_mutex_lock(&comp_param[idx].mutex); - set_compress_params(&comp_param[idx], block, offset); - qemu_cond_signal(&comp_param[idx].cond); - qemu_mutex_unlock(&comp_param[idx].mutex); - pages = 1; - ram_counters.normal++; - ram_counters.transferred += bytes_xmit; - break; - } - } - if (pages > 0) { +retry: + for (idx = 0; idx < thread_count; idx++) { + if (comp_param[idx].done) { + comp_param[idx].done = false; + bytes_xmit = qemu_put_qemu_file(rs->f, comp_param[idx].file); + qemu_mutex_lock(&comp_param[idx].mutex); + set_compress_params(&comp_param[idx], block, offset); + qemu_cond_signal(&comp_param[idx].cond); + qemu_mutex_unlock(&comp_param[idx].mutex); + pages = 1; + update_compress_thread_counts(&comp_param[idx], bytes_xmit); break; - } else { - qemu_cond_wait(&comp_done_cond, &comp_done_lock); } } + + /* + * wait for the free thread if the user specifies 'compress-wait-thread', + * otherwise we will post the page out in the main thread as normal page. + */ + if (pages < 0 && wait) { + qemu_cond_wait(&comp_done_cond, &comp_done_lock); + goto retry; + } qemu_mutex_unlock(&comp_done_lock); return pages; @@ -1945,17 +2169,22 @@ static bool find_dirty_block(RAMState *rs, PageSearchStatus *pss, bool *again) pss->page = 0; pss->block = QLIST_NEXT_RCU(pss->block, next); if (!pss->block) { + /* + * If memory migration starts over, we will meet a dirtied page + * which may still exists in compression threads's ring, so we + * should flush the compressed data to make sure the new page + * is not overwritten by the old one in the destination. + * + * Also If xbzrle is on, stop using the data compression at this + * point. In theory, xbzrle can do better than compression. + */ + flush_compressed_data(rs); + /* Hit the end of the list */ pss->block = QLIST_FIRST_RCU(&ram_list.blocks); /* Flag that we've looped */ pss->complete_round = true; rs->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(rs); - } } /* Didn't find anything this time, but try again on the new block */ *again = true; @@ -1982,6 +2211,10 @@ static RAMBlock *unqueue_page(RAMState *rs, ram_addr_t *offset) { RAMBlock *block = NULL; + if (QSIMPLEQ_EMPTY_ATOMIC(&rs->src_page_requests)) { + return NULL; + } + qemu_mutex_lock(&rs->src_page_req_mutex); if (!QSIMPLEQ_EMPTY(&rs->src_page_requests)) { struct RAMSrcPageRequest *entry = @@ -2174,6 +2407,40 @@ static bool save_page_use_compression(RAMState *rs) return false; } +/* + * try to compress the page before posting it out, return true if the page + * has been properly handled by compression, otherwise needs other + * paths to handle it + */ +static bool save_compress_page(RAMState *rs, RAMBlock *block, ram_addr_t offset) +{ + if (!save_page_use_compression(rs)) { + return false; + } + + /* + * When starting the process of a new block, the first page of + * the block should be sent out before other pages in the same + * block, and all the pages in last block should have been sent + * out, keeping this order is important, because the 'cont' flag + * is used to avoid resending the block name. + * + * We post the fist page as normal page as compression will take + * much CPU resource. + */ + if (block != rs->last_sent_block) { + flush_compressed_data(rs); + return false; + } + + if (compress_page_with_multi_thread(rs, block, offset) > 0) { + return true; + } + + compression_counters.busy++; + return false; +} + /** * ram_save_target_page: save one target page * @@ -2194,15 +2461,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, return res; } - /* - * When starting the process of a new block, the first page of - * the block should be sent out before other pages in the same - * block, and all the pages in last block should have been sent - * out, keeping this order is important, because the 'cont' flag - * is used to avoid resending the block name. - */ - if (block != rs->last_sent_block && save_page_use_compression(rs)) { - flush_compressed_data(rs); + if (save_compress_page(rs, block, offset)) { + return 1; } res = save_zero_page(rs, block, offset); @@ -2220,14 +2480,10 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss, } /* - * Make sure the first page is sent out before other pages. - * - * we post it as normal page as compression will take much - * CPU resource. + * do not use multifd for compression as the first page in the new + * block should be posted out before sending the compressed page */ - if (block == rs->last_sent_block && save_page_use_compression(rs)) { - return compress_page_with_multi_thread(rs, block, offset); - } else if (migrate_use_multifd()) { + if (!save_page_use_compression(rs) && migrate_use_multifd()) { return ram_save_multifd_page(rs, block, offset); } @@ -2259,7 +2515,7 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, size_t pagesize_bits = qemu_ram_pagesize(pss->block) >> TARGET_PAGE_BITS; - if (!qemu_ram_is_migratable(pss->block)) { + if (ramblock_is_ignored(pss->block)) { error_report("block %s should not be migrated !", pss->block->idstr); return 0; } @@ -2295,7 +2551,8 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss, * * Called within an RCU critical section. * - * Returns the number of pages written where zero means no dirty pages + * Returns the number of pages written where zero means no dirty pages, + * or negative on error * * @rs: current RAM state * @last_stage: if we are at the completion stage @@ -2356,19 +2613,30 @@ void acct_update_position(QEMUFile *f, size_t size, bool zero) } } -uint64_t ram_bytes_total(void) +static uint64_t ram_bytes_total_common(bool count_ignored) { RAMBlock *block; uint64_t total = 0; rcu_read_lock(); - RAMBLOCK_FOREACH_MIGRATABLE(block) { - total += block->used_length; + if (count_ignored) { + RAMBLOCK_FOREACH_MIGRATABLE(block) { + total += block->used_length; + } + } else { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + total += block->used_length; + } } rcu_read_unlock(); return total; } +uint64_t ram_bytes_total(void) +{ + return ram_bytes_total_common(false); +} + static void xbzrle_load_setup(void) { XBZRLE.decoded_buf = g_malloc(TARGET_PAGE_SIZE); @@ -2417,7 +2685,7 @@ static void ram_save_cleanup(void *opaque) */ memory_global_dirty_log_stop(); - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { g_free(block->bmap); block->bmap = NULL; g_free(block->unsentmap); @@ -2436,6 +2704,7 @@ static void ram_state_reset(RAMState *rs) rs->last_page = 0; rs->last_version = ram_list.version; rs->ram_bulk_stage = true; + rs->fpo_enabled = false; } #define MAX_WAIT 50 /* ms, half buffered_file limit */ @@ -2480,7 +2749,7 @@ void ram_postcopy_migrated_memory_release(MigrationState *ms) { struct RAMBlock *block; - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { unsigned long *bitmap = block->bmap; unsigned long range = block->used_length >> TARGET_PAGE_BITS; unsigned long run_start = find_next_zero_bit(bitmap, range, 0); @@ -2558,7 +2827,7 @@ static int postcopy_each_ram_send_discard(MigrationState *ms) struct RAMBlock *block; int ret; - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { PostcopyDiscardState *pds = postcopy_discard_send_init(ms, block->idstr); @@ -2766,7 +3035,7 @@ int ram_postcopy_send_discard_bitmap(MigrationState *ms) rs->last_sent_block = NULL; rs->last_page = 0; - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { unsigned long pages = block->used_length >> TARGET_PAGE_BITS; unsigned long *bitmap = block->bmap; unsigned long *unsentmap = block->unsentmap; @@ -2932,7 +3201,7 @@ static void ram_list_init_bitmaps(void) /* Skip setting bitmap if there is no RAM */ if (ram_bytes_total()) { - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { pages = block->max_length >> TARGET_PAGE_BITS; block->bmap = bitmap_new(pages); bitmap_set(block->bmap, 0, pages); @@ -2953,7 +3222,7 @@ static void ram_init_bitmaps(RAMState *rs) ram_list_init_bitmaps(); memory_global_dirty_log_start(); - migration_bitmap_sync(rs); + migration_bitmap_sync_precopy(rs); rcu_read_unlock(); qemu_mutex_unlock_ramlist(); @@ -2987,7 +3256,7 @@ static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) * about dirty page logging as well. */ - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { pages += bitmap_count_one(block->bmap, block->used_length >> TARGET_PAGE_BITS); } @@ -3011,6 +3280,53 @@ static void ram_state_resume_prepare(RAMState *rs, QEMUFile *out) trace_ram_state_resume_prepare(pages); } +/* + * This function clears bits of the free pages reported by the caller from the + * migration dirty bitmap. @addr is the host address corresponding to the + * start of the continuous guest free pages, and @len is the total bytes of + * those pages. + */ +void qemu_guest_free_page_hint(void *addr, size_t len) +{ + RAMBlock *block; + ram_addr_t offset; + size_t used_len, start, npages; + MigrationState *s = migrate_get_current(); + + /* This function is currently expected to be used during live migration */ + if (!migration_is_setup_or_active(s->state)) { + return; + } + + for (; len > 0; len -= used_len, addr += used_len) { + block = qemu_ram_block_from_host(addr, false, &offset); + if (unlikely(!block || offset >= block->used_length)) { + /* + * The implementation might not support RAMBlock resize during + * live migration, but it could happen in theory with future + * updates. So we add a check here to capture that case. + */ + error_report_once("%s unexpected error", __func__); + return; + } + + if (len <= block->used_length - offset) { + used_len = len; + } else { + used_len = block->used_length - offset; + } + + start = offset >> TARGET_PAGE_BITS; + npages = used_len >> TARGET_PAGE_BITS; + + qemu_mutex_lock(&ram_state->bitmap_mutex); + ram_state->migration_dirty_pages -= + bitmap_count_one_with_offset(block->bmap, start, npages); + bitmap_clear(block->bmap, start, npages); + qemu_mutex_unlock(&ram_state->bitmap_mutex); + } +} + /* * Each of ram_save_setup, ram_save_iterate and ram_save_complete has * long-running RCU critical section. When rcu-reclaims in the code @@ -3046,7 +3362,7 @@ static int ram_save_setup(QEMUFile *f, void *opaque) rcu_read_lock(); - qemu_put_be64(f, ram_bytes_total() | RAM_SAVE_FLAG_MEM_SIZE); + qemu_put_be64(f, ram_bytes_total_common(true) | RAM_SAVE_FLAG_MEM_SIZE); RAMBLOCK_FOREACH_MIGRATABLE(block) { qemu_put_byte(f, strlen(block->idstr)); @@ -3055,6 +3371,10 @@ static int ram_save_setup(QEMUFile *f, void *opaque) if (migrate_postcopy_ram() && block->page_size != qemu_host_page_size) { qemu_put_be64(f, block->page_size); } + if (migrate_ignore_shared()) { + qemu_put_be64(f, block->mr->addr); + qemu_put_byte(f, ramblock_is_ignored(block) ? 1 : 0); + } } rcu_read_unlock(); @@ -3119,7 +3439,13 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) done = 1; break; } - rs->iterations++; + + if (pages < 0) { + qemu_file_set_error(f, pages); + break; + } + + rs->target_page_count += pages; /* we want to check in the 1st loop, just in case it was the 1st time and we had to sync the dirty bitmap. @@ -3135,7 +3461,6 @@ static int ram_save_iterate(QEMUFile *f, void *opaque) } i++; } - flush_compressed_data(rs); rcu_read_unlock(); /* @@ -3161,7 +3486,7 @@ out: /** * ram_save_complete: function called to send the remaining amount of ram * - * Returns zero to indicate success + * Returns zero to indicate success or negative on error * * Called with iothread lock * @@ -3172,11 +3497,12 @@ static int ram_save_complete(QEMUFile *f, void *opaque) { RAMState **temp = opaque; RAMState *rs = *temp; + int ret = 0; rcu_read_lock(); if (!migration_in_postcopy()) { - migration_bitmap_sync(rs); + migration_bitmap_sync_precopy(rs); } ram_control_before_iterate(f, RAM_CONTROL_FINISH); @@ -3192,6 +3518,10 @@ static int ram_save_complete(QEMUFile *f, void *opaque) if (pages == 0) { break; } + if (pages < 0) { + ret = pages; + break; + } } flush_compressed_data(rs); @@ -3203,7 +3533,7 @@ static int ram_save_complete(QEMUFile *f, void *opaque) qemu_put_be64(f, RAM_SAVE_FLAG_EOS); qemu_fflush(f); - return 0; + return ret; } static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, @@ -3221,7 +3551,7 @@ static void ram_save_pending(QEMUFile *f, void *opaque, uint64_t max_size, remaining_size < max_size) { qemu_mutex_lock_iothread(); rcu_read_lock(); - migration_bitmap_sync(rs); + migration_bitmap_sync_precopy(rs); rcu_read_unlock(); qemu_mutex_unlock_iothread(); remaining_size = rs->migration_dirty_pages * TARGET_PAGE_SIZE; @@ -3303,7 +3633,7 @@ static inline RAMBlock *ram_block_from_stream(QEMUFile *f, int flags) return NULL; } - if (!qemu_ram_is_migratable(block)) { + if (ramblock_is_ignored(block)) { error_report("block %s should not be migrated !", id); return NULL; } @@ -3321,6 +3651,29 @@ static inline void *host_from_ram_block_offset(RAMBlock *block, return block->host + offset; } +static inline void *colo_cache_from_block_offset(RAMBlock *block, + ram_addr_t offset) +{ + if (!offset_in_ramblock(block, offset)) { + return NULL; + } + if (!block->colo_cache) { + error_report("%s: colo_cache is NULL in block :%s", + __func__, block->idstr); + return NULL; + } + + /* + * During colo checkpoint, we need bitmap of these migrated pages. + * It help us to decide which pages in ram cache should be flushed + * into VM's RAM later. + */ + if (!test_and_set_bit(offset >> TARGET_PAGE_BITS, block->bmap)) { + ram_state->migration_dirty_pages++; + } + return block->colo_cache + offset; +} + /** * ram_handle_compressed: handle the zero page case * @@ -3525,6 +3878,90 @@ static void decompress_data_with_multi_threads(QEMUFile *f, qemu_mutex_unlock(&decomp_done_lock); } +/* + * colo cache: this is for secondary VM, we cache the whole + * memory of the secondary VM, it is need to hold the global lock + * to call this helper. + */ +int colo_init_ram_cache(void) +{ + RAMBlock *block; + + rcu_read_lock(); + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + block->colo_cache = qemu_anon_ram_alloc(block->used_length, + NULL, + false); + if (!block->colo_cache) { + error_report("%s: Can't alloc memory for COLO cache of block %s," + "size 0x" RAM_ADDR_FMT, __func__, block->idstr, + block->used_length); + goto out_locked; + } + memcpy(block->colo_cache, block->host, block->used_length); + } + rcu_read_unlock(); + /* + * Record the dirty pages that sent by PVM, we use this dirty bitmap together + * with to decide which page in cache should be flushed into SVM's RAM. Here + * we use the same name 'ram_bitmap' as for migration. + */ + if (ram_bytes_total()) { + RAMBlock *block; + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + unsigned long pages = block->max_length >> TARGET_PAGE_BITS; + + block->bmap = bitmap_new(pages); + bitmap_set(block->bmap, 0, pages); + } + } + ram_state = g_new0(RAMState, 1); + ram_state->migration_dirty_pages = 0; + qemu_mutex_init(&ram_state->bitmap_mutex); + memory_global_dirty_log_start(); + + return 0; + +out_locked: + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if (block->colo_cache) { + qemu_anon_ram_free(block->colo_cache, block->used_length); + block->colo_cache = NULL; + } + } + + rcu_read_unlock(); + return -errno; +} + +/* It is need to hold the global lock to call this helper */ +void colo_release_ram_cache(void) +{ + RAMBlock *block; + + memory_global_dirty_log_stop(); + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + g_free(block->bmap); + block->bmap = NULL; + } + + rcu_read_lock(); + + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + if (block->colo_cache) { + qemu_anon_ram_free(block->colo_cache, block->used_length); + block->colo_cache = NULL; + } + } + + rcu_read_unlock(); + qemu_mutex_destroy(&ram_state->bitmap_mutex); + g_free(ram_state); + ram_state = NULL; +} + /** * ram_load_setup: Setup RAM for migration incoming side * @@ -3541,19 +3978,28 @@ static int ram_load_setup(QEMUFile *f, void *opaque) xbzrle_load_setup(); ramblock_recv_map_init(); + return 0; } static int ram_load_cleanup(void *opaque) { RAMBlock *rb; + + RAMBLOCK_FOREACH_NOT_IGNORED(rb) { + if (ramblock_is_pmem(rb)) { + pmem_persist(rb->host, rb->used_length); + } + } + xbzrle_load_cleanup(); compress_threads_load_cleanup(); - RAMBLOCK_FOREACH_MIGRATABLE(rb) { + RAMBLOCK_FOREACH_NOT_IGNORED(rb) { g_free(rb->receivedmap); rb->receivedmap = NULL; } + return 0; } @@ -3736,6 +4182,46 @@ static bool postcopy_is_running(void) return ps >= POSTCOPY_INCOMING_LISTENING && ps < POSTCOPY_INCOMING_END; } +/* + * Flush content of RAM cache into SVM's memory. + * Only flush the pages that be dirtied by PVM or SVM or both. + */ +static void colo_flush_ram_cache(void) +{ + RAMBlock *block = NULL; + void *dst_host; + void *src_host; + unsigned long offset = 0; + + memory_global_dirty_log_sync(); + rcu_read_lock(); + RAMBLOCK_FOREACH_NOT_IGNORED(block) { + migration_bitmap_sync_range(ram_state, block, 0, block->used_length); + } + rcu_read_unlock(); + + trace_colo_flush_ram_cache_begin(ram_state->migration_dirty_pages); + rcu_read_lock(); + block = QLIST_FIRST_RCU(&ram_list.blocks); + + while (block) { + offset = migration_bitmap_find_dirty(ram_state, block, offset); + + if (offset << TARGET_PAGE_BITS >= block->used_length) { + offset = 0; + block = QLIST_NEXT_RCU(block, next); + } else { + migration_bitmap_clear_dirty(ram_state, block, offset); + dst_host = block->host + (offset << TARGET_PAGE_BITS); + src_host = block->colo_cache + (offset << TARGET_PAGE_BITS); + memcpy(dst_host, src_host, TARGET_PAGE_SIZE); + } + } + + rcu_read_unlock(); + trace_colo_flush_ram_cache_end(); +} + static int ram_load(QEMUFile *f, void *opaque, int version_id) { int flags = 0, ret = 0, invalid_flags = 0; @@ -3791,13 +4277,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) 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); + /* + * After going into COLO, we should load the Page into colo_cache. + */ + if (migration_incoming_in_colo_state()) { + host = colo_cache_from_block_offset(block, addr); + } else { + host = host_from_ram_block_offset(block, addr); + } if (!host) { error_report("Illegal RAM offset " RAM_ADDR_FMT, addr); ret = -EINVAL; break; } - ramblock_recv_bitmap_set(block, host); + + if (!migration_incoming_in_colo_state()) { + ramblock_recv_bitmap_set(block, host); + } + trace_ram_load_loop(block->idstr, (uint64_t)addr, flags, host); } @@ -3841,6 +4338,23 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret = -EINVAL; } } + if (migrate_ignore_shared()) { + hwaddr addr = qemu_get_be64(f); + bool ignored = qemu_get_byte(f); + if (ignored != ramblock_is_ignored(block)) { + error_report("RAM block %s should %s be migrated", + id, ignored ? "" : "not"); + ret = -EINVAL; + } + if (ramblock_is_ignored(block) && + block->mr->addr != addr) { + error_report("Mismatched GPAs for block %s " + "%" PRId64 "!= %" PRId64, + id, (uint64_t)addr, + (uint64_t)block->mr->addr); + ret = -EINVAL; + } + } ram_control_load_hook(f, RAM_CONTROL_BLOCK_REG, block->idstr); } else { @@ -3901,11 +4415,24 @@ static int ram_load(QEMUFile *f, void *opaque, int version_id) ret |= wait_for_decompress_done(); rcu_read_unlock(); trace_ram_load_complete(ret, seq_iter); + + if (!ret && migration_incoming_in_colo_state()) { + colo_flush_ram_cache(); + } return ret; } static bool ram_has_postcopy(void *opaque) { + RAMBlock *rb; + RAMBLOCK_FOREACH_NOT_IGNORED(rb) { + if (ramblock_is_pmem(rb)) { + info_report("Block: %s, host: %p is a nvdimm memory, postcopy" + "is not supported now!", rb->idstr, rb->host); + return false; + } + } + return migrate_postcopy_ram(); } @@ -3918,7 +4445,7 @@ static int ram_dirty_bitmap_sync_all(MigrationState *s, RAMState *rs) trace_ram_dirty_bitmap_sync_start(); - RAMBLOCK_FOREACH_MIGRATABLE(block) { + RAMBLOCK_FOREACH_NOT_IGNORED(block) { qemu_savevm_send_recv_bitmap(file, block->idstr); trace_ram_dirty_bitmap_request(block->idstr); ramblock_count++; diff --git a/migration/ram.h b/migration/ram.h index 457bf54b8c..936177b3e9 100644 --- a/migration/ram.h +++ b/migration/ram.h @@ -36,17 +36,18 @@ extern MigrationStats ram_counters; extern XBZRLECacheStats xbzrle_counters; +extern CompressionStats compression_counters; int xbzrle_cache_resize(int64_t new_size, Error **errp); uint64_t ram_bytes_remaining(void); uint64_t ram_bytes_total(void); int multifd_save_setup(void); -int multifd_save_cleanup(Error **errp); +void multifd_save_cleanup(void); int multifd_load_setup(void); int multifd_load_cleanup(Error **errp); bool multifd_recv_all_channels_created(void); -bool multifd_recv_new_channel(QIOChannel *ioc); +bool multifd_recv_new_channel(QIOChannel *ioc, Error **errp); uint64_t ram_pagesize_summary(void); int ram_save_queue_pages(const char *rbname, ram_addr_t start, ram_addr_t len); @@ -70,4 +71,8 @@ int64_t ramblock_recv_bitmap_send(QEMUFile *file, const char *block_name); int ram_dirty_bitmap_reload(MigrationState *s, RAMBlock *rb); +/* ram cache */ +int colo_init_ram_cache(void); +void colo_release_ram_cache(void); + #endif diff --git a/migration/rdma.c b/migration/rdma.c index 8bd7159059..c1bcece53b 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -86,6 +86,7 @@ static uint32_t known_capabilities = RDMA_CAPABILITY_PIN_ALL; " to abort!"); \ rdma->error_reported = 1; \ } \ + rcu_read_unlock(); \ return rdma->error_state; \ } \ } while (0) @@ -387,6 +388,10 @@ typedef struct RDMAContext { uint64_t unregistrations[RDMA_SIGNALED_SEND_MAX]; GHashTable *blockmap; + + /* the RDMAContext for return path */ + struct RDMAContext *return_path; + bool is_return_path; } RDMAContext; #define TYPE_QIO_CHANNEL_RDMA "qio-channel-rdma" @@ -398,7 +403,8 @@ typedef struct QIOChannelRDMA QIOChannelRDMA; struct QIOChannelRDMA { QIOChannel parent; - RDMAContext *rdma; + RDMAContext *rdmain; + RDMAContext *rdmaout; QEMUFile *file; bool blocking; /* XXX we don't actually honour this yet */ }; @@ -618,9 +624,12 @@ static int rdma_add_block(RDMAContext *rdma, const char *block_name, * in advanced before the migration starts. This tells us where the RAM blocks * are so that we can register them individually. */ -static int qemu_rdma_init_one_block(const char *block_name, void *host_addr, - ram_addr_t block_offset, ram_addr_t length, void *opaque) +static int qemu_rdma_init_one_block(RAMBlock *rb, void *opaque) { + const char *block_name = qemu_ram_get_idstr(rb); + void *host_addr = qemu_ram_get_host_addr(rb); + ram_addr_t block_offset = qemu_ram_get_offset(rb); + ram_addr_t length = qemu_ram_get_used_length(rb); return rdma_add_block(opaque, block_name, host_addr, block_offset, length); } @@ -632,10 +641,14 @@ static int qemu_rdma_init_one_block(const char *block_name, void *host_addr, static int qemu_rdma_init_ram_blocks(RDMAContext *rdma) { RDMALocalBlocks *local = &rdma->local_ram_blocks; + int ret; assert(rdma->blockmap == NULL); memset(local, 0, sizeof *local); - qemu_ram_foreach_migratable_block(qemu_rdma_init_one_block, rdma); + ret = foreach_not_ignored_block(qemu_rdma_init_one_block, rdma); + if (ret) { + return ret; + } trace_qemu_rdma_init_ram_blocks(local->nb_blocks); rdma->dest_blocks = g_new0(RDMADestBlock, rdma->local_ram_blocks.nb_blocks); @@ -1483,27 +1496,56 @@ static uint64_t qemu_rdma_poll(RDMAContext *rdma, uint64_t *wr_id_out, */ static int qemu_rdma_wait_comp_channel(RDMAContext *rdma) { + struct rdma_cm_event *cm_event; + int ret = -1; + /* * Coroutine doesn't start until migration_fd_process_incoming() * so don't yield unless we know we're running inside of a coroutine. */ - if (rdma->migration_started_on_destination) { + if (rdma->migration_started_on_destination && + migration_incoming_get_current()->state == MIGRATION_STATUS_ACTIVE) { yield_until_fd_readable(rdma->comp_channel->fd); } else { /* This is the source side, we're in a separate thread * or destination prior to migration_fd_process_incoming() + * after postcopy, the destination also in a seprate thread. * we can't yield; so we have to poll the fd. * But we need to be able to handle 'cancel' or an error * without hanging forever. */ while (!rdma->error_state && !rdma->received_error) { - GPollFD pfds[1]; + GPollFD pfds[2]; pfds[0].fd = rdma->comp_channel->fd; pfds[0].events = G_IO_IN | G_IO_HUP | G_IO_ERR; + pfds[0].revents = 0; + + pfds[1].fd = rdma->channel->fd; + pfds[1].events = G_IO_IN | G_IO_HUP | G_IO_ERR; + pfds[1].revents = 0; + /* 0.1s timeout, should be fine for a 'cancel' */ - switch (qemu_poll_ns(pfds, 1, 100 * 1000 * 1000)) { + switch (qemu_poll_ns(pfds, 2, 100 * 1000 * 1000)) { + case 2: case 1: /* fd active */ - return 0; + if (pfds[0].revents) { + return 0; + } + + if (pfds[1].revents) { + ret = rdma_get_cm_event(rdma->channel, &cm_event); + if (!ret) { + rdma_ack_cm_event(cm_event); + } + + error_report("receive cm event while wait comp channel," + "cm event is %d", cm_event->event); + if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED || + cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { + return -EPIPE; + } + } + break; case 0: /* Timeout, go around again */ break; @@ -2286,6 +2328,9 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma->connected = false; } + if (rdma->channel) { + qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL); + } g_free(rdma->dest_blocks); rdma->dest_blocks = NULL; @@ -2323,10 +2368,22 @@ static void qemu_rdma_cleanup(RDMAContext *rdma) rdma_destroy_id(rdma->cm_id); rdma->cm_id = NULL; } + + /* the destination side, listen_id and channel is shared */ if (rdma->listen_id) { - rdma_destroy_id(rdma->listen_id); + if (!rdma->is_return_path) { + rdma_destroy_id(rdma->listen_id); + } rdma->listen_id = NULL; + + if (rdma->channel) { + if (!rdma->is_return_path) { + rdma_destroy_event_channel(rdma->channel); + } + rdma->channel = NULL; + } } + if (rdma->channel) { rdma_destroy_event_channel(rdma->channel); rdma->channel = NULL; @@ -2555,6 +2612,25 @@ err_dest_init_create_listen_id: } +static void qemu_rdma_return_path_dest_init(RDMAContext *rdma_return_path, + RDMAContext *rdma) +{ + int idx; + + for (idx = 0; idx < RDMA_WRID_MAX; idx++) { + rdma_return_path->wr_data[idx].control_len = 0; + rdma_return_path->wr_data[idx].control_curr = NULL; + } + + /*the CM channel and CM id is shared*/ + rdma_return_path->channel = rdma->channel; + rdma_return_path->listen_id = rdma->listen_id; + + rdma->return_path = rdma_return_path; + rdma_return_path->return_path = rdma; + rdma_return_path->is_return_path = true; +} + static void *qemu_rdma_data_init(const char *host_port, Error **errp) { RDMAContext *rdma = NULL; @@ -2595,12 +2671,20 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); QEMUFile *f = rioc->file; - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; int ret; ssize_t done = 0; size_t i; size_t len = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); /* @@ -2610,6 +2694,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, ret = qemu_rdma_write_flush(f, rdma); if (ret < 0) { rdma->error_state = ret; + rcu_read_unlock(); return ret; } @@ -2629,6 +2714,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, if (ret < 0) { rdma->error_state = ret; + rcu_read_unlock(); return ret; } @@ -2637,6 +2723,7 @@ static ssize_t qio_channel_rdma_writev(QIOChannel *ioc, } } + rcu_read_unlock(); return done; } @@ -2670,12 +2757,20 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, Error **errp) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; RDMAControlHeader head; int ret = 0; ssize_t i; size_t done = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmain); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); for (i = 0; i < niov; i++) { @@ -2687,7 +2782,7 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, * were given and dish out the bytes until we run * out of bytes. */ - ret = qemu_rdma_fill(rioc->rdma, data, want, 0); + ret = qemu_rdma_fill(rdma, data, want, 0); done += ret; want -= ret; /* Got what we needed, so go to next iovec */ @@ -2709,25 +2804,28 @@ static ssize_t qio_channel_rdma_readv(QIOChannel *ioc, if (ret < 0) { rdma->error_state = ret; + rcu_read_unlock(); return ret; } /* * SEND was received with new bytes, now try again. */ - ret = qemu_rdma_fill(rioc->rdma, data, want, 0); + ret = qemu_rdma_fill(rdma, data, want, 0); done += ret; want -= ret; /* Still didn't get enough, so lets just return */ if (want) { if (done == 0) { + rcu_read_unlock(); return QIO_CHANNEL_ERR_BLOCK; } else { break; } } } + rcu_read_unlock(); return done; } @@ -2779,15 +2877,29 @@ qio_channel_rdma_source_prepare(GSource *source, gint *timeout) { QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source; - RDMAContext *rdma = rsource->rioc->rdma; + RDMAContext *rdma; GIOCondition cond = 0; *timeout = -1; + rcu_read_lock(); + if (rsource->condition == G_IO_IN) { + rdma = atomic_rcu_read(&rsource->rioc->rdmain); + } else { + rdma = atomic_rcu_read(&rsource->rioc->rdmaout); + } + + if (!rdma) { + error_report("RDMAContext is NULL when prepare Gsource"); + rcu_read_unlock(); + return FALSE; + } + if (rdma->wr_data[0].control_len) { cond |= G_IO_IN; } cond |= G_IO_OUT; + rcu_read_unlock(); return cond & rsource->condition; } @@ -2795,14 +2907,28 @@ static gboolean qio_channel_rdma_source_check(GSource *source) { QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source; - RDMAContext *rdma = rsource->rioc->rdma; + RDMAContext *rdma; GIOCondition cond = 0; + rcu_read_lock(); + if (rsource->condition == G_IO_IN) { + rdma = atomic_rcu_read(&rsource->rioc->rdmain); + } else { + rdma = atomic_rcu_read(&rsource->rioc->rdmaout); + } + + if (!rdma) { + error_report("RDMAContext is NULL when check Gsource"); + rcu_read_unlock(); + return FALSE; + } + if (rdma->wr_data[0].control_len) { cond |= G_IO_IN; } cond |= G_IO_OUT; + rcu_read_unlock(); return cond & rsource->condition; } @@ -2813,14 +2939,28 @@ qio_channel_rdma_source_dispatch(GSource *source, { QIOChannelFunc func = (QIOChannelFunc)callback; QIOChannelRDMASource *rsource = (QIOChannelRDMASource *)source; - RDMAContext *rdma = rsource->rioc->rdma; + RDMAContext *rdma; GIOCondition cond = 0; + rcu_read_lock(); + if (rsource->condition == G_IO_IN) { + rdma = atomic_rcu_read(&rsource->rioc->rdmain); + } else { + rdma = atomic_rcu_read(&rsource->rioc->rdmaout); + } + + if (!rdma) { + error_report("RDMAContext is NULL when dispatch Gsource"); + rcu_read_unlock(); + return FALSE; + } + if (rdma->wr_data[0].control_len) { cond |= G_IO_IN; } cond |= G_IO_OUT; + rcu_read_unlock(); return (*func)(QIO_CHANNEL(rsource->rioc), (cond & rsource->condition), user_data); @@ -2860,20 +3000,91 @@ static GSource *qio_channel_rdma_create_watch(QIOChannel *ioc, return source; } +static void qio_channel_rdma_set_aio_fd_handler(QIOChannel *ioc, + AioContext *ctx, + IOHandler *io_read, + IOHandler *io_write, + void *opaque) +{ + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); + if (io_read) { + aio_set_fd_handler(ctx, rioc->rdmain->comp_channel->fd, + false, io_read, io_write, NULL, opaque); + } else { + aio_set_fd_handler(ctx, rioc->rdmaout->comp_channel->fd, + false, io_read, io_write, NULL, opaque); + } +} static int qio_channel_rdma_close(QIOChannel *ioc, Error **errp) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); + RDMAContext *rdmain, *rdmaout; trace_qemu_rdma_close(); - if (rioc->rdma) { - if (!rioc->rdma->error_state) { - rioc->rdma->error_state = qemu_file_get_error(rioc->file); - } - qemu_rdma_cleanup(rioc->rdma); - g_free(rioc->rdma); - rioc->rdma = NULL; + + rdmain = rioc->rdmain; + if (rdmain) { + atomic_rcu_set(&rioc->rdmain, NULL); } + + rdmaout = rioc->rdmaout; + if (rdmaout) { + atomic_rcu_set(&rioc->rdmaout, NULL); + } + + synchronize_rcu(); + + if (rdmain) { + qemu_rdma_cleanup(rdmain); + } + + if (rdmaout) { + qemu_rdma_cleanup(rdmaout); + } + + g_free(rdmain); + g_free(rdmaout); + + return 0; +} + +static int +qio_channel_rdma_shutdown(QIOChannel *ioc, + QIOChannelShutdown how, + Error **errp) +{ + QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(ioc); + RDMAContext *rdmain, *rdmaout; + + rcu_read_lock(); + + rdmain = atomic_rcu_read(&rioc->rdmain); + rdmaout = atomic_rcu_read(&rioc->rdmain); + + switch (how) { + case QIO_CHANNEL_SHUTDOWN_READ: + if (rdmain) { + rdmain->error_state = -1; + } + break; + case QIO_CHANNEL_SHUTDOWN_WRITE: + if (rdmaout) { + rdmaout->error_state = -1; + } + break; + case QIO_CHANNEL_SHUTDOWN_BOTH: + default: + if (rdmain) { + rdmain->error_state = -1; + } + if (rdmaout) { + rdmaout->error_state = -1; + } + break; + } + + rcu_read_unlock(); return 0; } @@ -2916,11 +3127,24 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, size_t size, uint64_t *bytes_sent) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; int ret; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); + if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + rcu_read_unlock(); + return RAM_SAVE_CONTROL_NOT_SUPP; + } + qemu_fflush(f); if (size > 0) { @@ -3002,12 +3226,45 @@ static size_t qemu_rdma_save_page(QEMUFile *f, void *opaque, } } + rcu_read_unlock(); return RAM_SAVE_CONTROL_DELAYED; err: rdma->error_state = ret; + rcu_read_unlock(); return ret; } +static void rdma_accept_incoming_migration(void *opaque); + +static void rdma_cm_poll_handler(void *opaque) +{ + RDMAContext *rdma = opaque; + int ret; + struct rdma_cm_event *cm_event; + MigrationIncomingState *mis = migration_incoming_get_current(); + + ret = rdma_get_cm_event(rdma->channel, &cm_event); + if (ret) { + error_report("get_cm_event failed %d", errno); + return; + } + rdma_ack_cm_event(cm_event); + + if (cm_event->event == RDMA_CM_EVENT_DISCONNECTED || + cm_event->event == RDMA_CM_EVENT_DEVICE_REMOVAL) { + error_report("receive cm event, cm event is %d", cm_event->event); + rdma->error_state = -EPIPE; + if (rdma->return_path) { + rdma->return_path->error_state = -EPIPE; + } + + if (mis->migration_incoming_co) { + qemu_coroutine_enter(mis->migration_incoming_co); + } + return; + } +} + static int qemu_rdma_accept(RDMAContext *rdma) { RDMACapabilities cap; @@ -3102,7 +3359,15 @@ static int qemu_rdma_accept(RDMAContext *rdma) } } - qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL); + /* Accept the second connection request for return path */ + if (migrate_postcopy() && !rdma->is_return_path) { + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, + NULL, + (void *)(intptr_t)rdma->return_path); + } else { + qemu_set_fd_handler(rdma->channel->fd, rdma_cm_poll_handler, + NULL, rdma); + } ret = rdma_accept(rdma->cm_id, &conn_param); if (ret) { @@ -3171,8 +3436,8 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) RDMAControlHeader blocks = { .type = RDMA_CONTROL_RAM_BLOCKS_RESULT, .repeat = 1 }; QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; - RDMALocalBlocks *local = &rdma->local_ram_blocks; + RDMAContext *rdma; + RDMALocalBlocks *local; RDMAControlHeader head; RDMARegister *reg, *registers; RDMACompress *comp; @@ -3185,8 +3450,17 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) int count = 0; int i = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmain); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); + local = &rdma->local_ram_blocks; do { trace_qemu_rdma_registration_handle_wait(); @@ -3346,13 +3620,16 @@ static int qemu_rdma_registration_handle(QEMUFile *f, void *opaque) } chunk_start = ram_chunk_start(block, chunk); chunk_end = ram_chunk_end(block, chunk + reg->chunks); + /* avoid "-Waddress-of-packed-member" warning */ + uint32_t tmp_rkey = 0; if (qemu_rdma_register_and_get_keys(rdma, block, - (uintptr_t)host_addr, NULL, ®_result->rkey, + (uintptr_t)host_addr, NULL, &tmp_rkey, chunk, chunk_start, chunk_end)) { error_report("cannot get rkey"); ret = -EINVAL; goto out; } + reg_result->rkey = tmp_rkey; reg_result->host_addr = (uintptr_t)block->local_host_addr; @@ -3420,6 +3697,7 @@ out: if (ret < 0) { rdma->error_state = ret; } + rcu_read_unlock(); return ret; } @@ -3433,10 +3711,18 @@ out: static int rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) { - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; int curr; int found = -1; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmain); + + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + /* Find the matching RAMBlock in our local list */ for (curr = 0; curr < rdma->local_ram_blocks.nb_blocks; curr++) { if (!strcmp(rdma->local_ram_blocks.block[curr].block_name, name)) { @@ -3447,6 +3733,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) if (found == -1) { error_report("RAMBlock '%s' not found on destination", name); + rcu_read_unlock(); return -ENOENT; } @@ -3454,6 +3741,7 @@ rdma_block_notification_handle(QIOChannelRDMA *rioc, const char *name) trace_rdma_block_notification_handle(name, rdma->next_src_index); rdma->next_src_index++; + rcu_read_unlock(); return 0; } @@ -3476,14 +3764,27 @@ static int qemu_rdma_registration_start(QEMUFile *f, void *opaque, uint64_t flags, void *data) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; + + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } CHECK_ERROR_STATE(); + if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + rcu_read_unlock(); + return 0; + } + trace_qemu_rdma_registration_start(flags); qemu_put_be64(f, RAM_SAVE_FLAG_HOOK); qemu_fflush(f); + rcu_read_unlock(); return 0; } @@ -3496,12 +3797,24 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, { Error *local_err = NULL, **errp = &local_err; QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(opaque); - RDMAContext *rdma = rioc->rdma; + RDMAContext *rdma; RDMAControlHeader head = { .len = 0, .repeat = 1 }; int ret = 0; + rcu_read_lock(); + rdma = atomic_rcu_read(&rioc->rdmaout); + if (!rdma) { + rcu_read_unlock(); + return -EIO; + } + CHECK_ERROR_STATE(); + if (migrate_get_current()->state == MIGRATION_STATUS_POSTCOPY_ACTIVE) { + rcu_read_unlock(); + return 0; + } + qemu_fflush(f); ret = qemu_rdma_drain_cq(f, rdma); @@ -3530,6 +3843,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, qemu_rdma_reg_whole_ram_blocks : NULL); if (ret < 0) { ERROR(errp, "receiving remote info!"); + rcu_read_unlock(); return ret; } @@ -3553,6 +3867,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, "not identical on both the source and destination.", local->nb_blocks, nb_dest_blocks); rdma->error_state = -EINVAL; + rcu_read_unlock(); return -EINVAL; } @@ -3569,6 +3884,7 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, local->block[i].length, rdma->dest_blocks[i].length); rdma->error_state = -EINVAL; + rcu_read_unlock(); return -EINVAL; } local->block[i].remote_host_addr = @@ -3586,9 +3902,11 @@ static int qemu_rdma_registration_stop(QEMUFile *f, void *opaque, goto err; } + rcu_read_unlock(); return 0; err: rdma->error_state = ret; + rcu_read_unlock(); return ret; } @@ -3606,10 +3924,15 @@ static const QEMUFileHooks rdma_write_hooks = { static void qio_channel_rdma_finalize(Object *obj) { QIOChannelRDMA *rioc = QIO_CHANNEL_RDMA(obj); - if (rioc->rdma) { - qemu_rdma_cleanup(rioc->rdma); - g_free(rioc->rdma); - rioc->rdma = NULL; + if (rioc->rdmain) { + qemu_rdma_cleanup(rioc->rdmain); + g_free(rioc->rdmain); + rioc->rdmain = NULL; + } + if (rioc->rdmaout) { + qemu_rdma_cleanup(rioc->rdmaout); + g_free(rioc->rdmaout); + rioc->rdmaout = NULL; } } @@ -3623,6 +3946,8 @@ static void qio_channel_rdma_class_init(ObjectClass *klass, ioc_klass->io_set_blocking = qio_channel_rdma_set_blocking; ioc_klass->io_close = qio_channel_rdma_close; ioc_klass->io_create_watch = qio_channel_rdma_create_watch; + ioc_klass->io_set_aio_fd_handler = qio_channel_rdma_set_aio_fd_handler; + ioc_klass->io_shutdown = qio_channel_rdma_shutdown; } static const TypeInfo qio_channel_rdma_info = { @@ -3649,13 +3974,16 @@ static QEMUFile *qemu_fopen_rdma(RDMAContext *rdma, const char *mode) } rioc = QIO_CHANNEL_RDMA(object_new(TYPE_QIO_CHANNEL_RDMA)); - rioc->rdma = rdma; if (mode[0] == 'w') { rioc->file = qemu_fopen_channel_output(QIO_CHANNEL(rioc)); + rioc->rdmaout = rdma; + rioc->rdmain = rdma->return_path; qemu_file_set_hooks(rioc->file, &rdma_write_hooks); } else { rioc->file = qemu_fopen_channel_input(QIO_CHANNEL(rioc)); + rioc->rdmain = rdma; + rioc->rdmaout = rdma->return_path; qemu_file_set_hooks(rioc->file, &rdma_read_hooks); } @@ -3679,6 +4007,10 @@ static void rdma_accept_incoming_migration(void *opaque) trace_qemu_rdma_accept_incoming_migration_accepted(); + if (rdma->is_return_path) { + return; + } + f = qemu_fopen_rdma(rdma, "rb"); if (f == NULL) { ERROR(errp, "could not qemu_fopen_rdma!"); @@ -3693,7 +4025,7 @@ static void rdma_accept_incoming_migration(void *opaque) void rdma_start_incoming_migration(const char *host_port, Error **errp) { int ret; - RDMAContext *rdma; + RDMAContext *rdma, *rdma_return_path = NULL; Error *local_err = NULL; trace_rdma_start_incoming_migration(); @@ -3720,12 +4052,24 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) trace_rdma_start_incoming_migration_after_rdma_listen(); + /* initialize the RDMAContext for return path */ + if (migrate_postcopy()) { + rdma_return_path = qemu_rdma_data_init(host_port, &local_err); + + if (rdma_return_path == NULL) { + goto err; + } + + qemu_rdma_return_path_dest_init(rdma_return_path, rdma); + } + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, NULL, (void *)(intptr_t)rdma); return; err: error_propagate(errp, local_err); g_free(rdma); + g_free(rdma_return_path); } void rdma_start_outgoing_migration(void *opaque, @@ -3733,6 +4077,7 @@ void rdma_start_outgoing_migration(void *opaque, { MigrationState *s = opaque; RDMAContext *rdma = qemu_rdma_data_init(host_port, errp); + RDMAContext *rdma_return_path = NULL; int ret = 0; if (rdma == NULL) { @@ -3753,6 +4098,32 @@ void rdma_start_outgoing_migration(void *opaque, goto err; } + /* RDMA postcopy need a seprate queue pair for return path */ + if (migrate_postcopy()) { + rdma_return_path = qemu_rdma_data_init(host_port, errp); + + if (rdma_return_path == NULL) { + goto err; + } + + ret = qemu_rdma_source_init(rdma_return_path, + s->enabled_capabilities[MIGRATION_CAPABILITY_RDMA_PIN_ALL], errp); + + if (ret) { + goto err; + } + + ret = qemu_rdma_connect(rdma_return_path, errp); + + if (ret) { + goto err; + } + + rdma->return_path = rdma_return_path; + rdma_return_path->return_path = rdma; + rdma_return_path->is_return_path = true; + } + trace_rdma_start_outgoing_migration_after_rdma_connect(); s->to_dst_file = qemu_fopen_rdma(rdma, "wb"); @@ -3760,4 +4131,5 @@ void rdma_start_outgoing_migration(void *opaque, return; err: g_free(rdma); + g_free(rdma_return_path); } diff --git a/migration/savevm.c b/migration/savevm.c index 7f92567a10..34bcad3807 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -56,13 +56,9 @@ #include "io/channel-file.h" #include "sysemu/replay.h" #include "qjson.h" - -#ifndef ETH_P_RARP -#define ETH_P_RARP 0x8035 -#endif -#define ARP_HTYPE_ETH 0x0001 -#define ARP_PTYPE_IP 0x0800 -#define ARP_OP_REQUEST_REV 0x3 +#include "migration/colo.h" +#include "qemu/bitmap.h" +#include "net/announce.h" const unsigned int postcopy_ram_discard_version = 0; @@ -82,6 +78,7 @@ enum qemu_vm_cmd { were previously sent during precopy but are dirty. */ MIG_CMD_PACKAGED, /* Send a wrapped stream within this stream */ + MIG_CMD_ENABLE_COLO, /* Enable COLO */ MIG_CMD_POSTCOPY_RESUME, /* resume postcopy on dest */ MIG_CMD_RECV_BITMAP, /* Request for recved bitmap on dst */ MIG_CMD_MAX @@ -123,67 +120,6 @@ static struct mig_cmd_args { * generic extendable format with an exception for two old entities. */ -static int announce_self_create(uint8_t *buf, - uint8_t *mac_addr) -{ - /* Ethernet header. */ - memset(buf, 0xff, 6); /* destination MAC addr */ - memcpy(buf + 6, mac_addr, 6); /* source MAC addr */ - *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */ - - /* RARP header. */ - *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */ - *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */ - *(buf + 18) = 6; /* hardware addr length (ethernet) */ - *(buf + 19) = 4; /* protocol addr length (IPv4) */ - *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */ - memcpy(buf + 22, mac_addr, 6); /* source hw addr */ - memset(buf + 28, 0x00, 4); /* source protocol addr */ - memcpy(buf + 32, mac_addr, 6); /* target hw addr */ - memset(buf + 38, 0x00, 4); /* target protocol addr */ - - /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */ - memset(buf + 42, 0x00, 18); - - return 60; /* len (FCS will be added by hardware) */ -} - -static void qemu_announce_self_iter(NICState *nic, void *opaque) -{ - uint8_t buf[60]; - int len; - - trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr)); - len = announce_self_create(buf, nic->conf->macaddr.a); - - qemu_send_packet_raw(qemu_get_queue(nic), buf, len); -} - - -static void qemu_announce_self_once(void *opaque) -{ - static int count = SELF_ANNOUNCE_ROUNDS; - QEMUTimer *timer = *(QEMUTimer **)opaque; - - qemu_foreach_nic(qemu_announce_self_iter, NULL); - - if (--count) { - /* delay 50ms, 150ms, 250ms, ... */ - timer_mod(timer, qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + - self_announce_delay(count)); - } else { - timer_del(timer); - timer_free(timer); - } -} - -void qemu_announce_self(void) -{ - static QEMUTimer *timer; - timer = timer_new_ms(QEMU_CLOCK_REALTIME, qemu_announce_self_once, &timer); - qemu_announce_self_once(&timer); -} - /***********************************************************/ /* savevm/loadvm support */ @@ -261,15 +197,16 @@ void timer_get(QEMUFile *f, QEMUTimer *ts) * Not in vmstate.c to not add qemu-timer.c as dependency to vmstate.c */ -static int get_timer(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_timer(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { QEMUTimer *v = pv; timer_get(f, v); return 0; } -static int put_timer(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_timer(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { QEMUTimer *v = pv; timer_put(f, v); @@ -300,7 +237,7 @@ typedef struct SaveStateEntry { int section_id; /* section id read from the stream */ int load_section_id; - SaveVMHandlers *ops; + const SaveVMHandlers *ops; const VMStateDescription *vmsd; void *opaque; CompatEntry *compat; @@ -313,6 +250,8 @@ typedef struct SaveState { uint32_t len; const char *name; uint32_t target_page_bits; + uint32_t caps_count; + MigrationCapability *capabilities; } SaveState; static SaveState savevm_state = { @@ -320,15 +259,51 @@ static SaveState savevm_state = { .global_section_id = 0, }; +static bool should_validate_capability(int capability) +{ + assert(capability >= 0 && capability < MIGRATION_CAPABILITY__MAX); + /* Validate only new capabilities to keep compatibility. */ + switch (capability) { + case MIGRATION_CAPABILITY_X_IGNORE_SHARED: + return true; + default: + return false; + } +} + +static uint32_t get_validatable_capabilities_count(void) +{ + MigrationState *s = migrate_get_current(); + uint32_t result = 0; + int i; + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (should_validate_capability(i) && s->enabled_capabilities[i]) { + result++; + } + } + return result; +} + static int configuration_pre_save(void *opaque) { SaveState *state = opaque; const char *current_name = MACHINE_GET_CLASS(current_machine)->name; + MigrationState *s = migrate_get_current(); + int i, j; state->len = strlen(current_name); state->name = current_name; state->target_page_bits = qemu_target_page_bits(); + state->caps_count = get_validatable_capabilities_count(); + state->capabilities = g_renew(MigrationCapability, state->capabilities, + state->caps_count); + for (i = j = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (should_validate_capability(i) && s->enabled_capabilities[i]) { + state->capabilities[j++] = i; + } + } + return 0; } @@ -344,6 +319,40 @@ static int configuration_pre_load(void *opaque) return 0; } +static bool configuration_validate_capabilities(SaveState *state) +{ + bool ret = true; + MigrationState *s = migrate_get_current(); + unsigned long *source_caps_bm; + int i; + + source_caps_bm = bitmap_new(MIGRATION_CAPABILITY__MAX); + for (i = 0; i < state->caps_count; i++) { + MigrationCapability capability = state->capabilities[i]; + set_bit(capability, source_caps_bm); + } + + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + bool source_state, target_state; + if (!should_validate_capability(i)) { + continue; + } + source_state = test_bit(i, source_caps_bm); + target_state = s->enabled_capabilities[i]; + if (source_state != target_state) { + error_report("Capability %s is %s, but received capability is %s", + MigrationCapability_str(i), + target_state ? "on" : "off", + source_state ? "on" : "off"); + ret = false; + /* Don't break here to report all failed capabilities */ + } + } + + g_free(source_caps_bm); + return ret; +} + static int configuration_post_load(void *opaque, int version_id) { SaveState *state = opaque; @@ -361,9 +370,53 @@ static int configuration_post_load(void *opaque, int version_id) return -EINVAL; } + if (!configuration_validate_capabilities(state)) { + return -EINVAL; + } + return 0; } +static int get_capability(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + MigrationCapability *capability = pv; + char capability_str[UINT8_MAX + 1]; + uint8_t len; + int i; + + len = qemu_get_byte(f); + qemu_get_buffer(f, (uint8_t *)capability_str, len); + capability_str[len] = '\0'; + for (i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (!strcmp(MigrationCapability_str(i), capability_str)) { + *capability = i; + return 0; + } + } + error_report("Received unknown capability %s", capability_str); + return -EINVAL; +} + +static int put_capability(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + MigrationCapability *capability = pv; + const char *capability_str = MigrationCapability_str(*capability); + size_t len = strlen(capability_str); + assert(len <= UINT8_MAX); + + qemu_put_byte(f, len); + qemu_put_buffer(f, (uint8_t *)capability_str, len); + return 0; +} + +static const VMStateInfo vmstate_info_capability = { + .name = "capability", + .get = get_capability, + .put = put_capability, +}; + /* The target-page-bits subsection is present only if the * target page size is not the same as the default (ie the * minimum page size for a variable-page-size guest CPU). @@ -388,6 +441,25 @@ static const VMStateDescription vmstate_target_page_bits = { } }; +static bool vmstate_capabilites_needed(void *opaque) +{ + return get_validatable_capabilities_count() > 0; +} + +static const VMStateDescription vmstate_capabilites = { + .name = "configuration/capabilities", + .version_id = 1, + .minimum_version_id = 1, + .needed = vmstate_capabilites_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32_V(caps_count, SaveState, 1), + VMSTATE_VARRAY_UINT32_ALLOC(capabilities, SaveState, caps_count, 1, + vmstate_info_capability, + MigrationCapability), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_configuration = { .name = "configuration", .version_id = 1, @@ -401,6 +473,7 @@ static const VMStateDescription vmstate_configuration = { }, .subsections = (const VMStateDescription*[]) { &vmstate_target_page_bits, + &vmstate_capabilites, NULL } }; @@ -611,7 +684,7 @@ int register_savevm_live(DeviceState *dev, const char *idstr, int instance_id, int version_id, - SaveVMHandlers *ops, + const SaveVMHandlers *ops, void *opaque) { SaveStateEntry *se; @@ -841,6 +914,12 @@ static void qemu_savevm_command_send(QEMUFile *f, qemu_fflush(f); } +void qemu_savevm_send_colo_enable(QEMUFile *f) +{ + trace_savevm_send_colo_enable(); + qemu_savevm_command_send(f, MIG_CMD_ENABLE_COLO, 0, NULL); +} + void qemu_savevm_send_ping(QEMUFile *f, uint32_t value) { uint32_t buf; @@ -1009,6 +1088,7 @@ void qemu_savevm_state_header(QEMUFile *f) void qemu_savevm_state_setup(QEMUFile *f) { SaveStateEntry *se; + Error *local_err = NULL; int ret; trace_savevm_state_setup(); @@ -1030,6 +1110,10 @@ void qemu_savevm_state_setup(QEMUFile *f) break; } } + + if (precopy_notify(PRECOPY_NOTIFY_SETUP, &local_err)) { + error_report_err(local_err); + } } int qemu_savevm_state_resume_prepare(MigrationState *s) @@ -1172,6 +1256,11 @@ int qemu_savevm_state_complete_precopy(QEMUFile *f, bool iterable_only, SaveStateEntry *se; int ret; bool in_postcopy = migration_in_postcopy(); + Error *local_err = NULL; + + if (precopy_notify(PRECOPY_NOTIFY_COMPLETE, &local_err)) { + error_report_err(local_err); + } trace_savevm_state_complete_precopy(); @@ -1304,6 +1393,11 @@ void qemu_savevm_state_pending(QEMUFile *f, uint64_t threshold_size, void qemu_savevm_state_cleanup(void) { SaveStateEntry *se; + Error *local_err = NULL; + + if (precopy_notify(PRECOPY_NOTIFY_CLEANUP, &local_err)) { + error_report_err(local_err); + } trace_savevm_state_cleanup(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1319,21 +1413,25 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) MigrationState *ms = migrate_get_current(); MigrationStatus status; - migrate_init(ms); - - ms->to_dst_file = f; + if (migration_is_setup_or_active(ms->state) || + ms->state == MIGRATION_STATUS_CANCELLING || + ms->state == MIGRATION_STATUS_COLO) { + error_setg(errp, QERR_MIGRATION_ACTIVE); + return -EINVAL; + } if (migration_is_blocked(errp)) { - ret = -EINVAL; - goto done; + return -EINVAL; } if (migrate_use_block()) { error_setg(errp, "Block migration and snapshots are incompatible"); - ret = -EINVAL; - goto done; + return -EINVAL; } + migrate_init(ms); + ms->to_dst_file = f; + qemu_mutex_unlock_iothread(); qemu_savevm_state_header(f); qemu_savevm_state_setup(f); @@ -1355,7 +1453,6 @@ static int qemu_savevm_state(QEMUFile *f, Error **errp) error_setg_errno(errp, -ret, "Error while writing VM state"); } -done: if (ret != 0) { status = MIGRATION_STATUS_FAILED; } else { @@ -1370,13 +1467,21 @@ done: return ret; } -static int qemu_save_device_state(QEMUFile *f) +void qemu_savevm_live_state(QEMUFile *f) +{ + /* save QEMU_VM_SECTION_END section */ + qemu_savevm_state_complete_precopy(f, true, false); + qemu_put_byte(f, QEMU_VM_EOF); +} + +int qemu_save_device_state(QEMUFile *f) { SaveStateEntry *se; - qemu_put_be32(f, QEMU_VM_FILE_MAGIC); - qemu_put_be32(f, QEMU_VM_FILE_VERSION); - + if (!migration_in_colo_state()) { + qemu_put_be32(f, QEMU_VM_FILE_MAGIC); + qemu_put_be32(f, QEMU_VM_FILE_VERSION); + } cpu_synchronize_all_states(); QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { @@ -1432,8 +1537,6 @@ enum LoadVMExitCodes { 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 @@ -1622,6 +1725,7 @@ static void *postcopy_ram_listen_thread(void *opaque) qemu_sem_post(&mis->listen_thread_sem); trace_postcopy_ram_listen_thread_start(); + rcu_register_thread(); /* * Because we're a thread and not a coroutine we can't yield * in qemu_file, and thus we must be blocking now. @@ -1662,6 +1766,7 @@ static void *postcopy_ram_listen_thread(void *opaque) * to leave the guest running and fire MCEs for pages that never * arrived as a desperate recovery step. */ + rcu_unregister_thread(); exit(EXIT_FAILURE); } @@ -1676,6 +1781,8 @@ static void *postcopy_ram_listen_thread(void *opaque) migration_incoming_state_destroy(); qemu_loadvm_state_cleanup(); + rcu_unregister_thread(); + mis->have_listen_thread = false; return NULL; } @@ -1707,6 +1814,7 @@ static int loadvm_postcopy_handle_listen(MigrationIncomingState *mis) */ if (migrate_postcopy_ram()) { if (postcopy_ram_enable_notify(mis)) { + postcopy_ram_incoming_cleanup(mis); return -1; } } @@ -1742,13 +1850,14 @@ static void loadvm_postcopy_handle_run_bh(void *opaque) { Error *local_err = NULL; HandleRunBhData *data = opaque; + MigrationIncomingState *mis = migration_incoming_get_current(); /* 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(); + qemu_announce_self(&mis->announce_timer, migrate_announce_params()); /* Make sure all file formats flush their mutable metadata. * If we get an error here, just don't restart the VM yet. */ @@ -1918,6 +2027,12 @@ static int loadvm_handle_recv_bitmap(MigrationIncomingState *mis, return 0; } +static int loadvm_process_enable_colo(MigrationIncomingState *mis) +{ + migration_incoming_enable_colo(); + return colo_init_ram_cache(); +} + /* * Process an incoming 'QEMU_VM_COMMAND' * 0 just a normal return @@ -1997,6 +2112,9 @@ static int loadvm_process_command(QEMUFile *f) case MIG_CMD_RECV_BITMAP: return loadvm_handle_recv_bitmap(mis, len); + + case MIG_CMD_ENABLE_COLO: + return loadvm_process_enable_colo(mis); } return 0; @@ -2075,7 +2193,9 @@ qemu_loadvm_section_start_full(QEMUFile *f, MigrationIncomingState *mis) /* Find savevm section */ se = find_se(idstr, instance_id); if (se == NULL) { - error_report("Unknown savevm section or instance '%s' %d", + error_report("Unknown savevm section or instance '%s' %d. " + "Make sure that your current VM setup matches your " + "saved VM setup, including any hotplugged devices", idstr, instance_id); return -EINVAL; } @@ -2224,7 +2344,7 @@ static bool postcopy_pause_incoming(MigrationIncomingState *mis) return true; } -static int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis) { uint8_t section_type; int ret = 0; @@ -2327,11 +2447,13 @@ int qemu_loadvm_state(QEMUFile *f) if (migrate_get_current()->send_configuration) { if (qemu_get_byte(f) != QEMU_VM_CONFIGURATION) { error_report("Configuration section missing"); + qemu_loadvm_state_cleanup(); return -EINVAL; } ret = vmstate_load_state(f, &vmstate_configuration, &savevm_state, 0); if (ret) { + qemu_loadvm_state_cleanup(); return ret; } } @@ -2393,6 +2515,22 @@ int qemu_loadvm_state(QEMUFile *f) return ret; } +int qemu_load_device_state(QEMUFile *f) +{ + MigrationIncomingState *mis = migration_incoming_get_current(); + int ret; + + /* Load QEMU_VM_SECTION_FULL section */ + ret = qemu_loadvm_state_main(f, mis); + if (ret < 0) { + error_report("Failed to load device state: %d", ret); + return ret; + } + + cpu_synchronize_all_post_init(); + return 0; +} + int save_snapshot(const char *name, Error **errp) { BlockDriverState *bs, *bs1; @@ -2405,9 +2543,13 @@ int save_snapshot(const char *name, Error **errp) struct tm tm; AioContext *aio_context; + if (migration_is_blocked(errp)) { + return false; + } + if (!replay_can_snapshot()) { - error_report("Record/replay does not allow making snapshot " - "right now. Try once more later."); + error_setg(errp, "Record/replay does not allow making snapshot " + "right now. Try once more later."); return ret; } @@ -2603,8 +2745,8 @@ int load_snapshot(const char *name, Error **errp) MigrationIncomingState *mis = migration_incoming_get_current(); if (!replay_can_snapshot()) { - error_report("Record/replay does not allow loading snapshot " - "right now. Try once more later."); + error_setg(errp, "Record/replay does not allow loading snapshot " + "right now. Try once more later."); return -EINVAL; } @@ -2702,7 +2844,7 @@ void vmstate_register_ram_global(MemoryRegion *mr) bool vmstate_check_only_migratable(const VMStateDescription *vmsd) { /* check needed if --only-migratable is specified */ - if (!migrate_get_current()->only_migratable) { + if (!only_migratable) { return true; } diff --git a/migration/savevm.h b/migration/savevm.h index a5e65b8ae3..51a4b9caa8 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -55,8 +55,13 @@ void qemu_savevm_send_postcopy_ram_discard(QEMUFile *f, const char *name, uint16_t len, uint64_t *start_list, uint64_t *length_list); +void qemu_savevm_send_colo_enable(QEMUFile *f); +void qemu_savevm_live_state(QEMUFile *f); +int qemu_save_device_state(QEMUFile *f); int qemu_loadvm_state(QEMUFile *f); void qemu_loadvm_state_cleanup(void); +int qemu_loadvm_state_main(QEMUFile *f, MigrationIncomingState *mis); +int qemu_load_device_state(QEMUFile *f); #endif diff --git a/migration/socket.c b/migration/socket.c index f4c8174400..49bacf78fd 100644 --- a/migration/socket.c +++ b/migration/socket.c @@ -15,6 +15,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu-common.h" #include "qemu/error-report.h" @@ -177,6 +178,7 @@ static void socket_start_incoming_migration(SocketAddress *saddr, Error **errp) { QIONetListener *listener = qio_net_listener_new(); + size_t i; qio_net_listener_set_name(listener, "migration-socket-listener"); @@ -189,6 +191,16 @@ static void socket_start_incoming_migration(SocketAddress *saddr, socket_accept_incoming_migration, NULL, NULL, g_main_context_get_thread_default()); + + for (i = 0; i < listener->nsioc; i++) { + SocketAddress *address = + qio_channel_socket_get_local_address(listener->sioc[i], errp); + if (!address) { + return; + } + migrate_add_address(address); + qapi_free_SocketAddress(address); + } } void tcp_start_incoming_migration(const char *host_port, Error **errp) diff --git a/migration/tls.c b/migration/tls.c index 3b9e8c9263..5171afc6c4 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -94,7 +94,7 @@ void migration_tls_channel_process_incoming(MigrationState *s, tioc = qio_channel_tls_new_server( ioc, creds, - NULL, /* XXX pass ACL name */ + s->parameters.tls_authz, errp); if (!tioc) { return; diff --git a/migration/trace-events b/migration/trace-events index 9430f3cbe0..de2e136e57 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# migration/savevm.c +# 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" @@ -37,6 +37,7 @@ savevm_send_ping(uint32_t val) "0x%x" savevm_send_postcopy_listen(void) "" savevm_send_postcopy_run(void) "" savevm_send_postcopy_resume(void) "" +savevm_send_colo_enable(void) "" savevm_send_recv_bitmap(char *name) "%s" savevm_state_setup(void) "" savevm_state_resume_prepare(void) "" @@ -45,15 +46,11 @@ savevm_state_iterate(void) "" savevm_state_cleanup(void) "" savevm_state_complete_precopy(void) "" vmstate_save(const char *idstr, const char *vmsd_name) "%s, %s" -vmstate_save_state_pre_save_res(const char *name, int res) "%s/%d" -vmstate_save_state_loop(const char *name, const char *field, int n_elems) "%s/%s[%d]" -vmstate_save_state_top(const char *idstr) "%s" -vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s" -vmstate_subsection_save_top(const char *idstr) "%s" vmstate_load(const char *idstr, const char *vmsd_name) "%s, %s" -qemu_announce_self_iter(const char *mac) "%s" +postcopy_pause_incoming(void) "" +postcopy_pause_incoming_continued(void) "" -# migration/vmstate.c +# vmstate.c vmstate_load_field_error(const char *field, int ret) "field \"%s\" load failed, ret = %d" vmstate_load_state(const char *name, int version_id) "%s v%d" vmstate_load_state_end(const char *name, const char *reason, int val) "%s %s/%d" @@ -62,27 +59,34 @@ vmstate_n_elems(const char *name, int n_elems) "%s: %d" vmstate_subsection_load(const char *parent) "%s" vmstate_subsection_load_bad(const char *parent, const char *sub, const char *sub2) "%s: %s/%s" vmstate_subsection_load_good(const char *parent) "%s" +vmstate_save_state_pre_save_res(const char *name, int res) "%s/%d" +vmstate_save_state_loop(const char *name, const char *field, int n_elems) "%s/%s[%d]" +vmstate_save_state_top(const char *idstr) "%s" +vmstate_subsection_save_loop(const char *name, const char *sub) "%s/%s" +vmstate_subsection_save_top(const char *idstr) "%s" + +# vmstate-types.c get_qtailq(const char *name, int version_id) "%s v%d" get_qtailq_end(const char *name, const char *reason, int val) "%s %s/%d" put_qtailq(const char *name, int version_id) "%s v%d" put_qtailq_end(const char *name, const char *reason) "%s %s" -# migration/qemu-file.c +# qemu-file.c qemu_file_fclose(void) "" -# migration/ram.c +# ram.c get_queued_page(const char *block_name, uint64_t tmp_offset, unsigned long page_abs) "%s/0x%" PRIx64 " page_abs=0x%lx" get_queued_page_not_dirty(const char *block_name, uint64_t tmp_offset, unsigned long page_abs, int sent) "%s/0x%" PRIx64 " page_abs=0x%lx (sent=%d)" migration_bitmap_sync_start(void) "" migration_bitmap_sync_end(uint64_t dirty_pages) "dirty_pages %" PRIu64 migration_throttle(void) "" -multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet number %" PRIu64 " pages %d flags 0x%x" +multifd_recv(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet number %" PRIu64 " pages %d flags 0x%x next packet size %d" multifd_recv_sync_main(long packet_num) "packet num %ld" multifd_recv_sync_main_signal(uint8_t id) "channel %d" multifd_recv_sync_main_wait(uint8_t id) "channel %d" multifd_recv_thread_end(uint8_t id, uint64_t packets, uint64_t pages) "channel %d packets %" PRIu64 " pages %" PRIu64 multifd_recv_thread_start(uint8_t id) "%d" -multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x" +multifd_send(uint8_t id, uint64_t packet_num, uint32_t used, uint32_t flags, uint32_t next_packet_size) "channel %d packet_num %" PRIu64 " pages %d flags 0x%x next packet size %d" multifd_send_sync_main(long packet_num) "packet num %ld" multifd_send_sync_main_signal(uint8_t id) "channel %d" multifd_send_sync_main_wait(uint8_t id) "channel %d" @@ -101,8 +105,14 @@ ram_dirty_bitmap_sync_start(void) "" ram_dirty_bitmap_sync_wait(void) "" ram_dirty_bitmap_sync_complete(void) "" ram_state_resume_prepare(uint64_t v) "%" PRId64 +colo_flush_ram_cache_begin(uint64_t dirty_pages) "dirty_pages %" PRIu64 +colo_flush_ram_cache_end(void) "" +save_xbzrle_page_skipping(void) "" +save_xbzrle_page_overflow(void) "" +ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" +ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 -# migration/migration.c +# migration.c await_return_path_close_on_source_close(void) "" await_return_path_close_on_source_joining(void) "" migrate_set_state(const char *new_state) "new state %s" @@ -128,11 +138,7 @@ open_return_path_on_source_continue(void) "" postcopy_start(void) "" postcopy_pause_return_path(void) "" postcopy_pause_return_path_continued(void) "" -postcopy_pause_fault_thread(void) "" -postcopy_pause_fault_thread_continued(void) "" postcopy_pause_continued(void) "" -postcopy_pause_incoming(void) "" -postcopy_pause_incoming_continued(void) "" postcopy_start_set_run(void) "" source_return_path_thread_bad_end(void) "" source_return_path_thread_end(void) "" @@ -141,19 +147,21 @@ source_return_path_thread_loop_top(void) "" source_return_path_thread_pong(uint32_t val) "0x%x" source_return_path_thread_shut(uint32_t val) "0x%x" source_return_path_thread_resume_ack(uint32_t v) "%"PRIu32 -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, uint64_t bandwidth, uint64_t size) "transferred %" PRIu64 " time_spent %" PRIu64 " bandwidth %" PRIu64 " 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) "" + +# channel.c migration_set_incoming_channel(void *ioc, const char *ioctype) "ioc=%p ioctype=%s" migration_set_outgoing_channel(void *ioc, const char *ioctype, const char *hostname, void *err) "ioc=%p ioctype=%s hostname=%s err=%p" -mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, int received) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d, already_received: %d" -mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" -# migration/rdma.c +# global_state.c +migrate_state_too_big(void) "" +migrate_global_state_post_load(const char *state) "loaded state: %s" +migrate_global_state_pre_save(const char *state) "saved state: %s" + +# rdma.c qemu_rdma_accept_incoming_migration(void) "" qemu_rdma_accept_incoming_migration_accepted(void) "" qemu_rdma_accept_pin_state(bool pin) "%d" @@ -215,7 +223,7 @@ 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-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_cleanup_range(const char *ramblock, void *host_addr, size_t offset, size_t length) "%s: %p offset=0x%zx length=0x%zx" @@ -224,6 +232,10 @@ postcopy_nhp_range(const char *ramblock, void *host_addr, size_t offset, size_t postcopy_place_page(void *host_addr) "host=%p" postcopy_place_page_zero(void *host_addr) "host=%p" postcopy_ram_enable_notify(void) "" +mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, int received) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d, already_received: %d" +mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" +postcopy_pause_fault_thread(void) "" +postcopy_pause_fault_thread_continued(void) "" postcopy_ram_fault_thread_entry(void) "" postcopy_ram_fault_thread_exit(void) "" postcopy_ram_fault_thread_fds_core(int baseufd, int quitfd) "ufd: %d quitfd: %d" @@ -239,26 +251,22 @@ postcopy_request_shared_page(const char *sharer, const char *rb, uint64_t rb_off postcopy_request_shared_page_present(const char *sharer, const char *rb, uint64_t rb_offset) "%s already %s offset 0x%"PRIx64 postcopy_wake_shared(uint64_t client_addr, const char *rb) "at 0x%"PRIx64" in %s" -save_xbzrle_page_skipping(void) "" -save_xbzrle_page_overflow(void) "" -ram_save_iterate_big_wait(uint64_t milliconds, int iterations) "big wait: %" PRIu64 " milliseconds, %d iterations" -ram_load_complete(int ret, uint64_t seq_iter) "exit_code %d seq iteration %" PRIu64 get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" -# migration/exec.c +# exec.c migration_exec_outgoing(const char *cmd) "cmd=%s" migration_exec_incoming(const char *cmd) "cmd=%s" -# migration/fd.c +# fd.c migration_fd_outgoing(int fd) "fd=%d" migration_fd_incoming(int fd) "fd=%d" -# migration/socket.c +# socket.c migration_socket_incoming_accepted(void) "" migration_socket_outgoing_connected(const char *hostname) "hostname=%s" migration_socket_outgoing_error(const char *err) "error=%s" -# migration/tls.c +# tls.c migration_tls_outgoing_handshake_start(const char *hostname) "hostname=%s" migration_tls_outgoing_handshake_error(const char *err) "err=%s" migration_tls_outgoing_handshake_complete(void) "" @@ -266,13 +274,15 @@ migration_tls_incoming_handshake_start(void) "" migration_tls_incoming_handshake_error(const char *err) "err=%s" migration_tls_incoming_handshake_complete(void) "" -# migration/colo.c +# colo.c colo_vm_state_change(const char *old, const char *new) "Change '%s' => '%s'" colo_send_message(const char *msg) "Send '%s' message" colo_receive_message(const char *msg) "Receive '%s' message" + +# colo-failover.c colo_failover_set_state(const char *new_state) "new state %s" -# migration/block-dirty-bitmap.c +# block-dirty-bitmap.c send_bitmap_header_enter(void) "" send_bitmap_bits(uint32_t flags, uint64_t start_sector, uint32_t nr_sectors, uint64_t data_size) "flags: 0x%x, start_sector: %" PRIu64 ", nr_sectors: %" PRIu32 ", data_size: %" PRIu64 dirty_bitmap_save_iterate(int in_postcopy) "in postcopy: %d" diff --git a/migration/vmstate-types.c b/migration/vmstate-types.c index 48184c380d..6f75f97a07 100644 --- a/migration/vmstate-types.c +++ b/migration/vmstate-types.c @@ -22,15 +22,16 @@ /* bool */ -static int get_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_bool(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { bool *v = pv; *v = qemu_get_byte(f); return 0; } -static int put_bool(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_bool(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { bool *v = pv; qemu_put_byte(f, *v); @@ -45,15 +46,16 @@ const VMStateInfo vmstate_info_bool = { /* 8 bit int */ -static int get_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_int8(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int8_t *v = pv; qemu_get_s8s(f, v); return 0; } -static int put_int8(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_int8(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { int8_t *v = pv; qemu_put_s8s(f, v); @@ -68,15 +70,16 @@ const VMStateInfo vmstate_info_int8 = { /* 16 bit int */ -static int get_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_int16(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int16_t *v = pv; qemu_get_sbe16s(f, v); return 0; } -static int put_int16(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_int16(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { int16_t *v = pv; qemu_put_sbe16s(f, v); @@ -91,15 +94,16 @@ const VMStateInfo vmstate_info_int16 = { /* 32 bit int */ -static int get_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_int32(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int32_t *v = pv; qemu_get_sbe32s(f, v); return 0; } -static int put_int32(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_int32(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { int32_t *v = pv; qemu_put_sbe32s(f, v); @@ -116,7 +120,7 @@ const VMStateInfo vmstate_info_int32 = { in the field */ static int get_int32_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { int32_t *v = pv; int32_t v2; @@ -142,7 +146,8 @@ const VMStateInfo vmstate_info_int32_equal = { * and less than or equal to the one in the field. */ -static int get_int32_le(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_int32_le(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int32_t *cur = pv; int32_t loaded; @@ -166,15 +171,16 @@ const VMStateInfo vmstate_info_int32_le = { /* 64 bit int */ -static int get_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_int64(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int64_t *v = pv; qemu_get_sbe64s(f, v); return 0; } -static int put_int64(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_int64(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { int64_t *v = pv; qemu_put_sbe64s(f, v); @@ -189,15 +195,16 @@ const VMStateInfo vmstate_info_int64 = { /* 8 bit unsigned int */ -static int get_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_uint8(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { uint8_t *v = pv; qemu_get_8s(f, v); return 0; } -static int put_uint8(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_uint8(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { uint8_t *v = pv; qemu_put_8s(f, v); @@ -212,15 +219,16 @@ const VMStateInfo vmstate_info_uint8 = { /* 16 bit unsigned int */ -static int get_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_uint16(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { uint16_t *v = pv; qemu_get_be16s(f, v); return 0; } -static int put_uint16(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_uint16(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { uint16_t *v = pv; qemu_put_be16s(f, v); @@ -235,15 +243,16 @@ const VMStateInfo vmstate_info_uint16 = { /* 32 bit unsigned int */ -static int get_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_uint32(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { uint32_t *v = pv; qemu_get_be32s(f, v); return 0; } -static int put_uint32(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_uint32(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { uint32_t *v = pv; qemu_put_be32s(f, v); @@ -260,7 +269,7 @@ const VMStateInfo vmstate_info_uint32 = { in the field */ static int get_uint32_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint32_t *v = pv; uint32_t v2; @@ -284,15 +293,16 @@ const VMStateInfo vmstate_info_uint32_equal = { /* 64 bit unsigned int */ -static int get_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_uint64(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { uint64_t *v = pv; qemu_get_be64s(f, v); return 0; } -static int put_uint64(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_uint64(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { uint64_t *v = pv; qemu_put_be64s(f, v); @@ -305,7 +315,8 @@ const VMStateInfo vmstate_info_uint64 = { .put = put_uint64, }; -static int get_nullptr(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_nullptr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { if (qemu_get_byte(f) == VMS_NULLPTR_MARKER) { @@ -316,7 +327,7 @@ static int get_nullptr(QEMUFile *f, void *pv, size_t size, VMStateField *field) } static int put_nullptr(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { if (pv == NULL) { @@ -337,7 +348,7 @@ const VMStateInfo vmstate_info_nullptr = { in the field */ static int get_uint64_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint64_t *v = pv; uint64_t v2; @@ -363,7 +374,7 @@ const VMStateInfo vmstate_info_uint64_equal = { in the field */ static int get_uint8_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint8_t *v = pv; uint8_t v2; @@ -389,7 +400,7 @@ const VMStateInfo vmstate_info_uint8_equal = { in the field */ static int get_uint16_equal(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint16_t *v = pv; uint16_t v2; @@ -414,7 +425,7 @@ const VMStateInfo vmstate_info_uint16_equal = { /* floating point */ static int get_float64(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { float64 *v = pv; @@ -422,8 +433,8 @@ static int get_float64(QEMUFile *f, void *pv, size_t size, return 0; } -static int put_float64(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_float64(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { uint64_t *v = pv; @@ -440,7 +451,7 @@ const VMStateInfo vmstate_info_float64 = { /* CPU_DoubleU type */ static int get_cpudouble(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { CPU_DoubleU *v = pv; qemu_get_be32s(f, &v->l.upper); @@ -449,7 +460,7 @@ static int get_cpudouble(QEMUFile *f, void *pv, size_t size, } static int put_cpudouble(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { CPU_DoubleU *v = pv; qemu_put_be32s(f, &v->l.upper); @@ -466,15 +477,15 @@ const VMStateInfo vmstate_info_cpudouble = { /* uint8_t buffers */ static int get_buffer(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint8_t *v = pv; qemu_get_buffer(f, v, size); return 0; } -static int put_buffer(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_buffer(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { uint8_t *v = pv; qemu_put_buffer(f, v, size); @@ -491,7 +502,7 @@ const VMStateInfo vmstate_info_buffer = { not useful anymore */ static int get_unused_buffer(QEMUFile *f, void *pv, size_t size, - VMStateField *field) + const VMStateField *field) { uint8_t buf[1024]; int block_len; @@ -505,7 +516,7 @@ static int get_unused_buffer(QEMUFile *f, void *pv, size_t size, } static int put_unused_buffer(QEMUFile *f, void *pv, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { static const uint8_t buf[1024]; int block_len; @@ -531,7 +542,8 @@ const VMStateInfo vmstate_info_unused_buffer = { * in fields that don't really exist in the parent but need to be in the * stream. */ -static int get_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_tmp(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int ret; const VMStateDescription *vmsd = field->vmsd; @@ -545,8 +557,8 @@ static int get_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field) return ret; } -static int put_tmp(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_tmp(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { const VMStateDescription *vmsd = field->vmsd; void *tmp = g_malloc(size); @@ -573,7 +585,8 @@ const VMStateInfo vmstate_info_tmp = { */ /* This is the number of 64 bit words sent over the wire */ #define BITS_TO_U64S(nr) DIV_ROUND_UP(nr, 64) -static int get_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_bitmap(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { unsigned long *bmp = pv; int i, idx = 0; @@ -587,8 +600,8 @@ static int get_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field) return 0; } -static int put_bitmap(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_bitmap(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { unsigned long *bmp = pv; int i, idx = 0; @@ -613,7 +626,7 @@ const VMStateInfo vmstate_info_bitmap = { * meta data about the QTAILQ is encoded in a VMStateField structure */ static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, - VMStateField *field) + const VMStateField *field) { int ret = 0; const VMStateDescription *vmsd = field->vmsd; @@ -652,7 +665,7 @@ static int get_qtailq(QEMUFile *f, void *pv, size_t unused_size, /* put for QTAILQ */ static int put_qtailq(QEMUFile *f, void *pv, size_t unused_size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { const VMStateDescription *vmsd = field->vmsd; /* offset of the QTAILQ entry in a QTAILQ element*/ diff --git a/migration/vmstate.c b/migration/vmstate.c index 6b9079bb51..e2bbb7b5f7 100644 --- a/migration/vmstate.c +++ b/migration/vmstate.c @@ -26,7 +26,7 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, static int vmstate_subsection_load(QEMUFile *f, const VMStateDescription *vmsd, void *opaque); -static int vmstate_n_elems(void *opaque, VMStateField *field) +static int vmstate_n_elems(void *opaque, const VMStateField *field) { int n_elems = 1; @@ -50,7 +50,7 @@ static int vmstate_n_elems(void *opaque, VMStateField *field) return n_elems; } -static int vmstate_size(void *opaque, VMStateField *field) +static int vmstate_size(void *opaque, const VMStateField *field) { int size = field->size; @@ -64,7 +64,8 @@ static int vmstate_size(void *opaque, VMStateField *field) return size; } -static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque) +static void vmstate_handle_alloc(void *ptr, const VMStateField *field, + void *opaque) { if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) { gsize size = vmstate_size(opaque, field); @@ -78,7 +79,7 @@ static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque) int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, int version_id) { - VMStateField *field = vmsd->fields; + const VMStateField *field = vmsd->fields; int ret = 0; trace_vmstate_load_state(vmsd->name, version_id); @@ -171,9 +172,10 @@ int vmstate_load_state(QEMUFile *f, const VMStateDescription *vmsd, return ret; } -static int vmfield_name_num(VMStateField *start, VMStateField *search) +static int vmfield_name_num(const VMStateField *start, + const VMStateField *search) { - VMStateField *field; + const VMStateField *field; int found = 0; for (field = start; field->name; field++) { @@ -188,9 +190,10 @@ static int vmfield_name_num(VMStateField *start, VMStateField *search) return -1; } -static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search) +static bool vmfield_name_is_unique(const VMStateField *start, + const VMStateField *search) { - VMStateField *field; + const VMStateField *field; int found = 0; for (field = start; field->name; field++) { @@ -206,7 +209,7 @@ static bool vmfield_name_is_unique(VMStateField *start, VMStateField *search) return true; } -static const char *vmfield_get_type_name(VMStateField *field) +static const char *vmfield_get_type_name(const VMStateField *field) { const char *type = "unknown"; @@ -221,7 +224,7 @@ static const char *vmfield_get_type_name(VMStateField *field) return type; } -static bool vmsd_can_compress(VMStateField *field) +static bool vmsd_can_compress(const VMStateField *field) { if (field->field_exists) { /* Dynamically existing fields mess up compression */ @@ -229,7 +232,7 @@ static bool vmsd_can_compress(VMStateField *field) } if (field->flags & VMS_STRUCT) { - VMStateField *sfield = field->vmsd->fields; + const VMStateField *sfield = field->vmsd->fields; while (sfield->name) { if (!vmsd_can_compress(sfield)) { /* Child elements can't compress, so can't we */ @@ -248,7 +251,7 @@ static bool vmsd_can_compress(VMStateField *field) } static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc, - VMStateField *field, int i, int max) + const VMStateField *field, int i, int max) { char *name, *old_name; bool is_array = max > 1; @@ -287,7 +290,7 @@ static void vmsd_desc_field_start(const VMStateDescription *vmsd, QJSON *vmdesc, } static void vmsd_desc_field_end(const VMStateDescription *vmsd, QJSON *vmdesc, - VMStateField *field, size_t size, int i) + const VMStateField *field, size_t size, int i) { if (!vmdesc) { return; @@ -323,7 +326,7 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, void *opaque, QJSON *vmdesc, int version_id) { int ret = 0; - VMStateField *field = vmsd->fields; + const VMStateField *field = vmsd->fields; trace_vmstate_save_state_top(vmsd->name); @@ -387,6 +390,9 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, if (ret) { error_report("Save of field %s/%s failed", vmsd->name, field->name); + if (vmsd->post_save) { + vmsd->post_save(opaque); + } return ret; } @@ -412,13 +418,21 @@ int vmstate_save_state_v(QEMUFile *f, const VMStateDescription *vmsd, json_end_array(vmdesc); } - return vmstate_subsection_save(f, vmsd, opaque, vmdesc); + ret = vmstate_subsection_save(f, vmsd, opaque, vmdesc); + + if (vmsd->post_save) { + int ps_ret = vmsd->post_save(opaque); + if (!ret) { + ret = ps_ret; + } + } + return ret; } static const VMStateDescription * vmstate_get_subsection(const VMStateDescription **sub, char *idstr) { - while (sub && *sub && (*sub)->needed) { + while (sub && *sub) { if (strcmp(idstr, (*sub)->name) == 0) { return *sub; } @@ -486,8 +500,8 @@ static int vmstate_subsection_save(QEMUFile *f, const VMStateDescription *vmsd, int ret = 0; trace_vmstate_subsection_save_top(vmsd->name); - while (sub && *sub && (*sub)->needed) { - if ((*sub)->needed(opaque)) { + while (sub && *sub) { + if (vmstate_save_needed(*sub, opaque)) { const VMStateDescription *vmsdsub = *sub; uint8_t len; diff --git a/monitor.c b/monitor.c index 77861e96af..4807bbe811 100644 --- a/monitor.c +++ b/monitor.c @@ -51,14 +51,14 @@ #include "sysemu/balloon.h" #include "qemu/timer.h" #include "sysemu/hw_accel.h" -#include "qemu/acl.h" +#include "authz/list.h" +#include "qapi/util.h" #include "sysemu/tpm.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qerror.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qapi/qmp/qjson.h" -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qlist.h" #include "qom/object_interfaces.h" @@ -76,7 +76,7 @@ #include "qemu/thread.h" #include "block/qapi.h" #include "qapi/qapi-commands.h" -#include "qapi/qapi-events.h" +#include "qapi/qapi-emit-events.h" #include "qapi/error.h" #include "qapi/qmp-event.h" #include "qapi/qapi-introspect.h" @@ -84,6 +84,7 @@ #include "sysemu/cpus.h" #include "sysemu/iothread.h" #include "qemu/cutils.h" +#include "tcg/tcg.h" #if defined(TARGET_S390X) #include "hw/s390x/storage-keys.h" @@ -183,8 +184,6 @@ typedef struct { QemuMutex qmp_queue_lock; /* Input queue that holds all the parsed QMP requests */ GQueue *qmp_requests; - /* Output queue contains all the QMP responses in order */ - GQueue *qmp_responses; } MonitorQMP; /* @@ -248,40 +247,30 @@ IOThread *mon_iothread; /* Bottom half to dispatch the requests received from I/O thread */ QEMUBH *qmp_dispatcher_bh; -/* Bottom half to deliver the responses back to clients */ -QEMUBH *qmp_respond_bh; - struct QMPRequest { /* Owner of the request */ Monitor *mon; - /* "id" field of the request */ - QObject *id; /* * Request object to be handled or Error to be reported * (exactly one of them is non-null) */ QObject *req; Error *err; - /* - * Whether we need to resume the monitor afterward. This flag is - * used to emulate the old QMP server behavior that the current - * command must be completed before execution of the next one. - */ - bool need_resume; }; typedef struct QMPRequest QMPRequest; /* QMP checker flags */ #define QMP_ACCEPT_UNKNOWNS 1 -/* Protects mon_list, monitor_qapi_event_state. */ +/* Protects mon_list, monitor_qapi_event_state, monitor_destroyed. */ static QemuMutex monitor_lock; static GHashTable *monitor_qapi_event_state; -static QTAILQ_HEAD(mon_list, Monitor) mon_list; +static QTAILQ_HEAD(, Monitor) mon_list; +static bool monitor_destroyed; /* Protects mon_fdsets */ static QemuMutex mon_fdsets_lock; -static QLIST_HEAD(mon_fdsets, MonFdset) mon_fdsets; +static QLIST_HEAD(, MonFdset) mon_fdsets; static int mon_refcount; @@ -362,7 +351,6 @@ int monitor_read_password(Monitor *mon, ReadLineFunc *readline_func, static void qmp_request_free(QMPRequest *req) { - qobject_unref(req->id); qobject_unref(req->req); error_free(req->err); g_free(req); @@ -376,19 +364,10 @@ static void monitor_qmp_cleanup_req_queue_locked(Monitor *mon) } } -/* Caller must hold the mon->qmp.qmp_queue_lock */ -static void monitor_qmp_cleanup_resp_queue_locked(Monitor *mon) -{ - while (!g_queue_is_empty(mon->qmp.qmp_responses)) { - qobject_unref((QDict *)g_queue_pop_head(mon->qmp.qmp_responses)); - } -} - static void monitor_qmp_cleanup_queues(Monitor *mon) { qemu_mutex_lock(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); - monitor_qmp_cleanup_resp_queue_locked(mon); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); } @@ -504,9 +483,9 @@ int monitor_fprintf(FILE *stream, const char *fmt, ...) return 0; } -static void qmp_send_response(Monitor *mon, QDict *rsp) +static void qmp_send_response(Monitor *mon, const QDict *rsp) { - QObject *data = QOBJECT(rsp); + const QObject *data = QOBJECT(rsp); QString *json; json = mon->flags & MONITOR_USE_PRETTY ? qobject_to_json_pretty(data) : @@ -519,85 +498,6 @@ static void qmp_send_response(Monitor *mon, QDict *rsp) qobject_unref(json); } -static void qmp_queue_response(Monitor *mon, QDict *rsp) -{ - if (mon->use_io_thread) { - /* - * Push a reference to the response queue. The I/O thread - * drains that queue and emits. - */ - qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - g_queue_push_tail(mon->qmp.qmp_responses, qobject_ref(rsp)); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - qemu_bh_schedule(qmp_respond_bh); - } else { - /* - * Not using monitor I/O thread, i.e. we are in the main thread. - * Emit right away. - */ - qmp_send_response(mon, rsp); - } -} - -struct QMPResponse { - Monitor *mon; - QDict *data; -}; -typedef struct QMPResponse QMPResponse; - -static QDict *monitor_qmp_response_pop_one(Monitor *mon) -{ - QDict *data; - - qemu_mutex_lock(&mon->qmp.qmp_queue_lock); - data = g_queue_pop_head(mon->qmp.qmp_responses); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - - return data; -} - -static void monitor_qmp_response_flush(Monitor *mon) -{ - QDict *data; - - while ((data = monitor_qmp_response_pop_one(mon))) { - qmp_send_response(mon, data); - qobject_unref(data); - } -} - -/* - * Pop a QMPResponse from any monitor's response queue into @response. - * Return false if all the queues are empty; else true. - */ -static bool monitor_qmp_response_pop_any(QMPResponse *response) -{ - Monitor *mon; - QDict *data = NULL; - - qemu_mutex_lock(&monitor_lock); - QTAILQ_FOREACH(mon, &mon_list, entry) { - data = monitor_qmp_response_pop_one(mon); - if (data) { - response->mon = mon; - response->data = data; - break; - } - } - qemu_mutex_unlock(&monitor_lock); - return data != NULL; -} - -static void monitor_qmp_bh_responder(void *opaque) -{ - QMPResponse response; - - while (monitor_qmp_response_pop_any(&response)) { - qmp_send_response(response.mon, response.data); - qobject_unref(response.data); - } -} - static MonitorQAPIEventConf monitor_qapi_event_conf[QAPI_EVENT__MAX] = { /* Limit guest-triggerable events to 1 per second */ [QAPI_EVENT_RTC_CHANGE] = { 1000 * SCALE_MS }, @@ -621,7 +521,7 @@ static void monitor_qapi_event_emit(QAPIEvent event, QDict *qdict) QTAILQ_FOREACH(mon, &mon_list, entry) { if (monitor_is_qmp(mon) && mon->qmp.commands != &qmp_cap_negotiation_commands) { - qmp_queue_response(mon, qdict); + qmp_send_response(mon, qdict); } } } @@ -688,8 +588,7 @@ monitor_qapi_event_queue_no_reenter(QAPIEvent event, QDict *qdict) qemu_mutex_unlock(&monitor_lock); } -static void -monitor_qapi_event_queue(QAPIEvent event, QDict *qdict, Error **errp) +void qapi_event_emit(QAPIEvent event, QDict *qdict) { /* * monitor_qapi_event_queue_no_reenter() is not reentrant: it @@ -802,14 +701,18 @@ static void monitor_qapi_event_init(void) { 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); } static void handle_hmp_command(Monitor *mon, const char *cmdline); +static void monitor_iothread_init(void); + static void monitor_data_init(Monitor *mon, bool skip_flush, bool use_io_thread) { + if (use_io_thread && !mon_iothread) { + monitor_iothread_init(); + } memset(mon, 0, sizeof(Monitor)); qemu_mutex_init(&mon->mon_lock); qemu_mutex_init(&mon->qmp.qmp_queue_lock); @@ -819,7 +722,6 @@ static void monitor_data_init(Monitor *mon, bool skip_flush, mon->skip_flush = skip_flush; mon->use_io_thread = use_io_thread; mon->qmp.qmp_requests = g_queue_new(); - mon->qmp.qmp_responses = g_queue_new(); } static void monitor_data_destroy(Monitor *mon) @@ -834,9 +736,7 @@ static void monitor_data_destroy(Monitor *mon) qemu_mutex_destroy(&mon->mon_lock); qemu_mutex_destroy(&mon->qmp.qmp_queue_lock); monitor_qmp_cleanup_req_queue_locked(mon); - monitor_qmp_cleanup_resp_queue_locked(mon); g_queue_free(mon->qmp.qmp_requests); - g_queue_free(mon->qmp.qmp_responses); } char *qmp_human_monitor_command(const char *command_line, bool has_cpu_index, @@ -1055,6 +955,7 @@ static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds, char **args, int nb_args, int arg_index) { const mon_cmd_t *cmd; + size_t i; /* No valid arg need to compare with, dump all in *cmds */ if (arg_index >= nb_args) { @@ -1076,9 +977,15 @@ static void help_cmd_dump(Monitor *mon, const mon_cmd_t *cmds, } else { help_cmd_dump_one(mon, cmd, args, arg_index); } - break; + return; } } + + /* Command not found */ + monitor_printf(mon, "unknown command: '"); + for (i = 0; i <= arg_index; i++) { + monitor_printf(mon, "%s%s", args[i], i == arg_index ? "'\n" : " "); + } } static void help_cmd(Monitor *mon, const char *name) @@ -1190,6 +1097,11 @@ CommandInfoList *qmp_query_commands(Error **errp) EventInfoList *qmp_query_events(Error **errp) { + /* + * TODO This deprecated command is the only user of + * QAPIEvent_str() and QAPIEvent_lookup[]. When the command goes, + * they should go, too. + */ EventInfoList *info, *ev_list = NULL; QAPIEvent e; @@ -1222,50 +1134,6 @@ static void qmp_query_qmp_schema(QDict *qdict, QObject **ret_data, *ret_data = qobject_from_qlit(&qmp_schema_qlit); } -/* - * We used to define commands in qmp-commands.hx in addition to the - * QAPI schema. This permitted defining some of them only in certain - * configurations. query-commands has always reflected that (good, - * because it lets QMP clients figure out what's actually available), - * while query-qmp-schema never did (not so good). This function is a - * hack to keep the configuration-specific commands defined exactly as - * before, even though qmp-commands.hx is gone. - * - * FIXME Educate the QAPI schema on configuration-specific commands, - * and drop this hack. - */ -static void qmp_unregister_commands_hack(void) -{ -#ifndef CONFIG_REPLICATION - qmp_unregister_command(&qmp_commands, "xen-set-replication"); - qmp_unregister_command(&qmp_commands, "query-xen-replication-status"); - qmp_unregister_command(&qmp_commands, "xen-colo-do-checkpoint"); -#endif -#ifndef TARGET_I386 - qmp_unregister_command(&qmp_commands, "rtc-reset-reinjection"); - qmp_unregister_command(&qmp_commands, "query-sev"); - qmp_unregister_command(&qmp_commands, "query-sev-launch-measure"); - qmp_unregister_command(&qmp_commands, "query-sev-capabilities"); -#endif -#ifndef TARGET_S390X - qmp_unregister_command(&qmp_commands, "dump-skeys"); -#endif -#ifndef TARGET_ARM - qmp_unregister_command(&qmp_commands, "query-gic-capabilities"); -#endif -#if !defined(TARGET_S390X) && !defined(TARGET_I386) - qmp_unregister_command(&qmp_commands, "query-cpu-model-expansion"); -#endif -#if !defined(TARGET_S390X) - qmp_unregister_command(&qmp_commands, "query-cpu-model-baseline"); - qmp_unregister_command(&qmp_commands, "query-cpu-model-comparison"); -#endif -#if !defined(TARGET_PPC) && !defined(TARGET_ARM) && !defined(TARGET_I386) \ - && !defined(TARGET_S390X) - qmp_unregister_command(&qmp_commands, "query-cpu-definitions"); -#endif -} - static void monitor_init_qmp_commands(void) { /* @@ -1284,8 +1152,6 @@ static void monitor_init_qmp_commands(void) qmp_register_command(&qmp_commands, "netdev_add", qmp_netdev_add, QCO_NO_OPTIONS); - qmp_unregister_commands_hack(); - QTAILQ_INIT(&qmp_cap_negotiation_commands); qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities", qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG); @@ -1454,6 +1320,17 @@ static void hmp_info_opcount(Monitor *mon, const QDict *qdict) } #endif +static void hmp_info_sync_profile(Monitor *mon, const QDict *qdict) +{ + int64_t max = qdict_get_try_int(qdict, "max", 10); + bool mean = qdict_get_try_bool(qdict, "mean", false); + bool coalesce = !qdict_get_try_bool(qdict, "no_coalesce", false); + enum QSPSortBy sort_by; + + sort_by = mean ? QSP_SORT_BY_AVG_WAIT_TIME : QSP_SORT_BY_TOTAL_WAIT_TIME; + qsp_report((FILE *)mon, monitor_fprintf, max, sort_by, coalesce); +} + static void hmp_info_history(Monitor *mon, const QDict *qdict) { int i; @@ -1683,7 +1560,13 @@ static void memory_dump(Monitor *mon, int count, int format, int wsize, if (l > line_size) l = line_size; if (is_physical) { - cpu_physical_memory_read(addr, buf, l); + AddressSpace *as = cs ? cs->as : &address_space_memory; + MemTxResult r = address_space_read(as, addr, + MEMTXATTRS_UNSPECIFIED, buf, l); + if (r != MEMTX_OK) { + monitor_printf(mon, " Cannot access memory\n"); + break; + } } else { if (cpu_memory_rw_debug(cs, addr, buf, l, 0) < 0) { monitor_printf(mon, " Cannot access memory\n"); @@ -2051,16 +1934,22 @@ static void hmp_info_numa(Monitor *mon, const QDict *qdict) #ifdef CONFIG_PROFILER -int64_t tcg_time; int64_t dev_time; static void hmp_info_profile(Monitor *mon, const QDict *qdict) { + static int64_t last_cpu_exec_time; + int64_t cpu_exec_time; + int64_t delta; + + cpu_exec_time = tcg_cpu_exec_time(); + delta = cpu_exec_time - last_cpu_exec_time; + monitor_printf(mon, "async time %" PRId64 " (%0.3f)\n", dev_time, dev_time / (double)NANOSECONDS_PER_SECOND); monitor_printf(mon, "qemu time %" PRId64 " (%0.3f)\n", - tcg_time, tcg_time / (double)NANOSECONDS_PER_SECOND); - tcg_time = 0; + delta, delta / (double)NANOSECONDS_PER_SECOND); + last_cpu_exec_time = cpu_exec_time; dev_time = 0; } #else @@ -2125,93 +2014,169 @@ static void hmp_wavcapture(Monitor *mon, const QDict *qdict) QLIST_INSERT_HEAD (&capture_head, s, entries); } -static qemu_acl *find_acl(Monitor *mon, const char *name) +static QAuthZList *find_auth(Monitor *mon, const char *name) { - qemu_acl *acl = qemu_acl_find(name); + Object *obj; + Object *container; - if (!acl) { + container = object_get_objects_root(); + obj = object_resolve_path_component(container, name); + if (!obj) { monitor_printf(mon, "acl: unknown list '%s'\n", name); + return NULL; } - return acl; + + return QAUTHZ_LIST(obj); +} + +static bool warn_acl; +static void hmp_warn_acl(void) +{ + if (warn_acl) { + return; + } + error_report("The acl_show, acl_reset, acl_policy, acl_add, acl_remove " + "commands are deprecated with no replacement. Authorization " + "for VNC should be performed using the pluggable QAuthZ " + "objects"); + warn_acl = true; } static void hmp_acl_show(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); - qemu_acl *acl = find_acl(mon, aclname); - qemu_acl_entry *entry; - int i = 0; + QAuthZList *auth = find_auth(mon, aclname); + QAuthZListRuleList *rules; + size_t i = 0; - if (acl) { - monitor_printf(mon, "policy: %s\n", - acl->defaultDeny ? "deny" : "allow"); - QTAILQ_FOREACH(entry, &acl->entries, next) { - i++; - monitor_printf(mon, "%d: %s %s\n", i, - entry->deny ? "deny" : "allow", entry->match); - } + hmp_warn_acl(); + + if (!auth) { + return; + } + + monitor_printf(mon, "policy: %s\n", + QAuthZListPolicy_str(auth->policy)); + + rules = auth->rules; + while (rules) { + QAuthZListRule *rule = rules->value; + i++; + monitor_printf(mon, "%zu: %s %s\n", i, + QAuthZListPolicy_str(rule->policy), + rule->match); + rules = rules->next; } } static void hmp_acl_reset(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); - qemu_acl *acl = find_acl(mon, aclname); + QAuthZList *auth = find_auth(mon, aclname); - if (acl) { - qemu_acl_reset(acl); - monitor_printf(mon, "acl: removed all rules\n"); + hmp_warn_acl(); + + if (!auth) { + return; } + + auth->policy = QAUTHZ_LIST_POLICY_DENY; + qapi_free_QAuthZListRuleList(auth->rules); + auth->rules = NULL; + monitor_printf(mon, "acl: removed all rules\n"); } static void hmp_acl_policy(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); const char *policy = qdict_get_str(qdict, "policy"); - qemu_acl *acl = find_acl(mon, aclname); + QAuthZList *auth = find_auth(mon, aclname); + int val; + Error *err = NULL; - if (acl) { - if (strcmp(policy, "allow") == 0) { - acl->defaultDeny = 0; + hmp_warn_acl(); + + if (!auth) { + return; + } + + val = qapi_enum_parse(&QAuthZListPolicy_lookup, + policy, + QAUTHZ_LIST_POLICY_DENY, + &err); + if (err) { + error_free(err); + monitor_printf(mon, "acl: unknown policy '%s', " + "expected 'deny' or 'allow'\n", policy); + } else { + auth->policy = val; + if (auth->policy == QAUTHZ_LIST_POLICY_ALLOW) { monitor_printf(mon, "acl: policy set to 'allow'\n"); - } else if (strcmp(policy, "deny") == 0) { - acl->defaultDeny = 1; - monitor_printf(mon, "acl: policy set to 'deny'\n"); } else { - monitor_printf(mon, "acl: unknown policy '%s', " - "expected 'deny' or 'allow'\n", policy); + monitor_printf(mon, "acl: policy set to 'deny'\n"); } } } +static QAuthZListFormat hmp_acl_get_format(const char *match) +{ + if (strchr(match, '*')) { + return QAUTHZ_LIST_FORMAT_GLOB; + } else { + return QAUTHZ_LIST_FORMAT_EXACT; + } +} + static void hmp_acl_add(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); const char *match = qdict_get_str(qdict, "match"); - const char *policy = qdict_get_str(qdict, "policy"); + const char *policystr = qdict_get_str(qdict, "policy"); int has_index = qdict_haskey(qdict, "index"); int index = qdict_get_try_int(qdict, "index", -1); - qemu_acl *acl = find_acl(mon, aclname); - int deny, ret; + QAuthZList *auth = find_auth(mon, aclname); + Error *err = NULL; + QAuthZListPolicy policy; + QAuthZListFormat format; + size_t i = 0; - if (acl) { - if (strcmp(policy, "allow") == 0) { - deny = 0; - } else if (strcmp(policy, "deny") == 0) { - deny = 1; - } else { - monitor_printf(mon, "acl: unknown policy '%s', " - "expected 'deny' or 'allow'\n", policy); - return; - } - if (has_index) - ret = qemu_acl_insert(acl, deny, match, index); - else - ret = qemu_acl_append(acl, deny, match); - if (ret < 0) - monitor_printf(mon, "acl: unable to add acl entry\n"); - else - monitor_printf(mon, "acl: added rule at position %d\n", ret); + hmp_warn_acl(); + + if (!auth) { + return; + } + + policy = qapi_enum_parse(&QAuthZListPolicy_lookup, + policystr, + QAUTHZ_LIST_POLICY_DENY, + &err); + if (err) { + error_free(err); + monitor_printf(mon, "acl: unknown policy '%s', " + "expected 'deny' or 'allow'\n", policystr); + return; + } + + format = hmp_acl_get_format(match); + + if (has_index && index == 0) { + monitor_printf(mon, "acl: unable to add acl entry\n"); + return; + } + + if (has_index) { + i = qauthz_list_insert_rule(auth, match, policy, + format, index - 1, &err); + } else { + i = qauthz_list_append_rule(auth, match, policy, + format, &err); + } + if (err) { + monitor_printf(mon, "acl: unable to add rule: %s", + error_get_pretty(err)); + error_free(err); + } else { + monitor_printf(mon, "acl: added rule at position %zu\n", i + 1); } } @@ -2219,15 +2184,20 @@ static void hmp_acl_remove(Monitor *mon, const QDict *qdict) { const char *aclname = qdict_get_str(qdict, "aclname"); const char *match = qdict_get_str(qdict, "match"); - qemu_acl *acl = find_acl(mon, aclname); - int ret; + QAuthZList *auth = find_auth(mon, aclname); + ssize_t i = 0; - if (acl) { - ret = qemu_acl_remove(acl, match); - if (ret < 0) - monitor_printf(mon, "acl: no matching acl entry\n"); - else - monitor_printf(mon, "acl: removed rule at position %d\n", ret); + hmp_warn_acl(); + + if (!auth) { + return; + } + + i = qauthz_list_delete_rule(auth, match); + if (i >= 0) { + monitor_printf(mon, "acl: removed rule at position %zu\n", i + 1); + } else { + monitor_printf(mon, "acl: no matching acl entry\n"); } } @@ -3304,7 +3274,7 @@ static QDict *monitor_parse_arguments(Monitor *mon, { int ret; uint64_t val; - char *end; + const char *end; while (qemu_isspace(*p)) { p++; @@ -4135,18 +4105,14 @@ static int monitor_can_read(void *opaque) * Null @rsp can only happen for commands with QCO_NO_SUCCESS_RESP. * Nothing is emitted then. */ -static void monitor_qmp_respond(Monitor *mon, QDict *rsp, QObject *id) +static void monitor_qmp_respond(Monitor *mon, QDict *rsp) { if (rsp) { - if (id) { - qdict_put_obj(rsp, "id", qobject_ref(id)); - } - - qmp_queue_response(mon, rsp); + qmp_send_response(mon, rsp); } } -static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) +static void monitor_qmp_dispatch(Monitor *mon, QObject *req) { Monitor *old_mon; QDict *rsp; @@ -4171,7 +4137,7 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) } } - monitor_qmp_respond(mon, rsp, id); + monitor_qmp_respond(mon, rsp); qobject_unref(rsp); } @@ -4182,8 +4148,12 @@ static void monitor_qmp_dispatch(Monitor *mon, QObject *req, QObject *id) * processing commands only on a very busy monitor. To achieve that, * when we process one request on a specific monitor, we put that * monitor to the end of mon_list queue. + * + * Note: if the function returned with non-NULL, then the caller will + * be with mon->qmp.qmp_queue_lock held, and the caller is responsible + * to release it. */ -static QMPRequest *monitor_qmp_requests_pop_any(void) +static QMPRequest *monitor_qmp_requests_pop_any_with_lock(void) { QMPRequest *req_obj = NULL; Monitor *mon; @@ -4193,10 +4163,11 @@ static QMPRequest *monitor_qmp_requests_pop_any(void) QTAILQ_FOREACH(mon, &mon_list, entry) { qemu_mutex_lock(&mon->qmp.qmp_queue_lock); req_obj = g_queue_pop_head(mon->qmp.qmp_requests); - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); if (req_obj) { + /* With the lock of corresponding queue held */ break; } + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); } if (req_obj) { @@ -4215,27 +4186,36 @@ static QMPRequest *monitor_qmp_requests_pop_any(void) static void monitor_qmp_bh_dispatcher(void *data) { - QMPRequest *req_obj = monitor_qmp_requests_pop_any(); + QMPRequest *req_obj = monitor_qmp_requests_pop_any_with_lock(); QDict *rsp; + bool need_resume; + Monitor *mon; if (!req_obj) { return; } + mon = req_obj->mon; + /* qmp_oob_enabled() might change after "qmp_capabilities" */ + need_resume = !qmp_oob_enabled(mon) || + mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1; + qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); if (req_obj->req) { - trace_monitor_qmp_cmd_in_band(qobject_get_try_str(req_obj->id) ?: ""); - monitor_qmp_dispatch(req_obj->mon, req_obj->req, req_obj->id); + QDict *qdict = qobject_to(QDict, req_obj->req); + QObject *id = qdict ? qdict_get(qdict, "id") : NULL; + trace_monitor_qmp_cmd_in_band(qobject_get_try_str(id) ?: ""); + monitor_qmp_dispatch(mon, req_obj->req); } else { assert(req_obj->err); rsp = qmp_error_response(req_obj->err); req_obj->err = NULL; - monitor_qmp_respond(req_obj->mon, rsp, NULL); + monitor_qmp_respond(mon, rsp); qobject_unref(rsp); } - if (req_obj->need_resume) { + if (need_resume) { /* Pairs with the monitor_suspend() in handle_qmp_command() */ - monitor_resume(req_obj->mon); + monitor_resume(mon); } qmp_request_free(req_obj); @@ -4243,27 +4223,18 @@ static void monitor_qmp_bh_dispatcher(void *data) qemu_bh_schedule(qmp_dispatcher_bh); } -#define QMP_REQ_QUEUE_LEN_MAX (8) - -static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) +static void handle_qmp_command(void *opaque, QObject *req, Error *err) { - QObject *req, *id = NULL; + Monitor *mon = opaque; + QObject *id = NULL; QDict *qdict; - MonitorQMP *mon_qmp = container_of(parser, MonitorQMP, parser); - Monitor *mon = container_of(mon_qmp, Monitor, qmp); - Error *err = NULL; QMPRequest *req_obj; - req = json_parser_parse_err(tokens, NULL, &err); - if (!req && !err) { - /* json_parser_parse_err() sucks: can fail without setting @err */ - error_setg(&err, QERR_JSON_PARSING); - } + assert(!req != !err); qdict = qobject_to(QDict, req); if (qdict) { - id = qobject_ref(qdict_get(qdict, "id")); - qdict_del(qdict, "id"); + id = qdict_get(qdict, "id"); } /* else will fail qmp_dispatch() */ if (req && trace_event_get_state_backends(TRACE_HANDLE_QMP_COMMAND)) { @@ -4274,54 +4245,37 @@ static void handle_qmp_command(JSONMessageParser *parser, GQueue *tokens) if (qdict && qmp_is_oob(qdict)) { /* OOB commands are executed immediately */ - trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) - ?: ""); - monitor_qmp_dispatch(mon, req, id); + trace_monitor_qmp_cmd_out_of_band(qobject_get_try_str(id) ?: ""); + monitor_qmp_dispatch(mon, req); + qobject_unref(req); return; } req_obj = g_new0(QMPRequest, 1); req_obj->mon = mon; - req_obj->id = id; req_obj->req = req; req_obj->err = err; - req_obj->need_resume = false; /* Protect qmp_requests and fetching its length. */ qemu_mutex_lock(&mon->qmp.qmp_queue_lock); /* - * If OOB is not enabled on the current monitor, we'll emulate the - * old behavior that we won't process the current monitor any more - * until it has responded. This helps make sure that as long as - * OOB is not enabled, the server will never drop any command. + * Suspend the monitor when we can't queue more requests after + * this one. Dequeuing in monitor_qmp_bh_dispatcher() will resume + * it. Note that when OOB is disabled, we queue at most one + * command, for backward compatibility. */ - if (!qmp_oob_enabled(mon)) { + if (!qmp_oob_enabled(mon) || + mon->qmp.qmp_requests->length == QMP_REQ_QUEUE_LEN_MAX - 1) { monitor_suspend(mon); - req_obj->need_resume = true; - } else { - /* Drop the request if queue is full. */ - if (mon->qmp.qmp_requests->length >= QMP_REQ_QUEUE_LEN_MAX) { - qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); - /* - * FIXME @id's scope is just @mon, and broadcasting it is - * wrong. If another monitor's client has a command with - * the same ID in flight, the event will incorrectly claim - * that command was dropped. - */ - qapi_event_send_command_dropped(id, - COMMAND_DROP_REASON_QUEUE_FULL, - &error_abort); - qmp_request_free(req_obj); - return; - } } /* * Put the request to the end of queue so that requests will be - * handled in time order. Ownership for req_obj, req, id, + * handled in time order. Ownership for req_obj, req, * etc. will be delivered to the handler side. */ + assert(mon->qmp.qmp_requests->length < QMP_REQ_QUEUE_LEN_MAX); g_queue_push_tail(mon->qmp.qmp_requests, req_obj); qemu_mutex_unlock(&mon->qmp.qmp_queue_lock); @@ -4374,7 +4328,7 @@ int monitor_suspend(Monitor *mon) atomic_inc(&mon->suspend_cnt); - if (monitor_is_qmp(mon)) { + if (mon->use_io_thread) { /* * Kick I/O thread to make sure this takes effect. It'll be * evaluated again in prepare() of the watch object. @@ -4386,6 +4340,13 @@ int monitor_suspend(Monitor *mon) return 0; } +static void monitor_accept_input(void *opaque) +{ + Monitor *mon = opaque; + + qemu_chr_fe_accept_input(&mon->chr); +} + void monitor_resume(Monitor *mon) { if (monitor_is_hmp_non_interactive(mon)) { @@ -4393,19 +4354,22 @@ void monitor_resume(Monitor *mon) } if (atomic_dec_fetch(&mon->suspend_cnt) == 0) { - if (monitor_is_qmp(mon)) { - /* - * For QMP monitors that are running in the I/O thread, - * let's kick the thread in case it's sleeping. - */ - if (mon->use_io_thread) { - aio_notify(iothread_get_aio_context(mon_iothread)); - } + AioContext *ctx; + + if (mon->use_io_thread) { + ctx = iothread_get_aio_context(mon_iothread); } else { + ctx = qemu_get_aio_context(); + } + + if (!monitor_is_qmp(mon)) { assert(mon->rs); readline_show_prompt(mon->rs); } + + aio_bh_schedule_oneshot(ctx, monitor_accept_input, mon); } + trace_monitor_suspend(mon, -1); } @@ -4438,7 +4402,7 @@ static void monitor_qmp_event(void *opaque, int event) mon->qmp.commands = &qmp_cap_negotiation_commands; monitor_qmp_caps_reset(mon); data = qmp_greeting(mon); - qmp_queue_response(mon, data); + qmp_send_response(mon, data); qobject_unref(data); mon_refcount++; break; @@ -4449,10 +4413,10 @@ static void monitor_qmp_event(void *opaque, int event) * stdio, it's possible that stdout is still open when stdin * is closed. */ - monitor_qmp_response_flush(mon); monitor_qmp_cleanup_queues(mon); json_message_parser_destroy(&mon->qmp.parser); - json_message_parser_init(&mon->qmp.parser, handle_qmp_command); + json_message_parser_init(&mon->qmp.parser, handle_qmp_command, + mon, NULL); mon_refcount--; monitor_fdsets_cleanup(); break; @@ -4529,37 +4493,9 @@ static void sortcmdlist(void) qsort((void *)info_cmds, array_num, elem_size, compare_mon_cmd); } -static GMainContext *monitor_get_io_context(void) -{ - return iothread_get_g_main_context(mon_iothread); -} - -static AioContext *monitor_get_aio_context(void) -{ - return iothread_get_aio_context(mon_iothread); -} - static void monitor_iothread_init(void) { mon_iothread = iothread_create("mon_iothread", &error_abort); - - /* - * The dispatcher BH must run in the main loop thread, since we - * have commands assuming that context. It would be nice to get - * rid of those assumptions. - */ - qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), - monitor_qmp_bh_dispatcher, - NULL); - - /* - * The responder BH must be run in the monitor I/O thread, so that - * monitors that are using the I/O thread have their output - * written by the I/O thread. - */ - qmp_respond_bh = aio_bh_new(monitor_get_aio_context(), - monitor_qmp_bh_responder, - NULL); } void monitor_init_globals(void) @@ -4569,7 +4505,15 @@ void monitor_init_globals(void) sortcmdlist(); qemu_mutex_init(&monitor_lock); qemu_mutex_init(&mon_fdsets_lock); - monitor_iothread_init(); + + /* + * The dispatcher BH must run in the main loop thread, since we + * have commands assuming that context. It would be nice to get + * rid of those assumptions. + */ + qmp_dispatcher_bh = aio_bh_new(iohandler_get_aio_context(), + monitor_qmp_bh_dispatcher, + NULL); } /* These functions just adapt the readline interface in a typesafe way. We @@ -4589,6 +4533,20 @@ static void monitor_readline_flush(void *opaque) monitor_flush(opaque); } +/* + * Print to current monitor if we have one, else to stream. + * TODO should return int, so callers can calculate width, but that + * requires surgery to monitor_vprintf(). Left for another day. + */ +void monitor_vfprintf(FILE *stream, const char *fmt, va_list ap) +{ + if (cur_mon && !monitor_cur_is_qmp()) { + monitor_vprintf(cur_mon, fmt, ap); + } else { + vfprintf(stream, fmt, ap); + } +} + /* * Print to current monitor if we have one, else to stderr. * TODO should return int, so callers can calculate width, but that @@ -4596,11 +4554,7 @@ static void monitor_readline_flush(void *opaque) */ void error_vprintf(const char *fmt, va_list ap) { - if (cur_mon && !monitor_cur_is_qmp()) { - monitor_vprintf(cur_mon, fmt, ap); - } else { - vfprintf(stderr, fmt, ap); - } + monitor_vfprintf(stderr, fmt, ap); } void error_vprintf_unless_qmp(const char *fmt, va_list ap) @@ -4615,8 +4569,21 @@ void error_vprintf_unless_qmp(const char *fmt, va_list ap) static void monitor_list_append(Monitor *mon) { qemu_mutex_lock(&monitor_lock); - QTAILQ_INSERT_HEAD(&mon_list, mon, entry); + /* + * This prevents inserting new monitors during monitor_cleanup(). + * A cleaner solution would involve the main thread telling other + * threads to terminate, waiting for their termination. + */ + if (!monitor_destroyed) { + QTAILQ_INSERT_HEAD(&mon_list, mon, entry); + mon = NULL; + } qemu_mutex_unlock(&monitor_lock); + + if (mon) { + monitor_data_destroy(mon); + g_free(mon); + } } static void monitor_qmp_setup_handlers_bh(void *opaque) @@ -4624,15 +4591,9 @@ static void monitor_qmp_setup_handlers_bh(void *opaque) Monitor *mon = opaque; GMainContext *context; - if (mon->use_io_thread) { - /* Use @mon_iothread context */ - context = monitor_get_io_context(); - assert(context); - } else { - /* Use default main loop context */ - context = NULL; - } - + assert(mon->use_io_thread); + context = iothread_get_g_main_context(mon_iothread); + assert(context); qemu_chr_fe_set_handlers(&mon->chr, monitor_can_read, monitor_qmp_read, monitor_qmp_event, NULL, mon, context, true); monitor_list_append(mon); @@ -4642,21 +4603,12 @@ void monitor_init(Chardev *chr, int flags) { Monitor *mon = g_malloc(sizeof(*mon)); bool use_readline = flags & MONITOR_USE_READLINE; - bool use_oob = flags & MONITOR_USE_OOB; - if (use_oob) { - if (CHARDEV_IS_MUX(chr)) { - error_report("Monitor out-of-band is not supported with " - "MUX typed chardev backend"); - exit(1); - } - if (use_readline) { - error_report("Monitor out-of-band is only supported by QMP"); - exit(1); - } - } - - monitor_data_init(mon, false, use_oob); + /* Note: we run QMP monitor in I/O thread when @chr supports that */ + monitor_data_init(mon, false, + (flags & MONITOR_USE_CONTROL) + && qemu_chr_has_feature(chr, + QEMU_CHAR_FEATURE_GCONTEXT)); qemu_chr_fe_init(&mon->chr, chr, &error_abort); mon->flags = flags; @@ -4670,7 +4622,8 @@ void monitor_init(Chardev *chr, int flags) if (monitor_is_qmp(mon)) { qemu_chr_fe_set_echo(&mon->chr, true); - json_message_parser_init(&mon->qmp.parser, handle_qmp_command); + json_message_parser_init(&mon->qmp.parser, handle_qmp_command, + mon, NULL); if (mon->use_io_thread) { /* * Make sure the old iowatch is gone. It's possible when @@ -4682,7 +4635,7 @@ void monitor_init(Chardev *chr, int flags) * since chardev might be running in the monitor I/O * thread. Schedule a bottom half. */ - aio_bh_schedule_oneshot(monitor_get_aio_context(), + aio_bh_schedule_oneshot(iothread_get_aio_context(mon_iothread), monitor_qmp_setup_handlers_bh, mon); /* The bottom half will add @mon to @mon_list */ return; @@ -4701,28 +4654,27 @@ void monitor_init(Chardev *chr, int flags) void monitor_cleanup(void) { - Monitor *mon, *next; - /* * We need to explicitly stop the I/O thread (but not destroy it), * clean up the monitor resources, then destroy the I/O thread since * we need to unregister from chardev below in * monitor_data_destroy(), and chardev is not thread-safe yet */ - iothread_stop(mon_iothread); - - /* - * Flush all response queues. Note that even after this flush, - * data may remain in output buffers. - */ - monitor_qmp_bh_responder(NULL); + if (mon_iothread) { + iothread_stop(mon_iothread); + } /* Flush output buffers and destroy monitors */ qemu_mutex_lock(&monitor_lock); - QTAILQ_FOREACH_SAFE(mon, &mon_list, entry, next) { + monitor_destroyed = true; + while (!QTAILQ_EMPTY(&mon_list)) { + Monitor *mon = QTAILQ_FIRST(&mon_list); QTAILQ_REMOVE(&mon_list, mon, entry); + /* Permit QAPI event emission from character frontend release */ + qemu_mutex_unlock(&monitor_lock); monitor_flush(mon); monitor_data_destroy(mon); + qemu_mutex_lock(&monitor_lock); g_free(mon); } qemu_mutex_unlock(&monitor_lock); @@ -4730,11 +4682,10 @@ void monitor_cleanup(void) /* QEMUBHs needs to be deleted before destroying the I/O thread */ qemu_bh_delete(qmp_dispatcher_bh); qmp_dispatcher_bh = NULL; - qemu_bh_delete(qmp_respond_bh); - qmp_respond_bh = NULL; - - iothread_destroy(mon_iothread); - mon_iothread = NULL; + if (mon_iothread) { + iothread_destroy(mon_iothread); + mon_iothread = NULL; + } } QemuOptsList qemu_mon_opts = { @@ -4751,54 +4702,11 @@ QemuOptsList qemu_mon_opts = { },{ .name = "pretty", .type = QEMU_OPT_BOOL, - },{ - .name = "x-oob", - .type = QEMU_OPT_BOOL, }, { /* end of list */ } }, }; -#ifndef TARGET_I386 -void qmp_rtc_reset_reinjection(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "rtc-reset-reinjection"); -} - -SevInfo *qmp_query_sev(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "query-sev"); - return NULL; -} - -SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-launch-measure"); - return NULL; -} - -SevCapability *qmp_query_sev_capabilities(Error **errp) -{ - error_setg(errp, QERR_FEATURE_DISABLED, "query-sev-capabilities"); - return NULL; -} -#endif - -#ifndef TARGET_S390X -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 - HotpluggableCPUList *qmp_query_hotpluggable_cpus(Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); diff --git a/nbd/client.c b/nbd/client.c index 40b74d9761..4de30630c7 100644 --- a/nbd/client.c +++ b/nbd/client.c @@ -21,6 +21,7 @@ #include "qapi/error.h" #include "trace.h" #include "nbd-internal.h" +#include "qemu/cutils.h" /* Definitions for opaque data types */ @@ -112,15 +113,14 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, NBDOptionReply *reply, Error **errp) { QEMU_BUILD_BUG_ON(sizeof(*reply) != 20); - if (nbd_read(ioc, reply, sizeof(*reply), errp) < 0) { - error_prepend(errp, "failed to read option reply: "); + if (nbd_read(ioc, reply, sizeof(*reply), "option reply", errp) < 0) { nbd_send_opt_abort(ioc); return -1; } - be64_to_cpus(&reply->magic); - be32_to_cpus(&reply->option); - be32_to_cpus(&reply->type); - be32_to_cpus(&reply->length); + reply->magic = be64_to_cpu(reply->magic); + reply->option = be32_to_cpu(reply->option); + reply->type = be32_to_cpu(reply->type); + reply->length = be32_to_cpu(reply->length); trace_nbd_receive_option_reply(reply->option, nbd_opt_lookup(reply->option), reply->type, nbd_rep_lookup(reply->type), @@ -132,8 +132,9 @@ static int nbd_receive_option_reply(QIOChannel *ioc, uint32_t opt, return -1; } if (reply->option != opt) { - error_setg(errp, "Unexpected option type %x expected %x", - reply->option, opt); + error_setg(errp, "Unexpected option type %u (%s), expected %u (%s)", + reply->option, nbd_opt_lookup(reply->option), + opt, nbd_opt_lookup(opt)); nbd_send_opt_abort(ioc); return -1; } @@ -164,13 +165,15 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, goto cleanup; } msg = g_malloc(reply->length + 1); - if (nbd_read(ioc, msg, reply->length, errp) < 0) { - error_prepend(errp, "failed to read option error %" PRIu32 + if (nbd_read(ioc, msg, reply->length, NULL, errp) < 0) { + error_prepend(errp, "Failed to read option error %" PRIu32 " (%s) message: ", reply->type, nbd_rep_lookup(reply->type)); goto cleanup; } msg[reply->length] = '\0'; + trace_nbd_server_error_msg(reply->type, + nbd_reply_type_lookup(reply->type), msg); } switch (reply->type) { @@ -231,18 +234,24 @@ static int nbd_handle_reply_err(QIOChannel *ioc, NBDOptionReply *reply, return result; } -/* Process another portion of the NBD_OPT_LIST reply. Set *@match if - * the current reply matches @want or if the server does not support - * NBD_OPT_LIST, otherwise leave @match alone. Return 0 if iteration - * is complete, positive if more replies are expected, or negative - * with @errp set if an unrecoverable error occurred. */ -static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, +/* nbd_receive_list: + * Process another portion of the NBD_OPT_LIST reply, populating any + * name received into *@name. If @description is non-NULL, and the + * server provided a description, that is also populated. The caller + * must eventually call g_free() on success. + * Returns 1 if name and description were set and iteration must continue, + * 0 if iteration is complete (including if OPT_LIST unsupported), + * -1 with @errp set if an unrecoverable error occurred. + */ +static int nbd_receive_list(QIOChannel *ioc, char **name, char **description, Error **errp) { + int ret = -1; NBDOptionReply reply; uint32_t len; uint32_t namelen; - char name[NBD_MAX_NAME_SIZE + 1]; + char *local_name = NULL; + char *local_desc = NULL; int error; if (nbd_receive_option_reply(ioc, NBD_OPT_LIST, &reply, errp) < 0) { @@ -250,9 +259,6 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, } error = nbd_handle_reply_err(ioc, &reply, errp); if (error <= 0) { - /* The server did not support NBD_OPT_LIST, so set *match on - * the assumption that any name will be accepted. */ - *match = true; return error; } len = reply.length; @@ -265,8 +271,9 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, } return 0; } else if (reply.type != NBD_REP_SERVER) { - error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", - reply.type, NBD_REP_SERVER); + error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", + reply.type, nbd_rep_lookup(reply.type), + NBD_REP_SERVER, nbd_rep_lookup(NBD_REP_SERVER)); nbd_send_opt_abort(ioc); return -1; } @@ -276,57 +283,62 @@ static int nbd_receive_list(QIOChannel *ioc, const char *want, bool *match, nbd_send_opt_abort(ioc); return -1; } - if (nbd_read(ioc, &namelen, sizeof(namelen), errp) < 0) { - error_prepend(errp, "failed to read option name length: "); + if (nbd_read32(ioc, &namelen, "option name length", errp) < 0) { nbd_send_opt_abort(ioc); return -1; } - namelen = be32_to_cpu(namelen); len -= sizeof(namelen); if (len < namelen) { error_setg(errp, "incorrect option name length"); nbd_send_opt_abort(ioc); return -1; } - if (namelen != strlen(want)) { - if (nbd_drop(ioc, len, errp) < 0) { - error_prepend(errp, - "failed to skip export name with wrong length: "); + + local_name = g_malloc(namelen + 1); + if (nbd_read(ioc, local_name, namelen, "export name", errp) < 0) { + nbd_send_opt_abort(ioc); + goto out; + } + local_name[namelen] = '\0'; + len -= namelen; + if (len) { + local_desc = g_malloc(len + 1); + if (nbd_read(ioc, local_desc, len, "export description", errp) < 0) { nbd_send_opt_abort(ioc); - return -1; + goto out; } - return 1; + local_desc[len] = '\0'; } - assert(namelen < sizeof(name)); - if (nbd_read(ioc, name, namelen, errp) < 0) { - error_prepend(errp, "failed to read export name: "); - nbd_send_opt_abort(ioc); - return -1; + trace_nbd_receive_list(local_name, local_desc ?: ""); + *name = local_name; + local_name = NULL; + if (description) { + *description = local_desc; + local_desc = NULL; } - name[namelen] = '\0'; - len -= namelen; - if (nbd_drop(ioc, len, errp) < 0) { - error_prepend(errp, "failed to read export description: "); - nbd_send_opt_abort(ioc); - return -1; - } - if (!strcmp(name, want)) { - *match = true; - } - return 1; + ret = 1; + + out: + g_free(local_name); + g_free(local_desc); + return ret; } -/* Returns -1 if NBD_OPT_GO proves the export @wantname cannot be - * used, 0 if NBD_OPT_GO is unsupported (fall back to NBD_OPT_LIST and +/* + * nbd_opt_info_or_go: + * Send option for NBD_OPT_INFO or NBD_OPT_GO and parse the reply. + * Returns -1 if the option proves the export @info->name cannot be + * used, 0 if the option is unsupported (fall back to NBD_OPT_LIST and * NBD_OPT_EXPORT_NAME in that case), and > 0 if the export is good to - * go (with @info populated). */ -static int nbd_opt_go(QIOChannel *ioc, const char *wantname, - NBDExportInfo *info, Error **errp) + * go (with the rest of @info populated). + */ +static int nbd_opt_info_or_go(QIOChannel *ioc, uint32_t opt, + NBDExportInfo *info, Error **errp) { NBDOptionReply reply; - uint32_t len = strlen(wantname); + uint32_t len = strlen(info->name); uint16_t type; int error; char *buf; @@ -336,16 +348,17 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, * flags still 0 is a witness of a broken server. */ info->flags = 0; - trace_nbd_opt_go_start(wantname); + assert(opt == NBD_OPT_GO || opt == NBD_OPT_INFO); + trace_nbd_opt_info_go_start(nbd_opt_lookup(opt), info->name); buf = g_malloc(4 + len + 2 + 2 * info->request_sizes + 1); stl_be_p(buf, len); - memcpy(buf + 4, wantname, len); + memcpy(buf + 4, info->name, len); /* At most one request, everything else up to server */ stw_be_p(buf + 4 + len, info->request_sizes); if (info->request_sizes) { stw_be_p(buf + 4 + len + 2, NBD_INFO_BLOCK_SIZE); } - error = nbd_send_option_request(ioc, NBD_OPT_GO, + error = nbd_send_option_request(ioc, opt, 4 + len + 2 + 2 * info->request_sizes, buf, errp); g_free(buf); @@ -354,7 +367,7 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, } while (1) { - if (nbd_receive_option_reply(ioc, NBD_OPT_GO, &reply, errp) < 0) { + if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { return -1; } error = nbd_handle_reply_err(ioc, &reply, errp); @@ -364,8 +377,10 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, len = reply.length; if (reply.type == NBD_REP_ACK) { - /* Server is done sending info and moved into transmission - phase, but make sure it sent flags */ + /* + * Server is done sending info, and moved into transmission + * phase for NBD_OPT_GO, but make sure it sent flags + */ if (len) { error_setg(errp, "server sent invalid NBD_REP_ACK"); return -1; @@ -374,13 +389,13 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, error_setg(errp, "broken server omitted NBD_INFO_EXPORT"); return -1; } - trace_nbd_opt_go_success(); + trace_nbd_opt_info_go_success(nbd_opt_lookup(opt)); return 1; } if (reply.type != NBD_REP_INFO) { - error_setg(errp, "unexpected reply type %" PRIu32 - " (%s), expected %u", - reply.type, nbd_rep_lookup(reply.type), NBD_REP_INFO); + error_setg(errp, "unexpected reply type %u (%s), expected %u (%s)", + reply.type, nbd_rep_lookup(reply.type), + NBD_REP_INFO, nbd_rep_lookup(NBD_REP_INFO)); nbd_send_opt_abort(ioc); return -1; } @@ -390,13 +405,11 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, nbd_send_opt_abort(ioc); return -1; } - if (nbd_read(ioc, &type, sizeof(type), errp) < 0) { - error_prepend(errp, "failed to read info type: "); + if (nbd_read16(ioc, &type, "info type", errp) < 0) { nbd_send_opt_abort(ioc); return -1; } len -= sizeof(type); - be16_to_cpus(&type); switch (type) { case NBD_INFO_EXPORT: if (len != sizeof(info->size) + sizeof(info->flags)) { @@ -405,18 +418,22 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, nbd_send_opt_abort(ioc); return -1; } - if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { - error_prepend(errp, "failed to read info size: "); + if (nbd_read64(ioc, &info->size, "info size", errp) < 0) { nbd_send_opt_abort(ioc); return -1; } - be64_to_cpus(&info->size); - if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { - error_prepend(errp, "failed to read info flags: "); + if (nbd_read16(ioc, &info->flags, "info flags", errp) < 0) { + nbd_send_opt_abort(ioc); + return -1; + } + if (info->min_block && + !QEMU_IS_ALIGNED(info->size, info->min_block)) { + error_setg(errp, "export size %" PRIu64 " is not multiple of " + "minimum block size %" PRIu32, info->size, + info->min_block); nbd_send_opt_abort(ioc); return -1; } - be16_to_cpus(&info->flags); trace_nbd_receive_negotiate_size_flags(info->size, info->flags); break; @@ -427,27 +444,23 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, nbd_send_opt_abort(ioc); return -1; } - if (nbd_read(ioc, &info->min_block, sizeof(info->min_block), - errp) < 0) { - error_prepend(errp, "failed to read info minimum block size: "); + if (nbd_read32(ioc, &info->min_block, "info minimum block size", + errp) < 0) { nbd_send_opt_abort(ioc); return -1; } - be32_to_cpus(&info->min_block); if (!is_power_of_2(info->min_block)) { error_setg(errp, "server minimum block size %" PRIu32 " is not a power of two", info->min_block); nbd_send_opt_abort(ioc); return -1; } - if (nbd_read(ioc, &info->opt_block, sizeof(info->opt_block), - errp) < 0) { - error_prepend(errp, - "failed to read info preferred block size: "); + if (nbd_read32(ioc, &info->opt_block, "info preferred block size", + errp) < 0) + { nbd_send_opt_abort(ioc); return -1; } - be32_to_cpus(&info->opt_block); if (!is_power_of_2(info->opt_block) || info->opt_block < info->min_block) { error_setg(errp, "server preferred block size %" PRIu32 @@ -455,25 +468,24 @@ static int nbd_opt_go(QIOChannel *ioc, const char *wantname, nbd_send_opt_abort(ioc); return -1; } - if (nbd_read(ioc, &info->max_block, sizeof(info->max_block), - errp) < 0) { - error_prepend(errp, "failed to read info maximum block size: "); + if (nbd_read32(ioc, &info->max_block, "info maximum block size", + errp) < 0) + { nbd_send_opt_abort(ioc); return -1; } - be32_to_cpus(&info->max_block); if (info->max_block < info->min_block) { error_setg(errp, "server maximum block size %" PRIu32 " is not valid", info->max_block); nbd_send_opt_abort(ioc); return -1; } - trace_nbd_opt_go_info_block_size(info->min_block, info->opt_block, - info->max_block); + trace_nbd_opt_info_block_size(info->min_block, info->opt_block, + info->max_block); break; default: - trace_nbd_opt_go_info_unknown(type, nbd_info_lookup(type)); + trace_nbd_opt_info_unknown(type, nbd_info_lookup(type)); if (nbd_drop(ioc, len, errp) < 0) { error_prepend(errp, "Failed to read info payload: "); nbd_send_opt_abort(ioc); @@ -489,7 +501,8 @@ static int nbd_receive_query_exports(QIOChannel *ioc, const char *wantname, Error **errp) { - bool foundExport = false; + bool list_empty = true; + bool found_export = false; trace_nbd_receive_query_exports_start(wantname); if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { @@ -497,14 +510,25 @@ static int nbd_receive_query_exports(QIOChannel *ioc, } while (1) { - int ret = nbd_receive_list(ioc, wantname, &foundExport, errp); + char *name; + int ret = nbd_receive_list(ioc, &name, NULL, errp); if (ret < 0) { /* Server gave unexpected reply */ return -1; } else if (ret == 0) { /* Done iterating. */ - if (!foundExport) { + if (list_empty) { + /* + * We don't have enough context to tell a server that + * sent an empty list apart from a server that does + * not support the list command; but as this function + * is just used to trigger a nicer error message + * before trying NBD_OPT_EXPORT_NAME, assume the + * export is available. + */ + return 0; + } else if (!found_export) { error_setg(errp, "No export with name '%s' available", wantname); nbd_send_opt_abort(ioc); @@ -513,6 +537,11 @@ static int nbd_receive_query_exports(QIOChannel *ioc, trace_nbd_receive_query_exports_success(wantname); return 0; } + list_empty = false; + if (!strcmp(name, wantname)) { + found_export = true; + } + g_free(name); } } @@ -601,51 +630,67 @@ static QIOChannel *nbd_receive_starttls(QIOChannel *ioc, return QIO_CHANNEL(tioc); } -/* nbd_negotiate_simple_meta_context: - * Set one meta context. Simple means that reply must contain zero (not - * negotiated) or one (negotiated) contexts. More contexts would be considered - * as a protocol error. It's also implied that meta-data query equals queried - * context name, so, if server replies with something different than @context, - * it is considered an error too. - * return 1 for successful negotiation, context_id is set - * 0 if operation is unsupported, - * -1 with errp set for any other error +/* + * nbd_send_meta_query: + * Send 0 or 1 set/list meta context queries. + * Return 0 on success, -1 with errp set for any error */ -static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, - const char *export, - const char *context, - uint32_t *context_id, - Error **errp) +static int nbd_send_meta_query(QIOChannel *ioc, uint32_t opt, + const char *export, const char *query, + Error **errp) +{ + int ret; + uint32_t export_len = strlen(export); + uint32_t queries = !!query; + uint32_t query_len = 0; + uint32_t data_len; + char *data; + char *p; + + data_len = sizeof(export_len) + export_len + sizeof(queries); + if (query) { + query_len = strlen(query); + data_len += sizeof(query_len) + query_len; + } else { + assert(opt == NBD_OPT_LIST_META_CONTEXT); + } + p = data = g_malloc(data_len); + + trace_nbd_opt_meta_request(nbd_opt_lookup(opt), query ?: "(all)", export); + stl_be_p(p, export_len); + memcpy(p += sizeof(export_len), export, export_len); + stl_be_p(p += export_len, queries); + if (query) { + stl_be_p(p += sizeof(queries), query_len); + memcpy(p += sizeof(query_len), query, query_len); + } + + ret = nbd_send_option_request(ioc, opt, data_len, data, errp); + g_free(data); + return ret; +} + +/* + * nbd_receive_one_meta_context: + * Called in a loop to receive and trace one set/list meta context reply. + * Pass non-NULL @name or @id to collect results back to the caller, which + * must eventually call g_free(). + * return 1 if name is set and iteration must continue, + * 0 if iteration is complete (including if option is unsupported), + * -1 with errp set for any error + */ +static int nbd_receive_one_meta_context(QIOChannel *ioc, + uint32_t opt, + char **name, + uint32_t *id, + Error **errp) { int ret; NBDOptionReply reply; - uint32_t received_id = 0; - bool received = false; - uint32_t export_len = strlen(export); - uint32_t context_len = strlen(context); - uint32_t data_len = sizeof(export_len) + export_len + - sizeof(uint32_t) + /* number of queries */ - sizeof(context_len) + context_len; - char *data = g_malloc(data_len); - char *p = data; + char *local_name = NULL; + uint32_t local_id; - trace_nbd_opt_meta_request(context, export); - stl_be_p(p, export_len); - memcpy(p += sizeof(export_len), export, export_len); - stl_be_p(p += export_len, 1); - stl_be_p(p += sizeof(uint32_t), context_len); - memcpy(p += sizeof(context_len), context, context_len); - - ret = nbd_send_option_request(ioc, NBD_OPT_SET_META_CONTEXT, data_len, data, - errp); - g_free(data); - if (ret < 0) { - return ret; - } - - if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, - errp) < 0) - { + if (nbd_receive_option_reply(ioc, opt, &reply, errp) < 0) { return -1; } @@ -654,29 +699,91 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, return ret; } - if (reply.type == NBD_REP_META_CONTEXT) { - char *name; - - if (reply.length != sizeof(received_id) + context_len) { - error_setg(errp, "Failed to negotiate meta context '%s', server " - "answered with unexpected length %" PRIu32, context, - reply.length); + if (reply.type == NBD_REP_ACK) { + if (reply.length != 0) { + error_setg(errp, "Unexpected length to ACK response"); nbd_send_opt_abort(ioc); return -1; } + return 0; + } else if (reply.type != NBD_REP_META_CONTEXT) { + error_setg(errp, "Unexpected reply type %u (%s), expected %u (%s)", + reply.type, nbd_rep_lookup(reply.type), + NBD_REP_META_CONTEXT, nbd_rep_lookup(NBD_REP_META_CONTEXT)); + nbd_send_opt_abort(ioc); + return -1; + } - if (nbd_read(ioc, &received_id, sizeof(received_id), errp) < 0) { - return -1; - } - be32_to_cpus(&received_id); + if (reply.length <= sizeof(local_id) || + reply.length > NBD_MAX_BUFFER_SIZE) { + error_setg(errp, "Failed to negotiate meta context, server " + "answered with unexpected length %" PRIu32, + reply.length); + nbd_send_opt_abort(ioc); + return -1; + } - reply.length -= sizeof(received_id); - name = g_malloc(reply.length + 1); - if (nbd_read(ioc, name, reply.length, errp) < 0) { - g_free(name); - return -1; - } - name[reply.length] = '\0'; + if (nbd_read32(ioc, &local_id, "context id", errp) < 0) { + return -1; + } + + reply.length -= sizeof(local_id); + local_name = g_malloc(reply.length + 1); + if (nbd_read(ioc, local_name, reply.length, "context name", errp) < 0) { + g_free(local_name); + return -1; + } + local_name[reply.length] = '\0'; + trace_nbd_opt_meta_reply(nbd_opt_lookup(opt), local_name, local_id); + + if (name) { + *name = local_name; + } else { + g_free(local_name); + } + if (id) { + *id = local_id; + } + return 1; +} + +/* + * nbd_negotiate_simple_meta_context: + * Request the server to set the meta context for export @info->name + * using @info->x_dirty_bitmap with a fallback to "base:allocation", + * setting @info->context_id to the resulting id. Fail if the server + * responds with more than one context or with a context different + * than the query. + * return 1 for successful negotiation, + * 0 if operation is unsupported, + * -1 with errp set for any other error + */ +static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, + NBDExportInfo *info, + Error **errp) +{ + /* + * TODO: Removing the x_dirty_bitmap hack will mean refactoring + * this function to request and store ids for multiple contexts + * (both base:allocation and a dirty bitmap), at which point this + * function should lose the term _simple. + */ + int ret; + const char *context = info->x_dirty_bitmap ?: "base:allocation"; + bool received = false; + char *name = NULL; + + if (nbd_send_meta_query(ioc, NBD_OPT_SET_META_CONTEXT, + info->name, context, errp) < 0) { + return -1; + } + + ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, + &name, &info->context_id, errp); + if (ret < 0) { + return -1; + } + if (ret == 1) { if (strcmp(context, name)) { error_setg(errp, "Failed to negotiate meta context '%s', server " "answered with different context '%s'", context, @@ -686,93 +793,113 @@ static int nbd_negotiate_simple_meta_context(QIOChannel *ioc, return -1; } g_free(name); - - trace_nbd_opt_meta_reply(context, received_id); received = true; - /* receive NBD_REP_ACK */ - if (nbd_receive_option_reply(ioc, NBD_OPT_SET_META_CONTEXT, &reply, - errp) < 0) - { + ret = nbd_receive_one_meta_context(ioc, NBD_OPT_SET_META_CONTEXT, + NULL, NULL, errp); + if (ret < 0) { return -1; } + } + if (ret != 0) { + error_setg(errp, "Server answered with more than one context"); + nbd_send_opt_abort(ioc); + return -1; + } + return received; +} - ret = nbd_handle_reply_err(ioc, &reply, errp); +/* + * nbd_list_meta_contexts: + * Request the server to list all meta contexts for export @info->name. + * return 0 if list is complete (even if empty), + * -1 with errp set for any error + */ +static int nbd_list_meta_contexts(QIOChannel *ioc, + NBDExportInfo *info, + Error **errp) +{ + int ret; + int seen_any = false; + int seen_qemu = false; + + if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT, + info->name, NULL, errp) < 0) { + return -1; + } + + while (1) { + char *context; + + ret = nbd_receive_one_meta_context(ioc, NBD_OPT_LIST_META_CONTEXT, + &context, NULL, errp); + if (ret == 0 && seen_any && !seen_qemu) { + /* + * Work around qemu 3.0 bug: the server forgot to send + * "qemu:" replies to 0 queries. If we saw at least one + * reply (probably base:allocation), but none of them were + * qemu:, then run a more specific query to make sure. + */ + seen_qemu = true; + if (nbd_send_meta_query(ioc, NBD_OPT_LIST_META_CONTEXT, + info->name, "qemu:", errp) < 0) { + return -1; + } + continue; + } if (ret <= 0) { return ret; } + seen_any = true; + seen_qemu |= strstart(context, "qemu:", NULL); + info->contexts = g_renew(char *, info->contexts, ++info->n_contexts); + info->contexts[info->n_contexts - 1] = context; } - - if (reply.type != NBD_REP_ACK) { - error_setg(errp, "Unexpected reply type %" PRIx32 " expected %x", - reply.type, NBD_REP_ACK); - nbd_send_opt_abort(ioc); - return -1; - } - if (reply.length) { - error_setg(errp, "Unexpected length to ACK response"); - nbd_send_opt_abort(ioc); - return -1; - } - - if (received) { - *context_id = received_id; - return 1; - } - - return 0; } -int nbd_receive_negotiate(QIOChannel *ioc, const char *name, - QCryptoTLSCreds *tlscreds, const char *hostname, - QIOChannel **outioc, NBDExportInfo *info, - Error **errp) +/* + * nbd_start_negotiate: + * Start the handshake to the server. After a positive return, the server + * is ready to accept additional NBD_OPT requests. + * Returns: negative errno: failure talking to server + * 0: server is oldstyle, must call nbd_negotiate_finish_oldstyle + * 1: server is newstyle, but can only accept EXPORT_NAME + * 2: server is newstyle, but lacks structured replies + * 3: server is newstyle and set up for structured replies + */ +static int nbd_start_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + const char *hostname, QIOChannel **outioc, + bool structured_reply, bool *zeroes, + Error **errp) { - char buf[256]; uint64_t magic; - int rc; - bool zeroes = true; - bool structured_reply = info->structured_reply; - bool base_allocation = info->base_allocation; - trace_nbd_receive_negotiate(tlscreds, hostname ? hostname : ""); - - info->structured_reply = false; - info->base_allocation = false; - rc = -EINVAL; + trace_nbd_start_negotiate(tlscreds, hostname ? hostname : ""); + if (zeroes) { + *zeroes = true; + } if (outioc) { *outioc = NULL; } if (tlscreds && !outioc) { error_setg(errp, "Output I/O channel required for TLS"); - goto fail; + return -EINVAL; } - if (nbd_read(ioc, buf, 8, errp) < 0) { - error_prepend(errp, "Failed to read data: "); - goto fail; + if (nbd_read64(ioc, &magic, "initial magic", errp) < 0) { + return -EINVAL; } - - buf[8] = '\0'; - if (strlen(buf) == 0) { - error_setg(errp, "Server connection closed unexpectedly"); - goto fail; - } - - magic = ldq_be_p(buf); trace_nbd_receive_negotiate_magic(magic); - if (memcmp(buf, "NBDMAGIC", 8) != 0) { - error_setg(errp, "Invalid magic received"); - goto fail; + if (magic != NBD_INIT_MAGIC) { + error_setg(errp, "Bad initial magic received: 0x%" PRIx64, magic); + return -EINVAL; } - if (nbd_read(ioc, &magic, sizeof(magic), errp) < 0) { - error_prepend(errp, "Failed to read magic: "); - goto fail; + if (nbd_read64(ioc, &magic, "server magic", errp) < 0) { + return -EINVAL; } - magic = be64_to_cpu(magic); trace_nbd_receive_negotiate_magic(magic); if (magic == NBD_OPTS_MAGIC) { @@ -780,147 +907,317 @@ int nbd_receive_negotiate(QIOChannel *ioc, const char *name, uint16_t globalflags; bool fixedNewStyle = false; - if (nbd_read(ioc, &globalflags, sizeof(globalflags), errp) < 0) { - error_prepend(errp, "Failed to read server flags: "); - goto fail; + if (nbd_read16(ioc, &globalflags, "server flags", errp) < 0) { + return -EINVAL; } - globalflags = be16_to_cpu(globalflags); trace_nbd_receive_negotiate_server_flags(globalflags); if (globalflags & NBD_FLAG_FIXED_NEWSTYLE) { fixedNewStyle = true; clientflags |= NBD_FLAG_C_FIXED_NEWSTYLE; } if (globalflags & NBD_FLAG_NO_ZEROES) { - zeroes = false; + if (zeroes) { + *zeroes = false; + } clientflags |= NBD_FLAG_C_NO_ZEROES; } /* client requested flags */ clientflags = cpu_to_be32(clientflags); if (nbd_write(ioc, &clientflags, sizeof(clientflags), errp) < 0) { error_prepend(errp, "Failed to send clientflags field: "); - goto fail; + return -EINVAL; } if (tlscreds) { if (fixedNewStyle) { *outioc = nbd_receive_starttls(ioc, tlscreds, hostname, errp); if (!*outioc) { - goto fail; + return -EINVAL; } ioc = *outioc; } else { error_setg(errp, "Server does not support STARTTLS"); - goto fail; + return -EINVAL; } } - if (!name) { - trace_nbd_receive_negotiate_default_name(); - name = ""; - } if (fixedNewStyle) { - int result; + int result = 0; if (structured_reply) { result = nbd_request_simple_option(ioc, NBD_OPT_STRUCTURED_REPLY, errp); if (result < 0) { - goto fail; + return -EINVAL; } - info->structured_reply = result == 1; - } - - if (info->structured_reply && base_allocation) { - result = nbd_negotiate_simple_meta_context( - ioc, name, info->x_dirty_bitmap ?: "base:allocation", - &info->meta_base_allocation_id, errp); - if (result < 0) { - goto fail; - } - info->base_allocation = result == 1; - } - - /* Try NBD_OPT_GO first - if it works, we are done (it - * also gives us a good message if the server requires - * TLS). If it is not available, fall back to - * NBD_OPT_LIST for nicer error messages about a missing - * export, then use NBD_OPT_EXPORT_NAME. */ - result = nbd_opt_go(ioc, name, info, errp); - if (result < 0) { - goto fail; - } - if (result > 0) { - return 0; - } - /* 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 better error reporting if the - * export name is not available. - */ - if (nbd_receive_query_exports(ioc, name, errp) < 0) { - goto fail; } + return 2 + result; + } else { + return 1; } + } else if (magic == NBD_CLIENT_MAGIC) { + if (tlscreds) { + error_setg(errp, "Server does not support STARTTLS"); + return -EINVAL; + } + return 0; + } else { + error_setg(errp, "Bad server magic received: 0x%" PRIx64, magic); + return -EINVAL; + } +} + +/* + * nbd_negotiate_finish_oldstyle: + * Populate @info with the size and export flags from an oldstyle server, + * but does not consume 124 bytes of reserved zero padding. + * Returns 0 on success, -1 with @errp set on failure + */ +static int nbd_negotiate_finish_oldstyle(QIOChannel *ioc, NBDExportInfo *info, + Error **errp) +{ + uint32_t oldflags; + + if (nbd_read64(ioc, &info->size, "export length", errp) < 0) { + return -EINVAL; + } + + if (nbd_read32(ioc, &oldflags, "export flags", errp) < 0) { + return -EINVAL; + } + if (oldflags & ~0xffff) { + error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); + return -EINVAL; + } + info->flags = oldflags; + return 0; +} + +/* + * nbd_receive_negotiate: + * Connect to server, complete negotiation, and move into transmission phase. + * Returns: negative errno: failure talking to server + * 0: server is connected + */ +int nbd_receive_negotiate(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + const char *hostname, QIOChannel **outioc, + NBDExportInfo *info, Error **errp) +{ + int result; + bool zeroes; + bool base_allocation = info->base_allocation; + + assert(info->name); + trace_nbd_receive_negotiate_name(info->name); + + result = nbd_start_negotiate(ioc, tlscreds, hostname, outioc, + info->structured_reply, &zeroes, errp); + + info->structured_reply = false; + info->base_allocation = false; + if (tlscreds && *outioc) { + ioc = *outioc; + } + + switch (result) { + case 3: /* newstyle, with structured replies */ + info->structured_reply = true; + if (base_allocation) { + result = nbd_negotiate_simple_meta_context(ioc, info, errp); + if (result < 0) { + return -EINVAL; + } + info->base_allocation = result == 1; + } + /* fall through */ + case 2: /* newstyle, try OPT_GO */ + /* Try NBD_OPT_GO first - if it works, we are done (it + * also gives us a good message if the server requires + * TLS). If it is not available, fall back to + * NBD_OPT_LIST for nicer error messages about a missing + * export, then use NBD_OPT_EXPORT_NAME. */ + result = nbd_opt_info_or_go(ioc, NBD_OPT_GO, info, errp); + if (result < 0) { + return -EINVAL; + } + if (result > 0) { + return 0; + } + /* 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 better error reporting if the + * export name is not available. + */ + if (nbd_receive_query_exports(ioc, info->name, errp) < 0) { + return -EINVAL; + } + /* fall through */ + case 1: /* newstyle, but limited to EXPORT_NAME */ /* write the export name request */ - if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, name, + if (nbd_send_option_request(ioc, NBD_OPT_EXPORT_NAME, -1, info->name, errp) < 0) { - goto fail; + return -EINVAL; } /* Read the response */ - if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { - error_prepend(errp, "Failed to read export length: "); - goto fail; - } - be64_to_cpus(&info->size); - - if (nbd_read(ioc, &info->flags, sizeof(info->flags), errp) < 0) { - error_prepend(errp, "Failed to read export flags: "); - goto fail; - } - be16_to_cpus(&info->flags); - } else if (magic == NBD_CLIENT_MAGIC) { - uint32_t oldflags; - - 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 (nbd_read64(ioc, &info->size, "export length", errp) < 0) { + return -EINVAL; } - if (nbd_read(ioc, &info->size, sizeof(info->size), errp) < 0) { - error_prepend(errp, "Failed to read export length: "); - goto fail; + if (nbd_read16(ioc, &info->flags, "export flags", errp) < 0) { + return -EINVAL; } - be64_to_cpus(&info->size); - - if (nbd_read(ioc, &oldflags, sizeof(oldflags), errp) < 0) { - error_prepend(errp, "Failed to read export flags: "); - goto fail; + break; + case 0: /* oldstyle, parse length and flags */ + if (*info->name) { + error_setg(errp, "Server does not support non-empty export names"); + return -EINVAL; } - be32_to_cpus(&oldflags); - if (oldflags & ~0xffff) { - error_setg(errp, "Unexpected export flags %0x" PRIx32, oldflags); - goto fail; + if (nbd_negotiate_finish_oldstyle(ioc, info, errp) < 0) { + return -EINVAL; } - info->flags = oldflags; - } else { - error_setg(errp, "Bad magic received"); - goto fail; + break; + default: + return result; } trace_nbd_receive_negotiate_size_flags(info->size, info->flags); if (zeroes && nbd_drop(ioc, 124, errp) < 0) { error_prepend(errp, "Failed to read reserved block: "); - goto fail; + return -EINVAL; } - rc = 0; + return 0; +} -fail: - return rc; +/* Clean up result of nbd_receive_export_list */ +void nbd_free_export_list(NBDExportInfo *info, int count) +{ + int i, j; + + if (!info) { + return; + } + + for (i = 0; i < count; i++) { + g_free(info[i].name); + g_free(info[i].description); + for (j = 0; j < info[i].n_contexts; j++) { + g_free(info[i].contexts[j]); + } + g_free(info[i].contexts); + } + g_free(info); +} + +/* + * nbd_receive_export_list: + * Query details about a server's exports, then disconnect without + * going into transmission phase. Return a count of the exports listed + * in @info by the server, or -1 on error. Caller must free @info using + * nbd_free_export_list(). + */ +int nbd_receive_export_list(QIOChannel *ioc, QCryptoTLSCreds *tlscreds, + const char *hostname, NBDExportInfo **info, + Error **errp) +{ + int result; + int count = 0; + int i; + int rc; + int ret = -1; + NBDExportInfo *array = NULL; + QIOChannel *sioc = NULL; + + *info = NULL; + result = nbd_start_negotiate(ioc, tlscreds, hostname, &sioc, true, NULL, + errp); + if (tlscreds && sioc) { + ioc = sioc; + } + + switch (result) { + case 2: + case 3: + /* newstyle - use NBD_OPT_LIST to populate array, then try + * NBD_OPT_INFO on each array member. If structured replies + * are enabled, also try NBD_OPT_LIST_META_CONTEXT. */ + if (nbd_send_option_request(ioc, NBD_OPT_LIST, 0, NULL, errp) < 0) { + goto out; + } + while (1) { + char *name; + char *desc; + + rc = nbd_receive_list(ioc, &name, &desc, errp); + if (rc < 0) { + goto out; + } else if (rc == 0) { + break; + } + array = g_renew(NBDExportInfo, array, ++count); + memset(&array[count - 1], 0, sizeof(*array)); + array[count - 1].name = name; + array[count - 1].description = desc; + array[count - 1].structured_reply = result == 3; + } + + for (i = 0; i < count; i++) { + array[i].request_sizes = true; + rc = nbd_opt_info_or_go(ioc, NBD_OPT_INFO, &array[i], errp); + if (rc < 0) { + goto out; + } else if (rc == 0) { + /* + * Pointless to try rest of loop. If OPT_INFO doesn't work, + * it's unlikely that meta contexts work either + */ + break; + } + + if (result == 3 && + nbd_list_meta_contexts(ioc, &array[i], errp) < 0) { + goto out; + } + } + + /* Send NBD_OPT_ABORT as a courtesy before hanging up */ + nbd_send_opt_abort(ioc); + break; + case 1: /* newstyle, but limited to EXPORT_NAME */ + error_setg(errp, "Server does not support export lists"); + /* We can't even send NBD_OPT_ABORT, so merely hang up */ + goto out; + case 0: /* oldstyle, parse length and flags */ + array = g_new0(NBDExportInfo, 1); + array->name = g_strdup(""); + count = 1; + + if (nbd_negotiate_finish_oldstyle(ioc, array, errp) < 0) { + goto out; + } + + /* Send NBD_CMD_DISC as a courtesy to the server, but ignore all + * errors now that we have the information we wanted. */ + if (nbd_drop(ioc, 124, NULL) == 0) { + NBDRequest request = { .type = NBD_CMD_DISC }; + + nbd_send_request(ioc, &request); + } + break; + default: + goto out; + } + + *info = array; + array = NULL; + ret = count; + + out: + qio_channel_shutdown(ioc, QIO_CHANNEL_SHUTDOWN_BOTH, NULL); + qio_channel_close(ioc, NULL); + object_unref(OBJECT(sioc)); + nbd_free_export_list(array, count); + return ret; } #ifdef __linux__ @@ -1026,23 +1323,7 @@ int nbd_disconnect(int fd) return 0; } -#else -int nbd_init(int fd, QIOChannelSocket *ioc, NBDExportInfo *info, - Error **errp) -{ - error_setg(errp, "nbd_init is only supported on Linux"); - return -ENOTSUP; -} - -int nbd_client(int fd) -{ - return -ENOTSUP; -} -int nbd_disconnect(int fd) -{ - return -ENOTSUP; -} -#endif +#endif /* __linux__ */ int nbd_send_request(QIOChannel *ioc, NBDRequest *request) { @@ -1075,13 +1356,13 @@ static int nbd_receive_simple_reply(QIOChannel *ioc, NBDSimpleReply *reply, assert(reply->magic == NBD_SIMPLE_REPLY_MAGIC); ret = nbd_read(ioc, (uint8_t *)reply + sizeof(reply->magic), - sizeof(*reply) - sizeof(reply->magic), errp); + sizeof(*reply) - sizeof(reply->magic), "reply", errp); if (ret < 0) { return ret; } - be32_to_cpus(&reply->error); - be64_to_cpus(&reply->handle); + reply->error = be32_to_cpu(reply->error); + reply->handle = be64_to_cpu(reply->handle); return 0; } @@ -1100,35 +1381,84 @@ static int nbd_receive_structured_reply_chunk(QIOChannel *ioc, assert(chunk->magic == NBD_STRUCTURED_REPLY_MAGIC); ret = nbd_read(ioc, (uint8_t *)chunk + sizeof(chunk->magic), - sizeof(*chunk) - sizeof(chunk->magic), errp); + sizeof(*chunk) - sizeof(chunk->magic), "structured chunk", + errp); if (ret < 0) { return ret; } - be16_to_cpus(&chunk->flags); - be16_to_cpus(&chunk->type); - be64_to_cpus(&chunk->handle); - be32_to_cpus(&chunk->length); + chunk->flags = be16_to_cpu(chunk->flags); + chunk->type = be16_to_cpu(chunk->type); + chunk->handle = be64_to_cpu(chunk->handle); + chunk->length = be32_to_cpu(chunk->length); return 0; } -/* nbd_receive_reply +/* nbd_read_eof + * Tries to read @size bytes from @ioc. * Returns 1 on success * 0 on eof, when no data was read (errp is not set) * negative errno on failure (errp is set) */ -int nbd_receive_reply(QIOChannel *ioc, NBDReply *reply, Error **errp) +static inline int coroutine_fn +nbd_read_eof(BlockDriverState *bs, QIOChannel *ioc, void *buffer, size_t size, + Error **errp) +{ + bool partial = false; + + assert(size); + while (size > 0) { + struct iovec iov = { .iov_base = buffer, .iov_len = size }; + ssize_t len; + + len = qio_channel_readv(ioc, &iov, 1, errp); + if (len == QIO_CHANNEL_ERR_BLOCK) { + bdrv_dec_in_flight(bs); + qio_channel_yield(ioc, G_IO_IN); + bdrv_inc_in_flight(bs); + continue; + } else if (len < 0) { + return -EIO; + } else if (len == 0) { + if (partial) { + error_setg(errp, + "Unexpected end-of-file before all bytes were read"); + return -EIO; + } else { + return 0; + } + } + + partial = true; + size -= len; + buffer = (uint8_t*) buffer + len; + } + return 1; +} + +/* nbd_receive_reply + * + * Decreases bs->in_flight while waiting for a new reply. This yield is where + * we wait indefinitely and the coroutine must be able to be safely reentered + * for nbd_client_attach_aio_context(). + * + * Returns 1 on success + * 0 on eof, when no data was read (errp is not set) + * negative errno on failure (errp is set) + */ +int coroutine_fn nbd_receive_reply(BlockDriverState *bs, QIOChannel *ioc, + NBDReply *reply, Error **errp) { int ret; const char *type; - ret = nbd_read_eof(ioc, &reply->magic, sizeof(reply->magic), errp); + ret = nbd_read_eof(bs, ioc, &reply->magic, sizeof(reply->magic), errp); if (ret <= 0) { return ret; } - be32_to_cpus(&reply->magic); + reply->magic = be32_to_cpu(reply->magic); switch (reply->magic) { case NBD_SIMPLE_REPLY_MAGIC: diff --git a/nbd/common.c b/nbd/common.c index 41f5ed8d9f..cc8b278e54 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -31,7 +31,7 @@ int nbd_drop(QIOChannel *ioc, size_t size, Error **errp) buffer = sizeof(small) >= size ? small : g_malloc(MIN(65536, size)); while (size > 0) { ssize_t count = MIN(65536, size); - ret = nbd_read(ioc, buffer, MIN(65536, size), errp); + ret = nbd_read(ioc, buffer, MIN(65536, size), NULL, errp); if (ret < 0) { goto cleanup; diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index eeff78d3c9..049f83df77 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -46,8 +46,9 @@ /* Size of oldstyle negotiation */ #define NBD_OLDSTYLE_NEGOTIATE_SIZE (8 + 8 + 8 + 4 + 124) +#define NBD_INIT_MAGIC 0x4e42444d41474943LL /* ASCII "NBDMAGIC" */ #define NBD_REQUEST_MAGIC 0x25609513 -#define NBD_OPTS_MAGIC 0x49484156454F5054LL +#define NBD_OPTS_MAGIC 0x49484156454F5054LL /* ASCII "IHAVEOPT" */ #define NBD_CLIENT_MAGIC 0x0000420281861253LL #define NBD_REP_MAGIC 0x0003e889045565a9LL @@ -63,25 +64,6 @@ #define NBD_SET_TIMEOUT _IO(0xab, 9) #define NBD_SET_FLAGS _IO(0xab, 10) -/* nbd_read_eof - * Tries to read @size bytes from @ioc. - * Returns 1 on success - * 0 on eof, when no data was read (errp is not set) - * negative errno on failure (errp is set) - */ -static inline int nbd_read_eof(QIOChannel *ioc, void *buffer, size_t size, - Error **errp) -{ - int ret; - - assert(size); - ret = qio_channel_read_all_eof(ioc, buffer, size, errp); - if (ret < 0) { - ret = -EIO; - } - return ret; -} - /* nbd_write * Writes @size bytes to @ioc. Returns 0 on success. */ @@ -100,11 +82,6 @@ struct NBDTLSHandshakeData { void nbd_tls_handshake(QIOTask *task, void *opaque); -const char *nbd_opt_lookup(uint32_t opt); -const char *nbd_rep_lookup(uint32_t rep); -const char *nbd_info_lookup(uint16_t info); -const char *nbd_cmd_lookup(uint16_t info); -const char *nbd_err_lookup(int err); int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); diff --git a/nbd/server.c b/nbd/server.c index ea5fe0eb33..e21bd501dc 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -77,8 +77,8 @@ struct NBDExport { BlockBackend *blk; char *name; char *description; - off_t dev_offset; - off_t size; + uint64_t dev_offset; + uint64_t size; uint16_t nbdflags; QTAILQ_HEAD(, NBDClient) clients; QTAILQ_ENTRY(NBDExport) next; @@ -111,7 +111,7 @@ struct NBDClient { NBDExport *exp; QCryptoTLSCreds *tlscreds; - char *tlsaclname; + char *tlsauthz; QIOChannelSocket *sioc; /* The underlying data channel */ QIOChannel *ioc; /* The current I/O channel which may differ (eg TLS) */ @@ -124,6 +124,8 @@ struct NBDClient { int nb_requests; bool closing; + uint32_t check_align; /* If non-zero, check for aligned client requests */ + bool structured_reply; NBDExportMetaContexts export_meta; @@ -333,7 +335,7 @@ static int nbd_opt_read_name(NBDClient *client, char *name, uint32_t *length, if (ret <= 0) { return ret; } - cpu_to_be32s(&len); + len = cpu_to_be32(len); if (len > NBD_MAX_NAME_SIZE) { return nbd_opt_invalid(client, errp, @@ -438,8 +440,7 @@ static int nbd_negotiate_handle_export_name(NBDClient *client, error_setg(errp, "Bad length received"); return -EINVAL; } - if (nbd_read(client->ioc, name, client->optlen, errp) < 0) { - error_prepend(errp, "read failed: "); + if (nbd_read(client->ioc, name, client->optlen, "export name", errp) < 0) { return -EIO; } name[client->optlen] = '\0'; @@ -486,7 +487,7 @@ static int nbd_negotiate_send_info(NBDClient *client, if (rc < 0) { return rc; } - cpu_to_be16s(&info); + info = cpu_to_be16(info); if (nbd_write(client->ioc, &info, sizeof(info), errp) < 0) { return -EIO; } @@ -534,6 +535,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, bool blocksize = false; uint32_t sizes[3]; char buf[sizeof(uint64_t) + sizeof(uint16_t)]; + uint32_t check_align = 0; /* Client sends: 4 bytes: L, name length (can be 0) @@ -551,14 +553,14 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, if (rc <= 0) { return rc; } - be16_to_cpus(&requests); + requests = be16_to_cpu(requests); trace_nbd_negotiate_handle_info_requests(requests); while (requests--) { rc = nbd_opt_read(client, &request, sizeof(request), errp); if (rc <= 0) { return rc; } - be16_to_cpus(&request); + request = be16_to_cpu(request); trace_nbd_negotiate_handle_info_request(request, nbd_info_lookup(request)); /* We care about NBD_INFO_NAME and NBD_INFO_BLOCK_SIZE; @@ -608,19 +610,22 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, /* Send NBD_INFO_BLOCK_SIZE always, but tweak the minimum size * according to whether the client requested it, and according to * whether this is OPT_INFO or OPT_GO. */ - /* minimum - 1 for back-compat, or 512 if client is new enough. - * TODO: consult blk_bs(blk)->bl.request_alignment? */ - sizes[0] = - (client->opt == NBD_OPT_INFO || blocksize) ? BDRV_SECTOR_SIZE : 1; + /* minimum - 1 for back-compat, or actual if client will obey it. */ + if (client->opt == NBD_OPT_INFO || blocksize) { + check_align = sizes[0] = blk_get_request_alignment(exp->blk); + } else { + sizes[0] = 1; + } + assert(sizes[0] <= NBD_MAX_BUFFER_SIZE); /* preferred - Hard-code to 4096 for now. * TODO: is blk_bs(blk)->bl.opt_transfer appropriate? */ - sizes[1] = 4096; + sizes[1] = MAX(4096, sizes[0]); /* maximum - At most 32M, but smaller as appropriate. */ sizes[2] = MIN(blk_get_max_transfer(exp->blk), NBD_MAX_BUFFER_SIZE); trace_nbd_negotiate_handle_info_block_size(sizes[0], sizes[1], sizes[2]); - cpu_to_be32s(&sizes[0]); - cpu_to_be32s(&sizes[1]); - cpu_to_be32s(&sizes[2]); + sizes[0] = cpu_to_be32(sizes[0]); + sizes[1] = cpu_to_be32(sizes[1]); + sizes[2] = cpu_to_be32(sizes[2]); rc = nbd_negotiate_send_info(client, NBD_INFO_BLOCK_SIZE, sizeof(sizes), sizes, errp); if (rc < 0) { @@ -638,11 +643,14 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, return rc; } - /* If the client is just asking for NBD_OPT_INFO, but forgot to - * request block sizes, return an error. - * TODO: consult blk_bs(blk)->request_align, and only error if it - * is not 1? */ - if (client->opt == NBD_OPT_INFO && !blocksize) { + /* + * If the client is just asking for NBD_OPT_INFO, but forgot to + * request block sizes in a situation that would impact + * performance, then return an error. But for NBD_OPT_GO, we + * tolerate all clients, regardless of alignments. + */ + if (client->opt == NBD_OPT_INFO && !blocksize && + blk_get_request_alignment(exp->blk) > 1) { return nbd_negotiate_send_rep_err(client, NBD_REP_ERR_BLOCK_SIZE_REQD, errp, @@ -658,6 +666,7 @@ static int nbd_negotiate_handle_info(NBDClient *client, uint16_t myflags, if (client->opt == NBD_OPT_GO) { client->exp = exp; + client->check_align = check_align; QTAILQ_INSERT_TAIL(&client->exp->clients, client, next); nbd_export_get(client->exp); nbd_check_meta_export(client); @@ -687,7 +696,7 @@ static QIOChannel *nbd_negotiate_handle_starttls(NBDClient *client, tioc = qio_channel_tls_new_server(ioc, client->tlscreds, - client->tlsaclname, + client->tlsauthz, errp); if (!tioc) { return NULL; @@ -904,7 +913,7 @@ static int nbd_negotiate_meta_query(NBDClient *client, if (ret <= 0) { return ret; } - cpu_to_be32s(&len); + len = cpu_to_be32(len); if (len < ns_len) { trace_nbd_negotiate_meta_query_skip("length too short"); @@ -971,13 +980,14 @@ static int nbd_negotiate_meta_queries(NBDClient *client, if (ret <= 0) { return ret; } - cpu_to_be32s(&nb_queries); + nb_queries = cpu_to_be32(nb_queries); trace_nbd_negotiate_meta_context(nbd_opt_lookup(client->opt), export_name, nb_queries); if (client->opt == NBD_OPT_LIST_META_CONTEXT && !nb_queries) { /* enable all known contexts */ meta->base_allocation = true; + meta->bitmap = !!meta->exp->export_bitmap; } else { for (i = 0; i < nb_queries; ++i) { ret = nbd_negotiate_meta_query(client, meta, errp); @@ -1045,11 +1055,9 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, ... Rest of request */ - if (nbd_read(client->ioc, &flags, sizeof(flags), errp) < 0) { - error_prepend(errp, "read failed: "); + if (nbd_read32(client->ioc, &flags, "flags", errp) < 0) { return -EIO; } - be32_to_cpus(&flags); trace_nbd_negotiate_options_flags(flags); if (flags & NBD_FLAG_C_FIXED_NEWSTYLE) { fixedNewstyle = true; @@ -1069,30 +1077,23 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, uint32_t option, length; uint64_t magic; - if (nbd_read(client->ioc, &magic, sizeof(magic), errp) < 0) { - error_prepend(errp, "read failed: "); + if (nbd_read64(client->ioc, &magic, "opts magic", errp) < 0) { return -EINVAL; } - magic = be64_to_cpu(magic); trace_nbd_negotiate_options_check_magic(magic); if (magic != NBD_OPTS_MAGIC) { error_setg(errp, "Bad magic received"); return -EINVAL; } - if (nbd_read(client->ioc, &option, - sizeof(option), errp) < 0) { - error_prepend(errp, "read failed: "); + if (nbd_read32(client->ioc, &option, "option", errp) < 0) { return -EINVAL; } - option = be32_to_cpu(option); client->opt = option; - if (nbd_read(client->ioc, &length, sizeof(length), errp) < 0) { - error_prepend(errp, "read failed: "); + if (nbd_read32(client->ioc, &length, "option length", errp) < 0) { return -EINVAL; } - length = be32_to_cpu(length); assert(!client->optlen); client->optlen = length; @@ -1134,12 +1135,16 @@ static int nbd_negotiate_options(NBDClient *client, uint16_t myflags, return -EINVAL; default: - ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, errp, - "Option 0x%" PRIx32 - "not permitted before TLS", option); /* Let the client keep trying, unless they asked to - * quit. In this mode, we've already sent an error, so - * we can't ack the abort. */ + * quit. Always try to give an error back to the + * client; but when replying to OPT_ABORT, be aware + * that the client may hang up before receiving the + * error, in which case we are fine ignoring the + * resulting EPIPE. */ + ret = nbd_opt_drop(client, NBD_REP_ERR_TLS_REQD, + option == NBD_OPT_ABORT ? NULL : errp, + "Option 0x%" PRIx32 + " not permitted before TLS", option); if (option == NBD_OPT_ABORT) { return 1; } @@ -1253,7 +1258,6 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) const uint16_t myflags = (NBD_FLAG_HAS_FLAGS | NBD_FLAG_SEND_TRIM | NBD_FLAG_SEND_FLUSH | NBD_FLAG_SEND_FUA | NBD_FLAG_SEND_WRITE_ZEROES | NBD_FLAG_SEND_CACHE); - bool oldStyle; /* Old style negotiation header, no room for options [ 0 .. 7] passwd ("NBDMAGIC") @@ -1274,33 +1278,19 @@ static coroutine_fn int nbd_negotiate(NBDClient *client, Error **errp) trace_nbd_negotiate_begin(); memcpy(buf, "NBDMAGIC", 8); - oldStyle = client->exp != NULL && !client->tlscreds; - if (oldStyle) { - trace_nbd_negotiate_old_style(client->exp->size, - client->exp->nbdflags | myflags); - stq_be_p(buf + 8, NBD_CLIENT_MAGIC); - stq_be_p(buf + 16, client->exp->size); - stl_be_p(buf + 24, client->exp->nbdflags | myflags); + stq_be_p(buf + 8, NBD_OPTS_MAGIC); + stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES); - if (nbd_write(client->ioc, buf, sizeof(buf), errp) < 0) { - error_prepend(errp, "write failed: "); - return -EINVAL; - } - } else { - stq_be_p(buf + 8, NBD_OPTS_MAGIC); - stw_be_p(buf + 16, NBD_FLAG_FIXED_NEWSTYLE | NBD_FLAG_NO_ZEROES); - - if (nbd_write(client->ioc, buf, 18, errp) < 0) { - error_prepend(errp, "write failed: "); - return -EINVAL; - } - ret = nbd_negotiate_options(client, myflags, errp); - if (ret != 0) { - if (ret < 0) { - error_prepend(errp, "option negotiation failed: "); - } - return ret; + if (nbd_write(client->ioc, buf, 18, errp) < 0) { + error_prepend(errp, "write failed: "); + return -EINVAL; + } + ret = nbd_negotiate_options(client, myflags, errp); + if (ret != 0) { + if (ret < 0) { + error_prepend(errp, "option negotiation failed: "); } + return ret; } assert(!client->optlen); @@ -1316,7 +1306,7 @@ static int nbd_receive_request(QIOChannel *ioc, NBDRequest *request, uint32_t magic; int ret; - ret = nbd_read(ioc, buf, sizeof(buf), errp); + ret = nbd_read(ioc, buf, sizeof(buf), "request", errp); if (ret < 0) { return ret; } @@ -1368,7 +1358,7 @@ void nbd_client_put(NBDClient *client) if (client->tlscreds) { object_unref(OBJECT(client->tlscreds)); } - g_free(client->tlsaclname); + g_free(client->tlsauthz); if (client->exp) { QTAILQ_REMOVE(&client->exp->clients, client, next); nbd_export_put(client->exp); @@ -1465,10 +1455,11 @@ static void nbd_eject_notifier(Notifier *n, void *data) nbd_export_close(exp); } -NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, - uint16_t nbdflags, void (*close)(NBDExport *), - bool writethrough, BlockBackend *on_eject_blk, - Error **errp) +NBDExport *nbd_export_new(BlockDriverState *bs, uint64_t dev_offset, + uint64_t size, const char *name, const char *desc, + const char *bitmap, uint16_t nbdflags, + void (*close)(NBDExport *), bool writethrough, + BlockBackend *on_eject_blk, Error **errp) { AioContext *ctx; BlockBackend *blk; @@ -1481,6 +1472,7 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, * that BDRV_O_INACTIVE is cleared and the image is ready for write * access since the export could be available before migration handover. */ + assert(name); ctx = bdrv_get_aio_context(bs); aio_context_acquire(ctx); bdrv_invalidate_cache(bs, NULL); @@ -1503,15 +1495,48 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, exp->refcount = 1; QTAILQ_INIT(&exp->clients); exp->blk = blk; + assert(dev_offset <= INT64_MAX); exp->dev_offset = dev_offset; + exp->name = g_strdup(name); + exp->description = g_strdup(desc); 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; + assert(size <= INT64_MAX - dev_offset); + exp->size = QEMU_ALIGN_DOWN(size, BDRV_SECTOR_SIZE); + + if (bitmap) { + BdrvDirtyBitmap *bm = NULL; + + while (true) { + bm = bdrv_find_dirty_bitmap(bs, bitmap); + if (bm != NULL || bs->backing == NULL) { + break; + } + + bs = bs->backing->bs; + } + + if (bm == NULL) { + error_setg(errp, "Bitmap '%s' is not found", bitmap); + goto fail; + } + + if (bdrv_dirty_bitmap_check(bm, BDRV_BITMAP_ALLOW_RO, errp)) { + goto fail; + } + + if ((nbdflags & NBD_FLAG_READ_ONLY) && bdrv_is_writable(bs) && + bdrv_dirty_bitmap_enabled(bm)) { + error_setg(errp, + "Enabled bitmap '%s' incompatible with readonly export", + bitmap); + goto fail; + } + + bdrv_dirty_bitmap_set_busy(bm, true); + exp->export_bitmap = bm; + exp->export_bitmap_context = g_strdup_printf("qemu:dirty-bitmap:%s", + bitmap); } - exp->size -= exp->size % BDRV_SECTOR_SIZE; exp->close = close; exp->ctx = blk_get_aio_context(blk); @@ -1523,10 +1548,14 @@ NBDExport *nbd_export_new(BlockDriverState *bs, off_t dev_offset, off_t size, exp->eject_notifier.notify = nbd_eject_notifier; blk_add_remove_bs_notifier(on_eject_blk, &exp->eject_notifier); } + QTAILQ_INSERT_TAIL(&exports, exp, next); + nbd_export_get(exp); return exp; fail: blk_unref(blk); + g_free(exp->name); + g_free(exp->description); g_free(exp); return NULL; } @@ -1543,43 +1572,29 @@ NBDExport *nbd_export_find(const char *name) 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_set_description(NBDExport *exp, const char *description) -{ - g_free(exp->description); - exp->description = g_strdup(description); -} - void nbd_export_close(NBDExport *exp) { NBDClient *client, *next; nbd_export_get(exp); + /* + * TODO: Should we expand QMP NbdServerRemoveNode enum to allow a + * close mode that stops advertising the export to new clients but + * still permits existing clients to run to completion? Because of + * that possibility, nbd_export_close() can be called more than + * once on an export. + */ QTAILQ_FOREACH_SAFE(client, &exp->clients, next, next) { client_close(client, true); } - nbd_export_set_name(exp, NULL); - nbd_export_set_description(exp, NULL); + if (exp->name) { + nbd_export_put(exp); + g_free(exp->name); + exp->name = NULL; + QTAILQ_REMOVE(&exports, exp, next); + } + g_free(exp->description); + exp->description = NULL; nbd_export_put(exp); } @@ -1635,7 +1650,7 @@ void nbd_export_put(NBDExport *exp) } if (exp->export_bitmap) { - bdrv_dirty_bitmap_set_qmp_locked(exp->export_bitmap, false); + bdrv_dirty_bitmap_set_busy(exp->export_bitmap, false); g_free(exp->export_bitmap_context); } @@ -1844,16 +1859,28 @@ static int coroutine_fn nbd_co_send_sparse_read(NBDClient *client, return ret; } -static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, - uint64_t bytes, NBDExtent *extent) +/* + * Populate @extents from block status. Update @bytes to be the actual + * length encoded (which may be smaller than the original), and update + * @nb_extents to the number of extents used. + * + * Returns zero on success and -errno on bdrv_block_status_above failure. + */ +static int blockstatus_to_extents(BlockDriverState *bs, uint64_t offset, + uint64_t *bytes, NBDExtent *extents, + unsigned int *nb_extents) { - uint64_t remaining_bytes = bytes; + uint64_t remaining_bytes = *bytes; + NBDExtent *extent = extents, *extents_end = extents + *nb_extents; + bool first_extent = true; + assert(*nb_extents); while (remaining_bytes) { uint32_t flags; int64_t num; int ret = bdrv_block_status_above(bs, NULL, offset, remaining_bytes, &num, NULL, NULL); + if (ret < 0) { return ret; } @@ -1861,20 +1888,36 @@ static int blockstatus_to_extent_be(BlockDriverState *bs, uint64_t offset, flags = (ret & BDRV_BLOCK_ALLOCATED ? 0 : NBD_STATE_HOLE) | (ret & BDRV_BLOCK_ZERO ? NBD_STATE_ZERO : 0); - if (remaining_bytes == bytes) { + if (first_extent) { extent->flags = flags; - } + extent->length = num; + first_extent = false; + } else if (flags == extent->flags) { + /* extend current extent */ + extent->length += num; + } else { + if (extent + 1 == extents_end) { + break; + } - if (flags != extent->flags) { - break; + /* start new extent */ + extent++; + extent->flags = flags; + extent->length = num; } - offset += num; remaining_bytes -= num; } - cpu_to_be32s(&extent->flags); - extent->length = cpu_to_be32(bytes - remaining_bytes); + extents_end = extent + 1; + + for (extent = extents; extent < extents_end; extent++) { + extent->flags = cpu_to_be32(extent->flags); + extent->length = cpu_to_be32(extent->length); + } + + *bytes -= remaining_bytes; + *nb_extents = extents_end - extents; return 0; } @@ -1910,21 +1953,29 @@ static int nbd_co_send_extents(NBDClient *client, uint64_t handle, /* Get block status from the exported device and send it to the client */ static int nbd_co_send_block_status(NBDClient *client, uint64_t handle, BlockDriverState *bs, uint64_t offset, - uint32_t length, bool last, - uint32_t context_id, Error **errp) + uint32_t length, bool dont_fragment, + bool last, uint32_t context_id, + Error **errp) { int ret; - NBDExtent extent; + unsigned int nb_extents = dont_fragment ? 1 : NBD_MAX_BITMAP_EXTENTS; + NBDExtent *extents = g_new(NBDExtent, nb_extents); + uint64_t final_length = length; - ret = blockstatus_to_extent_be(bs, offset, length, &extent); + ret = blockstatus_to_extents(bs, offset, &final_length, extents, + &nb_extents); if (ret < 0) { + g_free(extents); return nbd_co_send_structured_error( client, handle, -ret, "can't get block status", errp); } - return nbd_co_send_extents(client, handle, &extent, 1, - be32_to_cpu(extent.length), last, - context_id, errp); + ret = nbd_co_send_extents(client, handle, extents, nb_extents, + final_length, last, context_id, errp); + + g_free(extents); + + return ret; } /* @@ -1951,8 +2002,10 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, assert(begin < overall_end && nb_extents); while (begin < overall_end && i < nb_extents) { + bool next_dirty = !dirty; + if (dirty) { - end = bdrv_dirty_bitmap_next_zero(bitmap, begin); + end = bdrv_dirty_bitmap_next_zero(bitmap, begin, UINT64_MAX); } else { bdrv_set_dirty_iter(it, begin); end = bdrv_dirty_iter_next(it); @@ -1962,6 +2015,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, end = MIN(bdrv_dirty_bitmap_size(bitmap), begin + UINT32_MAX + 1 - bdrv_dirty_bitmap_granularity(bitmap)); + next_dirty = dirty; } if (dont_fragment && end > overall_end) { end = overall_end; @@ -1971,7 +2025,7 @@ static unsigned int bitmap_to_extents(BdrvDirtyBitmap *bitmap, uint64_t offset, extents[i].flags = cpu_to_be32(dirty ? NBD_STATE_DIRTY : 0); i++; begin = end; - dirty = !dirty; + dirty = next_dirty; } bdrv_dirty_iter_free(it); @@ -2052,8 +2106,9 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, } } if (request->type == NBD_CMD_WRITE) { - if (nbd_read(client->ioc, req->data, request->len, errp) < 0) { - error_prepend(errp, "reading from socket failed: "); + if (nbd_read(client->ioc, req->data, request->len, "CMD_WRITE data", + errp) < 0) + { return -EIO; } req->complete = true; @@ -2071,13 +2126,24 @@ static int nbd_co_receive_request(NBDRequestData *req, NBDRequest *request, return -EROFS; } if (request->from > client->exp->size || - request->from + request->len > client->exp->size) { + request->len > client->exp->size - request->from) { error_setg(errp, "operation past EOF; From: %" PRIu64 ", Len: %" PRIu32 ", Size: %" PRIu64, request->from, request->len, - (uint64_t)client->exp->size); + client->exp->size); return (request->type == NBD_CMD_WRITE || request->type == NBD_CMD_WRITE_ZEROES) ? -ENOSPC : -EINVAL; } + if (client->check_align && !QEMU_IS_ALIGNED(request->from | request->len, + client->check_align)) { + /* + * The block layer gracefully handles unaligned requests, but + * it's still worth tracing client non-compliance + */ + trace_nbd_co_receive_align_compliance(nbd_cmd_lookup(request->type), + request->from, + request->len, + client->check_align); + } valid_flags = NBD_CMD_FLAG_FUA; if (request->type == NBD_CMD_READ && client->structured_reply) { valid_flags |= NBD_CMD_FLAG_DF; @@ -2135,7 +2201,8 @@ static coroutine_fn int nbd_do_cmd_read(NBDClient *client, NBDRequest *request, } if (client->structured_reply && !(request->flags & NBD_CMD_FLAG_DF) && - request->len) { + request->len && request->type != NBD_CMD_CACHE) + { return nbd_co_send_sparse_read(client, request->handle, request->from, data, request->len, errp); } @@ -2228,10 +2295,12 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, (client->export_meta.base_allocation || client->export_meta.bitmap)) { + bool dont_fragment = request->flags & NBD_CMD_FLAG_REQ_ONE; + if (client->export_meta.base_allocation) { ret = nbd_co_send_block_status(client, request->handle, blk_bs(exp->blk), request->from, - request->len, + request->len, dont_fragment, !client->export_meta.bitmap, NBD_META_ID_BASE_ALLOCATION, errp); @@ -2244,7 +2313,7 @@ static coroutine_fn int nbd_handle_request(NBDClient *client, ret = nbd_co_send_bitmap(client, request->handle, client->exp->export_bitmap, request->from, request->len, - request->flags & NBD_CMD_FLAG_REQ_ONE, + dont_fragment, true, NBD_META_ID_DIRTY_BITMAP, errp); if (ret < 0) { return ret; @@ -2351,13 +2420,8 @@ static void nbd_client_receive_next_request(NBDClient *client) static coroutine_fn void nbd_co_client_start(void *opaque) { NBDClient *client = opaque; - NBDExport *exp = client->exp; Error *local_err = NULL; - if (exp) { - nbd_export_get(exp); - QTAILQ_INSERT_TAIL(&exp->clients, client, next); - } qemu_co_mutex_init(&client->send_lock); if (nbd_negotiate(client, &local_err)) { @@ -2372,15 +2436,13 @@ static coroutine_fn void nbd_co_client_start(void *opaque) } /* - * Create a new client listener on the given export @exp, using the - * given channel @sioc. Begin servicing it in a coroutine. When the - * connection closes, call @close_fn with an indication of whether the - * client completed negotiation. + * Create a new client listener using the given channel @sioc. + * Begin servicing it in a coroutine. When the connection closes, call + * @close_fn with an indication of whether the client completed negotiation. */ -void nbd_client_new(NBDExport *exp, - QIOChannelSocket *sioc, +void nbd_client_new(QIOChannelSocket *sioc, QCryptoTLSCreds *tlscreds, - const char *tlsaclname, + const char *tlsauthz, void (*close_fn)(NBDClient *, bool)) { NBDClient *client; @@ -2388,12 +2450,11 @@ void nbd_client_new(NBDExport *exp, client = g_new0(NBDClient, 1); client->refcount = 1; - client->exp = exp; client->tlscreds = tlscreds; if (tlscreds) { object_ref(OBJECT(client->tlscreds)); } - client->tlsaclname = g_strdup(tlsaclname); + client->tlsauthz = g_strdup(tlsauthz); client->sioc = sioc; object_ref(OBJECT(client->sioc)); client->ioc = QIO_CHANNEL(sioc); @@ -2403,44 +2464,3 @@ void nbd_client_new(NBDExport *exp, co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } - -void nbd_export_bitmap(NBDExport *exp, const char *bitmap, - const char *bitmap_export_name, Error **errp) -{ - BdrvDirtyBitmap *bm = NULL; - BlockDriverState *bs = blk_bs(exp->blk); - - if (exp->export_bitmap) { - error_setg(errp, "Export bitmap is already set"); - return; - } - - while (true) { - bm = bdrv_find_dirty_bitmap(bs, bitmap); - if (bm != NULL || bs->backing == NULL) { - break; - } - - bs = bs->backing->bs; - } - - if (bm == NULL) { - error_setg(errp, "Bitmap '%s' is not found", bitmap); - return; - } - - if (bdrv_dirty_bitmap_enabled(bm)) { - error_setg(errp, "Bitmap '%s' is enabled", bitmap); - return; - } - - if (bdrv_dirty_bitmap_qmp_locked(bm)) { - error_setg(errp, "Bitmap '%s' is locked", bitmap); - return; - } - - bdrv_dirty_bitmap_set_qmp_locked(bm, true); - exp->export_bitmap = bm; - exp->export_bitmap_context = - g_strdup_printf("qemu:dirty-bitmap:%s", bitmap_export_name); -} diff --git a/nbd/trace-events b/nbd/trace-events index 5e1d4afe8e..7ab6b3788c 100644 --- a/nbd/trace-events +++ b/nbd/trace-events @@ -1,21 +1,25 @@ -# nbd/client.c +# See docs/devel/tracing.txt for syntax documentation. + +# client.c nbd_send_option_request(uint32_t opt, const char *name, uint32_t len) "Sending option request %" PRIu32" (%s), len %" PRIu32 nbd_receive_option_reply(uint32_t option, const char *optname, uint32_t type, const char *typename, uint32_t length) "Received option reply %" PRIu32" (%s), type %" PRIu32" (%s), len %" PRIu32 +nbd_server_error_msg(uint32_t err, const char *type, const char *msg) "server reported error 0x%" PRIx32 " (%s) with additional message: %s" nbd_reply_err_unsup(uint32_t option, const char *name) "server doesn't understand request %" PRIu32 " (%s), attempting fallback" -nbd_opt_go_start(const char *name) "Attempting NBD_OPT_GO for export '%s'" -nbd_opt_go_success(void) "Export is good to go" -nbd_opt_go_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" -nbd_opt_go_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 +nbd_receive_list(const char *name, const char *desc) "export list includes '%s', description '%s'" +nbd_opt_info_go_start(const char *opt, const char *name) "Attempting %s for export '%s'" +nbd_opt_info_go_success(const char *opt) "Export is ready after %s request" +nbd_opt_info_unknown(int info, const char *name) "Ignoring unknown info %d (%s)" +nbd_opt_info_block_size(uint32_t minimum, uint32_t preferred, uint32_t maximum) "Block sizes are 0x%" PRIx32 ", 0x%" PRIx32 ", 0x%" PRIx32 nbd_receive_query_exports_start(const char *wantname) "Querying export list for '%s'" nbd_receive_query_exports_success(const char *wantname) "Found desired export name '%s'" nbd_receive_starttls_new_client(void) "Setting up TLS" nbd_receive_starttls_tls_handshake(void) "Starting TLS handshake" -nbd_opt_meta_request(const char *context, const char *export) "Requesting to set meta context %s for export %s" -nbd_opt_meta_reply(const char *context, uint32_t id) "Received mapping of context %s to id %" PRIu32 -nbd_receive_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" +nbd_opt_meta_request(const char *optname, const char *context, const char *export) "Requesting %s %s for export %s" +nbd_opt_meta_reply(const char *optname, const char *context, uint32_t id) "Received %s mapping of %s to id %" PRIu32 +nbd_start_negotiate(void *tlscreds, const char *hostname) "Receiving negotiation tlscreds=%p hostname=%s" nbd_receive_negotiate_magic(uint64_t magic) "Magic is 0x%" PRIx64 nbd_receive_negotiate_server_flags(uint32_t globalflags) "Global flags are 0x%" PRIx32 -nbd_receive_negotiate_default_name(void) "Using default NBD export name \"\"" +nbd_receive_negotiate_name(const char *name) "Requesting NBD export name '%s'" nbd_receive_negotiate_size_flags(uint64_t size, uint16_t flags) "Size is %" PRIu64 ", export flags 0x%" PRIx16 nbd_init_set_socket(void) "Setting NBD socket" nbd_init_set_block_size(unsigned long block_size) "Setting block size to %lu" @@ -31,10 +35,10 @@ nbd_send_request(uint64_t from, uint32_t len, uint64_t handle, uint16_t flags, u nbd_receive_simple_reply(int32_t error, const char *errname, uint64_t handle) "Got simple reply: { .error = %" PRId32 " (%s), handle = %" PRIu64" }" nbd_receive_structured_reply_chunk(uint16_t flags, uint16_t type, const char *name, uint64_t handle, uint32_t length) "Got structured reply chunk: { flags = 0x%" PRIx16 ", type = %d (%s), handle = %" PRIu64 ", length = %" PRIu32 " }" -# nbd/common.c +# common.c nbd_unknown_error(int err) "Squashing unexpected error %d to EINVAL" -# nbd/server.c +# server.c nbd_negotiate_send_rep_len(uint32_t opt, const char *optname, uint32_t type, const char *typename, uint32_t len) "Reply opt=%" PRIu32 " (%s), type=%" PRIu32 " (%s), len=%" PRIu32 nbd_negotiate_send_rep_err(const char *msg) "sending error message \"%s\"" nbd_negotiate_send_rep_list(const char *name, const char *desc) "Advertising export name '%s' description '%s'" @@ -54,7 +58,6 @@ nbd_negotiate_options_flags(uint32_t flags) "Received client flags 0x%" PRIx32 nbd_negotiate_options_check_magic(uint64_t magic) "Checking opts magic 0x%" PRIx64 nbd_negotiate_options_check_option(uint32_t option, const char *name) "Checking option %" PRIu32 " (%s)" nbd_negotiate_begin(void) "Beginning negotiation" -nbd_negotiate_old_style(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags 0x%x" nbd_negotiate_new_style_size_flags(uint64_t size, unsigned flags) "advertising size %" PRIu64 " and flags 0x%x" nbd_negotiate_success(void) "Negotiation succeeded" nbd_receive_request(uint32_t magic, uint16_t flags, uint16_t type, uint64_t from, uint32_t len) "Got request: { magic = 0x%" PRIx32 ", .flags = 0x%" PRIx16 ", .type = 0x%" PRIx16 ", from = %" PRIu64 ", len = %" PRIu32 " }" @@ -68,5 +71,5 @@ nbd_co_send_extents(uint64_t handle, unsigned int extents, uint32_t id, uint64_t nbd_co_send_structured_error(uint64_t handle, int err, const char *errname, const char *msg) "Send structured error reply: handle = %" PRIu64 ", error = %d (%s), msg = '%s'" nbd_co_receive_request_decode_type(uint64_t handle, uint16_t type, const char *name) "Decoding type: handle = %" PRIu64 ", type = %" PRIu16 " (%s)" nbd_co_receive_request_payload_received(uint64_t handle, uint32_t len) "Payload received: handle = %" PRIu64 ", len = %" PRIu32 -nbd_co_receive_request_cmd_write(uint32_t len) "Reading %" PRIu32 " byte(s)" +nbd_co_receive_align_compliance(const char *op, uint64_t from, uint32_t len, uint32_t align) "client sent non-compliant unaligned %s request: from=0x%" PRIx64 ", len=0x%" PRIx32 ", align=0x%" PRIx32 nbd_trip(void) "Reading request" diff --git a/net/Makefile.objs b/net/Makefile.objs index b2bf88a0ef..c5d076d19c 100644 --- a/net/Makefile.objs +++ b/net/Makefile.objs @@ -2,9 +2,14 @@ common-obj-y = net.o queue.o checksum.o util.o hub.o common-obj-y += socket.o common-obj-y += dump.o common-obj-y += eth.o +common-obj-y += announce.o common-obj-$(CONFIG_L2TPV3) += l2tpv3.o -common-obj-$(CONFIG_POSIX) += vhost-user.o +common-obj-$(call land,$(CONFIG_VIRTIO_NET),$(CONFIG_VHOST_NET_USER)) += vhost-user.o +common-obj-$(call land,$(call lnot,$(CONFIG_VIRTIO_NET)),$(CONFIG_VHOST_NET_USER)) += vhost-user-stub.o +common-obj-$(CONFIG_ALL) += vhost-user-stub.o common-obj-$(CONFIG_SLIRP) += slirp.o +slirp.o-cflags := $(SLIRP_CFLAGS) +slirp.o-libs := $(SLIRP_LIBS) common-obj-$(CONFIG_VDE) += vde.o common-obj-$(CONFIG_NETMAP) += netmap.o common-obj-y += filter.o diff --git a/net/announce.c b/net/announce.c new file mode 100644 index 0000000000..91e9a6e267 --- /dev/null +++ b/net/announce.c @@ -0,0 +1,140 @@ +/* + * Self-announce + * (c) 2017-2019 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 "qemu-common.h" +#include "net/announce.h" +#include "net/net.h" +#include "qapi/clone-visitor.h" +#include "qapi/qapi-visit-net.h" +#include "qapi/qapi-commands-net.h" +#include "trace.h" + +int64_t qemu_announce_timer_step(AnnounceTimer *timer) +{ + int64_t step; + + step = timer->params.initial + + (timer->params.rounds - timer->round - 1) * + timer->params.step; + + if (step < 0 || step > timer->params.max) { + step = timer->params.max; + } + timer_mod(timer->tm, qemu_clock_get_ms(timer->type) + step); + + return step; +} + +void qemu_announce_timer_del(AnnounceTimer *timer) +{ + if (timer->tm) { + timer_del(timer->tm); + timer_free(timer->tm); + timer->tm = NULL; + } +} + +/* + * Under BQL/main thread + * Reset the timer to the given parameters/type/notifier. + */ +void qemu_announce_timer_reset(AnnounceTimer *timer, + AnnounceParameters *params, + QEMUClockType type, + QEMUTimerCB *cb, + void *opaque) +{ + /* + * We're under the BQL, so the current timer can't + * be firing, so we should be able to delete it. + */ + qemu_announce_timer_del(timer); + + QAPI_CLONE_MEMBERS(AnnounceParameters, &timer->params, params); + timer->round = params->rounds; + timer->type = type; + timer->tm = timer_new_ms(type, cb, opaque); +} + +#ifndef ETH_P_RARP +#define ETH_P_RARP 0x8035 +#endif +#define ARP_HTYPE_ETH 0x0001 +#define ARP_PTYPE_IP 0x0800 +#define ARP_OP_REQUEST_REV 0x3 + +static int announce_self_create(uint8_t *buf, + uint8_t *mac_addr) +{ + /* Ethernet header. */ + memset(buf, 0xff, 6); /* destination MAC addr */ + memcpy(buf + 6, mac_addr, 6); /* source MAC addr */ + *(uint16_t *)(buf + 12) = htons(ETH_P_RARP); /* ethertype */ + + /* RARP header. */ + *(uint16_t *)(buf + 14) = htons(ARP_HTYPE_ETH); /* hardware addr space */ + *(uint16_t *)(buf + 16) = htons(ARP_PTYPE_IP); /* protocol addr space */ + *(buf + 18) = 6; /* hardware addr length (ethernet) */ + *(buf + 19) = 4; /* protocol addr length (IPv4) */ + *(uint16_t *)(buf + 20) = htons(ARP_OP_REQUEST_REV); /* opcode */ + memcpy(buf + 22, mac_addr, 6); /* source hw addr */ + memset(buf + 28, 0x00, 4); /* source protocol addr */ + memcpy(buf + 32, mac_addr, 6); /* target hw addr */ + memset(buf + 38, 0x00, 4); /* target protocol addr */ + + /* Padding to get up to 60 bytes (ethernet min packet size, minus FCS). */ + memset(buf + 42, 0x00, 18); + + return 60; /* len (FCS will be added by hardware) */ +} + +static void qemu_announce_self_iter(NICState *nic, void *opaque) +{ + uint8_t buf[60]; + int len; + + trace_qemu_announce_self_iter(qemu_ether_ntoa(&nic->conf->macaddr)); + len = announce_self_create(buf, nic->conf->macaddr.a); + + qemu_send_packet_raw(qemu_get_queue(nic), buf, len); + + /* if the NIC provides it's own announcement support, use it as well */ + if (nic->ncs->info->announce) { + nic->ncs->info->announce(nic->ncs); + } +} +static void qemu_announce_self_once(void *opaque) +{ + AnnounceTimer *timer = (AnnounceTimer *)opaque; + + qemu_foreach_nic(qemu_announce_self_iter, NULL); + + if (--timer->round) { + qemu_announce_timer_step(timer); + } else { + qemu_announce_timer_del(timer); + } +} + +void qemu_announce_self(AnnounceTimer *timer, AnnounceParameters *params) +{ + qemu_announce_timer_reset(timer, params, QEMU_CLOCK_REALTIME, + qemu_announce_self_once, timer); + if (params->rounds) { + qemu_announce_self_once(timer); + } else { + qemu_announce_timer_del(timer); + } +} + +void qmp_announce_self(AnnounceParameters *params, Error **errp) +{ + static AnnounceTimer announce_timer; + qemu_announce_self(&announce_timer, params); +} diff --git a/net/checksum.c b/net/checksum.c index 4da72a6a6c..273bc9c6bc 100644 --- a/net/checksum.c +++ b/net/checksum.c @@ -43,7 +43,7 @@ uint32_t net_checksum_add_cont(int len, uint8_t *buf, int seq) uint16_t net_checksum_finish(uint32_t sum) { while (sum>>16) - sum = (sum & 0xFFFF)+(sum >> 16); + sum = (sum & 0xFFFF)+(sum >> 16); return ~sum; } diff --git a/net/colo-compare.c b/net/colo-compare.c index dd745a491b..bf10526f05 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -27,11 +27,21 @@ #include "qemu/sockets.h" #include "colo.h" #include "sysemu/iothread.h" +#include "net/colo-compare.h" +#include "migration/colo.h" +#include "migration/migration.h" +#include "util.h" #define TYPE_COLO_COMPARE "colo-compare" #define COLO_COMPARE(obj) \ OBJECT_CHECK(CompareState, (obj), TYPE_COLO_COMPARE) +static QTAILQ_HEAD(, CompareState) net_compares = + QTAILQ_HEAD_INITIALIZER(net_compares); + +static NotifierList colo_compare_notifiers = + NOTIFIER_LIST_INITIALIZER(colo_compare_notifiers); + #define COMPARE_READ_LEN_MAX NET_BUFSIZE #define MAX_QUEUE_SIZE 1024 @@ -41,6 +51,10 @@ /* TODO: Should be configurable */ #define REGULAR_PACKET_CHECK_MS 3000 +static QemuMutex event_mtx; +static QemuCond event_complete_cond; +static int event_unhandled_count; + /* * + CompareState ++ * | | @@ -87,6 +101,11 @@ typedef struct CompareState { IOThread *iothread; GMainContext *worker_context; QEMUTimer *packet_check_timer; + + QEMUBH *event_bh; + enum colo_event event; + + QTAILQ_ENTRY(CompareState) next; } CompareState; typedef struct CompareClass { @@ -98,6 +117,12 @@ enum { SECONDARY_IN, }; +static void colo_compare_inconsistency_notify(void) +{ + notifier_list_notify(&colo_compare_notifiers, + migrate_get_current()); +} + static int compare_chr_send(CompareState *s, const uint8_t *buf, uint32_t size, @@ -105,19 +130,19 @@ static int compare_chr_send(CompareState *s, static gint seq_sorter(Packet *a, Packet *b, gpointer data) { - struct tcphdr *atcp, *btcp; + struct tcp_hdr *atcp, *btcp; - atcp = (struct tcphdr *)(a->transport_header); - btcp = (struct tcphdr *)(b->transport_header); + atcp = (struct tcp_hdr *)(a->transport_header); + btcp = (struct tcp_hdr *)(b->transport_header); return ntohl(atcp->th_seq) - ntohl(btcp->th_seq); } static void fill_pkt_tcp_info(void *data, uint32_t *max_ack) { Packet *pkt = data; - struct tcphdr *tcphd; + struct tcp_hdr *tcphd; - tcphd = (struct tcphdr *)pkt->transport_header; + tcphd = (struct tcp_hdr *)pkt->transport_header; pkt->tcp_seq = ntohl(tcphd->th_seq); pkt->tcp_ack = ntohl(tcphd->th_ack); @@ -261,14 +286,6 @@ static bool colo_mark_tcp_pkt(Packet *ppkt, Packet *spkt, { *mark = 0; - if (ppkt->tcp_seq == spkt->tcp_seq && ppkt->seq_end == spkt->seq_end) { - if (colo_compare_packet_payload(ppkt, spkt, - ppkt->header_size, spkt->header_size, - ppkt->payload_size)) { - *mark = COLO_COMPARE_FREE_SECONDARY | COLO_COMPARE_FREE_PRIMARY; - return true; - } - } if (ppkt->tcp_seq == spkt->tcp_seq && ppkt->seq_end == spkt->seq_end) { if (colo_compare_packet_payload(ppkt, spkt, ppkt->header_size, spkt->header_size, @@ -413,10 +430,7 @@ sec: qemu_hexdump((char *)spkt->data, stderr, "colo-compare spkt", spkt->size); - /* - * colo_compare_inconsistent_notify(); - * TODO: notice to checkpoint(); - */ + colo_compare_inconsistency_notify(); } } @@ -547,8 +561,18 @@ static int colo_old_packet_check_one(Packet *pkt, int64_t *check_time) } } +void colo_compare_register_notifier(Notifier *notify) +{ + notifier_list_add(&colo_compare_notifiers, notify); +} + +void colo_compare_unregister_notifier(Notifier *notify) +{ + notifier_remove(notify); +} + static int colo_old_packet_check_one_conn(Connection *conn, - void *user_data) + void *user_data) { GList *result = NULL; int64_t check_time = REGULAR_PACKET_CHECK_MS; @@ -559,10 +583,7 @@ static int colo_old_packet_check_one_conn(Connection *conn, if (result) { /* Do checkpoint will flush old packet */ - /* - * TODO: Notify colo frame to do checkpoint. - * colo_compare_inconsistent_notify(); - */ + colo_compare_inconsistency_notify(); return 0; } @@ -606,11 +627,12 @@ static void colo_compare_packet(CompareState *s, Connection *conn, /* * If one packet arrive late, the secondary_list or * primary_list will be empty, so we can't compare it - * until next comparison. + * until next comparison. If the packets in the list are + * timeout, it will trigger a checkpoint request. */ trace_colo_compare_main("packet different"); g_queue_push_head(&conn->primary_list, pkt); - /* TODO: colo_notify_checkpoint();*/ + colo_compare_inconsistency_notify(); break; } } @@ -736,6 +758,25 @@ static void check_old_packet_regular(void *opaque) REGULAR_PACKET_CHECK_MS); } +/* Public API, Used for COLO frame to notify compare event */ +void colo_notify_compares_event(void *opaque, int event, Error **errp) +{ + CompareState *s; + + qemu_mutex_lock(&event_mtx); + QTAILQ_FOREACH(s, &net_compares, next) { + s->event = event; + qemu_bh_schedule(s->event_bh); + event_unhandled_count++; + } + /* Wait all compare threads to finish handling this event */ + while (event_unhandled_count > 0) { + qemu_cond_wait(&event_complete_cond, &event_mtx); + } + + qemu_mutex_unlock(&event_mtx); +} + static void colo_compare_timer_init(CompareState *s) { AioContext *ctx = iothread_get_aio_context(s->iothread); @@ -756,6 +797,30 @@ static void colo_compare_timer_del(CompareState *s) } } +static void colo_flush_packets(void *opaque, void *user_data); + +static void colo_compare_handle_event(void *opaque) +{ + CompareState *s = opaque; + + switch (s->event) { + case COLO_EVENT_CHECKPOINT: + g_queue_foreach(&s->conn_list, colo_flush_packets, s); + break; + case COLO_EVENT_FAILOVER: + break; + default: + break; + } + + assert(event_unhandled_count > 0); + + qemu_mutex_lock(&event_mtx); + event_unhandled_count--; + qemu_cond_broadcast(&event_complete_cond); + qemu_mutex_unlock(&event_mtx); +} + static void colo_compare_iothread(CompareState *s) { object_ref(OBJECT(s->iothread)); @@ -769,6 +834,7 @@ static void colo_compare_iothread(CompareState *s) s, s->worker_context, true); colo_compare_timer_init(s); + s->event_bh = qemu_bh_new(colo_compare_handle_event, s); } static char *compare_get_pri_indev(Object *obj, Error **errp) @@ -884,6 +950,12 @@ static int find_and_check_chardev(Chardev **chr, return 1; } + if (!qemu_chr_has_feature(*chr, QEMU_CHAR_FEATURE_GCONTEXT)) { + error_setg(errp, "chardev \"%s\" cannot switch context", + chr_name); + return 1; + } + return 0; } @@ -926,8 +998,13 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) net_socket_rs_init(&s->pri_rs, compare_pri_rs_finalize, s->vnet_hdr); net_socket_rs_init(&s->sec_rs, compare_sec_rs_finalize, s->vnet_hdr); + QTAILQ_INSERT_TAIL(&net_compares, s, next); + g_queue_init(&s->conn_list); + qemu_mutex_init(&event_mtx); + qemu_cond_init(&event_complete_cond); + s->connection_track_table = g_hash_table_new_full(connection_key_hash, connection_key_equal, g_free, @@ -990,6 +1067,7 @@ static void colo_compare_init(Object *obj) static void colo_compare_finalize(Object *obj) { CompareState *s = COLO_COMPARE(obj); + CompareState *tmp = NULL; qemu_chr_fe_deinit(&s->chr_pri_in, false); qemu_chr_fe_deinit(&s->chr_sec_in, false); @@ -997,6 +1075,16 @@ static void colo_compare_finalize(Object *obj) if (s->iothread) { colo_compare_timer_del(s); } + + qemu_bh_delete(s->event_bh); + + QTAILQ_FOREACH(tmp, &net_compares, next) { + if (tmp == s) { + QTAILQ_REMOVE(&net_compares, s, next); + break; + } + } + /* Release all unhandled packets after compare thead exited */ g_queue_foreach(&s->conn_list, colo_flush_packets, s); @@ -1009,6 +1097,10 @@ static void colo_compare_finalize(Object *obj) if (s->iothread) { object_unref(OBJECT(s->iothread)); } + + qemu_mutex_destroy(&event_mtx); + qemu_cond_destroy(&event_complete_cond); + g_free(s->pri_indev); g_free(s->sec_indev); g_free(s->outdev); diff --git a/net/colo-compare.h b/net/colo-compare.h new file mode 100644 index 0000000000..22ddd512e2 --- /dev/null +++ b/net/colo-compare.h @@ -0,0 +1,24 @@ +/* + * COarse-grain LOck-stepping Virtual Machines for Non-stop Service (COLO) + * (a.k.a. Fault Tolerance or Continuous Replication) + * + * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. + * Copyright (c) 2017 FUJITSU LIMITED + * Copyright (c) 2017 Intel Corporation + * + * Authors: + * zhanghailiang + * 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. + */ + +#ifndef QEMU_COLO_COMPARE_H +#define QEMU_COLO_COMPARE_H + +void colo_notify_compares_event(void *opaque, int event, Error **errp); +void colo_compare_register_notifier(Notifier *notify); +void colo_compare_unregister_notifier(Notifier *notify); + +#endif /* QEMU_COLO_COMPARE_H */ diff --git a/net/colo.c b/net/colo.c index 6dda4ed66e..8196b35837 100644 --- a/net/colo.c +++ b/net/colo.c @@ -15,6 +15,7 @@ #include "qemu/osdep.h" #include "trace.h" #include "colo.h" +#include "util.h" uint32_t connection_key_hash(const void *opaque) { @@ -137,7 +138,7 @@ Connection *connection_new(ConnectionKey *key) conn->ip_proto = key->ip_proto; conn->processing = false; conn->offset = 0; - conn->syn_flag = 0; + conn->tcp_state = TCPS_CLOSED; conn->pack = 0; conn->sack = 0; g_queue_init(&conn->primary_list); @@ -221,3 +222,11 @@ Connection *connection_get(GHashTable *connection_track_table, return conn; } + +bool connection_has_tracked(GHashTable *connection_track_table, + ConnectionKey *key) +{ + Connection *conn = g_hash_table_lookup(connection_track_table, key); + + return conn ? true : false; +} diff --git a/net/colo.h b/net/colo.h index da6c36dcf7..b21c6830b5 100644 --- a/net/colo.h +++ b/net/colo.h @@ -15,9 +15,9 @@ #ifndef QEMU_COLO_PROXY_H #define QEMU_COLO_PROXY_H -#include "slirp/slirp.h" #include "qemu/jhash.h" #include "qemu/timer.h" +#include "net/eth.h" #define HASHTABLE_MAX_SIZE 16384 @@ -80,12 +80,10 @@ typedef struct Connection { /* the maximum of acknowledgement number in secondary_list queue */ uint32_t sack; /* offset = secondary_seq - primary_seq */ - tcp_seq offset; - /* - * we use this flag update offset func - * run once in independent tcp connection - */ - int syn_flag; + uint32_t offset; + + int tcp_state; /* TCP FSM state */ + uint32_t fin_ack_seq; /* the seq of 'fin=1,ack=1' */ } Connection; uint32_t connection_key_hash(const void *opaque); @@ -99,6 +97,8 @@ void connection_destroy(void *opaque); Connection *connection_get(GHashTable *connection_track_table, ConnectionKey *key, GQueue *conn_list); +bool connection_has_tracked(GHashTable *connection_track_table, + ConnectionKey *key); void connection_hashtable_reset(GHashTable *connection_track_table); Packet *packet_new(const void *data, int size, int vnet_hdr_len); void packet_destroy(void *opaque, void *user_data); diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index f584e4eba4..b464abe5e8 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -20,11 +20,16 @@ #include "qemu/main-loop.h" #include "qemu/iov.h" #include "net/checksum.h" +#include "net/colo.h" +#include "migration/colo.h" +#include "util.h" #define FILTER_COLO_REWRITER(obj) \ OBJECT_CHECK(RewriterState, (obj), TYPE_FILTER_REWRITER) #define TYPE_FILTER_REWRITER "filter-rewriter" +#define FAILOVER_MODE_ON true +#define FAILOVER_MODE_OFF false typedef struct RewriterState { NetFilterState parent_obj; @@ -32,8 +37,14 @@ typedef struct RewriterState { /* hashtable to save connection */ GHashTable *connection_track_table; bool vnet_hdr; + bool failover_mode; } RewriterState; +static void filter_rewriter_failover_mode(RewriterState *s) +{ + s->failover_mode = FAILOVER_MODE_ON; +} + static void filter_rewriter_flush(NetFilterState *nf) { RewriterState *s = FILTER_COLO_REWRITER(nf); @@ -59,13 +70,13 @@ static int is_tcp_packet(Packet *pkt) } /* handle tcp packet from primary guest */ -static int handle_primary_tcp_pkt(NetFilterState *nf, +static int handle_primary_tcp_pkt(RewriterState *rf, Connection *conn, - Packet *pkt) + Packet *pkt, ConnectionKey *key) { - struct tcphdr *tcp_pkt; + struct tcp_hdr *tcp_pkt; - tcp_pkt = (struct tcphdr *)pkt->transport_header; + tcp_pkt = (struct tcp_hdr *)pkt->transport_header; if (trace_event_get_state_backends(TRACE_COLO_FILTER_REWRITER_DEBUG)) { trace_colo_filter_rewriter_pkt_info(__func__, inet_ntoa(pkt->ip->ip_src), inet_ntoa(pkt->ip->ip_dst), @@ -74,23 +85,28 @@ static int handle_primary_tcp_pkt(NetFilterState *nf, trace_colo_filter_rewriter_conn_offset(conn->offset); } + if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN)) && + conn->tcp_state == TCPS_SYN_SENT) { + conn->tcp_state = TCPS_ESTABLISHED; + } + if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { /* * we use this flag update offset func * run once in independent tcp connection */ - conn->syn_flag = 1; + conn->tcp_state = TCPS_SYN_RECEIVED; } if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK)) { - if (conn->syn_flag) { + if (conn->tcp_state == TCPS_SYN_RECEIVED) { /* * offset = secondary_seq - primary seq * ack packet sent by guest from primary node, * so we use th_ack - 1 get primary_seq */ conn->offset -= (ntohl(tcp_pkt->th_ack) - 1); - conn->syn_flag = 0; + conn->tcp_state = TCPS_ESTABLISHED; } if (conn->offset) { /* handle packets to the secondary from the primary */ @@ -99,19 +115,71 @@ static int handle_primary_tcp_pkt(NetFilterState *nf, net_checksum_calculate((uint8_t *)pkt->data + pkt->vnet_hdr_len, pkt->size - pkt->vnet_hdr_len); } + + /* + * Passive close step 3 + */ + if ((conn->tcp_state == TCPS_LAST_ACK) && + (ntohl(tcp_pkt->th_ack) == (conn->fin_ack_seq + 1))) { + conn->tcp_state = TCPS_CLOSED; + g_hash_table_remove(rf->connection_track_table, key); + } + } + + if ((tcp_pkt->th_flags & TH_FIN) == TH_FIN) { + /* + * Passive close. + * Step 1: + * The *server* side of this connect is VM, *client* tries to close + * the connection. We will into CLOSE_WAIT status. + * + * Step 2: + * In this step we will into LAST_ACK status. + * + * We got 'fin=1, ack=1' packet from server side, we need to + * record the seq of 'fin=1, ack=1' packet. + * + * Step 3: + * We got 'ack=1' packets from client side, it acks 'fin=1, ack=1' + * packet from server side. From this point, we can ensure that there + * will be no packets in the connection, except that, some errors + * happen between the path of 'filter object' and vNIC, if this rare + * case really happen, we can still create a new connection, + * So it is safe to remove the connection from connection_track_table. + * + */ + if (conn->tcp_state == TCPS_ESTABLISHED) { + conn->tcp_state = TCPS_CLOSE_WAIT; + } + + /* + * Active close step 2. + */ + if (conn->tcp_state == TCPS_FIN_WAIT_1) { + /* + * For simplify implementation, we needn't wait 2MSL time + * in filter rewriter. Because guest kernel will track the + * TCP status and wait 2MSL time, if client resend the FIN + * packet, guest will apply the last ACK too. + * So, we skip the TCPS_TIME_WAIT state here and go straight + * to TCPS_CLOSED state. + */ + conn->tcp_state = TCPS_CLOSED; + g_hash_table_remove(rf->connection_track_table, key); + } } return 0; } /* handle tcp packet from secondary guest */ -static int handle_secondary_tcp_pkt(NetFilterState *nf, +static int handle_secondary_tcp_pkt(RewriterState *rf, Connection *conn, - Packet *pkt) + Packet *pkt, ConnectionKey *key) { - struct tcphdr *tcp_pkt; + struct tcp_hdr *tcp_pkt; - tcp_pkt = (struct tcphdr *)pkt->transport_header; + tcp_pkt = (struct tcp_hdr *)pkt->transport_header; if (trace_event_get_state_backends(TRACE_COLO_FILTER_REWRITER_DEBUG)) { trace_colo_filter_rewriter_pkt_info(__func__, @@ -121,7 +189,8 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf, trace_colo_filter_rewriter_conn_offset(conn->offset); } - if (((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { + if (conn->tcp_state == TCPS_SYN_RECEIVED && + ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == (TH_ACK | TH_SYN))) { /* * save offset = secondary_seq and then * in handle_primary_tcp_pkt make offset @@ -130,6 +199,12 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf, conn->offset = ntohl(tcp_pkt->th_seq); } + /* VM active connect */ + if (conn->tcp_state == TCPS_CLOSED && + ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_SYN)) { + conn->tcp_state = TCPS_SYN_SENT; + } + if ((tcp_pkt->th_flags & (TH_ACK | TH_SYN)) == TH_ACK) { /* Only need to adjust seq while offset is Non-zero */ if (conn->offset) { @@ -141,6 +216,32 @@ static int handle_secondary_tcp_pkt(NetFilterState *nf, } } + /* + * Passive close step 2: + */ + if (conn->tcp_state == TCPS_CLOSE_WAIT && + (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == (TH_ACK | TH_FIN)) { + conn->fin_ack_seq = ntohl(tcp_pkt->th_seq); + conn->tcp_state = TCPS_LAST_ACK; + } + + /* + * Active close + * + * Step 1: + * The *server* side of this connect is VM, *server* tries to close + * the connection. + * + * Step 2: + * We will into CLOSE_WAIT status. + * We simplify the TCPS_FIN_WAIT_2, TCPS_TIME_WAIT and + * CLOSING status. + */ + if (conn->tcp_state == TCPS_ESTABLISHED && + (tcp_pkt->th_flags & (TH_ACK | TH_FIN)) == TH_FIN) { + conn->tcp_state = TCPS_FIN_WAIT_1; + } + return 0; } @@ -184,13 +285,20 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, */ reverse_connection_key(&key); } + + /* After failover we needn't change new TCP packet */ + if (s->failover_mode && + !connection_has_tracked(s->connection_track_table, &key)) { + goto out; + } + conn = connection_get(s->connection_track_table, &key, NULL); if (sender == nf->netdev) { /* NET_FILTER_DIRECTION_TX */ - if (!handle_primary_tcp_pkt(nf, conn, pkt)) { + if (!handle_primary_tcp_pkt(s, conn, pkt, &key)) { qemu_net_queue_send(s->incoming_queue, sender, 0, (const uint8_t *)pkt->data, pkt->size, NULL); packet_destroy(pkt, NULL); @@ -203,7 +311,7 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, } } else { /* NET_FILTER_DIRECTION_RX */ - if (!handle_secondary_tcp_pkt(nf, conn, pkt)) { + if (!handle_secondary_tcp_pkt(s, conn, pkt, &key)) { qemu_net_queue_send(s->incoming_queue, sender, 0, (const uint8_t *)pkt->data, pkt->size, NULL); packet_destroy(pkt, NULL); @@ -217,11 +325,49 @@ static ssize_t colo_rewriter_receive_iov(NetFilterState *nf, } } +out: packet_destroy(pkt, NULL); pkt = NULL; return 0; } +static void reset_seq_offset(gpointer key, gpointer value, gpointer user_data) +{ + Connection *conn = (Connection *)value; + + conn->offset = 0; +} + +static gboolean offset_is_nonzero(gpointer key, + gpointer value, + gpointer user_data) +{ + Connection *conn = (Connection *)value; + + return conn->offset ? true : false; +} + +static void colo_rewriter_handle_event(NetFilterState *nf, int event, + Error **errp) +{ + RewriterState *rs = FILTER_COLO_REWRITER(nf); + + switch (event) { + case COLO_EVENT_CHECKPOINT: + g_hash_table_foreach(rs->connection_track_table, + reset_seq_offset, NULL); + break; + case COLO_EVENT_FAILOVER: + if (!g_hash_table_find(rs->connection_track_table, + offset_is_nonzero, NULL)) { + filter_rewriter_failover_mode(rs); + } + break; + default: + break; + } +} + static void colo_rewriter_cleanup(NetFilterState *nf) { RewriterState *s = FILTER_COLO_REWRITER(nf); @@ -265,6 +411,7 @@ static void filter_rewriter_init(Object *obj) RewriterState *s = FILTER_COLO_REWRITER(obj); s->vnet_hdr = false; + s->failover_mode = FAILOVER_MODE_OFF; object_property_add_bool(obj, "vnet_hdr_support", filter_rewriter_get_vnet_hdr, filter_rewriter_set_vnet_hdr, NULL); @@ -277,6 +424,7 @@ static void colo_rewriter_class_init(ObjectClass *oc, void *data) nfc->setup = colo_rewriter_setup; nfc->cleanup = colo_rewriter_cleanup; nfc->receive_iov = colo_rewriter_receive_iov; + nfc->handle_event = colo_rewriter_handle_event; } static const TypeInfo colo_rewriter_info = { diff --git a/net/filter.c b/net/filter.c index 2fd7d7d663..28d1930db7 100644 --- a/net/filter.c +++ b/net/filter.c @@ -17,6 +17,8 @@ #include "net/vhost_net.h" #include "qom/object_interfaces.h" #include "qemu/iov.h" +#include "net/colo.h" +#include "migration/colo.h" static inline bool qemu_can_skip_netfilter(NetFilterState *nf) { @@ -53,7 +55,7 @@ static NetFilterState *netfilter_next(NetFilterState *nf, next = QTAILQ_NEXT(nf, next); } else { /* reverse order */ - next = QTAILQ_PREV(nf, NetFilterHead, next); + next = QTAILQ_PREV(nf, next); } return next; @@ -245,11 +247,26 @@ static void netfilter_finalize(Object *obj) g_free(nf->netdev_id); } +static void default_handle_event(NetFilterState *nf, int event, Error **errp) +{ + switch (event) { + case COLO_EVENT_CHECKPOINT: + break; + case COLO_EVENT_FAILOVER: + object_property_set_str(OBJECT(nf), "off", "status", errp); + break; + default: + break; + } +} + static void netfilter_class_init(ObjectClass *oc, void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); + NetFilterClass *nfc = NETFILTER_CLASS(oc); ucc->complete = netfilter_complete; + nfc->handle_event = default_handle_event; } static const TypeInfo netfilter_info = { diff --git a/net/hub.c b/net/hub.c index 78b671ed95..5795a678ed 100644 --- a/net/hub.c +++ b/net/hub.c @@ -20,6 +20,7 @@ #include "hub.h" #include "qemu/iov.h" #include "qemu/error-report.h" +#include "sysemu/qtest.h" /* * A hub broadcasts incoming packets to all its ports except the source port. @@ -346,7 +347,7 @@ void net_hub_check_clients(void) if (has_host_dev && !has_nic) { warn_report("hub %d with no nics", hub->id); } - if (has_nic && !has_host_dev) { + if (has_nic && !has_host_dev && !qtest_enabled()) { warn_report("hub %d is not connected to host network", hub->id); } } diff --git a/net/l2tpv3.c b/net/l2tpv3.c index 6745b78990..81db24dc8c 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -28,6 +28,7 @@ #include #include "net/net.h" #include "clients.h" +#include "qapi/error.h" #include "qemu-common.h" #include "qemu/error-report.h" #include "qemu/option.h" @@ -528,7 +529,6 @@ int net_init_l2tpv3(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp) { - /* FIXME error_setg(errp, ...) on failure */ const NetdevL2TPv3Options *l2tpv3; NetL2TPV3State *s; NetClientState *nc; @@ -555,7 +555,7 @@ int net_init_l2tpv3(const Netdev *netdev, } if ((l2tpv3->has_offset) && (l2tpv3->offset > 256)) { - error_report("l2tpv3_open : offset must be less than 256 bytes"); + error_setg(errp, "offset must be less than 256 bytes"); goto outerr; } @@ -563,6 +563,8 @@ int net_init_l2tpv3(const Netdev *netdev, if (l2tpv3->has_rxcookie && l2tpv3->has_txcookie) { s->cookie = true; } else { + error_setg(errp, + "require both 'rxcookie' and 'txcookie' or neither"); goto outerr; } } else { @@ -578,7 +580,7 @@ int net_init_l2tpv3(const Netdev *netdev, if (l2tpv3->has_udp && l2tpv3->udp) { s->udp = true; if (!(l2tpv3->has_srcport && l2tpv3->has_dstport)) { - error_report("l2tpv3_open : need both src and dst port for udp"); + error_setg(errp, "need both src and dst port for udp"); goto outerr; } else { srcport = l2tpv3->srcport; @@ -639,20 +641,19 @@ int net_init_l2tpv3(const Netdev *netdev, gairet = getaddrinfo(l2tpv3->src, srcport, &hints, &result); if ((gairet != 0) || (result == NULL)) { - error_report( - "l2tpv3_open : could not resolve src, errno = %s", - gai_strerror(gairet) - ); + error_setg(errp, "could not resolve src, errno = %s", + gai_strerror(gairet)); goto outerr; } fd = socket(result->ai_family, result->ai_socktype, result->ai_protocol); if (fd == -1) { fd = -errno; - error_report("l2tpv3_open : socket creation failed, errno = %d", -fd); + error_setg(errp, "socket creation failed, errno = %d", + -fd); goto outerr; } if (bind(fd, (struct sockaddr *) result->ai_addr, result->ai_addrlen)) { - error_report("l2tpv3_open : could not bind socket err=%i", errno); + error_setg(errp, "could not bind socket err=%i", errno); goto outerr; } if (result) { @@ -677,10 +678,8 @@ int net_init_l2tpv3(const Netdev *netdev, result = NULL; gairet = getaddrinfo(l2tpv3->dst, dstport, &hints, &result); if ((gairet != 0) || (result == NULL)) { - error_report( - "l2tpv3_open : could not resolve dst, error = %s", - gai_strerror(gairet) - ); + error_setg(errp, "could not resolve dst, error = %s", + gai_strerror(gairet)); goto outerr; } diff --git a/net/net.c b/net/net.c index 2a3133990c..f3a3c5444c 100644 --- a/net/net.c +++ b/net/net.c @@ -231,6 +231,11 @@ static void qemu_net_client_destructor(NetClientState *nc) { g_free(nc); } +static ssize_t qemu_deliver_packet_iov(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque); static void qemu_net_client_setup(NetClientState *nc, NetClientInfo *info, @@ -558,7 +563,7 @@ static ssize_t filter_receive_iov(NetClientState *nc, } } } else { - QTAILQ_FOREACH_REVERSE(nf, &nc->filters, NetFilterHead, next) { + QTAILQ_FOREACH_REVERSE(nf, &nc->filters, next) { ret = qemu_netfilter_receive(nf, direction, sender, flags, iov, iovcnt, sent_cb); if (ret) { @@ -663,9 +668,9 @@ ssize_t qemu_send_packet_async(NetClientState *sender, buf, size, sent_cb); } -void qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size) +ssize_t qemu_send_packet(NetClientState *nc, const uint8_t *buf, int size) { - qemu_send_packet_async(nc, buf, size, NULL); + return qemu_send_packet_async(nc, buf, size, NULL); } ssize_t qemu_send_packet_raw(NetClientState *nc, const uint8_t *buf, int size) @@ -705,15 +710,16 @@ static ssize_t nc_sendv_compat(NetClientState *nc, const struct iovec *iov, return ret; } -ssize_t qemu_deliver_packet_iov(NetClientState *sender, - unsigned flags, - const struct iovec *iov, - int iovcnt, - void *opaque) +static ssize_t qemu_deliver_packet_iov(NetClientState *sender, + unsigned flags, + const struct iovec *iov, + int iovcnt, + void *opaque) { NetClientState *nc = opaque; int ret; + if (nc->link_down) { return iov_size(iov, iovcnt); } @@ -740,10 +746,15 @@ ssize_t qemu_sendv_packet_async(NetClientState *sender, NetPacketSent *sent_cb) { NetQueue *queue; + size_t size = iov_size(iov, iovcnt); int ret; + if (size > NET_BUFSIZE) { + return size; + } + if (sender->link_down || !sender->peer) { - return iov_size(iov, iovcnt); + return size; } /* Let filters handle the packet first */ @@ -950,7 +961,7 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( [NET_CLIENT_DRIVER_BRIDGE] = net_init_bridge, #endif [NET_CLIENT_DRIVER_HUBPORT] = net_init_hubport, -#ifdef CONFIG_VHOST_NET_USED +#ifdef CONFIG_VHOST_NET_USER [NET_CLIENT_DRIVER_VHOST_USER] = net_init_vhost_user, #endif #ifdef CONFIG_L2TPV3 @@ -984,6 +995,10 @@ static int net_client_init1(const void *object, bool is_netdev, Error **errp) /* missing optional values have been initialized to "all bits zero" */ name = net->has_id ? net->id : net->name; + if (net->has_name) { + warn_report("The 'name' parameter is deprecated, use 'id' instead"); + } + /* Map the old options to the new flat type */ switch (opts->type) { case NET_LEGACY_OPTIONS_TYPE_NONE: @@ -1331,6 +1346,25 @@ void hmp_info_network(Monitor *mon, const QDict *qdict) } } +void colo_notify_filters_event(int event, Error **errp) +{ + NetClientState *nc; + NetFilterState *nf; + NetFilterClass *nfc = NULL; + Error *local_err = NULL; + + QTAILQ_FOREACH(nc, &net_clients, next) { + QTAILQ_FOREACH(nf, &nc->filters, next) { + nfc = NETFILTER_GET_CLASS(OBJECT(nf)); + nfc->handle_event(nf, event, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return; + } + } + } +} + void qmp_set_link(const char *name, bool up, Error **errp) { NetClientState *ncs[MAX_QUEUE_NUM]; diff --git a/net/netmap.c b/net/netmap.c index 2d11a8f4be..0cc8f545c5 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -154,65 +154,27 @@ static void netmap_writable(void *opaque) qemu_flush_queued_packets(&s->nc); } -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->tx; - uint32_t i; - uint32_t idx; - uint8_t *dst; - - if (unlikely(!ring)) { - /* Drop. */ - return size; - } - - if (unlikely(size > ring->nr_buf_size)) { - RD(5, "[netmap_receive] drop packet of size %d > %d\n", - (int)size, ring->nr_buf_size); - return size; - } - - if (nm_ring_empty(ring)) { - /* No available slots in the netmap TX ring. */ - netmap_write_poll(s, true); - return 0; - } - - i = ring->cur; - idx = ring->slot[i].buf_idx; - dst = (uint8_t *)NETMAP_BUF(ring, idx); - - ring->slot[i].len = size; - ring->slot[i].flags = 0; - pkt_copy(buf, dst, size); - ring->cur = ring->head = nm_ring_next(ring, i); - ioctl(s->nmd->fd, NIOCTXSYNC, NULL); - - return size; -} - 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->tx; + unsigned int tail = ring->tail; + ssize_t totlen = 0; uint32_t last; uint32_t idx; uint8_t *dst; int j; uint32_t i; - if (unlikely(!ring)) { - /* Drop the packet. */ - return iov_size(iov, iovcnt); - } - - last = i = ring->cur; + last = i = ring->head; if (nm_ring_space(ring) < iovcnt) { - /* Not enough netmap slots. */ + /* Not enough netmap slots. Tell the kernel that we have seen the new + * available slots (so that it notifies us again when it has more + * ones), but without publishing any new slots to be processed + * (e.g., we don't advance ring->head). */ + ring->cur = tail; netmap_write_poll(s, true); return 0; } @@ -222,14 +184,17 @@ static ssize_t netmap_receive_iov(NetClientState *nc, int offset = 0; int nm_frag_size; + totlen += iov_frag_size; + /* Split each iovec fragment over more netmap slots, if necessary. */ while (iov_frag_size) { nm_frag_size = MIN(iov_frag_size, ring->nr_buf_size); - if (unlikely(nm_ring_empty(ring))) { - /* We run out of netmap slots while splitting the + if (unlikely(i == tail)) { + /* We ran out of netmap slots while splitting the iovec fragments. */ + ring->cur = tail; netmap_write_poll(s, true); return 0; } @@ -251,12 +216,24 @@ static ssize_t netmap_receive_iov(NetClientState *nc, /* The last slot must not have NS_MOREFRAG set. */ ring->slot[last].flags &= ~NS_MOREFRAG; - /* Now update ring->cur and ring->head. */ - ring->cur = ring->head = i; + /* Now update ring->head and ring->cur to publish the new slots and + * the new wakeup point. */ + ring->head = ring->cur = i; ioctl(s->nmd->fd, NIOCTXSYNC, NULL); - return iov_size(iov, iovcnt); + return totlen; +} + +static ssize_t netmap_receive(NetClientState *nc, + const uint8_t *buf, size_t size) +{ + struct iovec iov; + + iov.iov_base = (void *)buf; + iov.iov_len = size; + + return netmap_receive_iov(nc, &iov, 1); } /* Complete a previous send (backend --> guest) and enable the @@ -272,39 +249,46 @@ static void netmap_send(void *opaque) { NetmapState *s = opaque; struct netmap_ring *ring = s->rx; + unsigned int tail = ring->tail; - /* Keep sending while there are available packets into the netmap + /* Keep sending while there are available slots in the netmap RX ring and the forwarding path towards the peer is open. */ - while (!nm_ring_empty(ring)) { - uint32_t i; + while (ring->head != tail) { + uint32_t i = ring->head; uint32_t idx; bool morefrag; int iovcnt = 0; int iovsize; + /* Get a (possibly multi-slot) packet. */ do { - i = ring->cur; idx = ring->slot[i].buf_idx; morefrag = (ring->slot[i].flags & NS_MOREFRAG); - s->iov[iovcnt].iov_base = (u_char *)NETMAP_BUF(ring, idx); + s->iov[iovcnt].iov_base = (void *)NETMAP_BUF(ring, idx); s->iov[iovcnt].iov_len = ring->slot[i].len; iovcnt++; + i = nm_ring_next(ring, i); + } while (i != tail && morefrag); - ring->cur = ring->head = nm_ring_next(ring, i); - } while (!nm_ring_empty(ring) && morefrag); + /* Advance ring->cur to tell the kernel that we have seen the slots. */ + ring->cur = i; - if (unlikely(nm_ring_empty(ring) && morefrag)) { - RD(5, "[netmap_send] ran out of slots, with a pending" - "incomplete packet\n"); + if (unlikely(morefrag)) { + /* This is a truncated packet, so we can stop without releasing the + * incomplete slots by updating ring->head. We will hopefully + * re-read the complete packet the next time we are called. */ + break; } iovsize = qemu_sendv_packet_async(&s->nc, s->iov, iovcnt, netmap_send_completed); + /* Release the slots to the kernel. */ + ring->head = i; + if (iovsize == 0) { /* The peer does not receive anymore. Packet is queued, stop - * reading from the backend until netmap_send_completed() - */ + * reading from the backend until netmap_send_completed(). */ netmap_read_poll(s, false); break; } diff --git a/net/queue.c b/net/queue.c index 9c32abdb8f..61276ca4be 100644 --- a/net/queue.c +++ b/net/queue.c @@ -55,7 +55,7 @@ struct NetQueue { uint32_t nq_count; NetQueueDeliverFunc *deliver; - QTAILQ_HEAD(packets, NetPacket) packets; + QTAILQ_HEAD(, NetPacket) packets; unsigned delivering : 1; }; diff --git a/net/slirp.c b/net/slirp.c index 1e14318b4d..95934fb36d 100644 --- a/net/slirp.c +++ b/net/slirp.c @@ -23,6 +23,7 @@ */ #include "qemu/osdep.h" +#include "qemu/log.h" #include "net/slirp.h" @@ -36,13 +37,15 @@ #include "monitor/monitor.h" #include "qemu/error-report.h" #include "qemu/sockets.h" -#include "slirp/libslirp.h" -#include "slirp/ip6.h" +#include #include "chardev/char-fe.h" #include "sysemu/sysemu.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" +#include "util.h" +#include "migration/register.h" +#include "migration/qemu-file-types.h" static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) { @@ -67,39 +70,40 @@ static int get_str_sep(char *buf, int buf_size, const char **pp, int sep) /* slirp network adapter */ #define SLIRP_CFG_HOSTFWD 1 -#define SLIRP_CFG_LEGACY 2 struct slirp_config_str { struct slirp_config_str *next; int flags; char str[1024]; - int legacy_format; +}; + +struct GuestFwd { + CharBackend hd; + struct in_addr server; + int port; + Slirp *slirp; }; typedef struct SlirpState { NetClientState nc; QTAILQ_ENTRY(SlirpState) entry; Slirp *slirp; + Notifier poll_notifier; Notifier exit_notifier; #ifndef _WIN32 gchar *smb_dir; #endif + GSList *fwd; } SlirpState; static struct slirp_config_str *slirp_configs; -const char *legacy_tftp_prefix; -const char *legacy_bootp_filename; -static QTAILQ_HEAD(slirp_stacks, SlirpState) slirp_stacks = +static QTAILQ_HEAD(, SlirpState) slirp_stacks = QTAILQ_HEAD_INITIALIZER(slirp_stacks); -static int slirp_hostfwd(SlirpState *s, const char *redir_str, - int legacy_format, Error **errp); -static int slirp_guestfwd(SlirpState *s, const char *config_str, - int legacy_format, Error **errp); +static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp); +static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp); #ifndef _WIN32 -static const char *legacy_smb_export; - static int slirp_smb(SlirpState *s, const char *exported_dir, struct in_addr vserver_addr, Error **errp); static void slirp_smb_cleanup(SlirpState *s); @@ -107,11 +111,12 @@ static void slirp_smb_cleanup(SlirpState *s); static inline void slirp_smb_cleanup(SlirpState *s) { } #endif -void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len) +static ssize_t net_slirp_send_packet(const void *pkt, size_t pkt_len, + void *opaque) { SlirpState *s = opaque; - qemu_send_packet(&s->nc, pkt, pkt_len); + return qemu_send_packet(&s->nc, pkt, pkt_len); } static ssize_t net_slirp_receive(NetClientState *nc, const uint8_t *buf, size_t size) @@ -129,10 +134,21 @@ static void slirp_smb_exit(Notifier *n, void *data) slirp_smb_cleanup(s); } +static void slirp_free_fwd(gpointer data) +{ + struct GuestFwd *fwd = data; + + qemu_chr_fe_deinit(&fwd->hd, true); + g_free(data); +} + static void net_slirp_cleanup(NetClientState *nc) { SlirpState *s = DO_UPCAST(SlirpState, nc, nc); + g_slist_free_full(s->fwd, slirp_free_fwd); + main_loop_poll_remove_notifier(&s->poll_notifier); + unregister_savevm(NULL, "slirp", s->slirp); slirp_cleanup(s->slirp); if (s->exit_notifier.notify) { qemu_remove_exit_notifier(&s->exit_notifier); @@ -148,6 +164,188 @@ static NetClientInfo net_slirp_info = { .cleanup = net_slirp_cleanup, }; +static void net_slirp_guest_error(const char *msg, void *opaque) +{ + qemu_log_mask(LOG_GUEST_ERROR, "%s", msg); +} + +static int64_t net_slirp_clock_get_ns(void *opaque) +{ + return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); +} + +static void *net_slirp_timer_new(SlirpTimerCb cb, + void *cb_opaque, void *opaque) +{ + return timer_new_full(NULL, QEMU_CLOCK_VIRTUAL, + SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, + cb, cb_opaque); +} + +static void net_slirp_timer_free(void *timer, void *opaque) +{ + timer_del(timer); + timer_free(timer); +} + +static void net_slirp_timer_mod(void *timer, int64_t expire_timer, + void *opaque) +{ + timer_mod(timer, expire_timer); +} + +static void net_slirp_register_poll_fd(int fd, void *opaque) +{ + qemu_fd_register(fd); +} + +static void net_slirp_unregister_poll_fd(int fd, void *opaque) +{ + /* no qemu_fd_unregister */ +} + +static void net_slirp_notify(void *opaque) +{ + qemu_notify_event(); +} + +static const SlirpCb slirp_cb = { + .send_packet = net_slirp_send_packet, + .guest_error = net_slirp_guest_error, + .clock_get_ns = net_slirp_clock_get_ns, + .timer_new = net_slirp_timer_new, + .timer_free = net_slirp_timer_free, + .timer_mod = net_slirp_timer_mod, + .register_poll_fd = net_slirp_register_poll_fd, + .unregister_poll_fd = net_slirp_unregister_poll_fd, + .notify = net_slirp_notify, +}; + +static int slirp_poll_to_gio(int events) +{ + int ret = 0; + + if (events & SLIRP_POLL_IN) { + ret |= G_IO_IN; + } + if (events & SLIRP_POLL_OUT) { + ret |= G_IO_OUT; + } + if (events & SLIRP_POLL_PRI) { + ret |= G_IO_PRI; + } + if (events & SLIRP_POLL_ERR) { + ret |= G_IO_ERR; + } + if (events & SLIRP_POLL_HUP) { + ret |= G_IO_HUP; + } + + return ret; +} + +static int net_slirp_add_poll(int fd, int events, void *opaque) +{ + GArray *pollfds = opaque; + GPollFD pfd = { + .fd = fd, + .events = slirp_poll_to_gio(events), + }; + int idx = pollfds->len; + g_array_append_val(pollfds, pfd); + return idx; +} + +static int slirp_gio_to_poll(int events) +{ + int ret = 0; + + if (events & G_IO_IN) { + ret |= SLIRP_POLL_IN; + } + if (events & G_IO_OUT) { + ret |= SLIRP_POLL_OUT; + } + if (events & G_IO_PRI) { + ret |= SLIRP_POLL_PRI; + } + if (events & G_IO_ERR) { + ret |= SLIRP_POLL_ERR; + } + if (events & G_IO_HUP) { + ret |= SLIRP_POLL_HUP; + } + + return ret; +} + +static int net_slirp_get_revents(int idx, void *opaque) +{ + GArray *pollfds = opaque; + + return slirp_gio_to_poll(g_array_index(pollfds, GPollFD, idx).revents); +} + +static void net_slirp_poll_notify(Notifier *notifier, void *data) +{ + MainLoopPoll *poll = data; + SlirpState *s = container_of(notifier, SlirpState, poll_notifier); + + switch (poll->state) { + case MAIN_LOOP_POLL_FILL: + slirp_pollfds_fill(s->slirp, &poll->timeout, + net_slirp_add_poll, poll->pollfds); + break; + case MAIN_LOOP_POLL_OK: + case MAIN_LOOP_POLL_ERR: + slirp_pollfds_poll(s->slirp, poll->state == MAIN_LOOP_POLL_ERR, + net_slirp_get_revents, poll->pollfds); + break; + default: + g_assert_not_reached(); + } +} + +static ssize_t +net_slirp_stream_read(void *buf, size_t size, void *opaque) +{ + QEMUFile *f = opaque; + + return qemu_get_buffer(f, buf, size); +} + +static ssize_t +net_slirp_stream_write(const void *buf, size_t size, void *opaque) +{ + QEMUFile *f = opaque; + + qemu_put_buffer(f, buf, size); + if (qemu_file_get_error(f)) { + return -1; + } + + return size; +} + +static int net_slirp_state_load(QEMUFile *f, void *opaque, int version_id) +{ + Slirp *slirp = opaque; + + return slirp_state_load(slirp, version_id, net_slirp_stream_read, f); +} + +static void net_slirp_state_save(QEMUFile *f, void *opaque) +{ + Slirp *slirp = opaque; + + slirp_state_save(slirp, net_slirp_stream_write, f); +} + +static SaveVMHandlers savevm_slirp_state = { + .save_state = net_slirp_state_save, + .load_state = net_slirp_state_load, +}; + static int net_slirp_init(NetClientState *peer, const char *model, const char *name, int restricted, bool ipv4, const char *vnetwork, const char *vhost, @@ -158,6 +356,7 @@ static int net_slirp_init(NetClientState *peer, const char *model, const char *vnameserver, const char *vnameserver6, const char *smb_export, const char *vsmbserver, const char **dnssearch, const char *vdomainname, + const char *tftp_server_name, Error **errp) { /* default settings according to historic slirp */ @@ -196,13 +395,6 @@ static int net_slirp_init(NetClientState *peer, const char *model, return -1; } - if (!tftp_export) { - tftp_export = legacy_tftp_prefix; - } - if (!bootfile) { - bootfile = legacy_bootp_filename; - } - if (vnetwork) { if (get_str_sep(buf, sizeof(buf), &vnetwork, '/') < 0) { if (!inet_aton(vnetwork, &net)) { @@ -293,17 +485,6 @@ 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 */ - error_setg(errp, "IPv6 prefix not supported"); - 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::"; } @@ -311,7 +492,6 @@ static int net_slirp_init(NetClientState *peer, const char *model, error_setg(errp, "Failed to parse IPv6 prefix"); return -1; } -#endif if (!vprefix6_len) { vprefix6_len = 64; @@ -323,10 +503,6 @@ static int net_slirp_init(NetClientState *peer, const char *model, } if (vhost6) { -#if defined(_WIN32) && (_WIN32_WINNT < 0x0600) - error_setg(errp, "IPv6 host not supported"); - return -1; -#else if (!inet_pton(AF_INET6, vhost6, &ip6_host)) { error_setg(errp, "Failed to parse IPv6 host"); return -1; @@ -335,17 +511,12 @@ static int net_slirp_init(NetClientState *peer, const char *model, error_setg(errp, "IPv6 Host doesn't belong to network"); return -1; } -#endif } else { ip6_host = ip6_prefix; ip6_host.s6_addr[15] |= 2; } if (vnameserver6) { -#if defined(_WIN32) && (_WIN32_WINNT < 0x0600) - error_setg(errp, "IPv6 DNS not supported"); - return -1; -#else if (!inet_pton(AF_INET6, vnameserver6, &ip6_dns)) { error_setg(errp, "Failed to parse IPv6 DNS"); return -1; @@ -354,7 +525,6 @@ static int net_slirp_init(NetClientState *peer, const char *model, error_setg(errp, "IPv6 DNS doesn't belong to network"); return -1; } -#endif } else { ip6_dns = ip6_prefix; ip6_dns.s6_addr[15] |= 3; @@ -365,6 +535,20 @@ static int net_slirp_init(NetClientState *peer, const char *model, return -1; } + if (vdomainname && strlen(vdomainname) > 255) { + error_setg(errp, "'domainname' parameter cannot exceed 255 bytes"); + return -1; + } + + if (vhostname && strlen(vhostname) > 255) { + error_setg(errp, "'vhostname' parameter cannot exceed 255 bytes"); + return -1; + } + + if (tftp_server_name && strlen(tftp_server_name) > 255) { + error_setg(errp, "'tftp-server-name' parameter cannot exceed 255 bytes"); + return -1; + } nc = qemu_new_net_client(&net_slirp_info, peer, model, name); @@ -376,27 +560,39 @@ static int net_slirp_init(NetClientState *peer, const char *model, 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, vdomainname, s); + vhostname, tftp_server_name, + tftp_export, bootfile, dhcp, + dns, ip6_dns, dnssearch, vdomainname, + &slirp_cb, s); QTAILQ_INSERT_TAIL(&slirp_stacks, s, entry); + /* + * Make sure the current bitstream version of slirp is 4, to avoid + * QEMU migration incompatibilities, if upstream slirp bumped the + * version. + * + * FIXME: use bitfields of features? teach libslirp to save with + * specific version? + */ + g_assert(slirp_state_version() == 4); + register_savevm_live(NULL, "slirp", 0, slirp_state_version(), + &savevm_slirp_state, s->slirp); + + s->poll_notifier.notify = net_slirp_poll_notify; + main_loop_poll_add_notifier(&s->poll_notifier); + for (config = slirp_configs; config; config = config->next) { if (config->flags & SLIRP_CFG_HOSTFWD) { - if (slirp_hostfwd(s, config->str, - config->flags & SLIRP_CFG_LEGACY, errp) < 0) { + if (slirp_hostfwd(s, config->str, errp) < 0) { goto error; } } else { - if (slirp_guestfwd(s, config->str, - config->flags & SLIRP_CFG_LEGACY, errp) < 0) { + if (slirp_guestfwd(s, config->str, errp) < 0) { goto error; } } } #ifndef _WIN32 - if (!smb_export) { - smb_export = legacy_smb_export; - } if (smb_export) { if (slirp_smb(s, smb_export, smbsrv, errp) < 0) { goto error; @@ -424,6 +620,8 @@ static SlirpState *slirp_lookup(Monitor *mon, const char *hub_id, monitor_printf(mon, "unrecognized (hub-id, stackname) pair\n"); return NULL; } + warn_report("Using 'hub-id' is deprecated, specify the netdev id " + "directly instead"); } else { nc = qemu_find_netdev(name); if (!nc) { @@ -506,8 +704,7 @@ void hmp_hostfwd_remove(Monitor *mon, const QDict *qdict) monitor_printf(mon, "invalid format\n"); } -static int slirp_hostfwd(SlirpState *s, const char *redir_str, - int legacy_format, Error **errp) +static int slirp_hostfwd(SlirpState *s, const char *redir_str, Error **errp) { struct in_addr host_addr = { .s_addr = INADDR_ANY }; struct in_addr guest_addr = { .s_addr = 0 }; @@ -532,18 +729,16 @@ static int slirp_hostfwd(SlirpState *s, const char *redir_str, goto fail_syntax; } - if (!legacy_format) { - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - fail_reason = "Missing : separator"; - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { - fail_reason = "Bad host address"; - goto fail_syntax; - } + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + fail_reason = "Missing : separator"; + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &host_addr)) { + fail_reason = "Bad host address"; + goto fail_syntax; } - if (get_str_sep(buf, sizeof(buf), &p, legacy_format ? ':' : '-') < 0) { + if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { fail_reason = "Bad host port separator"; goto fail_syntax; } @@ -602,35 +797,13 @@ void hmp_hostfwd_add(Monitor *mon, const QDict *qdict) } if (s) { Error *err = NULL; - if (slirp_hostfwd(s, redir_str, 0, &err) < 0) { + if (slirp_hostfwd(s, redir_str, &err) < 0) { error_report_err(err); } } } -int net_slirp_redir(const char *redir_str) -{ - struct slirp_config_str *config; - Error *err = NULL; - int res; - - if (QTAILQ_EMPTY(&slirp_stacks)) { - config = g_malloc(sizeof(*config)); - pstrcpy(config->str, sizeof(config->str), redir_str); - config->flags = SLIRP_CFG_HOSTFWD | SLIRP_CFG_LEGACY; - config->next = slirp_configs; - slirp_configs = config; - return 0; - } - - res = slirp_hostfwd(QTAILQ_FIRST(&slirp_stacks), redir_str, 1, &err); - if (res < 0) { - error_report_err(err); - } - return res; -} - #ifndef _WIN32 /* automatic user mode samba server configuration */ @@ -735,8 +908,8 @@ static int slirp_smb(SlirpState* s, const char *exported_dir, CONFIG_SMBD_COMMAND, s->smb_dir, smb_conf); g_free(smb_conf); - if (slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 139) < 0 || - slirp_add_exec(s->slirp, 0, smb_cmdline, &vserver_addr, 445) < 0) { + if (slirp_add_exec(s->slirp, smb_cmdline, &vserver_addr, 139) < 0 || + slirp_add_exec(s->slirp, smb_cmdline, &vserver_addr, 445) < 0) { slirp_smb_cleanup(s); g_free(smb_cmdline); error_setg(errp, "Conflicting/invalid smbserver address"); @@ -746,37 +919,8 @@ static int slirp_smb(SlirpState* s, const char *exported_dir, return 0; } -/* automatic user mode samba server configuration (legacy interface) */ -int net_slirp_smb(const char *exported_dir) -{ - struct in_addr vserver_addr = { .s_addr = 0 }; - - if (legacy_smb_export) { - fprintf(stderr, "-smb given twice\n"); - return -1; - } - legacy_smb_export = exported_dir; - if (!QTAILQ_EMPTY(&slirp_stacks)) { - Error *err = NULL; - int res = slirp_smb(QTAILQ_FIRST(&slirp_stacks), exported_dir, - vserver_addr, &err); - if (res < 0) { - error_report_err(err); - } - return res; - } - return 0; -} - #endif /* !defined(_WIN32) */ -struct GuestFwd { - CharBackend hd; - struct in_addr server; - int port; - Slirp *slirp; -}; - static int guestfwd_can_read(void *opaque) { struct GuestFwd *fwd = opaque; @@ -789,9 +933,14 @@ static void guestfwd_read(void *opaque, const uint8_t *buf, int size) slirp_socket_recv(fwd->slirp, fwd->server, fwd->port, buf, size); } -static int slirp_guestfwd(SlirpState *s, const char *config_str, - int legacy_format, Error **errp) +static ssize_t guestfwd_write(const void *buf, size_t len, void *chr) { + return qemu_chr_fe_write_all(chr, buf, len); +} + +static int slirp_guestfwd(SlirpState *s, const char *config_str, Error **errp) +{ + /* TODO: IPv6 */ struct in_addr server = { .s_addr = 0 }; struct GuestFwd *fwd; const char *p; @@ -800,26 +949,20 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, int port; p = config_str; - if (legacy_format) { - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - } else { - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (strcmp(buf, "tcp") && buf[0] != '\0') { - goto fail_syntax; - } - if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { - goto fail_syntax; - } - if (buf[0] != '\0' && !inet_aton(buf, &server)) { - goto fail_syntax; - } - if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { - goto fail_syntax; - } + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (strcmp(buf, "tcp") && buf[0] != '\0') { + goto fail_syntax; + } + if (get_str_sep(buf, sizeof(buf), &p, ':') < 0) { + goto fail_syntax; + } + if (buf[0] != '\0' && !inet_aton(buf, &server)) { + goto fail_syntax; + } + if (get_str_sep(buf, sizeof(buf), &p, '-') < 0) { + goto fail_syntax; } port = strtol(buf, &end, 10); if (*end != '\0' || port < 1 || port > 65535) { @@ -828,15 +971,19 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, snprintf(buf, sizeof(buf), "guestfwd.tcp.%d", port); - if ((strlen(p) > 4) && !strncmp(p, "cmd:", 4)) { - if (slirp_add_exec(s->slirp, 0, &p[4], &server, port) < 0) { + if (g_str_has_prefix(p, "cmd:")) { + if (slirp_add_exec(s->slirp, &p[4], &server, port) < 0) { error_setg(errp, "Conflicting/invalid host:port in guest " "forwarding rule '%s'", config_str); return -1; } } else { Error *err = NULL; - Chardev *chr = qemu_chr_new(buf, p); + /* + * FIXME: sure we want to support implicit + * muxed monitors here? + */ + Chardev *chr = qemu_chr_new_mux_mon(buf, p, NULL); if (!chr) { error_setg(errp, "Could not open guest forwarding device '%s'", @@ -848,13 +995,16 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, qemu_chr_fe_init(&fwd->hd, chr, &err); if (err) { error_propagate(errp, err); + object_unparent(OBJECT(chr)); g_free(fwd); return -1; } - if (slirp_add_exec(s->slirp, 3, &fwd->hd, &server, port) < 0) { + if (slirp_add_guestfwd(s->slirp, guestfwd_write, &fwd->hd, + &server, port) < 0) { error_setg(errp, "Conflicting/invalid host:port in guest " "forwarding rule '%s'", config_str); + qemu_chr_fe_deinit(&fwd->hd, true); g_free(fwd); return -1; } @@ -864,6 +1014,7 @@ static int slirp_guestfwd(SlirpState *s, const char *config_str, qemu_chr_fe_set_handlers(&fwd->hd, guestfwd_can_read, guestfwd_read, NULL, NULL, fwd, NULL, true); + s->fwd = g_slist_append(s->fwd, fwd); } return 0; @@ -879,10 +1030,11 @@ void hmp_info_usernet(Monitor *mon, const QDict *qdict) QTAILQ_FOREACH(s, &slirp_stacks, entry) { int id; bool got_hub_id = net_hub_id_for_client(&s->nc, &id) == 0; - monitor_printf(mon, "Hub %d (%s):\n", + char *info = slirp_connection_info(s->slirp); + monitor_printf(mon, "Hub %d (%s):\n%s", got_hub_id ? id : -1, - s->nc.name); - slirp_connection_info(s->slirp, mon); + s->nc.name, info); + g_free(info); } } @@ -966,7 +1118,8 @@ int net_init_slirp(const Netdev *netdev, const char *name, user->ipv6_host, user->hostname, user->tftp, user->bootfile, user->dhcpstart, user->dns, user->ipv6_dns, user->smb, - user->smbserver, dnssearch, user->domainname, errp); + user->smbserver, dnssearch, user->domainname, + user->tftp_server_name, errp); while (slirp_configs) { config = slirp_configs; diff --git a/net/socket.c b/net/socket.c index 6917fbcbf5..c92354049b 100644 --- a/net/socket.c +++ b/net/socket.c @@ -119,9 +119,13 @@ static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, ssize_t ret; do { - ret = qemu_sendto(s->fd, buf, size, 0, - (struct sockaddr *)&s->dgram_dst, - sizeof(s->dgram_dst)); + if (s->dgram_dst.sin_family != AF_UNIX) { + ret = qemu_sendto(s->fd, buf, size, 0, + (struct sockaddr *)&s->dgram_dst, + sizeof(s->dgram_dst)); + } else { + ret = send(s->fd, buf, size, 0); + } } while (ret == -1 && errno == EINTR); if (ret == -1 && errno == EAGAIN) { @@ -336,6 +340,15 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, int newfd; NetClientState *nc; NetSocketState *s; + SocketAddress *sa; + SocketAddressType sa_type; + + sa = socket_local_address(fd, errp); + if (!sa) { + return NULL; + } + sa_type = sa->type; + qapi_free_SocketAddress(sa); /* fd passed: multicast: "learn" dgram_dst address from bound address and save it * Because this may be "shared" socket from a "master" process, datagrams would be recv() @@ -379,8 +392,12 @@ static NetSocketState *net_socket_fd_init_dgram(NetClientState *peer, "socket: fd=%d (cloned mcast=%s:%d)", fd, inet_ntoa(saddr.sin_addr), ntohs(saddr.sin_port)); } else { + if (sa_type == SOCKET_ADDRESS_TYPE_UNIX) { + s->dgram_dst.sin_family = AF_UNIX; + } + snprintf(nc->info_str, sizeof(nc->info_str), - "socket: fd=%d", fd); + "socket: fd=%d %s", fd, SocketAddressType_str(sa_type)); } return s; @@ -453,8 +470,8 @@ static NetSocketState *net_socket_fd_init(NetClientState *peer, case SOCK_STREAM: return net_socket_fd_init_stream(peer, model, name, fd, is_connected); default: - error_report("socket type=%d for fd=%d must be either" - " SOCK_DGRAM or SOCK_STREAM", so_type, fd); + error_setg(errp, "socket type=%d for fd=%d must be either" + " SOCK_DGRAM or SOCK_STREAM", so_type, fd); closesocket(fd); } return NULL; diff --git a/net/tap.c b/net/tap.c index cc8525f154..e8aadd8d4b 100644 --- a/net/tap.c +++ b/net/tap.c @@ -592,7 +592,7 @@ int net_init_bridge(const Netdev *netdev, const char *name, return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(fd); vnet_hdr = tap_probe_vnet_hdr(fd); s = net_tap_fd_init(peer, "bridge", name, fd, vnet_hdr); @@ -707,7 +707,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } return; } - fcntl(vhostfd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(vhostfd); } options.opaque = (void *)(uintptr_t)vhostfd; @@ -791,7 +791,7 @@ int net_init_tap(const Netdev *netdev, const char *name, return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(fd); vnet_hdr = tap_probe_vnet_hdr(fd); @@ -839,7 +839,7 @@ int net_init_tap(const Netdev *netdev, const char *name, goto free_fail; } - fcntl(fd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(fd); if (i == 0) { vnet_hdr = tap_probe_vnet_hdr(fd); @@ -887,7 +887,7 @@ free_fail: return -1; } - fcntl(fd, F_SETFL, O_NONBLOCK); + qemu_set_nonblock(fd); vnet_hdr = tap_probe_vnet_hdr(fd); net_init_tap_one(tap, peer, "bridge", name, ifname, diff --git a/net/trace-events b/net/trace-events index 7b594cfdd2..a7937f3f3a 100644 --- a/net/trace-events +++ b/net/trace-events @@ -1,12 +1,15 @@ # See docs/devel/tracing.txt for syntax documentation. -# net/vhost-user.c +# announce.c +qemu_announce_self_iter(const char *mac) "%s" + +# vhost-user.c vhost_user_event(const char *chr, int event) "chr: %s got event: %d" -# net/colo.c +# colo.c colo_proxy_main(const char *chr) ": %s" -# net/colo-compare.c +# colo-compare.c colo_compare_main(const char *chr) ": %s" colo_compare_udp_miscompare(const char *sta, int size) ": %s = %d" colo_compare_icmp_miscompare(const char *sta, int size) ": %s = %d" @@ -15,7 +18,7 @@ colo_old_packet_check_found(int64_t old_time) "%" PRId64 colo_compare_miscompare(void) "" colo_compare_tcp_info(const char *pkt, uint32_t seq, uint32_t ack, int hdlen, int pdlen, int offset, int flags) "%s: seq/ack= %u/%u hdlen= %d pdlen= %d offset= %d flags=%d\n" -# net/filter-rewriter.c +# filter-rewriter.c colo_filter_rewriter_debug(void) "" colo_filter_rewriter_pkt_info(const char *func, const char *src, const char *dst, uint32_t seq, uint32_t ack, uint32_t flag) "%s: src/dst: %s/%s p: seq/ack=%u/%u flags=0x%x\n" colo_filter_rewriter_conn_offset(uint32_t offset) ": offset=%u\n" diff --git a/net/util.h b/net/util.h index 60b73d372d..358185fd50 100644 --- a/net/util.h +++ b/net/util.h @@ -26,6 +26,61 @@ #define QEMU_NET_UTIL_H +/* + * Structure of an internet header, naked of options. + */ +struct ip { +#ifdef HOST_WORDS_BIGENDIAN + uint8_t ip_v:4, /* version */ + ip_hl:4; /* header length */ +#else + uint8_t ip_hl:4, /* header length */ + ip_v:4; /* version */ +#endif + 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 */ +#define IP_DF 0x4000 /* don't fragment flag */ +#define IP_MF 0x2000 /* more fragments flag */ +#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */ + uint8_t ip_ttl; /* time to live */ + uint8_t ip_p; /* protocol */ + uint16_t ip_sum; /* checksum */ + struct in_addr ip_src, ip_dst; /* source and dest address */ +} QEMU_PACKED; + +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)); +} + +#define TCPS_CLOSED 0 /* closed */ +#define TCPS_LISTEN 1 /* listening for connection */ +#define TCPS_SYN_SENT 2 /* active, have sent syn */ +#define TCPS_SYN_RECEIVED 3 /* have send and received syn */ +/* states < TCPS_ESTABLISHED are those where connections not established */ +#define TCPS_ESTABLISHED 4 /* established */ +#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */ +/* states > TCPS_CLOSE_WAIT are those where user has closed */ +#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */ +#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */ +#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */ +/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */ +#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */ +#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */ + int net_parse_macaddr(uint8_t *macaddr, const char *p); #endif /* QEMU_NET_UTIL_H */ diff --git a/net/vhost-user-stub.c b/net/vhost-user-stub.c new file mode 100644 index 0000000000..52ab4e13f1 --- /dev/null +++ b/net/vhost-user-stub.c @@ -0,0 +1,23 @@ +/* + * vhost-user-stub.c + * + * Copyright (c) 2018 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 "clients.h" +#include "net/vhost_net.h" +#include "net/vhost-user.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + +int net_init_vhost_user(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); + return -1; +} diff --git a/net/vhost-user.c b/net/vhost-user.c index a39f9c9974..5a26a24708 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -172,6 +172,17 @@ static void net_vhost_user_cleanup(NetClientState *nc) qemu_purge_queued_packets(nc); } +static int vhost_user_set_vnet_endianness(NetClientState *nc, + bool enable) +{ + /* Nothing to do. If the server supports + * VHOST_USER_PROTOCOL_F_CROSS_ENDIAN, it will get the + * vnet header endianness from there. If it doesn't, negotiation + * fails. + */ + return 0; +} + static bool vhost_user_has_vnet_hdr(NetClientState *nc) { assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); @@ -193,6 +204,8 @@ static NetClientInfo net_vhost_user_info = { .cleanup = net_vhost_user_cleanup, .has_vnet_hdr = vhost_user_has_vnet_hdr, .has_ufo = vhost_user_has_ufo, + .set_vnet_be = vhost_user_set_vnet_endianness, + .set_vnet_le = vhost_user_set_vnet_endianness, }; static gboolean net_vhost_user_watch(GIOChannel *chan, GIOCondition cond, @@ -291,19 +304,14 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, { Error *err = NULL; NetClientState *nc, *nc0 = NULL; - VhostUserState *user = NULL; NetVhostUserState *s = NULL; + VhostUserState *user; int i; assert(name); assert(queues > 0); - user = vhost_user_init(); - if (!user) { - error_report("failed to init vhost_user"); - goto err; - } - + user = g_new0(struct VhostUserState, 1); for (i = 0; i < queues; i++) { nc = qemu_new_net_client(&net_vhost_user_info, peer, device, name); snprintf(nc->info_str, sizeof(nc->info_str), "vhost-user%d to %s", @@ -312,11 +320,11 @@ static int net_vhost_user_init(NetClientState *peer, const char *device, if (!nc0) { nc0 = nc; s = DO_UPCAST(NetVhostUserState, nc, nc); - if (!qemu_chr_fe_init(&s->chr, chr, &err)) { + if (!qemu_chr_fe_init(&s->chr, chr, &err) || + !vhost_user_init(user, &s->chr, &err)) { error_report_err(err); goto err; } - user->chr = &s->chr; } s = DO_UPCAST(NetVhostUserState, nc, nc); s->vhost_user = user; diff --git a/numa.c b/numa.c index 5f6367b989..3875e1efda 100644 --- a/numa.c +++ b/numa.c @@ -60,6 +60,7 @@ NodeInfo numa_info[MAX_NODES]; static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, Error **errp) { + Error *err = NULL; uint16_t nodenr; uint16List *cpus = NULL; MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -82,8 +83,8 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, } if (!mc->cpu_index_to_instance_props || !mc->get_default_cpu_node_id) { - error_report("NUMA is not supported by this machine-type"); - exit(1); + error_setg(errp, "NUMA is not supported by this machine-type"); + return; } for (cpus = node->cpus; cpus; cpus = cpus->next) { CpuInstanceProperties props; @@ -97,7 +98,11 @@ static void parse_numa_node(MachineState *ms, NumaNodeOptions *node, props = mc->cpu_index_to_instance_props(ms, cpus->value); props.node_id = nodenr; props.has_node_id = true; - machine_set_cpu_numa_node(ms, &props, &error_fatal); + machine_set_cpu_numa_node(ms, &props, &err); + if (err) { + error_propagate(errp, err); + return; + } } if (node->has_mem && node->has_memdev) { @@ -210,7 +215,7 @@ end: error_propagate(errp, err); } -int parse_numa(void *opaque, QemuOpts *opts, Error **errp) +static int parse_numa(void *opaque, QemuOpts *opts, Error **errp) { NumaOptions *object = NULL; MachineState *ms = MACHINE(opaque); @@ -234,7 +239,7 @@ int parse_numa(void *opaque, QemuOpts *opts, Error **errp) end: qapi_free_NumaOptions(object); if (err) { - error_report_err(err); + error_propagate(errp, err); return -1; } @@ -367,7 +372,7 @@ void numa_complete_configuration(MachineState *ms) if (ms->ram_slots > 0 && nb_numa_nodes == 0 && mc->auto_enable_numa_with_memhp) { NumaNodeOptions node = { }; - parse_numa_node(ms, &node, NULL); + parse_numa_node(ms, &node, &error_abort); } assert(max_numa_nodeid <= MAX_NODES); @@ -439,9 +444,7 @@ void numa_complete_configuration(MachineState *ms) void parse_numa_opts(MachineState *ms) { - if (qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("numa"), parse_numa, ms, &error_fatal); } void qmp_set_numa_node(NumaOptions *cmd, Error **errp) @@ -479,7 +482,7 @@ static void allocate_system_memory_nonnuma(MemoryRegion *mr, Object *owner, if (mem_path) { #ifdef __linux__ Error *err = NULL; - memory_region_init_ram_from_file(mr, owner, name, ram_size, 0, false, + memory_region_init_ram_from_file(mr, owner, name, ram_size, 0, 0, mem_path, &err); if (err) { error_report_err(err); @@ -530,6 +533,7 @@ void memory_region_allocate_system_memory(MemoryRegion *mr, Object *owner, error_report("memory backend %s is used multiple times. Each " "-numa option must use a different memdev value.", path); + g_free(path); exit(1); } diff --git a/os-posix.c b/os-posix.c index 9ce6f74513..4bd80e44e6 100644 --- a/os-posix.c +++ b/os-posix.c @@ -168,14 +168,6 @@ static bool os_parse_runas_uid_gid(const char *optarg) int 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; -#endif case QEMU_OPTION_runas: user_pwd = getpwnam(optarg); if (user_pwd) { @@ -352,30 +344,6 @@ void os_set_line_buffering(void) setvbuf(stdout, NULL, _IOLBF, 0); } -int qemu_create_pidfile(const char *filename) -{ - char buffer[128]; - int len; - int fd; - - fd = qemu_open(filename, O_RDWR | O_CREAT, 0600); - if (fd == -1) { - return -1; - } - if (lockf(fd, F_TLOCK, 0) == -1) { - close(fd); - return -1; - } - len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", getpid()); - if (write(fd, buffer, len) != len) { - close(fd); - return -1; - } - - /* keep pidfile open & locked forever */ - return 0; -} - bool is_daemonized(void) { return daemonize; diff --git a/os-win32.c b/os-win32.c index 0674f94b57..0e0d7f50f3 100644 --- a/os-win32.c +++ b/os-win32.c @@ -97,28 +97,3 @@ int os_parse_cmd_args(int index, const char *optarg) { return -1; } - -int qemu_create_pidfile(const char *filename) -{ - char buffer[128]; - int len; - HANDLE file; - OVERLAPPED overlap; - BOOL ret; - memset(&overlap, 0, sizeof(overlap)); - - file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, - OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); - - if (file == INVALID_HANDLE_VALUE) { - return -1; - } - len = snprintf(buffer, sizeof(buffer), "%d\n", getpid()); - ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len, - NULL, &overlap); - CloseHandle(file); - if (ret == 0) { - return -1; - } - return 0; -} diff --git a/pc-bios/README b/pc-bios/README index 99e15a737b..d421cb3f1f 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -5,7 +5,7 @@ project (http://www.nongnu.org/vgabios/). - The PowerPC Open Hack'Ware Open Firmware Compatible BIOS is - available at http://repo.or.cz/w/openhackware.git. + available at https://repo.or.cz/openhackware.git. - OpenBIOS (http://www.openbios.org/) is a free (GPL v2) portable firmware implementation. The goal is to implement a 100% IEEE @@ -17,13 +17,13 @@ - 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-20180621. + built from git tag qemu-slof-20190114. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as if a video card were attached. The master sources reside in a subversion repository at http://sgabios.googlecode.com/svn/trunk. A git mirror is - available at git://git.qemu.org/sgabios.git. + available at https://git.qemu.org/git/sgabios.git. - The PXE roms come from the iPXE project. Built with BANNER_TIME 0. Sources available at http://ipxe.org. Vendor:Device ID -> ROM mapping: @@ -36,11 +36,11 @@ 1af4:1000 -> pxe-virtio.rom - The sources for the Alpha palcode image is available from: - git://github.com/rth7680/qemu-palcode.git + https://github.com/rth7680/qemu-palcode.git - The u-boot binary for e500 comes from the upstream denx u-boot project where it was compiled using the qemu-ppce500 target. - A git mirror is available at: git://git.qemu.org/u-boot.git + A git mirror is available at: https://git.qemu.org/git/u-boot.git The hash used to compile the current version is: 2072e72 - Skiboot (https://github.com/open-power/skiboot/) is an OPAL diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 6ffa6ec524..04671f160a 100644 Binary files a/pc-bios/bios-256k.bin and b/pc-bios/bios-256k.bin differ diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index afa450c4a0..0159c9b5e9 100644 Binary files a/pc-bios/bios.bin and b/pc-bios/bios.bin differ diff --git a/pc-bios/efi-e1000.rom b/pc-bios/efi-e1000.rom index 4da9de33da..6f088d41dd 100644 Binary files a/pc-bios/efi-e1000.rom and b/pc-bios/efi-e1000.rom differ diff --git a/pc-bios/efi-e1000e.rom b/pc-bios/efi-e1000e.rom index c2474a8fab..f536bdbd45 100644 Binary files a/pc-bios/efi-e1000e.rom and b/pc-bios/efi-e1000e.rom differ diff --git a/pc-bios/efi-eepro100.rom b/pc-bios/efi-eepro100.rom index 7950faf7cd..64d8891485 100644 Binary files a/pc-bios/efi-eepro100.rom and b/pc-bios/efi-eepro100.rom differ diff --git a/pc-bios/efi-ne2k_pci.rom b/pc-bios/efi-ne2k_pci.rom index 30edb1392a..02ad0cb505 100644 Binary files a/pc-bios/efi-ne2k_pci.rom and b/pc-bios/efi-ne2k_pci.rom differ diff --git a/pc-bios/efi-pcnet.rom b/pc-bios/efi-pcnet.rom index 23057c5724..88d25fa625 100644 Binary files a/pc-bios/efi-pcnet.rom and b/pc-bios/efi-pcnet.rom differ diff --git a/pc-bios/efi-rtl8139.rom b/pc-bios/efi-rtl8139.rom index beb9301839..53f125e3bb 100644 Binary files a/pc-bios/efi-rtl8139.rom and b/pc-bios/efi-rtl8139.rom differ diff --git a/pc-bios/efi-virtio.rom b/pc-bios/efi-virtio.rom index f4de5957ec..a87321e928 100644 Binary files a/pc-bios/efi-virtio.rom and b/pc-bios/efi-virtio.rom differ diff --git a/pc-bios/efi-vmxnet3.rom b/pc-bios/efi-vmxnet3.rom index 7501477ea6..d017cafe22 100644 Binary files a/pc-bios/efi-vmxnet3.rom and b/pc-bios/efi-vmxnet3.rom differ diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img old mode 100755 new mode 100644 index 4ec0dbfc4a..c79e1e923c Binary files a/pc-bios/hppa-firmware.img and b/pc-bios/hppa-firmware.img differ diff --git a/pc-bios/keymaps/Makefile b/pc-bios/keymaps/Makefile index f0e44fd110..76217b0689 100644 --- a/pc-bios/keymaps/Makefile +++ b/pc-bios/keymaps/Makefile @@ -9,7 +9,7 @@ ar : MAP_FLAGS := -l ar bepo : MAP_FLAGS := -l fr -v dvorak cz : MAP_FLAGS := -l cz da : MAP_FLAGS := -l dk -de : MAP_FLAGS := -l de +de : MAP_FLAGS := -l de -v nodeadkeys de-ch : MAP_FLAGS := -l ch en-us : MAP_FLAGS := -l us en-gb : MAP_FLAGS := -l gb @@ -17,7 +17,7 @@ es : MAP_FLAGS := -l es et : MAP_FLAGS := -l et fi : MAP_FLAGS := -l fi fo : MAP_FLAGS := -l fo -fr : MAP_FLAGS := -l fr +fr : MAP_FLAGS := -l fr -v nodeadkeys fr-be : MAP_FLAGS := -l be fr-ca : MAP_FLAGS := -l ca -v fr fr-ch : MAP_FLAGS := -l ch -v fr diff --git a/pc-bios/keymaps/ar b/pc-bios/keymaps/ar index a763c9a027..f62b297c54 100644 --- a/pc-bios/keymaps/ar +++ b/pc-bios/keymaps/ar @@ -36,50 +36,65 @@ Escape 0x01 # evdev 2 (0x2), QKeyCode "1", number 0x2 1 0x02 exclam 0x02 shift +Arabic_1 0x02 altgr # evdev 3 (0x3), QKeyCode "2", number 0x3 2 0x03 at 0x03 shift +Arabic_2 0x03 altgr # evdev 4 (0x4), QKeyCode "3", number 0x4 3 0x04 numbersign 0x04 shift +Arabic_3 0x04 altgr # evdev 5 (0x5), QKeyCode "4", number 0x5 4 0x05 dollar 0x05 shift +Arabic_4 0x05 altgr # evdev 6 (0x6), QKeyCode "5", number 0x6 5 0x06 percent 0x06 shift +Arabic_5 0x06 altgr +U2030 0x06 shift altgr # evdev 7 (0x7), QKeyCode "6", number 0x7 6 0x07 asciicircum 0x07 shift +Arabic_6 0x07 altgr # evdev 8 (0x8), QKeyCode "7", number 0x8 7 0x08 ampersand 0x08 shift +Arabic_7 0x08 altgr # evdev 9 (0x9), QKeyCode "8", number 0x9 8 0x09 asterisk 0x09 shift +Arabic_8 0x09 altgr # evdev 10 (0xa), QKeyCode "9", number 0xa 9 0x0a parenright 0x0a shift +Arabic_9 0x0a altgr # evdev 11 (0xb), QKeyCode "0", number 0xb 0 0x0b parenleft 0x0b shift +Arabic_0 0x0b altgr # evdev 12 (0xc), QKeyCode "minus", number 0xc minus 0x0c underscore 0x0c shift +endash 0x0c altgr +U2011 0x0c shift altgr # evdev 13 (0xd), QKeyCode "equal", number 0xd equal 0x0d plus 0x0d shift +notequal 0x0d altgr +approxeq 0x0d shift altgr # evdev 14 (0xe), QKeyCode "backspace", number 0xe BackSpace 0x0e @@ -91,18 +106,22 @@ ISO_Left_Tab 0x0f shift # evdev 16 (0x10), QKeyCode "q", number 0x10 Arabic_dad 0x10 Arabic_fatha 0x10 shift +U2066 0x10 shift altgr # evdev 17 (0x11), QKeyCode "w", number 0x11 Arabic_sad 0x11 Arabic_fathatan 0x11 shift +U2067 0x11 shift altgr # evdev 18 (0x12), QKeyCode "e", number 0x12 Arabic_theh 0x12 Arabic_damma 0x12 shift +U2068 0x12 shift altgr # evdev 19 (0x13), QKeyCode "r", number 0x13 Arabic_qaf 0x13 Arabic_dammatan 0x13 shift +U2069 0x13 shift altgr # evdev 20 (0x14), QKeyCode "t", number 0x14 Arabic_feh 0x14 @@ -112,14 +131,17 @@ Arabic_veh 0x14 altgr # evdev 21 (0x15), QKeyCode "y", number 0x15 Arabic_ghain 0x15 Arabic_hamzaunderalef 0x15 shift +U202A 0x15 shift altgr # evdev 22 (0x16), QKeyCode "u", number 0x16 Arabic_ain 0x16 grave 0x16 shift +U202B 0x16 shift altgr # evdev 23 (0x17), QKeyCode "i", number 0x17 Arabic_ha 0x17 division 0x17 shift +U202C 0x17 shift altgr # evdev 24 (0x18), QKeyCode "o", number 0x18 Arabic_khah 0x18 @@ -128,15 +150,18 @@ multiply 0x18 shift # evdev 25 (0x19), QKeyCode "p", number 0x19 Arabic_hah 0x19 Arabic_semicolon 0x19 shift +U200E 0x19 shift altgr # evdev 26 (0x1a), QKeyCode "bracket_left", number 0x1a Arabic_jeem 0x1a less 0x1a shift Arabic_tcheh 0x1a altgr +U200F 0x1a shift altgr # evdev 27 (0x1b), QKeyCode "bracket_right", number 0x1b Arabic_dal 0x1b greater 0x1b shift +U061C 0x1b shift altgr # evdev 28 (0x1c), QKeyCode "ret", number 0x1c Return 0x1c @@ -177,6 +202,7 @@ Arabic_tatweel 0x24 shift # evdev 37 (0x25), QKeyCode "k", number 0x25 Arabic_noon 0x25 Arabic_comma 0x25 shift +U066B 0x25 altgr # evdev 38 (0x26), QKeyCode "l", number 0x26 Arabic_meem 0x26 @@ -190,27 +216,35 @@ Arabic_gaf 0x27 altgr # evdev 40 (0x28), QKeyCode "apostrophe", number 0x28 Arabic_tah 0x28 quotedbl 0x28 shift +U27E9 0x28 altgr +U200D 0x28 shift altgr # evdev 41 (0x29), QKeyCode "grave_accent", number 0x29 Arabic_thal 0x29 Arabic_shadda 0x29 shift +Arabic_percent 0x29 altgr +U0609 0x29 shift altgr # evdev 42 (0x2a), QKeyCode "shift", number 0x2a Shift_L 0x2a # evdev 43 (0x2b), QKeyCode "backslash", number 0x2b backslash 0x2b -bar 0x2b shift +ellipsis 0x2b shift +U27E8 0x2b altgr +U202F 0x2b shift altgr # evdev 44 (0x2c), QKeyCode "z", number 0x2c Arabic_hamzaonyeh 0x2c asciitilde 0x2c shift guillemotright 0x2c altgr +U203A 0x2c shift altgr # evdev 45 (0x2d), QKeyCode "x", number 0x2d Arabic_hamza 0x2d Arabic_sukun 0x2d shift guillemotleft 0x2d altgr +U2039 0x2d shift altgr # evdev 46 (0x2e), QKeyCode "c", number 0x2e Arabic_hamzaonwaw 0x2e @@ -236,14 +270,18 @@ apostrophe 0x32 shift # evdev 51 (0x33), QKeyCode "comma", number 0x33 Arabic_waw 0x33 comma 0x33 shift +U066C 0x33 altgr # evdev 52 (0x34), QKeyCode "dot", number 0x34 Arabic_zain 0x34 period 0x34 shift +Arabic_jeh 0x34 altgr # evdev 53 (0x35), QKeyCode "slash", number 0x35 Arabic_zah 0x35 Arabic_question_mark 0x35 shift +U066D 0x35 altgr +U200C 0x35 shift altgr # evdev 54 (0x36), QKeyCode "shift_r", number 0x36 Shift_R 0x36 @@ -371,9 +409,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -390,8 +430,7 @@ KP_Divide 0xb5 Print 0x54 # evdev 100 (0x64), QKeyCode "alt_r", number 0xb8 -Alt_R 0xb8 -Meta_R 0xb8 shift +ISO_Level3_Shift 0xb8 # evdev 101 (0x65), QKeyCode "lf", number 0x5b Linefeed 0x5b @@ -733,6 +772,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/bepo b/pc-bios/keymaps/bepo index dbe8daa257..2292cbc499 100644 --- a/pc-bios/keymaps/bepo +++ b/pc-bios/keymaps/bepo @@ -399,9 +399,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -761,6 +763,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/common b/pc-bios/keymaps/common deleted file mode 100644 index adc56c77d2..0000000000 --- a/pc-bios/keymaps/common +++ /dev/null @@ -1,157 +0,0 @@ -include modifiers - -# -# Top row -# -1 0x2 -2 0x3 -3 0x4 -4 0x5 -5 0x6 -6 0x7 -7 0x8 -8 0x9 -9 0xa -0 0xb -BackSpace 0xe - -# -# QWERTY first row -# -Tab 0xf localstate -ISO_Left_Tab 0xf shift -q 0x10 addupper -w 0x11 addupper -e 0x12 addupper -r 0x13 addupper -t 0x14 addupper -y 0x15 addupper -u 0x16 addupper -i 0x17 addupper -o 0x18 addupper -p 0x19 addupper - -# -# QWERTY second row -# -a 0x1e addupper -s 0x1f addupper -d 0x20 addupper -f 0x21 addupper -g 0x22 addupper -h 0x23 addupper -j 0x24 addupper -k 0x25 addupper -l 0x26 addupper -Return 0x1c localstate - -# -# QWERTY third row -# -z 0x2c addupper -x 0x2d addupper -c 0x2e addupper -v 0x2f addupper -b 0x30 addupper -n 0x31 addupper -m 0x32 addupper - -space 0x39 localstate - -less 0x56 -greater 0x56 shift -bar 0x56 altgr -brokenbar 0x56 shift altgr - -# -# Esc and Function keys -# -Escape 0x1 localstate -F1 0x3b localstate -F2 0x3c localstate -F3 0x3d localstate -F4 0x3e localstate -F5 0x3f localstate -F6 0x40 localstate -F7 0x41 localstate -F8 0x42 localstate -F9 0x43 localstate -F10 0x44 localstate -F11 0x57 localstate -F12 0x58 localstate - -# Printscreen, Scrollock and Pause -# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37), -# but (0xe0, 0x37) seems to work. -Print 0xb7 localstate -Sys_Req 0xb7 localstate -Execute 0xb7 localstate -Scroll_Lock 0x46 - -# -# Insert - PgDown -# -Insert 0xd2 localstate -Delete 0xd3 localstate -Home 0xc7 localstate -End 0xcf localstate -Page_Up 0xc9 localstate -Page_Down 0xd1 localstate - -# -# Arrow keys -# -Left 0xcb localstate -Up 0xc8 localstate -Down 0xd0 localstate -Right 0xcd localstate - -# -# Numpad -# -Num_Lock 0x45 -KP_Divide 0xb5 -KP_Multiply 0x37 -KP_Subtract 0x4a -KP_Add 0x4e -KP_Enter 0x9c - -KP_Decimal 0x53 numlock -KP_Separator 0x53 numlock -KP_Delete 0x53 - -KP_0 0x52 numlock -KP_Insert 0x52 - -KP_1 0x4f numlock -KP_End 0x4f - -KP_2 0x50 numlock -KP_Down 0x50 - -KP_3 0x51 numlock -KP_Next 0x51 - -KP_4 0x4b numlock -KP_Left 0x4b - -KP_5 0x4c numlock -KP_Begin 0x4c - -KP_6 0x4d numlock -KP_Right 0x4d - -KP_7 0x47 numlock -KP_Home 0x47 - -KP_8 0x48 numlock -KP_Up 0x48 - -KP_9 0x49 numlock -KP_Prior 0x49 - -Caps_Lock 0x3a -# -# Inhibited keys -# -Multi_key 0x0 inhibit diff --git a/pc-bios/keymaps/cz b/pc-bios/keymaps/cz index 46b37757fe..29cf2b05d8 100644 --- a/pc-bios/keymaps/cz +++ b/pc-bios/keymaps/cz @@ -456,9 +456,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -817,6 +819,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/da b/pc-bios/keymaps/da index f7cdad7420..547d8c716b 100644 --- a/pc-bios/keymaps/da +++ b/pc-bios/keymaps/da @@ -455,9 +455,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -816,6 +818,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym 0x0edd0160) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym 0x0edd0270) + # # quirks section start # diff --git a/pc-bios/keymaps/de b/pc-bios/keymaps/de index d0b4715474..261243edbc 100644 --- a/pc-bios/keymaps/de +++ b/pc-bios/keymaps/de @@ -2,10 +2,10 @@ # generated by qemu-keymap # model : pc105 # layout : de -# variant : - +# variant : nodeadkeys # options : - -# name: "German" +# name: "German (no dead keys)" # modifiers # 0: Shift @@ -100,10 +100,9 @@ backslash 0x0c altgr questiondown 0x0c shift altgr # evdev 13 (0xd), QKeyCode "equal", number 0xd -dead_acute 0x0d -dead_grave 0x0d shift -dead_cedilla 0x0d altgr -dead_ogonek 0x0d shift altgr +acute 0x0d +grave 0x0d shift +cedilla 0x0d altgr # evdev 14 (0xe), QKeyCode "backspace", number 0xe BackSpace 0x0e @@ -174,8 +173,7 @@ THORN 0x19 shift altgr # evdev 26 (0x1a), QKeyCode "bracket_left", number 0x1a udiaeresis 0x1a Udiaeresis 0x1a shift -dead_diaeresis 0x1a altgr -dead_abovering 0x1a shift altgr +diaeresis 0x1a altgr # evdev 27 (0x1b), QKeyCode "bracket_right", number 0x1b plus 0x1b @@ -246,20 +244,17 @@ Lstroke 0x26 shift altgr # evdev 39 (0x27), QKeyCode "semicolon", number 0x27 odiaeresis 0x27 Odiaeresis 0x27 shift -dead_doubleacute 0x27 altgr -dead_belowdot 0x27 shift altgr +doubleacute 0x27 altgr # evdev 40 (0x28), QKeyCode "apostrophe", number 0x28 adiaeresis 0x28 Adiaeresis 0x28 shift -dead_circumflex 0x28 altgr -dead_caron 0x28 shift altgr +asciicircum 0x28 altgr # evdev 41 (0x29), QKeyCode "grave_accent", number 0x29 -dead_circumflex 0x29 +asciicircum 0x29 degree 0x29 shift -U2032 0x29 altgr -U2033 0x29 shift altgr +notsign 0x29 altgr # evdev 42 (0x2a), QKeyCode "shift", number 0x2a Shift_L 0x2a @@ -268,7 +263,7 @@ Shift_L 0x2a numbersign 0x2b apostrophe 0x2b shift rightsinglequotemark 0x2b altgr -dead_breve 0x2b shift altgr +grave 0x2b shift altgr # evdev 44 (0x2c), QKeyCode "z", number 0x2c y 0x2c @@ -440,7 +435,7 @@ KP_Separator 0x53 numlock less 0x56 greater 0x56 shift bar 0x56 altgr -brokenbar 0x56 shift altgr +dead_belowmacron 0x56 shift altgr # evdev 87 (0x57), QKeyCode "f11", number 0x57 F11 0x57 @@ -458,9 +453,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -819,6 +816,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/de-ch b/pc-bios/keymaps/de-ch index ad37f6cc19..ed3da058e4 100644 --- a/pc-bios/keymaps/de-ch +++ b/pc-bios/keymaps/de-ch @@ -455,9 +455,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -816,6 +818,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/en-gb b/pc-bios/keymaps/en-gb index 999cca7c71..18d8fce4d4 100644 --- a/pc-bios/keymaps/en-gb +++ b/pc-bios/keymaps/en-gb @@ -453,9 +453,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -815,6 +817,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/en-us b/pc-bios/keymaps/en-us index a70e03adc0..06762cfc6c 100644 --- a/pc-bios/keymaps/en-us +++ b/pc-bios/keymaps/en-us @@ -365,9 +365,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -727,6 +729,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/es b/pc-bios/keymaps/es index 53e66e88c7..fde441492d 100644 --- a/pc-bios/keymaps/es +++ b/pc-bios/keymaps/es @@ -454,9 +454,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -815,6 +817,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/et b/pc-bios/keymaps/et index 7bed679bc9..a5dcbd18ab 100644 --- a/pc-bios/keymaps/et +++ b/pc-bios/keymaps/et @@ -363,9 +363,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -725,6 +727,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/fi b/pc-bios/keymaps/fi index 1c7653dc6b..816749421e 100644 --- a/pc-bios/keymaps/fi +++ b/pc-bios/keymaps/fi @@ -429,9 +429,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -790,6 +792,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/fo b/pc-bios/keymaps/fo index e69575bcb7..8bb6f97bba 100644 --- a/pc-bios/keymaps/fo +++ b/pc-bios/keymaps/fo @@ -456,9 +456,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -817,6 +819,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/fr b/pc-bios/keymaps/fr index 5b25227455..82ca812c7e 100644 --- a/pc-bios/keymaps/fr +++ b/pc-bios/keymaps/fr @@ -2,10 +2,10 @@ # generated by qemu-keymap # model : pc105 # layout : fr -# variant : - +# variant : nodeadkeys # options : - -# name: "French" +# name: "French (no dead keys)" # modifiers # 0: Shift @@ -103,7 +103,7 @@ questiondown 0x0c shift altgr equal 0x0d plus 0x0d shift braceright 0x0d altgr -dead_ogonek 0x0d shift altgr +ogonek 0x0d shift altgr # evdev 14 (0xe), QKeyCode "backspace", number 0xe BackSpace 0x0e @@ -173,8 +173,8 @@ thorn 0x19 altgr THORN 0x19 shift altgr # evdev 26 (0x1a), QKeyCode "bracket_left", number 0x1a -dead_circumflex 0x1a -dead_diaeresis 0x1a shift +asciicircum 0x1a +diaeresis 0x1a shift dead_diaeresis 0x1a altgr dead_abovering 0x1a shift altgr @@ -182,7 +182,7 @@ dead_abovering 0x1a shift altgr dollar 0x1b sterling 0x1b shift currency 0x1b altgr -dead_macron 0x1b shift altgr +macron 0x1b shift altgr # evdev 28 (0x1c), QKeyCode "ret", number 0x1c Return 0x1c @@ -253,8 +253,8 @@ masculine 0x27 shift altgr # evdev 40 (0x28), QKeyCode "apostrophe", number 0x28 ugrave 0x28 percent 0x28 shift -dead_circumflex 0x28 altgr -dead_caron 0x28 shift altgr +asciicircum 0x28 altgr +caron 0x28 shift altgr # evdev 41 (0x29), QKeyCode "grave_accent", number 0x29 twosuperior 0x29 @@ -267,8 +267,8 @@ Shift_L 0x2a # evdev 43 (0x2b), QKeyCode "backslash", number 0x2b asterisk 0x2b mu 0x2b shift -dead_grave 0x2b altgr -dead_breve 0x2b shift altgr +grave 0x2b altgr +breve 0x2b shift altgr # evdev 44 (0x2c), QKeyCode "z", number 0x2c w 0x2c @@ -307,8 +307,8 @@ N 0x31 shift # evdev 50 (0x32), QKeyCode "m", number 0x32 comma 0x32 question 0x32 shift -dead_acute 0x32 altgr -dead_doubleacute 0x32 shift altgr +acute 0x32 altgr +doubleacute 0x32 shift altgr # evdev 51 (0x33), QKeyCode "comma", number 0x33 semicolon 0x33 @@ -326,7 +326,7 @@ division 0x34 shift altgr exclam 0x35 section 0x35 shift dead_belowdot 0x35 altgr -dead_abovedot 0x35 shift altgr +abovedot 0x35 shift altgr # evdev 54 (0x36), QKeyCode "shift_r", number 0x36 Shift_R 0x36 @@ -456,9 +456,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -817,6 +819,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/fr-be b/pc-bios/keymaps/fr-be index 9d2ac5ddf9..f225f179a2 100644 --- a/pc-bios/keymaps/fr-be +++ b/pc-bios/keymaps/fr-be @@ -455,9 +455,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -816,6 +818,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/fr-ca b/pc-bios/keymaps/fr-ca index 736897bc43..f264d0a9c3 100644 --- a/pc-bios/keymaps/fr-ca +++ b/pc-bios/keymaps/fr-ca @@ -387,9 +387,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -748,6 +750,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/fr-ch b/pc-bios/keymaps/fr-ch index 40e1fefd54..bdd944da1c 100644 --- a/pc-bios/keymaps/fr-ch +++ b/pc-bios/keymaps/fr-ch @@ -455,9 +455,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -816,6 +818,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/hr b/pc-bios/keymaps/hr index 6b89f09922..252936c3ae 100644 --- a/pc-bios/keymaps/hr +++ b/pc-bios/keymaps/hr @@ -456,9 +456,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -817,6 +819,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/hu b/pc-bios/keymaps/hu index a6bd66d08d..853147610a 100644 --- a/pc-bios/keymaps/hu +++ b/pc-bios/keymaps/hu @@ -455,9 +455,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -816,6 +818,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym 0x0a865600) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym 0x0a865710) + # # quirks section start # diff --git a/pc-bios/keymaps/is b/pc-bios/keymaps/is index 063675d118..c88fa603aa 100644 --- a/pc-bios/keymaps/is +++ b/pc-bios/keymaps/is @@ -456,9 +456,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -817,6 +819,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/it b/pc-bios/keymaps/it index abc3ed15de..df00156cf8 100644 --- a/pc-bios/keymaps/it +++ b/pc-bios/keymaps/it @@ -459,9 +459,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -820,6 +822,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/ja b/pc-bios/keymaps/ja index aae93e8c52..6eb2e7bc20 100644 --- a/pc-bios/keymaps/ja +++ b/pc-bios/keymaps/ja @@ -367,9 +367,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -731,6 +733,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/lt b/pc-bios/keymaps/lt index 41013675f8..db9b24f611 100644 --- a/pc-bios/keymaps/lt +++ b/pc-bios/keymaps/lt @@ -454,9 +454,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -815,6 +817,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/lv b/pc-bios/keymaps/lv index 27260ce32e..54997ce092 100644 --- a/pc-bios/keymaps/lv +++ b/pc-bios/keymaps/lv @@ -429,9 +429,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -790,6 +792,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/mk b/pc-bios/keymaps/mk index 30a597caa2..cb362b6d39 100644 --- a/pc-bios/keymaps/mk +++ b/pc-bios/keymaps/mk @@ -365,9 +365,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -727,6 +729,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/modifiers b/pc-bios/keymaps/modifiers deleted file mode 100644 index d73b7a6637..0000000000 --- a/pc-bios/keymaps/modifiers +++ /dev/null @@ -1,18 +0,0 @@ -Shift_R 0x36 -Shift_L 0x2a - -Alt_R 0xb8 -Mode_switch 0xb8 -ISO_Level3_Shift 0xb8 -Alt_L 0x38 - -Control_R 0x9d -Control_L 0x1d - -# Translate Super to Windows keys. -# This is hardcoded. See documentation for details. -Super_R 0xdc -Super_L 0xdb - -# Translate Menu to the Windows Application key. -Menu 0xdd diff --git a/pc-bios/keymaps/nl b/pc-bios/keymaps/nl index ae7c8f5643..1579829eef 100644 --- a/pc-bios/keymaps/nl +++ b/pc-bios/keymaps/nl @@ -456,9 +456,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -817,6 +819,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/nl-be b/pc-bios/keymaps/nl-be deleted file mode 100644 index 34fc881ad0..0000000000 --- a/pc-bios/keymaps/nl-be +++ /dev/null @@ -1,3 +0,0 @@ -# Dutch (Belgium) -map 0x813 -include common diff --git a/pc-bios/keymaps/no b/pc-bios/keymaps/no index 8afd1996b5..cd634e7b90 100644 --- a/pc-bios/keymaps/no +++ b/pc-bios/keymaps/no @@ -467,9 +467,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -831,6 +833,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym 0x00408160) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym 0x00408270) + # # quirks section start # diff --git a/pc-bios/keymaps/pl b/pc-bios/keymaps/pl index df2720622f..30d93ca727 100644 --- a/pc-bios/keymaps/pl +++ b/pc-bios/keymaps/pl @@ -460,9 +460,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -821,6 +823,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/pt b/pc-bios/keymaps/pt index ab590017cf..c34d6e3761 100644 --- a/pc-bios/keymaps/pt +++ b/pc-bios/keymaps/pt @@ -453,9 +453,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -814,6 +816,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/pt-br b/pc-bios/keymaps/pt-br index fe9ec81303..2d409c0bc0 100644 --- a/pc-bios/keymaps/pt-br +++ b/pc-bios/keymaps/pt-br @@ -453,9 +453,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -814,6 +816,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/ru b/pc-bios/keymaps/ru index 7566052262..9502ba1269 100644 --- a/pc-bios/keymaps/ru +++ b/pc-bios/keymaps/ru @@ -366,9 +366,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -728,6 +730,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/sl b/pc-bios/keymaps/sl index 56835a92c3..73eb956d04 100644 --- a/pc-bios/keymaps/sl +++ b/pc-bios/keymaps/sl @@ -1,5 +1,180 @@ # generated from XKB map sl -include common + +Shift_R 0x36 +Shift_L 0x2a + +Alt_R 0xb8 +Mode_switch 0xb8 +ISO_Level3_Shift 0xb8 +Alt_L 0x38 + +Control_R 0x9d +Control_L 0x1d + +# Translate Super to Windows keys. +# This is hardcoded. See documentation for details. +Super_R 0xdc +Super_L 0xdb + +# Translate Menu to the Windows Application key. +Menu 0xdd + +# +# Top row +# +1 0x2 +2 0x3 +3 0x4 +4 0x5 +5 0x6 +6 0x7 +7 0x8 +8 0x9 +9 0xa +0 0xb +BackSpace 0xe + +# +# QWERTY first row +# +Tab 0xf localstate +ISO_Left_Tab 0xf shift +q 0x10 addupper +w 0x11 addupper +e 0x12 addupper +r 0x13 addupper +t 0x14 addupper +y 0x15 addupper +u 0x16 addupper +i 0x17 addupper +o 0x18 addupper +p 0x19 addupper + +# +# QWERTY second row +# +a 0x1e addupper +s 0x1f addupper +d 0x20 addupper +f 0x21 addupper +g 0x22 addupper +h 0x23 addupper +j 0x24 addupper +k 0x25 addupper +l 0x26 addupper +Return 0x1c localstate + +# +# QWERTY third row +# +z 0x2c addupper +x 0x2d addupper +c 0x2e addupper +v 0x2f addupper +b 0x30 addupper +n 0x31 addupper +m 0x32 addupper + +space 0x39 localstate + +less 0x56 +greater 0x56 shift +bar 0x56 altgr +brokenbar 0x56 shift altgr + +# +# Esc and Function keys +# +Escape 0x1 localstate +F1 0x3b localstate +F2 0x3c localstate +F3 0x3d localstate +F4 0x3e localstate +F5 0x3f localstate +F6 0x40 localstate +F7 0x41 localstate +F8 0x42 localstate +F9 0x43 localstate +F10 0x44 localstate +F11 0x57 localstate +F12 0x58 localstate + +# Printscreen, Scrollock and Pause +# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37), +# but (0xe0, 0x37) seems to work. +Print 0xb7 localstate +Sys_Req 0xb7 localstate +Execute 0xb7 localstate +Scroll_Lock 0x46 + +# +# Insert - PgDown +# +Insert 0xd2 localstate +Delete 0xd3 localstate +Home 0xc7 localstate +End 0xcf localstate +Page_Up 0xc9 localstate +Page_Down 0xd1 localstate + +# +# Arrow keys +# +Left 0xcb localstate +Up 0xc8 localstate +Down 0xd0 localstate +Right 0xcd localstate + +# +# Numpad +# +Num_Lock 0x45 +KP_Divide 0xb5 +KP_Multiply 0x37 +KP_Subtract 0x4a +KP_Add 0x4e +KP_Enter 0x9c + +KP_Decimal 0x53 numlock +KP_Separator 0x53 numlock +KP_Delete 0x53 + +KP_0 0x52 numlock +KP_Insert 0x52 + +KP_1 0x4f numlock +KP_End 0x4f + +KP_2 0x50 numlock +KP_Down 0x50 + +KP_3 0x51 numlock +KP_Next 0x51 + +KP_4 0x4b numlock +KP_Left 0x4b + +KP_5 0x4c numlock +KP_Begin 0x4c + +KP_6 0x4d numlock +KP_Right 0x4d + +KP_7 0x47 numlock +KP_Home 0x47 + +KP_8 0x48 numlock +KP_Up 0x48 + +KP_9 0x49 numlock +KP_Prior 0x49 + +Caps_Lock 0x3a +# +# Inhibited keys +# +Multi_key 0x0 inhibit + map 0x424 exclam 0x02 shift asciitilde 0x02 altgr diff --git a/pc-bios/keymaps/sv b/pc-bios/keymaps/sv index 5d9080eff1..0717b7114f 100644 --- a/pc-bios/keymaps/sv +++ b/pc-bios/keymaps/sv @@ -1,5 +1,179 @@ map 0x0000041d -include common + +Shift_R 0x36 +Shift_L 0x2a + +Alt_R 0xb8 +Mode_switch 0xb8 +ISO_Level3_Shift 0xb8 +Alt_L 0x38 + +Control_R 0x9d +Control_L 0x1d + +# Translate Super to Windows keys. +# This is hardcoded. See documentation for details. +Super_R 0xdc +Super_L 0xdb + +# Translate Menu to the Windows Application key. +Menu 0xdd + +# +# Top row +# +1 0x2 +2 0x3 +3 0x4 +4 0x5 +5 0x6 +6 0x7 +7 0x8 +8 0x9 +9 0xa +0 0xb +BackSpace 0xe + +# +# QWERTY first row +# +Tab 0xf localstate +ISO_Left_Tab 0xf shift +q 0x10 addupper +w 0x11 addupper +e 0x12 addupper +r 0x13 addupper +t 0x14 addupper +y 0x15 addupper +u 0x16 addupper +i 0x17 addupper +o 0x18 addupper +p 0x19 addupper + +# +# QWERTY second row +# +a 0x1e addupper +s 0x1f addupper +d 0x20 addupper +f 0x21 addupper +g 0x22 addupper +h 0x23 addupper +j 0x24 addupper +k 0x25 addupper +l 0x26 addupper +Return 0x1c localstate + +# +# QWERTY third row +# +z 0x2c addupper +x 0x2d addupper +c 0x2e addupper +v 0x2f addupper +b 0x30 addupper +n 0x31 addupper +m 0x32 addupper + +space 0x39 localstate + +less 0x56 +greater 0x56 shift +bar 0x56 altgr +brokenbar 0x56 shift altgr + +# +# Esc and Function keys +# +Escape 0x1 localstate +F1 0x3b localstate +F2 0x3c localstate +F3 0x3d localstate +F4 0x3e localstate +F5 0x3f localstate +F6 0x40 localstate +F7 0x41 localstate +F8 0x42 localstate +F9 0x43 localstate +F10 0x44 localstate +F11 0x57 localstate +F12 0x58 localstate + +# Printscreen, Scrollock and Pause +# Printscreen really requires four scancodes (0xe0, 0x2a, 0xe0, 0x37), +# but (0xe0, 0x37) seems to work. +Print 0xb7 localstate +Sys_Req 0xb7 localstate +Execute 0xb7 localstate +Scroll_Lock 0x46 + +# +# Insert - PgDown +# +Insert 0xd2 localstate +Delete 0xd3 localstate +Home 0xc7 localstate +End 0xcf localstate +Page_Up 0xc9 localstate +Page_Down 0xd1 localstate + +# +# Arrow keys +# +Left 0xcb localstate +Up 0xc8 localstate +Down 0xd0 localstate +Right 0xcd localstate + +# +# Numpad +# +Num_Lock 0x45 +KP_Divide 0xb5 +KP_Multiply 0x37 +KP_Subtract 0x4a +KP_Add 0x4e +KP_Enter 0x9c + +KP_Decimal 0x53 numlock +KP_Separator 0x53 numlock +KP_Delete 0x53 + +KP_0 0x52 numlock +KP_Insert 0x52 + +KP_1 0x4f numlock +KP_End 0x4f + +KP_2 0x50 numlock +KP_Down 0x50 + +KP_3 0x51 numlock +KP_Next 0x51 + +KP_4 0x4b numlock +KP_Left 0x4b + +KP_5 0x4c numlock +KP_Begin 0x4c + +KP_6 0x4d numlock +KP_Right 0x4d + +KP_7 0x47 numlock +KP_Home 0x47 + +KP_8 0x48 numlock +KP_Up 0x48 + +KP_9 0x49 numlock +KP_Prior 0x49 + +Caps_Lock 0x3a +# +# Inhibited keys +# +Multi_key 0x0 inhibit # # Top row diff --git a/pc-bios/keymaps/th b/pc-bios/keymaps/th index 56a01354d6..b8298d902b 100644 --- a/pc-bios/keymaps/th +++ b/pc-bios/keymaps/th @@ -365,9 +365,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -727,6 +729,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/keymaps/tr b/pc-bios/keymaps/tr index 4d1a4c3311..8ef60a60c1 100644 --- a/pc-bios/keymaps/tr +++ b/pc-bios/keymaps/tr @@ -449,9 +449,11 @@ Hiragana 0x77 # evdev 92 (0x5c), QKeyCode "henkan", number 0x79 Henkan_Mode 0x79 -# evdev 93 (0x5d): no evdev -> QKeyCode mapping (xkb keysym Hiragana_Katakana) +# evdev 93 (0x5d), QKeyCode "katakanahiragana", number 0x70 +Hiragana_Katakana 0x70 -# evdev 94 (0x5e): no evdev -> QKeyCode mapping (xkb keysym Muhenkan) +# evdev 94 (0x5e), QKeyCode "muhenkan", number 0x7b +Muhenkan 0x7b # evdev 95 (0x5f): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) @@ -810,6 +812,10 @@ XF86AudioMedia 0xed # evdev 245 (0xf5): no evdev -> QKeyCode mapping (xkb keysym NoSymbol) +# evdev 246 (0xf6): no evdev -> QKeyCode mapping (xkb keysym Invalid) + +# evdev 247 (0xf7): no evdev -> QKeyCode mapping (xkb keysym Invalid) + # # quirks section start # diff --git a/pc-bios/openbios-ppc b/pc-bios/openbios-ppc index a39cbe57ca..4df553c8a3 100644 Binary files a/pc-bios/openbios-ppc and b/pc-bios/openbios-ppc differ diff --git a/pc-bios/openbios-sparc32 b/pc-bios/openbios-sparc32 index 7163ba8b3b..270c5000f9 100644 Binary files a/pc-bios/openbios-sparc32 and b/pc-bios/openbios-sparc32 differ diff --git a/pc-bios/openbios-sparc64 b/pc-bios/openbios-sparc64 index 0a9a338f78..a37a877b3e 100644 Binary files a/pc-bios/openbios-sparc64 and b/pc-bios/openbios-sparc64 differ diff --git a/pc-bios/optionrom/Makefile b/pc-bios/optionrom/Makefile index a9a9e5e7eb..e33a24da0d 100644 --- a/pc-bios/optionrom/Makefile +++ b/pc-bios/optionrom/Makefile @@ -37,7 +37,7 @@ Wa = -Wa, ASFLAGS += -32 QEMU_CFLAGS += $(call cc-c-option, $(QEMU_CFLAGS), $(Wa)-32) -build-all: multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin +build-all: multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin pvh.bin # suppress auto-removal of intermediate files .SECONDARY: @@ -46,6 +46,9 @@ build-all: multiboot.bin linuxboot.bin linuxboot_dma.bin kvmvapic.bin %.o: %.S $(call quiet-command,$(CPP) $(QEMU_INCLUDES) $(QEMU_DGFLAGS) -c -o - $< | $(AS) $(ASFLAGS) -o $@,"AS","$(TARGET_DIR)$@") +pvh.img: pvh.o pvh_main.o + $(call quiet-command,$(LD) $(LDFLAGS_NOPIE) -m $(LD_I386_EMULATION) -T $(SRC_PATH)/pc-bios/optionrom/flat.lds -s -o $@ $^,"BUILD","$(TARGET_DIR)$@") + %.img: %.o $(call quiet-command,$(LD) $(LDFLAGS_NOPIE) -m $(LD_I386_EMULATION) -T $(SRC_PATH)/pc-bios/optionrom/flat.lds -s -o $@ $<,"BUILD","$(TARGET_DIR)$@") diff --git a/pc-bios/optionrom/linuxboot_dma.c b/pc-bios/optionrom/linuxboot_dma.c index 4754282ad7..cbcf6679d9 100644 --- a/pc-bios/optionrom/linuxboot_dma.c +++ b/pc-bios/optionrom/linuxboot_dma.c @@ -58,33 +58,13 @@ asm( " jmp load_kernel\n" ); -#include "../../include/hw/nvram/fw_cfg_keys.h" - -/* QEMU_CFG_DMA_CONTROL bits */ -#define BIOS_CFG_DMA_CTL_ERROR 0x01 -#define BIOS_CFG_DMA_CTL_READ 0x02 -#define BIOS_CFG_DMA_CTL_SKIP 0x04 -#define BIOS_CFG_DMA_CTL_SELECT 0x08 - -#define BIOS_CFG_DMA_ADDR_HIGH 0x514 -#define BIOS_CFG_DMA_ADDR_LOW 0x518 - -#define uint64_t unsigned long long -#define uint32_t unsigned int -#define uint16_t unsigned short - -#define barrier() asm("" : : : "memory") - -typedef struct FWCfgDmaAccess { - uint32_t control; - uint32_t length; - uint64_t address; -} __attribute__((packed)) FWCfgDmaAccess; - -static inline void outl(uint32_t value, uint16_t port) -{ - asm("outl %0, %w1" : : "a"(value), "Nd"(port)); -} +/* + * The includes of C headers must be after the asm block to avoid compiler + * errors. + */ +#include +#include "optrom.h" +#include "optrom_fw_cfg.h" static inline void set_es(void *addr) { @@ -92,12 +72,6 @@ static inline void set_es(void *addr) asm("movl %0, %%es" : : "r"(seg)); } -#ifdef __clang__ -#define ADDR32 -#else -#define ADDR32 "addr32 " -#endif - static inline uint16_t readw_es(uint16_t offset) { uint16_t val; @@ -120,56 +94,6 @@ static inline void writel_es(uint16_t offset, uint32_t val) asm(ADDR32 "movl %0, %%es:(%1)" : : "r"(val), "r"((uint32_t)offset)); } -static inline uint32_t bswap32(uint32_t x) -{ - asm("bswapl %0" : "=r" (x) : "0" (x)); - return x; -} - -static inline uint64_t bswap64(uint64_t x) -{ - asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x)); - return x; -} - -static inline uint64_t cpu_to_be64(uint64_t x) -{ - return bswap64(x); -} - -static inline uint32_t cpu_to_be32(uint32_t x) -{ - return bswap32(x); -} - -static inline uint32_t be32_to_cpu(uint32_t x) -{ - return bswap32(x); -} - -/* clang is happy to inline this function, and bloats the - * ROM. - */ -static __attribute__((__noinline__)) -void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len) -{ - FWCfgDmaAccess access; - uint32_t control = (entry << 16) | BIOS_CFG_DMA_CTL_SELECT - | BIOS_CFG_DMA_CTL_READ; - - access.address = cpu_to_be64((uint64_t)(uint32_t)buf); - access.length = cpu_to_be32(len); - access.control = cpu_to_be32(control); - - barrier(); - - outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW); - - while (be32_to_cpu(access.control) & ~BIOS_CFG_DMA_CTL_ERROR) { - barrier(); - } -} - /* Return top of memory using BIOS function E801. */ static uint32_t get_e801_addr(void) { @@ -223,9 +147,9 @@ void load_kernel(void) uint32_t initrd_end_page, max_allowed_page; uint32_t segment_addr, stack_addr; - bios_cfg_read_entry(&setup_addr, FW_CFG_SETUP_ADDR, 4); - bios_cfg_read_entry(&setup_size, FW_CFG_SETUP_SIZE, 4); - bios_cfg_read_entry(setup_addr, FW_CFG_SETUP_DATA, setup_size); + bios_cfg_read_entry_dma(&setup_addr, FW_CFG_SETUP_ADDR, 4); + bios_cfg_read_entry_dma(&setup_size, FW_CFG_SETUP_SIZE, 4); + bios_cfg_read_entry_dma(setup_addr, FW_CFG_SETUP_DATA, setup_size); set_es(setup_addr); @@ -235,8 +159,8 @@ void load_kernel(void) writel_es(0x22c, 0x37ffffff); } - bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4); - bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4); + bios_cfg_read_entry_dma(&initrd_addr, FW_CFG_INITRD_ADDR, 4); + bios_cfg_read_entry_dma(&initrd_size, FW_CFG_INITRD_SIZE, 4); initrd_end_page = ((uint32_t)(initrd_addr + initrd_size) & -4096); max_allowed_page = (readl_es(0x22c) & -4096); @@ -251,15 +175,15 @@ void load_kernel(void) } - bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size); + bios_cfg_read_entry_dma(initrd_addr, FW_CFG_INITRD_DATA, initrd_size); - bios_cfg_read_entry(&kernel_addr, FW_CFG_KERNEL_ADDR, 4); - bios_cfg_read_entry(&kernel_size, FW_CFG_KERNEL_SIZE, 4); - bios_cfg_read_entry(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size); + bios_cfg_read_entry_dma(&kernel_addr, FW_CFG_KERNEL_ADDR, 4); + bios_cfg_read_entry_dma(&kernel_size, FW_CFG_KERNEL_SIZE, 4); + bios_cfg_read_entry_dma(kernel_addr, FW_CFG_KERNEL_DATA, kernel_size); - bios_cfg_read_entry(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4); - bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4); - bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size); + bios_cfg_read_entry_dma(&cmdline_addr, FW_CFG_CMDLINE_ADDR, 4); + bios_cfg_read_entry_dma(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4); + bios_cfg_read_entry_dma(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size); /* Boot linux */ segment_addr = ((uint32_t)setup_addr >> 4); diff --git a/pc-bios/optionrom/optionrom.h b/pc-bios/optionrom/optionrom.h index 6c4c2c82f4..a2b612f1a7 100644 --- a/pc-bios/optionrom/optionrom.h +++ b/pc-bios/optionrom/optionrom.h @@ -19,7 +19,20 @@ */ -#include "../../include/hw/nvram/fw_cfg_keys.h" +#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_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 BIOS_CFG_IOPORT_CFG 0x510 #define BIOS_CFG_IOPORT_DATA 0x511 diff --git a/pc-bios/optionrom/optrom.h b/pc-bios/optionrom/optrom.h new file mode 100644 index 0000000000..357819259a --- /dev/null +++ b/pc-bios/optionrom/optrom.h @@ -0,0 +1,110 @@ +/* + * Common Option ROM Functions for C 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; 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 . + * + * Copyright (c) 2015-2019 Red Hat Inc. + * Authors: + * Marc Marí + * Richard W.M. Jones + * Stefano Garzarella + */ + +#ifndef OPTROM_H +#define OPTROM_H + +#include +#include "../../include/standard-headers/linux/qemu_fw_cfg.h" + +#define barrier() asm("" : : : "memory") + +#ifdef __clang__ +#define ADDR32 +#else +#define ADDR32 "addr32 " +#endif + +static inline void outb(uint8_t value, uint16_t port) +{ + asm volatile("outb %0, %w1" : : "a"(value), "Nd"(port)); +} + +static inline void outw(uint16_t value, uint16_t port) +{ + asm volatile("outw %0, %w1" : : "a"(value), "Nd"(port)); +} + +static inline void outl(uint32_t value, uint16_t port) +{ + asm volatile("outl %0, %w1" : : "a"(value), "Nd"(port)); +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t value; + + asm volatile("inb %w1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline uint16_t inw(uint16_t port) +{ + uint16_t value; + + asm volatile("inw %w1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline uint32_t inl(uint16_t port) +{ + uint32_t value; + + asm volatile("inl %w1, %0" : "=a"(value) : "Nd"(port)); + return value; +} + +static inline void insb(uint16_t port, uint8_t *buf, uint32_t len) +{ + asm volatile("rep insb %%dx, %%es:(%%edi)" + : "+c"(len), "+D"(buf) : "d"(port) : "memory"); +} + +static inline uint32_t bswap32(uint32_t x) +{ + asm("bswapl %0" : "=r" (x) : "0" (x)); + return x; +} + +static inline uint64_t bswap64(uint64_t x) +{ + asm("bswapl %%eax; bswapl %%edx; xchg %%eax, %%edx" : "=A" (x) : "0" (x)); + return x; +} + +static inline uint64_t cpu_to_be64(uint64_t x) +{ + return bswap64(x); +} + +static inline uint32_t cpu_to_be32(uint32_t x) +{ + return bswap32(x); +} + +static inline uint32_t be32_to_cpu(uint32_t x) +{ + return bswap32(x); +} + +#endif /* OPTROM_H */ diff --git a/pc-bios/optionrom/optrom_fw_cfg.h b/pc-bios/optionrom/optrom_fw_cfg.h new file mode 100644 index 0000000000..a3660a5200 --- /dev/null +++ b/pc-bios/optionrom/optrom_fw_cfg.h @@ -0,0 +1,92 @@ +/* + * Common Option ROM Functions for fw_cfg + * + * 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 . + * + * Copyright (c) 2015-2019 Red Hat Inc. + * Authors: + * Marc Marí + * Richard W.M. Jones + * Stefano Garzarella + */ + +#ifndef OPTROM_FW_CFG_H +#define OPTROM_FW_CFG_H + +#include "../../include/standard-headers/linux/qemu_fw_cfg.h" + +#define BIOS_CFG_IOPORT_CFG 0x510 +#define BIOS_CFG_IOPORT_DATA 0x511 +#define BIOS_CFG_DMA_ADDR_HIGH 0x514 +#define BIOS_CFG_DMA_ADDR_LOW 0x518 + +static __attribute__((unused)) +void bios_cfg_select(uint16_t key) +{ + outw(key, BIOS_CFG_IOPORT_CFG); +} + +static __attribute__((unused)) +void bios_cfg_read_entry_io(void *buf, uint16_t entry, uint32_t len) +{ + bios_cfg_select(entry); + insb(BIOS_CFG_IOPORT_DATA, buf, len); +} + +/* + * clang is happy to inline this function, and bloats the + * ROM. + */ +static __attribute__((__noinline__)) __attribute__((unused)) +void bios_cfg_read_entry_dma(void *buf, uint16_t entry, uint32_t len) +{ + struct fw_cfg_dma_access access; + uint32_t control = (entry << 16) | FW_CFG_DMA_CTL_SELECT + | FW_CFG_DMA_CTL_READ; + + access.address = cpu_to_be64((uint64_t)(uint32_t)buf); + access.length = cpu_to_be32(len); + access.control = cpu_to_be32(control); + + barrier(); + + outl(cpu_to_be32((uint32_t)&access), BIOS_CFG_DMA_ADDR_LOW); + + while (be32_to_cpu(access.control) & ~FW_CFG_DMA_CTL_ERROR) { + barrier(); + } +} + +static __attribute__((unused)) +void bios_cfg_read_entry(void *buf, uint16_t entry, uint32_t len, + uint32_t version) +{ + if (version & FW_CFG_VERSION_DMA) { + bios_cfg_read_entry_dma(buf, entry, len); + } else { + bios_cfg_read_entry_io(buf, entry, len); + } +} + +static __attribute__((unused)) +uint32_t bios_cfg_version(void) +{ + uint32_t version; + + bios_cfg_read_entry_io(&version, FW_CFG_ID, sizeof(version)); + + return version; +} + +#endif /* OPTROM_FW_CFG_H */ diff --git a/pc-bios/optionrom/pvh.S b/pc-bios/optionrom/pvh.S new file mode 100644 index 0000000000..e1d7f4a7a7 --- /dev/null +++ b/pc-bios/optionrom/pvh.S @@ -0,0 +1,200 @@ +/* + * PVH Option ROM + * + * 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 . + * + * Copyright Novell Inc, 2009 + * Authors: Alexander Graf + * + * Copyright (c) 2019 Red Hat Inc. + * Authors: Stefano Garzarella + */ + +#include "optionrom.h" + +#define BOOT_ROM_PRODUCT "PVH loader" + +#define GS_PROT_JUMP 0 +#define GS_GDT_DESC 6 + +#ifdef OPTION_ROM_START +#undef OPTION_ROM_START +#endif +#ifdef OPTION_ROM_END +#undef OPTION_ROM_END +#endif + +/* + * Redefine OPTION_ROM_START and OPTION_ROM_END, because this rom is produced + * linking multiple objects. + * signrom.py will add padding. + */ +#define OPTION_ROM_START \ + .code16; \ + .text; \ + .global _start; \ + _start:; \ + .short 0xaa55; \ + .byte 3; /* desired size in 512 units */ + +#define OPTION_ROM_END \ + _end: + +BOOT_ROM_START + +run_pvhboot: + + cli + cld + + mov %cs, %eax + shl $0x4, %eax + + /* set up a long jump descriptor that is PC relative */ + + /* move stack memory to %gs */ + mov %ss, %ecx + shl $0x4, %ecx + mov %esp, %ebx + add %ebx, %ecx + sub $0x20, %ecx + sub $0x30, %esp + shr $0x4, %ecx + mov %cx, %gs + + /* now push the indirect jump descriptor there */ + mov (prot_jump), %ebx + add %eax, %ebx + movl %ebx, %gs:GS_PROT_JUMP + mov $8, %bx + movw %bx, %gs:GS_PROT_JUMP + 4 + + /* fix the gdt descriptor to be PC relative */ + movw (gdt_desc), %bx + movw %bx, %gs:GS_GDT_DESC + movl (gdt_desc+2), %ebx + add %eax, %ebx + movl %ebx, %gs:GS_GDT_DESC + 2 + + /* initialize HVM memmap table using int 0x15(e820) */ + + /* ES = pvh_e820 struct */ + mov $pvh_e820, %eax + shr $4, %eax + mov %ax, %es + + /* start storing memmap table at %es:8 (pvh_e820.table) */ + mov $8,%edi + xor %ebx, %ebx + jmp memmap_loop + +memmap_loop_check: + /* pvh_e820 can contains up to 128 entries */ + cmp $128, %ebx + je memmap_done + +memmap_loop: + /* entry size (hvm_memmap_table_entry) & max buffer size (int15) */ + movl $24, %ecx + /* e820 */ + movl $0x0000e820, %eax + /* 'SMAP' magic */ + movl $0x534d4150, %edx + /* store counter value at %es:0 (pvh_e820.entries) */ + movl %ebx, %es:0 + + int $0x15 + /* error or last entry already done? */ + jb memmap_err + + /* %edi += entry size (hvm_memmap_table_entry) */ + add $24, %edi + + /* continuation value 0 means last entry */ + test %ebx, %ebx + jnz memmap_loop_check + + /* increase pvh_e820.entries to save the last entry */ + movl %es:0, %ebx + inc %ebx + +memmap_done: + movl %ebx, %es:0 + +memmap_err: + + /* load the GDT before going into protected mode */ +lgdt: + data32 lgdt %gs:GS_GDT_DESC + + /* get us to protected mode now */ + movl $1, %eax + movl %eax, %cr0 + + /* the LJMP sets CS for us and gets us to 32-bit */ +ljmp: + data32 ljmp *%gs:GS_PROT_JUMP + +prot_mode: +.code32 + + /* initialize all other segments */ + movl $0x10, %eax + movl %eax, %ss + movl %eax, %ds + movl %eax, %es + movl %eax, %fs + movl %eax, %gs + + jmp pvh_load_kernel + +/* Variables */ +.align 4, 0 +prot_jump: .long prot_mode + .short 8 + +.align 4, 0 +gdt: + /* 0x00 */ +.byte 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + + /* + * 0x08: code segment + * (base=0, limit=0xfffff, type=32bit code exec/read, DPL=0, 4k) + */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9a, 0xcf, 0x00 + + /* + * 0x10: data segment + * (base=0, limit=0xfffff, type=32bit data read/write, DPL=0, 4k) + */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0xcf, 0x00 + + /* + * 0x18: code segment + * (base=0, limit=0x0ffff, type=16bit code exec/read/conf, DPL=0, 1b) + */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00 + + /* + * 0x20: data segment + * (base=0, limit=0x0ffff, type=16bit data read/write, DPL=0, 1b) + */ +.byte 0xff, 0xff, 0x00, 0x00, 0x00, 0x92, 0x00, 0x00 + +gdt_desc: +.short (5 * 8) - 1 +.long gdt + +BOOT_ROM_END diff --git a/pc-bios/optionrom/pvh_main.c b/pc-bios/optionrom/pvh_main.c new file mode 100644 index 0000000000..a015e1bf22 --- /dev/null +++ b/pc-bios/optionrom/pvh_main.c @@ -0,0 +1,133 @@ +/* + * PVH Option ROM for fw_cfg DMA + * + * 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 . + * + * Copyright (c) 2019 Red Hat Inc. + * Authors: + * Stefano Garzarella + */ + +asm (".code32"); /* this code will be executed in protected mode */ + +#include +#include +#include "optrom.h" +#include "optrom_fw_cfg.h" +#include "../../include/hw/xen/start_info.h" + +#define RSDP_SIGNATURE 0x2052545020445352LL /* "RSD PTR " */ +#define RSDP_AREA_ADDR 0x000E0000 +#define RSDP_AREA_SIZE 2048 +#define EBDA_BASE_ADDR 0x0000040E +#define EBDA_SIZE 1024 + +#define E820_MAXENTRIES 128 +#define CMDLINE_BUFSIZE 4096 + +/* e820 table filled in pvh.S using int 0x15 */ +struct pvh_e820_table { + uint32_t entries; + uint32_t reserved; + struct hvm_memmap_table_entry table[E820_MAXENTRIES]; +}; + +struct pvh_e820_table pvh_e820 asm("pvh_e820") __attribute__ ((aligned)); + +static struct hvm_start_info start_info; +static struct hvm_modlist_entry ramdisk_mod; +static uint8_t cmdline_buffer[CMDLINE_BUFSIZE]; + + +/* Search RSDP signature. */ +static uintptr_t search_rsdp(uint32_t start_addr, uint32_t end_addr) +{ + uint64_t *rsdp_p; + + /* RSDP signature is always on a 16 byte boundary */ + for (rsdp_p = (uint64_t *)start_addr; rsdp_p < (uint64_t *)end_addr; + rsdp_p += 2) { + if (*rsdp_p == RSDP_SIGNATURE) { + return (uintptr_t)rsdp_p; + } + } + + return 0; +} + +/* Force the asm name without leading underscore, even on Win32. */ +extern void pvh_load_kernel(void) asm("pvh_load_kernel"); + +void pvh_load_kernel(void) +{ + void *cmdline_addr = &cmdline_buffer; + void *kernel_entry, *initrd_addr; + uint32_t cmdline_size, initrd_size, fw_cfg_version = bios_cfg_version(); + + start_info.magic = XEN_HVM_START_MAGIC_VALUE; + start_info.version = 1; + + /* + * pvh_e820 is filled in the pvh.S before to switch in protected mode, + * because we can use int 0x15 only in real mode. + */ + start_info.memmap_entries = pvh_e820.entries; + start_info.memmap_paddr = (uintptr_t)pvh_e820.table; + + /* + * Search RSDP in the main BIOS area below 1 MB. + * SeaBIOS store the RSDP in this area, so we try it first. + */ + start_info.rsdp_paddr = search_rsdp(RSDP_AREA_ADDR, + RSDP_AREA_ADDR + RSDP_AREA_SIZE); + + /* Search RSDP in the EBDA if it is not found */ + if (!start_info.rsdp_paddr) { + /* + * Th EBDA address is stored at EBDA_BASE_ADDR. It contains 2 bytes + * segment pointer to EBDA, so we must convert it to a linear address. + */ + uint32_t ebda_paddr = ((uint32_t)*((uint16_t *)EBDA_BASE_ADDR)) << 4; + if (ebda_paddr > 0x400) { + uint32_t *ebda = (uint32_t *)ebda_paddr; + + start_info.rsdp_paddr = search_rsdp(*ebda, *ebda + EBDA_SIZE); + } + } + + bios_cfg_read_entry(&cmdline_size, FW_CFG_CMDLINE_SIZE, 4, fw_cfg_version); + bios_cfg_read_entry(cmdline_addr, FW_CFG_CMDLINE_DATA, cmdline_size, + fw_cfg_version); + start_info.cmdline_paddr = (uintptr_t)cmdline_addr; + + /* Check if we have the initrd to load */ + bios_cfg_read_entry(&initrd_size, FW_CFG_INITRD_SIZE, 4, fw_cfg_version); + if (initrd_size) { + bios_cfg_read_entry(&initrd_addr, FW_CFG_INITRD_ADDR, 4, + fw_cfg_version); + bios_cfg_read_entry(initrd_addr, FW_CFG_INITRD_DATA, initrd_size, + fw_cfg_version); + + ramdisk_mod.paddr = (uintptr_t)initrd_addr; + ramdisk_mod.size = initrd_size; + + /* The first module is always ramdisk. */ + start_info.modlist_paddr = (uintptr_t)&ramdisk_mod; + start_info.nr_modules = 1; + } + + bios_cfg_read_entry(&kernel_entry, FW_CFG_KERNEL_ENTRY, 4, fw_cfg_version); + + asm volatile("jmp *%1" : : "b"(&start_info), "c"(kernel_entry)); +} diff --git a/pc-bios/palcode-clipper b/pc-bios/palcode-clipper old mode 100755 new mode 100644 index 1df377a0fd..b7dbc5d88c Binary files a/pc-bios/palcode-clipper and b/pc-bios/palcode-clipper differ diff --git a/pc-bios/pvh.bin b/pc-bios/pvh.bin new file mode 100644 index 0000000000..8033080ada Binary files /dev/null and b/pc-bios/pvh.bin differ diff --git a/pc-bios/qemu-icon.bmp b/pc-bios/qemu-icon.bmp deleted file mode 100644 index 72d9a2f561..0000000000 Binary files a/pc-bios/qemu-icon.bmp and /dev/null differ diff --git a/pc-bios/qemu_vga.ndrv b/pc-bios/qemu_vga.ndrv index 6e02f74d61..de81cfd1c5 100644 Binary files a/pc-bios/qemu_vga.ndrv and b/pc-bios/qemu_vga.ndrv differ diff --git a/pc-bios/s390-ccw/start.S b/pc-bios/s390-ccw/start.S index eb8d024dbb..5c22cb0849 100644 --- a/pc-bios/s390-ccw/start.S +++ b/pc-bios/s390-ccw/start.S @@ -59,9 +59,9 @@ disabled_wait: .globl consume_sclp_int consume_sclp_int: /* enable service interrupts in cr0 */ - stctg 0,0,0(15) - oi 6(15), 0x2 - lctlg 0,0,0(15) + stctg %c0,%c0,0(%r15) + oi 6(%r15),0x2 + lctlg %c0,%c0,0(%r15) /* prepare external call handler */ larl %r1, external_new_code stg %r1, 0x1b8 @@ -73,10 +73,10 @@ consume_sclp_int: external_new_code: /* disable service interrupts in cr0 */ - stctg 0,0,0(15) - ni 6(15), 0xfd - lctlg 0,0,0(15) - br 14 + stctg %c0,%c0,0(%r15) + ni 6(%r15),0xfd + lctlg %c0,%c0,0(%r15) + br %r14 .align 8 disabled_wait_psw: diff --git a/pc-bios/skiboot.lid b/pc-bios/skiboot.lid index 70612962c6..6d5966c3ae 100644 Binary files a/pc-bios/skiboot.lid and b/pc-bios/skiboot.lid differ diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin index 4e0e33f829..65db1a6e57 100644 Binary files a/pc-bios/slof.bin and b/pc-bios/slof.bin differ diff --git a/pc-bios/u-boot-sam460-20100605.bin b/pc-bios/u-boot-sam460-20100605.bin old mode 100755 new mode 100644 diff --git a/pc-bios/u-boot.e500 b/pc-bios/u-boot.e500 old mode 100755 new mode 100644 index 25537f8fe3..732660f348 Binary files a/pc-bios/u-boot.e500 and b/pc-bios/u-boot.e500 differ diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin index 6021d9b199..dd8a1f7e66 100644 Binary files a/pc-bios/vgabios-bochs-display.bin and b/pc-bios/vgabios-bochs-display.bin differ diff --git a/pc-bios/vgabios-cirrus.bin b/pc-bios/vgabios-cirrus.bin index c1ec6b6298..66b2adc5ff 100644 Binary files a/pc-bios/vgabios-cirrus.bin and b/pc-bios/vgabios-cirrus.bin differ diff --git a/pc-bios/vgabios-qxl.bin b/pc-bios/vgabios-qxl.bin index 2529ac954d..96aab241be 100644 Binary files a/pc-bios/vgabios-qxl.bin and b/pc-bios/vgabios-qxl.bin differ diff --git a/pc-bios/vgabios-ramfb.bin b/pc-bios/vgabios-ramfb.bin index 30a124538f..04eb20ed89 100644 Binary files a/pc-bios/vgabios-ramfb.bin and b/pc-bios/vgabios-ramfb.bin differ diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index 2d868321c7..8559c4c562 100644 Binary files a/pc-bios/vgabios-stdvga.bin and b/pc-bios/vgabios-stdvga.bin differ diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index 8188eabb18..4e274c1e83 100644 Binary files a/pc-bios/vgabios-virtio.bin and b/pc-bios/vgabios-virtio.bin differ diff --git a/pc-bios/vgabios-vmware.bin b/pc-bios/vgabios-vmware.bin index 58afa79d2f..c6df59fb67 100644 Binary files a/pc-bios/vgabios-vmware.bin and b/pc-bios/vgabios-vmware.bin differ diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin index 136c94520c..6bf8e76874 100644 Binary files a/pc-bios/vgabios.bin and b/pc-bios/vgabios.bin differ diff --git a/po/Makefile b/po/Makefile index e47e262ee6..c041f4c858 100644 --- a/po/Makefile +++ b/po/Makefile @@ -36,7 +36,7 @@ clean: install: $(OBJS) for obj in $(OBJS); do \ - base=`basename $$obj .mo`; \ + base=$$(basename $$obj .mo); \ $(INSTALL) -d $(DESTDIR)$(prefix)/share/locale/$$base/LC_MESSAGES; \ $(INSTALL) -m644 $$obj $(DESTDIR)$(prefix)/share/locale/$$base/LC_MESSAGES/qemu.mo; \ done diff --git a/scripts/qemu.py b/python/qemu/__init__.py similarity index 83% rename from scripts/qemu.py rename to python/qemu/__init__.py index f099ce7278..fd144c0006 100644 --- a/scripts/qemu.py +++ b/python/qemu/__init__.py @@ -16,15 +16,30 @@ import errno import logging import os import subprocess -import qmp.qmp import re import shutil import socket import tempfile +from . import qmp + LOG = logging.getLogger(__name__) +# Mapping host architecture to any additional architectures it can +# support which often includes its 32 bit cousin. +ADDITIONAL_ARCHES = { + "x86_64" : "i386", + "aarch64" : "armhf" +} + +def kvm_available(target_arch=None): + host_arch = os.uname()[4] + if target_arch and target_arch != host_arch: + if target_arch != ADDITIONAL_ARCHES.get(host_arch): + return False + return os.access("/dev/kvm", os.R_OK | os.W_OK) + #: Maps machine types to the preferred console device types CONSOLE_DEV_TYPES = { @@ -52,10 +67,10 @@ class QEMUMachineAddDeviceError(QEMUMachineError): failures reported by the QEMU binary itself. """ -class MonitorResponseError(qmp.qmp.QMPError): - ''' +class MonitorResponseError(qmp.QMPError): + """ Represents erroneous QMP monitor reply - ''' + """ def __init__(self, reply): try: desc = reply["error"]["desc"] @@ -66,14 +81,15 @@ class MonitorResponseError(qmp.qmp.QMPError): class QEMUMachine(object): - '''A QEMU VM + """ + A QEMU VM Use this object as a context manager to ensure the QEMU process terminates:: with VM(binary) as vm: ... # vm is guaranteed to be shut down here - ''' + """ def __init__(self, binary, args=None, wrapper=None, name=None, test_dir="/var/tmp", monitor_address=None, @@ -87,7 +103,7 @@ class QEMUMachine(object): @param name: prefix for socket and log file names (default: qemu-PID) @param test_dir: where to create socket and log file @param monitor_address: address for QMP monitor - @param socket_scm_helper: helper program, required for send_fd_scm()" + @param socket_scm_helper: helper program, required for send_fd_scm() @note: Qemu process is not started until launch() is used. ''' if args is None: @@ -129,24 +145,33 @@ class QEMUMachine(object): return False # This can be used to add an unused monitor instance. - def add_monitor_telnet(self, ip, port): - args = 'tcp:%s:%d,server,nowait,telnet' % (ip, port) + def add_monitor_null(self): self._args.append('-monitor') - self._args.append(args) + self._args.append('null') def add_fd(self, fd, fdset, opaque, opts=''): - '''Pass a file descriptor to the VM''' + """ + Pass a file descriptor to the VM + """ options = ['fd=%d' % fd, 'set=%d' % fdset, 'opaque=%s' % opaque] if opts: options.append(opts) + # This did not exist before 3.4, but since then it is + # mandatory for our purpose + if hasattr(os, 'set_inheritable'): + os.set_inheritable(fd, True) + self._args.append('-add-fd') self._args.append(','.join(options)) return self - def send_fd_scm(self, fd_file_path): + # Exactly one of fd and file_path must be given. + # (If it is file_path, the helper will open that file and pass its + # own fd) + def send_fd_scm(self, fd=None, file_path=None): # In iotest.py, the qmp should always use unix socket. assert self._qmp.is_scm_available() if self._socket_scm_helper is None: @@ -154,12 +179,27 @@ class QEMUMachine(object): if not os.path.exists(self._socket_scm_helper): raise QEMUMachineError("%s does not exist" % self._socket_scm_helper) + + # This did not exist before 3.4, but since then it is + # mandatory for our purpose + if hasattr(os, 'set_inheritable'): + os.set_inheritable(self._qmp.get_sock_fd(), True) + if fd is not None: + os.set_inheritable(fd, True) + fd_param = ["%s" % self._socket_scm_helper, - "%d" % self._qmp.get_sock_fd(), - "%s" % fd_file_path] + "%d" % self._qmp.get_sock_fd()] + + if file_path is not None: + assert fd is None + fd_param.append(file_path) + else: + assert fd is not None + fd_param.append(str(fd)) + devnull = open(os.path.devnull, 'rb') proc = subprocess.Popen(fd_param, stdin=devnull, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, close_fds=False) output = proc.communicate()[0] if output: LOG.debug(output) @@ -168,7 +208,9 @@ class QEMUMachine(object): @staticmethod def _remove_if_exists(path): - '''Remove file object at path if it exists''' + """ + Remove file object at path if it exists + """ try: os.remove(path) except OSError as exception: @@ -225,8 +267,8 @@ class QEMUMachine(object): self._qemu_log_path = os.path.join(self._temp_dir, self._name + ".log") self._qemu_log_file = open(self._qemu_log_path, 'wb') - self._qmp = qmp.qmp.QEMUMonitorProtocol(self._vm_monitor, - server=True) + self._qmp = qmp.QEMUMonitorProtocol(self._vm_monitor, + server=True) def _post_launch(self): self._qmp.accept() @@ -271,27 +313,35 @@ class QEMUMachine(object): raise def _launch(self): - '''Launch the VM and establish a QMP connection''' + """ + Launch the VM and establish a QMP connection + """ devnull = open(os.path.devnull, 'rb') self._pre_launch() self._qemu_full_args = (self._wrapper + [self._binary] + self._base_args() + self._args) + LOG.debug('VM launch command: %r', ' '.join(self._qemu_full_args)) self._popen = subprocess.Popen(self._qemu_full_args, stdin=devnull, stdout=self._qemu_log_file, stderr=subprocess.STDOUT, - shell=False) + shell=False, + close_fds=False) self._post_launch() def wait(self): - '''Wait for the VM to power off''' + """ + Wait for the VM to power off + """ self._popen.wait() self._qmp.close() self._load_io_log() self._post_shutdown() def shutdown(self): - '''Terminate the VM and clean up''' + """ + Terminate the VM and clean up + """ if self.is_running(): try: self._qmp.cmd('quit') @@ -310,12 +360,14 @@ class QEMUMachine(object): command = ' '.join(self._qemu_full_args) else: command = '' - LOG.warn(msg, exitcode, command) + LOG.warn(msg, -exitcode, command) self._launched = False def qmp(self, cmd, conv_keys=True, **args): - '''Invoke a QMP command and return the response dict''' + """ + Invoke a QMP command and return the response dict + """ qmp_args = dict() for key, value in args.items(): if conv_keys: @@ -326,26 +378,30 @@ class QEMUMachine(object): return self._qmp.cmd(cmd, args=qmp_args) def command(self, cmd, conv_keys=True, **args): - ''' + """ Invoke a QMP command. On success return the response dict. On failure raise an exception. - ''' + """ reply = self.qmp(cmd, conv_keys, **args) if reply is None: - raise qmp.qmp.QMPError("Monitor is closed") + raise qmp.QMPError("Monitor is closed") if "error" in reply: raise MonitorResponseError(reply) return reply["return"] def get_qmp_event(self, wait=False): - '''Poll for one queued QMP events and return it''' + """ + Poll for one queued QMP events and return it + """ if len(self._events) > 0: return self._events.pop(0) return self._qmp.pull_event(wait=wait) def get_qmp_events(self, wait=False): - '''Poll for queued QMP events and return a list of dicts''' + """ + Poll for queued QMP events and return a list of dicts + """ events = self._qmp.get_events(wait=wait) events.extend(self._events) del self._events[:] @@ -353,7 +409,7 @@ class QEMUMachine(object): return events def event_wait(self, name, timeout=60.0, match=None): - ''' + """ Wait for specified timeout on named event in QMP; optionally filter results by match. @@ -361,7 +417,7 @@ class QEMUMachine(object): branch processing on match's value None {"foo": {"bar": 1}} matches {"foo": None} {"foo": {"bar": 1}} does not matches {"foo": {"baz": None}} - ''' + """ def event_match(event, match=None): if match is None: return True @@ -394,29 +450,29 @@ class QEMUMachine(object): return None def get_log(self): - ''' + """ After self.shutdown or failed qemu execution, this returns the output of the qemu process. - ''' + """ return self._iolog def add_args(self, *args): - ''' + """ Adds to the list of extra arguments to be given to the QEMU binary - ''' + """ self._args.extend(args) def set_machine(self, machine_type): - ''' + """ Sets the machine type If set, the machine type will be added to the base arguments of the resulting QEMU command line. - ''' + """ self._machine = machine_type def set_console(self, device_type=None): - ''' + """ Sets the device type for a console device If set, the console device and a backing character device will @@ -434,7 +490,7 @@ class QEMUMachine(object): @param device_type: the device type, such as "isa-serial" @raises: QEMUMachineAddDeviceError if the device type is not given and can not be determined. - ''' + """ if device_type is None: if self._machine is None: raise QEMUMachineAddDeviceError("Can not add a console device:" diff --git a/scripts/qmp/qmp.py b/python/qemu/qmp.py similarity index 100% rename from scripts/qmp/qmp.py rename to python/qemu/qmp.py diff --git a/scripts/qtest.py b/python/qemu/qtest.py similarity index 89% rename from scripts/qtest.py rename to python/qemu/qtest.py index df0daf26ca..eb45824dd0 100644 --- a/scripts/qtest.py +++ b/python/qemu/qtest.py @@ -13,7 +13,8 @@ import socket import os -import qemu + +from . import QEMUMachine class QEMUQtestProtocol(object): @@ -31,6 +32,7 @@ class QEMUQtestProtocol(object): """ self._address = address self._sock = self._get_sock() + self._sockfile = None if server: self._sock.bind(self._address) self._sock.listen(1) @@ -49,6 +51,7 @@ class QEMUQtestProtocol(object): @raise socket.error on socket connection errors """ self._sock.connect(self._address) + self._sockfile = self._sock.makefile() def accept(self): """ @@ -57,6 +60,7 @@ class QEMUQtestProtocol(object): @raise socket.error on socket connection errors """ self._sock, _ = self._sock.accept() + self._sockfile = self._sock.makefile() def cmd(self, qtest_cmd): """ @@ -64,16 +68,19 @@ class QEMUQtestProtocol(object): @param qtest_cmd: qtest command text to be sent """ - self._sock.sendall(qtest_cmd + "\n") + self._sock.sendall((qtest_cmd + "\n").encode('utf-8')) + resp = self._sockfile.readline() + return resp def close(self): self._sock.close() + self._sockfile.close() def settimeout(self, timeout): self._sock.settimeout(timeout) -class QEMUQtestMachine(qemu.QEMUMachine): +class QEMUQtestMachine(QEMUMachine): '''A QEMU VM''' def __init__(self, binary, args=None, name=None, test_dir="/var/tmp", diff --git a/qapi/Makefile.objs b/qapi/Makefile.objs index 33906ff321..729e5185c5 100644 --- a/qapi/Makefile.objs +++ b/qapi/Makefile.objs @@ -4,3 +4,28 @@ util-obj-y += string-input-visitor.o string-output-visitor.o util-obj-y += opts-visitor.o qapi-clone-visitor.o util-obj-y += qmp-event.o util-obj-y += qapi-util.o + +QAPI_COMMON_MODULES = audio authz block-core block char common crypto +QAPI_COMMON_MODULES += introspect job migration misc net rdma rocker +QAPI_COMMON_MODULES += run-state sockets tpm trace transaction ui +QAPI_TARGET_MODULES = target +QAPI_MODULES = $(QAPI_COMMON_MODULES) $(QAPI_TARGET_MODULES) + +util-obj-y += qapi-builtin-types.o +util-obj-y += $(QAPI_COMMON_MODULES:%=qapi-types-%.o) +util-obj-y += qapi-builtin-visit.o +util-obj-y += $(QAPI_COMMON_MODULES:%=qapi-visit-%.o) +util-obj-y += qapi-emit-events.o +util-obj-y += $(QAPI_COMMON_MODULES:%=qapi-events-%.o) + +common-obj-y = $(QAPI_COMMON_MODULES:%=qapi-commands-%.o) + +obj-y = qapi-introspect.o +obj-y += $(QAPI_TARGET_MODULES:%=qapi-types-%.o) +obj-y += qapi-types.o +obj-y += $(QAPI_TARGET_MODULES:%=qapi-visit-%.o) +obj-y += qapi-visit.o +obj-y += $(QAPI_TARGET_MODULES:%=qapi-events-%.o) +obj-y += qapi-events.o +obj-y += $(QAPI_TARGET_MODULES:%=qapi-commands-%.o) +obj-y += qapi-commands.o diff --git a/qapi/audio.json b/qapi/audio.json new file mode 100644 index 0000000000..9fefdf5186 --- /dev/null +++ b/qapi/audio.json @@ -0,0 +1,308 @@ +# -*- mode: python -*- +# +# Copyright (C) 2015-2019 Zoltán Kővágó +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. + +## +# @AudiodevPerDirectionOptions: +# +# General audio backend options that are used for both playback and +# recording. +# +# @fixed-settings: use fixed settings for host input/output. When off, +# frequency, channels and format must not be +# specified (default true) +# +# @frequency: frequency to use when using fixed settings +# (default 44100) +# +# @channels: number of channels when using fixed settings (default 2) +# +# @voices: number of voices to use (default 1) +# +# @format: sample format to use when using fixed settings +# (default s16) +# +# @buffer-length: the buffer length in microseconds +# +# Since: 4.0 +## +{ 'struct': 'AudiodevPerDirectionOptions', + 'data': { + '*fixed-settings': 'bool', + '*frequency': 'uint32', + '*channels': 'uint32', + '*voices': 'uint32', + '*format': 'AudioFormat', + '*buffer-length': 'uint32' } } + +## +# @AudiodevGenericOptions: +# +# Generic driver-specific options. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 4.0 +## +{ 'struct': 'AudiodevGenericOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions' } } + +## +# @AudiodevAlsaPerDirectionOptions: +# +# Options of the ALSA backend that are used for both playback and +# recording. +# +# @dev: the name of the ALSA device to use (default 'default') +# +# @period-length: the period length in microseconds +# +# @try-poll: attempt to use poll mode, falling back to non-polling +# access on failure (default true) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevAlsaPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*dev': 'str', + '*period-length': 'uint32', + '*try-poll': 'bool' } } + +## +# @AudiodevAlsaOptions: +# +# Options of the ALSA audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @threshold: set the threshold (in microseconds) when playback starts +# +# Since: 4.0 +## +{ 'struct': 'AudiodevAlsaOptions', + 'data': { + '*in': 'AudiodevAlsaPerDirectionOptions', + '*out': 'AudiodevAlsaPerDirectionOptions', + '*threshold': 'uint32' } } + +## +# @AudiodevCoreaudioPerDirectionOptions: +# +# Options of the Core Audio backend that are used for both playback and +# recording. +# +# @buffer-count: number of buffers +# +# Since: 4.0 +## +{ 'struct': 'AudiodevCoreaudioPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*buffer-count': 'uint32' } } + +## +# @AudiodevCoreaudioOptions: +# +# Options of the coreaudio audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# Since: 4.0 +## +{ 'struct': 'AudiodevCoreaudioOptions', + 'data': { + '*in': 'AudiodevCoreaudioPerDirectionOptions', + '*out': 'AudiodevCoreaudioPerDirectionOptions' } } + +## +# @AudiodevDsoundOptions: +# +# Options of the DirectSound audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @latency: add extra latency to playback in microseconds +# (default 10000) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevDsoundOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*latency': 'uint32' } } + +## +# @AudiodevOssPerDirectionOptions: +# +# Options of the OSS backend that are used for both playback and +# recording. +# +# @dev: file name of the OSS device (default '/dev/dsp') +# +# @buffer-count: number of buffers +# +# @try-poll: attempt to use poll mode, falling back to non-polling +# access on failure (default true) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevOssPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*dev': 'str', + '*buffer-count': 'uint32', + '*try-poll': 'bool' } } + +## +# @AudiodevOssOptions: +# +# Options of the OSS audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @try-mmap: try using memory-mapped access, falling back to +# non-memory-mapped access on failure (default true) +# +# @exclusive: open device in exclusive mode (vmix won't work) +# (default false) +# +# @dsp-policy: set the timing policy of the device (between 0 and 10, +# where smaller number means smaller latency but higher +# CPU usage) or -1 to use fragment mode (option ignored +# on some platforms) (default 5) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevOssOptions', + 'data': { + '*in': 'AudiodevOssPerDirectionOptions', + '*out': 'AudiodevOssPerDirectionOptions', + '*try-mmap': 'bool', + '*exclusive': 'bool', + '*dsp-policy': 'uint32' } } + +## +# @AudiodevPaPerDirectionOptions: +# +# Options of the Pulseaudio backend that are used for both playback and +# recording. +# +# @name: name of the sink/source to use +# +# @latency: latency you want PulseAudio to achieve in microseconds +# (default 15000) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevPaPerDirectionOptions', + 'base': 'AudiodevPerDirectionOptions', + 'data': { + '*name': 'str', + '*latency': 'uint32' } } + +## +# @AudiodevPaOptions: +# +# Options of the PulseAudio audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @server: PulseAudio server address (default: let PulseAudio choose) +# +# Since: 4.0 +## +{ 'struct': 'AudiodevPaOptions', + 'data': { + '*in': 'AudiodevPaPerDirectionOptions', + '*out': 'AudiodevPaPerDirectionOptions', + '*server': 'str' } } + +## +# @AudiodevWavOptions: +# +# Options of the wav audio backend. +# +# @in: options of the capture stream +# +# @out: options of the playback stream +# +# @path: name of the wav file to record (default 'qemu.wav') +# +# Since: 4.0 +## +{ 'struct': 'AudiodevWavOptions', + 'data': { + '*in': 'AudiodevPerDirectionOptions', + '*out': 'AudiodevPerDirectionOptions', + '*path': 'str' } } + + +## +# @AudioFormat: +# +# An enumeration of possible audio formats. +# +# Since: 4.0 +## +{ 'enum': 'AudioFormat', + 'data': [ 'u8', 's8', 'u16', 's16', 'u32', 's32' ] } + +## +# @AudiodevDriver: +# +# An enumeration of possible audio backend drivers. +# +# Since: 4.0 +## +{ 'enum': 'AudiodevDriver', + 'data': [ 'none', 'alsa', 'coreaudio', 'dsound', 'oss', 'pa', 'sdl', + 'spice', 'wav' ] } + +## +# @Audiodev: +# +# Options of an audio backend. +# +# @id: identifier of the backend +# +# @driver: the backend driver to use +# +# @timer-period: timer period (in microseconds, 0: use lowest possible) +# +# Since: 4.0 +## +{ 'union': 'Audiodev', + 'base': { + 'id': 'str', + 'driver': 'AudiodevDriver', + '*timer-period': 'uint32' }, + 'discriminator': 'driver', + 'data': { + 'none': 'AudiodevGenericOptions', + 'alsa': 'AudiodevAlsaOptions', + 'coreaudio': 'AudiodevCoreaudioOptions', + 'dsound': 'AudiodevDsoundOptions', + 'oss': 'AudiodevOssOptions', + 'pa': 'AudiodevPaOptions', + 'sdl': 'AudiodevGenericOptions', + 'spice': 'AudiodevGenericOptions', + 'wav': 'AudiodevWavOptions' } } diff --git a/qapi/authz.json b/qapi/authz.json new file mode 100644 index 0000000000..1c836a3abd --- /dev/null +++ b/qapi/authz.json @@ -0,0 +1,58 @@ +# -*- Mode: Python -*- +# +# QAPI authz definitions + +## +# @QAuthZListPolicy: +# +# The authorization policy result +# +# @deny: deny access +# @allow: allow access +# +# Since: 4.0 +## +{ 'enum': 'QAuthZListPolicy', + 'prefix': 'QAUTHZ_LIST_POLICY', + 'data': ['deny', 'allow']} + +## +# @QAuthZListFormat: +# +# The authorization policy match format +# +# @exact: an exact string match +# @glob: string with ? and * shell wildcard support +# +# Since: 4.0 +## +{ 'enum': 'QAuthZListFormat', + 'prefix': 'QAUTHZ_LIST_FORMAT', + 'data': ['exact', 'glob']} + +## +# @QAuthZListRule: +# +# A single authorization rule. +# +# @match: a string or glob to match against a user identity +# @policy: the result to return if @match evaluates to true +# @format: the format of the @match rule (default 'exact') +# +# Since: 4.0 +## +{ 'struct': 'QAuthZListRule', + 'data': {'match': 'str', + 'policy': 'QAuthZListPolicy', + '*format': 'QAuthZListFormat'}} + +## +# @QAuthZListRuleListHack: +# +# Not exposed via QMP; hack to generate QAuthZListRuleList +# for use internally by the code. +# +# Since: 4.0 +## +{ 'struct': 'QAuthZListRuleListHack', + 'data': { 'unused': ['QAuthZListRule'] } } diff --git a/qapi/block-core.json b/qapi/block-core.json index 5b9084a394..7ccbfff9d0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -59,6 +59,13 @@ # # @compat: compatibility level # +# @data-file: the filename of the external data file that is stored in the +# image and used as a default for opening the image (since: 4.0) +# +# @data-file-raw: True if the external data file must stay valid as a +# standalone (read-only) raw image without looking at qcow2 +# metadata (since: 4.0) +# # @lazy-refcounts: on or off; only valid for compat >= 1.1 # # @corrupt: true if the image has been marked corrupt; only valid for @@ -69,15 +76,20 @@ # @encrypt: details about encryption parameters; only set if image # is encrypted (since 2.10) # +# @bitmaps: A list of qcow2 bitmap details (since 4.0) +# # Since: 1.7 ## { 'struct': 'ImageInfoSpecificQCow2', 'data': { 'compat': 'str', + '*data-file': 'str', + '*data-file-raw': 'bool', '*lazy-refcounts': 'bool', '*corrupt': 'bool', 'refcount-bits': 'int', - '*encrypt': 'ImageInfoSpecificQCow2Encryption' + '*encrypt': 'ImageInfoSpecificQCow2Encryption', + '*bitmaps': ['Qcow2BitmapInfo'] } } ## @@ -417,22 +429,37 @@ # # An enumeration of possible states that a dirty bitmap can report to the user. # -# @frozen: The bitmap is currently in-use by a backup operation or block job, -# and is immutable. +# @frozen: The bitmap is currently in-use by some operation and is immutable. +# If the bitmap was @active prior to the operation, new writes by the +# guest are being recorded in a temporary buffer, and will not be lost. +# Generally, bitmaps are cleared on successful use in an operation and +# the temporary buffer is committed into the bitmap. On failure, the +# temporary buffer is merged back into the bitmap without first +# clearing it. +# Please refer to the documentation for each bitmap-using operation, +# See also @blockdev-backup, @drive-backup. # -# @disabled: The bitmap is currently in-use by an internal operation and is -# read-only. It can still be deleted. +# @disabled: The bitmap is not currently recording new writes by the guest. +# This is requested explicitly via @block-dirty-bitmap-disable. +# It can still be cleared, deleted, or used for backup operations. # # @active: The bitmap is actively monitoring for new writes, and can be cleared, # deleted, or used for backup operations. # -# @locked: The bitmap is currently in-use by some operation and can not be -# cleared, deleted, or used for backup operations. (Since 2.12) +# @locked: The bitmap is currently in-use by some operation and is immutable. +# If the bitmap was @active prior to the operation, it is still +# recording new writes. If the bitmap was @disabled, it is not +# recording new writes. (Since 2.12) +# +# @inconsistent: This is a persistent dirty bitmap that was marked in-use on +# disk, and is unusable by QEMU. It can only be deleted. +# Please rely on the inconsistent field in @BlockDirtyInfo +# instead, as the status field is deprecated. (Since 4.0) # # Since: 2.4 ## { 'enum': 'DirtyBitmapStatus', - 'data': ['active', 'disabled', 'frozen', 'locked'] } + 'data': ['active', 'disabled', 'frozen', 'locked', 'inconsistent'] } ## # @BlockDirtyInfo: @@ -445,13 +472,65 @@ # # @granularity: granularity of the dirty bitmap in bytes (since 1.4) # -# @status: current status of the dirty bitmap (since 2.4) +# @status: Deprecated in favor of @recording and @locked. (since 2.4) +# +# @recording: true if the bitmap is recording new writes from the guest. +# Replaces `active` and `disabled` statuses. (since 4.0) +# +# @busy: true if the bitmap is in-use by some operation (NBD or jobs) +# and cannot be modified via QMP or used by another operation. +# Replaces `locked` and `frozen` statuses. (since 4.0) +# +# @persistent: true if the bitmap was stored on disk, is scheduled to be stored +# on disk, or both. (since 4.0) +# +# @inconsistent: true if this is a persistent bitmap that was improperly +# stored. Implies @persistent to be true; @recording and +# @busy to be false. This bitmap cannot be used. To remove +# it, use @block-dirty-bitmap-remove. (Since 4.0) # # Since: 1.3 ## { 'struct': 'BlockDirtyInfo', 'data': {'*name': 'str', 'count': 'int', 'granularity': 'uint32', - 'status': 'DirtyBitmapStatus'} } + 'recording': 'bool', 'busy': 'bool', 'status': 'DirtyBitmapStatus', + 'persistent': 'bool', '*inconsistent': 'bool' } } + +## +# @Qcow2BitmapInfoFlags: +# +# An enumeration of flags that a bitmap can report to the user. +# +# @in-use: This flag is set by any process actively modifying the qcow2 file, +# and cleared when the updated bitmap is flushed to the qcow2 image. +# The presence of this flag in an offline image means that the bitmap +# was not saved correctly after its last usage, and may contain +# inconsistent data. +# +# @auto: The bitmap must reflect all changes of the virtual disk by any +# application that would write to this qcow2 file. +# +# Since: 4.0 +## +{ 'enum': 'Qcow2BitmapInfoFlags', + 'data': ['in-use', 'auto'] } + +## +# @Qcow2BitmapInfo: +# +# Qcow2 bitmap information. +# +# @name: the name of the bitmap +# +# @granularity: granularity of the bitmap in bytes +# +# @flags: flags of the bitmap +# +# Since: 4.0 +## +{ 'struct': 'Qcow2BitmapInfo', + 'data': {'name': 'str', 'granularity': 'uint32', + 'flags': ['Qcow2BitmapInfoFlags'] } } ## # @BlockLatencyHistogramInfo: @@ -476,20 +555,20 @@ # +------------------ # 10 50 100 # -# Since: 2.12 +# Since: 4.0 ## { 'struct': 'BlockLatencyHistogramInfo', 'data': {'boundaries': ['uint64'], 'bins': ['uint64'] } } ## -# @x-block-latency-histogram-set: +# @block-latency-histogram-set: # # Manage read, write and flush latency histograms for the device. # -# If only @device parameter is specified, remove all present latency histograms +# If only @id parameter is specified, remove all present latency histograms # for the device. Otherwise, add/reset some of (or all) latency histograms. # -# @device: device name to set latency histogram for. +# @id: The name or QOM path of the guest device. # # @boundaries: list of interval boundary values (see description in # BlockLatencyHistogramInfo definition). If specified, all @@ -512,13 +591,13 @@ # # Returns: error if device is not found or any boundary arrays are invalid. # -# Since: 2.12 +# Since: 4.0 # # Example: set new histograms for all io types with intervals # [0, 10), [10, 50), [50, 100), [100, +inf): # # -> { "execute": "block-latency-histogram-set", -# "arguments": { "device": "drive0", +# "arguments": { "id": "drive0", # "boundaries": [10, 50, 100] } } # <- { "return": {} } # @@ -526,7 +605,7 @@ # not changed (or not created): # # -> { "execute": "block-latency-histogram-set", -# "arguments": { "device": "drive0", +# "arguments": { "id": "drive0", # "boundaries-write": [10, 50, 100] } } # <- { "return": {} } # @@ -535,7 +614,7 @@ # write: [0, 1000), [1000, 5000), [5000, +inf) # # -> { "execute": "block-latency-histogram-set", -# "arguments": { "device": "drive0", +# "arguments": { "id": "drive0", # "boundaries": [10, 50, 100], # "boundaries-write": [1000, 5000] } } # <- { "return": {} } @@ -543,11 +622,11 @@ # Example: remove all latency histograms: # # -> { "execute": "block-latency-histogram-set", -# "arguments": { "device": "drive0" } } +# "arguments": { "id": "drive0" } } # <- { "return": {} } ## -{ 'command': 'x-block-latency-histogram-set', - 'data': {'device': 'str', +{ 'command': 'block-latency-histogram-set', + 'data': {'id': 'str', '*boundaries': ['uint64'], '*boundaries-read': ['uint64'], '*boundaries-write': ['uint64'], @@ -833,11 +912,11 @@ # @timed_stats: Statistics specific to the set of previously defined # intervals of time (Since 2.5) # -# @x_rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12) +# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) # -# @x_wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12) +# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) # -# @x_flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 2.12) +# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) # # Since: 0.14.0 ## @@ -852,9 +931,9 @@ 'invalid_wr_operations': 'int', 'invalid_flush_operations': 'int', 'account_invalid': 'bool', 'account_failed': 'bool', 'timed_stats': ['BlockDeviceTimedStats'], - '*x_rd_latency_histogram': 'BlockLatencyHistogramInfo', - '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo', - '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } } + '*rd_latency_histogram': 'BlockLatencyHistogramInfo', + '*wr_latency_histogram': 'BlockLatencyHistogramInfo', + '*flush_latency_histogram': 'BlockLatencyHistogramInfo' } } ## # @BlockStats: @@ -1143,8 +1222,10 @@ # This command is now obsolete and will always return an error since 2.10 # ## -{ 'command': 'block_passwd', 'data': {'*device': 'str', - '*node-name': 'str', 'password': 'str'} } +{ 'command': 'block_passwd', + 'data': { '*device': 'str', + '*node-name': 'str', + 'password': 'str' } } ## # @block_resize: @@ -1171,9 +1252,10 @@ # <- { "return": {} } # ## -{ 'command': 'block_resize', 'data': { '*device': 'str', - '*node-name': 'str', - 'size': 'int' }} +{ 'command': 'block_resize', + 'data': { '*device': 'str', + '*node-name': 'str', + 'size': 'int' } } ## # @NewImageMode: @@ -1272,13 +1354,14 @@ # a different block device than @device). # # @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize. -# When true, this job will automatically perform its abort or -# commit actions. +# finished its work, waiting for @block-job-finalize before +# making any block graph changes. +# When true, this job will automatically +# perform its abort or commit actions. # Defaults to true. (Since 2.12) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completed ceased all work, and wait for @block-job-dismiss. +# has completely ceased all work, and awaits @block-job-dismiss. # When true, this job will automatically disappear from the query # list without user intervention. # Defaults to true. (Since 2.12) @@ -1315,6 +1398,10 @@ # @speed: the maximum speed, in bytes per second. The default is 0, # for unlimited. # +# @bitmap: the name of dirty bitmap if sync is "incremental". +# Must be present if sync is "incremental", must NOT be present +# otherwise. (Since 3.1) +# # @compress: true to compress data, if the target format supports it. # (default: false) (since 2.8) # @@ -1327,13 +1414,14 @@ # a different block device than @device). # # @auto-finalize: When false, this job will wait in a PENDING state after it has -# finished its work, waiting for @block-job-finalize. -# When true, this job will automatically perform its abort or -# commit actions. +# finished its work, waiting for @block-job-finalize before +# making any block graph changes. +# When true, this job will automatically +# perform its abort or commit actions. # Defaults to true. (Since 2.12) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state after it -# has completed ceased all work, and wait for @block-job-dismiss. +# has completely ceased all work, and awaits @block-job-dismiss. # When true, this job will automatically disappear from the query # list without user intervention. # Defaults to true. (Since 2.12) @@ -1346,7 +1434,8 @@ ## { 'struct': 'BlockdevBackup', 'data': { '*job-id': 'str', 'device': 'str', 'target': 'str', - 'sync': 'MirrorSyncMode', '*speed': 'int', '*compress': 'bool', + 'sync': 'MirrorSyncMode', '*speed': 'int', + '*bitmap': 'str', '*compress': 'bool', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } @@ -1455,12 +1544,23 @@ # # @device: the device name or node-name of a root node # -# @base: The file name of the backing image to write data into. -# If not specified, this is the deepest backing image. +# @base-node: The node name of the backing image to write data into. +# If not specified, this is the deepest backing image. +# (since: 3.1) # -# @top: The file name of the backing image within the image chain, -# which contains the topmost data to be committed down. If -# not specified, this is the active layer. +# @base: Same as @base-node, except that it is a file name rather than a node +# name. This must be the exact filename string that was used to open the +# node; other strings, even if addressing the same file, are not +# accepted (deprecated, use @base-node instead) +# +# @top-node: The node name of the backing image within the image chain +# which contains the topmost data to be committed down. If +# not specified, this is the active layer. (since: 3.1) +# +# @top: Same as @top-node, except that it is a file name rather than a node +# name. This must be the exact filename string that was used to open the +# node; other strings, even if addressing the same file, are not +# accepted (deprecated, use @base-node instead) # # @backing-file: The backing file string to write into the overlay # image of 'top'. If 'top' is the active layer, @@ -1498,12 +1598,22 @@ # above @top. If this option is not given, a node name is # autogenerated. (Since: 2.9) # +# @auto-finalize: When false, this job will wait in a PENDING state after it has +# finished its work, waiting for @block-job-finalize before +# making any block graph changes. +# When true, this job will automatically +# perform its abort or commit actions. +# Defaults to true. (Since 3.1) +# +# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it +# has completely ceased all work, and awaits @block-job-dismiss. +# When true, this job will automatically disappear from the query +# list without user intervention. +# Defaults to true. (Since 3.1) +# # Returns: Nothing on success -# If commit or stream is already active on this device, DeviceInUse # If @device does not exist, DeviceNotFound -# If image commit is not supported by this device, NotSupported -# If @base or @top is invalid, a generic error is returned -# If @speed is invalid, InvalidParameter +# Any other error returns a GenericError. # # Since: 1.3 # @@ -1516,9 +1626,11 @@ # ## { 'command': 'block-commit', - 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*top': 'str', + 'data': { '*job-id': 'str', 'device': 'str', '*base-node': 'str', + '*base': 'str', '*top-node': 'str', '*top': 'str', '*backing-file': 'str', '*speed': 'int', - '*filter-node-name': 'str' } } + '*filter-node-name': 'str', + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } ## # @drive-backup: @@ -1632,6 +1744,114 @@ ## { 'command': 'query-named-block-nodes', 'returns': [ 'BlockDeviceInfo' ] } +## +# @XDbgBlockGraphNodeType: +# +# @block-backend: corresponds to BlockBackend +# +# @block-job: corresonds to BlockJob +# +# @block-driver: corresponds to BlockDriverState +# +# Since: 4.0 +## +{ 'enum': 'XDbgBlockGraphNodeType', + 'data': [ 'block-backend', 'block-job', 'block-driver' ] } + +## +# @XDbgBlockGraphNode: +# +# @id: Block graph node identifier. This @id is generated only for +# x-debug-query-block-graph and does not relate to any other identifiers in +# Qemu. +# +# @type: Type of graph node. Can be one of block-backend, block-job or +# block-driver-state. +# +# @name: Human readable name of the node. Corresponds to node-name for +# block-driver-state nodes; is not guaranteed to be unique in the whole +# graph (with block-jobs and block-backends). +# +# Since: 4.0 +## +{ 'struct': 'XDbgBlockGraphNode', + 'data': { 'id': 'uint64', 'type': 'XDbgBlockGraphNodeType', 'name': 'str' } } + +## +# @BlockPermission: +# +# Enum of base block permissions. +# +# @consistent-read: A user that has the "permission" of consistent reads is +# guaranteed that their view of the contents of the block +# device is complete and self-consistent, representing the +# contents of a disk at a specific point. +# For most block devices (including their backing files) this +# is true, but the property cannot be maintained in a few +# situations like for intermediate nodes of a commit block +# job. +# +# @write: This permission is required to change the visible disk contents. +# +# @write-unchanged: This permission (which is weaker than BLK_PERM_WRITE) is +# both enough and required for writes to the block node when +# the caller promises that the visible disk content doesn't +# change. +# As the BLK_PERM_WRITE permission is strictly stronger, +# either is sufficient to perform an unchanging write. +# +# @resize: This permission is required to change the size of a block node. +# +# @graph-mod: This permission is required to change the node that this +# BdrvChild points to. +# +# Since: 4.0 +## + { 'enum': 'BlockPermission', + 'data': [ 'consistent-read', 'write', 'write-unchanged', 'resize', + 'graph-mod' ] } +## +# @XDbgBlockGraphEdge: +# +# Block Graph edge description for x-debug-query-block-graph. +# +# @parent: parent id +# +# @child: child id +# +# @name: name of the relation (examples are 'file' and 'backing') +# +# @perm: granted permissions for the parent operating on the child +# +# @shared-perm: permissions that can still be granted to other users of the +# child while it is still attached to this parent +# +# Since: 4.0 +## +{ 'struct': 'XDbgBlockGraphEdge', + 'data': { 'parent': 'uint64', 'child': 'uint64', + 'name': 'str', 'perm': [ 'BlockPermission' ], + 'shared-perm': [ 'BlockPermission' ] } } + +## +# @XDbgBlockGraph: +# +# Block Graph - list of nodes and list of edges. +# +# Since: 4.0 +## +{ 'struct': 'XDbgBlockGraph', + 'data': { 'nodes': ['XDbgBlockGraphNode'], 'edges': ['XDbgBlockGraphEdge'] } } + +## +# @x-debug-query-block-graph: +# +# Get the block graph. +# +# Since: 4.0 +## +{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph' } + ## # @drive-mirror: # @@ -1718,6 +1938,18 @@ # @copy-mode: when to copy data to the destination; defaults to 'background' # (Since: 3.0) # +# @auto-finalize: When false, this job will wait in a PENDING state after it has +# finished its work, waiting for @block-job-finalize before +# making any block graph changes. +# When true, this job will automatically +# perform its abort or commit actions. +# Defaults to true. (Since 3.1) +# +# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it +# has completely ceased all work, and awaits @block-job-dismiss. +# When true, this job will automatically disappear from the query +# list without user intervention. +# Defaults to true. (Since 3.1) # Since: 1.3 ## { 'struct': 'DriveMirror', @@ -1727,7 +1959,8 @@ '*speed': 'int', '*granularity': 'uint32', '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', - '*unmap': 'bool', '*copy-mode': 'MirrorCopyMode' } } + '*unmap': 'bool', '*copy-mode': 'MirrorCopyMode', + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } ## # @BlockDirtyBitmap: @@ -1760,29 +1993,29 @@ # Currently, all dirty tracking bitmaps are loaded from Qcow2 on # open. # -# @x-disabled: the bitmap is created in the disabled state, which means that -# it will not track drive changes. The bitmap may be enabled with -# x-block-dirty-bitmap-enable. Default is false. (Since: 3.0) +# @disabled: the bitmap is created in the disabled state, which means that +# it will not track drive changes. The bitmap may be enabled with +# block-dirty-bitmap-enable. Default is false. (Since: 4.0) # # Since: 2.4 ## { 'struct': 'BlockDirtyBitmapAdd', 'data': { 'node': 'str', 'name': 'str', '*granularity': 'uint32', - '*persistent': 'bool', '*autoload': 'bool', '*x-disabled': 'bool' } } + '*persistent': 'bool', '*autoload': 'bool', '*disabled': 'bool' } } ## # @BlockDirtyBitmapMerge: # # @node: name of device/node which the bitmap is tracking # -# @dst_name: name of the destination dirty bitmap +# @target: name of the destination dirty bitmap # -# @src_name: name of the source dirty bitmap +# @bitmaps: name(s) of the source dirty bitmap(s) # -# Since: 3.0 +# Since: 4.0 ## { 'struct': 'BlockDirtyBitmapMerge', - 'data': { 'node': 'str', 'dst_name': 'str', 'src_name': 'str' } } + 'data': { 'node': 'str', 'target': 'str', 'bitmaps': ['str'] } } ## # @block-dirty-bitmap-add: @@ -1853,7 +2086,7 @@ 'data': 'BlockDirtyBitmap' } ## -# @x-block-dirty-bitmap-enable: +# @block-dirty-bitmap-enable: # # Enables a dirty bitmap so that it will begin tracking disk changes. # @@ -1861,20 +2094,20 @@ # If @node is not a valid block device, DeviceNotFound # If @name is not found, GenericError with an explanation # -# Since: 3.0 +# Since: 4.0 # # Example: # -# -> { "execute": "x-block-dirty-bitmap-enable", +# -> { "execute": "block-dirty-bitmap-enable", # "arguments": { "node": "drive0", "name": "bitmap0" } } # <- { "return": {} } # ## - { 'command': 'x-block-dirty-bitmap-enable', + { 'command': 'block-dirty-bitmap-enable', 'data': 'BlockDirtyBitmap' } ## -# @x-block-dirty-bitmap-disable: +# @block-dirty-bitmap-disable: # # Disables a dirty bitmap so that it will stop tracking disk changes. # @@ -1882,40 +2115,48 @@ # If @node is not a valid block device, DeviceNotFound # If @name is not found, GenericError with an explanation # -# Since: 3.0 +# Since: 4.0 # # Example: # -# -> { "execute": "x-block-dirty-bitmap-disable", +# -> { "execute": "block-dirty-bitmap-disable", # "arguments": { "node": "drive0", "name": "bitmap0" } } # <- { "return": {} } # ## - { 'command': 'x-block-dirty-bitmap-disable', + { 'command': 'block-dirty-bitmap-disable', 'data': 'BlockDirtyBitmap' } ## -# @x-block-dirty-bitmap-merge: +# @block-dirty-bitmap-merge: # -# Merge @src_name dirty bitmap to @dst_name dirty bitmap. @src_name dirty -# bitmap is unchanged. On error, @dst_name is unchanged. +# Merge dirty bitmaps listed in @bitmaps to the @target dirty bitmap. +# Dirty bitmaps in @bitmaps will be unchanged, except if it also appears +# as the @target bitmap. Any bits already set in @target will still be +# set after the merge, i.e., this operation does not clear the target. +# On error, @target is unchanged. +# +# The resulting bitmap will count as dirty any clusters that were dirty in any +# of the source bitmaps. This can be used to achieve backup checkpoints, or in +# simpler usages, to copy bitmaps. # # Returns: nothing on success # If @node is not a valid block device, DeviceNotFound -# If @dst_name or @src_name is not found, GenericError -# If bitmaps has different sizes or granularities, GenericError +# If any bitmap in @bitmaps or @target is not found, GenericError +# If any of the bitmaps have different sizes or granularities, +# GenericError # -# Since: 3.0 +# Since: 4.0 # # Example: # -# -> { "execute": "x-block-dirty-bitmap-merge", -# "arguments": { "node": "drive0", "dst_name": "bitmap0", -# "src_name": "bitmap1" } } +# -> { "execute": "block-dirty-bitmap-merge", +# "arguments": { "node": "drive0", "target": "bitmap0", +# "bitmaps": ["bitmap1"] } } # <- { "return": {} } # ## - { 'command': 'x-block-dirty-bitmap-merge', + { 'command': 'block-dirty-bitmap-merge', 'data': 'BlockDirtyBitmapMerge' } ## @@ -1933,7 +2174,7 @@ ## # @x-debug-block-dirty-bitmap-sha256: # -# Get bitmap SHA256 +# Get bitmap SHA256. # # Returns: BlockDirtyBitmapSha256 on success # If @node is not a valid block device, DeviceNotFound @@ -1993,6 +2234,18 @@ # @copy-mode: when to copy data to the destination; defaults to 'background' # (Since: 3.0) # +# @auto-finalize: When false, this job will wait in a PENDING state after it has +# finished its work, waiting for @block-job-finalize before +# making any block graph changes. +# When true, this job will automatically +# perform its abort or commit actions. +# Defaults to true. (Since 3.1) +# +# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it +# has completely ceased all work, and awaits @block-job-dismiss. +# When true, this job will automatically disappear from the query +# list without user intervention. +# Defaults to true. (Since 3.1) # Returns: nothing on success. # # Since: 2.6 @@ -2014,7 +2267,8 @@ '*buf-size': 'int', '*on-source-error': 'BlockdevOnError', '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', - '*copy-mode': 'MirrorCopyMode' } } + '*copy-mode': 'MirrorCopyMode', + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } ## # @block_set_io_throttle: @@ -2280,6 +2534,19 @@ # 'stop' and 'enospc' can only be used if the block device # supports io-status (see BlockInfo). Since 1.3. # +# @auto-finalize: When false, this job will wait in a PENDING state after it has +# finished its work, waiting for @block-job-finalize before +# making any block graph changes. +# When true, this job will automatically +# perform its abort or commit actions. +# Defaults to true. (Since 3.1) +# +# @auto-dismiss: When false, this job will wait in a CONCLUDED state after it +# has completely ceased all work, and awaits @block-job-dismiss. +# When true, this job will automatically disappear from the query +# list without user intervention. +# Defaults to true. (Since 3.1) +# # Returns: Nothing on success. If @device does not exist, DeviceNotFound. # # Since: 1.1 @@ -2295,7 +2562,8 @@ { 'command': 'block-stream', 'data': { '*job-id': 'str', 'device': 'str', '*base': 'str', '*base-node': 'str', '*backing-file': 'str', '*speed': 'int', - '*on-error': 'BlockdevOnError' } } + '*on-error': 'BlockdevOnError', + '*auto-finalize': 'bool', '*auto-dismiss': 'bool' } } ## # @block-job-set-speed: @@ -2548,7 +2816,9 @@ 'copy-on-read', 'dmg', 'file', 'ftp', 'ftps', 'gluster', 'host_cdrom', 'host_device', 'http', 'https', 'iscsi', 'luks', 'nbd', 'nfs', 'null-aio', 'null-co', 'nvme', 'parallels', 'qcow', - 'qcow2', 'qed', 'quorum', 'raw', 'rbd', 'replication', 'sheepdog', + 'qcow2', 'qed', 'quorum', 'raw', 'rbd', + { 'name': 'replication', 'if': 'defined(CONFIG_REPLICATION)' }, + 'sheepdog', 'ssh', 'throttle', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat', 'vxhs' ] } ## @@ -2564,6 +2834,10 @@ # @locking: whether to enable file locking. If set to 'auto', only enable # when Open File Descriptor (OFD) locking API is available # (default: auto, since 2.10) +# @drop-cache: invalidate page cache during live migration. This prevents +# stale data on the migration destination with cache.direct=off. +# Currently only supported on Linux hosts. +# (default: on, since: 4.0) # @x-check-cache-dropped: whether to check that page cache was dropped on live # migration. May cause noticeable delays if the image # file is large, do not use in production. @@ -2576,6 +2850,8 @@ '*pr-manager': 'str', '*locking': 'OnOffAuto', '*aio': 'BlockdevAioOptions', + '*drop-cache': {'type': 'bool', + 'if': 'defined(CONFIG_LINUX)'}, '*x-check-cache-dropped': 'bool' } } ## @@ -2774,7 +3050,7 @@ ## # @BlockdevQcow2EncryptionFormat: -# @aes: AES-CBC with plain64 initialization venctors +# @aes: AES-CBC with plain64 initialization vectors # # Since: 2.10 ## @@ -2830,11 +3106,19 @@ # # @cache-clean-interval: 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) +# is 600 on supporting platforms, and 0 on other +# platforms. 0 disables this feature. (since 2.5) +# # @encrypt: Image decryption options. Mandatory for # encrypted images, except when doing a metadata-only # probe of the image. (since 2.10) # +# @data-file: reference to or definition of the external data file. +# This may only be specified for images that require an +# external data file. If it is not specified for such +# an image, the data file name is loaded from the image +# file. (since 4.0) +# # Since: 2.9 ## { 'struct': 'BlockdevOptionsQcow2', @@ -2849,7 +3133,8 @@ '*l2-cache-entry-size': 'int', '*refcount-cache-size': 'int', '*cache-clean-interval': 'int', - '*encrypt': 'BlockdevQcow2Encryption' } } + '*encrypt': 'BlockdevQcow2Encryption', + '*data-file': 'BlockdevRef' } } ## # @SshHostKeyCheckMode: @@ -3303,7 +3588,8 @@ # # Since: 2.9 ## -{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ] } +{ 'enum' : 'ReplicationMode', 'data' : [ 'primary', 'secondary' ], + 'if': 'defined(CONFIG_REPLICATION)' } ## # @BlockdevOptionsReplication: @@ -3321,7 +3607,8 @@ { 'struct': 'BlockdevOptionsReplication', 'base': 'BlockdevOptionsGenericFormat', 'data': { 'mode': 'ReplicationMode', - '*top-id': 'str' } } + '*top-id': 'str' }, + 'if': 'defined(CONFIG_REPLICATION)' } ## # @NFSTransport: @@ -3572,6 +3859,9 @@ # @driver: block driver name # @node-name: the node name of the new node (Since 2.0). # This option is required on the top level of blockdev-add. +# Valid node names start with an alphabetic character and may +# contain only alphanumeric characters, '-', '.' and '_'. Their +# maximum length is 31 characters. # @discard: discard-related options (default: ignore) # @cache: cache-related options # @read-only: whether the block device should be read-only (default: false). @@ -3579,6 +3869,12 @@ # either generally or in certain configurations. In this case, # the default value does not work and the option must be # specified explicitly. +# @auto-read-only: if true and @read-only is false, QEMU may automatically +# decide not to open the image read-write as requested, but +# fall back to read-only instead (and switch between the modes +# later), e.g. depending on whether the image file is writable +# or whether a writing user is attached to the node +# (default: false, since 3.1) # @detect-zeroes: detect and optimize zero writes (Since 2.1) # (default: off) # @force-share: force share all permission on added nodes. @@ -3594,6 +3890,7 @@ '*discard': 'BlockdevDiscardOptions', '*cache': 'BlockdevCacheOptions', '*read-only': 'bool', + '*auto-read-only': 'bool', '*force-share': 'bool', '*detect-zeroes': 'BlockdevDetectZeroesOptions' }, 'discriminator': 'driver', @@ -3627,7 +3924,8 @@ 'quorum': 'BlockdevOptionsQuorum', 'raw': 'BlockdevOptionsRaw', 'rbd': 'BlockdevOptionsRbd', - 'replication':'BlockdevOptionsReplication', + 'replication': { 'type': 'BlockdevOptionsReplication', + 'if': 'defined(CONFIG_REPLICATION)' }, 'sheepdog': 'BlockdevOptionsSheepdog', 'ssh': 'BlockdevOptionsSsh', 'throttle': 'BlockdevOptionsThrottle', @@ -3723,6 +4021,48 @@ ## { 'command': 'blockdev-add', 'data': 'BlockdevOptions', 'boxed': true } +## +# @x-blockdev-reopen: +# +# Reopens a block device using the given set of options. Any option +# not specified will be reset to its default value regardless of its +# previous status. If an option cannot be changed or a particular +# driver does not support reopening then the command will return an +# error. +# +# The top-level @node-name option (from BlockdevOptions) must be +# specified and is used to select the block device to be reopened. +# Other @node-name options must be either omitted or set to the +# current name of the appropriate node. This command won't change any +# node name and any attempt to do it will result in an error. +# +# In the case of options that refer to child nodes, the behavior of +# this command depends on the value: +# +# 1) A set of options (BlockdevOptions): the child is reopened with +# the specified set of options. +# +# 2) A reference to the current child: the child is reopened using +# its existing set of options. +# +# 3) A reference to a different node: the current child is replaced +# with the specified one. +# +# 4) NULL: the current child (if any) is detached. +# +# Options (1) and (2) are supported in all cases, but at the moment +# only @backing allows replacing or detaching an existing child. +# +# Unlike with blockdev-add, the @backing option must always be present +# unless the node being reopened does not have a backing file and its +# image does not have a default backing file name as part of its +# metadata. +# +# Since: 4.0 +## +{ 'command': 'x-blockdev-reopen', + 'data': 'BlockdevOptions', 'boxed': true } + ## # @blockdev-del: # @@ -3872,6 +4212,12 @@ # Driver specific image creation options for qcow2. # # @file Node to create the image format on +# @data-file Node to use as an external data file in which all guest +# data is stored so that only metadata remains in the qcow2 +# file (since: 4.0) +# @data-file-raw True if the external data file must stay valid as a +# standalone (read-only) raw image without looking at qcow2 +# metadata (default: false; since: 4.0) # @size Size of the virtual disk in bytes # @version Compatibility level (default: v3) # @backing-file File name of the backing file if a backing file @@ -3887,6 +4233,8 @@ ## { 'struct': 'BlockdevCreateOptionsQcow2', 'data': { 'file': 'BlockdevRef', + '*data-file': 'BlockdevRef', + '*data-file-raw': 'bool', 'size': 'size', '*version': 'BlockdevQcow2Version', '*backing-file': 'str', @@ -3937,6 +4285,76 @@ 'size': 'size', '*cluster-size' : 'size' } } +## +# @BlockdevVmdkSubformat: +# +# Subformat options for VMDK images +# +# @monolithicSparse: Single file image with sparse cluster allocation +# +# @monolithicFlat: Single flat data image and a descriptor file +# +# @twoGbMaxExtentSparse: Data is split into 2GB (per virtual LBA) sparse extent +# files, in addition to a descriptor file +# +# @twoGbMaxExtentFlat: Data is split into 2GB (per virtual LBA) flat extent +# files, in addition to a descriptor file +# +# @streamOptimized: Single file image sparse cluster allocation, optimized +# for streaming over network. +# +# Since: 4.0 +## +{ 'enum': 'BlockdevVmdkSubformat', + 'data': [ 'monolithicSparse', 'monolithicFlat', 'twoGbMaxExtentSparse', + 'twoGbMaxExtentFlat', 'streamOptimized'] } + +## +# @BlockdevVmdkAdapterType: +# +# Adapter type info for VMDK images +# +# Since: 4.0 +## +{ 'enum': 'BlockdevVmdkAdapterType', + 'data': [ 'ide', 'buslogic', 'lsilogic', 'legacyESX'] } + +## +# @BlockdevCreateOptionsVmdk: +# +# Driver specific image creation options for VMDK. +# +# @file Where to store the new image file. This refers to the image +# file for monolithcSparse and streamOptimized format, or the +# descriptor file for other formats. +# @size Size of the virtual disk in bytes +# @extents Where to store the data extents. Required for monolithcFlat, +# twoGbMaxExtentSparse and twoGbMaxExtentFlat formats. For +# monolithicFlat, only one entry is required; for +# twoGbMaxExtent* formats, the number of entries required is +# calculated as extent_number = virtual_size / 2GB. Providing +# more extents than will be used is an error. +# @subformat The subformat of the VMDK image. Default: "monolithicSparse". +# @backing-file The path of backing file. Default: no backing file is used. +# @adapter-type The adapter type used to fill in the descriptor. Default: ide. +# @hwversion Hardware version. The meaningful options are "4" or "6". +# Default: "4". +# @zeroed-grain Whether to enable zeroed-grain feature for sparse subformats. +# Default: false. +# +# Since: 4.0 +## +{ 'struct': 'BlockdevCreateOptionsVmdk', + 'data': { 'file': 'BlockdevRef', + 'size': 'size', + '*extents': ['BlockdevRef'], + '*subformat': 'BlockdevVmdkSubformat', + '*backing-file': 'str', + '*adapter-type': 'BlockdevVmdkAdapterType', + '*hwversion': 'str', + '*zeroed-grain': 'bool' } } + + ## # @SheepdogRedundancyType: # @@ -4131,6 +4549,7 @@ 'ssh': 'BlockdevCreateOptionsSsh', 'vdi': 'BlockdevCreateOptionsVdi', 'vhdx': 'BlockdevCreateOptionsVhdx', + 'vmdk': 'BlockdevCreateOptionsVmdk', 'vpc': 'BlockdevCreateOptionsVpc' } } diff --git a/qapi/block.json b/qapi/block.json index 11f01f28ef..145c268bb6 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -224,7 +224,12 @@ # 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 +# @tls-creds: ID of the TLS credentials object (since 2.6). +# @tls-authz: ID of the QAuthZ authorization object used to validate +# the client's x509 distinguished name. This object is +# is only resolved at time of use, so can be deleted and +# recreated on the fly while the NBD server is active. +# If missing, it will default to denying access (since 4.0). # # Returns: error if the server is already running. # @@ -232,7 +237,8 @@ ## { 'command': 'nbd-server-start', 'data': { 'addr': 'SocketAddressLegacy', - '*tls-creds': 'str'} } + '*tls-creds': 'str', + '*tls-authz': 'str'} } ## # @nbd-server-add: @@ -246,6 +252,10 @@ # # @writable: Whether clients should be able to write to the device via the # NBD connection (default false). + +# @bitmap: Also export the dirty bitmap reachable from @device, so the +# NBD client can use NBD_OPT_SET_META_CONTEXT with +# "qemu:dirty-bitmap:NAME" to inspect the bitmap. (since 4.0) # # Returns: error if the server is not running, or export with the same name # already exists. @@ -253,7 +263,8 @@ # Since: 1.3.0 ## { 'command': 'nbd-server-add', - 'data': {'device': 'str', '*name': 'str', '*writable': 'bool'} } + 'data': {'device': 'str', '*name': 'str', '*writable': 'bool', + '*bitmap': 'str' } } ## # @NbdServerRemoveMode: @@ -296,29 +307,6 @@ { 'command': 'nbd-server-remove', 'data': {'name': 'str', '*mode': 'NbdServerRemoveMode'} } -## -# @x-nbd-server-add-bitmap: -# -# Expose a dirty bitmap associated with the selected export. The bitmap search -# starts at the device attached to the export, and includes all backing files. -# The exported bitmap is then locked until the NBD export is removed. -# -# @name: Export name. -# -# @bitmap: Bitmap name to search for. -# -# @bitmap-export-name: How the bitmap will be seen by nbd clients -# (default @bitmap) -# -# Note: the client must use NBD_OPT_SET_META_CONTEXT with a query of -# "qemu:dirty-bitmap:NAME" (where NAME matches @bitmap-export-name) to access -# the exposed bitmap. -# -# Since: 3.0 -## - { 'command': 'x-nbd-server-add-bitmap', - 'data': {'name': 'str', 'bitmap': 'str', '*bitmap-export-name': 'str'} } - ## # @nbd-server-stop: # diff --git a/qapi/char.json b/qapi/char.json index b7b2a05766..a6e81ac7bc 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -25,9 +25,10 @@ # # Since: 0.14.0 ## -{ 'struct': 'ChardevInfo', 'data': {'label': 'str', - 'filename': 'str', - 'frontend-open': 'bool'} } +{ 'struct': 'ChardevInfo', + 'data': { 'label': 'str', + 'filename': 'str', + 'frontend-open': 'bool' } } ## # @query-chardev: @@ -152,7 +153,8 @@ # ## { 'command': 'ringbuf-write', - 'data': {'device': 'str', 'data': 'str', + 'data': { 'device': 'str', + 'data': 'str', '*format': 'DataFormat'} } ## @@ -202,8 +204,9 @@ # # Since: 2.6 ## -{ 'struct': 'ChardevCommon', 'data': { '*logfile': 'str', - '*logappend': 'bool' } } +{ 'struct': 'ChardevCommon', + 'data': { '*logfile': 'str', + '*logappend': 'bool' } } ## # @ChardevFile: @@ -217,9 +220,10 @@ # # Since: 1.4 ## -{ 'struct': 'ChardevFile', 'data': { '*in' : 'str', - 'out' : 'str', - '*append': 'bool' }, +{ 'struct': 'ChardevFile', + 'data': { '*in': 'str', + 'out': 'str', + '*append': 'bool' }, 'base': 'ChardevCommon' } ## @@ -232,7 +236,8 @@ # # Since: 1.4 ## -{ 'struct': 'ChardevHostdev', 'data': { 'device' : 'str' }, +{ 'struct': 'ChardevHostdev', + 'data': { 'device': 'str' }, 'base': 'ChardevCommon' } ## @@ -243,6 +248,11 @@ # @addr: socket address to listen on (server=true) # or connect to (server=false) # @tls-creds: the ID of the TLS credentials object (since 2.6) +# @tls-authz: the ID of the QAuthZ authorization object against which +# the client's x509 distinguished name will be validated. This +# object is only resolved at time of use, so can be deleted +# and recreated on the fly while the chardev server is active. +# If missing, it will default to denying access (since 4.0) # @server: create server socket (default: true) # @wait: wait for incoming connection on server # sockets (default: false). @@ -251,6 +261,8 @@ # sockets (default: false) # @tn3270: enable tn3270 protocol on server # sockets (default: false) (Since: 2.10) +# @websocket: enable websocket protocol on server +# sockets (default: false) (Since: 3.1) # @reconnect: For a client socket, if a socket is disconnected, # then attempt a reconnect after the given number of seconds. # Setting this to zero disables this function. (default: 0) @@ -258,14 +270,17 @@ # # Since: 1.4 ## -{ 'struct': 'ChardevSocket', 'data': { 'addr' : 'SocketAddressLegacy', - '*tls-creds' : 'str', - '*server' : 'bool', - '*wait' : 'bool', - '*nodelay' : 'bool', - '*telnet' : 'bool', - '*tn3270' : 'bool', - '*reconnect' : 'int' }, +{ 'struct': 'ChardevSocket', + 'data': { 'addr': 'SocketAddressLegacy', + '*tls-creds': 'str', + '*tls-authz' : 'str', + '*server': 'bool', + '*wait': 'bool', + '*nodelay': 'bool', + '*telnet': 'bool', + '*tn3270': 'bool', + '*websocket': 'bool', + '*reconnect': 'int' }, 'base': 'ChardevCommon' } ## @@ -278,8 +293,9 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevUdp', 'data': { 'remote' : 'SocketAddressLegacy', - '*local' : 'SocketAddressLegacy' }, +{ 'struct': 'ChardevUdp', + 'data': { 'remote': 'SocketAddressLegacy', + '*local': 'SocketAddressLegacy' }, 'base': 'ChardevCommon' } ## @@ -291,7 +307,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevMux', 'data': { 'chardev' : 'str' }, +{ 'struct': 'ChardevMux', + 'data': { 'chardev': 'str' }, 'base': 'ChardevCommon' } ## @@ -305,7 +322,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevStdio', 'data': { '*signal' : 'bool' }, +{ 'struct': 'ChardevStdio', + 'data': { '*signal': 'bool' }, 'base': 'ChardevCommon' } @@ -318,9 +336,10 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevSpiceChannel', 'data': { 'type' : 'str' }, - 'base': 'ChardevCommon' } -# TODO: 'if': 'defined(CONFIG_SPICE)' +{ 'struct': 'ChardevSpiceChannel', + 'data': { 'type': 'str' }, + 'base': 'ChardevCommon', + 'if': 'defined(CONFIG_SPICE)' } ## # @ChardevSpicePort: @@ -331,9 +350,10 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevSpicePort', 'data': { 'fqdn' : 'str' }, - 'base': 'ChardevCommon' } -# TODO: 'if': 'defined(CONFIG_SPICE)' +{ 'struct': 'ChardevSpicePort', + 'data': { 'fqdn': 'str' }, + 'base': 'ChardevCommon', + 'if': 'defined(CONFIG_SPICE)' } ## # @ChardevVC: @@ -347,10 +367,11 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevVC', 'data': { '*width' : 'int', - '*height' : 'int', - '*cols' : 'int', - '*rows' : 'int' }, +{ 'struct': 'ChardevVC', + 'data': { '*width': 'int', + '*height': 'int', + '*cols': 'int', + '*rows': 'int' }, 'base': 'ChardevCommon' } ## @@ -362,7 +383,8 @@ # # Since: 1.5 ## -{ 'struct': 'ChardevRingbuf', 'data': { '*size' : 'int' }, +{ 'struct': 'ChardevRingbuf', + 'data': { '*size': 'int' }, 'base': 'ChardevCommon' } ## @@ -372,29 +394,30 @@ # # Since: 1.4 (testdev since 2.2, wctablet since 2.9) ## -{ 'union': 'ChardevBackend', 'data': { 'file' : 'ChardevFile', - 'serial' : 'ChardevHostdev', - 'parallel': 'ChardevHostdev', - 'pipe' : 'ChardevHostdev', - 'socket' : 'ChardevSocket', - 'udp' : 'ChardevUdp', - 'pty' : 'ChardevCommon', - 'null' : 'ChardevCommon', - 'mux' : 'ChardevMux', - 'msmouse': 'ChardevCommon', - 'wctablet' : 'ChardevCommon', - 'braille': 'ChardevCommon', - 'testdev': 'ChardevCommon', - 'stdio' : 'ChardevStdio', - 'console': 'ChardevCommon', - 'spicevmc': 'ChardevSpiceChannel', -# TODO: { 'type': 'ChardevSpiceChannel', 'if': 'defined(CONFIG_SPICE)' }, - 'spiceport': 'ChardevSpicePort', -# TODO: { 'type': 'ChardevSpicePort', 'if': 'defined(CONFIG_SPICE)' }, - 'vc' : 'ChardevVC', - 'ringbuf': 'ChardevRingbuf', - # next one is just for compatibility - 'memory' : 'ChardevRingbuf' } } +{ 'union': 'ChardevBackend', + 'data': { 'file': 'ChardevFile', + 'serial': 'ChardevHostdev', + 'parallel': 'ChardevHostdev', + 'pipe': 'ChardevHostdev', + 'socket': 'ChardevSocket', + 'udp': 'ChardevUdp', + 'pty': 'ChardevCommon', + 'null': 'ChardevCommon', + 'mux': 'ChardevMux', + 'msmouse': 'ChardevCommon', + 'wctablet': 'ChardevCommon', + 'braille': 'ChardevCommon', + 'testdev': 'ChardevCommon', + 'stdio': 'ChardevStdio', + 'console': 'ChardevCommon', + 'spicevmc': { 'type': 'ChardevSpiceChannel', + 'if': 'defined(CONFIG_SPICE)' }, + 'spiceport': { 'type': 'ChardevSpicePort', + 'if': 'defined(CONFIG_SPICE)' }, + 'vc': 'ChardevVC', + 'ringbuf': 'ChardevRingbuf', + # next one is just for compatibility + 'memory': 'ChardevRingbuf' } } ## # @ChardevReturn: @@ -406,7 +429,8 @@ # # Since: 1.4 ## -{ 'struct' : 'ChardevReturn', 'data': { '*pty' : 'str' } } +{ 'struct' : 'ChardevReturn', + 'data': { '*pty': 'str' } } ## # @chardev-add: @@ -439,8 +463,9 @@ # <- { "return": { "pty" : "/dev/pty/42" } } # ## -{ 'command': 'chardev-add', 'data': {'id' : 'str', - 'backend' : 'ChardevBackend' }, +{ 'command': 'chardev-add', + 'data': { 'id': 'str', + 'backend': 'ChardevBackend' }, 'returns': 'ChardevReturn' } ## @@ -479,8 +504,9 @@ # <- {"return": {}} # ## -{ 'command': 'chardev-change', 'data': {'id' : 'str', - 'backend' : 'ChardevBackend' }, +{ 'command': 'chardev-change', + 'data': { 'id': 'str', + 'backend': 'ChardevBackend' }, 'returns': 'ChardevReturn' } ## @@ -500,7 +526,8 @@ # <- { "return": {} } # ## -{ 'command': 'chardev-remove', 'data': {'id': 'str'} } +{ 'command': 'chardev-remove', + 'data': { 'id': 'str' } } ## # @chardev-send-break: @@ -519,7 +546,8 @@ # <- { "return": {} } # ## -{ 'command': 'chardev-send-break', 'data': {'id': 'str'} } +{ 'command': 'chardev-send-break', + 'data': { 'id': 'str' } } ## # @VSERPORT_CHANGE: @@ -540,4 +568,5 @@ # ## { 'event': 'VSERPORT_CHANGE', - 'data': { 'id': 'str', 'open': 'bool' } } + 'data': { 'id': 'str', + 'open': 'bool' } } diff --git a/qapi/common.json b/qapi/common.json index c367adc4b6..99d313ef3b 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -127,6 +127,48 @@ { 'enum': 'OffAutoPCIBAR', 'data': [ 'off', 'auto', 'bar0', 'bar1', 'bar2', 'bar3', 'bar4', 'bar5' ] } +## +# @PCIELinkSpeed: +# +# An enumeration of PCIe link speeds in units of GT/s +# +# @2_5: 2.5GT/s +# +# @5: 5.0GT/s +# +# @8: 8.0GT/s +# +# @16: 16.0GT/s +# +# Since: 4.0 +## +{ 'enum': 'PCIELinkSpeed', + 'data': [ '2_5', '5', '8', '16' ] } + +## +# @PCIELinkWidth: +# +# An enumeration of PCIe link width +# +# @1: x1 +# +# @2: x2 +# +# @4: x4 +# +# @8: x8 +# +# @12: x12 +# +# @16: x16 +# +# @32: x32 +# +# Since: 4.0 +## +{ 'enum': 'PCIELinkWidth', + 'data': [ '1', '2', '4', '8', '12', '16', '32' ] } + ## # @SysEmuTarget: # @@ -140,12 +182,14 @@ # prefix to produce the corresponding QEMU executable name. This # is true even for "qemu-system-x86_64". # +# ppcemb: dropped in 3.1 +# # Since: 3.0 ## { 'enum' : 'SysEmuTarget', 'data' : [ 'aarch64', 'alpha', 'arm', 'cris', 'hppa', 'i386', 'lm32', 'm68k', 'microblaze', 'microblazeel', 'mips', 'mips64', 'mips64el', 'mipsel', 'moxie', 'nios2', 'or1k', 'ppc', - 'ppc64', 'ppcemb', 'riscv32', 'riscv64', 's390x', 'sh4', + 'ppc64', 'riscv32', 'riscv64', 's390x', 'sh4', 'sh4eb', 'sparc', 'sparc64', 'tricore', 'unicore32', 'x86_64', 'xtensa', 'xtensaeb' ] } diff --git a/qapi/crypto.json b/qapi/crypto.json index a51b434412..b2a4cff683 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -181,11 +181,11 @@ # The options that apply to LUKS encryption format initialization # # @cipher-alg: the cipher algorithm for data encryption -# Currently defaults to 'aes'. +# Currently defaults to 'aes-256'. # @cipher-mode: the cipher mode for data encryption -# Currently defaults to 'cbc' +# Currently defaults to 'xts' # @ivgen-alg: the initialization vector generator -# Currently defaults to 'essiv' +# Currently defaults to 'plain64' # @ivgen-hash-alg: the initialization vector generator hash # Currently defaults to 'sha256' # @hash-alg: the master key hash algorithm diff --git a/qapi/introspect.json b/qapi/introspect.json index 137b39b992..3d22166b2b 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -120,7 +120,7 @@ ## # @JSONType: # -# The four primitive and two structured types according to RFC 7159 +# The four primitive and two structured types according to RFC 8259 # section 1, plus 'int' (split off 'number'), plus the obvious top # type 'value'. # diff --git a/qapi/migration.json b/qapi/migration.json index 186e8a7303..9cfbaf8c6c 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -6,6 +6,7 @@ ## { 'include': 'common.json' } +{ 'include': 'sockets.json' } ## # @MigrationStats: @@ -41,6 +42,9 @@ # # @multifd-bytes: The number of bytes sent through multifd (since 3.0) # +# @pages-per-second: the number of memory pages transferred per second +# (Since 4.0) +# # Since: 0.14.0 ## { 'struct': 'MigrationStats', @@ -49,7 +53,7 @@ 'normal-bytes': 'int', 'dirty-pages-rate' : 'int', 'mbps' : 'number', 'dirty-sync-count' : 'int', 'postcopy-requests' : 'int', 'page-size' : 'int', - 'multifd-bytes' : 'uint64' } } + 'multifd-bytes' : 'uint64', 'pages-per-second' : 'uint64' } } ## # @XBZRLECacheStats: @@ -75,6 +79,27 @@ 'cache-miss': 'int', 'cache-miss-rate': 'number', 'overflow': 'int' } } +## +# @CompressionStats: +# +# Detailed migration compression statistics +# +# @pages: amount of pages compressed and transferred to the target VM +# +# @busy: count of times that no free thread was available to compress data +# +# @busy-rate: rate of thread busy +# +# @compressed-size: amount of bytes after compression +# +# @compression-rate: rate of compressed size +# +# Since: 3.1 +## +{ 'struct': 'CompressionStats', + 'data': {'pages': 'int', 'busy': 'int', 'busy-rate': 'number', + 'compressed-size': 'int', 'compression-rate': 'number' } } + ## # @MigrationStatus: # @@ -172,6 +197,10 @@ # only present when the postcopy-blocktime migration capability # is enabled. (Since 3.0) # +# @compression: migration compression statistics, only returned if compression +# feature is on and status is 'active' or 'completed' (Since 3.1) +# +# @socket-address: Only used for tcp, to know what the real port is (Since 4.0) # # Since: 0.14.0 ## @@ -186,7 +215,9 @@ '*cpu-throttle-percentage': 'int', '*error-desc': 'str', '*postcopy-blocktime' : 'uint32', - '*postcopy-vcpu-blocktime': ['uint32']} } + '*postcopy-vcpu-blocktime': ['uint32'], + '*compression': 'CompressionStats', + '*socket-address': ['SocketAddress'] } } ## # @query-migrate: @@ -212,13 +243,13 @@ # -> { "execute": "query-migrate" } # <- { "return": { # "status": "completed", +# "total-time":12345, +# "setup-time":12345, +# "downtime":12345, # "ram":{ # "transferred":123, # "remaining":123, # "total":246, -# "total-time":12345, -# "setup-time":12345, -# "downtime":12345, # "duplicate":123, # "normal":123, # "normal-bytes":123456, @@ -238,13 +269,13 @@ # <- { # "return":{ # "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, # "ram":{ # "transferred":123, # "remaining":123, # "total":246, -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, # "duplicate":123, # "normal":123, # "normal-bytes":123456, @@ -259,13 +290,13 @@ # <- { # "return":{ # "status":"active", +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, # "ram":{ # "total":1057024, # "remaining":1053304, # "transferred":3720, -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, # "duplicate":123, # "normal":123, # "normal-bytes":123456, @@ -285,14 +316,13 @@ # <- { # "return":{ # "status":"active", -# "capabilities" : [ { "capability": "xbzrle", "state" : true } ], +# "total-time":12345, +# "setup-time":12345, +# "expected-downtime":12345, # "ram":{ # "total":1057024, # "remaining":1053304, # "transferred":3720, -# "total-time":12345, -# "setup-time":12345, -# "expected-downtime":12345, # "duplicate":10, # "normal":3333, # "normal-bytes":3412992, @@ -371,7 +401,7 @@ # @pause-before-switchover: Pause outgoing migration before serialising device # state and before disabling block IO (since 2.11) # -# @x-multifd: Use more than one fd for migration (since 2.11) +# @multifd: Use more than one fd for migration (since 4.0) # # @dirty-bitmaps: If enabled, QEMU will migrate named dirty bitmaps. # (since 2.12) @@ -383,13 +413,16 @@ # devices (and thus take locks) immediately at the end of migration. # (since 3.0) # +# @x-ignore-shared: If enabled, QEMU will not migrate shared memory (since 4.0) +# # Since: 1.2 ## { 'enum': 'MigrationCapability', 'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks', 'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram', - 'block', 'return-path', 'pause-before-switchover', 'x-multifd', - 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate' ] } + 'block', 'return-path', 'pause-before-switchover', 'multifd', + 'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate', + 'x-ignore-shared' ] } ## # @MigrationCapabilityStatus: @@ -454,6 +487,18 @@ # # Migration parameters enumeration # +# @announce-initial: Initial delay (in milliseconds) before sending the first +# announce (Since 4.0) +# +# @announce-max: Maximum delay (in milliseconds) between packets in the +# announcement (Since 4.0) +# +# @announce-rounds: Number of self-announce packets sent after migration +# (Since 4.0) +# +# @announce-step: Increase in delay (in milliseconds) between subsequent +# packets in the announcement (Since 4.0) +# # @compress-level: Set the compression level to be used in live migration, # the compression level is an integer between 0 and 9, where 0 means # no compression, 1 means the best compression speed, and 9 means best @@ -462,6 +507,11 @@ # @compress-threads: Set compression thread count to be used in live migration, # the compression thread count is an integer between 1 and 255. # +# @compress-wait-thread: Controls behavior when all compression threads are +# currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, +# send the page uncompressed. (Since 3.1) +# # @decompress-threads: Set decompression thread count to be used in live # migration, the decompression thread count is an integer between 1 # and 255. Usually, decompression is at least 4 times as fast as @@ -491,6 +541,12 @@ # hostname must be provided so that the server's x509 # certificate identity can be validated. (Since 2.7) # +# @tls-authz: ID of the 'authz' object subclass that provides access control +# checking of the TLS x509 certificate distinguished name. +# This object is only resolved at time of use, so can be deleted +# and recreated on the fly while the migration server is active. +# If missing, it will default to denying access (Since 4.0) +# # @max-bandwidth: to set maximum speed for migration. maximum speed in # bytes per second. (Since 2.8) # @@ -507,13 +563,10 @@ # migrated and the destination must already have access to the # same backing chain as was used on the source. (since 2.10) # -# @x-multifd-channels: Number of channels used to migrate data in -# parallel. This is the same number that the -# number of sockets used for migration. The -# default value is 2 (since 2.11) -# -# @x-multifd-page-count: Number of pages sent together to a thread. -# The default value is 16 (since 2.11) +# @multifd-channels: Number of channels used to migrate data in +# parallel. This is the same number that the +# number of sockets used for migration. The +# default value is 2 (since 4.0) # # @xbzrle-cache-size: cache size to be used by XBZRLE migration. It # needs to be a multiple of the target page size @@ -523,23 +576,48 @@ # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. # Defaults to 0 (unlimited). In bytes per second. # (Since 3.0) +# +# @max-cpu-throttle: maximum cpu throttle percentage. +# Defaults to 99. (Since 3.1) +# # Since: 2.4 ## { 'enum': 'MigrationParameter', - 'data': ['compress-level', 'compress-threads', 'decompress-threads', + 'data': ['announce-initial', 'announce-max', + 'announce-rounds', 'announce-step', + 'compress-level', 'compress-threads', 'decompress-threads', + 'compress-wait-thread', 'cpu-throttle-initial', 'cpu-throttle-increment', - 'tls-creds', 'tls-hostname', 'max-bandwidth', + 'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth', 'downtime-limit', 'x-checkpoint-delay', 'block-incremental', - 'x-multifd-channels', 'x-multifd-page-count', - 'xbzrle-cache-size', 'max-postcopy-bandwidth' ] } + 'multifd-channels', + 'xbzrle-cache-size', 'max-postcopy-bandwidth', + 'max-cpu-throttle' ] } ## # @MigrateSetParameters: # +# @announce-initial: Initial delay (in milliseconds) before sending the first +# announce (Since 4.0) +# +# @announce-max: Maximum delay (in milliseconds) between packets in the +# announcement (Since 4.0) +# +# @announce-rounds: Number of self-announce packets sent after migration +# (Since 4.0) +# +# @announce-step: Increase in delay (in milliseconds) between subsequent +# packets in the announcement (Since 4.0) +# # @compress-level: compression level # # @compress-threads: compression thread count # +# @compress-wait-thread: Controls behavior when all compression threads are +# currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, +# send the page uncompressed. (Since 3.1) +# # @decompress-threads: decompression thread count # # @cpu-throttle-initial: Initial percentage of time guest cpus are @@ -587,13 +665,10 @@ # migrated and the destination must already have access to the # same backing chain as was used on the source. (since 2.10) # -# @x-multifd-channels: Number of channels used to migrate data in -# parallel. This is the same number that the -# number of sockets used for migration. The -# default value is 2 (since 2.11) -# -# @x-multifd-page-count: Number of pages sent together to a thread. -# The default value is 16 (since 2.11) +# @multifd-channels: Number of channels used to migrate data in +# parallel. This is the same number that the +# number of sockets used for migration. The +# default value is 2 (since 4.0) # # @xbzrle-cache-size: cache size to be used by XBZRLE migration. It # needs to be a multiple of the target page size @@ -603,26 +678,36 @@ # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. # Defaults to 0 (unlimited). In bytes per second. # (Since 3.0) +# +# @max-cpu-throttle: maximum cpu throttle percentage. +# The default value is 99. (Since 3.1) +# # Since: 2.4 ## # TODO either fuse back into MigrationParameters, or make # MigrationParameters members mandatory { 'struct': 'MigrateSetParameters', - 'data': { '*compress-level': 'int', + 'data': { '*announce-initial': 'size', + '*announce-max': 'size', + '*announce-rounds': 'size', + '*announce-step': 'size', + '*compress-level': 'int', '*compress-threads': 'int', + '*compress-wait-thread': 'bool', '*decompress-threads': 'int', '*cpu-throttle-initial': 'int', '*cpu-throttle-increment': 'int', '*tls-creds': 'StrOrNull', '*tls-hostname': 'StrOrNull', + '*tls-authz': 'StrOrNull', '*max-bandwidth': 'int', '*downtime-limit': 'int', '*x-checkpoint-delay': 'int', '*block-incremental': 'bool', - '*x-multifd-channels': 'int', - '*x-multifd-page-count': 'int', + '*multifd-channels': 'int', '*xbzrle-cache-size': 'size', - '*max-postcopy-bandwidth': 'size' } } + '*max-postcopy-bandwidth': 'size', + '*max-cpu-throttle': 'int' } } ## # @migrate-set-parameters: @@ -645,10 +730,27 @@ # # The optional members aren't actually optional. # +# @announce-initial: Initial delay (in milliseconds) before sending the +# first announce (Since 4.0) +# +# @announce-max: Maximum delay (in milliseconds) between packets in the +# announcement (Since 4.0) +# +# @announce-rounds: Number of self-announce packets sent after migration +# (Since 4.0) +# +# @announce-step: Increase in delay (in milliseconds) between subsequent +# packets in the announcement (Since 4.0) +# # @compress-level: compression level # # @compress-threads: compression thread count # +# @compress-wait-thread: Controls behavior when all compression threads are +# currently busy. If true (default), wait for a free +# compression thread to become available; otherwise, +# send the page uncompressed. (Since 3.1) +# # @decompress-threads: decompression thread count # # @cpu-throttle-initial: Initial percentage of time guest cpus are @@ -678,6 +780,10 @@ # associated with the migration URI, if any. (Since 2.9) # Note: 2.8 reports this by omitting tls-hostname instead. # +# @tls-authz: ID of the 'authz' object subclass that provides access control +# checking of the TLS x509 certificate distinguished name. (Since +# 4.0) +# # @max-bandwidth: to set maximum speed for migration. maximum speed in # bytes per second. (Since 2.8) # @@ -693,13 +799,10 @@ # migrated and the destination must already have access to the # same backing chain as was used on the source. (since 2.10) # -# @x-multifd-channels: Number of channels used to migrate data in -# parallel. This is the same number that the -# number of sockets used for migration. -# The default value is 2 (since 2.11) -# -# @x-multifd-page-count: Number of pages sent together to a thread. -# The default value is 16 (since 2.11) +# @multifd-channels: Number of channels used to migrate data in +# parallel. This is the same number that the +# number of sockets used for migration. +# The default value is 2 (since 4.0) # # @xbzrle-cache-size: cache size to be used by XBZRLE migration. It # needs to be a multiple of the target page size @@ -709,24 +812,35 @@ # @max-postcopy-bandwidth: Background transfer bandwidth during postcopy. # Defaults to 0 (unlimited). In bytes per second. # (Since 3.0) +# +# @max-cpu-throttle: maximum cpu throttle percentage. +# Defaults to 99. +# (Since 3.1) +# # Since: 2.4 ## { 'struct': 'MigrationParameters', - 'data': { '*compress-level': 'uint8', + 'data': { '*announce-initial': 'size', + '*announce-max': 'size', + '*announce-rounds': 'size', + '*announce-step': 'size', + '*compress-level': 'uint8', '*compress-threads': 'uint8', + '*compress-wait-thread': 'bool', '*decompress-threads': 'uint8', '*cpu-throttle-initial': 'uint8', '*cpu-throttle-increment': 'uint8', '*tls-creds': 'str', '*tls-hostname': 'str', + '*tls-authz': 'str', '*max-bandwidth': 'size', '*downtime-limit': 'uint64', '*x-checkpoint-delay': 'uint32', '*block-incremental': 'bool' , - '*x-multifd-channels': 'uint8', - '*x-multifd-page-count': 'uint32', + '*multifd-channels': 'uint8', '*xbzrle-cache-size': 'size', - '*max-postcopy-bandwidth': 'size' } } + '*max-postcopy-bandwidth': 'size', + '*max-cpu-throttle':'uint8'} } ## # @query-migrate-parameters: @@ -867,18 +981,18 @@ ## # @COLOMode: # -# The colo mode +# The COLO current mode. # -# @unknown: unknown mode +# @none: COLO is disabled. # -# @primary: master side +# @primary: COLO node in primary side. # -# @secondary: slave side +# @secondary: COLO node in slave side. # # Since: 2.8 ## { 'enum': 'COLOMode', - 'data': [ 'unknown', 'primary', 'secondary'] } + 'data': [ 'none', 'primary', 'secondary'] } ## # @FailoverStatus: @@ -900,6 +1014,47 @@ { 'enum': 'FailoverStatus', 'data': [ 'none', 'require', 'active', 'completed', 'relaunch' ] } +## +# @COLO_EXIT: +# +# Emitted when VM finishes COLO mode due to some errors happening or +# at the request of users. +# +# @mode: report COLO mode when COLO exited. +# +# @reason: describes the reason for the COLO exit. +# +# Since: 3.1 +# +# Example: +# +# <- { "timestamp": {"seconds": 2032141960, "microseconds": 417172}, +# "event": "COLO_EXIT", "data": {"mode": "primary", "reason": "request" } } +# +## +{ 'event': 'COLO_EXIT', + 'data': {'mode': 'COLOMode', 'reason': 'COLOExitReason' } } + +## +# @COLOExitReason: +# +# The reason for a COLO exit. +# +# @none: failover has never happened. This state does not occur +# in the COLO_EXIT event, and is only visible in the result of +# query-colo-status. +# +# @request: COLO exit is due to an external request. +# +# @error: COLO exit is due to an internal error. +# +# @processing: COLO is currently handling a failover (since 4.0). +# +# Since: 3.1 +## +{ 'enum': 'COLOExitReason', + 'data': [ 'none', 'request', 'error' , 'processing' ] } + ## # @x-colo-lost-heartbeat: # @@ -1163,7 +1318,8 @@ # Since: 2.9 ## { 'command': 'xen-set-replication', - 'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' } } + 'data': { 'enable': 'bool', 'primary': 'bool', '*failover' : 'bool' }, + 'if': 'defined(CONFIG_REPLICATION)' } ## # @ReplicationStatus: @@ -1178,7 +1334,8 @@ # Since: 2.9 ## { 'struct': 'ReplicationStatus', - 'data': { 'error': 'bool', '*desc': 'str' } } + 'data': { 'error': 'bool', '*desc': 'str' }, + 'if': 'defined(CONFIG_REPLICATION)' } ## # @query-xen-replication-status: @@ -1195,7 +1352,8 @@ # Since: 2.9 ## { 'command': 'query-xen-replication-status', - 'returns': 'ReplicationStatus' } + 'returns': 'ReplicationStatus', + 'if': 'defined(CONFIG_REPLICATION)' } ## # @xen-colo-do-checkpoint: @@ -1211,7 +1369,45 @@ # # Since: 2.9 ## -{ 'command': 'xen-colo-do-checkpoint' } +{ 'command': 'xen-colo-do-checkpoint', + 'if': 'defined(CONFIG_REPLICATION)' } + +## +# @COLOStatus: +# +# The result format for 'query-colo-status'. +# +# @mode: COLO running mode. If COLO is running, this field will return +# 'primary' or 'secondary'. +# +# @last-mode: COLO last running mode. If COLO is running, this field +# will return same like mode field, after failover we can +# use this field to get last colo mode. (since 4.0) +# +# @reason: describes the reason for the COLO exit. +# +# Since: 3.1 +## +{ 'struct': 'COLOStatus', + 'data': { 'mode': 'COLOMode', 'last-mode': 'COLOMode', + 'reason': 'COLOExitReason' } } + +## +# @query-colo-status: +# +# Query COLO status while the vm is running. +# +# Returns: A @COLOStatus object showing the status. +# +# Example: +# +# -> { "execute": "query-colo-status" } +# <- { "return": { "mode": "primary", "reason": "request" } } +# +# Since: 3.1 +## +{ 'command': 'query-colo-status', + 'returns': 'COLOStatus' } ## # @migrate-recover: @@ -1230,7 +1426,8 @@ # # Since: 3.0 ## -{ 'command': 'migrate-recover', 'data': { 'uri': 'str' }, +{ 'command': 'migrate-recover', + 'data': { 'uri': 'str' }, 'allow-oob': true } ## diff --git a/qapi/misc.json b/qapi/misc.json index d450cfef21..8b3ca4fdd3 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -318,12 +318,15 @@ ## # @query-events: # -# Return a list of supported QMP events by this server +# Return information on QMP events. # -# Returns: A list of @EventInfo for all supported events +# Returns: A list of @EventInfo. # # Since: 1.2.0 # +# Note: This command is deprecated, because its output doesn't reflect +# compile-time configuration. Use query-qmp-schema instead. +# # Example: # # -> { "execute": "query-events" } @@ -832,10 +835,15 @@ # # @vendor: the PCI vendor id # +# @subsystem: the PCI subsystem id (since 3.1) +# +# @subsystem-vendor: the PCI subsystem vendor id (since 3.1) +# # Since: 2.4 ## { 'struct': 'PciDeviceId', - 'data': {'device': 'int', 'vendor': 'int'} } + 'data': {'device': 'int', 'vendor': 'int', '*subsystem': 'int', + '*subsystem-vendor': 'int'} } ## # @PciDeviceInfo: @@ -1104,7 +1112,7 @@ ## # @cpu-add: # -# Adds CPU with specified ID +# Adds CPU with specified ID. # # @id: ID of CPU to be created, valid values [0..max_cpus) # @@ -1112,6 +1120,10 @@ # # Since: 1.5 # +# Note: This command is deprecated. The `device_add` command should be +# used instead. See the `query-hotpluggable-cpus` command for +# details. +# # Example: # # -> { "execute": "cpu-add", "arguments": { "id": 2 } } @@ -1230,12 +1242,18 @@ ## # @system_wakeup: # -# Wakeup guest from suspend. Does nothing in case the guest isn't suspended. +# Wake up guest from suspend. If the guest has wake-up from suspend +# support enabled (wakeup-suspend-support flag from +# query-current-machine), wake-up guest from suspend if the guest is +# in SUSPENDED state. Return an error otherwise. # # Since: 1.1 # # Returns: nothing. # +# Note: prior to 4.0, this command does nothing in case the guest +# isn't suspended. +# # Example: # # -> { "execute": "system_wakeup" } @@ -1365,6 +1383,16 @@ # object. # # Since: 1.2 +# +# Example: +# +# -> { "execute": "qom-list", +# "arguments": { "path": "/chardevs" } } +# <- { "return": [ { "name": "type", "type": "string" }, +# { "name": "parallel0", "type": "child" }, +# { "name": "serial0", "type": "child" }, +# { "name": "mon0", "type": "child" } ] } +# ## { 'command': 'qom-list', 'data': { 'path': 'str' }, @@ -1402,6 +1430,23 @@ # returned as #int. # # Since: 1.2 +# +# Example: +# +# 1. Use absolute path +# +# -> { "execute": "qom-get", +# "arguments": { "path": "/machine/unattached/device[0]", +# "property": "hotplugged" } } +# <- { "return": false } +# +# 2. Use partial path +# +# -> { "execute": "qom-get", +# "arguments": { "path": "unattached/sysbus", +# "property": "type" } } +# <- { "return": "System" } +# ## { 'command': 'qom-get', 'data': { 'path': 'str', 'property': 'str' }, @@ -1421,6 +1466,15 @@ # for a description of type mapping. # # Since: 1.2 +# +# Example: +# +# -> { "execute": "qom-set", +# "arguments": { "path": "/machine", +# "property": "graphics", +# "value": false } } +# <- { "return": {} } +# ## { 'command': 'qom-set', 'data': { 'path': 'str', 'property': 'str', 'value': 'any' }, @@ -1856,27 +1910,6 @@ { '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 -# -# Example: -# -# -> { "execute": "dump-skeys", -# "arguments": { "filename": "/tmp/skeys" } } -# <- { "return": {} } -# -## -{ 'command': 'dump-skeys', - 'data': { 'filename': 'str' } } - ## # @object-add: # @@ -2004,52 +2037,28 @@ { 'command': 'query-machines', 'returns': ['MachineInfo'] } ## -# @CpuDefinitionInfo: +# @CurrentMachineParams: # -# Virtual CPU definition. +# Information describing the running machine parameters. # -# @name: the name of the CPU definition +# @wakeup-suspend-support: true if the machine supports wake up from +# suspend # -# @migration-safe: whether a CPU definition can be safely used for -# migration in combination with a QEMU compatibility machine -# when migrating between different QMU versions and between -# hosts with different sets of (hardware or software) -# capabilities. If not provided, information is not available -# and callers should not assume the CPU definition to be -# migration-safe. (since 2.8) -# -# @static: whether a CPU definition is static and will not change depending on -# QEMU version, machine type, machine options and accelerator options. -# A static model is always migration-safe. (since 2.8) -# -# @unavailable-features: List of properties that prevent -# the CPU model from running in the current -# host. (since 2.8) -# @typename: Type name that can be used as argument to @device-list-properties, -# to introspect properties configurable using -cpu or -global. -# (since 2.9) -# -# @unavailable-features is a list of QOM property names that -# represent CPU model attributes that prevent the CPU from running. -# If the QOM property is read-only, that means there's no known -# way to make the CPU model run in the current host. Implementations -# that choose not to provide specific information return the -# property name "type". -# If the property is read-write, it means that it MAY be possible -# to run the CPU model in the current host if that property is -# changed. Management software can use it as hints to suggest or -# choose an alternative for the user, or just to generate meaningful -# error messages explaining why the CPU model can't be used. -# If @unavailable-features is an empty list, the CPU model is -# runnable using the current host and machine-type. -# If @unavailable-features is not present, runnability -# information for the CPU is not available. -# -# Since: 1.2.0 +# Since: 4.0 ## -{ 'struct': 'CpuDefinitionInfo', - 'data': { 'name': 'str', '*migration-safe': 'bool', 'static': 'bool', - '*unavailable-features': [ 'str' ], 'typename': 'str' } } +{ 'struct': 'CurrentMachineParams', + 'data': { 'wakeup-suspend-support': 'bool'} } + +## +# @query-current-machine: +# +# Return information on the current virtual machine. +# +# Returns: CurrentMachineParams +# +# Since: 4.0 +## +{ 'command': 'query-current-machine', 'returns': 'CurrentMachineParams' } ## # @MemoryInfo: @@ -2061,7 +2070,7 @@ # # @plugged-memory: size of memory that can be hot-unplugged. This field # is omitted if target doesn't support memory hotplug -# (i.e. CONFIG_MEM_HOTPLUG not defined on build time). +# (i.e. CONFIG_MEM_DEVICE not defined at build time). # # Since: 2.11.0 ## @@ -2083,16 +2092,6 @@ ## { 'command': 'query-memory-size-summary', 'returns': 'MemoryInfo' } -## -# @query-cpu-definitions: -# -# Return a list of supported virtual CPU definitions -# -# Returns: a list of CpuDefInfo -# -# Since: 1.2.0 -## -{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] } ## # @CpuModelInfo: @@ -2121,11 +2120,11 @@ # @static: Expand to a static CPU model, a combination of a static base # model name and property delta changes. As the static base model will # never change, the expanded CPU model will be the same, independent of -# independent of QEMU version, machine type, machine options, and -# accelerator options. Therefore, the resulting model can be used by -# tooling without having to specify a compatibility machine - e.g. when -# displaying the "host" model. static CPU models are migration-safe. -# +# QEMU version, machine type, machine options, and accelerator options. +# Therefore, the resulting model can be used by tooling without having +# to specify a compatibility machine - e.g. when displaying the "host" +# model. The @static CPU models are migration-safe. + # @full: Expand all properties. The produced model is not guaranteed to be # migration-safe, but allows tooling to get an insight and work with # model details. @@ -2145,57 +2144,6 @@ 'data': [ 'static', 'full' ] } -## -# @CpuModelExpansionInfo: -# -# The result of a cpu model expansion. -# -# @model: the expanded CpuModelInfo. -# -# Since: 2.8.0 -## -{ 'struct': 'CpuModelExpansionInfo', - 'data': { 'model': 'CpuModelInfo' } } - - -## -# @query-cpu-model-expansion: -# -# Expands a given CPU model (or a combination of CPU model + additional options) -# to different granularities, allowing tooling to get an understanding what a -# specific CPU model looks like in QEMU under a certain configuration. -# -# This interface can be used to query the "host" CPU model. -# -# The data returned by this command may be affected by: -# -# * QEMU version: CPU models may look different depending on the QEMU version. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the machine-type. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, CPU models -# may look different depending on machine and accelerator options. (Except for -# CPU models reported as "static" in query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu option and -# global properties may affect expansion of CPU models. Using -# query-cpu-model-expansion while using these is not advised. -# -# Some architectures may not support all expansion types. s390x supports -# "full" and "static". -# -# Returns: a CpuModelExpansionInfo. Returns an error if expanding CPU models is -# not supported, if the model cannot be expanded, if the model contains -# an unknown CPU definition name, unknown properties or properties -# with a wrong type. Also returns an error if an expansion type is -# not supported. -# -# Since: 2.8.0 -## -{ 'command': 'query-cpu-model-expansion', - 'data': { 'type': 'CpuModelExpansionType', - 'model': 'CpuModelInfo' }, - 'returns': 'CpuModelExpansionInfo' } - ## # @CpuModelCompareResult: # @@ -2219,122 +2167,6 @@ { 'enum': 'CpuModelCompareResult', 'data': [ 'incompatible', 'identical', 'superset', 'subset' ] } -## -# @CpuModelCompareInfo: -# -# The result of a CPU model comparison. -# -# @result: The result of the compare operation. -# @responsible-properties: List of properties that led to the comparison result -# not being identical. -# -# @responsible-properties is a list of QOM property names that led to -# both CPUs not being detected as identical. For identical models, this -# list is empty. -# If a QOM property is read-only, that means there's no known way to make the -# CPU models identical. If the special property name "type" is included, the -# models are by definition not identical and cannot be made identical. -# -# Since: 2.8.0 -## -{ 'struct': 'CpuModelCompareInfo', - 'data': {'result': 'CpuModelCompareResult', - 'responsible-properties': ['str'] - } -} - -## -# @query-cpu-model-comparison: -# -# Compares two CPU models, returning how they compare in a specific -# configuration. The results indicates how both models compare regarding -# runnability. This result can be used by tooling to make decisions if a -# certain CPU model will run in a certain configuration or if a compatible -# CPU model has to be created by baselining. -# -# Usually, a CPU model is compared against the maximum possible CPU model -# of a certain configuration (e.g. the "host" model for KVM). If that CPU -# model is identical or a subset, it will run in that configuration. -# -# The result returned by this command may be affected by: -# -# * QEMU version: CPU models may look different depending on the QEMU version. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the machine-type. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, CPU models -# may look different depending on machine and accelerator options. (Except for -# CPU models reported as "static" in query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu option and -# global properties may affect expansion of CPU models. Using -# query-cpu-model-expansion while using these is not advised. -# -# Some architectures may not support comparing CPU models. s390x supports -# comparing CPU models. -# -# Returns: a CpuModelBaselineInfo. Returns an error if comparing CPU models is -# not supported, if a model cannot be used, if a model contains -# an unknown cpu definition name, unknown properties or properties -# with wrong types. -# -# Since: 2.8.0 -## -{ 'command': 'query-cpu-model-comparison', - 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, - 'returns': 'CpuModelCompareInfo' } - -## -# @CpuModelBaselineInfo: -# -# The result of a CPU model baseline. -# -# @model: the baselined CpuModelInfo. -# -# Since: 2.8.0 -## -{ 'struct': 'CpuModelBaselineInfo', - 'data': { 'model': 'CpuModelInfo' } } - -## -# @query-cpu-model-baseline: -# -# Baseline two CPU models, creating a compatible third model. The created -# model will always be a static, migration-safe CPU model (see "static" -# CPU model expansion for details). -# -# This interface can be used by tooling to create a compatible CPU model out -# two CPU models. The created CPU model will be identical to or a subset of -# both CPU models when comparing them. Therefore, the created CPU model is -# guaranteed to run where the given CPU models run. -# -# The result returned by this command may be affected by: -# -# * QEMU version: CPU models may look different depending on the QEMU version. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the machine-type. -# (Except for CPU models reported as "static" in query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, CPU models -# may look different depending on machine and accelerator options. (Except for -# CPU models reported as "static" in query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu option and -# global properties may affect expansion of CPU models. Using -# query-cpu-model-expansion while using these is not advised. -# -# Some architectures may not support baselining CPU models. s390x supports -# baselining CPU models. -# -# Returns: a CpuModelBaselineInfo. Returns an error if baselining CPU models is -# not supported, if a model cannot be used, if a model contains -# an unknown cpu definition name, unknown properties or properties -# with wrong types. -# -# Since: 2.8.0 -## -{ 'command': 'query-cpu-model-baseline', - 'data': { 'modela': 'CpuModelInfo', - 'modelb': 'CpuModelInfo' }, - 'returns': 'CpuModelBaselineInfo' } - ## # @AddfdInfo: # @@ -2376,7 +2208,9 @@ # <- { "return": { "fdset-id": 1, "fd": 3 } } # ## -{ 'command': 'add-fd', 'data': {'*fdset-id': 'int', '*opaque': 'str'}, +{ 'command': 'add-fd', + 'data': { '*fdset-id': 'int', + '*opaque': 'str' }, 'returns': 'AddfdInfo' } ## @@ -2648,7 +2482,8 @@ # } # ## -{'command': 'query-command-line-options', 'data': { '*option': 'str' }, +{'command': 'query-command-line-options', + 'data': { '*option': 'str' }, 'returns': ['CommandLineOptionInfo'], 'allow-preconfig': true } @@ -3041,46 +2876,6 @@ { 'event': 'ACPI_DEVICE_OST', 'data': { 'info': 'ACPIOSTInfo' } } -## -# @rtc-reset-reinjection: -# -# This command will reset the RTC interrupt reinjection backlog. -# Can be used if another mechanism to synchronize guest time -# is in effect, for example QEMU guest agent's guest-set-time -# command. -# -# Since: 2.1 -# -# Example: -# -# -> { "execute": "rtc-reset-reinjection" } -# <- { "return": {} } -# -## -{ 'command': 'rtc-reset-reinjection' } - -## -# @RTC_CHANGE: -# -# Emitted when the guest changes the RTC time. -# -# @offset: offset between base RTC clock (as specified by -rtc base), and -# new RTC clock value -# -# Note: This event is rate-limited. -# -# Since: 0.13.0 -# -# Example: -# -# <- { "event": "RTC_CHANGE", -# "data": { "offset": 78 }, -# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } -# -## -{ 'event': 'RTC_CHANGE', - 'data': { 'offset': 'int' } } - ## # @ReplayMode: # @@ -3120,49 +2915,6 @@ ## { 'command': 'xen-load-devices-state', 'data': {'filename': 'str'} } -## -# @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 -# -# Example: -# -# -> { "execute": "query-gic-capabilities" } -# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, -# { "version": 3, "emulated": false, "kernel": true } ] } -# -## -{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] } - ## # @CpuInstanceProperties: # @@ -3213,6 +2965,8 @@ ## # @query-hotpluggable-cpus: # +# TODO: Better documentation; currently there is none. +# # Returns: a list of HotpluggableCPU objects. # # Since: 2.7 @@ -3284,194 +3038,6 @@ ## { 'command': 'query-vm-generation-id', 'returns': 'GuidInfo' } - -## -# @SevState: -# -# An enumeration of SEV state information used during @query-sev. -# -# @uninit: The guest is uninitialized. -# -# @launch-update: The guest is currently being launched; plaintext data and -# register state is being imported. -# -# @launch-secret: The guest is currently being launched; ciphertext data -# is being imported. -# -# @running: The guest is fully launched or migrated in. -# -# @send-update: The guest is currently being migrated out to another machine. -# -# @receive-update: The guest is currently being migrated from another machine. -# -# Since: 2.12 -## -{ 'enum': 'SevState', - 'data': ['uninit', 'launch-update', 'launch-secret', 'running', - 'send-update', 'receive-update' ] } - -## -# @SevInfo: -# -# Information about Secure Encrypted Virtualization (SEV) support -# -# @enabled: true if SEV is active -# -# @api-major: SEV API major version -# -# @api-minor: SEV API minor version -# -# @build-id: SEV FW build id -# -# @policy: SEV policy value -# -# @state: SEV guest state -# -# @handle: SEV firmware handle -# -# Since: 2.12 -## -{ 'struct': 'SevInfo', - 'data': { 'enabled': 'bool', - 'api-major': 'uint8', - 'api-minor' : 'uint8', - 'build-id' : 'uint8', - 'policy' : 'uint32', - 'state' : 'SevState', - 'handle' : 'uint32' - } -} - -## -# @query-sev: -# -# Returns information about SEV -# -# Returns: @SevInfo -# -# Since: 2.12 -# -# Example: -# -# -> { "execute": "query-sev" } -# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, -# "build-id" : 0, "policy" : 0, "state" : "running", -# "handle" : 1 } } -# -## -{ 'command': 'query-sev', 'returns': 'SevInfo' } - -## -# @SevLaunchMeasureInfo: -# -# SEV Guest Launch measurement information -# -# @data: the measurement value encoded in base64 -# -# Since: 2.12 -# -## -{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'} } - -## -# @query-sev-launch-measure: -# -# Query the SEV guest launch information. -# -# Returns: The @SevLaunchMeasureInfo for the guest -# -# Since: 2.12 -# -# Example: -# -# -> { "execute": "query-sev-launch-measure" } -# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } -# -## -{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo' } - -## -# @SevCapability: -# -# The struct describes capability for a Secure Encrypted Virtualization -# feature. -# -# @pdh: Platform Diffie-Hellman key (base64 encoded) -# -# @cert-chain: PDH certificate chain (base64 encoded) -# -# @cbitpos: C-bit location in page table entry -# -# @reduced-phys-bits: Number of physical Address bit reduction when SEV is -# enabled -# -# Since: 2.12 -## -{ 'struct': 'SevCapability', - 'data': { 'pdh': 'str', - 'cert-chain': 'str', - 'cbitpos': 'int', - 'reduced-phys-bits': 'int'} } - -## -# @query-sev-capabilities: -# -# This command is used to get the SEV capabilities, and is supported on AMD -# X86 platforms only. -# -# Returns: SevCapability objects. -# -# Since: 2.12 -# -# Example: -# -# -> { "execute": "query-sev-capabilities" } -# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", -# "cbitpos": 47, "reduced-phys-bits": 5}} -# -## -{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability' } - -## -# @CommandDropReason: -# -# Reasons that caused one command to be dropped. -# -# @queue-full: the command queue is full. This can only occur when -# the client sends a new non-oob command before the -# response to the previous non-oob command has been -# received. -# -# Since: 2.12 -## -{ 'enum': 'CommandDropReason', - 'data': [ 'queue-full' ] } - -## -# @COMMAND_DROPPED: -# -# Emitted when a command is dropped due to some reason. Commands can -# only be dropped when the oob capability is enabled. -# -# @id: The dropped command's "id" field. -# FIXME Broken by design. Events are broadcast to all monitors. If -# another monitor's client has a command with the same ID in flight, -# the event will incorrectly claim that command was dropped. -# -# @reason: The reason why the command is dropped. -# -# Since: 2.12 -# -# Example: -# -# { "event": "COMMAND_DROPPED", -# "data": {"result": {"id": "libvirt-102", -# "reason": "queue-full" } } } -# -## -{ 'event': 'COMMAND_DROPPED' , - 'data': { 'id': 'any', 'reason': 'CommandDropReason' } } - ## # @set-numa-node: # diff --git a/qapi/net.json b/qapi/net.json index c86f351161..5f7bff1637 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -174,6 +174,8 @@ # # @guestfwd: forward guest TCP connections # +# @tftp-server-name: RFC2132 "TFTP server name" string (Since 3.1) +# # Since: 1.2 ## { 'struct': 'NetdevUserOptions', @@ -198,7 +200,8 @@ '*smb': 'str', '*smbserver': 'str', '*hostfwd': ['String'], - '*guestfwd': ['String'] } } + '*guestfwd': ['String'], + '*tftp-server-name': 'str' } } ## # @NetdevTapOptions: @@ -654,7 +657,8 @@ # } # ## -{ 'command': 'query-rx-filter', 'data': { '*name': 'str' }, +{ 'command': 'query-rx-filter', + 'data': { '*name': 'str' }, 'returns': ['RxFilterInfo'] } ## @@ -680,3 +684,46 @@ ## { 'event': 'NIC_RX_FILTER_CHANGED', 'data': { '*name': 'str', 'path': 'str' } } + +## +# @AnnounceParameters: +# +# Parameters for self-announce timers +# +# @initial: Initial delay (in ms) before sending the first GARP/RARP +# announcement +# +# @max: Maximum delay (in ms) between GARP/RARP announcement packets +# +# @rounds: Number of self-announcement attempts +# +# @step: Delay increase (in ms) after each self-announcement attempt +# +# Since: 4.0 +## + +{ 'struct': 'AnnounceParameters', + 'data': { 'initial': 'int', + 'max': 'int', + 'rounds': 'int', + 'step': 'int' } } + +## +# @announce-self: +# +# Trigger generation of broadcast RARP frames to update network switches. +# This can be useful when network bonds fail-over the active slave. +# +# @params: AnnounceParameters giving timing and repetition count of announce +# +# Example: +# +# -> { "execute": "announce-self" +# "arguments": { +# "initial": 50, "max": 550, "rounds": 10, "step": 50 } } +# <- { "return": {} } +# +# Since: 4.0 +## +{ 'command': 'announce-self', 'boxed': true, + 'data' : 'AnnounceParameters'} diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 65b6dc2f6f..4bd1223637 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -63,13 +63,15 @@ 'query-tpm-types', 'ringbuf-read' ], 'name-case-whitelist': [ - '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 + 'ACPISlotType', # DIMM, visible through query-acpi-ospm-status + 'CpuInfoMIPS', # PC, visible through query-cpu + 'CpuInfoTricore', # PC, visible through query-cpu + 'BlockdevVmdkSubformat', # all members, to match VMDK spec spellings + 'BlockdevVmdkAdapterType', # legacyESX, to match VMDK spec spellings + '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 ] } } # Documentation generated with qapi-gen.py is in source order, with @@ -86,11 +88,15 @@ { 'include': 'char.json' } { 'include': 'job.json' } { 'include': 'net.json' } +{ 'include': 'rdma.json' } { 'include': 'rocker.json' } { 'include': 'tpm.json' } { 'include': 'ui.json' } +{ 'include': 'authz.json' } { 'include': 'migration.json' } { 'include': 'transaction.json' } { 'include': 'trace.json' } { 'include': 'introspect.json' } { 'include': 'misc.json' } +{ 'include': 'target.json' } +{ 'include': 'audio.json' } diff --git a/qapi/qmp-dispatch.c b/qapi/qmp-dispatch.c index 6f2d466596..e2c366e09e 100644 --- a/qapi/qmp-dispatch.c +++ b/qapi/qmp-dispatch.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/qmp/dispatch.h" -#include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" @@ -59,6 +58,8 @@ static QDict *qmp_dispatch_check_obj(const QObject *request, bool allow_oob, "QMP input member 'arguments' must be an object"); return NULL; } + } else if (!strcmp(arg_name, "id")) { + continue; } else { error_setg(errp, "QMP input member '%s' is unexpected", arg_name); @@ -110,7 +111,7 @@ static QObject *do_qmp_dispatch(QmpCommandList *cmds, QObject *request, if (oob && !(cmd->options & QCO_ALLOW_OOB)) { error_setg(errp, "The command %s does not support OOB", command); - return false; + return NULL; } if (runstate_check(RUN_STATE_PRECONFIG) && @@ -156,7 +157,7 @@ QDict *qmp_error_response(Error *err) /* * Does @qdict look like a command to be run out-of-band? */ -bool qmp_is_oob(QDict *dict) +bool qmp_is_oob(const QDict *dict) { return qdict_haskey(dict, "exec-oob") && !qdict_haskey(dict, "execute"); @@ -166,11 +167,11 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, bool allow_oob) { Error *err = NULL; - QObject *ret; + QDict *dict = qobject_to(QDict, request); + QObject *ret, *id = dict ? qdict_get(dict, "id") : NULL; QDict *rsp; ret = do_qmp_dispatch(cmds, request, allow_oob, &err); - if (err) { rsp = qmp_error_response(err); } else if (ret) { @@ -181,5 +182,9 @@ QDict *qmp_dispatch(QmpCommandList *cmds, QObject *request, rsp = NULL; } + if (rsp && id) { + qdict_put_obj(rsp, "id", qobject_ref(id)); + } + return rsp; } diff --git a/qapi/qmp-event.c b/qapi/qmp-event.c index 5b8854043e..81ddd5331f 100644 --- a/qapi/qmp-event.c +++ b/qapi/qmp-event.c @@ -19,18 +19,6 @@ #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" -static QMPEventFuncEmit qmp_emit; - -void qmp_event_set_func_emit(QMPEventFuncEmit emit) -{ - qmp_emit = emit; -} - -QMPEventFuncEmit qmp_event_get_func_emit(void) -{ - return qmp_emit; -} - static void timestamp_put(QDict *qdict) { int err; diff --git a/qapi/qmp-registry.c b/qapi/qmp-registry.c index 5af484cd9a..ca00f74795 100644 --- a/qapi/qmp-registry.c +++ b/qapi/qmp-registry.c @@ -27,14 +27,6 @@ void qmp_register_command(QmpCommandList *cmds, const char *name, QTAILQ_INSERT_TAIL(cmds, cmd, node); } -void qmp_unregister_command(QmpCommandList *cmds, const char *name) -{ - QmpCommand *cmd = qmp_find_command(cmds, name); - - QTAILQ_REMOVE(cmds, cmd, node); - g_free(cmd); -} - QmpCommand *qmp_find_command(QmpCommandList *cmds, const char *name) { QmpCommand *cmd; diff --git a/qapi/qobject-input-visitor.c b/qapi/qobject-input-visitor.c index da57f4cc24..07465f9947 100644 --- a/qapi/qobject-input-visitor.c +++ b/qapi/qobject-input-visitor.c @@ -562,19 +562,20 @@ static void qobject_input_type_number_keyval(Visitor *v, const char *name, { QObjectInputVisitor *qiv = to_qiv(v); const char *str = qobject_input_get_keyval(qiv, name, errp); - char *endp; + double val; if (!str) { return; } - errno = 0; - *obj = strtod(str, &endp); - if (errno || endp == str || *endp || !isfinite(*obj)) { + if (qemu_strtod_finite(str, NULL, &val)) { /* TODO report -ERANGE more nicely */ error_setg(errp, QERR_INVALID_PARAMETER_TYPE, full_name(qiv, name), "number"); + return; } + + *obj = val; } static void qobject_input_type_any(Visitor *v, const char *name, QObject **obj, @@ -725,11 +726,6 @@ Visitor *qobject_input_visitor_new_str(const char *str, if (is_json) { obj = qobject_from_json(str, errp); if (!obj) { - /* Work around qobject_from_json() lossage TODO fix that */ - if (errp && !*errp) { - error_setg(errp, "JSON parse error"); - return NULL; - } return NULL; } args = qobject_to(QDict, obj); diff --git a/qapi/rdma.json b/qapi/rdma.json new file mode 100644 index 0000000000..b58105b1b6 --- /dev/null +++ b/qapi/rdma.json @@ -0,0 +1,38 @@ +# -*- Mode: Python -*- +# + +## +# = RDMA device +## + +## +# @RDMA_GID_STATUS_CHANGED: +# +# Emitted when guest driver adds/deletes GID to/from device +# +# @netdev: RoCE Network Device name +# +# @gid-status: Add or delete indication +# +# @subnet-prefix: Subnet Prefix +# +# @interface-id : Interface ID +# +# Since: 4.0 +# +# Example: +# +# <- {"timestamp": {"seconds": 1541579657, "microseconds": 986760}, +# "event": "RDMA_GID_STATUS_CHANGED", +# "data": +# {"netdev": "bridge0", +# "interface-id": 15880512517475447892, +# "gid-status": true, +# "subnet-prefix": 33022}} +# +## +{ 'event': 'RDMA_GID_STATUS_CHANGED', + 'data': { 'netdev' : 'str', + 'gid-status' : 'bool', + 'subnet-prefix' : 'uint64', + 'interface-id' : 'uint64' } } diff --git a/qapi/run-state.json b/qapi/run-state.json index 332e44897b..d7477cd715 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -59,6 +59,42 @@ 'running', 'save-vm', 'shutdown', 'suspended', 'watchdog', 'guest-panicked', 'colo', 'preconfig' ] } +## +# @ShutdownCause: +# +# An enumeration of reasons for a Shutdown. +# +# @none: No shutdown request pending +# +# @host-error: An error prevents further use of guest +# +# @host-qmp-quit: Reaction to the QMP command 'quit' +# +# @host-qmp-system-reset: Reaction to the QMP command 'system_reset' +# +# @host-signal: Reaction to a signal, such as SIGINT +# +# @host-ui: Reaction to a UI event, like window close +# +# @guest-shutdown: Guest shutdown/suspend request, via ACPI or other +# hardware-specific means +# +# @guest-reset: Guest reset request, and command line turns that into +# a shutdown +# +# @guest-panic: Guest panicked, and command line turns that into a shutdown +# +# @subsystem-reset: Partial guest reset that does not trigger QMP events and +# ignores --no-reboot. This is useful for sanitizing +# hypercalls on s390 that are used during kexec/kdump/boot +# +## +{ 'enum': 'ShutdownCause', + # Beware, shutdown_caused_by_guest() depends on enumeration order + 'data': [ 'none', 'host-error', 'host-qmp-quit', 'host-qmp-system-reset', + 'host-signal', 'host-ui', 'guest-shutdown', 'guest-reset', + 'guest-panic', 'subsystem-reset'] } + ## # @StatusInfo: # @@ -107,6 +143,8 @@ # a guest-initiated ACPI shutdown request or other hardware-specific action) # rather than a host request (such as sending qemu a SIGINT). (since 2.10) # +# @reason: The @ShutdownCause which resulted in the SHUTDOWN. (since 4.0) +# # Note: If the command-line option "-no-shutdown" has been specified, qemu will # not exit, and a STOP event will eventually follow the SHUTDOWN event # @@ -118,7 +156,7 @@ # "timestamp": { "seconds": 1267040730, "microseconds": 682951 } } # ## -{ 'event': 'SHUTDOWN', 'data': { 'guest': 'bool' } } +{ 'event': 'SHUTDOWN', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } ## # @POWERDOWN: @@ -146,6 +184,8 @@ # rather than a host request (such as the QMP command system_reset). # (since 2.10) # +# @reason: The @ShutdownCause of the RESET. (since 4.0) +# # Since: 0.12.0 # # Example: @@ -154,7 +194,7 @@ # "timestamp": { "seconds": 1267041653, "microseconds": 9518 } } # ## -{ 'event': 'RESET', 'data': { 'guest': 'bool' } } +{ 'event': 'RESET', 'data': { 'guest': 'bool', 'reason': 'ShutdownCause' } } ## # @STOP: diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c index b3fdd0827d..bd92080667 100644 --- a/qapi/string-input-visitor.c +++ b/qapi/string-input-visitor.c @@ -4,10 +4,10 @@ * Copyright Red Hat, Inc. 2012-2016 * * Author: Paolo Bonzini + * David Hildenbrand * * 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" @@ -18,20 +18,42 @@ #include "qapi/qmp/qerror.h" #include "qapi/qmp/qnull.h" #include "qemu/option.h" -#include "qemu/queue.h" -#include "qemu/range.h" +#include "qemu/cutils.h" +typedef enum ListMode { + /* no list parsing active / no list expected */ + LM_NONE, + /* we have an unparsed string remaining */ + LM_UNPARSED, + /* we have an unfinished int64 range */ + LM_INT64_RANGE, + /* we have an unfinished uint64 range */ + LM_UINT64_RANGE, + /* we have parsed the string completely and no range is remaining */ + LM_END, +} ListMode; + +/* protect against DOS attacks, limit the amount of elements per range */ +#define RANGE_MAX_ELEMENTS 65536 + +typedef union RangeElement { + int64_t i64; + uint64_t u64; +} RangeElement; struct StringInputVisitor { Visitor visitor; - GList *ranges; - GList *cur_range; - int64_t cur; + /* List parsing state */ + ListMode lm; + RangeElement rangeNext; + RangeElement rangeEnd; + const char *unparsed_string; + void *list; + /* The original string to parse */ const char *string; - void *list; /* Only needed for sanity checking the caller */ }; static StringInputVisitor *to_siv(Visitor *v) @@ -39,136 +61,42 @@ static StringInputVisitor *to_siv(Visitor *v) return container_of(v, StringInputVisitor, visitor); } -static void free_range(void *range, void *dummy) -{ - g_free(range); -} - -static int parse_str(StringInputVisitor *siv, const char *name, Error **errp) -{ - char *str = (char *) siv->string; - long long start, end; - Range *cur; - char *endptr; - - if (siv->ranges) { - return 0; - } - - if (!*str) { - return 0; - } - - do { - errno = 0; - start = strtoll(str, &endptr, 0); - if (errno == 0 && endptr > str) { - if (*endptr == '\0') { - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, start); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - str = NULL; - } else if (*endptr == '-') { - str = endptr + 1; - errno = 0; - end = strtoll(str, &endptr, 0); - if (errno == 0 && endptr > str && start <= end && - (start > INT64_MAX - 65536 || - end < start + 65536)) { - if (*endptr == '\0') { - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, end); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - str = NULL; - } else if (*endptr == ',') { - str = endptr + 1; - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, end); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - } else { - goto error; - } - } else { - goto error; - } - } else if (*endptr == ',') { - str = endptr + 1; - cur = g_malloc0(sizeof(*cur)); - range_set_bounds(cur, start, start); - siv->ranges = range_list_insert(siv->ranges, cur); - cur = NULL; - } else { - goto error; - } - } else { - goto error; - } - } while (str); - - return 0; -error: - g_list_foreach(siv->ranges, free_range, NULL); - g_list_free(siv->ranges); - siv->ranges = NULL; - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "an int64 value or range"); - return -1; -} - -static void -start_list(Visitor *v, const char *name, GenericList **list, size_t size, - Error **errp) +static void start_list(Visitor *v, const char *name, GenericList **list, + size_t size, Error **errp) { StringInputVisitor *siv = to_siv(v); - /* We don't support visits without a list */ - assert(list); + assert(siv->lm == LM_NONE); siv->list = list; + siv->unparsed_string = siv->string; - if (parse_str(siv, name, errp) < 0) { - *list = NULL; - return; - } - - siv->cur_range = g_list_first(siv->ranges); - if (siv->cur_range) { - Range *r = siv->cur_range->data; - if (r) { - siv->cur = range_lob(r); + if (!siv->string[0]) { + if (list) { + *list = NULL; } - *list = g_malloc0(size); + siv->lm = LM_END; } else { - *list = NULL; + if (list) { + *list = g_malloc0(size); + } + siv->lm = LM_UNPARSED; } } static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) { StringInputVisitor *siv = to_siv(v); - Range *r; - if (!siv->ranges || !siv->cur_range) { + switch (siv->lm) { + case LM_END: return NULL; - } - - r = siv->cur_range->data; - if (!r) { - return NULL; - } - - if (!range_contains(r, siv->cur)) { - siv->cur_range = g_list_next(siv->cur_range); - if (!siv->cur_range) { - return NULL; - } - r = siv->cur_range->data; - if (!r) { - return NULL; - } - siv->cur = range_lob(r); + case LM_INT64_RANGE: + case LM_UINT64_RANGE: + case LM_UNPARSED: + /* we have an unparsed string or something left in a range */ + break; + default: + abort(); } tail->next = g_malloc0(size); @@ -178,88 +106,208 @@ static GenericList *next_list(Visitor *v, GenericList *tail, size_t size) static void check_list(Visitor *v, Error **errp) { const StringInputVisitor *siv = to_siv(v); - Range *r; - GList *cur_range; - if (!siv->ranges || !siv->cur_range) { + switch (siv->lm) { + case LM_INT64_RANGE: + case LM_UINT64_RANGE: + case LM_UNPARSED: + error_setg(errp, "Fewer list elements expected"); return; - } - - r = siv->cur_range->data; - if (!r) { + case LM_END: return; + default: + abort(); } - - if (!range_contains(r, siv->cur)) { - cur_range = g_list_next(siv->cur_range); - if (!cur_range) { - return; - } - r = cur_range->data; - if (!r) { - return; - } - } - - error_setg(errp, "Range contains too many values"); } static void end_list(Visitor *v, void **obj) { StringInputVisitor *siv = to_siv(v); + assert(siv->lm != LM_NONE); assert(siv->list == obj); + siv->list = NULL; + siv->unparsed_string = NULL; + siv->lm = LM_NONE; +} + +static int try_parse_int64_list_entry(StringInputVisitor *siv, int64_t *obj) +{ + const char *endptr; + int64_t start, end; + + /* parse a simple int64 or range */ + if (qemu_strtoi64(siv->unparsed_string, &endptr, 0, &start)) { + return -EINVAL; + } + end = start; + + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + case '-': + /* parse the end of the range */ + if (qemu_strtoi64(endptr + 1, &endptr, 0, &end)) { + return -EINVAL; + } + if (start > end || end - start >= RANGE_MAX_ELEMENTS) { + return -EINVAL; + } + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + /* we have a proper range (with maybe only one element) */ + siv->lm = LM_INT64_RANGE; + siv->rangeNext.i64 = start; + siv->rangeEnd.i64 = end; + return 0; } static void parse_type_int64(Visitor *v, const char *name, int64_t *obj, Error **errp) { StringInputVisitor *siv = to_siv(v); + int64_t val; - if (parse_str(siv, name, errp) < 0) { + switch (siv->lm) { + case LM_NONE: + /* just parse a simple int64, bail out if not completely consumed */ + if (qemu_strtoi64(siv->string, NULL, 0, &val)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, + name ? name : "null", "int64"); + return; + } + *obj = val; return; - } - - if (!siv->ranges) { - goto error; - } - - if (!siv->cur_range) { - Range *r; - - siv->cur_range = g_list_first(siv->ranges); - if (!siv->cur_range) { - goto error; + case LM_UNPARSED: + if (try_parse_int64_list_entry(siv, obj)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "list of int64 values or ranges"); + return; } + assert(siv->lm == LM_INT64_RANGE); + /* fall through */ + case LM_INT64_RANGE: + /* return the next element in the range */ + assert(siv->rangeNext.i64 <= siv->rangeEnd.i64); + *obj = siv->rangeNext.i64++; - r = siv->cur_range->data; - if (!r) { - goto error; + if (siv->rangeNext.i64 > siv->rangeEnd.i64 || *obj == INT64_MAX) { + /* end of range, check if there is more to parse */ + siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; } + return; + case LM_END: + error_setg(errp, "Fewer list elements expected"); + return; + default: + abort(); + } +} - siv->cur = range_lob(r); +static int try_parse_uint64_list_entry(StringInputVisitor *siv, uint64_t *obj) +{ + const char *endptr; + uint64_t start, end; + + /* parse a simple uint64 or range */ + if (qemu_strtou64(siv->unparsed_string, &endptr, 0, &start)) { + return -EINVAL; + } + end = start; + + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + case '-': + /* parse the end of the range */ + if (qemu_strtou64(endptr + 1, &endptr, 0, &end)) { + return -EINVAL; + } + if (start > end || end - start >= RANGE_MAX_ELEMENTS) { + return -EINVAL; + } + switch (endptr[0]) { + case '\0': + siv->unparsed_string = endptr; + break; + case ',': + siv->unparsed_string = endptr + 1; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; } - *obj = siv->cur; - siv->cur++; - return; - -error: - error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", - "an int64 value or range"); + /* we have a proper range (with maybe only one element) */ + siv->lm = LM_UINT64_RANGE; + siv->rangeNext.u64 = start; + siv->rangeEnd.u64 = end; + return 0; } 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; + StringInputVisitor *siv = to_siv(v); + uint64_t val; + + switch (siv->lm) { + case LM_NONE: + /* just parse a simple uint64, bail out if not completely consumed */ + if (qemu_strtou64(siv->string, NULL, 0, &val)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "uint64"); + return; + } + *obj = val; + return; + case LM_UNPARSED: + if (try_parse_uint64_list_entry(siv, obj)) { + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, name ? name : "null", + "list of uint64 values or ranges"); + return; + } + assert(siv->lm == LM_UINT64_RANGE); + /* fall through */ + case LM_UINT64_RANGE: + /* return the next element in the range */ + assert(siv->rangeNext.u64 <= siv->rangeEnd.u64); + *obj = siv->rangeNext.u64++; + + if (siv->rangeNext.u64 > siv->rangeEnd.u64 || *obj == UINT64_MAX) { + /* end of range, check if there is more to parse */ + siv->lm = siv->unparsed_string[0] ? LM_UNPARSED : LM_END; + } + return; + case LM_END: + error_setg(errp, "Fewer list elements expected"); + return; + default: + abort(); } } @@ -270,6 +318,7 @@ static void parse_type_size(Visitor *v, const char *name, uint64_t *obj, Error *err = NULL; uint64_t val; + assert(siv->lm == LM_NONE); parse_option_size(name, siv->string, &val, &err); if (err) { error_propagate(errp, err); @@ -284,6 +333,7 @@ static void parse_type_bool(Visitor *v, const char *name, bool *obj, { StringInputVisitor *siv = to_siv(v); + assert(siv->lm == LM_NONE); if (!strcasecmp(siv->string, "on") || !strcasecmp(siv->string, "yes") || !strcasecmp(siv->string, "true")) { @@ -306,6 +356,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj, { StringInputVisitor *siv = to_siv(v); + assert(siv->lm == LM_NONE); *obj = g_strdup(siv->string); } @@ -313,12 +364,10 @@ static void parse_type_number(Visitor *v, const char *name, double *obj, Error **errp) { StringInputVisitor *siv = to_siv(v); - char *endp = (char *) siv->string; double val; - errno = 0; - val = strtod(siv->string, &endp); - if (errno || endp == siv->string || *endp) { + assert(siv->lm == LM_NONE); + if (qemu_strtod_finite(siv->string, NULL, &val)) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "number"); return; @@ -332,9 +381,10 @@ static void parse_type_null(Visitor *v, const char *name, QNull **obj, { StringInputVisitor *siv = to_siv(v); + assert(siv->lm == LM_NONE); *obj = NULL; - if (!siv->string || siv->string[0]) { + if (siv->string[0]) { error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null", "null"); return; @@ -347,8 +397,6 @@ static void string_input_free(Visitor *v) { StringInputVisitor *siv = to_siv(v); - g_list_foreach(siv->ranges, free_range, NULL); - g_list_free(siv->ranges); g_free(siv); } @@ -374,5 +422,6 @@ Visitor *string_input_visitor_new(const char *str) v->visitor.free = string_input_free; v->string = str; + v->lm = LM_NONE; return &v->visitor; } diff --git a/qapi/target.json b/qapi/target.json new file mode 100644 index 0000000000..1d4d54b600 --- /dev/null +++ b/qapi/target.json @@ -0,0 +1,514 @@ +# -*- Mode: Python -*- +# + +## +# = Target-specific commands & events +## + +{ 'include': 'misc.json' } + +## +# @RTC_CHANGE: +# +# Emitted when the guest changes the RTC time. +# +# @offset: offset between base RTC clock (as specified by -rtc base), and +# new RTC clock value +# +# Note: This event is rate-limited. +# +# Since: 0.13.0 +# +# Example: +# +# <- { "event": "RTC_CHANGE", +# "data": { "offset": 78 }, +# "timestamp": { "seconds": 1267020223, "microseconds": 435656 } } +# +## +{ 'event': 'RTC_CHANGE', + 'data': { 'offset': 'int' }, + 'if': 'defined(TARGET_ALPHA) || defined(TARGET_ARM) || defined(TARGET_HPPA) || defined(TARGET_I386) || defined(TARGET_MIPS) || defined(TARGET_MIPS64) || defined(TARGET_MOXIE) || defined(TARGET_PPC) || defined(TARGET_PPC64) || defined(TARGET_S390X) || defined(TARGET_SH4) || defined(TARGET_SPARC)' } + +## +# @rtc-reset-reinjection: +# +# This command will reset the RTC interrupt reinjection backlog. +# Can be used if another mechanism to synchronize guest time +# is in effect, for example QEMU guest agent's guest-set-time +# command. +# +# Since: 2.1 +# +# Example: +# +# -> { "execute": "rtc-reset-reinjection" } +# <- { "return": {} } +# +## +{ 'command': 'rtc-reset-reinjection', + 'if': 'defined(TARGET_I386)' } + + +## +# @SevState: +# +# An enumeration of SEV state information used during @query-sev. +# +# @uninit: The guest is uninitialized. +# +# @launch-update: The guest is currently being launched; plaintext data and +# register state is being imported. +# +# @launch-secret: The guest is currently being launched; ciphertext data +# is being imported. +# +# @running: The guest is fully launched or migrated in. +# +# @send-update: The guest is currently being migrated out to another machine. +# +# @receive-update: The guest is currently being migrated from another machine. +# +# Since: 2.12 +## +{ 'enum': 'SevState', + 'data': ['uninit', 'launch-update', 'launch-secret', 'running', + 'send-update', 'receive-update' ], + 'if': 'defined(TARGET_I386)' } + +## +# @SevInfo: +# +# Information about Secure Encrypted Virtualization (SEV) support +# +# @enabled: true if SEV is active +# +# @api-major: SEV API major version +# +# @api-minor: SEV API minor version +# +# @build-id: SEV FW build id +# +# @policy: SEV policy value +# +# @state: SEV guest state +# +# @handle: SEV firmware handle +# +# Since: 2.12 +## +{ 'struct': 'SevInfo', + 'data': { 'enabled': 'bool', + 'api-major': 'uint8', + 'api-minor' : 'uint8', + 'build-id' : 'uint8', + 'policy' : 'uint32', + 'state' : 'SevState', + 'handle' : 'uint32' + }, + 'if': 'defined(TARGET_I386)' +} + +## +# @query-sev: +# +# Returns information about SEV +# +# Returns: @SevInfo +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev" } +# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, +# "build-id" : 0, "policy" : 0, "state" : "running", +# "handle" : 1 } } +# +## +{ 'command': 'query-sev', 'returns': 'SevInfo', + 'if': 'defined(TARGET_I386)' } + + +## +# @SevLaunchMeasureInfo: +# +# SEV Guest Launch measurement information +# +# @data: the measurement value encoded in base64 +# +# Since: 2.12 +# +## +{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'}, + 'if': 'defined(TARGET_I386)' } + +## +# @query-sev-launch-measure: +# +# Query the SEV guest launch information. +# +# Returns: The @SevLaunchMeasureInfo for the guest +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } +# +## +{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo', + 'if': 'defined(TARGET_I386)' } + + +## +# @SevCapability: +# +# The struct describes capability for a Secure Encrypted Virtualization +# feature. +# +# @pdh: Platform Diffie-Hellman key (base64 encoded) +# +# @cert-chain: PDH certificate chain (base64 encoded) +# +# @cbitpos: C-bit location in page table entry +# +# @reduced-phys-bits: Number of physical Address bit reduction when SEV is +# enabled +# +# Since: 2.12 +## +{ 'struct': 'SevCapability', + 'data': { 'pdh': 'str', + 'cert-chain': 'str', + 'cbitpos': 'int', + 'reduced-phys-bits': 'int'}, + 'if': 'defined(TARGET_I386)' } + +## +# @query-sev-capabilities: +# +# This command is used to get the SEV capabilities, and is supported on AMD +# X86 platforms only. +# +# Returns: SevCapability objects. +# +# Since: 2.12 +# +# Example: +# +# -> { "execute": "query-sev-capabilities" } +# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", +# "cbitpos": 47, "reduced-phys-bits": 5}} +# +## +{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability', + 'if': 'defined(TARGET_I386)' } + +## +# @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 +# +# Example: +# +# -> { "execute": "dump-skeys", +# "arguments": { "filename": "/tmp/skeys" } } +# <- { "return": {} } +# +## +{ 'command': 'dump-skeys', + 'data': { 'filename': 'str' }, + 'if': 'defined(TARGET_S390X)' } + +## +# @CpuModelBaselineInfo: +# +# The result of a CPU model baseline. +# +# @model: the baselined CpuModelInfo. +# +# Since: 2.8.0 +## +{ 'struct': 'CpuModelBaselineInfo', + 'data': { 'model': 'CpuModelInfo' }, + 'if': 'defined(TARGET_S390X)' } + +## +# @CpuModelCompareInfo: +# +# The result of a CPU model comparison. +# +# @result: The result of the compare operation. +# @responsible-properties: List of properties that led to the comparison result +# not being identical. +# +# @responsible-properties is a list of QOM property names that led to +# both CPUs not being detected as identical. For identical models, this +# list is empty. +# If a QOM property is read-only, that means there's no known way to make the +# CPU models identical. If the special property name "type" is included, the +# models are by definition not identical and cannot be made identical. +# +# Since: 2.8.0 +## +{ 'struct': 'CpuModelCompareInfo', + 'data': { 'result': 'CpuModelCompareResult', + 'responsible-properties': ['str'] }, + 'if': 'defined(TARGET_S390X)' } + +## +# @query-cpu-model-comparison: +# +# Compares two CPU models, returning how they compare in a specific +# configuration. The results indicates how both models compare regarding +# runnability. This result can be used by tooling to make decisions if a +# certain CPU model will run in a certain configuration or if a compatible +# CPU model has to be created by baselining. +# +# Usually, a CPU model is compared against the maximum possible CPU model +# of a certain configuration (e.g. the "host" model for KVM). If that CPU +# model is identical or a subset, it will run in that configuration. +# +# The result returned by this command may be affected by: +# +# * QEMU version: CPU models may look different depending on the QEMU version. +# (Except for CPU models reported as "static" in query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the machine-type. +# (Except for CPU models reported as "static" in query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, CPU models +# may look different depending on machine and accelerator options. (Except for +# CPU models reported as "static" in query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu option and +# global properties may affect expansion of CPU models. Using +# query-cpu-model-expansion while using these is not advised. +# +# Some architectures may not support comparing CPU models. s390x supports +# comparing CPU models. +# +# Returns: a CpuModelBaselineInfo. Returns an error if comparing CPU models is +# not supported, if a model cannot be used, if a model contains +# an unknown cpu definition name, unknown properties or properties +# with wrong types. +# +# Note: this command isn't specific to s390x, but is only implemented +# on this architecture currently. +# +# Since: 2.8.0 +## +{ 'command': 'query-cpu-model-comparison', + 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, + 'returns': 'CpuModelCompareInfo', + 'if': 'defined(TARGET_S390X)' } + +## +# @query-cpu-model-baseline: +# +# Baseline two CPU models, creating a compatible third model. The created +# model will always be a static, migration-safe CPU model (see "static" +# CPU model expansion for details). +# +# This interface can be used by tooling to create a compatible CPU model out +# two CPU models. The created CPU model will be identical to or a subset of +# both CPU models when comparing them. Therefore, the created CPU model is +# guaranteed to run where the given CPU models run. +# +# The result returned by this command may be affected by: +# +# * QEMU version: CPU models may look different depending on the QEMU version. +# (Except for CPU models reported as "static" in query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the machine-type. +# (Except for CPU models reported as "static" in query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, CPU models +# may look different depending on machine and accelerator options. (Except for +# CPU models reported as "static" in query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu option and +# global properties may affect expansion of CPU models. Using +# query-cpu-model-expansion while using these is not advised. +# +# Some architectures may not support baselining CPU models. s390x supports +# baselining CPU models. +# +# Returns: a CpuModelBaselineInfo. Returns an error if baselining CPU models is +# not supported, if a model cannot be used, if a model contains +# an unknown cpu definition name, unknown properties or properties +# with wrong types. +# +# Note: this command isn't specific to s390x, but is only implemented +# on this architecture currently. +# +# Since: 2.8.0 +## +{ 'command': 'query-cpu-model-baseline', + 'data': { 'modela': 'CpuModelInfo', + 'modelb': 'CpuModelInfo' }, + 'returns': 'CpuModelBaselineInfo', + 'if': 'defined(TARGET_S390X)' } + +## +# @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' }, + 'if': 'defined(TARGET_ARM)' } + +## +# @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 +# +# Example: +# +# -> { "execute": "query-gic-capabilities" } +# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, +# { "version": 3, "emulated": false, "kernel": true } ] } +# +## +{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'], + 'if': 'defined(TARGET_ARM)' } + +## +# @CpuModelExpansionInfo: +# +# The result of a cpu model expansion. +# +# @model: the expanded CpuModelInfo. +# +# Since: 2.8.0 +## +{ 'struct': 'CpuModelExpansionInfo', + 'data': { 'model': 'CpuModelInfo' }, + 'if': 'defined(TARGET_S390X) || defined(TARGET_I386)' } + +## +# @query-cpu-model-expansion: +# +# Expands a given CPU model (or a combination of CPU model + additional options) +# to different granularities, allowing tooling to get an understanding what a +# specific CPU model looks like in QEMU under a certain configuration. +# +# This interface can be used to query the "host" CPU model. +# +# The data returned by this command may be affected by: +# +# * QEMU version: CPU models may look different depending on the QEMU version. +# (Except for CPU models reported as "static" in query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the machine-type. +# (Except for CPU models reported as "static" in query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, CPU models +# may look different depending on machine and accelerator options. (Except for +# CPU models reported as "static" in query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu option and +# global properties may affect expansion of CPU models. Using +# query-cpu-model-expansion while using these is not advised. +# +# Some architectures may not support all expansion types. s390x supports +# "full" and "static". +# +# Returns: a CpuModelExpansionInfo. Returns an error if expanding CPU models is +# not supported, if the model cannot be expanded, if the model contains +# an unknown CPU definition name, unknown properties or properties +# with a wrong type. Also returns an error if an expansion type is +# not supported. +# +# Since: 2.8.0 +## +{ 'command': 'query-cpu-model-expansion', + 'data': { 'type': 'CpuModelExpansionType', + 'model': 'CpuModelInfo' }, + 'returns': 'CpuModelExpansionInfo', + 'if': 'defined(TARGET_S390X) || defined(TARGET_I386)' } + +## +# @CpuDefinitionInfo: +# +# Virtual CPU definition. +# +# @name: the name of the CPU definition +# +# @migration-safe: whether a CPU definition can be safely used for +# migration in combination with a QEMU compatibility machine +# when migrating between different QEMU versions and between +# hosts with different sets of (hardware or software) +# capabilities. If not provided, information is not available +# and callers should not assume the CPU definition to be +# migration-safe. (since 2.8) +# +# @static: whether a CPU definition is static and will not change depending on +# QEMU version, machine type, machine options and accelerator options. +# A static model is always migration-safe. (since 2.8) +# +# @unavailable-features: List of properties that prevent +# the CPU model from running in the current +# host. (since 2.8) +# @typename: Type name that can be used as argument to @device-list-properties, +# to introspect properties configurable using -cpu or -global. +# (since 2.9) +# +# @unavailable-features is a list of QOM property names that +# represent CPU model attributes that prevent the CPU from running. +# If the QOM property is read-only, that means there's no known +# way to make the CPU model run in the current host. Implementations +# that choose not to provide specific information return the +# property name "type". +# If the property is read-write, it means that it MAY be possible +# to run the CPU model in the current host if that property is +# changed. Management software can use it as hints to suggest or +# choose an alternative for the user, or just to generate meaningful +# error messages explaining why the CPU model can't be used. +# If @unavailable-features is an empty list, the CPU model is +# runnable using the current host and machine-type. +# If @unavailable-features is not present, runnability +# information for the CPU is not available. +# +# Since: 1.2.0 +## +{ 'struct': 'CpuDefinitionInfo', + 'data': { 'name': 'str', + '*migration-safe': 'bool', + 'static': 'bool', + '*unavailable-features': [ 'str' ], + 'typename': 'str' }, + 'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' } + +## +# @query-cpu-definitions: +# +# Return a list of supported virtual CPU definitions +# +# Returns: a list of CpuDefInfo +# +# Since: 1.2.0 +## +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'], + 'if': 'defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_I386) || defined(TARGET_S390X) || defined(TARGET_MIPS)' } diff --git a/qapi/tpm.json b/qapi/tpm.json index d50deef5e9..b30323bb6b 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -76,8 +76,9 @@ # # Since: 1.5 ## -{ 'struct': 'TPMPassthroughOptions', 'data': { '*path' : 'str', - '*cancel-path' : 'str'} } +{ 'struct': 'TPMPassthroughOptions', + 'data': { '*path': 'str', + '*cancel-path': 'str' } } ## # @TPMEmulatorOptions: diff --git a/qapi/trace-events b/qapi/trace-events index 70e049ea80..5eb4afa110 100644 --- a/qapi/trace-events +++ b/qapi/trace-events @@ -1,4 +1,6 @@ -# qapi/qapi-visit-core.c +# See docs/devel/tracing.txt for syntax documentation. + +# qapi-visit-core.c visit_free(void *v) "v=%p" visit_complete(void *v, void *opaque) "v=%p opaque=%p" diff --git a/qapi/transaction.json b/qapi/transaction.json index d7e4274550..95edb78227 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -46,8 +46,9 @@ # - @abort: since 1.6 # - @block-dirty-bitmap-add: since 2.5 # - @block-dirty-bitmap-clear: since 2.5 -# - @x-block-dirty-bitmap-enable: since 3.0 -# - @x-block-dirty-bitmap-disable: since 3.0 +# - @block-dirty-bitmap-enable: since 4.0 +# - @block-dirty-bitmap-disable: since 4.0 +# - @block-dirty-bitmap-merge: since 4.0 # - @blockdev-backup: since 2.3 # - @blockdev-snapshot: since 2.5 # - @blockdev-snapshot-internal-sync: since 1.7 @@ -61,8 +62,9 @@ 'abort': 'Abort', 'block-dirty-bitmap-add': 'BlockDirtyBitmapAdd', 'block-dirty-bitmap-clear': 'BlockDirtyBitmap', - 'x-block-dirty-bitmap-enable': 'BlockDirtyBitmap', - 'x-block-dirty-bitmap-disable': 'BlockDirtyBitmap', + 'block-dirty-bitmap-enable': 'BlockDirtyBitmap', + 'block-dirty-bitmap-disable': 'BlockDirtyBitmap', + 'block-dirty-bitmap-merge': 'BlockDirtyBitmapMerge', 'blockdev-backup': 'BlockdevBackup', 'blockdev-snapshot': 'BlockdevSnapshot', 'blockdev-snapshot-internal-sync': 'BlockdevSnapshotInternal', diff --git a/qapi/ui.json b/qapi/ui.json index 4ca91bb45a..59e412139a 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -598,7 +598,8 @@ # Notes: An empty password in this command will set the password to the empty # string. Existing clients are unaffected by executing this command. ## -{ 'command': 'change-vnc-password', 'data': {'password': 'str'}, +{ 'command': 'change-vnc-password', + 'data': { 'password': 'str' }, 'if': 'defined(CONFIG_VNC)' } ## @@ -1015,6 +1016,16 @@ '*head' : 'int', 'events' : [ 'InputEvent' ] } } +## +# @GrabToggleKeys: +# +# Keys to toggle input-linux between host and guest. +# +# Since: 4.0 +# +## +{ 'enum': 'GrabToggleKeys', + 'data': [ 'ctrl-ctrl', 'alt-alt', 'meta-meta', 'scrolllock', 'ctrl-scrolllock' ] } ## # @DisplayGTK: @@ -1022,12 +1033,34 @@ # GTK display options. # # @grab-on-hover: Grab keyboard input on mouse hover. +# @zoom-to-fit: Zoom guest display to fit into the host window. When +# turned off the host window will be resized instead. +# In case the display device can notify the guest on +# window resizes (virtio-gpu) this will default to "on", +# assuming the guest will resize the display to match +# the window size then. Otherwise it defaults to "off". +# Since 3.1 # # Since: 2.12 # ## { 'struct' : 'DisplayGTK', - 'data' : { '*grab-on-hover' : 'bool' } } + 'data' : { '*grab-on-hover' : 'bool', + '*zoom-to-fit' : 'bool' } } + +## +# @DisplayEGLHeadless: +# +# EGL headless display options. +# +# @rendernode: Which DRM render node should be used. Default is the first +# available node on the host. +# +# Since: 3.1 +# +## +{ 'struct' : 'DisplayEGLHeadless', + 'data' : { '*rendernode' : 'str' } } ## # @DisplayGLMode: @@ -1047,17 +1080,60 @@ { 'enum' : 'DisplayGLMode', 'data' : [ 'off', 'on', 'core', 'es' ] } +## +# @DisplayCurses: +# +# Curses display options. +# +# @charset: Font charset used by guest (default: CP437). +# +# Since: 4.0 +# +## +{ 'struct' : 'DisplayCurses', + 'data' : { '*charset' : 'str' } } + ## # @DisplayType: # # Display (user interface) type. # +# @default: The default user interface, selecting from the first available +# of gtk, sdl, cocoa, and vnc. +# +# @none: No user interface or video output display. The guest will +# still see an emulated graphics card, but its output will not +# be displayed to the QEMU user. +# +# @gtk: The GTK user interface. +# +# @sdl: The SDL user interface. +# +# @egl-headless: No user interface, offload GL operations to a local +# DRI device. Graphical display need to be paired with +# VNC or Spice. (Since 3.1) +# +# @curses: Display video output via curses. For graphics device +# models which support a text mode, QEMU can display this +# output using a curses/ncurses interface. Nothing is +# displayed when the graphics device is in graphical mode or +# if the graphics device does not support a text +# mode. Generally only the VGA device models support text +# mode. +# +# @cocoa: The Cocoa user interface. +# +# @spice-app: Set up a Spice server and run the default associated +# application to connect to it. The server will redirect +# the serial console and QEMU monitors. (Since 4.0) +# # Since: 2.12 # ## { 'enum' : 'DisplayType', 'data' : [ 'default', 'none', 'gtk', 'sdl', - 'egl-headless', 'curses', 'cocoa' ] } + 'egl-headless', 'curses', 'cocoa', + 'spice-app'] } ## # @DisplayOptions: @@ -1078,4 +1154,19 @@ '*window-close' : 'bool', '*gl' : 'DisplayGLMode' }, 'discriminator' : 'type', - 'data' : { 'gtk' : 'DisplayGTK' } } + 'data' : { 'gtk' : 'DisplayGTK', + 'curses' : 'DisplayCurses', + 'egl-headless' : 'DisplayEGLHeadless'} } + +## +# @query-display-options: +# +# Returns information about display configuration +# +# Returns: @DisplayOptions +# +# Since: 3.1 +# +## +{ 'command': 'query-display-options', + 'returns': 'DisplayOptions' } diff --git a/qdev-monitor.c b/qdev-monitor.c index 61e0300991..d4320986a2 100644 --- a/qdev-monitor.c +++ b/qdev-monitor.c @@ -104,22 +104,31 @@ static bool qdev_class_has_alias(DeviceClass *dc) return (qdev_class_get_alias(dc) != NULL); } +static void out_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + monitor_vfprintf(stdout, fmt, ap); + va_end(ap); +} + static void qdev_print_devinfo(DeviceClass *dc) { - error_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc))); + out_printf("name \"%s\"", object_class_get_name(OBJECT_CLASS(dc))); if (dc->bus_type) { - error_printf(", bus %s", dc->bus_type); + out_printf(", bus %s", dc->bus_type); } if (qdev_class_has_alias(dc)) { - error_printf(", alias \"%s\"", qdev_class_get_alias(dc)); + out_printf(", alias \"%s\"", qdev_class_get_alias(dc)); } if (dc->desc) { - error_printf(", desc \"%s\"", dc->desc); + out_printf(", desc \"%s\"", dc->desc); } if (!dc->user_creatable) { - error_printf(", no-user"); + out_printf(", no-user"); } - error_printf("\n"); + out_printf("\n"); } static void qdev_print_devinfos(bool show_no_user) @@ -155,8 +164,7 @@ static void qdev_print_devinfos(bool show_no_user) continue; } if (!cat_printed) { - error_printf("%s%s devices:\n", i ? "\n" : "", - cat_name[i]); + out_printf("%s%s devices:\n", i ? "\n" : "", cat_name[i]); cat_printed = true; } qdev_print_devinfo(dc); @@ -277,14 +285,21 @@ int qdev_device_help(QemuOpts *opts) goto error; } + if (prop_list) { + out_printf("%s options:\n", driver); + } else { + out_printf("There are no options for %s.\n", driver); + } for (prop = prop_list; prop; prop = prop->next) { - error_printf("%s.%s=%s", driver, - prop->value->name, - prop->value->type); + int len; + out_printf(" %s=<%s>%n", prop->value->name, prop->value->type, &len); if (prop->value->has_description) { - error_printf(" (%s)\n", prop->value->description); + if (len < 24) { + out_printf("%*s", 24 - len, ""); + } + out_printf(" - %s\n", prop->value->description); } else { - error_printf("\n"); + out_printf("\n"); } } @@ -399,7 +414,7 @@ static DeviceState *qbus_find_dev(BusState *bus, char *elem) static inline bool qbus_is_full(BusState *bus) { BusClass *bus_class = BUS_GET_CLASS(bus); - return bus_class->max_dev && bus->max_index >= bus_class->max_dev; + return bus_class->max_dev && bus->num_children >= bus_class->max_dev; } /* @@ -847,6 +862,7 @@ void qdev_unplug(DeviceState *dev, Error **errp) DeviceClass *dc = DEVICE_GET_CLASS(dev); HotplugHandler *hotplug_ctrl; HotplugHandlerClass *hdc; + Error *local_err = NULL; if (dev->parent_bus && !qbus_is_hotpluggable(dev->parent_bus)) { error_setg(errp, QERR_BUS_NO_HOTPLUG, dev->parent_bus->name); @@ -875,10 +891,14 @@ void qdev_unplug(DeviceState *dev, Error **errp) * otherwise just remove it synchronously */ hdc = HOTPLUG_HANDLER_GET_CLASS(hotplug_ctrl); if (hdc->unplug_request) { - hotplug_handler_unplug_request(hotplug_ctrl, dev, errp); + hotplug_handler_unplug_request(hotplug_ctrl, dev, &local_err); } else { - hotplug_handler_unplug(hotplug_ctrl, dev, errp); + hotplug_handler_unplug(hotplug_ctrl, dev, &local_err); + if (!local_err) { + object_unparent(OBJECT(dev)); + } } + error_propagate(errp, local_err); } void qmp_device_del(const char *id, Error **errp) diff --git a/qemu-deprecated.texi b/qemu-deprecated.texi index 9920a85adc..842e71b11d 100644 --- a/qemu-deprecated.texi +++ b/qemu-deprecated.texi @@ -17,98 +17,18 @@ they were first deprecated in the 2.10.0 release. What follows is a list of all features currently marked as deprecated. -@section Build options - -@subsection GTK 2.x - -Previously QEMU has supported building against both GTK 2.x -and 3.x series APIs. Support for the GTK 2.x builds will be -discontinued, so maintainers should switch to using GTK 3.x, -which is the default. - -@subsection SDL 1.2 - -Previously QEMU has supported building against both SDL 1.2 -and 2.0 series APIs. Support for the SDL 1.2 builds will be -discontinued, so maintainers should switch to using SDL 2.0, -which is the default. - @section System emulator command line arguments +@subsection -machine enforce-config-section=on|off (since 3.1) + +The @option{enforce-config-section} parameter is replaced by the +@option{-global migration.send-configuration=@var{on|off}} option. + @subsection -no-kvm (since 1.3.0) The ``-no-kvm'' argument is now a synonym for setting ``-machine accel=tcg''. -@subsection -vnc tls (since 2.5.0) - -The ``-vnc tls'' argument is now a synonym for setting -``-object tls-creds-anon,id=tls0'' combined with -``-vnc tls-creds=tls0' - -@subsection -vnc x509 (since 2.5.0) - -The ``-vnc x509=/path/to/certs'' argument is now a -synonym for setting -``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=no'' -combined with ``-vnc tls-creds=tls0' - -@subsection -vnc x509verify (since 2.5.0) - -The ``-vnc x509verify=/path/to/certs'' argument is now a -synonym for setting -``-object tls-creds-x509,dir=/path/to/certs,id=tls0,verify-peer=yes'' -combined with ``-vnc tls-creds=tls0' - -@subsection -tftp (since 2.6.0) - -The ``-tftp /some/dir'' argument is replaced by either -``-netdev user,id=x,tftp=/some/dir '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,tftp=/some/dir'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -bootp (since 2.6.0) - -The ``-bootp /some/file'' argument is replaced by either -``-netdev user,id=x,bootp=/some/file '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,bootp=/some/file'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -redir (since 2.6.0) - -The ``-redir [tcp|udp]:hostport:[guestaddr]:guestport'' argument is -replaced by either -``-netdev user,id=x,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' -(for pluggable NICs, accompanied with ``-device ...,netdev=x'') or -``-nic user,hostfwd=[tcp|udp]:[hostaddr]:hostport-[guestaddr]:guestport'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -smb (since 2.6.0) - -The ``-smb /some/dir'' argument is replaced by either -``-netdev user,id=x,smb=/some/dir '' (for pluggable NICs, accompanied -with ``-device ...,netdev=x''), or ``-nic user,smb=/some/dir'' -(for embedded NICs). The new syntax allows different settings to be -provided per NIC. - -@subsection -drive cyls=...,heads=...,secs=...,trans=... (since 2.10.0) - -The drive geometry arguments are replaced by the the geometry arguments -that can be specified with the ``-device'' parameter. - -@subsection -drive serial=... (since 2.10.0) - -The drive serial argument is replaced by the the serial argument -that can be specified with the ``-device'' parameter. - -@subsection -drive addr=... (since 2.10.0) - -The drive addr argument is replaced by the the addr argument -that can be specified with the ``-device'' parameter. - @subsection -usbdevice (since 2.10.0) The ``-usbdevice DEV'' argument is now a synonym for setting @@ -117,71 +37,41 @@ would automatically enable USB support on the machine type. If using the new syntax, USB support must be explicitly enabled via the ``-machine usb=on'' argument. -@subsection -nodefconfig (since 2.11.0) - -The ``-nodefconfig`` argument is a synonym for ``-no-user-config``. - -@subsection -balloon (since 2.12.0) - -The @option{--balloon virtio} argument has been superseded by -@option{--device virtio-balloon}. - -@subsection -machine s390-squash-mcss=on|off (since 2.12.0) - -The ``s390-squash-mcss=on`` property has been obsoleted by allowing the -cssid to be chosen freely. Instead of squashing subchannels into the -default channel subsystem image for guests that do not support multiple -channel subsystems, all devices can be put into the default channel -subsystem image. - -@subsection -fsdev handle (since 2.12.0) - -The ``handle'' fsdev backend does not support symlinks and causes the 9p -filesystem in the guest to fail a fair amount of tests from the PJD POSIX -filesystem test suite. Also it requires the CAP_DAC_READ_SEARCH capability, -which is not the recommended way to run QEMU. This backend should not be -used and it will be removed with no replacement. - -@subsection -no-frame (since 2.12.0) - -The @code{--no-frame} argument works with SDL 1.2 only. The other user -interfaces never implemented this in the first place. So this will be -removed together with SDL 1.2 support. - -@subsection -rtc-td-hack (since 2.12.0) - -The @code{-rtc-td-hack} option has been replaced by -@code{-rtc driftfix=slew}. - -@subsection -localtime (since 2.12.0) - -The @code{-localtime} option has been replaced by @code{-rtc base=localtime}. - -@subsection -startdate (since 2.12.0) - -The @code{-startdate} option has been replaced by @code{-rtc base=@var{date}}. - -@subsection -virtioconsole (since 3.0.0) - -Option @option{-virtioconsole} has been replaced by -@option{-device virtconsole}. - -@subsection -clock (since 3.0.0) - -The @code{-clock} option is ignored since QEMU version 1.7.0. There is no -replacement since it is not needed anymore. - -@subsection -enable-hax (since 3.0.0) - -The @option{-enable-hax} option has been replaced by @option{-accel hax}. -Both options have been introduced in QEMU version 2.9.0. - @subsection -drive file=json:@{...@{'driver':'file'@}@} (since 3.0) The 'file' driver for drives is no longer appropriate for character or host devices and will only accept regular files (S_IFREG). The correct driver for these file types is 'host_cdrom' or 'host_device' as appropriate. +@subsection -net ...,name=@var{name} (since 3.1) + +The @option{name} parameter of the @option{-net} option is a synonym +for the @option{id} parameter, which should now be used instead. + +@subsection -smp (invalid topologies) (since 3.1) + +CPU topology properties should describe whole machine topology including +possible CPUs. + +However, historically it was possible to start QEMU with an incorrect topology +where @math{@var{n} <= @var{sockets} * @var{cores} * @var{threads} < @var{maxcpus}}, +which could lead to an incorrect topology enumeration by the guest. +Support for invalid topologies will be removed, the user must ensure +topologies described with -smp include all possible cpus, i.e. + @math{@var{sockets} * @var{cores} * @var{threads} = @var{maxcpus}}. + +@subsection -vnc acl (since 4.0.0) + +The @code{acl} option to the @code{-vnc} argument has been replaced +by the @code{tls-authz} and @code{sasl-authz} options. + +@subsection QEMU_AUDIO_ environment variables and -audio-help (since 4.0) + +The ``-audiodev'' argument is now the preferred way to specify audio +backend settings instead of environment variables. To ease migration to +the new format, the ``-audiodev-help'' option can be used to convert +the current values of the environment variables to ``-audiodev'' options. + @section QEMU Machine Protocol (QMP) commands @subsection block-dirty-bitmap-add "autoload" parameter (since 2.12.0) @@ -189,6 +79,12 @@ for these file types is 'host_cdrom' or 'host_device' as appropriate. "autoload" parameter is now ignored. All bitmaps are automatically loaded from qcow2 images. +@subsection query-block result field dirty-bitmaps[i].status (since 4.0) + +The ``status'' field of the ``BlockDirtyInfo'' structure, returned by +the query-block command is deprecated. Two new boolean fields, +``recording'' and ``busy'' effectively replace it. + @subsection query-cpus (since 2.12.0) The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. @@ -198,26 +94,62 @@ The ``query-cpus'' command is replaced by the ``query-cpus-fast'' command. The ``arch'' output member of the ``query-cpus-fast'' command is replaced by the ``target'' output member. +@subsection cpu-add (since 4.0) + +Use ``device_add'' for hotplugging vCPUs instead of ``cpu-add''. See +documentation of ``query-hotpluggable-cpus'' for additional +details. + +@subsection query-events (since 4.0) + +The ``query-events'' command has been superseded by the more powerful +and accurate ``query-qmp-schema'' command. + +@subsection chardev client socket with 'wait' option (since 4.0) + +Character devices creating sockets in client mode should not specify +the 'wait' field, which is only applicable to sockets in server mode + +@section Human Monitor Protocol (HMP) commands + +@subsection The hub_id parameter of 'hostfwd_add' / 'hostfwd_remove' (since 3.1) + +The @option{[hub_id name]} parameter tuple of the 'hostfwd_add' and +'hostfwd_remove' HMP commands has been replaced by @option{netdev_id}. + +@subsection cpu-add (since 4.0) + +Use ``device_add'' for hotplugging vCPUs instead of ``cpu-add''. See +documentation of ``query-hotpluggable-cpus'' for additional details. + +@subsection acl_show, acl_reset, acl_policy, acl_add, acl_remove (since 4.0.0) + +The ``acl_show'', ``acl_reset'', ``acl_policy'', ``acl_add'', and +``acl_remove'' commands are deprecated with no replacement. Authorization +for VNC should be performed using the pluggable QAuthZ objects. + @section System emulator devices -@subsection ivshmem (since 2.6.0) +@subsection bluetooth (since 3.1) -The ``ivshmem'' device type is replaced by either the ``ivshmem-plain'' -or ``ivshmem-doorbell`` device types. - -@subsection Page size support < 4k for embedded PowerPC CPUs (since 2.12.0) - -qemu-system-ppcemb will be removed. qemu-system-ppc (or qemu-system-ppc64) -should be used instead. That means that embedded 4xx PowerPC CPUs will not -support page sizes < 4096 any longer. +The bluetooth subsystem is unmaintained since many years and likely bitrotten +quite a bit. It will be removed without replacement unless some users speaks +up at the @email{qemu-devel@@nongnu.org} mailing list with information about +their usecases. @section System emulator machines -@subsection pc-0.10 and pc-0.11 (since 3.0) +@subsection pc-0.12, pc-0.13, pc-0.14 and pc-0.15 (since 4.0) These machine types are very old and likely can not be used for live migration from old QEMU versions anymore. A newer machine type should be used instead. +@subsection prep (PowerPC) (since 3.1) + +This machine type uses an unmaintained firmware, broken in lots of ways, +and unable to start post-2004 operating systems. 40p machine type should be +used instead. + @section Device options @subsection Block device options @@ -227,8 +159,50 @@ from old QEMU versions anymore. A newer machine type should be used instead. In order to prevent QEMU from automatically opening an image's backing chain, use ``"backing": null'' instead. -@subsection vio-spapr-device device options +@subsubsection rbd keyvalue pair encoded filenames: "" (since 3.1.0) -@subsubsection "irq": "" (since 3.0.0) +Options for ``rbd'' should be specified according to its runtime options, +like other block drivers. Legacy parsing of keyvalue pair encoded +filenames is useful to open images with the old format for backing files; +These image files should be updated to use the current format. -The ``irq'' property is obsoleted. +Example of legacy encoding: + +@code{json:@{"file.driver":"rbd", "file.filename":"rbd:rbd/name"@}} + +The above, converted to the current supported format: + +@code{json:@{"file.driver":"rbd", "file.pool":"rbd", "file.image":"name"@}} + +@section Related binaries + +@subsection qemu-nbd --partition (since 4.0.0) + +The ``qemu-nbd --partition $digit'' code (also spelled @option{-P}) +can only handle MBR partitions, and has never correctly handled +logical partitions beyond partition 5. If you know the offset and +length of the partition (perhaps by using @code{sfdisk} within the +guest), you can achieve the effect of exporting just that subset of +the disk by use of the @option{--image-opts} option with a raw +blockdev using the @code{offset} and @code{size} parameters layered on +top of any other existing blockdev. For example, if partition 1 is +100MiB long starting at 1MiB, the old command: + +@code{qemu-nbd -t -P 1 -f qcow2 file.qcow2} + +can be rewritten as: + +@code{qemu-nbd -t --image-opts driver=raw,offset=1M,size=100M,file.driver=qcow2,file.backing.driver=file,file.backing.filename=file.qcow2} + +Alternatively, the @code{nbdkit} project provides a more powerful +partition filter on top of its nbd plugin, which can be used to select +an arbitrary MBR or GPT partition on top of any other full-image NBD +export. Using this to rewrite the above example results in: + +@code{qemu-nbd -t -k /tmp/sock -f qcow2 file.qcow2 &} +@code{nbdkit -f --filter=partition nbd socket=/tmp/sock partition=1} + +Note that if you are exposing the export via /dev/nbd0, it is easier +to just export the entire image and then mount only /dev/nbd0p1 than +it is to reinvoke @command{qemu-nbd -c /dev/nbd0} limited to just a +subset of the image. diff --git a/qemu-doc.texi b/qemu-doc.texi index abfd2db546..ae3c3f9632 100644 --- a/qemu-doc.texi +++ b/qemu-doc.texi @@ -37,6 +37,7 @@ * QEMU System emulator for non PC targets:: * QEMU Guest Agent:: * QEMU User space emulator:: +* System requirements:: * Implementation notes:: * Deprecated features:: * Supported build platforms:: @@ -135,6 +136,7 @@ accelerator is required to use more than one host CPU for emulation. * pcsys_keys:: Keys in the graphical frontends * mux_keys:: Keys in the character backend multiplexer * pcsys_monitor:: QEMU Monitor +* cpu_models:: CPU models * disk_images:: Disk Images * pcsys_network:: Network emulation * pcsys_other_devs:: Other Devices @@ -602,6 +604,11 @@ The monitor understands integers expressions for every integer argument. You can use register names to get the value of specifics CPU registers by prefixing them with @emph{$}. +@node cpu_models +@section CPU models + +@include docs/qemu-cpu-models.texi + @node disk_images @section Disk Images @@ -937,7 +944,7 @@ for details Bulk-only transport storage device, see @url{https://git.qemu.org/?p=qemu.git;a=blob_plain;f=docs/usb-storage.txt,usb-storage.txt} for details here, too -@item usb-mtp,x-root=@var{dir} +@item usb-mtp,rootdir=@var{dir} Media transfer protocol device, using @var{dir} as root of the file tree that is presented to the guest. @item usb-host,hostbus=@var{bus},hostaddr=@var{addr} @@ -1097,7 +1104,9 @@ support provides a secure session, but no authentication. This allows any client to connect, and provides an encrypted session. @example -qemu-system-i386 [...OPTIONS...] -vnc :1,tls,x509=/etc/pki/qemu -monitor stdio +qemu-system-i386 [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=no \ + -vnc :1,tls-creds=tls0 -monitor stdio @end example In the above example @code{/etc/pki/qemu} should contain at least three files, @@ -1112,10 +1121,14 @@ only be readable by the user owning it. Certificates can also provide a means to authenticate the client connecting. The server will request that the client provide a certificate, which it will then validate against the CA certificate. This is a good choice if deploying -in an environment with a private internal certificate authority. +in an environment with a private internal certificate authority. It uses the +same syntax as previously, but with @code{verify-peer} set to @code{yes} +instead. @example -qemu-system-i386 [...OPTIONS...] -vnc :1,tls,x509verify=/etc/pki/qemu -monitor stdio +qemu-system-i386 [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ + -vnc :1,tls-creds=tls0 -monitor stdio @end example @@ -1126,7 +1139,9 @@ Finally, the previous method can be combined with VNC password authentication to provide two layers of authentication for clients. @example -qemu-system-i386 [...OPTIONS...] -vnc :1,password,tls,x509verify=/etc/pki/qemu -monitor stdio +qemu-system-i386 [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ + -vnc :1,tls-creds=tls0,password -monitor stdio (qemu) change vnc password Password: ******** (qemu) @@ -1163,7 +1178,9 @@ credentials. This can be enabled, by combining the 'sasl' option with the aforementioned TLS + x509 options: @example -qemu-system-i386 [...OPTIONS...] -vnc :1,tls,x509,sasl -monitor stdio +qemu-system-i386 [...OPTIONS...] \ + -object tls-creds-x509,id=tls0,dir=/etc/pki/qemu,endpoint=server,verify-peer=yes \ + -vnc :1,tls-creds=tls0,sasl -monitor stdio @end example @node vnc_setup_sasl @@ -1978,6 +1995,10 @@ Set the emulated machine type. The default is sun4u. @section MIPS System emulator @cindex system emulation (MIPS) +@menu +* nanoMIPS System emulator :: +@end menu + Four executables cover simulation of 32 and 64-bit MIPS systems in both endian options, @file{qemu-system-mips}, @file{qemu-system-mipsel} @file{qemu-system-mips64} and @file{qemu-system-mips64el}. @@ -2028,6 +2049,15 @@ Malta FPGA serial device Cirrus (default) or any other PCI VGA graphics card @end itemize +The Boston board emulation supports the following devices: + +@itemize @minus +@item +Xilinx FPGA, which includes a PCIe root port and an UART +@item +Intel EG20T PCH connects the I/O peripherals, but only the SATA bus is emulated +@end itemize + The ACER Pica emulation supports: @itemize @minus @@ -2041,19 +2071,6 @@ PC Keyboard IDE controller @end itemize -The mipssim pseudo board emulation provides an environment similar -to what the proprietary MIPS emulator uses for running Linux. -It supports: - -@itemize @minus -@item -A range of MIPS CPUs, default is the 24Kf -@item -PC style serial port -@item -MIPSnet network emulation -@end itemize - The MIPS Magnum R4000 emulation supports: @itemize @minus @@ -2069,6 +2086,57 @@ SCSI controller G364 framebuffer @end itemize +The Fulong 2E emulation supports: + +@itemize @minus +@item +Loongson 2E CPU +@item +Bonito64 system controller as North Bridge +@item +VT82C686 chipset as South Bridge +@item +RTL8139D as a network card chipset +@end itemize + +The mipssim pseudo board emulation provides an environment similar +to what the proprietary MIPS emulator uses for running Linux. +It supports: + +@itemize @minus +@item +A range of MIPS CPUs, default is the 24Kf +@item +PC style serial port +@item +MIPSnet network emulation +@end itemize + +@node nanoMIPS System emulator +@subsection nanoMIPS System emulator +@cindex system emulation (nanoMIPS) + +Executable @file{qemu-system-mipsel} also covers simulation of +32-bit nanoMIPS system in little endian mode: + +@itemize @minus +@item +nanoMIPS I7200 CPU +@end itemize + +Example of @file{qemu-system-mipsel} usage for nanoMIPS is shown below: + +Download @code{} from @url{https://mipsdistros.mips.com/LinuxDistro/nanomips/buildroot/index.html}. + +Download @code{} from @url{https://mipsdistros.mips.com/LinuxDistro/nanomips/kernels/v4.15.18-432-gb2eb9a8b07a1-20180627102142/index.html}. + +Start system emulation of Malta board with nanoMIPS I7200 CPU: +@example +qemu-system-mipsel -cpu I7200 -kernel @code{} \ + -M malta -serial stdio -m @code{} -hda @code{} \ + -append "mem=256m@@0x0 rw console=ttyS0 vga=cirrus vesa=0x111 root=/dev/sda" +@end example + @node ARM System emulator @section ARM System emulator @@ -2693,8 +2761,17 @@ The binary format is detected automatically. @command{qemu-microblaze} TODO. @cindex user mode (MIPS) -@command{qemu-mips} TODO. -@command{qemu-mipsel} TODO. +@command{qemu-mips} executes 32-bit big endian MIPS binaries (MIPS O32 ABI). + +@command{qemu-mipsel} executes 32-bit little endian MIPS binaries (MIPS O32 ABI). + +@command{qemu-mips64} executes 64-bit big endian MIPS binaries (MIPS N64 ABI). + +@command{qemu-mips64el} executes 64-bit little endian MIPS binaries (MIPS N64 ABI). + +@command{qemu-mipsn32} executes 32-bit big endian MIPS binaries (MIPS N32 ABI). + +@command{qemu-mipsn32el} executes 32-bit little endian MIPS binaries (MIPS N32 ABI). @cindex user mode (NiosII) @command{qemu-nios2} TODO. @@ -2788,6 +2865,18 @@ Act as if the host page size was 'pagesize' bytes Run the emulation in single step mode. @end table +@node System requirements +@chapter System requirements + +@section KVM kernel module + +On x86_64 hosts, the default set of CPU features enabled by the KVM accelerator +require the host to be running Linux v4.5 or newer. + +The OpteronG[345] CPU models require KVM support for RDTSCP, which was +added with Linux 4.5 which is supported by the major distros. And even +if RHEL7 has kernel 3.10, KVM there has the required functionality there +to make it close to a 4.5 or newer kernel. @include qemu-tech.texi diff --git a/qemu-edid.c b/qemu-edid.c new file mode 100644 index 0000000000..ae8b8a6d9b --- /dev/null +++ b/qemu-edid.c @@ -0,0 +1,120 @@ +/* + * QEMU EDID test tool. + * + * 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/bswap.h" +#include "qemu/cutils.h" +#include "hw/display/edid.h" + +static qemu_edid_info info; + +static void usage(FILE *out) +{ + fprintf(out, + "\n" + "This is a test tool for the qemu edid generator.\n" + "\n" + "Typically you'll pipe the output into edid-decode\n" + "to check if the generator works correctly.\n" + "\n" + "usage: qemu-edid \n" + "options:\n" + " -h print this text\n" + " -o set output file (stdout by default)\n" + " -v set monitor vendor (three letters)\n" + " -n set monitor name\n" + " -s set monitor serial\n" + " -d set display resolution\n" + " -x set preferred width\n" + " -y set preferred height\n" + " -X set maximum width\n" + " -Y set maximum height\n" + "\n"); +} + +int main(int argc, char *argv[]) +{ + FILE *outfile = NULL; + uint8_t blob[256]; + int rc; + + for (;;) { + rc = getopt(argc, argv, "ho:x:y:X:Y:d:v:n:s:"); + if (rc == -1) { + break; + } + switch (rc) { + case 'o': + if (outfile) { + fprintf(stderr, "outfile specified twice\n"); + exit(1); + } + outfile = fopen(optarg, "w"); + if (outfile == NULL) { + fprintf(stderr, "open %s: %s\n", optarg, strerror(errno)); + exit(1); + } + break; + case 'x': + if (qemu_strtoui(optarg, NULL, 10, &info.prefx) < 0) { + fprintf(stderr, "not a number: %s\n", optarg); + exit(1); + } + break; + case 'y': + if (qemu_strtoui(optarg, NULL, 10, &info.prefy) < 0) { + fprintf(stderr, "not a number: %s\n", optarg); + exit(1); + } + break; + case 'X': + if (qemu_strtoui(optarg, NULL, 10, &info.maxx) < 0) { + fprintf(stderr, "not a number: %s\n", optarg); + exit(1); + } + break; + case 'Y': + if (qemu_strtoui(optarg, NULL, 10, &info.maxy) < 0) { + fprintf(stderr, "not a number: %s\n", optarg); + exit(1); + } + break; + case 'd': + if (qemu_strtoui(optarg, NULL, 10, &info.dpi) < 0) { + fprintf(stderr, "not a number: %s\n", optarg); + exit(1); + } + break; + case 'v': + info.vendor = optarg; + break; + case 'n': + info.name = optarg; + break; + case 's': + info.serial = optarg; + break; + case 'h': + usage(stdout); + exit(0); + default: + usage(stderr); + exit(1); + } + } + + if (outfile == NULL) { + outfile = stdout; + } + + memset(blob, 0, sizeof(blob)); + qemu_edid_generate(blob, sizeof(blob), &info); + fwrite(blob, sizeof(blob), 1, outfile); + fflush(outfile); + + exit(0); +} diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index 1526f327a5..4b47f7495d 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -46,7 +46,7 @@ ETEXI DEF("convert", img_convert, "convert [--object objectdef] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f fmt] [-t cache] [-T src_cache] [-O output_fmt] [-B backing_file] [-o options] [-l snapshot_param] [-S sparse_size] [-m num_coroutines] [-W] filename [filename2 [...]] output_filename") STEXI -@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} +@item convert [--object @var{objectdef}] [--image-opts] [--target-image-opts] [-U] [-C] [-c] [-p] [-q] [-n] [-f @var{fmt}] [-t @var{cache}] [-T @var{src_cache}] [-O @var{output_fmt}] [-B @var{backing_file}] [-o @var{options}] [-l @var{snapshot_param}] [-S @var{sparse_size}] [-m @var{num_coroutines}] [-W] @var{filename} [@var{filename2} [...]] @var{output_filename} ETEXI DEF("create", img_create, diff --git a/qemu-img.c b/qemu-img.c index 1acddf693c..aa6f81f1ea 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -198,7 +198,7 @@ static void QEMU_NORETURN help(void) " 'skip=N' skip N bs-sized blocks at the start of input\n"; printf("%s\nSupported formats:", help_msg); - bdrv_iterate_format(format_print, NULL); + bdrv_iterate_format(format_print, NULL, false); printf("\n\n" QEMU_HELP_BOTTOM "\n"); exit(EXIT_SUCCESS); } @@ -261,15 +261,16 @@ static int print_block_option_help(const char *filename, const char *fmt) return 1; } if (!proto_drv->create_opts) { - error_report("Protocal driver '%s' does not support image creation", + error_report("Protocol driver '%s' does not support image creation", proto_drv->format_name); + qemu_opts_free(create_opts); return 1; } create_opts = qemu_opts_append(create_opts, proto_drv->create_opts); } printf("Supported options:\n"); - qemu_opts_print_help(create_opts); + qemu_opts_print_help(create_opts, false); qemu_opts_free(create_opts); return 0; } @@ -345,21 +346,6 @@ static int img_add_key_secrets(void *opaque, return 0; } -static BlockBackend *img_open_new_file(const char *filename, - QemuOpts *create_opts, - const char *fmt, int flags, - bool writethrough, bool quiet, - bool force_share) -{ - QDict *options = NULL; - - options = qdict_new(); - qemu_opt_foreach(create_opts, img_add_key_secrets, options, &error_abort); - - return img_open_file(filename, options, fmt, flags, writethrough, quiet, - force_share); -} - static BlockBackend *img_open(bool image_opts, const char *filename, @@ -517,7 +503,7 @@ static int img_create(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { goto fail; } @@ -767,7 +753,7 @@ static int img_check(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -980,7 +966,7 @@ static int img_commit(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -1044,6 +1030,7 @@ static int img_commit(int argc, char **argv) } job = block_job_get("commit"); + assert(job); run_block_job(job, &local_err); if (local_err) { goto unref_backing; @@ -1362,7 +1349,7 @@ static int img_compare(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { ret = 2; goto out4; } @@ -1643,6 +1630,8 @@ static int convert_iteration_sectors(ImgConvertState *s, int64_t sector_num) count, &count, NULL, NULL); } if (ret < 0) { + error_report("error while reading block status of sector %" PRId64 + ": %s", sector_num, strerror(-ret)); return ret; } n = DIV_ROUND_UP(count, BDRV_SECTOR_SIZE); @@ -1683,7 +1672,6 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, { int n, ret; QEMUIOVector qiov; - struct iovec iov; assert(nb_sectors <= s->buf_sectors); while (nb_sectors > 0) { @@ -1699,9 +1687,7 @@ static int coroutine_fn convert_co_read(ImgConvertState *s, int64_t sector_num, bs_sectors = s->src_sectors[src_cur]; n = MIN(nb_sectors, bs_sectors - (sector_num - src_cur_offset)); - iov.iov_base = buf; - iov.iov_len = n << BDRV_SECTOR_BITS; - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS); ret = blk_co_preadv( blk, (sector_num - src_cur_offset) << BDRV_SECTOR_BITS, @@ -1725,7 +1711,6 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, { int ret; QEMUIOVector qiov; - struct iovec iov; while (nb_sectors > 0) { int n = nb_sectors; @@ -1753,9 +1738,7 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, (s->compressed && !buffer_is_zero(buf, n * BDRV_SECTOR_SIZE))) { - iov.iov_base = buf; - iov.iov_len = n << BDRV_SECTOR_BITS; - qemu_iovec_init_external(&qiov, &iov, 1); + qemu_iovec_init_buf(&qiov, buf, n << BDRV_SECTOR_BITS); ret = blk_co_pwritev(s->target, sector_num << BDRV_SECTOR_BITS, n << BDRV_SECTOR_BITS, &qiov, flags); @@ -1773,7 +1756,8 @@ static int coroutine_fn convert_co_write(ImgConvertState *s, int64_t sector_num, } ret = blk_co_pwrite_zeroes(s->target, sector_num << BDRV_SECTOR_BITS, - n << BDRV_SECTOR_BITS, 0); + n << BDRV_SECTOR_BITS, + BDRV_REQ_MAY_UNMAP); if (ret < 0) { return ret; } @@ -1951,7 +1935,7 @@ static int convert_do_copy(ImgConvertState *s) if (!s->has_zero_init && !s->target_has_backing && bdrv_can_write_zeroes_with_unmap(blk_bs(s->target))) { - ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP); + ret = blk_make_zero(s->target, BDRV_REQ_MAY_UNMAP | BDRV_REQ_NO_FALLBACK); if (ret == 0) { s->has_zero_init = true; } @@ -2018,6 +2002,7 @@ static int img_convert(int argc, char **argv) BlockDriverState *out_bs; QemuOpts *opts = NULL, *sn_opts = NULL; QemuOptsList *create_opts = NULL; + QDict *open_opts = NULL; char *options = NULL; Error *local_err = NULL; bool writethrough, src_writethrough, quiet = false, image_opts = false, @@ -2171,7 +2156,7 @@ static int img_convert(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { goto fail_getopt; } @@ -2362,6 +2347,16 @@ static int img_convert(int argc, char **argv) } } + /* + * The later open call will need any decryption secrets, and + * bdrv_create() will purge "opts", so extract them now before + * they are lost. + */ + if (!skip_create) { + open_opts = qdict_new(); + qemu_opt_foreach(opts, img_add_key_secrets, open_opts, &error_abort); + } + if (!skip_create) { /* Create the new image */ ret = bdrv_create(drv, out_filename, opts, &local_err); @@ -2388,8 +2383,9 @@ static int img_convert(int argc, char **argv) * That has to wait for bdrv_create to be improved * to allow filenames in option syntax */ - s.target = img_open_new_file(out_filename, opts, out_fmt, - flags, writethrough, quiet, false); + s.target = img_open_file(out_filename, open_opts, out_fmt, + flags, writethrough, quiet, false); + open_opts = NULL; /* blk_new_open will have freed it */ } if (!s.target) { ret = -1; @@ -2464,6 +2460,7 @@ out: qemu_opts_del(opts); qemu_opts_free(create_opts); qemu_opts_del(sn_opts); + qobject_unref(open_opts); blk_unref(s.target); if (s.src) { for (bs_i = 0; bs_i < s.src_num; bs_i++) { @@ -2713,7 +2710,7 @@ static int img_info(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -2740,14 +2737,14 @@ static int img_info(int argc, char **argv) return 0; } -static void dump_map_entry(OutputFormat output_format, MapEntry *e, - MapEntry *next) +static int dump_map_entry(OutputFormat output_format, MapEntry *e, + MapEntry *next) { switch (output_format) { case OFORMAT_HUMAN: if (e->data && !e->has_offset) { error_report("File contains external, encrypted or compressed clusters."); - exit(1); + return -1; } if (e->data && !e->zero) { printf("%#-16"PRIx64"%#-16"PRIx64"%#-16"PRIx64"%s\n", @@ -2780,6 +2777,7 @@ static void dump_map_entry(OutputFormat output_format, MapEntry *e, } break; } + return 0; } static int get_block_status(BlockDriverState *bs, int64_t offset, @@ -2790,6 +2788,7 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, BlockDriverState *file; bool has_offset; int64_t map; + char *filename = NULL; /* As an optimization, we could cache the current range of unallocated * clusters in each file of the chain, and avoid querying the same @@ -2817,6 +2816,11 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, has_offset = !!(ret & BDRV_BLOCK_OFFSET_VALID); + if (file && has_offset) { + bdrv_refresh_filename(file); + filename = file->filename; + } + *e = (MapEntry) { .start = offset, .length = bytes, @@ -2825,8 +2829,8 @@ static int get_block_status(BlockDriverState *bs, int64_t offset, .offset = map, .has_offset = has_offset, .depth = depth, - .has_filename = file && has_offset, - .filename = file && has_offset ? file->filename : NULL, + .has_filename = filename, + .filename = filename, }; return 0; @@ -2932,7 +2936,7 @@ static int img_map(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -2966,12 +2970,15 @@ static int img_map(int argc, char **argv) } if (curr.length > 0) { - dump_map_entry(output_format, &curr, &next); + ret = dump_map_entry(output_format, &curr, &next); + if (ret < 0) { + goto out; + } } curr = next; } - dump_map_entry(output_format, &curr, NULL); + ret = dump_map_entry(output_format, &curr, NULL); out: blk_unref(blk); @@ -3081,7 +3088,7 @@ static int img_snapshot(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -3123,11 +3130,18 @@ static int img_snapshot(int argc, char **argv) break; case SNAPSHOT_DELETE: - bdrv_snapshot_delete_by_id_or_name(bs, snapshot_name, &err); - if (err) { - error_reportf_err(err, "Could not delete snapshot '%s': ", - snapshot_name); + ret = bdrv_snapshot_find(bs, &sn, snapshot_name); + if (ret < 0) { + error_report("Could not delete snapshot '%s': snapshot not " + "found", snapshot_name); ret = 1; + } else { + ret = bdrv_snapshot_delete(bs, sn.id_str, sn.name, &err); + if (ret < 0) { + error_reportf_err(err, "Could not delete snapshot '%s': ", + snapshot_name); + ret = 1; + } } break; } @@ -3241,7 +3255,7 @@ static int img_rebase(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -3327,20 +3341,17 @@ static int img_rebase(int argc, char **argv) qdict_put_bool(options, BDRV_OPT_FORCE_SHARE, true); } + bdrv_refresh_filename(bs); overlay_filename = bs->exact_filename[0] ? bs->exact_filename : bs->filename; - out_real_path = g_malloc(PATH_MAX); - - bdrv_get_full_backing_filename_from_filename(overlay_filename, - out_baseimg, - out_real_path, - PATH_MAX, - &local_err); + out_real_path = + bdrv_get_full_backing_filename_from_filename(overlay_filename, + out_baseimg, + &local_err); if (local_err) { error_reportf_err(local_err, "Could not resolve backing filename: "); ret = -1; - g_free(out_real_path); goto out; } @@ -3621,7 +3632,7 @@ static int img_resize(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { return 1; } @@ -3775,7 +3786,7 @@ static int print_amend_option_help(const char *format) assert(drv->create_opts); printf("Creation options for '%s':\n", format); - qemu_opts_print_help(drv->create_opts); + qemu_opts_print_help(drv->create_opts, false); printf("\nNote that not all of these options may be amendable.\n"); return 0; } @@ -3865,7 +3876,7 @@ static int img_amend(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { ret = -1; goto out_no_progress; } @@ -4509,7 +4520,7 @@ static int img_dd(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { ret = -1; goto out; } @@ -4786,7 +4797,7 @@ static int img_measure(int argc, char **argv) if (qemu_opts_foreach(&qemu_object_opts, user_creatable_add_opts_foreach, - NULL, NULL)) { + NULL, &error_fatal)) { goto out; } @@ -4962,7 +4973,7 @@ int main(int argc, char **argv) return 0; } argv += optind; - optind = 0; + qemu_reset_optind(); if (!trace_init_backends()) { exit(1); diff --git a/qemu-img.texi b/qemu-img.texi index 3b6710a580..724f244ba1 100644 --- a/qemu-img.texi +++ b/qemu-img.texi @@ -197,7 +197,7 @@ Command description: @table @option -@item amend [--object @var{objectdef}] [--image-opts] [-p] [-p] [-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} Amends the image format specific @var{options} for the image file @var{filename}. Not all file formats support this operation. @@ -424,7 +424,7 @@ To enumerate information about each disk image in the above chain, starting from qemu-img info --backing-chain snap2.qcow2 @end example -@item map [-f @var{fmt}] [--output=@var{ofmt}] @var{filename} +@item map [--object @var{objectdef}] [--image-opts] [-f @var{fmt}] [--output=@var{ofmt}] [-U] @var{filename} Dump the metadata of image @var{filename} and its backing file chain. In particular, this commands dumps the allocation state of every sector diff --git a/qemu-io-cmds.c b/qemu-io-cmds.c index 5bf5f28178..09750a23ce 100644 --- a/qemu-io-cmds.c +++ b/qemu-io-cmds.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qapi/qmp/qdict.h" #include "qemu-io.h" #include "sysemu/block-backend.h" #include "block/block.h" @@ -113,7 +114,7 @@ static int command(BlockBackend *blk, const cmdinfo_t *ct, int argc, } } - optind = 0; + qemu_reset_optind(); return ct->cfunc(blk, argc, argv); } @@ -907,7 +908,7 @@ static int readv_f(BlockBackend *blk, int argc, char **argv) memset(cmp_buf, pattern, qiov.size); if (memcmp(buf, cmp_buf, qiov.size)) { printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", offset, qiov.size); + PRId64 ", %zu bytes\n", offset, qiov.size); ret = -EINVAL; } g_free(cmp_buf); @@ -945,6 +946,7 @@ static void write_help(void) " -b, -- write to the VM state rather than the virtual disk\n" " -c, -- write compressed data with blk_write_compressed\n" " -f, -- use Force Unit Access semantics\n" +" -n, -- with -z, don't allow slow fallback\n" " -p, -- ignored for backwards compatibility\n" " -P, -- use different pattern to fill file\n" " -C, -- report statistics in a machine parsable format\n" @@ -963,7 +965,7 @@ static const cmdinfo_t write_cmd = { .perm = BLK_PERM_WRITE, .argmin = 2, .argmax = -1, - .args = "[-bcCfquz] [-P pattern] off len", + .args = "[-bcCfnquz] [-P pattern] off len", .oneline = "writes a number of bytes at a specified offset", .help = write_help, }; @@ -982,7 +984,7 @@ static int write_f(BlockBackend *blk, int argc, char **argv) int64_t total = 0; int pattern = 0xcd; - while ((c = getopt(argc, argv, "bcCfpP:quz")) != -1) { + while ((c = getopt(argc, argv, "bcCfnpP:quz")) != -1) { switch (c) { case 'b': bflag = true; @@ -996,6 +998,9 @@ static int write_f(BlockBackend *blk, int argc, char **argv) case 'f': flags |= BDRV_REQ_FUA; break; + case 'n': + flags |= BDRV_REQ_NO_FALLBACK; + break; case 'p': /* Ignored for backwards compatibility */ break; @@ -1036,6 +1041,11 @@ static int write_f(BlockBackend *blk, int argc, char **argv) return -EINVAL; } + if ((flags & BDRV_REQ_NO_FALLBACK) && !zflag) { + printf("-n requires -z to be specified\n"); + return -EINVAL; + } + if ((flags & BDRV_REQ_MAY_UNMAP) && !zflag) { printf("-u requires -z to be specified\n"); return -EINVAL; @@ -1294,7 +1304,7 @@ static void aio_read_done(void *opaque, int ret) memset(cmp_buf, ctx->pattern, ctx->qiov.size); if (memcmp(ctx->buf, cmp_buf, ctx->qiov.size)) { printf("Pattern verification failed at offset %" - PRId64 ", %zd bytes\n", ctx->offset, ctx->qiov.size); + PRId64 ", %zu bytes\n", ctx->offset, ctx->qiov.size); } g_free(cmp_buf); } @@ -1660,6 +1670,7 @@ static int info_f(BlockBackend *blk, int argc, char **argv) BlockDriverState *bs = blk_bs(blk); BlockDriverInfo bdi; ImageInfoSpecific *spec_info; + Error *local_err = NULL; char s1[64], s2[64]; int ret; @@ -1681,7 +1692,11 @@ static int info_f(BlockBackend *blk, int argc, char **argv) printf("cluster size: %s\n", s1); printf("vm state offset: %s\n", s2); - spec_info = bdrv_get_specific_info(bs); + spec_info = bdrv_get_specific_info(bs, &local_err); + if (local_err) { + error_report_err(local_err); + return -EIO; + } if (spec_info) { printf("Format specific information:\n"); bdrv_image_info_specific_dump(fprintf, stdout, spec_info); @@ -1978,6 +1993,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) int flags = bs->open_flags; bool writethrough = !blk_enable_write_cache(blk); bool has_rw_option = false; + bool has_cache_option = false; BlockReopenQueue *brq; Error *local_err = NULL; @@ -1989,6 +2005,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) error_report("Invalid cache option: %s", optarg); return -EINVAL; } + has_cache_option = true; break; case 'o': if (!qemu_opts_parse_noisily(&reopen_opts, optarg, 0)) { @@ -2025,7 +2042,7 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) return -EINVAL; } - if (writethrough != blk_enable_write_cache(blk) && + if (!writethrough != blk_enable_write_cache(blk) && blk_get_attached_dev(blk)) { error_report("Cannot change cache.writeback: Device attached"); @@ -2046,12 +2063,34 @@ static int reopen_f(BlockBackend *blk, int argc, char **argv) } qopts = qemu_opts_find(&reopen_opts, NULL); - opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : NULL; + opts = qopts ? qemu_opts_to_qdict(qopts, NULL) : qdict_new(); qemu_opts_reset(&reopen_opts); + if (qdict_haskey(opts, BDRV_OPT_READ_ONLY)) { + if (has_rw_option) { + error_report("Cannot set both -r/-w and '" BDRV_OPT_READ_ONLY "'"); + qobject_unref(opts); + return -EINVAL; + } + } else { + qdict_put_bool(opts, BDRV_OPT_READ_ONLY, !(flags & BDRV_O_RDWR)); + } + + if (qdict_haskey(opts, BDRV_OPT_CACHE_DIRECT) || + qdict_haskey(opts, BDRV_OPT_CACHE_NO_FLUSH)) { + if (has_cache_option) { + error_report("Cannot set both -c and the cache options"); + qobject_unref(opts); + return -EINVAL; + } + } else { + qdict_put_bool(opts, BDRV_OPT_CACHE_DIRECT, flags & BDRV_O_NOCACHE); + qdict_put_bool(opts, BDRV_OPT_CACHE_NO_FLUSH, flags & BDRV_O_NO_FLUSH); + } + bdrv_subtree_drained_begin(bs); - brq = bdrv_reopen_queue(NULL, bs, opts, flags); - bdrv_reopen_multiple(bdrv_get_aio_context(bs), brq, &local_err); + brq = bdrv_reopen_queue(NULL, bs, opts, true); + bdrv_reopen_multiple(brq, &local_err); bdrv_subtree_drained_end(bs); if (local_err) { diff --git a/qemu-io.c b/qemu-io.c index 13829f5e21..6df7731af4 100644 --- a/qemu-io.c +++ b/qemu-io.c @@ -620,11 +620,9 @@ int main(int argc, char **argv) exit(1); } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, &error_fatal); if (!trace_init_backends()) { exit(1); diff --git a/qemu-keymap.c b/qemu-keymap.c index 6216371aa1..4d00468747 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -84,7 +84,7 @@ static void walk_map(struct xkb_keymap *map, xkb_keycode_t code, void *data) } fprintf(outfile, "# evdev %d (0x%x), QKeyCode \"%s\", number 0x%x\n", evdev, evdev, - QKeyCode_lookup.array[qcode], + QKeyCode_str(qcode), qcode_to_number(qcode)); /* diff --git a/qemu-nbd.c b/qemu-nbd.c index 51b9d38c72..941ba729c2 100644 --- a/qemu-nbd.c +++ b/qemu-nbd.c @@ -43,6 +43,12 @@ #include "trace/control.h" #include "qemu-version.h" +#ifdef __linux__ +#define HAVE_NBD_DEVICE 1 +#else +#define HAVE_NBD_DEVICE 0 +#endif + #define SOCKET_PATH "/var/lock/qemu-nbd-%s" #define QEMU_NBD_OPT_CACHE 256 #define QEMU_NBD_OPT_AIO 257 @@ -52,11 +58,11 @@ #define QEMU_NBD_OPT_TLSCREDS 261 #define QEMU_NBD_OPT_IMAGE_OPTS 262 #define QEMU_NBD_OPT_FORK 263 +#define QEMU_NBD_OPT_TLSAUTHZ 264 #define MBR_SIZE 512 -static NBDExport *exp; -static bool newproto; +static NBDExport *export; static int verbose; static char *srcpath; static SocketAddress *saddr; @@ -66,12 +72,14 @@ static int shared = 1; static int nb_fds; static QIONetListener *server; static QCryptoTLSCreds *tlscreds; +static const char *tlsauthz; static void usage(const char *name) { (printf) ( "Usage: %s [OPTIONS] FILE\n" -"QEMU Disk Network Block Device Server\n" +" or: %s -L [OPTIONS]\n" +"QEMU Disk Network Block Device Utility\n" "\n" " -h, --help display this help and exit\n" " -V, --version output version information and exit\n" @@ -84,25 +92,30 @@ 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" -" -D, --description=TEXT with -x, also export a human-readable description\n" +" -x, --export-name=NAME expose export by name (default is empty string)\n" +" -D, --description=TEXT export a human-readable description\n" "\n" "Exposing part of the image:\n" " -o, --offset=OFFSET offset into the image\n" " -P, --partition=NUM only expose partition NUM\n" +" -B, --bitmap=NAME expose a persistent dirty bitmap\n" "\n" "General purpose options:\n" +" -L, --list list exports available from another NBD server\n" " --object type,id=ID,... define an object such as 'secret' for providing\n" " passwords and/or encryption keys\n" +" --tls-creds=ID use id of an earlier --object to provide TLS\n" +" --tls-authz=ID use id of an earlier --object to provide\n" +" authorization\n" " -T, --trace [[enable=]][,events=][,file=]\n" " specify tracing options\n" " --fork fork off the server process and exit the parent\n" " once the server is running\n" -#ifdef __linux__ +#if HAVE_NBD_DEVICE +"\n" "Kernel NBD client support:\n" " -c, --connect=DEV connect FILE to the local NBD device DEV\n" " -d, --disconnect disconnect the specified device\n" -"\n" #endif "\n" "Block device options:\n" @@ -124,7 +137,7 @@ static void usage(const char *name) " --image-opts treat FILE as a full set of image options\n" "\n" QEMU_HELP_BOTTOM "\n" - , name, NBD_DEFAULT_PORT, "DEVICE"); + , name, name, NBD_DEFAULT_PORT, "DEVICE"); } static void version(const char *name) @@ -169,7 +182,7 @@ static void read_partition(uint8_t *p, struct partition_record *r) } static int find_partition(BlockBackend *blk, int partition, - off_t *offset, off_t *size) + uint64_t *offset, uint64_t *size) { struct partition_record mbr[4]; uint8_t data[MBR_SIZE]; @@ -236,6 +249,92 @@ static void termsig_handler(int signum) } +static int qemu_nbd_client_list(SocketAddress *saddr, QCryptoTLSCreds *tls, + const char *hostname) +{ + int ret = EXIT_FAILURE; + int rc; + Error *err = NULL; + QIOChannelSocket *sioc; + NBDExportInfo *list; + int i, j; + + sioc = qio_channel_socket_new(); + if (qio_channel_socket_connect_sync(sioc, saddr, &err) < 0) { + error_report_err(err); + return EXIT_FAILURE; + } + rc = nbd_receive_export_list(QIO_CHANNEL(sioc), tls, hostname, &list, + &err); + if (rc < 0) { + if (err) { + error_report_err(err); + } + goto out; + } + printf("exports available: %d\n", rc); + for (i = 0; i < rc; i++) { + printf(" export: '%s'\n", list[i].name); + if (list[i].description && *list[i].description) { + printf(" description: %s\n", list[i].description); + } + if (list[i].flags & NBD_FLAG_HAS_FLAGS) { + printf(" size: %" PRIu64 "\n", list[i].size); + printf(" flags: 0x%x (", list[i].flags); + if (list[i].flags & NBD_FLAG_READ_ONLY) { + printf(" readonly"); + } + if (list[i].flags & NBD_FLAG_SEND_FLUSH) { + printf(" flush"); + } + if (list[i].flags & NBD_FLAG_SEND_FUA) { + printf(" fua"); + } + if (list[i].flags & NBD_FLAG_ROTATIONAL) { + printf(" rotational"); + } + if (list[i].flags & NBD_FLAG_SEND_TRIM) { + printf(" trim"); + } + if (list[i].flags & NBD_FLAG_SEND_WRITE_ZEROES) { + printf(" zeroes"); + } + if (list[i].flags & NBD_FLAG_SEND_DF) { + printf(" df"); + } + if (list[i].flags & NBD_FLAG_CAN_MULTI_CONN) { + printf(" multi"); + } + if (list[i].flags & NBD_FLAG_SEND_RESIZE) { + printf(" resize"); + } + if (list[i].flags & NBD_FLAG_SEND_CACHE) { + printf(" cache"); + } + printf(" )\n"); + } + if (list[i].min_block) { + printf(" min block: %u\n", list[i].min_block); + printf(" opt block: %u\n", list[i].opt_block); + printf(" max block: %u\n", list[i].max_block); + } + if (list[i].n_contexts) { + printf(" available meta contexts: %d\n", list[i].n_contexts); + for (j = 0; j < list[i].n_contexts; j++) { + printf(" %s\n", list[i].contexts[j]); + } + } + } + nbd_free_export_list(list, rc); + + ret = EXIT_SUCCESS; + out: + object_unref(OBJECT(sioc)); + return ret; +} + + +#if HAVE_NBD_DEVICE static void *show_parts(void *arg) { char *device = arg; @@ -256,7 +355,7 @@ static void *show_parts(void *arg) static void *nbd_client_thread(void *arg) { char *device = arg; - NBDExportInfo info = { .request_sizes = false, }; + NBDExportInfo info = { .request_sizes = false, .name = g_strdup("") }; QIOChannelSocket *sioc; int fd; int ret; @@ -271,7 +370,7 @@ static void *nbd_client_thread(void *arg) goto out; } - ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, + ret = nbd_receive_negotiate(QIO_CHANNEL(sioc), NULL, NULL, NULL, &info, &local_error); if (ret < 0) { if (local_error) { @@ -310,6 +409,7 @@ static void *nbd_client_thread(void *arg) } close(fd); object_unref(OBJECT(sioc)); + g_free(info.name); kill(getpid(), SIGTERM); return (void *) EXIT_SUCCESS; @@ -318,16 +418,18 @@ out_fd: out_socket: object_unref(OBJECT(sioc)); out: + g_free(info.name); kill(getpid(), SIGTERM); return (void *) EXIT_FAILURE; } +#endif /* HAVE_NBD_DEVICE */ static int nbd_can_accept(void) { return state == RUNNING && nb_fds < shared; } -static void nbd_export_closed(NBDExport *exp) +static void nbd_export_closed(NBDExport *export) { assert(state == TERMINATING); state = TERMINATED; @@ -354,8 +456,7 @@ static void nbd_accept(QIONetListener *listener, QIOChannelSocket *cioc, nb_fds++; nbd_update_server_watch(); - nbd_client_new(newproto ? NULL : exp, cioc, - tlscreds, NULL, nbd_client_closed); + nbd_client_new(cioc, tlscreds, tlsauthz, nbd_client_closed); } static void nbd_update_server_watch(void) @@ -415,7 +516,8 @@ static QemuOptsList qemu_object_opts = { -static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) +static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, bool list, + Error **errp) { Object *obj; QCryptoTLSCreds *creds; @@ -435,10 +537,18 @@ static QCryptoTLSCreds *nbd_get_tls_creds(const char *id, Error **errp) return NULL; } - if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_SERVER) { - error_setg(errp, - "Expecting TLS credentials with a server endpoint"); - return NULL; + if (list) { + if (creds->endpoint != QCRYPTO_TLS_CREDS_ENDPOINT_CLIENT) { + error_setg(errp, + "Expecting TLS credentials with a client endpoint"); + return NULL; + } + } else { + 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; @@ -461,7 +571,8 @@ static void setup_address_and_port(const char **address, const char **port) static const char *socket_activation_validate_opts(const char *device, const char *sockpath, const char *address, - const char *port) + const char *port, + bool list) { if (device != NULL) { return "NBD device can't be set when using socket activation"; @@ -479,6 +590,10 @@ static const char *socket_activation_validate_opts(const char *device, return "TCP port number can't be set when using socket activation"; } + if (list) { + return "List mode is incompatible with socket activation"; + } + return NULL; } @@ -492,17 +607,17 @@ int main(int argc, char **argv) { BlockBackend *blk; BlockDriverState *bs; - off_t dev_offset = 0; + uint64_t dev_offset = 0; uint16_t nbdflags = 0; bool disconnect = false; const char *bindto = NULL; const char *port = NULL; char *sockpath = NULL; char *device = NULL; - off_t fd_size; + int64_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:x:T:D:"; + const char *sopt = "hVb:o:p:rsnP:c:dvk:e:f:tl:x:T:D:B:L"; struct option lopt[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'V' }, @@ -512,8 +627,10 @@ int main(int argc, char **argv) { "offset", required_argument, NULL, 'o' }, { "read-only", no_argument, NULL, 'r' }, { "partition", required_argument, NULL, 'P' }, + { "bitmap", required_argument, NULL, 'B' }, { "connect", required_argument, NULL, 'c' }, { "disconnect", no_argument, NULL, 'd' }, + { "list", no_argument, NULL, 'L' }, { "snapshot", no_argument, NULL, 's' }, { "load-snapshot", required_argument, NULL, 'l' }, { "nocache", no_argument, NULL, 'n' }, @@ -530,6 +647,7 @@ int main(int argc, char **argv) { "export-name", required_argument, NULL, 'x' }, { "description", required_argument, NULL, 'D' }, { "tls-creds", required_argument, NULL, QEMU_NBD_OPT_TLSCREDS }, + { "tls-authz", required_argument, NULL, QEMU_NBD_OPT_TLSAUTHZ }, { "image-opts", no_argument, NULL, QEMU_NBD_OPT_IMAGE_OPTS }, { "trace", required_argument, NULL, 'T' }, { "fork", no_argument, NULL, QEMU_NBD_OPT_FORK }, @@ -537,9 +655,8 @@ int main(int argc, char **argv) }; int ch; int opt_ind = 0; - char *end; int flags = BDRV_O_RDWR; - int partition = -1; + int partition = 0; int ret = 0; bool seen_cache = false; bool seen_discard = false; @@ -549,13 +666,15 @@ int main(int argc, char **argv) Error *local_err = NULL; BlockdevDetectZeroesOptions detect_zeroes = BLOCKDEV_DETECT_ZEROES_OPTIONS_OFF; QDict *options = NULL; - const char *export_name = NULL; + const char *export_name = NULL; /* defaults to "" later for server mode */ const char *export_description = NULL; + const char *bitmap = NULL; const char *tlscredsid = NULL; bool imageOpts = false; bool writethrough = true; char *trace_file = NULL; bool fork_process = false; + bool list = false; int old_stderr = -1; unsigned socket_activation; @@ -572,6 +691,7 @@ int main(int argc, char **argv) #endif module_call_init(MODULE_INIT_TRACE); + error_set_progname(argv[0]); qcrypto_init(&error_fatal); module_call_init(MODULE_INIT_QOM); @@ -649,13 +769,8 @@ int main(int argc, char **argv) port = optarg; break; case 'o': - dev_offset = strtoll (optarg, &end, 0); - if (*end) { - error_report("Invalid offset `%s'", optarg); - exit(EXIT_FAILURE); - } - if (dev_offset < 0) { - error_report("Offset must be positive `%s'", optarg); + if (qemu_strtou64(optarg, NULL, 0, &dev_offset) < 0) { + error_report("Invalid offset '%s'", optarg); exit(EXIT_FAILURE); } break; @@ -677,16 +792,17 @@ int main(int argc, char **argv) flags &= ~BDRV_O_RDWR; break; case 'P': - partition = strtol(optarg, &end, 0); - if (*end) { - error_report("Invalid partition `%s'", optarg); - exit(EXIT_FAILURE); - } - if (partition < 1 || partition > 8) { - error_report("Invalid partition %d", partition); + warn_report("The '-P' option is deprecated; use --image-opts with " + "a raw device wrapper for subset exports instead"); + if (qemu_strtoi(optarg, NULL, 0, &partition) < 0 || + partition < 1 || partition > 8) { + error_report("Invalid partition '%s'", optarg); exit(EXIT_FAILURE); } break; + case 'B': + bitmap = optarg; + break; case 'k': sockpath = optarg; if (sockpath[0] != '/') { @@ -701,15 +817,11 @@ int main(int argc, char **argv) device = optarg; break; case 'e': - shared = strtol(optarg, &end, 0); - if (*end) { + if (qemu_strtoi(optarg, NULL, 0, &shared) < 0 || + shared < 1) { error_report("Invalid shared device number '%s'", optarg); exit(EXIT_FAILURE); } - if (shared < 1) { - error_report("Shared device number must be greater than 0"); - exit(EXIT_FAILURE); - } break; case 'f': fmt = optarg; @@ -755,23 +867,44 @@ int main(int argc, char **argv) g_free(trace_file); trace_file = trace_opt_parse(optarg); break; + case QEMU_NBD_OPT_TLSAUTHZ: + tlsauthz = optarg; + break; case QEMU_NBD_OPT_FORK: fork_process = true; break; + case 'L': + list = true; + break; } } - if ((argc - optind) != 1) { + if (list) { + if (argc != optind) { + error_report("List mode is incompatible with a file name"); + exit(EXIT_FAILURE); + } + if (export_name || export_description || dev_offset || partition || + device || disconnect || fmt || sn_id_or_name || bitmap || + seen_aio || seen_discard || seen_cache) { + error_report("List mode is incompatible with per-device settings"); + exit(EXIT_FAILURE); + } + if (fork_process) { + error_report("List mode is incompatible with forking"); + exit(EXIT_FAILURE); + } + } else if ((argc - optind) != 1) { error_report("Invalid number of arguments"); error_printf("Try `%s --help' for more information.\n", argv[0]); exit(EXIT_FAILURE); + } else if (!export_name) { + export_name = ""; } - if (qemu_opts_foreach(&qemu_object_opts, - user_creatable_add_opts_foreach, - NULL, NULL)) { - exit(EXIT_FAILURE); - } + qemu_opts_foreach(&qemu_object_opts, + user_creatable_add_opts_foreach, + NULL, &error_fatal); if (!trace_init_backends()) { exit(1); @@ -785,7 +918,8 @@ int main(int argc, char **argv) } else { /* Using socket activation - check user didn't use -p etc. */ const char *err_msg = socket_activation_validate_opts(device, sockpath, - bindto, port); + bindto, port, + list); if (err_msg != NULL) { error_report("%s", err_msg); exit(EXIT_FAILURE); @@ -808,19 +942,34 @@ int main(int argc, char **argv) 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 = ""; + if (tlsauthz && list) { + error_report("TLS authorization is incompatible with export list"); + exit(EXIT_FAILURE); } - tlscreds = nbd_get_tls_creds(tlscredsid, &local_err); + tlscreds = nbd_get_tls_creds(tlscredsid, list, &local_err); if (local_err) { error_report("Failed to get TLS creds %s", error_get_pretty(local_err)); exit(EXIT_FAILURE); } + } else { + if (tlsauthz) { + error_report("--tls-authz is not permitted without --tls-creds"); + exit(EXIT_FAILURE); + } } + if (list) { + saddr = nbd_build_socket_address(sockpath, bindto, port); + return qemu_nbd_client_list(saddr, tlscreds, bindto); + } + +#if !HAVE_NBD_DEVICE + if (disconnect || device) { + error_report("Kernel /dev/nbdN support not available"); + exit(EXIT_FAILURE); + } +#else /* HAVE_NBD_DEVICE */ if (disconnect) { int nbdfd = open(argv[optind], O_RDWR); if (nbdfd < 0) { @@ -836,6 +985,7 @@ int main(int argc, char **argv) return 0; } +#endif if ((device && !verbose) || fork_process) { int stderr_fd[2]; @@ -991,38 +1141,46 @@ int main(int argc, char **argv) } if (dev_offset >= fd_size) { - error_report("Offset (%lld) has to be smaller than the image size " - "(%lld)", - (long long int)dev_offset, (long long int)fd_size); + error_report("Offset (%" PRIu64 ") has to be smaller than the image " + "size (%" PRId64 ")", dev_offset, fd_size); exit(EXIT_FAILURE); } fd_size -= dev_offset; - if (partition != -1) { - ret = find_partition(blk, partition, &dev_offset, &fd_size); + if (partition) { + uint64_t limit; + + if (dev_offset) { + error_report("Cannot request partition and offset together"); + exit(EXIT_FAILURE); + } + ret = find_partition(blk, partition, &dev_offset, &limit); if (ret < 0) { error_report("Could not find partition %d: %s", partition, strerror(-ret)); exit(EXIT_FAILURE); } + /* + * MBR partition limits are (32-bit << 9); this assert lets + * the compiler know that we can't overflow 64 bits. + */ + assert(dev_offset + limit >= dev_offset); + if (dev_offset + limit > fd_size) { + error_report("Discovered partition %d at offset %" PRIu64 + " size %" PRIu64 ", but size exceeds file length %" + PRId64, partition, dev_offset, limit, fd_size); + exit(EXIT_FAILURE); + } + fd_size = limit; } - exp = nbd_export_new(bs, dev_offset, fd_size, nbdflags, nbd_export_closed, - writethrough, NULL, &local_err); - if (!exp) { - error_report_err(local_err); - exit(EXIT_FAILURE); - } - if (export_name) { - nbd_export_set_name(exp, export_name); - nbd_export_set_description(exp, export_description); - newproto = true; - } else if (export_description) { - error_report("Export description requires an export name"); - exit(EXIT_FAILURE); - } + export = nbd_export_new(bs, dev_offset, fd_size, export_name, + export_description, bitmap, nbdflags, + nbd_export_closed, writethrough, NULL, + &error_fatal); if (device) { +#if HAVE_NBD_DEVICE int ret; ret = pthread_create(&client_thread, NULL, nbd_client_thread, device); @@ -1030,6 +1188,7 @@ int main(int argc, char **argv) error_report("Failed to create client thread: %s", strerror(ret)); exit(EXIT_FAILURE); } +#endif } else { /* Shut up GCC warnings. */ memset(&client_thread, 0, sizeof(client_thread)); @@ -1055,9 +1214,9 @@ int main(int argc, char **argv) main_loop_wait(false); if (state == TERMINATE) { state = TERMINATING; - nbd_export_close(exp); - nbd_export_put(exp); - exp = NULL; + nbd_export_close(export); + nbd_export_put(export); + export = NULL; } } while (state != TERMINATED); diff --git a/qemu-nbd.texi b/qemu-nbd.texi index 9a84e81eed..de342c76b8 100644 --- a/qemu-nbd.texi +++ b/qemu-nbd.texi @@ -2,6 +2,8 @@ @c man begin SYNOPSIS @command{qemu-nbd} [OPTION]... @var{filename} +@command{qemu-nbd} @option{-L} [OPTION]... + @command{qemu-nbd} @option{-d} @var{dev} @c man end @end example @@ -10,11 +12,19 @@ Export a QEMU disk image using the NBD protocol. +Other uses: +@itemize +@item +Bind a /dev/nbdX block device to a QEMU server (on Linux). +@item +As a client to query exports of a remote NBD server. +@end itemize + @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. +driver options if @option{--image-opts} is specified. @var{dev} is an NBD device. @@ -25,30 +35,39 @@ 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. +credentials for the qemu-nbd server or client. @item -p, --port=@var{port} -The TCP port to listen on (default @samp{10809}) +The TCP port to listen on as a server, or connect to as a client +(default @samp{10809}). @item -o, --offset=@var{offset} -The offset into the image +The offset into the image. @item -b, --bind=@var{iface} -The interface to bind to (default @samp{0.0.0.0}) +The interface to bind to as a server, or connect to as a client +(default @samp{0.0.0.0}). @item -k, --socket=@var{path} -Use a unix socket with path @var{path} +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 +auto-detecting. @item -r, --read-only -Export the disk as read-only +Export the disk as read-only. @item -P, --partition=@var{num} -Only expose partition @var{num} +Deprecated: Only expose MBR partition @var{num}. Understands physical +partitions 1-4 and logical partition 5. New code should instead use +@option{--image-opts} with the raw driver wrapping a subset of the +original image. +@item -B, --bitmap=@var{name} +If @var{filename} has a qcow2 persistent bitmap @var{name}, expose +that bitmap via the ``qemu:dirty-bitmap:@var{name}'' context +accessible through NBD_OPT_SET_META_CONTEXT. @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 +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 @@ -72,31 +91,42 @@ driver-specific optimized zero write commands. @var{detect-zeroes} is one of 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} (Linux only). @item -d, --disconnect -Disconnect the device @var{dev} +Disconnect the device @var{dev} (Linux only). @item -e, --shared=@var{num} -Allow up to @var{num} clients to share the device (default @samp{1}) +Allow up to @var{num} clients to share the device (default +@samp{1}). Safe for readers, but for now, consistency is not +guaranteed between multiple writers. @item -t, --persistent -Don't exit on the last connection +Don't exit on the last connection. @item -x, --export-name=@var{name} -Set the NBD volume export name. This switches the server to use -the new style NBD protocol negotiation +Set the NBD volume export name (default of a zero-length string). @item -D, --description=@var{description} Set the NBD volume export description, as a human-readable -string. Requires the use of @option{-x} +string. +@item -L, --list +Connect as a client and list all details about the exports exposed by +a remote NBD server. This enables list mode, and is incompatible +with options that change behavior related to a specific export (such as +@option{--export-name}, @option{--offset}, ...). @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. +option; or provide the credentials needed for connecting as a client +in list mode. @item --fork Fork off the server process and exit the parent once the server is running. +@item --tls-authz=ID +Specify the ID of a qauthz object previously created with the +--object option. This will be used to authorize connecting users +against their x509 distinguished name. @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 -Display version information and exit +Display version information and exit. @item -T, --trace [[enable=]@var{pattern}][,events=@var{file}][,file=@var{file}] @findex --trace @include qemu-option-trace.texi @@ -104,6 +134,66 @@ Display version information and exit @c man end +@c man begin EXAMPLES +Start a server listening on port 10809 that exposes only the +guest-visible contents of a qcow2 file, with no TLS encryption, and +with the default export name (an empty string). The command is +one-shot, and will block until the first successful client +disconnects: + +@example +qemu-nbd -f qcow2 file.qcow2 +@end example + +Start a long-running server listening with encryption on port 10810, +and whitelist clients with a specific X.509 certificate to connect to +a 1 megabyte subset of a raw file, using the export name 'subset': + +@example +qemu-nbd \ + --object tls-creds-x509,id=tls0,endpoint=server,dir=/path/to/qemutls \ + --object 'authz-simple,id=auth0,identity=CN=laptop.example.com,,\ + O=Example Org,,L=London,,ST=London,,C=GB' \ + --tls-creds tls0 --tls-authz auth0 \ + -t -x subset -p 10810 \ + --image-opts driver=raw,offset=1M,size=1M,file.driver=file,file.filename=file.raw +@end example + +Serve a read-only copy of just the first MBR partition of a guest +image over a Unix socket with as many as 5 simultaneous readers, with +a persistent process forked as a daemon: + +@example +qemu-nbd --fork --persistent --shared=5 --socket=/path/to/sock \ + --partition=1 --read-only --format=qcow2 file.qcow2 +@end example + +Expose the guest-visible contents of a qcow2 file via a block device +/dev/nbd0 (and possibly creating /dev/nbd0p1 and friends for +partitions found within), then disconnect the device when done. +Access to bind qemu-nbd to an /dev/nbd device generally requires root +privileges, and may also require the execution of @code{modprobe nbd} +to enable the kernel NBD client module. @emph{CAUTION}: Do not use +this method to mount filesystems from an untrusted guest image - a +malicious guest may have prepared the image to attempt to trigger +kernel bugs in partition probing or file system mounting. + +@example +qemu-nbd -c /dev/nbd0 -f qcow2 file.qcow2 +qemu-nbd -d /dev/nbd0 +@end example + +Query a remote server to see details about what export(s) it is +serving on port 10809, and authenticating via PSK: + +@example +qemu-nbd \ + --object tls-creds-psk,id=tls0,dir=/tmp/keys,username=eblake,endpoint=client \ + --tls-creds tls0 -L -b remote.example.com +@end example + +@c man end + @ignore @setfilename qemu-nbd diff --git a/qemu-options.hx b/qemu-options.hx index b1bf0f485f..08749a3391 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -43,7 +43,6 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " suppress-vmdesc=on|off disables self-describing migration (default=off)\n" " nvdimm=on|off controls NVDIMM support (default=off)\n" " enforce-config-section=on|off enforce configuration section migration (default=off)\n" - " s390-squash-mcss=on|off (deprecated) controls support for squashing into default css (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n", QEMU_ARCH_ALL) STEXI @@ -96,15 +95,6 @@ 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. -@item s390-squash-mcss=on|off -Enables or disables squashing subchannels into the default css. -The default is off. -NOTE: This property is deprecated and will be removed in future releases. -The ``s390-squash-mcss=on`` property has been obsoleted by allowing the -cssid to be chosen freely. Instead of squashing subchannels into the -default channel subsystem image for guests that do not support multiple -channel subsystems, all devices can be put into the default channel -subsystem image. @item enforce-config-section=on|off If @option{enforce-config-section} is set to @var{on}, force migration code to send configuration section even if the machine-type sets the @@ -426,14 +416,244 @@ The default is @code{en-us}. ETEXI +HXCOMM Deprecated by -audiodev DEF("audio-help", 0, QEMU_OPTION_audio_help, - "-audio-help print list of audio drivers and their options\n", + "-audio-help show -audiodev equivalent of the currently specified audio settings\n", QEMU_ARCH_ALL) STEXI @item -audio-help @findex -audio-help -Will show the audio subsystem help: list of drivers, tunable -parameters. +Will show the -audiodev equivalent of the currently specified +(deprecated) environment variables. +ETEXI + +DEF("audiodev", HAS_ARG, QEMU_OPTION_audiodev, + "-audiodev [driver=]driver,id=id[,prop[=value][,...]]\n" + " specifies the audio backend to use\n" + " id= identifier of the backend\n" + " timer-period= timer period in microseconds\n" + " in|out.fixed-settings= use fixed settings for host audio\n" + " in|out.frequency= frequency to use with fixed settings\n" + " in|out.channels= number of channels to use with fixed settings\n" + " in|out.format= sample format to use with fixed settings\n" + " valid values: s8, s16, s32, u8, u16, u32\n" + " in|out.voices= number of voices to use\n" + " in|out.buffer-len= length of buffer in microseconds\n" + "-audiodev none,id=id,[,prop[=value][,...]]\n" + " dummy driver that discards all output\n" +#ifdef CONFIG_AUDIO_ALSA + "-audiodev alsa,id=id[,prop[=value][,...]]\n" + " in|out.dev= name of the audio device to use\n" + " in|out.period-len= length of period in microseconds\n" + " in|out.try-poll= attempt to use poll mode\n" + " threshold= threshold (in microseconds) when playback starts\n" +#endif +#ifdef CONFIG_AUDIO_COREAUDIO + "-audiodev coreaudio,id=id[,prop[=value][,...]]\n" + " in|out.buffer-count= number of buffers\n" +#endif +#ifdef CONFIG_AUDIO_DSOUND + "-audiodev dsound,id=id[,prop[=value][,...]]\n" + " latency= add extra latency to playback in microseconds\n" +#endif +#ifdef CONFIG_AUDIO_OSS + "-audiodev oss,id=id[,prop[=value][,...]]\n" + " in|out.dev= path of the audio device to use\n" + " in|out.buffer-count= number of buffers\n" + " in|out.try-poll= attempt to use poll mode\n" + " try-mmap= try using memory mapped access\n" + " exclusive= open device in exclusive mode\n" + " dsp-policy= set timing policy (0..10), -1 to use fragment mode\n" +#endif +#ifdef CONFIG_AUDIO_PA + "-audiodev pa,id=id[,prop[=value][,...]]\n" + " server= PulseAudio server address\n" + " in|out.name= source/sink device name\n" +#endif +#ifdef CONFIG_AUDIO_SDL + "-audiodev sdl,id=id[,prop[=value][,...]]\n" +#endif +#ifdef CONFIG_SPICE + "-audiodev spice,id=id[,prop[=value][,...]]\n" +#endif + "-audiodev wav,id=id[,prop[=value][,...]]\n" + " path= path of wav file to record\n", + QEMU_ARCH_ALL) +STEXI +@item -audiodev [driver=]@var{driver},id=@var{id}[,@var{prop}[=@var{value}][,...]] +@findex -audiodev +Adds a new audio backend @var{driver} identified by @var{id}. There are +global and driver specific properties. Some values can be set +differently for input and output, they're marked with @code{in|out.}. +You can set the input's property with @code{in.@var{prop}} and the +output's property with @code{out.@var{prop}}. For example: +@example +-audiodev alsa,id=example,in.frequency=44110,out.frequency=8000 +-audiodev alsa,id=example,out.channels=1 # leaves in.channels unspecified +@end example + +Valid global options are: + +@table @option +@item id=@var{identifier} +Identifies the audio backend. + +@item timer-period=@var{period} +Sets the timer @var{period} used by the audio subsystem in microseconds. +Default is 10000 (10 ms). + +@item in|out.fixed-settings=on|off +Use fixed settings for host audio. When off, it will change based on +how the guest opens the sound card. In this case you must not specify +@var{frequency}, @var{channels} or @var{format}. Default is on. + +@item in|out.frequency=@var{frequency} +Specify the @var{frequency} to use when using @var{fixed-settings}. +Default is 44100Hz. + +@item in|out.channels=@var{channels} +Specify the number of @var{channels} to use when using +@var{fixed-settings}. Default is 2 (stereo). + +@item in|out.format=@var{format} +Specify the sample @var{format} to use when using @var{fixed-settings}. +Valid values are: @code{s8}, @code{s16}, @code{s32}, @code{u8}, +@code{u16}, @code{u32}. Default is @code{s16}. + +@item in|out.voices=@var{voices} +Specify the number of @var{voices} to use. Default is 1. + +@item in|out.buffer=@var{usecs} +Sets the size of the buffer in microseconds. + +@end table + +@item -audiodev none,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a dummy backend that discards all outputs. This backend has no +backend specific properties. + +@item -audiodev alsa,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates backend using the ALSA. This backend is only available on +Linux. + +ALSA specific options are: + +@table @option + +@item in|out.dev=@var{device} +Specify the ALSA @var{device} to use for input and/or output. Default +is @code{default}. + +@item in|out.period-len=@var{usecs} +Sets the period length in microseconds. + +@item in|out.try-poll=on|off +Attempt to use poll mode with the device. Default is on. + +@item threshold=@var{threshold} +Threshold (in microseconds) when playback starts. Default is 0. + +@end table + +@item -audiodev coreaudio,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend using Apple's Core Audio. This backend is only +available on Mac OS and only supports playback. + +Core Audio specific options are: + +@table @option + +@item in|out.buffer-count=@var{count} +Sets the @var{count} of the buffers. + +@end table + +@item -audiodev dsound,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend using Microsoft's DirectSound. This backend is only +available on Windows and only supports playback. + +DirectSound specific options are: + +@table @option + +@item latency=@var{usecs} +Add extra @var{usecs} microseconds latency to playback. Default is +10000 (10 ms). + +@end table + +@item -audiodev oss,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend using OSS. This backend is available on most +Unix-like systems. + +OSS specific options are: + +@table @option + +@item in|out.dev=@var{device} +Specify the file name of the OSS @var{device} to use. Default is +@code{/dev/dsp}. + +@item in|out.buffer-count=@var{count} +Sets the @var{count} of the buffers. + +@item in|out.try-poll=on|of +Attempt to use poll mode with the device. Default is on. + +@item try-mmap=on|off +Try using memory mapped device access. Default is off. + +@item exclusive=on|off +Open the device in exclusive mode (vmix won't work in this case). +Default is off. + +@item dsp-policy=@var{policy} +Sets the timing policy (between 0 and 10, where smaller number means +smaller latency but higher CPU usage). Use -1 to use buffer sizes +specified by @code{buffer} and @code{buffer-count}. This option is +ignored if you do not have OSS 4. Default is 5. + +@end table + +@item -audiodev pa,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend using PulseAudio. This backend is available on most +systems. + +PulseAudio specific options are: + +@table @option + +@item server=@var{server} +Sets the PulseAudio @var{server} to connect to. + +@item in|out.name=@var{sink} +Use the specified source/sink for recording/playback. + +@end table + +@item -audiodev sdl,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend using SDL. This backend is available on most systems, +but you should use your platform's native backend if possible. This +backend has no backend specific properties. + +@item -audiodev spice,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend that sends audio through SPICE. This backend requires +@code{-spice} and automatically selected in that case, so usually you +can ignore this option. This backend has no backend specific +properties. + +@item -audiodev wav,id=@var{id}[,@var{prop}[=@var{value}][,...]] +Creates a backend that writes audio to a WAV file. + +Backend specific options are: + +@table @option + +@item path=@var{path} +Write recorded audio into the specified file. Default is +@code{qemu.wav}. + +@end table ETEXI DEF("soundhw", HAS_ARG, QEMU_OPTION_soundhw, @@ -464,16 +684,6 @@ modprobe i810_audio clocking=48000 @end example ETEXI -DEF("balloon", HAS_ARG, QEMU_OPTION_balloon, - "-balloon virtio[,addr=str]\n" - " enable virtio balloon device (deprecated)\n", QEMU_ARCH_ALL) -STEXI -@item -balloon virtio[,addr=@var{addr}] -@findex -balloon -Enable virtio balloon device, optionally with PCI address @var{addr}. This -option is deprecated, use @option{-device virtio-balloon} instead. -ETEXI - DEF("device", HAS_ARG, QEMU_OPTION_device, "-device driver[,prop[=value][,...]]\n" " add device (based on driver)\n" @@ -558,8 +768,8 @@ ETEXI DEF("name", HAS_ARG, QEMU_OPTION_name, "-name string1[,process=string2][,debug-threads=on|off]\n" " set the name of the guest\n" - " string1 sets the window title and string2 the process name (on Linux)\n" - " When debug-threads is enabled, individual threads are given a separate name (on Linux)\n" + " string1 sets the window title and string2 the process name\n" + " When debug-threads is enabled, individual threads are given a separate name\n" " NOTE: The thread names are for debugging and not a stable API.\n", QEMU_ARCH_ALL) STEXI @@ -752,19 +962,23 @@ image file) @item cache-size The maximum total size of the L2 table and refcount block caches in bytes -(default: 1048576 bytes or 8 clusters, whichever is larger) +(default: the sum of l2-cache-size and refcount-cache-size) @item l2-cache-size The maximum size of the L2 table cache in bytes -(default: 4/5 of the total cache size) +(default: if cache-size is not specified - 32M on Linux platforms, and 8M on +non-Linux platforms; otherwise, as large as possible within the cache-size, +while permitting the requested or the minimal refcount cache size) @item refcount-cache-size The maximum size of the refcount block cache in bytes -(default: 1/5 of the total cache size) +(default: 4 times the cluster size; or if cache-size is specified, the part of +it which is not used for the L2 cache) @item cache-clean-interval Clean unused entries in the L2 and refcount caches. The interval is in seconds. -The default value is 0 and it disables this feature. +The default value is 600 on supporting platforms, and 0 on other platforms. +Setting it to 0 disables this feature. @item pass-discard-request Whether discard requests to the qcow2 device should be forwarded to the data @@ -804,9 +1018,8 @@ ETEXI DEF("drive", HAS_ARG, QEMU_OPTION_drive, "-drive [file=file][,if=type][,bus=n][,unit=m][,media=d][,index=i]\n" - " [,cyls=c,heads=h,secs=s[,trans=t]][,snapshot=on|off]\n" " [,cache=writethrough|writeback|none|directsync|unsafe][,format=f]\n" - " [,serial=s][,addr=A][,rerror=ignore|stop|report]\n" + " [,snapshot=on|off][,rerror=ignore|stop|report]\n" " [,werror=ignore|stop|report|enospc][,id=name][,aio=threads|native]\n" " [,readonly=on|off][,copy-on-read=on|off]\n" " [,discard=ignore|unmap][,detect-zeroes=on|off|unmap]\n" @@ -847,10 +1060,6 @@ This option defines where is connected the drive by using an index in the list of available connectors of a given interface type. @item media=@var{media} This option defines the type of the media: disk or cdrom. -@item cyls=@var{c},heads=@var{h},secs=@var{s}[,trans=@var{t}] -Force disk physical geometry and the optional BIOS translation (trans=none or -lba). These parameters are deprecated, use the corresponding parameters -of @code{-device} instead. @item snapshot=@var{snapshot} @var{snapshot} is "on" or "off" and controls snapshot mode for the given drive (see @option{-snapshot}). @@ -884,13 +1093,6 @@ The default mode is @option{cache=writeback}. Specify which disk @var{format} will be used rather than detecting the format. Can be used to specify format=raw to avoid interpreting an untrusted format header. -@item serial=@var{serial} -This option specifies the serial number to assign to the device. This -parameter is deprecated, use the corresponding parameter of @code{-device} -instead. -@item addr=@var{addr} -Specify the controller's PCI address (if=virtio only). This parameter is -deprecated, use the corresponding parameter of @code{-device} instead. @item werror=@var{action},rerror=@var{action} Specify which @var{action} to take on write and read errors. Valid actions are: "ignore" (ignore the error and try to continue), "stop" (pause QEMU), @@ -1047,7 +1249,7 @@ Define a new file system device. Valid options are: @table @option @item @var{fsdriver} This option specifies the fs driver backend to use. -Currently "local", "handle" and "proxy" file system drivers are supported. +Currently "local" and "proxy" file system drivers are supported. @item id=@var{id} Specifies identifier for this device @item path=@var{path} @@ -1065,7 +1267,7 @@ hidden .virtfs_metadata directory. Directories exported by this security model c interact with other unix tools. "none" security model is same as passthrough except the sever won't report failures if it fails to set file attributes like ownership. Security model is mandatory -only for local fsdriver. Other fsdrivers (like handle, proxy) don't take +only for local fsdriver. Other fsdrivers (like proxy) don't take security model as a parameter. @item writeout=@var{writeout} This is an optional argument. The only supported value is "immediate". @@ -1116,7 +1318,7 @@ The general form of a Virtual File system pass-through options are: @table @option @item @var{fsdriver} This option specifies the fs driver backend to use. -Currently "local", "handle" and "proxy" file system drivers are supported. +Currently "local" and "proxy" file system drivers are supported. @item id=@var{id} Specifies identifier for this device @item path=@var{path} @@ -1134,7 +1336,7 @@ hidden .virtfs_metadata directory. Directories exported by this security model c interact with other unix tools. "none" security model is same as passthrough except the sever won't report failures if it fails to set file attributes like ownership. Security model is mandatory only -for local fsdriver. Other fsdrivers (like handle, proxy) don't take security +for local fsdriver. Other fsdrivers (like proxy) don't take security model as a parameter. @item writeout=@var{writeout} This is an optional argument. The only supported value is "immediate". @@ -1239,12 +1441,14 @@ STEXI ETEXI DEF("display", HAS_ARG, QEMU_OPTION_display, + "-display spice-app[,gl=on|off]\n" "-display sdl[,frame=on|off][,alt_grab=on|off][,ctrl_grab=on|off]\n" " [,window_close=on|off][,gl=on|core|es|off]\n" "-display gtk[,grab_on_hover=on|off][,gl=on|off]|\n" "-display vnc=[,]\n" - "-display curses\n" - "-display none" + "-display curses[,charset=]\n" + "-display none\n" + "-display egl-headless[,rendernode=]" " select display type\n" "The default display is equivalent to\n" #if defined(CONFIG_GTK) @@ -1274,6 +1478,9 @@ support a text mode, QEMU can display this output using a curses/ncurses interface. Nothing is displayed when the graphics device is in graphical mode or if the graphics device does not support a text mode. Generally only the VGA device models support text mode. +The font charset used by the guest can be specified with the +@code{charset} option, for example @code{charset=CP850} for IBM CP850 +encoding. The default is @code{CP437}. @item none Do not display video output. The guest will still see an emulated graphics card, but its output will not be displayed to the QEMU @@ -1286,6 +1493,13 @@ menus and other UI elements to configure and control the VM during runtime. @item vnc Start a VNC server on display +@item egl-headless +Offload all OpenGL operations to a local DRI device. For any graphical display, +this display needs to be paired with either VNC or SPICE displays. +@item spice-app +Start QEMU as a Spice server and launch the default Spice client +application. The Spice server will redirect the serial consoles and +QEMU monitors. (Since 4.0) @end table ETEXI @@ -1318,17 +1532,6 @@ mode using a curses/ncurses interface. Nothing is displayed in graphical mode. ETEXI -DEF("no-frame", 0, QEMU_OPTION_no_frame, - "-no-frame open SDL window without a frame and window decorations\n", - QEMU_ARCH_ALL) -STEXI -@item -no-frame -@findex -no-frame -Do not use decorations for SDL windows and start them using the whole -available screen space. This makes the using QEMU in a dedicated desktop -workspace more convenient. -ETEXI - DEF("alt-grab", 0, QEMU_OPTION_alt_grab, "-alt-grab use Ctrl-Alt-Shift to grab mouse (instead of Ctrl-Alt)\n", QEMU_ARCH_ALL) @@ -1654,48 +1857,13 @@ 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-authz=@var{ID} -@item tls - -Require that client use TLS when communicating with the VNC server. This -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 -for negotiating the TLS session. The server will send its x509 certificate -to the client. It is recommended that a password be set on the VNC server -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 -for negotiating the TLS session. The server will send its x509 certificate -to the client, and request that the client send its own x509 certificate. -The server will validate the client's certificate against the CA certificate, -and reject clients when validation fails. If the certificate authority is -trusted, this is a sufficient authentication mechanism. You may still wish -to set a password on the VNC server as a second authentication layer. 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. +Provides the ID of the QAuthZ authorization object against which +the client's x509 distinguished name will validated. This object is +only resolved at time of use, so can be deleted and recreated on the +fly while the VNC server is active. If missing, it will default +to denying access. @item sasl @@ -1712,18 +1880,25 @@ ensures a data encryption preventing compromise of authentication credentials. See the @ref{vnc_security} section for details on using SASL authentication. +@item sasl-authz=@var{ID} + +Provides the ID of the QAuthZ authorization object against which +the client's SASL username will validated. This object is +only resolved at time of use, so can be deleted and recreated on the +fly while the VNC server is active. If missing, it will default +to denying access. + @item acl -Turn on access control lists for checking of the x509 client certificate -and SASL party. For x509 certs, the ACL check is made against the -certificate's distinguished name. This is something that looks like -@code{C=GB,O=ACME,L=Boston,CN=bob}. For SASL party, the ACL check is -made against the username, which depending on the SASL plugin, may -include a realm component, eg @code{bob} or @code{bob@@EXAMPLE.COM}. -When the @option{acl} flag is set, the initial access list will be -empty, with a @code{deny} policy. Thus no one will be allowed to -use the VNC server until the ACLs have been loaded. This can be -achieved using the @code{acl} monitor command. +Legacy method for enabling authorization of clients against the +x509 distinguished name and SASL username. It results in the creation +of two @code{authz-list} objects with IDs of @code{vnc.username} and +@code{vnc.x509dname}. The rules for these objects must be configured +with the HMP ACL commands. + +This option is deprecated and should no longer be used. The new +@option{sasl-authz} and @option{tls-authz} options are a +replacement. @item lossy @@ -1786,9 +1961,6 @@ Windows 2000 is installed, you no longer need this option (this option slows down the IDE transfers). ETEXI -HXCOMM Deprecated by -rtc -DEF("rtc-td-hack", 0, QEMU_OPTION_rtc_td_hack, "", QEMU_ARCH_I386) - DEF("no-fd-bootchk", 0, QEMU_OPTION_no_fd_bootchk, "-no-fd-bootchk disable boot signature checking for floppy disks\n", QEMU_ARCH_I386) @@ -1891,23 +2063,13 @@ STEXI @table @option ETEXI -HXCOMM Legacy slirp options (now moved to -net user): -#ifdef CONFIG_SLIRP -DEF("tftp", HAS_ARG, QEMU_OPTION_tftp, "", QEMU_ARCH_ALL) -DEF("bootp", HAS_ARG, QEMU_OPTION_bootp, "", QEMU_ARCH_ALL) -DEF("redir", HAS_ARG, QEMU_OPTION_redir, "", QEMU_ARCH_ALL) -#ifndef _WIN32 -DEF("smb", HAS_ARG, QEMU_OPTION_smb, "", QEMU_ARCH_ALL) -#endif -#endif - DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_SLIRP "-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][,domainname=domain]\n" - " [,tftp=dir][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" + " [,tftp=dir][,tftp-server-name=name][,bootfile=f][,hostfwd=rule][,guestfwd=rule]" #ifndef _WIN32 "[,smb=dir[,smbserver=addr]]\n" #endif @@ -2144,6 +2306,11 @@ server. The files in @var{dir} will be exposed as the root of a TFTP server. The TFTP client on the guest must be configured in binary mode (use the command @code{bin} of the Unix TFTP client). +@item tftp-server-name=@var{name} +In BOOTP reply, broadcast @var{name} as the "TFTP server name" (RFC2132 option +66). This can be used to advise the guest to load boot files or configurations +from a different server than the host address. + @item bootfile=@var{file} When using the user mode network stack, broadcast @var{file} as the BOOTP filename. In conjunction with @option{tftp}, this can be used to network boot @@ -2228,11 +2395,6 @@ qemu-system-i386 -nic 'user,id=n1,guestfwd=tcp:10.0.2.100:1234-cmd:netcat 10.10 @end table -Note: Legacy stand-alone options -tftp, -bootp, -smb and -redir are still -processed and applied to -net user. Mixing them with the new configuration -syntax gives undefined results. Their use for new applications is discouraged -as they will be removed from future versions. - @item -netdev tap,id=@var{id}[,fd=@var{h}][,ifname=@var{name}][,script=@var{file}][,downscript=@var{dfile}][,br=@var{bridge}][,helper=@var{helper}] Configure a host TAP network backend with ID @var{id}. @@ -2345,7 +2507,7 @@ qemu-system-i386 linux.img \ -netdev socket,id=n2,mcast=230.0.0.1:1234 # launch yet another QEMU instance on same "bus" qemu-system-i386 linux.img \ - -device e1000,netdev=n3,macaddr=52:54:00:12:34:58 \ + -device e1000,netdev=n3,mac=52:54:00:12:34:58 \ -netdev socket,id=n3,mcast=230.0.0.1:1234 @end example @@ -2498,9 +2660,9 @@ DEF("chardev", HAS_ARG, QEMU_OPTION_chardev, "-chardev help\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]\n" - " [,logfile=PATH][,logappend=on|off][,tls-creds=ID] (tcp)\n" - "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,reconnect=seconds]\n" + " [,server][,nowait][,telnet][,websocket][,reconnect=seconds][,mux=on|off]\n" + " [,logfile=PATH][,logappend=on|off][,tls-creds=ID][,tls-authz=ID] (tcp)\n" + "-chardev socket,id=id,path=path[,server][,nowait][,telnet][,websocket][,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" @@ -2628,7 +2790,7 @@ The available backends are: 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}][,tls-creds=@var{id}] +@item -chardev socket,id=@var{id}[,@var{TCP options} or @var{unix options}][,server][,nowait][,telnet][,websocket][,reconnect=@var{seconds}][,tls-creds=@var{id}][,tls-authz=@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 @@ -2642,6 +2804,9 @@ connect to a listening socket. @option{telnet} specifies that traffic on the socket should interpret telnet escape sequences. +@option{websocket} specifies that the socket uses WebSocket protocol for +communication. + @option{reconnect} sets the timeout for reconnecting on non-server sockets when the remote end goes away. qemu will delay this many seconds and then attempt to reconnect. Zero disables reconnecting, and is the default. @@ -2651,6 +2816,12 @@ 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. +@option{tls-auth} provides the ID of the QAuthZ authorization object against +which the client's x509 distinguished name will be validated. This object is +only resolved at time of use, so can be deleted and recreated on the fly +while the chardev server is active. If missing, it will default to denying +access. + TCP and unix socket options are given below: @table @option @@ -2853,6 +3024,10 @@ logic. The Transport Layer is decided by the machine type. Currently the machines @code{n800} and @code{n810} have one HCI and all other machines have none. +Note: This option and the whole bluetooth subsystem is considered as deprecated. +If you still use it, please send a mail to @email{qemu-devel@@nongnu.org} where +you describe your usecase. + @anchor{bt-hcis} The following three types are recognized: @@ -3190,6 +3365,10 @@ MAGIC_SYSRQ sequence if you use a telnet that supports sending the break sequence. Typically in unix telnet you do it with Control-] and then type "send break" followed by pressing the enter key. +@item websocket:@var{host}:@var{port},server[,nowait][,nodelay] +The WebSocket protocol is used instead of raw tcp socket. The port acts as +a WebSocket server. Client mode is not supported. + @item unix:@var{path}[,server][,nowait][,reconnect=@var{seconds}] A unix domain socket is used instead of a tcp socket. The option works the same as if you had specified @code{-serial tcp} except the unix domain socket @@ -3450,26 +3629,11 @@ Enable KVM full virtualization support. This option is only available if KVM support is enabled when compiling. ETEXI -DEF("enable-hax", 0, QEMU_OPTION_enable_hax, \ - "-enable-hax enable HAX virtualization support\n", QEMU_ARCH_I386) -STEXI -@item -enable-hax -@findex -enable-hax -Enable HAX (Hardware-based Acceleration eXecution) support. This option -is only available if HAX support is enabled when compiling. HAX is only -applicable to MAC and Windows platform, and thus does not conflict with -KVM. This option is deprecated, use @option{-accel hax} instead. -ETEXI - DEF("xen-domid", HAS_ARG, QEMU_OPTION_xen_domid, "-xen-domid id specify xen guest domain id\n", QEMU_ARCH_ALL) -DEF("xen-create", 0, QEMU_OPTION_xen_create, - "-xen-create create domain using xen hypercalls, bypassing xend\n" - " warning: should not be used when xend is in use\n", - QEMU_ARCH_ALL) DEF("xen-attach", 0, QEMU_OPTION_xen_attach, "-xen-attach attach to existing xen domain\n" - " xend will use this when starting QEMU\n", + " libxl will use this when starting QEMU\n", QEMU_ARCH_ALL) DEF("xen-domid-restrict", 0, QEMU_OPTION_xen_domid_restrict, "-xen-domid-restrict restrict set of available xen operations\n" @@ -3480,14 +3644,10 @@ STEXI @item -xen-domid @var{id} @findex -xen-domid Specify xen guest domain @var{id} (XEN only). -@item -xen-create -@findex -xen-create -Create domain using xen hypercalls, bypassing xend. -Warning: should not be used when xend is in use (XEN only). @item -xen-attach @findex -xen-attach Attach to existing xen domain. -xend will use this when starting QEMU (XEN only). +libxl will use this when starting QEMU (XEN only). @findex -xen-domid-restrict Restrict set of available xen operations to specified domain id (XEN only). ETEXI @@ -3543,33 +3703,30 @@ Load the contents of @var{file} as an option ROM. This option is useful to load things like EtherBoot. ETEXI -HXCOMM Silently ignored for compatibility -DEF("clock", HAS_ARG, QEMU_OPTION_clock, "", QEMU_ARCH_ALL) - -HXCOMM Options deprecated by -rtc -DEF("localtime", 0, QEMU_OPTION_localtime, "", QEMU_ARCH_ALL) -DEF("startdate", HAS_ARG, QEMU_OPTION_startdate, "", QEMU_ARCH_ALL) - DEF("rtc", HAS_ARG, QEMU_OPTION_rtc, \ - "-rtc [base=utc|localtime|date][,clock=host|rt|vm][,driftfix=none|slew]\n" \ + "-rtc [base=utc|localtime|][,clock=host|rt|vm][,driftfix=none|slew]\n" \ " set the RTC base and clock, enable drift fix for clock ticks (x86 only)\n", QEMU_ARCH_ALL) STEXI -@item -rtc [base=utc|localtime|@var{date}][,clock=host|vm][,driftfix=none|slew] +@item -rtc [base=utc|localtime|@var{datetime}][,clock=host|rt|vm][,driftfix=none|slew] @findex -rtc Specify @option{base} as @code{utc} or @code{localtime} to let the RTC start at the current UTC or local time, respectively. @code{localtime} is required for correct date in -MS-DOS or Windows. To start at a specific point in time, provide @var{date} in the +MS-DOS or Windows. To start at a specific point in time, provide @var{datetime} in the format @code{2006-06-17T16:01:21} or @code{2006-06-17}. The default base is UTC. By default the RTC is driven by the host system time. This allows using of the RTC as accurate reference clock inside the guest, specifically if the host time is smoothly following an accurate external reference clock, e.g. via NTP. If you want to isolate the guest time from the host, you can set @option{clock} -to @code{rt} instead. To even prevent it from progressing during suspension, -you can set it to @code{vm}. +to @code{rt} instead, which provides a host monotonic clock if host support it. +To even prevent the RTC from progressing during suspension, you can set @option{clock} +to @code{vm} (virtual clock). @samp{clock=vm} is recommended especially in +icount mode in order to preserve determinism; however, note that in icount mode +the speed of the virtual clock is variable and can in general differ from the +host clock. Enable @option{driftfix} (i386 targets only) if you experience time drift problems, specifically with Windows' ACPI HAL. This option will try to figure out how @@ -3704,16 +3861,6 @@ character to Control-t. @end table ETEXI -DEF("virtioconsole", HAS_ARG, QEMU_OPTION_virtiocon, \ - "-virtioconsole c\n" \ - " set virtio console\n", QEMU_ARCH_ALL) -STEXI -@item -virtioconsole @var{c} -@findex -virtioconsole -Set virtio console. -This option is deprecated, please use @option{-device virtconsole} instead. -ETEXI - DEF("show-cursor", 0, QEMU_OPTION_show_cursor, \ "-show-cursor show cursor\n", QEMU_ARCH_ALL) STEXI @@ -3908,8 +4055,7 @@ Write device configuration to @var{file}. The @var{file} can be either filename command line and device configuration into file or dash @code{-}) character to print the output to stdout. This can be later used as input file for @code{-readconfig} option. ETEXI -HXCOMM Deprecated, same as -no-user-config -DEF("nodefconfig", 0, QEMU_OPTION_nodefconfig, "", QEMU_ARCH_ALL) + DEF("no-user-config", 0, QEMU_OPTION_nouserconfig, "-no-user-config\n" " do not load default user-provided config files at startup\n", @@ -3920,6 +4066,7 @@ STEXI The @code{-no-user-config} option makes QEMU not load any of the user-provided config files on @var{sysconfdir}. ETEXI + DEF("trace", HAS_ARG, QEMU_OPTION_trace, "-trace [[enable=]][,events=][,file=]\n" " specify tracing options\n", @@ -3975,6 +4122,16 @@ Dump json-encoded vmstate information for current machine type to file in @var{file} ETEXI +DEF("enable-sync-profile", 0, QEMU_OPTION_enable_sync_profile, + "-enable-sync-profile\n" + " enable synchronization profiling\n", + QEMU_ARCH_ALL) +STEXI +@item -enable-sync-profile +@findex -enable-sync-profile +Enable synchronization profiling. +ETEXI + STEXI @end table ETEXI @@ -4070,6 +4227,13 @@ requires an alignment different than the default one used by QEMU, eg the device DAX /dev/dax0.0 requires 2M alignment rather than 4K. In such cases, users can specify the required alignment via this option. +The @option{pmem} option specifies whether the backing file specified +by @option{mem-path} is in host persistent memory that can be accessed +using the SNIA NVM programming model (e.g. Intel NVDIMM). +If @option{pmem} is set to 'on', QEMU will take necessary operations to +guarantee the persistence of its own writes to @option{mem-path} +(e.g. in vNVDIMM label emulation and live migration). + @item -object memory-backend-ram,id=@var{id},merge=@var{on|off},dump=@var{on|off},share=@var{on|off},prealloc=@var{on|off},size=@var{size},host-nodes=@var{host-nodes},policy=@var{default|preferred|bind|interleave} Creates a memory backend object, which can be used to back the guest RAM. @@ -4077,7 +4241,7 @@ Memory backend objects offer more control than the @option{-m} option that is traditionally used to define guest RAM. Please refer to @option{memory-backend-file} for a description of the options. -@item -object memory-backend-memfd,id=@var{id},merge=@var{on|off},dump=@var{on|off},prealloc=@var{on|off},size=@var{size},host-nodes=@var{host-nodes},policy=@var{default|preferred|bind|interleave},seal=@var{on|off},hugetlb=@var{on|off},hugetlbsize=@var{size} +@item -object memory-backend-memfd,id=@var{id},merge=@var{on|off},dump=@var{on|off},share=@var{on|off},prealloc=@var{on|off},size=@var{size},host-nodes=@var{host-nodes},policy=@var{default|preferred|bind|interleave},seal=@var{on|off},hugetlb=@var{on|off},hugetlbsize=@var{size} Creates an anonymous memory file backend object, which allows QEMU to share the memory with an external process (e.g. when using @@ -4099,6 +4263,8 @@ with the @option{seal} option (requires at least Linux 4.16). Please refer to @option{memory-backend-file} for a description of the other options. +The @option{share} boolean option is @var{on} by default with memfd. + @item -object rng-random,id=@var{id},filename=@var{/dev/random} Creates a random number generator backend which obtains entropy from @@ -4453,6 +4619,111 @@ e.g to launch a SEV guest ..... @end example + + +@item -object authz-simple,id=@var{id},identity=@var{string} + +Create an authorization object that will control access to network services. + +The @option{identity} parameter is identifies the user and its format +depends on the network service that authorization object is associated +with. For authorizing based on TLS x509 certificates, the identity must +be the x509 distinguished name. Note that care must be taken to escape +any commas in the distinguished name. + +An example authorization object to validate a x509 distinguished name +would look like: +@example + # $QEMU \ + ... + -object 'authz-simple,id=auth0,identity=CN=laptop.example.com,,O=Example Org,,L=London,,ST=London,,C=GB' \ + ... +@end example + +Note the use of quotes due to the x509 distinguished name containing +whitespace, and escaping of ','. + +@item -object authz-listfile,id=@var{id},filename=@var{path},refresh=@var{yes|no} + +Create an authorization object that will control access to network services. + +The @option{filename} parameter is the fully qualified path to a file +containing the access control list rules in JSON format. + +An example set of rules that match against SASL usernames might look +like: + +@example + @{ + "rules": [ + @{ "match": "fred", "policy": "allow", "format": "exact" @}, + @{ "match": "bob", "policy": "allow", "format": "exact" @}, + @{ "match": "danb", "policy": "deny", "format": "glob" @}, + @{ "match": "dan*", "policy": "allow", "format": "exact" @}, + ], + "policy": "deny" + @} +@end example + +When checking access the object will iterate over all the rules and +the first rule to match will have its @option{policy} value returned +as the result. If no rules match, then the default @option{policy} +value is returned. + +The rules can either be an exact string match, or they can use the +simple UNIX glob pattern matching to allow wildcards to be used. + +If @option{refresh} is set to true the file will be monitored +and automatically reloaded whenever its content changes. + +As with the @code{authz-simple} object, the format of the identity +strings being matched depends on the network service, but is usually +a TLS x509 distinguished name, or a SASL username. + +An example authorization object to validate a SASL username +would look like: +@example + # $QEMU \ + ... + -object authz-simple,id=auth0,filename=/etc/qemu/vnc-sasl.acl,refresh=yes + ... +@end example + +@item -object authz-pam,id=@var{id},service=@var{string} + +Create an authorization object that will control access to network services. + +The @option{service} parameter provides the name of a PAM service to use +for authorization. It requires that a file @code{/etc/pam.d/@var{service}} +exist to provide the configuration for the @code{account} subsystem. + +An example authorization object to validate a TLS x509 distinguished +name would look like: + +@example + # $QEMU \ + ... + -object authz-pam,id=auth0,service=qemu-vnc + ... +@end example + +There would then be a corresponding config file for PAM at +@code{/etc/pam.d/qemu-vnc} that contains: + +@example +account requisite pam_listfile.so item=user sense=allow \ + file=/etc/qemu/vnc.allow +@end example + +Finally the @code{/etc/qemu/vnc.allow} file would contain +the list of x509 distingished names that are permitted +access + +@example +CN=laptop.example.com,O=Example Home,L=London,ST=London,C=GB +@end example + + @end table ETEXI diff --git a/qemu-seccomp.c b/qemu-seccomp.c index 9cd8eb9499..e0a1829b3d 100644 --- a/qemu-seccomp.c +++ b/qemu-seccomp.c @@ -12,14 +12,16 @@ * 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/option.h" #include "qemu/module.h" -#include "qemu/error-report.h" #include #include #include "sysemu/seccomp.h" +#include /* For some architectures (notably ARM) cacheflush is not supported until * libseccomp 2.2.3, but configure enforces that we are using a more recent @@ -39,7 +41,8 @@ struct QemuSeccompSyscall { }; const struct scmp_arg_cmp sched_setscheduler_arg[] = { - SCMP_A1(SCMP_CMP_NE, SCHED_IDLE) + /* was SCMP_A1(SCMP_CMP_NE, SCHED_IDLE), but expanded due to GCC 4.x bug */ + { .arg = 1, .op = SCMP_CMP_NE, .datum_a = SCHED_IDLE } }; static const struct QemuSeccompSyscall blacklist[] = { @@ -107,36 +110,95 @@ static const struct QemuSeccompSyscall blacklist[] = { { SCMP_SYS(sched_get_priority_min), QEMU_SECCOMP_SET_RESOURCECTL }, }; - -static int seccomp_start(uint32_t seccomp_opts) +static inline __attribute__((unused)) int +qemu_seccomp(unsigned int operation, unsigned int flags, void *args) { - int rc = 0; +#ifdef __NR_seccomp + return syscall(__NR_seccomp, operation, flags, args); +#else + errno = ENOSYS; + return -1; +#endif +} + +static uint32_t qemu_seccomp_get_action(int set) +{ + switch (set) { + case QEMU_SECCOMP_SET_DEFAULT: + case QEMU_SECCOMP_SET_OBSOLETE: + case QEMU_SECCOMP_SET_PRIVILEGED: + case QEMU_SECCOMP_SET_SPAWN: { +#if defined(SECCOMP_GET_ACTION_AVAIL) && defined(SCMP_ACT_KILL_PROCESS) && \ + defined(SECCOMP_RET_KILL_PROCESS) + static int kill_process = -1; + if (kill_process == -1) { + uint32_t action = SECCOMP_RET_KILL_PROCESS; + + if (qemu_seccomp(SECCOMP_GET_ACTION_AVAIL, 0, &action) == 0) { + kill_process = 1; + } + kill_process = 0; + } + if (kill_process == 1) { + return SCMP_ACT_KILL_PROCESS; + } +#endif + return SCMP_ACT_TRAP; + } + + case QEMU_SECCOMP_SET_RESOURCECTL: + return SCMP_ACT_ERRNO(EPERM); + + default: + g_assert_not_reached(); + } +} + + +static int seccomp_start(uint32_t seccomp_opts, Error **errp) +{ + int rc = -1; unsigned int i = 0; scmp_filter_ctx ctx; ctx = seccomp_init(SCMP_ACT_ALLOW); if (ctx == NULL) { - rc = -1; + error_setg(errp, "failed to initialize seccomp context"); + goto seccomp_return; + } + + rc = seccomp_attr_set(ctx, SCMP_FLTATR_CTL_TSYNC, 1); + if (rc != 0) { + error_setg_errno(errp, -rc, + "failed to set seccomp thread synchronization"); goto seccomp_return; } for (i = 0; i < ARRAY_SIZE(blacklist); i++) { + uint32_t action; if (!(seccomp_opts & blacklist[i].set)) { continue; } - rc = seccomp_rule_add_array(ctx, SCMP_ACT_KILL, blacklist[i].num, + action = qemu_seccomp_get_action(blacklist[i].set); + rc = seccomp_rule_add_array(ctx, action, blacklist[i].num, blacklist[i].narg, blacklist[i].arg_cmp); if (rc < 0) { + error_setg_errno(errp, -rc, + "failed to add seccomp blacklist rules"); goto seccomp_return; } } rc = seccomp_load(ctx); + if (rc < 0) { + error_setg_errno(errp, -rc, + "failed to load seccomp syscall filter in kernel"); + } seccomp_return: seccomp_release(ctx); - return rc; + return rc < 0 ? -1 : 0; } #ifdef CONFIG_SECCOMP @@ -156,7 +218,7 @@ int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) * to provide a little bit of consistency for * the command line */ } else { - error_report("invalid argument for obsolete"); + error_setg(errp, "invalid argument for obsolete"); return -1; } } @@ -171,14 +233,13 @@ int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) /* calling prctl directly because we're * not sure if host has CAP_SYS_ADMIN set*/ if (prctl(PR_SET_NO_NEW_PRIVS, 1)) { - error_report("failed to set no_new_privs " - "aborting"); + error_setg(errp, "failed to set no_new_privs aborting"); return -1; } } else if (g_str_equal(value, "allow")) { /* default value */ } else { - error_report("invalid argument for elevateprivileges"); + error_setg(errp, "invalid argument for elevateprivileges"); return -1; } } @@ -190,7 +251,7 @@ int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) } else if (g_str_equal(value, "allow")) { /* default value */ } else { - error_report("invalid argument for spawn"); + error_setg(errp, "invalid argument for spawn"); return -1; } } @@ -202,14 +263,12 @@ int parse_sandbox(void *opaque, QemuOpts *opts, Error **errp) } else if (g_str_equal(value, "allow")) { /* default value */ } else { - error_report("invalid argument for resourcecontrol"); + error_setg(errp, "invalid argument for resourcecontrol"); return -1; } } - if (seccomp_start(seccomp_opts) < 0) { - error_report("failed to install seccomp syscall filter " - "in the kernel"); + if (seccomp_start(seccomp_opts, errp) < 0) { return -1; } } @@ -248,7 +307,24 @@ static QemuOptsList qemu_sandbox_opts = { static void seccomp_register(void) { - qemu_add_opts(&qemu_sandbox_opts); + bool add = false; + + /* FIXME: use seccomp_api_get() >= 2 check when released */ + +#if defined(SECCOMP_FILTER_FLAG_TSYNC) + int check; + + /* check host TSYNC capability, it returns errno == ENOSYS if unavailable */ + check = qemu_seccomp(SECCOMP_SET_MODE_FILTER, + SECCOMP_FILTER_FLAG_TSYNC, NULL); + if (check < 0 && errno == EFAULT) { + add = true; + } +#endif + + if (add) { + qemu_add_opts(&qemu_sandbox_opts); + } } opts_init(seccomp_register); #endif diff --git a/qga/Makefile.objs b/qga/Makefile.objs index ed08c5917c..80e6bb3c2e 100644 --- a/qga/Makefile.objs +++ b/qga/Makefile.objs @@ -1,3 +1,4 @@ +commands-posix.o-libs := $(LIBUDEV_LIBS) qga-obj-y = commands.o guest-agent-command-state.o main.o qga-obj-$(CONFIG_POSIX) += commands-posix.o channel-posix.o qga-obj-$(CONFIG_WIN32) += commands-win32.o channel-win32.o service-win32.o diff --git a/qga/channel-win32.c b/qga/channel-win32.c index b3597a8a0f..c86f4388db 100644 --- a/qga/channel-win32.c +++ b/qga/channel-win32.c @@ -302,7 +302,8 @@ static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); if (c->handle == INVALID_HANDLE_VALUE) { - g_critical("error opening path %s", newpath); + g_critical("error opening path %s: %s", newpath, + g_win32_error_message(GetLastError())); return false; } diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 37e8a2d791..7ee6a33cce 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -48,6 +48,10 @@ extern char **environ; #include #include +#ifdef CONFIG_LIBUDEV +#include +#endif + #ifdef FIFREEZE #define CONFIG_FSFREEZE #endif @@ -872,6 +876,10 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, GuestDiskAddressList *list = NULL; bool has_ata = false, has_host = false, has_tgt = false; char *p, *q, *driver = NULL; +#ifdef CONFIG_LIBUDEV + struct udev *udev = NULL; + struct udev_device *udevice = NULL; +#endif p = strstr(syspath, "/devices/pci"); if (!p || sscanf(p + 12, "%*x:%*x/%x:%x:%x.%x%n", @@ -936,6 +944,26 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, list = g_malloc0(sizeof(*list)); list->value = disk; +#ifdef CONFIG_LIBUDEV + udev = udev_new(); + udevice = udev_device_new_from_syspath(udev, syspath); + if (udev == NULL || udevice == NULL) { + g_debug("failed to query udev"); + } else { + const char *devnode, *serial; + devnode = udev_device_get_devnode(udevice); + if (devnode != NULL) { + disk->dev = g_strdup(devnode); + disk->has_dev = true; + } + serial = udev_device_get_property_value(udevice, "ID_SERIAL"); + if (serial != NULL && *serial != 0) { + disk->serial = g_strdup(serial); + disk->has_serial = true; + } + } +#endif + if (strcmp(driver, "ata_piix") == 0) { /* a host per ide bus, target*:0::0 */ if (!has_host || !has_tgt) { @@ -995,14 +1023,19 @@ static void build_guest_fsinfo_for_real_device(char const *syspath, list->next = fs->disk; fs->disk = list; - g_free(driver); - return; + goto out; cleanup: if (list) { qapi_free_GuestDiskAddressList(list); } +out: g_free(driver); +#ifdef CONFIG_LIBUDEV + udev_unref(udev); + udev_device_unref(udevice); +#endif + return; } static void build_guest_fsinfo_for_device(char const *devpath, @@ -1258,7 +1291,7 @@ int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, /* cannot risk guest agent blocking itself on a write in this state */ ga_set_frozen(ga_state); - QTAILQ_FOREACH_REVERSE(mount, &mounts, FsMountList, next) { + QTAILQ_FOREACH_REVERSE(mount, &mounts, next) { /* To issue fsfreeze in the reverse order of mounts, check if the * mount is listed in the list here */ if (has_mountpoints) { @@ -1540,10 +1573,7 @@ static bool systemd_supports_mode(SuspendMode mode, Error **errp) return true; } - if (local_err) { - error_propagate(errp, local_err); - } - + error_propagate(errp, local_err); return false; } @@ -1749,7 +1779,7 @@ static void guest_suspend(SuspendMode mode, Error **errp) if (!mode_supported) { error_setg(errp, "the requested suspend mode is not supported by the guest"); - } else if (local_err) { + } else { error_propagate(errp, local_err); } } @@ -2035,61 +2065,56 @@ static long sysconf_exact(int name, const char *name_str, Error **errp) * Written members remain unmodified on error. */ static void transfer_vcpu(GuestLogicalProcessor *vcpu, bool sys2vcpu, - Error **errp) + char *dirpath, Error **errp) { - char *dirpath; + int fd; + int res; int dirfd; + static const char fn[] = "online"; - dirpath = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", - vcpu->logical_id); dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); if (dirfd == -1) { error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + return; + } + + fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); + if (fd == -1) { + if (errno != ENOENT) { + error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); + } else if (sys2vcpu) { + vcpu->online = true; + vcpu->can_offline = false; + } else if (!vcpu->online) { + error_setg(errp, "logical processor #%" PRId64 " can't be " + "offlined", vcpu->logical_id); + } /* otherwise pretend successful re-onlining */ } else { - static const char fn[] = "online"; - int fd; - int res; + unsigned char status; - fd = openat(dirfd, fn, sys2vcpu ? O_RDONLY : O_RDWR); - if (fd == -1) { - if (errno != ENOENT) { - error_setg_errno(errp, errno, "open(\"%s/%s\")", dirpath, fn); - } else if (sys2vcpu) { - vcpu->online = true; - vcpu->can_offline = false; - } else if (!vcpu->online) { - error_setg(errp, "logical processor #%" PRId64 " can't be " - "offlined", vcpu->logical_id); - } /* otherwise pretend successful re-onlining */ - } else { - unsigned char status; + res = pread(fd, &status, 1, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); + } else if (res == 0) { + error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, + fn); + } else if (sys2vcpu) { + vcpu->online = (status != '0'); + vcpu->can_offline = true; + } else if (vcpu->online != (status != '0')) { + status = '0' + vcpu->online; + if (pwrite(fd, &status, 1, 0) == -1) { + error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, + fn); + } + } /* otherwise pretend successful re-(on|off)-lining */ - res = pread(fd, &status, 1, 0); - if (res == -1) { - error_setg_errno(errp, errno, "pread(\"%s/%s\")", dirpath, fn); - } else if (res == 0) { - error_setg(errp, "pread(\"%s/%s\"): unexpected EOF", dirpath, - fn); - } else if (sys2vcpu) { - vcpu->online = (status != '0'); - vcpu->can_offline = true; - } else if (vcpu->online != (status != '0')) { - status = '0' + vcpu->online; - if (pwrite(fd, &status, 1, 0) == -1) { - error_setg_errno(errp, errno, "pwrite(\"%s/%s\")", dirpath, - fn); - } - } /* otherwise pretend successful re-(on|off)-lining */ - - res = close(fd); - g_assert(res == 0); - } - - res = close(dirfd); + res = close(fd); g_assert(res == 0); } - g_free(dirpath); + res = close(dirfd); + g_assert(res == 0); } GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) @@ -2107,17 +2132,21 @@ GuestLogicalProcessorList *qmp_guest_get_vcpus(Error **errp) while (local_err == NULL && current < sc_max) { GuestLogicalProcessor *vcpu; GuestLogicalProcessorList *entry; + int64_t id = current++; + char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", + id); - vcpu = g_malloc0(sizeof *vcpu); - vcpu->logical_id = current++; - vcpu->has_can_offline = true; /* lolspeak ftw */ - transfer_vcpu(vcpu, true, &local_err); - - entry = g_malloc0(sizeof *entry); - entry->value = vcpu; - - *link = entry; - link = &entry->next; + if (g_file_test(path, G_FILE_TEST_EXISTS)) { + vcpu = g_malloc0(sizeof *vcpu); + vcpu->logical_id = id; + vcpu->has_can_offline = true; /* lolspeak ftw */ + transfer_vcpu(vcpu, true, path, &local_err); + entry = g_malloc0(sizeof *entry); + entry->value = vcpu; + *link = entry; + link = &entry->next; + } + g_free(path); } if (local_err == NULL) { @@ -2138,7 +2167,11 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) processed = 0; while (vcpus != NULL) { - transfer_vcpu(vcpus->value, false, &local_err); + char *path = g_strdup_printf("/sys/devices/system/cpu/cpu%" PRId64 "/", + vcpus->value->logical_id); + + transfer_vcpu(vcpus->value, false, path, &local_err); + g_free(path); if (local_err != NULL) { break; } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 98d9735389..d40d61f605 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -10,12 +10,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. */ - -#ifndef _WIN32_WINNT -# define _WIN32_WINNT 0x0600 -#endif - #include "qemu/osdep.h" + #include #include #include @@ -26,6 +22,7 @@ #include #include #include +#include #include #endif #include @@ -89,6 +86,12 @@ static OpenFlags guest_file_open_modes[] = { {"a+b", FILE_GENERIC_APPEND|GENERIC_READ, OPEN_ALWAYS } }; +#define debug_error(msg) do { \ + char *suffix = g_win32_error_message(GetLastError()); \ + g_debug("%s: %s", (msg), suffix); \ + g_free(suffix); \ +} while (0) + static OpenFlags *find_open_flag(const char *mode_str) { int mode; @@ -160,13 +163,15 @@ static void handle_set_nonblocking(HANDLE fh) int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **errp) { - int64_t fd; + int64_t fd = -1; HANDLE fh; HANDLE templ_file = NULL; DWORD share_mode = FILE_SHARE_READ; DWORD flags_and_attr = FILE_ATTRIBUTE_NORMAL; LPSECURITY_ATTRIBUTES sa_attr = NULL; OpenFlags *guest_flags; + GError *gerr = NULL; + wchar_t *w_path = NULL; if (!has_mode) { mode = "r"; @@ -175,16 +180,21 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, guest_flags = find_open_flag(mode); if (guest_flags == NULL) { error_setg(errp, "invalid file open mode"); - return -1; + goto done; } - fh = CreateFile(path, guest_flags->desired_access, share_mode, sa_attr, + w_path = g_utf8_to_utf16(path, -1, NULL, NULL, &gerr); + if (!w_path) { + goto done; + } + + fh = CreateFileW(w_path, guest_flags->desired_access, share_mode, sa_attr, guest_flags->creation_disposition, flags_and_attr, templ_file); if (fh == INVALID_HANDLE_VALUE) { error_setg_win32(errp, GetLastError(), "failed to open file '%s'", path); - return -1; + goto done; } /* set fd non-blocking to avoid common use cases (like reading from a @@ -196,10 +206,17 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, if (fd < 0) { CloseHandle(fh); error_setg(errp, "failed to add handle to qmp handle table"); - return -1; + goto done; } slog("guest-file-open, handle: % " PRId64, fd); + +done: + if (gerr) { + error_setg(errp, QERR_QGA_COMMAND_FAILED, gerr->message); + g_error_free(gerr); + } + g_free(w_path); return fd; } @@ -450,13 +467,11 @@ static STORAGE_BUS_TYPE win2qemu[] = { [BusTypeFibre] = GUEST_DISK_BUS_TYPE_SSA, [BusTypeUsb] = GUEST_DISK_BUS_TYPE_USB, [BusTypeRAID] = GUEST_DISK_BUS_TYPE_RAID, -#if (_WIN32_WINNT >= 0x0600) [BusTypeiScsi] = GUEST_DISK_BUS_TYPE_ISCSI, [BusTypeSas] = GUEST_DISK_BUS_TYPE_SAS, [BusTypeSata] = GUEST_DISK_BUS_TYPE_SATA, [BusTypeSd] = GUEST_DISK_BUS_TYPE_SD, [BusTypeMmc] = GUEST_DISK_BUS_TYPE_MMC, -#endif #if (_WIN32_WINNT >= 0x0601) [BusTypeVirtual] = GUEST_DISK_BUS_TYPE_VIRTUAL, [BusTypeFileBackedVirtual] = GUEST_DISK_BUS_TYPE_FILE_BACKED_VIRTUAL, @@ -465,132 +480,373 @@ static STORAGE_BUS_TYPE win2qemu[] = { static GuestDiskBusType find_bus_type(STORAGE_BUS_TYPE bus) { - if (bus > ARRAY_SIZE(win2qemu) || (int)bus < 0) { + if (bus >= ARRAY_SIZE(win2qemu) || (int)bus < 0) { return GUEST_DISK_BUS_TYPE_UNKNOWN; } return win2qemu[(int)bus]; } -DEFINE_GUID(GUID_DEVINTERFACE_VOLUME, - 0x53f5630dL, 0xb6bf, 0x11d0, 0x94, 0xf2, +DEFINE_GUID(GUID_DEVINTERFACE_DISK, + 0x53f56307L, 0xb6bf, 0x11d0, 0x94, 0xf2, + 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); +DEFINE_GUID(GUID_DEVINTERFACE_STORAGEPORT, + 0x2accfe60L, 0xc130, 0x11d2, 0xb0, 0x82, 0x00, 0xa0, 0xc9, 0x1e, 0xfb, 0x8b); -static GuestPCIAddress *get_pci_info(char *guid, Error **errp) +static GuestPCIAddress *get_pci_info(int number, Error **errp) { HDEVINFO dev_info; SP_DEVINFO_DATA dev_info_data; - DWORD size = 0; + SP_DEVICE_INTERFACE_DATA dev_iface_data; + HANDLE dev_file; int i; - char dev_name[MAX_PATH]; - char *buffer = NULL; GuestPCIAddress *pci = NULL; - char *name = g_strdup(&guid[4]); + bool partial_pci = false; - if (!QueryDosDevice(name, dev_name, ARRAY_SIZE(dev_name))) { - error_setg_win32(errp, GetLastError(), "failed to get dos device name"); - goto out; - } + pci = g_malloc0(sizeof(*pci)); + pci->domain = -1; + pci->slot = -1; + pci->function = -1; + pci->bus = -1; - dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_VOLUME, 0, 0, + dev_info = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); if (dev_info == INVALID_HANDLE_VALUE) { error_setg_win32(errp, GetLastError(), "failed to get devices tree"); goto out; } + g_debug("enumerating devices"); dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + dev_iface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); for (i = 0; SetupDiEnumDeviceInfo(dev_info, i, &dev_info_data); i++) { - DWORD addr, bus, slot, func, dev, data, size2; - while (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, - SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, - &data, (PBYTE)buffer, size, - &size2)) { - size = MAX(size, size2); - if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { - g_free(buffer); - /* Double the size to avoid problems on - * W2k MBCS systems per KB 888609. - * https://support.microsoft.com/en-us/kb/259695 */ - buffer = g_malloc(size * 2); - } else { + PSP_DEVICE_INTERFACE_DETAIL_DATA pdev_iface_detail_data = NULL; + STORAGE_DEVICE_NUMBER sdn; + char *parent_dev_id = NULL; + HDEVINFO parent_dev_info; + SP_DEVINFO_DATA parent_dev_info_data; + DWORD j; + DWORD size = 0; + + g_debug("getting device path"); + if (SetupDiEnumDeviceInterfaces(dev_info, &dev_info_data, + &GUID_DEVINTERFACE_DISK, 0, + &dev_iface_data)) { + while (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_iface_data, + pdev_iface_detail_data, + size, &size, + &dev_info_data)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + pdev_iface_detail_data = g_malloc(size); + pdev_iface_detail_data->cbSize = + sizeof(*pdev_iface_detail_data); + } else { + error_setg_win32(errp, GetLastError(), + "failed to get device interfaces"); + goto free_dev_info; + } + } + + dev_file = CreateFile(pdev_iface_detail_data->DevicePath, 0, + FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, + NULL); + g_free(pdev_iface_detail_data); + + if (!DeviceIoControl(dev_file, IOCTL_STORAGE_GET_DEVICE_NUMBER, + NULL, 0, &sdn, sizeof(sdn), &size, NULL)) { + CloseHandle(dev_file); error_setg_win32(errp, GetLastError(), - "failed to get device name"); + "failed to get device slot number"); goto free_dev_info; } + + CloseHandle(dev_file); + if (sdn.DeviceNumber != number) { + continue; + } + } else { + error_setg_win32(errp, GetLastError(), + "failed to get device interfaces"); + goto free_dev_info; + } + + g_debug("found device slot %d. Getting storage controller", number); + { + CONFIGRET cr; + DEVINST dev_inst, parent_dev_inst; + ULONG dev_id_size = 0; + + size = 0; + while (!SetupDiGetDeviceInstanceId(dev_info, &dev_info_data, + parent_dev_id, size, &size)) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + parent_dev_id = g_malloc(size); + } else { + error_setg_win32(errp, GetLastError(), + "failed to get device instance ID"); + goto out; + } + } + + /* + * CM API used here as opposed to + * SetupDiGetDeviceProperty(..., DEVPKEY_Device_Parent, ...) + * which exports are only available in mingw-w64 6+ + */ + cr = CM_Locate_DevInst(&dev_inst, parent_dev_id, 0); + if (cr != CR_SUCCESS) { + g_error("CM_Locate_DevInst failed with code %lx", cr); + error_setg_win32(errp, GetLastError(), + "failed to get device instance"); + goto out; + } + cr = CM_Get_Parent(&parent_dev_inst, dev_inst, 0); + if (cr != CR_SUCCESS) { + g_error("CM_Get_Parent failed with code %lx", cr); + error_setg_win32(errp, GetLastError(), + "failed to get parent device instance"); + goto out; + } + + cr = CM_Get_Device_ID_Size(&dev_id_size, parent_dev_inst, 0); + if (cr != CR_SUCCESS) { + g_error("CM_Get_Device_ID_Size failed with code %lx", cr); + error_setg_win32(errp, GetLastError(), + "failed to get parent device ID length"); + goto out; + } + + ++dev_id_size; + if (dev_id_size > size) { + g_free(parent_dev_id); + parent_dev_id = g_malloc(dev_id_size); + } + + cr = CM_Get_Device_ID(parent_dev_inst, parent_dev_id, dev_id_size, + 0); + if (cr != CR_SUCCESS) { + g_error("CM_Get_Device_ID failed with code %lx", cr); + error_setg_win32(errp, GetLastError(), + "failed to get parent device ID"); + goto out; + } } - if (g_strcmp0(buffer, dev_name)) { - continue; + g_debug("querying storage controller %s for PCI information", + parent_dev_id); + parent_dev_info = + SetupDiGetClassDevs(&GUID_DEVINTERFACE_STORAGEPORT, parent_dev_id, + NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + g_free(parent_dev_id); + + if (parent_dev_info == INVALID_HANDLE_VALUE) { + error_setg_win32(errp, GetLastError(), + "failed to get parent device"); + goto out; } - /* There is no need to allocate buffer in the next functions. The size - * is known and ULONG according to - * https://support.microsoft.com/en-us/kb/253232 - * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx - */ - if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, - SPDRP_BUSNUMBER, &data, (PBYTE)&bus, size, NULL)) { - break; + parent_dev_info_data.cbSize = sizeof(SP_DEVINFO_DATA); + if (!SetupDiEnumDeviceInfo(parent_dev_info, 0, &parent_dev_info_data)) { + error_setg_win32(errp, GetLastError(), + "failed to get parent device data"); + goto out; } - /* The function retrieves the device's address. This value will be - * transformed into device function and number */ - if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, - SPDRP_ADDRESS, &data, (PBYTE)&addr, size, NULL)) { - break; + for (j = 0; + SetupDiEnumDeviceInfo(parent_dev_info, j, &parent_dev_info_data); + j++) { + DWORD addr, bus, ui_slot, type; + int func, slot; + + /* + * There is no need to allocate buffer in the next functions. The + * size is known and ULONG according to + * https://msdn.microsoft.com/en-us/library/windows/hardware/ff543095(v=vs.85).aspx + */ + if (!SetupDiGetDeviceRegistryProperty( + parent_dev_info, &parent_dev_info_data, SPDRP_BUSNUMBER, + &type, (PBYTE)&bus, size, NULL)) { + debug_error("failed to get PCI bus"); + bus = -1; + partial_pci = true; + } + + /* + * The function retrieves the device's address. This value will be + * transformed into device function and number + */ + if (!SetupDiGetDeviceRegistryProperty( + parent_dev_info, &parent_dev_info_data, SPDRP_ADDRESS, + &type, (PBYTE)&addr, size, NULL)) { + debug_error("failed to get PCI address"); + addr = -1; + partial_pci = true; + } + + /* + * This call returns UINumber of DEVICE_CAPABILITIES structure. + * This number is typically a user-perceived slot number. + */ + if (!SetupDiGetDeviceRegistryProperty( + parent_dev_info, &parent_dev_info_data, SPDRP_UI_NUMBER, + &type, (PBYTE)&ui_slot, size, NULL)) { + debug_error("failed to get PCI slot"); + ui_slot = -1; + partial_pci = true; + } + + /* + * SetupApi gives us the same information as driver with + * IoGetDeviceProperty. According to Microsoft: + * + * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF) + * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF) + * SPDRP_ADDRESS is propertyAddress, so we do the same. + * + * https://docs.microsoft.com/en-us/windows/desktop/api/setupapi/nf-setupapi-setupdigetdeviceregistrypropertya + */ + if (partial_pci) { + pci->domain = -1; + pci->slot = -1; + pci->function = -1; + pci->bus = -1; + continue; + } else { + func = ((int)addr == -1) ? -1 : addr & 0x0000FFFF; + slot = ((int)addr == -1) ? -1 : (addr >> 16) & 0x0000FFFF; + if ((int)ui_slot != slot) { + g_debug("mismatch with reported slot values: %d vs %d", + (int)ui_slot, slot); + } + pci->domain = 0; + pci->slot = (int)ui_slot; + pci->function = func; + pci->bus = (int)bus; + break; + } } - - /* This call returns UINumber of DEVICE_CAPABILITIES structure. - * This number is typically a user-perceived slot number. */ - if (!SetupDiGetDeviceRegistryProperty(dev_info, &dev_info_data, - SPDRP_UI_NUMBER, &data, (PBYTE)&slot, size, NULL)) { - break; - } - - /* SetupApi gives us the same information as driver with - * IoGetDeviceProperty. According to Microsoft - * https://support.microsoft.com/en-us/kb/253232 - * FunctionNumber = (USHORT)((propertyAddress) & 0x0000FFFF); - * DeviceNumber = (USHORT)(((propertyAddress) >> 16) & 0x0000FFFF); - * SPDRP_ADDRESS is propertyAddress, so we do the same.*/ - - func = addr & 0x0000FFFF; - dev = (addr >> 16) & 0x0000FFFF; - pci = g_malloc0(sizeof(*pci)); - pci->domain = dev; - pci->slot = slot; - pci->function = func; - pci->bus = bus; + SetupDiDestroyDeviceInfoList(parent_dev_info); break; } free_dev_info: SetupDiDestroyDeviceInfoList(dev_info); out: - g_free(buffer); - g_free(name); return pci; } -static int get_disk_bus_type(HANDLE vol_h, Error **errp) +static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk, + Error **errp) { STORAGE_PROPERTY_QUERY query; STORAGE_DEVICE_DESCRIPTOR *dev_desc, buf; DWORD received; + ULONG size = sizeof(buf); dev_desc = &buf; - dev_desc->Size = sizeof(buf); query.PropertyId = StorageDeviceProperty; query.QueryType = PropertyStandardQuery; if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query, sizeof(STORAGE_PROPERTY_QUERY), dev_desc, - dev_desc->Size, &received, NULL)) { + size, &received, NULL)) { error_setg_win32(errp, GetLastError(), "failed to get bus type"); - return -1; + return; + } + disk->bus_type = find_bus_type(dev_desc->BusType); + g_debug("bus type %d", disk->bus_type); + + /* Query once more. Now with long enough buffer. */ + size = dev_desc->Size; + dev_desc = g_malloc0(size); + if (!DeviceIoControl(vol_h, IOCTL_STORAGE_QUERY_PROPERTY, &query, + sizeof(STORAGE_PROPERTY_QUERY), dev_desc, + size, &received, NULL)) { + error_setg_win32(errp, GetLastError(), "failed to get serial number"); + g_debug("failed to get serial number"); + goto out_free; + } + if (dev_desc->SerialNumberOffset > 0) { + const char *serial; + size_t len; + + if (dev_desc->SerialNumberOffset >= received) { + error_setg(errp, "failed to get serial number: offset outside the buffer"); + g_debug("serial number offset outside the buffer"); + goto out_free; + } + serial = (char *)dev_desc + dev_desc->SerialNumberOffset; + len = received - dev_desc->SerialNumberOffset; + g_debug("serial number \"%s\"", serial); + if (*serial != 0) { + disk->serial = g_strndup(serial, len); + disk->has_serial = true; + } + } +out_free: + g_free(dev_desc); + + return; +} + +static void get_single_disk_info(int disk_number, + GuestDiskAddress *disk, Error **errp) +{ + SCSI_ADDRESS addr, *scsi_ad; + DWORD len; + HANDLE disk_h; + Error *local_err = NULL; + + scsi_ad = &addr; + + g_debug("getting disk info for: %s", disk->dev); + disk_h = CreateFile(disk->dev, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, + 0, NULL); + if (disk_h == INVALID_HANDLE_VALUE) { + error_setg_win32(errp, GetLastError(), "failed to open disk"); + return; } - return dev_desc->BusType; + get_disk_properties(disk_h, disk, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto err_close; + } + + g_debug("bus type %d", disk->bus_type); + /* always set pci_controller as required by schema. get_pci_info() should + * report -1 values for non-PCI buses rather than fail. fail the command + * if that doesn't hold since that suggests some other unexpected + * breakage + */ + disk->pci_controller = get_pci_info(disk_number, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto err_close; + } + if (disk->bus_type == GUEST_DISK_BUS_TYPE_SCSI + || disk->bus_type == GUEST_DISK_BUS_TYPE_IDE + || disk->bus_type == GUEST_DISK_BUS_TYPE_RAID + /* This bus type is not supported before Windows Server 2003 SP1 */ + || disk->bus_type == GUEST_DISK_BUS_TYPE_SAS + ) { + /* We are able to use the same ioctls for different bus types + * according to Microsoft docs + * https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */ + g_debug("getting SCSI info"); + if (DeviceIoControl(disk_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad, + sizeof(SCSI_ADDRESS), &len, NULL)) { + disk->unit = addr.Lun; + disk->target = addr.TargetId; + disk->bus = addr.PathId; + } + /* We do not set error in this case, because we still have enough + * information about volume. */ + } + +err_close: + CloseHandle(disk_h); + return; } /* VSS provider works with volumes, thus there is no difference if @@ -598,59 +854,109 @@ static int get_disk_bus_type(HANDLE vol_h, Error **errp) * volume is returned for the spanned disk group (LVM) */ static GuestDiskAddressList *build_guest_disk_info(char *guid, Error **errp) { - GuestDiskAddressList *list = NULL; - GuestDiskAddress *disk; - SCSI_ADDRESS addr, *scsi_ad; - DWORD len; - int bus; + Error *local_err = NULL; + GuestDiskAddressList *list = NULL, *cur_item = NULL; + GuestDiskAddress *disk = NULL; + int i; HANDLE vol_h; + DWORD size; + PVOLUME_DISK_EXTENTS extents = NULL; - scsi_ad = &addr; - char *name = g_strndup(guid, strlen(guid)-1); + /* strip final backslash */ + char *name = g_strdup(guid); + if (g_str_has_suffix(name, "\\")) { + name[strlen(name) - 1] = 0; + } + g_debug("opening %s", name); vol_h = CreateFile(name, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if (vol_h == INVALID_HANDLE_VALUE) { error_setg_win32(errp, GetLastError(), "failed to open volume"); - goto out_free; + goto out; } - bus = get_disk_bus_type(vol_h, errp); - if (bus < 0) { - goto out_close; - } - - disk = g_malloc0(sizeof(*disk)); - disk->bus_type = find_bus_type(bus); - if (bus == BusTypeScsi || bus == BusTypeAta || bus == BusTypeRAID -#if (_WIN32_WINNT >= 0x0600) - /* This bus type is not supported before Windows Server 2003 SP1 */ - || bus == BusTypeSas -#endif - ) { - /* We are able to use the same ioctls for different bus types - * according to Microsoft docs - * https://technet.microsoft.com/en-us/library/ee851589(v=ws.10).aspx */ - if (DeviceIoControl(vol_h, IOCTL_SCSI_GET_ADDRESS, NULL, 0, scsi_ad, - sizeof(SCSI_ADDRESS), &len, NULL)) { - disk->unit = addr.Lun; - disk->target = addr.TargetId; - disk->bus = addr.PathId; - disk->pci_controller = get_pci_info(name, errp); + /* Get list of extents */ + g_debug("getting disk extents"); + size = sizeof(VOLUME_DISK_EXTENTS); + extents = g_malloc0(size); + if (!DeviceIoControl(vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, + 0, extents, size, &size, NULL)) { + DWORD last_err = GetLastError(); + if (last_err == ERROR_MORE_DATA) { + /* Try once more with big enough buffer */ + g_free(extents); + extents = g_malloc0(size); + if (!DeviceIoControl( + vol_h, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, + 0, extents, size, NULL, NULL)) { + error_setg_win32(errp, GetLastError(), + "failed to get disk extents"); + goto out; + } + } else if (last_err == ERROR_INVALID_FUNCTION) { + /* Possibly CD-ROM or a shared drive. Try to pass the volume */ + g_debug("volume not on disk"); + disk = g_malloc0(sizeof(GuestDiskAddress)); + disk->has_dev = true; + disk->dev = g_strdup(name); + get_single_disk_info(0xffffffff, disk, &local_err); + if (local_err) { + g_debug("failed to get disk info, ignoring error: %s", + error_get_pretty(local_err)); + error_free(local_err); + goto out; + } + list = g_malloc0(sizeof(*list)); + list->value = disk; + disk = NULL; + list->next = NULL; + goto out; + } else { + error_setg_win32(errp, GetLastError(), + "failed to get disk extents"); + goto out; } - /* We do not set error in this case, because we still have enough - * information about volume. */ - } else { - disk->pci_controller = NULL; + } + g_debug("Number of extents: %lu", extents->NumberOfDiskExtents); + + /* Go through each extent */ + for (i = 0; i < extents->NumberOfDiskExtents; i++) { + disk = g_malloc0(sizeof(GuestDiskAddress)); + + /* Disk numbers directly correspond to numbers used in UNCs + * + * See documentation for DISK_EXTENT: + * https://docs.microsoft.com/en-us/windows/desktop/api/winioctl/ns-winioctl-_disk_extent + * + * See also Naming Files, Paths and Namespaces: + * https://docs.microsoft.com/en-us/windows/desktop/FileIO/naming-a-file#win32-device-namespaces + */ + disk->has_dev = true; + disk->dev = g_strdup_printf("\\\\.\\PhysicalDrive%lu", + extents->Extents[i].DiskNumber); + + get_single_disk_info(extents->Extents[i].DiskNumber, disk, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + cur_item = g_malloc0(sizeof(*list)); + cur_item->value = disk; + disk = NULL; + cur_item->next = list; + list = cur_item; } - list = g_malloc0(sizeof(*list)); - list->value = disk; - list->next = NULL; -out_close: - CloseHandle(vol_h); -out_free: + +out: + if (vol_h != INVALID_HANDLE_VALUE) { + CloseHandle(vol_h); + } + qapi_free_GuestDiskAddress(disk); + g_free(extents); g_free(name); + return list; } @@ -776,6 +1082,13 @@ GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **errp) * The frozen state is limited for up to 10 seconds by VSS. */ int64_t qmp_guest_fsfreeze_freeze(Error **errp) +{ + return qmp_guest_fsfreeze_freeze_list(false, NULL, errp); +} + +int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, + strList *mountpoints, + Error **errp) { int i; Error *local_err = NULL; @@ -790,7 +1103,7 @@ int64_t qmp_guest_fsfreeze_freeze(Error **errp) /* cannot risk guest agent blocking itself on a write in this state */ ga_set_frozen(ga_state); - qga_vss_fsfreeze(&i, true, &local_err); + qga_vss_fsfreeze(&i, true, mountpoints, &local_err); if (local_err) { error_propagate(errp, local_err); goto error; @@ -808,15 +1121,6 @@ error: return 0; } -int64_t qmp_guest_fsfreeze_freeze_list(bool has_mountpoints, - strList *mountpoints, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - - return 0; -} - /* * Thaw local file systems using Volume Shadow-copy Service. */ @@ -829,7 +1133,7 @@ int64_t qmp_guest_fsfreeze_thaw(Error **errp) return 0; } - qga_vss_fsfreeze(&i, false, errp); + qga_vss_fsfreeze(&i, false, NULL, errp); ga_unset_frozen(ga_state); return i; @@ -1117,7 +1421,6 @@ static char *guest_addr_to_str(IP_ADAPTER_UNICAST_ADDRESS *ip_addr, return NULL; } -#if (_WIN32_WINNT >= 0x0600) static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr) { /* For Windows Vista/2008 and newer, use the OnLinkPrefixLength @@ -1125,60 +1428,6 @@ static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr) */ return ip_addr->OnLinkPrefixLength; } -#else -/* When using the Windows XP and 2003 build environment, do the best we can to - * figure out the prefix. - */ -static IP_ADAPTER_INFO *guest_get_adapters_info(void) -{ - IP_ADAPTER_INFO *adptr_info = NULL; - ULONG adptr_info_len = 0; - DWORD ret; - - /* Call the first time to get the adptr_info_len. */ - GetAdaptersInfo(adptr_info, &adptr_info_len); - - adptr_info = g_malloc(adptr_info_len); - ret = GetAdaptersInfo(adptr_info, &adptr_info_len); - if (ret != ERROR_SUCCESS) { - g_free(adptr_info); - adptr_info = NULL; - } - return adptr_info; -} - -static int64_t guest_ip_prefix(IP_ADAPTER_UNICAST_ADDRESS *ip_addr) -{ - int64_t prefix = -1; /* Use for AF_INET6 and unknown/undetermined values. */ - IP_ADAPTER_INFO *adptr_info, *info; - IP_ADDR_STRING *ip; - struct in_addr *p; - - if (ip_addr->Address.lpSockaddr->sa_family != AF_INET) { - return prefix; - } - adptr_info = guest_get_adapters_info(); - if (adptr_info == NULL) { - return prefix; - } - - /* Match up the passed in ip_addr with one found in adaptr_info. - * The matching one in adptr_info will have the netmask. - */ - p = &((struct sockaddr_in *)ip_addr->Address.lpSockaddr)->sin_addr; - for (info = adptr_info; info; info = info->Next) { - for (ip = &info->IpAddressList; ip; ip = ip->Next) { - if (p->S_un.S_addr == inet_addr(ip->IpAddress.String)) { - prefix = ctpop32(inet_addr(ip->IpMask.String)); - goto out; - } - } - } -out: - g_free(adptr_info); - return prefix; -} -#endif #define INTERFACE_PATH_BUF_SZ 512 @@ -1646,7 +1895,6 @@ GList *ga_command_blacklist_init(GList *blacklist) "guest-set-vcpus", "guest-get-memory-blocks", "guest-set-memory-blocks", "guest-get-memory-block-size", - "guest-fsfreeze-freeze-list", NULL}; char **p = (char **)list_unsupported; @@ -1700,7 +1948,6 @@ typedef struct _GA_WTSINFOA { GuestUserList *qmp_guest_get_users(Error **err) { -#if (_WIN32_WINNT >= 0x0600) #define QGA_NANOSECONDS 10000000 GHashTable *cache = NULL; @@ -1770,10 +2017,6 @@ GuestUserList *qmp_guest_get_users(Error **err) } g_hash_table_destroy(cache); return head; -#else - error_setg(err, QERR_UNSUPPORTED); - return NULL; -#endif } typedef struct _ga_matrix_lookup_t { @@ -1801,12 +2044,24 @@ static ga_matrix_lookup_t const WIN_VERSION_MATRIX[2][8] = { { 6, 1, "Microsoft Windows Server 2008 R2", "2008r2"}, { 6, 2, "Microsoft Windows Server 2012", "2012"}, { 6, 3, "Microsoft Windows Server 2012 R2", "2012r2"}, - {10, 0, "Microsoft Windows Server 2016", "2016"}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} } }; +typedef struct _ga_win_10_0_server_t { + int final_build; + char const *version; + char const *version_id; +} ga_win_10_0_server_t; + +static ga_win_10_0_server_t const WIN_10_0_SERVER_VERSION_MATRIX[3] = { + {14393, "Microsoft Windows Server 2016", "2016"}, + {17763, "Microsoft Windows Server 2019", "2019"}, + {0, 0} +}; + static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) { typedef NTSTATUS(WINAPI * rtl_get_version_t)( @@ -1831,10 +2086,23 @@ static char *ga_get_win_name(OSVERSIONINFOEXW const *os_version, bool id) { DWORD major = os_version->dwMajorVersion; DWORD minor = os_version->dwMinorVersion; + DWORD build = os_version->dwBuildNumber; int tbl_idx = (os_version->wProductType != VER_NT_WORKSTATION); ga_matrix_lookup_t const *table = WIN_VERSION_MATRIX[tbl_idx]; + ga_win_10_0_server_t const *win_10_0_table = WIN_10_0_SERVER_VERSION_MATRIX; while (table->version != NULL) { - if (major == table->major && minor == table->minor) { + if (major == 10 && minor == 0 && tbl_idx) { + while (win_10_0_table->version != NULL) { + if (build <= win_10_0_table->final_build) { + if (id) { + return g_strdup(win_10_0_table->version_id); + } else { + return g_strdup(win_10_0_table->version); + } + } + win_10_0_table++; + } + } else if (major == table->major && minor == table->minor) { if (id) { return g_strdup(table->version_id); } else { diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index 6f4d214cb9..60eae16f27 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -10,6 +10,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. */ +#ifndef GUEST_AGENT_CORE_H +#define GUEST_AGENT_CORE_H + #include "qapi/qmp/dispatch.h" #include "qemu-common.h" #include "qga-qapi-types.h" @@ -46,3 +49,5 @@ int ga_parse_whence(GuestFileWhence *whence, Error **errp); #ifndef _WIN32 void reopen_fd_to_null(int fd); #endif + +#endif /* GUEST_AGENT_CORE_H */ diff --git a/qga/installer/qemu-ga.wxs b/qga/installer/qemu-ga.wxs index f751a7e9f7..64bf90bd85 100644 --- a/qga/installer/qemu-ga.wxs +++ b/qga/installer/qemu-ga.wxs @@ -78,7 +78,7 @@ Account="LocalSystem" ErrorControl="ignore" Interactive="no" - Arguments="-d" + Arguments="-d --retry-path" > diff --git a/qga/main.c b/qga/main.c index 87372d40ef..c0d77c79c4 100644 --- a/qga/main.c +++ b/qga/main.c @@ -18,7 +18,6 @@ #include #include #endif -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/json-parser.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" @@ -35,6 +34,7 @@ #include "qemu/systemd.h" #include "qemu-version.h" #ifdef _WIN32 +#include #include "qga/service-win32.h" #include "qga/vss-win32.h" #endif @@ -59,6 +59,7 @@ #endif #define QGA_SENTINEL_BYTE 0xFF #define QGA_CONF_DEFAULT CONFIG_QEMU_CONFDIR G_DIR_SEPARATOR_S "qemu-ga.conf" +#define QGA_RETRY_INTERVAL 5 static struct { const char *state_dir; @@ -70,6 +71,8 @@ typedef struct GAPersistentState { int64_t fd_counter; } GAPersistentState; +typedef struct GAConfig GAConfig; + struct GAState { JSONMessageParser parser; GMainLoop *main_loop; @@ -81,6 +84,7 @@ struct GAState { bool logging_enabled; #ifdef _WIN32 GAService service; + HANDLE wakeup_event; #endif bool delimit_response; bool frozen; @@ -95,6 +99,9 @@ struct GAState { #endif gchar *pstate_filepath; GAPersistentState pstate; + GAConfig *config; + int socket_activation; + bool force_exit; }; struct GAState *ga_state; @@ -114,8 +121,11 @@ static const char *ga_freeze_whitelist[] = { #ifdef _WIN32 DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, LPVOID ctx); +DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data); VOID WINAPI service_main(DWORD argc, TCHAR *argv[]); #endif +static int run_agent(GAState *s); +static void stop_agent(GAState *s, bool requested); static void init_dfl_pathnames(void) @@ -152,7 +162,7 @@ static void quit_handler(int sig) WaitForSingleObject(hEventTimeout, 0); CloseHandle(hEventTimeout); } - qga_vss_fsfreeze(&i, false, &err); + qga_vss_fsfreeze(&i, false, NULL, &err); if (err) { g_debug("Error unfreezing filesystems prior to exiting: %s", error_get_pretty(err)); @@ -164,9 +174,7 @@ static void quit_handler(int sig) } g_debug("received signal num %d, quitting", sig); - if (g_main_loop_is_running(ga_state->main_loop)) { - g_main_loop_quit(ga_state->main_loop); - } + stop_agent(ga_state, true); } #ifndef _WIN32 @@ -251,6 +259,10 @@ QEMU_COPYRIGHT "\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" +" -r, --retry-path attempt re-opening path if it's unavailable or closed\n" +" due to an error which may be recoverable in the future\n" +" (virtio-serial driver re-install, serial device hot\n" +" plug/unplug, etc.)\n" " -h, --help display this help and exit\n" "\n" QEMU_HELP_BOTTOM "\n" @@ -341,46 +353,6 @@ static FILE *ga_open_logfile(const char *logfile) return f; } -#ifndef _WIN32 -static bool ga_open_pidfile(const char *pidfile) -{ - int pidfd; - char pidstr[32]; - - pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); - if (pidfd == -1 || lockf(pidfd, F_TLOCK, 0)) { - g_critical("Cannot lock pid file, %s", strerror(errno)); - if (pidfd != -1) { - close(pidfd); - } - return false; - } - - if (ftruncate(pidfd, 0)) { - g_critical("Failed to truncate pid file"); - goto fail; - } - snprintf(pidstr, sizeof(pidstr), "%d\n", getpid()); - if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { - g_critical("Failed to write pid file"); - goto fail; - } - - /* keep pidfile open & locked forever */ - return true; - -fail: - unlink(pidfile); - close(pidfd); - return false; -} -#else /* _WIN32 */ -static bool ga_open_pidfile(const char *pidfile) -{ - return true; -} -#endif - static gint ga_strcmp(gconstpointer str1, gconstpointer str2) { return strcmp(str1, str2); @@ -480,8 +452,11 @@ void ga_unset_frozen(GAState *s) ga_enable_logging(s); g_warning("logging re-enabled due to filesystem unfreeze"); if (s->deferred_options.pid_filepath) { - if (!ga_open_pidfile(s->deferred_options.pid_filepath)) { - g_warning("failed to create/open pid file"); + Error *err = NULL; + + if (!qemu_write_pidfile(s->deferred_options.pid_filepath, &err)) { + g_warning("%s", error_get_pretty(err)); + error_free(err); } s->deferred_options.pid_filepath = NULL; } @@ -516,8 +491,11 @@ static void become_daemon(const char *pidfile) } if (pidfile) { - if (!ga_open_pidfile(pidfile)) { - g_critical("failed to create pidfile"); + Error *err = NULL; + + if (!qemu_write_pidfile(pidfile, &err)) { + g_critical("%s", error_get_pretty(err)); + error_free(err); exit(EXIT_FAILURE); } } @@ -545,15 +523,15 @@ fail: #endif } -static int send_response(GAState *s, QDict *payload) +static int send_response(GAState *s, const QDict *rsp) { const char *buf; QString *payload_qstr, *response_qstr; GIOStatus status; - g_assert(payload && s->channel); + g_assert(rsp && s->channel); - payload_qstr = qobject_to_json(QOBJECT(payload)); + payload_qstr = qobject_to_json(QOBJECT(rsp)); if (!payload_qstr) { return -EINVAL; } @@ -579,57 +557,24 @@ static int send_response(GAState *s, QDict *payload) return 0; } -static void process_command(GAState *s, QDict *req) +/* handle requests/control events coming in over the channel */ +static void process_event(void *opaque, QObject *obj, Error *err) { + GAState *s = opaque; QDict *rsp; int ret; - g_assert(req); - g_debug("processing command"); - rsp = qmp_dispatch(&ga_commands, QOBJECT(req), false); - if (rsp) { - ret = send_response(s, rsp); - if (ret < 0) { - g_warning("error sending response: %s", strerror(-ret)); - } - qobject_unref(rsp); - } -} - -/* handle requests/control events coming in over the channel */ -static void process_event(JSONMessageParser *parser, GQueue *tokens) -{ - GAState *s = container_of(parser, GAState, parser); - QObject *obj; - QDict *req, *rsp; - Error *err = NULL; - int ret; - - g_assert(s && parser); - g_debug("process_event: called"); - obj = json_parser_parse_err(tokens, NULL, &err); + assert(!obj != !err); if (err) { - goto err; - } - req = qobject_to(QDict, obj); - if (!req) { - error_setg(&err, QERR_JSON_PARSING); - goto err; - } - if (!qdict_haskey(req, "execute")) { - g_warning("unrecognized payload format"); - error_setg(&err, QERR_UNSUPPORTED); - goto err; + rsp = qmp_error_response(err); + goto end; } - process_command(s, req); - qobject_unref(obj); - return; + g_debug("processing command"); + rsp = qmp_dispatch(&ga_commands, obj, false); -err: - g_warning("failed to parse event: %s", error_get_pretty(err)); - rsp = qmp_error_response(err); +end: ret = send_response(s, rsp); if (ret < 0) { g_warning("error sending error response: %s", strerror(-ret)); @@ -648,6 +593,7 @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) switch (status) { case G_IO_STATUS_ERROR: g_warning("error reading channel"); + stop_agent(s, false); return false; case G_IO_STATUS_NORMAL: buf[count] = 0; @@ -705,6 +651,36 @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path, } #ifdef _WIN32 +DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data) +{ + DWORD ret = NO_ERROR; + PDEV_BROADCAST_HDR broadcast_header = (PDEV_BROADCAST_HDR)data; + + if (broadcast_header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { + switch (type) { + /* Device inserted */ + case DBT_DEVICEARRIVAL: + /* Start QEMU-ga's service */ + if (!SetEvent(ga_state->wakeup_event)) { + ret = GetLastError(); + } + break; + /* Device removed */ + case DBT_DEVICEQUERYREMOVE: + case DBT_DEVICEREMOVEPENDING: + case DBT_DEVICEREMOVECOMPLETE: + /* Stop QEMU-ga's service */ + if (!ResetEvent(ga_state->wakeup_event)) { + ret = GetLastError(); + } + break; + default: + ret = ERROR_CALL_NOT_IMPLEMENTED; + } + } + return ret; +} + DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, LPVOID ctx) { @@ -716,9 +692,13 @@ DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, case SERVICE_CONTROL_STOP: case SERVICE_CONTROL_SHUTDOWN: quit_handler(SIGTERM); + SetEvent(ga_state->wakeup_event); service->status.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(service->status_handle, &service->status); break; + case SERVICE_CONTROL_DEVICEEVENT: + handle_serial_device_events(type, data); + break; default: ret = ERROR_CALL_NOT_IMPLEMENTED; @@ -745,10 +725,24 @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[]) service->status.dwServiceSpecificExitCode = NO_ERROR; service->status.dwCheckPoint = 0; service->status.dwWaitHint = 0; + DEV_BROADCAST_DEVICEINTERFACE notification_filter; + ZeroMemory(¬ification_filter, sizeof(notification_filter)); + notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; + notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); + notification_filter.dbcc_classguid = GUID_VIOSERIAL_PORT; + + service->device_notification_handle = + RegisterDeviceNotification(service->status_handle, + ¬ification_filter, DEVICE_NOTIFY_SERVICE_HANDLE); + if (!service->device_notification_handle) { + g_critical("Failed to register device notification handle!\n"); + return; + } SetServiceStatus(service->status_handle, &service->status); - g_main_loop_run(ga_state->main_loop); + run_agent(ga_state); + UnregisterDeviceNotification(service->device_notification_handle); service->status.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(service->status_handle, &service->status); } @@ -944,7 +938,7 @@ static GList *split_list(const gchar *str, const gchar *delim) return list; } -typedef struct GAConfig { +struct GAConfig { char *channel_path; char *method; char *log_filepath; @@ -961,7 +955,8 @@ typedef struct GAConfig { int daemonize; GLogLevelFlags log_level; int dumpconf; -} GAConfig; + bool retry_path; +}; static void config_load(GAConfig *config) { @@ -1010,6 +1005,10 @@ static void config_load(GAConfig *config) /* enable all log levels */ config->log_level = G_LOG_LEVEL_MASK; } + if (g_key_file_has_key(keyfile, "general", "retry-path", NULL)) { + config->retry_path = + g_key_file_get_boolean(keyfile, "general", "retry-path", &gerr); + } if (g_key_file_has_key(keyfile, "general", "blacklist", NULL)) { config->bliststr = g_key_file_get_string(keyfile, "general", "blacklist", &gerr); @@ -1071,6 +1070,8 @@ static void config_dump(GAConfig *config) 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); + g_key_file_set_boolean(keyfile, "general", "retry-path", + config->retry_path); tmp = list_join(config->blacklist, ','); g_key_file_set_string(keyfile, "general", "blacklist", tmp); g_free(tmp); @@ -1089,7 +1090,7 @@ static void config_dump(GAConfig *config) static void config_parse(GAConfig *config, int argc, char **argv) { - const char *sopt = "hVvdm:p:l:f:F::b:s:t:D"; + const char *sopt = "hVvdm:p:l:f:F::b:s:t:Dr"; int opt_ind = 0, ch; const struct option lopt[] = { { "help", 0, NULL, 'h' }, @@ -1109,6 +1110,7 @@ static void config_parse(GAConfig *config, int argc, char **argv) { "service", 1, NULL, 's' }, #endif { "statedir", 1, NULL, 't' }, + { "retry-path", 0, NULL, 'r' }, { NULL, 0, NULL, 0 } }; @@ -1153,6 +1155,9 @@ static void config_parse(GAConfig *config, int argc, char **argv) case 'D': config->dumpconf = 1; break; + case 'r': + config->retry_path = true; + break; case 'b': { if (is_help_option(optarg)) { qmp_for_each_command(&ga_commands, ga_print_cmd, NULL); @@ -1250,9 +1255,21 @@ static bool check_is_frozen(GAState *s) return false; } -static int run_agent(GAState *s, GAConfig *config, int socket_activation) +static GAState *initialize_agent(GAConfig *config, int socket_activation) { - ga_state = s; + GAState *s = g_new0(GAState, 1); + + g_assert(ga_state == NULL); + + 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); g_log_set_default_handler(ga_log, s); g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR); @@ -1268,7 +1285,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) 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; + return NULL; } #endif @@ -1293,7 +1310,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) if (!log_file) { g_critical("unable to open specified log file: %s", strerror(errno)); - return EXIT_FAILURE; + return NULL; } s->log_file = log_file; } @@ -1304,7 +1321,7 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) s->pstate_filepath, ga_is_frozen(s))) { g_critical("failed to load persistent state"); - return EXIT_FAILURE; + return NULL; } config->blacklist = ga_command_blacklist_init(config->blacklist); @@ -1320,41 +1337,121 @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) s->command_state = ga_command_state_new(); ga_command_state_init(s, s->command_state); ga_command_state_init_all(s->command_state); - json_message_parser_init(&s->parser, process_event); + json_message_parser_init(&s->parser, process_event, s, NULL); #ifndef _WIN32 if (!register_signal_handlers()) { g_critical("failed to register signal handlers"); - return EXIT_FAILURE; + return NULL; } #endif s->main_loop = g_main_loop_new(NULL, false); - if (!channel_init(ga_state, config->method, config->channel_path, - socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { - g_critical("failed to initialize guest agent channel"); - return EXIT_FAILURE; - } -#ifndef _WIN32 - g_main_loop_run(ga_state->main_loop); -#else - if (config->daemonize) { - SERVICE_TABLE_ENTRY service_table[] = { - { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; - StartServiceCtrlDispatcher(service_table); - } else { - g_main_loop_run(ga_state->main_loop); + s->config = config; + s->socket_activation = socket_activation; + +#ifdef _WIN32 + s->wakeup_event = CreateEvent(NULL, TRUE, FALSE, TEXT("WakeUp")); + if (s->wakeup_event == NULL) { + g_critical("CreateEvent failed"); + return NULL; } #endif + ga_state = s; + return s; +} + +static void cleanup_agent(GAState *s) +{ +#ifdef _WIN32 + CloseHandle(s->wakeup_event); +#endif + if (s->command_state) { + ga_command_state_cleanup_all(s->command_state); + ga_command_state_free(s->command_state); + json_message_parser_destroy(&s->parser); + } + g_free(s->pstate_filepath); + g_free(s->state_filepath_isfrozen); + if (s->main_loop) { + g_main_loop_unref(s->main_loop); + } + g_free(s); + ga_state = NULL; +} + +static int run_agent_once(GAState *s) +{ + if (!channel_init(s, s->config->method, s->config->channel_path, + s->socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { + g_critical("failed to initialize guest agent channel"); + return EXIT_FAILURE; + } + + g_main_loop_run(ga_state->main_loop); + + if (s->channel) { + ga_channel_free(s->channel); + } + return EXIT_SUCCESS; } +static void wait_for_channel_availability(GAState *s) +{ + g_warning("waiting for channel path..."); +#ifndef _WIN32 + sleep(QGA_RETRY_INTERVAL); +#else + DWORD dwWaitResult; + + dwWaitResult = WaitForSingleObject(s->wakeup_event, INFINITE); + + switch (dwWaitResult) { + case WAIT_OBJECT_0: + break; + case WAIT_TIMEOUT: + break; + default: + g_critical("WaitForSingleObject failed"); + } +#endif +} + +static int run_agent(GAState *s) +{ + int ret = EXIT_SUCCESS; + + s->force_exit = false; + + do { + ret = run_agent_once(s); + if (s->config->retry_path && !s->force_exit) { + g_warning("agent stopped unexpectedly, restarting..."); + wait_for_channel_availability(s); + } + } while (s->config->retry_path && !s->force_exit); + + return ret; +} + +static void stop_agent(GAState *s, bool requested) +{ + if (!s->force_exit) { + s->force_exit = requested; + } + + if (g_main_loop_is_running(s->main_loop)) { + g_main_loop_quit(s->main_loop); + } +} + int main(int argc, char **argv) { int ret = EXIT_SUCCESS; - GAState *s = g_new0(GAState, 1); + GAState *s; GAConfig *config = g_new0(GAConfig, 1); int socket_activation; @@ -1422,44 +1519,37 @@ int main(int argc, char **argv) } } - 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, socket_activation); + s = initialize_agent(config, socket_activation); + if (!s) { + g_critical("error initializing guest agent"); + goto end; + } + +#ifdef _WIN32 + if (config->daemonize) { + SERVICE_TABLE_ENTRY service_table[] = { + { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; + StartServiceCtrlDispatcher(service_table); + } else { + ret = run_agent(s); + } +#else + ret = run_agent(s); +#endif + + cleanup_agent(s); end: - if (s->command_state) { - ga_command_state_cleanup_all(s->command_state); - ga_command_state_free(s->command_state); - json_message_parser_destroy(&s->parser); - } - if (s->channel) { - ga_channel_free(s->channel); - } - g_free(s->pstate_filepath); - g_free(s->state_filepath_isfrozen); - if (config->daemonize) { unlink(config->pid_filepath); } config_free(config); - if (s->main_loop) { - g_main_loop_unref(s->main_loop); - } - g_free(s); return ret; } diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index dfbc4a5e32..fb4605cc19 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -532,12 +532,12 @@ # # Suspend guest to disk. # -# This command tries to execute the scripts provided by the pm-utils package. -# If it's not available, the suspend operation will be performed by manually -# writing to a sysfs file. +# This command attempts to suspend the guest using three strategies, in this +# order: # -# For the best results it's strongly recommended to have the pm-utils -# package installed in the guest. +# - systemd hibernate +# - pm-utils (via pm-hibernate) +# - manual write into sysfs # # This command does NOT return a response on success. There is a high chance # the command succeeded if the VM exits with a zero exit status or, when @@ -560,16 +560,18 @@ # # Suspend guest to ram. # -# This command tries to execute the scripts provided by the pm-utils package. -# If it's not available, the suspend operation will be performed by manually -# writing to a sysfs file. +# This command attempts to suspend the guest using three strategies, in this +# order: # -# For the best results it's strongly recommended to have the pm-utils -# package installed in the guest. +# - systemd suspend +# - pm-utils (via pm-suspend) +# - manual write into sysfs # -# IMPORTANT: guest-suspend-ram requires QEMU to support the 'system_wakeup' -# command. Thus, it's *required* to query QEMU for the presence of the -# 'system_wakeup' command before issuing guest-suspend-ram. +# IMPORTANT: guest-suspend-ram requires working wakeup support in +# QEMU. You should check QMP command query-current-machine returns +# wakeup-suspend-support: true before issuing this command. Failure in +# doing so can result in a suspended guest that QEMU will not be able to +# awaken, forcing the user to power cycle the guest to bring it back. # # This command does NOT return a response on success. There are two options # to check for success: @@ -592,11 +594,16 @@ # # Save guest state to disk and suspend to ram. # -# This command requires the pm-utils package to be installed in the guest. +# This command attempts to suspend the guest by executing, in this order: # -# IMPORTANT: guest-suspend-hybrid requires QEMU to support the 'system_wakeup' -# command. Thus, it's *required* to query QEMU for the presence of the -# 'system_wakeup' command before issuing guest-suspend-hybrid. +# - systemd hybrid-sleep +# - pm-utils (via pm-suspend-hybrid) +# +# IMPORTANT: guest-suspend-hybrid requires working wakeup support in +# QEMU. You should check QMP command query-current-machine returns +# wakeup-suspend-support: true before issuing this command. Failure in +# doing so can result in a suspended guest that QEMU will not be able to +# awaken, forcing the user to power cycle the guest to bring it back. # # This command does NOT return a response on success. There are two options # to check for success: @@ -834,13 +841,16 @@ # @bus: bus id # @target: target id # @unit: unit id +# @serial: serial number (since: 3.1) +# @dev: device node (POSIX) or device UNC (Windows) (since: 3.1) # # Since: 2.2 ## { 'struct': 'GuestDiskAddress', 'data': {'pci-controller': 'GuestPCIAddress', 'bus-type': 'GuestDiskBusType', - 'bus': 'int', 'target': 'int', 'unit': 'int'} } + 'bus': 'int', 'target': 'int', 'unit': 'int', + '*serial': 'str', '*dev': 'str'} } ## # @GuestFilesystemInfo: diff --git a/qga/service-win32.h b/qga/service-win32.h index 89e99dfede..7b16d69b57 100644 --- a/qga/service-win32.h +++ b/qga/service-win32.h @@ -20,9 +20,13 @@ #define QGA_SERVICE_NAME "qemu-ga" #define QGA_SERVICE_DESCRIPTION "Enables integration with QEMU machine emulator and virtualizer." +static const GUID GUID_VIOSERIAL_PORT = { 0x6fde7521, 0x1b65, 0x48ae, +{ 0xb6, 0x28, 0x80, 0xbe, 0x62, 0x1, 0x60, 0x26 } }; + typedef struct GAService { SERVICE_STATUS status; SERVICE_STATUS_HANDLE status_handle; + HDEVNOTIFY device_notification_handle; } GAService; int ga_install_service(const char *path, const char *logfile, diff --git a/qga/vss-win32.c b/qga/vss-win32.c index a541f3ae01..f444a25a70 100644 --- a/qga/vss-win32.c +++ b/qga/vss-win32.c @@ -147,7 +147,8 @@ void ga_uninstall_vss_provider(void) } /* Call VSS requester and freeze/thaw filesystems and applications */ -void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp) +void qga_vss_fsfreeze(int *nr_volume, bool freeze, + strList *mountpoints, Error **errp) { const char *func_name = freeze ? "requester_freeze" : "requester_thaw"; QGAVSSRequesterFunc func; @@ -164,5 +165,5 @@ void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp) return; } - func(nr_volume, &errset); + func(nr_volume, mountpoints, &errset); } diff --git a/qga/vss-win32.h b/qga/vss-win32.h index 4f8e39aa5c..ce2abe5a72 100644 --- a/qga/vss-win32.h +++ b/qga/vss-win32.h @@ -22,6 +22,7 @@ bool vss_initialized(void); int ga_install_vss_provider(void); void ga_uninstall_vss_provider(void); -void qga_vss_fsfreeze(int *nr_volume, bool freeze, Error **errp); +void qga_vss_fsfreeze(int *nr_volume, bool freeze, + strList *mountpints, Error **errp); #endif diff --git a/qga/vss-win32/Makefile.objs b/qga/vss-win32/Makefile.objs index 23d08da225..fd3ba1896b 100644 --- a/qga/vss-win32/Makefile.objs +++ b/qga/vss-win32/Makefile.objs @@ -3,9 +3,9 @@ qga-vss-dll-obj-y += requester.o provider.o install.o obj-qga-vss-dll-obj-y = $(addprefix $(obj)/, $(qga-vss-dll-obj-y)) -$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS = $(filter-out -Wstrict-prototypes -Wmissing-prototypes -Wnested-externs -Wold-style-declaration -Wold-style-definition -Wredundant-decls -fstack-protector-all -fstack-protector-strong, $(QEMU_CFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor +$(obj-qga-vss-dll-obj-y): QEMU_CXXFLAGS := $(filter-out -fstack-protector-all -fstack-protector-strong, $(QEMU_CXXFLAGS)) -Wno-unknown-pragmas -Wno-delete-non-virtual-dtor -$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lole32 -loleaut32 -lshlwapi -luuid -static +$(obj)/qga-vss.dll: LDFLAGS = -shared -Wl,--add-stdcall-alias,--enable-stdcall-fixup -lglib-2.0 -lole32 -loleaut32 -lshlwapi -luuid -lintl -lws2_32 -static $(obj)/qga-vss.dll: $(obj-qga-vss-dll-obj-y) $(SRC_PATH)/$(obj)/qga-vss.def $(call quiet-command,$(CXX) -o $@ $(qga-vss-dll-obj-y) $(SRC_PATH)/qga/vss-win32/qga-vss.def $(CXXFLAGS) $(LDFLAGS),"LINK","$(TARGET_DIR)$@") diff --git a/qga/vss-win32/requester.cpp b/qga/vss-win32/requester.cpp index 3d9c9716c0..5378c55d23 100644 --- a/qga/vss-win32/requester.cpp +++ b/qga/vss-win32/requester.cpp @@ -234,7 +234,7 @@ out: } } -void requester_freeze(int *num_vols, ErrorSet *errset) +void requester_freeze(int *num_vols, void *mountpoints, ErrorSet *errset) { COMPointer pAsync; HANDLE volume; @@ -246,6 +246,7 @@ void requester_freeze(int *num_vols, ErrorSet *errset) WCHAR short_volume_name[64], *display_name = short_volume_name; DWORD wait_status; int num_fixed_drives = 0, i; + int num_mount_points = 0; if (vss_ctx.pVssbc) { /* already frozen */ *num_vols = 0; @@ -337,39 +338,73 @@ void requester_freeze(int *num_vols, ErrorSet *errset) goto out; } - volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name)); - if (volume == INVALID_HANDLE_VALUE) { - err_set(errset, hr, "failed to find first volume"); - goto out; - } - for (;;) { - if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) { + if (mountpoints) { + PWCHAR volume_name_wchar; + for (volList *list = (volList *)mountpoints; list; list = list->next) { + size_t len = strlen(list->value) + 1; + size_t converted = 0; VSS_ID pid; - hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name, + + volume_name_wchar = new wchar_t[len]; + mbstowcs_s(&converted, volume_name_wchar, len, + list->value, _TRUNCATE); + + hr = vss_ctx.pVssbc->AddToSnapshotSet(volume_name_wchar, g_gProviderId, &pid); if (FAILED(hr)) { - WCHAR volume_path_name[PATH_MAX]; - if (GetVolumePathNamesForVolumeNameW( - short_volume_name, volume_path_name, - sizeof(volume_path_name), NULL) && *volume_path_name) { - display_name = volume_path_name; - } err_set(errset, hr, "failed to add %S to snapshot set", - display_name); - FindVolumeClose(volume); + volume_name_wchar); + delete volume_name_wchar; goto out; } - num_fixed_drives++; + num_mount_points++; + + delete volume_name_wchar; } - if (!FindNextVolumeW(volume, short_volume_name, - sizeof(short_volume_name))) { - FindVolumeClose(volume); - break; + + if (num_mount_points == 0) { + /* If there is no valid mount points, just exit. */ + goto out; } } - if (num_fixed_drives == 0) { - goto out; /* If there is no fixed drive, just exit. */ + if (!mountpoints) { + volume = FindFirstVolumeW(short_volume_name, sizeof(short_volume_name)); + if (volume == INVALID_HANDLE_VALUE) { + err_set(errset, hr, "failed to find first volume"); + goto out; + } + + for (;;) { + if (GetDriveTypeW(short_volume_name) == DRIVE_FIXED) { + VSS_ID pid; + hr = vss_ctx.pVssbc->AddToSnapshotSet(short_volume_name, + g_gProviderId, &pid); + if (FAILED(hr)) { + WCHAR volume_path_name[PATH_MAX]; + if (GetVolumePathNamesForVolumeNameW( + short_volume_name, volume_path_name, + sizeof(volume_path_name), NULL) && + *volume_path_name) { + display_name = volume_path_name; + } + err_set(errset, hr, "failed to add %S to snapshot set", + display_name); + FindVolumeClose(volume); + goto out; + } + num_fixed_drives++; + } + if (!FindNextVolumeW(volume, short_volume_name, + sizeof(short_volume_name))) { + FindVolumeClose(volume); + break; + } + } + + if (num_fixed_drives == 0) { + goto out; /* If there is no fixed drive, just exit. */ + } } hr = vss_ctx.pVssbc->PrepareForBackup(pAsync.replace()); @@ -435,7 +470,12 @@ void requester_freeze(int *num_vols, ErrorSet *errset) goto out; } - *num_vols = vss_ctx.cFrozenVols = num_fixed_drives; + if (mountpoints) { + *num_vols = vss_ctx.cFrozenVols = num_mount_points; + } else { + *num_vols = vss_ctx.cFrozenVols = num_fixed_drives; + } + return; out: @@ -449,7 +489,7 @@ out1: } -void requester_thaw(int *num_vols, ErrorSet *errset) +void requester_thaw(int *num_vols, void *mountpints, ErrorSet *errset) { COMPointer pAsync; diff --git a/qga/vss-win32/requester.h b/qga/vss-win32/requester.h index 2a39d734a2..5a8e8faf0c 100644 --- a/qga/vss-win32/requester.h +++ b/qga/vss-win32/requester.h @@ -34,9 +34,16 @@ typedef struct ErrorSet { STDAPI requester_init(void); STDAPI requester_deinit(void); -typedef void (*QGAVSSRequesterFunc)(int *, ErrorSet *); -void requester_freeze(int *num_vols, ErrorSet *errset); -void requester_thaw(int *num_vols, ErrorSet *errset); +typedef struct volList volList; + +struct volList { + volList *next; + char *value; +}; + +typedef void (*QGAVSSRequesterFunc)(int *, void *, ErrorSet *); +void requester_freeze(int *num_vols, void *volList, ErrorSet *errset); +void requester_thaw(int *num_vols, void *volList, ErrorSet *errset); #ifdef __cplusplus } diff --git a/qmp.c b/qmp.c index e7c0a2fd60..b92d62cd5f 100644 --- a/qmp.c +++ b/qmp.c @@ -88,7 +88,7 @@ UuidInfo *qmp_query_uuid(Error **errp) void qmp_quit(Error **errp) { no_shutdown = 0; - qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_QMP); + qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_QMP_QUIT); } void qmp_stop(Error **errp) @@ -109,7 +109,7 @@ void qmp_stop(Error **errp) void qmp_system_reset(Error **errp) { - qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP); + qemu_system_reset_request(SHUTDOWN_CAUSE_HOST_QMP_SYSTEM_RESET); } void qmp_system_powerdown(Error **erp) @@ -183,7 +183,13 @@ void qmp_cont(Error **errp) void qmp_system_wakeup(Error **errp) { - qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER); + if (!qemu_wakeup_suspend_enabled()) { + error_setg(errp, + "wake-up from suspend is not supported by this guest"); + return; + } + + qemu_system_wakeup_request(QEMU_WAKEUP_REASON_OTHER, errp); } ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) @@ -599,32 +605,6 @@ ObjectPropertyInfoList *qmp_qom_list_properties(const char *typename, return prop_list; } -CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) -{ - return arch_query_cpu_definitions(errp); -} - -CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *model, - Error **errp) -{ - return arch_query_cpu_model_expansion(type, model, errp); -} - -CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *modela, - CpuModelInfo *modelb, - Error **errp) -{ - return arch_query_cpu_model_comparison(modela, modelb, errp); -} - -CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *modela, - CpuModelInfo *modelb, - Error **errp) -{ - return arch_query_cpu_model_baseline(modela, modelb, errp); -} - void qmp_add_client(const char *protocol, const char *fdname, bool has_skipauth, bool skipauth, bool has_tls, bool tls, Error **errp) diff --git a/qobject/block-qdict.c b/qobject/block-qdict.c index 80c653013f..1487cc5dd8 100644 --- a/qobject/block-qdict.c +++ b/qobject/block-qdict.c @@ -158,20 +158,25 @@ void qdict_flatten(QDict *qdict) qdict_flatten_qdict(qdict, qdict, NULL); } -/* extract all the src QDict entries starting by start into dst */ +/* extract all the src QDict entries starting by start into dst. + * If dst is NULL then the entries are simply removed from src. */ void qdict_extract_subqdict(QDict *src, QDict **dst, const char *start) { const QDictEntry *entry, *next; const char *p; - *dst = qdict_new(); + if (dst) { + *dst = qdict_new(); + } entry = qdict_first(src); while (entry != NULL) { next = qdict_next(src, entry); if (strstart(entry->key, start, &p)) { - qdict_put_obj(*dst, p, qobject_ref(entry->value)); + if (dst) { + qdict_put_obj(*dst, p, qobject_ref(entry->value)); + } qdict_del(src, entry->key); } entry = next; @@ -572,7 +577,7 @@ static QObject *qdict_crumple_for_keyval_qiv(QDict *src, Error **errp) if (!tmp) { tmp = qdict_clone_shallow(src); } - qdict_put(tmp, ent->key, qstring_from_str(s)); + qdict_put_str(tmp, ent->key, s); g_free(buf); } diff --git a/qobject/json-lexer.c b/qobject/json-lexer.c index 980ba159d6..632320d72d 100644 --- a/qobject/json-lexer.c +++ b/qobject/json-lexer.c @@ -12,151 +12,163 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/qmp/json-lexer.h" +#include "json-parser-int.h" #define MAX_TOKEN_SIZE (64ULL << 20) /* - * Required by JSON (RFC 7159): + * From RFC 8259 "The JavaScript Object Notation (JSON) Data + * Interchange Format", with [comments in brackets]: * - * \"([^\\\"]|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*\" - * -?(0|[1-9][0-9]*)(.[0-9]+)?([eE][-+]?[0-9]+)? - * [{}\[\],:] - * [a-z]+ # covers null, true, false + * The set of tokens includes six structural characters, strings, + * numbers, and three literal names. * - * Extension of '' strings: + * These are the six structural characters: * - * '([^\\']|\\[\"'\\/bfnrt]|\\u[0-9a-fA-F]{4})*' + * begin-array = ws %x5B ws ; [ left square bracket + * begin-object = ws %x7B ws ; { left curly bracket + * end-array = ws %x5D ws ; ] right square bracket + * end-object = ws %x7D ws ; } right curly bracket + * name-separator = ws %x3A ws ; : colon + * value-separator = ws %x2C ws ; , comma * - * Extension for vararg handling in JSON construction: + * Insignificant whitespace is allowed before or after any of the six + * structural characters. + * [This lexer accepts it before or after any token, which is actually + * the same, as the grammar always has structural characters between + * other tokens.] * - * %((l|ll|I64)?d|[ipsf]) + * ws = *( + * %x20 / ; Space + * %x09 / ; Horizontal tab + * %x0A / ; Line feed or New line + * %x0D ) ; Carriage return * + * [...] three literal names: + * false null true + * [This lexer accepts [a-z]+, and leaves rejecting unknown literal + * names to the parser.] + * + * [Numbers:] + * + * number = [ minus ] int [ frac ] [ exp ] + * decimal-point = %x2E ; . + * digit1-9 = %x31-39 ; 1-9 + * e = %x65 / %x45 ; e E + * exp = e [ minus / plus ] 1*DIGIT + * frac = decimal-point 1*DIGIT + * int = zero / ( digit1-9 *DIGIT ) + * minus = %x2D ; - + * plus = %x2B ; + + * zero = %x30 ; 0 + * + * [Strings:] + * string = quotation-mark *char quotation-mark + * + * char = unescaped / + * escape ( + * %x22 / ; " quotation mark U+0022 + * %x5C / ; \ reverse solidus U+005C + * %x2F / ; / solidus U+002F + * %x62 / ; b backspace U+0008 + * %x66 / ; f form feed U+000C + * %x6E / ; n line feed U+000A + * %x72 / ; r carriage return U+000D + * %x74 / ; t tab U+0009 + * %x75 4HEXDIG ) ; uXXXX U+XXXX + * escape = %x5C ; \ + * quotation-mark = %x22 ; " + * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + * [This lexer accepts any non-control character after escape, and + * leaves rejecting invalid ones to the parser.] + * + * + * Extensions over RFC 8259: + * - Extra escape sequence in strings: + * 0x27 (apostrophe) is recognized after escape, too + * - Single-quoted strings: + * Like double-quoted strings, except they're delimited by %x27 + * (apostrophe) instead of %x22 (quotation mark), and can't contain + * unescaped apostrophe, but can contain unescaped quotation mark. + * - Interpolation, if enabled: + * The lexer accepts %[A-Za-z0-9]*, and leaves rejecting invalid + * ones to the parser. + * + * Note: + * - Input must be encoded in modified UTF-8. + * - Decoding and validating is left to the parser. */ enum json_lexer_state { - IN_ERROR = 0, /* must really be 0, see json_lexer[] */ - IN_DQ_UCODE3, - IN_DQ_UCODE2, - IN_DQ_UCODE1, - IN_DQ_UCODE0, + IN_RECOVERY = 1, IN_DQ_STRING_ESCAPE, IN_DQ_STRING, - IN_SQ_UCODE3, - IN_SQ_UCODE2, - IN_SQ_UCODE1, - IN_SQ_UCODE0, IN_SQ_STRING_ESCAPE, IN_SQ_STRING, IN_ZERO, - IN_DIGITS, - IN_DIGIT, + IN_EXP_DIGITS, + IN_EXP_SIGN, IN_EXP_E, IN_MANTISSA, IN_MANTISSA_DIGITS, - IN_NONZERO_NUMBER, - IN_NEG_NONZERO_NUMBER, + IN_DIGITS, + IN_SIGN, IN_KEYWORD, - IN_ESCAPE, - IN_ESCAPE_L, - IN_ESCAPE_LL, - IN_ESCAPE_I, - IN_ESCAPE_I6, - IN_ESCAPE_I64, - IN_WHITESPACE, + IN_INTERP, IN_START, + IN_START_INTERP, /* must be IN_START + 1 */ }; -QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START); +QEMU_BUILD_BUG_ON(JSON_ERROR != 0); +QEMU_BUILD_BUG_ON(IN_RECOVERY != JSON_ERROR + 1); +QEMU_BUILD_BUG_ON((int)JSON_MIN <= (int)IN_START_INTERP); +QEMU_BUILD_BUG_ON(JSON_MAX >= 0x80); +QEMU_BUILD_BUG_ON(IN_START_INTERP != IN_START + 1); -#define TERMINAL(state) [0 ... 0x7F] = (state) - -/* Return whether TERMINAL is a terminal state and the transition to it - from OLD_STATE required lookahead. This happens whenever the table - below uses the TERMINAL macro. */ -#define TERMINAL_NEEDED_LOOKAHEAD(old_state, terminal) \ - (json_lexer[(old_state)][0] == (terminal)) +#define LOOKAHEAD 0x80 +#define TERMINAL(state) [0 ... 0xFF] = ((state) | LOOKAHEAD) static const uint8_t json_lexer[][256] = { /* Relies on default initialization to IN_ERROR! */ + /* error recovery */ + [IN_RECOVERY] = { + /* + * Skip characters until a structural character, an ASCII + * control character other than '\t', or impossible UTF-8 + * bytes '\xFE', '\xFF'. Structural characters and line + * endings are promising resynchronization points. Clients + * may use the others to force the JSON parser into known-good + * state; see docs/interop/qmp-spec.txt. + */ + [0 ... 0x1F] = IN_START | LOOKAHEAD, + [0x20 ... 0xFD] = IN_RECOVERY, + [0xFE ... 0xFF] = IN_START | LOOKAHEAD, + ['\t'] = IN_RECOVERY, + ['['] = IN_START | LOOKAHEAD, + [']'] = IN_START | LOOKAHEAD, + ['{'] = IN_START | LOOKAHEAD, + ['}'] = IN_START | LOOKAHEAD, + [':'] = IN_START | LOOKAHEAD, + [','] = IN_START | LOOKAHEAD, + }, + /* double quote string */ - [IN_DQ_UCODE3] = { - ['0' ... '9'] = IN_DQ_STRING, - ['a' ... 'f'] = IN_DQ_STRING, - ['A' ... 'F'] = IN_DQ_STRING, - }, - [IN_DQ_UCODE2] = { - ['0' ... '9'] = IN_DQ_UCODE3, - ['a' ... 'f'] = IN_DQ_UCODE3, - ['A' ... 'F'] = IN_DQ_UCODE3, - }, - [IN_DQ_UCODE1] = { - ['0' ... '9'] = IN_DQ_UCODE2, - ['a' ... 'f'] = IN_DQ_UCODE2, - ['A' ... 'F'] = IN_DQ_UCODE2, - }, - [IN_DQ_UCODE0] = { - ['0' ... '9'] = IN_DQ_UCODE1, - ['a' ... 'f'] = IN_DQ_UCODE1, - ['A' ... 'F'] = IN_DQ_UCODE1, - }, [IN_DQ_STRING_ESCAPE] = { - ['b'] = IN_DQ_STRING, - ['f'] = IN_DQ_STRING, - ['n'] = IN_DQ_STRING, - ['r'] = IN_DQ_STRING, - ['t'] = IN_DQ_STRING, - ['/'] = IN_DQ_STRING, - ['\\'] = IN_DQ_STRING, - ['\''] = IN_DQ_STRING, - ['\"'] = IN_DQ_STRING, - ['u'] = IN_DQ_UCODE0, + [0x20 ... 0xFD] = IN_DQ_STRING, }, [IN_DQ_STRING] = { - [1 ... 0xBF] = IN_DQ_STRING, - [0xC2 ... 0xF4] = IN_DQ_STRING, + [0x20 ... 0xFD] = IN_DQ_STRING, ['\\'] = IN_DQ_STRING_ESCAPE, ['"'] = JSON_STRING, }, /* single quote string */ - [IN_SQ_UCODE3] = { - ['0' ... '9'] = IN_SQ_STRING, - ['a' ... 'f'] = IN_SQ_STRING, - ['A' ... 'F'] = IN_SQ_STRING, - }, - [IN_SQ_UCODE2] = { - ['0' ... '9'] = IN_SQ_UCODE3, - ['a' ... 'f'] = IN_SQ_UCODE3, - ['A' ... 'F'] = IN_SQ_UCODE3, - }, - [IN_SQ_UCODE1] = { - ['0' ... '9'] = IN_SQ_UCODE2, - ['a' ... 'f'] = IN_SQ_UCODE2, - ['A' ... 'F'] = IN_SQ_UCODE2, - }, - [IN_SQ_UCODE0] = { - ['0' ... '9'] = IN_SQ_UCODE1, - ['a' ... 'f'] = IN_SQ_UCODE1, - ['A' ... 'F'] = IN_SQ_UCODE1, - }, [IN_SQ_STRING_ESCAPE] = { - ['b'] = IN_SQ_STRING, - ['f'] = IN_SQ_STRING, - ['n'] = IN_SQ_STRING, - ['r'] = IN_SQ_STRING, - ['t'] = IN_SQ_STRING, - ['/'] = IN_SQ_STRING, - ['\\'] = IN_SQ_STRING, - ['\''] = IN_SQ_STRING, - ['\"'] = IN_SQ_STRING, - ['u'] = IN_SQ_UCODE0, + [0x20 ... 0xFD] = IN_SQ_STRING, }, [IN_SQ_STRING] = { - [1 ... 0xBF] = IN_SQ_STRING, - [0xC2 ... 0xF4] = IN_SQ_STRING, + [0x20 ... 0xFD] = IN_SQ_STRING, ['\\'] = IN_SQ_STRING_ESCAPE, ['\''] = JSON_STRING, }, @@ -164,24 +176,24 @@ static const uint8_t json_lexer[][256] = { /* Zero */ [IN_ZERO] = { TERMINAL(JSON_INTEGER), - ['0' ... '9'] = IN_ERROR, + ['0' ... '9'] = JSON_ERROR, ['.'] = IN_MANTISSA, }, /* Float */ - [IN_DIGITS] = { + [IN_EXP_DIGITS] = { TERMINAL(JSON_FLOAT), - ['0' ... '9'] = IN_DIGITS, + ['0' ... '9'] = IN_EXP_DIGITS, }, - [IN_DIGIT] = { - ['0' ... '9'] = IN_DIGITS, + [IN_EXP_SIGN] = { + ['0' ... '9'] = IN_EXP_DIGITS, }, [IN_EXP_E] = { - ['-'] = IN_DIGIT, - ['+'] = IN_DIGIT, - ['0' ... '9'] = IN_DIGITS, + ['-'] = IN_EXP_SIGN, + ['+'] = IN_EXP_SIGN, + ['0' ... '9'] = IN_EXP_DIGITS, }, [IN_MANTISSA_DIGITS] = { @@ -196,17 +208,17 @@ static const uint8_t json_lexer[][256] = { }, /* Number */ - [IN_NONZERO_NUMBER] = { + [IN_DIGITS] = { TERMINAL(JSON_INTEGER), - ['0' ... '9'] = IN_NONZERO_NUMBER, + ['0' ... '9'] = IN_DIGITS, ['e'] = IN_EXP_E, ['E'] = IN_EXP_E, ['.'] = IN_MANTISSA, }, - [IN_NEG_NONZERO_NUMBER] = { + [IN_SIGN] = { ['0'] = IN_ZERO, - ['1' ... '9'] = IN_NONZERO_NUMBER, + ['1' ... '9'] = IN_DIGITS, }, /* keywords */ @@ -215,58 +227,25 @@ static const uint8_t json_lexer[][256] = { ['a' ... 'z'] = IN_KEYWORD, }, - /* whitespace */ - [IN_WHITESPACE] = { - TERMINAL(JSON_SKIP), - [' '] = IN_WHITESPACE, - ['\t'] = IN_WHITESPACE, - ['\r'] = IN_WHITESPACE, - ['\n'] = IN_WHITESPACE, + /* interpolation */ + [IN_INTERP] = { + TERMINAL(JSON_INTERP), + ['A' ... 'Z'] = IN_INTERP, + ['a' ... 'z'] = IN_INTERP, + ['0' ... '9'] = IN_INTERP, }, - /* escape */ - [IN_ESCAPE_LL] = { - ['d'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_L] = { - ['d'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_LL, - ['u'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_I64] = { - ['d'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, - }, - - [IN_ESCAPE_I6] = { - ['4'] = IN_ESCAPE_I64, - }, - - [IN_ESCAPE_I] = { - ['6'] = IN_ESCAPE_I6, - }, - - [IN_ESCAPE] = { - ['d'] = JSON_ESCAPE, - ['i'] = JSON_ESCAPE, - ['p'] = JSON_ESCAPE, - ['s'] = JSON_ESCAPE, - ['u'] = JSON_ESCAPE, - ['f'] = JSON_ESCAPE, - ['l'] = IN_ESCAPE_L, - ['I'] = IN_ESCAPE_I, - }, - - /* top level rule */ - [IN_START] = { + /* + * Two start states: + * - IN_START recognizes JSON tokens with our string extensions + * - IN_START_INTERP additionally recognizes interpolation. + */ + [IN_START ... IN_START_INTERP] = { ['"'] = IN_DQ_STRING, ['\''] = IN_SQ_STRING, ['0'] = IN_ZERO, - ['1' ... '9'] = IN_NONZERO_NUMBER, - ['-'] = IN_NEG_NONZERO_NUMBER, + ['1' ... '9'] = IN_DIGITS, + ['-'] = IN_SIGN, ['{'] = JSON_LCURLY, ['}'] = JSON_RCURLY, ['['] = JSON_LSQUARE, @@ -274,25 +253,37 @@ static const uint8_t json_lexer[][256] = { [','] = JSON_COMMA, [':'] = JSON_COLON, ['a' ... 'z'] = IN_KEYWORD, - ['%'] = IN_ESCAPE, - [' '] = IN_WHITESPACE, - ['\t'] = IN_WHITESPACE, - ['\r'] = IN_WHITESPACE, - ['\n'] = IN_WHITESPACE, + [' '] = IN_START, + ['\t'] = IN_START, + ['\r'] = IN_START, + ['\n'] = IN_START, }, + [IN_START_INTERP]['%'] = IN_INTERP, }; -void json_lexer_init(JSONLexer *lexer, JSONLexerEmitter func) +static inline uint8_t next_state(JSONLexer *lexer, char ch, bool flush, + bool *char_consumed) { - lexer->emit = func; - lexer->state = IN_START; + uint8_t next; + + assert(lexer->state < ARRAY_SIZE(json_lexer)); + next = json_lexer[lexer->state][(uint8_t)ch]; + *char_consumed = !flush && !(next & LOOKAHEAD); + return next & ~LOOKAHEAD; +} + +void json_lexer_init(JSONLexer *lexer, bool enable_interpolation) +{ + lexer->start_state = lexer->state = enable_interpolation + ? IN_START_INTERP : IN_START; lexer->token = g_string_sized_new(3); lexer->x = lexer->y = 0; } -static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) +static void json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) { - int char_consumed, new_state; + int new_state; + bool char_consumed = false; lexer->x++; if (ch == '\n') { @@ -300,11 +291,10 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) lexer->y++; } - 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); + while (flush ? lexer->state != lexer->start_state : !char_consumed) { + new_state = next_state(lexer, ch, flush, &char_consumed); if (char_consumed) { + assert(!flush); g_string_append_c(lexer->token, ch); } @@ -315,73 +305,58 @@ static int json_lexer_feed_char(JSONLexer *lexer, char ch, bool flush) case JSON_RSQUARE: case JSON_COLON: case JSON_COMMA: - case JSON_ESCAPE: + case JSON_INTERP: case JSON_INTEGER: case JSON_FLOAT: case JSON_KEYWORD: case JSON_STRING: - lexer->emit(lexer, lexer->token, new_state, lexer->x, lexer->y); + json_message_process_token(lexer, lexer->token, new_state, + lexer->x, lexer->y); /* fall through */ - case JSON_SKIP: + case IN_START: g_string_truncate(lexer->token, 0); - new_state = IN_START; + new_state = lexer->start_state; break; - case IN_ERROR: - /* XXX: To avoid having previous bad input leaving the parser in an - * unresponsive state where we consume unpredictable amounts of - * subsequent "good" input, percolate this error state up to the - * tokenizer/parser by forcing a NULL object to be emitted, then - * reset state. - * - * Also note that this handling is required for reliable channel - * negotiation between QMP and the guest agent, since chr(0xFF) - * is placed at the beginning of certain events to ensure proper - * delivery when the channel is in an unknown state. chr(0xFF) is - * never a valid ASCII/UTF-8 sequence, so this should reliably - * induce an error/flush state. - */ - lexer->emit(lexer, lexer->token, JSON_ERROR, lexer->x, lexer->y); + case JSON_ERROR: + json_message_process_token(lexer, lexer->token, JSON_ERROR, + lexer->x, lexer->y); + new_state = IN_RECOVERY; + /* fall through */ + case IN_RECOVERY: g_string_truncate(lexer->token, 0); - new_state = IN_START; - lexer->state = new_state; - return 0; + break; default: break; } lexer->state = new_state; - } while (!char_consumed && !flush); + } /* Do not let a single token grow to an arbitrarily large size, * this is a security consideration. */ if (lexer->token->len > MAX_TOKEN_SIZE) { - lexer->emit(lexer, lexer->token, lexer->state, lexer->x, lexer->y); + json_message_process_token(lexer, lexer->token, lexer->state, + lexer->x, lexer->y); g_string_truncate(lexer->token, 0); - lexer->state = IN_START; + lexer->state = lexer->start_state; } - - return 0; } -int json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) +void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size) { size_t i; for (i = 0; i < size; i++) { - int err; - - err = json_lexer_feed_char(lexer, buffer[i], false); - if (err < 0) { - return err; - } + json_lexer_feed_char(lexer, buffer[i], false); } - - return 0; } -int json_lexer_flush(JSONLexer *lexer) +void json_lexer_flush(JSONLexer *lexer) { - return lexer->state == IN_START ? 0 : json_lexer_feed_char(lexer, 0, true); + json_lexer_feed_char(lexer, 0, true); + assert(lexer->state == lexer->start_state); + json_message_process_token(lexer, lexer->token, JSON_END_OF_INPUT, + lexer->x, lexer->y); } void json_lexer_destroy(JSONLexer *lexer) diff --git a/qobject/json-parser-int.h b/qobject/json-parser-int.h new file mode 100644 index 0000000000..16a25d00bb --- /dev/null +++ b/qobject/json-parser-int.h @@ -0,0 +1,54 @@ +/* + * JSON Parser + * + * Copyright IBM, Corp. 2009 + * + * Authors: + * Anthony Liguori + * + * 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 JSON_PARSER_INT_H +#define JSON_PARSER_INT_H + +#include "qapi/qmp/json-parser.h" + +typedef enum json_token_type { + JSON_ERROR = 0, /* must be zero, see json_lexer[] */ + /* Gap for lexer states */ + JSON_LCURLY = 100, + JSON_MIN = JSON_LCURLY, + JSON_RCURLY, + JSON_LSQUARE, + JSON_RSQUARE, + JSON_COLON, + JSON_COMMA, + JSON_INTEGER, + JSON_FLOAT, + JSON_KEYWORD, + JSON_STRING, + JSON_INTERP, + JSON_END_OF_INPUT, + JSON_MAX = JSON_END_OF_INPUT +} JSONTokenType; + +typedef struct JSONToken JSONToken; + +/* json-lexer.c */ +void json_lexer_init(JSONLexer *lexer, bool enable_interpolation); +void json_lexer_feed(JSONLexer *lexer, const char *buffer, size_t size); +void json_lexer_flush(JSONLexer *lexer); +void json_lexer_destroy(JSONLexer *lexer); + +/* json-streamer.c */ +void json_message_process_token(JSONLexer *lexer, GString *input, + JSONTokenType type, int x, int y); + +/* json-parser.c */ +JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr); +QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp); + +#endif diff --git a/qobject/json-parser.c b/qobject/json-parser.c index a5aa790d62..d8eb210c0c 100644 --- a/qobject/json-parser.c +++ b/qobject/json-parser.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" +#include "qemu/unicode.h" #include "qapi/error.h" #include "qemu-common.h" #include "qapi/qmp/qbool.h" @@ -21,15 +22,21 @@ #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" -#include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" +#include "json-parser-int.h" + +struct JSONToken { + JSONTokenType type; + int x; + int y; + char str[]; +}; typedef struct JSONParserContext { Error *err; JSONToken *current; GQueue *buf; + va_list *ap; } JSONParserContext; #define BUG_ON(cond) assert(!(cond)) @@ -43,7 +50,7 @@ typedef struct JSONParserContext * 4) deal with premature EOI */ -static QObject *parse_value(JSONParserContext *ctxt, va_list *ap); +static QObject *parse_value(JSONParserContext *ctxt); /** * Error handler @@ -53,169 +60,172 @@ static void GCC_FMT_ATTR(3, 4) parse_error(JSONParserContext *ctxt, { va_list ap; char message[1024]; + + if (ctxt->err) { + return; + } va_start(ap, msg); vsnprintf(message, sizeof(message), msg, ap); va_end(ap); - if (ctxt->err) { - error_free(ctxt->err); - ctxt->err = NULL; - } error_setg(&ctxt->err, "JSON parse error, %s", message); } -/** - * String helpers - * - * These helpers are used to unescape strings. - */ -static void wchar_to_utf8(uint16_t wchar, char *buffer, size_t buffer_length) +static int cvt4hex(const char *s) { - if (wchar <= 0x007F) { - BUG_ON(buffer_length < 2); + int cp, i; - buffer[0] = wchar & 0x7F; - buffer[1] = 0; - } else if (wchar <= 0x07FF) { - BUG_ON(buffer_length < 3); - - buffer[0] = 0xC0 | ((wchar >> 6) & 0x1F); - buffer[1] = 0x80 | (wchar & 0x3F); - buffer[2] = 0; - } else { - BUG_ON(buffer_length < 4); - - buffer[0] = 0xE0 | ((wchar >> 12) & 0x0F); - buffer[1] = 0x80 | ((wchar >> 6) & 0x3F); - buffer[2] = 0x80 | (wchar & 0x3F); - buffer[3] = 0; + cp = 0; + for (i = 0; i < 4; i++) { + if (!qemu_isxdigit(s[i])) { + return -1; + } + cp <<= 4; + if (s[i] >= '0' && s[i] <= '9') { + cp |= s[i] - '0'; + } else if (s[i] >= 'a' && s[i] <= 'f') { + cp |= 10 + s[i] - 'a'; + } else if (s[i] >= 'A' && s[i] <= 'F') { + cp |= 10 + s[i] - 'A'; + } else { + return -1; + } } -} - -static int hex2decimal(char ch) -{ - if (ch >= '0' && ch <= '9') { - return (ch - '0'); - } else if (ch >= 'a' && ch <= 'f') { - return 10 + (ch - 'a'); - } else if (ch >= 'A' && ch <= 'F') { - return 10 + (ch - 'A'); - } - - return -1; + return cp; } /** - * parse_string(): Parse a json string and return a QObject + * parse_string(): Parse a JSON string * - * string - * "" - * " chars " - * chars - * char - * char chars - * char - * any-Unicode-character- - * except-"-or-\-or- - * control-character - * \" - * \\ - * \/ - * \b - * \f - * \n - * \r - * \t - * \u four-hex-digits + * From RFC 8259 "The JavaScript Object Notation (JSON) Data + * Interchange Format": + * + * char = unescaped / + * escape ( + * %x22 / ; " quotation mark U+0022 + * %x5C / ; \ reverse solidus U+005C + * %x2F / ; / solidus U+002F + * %x62 / ; b backspace U+0008 + * %x66 / ; f form feed U+000C + * %x6E / ; n line feed U+000A + * %x72 / ; r carriage return U+000D + * %x74 / ; t tab U+0009 + * %x75 4HEXDIG ) ; uXXXX U+XXXX + * escape = %x5C ; \ + * quotation-mark = %x22 ; " + * unescaped = %x20-21 / %x23-5B / %x5D-10FFFF + * + * Extensions over RFC 8259: + * - Extra escape sequence in strings: + * 0x27 (apostrophe) is recognized after escape, too + * - Single-quoted strings: + * Like double-quoted strings, except they're delimited by %x27 + * (apostrophe) instead of %x22 (quotation mark), and can't contain + * unescaped apostrophe, but can contain unescaped quotation mark. + * + * Note: + * - Encoding is modified UTF-8. + * - Invalid Unicode characters are rejected. + * - Control characters \x00..\x1F are rejected by the lexer. */ -static QString *qstring_from_escaped_str(JSONParserContext *ctxt, - JSONToken *token) +static QString *parse_string(JSONParserContext *ctxt, JSONToken *token) { const char *ptr = token->str; QString *str; - int double_quote = 1; - - if (*ptr == '"') { - double_quote = 1; - } else { - double_quote = 0; - } - ptr++; + char quote; + const char *beg; + int cp, trailing; + char *end; + ssize_t len; + char utf8_buf[5]; + assert(*ptr == '"' || *ptr == '\''); + quote = *ptr++; str = qstring_new(); - while (*ptr && - ((double_quote && *ptr != '"') || (!double_quote && *ptr != '\''))) { - if (*ptr == '\\') { - ptr++; - switch (*ptr) { + while (*ptr != quote) { + assert(*ptr); + switch (*ptr) { + case '\\': + beg = ptr++; + switch (*ptr++) { case '"': - qstring_append(str, "\""); - ptr++; + qstring_append_chr(str, '"'); break; case '\'': - qstring_append(str, "'"); - ptr++; + qstring_append_chr(str, '\''); break; case '\\': - qstring_append(str, "\\"); - ptr++; + qstring_append_chr(str, '\\'); break; case '/': - qstring_append(str, "/"); - ptr++; + qstring_append_chr(str, '/'); break; case 'b': - qstring_append(str, "\b"); - ptr++; + qstring_append_chr(str, '\b'); break; case 'f': - qstring_append(str, "\f"); - ptr++; + qstring_append_chr(str, '\f'); break; case 'n': - qstring_append(str, "\n"); - ptr++; + qstring_append_chr(str, '\n'); break; case 'r': - qstring_append(str, "\r"); - ptr++; + qstring_append_chr(str, '\r'); break; case 't': - qstring_append(str, "\t"); - ptr++; + qstring_append_chr(str, '\t'); break; - case 'u': { - uint16_t unicode_char = 0; - char utf8_char[4]; - int i = 0; + case 'u': + cp = cvt4hex(ptr); + ptr += 4; - ptr++; - - for (i = 0; i < 4; i++) { - if (qemu_isxdigit(*ptr)) { - unicode_char |= hex2decimal(*ptr) << ((3 - i) * 4); + /* handle surrogate pairs */ + if (cp >= 0xD800 && cp <= 0xDBFF + && ptr[0] == '\\' && ptr[1] == 'u') { + /* leading surrogate followed by \u */ + cp = 0x10000 + ((cp & 0x3FF) << 10); + trailing = cvt4hex(ptr + 2); + if (trailing >= 0xDC00 && trailing <= 0xDFFF) { + /* followed by trailing surrogate */ + cp |= trailing & 0x3FF; + ptr += 6; } else { - parse_error(ctxt, token, - "invalid hex escape sequence in string"); - goto out; + cp = -1; /* invalid */ } - ptr++; } - wchar_to_utf8(unicode_char, utf8_char, sizeof(utf8_char)); - qstring_append(str, utf8_char); - } break; + if (mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp) < 0) { + parse_error(ctxt, token, + "%.*s is not a valid Unicode character", + (int)(ptr - beg), beg); + goto out; + } + qstring_append(str, utf8_buf); + break; default: parse_error(ctxt, token, "invalid escape sequence in string"); goto out; } - } else { - char dummy[2]; - - dummy[0] = *ptr++; - dummy[1] = 0; - - qstring_append(str, dummy); + break; + case '%': + if (ctxt->ap) { + if (ptr[1] != '%') { + parse_error(ctxt, token, "can't interpolate into string"); + goto out; + } + ptr++; + } + /* fall through */ + default: + cp = mod_utf8_codepoint(ptr, 6, &end); + if (cp < 0) { + parse_error(ctxt, token, "invalid UTF-8 sequence in string"); + goto out; + } + ptr = end; + len = mod_utf8_encode(utf8_buf, sizeof(utf8_buf), cp); + assert(len >= 0); + qstring_append(str, utf8_buf); } } @@ -233,48 +243,19 @@ out: static JSONToken *parser_context_pop_token(JSONParserContext *ctxt) { g_free(ctxt->current); - assert(!g_queue_is_empty(ctxt->buf)); ctxt->current = g_queue_pop_head(ctxt->buf); return ctxt->current; } static JSONToken *parser_context_peek_token(JSONParserContext *ctxt) { - assert(!g_queue_is_empty(ctxt->buf)); return g_queue_peek_head(ctxt->buf); } -static JSONParserContext *parser_context_new(GQueue *tokens) -{ - JSONParserContext *ctxt; - - if (!tokens) { - return NULL; - } - - ctxt = g_malloc0(sizeof(JSONParserContext)); - ctxt->buf = tokens; - - return ctxt; -} - -/* to support error propagation, ctxt->err must be freed separately */ -static void parser_context_free(JSONParserContext *ctxt) -{ - if (ctxt) { - while (!g_queue_is_empty(ctxt->buf)) { - parser_context_pop_token(ctxt); - } - g_free(ctxt->current); - g_queue_free(ctxt->buf); - g_free(ctxt); - } -} - /** * Parsing rules */ -static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) +static int parse_pair(JSONParserContext *ctxt, QDict *dict) { QObject *value; QString *key = NULL; @@ -286,7 +267,7 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) goto out; } - key = qobject_to(QString, parse_value(ctxt, ap)); + key = qobject_to(QString, parse_value(ctxt)); if (!key) { parse_error(ctxt, peek, "key is not a string in object"); goto out; @@ -303,12 +284,17 @@ static int parse_pair(JSONParserContext *ctxt, QDict *dict, va_list *ap) goto out; } - value = parse_value(ctxt, ap); + value = parse_value(ctxt); if (value == NULL) { parse_error(ctxt, token, "Missing value in dict"); goto out; } + if (qdict_haskey(dict, qstring_get_str(key))) { + parse_error(ctxt, token, "duplicate key"); + goto out; + } + qdict_put_obj(dict, qstring_get_str(key), value); qobject_unref(key); @@ -321,7 +307,7 @@ out: return -1; } -static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_object(JSONParserContext *ctxt) { QDict *dict = NULL; JSONToken *token, *peek; @@ -338,7 +324,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) } if (peek->type != JSON_RCURLY) { - if (parse_pair(ctxt, dict, ap) == -1) { + if (parse_pair(ctxt, dict) == -1) { goto out; } @@ -354,7 +340,7 @@ static QObject *parse_object(JSONParserContext *ctxt, va_list *ap) goto out; } - if (parse_pair(ctxt, dict, ap) == -1) { + if (parse_pair(ctxt, dict) == -1) { goto out; } @@ -375,7 +361,7 @@ out: return NULL; } -static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_array(JSONParserContext *ctxt) { QList *list = NULL; JSONToken *token, *peek; @@ -394,7 +380,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) if (peek->type != JSON_RSQUARE) { QObject *obj; - obj = parse_value(ctxt, ap); + obj = parse_value(ctxt); if (obj == NULL) { parse_error(ctxt, token, "expecting value"); goto out; @@ -414,7 +400,7 @@ static QObject *parse_array(JSONParserContext *ctxt, va_list *ap) goto out; } - obj = parse_value(ctxt, ap); + obj = parse_value(ctxt); if (obj == NULL) { parse_error(ctxt, token, "expecting value"); goto out; @@ -457,40 +443,39 @@ static QObject *parse_keyword(JSONParserContext *ctxt) return NULL; } -static QObject *parse_escape(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_interpolation(JSONParserContext *ctxt) { JSONToken *token; - if (ap == NULL) { - return NULL; - } - token = parser_context_pop_token(ctxt); - assert(token && token->type == JSON_ESCAPE); + assert(token && token->type == JSON_INTERP); if (!strcmp(token->str, "%p")) { - return va_arg(*ap, QObject *); + return va_arg(*ctxt->ap, QObject *); } else if (!strcmp(token->str, "%i")) { - return QOBJECT(qbool_from_bool(va_arg(*ap, int))); + return QOBJECT(qbool_from_bool(va_arg(*ctxt->ap, int))); } else if (!strcmp(token->str, "%d")) { - return QOBJECT(qnum_from_int(va_arg(*ap, int))); + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int))); } else if (!strcmp(token->str, "%ld")) { - return QOBJECT(qnum_from_int(va_arg(*ap, long))); - } else if (!strcmp(token->str, "%lld") || - !strcmp(token->str, "%I64d")) { - return QOBJECT(qnum_from_int(va_arg(*ap, long long))); + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long))); + } else if (!strcmp(token->str, "%lld")) { + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, long long))); + } else if (!strcmp(token->str, "%" PRId64)) { + return QOBJECT(qnum_from_int(va_arg(*ctxt->ap, int64_t))); } else if (!strcmp(token->str, "%u")) { - return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned int))); + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned int))); } else if (!strcmp(token->str, "%lu")) { - return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long))); - } else if (!strcmp(token->str, "%llu") || - !strcmp(token->str, "%I64u")) { - return QOBJECT(qnum_from_uint(va_arg(*ap, unsigned long long))); + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long))); + } else if (!strcmp(token->str, "%llu")) { + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, unsigned long long))); + } else if (!strcmp(token->str, "%" PRIu64)) { + return QOBJECT(qnum_from_uint(va_arg(*ctxt->ap, uint64_t))); } else if (!strcmp(token->str, "%s")) { - return QOBJECT(qstring_from_str(va_arg(*ap, const char *))); + return QOBJECT(qstring_from_str(va_arg(*ctxt->ap, const char *))); } else if (!strcmp(token->str, "%f")) { - return QOBJECT(qnum_from_double(va_arg(*ap, double))); + return QOBJECT(qnum_from_double(va_arg(*ctxt->ap, double))); } + parse_error(ctxt, token, "invalid interpolation '%s'", token->str); return NULL; } @@ -503,7 +488,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) switch (token->type) { case JSON_STRING: - return QOBJECT(qstring_from_escaped_str(ctxt, token)); + return QOBJECT(parse_string(ctxt, token)); case JSON_INTEGER: { /* * Represent JSON_INTEGER as QNUM_I64 if possible, else as @@ -538,7 +523,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) } case JSON_FLOAT: /* FIXME dependent on locale; a pervasive issue in QEMU */ - /* FIXME our lexer matches RFC 7159 in forbidding Inf or NaN, + /* FIXME our lexer matches RFC 8259 in forbidding Inf or NaN, * but those might be useful extensions beyond JSON */ return QOBJECT(qnum_from_double(strtod(token->str, NULL))); default: @@ -546,7 +531,7 @@ static QObject *parse_literal(JSONParserContext *ctxt) } } -static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) +static QObject *parse_value(JSONParserContext *ctxt) { JSONToken *token; @@ -558,11 +543,11 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) switch (token->type) { case JSON_LCURLY: - return parse_object(ctxt, ap); + return parse_object(ctxt); case JSON_LSQUARE: - return parse_array(ctxt, ap); - case JSON_ESCAPE: - return parse_escape(ctxt, ap); + return parse_array(ctxt); + case JSON_INTERP: + return parse_interpolation(ctxt); case JSON_INTEGER: case JSON_FLOAT: case JSON_STRING: @@ -575,25 +560,32 @@ static QObject *parse_value(JSONParserContext *ctxt, va_list *ap) } } -QObject *json_parser_parse(GQueue *tokens, va_list *ap) +JSONToken *json_token(JSONTokenType type, int x, int y, GString *tokstr) { - return json_parser_parse_err(tokens, ap, NULL); + JSONToken *token = g_malloc(sizeof(JSONToken) + tokstr->len + 1); + + token->type = type; + memcpy(token->str, tokstr->str, tokstr->len); + token->str[tokstr->len] = 0; + token->x = x; + token->y = y; + return token; } -QObject *json_parser_parse_err(GQueue *tokens, va_list *ap, Error **errp) +QObject *json_parser_parse(GQueue *tokens, va_list *ap, Error **errp) { - JSONParserContext *ctxt = parser_context_new(tokens); + JSONParserContext ctxt = { .buf = tokens, .ap = ap }; QObject *result; - if (!ctxt) { - return NULL; + result = parse_value(&ctxt); + assert(ctxt.err || g_queue_is_empty(ctxt.buf)); + + error_propagate(errp, ctxt.err); + + while (!g_queue_is_empty(ctxt.buf)) { + parser_context_pop_token(&ctxt); } - - result = parse_value(ctxt, ap); - - error_propagate(errp, ctxt->err); - - parser_context_free(ctxt); + g_free(ctxt.current); return result; } diff --git a/qobject/json-streamer.c b/qobject/json-streamer.c index c51c2021f9..47dd7ea576 100644 --- a/qobject/json-streamer.c +++ b/qobject/json-streamer.c @@ -12,34 +12,29 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" -#include "qapi/qmp/json-lexer.h" -#include "qapi/qmp/json-streamer.h" +#include "qapi/error.h" +#include "json-parser-int.h" #define MAX_TOKEN_SIZE (64ULL << 20) #define MAX_TOKEN_COUNT (2ULL << 20) -#define MAX_NESTING (1ULL << 10) - -static void json_message_free_token(void *token, void *opaque) -{ - g_free(token); -} +#define MAX_NESTING (1 << 10) static void json_message_free_tokens(JSONMessageParser *parser) { - if (parser->tokens) { - g_queue_foreach(parser->tokens, json_message_free_token, NULL); - g_queue_free(parser->tokens); - parser->tokens = NULL; + JSONToken *token; + + while ((token = g_queue_pop_head(&parser->tokens))) { + g_free(token); } } -static void json_message_process_token(JSONLexer *lexer, GString *input, - JSONTokenType type, int x, int y) +void json_message_process_token(JSONLexer *lexer, GString *input, + JSONTokenType type, int x, int y) { JSONMessageParser *parser = container_of(lexer, JSONMessageParser, lexer); + QObject *json = NULL; + Error *err = NULL; JSONToken *token; - GQueue *tokens; switch (type) { case JSON_LCURLY: @@ -54,79 +49,82 @@ static void json_message_process_token(JSONLexer *lexer, GString *input, case JSON_RSQUARE: parser->bracket_count--; break; + case JSON_ERROR: + error_setg(&err, "JSON parse error, stray '%s'", input->str); + goto out_emit; + case JSON_END_OF_INPUT: + if (g_queue_is_empty(&parser->tokens)) { + return; + } + json = json_parser_parse(&parser->tokens, parser->ap, &err); + goto out_emit; default: break; } - 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 += input->len; - - g_queue_push_tail(parser->tokens, token); - - if (type == JSON_ERROR) { - goto out_emit_bad; - } else if (parser->brace_count < 0 || - parser->bracket_count < 0 || - (parser->brace_count == 0 && - parser->bracket_count == 0)) { + /* + * Security consideration, we limit total memory allocated per object + * and the maximum recursion depth that a message can force. + */ + if (parser->token_size + input->len + 1 > MAX_TOKEN_SIZE) { + error_setg(&err, "JSON token size limit exceeded"); + goto out_emit; + } + if (g_queue_get_length(&parser->tokens) + 1 > MAX_TOKEN_COUNT) { + error_setg(&err, "JSON token count limit exceeded"); + goto out_emit; + } + if (parser->bracket_count + parser->brace_count > MAX_NESTING) { + error_setg(&err, "JSON nesting depth limit exceeded"); goto out_emit; - } else if (parser->token_size > MAX_TOKEN_SIZE || - 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_bad; } - return; + token = json_token(type, x, y, input); + parser->token_size += input->len; + + g_queue_push_tail(&parser->tokens, token); + + if ((parser->brace_count > 0 || parser->bracket_count > 0) + && parser->bracket_count >= 0 && parser->bracket_count >= 0) { + return; + } + + json = json_parser_parse(&parser->tokens, parser->ap, &err); -out_emit_bad: - /* - * Clear out token list and tell the parser to emit an error - * indication by passing it a NULL list - */ - 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. Remove our own - * reference to parser->tokens before handing it out to parser->emit. - */ - tokens = parser->tokens; - parser->tokens = g_queue_new(); - parser->emit(parser, tokens); + json_message_free_tokens(parser); parser->token_size = 0; + parser->emit(parser->opaque, json, err); } void json_message_parser_init(JSONMessageParser *parser, - void (*func)(JSONMessageParser *, GQueue *)) + void (*emit)(void *opaque, QObject *json, + Error *err), + void *opaque, va_list *ap) { - parser->emit = func; + parser->emit = emit; + parser->opaque = opaque; + parser->ap = ap; parser->brace_count = 0; parser->bracket_count = 0; - parser->tokens = g_queue_new(); + g_queue_init(&parser->tokens); parser->token_size = 0; - json_lexer_init(&parser->lexer, json_message_process_token); + json_lexer_init(&parser->lexer, !!ap); } -int json_message_parser_feed(JSONMessageParser *parser, +void json_message_parser_feed(JSONMessageParser *parser, const char *buffer, size_t size) { - return json_lexer_feed(&parser->lexer, buffer, size); + json_lexer_feed(&parser->lexer, buffer, size); } -int json_message_parser_flush(JSONMessageParser *parser) +void json_message_parser_flush(JSONMessageParser *parser) { - return json_lexer_flush(&parser->lexer); + json_lexer_flush(&parser->lexer); + assert(g_queue_is_empty(&parser->tokens)); } void json_message_parser_destroy(JSONMessageParser *parser) diff --git a/qobject/qbool.c b/qobject/qbool.c index b58249925c..06dfc43498 100644 --- a/qobject/qbool.c +++ b/qobject/qbool.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qbool.h" -#include "qemu-common.h" /** * qbool_from_bool(): Create a new QBool from a bool diff --git a/qobject/qjson.c b/qobject/qjson.c index 2f6a590e44..db36101f3b 100644 --- a/qobject/qjson.c +++ b/qobject/qjson.c @@ -13,9 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qmp/json-lexer.h" #include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qdict.h" @@ -27,29 +25,55 @@ typedef struct JSONParsingState { JSONMessageParser parser; - va_list *ap; QObject *result; Error *err; } JSONParsingState; -static void parse_json(JSONMessageParser *parser, GQueue *tokens) +static void consume_json(void *opaque, QObject *json, Error *err) { - JSONParsingState *s = container_of(parser, JSONParsingState, parser); + JSONParsingState *s = opaque; - s->result = json_parser_parse_err(tokens, s->ap, &s->err); + assert(!json != !err); + assert(!s->result || !s->err); + + if (s->result) { + qobject_unref(s->result); + s->result = NULL; + error_setg(&s->err, "Expecting at most one JSON value"); + } + if (s->err) { + qobject_unref(json); + error_free(err); + return; + } + s->result = json; + s->err = err; } -QObject *qobject_from_jsonv(const char *string, va_list *ap, Error **errp) +/* + * Parse @string as JSON value. + * If @ap is non-null, interpolate %-escapes. + * Takes ownership of %p arguments. + * On success, return the JSON value. + * On failure, store an error through @errp and return NULL. + * Ownership of %p arguments becomes indeterminate then. To avoid + * leaks, callers passing %p must terminate on error, e.g. by passing + * &error_abort. + */ +static QObject *qobject_from_jsonv(const char *string, va_list *ap, + Error **errp) { JSONParsingState state = {}; - state.ap = ap; - - json_message_parser_init(&state.parser, parse_json); + json_message_parser_init(&state.parser, consume_json, &state, ap); json_message_parser_feed(&state.parser, string, strlen(string)); json_message_parser_flush(&state.parser); json_message_parser_destroy(&state.parser); + if (!state.result && !state.err) { + error_setg(&state.err, "Expecting a JSON value"); + } + error_propagate(errp, state.err); return state.result; } @@ -59,18 +83,56 @@ QObject *qobject_from_json(const char *string, Error **errp) return qobject_from_jsonv(string, NULL, errp); } -QObject *qobject_from_jsonf(const char *string, ...) +/* + * Parse @string as JSON value with %-escapes interpolated. + * Abort on error. Do not use with untrusted @string. + * Return the resulting QObject. It is never null. + */ +QObject *qobject_from_vjsonf_nofail(const char *string, va_list ap) +{ + va_list ap_copy; + QObject *obj; + + /* va_copy() is needed when va_list is an array type */ + va_copy(ap_copy, ap); + obj = qobject_from_jsonv(string, &ap_copy, &error_abort); + va_end(ap_copy); + + assert(obj); + return obj; +} + +/* + * Parse @string as JSON value with %-escapes interpolated. + * Abort on error. Do not use with untrusted @string. + * Return the resulting QObject. It is never null. + */ +QObject *qobject_from_jsonf_nofail(const char *string, ...) { QObject *obj; va_list ap; va_start(ap, string); - obj = qobject_from_jsonv(string, &ap, &error_abort); + obj = qobject_from_vjsonf_nofail(string, ap); va_end(ap); return obj; } +/* + * Parse @string as JSON object with %-escapes interpolated. + * Abort on error. Do not use with untrusted @string. + * Return the resulting QDict. It is never null. + */ +QDict *qdict_from_vjsonf_nofail(const char *string, va_list ap) +{ + QDict *qdict; + + qdict = qobject_to(QDict, qobject_from_vjsonf_nofail(string, ap)); + assert(qdict); + return qdict; +} + /* * Parse @string as JSON object with %-escapes interpolated. * Abort on error. Do not use with untrusted @string. @@ -78,15 +140,13 @@ QObject *qobject_from_jsonf(const char *string, ...) */ QDict *qdict_from_jsonf_nofail(const char *string, ...) { - QDict *obj; + QDict *qdict; va_list ap; va_start(ap, string); - obj = qobject_to(QDict, qobject_from_jsonv(string, &ap, &error_abort)); + qdict = qdict_from_vjsonf_nofail(string, ap); va_end(ap); - - assert(obj); - return obj; + return qdict; } typedef struct ToJsonIterState diff --git a/qobject/qlist.c b/qobject/qlist.c index 37c1c167f1..b3274af88b 100644 --- a/qobject/qlist.c +++ b/qobject/qlist.c @@ -17,7 +17,6 @@ #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" #include "qemu/queue.h" -#include "qemu-common.h" /** * qlist_new(): Create a new QList diff --git a/qobject/qnull.c b/qobject/qnull.c index f6f55f11ea..00870a1824 100644 --- a/qobject/qnull.c +++ b/qobject/qnull.c @@ -11,7 +11,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qnull.h" QNull qnull_ = { diff --git a/qobject/qnum.c b/qobject/qnum.c index 1501c82832..7012fc57f2 100644 --- a/qobject/qnum.c +++ b/qobject/qnum.c @@ -14,7 +14,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qnum.h" -#include "qemu-common.h" /** * qnum_from_int(): Create a new QNum from an int64_t diff --git a/qobject/qobject.c b/qobject/qobject.c index cf4b7e229e..878dd76e79 100644 --- a/qobject/qobject.c +++ b/qobject/qobject.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "qemu-common.h" #include "qapi/qmp/qbool.h" #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" diff --git a/qobject/qstring.c b/qobject/qstring.c index 0f1510e792..1c6897df00 100644 --- a/qobject/qstring.c +++ b/qobject/qstring.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qapi/qmp/qstring.h" -#include "qemu-common.h" /** * qstring_new(): Create a new empty QString diff --git a/qom/cpu.c b/qom/cpu.c index 92599f3541..a8d2958956 100644 --- a/qom/cpu.c +++ b/qom/cpu.c @@ -194,7 +194,6 @@ static bool cpu_common_debug_check_watchpoint(CPUState *cpu, CPUWatchpoint *wp) return true; } -bool target_words_bigendian(void); static bool cpu_common_virtio_is_big_endian(CPUState *cpu) { return target_words_bigendian(); @@ -266,7 +265,7 @@ static void cpu_common_reset(CPUState *cpu) cpu->mem_io_pc = 0; cpu->mem_io_vaddr = 0; cpu->icount_extra = 0; - cpu->icount_decr.u32 = 0; + atomic_set(&cpu->icount_decr.u32, 0); cpu->can_do_io = 1; cpu->exception_index = -1; cpu->crash_occurred = false; @@ -313,7 +312,6 @@ static void cpu_common_parse_features(const char *typename, char *features, prop->driver = typename; prop->property = g_strdup(featurestr); prop->value = g_strdup(val); - prop->errp = &error_fatal; qdev_prop_register_global(prop); } else { error_setg(errp, "Expected key=value format, found %s.", @@ -366,6 +364,7 @@ static void cpu_common_initfn(Object *obj) CPUClass *cc = CPU_GET_CLASS(obj); cpu->cpu_index = UNASSIGNED_CPU_INDEX; + cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; cpu->gdb_num_regs = cpu->gdb_num_g_regs = cc->gdb_num_core_regs; /* *-user doesn't have configurable SMP topology */ /* the default value is changed by qemu_init_vcpu() for softmmu */ @@ -381,6 +380,9 @@ static void cpu_common_initfn(Object *obj) static void cpu_common_finalize(Object *obj) { + CPUState *cpu = CPU(obj); + + qemu_mutex_destroy(&cpu->work_mutex); } static int64_t cpu_common_get_arch_id(CPUState *cpu) diff --git a/qom/object.c b/qom/object.c index 75d1d48944..e3206d6799 100644 --- a/qom/object.c +++ b/qom/object.c @@ -49,7 +49,6 @@ struct TypeImpl void (*class_init)(ObjectClass *klass, void *data); void (*class_base_init)(ObjectClass *klass, void *data); - void (*class_finalize)(ObjectClass *klass, void *data); void *class_data; @@ -114,7 +113,6 @@ static TypeImpl *type_new(const TypeInfo *info) ti->class_init = info->class_init; ti->class_base_init = info->class_base_init; - ti->class_finalize = info->class_finalize; ti->class_data = info->class_data; ti->instance_init = info->instance_init; @@ -286,7 +284,14 @@ static void type_initialize(TypeImpl *ti) if (ti->instance_size == 0) { ti->abstract = true; } - + if (type_is_ancestor(ti, type_interface)) { + assert(ti->instance_size == 0); + assert(ti->abstract); + assert(!ti->instance_init); + assert(!ti->instance_post_init); + assert(!ti->instance_finalize); + assert(!ti->num_interfaces); + } ti->class = g_malloc0(ti->class_size); parent = type_get_parent(ti); @@ -365,6 +370,83 @@ static void object_post_init_with_type(Object *obj, TypeImpl *ti) } } +void object_apply_global_props(Object *obj, const GPtrArray *props, Error **errp) +{ + int i; + + if (!props) { + return; + } + + for (i = 0; i < props->len; i++) { + GlobalProperty *p = g_ptr_array_index(props, i); + Error *err = NULL; + + if (object_dynamic_cast(obj, p->driver) == NULL) { + continue; + } + if (p->optional && !object_property_find(obj, p->property, NULL)) { + continue; + } + p->used = true; + object_property_parse(obj, p->value, p->property, &err); + if (err != NULL) { + error_prepend(&err, "can't apply global %s.%s=%s: ", + p->driver, p->property, p->value); + /* + * If errp != NULL, propagate error and return. + * If errp == NULL, report a warning, but keep going + * with the remaining globals. + */ + if (errp) { + error_propagate(errp, err); + return; + } else { + warn_report_err(err); + } + } + } +} + +/* + * Global property defaults + * Slot 0: accelerator's global property defaults + * Slot 1: machine's global property defaults + * Each is a GPtrArray of of GlobalProperty. + * Applied in order, later entries override earlier ones. + */ +static GPtrArray *object_compat_props[2]; + +/* + * Set machine's global property defaults to @compat_props. + * May be called at most once. + */ +void object_set_machine_compat_props(GPtrArray *compat_props) +{ + assert(!object_compat_props[1]); + object_compat_props[1] = compat_props; +} + +/* + * Set accelerator's global property defaults to @compat_props. + * May be called at most once. + */ +void object_set_accelerator_compat_props(GPtrArray *compat_props) +{ + assert(!object_compat_props[0]); + object_compat_props[0] = compat_props; +} + +void object_apply_compat_props(Object *obj) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(object_compat_props); i++) { + object_apply_global_props(obj, object_compat_props[i], + &error_abort); + } +} + static void object_initialize_with_type(void *data, size_t size, TypeImpl *type) { Object *obj = data; @@ -410,6 +492,7 @@ void object_initialize_childv(Object *parentobj, const char *propname, { Error *local_err = NULL; Object *obj; + UserCreatable *uc; object_initialize(childobj, size, type); obj = OBJECT(childobj); @@ -424,8 +507,9 @@ void object_initialize_childv(Object *parentobj, const char *propname, goto out; } - if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) { - user_creatable_complete(obj, &local_err); + uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); + if (uc) { + user_creatable_complete(uc, &local_err); if (local_err) { object_unparent(obj); goto out; @@ -583,6 +667,7 @@ Object *object_new_with_propv(const char *typename, Object *obj; ObjectClass *klass; Error *local_err = NULL; + UserCreatable *uc; klass = object_class_by_name(typename); if (!klass) { @@ -600,15 +685,20 @@ Object *object_new_with_propv(const char *typename, goto error; } - object_property_add_child(parent, id, obj, &local_err); - if (local_err) { - goto error; + if (id != NULL) { + object_property_add_child(parent, id, obj, &local_err); + if (local_err) { + goto error; + } } - if (object_dynamic_cast(obj, TYPE_USER_CREATABLE)) { - user_creatable_complete(obj, &local_err); + uc = (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); + if (uc) { + user_creatable_complete(uc, &local_err); if (local_err) { - object_unparent(obj); + if (id != NULL) { + object_unparent(obj); + } goto error; } } @@ -1108,7 +1198,7 @@ void object_class_property_iter_init(ObjectPropertyIterator *iter, ObjectClass *klass) { g_hash_table_iter_init(&iter->iter, klass->properties); - iter->nextclass = klass; + iter->nextclass = object_class_get_parent(klass); } ObjectProperty *object_class_property_find(ObjectClass *klass, const char *name, @@ -2423,9 +2513,10 @@ void object_class_property_set_description(ObjectClass *klass, op->description = g_strdup(description); } -static void object_instance_init(Object *obj) +static void object_class_init(ObjectClass *klass, void *data) { - object_property_add_str(obj, "type", qdev_get_type, NULL, NULL); + object_class_property_add_str(klass, "type", qdev_get_type, + NULL, &error_abort); } static void register_types(void) @@ -2439,7 +2530,7 @@ static void register_types(void) static TypeInfo object_info = { .name = TYPE_OBJECT, .instance_size = sizeof(Object), - .instance_init = object_instance_init, + .class_init = object_class_init, .abstract = true, }; diff --git a/qom/object_interfaces.c b/qom/object_interfaces.c index 72b97a8bed..cb5809934a 100644 --- a/qom/object_interfaces.c +++ b/qom/object_interfaces.c @@ -8,18 +8,10 @@ #include "qapi/opts-visitor.h" #include "qemu/config-file.h" -void user_creatable_complete(Object *obj, Error **errp) +void user_creatable_complete(UserCreatable *uc, Error **errp) { + UserCreatableClass *ucc = USER_CREATABLE_GET_CLASS(uc); - UserCreatableClass *ucc; - UserCreatable *uc = - (UserCreatable *)object_dynamic_cast(obj, TYPE_USER_CREATABLE); - - if (!uc) { - return; - } - - ucc = USER_CREATABLE_GET_CLASS(uc); if (ucc->complete) { ucc->complete(uc, errp); } @@ -83,16 +75,20 @@ Object *user_creatable_add_type(const char *type, const char *id, goto out; } - object_property_add_child(object_get_objects_root(), - id, obj, &local_err); - if (local_err) { - goto out; + if (id != NULL) { + object_property_add_child(object_get_objects_root(), + id, obj, &local_err); + if (local_err) { + goto out; + } } - user_creatable_complete(obj, &local_err); + user_creatable_complete(USER_CREATABLE(obj), &local_err); if (local_err) { - object_property_del(object_get_objects_root(), - id, &error_abort); + if (id != NULL) { + object_property_del(object_get_objects_root(), + id, &error_abort); + } goto out; } out: @@ -141,20 +137,18 @@ Object *user_creatable_add_opts(QemuOpts *opts, Error **errp) int user_creatable_add_opts_foreach(void *opaque, QemuOpts *opts, Error **errp) { - bool (*type_predicate)(const char *) = opaque; + bool (*type_opt_predicate)(const char *, QemuOpts *) = opaque; Object *obj = NULL; - Error *err = NULL; const char *type; type = qemu_opt_get(opts, "qom-type"); - if (type && type_predicate && - !type_predicate(type)) { + if (type && type_opt_predicate && + !type_opt_predicate(type, opts)) { return 0; } - obj = user_creatable_add_opts(opts, &err); + obj = user_creatable_add_opts(opts, errp); if (!obj) { - error_report_err(err); return -1; } object_unref(obj); diff --git a/qom/trace-events b/qom/trace-events index 5d86fbf019..945205bd10 100644 --- a/qom/trace-events +++ b/qom/trace-events @@ -1,5 +1,5 @@ # See docs/devel/tracing.txt for syntax documentation. -# qom/object.c +# 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)" object_class_dynamic_cast_assert(const char *type, const char *target, const char *file, int line, const char *func) "%s->%s (%s:%d:%s)" diff --git a/qtest.c b/qtest.c index 69b9e9962b..527141785f 100644 --- a/qtest.c +++ b/qtest.c @@ -164,6 +164,17 @@ static bool qtest_opened; * where NUM is an IRQ number. For the PC, interrupts can be intercepted * simply with "irq_intercept_in ioapic" (note that IRQ0 comes out with * NUM=0 even though it is remapped to GSI 2). + * + * Setting interrupt level: + * + * > set_irq_in QOM-PATH NAME NUM LEVEL + * < OK + * + * where NAME is the name of the irq/gpio list, NUM is an IRQ number and + * LEVEL is an signed integer IRQ level. + * + * Forcibly set the given interrupt pin to the given level. + * */ static int hex2nib(char ch) @@ -290,7 +301,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) if (!dev) { qtest_send_prefix(chr); qtest_send(chr, "FAIL Unknown device\n"); - return; + return; } if (irq_intercept_dev) { @@ -300,7 +311,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) } else { qtest_send(chr, "OK\n"); } - return; + return; } QLIST_FOREACH(ngl, &dev->gpios, node) { @@ -326,7 +337,39 @@ static void qtest_process_command(CharBackend *chr, gchar **words) irq_intercept_dev = dev; qtest_send_prefix(chr); qtest_send(chr, "OK\n"); + } else if (strcmp(words[0], "set_irq_in") == 0) { + DeviceState *dev; + qemu_irq irq; + char *name; + int ret; + int num; + int level; + g_assert(words[1] && words[2] && words[3] && words[4]); + + dev = DEVICE(object_resolve_path(words[1], NULL)); + if (!dev) { + qtest_send_prefix(chr); + qtest_send(chr, "FAIL Unknown device\n"); + return; + } + + if (strcmp(words[2], "unnamed-gpio-in") == 0) { + name = NULL; + } else { + name = words[2]; + } + + ret = qemu_strtoi(words[3], NULL, 0, &num); + g_assert(!ret); + ret = qemu_strtoi(words[4], NULL, 0, &level); + g_assert(!ret); + + irq = qdev_get_gpio_in_named(dev, name, num); + + qemu_set_irq(irq, level); + qtest_send_prefix(chr); + qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "outb") == 0 || strcmp(words[0], "outw") == 0 || strcmp(words[0], "outl") == 0) { @@ -720,7 +763,7 @@ void qtest_init(const char *qtest_chrdev, const char *qtest_log, Error **errp) { Chardev *chr; - chr = qemu_chr_new("qtest", qtest_chrdev); + chr = qemu_chr_new("qtest", qtest_chrdev, NULL); if (chr == NULL) { error_setg(errp, "Failed to initialize device for qtest: \"%s\"", diff --git a/replay/replay-char.c b/replay/replay-char.c old mode 100755 new mode 100644 diff --git a/replay/replay-events.c b/replay/replay-events.c index 707de3867b..d9a2d495b9 100644 --- a/replay/replay-events.c +++ b/replay/replay-events.c @@ -94,18 +94,6 @@ void replay_disable_events(void) } } -void replay_clear_events(void) -{ - g_assert(replay_mutex_locked()); - - while (!QTAILQ_EMPTY(&events_list)) { - Event *event = QTAILQ_FIRST(&events_list); - QTAILQ_REMOVE(&events_list, event, events); - - g_free(event); - } -} - /*! Adds specified async event to the queue */ void replay_add_event(ReplayAsyncEventKind event_kind, void *opaque, @@ -202,6 +190,7 @@ void replay_save_events(int checkpoint) { g_assert(replay_mutex_locked()); g_assert(checkpoint != CHECKPOINT_CLOCK_WARP_START); + g_assert(checkpoint != CHECKPOINT_CLOCK_VIRTUAL); while (!QTAILQ_EMPTY(&events_list)) { Event *event = QTAILQ_FIRST(&events_list); replay_save_event(event, checkpoint); @@ -308,7 +297,7 @@ void replay_init_events(void) void replay_finish_events(void) { events_enabled = false; - replay_clear_events(); + replay_flush_events(); } bool replay_events_enabled(void) diff --git a/replay/replay-internal.c b/replay/replay-internal.c index b077cb5fd5..8f87e9b957 100644 --- a/replay/replay-internal.c +++ b/replay/replay-internal.c @@ -35,6 +35,12 @@ static void replay_write_error(void) } } +static void replay_read_error(void) +{ + error_report("error reading the replay data"); + exit(1); +} + void replay_put_byte(uint8_t byte) { if (replay_file) { @@ -83,7 +89,11 @@ uint8_t replay_get_byte(void) { uint8_t byte = 0; if (replay_file) { - byte = getc(replay_file); + int r = getc(replay_file); + if (r == EOF) { + replay_read_error(); + } + byte = r; } return byte; } @@ -126,7 +136,7 @@ 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"); + replay_read_error(); } } } @@ -137,7 +147,7 @@ void replay_get_array_alloc(uint8_t **buf, size_t *size) *size = replay_get_dword(); *buf = g_malloc(*size); if (fread(*buf, 1, *size, replay_file) != *size) { - error_report("replay read error"); + replay_read_error(); } } } @@ -217,20 +227,25 @@ void replay_mutex_unlock(void) } } +void replay_advance_current_step(uint64_t current_step) +{ + int diff = (int)(replay_get_current_step() - replay_state.current_step); + + /* Time can only go forward */ + assert(diff >= 0); + + if (diff > 0) { + replay_put_event(EVENT_INSTRUCTION); + replay_put_dword(diff); + replay_state.current_step += diff; + } +} + /*! Saves cached instructions. */ void replay_save_instructions(void) { if (replay_file && replay_mode == REPLAY_MODE_RECORD) { g_assert(replay_mutex_locked()); - int diff = (int)(replay_get_current_step() - replay_state.current_step); - - /* Time can only go forward */ - assert(diff >= 0); - - if (diff > 0) { - replay_put_event(EVENT_INSTRUCTION); - replay_put_dword(diff); - replay_state.current_step += diff; - } + replay_advance_current_step(replay_get_current_step()); } } diff --git a/replay/replay-internal.h b/replay/replay-internal.h index ac4b27b674..af6f4d55d4 100644 --- a/replay/replay-internal.h +++ b/replay/replay-internal.h @@ -122,6 +122,8 @@ void replay_finish_event(void); data_kind variable. */ void replay_fetch_data_kind(void); +/*! Advance replay_state.current_step to the specified value. */ +void replay_advance_current_step(uint64_t current_step); /*! Saves queued events (like instructions and sound). */ void replay_save_instructions(void); @@ -142,8 +144,6 @@ void replay_init_events(void); void replay_finish_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 */ diff --git a/replay/replay-snapshot.c b/replay/replay-snapshot.c index 2ab85cfc60..16bacc98bc 100644 --- a/replay/replay-snapshot.c +++ b/replay/replay-snapshot.c @@ -33,11 +33,18 @@ static int replay_pre_save(void *opaque) static int replay_post_load(void *opaque, int version_id) { ReplayState *state = opaque; - fseek(replay_file, state->file_offset, SEEK_SET); - qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last); - /* If this was a vmstate, saved in recording mode, - we need to initialize replay data fields. */ - replay_fetch_data_kind(); + if (replay_mode == REPLAY_MODE_PLAY) { + fseek(replay_file, state->file_offset, SEEK_SET); + qemu_clock_set_last(QEMU_CLOCK_HOST, state->host_clock_last); + /* If this was a vmstate, saved in recording mode, + we need to initialize replay data fields. */ + replay_fetch_data_kind(); + } else if (replay_mode == REPLAY_MODE_RECORD) { + /* This is only useful for loading the initial state. + Therefore reset all the counters. */ + state->instructions_count = 0; + state->block_request_id = 0; + } return 0; } diff --git a/replay/replay-time.c b/replay/replay-time.c index 6a7565ec8d..0df1693337 100644 --- a/replay/replay-time.c +++ b/replay/replay-time.c @@ -15,13 +15,15 @@ #include "replay-internal.h" #include "qemu/error-report.h" -int64_t replay_save_clock(ReplayClockKind kind, int64_t clock) +int64_t replay_save_clock(ReplayClockKind kind, int64_t clock, int64_t raw_icount) { - if (replay_file) { g_assert(replay_mutex_locked()); - replay_save_instructions(); + /* Due to the caller's locking requirements we get the icount from it + * instead of using replay_save_instructions(). + */ + replay_advance_current_step(raw_icount); replay_put_event(EVENT_CLOCK + kind); replay_put_qword(clock); } diff --git a/replay/replay.c b/replay/replay.c index 8228261401..8b172b2d1b 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -214,7 +214,14 @@ bool replay_checkpoint(ReplayCheckpoint checkpoint) /* This checkpoint belongs to several threads. Processing events from different threads is non-deterministic */ - if (checkpoint != CHECKPOINT_CLOCK_WARP_START) { + if (checkpoint != CHECKPOINT_CLOCK_WARP_START + /* FIXME: this is temporary fix, other checkpoints + may also be invoked from the different threads someday. + Asynchronous event processing should be refactored + to create additional replay event kind which is + nailed to the one of the threads and which processes + the event queue. */ + && checkpoint != CHECKPOINT_CLOCK_VIRTUAL) { replay_save_events(checkpoint); } res = true; @@ -224,6 +231,18 @@ out: return res; } +bool replay_has_checkpoint(void) +{ + bool res = false; + if (replay_mode == REPLAY_MODE_PLAY) { + g_assert(replay_mutex_locked()); + replay_account_executed_instructions(); + res = EVENT_CHECKPOINT <= replay_state.data_kind + && replay_state.data_kind <= EVENT_CHECKPOINT_LAST; + } + return res; +} + static void replay_enable(const char *fname, int mode) { const char *fmode = NULL; diff --git a/roms/Makefile b/roms/Makefile index f1ac85ae9b..1ff78b63bb 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -39,7 +39,7 @@ powerpc_cross_prefix := $(call find-cross-prefix,powerpc) x86_64_cross_prefix := $(call find-cross-prefix,x86_64) # tag our seabios builds -SEABIOS_EXTRAVERSION="-prebuilt.qemu-project.org" +SEABIOS_EXTRAVERSION="-prebuilt.qemu.org" # # EfiRom utility is shipped with edk2 / tianocore, in BaseTools/ @@ -47,10 +47,7 @@ SEABIOS_EXTRAVERSION="-prebuilt.qemu-project.org" # We need that to combine multiple images (legacy bios, # efi ia32, efi x64) into a single rom binary. # -# We try to find it in the path. You can also pass the location on -# the command line, i.e. "make EFIROM=/path/to/EfiRom efirom" -# -EFIROM ?= $(shell which EfiRom 2>/dev/null) +EDK2_EFIROM = edk2/BaseTools/Source/C/bin/EfiRom default: @echo "nothing is build by default" @@ -59,8 +56,7 @@ default: @echo " vgabios -- update vgabios binaries (seabios)" @echo " sgabios -- update sgabios binaries" @echo " pxerom -- update nic roms (bios only)" - @echo " efirom -- update nic roms (bios+efi, this needs" - @echo " the EfiRom utility from edk2 / tianocore)" + @echo " efirom -- update nic roms (bios+efi)" @echo " slof -- update slof.bin" @echo " skiboot -- update skiboot.lid" @echo " u-boot.e500 -- update u-boot.e500" @@ -83,12 +79,12 @@ build-seabios-config-%: config.% cp $< seabios/builds/$*/.config $(MAKE) -C seabios \ EXTRAVERSION=$(SEABIOS_EXTRAVERSION) \ - CROSS_COMPILE=$(x86_64_cross_prefix) \ + CROSS_PREFIX=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ oldnoconfig $(MAKE) -C seabios \ EXTRAVERSION=$(SEABIOS_EXTRAVERSION) \ - CROSS_COMPILE=$(x86_64_cross_prefix) \ + CROSS_PREFIX=$(x86_64_cross_prefix) \ KCONFIG_CONFIG=$(CURDIR)/seabios/builds/$*/.config \ OUT=$(CURDIR)/seabios/builds/$*/ all @@ -106,8 +102,8 @@ pxe-rom-%: build-pxe-roms efirom: $(patsubst %,efi-rom-%,$(pxerom_variants)) -efi-rom-%: build-pxe-roms build-efi-roms - $(EFIROM) -f "0x$(VID)" -i "0x$(DID)" -l 0x02 \ +efi-rom-%: build-pxe-roms build-efi-roms $(EDK2_EFIROM) + $(EDK2_EFIROM) -f "0x$(VID)" -i "0x$(DID)" -l 0x02 \ -b ipxe/src/bin/$(VID)$(DID).rom \ -ec ipxe/src/bin-i386-efi/$(VID)$(DID).efidrv \ -ec ipxe/src/bin-x86_64-efi/$(VID)$(DID).efidrv \ @@ -124,6 +120,21 @@ build-efi-roms: build-pxe-roms $(patsubst %,bin-i386-efi/%.efidrv,$(pxerom_targets)) \ $(patsubst %,bin-x86_64-efi/%.efidrv,$(pxerom_targets)) +# Build scripts can pass compiler/linker flags to the EDK2 build tools +# via the EDK2_BASETOOLS_OPTFLAGS (CPPFLAGS and CFLAGS) and +# EDK2_BASETOOLS_LDFLAGS (LDFLAGS) environment variables. +# +# Example: +# +# make -C roms \ +# EDK2_BASETOOLS_OPTFLAGS='...' \ +# EDK2_BASETOOLS_LDFLAGS='...' \ +# efirom +# +$(EDK2_EFIROM): + $(MAKE) -C edk2/BaseTools \ + EXTRA_OPTFLAGS='$(EDK2_BASETOOLS_OPTFLAGS)' \ + EXTRA_LDFLAGS='$(EDK2_BASETOOLS_LDFLAGS)' slof: $(MAKE) -C SLOF CROSS=$(powerpc64_cross_prefix) qemu @@ -150,6 +161,7 @@ clean: $(MAKE) -C sgabios clean rm -f sgabios/.depend $(MAKE) -C ipxe/src veryclean + $(MAKE) -C edk2/BaseTools clean $(MAKE) -C SLOF clean rm -rf u-boot/build.e500 $(MAKE) -C u-boot-sam460ex distclean diff --git a/roms/QemuMacDrivers b/roms/QemuMacDrivers index d4e7d7ac66..90c488d5f4 160000 --- a/roms/QemuMacDrivers +++ b/roms/QemuMacDrivers @@ -1 +1 @@ -Subproject commit d4e7d7ac663fcb55f1b93575445fcbca372f17a7 +Subproject commit 90c488d5f4a407342247b9ea869df1c2d9c8e266 diff --git a/roms/SLOF b/roms/SLOF index 7d37babcfa..a5b428e1c1 160000 --- a/roms/SLOF +++ b/roms/SLOF @@ -1 +1 @@ -Subproject commit 7d37babcfa48a6eb08e726a8d13b745cb2eebe1c +Subproject commit a5b428e1c1eae703bdd62a3f527223c291ee3fdc diff --git a/roms/config.seabios-128k b/roms/config.seabios-128k index 35b5a07d8f..a17502ca0f 100644 --- a/roms/config.seabios-128k +++ b/roms/config.seabios-128k @@ -2,7 +2,7 @@ # need to turn off features (xhci,uas) to make it fit into 128k CONFIG_QEMU=y CONFIG_ROM_SIZE=128 -CONFIG_ATA_DMA=y +CONFIG_ATA_DMA=n CONFIG_BOOTSPLASH=n CONFIG_XEN=n CONFIG_USB_OHCI=n diff --git a/roms/config.seabios-256k b/roms/config.seabios-256k index b14b614fcc..d1bcc9453c 100644 --- a/roms/config.seabios-256k +++ b/roms/config.seabios-256k @@ -1,4 +1,4 @@ # for qemu machine types 2.0 + newer CONFIG_QEMU=y CONFIG_ROM_SIZE=256 -CONFIG_ATA_DMA=y +CONFIG_ATA_DMA=n diff --git a/roms/edk2 b/roms/edk2 new file mode 160000 index 0000000000..8558838922 --- /dev/null +++ b/roms/edk2 @@ -0,0 +1 @@ +Subproject commit 85588389222a3636baf0f9ed8227f2434af4c3f9 diff --git a/roms/ipxe b/roms/ipxe index 0600d3ae94..de4565cbe7 160000 --- a/roms/ipxe +++ b/roms/ipxe @@ -1 +1 @@ -Subproject commit 0600d3ae94f93efd10fc6b3c7420a9557a3a1670 +Subproject commit de4565cbe76ea9f7913a01f331be3ee901bb6e17 diff --git a/roms/openbios b/roms/openbios index 8fe6f5f96f..3464681b2b 160000 --- a/roms/openbios +++ b/roms/openbios @@ -1 +1 @@ -Subproject commit 8fe6f5f96f6ca39f1f62200be7fa130e929f13f2 +Subproject commit 3464681b2b5983df80086a40179d324102347da3 diff --git a/roms/qemu-palcode b/roms/qemu-palcode index f3c7e44c70..bf0e136988 160000 --- a/roms/qemu-palcode +++ b/roms/qemu-palcode @@ -1 +1 @@ -Subproject commit f3c7e44c70254975df2a00af39701eafbac4d471 +Subproject commit bf0e13698872450164fa7040da36a95d2d4b326f diff --git a/roms/seabios b/roms/seabios index f9626ccb91..a5cab58e9a 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit f9626ccb91e771f990fbb2da92e427a399d7d918 +Subproject commit a5cab58e9a3fb6e168aba919c5669bea406573b4 diff --git a/roms/seabios-hppa b/roms/seabios-hppa index 1ef99a0157..0f4fe84658 160000 --- a/roms/seabios-hppa +++ b/roms/seabios-hppa @@ -1 +1 @@ -Subproject commit 1ef99a01572c2581c30e16e6fe69e9ea2ef92ce0 +Subproject commit 0f4fe84658165e96ce35870fd19fc634e182e77b diff --git a/roms/skiboot b/roms/skiboot index e0ee24c27a..261ca8e779 160000 --- a/roms/skiboot +++ b/roms/skiboot @@ -1 +1 @@ -Subproject commit e0ee24c27a172bcf482f6f2bc905e6211c134bcc +Subproject commit 261ca8e779e5138869a45f174caa49be6a274501 diff --git a/roms/u-boot b/roms/u-boot index d85ca029f2..d3689267f9 160000 --- a/roms/u-boot +++ b/roms/u-boot @@ -1 +1 @@ -Subproject commit d85ca029f257b53a96da6c2fb421e78a003a9943 +Subproject commit d3689267f92c5956e09cc7d1baa4700141662bff diff --git a/rules.mak b/rules.mak index bbb2667928..df45bcffb4 100644 --- a/rules.mak +++ b/rules.mak @@ -132,7 +132,9 @@ modules: # otherwise print the 'quiet' output in the format " NAME args to print" # NAME should be a short name of the command, 7 letters or fewer. # If called with only a single argument, will print nothing in quiet mode. -quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) +quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1 +quiet-@ = $(if $(V),,@) +quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3) # cc-option # Usage: CFLAGS+=$(call cc-option, -falign-functions=0, -malign-functions=0) @@ -142,7 +144,7 @@ cc-option = $(if $(shell $(CC) $1 $2 -S -o /dev/null -xc /dev/null \ cc-c-option = $(if $(shell $(CC) $1 $2 -c -o /dev/null -xc /dev/null \ >/dev/null 2>&1 && echo OK), $2, $3) -VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc +VPATH_SUFFIXES = %.c %.h %.S %.cc %.cpp %.m %.mak %.texi %.sh %.rc Kconfig% set-vpath = $(if $1,$(foreach PATTERN,$(VPATH_SUFFIXES),$(eval vpath $(PATTERN) $1))) # install-prog list, dir @@ -325,7 +327,7 @@ endef # ../water/ice.mo-libs = -licemaker # ../water/ice.mo-objs = ../water/ice1.o ../water/ice2.o # -# Note that 'hot' didn't include 'season/' in the input, so 'summer.o' is not +# Note that 'hot' didn't include 'water/' in the input, so 'steam.o' is not # included. # define unnest-vars diff --git a/scripts/analyse-locks-simpletrace.py b/scripts/analyse-locks-simpletrace.py index 30090bdfff..7d9b574300 100755 --- a/scripts/analyse-locks-simpletrace.py +++ b/scripts/analyse-locks-simpletrace.py @@ -7,7 +7,6 @@ # from __future__ import print_function -import os import simpletrace import argparse import numpy as np diff --git a/scripts/analyze-migration.py b/scripts/analyze-migration.py index 5c2010c917..e527eb168e 100755 --- a/scripts/analyze-migration.py +++ b/scripts/analyze-migration.py @@ -23,7 +23,6 @@ import json import os import argparse import collections -import pprint def mkdir_p(path): try: diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 4e63774f9a..d3a88f71e9 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -18,7 +18,7 @@ if test $# -lt 1; then error "Usage: $0 " fi -tar_file=`realpath "$1"` +tar_file=$(realpath "$1") list_file="${tar_file}.list" vroot_dir="${tar_file}.vroot" @@ -26,7 +26,7 @@ vroot_dir="${tar_file}.vroot" # independent of what the developer currently has initialized # in their checkout, because the build environment is completely # different to the host OS. -submodules="dtc ui/keycodemapdb" +submodules="dtc ui/keycodemapdb tests/fp/berkeley-softfloat-3 tests/fp/berkeley-testfloat-3" trap "status=$?; rm -rf \"$list_file\" \"$vroot_dir\"; exit \$status" 0 1 2 3 15 @@ -34,10 +34,17 @@ if git diff-index --quiet HEAD -- &>/dev/null then HEAD=HEAD else - HEAD=`git stash create` + HEAD=$(git stash create) fi git clone --shared . "$vroot_dir" test $? -ne 0 && error "failed to clone into '$vroot_dir'" +for sm in $submodules; do + if test -d "$sm/.git" + then + git clone --shared "$sm" "$vroot_dir/$sm" + test $? -ne 0 && error "failed to clone submodule $sm" + fi +done cd "$vroot_dir" test $? -ne 0 && error "failed to change into '$vroot_dir'" diff --git a/scripts/check-qerror.sh b/scripts/check-qerror.sh deleted file mode 100755 index af7fbd5249..0000000000 --- a/scripts/check-qerror.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# This script verifies that qerror definitions and table entries are -# alphabetically ordered. - -check_order() { - errmsg=$1 - shift - - # sort -C verifies order but does not print a message. sort -c does print a - # message. These options are both in POSIX. - if ! "$@" | sort -C; then - echo "$errmsg" - "$@" | sort -c - exit 1 - fi - return 0 -} - -check_order 'Definitions in qerror.h must be in alphabetical order:' \ - grep '^#define QERR_' qerror.h -check_order 'Entries in qerror.c:qerror_table must be in alphabetical order:' \ - sed -n '/^static.*qerror_table\[\]/,/^};/s/QERR_/&/gp' qerror.c diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 42e1c50dd8..88682cb0a9 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -7,6 +7,7 @@ use strict; use warnings; +use Term::ANSIColor qw(:constants); my $P = $0; $P =~ s@.*/@@g; @@ -26,6 +27,7 @@ my $tst_only; my $emacs = 0; my $terse = 0; my $file = undef; +my $color = "auto"; my $no_warnings = 0; my $summary = 1; my $mailback = 0; @@ -64,6 +66,8 @@ Options: is all off) --test-only=WORD report only warnings/errors containing WORD literally + --color[=WHEN] Use colors 'always', 'never', or only when output + is a terminal ('auto'). Default is 'auto'. -h, --help, --version display this help and exit When FILE is - read standard input. @@ -72,6 +76,14 @@ EOM exit($exitcode); } +# Perl's Getopt::Long allows options to take optional arguments after a space. +# Prevent --color by itself from consuming other arguments +foreach (@ARGV) { + if ($_ eq "--color" || $_ eq "-color") { + $_ = "--color=$color"; + } +} + GetOptions( 'q|quiet+' => \$quiet, 'tree!' => \$tree, @@ -89,6 +101,8 @@ GetOptions( 'debug=s' => \%debug, 'test-only=s' => \$tst_only, + 'color=s' => \$color, + 'no-color' => sub { $color = 'never'; }, 'h|help' => \$help, 'version' => \$help ) or help(1); @@ -144,6 +158,16 @@ if (!$chk_patch && !$chk_branch && !$file) { die "One of --file, --branch, --patch is required\n"; } +if ($color =~ /^always$/i) { + $color = 1; +} elsif ($color =~ /^never$/i) { + $color = 0; +} elsif ($color =~ /^auto$/i) { + $color = (-t STDOUT); +} else { + die "Invalid color mode: $color\n"; +} + my $dbg_values = 0; my $dbg_possible = 0; my $dbg_type = 0; @@ -202,7 +226,6 @@ our $Attribute = qr{ QEMU_NORETURN| QEMU_WARN_UNUSED_RESULT| QEMU_SENTINEL| - QEMU_ARTIFICIAL| QEMU_PACKED| GCC_FMT_ATTR }x; @@ -340,13 +363,18 @@ my @lines = (); my $vname; if ($chk_branch) { my @patches; + my %git_commits = (); my $HASH; - open($HASH, "-|", "git", "log", "--format=%H", $ARGV[0]) || - die "$P: git log --format=%H $ARGV[0] failed - $!\n"; + open($HASH, "-|", "git", "log", "--reverse", "--no-merges", "--format=%H %s", $ARGV[0]) || + die "$P: git log --reverse --no-merges --format='%H %s' $ARGV[0] failed - $!\n"; - while (<$HASH>) { - chomp; - push @patches, $_; + for my $line (<$HASH>) { + $line =~ /^([0-9a-fA-F]{40,40}) (.*)$/; + next if (!defined($1) || !defined($2)); + my $sha1 = $1; + my $subject = $2; + push(@patches, $sha1); + $git_commits{$sha1} = $subject; } close $HASH; @@ -354,21 +382,33 @@ if ($chk_branch) { die "$P: no revisions returned for revlist '$chk_branch'\n" unless @patches; + my $i = 1; + my $num_patches = @patches; for my $hash (@patches) { my $FILE; open($FILE, '-|', "git", "show", $hash) || die "$P: git show $hash - $!\n"; - $vname = $hash; while (<$FILE>) { chomp; push(@rawlines, $_); } close($FILE); + $vname = substr($hash, 0, 12) . ' (' . $git_commits{$hash} . ')'; + if ($num_patches > 1 && $quiet == 0) { + my $prefix = "$i/$num_patches"; + $prefix = BLUE . BOLD . $prefix . RESET if $color; + print "$prefix Checking commit $vname\n"; + $vname = "Patch $i/$num_patches"; + } else { + $vname = "Commit " . $vname; + } if (!process($hash)) { $exit = 1; + print "\n" if ($num_patches > 1 && $quiet == 0); } @rawlines = (); @lines = (); + $i++; } } else { for my $filename (@ARGV) { @@ -387,6 +427,7 @@ if ($chk_branch) { } else { $vname = $filename; } + print "Checking $filename...\n" if @ARGV > 1 && $quiet == 0; while (<$FILE>) { chomp; push(@rawlines, $_); @@ -1166,14 +1207,23 @@ sub possible { my $prefix = ''; sub report { - if (defined $tst_only && $_[0] !~ /\Q$tst_only\E/) { + my ($level, $msg) = @_; + if (defined $tst_only && $msg !~ /\Q$tst_only\E/) { return 0; } - my $line = $prefix . $_[0]; - $line = (split('\n', $line))[0] . "\n" if ($terse); + my $output = ''; + $output .= BOLD if $color; + $output .= $prefix; + $output .= RED if $color && $level eq 'ERROR'; + $output .= MAGENTA if $color && $level eq 'WARNING'; + $output .= $level . ':'; + $output .= RESET if $color; + $output .= ' ' . $msg . "\n"; - push(our @report, $line); + $output = (split('\n', $output))[0] . "\n" if ($terse); + + push(our @report, $output); return 1; } @@ -1181,13 +1231,13 @@ sub report_dump { our @report; } sub ERROR { - if (report("ERROR: $_[0]\n")) { + if (report("ERROR", $_[0])) { our $clean = 0; our $cnt_error++; } } sub WARN { - if (report("WARNING: $_[0]\n")) { + if (report("WARNING", $_[0])) { our $clean = 0; our $cnt_warn++; } @@ -1367,10 +1417,10 @@ sub process { # extract the filename as it passes if ($line =~ /^diff --git.*?(\S+)$/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; - $realfile =~ s@^([^/]*)/@@; + $realfile =~ s@^([^/]*)/@@ if (!$file); $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && @@ -1402,6 +1452,10 @@ sub process { $is_patch = 1; } + if ($line =~ /^Author: .*via Qemu-devel.*/) { + ERROR("Author email address is mangled by the mailing list\n" . $herecurr); + } + #check the patch for a signoff: if ($line =~ /^\s*signed-off-by:/i) { # This is a signoff, if ugly, so do not double report. @@ -1566,6 +1620,54 @@ sub process { # check we are in a valid C source file if not then ignore this hunk next if ($realfile !~ /\.(h|c|cpp)$/); +# Block comment styles + + # Block comments use /* on a line of its own + if ($rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline =~ m@^\+.*/\*\*?+[ \t]*[^ \t]@) { # /* or /** non-blank + WARN("Block comments use a leading /* on a separate line\n" . $herecurr); + } + +# Block comments use * on subsequent lines + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $prevrawline =~ /^\+.*?\/\*/ && #starting /* + $prevrawline !~ /\*\/[ \t]*$/ && #no trailing */ + $rawline =~ /^\+/ && #line is new + $rawline !~ /^\+[ \t]*\*/) { #no leading * + WARN("Block comments use * on subsequent lines\n" . $hereprev); + } + +# Block comments use */ on trailing lines + if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ && #trailing */ + $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ && #inline /*...*/ + $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ && #trailing **/ + $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) { #non blank */ + WARN("Block comments use a trailing */ on a separate line\n" . $herecurr); + } + +# Block comment * alignment + if ($prevline =~ /$;[ \t]*$/ && #ends in comment + $line =~ /^\+[ \t]*$;/ && #leading comment + $rawline =~ /^\+[ \t]*\*/ && #leading * + (($prevrawline =~ /^\+.*?\/\*/ && #leading /* + $prevrawline !~ /\*\/[ \t]*$/) || #no trailing */ + $prevrawline =~ /^\+[ \t]*\*/)) { #leading * + my $oldindent; + $prevrawline =~ m@^\+([ \t]*/?)\*@; + if (defined($1)) { + $oldindent = expand_tabs($1); + } else { + $prevrawline =~ m@^\+(.*/?)\*@; + $oldindent = expand_tabs($1); + } + $rawline =~ m@^\+([ \t]*)\*@; + my $newindent = $1; + $newindent = expand_tabs($newindent); + if (length($oldindent) ne length($newindent)) { + WARN("Block comments should align the * on each line\n" . $hereprev); + } + } + # Check for potential 'bare' types my ($stat, $cond, $line_nr_next, $remain_next, $off_next, $realline_next); @@ -1929,9 +2031,8 @@ sub process { my ($where, $prefix) = ($-[1], $1); if ($prefix !~ /$Type\s+$/ && ($where != 0 || $prefix !~ /^.\s+$/) && - $prefix !~ /{\s+$/ && $prefix !~ /\#\s*define[^(]*\([^)]*\)\s+$/ && - $prefix !~ /,\s+$/) { + $prefix !~ /[,{:]\s+$/) { ERROR("space prohibited before open square bracket '['\n" . $herecurr); } } @@ -2209,6 +2310,11 @@ sub process { } } + if ($line =~ /^.\s*(Q(?:S?LIST|SIMPLEQ|TAILQ)_HEAD)\s*\(\s*[^,]/ && + $line !~ /^.typedef/) { + ERROR("named $1 should be typedefed separately\n" . $herecurr); + } + # Need a space before open parenthesis after if, while etc if ($line=~/\b(if|while|for|switch)\(/) { ERROR("space required before the open parenthesis '('\n" . $herecurr); @@ -2749,7 +2855,8 @@ sub process { info_vreport| error_report| warn_report| - info_report}x; + info_report| + g_test_message}x; if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) { ERROR("Error messages should not contain newlines\n" . $herecurr); @@ -2813,30 +2920,31 @@ sub process { } } + if ($is_patch && $chk_signoff && $signoff == 0) { + ERROR("Missing Signed-off-by: line(s)\n"); + } + # If we have no input at all, then there is nothing to report on # so just keep quiet. if ($#rawlines == -1) { - exit(0); + return 1; } # In mailback mode only produce a report in the negative, for # things that appear to be patches. if ($mailback && ($clean == 1 || !$is_patch)) { - exit(0); + return 1; } # This is not a patch, and we are are in 'no-patch' mode so # just keep quiet. if (!$chk_patch && !$is_patch) { - exit(0); + return 1; } if (!$is_patch) { ERROR("Does not appear to be a unified-diff format patch\n"); } - if ($is_patch && $chk_signoff && $signoff == 0) { - ERROR("Missing Signed-off-by: line(s)\n"); - } print report_dump(); if ($summary && !($clean == 1 && $quiet == 1)) { diff --git a/scripts/cleanup-trace-events.pl b/scripts/cleanup-trace-events.pl index e93abc00da..d4f0e4cab5 100755 --- a/scripts/cleanup-trace-events.pl +++ b/scripts/cleanup-trace-events.pl @@ -13,6 +13,7 @@ use warnings; use strict; +use File::Basename; my $buf = ''; my %seen = (); @@ -23,12 +24,19 @@ sub out { %seen = (); } -while (<>) { - if (/^(disable )?([a-z_0-9]+)\(/) { - open GREP, '-|', 'git', 'grep', '-lw', "trace_$2" +$#ARGV == 0 or die "usage: $0 FILE"; +my $in = $ARGV[0]; +my $dir = dirname($in); +open(IN, $in) or die "open $in: $!"; +chdir($dir) or die "chdir $dir: $!"; + +while () { + if (/^(disable |(tcg) |vcpu )*([a-z_0-9]+)\(/i) { + my $pat = "trace_$3"; + $pat .= '_tcg' if (defined $2); + open GREP, '-|', 'git', 'grep', '-lw', '--max-depth', '1', $pat or die "run git grep: $!"; - my $fname; - while ($fname = ) { + while (my $fname = ) { chomp $fname; next if $seen{$fname} || $fname eq 'trace-events'; $seen{$fname} = 1; @@ -49,3 +57,4 @@ while (<>) { } out; +close(IN) or die "close $in: $!"; diff --git a/scripts/cocci-macro-file.h b/scripts/cocci-macro-file.h index 9f2e72e7e1..e485cdccae 100644 --- a/scripts/cocci-macro-file.h +++ b/scripts/cocci-macro-file.h @@ -23,7 +23,6 @@ #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 @@ -93,29 +92,19 @@ struct { \ /* * 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 */ \ +union name { \ + struct type *tqh_first; /* first element */ \ + QTailQLink tqh_circ; /* link for last element */ \ } #define QTAILQ_HEAD_INITIALIZER(head) \ - { NULL, &(head).tqh_first } + { .tqh_circ = { NULL, &(head).tqh_circ } } -#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 */ \ +union { \ + struct type *tqe_next; /* next element */ \ + QTailQLink tqe_circ; /* link for prev element */ \ } /* From glib */ diff --git a/scripts/coccinelle/inplace-byteswaps.cocci b/scripts/coccinelle/inplace-byteswaps.cocci new file mode 100644 index 0000000000..a869a90cbf --- /dev/null +++ b/scripts/coccinelle/inplace-byteswaps.cocci @@ -0,0 +1,65 @@ +// Replace uses of in-place byteswapping functions with calls to the +// equivalent not-in-place functions. This is necessary to avoid +// undefined behaviour if the expression being swapped is a field in a +// packed struct. + +@@ +expression E; +@@ +-be16_to_cpus(&E); ++E = be16_to_cpu(E); +@@ +expression E; +@@ +-be32_to_cpus(&E); ++E = be32_to_cpu(E); +@@ +expression E; +@@ +-be64_to_cpus(&E); ++E = be64_to_cpu(E); +@@ +expression E; +@@ +-cpu_to_be16s(&E); ++E = cpu_to_be16(E); +@@ +expression E; +@@ +-cpu_to_be32s(&E); ++E = cpu_to_be32(E); +@@ +expression E; +@@ +-cpu_to_be64s(&E); ++E = cpu_to_be64(E); +@@ +expression E; +@@ +-le16_to_cpus(&E); ++E = le16_to_cpu(E); +@@ +expression E; +@@ +-le32_to_cpus(&E); ++E = le32_to_cpu(E); +@@ +expression E; +@@ +-le64_to_cpus(&E); ++E = le64_to_cpu(E); +@@ +expression E; +@@ +-cpu_to_le16s(&E); ++E = cpu_to_le16(E); +@@ +expression E; +@@ +-cpu_to_le32s(&E); ++E = cpu_to_le32(E); +@@ +expression E; +@@ +-cpu_to_le64s(&E); ++E = cpu_to_le64(E); diff --git a/scripts/coccinelle/tcg_gen_extract.cocci b/scripts/coccinelle/tcg_gen_extract.cocci index 81e66a35ae..c10c863482 100644 --- a/scripts/coccinelle/tcg_gen_extract.cocci +++ b/scripts/coccinelle/tcg_gen_extract.cocci @@ -17,7 +17,7 @@ // --keep-comments --in-place \ // --use-gitgrep --dir target // -// $ docker run --rm -v `pwd`:`pwd` -w `pwd` philmd/coccinelle \ +// $ docker run --rm -v $PWD:$PWD -w $PWD philmd/coccinelle \ // --macro-file scripts/cocci-macro-file.h \ // --sp-file scripts/coccinelle/tcg_gen_extract.cocci \ // --keep-comments --in-place \ diff --git a/scripts/coccinelle/use-error_fatal.cocci b/scripts/coccinelle/use-error_fatal.cocci new file mode 100644 index 0000000000..10fff0aec4 --- /dev/null +++ b/scripts/coccinelle/use-error_fatal.cocci @@ -0,0 +1,20 @@ +@@ +type T; +identifier FUN, RET; +expression list ARGS; +expression ERR, EC, FAIL; +@@ +( +- T RET = FUN(ARGS, &ERR); ++ T RET = FUN(ARGS, &error_fatal); +| +- RET = FUN(ARGS, &ERR); ++ RET = FUN(ARGS, &error_fatal); +| +- FUN(ARGS, &ERR); ++ FUN(ARGS, &error_fatal); +) +- if (FAIL) { +- error_report_err(ERR); +- exit(EC); +- } diff --git a/scripts/decodetree.py b/scripts/decodetree.py index 277f9a9bba..aa790b596a 100755 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -17,144 +17,13 @@ # # Generate a decoding tree from a specification file. -# -# The tree is built from instruction "patterns". A pattern may represent -# a single architectural instruction or a group of same, depending on what -# is convenient for further processing. -# -# Each pattern has "fixedbits" & "fixedmask", the combination of which -# describes the condition under which the pattern is matched: -# -# (insn & fixedmask) == fixedbits -# -# Each pattern may have "fields", which are extracted from the insn and -# passed along to the translator. Examples of such are registers, -# immediates, and sub-opcodes. -# -# In support of patterns, one may declare fields, argument sets, and -# formats, each of which may be re-used to simplify further definitions. -# -# *** Field syntax: -# -# field_def := '%' identifier ( unnamed_field )+ ( !function=identifier )? -# unnamed_field := number ':' ( 's' ) number -# -# For unnamed_field, the first number is the least-significant bit position of -# the field and the second number is the length of the field. If the 's' is -# present, the field is considered signed. If multiple unnamed_fields are -# present, they are concatenated. In this way one can define disjoint fields. -# -# If !function is specified, the concatenated result is passed through the -# named function, taking and returning an integral value. -# -# FIXME: the fields of the structure into which this result will be stored -# is restricted to "int". Which means that we cannot expand 64-bit items. -# -# Field examples: -# -# %disp 0:s16 -- sextract(i, 0, 16) -# %imm9 16:6 10:3 -- extract(i, 16, 6) << 3 | extract(i, 10, 3) -# %disp12 0:s1 1:1 2:10 -- sextract(i, 0, 1) << 11 -# | extract(i, 1, 1) << 10 -# | extract(i, 2, 10) -# %shimm8 5:s8 13:1 !function=expand_shimm8 -# -- expand_shimm8(sextract(i, 5, 8) << 1 -# | extract(i, 13, 1)) -# -# *** Argument set syntax: -# -# args_def := '&' identifier ( args_elt )+ -# args_elt := identifier -# -# Each args_elt defines an argument within the argument set. -# Each argument set will be rendered as a C structure "arg_$name" -# with each of the fields being one of the member arguments. -# -# Argument set examples: -# -# ®3 ra rb rc -# &loadstore reg base offset -# -# *** Format syntax: -# -# fmt_def := '@' identifier ( fmt_elt )+ -# fmt_elt := fixedbit_elt | field_elt | field_ref | args_ref -# fixedbit_elt := [01.-]+ -# field_elt := identifier ':' 's'? number -# field_ref := '%' identifier | identifier '=' '%' identifier -# args_ref := '&' identifier -# -# Defining a format is a handy way to avoid replicating groups of fields -# across many instruction patterns. -# -# A fixedbit_elt describes a contiguous sequence of bits that must -# be 1, 0, [.-] for don't care. The difference between '.' and '-' -# is that '.' means that the bit will be covered with a field or a -# final [01] from the pattern, and '-' means that the bit is really -# ignored by the cpu and will not be specified. -# -# A field_elt describes a simple field only given a width; the position of -# the field is implied by its position with respect to other fixedbit_elt -# and field_elt. -# -# If any fixedbit_elt or field_elt appear then all bits must be defined. -# Padding with a fixedbit_elt of all '.' is an easy way to accomplish that. -# -# A field_ref incorporates a field by reference. This is the only way to -# add a complex field to a format. A field may be renamed in the process -# via assignment to another identifier. This is intended to allow the -# same argument set be used with disjoint named fields. -# -# A single args_ref may specify an argument set to use for the format. -# The set of fields in the format must be a subset of the arguments in -# the argument set. If an argument set is not specified, one will be -# inferred from the set of fields. -# -# It is recommended, but not required, that all field_ref and args_ref -# appear at the end of the line, not interleaving with fixedbit_elf or -# field_elt. -# -# Format examples: -# -# @opr ...... ra:5 rb:5 ... 0 ....... rc:5 -# @opi ...... ra:5 lit:8 1 ....... rc:5 -# -# *** Pattern syntax: -# -# pat_def := identifier ( pat_elt )+ -# pat_elt := fixedbit_elt | field_elt | field_ref -# | args_ref | fmt_ref | const_elt -# fmt_ref := '@' identifier -# const_elt := identifier '=' number -# -# The fixedbit_elt and field_elt specifiers are unchanged from formats. -# A pattern that does not specify a named format will have one inferred -# from a referenced argument set (if present) and the set of fields. -# -# A const_elt allows a argument to be set to a constant value. This may -# come in handy when fields overlap between patterns and one has to -# include the values in the fixedbit_elt instead. -# -# The decoder will call a translator function for each pattern matched. -# -# Pattern examples: -# -# addl_r 010000 ..... ..... .... 0000000 ..... @opr -# addl_i 010000 ..... ..... .... 0000000 ..... @opi -# -# which will, in part, invoke -# -# trans_addl_r(ctx, &arg_opr, insn) -# and -# trans_addl_i(ctx, &arg_opi, insn) +# See the syntax and semantics in docs/devel/decodetree.rst. # -import io import os import re import sys import getopt -import pdb insnwidth = 32 insnmask = 0xffffffff @@ -162,6 +31,7 @@ fields = {} arguments = {} formats = {} patterns = [] +allpatterns = [] translate_prefix = 'trans' translate_scope = 'static ' @@ -169,19 +39,20 @@ input_file = '' output_file = None output_fd = None insntype = 'uint32_t' +decode_function = 'decode' re_ident = '[a-zA-Z][a-zA-Z0-9_]*' -def error(lineno, *args): +def error_with_file(file, lineno, *args): """Print an error message from file:line and args and exit.""" global output_file global output_fd if lineno: - r = '{0}:{1}: error:'.format(input_file, lineno) + r = '{0}:{1}: error:'.format(file, lineno) elif input_file: - r = '{0}: error:'.format(input_file) + r = '{0}: error:'.format(file) else: r = 'error:' for a in args: @@ -193,6 +64,8 @@ def error(lineno, *args): os.remove(output_file) exit(1) +def error(lineno, *args): + error_with_file(input_file, lineno, args) def output(*args): global output_fd @@ -200,7 +73,7 @@ def output(*args): output_fd.write(a) -if sys.version_info >= (3, 0): +if sys.version_info >= (3, 4): re_fullmatch = re.fullmatch else: def re_fullmatch(pat, str): @@ -300,7 +173,7 @@ class Field: s = 's' else: s = '' - return str(pos) + ':' + s + str(len) + return str(self.pos) + ':' + s + str(self.len) def str_extract(self): if self.sign: @@ -394,8 +267,9 @@ class FunctionField: class Arguments: """Class representing the extracted fields of a format""" - def __init__(self, nm, flds): + def __init__(self, nm, flds, extern): self.name = nm + self.extern = extern self.fields = sorted(flds) def __str__(self): @@ -405,10 +279,11 @@ class Arguments: return 'arg_' + self.name def output_def(self): - output('typedef struct {\n') - for n in self.fields: - output(' int ', n, ';\n') - output('} ', self.struct_name(), ';\n\n') + if not self.extern: + output('typedef struct {\n') + for n in self.fields: + output(' int ', n, ';\n') + output('} ', self.struct_name(), ';\n\n') # end Arguments @@ -416,6 +291,7 @@ class General: """Common code between instruction formats and instruction patterns""" def __init__(self, name, lineno, base, fixb, fixm, udfm, fldm, flds): self.name = name + self.file = input_file self.lineno = lineno self.base = base self.fixedbits = fixb @@ -425,13 +301,7 @@ class General: self.fields = flds def __str__(self): - r = self.name - if self.base: - r = r + ' ' + self.base.name - else: - r = r + ' ' + str(self.fields) - r = r + ' ' + str_match_bits(self.fixedbits, self.fixedmask) - return r + return self.name + ' ' + str_match_bits(self.fixedbits, self.fixedmask) def str1(self, i): return str_indent(i) + self.__str__() @@ -442,7 +312,8 @@ class Format(General): """Class representing an instruction format""" def extract_name(self): - return 'extract_' + self.name + global decode_function + return decode_function + '_extract_' + self.name def output_extract(self): output('static void ', self.extract_name(), '(', @@ -462,23 +333,63 @@ class Pattern(General): output('typedef ', self.base.base.struct_name(), ' arg_', self.name, ';\n') output(translate_scope, 'bool ', translate_prefix, '_', self.name, - '(DisasContext *ctx, arg_', self.name, - ' *a, ', insntype, ' insn);\n') + '(DisasContext *ctx, arg_', self.name, ' *a);\n') def output_code(self, i, extracted, outerbits, outermask): global translate_prefix ind = str_indent(i) arg = self.base.base.name - output(ind, '/* line ', str(self.lineno), ' */\n') + output(ind, '/* ', self.file, ':', str(self.lineno), ' */\n') if not extracted: output(ind, self.base.extract_name(), '(&u.f_', arg, ', insn);\n') for n, f in self.fields.items(): output(ind, 'u.f_', arg, '.', n, ' = ', f.str_extract(), ';\n') - output(ind, 'return ', translate_prefix, '_', self.name, - '(ctx, &u.f_', arg, ', insn);\n') + output(ind, 'if (', translate_prefix, '_', self.name, + '(ctx, &u.f_', arg, ')) return true;\n') # end Pattern +class MultiPattern(General): + """Class representing an overlapping set of instruction patterns""" + + def __init__(self, lineno, pats, fixb, fixm, udfm): + self.file = input_file + self.lineno = lineno + self.pats = pats + self.base = None + self.fixedbits = fixb + self.fixedmask = fixm + self.undefmask = udfm + + def __str__(self): + r = "{" + for p in self.pats: + r = r + ' ' + str(p) + return r + "}" + + def output_decl(self): + for p in self.pats: + p.output_decl() + + def output_code(self, i, extracted, outerbits, outermask): + global translate_prefix + ind = str_indent(i) + for p in self.pats: + if outermask != p.fixedmask: + innermask = p.fixedmask & ~outermask + innerbits = p.fixedbits & ~outermask + output(ind, 'if ((insn & ', + '0x{0:08x}) == 0x{1:08x}'.format(innermask, innerbits), + ') {\n') + output(ind, ' /* ', + str_match_bits(p.fixedbits, p.fixedmask), ' */\n') + p.output_code(i + 4, extracted, p.fixedbits, p.fixedmask) + output(ind, '}\n') + else: + p.output_code(i, extracted, p.fixedbits, p.fixedmask) +#end MultiPattern + + def parse_field(lineno, name, toks): """Parse one instruction field from TOKS at LINENO""" global fields @@ -542,7 +453,11 @@ def parse_arguments(lineno, name, toks): global re_ident flds = [] + extern = False for t in toks: + if re_fullmatch('!extern', t): + extern = True + continue if not re_fullmatch(re_ident, t): error(lineno, 'invalid argument set token "{0}"'.format(t)) if t in flds: @@ -551,7 +466,7 @@ def parse_arguments(lineno, name, toks): if name in arguments: error(lineno, 'duplicate argument set', name) - arguments[name] = Arguments(name, flds) + arguments[name] = Arguments(name, flds, extern) # end parse_arguments @@ -575,13 +490,14 @@ def add_field_byname(lineno, flds, new_name, old_name): def infer_argument_set(flds): global arguments + global decode_function for arg in arguments.values(): if eq_fields_for_args(flds, arg.fields): return arg - name = str(len(arguments)) - arg = Arguments(name, flds.keys()) + name = decode_function + str(len(arguments)) + arg = Arguments(name, flds.keys(), False) arguments[name] = arg return arg @@ -589,6 +505,7 @@ def infer_argument_set(flds): def infer_format(arg, fieldmask, flds): global arguments global formats + global decode_function const_flds = {} var_flds = {} @@ -608,7 +525,7 @@ def infer_format(arg, fieldmask, flds): continue return (fmt, const_flds) - name = 'Fmt_' + str(len(formats)) + name = decode_function + '_Fmt_' + str(len(formats)) if not arg: arg = infer_argument_set(flds) @@ -625,6 +542,7 @@ def parse_generic(lineno, is_format, name, toks): global arguments global formats global patterns + global allpatterns global re_ident global insnwidth global insnmask @@ -672,7 +590,7 @@ def parse_generic(lineno, is_format, name, toks): continue # 'Foo=number' sets an argument field to a constant value - if re_fullmatch(re_ident + '=[0-9]+', t): + if re_fullmatch(re_ident + '=[+-]?[0-9]+', t): (fname, value) = t.split('=') value = int(value) flds = add_field(lineno, flds, fname, ConstField(value)) @@ -704,6 +622,8 @@ def parse_generic(lineno, is_format, name, toks): sign = True flen = flen[1:] shift = int(flen, 10) + if shift + width > insnwidth: + error(lineno, 'field {0} exceeds insnwidth'.format(fname)) f = Field(sign, insnwidth - width - shift, shift) flds = add_field(lineno, flds, fname, f) fixedbits <<= shift @@ -769,6 +689,7 @@ def parse_generic(lineno, is_format, name, toks): pat = Pattern(name, lineno, fmt, fixedbits, fixedmask, undefmask, fieldmask, flds) patterns.append(pat) + allpatterns.append(pat) # Validate the masks that we have assembled. if fieldmask & fixedmask: @@ -787,17 +708,66 @@ def parse_generic(lineno, is_format, name, toks): .format(allbits ^ insnmask)) # end parse_general +def build_multi_pattern(lineno, pats): + """Validate the Patterns going into a MultiPattern.""" + global patterns + global insnmask + + if len(pats) < 2: + error(lineno, 'less than two patterns within braces') + + fixedmask = insnmask + undefmask = insnmask + + # Collect fixed/undefmask for all of the children. + # Move the defining lineno back to that of the first child. + for p in pats: + fixedmask &= p.fixedmask + undefmask &= p.undefmask + if p.lineno < lineno: + lineno = p.lineno + + repeat = True + while repeat: + if fixedmask == 0: + error(lineno, 'no overlap in patterns within braces') + fixedbits = None + for p in pats: + thisbits = p.fixedbits & fixedmask + if fixedbits is None: + fixedbits = thisbits + elif fixedbits != thisbits: + fixedmask &= ~(fixedbits ^ thisbits) + break + else: + repeat = False + + mp = MultiPattern(lineno, pats, fixedbits, fixedmask, undefmask) + patterns.append(mp) +# end build_multi_pattern def parse_file(f): """Parse all of the patterns within a file""" + global patterns + # Read all of the lines of the file. Concatenate lines # ending in backslash; discard empty lines and comments. toks = [] lineno = 0 + nesting = 0 + saved_pats = [] + for line in f: lineno += 1 + # Expand and strip spaces, to find indent. + line = line.rstrip() + line = line.expandtabs() + len1 = len(line) + line = line.lstrip() + len2 = len(line) + # Discard comments end = line.find('#') if end >= 0: @@ -807,10 +777,18 @@ def parse_file(f): if len(toks) != 0: # Next line after continuation toks.extend(t) - elif len(t) == 0: - # Empty line - continue else: + # Allow completely blank lines. + if len1 == 0: + continue + indent = len1 - len2 + # Empty line due to comment. + if len(t) == 0: + # Indentation must be correct, even for comment lines. + if indent != nesting: + error(lineno, 'indentation ', indent, ' != ', nesting) + continue + start_lineno = lineno toks = t # Continuation? @@ -818,21 +796,47 @@ def parse_file(f): toks.pop() continue - if len(toks) < 2: - error(lineno, 'short line') - name = toks[0] del toks[0] + # End nesting? + if name == '}': + if nesting == 0: + error(start_lineno, 'mismatched close brace') + if len(toks) != 0: + error(start_lineno, 'extra tokens after close brace') + nesting -= 2 + if indent != nesting: + error(start_lineno, 'indentation ', indent, ' != ', nesting) + pats = patterns + patterns = saved_pats.pop() + build_multi_pattern(lineno, pats) + toks = [] + continue + + # Everything else should have current indentation. + if indent != nesting: + error(start_lineno, 'indentation ', indent, ' != ', nesting) + + # Start nesting? + if name == '{': + if len(toks) != 0: + error(start_lineno, 'extra tokens after open brace') + saved_pats.append(patterns) + patterns = [] + nesting += 2 + toks = [] + continue + # Determine the type of object needing to be parsed. if name[0] == '%': - parse_field(lineno, name[1:], toks) + parse_field(start_lineno, name[1:], toks) elif name[0] == '&': - parse_arguments(lineno, name[1:], toks) + parse_arguments(start_lineno, name[1:], toks) elif name[0] == '@': - parse_generic(lineno, True, name[1:], toks) + parse_generic(start_lineno, True, name[1:], toks) else: - parse_generic(lineno, False, name, toks) + parse_generic(start_lineno, False, name, toks) toks = [] # end parse_file @@ -897,22 +901,22 @@ class Tree: output(ind, ' /* ', str_match_bits(innerbits, innermask), ' */\n') s.output_code(i + 4, extracted, innerbits, innermask) + output(ind, ' return false;\n') output(ind, '}\n') - output(ind, 'return false;\n') # end Tree def build_tree(pats, outerbits, outermask): # Find the intersection of all remaining fixedmask. - innermask = ~outermask + innermask = ~outermask & insnmask for i in pats: innermask &= i.fixedmask if innermask == 0: - pnames = [] + text = 'overlapping patterns:' for p in pats: - pnames.append(p.name + ':' + str(p.lineno)) - error(pats[0].lineno, 'overlapping patterns:', pnames) + text += '\n' + p.file + ':' + str(p.lineno) + ': ' + str(p) + error_with_file(pats[0].file, pats[0].lineno, text) fullmask = outermask | innermask @@ -965,6 +969,7 @@ def main(): global arguments global formats global patterns + global allpatterns global translate_scope global translate_prefix global output_fd @@ -973,11 +978,12 @@ def main(): global insnwidth global insntype global insnmask + global decode_function - decode_function = 'decode' decode_scope = 'static ' - long_opts = ['decode=', 'translate=', 'output=', 'insnwidth='] + long_opts = ['decode=', 'translate=', 'output=', 'insnwidth=', + 'static-decode='] try: (opts, args) = getopt.getopt(sys.argv[1:], 'o:w:', long_opts) except getopt.GetoptError as err: @@ -988,6 +994,8 @@ def main(): elif o == '--decode': decode_function = a decode_scope = '' + elif o == '--static-decode': + decode_function = a elif o == '--translate': translate_prefix = a translate_scope = '' @@ -1003,10 +1011,11 @@ def main(): if len(args) < 1: error(0, 'missing input file') - input_file = args[0] - f = open(input_file, 'r') - parse_file(f) - f.close() + for filename in args: + input_file = filename + f = open(filename, 'r') + parse_file(f) + f.close() t = build_tree(patterns, 0, 0) prop_format(t) @@ -1025,7 +1034,7 @@ def main(): # Make sure that the argument sets are the same, and declare the # function only once. out_pats = {} - for i in patterns: + for i in allpatterns: if i.name in out_pats: p = out_pats[i.name] if i.base.base != p.base.base: @@ -1043,14 +1052,16 @@ def main(): '(DisasContext *ctx, ', insntype, ' insn)\n{\n') i4 = str_indent(4) - output(i4, 'union {\n') - for n in sorted(arguments.keys()): - f = arguments[n] - output(i4, i4, f.struct_name(), ' f_', f.name, ';\n') - output(i4, '} u;\n\n') - t.output_code(4, False, 0, 0) + if len(allpatterns) != 0: + output(i4, 'union {\n') + for n in sorted(arguments.keys()): + f = arguments[n] + output(i4, i4, f.struct_name(), ' f_', f.name, ';\n') + output(i4, '} u;\n\n') + t.output_code(4, False, 0, 0) + output(i4, 'return false;\n') output('}\n') if output_file: diff --git a/scripts/device-crash-test b/scripts/device-crash-test index e6c233e9bf..a6748910ad 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -25,8 +25,8 @@ check for crashes and unexpected errors. """ from __future__ import print_function -import sys import os +import sys import glob import logging import traceback @@ -35,7 +35,7 @@ import random import argparse from itertools import chain -sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'scripts')) +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) from qemu import QEMUMachine logger = logging.getLogger('device-crash-test') @@ -73,21 +73,6 @@ ERROR_WHITELIST = [ # devices that don't work out of the box because they require extra options to "-device DEV": # DEVICE | ERROR MESSAGE {'device':'.*-(i386|x86_64)-cpu', 'expected':True}, # CPU socket-id is not set - {'device':'ARM,bitband-memory', 'expected':True}, # source-memory property not set - {'device':'arm.cortex-a9-global-timer', 'expected':True}, # a9_gtimer_realize: num-cpu must be between 1 and 4 - {'device':'arm_mptimer', 'expected':True}, # num-cpu must be between 1 and 4 - {'device':'armv7m', 'expected':True}, # memory property was not set - {'device':'aspeed.scu', 'expected':True}, # Unknown silicon revision: 0x0 - {'device':'aspeed.sdmc', 'expected':True}, # Unknown silicon revision: 0x0 - {'device':'bcm2835-dma', 'expected':True}, # bcm2835_dma_realize: required dma-mr link not found: Property '.dma-mr' not found - {'device':'bcm2835-fb', 'expected':True}, # bcm2835_fb_realize: required vcram-base property not set - {'device':'bcm2835-mbox', 'expected':True}, # bcm2835_mbox_realize: required mbox-mr link not found: Property '.mbox-mr' not found - {'device':'bcm2835-peripherals', 'expected':True}, # bcm2835_peripherals_realize: required ram link not found: Property '.ram' not found - {'device':'bcm2835-property', 'expected':True}, # bcm2835_property_realize: required fb link not found: Property '.fb' not found - {'device':'bcm2835_gpio', 'expected':True}, # bcm2835_gpio_realize: required sdhci link not found: Property '.sdbus-sdhci' not found - {'device':'bcm2836', 'expected':True}, # bcm2836_realize: required ram link not found: Property '.ram' not found - {'device':'cfi.pflash01', 'expected':True}, # attribute "sector-length" not specified or zero. - {'device':'cfi.pflash02', 'expected':True}, # attribute "sector-length" not specified or zero. {'device':'icp', 'expected':True}, # icp_realize: required link 'xics' not found: Property '.xics' not found {'device':'ics', 'expected':True}, # ics_base_realize: required link 'xics' not found: Property '.xics' not found # "-device ide-cd" does work on more recent QEMU versions, so it doesn't have expected=True @@ -99,8 +84,6 @@ ERROR_WHITELIST = [ {'device':'isa-ipmi-bt', 'expected':True}, # IPMI device requires a bmc attribute to be set {'device':'isa-ipmi-kcs', 'expected':True}, # IPMI device requires a bmc attribute to be set {'device':'isa-parallel', 'expected':True}, # Can't create serial device, empty char device - {'device':'isa-serial', 'expected':True}, # Can't create serial device, empty char device - {'device':'ivshmem', 'expected':True}, # You must specify either 'shm' or 'chardev' {'device':'ivshmem-doorbell', 'expected':True}, # You must specify a 'chardev' {'device':'ivshmem-plain', 'expected':True}, # You must specify a 'memdev' {'device':'loader', 'expected':True}, # please include valid arguments @@ -110,10 +93,6 @@ ERROR_WHITELIST = [ {'device':'pc-dimm', 'expected':True}, # 'memdev' property is not set {'device':'pci-bridge', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0. {'device':'pci-bridge-seat', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0. - {'device':'pci-serial', 'expected':True}, # Can't create serial device, empty char device - {'device':'pci-serial-2x', 'expected':True}, # Can't create serial device, empty char device - {'device':'pci-serial-4x', 'expected':True}, # Can't create serial device, empty char device - {'device':'pxa2xx-dma', 'expected':True}, # channels value invalid {'device':'pxb', 'expected':True}, # Bridge chassis not specified. Each bridge is required to be assigned a unique chassis id > 0. {'device':'scsi-block', 'expected':True}, # drive property not set {'device':'scsi-disk', 'expected':True}, # drive property not set @@ -125,7 +104,7 @@ ERROR_WHITELIST = [ {'device':'tpm-tis', 'expected':True}, # tpm_tis: backend driver with id (null) could not be found {'device':'unimplemented-device', 'expected':True}, # property 'size' not specified or zero {'device':'usb-braille', 'expected':True}, # Property chardev is required - {'device':'usb-mtp', 'expected':True}, # x-root property must be configured + {'device':'usb-mtp', 'expected':True}, # rootdir property must be configured {'device':'usb-redir', 'expected':True}, # Parameter 'chardev' is missing {'device':'usb-serial', 'expected':True}, # Property chardev is required {'device':'usb-storage', 'expected':True}, # drive property not set @@ -150,7 +129,6 @@ ERROR_WHITELIST = [ {'device':'virtio-input-host-pci', 'expected':True}, # evdev property is required {'device':'xen-pvdevice', 'expected':True}, # Device ID invalid, it must always be supplied {'device':'vhost-vsock-ccw', 'expected':True}, # guest-cid property must be greater than 2 - {'device':'ALTR.timer', 'expected':True}, # "clock-frequency" property must be provided {'device':'zpci', 'expected':True}, # target must be defined {'device':'pnv-(occ|icp|lpc)', 'expected':True}, # required link 'xics' not found: Property '.xics' not found {'device':'powernv-cpu-.*', 'expected':True}, # pnv_core_realize: required link 'xics' not found: Property '.xics' not found @@ -204,22 +182,6 @@ ERROR_WHITELIST = [ # other exitcode=1 failures not listed above will just generate INFO messages: {'exitcode':1, 'loglevel':logging.INFO}, - # KNOWN CRASHES: - # Known crashes will generate error messages, but won't be fatal. - # Those entries must be removed once we fix the crashes. - {'exitcode':-6, 'log':r"Device 'serial0' is in use", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"qemu_net_client_setup: Assertion `!peer->peer' failed", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r'RAMBlock "[\w.-]+" already registered', 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"find_ram_offset: Assertion `size != 0' failed.", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"add_cpreg_to_hashtable: code should not be reached", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"qemu_alloc_display: Assertion `surface->image != NULL' failed", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"Unexpected error in error_set_from_qdev_prop_error", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"Object .* is not an instance of type spapr-machine", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"Object .* is not an instance of type generic-pc-machine", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"Object .* is not an instance of type e500-ccsr", 'loglevel':logging.ERROR}, - {'exitcode':-6, 'log':r"vmstate_register_with_alias_id: Assertion `!se->compat \|\| se->instance_id == 0' failed", 'loglevel':logging.ERROR}, - {'exitcode':-11, 'device':'isa-serial', 'loglevel':logging.ERROR, 'expected':True}, - # everything else (including SIGABRT and SIGSEGV) will be a fatal error: {'exitcode':None, 'fatal':True, 'loglevel':logging.FATAL}, ] @@ -597,7 +559,8 @@ def main(): logger.info("Skipped %d test cases", skipped) if args.debug: - stats = sorted([(len(wl_stats.get(i, [])), wl) for i, wl in enumerate(ERROR_WHITELIST)]) + stats = sorted([(len(wl_stats.get(i, [])), wl) for i, wl in + enumerate(ERROR_WHITELIST)], key=lambda x: x[0]) for count, wl in stats: dbg("whitelist entry stats: %d: %r", count, wl) diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 5a857cebcf..2c587cbefc 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -163,6 +163,7 @@ class ELF(object): phdr = get_arch_phdr(self.endianness, self.elfclass) phdr.p_type = p_type phdr.p_paddr = p_paddr + phdr.p_vaddr = p_paddr phdr.p_filesz = p_size phdr.p_memsz = p_size self.segments.append(phdr) @@ -417,7 +418,9 @@ def get_guest_phys_blocks(): memory_region = flat_range["mr"].dereference() # we only care about RAM - if not memory_region["ram"]: + if (not memory_region["ram"] or + memory_region["ram_device"] or + memory_region["nonvolatile"]): continue section_size = int128_get64(flat_range["addr"]["size"]) diff --git a/scripts/fix-multiline-comments.sh b/scripts/fix-multiline-comments.sh new file mode 100755 index 0000000000..93f9b10669 --- /dev/null +++ b/scripts/fix-multiline-comments.sh @@ -0,0 +1,62 @@ +#! /bin/sh +# +# Fix multiline comments to match CODING_STYLE +# +# Copyright (C) 2018 Red Hat, Inc. +# +# Author: Paolo Bonzini +# +# Usage: scripts/fix-multiline-comments.sh [-i] FILE... +# +# -i edits the file in place (requires gawk 4.1.0). +# +# Set the AWK environment variable to choose the awk interpreter to use +# (default 'awk') + +if test "$1" = -i; then + # gawk extension + inplace="-i inplace" + shift +fi +${AWK-awk} $inplace 'BEGIN { indent = -1 } +{ + line = $0 + # apply a star to the indent on lines after the first + if (indent != -1) { + if (line == "") { + line = sp " *" + } else if (substr(line, 1, indent + 2) == sp " ") { + line = sp " *" substr(line, indent + 3) + } + } + + is_lead = (line ~ /^[ \t]*\/\*/) + is_trail = (line ~ /\*\//) + if (is_lead && !is_trail) { + # grab the indent at the start of a comment, but not for + # single-line comments + match(line, /^[ \t]*\/\*/) + indent = RLENGTH - 2 + sp = substr(line, 1, indent) + } + + # the regular expression filters out lone /*, /**, or */ + if (indent != -1 && !(line ~ /^[ \t]*(\/\*+|\*\/)[ \t]*$/)) { + if (is_lead) { + # split the leading /* or /** on a separate line + match(line, /^[ \t]*\/\*+/) + lead = substr(line, 1, RLENGTH) + match(line, /^[ \t]*\/\*+[ \t]*/) + line = lead "\n" sp " *" substr(line, RLENGTH) + } + if (is_trail) { + # split the trailing */ on a separate line + match(line, /[ \t]*\*\//) + line = substr(line, 1, RSTART - 1) "\n" sp " */" + } + } + if (is_trail) { + indent = -1 + } + print line +}' "$@" diff --git a/scripts/get_maintainer.pl b/scripts/get_maintainer.pl index 43fb5f512f..71415e3c70 100755 --- a/scripts/get_maintainer.pl +++ b/scripts/get_maintainer.pl @@ -1376,7 +1376,7 @@ sub vcs_exists { warn("$P: No supported VCS found. Add --nogit to options?\n"); warn("Using a git repository produces better results.\n"); warn("Try latest git repository using:\n"); - warn("git clone git://git.qemu.org/qemu.git\n"); + warn("git clone https://git.qemu.org/git/qemu.git\n"); $printed_novcs = 1; } return 0; diff --git a/scripts/git-submodule.sh b/scripts/git-submodule.sh index 807ca0b4f8..98ca0f2737 100755 --- a/scripts/git-submodule.sh +++ b/scripts/git-submodule.sh @@ -59,8 +59,8 @@ status) fi test -f "$substat" || exit 1 - CURSTATUS=`$GIT submodule status $modules` - OLDSTATUS=`cat $substat` + CURSTATUS=$($GIT submodule status $modules) + OLDSTATUS=$(cat $substat) test "$CURSTATUS" = "$OLDSTATUS" exit $? ;; diff --git a/scripts/gtester-cat b/scripts/gtester-cat deleted file mode 100755 index 061a952cad..0000000000 --- a/scripts/gtester-cat +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -# -# Copyright IBM, Corp. 2012 -# -# Authors: -# Anthony Liguori -# -# This work is licensed under the terms of the GNU GPLv2 or later. -# See the COPYING file in the top-level directory. - -cat < - - - qemu - 0.0 - rev - -EOF - -sed \ - -e '/$/d' \ - -e '//,/<\/info>/d' \ - -e '$b' \ - -e '/^<\/gtester>$/d' "$@" diff --git a/scripts/make-release b/scripts/make-release index 04fa9defdc..b4af9c9e52 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -19,10 +19,7 @@ pushd ${destination} git checkout "v${version}" git submodule update --init (cd roms/seabios && git describe --tags --long --dirty > .version) -# FIXME: The following line is a workaround for avoiding filename collisions -# when unpacking u-boot sources on case-insensitive filesystems. Once we -# update to something with u-boot commit 610eec7f0 we can drop this line. -tar --exclude=.git -cjf roms/u-boot.tar.bz2 -C roms u-boot && rm -rf roms/u-boot +(cd roms/skiboot && ./make_version.sh > .version) popd tar --exclude=.git -cjf ${destination}.tar.bz2 ${destination} rm -rf ${destination} diff --git a/scripts/make_device_config.sh b/scripts/make_device_config.sh deleted file mode 100644 index 354af317b3..0000000000 --- a/scripts/make_device_config.sh +++ /dev/null @@ -1,30 +0,0 @@ -#! /bin/sh -# Writes a target device config file to stdout, from a default and from -# include directives therein. Also emits Makefile dependencies. -# -# Usage: make_device_config.sh SRC DEPFILE-NAME DEPFILE-TARGET > DEST - -src=$1 -dep=$2 -target=$3 -src_dir=$(dirname $src) -all_includes= - -process_includes () { - cat $1 | grep '^include' | \ - while read include file ; do - all_includes="$all_includes $src_dir/$file" - process_includes $src_dir/$file - done -} - -f=$src -while [ -n "$f" ] ; do - f=$(cat $f | tr -d '\r' | awk '/^include / {printf "'$src_dir'/%s ", $2}') - [ $? = 0 ] || exit 1 - all_includes="$all_includes $f" -done -process_includes $src - -cat $src $all_includes | grep -v '^include' -echo "$target: $all_includes" > $dep diff --git a/scripts/minikconf.py b/scripts/minikconf.py new file mode 100644 index 0000000000..0ffc6c38da --- /dev/null +++ b/scripts/minikconf.py @@ -0,0 +1,708 @@ +# +# Mini-Kconfig parser +# +# Copyright (c) 2015 Red Hat Inc. +# +# Authors: +# Paolo Bonzini +# +# 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. + +from __future__ import print_function +import os +import sys +import re +import random + +__all__ = [ 'KconfigDataError', 'KconfigParserError', + 'KconfigData', 'KconfigParser' , + 'defconfig', 'allyesconfig', 'allnoconfig', 'randconfig' ] + +def debug_print(*args): + #print('# ' + (' '.join(str(x) for x in args))) + pass + +# ------------------------------------------- +# KconfigData implements the Kconfig semantics. For now it can only +# detect undefined symbols, i.e. symbols that were referenced in +# assignments or dependencies but were not declared with "config FOO". +# +# Semantic actions are represented by methods called do_*. The do_var +# method return the semantic value of a variable (which right now is +# just its name). +# ------------------------------------------- + +class KconfigDataError(Exception): + def __init__(self, msg): + self.msg = msg + + def __str__(self): + return self.msg + +allyesconfig = lambda x: True +allnoconfig = lambda x: False +defconfig = lambda x: x +randconfig = lambda x: random.randint(0, 1) == 1 + +class KconfigData: + class Expr: + def __and__(self, rhs): + return KconfigData.AND(self, rhs) + def __or__(self, rhs): + return KconfigData.OR(self, rhs) + def __invert__(self): + return KconfigData.NOT(self) + + # Abstract methods + def add_edges_to(self, var): + pass + def evaluate(self): + assert False + + class AND(Expr): + def __init__(self, lhs, rhs): + self.lhs = lhs + self.rhs = rhs + def __str__(self): + return "(%s && %s)" % (self.lhs, self.rhs) + + def add_edges_to(self, var): + self.lhs.add_edges_to(var) + self.rhs.add_edges_to(var) + def evaluate(self): + return self.lhs.evaluate() and self.rhs.evaluate() + + class OR(Expr): + def __init__(self, lhs, rhs): + self.lhs = lhs + self.rhs = rhs + def __str__(self): + return "(%s || %s)" % (self.lhs, self.rhs) + + def add_edges_to(self, var): + self.lhs.add_edges_to(var) + self.rhs.add_edges_to(var) + def evaluate(self): + return self.lhs.evaluate() or self.rhs.evaluate() + + class NOT(Expr): + def __init__(self, lhs): + self.lhs = lhs + def __str__(self): + return "!%s" % (self.lhs) + + def add_edges_to(self, var): + self.lhs.add_edges_to(var) + def evaluate(self): + return not self.lhs.evaluate() + + class Var(Expr): + def __init__(self, name): + self.name = name + self.value = None + self.outgoing = set() + self.clauses_for_var = list() + def __str__(self): + return self.name + + def has_value(self): + return not (self.value is None) + def set_value(self, val, clause): + self.clauses_for_var.append(clause) + if self.has_value() and self.value != val: + print("The following clauses were found for " + self.name) + for i in self.clauses_for_var: + print(" " + str(i), file=sys.stderr) + raise KconfigDataError('contradiction between clauses when setting %s' % self) + debug_print("=> %s is now %s" % (self.name, val)) + self.value = val + + # depth first search of the dependency graph + def dfs(self, visited, f): + if self in visited: + return + visited.add(self) + for v in self.outgoing: + v.dfs(visited, f) + f(self) + + def add_edges_to(self, var): + self.outgoing.add(var) + def evaluate(self): + if not self.has_value(): + raise KconfigDataError('cycle found including %s' % self) + return self.value + + class Clause: + def __init__(self, dest): + self.dest = dest + def priority(self): + return 0 + def process(self): + pass + + class AssignmentClause(Clause): + def __init__(self, dest, value): + KconfigData.Clause.__init__(self, dest) + self.value = value + def __str__(self): + return "CONFIG_%s=%s" % (self.dest, 'y' if self.value else 'n') + + def process(self): + self.dest.set_value(self.value, self) + + class DefaultClause(Clause): + def __init__(self, dest, value, cond=None): + KconfigData.Clause.__init__(self, dest) + self.value = value + self.cond = cond + if not (self.cond is None): + self.cond.add_edges_to(self.dest) + def __str__(self): + value = 'y' if self.value else 'n' + if self.cond is None: + return "config %s default %s" % (self.dest, value) + else: + return "config %s default %s if %s" % (self.dest, value, self.cond) + + def priority(self): + # Defaults are processed just before leaving the variable + return -1 + def process(self): + if not self.dest.has_value() and \ + (self.cond is None or self.cond.evaluate()): + self.dest.set_value(self.value, self) + + class DependsOnClause(Clause): + def __init__(self, dest, expr): + KconfigData.Clause.__init__(self, dest) + self.expr = expr + self.expr.add_edges_to(self.dest) + def __str__(self): + return "config %s depends on %s" % (self.dest, self.expr) + + def process(self): + if not self.expr.evaluate(): + self.dest.set_value(False, self) + + class SelectClause(Clause): + def __init__(self, dest, cond): + KconfigData.Clause.__init__(self, dest) + self.cond = cond + self.cond.add_edges_to(self.dest) + def __str__(self): + return "select %s if %s" % (self.dest, self.cond) + + def process(self): + if self.cond.evaluate(): + self.dest.set_value(True, self) + + def __init__(self, value_mangler=defconfig): + self.value_mangler = value_mangler + self.previously_included = [] + self.incl_info = None + self.defined_vars = set() + self.referenced_vars = dict() + self.clauses = list() + + # semantic analysis ------------- + + def check_undefined(self): + undef = False + for i in self.referenced_vars: + if not (i in self.defined_vars): + print("undefined symbol %s" % (i), file=sys.stderr) + undef = True + return undef + + def compute_config(self): + if self.check_undefined(): + raise KconfigDataError("there were undefined symbols") + return None + + debug_print("Input:") + for clause in self.clauses: + debug_print(clause) + + debug_print("\nDependency graph:") + for i in self.referenced_vars: + debug_print(i, "->", [str(x) for x in self.referenced_vars[i].outgoing]) + + # The reverse of the depth-first order is the topological sort + dfo = dict() + visited = set() + debug_print("\n") + def visit_fn(var): + debug_print(var, "has DFS number", len(dfo)) + dfo[var] = len(dfo) + + for name, v in self.referenced_vars.items(): + self.do_default(v, False) + v.dfs(visited, visit_fn) + + # Put higher DFS numbers and higher priorities first. This + # places the clauses in topological order and places defaults + # after assignments and dependencies. + self.clauses.sort(key=lambda x: (-dfo[x.dest], -x.priority())) + + debug_print("\nSorted clauses:") + for clause in self.clauses: + debug_print(clause) + clause.process() + + debug_print("") + values = dict() + for name, v in self.referenced_vars.items(): + debug_print("Evaluating", name) + values[name] = v.evaluate() + + return values + + # semantic actions ------------- + + def do_declaration(self, var): + if (var in self.defined_vars): + raise KconfigDataError('variable "' + var + '" defined twice') + + self.defined_vars.add(var.name) + + # var is a string with the variable's name. + def do_var(self, var): + if (var in self.referenced_vars): + return self.referenced_vars[var] + + var_obj = self.referenced_vars[var] = KconfigData.Var(var) + return var_obj + + def do_assignment(self, var, val): + self.clauses.append(KconfigData.AssignmentClause(var, val)) + + def do_default(self, var, val, cond=None): + val = self.value_mangler(val) + self.clauses.append(KconfigData.DefaultClause(var, val, cond)) + + def do_depends_on(self, var, expr): + self.clauses.append(KconfigData.DependsOnClause(var, expr)) + + def do_select(self, var, symbol, cond=None): + cond = (cond & var) if cond is not None else var + self.clauses.append(KconfigData.SelectClause(symbol, cond)) + + def do_imply(self, var, symbol, cond=None): + # "config X imply Y [if COND]" is the same as + # "config Y default y if X [&& COND]" + cond = (cond & var) if cond is not None else var + self.do_default(symbol, True, cond) + +# ------------------------------------------- +# KconfigParser implements a recursive descent parser for (simplified) +# Kconfig syntax. +# ------------------------------------------- + +# tokens table +TOKENS = {} +TOK_NONE = -1 +TOK_LPAREN = 0; TOKENS[TOK_LPAREN] = '"("'; +TOK_RPAREN = 1; TOKENS[TOK_RPAREN] = '")"'; +TOK_EQUAL = 2; TOKENS[TOK_EQUAL] = '"="'; +TOK_AND = 3; TOKENS[TOK_AND] = '"&&"'; +TOK_OR = 4; TOKENS[TOK_OR] = '"||"'; +TOK_NOT = 5; TOKENS[TOK_NOT] = '"!"'; +TOK_DEPENDS = 6; TOKENS[TOK_DEPENDS] = '"depends"'; +TOK_ON = 7; TOKENS[TOK_ON] = '"on"'; +TOK_SELECT = 8; TOKENS[TOK_SELECT] = '"select"'; +TOK_IMPLY = 9; TOKENS[TOK_IMPLY] = '"imply"'; +TOK_CONFIG = 10; TOKENS[TOK_CONFIG] = '"config"'; +TOK_DEFAULT = 11; TOKENS[TOK_DEFAULT] = '"default"'; +TOK_Y = 12; TOKENS[TOK_Y] = '"y"'; +TOK_N = 13; TOKENS[TOK_N] = '"n"'; +TOK_SOURCE = 14; TOKENS[TOK_SOURCE] = '"source"'; +TOK_BOOL = 15; TOKENS[TOK_BOOL] = '"bool"'; +TOK_IF = 16; TOKENS[TOK_IF] = '"if"'; +TOK_ID = 17; TOKENS[TOK_ID] = 'identifier'; +TOK_EOF = 18; TOKENS[TOK_EOF] = 'end of file'; + +class KconfigParserError(Exception): + def __init__(self, parser, msg, tok=None): + self.loc = parser.location() + tok = tok or parser.tok + if tok != TOK_NONE: + location = TOKENS.get(tok, None) or ('"%s"' % tok) + msg = '%s before %s' % (msg, location) + self.msg = msg + + def __str__(self): + return "%s: %s" % (self.loc, self.msg) + +class KconfigParser: + + @classmethod + def parse(self, fp, mode=None): + data = KconfigData(mode or KconfigParser.defconfig) + parser = KconfigParser(data) + parser.parse_file(fp) + return data + + def __init__(self, data): + self.data = data + + def parse_file(self, fp): + self.abs_fname = os.path.abspath(fp.name) + self.fname = fp.name + self.data.previously_included.append(self.abs_fname) + self.src = fp.read() + if self.src == '' or self.src[-1] != '\n': + self.src += '\n' + self.cursor = 0 + self.line = 1 + self.line_pos = 0 + self.get_token() + self.parse_config() + + def do_assignment(self, var, val): + if not var.startswith("CONFIG_"): + raise Error('assigned variable should start with CONFIG_') + var = self.data.do_var(var[7:]) + self.data.do_assignment(var, val) + + # file management ----- + + def error_path(self): + inf = self.data.incl_info + res = "" + while inf: + res = ("In file included from %s:%d:\n" % (inf['file'], + inf['line'])) + res + inf = inf['parent'] + return res + + def location(self): + col = 1 + for ch in self.src[self.line_pos:self.pos]: + if ch == '\t': + col += 8 - ((col - 1) % 8) + else: + col += 1 + return '%s%s:%d:%d' %(self.error_path(), self.fname, self.line, col) + + def do_include(self, include): + incl_abs_fname = os.path.join(os.path.dirname(self.abs_fname), + include) + # catch inclusion cycle + inf = self.data.incl_info + while inf: + if incl_abs_fname == os.path.abspath(inf['file']): + raise KconfigParserError(self, "Inclusion loop for %s" + % include) + inf = inf['parent'] + + # skip multiple include of the same file + if incl_abs_fname in self.data.previously_included: + return + try: + fp = open(incl_abs_fname, 'r') + except IOError as e: + raise KconfigParserError(self, + '%s: %s' % (e.strerror, include)) + + inf = self.data.incl_info + self.data.incl_info = { 'file': self.fname, 'line': self.line, + 'parent': inf } + KconfigParser(self.data).parse_file(fp) + self.data.incl_info = inf + + # recursive descent parser ----- + + # y_or_n: Y | N + def parse_y_or_n(self): + if self.tok == TOK_Y: + self.get_token() + return True + if self.tok == TOK_N: + self.get_token() + return False + raise KconfigParserError(self, 'Expected "y" or "n"') + + # var: ID + def parse_var(self): + if self.tok == TOK_ID: + val = self.val + self.get_token() + return self.data.do_var(val) + else: + raise KconfigParserError(self, 'Expected identifier') + + # assignment_var: ID (starting with "CONFIG_") + def parse_assignment_var(self): + if self.tok == TOK_ID: + val = self.val + if not val.startswith("CONFIG_"): + raise KconfigParserError(self, + 'Expected identifier starting with "CONFIG_"', TOK_NONE) + self.get_token() + return self.data.do_var(val[7:]) + else: + raise KconfigParserError(self, 'Expected identifier') + + # assignment: var EQUAL y_or_n + def parse_assignment(self): + var = self.parse_assignment_var() + if self.tok != TOK_EQUAL: + raise KconfigParserError(self, 'Expected "="') + self.get_token() + self.data.do_assignment(var, self.parse_y_or_n()) + + # primary: NOT primary + # | LPAREN expr RPAREN + # | var + def parse_primary(self): + if self.tok == TOK_NOT: + self.get_token() + val = ~self.parse_primary() + elif self.tok == TOK_LPAREN: + self.get_token() + val = self.parse_expr() + if self.tok != TOK_RPAREN: + raise KconfigParserError(self, 'Expected ")"') + self.get_token() + elif self.tok == TOK_ID: + val = self.parse_var() + else: + raise KconfigParserError(self, 'Expected "!" or "(" or identifier') + return val + + # disj: primary (OR primary)* + def parse_disj(self): + lhs = self.parse_primary() + while self.tok == TOK_OR: + self.get_token() + lhs = lhs | self.parse_primary() + return lhs + + # expr: disj (AND disj)* + def parse_expr(self): + lhs = self.parse_disj() + while self.tok == TOK_AND: + self.get_token() + lhs = lhs & self.parse_disj() + return lhs + + # condition: IF expr + # | empty + def parse_condition(self): + if self.tok == TOK_IF: + self.get_token() + return self.parse_expr() + else: + return None + + # property: DEFAULT y_or_n condition + # | DEPENDS ON expr + # | SELECT var condition + # | BOOL + def parse_property(self, var): + if self.tok == TOK_DEFAULT: + self.get_token() + val = self.parse_y_or_n() + cond = self.parse_condition() + self.data.do_default(var, val, cond) + elif self.tok == TOK_DEPENDS: + self.get_token() + if self.tok != TOK_ON: + raise KconfigParserError(self, 'Expected "on"') + self.get_token() + self.data.do_depends_on(var, self.parse_expr()) + elif self.tok == TOK_SELECT: + self.get_token() + symbol = self.parse_var() + cond = self.parse_condition() + self.data.do_select(var, symbol, cond) + elif self.tok == TOK_IMPLY: + self.get_token() + symbol = self.parse_var() + cond = self.parse_condition() + self.data.do_imply(var, symbol, cond) + elif self.tok == TOK_BOOL: + self.get_token() + else: + raise KconfigParserError(self, 'Error in recursive descent?') + + # properties: properties property + # | /* empty */ + def parse_properties(self, var): + had_default = False + while self.tok == TOK_DEFAULT or self.tok == TOK_DEPENDS or \ + self.tok == TOK_SELECT or self.tok == TOK_BOOL or \ + self.tok == TOK_IMPLY: + self.parse_property(var) + + # for nicer error message + if self.tok != TOK_SOURCE and self.tok != TOK_CONFIG and \ + self.tok != TOK_ID and self.tok != TOK_EOF: + raise KconfigParserError(self, 'expected "source", "config", identifier, ' + + '"default", "depends on", "imply" or "select"') + + # declaration: config var properties + def parse_declaration(self): + if self.tok == TOK_CONFIG: + self.get_token() + var = self.parse_var() + self.data.do_declaration(var) + self.parse_properties(var) + else: + raise KconfigParserError(self, 'Error in recursive descent?') + + # clause: SOURCE + # | declaration + # | assignment + def parse_clause(self): + if self.tok == TOK_SOURCE: + val = self.val + self.get_token() + self.do_include(val) + elif self.tok == TOK_CONFIG: + self.parse_declaration() + elif self.tok == TOK_ID: + self.parse_assignment() + else: + raise KconfigParserError(self, 'expected "source", "config" or identifier') + + # config: clause+ EOF + def parse_config(self): + while self.tok != TOK_EOF: + self.parse_clause() + return self.data + + # scanner ----- + + def get_token(self): + while True: + self.tok = self.src[self.cursor] + self.pos = self.cursor + self.cursor += 1 + + self.val = None + self.tok = self.scan_token() + if self.tok is not None: + return + + def check_keyword(self, rest): + if not self.src.startswith(rest, self.cursor): + return False + length = len(rest) + if self.src[self.cursor + length].isalnum() or self.src[self.cursor + length] == '_': + return False + self.cursor += length + return True + + def scan_token(self): + if self.tok == '#': + self.cursor = self.src.find('\n', self.cursor) + return None + elif self.tok == '=': + return TOK_EQUAL + elif self.tok == '(': + return TOK_LPAREN + elif self.tok == ')': + return TOK_RPAREN + elif self.tok == '&' and self.src[self.pos+1] == '&': + self.cursor += 1 + return TOK_AND + elif self.tok == '|' and self.src[self.pos+1] == '|': + self.cursor += 1 + return TOK_OR + elif self.tok == '!': + return TOK_NOT + elif self.tok == 'd' and self.check_keyword("epends"): + return TOK_DEPENDS + elif self.tok == 'o' and self.check_keyword("n"): + return TOK_ON + elif self.tok == 's' and self.check_keyword("elect"): + return TOK_SELECT + elif self.tok == 'i' and self.check_keyword("mply"): + return TOK_IMPLY + elif self.tok == 'c' and self.check_keyword("onfig"): + return TOK_CONFIG + elif self.tok == 'd' and self.check_keyword("efault"): + return TOK_DEFAULT + elif self.tok == 'b' and self.check_keyword("ool"): + return TOK_BOOL + elif self.tok == 'i' and self.check_keyword("f"): + return TOK_IF + elif self.tok == 'y' and self.check_keyword(""): + return TOK_Y + elif self.tok == 'n' and self.check_keyword(""): + return TOK_N + elif (self.tok == 's' and self.check_keyword("ource")) or \ + self.tok == 'i' and self.check_keyword("nclude"): + # source FILENAME + # include FILENAME + while self.src[self.cursor].isspace(): + self.cursor += 1 + start = self.cursor + self.cursor = self.src.find('\n', self.cursor) + self.val = self.src[start:self.cursor] + return TOK_SOURCE + elif self.tok.isalpha(): + # identifier + while self.src[self.cursor].isalnum() or self.src[self.cursor] == '_': + self.cursor += 1 + self.val = self.src[self.pos:self.cursor] + return TOK_ID + elif self.tok == '\n': + if self.cursor == len(self.src): + return TOK_EOF + self.line += 1 + self.line_pos = self.cursor + elif not self.tok.isspace(): + raise KconfigParserError(self, 'invalid input') + + return None + +if __name__ == '__main__': + argv = sys.argv + mode = defconfig + if len(sys.argv) > 1: + if argv[1] == '--defconfig': + del argv[1] + elif argv[1] == '--randconfig': + random.seed() + mode = randconfig + del argv[1] + elif argv[1] == '--allyesconfig': + mode = allyesconfig + del argv[1] + elif argv[1] == '--allnoconfig': + mode = allnoconfig + del argv[1] + + if len(argv) == 1: + print ("%s: at least one argument is required" % argv[0], file=sys.stderr) + sys.exit(1) + + if argv[1].startswith('-'): + print ("%s: invalid option %s" % (argv[0], argv[1]), file=sys.stderr) + sys.exit(1) + + data = KconfigData(mode) + parser = KconfigParser(data) + for arg in argv[3:]: + m = re.match(r'^(CONFIG_[A-Z0-9_]+)=([yn]?)$', arg) + if m is not None: + name, value = m.groups() + parser.do_assignment(name, value == 'y') + else: + fp = open(arg, 'r') + parser.parse_file(fp) + fp.close() + + config = data.compute_config() + for key in sorted(config.keys()): + print ('CONFIG_%s=%s' % (key, ('y' if config[key] else 'n'))) + + deps = open(argv[2], 'w') + for fname in data.previously_included: + print ('%s: %s' % (argv[1], fname), file=deps) + deps.close() diff --git a/scripts/qapi/commands.py b/scripts/qapi/commands.py index 0f3c991918..6d66bf6aa3 100644 --- a/scripts/qapi/commands.py +++ b/scripts/qapi/commands.py @@ -239,10 +239,10 @@ class QAPISchemaGenCommandVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-commands', ' * Schema-defined QAPI/QMP commands', __doc__) - self._regy = QAPIGenCCode() + self._regy = QAPIGenCCode(None) self._visited_ret_types = {} - def _begin_module(self, name): + def _begin_user_module(self, name): self._visited_ret_types[self._genc] = set() commands = self._module_basename('qapi-commands', name) types = self._module_basename('qapi-types', name) diff --git a/scripts/qapi/common.py b/scripts/qapi/common.py index 9230a2a3e8..f07869ec73 100644 --- a/scripts/qapi/common.py +++ b/scripts/qapi/common.py @@ -588,11 +588,11 @@ def discriminator_find_enum_define(expr): if not base_members: return None - discriminator_type = base_members.get(discriminator) - if not discriminator_type: + discriminator_value = base_members.get(discriminator) + if not discriminator_value: return None - return enum_types.get(discriminator_type) + return enum_types.get(discriminator_value['type']) # Names must be letters, numbers, -, and _. They must start with letter, @@ -704,8 +704,10 @@ def check_type(info, source, value, allow_array=False, % (source, key)) # Todo: allow dictionaries to represent default values of # an optional argument. - check_type(info, "Member '%s' of %s" % (key, source), arg, - allow_array=True, + check_known_keys(info, "member '%s' of %s" % (key, source), + arg, ['type'], ['if']) + check_type(info, "Member '%s' of %s" % (key, source), + arg['type'], allow_array=True, allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']) @@ -740,6 +742,10 @@ def check_event(expr, info): allow_metas=meta) +def enum_get_names(expr): + return [e['name'] for e in expr['data']] + + def check_union(expr, info): name = expr['union'] base = expr.get('base') @@ -772,13 +778,17 @@ def check_union(expr, info): # member of the base struct. check_name(info, "Discriminator of flat union '%s'" % name, discriminator) - discriminator_type = base_members.get(discriminator) - if not discriminator_type: + discriminator_value = base_members.get(discriminator) + if not discriminator_value: raise QAPISemError(info, "Discriminator '%s' is not a member of base " "struct '%s'" % (discriminator, base)) - enum_define = enum_types.get(discriminator_type) + if discriminator_value.get('if'): + raise QAPISemError(info, 'The discriminator %s.%s for union %s ' + 'must not be conditional' % + (base, discriminator, name)) + enum_define = enum_types.get(discriminator_value['type']) allow_metas = ['struct'] # Do not allow string discriminator if not enum_define: @@ -792,14 +802,17 @@ def check_union(expr, info): for (key, value) in members.items(): check_name(info, "Member of union '%s'" % name, key) + check_known_keys(info, "member '%s' of union '%s'" % (key, name), + value, ['type'], ['if']) # Each value must name a known type check_type(info, "Member '%s' of union '%s'" % (key, name), - value, allow_array=not base, allow_metas=allow_metas) + value['type'], + allow_array=not base, allow_metas=allow_metas) # If the discriminator names an enum type, then all members # of 'data' must also be members of the enum type. if enum_define: - if key not in enum_define['data']: + if key not in enum_get_names(enum_define): raise QAPISemError(info, "Discriminator value '%s' is not found in " "enum '%s'" @@ -818,20 +831,23 @@ def check_alternate(expr, info): "in 'data'" % name) for (key, value) in members.items(): check_name(info, "Member of alternate '%s'" % name, key) + check_known_keys(info, + "member '%s' of alternate '%s'" % (key, name), + value, ['type'], ['if']) + typ = value['type'] # Ensure alternates have no type conflicts. - check_type(info, "Member '%s' of alternate '%s'" % (key, name), - value, + check_type(info, "Member '%s' of alternate '%s'" % (key, name), typ, allow_metas=['built-in', 'union', 'struct', 'enum']) - qtype = find_alternate_member_qtype(value) + qtype = find_alternate_member_qtype(typ) if not qtype: raise QAPISemError(info, "Alternate '%s' member '%s' cannot use " - "type '%s'" % (name, key, value)) + "type '%s'" % (name, key, typ)) conflicting = set([qtype]) if qtype == 'QTYPE_QSTRING': - enum_expr = enum_types.get(value) + enum_expr = enum_types.get(typ) if enum_expr: - for v in enum_expr['data']: + for v in enum_get_names(enum_expr): if v in ['on', 'off']: conflicting.add('QTYPE_QBOOL') if re.match(r'[-+0-9.]', v): # lazy, could be tightened @@ -849,7 +865,7 @@ def check_alternate(expr, info): def check_enum(expr, info): name = expr['enum'] - members = expr.get('data') + members = expr['data'] prefix = expr.get('prefix') if not isinstance(members, list): @@ -858,8 +874,12 @@ def check_enum(expr, info): if prefix is not None and not isinstance(prefix, str): raise QAPISemError(info, "Enum '%s' requires a string for 'prefix'" % name) + for member in members: - check_name(info, "Member of enum '%s'" % name, member, + source = "dictionary member of enum '%s'" % name + check_known_keys(info, source, member, ['name'], ['if']) + check_if(member, info) + check_name(info, "Member of enum '%s'" % name, member['name'], enum_member=True) @@ -873,6 +893,24 @@ def check_struct(expr, info): allow_metas=['struct']) +def check_known_keys(info, source, keys, required, optional): + + def pprint(elems): + return ', '.join("'" + e + "'" for e in sorted(elems)) + + missing = set(required) - set(keys) + if missing: + raise QAPISemError(info, "Key%s %s %s missing from %s" + % ('s' if len(missing) > 1 else '', pprint(missing), + 'are' if len(missing) > 1 else 'is', source)) + allowed = set(required + optional) + unknown = set(keys) - allowed + if unknown: + raise QAPISemError(info, "Unknown key%s %s in %s\nValid keys are %s." + % ('s' if len(unknown) > 1 else '', pprint(unknown), + source, pprint(allowed))) + + def check_keys(expr_elem, meta, required, optional=[]): expr = expr_elem['expr'] info = expr_elem['info'] @@ -880,25 +918,34 @@ def check_keys(expr_elem, meta, required, optional=[]): if not isinstance(name, str): raise QAPISemError(info, "'%s' key must have a string value" % meta) required = required + [meta] + source = "%s '%s'" % (meta, name) + check_known_keys(info, source, expr.keys(), required, optional) for (key, value) in expr.items(): - if key not in required and key not in optional: - raise QAPISemError(info, "Unknown key '%s' in %s '%s'" - % (key, meta, name)) - if (key == 'gen' or key == 'success-response') and value is not False: + if key in ['gen', 'success-response'] and value is not False: raise QAPISemError(info, "'%s' of %s '%s' should only use false value" % (key, meta, name)) - if (key == 'boxed' or key == 'allow-oob' or - key == 'allow-preconfig') and value is not True: + if (key in ['boxed', 'allow-oob', 'allow-preconfig'] + and value is not True): raise QAPISemError(info, "'%s' of %s '%s' should only use true value" % (key, meta, name)) if key == 'if': check_if(expr, info) - for key in required: - if key not in expr: - raise QAPISemError(info, "Key '%s' is missing from %s '%s'" - % (key, meta, name)) + + +def normalize_enum(expr): + if isinstance(expr['data'], list): + expr['data'] = [m if isinstance(m, dict) else {'name': m} + for m in expr['data']] + + +def normalize_members(members): + if isinstance(members, OrderedDict): + for key, arg in members.items(): + if isinstance(arg, dict): + continue + members[key] = {'type': arg} def check_exprs(exprs): @@ -924,27 +971,34 @@ def check_exprs(exprs): if 'enum' in expr: meta = 'enum' check_keys(expr_elem, 'enum', ['data'], ['if', 'prefix']) + normalize_enum(expr) enum_types[expr[meta]] = expr elif 'union' in expr: meta = 'union' check_keys(expr_elem, 'union', ['data'], ['base', 'discriminator', 'if']) + normalize_members(expr.get('base')) + normalize_members(expr['data']) union_types[expr[meta]] = expr elif 'alternate' in expr: meta = 'alternate' check_keys(expr_elem, 'alternate', ['data'], ['if']) + normalize_members(expr['data']) elif 'struct' in expr: meta = 'struct' check_keys(expr_elem, 'struct', ['data'], ['base', 'if']) + normalize_members(expr['data']) struct_types[expr[meta]] = expr elif 'command' in expr: meta = 'command' check_keys(expr_elem, 'command', [], ['data', 'returns', 'gen', 'success-response', 'boxed', 'allow-oob', 'allow-preconfig', 'if']) + normalize_members(expr.get('data')) elif 'event' in expr: meta = 'event' check_keys(expr_elem, 'event', [], ['data', 'boxed', 'if']) + normalize_members(expr.get('data')) else: raise QAPISemError(expr_elem['info'], "Expression is missing metatype") @@ -1035,6 +1089,9 @@ class QAPISchemaEntity(object): self.ifcond = typ.ifcond else: self.ifcond = listify_cond(self._ifcond) + if self.info: + self.module = os.path.relpath(self.info['file'], + os.path.dirname(schema.fname)) def is_implicit(self): return not self.info @@ -1063,7 +1120,7 @@ class QAPISchemaVisitor(object): def visit_builtin_type(self, name, info, json_type): pass - def visit_enum_type(self, name, info, ifcond, values, prefix): + def visit_enum_type(self, name, info, ifcond, members, prefix): pass def visit_array_type(self, name, info, ifcond, element_type): @@ -1161,22 +1218,22 @@ class QAPISchemaBuiltinType(QAPISchemaType): class QAPISchemaEnumType(QAPISchemaType): - def __init__(self, name, info, doc, ifcond, values, prefix): + def __init__(self, name, info, doc, ifcond, members, prefix): QAPISchemaType.__init__(self, name, info, doc, ifcond) - for v in values: - assert isinstance(v, QAPISchemaMember) - v.set_owner(name) + for m in members: + assert isinstance(m, QAPISchemaMember) + m.set_owner(name) assert prefix is None or isinstance(prefix, str) - self.values = values + self.members = members self.prefix = prefix def check(self, schema): QAPISchemaType.check(self, schema) seen = {} - for v in self.values: - v.check_clash(self.info, seen) + for m in self.members: + m.check_clash(self.info, seen) if self.doc: - self.doc.connect_member(v) + self.doc.connect_member(m) def is_implicit(self): # See QAPISchema._make_implicit_enum_type() and ._def_predefineds() @@ -1186,14 +1243,14 @@ class QAPISchemaEnumType(QAPISchemaType): return c_name(self.name) def member_names(self): - return [v.name for v in self.values] + return [m.name for m in self.members] def json_type(self): return 'string' def visit(self, visitor): visitor.visit_enum_type(self.name, self.info, self.ifcond, - self.member_names(), self.prefix) + self.members, self.prefix) class QAPISchemaArrayType(QAPISchemaType): @@ -1208,6 +1265,7 @@ class QAPISchemaArrayType(QAPISchemaType): self.element_type = schema.lookup_type(self._element_type_name) assert self.element_type self.element_type.check(schema) + self.module = self.element_type.module self.ifcond = self.element_type.ifcond def is_implicit(self): @@ -1318,9 +1376,10 @@ class QAPISchemaObjectType(QAPISchemaType): class QAPISchemaMember(object): role = 'member' - def __init__(self, name): + def __init__(self, name, ifcond=None): assert isinstance(name, str) self.name = name + self.ifcond = listify_cond(ifcond) self.owner = None def set_owner(self, name): @@ -1361,8 +1420,8 @@ class QAPISchemaMember(object): class QAPISchemaObjectTypeMember(QAPISchemaMember): - def __init__(self, name, typ, optional): - QAPISchemaMember.__init__(self, name) + def __init__(self, name, typ, optional, ifcond=None): + QAPISchemaMember.__init__(self, name, ifcond) assert isinstance(typ, str) assert isinstance(optional, bool) self._type_name = typ @@ -1403,9 +1462,10 @@ class QAPISchemaObjectTypeVariants(object): if self._tag_name: # flat union # branches that are not explicitly covered get an empty type cases = set([v.name for v in self.variants]) - for val in self.tag_member.type.values: - if val.name not in cases: - v = QAPISchemaObjectTypeVariant(val.name, 'q_empty') + for m in self.tag_member.type.members: + if m.name not in cases: + v = QAPISchemaObjectTypeVariant(m.name, 'q_empty', + m.ifcond) v.set_owner(self.tag_member.owner) self.variants.append(v) for v in self.variants: @@ -1428,8 +1488,8 @@ class QAPISchemaObjectTypeVariants(object): class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember): role = 'branch' - def __init__(self, name, typ): - QAPISchemaObjectTypeMember.__init__(self, name, typ, False) + def __init__(self, name, typ, ifcond=None): + QAPISchemaObjectTypeMember.__init__(self, name, typ, False, ifcond) class QAPISchemaAlternateType(QAPISchemaType): @@ -1547,7 +1607,7 @@ class QAPISchemaEvent(QAPISchemaEntity): class QAPISchema(object): def __init__(self, fname): - self._fname = fname + self.fname = fname if sys.version_info[0] >= 3: f = open(fname, 'r', encoding='utf-8') else: @@ -1570,9 +1630,6 @@ class QAPISchema(object): self._entity_list.append(ent) if ent.name is not None: self._entity_dict[ent.name] = ent - if ent.info: - ent.module = os.path.relpath(ent.info['file'], - os.path.dirname(self._fname)) def lookup_entity(self, name, typ=None): ent = self._entity_dict.get(name) @@ -1620,14 +1677,16 @@ class QAPISchema(object): self.the_empty_object_type = QAPISchemaObjectType( 'q_empty', None, None, None, None, [], None) self._def_entity(self.the_empty_object_type) - qtype_values = self._make_enum_members(['none', 'qnull', 'qnum', - 'qstring', 'qdict', 'qlist', - 'qbool']) + + qtypes = ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', + 'qbool'] + qtype_values = self._make_enum_members([{'name': n} for n in qtypes]) + self._def_entity(QAPISchemaEnumType('QType', None, None, None, qtype_values, 'QTYPE')) def _make_enum_members(self, values): - return [QAPISchemaMember(v) for v in values] + return [QAPISchemaMember(v['name'], v.get('if')) for v in values] def _make_implicit_enum_type(self, name, info, ifcond, values): # See also QAPISchemaObjectTypeMember._pretty_owner() @@ -1674,7 +1733,7 @@ class QAPISchema(object): name, info, doc, ifcond, self._make_enum_members(data), prefix)) - def _make_member(self, name, typ, info): + def _make_member(self, name, typ, ifcond, info): optional = False if name.startswith('*'): name = name[1:] @@ -1682,10 +1741,10 @@ class QAPISchema(object): if isinstance(typ, list): assert len(typ) == 1 typ = self._make_array_type(typ[0], info) - return QAPISchemaObjectTypeMember(name, typ, optional) + return QAPISchemaObjectTypeMember(name, typ, optional, ifcond) def _make_members(self, data, info): - return [self._make_member(key, value, info) + return [self._make_member(key, value['type'], value.get('if'), info) for (key, value) in data.items()] def _def_struct_type(self, expr, info, doc): @@ -1697,17 +1756,17 @@ class QAPISchema(object): self._make_members(data, info), None)) - def _make_variant(self, case, typ): - return QAPISchemaObjectTypeVariant(case, typ) + def _make_variant(self, case, typ, ifcond): + return QAPISchemaObjectTypeVariant(case, typ, ifcond) - def _make_simple_variant(self, case, typ, info): + def _make_simple_variant(self, case, typ, ifcond, 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, None, self.lookup_type(typ), - 'wrapper', [self._make_member('data', typ, info)]) - return QAPISchemaObjectTypeVariant(case, typ) + 'wrapper', [self._make_member('data', typ, None, info)]) + return QAPISchemaObjectTypeVariant(case, typ, ifcond) def _def_union_type(self, expr, info, doc): name = expr['union'] @@ -1721,14 +1780,15 @@ class QAPISchema(object): name, info, doc, ifcond, 'base', self._make_members(base, info)) if tag_name: - variants = [self._make_variant(key, value) + variants = [self._make_variant(key, value['type'], value.get('if')) for (key, value) in data.items()] members = [] else: - variants = [self._make_simple_variant(key, value, info) + variants = [self._make_simple_variant(key, value['type'], + value.get('if'), info) for (key, value) in data.items()] - typ = self._make_implicit_enum_type(name, info, ifcond, - [v.name for v in variants]) + enum = [{'name': v.name, 'if': v.ifcond} for v in variants] + typ = self._make_implicit_enum_type(name, info, ifcond, enum) tag_member = QAPISchemaObjectTypeMember('type', typ, False) members = [tag_member] self._def_entity( @@ -1741,7 +1801,7 @@ class QAPISchema(object): name = expr['alternate'] data = expr['data'] ifcond = expr.get('if') - variants = [self._make_variant(key, value) + variants = [self._make_variant(key, value['type'], value.get('if')) for (key, value) in data.items()] tag_member = QAPISchemaObjectTypeMember('type', 'QType', False) self._def_entity( @@ -1809,6 +1869,7 @@ class QAPISchema(object): def visit(self, visitor): visitor.visit_begin(self) module = None + visitor.visit_module(module) for entity in self._entity_list: if visitor.visit_needed(entity): if entity.module != module: @@ -1845,12 +1906,12 @@ def camel_to_upper(value): return c_fun_str new_name = '' - l = len(c_fun_str) - for i in range(l): + length = len(c_fun_str) + for i in range(length): 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] != '_': - if i < l - 1 and c_fun_str[i + 1].islower(): + if i < length - 1 and c_fun_str[i + 1].islower(): new_name += '_' elif c_fun_str[i - 1].isdigit(): new_name += '_' @@ -1863,6 +1924,7 @@ def c_enum_const(type_name, const_name, prefix=None): type_name = prefix return camel_to_upper(type_name) + '_' + c_name(const_name, False).upper() + if hasattr(str, 'maketrans'): c_name_trans = str.maketrans('.-', '__') else: @@ -1912,6 +1974,7 @@ def c_name(name, protect=True): return 'q_' + name return name + eatspace = '\033EATSPACE.' pointer_suffix = ' *' + eatspace @@ -1922,6 +1985,7 @@ def genindent(count): ret += ' ' return ret + indent_level = 0 @@ -1954,8 +2018,8 @@ def mcgen(code, **kwds): return cgen(code, **kwds) -def guardname(filename): - return re.sub(r'[^A-Za-z0-9_]', '_', filename).upper() +def c_fname(filename): + return re.sub(r'[^A-Za-z0-9_]', '_', filename) def guardstart(name): @@ -1964,7 +2028,7 @@ def guardstart(name): #define %(name)s ''', - name=guardname(name)) + name=c_fname(name).upper()) def guardend(name): @@ -1972,7 +2036,7 @@ def guardend(name): #endif /* %(name)s */ ''', - name=guardname(name)) + name=c_fname(name).upper()) def gen_if(ifcond): @@ -2009,19 +2073,21 @@ def _wrap_ifcond(ifcond, before, after): return out -def gen_enum_lookup(name, values, prefix=None): +def gen_enum_lookup(name, members, prefix=None): ret = mcgen(''' const QEnumLookup %(c_name)s_lookup = { .array = (const char *const[]) { ''', c_name=c_name(name)) - for value in values: - index = c_enum_const(name, value, prefix) + for m in members: + ret += gen_if(m.ifcond) + index = c_enum_const(name, m.name, prefix) ret += mcgen(''' - [%(index)s] = "%(value)s", + [%(index)s] = "%(name)s", ''', - index=index, value=value) + index=index, name=m.name) + ret += gen_endif(m.ifcond) ret += mcgen(''' }, @@ -2032,9 +2098,9 @@ const QEnumLookup %(c_name)s_lookup = { return ret -def gen_enum(name, values, prefix=None): +def gen_enum(name, members, prefix=None): # append automatically generated _MAX value - enum_values = values + ['_MAX'] + enum_members = members + [QAPISchemaMember('_MAX')] ret = mcgen(''' @@ -2042,14 +2108,13 @@ typedef enum %(c_name)s { ''', c_name=c_name(name)) - i = 0 - for value in enum_values: + for m in enum_members: + ret += gen_if(m.ifcond) ret += mcgen(''' - %(c_enum)s = %(i)d, + %(c_enum)s, ''', - c_enum=c_enum_const(name, value, prefix), - i=i) - i += 1 + c_enum=c_enum_const(name, m.name, prefix)) + ret += gen_endif(m.ifcond) ret += mcgen(''' } %(c_name)s; @@ -2067,16 +2132,14 @@ extern const QEnumLookup %(c_name)s_lookup; return ret -def build_params(arg_type, boxed, extra): - if not arg_type: - assert not boxed - return extra +def build_params(arg_type, boxed, extra=None): ret = '' sep = '' if boxed: + assert arg_type ret += '%s arg' % arg_type.c_param_type() sep = ', ' - else: + elif arg_type: assert not arg_type.variants for memb in arg_type.members: ret += sep @@ -2087,7 +2150,7 @@ def build_params(arg_type, boxed, extra): c_name(memb.name)) if extra: ret += sep + extra - return ret + return ret if ret else 'void' # @@ -2096,7 +2159,8 @@ def build_params(arg_type, boxed, extra): class QAPIGen(object): - def __init__(self): + def __init__(self, fname): + self.fname = fname self._preamble = '' self._body = '' @@ -2106,18 +2170,17 @@ class QAPIGen(object): def add(self, text): self._body += text - def get_content(self, fname=None): - return (self._top(fname) + self._preamble + self._body - + self._bottom(fname)) + def get_content(self): + return self._top() + self._preamble + self._body + self._bottom() - def _top(self, fname): + def _top(self): return '' - def _bottom(self, fname): + def _bottom(self): return '' - def write(self, output_dir, fname): - pathname = os.path.join(output_dir, fname) + def write(self, output_dir): + pathname = os.path.join(output_dir, self.fname) dir = os.path.dirname(pathname) if dir: try: @@ -2130,7 +2193,7 @@ class QAPIGen(object): f = open(fd, 'r+', encoding='utf-8') else: f = os.fdopen(fd, 'r+') - text = self.get_content(fname) + text = self.get_content() oldtext = f.read(len(text) + 1) if text != oldtext: f.seek(0) @@ -2167,8 +2230,8 @@ def ifcontext(ifcond, *args): class QAPIGenCCode(QAPIGen): - def __init__(self): - QAPIGen.__init__(self) + def __init__(self, fname): + QAPIGen.__init__(self, fname) self._start_if = None def start_if(self, ifcond): @@ -2186,20 +2249,20 @@ class QAPIGenCCode(QAPIGen): self._preamble = _wrap_ifcond(self._start_if[0], self._start_if[2], self._preamble) - def get_content(self, fname=None): + def get_content(self): assert self._start_if is None - return QAPIGen.get_content(self, fname) + return QAPIGen.get_content(self) class QAPIGenC(QAPIGenCCode): - def __init__(self, blurb, pydoc): - QAPIGenCCode.__init__(self) + def __init__(self, fname, blurb, pydoc): + QAPIGenCCode.__init__(self, fname) self._blurb = blurb self._copyright = '\n * '.join(re.findall(r'^Copyright .*', pydoc, re.MULTILINE)) - def _top(self, fname): + def _top(self): return mcgen(''' /* AUTOMATICALLY GENERATED, DO NOT MODIFY */ @@ -2215,27 +2278,28 @@ class QAPIGenC(QAPIGenCCode): ''', blurb=self._blurb, copyright=self._copyright) - def _bottom(self, fname): + def _bottom(self): return mcgen(''' + /* Dummy declaration to prevent empty .o file */ -char dummy_%(name)s; +char qapi_dummy_%(name)s; ''', - name=c_name(fname)) + name=c_fname(self.fname)) class QAPIGenH(QAPIGenC): - def _top(self, fname): - return QAPIGenC._top(self, fname) + guardstart(fname) + def _top(self): + return QAPIGenC._top(self) + guardstart(self.fname) - def _bottom(self, fname): - return guardend(fname) + def _bottom(self): + return guardend(self.fname) class QAPIGenDoc(QAPIGen): - def _top(self, fname): - return (QAPIGen._top(self, fname) + def _top(self): + return (QAPIGen._top(self) + '@c AUTOMATICALLY GENERATED, DO NOT MODIFY\n\n') @@ -2244,12 +2308,14 @@ class QAPISchemaMonolithicCVisitor(QAPISchemaVisitor): def __init__(self, prefix, what, blurb, pydoc): self._prefix = prefix self._what = what - self._genc = QAPIGenC(blurb, pydoc) - self._genh = QAPIGenH(blurb, pydoc) + self._genc = QAPIGenC(self._prefix + self._what + '.c', + blurb, pydoc) + self._genh = QAPIGenH(self._prefix + self._what + '.h', + blurb, pydoc) def write(self, output_dir): - self._genc.write(output_dir, self._prefix + self._what + '.c') - self._genh.write(output_dir, self._prefix + self._what + '.h') + self._genc.write(output_dir) + self._genh.write(output_dir) class QAPISchemaModularCVisitor(QAPISchemaVisitor): @@ -2259,51 +2325,86 @@ class QAPISchemaModularCVisitor(QAPISchemaVisitor): self._what = what self._blurb = blurb self._pydoc = pydoc + self._genc = None + self._genh = None self._module = {} self._main_module = None + @staticmethod + def _is_user_module(name): + return name and not name.startswith('./') + + @staticmethod + def _is_builtin_module(name): + return not name + + def _module_dirname(self, what, name): + if self._is_user_module(name): + return os.path.dirname(name) + return '' + def _module_basename(self, what, name): - if name is None: - return re.sub(r'-', '-builtin-', what) - basename = os.path.join(os.path.dirname(name), - self._prefix + what) - if name == self._main_module: - return basename - return basename + '-' + os.path.splitext(os.path.basename(name))[0] + ret = '' if self._is_builtin_module(name) else self._prefix + if self._is_user_module(name): + basename = os.path.basename(name) + ret += what + if name != self._main_module: + ret += '-' + os.path.splitext(basename)[0] + else: + name = name[2:] if name else 'builtin' + ret += re.sub(r'-', '-' + name + '-', what) + return ret + + def _module_filename(self, what, name): + return os.path.join(self._module_dirname(what, name), + self._module_basename(what, name)) def _add_module(self, name, blurb): - if self._main_module is None and name is not None: - self._main_module = name - genc = QAPIGenC(blurb, self._pydoc) - genh = QAPIGenH(blurb, self._pydoc) + basename = self._module_filename(self._what, name) + genc = QAPIGenC(basename + '.c', blurb, self._pydoc) + genh = QAPIGenH(basename + '.h', blurb, self._pydoc) self._module[name] = (genc, genh) self._set_module(name) + def _add_user_module(self, name, blurb): + assert self._is_user_module(name) + if self._main_module is None: + self._main_module = name + self._add_module(name, blurb) + + def _add_system_module(self, name, blurb): + self._add_module(name and './' + name, blurb) + def _set_module(self, name): self._genc, self._genh = self._module[name] def write(self, output_dir, opt_builtins=False): for name in self._module: - if name is None and not opt_builtins: + if self._is_builtin_module(name) and not opt_builtins: continue - basename = self._module_basename(self._what, name) (genc, genh) = self._module[name] - genc.write(output_dir, basename + '.c') - genh.write(output_dir, basename + '.h') + genc.write(output_dir) + genh.write(output_dir) - def _begin_module(self, name): + def _begin_user_module(self, name): pass def visit_module(self, name): if name in self._module: self._set_module(name) - return - self._add_module(name, self._blurb) - self._begin_module(name) + elif self._is_builtin_module(name): + # The built-in module has not been created. No code may + # be generated. + self._genc = None + self._genh = None + else: + self._add_user_module(name, self._blurb) + self._begin_user_module(name) def visit_include(self, name, info): - basename = self._module_basename(self._what, name) + relname = os.path.relpath(self._module_filename(self._what, name), + os.path.dirname(self._genh.fname)) self._genh.preamble_add(mcgen(''' -#include "%(basename)s.h" +#include "%(relname)s.h" ''', - basename=basename)) + relname=relname)) diff --git a/scripts/qapi/doc.py b/scripts/qapi/doc.py index 987fd3c943..5c8c136899 100755 --- a/scripts/qapi/doc.py +++ b/scripts/qapi/doc.py @@ -126,19 +126,27 @@ def texi_body(doc): return texi_format(doc.body.text) -def texi_enum_value(value): +def texi_if(ifcond, prefix='\n', suffix='\n'): + """Format the #if condition""" + if not ifcond: + return '' + return '%s@b{If:} @code{%s}%s' % (prefix, ', '.join(ifcond), suffix) + + +def texi_enum_value(value, desc, suffix): """Format a table of members item for an enumeration value""" - return '@item @code{%s}\n' % value.name + return '@item @code{%s}\n%s%s' % ( + value.name, desc, texi_if(value.ifcond, prefix='@*')) -def texi_member(member, suffix=''): +def texi_member(member, desc, suffix): """Format a table of members item for an object type member""" typ = member.type.doc_type() membertype = ': ' + typ if typ else '' - return '@item @code{%s%s}%s%s\n' % ( + return '@item @code{%s%s}%s%s\n%s%s' % ( member.name, membertype, ' (optional)' if member.optional else '', - suffix) + suffix, desc, texi_if(member.ifcond, prefix='@*')) def texi_members(doc, what, base, variants, member_func): @@ -155,17 +163,17 @@ def texi_members(doc, what, base, variants, member_func): desc = 'One of ' + members_text + '\n' else: desc = 'Not documented\n' - items += member_func(section.member) + desc + items += member_func(section.member, desc, suffix='') if base: items += '@item The members of @code{%s}\n' % base.doc_type() if variants: for v in variants.variants: - when = ' when @code{%s} is @t{"%s"}' % ( - variants.tag_member.name, v.name) + when = ' when @code{%s} is @t{"%s"}%s' % ( + variants.tag_member.name, v.name, texi_if(v.ifcond, " (", ")")) if v.type.is_implicit(): assert not v.type.base and not v.type.variants for m in v.type.local_members: - items += member_func(m, when) + items += member_func(m, desc='', suffix=when) else: items += '@item The members of @code{%s}%s\n' % ( v.type.doc_type(), when) @@ -185,8 +193,7 @@ def texi_sections(doc, ifcond): body += texi_example(section.text) else: body += texi_format(section.text) - if ifcond: - body += '\n\n@b{If:} @code{%s}' % ", ".join(ifcond) + body += texi_if(ifcond, suffix='') return body @@ -200,13 +207,13 @@ def texi_entity(doc, what, ifcond, base=None, variants=None, class QAPISchemaGenDocVisitor(qapi.common.QAPISchemaVisitor): def __init__(self, prefix): self._prefix = prefix - self._gen = qapi.common.QAPIGenDoc() + self._gen = qapi.common.QAPIGenDoc(self._prefix + 'qapi-doc.texi') self.cur_doc = None def write(self, output_dir): - self._gen.write(output_dir, self._prefix + 'qapi-doc.texi') + self._gen.write(output_dir) - def visit_enum_type(self, name, info, ifcond, values, prefix): + def visit_enum_type(self, name, info, ifcond, members, prefix): doc = self.cur_doc self._gen.add(TYPE_FMT(type='Enum', name=doc.symbol, diff --git a/scripts/qapi/events.py b/scripts/qapi/events.py index 764ef177ab..2067660be4 100644 --- a/scripts/qapi/events.py +++ b/scripts/qapi/events.py @@ -18,7 +18,7 @@ from qapi.common import * def build_event_send_proto(name, arg_type, boxed): return 'void qapi_event_send_%(c_name)s(%(param)s)' % { 'c_name': c_name(name.lower()), - 'param': build_params(arg_type, boxed, 'Error **errp')} + 'param': build_params(arg_type, boxed)} def gen_event_send_decl(name, arg_type, boxed): @@ -58,7 +58,7 @@ def gen_param_var(typ): return ret -def gen_event_send(name, arg_type, boxed, event_enum_name): +def gen_event_send(name, arg_type, boxed, event_enum_name, event_emit): # 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 @@ -70,8 +70,6 @@ def gen_event_send(name, arg_type, boxed, event_enum_name): %(proto)s { QDict *qmp; - Error *err = NULL; - QMPEventFuncEmit emit; ''', proto=build_event_send_proto(name, arg_type, boxed)) @@ -87,11 +85,6 @@ def gen_event_send(name, arg_type, boxed, event_enum_name): ret += mcgen(''' - emit = qmp_event_get_func_emit(); - if (!emit) { - return; - } - qmp = qmp_event_build_dict("%(name)s"); ''', @@ -103,45 +96,36 @@ def gen_event_send(name, arg_type, boxed, event_enum_name): ''') if not arg_type.is_implicit(): ret += mcgen(''' - visit_type_%(c_name)s(v, "%(name)s", &arg, &err); + visit_type_%(c_name)s(v, "%(name)s", &arg, &error_abort); ''', name=name, c_name=arg_type.c_name()) else: ret += mcgen(''' - visit_start_struct(v, "%(name)s", NULL, 0, &err); - if (err) { - goto out; - } - visit_type_%(c_name)s_members(v, ¶m, &err); - if (!err) { - visit_check_struct(v, &err); - } + visit_start_struct(v, "%(name)s", NULL, 0, &error_abort); + visit_type_%(c_name)s_members(v, ¶m, &error_abort); + visit_check_struct(v, &error_abort); visit_end_struct(v, NULL); ''', name=name, c_name=arg_type.c_name()) ret += mcgen(''' - if (err) { - goto out; - } visit_complete(v, &obj); qdict_put_obj(qmp, "data", obj); ''') ret += mcgen(''' - emit(%(c_enum)s, qmp, &err); + %(event_emit)s(%(c_enum)s, qmp); ''', + event_emit=event_emit, c_enum=c_enum_const(event_enum_name, name)) if arg_type and not arg_type.is_empty(): ret += mcgen(''' -out: visit_free(v); ''') ret += mcgen(''' - error_propagate(errp, err); qobject_unref(qmp); } ''') @@ -154,16 +138,19 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-events', ' * Schema-defined QAPI/QMP events', __doc__) - self._enum_name = c_name(prefix + 'QAPIEvent', protect=False) - self._event_names = [] + self._event_enum_name = c_name(prefix + 'QAPIEvent', protect=False) + self._event_enum_members = [] + self._event_emit_name = c_name(prefix + 'qapi_event_emit') - def _begin_module(self, name): + def _begin_user_module(self, name): + events = self._module_basename('qapi-events', name) types = self._module_basename('qapi-types', name) visit = self._module_basename('qapi-visit', name) self._genc.add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" -#include "%(prefix)sqapi-events.h" +#include "%(prefix)sqapi-emit-events.h" +#include "%(events)s.h" #include "%(visit)s.h" #include "qapi/error.h" #include "qapi/qmp/qdict.h" @@ -171,25 +158,44 @@ class QAPISchemaGenEventVisitor(QAPISchemaModularCVisitor): #include "qapi/qmp-event.h" ''', - visit=visit, prefix=self._prefix)) + events=events, visit=visit, + prefix=self._prefix)) self._genh.add(mcgen(''' #include "qapi/util.h" #include "%(types)s.h" - ''', types=types)) def visit_end(self): - (genc, genh) = self._module[self._main_module] - genh.add(gen_enum(self._enum_name, self._event_names)) - genc.add(gen_enum_lookup(self._enum_name, self._event_names)) + self._add_system_module('emit', ' * QAPI Events emission') + self._genc.preamble_add(mcgen(''' +#include "qemu/osdep.h" +#include "%(prefix)sqapi-emit-events.h" +''', + prefix=self._prefix)) + self._genh.preamble_add(mcgen(''' +#include "qapi/util.h" +''')) + self._genh.add(gen_enum(self._event_enum_name, + self._event_enum_members)) + self._genc.add(gen_enum_lookup(self._event_enum_name, + self._event_enum_members)) + self._genh.add(mcgen(''' + +void %(event_emit)s(%(event_enum)s event, QDict *qdict); +''', + event_emit=self._event_emit_name, + event_enum=self._event_enum_name)) def visit_event(self, name, info, ifcond, arg_type, boxed): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_event_send_decl(name, arg_type, boxed)) self._genc.add(gen_event_send(name, arg_type, boxed, - self._enum_name)) - self._event_names.append(name) + self._event_enum_name, + self._event_emit_name)) + # Note: we generate the enum member regardless of @ifcond, to + # keep the enumeration usable in target-independent code. + self._event_enum_members.append(QAPISchemaMember(name)) def gen_events(schema, output_dir, prefix): diff --git a/scripts/qapi/introspect.py b/scripts/qapi/introspect.py index 189a4edaba..f7f2ca07e4 100644 --- a/scripts/qapi/introspect.py +++ b/scripts/qapi/introspect.py @@ -19,12 +19,17 @@ def to_qlit(obj, level=0, suppress_first_indent=False): return level * 4 * ' ' if isinstance(obj, tuple): - ifobj, ifcond = obj - ret = gen_if(ifcond) + ifobj, extra = obj + ifcond = extra.get('if') + comment = extra.get('comment') + ret = '' + if comment: + ret += indent(level) + '/* %s */\n' % comment + if ifcond: + ret += gen_if(ifcond) ret += to_qlit(ifobj, level) - endif = gen_endif(ifcond) - if endif: - ret += '\n' + endif + if ifcond: + ret += '\n' + gen_endif(ifcond) return ret ret = '' @@ -89,7 +94,6 @@ class QAPISchemaGenIntrospectVisitor(QAPISchemaMonolithicCVisitor): for typ in self._used_types: typ.visit(self) # generate C - # TODO can generate awfully long lines name = c_name(self._prefix, protect=False) + 'qmp_schema_qlit' self._genh.add(mcgen(''' #include "qapi/qmp/qlit.h" @@ -129,8 +133,8 @@ const QLitObject %(c_name)s = %(c_string)s; 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. + # type names as integers to reduce the temptation. Also, it + # saves a few characters on the wire. if isinstance(typ, QAPISchemaBuiltinType): return typ.name if isinstance(typ, QAPISchemaArrayType): @@ -138,16 +142,28 @@ const QLitObject %(c_name)s = %(c_string)s; return self._name(typ.name) def _gen_qlit(self, name, mtype, obj, ifcond): + extra = {} if mtype not in ('command', 'event', 'builtin', 'array'): + if not self._unmask: + # Output a comment to make it easy to map masked names + # back to the source when reading the generated output. + extra['comment'] = '"%s" = %s' % (self._name(name), name) name = self._name(name) obj['name'] = name obj['meta-type'] = mtype - self._qlits.append((obj, ifcond)) + if ifcond: + extra['if'] = ifcond + if extra: + self._qlits.append((obj, extra)) + else: + self._qlits.append(obj) def _gen_member(self, member): ret = {'name': member.name, 'type': self._use_type(member.type)} if member.optional: ret['default'] = None + if member.ifcond: + ret = (ret, {'if': member.ifcond}) return ret def _gen_variants(self, tag_name, variants): @@ -155,13 +171,17 @@ const QLitObject %(c_name)s = %(c_string)s; 'variants': [self._gen_variant(v) for v in variants]} def _gen_variant(self, variant): - return {'case': variant.name, 'type': self._use_type(variant.type)} + return ({'case': variant.name, 'type': self._use_type(variant.type)}, + {'if': variant.ifcond}) def visit_builtin_type(self, name, info, json_type): self._gen_qlit(name, 'builtin', {'json-type': json_type}, []) - def visit_enum_type(self, name, info, ifcond, values, prefix): - self._gen_qlit(name, 'enum', {'values': values}, ifcond) + def visit_enum_type(self, name, info, ifcond, members, prefix): + self._gen_qlit(name, 'enum', + {'values': + [(m.name, {'if': m.ifcond}) for m in members]}, + ifcond) def visit_array_type(self, name, info, ifcond, element_type): element = self._use_type(element_type) @@ -177,15 +197,16 @@ const QLitObject %(c_name)s = %(c_string)s; def visit_alternate_type(self, name, info, ifcond, variants): self._gen_qlit(name, 'alternate', - {'members': [{'type': self._use_type(m.type)} - for m in variants.variants]}, ifcond) + {'members': [ + ({'type': self._use_type(m.type)}, {'if': m.ifcond}) + for m in variants.variants]}, ifcond) def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): arg_type = arg_type or self._schema.the_empty_object_type ret_type = ret_type or self._schema.the_empty_object_type obj = {'arg-type': self._use_type(arg_type), - 'ret-type': self._use_type(ret_type) } + 'ret-type': self._use_type(ret_type)} if allow_oob: obj['allow-oob'] = allow_oob self._gen_qlit(name, 'command', obj, ifcond) diff --git a/scripts/qapi/types.py b/scripts/qapi/types.py index fd7808103c..2bd6fcd44f 100644 --- a/scripts/qapi/types.py +++ b/scripts/qapi/types.py @@ -43,6 +43,7 @@ struct %(c_name)s { def gen_struct_members(members): ret = '' for memb in members: + ret += gen_if(memb.ifcond) if memb.optional: ret += mcgen(''' bool has_%(c_name)s; @@ -52,6 +53,7 @@ def gen_struct_members(members): %(c_type)s %(c_name)s; ''', c_type=memb.type.c_type(), c_name=c_name(memb.name)) + ret += gen_endif(memb.ifcond) return ret @@ -131,11 +133,13 @@ def gen_variants(variants): for var in variants.variants: if var.type.name == 'q_empty': continue + ret += gen_if(var.ifcond) ret += mcgen(''' %(c_type)s %(c_name)s; ''', c_type=var.type.c_unboxed_type(), c_name=c_name(var.name)) + ret += gen_endif(var.ifcond) ret += mcgen(''' } u; @@ -179,7 +183,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-types', ' * Schema-defined QAPI types', __doc__) - self._add_module(None, ' * Built-in QAPI types') + self._add_system_module(None, ' * Built-in QAPI types') self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qapi/dealloc-visitor.h" @@ -190,7 +194,7 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): #include "qapi/util.h" ''')) - def _begin_module(self, name): + def _begin_user_module(self, name): types = self._module_basename('qapi-types', name) visit = self._module_basename('qapi-visit', name) self._genc.preamble_add(mcgen(''' @@ -212,10 +216,10 @@ class QAPISchemaGenTypeVisitor(QAPISchemaModularCVisitor): self._genh.add(gen_type_cleanup_decl(name)) self._genc.add(gen_type_cleanup(name)) - def visit_enum_type(self, name, info, ifcond, values, prefix): + def visit_enum_type(self, name, info, ifcond, members, prefix): with ifcontext(ifcond, self._genh, self._genc): - self._genh.preamble_add(gen_enum(name, values, prefix)) - self._genc.add(gen_enum_lookup(name, values, prefix)) + self._genh.preamble_add(gen_enum(name, members, prefix)) + self._genc.add(gen_enum_lookup(name, members, prefix)) def visit_array_type(self, name, info, ifcond, element_type): with ifcontext(ifcond, self._genh, self._genc): diff --git a/scripts/qapi/visit.py b/scripts/qapi/visit.py index dd5034a66a..826b8066e1 100644 --- a/scripts/qapi/visit.py +++ b/scripts/qapi/visit.py @@ -54,6 +54,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) c_type=base.c_name()) for memb in members: + ret += gen_if(memb.ifcond) if memb.optional: ret += mcgen(''' if (visit_optional(v, "%(name)s", &obj->has_%(c_name)s)) { @@ -73,6 +74,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) ret += mcgen(''' } ''') + ret += gen_endif(memb.ifcond) if variants: ret += mcgen(''' @@ -84,6 +86,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) case_str = c_enum_const(variants.tag_member.type.name, var.name, variants.tag_member.type.prefix) + ret += gen_if(var.ifcond) if var.type.name == 'q_empty': # valid variant and nothing to do ret += mcgen(''' @@ -100,6 +103,7 @@ void visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp) case=case_str, c_type=var.type.c_name(), c_name=c_name(var.name)) + ret += gen_endif(var.ifcond) ret += mcgen(''' default: abort(); @@ -187,9 +191,10 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error } switch ((*obj)->type) { ''', - c_name=c_name(name)) + c_name=c_name(name)) for var in variants.variants: + ret += gen_if(var.ifcond) ret += mcgen(''' case %(case)s: ''', @@ -217,6 +222,7 @@ void visit_type_%(c_name)s(Visitor *v, const char *name, %(c_name)s **obj, Error ret += mcgen(''' break; ''') + ret += gen_endif(var.ifcond) ret += mcgen(''' case QTYPE_NONE: @@ -278,7 +284,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): QAPISchemaModularCVisitor.__init__( self, prefix, 'qapi-visit', ' * Schema-defined QAPI visitors', __doc__) - self._add_module(None, ' * Built-in QAPI visitors') + self._add_system_module(None, ' * Built-in QAPI visitors') self._genc.preamble_add(mcgen(''' #include "qemu/osdep.h" #include "qemu-common.h" @@ -292,7 +298,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', prefix=prefix)) - def _begin_module(self, name): + def _begin_user_module(self, name): types = self._module_basename('qapi-types', name) visit = self._module_basename('qapi-visit', name) self._genc.preamble_add(mcgen(''' @@ -310,7 +316,7 @@ class QAPISchemaGenVisitVisitor(QAPISchemaModularCVisitor): ''', types=types)) - def visit_enum_type(self, name, info, ifcond, values, prefix): + def visit_enum_type(self, name, info, ifcond, members, prefix): with ifcontext(ifcond, self._genh, self._genc): self._genh.add(gen_visit_decl(name, scalar=True)) self._genc.add(gen_visit_enum(name)) diff --git a/scripts/qemu-binfmt-conf.sh b/scripts/qemu-binfmt-conf.sh index b0dc8a714a..b5a16742a1 100755 --- a/scripts/qemu-binfmt-conf.sh +++ b/scripts/qemu-binfmt-conf.sh @@ -4,7 +4,7 @@ qemu_target_list="i386 i486 alpha arm armeb sparc32plus ppc ppc64 ppc64le m68k \ mips mipsel mipsn32 mipsn32el mips64 mips64el \ sh4 sh4eb s390x aarch64 aarch64_be hppa riscv32 riscv64 xtensa xtensaeb \ -microblaze microblazeel or1k" +microblaze microblazeel or1k x86_64" i386_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x03\x00' i386_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' @@ -14,6 +14,10 @@ i486_magic='\x7fELF\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x06\ i486_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' i486_family=i386 +x86_64_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00' +x86_64_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' +x86_64_family=i386 + alpha_magic='\x7fELF\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x26\x90' alpha_mask='\xff\xff\xff\xff\xff\xfe\xfe\x00\xff\xff\xff\xff\xff\xff\xff\xff\xfe\xff\xff\xff' alpha_family=alpha diff --git a/scripts/qemu-gdb.py b/scripts/qemu-gdb.py index 690827e6fc..f2a305c42e 100644 --- a/scripts/qemu-gdb.py +++ b/scripts/qemu-gdb.py @@ -7,11 +7,8 @@ # 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. +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. # Usage: # At the (gdb) prompt, type "source scripts/qemu-gdb.py". diff --git a/scripts/qemu-guest-agent/fsfreeze-hook b/scripts/qemu-guest-agent/fsfreeze-hook index c27b29f282..13aafd4845 100755 --- a/scripts/qemu-guest-agent/fsfreeze-hook +++ b/scripts/qemu-guest-agent/fsfreeze-hook @@ -13,7 +13,7 @@ FSFREEZE_D=$(dirname -- "$0")/fsfreeze-hook.d # Check whether file $1 is a backup or rpm-generated file and should be ignored is_ignored_file() { case "$1" in - *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample) + *~ | *.bak | *.orig | *.rpmnew | *.rpmorig | *.rpmsave | *.sample | *.dpkg-old | *.dpkg-new | *.dpkg-tmp | *.dpkg-dist | *.dpkg-bak | *.dpkg-backup | *.dpkg-remove) return 0 ;; esac return 1 diff --git a/scripts/qemu-trace-stap b/scripts/qemu-trace-stap new file mode 100755 index 0000000000..91d1051cdc --- /dev/null +++ b/scripts/qemu-trace-stap @@ -0,0 +1,175 @@ +#!/usr/bin/python +# -*- python -*- +# +# Copyright (C) 2019 Red Hat, Inc +# +# QEMU SystemTap Trace Tool +# +# 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 . + +from __future__ import print_function + +import argparse +import copy +import os.path +import re +import subprocess +import sys + + +def probe_prefix(binary): + dirname, filename = os.path.split(binary) + return re.sub("-", ".", filename) + ".log" + + +def which(binary): + for path in os.environ["PATH"].split(os.pathsep): + if os.path.exists(os.path.join(path, binary)): + return os.path.join(path, binary) + + print("Unable to find '%s' in $PATH" % binary) + sys.exit(1) + + +def tapset_dir(binary): + dirname, filename = os.path.split(binary) + if dirname == '': + thisfile = which(binary) + else: + thisfile = os.path.realpath(binary) + if not os.path.exists(thisfile): + print("Unable to find '%s'" % thisfile) + sys.exit(1) + + basedir = os.path.split(thisfile)[0] + tapset = os.path.join(basedir, "..", "share", "systemtap", "tapset") + return os.path.realpath(tapset) + + +def tapset_env(tapset_dir): + tenv = copy.copy(os.environ) + tenv["SYSTEMTAP_TAPSET"] = tapset_dir + return tenv + +def cmd_run(args): + prefix = probe_prefix(args.binary) + tapsets = tapset_dir(args.binary) + + if args.verbose: + print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) + + probes = [] + for probe in args.probes: + probes.append("probe %s.%s {}" % (prefix, probe)) + if len(probes) == 0: + print("At least one probe pattern must be specified") + sys.exit(1) + + script = " ".join(probes) + if args.verbose: + print("Compiling script '%s'" % script) + script = """probe begin { print("Running script, -c to quit\\n") } """ + script + + # We request an 8MB buffer, since the stap default 1MB buffer + # can be easily overflowed by frequently firing QEMU traces + stapargs = ["stap", "-s", "8"] + if args.pid is not None: + stapargs.extend(["-x", args.pid]) + stapargs.extend(["-e", script]) + subprocess.call(stapargs, env=tapset_env(tapsets)) + + +def cmd_list(args): + tapsets = tapset_dir(args.binary) + + if args.verbose: + print("Using tapset dir '%s' for binary '%s'" % (tapsets, args.binary)) + + def print_probes(verbose, name): + prefix = probe_prefix(args.binary) + offset = len(prefix) + 1 + script = prefix + "." + name + + if verbose: + print("Listing probes with name '%s'" % script) + proc = subprocess.Popen(["stap", "-l", script], + stdout=subprocess.PIPE, env=tapset_env(tapsets)) + out, err = proc.communicate() + if proc.returncode != 0: + print("No probes found, are the tapsets installed in %s" % tapset_dir(args.binary)) + sys.exit(1) + + for line in out.splitlines(): + if line.startswith(prefix): + print("%s" % line[offset:]) + + if len(args.probes) == 0: + print_probes(args.verbose, "*") + else: + for probe in args.probes: + print_probes(args.verbose, probe) + + +def main(): + parser = argparse.ArgumentParser(description="QEMU SystemTap trace tool") + parser.add_argument("-v", "--verbose", help="Print verbose progress info", + action='store_true') + + subparser = parser.add_subparsers(help="commands") + subparser.required = True + subparser.dest = "command" + + runparser = subparser.add_parser("run", help="Run a trace session", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" + +To watch all trace points on the qemu-system-x86_64 binary: + + %(argv0)s run qemu-system-x86_64 + +To only watch the trace points matching the qio* and qcrypto* patterns + + %(argv0)s run qemu-system-x86_64 'qio*' 'qcrypto*' +""" % {"argv0": sys.argv[0]}) + runparser.set_defaults(func=cmd_run) + runparser.add_argument("--pid", "-p", dest="pid", + help="Restrict tracing to a specific process ID") + runparser.add_argument("binary", help="QEMU system or user emulator binary") + runparser.add_argument("probes", help="Probe names or wildcards", + nargs=argparse.REMAINDER) + + listparser = subparser.add_parser("list", help="List probe points", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=""" + +To list all trace points on the qemu-system-x86_64 binary: + + %(argv0)s list qemu-system-x86_64 + +To only list the trace points matching the qio* and qcrypto* patterns + + %(argv0)s list qemu-system-x86_64 'qio*' 'qcrypto*' +""" % {"argv0": sys.argv[0]}) + listparser.set_defaults(func=cmd_list) + listparser.add_argument("binary", help="QEMU system or user emulator binary") + listparser.add_argument("probes", help="Probe names or wildcards", + nargs=argparse.REMAINDER) + + args = parser.parse_args() + + args.func(args) + sys.exit(0) + +if __name__ == '__main__': + main() diff --git a/scripts/qemu-trace-stap.texi b/scripts/qemu-trace-stap.texi new file mode 100644 index 0000000000..07bb9eb94e --- /dev/null +++ b/scripts/qemu-trace-stap.texi @@ -0,0 +1,140 @@ +@example +@c man begin SYNOPSIS +@command{qemu-trace-stap} @var{GLOBAL-OPTIONS} @var{COMMAND} @var{COMMAND-OPTIONS} @var{ARGS...} +@c man end +@end example + +@c man begin DESCRIPTION + +The @command{qemu-trace-stap} program facilitates tracing of the execution +of QEMU emulators using SystemTap. + +It is required to have the SystemTap runtime environment installed to use +this program, since it is a wrapper around execution of the @command{stap} +program. + +@c man end + +@c man begin OPTIONS + +The following global options may be used regardless of which command +is executed: + +@table @option +@item @var{--verbose}, @var{-v} + +Display verbose information about command execution. + +@end table + +The following commands are valid: + +@table @option + +@item @var{list} @var{BINARY} @var{PATTERN...} + +List all the probe names provided by @var{BINARY} that match +@var{PATTERN}. + +If @var{BINARY} is not an absolute path, it will be located by searching +the directories listed in the @code{$PATH} environment variable. + +@var{PATTERN} is a plain string that is used to filter the results of +this command. It may optionally contain a @code{*} wildcard to facilitate +matching multiple probes without listing each one explicitly. Multiple +@var{PATTERN} arguments may be given, causing listing of probes that match +any of the listed names. If no @var{PATTERN} is given, the all possible +probes will be listed. + +For example, to list all probes available in the @command{qemu-system-x86_64} +binary: + +@example +$ qemu-trace-stap list qemu-system-x86_64 +@end example + +To filter the list to only cover probes related to QEMU's cryptographic +subsystem, in a binary outside @code{$PATH} + +@example +$ qemu-trace-stap list /opt/qemu/4.0.0/bin/qemu-system-x86_64 'qcrypto*' +@end example + + +@item @var{run} @var{OPTIONS} @var{BINARY} @var{PATTERN...} + +Run a trace session, printing formatted output any time a process that is +executing @var{BINARY} triggers a probe matching @var{PATTERN}. + +If @var{BINARY} is not an absolute path, it will be located by searching +the directories listed in the @code{$PATH} environment variable. + +@var{PATTERN} is a plain string that matches a probe name shown by the +@var{list} command. It may optionally contain a @code{*} wildcard to +facilitate matching multiple probes without listing each one explicitly. +Multiple @var{PATTERN} arguments may be given, causing all matching probes +to be monitored. At least one @var{PATTERN} is required, since stap is not +capable of tracing all known QEMU probes concurrently without overflowing +its trace buffer. + +Invocation of this command does not need to be synchronized with +invocation of the QEMU process(es). It will match probes on all +existing running processes and all future launched processes, +unless told to only monitor a specific process. + +Valid command specific options are: + +@table @option +@item @var{--pid=PID}, @var{-p PID} + +Restrict the tracing session so that it only triggers for the process +identified by @code{PID}. + +@end table + +For example, to monitor all processes executing @command{qemu-system-x86_64} +as found on $PATH, displaying all I/O related probes: + +@example +$ qemu-trace-stap run qemu-system-x86_64 'qio*' +@end example + +To monitor only the QEMU process with PID 1732 + +@example +$ qemu-trace-stap run --pid=1732 qemu-system-x86_64 'qio*' +@end example + +To monitor QEMU processes running an alternative binary outside of +@code{$PATH}, displaying verbose information about setup of the +tracing environment: + +@example +$ qemu-trace-stap -v run /opt/qemu/4.0.0/qemu-system-x86_64 'qio*' +@end example + +@end table + +@c man end + +@ignore + +@setfilename qemu-trace-stap +@settitle QEMU SystemTap trace tool + +@c man begin LICENSE + +Copyright (C) 2019 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. + +@c man end + +@c man begin SEEALSO +qemu(1), stap(1) +@c man end + +@end ignore diff --git a/scripts/qemugdb/coroutine.py b/scripts/qemugdb/coroutine.py index ab699794ab..41e079d0e2 100644 --- a/scripts/qemugdb/coroutine.py +++ b/scripts/qemugdb/coroutine.py @@ -7,11 +7,8 @@ # 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. +# 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 @@ -22,7 +19,7 @@ def get_fs_base(): 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) + gdb.execute('call (int)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 diff --git a/scripts/qemugdb/mtree.py b/scripts/qemugdb/mtree.py index e6791b7885..3030a60d3f 100644 --- a/scripts/qemugdb/mtree.py +++ b/scripts/qemugdb/mtree.py @@ -7,11 +7,8 @@ # 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. +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. # 'qemu mtree' -- display the memory hierarchy diff --git a/scripts/qemugdb/tcg.py b/scripts/qemugdb/tcg.py index 8c7f1d7454..18880fc9a7 100644 --- a/scripts/qemugdb/tcg.py +++ b/scripts/qemugdb/tcg.py @@ -8,11 +8,8 @@ # Authors: # Alex Bennée # -# 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. +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. # 'qemu tcg-lock-status' -- display the TCG lock status across threads diff --git a/scripts/qemugdb/timers.py b/scripts/qemugdb/timers.py index 51ea04b5e2..f0e132d27a 100644 --- a/scripts/qemugdb/timers.py +++ b/scripts/qemugdb/timers.py @@ -6,8 +6,10 @@ # # Author: Alex Bennée # -# 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. +# +# SPDX-License-Identifier: GPL-2.0-or-later # 'qemu timers' -- display the current timerlists diff --git a/scripts/qmp/qemu-ga-client b/scripts/qmp/qemu-ga-client index e8cb7646a0..30cf8a9a0d 100755 --- a/scripts/qmp/qemu-ga-client +++ b/scripts/qmp/qemu-ga-client @@ -37,10 +37,13 @@ # from __future__ import print_function +import os +import sys import base64 import random -import qmp +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import qmp class QemuGuestAgent(qmp.QEMUMonitorProtocol): diff --git a/scripts/qmp/qmp-shell b/scripts/qmp/qmp-shell index a42306dd89..7776c7b141 100755 --- a/scripts/qmp/qmp-shell +++ b/scripts/qmp/qmp-shell @@ -66,7 +66,6 @@ # sent to QEMU, which is useful for debugging and documentation generation. from __future__ import print_function -import qmp import json import ast import readline @@ -74,6 +73,10 @@ import sys import os import errno import atexit +import re + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import qmp class QMPCompleter(list): def complete(self, text, state): @@ -219,7 +222,7 @@ class QMPShell(qmp.QEMUMonitorProtocol): < command-name > [ arg-name1=arg1 ] ... [ arg-nameN=argN ] """ - cmdargs = cmdline.split() + cmdargs = re.findall(r'''(?:[^\s"']|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')+''', cmdline) # Transactional CLI entry/exit: if cmdargs[0] == 'transaction(': diff --git a/scripts/render_block_graph.py b/scripts/render_block_graph.py new file mode 100755 index 0000000000..3e9d282a49 --- /dev/null +++ b/scripts/render_block_graph.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# +# Render Qemu Block Graph +# +# Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import sys +import subprocess +import json +from graphviz import Digraph + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'python')) +from qemu import MonitorResponseError + + +def perm(arr): + s = 'w' if 'write' in arr else '_' + s += 'r' if 'consistent-read' in arr else '_' + s += 'u' if 'write-unchanged' in arr else '_' + s += 'g' if 'graph-mod' in arr else '_' + s += 's' if 'resize' in arr else '_' + return s + + +def render_block_graph(qmp, filename, format='png'): + ''' + Render graph in text (dot) representation into "@filename" and + representation in @format into "@filename.@format" + ''' + + bds_nodes = qmp.command('query-named-block-nodes') + bds_nodes = {n['node-name']: n for n in bds_nodes} + + job_nodes = qmp.command('query-block-jobs') + job_nodes = {n['device']: n for n in job_nodes} + + block_graph = qmp.command('x-debug-query-block-graph') + + graph = Digraph(comment='Block Nodes Graph') + graph.format = format + graph.node('permission symbols:\l' + ' w - Write\l' + ' r - consistent-Read\l' + ' u - write - Unchanged\l' + ' g - Graph-mod\l' + ' s - reSize\l' + 'edge label scheme:\l' + ' \l' + ' \l' + ' \l', shape='none') + + for n in block_graph['nodes']: + if n['type'] == 'block-driver': + info = bds_nodes[n['name']] + label = n['name'] + ' [' + info['drv'] + ']' + if info['drv'] == 'file': + label += '\n' + os.path.basename(info['file']) + shape = 'ellipse' + elif n['type'] == 'block-job': + info = job_nodes[n['name']] + label = info['type'] + ' job (' + n['name'] + ')' + shape = 'box' + else: + assert n['type'] == 'block-backend' + label = n['name'] if n['name'] else 'unnamed blk' + shape = 'box' + + graph.node(str(n['id']), label, shape=shape) + + for e in block_graph['edges']: + label = '%s\l%s\l%s\l' % (e['name'], perm(e['perm']), + perm(e['shared-perm'])) + graph.edge(str(e['parent']), str(e['child']), label=label) + + graph.render(filename) + + +class LibvirtGuest(): + def __init__(self, name): + self.name = name + + def command(self, cmd): + # only supports qmp commands without parameters + m = {'execute': cmd} + ar = ['virsh', 'qemu-monitor-command', self.name, json.dumps(m)] + + reply = json.loads(subprocess.check_output(ar)) + + if 'error' in reply: + raise MonitorResponseError(reply) + + return reply['return'] + + +if __name__ == '__main__': + obj = sys.argv[1] + out = sys.argv[2] + + if os.path.exists(obj): + # assume unix socket + qmp = QEMUMonitorProtocol(obj) + qmp.connect() + else: + # assume libvirt guest name + qmp = LibvirtGuest(obj) + + render_block_graph(qmp, out) diff --git a/scripts/replay-dump.py b/scripts/replay-dump.py index 5ae77c8a92..ee7fda2638 100755 --- a/scripts/replay-dump.py +++ b/scripts/replay-dump.py @@ -3,7 +3,7 @@ # # Dump the contents of a recorded execution stream # -# Copyright (c) 2017 Alex Bennée +# Copyright (c) 2017 Alex Bennée # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public diff --git a/scripts/show-fixed-bugs.sh b/scripts/show-fixed-bugs.sh index 36f306898f..a095a4d6ba 100755 --- a/scripts/show-fixed-bugs.sh +++ b/scripts/show-fixed-bugs.sh @@ -23,10 +23,10 @@ while getopts "s:e:cbh" opt; do done if [ "x$start" = "x" ]; then - start=`git tag -l 'v[0-9]*\.[0-9]*\.0' | tail -n 2 | head -n 1` + start=$(git tag -l 'v[0-9]*\.[0-9]*\.0' | tail -n 2 | head -n 1) fi if [ "x$end" = "x" ]; then - end=`git tag -l 'v[0-9]*\.[0-9]*\.0' | tail -n 1` + end=$(git tag -l 'v[0-9]*\.[0-9]*\.0' | tail -n 1) fi if [ "x$start" = "x" ] || [ "x$end" = "x" ]; then @@ -38,9 +38,9 @@ fi echo "Searching git log for bugs in the range $start..$end" urlstr='https://bugs.launchpad.net/\(bugs\|qemu/+bug\)/' -bug_urls=`git log $start..$end \ +bug_urls=$(git log $start..$end \ | sed -n '\,'"$urlstr"', s,\(.*\)\('"$urlstr"'\)\([0-9]*\).*,\2\4,p' \ - | sort -u` + | sort -u) echo Found bug URLs: for i in $bug_urls ; do echo " $i" ; done @@ -68,7 +68,7 @@ elif [ "x$show_in_browser" = "x1" ]; then bugbrowser=xdg-open elif command -v gnome-open >/dev/null 2>&1; then bugbrowser=gnome-open - elif [ "`uname`" = "Darwin" ]; then + elif [ "$(uname)" = "Darwin" ]; then bugbrowser=open elif command -v sensible-browser >/dev/null 2>&1; then bugbrowser=sensible-browser diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py index 4ad34f90cd..45485b864b 100755 --- a/scripts/simpletrace.py +++ b/scripts/simpletrace.py @@ -11,7 +11,6 @@ from __future__ import print_function import struct -import re import inspect from tracetool import read_events, Event from tracetool.backend.simple import is_string diff --git a/scripts/tap-driver.pl b/scripts/tap-driver.pl new file mode 100755 index 0000000000..6621a5cd67 --- /dev/null +++ b/scripts/tap-driver.pl @@ -0,0 +1,379 @@ +#! /usr/bin/env perl +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, 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 . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# ---------------------------------- # +# Imports, static data, and setup. # +# ---------------------------------- # + +use warnings FATAL => 'all'; +use strict; +use Getopt::Long (); +use TAP::Parser; +use Term::ANSIColor qw(:constants); + +my $ME = "tap-driver.pl"; +my $VERSION = "2018-11-30"; + +my $USAGE = <<'END'; +Usage: + tap-driver [--test-name=TEST] [--color={always|never|auto}] + [--verbose] [--show-failures-only] +END + +my $HELP = "$ME: TAP-aware test driver for QEMU testsuite harness." . + "\n" . $USAGE; + +# It's important that NO_PLAN evaluates "false" as a boolean. +use constant NO_PLAN => 0; +use constant EARLY_PLAN => 1; +use constant LATE_PLAN => 2; + +use constant DIAG_STRING => "#"; + +# ------------------- # +# Global variables. # +# ------------------- # + +my $testno = 0; # Number of test results seen so far. +my $bailed_out = 0; # Whether a "Bail out!" directive has been seen. +my $failed = 0; # Final exit code + +# Whether the TAP plan has been seen or not, and if yes, which kind +# it is ("early" is seen before any test result, "late" otherwise). +my $plan_seen = NO_PLAN; + +# ----------------- # +# Option parsing. # +# ----------------- # + +my %cfg = ( + "color" => 0, + "verbose" => 0, + "show-failures-only" => 0, +); + +my $color = "auto"; +my $test_name = undef; + +# Perl's Getopt::Long allows options to take optional arguments after a space. +# Prevent --color by itself from consuming other arguments +foreach (@ARGV) { + if ($_ eq "--color" || $_ eq "-color") { + $_ = "--color=$color"; + } +} + +Getopt::Long::GetOptions + ( + 'help' => sub { print $HELP; exit 0; }, + 'version' => sub { print "$ME $VERSION\n"; exit 0; }, + 'test-name=s' => \$test_name, + 'color=s' => \$color, + 'show-failures-only' => sub { $cfg{"show-failures-only"} = 1; }, + 'verbose' => sub { $cfg{"verbose"} = 1; }, + ) or exit 1; + +if ($color =~ /^always$/i) { + $cfg{'color'} = 1; +} elsif ($color =~ /^never$/i) { + $cfg{'color'} = 0; +} elsif ($color =~ /^auto$/i) { + $cfg{'color'} = (-t STDOUT); +} else { + die "Invalid color mode: $color\n"; +} + +# ------------- # +# Prototypes. # +# ------------- # + +sub colored ($$); +sub decorate_result ($); +sub extract_tap_comment ($); +sub handle_tap_bailout ($); +sub handle_tap_plan ($); +sub handle_tap_result ($); +sub is_null_string ($); +sub main (); +sub report ($;$); +sub stringify_result_obj ($); +sub testsuite_error ($); + +# -------------- # +# Subroutines. # +# -------------- # + +# If the given string is undefined or empty, return true, otherwise +# return false. This function is useful to avoid pitfalls like: +# if ($message) { print "$message\n"; } +# which wouldn't print anything if $message is the literal "0". +sub is_null_string ($) +{ + my $str = shift; + return ! (defined $str and length $str); +} + +sub stringify_result_obj ($) +{ + my $result_obj = shift; + if ($result_obj->is_unplanned || $result_obj->number != $testno) + { + return "ERROR"; + } + elsif ($plan_seen == LATE_PLAN) + { + return "ERROR"; + } + elsif (!$result_obj->directive) + { + return $result_obj->is_ok ? "PASS" : "FAIL"; + } + elsif ($result_obj->has_todo) + { + return $result_obj->is_actual_ok ? "XPASS" : "XFAIL"; + } + elsif ($result_obj->has_skip) + { + return $result_obj->is_ok ? "SKIP" : "FAIL"; + } + die "$ME: INTERNAL ERROR"; # NOTREACHED +} + +sub colored ($$) +{ + my ($color_string, $text) = @_; + return $color_string . $text . RESET; +} + +sub decorate_result ($) +{ + my $result = shift; + return $result unless $cfg{"color"}; + my %color_for_result = + ( + "ERROR" => BOLD.MAGENTA, + "PASS" => GREEN, + "XPASS" => BOLD.YELLOW, + "FAIL" => BOLD.RED, + "XFAIL" => YELLOW, + "SKIP" => BLUE, + ); + if (my $color = $color_for_result{$result}) + { + return colored ($color, $result); + } + else + { + return $result; # Don't colorize unknown stuff. + } +} + +sub report ($;$) +{ + my ($msg, $result, $explanation) = (undef, @_); + if ($result =~ /^(?:X?(?:PASS|FAIL)|SKIP|ERROR)/) + { + # Output on console might be colorized. + $msg = decorate_result($result); + if ($result =~ /^(?:PASS|XFAIL|SKIP)/) + { + return if $cfg{"show-failures-only"}; + } + else + { + $failed = 1; + } + } + elsif ($result eq "#") + { + $msg = " "; + } + else + { + die "$ME: INTERNAL ERROR"; # NOTREACHED + } + $msg .= " $explanation" if defined $explanation; + print $msg . "\n"; +} + +sub testsuite_error ($) +{ + report "ERROR", "- $_[0]"; +} + +sub handle_tap_result ($) +{ + $testno++; + my $result_obj = shift; + + my $test_result = stringify_result_obj $result_obj; + my $string = $result_obj->number; + + my $description = $result_obj->description; + $string .= " $test_name" unless is_null_string $test_name; + $string .= " $description" unless is_null_string $description; + + if ($plan_seen == LATE_PLAN) + { + $string .= " # AFTER LATE PLAN"; + } + elsif ($result_obj->is_unplanned) + { + $string .= " # UNPLANNED"; + } + elsif ($result_obj->number != $testno) + { + $string .= " # OUT-OF-ORDER (expecting $testno)"; + } + elsif (my $directive = $result_obj->directive) + { + $string .= " # $directive"; + my $explanation = $result_obj->explanation; + $string .= " $explanation" + unless is_null_string $explanation; + } + + report $test_result, $string; +} + +sub handle_tap_plan ($) +{ + my $plan = shift; + if ($plan_seen) + { + # Error, only one plan per stream is acceptable. + testsuite_error "multiple test plans"; + return; + } + # The TAP plan can come before or after *all* the TAP results; we speak + # respectively of an "early" or a "late" plan. If we see the plan line + # after at least one TAP result has been seen, assume we have a late + # plan; in this case, any further test result seen after the plan will + # be flagged as an error. + $plan_seen = ($testno >= 1 ? LATE_PLAN : EARLY_PLAN); + # If $testno > 0, we have an error ("too many tests run") that will be + # automatically dealt with later, so don't worry about it here. If + # $plan_seen is true, we have an error due to a repeated plan, and that + # has already been dealt with above. Otherwise, we have a valid "plan + # with SKIP" specification, and should report it as a particular kind + # of SKIP result. + if ($plan->directive && $testno == 0) + { + my $explanation = is_null_string ($plan->explanation) ? + undef : "- " . $plan->explanation; + report "SKIP", $explanation; + } +} + +sub handle_tap_bailout ($) +{ + my ($bailout, $msg) = ($_[0], "Bail out!"); + $bailed_out = 1; + $msg .= " " . $bailout->explanation + unless is_null_string $bailout->explanation; + testsuite_error $msg; +} + +sub extract_tap_comment ($) +{ + my $line = shift; + if (index ($line, DIAG_STRING) == 0) + { + # Strip leading `DIAG_STRING' from `$line'. + $line = substr ($line, length (DIAG_STRING)); + # And strip any leading and trailing whitespace left. + $line =~ s/(?:^\s*|\s*$)//g; + # Return what is left (if any). + return $line; + } + return ""; +} + +sub main () +{ + my $iterator = TAP::Parser::Iterator::Stream->new(\*STDIN); + my $parser = TAP::Parser->new ({iterator => $iterator }); + + STDOUT->autoflush(1); + while (defined (my $cur = $parser->next)) + { + # Parsing of TAP input should stop after a "Bail out!" directive. + next if $bailed_out; + + if ($cur->is_plan) + { + handle_tap_plan ($cur); + } + elsif ($cur->is_test) + { + handle_tap_result ($cur); + } + elsif ($cur->is_bailout) + { + handle_tap_bailout ($cur); + } + elsif ($cfg{"verbose"}) + { + my $comment = extract_tap_comment ($cur->raw); + report "#", "$comment" if length $comment; + } + } + # A "Bail out!" directive should cause us to ignore any following TAP + # error. + if (!$bailed_out) + { + if (!$plan_seen) + { + testsuite_error "missing test plan"; + } + elsif ($parser->tests_planned != $parser->tests_run) + { + my ($planned, $run) = ($parser->tests_planned, $parser->tests_run); + my $bad_amount = $run > $planned ? "many" : "few"; + testsuite_error (sprintf "too %s tests run (expected %d, got %d)", + $bad_amount, $planned, $run); + } + } +} + +# ----------- # +# Main code. # +# ----------- # + +main; +exit($failed); + +# Local Variables: +# perl-indent-level: 2 +# perl-continued-statement-offset: 2 +# perl-continued-brace-offset: 0 +# perl-brace-offset: 0 +# perl-brace-imaginary-offset: 0 +# perl-label-offset: -2 +# cperl-indent-level: 2 +# cperl-brace-offset: 0 +# cperl-continued-brace-offset: 0 +# cperl-label-offset: -2 +# cperl-extra-newline-before-brace: t +# cperl-merge-trailing-else: nil +# cperl-continued-statement-offset: 2 +# End: diff --git a/scripts/tap-merge.pl b/scripts/tap-merge.pl new file mode 100755 index 0000000000..10ccf57bb2 --- /dev/null +++ b/scripts/tap-merge.pl @@ -0,0 +1,111 @@ +#! /usr/bin/env perl +# Copyright (C) 2018 Red Hat, Inc. +# +# Author: Paolo Bonzini +# +# 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. +# +# 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 . + +# ---------------------------------- # +# Imports, static data, and setup. # +# ---------------------------------- # + +use warnings FATAL => 'all'; +use strict; +use Getopt::Long (); +use TAP::Parser; + +my $ME = "tap-merge.pl"; +my $VERSION = "2018-11-30"; + +my $HELP = "$ME: merge multiple TAP inputs from stdin."; + +use constant DIAG_STRING => "#"; + +# ----------------- # +# Option parsing. # +# ----------------- # + +Getopt::Long::GetOptions + ( + 'help' => sub { print $HELP; exit 0; }, + 'version' => sub { print "$ME $VERSION\n"; exit 0; }, + ); + +# -------------- # +# Subroutines. # +# -------------- # + +sub main () +{ + my $iterator = TAP::Parser::Iterator::Stream->new(\*STDIN); + my $parser = TAP::Parser->new ({iterator => $iterator }); + my $testno = 0; # Number of test results seen so far. + my $bailed_out = 0; # Whether a "Bail out!" directive has been seen. + + STDOUT->autoflush(1); + while (defined (my $cur = $parser->next)) + { + if ($cur->is_bailout) + { + $bailed_out = 1; + print DIAG_STRING . " " . $cur->as_string . "\n"; + next; + } + elsif ($cur->is_plan) + { + $bailed_out = 0; + next; + } + elsif ($cur->is_test) + { + $bailed_out = 0 if $cur->number == 1; + $testno++; + $cur = TAP::Parser::Result::Test->new({ + ok => $cur->ok, + test_num => $testno, + directive => $cur->directive, + explanation => $cur->explanation, + description => $cur->description + }); + } + elsif ($cur->is_version) + { + next if $testno > 0; + } + print $cur->as_string . "\n" unless $bailed_out; + } + print "1..$testno\n"; +} + +# ----------- # +# Main code. # +# ----------- # + +main; + +# Local Variables: +# perl-indent-level: 2 +# perl-continued-statement-offset: 2 +# perl-continued-brace-offset: 0 +# perl-brace-offset: 0 +# perl-brace-imaginary-offset: 0 +# perl-label-offset: -2 +# cperl-indent-level: 2 +# cperl-brace-offset: 0 +# cperl-continued-brace-offset: 0 +# cperl-label-offset: -2 +# cperl-extra-newline-before-brace: t +# cperl-merge-trailing-else: nil +# cperl-continued-statement-offset: 2 +# End: diff --git a/scripts/texi2pod.pl b/scripts/texi2pod.pl index 39ce584a32..839b7917cf 100755 --- a/scripts/texi2pod.pl +++ b/scripts/texi2pod.pl @@ -398,7 +398,7 @@ $sects{NAME} = "$fn \- $tl\n"; $sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES}; for $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES - BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) { + BUGS NOTES FOOTNOTES EXAMPLES SEEALSO AUTHOR COPYRIGHT)) { if(exists $sects{$sect}) { $head = $sect; $head =~ s/SEEALSO/SEE ALSO/; diff --git a/scripts/tracetool.py b/scripts/tracetool.py index fe2b0771f2..3beaa66bd8 100755 --- a/scripts/tracetool.py +++ b/scripts/tracetool.py @@ -15,8 +15,6 @@ __email__ = "stefanha@linux.vnet.ibm.com" import sys import getopt -import os.path -import re from tracetool import error_write, out import tracetool.backend diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 0e3c9e146c..6fca674936 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -274,6 +274,10 @@ class Event(object): props = groups["props"].split() fmt = groups["fmt"] fmt_trans = groups["fmt_trans"] + if fmt.find("%m") != -1 or fmt_trans.find("%m") != -1: + raise ValueError("Event format '%m' is forbidden, pass the error " + "as an explicit trace argument") + if len(fmt_trans) > 0: fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) @@ -350,6 +354,8 @@ def read_events(fobj, fname): events = [] for lineno, line in enumerate(fobj, 1): + if line[-1] != '\n': + raise ValueError("%s does not end with a new line" % fname) if not line.strip(): continue if line.lstrip().startswith('#'): diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 6751f41bc5..33c95af8e9 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -39,7 +39,7 @@ def generate_h(event, group): ' struct timeval _now;', ' gettimeofday(&_now, NULL);', ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', - ' getpid(),', + ' qemu_get_thread_id(),', ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', ' %(argnames)s);', ' }', diff --git a/scripts/tracetool/format/d.py b/scripts/tracetool/format/d.py index 78397c24d2..c7cb2a93a6 100644 --- a/scripts/tracetool/format/d.py +++ b/scripts/tracetool/format/d.py @@ -33,6 +33,11 @@ def generate(events, backend, group): events = [e for e in events if "disable" not in e.properties] + # SystemTap's dtrace(1) warns about empty "provider qemu {}" but is happy + # with an empty file. Avoid the warning. + if not events: + return + out('/* This file is autogenerated by tracetool, do not edit. */' '', 'provider qemu {') diff --git a/scripts/tracetool/format/log_stap.py b/scripts/tracetool/format/log_stap.py new file mode 100644 index 0000000000..3ccbc09d61 --- /dev/null +++ b/scripts/tracetool/format/log_stap.py @@ -0,0 +1,127 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +""" +Generate .stp file that printfs log messages (DTrace with SystemTAP only). +""" + +__author__ = "Daniel P. Berrange " +__copyright__ = "Copyright (C) 2014-2019, Red Hat, Inc." +__license__ = "GPL version 2 or (at your option) any later version" + +__maintainer__ = "Daniel Berrange" +__email__ = "berrange@redhat.com" + +import re + +from tracetool import out +from tracetool.backend.dtrace import binary, probeprefix +from tracetool.backend.simple import is_string +from tracetool.format.stap import stap_escape + +def global_var_name(name): + return probeprefix().replace(".", "_") + "_" + name + +STATE_SKIP = 0 +STATE_LITERAL = 1 +STATE_MACRO = 2 + +def c_macro_to_format(macro): + if macro.startswith("PRI"): + return macro[3] + + if macro == "TARGET_FMT_plx": + return "%016x" + + raise Exception("Unhandled macro '%s'" % macro) + +def c_fmt_to_stap(fmt): + state = 0 + bits = [] + literal = "" + macro = "" + escape = 0; + for i in range(len(fmt)): + if fmt[i] == '\\': + if escape: + escape = 0 + else: + escape = 1 + if state != STATE_LITERAL: + raise Exception("Unexpected escape outside string literal") + literal = literal + fmt[i] + elif fmt[i] == '"' and not escape: + if state == STATE_LITERAL: + state = STATE_SKIP + bits.append(literal) + literal = "" + else: + if state == STATE_MACRO: + bits.append(c_macro_to_format(macro)) + state = STATE_LITERAL + elif fmt[i] == ' ' or fmt[i] == '\t': + if state == STATE_MACRO: + bits.append(c_macro_to_format(macro)) + macro = "" + state = STATE_SKIP + elif state == STATE_LITERAL: + literal = literal + fmt[i] + else: + escape = 0 + if state == STATE_SKIP: + state = STATE_MACRO + + if state == STATE_LITERAL: + literal = literal + fmt[i] + else: + macro = macro + fmt[i] + + if state == STATE_MACRO: + bits.append(c_macro_to_format(macro)) + elif state == STATE_LITERAL: + bits.append(literal) + + fmt = re.sub("%(\d*)z(x|u|d)", "%\\1\\2", "".join(bits)) + return fmt + +def generate(events, backend, group): + out('/* This file is autogenerated by tracetool, do not edit. */', + '') + + for event_id, e in enumerate(events): + if 'disable' in e.properties: + continue + + out('probe %(probeprefix)s.log.%(name)s = %(probeprefix)s.%(name)s ?', + '{', + probeprefix=probeprefix(), + name=e.name) + + # Get references to userspace strings + for type_, name in e.args: + name = stap_escape(name) + if is_string(type_): + out(' try {', + ' arg%(name)s_str = %(name)s ? ' + + 'user_string_n(%(name)s, 512) : ""', + ' } catch {}', + name=name) + + # Determine systemtap's view of variable names + fields = ["pid()", "gettimeofday_ns()"] + for type_, name in e.args: + name = stap_escape(name) + if is_string(type_): + fields.append("arg" + name + "_str") + else: + fields.append(name) + + # Emit the entire record in a single SystemTap printf() + arg_str = ', '.join(arg for arg in fields) + fmt_str = "%d@%d " + e.name + " " + c_fmt_to_stap(e.fmt) + "\\n" + out(' printf("%(fmt_str)s", %(arg_str)s)', + fmt_str=fmt_str, arg_str=arg_str) + + out('}') + + out() diff --git a/scripts/tracetool/format/simpletrace_stap.py b/scripts/tracetool/format/simpletrace_stap.py index e7e44842ca..57b04061cf 100644 --- a/scripts/tracetool/format/simpletrace_stap.py +++ b/scripts/tracetool/format/simpletrace_stap.py @@ -14,7 +14,7 @@ __email__ = "stefanha@redhat.com" from tracetool import out -from tracetool.backend.dtrace import binary, probeprefix +from tracetool.backend.dtrace import probeprefix from tracetool.backend.simple import is_string from tracetool.format.stap import stap_escape diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index feb75390aa..a310a9072b 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -101,6 +101,13 @@ for arch in $ARCHLIST; do if [ $arch = mips ]; then cp "$tmpdir/include/asm/sgidefs.h" "$output/linux-headers/asm-mips/" + cp "$tmpdir/include/asm/unistd_o32.h" "$output/linux-headers/asm-mips/" + cp "$tmpdir/include/asm/unistd_n32.h" "$output/linux-headers/asm-mips/" + cp "$tmpdir/include/asm/unistd_n64.h" "$output/linux-headers/asm-mips/" + fi + if [ $arch = powerpc ]; then + cp "$tmpdir/include/asm/unistd_32.h" "$output/linux-headers/asm-powerpc/" + cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-powerpc/" fi rm -rf "$output/include/standard-headers/asm-$arch" @@ -120,6 +127,12 @@ for arch in $ARCHLIST; do cp "$tmpdir/include/asm/unistd_x32.h" "$output/linux-headers/asm-x86/" cp "$tmpdir/include/asm/unistd_64.h" "$output/linux-headers/asm-x86/" cp_portable "$tmpdir/include/asm/kvm_para.h" "$output/include/standard-headers/asm-$arch" + # Remove everything except the macros from bootparam.h avoiding the + # unnecessary import of several video/ist/etc headers + sed -e '/__ASSEMBLY__/,/__ASSEMBLY__/d' \ + "$tmpdir/include/asm/bootparam.h" > "$tmpdir/bootparam.h" + cp_portable "$tmpdir/bootparam.h" \ + "$output/include/standard-headers/asm-$arch" fi done @@ -162,13 +175,19 @@ EOF cat <$output/linux-headers/linux/virtio_ring.h #include "standard-headers/linux/virtio_ring.h" EOF +cat <$output/linux-headers/linux/vhost_types.h +#include "standard-headers/linux/vhost_types.h" +EOF rm -rf "$output/include/standard-headers/linux" mkdir -p "$output/include/standard-headers/linux" -for i in "$tmpdir"/include/linux/*virtio*.h "$tmpdir/include/linux/input.h" \ +for i in "$tmpdir"/include/linux/*virtio*.h \ + "$tmpdir/include/linux/qemu_fw_cfg.h" \ + "$tmpdir/include/linux/input.h" \ "$tmpdir/include/linux/input-event-codes.h" \ "$tmpdir/include/linux/pci_regs.h" \ "$tmpdir/include/linux/ethtool.h" "$tmpdir/include/linux/kernel.h" \ + "$tmpdir/include/linux/vhost_types.h" \ "$tmpdir/include/linux/sysinfo.h"; do cp_portable "$i" "$output/include/standard-headers/linux" done diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 3027dde60d..438380fced 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -44,8 +44,7 @@ static void pr_manager_send_status_changed_event(PRManagerHelper *pr_mgr) char *id = object_get_canonical_path_component(OBJECT(pr_mgr)); if (id) { - qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc, - &error_abort); + qapi_event_send_pr_manager_status_changed(id, !!pr_mgr->ioc); g_free(id); } } diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index 2a8f300dde..d9f4e8c3ad 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -48,24 +48,21 @@ static int pr_manager_worker(void *opaque) } -BlockAIOCB *pr_manager_execute(PRManager *pr_mgr, - AioContext *ctx, int fd, - struct sg_io_hdr *hdr, - BlockCompletionFunc *complete, - void *opaque) +int coroutine_fn pr_manager_execute(PRManager *pr_mgr, AioContext *ctx, int fd, + struct sg_io_hdr *hdr) { - PRManagerData *data = g_new(PRManagerData, 1); ThreadPool *pool = aio_get_thread_pool(ctx); + PRManagerData data = { + .pr_mgr = pr_mgr, + .fd = fd, + .hdr = hdr, + }; - trace_pr_manager_execute(fd, hdr->cmdp[0], hdr->cmdp[1], opaque); - data->pr_mgr = pr_mgr; - data->fd = fd; - data->hdr = hdr; + trace_pr_manager_execute(fd, hdr->cmdp[0], hdr->cmdp[1]); /* The matching object_unref is in pr_manager_worker. */ object_ref(OBJECT(pr_mgr)); - return thread_pool_submit_aio(pool, pr_manager_worker, - data, complete, opaque); + return thread_pool_submit_co(pool, pr_manager_worker, &data); } bool pr_manager_is_connected(PRManager *pr_mgr) diff --git a/scsi/qemu-pr-helper.c b/scsi/qemu-pr-helper.c index 1528a712a0..e7af637232 100644 --- a/scsi/qemu-pr-helper.c +++ b/scsi/qemu-pr-helper.c @@ -117,39 +117,6 @@ QEMU_COPYRIGHT "\n" , name); } -static void write_pidfile(void) -{ - int pidfd; - char pidstr[32]; - - pidfd = qemu_open(pidfile, O_CREAT|O_WRONLY, S_IRUSR|S_IWUSR); - if (pidfd == -1) { - error_report("Cannot open pid file, %s", strerror(errno)); - exit(EXIT_FAILURE); - } - - if (lockf(pidfd, F_TLOCK, 0)) { - error_report("Cannot lock pid file, %s", strerror(errno)); - goto fail; - } - if (ftruncate(pidfd, 0)) { - error_report("Failed to truncate pid file"); - goto fail; - } - - snprintf(pidstr, sizeof(pidstr), "%d\n", getpid()); - if (write(pidfd, pidstr, strlen(pidstr)) != strlen(pidstr)) { - error_report("Failed to write pid file"); - goto fail; - } - return; - -fail: - unlink(pidfile); - close(pidfd); - exit(EXIT_FAILURE); -} - /* SG_IO support */ typedef struct PRHelperSGIOData { @@ -269,7 +236,7 @@ static void dm_init(void) perror("Cannot open " CONTROL_PATH); exit(1); } - struct dm_ioctl dm = { 0 }; + struct dm_ioctl dm = { }; if (!dm_ioctl(DM_VERSION, &dm)) { perror("ioctl"); exit(1); @@ -301,7 +268,11 @@ void put_multipath_config(struct config *conf) static void multipath_pr_init(void) { udev = udev_new(); +#ifdef CONFIG_MPATH_NEW_API multipath_conf = mpath_lib_init(); +#else + mpath_lib_init(udev); +#endif } static int is_mpath(int fd) @@ -1076,8 +1047,11 @@ int main(int argc, char **argv) } } - if (daemonize || pidfile_specified) - write_pidfile(); + if ((daemonize || pidfile_specified) && + !qemu_write_pidfile(pidfile, &local_err)) { + error_report_err(local_err); + exit(EXIT_FAILURE); + } #ifdef CONFIG_LIBCAP if (drop_privileges() < 0) { diff --git a/scsi/trace-events b/scsi/trace-events index 45f5b6e49b..6dbfeae790 100644 --- a/scsi/trace-events +++ b/scsi/trace-events @@ -1,3 +1,5 @@ -# scsi/pr-manager.c -pr_manager_execute(int fd, int cmd, int sa, void *opaque) "fd=%d cmd=0x%02x service action=0x%02x opaque=%p" +# See docs/devel/tracing.txt for syntax documentation. + +# pr-manager.c +pr_manager_execute(int fd, int cmd, int sa) "fd=%d cmd=0x%02x service action=0x%02x" pr_manager_run(int fd, int cmd, int sa) "fd=%d cmd=0x%02x service action=0x%02x" diff --git a/slirp/COPYRIGHT b/slirp/COPYRIGHT index 1bc83d497e..ed49512dbc 100644 --- a/slirp/COPYRIGHT +++ b/slirp/COPYRIGHT @@ -1,8 +1,6 @@ Slirp was written by Danny Gasparovski. Copyright (c), 1995,1996 All Rights Reserved. -Slirp is maintained by Kelly Price - Slirp is free software; "free" as in you don't have to pay for it, and you are free to do whatever you want with it. I do not accept any donations, monetary or otherwise, for Slirp. Instead, I would ask you to pass this @@ -25,6 +23,9 @@ The copyright terms and conditions: 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY diff --git a/slirp/Makefile b/slirp/Makefile new file mode 100644 index 0000000000..6d48f626ba --- /dev/null +++ b/slirp/Makefile @@ -0,0 +1,47 @@ +ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) +BUILD_DIR ?= . + +LIBSLIRP = $(BUILD_DIR)/libslirp.a + +all: $(LIBSLIRP) + +SRCS := $(wildcard src/*.c) +OBJS := $(SRCS:%.c=$(BUILD_DIR)/%.o) +DEPS := $(OBJS:%.o=%.d) + +INC_DIRS := $(BUILD_DIR)/src +INC_FLAGS := $(addprefix -I,$(INC_DIRS)) + +override CFLAGS += \ + -DG_LOG_DOMAIN='"Slirp"' \ + $(shell $(PKG_CONFIG) --cflags glib-2.0) \ + $(INC_FLAGS) \ + -MMD -MP +override LDFLAGS += $(shell $(PKG_CONFIG) --libs glib-2.0) + +$(LIBSLIRP): $(OBJS) + +.PHONY: clean + +clean: + rm -r $(OBJS) $(DEPS) $(LIBSLIRP) + +$(BUILD_DIR)/src/%.o: $(ROOT_DIR)/src/%.c + @$(MKDIR_P) $(dir $@) + $(call quiet-command,$(CC) $(CFLAGS) -c -o $@ $<,"CC","$@") + +%.a: + $(call quiet-command,rm -f $@ && $(AR) rcs $@ $^,"AR","$@") + +PKG_CONFIG ?= pkg-config +MKDIR_P ?= mkdir -p +quiet-command-run = $(if $(V),,$(if $2,printf " %-7s %s\n" $2 $3 && ))$1 +quiet-@ = $(if $(V),,@) +quiet-command = $(quiet-@)$(call quiet-command-run,$1,$2,$3) + +print-%: + @echo '$*=$($*)' + +.SUFFIXES: + +-include $(DEPS) diff --git a/slirp/Makefile.objs b/slirp/Makefile.objs deleted file mode 100644 index 28049b03cd..0000000000 --- a/slirp/Makefile.objs +++ /dev/null @@ -1,5 +0,0 @@ -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 dhcpv6.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 udp6.o bootp.o tftp.o arp_table.o \ - ndp_table.o ncsi.o diff --git a/slirp/debug.h b/slirp/debug.h deleted file mode 100644 index 6cfa61edb3..0000000000 --- a/slirp/debug.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -//#define DEBUG 1 - -#ifdef DEBUG - -#define DBG_CALL 0x1 -#define DBG_MISC 0x2 -#define DBG_ERROR 0x4 - -#define dfd stderr - -extern int slirp_debug; - -#define DEBUG_CALL(x) if (slirp_debug & DBG_CALL) { fprintf(dfd, "%s...\n", x); fflush(dfd); } -#define DEBUG_ARG(x, y) if (slirp_debug & DBG_CALL) { fputc(' ', dfd); fprintf(dfd, x, y); fputc('\n', dfd); fflush(dfd); } -#define DEBUG_ARGS(x) if (slirp_debug & DBG_CALL) { fprintf x ; fflush(dfd); } -#define DEBUG_MISC(x) if (slirp_debug & DBG_MISC) { fprintf x ; fflush(dfd); } -#define DEBUG_ERROR(x) if (slirp_debug & DBG_ERROR) {fprintf x ; fflush(dfd); } - -#else - -#define DEBUG_CALL(x) -#define DEBUG_ARG(x, y) -#define DEBUG_ARGS(x) -#define DEBUG_MISC(x) -#define DEBUG_ERROR(x) - -#endif diff --git a/slirp/dhcpv6.h b/slirp/dhcpv6.h deleted file mode 100644 index 3373f6cb89..0000000000 --- a/slirp/dhcpv6.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Definitions and prototypes for SLIRP stateless DHCPv6 - * - * Copyright 2016 Thomas Huth, 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 SLIRP_DHCPV6_H -#define SLIRP_DHCPV6_H - -#define DHCPV6_SERVER_PORT 547 - -#define ALLDHCP_MULTICAST { .s6_addr = \ - { 0xff, 0x02, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x00, 0x00, 0x00,\ - 0x00, 0x01, 0x00, 0x02 } } - -#define in6_dhcp_multicast(a)\ - in6_equal(a, &(struct in6_addr)ALLDHCP_MULTICAST) - -void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m); - -#endif diff --git a/slirp/libslirp.h b/slirp/libslirp.h deleted file mode 100644 index 740408a96e..0000000000 --- a/slirp/libslirp.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef LIBSLIRP_H -#define LIBSLIRP_H - -#include "qemu-common.h" - -typedef struct Slirp Slirp; - -int get_dns_addr(struct in_addr *pdns_addr); -int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id); - -Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, - struct in_addr vnetmask, struct in_addr vhost, - 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, - const char *vdomainname, void *opaque); -void slirp_cleanup(Slirp *slirp); - -void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout); - -void slirp_pollfds_poll(GArray *pollfds, int select_error); - -void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); - -/* you must provide the following functions: */ -void slirp_output(void *opaque, const uint8_t *pkt, int pkt_len); - -int slirp_add_hostfwd(Slirp *slirp, int is_udp, - struct in_addr host_addr, int host_port, - struct in_addr guest_addr, int guest_port); -int slirp_remove_hostfwd(Slirp *slirp, int is_udp, - struct in_addr host_addr, int host_port); -int slirp_add_exec(Slirp *slirp, int do_pty, const void *args, - struct in_addr *guest_addr, int guest_port); - -void slirp_connection_info(Slirp *slirp, Monitor *mon); - -void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, - int guest_port, const uint8_t *buf, int size); -size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, - int guest_port); - -#endif diff --git a/slirp/main.h b/slirp/main.h deleted file mode 100644 index 90053ce5ec..0000000000 --- a/slirp/main.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#ifndef SLIRP_MAIN_H -#define SLIRP_MAIN_H - -#ifdef HAVE_SYS_SELECT_H -#include -#endif - -#define TOWRITEMAX 512 - -extern int slirp_socket; -extern int slirp_socket_unit; -extern int slirp_socket_port; -extern uint32_t slirp_socket_addr; -extern char *slirp_socket_passwd; -extern int ctty_closed; - -/* - * Get the difference in 2 times from updtim() - * Allow for wraparound times, "just in case" - * x is the greater of the 2 (current time) and y is - * what it's being compared against. - */ -#define TIME_DIFF(x,y) (x)-(y) < 0 ? ~0-(y)+(x) : (x)-(y) - -extern char *slirp_tty; -extern char *exec_shell; -extern u_int curtime; -extern struct in_addr loopback_addr; -extern unsigned long loopback_mask; -extern char *username; -extern char *socket_path; -extern int towrite_max; -extern int ppp_exit; -extern int tcp_keepintvl; - -#define PROTO_SLIP 0x1 -#ifdef USE_PPP -#define PROTO_PPP 0x2 -#endif - -int if_encap(Slirp *slirp, struct mbuf *ifm); -ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags); - -#endif diff --git a/slirp/misc.c b/slirp/misc.c deleted file mode 100644 index 260187b6b6..0000000000 --- a/slirp/misc.c +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. - */ - -#include "qemu/osdep.h" -#include "slirp.h" -#include "libslirp.h" -#include "monitor/monitor.h" -#include "qemu/error-report.h" -#include "qemu/main-loop.h" - -#ifdef DEBUG -int slirp_debug = DBG_CALL|DBG_MISC|DBG_ERROR; -#endif - -inline void -insque(void *a, void *b) -{ - register struct quehead *element = (struct quehead *) a; - register struct quehead *head = (struct quehead *) b; - element->qh_link = head->qh_link; - head->qh_link = (struct quehead *)element; - element->qh_rlink = (struct quehead *)head; - ((struct quehead *)(element->qh_link))->qh_rlink - = (struct quehead *)element; -} - -inline void -remque(void *a) -{ - register struct quehead *element = (struct quehead *) a; - ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; - ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link; - element->qh_rlink = NULL; -} - -int add_exec(struct ex_list **ex_ptr, int do_pty, char *exec, - struct in_addr addr, int port) -{ - struct ex_list *tmp_ptr; - - /* First, check if the port is "bound" */ - for (tmp_ptr = *ex_ptr; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) { - if (port == tmp_ptr->ex_fport && - addr.s_addr == tmp_ptr->ex_addr.s_addr) - return -1; - } - - tmp_ptr = *ex_ptr; - *ex_ptr = g_new(struct ex_list, 1); - (*ex_ptr)->ex_fport = port; - (*ex_ptr)->ex_addr = addr; - (*ex_ptr)->ex_pty = do_pty; - (*ex_ptr)->ex_exec = (do_pty == 3) ? exec : g_strdup(exec); - (*ex_ptr)->ex_next = tmp_ptr; - return 0; -} - - -#ifdef _WIN32 - -int -fork_exec(struct socket *so, const char *ex, int do_pty) -{ - /* not implemented */ - return 0; -} - -#else - -/* - * XXX This is ugly - * We create and bind a socket, then fork off to another - * process, which connects to this socket, after which we - * exec the wanted program. If something (strange) happens, - * the accept() call could block us forever. - * - * do_pty = 0 Fork/exec inetd style - * do_pty = 1 Fork/exec using slirp.telnetd - * do_ptr = 2 Fork/exec using pty - */ -int -fork_exec(struct socket *so, const char *ex, int do_pty) -{ - int s; - struct sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - int opt; - const char *argv[256]; - /* don't want to clobber the original */ - char *bptr; - const char *curarg; - int c, i, ret; - pid_t pid; - - DEBUG_CALL("fork_exec"); - DEBUG_ARG("so = %p", so); - DEBUG_ARG("ex = %p", ex); - DEBUG_ARG("do_pty = %x", do_pty); - - if (do_pty == 2) { - return 0; - } else { - addr.sin_family = AF_INET; - addr.sin_port = 0; - addr.sin_addr.s_addr = INADDR_ANY; - - if ((s = qemu_socket(AF_INET, SOCK_STREAM, 0)) < 0 || - bind(s, (struct sockaddr *)&addr, addrlen) < 0 || - listen(s, 1) < 0) { - error_report("Error: inet socket: %s", strerror(errno)); - if (s >= 0) { - closesocket(s); - } - - return 0; - } - } - - pid = fork(); - switch(pid) { - case -1: - error_report("Error: fork failed: %s", strerror(errno)); - close(s); - return 0; - - case 0: - setsid(); - - /* Set the DISPLAY */ - getsockname(s, (struct sockaddr *)&addr, &addrlen); - close(s); - /* - * Connect to the socket - * XXX If any of these fail, we're in trouble! - */ - s = qemu_socket(AF_INET, SOCK_STREAM, 0); - addr.sin_addr = loopback_addr; - do { - ret = connect(s, (struct sockaddr *)&addr, addrlen); - } while (ret < 0 && errno == EINTR); - - dup2(s, 0); - dup2(s, 1); - dup2(s, 2); - for (s = getdtablesize() - 1; s >= 3; s--) - close(s); - - i = 0; - bptr = g_strdup(ex); /* No need to free() this */ - if (do_pty == 1) { - /* Setup "slirp.telnetd -x" */ - argv[i++] = "slirp.telnetd"; - argv[i++] = "-x"; - argv[i++] = bptr; - } else - do { - /* Change the string into argv[] */ - curarg = bptr; - while (*bptr != ' ' && *bptr != (char)0) - bptr++; - c = *bptr; - *bptr++ = (char)0; - argv[i++] = g_strdup(curarg); - } while (c); - - argv[i] = NULL; - execvp(argv[0], (char **)argv); - - /* Ooops, failed, let's tell the user why */ - fprintf(stderr, "Error: execvp of %s failed: %s\n", - argv[0], strerror(errno)); - close(0); close(1); close(2); /* XXX */ - exit(1); - - default: - qemu_add_child_watch(pid); - /* - * XXX this could block us... - * XXX Should set a timer here, and if accept() doesn't - * return after X seconds, declare it a failure - * The only reason this will block forever is if socket() - * of connect() fail in the child process - */ - do { - so->s = accept(s, (struct sockaddr *)&addr, &addrlen); - } while (so->s < 0 && errno == EINTR); - closesocket(s); - socket_set_fast_reuse(so->s); - opt = 1; - qemu_setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); - qemu_set_nonblock(so->s); - - /* Append the telnet options now */ - if (so->so_m != NULL && do_pty == 1) { - sbappend(so, so->so_m); - so->so_m = NULL; - } - - return 1; - } -} -#endif - -void slirp_connection_info(Slirp *slirp, Monitor *mon) -{ - const char * const tcpstates[] = { - [TCPS_CLOSED] = "CLOSED", - [TCPS_LISTEN] = "LISTEN", - [TCPS_SYN_SENT] = "SYN_SENT", - [TCPS_SYN_RECEIVED] = "SYN_RCVD", - [TCPS_ESTABLISHED] = "ESTABLISHED", - [TCPS_CLOSE_WAIT] = "CLOSE_WAIT", - [TCPS_FIN_WAIT_1] = "FIN_WAIT_1", - [TCPS_CLOSING] = "CLOSING", - [TCPS_LAST_ACK] = "LAST_ACK", - [TCPS_FIN_WAIT_2] = "FIN_WAIT_2", - [TCPS_TIME_WAIT] = "TIME_WAIT", - }; - struct in_addr dst_addr; - struct sockaddr_in src; - socklen_t src_len; - uint16_t dst_port; - struct socket *so; - const char *state; - char buf[20]; - - monitor_printf(mon, " Protocol[State] FD Source Address Port " - "Dest. Address Port RecvQ SendQ\n"); - - for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { - if (so->so_state & SS_HOSTFWD) { - state = "HOST_FORWARD"; - } else if (so->so_tcpcb) { - state = tcpstates[so->so_tcpcb->t_state]; - } else { - state = "NONE"; - } - if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) { - src_len = sizeof(src); - getsockname(so->s, (struct sockaddr *)&src, &src_len); - dst_addr = so->so_laddr; - dst_port = so->so_lport; - } else { - src.sin_addr = so->so_laddr; - src.sin_port = so->so_lport; - dst_addr = so->so_faddr; - dst_port = so->so_fport; - } - snprintf(buf, sizeof(buf), " TCP[%s]", state); - monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*", - ntohs(src.sin_port)); - monitor_printf(mon, "%15s %5d %5d %5d\n", - inet_ntoa(dst_addr), ntohs(dst_port), - so->so_rcv.sb_cc, so->so_snd.sb_cc); - } - - for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) { - if (so->so_state & SS_HOSTFWD) { - snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]"); - src_len = sizeof(src); - getsockname(so->s, (struct sockaddr *)&src, &src_len); - dst_addr = so->so_laddr; - dst_port = so->so_lport; - } else { - snprintf(buf, sizeof(buf), " UDP[%d sec]", - (so->so_expire - curtime) / 1000); - src.sin_addr = so->so_laddr; - src.sin_port = so->so_lport; - dst_addr = so->so_faddr; - dst_port = so->so_fport; - } - monitor_printf(mon, "%-19s %3d %15s %5d ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*", - ntohs(src.sin_port)); - monitor_printf(mon, "%15s %5d %5d %5d\n", - inet_ntoa(dst_addr), ntohs(dst_port), - so->so_rcv.sb_cc, so->so_snd.sb_cc); - } - - for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { - snprintf(buf, sizeof(buf), " ICMP[%d sec]", - (so->so_expire - curtime) / 1000); - src.sin_addr = so->so_laddr; - dst_addr = so->so_faddr; - monitor_printf(mon, "%-19s %3d %15s - ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*"); - monitor_printf(mon, "%15s - %5d %5d\n", inet_ntoa(dst_addr), - so->so_rcv.sb_cc, so->so_snd.sb_cc); - } -} diff --git a/slirp/slirp.c b/slirp/slirp.c deleted file mode 100644 index 5c3bd6163f..0000000000 --- a/slirp/slirp.c +++ /dev/null @@ -1,1493 +0,0 @@ -/* - * libslirp glue - * - * Copyright (c) 2004-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 "qemu-common.h" -#include "qemu/timer.h" -#include "qemu/error-report.h" -#include "chardev/char-fe.h" -#include "migration/register.h" -#include "slirp.h" -#include "hw/hw.h" -#include "qemu/cutils.h" - -#ifndef _WIN32 -#include -#endif - -/* host loopback address */ -struct in_addr loopback_addr; -/* host loopback network mask */ -unsigned long loopback_mask; - -/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ -static const uint8_t special_ethaddr[ETH_ALEN] = { - 0x52, 0x55, 0x00, 0x00, 0x00, 0x00 -}; - -u_int curtime; - -static QTAILQ_HEAD(slirp_instances, Slirp) slirp_instances = - QTAILQ_HEAD_INITIALIZER(slirp_instances); - -static struct in_addr dns_addr; -#ifndef _WIN32 -static struct in6_addr dns6_addr; -#endif -static u_int dns_addr_time; -#ifndef _WIN32 -static u_int dns6_addr_time; -#endif - -#define TIMEOUT_FAST 2 /* milliseconds */ -#define TIMEOUT_SLOW 499 /* milliseconds */ -/* for the aging of certain requests like DNS */ -#define TIMEOUT_DEFAULT 1000 /* milliseconds */ - -#ifdef _WIN32 - -int get_dns_addr(struct in_addr *pdns_addr) -{ - FIXED_INFO *FixedInfo=NULL; - ULONG BufLen; - DWORD ret; - IP_ADDR_STRING *pIPAddr; - struct in_addr tmp_addr; - - if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) { - *pdns_addr = dns_addr; - return 0; - } - - FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO)); - BufLen = sizeof(FIXED_INFO); - - if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) { - if (FixedInfo) { - GlobalFree(FixedInfo); - FixedInfo = NULL; - } - FixedInfo = GlobalAlloc(GPTR, BufLen); - } - - if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) { - printf("GetNetworkParams failed. ret = %08x\n", (u_int)ret ); - if (FixedInfo) { - GlobalFree(FixedInfo); - FixedInfo = NULL; - } - return -1; - } - - pIPAddr = &(FixedInfo->DnsServerList); - inet_aton(pIPAddr->IpAddress.String, &tmp_addr); - *pdns_addr = tmp_addr; - dns_addr = tmp_addr; - dns_addr_time = curtime; - if (FixedInfo) { - GlobalFree(FixedInfo); - FixedInfo = NULL; - } - return 0; -} - -int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) -{ - return -1; -} - -static void winsock_cleanup(void) -{ - WSACleanup(); -} - -#else - -static int get_dns_addr_cached(void *pdns_addr, void *cached_addr, - socklen_t addrlen, - struct stat *cached_stat, u_int *cached_time) -{ - struct stat old_stat; - if (curtime - *cached_time < TIMEOUT_DEFAULT) { - memcpy(pdns_addr, cached_addr, addrlen); - return 0; - } - old_stat = *cached_stat; - if (stat("/etc/resolv.conf", cached_stat) != 0) { - return -1; - } - if (cached_stat->st_dev == old_stat.st_dev - && cached_stat->st_ino == old_stat.st_ino - && cached_stat->st_size == old_stat.st_size - && cached_stat->st_mtime == old_stat.st_mtime) { - memcpy(pdns_addr, cached_addr, addrlen); - return 0; - } - return 1; -} - -static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr, - socklen_t addrlen, uint32_t *scope_id, - u_int *cached_time) -{ - char buff[512]; - char buff2[257]; - FILE *f; - int found = 0; - void *tmp_addr = alloca(addrlen); - unsigned if_index; - - f = fopen("/etc/resolv.conf", "r"); - if (!f) - return -1; - -#ifdef DEBUG - fprintf(stderr, "IP address of your DNS(s): "); -#endif - while (fgets(buff, 512, f) != NULL) { - if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { - char *c = strchr(buff2, '%'); - if (c) { - if_index = if_nametoindex(c + 1); - *c = '\0'; - } else { - if_index = 0; - } - - if (!inet_pton(af, buff2, tmp_addr)) { - continue; - } - /* If it's the first one, set it to dns_addr */ - if (!found) { - memcpy(pdns_addr, tmp_addr, addrlen); - memcpy(cached_addr, tmp_addr, addrlen); - if (scope_id) { - *scope_id = if_index; - } - *cached_time = curtime; - } -#ifdef DEBUG - else - fprintf(stderr, ", "); -#endif - if (++found > 3) { -#ifdef DEBUG - fprintf(stderr, "(more)"); -#endif - break; - } -#ifdef DEBUG - else { - char s[INET6_ADDRSTRLEN]; - const char *res = inet_ntop(af, tmp_addr, s, sizeof(s)); - if (!res) { - res = "(string conversion error)"; - } - fprintf(stderr, "%s", res); - } -#endif - } - } - fclose(f); - if (!found) - return -1; - return 0; -} - -int get_dns_addr(struct in_addr *pdns_addr) -{ - static struct stat dns_addr_stat; - - if (dns_addr.s_addr != 0) { - int ret; - ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr), - &dns_addr_stat, &dns_addr_time); - if (ret <= 0) { - return ret; - } - } - return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr, - sizeof(dns_addr), NULL, &dns_addr_time); -} - -int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) -{ - static struct stat dns6_addr_stat; - - if (!in6_zero(&dns6_addr)) { - int ret; - ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), - &dns6_addr_stat, &dns6_addr_time); - if (ret <= 0) { - return ret; - } - } - return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr, - sizeof(dns6_addr), - scope_id, &dns6_addr_time); -} - -#endif - -static void slirp_init_once(void) -{ - static int initialized; -#ifdef _WIN32 - WSADATA Data; -#endif - - if (initialized) { - return; - } - initialized = 1; - -#ifdef _WIN32 - WSAStartup(MAKEWORD(2,0), &Data); - atexit(winsock_cleanup); -#endif - - loopback_addr.s_addr = htonl(INADDR_LOOPBACK); - loopback_mask = htonl(IN_CLASSA_NET); -} - -static void slirp_state_save(QEMUFile *f, void *opaque); -static int slirp_state_load(QEMUFile *f, void *opaque, int version_id); - -static SaveVMHandlers savevm_slirp_state = { - .save_state = slirp_state_save, - .load_state = slirp_state_load, -}; - -Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, - struct in_addr vnetmask, struct in_addr vhost, - 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, - const char *vdomainname, 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); - - 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); - } - slirp->tftp_prefix = g_strdup(tftp_path); - slirp->bootp_filename = g_strdup(bootfile); - slirp->vdomainname = g_strdup(vdomainname); - slirp->vdhcp_startaddr = vdhcp_start; - slirp->vnameserver_addr = vnameserver; - slirp->vnameserver_addr6 = vnameserver6; - - if (vdnssearch) { - translate_dnssearch(slirp, vdnssearch); - } - - slirp->opaque = opaque; - - register_savevm_live(NULL, "slirp", 0, 4, &savevm_slirp_state, slirp); - - QTAILQ_INSERT_TAIL(&slirp_instances, slirp, entry); - - return slirp; -} - -void slirp_cleanup(Slirp *slirp) -{ - QTAILQ_REMOVE(&slirp_instances, slirp, entry); - - 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); - g_free(slirp->vdomainname); - g_free(slirp); -} - -#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) -#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) - -static void slirp_update_timeout(uint32_t *timeout) -{ - Slirp *slirp; - uint32_t t; - - if (*timeout <= TIMEOUT_FAST) { - return; - } - - t = MIN(1000, *timeout); - - /* If we have tcp timeout with slirp, then we will fill @timeout with - * more precise value. - */ - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - if (slirp->time_fasttimo) { - *timeout = TIMEOUT_FAST; - return; - } - if (slirp->do_slowtimo) { - t = MIN(TIMEOUT_SLOW, t); - } - } - *timeout = t; -} - -void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout) -{ - Slirp *slirp; - struct socket *so, *so_next; - - if (QTAILQ_EMPTY(&slirp_instances)) { - return; - } - - /* - * First, TCP sockets - */ - - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - /* - * *_slowtimo needs calling if there are IP fragments - * in the fragment queue, or there are TCP connections active - */ - slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) || - (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); - - for (so = slirp->tcb.so_next; so != &slirp->tcb; - so = so_next) { - int events = 0; - - so_next = so->so_next; - - so->pollfds_idx = -1; - - /* - * See if we need a tcp_fasttimo - */ - if (slirp->time_fasttimo == 0 && - so->so_tcpcb->t_flags & TF_DELACK) { - slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */ - } - - /* - * NOFDREF can include still connecting to local-host, - * newly socreated() sockets etc. Don't want to select these. - */ - if (so->so_state & SS_NOFDREF || so->s == -1) { - continue; - } - - /* - * Set for reading sockets which are accepting - */ - if (so->so_state & SS_FACCEPTCONN) { - GPollFD pfd = { - .fd = so->s, - .events = G_IO_IN | G_IO_HUP | G_IO_ERR, - }; - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - continue; - } - - /* - * Set for writing sockets which are connecting - */ - if (so->so_state & SS_ISFCONNECTING) { - GPollFD pfd = { - .fd = so->s, - .events = G_IO_OUT | G_IO_ERR, - }; - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - continue; - } - - /* - * Set for writing if we are connected, can send more, and - * we have something to send - */ - if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { - events |= G_IO_OUT | G_IO_ERR; - } - - /* - * Set for reading (and urgent data) if we are connected, can - * receive more, and we have room for it XXX /2 ? - */ - if (CONN_CANFRCV(so) && - (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { - events |= G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_PRI; - } - - if (events) { - GPollFD pfd = { - .fd = so->s, - .events = events, - }; - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } - } - - /* - * UDP sockets - */ - for (so = slirp->udb.so_next; so != &slirp->udb; - so = so_next) { - so_next = so->so_next; - - so->pollfds_idx = -1; - - /* - * See if it's timed out - */ - if (so->so_expire) { - if (so->so_expire <= curtime) { - udp_detach(so); - continue; - } else { - slirp->do_slowtimo = true; /* Let socket expire */ - } - } - - /* - * When UDP packets are received from over the - * link, they're sendto()'d straight away, so - * no need for setting for writing - * Limit the number of packets queued by this session - * to 4. Note that even though we try and limit this - * to 4 packets, the session could have more queued - * if the packets needed to be fragmented - * (XXX <= 4 ?) - */ - if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { - GPollFD pfd = { - .fd = so->s, - .events = G_IO_IN | G_IO_HUP | G_IO_ERR, - }; - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } - } - - /* - * ICMP sockets - */ - for (so = slirp->icmp.so_next; so != &slirp->icmp; - so = so_next) { - so_next = so->so_next; - - so->pollfds_idx = -1; - - /* - * See if it's timed out - */ - if (so->so_expire) { - if (so->so_expire <= curtime) { - icmp_detach(so); - continue; - } else { - slirp->do_slowtimo = true; /* Let socket expire */ - } - } - - if (so->so_state & SS_ISFCONNECTED) { - GPollFD pfd = { - .fd = so->s, - .events = G_IO_IN | G_IO_HUP | G_IO_ERR, - }; - so->pollfds_idx = pollfds->len; - g_array_append_val(pollfds, pfd); - } - } - } - slirp_update_timeout(timeout); -} - -void slirp_pollfds_poll(GArray *pollfds, int select_error) -{ - Slirp *slirp; - struct socket *so, *so_next; - int ret; - - if (QTAILQ_EMPTY(&slirp_instances)) { - return; - } - - curtime = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); - - QTAILQ_FOREACH(slirp, &slirp_instances, entry) { - /* - * See if anything has timed out - */ - if (slirp->time_fasttimo && - ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) { - tcp_fasttimo(slirp); - slirp->time_fasttimo = 0; - } - if (slirp->do_slowtimo && - ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) { - ip_slowtimo(slirp); - tcp_slowtimo(slirp); - slirp->last_slowtimo = curtime; - } - - /* - * Check sockets - */ - if (!select_error) { - /* - * Check TCP sockets - */ - for (so = slirp->tcb.so_next; so != &slirp->tcb; - so = so_next) { - int revents; - - so_next = so->so_next; - - revents = 0; - if (so->pollfds_idx != -1) { - revents = g_array_index(pollfds, GPollFD, - so->pollfds_idx).revents; - } - - if (so->so_state & SS_NOFDREF || so->s == -1) { - continue; - } - - /* - * Check for URG data - * This will soread as well, so no need to - * test for G_IO_IN below if this succeeds - */ - if (revents & G_IO_PRI) { - 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 - */ - else if (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR)) { - /* - * Check for incoming connections - */ - if (so->so_state & SS_FACCEPTCONN) { - tcp_connect(so); - continue; - } /* else */ - ret = soread(so); - - /* Output it if we read something */ - 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; - } - } - - /* - * Check sockets for writing - */ - if (!(so->so_state & SS_NOFDREF) && - (revents & (G_IO_OUT | G_IO_ERR))) { - /* - * Check for non-blocking, still-connecting sockets - */ - if (so->so_state & SS_ISFCONNECTING) { - /* Connected */ - so->so_state &= ~SS_ISFCONNECTING; - - ret = send(so->s, (const void *) &ret, 0, 0); - if (ret < 0) { - /* XXXXX Must fix, zero bytes is a NOP */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) { - continue; - } - - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - } - /* else so->so_state &= ~SS_ISFCONNECTING; */ - - /* - * Continue tcp_input - */ - tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, - so->so_ffamily); - /* continue; */ - } else { - ret = sowrite(so); - if (ret > 0) { - /* Call tcp_output in case we need to send a window - * update to the guest, otherwise it will be stuck - * until it sends a window probe. */ - tcp_output(sototcpcb(so)); - } - } - } - - /* - * Probe a still-connecting, non-blocking socket - * to check if it's still alive - */ -#ifdef PROBE_CONN - if (so->so_state & SS_ISFCONNECTING) { - ret = qemu_recv(so->s, &ret, 0, 0); - - if (ret < 0) { - /* XXX */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) { - continue; /* Still connecting, continue */ - } - - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - - /* tcp_input will take care of it */ - } else { - ret = send(so->s, &ret, 0, 0); - if (ret < 0) { - /* XXX */ - if (errno == EAGAIN || errno == EWOULDBLOCK || - errno == EINPROGRESS || errno == ENOTCONN) { - continue; - } - /* else failed */ - so->so_state &= SS_PERSISTENT_MASK; - so->so_state |= SS_NOFDREF; - } else { - so->so_state &= ~SS_ISFCONNECTING; - } - - } - tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, - so->so_ffamily); - } /* SS_ISFCONNECTING */ -#endif - } - - /* - * Now UDP sockets. - * Incoming packets are sent straight away, they're not buffered. - * Incoming UDP data isn't buffered either. - */ - for (so = slirp->udb.so_next; so != &slirp->udb; - so = so_next) { - int revents; - - so_next = so->so_next; - - revents = 0; - if (so->pollfds_idx != -1) { - revents = g_array_index(pollfds, GPollFD, - so->pollfds_idx).revents; - } - - if (so->s != -1 && - (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { - sorecvfrom(so); - } - } - - /* - * Check incoming ICMP relies. - */ - for (so = slirp->icmp.so_next; so != &slirp->icmp; - so = so_next) { - int revents; - - so_next = so->so_next; - - revents = 0; - if (so->pollfds_idx != -1) { - revents = g_array_index(pollfds, GPollFD, - so->pollfds_idx).revents; - } - - if (so->s != -1 && - (revents & (G_IO_IN | G_IO_HUP | G_IO_ERR))) { - icmp_receive(so); - } - } - } - - if_start(slirp); - } -} - -static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) -{ - struct slirp_arphdr *ah = (struct slirp_arphdr *)(pkt + ETH_HLEN); - uint8_t arp_reply[MAX(ETH_HLEN + sizeof(struct slirp_arphdr), 64)]; - struct ethhdr *reh = (struct ethhdr *)arp_reply; - struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_reply + ETH_HLEN); - 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: - if (ah->ar_tip == ah->ar_sip) { - /* Gratuitous ARP */ - arp_table_add(slirp, ah->ar_sip, ah->ar_sha); - return; - } - - if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == - slirp->vnetwork_addr.s_addr) { - if (ah->ar_tip == slirp->vnameserver_addr.s_addr || - ah->ar_tip == slirp->vhost_addr.s_addr) - goto arp_ok; - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { - if (ex_ptr->ex_addr.s_addr == ah->ar_tip) - goto arp_ok; - } - return; - arp_ok: - memset(arp_reply, 0, sizeof(arp_reply)); - - arp_table_add(slirp, ah->ar_sip, ah->ar_sha); - - /* ARP request for alias/dns mac address */ - memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); - memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); - memcpy(&reh->h_source[2], &ah->ar_tip, 4); - reh->h_proto = htons(ETH_P_ARP); - - rah->ar_hrd = htons(1); - rah->ar_pro = htons(ETH_P_IP); - rah->ar_hln = ETH_ALEN; - rah->ar_pln = 4; - rah->ar_op = htons(ARPOP_REPLY); - memcpy(rah->ar_sha, reh->h_source, ETH_ALEN); - rah->ar_sip = ah->ar_tip; - memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); - rah->ar_tip = ah->ar_sip; - slirp_output(slirp->opaque, arp_reply, sizeof(arp_reply)); - } - break; - case ARPOP_REPLY: - arp_table_add(slirp, ah->ar_sip, ah->ar_sha); - break; - default: - break; - } -} - -void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) -{ - struct mbuf *m; - int proto; - - if (pkt_len < ETH_HLEN) - return; - - proto = ntohs(*(uint16_t *)(pkt + 12)); - switch(proto) { - case ETH_P_ARP: - 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 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 + TCPIPHDR_DELTA + 2; - memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len); - - m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN; - m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN; - - if (proto == ETH_P_IP) { - ip_input(m); - } else if (proto == ETH_P_IPV6) { - ip6_input(m); - } - break; - - case ETH_P_NCSI: - ncsi_input(slirp, pkt, pkt_len); - break; - - default: - break; - } -} - -/* 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. - */ -static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, - uint8_t ethaddr[ETH_ALEN]) -{ - const struct ip *iph = (const struct ip *)ifm->m_data; - - if (iph->ip_dst.s_addr == 0) { - /* 0.0.0.0 can not be a destination address, something went wrong, - * avoid making it worse */ - return 1; - } - if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { - uint8_t arp_req[ETH_HLEN + sizeof(struct slirp_arphdr)]; - struct ethhdr *reh = (struct ethhdr *)arp_req; - struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_req + ETH_HLEN); - - 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); - memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); - reh->h_proto = htons(ETH_P_ARP); - rah->ar_hrd = htons(1); - rah->ar_pro = htons(ETH_P_IP); - rah->ar_hln = ETH_ALEN; - rah->ar_pln = 4; - rah->ar_op = htons(ARPOP_REQUEST); - - /* source hw addr */ - memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); - memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); - - /* source IP */ - rah->ar_sip = slirp->vhost_addr.s_addr; - - /* target hw addr (none) */ - memset(rah->ar_tha, 0, ETH_ALEN); - - /* target IP */ - rah->ar_tip = iph->ip_dst.s_addr; - slirp->client_ipaddr = iph->ip_dst; - slirp_output(slirp->opaque, arp_req, sizeof(arp_req)); - 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_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); - - /* 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. */ -int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, - int host_port) -{ - struct socket *so; - struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb); - struct sockaddr_in addr; - int port = htons(host_port); - socklen_t addr_len; - - for (so = head->so_next; so != head; so = so->so_next) { - addr_len = sizeof(addr); - if ((so->so_state & SS_HOSTFWD) && - getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 && - addr.sin_addr.s_addr == host_addr.s_addr && - addr.sin_port == port) { - close(so->s); - sofree(so); - return 0; - } - } - - return -1; -} - -int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, - int host_port, struct in_addr guest_addr, int guest_port) -{ - if (!guest_addr.s_addr) { - guest_addr = slirp->vdhcp_startaddr; - } - if (is_udp) { - if (!udp_listen(slirp, host_addr.s_addr, htons(host_port), - guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) - return -1; - } else { - if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port), - guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) - return -1; - } - return 0; -} - -int slirp_add_exec(Slirp *slirp, int do_pty, const void *args, - struct in_addr *guest_addr, int guest_port) -{ - if (!guest_addr->s_addr) { - guest_addr->s_addr = slirp->vnetwork_addr.s_addr | - (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr); - } - if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) != - slirp->vnetwork_addr.s_addr || - guest_addr->s_addr == slirp->vhost_addr.s_addr || - guest_addr->s_addr == slirp->vnameserver_addr.s_addr) { - return -1; - } - return add_exec(&slirp->exec_list, do_pty, (char *)args, *guest_addr, - htons(guest_port)); -} - -ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags) -{ - if (so->s == -1 && so->extra) { - /* XXX this blocks entire thread. Rewrite to use - * qemu_chr_fe_write and background I/O callbacks */ - qemu_chr_fe_write_all(so->extra, buf, len); - return len; - } - - return send(so->s, buf, len, flags); -} - -static struct socket * -slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port) -{ - struct socket *so; - - for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { - if (so->so_faddr.s_addr == guest_addr.s_addr && - htons(so->so_fport) == guest_port) { - return so; - } - } - return NULL; -} - -size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, - int guest_port) -{ - struct iovec iov[2]; - struct socket *so; - - so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); - - if (!so || so->so_state & SS_NOFDREF) { - return 0; - } - - if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2)) { - return 0; - } - - return sopreprbuf(so, iov, NULL); -} - -void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, - const uint8_t *buf, int size) -{ - int ret; - struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); - - if (!so) - return; - - ret = soreadbuf(so, (const char *)buf, size); - - if (ret > 0) - tcp_output(sototcpcb(so)); -} - -static int slirp_tcp_post_load(void *opaque, int version) -{ - tcp_template((struct tcpcb *)opaque); - - return 0; -} - -static const VMStateDescription vmstate_slirp_tcp = { - .name = "slirp-tcp", - .version_id = 0, - .post_load = slirp_tcp_post_load, - .fields = (VMStateField[]) { - VMSTATE_INT16(t_state, struct tcpcb), - VMSTATE_INT16_ARRAY(t_timer, struct tcpcb, TCPT_NTIMERS), - VMSTATE_INT16(t_rxtshift, struct tcpcb), - VMSTATE_INT16(t_rxtcur, struct tcpcb), - VMSTATE_INT16(t_dupacks, struct tcpcb), - VMSTATE_UINT16(t_maxseg, struct tcpcb), - VMSTATE_UINT8(t_force, struct tcpcb), - VMSTATE_UINT16(t_flags, struct tcpcb), - VMSTATE_UINT32(snd_una, struct tcpcb), - VMSTATE_UINT32(snd_nxt, struct tcpcb), - VMSTATE_UINT32(snd_up, struct tcpcb), - VMSTATE_UINT32(snd_wl1, struct tcpcb), - VMSTATE_UINT32(snd_wl2, struct tcpcb), - VMSTATE_UINT32(iss, struct tcpcb), - VMSTATE_UINT32(snd_wnd, struct tcpcb), - VMSTATE_UINT32(rcv_wnd, struct tcpcb), - VMSTATE_UINT32(rcv_nxt, struct tcpcb), - VMSTATE_UINT32(rcv_up, struct tcpcb), - VMSTATE_UINT32(irs, struct tcpcb), - VMSTATE_UINT32(rcv_adv, struct tcpcb), - VMSTATE_UINT32(snd_max, struct tcpcb), - VMSTATE_UINT32(snd_cwnd, struct tcpcb), - VMSTATE_UINT32(snd_ssthresh, struct tcpcb), - VMSTATE_INT16(t_idle, struct tcpcb), - VMSTATE_INT16(t_rtt, struct tcpcb), - VMSTATE_UINT32(t_rtseq, struct tcpcb), - VMSTATE_INT16(t_srtt, struct tcpcb), - VMSTATE_INT16(t_rttvar, struct tcpcb), - VMSTATE_UINT16(t_rttmin, struct tcpcb), - VMSTATE_UINT32(max_sndwnd, struct tcpcb), - VMSTATE_UINT8(t_oobflags, struct tcpcb), - VMSTATE_UINT8(t_iobc, struct tcpcb), - VMSTATE_INT16(t_softerror, struct tcpcb), - VMSTATE_UINT8(snd_scale, struct tcpcb), - VMSTATE_UINT8(rcv_scale, struct tcpcb), - VMSTATE_UINT8(request_r_scale, struct tcpcb), - VMSTATE_UINT8(requested_s_scale, struct tcpcb), - VMSTATE_UINT32(ts_recent, struct tcpcb), - VMSTATE_UINT32(ts_recent_age, struct tcpcb), - VMSTATE_UINT32(last_ack_sent, struct tcpcb), - VMSTATE_END_OF_LIST() - } -}; - -/* The sbuf has a pair of pointers that are migrated as offsets; - * we calculate the offsets and restore the pointers using - * pre_save/post_load on a tmp structure. - */ -struct sbuf_tmp { - struct sbuf *parent; - uint32_t roff, woff; -}; - -static int sbuf_tmp_pre_save(void *opaque) -{ - struct sbuf_tmp *tmp = opaque; - tmp->woff = tmp->parent->sb_wptr - tmp->parent->sb_data; - tmp->roff = tmp->parent->sb_rptr - tmp->parent->sb_data; - - return 0; -} - -static int sbuf_tmp_post_load(void *opaque, int version) -{ - struct sbuf_tmp *tmp = opaque; - uint32_t requested_len = tmp->parent->sb_datalen; - - /* Allocate the buffer space used by the field after the tmp */ - sbreserve(tmp->parent, tmp->parent->sb_datalen); - - if (tmp->parent->sb_datalen != requested_len) { - return -ENOMEM; - } - if (tmp->woff >= requested_len || - tmp->roff >= requested_len) { - error_report("invalid sbuf offsets r/w=%u/%u len=%u", - tmp->roff, tmp->woff, requested_len); - return -EINVAL; - } - - tmp->parent->sb_wptr = tmp->parent->sb_data + tmp->woff; - tmp->parent->sb_rptr = tmp->parent->sb_data + tmp->roff; - - return 0; -} - - -static const VMStateDescription vmstate_slirp_sbuf_tmp = { - .name = "slirp-sbuf-tmp", - .post_load = sbuf_tmp_post_load, - .pre_save = sbuf_tmp_pre_save, - .version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(woff, struct sbuf_tmp), - VMSTATE_UINT32(roff, struct sbuf_tmp), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slirp_sbuf = { - .name = "slirp-sbuf", - .version_id = 0, - .fields = (VMStateField[]) { - VMSTATE_UINT32(sb_cc, struct sbuf), - VMSTATE_UINT32(sb_datalen, struct sbuf), - VMSTATE_WITH_TMP(struct sbuf, struct sbuf_tmp, vmstate_slirp_sbuf_tmp), - VMSTATE_VBUFFER_UINT32(sb_data, struct sbuf, 0, NULL, sb_datalen), - VMSTATE_END_OF_LIST() - } -}; - -static bool slirp_older_than_v4(void *opaque, int version_id) -{ - return version_id < 4; -} - -static bool slirp_family_inet(void *opaque, int version_id) -{ - union slirp_sockaddr *ssa = (union slirp_sockaddr *)opaque; - return ssa->ss.ss_family == AF_INET; -} - -static int slirp_socket_pre_load(void *opaque) -{ - struct socket *so = opaque; - if (tcp_attach(so) < 0) { - return -ENOMEM; - } - /* Older versions don't load these fields */ - so->so_ffamily = AF_INET; - so->so_lfamily = AF_INET; - return 0; -} - -#ifndef _WIN32 -#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_UINT32_TEST(f, s, t) -#else -/* Win uses u_long rather than uint32_t - but it's still 32bits long */ -#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_SINGLE_TEST(f, s, t, 0, \ - vmstate_info_uint32, u_long) -#endif - -/* The OS provided ss_family field isn't that portable; it's size - * and type varies (16/8 bit, signed, unsigned) - * and the values it contains aren't fully portable. - */ -typedef struct SS_FamilyTmpStruct { - union slirp_sockaddr *parent; - uint16_t portable_family; -} SS_FamilyTmpStruct; - -#define SS_FAMILY_MIG_IPV4 2 /* Linux, BSD, Win... */ -#define SS_FAMILY_MIG_IPV6 10 /* Linux */ -#define SS_FAMILY_MIG_OTHER 0xffff - -static int ss_family_pre_save(void *opaque) -{ - SS_FamilyTmpStruct *tss = opaque; - - tss->portable_family = SS_FAMILY_MIG_OTHER; - - if (tss->parent->ss.ss_family == AF_INET) { - tss->portable_family = SS_FAMILY_MIG_IPV4; - } else if (tss->parent->ss.ss_family == AF_INET6) { - tss->portable_family = SS_FAMILY_MIG_IPV6; - } - - return 0; -} - -static int ss_family_post_load(void *opaque, int version_id) -{ - SS_FamilyTmpStruct *tss = opaque; - - switch (tss->portable_family) { - case SS_FAMILY_MIG_IPV4: - tss->parent->ss.ss_family = AF_INET; - break; - case SS_FAMILY_MIG_IPV6: - case 23: /* compatibility: AF_INET6 from mingw */ - case 28: /* compatibility: AF_INET6 from FreeBSD sys/socket.h */ - tss->parent->ss.ss_family = AF_INET6; - break; - default: - error_report("invalid ss_family type %x", tss->portable_family); - return -EINVAL; - } - - return 0; -} - -static const VMStateDescription vmstate_slirp_ss_family = { - .name = "slirp-socket-addr/ss_family", - .pre_save = ss_family_pre_save, - .post_load = ss_family_post_load, - .fields = (VMStateField[]) { - VMSTATE_UINT16(portable_family, SS_FamilyTmpStruct), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slirp_socket_addr = { - .name = "slirp-socket-addr", - .version_id = 4, - .fields = (VMStateField[]) { - VMSTATE_WITH_TMP(union slirp_sockaddr, SS_FamilyTmpStruct, - vmstate_slirp_ss_family), - VMSTATE_SIN4_ADDR(sin.sin_addr.s_addr, union slirp_sockaddr, - slirp_family_inet), - VMSTATE_UINT16_TEST(sin.sin_port, union slirp_sockaddr, - slirp_family_inet), - -#if 0 - /* Untested: Needs checking by someone with IPv6 test */ - VMSTATE_BUFFER_TEST(sin6.sin6_addr, union slirp_sockaddr, - slirp_family_inet6), - VMSTATE_UINT16_TEST(sin6.sin6_port, union slirp_sockaddr, - slirp_family_inet6), - VMSTATE_UINT32_TEST(sin6.sin6_flowinfo, union slirp_sockaddr, - slirp_family_inet6), - VMSTATE_UINT32_TEST(sin6.sin6_scope_id, union slirp_sockaddr, - slirp_family_inet6), -#endif - - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slirp_socket = { - .name = "slirp-socket", - .version_id = 4, - .pre_load = slirp_socket_pre_load, - .fields = (VMStateField[]) { - VMSTATE_UINT32(so_urgc, struct socket), - /* Pre-v4 versions */ - VMSTATE_SIN4_ADDR(so_faddr.s_addr, struct socket, - slirp_older_than_v4), - VMSTATE_SIN4_ADDR(so_laddr.s_addr, struct socket, - slirp_older_than_v4), - VMSTATE_UINT16_TEST(so_fport, struct socket, slirp_older_than_v4), - VMSTATE_UINT16_TEST(so_lport, struct socket, slirp_older_than_v4), - /* v4 and newer */ - VMSTATE_STRUCT(fhost, struct socket, 4, vmstate_slirp_socket_addr, - union slirp_sockaddr), - VMSTATE_STRUCT(lhost, struct socket, 4, vmstate_slirp_socket_addr, - union slirp_sockaddr), - - VMSTATE_UINT8(so_iptos, struct socket), - VMSTATE_UINT8(so_emu, struct socket), - VMSTATE_UINT8(so_type, struct socket), - VMSTATE_INT32(so_state, struct socket), - VMSTATE_STRUCT(so_rcv, struct socket, 0, vmstate_slirp_sbuf, - struct sbuf), - VMSTATE_STRUCT(so_snd, struct socket, 0, vmstate_slirp_sbuf, - struct sbuf), - VMSTATE_STRUCT_POINTER(so_tcpcb, struct socket, vmstate_slirp_tcp, - struct tcpcb), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slirp_bootp_client = { - .name = "slirp_bootpclient", - .fields = (VMStateField[]) { - VMSTATE_UINT16(allocated, BOOTPClient), - VMSTATE_BUFFER(macaddr, BOOTPClient), - VMSTATE_END_OF_LIST() - } -}; - -static const VMStateDescription vmstate_slirp = { - .name = "slirp", - .version_id = 4, - .fields = (VMStateField[]) { - VMSTATE_UINT16_V(ip_id, Slirp, 2), - VMSTATE_STRUCT_ARRAY(bootp_clients, Slirp, NB_BOOTP_CLIENTS, 3, - vmstate_slirp_bootp_client, BOOTPClient), - VMSTATE_END_OF_LIST() - } -}; - -static void slirp_state_save(QEMUFile *f, void *opaque) -{ - Slirp *slirp = opaque; - struct ex_list *ex_ptr; - - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) - if (ex_ptr->ex_pty == 3) { - struct socket *so; - so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, - ntohs(ex_ptr->ex_fport)); - if (!so) - continue; - - qemu_put_byte(f, 42); - vmstate_save_state(f, &vmstate_slirp_socket, so, NULL); - } - qemu_put_byte(f, 0); - - vmstate_save_state(f, &vmstate_slirp, slirp, NULL); -} - - -static int slirp_state_load(QEMUFile *f, void *opaque, int version_id) -{ - Slirp *slirp = opaque; - struct ex_list *ex_ptr; - - while (qemu_get_byte(f)) { - int ret; - struct socket *so = socreate(slirp); - - if (!so) - return -ENOMEM; - - ret = vmstate_load_state(f, &vmstate_slirp_socket, so, version_id); - - if (ret < 0) - return ret; - - if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) != - slirp->vnetwork_addr.s_addr) { - return -EINVAL; - } - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { - if (ex_ptr->ex_pty == 3 && - so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr && - so->so_fport == ex_ptr->ex_fport) { - break; - } - } - if (!ex_ptr) - return -EINVAL; - - so->extra = (void *)ex_ptr->ex_exec; - } - - return vmstate_load_state(f, &vmstate_slirp, slirp, version_id); -} diff --git a/slirp/slirp_config.h b/slirp/slirp_config.h deleted file mode 100644 index c59f655207..0000000000 --- a/slirp/slirp_config.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * User definable configuration options - */ - -/* Define if you want the connection to be probed */ -/* XXX Not working yet, so ignore this for now */ -#undef PROBE_CONN - -/* Define to 1 if you want KEEPALIVE timers */ -#define DO_KEEPALIVE 0 - -/* Define this if you want slirp to write to the tty as fast as it can */ -/* This should only be set if you are using load-balancing, slirp does a */ -/* pretty good job on single modems already, and seting this will make */ -/* interactive sessions less responsive */ -/* XXXXX Talk about having fast modem as unit 0 */ -#undef FULL_BOLT - -/*********************************************************/ -/* - * Autoconf defined configuration options - * You shouldn't need to touch any of these - */ - -/* Define if you have sys/ioctl.h */ -#undef HAVE_SYS_IOCTL_H -#ifndef _WIN32 -#define HAVE_SYS_IOCTL_H -#endif - -/* Define if you have sys/filio.h */ -#undef HAVE_SYS_FILIO_H -#ifdef __APPLE__ -#define HAVE_SYS_FILIO_H -#endif - -/* Define if you have sys/bitypes.h */ -#undef HAVE_SYS_BITYPES_H - -/* Define if the machine is big endian */ -//#undef HOST_WORDS_BIGENDIAN - -/* Define if you have readv */ -#undef HAVE_READV - -/* Define if iovec needs to be declared */ -#undef DECLARE_IOVEC -#ifdef _WIN32 -#define DECLARE_IOVEC -#endif - -/* Define if you have a POSIX.1 sys/wait.h */ -#undef HAVE_SYS_WAIT_H - -/* Define if you have sys/select.h */ -#undef HAVE_SYS_SELECT_H -#ifndef _WIN32 -#define HAVE_SYS_SELECT_H -#endif - -/* Define if you have arpa/inet.h */ -#undef HAVE_ARPA_INET_H -#ifndef _WIN32 -#define HAVE_ARPA_INET_H -#endif - -/* Define if you have sys/signal.h */ -#undef HAVE_SYS_SIGNAL_H - -/* Define if you have sys/stropts.h */ -#undef HAVE_SYS_STROPTS_H - -/* Define to sizeof(char *) */ -#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8) - -/* Define if you have inet_aton */ -#undef HAVE_INET_ATON -#ifndef _WIN32 -#define HAVE_INET_ATON -#endif - -/* Define if you DON'T have unix-domain sockets */ -#undef NO_UNIX_SOCKETS -#ifdef _WIN32 -#define NO_UNIX_SOCKETS -#endif diff --git a/slirp/arp_table.c b/slirp/src/arp_table.c similarity index 87% rename from slirp/arp_table.c rename to slirp/src/arp_table.c index f81963bb88..9d7a59eb2c 100644 --- a/slirp/arp_table.c +++ b/slirp/src/arp_table.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * ARP table * @@ -22,9 +23,10 @@ * THE SOFTWARE. */ -#include "qemu/osdep.h" #include "slirp.h" +#include + void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) { const uint32_t broadcast_addr = @@ -34,9 +36,9 @@ void arp_table_add(Slirp *slirp, uint32_t ip_addr, uint8_t ethaddr[ETH_ALEN]) DEBUG_CALL("arp_table_add"); DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){.s_addr = ip_addr})); - DEBUG_ARGS((dfd, " hw addr = %02x:%02x:%02x:%02x:%02x:%02x\n", - ethaddr[0], ethaddr[1], ethaddr[2], - ethaddr[3], ethaddr[4], ethaddr[5])); + DEBUG_ARG("hw addr = %02x:%02x:%02x:%02x:%02x:%02x", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) { /* Do not register broadcast addresses */ @@ -79,9 +81,9 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, for (i = 0; i < ARP_TABLE_SIZE; i++) { if (arptbl->table[i].ar_sip == ip_addr) { memcpy(out_ethaddr, arptbl->table[i].ar_sha, 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])); + DEBUG_ARG("found hw addr = %02x:%02x:%02x:%02x:%02x:%02x", + out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], + out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]); return 1; } } diff --git a/slirp/bootp.c b/slirp/src/bootp.c similarity index 87% rename from slirp/bootp.c rename to slirp/src/bootp.c index 9e7b53ba94..b208e3b216 100644 --- a/slirp/bootp.c +++ b/slirp/src/bootp.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * QEMU BOOTP/DHCP server * @@ -21,7 +22,6 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -#include "qemu/osdep.h" #include "slirp.h" #if defined(_WIN32) @@ -36,12 +36,7 @@ static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; -#ifdef DEBUG -#define DPRINTF(fmt, ...) \ -do if (slirp_debug & DBG_CALL) { fprintf(dfd, fmt, ## __VA_ARGS__); fflush(dfd); } while (0) -#else -#define DPRINTF(fmt, ...) do{}while(0) -#endif +#define DPRINTF(fmt, ...) DEBUG_CALL(fmt, ##__VA_ARGS__) static BOOTPClient *get_new_addr(Slirp *slirp, struct in_addr *paddr, const uint8_t *macaddr) @@ -159,6 +154,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) struct in_addr preq_addr; int dhcp_msg_type, val; uint8_t *q; + uint8_t *end; uint8_t client_ethaddr[ETH_ALEN]; /* extract exact DHCP msg type */ @@ -166,8 +162,9 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type); if (preq_addr.s_addr != htonl(0L)) DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr)); - else + else { DPRINTF("\n"); + } if (dhcp_msg_type == 0) dhcp_msg_type = DHCPREQUEST; /* Force reply for old BOOTP clients */ @@ -240,6 +237,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */ q = rbp->bp_vend; + end = (uint8_t *)&rbp[1]; memcpy(q, rfc1533_cookie, 4); q += 4; @@ -292,24 +290,46 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) if (*slirp->client_hostname) { val = strlen(slirp->client_hostname); - *q++ = RFC1533_HOSTNAME; - *q++ = val; - memcpy(q, slirp->client_hostname, val); - q += val; + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting host name option."); + } else { + *q++ = RFC1533_HOSTNAME; + *q++ = val; + memcpy(q, slirp->client_hostname, val); + q += val; + } } if (slirp->vdomainname) { val = strlen(slirp->vdomainname); - *q++ = RFC1533_DOMAINNAME; - *q++ = val; - memcpy(q, slirp->vdomainname, val); - q += val; + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting domain name option."); + } else { + *q++ = RFC1533_DOMAINNAME; + *q++ = val; + memcpy(q, slirp->vdomainname, val); + q += val; + } + } + + if (slirp->tftp_server_name) { + val = strlen(slirp->tftp_server_name); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting tftp-server-name option."); + } else { + *q++ = RFC2132_TFTP_SERVER_NAME; + *q++ = val; + memcpy(q, slirp->tftp_server_name, val); + q += val; + } } if (slirp->vdnssearch) { - size_t spaceleft = sizeof(rbp->bp_vend) - (q - rbp->bp_vend); val = slirp->vdnssearch_len; - if (val + 1 > spaceleft) { + if (q + val >= end) { g_warning("DHCP packet size exceeded, " "omitting domain-search option."); } else { @@ -331,6 +351,7 @@ static void bootp_reply(Slirp *slirp, const struct bootp_t *bp) memcpy(q, nak_msg, sizeof(nak_msg) - 1); q += sizeof(nak_msg) - 1; } + assert(q < end); *q = RFC1533_END; daddr.sin_addr.s_addr = 0xffffffffu; diff --git a/slirp/bootp.h b/slirp/src/bootp.h similarity index 97% rename from slirp/bootp.h rename to slirp/src/bootp.h index 394525733e..d881ad620a 100644 --- a/slirp/bootp.h +++ b/slirp/src/bootp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* bootp/dhcp defines */ #ifndef SLIRP_BOOTP_H @@ -70,6 +71,7 @@ #define RFC2132_MAX_SIZE 57 #define RFC2132_RENEWAL_TIME 58 #define RFC2132_REBIND_TIME 59 +#define RFC2132_TFTP_SERVER_NAME 66 #define DHCPDISCOVER 1 #define DHCPOFFER 2 diff --git a/slirp/cksum.c b/slirp/src/cksum.c similarity index 96% rename from slirp/cksum.c rename to slirp/src/cksum.c index 6d73abf4a0..9599f6a280 100644 --- a/slirp/cksum.c +++ b/slirp/src/cksum.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1988, 1992, 1993 * The Regents of the University of California. All rights reserved. @@ -30,7 +31,6 @@ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp */ -#include "qemu/osdep.h" #include "slirp.h" /* @@ -70,9 +70,7 @@ int cksum(struct mbuf *m, int len) if (len < mlen) mlen = len; -#ifdef DEBUG len -= mlen; -#endif /* * Force to even boundary. */ @@ -122,12 +120,10 @@ int cksum(struct mbuf *m, int len) s_util.c[0] = *(uint8_t *)w; cont: -#ifdef DEBUG if (len) { - DEBUG_ERROR((dfd, "cksum: out of data\n")); - DEBUG_ERROR((dfd, " len = %d\n", len)); + DEBUG_ERROR("cksum: out of data"); + DEBUG_ERROR(" len = %d", len); } -#endif if (mlen == -1) { /* The last mbuf has odd # of bytes. Follow the standard (the odd byte may be shifted left by 8 bits diff --git a/slirp/src/debug.h b/slirp/src/debug.h new file mode 100644 index 0000000000..c95fd8ffd2 --- /dev/null +++ b/slirp/src/debug.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#ifndef DEBUG_H_ +#define DEBUG_H_ + +#define DBG_CALL (1 << 0) +#define DBG_MISC (1 << 1) +#define DBG_ERROR (1 << 2) +#define DBG_TFTP (1 << 3) + +extern int slirp_debug; + +#define DEBUG_CALL(fmt, ...) do { \ + if (G_UNLIKELY(slirp_debug & DBG_CALL)) { \ + g_debug(fmt "...", ##__VA_ARGS__); \ + } \ +} while (0) + +#define DEBUG_ARG(fmt, ...) do { \ + if (G_UNLIKELY(slirp_debug & DBG_CALL)) { \ + g_debug(" " fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define DEBUG_MISC(fmt, ...) do { \ + if (G_UNLIKELY(slirp_debug & DBG_MISC)) { \ + g_debug(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define DEBUG_ERROR(fmt, ...) do { \ + if (G_UNLIKELY(slirp_debug & DBG_ERROR)) { \ + g_debug(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#define DEBUG_TFTP(fmt, ...) do { \ + if (G_UNLIKELY(slirp_debug & DBG_TFTP)) { \ + g_debug(fmt, ##__VA_ARGS__); \ + } \ +} while (0) + +#endif /* DEBUG_H_ */ diff --git a/slirp/dhcpv6.c b/slirp/src/dhcpv6.c similarity index 73% rename from slirp/dhcpv6.c rename to slirp/src/dhcpv6.c index d266611e85..3c8f420912 100644 --- a/slirp/dhcpv6.c +++ b/slirp/src/dhcpv6.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * SLIRP stateless DHCPv6 * @@ -6,22 +7,37 @@ * * Copyright 2016 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: * - * 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. + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see . + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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 "qemu/osdep.h" -#include "qemu/log.h" #include "slirp.h" #include "dhcpv6.h" @@ -50,7 +66,7 @@ struct requested_infos { * the odata region, thus the caller must keep odata valid as long as it * needs to access the requested_infos struct. */ -static int dhcpv6_parse_info_request(uint8_t *odata, int olen, +static int dhcpv6_parse_info_request(Slirp *slirp, uint8_t *odata, int olen, struct requested_infos *ri) { int i, req_opt; @@ -61,7 +77,7 @@ static int dhcpv6_parse_info_request(uint8_t *odata, int olen, int len = odata[2] << 8 | odata[3]; if (len + 4 > olen) { - qemu_log_mask(LOG_GUEST_ERROR, "Guest sent bad DHCPv6 packet!\n"); + slirp->cb->guest_error("Guest sent bad DHCPv6 packet!", slirp->opaque); return -E2BIG; } @@ -92,14 +108,14 @@ static int dhcpv6_parse_info_request(uint8_t *odata, int olen, ri->want_boot_url = true; break; default: - DEBUG_MISC((dfd, "dhcpv6: Unsupported option request %d\n", - req_opt)); + DEBUG_MISC("dhcpv6: Unsupported option request %d", + req_opt); } } break; default: - DEBUG_MISC((dfd, "dhcpv6 info req: Unsupported option %d, len=%d\n", - option, len)); + DEBUG_MISC("dhcpv6 info req: Unsupported option %d, len=%d", + option, len); } odata += len + 4; @@ -121,7 +137,7 @@ static void dhcpv6_info_request(Slirp *slirp, struct sockaddr_in6 *srcsas, struct mbuf *m; uint8_t *resp; - if (dhcpv6_parse_info_request(odata, olen, &ri) < 0) { + if (dhcpv6_parse_info_request(slirp, odata, olen, &ri) < 0) { return; } @@ -203,7 +219,6 @@ void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m) dhcpv6_info_request(m->slirp, srcsas, xid, &data[4], data_len - 4); break; default: - DEBUG_MISC((dfd, "dhcpv6_input: Unsupported message type 0x%x\n", - data[0])); + DEBUG_MISC("dhcpv6_input: Unsupported message type 0x%x", data[0]); } } diff --git a/slirp/src/dhcpv6.h b/slirp/src/dhcpv6.h new file mode 100644 index 0000000000..dc26a93cff --- /dev/null +++ b/slirp/src/dhcpv6.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Definitions and prototypes for SLIRP stateless DHCPv6 + * + * Copyright 2016 Thomas Huth, Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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 SLIRP_DHCPV6_H +#define SLIRP_DHCPV6_H + +#define DHCPV6_SERVER_PORT 547 + +#define ALLDHCP_MULTICAST { .s6_addr = \ + { 0xff, 0x02, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x00, 0x00, 0x00,\ + 0x00, 0x01, 0x00, 0x02 } } + +#define in6_dhcp_multicast(a)\ + in6_equal(a, &(struct in6_addr)ALLDHCP_MULTICAST) + +void dhcpv6_input(struct sockaddr_in6 *srcsas, struct mbuf *m); + +#endif diff --git a/slirp/dnssearch.c b/slirp/src/dnssearch.c similarity index 99% rename from slirp/dnssearch.c rename to slirp/src/dnssearch.c index 8fb563321b..12c488971e 100644 --- a/slirp/dnssearch.c +++ b/slirp/src/dnssearch.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * Domain search option for DHCP (RFC 3397) * @@ -22,7 +23,6 @@ * THE SOFTWARE. */ -#include "qemu/osdep.h" #include "slirp.h" static const uint8_t RFC3397_OPT_DOMAIN_SEARCH = 119; diff --git a/slirp/if.c b/slirp/src/if.c similarity index 96% rename from slirp/if.c rename to slirp/src/if.c index 590753c658..6eaac7292a 100644 --- a/slirp/if.c +++ b/slirp/src/if.c @@ -1,13 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" -#include "qemu/timer.h" static void ifs_insque(struct mbuf *ifm, struct mbuf *ifmhead) @@ -131,12 +127,10 @@ diddit: } } -#ifndef FULL_BOLT /* * This prevents us from malloc()ing too many mbufs */ if_start(ifm->slirp); -#endif } /* @@ -150,7 +144,7 @@ diddit: */ void if_start(Slirp *slirp) { - uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); + uint64_t now = slirp->cb->clock_get_ns(slirp->opaque); bool from_batchq = false; struct mbuf *ifm, *ifm_next, *ifqt; diff --git a/slirp/if.h b/slirp/src/if.h similarity index 84% rename from slirp/if.h rename to slirp/src/if.h index 69569c10df..b71c37d6ea 100644 --- a/slirp/if.h +++ b/slirp/src/if.h @@ -1,8 +1,6 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ #ifndef IF_H diff --git a/slirp/ip.h b/slirp/src/ip.h similarity index 94% rename from slirp/ip.h rename to slirp/src/ip.h index 59cf4aa918..1484de1176 100644 --- a/slirp/ip.h +++ b/slirp/src/ip.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. @@ -33,7 +34,9 @@ #ifndef IP_H #define IP_H -#ifdef HOST_WORDS_BIGENDIAN +#include + +#if G_BYTE_ORDER == G_BIG_ENDIAN # undef NTOHL # undef NTOHS # undef HTONL @@ -69,7 +72,7 @@ typedef uint32_t n_long; /* long as received from the net */ * Structure of an internet header, naked of options. */ struct ip { -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint8_t ip_v:4, /* version */ ip_hl:4; /* header length */ #else @@ -87,7 +90,7 @@ struct ip { uint8_t ip_p; /* protocol */ uint16_t ip_sum; /* checksum */ struct in_addr ip_src,ip_dst; /* source and dest address */ -} QEMU_PACKED; +} SLIRP_PACKED; #define IP_MAXPACKET 65535 /* maximum packet size */ @@ -135,7 +138,7 @@ struct ip_timestamp { uint8_t ipt_code; /* IPOPT_TS */ uint8_t ipt_len; /* size of structure (variable) */ uint8_t ipt_ptr; /* index of current entry */ -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint8_t ipt_oflw:4, /* overflow counter */ ipt_flg:4; /* flags, see below */ #else @@ -149,7 +152,7 @@ struct ip_timestamp { n_long ipt_time; } ipt_ta[1]; } ipt_timestamp; -} QEMU_PACKED; +} SLIRP_PACKED; /* flag bits for ipt_flg */ #define IPOPT_TS_TSONLY 0 /* timestamps only */ @@ -175,15 +178,15 @@ struct ip_timestamp { #define IP_MSS 576 /* default maximum segment size */ -#if SIZEOF_CHAR_P == 4 +#if GLIB_SIZEOF_VOID_P == 4 struct mbuf_ptr { struct mbuf *mptr; uint32_t dummy; -} QEMU_PACKED; +} SLIRP_PACKED; #else struct mbuf_ptr { struct mbuf *mptr; -} QEMU_PACKED; +} SLIRP_PACKED; #endif struct qlink { void *next, *prev; @@ -199,7 +202,7 @@ struct ipovly { uint16_t ih_len; /* protocol length */ struct in_addr ih_src; /* source internet address */ struct in_addr ih_dst; /* destination internet address */ -} QEMU_PACKED; +} SLIRP_PACKED; /* * Ip reassembly queue structure. Each fragment @@ -215,7 +218,7 @@ struct ipq { uint8_t ipq_p; /* protocol of this fragment */ uint16_t ipq_id; /* sequence id for reassembly */ struct in_addr ipq_src,ipq_dst; -} QEMU_PACKED; +}; /* * Ip header, when holding a fragment. @@ -225,7 +228,10 @@ struct ipq { struct ipasfrag { struct qlink ipf_link; struct ip ipf_ip; -} QEMU_PACKED; +}; + +G_STATIC_ASSERT(offsetof(struct ipq, frag_link) == + offsetof(struct ipasfrag, ipf_link)); #define ipf_off ipf_ip.ip_off #define ipf_tos ipf_ip.ip_tos diff --git a/slirp/ip6.h b/slirp/src/ip6.h similarity index 90% rename from slirp/ip6.h rename to slirp/src/ip6.h index b1bea43b3c..33683c8e20 100644 --- a/slirp/ip6.h +++ b/slirp/src/ip6.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 2013 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. @@ -6,7 +7,8 @@ #ifndef SLIRP_IP6_H #define SLIRP_IP6_H -#include "net/eth.h" +#include +#include #define ALLNODES_MULTICAST { .s6_addr = \ { 0xff, 0x02, 0x00, 0x00,\ @@ -113,7 +115,7 @@ static inline void in6_compute_ethaddr(struct in6_addr ip, * Structure of an internet header, naked of options. */ struct ip6 { -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint32_t ip_v:4, /* version */ ip_tc_hi:4, /* traffic class */ @@ -132,7 +134,7 @@ struct ip6 { 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 @@ -144,7 +146,15 @@ struct ip6_pseudohdr { uint16_t ih_zero_hi; /* zero */ uint8_t ih_zero_lo; /* zero */ uint8_t ih_nh; /* next header */ -} QEMU_PACKED; +}; +/* + * We don't want to mark these ip6 structs as packed as they are naturally + * correctly aligned; instead assert that there is no stray padding. + * If we marked the struct as packed then we would be unable to take + * the address of any of the fields in it. + */ +G_STATIC_ASSERT(sizeof(struct ip6) == 40); +G_STATIC_ASSERT(sizeof(struct ip6_pseudohdr) == 40); #endif diff --git a/slirp/ip6_icmp.c b/slirp/src/ip6_icmp.c similarity index 92% rename from slirp/ip6_icmp.c rename to slirp/src/ip6_icmp.c index ee333d05a2..5642457fdd 100644 --- a/slirp/ip6_icmp.c +++ b/slirp/src/ip6_icmp.c @@ -1,14 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * 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" #define NDP_Interval g_rand_int_range(slirp->grand, \ NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval) @@ -16,8 +13,10 @@ static void ra_timer_handler(void *opaque) { Slirp *slirp = opaque; - timer_mod(slirp->ra_timer, - qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL) + NDP_Interval); + + slirp->cb->timer_mod(slirp->ra_timer, + slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval, + slirp->opaque); ndp_send_ra(slirp); } @@ -27,9 +26,10 @@ void icmp6_init(Slirp *slirp) 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); + slirp->ra_timer = slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque); + slirp->cb->timer_mod(slirp->ra_timer, + slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval, + slirp->opaque); } void icmp6_cleanup(Slirp *slirp) @@ -38,8 +38,7 @@ void icmp6_cleanup(Slirp *slirp) return; } - timer_del(slirp->ra_timer); - timer_free(slirp->ra_timer); + slirp->cb->timer_free(slirp->ra_timer, slirp->opaque); } static void icmp6_send_echoreply(struct mbuf *m, Slirp *slirp, struct ip6 *ip, @@ -72,9 +71,10 @@ 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 *); + char addrstr[INET6_ADDRSTRLEN]; DEBUG_CALL("icmp6_send_error"); - DEBUG_ARGS((dfd, " type = %d, code = %d\n", type, code)); + DEBUG_ARG("type = %d, code = %d", type, code); if (IN6_IS_ADDR_MULTICAST(&ip->ip_src) || in6_zero(&ip->ip_src)) { @@ -88,11 +88,8 @@ void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code) 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, @@ -220,12 +217,12 @@ void ndp_send_ra(Slirp *slirp) */ 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_CALL("ndp_send_ns"); DEBUG_ARG("target = %s", addrstr); -#endif /* Build IPv6 packet */ struct mbuf *t = m_get(slirp); @@ -340,8 +337,8 @@ static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip, case ICMP6_NDP_RA: DEBUG_CALL(" type = Router Advertisement"); - qemu_log_mask(LOG_GUEST_ERROR, - "Warning: guest sent NDP RA, but shouldn't"); + slirp->cb->guest_error("Warning: guest sent NDP RA, but shouldn't", + slirp->opaque); break; case ICMP6_NDP_NS: @@ -374,8 +371,8 @@ static void ndp_input(struct mbuf *m, Slirp *slirp, struct ip6 *ip, case ICMP6_NDP_REDIRECT: DEBUG_CALL(" type = Redirect"); - qemu_log_mask(LOG_GUEST_ERROR, - "Warning: guest sent NDP REDIRECT, but shouldn't"); + slirp->cb->guest_error( + "Warning: guest sent NDP REDIRECT, but shouldn't", slirp->opaque); break; } } @@ -391,7 +388,7 @@ void icmp6_input(struct mbuf *m) int hlen = sizeof(struct ip6); DEBUG_CALL("icmp6_input"); - DEBUG_ARG("m = %lx", (long) m); + DEBUG_ARG("m = %p", m); DEBUG_ARG("m_len = %d", m->m_len); if (ntohs(ip->ip_pl) < ICMP6_MINLEN) { @@ -415,7 +412,7 @@ void icmp6_input(struct mbuf *m) icmp6_send_echoreply(m, slirp, ip, icmp); } else { /* TODO */ - error_report("external icmpv6 not supported yet"); + g_critical("external icmpv6 not supported yet"); } break; diff --git a/slirp/ip6_icmp.h b/slirp/src/ip6_icmp.h similarity index 93% rename from slirp/ip6_icmp.h rename to slirp/src/ip6_icmp.h index b3378b17b5..d8d13e30fc 100644 --- a/slirp/ip6_icmp.h +++ b/slirp/src/ip6_icmp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 2013 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. @@ -34,7 +35,7 @@ struct ndp_rs { /* Router Solicitation Message */ struct ndp_ra { /* Router Advertisement Message */ uint8_t chl; /* Cur Hop Limit */ -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint8_t M:1, O:1, @@ -48,15 +49,19 @@ struct ndp_ra { /* Router Advertisement Message */ uint16_t lifetime; /* Router Lifetime */ uint32_t reach_time; /* Reachable Time */ uint32_t retrans_time; /* Retrans Timer */ -} QEMU_PACKED; +}; + +G_STATIC_ASSERT(sizeof(struct ndp_ra) == 12); struct ndp_ns { /* Neighbor Solicitation Message */ uint32_t reserved; struct in6_addr target; /* Target Address */ -} QEMU_PACKED; +}; + +G_STATIC_ASSERT(sizeof(struct ndp_ns) == 20); struct ndp_na { /* Neighbor Advertisement Message */ -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint32_t R:1, /* Router Flag */ S:1, /* Solicited Flag */ @@ -72,13 +77,17 @@ struct ndp_na { /* Neighbor Advertisement Message */ reserved_lo:24; #endif struct in6_addr target; /* Target Address */ -} QEMU_PACKED; +}; + +G_STATIC_ASSERT(sizeof(struct ndp_na) == 20); struct ndp_redirect { uint32_t reserved; struct in6_addr target; /* Target Address */ struct in6_addr dest; /* Destination Address */ -} QEMU_PACKED; +}; + +G_STATIC_ASSERT(sizeof(struct ndp_redirect) == 36); /* * Structure of an icmpv6 header. @@ -103,7 +112,9 @@ struct icmp6 { #define icmp6_nns icmp6_body.ndp_ns #define icmp6_nna icmp6_body.ndp_na #define icmp6_redirect icmp6_body.ndp_redirect -} QEMU_PACKED; +}; + +G_STATIC_ASSERT(sizeof(struct icmp6) == 40); #define ICMP6_MINLEN 4 #define ICMP6_ERROR_MINLEN 8 @@ -125,7 +136,7 @@ struct ndpopt { #define ndpopt_linklayer ndpopt_body.linklayer_addr struct prefixinfo { /* Prefix Information */ uint8_t prefix_length; -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint8_t L:1, A:1, reserved1:6; #else uint8_t reserved1:6, A:1, L:1; @@ -134,16 +145,16 @@ struct ndpopt { uint32_t pref_lt; /* Preferred Lifetime */ uint32_t reserved2; struct in6_addr prefix; - } QEMU_PACKED prefixinfo; + } SLIRP_PACKED prefixinfo; #define ndpopt_prefixinfo ndpopt_body.prefixinfo struct rdnss { uint16_t reserved; uint32_t lifetime; struct in6_addr addr; - } QEMU_PACKED rdnss; + } SLIRP_PACKED rdnss; #define ndpopt_rdnss ndpopt_body.rdnss } ndpopt_body; -} QEMU_PACKED; +} SLIRP_PACKED; /* NDP options type */ #define NDPOPT_LINKLAYER_SOURCE 1 /* Source Link-Layer Address */ diff --git a/slirp/ip6_input.c b/slirp/src/ip6_input.c similarity index 95% rename from slirp/ip6_input.c rename to slirp/src/ip6_input.c index ac2e3ea882..d9d2b7e9cd 100644 --- a/slirp/ip6_input.c +++ b/slirp/src/ip6_input.c @@ -1,9 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 2013 * Guillaume Subiron, Yann Bordenave, Serigne Modou Wagne. */ -#include "qemu/osdep.h" #include "slirp.h" #include "ip6_icmp.h" @@ -31,7 +31,7 @@ void ip6_input(struct mbuf *m) } DEBUG_CALL("ip6_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 ip6)) { diff --git a/slirp/ip6_output.c b/slirp/src/ip6_output.c similarity index 85% rename from slirp/ip6_output.c rename to slirp/src/ip6_output.c index 762cbfe89c..b86110662c 100644 --- a/slirp/ip6_output.c +++ b/slirp/src/ip6_output.c @@ -1,10 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * 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 @@ -19,8 +18,8 @@ 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); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); /* Fill IPv6 header */ ip->ip_v = IP6VERSION; diff --git a/slirp/ip_icmp.c b/slirp/src/ip_icmp.c similarity index 85% rename from slirp/ip_icmp.c rename to slirp/src/ip_icmp.c index 0b667a429a..1aea18afa7 100644 --- a/slirp/ip_icmp.c +++ b/slirp/src/ip_icmp.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. @@ -30,10 +31,13 @@ * 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" +#ifndef WITH_ICMP_ERROR_MSG +#define WITH_ICMP_ERROR_MSG 0 +#endif + /* The message sent when emulating PING */ /* 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"; @@ -79,7 +83,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) struct ip *ip = mtod(m, struct ip *); struct sockaddr_in addr; - so->s = qemu_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); + so->s = slirp_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); if (so->s == -1) { return -1; } @@ -99,8 +103,8 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) { - DEBUG_MISC((dfd, "icmp_input icmp sendto tx errno = %d-%s\n", - errno, strerror(errno))); + DEBUG_MISC("icmp_input icmp sendto tx errno = %d-%s", + errno, strerror(errno)); icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); icmp_detach(so); } @@ -110,6 +114,7 @@ static int icmp_send(struct socket *so, struct mbuf *m, int hlen) void icmp_detach(struct socket *so) { + so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); closesocket(so->s); sofree(so); } @@ -160,13 +165,13 @@ icmp_input(struct mbuf *m, int hlen) } else { struct socket *so; struct sockaddr_storage addr; - if ((so = socreate(slirp)) == NULL) goto freeit; + so = socreate(slirp); if (icmp_send(so, m, hlen) == 0) { return; } if (udp_attach(so, AF_INET) == -1) { - DEBUG_MISC((dfd,"icmp_input udp_attach errno = %d-%s\n", - errno,strerror(errno))); + DEBUG_MISC("icmp_input udp_attach errno = %d-%s", + errno,strerror(errno)); sofree(so); m_free(m); goto end_error; @@ -188,8 +193,8 @@ icmp_input(struct mbuf *m, int hlen) if(sendto(so->s, icmp_ping_msg, strlen(icmp_ping_msg), 0, (struct sockaddr *)&addr, sockaddr_size(&addr)) == -1) { - DEBUG_MISC((dfd,"icmp_input udp sendto tx errno = %d-%s\n", - errno,strerror(errno))); + DEBUG_MISC("icmp_input udp sendto tx errno = %d-%s", + errno,strerror(errno)); icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); udp_detach(so); } @@ -236,7 +241,7 @@ end_error: #define ICMP_MAXDATALEN (IP_MSS-28) void -icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, +icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, const char *message) { unsigned hlen, shlen, s_ip_len; @@ -253,13 +258,12 @@ icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, /* check msrc */ if(!msrc) goto end_error; ip = mtod(msrc, struct ip *); -#ifdef DEBUG - { char bufa[20], bufb[20]; + if (slirp_debug & DBG_MISC) { + char bufa[20], bufb[20]; strcpy(bufa, inet_ntoa(ip->ip_src)); strcpy(bufb, inet_ntoa(ip->ip_dst)); - DEBUG_MISC((dfd, " %.16s to %.16s\n", bufa, bufb)); + DEBUG_MISC(" %.16s to %.16s", bufa, bufb); } -#endif if(ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */ /* Do not reply to source-only IPs */ @@ -319,8 +323,7 @@ icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, HTONS(icp->icmp_ip.ip_id); HTONS(icp->icmp_ip.ip_off); -#ifdef DEBUG - if(message) { /* DEBUG : append message to ICMP packet */ + if (message && WITH_ICMP_ERROR_MSG) { /* append message to ICMP packet */ int message_len; char *cpnt; message_len=strlen(message); @@ -329,7 +332,6 @@ icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, memcpy(cpnt, message, message_len); m->m_len+=message_len; } -#endif icp->icmp_cksum = 0; icp->icmp_cksum = cksum(m, m->m_len); @@ -387,7 +389,7 @@ icmp_reflect(struct mbuf *m) * Strip out original options by copying rest of first * mbuf's data back, and adjust the IP length. */ - memmove((caddr_t)(ip + 1), (caddr_t)ip + hlen, + memmove((char *)(ip + 1), (char *)ip + hlen, (unsigned )(m->m_len - hlen)); hlen -= optlen; ip->ip_hl = hlen >> 2; @@ -411,7 +413,7 @@ void icmp_receive(struct socket *so) struct mbuf *m = so->so_m; struct ip *ip = mtod(m, struct ip *); int hlen = ip->ip_hl << 2; - u_char error_code; + uint8_t error_code; struct icmp *icp; int id, len; @@ -420,7 +422,32 @@ void icmp_receive(struct socket *so) icp = mtod(m, struct icmp *); id = icp->icmp_id; - len = qemu_recv(so->s, icp, m->m_len, 0); + len = recv(so->s, icp, M_ROOM(m), 0); + /* + * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent + * between host OSes. On Linux, only the ICMP header and payload is + * included. On macOS/Darwin, the socket acts like a raw socket and + * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP + * sockets aren't supported at all, so we treat them like raw sockets. It + * isn't possible to detect this difference at runtime, so we must use an + * #ifdef to determine if we need to remove the IP header. + */ +#ifdef CONFIG_BSD + if (len >= sizeof(struct ip)) { + struct ip *inner_ip = mtod(m, struct ip *); + int inner_hlen = inner_ip->ip_hl << 2; + if (inner_hlen > len) { + len = -1; + errno = -EINVAL; + } else { + len -= inner_hlen; + memmove(icp, (unsigned char *)icp + inner_hlen, len); + } + } else { + len = -1; + errno = -EINVAL; + } +#endif icp->icmp_id = id; m->m_data -= hlen; @@ -432,8 +459,8 @@ void icmp_receive(struct socket *so) } else { error_code = ICMP_UNREACH_HOST; } - DEBUG_MISC((dfd, " udp icmp rx errno = %d-%s\n", errno, - strerror(errno))); + DEBUG_MISC(" udp icmp rx errno = %d-%s", errno, + strerror(errno)); icmp_send_error(so->so_m, ICMP_UNREACH, error_code, 0, strerror(errno)); } else { icmp_reflect(so->so_m); diff --git a/slirp/ip_icmp.h b/slirp/src/ip_icmp.h similarity index 93% rename from slirp/ip_icmp.h rename to slirp/src/ip_icmp.h index d88ab34c1b..05d85c59dd 100644 --- a/slirp/ip_icmp.h +++ b/slirp/src/ip_icmp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. @@ -44,22 +45,22 @@ typedef uint32_t n_time; * Structure of an icmp header. */ struct icmp { - u_char icmp_type; /* type of message, see below */ - u_char icmp_code; /* type sub code */ - u_short icmp_cksum; /* ones complement cksum of struct */ + uint8_t icmp_type; /* type of message, see below */ + uint8_t icmp_code; /* type sub code */ + uint16_t icmp_cksum; /* ones complement cksum of struct */ union { - u_char ih_pptr; /* ICMP_PARAMPROB */ + uint8_t ih_pptr; /* ICMP_PARAMPROB */ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */ struct ih_idseq { - u_short icd_id; - u_short icd_seq; + uint16_t icd_id; + uint16_t icd_seq; } ih_idseq; int ih_void; /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */ struct ih_pmtu { - u_short ipm_void; - u_short ipm_nextmtu; + uint16_t ipm_void; + uint16_t ipm_nextmtu; } ih_pmtu; } icmp_hun; #define icmp_pptr icmp_hun.ih_pptr @@ -156,7 +157,7 @@ struct icmp { void icmp_init(Slirp *slirp); void icmp_cleanup(Slirp *slirp); void icmp_input(struct mbuf *, int); -void icmp_send_error(struct mbuf *msrc, u_char type, u_char code, int minsize, +void icmp_send_error(struct mbuf *msrc, uint8_t type, uint8_t code, int minsize, const char *message); void icmp_reflect(struct mbuf *); void icmp_receive(struct socket *so); diff --git a/slirp/ip_input.c b/slirp/src/ip_input.c similarity index 69% rename from slirp/ip_input.c rename to slirp/src/ip_input.c index 348e1dca5a..a714fecd58 100644 --- a/slirp/ip_input.c +++ b/slirp/src/ip_input.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. @@ -33,12 +34,8 @@ /* * Changes and additions relating to SLiRP are * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" #include "ip_icmp.h" @@ -193,7 +190,7 @@ ip_input(struct mbuf *m) m = dtom(slirp, ip); } else if (fp) - ip_freef(slirp, fp); + ip_freef(slirp, fp); } else ip->ip_len -= hlen; @@ -447,206 +444,6 @@ ip_slowtimo(Slirp *slirp) } } -/* - * Do option processing on a datagram, - * possibly discarding it if bad options are encountered, - * or forwarding it if source-routed. - * Returns 1 if packet has been forwarded/freed, - * 0 if the packet should be processed further. - */ - -#ifdef notdef - -int -ip_dooptions(m) - struct mbuf *m; -{ - register struct ip *ip = mtod(m, struct ip *); - register u_char *cp; - register struct ip_timestamp *ipt; - register struct in_ifaddr *ia; - int opt, optlen, cnt, off, code, type, forward = 0; - struct in_addr *sin, dst; -typedef uint32_t n_time; - n_time ntime; - - dst = ip->ip_dst; - cp = (u_char *)(ip + 1); - cnt = (ip->ip_hl << 2) - sizeof (struct ip); - for (; cnt > 0; cnt -= optlen, cp += optlen) { - opt = cp[IPOPT_OPTVAL]; - if (opt == IPOPT_EOL) - break; - if (opt == IPOPT_NOP) - optlen = 1; - else { - optlen = cp[IPOPT_OLEN]; - if (optlen <= 0 || optlen > cnt) { - code = &cp[IPOPT_OLEN] - (u_char *)ip; - goto bad; - } - } - switch (opt) { - - default: - break; - - /* - * Source routing with record. - * Find interface with current destination address. - * If none on this machine then drop if strictly routed, - * or do nothing if loosely routed. - * Record interface address and bring up next address - * component. If strictly routed make sure next - * address is on directly accessible net. - */ - case IPOPT_LSRR: - case IPOPT_SSRR: - if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { - code = &cp[IPOPT_OFFSET] - (u_char *)ip; - goto bad; - } - ipaddr.sin_addr = ip->ip_dst; - ia = (struct in_ifaddr *) - ifa_ifwithaddr((struct sockaddr *)&ipaddr); - if (ia == 0) { - if (opt == IPOPT_SSRR) { - type = ICMP_UNREACH; - code = ICMP_UNREACH_SRCFAIL; - goto bad; - } - /* - * Loose routing, and not at next destination - * yet; nothing to do except forward. - */ - break; - } - off--; /* 0 origin */ - if (off > optlen - sizeof(struct in_addr)) { - /* - * End of source route. Should be for us. - */ - save_rte(cp, ip->ip_src); - break; - } - /* - * locate outgoing interface - */ - bcopy((caddr_t)(cp + off), (caddr_t)&ipaddr.sin_addr, - sizeof(ipaddr.sin_addr)); - if (opt == IPOPT_SSRR) { -#define INA struct in_ifaddr * -#define SA struct sockaddr * - if ((ia = (INA)ifa_ifwithdstaddr((SA)&ipaddr)) == 0) - ia = (INA)ifa_ifwithnet((SA)&ipaddr); - } else - ia = ip_rtaddr(ipaddr.sin_addr); - if (ia == 0) { - type = ICMP_UNREACH; - code = ICMP_UNREACH_SRCFAIL; - goto bad; - } - ip->ip_dst = ipaddr.sin_addr; - bcopy((caddr_t)&(IA_SIN(ia)->sin_addr), - (caddr_t)(cp + off), sizeof(struct in_addr)); - cp[IPOPT_OFFSET] += sizeof(struct in_addr); - /* - * Let ip_intr's mcast routing check handle mcast pkts - */ - forward = !IN_MULTICAST(ntohl(ip->ip_dst.s_addr)); - break; - - case IPOPT_RR: - if ((off = cp[IPOPT_OFFSET]) < IPOPT_MINOFF) { - code = &cp[IPOPT_OFFSET] - (u_char *)ip; - goto bad; - } - /* - * If no space remains, ignore. - */ - off--; /* 0 origin */ - if (off > optlen - sizeof(struct in_addr)) - break; - bcopy((caddr_t)(&ip->ip_dst), (caddr_t)&ipaddr.sin_addr, - sizeof(ipaddr.sin_addr)); - /* - * locate outgoing interface; if we're the destination, - * use the incoming interface (should be same). - */ - if ((ia = (INA)ifa_ifwithaddr((SA)&ipaddr)) == 0 && - (ia = ip_rtaddr(ipaddr.sin_addr)) == 0) { - type = ICMP_UNREACH; - code = ICMP_UNREACH_HOST; - goto bad; - } - bcopy((caddr_t)&(IA_SIN(ia)->sin_addr), - (caddr_t)(cp + off), sizeof(struct in_addr)); - cp[IPOPT_OFFSET] += sizeof(struct in_addr); - break; - - case IPOPT_TS: - code = cp - (u_char *)ip; - ipt = (struct ip_timestamp *)cp; - if (ipt->ipt_len < 5) - goto bad; - if (ipt->ipt_ptr > ipt->ipt_len - sizeof (int32_t)) { - if (++ipt->ipt_oflw == 0) - goto bad; - break; - } - sin = (struct in_addr *)(cp + ipt->ipt_ptr - 1); - switch (ipt->ipt_flg) { - - case IPOPT_TS_TSONLY: - break; - - case IPOPT_TS_TSANDADDR: - if (ipt->ipt_ptr + sizeof(n_time) + - sizeof(struct in_addr) > ipt->ipt_len) - goto bad; - ipaddr.sin_addr = dst; - ia = (INA)ifaof_ i f p foraddr((SA)&ipaddr, - m->m_pkthdr.rcvif); - if (ia == 0) - continue; - bcopy((caddr_t)&IA_SIN(ia)->sin_addr, - (caddr_t)sin, sizeof(struct in_addr)); - ipt->ipt_ptr += sizeof(struct in_addr); - break; - - case IPOPT_TS_PRESPEC: - if (ipt->ipt_ptr + sizeof(n_time) + - sizeof(struct in_addr) > ipt->ipt_len) - goto bad; - bcopy((caddr_t)sin, (caddr_t)&ipaddr.sin_addr, - sizeof(struct in_addr)); - if (ifa_ifwithaddr((SA)&ipaddr) == 0) - continue; - ipt->ipt_ptr += sizeof(struct in_addr); - break; - - default: - goto bad; - } - ntime = iptime(); - bcopy((caddr_t)&ntime, (caddr_t)cp + ipt->ipt_ptr - 1, - sizeof(n_time)); - ipt->ipt_ptr += sizeof(n_time); - } - } - if (forward) { - ip_forward(m, 1); - return (1); - } - return (0); -bad: - icmp_send_error(m, type, code, 0, 0); - - return (1); -} - -#endif /* notdef */ - /* * Strip out IP options, at higher * level protocol in the kernel. @@ -659,11 +456,11 @@ ip_stripoptions(register struct mbuf *m, struct mbuf *mopt) { register int i; struct ip *ip = mtod(m, struct ip *); - register caddr_t opts; + register char *opts; int olen; olen = (ip->ip_hl<<2) - sizeof (struct ip); - opts = (caddr_t)(ip + 1); + opts = (char *)(ip + 1); i = m->m_len - (sizeof (struct ip) + olen); memcpy(opts, opts + olen, (unsigned)i); m->m_len -= olen; diff --git a/slirp/ip_output.c b/slirp/src/ip_output.c similarity index 97% rename from slirp/ip_output.c rename to slirp/src/ip_output.c index db403f04c1..8560197cf6 100644 --- a/slirp/ip_output.c +++ b/slirp/src/ip_output.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -33,12 +34,8 @@ /* * Changes and additions relating to SLiRP are * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" /* Number of packets queued before we start sending diff --git a/slirp/src/libslirp.h b/slirp/src/libslirp.h new file mode 100644 index 0000000000..3b28764bec --- /dev/null +++ b/slirp/src/libslirp.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef LIBSLIRP_H +#define LIBSLIRP_H + +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Slirp Slirp; + +enum { + SLIRP_POLL_IN = 1 << 0, + SLIRP_POLL_OUT = 1 << 1, + SLIRP_POLL_PRI = 1 << 2, + SLIRP_POLL_ERR = 1 << 3, + SLIRP_POLL_HUP = 1 << 4, +}; + +typedef ssize_t (*SlirpReadCb)(void *buf, size_t len, void *opaque); +typedef ssize_t (*SlirpWriteCb)(const void *buf, size_t len, void *opaque); +typedef void (*SlirpTimerCb)(void *opaque); +typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque); +typedef int (*SlirpGetREventsCb)(int idx, void *opaque); + +/* + * Callbacks from slirp + */ +typedef struct SlirpCb { + /* + * Send an ethernet frame to the guest network. The opaque + * parameter is the one given to slirp_init(). The function + * doesn't need to send all the data and may return size) { @@ -159,17 +156,17 @@ m_inc(struct mbuf *m, int size) } if (m->m_flags & M_EXT) { - datasize = m->m_data - m->m_ext; - m->m_ext = g_realloc(m->m_ext, size + datasize); + gapsize = m->m_data - m->m_ext; + m->m_ext = g_realloc(m->m_ext, size + gapsize); } else { - datasize = m->m_data - m->m_dat; - m->m_ext = g_malloc(size + datasize); + gapsize = m->m_data - m->m_dat; + m->m_ext = g_malloc(size + gapsize); memcpy(m->m_ext, m->m_dat, m->m_size); m->m_flags |= M_EXT; } - m->m_data = m->m_ext + datasize; - m->m_size = size + datasize; + m->m_data = m->m_ext + gapsize; + m->m_size = size + gapsize; } @@ -232,7 +229,7 @@ dtom(Slirp *slirp, void *dat) } } - DEBUG_ERROR((dfd, "dtom failed")); + DEBUG_ERROR("dtom failed"); return (struct mbuf *)0; } diff --git a/slirp/mbuf.h b/slirp/src/mbuf.h similarity index 87% rename from slirp/mbuf.h rename to slirp/src/mbuf.h index 33b84485d6..732c85c63c 100644 --- a/slirp/mbuf.h +++ b/slirp/src/mbuf.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1993 * The Regents of the University of California. All rights reserved. @@ -47,6 +48,19 @@ * free the m_ext. This is inefficient memory-wise, but who cares. */ +/* + * mbufs allow to have a gap between the start of the allocated buffer (m_ext if + * M_EXT is set, m_dat otherwise) and the in-use data: + * + * |--gapsize----->|---m_len-------> + * |----------m_size------------------------------> + * |----M_ROOM--------------------> + * |-M_FREEROOM--> + * + * ^ ^ ^ + * m_dat/m_ext m_data end of buffer + */ + /* * How much room is in the mbuf, from m_data to the end of the mbuf */ @@ -59,7 +73,6 @@ * How much free room there is */ #define M_FREEROOM(m) (M_ROOM(m) - (m)->m_len) -#define M_TRAILINGSPACE M_FREEROOM struct mbuf { /* XXX should union some of these! */ @@ -73,7 +86,7 @@ struct mbuf { int m_size; /* Size of mbuf, from m_dat or m_ext */ struct socket *m_so; - caddr_t m_data; /* Current location of data */ + char *m_data; /* Current location of data */ int m_len; /* Amount of data in this mbuf, from m_data */ Slirp *slirp; diff --git a/slirp/src/misc.c b/slirp/src/misc.c new file mode 100644 index 0000000000..7c5db0e0aa --- /dev/null +++ b/slirp/src/misc.c @@ -0,0 +1,321 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 1995 Danny Gasparovski. + */ + +#include "slirp.h" + +inline void +insque(void *a, void *b) +{ + register struct quehead *element = (struct quehead *) a; + register struct quehead *head = (struct quehead *) b; + element->qh_link = head->qh_link; + head->qh_link = (struct quehead *)element; + element->qh_rlink = (struct quehead *)head; + ((struct quehead *)(element->qh_link))->qh_rlink + = (struct quehead *)element; +} + +inline void +remque(void *a) +{ + register struct quehead *element = (struct quehead *) a; + ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; + ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link; + element->qh_rlink = NULL; +} + +/* TODO: IPv6 */ +struct gfwd_list * +add_guestfwd(struct gfwd_list **ex_ptr, + SlirpWriteCb write_cb, void *opaque, + struct in_addr addr, int port) +{ + struct gfwd_list *f = g_new0(struct gfwd_list, 1); + + f->write_cb = write_cb; + f->opaque = opaque; + f->ex_fport = port; + f->ex_addr = addr; + f->ex_next = *ex_ptr; + *ex_ptr = f; + + return f; +} + +struct gfwd_list * +add_exec(struct gfwd_list **ex_ptr, const char *cmdline, + struct in_addr addr, int port) +{ + struct gfwd_list *f = add_guestfwd(ex_ptr, NULL, NULL, addr, port); + + f->ex_exec = g_strdup(cmdline); + + return f; +} + +static int +slirp_socketpair_with_oob(int sv[2]) +{ + struct sockaddr_in addr = { + .sin_family = AF_INET, + .sin_port = 0, + .sin_addr.s_addr = INADDR_ANY, + }; + socklen_t addrlen = sizeof(addr); + int ret, s; + + sv[1] = -1; + s = slirp_socket(AF_INET, SOCK_STREAM, 0); + if (s < 0 || bind(s, (struct sockaddr *)&addr, addrlen) < 0 || + listen(s, 1) < 0 || + getsockname(s, (struct sockaddr *)&addr, &addrlen) < 0) { + goto err; + } + + sv[1] = slirp_socket(AF_INET, SOCK_STREAM, 0); + if (sv[1] < 0) { + goto err; + } + /* + * This connect won't block because we've already listen()ed on + * the server end (even though we won't accept() the connection + * until later on). + */ + do { + ret = connect(sv[1], (struct sockaddr *)&addr, addrlen); + } while (ret < 0 && errno == EINTR); + if (ret < 0) { + goto err; + } + + do { + sv[0] = accept(s, (struct sockaddr *)&addr, &addrlen); + } while (sv[0] < 0 && errno == EINTR); + if (sv[0] < 0) { + goto err; + } + + closesocket(s); + return 0; + +err: + g_critical("slirp_socketpair(): %s", strerror(errno)); + if (s >= 0) { + closesocket(s); + } + if (sv[1] >= 0) { + closesocket(sv[1]); + } + return -1; +} + +static void +fork_exec_child_setup(gpointer data) +{ +#ifndef _WIN32 + setsid(); +#endif +} + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated-declarations" + +#if !GLIB_CHECK_VERSION(2, 58, 0) +typedef struct SlirpGSpawnFds { + GSpawnChildSetupFunc child_setup; + gpointer user_data; + gint stdin_fd; + gint stdout_fd; + gint stderr_fd; +} SlirpGSpawnFds; + +static inline void +slirp_gspawn_fds_setup(gpointer user_data) +{ + SlirpGSpawnFds *q = (SlirpGSpawnFds *)user_data; + + dup2(q->stdin_fd, 0); + dup2(q->stdout_fd, 1); + dup2(q->stderr_fd, 2); + q->child_setup(q->user_data); +} +#endif + +static inline gboolean +g_spawn_async_with_fds_slirp(const gchar *working_directory, + gchar **argv, + gchar **envp, + GSpawnFlags flags, + GSpawnChildSetupFunc child_setup, + gpointer user_data, + GPid *child_pid, + gint stdin_fd, + gint stdout_fd, + gint stderr_fd, + GError **error) +{ +#if GLIB_CHECK_VERSION(2, 58, 0) + return g_spawn_async_with_fds(working_directory, argv, envp, flags, + child_setup, user_data, + child_pid, stdin_fd, stdout_fd, stderr_fd, + error); +#else + SlirpGSpawnFds setup = { + .child_setup = child_setup, + .user_data = user_data, + .stdin_fd = stdin_fd, + .stdout_fd = stdout_fd, + .stderr_fd = stderr_fd, + }; + + return g_spawn_async(working_directory, argv, envp, flags, + slirp_gspawn_fds_setup, &setup, + child_pid, error); +#endif +} + +#define g_spawn_async_with_fds(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) \ + g_spawn_async_with_fds_slirp(wd, argv, env, f, c, d, p, ifd, ofd, efd, err) + +#pragma GCC diagnostic pop + +int +fork_exec(struct socket *so, const char *ex) +{ + GError *err = NULL; + char **argv; + int opt, sp[2]; + + DEBUG_CALL("fork_exec"); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("ex = %p", ex); + + if (slirp_socketpair_with_oob(sp) < 0) { + return 0; + } + + argv = g_strsplit(ex, " ", -1); + g_spawn_async_with_fds(NULL /* cwd */, + argv, + NULL /* env */, + G_SPAWN_SEARCH_PATH, + fork_exec_child_setup, NULL /* data */, + NULL /* child_pid */, + sp[1], sp[1], sp[1], + &err); + g_strfreev(argv); + + if (err) { + g_critical("fork_exec: %s", err->message); + g_error_free(err); + closesocket(sp[0]); + closesocket(sp[1]); + return 0; + } + + so->s = sp[0]; + closesocket(sp[1]); + slirp_socket_set_fast_reuse(so->s); + opt = 1; + setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + slirp_set_nonblock(so->s); + so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); + return 1; +} + +char *slirp_connection_info(Slirp *slirp) +{ + GString *str = g_string_new(NULL); + const char * const tcpstates[] = { + [TCPS_CLOSED] = "CLOSED", + [TCPS_LISTEN] = "LISTEN", + [TCPS_SYN_SENT] = "SYN_SENT", + [TCPS_SYN_RECEIVED] = "SYN_RCVD", + [TCPS_ESTABLISHED] = "ESTABLISHED", + [TCPS_CLOSE_WAIT] = "CLOSE_WAIT", + [TCPS_FIN_WAIT_1] = "FIN_WAIT_1", + [TCPS_CLOSING] = "CLOSING", + [TCPS_LAST_ACK] = "LAST_ACK", + [TCPS_FIN_WAIT_2] = "FIN_WAIT_2", + [TCPS_TIME_WAIT] = "TIME_WAIT", + }; + struct in_addr dst_addr; + struct sockaddr_in src; + socklen_t src_len; + uint16_t dst_port; + struct socket *so; + const char *state; + char buf[20]; + + g_string_append_printf(str, + " Protocol[State] FD Source Address Port " + "Dest. Address Port RecvQ SendQ\n"); + + /* TODO: IPv6 */ + + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { + if (so->so_state & SS_HOSTFWD) { + state = "HOST_FORWARD"; + } else if (so->so_tcpcb) { + state = tcpstates[so->so_tcpcb->t_state]; + } else { + state = "NONE"; + } + if (so->so_state & (SS_HOSTFWD | SS_INCOMING)) { + src_len = sizeof(src); + getsockname(so->s, (struct sockaddr *)&src, &src_len); + dst_addr = so->so_laddr; + dst_port = so->so_lport; + } else { + src.sin_addr = so->so_laddr; + src.sin_port = so->so_lport; + dst_addr = so->so_faddr; + dst_port = so->so_fport; + } + snprintf(buf, sizeof(buf), " TCP[%s]", state); + g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s, + src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*", + ntohs(src.sin_port)); + g_string_append_printf(str, "%15s %5d %5d %5d\n", + inet_ntoa(dst_addr), ntohs(dst_port), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } + + for (so = slirp->udb.so_next; so != &slirp->udb; so = so->so_next) { + if (so->so_state & SS_HOSTFWD) { + snprintf(buf, sizeof(buf), " UDP[HOST_FORWARD]"); + src_len = sizeof(src); + getsockname(so->s, (struct sockaddr *)&src, &src_len); + dst_addr = so->so_laddr; + dst_port = so->so_lport; + } else { + snprintf(buf, sizeof(buf), " UDP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + src.sin_port = so->so_lport; + dst_addr = so->so_faddr; + dst_port = so->so_fport; + } + g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s, + src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*", + ntohs(src.sin_port)); + g_string_append_printf(str, "%15s %5d %5d %5d\n", + inet_ntoa(dst_addr), ntohs(dst_port), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } + + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so->so_next) { + snprintf(buf, sizeof(buf), " ICMP[%d sec]", + (so->so_expire - curtime) / 1000); + src.sin_addr = so->so_laddr; + dst_addr = so->so_faddr; + g_string_append_printf(str, "%-19s %3d %15s - ", buf, so->s, + src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : "*"); + g_string_append_printf(str, "%15s - %5d %5d\n", inet_ntoa(dst_addr), + so->so_rcv.sb_cc, so->so_snd.sb_cc); + } + + return g_string_free(str, FALSE); +} diff --git a/slirp/misc.h b/slirp/src/misc.h similarity index 60% rename from slirp/misc.h rename to slirp/src/misc.h index 5211bbd30a..23b7490448 100644 --- a/slirp/misc.h +++ b/slirp/src/misc.h @@ -1,19 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ #ifndef MISC_H #define MISC_H -struct ex_list { - int ex_pty; /* Do we want a pty? */ +#include "libslirp.h" + +struct gfwd_list { + SlirpWriteCb write_cb; + void *opaque; struct in_addr ex_addr; /* Server address */ int ex_fport; /* Port to telnet to */ - const char *ex_exec; /* Command line of what to exec */ - struct ex_list *ex_next; + char *ex_exec; /* Command line of what to exec */ + struct gfwd_list *ex_next; }; #define EMU_NONE 0x0 @@ -26,7 +27,6 @@ struct ex_list { #define EMU_REALAUDIO 0x5 #define EMU_RLOGIN 0x6 #define EMU_IDENT 0x7 -#define EMU_RSH 0x8 #define EMU_NOCONNECT 0x10 /* Don't connect */ @@ -52,7 +52,15 @@ struct slirp_quehead { void slirp_insque(void *, void *); void slirp_remque(void *); -int add_exec(struct ex_list **, int, char *, struct in_addr, int); -int fork_exec(struct socket *so, const char *ex, int do_pty); +int fork_exec(struct socket *so, const char *ex); + +struct gfwd_list * +add_guestfwd(struct gfwd_list **ex_ptr, + SlirpWriteCb write_cb, void *opaque, + struct in_addr addr, int port); + +struct gfwd_list * +add_exec(struct gfwd_list **ex_ptr, const char *cmdline, + struct in_addr addr, int port); #endif diff --git a/slirp/ncsi-pkt.h b/slirp/src/ncsi-pkt.h similarity index 93% rename from slirp/ncsi-pkt.h rename to slirp/src/ncsi-pkt.h index ea07d1cd0f..4c0be39f6e 100644 --- a/slirp/ncsi-pkt.h +++ b/slirp/src/ncsi-pkt.h @@ -1,10 +1,36 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright Gavin Shan, IBM Corporation 2016. * - * 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. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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 NCSI_PKT_H diff --git a/slirp/ncsi.c b/slirp/src/ncsi.c similarity index 74% rename from slirp/ncsi.c rename to slirp/src/ncsi.c index 7116034afc..6d574ec5ec 100644 --- a/slirp/ncsi.c +++ b/slirp/src/ncsi.c @@ -1,12 +1,39 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * NC-SI (Network Controller Sideband Interface) "echo" model * * Copyright (C) 2016-2018 IBM Corp. * - * This code is licensed under the GPL version 2 or later. See the - * COPYING file in the top-level directory. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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 "qemu/osdep.h" #include "slirp.h" #include "ncsi-pkt.h" @@ -128,7 +155,7 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) memset(reh->h_source, 0xff, ETH_ALEN); reh->h_proto = htons(ETH_P_NCSI); - for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) { + for (i = 0; i < G_N_ELEMENTS(ncsi_rsp_handlers); i++) { if (ncsi_rsp_handlers[i].type == nh->type + 0x80) { handler = &ncsi_rsp_handlers[i]; break; @@ -163,5 +190,5 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) *pchecksum = htonl(checksum); ncsi_rsp_len += 4; - slirp_output(slirp->opaque, ncsi_reply, ETH_HLEN + ncsi_rsp_len); + slirp_send_packet_all(slirp, ncsi_reply, ETH_HLEN + ncsi_rsp_len); } diff --git a/slirp/ndp_table.c b/slirp/src/ndp_table.c similarity index 75% rename from slirp/ndp_table.c rename to slirp/src/ndp_table.c index e1676a0a7b..78324877e2 100644 --- a/slirp/ndp_table.c +++ b/slirp/src/ndp_table.c @@ -1,27 +1,25 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * 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]) { + char addrstr[INET6_ADDRSTRLEN]; 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_CALL("ndp_table_add"); 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])); + DEBUG_ARG("hw addr = %02x:%02x:%02x:%02x:%02x:%02x", + ethaddr[0], ethaddr[1], ethaddr[2], + ethaddr[3], ethaddr[4], ethaddr[5]); if (IN6_IS_ADDR_MULTICAST(&ip_addr) || in6_zero(&ip_addr)) { /* Do not register multicast or unspecified addresses */ @@ -50,15 +48,14 @@ void ndp_table_add(Slirp *slirp, struct in6_addr ip_addr, bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, uint8_t out_ethaddr[ETH_ALEN]) { + char addrstr[INET6_ADDRSTRLEN]; 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_CALL("ndp_table_search"); DEBUG_ARG("ip = %s", addrstr); -#endif assert(!in6_zero(&ip_addr)); @@ -69,18 +66,18 @@ bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, 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])); + DEBUG_ARG("multicast addr = %02x:%02x:%02x:%02x:%02x:%02x", + 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])); + DEBUG_ARG("found hw addr = %02x:%02x:%02x:%02x:%02x:%02x", + out_ethaddr[0], out_ethaddr[1], out_ethaddr[2], + out_ethaddr[3], out_ethaddr[4], out_ethaddr[5]); return 1; } } diff --git a/slirp/src/qtailq.h b/slirp/src/qtailq.h new file mode 100644 index 0000000000..d8aa0e19a4 --- /dev/null +++ b/slirp/src/qtailq.h @@ -0,0 +1,194 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* $NetBSD: queue.h,v 1.52 2009/04/20 09:56:08 mschuett Exp $ */ + +/* + * slirp version: Copy from QEMU, removed all but tail queues. + */ + +/* + * Copyright (c) 1991, 1993 + * The Regents of the University of California. 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 the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + */ + +#ifndef QTAILQ_H +#define QTAILQ_H + +/* + * A tail queue is headed by a pair of pointers, one to the head of the + * list and the other to the tail of the list. The elements are doubly + * linked so that an arbitrary element can be removed without a need to + * traverse the list. New elements can be added to the list before or + * after an existing element, at the head of the list, or at the end of + * the list. A tail queue may be traversed in either direction. + */ +typedef struct QTailQLink { + void *tql_next; + struct QTailQLink *tql_prev; +} QTailQLink; + +/* + * Tail queue definitions. The union acts as a poor man template, as if + * it were QTailQLink. + */ +#define QTAILQ_HEAD(name, type) \ + union name { \ + struct type *tqh_first; /* first element */ \ + QTailQLink tqh_circ; /* link for circular backwards list */ \ + } + +#define QTAILQ_HEAD_INITIALIZER(head) \ + { .tqh_circ = { NULL, &(head).tqh_circ } } + +#define QTAILQ_ENTRY(type) \ + union { \ + struct type *tqe_next; /* next element */ \ + QTailQLink tqe_circ; /* link for circular backwards list */ \ + } + +#define QTAILQ_INIT(head) do { \ + (head)->tqh_first = NULL; \ + (head)->tqh_circ.tql_prev = &(head)->tqh_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_HEAD(head, elm, field) do { \ + if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ + (head)->tqh_first->field.tqe_circ.tql_prev = \ + &(elm)->field.tqe_circ; \ + else \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ + (head)->tqh_first = (elm); \ + (elm)->field.tqe_circ.tql_prev = &(head)->tqh_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_TAIL(head, elm, field) do { \ + (elm)->field.tqe_next = NULL; \ + (elm)->field.tqe_circ.tql_prev = (head)->tqh_circ.tql_prev; \ + (head)->tqh_circ.tql_prev->tql_next = (elm); \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ + if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ + (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ + &(elm)->field.tqe_circ; \ + else \ + (head)->tqh_circ.tql_prev = &(elm)->field.tqe_circ; \ + (listelm)->field.tqe_next = (elm); \ + (elm)->field.tqe_circ.tql_prev = &(listelm)->field.tqe_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.tqe_circ.tql_prev = (listelm)->field.tqe_circ.tql_prev; \ + (elm)->field.tqe_next = (listelm); \ + (listelm)->field.tqe_circ.tql_prev->tql_next = (elm); \ + (listelm)->field.tqe_circ.tql_prev = &(elm)->field.tqe_circ; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_REMOVE(head, elm, field) do { \ + if (((elm)->field.tqe_next) != NULL) \ + (elm)->field.tqe_next->field.tqe_circ.tql_prev = \ + (elm)->field.tqe_circ.tql_prev; \ + else \ + (head)->tqh_circ.tql_prev = (elm)->field.tqe_circ.tql_prev; \ + (elm)->field.tqe_circ.tql_prev->tql_next = (elm)->field.tqe_next; \ + (elm)->field.tqe_circ.tql_prev = NULL; \ +} while (/*CONSTCOND*/0) + +#define QTAILQ_FOREACH(var, head, field) \ + for ((var) = ((head)->tqh_first); \ + (var); \ + (var) = ((var)->field.tqe_next)) + +#define QTAILQ_FOREACH_SAFE(var, head, field, next_var) \ + for ((var) = ((head)->tqh_first); \ + (var) && ((next_var) = ((var)->field.tqe_next), 1); \ + (var) = (next_var)) + +#define QTAILQ_FOREACH_REVERSE(var, head, field) \ + for ((var) = QTAILQ_LAST(head); \ + (var); \ + (var) = QTAILQ_PREV(var, field)) + +#define QTAILQ_FOREACH_REVERSE_SAFE(var, head, field, prev_var) \ + for ((var) = QTAILQ_LAST(head); \ + (var) && ((prev_var) = QTAILQ_PREV(var, field)); \ + (var) = (prev_var)) + +/* + * Tail queue access methods. + */ +#define QTAILQ_EMPTY(head) ((head)->tqh_first == NULL) +#define QTAILQ_FIRST(head) ((head)->tqh_first) +#define QTAILQ_NEXT(elm, field) ((elm)->field.tqe_next) +#define QTAILQ_IN_USE(elm, field) ((elm)->field.tqe_circ.tql_prev != NULL) + +#define QTAILQ_LINK_PREV(link) \ + ((link).tql_prev->tql_prev->tql_next) +#define QTAILQ_LAST(head) \ + ((typeof((head)->tqh_first)) QTAILQ_LINK_PREV((head)->tqh_circ)) +#define QTAILQ_PREV(elm, field) \ + ((typeof((elm)->field.tqe_next)) QTAILQ_LINK_PREV((elm)->field.tqe_circ)) + +#define field_at_offset(base, offset, type) \ + ((type *) (((char *) (base)) + (offset))) + +/* + * Raw access of elements of a tail queue head. Offsets are all zero + * because it's a union. + */ +#define QTAILQ_RAW_FIRST(head) \ + field_at_offset(head, 0, void *) +#define QTAILQ_RAW_TQH_CIRC(head) \ + field_at_offset(head, 0, QTailQLink) + +/* + * Raw access of elements of a tail entry + */ +#define QTAILQ_RAW_NEXT(elm, entry) \ + field_at_offset(elm, entry, void *) +#define QTAILQ_RAW_TQE_CIRC(elm, entry) \ + field_at_offset(elm, entry, QTailQLink) +/* + * Tail queue traversal using pointer arithmetic. + */ +#define QTAILQ_RAW_FOREACH(elm, head, entry) \ + for ((elm) = *QTAILQ_RAW_FIRST(head); \ + (elm); \ + (elm) = *QTAILQ_RAW_NEXT(elm, entry)) +/* + * Tail queue insertion using pointer arithmetic. + */ +#define QTAILQ_RAW_INSERT_TAIL(head, elm, entry) do { \ + *QTAILQ_RAW_NEXT(elm, entry) = NULL; \ + QTAILQ_RAW_TQE_CIRC(elm, entry)->tql_prev = QTAILQ_RAW_TQH_CIRC(head)->tql_prev; \ + QTAILQ_RAW_TQH_CIRC(head)->tql_prev->tql_next = (elm); \ + QTAILQ_RAW_TQH_CIRC(head)->tql_prev = QTAILQ_RAW_TQE_CIRC(elm, entry); \ +} while (/*CONSTCOND*/0) + +#endif /* QTAILQ_H */ diff --git a/slirp/sbuf.c b/slirp/src/sbuf.c similarity index 95% rename from slirp/sbuf.c rename to slirp/src/sbuf.c index 912f235f65..9c0b31b513 100644 --- a/slirp/sbuf.c +++ b/slirp/src/sbuf.c @@ -1,13 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" -#include "qemu/main-loop.h" static void sbappendsb(struct sbuf *sb, struct mbuf *m); @@ -17,7 +13,7 @@ sbfree(struct sbuf *sb) free(sb->sb_data); } -void +bool sbdrop(struct sbuf *sb, int num) { int limit = sb->sb_datalen / 2; @@ -34,8 +30,10 @@ sbdrop(struct sbuf *sb, int num) sb->sb_rptr -= sb->sb_datalen; if (sb->sb_cc < limit && sb->sb_cc + num >= limit) { - qemu_notify_event(); + return true; } + + return false; } void diff --git a/slirp/sbuf.h b/slirp/src/sbuf.h similarity index 79% rename from slirp/sbuf.h rename to slirp/src/sbuf.h index a722ecb629..337af1bbde 100644 --- a/slirp/sbuf.h +++ b/slirp/src/sbuf.h @@ -1,14 +1,11 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ #ifndef SBUF_H #define SBUF_H -#define sbflush(sb) sbdrop((sb),(sb)->sb_cc) #define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc) struct sbuf { @@ -22,7 +19,7 @@ struct sbuf { }; void sbfree(struct sbuf *); -void sbdrop(struct sbuf *, int); +bool sbdrop(struct sbuf *, int); void sbreserve(struct sbuf *, int); void sbappend(struct socket *, struct mbuf *); void sbcopy(struct sbuf *, int, int, char *); diff --git a/slirp/src/slirp.c b/slirp/src/slirp.c new file mode 100644 index 0000000000..169c85b906 --- /dev/null +++ b/slirp/src/slirp.c @@ -0,0 +1,1118 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libslirp glue + * + * Copyright (c) 2004-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 "slirp.h" + + +#ifndef _WIN32 +#include +#endif + +int slirp_debug; + +/* Define to 1 if you want KEEPALIVE timers */ +bool slirp_do_keepalive; + +/* host loopback address */ +struct in_addr loopback_addr; +/* host loopback network mask */ +unsigned long loopback_mask; + +/* emulated hosts use the MAC addr 52:55:IP:IP:IP:IP */ +static const uint8_t special_ethaddr[ETH_ALEN] = { + 0x52, 0x55, 0x00, 0x00, 0x00, 0x00 +}; + +unsigned curtime; + +static struct in_addr dns_addr; +#ifndef _WIN32 +static struct in6_addr dns6_addr; +#endif +static unsigned dns_addr_time; +#ifndef _WIN32 +static unsigned dns6_addr_time; +#endif + +#define TIMEOUT_FAST 2 /* milliseconds */ +#define TIMEOUT_SLOW 499 /* milliseconds */ +/* for the aging of certain requests like DNS */ +#define TIMEOUT_DEFAULT 1000 /* milliseconds */ + +#ifdef _WIN32 + +int get_dns_addr(struct in_addr *pdns_addr) +{ + FIXED_INFO *FixedInfo=NULL; + ULONG BufLen; + DWORD ret; + IP_ADDR_STRING *pIPAddr; + struct in_addr tmp_addr; + + if (dns_addr.s_addr != 0 && (curtime - dns_addr_time) < TIMEOUT_DEFAULT) { + *pdns_addr = dns_addr; + return 0; + } + + FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof(FIXED_INFO)); + BufLen = sizeof(FIXED_INFO); + + if (ERROR_BUFFER_OVERFLOW == GetNetworkParams(FixedInfo, &BufLen)) { + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + FixedInfo = GlobalAlloc(GPTR, BufLen); + } + + if ((ret = GetNetworkParams(FixedInfo, &BufLen)) != ERROR_SUCCESS) { + printf("GetNetworkParams failed. ret = %08x\n", (unsigned)ret ); + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return -1; + } + + pIPAddr = &(FixedInfo->DnsServerList); + inet_aton(pIPAddr->IpAddress.String, &tmp_addr); + *pdns_addr = tmp_addr; + dns_addr = tmp_addr; + dns_addr_time = curtime; + if (FixedInfo) { + GlobalFree(FixedInfo); + FixedInfo = NULL; + } + return 0; +} + +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) +{ + return -1; +} + +static void winsock_cleanup(void) +{ + WSACleanup(); +} + +#else + +static int get_dns_addr_cached(void *pdns_addr, void *cached_addr, + socklen_t addrlen, + struct stat *cached_stat, unsigned *cached_time) +{ + struct stat old_stat; + if (curtime - *cached_time < TIMEOUT_DEFAULT) { + memcpy(pdns_addr, cached_addr, addrlen); + return 0; + } + old_stat = *cached_stat; + if (stat("/etc/resolv.conf", cached_stat) != 0) { + return -1; + } + if (cached_stat->st_dev == old_stat.st_dev + && cached_stat->st_ino == old_stat.st_ino + && cached_stat->st_size == old_stat.st_size + && cached_stat->st_mtime == old_stat.st_mtime) { + memcpy(pdns_addr, cached_addr, addrlen); + return 0; + } + return 1; +} + +static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr, + socklen_t addrlen, uint32_t *scope_id, + unsigned *cached_time) +{ + char buff[512]; + char buff2[257]; + FILE *f; + int found = 0; + void *tmp_addr = alloca(addrlen); + unsigned if_index; + + f = fopen("/etc/resolv.conf", "r"); + if (!f) + return -1; + + DEBUG_MISC("IP address of your DNS(s):"); + while (fgets(buff, 512, f) != NULL) { + if (sscanf(buff, "nameserver%*[ \t]%256s", buff2) == 1) { + char *c = strchr(buff2, '%'); + if (c) { + if_index = if_nametoindex(c + 1); + *c = '\0'; + } else { + if_index = 0; + } + + if (!inet_pton(af, buff2, tmp_addr)) { + continue; + } + /* If it's the first one, set it to dns_addr */ + if (!found) { + memcpy(pdns_addr, tmp_addr, addrlen); + memcpy(cached_addr, tmp_addr, addrlen); + if (scope_id) { + *scope_id = if_index; + } + *cached_time = curtime; + } + + if (++found > 3) { + DEBUG_MISC(" (more)"); + break; + } else if (slirp_debug & DBG_MISC) { + char s[INET6_ADDRSTRLEN]; + const char *res = inet_ntop(af, tmp_addr, s, sizeof(s)); + if (!res) { + res = " (string conversion error)"; + } + DEBUG_MISC(" %s", res); + } + } + } + fclose(f); + if (!found) + return -1; + return 0; +} + +int get_dns_addr(struct in_addr *pdns_addr) +{ + static struct stat dns_addr_stat; + + if (dns_addr.s_addr != 0) { + int ret; + ret = get_dns_addr_cached(pdns_addr, &dns_addr, sizeof(dns_addr), + &dns_addr_stat, &dns_addr_time); + if (ret <= 0) { + return ret; + } + } + return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr, + sizeof(dns_addr), NULL, &dns_addr_time); +} + +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) +{ + static struct stat dns6_addr_stat; + + if (!in6_zero(&dns6_addr)) { + int ret; + ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), + &dns6_addr_stat, &dns6_addr_time); + if (ret <= 0) { + return ret; + } + } + return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr, + sizeof(dns6_addr), + scope_id, &dns6_addr_time); +} + +#endif + +static void slirp_init_once(void) +{ + static int initialized; + const char *debug; +#ifdef _WIN32 + WSADATA Data; +#endif + + if (initialized) { + return; + } + initialized = 1; + +#ifdef _WIN32 + WSAStartup(MAKEWORD(2,0), &Data); + atexit(winsock_cleanup); +#endif + + loopback_addr.s_addr = htonl(INADDR_LOOPBACK); + loopback_mask = htonl(IN_CLASSA_NET); + + debug = g_getenv("SLIRP_DEBUG"); + if (debug) { + const GDebugKey keys[] = { + { "call", DBG_CALL }, + { "misc", DBG_MISC }, + { "error", DBG_ERROR }, + { "tftp", DBG_TFTP }, + }; + slirp_debug = g_parse_debug_string(debug, keys, G_N_ELEMENTS(keys)); + } + + +} + +Slirp *slirp_init(int restricted, bool in_enabled, struct in_addr vnetwork, + struct in_addr vnetmask, struct in_addr vhost, + bool in6_enabled, + struct in6_addr vprefix_addr6, uint8_t vprefix_len, + struct in6_addr vhost6, const char *vhostname, + const char *tftp_server_name, + const char *tftp_path, const char *bootfile, + struct in_addr vdhcp_start, struct in_addr vnameserver, + struct in6_addr vnameserver6, const char **vdnssearch, + const char *vdomainname, + const SlirpCb *callbacks, + void *opaque) +{ + Slirp *slirp = g_malloc0(sizeof(Slirp)); + + slirp_init_once(); + + slirp->opaque = opaque; + slirp->cb = callbacks; + 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); + + 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) { + slirp_pstrcpy(slirp->client_hostname, sizeof(slirp->client_hostname), + vhostname); + } + slirp->tftp_prefix = g_strdup(tftp_path); + slirp->bootp_filename = g_strdup(bootfile); + slirp->vdomainname = g_strdup(vdomainname); + slirp->vdhcp_startaddr = vdhcp_start; + slirp->vnameserver_addr = vnameserver; + slirp->vnameserver_addr6 = vnameserver6; + slirp->tftp_server_name = g_strdup(tftp_server_name); + + if (vdnssearch) { + translate_dnssearch(slirp, vdnssearch); + } + + return slirp; +} + +void slirp_cleanup(Slirp *slirp) +{ + struct gfwd_list *e, *next; + + for (e = slirp->guestfwd_list; e; e = next) { + next = e->ex_next; + g_free(e->ex_exec); + g_free(e); + } + + 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); + g_free(slirp->vdomainname); + g_free(slirp); +} + +#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) +#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED) + +static void slirp_update_timeout(Slirp *slirp, uint32_t *timeout) +{ + uint32_t t; + + if (*timeout <= TIMEOUT_FAST) { + return; + } + + t = MIN(1000, *timeout); + + /* If we have tcp timeout with slirp, then we will fill @timeout with + * more precise value. + */ + if (slirp->time_fasttimo) { + *timeout = TIMEOUT_FAST; + return; + } + if (slirp->do_slowtimo) { + t = MIN(TIMEOUT_SLOW, t); + } + *timeout = t; +} + +void slirp_pollfds_fill(Slirp *slirp, uint32_t *timeout, + SlirpAddPollCb add_poll, void *opaque) +{ + struct socket *so, *so_next; + + /* + * First, TCP sockets + */ + + /* + * *_slowtimo needs calling if there are IP fragments + * in the fragment queue, or there are TCP connections active + */ + slirp->do_slowtimo = ((slirp->tcb.so_next != &slirp->tcb) || + (&slirp->ipq.ip_link != slirp->ipq.ip_link.next)); + + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so_next) { + int events = 0; + + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if we need a tcp_fasttimo + */ + if (slirp->time_fasttimo == 0 && + so->so_tcpcb->t_flags & TF_DELACK) { + slirp->time_fasttimo = curtime; /* Flag when want a fasttimo */ + } + + /* + * NOFDREF can include still connecting to local-host, + * newly socreated() sockets etc. Don't want to select these. + */ + if (so->so_state & SS_NOFDREF || so->s == -1) { + continue; + } + + /* + * Set for reading sockets which are accepting + */ + if (so->so_state & SS_FACCEPTCONN) { + so->pollfds_idx = add_poll(so->s, + SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); + continue; + } + + /* + * Set for writing sockets which are connecting + */ + if (so->so_state & SS_ISFCONNECTING) { + so->pollfds_idx = add_poll(so->s, + SLIRP_POLL_OUT | SLIRP_POLL_ERR, opaque); + continue; + } + + /* + * Set for writing if we are connected, can send more, and + * we have something to send + */ + if (CONN_CANFSEND(so) && so->so_rcv.sb_cc) { + events |= SLIRP_POLL_OUT | SLIRP_POLL_ERR; + } + + /* + * Set for reading (and urgent data) if we are connected, can + * receive more, and we have room for it XXX /2 ? + */ + if (CONN_CANFRCV(so) && + (so->so_snd.sb_cc < (so->so_snd.sb_datalen/2))) { + events |= SLIRP_POLL_IN | SLIRP_POLL_HUP | + SLIRP_POLL_ERR | SLIRP_POLL_PRI; + } + + if (events) { + so->pollfds_idx = add_poll(so->s, events, opaque); + } + } + + /* + * UDP sockets + */ + for (so = slirp->udb.so_next; so != &slirp->udb; so = so_next) { + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + udp_detach(so); + continue; + } else { + slirp->do_slowtimo = true; /* Let socket expire */ + } + } + + /* + * When UDP packets are received from over the + * link, they're sendto()'d straight away, so + * no need for setting for writing + * Limit the number of packets queued by this session + * to 4. Note that even though we try and limit this + * to 4 packets, the session could have more queued + * if the packets needed to be fragmented + * (XXX <= 4 ?) + */ + if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4) { + so->pollfds_idx = add_poll(so->s, + SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); + } + } + + /* + * ICMP sockets + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; so = so_next) { + so_next = so->so_next; + + so->pollfds_idx = -1; + + /* + * See if it's timed out + */ + if (so->so_expire) { + if (so->so_expire <= curtime) { + icmp_detach(so); + continue; + } else { + slirp->do_slowtimo = true; /* Let socket expire */ + } + } + + if (so->so_state & SS_ISFCONNECTED) { + so->pollfds_idx = add_poll(so->s, + SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR, opaque); + } + } + + slirp_update_timeout(slirp, timeout); +} + +void slirp_pollfds_poll(Slirp *slirp, int select_error, + SlirpGetREventsCb get_revents, void *opaque) +{ + struct socket *so, *so_next; + int ret; + + curtime = slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS; + + /* + * See if anything has timed out + */ + if (slirp->time_fasttimo && + ((curtime - slirp->time_fasttimo) >= TIMEOUT_FAST)) { + tcp_fasttimo(slirp); + slirp->time_fasttimo = 0; + } + if (slirp->do_slowtimo && + ((curtime - slirp->last_slowtimo) >= TIMEOUT_SLOW)) { + ip_slowtimo(slirp); + tcp_slowtimo(slirp); + slirp->last_slowtimo = curtime; + } + + /* + * Check sockets + */ + if (!select_error) { + /* + * Check TCP sockets + */ + for (so = slirp->tcb.so_next; so != &slirp->tcb; + so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = get_revents(so->pollfds_idx, opaque); + } + + if (so->so_state & SS_NOFDREF || so->s == -1) { + continue; + } + + /* + * Check for URG data + * This will soread as well, so no need to + * test for SLIRP_POLL_IN below if this succeeds + */ + if (revents & SLIRP_POLL_PRI) { + 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 + */ + else if (revents & + (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR)) { + /* + * Check for incoming connections + */ + if (so->so_state & SS_FACCEPTCONN) { + tcp_connect(so); + continue; + } /* else */ + ret = soread(so); + + /* Output it if we read something */ + 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; + } + } + + /* + * Check sockets for writing + */ + if (!(so->so_state & SS_NOFDREF) && + (revents & (SLIRP_POLL_OUT | SLIRP_POLL_ERR))) { + /* + * Check for non-blocking, still-connecting sockets + */ + if (so->so_state & SS_ISFCONNECTING) { + /* Connected */ + so->so_state &= ~SS_ISFCONNECTING; + + ret = send(so->s, (const void *) &ret, 0, 0); + if (ret < 0) { + /* XXXXX Must fix, zero bytes is a NOP */ + if (errno == EAGAIN || errno == EWOULDBLOCK || + errno == EINPROGRESS || errno == ENOTCONN) { + continue; + } + + /* else failed */ + so->so_state &= SS_PERSISTENT_MASK; + so->so_state |= SS_NOFDREF; + } + /* else so->so_state &= ~SS_ISFCONNECTING; */ + + /* + * Continue tcp_input + */ + tcp_input((struct mbuf *)NULL, sizeof(struct ip), so, + so->so_ffamily); + /* continue; */ + } else { + ret = sowrite(so); + if (ret > 0) { + /* Call tcp_output in case we need to send a window + * update to the guest, otherwise it will be stuck + * until it sends a window probe. */ + tcp_output(sototcpcb(so)); + } + } + } + } + + /* + * Now UDP sockets. + * Incoming packets are sent straight away, they're not buffered. + * Incoming UDP data isn't buffered either. + */ + for (so = slirp->udb.so_next; so != &slirp->udb; + so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = get_revents(so->pollfds_idx, opaque); + } + + if (so->s != -1 && + (revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) { + sorecvfrom(so); + } + } + + /* + * Check incoming ICMP relies. + */ + for (so = slirp->icmp.so_next; so != &slirp->icmp; + so = so_next) { + int revents; + + so_next = so->so_next; + + revents = 0; + if (so->pollfds_idx != -1) { + revents = get_revents(so->pollfds_idx, opaque); + } + + if (so->s != -1 && + (revents & (SLIRP_POLL_IN | SLIRP_POLL_HUP | SLIRP_POLL_ERR))) { + icmp_receive(so); + } + } + } + + if_start(slirp); +} + +static void arp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) +{ + struct slirp_arphdr *ah = (struct slirp_arphdr *)(pkt + ETH_HLEN); + uint8_t arp_reply[MAX(ETH_HLEN + sizeof(struct slirp_arphdr), 64)]; + struct ethhdr *reh = (struct ethhdr *)arp_reply; + struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_reply + ETH_HLEN); + int ar_op; + struct gfwd_list *ex_ptr; + + if (!slirp->in_enabled) { + return; + } + + ar_op = ntohs(ah->ar_op); + switch(ar_op) { + case ARPOP_REQUEST: + if (ah->ar_tip == ah->ar_sip) { + /* Gratuitous ARP */ + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + return; + } + + if ((ah->ar_tip & slirp->vnetwork_mask.s_addr) == + slirp->vnetwork_addr.s_addr) { + if (ah->ar_tip == slirp->vnameserver_addr.s_addr || + ah->ar_tip == slirp->vhost_addr.s_addr) + goto arp_ok; + /* TODO: IPv6 */ + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->ex_addr.s_addr == ah->ar_tip) + goto arp_ok; + } + return; + arp_ok: + memset(arp_reply, 0, sizeof(arp_reply)); + + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + + /* ARP request for alias/dns mac address */ + memcpy(reh->h_dest, pkt + ETH_ALEN, ETH_ALEN); + memcpy(reh->h_source, special_ethaddr, ETH_ALEN - 4); + memcpy(&reh->h_source[2], &ah->ar_tip, 4); + reh->h_proto = htons(ETH_P_ARP); + + rah->ar_hrd = htons(1); + rah->ar_pro = htons(ETH_P_IP); + rah->ar_hln = ETH_ALEN; + rah->ar_pln = 4; + rah->ar_op = htons(ARPOP_REPLY); + memcpy(rah->ar_sha, reh->h_source, ETH_ALEN); + rah->ar_sip = ah->ar_tip; + memcpy(rah->ar_tha, ah->ar_sha, ETH_ALEN); + rah->ar_tip = ah->ar_sip; + slirp_send_packet_all(slirp, arp_reply, sizeof(arp_reply)); + } + break; + case ARPOP_REPLY: + arp_table_add(slirp, ah->ar_sip, ah->ar_sha); + break; + default: + break; + } +} + +void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len) +{ + struct mbuf *m; + int proto; + + if (pkt_len < ETH_HLEN) + return; + + proto = (((uint16_t) pkt[12]) << 8) + pkt[13]; + switch(proto) { + case ETH_P_ARP: + 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 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 + TCPIPHDR_DELTA + 2; + memcpy(m->m_data + TCPIPHDR_DELTA + 2, pkt, pkt_len); + + m->m_data += TCPIPHDR_DELTA + 2 + ETH_HLEN; + m->m_len -= TCPIPHDR_DELTA + 2 + ETH_HLEN; + + if (proto == ETH_P_IP) { + ip_input(m); + } else if (proto == ETH_P_IPV6) { + ip6_input(m); + } + break; + + case ETH_P_NCSI: + ncsi_input(slirp, pkt, pkt_len); + break; + + default: + break; + } +} + +/* 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. + */ +static int if_encap4(Slirp *slirp, struct mbuf *ifm, struct ethhdr *eh, + uint8_t ethaddr[ETH_ALEN]) +{ + const struct ip *iph = (const struct ip *)ifm->m_data; + + if (iph->ip_dst.s_addr == 0) { + /* 0.0.0.0 can not be a destination address, something went wrong, + * avoid making it worse */ + return 1; + } + if (!arp_table_search(slirp, iph->ip_dst.s_addr, ethaddr)) { + uint8_t arp_req[ETH_HLEN + sizeof(struct slirp_arphdr)]; + struct ethhdr *reh = (struct ethhdr *)arp_req; + struct slirp_arphdr *rah = (struct slirp_arphdr *)(arp_req + ETH_HLEN); + + 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); + memcpy(&reh->h_source[2], &slirp->vhost_addr, 4); + reh->h_proto = htons(ETH_P_ARP); + rah->ar_hrd = htons(1); + rah->ar_pro = htons(ETH_P_IP); + rah->ar_hln = ETH_ALEN; + rah->ar_pln = 4; + rah->ar_op = htons(ARPOP_REQUEST); + + /* source hw addr */ + memcpy(rah->ar_sha, special_ethaddr, ETH_ALEN - 4); + memcpy(&rah->ar_sha[2], &slirp->vhost_addr, 4); + + /* source IP */ + rah->ar_sip = slirp->vhost_addr.s_addr; + + /* target hw addr (none) */ + memset(rah->ar_tha, 0, ETH_ALEN); + + /* target IP */ + rah->ar_tip = iph->ip_dst.s_addr; + slirp->client_ipaddr = iph->ip_dst; + slirp_send_packet_all(slirp, arp_req, sizeof(arp_req)); + ifm->resolution_requested = true; + + /* Expire request and drop outgoing packet after 1 second */ + ifm->expiration_date = + slirp->cb->clock_get_ns(slirp->opaque) + 1000000000ULL; + } + return 0; + } else { + 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); + + /* 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 = slirp->cb->clock_get_ns(slirp->opaque) + 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_ARG("src = %02x:%02x:%02x:%02x:%02x:%02x", + 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_ARG("dst = %02x:%02x:%02x:%02x:%02x:%02x", + 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_send_packet_all(slirp, buf, ifm->m_len + ETH_HLEN); + return 1; +} + +/* Drop host forwarding rule, return 0 if found. */ +/* TODO: IPv6 */ +int slirp_remove_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, + int host_port) +{ + struct socket *so; + struct socket *head = (is_udp ? &slirp->udb : &slirp->tcb); + struct sockaddr_in addr; + int port = htons(host_port); + socklen_t addr_len; + + for (so = head->so_next; so != head; so = so->so_next) { + addr_len = sizeof(addr); + if ((so->so_state & SS_HOSTFWD) && + getsockname(so->s, (struct sockaddr *)&addr, &addr_len) == 0 && + addr.sin_addr.s_addr == host_addr.s_addr && + addr.sin_port == port) { + so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); + closesocket(so->s); + sofree(so); + return 0; + } + } + + return -1; +} + +/* TODO: IPv6 */ +int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, + int host_port, struct in_addr guest_addr, int guest_port) +{ + if (!guest_addr.s_addr) { + guest_addr = slirp->vdhcp_startaddr; + } + if (is_udp) { + if (!udp_listen(slirp, host_addr.s_addr, htons(host_port), + guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) + return -1; + } else { + if (!tcp_listen(slirp, host_addr.s_addr, htons(host_port), + guest_addr.s_addr, htons(guest_port), SS_HOSTFWD)) + return -1; + } + return 0; +} + +/* TODO: IPv6 */ +static bool +check_guestfwd(Slirp *slirp, struct in_addr *guest_addr, int guest_port) +{ + struct gfwd_list *tmp_ptr; + + if (!guest_addr->s_addr) { + guest_addr->s_addr = slirp->vnetwork_addr.s_addr | + (htonl(0x0204) & ~slirp->vnetwork_mask.s_addr); + } + if ((guest_addr->s_addr & slirp->vnetwork_mask.s_addr) != + slirp->vnetwork_addr.s_addr || + guest_addr->s_addr == slirp->vhost_addr.s_addr || + guest_addr->s_addr == slirp->vnameserver_addr.s_addr) { + return false; + } + + /* check if the port is "bound" */ + for (tmp_ptr = slirp->guestfwd_list; tmp_ptr; tmp_ptr = tmp_ptr->ex_next) { + if (guest_port == tmp_ptr->ex_fport && + guest_addr->s_addr == tmp_ptr->ex_addr.s_addr) + return false; + } + + return true; +} + +int slirp_add_exec(Slirp *slirp, const char *cmdline, + struct in_addr *guest_addr, int guest_port) +{ + if (!check_guestfwd(slirp, guest_addr, guest_port)) { + return -1; + } + + add_exec(&slirp->guestfwd_list, cmdline, *guest_addr, htons(guest_port)); + return 0; +} + +int slirp_add_guestfwd(Slirp *slirp, SlirpWriteCb write_cb, void *opaque, + struct in_addr *guest_addr, int guest_port) +{ + if (!check_guestfwd(slirp, guest_addr, guest_port)) { + return -1; + } + + add_guestfwd(&slirp->guestfwd_list, write_cb, opaque, + *guest_addr, htons(guest_port)); + return 0; +} + +ssize_t slirp_send(struct socket *so, const void *buf, size_t len, int flags) +{ + if (so->s == -1 && so->guestfwd) { + /* XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks */ + so->guestfwd->write_cb(buf, len, so->guestfwd->opaque); + return len; + } + + if (so->s == -1) { + /* + * This should in theory not happen but it is hard to be + * sure because some code paths will end up with so->s == -1 + * on a failure but don't dispose of the struct socket. + * Check specifically, so we don't pass -1 to send(). + */ + errno = EBADF; + return -1; + } + + return send(so->s, buf, len, flags); +} + +struct socket * +slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port) +{ + struct socket *so; + + /* TODO: IPv6 */ + for (so = slirp->tcb.so_next; so != &slirp->tcb; so = so->so_next) { + if (so->so_faddr.s_addr == guest_addr.s_addr && + htons(so->so_fport) == guest_port) { + return so; + } + } + return NULL; +} + +size_t slirp_socket_can_recv(Slirp *slirp, struct in_addr guest_addr, + int guest_port) +{ + struct iovec iov[2]; + struct socket *so; + + so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); + + if (!so || so->so_state & SS_NOFDREF) { + return 0; + } + + if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen/2)) { + return 0; + } + + return sopreprbuf(so, iov, NULL); +} + +void slirp_socket_recv(Slirp *slirp, struct in_addr guest_addr, int guest_port, + const uint8_t *buf, int size) +{ + int ret; + struct socket *so = slirp_find_ctl_socket(slirp, guest_addr, guest_port); + + if (!so) + return; + + ret = soreadbuf(so, (const char *)buf, size); + + if (ret > 0) + tcp_output(sototcpcb(so)); +} + +void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len) +{ + ssize_t ret = slirp->cb->send_packet(buf, len, slirp->opaque); + + if (ret < 0) { + g_critical("Failed to send packet, ret: %ld", (long) ret); + } else if (ret < len) { + DEBUG_ERROR("send_packet() didn't send all data: %ld < %lu", + (long) ret, (unsigned long) len); + } +} diff --git a/slirp/slirp.h b/slirp/src/slirp.h similarity index 87% rename from slirp/slirp.h rename to slirp/src/slirp.h index 10b410898a..39580934f3 100644 --- a/slirp/slirp.h +++ b/slirp/src/slirp.h @@ -1,14 +1,20 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ #ifndef SLIRP_H #define SLIRP_H -#include "slirp_config.h" - #ifdef _WIN32 -typedef char *caddr_t; +/* as defined in sdkddkver.h */ +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0600 /* Vista */ +#endif +/* reduces the number of implicitly included headers */ +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif -# include # include +# include # include # include # include @@ -19,42 +25,15 @@ typedef char *caddr_t; # endif #endif -#ifdef HAVE_SYS_BITYPES_H -# include -#endif - #ifndef _WIN32 #include -#endif - -#ifndef _WIN32 #include #include -#endif - -#ifndef NO_UNIX_SOCKETS -#include -#endif -#ifdef HAVE_SYS_SIGNAL_H -# include -#endif -#ifndef _WIN32 #include +#include #endif -#if defined(HAVE_SYS_IOCTL_H) -# include -#endif - -#ifdef HAVE_SYS_SELECT_H -# include -#endif - -#ifdef HAVE_SYS_WAIT_H -# include -#endif - -#ifdef HAVE_SYS_FILIO_H +#ifdef __APPLE__ # include #endif @@ -64,16 +43,9 @@ typedef char *caddr_t; #define remque slirp_remque #define quehead slirp_quehead -#ifdef HAVE_SYS_STROPTS_H -#include -#endif - - #include "debug.h" - -#include "qemu/queue.h" -#include "qemu/sockets.h" -#include "net/eth.h" +#include "util.h" +#include "qtailq.h" #include "libslirp.h" #include "ip.h" @@ -118,7 +90,7 @@ struct slirp_arphdr { uint32_t ar_sip; /* sender IP address */ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */ uint32_t ar_tip; /* target IP address */ -} QEMU_PACKED; +} SLIRP_PACKED; #define ARP_TABLE_SIZE 16 @@ -135,7 +107,7 @@ bool arp_table_search(Slirp *slirp, uint32_t ip_addr, 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 @@ -151,8 +123,8 @@ bool ndp_table_search(Slirp *slirp, struct in6_addr ip_addr, struct Slirp { QTAILQ_ENTRY(Slirp) entry; - u_int time_fasttimo; - u_int last_slowtimo; + unsigned time_fasttimo; + unsigned last_slowtimo; bool do_slowtimo; bool in_enabled, in6_enabled; @@ -172,7 +144,7 @@ struct Slirp { char client_hostname[33]; int restricted; - struct ex_list *exec_list; + struct gfwd_list *guestfwd_list; /* mbuf states */ struct quehead m_freelist; @@ -212,24 +184,23 @@ struct Slirp { /* tftp states */ char *tftp_prefix; struct tftp_session tftp_sessions[TFTP_SESSIONS_MAX]; + char *tftp_server_name; ArpTable arp_table; NdpTable ndp_table; GRand *grand; - QEMUTimer *ra_timer; + void *ra_timer; + const SlirpCb *cb; void *opaque; }; -extern Slirp *slirp_instance; - -#ifndef NULL -#define NULL (void *)0 -#endif - void if_start(Slirp *); +int get_dns_addr(struct in_addr *pdns_addr); +int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id); + /* ncsi.c */ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); @@ -237,7 +208,9 @@ void ncsi_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); #include #endif -#define SO_OPTIONS DO_KEEPALIVE + +extern bool slirp_do_keepalive; + #define TCP_MAXIDLE (TCPTV_KEEPCNT * TCPTV_KEEPINTVL) /* dnssearch.c */ @@ -271,7 +244,7 @@ int ip6_output(struct socket *, struct mbuf *, int fast); /* tcp_input.c */ void tcp_input(register struct mbuf *, int, struct socket *, unsigned short af); -int tcp_mss(register struct tcpcb *, u_int); +int tcp_mss(register struct tcpcb *, unsigned); /* tcp_output.c */ int tcp_output(register struct tcpcb *); @@ -294,4 +267,9 @@ int tcp_emu(struct socket *, struct mbuf *); int tcp_ctl(struct socket *); struct tcpcb *tcp_drop(struct tcpcb *tp, int err); +struct socket * +slirp_find_ctl_socket(Slirp *slirp, struct in_addr guest_addr, int guest_port); + +void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len); + #endif diff --git a/slirp/socket.c b/slirp/src/socket.c similarity index 89% rename from slirp/socket.c rename to slirp/src/socket.c index 08fe98907d..bb752fdcae 100644 --- a/slirp/socket.c +++ b/slirp/src/socket.c @@ -1,12 +1,8 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" -#include "qemu-common.h" #include "slirp.h" #include "ip_icmp.h" #ifdef __sun__ @@ -46,17 +42,15 @@ struct socket *solookup(struct socket **last, struct socket *head, struct socket * socreate(Slirp *slirp) { - struct socket *so; + struct socket *so = g_new(struct socket, 1); - so = (struct socket *)malloc(sizeof(struct socket)); - if(so) { memset(so, 0, sizeof(struct socket)); so->so_state = SS_NOFDREF; so->s = -1; so->slirp = slirp; so->pollfds_idx = -1; - } - return(so); + + return so; } /* @@ -91,10 +85,6 @@ sofree(struct socket *so) soqfree(so, &slirp->if_fastq); soqfree(so, &slirp->if_batchq); - if (so->so_emu==EMU_RSH && so->extra) { - sofree(so->extra); - so->extra=NULL; - } if (so == slirp->tcp_last_so) { slirp->tcp_last_so = &slirp->tcb; } else if (so == slirp->udp_last_so) { @@ -110,7 +100,7 @@ sofree(struct socket *so) if (so->so_tcpcb) { free(so->so_tcpcb); } - free(so); + g_free(so); } size_t sopreprbuf(struct socket *so, struct iovec *iov, int *np) @@ -181,6 +171,7 @@ int soread(struct socket *so) { int n, nn; + size_t buf_len; struct sbuf *sb = &so->so_snd; struct iovec iov[2]; @@ -191,28 +182,32 @@ soread(struct socket *so) * No need to check if there's enough room to read. * soread wouldn't have been called if there weren't */ - sopreprbuf(so, iov, &n); + buf_len = sopreprbuf(so, iov, &n); + assert(buf_len != 0); -#ifdef HAVE_READV - nn = readv(so->s, (struct iovec *)iov, n); - DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); -#else - nn = qemu_recv(so->s, iov[0].iov_base, iov[0].iov_len,0); -#endif + nn = recv(so->s, iov[0].iov_base, iov[0].iov_len,0); if (nn <= 0) { if (nn < 0 && (errno == EINTR || errno == EAGAIN)) return 0; else { int err; - socklen_t slen = sizeof err; + socklen_t elen = sizeof err; + struct sockaddr_storage addr; + struct sockaddr *paddr = (struct sockaddr *) &addr; + socklen_t alen = sizeof addr; err = errno; if (nn == 0) { - getsockopt(so->s, SOL_SOCKET, SO_ERROR, - &err, &slen); + if (getpeername(so->s, paddr, &alen) < 0) { + err = errno; + } else { + getsockopt(so->s, SOL_SOCKET, SO_ERROR, + &err, &elen); + } } - DEBUG_MISC((dfd, " --- soread() disconnected, nn = %d, errno = %d-%s\n", nn, errno,strerror(errno))); + DEBUG_MISC(" --- soread() disconnected, nn = %d, errno = %d-%s", + nn, errno,strerror(errno)); sofcantrcvmore(so); if (err == ECONNRESET || err == ECONNREFUSED @@ -225,7 +220,6 @@ soread(struct socket *so) } } -#ifndef HAVE_READV /* * If there was no error, try and read the second time round * We read again if n = 2 (ie, there's another part of the buffer) @@ -237,13 +231,12 @@ soread(struct socket *so) */ if (n == 2 && nn == iov[0].iov_len) { int ret; - ret = qemu_recv(so->s, iov[1].iov_base, iov[1].iov_len,0); + ret = recv(so->s, iov[1].iov_base, iov[1].iov_len,0); if (ret > 0) nn += ret; } - DEBUG_MISC((dfd, " ... read nn = %d bytes\n", nn)); -#endif + DEBUG_MISC(" ... read nn = %d bytes", nn); /* Update fields */ sb->sb_cc += nn; @@ -266,6 +259,7 @@ int soreadbuf(struct socket *so, const char *buf, int size) * No need to check if there's enough room to read. * soread wouldn't have been called if there weren't */ + assert(size > 0); if (sopreprbuf(so, iov, &n) < size) goto err; @@ -291,7 +285,7 @@ err: sofcantrcvmore(so); tcp_sockclosed(sototcpcb(so)); - fprintf(stderr, "soreadbuf buffer to small"); + g_critical("soreadbuf buffer too small"); return -1; } @@ -376,7 +370,7 @@ sosendoob(struct socket *so) n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ #ifdef DEBUG if (n != len) { - DEBUG_ERROR((dfd, "Didn't send all data urgently XXXXX\n")); + DEBUG_ERROR("Didn't send all data urgently XXXXX"); } #endif } @@ -385,7 +379,7 @@ sosendoob(struct socket *so) return n; } so->so_urgc -= n; - DEBUG_MISC((dfd, " ---2 sent %d bytes urgent data, %d urgent bytes left\n", n, so->so_urgc)); + DEBUG_MISC(" ---2 sent %d bytes urgent data, %d urgent bytes left", n, so->so_urgc); sb->sb_cc -= n; sb->sb_rptr += n; @@ -451,13 +445,7 @@ sowrite(struct socket *so) } /* Check if there's urgent data to send, and if so, send it */ -#ifdef HAVE_READV - nn = writev(so->s, (const struct iovec *)iov, n); - - DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); -#else nn = slirp_send(so, iov[0].iov_base, iov[0].iov_len,0); -#endif /* This should never happen, but people tell me it does *shrug* */ if (nn < 0 && (errno == EAGAIN || errno == EINTR)) return 0; @@ -466,15 +454,13 @@ sowrite(struct socket *so) goto err_disconnected; } -#ifndef HAVE_READV if (n == 2 && nn == iov[0].iov_len) { int ret; ret = slirp_send(so, iov[1].iov_base, iov[1].iov_len,0); if (ret > 0) nn += ret; } - DEBUG_MISC((dfd, " ... wrote nn = %d bytes\n", nn)); -#endif + DEBUG_MISC(" ... wrote nn = %d bytes", nn); /* Update sbuf */ sb->sb_cc -= nn; @@ -492,8 +478,8 @@ sowrite(struct socket *so) return nn; err_disconnected: - DEBUG_MISC((dfd, " --- sowrite disconnected, so->so_state = %x, errno = %d\n", - so->so_state, errno)); + DEBUG_MISC(" --- sowrite disconnected, so->so_state = %x, errno = %d", + so->so_state, errno); sofcantsendmore(so); tcp_sockclosed(sototcpcb(so)); return -1; @@ -521,13 +507,13 @@ sorecvfrom(struct socket *so) /* XXX Check if reply is "correct"? */ if(len == -1 || len == 0) { - u_char code=ICMP_UNREACH_PORT; + uint8_t code=ICMP_UNREACH_PORT; if(errno == EHOSTUNREACH) code=ICMP_UNREACH_HOST; else if(errno == ENETUNREACH) code=ICMP_UNREACH_NET; - DEBUG_MISC((dfd," udp icmp rx errno = %d-%s\n", - errno,strerror(errno))); + DEBUG_MISC(" udp icmp rx errno = %d-%s", + errno,strerror(errno)); icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); } else { icmp_reflect(so->so_m); @@ -544,6 +530,15 @@ sorecvfrom(struct socket *so) int n; #endif + if (ioctlsocket(so->s, FIONREAD, &n) != 0) { + DEBUG_MISC(" ioctlsocket errno = %d-%s\n", + errno,strerror(errno)); + return; + } + if (n == 0) { + return; + } + m = m_get(so->slirp); if (!m) { return; @@ -567,7 +562,6 @@ sorecvfrom(struct socket *so) */ len = M_FREEROOM(m); /* if (so->so_fport != htons(53)) { */ - ioctlsocket(so->s, FIONREAD, &n); if (n > len) { n = (m->m_data - m->m_dat) + m->m_len + n + 1; @@ -578,8 +572,8 @@ sorecvfrom(struct socket *so) m->m_len = recvfrom(so->s, m->m_data, len, 0, (struct sockaddr *)&addr, &addrlen); - DEBUG_MISC((dfd, " did recvfrom %d, errno = %d-%s\n", - m->m_len, errno,strerror(errno))); + DEBUG_MISC(" did recvfrom %d, errno = %d-%s", + m->m_len, errno,strerror(errno)); if(m->m_len<0) { /* Report error as ICMP */ switch (so->so_lfamily) { @@ -593,7 +587,7 @@ sorecvfrom(struct socket *so) code = ICMP_UNREACH_NET; } - DEBUG_MISC((dfd, " rx error, tx icmp ICMP_UNREACH:%i\n", code)); + DEBUG_MISC(" rx error, tx icmp ICMP_UNREACH:%i", code); icmp_send_error(so->so_m, ICMP_UNREACH, code, 0, strerror(errno)); break; case AF_INET6: @@ -605,7 +599,7 @@ sorecvfrom(struct socket *so) code = ICMP6_UNREACH_NO_ROUTE; } - DEBUG_MISC((dfd, " rx error, tx icmp6 ICMP_UNREACH:%i\n", code)); + DEBUG_MISC(" rx error, tx icmp6 ICMP_UNREACH:%i", code); icmp6_send_error(so->so_m, ICMP6_UNREACH, code); break; default: @@ -691,9 +685,10 @@ sosendto(struct socket *so, struct mbuf *m) * Listen for incoming TCP connections */ struct socket * -tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, - u_int lport, int flags) +tcp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, uint32_t laddr, + unsigned lport, int flags) { + /* TODO: IPv6 */ struct sockaddr_in addr; struct socket *so; int s, opt = 1; @@ -708,14 +703,11 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, DEBUG_ARG("flags = %x", flags); so = socreate(slirp); - if (!so) { - return NULL; - } /* Don't tcp_attach... we don't need so_snd nor so_rcv */ if ((so->so_tcpcb = tcp_newtcpcb(so)) == NULL) { - free(so); - return NULL; + g_free(so); + return NULL; } insque(so, &slirp->tcb); @@ -735,8 +727,8 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, addr.sin_addr.s_addr = haddr; addr.sin_port = hport; - if (((s = qemu_socket(AF_INET,SOCK_STREAM,0)) < 0) || - (socket_set_fast_reuse(s) < 0) || + if (((s = slirp_socket(AF_INET,SOCK_STREAM,0)) < 0) || + (slirp_socket_set_fast_reuse(s) < 0) || (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0) || (listen(s,1) < 0)) { int tmperrno = errno; /* Don't clobber the real reason we failed */ @@ -753,9 +745,9 @@ tcp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, #endif return NULL; } - qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); opt = 1; - qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(int)); getsockname(s,(struct sockaddr *)&addr,&addrlen); so->so_ffamily = AF_INET; @@ -856,9 +848,8 @@ void sotranslate_out(struct socket *so, struct sockaddr_storage *addr) } } - DEBUG_MISC((dfd, " addr.sin_port=%d, " - "addr.sin_addr.s_addr=%.16s\n", - ntohs(sin->sin_port), inet_ntoa(sin->sin_addr))); + DEBUG_MISC(" addr.sin_port=%d, addr.sin_addr.s_addr=%.16s", + ntohs(sin->sin_port), inet_ntoa(sin->sin_addr)); break; case AF_INET6: @@ -945,3 +936,10 @@ void sotranslate_accept(struct socket *so) break; } } + +void sodrop(struct socket *s, int num) +{ + if (sbdrop(&s->so_snd, num)) { + s->slirp->cb->notify(s->slirp->opaque); + } +} diff --git a/slirp/socket.h b/slirp/src/socket.h similarity index 94% rename from slirp/socket.h rename to slirp/src/socket.h index 2f224bc34f..25403898cd 100644 --- a/slirp/socket.h +++ b/slirp/src/socket.h @@ -1,13 +1,13 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ #ifndef SLIRP_SOCKET_H #define SLIRP_SOCKET_H +#include "misc.h" + #define SO_EXPIRE 240000 #define SO_EXPIREFAST 10000 @@ -25,6 +25,7 @@ struct socket { struct socket *so_next,*so_prev; /* For a linked list of sockets */ int s; /* The actual socket */ + struct gfwd_list *guestfwd; int pollfds_idx; /* GPollFD GArray index */ @@ -58,7 +59,7 @@ struct socket { int32_t so_state; /* internal state flags SS_*, below */ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */ - u_int so_expire; /* When the socket will expire */ + unsigned so_expire; /* When the socket will expire */ int so_queued; /* Number of packets queued from this socket */ int so_nqueued; /* Number of packets queued in a row @@ -67,7 +68,6 @@ struct socket { struct sbuf so_rcv; /* Receive buffer */ struct sbuf so_snd; /* Send buffer */ - void * extra; /* Extra pointer */ }; @@ -142,7 +142,7 @@ int sosendoob(struct socket *); int sowrite(struct socket *); void sorecvfrom(struct socket *); int sosendto(struct socket *, struct mbuf *); -struct socket * tcp_listen(Slirp *, uint32_t, u_int, uint32_t, u_int, +struct socket * tcp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int); void soisfconnecting(register struct socket *); void soisfconnected(register struct socket *); @@ -154,6 +154,7 @@ 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 *); +void sodrop(struct socket *, int num); #endif /* SLIRP_SOCKET_H */ diff --git a/slirp/src/state.c b/slirp/src/state.c new file mode 100644 index 0000000000..09cea3590e --- /dev/null +++ b/slirp/src/state.c @@ -0,0 +1,388 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libslirp + * + * Copyright (c) 2004-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 "slirp.h" +#include "vmstate.h" +#include "stream.h" + +static int slirp_tcp_post_load(void *opaque, int version) +{ + tcp_template((struct tcpcb *)opaque); + + return 0; +} + +static const VMStateDescription vmstate_slirp_tcp = { + .name = "slirp-tcp", + .version_id = 0, + .post_load = slirp_tcp_post_load, + .fields = (VMStateField[]) { + VMSTATE_INT16(t_state, struct tcpcb), + VMSTATE_INT16_ARRAY(t_timer, struct tcpcb, TCPT_NTIMERS), + VMSTATE_INT16(t_rxtshift, struct tcpcb), + VMSTATE_INT16(t_rxtcur, struct tcpcb), + VMSTATE_INT16(t_dupacks, struct tcpcb), + VMSTATE_UINT16(t_maxseg, struct tcpcb), + VMSTATE_UINT8(t_force, struct tcpcb), + VMSTATE_UINT16(t_flags, struct tcpcb), + VMSTATE_UINT32(snd_una, struct tcpcb), + VMSTATE_UINT32(snd_nxt, struct tcpcb), + VMSTATE_UINT32(snd_up, struct tcpcb), + VMSTATE_UINT32(snd_wl1, struct tcpcb), + VMSTATE_UINT32(snd_wl2, struct tcpcb), + VMSTATE_UINT32(iss, struct tcpcb), + VMSTATE_UINT32(snd_wnd, struct tcpcb), + VMSTATE_UINT32(rcv_wnd, struct tcpcb), + VMSTATE_UINT32(rcv_nxt, struct tcpcb), + VMSTATE_UINT32(rcv_up, struct tcpcb), + VMSTATE_UINT32(irs, struct tcpcb), + VMSTATE_UINT32(rcv_adv, struct tcpcb), + VMSTATE_UINT32(snd_max, struct tcpcb), + VMSTATE_UINT32(snd_cwnd, struct tcpcb), + VMSTATE_UINT32(snd_ssthresh, struct tcpcb), + VMSTATE_INT16(t_idle, struct tcpcb), + VMSTATE_INT16(t_rtt, struct tcpcb), + VMSTATE_UINT32(t_rtseq, struct tcpcb), + VMSTATE_INT16(t_srtt, struct tcpcb), + VMSTATE_INT16(t_rttvar, struct tcpcb), + VMSTATE_UINT16(t_rttmin, struct tcpcb), + VMSTATE_UINT32(max_sndwnd, struct tcpcb), + VMSTATE_UINT8(t_oobflags, struct tcpcb), + VMSTATE_UINT8(t_iobc, struct tcpcb), + VMSTATE_INT16(t_softerror, struct tcpcb), + VMSTATE_UINT8(snd_scale, struct tcpcb), + VMSTATE_UINT8(rcv_scale, struct tcpcb), + VMSTATE_UINT8(request_r_scale, struct tcpcb), + VMSTATE_UINT8(requested_s_scale, struct tcpcb), + VMSTATE_UINT32(ts_recent, struct tcpcb), + VMSTATE_UINT32(ts_recent_age, struct tcpcb), + VMSTATE_UINT32(last_ack_sent, struct tcpcb), + VMSTATE_END_OF_LIST() + } +}; + +/* The sbuf has a pair of pointers that are migrated as offsets; + * we calculate the offsets and restore the pointers using + * pre_save/post_load on a tmp structure. + */ +struct sbuf_tmp { + struct sbuf *parent; + uint32_t roff, woff; +}; + +static int sbuf_tmp_pre_save(void *opaque) +{ + struct sbuf_tmp *tmp = opaque; + tmp->woff = tmp->parent->sb_wptr - tmp->parent->sb_data; + tmp->roff = tmp->parent->sb_rptr - tmp->parent->sb_data; + + return 0; +} + +static int sbuf_tmp_post_load(void *opaque, int version) +{ + struct sbuf_tmp *tmp = opaque; + uint32_t requested_len = tmp->parent->sb_datalen; + + /* Allocate the buffer space used by the field after the tmp */ + sbreserve(tmp->parent, tmp->parent->sb_datalen); + + if (tmp->parent->sb_datalen != requested_len) { + return -ENOMEM; + } + if (tmp->woff >= requested_len || + tmp->roff >= requested_len) { + g_critical("invalid sbuf offsets r/w=%u/%u len=%u", + tmp->roff, tmp->woff, requested_len); + return -EINVAL; + } + + tmp->parent->sb_wptr = tmp->parent->sb_data + tmp->woff; + tmp->parent->sb_rptr = tmp->parent->sb_data + tmp->roff; + + return 0; +} + + +static const VMStateDescription vmstate_slirp_sbuf_tmp = { + .name = "slirp-sbuf-tmp", + .post_load = sbuf_tmp_post_load, + .pre_save = sbuf_tmp_pre_save, + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(woff, struct sbuf_tmp), + VMSTATE_UINT32(roff, struct sbuf_tmp), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_sbuf = { + .name = "slirp-sbuf", + .version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_UINT32(sb_cc, struct sbuf), + VMSTATE_UINT32(sb_datalen, struct sbuf), + VMSTATE_WITH_TMP(struct sbuf, struct sbuf_tmp, vmstate_slirp_sbuf_tmp), + VMSTATE_VBUFFER_UINT32(sb_data, struct sbuf, 0, NULL, sb_datalen), + VMSTATE_END_OF_LIST() + } +}; + +static bool slirp_older_than_v4(void *opaque, int version_id) +{ + return version_id < 4; +} + +static bool slirp_family_inet(void *opaque, int version_id) +{ + union slirp_sockaddr *ssa = (union slirp_sockaddr *)opaque; + return ssa->ss.ss_family == AF_INET; +} + +static int slirp_socket_pre_load(void *opaque) +{ + struct socket *so = opaque; + if (tcp_attach(so) < 0) { + return -ENOMEM; + } + /* Older versions don't load these fields */ + so->so_ffamily = AF_INET; + so->so_lfamily = AF_INET; + return 0; +} + +#ifndef _WIN32 +#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_UINT32_TEST(f, s, t) +#else +/* Win uses u_long rather than uint32_t - but it's still 32bits long */ +#define VMSTATE_SIN4_ADDR(f, s, t) VMSTATE_SINGLE_TEST(f, s, t, 0, \ + slirp_vmstate_info_uint32, u_long) +#endif + +/* The OS provided ss_family field isn't that portable; it's size + * and type varies (16/8 bit, signed, unsigned) + * and the values it contains aren't fully portable. + */ +typedef struct SS_FamilyTmpStruct { + union slirp_sockaddr *parent; + uint16_t portable_family; +} SS_FamilyTmpStruct; + +#define SS_FAMILY_MIG_IPV4 2 /* Linux, BSD, Win... */ +#define SS_FAMILY_MIG_IPV6 10 /* Linux */ +#define SS_FAMILY_MIG_OTHER 0xffff + +static int ss_family_pre_save(void *opaque) +{ + SS_FamilyTmpStruct *tss = opaque; + + tss->portable_family = SS_FAMILY_MIG_OTHER; + + if (tss->parent->ss.ss_family == AF_INET) { + tss->portable_family = SS_FAMILY_MIG_IPV4; + } else if (tss->parent->ss.ss_family == AF_INET6) { + tss->portable_family = SS_FAMILY_MIG_IPV6; + } + + return 0; +} + +static int ss_family_post_load(void *opaque, int version_id) +{ + SS_FamilyTmpStruct *tss = opaque; + + switch (tss->portable_family) { + case SS_FAMILY_MIG_IPV4: + tss->parent->ss.ss_family = AF_INET; + break; + case SS_FAMILY_MIG_IPV6: + case 23: /* compatibility: AF_INET6 from mingw */ + case 28: /* compatibility: AF_INET6 from FreeBSD sys/socket.h */ + tss->parent->ss.ss_family = AF_INET6; + break; + default: + g_critical("invalid ss_family type %x", tss->portable_family); + return -EINVAL; + } + + return 0; +} + +static const VMStateDescription vmstate_slirp_ss_family = { + .name = "slirp-socket-addr/ss_family", + .pre_save = ss_family_pre_save, + .post_load = ss_family_post_load, + .fields = (VMStateField[]) { + VMSTATE_UINT16(portable_family, SS_FamilyTmpStruct), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_socket_addr = { + .name = "slirp-socket-addr", + .version_id = 4, + .fields = (VMStateField[]) { + VMSTATE_WITH_TMP(union slirp_sockaddr, SS_FamilyTmpStruct, + vmstate_slirp_ss_family), + VMSTATE_SIN4_ADDR(sin.sin_addr.s_addr, union slirp_sockaddr, + slirp_family_inet), + VMSTATE_UINT16_TEST(sin.sin_port, union slirp_sockaddr, + slirp_family_inet), + +#if 0 + /* Untested: Needs checking by someone with IPv6 test */ + VMSTATE_BUFFER_TEST(sin6.sin6_addr, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT16_TEST(sin6.sin6_port, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT32_TEST(sin6.sin6_flowinfo, union slirp_sockaddr, + slirp_family_inet6), + VMSTATE_UINT32_TEST(sin6.sin6_scope_id, union slirp_sockaddr, + slirp_family_inet6), +#endif + + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_socket = { + .name = "slirp-socket", + .version_id = 4, + .pre_load = slirp_socket_pre_load, + .fields = (VMStateField[]) { + VMSTATE_UINT32(so_urgc, struct socket), + /* Pre-v4 versions */ + VMSTATE_SIN4_ADDR(so_faddr.s_addr, struct socket, + slirp_older_than_v4), + VMSTATE_SIN4_ADDR(so_laddr.s_addr, struct socket, + slirp_older_than_v4), + VMSTATE_UINT16_TEST(so_fport, struct socket, slirp_older_than_v4), + VMSTATE_UINT16_TEST(so_lport, struct socket, slirp_older_than_v4), + /* v4 and newer */ + VMSTATE_STRUCT(fhost, struct socket, 4, vmstate_slirp_socket_addr, + union slirp_sockaddr), + VMSTATE_STRUCT(lhost, struct socket, 4, vmstate_slirp_socket_addr, + union slirp_sockaddr), + + VMSTATE_UINT8(so_iptos, struct socket), + VMSTATE_UINT8(so_emu, struct socket), + VMSTATE_UINT8(so_type, struct socket), + VMSTATE_INT32(so_state, struct socket), + VMSTATE_STRUCT(so_rcv, struct socket, 0, vmstate_slirp_sbuf, + struct sbuf), + VMSTATE_STRUCT(so_snd, struct socket, 0, vmstate_slirp_sbuf, + struct sbuf), + VMSTATE_STRUCT_POINTER(so_tcpcb, struct socket, vmstate_slirp_tcp, + struct tcpcb), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp_bootp_client = { + .name = "slirp_bootpclient", + .fields = (VMStateField[]) { + VMSTATE_UINT16(allocated, BOOTPClient), + VMSTATE_BUFFER(macaddr, BOOTPClient), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_slirp = { + .name = "slirp", + .version_id = 4, + .fields = (VMStateField[]) { + VMSTATE_UINT16_V(ip_id, Slirp, 2), + VMSTATE_STRUCT_ARRAY(bootp_clients, Slirp, NB_BOOTP_CLIENTS, 3, + vmstate_slirp_bootp_client, BOOTPClient), + VMSTATE_END_OF_LIST() + } +}; + +void slirp_state_save(Slirp *slirp, SlirpWriteCb write_cb, void *opaque) +{ + struct gfwd_list *ex_ptr; + SlirpOStream f = { + .write_cb = write_cb, + .opaque = opaque, + }; + + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) + if (ex_ptr->write_cb) { + struct socket *so; + so = slirp_find_ctl_socket(slirp, ex_ptr->ex_addr, + ntohs(ex_ptr->ex_fport)); + if (!so) { + continue; + } + + slirp_ostream_write_u8(&f, 42); + slirp_vmstate_save_state(&f, &vmstate_slirp_socket, so); + } + slirp_ostream_write_u8(&f, 0); + + slirp_vmstate_save_state(&f, &vmstate_slirp, slirp); +} + + +int slirp_state_load(Slirp *slirp, int version_id, + SlirpReadCb read_cb, void *opaque) +{ + struct gfwd_list *ex_ptr; + SlirpIStream f = { + .read_cb = read_cb, + .opaque = opaque, + }; + + while (slirp_istream_read_u8(&f)) { + int ret; + struct socket *so = socreate(slirp); + + ret = slirp_vmstate_load_state(&f, &vmstate_slirp_socket, so, version_id); + if (ret < 0) { + return ret; + } + + if ((so->so_faddr.s_addr & slirp->vnetwork_mask.s_addr) != + slirp->vnetwork_addr.s_addr) { + return -EINVAL; + } + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + if (ex_ptr->write_cb && + so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr && + so->so_fport == ex_ptr->ex_fport) { + break; + } + } + if (!ex_ptr) { + return -EINVAL; + } + } + + return slirp_vmstate_load_state(&f, &vmstate_slirp, slirp, version_id); +} + +int slirp_state_version(void) +{ + return 4; +} diff --git a/slirp/src/stream.c b/slirp/src/stream.c new file mode 100644 index 0000000000..9c1764c0b7 --- /dev/null +++ b/slirp/src/stream.c @@ -0,0 +1,120 @@ +/* SPDX-License-Identifier: MIT */ +/* + * libslirp io streams + * + * Copyright (c) 2018 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 "stream.h" +#include + +bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size) +{ + return f->read_cb(buf, size, f->opaque) == size; +} + +bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size) +{ + return f->write_cb(buf, size, f->opaque) == size; +} + +uint8_t slirp_istream_read_u8(SlirpIStream *f) +{ + uint8_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return b; + } + + return 0; +} + +bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b) +{ + return slirp_ostream_write(f, &b, sizeof(b)); +} + +uint16_t slirp_istream_read_u16(SlirpIStream *f) +{ + uint16_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GUINT16_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b) +{ + b = GUINT16_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} + +uint32_t slirp_istream_read_u32(SlirpIStream *f) +{ + uint32_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GUINT32_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b) +{ + b = GUINT32_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} + +int16_t slirp_istream_read_i16(SlirpIStream *f) +{ + int16_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GINT16_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b) +{ + b = GINT16_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} + +int32_t slirp_istream_read_i32(SlirpIStream *f) +{ + int32_t b; + + if (slirp_istream_read(f, &b, sizeof(b))) { + return GINT32_FROM_BE(b); + } + + return 0; +} + +bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b) +{ + b = GINT32_TO_BE(b); + return slirp_ostream_write(f, &b, sizeof(b)); +} diff --git a/slirp/src/stream.h b/slirp/src/stream.h new file mode 100644 index 0000000000..08bb5b6610 --- /dev/null +++ b/slirp/src/stream.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +#ifndef STREAM_H_ +#define STREAM_H_ + +#include "libslirp.h" + +typedef struct SlirpIStream { + SlirpReadCb read_cb; + void *opaque; +} SlirpIStream; + +typedef struct SlirpOStream { + SlirpWriteCb write_cb; + void *opaque; +} SlirpOStream; + +bool slirp_istream_read(SlirpIStream *f, void *buf, size_t size); +bool slirp_ostream_write(SlirpOStream *f, const void *buf, size_t size); + +uint8_t slirp_istream_read_u8(SlirpIStream *f); +bool slirp_ostream_write_u8(SlirpOStream *f, uint8_t b); + +uint16_t slirp_istream_read_u16(SlirpIStream *f); +bool slirp_ostream_write_u16(SlirpOStream *f, uint16_t b); + +uint32_t slirp_istream_read_u32(SlirpIStream *f); +bool slirp_ostream_write_u32(SlirpOStream *f, uint32_t b); + +int16_t slirp_istream_read_i16(SlirpIStream *f); +bool slirp_ostream_write_i16(SlirpOStream *f, int16_t b); + +int32_t slirp_istream_read_i32(SlirpIStream *f); +bool slirp_ostream_write_i32(SlirpOStream *f, int32_t b); + +#endif /* STREAM_H_ */ diff --git a/slirp/tcp.h b/slirp/src/tcp.h similarity index 98% rename from slirp/tcp.h rename to slirp/src/tcp.h index 174d3d960c..79d3251bb5 100644 --- a/slirp/tcp.h +++ b/slirp/src/tcp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. @@ -33,6 +34,8 @@ #ifndef TCP_H #define TCP_H +#include + typedef uint32_t tcp_seq; #define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */ @@ -51,7 +54,7 @@ struct tcphdr { uint16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ -#ifdef HOST_WORDS_BIGENDIAN +#if G_BYTE_ORDER == G_BIG_ENDIAN uint8_t th_off:4, /* data offset */ th_x2:4; /* (unused) */ #else diff --git a/slirp/tcp_input.c b/slirp/src/tcp_input.c similarity index 93% rename from slirp/tcp_input.c rename to slirp/src/tcp_input.c index 07bcbdb2dd..50a1145ec9 100644 --- a/slirp/tcp_input.c +++ b/slirp/src/tcp_input.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994 * The Regents of the University of California. All rights reserved. @@ -33,12 +34,8 @@ /* * Changes and additions relating to SLiRP * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" #include "ip_icmp.h" @@ -60,27 +57,6 @@ * Set DELACK for segments received in order, but ack immediately * when segments are out of order (so fast retransmit can work). */ -#ifdef TCP_ACK_HACK -#define TCP_REASS(tp, ti, m, so, flags) {\ - if ((ti)->ti_seq == (tp)->rcv_nxt && \ - tcpfrag_list_empty(tp) && \ - (tp)->t_state == TCPS_ESTABLISHED) {\ - if (ti->ti_flags & TH_PUSH) \ - tp->t_flags |= TF_ACKNOW; \ - else \ - tp->t_flags |= TF_DELACK; \ - (tp)->rcv_nxt += (ti)->ti_len; \ - flags = (ti)->ti_flags & TH_FIN; \ - if (so->so_emu) { \ - if (tcp_emu((so),(m))) sbappend((so), (m)); \ - } else \ - sbappend((so), (m)); \ - } else {\ - (flags) = tcp_reass((tp), (ti), (m)); \ - tp->t_flags |= TF_ACKNOW; \ - } \ -} -#else #define TCP_REASS(tp, ti, m, so, flags) { \ if ((ti)->ti_seq == (tp)->rcv_nxt && \ tcpfrag_list_empty(tp) && \ @@ -97,8 +73,8 @@ tp->t_flags |= TF_ACKNOW; \ } \ } -#endif -static void tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, + +static void tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt, struct tcpiphdr *ti); static void tcp_xmit_timer(register struct tcpcb *tp, int rtt); @@ -219,7 +195,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af) struct ip save_ip, *ip; struct ip6 save_ip6, *ip6; register struct tcpiphdr *ti; - caddr_t optp = NULL; + char *optp = NULL; int optlen = 0; int len, tlen, off; register struct tcpcb *tp = NULL; @@ -227,17 +203,17 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af) struct socket *so = NULL; int todrop, acked, ourfinisacked, needoutput = 0; int iss = 0; - u_long tiwin; + uint32_t tiwin; int ret; struct sockaddr_storage lhost, fhost; struct sockaddr_in *lhost4, *fhost4; struct sockaddr_in6 *lhost6, *fhost6; - struct ex_list *ex_ptr; + struct gfwd_list *ex_ptr; Slirp *slirp; DEBUG_CALL("tcp_input"); - DEBUG_ARGS((dfd, " m = %p iphlen = %2d inso = %p\n", - m, iphlen, inso)); + DEBUG_ARG("m = %p iphlen = %2d inso = %p", + m, iphlen, inso); /* * If called with m == 0, then we're continuing the connect @@ -349,7 +325,7 @@ tcp_input(struct mbuf *m, int iphlen, struct socket *inso, unsigned short af) ti->ti_len = tlen; if (off > sizeof (struct tcphdr)) { optlen = off - sizeof (struct tcphdr); - optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr); + optp = mtod(m, char *) + sizeof (struct tcpiphdr); } tiflags = ti->ti_flags; @@ -410,12 +386,13 @@ findso: * as if it was LISTENING, and continue... */ if (so == NULL) { + /* TODO: IPv6 */ if (slirp->restricted) { /* Any hostfwds will have an existing socket, so we only get here * for non-hostfwd connections. These should be dropped, unless it * happens to be a guestfwd. */ - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { if (ex_ptr->ex_fport == ti->ti_dport && ti->ti_dst.s_addr == ex_ptr->ex_addr.s_addr) { break; @@ -429,11 +406,10 @@ findso: if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN) goto dropwithreset; - if ((so = socreate(slirp)) == NULL) - goto dropwithreset; + so = socreate(slirp); if (tcp_attach(so) < 0) { - free(so); /* Not sofree (if it failed, it's not insqued) */ - goto dropwithreset; + g_free(so); /* Not sofree (if it failed, it's not insqued) */ + goto dropwithreset; } sbreserve(&so->so_snd, TCP_SNDSPACE); @@ -482,7 +458,7 @@ findso: * Reset idle time and keep-alive timer. */ tp->t_idle = 0; - if (SO_OPTIONS) + if (slirp_do_keepalive) tp->t_timer[TCPT_KEEP] = TCPTV_KEEPINTVL; else tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_IDLE; @@ -492,7 +468,7 @@ findso: * else do it below (after getting remote address). */ if (optp && tp->t_state != TCPS_LISTEN) - tcp_dooptions(tp, (u_char *)optp, optlen, ti); + tcp_dooptions(tp, (uint8_t *)optp, optlen, ti); /* * Header prediction: check for the two common cases @@ -528,7 +504,7 @@ findso: SEQ_GT(ti->ti_ack, tp->t_rtseq)) tcp_xmit_timer(tp, tp->t_rtt); acked = ti->ti_ack - tp->snd_una; - sbdrop(&so->so_snd, acked); + sodrop(so, acked); tp->snd_una = ti->ti_ack; m_free(m); @@ -632,13 +608,14 @@ findso: * If this is destined for the control address, then flag to * tcp_ctl once connected, otherwise connect */ + /* TODO: IPv6 */ 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 */ - for (ex_ptr = slirp->exec_list; ex_ptr; + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { if(ex_ptr->ex_fport == so->so_fport && so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { @@ -663,8 +640,7 @@ findso: (errno != EINPROGRESS) && (errno != EWOULDBLOCK) ) { uint8_t code; - DEBUG_MISC((dfd, " tcp fconnect errno = %d-%s\n", - errno,strerror(errno))); + DEBUG_MISC(" tcp fconnect errno = %d-%s", 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, @@ -748,7 +724,7 @@ findso: tcp_template(tp); if (optp) - tcp_dooptions(tp, (u_char *)optp, optlen, ti); + tcp_dooptions(tp, (uint8_t *)optp, optlen, ti); if (iss) tp->iss = iss; @@ -1033,8 +1009,7 @@ 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 = %p so = %p\n", - m, so)); + DEBUG_MISC(" dup ack m = %p so = %p", m, so); /* * If we have outstanding data (other than * a window probe), this is a completely @@ -1064,7 +1039,7 @@ trimthenstep6: tp->t_dupacks = 0; else if (++tp->t_dupacks == TCPREXMTTHRESH) { tcp_seq onxt = tp->snd_nxt; - u_int win = + unsigned win = MIN(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; @@ -1133,8 +1108,8 @@ trimthenstep6: * (maxseg^2 / cwnd per packet). */ { - register u_int cw = tp->snd_cwnd; - register u_int incr = tp->t_maxseg; + register unsigned cw = tp->snd_cwnd; + register unsigned incr = tp->t_maxseg; if (cw > tp->snd_ssthresh) incr = incr * incr / cw; @@ -1142,10 +1117,10 @@ trimthenstep6: } if (acked > so->so_snd.sb_cc) { tp->snd_wnd -= so->so_snd.sb_cc; - sbdrop(&so->so_snd, (int )so->so_snd.sb_cc); + sodrop(so, (int)so->so_snd.sb_cc); ourfinisacked = 1; } else { - sbdrop(&so->so_snd, acked); + sodrop(so, acked); tp->snd_wnd -= acked; ourfinisacked = 0; } @@ -1176,7 +1151,7 @@ trimthenstep6: } break; - /* + /* * In CLOSING STATE in addition to the processing for * the ESTABLISHED state if the ACK acknowledges our FIN * then enter the TIME-WAIT state, otherwise ignore @@ -1326,7 +1301,7 @@ dodata: } switch (tp->t_state) { - /* + /* * In SYN_RECEIVED and ESTABLISHED STATES * enter the CLOSE_WAIT state. */ @@ -1338,7 +1313,7 @@ dodata: tp->t_state = TCPS_CLOSE_WAIT; break; - /* + /* * If still in FIN_WAIT_1 STATE FIN has not been acked so * enter the CLOSING state. */ @@ -1346,7 +1321,7 @@ dodata: tp->t_state = TCPS_CLOSING; break; - /* + /* * In FIN_WAIT_2 state enter the TIME_WAIT state, * starting the time-wait timer, turning off the other * standard timers. @@ -1406,13 +1381,13 @@ drop: } static void -tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) +tcp_dooptions(struct tcpcb *tp, uint8_t *cp, int cnt, struct tcpiphdr *ti) { uint16_t mss; int opt, optlen; DEBUG_CALL("tcp_dooptions"); - DEBUG_ARGS((dfd, " tp = %p cnt=%i\n", tp, cnt)); + DEBUG_ARG("tp = %p cnt=%i", tp, cnt); for (; cnt > 0; cnt -= optlen, cp += optlen) { opt = cp[0]; @@ -1443,45 +1418,6 @@ tcp_dooptions(struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti) } } - -/* - * Pull out of band byte out of a segment so - * it doesn't appear in the user's data queue. - * It is still reflected in the segment length for - * sequencing purposes. - */ - -#ifdef notdef - -void -tcp_pulloutofband(so, ti, m) - struct socket *so; - struct tcpiphdr *ti; - register struct mbuf *m; -{ - int cnt = ti->ti_urp - 1; - - while (cnt >= 0) { - if (m->m_len > cnt) { - char *cp = mtod(m, caddr_t) + cnt; - struct tcpcb *tp = sototcpcb(so); - - tp->t_iobc = *cp; - tp->t_oobflags |= TCPOOB_HAVEDATA; - memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1)); - m->m_len--; - return; - } - cnt -= m->m_len; - m = m->m_next; /* XXX WRONG! Fix it! */ - if (m == 0) - break; - } - panic("tcp_pulloutofband"); -} - -#endif /* notdef */ - /* * Collect new round-trip time estimate * and update averages and current timeout. @@ -1575,7 +1511,7 @@ tcp_xmit_timer(register struct tcpcb *tp, int rtt) */ int -tcp_mss(struct tcpcb *tp, u_int offer) +tcp_mss(struct tcpcb *tp, unsigned offer) { struct socket *so = tp->t_socket; int mss; @@ -1612,7 +1548,7 @@ tcp_mss(struct tcpcb *tp, u_int offer) (mss - (TCP_RCVSPACE % mss)) : 0)); - DEBUG_MISC((dfd, " returning mss = %d\n", mss)); + DEBUG_MISC(" returning mss = %d", mss); return mss; } diff --git a/slirp/tcp_output.c b/slirp/src/tcp_output.c similarity index 96% rename from slirp/tcp_output.c rename to slirp/src/tcp_output.c index 90b5c376f7..6bbaf6ca6f 100644 --- a/slirp/tcp_output.c +++ b/slirp/src/tcp_output.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -33,15 +34,11 @@ /* * Changes and additions relating to SLiRP * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" -static const u_char tcp_outflags[TCP_NSTATES] = { +static const uint8_t tcp_outflags[TCP_NSTATES] = { TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK, TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK, TH_ACK, TH_ACK, @@ -64,7 +61,7 @@ tcp_output(struct tcpcb *tp) register struct tcpiphdr *ti, tcpiph_save; struct ip *ip; struct ip6 *ip6; - u_char opt[MAX_TCPOPTLEN]; + uint8_t opt[MAX_TCPOPTLEN]; unsigned optlen, hdrlen; int idle, sendalot; @@ -92,7 +89,7 @@ again: flags = tcp_outflags[tp->t_state]; - DEBUG_MISC((dfd, " --- tcp_output flags = 0x%x\n",flags)); + DEBUG_MISC(" --- tcp_output flags = 0x%x", flags); /* * If in persist timeout with window of 0, send 1 byte. @@ -272,12 +269,12 @@ send: opt[0] = TCPOPT_MAXSEG; opt[1] = 4; mss = htons((uint16_t) tcp_mss(tp, 0)); - memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss)); + memcpy((char *)(opt + 2), (char *)&mss, sizeof(mss)); optlen = 4; } - } + } - hdrlen += optlen; + hdrlen += optlen; /* * Adjust data length if insertion of options will @@ -302,7 +299,7 @@ send: m->m_data += IF_MAXLINKHDR; m->m_len = hdrlen; - sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen); + sbcopy(&so->so_snd, off, (int) len, mtod(m, char *) + hdrlen); m->m_len += len; /* @@ -325,7 +322,7 @@ send: ti = mtod(m, struct tcpiphdr *); - memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr)); + memcpy((char *)ti, &tp->t_template, sizeof (struct tcpiphdr)); /* * Fill in fields, remembering maximum advertised @@ -354,7 +351,7 @@ send: ti->ti_seq = htonl(tp->snd_max); ti->ti_ack = htonl(tp->rcv_nxt); if (optlen) { - memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen); + memcpy((char *)(ti + 1), (char *)opt, optlen); ti->ti_off = (sizeof (struct tcphdr) + optlen) >> 2; } ti->ti_flags = flags; diff --git a/slirp/tcp_subr.c b/slirp/src/tcp_subr.c similarity index 93% rename from slirp/tcp_subr.c rename to slirp/src/tcp_subr.c index 8d0f94b75f..fde9207b0c 100644 --- a/slirp/tcp_subr.c +++ b/slirp/src/tcp_subr.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -33,12 +34,8 @@ /* * Changes and additions relating to SLiRP * Copyright (c) 1995 Danny Gasparovski. - * - * Please read the file COPYRIGHT for the - * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" /* patchable/settable parameters for tcp */ @@ -164,7 +161,7 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, * ti points into m so the next line is just making * the mbuf point to ti */ - m->m_data = (caddr_t)ti; + m->m_data = (char *)ti; m->m_len = sizeof (struct tcpiphdr); tlen = 0; @@ -183,7 +180,7 @@ tcp_respond(struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, } #undef xchg } - ti->ti_len = htons((u_short)(sizeof (struct tcphdr) + tlen)); + ti->ti_len = htons((uint16_t)(sizeof (struct tcphdr) + tlen)); tlen += sizeof (struct tcpiphdr); m->m_len = tlen; @@ -337,6 +334,7 @@ tcp_close(struct tcpcb *tp) /* clobber input socket cache if we're closing the cached connection */ if (so == slirp->tcp_last_so) slirp->tcp_last_so = &slirp->tcb; + so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); closesocket(so->s); sbfree(&so->so_rcv); sbfree(&so->so_snd); @@ -407,20 +405,21 @@ int tcp_fconnect(struct socket *so, unsigned short af) DEBUG_CALL("tcp_fconnect"); DEBUG_ARG("so = %p", so); - ret = so->s = qemu_socket(af, SOCK_STREAM, 0); + ret = so->s = slirp_socket(af, SOCK_STREAM, 0); if (ret >= 0) { int opt, s=so->s; struct sockaddr_storage addr; - qemu_set_nonblock(s); - socket_set_fast_reuse(s); + slirp_set_nonblock(s); + so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); + slirp_socket_set_fast_reuse(s); opt = 1; - qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(opt)); opt = 1; - qemu_setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); + setsockopt(s, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)); addr = so->fhost.ss; - DEBUG_CALL(" connect()ing") + DEBUG_CALL(" connect()ing"); sotranslate_out(so, &addr); /* We don't care what port we get */ @@ -469,13 +468,8 @@ void tcp_connect(struct socket *inso) so = inso; } else { so = socreate(slirp); - if (so == NULL) { - /* If it failed, get rid of the pending connection */ - closesocket(accept(inso->s, (struct sockaddr *)&addr, &addrlen)); - return; - } if (tcp_attach(so) < 0) { - free(so); /* NOT sofree */ + g_free(so); /* NOT sofree */ return; } so->lhost = inso->lhost; @@ -489,11 +483,12 @@ void tcp_connect(struct socket *inso) tcp_close(sototcpcb(so)); /* This will sofree() as well */ return; } - qemu_set_nonblock(s); - socket_set_fast_reuse(s); + slirp_set_nonblock(s); + so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); + slirp_socket_set_fast_reuse(s); opt = 1; - qemu_setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); - socket_set_nodelay(s); + setsockopt(s, SOL_SOCKET, SO_OOBINLINE, &opt, sizeof(int)); + slirp_socket_set_nodelay(s); so->fhost.ss = addr; sotranslate_accept(so); @@ -501,6 +496,7 @@ void tcp_connect(struct socket *inso) /* Close the accept() socket, set right state */ if (inso->so_state & SS_FACCEPTONCE) { /* If we only accept once, close the accept() socket */ + so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); closesocket(so->s); /* Don't select it yet, even though we have an FD */ @@ -546,7 +542,6 @@ static const struct tos_t tcptos[] = { {0, 23, IPTOS_LOWDELAY, 0}, /* telnet */ {0, 80, IPTOS_THROUGHPUT, 0}, /* WWW */ {0, 513, IPTOS_LOWDELAY, EMU_RLOGIN|EMU_NOCONNECT}, /* rlogin */ - {0, 514, IPTOS_LOWDELAY, EMU_RSH|EMU_NOCONNECT}, /* shell */ {0, 544, IPTOS_LOWDELAY, EMU_KSH}, /* kshell */ {0, 543, IPTOS_LOWDELAY, 0}, /* klogin */ {0, 6667, IPTOS_THROUGHPUT, EMU_IRC}, /* IRC */ @@ -616,10 +611,10 @@ int tcp_emu(struct socket *so, struct mbuf *m) { Slirp *slirp = so->slirp; - u_int n1, n2, n3, n4, n5, n6; + unsigned n1, n2, n3, n4, n5, n6; char buff[257]; uint32_t laddr; - u_int lport; + unsigned lport; char *bptr; DEBUG_CALL("tcp_emu"); @@ -629,6 +624,7 @@ tcp_emu(struct socket *so, struct mbuf *m) switch(so->so_emu) { int x, i; + /* TODO: IPv6 */ case EMU_IDENT: /* * Identification protocol as per rfc-1413 @@ -640,6 +636,11 @@ tcp_emu(struct socket *so, struct mbuf *m) socklen_t addrlen = sizeof(struct sockaddr_in); struct sbuf *so_rcv = &so->so_rcv; + if (m->m_len > so_rcv->sb_datalen + - (so_rcv->sb_wptr - so_rcv->sb_data)) { + return 1; + } + memcpy(so_rcv->sb_wptr, m->m_data, m->m_len); so_rcv->sb_wptr += m->m_len; so_rcv->sb_rptr += m->m_len; @@ -658,16 +659,18 @@ tcp_emu(struct socket *so, struct mbuf *m) tmpso->so_fport == n1) { if (getsockname(tmpso->s, (struct sockaddr *)&addr, &addrlen) == 0) - n2 = ntohs(addr.sin_port); + n2 = addr.sin_port; break; } } + NTOHS(n1); + NTOHS(n2); + so_rcv->sb_cc = snprintf(so_rcv->sb_data, + so_rcv->sb_datalen, + "%d,%d\r\n", n1, n2); + so_rcv->sb_rptr = so_rcv->sb_data; + so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; } - so_rcv->sb_cc = snprintf(so_rcv->sb_data, - so_rcv->sb_datalen, - "%d,%d\r\n", n1, n2); - so_rcv->sb_rptr = so_rcv->sb_data; - so_rcv->sb_wptr = so_rcv->sb_data + so_rcv->sb_cc; } m_free(m); return 0; @@ -851,7 +854,7 @@ tcp_emu(struct socket *so, struct mbuf *m) bptr = m->m_data; while (bptr < m->m_data + m->m_len) { - u_short p; + uint16_t p; static int ra = 0; char ra_tbl[4]; @@ -907,8 +910,8 @@ tcp_emu(struct socket *so, struct mbuf *m) /* This is the field containing the port * number that RA-player is listening to. */ - lport = (((u_char*)bptr)[0] << 8) - + ((u_char *)bptr)[1]; + lport = (((uint8_t*)bptr)[0] << 8) + + ((uint8_t *)bptr)[1]; if (lport < 6970) lport += 256; /* don't know why */ if (lport < 6970 || lport > 7170) @@ -926,8 +929,8 @@ tcp_emu(struct socket *so, struct mbuf *m) } if (p == 7071) p = 0; - *(u_char *)bptr++ = (p >> 8) & 0xff; - *(u_char *)bptr = p & 0xff; + *(uint8_t *)bptr++ = (p >> 8) & 0xff; + *(uint8_t *)bptr = p & 0xff; ra = 0; return 1; /* port redirected, we're done */ break; @@ -955,25 +958,24 @@ int tcp_ctl(struct socket *so) { Slirp *slirp = so->slirp; struct sbuf *sb = &so->so_snd; - struct ex_list *ex_ptr; - int do_pty; + struct gfwd_list *ex_ptr; DEBUG_CALL("tcp_ctl"); DEBUG_ARG("so = %p", so); + /* TODO: IPv6 */ if (so->so_faddr.s_addr != slirp->vhost_addr.s_addr) { /* Check if it's pty_exec */ - for (ex_ptr = slirp->exec_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { + for (ex_ptr = slirp->guestfwd_list; ex_ptr; ex_ptr = ex_ptr->ex_next) { if (ex_ptr->ex_fport == so->so_fport && so->so_faddr.s_addr == ex_ptr->ex_addr.s_addr) { - if (ex_ptr->ex_pty == 3) { + if (ex_ptr->write_cb) { so->s = -1; - so->extra = (void *)ex_ptr->ex_exec; + so->guestfwd = ex_ptr; return 1; } - do_pty = ex_ptr->ex_pty; - DEBUG_MISC((dfd, " executing %s\n", ex_ptr->ex_exec)); - return fork_exec(so, ex_ptr->ex_exec, do_pty); + DEBUG_MISC(" executing %s", ex_ptr->ex_exec); + return fork_exec(so, ex_ptr->ex_exec); } } } diff --git a/slirp/tcp_timer.c b/slirp/src/tcp_timer.c similarity index 97% rename from slirp/tcp_timer.c rename to slirp/src/tcp_timer.c index 52ef5f9100..be361a1bb6 100644 --- a/slirp/tcp_timer.c +++ b/slirp/src/tcp_timer.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -30,7 +31,6 @@ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp */ -#include "qemu/osdep.h" #include "slirp.h" static struct tcpcb *tcp_timers(register struct tcpcb *tp, int timer); @@ -233,7 +233,7 @@ tcp_timers(register struct tcpcb *tp, int timer) * to go below this.) */ { - u_int win = MIN(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; + unsigned win = MIN(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg; if (win < 2) win = 2; tp->snd_cwnd = tp->t_maxseg; @@ -262,8 +262,8 @@ tcp_timers(register struct tcpcb *tp, int timer) if (tp->t_state < TCPS_ESTABLISHED) goto dropit; - if ((SO_OPTIONS) && tp->t_state <= TCPS_CLOSE_WAIT) { - if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE) + if (slirp_do_keepalive && tp->t_state <= TCPS_CLOSE_WAIT) { + if (tp->t_idle >= TCPTV_KEEP_IDLE + TCP_MAXIDLE) goto dropit; /* * Send a packet designed to force a response diff --git a/slirp/tcp_timer.h b/slirp/src/tcp_timer.h similarity index 99% rename from slirp/tcp_timer.h rename to slirp/src/tcp_timer.h index b25b3911d7..709f63987a 100644 --- a/slirp/tcp_timer.h +++ b/slirp/src/tcp_timer.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. diff --git a/slirp/tcp_var.h b/slirp/src/tcp_var.h similarity index 94% rename from slirp/tcp_var.h rename to slirp/src/tcp_var.h index 895ef6df1e..162be6e95e 100644 --- a/slirp/tcp_var.h +++ b/slirp/src/tcp_var.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993, 1994 * The Regents of the University of California. All rights reserved. @@ -47,9 +48,9 @@ struct tcpcb { short t_rxtshift; /* log(2) of rexmt exp. backoff */ short t_rxtcur; /* current retransmit value */ short t_dupacks; /* consecutive dup acks recd */ - u_short t_maxseg; /* maximum segment size */ + uint16_t t_maxseg; /* maximum segment size */ uint8_t t_force; /* 1 if forcing out a byte */ - u_short t_flags; + uint16_t t_flags; #define TF_ACKNOW 0x0001 /* ack peer immediately */ #define TF_DELACK 0x0002 /* ack, but try to delay it */ #define TF_NODELAY 0x0004 /* don't delay packets to coalesce */ @@ -105,7 +106,7 @@ struct tcpcb { tcp_seq t_rtseq; /* sequence number being timed */ short t_srtt; /* smoothed round-trip time */ short t_rttvar; /* variance in round-trip time */ - u_short t_rttmin; /* minimum rtt allowed */ + uint16_t t_rttmin; /* minimum rtt allowed */ uint32_t max_sndwnd; /* largest window peer has offered */ /* out-of-band data */ @@ -116,10 +117,10 @@ struct tcpcb { short t_softerror; /* possible error not yet reported */ /* RFC 1323 variables */ - u_char snd_scale; /* window scaling for send window */ - u_char rcv_scale; /* window scaling for recv window */ - u_char request_r_scale; /* pending window scaling */ - u_char requested_s_scale; + uint8_t snd_scale; /* window scaling for send window */ + uint8_t rcv_scale; /* window scaling for recv window */ + uint8_t request_r_scale; /* pending window scaling */ + uint8_t requested_s_scale; uint32_t ts_recent; /* timestamp echo data */ uint32_t ts_recent_age; /* when last updated */ tcp_seq last_ack_sent; diff --git a/slirp/tcpip.h b/slirp/src/tcpip.h similarity index 98% rename from slirp/tcpip.h rename to slirp/src/tcpip.h index 07dbf2c432..560a86417c 100644 --- a/slirp/tcpip.h +++ b/slirp/src/tcpip.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. diff --git a/slirp/tftp.c b/slirp/src/tftp.c similarity index 96% rename from slirp/tftp.c rename to slirp/src/tftp.c index a9bc4bb1b6..2071dca2a6 100644 --- a/slirp/tftp.c +++ b/slirp/src/tftp.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: MIT */ /* * tftp.c - a simple, read-only tftp server for qemu * @@ -22,10 +23,11 @@ * THE SOFTWARE. */ -#include "qemu/osdep.h" #include "slirp.h" -#include "qemu-common.h" -#include "qemu/cutils.h" + +#include +#include +#include static inline int tftp_session_in_use(struct tftp_session *spt) { @@ -204,6 +206,8 @@ static void tftp_send_error(struct tftp_session *spt, struct mbuf *m; struct tftp_t *tp; + DEBUG_TFTP("tftp error msg: %s", msg); + m = m_get(spt->slirp); if (!m) { @@ -214,7 +218,7 @@ static void tftp_send_error(struct tftp_session *spt, 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); + slirp_pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg), msg); m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + 3 + strlen(msg) - sizeof(struct udphdr); @@ -324,6 +328,8 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas, } } + DEBUG_TFTP("tftp rrq file: %s", req_fname); + /* check mode */ if ((pktlen - k) < 6) { tftp_send_error(spt, 2, "Access violation", tp); @@ -356,7 +362,7 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas, return; } - while (k < pktlen && nb_options < ARRAY_SIZE(option_name)) { + while (k < pktlen && nb_options < G_N_ELEMENTS(option_name)) { const char *key, *value; key = &tp->x.tp_buf[k]; @@ -400,7 +406,7 @@ static void tftp_handle_rrq(Slirp *slirp, struct sockaddr_storage *srcsas, } if (nb_options > 0) { - assert(nb_options <= ARRAY_SIZE(option_name)); + assert(nb_options <= G_N_ELEMENTS(option_name)); tftp_send_oack(spt, option_name, option_value, nb_options, tp); return; } diff --git a/slirp/tftp.h b/slirp/src/tftp.h similarity index 95% rename from slirp/tftp.h rename to slirp/src/tftp.h index a4c4a64e64..3fe3b70205 100644 --- a/slirp/tftp.h +++ b/slirp/src/tftp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* tftp defines */ #ifndef SLIRP_TFTP_H diff --git a/slirp/udp.c b/slirp/src/udp.c similarity index 93% rename from slirp/udp.c rename to slirp/src/udp.c index e5bf065bf2..27bb829c37 100644 --- a/slirp/udp.c +++ b/slirp/src/udp.c @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1988, 1990, 1993 * The Regents of the University of California. All rights reserved. @@ -38,7 +39,6 @@ * terms and conditions of the copyright. */ -#include "qemu/osdep.h" #include "slirp.h" #include "ip_icmp.h" @@ -93,7 +93,7 @@ udp_input(register struct mbuf *m, int iphlen) * Get IP and UDP header together in first mbuf. */ ip = mtod(m, struct ip *); - uh = (struct udphdr *)((caddr_t)ip + iphlen); + uh = (struct udphdr *)((char *)ip + iphlen); /* * Make mbuf data length reflect UDP length. @@ -171,12 +171,8 @@ udp_input(register struct mbuf *m, int iphlen) * create one */ so = socreate(slirp); - if (!so) { - goto bad; - } if (udp_attach(so, AF_INET) == -1) { - DEBUG_MISC((dfd," udp_attach errno = %d-%s\n", - errno,strerror(errno))); + DEBUG_MISC(" udp_attach errno = %d-%s", errno, strerror(errno)); sofree(so); goto bad; } @@ -212,7 +208,7 @@ udp_input(register struct mbuf *m, int iphlen) m->m_len += iphlen; m->m_data -= iphlen; *ip=save_ip; - DEBUG_MISC((dfd,"udp tx errno = %d-%s\n",errno,strerror(errno))); + DEBUG_MISC("udp tx errno = %d-%s", errno, strerror(errno)); icmp_send_error(m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno)); goto bad; @@ -285,7 +281,7 @@ int udp_output(struct socket *so, struct mbuf *m, int udp_attach(struct socket *so, unsigned short af) { - so->s = qemu_socket(af, SOCK_DGRAM, 0); + so->s = slirp_socket(af, SOCK_DGRAM, 0); if (so->s != -1) { so->so_expire = curtime + SO_EXPIRE; insque(so, &so->slirp->udb); @@ -296,6 +292,7 @@ udp_attach(struct socket *so, unsigned short af) void udp_detach(struct socket *so) { + so->slirp->cb->unregister_poll_fd(so->s, so->slirp->opaque); closesocket(so->s); sofree(so); } @@ -313,7 +310,7 @@ udp_tos(struct socket *so) while(udptos[i].tos) { if ((udptos[i].fport && ntohs(so->so_fport) == udptos[i].fport) || (udptos[i].lport && ntohs(so->so_lport) == udptos[i].lport)) { - so->so_emu = udptos[i].emu; + so->so_emu = udptos[i].emu; return udptos[i].tos; } i++; @@ -323,18 +320,16 @@ udp_tos(struct socket *so) } struct socket * -udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, - u_int lport, int flags) +udp_listen(Slirp *slirp, uint32_t haddr, unsigned hport, uint32_t laddr, + unsigned lport, int flags) { + /* TODO: IPv6 */ struct sockaddr_in addr; struct socket *so; socklen_t addrlen = sizeof(struct sockaddr_in); so = socreate(slirp); - if (!so) { - return NULL; - } - so->s = qemu_socket(AF_INET,SOCK_DGRAM,0); + so->s = slirp_socket(AF_INET,SOCK_DGRAM,0); if (so->s < 0) { sofree(so); return NULL; @@ -350,7 +345,7 @@ udp_listen(Slirp *slirp, uint32_t haddr, u_int hport, uint32_t laddr, udp_detach(so); return NULL; } - socket_set_fast_reuse(so->s); + slirp_socket_set_fast_reuse(so->s); getsockname(so->s,(struct sockaddr *)&addr,&addrlen); so->fhost.sin = addr; diff --git a/slirp/udp.h b/slirp/src/udp.h similarity index 96% rename from slirp/udp.h rename to slirp/src/udp.h index be657cf922..29c0297179 100644 --- a/slirp/udp.h +++ b/slirp/src/udp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 1982, 1986, 1993 * The Regents of the University of California. All rights reserved. @@ -78,7 +79,7 @@ void udp_cleanup(Slirp *); void udp_input(register struct mbuf *, int); 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, +struct socket * udp_listen(Slirp *, uint32_t, unsigned, uint32_t, unsigned, int); int udp_output(struct socket *so, struct mbuf *m, struct sockaddr_in *saddr, struct sockaddr_in *daddr, diff --git a/slirp/udp6.c b/slirp/src/udp6.c similarity index 91% rename from slirp/udp6.c rename to slirp/src/udp6.c index 7c4a6b003a..bfcc7ec6fa 100644 --- a/slirp/udp6.c +++ b/slirp/src/udp6.c @@ -1,10 +1,9 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Copyright (c) 2013 * Guillaume Subiron */ -#include "qemu/osdep.h" -#include "qemu-common.h" #include "slirp.h" #include "udp.h" #include "dhcpv6.h" @@ -20,7 +19,7 @@ void udp6_input(struct mbuf *m) struct sockaddr_in6 lhost; DEBUG_CALL("udp6_input"); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("m = %p", m); if (slirp->restricted) { goto bad; @@ -91,12 +90,8 @@ void udp6_input(struct mbuf *m) 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))); + DEBUG_MISC(" udp6_attach errno = %d-%s", errno, strerror(errno)); sofree(so); goto bad; } @@ -122,7 +117,7 @@ void udp6_input(struct mbuf *m) m->m_len += iphlen; m->m_data -= iphlen; *ip = save_ip; - DEBUG_MISC((dfd, "udp tx errno = %d-%s\n", errno, strerror(errno))); + DEBUG_MISC("udp tx errno = %d-%s", errno, strerror(errno)); icmp6_send_error(m, ICMP6_UNREACH, ICMP6_UNREACH_NO_ROUTE); goto bad; } @@ -147,8 +142,8 @@ int udp6_output(struct socket *so, struct mbuf *m, struct udphdr *uh; DEBUG_CALL("udp6_output"); - DEBUG_ARG("so = %lx", (long)so); - DEBUG_ARG("m = %lx", (long)m); + DEBUG_ARG("so = %p", so); + DEBUG_ARG("m = %p", m); /* adjust for header */ m->m_data -= sizeof(struct udphdr); diff --git a/slirp/src/util.c b/slirp/src/util.c new file mode 100644 index 0000000000..60bb200801 --- /dev/null +++ b/slirp/src/util.c @@ -0,0 +1,368 @@ +/* SPDX-License-Identifier: MIT */ +/* + * util.c (mostly based on QEMU os-win32.c) + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010-2016 Red Hat, Inc. + * + * QEMU library functions for win32 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 "util.h" + +#include +#include +#include + +#if defined(_WIN32) +int slirp_inet_aton(const char *cp, struct in_addr *ia) +{ + uint32_t addr = inet_addr(cp); + if (addr == 0xffffffff) { + return 0; + } + ia->s_addr = addr; + return 1; +} +#endif + +void slirp_set_nonblock(int fd) +{ +#ifndef _WIN32 + int f; + f = fcntl(fd, F_GETFL); + assert(f != -1); + f = fcntl(fd, F_SETFL, f | O_NONBLOCK); + assert(f != -1); +#else + unsigned long opt = 1; + ioctlsocket(fd, FIONBIO, &opt); +#endif +} + +static void slirp_set_cloexec(int fd) +{ +#ifndef _WIN32 + int f; + f = fcntl(fd, F_GETFD); + assert(f != -1); + f = fcntl(fd, F_SETFD, f | FD_CLOEXEC); + assert(f != -1); +#endif +} + +/* + * Opens a socket with FD_CLOEXEC set + */ +int slirp_socket(int domain, int type, int protocol) +{ + int ret; + +#ifdef SOCK_CLOEXEC + ret = socket(domain, type | SOCK_CLOEXEC, protocol); + if (ret != -1 || errno != EINVAL) { + return ret; + } +#endif + ret = socket(domain, type, protocol); + if (ret >= 0) { + slirp_set_cloexec(ret); + } + + return ret; +} + +#ifdef _WIN32 +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; + } +} + +#undef ioctlsocket +int slirp_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 slirp_closesocket_wrap(int fd) +{ + int ret; + ret = closesocket(fd); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef connect +int slirp_connect_wrap(int sockfd, const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = connect(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef listen +int slirp_listen_wrap(int sockfd, int backlog) +{ + int ret; + ret = listen(sockfd, backlog); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef bind +int slirp_bind_wrap(int sockfd, const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = bind(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef socket +int slirp_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 slirp_accept_wrap(int sockfd, struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = accept(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef shutdown +int slirp_shutdown_wrap(int sockfd, int how) +{ + int ret; + ret = shutdown(sockfd, how); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef getsockopt +int slirp_getsockopt_wrap(int sockfd, int level, int optname, + void *optval, int *optlen) +{ + int ret; + ret = getsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef setsockopt +int slirp_setsockopt_wrap(int sockfd, int level, int optname, + const void *optval, int optlen) +{ + int ret; + ret = setsockopt(sockfd, level, optname, optval, optlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef getpeername +int slirp_getpeername_wrap(int sockfd, struct sockaddr *addr, + int *addrlen) +{ + int ret; + ret = getpeername(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef getsockname +int slirp_getsockname_wrap(int sockfd, struct sockaddr *addr, + int *addrlen) +{ + int ret; + ret = getsockname(sockfd, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef send +ssize_t slirp_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 slirp_sendto_wrap(int sockfd, const void *buf, size_t len, int flags, + const struct sockaddr *addr, int addrlen) +{ + int ret; + ret = sendto(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} + +#undef recv +ssize_t slirp_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 slirp_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, + struct sockaddr *addr, int *addrlen) +{ + int ret; + ret = recvfrom(sockfd, buf, len, flags, addr, addrlen); + if (ret < 0) { + errno = socket_error(); + } + return ret; +} +#endif /* WIN32 */ + +void slirp_pstrcpy(char *buf, int buf_size, const char *str) +{ + int c; + char *q = buf; + + if (buf_size <= 0) + return; + + for(;;) { + c = *str++; + if (c == 0 || q >= buf + buf_size - 1) + break; + *q++ = c; + } + *q = '\0'; +} diff --git a/slirp/src/util.h b/slirp/src/util.h new file mode 100644 index 0000000000..01f1e0e068 --- /dev/null +++ b/slirp/src/util.h @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2010-2019 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 UTIL_H_ +#define UTIL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#else +#include +#include +#include +#endif + +#if defined(_WIN32) +# define SLIRP_PACKED __attribute__((gcc_struct, packed)) +#else +# define SLIRP_PACKED __attribute__((packed)) +#endif + +#ifndef DIV_ROUND_UP +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#endif + +#ifndef container_of +#define container_of(ptr, type, member) __extension__ ({ \ + void *__mptr = (void *)(ptr); \ + ((type *)(__mptr - offsetof(type, member))); }) +#endif + +#if defined(_WIN32) /* CONFIG_IOVEC */ +# if !defined(IOV_MAX) /* XXX: to avoid duplicate with QEMU osdep.h */ +struct iovec { + void *iov_base; + size_t iov_len; +}; +# endif +#else +#include +#endif + +#define SCALE_MS 1000000 + +#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 ETH_P_IPV6 (0x86dd) +#define ETH_P_VLAN (0x8100) +#define ETH_P_DVLAN (0x88a8) +#define ETH_P_NCSI (0x88f8) +#define ETH_P_UNKNOWN (0xffff) + +/* FIXME: remove me when made standalone */ +#ifdef _WIN32 +#undef accept +#undef bind +#undef closesocket +#undef connect +#undef getpeername +#undef getsockname +#undef getsockopt +#undef ioctlsocket +#undef listen +#undef recv +#undef recvfrom +#undef send +#undef sendto +#undef setsockopt +#undef shutdown +#undef socket +#endif + +#ifdef _WIN32 +#define connect slirp_connect_wrap +int slirp_connect_wrap(int fd, const struct sockaddr *addr, int addrlen); +#define listen slirp_listen_wrap +int slirp_listen_wrap(int fd, int backlog); +#define bind slirp_bind_wrap +int slirp_bind_wrap(int fd, const struct sockaddr *addr, int addrlen); +#define socket slirp_socket_wrap +int slirp_socket_wrap(int domain, int type, int protocol); +#define accept slirp_accept_wrap +int slirp_accept_wrap(int fd, struct sockaddr *addr, int *addrlen); +#define shutdown slirp_shutdown_wrap +int slirp_shutdown_wrap(int fd, int how); +#define getpeername slirp_getpeername_wrap +int slirp_getpeername_wrap(int fd, struct sockaddr *addr, int *addrlen); +#define getsockname slirp_getsockname_wrap +int slirp_getsockname_wrap(int fd, struct sockaddr *addr, int *addrlen); +#define send slirp_send_wrap +ssize_t slirp_send_wrap(int fd, const void *buf, size_t len, int flags); +#define sendto slirp_sendto_wrap +ssize_t slirp_sendto_wrap(int fd, const void *buf, size_t len, int flags, + const struct sockaddr *dest_addr, int addrlen); +#define recv slirp_recv_wrap +ssize_t slirp_recv_wrap(int fd, void *buf, size_t len, int flags); +#define recvfrom slirp_recvfrom_wrap +ssize_t slirp_recvfrom_wrap(int fd, void *buf, size_t len, int flags, + struct sockaddr *src_addr, int *addrlen); +#define closesocket slirp_closesocket_wrap +int slirp_closesocket_wrap(int fd); +#define ioctlsocket slirp_ioctlsocket_wrap +int slirp_ioctlsocket_wrap(int fd, int req, void *val); +#define getsockopt slirp_getsockopt_wrap +int slirp_getsockopt_wrap(int sockfd, int level, int optname, + void *optval, int *optlen); +#define setsockopt slirp_setsockopt_wrap +int slirp_setsockopt_wrap(int sockfd, int level, int optname, + const void *optval, int optlen); +#define inet_aton slirp_inet_aton +int slirp_inet_aton(const char *cp, struct in_addr *ia); +#else +#define closesocket(s) close(s) +#define ioctlsocket(s, r, v) ioctl(s, r, v) +#endif + +int slirp_socket(int domain, int type, int protocol); +void slirp_set_nonblock(int fd); + +static inline int slirp_socket_set_nodelay(int fd) +{ + int v = 1; + return setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &v, sizeof(v)); +} + +static inline int slirp_socket_set_fast_reuse(int fd) +{ +#ifndef _WIN32 + int v = 1; + return setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(v)); +#else + /* Enabling the reuse of an endpoint that was used by a socket still in + * TIME_WAIT state is usually performed by setting SO_REUSEADDR. On Windows + * fast reuse is the default and SO_REUSEADDR does strange things. So we + * don't have to do anything here. More info can be found at: + * http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621.aspx */ + return 0; +#endif +} + +void slirp_pstrcpy(char *buf, int buf_size, const char *str); + +#endif diff --git a/slirp/src/vmstate.c b/slirp/src/vmstate.c new file mode 100644 index 0000000000..43bb3ebc6f --- /dev/null +++ b/slirp/src/vmstate.c @@ -0,0 +1,441 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * VMState interpreter + * + * Copyright (c) 2009-2018 Red Hat Inc + * + * Authors: + * Juan Quintela + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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 +#include +#include + +#include "stream.h" +#include "vmstate.h" + +static int get_nullptr(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + if (slirp_istream_read_u8(f) == VMS_NULLPTR_MARKER) { + return 0; + } + g_warning("vmstate: get_nullptr expected VMS_NULLPTR_MARKER"); + return -EINVAL; +} + +static int put_nullptr(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) + +{ + if (pv == NULL) { + slirp_ostream_write_u8(f, VMS_NULLPTR_MARKER); + return 0; + } + g_warning("vmstate: put_nullptr must be called with pv == NULL"); + return -EINVAL; +} + +const VMStateInfo slirp_vmstate_info_nullptr = { + .name = "uint64", + .get = get_nullptr, + .put = put_nullptr, +}; + +/* 8 bit unsigned int */ + +static int get_uint8(SlirpIStream *f, void *pv, size_t size, const VMStateField *field) +{ + uint8_t *v = pv; + *v = slirp_istream_read_u8(f); + return 0; +} + +static int put_uint8(SlirpOStream *f, void *pv, size_t size, const VMStateField *field) +{ + uint8_t *v = pv; + slirp_ostream_write_u8(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_uint8 = { + .name = "uint8", + .get = get_uint8, + .put = put_uint8, +}; + +/* 16 bit unsigned int */ + +static int get_uint16(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint16_t *v = pv; + *v = slirp_istream_read_u16(f); + return 0; +} + +static int put_uint16(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint16_t *v = pv; + slirp_ostream_write_u16(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_uint16 = { + .name = "uint16", + .get = get_uint16, + .put = put_uint16, +}; + +/* 32 bit unsigned int */ + +static int get_uint32(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint32_t *v = pv; + *v = slirp_istream_read_u32(f); + return 0; +} + +static int put_uint32(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + uint32_t *v = pv; + slirp_ostream_write_u32(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_uint32 = { + .name = "uint32", + .get = get_uint32, + .put = put_uint32, +}; + +/* 16 bit int */ + +static int get_int16(SlirpIStream *f, void *pv, size_t size, const VMStateField *field) +{ + int16_t *v = pv; + *v = slirp_istream_read_i16(f); + return 0; +} + +static int put_int16(SlirpOStream *f, void *pv, size_t size, const VMStateField *field) +{ + int16_t *v = pv; + slirp_ostream_write_i16(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_int16 = { + .name = "int16", + .get = get_int16, + .put = put_int16, +}; + +/* 32 bit int */ + +static int get_int32(SlirpIStream *f, void *pv, size_t size, const VMStateField *field) +{ + int32_t *v = pv; + *v = slirp_istream_read_i32(f); + return 0; +} + +static int put_int32(SlirpOStream *f, void *pv, size_t size, const VMStateField *field) +{ + int32_t *v = pv; + slirp_ostream_write_i32(f, *v); + return 0; +} + +const VMStateInfo slirp_vmstate_info_int32 = { + .name = "int32", + .get = get_int32, + .put = put_int32, +}; + +/* vmstate_info_tmp, see VMSTATE_WITH_TMP, the idea is that we allocate + * a temporary buffer and the pre_load/pre_save methods in the child vmsd + * copy stuff from the parent into the child and do calculations to fill + * in fields that don't really exist in the parent but need to be in the + * stream. + */ +static int get_tmp(SlirpIStream *f, void *pv, size_t size, const VMStateField *field) +{ + int ret; + const VMStateDescription *vmsd = field->vmsd; + int version_id = field->version_id; + void *tmp = g_malloc(size); + + /* Writes the parent field which is at the start of the tmp */ + *(void **)tmp = pv; + ret = slirp_vmstate_load_state(f, vmsd, tmp, version_id); + g_free(tmp); + return ret; +} + +static int put_tmp(SlirpOStream *f, void *pv, size_t size, const VMStateField *field) +{ + const VMStateDescription *vmsd = field->vmsd; + void *tmp = g_malloc(size); + int ret; + + /* Writes the parent field which is at the start of the tmp */ + *(void **)tmp = pv; + ret = slirp_vmstate_save_state(f, vmsd, tmp); + g_free(tmp); + + return ret; +} + +const VMStateInfo slirp_vmstate_info_tmp = { + .name = "tmp", + .get = get_tmp, + .put = put_tmp, +}; + +/* uint8_t buffers */ + +static int get_buffer(SlirpIStream *f, void *pv, size_t size, + const VMStateField *field) +{ + slirp_istream_read(f, pv, size); + return 0; +} + +static int put_buffer(SlirpOStream *f, void *pv, size_t size, + const VMStateField *field) +{ + slirp_ostream_write(f, pv, size); + return 0; +} + +const VMStateInfo slirp_vmstate_info_buffer = { + .name = "buffer", + .get = get_buffer, + .put = put_buffer, +}; + +static int vmstate_n_elems(void *opaque, const VMStateField *field) +{ + int n_elems = 1; + + if (field->flags & VMS_ARRAY) { + n_elems = field->num; + } else if (field->flags & VMS_VARRAY_INT32) { + n_elems = *(int32_t *)(opaque + field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT32) { + n_elems = *(uint32_t *)(opaque + field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT16) { + n_elems = *(uint16_t *)(opaque + field->num_offset); + } else if (field->flags & VMS_VARRAY_UINT8) { + n_elems = *(uint8_t *)(opaque + field->num_offset); + } + + if (field->flags & VMS_MULTIPLY_ELEMENTS) { + n_elems *= field->num; + } + + return n_elems; +} + +static int vmstate_size(void *opaque, const VMStateField *field) +{ + int size = field->size; + + if (field->flags & VMS_VBUFFER) { + size = *(int32_t *)(opaque + field->size_offset); + if (field->flags & VMS_MULTIPLY) { + size *= field->size; + } + } + + return size; +} + +static int +vmstate_save_state_v(SlirpOStream *f, const VMStateDescription *vmsd, + void *opaque, int version_id) +{ + int ret = 0; + const VMStateField *field = vmsd->fields; + + if (vmsd->pre_save) { + ret = vmsd->pre_save(opaque); + if (ret) { + g_warning("pre-save failed: %s", vmsd->name); + return ret; + } + } + + while (field->name) { + if ((field->field_exists && + field->field_exists(opaque, version_id)) || + (!field->field_exists && + field->version_id <= version_id)) { + void *first_elem = opaque + field->offset; + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); + + if (field->flags & VMS_POINTER) { + first_elem = *(void **)first_elem; + assert(first_elem || !n_elems || !size); + } + for (i = 0; i < n_elems; i++) { + void *curr_elem = first_elem + size * i; + ret = 0; + + if (field->flags & VMS_ARRAY_OF_POINTER) { + assert(curr_elem); + curr_elem = *(void **)curr_elem; + } + if (!curr_elem && size) { + /* if null pointer write placeholder and do not follow */ + assert(field->flags & VMS_ARRAY_OF_POINTER); + ret = slirp_vmstate_info_nullptr.put(f, curr_elem, size, NULL); + } else if (field->flags & VMS_STRUCT) { + ret = slirp_vmstate_save_state(f, field->vmsd, curr_elem); + } else if (field->flags & VMS_VSTRUCT) { + ret = vmstate_save_state_v(f, field->vmsd, curr_elem, + field->struct_version_id); + } else { + ret = field->info->put(f, curr_elem, size, field); + } + if (ret) { + g_warning("Save of field %s/%s failed", + vmsd->name, field->name); + return ret; + } + } + } else { + if (field->flags & VMS_MUST_EXIST) { + g_warning("Output state validation failed: %s/%s", + vmsd->name, field->name); + assert(!(field->flags & VMS_MUST_EXIST)); + } + } + field++; + } + + return 0; +} + +int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd, + void *opaque) +{ + return vmstate_save_state_v(f, vmsd, opaque, vmsd->version_id); +} + +static void vmstate_handle_alloc(void *ptr, VMStateField *field, void *opaque) +{ + if (field->flags & VMS_POINTER && field->flags & VMS_ALLOC) { + size_t size = vmstate_size(opaque, field); + size *= vmstate_n_elems(opaque, field); + if (size) { + *(void **)ptr = g_malloc(size); + } + } +} + +int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd, + void *opaque, int version_id) +{ + VMStateField *field = vmsd->fields; + int ret = 0; + + if (version_id > vmsd->version_id) { + g_warning("%s: incoming version_id %d is too new " + "for local version_id %d", + vmsd->name, version_id, vmsd->version_id); + return -EINVAL; + } + if (vmsd->pre_load) { + int ret = vmsd->pre_load(opaque); + if (ret) { + return ret; + } + } + while (field->name) { + if ((field->field_exists && + field->field_exists(opaque, version_id)) || + (!field->field_exists && + field->version_id <= version_id)) { + void *first_elem = opaque + field->offset; + int i, n_elems = vmstate_n_elems(opaque, field); + int size = vmstate_size(opaque, field); + + vmstate_handle_alloc(first_elem, field, opaque); + if (field->flags & VMS_POINTER) { + first_elem = *(void **)first_elem; + assert(first_elem || !n_elems || !size); + } + for (i = 0; i < n_elems; i++) { + void *curr_elem = first_elem + size * i; + + if (field->flags & VMS_ARRAY_OF_POINTER) { + curr_elem = *(void **)curr_elem; + } + if (!curr_elem && size) { + /* if null pointer check placeholder and do not follow */ + assert(field->flags & VMS_ARRAY_OF_POINTER); + ret = slirp_vmstate_info_nullptr.get(f, curr_elem, size, NULL); + } else if (field->flags & VMS_STRUCT) { + ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem, + field->vmsd->version_id); + } else if (field->flags & VMS_VSTRUCT) { + ret = slirp_vmstate_load_state(f, field->vmsd, curr_elem, + field->struct_version_id); + } else { + ret = field->info->get(f, curr_elem, size, field); + } + if (ret < 0) { + g_warning("Failed to load %s:%s", vmsd->name, + field->name); + return ret; + } + } + } else if (field->flags & VMS_MUST_EXIST) { + g_warning("Input validation failed: %s/%s", + vmsd->name, field->name); + return -1; + } + field++; + } + if (vmsd->post_load) { + ret = vmsd->post_load(opaque, version_id); + } + return ret; +} diff --git a/slirp/src/vmstate.h b/slirp/src/vmstate.h new file mode 100644 index 0000000000..44efea7b50 --- /dev/null +++ b/slirp/src/vmstate.h @@ -0,0 +1,409 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * QEMU migration/snapshot declarations + * + * Copyright (c) 2009-2011 Red Hat, Inc. + * + * Original author: Juan Quintela + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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 VMSTATE_H_ +#define VMSTATE_H_ + +#include +#include +#include +#include "slirp.h" +#include "stream.h" + +#define stringify(s) tostring(s) +#define tostring(s) #s + +typedef struct VMStateInfo VMStateInfo; +typedef struct VMStateDescription VMStateDescription; +typedef struct VMStateField VMStateField; + +int slirp_vmstate_save_state(SlirpOStream *f, const VMStateDescription *vmsd, + void *opaque); +int slirp_vmstate_load_state(SlirpIStream *f, const VMStateDescription *vmsd, + void *opaque, int version_id); + +/* VMStateInfo allows customized migration of objects that don't fit in + * any category in VMStateFlags. Additional information is always passed + * into get and put in terms of field and vmdesc parameters. However + * these two parameters should only be used in cases when customized + * handling is needed, such as QTAILQ. For primitive data types such as + * integer, field and vmdesc parameters should be ignored inside get/put. + */ +struct VMStateInfo { + const char *name; + int (*get)(SlirpIStream *f, void *pv, size_t size, const VMStateField *field); + int (*put)(SlirpOStream *f, void *pv, size_t size, const VMStateField *field); +}; + +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, + + /* 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, + + /* 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, + + /* A structure field that is like VMS_STRUCT, but uses + * VMStateField.struct_version_id to tell which version of the + * structure we are referencing to use. */ + VMS_VSTRUCT = 0x8000, +}; + +struct VMStateField { + const char *name; + size_t offset; + size_t size; + size_t start; + int num; + size_t num_offset; + size_t size_offset; + const VMStateInfo *info; + enum VMStateFlags flags; + const VMStateDescription *vmsd; + int version_id; + int struct_version_id; + bool (*field_exists)(void *opaque, int version_id); +}; + +struct VMStateDescription { + const char *name; + int version_id; + int (*pre_load)(void *opaque); + int (*post_load)(void *opaque, int version_id); + int (*pre_save)(void *opaque); + VMStateField *fields; +}; + + +extern const VMStateInfo slirp_vmstate_info_int16; +extern const VMStateInfo slirp_vmstate_info_int32; +extern const VMStateInfo slirp_vmstate_info_uint8; +extern const VMStateInfo slirp_vmstate_info_uint16; +extern const VMStateInfo slirp_vmstate_info_uint32; + +/** Put this in the stream when migrating a null pointer.*/ +#define VMS_NULLPTR_MARKER (0x30U) /* '0' */ +extern const VMStateInfo slirp_vmstate_info_nullptr; + +extern const VMStateInfo slirp_vmstate_info_buffer; +extern const VMStateInfo slirp_vmstate_info_tmp; + +#define type_check_array(t1,t2,n) ((t1(*)[n])0 - (t2*)0) +#define type_check_pointer(t1,t2) ((t1**)0 - (t2*)0) +#define typeof_field(type, field) typeof(((type *)0)->field) +#define type_check(t1,t2) ((t1*)0 - (t2*)0) + +#define vmstate_offset_value(_state, _field, _type) \ + (offsetof(_state, _field) + \ + type_check(_type, typeof_field(_state, _field))) + +#define vmstate_offset_pointer(_state, _field, _type) \ + (offsetof(_state, _field) + \ + type_check_pointer(_type, typeof_field(_state, _field))) + +#define vmstate_offset_array(_state, _field, _type, _num) \ + (offsetof(_state, _field) + \ + type_check_array(_type, typeof_field(_state, _field), _num)) + +#define vmstate_offset_buffer(_state, _field) \ + vmstate_offset_array(_state, _field, uint8_t, \ + sizeof(typeof_field(_state, _field))) + +/* In the macros below, if there is a _version, that means the macro's + * field will be processed only if the version being received is >= + * the _version specified. In general, if you add a new field, you + * would increment the structure's version and put that version + * number into the new field so it would only be processed with the + * new version. + * + * In particular, for VMSTATE_STRUCT() and friends the _version does + * *NOT* pick the version of the sub-structure. It works just as + * specified above. The version of the top-level structure received + * is passed down to all sub-structures. This means that the + * sub-structures must have version that are compatible with all the + * structures that use them. + * + * If you want to specify the version of the sub-structure, use + * VMSTATE_VSTRUCT(), which allows the specific sub-structure version + * to be directly specified. + */ + +#define VMSTATE_SINGLE_TEST(_field, _state, _test, _version, _info, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size = sizeof(_type), \ + .info = &(_info), \ + .flags = VMS_SINGLE, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + +#define VMSTATE_ARRAY(_field, _state, _num, _version, _info, _type) {\ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .num = (_num), \ + .info = &(_info), \ + .size = sizeof(_type), \ + .flags = VMS_ARRAY, \ + .offset = vmstate_offset_array(_state, _field, _type, _num), \ +} + +#define VMSTATE_STRUCT_TEST(_field, _state, _test, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, _type), \ +} + +#define VMSTATE_STRUCT_POINTER_V(_field, _state, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type *), \ + .flags = VMS_STRUCT|VMS_POINTER, \ + .offset = vmstate_offset_pointer(_state, _field, _type), \ +} + +#define VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, _test, _version, _vmsd, _type) { \ + .name = (stringify(_field)), \ + .num = (_num), \ + .field_exists = (_test), \ + .version_id = (_version), \ + .vmsd = &(_vmsd), \ + .size = sizeof(_type), \ + .flags = VMS_STRUCT|VMS_ARRAY, \ + .offset = vmstate_offset_array(_state, _field, _type, _num),\ +} + +#define VMSTATE_STATIC_BUFFER(_field, _state, _version, _test, _start, _size) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size = (_size - _start), \ + .info = &slirp_vmstate_info_buffer, \ + .flags = VMS_BUFFER, \ + .offset = vmstate_offset_buffer(_state, _field) + _start, \ +} + +#define VMSTATE_VBUFFER_UINT32(_field, _state, _version, _test, _field_size) { \ + .name = (stringify(_field)), \ + .version_id = (_version), \ + .field_exists = (_test), \ + .size_offset = vmstate_offset_value(_state, _field_size, uint32_t),\ + .info = &slirp_vmstate_info_buffer, \ + .flags = VMS_VBUFFER|VMS_POINTER, \ + .offset = offsetof(_state, _field), \ +} + +#define QEMU_BUILD_BUG_ON_STRUCT(x) \ + struct { \ + int:(x) ? -1 : 1; \ + } + +#define QEMU_BUILD_BUG_ON_ZERO(x) (sizeof(QEMU_BUILD_BUG_ON_STRUCT(x)) - \ + sizeof(QEMU_BUILD_BUG_ON_STRUCT(x))) + +/* Allocate a temporary of type 'tmp_type', set tmp->parent to _state + * and execute the vmsd on the temporary. Note that we're working with + * the whole of _state here, not a field within it. + * We compile time check that: + * That _tmp_type contains a 'parent' member that's a pointer to the + * '_state' type + * That the pointer is right at the start of _tmp_type. + */ +#define VMSTATE_WITH_TMP(_state, _tmp_type, _vmsd) { \ + .name = "tmp", \ + .size = sizeof(_tmp_type) + \ + QEMU_BUILD_BUG_ON_ZERO(offsetof(_tmp_type, parent) != 0) + \ + type_check_pointer(_state, \ + typeof_field(_tmp_type, parent)), \ + .vmsd = &(_vmsd), \ + .info = &slirp_vmstate_info_tmp, \ +} + +#define VMSTATE_SINGLE(_field, _state, _version, _info, _type) \ + VMSTATE_SINGLE_TEST(_field, _state, NULL, _version, _info, _type) + +#define VMSTATE_STRUCT(_field, _state, _version, _vmsd, _type) \ + VMSTATE_STRUCT_TEST(_field, _state, NULL, _version, _vmsd, _type) + +#define VMSTATE_STRUCT_POINTER(_field, _state, _vmsd, _type) \ + VMSTATE_STRUCT_POINTER_V(_field, _state, 0, _vmsd, _type) + +#define VMSTATE_STRUCT_ARRAY(_field, _state, _num, _version, _vmsd, _type) \ + VMSTATE_STRUCT_ARRAY_TEST(_field, _state, _num, NULL, _version, \ + _vmsd, _type) + +#define VMSTATE_INT16_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int16, int16_t) +#define VMSTATE_INT32_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_int32, int32_t) + +#define VMSTATE_UINT8_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint8, uint8_t) +#define VMSTATE_UINT16_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint16, uint16_t) +#define VMSTATE_UINT32_V(_f, _s, _v) \ + VMSTATE_SINGLE(_f, _s, _v, slirp_vmstate_info_uint32, uint32_t) + +#define VMSTATE_INT16(_f, _s) \ + VMSTATE_INT16_V(_f, _s, 0) +#define VMSTATE_INT32(_f, _s) \ + VMSTATE_INT32_V(_f, _s, 0) + +#define VMSTATE_UINT8(_f, _s) \ + VMSTATE_UINT8_V(_f, _s, 0) +#define VMSTATE_UINT16(_f, _s) \ + VMSTATE_UINT16_V(_f, _s, 0) +#define VMSTATE_UINT32(_f, _s) \ + VMSTATE_UINT32_V(_f, _s, 0) + +#define VMSTATE_UINT16_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint16, uint16_t) + +#define VMSTATE_UINT32_TEST(_f, _s, _t) \ + VMSTATE_SINGLE_TEST(_f, _s, _t, 0, slirp_vmstate_info_uint32, uint32_t) + +#define VMSTATE_INT16_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_ARRAY(_f, _s, _n, _v, slirp_vmstate_info_int16, int16_t) + +#define VMSTATE_INT16_ARRAY(_f, _s, _n) \ + VMSTATE_INT16_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))) + +#define VMSTATE_BUFFER(_f, _s) \ + VMSTATE_BUFFER_V(_f, _s, 0) + +#define VMSTATE_END_OF_LIST() \ + {} + +#endif diff --git a/stubs/Makefile.objs b/stubs/Makefile.objs index 53d3f32cb2..269dfa5832 100644 --- a/stubs/Makefile.objs +++ b/stubs/Makefile.objs @@ -1,7 +1,3 @@ -stub-obj-y += arch-query-cpu-def.o -stub-obj-y += arch-query-cpu-model-expansion.o -stub-obj-y += arch-query-cpu-model-comparison.o -stub-obj-y += arch-query-cpu-model-baseline.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 @@ -26,14 +22,13 @@ stub-obj-y += qtest.o stub-obj-y += replay.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 += tpm.o stub-obj-y += trace-control.o stub-obj-y += uuid.o stub-obj-y += vm-stop.o stub-obj-y += vmstate.o -stub-obj-$(CONFIG_WIN32) += fd-register.o +stub-obj-y += fd-register.o stub-obj-y += qmp_memory_device.o stub-obj-y += target-monitor-defs.o stub-obj-y += target-get-monitor-def.o @@ -43,3 +38,4 @@ stub-obj-y += xen-common.o stub-obj-y += xen-hvm.o stub-obj-y += pci-host-piix.o stub-obj-y += ram-block.o +stub-obj-y += ramfb.o diff --git a/stubs/arch-query-cpu-def.c b/stubs/arch-query-cpu-def.c deleted file mode 100644 index d436f95314..0000000000 --- a/stubs/arch-query-cpu-def.c +++ /dev/null @@ -1,11 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/arch_init.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" - -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} diff --git a/stubs/arch-query-cpu-model-baseline.c b/stubs/arch-query-cpu-model-baseline.c deleted file mode 100644 index 0d066da328..0000000000 --- a/stubs/arch-query-cpu-model-baseline.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/arch_init.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" - -CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *modela, - CpuModelInfo *modelb, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} diff --git a/stubs/arch-query-cpu-model-comparison.c b/stubs/arch-query-cpu-model-comparison.c deleted file mode 100644 index 8eb311a26c..0000000000 --- a/stubs/arch-query-cpu-model-comparison.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/arch_init.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" - -CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *modela, - CpuModelInfo *modelb, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} diff --git a/stubs/arch-query-cpu-model-expansion.c b/stubs/arch-query-cpu-model-expansion.c deleted file mode 100644 index 26273a8b10..0000000000 --- a/stubs/arch-query-cpu-model-expansion.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "sysemu/arch_init.h" -#include "qapi/error.h" -#include "qapi/qmp/qerror.h" - -CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type, - CpuModelInfo *mode, - Error **errp) -{ - error_setg(errp, QERR_UNSUPPORTED); - return NULL; -} diff --git a/stubs/cpu-get-icount.c b/stubs/cpu-get-icount.c index 0b7239d721..35f0c1e24c 100644 --- a/stubs/cpu-get-icount.c +++ b/stubs/cpu-get-icount.c @@ -11,6 +11,11 @@ int64_t cpu_get_icount(void) abort(); } +int64_t cpu_get_icount_raw(void) +{ + abort(); +} + void qemu_timer_notify_cb(void *opaque, QEMUClockType type) { qemu_notify_event(); diff --git a/stubs/iothread-lock.c b/stubs/iothread-lock.c index 9b6db2e740..eb745d7d6a 100644 --- a/stubs/iothread-lock.c +++ b/stubs/iothread-lock.c @@ -7,7 +7,7 @@ bool qemu_mutex_iothread_locked(void) return true; } -void qemu_mutex_lock_iothread(void) +void qemu_mutex_lock_iothread_impl(const char *file, int line) { } diff --git a/stubs/monitor.c b/stubs/monitor.c index 3890771bb5..b57fe6c32f 100644 --- a/stubs/monitor.c +++ b/stubs/monitor.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qapi/qapi-emit-events.h" #include "qemu-common.h" #include "monitor/monitor.h" @@ -14,3 +15,7 @@ int monitor_get_fd(Monitor *mon, const char *name, Error **errp) void monitor_init(Chardev *chr, int flags) { } + +void qapi_event_emit(QAPIEvent event, QDict *qdict) +{ +} diff --git a/stubs/ram-block.c b/stubs/ram-block.c index cfa5d8678f..73c0a3ee08 100644 --- a/stubs/ram-block.c +++ b/stubs/ram-block.c @@ -2,6 +2,21 @@ #include "exec/ramlist.h" #include "exec/cpu-common.h" +void *qemu_ram_get_host_addr(RAMBlock *rb) +{ + return 0; +} + +ram_addr_t qemu_ram_get_offset(RAMBlock *rb) +{ + return 0; +} + +ram_addr_t qemu_ram_get_used_length(RAMBlock *rb) +{ + return 0; +} + void ram_block_notifier_add(RAMBlockNotifier *n) { } diff --git a/stubs/ramfb.c b/stubs/ramfb.c new file mode 100644 index 0000000000..48143f3354 --- /dev/null +++ b/stubs/ramfb.c @@ -0,0 +1,13 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/display/ramfb.h" + +void ramfb_display_update(QemuConsole *con, RAMFBState *s) +{ +} + +RAMFBState *ramfb_setup(Error **errp) +{ + error_setg(errp, "ramfb support not available"); + return NULL; +} diff --git a/stubs/replay.c b/stubs/replay.c index 04279abb2c..4ac607895d 100644 --- a/stubs/replay.c +++ b/stubs/replay.c @@ -4,7 +4,7 @@ ReplayMode replay_mode; -int64_t replay_save_clock(unsigned int kind, int64_t clock) +int64_t replay_save_clock(unsigned int kind, int64_t clock, int64_t raw_icount) { abort(); return 0; diff --git a/stubs/slirp.c b/stubs/slirp.c deleted file mode 100644 index 42f7e1afd0..0000000000 --- a/stubs/slirp.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "qemu/osdep.h" -#include "qemu-common.h" -#include "qemu/host-utils.h" -#include "slirp/slirp.h" - -void slirp_pollfds_fill(GArray *pollfds, uint32_t *timeout) -{ -} - -void slirp_pollfds_poll(GArray *pollfds, int select_error) -{ -} - diff --git a/stubs/tpm.c b/stubs/tpm.c index 6729bc8517..66c99d667d 100644 --- a/stubs/tpm.c +++ b/stubs/tpm.c @@ -8,10 +8,10 @@ #include "qemu/osdep.h" #include "qapi/qapi-commands-tpm.h" #include "sysemu/tpm.h" +#include "hw/acpi/tpm.h" -int tpm_init(void) +void tpm_init(void) { - return 0; } void tpm_cleanup(void) @@ -32,3 +32,7 @@ TpmModelList *qmp_query_tpm_models(Error **errp) { return NULL; } + +void tpm_build_ppi_acpi(TPMIf *tpm, Aml *dev) +{ +} diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index b08078e7fc..1fd95d6c0f 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -201,14 +201,13 @@ static void alpha_cpu_initfn(Object *obj) CPUAlphaState *env = &cpu->env; cs->env_ptr = env; - tlb_flush(cs); env->lock_addr = -1; #if defined(CONFIG_USER_ONLY) env->flags = ENV_FLAG_PS_USER | ENV_FLAG_FEN; - cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD - | FPCR_UNFD | FPCR_INED | FPCR_DNOD - | FPCR_DYN_NORMAL)); + cpu_alpha_store_fpcr(env, (uint64_t)(FPCR_INVD | FPCR_DZED | FPCR_OVFD + | FPCR_UNFD | FPCR_INED | FPCR_DNOD + | FPCR_DYN_NORMAL) << 32); #else env->flags = ENV_FLAG_PAL_MODE | ENV_FLAG_FEN; #endif diff --git a/target/alpha/machine.c b/target/alpha/machine.c index 0914ba5fc1..abc81cef7b 100644 --- a/target/alpha/machine.c +++ b/target/alpha/machine.c @@ -5,7 +5,8 @@ #include "hw/boards.h" #include "migration/cpu.h" -static int get_fpcr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) +static int get_fpcr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) { CPUAlphaState *env = opaque; cpu_alpha_store_fpcr(env, qemu_get_be64(f)); @@ -13,7 +14,7 @@ static int get_fpcr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) } static int put_fpcr(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { CPUAlphaState *env = opaque; qemu_put_be64(f, cpu_alpha_load_fpcr(env)); diff --git a/target/alpha/translate.c b/target/alpha/translate.c index e5d62850c5..9d8f9b3eea 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -804,7 +804,7 @@ static void gen_cvttq(DisasContext *ctx, int rb, int rc, int fn11) static void gen_ieee_intcvt(DisasContext *ctx, void (*helper)(TCGv, TCGv_ptr, TCGv), - int rb, int rc, int fn11) + int rb, int rc, int fn11) { TCGv vb, vc; diff --git a/target/arm/Makefile.objs b/target/arm/Makefile.objs index 11c7baf8a3..6bdcc65c2c 100644 --- a/target/arm/Makefile.objs +++ b/target/arm/Makefile.objs @@ -5,9 +5,10 @@ obj-$(call land,$(CONFIG_KVM),$(call lnot,$(TARGET_AARCH64))) += kvm32.o obj-$(call land,$(CONFIG_KVM),$(TARGET_AARCH64)) += kvm64.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o obj-y += translate.o op_helper.o helper.o cpu.o -obj-y += neon_helper.o iwmmxt_helper.o vec_helper.o +obj-y += neon_helper.o iwmmxt_helper.o vec_helper.o vfp_helper.o obj-y += gdbstub.o obj-$(TARGET_AARCH64) += cpu64.o translate-a64.o helper-a64.o gdbstub64.o +obj-$(TARGET_AARCH64) += pauth_helper.o obj-y += crypto_helper.o obj-$(CONFIG_SOFTMMU) += arm-powerctl.o diff --git a/target/arm/arm-powerctl.c b/target/arm/arm-powerctl.c index ce55eeb682..f77a950db6 100644 --- a/target/arm/arm-powerctl.c +++ b/target/arm/arm-powerctl.c @@ -103,6 +103,16 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, } else { /* Processor is not in secure mode */ target_cpu->env.cp15.scr_el3 |= SCR_NS; + + /* + * If QEMU is providing the equivalent of EL3 firmware, then we need + * to make sure a CPU targeting EL2 comes out of reset with a + * functional HVC insn. + */ + if (arm_feature(&target_cpu->env, ARM_FEATURE_EL3) + && info->target_el == 2) { + target_cpu->env.cp15.scr_el3 |= SCR_HCE; + } } /* We check if the started CPU is now at the correct level */ @@ -110,11 +120,8 @@ static void arm_set_cpu_on_async_work(CPUState *target_cpu_state, if (info->target_aa64) { target_cpu->env.xregs[0] = info->context_id; - target_cpu->env.thumb = false; } else { target_cpu->env.regs[0] = info->context_id; - target_cpu->env.thumb = info->entry & 1; - info->entry &= 0xfffffffe; } /* Start the new CPU at the requested address */ @@ -221,6 +228,62 @@ int arm_set_cpu_on(uint64_t cpuid, uint64_t entry, uint64_t context_id, return QEMU_ARM_POWERCTL_RET_SUCCESS; } +static void arm_set_cpu_on_and_reset_async_work(CPUState *target_cpu_state, + run_on_cpu_data data) +{ + ARMCPU *target_cpu = ARM_CPU(target_cpu_state); + + /* Initialize the cpu we are turning on */ + cpu_reset(target_cpu_state); + target_cpu_state->halted = 0; + + /* Finally set the power status */ + assert(qemu_mutex_iothread_locked()); + target_cpu->power_state = PSCI_ON; +} + +int arm_set_cpu_on_and_reset(uint64_t cpuid) +{ + CPUState *target_cpu_state; + ARMCPU *target_cpu; + + assert(qemu_mutex_iothread_locked()); + + /* Retrieve the cpu we are powering up */ + target_cpu_state = arm_get_cpu_by_id(cpuid); + if (!target_cpu_state) { + /* The cpu was not found */ + return QEMU_ARM_POWERCTL_INVALID_PARAM; + } + + target_cpu = ARM_CPU(target_cpu_state); + if (target_cpu->power_state == PSCI_ON) { + qemu_log_mask(LOG_GUEST_ERROR, + "[ARM]%s: CPU %" PRId64 " is already on\n", + __func__, cpuid); + return QEMU_ARM_POWERCTL_ALREADY_ON; + } + + /* + * If another CPU has powered the target on we are in the state + * ON_PENDING and additional attempts to power on the CPU should + * fail (see 6.6 Implementation CPU_ON/CPU_OFF races in the PSCI + * spec) + */ + if (target_cpu->power_state == PSCI_ON_PENDING) { + qemu_log_mask(LOG_GUEST_ERROR, + "[ARM]%s: CPU %" PRId64 " is already powering on\n", + __func__, cpuid); + return QEMU_ARM_POWERCTL_ON_PENDING; + } + + async_run_on_cpu(target_cpu_state, arm_set_cpu_on_and_reset_async_work, + RUN_ON_CPU_NULL); + + /* We are good to go */ + return QEMU_ARM_POWERCTL_RET_SUCCESS; +} + static void arm_set_cpu_off_async_work(CPUState *target_cpu_state, run_on_cpu_data data) { diff --git a/target/arm/arm-powerctl.h b/target/arm/arm-powerctl.h index 04353923c0..37c8a04f0a 100644 --- a/target/arm/arm-powerctl.h +++ b/target/arm/arm-powerctl.h @@ -74,4 +74,20 @@ int arm_set_cpu_off(uint64_t cpuid); */ int arm_reset_cpu(uint64_t cpuid); +/* + * arm_set_cpu_on_and_reset: + * @cpuid: the id of the CPU we want to star + * + * Start the cpu designated by @cpuid and put it through its normal + * CPU reset process. The CPU will start in the way it is architected + * to start after a power-on reset. + * + * Returns: QEMU_ARM_POWERCTL_RET_SUCCESS on success. + * QEMU_ARM_POWERCTL_INVALID_PARAM if there is no CPU with that ID. + * QEMU_ARM_POWERCTL_ALREADY_ON if the CPU is already on. + * QEMU_ARM_POWERCTL_ON_PENDING if the CPU is already partway through + * powering on. + */ +int arm_set_cpu_on_and_reset(uint64_t cpuid); + #endif diff --git a/target/arm/arm-semi.c b/target/arm/arm-semi.c index 7cac8734c7..b2b22d231e 100644 --- a/target/arm/arm-semi.c +++ b/target/arm/arm-semi.c @@ -136,7 +136,7 @@ static void arm_semi_cb(CPUState *cs, target_ulong ret, target_ulong err) #ifdef CONFIG_USER_ONLY ts->swi_errno = err; #else - syscall_err = err; + syscall_err = err; #endif reg0 = ret; } else { diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index d135ff8e06..2049fa9612 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -35,6 +35,8 @@ struct arm_boot_info; #define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU +typedef struct ARMCPUInfo ARMCPUInfo; + /** * ARMCPUClass: * @parent_realize: The parent class' realize handler. @@ -47,6 +49,7 @@ typedef struct ARMCPUClass { CPUClass parent_class; /*< public >*/ + const ARMCPUInfo *info; DeviceRealize parent_realize; void (*parent_reset)(CPUState *cpu); } ARMCPUClass; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 64a8005a4b..4155782197 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -22,6 +22,7 @@ #include "target/arm/idau.h" #include "qemu/error-report.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "cpu.h" #include "internals.h" #include "qemu-common.h" @@ -40,8 +41,31 @@ static void arm_cpu_set_pc(CPUState *cs, vaddr value) { ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; - cpu->env.regs[15] = value; + if (is_a64(env)) { + env->pc = value; + env->thumb = 0; + } else { + env->regs[15] = value & ~1; + env->thumb = value & 1; + } +} + +static void arm_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + /* + * It's OK to look at env for the current mode here, because it's + * never possible for an AArch64 TB to chain to an AArch32 TB. + */ + if (is_a64(env)) { + env->pc = tb->pc; + } else { + env->regs[15] = tb->pc; + } } static bool arm_cpu_has_work(CPUState *cs) @@ -144,9 +168,9 @@ static void arm_cpu_reset(CPUState *s) 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; - env->vfp.xregs[ARM_VFP_MVFR2] = cpu->mvfr2; + env->vfp.xregs[ARM_VFP_MVFR0] = cpu->isar.mvfr0; + env->vfp.xregs[ARM_VFP_MVFR1] = cpu->isar.mvfr1; + env->vfp.xregs[ARM_VFP_MVFR2] = cpu->isar.mvfr2; cpu->power_state = cpu->start_powered_off ? PSCI_OFF : PSCI_ON; s->halted = cpu->start_powered_off; @@ -162,15 +186,27 @@ static void arm_cpu_reset(CPUState *s) env->pstate = PSTATE_MODE_EL0t; /* Userspace expects access to DC ZVA, CTL_EL0 and the cache ops */ env->cp15.sctlr_el[1] |= SCTLR_UCT | SCTLR_UCI | SCTLR_DZE; + /* Enable all PAC keys. */ + env->cp15.sctlr_el[1] |= (SCTLR_EnIA | SCTLR_EnIB | + SCTLR_EnDA | SCTLR_EnDB); + /* Enable all PAC instructions */ + env->cp15.hcr_el2 |= HCR_API; + env->cp15.scr_el3 |= SCR_API; /* and to the FP/Neon instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 20, 2, 3); /* and to the SVE instructions */ env->cp15.cpacr_el1 = deposit64(env->cp15.cpacr_el1, 16, 2, 3); env->cp15.cptr_el[3] |= CPTR_EZ; /* with maximum vector length */ - env->vfp.zcr_el[1] = ARM_MAX_VQ - 1; - env->vfp.zcr_el[2] = ARM_MAX_VQ - 1; - env->vfp.zcr_el[3] = ARM_MAX_VQ - 1; + env->vfp.zcr_el[1] = cpu->sve_max_vq - 1; + env->vfp.zcr_el[2] = env->vfp.zcr_el[1]; + env->vfp.zcr_el[3] = env->vfp.zcr_el[1]; + /* + * Enable TBI0 and TBI1. While the real kernel only enables TBI0, + * turning on both here will produce smaller code and otherwise + * make no difference to the user-level emulation. + */ + env->cp15.tcr_el[1].raw_tcr = (3ULL << 37); #else /* Reset into the highest available EL */ if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -199,8 +235,18 @@ static void arm_cpu_reset(CPUState *s) env->cp15.c15_cpar = 1; } #else - /* SVC mode with interrupts disabled. */ - env->uncached_cpsr = ARM_CPU_MODE_SVC; + + /* + * If the highest available EL is EL2, AArch32 will start in Hyp + * mode; otherwise it starts in SVC. Note that if we start in + * AArch64 then these values in the uncached_cpsr will be ignored. + */ + if (arm_feature(env, ARM_FEATURE_EL2) && + !arm_feature(env, ARM_FEATURE_EL3)) { + env->uncached_cpsr = ARM_CPU_MODE_HYP; + } else { + env->uncached_cpsr = ARM_CPU_MODE_SVC; + } env->daif = PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F; if (arm_feature(env, ARM_FEATURE_M)) { @@ -231,6 +277,10 @@ static void arm_cpu_reset(CPUState *s) env->v7m.ccr[M_REG_NS] |= R_V7M_CCR_NONBASETHRDENA_MASK; env->v7m.ccr[M_REG_S] |= R_V7M_CCR_NONBASETHRDENA_MASK; } + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.ccr[M_REG_NS] |= R_V7M_CCR_UNALIGN_TRP_MASK; + env->v7m.ccr[M_REG_S] |= R_V7M_CCR_UNALIGN_TRP_MASK; + } /* Unlike A/R profile, M profile defines the reset LR value */ env->regs[14] = 0xffffffff; @@ -422,6 +472,48 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif +void arm_cpu_update_virq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VIRQ, which is the logical OR of + * the HCR_EL2.VI bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (env->cp15.hcr_el2 & HCR_VI) || + (env->irq_line_state & CPU_INTERRUPT_VIRQ); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VIRQ) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VIRQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VIRQ); + } + } +} + +void arm_cpu_update_vfiq(ARMCPU *cpu) +{ + /* + * Update the interrupt level for VFIQ, which is the logical OR of + * the HCR_EL2.VF bit and the input line level from the GIC. + */ + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + + bool new_state = (env->cp15.hcr_el2 & HCR_VF) || + (env->irq_line_state & CPU_INTERRUPT_VFIQ); + + if (new_state != ((cs->interrupt_request & CPU_INTERRUPT_VFIQ) != 0)) { + if (new_state) { + cpu_interrupt(cs, CPU_INTERRUPT_VFIQ); + } else { + cpu_reset_interrupt(cs, CPU_INTERRUPT_VFIQ); + } + } +} + #ifndef CONFIG_USER_ONLY static void arm_cpu_set_irq(void *opaque, int irq, int level) { @@ -435,11 +527,21 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) [ARM_CPU_VFIQ] = CPU_INTERRUPT_VFIQ }; + if (level) { + env->irq_line_state |= mask[irq]; + } else { + env->irq_line_state &= ~mask[irq]; + } + switch (irq) { case ARM_CPU_VIRQ: + assert(arm_feature(env, ARM_FEATURE_EL2)); + arm_cpu_update_virq(cpu); + break; case ARM_CPU_VFIQ: assert(arm_feature(env, ARM_FEATURE_EL2)); - /* fall through */ + arm_cpu_update_vfiq(cpu); + break; case ARM_CPU_IRQ: case ARM_CPU_FIQ: if (level) { @@ -457,19 +559,30 @@ static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) { #ifdef CONFIG_KVM ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; CPUState *cs = CPU(cpu); int kvm_irq = KVM_ARM_IRQ_TYPE_CPU << KVM_ARM_IRQ_TYPE_SHIFT; + uint32_t linestate_bit; switch (irq) { case ARM_CPU_IRQ: kvm_irq |= KVM_ARM_IRQ_CPU_IRQ; + linestate_bit = CPU_INTERRUPT_HARD; break; case ARM_CPU_FIQ: kvm_irq |= KVM_ARM_IRQ_CPU_FIQ; + linestate_bit = CPU_INTERRUPT_FIQ; break; default: g_assert_not_reached(); } + + if (level) { + env->irq_line_state |= linestate_bit; + } else { + env->irq_line_state &= ~linestate_bit; + } + kvm_irq |= cs->cpu_index << KVM_ARM_IRQ_VCPU_SHIFT; kvm_set_irq(kvm_state, kvm_irq, level ? 1 : 0); #endif @@ -565,6 +678,20 @@ uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz) return (Aff1 << ARM_AFF1_SHIFT) | Aff0; } +static void cpreg_hashtable_data_destroy(gpointer data) +{ + /* + * Destroy function for cpu->cp_regs hashtable data entries. + * We must free the name string because it was g_strdup()ed in + * add_cpreg_to_hashtable(). It's OK to cast away the 'const' + * from r->name because we know we definitely allocated it. + */ + ARMCPRegInfo *r = data; + + g_free((void *)r->name); + g_free(r); +} + static void arm_cpu_initfn(Object *obj) { CPUState *cs = CPU(obj); @@ -572,7 +699,7 @@ static void arm_cpu_initfn(Object *obj) cs->env_ptr = &cpu->env; cpu->cp_regs = g_hash_table_new_full(g_int_hash, g_int_equal, - g_free, g_free); + g_free, cpreg_hashtable_data_destroy); QLIST_INIT(&cpu->pre_el_change_hooks); QLIST_INIT(&cpu->el_change_hooks); @@ -588,14 +715,6 @@ static void arm_cpu_initfn(Object *obj) qdev_init_gpio_in(DEVICE(cpu), arm_cpu_set_irq, 4); } - cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, - 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)); @@ -653,11 +772,23 @@ static Property arm_cpu_pmsav7_dregion_property = pmsav7_dregion, qdev_prop_uint32, uint32_t); -/* M profile: initial value of the Secure VTOR */ -static Property arm_cpu_initsvtor_property = - DEFINE_PROP_UINT32("init-svtor", ARMCPU, init_svtor, 0); +static void arm_get_init_svtor(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); -static void arm_cpu_post_init(Object *obj) + visit_type_uint32(v, name, &cpu->init_svtor, errp); +} + +static void arm_set_init_svtor(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + visit_type_uint32(v, name, &cpu->init_svtor, errp); +} + +void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -727,8 +858,14 @@ static void arm_cpu_post_init(Object *obj) qdev_prop_allow_set_link_before_realize, OBJ_PROP_LINK_STRONG, &error_abort); - qdev_property_add_static(DEVICE(obj), &arm_cpu_initsvtor_property, - &error_abort); + /* + * M profile: initial value of the Secure VTOR. We can't just use + * a simple DEFINE_PROP_UINT32 for this because we want to permit + * the property to be set after realize. + */ + object_property_add(obj, "init-svtor", "uint32", + arm_get_init_svtor, arm_set_init_svtor, + NULL, NULL, &error_abort); } qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property, @@ -750,6 +887,13 @@ static void arm_cpu_finalizefn(Object *obj) QLIST_REMOVE(hook, node); g_free(hook); } +#ifndef CONFIG_USER_ONLY + if (cpu->pmu_timer) { + timer_del(cpu->pmu_timer); + timer_deinit(cpu->pmu_timer); + timer_free(cpu->pmu_timer); + } +#endif } static void arm_cpu_realizefn(DeviceState *dev, Error **errp) @@ -760,6 +904,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) CPUARMState *env = &cpu->env; int pagebits; Error *local_err = NULL; + bool no_aa32 = false; /* If we needed to query the host kernel for the CPU features * then it's possible that might have failed in the initfn, but @@ -790,6 +935,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) return; } } + + cpu->gt_timer[GTIMER_PHYS] = timer_new(QEMU_CLOCK_VIRTUAL, GTIMER_SCALE, + 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); #endif cpu_exec_realizefn(cs, &local_err); @@ -800,8 +954,22 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) /* Some features automatically imply others: */ if (arm_feature(env, ARM_FEATURE_V8)) { - set_feature(env, ARM_FEATURE_V7VE); + if (arm_feature(env, ARM_FEATURE_M)) { + set_feature(env, ARM_FEATURE_V7); + } else { + set_feature(env, ARM_FEATURE_V7VE); + } } + + /* + * There exist AArch64 cpus without AArch32 support. When KVM + * queries ID_ISAR0_EL1 on such a host, the value is UNKNOWN. + * Similarly, we cannot check ID_AA64PFR0 without AArch64 support. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + no_aa32 = !cpu_isar_feature(aa64_aa32, cpu); + } + if (arm_feature(env, ARM_FEATURE_V7VE)) { /* v7 Virtualization Extensions. In real hardware this implies * EL2 and also the presence of the Security Extensions. @@ -811,7 +979,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * Presence of EL2 itself is ARM_FEATURE_EL2, and of the * Security Extensions is ARM_FEATURE_EL3. */ - set_feature(env, ARM_FEATURE_ARM_DIV); + assert(no_aa32 || cpu_isar_feature(arm_div, cpu)); set_feature(env, ARM_FEATURE_LPAE); set_feature(env, ARM_FEATURE_V7); } @@ -836,23 +1004,16 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } if (arm_feature(env, ARM_FEATURE_V6)) { set_feature(env, ARM_FEATURE_V5); - set_feature(env, ARM_FEATURE_JAZELLE); if (!arm_feature(env, ARM_FEATURE_M)) { + assert(no_aa32 || cpu_isar_feature(jazelle, cpu)); set_feature(env, ARM_FEATURE_AUXCR); } } if (arm_feature(env, ARM_FEATURE_V5)) { set_feature(env, ARM_FEATURE_V4T); } - if (arm_feature(env, ARM_FEATURE_M)) { - set_feature(env, ARM_FEATURE_THUMB_DIV); - } - if (arm_feature(env, ARM_FEATURE_ARM_DIV)) { - set_feature(env, ARM_FEATURE_THUMB_DIV); - } if (arm_feature(env, ARM_FEATURE_VFP4)) { set_feature(env, ARM_FEATURE_VFP3); - set_feature(env, ARM_FEATURE_VFP_FP16); } if (arm_feature(env, ARM_FEATURE_VFP3)) { set_feature(env, ARM_FEATURE_VFP); @@ -924,7 +1085,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * registers as well. These are id_pfr1[7:4] and id_aa64pfr0[15:12]. */ cpu->id_pfr1 &= ~0xf0; - cpu->id_aa64pfr0 &= ~0xf000; + cpu->isar.id_aa64pfr0 &= ~0xf000; } if (!cpu->has_el2) { @@ -933,7 +1094,24 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) if (!cpu->has_pmu) { unset_feature(env, ARM_FEATURE_PMU); + } + if (arm_feature(env, ARM_FEATURE_PMU)) { + pmu_init(cpu); + + if (!kvm_enabled()) { + arm_register_pre_el_change_hook(cpu, &pmu_pre_el_change, 0); + arm_register_el_change_hook(cpu, &pmu_post_el_change, 0); + } + +#ifndef CONFIG_USER_ONLY + cpu->pmu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, arm_pmu_timer_cb, + cpu); +#endif + } else { cpu->id_aa64dfr0 &= ~0xf00; + cpu->id_dfr0 &= ~(0xf << 24); + cpu->pmceid0 = 0; + cpu->pmceid1 = 0; } if (!arm_feature(env, ARM_FEATURE_EL2)) { @@ -941,7 +1119,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * registers if we don't have EL2. These are id_pfr1[15:12] and * id_aa64pfr0_el1[11:8]. */ - cpu->id_aa64pfr0 &= ~0xf00; + cpu->isar.id_aa64pfr0 &= ~0xf00; cpu->id_pfr1 &= ~0xf000; } @@ -1070,11 +1248,16 @@ static void arm926_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_VFP); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); - set_feature(&cpu->env, ARM_FEATURE_JAZELLE); cpu->midr = 0x41069265; cpu->reset_fpsid = 0x41011090; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00090078; + + /* + * ARMv5 does not have the ID_ISAR registers, but we can still + * set the field to indicate Jazelle support within QEMU. + */ + cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); } static void arm946_initfn(Object *obj) @@ -1100,12 +1283,18 @@ static void arm1026_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_AUXCR); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CACHE_TEST_CLEAN); - set_feature(&cpu->env, ARM_FEATURE_JAZELLE); cpu->midr = 0x4106a262; cpu->reset_fpsid = 0x410110a0; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00090078; cpu->reset_auxcr = 1; + + /* + * ARMv5 does not have the ID_ISAR registers, but we can still + * set the field to indicate Jazelle support within QEMU. + */ + cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + { /* The 1026 had an IFAR at c6,c0,0,1 rather than the ARMv6 c6,c0,0,2 */ ARMCPRegInfo ifar = { @@ -1137,8 +1326,8 @@ static void arm1136_r2_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); cpu->midr = 0x4107b362; cpu->reset_fpsid = 0x410120b4; - cpu->mvfr0 = 0x11111111; - cpu->mvfr1 = 0x00000000; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; cpu->id_pfr0 = 0x111; @@ -1148,11 +1337,11 @@ static void arm1136_r2_initfn(Object *obj) cpu->id_mmfr0 = 0x01130003; cpu->id_mmfr1 = 0x10030302; cpu->id_mmfr2 = 0x01222110; - cpu->id_isar0 = 0x00140011; - cpu->id_isar1 = 0x12002111; - cpu->id_isar2 = 0x11231111; - cpu->id_isar3 = 0x01102131; - cpu->id_isar4 = 0x141; + cpu->isar.id_isar0 = 0x00140011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11231111; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x141; cpu->reset_auxcr = 7; } @@ -1169,8 +1358,8 @@ static void arm1136_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CACHE_BLOCK_OPS); cpu->midr = 0x4117b363; cpu->reset_fpsid = 0x410120b4; - cpu->mvfr0 = 0x11111111; - cpu->mvfr1 = 0x00000000; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; cpu->id_pfr0 = 0x111; @@ -1180,11 +1369,11 @@ static void arm1136_initfn(Object *obj) cpu->id_mmfr0 = 0x01130003; cpu->id_mmfr1 = 0x10030302; cpu->id_mmfr2 = 0x01222110; - cpu->id_isar0 = 0x00140011; - cpu->id_isar1 = 0x12002111; - cpu->id_isar2 = 0x11231111; - cpu->id_isar3 = 0x01102131; - cpu->id_isar4 = 0x141; + cpu->isar.id_isar0 = 0x00140011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11231111; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x141; cpu->reset_auxcr = 7; } @@ -1202,8 +1391,8 @@ static void arm1176_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->midr = 0x410fb767; cpu->reset_fpsid = 0x410120b5; - cpu->mvfr0 = 0x11111111; - cpu->mvfr1 = 0x00000000; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; cpu->id_pfr0 = 0x111; @@ -1213,11 +1402,11 @@ static void arm1176_initfn(Object *obj) cpu->id_mmfr0 = 0x01130003; cpu->id_mmfr1 = 0x10030302; cpu->id_mmfr2 = 0x01222100; - cpu->id_isar0 = 0x0140011; - cpu->id_isar1 = 0x12002111; - cpu->id_isar2 = 0x11231121; - cpu->id_isar3 = 0x01102131; - cpu->id_isar4 = 0x01141; + cpu->isar.id_isar0 = 0x0140011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11231121; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x01141; cpu->reset_auxcr = 7; } @@ -1233,8 +1422,8 @@ static void arm11mpcore_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); cpu->midr = 0x410fb022; cpu->reset_fpsid = 0x410120b4; - cpu->mvfr0 = 0x11111111; - cpu->mvfr1 = 0x00000000; + cpu->isar.mvfr0 = 0x11111111; + cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ cpu->id_pfr0 = 0x111; cpu->id_pfr1 = 0x1; @@ -1243,14 +1432,23 @@ static void arm11mpcore_initfn(Object *obj) cpu->id_mmfr0 = 0x01100103; cpu->id_mmfr1 = 0x10020302; cpu->id_mmfr2 = 0x01222000; - cpu->id_isar0 = 0x00100011; - cpu->id_isar1 = 0x12002111; - cpu->id_isar2 = 0x11221011; - cpu->id_isar3 = 0x01102131; - cpu->id_isar4 = 0x141; + cpu->isar.id_isar0 = 0x00100011; + cpu->isar.id_isar1 = 0x12002111; + cpu->isar.id_isar2 = 0x11221011; + cpu->isar.id_isar3 = 0x01102131; + cpu->isar.id_isar4 = 0x141; cpu->reset_auxcr = 1; } +static void cortex_m0_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + set_feature(&cpu->env, ARM_FEATURE_V6); + set_feature(&cpu->env, ARM_FEATURE_M); + + cpu->midr = 0x410cc200; +} + static void cortex_m3_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); @@ -1267,13 +1465,13 @@ static void cortex_m3_initfn(Object *obj) cpu->id_mmfr1 = 0x00000000; cpu->id_mmfr2 = 0x00000000; cpu->id_mmfr3 = 0x00000000; - cpu->id_isar0 = 0x01141110; - cpu->id_isar1 = 0x02111000; - cpu->id_isar2 = 0x21112231; - cpu->id_isar3 = 0x01111110; - cpu->id_isar4 = 0x01310102; - cpu->id_isar5 = 0x00000000; - cpu->id_isar6 = 0x00000000; + cpu->isar.id_isar0 = 0x01141110; + cpu->isar.id_isar1 = 0x02111000; + cpu->isar.id_isar2 = 0x21112231; + cpu->isar.id_isar3 = 0x01111110; + cpu->isar.id_isar4 = 0x01310102; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; } static void cortex_m4_initfn(Object *obj) @@ -1294,13 +1492,13 @@ static void cortex_m4_initfn(Object *obj) cpu->id_mmfr1 = 0x00000000; cpu->id_mmfr2 = 0x00000000; cpu->id_mmfr3 = 0x00000000; - cpu->id_isar0 = 0x01141110; - cpu->id_isar1 = 0x02111000; - cpu->id_isar2 = 0x21112231; - cpu->id_isar3 = 0x01111110; - cpu->id_isar4 = 0x01310102; - cpu->id_isar5 = 0x00000000; - cpu->id_isar6 = 0x00000000; + cpu->isar.id_isar0 = 0x01141110; + cpu->isar.id_isar1 = 0x02111000; + cpu->isar.id_isar2 = 0x21112231; + cpu->isar.id_isar3 = 0x01111110; + cpu->isar.id_isar4 = 0x01310102; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; } static void cortex_m33_initfn(Object *obj) @@ -1323,21 +1521,23 @@ static void cortex_m33_initfn(Object *obj) cpu->id_mmfr1 = 0x00000000; cpu->id_mmfr2 = 0x01000000; cpu->id_mmfr3 = 0x00000000; - cpu->id_isar0 = 0x01101110; - cpu->id_isar1 = 0x02212000; - cpu->id_isar2 = 0x20232232; - cpu->id_isar3 = 0x01111131; - cpu->id_isar4 = 0x01310132; - cpu->id_isar5 = 0x00000000; - cpu->id_isar6 = 0x00000000; + cpu->isar.id_isar0 = 0x01101110; + cpu->isar.id_isar1 = 0x02212000; + cpu->isar.id_isar2 = 0x20232232; + cpu->isar.id_isar3 = 0x01111131; + cpu->isar.id_isar4 = 0x01310132; + cpu->isar.id_isar5 = 0x00000000; + cpu->isar.id_isar6 = 0x00000000; cpu->clidr = 0x00000000; cpu->ctr = 0x8000c000; } static void arm_v7m_class_init(ObjectClass *oc, void *data) { + ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); + acc->info = data; #ifndef CONFIG_USER_ONLY cc->do_interrupt = arm_v7m_cpu_do_interrupt; #endif @@ -1361,8 +1561,6 @@ static void cortex_r5_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); set_feature(&cpu->env, ARM_FEATURE_V7); - set_feature(&cpu->env, ARM_FEATURE_THUMB_DIV); - set_feature(&cpu->env, ARM_FEATURE_ARM_DIV); set_feature(&cpu->env, ARM_FEATURE_V7MP); set_feature(&cpu->env, ARM_FEATURE_PMSA); cpu->midr = 0x411fc153; /* r1p3 */ @@ -1374,13 +1572,13 @@ static void cortex_r5_initfn(Object *obj) cpu->id_mmfr1 = 0x00000000; cpu->id_mmfr2 = 0x01200000; cpu->id_mmfr3 = 0x0211; - cpu->id_isar0 = 0x2101111; - cpu->id_isar1 = 0x13112111; - cpu->id_isar2 = 0x21232141; - cpu->id_isar3 = 0x01112131; - cpu->id_isar4 = 0x0010142; - cpu->id_isar5 = 0x0; - cpu->id_isar6 = 0x0; + cpu->isar.id_isar0 = 0x02101111; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232141; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x0010142; + cpu->isar.id_isar5 = 0x0; + cpu->isar.id_isar6 = 0x0; cpu->mp_is_up = true; cpu->pmsav7_dregion = 16; define_arm_cp_regs(cpu, cortexr5_cp_reginfo); @@ -1415,8 +1613,8 @@ static void cortex_a8_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_EL3); cpu->midr = 0x410fc080; cpu->reset_fpsid = 0x410330c0; - cpu->mvfr0 = 0x11110222; - cpu->mvfr1 = 0x00011111; + cpu->isar.mvfr0 = 0x11110222; + cpu->isar.mvfr1 = 0x00011111; cpu->ctr = 0x82048004; cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x1031; @@ -1427,11 +1625,11 @@ static void cortex_a8_initfn(Object *obj) cpu->id_mmfr1 = 0x20000000; cpu->id_mmfr2 = 0x01202000; cpu->id_mmfr3 = 0x11; - cpu->id_isar0 = 0x00101111; - cpu->id_isar1 = 0x12112111; - cpu->id_isar2 = 0x21232031; - cpu->id_isar3 = 0x11112131; - cpu->id_isar4 = 0x00111142; + cpu->isar.id_isar0 = 0x00101111; + cpu->isar.id_isar1 = 0x12112111; + cpu->isar.id_isar2 = 0x21232031; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x00111142; cpu->dbgdidr = 0x15141000; cpu->clidr = (1 << 27) | (2 << 24) | 3; cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ @@ -1477,7 +1675,6 @@ static void cortex_a9_initfn(Object *obj) cpu->dtb_compatible = "arm,cortex-a9"; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_VFP3); - set_feature(&cpu->env, ARM_FEATURE_VFP_FP16); set_feature(&cpu->env, ARM_FEATURE_NEON); set_feature(&cpu->env, ARM_FEATURE_THUMB2EE); set_feature(&cpu->env, ARM_FEATURE_EL3); @@ -1489,8 +1686,8 @@ static void cortex_a9_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_CBAR); cpu->midr = 0x410fc090; cpu->reset_fpsid = 0x41033090; - cpu->mvfr0 = 0x11110222; - cpu->mvfr1 = 0x01111111; + cpu->isar.mvfr0 = 0x11110222; + cpu->isar.mvfr1 = 0x01111111; cpu->ctr = 0x80038003; cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x1031; @@ -1501,11 +1698,11 @@ static void cortex_a9_initfn(Object *obj) cpu->id_mmfr1 = 0x20000000; cpu->id_mmfr2 = 0x01230000; cpu->id_mmfr3 = 0x00002111; - cpu->id_isar0 = 0x00101111; - cpu->id_isar1 = 0x13112111; - cpu->id_isar2 = 0x21232041; - cpu->id_isar3 = 0x11112131; - cpu->id_isar4 = 0x00111142; + cpu->isar.id_isar0 = 0x00101111; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232041; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x00111142; cpu->dbgdidr = 0x35141000; cpu->clidr = (1 << 27) | (1 << 24) | 3; cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ @@ -1546,29 +1743,32 @@ static void cortex_a7_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A7; cpu->midr = 0x410fc075; cpu->reset_fpsid = 0x41023075; - cpu->mvfr0 = 0x10110222; - cpu->mvfr1 = 0x11111111; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x11111111; cpu->ctr = 0x84448003; cpu->reset_sctlr = 0x00c50078; cpu->id_pfr0 = 0x00001131; cpu->id_pfr1 = 0x00011011; cpu->id_dfr0 = 0x02010555; - cpu->pmceid0 = 0x00000000; - cpu->pmceid1 = 0x00000000; cpu->id_afr0 = 0x00000000; cpu->id_mmfr0 = 0x10101105; cpu->id_mmfr1 = 0x40000000; cpu->id_mmfr2 = 0x01240000; cpu->id_mmfr3 = 0x02102211; - cpu->id_isar0 = 0x01101110; - cpu->id_isar1 = 0x13112111; - cpu->id_isar2 = 0x21232041; - cpu->id_isar3 = 0x11112131; - cpu->id_isar4 = 0x10011142; + /* a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but + * table 4-41 gives 0x02101110, which includes the arm div insns. + */ + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232041; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x10011142; cpu->dbgdidr = 0x3515f005; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ @@ -1589,29 +1789,29 @@ static void cortex_a15_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_DUMMY_C15_REGS); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->kvm_target = QEMU_KVM_ARM_TARGET_CORTEX_A15; cpu->midr = 0x412fc0f1; cpu->reset_fpsid = 0x410430f0; - cpu->mvfr0 = 0x10110222; - cpu->mvfr1 = 0x11111111; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x11111111; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50078; 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; cpu->id_mmfr2 = 0x01240000; cpu->id_mmfr3 = 0x02102211; - cpu->id_isar0 = 0x02101110; - cpu->id_isar1 = 0x13112111; - cpu->id_isar2 = 0x21232041; - cpu->id_isar3 = 0x11112131; - cpu->id_isar4 = 0x10011142; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232041; + cpu->isar.id_isar3 = 0x11112131; + cpu->isar.id_isar4 = 0x10011142; cpu->dbgdidr = 0x3515f021; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ @@ -1804,17 +2004,39 @@ static void arm_max_initfn(Object *obj) cortex_a15_initfn(obj); #ifdef CONFIG_USER_ONLY /* We don't set these in system emulation mode for the moment, - * since we don't correctly set the ID registers to advertise them, + * since we don't correctly set (all of) the ID registers to + * advertise them. */ set_feature(&cpu->env, ARM_FEATURE_V8); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - 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_V8_RDM); - set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); - set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); + { + uint32_t t; + + t = cpu->isar.id_isar5; + t = FIELD_DP32(t, ID_ISAR5, AES, 2); + t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); + t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); + t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); + t = FIELD_DP32(t, ID_ISAR5, RDM, 1); + t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); + cpu->isar.id_isar5 = t; + + t = cpu->isar.id_isar6; + t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); + t = FIELD_DP32(t, ID_ISAR6, DP, 1); + t = FIELD_DP32(t, ID_ISAR6, FHM, 1); + t = FIELD_DP32(t, ID_ISAR6, SB, 1); + t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); + cpu->isar.id_isar6 = t; + + t = cpu->isar.mvfr2; + t = FIELD_DP32(t, MVFR2, SIMDMISC, 3); /* SIMD MaxNum */ + t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ + cpu->isar.mvfr2 = t; + + t = cpu->id_mmfr4; + t = FIELD_DP32(t, ID_MMFR4, HPDS, 1); /* AA32HPD */ + cpu->id_mmfr4 = t; + } #endif } } @@ -1822,11 +2044,11 @@ static void arm_max_initfn(Object *obj) #endif /* !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) */ -typedef struct ARMCPUInfo { +struct ARMCPUInfo { const char *name; void (*initfn)(Object *obj); void (*class_init)(ObjectClass *oc, void *data); -} ARMCPUInfo; +}; static const ARMCPUInfo arm_cpus[] = { #if !defined(CONFIG_USER_ONLY) || !defined(TARGET_AARCH64) @@ -1841,6 +2063,8 @@ static const ARMCPUInfo arm_cpus[] = { { .name = "arm1136", .initfn = arm1136_initfn }, { .name = "arm1176", .initfn = arm1176_initfn }, { .name = "arm11mpcore", .initfn = arm11mpcore_initfn }, + { .name = "cortex-m0", .initfn = cortex_m0_initfn, + .class_init = arm_v7m_class_init }, { .name = "cortex-m3", .initfn = cortex_m3_initfn, .class_init = arm_v7m_class_init }, { .name = "cortex-m4", .initfn = cortex_m4_initfn, @@ -1936,6 +2160,7 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) cc->cpu_exec_interrupt = arm_cpu_exec_interrupt; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; + cc->synchronize_from_tb = arm_cpu_synchronize_from_tb; cc->gdb_read_register = arm_cpu_gdb_read_register; cc->gdb_write_register = arm_cpu_gdb_write_register; #ifdef CONFIG_USER_ONLY @@ -1974,6 +2199,7 @@ static void arm_host_initfn(Object *obj) ARMCPU *cpu = ARM_CPU(obj); kvm_arm_set_cpu_features_from_host(cpu); + arm_cpu_post_init(obj); } static const TypeInfo host_arm_cpu_type_info = { @@ -1988,14 +2214,30 @@ static const TypeInfo host_arm_cpu_type_info = { #endif +static void arm_cpu_instance_init(Object *obj) +{ + ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj); + + acc->info->initfn(obj); + arm_cpu_post_init(obj); +} + +static void cpu_register_class_init(ObjectClass *oc, void *data) +{ + ARMCPUClass *acc = ARM_CPU_CLASS(oc); + + acc->info = data; +} + static void cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { .parent = TYPE_ARM_CPU, .instance_size = sizeof(ARMCPU), - .instance_init = info->initfn, + .instance_init = arm_cpu_instance_init, .class_size = sizeof(ARMCPUClass), - .class_init = info->class_init, + .class_init = info->class_init ?: cpu_register_class_init, + .class_data = (void *)info, }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); @@ -2008,7 +2250,6 @@ static const TypeInfo arm_cpu_type_info = { .parent = TYPE_CPU, .instance_size = sizeof(ARMCPU), .instance_init = arm_cpu_initfn, - .instance_post_init = arm_cpu_post_init, .instance_finalize = arm_cpu_finalizefn, .abstract = true, .class_size = sizeof(ARMCPUClass), diff --git a/target/arm/cpu.h b/target/arm/cpu.h index e310ffc29d..d4d2836923 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -56,6 +56,7 @@ #define EXCP_SEMIHOST 16 /* semihosting call */ #define EXCP_NOCP 17 /* v7M NOCP UsageFault */ #define EXCP_INVSTATE 18 /* v7M INVSTATE UsageFault */ +#define EXCP_STKOF 19 /* v8M STKOF UsageFault */ /* NB: add new EXCP_ defines to the array in arm_log_exception() too */ #define ARMV7M_EXCP_RESET 1 @@ -200,11 +201,16 @@ typedef struct ARMVectorReg { uint64_t d[2 * ARM_MAX_VQ] QEMU_ALIGNED(16); } ARMVectorReg; -/* In AArch32 mode, predicate registers do not exist at all. */ #ifdef TARGET_AARCH64 +/* In AArch32 mode, predicate registers do not exist at all. */ typedef struct ARMPredicateReg { uint64_t p[2 * ARM_MAX_VQ / 8] QEMU_ALIGNED(16); } ARMPredicateReg; + +/* In AArch32 mode, PAC keys do not exist at all. */ +typedef struct ARMPACKey { + uint64_t lo, hi; +} ARMPACKey; #endif @@ -228,6 +234,7 @@ typedef struct CPUARMState { * semantics as for AArch32, as described in the comments on each field) * nRW (also known as M[4]) is kept, inverted, in env->aarch64 * DAIF (exception masks) are kept in env->daif + * BTYPE is kept in env->btype * all other bits are stored in their correct places in env->pstate */ uint32_t pstate; @@ -257,6 +264,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]. */ + uint32_t btype; /* BTI branch type. spsr[11:10]. */ uint64_t daif; /* exception masks, in the bits they are in PSTATE */ uint64_t elr_el[4]; /* AArch64 exception link regs */ @@ -467,10 +475,23 @@ typedef struct CPUARMState { 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 + /* Stores the architectural value of the counter *the last time it was + * updated* by pmccntr_op_start. Accesses should always be surrounded + * by pmccntr_op_start/pmccntr_op_finish to guarantee the latest + * architecturally-correct value is being read/set. */ uint64_t c15_ccnt; + /* Stores the delta between the architectural value and the underlying + * cycle count during normal operation. It is used to update c15_ccnt + * to be the correct architectural value before accesses. During + * accesses, c15_ccnt_delta contains the underlying count being used + * for the access, after which it reverts to the delta value in + * pmccntr_op_finish. + */ + uint64_t c15_ccnt_delta; + uint64_t c14_pmevcntr[31]; + uint64_t c14_pmevcntr_delta[31]; + uint64_t c14_pmevtyper[31]; uint64_t pmccfiltr_el0; /* Performance Monitor Filter Register */ uint64_t vpidr_el2; /* Virtualization Processor ID Register */ uint64_t vmpidr_el2; /* Virtualization Multiprocessor ID Register */ @@ -530,6 +551,16 @@ typedef struct CPUARMState { */ } exception; + /* Information associated with an SError */ + struct { + uint8_t pending; + uint8_t has_esr; + uint64_t esr; + } serror; + + /* State of our input IRQ/FIQ/VIRQ/VFIQ lines */ + uint32_t irq_line_state; + /* Thumb-2 EE state. */ uint32_t teecr; uint32_t teehbr; @@ -546,11 +577,13 @@ typedef struct CPUARMState { ARMPredicateReg preg_tmp; #endif - uint32_t xregs[16]; /* We store these fpcsr fields separately for convenience. */ + uint32_t qc[4] QEMU_ALIGNED(16); int vec_len; int vec_stride; + uint32_t xregs[16]; + /* Scratch space for aa32 neon expansion. */ uint32_t scratch[8]; @@ -594,6 +627,14 @@ typedef struct CPUARMState { uint32_t cregs[16]; } iwmmxt; +#ifdef TARGET_AARCH64 + ARMPACKey apia_key; + ARMPACKey apib_key; + ARMPACKey apda_key; + ARMPACKey apdb_key; + ARMPACKey apga_key; +#endif + #if defined(CONFIG_USER_ONLY) /* For usermode syscall translation. */ int eabi; @@ -668,6 +709,8 @@ typedef enum ARMPSCIState { PSCI_ON_PENDING = 2 } ARMPSCIState; +typedef struct ARMISARegisters ARMISARegisters; + /** * ARMCPU: * @env: #CPUARMState @@ -707,6 +750,11 @@ struct ARMCPU { /* Timers used by the generic (architected) timer */ QEMUTimer *gt_timer[NUM_GTIMERS]; + /* + * Timer used by the PMU. Its state is restored after migration by + * pmu_op_finish() - it does not need other handling during migration + */ + QEMUTimer *pmu_timer; /* GPIO outputs for generic timer */ qemu_irq gt_timer_outputs[NUM_GTIMERS]; /* GPIO output for GICv3 maintenance interrupt signal */ @@ -787,43 +835,47 @@ struct ARMCPU { * ARMv7AR ARM Architecture Reference Manual. A reset_ prefix * is used for reset values of non-constant registers; no reset_ * prefix means a constant register. + * Some of these registers are split out into a substructure that + * is shared with the translators to control the ISA. */ + struct ARMISARegisters { + uint32_t id_isar0; + uint32_t id_isar1; + uint32_t id_isar2; + uint32_t id_isar3; + uint32_t id_isar4; + uint32_t id_isar5; + uint32_t id_isar6; + uint32_t mvfr0; + uint32_t mvfr1; + uint32_t mvfr2; + uint64_t id_aa64isar0; + uint64_t id_aa64isar1; + uint64_t id_aa64pfr0; + uint64_t id_aa64pfr1; + uint64_t id_aa64mmfr0; + uint64_t id_aa64mmfr1; + } isar; uint32_t midr; uint32_t revidr; uint32_t reset_fpsid; - uint32_t mvfr0; - uint32_t mvfr1; - uint32_t mvfr2; uint32_t ctr; uint32_t reset_sctlr; uint32_t id_pfr0; uint32_t id_pfr1; uint32_t id_dfr0; - uint32_t pmceid0; - uint32_t pmceid1; + uint64_t pmceid0; + uint64_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; - uint32_t id_isar3; - uint32_t id_isar4; - uint32_t id_isar5; - uint32_t id_isar6; - uint64_t id_aa64pfr0; - uint64_t id_aa64pfr1; uint64_t id_aa64dfr0; uint64_t id_aa64dfr1; uint64_t id_aa64afr0; uint64_t id_aa64afr1; - uint64_t id_aa64isar0; - uint64_t id_aa64isar1; - uint64_t id_aa64mmfr0; - uint64_t id_aa64mmfr1; uint32_t dbgdidr; uint32_t clidr; uint64_t mp_affinity; /* MP ID without feature bits */ @@ -857,6 +909,9 @@ struct ARMCPU { /* Used to synchronize KVM and QEMU in-kernel device levels */ uint8_t device_irq_level; + + /* Used to set the maximum vector length the cpu will support. */ + uint32_t sve_max_vq; }; static inline ARMCPU *arm_env_get_cpu(CPUARMState *env) @@ -864,6 +919,8 @@ static inline ARMCPU *arm_env_get_cpu(CPUARMState *env) return container_of(env, ARMCPU, env); } +void arm_cpu_post_init(Object *obj); + uint64_t arm_cpu_mp_affinity(int idx, uint8_t clustersz); #define ENV_GET_CPU(e) CPU(arm_env_get_cpu(e)) @@ -907,12 +964,23 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, 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_sve_narrow_vq(CPUARMState *env, unsigned vq); +void aarch64_sve_change_el(CPUARMState *env, int old_el, + int new_el, bool el0_a64); +#else +static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } +static inline void aarch64_sve_change_el(CPUARMState *env, int o, + int n, bool a) +{ } #endif target_ulong do_arm_semihosting(CPUARMState *env); void aarch64_sync_32_to_64(CPUARMState *env); void aarch64_sync_64_to_32(CPUARMState *env); +int fp_exception_el(CPUARMState *env, int cur_el); +int sve_exception_el(CPUARMState *env, int cur_el); +uint32_t sve_zcr_len_for_el(CPUARMState *env, int el); + static inline bool is_a64(CPUARMState *env) { return env->aarch64; @@ -925,15 +993,35 @@ int cpu_arm_signal_handler(int host_signum, void *pinfo, void *puc); /** - * pmccntr_sync + * pmu_op_start/finish * @env: CPUARMState * - * Synchronises the counter in the PMCCNTR. This must always be called twice, - * once before any action that might affect the timer and again afterwards. - * The function is used to swap the state of the register if required. - * This only happens when not in user mode (!CONFIG_USER_ONLY) + * Convert all PMU counters between their delta form (the typical mode when + * they are enabled) and the guest-visible values. These two calls must + * surround any action which might affect the counters. */ -void pmccntr_sync(CPUARMState *env); +void pmu_op_start(CPUARMState *env); +void pmu_op_finish(CPUARMState *env); + +/* + * Called when a PMU counter is due to overflow + */ +void arm_pmu_timer_cb(void *opaque); + +/** + * Functions to register as EL change hooks for PMU mode filtering + */ +void pmu_pre_el_change(ARMCPU *cpu, void *ignored); +void pmu_post_el_change(ARMCPU *cpu, void *ignored); + +/* + * pmu_init + * @cpu: ARMCPU + * + * Initialize the CPU's PMCEID[01]_EL0 registers and associated internal state + * for the current configuration + */ +void pmu_init(ARMCPU *cpu); /* SCTLR bit meanings. Several bits have been reused in newer * versions of the architecture; in that case we define constants @@ -945,12 +1033,15 @@ void pmccntr_sync(CPUARMState *env); #define SCTLR_A (1U << 1) #define SCTLR_C (1U << 2) #define SCTLR_W (1U << 3) /* up to v6; RAO in v7 */ -#define SCTLR_SA (1U << 3) +#define SCTLR_nTLSMD_32 (1U << 3) /* v8.2-LSMAOC, AArch32 only */ +#define SCTLR_SA (1U << 3) /* AArch64 only */ #define SCTLR_P (1U << 4) /* up to v5; RAO in v6 and v7 */ +#define SCTLR_LSMAOE_32 (1U << 4) /* v8.2-LSMAOC, AArch32 only */ #define SCTLR_SA0 (1U << 4) /* v8 onward, AArch64 only */ #define SCTLR_D (1U << 5) /* up to v5; RAO in v6 */ #define SCTLR_CP15BEN (1U << 5) /* v7 onward */ #define SCTLR_L (1U << 6) /* up to v5; RAO in v6 and v7; RAZ in v8 */ +#define SCTLR_nAA (1U << 6) /* when v8.4-LSE is implemented */ #define SCTLR_B (1U << 7) /* up to v6; RAZ in v7 */ #define SCTLR_ITD (1U << 7) /* v8 onward */ #define SCTLR_S (1U << 8) /* up to v6; RAZ in v7 */ @@ -958,35 +1049,54 @@ void pmccntr_sync(CPUARMState *env); #define SCTLR_R (1U << 9) /* up to v6; RAZ in v7 */ #define SCTLR_UMA (1U << 9) /* v8 onward, AArch64 only */ #define SCTLR_F (1U << 10) /* up to v6 */ -#define SCTLR_SW (1U << 10) /* v7 onward */ -#define SCTLR_Z (1U << 11) +#define SCTLR_SW (1U << 10) /* v7 */ +#define SCTLR_EnRCTX (1U << 10) /* in v8.0-PredInv */ +#define SCTLR_Z (1U << 11) /* in v7, RES1 in v8 */ +#define SCTLR_EOS (1U << 11) /* v8.5-ExS */ #define SCTLR_I (1U << 12) -#define SCTLR_V (1U << 13) +#define SCTLR_V (1U << 13) /* AArch32 only */ +#define SCTLR_EnDB (1U << 13) /* v8.3, AArch64 only */ #define SCTLR_RR (1U << 14) /* up to v7 */ #define SCTLR_DZE (1U << 14) /* v8 onward, AArch64 only */ #define SCTLR_L4 (1U << 15) /* up to v6; RAZ in v7 */ #define SCTLR_UCT (1U << 15) /* v8 onward, AArch64 only */ #define SCTLR_DT (1U << 16) /* up to ??, RAO in v6 and v7 */ #define SCTLR_nTWI (1U << 16) /* v8 onward */ -#define SCTLR_HA (1U << 17) +#define SCTLR_HA (1U << 17) /* up to v7, RES0 in v8 */ #define SCTLR_BR (1U << 17) /* PMSA only */ #define SCTLR_IT (1U << 18) /* up to ??, RAO in v6 and v7 */ #define SCTLR_nTWE (1U << 18) /* v8 onward */ #define SCTLR_WXN (1U << 19) #define SCTLR_ST (1U << 20) /* up to ??, RAZ in v6 */ -#define SCTLR_UWXN (1U << 20) /* v7 onward */ -#define SCTLR_FI (1U << 21) -#define SCTLR_U (1U << 22) +#define SCTLR_UWXN (1U << 20) /* v7 onward, AArch32 only */ +#define SCTLR_FI (1U << 21) /* up to v7, v8 RES0 */ +#define SCTLR_IESB (1U << 21) /* v8.2-IESB, AArch64 only */ +#define SCTLR_U (1U << 22) /* up to v6, RAO in v7 */ +#define SCTLR_EIS (1U << 22) /* v8.5-ExS */ #define SCTLR_XP (1U << 23) /* up to v6; v7 onward RAO */ +#define SCTLR_SPAN (1U << 23) /* v8.1-PAN */ #define SCTLR_VE (1U << 24) /* up to v7 */ #define SCTLR_E0E (1U << 24) /* v8 onward, AArch64 only */ #define SCTLR_EE (1U << 25) #define SCTLR_L2 (1U << 26) /* up to v6, RAZ in v7 */ #define SCTLR_UCI (1U << 26) /* v8 onward, AArch64 only */ -#define SCTLR_NMFI (1U << 27) -#define SCTLR_TRE (1U << 28) -#define SCTLR_AFE (1U << 29) -#define SCTLR_TE (1U << 30) +#define SCTLR_NMFI (1U << 27) /* up to v7, RAZ in v7VE and v8 */ +#define SCTLR_EnDA (1U << 27) /* v8.3, AArch64 only */ +#define SCTLR_TRE (1U << 28) /* AArch32 only */ +#define SCTLR_nTLSMD_64 (1U << 28) /* v8.2-LSMAOC, AArch64 only */ +#define SCTLR_AFE (1U << 29) /* AArch32 only */ +#define SCTLR_LSMAOE_64 (1U << 29) /* v8.2-LSMAOC, AArch64 only */ +#define SCTLR_TE (1U << 30) /* AArch32 only */ +#define SCTLR_EnIB (1U << 30) /* v8.3, AArch64 only */ +#define SCTLR_EnIA (1U << 31) /* v8.3, AArch64 only */ +#define SCTLR_BT0 (1ULL << 35) /* v8.5-BTI */ +#define SCTLR_BT1 (1ULL << 36) /* v8.5-BTI */ +#define SCTLR_ITFSB (1ULL << 37) /* v8.5-MemTag */ +#define SCTLR_TCF0 (3ULL << 38) /* v8.5-MemTag */ +#define SCTLR_TCF (3ULL << 40) /* v8.5-MemTag */ +#define SCTLR_ATA0 (1ULL << 42) /* v8.5-MemTag */ +#define SCTLR_ATA (1ULL << 43) /* v8.5-MemTag */ +#define SCTLR_DSSBS (1ULL << 44) /* v8.5 */ #define CPTR_TCPAC (1U << 31) #define CPTR_TTA (1U << 20) @@ -996,7 +1106,8 @@ void pmccntr_sync(CPUARMState *env); #define MDCR_EPMAD (1U << 21) #define MDCR_EDAD (1U << 20) -#define MDCR_SPME (1U << 17) +#define MDCR_SPME (1U << 17) /* MDCR_EL3 */ +#define MDCR_HPMD (1U << 17) /* MDCR_EL2 */ #define MDCR_SDD (1U << 16) #define MDCR_SPD (3U << 14) #define MDCR_TDRA (1U << 11) @@ -1006,6 +1117,7 @@ void pmccntr_sync(CPUARMState *env); #define MDCR_HPME (1U << 7) #define MDCR_TPM (1U << 6) #define MDCR_TPMCR (1U << 5) +#define MDCR_HPMN (0x1fU) /* 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) @@ -1088,6 +1200,7 @@ void pmccntr_sync(CPUARMState *env); #define PSTATE_I (1U << 7) #define PSTATE_A (1U << 8) #define PSTATE_D (1U << 9) +#define PSTATE_BTYPE (3U << 10) #define PSTATE_IL (1U << 20) #define PSTATE_SS (1U << 21) #define PSTATE_V (1U << 28) @@ -1096,7 +1209,7 @@ void pmccntr_sync(CPUARMState *env); #define PSTATE_N (1U << 31) #define PSTATE_NZCV (PSTATE_N | PSTATE_Z | PSTATE_C | PSTATE_V) #define PSTATE_DAIF (PSTATE_D | PSTATE_A | PSTATE_I | PSTATE_F) -#define CACHED_PSTATE_BITS (PSTATE_NZCV | PSTATE_DAIF) +#define CACHED_PSTATE_BITS (PSTATE_NZCV | PSTATE_DAIF | PSTATE_BTYPE) /* Mode values for AArch64 */ #define PSTATE_MODE_EL3h 13 #define PSTATE_MODE_EL3t 12 @@ -1128,7 +1241,7 @@ static inline uint32_t pstate_read(CPUARMState *env) ZF = (env->ZF == 0); return (env->NF & 0x80000000) | (ZF << 30) | (env->CF << 29) | ((env->VF & 0x80000000) >> 3) - | env->pstate | env->daif; + | env->pstate | env->daif | (env->btype << 10); } static inline void pstate_write(CPUARMState *env, uint32_t val) @@ -1138,6 +1251,7 @@ static inline void pstate_write(CPUARMState *env, uint32_t val) env->CF = (val >> 29) & 1; env->VF = (val << 3) & 0x80000000; env->daif = val & PSTATE_DAIF; + env->btype = (val >> 10) & 3; env->pstate = val & ~CACHED_PSTATE_BITS; } @@ -1218,7 +1332,7 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define HCR_TIDCP (1ULL << 20) #define HCR_TACR (1ULL << 21) #define HCR_TSW (1ULL << 22) -#define HCR_TPC (1ULL << 23) +#define HCR_TPCP (1ULL << 23) #define HCR_TPU (1ULL << 24) #define HCR_TTLB (1ULL << 25) #define HCR_TVM (1ULL << 26) @@ -1229,6 +1343,32 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define HCR_RW (1ULL << 31) #define HCR_CD (1ULL << 32) #define HCR_ID (1ULL << 33) +#define HCR_E2H (1ULL << 34) +#define HCR_TLOR (1ULL << 35) +#define HCR_TERR (1ULL << 36) +#define HCR_TEA (1ULL << 37) +#define HCR_MIOCNCE (1ULL << 38) +#define HCR_APK (1ULL << 40) +#define HCR_API (1ULL << 41) +#define HCR_NV (1ULL << 42) +#define HCR_NV1 (1ULL << 43) +#define HCR_AT (1ULL << 44) +#define HCR_NV2 (1ULL << 45) +#define HCR_FWB (1ULL << 46) +#define HCR_FIEN (1ULL << 47) +#define HCR_TID4 (1ULL << 49) +#define HCR_TICAB (1ULL << 50) +#define HCR_TOCU (1ULL << 52) +#define HCR_TTLBIS (1ULL << 54) +#define HCR_TTLBOS (1ULL << 55) +#define HCR_ATA (1ULL << 56) +#define HCR_DCT (1ULL << 57) + +/* + * When we actually implement ARMv8.1-VHE we should add HCR_E2H to + * HCR_MASK and then clear it again if the feature bit is not set in + * hcr_write(). + */ #define HCR_MASK ((1ULL << 34) - 1) #define SCR_NS (1U << 0) @@ -1245,8 +1385,16 @@ static inline void xpsr_write(CPUARMState *env, uint32_t val, uint32_t mask) #define SCR_ST (1U << 11) #define SCR_TWI (1U << 12) #define SCR_TWE (1U << 13) -#define SCR_AARCH32_MASK (0x3fff & ~(SCR_RW | SCR_ST)) -#define SCR_AARCH64_MASK (0x3fff & ~SCR_NET) +#define SCR_TLOR (1U << 14) +#define SCR_TERR (1U << 15) +#define SCR_APK (1U << 16) +#define SCR_API (1U << 17) +#define SCR_EEL2 (1U << 18) +#define SCR_EASE (1U << 19) +#define SCR_NMEA (1U << 20) +#define SCR_FIEN (1U << 21) +#define SCR_ENSCXT (1U << 25) +#define SCR_ATA (1U << 26) /* Return the current FPSCR value. */ uint32_t vfp_get_fpscr(CPUARMState *env); @@ -1260,11 +1408,18 @@ void vfp_set_fpscr(CPUARMState *env, uint32_t val); * we store the underlying state in fpscr and just mask on read/write. */ #define FPSR_MASK 0xf800009f -#define FPCR_MASK 0x07f79f00 +#define FPCR_MASK 0x07ff9f00 +#define FPCR_IOE (1 << 8) /* Invalid Operation exception trap enable */ +#define FPCR_DZE (1 << 9) /* Divide by Zero exception trap enable */ +#define FPCR_OFE (1 << 10) /* Overflow exception trap enable */ +#define FPCR_UFE (1 << 11) /* Underflow exception trap enable */ +#define FPCR_IXE (1 << 12) /* Inexact exception trap enable */ +#define FPCR_IDE (1 << 15) /* Input Denormal exception trap enable */ #define FPCR_FZ16 (1 << 19) /* ARMv8.2+, FP16 flush-to-zero */ #define FPCR_FZ (1 << 24) /* Flush-to-zero enable bit */ #define FPCR_DN (1 << 25) /* Default NaN enable bit */ +#define FPCR_QC (1 << 27) /* Cumulative saturation bit */ static inline uint32_t vfp_get_fpsr(CPUARMState *env) { @@ -1311,14 +1466,14 @@ enum arm_cpu_mode { #define ARM_VFP_FPINST2 10 /* iwMMXt coprocessor control registers. */ -#define ARM_IWMMXT_wCID 0 -#define ARM_IWMMXT_wCon 1 -#define ARM_IWMMXT_wCSSF 2 -#define ARM_IWMMXT_wCASF 3 -#define ARM_IWMMXT_wCGR0 8 -#define ARM_IWMMXT_wCGR1 9 -#define ARM_IWMMXT_wCGR2 10 -#define ARM_IWMMXT_wCGR3 11 +#define ARM_IWMMXT_wCID 0 +#define ARM_IWMMXT_wCon 1 +#define ARM_IWMMXT_wCSSF 2 +#define ARM_IWMMXT_wCASF 3 +#define ARM_IWMMXT_wCGR0 8 +#define ARM_IWMMXT_wCGR1 9 +#define ARM_IWMMXT_wCGR2 10 +#define ARM_IWMMXT_wCGR3 11 /* V7M CCR bits */ FIELD(V7M_CCR, NONBASETHRDENA, 0, 1) @@ -1327,8 +1482,10 @@ FIELD(V7M_CCR, UNALIGN_TRP, 3, 1) FIELD(V7M_CCR, DIV_0_TRP, 4, 1) FIELD(V7M_CCR, BFHFNMIGN, 8, 1) FIELD(V7M_CCR, STKALIGN, 9, 1) +FIELD(V7M_CCR, STKOFHFNMIGN, 10, 1) FIELD(V7M_CCR, DC, 16, 1) FIELD(V7M_CCR, IC, 17, 1) +FIELD(V7M_CCR, BP, 18, 1) /* V7M SCR bits */ FIELD(V7M_SCR, SLEEPONEXIT, 1, 1) @@ -1369,6 +1526,7 @@ FIELD(V7M_CFSR, UNDEFINSTR, 16 + 0, 1) FIELD(V7M_CFSR, INVSTATE, 16 + 1, 1) FIELD(V7M_CFSR, INVPC, 16 + 2, 1) FIELD(V7M_CFSR, NOCP, 16 + 3, 1) +FIELD(V7M_CFSR, STKOF, 16 + 4, 1) FIELD(V7M_CFSR, UNALIGNED, 16 + 8, 1) FIELD(V7M_CFSR, DIVBYZERO, 16 + 9, 1) @@ -1419,6 +1577,170 @@ FIELD(V7M_CSSELR, LEVEL, 1, 3) */ FIELD(V7M_CSSELR, INDEX, 0, 4) +/* + * System register ID fields. + */ +FIELD(ID_ISAR0, SWAP, 0, 4) +FIELD(ID_ISAR0, BITCOUNT, 4, 4) +FIELD(ID_ISAR0, BITFIELD, 8, 4) +FIELD(ID_ISAR0, CMPBRANCH, 12, 4) +FIELD(ID_ISAR0, COPROC, 16, 4) +FIELD(ID_ISAR0, DEBUG, 20, 4) +FIELD(ID_ISAR0, DIVIDE, 24, 4) + +FIELD(ID_ISAR1, ENDIAN, 0, 4) +FIELD(ID_ISAR1, EXCEPT, 4, 4) +FIELD(ID_ISAR1, EXCEPT_AR, 8, 4) +FIELD(ID_ISAR1, EXTEND, 12, 4) +FIELD(ID_ISAR1, IFTHEN, 16, 4) +FIELD(ID_ISAR1, IMMEDIATE, 20, 4) +FIELD(ID_ISAR1, INTERWORK, 24, 4) +FIELD(ID_ISAR1, JAZELLE, 28, 4) + +FIELD(ID_ISAR2, LOADSTORE, 0, 4) +FIELD(ID_ISAR2, MEMHINT, 4, 4) +FIELD(ID_ISAR2, MULTIACCESSINT, 8, 4) +FIELD(ID_ISAR2, MULT, 12, 4) +FIELD(ID_ISAR2, MULTS, 16, 4) +FIELD(ID_ISAR2, MULTU, 20, 4) +FIELD(ID_ISAR2, PSR_AR, 24, 4) +FIELD(ID_ISAR2, REVERSAL, 28, 4) + +FIELD(ID_ISAR3, SATURATE, 0, 4) +FIELD(ID_ISAR3, SIMD, 4, 4) +FIELD(ID_ISAR3, SVC, 8, 4) +FIELD(ID_ISAR3, SYNCHPRIM, 12, 4) +FIELD(ID_ISAR3, TABBRANCH, 16, 4) +FIELD(ID_ISAR3, T32COPY, 20, 4) +FIELD(ID_ISAR3, TRUENOP, 24, 4) +FIELD(ID_ISAR3, T32EE, 28, 4) + +FIELD(ID_ISAR4, UNPRIV, 0, 4) +FIELD(ID_ISAR4, WITHSHIFTS, 4, 4) +FIELD(ID_ISAR4, WRITEBACK, 8, 4) +FIELD(ID_ISAR4, SMC, 12, 4) +FIELD(ID_ISAR4, BARRIER, 16, 4) +FIELD(ID_ISAR4, SYNCHPRIM_FRAC, 20, 4) +FIELD(ID_ISAR4, PSR_M, 24, 4) +FIELD(ID_ISAR4, SWP_FRAC, 28, 4) + +FIELD(ID_ISAR5, SEVL, 0, 4) +FIELD(ID_ISAR5, AES, 4, 4) +FIELD(ID_ISAR5, SHA1, 8, 4) +FIELD(ID_ISAR5, SHA2, 12, 4) +FIELD(ID_ISAR5, CRC32, 16, 4) +FIELD(ID_ISAR5, RDM, 24, 4) +FIELD(ID_ISAR5, VCMA, 28, 4) + +FIELD(ID_ISAR6, JSCVT, 0, 4) +FIELD(ID_ISAR6, DP, 4, 4) +FIELD(ID_ISAR6, FHM, 8, 4) +FIELD(ID_ISAR6, SB, 12, 4) +FIELD(ID_ISAR6, SPECRES, 16, 4) + +FIELD(ID_MMFR4, SPECSEI, 0, 4) +FIELD(ID_MMFR4, AC2, 4, 4) +FIELD(ID_MMFR4, XNX, 8, 4) +FIELD(ID_MMFR4, CNP, 12, 4) +FIELD(ID_MMFR4, HPDS, 16, 4) +FIELD(ID_MMFR4, LSM, 20, 4) +FIELD(ID_MMFR4, CCIDX, 24, 4) +FIELD(ID_MMFR4, EVT, 28, 4) + +FIELD(ID_AA64ISAR0, AES, 4, 4) +FIELD(ID_AA64ISAR0, SHA1, 8, 4) +FIELD(ID_AA64ISAR0, SHA2, 12, 4) +FIELD(ID_AA64ISAR0, CRC32, 16, 4) +FIELD(ID_AA64ISAR0, ATOMIC, 20, 4) +FIELD(ID_AA64ISAR0, RDM, 28, 4) +FIELD(ID_AA64ISAR0, SHA3, 32, 4) +FIELD(ID_AA64ISAR0, SM3, 36, 4) +FIELD(ID_AA64ISAR0, SM4, 40, 4) +FIELD(ID_AA64ISAR0, DP, 44, 4) +FIELD(ID_AA64ISAR0, FHM, 48, 4) +FIELD(ID_AA64ISAR0, TS, 52, 4) +FIELD(ID_AA64ISAR0, TLB, 56, 4) +FIELD(ID_AA64ISAR0, RNDR, 60, 4) + +FIELD(ID_AA64ISAR1, DPB, 0, 4) +FIELD(ID_AA64ISAR1, APA, 4, 4) +FIELD(ID_AA64ISAR1, API, 8, 4) +FIELD(ID_AA64ISAR1, JSCVT, 12, 4) +FIELD(ID_AA64ISAR1, FCMA, 16, 4) +FIELD(ID_AA64ISAR1, LRCPC, 20, 4) +FIELD(ID_AA64ISAR1, GPA, 24, 4) +FIELD(ID_AA64ISAR1, GPI, 28, 4) +FIELD(ID_AA64ISAR1, FRINTTS, 32, 4) +FIELD(ID_AA64ISAR1, SB, 36, 4) +FIELD(ID_AA64ISAR1, SPECRES, 40, 4) + +FIELD(ID_AA64PFR0, EL0, 0, 4) +FIELD(ID_AA64PFR0, EL1, 4, 4) +FIELD(ID_AA64PFR0, EL2, 8, 4) +FIELD(ID_AA64PFR0, EL3, 12, 4) +FIELD(ID_AA64PFR0, FP, 16, 4) +FIELD(ID_AA64PFR0, ADVSIMD, 20, 4) +FIELD(ID_AA64PFR0, GIC, 24, 4) +FIELD(ID_AA64PFR0, RAS, 28, 4) +FIELD(ID_AA64PFR0, SVE, 32, 4) + +FIELD(ID_AA64PFR1, BT, 0, 4) +FIELD(ID_AA64PFR1, SBSS, 4, 4) +FIELD(ID_AA64PFR1, MTE, 8, 4) +FIELD(ID_AA64PFR1, RAS_FRAC, 12, 4) + +FIELD(ID_AA64MMFR0, PARANGE, 0, 4) +FIELD(ID_AA64MMFR0, ASIDBITS, 4, 4) +FIELD(ID_AA64MMFR0, BIGEND, 8, 4) +FIELD(ID_AA64MMFR0, SNSMEM, 12, 4) +FIELD(ID_AA64MMFR0, BIGENDEL0, 16, 4) +FIELD(ID_AA64MMFR0, TGRAN16, 20, 4) +FIELD(ID_AA64MMFR0, TGRAN64, 24, 4) +FIELD(ID_AA64MMFR0, TGRAN4, 28, 4) +FIELD(ID_AA64MMFR0, TGRAN16_2, 32, 4) +FIELD(ID_AA64MMFR0, TGRAN64_2, 36, 4) +FIELD(ID_AA64MMFR0, TGRAN4_2, 40, 4) +FIELD(ID_AA64MMFR0, EXS, 44, 4) + +FIELD(ID_AA64MMFR1, HAFDBS, 0, 4) +FIELD(ID_AA64MMFR1, VMIDBITS, 4, 4) +FIELD(ID_AA64MMFR1, VH, 8, 4) +FIELD(ID_AA64MMFR1, HPDS, 12, 4) +FIELD(ID_AA64MMFR1, LO, 16, 4) +FIELD(ID_AA64MMFR1, PAN, 20, 4) +FIELD(ID_AA64MMFR1, SPECSEI, 24, 4) +FIELD(ID_AA64MMFR1, XNX, 28, 4) + +FIELD(ID_DFR0, COPDBG, 0, 4) +FIELD(ID_DFR0, COPSDBG, 4, 4) +FIELD(ID_DFR0, MMAPDBG, 8, 4) +FIELD(ID_DFR0, COPTRC, 12, 4) +FIELD(ID_DFR0, MMAPTRC, 16, 4) +FIELD(ID_DFR0, MPROFDBG, 20, 4) +FIELD(ID_DFR0, PERFMON, 24, 4) +FIELD(ID_DFR0, TRACEFILT, 28, 4) + +FIELD(MVFR0, SIMDREG, 0, 4) +FIELD(MVFR0, FPSP, 4, 4) +FIELD(MVFR0, FPDP, 8, 4) +FIELD(MVFR0, FPTRAP, 12, 4) +FIELD(MVFR0, FPDIVIDE, 16, 4) +FIELD(MVFR0, FPSQRT, 20, 4) +FIELD(MVFR0, FPSHVEC, 24, 4) +FIELD(MVFR0, FPROUND, 28, 4) + +FIELD(MVFR1, FPFTZ, 0, 4) +FIELD(MVFR1, FPDNAN, 4, 4) +FIELD(MVFR1, SIMDLS, 8, 4) +FIELD(MVFR1, SIMDINT, 12, 4) +FIELD(MVFR1, SIMDSP, 16, 4) +FIELD(MVFR1, SIMDHP, 20, 4) +FIELD(MVFR1, FPHP, 24, 4) +FIELD(MVFR1, SIMDFMAC, 28, 4) + +FIELD(MVFR2, SIMDMISC, 0, 4) +FIELD(MVFR2, FPMISC, 4, 4) + QEMU_BUILD_BUG_ON(ARRAY_SIZE(((ARMCPU *)0)->ccsidr) <= R_V7M_CSSELR_INDEX_MASK); /* If adding a feature bit which corresponds to a Linux ELF @@ -1436,9 +1758,7 @@ enum arm_features { ARM_FEATURE_THUMB2, ARM_FEATURE_PMSA, /* no MMU; may have Memory Protection Unit */ ARM_FEATURE_VFP3, - ARM_FEATURE_VFP_FP16, ARM_FEATURE_NEON, - ARM_FEATURE_THUMB_DIV, /* divide supported in Thumb encoding */ ARM_FEATURE_M, /* Microcontroller profile. */ ARM_FEATURE_OMAPCP, /* OMAP specific CP15 ops handling. */ ARM_FEATURE_THUMB2EE, @@ -1448,7 +1768,6 @@ enum arm_features { ARM_FEATURE_V5, ARM_FEATURE_STRONGARM, ARM_FEATURE_VAPA, /* cp15 VA to PA lookups */ - ARM_FEATURE_ARM_DIV, /* divide supported in ARM encoding */ ARM_FEATURE_VFP4, /* VFPv4 (implies that NEON is v2) */ ARM_FEATURE_GENERIC_TIMER, ARM_FEATURE_MVFR, /* Media and VFP Feature Registers 0 and 1 */ @@ -1461,30 +1780,15 @@ enum arm_features { ARM_FEATURE_LPAE, /* has Large Physical Address Extension */ ARM_FEATURE_V8, ARM_FEATURE_AARCH64, /* supports 64 bit mode */ - ARM_FEATURE_V8_AES, /* implements AES part of v8 Crypto Extensions */ ARM_FEATURE_CBAR, /* has cp15 CBAR */ ARM_FEATURE_CRC, /* ARMv8 CRC instructions */ ARM_FEATURE_CBAR_RO, /* has cp15 CBAR and it is read-only */ ARM_FEATURE_EL2, /* has EL2 Virtualization support */ ARM_FEATURE_EL3, /* has EL3 Secure monitor support */ - ARM_FEATURE_V8_SHA1, /* implements SHA1 part of v8 Crypto Extensions */ - ARM_FEATURE_V8_SHA256, /* implements SHA256 part of v8 Crypto Extensions */ - ARM_FEATURE_V8_PMULL, /* implements PMULL part of v8 Crypto Extensions */ ARM_FEATURE_THUMB_DSP, /* DSP insns supported in the Thumb encodings */ ARM_FEATURE_PMU, /* has PMU support */ ARM_FEATURE_VBAR, /* has cp15 VBAR */ ARM_FEATURE_M_SECURITY, /* M profile Security Extension */ - ARM_FEATURE_JAZELLE, /* has (trivial) Jazelle implementation */ - ARM_FEATURE_SVE, /* has Scalable Vector Extension */ - ARM_FEATURE_V8_SHA512, /* implements SHA512 part of v8 Crypto Extensions */ - ARM_FEATURE_V8_SHA3, /* implements SHA3 part of v8 Crypto Extensions */ - ARM_FEATURE_V8_SM3, /* implements SM3 part of v8 Crypto Extensions */ - ARM_FEATURE_V8_SM4, /* implements SM4 part of v8 Crypto Extensions */ - ARM_FEATURE_V8_ATOMICS, /* ARMv8.1-Atomics feature */ - ARM_FEATURE_V8_RDM, /* implements v8.1 simd round multiply */ - ARM_FEATURE_V8_DOTPROD, /* implements v8.2 simd dot product */ - ARM_FEATURE_V8_FP16, /* implements v8.2 half-precision float */ - ARM_FEATURE_V8_FCMA, /* has complex number part of v8.3 extensions. */ ARM_FEATURE_M_MAIN, /* M profile Main Extension */ }; @@ -1549,6 +1853,14 @@ static inline bool arm_is_secure(CPUARMState *env) } #endif +/** + * arm_hcr_el2_eff(): Return the effective value of HCR_EL2. + * E.g. when in secure state, fields in HCR_EL2 are suppressed, + * "for all purposes other than a direct read or write access of HCR_EL2." + * Not included here is HCR_RW. + */ +uint64_t arm_hcr_el2_eff(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) { @@ -1927,6 +2239,18 @@ static inline bool cptype_valid(int cptype) #define PL0_R (0x02 | PL1_R) #define PL0_W (0x01 | PL1_W) +/* + * For user-mode some registers are accessible to EL0 via a kernel + * trap-and-emulate ABI. In this case we define the read permissions + * as actually being PL0_R. However some bits of any given register + * may still be masked. + */ +#ifdef CONFIG_USER_ONLY +#define PL0U_R PL0_R +#else +#define PL0U_R PL1_R +#endif + #define PL3_RW (PL3_R | PL3_W) #define PL2_RW (PL2_R | PL2_W) #define PL1_RW (PL1_R | PL1_W) @@ -2153,6 +2477,30 @@ static inline void define_one_arm_cp_reg(ARMCPU *cpu, const ARMCPRegInfo *regs) } const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp); +/* + * Definition of an ARM co-processor register as viewed from + * userspace. This is used for presenting sanitised versions of + * registers to userspace when emulating the Linux AArch64 CPU + * ID/feature ABI (advertised as HWCAP_CPUID). + */ +typedef struct ARMCPRegUserSpaceInfo { + /* Name of register */ + const char *name; + + /* Is the name actually a glob pattern */ + bool is_glob; + + /* Only some bits are exported to user space */ + uint64_t exported_bits; + + /* Fixed bits are applied after the mask */ + uint64_t fixed_bits; +} ARMCPRegUserSpaceInfo; + +#define REGUSERINFO_SENTINEL { .name = NULL } + +void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods); + /* CPWriteFn that can be used to implement writes-ignored behaviour */ void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); @@ -2228,7 +2576,7 @@ bool write_cpustate_to_list(ARMCPU *cpu); #if defined(TARGET_AARCH64) # define TARGET_PHYS_ADDR_SPACE_BITS 48 -# define TARGET_VIRT_ADDR_SPACE_BITS 64 +# define TARGET_VIRT_ADDR_SPACE_BITS 48 #else # define TARGET_PHYS_ADDR_SPACE_BITS 40 # define TARGET_VIRT_ADDR_SPACE_BITS 32 @@ -2242,6 +2590,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, bool secure = arm_is_secure(env); bool pstate_unmasked; int8_t unmasked = 0; + uint64_t hcr_el2; /* Don't take exceptions if they target a lower EL. * This check should catch any exceptions that would not be taken but left @@ -2251,6 +2600,8 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, return false; } + hcr_el2 = arm_hcr_el2_eff(env); + switch (excp_idx) { case EXCP_FIQ: pstate_unmasked = !(env->daif & PSTATE_F); @@ -2261,13 +2612,13 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, break; case EXCP_VFIQ: - if (secure || !(env->cp15.hcr_el2 & HCR_FMO)) { + if (secure || !(hcr_el2 & HCR_FMO) || (hcr_el2 & HCR_TGE)) { /* VFIQs are only taken when hypervized and non-secure. */ return false; } return !(env->daif & PSTATE_F); case EXCP_VIRQ: - if (secure || !(env->cp15.hcr_el2 & HCR_IMO)) { + if (secure || !(hcr_el2 & HCR_IMO) || (hcr_el2 & HCR_TGE)) { /* VIRQs are only taken when hypervized and non-secure. */ return false; } @@ -2306,7 +2657,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * to the CPSR.F setting otherwise we further assess the state * below. */ - hcr = (env->cp15.hcr_el2 & HCR_FMO); + hcr = hcr_el2 & HCR_FMO; scr = (env->cp15.scr_el3 & SCR_FIQ); /* When EL3 is 32-bit, the SCR.FW bit controls whether the @@ -2323,7 +2674,7 @@ static inline bool arm_excp_unmasked(CPUState *cs, unsigned int excp_idx, * when setting the target EL, so it does not have a further * affect here. */ - hcr = (env->cp15.hcr_el2 & HCR_IMO); + hcr = hcr_el2 & HCR_IMO; scr = false; break; default: @@ -2514,54 +2865,23 @@ static inline int arm_mmu_idx_to_el(ARMMMUIdx mmu_idx) } /* Return the MMU index for a v7M CPU in the specified security and - * privilege state + * privilege state. */ -static inline ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, - bool secstate, - bool priv) -{ - ARMMMUIdx mmu_idx = ARM_MMU_IDX_M; - - if (priv) { - mmu_idx |= ARM_MMU_IDX_M_PRIV; - } - - if (armv7m_nvic_neg_prio_requested(env->nvic, secstate)) { - mmu_idx |= ARM_MMU_IDX_M_NEGPRI; - } - - if (secstate) { - mmu_idx |= ARM_MMU_IDX_M_S; - } - - return mmu_idx; -} +ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, + bool secstate, bool priv); /* Return the MMU index for a v7M CPU in the specified security state */ -static inline ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, - bool secstate) -{ - bool priv = arm_current_el(env) != 0; +ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate); - return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv); -} - -/* Determine the current mmu_idx to use for normal loads/stores */ -static inline int cpu_mmu_index(CPUARMState *env, bool ifetch) -{ - int el = arm_current_el(env); - - if (arm_feature(env, ARM_FEATURE_M)) { - ARMMMUIdx mmu_idx = arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure); - - return arm_to_core_mmu_idx(mmu_idx); - } - - if (el < 2 && arm_is_secure_below_el3(env)) { - return arm_to_core_mmu_idx(ARMMMUIdx_S1SE0 + el); - } - return el; -} +/** + * cpu_mmu_index: + * @env: The cpu environment + * @ifetch: True for code access, false for data access. + * + * Return the core mmu index for the current translation regime. + * This function is used by generic TCG code paths. + */ +int cpu_mmu_index(CPUARMState *env, bool ifetch); /* Indexes used when registering address spaces with cpu_address_space_init */ typedef enum ARMASIdx { @@ -2577,7 +2897,7 @@ static inline int arm_debug_target_el(CPUARMState *env) if (arm_feature(env, ARM_FEATURE_EL2) && !secure) { route_to_el2 = env->cp15.hcr_el2 & HCR_TGE || - env->cp15.mdcr_el2 & (1 << 8); + env->cp15.mdcr_el2 & MDCR_TDE; } if (route_to_el2) { @@ -2598,23 +2918,35 @@ static inline bool arm_v7m_csselr_razwi(ARMCPU *cpu) return (cpu->clidr & R_V7M_CLIDR_CTYPE_ALL_MASK) != 0; } +/* See AArch64.GenerateDebugExceptionsFrom() in ARM ARM pseudocode */ 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; - } + int cur_el = arm_current_el(env); + int debug_el; + + if (cur_el == 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)) { - return false; - } + /* MDCR_EL3.SDD disables debug events from Secure state */ + if (arm_is_secure_below_el3(env) + && extract32(env->cp15.mdcr_el3, 16, 1)) { + return false; } - return true; + + /* + * Same EL to same EL debug exceptions need MDSCR_KDE enabled + * while not masking the (D)ebug bit in DAIF. + */ + debug_el = arm_debug_target_el(env); + + if (cur_el == debug_el) { + return extract32(env->cp15.mdscr_el1, 13, 1) + && !(env->daif & PSTATE_D); + } + + /* Otherwise the debug target needs to be a higher EL */ + return debug_el > cur_el; } static inline bool aa32_generate_debug_exceptions(CPUARMState *env) @@ -2667,9 +2999,6 @@ static inline bool aa32_generate_debug_exceptions(CPUARMState *env) * since the pseudocode has it at all callsites except for the one in * CheckSoftwareStep(), where it is elided because both branches would * always return the same value. - * - * Parts of the pseudocode relating to EL2 and EL3 are omitted because we - * don't yet implement those exception levels or their associated trap bits. */ static inline bool arm_generate_debug_exceptions(CPUARMState *env) { @@ -2703,11 +3032,20 @@ static inline bool arm_sctlr_b(CPUARMState *env) (env->cp15.sctlr_el[1] & SCTLR_B) != 0; } +static inline uint64_t arm_sctlr(CPUARMState *env, int el) +{ + if (el == 0) { + /* FIXME: ARMv8.1-VHE S2 translation regime. */ + return env->cp15.sctlr_el[1]; + } else { + return env->cp15.sctlr_el[el]; + } +} + + /* 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 @@ -2726,15 +3064,12 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) arm_sctlr_b(env) || #endif ((env->uncached_cpsr & CPSR_E) ? 1 : 0); + } else { + int cur_el = arm_current_el(env); + uint64_t sctlr = arm_sctlr(env, cur_el); + + return (sctlr & (cur_el ? SCTLR_EE : SCTLR_E0E)) != 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" @@ -2744,97 +3079,43 @@ static inline bool arm_cpu_data_is_big_endian(CPUARMState *env) * We put flags which are shared between 32 and 64 bit mode at the top * of the word, and flags which apply to only one mode at the bottom. */ -#define ARM_TBFLAG_AARCH64_STATE_SHIFT 31 -#define ARM_TBFLAG_AARCH64_STATE_MASK (1U << ARM_TBFLAG_AARCH64_STATE_SHIFT) -#define ARM_TBFLAG_MMUIDX_SHIFT 28 -#define ARM_TBFLAG_MMUIDX_MASK (0x7 << ARM_TBFLAG_MMUIDX_SHIFT) -#define ARM_TBFLAG_SS_ACTIVE_SHIFT 27 -#define ARM_TBFLAG_SS_ACTIVE_MASK (1 << ARM_TBFLAG_SS_ACTIVE_SHIFT) -#define ARM_TBFLAG_PSTATE_SS_SHIFT 26 -#define ARM_TBFLAG_PSTATE_SS_MASK (1 << ARM_TBFLAG_PSTATE_SS_SHIFT) +FIELD(TBFLAG_ANY, AARCH64_STATE, 31, 1) +FIELD(TBFLAG_ANY, MMUIDX, 28, 3) +FIELD(TBFLAG_ANY, SS_ACTIVE, 27, 1) +FIELD(TBFLAG_ANY, PSTATE_SS, 26, 1) /* Target EL if we take a floating-point-disabled exception */ -#define ARM_TBFLAG_FPEXC_EL_SHIFT 24 -#define ARM_TBFLAG_FPEXC_EL_MASK (0x3 << ARM_TBFLAG_FPEXC_EL_SHIFT) +FIELD(TBFLAG_ANY, FPEXC_EL, 24, 2) +FIELD(TBFLAG_ANY, BE_DATA, 23, 1) /* Bit usage when in AArch32 state: */ -#define ARM_TBFLAG_THUMB_SHIFT 0 -#define ARM_TBFLAG_THUMB_MASK (1 << ARM_TBFLAG_THUMB_SHIFT) -#define ARM_TBFLAG_VECLEN_SHIFT 1 -#define ARM_TBFLAG_VECLEN_MASK (0x7 << ARM_TBFLAG_VECLEN_SHIFT) -#define ARM_TBFLAG_VECSTRIDE_SHIFT 4 -#define ARM_TBFLAG_VECSTRIDE_MASK (0x3 << ARM_TBFLAG_VECSTRIDE_SHIFT) -#define ARM_TBFLAG_VFPEN_SHIFT 7 -#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_SCTLR_B_SHIFT 16 -#define ARM_TBFLAG_SCTLR_B_MASK (1 << ARM_TBFLAG_SCTLR_B_SHIFT) +FIELD(TBFLAG_A32, THUMB, 0, 1) +FIELD(TBFLAG_A32, VECLEN, 1, 3) +FIELD(TBFLAG_A32, VECSTRIDE, 4, 2) +FIELD(TBFLAG_A32, VFPEN, 7, 1) +FIELD(TBFLAG_A32, CONDEXEC, 8, 8) +FIELD(TBFLAG_A32, SCTLR_B, 16, 1) /* We store the bottom two bits of the CPAR as TB flags and handle * checks on the other bits at runtime */ -#define ARM_TBFLAG_XSCALE_CPAR_SHIFT 17 -#define ARM_TBFLAG_XSCALE_CPAR_MASK (3 << ARM_TBFLAG_XSCALE_CPAR_SHIFT) +FIELD(TBFLAG_A32, XSCALE_CPAR, 17, 2) /* Indicates whether cp register reads and writes by guest code should access * the secure or nonsecure bank of banked registers; note that this is not * the same thing as the current security state of the processor! */ -#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) +FIELD(TBFLAG_A32, NS, 19, 1) /* For M profile only, Handler (ie not Thread) mode */ -#define ARM_TBFLAG_HANDLER_SHIFT 21 -#define ARM_TBFLAG_HANDLER_MASK (1 << ARM_TBFLAG_HANDLER_SHIFT) +FIELD(TBFLAG_A32, HANDLER, 21, 1) +/* For M profile only, whether we should generate stack-limit checks */ +FIELD(TBFLAG_A32, STACKCHECK, 22, 1) /* Bit usage when in AArch64 state */ -#define ARM_TBFLAG_TBI0_SHIFT 0 /* TBI0 for EL0/1 or TBI for EL2/3 */ -#define ARM_TBFLAG_TBI0_MASK (0x1ull << ARM_TBFLAG_TBI0_SHIFT) -#define ARM_TBFLAG_TBI1_SHIFT 1 /* TBI1 for EL0/1 */ -#define ARM_TBFLAG_TBI1_MASK (0x1ull << ARM_TBFLAG_TBI1_SHIFT) -#define ARM_TBFLAG_SVEEXC_EL_SHIFT 2 -#define ARM_TBFLAG_SVEEXC_EL_MASK (0x3 << ARM_TBFLAG_SVEEXC_EL_SHIFT) -#define ARM_TBFLAG_ZCR_LEN_SHIFT 4 -#define ARM_TBFLAG_ZCR_LEN_MASK (0xf << ARM_TBFLAG_ZCR_LEN_SHIFT) - -/* some convenience accessor macros */ -#define ARM_TBFLAG_AARCH64_STATE(F) \ - (((F) & ARM_TBFLAG_AARCH64_STATE_MASK) >> ARM_TBFLAG_AARCH64_STATE_SHIFT) -#define ARM_TBFLAG_MMUIDX(F) \ - (((F) & ARM_TBFLAG_MMUIDX_MASK) >> ARM_TBFLAG_MMUIDX_SHIFT) -#define ARM_TBFLAG_SS_ACTIVE(F) \ - (((F) & ARM_TBFLAG_SS_ACTIVE_MASK) >> ARM_TBFLAG_SS_ACTIVE_SHIFT) -#define ARM_TBFLAG_PSTATE_SS(F) \ - (((F) & ARM_TBFLAG_PSTATE_SS_MASK) >> ARM_TBFLAG_PSTATE_SS_SHIFT) -#define ARM_TBFLAG_FPEXC_EL(F) \ - (((F) & ARM_TBFLAG_FPEXC_EL_MASK) >> ARM_TBFLAG_FPEXC_EL_SHIFT) -#define ARM_TBFLAG_THUMB(F) \ - (((F) & ARM_TBFLAG_THUMB_MASK) >> ARM_TBFLAG_THUMB_SHIFT) -#define ARM_TBFLAG_VECLEN(F) \ - (((F) & ARM_TBFLAG_VECLEN_MASK) >> ARM_TBFLAG_VECLEN_SHIFT) -#define ARM_TBFLAG_VECSTRIDE(F) \ - (((F) & ARM_TBFLAG_VECSTRIDE_MASK) >> ARM_TBFLAG_VECSTRIDE_SHIFT) -#define ARM_TBFLAG_VFPEN(F) \ - (((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_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) -#define ARM_TBFLAG_HANDLER(F) \ - (((F) & ARM_TBFLAG_HANDLER_MASK) >> ARM_TBFLAG_HANDLER_SHIFT) -#define ARM_TBFLAG_TBI0(F) \ - (((F) & ARM_TBFLAG_TBI0_MASK) >> ARM_TBFLAG_TBI0_SHIFT) -#define ARM_TBFLAG_TBI1(F) \ - (((F) & ARM_TBFLAG_TBI1_MASK) >> ARM_TBFLAG_TBI1_SHIFT) -#define ARM_TBFLAG_SVEEXC_EL(F) \ - (((F) & ARM_TBFLAG_SVEEXC_EL_MASK) >> ARM_TBFLAG_SVEEXC_EL_SHIFT) -#define ARM_TBFLAG_ZCR_LEN(F) \ - (((F) & ARM_TBFLAG_ZCR_LEN_MASK) >> ARM_TBFLAG_ZCR_LEN_SHIFT) +FIELD(TBFLAG_A64, TBII, 0, 2) +FIELD(TBFLAG_A64, SVEEXC_EL, 2, 2) +FIELD(TBFLAG_A64, ZCR_LEN, 4, 4) +FIELD(TBFLAG_A64, PAUTH_ACTIVE, 8, 1) +FIELD(TBFLAG_A64, BT, 9, 1) +FIELD(TBFLAG_A64, BTYPE, 10, 2) +FIELD(TBFLAG_A64, TBID, 12, 2) static inline bool bswap_code(bool sctlr_b) { @@ -2867,41 +3148,6 @@ static inline bool arm_cpu_bswap_data(CPUARMState *env) } #endif -#ifndef CONFIG_USER_ONLY -/** - * arm_regime_tbi0: - * @env: CPUARMState - * @mmu_idx: MMU index indicating required translation regime - * - * Extracts the TBI0 value from the appropriate TCR for the current EL - * - * Returns: the TBI0 value. - */ -uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx); - -/** - * arm_regime_tbi1: - * @env: CPUARMState - * @mmu_idx: MMU index indicating required translation regime - * - * Extracts the TBI1 value from the appropriate TCR for the current EL - * - * Returns: the TBI1 value. - */ -uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx); -#else -/* We can't handle tagged addresses properly in user-only mode */ -static inline uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - return 0; -} - -static inline uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - return 0; -} -#endif - void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags); @@ -2983,4 +3229,277 @@ static inline uint64_t *aa64_vfp_qreg(CPUARMState *env, unsigned regno) /* Shared between translate-sve.c and sve_helper.c. */ extern const uint64_t pred_esz_masks[4]; +/* + * 32-bit feature tests via id registers. + */ +static inline bool isar_feature_thumb_div(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; +} + +static inline bool isar_feature_arm_div(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) > 1; +} + +static inline bool isar_feature_jazelle(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; +} + +static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; +} + +static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; +} + +static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; +} + +static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; +} + +static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; +} + +static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; +} + +static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; +} + +static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; +} + +static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; +} + +static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; +} + +static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; +} + +static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) +{ + return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; +} + +static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) +{ + /* + * This is a placeholder for use by VCMA until the rest of + * the ARMv8.2-FP16 extension is implemented for aa32 mode. + * At which point we can properly set and check MVFR1.FPHP. + */ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; +} + +/* + * We always set the FP and SIMD FP16 fields to indicate identical + * levels of support (assuming SIMD is implemented at all), so + * we only need one set of accessors. + */ +static inline bool isar_feature_aa32_fp16_spconv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->mvfr1, MVFR1, FPHP) > 0; +} + +static inline bool isar_feature_aa32_fp16_dpconv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->mvfr1, MVFR1, FPHP) > 1; +} + +static inline bool isar_feature_aa32_vsel(const ARMISARegisters *id) +{ + return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 1; +} + +static inline bool isar_feature_aa32_vcvt_dr(const ARMISARegisters *id) +{ + return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 2; +} + +static inline bool isar_feature_aa32_vrint(const ARMISARegisters *id) +{ + return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 3; +} + +static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->mvfr2, MVFR2, FPMISC) >= 4; +} + +/* + * 64-bit feature tests via id registers. + */ +static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; +} + +static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; +} + +static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; +} + +static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; +} + +static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; +} + +static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; +} + +static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; +} + +static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; +} + +static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; +} + +static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; +} + +static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; +} + +static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; +} + +static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; +} + +static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; +} + +static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; +} + +static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; +} + +static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; +} + +static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) +{ + /* + * Note that while QEMU will only implement the architected algorithm + * QARMA, and thus APA+GPA, the host cpu for kvm may use implementation + * defined algorithms, and thus API+GPI, and this predicate controls + * migration of the 128-bit keys. + */ + return (id->id_aa64isar1 & + (FIELD_DP64(0, ID_AA64ISAR1, APA, 0xf) | + FIELD_DP64(0, ID_AA64ISAR1, API, 0xf) | + FIELD_DP64(0, ID_AA64ISAR1, GPA, 0xf) | + FIELD_DP64(0, ID_AA64ISAR1, GPI, 0xf))) != 0; +} + +static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; +} + +static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; +} + +static inline bool isar_feature_aa64_frint(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0; +} + +static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id) +{ + /* We always set the AdvSIMD and FP fields identically wrt FP16. */ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; +} + +static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; +} + +static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; +} + +static inline bool isar_feature_aa64_lor(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, LO) != 0; +} + +static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) +{ + return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; +} + +/* + * Forward to the above feature tests given an ARMCPU pointer. + */ +#define cpu_isar_feature(name, cpu) \ + ({ ARMCPU *cpu_ = (cpu); isar_feature_##name(&cpu_->isar); }) + #endif diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index d0581d59d8..228906f267 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -29,6 +29,7 @@ #include "sysemu/sysemu.h" #include "sysemu/kvm.h" #include "kvm_arm.h" +#include "qapi/visitor.h" static inline void set_feature(CPUARMState *env, int feature) { @@ -50,7 +51,7 @@ static uint64_t a57_a53_l2ctlr_read(CPUARMState *env, const ARMCPRegInfo *ri) } #endif -static const ARMCPRegInfo cortex_a57_a53_cp_reginfo[] = { +static const ARMCPRegInfo cortex_a72_a57_a53_cp_reginfo[] = { #ifndef CONFIG_USER_ONLY { .name = "L2CTLR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 1, .crn = 11, .crm = 0, .opc2 = 2, @@ -108,11 +109,6 @@ static void aarch64_a57_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - 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_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); @@ -120,9 +116,9 @@ static void aarch64_a57_initfn(Object *obj) cpu->midr = 0x411fd070; cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034070; - cpu->mvfr0 = 0x10110222; - cpu->mvfr1 = 0x12111111; - cpu->mvfr2 = 0x00000043; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50838; cpu->id_pfr0 = 0x00000131; @@ -133,19 +129,17 @@ static void aarch64_a57_initfn(Object *obj) cpu->id_mmfr1 = 0x40000000; cpu->id_mmfr2 = 0x01260000; cpu->id_mmfr3 = 0x02102211; - cpu->id_isar0 = 0x02101110; - cpu->id_isar1 = 0x13112111; - cpu->id_isar2 = 0x21232042; - cpu->id_isar3 = 0x01112131; - cpu->id_isar4 = 0x00011142; - cpu->id_isar5 = 0x00011121; - cpu->id_isar6 = 0; - cpu->id_aa64pfr0 = 0x00002222; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_isar6 = 0; + cpu->isar.id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; - cpu->pmceid0 = 0x00000000; - cpu->pmceid1 = 0x00000000; - cpu->id_aa64isar0 = 0x00011120; - cpu->id_aa64mmfr0 = 0x00001124; + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64mmfr0 = 0x00001124; cpu->dbgdidr = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ @@ -155,7 +149,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->gic_num_lrs = 4; cpu->gic_vpribits = 5; cpu->gic_vprebits = 5; - define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo); + define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo); } static void aarch64_a53_initfn(Object *obj) @@ -169,11 +163,6 @@ static void aarch64_a53_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); set_feature(&cpu->env, ARM_FEATURE_AARCH64); set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); - set_feature(&cpu->env, ARM_FEATURE_V8_AES); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA1); - 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_EL2); set_feature(&cpu->env, ARM_FEATURE_EL3); set_feature(&cpu->env, ARM_FEATURE_PMU); @@ -181,9 +170,9 @@ static void aarch64_a53_initfn(Object *obj) cpu->midr = 0x410fd034; cpu->revidr = 0x00000000; cpu->reset_fpsid = 0x41034070; - cpu->mvfr0 = 0x10110222; - cpu->mvfr1 = 0x12111111; - cpu->mvfr2 = 0x00000043; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x84448004; /* L1Ip = VIPT */ cpu->reset_sctlr = 0x00c50838; cpu->id_pfr0 = 0x00000131; @@ -194,17 +183,17 @@ static void aarch64_a53_initfn(Object *obj) cpu->id_mmfr1 = 0x40000000; cpu->id_mmfr2 = 0x01260000; cpu->id_mmfr3 = 0x02102211; - cpu->id_isar0 = 0x02101110; - cpu->id_isar1 = 0x13112111; - cpu->id_isar2 = 0x21232042; - cpu->id_isar3 = 0x01112131; - cpu->id_isar4 = 0x00011142; - cpu->id_isar5 = 0x00011121; - cpu->id_isar6 = 0; - cpu->id_aa64pfr0 = 0x00002222; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_isar6 = 0; + cpu->isar.id_aa64pfr0 = 0x00002222; cpu->id_aa64dfr0 = 0x10305106; - cpu->id_aa64isar0 = 0x00011120; - cpu->id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ cpu->dbgdidr = 0x3516d000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ @@ -214,7 +203,82 @@ static void aarch64_a53_initfn(Object *obj) cpu->gic_num_lrs = 4; cpu->gic_vpribits = 5; cpu->gic_vprebits = 5; - define_arm_cp_regs(cpu, cortex_a57_a53_cp_reginfo); + define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo); +} + +static void aarch64_a72_initfn(Object *obj) +{ + ARMCPU *cpu = ARM_CPU(obj); + + cpu->dtb_compatible = "arm,cortex-a72"; + set_feature(&cpu->env, ARM_FEATURE_V8); + set_feature(&cpu->env, ARM_FEATURE_VFP4); + set_feature(&cpu->env, ARM_FEATURE_NEON); + set_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER); + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + set_feature(&cpu->env, ARM_FEATURE_CBAR_RO); + set_feature(&cpu->env, ARM_FEATURE_EL2); + set_feature(&cpu->env, ARM_FEATURE_EL3); + set_feature(&cpu->env, ARM_FEATURE_PMU); + cpu->midr = 0x410fd083; + cpu->revidr = 0x00000000; + cpu->reset_fpsid = 0x41034080; + cpu->isar.mvfr0 = 0x10110222; + cpu->isar.mvfr1 = 0x12111111; + cpu->isar.mvfr2 = 0x00000043; + cpu->ctr = 0x8444c004; + cpu->reset_sctlr = 0x00c50838; + cpu->id_pfr0 = 0x00000131; + cpu->id_pfr1 = 0x00011011; + cpu->id_dfr0 = 0x03010066; + cpu->id_afr0 = 0x00000000; + cpu->id_mmfr0 = 0x10201105; + cpu->id_mmfr1 = 0x40000000; + cpu->id_mmfr2 = 0x01260000; + cpu->id_mmfr3 = 0x02102211; + cpu->isar.id_isar0 = 0x02101110; + cpu->isar.id_isar1 = 0x13112111; + cpu->isar.id_isar2 = 0x21232042; + cpu->isar.id_isar3 = 0x01112131; + cpu->isar.id_isar4 = 0x00011142; + cpu->isar.id_isar5 = 0x00011121; + cpu->isar.id_aa64pfr0 = 0x00002222; + cpu->id_aa64dfr0 = 0x10305106; + cpu->isar.id_aa64isar0 = 0x00011120; + cpu->isar.id_aa64mmfr0 = 0x00001124; + cpu->dbgdidr = 0x3516d000; + cpu->clidr = 0x0a200023; + cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ + cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ + cpu->ccsidr[2] = 0x707fe07a; /* 1MB L2 cache */ + cpu->dcz_blocksize = 4; /* 64 bytes */ + cpu->gic_num_lrs = 4; + cpu->gic_vpribits = 5; + cpu->gic_vprebits = 5; + define_arm_cp_regs(cpu, cortex_a72_a57_a53_cp_reginfo); +} + +static void cpu_max_get_sve_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + visit_type_uint32(v, name, &cpu->sve_max_vq, errp); +} + +static void cpu_max_set_sve_vq(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + Error *err = NULL; + + visit_type_uint32(v, name, &cpu->sve_max_vq, &err); + + if (!err && (cpu->sve_max_vq == 0 || cpu->sve_max_vq > ARM_MAX_VQ)) { + error_setg(&err, "unsupported SVE vector length"); + error_append_hint(&err, "Valid sve-max-vq in range [1-%d]\n", + ARM_MAX_VQ); + } + error_propagate(errp, err); } /* -cpu max: if KVM is enabled, like -cpu host (best possible with this host); @@ -229,42 +293,101 @@ static void aarch64_max_initfn(Object *obj) if (kvm_enabled()) { kvm_arm_set_cpu_features_from_host(cpu); } else { + uint64_t t; + uint32_t u; aarch64_a57_initfn(obj); -#ifdef CONFIG_USER_ONLY - /* We don't set these in system emulation mode for the moment, - * since we don't correctly set the ID registers to advertise them, - * and in some cases they're only available in AArch64 and not AArch32, - * whereas the architecture requires them to be present in both if - * present in either. + + t = cpu->isar.id_aa64isar0; + t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* AES + PMULL */ + t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* SHA512 */ + t = FIELD_DP64(t, ID_AA64ISAR0, CRC32, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, ATOMIC, 2); + t = FIELD_DP64(t, ID_AA64ISAR0, RDM, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, SHA3, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, DP, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 1); + t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* v8.5-CondM */ + cpu->isar.id_aa64isar0 = t; + + t = cpu->isar.id_aa64isar1; + t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, APA, 1); /* PAuth, architected only */ + t = FIELD_DP64(t, ID_AA64ISAR1, API, 0); + t = FIELD_DP64(t, ID_AA64ISAR1, GPA, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, GPI, 0); + t = FIELD_DP64(t, ID_AA64ISAR1, SB, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, SPECRES, 1); + t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 1); + cpu->isar.id_aa64isar1 = t; + + t = cpu->isar.id_aa64pfr0; + t = FIELD_DP64(t, ID_AA64PFR0, SVE, 1); + t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); + t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); + cpu->isar.id_aa64pfr0 = t; + + t = cpu->isar.id_aa64pfr1; + t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); + cpu->isar.id_aa64pfr1 = t; + + t = cpu->isar.id_aa64mmfr1; + t = FIELD_DP64(t, ID_AA64MMFR1, HPDS, 1); /* HPD */ + t = FIELD_DP64(t, ID_AA64MMFR1, LO, 1); + cpu->isar.id_aa64mmfr1 = t; + + /* Replicate the same data to the 32-bit id registers. */ + u = cpu->isar.id_isar5; + u = FIELD_DP32(u, ID_ISAR5, AES, 2); /* AES + PMULL */ + u = FIELD_DP32(u, ID_ISAR5, SHA1, 1); + u = FIELD_DP32(u, ID_ISAR5, SHA2, 1); + u = FIELD_DP32(u, ID_ISAR5, CRC32, 1); + u = FIELD_DP32(u, ID_ISAR5, RDM, 1); + u = FIELD_DP32(u, ID_ISAR5, VCMA, 1); + cpu->isar.id_isar5 = u; + + u = cpu->isar.id_isar6; + u = FIELD_DP32(u, ID_ISAR6, JSCVT, 1); + u = FIELD_DP32(u, ID_ISAR6, DP, 1); + u = FIELD_DP32(u, ID_ISAR6, FHM, 1); + u = FIELD_DP32(u, ID_ISAR6, SB, 1); + u = FIELD_DP32(u, ID_ISAR6, SPECRES, 1); + cpu->isar.id_isar6 = u; + + /* + * FIXME: We do not yet support ARMv8.2-fp16 for AArch32 yet, + * so do not set MVFR1.FPHP. Strictly speaking this is not legal, + * but it is also not legal to enable SVE without support for FP16, + * and enabling SVE in system mode is more useful in the short term. */ - set_feature(&cpu->env, ARM_FEATURE_V8_SHA512); - set_feature(&cpu->env, ARM_FEATURE_V8_SHA3); - set_feature(&cpu->env, ARM_FEATURE_V8_SM3); - set_feature(&cpu->env, ARM_FEATURE_V8_SM4); - set_feature(&cpu->env, ARM_FEATURE_V8_ATOMICS); - set_feature(&cpu->env, ARM_FEATURE_V8_RDM); - set_feature(&cpu->env, ARM_FEATURE_V8_DOTPROD); - set_feature(&cpu->env, ARM_FEATURE_V8_FP16); - set_feature(&cpu->env, ARM_FEATURE_V8_FCMA); - set_feature(&cpu->env, ARM_FEATURE_SVE); + +#ifdef CONFIG_USER_ONLY /* For usermode -cpu max we can use a larger and more efficient DCZ * blocksize since we don't have to follow what the hardware does. */ cpu->ctr = 0x80038003; /* 32 byte I and D cacheline size, VIPT icache */ cpu->dcz_blocksize = 7; /* 512 bytes */ #endif + + cpu->sve_max_vq = ARM_MAX_VQ; + object_property_add(obj, "sve-max-vq", "uint32", cpu_max_get_sve_vq, + cpu_max_set_sve_vq, NULL, NULL, &error_fatal); } } -typedef struct ARMCPUInfo { +struct ARMCPUInfo { const char *name; void (*initfn)(Object *obj); void (*class_init)(ObjectClass *oc, void *data); -} ARMCPUInfo; +}; static const ARMCPUInfo aarch64_cpus[] = { { .name = "cortex-a57", .initfn = aarch64_a57_initfn }, { .name = "cortex-a53", .initfn = aarch64_a53_initfn }, + { .name = "cortex-a72", .initfn = aarch64_a72_initfn }, { .name = "max", .initfn = aarch64_max_initfn }, { .name = NULL } }; @@ -311,20 +434,6 @@ static void aarch64_cpu_finalizefn(Object *obj) { } -static void aarch64_cpu_set_pc(CPUState *cs, vaddr value) -{ - ARMCPU *cpu = ARM_CPU(cs); - /* It's OK to look at env for the current mode here, because it's - * never possible for an AArch64 TB to chain to an AArch32 TB. - * (Otherwise we would need to use synchronize_from_tb instead.) - */ - if (is_a64(&cpu->env)) { - cpu->env.pc = value; - } else { - cpu->env.regs[15] = value; - } -} - static gchar *aarch64_gdb_arch_name(CPUState *cs) { return g_strdup("aarch64"); @@ -335,7 +444,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) CPUClass *cc = CPU_CLASS(oc); 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; @@ -343,14 +451,30 @@ static void aarch64_cpu_class_init(ObjectClass *oc, void *data) cc->gdb_arch_name = aarch64_gdb_arch_name; } +static void aarch64_cpu_instance_init(Object *obj) +{ + ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj); + + acc->info->initfn(obj); + arm_cpu_post_init(obj); +} + +static void cpu_register_class_init(ObjectClass *oc, void *data) +{ + ARMCPUClass *acc = ARM_CPU_CLASS(oc); + + acc->info = data; +} + static void aarch64_cpu_register(const ARMCPUInfo *info) { TypeInfo type_info = { .parent = TYPE_AARCH64_CPU, .instance_size = sizeof(ARMCPU), - .instance_init = info->initfn, + .instance_init = aarch64_cpu_instance_init, .class_size = sizeof(ARMCPUClass), - .class_init = info->class_init, + .class_init = info->class_init ?: cpu_register_class_init, + .class_data = (void *)info, }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); @@ -382,44 +506,3 @@ static void aarch64_cpu_register_types(void) } type_init(aarch64_cpu_register_types) - -/* The manual says that when SVE is enabled and VQ is widened the - * implementation is allowed to zero the previously inaccessible - * portion of the registers. The corollary to that is that when - * SVE is enabled and VQ is narrowed we are also allowed to zero - * the now inaccessible portion of the registers. - * - * The intent of this is that no predicate bit beyond VQ is ever set. - * Which means that some operations on predicate registers themselves - * may operate on full uint64_t or even unrolled across the maximum - * uint64_t[4]. Performing 4 bits of host arithmetic unconditionally - * may well be cheaper than conditionals to restrict the operation - * to the relevant portion of a uint16_t[16]. - * - * TODO: Need to call this for changes to the real system registers - * and EL state changes. - */ -void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) -{ - int i, j; - uint64_t pmask; - - assert(vq >= 1 && vq <= ARM_MAX_VQ); - - /* Zap the high bits of the zregs. */ - for (i = 0; i < 32; i++) { - memset(&env->vfp.zregs[i].d[2 * vq], 0, 16 * (ARM_MAX_VQ - vq)); - } - - /* Zap the high bits of the pregs and ffr. */ - pmask = 0; - if (vq & 3) { - pmask = ~(-1ULL << (16 * (vq & 3))); - } - for (j = vq / 4; j < ARM_MAX_VQ / 4; j++) { - for (i = 0; i < 17; ++i) { - env->vfp.pregs[i].p[j] &= pmask; - } - pmask = 0; - } -} diff --git a/target/arm/helper-a64.c b/target/arm/helper-a64.c index 7f6ad3000b..796ef34b55 100644 --- a/target/arm/helper-a64.c +++ b/target/arm/helper-a64.c @@ -30,6 +30,7 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "qemu/int128.h" +#include "qemu/atomic128.h" #include "tcg.h" #include "fpu/softfloat.h" #include /* For crc32 */ @@ -60,6 +61,36 @@ uint64_t HELPER(rbit64)(uint64_t x) return revbit64(x); } +void HELPER(msr_i_spsel)(CPUARMState *env, uint32_t imm) +{ + update_spsel(env, imm); +} + +static void daif_check(CPUARMState *env, uint32_t op, + uint32_t imm, uintptr_t ra) +{ + /* DAIF update to PSTATE. This is OK from EL0 only if UMA is set. */ + if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { + raise_exception_ra(env, EXCP_UDEF, + syn_aa64_sysregtrap(0, extract32(op, 0, 3), + extract32(op, 3, 3), 4, + imm, 0x1f, 0), + exception_target_el(env), ra); + } +} + +void HELPER(msr_i_daifset)(CPUARMState *env, uint32_t imm) +{ + daif_check(env, 0x1e, imm, GETPC()); + env->daif |= (imm << 6) & PSTATE_DAIF; +} + +void HELPER(msr_i_daifclear)(CPUARMState *env, uint32_t imm) +{ + daif_check(env, 0x1f, imm, GETPC()); + env->daif &= ~((imm << 6) & PSTATE_DAIF); +} + /* Convert a softfloat float_relation_ (as returned by * the float*_compare functions) to the correct ARM * NZCV flag state. @@ -509,189 +540,187 @@ uint64_t HELPER(crc32c_64)(uint64_t acc, uint64_t val, uint32_t bytes) return crc32c(acc, buf, bytes) ^ 0xffffffff; } -/* Returns 0 on success; 1 otherwise. */ -static uint64_t do_paired_cmpxchg64_le(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi, - bool parallel, uintptr_t ra) +uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr, + uint64_t new_lo, uint64_t new_hi) { - Int128 oldv, cmpv, newv; + Int128 cmpv = int128_make128(env->exclusive_val, env->exclusive_high); + Int128 newv = int128_make128(new_lo, new_hi); + Int128 oldv; + uintptr_t ra = GETPC(); + uint64_t o0, o1; bool success; - cmpv = int128_make128(env->exclusive_val, env->exclusive_high); - newv = int128_make128(new_lo, new_hi); - - if (parallel) { -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); - oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); - success = int128_eq(oldv, cmpv); -#endif - } else { - uint64_t o0, o1; - #ifdef CONFIG_USER_ONLY - /* ??? Enforce alignment. */ - uint64_t *haddr = g2h(addr); + /* ??? Enforce alignment. */ + uint64_t *haddr = g2h(addr); - helper_retaddr = ra; - o0 = ldq_le_p(haddr + 0); - o1 = ldq_le_p(haddr + 1); - oldv = int128_make128(o0, o1); + helper_retaddr = ra; + o0 = ldq_le_p(haddr + 0); + o1 = ldq_le_p(haddr + 1); + oldv = int128_make128(o0, o1); - success = int128_eq(oldv, cmpv); - if (success) { - stq_le_p(haddr + 0, int128_getlo(newv)); - stq_le_p(haddr + 1, int128_gethi(newv)); - } - helper_retaddr = 0; -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); - TCGMemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx); - - o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra); - o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - helper_le_stq_mmu(env, addr + 0, int128_getlo(newv), oi1, ra); - helper_le_stq_mmu(env, addr + 8, int128_gethi(newv), oi1, ra); - } -#endif + success = int128_eq(oldv, cmpv); + if (success) { + stq_le_p(haddr + 0, int128_getlo(newv)); + stq_le_p(haddr + 1, int128_gethi(newv)); } + helper_retaddr = 0; +#else + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi0 = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); + TCGMemOpIdx oi1 = make_memop_idx(MO_LEQ, mem_idx); + + o0 = helper_le_ldq_mmu(env, addr + 0, oi0, ra); + o1 = helper_le_ldq_mmu(env, addr + 8, oi1, ra); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + helper_le_stq_mmu(env, addr + 0, int128_getlo(newv), oi1, ra); + helper_le_stq_mmu(env, addr + 8, int128_gethi(newv), oi1, ra); + } +#endif return !success; } -uint64_t HELPER(paired_cmpxchg64_le)(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) -{ - return do_paired_cmpxchg64_le(env, addr, new_lo, new_hi, false, GETPC()); -} - uint64_t HELPER(paired_cmpxchg64_le_parallel)(CPUARMState *env, uint64_t addr, uint64_t new_lo, uint64_t new_hi) -{ - return do_paired_cmpxchg64_le(env, addr, new_lo, new_hi, true, GETPC()); -} - -static uint64_t do_paired_cmpxchg64_be(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi, - bool parallel, uintptr_t ra) { Int128 oldv, cmpv, newv; + uintptr_t ra = GETPC(); bool success; + int mem_idx; + TCGMemOpIdx oi; - /* high and low need to be switched here because this is not actually a - * 128bit store but two doublewords stored consecutively - */ - cmpv = int128_make128(env->exclusive_high, env->exclusive_val); - newv = int128_make128(new_hi, new_lo); + assert(HAVE_CMPXCHG128); - if (parallel) { -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); - oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); - success = int128_eq(oldv, cmpv); -#endif - } else { - uint64_t o0, o1; + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); -#ifdef CONFIG_USER_ONLY - /* ??? Enforce alignment. */ - uint64_t *haddr = g2h(addr); - - helper_retaddr = ra; - o1 = ldq_be_p(haddr + 0); - o0 = ldq_be_p(haddr + 1); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - stq_be_p(haddr + 0, int128_gethi(newv)); - stq_be_p(haddr + 1, int128_getlo(newv)); - } - helper_retaddr = 0; -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); - TCGMemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx); - - o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra); - o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra); - oldv = int128_make128(o0, o1); - - success = int128_eq(oldv, cmpv); - if (success) { - helper_be_stq_mmu(env, addr + 0, int128_gethi(newv), oi1, ra); - helper_be_stq_mmu(env, addr + 8, int128_getlo(newv), oi1, ra); - } -#endif - } + cmpv = int128_make128(env->exclusive_val, env->exclusive_high); + newv = int128_make128(new_lo, new_hi); + oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); + success = int128_eq(oldv, cmpv); return !success; } uint64_t HELPER(paired_cmpxchg64_be)(CPUARMState *env, uint64_t addr, uint64_t new_lo, uint64_t new_hi) { - return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, false, GETPC()); + /* + * High and low need to be switched here because this is not actually a + * 128bit store but two doublewords stored consecutively + */ + Int128 cmpv = int128_make128(env->exclusive_high, env->exclusive_val); + Int128 newv = int128_make128(new_hi, new_lo); + Int128 oldv; + uintptr_t ra = GETPC(); + uint64_t o0, o1; + bool success; + +#ifdef CONFIG_USER_ONLY + /* ??? Enforce alignment. */ + uint64_t *haddr = g2h(addr); + + helper_retaddr = ra; + o1 = ldq_be_p(haddr + 0); + o0 = ldq_be_p(haddr + 1); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + stq_be_p(haddr + 0, int128_gethi(newv)); + stq_be_p(haddr + 1, int128_getlo(newv)); + } + helper_retaddr = 0; +#else + int mem_idx = cpu_mmu_index(env, false); + TCGMemOpIdx oi0 = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); + TCGMemOpIdx oi1 = make_memop_idx(MO_BEQ, mem_idx); + + o1 = helper_be_ldq_mmu(env, addr + 0, oi0, ra); + o0 = helper_be_ldq_mmu(env, addr + 8, oi1, ra); + oldv = int128_make128(o0, o1); + + success = int128_eq(oldv, cmpv); + if (success) { + helper_be_stq_mmu(env, addr + 0, int128_gethi(newv), oi1, ra); + helper_be_stq_mmu(env, addr + 8, int128_getlo(newv), oi1, ra); + } +#endif + + return !success; } uint64_t HELPER(paired_cmpxchg64_be_parallel)(CPUARMState *env, uint64_t addr, - uint64_t new_lo, uint64_t new_hi) + uint64_t new_lo, uint64_t new_hi) { - return do_paired_cmpxchg64_be(env, addr, new_lo, new_hi, true, GETPC()); + Int128 oldv, cmpv, newv; + uintptr_t ra = GETPC(); + bool success; + int mem_idx; + TCGMemOpIdx oi; + + assert(HAVE_CMPXCHG128); + + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_BEQ | MO_ALIGN_16, mem_idx); + + /* + * High and low need to be switched here because this is not actually a + * 128bit store but two doublewords stored consecutively + */ + cmpv = int128_make128(env->exclusive_high, env->exclusive_val); + newv = int128_make128(new_hi, new_lo); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); + + success = int128_eq(oldv, cmpv); + return !success; } /* Writes back the old data into Rs. */ void HELPER(casp_le_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, uint64_t new_lo, uint64_t new_hi) { - uintptr_t ra = GETPC(); -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else Int128 oldv, cmpv, newv; + uintptr_t ra = GETPC(); + int mem_idx; + TCGMemOpIdx oi; + + assert(HAVE_CMPXCHG128); + + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); cmpv = int128_make128(env->xregs[rs], env->xregs[rs + 1]); newv = int128_make128(new_lo, new_hi); - - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); oldv = helper_atomic_cmpxchgo_le_mmu(env, addr, cmpv, newv, oi, ra); env->xregs[rs] = int128_getlo(oldv); env->xregs[rs + 1] = int128_gethi(oldv); -#endif } void HELPER(casp_be_parallel)(CPUARMState *env, uint32_t rs, uint64_t addr, uint64_t new_hi, uint64_t new_lo) { - uintptr_t ra = GETPC(); -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else Int128 oldv, cmpv, newv; + uintptr_t ra = GETPC(); + int mem_idx; + TCGMemOpIdx oi; + + assert(HAVE_CMPXCHG128); + + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); cmpv = int128_make128(env->xregs[rs + 1], env->xregs[rs]); newv = int128_make128(new_lo, new_hi); - - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_LEQ | MO_ALIGN_16, mem_idx); oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); env->xregs[rs + 1] = int128_getlo(oldv); env->xregs[rs] = int128_gethi(oldv); -#endif } /* @@ -888,6 +917,161 @@ uint32_t HELPER(advsimd_f16touinth)(uint32_t a, void *fpstp) return float16_to_uint16(a, fpst); } +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, uint64_t new_pc) +{ + 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); + + arm_clear_exclusive(env); + + /* We must squash the PSTATE.SS bit to zero unless both of the + * following hold: + * 1. debug exceptions are currently disabled + * 2. singlestep will be active in the EL we return to + * We check 1 here and 2 after we've done the pstate/cpsr write() to + * transition to the EL we're going to. + */ + if (arm_generate_debug_exceptions(env)) { + spsr &= ~PSTATE_SS; + } + + 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 && (arm_hcr_el2_eff(env) & HCR_TGE)) { + goto illegal_return; + } + + qemu_mutex_lock_iothread(); + arm_call_pre_el_change_hook(arm_env_get_cpu(env)); + qemu_mutex_unlock_iothread(); + + if (!return_to_aa64) { + env->aarch64 = 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); + + if (spsr & CPSR_T) { + env->regs[15] = new_pc & ~0x1; + } else { + env->regs[15] = new_pc & ~0x3; + } + qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " + "AArch32 EL%d PC 0x%" PRIx32 "\n", + cur_el, new_el, env->regs[15]); + } else { + env->aarch64 = 1; + pstate_write(env, spsr); + if (!arm_singlestep_active(env)) { + env->pstate &= ~PSTATE_SS; + } + aarch64_restore_sp(env, new_el); + env->pc = new_pc; + qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " + "AArch64 EL%d PC 0x%" PRIx64 "\n", + cur_el, new_el, env->pc); + } + /* + * Note that cur_el can never be 0. If new_el is 0, then + * el0_a64 is return_to_aa64, else el0_a64 is ignored. + */ + aarch64_sve_change_el(env, cur_el, new_el, return_to_aa64); + + qemu_mutex_lock_iothread(); + arm_call_el_change_hook(arm_env_get_cpu(env)); + qemu_mutex_unlock_iothread(); + + return; + +illegal_return: + /* Illegal return events of various kinds have architecturally + * mandated behaviour: + * restore NZCV and DAIF from SPSR_ELx + * set PSTATE.IL + * restore PC from ELR_ELx + * no change to exception level, execution state or stack pointer + */ + env->pstate |= PSTATE_IL; + env->pc = new_pc; + spsr &= PSTATE_NZCV | PSTATE_DAIF; + spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); + pstate_write(env, spsr); + if (!arm_singlestep_active(env)) { + env->pstate &= ~PSTATE_SS; + } + qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: " + "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); +} + /* * Square Root and Reciprocal square root */ diff --git a/target/arm/helper-a64.h b/target/arm/helper-a64.h index 9d3a907049..a915c1247f 100644 --- a/target/arm/helper-a64.h +++ b/target/arm/helper-a64.h @@ -19,6 +19,9 @@ DEF_HELPER_FLAGS_2(udiv64, TCG_CALL_NO_RWG_SE, i64, i64, i64) DEF_HELPER_FLAGS_2(sdiv64, TCG_CALL_NO_RWG_SE, s64, s64, s64) DEF_HELPER_FLAGS_1(rbit64, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_2(msr_i_spsel, void, env, i32) +DEF_HELPER_2(msr_i_daifset, void, env, i32) +DEF_HELPER_2(msr_i_daifclear, void, env, i32) DEF_HELPER_3(vfp_cmph_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmpeh_a64, i64, f16, f16, ptr) DEF_HELPER_3(vfp_cmps_a64, i64, f32, f32, ptr) @@ -85,3 +88,17 @@ DEF_HELPER_2(advsimd_rinth, f16, f16, ptr) DEF_HELPER_2(advsimd_f16tosinth, i32, f16, ptr) DEF_HELPER_2(advsimd_f16touinth, i32, f16, ptr) DEF_HELPER_2(sqrt_f16, f16, f16, ptr) + +DEF_HELPER_2(exception_return, void, env, i64) + +DEF_HELPER_FLAGS_3(pacia, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(pacib, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(pacda, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(pacdb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(pacga, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autia, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autib, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autda, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(autdb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(xpaci, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(xpacd, TCG_CALL_NO_RWG_SE, i64, env, i64) diff --git a/target/arm/helper-sve.h b/target/arm/helper-sve.h index 023952a9a4..9e79182ab4 100644 --- a/target/arm/helper-sve.h +++ b/target/arm/helper-sve.h @@ -1128,20 +1128,35 @@ DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1150,13 +1165,21 @@ DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1166,17 +1189,28 @@ DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1186,218 +1220,357 @@ DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hs_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hs_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1hd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1sd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1sd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbsu_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhsu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhsu_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldssu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhsu_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldss_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldss_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbss_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhss_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhss_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhss_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbsu_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhsu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhsu_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldssu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhsu_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldss_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldss_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbss_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhss_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhss_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldhss_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbdu_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhdu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhdu_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldsdu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhdu_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldddu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldsdu_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_lddd_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_lddd_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbds_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhds_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhds_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldsds_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhds_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbdu_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhdu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhdu_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldsdu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhdu_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldddu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldsdu_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_lddd_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_lddd_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbds_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhds_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhds_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldsds_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhds_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbdu_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhdu_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhdu_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldsdu_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhdu_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldddu_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldsdu_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsdu_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_lddd_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_lddd_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbds_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldhds_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhds_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldsds_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldhds_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldsds_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhsu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffssu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffss_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffss_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbss_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhss_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhss_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhss_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbsu_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhsu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffssu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffss_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffss_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbss_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhss_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhss_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffhss_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhdu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffsdu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffddu_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffdd_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffdd_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbds_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhds_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhds_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffsds_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhds_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbdu_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhdu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffsdu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffddu_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffdd_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffdd_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbds_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhds_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhds_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffsds_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhds_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbdu_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhdu_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffsdu_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffddu_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffdd_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffdd_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbds_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffhds_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhds_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_ldffsds_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_ldffhds_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldffsds_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_sths_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sths_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stss_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sths_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stbs_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_sths_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sths_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stss_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sths_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stss_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stbd_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_sthd_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sthd_le_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stsd_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sthd_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stdd_zsu, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_stsd_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_be_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_le_zsu, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_be_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stbd_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_sthd_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sthd_le_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stsd_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sthd_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stdd_zss, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_stsd_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_be_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_le_zss, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_be_zss, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stbd_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_sthd_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sthd_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stsd_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_sthd_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_6(sve_stdd_zd, TCG_CALL_NO_WG, +DEF_HELPER_FLAGS_6(sve_stsd_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stsd_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stdd_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) diff --git a/target/arm/helper.c b/target/arm/helper.c index 66afb08ee0..a36f4b3d69 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -15,9 +15,11 @@ #include "arm_ldst.h" #include /* For crc32 */ #include "exec/semihost.h" +#include "sysemu/cpus.h" #include "sysemu/kvm.h" #include "fpu/softfloat.h" #include "qemu/range.h" +#include "qapi/qapi-commands-target.h" #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ @@ -56,6 +58,8 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, V8M_SAttributes *sattrs); #endif +static void switch_mode(CPUARMState *env, int mode); + static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) { int nregs; @@ -78,7 +82,7 @@ static int vfp_gdb_get_reg(CPUARMState *env, uint8_t *buf, int reg) } switch (reg - nregs) { case 0: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSID]); return 4; - case 1: stl_p(buf, env->vfp.xregs[ARM_VFP_FPSCR]); return 4; + case 1: stl_p(buf, vfp_get_fpscr(env)); return 4; case 2: stl_p(buf, env->vfp.xregs[ARM_VFP_FPEXC]); return 4; } return 0; @@ -104,7 +108,7 @@ static int vfp_gdb_set_reg(CPUARMState *env, uint8_t *buf, int reg) } switch (reg - nregs) { case 0: env->vfp.xregs[ARM_VFP_FPSID] = ldl_p(buf); return 4; - case 1: env->vfp.xregs[ARM_VFP_FPSCR] = ldl_p(buf); return 4; + case 1: vfp_set_fpscr(env, ldl_p(buf)); return 4; case 2: env->vfp.xregs[ARM_VFP_FPEXC] = ldl_p(buf) & (1 << 30); return 4; } return 0; @@ -444,9 +448,11 @@ static CPAccessResult access_tdosa(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + bool mdcr_el2_tdosa = (env->cp15.mdcr_el2 & MDCR_TDOSA) || + (env->cp15.mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); - if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDOSA) - && !arm_is_secure_below_el3(env)) { + if (el < 2 && mdcr_el2_tdosa && !arm_is_secure_below_el3(env)) { return CP_ACCESS_TRAP_EL2; } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDOSA)) { @@ -462,9 +468,11 @@ static CPAccessResult access_tdra(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + bool mdcr_el2_tdra = (env->cp15.mdcr_el2 & MDCR_TDRA) || + (env->cp15.mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); - if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDRA) - && !arm_is_secure_below_el3(env)) { + if (el < 2 && mdcr_el2_tdra && !arm_is_secure_below_el3(env)) { return CP_ACCESS_TRAP_EL2; } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { @@ -480,9 +488,11 @@ static CPAccessResult access_tda(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { int el = arm_current_el(env); + bool mdcr_el2_tda = (env->cp15.mdcr_el2 & MDCR_TDA) || + (env->cp15.mdcr_el2 & MDCR_TDE) || + (arm_hcr_el2_eff(env) & HCR_TGE); - if (el < 2 && (env->cp15.mdcr_el2 & MDCR_TDA) - && !arm_is_secure_below_el3(env)) { + if (el < 2 && mdcr_el2_tda && !arm_is_secure_below_el3(env)) { return CP_ACCESS_TRAP_EL2; } if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TDA)) { @@ -546,42 +556,6 @@ static void contextidr_write(CPUARMState *env, const ARMCPRegInfo *ri, raw_write(env, ri, value); } -static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate all (TLBIALL) */ - ARMCPU *cpu = arm_env_get_cpu(env); - - tlb_flush(CPU(cpu)); -} - -static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ - ARMCPU *cpu = arm_env_get_cpu(env); - - tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); -} - -static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate by ASID (TLBIASID) */ - ARMCPU *cpu = arm_env_get_cpu(env); - - tlb_flush(CPU(cpu)); -} - -static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ - ARMCPU *cpu = arm_env_get_cpu(env); - - tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); -} - /* IS variants of TLB operations must affect all cores */ static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) @@ -615,6 +589,73 @@ static void tlbimvaa_is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_page_all_cpus_synced(cs, value & TARGET_PAGE_MASK); } +/* + * Non-IS variants of TLB operations are upgraded to + * IS versions if we are at NS EL1 and HCR_EL2.FB is set to + * force broadcast of these operations. + */ +static bool tlb_force_broadcast(CPUARMState *env) +{ + return (env->cp15.hcr_el2 & HCR_FB) && + arm_current_el(env) == 1 && arm_is_secure_below_el3(env); +} + +static void tlbiall_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate all (TLBIALL) */ + ARMCPU *cpu = arm_env_get_cpu(env); + + if (tlb_force_broadcast(env)) { + tlbiall_is_write(env, NULL, value); + return; + } + + tlb_flush(CPU(cpu)); +} + +static void tlbimva_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single TLB entry by MVA and ASID (TLBIMVA) */ + ARMCPU *cpu = arm_env_get_cpu(env); + + if (tlb_force_broadcast(env)) { + tlbimva_is_write(env, NULL, value); + return; + } + + tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); +} + +static void tlbiasid_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate by ASID (TLBIASID) */ + ARMCPU *cpu = arm_env_get_cpu(env); + + if (tlb_force_broadcast(env)) { + tlbiasid_is_write(env, NULL, value); + return; + } + + tlb_flush(CPU(cpu)); +} + +static void tlbimvaa_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Invalidate single entry by MVA, all ASIDs (TLBIMVAA) */ + ARMCPU *cpu = arm_env_get_cpu(env); + + if (tlb_force_broadcast(env)) { + tlbimvaa_is_write(env, NULL, value); + return; + } + + tlb_flush_page(CPU(cpu), value & TARGET_PAGE_MASK); +} + static void tlbiall_nsnh_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -937,10 +978,30 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { /* Definitions for the PMU registers */ #define PMCRN_MASK 0xf800 #define PMCRN_SHIFT 11 +#define PMCRLC 0x40 +#define PMCRDP 0x10 #define PMCRD 0x8 #define PMCRC 0x4 +#define PMCRP 0x2 #define PMCRE 0x1 +#define PMXEVTYPER_P 0x80000000 +#define PMXEVTYPER_U 0x40000000 +#define PMXEVTYPER_NSK 0x20000000 +#define PMXEVTYPER_NSU 0x10000000 +#define PMXEVTYPER_NSH 0x08000000 +#define PMXEVTYPER_M 0x04000000 +#define PMXEVTYPER_MT 0x02000000 +#define PMXEVTYPER_EVTCOUNT 0x0000ffff +#define PMXEVTYPER_MASK (PMXEVTYPER_P | PMXEVTYPER_U | PMXEVTYPER_NSK | \ + PMXEVTYPER_NSU | PMXEVTYPER_NSH | \ + PMXEVTYPER_M | PMXEVTYPER_MT | \ + PMXEVTYPER_EVTCOUNT) + +#define PMCCFILTR 0xf8000000 +#define PMCCFILTR_M PMXEVTYPER_M +#define PMCCFILTR_EL0 (PMCCFILTR | PMCCFILTR_M) + static inline uint32_t pmu_num_counters(CPUARMState *env) { return (env->cp15.c9_pmcr & PMCRN_MASK) >> PMCRN_SHIFT; @@ -952,6 +1013,158 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) return (1 << 31) | ((1 << pmu_num_counters(env)) - 1); } +typedef struct pm_event { + uint16_t number; /* PMEVTYPER.evtCount is 16 bits wide */ + /* If the event is supported on this CPU (used to generate PMCEID[01]) */ + bool (*supported)(CPUARMState *); + /* + * Retrieve the current count of the underlying event. The programmed + * counters hold a difference from the return value from this function + */ + uint64_t (*get_count)(CPUARMState *); + /* + * Return how many nanoseconds it will take (at a minimum) for count events + * to occur. A negative value indicates the counter will never overflow, or + * that the counter has otherwise arranged for the overflow bit to be set + * and the PMU interrupt to be raised on overflow. + */ + int64_t (*ns_per_count)(uint64_t); +} pm_event; + +static bool event_always_supported(CPUARMState *env) +{ + return true; +} + +static uint64_t swinc_get_count(CPUARMState *env) +{ + /* + * SW_INCR events are written directly to the pmevcntr's by writes to + * PMSWINC, so there is no underlying count maintained by the PMU itself + */ + return 0; +} + +static int64_t swinc_ns_per(uint64_t ignored) +{ + return -1; +} + +/* + * Return the underlying cycle count for the PMU cycle counters. If we're in + * usermode, simply return 0. + */ +static uint64_t cycles_get_count(CPUARMState *env) +{ +#ifndef CONFIG_USER_ONLY + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); +#else + return cpu_get_host_ticks(); +#endif +} + +#ifndef CONFIG_USER_ONLY +static int64_t cycles_ns_per(uint64_t cycles) +{ + return (ARM_CPU_FREQ / NANOSECONDS_PER_SECOND) * cycles; +} + +static bool instructions_supported(CPUARMState *env) +{ + return use_icount == 1 /* Precise instruction counting */; +} + +static uint64_t instructions_get_count(CPUARMState *env) +{ + return (uint64_t)cpu_get_icount_raw(); +} + +static int64_t instructions_ns_per(uint64_t icount) +{ + return cpu_icount_to_ns((int64_t)icount); +} +#endif + +static const pm_event pm_events[] = { + { .number = 0x000, /* SW_INCR */ + .supported = event_always_supported, + .get_count = swinc_get_count, + .ns_per_count = swinc_ns_per, + }, +#ifndef CONFIG_USER_ONLY + { .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */ + .supported = instructions_supported, + .get_count = instructions_get_count, + .ns_per_count = instructions_ns_per, + }, + { .number = 0x011, /* CPU_CYCLES, Cycle */ + .supported = event_always_supported, + .get_count = cycles_get_count, + .ns_per_count = cycles_ns_per, + } +#endif +}; + +/* + * Note: Before increasing MAX_EVENT_ID beyond 0x3f into the 0x40xx range of + * events (i.e. the statistical profiling extension), this implementation + * should first be updated to something sparse instead of the current + * supported_event_map[] array. + */ +#define MAX_EVENT_ID 0x11 +#define UNSUPPORTED_EVENT UINT16_MAX +static uint16_t supported_event_map[MAX_EVENT_ID + 1]; + +/* + * Called upon CPU initialization to initialize PMCEID[01]_EL0 and build a map + * of ARM event numbers to indices in our pm_events array. + * + * Note: Events in the 0x40XX range are not currently supported. + */ +void pmu_init(ARMCPU *cpu) +{ + unsigned int i; + + /* + * Empty supported_event_map and cpu->pmceid[01] before adding supported + * events to them + */ + for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) { + supported_event_map[i] = UNSUPPORTED_EVENT; + } + cpu->pmceid0 = 0; + cpu->pmceid1 = 0; + + for (i = 0; i < ARRAY_SIZE(pm_events); i++) { + const pm_event *cnt = &pm_events[i]; + assert(cnt->number <= MAX_EVENT_ID); + /* We do not currently support events in the 0x40xx range */ + assert(cnt->number <= 0x3f); + + if (cnt->supported(&cpu->env)) { + supported_event_map[cnt->number] = i; + uint64_t event_mask = 1ULL << (cnt->number & 0x1f); + if (cnt->number & 0x20) { + cpu->pmceid1 |= event_mask; + } else { + cpu->pmceid0 |= event_mask; + } + } + } +} + +/* + * Check at runtime whether a PMU event is supported for the current machine + */ +static bool event_supported(uint16_t number) +{ + if (number > MAX_EVENT_ID) { + return false; + } + return supported_event_map[number] != UNSUPPORTED_EVENT; +} + static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -1005,8 +1218,6 @@ static CPAccessResult pmreg_access_swinc(CPUARMState *env, return pmreg_access(env, ri, isread); } -#ifndef CONFIG_USER_ONLY - static CPAccessResult pmreg_access_selr(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -1036,68 +1247,303 @@ static CPAccessResult pmreg_access_ccntr(CPUARMState *env, return pmreg_access(env, ri, isread); } -static inline bool arm_ccnt_enabled(CPUARMState *env) +/* Returns true if the counter (pass 31 for PMCCNTR) should count events using + * the current EL, security state, and register configuration. + */ +static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) { - /* This does not support checking PMCCFILTR_EL0 register */ + uint64_t filter; + bool e, p, u, nsk, nsu, nsh, m; + bool enabled, prohibited, filtered; + bool secure = arm_is_secure(env); + int el = arm_current_el(env); + uint8_t hpmn = env->cp15.mdcr_el2 & MDCR_HPMN; - if (!(env->cp15.c9_pmcr & PMCRE) || !(env->cp15.c9_pmcnten & (1 << 31))) { + if (!arm_feature(env, ARM_FEATURE_PMU)) { return false; } - return true; + if (!arm_feature(env, ARM_FEATURE_EL2) || + (counter < hpmn || counter == 31)) { + e = env->cp15.c9_pmcr & PMCRE; + } else { + e = env->cp15.mdcr_el2 & MDCR_HPME; + } + enabled = e && (env->cp15.c9_pmcnten & (1 << counter)); + + if (!secure) { + if (el == 2 && (counter < hpmn || counter == 31)) { + prohibited = env->cp15.mdcr_el2 & MDCR_HPMD; + } else { + prohibited = false; + } + } else { + prohibited = arm_feature(env, ARM_FEATURE_EL3) && + (env->cp15.mdcr_el3 & MDCR_SPME); + } + + if (prohibited && counter == 31) { + prohibited = env->cp15.c9_pmcr & PMCRDP; + } + + if (counter == 31) { + filter = env->cp15.pmccfiltr_el0; + } else { + filter = env->cp15.c14_pmevtyper[counter]; + } + + p = filter & PMXEVTYPER_P; + u = filter & PMXEVTYPER_U; + nsk = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSK); + nsu = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSU); + nsh = arm_feature(env, ARM_FEATURE_EL2) && (filter & PMXEVTYPER_NSH); + m = arm_el_is_aa64(env, 1) && + arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_M); + + if (el == 0) { + filtered = secure ? u : u != nsu; + } else if (el == 1) { + filtered = secure ? p : p != nsk; + } else if (el == 2) { + filtered = !nsh; + } else { /* EL3 */ + filtered = m != p; + } + + if (counter != 31) { + /* + * If not checking PMCCNTR, ensure the counter is setup to an event we + * support + */ + uint16_t event = filter & PMXEVTYPER_EVTCOUNT; + if (!event_supported(event)) { + return false; + } + } + + return enabled && !prohibited && !filtered; } -void pmccntr_sync(CPUARMState *env) +static void pmu_update_irq(CPUARMState *env) { - uint64_t temp_ticks; + ARMCPU *cpu = arm_env_get_cpu(env); + qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) && + (env->cp15.c9_pminten & env->cp15.c9_pmovsr)); +} - temp_ticks = muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); +/* + * Ensure c15_ccnt is the guest-visible count so that operations such as + * enabling/disabling the counter or filtering, modifying the count itself, + * etc. can be done logically. This is essentially a no-op if the counter is + * not enabled at the time of the call. + */ +static void pmccntr_op_start(CPUARMState *env) +{ + uint64_t cycles = cycles_get_count(env); - if (env->cp15.c9_pmcr & PMCRD) { - /* Increment once every 64 processor clock cycles */ - temp_ticks /= 64; + if (pmu_counter_enabled(env, 31)) { + uint64_t eff_cycles = cycles; + if (env->cp15.c9_pmcr & PMCRD) { + /* Increment once every 64 processor clock cycles */ + eff_cycles /= 64; + } + + uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta; + + uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \ + 1ull << 63 : 1ull << 31; + if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) { + env->cp15.c9_pmovsr |= (1 << 31); + pmu_update_irq(env); + } + + env->cp15.c15_ccnt = new_pmccntr; + } + env->cp15.c15_ccnt_delta = cycles; +} + +/* + * If PMCCNTR is enabled, recalculate the delta between the clock and the + * guest-visible count. A call to pmccntr_op_finish should follow every call to + * pmccntr_op_start. + */ +static void pmccntr_op_finish(CPUARMState *env) +{ + if (pmu_counter_enabled(env, 31)) { +#ifndef CONFIG_USER_ONLY + /* Calculate when the counter will next overflow */ + uint64_t remaining_cycles = -env->cp15.c15_ccnt; + if (!(env->cp15.c9_pmcr & PMCRLC)) { + remaining_cycles = (uint32_t)remaining_cycles; + } + int64_t overflow_in = cycles_ns_per(remaining_cycles); + + if (overflow_in > 0) { + int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + overflow_in; + ARMCPU *cpu = arm_env_get_cpu(env); + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + } +#endif + + uint64_t prev_cycles = env->cp15.c15_ccnt_delta; + if (env->cp15.c9_pmcr & PMCRD) { + /* Increment once every 64 processor clock cycles */ + prev_cycles /= 64; + } + env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt; + } +} + +static void pmevcntr_op_start(CPUARMState *env, uint8_t counter) +{ + + uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; + uint64_t count = 0; + if (event_supported(event)) { + uint16_t event_idx = supported_event_map[event]; + count = pm_events[event_idx].get_count(env); } - if (arm_ccnt_enabled(env)) { - env->cp15.c15_ccnt = temp_ticks - env->cp15.c15_ccnt; + if (pmu_counter_enabled(env, counter)) { + uint32_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; + + if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & INT32_MIN) { + env->cp15.c9_pmovsr |= (1 << counter); + pmu_update_irq(env); + } + env->cp15.c14_pmevcntr[counter] = new_pmevcntr; } + env->cp15.c14_pmevcntr_delta[counter] = count; +} + +static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter) +{ + if (pmu_counter_enabled(env, counter)) { +#ifndef CONFIG_USER_ONLY + uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; + uint16_t event_idx = supported_event_map[event]; + uint64_t delta = UINT32_MAX - + (uint32_t)env->cp15.c14_pmevcntr[counter] + 1; + int64_t overflow_in = pm_events[event_idx].ns_per_count(delta); + + if (overflow_in > 0) { + int64_t overflow_at = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + + overflow_in; + ARMCPU *cpu = arm_env_get_cpu(env); + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + } +#endif + + env->cp15.c14_pmevcntr_delta[counter] -= + env->cp15.c14_pmevcntr[counter]; + } +} + +void pmu_op_start(CPUARMState *env) +{ + unsigned int i; + pmccntr_op_start(env); + for (i = 0; i < pmu_num_counters(env); i++) { + pmevcntr_op_start(env, i); + } +} + +void pmu_op_finish(CPUARMState *env) +{ + unsigned int i; + pmccntr_op_finish(env); + for (i = 0; i < pmu_num_counters(env); i++) { + pmevcntr_op_finish(env, i); + } +} + +void pmu_pre_el_change(ARMCPU *cpu, void *ignored) +{ + pmu_op_start(&cpu->env); +} + +void pmu_post_el_change(ARMCPU *cpu, void *ignored) +{ + pmu_op_finish(&cpu->env); +} + +void arm_pmu_timer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + /* + * Update all the counter values based on the current underlying counts, + * triggering interrupts to be raised, if necessary. pmu_op_finish() also + * has the effect of setting the cpu->pmu_timer to the next earliest time a + * counter may expire. + */ + pmu_op_start(&cpu->env); + pmu_op_finish(&cpu->env); } static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - pmccntr_sync(env); + pmu_op_start(env); if (value & PMCRC) { /* The counter has been reset */ env->cp15.c15_ccnt = 0; } + if (value & PMCRP) { + unsigned int i; + for (i = 0; i < pmu_num_counters(env); i++) { + env->cp15.c14_pmevcntr[i] = 0; + } + } + /* only the DP, X, D and E bits are writable */ env->cp15.c9_pmcr &= ~0x39; env->cp15.c9_pmcr |= (value & 0x39); - pmccntr_sync(env); + pmu_op_finish(env); +} + +static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + unsigned int i; + for (i = 0; i < pmu_num_counters(env); i++) { + /* Increment a counter's count iff: */ + if ((value & (1 << i)) && /* counter's bit is set */ + /* counter is enabled and not filtered */ + pmu_counter_enabled(env, i) && + /* counter is SW_INCR */ + (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) { + pmevcntr_op_start(env, i); + + /* + * Detect if this write causes an overflow since we can't predict + * PMSWINC overflows like we can for other events + */ + uint32_t new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; + + if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & INT32_MIN) { + env->cp15.c9_pmovsr |= (1 << i); + pmu_update_irq(env); + } + + env->cp15.c14_pmevcntr[i] = new_pmswinc; + + pmevcntr_op_finish(env, i); + } + } } static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) { - uint64_t total_ticks; - - if (!arm_ccnt_enabled(env)) { - /* Counter is disabled, do not change value */ - return env->cp15.c15_ccnt; - } - - 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 */ - total_ticks /= 64; - } - return total_ticks - env->cp15.c15_ccnt; + uint64_t ret; + pmccntr_op_start(env); + ret = env->cp15.c15_ccnt; + pmccntr_op_finish(env); + return ret; } static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1114,22 +1560,9 @@ static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri, static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - uint64_t total_ticks; - - if (!arm_ccnt_enabled(env)) { - /* Counter is disabled, set the absolute value */ - env->cp15.c15_ccnt = value; - return; - } - - 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 */ - total_ticks /= 64; - } - env->cp15.c15_ccnt = total_ticks - value; + pmccntr_op_start(env); + env->cp15.c15_ccnt = value; + pmccntr_op_finish(env); } static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1140,20 +1573,28 @@ static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri, pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value)); } -#else /* CONFIG_USER_ONLY */ - -void pmccntr_sync(CPUARMState *env) -{ -} - -#endif - static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - pmccntr_sync(env); - env->cp15.pmccfiltr_el0 = value & 0xfc000000; - pmccntr_sync(env); + pmccntr_op_start(env); + env->cp15.pmccfiltr_el0 = value & PMCCFILTR_EL0; + pmccntr_op_finish(env); +} + +static void pmccfiltr_write_a32(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmccntr_op_start(env); + /* M is not accessible from AArch32 */ + env->cp15.pmccfiltr_el0 = (env->cp15.pmccfiltr_el0 & PMCCFILTR_M) | + (value & PMCCFILTR); + pmccntr_op_finish(env); +} + +static uint64_t pmccfiltr_read_a32(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* M is not visible in AArch32 */ + return env->cp15.pmccfiltr_el0 & PMCCFILTR; } static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1173,33 +1614,187 @@ static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + value &= pmu_counter_mask(env); env->cp15.c9_pmovsr &= ~value; + pmu_update_irq(env); +} + +static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= pmu_counter_mask(env); + env->cp15.c9_pmovsr |= value; + pmu_update_irq(env); +} + +static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value, const uint8_t counter) +{ + if (counter == 31) { + pmccfiltr_write(env, ri, value); + } else if (counter < pmu_num_counters(env)) { + pmevcntr_op_start(env, counter); + + /* + * If this counter's event type is changing, store the current + * underlying count for the new type in c14_pmevcntr_delta[counter] so + * pmevcntr_op_finish has the correct baseline when it converts back to + * a delta. + */ + uint16_t old_event = env->cp15.c14_pmevtyper[counter] & + PMXEVTYPER_EVTCOUNT; + uint16_t new_event = value & PMXEVTYPER_EVTCOUNT; + if (old_event != new_event) { + uint64_t count = 0; + if (event_supported(new_event)) { + uint16_t event_idx = supported_event_map[new_event]; + count = pm_events[event_idx].get_count(env); + } + env->cp15.c14_pmevcntr_delta[counter] = count; + } + + env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK; + pmevcntr_op_finish(env, counter); + } + /* Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when + * PMSELR value is equal to or greater than the number of implemented + * counters, but not equal to 0x1f. We opt to behave as a RAZ/WI. + */ +} + +static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri, + const uint8_t counter) +{ + if (counter == 31) { + return env->cp15.pmccfiltr_el0; + } else if (counter < pmu_num_counters(env)) { + return env->cp15.c14_pmevtyper[counter]; + } else { + /* + * We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER + * are CONSTRAINED UNPREDICTABLE. See comments in pmevtyper_write(). + */ + return 0; + } +} + +static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + pmevtyper_write(env, ri, value, counter); +} + +static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + env->cp15.c14_pmevtyper[counter] = value; + + /* + * pmevtyper_rawwrite is called between a pair of pmu_op_start and + * pmu_op_finish calls when loading saved state for a migration. Because + * we're potentially updating the type of event here, the value written to + * c14_pmevcntr_delta by the preceeding pmu_op_start call may be for a + * different counter type. Therefore, we need to set this value to the + * current count for the counter type we're writing so that pmu_op_finish + * has the correct count for its calculation. + */ + uint16_t event = value & PMXEVTYPER_EVTCOUNT; + if (event_supported(event)) { + uint16_t event_idx = supported_event_map[event]; + env->cp15.c14_pmevcntr_delta[counter] = + pm_events[event_idx].get_count(env); + } +} + +static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + return pmevtyper_read(env, ri, counter); } static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when - * PMSELR value is equal to or greater than the number of implemented - * counters, but not equal to 0x1f. We opt to behave as a RAZ/WI. - */ - if (env->cp15.c9_pmselr == 0x1f) { - pmccfiltr_write(env, ri, value); - } + pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31); } static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri) { - /* We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER - * are CONSTRAINED UNPREDICTABLE. See comments in pmxevtyper_write(). + return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31); +} + +static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value, uint8_t counter) +{ + if (counter < pmu_num_counters(env)) { + pmevcntr_op_start(env, counter); + env->cp15.c14_pmevcntr[counter] = value; + pmevcntr_op_finish(env, counter); + } + /* + * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR + * are CONSTRAINED UNPREDICTABLE. */ - if (env->cp15.c9_pmselr == 0x1f) { - return env->cp15.pmccfiltr_el0; +} + +static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint8_t counter) +{ + if (counter < pmu_num_counters(env)) { + uint64_t ret; + pmevcntr_op_start(env, counter); + ret = env->cp15.c14_pmevcntr[counter]; + pmevcntr_op_finish(env, counter); + return ret; } else { + /* We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR + * are CONSTRAINED UNPREDICTABLE. */ return 0; } } +static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + pmevcntr_write(env, ri, value, counter); +} + +static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + return pmevcntr_read(env, ri, counter); +} + +static void pmevcntr_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + assert(counter < pmu_num_counters(env)); + env->cp15.c14_pmevcntr[counter] = value; + pmevcntr_write(env, ri, value, counter); +} + +static uint64_t pmevcntr_rawread(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + assert(counter < pmu_num_counters(env)); + return env->cp15.c14_pmevcntr[counter]; +} + +static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31); +} + +static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31); +} + static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -1216,6 +1811,7 @@ static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, /* We have no event counters so only the C bit can be changed */ value &= pmu_counter_mask(env); env->cp15.c9_pminten |= value; + pmu_update_irq(env); } static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1223,6 +1819,7 @@ static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, { value &= pmu_counter_mask(env); env->cp15.c9_pminten &= ~value; + pmu_update_irq(env); } static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -1239,11 +1836,16 @@ static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* We only mask off bits that are RES0 both for AArch64 and AArch32. - * For bits that vary between AArch32/64, code needs to check the - * current execution mode before directly using the feature bit. - */ - uint32_t valid_mask = SCR_AARCH64_MASK | SCR_AARCH32_MASK; + /* Begin with base v8.0 state. */ + uint32_t valid_mask = 0x3fff; + ARMCPU *cpu = arm_env_get_cpu(env); + + if (arm_el_is_aa64(env, 3)) { + value |= SCR_FW | SCR_AW; /* these two bits are RES1. */ + valid_mask &= ~SCR_NET; + } else { + valid_mask &= ~(SCR_RW | SCR_ST); + } if (!arm_feature(env, ARM_FEATURE_EL2)) { valid_mask &= ~SCR_HCE; @@ -1259,6 +1861,12 @@ static void scr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) valid_mask &= ~SCR_SMD; } } + if (cpu_isar_feature(aa64_lor, cpu)) { + valid_mask |= SCR_TLOR; + } + if (cpu_isar_feature(aa64_pauth, cpu)) { + valid_mask |= SCR_API | SCR_APK; + } /* Clear all-context RES0 bits. */ value &= valid_mask; @@ -1287,14 +1895,29 @@ static void csselr_write(CPUARMState *env, const ARMCPRegInfo *ri, static uint64_t isr_read(CPUARMState *env, const ARMCPRegInfo *ri) { CPUState *cs = ENV_GET_CPU(env); + uint64_t hcr_el2 = arm_hcr_el2_eff(env); uint64_t ret = 0; - if (cs->interrupt_request & CPU_INTERRUPT_HARD) { - ret |= CPSR_I; + if (hcr_el2 & HCR_IMO) { + if (cs->interrupt_request & CPU_INTERRUPT_VIRQ) { + ret |= CPSR_I; + } + } else { + if (cs->interrupt_request & CPU_INTERRUPT_HARD) { + ret |= CPSR_I; + } } - if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { - ret |= CPSR_F; + + if (hcr_el2 & HCR_FMO) { + if (cs->interrupt_request & CPU_INTERRUPT_VFIQ) { + ret |= CPSR_F; + } + } else { + if (cs->interrupt_request & CPU_INTERRUPT_FIQ) { + ret |= CPSR_F; + } } + /* External aborts are not possible in QEMU so A bit is always clear */ return ret; } @@ -1305,7 +1928,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .access = PL1_W, .type = ARM_CP_NOP }, /* Performance monitors are implementation defined in v7, * but with an ARM recommended set of registers, which we - * follow (although we don't actually implement any counters) + * follow. * * Performance registers fall into three categories: * (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR) @@ -1338,7 +1961,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .writefn = pmcntenclr_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, - .access = PL0_RW, + .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), .accessfn = pmreg_access, .writefn = pmovsr_write, @@ -1346,14 +1969,19 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .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, + .type = ARM_CP_ALIAS | ARM_CP_IO, .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_swinc, .type = ARM_CP_NOP }, -#ifndef CONFIG_USER_ONLY + .access = PL0_W, .accessfn = pmreg_access_swinc, + .type = ARM_CP_NO_RAW | ARM_CP_IO, + .writefn = pmswinc_write }, + { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, + .access = PL0_W, .accessfn = pmreg_access_swinc, + .type = ARM_CP_NO_RAW | ARM_CP_IO, + .writefn = pmswinc_write }, { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, .access = PL0_RW, .type = ARM_CP_ALIAS, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), @@ -1372,26 +2000,39 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access_ccntr, .type = ARM_CP_IO, - .readfn = pmccntr_read, .writefn = pmccntr_write, }, -#endif + .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), + .readfn = pmccntr_read, .writefn = pmccntr_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, }, + { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7, + .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32, + .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .resetvalue = 0, }, { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, - .writefn = pmccfiltr_write, + .writefn = pmccfiltr_write, .raw_writefn = raw_write, .access = PL0_RW, .accessfn = pmreg_access, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0), .resetvalue = 0, }, { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_NO_RAW, .accessfn = pmreg_access, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access, .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_NO_RAW, .accessfn = pmreg_access, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access, .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, - /* Unimplemented, RAZ/WI. */ { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, - .access = PL0_RW, .type = ARM_CP_CONST, .resetvalue = 0, - .accessfn = pmreg_access_xevcntr }, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access_xevcntr, + .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, + { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access_xevcntr, + .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, .access = PL0_R | PL1_RW, .accessfn = access_tpm, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr), @@ -1417,12 +2058,14 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .writefn = pmintenset_write, .raw_writefn = raw_write, .resetvalue = 0x0 }, { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, + .access = PL1_RW, .accessfn = access_tpm, + .type = ARM_CP_ALIAS | ARM_CP_IO, .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, + .access = PL1_RW, .accessfn = access_tpm, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), .writefn = pmintenclr_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, @@ -1520,6 +2163,24 @@ static const ARMCPRegInfo v7mp_cp_reginfo[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo pmovsset_cp_reginfo[] = { + /* PMOVSSET is not implemented in v7 before v7ve */ + { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsset_write, + .raw_writefn = raw_write }, + { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsset_write, + .raw_writefn = raw_write }, + REGINFO_SENTINEL +}; + static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -2008,7 +2669,7 @@ 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, + .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL0_RW, .accessfn = gt_ptimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), @@ -2017,7 +2678,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { { .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, + .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL0_RW, .accessfn = gt_ptimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_SEC].ctl), @@ -2025,14 +2686,14 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTP_CTL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 1, - .type = ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_IO, .access = PL0_RW, .accessfn = gt_ptimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].ctl), .resetvalue = 0, .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, + .type = ARM_CP_IO | ARM_CP_ALIAS, .access = PL0_RW, .accessfn = gt_vtimer_access, .fieldoffset = offsetoflow32(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), @@ -2040,7 +2701,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTV_CTL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 1, - .type = ARM_CP_IO, .access = PL1_RW | PL0_R, + .type = ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].ctl), .resetvalue = 0, @@ -2049,31 +2710,31 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { /* 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, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_ptimer_access, .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, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .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, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .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, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, .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, + .type = ARM_CP_NO_RAW | ARM_CP_IO, .access = PL0_RW, .accessfn = gt_vtimer_access, .resetfn = gt_virt_timer_reset, .readfn = gt_virt_tval_read, .writefn = gt_virt_tval_write, }, @@ -2101,7 +2762,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { /* 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, + .access = PL0_RW, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .accessfn = gt_ptimer_access, @@ -2109,7 +2770,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTP_CVAL_S", .cp = 15, .crm = 14, .opc1 = 2, .secure = ARM_CP_SECSTATE_S, - .access = PL1_RW | PL0_R, + .access = PL0_RW, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_SEC].cval), .accessfn = gt_ptimer_access, @@ -2117,14 +2778,14 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTP_CVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 2, .opc2 = 2, - .access = PL1_RW | PL0_R, + .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_PHYS].cval), .resetvalue = 0, .accessfn = gt_ptimer_access, .writefn = gt_phys_cval_write, .raw_writefn = raw_write, }, { .name = "CNTV_CVAL", .cp = 15, .crm = 14, .opc1 = 3, - .access = PL1_RW | PL0_R, + .access = PL0_RW, .type = ARM_CP_64BIT | ARM_CP_IO | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .accessfn = gt_vtimer_access, @@ -2132,7 +2793,7 @@ static const ARMCPRegInfo generic_timer_cp_reginfo[] = { }, { .name = "CNTV_CVAL_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 3, .opc2 = 2, - .access = PL1_RW | PL0_R, + .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c14_timer[GTIMER_VIRT].cval), .resetvalue = 0, .accessfn = gt_vtimer_access, @@ -2261,13 +2922,15 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, * * The Non-secure TTBCR.EAE bit is set to 1 * * The implementation includes EL2, and the value of HCR.VM is 1 * - * ATS1Hx always uses the 64bit format (not supported yet). + * (Note that HCR.DC makes HCR.VM behave as if it is 1.) + * + * ATS1Hx always uses the 64bit format. */ format64 = arm_s1_regime_using_lpae_format(env, mmu_idx); if (arm_feature(env, ARM_FEATURE_EL2)) { if (mmu_idx == ARMMMUIdx_S12NSE0 || mmu_idx == ARMMMUIdx_S12NSE1) { - format64 |= env->cp15.hcr_el2 & HCR_VM; + format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC); } else { format64 |= arm_current_el(env) == 2; } @@ -2289,10 +2952,12 @@ static uint64_t do_ats_write(CPUARMState *env, uint64_t value, par64 |= 1; /* F */ par64 |= (fsr & 0x3f) << 1; /* FS */ - /* Note that S2WLK and FSTAGE are always zero, because we don't - * implement virtualization and therefore there can't be a stage 2 - * fault. - */ + if (fi.stage2) { + par64 |= (1 << 9); /* S */ + } + if (fi.s1ptw) { + par64 |= (1 << 8); /* PTW */ + } } } else { /* fsr is a DFSR/IFSR value for the short descriptor @@ -2384,7 +3049,7 @@ static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; uint64_t par64; - par64 = do_ats_write(env, value, access_type, ARMMMUIdx_S2NS); + par64 = do_ats_write(env, value, access_type, ARMMMUIdx_S1E2); A32_BANKED_CURRENT_REG_SET(env, par, par64); } @@ -2664,6 +3329,7 @@ static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { ARMCPU *cpu = arm_env_get_cpu(env); + TCR *tcr = raw_ptr(env, ri); if (arm_feature(env, ARM_FEATURE_LPAE)) { /* With LPAE the TTBCR could result in a change of ASID @@ -2671,6 +3337,8 @@ static void vmsa_ttbcr_write(CPUARMState *env, const ARMCPRegInfo *ri, */ tlb_flush(CPU(cpu)); } + /* Preserve the high half of TCR_EL1, set via TTBCR2. */ + value = deposit64(tcr->raw_tcr, 0, 32, value); vmsa_ttbcr_raw_write(env, ri, value); } @@ -2700,12 +3368,10 @@ static void vmsa_tcr_el1_write(CPUARMState *env, const ARMCPRegInfo *ri, static void vmsa_ttbr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { - /* 64 bit accesses to the TTBRs can change the ASID and so we - * must flush the TLB. - */ - if (cpreg_field_is_64bit(ri)) { + /* If the ASID changes (with a 64-bit write), we must flush the TLB. */ + if (cpreg_field_is_64bit(ri) && + extract64(raw_read(env, ri) ^ value, 48, 16) != 0) { ARMCPU *cpu = arm_env_get_cpu(env); - tlb_flush(CPU(cpu)); } raw_write(env, ri, value); @@ -2775,6 +3441,16 @@ static const ARMCPRegInfo vmsa_cp_reginfo[] = { REGINFO_SENTINEL }; +/* Note that unlike TTBCR, writing to TTBCR2 does not require flushing + * qemu tlbs nor adjusting cached masks. + */ +static const ARMCPRegInfo ttbcr2_reginfo = { + .name = "TTBCR2", .cp = 15, .opc1 = 0, .crn = 2, .crm = 0, .opc2 = 3, + .access = PL1_RW, .type = ARM_CP_ALIAS, + .bank_fieldoffsets = { offsetofhigh32(CPUARMState, cp15.tcr_el[3]), + offsetofhigh32(CPUARMState, cp15.tcr_el[1]) }, +}; + static void omap_ticonfig_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -2986,13 +3662,6 @@ static uint64_t mpidr_read(CPUARMState *env, const ARMCPRegInfo *ri) 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, - .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, - REGINFO_SENTINEL -}; - static const ARMCPRegInfo lpae_cp_reginfo[] = { /* NOP AMAIR0/1 */ { .name = "AMAIR0", .state = ARM_CP_STATE_BOTH, @@ -3074,22 +3743,6 @@ static CPAccessResult aa64_cacheop_access(CPUARMState *env, * Page D4-1736 (DDI0487A.b) */ -static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - CPUState *cs = ENV_GET_CPU(env); - - if (arm_is_secure_below_el3(env)) { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); - } else { - tlb_flush_by_mmuidx(cs, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); - } -} - static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -3107,6 +3760,27 @@ static void tlbi_aa64_vmalle1is_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void tlbi_aa64_vmalle1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + CPUState *cs = ENV_GET_CPU(env); + + if (tlb_force_broadcast(env)) { + tlbi_aa64_vmalle1is_write(env, NULL, value); + return; + } + + if (arm_is_secure_below_el3(env)) { + tlb_flush_by_mmuidx(cs, + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); + } else { + tlb_flush_by_mmuidx(cs, + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); + } +} + static void tlbi_aa64_alle1_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -3196,29 +3870,6 @@ static void tlbi_aa64_alle3is_write(CPUARMState *env, const ARMCPRegInfo *ri, tlb_flush_by_mmuidx_all_cpus_synced(cs, ARMMMUIdxBit_S1E3); } -static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* 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); - - if (arm_is_secure_below_el3(env)) { - tlb_flush_page_by_mmuidx(cs, pageaddr, - ARMMMUIdxBit_S1SE1 | - ARMMMUIdxBit_S1SE0); - } else { - tlb_flush_page_by_mmuidx(cs, pageaddr, - ARMMMUIdxBit_S12NSE1 | - ARMMMUIdxBit_S12NSE0); - } -} - static void tlbi_aa64_vae2_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -3266,6 +3917,34 @@ static void tlbi_aa64_vae1is_write(CPUARMState *env, const ARMCPRegInfo *ri, } } +static void tlbi_aa64_vae1_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* 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); + + if (tlb_force_broadcast(env)) { + tlbi_aa64_vae1is_write(env, NULL, value); + return; + } + + if (arm_is_secure_below_el3(env)) { + tlb_flush_page_by_mmuidx(cs, pageaddr, + ARMMMUIdxBit_S1SE1 | + ARMMMUIdxBit_S1SE0); + } else { + tlb_flush_page_by_mmuidx(cs, pageaddr, + ARMMMUIdxBit_S12NSE1 | + ARMMMUIdxBit_S12NSE0); + } +} + static void tlbi_aa64_vae2is_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -3744,15 +4423,22 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { /* Used to describe the behaviour of EL2 regs when EL2 does not exist. */ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { - { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, - { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "HCR_EL2", .state = ARM_CP_STATE_BOTH, .type = ARM_CP_NO_RAW, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, - .readfn = arm_cp_read_zero, .writefn = arm_cp_write_ignore }, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HACR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, + .access = PL2_RW, + .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "CPTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 2, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, @@ -3761,14 +4447,14 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .cp = 15, .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, + { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, + .cp = 15, .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, @@ -3837,6 +4523,22 @@ static const ARMCPRegInfo el3_no_el2_cp_reginfo[] = { { .name = "HSTR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 3, .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "HIFAR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_CONST, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, + .access = PL2_RW, .resetvalue = 0 }, + REGINFO_SENTINEL +}; + +/* Ditto, but for registers which exist in ARMv8 but not v7 */ +static const ARMCPRegInfo el3_no_el2_v8_cp_reginfo[] = { + { .name = "HCR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, + .type = ARM_CP_CONST, .resetvalue = 0 }, REGINFO_SENTINEL }; @@ -3857,6 +4559,12 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) */ valid_mask &= ~HCR_TSC; } + if (cpu_isar_feature(aa64_lor, cpu)) { + valid_mask |= HCR_TLOR; + } + if (cpu_isar_feature(aa64_pauth, cpu)) { + valid_mask |= HCR_API | HCR_APK; + } /* Clear RES0 bits. */ value &= valid_mask; @@ -3866,34 +4574,124 @@ static void hcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) * HCR_PTW forbids certain page-table setups * HCR_DC Disables stage1 and enables stage2 translation */ - if ((raw_read(env, ri) ^ value) & (HCR_VM | HCR_PTW | HCR_DC)) { + if ((env->cp15.hcr_el2 ^ value) & (HCR_VM | HCR_PTW | HCR_DC)) { tlb_flush(CPU(cpu)); } - raw_write(env, ri, value); + env->cp15.hcr_el2 = value; + + /* + * Updates to VI and VF require us to update the status of + * virtual interrupts, which are the logical OR of these bits + * and the state of the input lines from the GIC. (This requires + * that we have the iothread lock, which is done by marking the + * reginfo structs as ARM_CP_IO.) + * Note that if a write to HCR pends a VIRQ or VFIQ it is never + * possible for it to be taken immediately, because VIRQ and + * VFIQ are masked unless running at EL0 or EL1, and HCR + * can only be written at EL2. + */ + g_assert(qemu_mutex_iothread_locked()); + arm_cpu_update_virq(cpu); + arm_cpu_update_vfiq(cpu); +} + +static void hcr_writehigh(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Handle HCR2 write, i.e. write to high half of HCR_EL2 */ + value = deposit64(env->cp15.hcr_el2, 32, 32, value); + hcr_write(env, NULL, value); +} + +static void hcr_writelow(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* Handle HCR write, i.e. write to low half of HCR_EL2 */ + value = deposit64(env->cp15.hcr_el2, 0, 32, value); + hcr_write(env, NULL, value); +} + +/* + * Return the effective value of HCR_EL2. + * Bits that are not included here: + * RW (read from SCR_EL3.RW as needed) + */ +uint64_t arm_hcr_el2_eff(CPUARMState *env) +{ + uint64_t ret = env->cp15.hcr_el2; + + if (arm_is_secure_below_el3(env)) { + /* + * "This register has no effect if EL2 is not enabled in the + * current Security state". This is ARMv8.4-SecEL2 speak for + * !(SCR_EL3.NS==1 || SCR_EL3.EEL2==1). + * + * Prior to that, the language was "In an implementation that + * includes EL3, when the value of SCR_EL3.NS is 0 the PE behaves + * as if this field is 0 for all purposes other than a direct + * read or write access of HCR_EL2". With lots of enumeration + * on a per-field basis. In current QEMU, this is condition + * is arm_is_secure_below_el3. + * + * Since the v8.4 language applies to the entire register, and + * appears to be backward compatible, use that. + */ + ret = 0; + } else if (ret & HCR_TGE) { + /* These bits are up-to-date as of ARMv8.4. */ + if (ret & HCR_E2H) { + ret &= ~(HCR_VM | HCR_FMO | HCR_IMO | HCR_AMO | + HCR_BSU_MASK | HCR_DC | HCR_TWI | HCR_TWE | + HCR_TID0 | HCR_TID2 | HCR_TPCP | HCR_TPU | + HCR_TDZ | HCR_CD | HCR_ID | HCR_MIOCNCE); + } else { + ret |= HCR_FMO | HCR_IMO | HCR_AMO; + } + ret &= ~(HCR_SWIO | HCR_PTW | HCR_VF | HCR_VI | HCR_VSE | + HCR_FB | HCR_TID1 | HCR_TID3 | HCR_TSC | HCR_TACR | + HCR_TSW | HCR_TTLB | HCR_TVM | HCR_HCD | HCR_TRVM | + HCR_TLOR); + } + + return ret; } static const ARMCPRegInfo el2_cp_reginfo[] = { { .name = "HCR_EL2", .state = ARM_CP_STATE_AA64, + .type = ARM_CP_IO, .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), .writefn = hcr_write }, + { .name = "HCR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 0, + .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.hcr_el2), + .writefn = hcr_writelow }, + { .name = "HACR_EL2", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 7, + .access = PL2_RW, .type = ARM_CP_CONST, .resetvalue = 0 }, { .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, + { .name = "ESR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 5, .crm = 2, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.esr_el[2]) }, - { .name = "FAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "FAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 0, .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.far_el[2]) }, + { .name = "HIFAR", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS, + .cp = 15, .opc1 = 4, .crn = 6, .crm = 0, .opc2 = 2, + .access = PL2_RW, + .fieldoffset = offsetofhigh32(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[BANK_HYP]) }, - { .name = "VBAR_EL2", .state = ARM_CP_STATE_AA64, + { .name = "VBAR_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 12, .crm = 0, .opc2 = 0, .access = PL2_RW, .writefn = vbar_write, .fieldoffset = offsetof(CPUARMState, cp15.vbar_el[2]), @@ -3911,7 +4709,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .fieldoffset = offsetof(CPUARMState, cp15.mair_el[2]), .resetvalue = 0 }, { .name = "HMAIR1", .state = ARM_CP_STATE_AA32, - .opc1 = 4, .crn = 10, .crm = 2, .opc2 = 1, + .cp = 15, .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, @@ -3919,8 +4717,8 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .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, + { .name = "HAMAIR1", .state = ARM_CP_STATE_AA32, + .cp = 15, .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, @@ -4084,7 +4882,7 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { #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 + * don't implement any PMU event counters, so using zero as a reset * value for MDCR_EL2 is okay */ { .name = "MDCR_EL2", .state = ARM_CP_STATE_BOTH, @@ -4106,6 +4904,16 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { REGINFO_SENTINEL }; +static const ARMCPRegInfo el2_v8_cp_reginfo[] = { + { .name = "HCR2", .state = ARM_CP_STATE_AA32, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 1, .opc2 = 4, + .access = PL2_RW, + .fieldoffset = offsetofhigh32(CPUARMState, cp15.hcr_el2), + .writefn = hcr_writehigh }, + REGINFO_SENTINEL +}; + static CPAccessResult nsacr_access(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -4149,7 +4957,7 @@ static const ARMCPRegInfo el3_cp_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.mvbar) }, { .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, + .access = PL3_RW, .resetvalue = 0, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[3]) }, { .name = "TCR_EL3", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 6, .crn = 2, .crm = 0, .opc2 = 2, @@ -4338,78 +5146,104 @@ static const ARMCPRegInfo debug_lpae_cp_reginfo[] = { REGINFO_SENTINEL }; -/* Return the exception level to which SVE-disabled exceptions should - * be taken, or 0 if SVE is enabled. +/* Return the exception level to which exceptions should be taken + * via SVEAccessTrap. If an exception should be routed through + * AArch64.AdvSIMDFPAccessTrap, return 0; fp_exception_el should + * take care of raising that exception. + * C.f. the ARM pseudocode function CheckSVEEnabled. */ -static int sve_exception_el(CPUARMState *env) +int sve_exception_el(CPUARMState *env, int el) { #ifndef CONFIG_USER_ONLY - unsigned current_el = arm_current_el(env); + if (el <= 1) { + bool disabled = false; - /* The CPACR.ZEN controls traps to EL1: - * 0, 2 : trap EL0 and EL1 accesses - * 1 : trap only EL0 accesses - * 3 : trap no accesses + /* The CPACR.ZEN controls traps to EL1: + * 0, 2 : trap EL0 and EL1 accesses + * 1 : trap only EL0 accesses + * 3 : trap no accesses + */ + if (!extract32(env->cp15.cpacr_el1, 16, 1)) { + disabled = true; + } else if (!extract32(env->cp15.cpacr_el1, 17, 1)) { + disabled = el == 0; + } + if (disabled) { + /* route_to_el2 */ + return (arm_feature(env, ARM_FEATURE_EL2) + && (arm_hcr_el2_eff(env) & HCR_TGE) ? 2 : 1); + } + + /* Check CPACR.FPEN. */ + if (!extract32(env->cp15.cpacr_el1, 20, 1)) { + disabled = true; + } else if (!extract32(env->cp15.cpacr_el1, 21, 1)) { + disabled = el == 0; + } + if (disabled) { + return 0; + } + } + + /* CPTR_EL2. Since TZ and TFP are positive, + * they will be zero when EL2 is not present. */ - switch (extract32(env->cp15.cpacr_el1, 16, 2)) { - default: - if (current_el <= 1) { - /* Trap to PL1, which might be EL1 or EL3 */ - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { - return 3; - } - return 1; + if (el <= 2 && !arm_is_secure_below_el3(env)) { + if (env->cp15.cptr_el[2] & CPTR_TZ) { + return 2; } - break; - case 1: - if (current_el == 0) { - return 1; + if (env->cp15.cptr_el[2] & CPTR_TFP) { + return 0; } - break; - case 3: - break; } - /* Similarly for CPACR.FPEN, after having checked ZEN. */ - switch (extract32(env->cp15.cpacr_el1, 20, 2)) { - default: - if (current_el <= 1) { - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3)) { - return 3; - } - return 1; - } - break; - case 1: - if (current_el == 0) { - return 1; - } - break; - case 3: - break; - } - - /* CPTR_EL2. Check both TZ and TFP. */ - if (current_el <= 2 - && (env->cp15.cptr_el[2] & (CPTR_TFP | CPTR_TZ)) - && !arm_is_secure_below_el3(env)) { - return 2; - } - - /* CPTR_EL3. Check both EZ and TFP. */ - if (!(env->cp15.cptr_el[3] & CPTR_EZ) - || (env->cp15.cptr_el[3] & CPTR_TFP)) { + /* CPTR_EL3. Since EZ is negative we must check for EL3. */ + if (arm_feature(env, ARM_FEATURE_EL3) + && !(env->cp15.cptr_el[3] & CPTR_EZ)) { return 3; } #endif return 0; } +/* + * Given that SVE is enabled, return the vector length for EL. + */ +uint32_t sve_zcr_len_for_el(CPUARMState *env, int el) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + uint32_t zcr_len = cpu->sve_max_vq - 1; + + if (el <= 1) { + zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[1]); + } + if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { + zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[2]); + } + if (el < 3 && arm_feature(env, ARM_FEATURE_EL3)) { + zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[3]); + } + return zcr_len; +} + static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { + int cur_el = arm_current_el(env); + int old_len = sve_zcr_len_for_el(env, cur_el); + int new_len; + /* Bits other than [3:0] are RAZ/WI. */ raw_write(env, ri, value & 0xf); + + /* + * Because we arrived here, we know both FP and SVE are enabled; + * otherwise we would have trapped access to the ZCR_ELn register. + */ + new_len = sve_zcr_len_for_el(env, cur_el); + if (new_len < old_len) { + aarch64_sve_narrow_vq(env, new_len + 1); + } } static const ARMCPRegInfo zcr_el1_reginfo = { @@ -4781,7 +5615,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = arm_env_get_cpu(env); - uint64_t pfr0 = cpu->id_aa64pfr0; + uint64_t pfr0 = cpu->isar.id_aa64pfr0; if (env->gicv3state) { pfr0 |= 1 << 24; @@ -4789,6 +5623,150 @@ static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) return pfr0; } +/* Shared logic between LORID and the rest of the LOR* registers. + * Secure state has already been delt with. + */ +static CPAccessResult access_lor_ns(CPUARMState *env) +{ + int el = arm_current_el(env); + + if (el < 2 && (arm_hcr_el2_eff(env) & HCR_TLOR)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.scr_el3 & SCR_TLOR)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static CPAccessResult access_lorid(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_is_secure_below_el3(env)) { + /* Access ok in secure mode. */ + return CP_ACCESS_OK; + } + return access_lor_ns(env); +} + +static CPAccessResult access_lor_other(CPUARMState *env, + const ARMCPRegInfo *ri, bool isread) +{ + if (arm_is_secure_below_el3(env)) { + /* Access denied in secure mode. */ + return CP_ACCESS_TRAP; + } + return access_lor_ns(env); +} + +#ifdef TARGET_AARCH64 +static CPAccessResult access_pauth(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el < 2 && + arm_feature(env, ARM_FEATURE_EL2) && + !(arm_hcr_el2_eff(env) & HCR_APK)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && + arm_feature(env, ARM_FEATURE_EL3) && + !(env->cp15.scr_el3 & SCR_APK)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo pauth_reginfo[] = { + { .name = "APDAKEYLO_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 0, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apda_key.lo) }, + { .name = "APDAKEYHI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 1, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apda_key.hi) }, + { .name = "APDBKEYLO_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 2, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apdb_key.lo) }, + { .name = "APDBKEYHI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 2, .opc2 = 3, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apdb_key.hi) }, + { .name = "APGAKEYLO_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 0, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apga_key.lo) }, + { .name = "APGAKEYHI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 3, .opc2 = 1, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apga_key.hi) }, + { .name = "APIAKEYLO_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 0, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apia_key.lo) }, + { .name = "APIAKEYHI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 1, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apia_key.hi) }, + { .name = "APIBKEYLO_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 2, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apib_key.lo) }, + { .name = "APIBKEYHI_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 2, .crm = 1, .opc2 = 3, + .access = PL1_RW, .accessfn = access_pauth, + .fieldoffset = offsetof(CPUARMState, apib_key.hi) }, + REGINFO_SENTINEL +}; +#endif + +static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + + if (el == 0) { + uint64_t sctlr = arm_sctlr(env, el); + if (!(sctlr & SCTLR_EnRCTX)) { + return CP_ACCESS_TRAP; + } + } else if (el == 1) { + uint64_t hcr = arm_hcr_el2_eff(env); + if (hcr & HCR_NV) { + return CP_ACCESS_TRAP_EL2; + } + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo predinv_reginfo[] = { + { .name = "CFP_RCTX", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 4, + .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, + { .name = "DVP_RCTX", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 5, + .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, + { .name = "CPP_RCTX", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 3, .crn = 7, .crm = 3, .opc2 = 7, + .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, + /* + * Note the AArch32 opcodes have a different OPC1. + */ + { .name = "CFPRCTX", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 4, + .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, + { .name = "DVPRCTX", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 5, + .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, + { .name = "CPPRCTX", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 7, .crm = 3, .opc2 = 7, + .type = ARM_CP_NOP, .access = PL0_W, .accessfn = access_predinv }, + REGINFO_SENTINEL +}; + void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ @@ -4848,27 +5826,27 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "ID_ISAR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar0 }, + .resetvalue = cpu->isar.id_isar0 }, { .name = "ID_ISAR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar1 }, + .resetvalue = cpu->isar.id_isar1 }, { .name = "ID_ISAR2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar2 }, + .resetvalue = cpu->isar.id_isar2 }, { .name = "ID_ISAR3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar3 }, + .resetvalue = cpu->isar.id_isar3 }, { .name = "ID_ISAR4", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar4 }, + .resetvalue = cpu->isar.id_isar4 }, { .name = "ID_ISAR5", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar5 }, + .resetvalue = cpu->isar.id_isar5 }, { .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, @@ -4876,7 +5854,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_isar6 }, + .resetvalue = cpu->isar.id_isar6 }, REGINFO_SENTINEL }; define_arm_cp_regs(cpu, v6_idregs); @@ -4891,12 +5869,15 @@ void register_cp_regs_for_features(ARMCPU *cpu) !arm_feature(env, ARM_FEATURE_PMSA)) { define_arm_cp_regs(cpu, v7mp_cp_reginfo); } + if (arm_feature(env, ARM_FEATURE_V7VE)) { + define_arm_cp_regs(cpu, pmovsset_cp_reginfo); + } if (arm_feature(env, ARM_FEATURE_V7)) { /* v7 performance monitor control register: same implementor - * field as main ID register, and we implement only the cycle - * count register. + * field as main ID register, and we implement four counters in + * addition to the cycle count register. */ -#ifndef CONFIG_USER_ONLY + unsigned int i, pmcrn = 4; ARMCPRegInfo pmcr = { .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, .access = PL0_RW, @@ -4911,12 +5892,48 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL0_RW, .accessfn = pmreg_access, .type = ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), - .resetvalue = cpu->midr & 0xff000000, + .resetvalue = (cpu->midr & 0xff000000) | (pmcrn << PMCRN_SHIFT), .writefn = pmcr_write, .raw_writefn = raw_write, }; define_one_arm_cp_reg(cpu, &pmcr); define_one_arm_cp_reg(cpu, &pmcr64); -#endif + for (i = 0; i < pmcrn; i++) { + char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); + char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i); + char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i); + char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i); + ARMCPRegInfo pmev_regs[] = { + { .name = pmevcntr_name, .cp = 15, .crn = 14, + .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, + .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, + .accessfn = pmreg_access }, + { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_IO, + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, + .raw_readfn = pmevcntr_rawread, + .raw_writefn = pmevcntr_rawwrite }, + { .name = pmevtyper_name, .cp = 15, .crn = 14, + .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, + .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, + .accessfn = pmreg_access }, + { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .type = ARM_CP_IO, + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, + .raw_writefn = pmevtyper_rawwrite }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, pmev_regs); + g_free(pmevcntr_name); + g_free(pmevcntr_el0_name); + g_free(pmevtyper_name); + g_free(pmevtyper_el0_name); + } ARMCPRegInfo clidr = { .name = "CLIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 1, @@ -4928,6 +5945,21 @@ void register_cp_regs_for_features(ARMCPU *cpu) } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); } + if (FIELD_EX32(cpu->id_dfr0, ID_DFR0, PERFMON) >= 4 && + FIELD_EX32(cpu->id_dfr0, ID_DFR0, PERFMON) != 0xf) { + ARMCPRegInfo v81_pmu_regs[] = { + { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = extract64(cpu->pmceid0, 32, 32) }, + { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .resetvalue = extract64(cpu->pmceid1, 32, 32) }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, v81_pmu_regs); + } if (arm_feature(env, ARM_FEATURE_V8)) { /* AArch64 ID registers, which all have impdef reset values. * Note that within the ID register ranges the unused slots @@ -4947,7 +5979,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .name = "ID_AA64PFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_aa64pfr1}, + .resetvalue = cpu->isar.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, @@ -4956,9 +5988,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) .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, + { .name = "ID_AA64ZFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, + /* At present, only SVEver == 0 is defined anyway. */ .resetvalue = 0 }, { .name = "ID_AA64PFR5_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5, @@ -5007,11 +6040,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .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, - .resetvalue = cpu->id_aa64isar0 }, + .resetvalue = cpu->isar.id_aa64isar0 }, { .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_aa64isar1 }, + .resetvalue = cpu->isar.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, @@ -5039,11 +6072,11 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .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, - .resetvalue = cpu->id_aa64mmfr0 }, + .resetvalue = cpu->isar.id_aa64mmfr0 }, { .name = "ID_AA64MMFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->id_aa64mmfr1 }, + .resetvalue = cpu->isar.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, @@ -5071,15 +6104,15 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .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, - .resetvalue = cpu->mvfr0 }, + .resetvalue = cpu->isar.mvfr0 }, { .name = "MVFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->mvfr1 }, + .resetvalue = cpu->isar.mvfr1 }, { .name = "MVFR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, - .resetvalue = cpu->mvfr2 }, + .resetvalue = cpu->isar.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, @@ -5103,7 +6136,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .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 }, + .resetvalue = extract64(cpu->pmceid0, 0, 32) }, { .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, @@ -5111,13 +6144,45 @@ void register_cp_regs_for_features(ARMCPU *cpu) { .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 }, + .resetvalue = extract64(cpu->pmceid1, 0, 32) }, { .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 }; +#ifdef CONFIG_USER_ONLY + ARMCPRegUserSpaceInfo v8_user_idregs[] = { + { .name = "ID_AA64PFR0_EL1", + .exported_bits = 0x000f000f00ff0000, + .fixed_bits = 0x0000000000000011 }, + { .name = "ID_AA64PFR1_EL1", + .exported_bits = 0x00000000000000f0 }, + { .name = "ID_AA64PFR*_EL1_RESERVED", + .is_glob = true }, + { .name = "ID_AA64ZFR0_EL1" }, + { .name = "ID_AA64MMFR0_EL1", + .fixed_bits = 0x00000000ff000000 }, + { .name = "ID_AA64MMFR1_EL1" }, + { .name = "ID_AA64MMFR*_EL1_RESERVED", + .is_glob = true }, + { .name = "ID_AA64DFR0_EL1", + .fixed_bits = 0x0000000000000006 }, + { .name = "ID_AA64DFR1_EL1" }, + { .name = "ID_AA64DFR*_EL1_RESERVED", + .is_glob = true }, + { .name = "ID_AA64AFR*", + .is_glob = true }, + { .name = "ID_AA64ISAR0_EL1", + .exported_bits = 0x00fffffff0fffff0 }, + { .name = "ID_AA64ISAR1_EL1", + .exported_bits = 0x000000f0ffffffff }, + { .name = "ID_AA64ISAR*_EL1_RESERVED", + .is_glob = true }, + REGUSERINFO_SENTINEL + }; + modify_arm_cp_regs(v8_idregs, v8_user_idregs); +#endif /* RVBAR_EL1 is only implemented if EL1 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3) && !arm_feature(env, ARM_FEATURE_EL2)) { @@ -5157,6 +6222,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el2_cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, el2_v8_cp_reginfo); + } /* RVBAR_EL2 is only implemented if EL2 is the highest EL */ if (!arm_feature(env, ARM_FEATURE_EL3)) { ARMCPRegInfo rvbar = { @@ -5189,6 +6257,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) }; define_arm_cp_regs(cpu, vpidr_regs); define_arm_cp_regs(cpu, el3_no_el2_cp_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, el3_no_el2_v8_cp_reginfo); + } } } if (arm_feature(env, ARM_FEATURE_EL3)) { @@ -5259,6 +6330,10 @@ void register_cp_regs_for_features(ARMCPU *cpu) } else { define_arm_cp_regs(cpu, vmsa_pmsa_cp_reginfo); define_arm_cp_regs(cpu, vmsa_cp_reginfo); + /* TTCBR2 is introduced with ARMv8.2-A32HPD. */ + if (FIELD_EX32(cpu->id_mmfr4, ID_MMFR4, HPDS) != 0) { + define_one_arm_cp_reg(cpu, &ttbcr2_reginfo); + } } if (arm_feature(env, ARM_FEATURE_THUMB2EE)) { define_arm_cp_regs(cpu, t2ee_cp_reginfo); @@ -5384,6 +6459,15 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc1 = CP_ANY, .opc2 = CP_ANY, .access = PL1_W, .type = ARM_CP_NOP | ARM_CP_OVERRIDE }; +#ifdef CONFIG_USER_ONLY + ARMCPRegUserSpaceInfo id_v8_user_midr_cp_reginfo[] = { + { .name = "MIDR_EL1", + .exported_bits = 0x00000000ffffffff }, + { .name = "REVIDR_EL1" }, + REGUSERINFO_SENTINEL + }; + modify_arm_cp_regs(id_v8_midr_cp_reginfo, id_v8_user_midr_cp_reginfo); +#endif if (arm_feature(env, ARM_FEATURE_OMAPCP) || arm_feature(env, ARM_FEATURE_STRONGARM)) { ARMCPRegInfo *r; @@ -5417,6 +6501,20 @@ void register_cp_regs_for_features(ARMCPU *cpu) } if (arm_feature(env, ARM_FEATURE_MPIDR)) { + ARMCPRegInfo mpidr_cp_reginfo[] = { + { .name = "MPIDR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 0, .opc2 = 5, + .access = PL1_R, .readfn = mpidr_read, .type = ARM_CP_NO_RAW }, + REGINFO_SENTINEL + }; +#ifdef CONFIG_USER_ONLY + ARMCPRegUserSpaceInfo mpidr_user_cp_reginfo[] = { + { .name = "MPIDR_EL1", + .fixed_bits = 0x0000000080000000 }, + REGUSERINFO_SENTINEL + }; + modify_arm_cp_regs(mpidr_cp_reginfo, mpidr_user_cp_reginfo); +#endif define_arm_cp_regs(cpu, mpidr_cp_reginfo); } @@ -5437,6 +6535,16 @@ void register_cp_regs_for_features(ARMCPU *cpu) REGINFO_SENTINEL }; define_arm_cp_regs(cpu, auxcr_reginfo); + if (arm_feature(env, ARM_FEATURE_V8)) { + /* HACTLR2 maps to ACTLR_EL2[63:32] and is not in ARMv7 */ + ARMCPRegInfo hactlr2_reginfo = { + .name = "HACTLR2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 4, .crn = 1, .crm = 0, .opc2 = 3, + .access = PL2_RW, .type = ARM_CP_CONST, + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &hactlr2_reginfo); + } } if (arm_feature(env, ARM_FEATURE_CBAR)) { @@ -5509,7 +6617,39 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &sctlr); } - if (arm_feature(env, ARM_FEATURE_SVE)) { + if (cpu_isar_feature(aa64_lor, cpu)) { + /* + * A trivial implementation of ARMv8.1-LOR leaves all of these + * registers fixed at 0, which indicates that there are zero + * supported Limited Ordering regions. + */ + static const ARMCPRegInfo lor_reginfo[] = { + { .name = "LORSA_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 0, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LOREA_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 1, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LORN_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 2, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LORC_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 3, + .access = PL1_RW, .accessfn = access_lor_other, + .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "LORID_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 10, .crm = 4, .opc2 = 7, + .access = PL1_R, .accessfn = access_lorid, + .type = ARM_CP_CONST, .resetvalue = 0 }, + REGINFO_SENTINEL + }; + define_arm_cp_regs(cpu, lor_reginfo); + } + + if (cpu_isar_feature(aa64_sve, cpu)) { define_one_arm_cp_reg(cpu, &zcr_el1_reginfo); if (arm_feature(env, ARM_FEATURE_EL2)) { define_one_arm_cp_reg(cpu, &zcr_el2_reginfo); @@ -5520,6 +6660,23 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &zcr_el3_reginfo); } } + +#ifdef TARGET_AARCH64 + if (cpu_isar_feature(aa64_pauth, cpu)) { + define_arm_cp_regs(cpu, pauth_reginfo); + } +#endif + + /* + * While all v8.0 cpus support aarch64, QEMU does have configurations + * that do not set ID_AA64ISAR1, e.g. user-only qemu-arm -cpu max, + * which will set ID_ISAR6. + */ + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) + ? cpu_isar_feature(aa64_predinv, cpu) + : cpu_isar_feature(aa32_predinv, cpu)) { + define_arm_cp_regs(cpu, predinv_reginfo); + } } void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu) @@ -5613,7 +6770,7 @@ static void arm_cpu_add_definition(gpointer data, gpointer user_data) *cpu_list = entry; } -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) { CpuDefinitionInfoList *cpu_list = NULL; GSList *list; @@ -5808,7 +6965,11 @@ void define_one_arm_cp_reg_with_opaque(ARMCPU *cpu, if (r->state != ARM_CP_STATE_AA32) { int mask = 0; switch (r->opc1) { - case 0: case 1: case 2: + case 0: + /* min_EL EL1, but some accessible to EL0 via kernel ABI */ + mask = PL0U_R | PL1_RW; + break; + case 1: case 2: /* min_EL EL1 */ mask = PL1_RW; break; @@ -5913,6 +7074,44 @@ void define_arm_cp_regs_with_opaque(ARMCPU *cpu, } } +/* + * Modify ARMCPRegInfo for access from userspace. + * + * This is a data driven modification directed by + * ARMCPRegUserSpaceInfo. All registers become ARM_CP_CONST as + * user-space cannot alter any values and dynamic values pertaining to + * execution state are hidden from user space view anyway. + */ +void modify_arm_cp_regs(ARMCPRegInfo *regs, const ARMCPRegUserSpaceInfo *mods) +{ + const ARMCPRegUserSpaceInfo *m; + ARMCPRegInfo *r; + + for (m = mods; m->name; m++) { + GPatternSpec *pat = NULL; + if (m->is_glob) { + pat = g_pattern_spec_new(m->name); + } + for (r = regs; r->type != ARM_CP_SENTINEL; r++) { + if (pat && g_pattern_match_string(pat, r->name)) { + r->type = ARM_CP_CONST; + r->access = PL0U_R; + r->resetvalue = 0; + /* continue */ + } else if (strcmp(r->name, m->name) == 0) { + r->type = ARM_CP_CONST; + r->access = PL0U_R; + r->resetvalue &= m->exported_bits; + r->resetvalue |= m->fixed_bits; + break; + } + } + if (pat) { + g_pattern_spec_free(pat); + } + } +} + const ARMCPRegInfo *get_arm_cp_reginfo(GHashTable *cpregs, uint32_t encoded_cp) { return g_hash_table_lookup(cpregs, &encoded_cp); @@ -5965,15 +7164,14 @@ static int bad_mode_switch(CPUARMState *env, int mode, CPSRWriteType write_type) * 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)) { + (arm_hcr_el2_eff(env) & HCR_TGE)) { 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); + || arm_current_el(env) < 2 || arm_is_secure_below_el3(env); case ARM_CPU_MODE_MON: return arm_current_el(env) < 3; default: @@ -6099,7 +7297,17 @@ void cpsr_write(CPUARMState *env, uint32_t val, uint32_t mask, mask |= CPSR_IL; val |= CPSR_IL; } + qemu_log_mask(LOG_GUEST_ERROR, + "Illegal AArch32 mode switch attempt from %s to %s\n", + aarch32_mode_name(env->uncached_cpsr), + aarch32_mode_name(val)); } else { + qemu_log_mask(CPU_LOG_INT, "%s %s to %s PC 0x%" PRIx32 "\n", + write_type == CPSRWriteExceptionReturn ? + "Exception return from AArch32" : + "AArch32 mode switch from", + aarch32_mode_name(env->uncached_cpsr), + aarch32_mode_name(val), env->regs[15]); switch_mode(env, val & CPSR_M); } } @@ -6145,7 +7353,7 @@ uint32_t HELPER(rbit)(uint32_t x) return revbit32(x); } -#if defined(CONFIG_USER_ONLY) +#ifdef CONFIG_USER_ONLY /* These should probably raise undefined insn exceptions. */ void HELPER(v7m_msr)(CPUARMState *env, uint32_t reg, uint32_t val) @@ -6197,7 +7405,7 @@ uint32_t HELPER(v7m_tt)(CPUARMState *env, uint32_t addr, uint32_t op) return 0; } -void switch_mode(CPUARMState *env, int mode) +static void switch_mode(CPUARMState *env, int mode) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -6219,7 +7427,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) #else -void switch_mode(CPUARMState *env, int mode) +static void switch_mode(CPUARMState *env, int mode) { int old_mode; int i; @@ -6238,13 +7446,14 @@ void switch_mode(CPUARMState *env, int mode) i = bank_number(old_mode); env->banked_r13[i] = env->regs[13]; - env->banked_r14[i] = env->regs[14]; env->banked_spsr[i] = env->spsr; i = bank_number(mode); env->regs[13] = env->banked_r13[i]; - env->regs[14] = env->banked_r14[i]; env->spsr = env->banked_spsr[i]; + + env->banked_r14[r14_bank_number(old_mode)] = env->regs[14]; + env->regs[14] = env->banked_r14[r14_bank_number(mode)]; } /* Physical Interrupt Target EL Lookup Table @@ -6310,12 +7519,13 @@ 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; - int scr; - int hcr; + bool rw; + bool scr; + bool hcr; int target_el; /* Is the highest EL AArch64? */ - int is64 = arm_feature(env, ARM_FEATURE_AARCH64); + bool is64 = arm_feature(env, ARM_FEATURE_AARCH64); + uint64_t hcr_el2; if (arm_feature(env, ARM_FEATURE_EL3)) { rw = ((env->cp15.scr_el3 & SCR_RW) == SCR_RW); @@ -6327,24 +7537,22 @@ uint32_t arm_phys_excp_target_el(CPUState *cs, uint32_t excp_idx, rw = is64; } + hcr_el2 = arm_hcr_el2_eff(env); switch (excp_idx) { case EXCP_IRQ: scr = ((env->cp15.scr_el3 & SCR_IRQ) == SCR_IRQ); - hcr = ((env->cp15.hcr_el2 & HCR_IMO) == HCR_IMO); + hcr = hcr_el2 & HCR_IMO; break; case EXCP_FIQ: scr = ((env->cp15.scr_el3 & SCR_FIQ) == SCR_FIQ); - hcr = ((env->cp15.hcr_el2 & HCR_FMO) == HCR_FMO); + hcr = hcr_el2 & HCR_FMO; break; default: scr = ((env->cp15.scr_el3 & SCR_EA) == SCR_EA); - hcr = ((env->cp15.hcr_el2 & HCR_AMO) == HCR_AMO); + hcr = hcr_el2 & HCR_AMO; break; }; - /* If HCR.TGE is set then HCR is treated as being 1 */ - hcr |= ((env->cp15.hcr_el2 & HCR_TGE) == HCR_TGE); - /* Perform a table-lookup for the target EL given the current state */ target_el = target_el_table[is64][scr][rw][hcr][secure][cur_el]; @@ -6363,7 +7571,7 @@ static bool v7m_stack_write(ARMCPU *cpu, uint32_t addr, uint32_t value, target_ulong page_size; hwaddr physaddr; int prot; - ARMMMUFaultInfo fi; + ARMMMUFaultInfo fi = {}; bool secure = mmu_idx & ARM_MMU_IDX_M_S; int exc; bool exc_secure; @@ -6425,7 +7633,7 @@ static bool v7m_stack_read(ARMCPU *cpu, uint32_t *dest, uint32_t addr, target_ulong page_size; hwaddr physaddr; int prot; - ARMMMUFaultInfo fi; + ARMMMUFaultInfo fi = {}; bool secure = mmu_idx & ARM_MMU_IDX_M_S; int exc; bool exc_secure; @@ -6476,18 +7684,6 @@ pend_fault: return false; } -/* Return true if we're using the process stack pointer (not the MSP) */ -static bool v7m_using_psp(CPUARMState *env) -{ - /* Handler mode always uses the main stack; for thread mode - * the CONTROL.SPSEL bit determines the answer. - * Note that in v7M it is not possible to be in Handler mode with - * CONTROL.SPSEL non-zero, but in v8M it is, so we must check both. - */ - return !arm_v7m_is_handler_mode(env) && - env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK; -} - /* Write to v7M CONTROL.SPSEL bit for the specified security bank. * This may change the current stack pointer between Main and Process * stack pointers if it is done for the CONTROL register for the current @@ -6644,6 +7840,10 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) "BLXNS with misaligned SP is UNPREDICTABLE\n"); } + if (sp < v7m_sp_limit(env)) { + raise_exception(env, EXCP_STKOF, 0, 1); + } + saved_psr = env->v7m.exception; if (env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK) { saved_psr |= XPSR_SFPA; @@ -6773,6 +7973,8 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain, uint32_t frameptr; ARMMMUIdx mmu_idx; bool stacked_ok; + uint32_t limit; + bool want_psp; if (dotailchain) { bool mode = lr & R_V7M_EXCRET_MODE_MASK; @@ -6782,12 +7984,34 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain, mmu_idx = arm_v7m_mmu_idx_for_secstate_and_priv(env, M_REG_S, priv); frame_sp_p = get_v7m_sp_ptr(env, M_REG_S, mode, lr & R_V7M_EXCRET_SPSEL_MASK); + want_psp = mode && (lr & R_V7M_EXCRET_SPSEL_MASK); + if (want_psp) { + limit = env->v7m.psplim[M_REG_S]; + } else { + limit = env->v7m.msplim[M_REG_S]; + } } else { - mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); + mmu_idx = arm_mmu_idx(env); frame_sp_p = &env->regs[13]; + limit = v7m_sp_limit(env); } frameptr = *frame_sp_p - 0x28; + if (frameptr < limit) { + /* + * Stack limit failure: set SP to the limit value, and generate + * STKOF UsageFault. Stack pushes below the limit must not be + * performed. It is IMPDEF whether pushes above the limit are + * performed; we choose not to. + */ + qemu_log_mask(CPU_LOG_INT, + "...STKOF during callee-saves register stacking\n"); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + *frame_sp_p = limit; + return true; + } /* Write as much of the stack frame as we can. A write failure may * cause us to pend a derived exception. @@ -6811,10 +8035,7 @@ static bool v7m_push_callee_stack(ARMCPU *cpu, uint32_t lr, bool dotailchain, v7m_stack_write(cpu, frameptr + 0x24, env->regs[11], mmu_idx, ignore_faults); - /* Update SP regardless of whether any of the stack accesses failed. - * When we implement v8M stack limit checking then this attempt to - * update SP might also fail and result in a derived exception. - */ + /* Update SP regardless of whether any of the stack accesses failed. */ *frame_sp_p = frameptr; return !stacked_ok; @@ -6834,6 +8055,8 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, bool push_failed = false; armv7m_nvic_get_pending_irq_info(env->nvic, &exc, &targets_secure); + qemu_log_mask(CPU_LOG_INT, "...taking pending %s exception %d\n", + targets_secure ? "secure" : "nonsecure", exc); if (arm_feature(env, ARM_FEATURE_V8)) { if (arm_feature(env, ARM_FEATURE_M_SECURITY) && @@ -6858,7 +8081,7 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, * not already saved. */ if (lr & R_V7M_EXCRET_DCRS_MASK && - !(dotailchain && (lr & R_V7M_EXCRET_ES_MASK))) { + !(dotailchain && !(lr & R_V7M_EXCRET_ES_MASK))) { push_failed = v7m_push_callee_stack(cpu, lr, dotailchain, ignore_stackfaults); } @@ -6907,12 +8130,15 @@ static void v7m_exception_taken(ARMCPU *cpu, uint32_t lr, bool dotailchain, * we might now want to take a different exception which * targets a different security state, so try again from the top. */ + qemu_log_mask(CPU_LOG_INT, + "...derived exception on callee-saves register stacking"); v7m_exception_taken(cpu, lr, true, true); return; } if (!arm_v7m_load_vector(cpu, exc, targets_secure, &addr)) { /* Vector load failed: derived exception */ + qemu_log_mask(CPU_LOG_INT, "...derived exception on vector table load"); v7m_exception_taken(cpu, lr, true, true); return; } @@ -6946,7 +8172,7 @@ static bool v7m_push_stack(ARMCPU *cpu) CPUARMState *env = &cpu->env; uint32_t xpsr = xpsr_read(env); uint32_t frameptr = env->regs[13]; - ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); + ARMMMUIdx mmu_idx = arm_mmu_idx(env); /* Align stack pointer if the guest wants that */ if ((frameptr & 4) && @@ -6957,6 +8183,26 @@ static bool v7m_push_stack(ARMCPU *cpu) frameptr -= 0x20; + if (arm_feature(env, ARM_FEATURE_V8)) { + uint32_t limit = v7m_sp_limit(env); + + if (frameptr < limit) { + /* + * Stack limit failure: set SP to the limit value, and generate + * STKOF UsageFault. Stack pushes below the limit must not be + * performed. It is IMPDEF whether pushes above the limit are + * performed; we choose not to. + */ + qemu_log_mask(CPU_LOG_INT, + "...STKOF during stacking\n"); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, + env->v7m.secure); + env->regs[13] = limit; + return true; + } + } + /* Write as much of the stack frame as we can. If we fail a stack * write this will result in a derived exception being pended * (which may be taken in preference to the one we started with @@ -6972,10 +8218,7 @@ static bool v7m_push_stack(ARMCPU *cpu) v7m_stack_write(cpu, frameptr + 24, env->regs[15], mmu_idx, false) && v7m_stack_write(cpu, frameptr + 28, xpsr, mmu_idx, false); - /* Update SP regardless of whether any of the stack accesses failed. - * When we implement v8M stack limit checking then this attempt to - * update SP might also fail and result in a derived exception. - */ + /* Update SP regardless of whether any of the stack accesses failed. */ env->regs[13] = frameptr; return !stacked_ok; @@ -7041,6 +8284,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* For all other purposes, treat ES as 0 (R_HXSR) */ excret &= ~R_V7M_EXCRET_ES_MASK; } + exc_secure = excret & R_V7M_EXCRET_ES_MASK; } if (env->v7m.exception != ARMV7M_EXCP_NMI) { @@ -7051,7 +8295,6 @@ static void do_v7m_exception_exit(ARMCPU *cpu) * which security state's faultmask to clear. (v8M ARM ARM R_KBNF.) */ if (arm_feature(env, ARM_FEATURE_M_SECURITY)) { - exc_secure = excret & R_V7M_EXCRET_ES_MASK; if (armv7m_nvic_raw_execution_priority(env->nvic) >= 0) { env->v7m.faultmask[exc_secure] = 0; } @@ -7120,12 +8363,22 @@ static void do_v7m_exception_exit(ARMCPU *cpu) } } + /* + * Set CONTROL.SPSEL from excret.SPSEL. Since we're still in + * Handler mode (and will be until we write the new XPSR.Interrupt + * field) this does not switch around the current stack pointer. + * We must do this before we do any kind of tailchaining, including + * for the derived exceptions on integrity check failures, or we will + * give the guest an incorrect EXCRET.SPSEL value on exception entry. + */ + write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure); + if (sfault) { env->v7m.sfsr |= R_V7M_SFSR_INVER_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " "stackframe: failed EXC_RETURN.ES validity check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } @@ -7135,17 +8388,27 @@ static void do_v7m_exception_exit(ARMCPU *cpu) */ env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " "stackframe: failed exception return integrity check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } - /* Set CONTROL.SPSEL from excret.SPSEL. Since we're still in - * Handler mode (and will be until we write the new XPSR.Interrupt - * field) this does not switch around the current stack pointer. + /* + * Tailchaining: if there is currently a pending exception that + * is high enough priority to preempt execution at the level we're + * about to return to, then just directly take that exception now, + * avoiding an unstack-and-then-stack. Note that now we have + * deactivated the previous exception by calling armv7m_nvic_complete_irq() + * our current execution priority is already the execution priority we are + * returning to -- none of the state we would unstack or set based on + * the EXCRET value affects it. */ - write_v7m_control_spsel_for_secstate(env, return_to_sp_process, exc_secure); + if (armv7m_nvic_can_take_pending_exception(env->nvic)) { + qemu_log_mask(CPU_LOG_INT, "...tailchaining to pending exception\n"); + v7m_exception_taken(cpu, excret, true, false); + return; + } switch_v7m_security_state(env, return_to_secure); @@ -7192,15 +8455,14 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* Take a SecureFault on the current stack */ env->v7m.sfsr |= R_V7M_SFSR_INVIS_MASK; armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SECURE, false); - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking SecureFault on existing " "stackframe: failed exception return integrity " "signature check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } pop_ok = pop_ok && - v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && v7m_stack_read(cpu, &env->regs[4], frameptr + 0x8, mmu_idx) && v7m_stack_read(cpu, &env->regs[5], frameptr + 0xc, mmu_idx) && v7m_stack_read(cpu, &env->regs[6], frameptr + 0x10, mmu_idx) && @@ -7228,6 +8490,7 @@ static void do_v7m_exception_exit(ARMCPU *cpu) /* v7m_stack_read() pended a fault, so take it (as a tail * chained exception on the same stack frame) */ + qemu_log_mask(CPU_LOG_INT, "...derived exception on unstacking\n"); v7m_exception_taken(cpu, excret, true, false); return; } @@ -7264,10 +8527,10 @@ static void do_v7m_exception_exit(ARMCPU *cpu) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; - v7m_exception_taken(cpu, excret, true, false); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on existing " "stackframe: failed exception return integrity " "check\n"); + v7m_exception_taken(cpu, excret, true, false); return; } } @@ -7303,9 +8566,9 @@ static void do_v7m_exception_exit(ARMCPU *cpu) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, false); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVPC_MASK; ignore_stackfaults = v7m_push_stack(cpu); - v7m_exception_taken(cpu, excret, false, ignore_stackfaults); qemu_log_mask(CPU_LOG_INT, "...taking UsageFault on new stackframe: " "failed exception return integrity check\n"); + v7m_exception_taken(cpu, excret, false, ignore_stackfaults); return; } @@ -7408,6 +8671,7 @@ static void arm_log_exception(int idx) [EXCP_SEMIHOST] = "Semihosting call", [EXCP_NOCP] = "v7M NOCP UsageFault", [EXCP_INVSTATE] = "v7M INVSTATE UsageFault", + [EXCP_STKOF] = "v8M STKOF UsageFault", }; if (idx >= 0 && idx < ARRAY_SIZE(excnames)) { @@ -7563,6 +8827,10 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_INVSTATE_MASK; break; + case EXCP_STKOF: + armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_USAGE, env->v7m.secure); + env->v7m.cfsr[env->v7m.secure] |= R_V7M_CFSR_STKOF_MASK; + break; case EXCP_SWI: /* The PC already points to the next instruction. */ armv7m_nvic_set_pending(env->nvic, ARMV7M_EXCP_SVC, env->v7m.secure); @@ -7721,7 +8989,6 @@ void arm_v7m_cpu_do_interrupt(CPUState *cs) ignore_stackfaults = v7m_push_stack(cpu); v7m_exception_taken(cpu, lr, false, ignore_stackfaults); - qemu_log_mask(CPU_LOG_INT, "... as %d\n", env->v7m.exception); } /* Function used to synchronize QEMU's AArch64 register set with AArch32 @@ -7764,7 +9031,7 @@ void aarch64_sync_32_to_64(CPUARMState *env) if (mode == ARM_CPU_MODE_HYP) { env->xregs[14] = env->regs[14]; } else { - env->xregs[14] = env->banked_r14[bank_number(ARM_CPU_MODE_USR)]; + env->xregs[14] = env->banked_r14[r14_bank_number(ARM_CPU_MODE_USR)]; } } @@ -7778,7 +9045,7 @@ void aarch64_sync_32_to_64(CPUARMState *env) env->xregs[16] = env->regs[14]; env->xregs[17] = env->regs[13]; } else { - env->xregs[16] = env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)]; + env->xregs[16] = env->banked_r14[r14_bank_number(ARM_CPU_MODE_IRQ)]; env->xregs[17] = env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)]; } @@ -7786,7 +9053,7 @@ void aarch64_sync_32_to_64(CPUARMState *env) env->xregs[18] = env->regs[14]; env->xregs[19] = env->regs[13]; } else { - env->xregs[18] = env->banked_r14[bank_number(ARM_CPU_MODE_SVC)]; + env->xregs[18] = env->banked_r14[r14_bank_number(ARM_CPU_MODE_SVC)]; env->xregs[19] = env->banked_r13[bank_number(ARM_CPU_MODE_SVC)]; } @@ -7794,7 +9061,7 @@ void aarch64_sync_32_to_64(CPUARMState *env) env->xregs[20] = env->regs[14]; env->xregs[21] = env->regs[13]; } else { - env->xregs[20] = env->banked_r14[bank_number(ARM_CPU_MODE_ABT)]; + env->xregs[20] = env->banked_r14[r14_bank_number(ARM_CPU_MODE_ABT)]; env->xregs[21] = env->banked_r13[bank_number(ARM_CPU_MODE_ABT)]; } @@ -7802,7 +9069,7 @@ void aarch64_sync_32_to_64(CPUARMState *env) env->xregs[22] = env->regs[14]; env->xregs[23] = env->regs[13]; } else { - env->xregs[22] = env->banked_r14[bank_number(ARM_CPU_MODE_UND)]; + env->xregs[22] = env->banked_r14[r14_bank_number(ARM_CPU_MODE_UND)]; env->xregs[23] = env->banked_r13[bank_number(ARM_CPU_MODE_UND)]; } @@ -7819,7 +9086,7 @@ void aarch64_sync_32_to_64(CPUARMState *env) env->xregs[i] = env->fiq_regs[i - 24]; } env->xregs[29] = env->banked_r13[bank_number(ARM_CPU_MODE_FIQ)]; - env->xregs[30] = env->banked_r14[bank_number(ARM_CPU_MODE_FIQ)]; + env->xregs[30] = env->banked_r14[r14_bank_number(ARM_CPU_MODE_FIQ)]; } env->pc = env->regs[15]; @@ -7869,7 +9136,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) if (mode == ARM_CPU_MODE_HYP) { env->regs[14] = env->xregs[14]; } else { - env->banked_r14[bank_number(ARM_CPU_MODE_USR)] = env->xregs[14]; + env->banked_r14[r14_bank_number(ARM_CPU_MODE_USR)] = env->xregs[14]; } } @@ -7883,7 +9150,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[14] = env->xregs[16]; env->regs[13] = env->xregs[17]; } else { - env->banked_r14[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[16]; + env->banked_r14[r14_bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[16]; env->banked_r13[bank_number(ARM_CPU_MODE_IRQ)] = env->xregs[17]; } @@ -7891,7 +9158,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[14] = env->xregs[18]; env->regs[13] = env->xregs[19]; } else { - env->banked_r14[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[18]; + env->banked_r14[r14_bank_number(ARM_CPU_MODE_SVC)] = env->xregs[18]; env->banked_r13[bank_number(ARM_CPU_MODE_SVC)] = env->xregs[19]; } @@ -7899,7 +9166,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[14] = env->xregs[20]; env->regs[13] = env->xregs[21]; } else { - env->banked_r14[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[20]; + env->banked_r14[r14_bank_number(ARM_CPU_MODE_ABT)] = env->xregs[20]; env->banked_r13[bank_number(ARM_CPU_MODE_ABT)] = env->xregs[21]; } @@ -7907,7 +9174,7 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->regs[14] = env->xregs[22]; env->regs[13] = env->xregs[23]; } else { - env->banked_r14[bank_number(ARM_CPU_MODE_UND)] = env->xregs[22]; + env->banked_r14[r14_bank_number(ARM_CPU_MODE_UND)] = env->xregs[22]; env->banked_r13[bank_number(ARM_CPU_MODE_UND)] = env->xregs[23]; } @@ -7924,12 +9191,144 @@ void aarch64_sync_64_to_32(CPUARMState *env) env->fiq_regs[i - 24] = env->xregs[i]; } env->banked_r13[bank_number(ARM_CPU_MODE_FIQ)] = env->xregs[29]; - env->banked_r14[bank_number(ARM_CPU_MODE_FIQ)] = env->xregs[30]; + env->banked_r14[r14_bank_number(ARM_CPU_MODE_FIQ)] = env->xregs[30]; } env->regs[15] = env->pc; } +static void take_aarch32_exception(CPUARMState *env, int new_mode, + uint32_t mask, uint32_t offset, + uint32_t newpc) +{ + /* Change the CPU state so as to actually take the exception. */ + switch_mode(env, new_mode); + /* + * For exceptions taken to AArch32 we must clear the SS bit in both + * PSTATE and in the old-state value we save to SPSR_, so zero it now. + */ + env->uncached_cpsr &= ~PSTATE_SS; + env->spsr = cpsr_read(env); + /* Clear IT bits. */ + 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; + } + /* J and IL must always be cleared for exception entry */ + env->uncached_cpsr &= ~(CPSR_IL | CPSR_J); + env->daif |= mask; + + if (new_mode == ARM_CPU_MODE_HYP) { + env->thumb = (env->cp15.sctlr_el[2] & SCTLR_TE) != 0; + env->elr_el[2] = env->regs[15]; + } else { + /* + * this is a lie, as there was no c1_sys on V4T/V5, but who cares + * and we should just guard the thumb mode on V4 + */ + if (arm_feature(env, ARM_FEATURE_V4T)) { + env->thumb = + (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; + } + env->regs[14] = env->regs[15] + offset; + } + env->regs[15] = newpc; +} + +static void arm_cpu_do_interrupt_aarch32_hyp(CPUState *cs) +{ + /* + * Handle exception entry to Hyp mode; this is sufficiently + * different to entry to other AArch32 modes that we handle it + * separately here. + * + * The vector table entry used is always the 0x14 Hyp mode entry point, + * unless this is an UNDEF/HVC/abort taken from Hyp to Hyp. + * The offset applied to the preferred return address is always zero + * (see DDI0487C.a section G1.12.3). + * PSTATE A/I/F masks are set based only on the SCR.EA/IRQ/FIQ values. + */ + uint32_t addr, mask; + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + switch (cs->exception_index) { + case EXCP_UDEF: + addr = 0x04; + break; + case EXCP_SWI: + addr = 0x14; + break; + case EXCP_BKPT: + /* Fall through to prefetch abort. */ + case EXCP_PREFETCH_ABORT: + env->cp15.ifar_s = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with HIFAR 0x%x\n", + (uint32_t)env->exception.vaddress); + addr = 0x0c; + break; + case EXCP_DATA_ABORT: + env->cp15.dfar_s = env->exception.vaddress; + qemu_log_mask(CPU_LOG_INT, "...with HDFAR 0x%x\n", + (uint32_t)env->exception.vaddress); + addr = 0x10; + break; + case EXCP_IRQ: + addr = 0x18; + break; + case EXCP_FIQ: + addr = 0x1c; + break; + case EXCP_HVC: + addr = 0x08; + break; + case EXCP_HYP_TRAP: + addr = 0x14; + default: + cpu_abort(cs, "Unhandled exception 0x%x\n", cs->exception_index); + } + + if (cs->exception_index != EXCP_IRQ && cs->exception_index != EXCP_FIQ) { + if (!arm_feature(env, ARM_FEATURE_V8)) { + /* + * QEMU syndrome values are v8-style. v7 has the IL bit + * UNK/SBZP for "field not valid" cases, where v8 uses RES1. + * If this is a v7 CPU, squash the IL bit in those cases. + */ + if (cs->exception_index == EXCP_PREFETCH_ABORT || + (cs->exception_index == EXCP_DATA_ABORT && + !(env->exception.syndrome & ARM_EL_ISV)) || + syn_get_ec(env->exception.syndrome) == EC_UNCATEGORIZED) { + env->exception.syndrome &= ~ARM_EL_IL; + } + } + env->cp15.esr_el[2] = env->exception.syndrome; + } + + if (arm_current_el(env) != 2 && addr < 0x14) { + addr = 0x14; + } + + mask = 0; + if (!(env->cp15.scr_el3 & SCR_EA)) { + mask |= CPSR_A; + } + if (!(env->cp15.scr_el3 & SCR_IRQ)) { + mask |= CPSR_I; + } + if (!(env->cp15.scr_el3 & SCR_FIQ)) { + mask |= CPSR_F; + } + + addr += env->cp15.hvbar; + + take_aarch32_exception(env, ARM_CPU_MODE_HYP, mask, 0, addr); +} + static void arm_cpu_do_interrupt_aarch32(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); @@ -7941,7 +9340,7 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) uint32_t moe; /* If this is a debug exception we must update the DBGDSCR.MOE bits */ - switch (env->exception.syndrome >> ARM_EL_EC_SHIFT) { + switch (syn_get_ec(env->exception.syndrome)) { case EC_BREAKPOINT: case EC_BREAKPOINT_SAME_EL: moe = 1; @@ -7965,7 +9364,11 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->cp15.mdscr_el1 = deposit64(env->cp15.mdscr_el1, 2, 4, moe); } - /* TODO: Vectored interrupt controller. */ + if (env->exception.target_el == 2) { + arm_cpu_do_interrupt_aarch32_hyp(cs); + return; + } + switch (cs->exception_index) { case EXCP_UDEF: new_mode = ARM_CPU_MODE_UND; @@ -8072,29 +9475,7 @@ static void arm_cpu_do_interrupt_aarch32(CPUState *cs) env->cp15.scr_el3 &= ~SCR_NS; } - switch_mode (env, new_mode); - /* For exceptions taken to AArch32 we must clear the SS bit in both - * PSTATE and in the old-state value we save to SPSR_, so zero it now. - */ - env->uncached_cpsr &= ~PSTATE_SS; - env->spsr = cpsr_read(env); - /* Clear IT bits. */ - 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 */ - if (arm_feature(env, ARM_FEATURE_V4T)) { - env->thumb = (A32_BANKED_CURRENT_REG_GET(env, sctlr) & SCTLR_TE) != 0; - } - env->regs[14] = env->regs[15] + offset; - env->regs[15] = addr; + take_aarch32_exception(env, new_mode, mask, offset, addr); } /* Handle exception entry to a target EL which is using AArch64 */ @@ -8105,8 +9486,15 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) 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); + unsigned int cur_el = arm_current_el(env); - if (arm_current_el(env) < new_el) { + /* + * Note that new_el can never be 0. If cur_el is 0, then + * el0_a64 is is_a64(), else el0_a64 is ignored. + */ + aarch64_sve_change_el(env, cur_el, new_el, is_a64(env)); + + if (cur_el < new_el) { /* Entry vector offset depends on whether the implemented EL * immediately lower than the target level is using AArch32 or AArch64 */ @@ -8148,6 +9536,15 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) case EXCP_HVC: case EXCP_HYP_TRAP: case EXCP_SMC: + if (syn_get_ec(env->exception.syndrome) == EC_ADVSIMDFPACCESSTRAP) { + /* + * QEMU internal FP/SIMD syndromes from AArch32 include the + * TA and coproc fields which are only exposed if the exception + * is taken to AArch32 Hyp mode. Mask them out to get a valid + * AArch64 format syndrome. + */ + env->exception.syndrome &= ~MAKE_64BIT_MASK(0, 20); + } env->cp15.esr_el[new_el] = env->exception.syndrome; break; case EXCP_IRQ: @@ -8291,7 +9688,7 @@ void arm_cpu_do_interrupt(CPUState *cs) if (qemu_loglevel_mask(CPU_LOG_INT) && !excp_is_internal(cs->exception_index)) { qemu_log_mask(CPU_LOG_INT, "...with ESR 0x%x/0x%" PRIx32 "\n", - env->exception.syndrome >> ARM_EL_EC_SHIFT, + syn_get_ec(env->exception.syndrome), env->exception.syndrome); } @@ -8330,6 +9727,7 @@ void arm_cpu_do_interrupt(CPUState *cs) cs->interrupt_request |= CPU_INTERRUPT_EXITTB; } } +#endif /* !CONFIG_USER_ONLY */ /* Return the exception level which controls this address translation regime */ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) @@ -8359,6 +9757,8 @@ static inline uint32_t regime_el(CPUARMState *env, ARMMMUIdx mmu_idx) } } +#ifndef CONFIG_USER_ONLY + /* Return the SCTLR value which controls this address translation regime */ static inline uint32_t regime_sctlr(CPUARMState *env, ARMMMUIdx mmu_idx) { @@ -8388,8 +9788,23 @@ static inline bool regime_translation_disabled(CPUARMState *env, } if (mmu_idx == ARMMMUIdx_S2NS) { - return (env->cp15.hcr_el2 & HCR_VM) == 0; + /* HCR.DC means HCR.VM behaves as 1 */ + return (env->cp15.hcr_el2 & (HCR_DC | HCR_VM)) == 0; } + + if (env->cp15.hcr_el2 & HCR_TGE) { + /* TGE means that NS EL0/1 act as if SCTLR_EL1.M is zero */ + if (!regime_is_secure(env, mmu_idx) && regime_el(env, mmu_idx) == 1) { + return true; + } + } + + if ((env->cp15.hcr_el2 & HCR_DC) && + (mmu_idx == ARMMMUIdx_S1NSE0 || mmu_idx == ARMMMUIdx_S1NSE1)) { + /* HCR.DC means SCTLR_EL1.M behaves as 0 */ + return true; + } + return (regime_sctlr(env, mmu_idx) & SCTLR_M) == 0; } @@ -8399,6 +9814,22 @@ static inline bool regime_translation_big_endian(CPUARMState *env, return (regime_sctlr(env, mmu_idx) & SCTLR_EE) != 0; } +/* Return the TTBR associated with this translation regime */ +static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, + int ttbrn) +{ + if (mmu_idx == ARMMMUIdx_S2NS) { + return env->cp15.vttbr_el2; + } + if (ttbrn == 0) { + return env->cp15.ttbr0_el[regime_el(env, mmu_idx)]; + } else { + return env->cp15.ttbr1_el[regime_el(env, mmu_idx)]; + } +} + +#endif /* !CONFIG_USER_ONLY */ + /* Return the TCR controlling this translation regime */ static inline TCR *regime_tcr(CPUARMState *env, ARMMMUIdx mmu_idx) { @@ -8419,62 +9850,6 @@ static inline ARMMMUIdx stage_1_mmu_idx(ARMMMUIdx mmu_idx) return mmu_idx; } -/* Returns TBI0 value for current regime el */ -uint32_t arm_regime_tbi0(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - TCR *tcr; - uint32_t el; - - /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert - * a stage 1+2 mmu index into the appropriate stage 1 mmu index. - */ - mmu_idx = stage_1_mmu_idx(mmu_idx); - - tcr = regime_tcr(env, mmu_idx); - el = regime_el(env, mmu_idx); - - if (el > 1) { - return extract64(tcr->raw_tcr, 20, 1); - } else { - return extract64(tcr->raw_tcr, 37, 1); - } -} - -/* Returns TBI1 value for current regime el */ -uint32_t arm_regime_tbi1(CPUARMState *env, ARMMMUIdx mmu_idx) -{ - TCR *tcr; - uint32_t el; - - /* For EL0 and EL1, TBI is controlled by stage 1's TCR, so convert - * a stage 1+2 mmu index into the appropriate stage 1 mmu index. - */ - mmu_idx = stage_1_mmu_idx(mmu_idx); - - tcr = regime_tcr(env, mmu_idx); - el = regime_el(env, mmu_idx); - - if (el > 1) { - return 0; - } else { - return extract64(tcr->raw_tcr, 38, 1); - } -} - -/* Return the TTBR associated with this translation regime */ -static inline uint64_t regime_ttbr(CPUARMState *env, ARMMMUIdx mmu_idx, - int ttbrn) -{ - if (mmu_idx == ARMMMUIdx_S2NS) { - return env->cp15.vttbr_el2; - } - if (ttbrn == 0) { - return env->cp15.ttbr0_el[regime_el(env, mmu_idx)]; - } else { - return env->cp15.ttbr1_el[regime_el(env, mmu_idx)]; - } -} - /* Return true if the translation regime is using LPAE format page tables */ static inline bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) @@ -8500,6 +9875,7 @@ bool arm_s1_regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) return regime_using_lpae_format(env, mmu_idx); } +#ifndef CONFIG_USER_ONLY static inline bool regime_is_user(CPUARMState *env, ARMMMUIdx mmu_idx) { switch (mmu_idx) { @@ -8741,9 +10117,20 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, hwaddr s2pa; int s2prot; int ret; + ARMCacheAttrs cacheattrs = {}; + ARMCacheAttrs *pcacheattrs = NULL; + + if (env->cp15.hcr_el2 & HCR_PTW) { + /* + * PTW means we must fault if this S1 walk touches S2 Device + * memory; otherwise we don't care about the attributes and can + * save the S2 translation the effort of computing them. + */ + pcacheattrs = &cacheattrs; + } ret = get_phys_addr_lpae(env, addr, 0, ARMMMUIdx_S2NS, &s2pa, - &txattrs, &s2prot, &s2size, fi, NULL); + &txattrs, &s2prot, &s2size, fi, pcacheattrs); if (ret) { assert(fi->type != ARMFault_None); fi->s2addr = addr; @@ -8751,6 +10138,14 @@ static hwaddr S1_ptw_translate(CPUARMState *env, ARMMMUIdx mmu_idx, fi->s1ptw = true; return ~0; } + if (pcacheattrs && (pcacheattrs->attrs & 0xf0) == 0) { + /* Access was to Device memory: generate Permission fault */ + fi->type = ARMFault_Permission; + fi->s2addr = addr; + fi->stage2 = true; + fi->s1ptw = true; + return ~0; + } addr = s2pa; } return addr; @@ -9186,6 +10581,140 @@ static uint8_t convert_stage2_attrs(CPUARMState *env, uint8_t s2attrs) return (hiattr << 6) | (hihint << 4) | (loattr << 2) | lohint; } +#endif /* !CONFIG_USER_ONLY */ + +ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va, + ARMMMUIdx mmu_idx) +{ + uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint32_t el = regime_el(env, mmu_idx); + bool tbi, tbid, epd, hpd, using16k, using64k; + int select, tsz; + + /* + * Bit 55 is always between the two regions, and is canonical for + * determining if address tagging is enabled. + */ + select = extract64(va, 55, 1); + + if (el > 1) { + tsz = extract32(tcr, 0, 6); + using64k = extract32(tcr, 14, 1); + using16k = extract32(tcr, 15, 1); + if (mmu_idx == ARMMMUIdx_S2NS) { + /* VTCR_EL2 */ + tbi = tbid = hpd = false; + } else { + tbi = extract32(tcr, 20, 1); + hpd = extract32(tcr, 24, 1); + tbid = extract32(tcr, 29, 1); + } + epd = false; + } else if (!select) { + tsz = extract32(tcr, 0, 6); + epd = extract32(tcr, 7, 1); + using64k = extract32(tcr, 14, 1); + using16k = extract32(tcr, 15, 1); + tbi = extract64(tcr, 37, 1); + hpd = extract64(tcr, 41, 1); + tbid = extract64(tcr, 51, 1); + } else { + int tg = extract32(tcr, 30, 2); + using16k = tg == 1; + using64k = tg == 3; + tsz = extract32(tcr, 16, 6); + epd = extract32(tcr, 23, 1); + tbi = extract64(tcr, 38, 1); + hpd = extract64(tcr, 42, 1); + tbid = extract64(tcr, 52, 1); + } + tsz = MIN(tsz, 39); /* TODO: ARMv8.4-TTST */ + tsz = MAX(tsz, 16); /* TODO: ARMv8.2-LVA */ + + return (ARMVAParameters) { + .tsz = tsz, + .select = select, + .tbi = tbi, + .tbid = tbid, + .epd = epd, + .hpd = hpd, + .using16k = using16k, + .using64k = using64k, + }; +} + +ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, + ARMMMUIdx mmu_idx, bool data) +{ + ARMVAParameters ret = aa64_va_parameters_both(env, va, mmu_idx); + + /* Present TBI as a composite with TBID. */ + ret.tbi &= (data || !ret.tbid); + return ret; +} + +#ifndef CONFIG_USER_ONLY +static ARMVAParameters aa32_va_parameters(CPUARMState *env, uint32_t va, + ARMMMUIdx mmu_idx) +{ + uint64_t tcr = regime_tcr(env, mmu_idx)->raw_tcr; + uint32_t el = regime_el(env, mmu_idx); + int select, tsz; + bool epd, hpd; + + if (mmu_idx == ARMMMUIdx_S2NS) { + /* VTCR */ + bool sext = extract32(tcr, 4, 1); + bool sign = extract32(tcr, 3, 1); + + /* + * 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] mismatch\n"); + } + tsz = sextract32(tcr, 0, 4) + 8; + select = 0; + hpd = false; + epd = false; + } else if (el == 2) { + /* HTCR */ + tsz = extract32(tcr, 0, 3); + select = 0; + hpd = extract64(tcr, 24, 1); + epd = false; + } else { + int t0sz = extract32(tcr, 0, 3); + int t1sz = extract32(tcr, 16, 3); + + if (t1sz == 0) { + select = va > (0xffffffffu >> t0sz); + } else { + /* Note that we will detect errors later. */ + select = va >= ~(0xffffffffu >> t1sz); + } + if (!select) { + tsz = t0sz; + epd = extract32(tcr, 7, 1); + hpd = extract64(tcr, 41, 1); + } else { + tsz = t1sz; + epd = extract32(tcr, 23, 1); + hpd = extract64(tcr, 42, 1); + } + /* For aarch32, hpd0 is not enabled without t2e as well. */ + hpd &= extract32(tcr, 6, 1); + } + + return (ARMVAParameters) { + .tsz = tsz, + .select = select, + .epd = epd, + .hpd = hpd, + }; +} static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, MMUAccessType access_type, ARMMMUIdx mmu_idx, @@ -9198,25 +10727,21 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, /* Read an LPAE long-descriptor translation table. */ ARMFaultType fault_type = ARMFault_Translation; uint32_t level; - uint32_t epd = 0; - int32_t t0sz, t1sz; - uint32_t tg; + ARMVAParameters param; uint64_t ttbr; - int ttbr_select; hwaddr descaddr, indexmask, indexmask_grainsize; uint32_t tableattrs; target_ulong page_size; uint32_t attrs; - int32_t stride = 9; - int32_t addrsize; - int inputsize; - int32_t tbi = 0; + int32_t stride; + int addrsize, inputsize; TCR *tcr = regime_tcr(env, mmu_idx); int ap, ns, xn, pxn; uint32_t el = regime_el(env, mmu_idx); - bool ttbr1_valid = true; + bool ttbr1_valid; uint64_t descaddrmask; bool aarch64 = arm_el_is_aa64(env, el); + bool guarded = false; /* TODO: * This code does not handle the different format TCR for VTCR_EL2. @@ -9225,89 +10750,49 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, * support for those page table walks. */ if (aarch64) { + param = aa64_va_parameters(env, address, mmu_idx, + access_type != MMU_INST_FETCH); level = 0; - addrsize = 64; - if (el > 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); - } else { - tbi = extract64(tcr->raw_tcr, 37, 1); - } - } - tbi *= 8; - /* If we are in 64-bit EL2 or EL3 then there is no TTBR1, so mark it * invalid. */ - if (el > 1) { - ttbr1_valid = false; - } + ttbr1_valid = (el < 2); + addrsize = 64 - 8 * param.tbi; + inputsize = 64 - param.tsz; } else { + param = aa32_va_parameters(env, address, mmu_idx); level = 1; - addrsize = 32; /* There is no TTBR1 for EL2 */ - if (el == 2) { - ttbr1_valid = false; - } + ttbr1_valid = (el != 2); + addrsize = (mmu_idx == ARMMMUIdx_S2NS ? 40 : 32); + inputsize = addrsize - param.tsz; } - /* Determine whether this address is in the region controlled by - * TTBR0 or TTBR1 (or if it is in neither region and should fault). - * 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: + /* + * We determined the region when collecting the parameters, but we + * have not yet validated that the address is valid for the region. + * Extract the top bits and verify that they all match select. + * + * For aa32, if inputsize == addrsize, then we have selected the + * region by exclusion in aa32_va_parameters and there is no more + * validation to do here. */ - if (aarch64) { - /* 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); - /* Address size is 40-bit for a stage 2 translation, - * and t0sz can be negative (from -8 to 7), - * so we need to adjust it to use the TTBR selecting logic below. - */ - addrsize = 40; - t0sz = sextract32(tcr->raw_tcr, 0, 4) + 8; - - /* 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] mismatch\n"); + if (inputsize < addrsize) { + target_ulong top_bits = sextract64(address, inputsize, + addrsize - inputsize); + if (-top_bits != param.select || (param.select && !ttbr1_valid)) { + /* The gap between the two regions is a Translation fault */ + fault_type = ARMFault_Translation; + goto do_fault; } } - t1sz = extract32(tcr->raw_tcr, 16, 6); - if (aarch64) { - t1sz = MIN(t1sz, 39); - t1sz = MAX(t1sz, 16); - } - if (t0sz && !extract64(address, addrsize - t0sz, t0sz - tbi)) { - /* there is a ttbr0 region and we are in it (high bits all zero) */ - ttbr_select = 0; - } else if (ttbr1_valid && t1sz && - !extract64(~address, addrsize - t1sz, t1sz - tbi)) { - /* there is a ttbr1 region and we are in it (high bits all one) */ - ttbr_select = 1; - } else if (!t0sz) { - /* ttbr0 region is "everything not in the ttbr1 region" */ - ttbr_select = 0; - } else if (!t1sz && ttbr1_valid) { - /* ttbr1 region is "everything not in the ttbr0 region" */ - ttbr_select = 1; + + if (param.using64k) { + stride = 13; + } else if (param.using16k) { + stride = 11; } else { - /* in the gap between the two regions, this is a Translation fault */ - fault_type = ARMFault_Translation; - goto do_fault; + stride = 9; } /* Note that QEMU ignores shareability and cacheability attributes, @@ -9317,42 +10802,13 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, * implement any ASID-like capability so we can ignore it (instead * we will always flush the TLB any time the ASID is changed). */ - if (ttbr_select == 0) { - ttbr = regime_ttbr(env, mmu_idx, 0); - if (el < 2) { - epd = extract32(tcr->raw_tcr, 7, 1); - } - inputsize = addrsize - t0sz; - - tg = extract32(tcr->raw_tcr, 14, 2); - if (tg == 1) { /* 64KB pages */ - stride = 13; - } - if (tg == 2) { /* 16KB pages */ - stride = 11; - } - } else { - /* We should only be here if TTBR1 is valid */ - assert(ttbr1_valid); - - ttbr = regime_ttbr(env, mmu_idx, 1); - epd = extract32(tcr->raw_tcr, 23, 1); - inputsize = addrsize - t1sz; - - tg = extract32(tcr->raw_tcr, 30, 2); - if (tg == 3) { /* 64KB pages */ - stride = 13; - } - if (tg == 1) { /* 16KB pages */ - stride = 11; - } - } + ttbr = regime_ttbr(env, mmu_idx, param.select); /* Here we should have set up all the parameters for the translation: * inputsize, ttbr, epd, stride, tbi */ - if (epd) { + if (param.epd) { /* Translation table walk disabled => Translation fault on TLB miss * Note: This is always 0 on 64-bit EL2 and EL3. */ @@ -9439,7 +10895,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, descaddr = descriptor & descaddrmask; if ((descriptor & 2) && (level < 3)) { - /* Table entry. The top five bits are attributes which may + /* Table entry. The top five bits are attributes which may * propagate down through lower levels of the table (and * which are all arranged so that 0 means "no effect", so * we can gather them up by ORing in the bits at each level). @@ -9464,15 +10920,18 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, 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] */ + attrs |= nstable << 3; /* NS */ + guarded = extract64(descriptor, 50, 1); /* GP */ + if (param.hpd) { + /* HPD disables all the table attributes except NSTable. */ + break; + } + attrs |= extract32(tableattrs, 0, 2) << 11; /* XN, PXN */ /* The sense of AP[1] vs APTable[0] is reversed, as APTable[0] == 1 * means "force PL1 access only", which means forcing AP[1] to 0. */ - if (extract32(tableattrs, 2, 1)) { - attrs &= ~(1 << 4); - } - attrs |= nstable << 3; /* NS */ + attrs &= ~(extract32(tableattrs, 2, 1) << 4); /* !APT[0] => AP[1] */ + attrs |= extract32(tableattrs, 3, 1) << 5; /* APT[1] => AP[2] */ break; } /* Here descaddr is the final physical address, and attributes @@ -9508,6 +10967,10 @@ static bool get_phys_addr_lpae(CPUARMState *env, target_ulong address, */ txattrs->secure = false; } + /* When in aarch64 mode, and BTI is enabled, remember GP in the IOTLB. */ + if (aarch64 && guarded && cpu_isar_feature(aa64_bti, cpu)) { + txattrs->target_tlb_bit0 = true; + } if (cacheattrs != NULL) { if (mmu_idx == ARMMMUIdx_S2NS) { @@ -9795,17 +11258,6 @@ static bool get_phys_addr_pmsav7(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; - /* - * Core QEMU code can't handle execution from small pages yet, so - * don't try it. This way we'll get an MPU exception, rather than - * eventually causing QEMU to exit in get_page_addr_code(). - */ - if (*page_size < TARGET_PAGE_SIZE && (*prot & PAGE_EXEC)) { - qemu_log_mask(LOG_UNIMP, - "MPU: No support for execution from regions " - "smaller than 1K\n"); - *prot &= ~PAGE_EXEC; - } return !(*prot & (1 << access_type)); } @@ -9916,18 +11368,19 @@ static void v8m_security_lookup(CPUARMState *env, uint32_t address, } } } - - /* The IDAU will override the SAU lookup results if it specifies - * higher security than the SAU does. - */ - if (!idau_ns) { - if (sattrs->ns || (!idau_nsc && sattrs->nsc)) { - sattrs->ns = false; - sattrs->nsc = idau_nsc; - } - } break; } + + /* + * The IDAU will override the SAU lookup results if it specifies + * higher security than the SAU does. + */ + if (!idau_ns) { + if (sattrs->ns || (!idau_nsc && sattrs->nsc)) { + sattrs->ns = false; + sattrs->nsc = idau_nsc; + } + } } static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, @@ -9970,9 +11423,11 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, hit = true; } else if (m_is_ppb_region(env, address)) { hit = true; - } else if (pmsav7_use_background_region(cpu, mmu_idx, is_user)) { - hit = true; } else { + if (pmsav7_use_background_region(cpu, mmu_idx, is_user)) { + hit = true; + } + for (n = (int)cpu->pmsav7_dregion - 1; n >= 0; n--) { /* region search */ /* Note that the base address is bits [31:5] from the register @@ -10010,7 +11465,7 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, *is_subpage = true; } - if (hit) { + if (matchregion != -1) { /* Multiple regions match -- always a failure (unlike * PMSAv7 where highest-numbered-region wins) */ @@ -10056,18 +11511,6 @@ static bool pmsav8_mpu_lookup(CPUARMState *env, uint32_t address, fi->type = ARMFault_Permission; fi->level = 1; - /* - * Core QEMU code can't handle execution from small pages yet, so - * don't try it. This means any attempted execution will generate - * an MPU exception, rather than eventually causing QEMU to exit in - * get_page_addr_code(). - */ - if (*is_subpage && (*prot & PAGE_EXEC)) { - qemu_log_mask(LOG_UNIMP, - "MPU: No support for execution from regions " - "smaller than 1K\n"); - *prot &= ~PAGE_EXEC; - } return !(*prot & (1 << access_type)); } @@ -10142,18 +11585,6 @@ static bool get_phys_addr_pmsav8(CPUARMState *env, uint32_t address, ret = pmsav8_mpu_lookup(env, address, access_type, mmu_idx, phys_ptr, txattrs, prot, &mpu_is_subpage, fi, NULL); - /* - * TODO: this is a temporary hack to ignore the fact that the SAU region - * is smaller than a page if this is an executable region. We never - * supported small MPU regions, but we did (accidentally) allow small - * SAU regions, and if we now made small SAU regions not be executable - * then this would break previously working guest code. We can't - * remove this until/unless we implement support for execution from - * small regions. - */ - if (*prot & PAGE_EXEC) { - sattrs.subpage = false; - } *page_size = sattrs.subpage || mpu_is_subpage ? 1 : TARGET_PAGE_SIZE; return ret; } @@ -10393,6 +11824,16 @@ static bool get_phys_addr(CPUARMState *env, target_ulong address, /* Combine the S1 and S2 cache attributes, if needed */ if (!ret && cacheattrs != NULL) { + if (env->cp15.hcr_el2 & HCR_DC) { + /* + * HCR.DC forces the first stage attributes to + * Normal Non-Shareable, + * Inner Write-Back Read-Allocate Write-Allocate, + * Outer Write-Back Read-Allocate Write-Allocate. + */ + cacheattrs->attrs = 0xff; + cacheattrs->shareability = 0; + } *cacheattrs = combine_cacheattrs(*cacheattrs, cacheattrs2); } @@ -10524,7 +11965,7 @@ hwaddr arm_cpu_get_phys_page_attrs_debug(CPUState *cs, vaddr addr, int prot; bool ret; ARMMMUFaultInfo fi = {}; - ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); + ARMMMUIdx mmu_idx = arm_mmu_idx(env); *attrs = (MemTxAttrs) {}; @@ -10710,13 +12151,13 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) env->v7m.primask[M_REG_NS] = val & 1; return; case 0x91: /* BASEPRI_NS */ - if (!env->v7m.secure) { + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { return; } env->v7m.basepri[M_REG_NS] = val & 0xff; return; case 0x93: /* FAULTMASK_NS */ - if (!env->v7m.secure) { + if (!env->v7m.secure || !arm_feature(env, ARM_FEATURE_M_MAIN)) { return; } env->v7m.faultmask[M_REG_NS] = val & 1; @@ -10728,8 +12169,10 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) write_v7m_control_spsel_for_secstate(env, val & R_V7M_CONTROL_SPSEL_MASK, M_REG_NS); - env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; + if (arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.control[M_REG_NS] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[M_REG_NS] |= val & R_V7M_CONTROL_NPRIV_MASK; + } return; case 0x98: /* SP_NS */ { @@ -10737,11 +12180,23 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) * currently in handler mode or not, using the NS CONTROL.SPSEL. */ bool spsel = env->v7m.control[M_REG_NS] & R_V7M_CONTROL_SPSEL_MASK; + bool is_psp = !arm_v7m_is_handler_mode(env) && spsel; + uint32_t limit; if (!env->v7m.secure) { return; } - if (!arm_v7m_is_handler_mode(env) && spsel) { + + limit = is_psp ? env->v7m.psplim[false] : env->v7m.msplim[false]; + + if (val < limit) { + CPUState *cs = CPU(arm_env_get_cpu(env)); + + cpu_restore_state(cs, GETPC(), true); + raise_exception(env, EXCP_STKOF, 0, 1); + } + + if (is_psp) { env->v7m.other_ss_psp = val; } else { env->v7m.other_ss_msp = val; @@ -10798,9 +12253,15 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) env->v7m.primask[env->v7m.secure] = val & 1; break; case 17: /* BASEPRI */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } env->v7m.basepri[env->v7m.secure] = val & 0xff; break; case 18: /* BASEPRI_MAX */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } val &= 0xff; if (val != 0 && (val < env->v7m.basepri[env->v7m.secure] || env->v7m.basepri[env->v7m.secure] == 0)) { @@ -10808,6 +12269,9 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) } break; case 19: /* FAULTMASK */ + if (!arm_feature(env, ARM_FEATURE_M_MAIN)) { + goto bad_reg; + } env->v7m.faultmask[env->v7m.secure] = val & 1; break; case 20: /* CONTROL */ @@ -10822,8 +12286,10 @@ void HELPER(v7m_msr)(CPUARMState *env, uint32_t maskreg, uint32_t val) !arm_v7m_is_handler_mode(env)) { write_v7m_control_spsel(env, (val & R_V7M_CONTROL_SPSEL_MASK) != 0); } - env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; - env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; + if (arm_feature(env, ARM_FEATURE_M_MAIN)) { + env->v7m.control[env->v7m.secure] &= ~R_V7M_CONTROL_NPRIV_MASK; + env->v7m.control[env->v7m.secure] |= val & R_V7M_CONTROL_NPRIV_MASK; + } break; default: bad_reg: @@ -11251,1049 +12717,6 @@ uint32_t HELPER(sel_flags)(uint32_t flags, uint32_t a, uint32_t b) return (a & mask) | (b & ~mask); } -/* VFP support. We follow the convention used for VFP instructions: - Single precision routines have a "s" suffix, double precision a - "d" suffix. */ - -/* Convert host exception flags to vfp form. */ -static inline int vfp_exceptbits_from_host(int host_bits) -{ - int target_bits = 0; - - if (host_bits & float_flag_invalid) - target_bits |= 1; - if (host_bits & float_flag_divbyzero) - target_bits |= 2; - if (host_bits & float_flag_overflow) - target_bits |= 4; - if (host_bits & (float_flag_underflow | float_flag_output_denormal)) - target_bits |= 8; - if (host_bits & float_flag_inexact) - target_bits |= 0x10; - if (host_bits & float_flag_input_denormal) - target_bits |= 0x80; - return target_bits; -} - -uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) -{ - int i; - uint32_t fpscr; - - fpscr = (env->vfp.xregs[ARM_VFP_FPSCR] & 0xffc8ffff) - | (env->vfp.vec_len << 16) - | (env->vfp.vec_stride << 20); - i = get_float_exception_flags(&env->vfp.fp_status); - i |= get_float_exception_flags(&env->vfp.standard_fp_status); - i |= get_float_exception_flags(&env->vfp.fp_status_f16); - fpscr |= vfp_exceptbits_from_host(i); - return fpscr; -} - -uint32_t vfp_get_fpscr(CPUARMState *env) -{ - return HELPER(vfp_get_fpscr)(env); -} - -/* Convert vfp exception flags to target form. */ -static inline int vfp_exceptbits_to_host(int target_bits) -{ - int host_bits = 0; - - if (target_bits & 1) - host_bits |= float_flag_invalid; - if (target_bits & 2) - host_bits |= float_flag_divbyzero; - if (target_bits & 4) - host_bits |= float_flag_overflow; - if (target_bits & 8) - host_bits |= float_flag_underflow; - if (target_bits & 0x10) - host_bits |= float_flag_inexact; - if (target_bits & 0x80) - host_bits |= float_flag_input_denormal; - return host_bits; -} - -void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) -{ - int i; - uint32_t changed; - - changed = env->vfp.xregs[ARM_VFP_FPSCR]; - env->vfp.xregs[ARM_VFP_FPSCR] = (val & 0xffc8ffff); - env->vfp.vec_len = (val >> 16) & 7; - env->vfp.vec_stride = (val >> 20) & 3; - - changed ^= val; - if (changed & (3 << 22)) { - i = (val >> 22) & 3; - switch (i) { - case FPROUNDING_TIEEVEN: - i = float_round_nearest_even; - break; - case FPROUNDING_POSINF: - i = float_round_up; - break; - case FPROUNDING_NEGINF: - i = float_round_down; - break; - case FPROUNDING_ZERO: - i = float_round_to_zero; - break; - } - set_float_rounding_mode(i, &env->vfp.fp_status); - set_float_rounding_mode(i, &env->vfp.fp_status_f16); - } - if (changed & FPCR_FZ16) { - bool ftz_enabled = val & FPCR_FZ16; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16); - } - if (changed & FPCR_FZ) { - bool ftz_enabled = val & FPCR_FZ; - set_flush_to_zero(ftz_enabled, &env->vfp.fp_status); - set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status); - } - if (changed & FPCR_DN) { - bool dnan_enabled = val & FPCR_DN; - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); - set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); - } - - /* The exception flags are ORed together when we read fpscr so we - * only need to preserve the current state in one of our - * float_status values. - */ - i = vfp_exceptbits_to_host(val); - set_float_exception_flags(i, &env->vfp.fp_status); - set_float_exception_flags(0, &env->vfp.fp_status_f16); - set_float_exception_flags(0, &env->vfp.standard_fp_status); -} - -void vfp_set_fpscr(CPUARMState *env, uint32_t val) -{ - HELPER(vfp_set_fpscr)(env, val); -} - -#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) - -#define VFP_BINOP(name) \ -float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return float32_ ## name(a, b, fpst); \ -} \ -float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return float64_ ## name(a, b, fpst); \ -} -VFP_BINOP(add) -VFP_BINOP(sub) -VFP_BINOP(mul) -VFP_BINOP(div) -VFP_BINOP(min) -VFP_BINOP(max) -VFP_BINOP(minnum) -VFP_BINOP(maxnum) -#undef VFP_BINOP - -float32 VFP_HELPER(neg, s)(float32 a) -{ - return float32_chs(a); -} - -float64 VFP_HELPER(neg, d)(float64 a) -{ - return float64_chs(a); -} - -float32 VFP_HELPER(abs, s)(float32 a) -{ - return float32_abs(a); -} - -float64 VFP_HELPER(abs, d)(float64 a) -{ - return float64_abs(a); -} - -float32 VFP_HELPER(sqrt, s)(float32 a, CPUARMState *env) -{ - return float32_sqrt(a, &env->vfp.fp_status); -} - -float64 VFP_HELPER(sqrt, d)(float64 a, CPUARMState *env) -{ - return float64_sqrt(a, &env->vfp.fp_status); -} - -/* XXX: check quiet/signaling case */ -#define DO_VFP_cmp(p, type) \ -void VFP_HELPER(cmp, p)(type a, type b, CPUARMState *env) \ -{ \ - uint32_t flags; \ - switch(type ## _compare_quiet(a, b, &env->vfp.fp_status)) { \ - case 0: flags = 0x6; break; \ - case -1: flags = 0x8; break; \ - case 1: flags = 0x2; break; \ - default: case 2: flags = 0x3; break; \ - } \ - env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28) \ - | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \ -} \ -void VFP_HELPER(cmpe, p)(type a, type b, CPUARMState *env) \ -{ \ - uint32_t flags; \ - switch(type ## _compare(a, b, &env->vfp.fp_status)) { \ - case 0: flags = 0x6; break; \ - case -1: flags = 0x8; break; \ - case 1: flags = 0x2; break; \ - default: case 2: flags = 0x3; break; \ - } \ - env->vfp.xregs[ARM_VFP_FPSCR] = (flags << 28) \ - | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \ -} -DO_VFP_cmp(s, float32) -DO_VFP_cmp(d, float64) -#undef DO_VFP_cmp - -/* Integer to float and float to integer conversions */ - -#define CONV_ITOF(name, ftype, fsz, sign) \ -ftype HELPER(name)(uint32_t x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ -} - -#define CONV_FTOI(name, ftype, fsz, sign, round) \ -sign##int32_t HELPER(name)(ftype x, void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - return float##fsz##_to_##sign##int32##round(x, fpst); \ -} - -#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ - CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ - CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ - CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) - -FLOAT_CONVS(si, h, uint32_t, 16, ) -FLOAT_CONVS(si, s, float32, 32, ) -FLOAT_CONVS(si, d, float64, 64, ) -FLOAT_CONVS(ui, h, uint32_t, 16, u) -FLOAT_CONVS(ui, s, float32, 32, u) -FLOAT_CONVS(ui, d, float64, 64, u) - -#undef CONV_ITOF -#undef CONV_FTOI -#undef FLOAT_CONVS - -/* floating point conversion */ -float64 VFP_HELPER(fcvtd, s)(float32 x, CPUARMState *env) -{ - return float32_to_float64(x, &env->vfp.fp_status); -} - -float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) -{ - return float64_to_float32(x, &env->vfp.fp_status); -} - -/* VFP3 fixed point conversion. */ -#define VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -float##fsz HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ - void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - float##fsz tmp; \ - tmp = itype##_to_##float##fsz(x, fpst); \ - return float##fsz##_scalbn(tmp, -(int)shift, fpst); \ -} - -/* Notice that we want only input-denormal exception flags from the - * scalbn operation: the other possible flags (overflow+inexact if - * we overflow to infinity, output-denormal) aren't correct for the - * complete scale-and-convert operation. - */ -#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, round) \ -uint##isz##_t HELPER(vfp_to##name##p##round)(float##fsz x, \ - uint32_t shift, \ - void *fpstp) \ -{ \ - float_status *fpst = fpstp; \ - int old_exc_flags = get_float_exception_flags(fpst); \ - float##fsz tmp; \ - if (float##fsz##_is_any_nan(x)) { \ - float_raise(float_flag_invalid, fpst); \ - return 0; \ - } \ - tmp = float##fsz##_scalbn(x, shift, fpst); \ - old_exc_flags |= get_float_exception_flags(fpst) \ - & float_flag_input_denormal; \ - set_float_exception_flags(old_exc_flags, fpst); \ - return float##fsz##_to_##itype##round(tmp, fpst); \ -} - -#define VFP_CONV_FIX(name, p, fsz, isz, itype) \ -VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, _round_to_zero) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ) - -#define VFP_CONV_FIX_A64(name, p, fsz, isz, itype) \ -VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ -VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ) - -VFP_CONV_FIX(sh, d, 64, 64, int16) -VFP_CONV_FIX(sl, d, 64, 64, int32) -VFP_CONV_FIX_A64(sq, d, 64, 64, int64) -VFP_CONV_FIX(uh, d, 64, 64, uint16) -VFP_CONV_FIX(ul, d, 64, 64, uint32) -VFP_CONV_FIX_A64(uq, d, 64, 64, uint64) -VFP_CONV_FIX(sh, s, 32, 32, int16) -VFP_CONV_FIX(sl, s, 32, 32, int32) -VFP_CONV_FIX_A64(sq, s, 32, 64, int64) -VFP_CONV_FIX(uh, s, 32, 32, uint16) -VFP_CONV_FIX(ul, s, 32, 32, uint32) -VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) - -#undef VFP_CONV_FIX -#undef VFP_CONV_FIX_FLOAT -#undef VFP_CONV_FLOAT_FIX_ROUND -#undef VFP_CONV_FIX_A64 - -/* Conversion to/from f16 can overflow to infinity before/after scaling. - * Therefore we convert to f64, scale, and then convert f64 to f16; or - * vice versa for conversion to integer. - * - * For 16- and 32-bit integers, the conversion to f64 never rounds. - * For 64-bit integers, any integer that would cause rounding will also - * overflow to f16 infinity, so there is no double rounding problem. - */ - -static float16 do_postscale_fp16(float64 f, int shift, float_status *fpst) -{ - return float64_to_float16(float64_scalbn(f, -shift, fpst), true, fpst); -} - -uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) -{ - return do_postscale_fp16(int32_to_float64(x, fpst), shift, fpst); -} - -uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) -{ - return do_postscale_fp16(uint32_to_float64(x, fpst), shift, fpst); -} - -uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) -{ - return do_postscale_fp16(int64_to_float64(x, fpst), shift, fpst); -} - -uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) -{ - return do_postscale_fp16(uint64_to_float64(x, fpst), shift, fpst); -} - -static float64 do_prescale_fp16(float16 f, int shift, float_status *fpst) -{ - if (unlikely(float16_is_any_nan(f))) { - float_raise(float_flag_invalid, fpst); - return 0; - } else { - int old_exc_flags = get_float_exception_flags(fpst); - float64 ret; - - ret = float16_to_float64(f, true, fpst); - ret = float64_scalbn(ret, shift, fpst); - old_exc_flags |= get_float_exception_flags(fpst) - & float_flag_input_denormal; - set_float_exception_flags(old_exc_flags, fpst); - - return ret; - } -} - -uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_int16(do_prescale_fp16(x, shift, fpst), fpst); -} - -uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_uint16(do_prescale_fp16(x, shift, fpst), fpst); -} - -uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_int32(do_prescale_fp16(x, shift, fpst), fpst); -} - -uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_uint32(do_prescale_fp16(x, shift, fpst), fpst); -} - -uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_int64(do_prescale_fp16(x, shift, fpst), fpst); -} - -uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) -{ - return float64_to_uint64(do_prescale_fp16(x, shift, fpst), fpst); -} - -/* Set the current fp rounding mode and return the old one. - * The argument is a softfloat float_round_ value. - */ -uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp) -{ - float_status *fp_status = fpstp; - - uint32_t prev_rmode = get_float_rounding_mode(fp_status); - set_float_rounding_mode(rmode, fp_status); - - return prev_rmode; -} - -/* Set the current fp rounding mode in the standard fp status and return - * the old one. This is for NEON instructions that need to change the - * rounding mode but wish to use the standard FPSCR values for everything - * else. Always set the rounding mode back to the correct value after - * modifying it. - * The argument is a softfloat float_round_ value. - */ -uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) -{ - float_status *fp_status = &env->vfp.standard_fp_status; - - uint32_t prev_rmode = get_float_rounding_mode(fp_status); - set_float_rounding_mode(rmode, fp_status); - - return prev_rmode; -} - -/* Half precision conversions. */ -float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing input denormals. - */ - float_status *fpst = fpstp; - flag save = get_flush_inputs_to_zero(fpst); - set_flush_inputs_to_zero(false, fpst); - float32 r = float16_to_float32(a, !ahp_mode, fpst); - set_flush_inputs_to_zero(save, fpst); - return r; -} - -uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing output denormals. - */ - float_status *fpst = fpstp; - flag save = get_flush_to_zero(fpst); - set_flush_to_zero(false, fpst); - float16 r = float32_to_float16(a, !ahp_mode, fpst); - set_flush_to_zero(save, fpst); - return r; -} - -float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing input denormals. - */ - float_status *fpst = fpstp; - flag save = get_flush_inputs_to_zero(fpst); - set_flush_inputs_to_zero(false, fpst); - float64 r = float16_to_float64(a, !ahp_mode, fpst); - set_flush_inputs_to_zero(save, fpst); - return r; -} - -uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) -{ - /* Squash FZ16 to 0 for the duration of conversion. In this case, - * it would affect flushing output denormals. - */ - float_status *fpst = fpstp; - flag save = get_flush_to_zero(fpst); - set_flush_to_zero(false, fpst); - float16 r = float64_to_float16(a, !ahp_mode, fpst); - set_flush_to_zero(save, fpst); - return r; -} - -#define float32_two make_float32(0x40000000) -#define float32_three make_float32(0x40400000) -#define float32_one_point_five make_float32(0x3fc00000) - -float32 HELPER(recps_f32)(float32 a, float32 b, CPUARMState *env) -{ - float_status *s = &env->vfp.standard_fp_status; - if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || - (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { - if (!(float32_is_zero(a) || float32_is_zero(b))) { - float_raise(float_flag_input_denormal, s); - } - return float32_two; - } - return float32_sub(float32_two, float32_mul(a, b, s), s); -} - -float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUARMState *env) -{ - float_status *s = &env->vfp.standard_fp_status; - float32 product; - if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || - (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { - if (!(float32_is_zero(a) || float32_is_zero(b))) { - float_raise(float_flag_input_denormal, s); - } - return float32_one_point_five; - } - product = float32_mul(a, b, s); - return float32_div(float32_sub(float32_three, product, s), float32_two, s); -} - -/* NEON helpers. */ - -/* Constants 256 and 512 are used in some helpers; we avoid relying on - * int->float conversions at run-time. */ -#define float64_256 make_float64(0x4070000000000000LL) -#define float64_512 make_float64(0x4080000000000000LL) -#define float16_maxnorm make_float16(0x7bff) -#define float32_maxnorm make_float32(0x7f7fffff) -#define float64_maxnorm make_float64(0x7fefffffffffffffLL) - -/* Reciprocal functions - * - * The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate - */ - -/* See RecipEstimate() - * - * input is a 9 bit fixed point number - * input range 256 .. 511 for a number from 0.5 <= x < 1.0. - * result range 256 .. 511 for a number from 1.0 to 511/256. - */ - -static int recip_estimate(int input) -{ - int a, b, r; - assert(256 <= input && input < 512); - a = (input * 2) + 1; - b = (1 << 19) / a; - r = (b + 1) >> 1; - assert(256 <= r && r < 512); - return r; -} - -/* - * Common wrapper to call recip_estimate - * - * The parameters are exponent and 64 bit fraction (without implicit - * bit) where the binary point is nominally at bit 52. Returns a - * float64 which can then be rounded to the appropriate size by the - * callee. - */ - -static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac) -{ - uint32_t scaled, estimate; - uint64_t result_frac; - int result_exp; - - /* Handle sub-normals */ - if (*exp == 0) { - if (extract64(frac, 51, 1) == 0) { - *exp = -1; - frac <<= 2; - } else { - frac <<= 1; - } - } - - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - estimate = recip_estimate(scaled); - - result_exp = exp_off - *exp; - result_frac = deposit64(0, 44, 8, estimate); - if (result_exp == 0) { - result_frac = deposit64(result_frac >> 1, 51, 1, 1); - } else if (result_exp == -1) { - result_frac = deposit64(result_frac >> 2, 50, 2, 1); - result_exp = 0; - } - - *exp = result_exp; - - return result_frac; -} - -static bool round_to_inf(float_status *fpst, bool sign_bit) -{ - switch (fpst->float_rounding_mode) { - case float_round_nearest_even: /* Round to Nearest */ - return true; - case float_round_up: /* Round to +Inf */ - return !sign_bit; - case float_round_down: /* Round to -Inf */ - return sign_bit; - case float_round_to_zero: /* Round to Zero */ - return false; - } - - g_assert_not_reached(); -} - -uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) -{ - float_status *fpst = fpstp; - float16 f16 = float16_squash_input_denormal(input, fpst); - uint32_t f16_val = float16_val(f16); - uint32_t f16_sign = float16_is_neg(f16); - int f16_exp = extract32(f16_val, 10, 5); - uint32_t f16_frac = extract32(f16_val, 0, 10); - uint64_t f64_frac; - - if (float16_is_any_nan(f16)) { - float16 nan = f16; - if (float16_is_signaling_nan(f16, fpst)) { - float_raise(float_flag_invalid, fpst); - nan = float16_silence_nan(f16, fpst); - } - if (fpst->default_nan_mode) { - nan = float16_default_nan(fpst); - } - return nan; - } else if (float16_is_infinity(f16)) { - return float16_set_sign(float16_zero, float16_is_neg(f16)); - } else if (float16_is_zero(f16)) { - float_raise(float_flag_divbyzero, fpst); - return float16_set_sign(float16_infinity, float16_is_neg(f16)); - } else if (float16_abs(f16) < (1 << 8)) { - /* Abs(value) < 2.0^-16 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f16_sign)) { - return float16_set_sign(float16_infinity, f16_sign); - } else { - return float16_set_sign(float16_maxnorm, f16_sign); - } - } else if (f16_exp >= 29 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float16_set_sign(float16_zero, float16_is_neg(f16)); - } - - f64_frac = call_recip_estimate(&f16_exp, 29, - ((uint64_t) f16_frac) << (52 - 10)); - - /* result = sign : result_exp<4:0> : fraction<51:42> */ - f16_val = deposit32(0, 15, 1, f16_sign); - f16_val = deposit32(f16_val, 10, 5, f16_exp); - f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); - return make_float16(f16_val); -} - -float32 HELPER(recpe_f32)(float32 input, void *fpstp) -{ - float_status *fpst = fpstp; - float32 f32 = float32_squash_input_denormal(input, fpst); - uint32_t f32_val = float32_val(f32); - bool f32_sign = float32_is_neg(f32); - int f32_exp = extract32(f32_val, 23, 8); - uint32_t f32_frac = extract32(f32_val, 0, 23); - uint64_t f64_frac; - - if (float32_is_any_nan(f32)) { - float32 nan = f32; - if (float32_is_signaling_nan(f32, fpst)) { - float_raise(float_flag_invalid, fpst); - nan = float32_silence_nan(f32, fpst); - } - if (fpst->default_nan_mode) { - nan = float32_default_nan(fpst); - } - return nan; - } else if (float32_is_infinity(f32)) { - return float32_set_sign(float32_zero, float32_is_neg(f32)); - } else if (float32_is_zero(f32)) { - float_raise(float_flag_divbyzero, fpst); - return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if (float32_abs(f32) < (1ULL << 21)) { - /* Abs(value) < 2.0^-128 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f32_sign)) { - return float32_set_sign(float32_infinity, f32_sign); - } else { - return float32_set_sign(float32_maxnorm, f32_sign); - } - } else if (f32_exp >= 253 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float32_set_sign(float32_zero, float32_is_neg(f32)); - } - - f64_frac = call_recip_estimate(&f32_exp, 253, - ((uint64_t) f32_frac) << (52 - 23)); - - /* result = sign : result_exp<7:0> : fraction<51:29> */ - f32_val = deposit32(0, 31, 1, f32_sign); - f32_val = deposit32(f32_val, 23, 8, f32_exp); - f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); - return make_float32(f32_val); -} - -float64 HELPER(recpe_f64)(float64 input, void *fpstp) -{ - float_status *fpst = fpstp; - float64 f64 = float64_squash_input_denormal(input, fpst); - uint64_t f64_val = float64_val(f64); - bool f64_sign = float64_is_neg(f64); - int f64_exp = extract64(f64_val, 52, 11); - uint64_t f64_frac = extract64(f64_val, 0, 52); - - /* Deal with any special cases */ - if (float64_is_any_nan(f64)) { - float64 nan = f64; - if (float64_is_signaling_nan(f64, fpst)) { - float_raise(float_flag_invalid, fpst); - nan = float64_silence_nan(f64, fpst); - } - if (fpst->default_nan_mode) { - nan = float64_default_nan(fpst); - } - return nan; - } else if (float64_is_infinity(f64)) { - return float64_set_sign(float64_zero, float64_is_neg(f64)); - } else if (float64_is_zero(f64)) { - float_raise(float_flag_divbyzero, fpst); - return float64_set_sign(float64_infinity, float64_is_neg(f64)); - } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { - /* Abs(value) < 2.0^-1024 */ - float_raise(float_flag_overflow | float_flag_inexact, fpst); - if (round_to_inf(fpst, f64_sign)) { - return float64_set_sign(float64_infinity, f64_sign); - } else { - return float64_set_sign(float64_maxnorm, f64_sign); - } - } else if (f64_exp >= 2045 && fpst->flush_to_zero) { - float_raise(float_flag_underflow, fpst); - return float64_set_sign(float64_zero, float64_is_neg(f64)); - } - - f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac); - - /* result = sign : result_exp<10:0> : fraction<51:0>; */ - f64_val = deposit64(0, 63, 1, f64_sign); - f64_val = deposit64(f64_val, 52, 11, f64_exp); - f64_val = deposit64(f64_val, 0, 52, f64_frac); - return make_float64(f64_val); -} - -/* The algorithm that must be used to calculate the estimate - * is specified by the ARM ARM. - */ - -static int do_recip_sqrt_estimate(int a) -{ - int b, estimate; - - assert(128 <= a && a < 512); - if (a < 256) { - a = a * 2 + 1; - } else { - a = (a >> 1) << 1; - a = (a + 1) * 2; - } - b = 512; - while (a * (b + 1) * (b + 1) < (1 << 28)) { - b += 1; - } - estimate = (b + 1) / 2; - assert(256 <= estimate && estimate < 512); - - return estimate; -} - - -static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) -{ - int estimate; - uint32_t scaled; - - if (*exp == 0) { - while (extract64(frac, 51, 1) == 0) { - frac = frac << 1; - *exp -= 1; - } - frac = extract64(frac, 0, 51) << 1; - } - - if (*exp & 1) { - /* scaled = UInt('01':fraction<51:45>) */ - scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); - } else { - /* scaled = UInt('1':fraction<51:44>) */ - scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); - } - estimate = do_recip_sqrt_estimate(scaled); - - *exp = (exp_off - *exp) / 2; - return extract64(estimate, 0, 8) << 44; -} - -uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) -{ - float_status *s = fpstp; - float16 f16 = float16_squash_input_denormal(input, s); - uint16_t val = float16_val(f16); - bool f16_sign = float16_is_neg(f16); - int f16_exp = extract32(val, 10, 5); - uint16_t f16_frac = extract32(val, 0, 10); - uint64_t f64_frac; - - if (float16_is_any_nan(f16)) { - float16 nan = f16; - if (float16_is_signaling_nan(f16, s)) { - float_raise(float_flag_invalid, s); - nan = float16_silence_nan(f16, s); - } - if (s->default_nan_mode) { - nan = float16_default_nan(s); - } - return nan; - } else if (float16_is_zero(f16)) { - float_raise(float_flag_divbyzero, s); - return float16_set_sign(float16_infinity, f16_sign); - } else if (f16_sign) { - float_raise(float_flag_invalid, s); - return float16_default_nan(s); - } else if (float16_is_infinity(f16)) { - return float16_zero; - } - - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - f64_frac = ((uint64_t) f16_frac) << (52 - 10); - - f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ - val = deposit32(0, 15, 1, f16_sign); - val = deposit32(val, 10, 5, f16_exp); - val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float16(val); -} - -float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) -{ - float_status *s = fpstp; - float32 f32 = float32_squash_input_denormal(input, s); - uint32_t val = float32_val(f32); - uint32_t f32_sign = float32_is_neg(f32); - int f32_exp = extract32(val, 23, 8); - uint32_t f32_frac = extract32(val, 0, 23); - uint64_t f64_frac; - - if (float32_is_any_nan(f32)) { - float32 nan = f32; - if (float32_is_signaling_nan(f32, s)) { - float_raise(float_flag_invalid, s); - nan = float32_silence_nan(f32, s); - } - if (s->default_nan_mode) { - nan = float32_default_nan(s); - } - return nan; - } else if (float32_is_zero(f32)) { - float_raise(float_flag_divbyzero, s); - return float32_set_sign(float32_infinity, float32_is_neg(f32)); - } else if (float32_is_neg(f32)) { - float_raise(float_flag_invalid, s); - return float32_default_nan(s); - } else if (float32_is_infinity(f32)) { - return float32_zero; - } - - /* Scale and normalize to a double-precision value between 0.25 and 1.0, - * preserving the parity of the exponent. */ - - f64_frac = ((uint64_t) f32_frac) << 29; - - f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(15) */ - val = deposit32(0, 31, 1, f32_sign); - val = deposit32(val, 23, 8, f32_exp); - val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float32(val); -} - -float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) -{ - float_status *s = fpstp; - float64 f64 = float64_squash_input_denormal(input, s); - uint64_t val = float64_val(f64); - bool f64_sign = float64_is_neg(f64); - int f64_exp = extract64(val, 52, 11); - uint64_t f64_frac = extract64(val, 0, 52); - - if (float64_is_any_nan(f64)) { - float64 nan = f64; - if (float64_is_signaling_nan(f64, s)) { - float_raise(float_flag_invalid, s); - nan = float64_silence_nan(f64, s); - } - if (s->default_nan_mode) { - nan = float64_default_nan(s); - } - return nan; - } else if (float64_is_zero(f64)) { - float_raise(float_flag_divbyzero, s); - return float64_set_sign(float64_infinity, float64_is_neg(f64)); - } else if (float64_is_neg(f64)) { - float_raise(float_flag_invalid, s); - return float64_default_nan(s); - } else if (float64_is_infinity(f64)) { - return float64_zero; - } - - f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac); - - /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ - val = deposit64(0, 61, 1, f64_sign); - val = deposit64(val, 52, 11, f64_exp); - val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); - return make_float64(val); -} - -uint32_t HELPER(recpe_u32)(uint32_t a, void *fpstp) -{ - /* float_status *s = fpstp; */ - int input, estimate; - - if ((a & 0x80000000) == 0) { - return 0xffffffff; - } - - input = extract32(a, 23, 9); - estimate = recip_estimate(input); - - return deposit32(0, (32 - 9), 9, estimate); -} - -uint32_t HELPER(rsqrte_u32)(uint32_t a, void *fpstp) -{ - int estimate; - - if ((a & 0xc0000000) == 0) { - return 0xffffffff; - } - - estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); - - return deposit32(0, 23, 9, estimate); -} - -/* VFPv4 fused multiply-accumulate */ -float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, void *fpstp) -{ - float_status *fpst = fpstp; - return float32_muladd(a, b, c, 0, fpst); -} - -float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp) -{ - float_status *fpst = fpstp; - return float64_muladd(a, b, c, 0, fpst); -} - -/* ARMv8 round to integral */ -float32 HELPER(rints_exact)(float32 x, void *fp_status) -{ - return float32_round_to_int(x, fp_status); -} - -float64 HELPER(rintd_exact)(float64 x, void *fp_status) -{ - return float64_round_to_int(x, fp_status); -} - -float32 HELPER(rints)(float32 x, void *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float32 ret; - - ret = float32_round_to_int(x, fp_status); - - /* Suppress any inexact exceptions the conversion produced */ - if (!(old_flags & float_flag_inexact)) { - new_flags = get_float_exception_flags(fp_status); - set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); - } - - return ret; -} - -float64 HELPER(rintd)(float64 x, void *fp_status) -{ - int old_flags = get_float_exception_flags(fp_status), new_flags; - float64 ret; - - ret = float64_round_to_int(x, fp_status); - - new_flags = get_float_exception_flags(fp_status); - - /* Suppress any inexact exceptions the conversion produced */ - if (!(old_flags & float_flag_inexact)) { - new_flags = get_float_exception_flags(fp_status); - set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); - } - - return ret; -} - -/* Convert ARM rounding mode to softfloat */ -int arm_rmode_to_sf(int rmode) -{ - switch (rmode) { - case FPROUNDING_TIEAWAY: - rmode = float_round_ties_away; - break; - case FPROUNDING_ODD: - /* FIXME: add support for TIEAWAY and ODD */ - qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n", - rmode); - case FPROUNDING_TIEEVEN: - default: - rmode = float_round_nearest_even; - break; - case FPROUNDING_POSINF: - rmode = float_round_up; - break; - case FPROUNDING_NEGINF: - rmode = float_round_down; - break; - case FPROUNDING_ZERO: - rmode = float_round_to_zero; - break; - } - return rmode; -} - /* CRC helpers. * The upper bytes of val (above the number specified by 'bytes') must have * been zeroed out by the caller. @@ -12321,11 +12744,10 @@ uint32_t HELPER(crc32c)(uint32_t acc, uint32_t val, uint32_t bytes) /* Return the exception level to which FP-disabled exceptions should * be taken, or 0 if FP is enabled. */ -static inline int fp_exception_el(CPUARMState *env) +int fp_exception_el(CPUARMState *env, int cur_el) { #ifndef CONFIG_USER_ONLY int fpen; - int cur_el = arm_current_el(env); /* CPACR and the CPTR registers don't exist before v6, so FP is * always accessible @@ -12384,60 +12806,150 @@ static inline int fp_exception_el(CPUARMState *env) return 0; } +ARMMMUIdx arm_v7m_mmu_idx_for_secstate_and_priv(CPUARMState *env, + bool secstate, bool priv) +{ + ARMMMUIdx mmu_idx = ARM_MMU_IDX_M; + + if (priv) { + mmu_idx |= ARM_MMU_IDX_M_PRIV; + } + + if (armv7m_nvic_neg_prio_requested(env->nvic, secstate)) { + mmu_idx |= ARM_MMU_IDX_M_NEGPRI; + } + + if (secstate) { + mmu_idx |= ARM_MMU_IDX_M_S; + } + + return mmu_idx; +} + +/* Return the MMU index for a v7M CPU in the specified security state */ +ARMMMUIdx arm_v7m_mmu_idx_for_secstate(CPUARMState *env, bool secstate) +{ + bool priv = arm_current_el(env) != 0; + + return arm_v7m_mmu_idx_for_secstate_and_priv(env, secstate, priv); +} + +ARMMMUIdx arm_mmu_idx(CPUARMState *env) +{ + int el; + + if (arm_feature(env, ARM_FEATURE_M)) { + return arm_v7m_mmu_idx_for_secstate(env, env->v7m.secure); + } + + el = arm_current_el(env); + if (el < 2 && arm_is_secure_below_el3(env)) { + return ARMMMUIdx_S1SE0 + el; + } else { + return ARMMMUIdx_S12NSE0 + el; + } +} + +int cpu_mmu_index(CPUARMState *env, bool ifetch) +{ + return arm_to_core_mmu_idx(arm_mmu_idx(env)); +} + +#ifndef CONFIG_USER_ONLY +ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env) +{ + return stage_1_mmu_idx(arm_mmu_idx(env)); +} +#endif + void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *pflags) { - ARMMMUIdx mmu_idx = core_to_arm_mmu_idx(env, cpu_mmu_index(env, false)); - int fp_el = fp_exception_el(env); - uint32_t flags; + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + int current_el = arm_current_el(env); + int fp_el = fp_exception_el(env, current_el); + uint32_t flags = 0; if (is_a64(env)) { - int sve_el = sve_exception_el(env); - uint32_t zcr_len; + ARMCPU *cpu = arm_env_get_cpu(env); + uint64_t sctlr; *pc = env->pc; - flags = ARM_TBFLAG_AARCH64_STATE_MASK; - /* Get control bits for tagged addresses */ - flags |= (arm_regime_tbi0(env, mmu_idx) << ARM_TBFLAG_TBI0_SHIFT); - flags |= (arm_regime_tbi1(env, mmu_idx) << ARM_TBFLAG_TBI1_SHIFT); - flags |= sve_el << ARM_TBFLAG_SVEEXC_EL_SHIFT; + flags = FIELD_DP32(flags, TBFLAG_ANY, AARCH64_STATE, 1); - /* If SVE is disabled, but FP is enabled, - then the effective len is 0. */ - if (sve_el != 0 && fp_el == 0) { - zcr_len = 0; - } else { - int current_el = arm_current_el(env); + /* Get control bits for tagged addresses. */ + { + ARMMMUIdx stage1 = stage_1_mmu_idx(mmu_idx); + ARMVAParameters p0 = aa64_va_parameters_both(env, 0, stage1); + int tbii, tbid; - zcr_len = env->vfp.zcr_el[current_el <= 1 ? 1 : current_el]; - zcr_len &= 0xf; - if (current_el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { - zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[2]); + /* FIXME: ARMv8.1-VHE S2 translation regime. */ + if (regime_el(env, stage1) < 2) { + ARMVAParameters p1 = aa64_va_parameters_both(env, -1, stage1); + tbid = (p1.tbi << 1) | p0.tbi; + tbii = tbid & ~((p1.tbid << 1) | p0.tbid); + } else { + tbid = p0.tbi; + tbii = tbid & !p0.tbid; } - if (current_el < 3 && arm_feature(env, ARM_FEATURE_EL3)) { - zcr_len = MIN(zcr_len, 0xf & (uint32_t)env->vfp.zcr_el[3]); + + flags = FIELD_DP32(flags, TBFLAG_A64, TBII, tbii); + flags = FIELD_DP32(flags, TBFLAG_A64, TBID, tbid); + } + + if (cpu_isar_feature(aa64_sve, cpu)) { + int sve_el = sve_exception_el(env, current_el); + uint32_t zcr_len; + + /* If SVE is disabled, but FP is enabled, + * then the effective len is 0. + */ + if (sve_el != 0 && fp_el == 0) { + zcr_len = 0; + } else { + zcr_len = sve_zcr_len_for_el(env, current_el); + } + flags = FIELD_DP32(flags, TBFLAG_A64, SVEEXC_EL, sve_el); + flags = FIELD_DP32(flags, TBFLAG_A64, ZCR_LEN, zcr_len); + } + + sctlr = arm_sctlr(env, current_el); + + if (cpu_isar_feature(aa64_pauth, cpu)) { + /* + * In order to save space in flags, we record only whether + * pauth is "inactive", meaning all insns are implemented as + * a nop, or "active" when some action must be performed. + * The decision of which action to take is left to a helper. + */ + if (sctlr & (SCTLR_EnIA | SCTLR_EnIB | SCTLR_EnDA | SCTLR_EnDB)) { + flags = FIELD_DP32(flags, TBFLAG_A64, PAUTH_ACTIVE, 1); } } - flags |= zcr_len << ARM_TBFLAG_ZCR_LEN_SHIFT; + + if (cpu_isar_feature(aa64_bti, cpu)) { + /* Note that SCTLR_EL[23].BT == SCTLR_BT1. */ + if (sctlr & (current_el == 0 ? SCTLR_BT0 : SCTLR_BT1)) { + flags = FIELD_DP32(flags, TBFLAG_A64, BT, 1); + } + flags = FIELD_DP32(flags, TBFLAG_A64, BTYPE, env->btype); + } } else { *pc = env->regs[15]; - flags = (env->thumb << ARM_TBFLAG_THUMB_SHIFT) - | (env->vfp.vec_len << ARM_TBFLAG_VECLEN_SHIFT) - | (env->vfp.vec_stride << ARM_TBFLAG_VECSTRIDE_SHIFT) - | (env->condexec_bits << ARM_TBFLAG_CONDEXEC_SHIFT) - | (arm_sctlr_b(env) << ARM_TBFLAG_SCTLR_B_SHIFT); - if (!(access_secure_reg(env))) { - flags |= ARM_TBFLAG_NS_MASK; - } + flags = FIELD_DP32(flags, TBFLAG_A32, THUMB, env->thumb); + flags = FIELD_DP32(flags, TBFLAG_A32, VECLEN, env->vfp.vec_len); + flags = FIELD_DP32(flags, TBFLAG_A32, VECSTRIDE, env->vfp.vec_stride); + flags = FIELD_DP32(flags, TBFLAG_A32, CONDEXEC, env->condexec_bits); + flags = FIELD_DP32(flags, TBFLAG_A32, SCTLR_B, arm_sctlr_b(env)); + flags = FIELD_DP32(flags, TBFLAG_A32, NS, !access_secure_reg(env)); if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30) || arm_el_is_aa64(env, 1)) { - flags |= ARM_TBFLAG_VFPEN_MASK; + flags = FIELD_DP32(flags, TBFLAG_A32, VFPEN, 1); } - flags |= (extract32(env->cp15.c15_cpar, 0, 2) - << ARM_TBFLAG_XSCALE_CPAR_SHIFT); + flags = FIELD_DP32(flags, TBFLAG_A32, XSCALE_CPAR, env->cp15.c15_cpar); } - flags |= (arm_to_core_mmu_idx(mmu_idx) << ARM_TBFLAG_MMUIDX_SHIFT); + flags = FIELD_DP32(flags, TBFLAG_ANY, MMUIDX, arm_to_core_mmu_idx(mmu_idx)); /* The SS_ACTIVE and PSTATE_SS bits correspond to the state machine * states defined in the ARM ARM for software singlestep: @@ -12447,26 +12959,123 @@ void cpu_get_tb_cpu_state(CPUARMState *env, target_ulong *pc, * 1 1 Active-not-pending */ if (arm_singlestep_active(env)) { - flags |= ARM_TBFLAG_SS_ACTIVE_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, SS_ACTIVE, 1); if (is_a64(env)) { if (env->pstate & PSTATE_SS) { - flags |= ARM_TBFLAG_PSTATE_SS_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1); } } else { if (env->uncached_cpsr & PSTATE_SS) { - flags |= ARM_TBFLAG_PSTATE_SS_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, PSTATE_SS, 1); } } } if (arm_cpu_data_is_big_endian(env)) { - flags |= ARM_TBFLAG_BE_DATA_MASK; + flags = FIELD_DP32(flags, TBFLAG_ANY, BE_DATA, 1); } - flags |= fp_el << ARM_TBFLAG_FPEXC_EL_SHIFT; + flags = FIELD_DP32(flags, TBFLAG_ANY, FPEXC_EL, fp_el); if (arm_v7m_is_handler_mode(env)) { - flags |= ARM_TBFLAG_HANDLER_MASK; + flags = FIELD_DP32(flags, TBFLAG_A32, HANDLER, 1); + } + + /* v8M always applies stack limit checks unless CCR.STKOFHFNMIGN is + * suppressing them because the requested execution priority is less than 0. + */ + if (arm_feature(env, ARM_FEATURE_V8) && + arm_feature(env, ARM_FEATURE_M) && + !((mmu_idx & ARM_MMU_IDX_M_NEGPRI) && + (env->v7m.ccr[env->v7m.secure] & R_V7M_CCR_STKOFHFNMIGN_MASK))) { + flags = FIELD_DP32(flags, TBFLAG_A32, STACKCHECK, 1); } *pflags = flags; *cs_base = 0; } + +#ifdef TARGET_AARCH64 +/* + * The manual says that when SVE is enabled and VQ is widened the + * implementation is allowed to zero the previously inaccessible + * portion of the registers. The corollary to that is that when + * SVE is enabled and VQ is narrowed we are also allowed to zero + * the now inaccessible portion of the registers. + * + * The intent of this is that no predicate bit beyond VQ is ever set. + * Which means that some operations on predicate registers themselves + * may operate on full uint64_t or even unrolled across the maximum + * uint64_t[4]. Performing 4 bits of host arithmetic unconditionally + * may well be cheaper than conditionals to restrict the operation + * to the relevant portion of a uint16_t[16]. + */ +void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) +{ + int i, j; + uint64_t pmask; + + assert(vq >= 1 && vq <= ARM_MAX_VQ); + assert(vq <= arm_env_get_cpu(env)->sve_max_vq); + + /* Zap the high bits of the zregs. */ + for (i = 0; i < 32; i++) { + memset(&env->vfp.zregs[i].d[2 * vq], 0, 16 * (ARM_MAX_VQ - vq)); + } + + /* Zap the high bits of the pregs and ffr. */ + pmask = 0; + if (vq & 3) { + pmask = ~(-1ULL << (16 * (vq & 3))); + } + for (j = vq / 4; j < ARM_MAX_VQ / 4; j++) { + for (i = 0; i < 17; ++i) { + env->vfp.pregs[i].p[j] &= pmask; + } + pmask = 0; + } +} + +/* + * Notice a change in SVE vector size when changing EL. + */ +void aarch64_sve_change_el(CPUARMState *env, int old_el, + int new_el, bool el0_a64) +{ + ARMCPU *cpu = arm_env_get_cpu(env); + int old_len, new_len; + bool old_a64, new_a64; + + /* Nothing to do if no SVE. */ + if (!cpu_isar_feature(aa64_sve, cpu)) { + return; + } + + /* Nothing to do if FP is disabled in either EL. */ + if (fp_exception_el(env, old_el) || fp_exception_el(env, new_el)) { + return; + } + + /* + * DDI0584A.d sec 3.2: "If SVE instructions are disabled or trapped + * at ELx, or not available because the EL is in AArch32 state, then + * for all purposes other than a direct read, the ZCR_ELx.LEN field + * has an effective value of 0". + * + * Consider EL2 (aa64, vq=4) -> EL0 (aa32) -> EL1 (aa64, vq=0). + * If we ignore aa32 state, we would fail to see the vq4->vq0 transition + * from EL2->EL1. Thus we go ahead and narrow when entering aa32 so that + * we already have the correct register contents when encountering the + * vq0->vq0 transition between EL0->EL1. + */ + old_a64 = old_el ? arm_el_is_aa64(env, old_el) : el0_a64; + old_len = (old_a64 && !sve_exception_el(env, old_el) + ? sve_zcr_len_for_el(env, old_el) : 0); + new_a64 = new_el ? arm_el_is_aa64(env, new_el) : el0_a64; + new_len = (new_a64 && !sve_exception_el(env, new_el) + ? sve_zcr_len_for_el(env, new_el) : 0); + + /* When changing vector length, clear inaccessible state. */ + if (new_len < old_len) { + aarch64_sve_narrow_vq(env, new_len + 1); + } +} +#endif diff --git a/target/arm/helper.h b/target/arm/helper.h index 59e8c3bd1b..a09566f795 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -69,16 +69,14 @@ DEF_HELPER_2(v7m_blxns, void, env, i32) DEF_HELPER_3(v7m_tt, i32, env, i32, i32) +DEF_HELPER_2(v8m_stackcheck, void, env, 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) DEF_HELPER_2(get_cp_reg64, i64, env, ptr) -DEF_HELPER_3(msr_i_pstate, void, env, i32, i32) -DEF_HELPER_1(clear_pstate_ss, void, env) -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) @@ -217,6 +215,9 @@ DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, ptr) DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, ptr) DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) +DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, ptr) + /* neon_helper.c */ DEF_HELPER_FLAGS_3(neon_qadd_u8, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(neon_qadd_s8, TCG_CALL_NO_RWG, i32, env, i32, i32) @@ -275,18 +276,6 @@ DEF_HELPER_2(neon_cge_s16, i32, i32, i32) DEF_HELPER_2(neon_cge_u32, i32, i32, i32) DEF_HELPER_2(neon_cge_s32, i32, i32, i32) -DEF_HELPER_2(neon_min_u8, i32, i32, i32) -DEF_HELPER_2(neon_min_s8, i32, i32, i32) -DEF_HELPER_2(neon_min_u16, i32, i32, i32) -DEF_HELPER_2(neon_min_s16, i32, i32, i32) -DEF_HELPER_2(neon_min_u32, i32, i32, i32) -DEF_HELPER_2(neon_min_s32, i32, i32, i32) -DEF_HELPER_2(neon_max_u8, i32, i32, i32) -DEF_HELPER_2(neon_max_s8, i32, i32, i32) -DEF_HELPER_2(neon_max_u16, i32, i32, i32) -DEF_HELPER_2(neon_max_s16, i32, i32, i32) -DEF_HELPER_2(neon_max_u32, i32, i32, i32) -DEF_HELPER_2(neon_max_s32, i32, i32, i32) DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) @@ -652,6 +641,53 @@ DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, ptr) +DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, ptr) +DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, ptr) +DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, ptr) + #ifdef TARGET_AARCH64 #include "helper-a64.h" #include "helper-sve.h" diff --git a/target/arm/idau.h b/target/arm/idau.h index cac27b95fa..7c0e4e3776 100644 --- a/target/arm/idau.h +++ b/target/arm/idau.h @@ -38,9 +38,7 @@ #define IDAU_INTERFACE_GET_CLASS(obj) \ OBJECT_GET_CLASS(IDAUInterfaceClass, (obj), TYPE_IDAU_INTERFACE) -typedef struct IDAUInterface { - Object parent; -} IDAUInterface; +typedef struct IDAUInterface IDAUInterface; #define IREGION_NOTVALID -1 diff --git a/target/arm/internals.h b/target/arm/internals.h index dc9357766c..587a1ddf58 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -94,6 +94,22 @@ FIELD(V7M_EXCRET, RES1, 7, 25) /* including the must-be-1 prefix */ #define M_FAKE_FSR_NSC_EXEC 0xf /* NS executing in S&NSC memory */ #define M_FAKE_FSR_SFAULT 0xe /* SecureFault INVTRAN, INVEP or AUVIOL */ +/** + * raise_exception: Raise the specified exception. + * Raise a guest exception with the specified value, syndrome register + * and target exception level. This should be called from helper functions, + * and never returns because we will longjump back up to the CPU main loop. + */ +void QEMU_NORETURN raise_exception(CPUARMState *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el); + +/* + * Similarly, but also use unwinding to restore cpu state. + */ +void QEMU_NORETURN raise_exception_ra(CPUARMState *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el, + uintptr_t ra); + /* * For AArch64, map a given EL to an index in the banked_spsr array. * Note that this mapping and the AArch32 mapping defined in bank_number() @@ -136,7 +152,22 @@ static inline int bank_number(int mode) g_assert_not_reached(); } -void switch_mode(CPUARMState *, int); +/** + * r14_bank_number: Map CPU mode onto register bank for r14 + * + * Given an AArch32 CPU mode, return the index into the saved register + * banks to use for the R14 (LR) in that mode. This is the same as + * bank_number(), except for the special case of Hyp mode, where + * R14 is shared with USR and SYS, unlike its R13 and SPSR. + * This should be used as the index into env->banked_r14[], and + * bank_number() used for the index into env->banked_r13[] and + * env->banked_spsr[]. + */ +static inline int r14_bank_number(int mode) +{ + return (mode == ARM_CPU_MODE_HYP) ? BANK_USRSYS : bank_number(mode); +} + void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu); void arm_translate_init(void); @@ -205,7 +236,8 @@ static inline unsigned int arm_pamax(ARMCPU *cpu) [4] = 44, [5] = 48, }; - unsigned int parange = extract32(cpu->id_aa64mmfr0, 0, 4); + unsigned int parange = + FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); /* id_aa64mmfr0 is a read-only register so values outside of the * supported mappings can be considered an implementation error. */ @@ -234,7 +266,9 @@ enum arm_exception_class { EC_CP14DTTRAP = 0x06, EC_ADVSIMDFPACCESSTRAP = 0x07, EC_FPIDTRAP = 0x08, + EC_PACTRAP = 0x09, EC_CP14RRTTRAP = 0x0c, + EC_BTITRAP = 0x0d, EC_ILLEGALSTATE = 0x0e, EC_AA32_SVC = 0x11, EC_AA32_HVC = 0x12, @@ -270,14 +304,19 @@ enum arm_exception_class { #define ARM_EL_IL (1 << ARM_EL_IL_SHIFT) #define ARM_EL_ISV (1 << ARM_EL_ISV_SHIFT) +static inline uint32_t syn_get_ec(uint32_t syn) +{ + return syn >> ARM_EL_EC_SHIFT; +} + /* Utility functions for constructing various kinds of syndrome value. * Note that in general we follow the AArch64 syndrome values; in a * few cases the value in HSR for exceptions taken to AArch32 Hyp - * mode differs slightly, so if we ever implemented Hyp mode then the - * syndrome value would need some massaging on exception entry. - * (One example of this is that AArch64 defaults to IL bit set for - * exceptions which don't specifically indicate information about the - * trapping instruction, whereas AArch32 defaults to IL bit clear.) + * mode differs slightly, and we fix this up when populating HSR in + * arm_cpu_do_interrupt_aarch32_hyp(). + * The exception is FP/SIMD access traps -- these report extra information + * when taking an exception to AArch32. For those we include the extra coproc + * and TA fields, and mask them out when taking the exception to AArch64. */ static inline uint32_t syn_uncategorized(void) { @@ -377,9 +416,18 @@ static inline uint32_t syn_cp15_rrt_trap(int cv, int cond, int opc1, int crm, static inline uint32_t syn_fp_access_trap(int cv, int cond, bool is_16bit) { + /* AArch32 FP trap or any AArch64 FP/SIMD trap: TA == 0 coproc == 0xa */ return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) | (is_16bit ? 0 : ARM_EL_IL) - | (cv << 24) | (cond << 20); + | (cv << 24) | (cond << 20) | 0xa; +} + +static inline uint32_t syn_simd_access_trap(int cv, int cond, bool is_16bit) +{ + /* AArch32 SIMD trap: TA == 1 coproc == 0 */ + return (EC_ADVSIMDFPACCESSTRAP << ARM_EL_EC_SHIFT) + | (is_16bit ? 0 : ARM_EL_IL) + | (cv << 24) | (cond << 20) | (1 << 5); } static inline uint32_t syn_sve_access_trap(void) @@ -387,6 +435,16 @@ static inline uint32_t syn_sve_access_trap(void) return EC_SVEACCESSTRAP << ARM_EL_EC_SHIFT; } +static inline uint32_t syn_pactrap(void) +{ + return EC_PACTRAP << ARM_EL_EC_SHIFT; +} + +static inline uint32_t syn_btitrap(int btype) +{ + return (EC_BTITRAP << ARM_EL_EC_SHIFT) | btype; +} + static inline uint32_t syn_insn_abort(int same_el, int ea, int s1ptw, int fsc) { return (EC_INSNABORT << ARM_EL_EC_SHIFT) | (same_el << ARM_EL_EC_SHIFT) @@ -796,4 +854,133 @@ static inline uint32_t arm_debug_exception_fsr(CPUARMState *env) } } +/* Note make_memop_idx reserves 4 bits for mmu_idx, and MO_BSWAP is bit 3. + * Thus a TCGMemOpIdx, without any MO_ALIGN bits, fits in 8 bits. + */ +#define MEMOPIDX_SHIFT 8 + +/** + * v7m_using_psp: Return true if using process stack pointer + * Return true if the CPU is currently using the process stack + * pointer, or false if it is using the main stack pointer. + */ +static inline bool v7m_using_psp(CPUARMState *env) +{ + /* Handler mode always uses the main stack; for thread mode + * the CONTROL.SPSEL bit determines the answer. + * Note that in v7M it is not possible to be in Handler mode with + * CONTROL.SPSEL non-zero, but in v8M it is, so we must check both. + */ + return !arm_v7m_is_handler_mode(env) && + env->v7m.control[env->v7m.secure] & R_V7M_CONTROL_SPSEL_MASK; +} + +/** + * v7m_sp_limit: Return SP limit for current CPU state + * Return the SP limit value for the current CPU security state + * and stack pointer. + */ +static inline uint32_t v7m_sp_limit(CPUARMState *env) +{ + if (v7m_using_psp(env)) { + return env->v7m.psplim[env->v7m.secure]; + } else { + return env->v7m.msplim[env->v7m.secure]; + } +} + +/** + * aarch32_mode_name(): Return name of the AArch32 CPU mode + * @psr: Program Status Register indicating CPU mode + * + * Returns, for debug logging purposes, a printable representation + * of the AArch32 CPU mode ("svc", "usr", etc) as indicated by + * the low bits of the specified PSR. + */ +static inline const char *aarch32_mode_name(uint32_t psr) +{ + static const char cpu_mode_names[16][4] = { + "usr", "fiq", "irq", "svc", "???", "???", "mon", "abt", + "???", "???", "hyp", "und", "???", "???", "???", "sys" + }; + + return cpu_mode_names[psr & 0xf]; +} + +/** + * arm_cpu_update_virq: Update CPU_INTERRUPT_VIRQ bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VIRQ bit in cs->interrupt_request, following + * a change to either the input VIRQ line from the GIC or the HCR_EL2.VI bit. + * Must be called with the iothread lock held. + */ +void arm_cpu_update_virq(ARMCPU *cpu); + +/** + * arm_cpu_update_vfiq: Update CPU_INTERRUPT_VFIQ bit in cs->interrupt_request + * + * Update the CPU_INTERRUPT_VFIQ bit in cs->interrupt_request, following + * a change to either the input VFIQ line from the GIC or the HCR_EL2.VF bit. + * Must be called with the iothread lock held. + */ +void arm_cpu_update_vfiq(ARMCPU *cpu); + +/** + * arm_mmu_idx: + * @env: The cpu environment + * + * Return the full ARMMMUIdx for the current translation regime. + */ +ARMMMUIdx arm_mmu_idx(CPUARMState *env); + +/** + * arm_stage1_mmu_idx: + * @env: The cpu environment + * + * Return the ARMMMUIdx for the stage1 traversal for the current regime. + */ +#ifdef CONFIG_USER_ONLY +static inline ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env) +{ + return ARMMMUIdx_S1NSE0; +} +#else +ARMMMUIdx arm_stage1_mmu_idx(CPUARMState *env); +#endif + +/* + * Parameters of a given virtual address, as extracted from the + * translation control register (TCR) for a given regime. + */ +typedef struct ARMVAParameters { + unsigned tsz : 8; + unsigned select : 1; + bool tbi : 1; + bool tbid : 1; + bool epd : 1; + bool hpd : 1; + bool using16k : 1; + bool using64k : 1; +} ARMVAParameters; + +ARMVAParameters aa64_va_parameters_both(CPUARMState *env, uint64_t va, + ARMMMUIdx mmu_idx); +ARMVAParameters aa64_va_parameters(CPUARMState *env, uint64_t va, + ARMMMUIdx mmu_idx, bool data); + +static inline int exception_target_el(CPUARMState *env) +{ + int target_el = MAX(1, arm_current_el(env)); + + /* + * No such thing as secure EL1 if EL3 is aarch32, + * so update the target EL to EL3 in this case. + */ + if (arm_is_secure(env) && !arm_el_is_aa64(env, 3) && target_el == 1) { + target_el = 3; + } + + return target_el; +} + #endif diff --git a/target/arm/iwmmxt_helper.c b/target/arm/iwmmxt_helper.c index f6a4fc5b7f..24244d012c 100644 --- a/target/arm/iwmmxt_helper.c +++ b/target/arm/iwmmxt_helper.c @@ -27,30 +27,30 @@ /* iwMMXt macros extracted from GNU gdb. */ /* Set the SIMD wCASF flags for 8, 16, 32 or 64-bit operations. */ -#define SIMD8_SET( v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) -#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) -#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) -#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) +#define SIMD8_SET(v, n, b) ((v != 0) << ((((b) + 1) * 4) + (n))) +#define SIMD16_SET(v, n, h) ((v != 0) << ((((h) + 1) * 8) + (n))) +#define SIMD32_SET(v, n, w) ((v != 0) << ((((w) + 1) * 16) + (n))) +#define SIMD64_SET(v, n) ((v != 0) << (32 + (n))) /* Flags to pass as "n" above. */ -#define SIMD_NBIT -1 -#define SIMD_ZBIT -2 -#define SIMD_CBIT -3 -#define SIMD_VBIT -4 +#define SIMD_NBIT -1 +#define SIMD_ZBIT -2 +#define SIMD_CBIT -3 +#define SIMD_VBIT -4 /* Various status bit macros. */ -#define NBIT8(x) ((x) & 0x80) -#define NBIT16(x) ((x) & 0x8000) -#define NBIT32(x) ((x) & 0x80000000) -#define NBIT64(x) ((x) & 0x8000000000000000ULL) -#define ZBIT8(x) (((x) & 0xff) == 0) -#define ZBIT16(x) (((x) & 0xffff) == 0) -#define ZBIT32(x) (((x) & 0xffffffff) == 0) -#define ZBIT64(x) (x == 0) +#define NBIT8(x) ((x) & 0x80) +#define NBIT16(x) ((x) & 0x8000) +#define NBIT32(x) ((x) & 0x80000000) +#define NBIT64(x) ((x) & 0x8000000000000000ULL) +#define ZBIT8(x) (((x) & 0xff) == 0) +#define ZBIT16(x) (((x) & 0xffff) == 0) +#define ZBIT32(x) (((x) & 0xffffffff) == 0) +#define ZBIT64(x) (x == 0) /* Sign extension macros. */ -#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) -#define EXTEND8(a) ((uint32_t) (int8_t) (a)) -#define EXTEND16(a) ((uint32_t) (int16_t) (a)) -#define EXTEND16S(a) ((int32_t) (int16_t) (a)) -#define EXTEND32(a) ((uint64_t) (int32_t) (a)) +#define EXTEND8H(a) ((uint16_t) (int8_t) (a)) +#define EXTEND8(a) ((uint32_t) (int8_t) (a)) +#define EXTEND16(a) ((uint32_t) (int16_t) (a)) +#define EXTEND16S(a) ((int32_t) (int16_t) (a)) +#define EXTEND32(a) ((uint64_t) (int32_t) (a)) uint64_t HELPER(iwmmxt_maddsq)(uint64_t a, uint64_t b) { @@ -159,141 +159,141 @@ uint64_t HELPER(iwmmxt_macuw)(uint64_t a, uint64_t b) #define NZBIT64(x) \ SIMD64_SET(NBIT64(x), SIMD_NBIT) | \ SIMD64_SET(ZBIT64(x), SIMD_ZBIT) -#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ +#define IWMMXT_OP_UNPACK(S, SH0, SH1, SH2, SH3) \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, b)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ - (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ - (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ - (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ +{ \ + a = \ + (((a >> SH0) & 0xff) << 0) | (((b >> SH0) & 0xff) << 8) | \ + (((a >> SH1) & 0xff) << 16) | (((b >> SH1) & 0xff) << 24) | \ + (((a >> SH2) & 0xff) << 32) | (((b >> SH2) & 0xff) << 40) | \ + (((a >> SH3) & 0xff) << 48) | (((b >> SH3) & 0xff) << 56); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ + NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ + NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ + NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, w)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffff) << 0) | \ - (((b >> SH0) & 0xffff) << 16) | \ - (((a >> SH2) & 0xffff) << 32) | \ - (((b >> SH2) & 0xffff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ - NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ +{ \ + a = \ + (((a >> SH0) & 0xffff) << 0) | \ + (((b >> SH0) & 0xffff) << 16) | \ + (((a >> SH2) & 0xffff) << 32) | \ + (((b >> SH2) & 0xffff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 16, 1) | \ + NZBIT8(a >> 32, 2) | NZBIT8(a >> 48, 3); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, l)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - (((a >> SH0) & 0xffffffff) << 0) | \ - (((b >> SH0) & 0xffffffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ +{ \ + a = \ + (((a >> SH0) & 0xffffffff) << 0) | \ + (((b >> SH0) & 0xffffffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ub)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xff) << 0) | \ - (((x >> SH1) & 0xff) << 16) | \ - (((x >> SH2) & 0xff) << 32) | \ - (((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ +{ \ + x = \ + (((x >> SH0) & 0xff) << 0) | \ + (((x >> SH1) & 0xff) << 16) | \ + (((x >> SH2) & 0xff) << 32) | \ + (((x >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ + NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, uw)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - (((x >> SH0) & 0xffff) << 0) | \ - (((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ +{ \ + x = \ + (((x >> SH0) & 0xffff) << 0) | \ + (((x >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, ul)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = (((x >> SH0) & 0xffffffff) << 0); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ +{ \ + x = (((x >> SH0) & 0xffffffff) << 0); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sb)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ - ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ - ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ - ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ - NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ +{ \ + x = \ + ((uint64_t) EXTEND8H((x >> SH0) & 0xff) << 0) | \ + ((uint64_t) EXTEND8H((x >> SH1) & 0xff) << 16) | \ + ((uint64_t) EXTEND8H((x >> SH2) & 0xff) << 32) | \ + ((uint64_t) EXTEND8H((x >> SH3) & 0xff) << 48); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(x >> 0, 0) | NZBIT16(x >> 16, 1) | \ + NZBIT16(x >> 32, 2) | NZBIT16(x >> 48, 3); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sw)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = \ - ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ - ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ +{ \ + x = \ + ((uint64_t) EXTEND16((x >> SH0) & 0xffff) << 0) | \ + ((uint64_t) EXTEND16((x >> SH2) & 0xffff) << 32); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(x >> 0, 0) | NZBIT32(x >> 32, 1); \ return x; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_unpack, glue(S, sl)))(CPUARMState *env, \ uint64_t x) \ -{ \ - x = EXTEND32((x >> SH0) & 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ +{ \ + x = EXTEND32((x >> SH0) & 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = NZBIT64(x >> 0); \ return x; \ } IWMMXT_OP_UNPACK(l, 0, 8, 16, 24) IWMMXT_OP_UNPACK(h, 32, 40, 48, 56) -#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ +#define IWMMXT_OP_CMP(SUFF, Tb, Tw, Tl, O) \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, b)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = \ - CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ - CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ - CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ - CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ - NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ - NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ - NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ +{ \ + a = \ + CMP(0, Tb, O, 0xff) | CMP(8, Tb, O, 0xff) | \ + CMP(16, Tb, O, 0xff) | CMP(24, Tb, O, 0xff) | \ + CMP(32, Tb, O, 0xff) | CMP(40, Tb, O, 0xff) | \ + CMP(48, Tb, O, 0xff) | CMP(56, Tb, O, 0xff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT8(a >> 0, 0) | NZBIT8(a >> 8, 1) | \ + NZBIT8(a >> 16, 2) | NZBIT8(a >> 24, 3) | \ + NZBIT8(a >> 32, 4) | NZBIT8(a >> 40, 5) | \ + NZBIT8(a >> 48, 6) | NZBIT8(a >> 56, 7); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, w)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ - CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ - NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ +{ \ + a = CMP(0, Tw, O, 0xffff) | CMP(16, Tw, O, 0xffff) | \ + CMP(32, Tw, O, 0xffff) | CMP(48, Tw, O, 0xffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT16(a >> 0, 0) | NZBIT16(a >> 16, 1) | \ + NZBIT16(a >> 32, 2) | NZBIT16(a >> 48, 3); \ return a; \ -} \ +} \ uint64_t HELPER(glue(iwmmxt_, glue(SUFF, l)))(CPUARMState *env, \ uint64_t a, uint64_t b) \ -{ \ - a = CMP(0, Tl, O, 0xffffffff) | \ - CMP(32, Tl, O, 0xffffffff); \ - env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ - NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ +{ \ + a = CMP(0, Tl, O, 0xffffffff) | \ + CMP(32, Tl, O, 0xffffffff); \ + env->iwmmxt.cregs[ARM_IWMMXT_wCASF] = \ + NZBIT32(a >> 0, 0) | NZBIT32(a >> 32, 1); \ return a; \ } #define CMP(SHR, TYPE, OPER, MASK) ((((TYPE) ((a >> SHR) & MASK) OPER \ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 65f867d569..79a79f0190 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -18,6 +18,7 @@ #include "qemu/error-report.h" #include "sysemu/sysemu.h" #include "sysemu/kvm.h" +#include "sysemu/kvm_int.h" #include "kvm_arm.h" #include "cpu.h" #include "trace.h" @@ -34,6 +35,7 @@ const KVMCapabilityInfo kvm_arch_required_capabilities[] = { }; static bool cap_has_mp_state; +static bool cap_has_inject_serror_esr; static ARMHostCPUFeatures arm_host_cpu_features; @@ -48,6 +50,12 @@ int kvm_arm_vcpu_init(CPUState *cs) return kvm_vcpu_ioctl(cs, KVM_ARM_VCPU_INIT, &init); } +void kvm_arm_init_serror_injection(CPUState *cs) +{ + cap_has_inject_serror_esr = kvm_check_extension(cs->kvm_state, + KVM_CAP_ARM_INJECT_SERROR_ESR); +} + bool kvm_arm_create_scratch_host_vcpu(const uint32_t *cpus_to_try, int *fdarray, struct kvm_vcpu_init *init) @@ -151,9 +159,19 @@ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) cpu->kvm_target = arm_host_cpu_features.target; cpu->dtb_compatible = arm_host_cpu_features.dtb_compatible; + cpu->isar = arm_host_cpu_features.isar; env->features = arm_host_cpu_features.features; } +int kvm_arm_get_max_vm_ipa_size(MachineState *ms) +{ + KVMState *s = KVM_STATE(ms->accelerator); + int ret; + + ret = kvm_check_extension(s, KVM_CAP_ARM_VM_IPA_SIZE); + return ret > 0 ? ret : 40; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { /* For ARM interrupt delivery is always asynchronous, @@ -198,7 +216,7 @@ typedef struct KVMDevice { int dev_fd; } KVMDevice; -static QSLIST_HEAD(kvm_devices_head, KVMDevice) kvm_devices_head; +static QSLIST_HEAD(, KVMDevice) kvm_devices_head; static void kvm_arm_devlistener_add(MemoryListener *listener, MemoryRegionSection *section) @@ -310,7 +328,7 @@ static int compare_u64(const void *a, const void *b) return 0; } -/* Initialize the CPUState's cpreg list according to the kernel's +/* Initialize the ARMCPU cpreg list according to the kernel's * definition of what CPU registers it knows about (and throw away * the previous TCG-created cpreg list). */ @@ -522,6 +540,59 @@ int kvm_arm_sync_mpstate_to_qemu(ARMCPU *cpu) return 0; } +int kvm_put_vcpu_events(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + struct kvm_vcpu_events events; + int ret; + + if (!kvm_has_vcpu_events()) { + return 0; + } + + memset(&events, 0, sizeof(events)); + events.exception.serror_pending = env->serror.pending; + + /* Inject SError to guest with specified syndrome if host kernel + * supports it, otherwise inject SError without syndrome. + */ + if (cap_has_inject_serror_esr) { + events.exception.serror_has_esr = env->serror.has_esr; + events.exception.serror_esr = env->serror.esr; + } + + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_SET_VCPU_EVENTS, &events); + if (ret) { + error_report("failed to put vcpu events"); + } + + return ret; +} + +int kvm_get_vcpu_events(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + struct kvm_vcpu_events events; + int ret; + + if (!kvm_has_vcpu_events()) { + return 0; + } + + memset(&events, 0, sizeof(events)); + ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_VCPU_EVENTS, &events); + if (ret) { + error_report("failed to get vcpu events"); + return ret; + } + + env->serror.pending = events.exception.serror_pending; + env->serror.has_esr = events.exception.serror_has_esr; + env->serror.esr = events.exception.serror_esr; + + return 0; +} + void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { } diff --git a/target/arm/kvm32.c b/target/arm/kvm32.c index 4e91c11796..50327989dc 100644 --- a/target/arm/kvm32.c +++ b/target/arm/kvm32.c @@ -28,6 +28,14 @@ static inline void set_feature(uint64_t *features, int feature) *features |= 1ULL << feature; } +static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) +{ + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret }; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U32); + return ioctl(fd, KVM_GET_ONE_REG, &idreg); +} + bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and @@ -35,9 +43,10 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * we have to create a scratch VM, create a single CPU inside it, * and then query that CPU for the relevant ID registers. */ - int i, ret, fdarray[3]; - uint32_t midr, id_pfr0, mvfr1; + int err = 0, fdarray[3]; + uint32_t midr, id_pfr0; uint64_t features = 0; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however * we know these will only support creating one kind of guest CPU, * which is its preferred CPU type. @@ -47,23 +56,6 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) QEMU_KVM_ARM_TARGET_NONE }; struct kvm_vcpu_init init; - struct kvm_one_reg idregs[] = { - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 0, 0, 0), - .addr = (uintptr_t)&midr, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | ENCODE_CP_REG(15, 0, 0, 0, 1, 0, 0), - .addr = (uintptr_t)&id_pfr0, - }, - { - .id = KVM_REG_ARM | KVM_REG_SIZE_U32 - | KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1, - .addr = (uintptr_t)&mvfr1, - }, - }; if (!kvm_arm_create_scratch_host_vcpu(cpus_to_try, fdarray, &init)) { return false; @@ -77,16 +69,45 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) */ ahcf->dtb_compatible = "arm,arm-v7"; - for (i = 0; i < ARRAY_SIZE(idregs); i++) { - ret = ioctl(fdarray[2], KVM_GET_ONE_REG, &idregs[i]); - if (ret) { - break; - } + err |= read_sys_reg32(fdarray[2], &midr, ARM_CP15_REG32(0, 0, 0, 0)); + err |= read_sys_reg32(fdarray[2], &id_pfr0, ARM_CP15_REG32(0, 0, 1, 0)); + + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, + ARM_CP15_REG32(0, 0, 2, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, + ARM_CP15_REG32(0, 0, 2, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, + ARM_CP15_REG32(0, 0, 2, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, + ARM_CP15_REG32(0, 0, 2, 3)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, + ARM_CP15_REG32(0, 0, 2, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, + ARM_CP15_REG32(0, 0, 2, 5)); + if (read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, + ARM_CP15_REG32(0, 0, 2, 7))) { + /* + * Older kernels don't support reading ID_ISAR6. This register was + * only introduced in ARMv8, so we can assume that it is zero on a + * CPU that a kernel this old is running on. + */ + ahcf->isar.id_isar6 = 0; } + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR0); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + KVM_REG_ARM | KVM_REG_SIZE_U32 | + KVM_REG_ARM_VFP | KVM_REG_ARM_VFP_MVFR1); + /* + * FIXME: There is not yet a way to read MVFR2. + * Fortunately there is not yet anything in there that affects migration. + */ + kvm_arm_destroy_scratch_host_vcpu(fdarray); - if (ret) { + if (err < 0) { return false; } @@ -104,13 +125,10 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) if (extract32(id_pfr0, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_THUMB2EE); } - if (extract32(mvfr1, 20, 4) == 1) { - set_feature(&features, ARM_FEATURE_VFP_FP16); - } - if (extract32(mvfr1, 12, 4) == 1) { + if (extract32(ahcf->isar.mvfr1, 12, 4) == 1) { set_feature(&features, ARM_FEATURE_NEON); } - if (extract32(mvfr1, 28, 4) == 1) { + if (extract32(ahcf->isar.mvfr1, 28, 4) == 1) { /* FMAC support implies VFPv4 */ set_feature(&features, ARM_FEATURE_VFP4); } @@ -217,6 +235,9 @@ int kvm_arch_init_vcpu(CPUState *cs) } cpu->mp_affinity = mpidr & ARM32_AFFINITY_MASK; + /* Check whether userspace can specify guest syndrome value */ + kvm_arm_init_serror_injection(cs); + return kvm_arm_init_cpreg_list(cpu); } @@ -315,8 +336,8 @@ int kvm_arch_put_registers(CPUState *cs, int level) memcpy(env->usr_regs, env->regs + 8, 5 * sizeof(uint32_t)); } env->banked_r13[bn] = env->regs[13]; - env->banked_r14[bn] = env->regs[14]; env->banked_spsr[bn] = env->spsr; + env->banked_r14[r14_bank_number(mode)] = env->regs[14]; /* Now we can safely copy stuff down to the kernel */ for (i = 0; i < ARRAY_SIZE(regs); i++) { @@ -358,6 +379,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + ret = kvm_put_vcpu_events(cpu); + if (ret) { + return ret; + } + /* Note that we do not call write_cpustate_to_list() * here, so we are only writing the tuple list back to * KVM. This is safe because nothing can change the @@ -422,8 +448,8 @@ int kvm_arch_get_registers(CPUState *cs) memcpy(env->regs + 8, env->usr_regs, 5 * sizeof(uint32_t)); } env->regs[13] = env->banked_r13[bn]; - env->regs[14] = env->banked_r14[bn]; env->spsr = env->banked_spsr[bn]; + env->regs[14] = env->banked_r14[r14_bank_number(mode)]; /* VFP registers */ r.id = KVM_REG_ARM | KVM_REG_SIZE_U64 | KVM_REG_ARM_VFP; @@ -445,6 +471,11 @@ int kvm_arch_get_registers(CPUState *cs) } vfp_set_fpscr(env, fpscr); + ret = kvm_get_vcpu_events(cpu); + if (ret) { + return ret; + } + if (!write_kvmstate_to_list(cpu)) { return EINVAL; } diff --git a/target/arm/kvm64.c b/target/arm/kvm64.c index e0b8246283..089af9c5f0 100644 --- a/target/arm/kvm64.c +++ b/target/arm/kvm64.c @@ -103,7 +103,7 @@ static void kvm_arm_init_debug(CPUState *cs) * capable of fancier matching but that will require exposing that * fanciness to GDB's interface * - * D7.3.2 DBGBCR_EL1, Debug Breakpoint Control Registers + * DBGBCR_EL1, Debug Breakpoint Control Registers * * 31 24 23 20 19 16 15 14 13 12 9 8 5 4 3 2 1 0 * +------+------+-------+-----+----+------+-----+------+-----+---+ @@ -115,12 +115,25 @@ static void kvm_arm_init_debug(CPUState *cs) * SSC/HMC/PMC: Security, Higher and Priv access control (Table D-12) * BAS: Byte Address Select (RES1 for AArch64) * E: Enable bit + * + * DBGBVR_EL1, Debug Breakpoint Value Registers + * + * 63 53 52 49 48 2 1 0 + * +------+-----------+----------+-----+ + * | RESS | VA[52:49] | VA[48:2] | 0 0 | + * +------+-----------+----------+-----+ + * + * Depending on the addressing mode bits the top bits of the register + * are a sign extension of the highest applicable VA bit. Some + * versions of GDB don't do it correctly so we ensure they are correct + * here so future PC comparisons will work properly. */ + static int insert_hw_breakpoint(target_ulong addr) { HWBreakpoint brk = { .bcr = 0x1, /* BCR E=1, enable */ - .bvr = addr + .bvr = sextract64(addr, 0, 53) }; if (cur_hw_bps >= max_hw_bps) { @@ -443,17 +456,40 @@ static inline void unset_feature(uint64_t *features, int feature) *features &= ~(1ULL << feature); } +static int read_sys_reg32(int fd, uint32_t *pret, uint64_t id) +{ + uint64_t ret; + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)&ret }; + int err; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); + err = ioctl(fd, KVM_GET_ONE_REG, &idreg); + if (err < 0) { + return -1; + } + *pret = ret; + return 0; +} + +static int read_sys_reg64(int fd, uint64_t *pret, uint64_t id) +{ + struct kvm_one_reg idreg = { .id = id, .addr = (uintptr_t)pret }; + + assert((id & KVM_REG_SIZE_MASK) == KVM_REG_SIZE_U64); + return ioctl(fd, KVM_GET_ONE_REG, &idreg); +} + bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and * fill out the ARMHostCPUClass fields accordingly. To do this * we have to create a scratch VM, create a single CPU inside it, * and then query that CPU for the relevant ID registers. - * For AArch64 we currently don't care about ID registers at - * all; we just want to know the CPU type. */ int fdarray[3]; uint64_t features = 0; + int err; + /* Old kernels may not know about the PREFERRED_TARGET ioctl: however * we know these will only support creating one kind of guest CPU, * which is its preferred CPU type. Fortunately these old kernels @@ -474,8 +510,75 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->target = init.target; ahcf->dtb_compatible = "arm,arm-v8"; + err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, + ARM64_SYS_REG(3, 0, 0, 4, 0)); + if (unlikely(err < 0)) { + /* + * Before v4.15, the kernel only exposed a limited number of system + * registers, not including any of the interesting AArch64 ID regs. + * For the most part we could leave these fields as zero with minimal + * effect, since this does not affect the values seen by the guest. + * + * However, it could cause problems down the line for QEMU, + * so provide a minimal v8.0 default. + * + * ??? Could read MIDR and use knowledge from cpu64.c. + * ??? Could map a page of memory into our temp guest and + * run the tiniest of hand-crafted kernels to extract + * the values seen by the guest. + * ??? Either of these sounds like too much effort just + * to work around running a modern host kernel. + */ + ahcf->isar.id_aa64pfr0 = 0x00000011; /* EL1&0, AArch64 only */ + err = 0; + } else { + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr1, + ARM64_SYS_REG(3, 0, 0, 4, 1)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar0, + ARM64_SYS_REG(3, 0, 0, 6, 0)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar1, + ARM64_SYS_REG(3, 0, 0, 6, 1)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, + ARM64_SYS_REG(3, 0, 0, 7, 0)); + err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr1, + ARM64_SYS_REG(3, 0, 0, 7, 1)); + + /* + * Note that if AArch32 support is not present in the host, + * the AArch32 sysregs are present to be read, but will + * return UNKNOWN values. This is neither better nor worse + * than skipping the reads and leaving 0, as we must avoid + * considering the values in every case. + */ + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, + ARM64_SYS_REG(3, 0, 0, 2, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, + ARM64_SYS_REG(3, 0, 0, 2, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, + ARM64_SYS_REG(3, 0, 0, 2, 2)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, + ARM64_SYS_REG(3, 0, 0, 2, 3)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, + ARM64_SYS_REG(3, 0, 0, 2, 4)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, + ARM64_SYS_REG(3, 0, 0, 2, 5)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, + ARM64_SYS_REG(3, 0, 0, 2, 7)); + + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + ARM64_SYS_REG(3, 0, 0, 3, 0)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + ARM64_SYS_REG(3, 0, 0, 3, 1)); + err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, + ARM64_SYS_REG(3, 0, 0, 3, 2)); + } + kvm_arm_destroy_scratch_host_vcpu(fdarray); + if (err < 0) { + return false; + } + /* We can assume any KVM supporting CPU is at least a v8 * with VFPv4+Neon; this in turn implies most of the other * feature bits. @@ -546,6 +649,9 @@ int kvm_arch_init_vcpu(CPUState *cs) kvm_arm_init_debug(cs); + /* Check whether user space can specify guest syndrome value */ + kvm_arm_init_serror_injection(cs); + return kvm_arm_init_cpreg_list(cpu); } @@ -727,6 +833,11 @@ int kvm_arch_put_registers(CPUState *cs, int level) return ret; } + ret = kvm_put_vcpu_events(cpu); + if (ret) { + return ret; + } + if (!write_list_to_kvmstate(cpu, level)) { return EINVAL; } @@ -863,6 +974,11 @@ int kvm_arch_get_registers(CPUState *cs) } vfp_set_fpcr(env, fpr); + ret = kvm_get_vcpu_events(cpu); + if (ret) { + return ret; + } + if (!write_kvmstate_to_list(cpu)) { return EINVAL; } @@ -920,7 +1036,7 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) { - int hsr_ec = debug_exit->hsr >> ARM_EL_EC_SHIFT; + int hsr_ec = syn_get_ec(debug_exit->hsr); ARMCPU *cpu = ARM_CPU(cs); CPUClass *cc = CPU_GET_CLASS(cs); CPUARMState *env = &cpu->env; @@ -974,7 +1090,10 @@ bool kvm_arm_handle_debug(CPUState *cs, struct kvm_debug_exit_arch *debug_exit) cs->exception_index = EXCP_BKPT; env->exception.syndrome = debug_exit->hsr; env->exception.vaddress = debug_exit->far; + env->exception.target_el = 1; + qemu_mutex_lock_iothread(); cc->do_interrupt(cs); + qemu_mutex_unlock_iothread(); return false; } diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 863f205822..2a07333c61 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -50,9 +50,9 @@ void kvm_arm_register_device(MemoryRegion *mr, uint64_t devid, uint64_t group, /** * kvm_arm_init_cpreg_list: - * @cs: CPUState + * @cpu: ARMCPU * - * Initialize the CPUState's cpreg list according to the kernel's + * Initialize the ARMCPU cpreg list according to the kernel's * definition of what CPU registers it knows about (and throw away * the previous TCG-created cpreg list). * @@ -121,6 +121,30 @@ bool write_kvmstate_to_list(ARMCPU *cpu); */ void kvm_arm_reset_vcpu(ARMCPU *cpu); +/** + * kvm_arm_init_serror_injection: + * @cs: CPUState + * + * Check whether KVM can set guest SError syndrome. + */ +void kvm_arm_init_serror_injection(CPUState *cs); + +/** + * kvm_get_vcpu_events: + * @cpu: ARMCPU + * + * Get VCPU related state from kvm. + */ +int kvm_get_vcpu_events(ARMCPU *cpu); + +/** + * kvm_put_vcpu_events: + * @cpu: ARMCPU + * + * Put VCPU related state to kvm. + */ +int kvm_put_vcpu_events(ARMCPU *cpu); + #ifdef CONFIG_KVM /** * kvm_arm_create_scratch_host_vcpu: @@ -159,6 +183,7 @@ void kvm_arm_destroy_scratch_host_vcpu(int *fdarray); * by asking the host kernel) */ typedef struct ARMHostCPUFeatures { + ARMISARegisters isar; uint64_t features; uint32_t target; const char *dtb_compatible; @@ -182,6 +207,14 @@ bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf); */ void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu); +/** + * kvm_arm_get_max_vm_ipa_size - Returns the number of bits in the + * IPA address space supported by KVM + * + * @ms: Machine state handle + */ +int kvm_arm_get_max_vm_ipa_size(MachineState *ms); + /** * kvm_arm_sync_mpstate_to_kvm * @cpu: ARMCPU @@ -214,6 +247,11 @@ static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) cpu->host_cpu_probe_failed = true; } +static inline int kvm_arm_get_max_vm_ipa_size(MachineState *ms) +{ + return -ENOENT; +} + static inline int kvm_arm_vgic_probe(void) { return 0; diff --git a/target/arm/machine.c b/target/arm/machine.c index ff4ec22bf7..b292549614 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -18,7 +18,7 @@ static bool vfp_needed(void *opaque) } static int get_fpscr(QEMUFile *f, void *opaque, size_t size, - VMStateField *field) + const VMStateField *field) { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; @@ -29,7 +29,7 @@ static int get_fpscr(QEMUFile *f, void *opaque, size_t size, } static int put_fpscr(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; @@ -131,9 +131,8 @@ static const VMStateDescription vmstate_iwmmxt = { static bool sve_needed(void *opaque) { ARMCPU *cpu = opaque; - CPUARMState *env = &cpu->env; - return arm_feature(env, ARM_FEATURE_SVE); + return cpu_isar_feature(aa64_sve, cpu); } /* The first two words of each Zreg is stored in VFP state. */ @@ -172,6 +171,43 @@ static const VMStateDescription vmstate_sve = { }; #endif /* AARCH64 */ +static bool serror_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + return env->serror.pending != 0; +} + +static const VMStateDescription vmstate_serror = { + .name = "cpu/serror", + .version_id = 1, + .minimum_version_id = 1, + .needed = serror_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT8(env.serror.pending, ARMCPU), + VMSTATE_UINT8(env.serror.has_esr, ARMCPU), + VMSTATE_UINT64(env.serror.esr, ARMCPU), + VMSTATE_END_OF_LIST() + } +}; + +static bool irq_line_state_needed(void *opaque) +{ + return true; +} + +static const VMStateDescription vmstate_irq_line_state = { + .name = "cpu/irq-line-state", + .version_id = 1, + .minimum_version_id = 1, + .needed = irq_line_state_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT32(env.irq_line_state, ARMCPU), + VMSTATE_END_OF_LIST() + } +}; + static bool m_needed(void *opaque) { ARMCPU *cpu = opaque; @@ -467,7 +503,7 @@ static const VMStateDescription vmstate_m_security = { }; static int get_cpsr(QEMUFile *f, void *opaque, size_t size, - VMStateField *field) + const VMStateField *field) { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; @@ -523,7 +559,7 @@ static int get_cpsr(QEMUFile *f, void *opaque, size_t size, } static int put_cpsr(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { ARMCPU *cpu = opaque; CPUARMState *env = &cpu->env; @@ -549,7 +585,7 @@ static const VMStateInfo vmstate_cpsr = { }; static int get_power(QEMUFile *f, void *opaque, size_t size, - VMStateField *field) + const VMStateField *field) { ARMCPU *cpu = opaque; bool powered_off = qemu_get_byte(f); @@ -558,7 +594,7 @@ static int get_power(QEMUFile *f, void *opaque, size_t size, } static int put_power(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { ARMCPU *cpu = opaque; @@ -584,6 +620,10 @@ static int cpu_pre_save(void *opaque) { ARMCPU *cpu = opaque; + if (!kvm_enabled()) { + pmu_op_start(&cpu->env); + } + if (kvm_enabled()) { if (!write_kvmstate_to_list(cpu)) { /* This should never fail */ @@ -605,11 +645,59 @@ static int cpu_pre_save(void *opaque) return 0; } +static int cpu_post_save(void *opaque) +{ + ARMCPU *cpu = opaque; + + if (!kvm_enabled()) { + pmu_op_finish(&cpu->env); + } + + return 0; +} + +static int cpu_pre_load(void *opaque) +{ + ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; + + /* + * Pre-initialize irq_line_state to a value that's never valid as + * real data, so cpu_post_load() can tell whether we've seen the + * irq-line-state subsection in the incoming migration state. + */ + env->irq_line_state = UINT32_MAX; + + if (!kvm_enabled()) { + pmu_op_start(&cpu->env); + } + + return 0; +} + static int cpu_post_load(void *opaque, int version_id) { ARMCPU *cpu = opaque; + CPUARMState *env = &cpu->env; int i, v; + /* + * Handle migration compatibility from old QEMU which didn't + * send the irq-line-state subsection. A QEMU without it did not + * implement the HCR_EL2.{VI,VF} bits as generating interrupts, + * so for TCG the line state matches the bits set in cs->interrupt_request. + * For KVM the line state is not stored in cs->interrupt_request + * and so this will leave irq_line_state as 0, but this is OK because + * we only need to care about it for TCG. + */ + if (env->irq_line_state == UINT32_MAX) { + CPUState *cs = CPU(cpu); + + env->irq_line_state = cs->interrupt_request & + (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIQ | + CPU_INTERRUPT_VIRQ | CPU_INTERRUPT_VFIQ); + } + /* Update the values list from the incoming migration data. * Anything in the incoming data which we don't know about is * a migration failure; anything we know about but the incoming @@ -652,6 +740,10 @@ static int cpu_post_load(void *opaque, int version_id) hw_breakpoint_update_all(cpu); hw_watchpoint_update_all(cpu); + if (!kvm_enabled()) { + pmu_op_finish(&cpu->env); + } + return 0; } @@ -660,6 +752,8 @@ const VMStateDescription vmstate_arm_cpu = { .version_id = 22, .minimum_version_id = 22, .pre_save = cpu_pre_save, + .post_save = cpu_post_save, + .pre_load = cpu_pre_load, .post_load = cpu_post_load, .fields = (VMStateField[]) { VMSTATE_UINT32_ARRAY(env.regs, ARMCPU, 16), @@ -726,6 +820,8 @@ const VMStateDescription vmstate_arm_cpu = { #ifdef TARGET_AARCH64 &vmstate_sve, #endif + &vmstate_serror, + &vmstate_irq_line_state, NULL } }; diff --git a/target/arm/monitor.c b/target/arm/monitor.c index 4cdd2676dd..41b32b94b2 100644 --- a/target/arm/monitor.c +++ b/target/arm/monitor.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "hw/boards.h" #include "kvm_arm.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-target.h" static GICCapability *gic_cap_new(int version) { diff --git a/target/arm/neon_helper.c b/target/arm/neon_helper.c index c2c6491a83..ed1c6fc41c 100644 --- a/target/arm/neon_helper.c +++ b/target/arm/neon_helper.c @@ -15,7 +15,7 @@ #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) -#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] |= CPSR_Q +#define SET_QC() env->vfp.qc[0] = 1 #define NEON_TYPE1(name, type) \ typedef struct \ @@ -581,12 +581,6 @@ NEON_VOP(cge_u32, neon_u32, 1) #undef NEON_FN #define NEON_FN(dest, src1, src2) dest = (src1 < src2) ? src1 : src2 -NEON_VOP(min_s8, neon_s8, 4) -NEON_VOP(min_u8, neon_u8, 4) -NEON_VOP(min_s16, neon_s16, 2) -NEON_VOP(min_u16, neon_u16, 2) -NEON_VOP(min_s32, neon_s32, 1) -NEON_VOP(min_u32, neon_u32, 1) NEON_POP(pmin_s8, neon_s8, 4) NEON_POP(pmin_u8, neon_u8, 4) NEON_POP(pmin_s16, neon_s16, 2) @@ -594,12 +588,6 @@ NEON_POP(pmin_u16, neon_u16, 2) #undef NEON_FN #define NEON_FN(dest, src1, src2) dest = (src1 > src2) ? src1 : src2 -NEON_VOP(max_s8, neon_s8, 4) -NEON_VOP(max_u8, neon_u8, 4) -NEON_VOP(max_s16, neon_s16, 2) -NEON_VOP(max_u16, neon_u16, 2) -NEON_VOP(max_s32, neon_s32, 1) -NEON_VOP(max_u32, neon_u32, 1) NEON_POP(pmax_s8, neon_s8, 4) NEON_POP(pmax_u8, neon_u8, 4) NEON_POP(pmax_s16, neon_s16, 2) diff --git a/target/arm/op_helper.c b/target/arm/op_helper.c index f728f25e4b..8698b4dc83 100644 --- a/target/arm/op_helper.c +++ b/target/arm/op_helper.c @@ -28,30 +28,44 @@ #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) -static void raise_exception(CPUARMState *env, uint32_t excp, - uint32_t syndrome, uint32_t target_el) +static CPUState *do_raise_exception(CPUARMState *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el) { CPUState *cs = CPU(arm_env_get_cpu(env)); + if (target_el == 1 && (arm_hcr_el2_eff(env) & HCR_TGE)) { + /* + * Redirect NS EL1 exceptions to NS EL2. These are reported with + * their original syndrome register value, with the exception of + * SIMD/FP access traps, which are reported as uncategorized + * (see DDI0478C.a D1.10.4) + */ + target_el = 2; + if (syn_get_ec(syndrome) == EC_ADVSIMDFPACCESSTRAP) { + syndrome = syn_uncategorized(); + } + } + assert(!excp_is_internal(excp)); cs->exception_index = excp; env->exception.syndrome = syndrome; env->exception.target_el = target_el; + + return cs; +} + +void raise_exception(CPUARMState *env, uint32_t excp, + uint32_t syndrome, uint32_t target_el) +{ + CPUState *cs = do_raise_exception(env, excp, syndrome, target_el); cpu_loop_exit(cs); } -static int exception_target_el(CPUARMState *env) +void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, + uint32_t target_el, uintptr_t ra) { - int target_el = MAX(1, arm_current_el(env)); - - /* No such thing as secure EL1 if EL3 is aarch32, so update the target EL - * to EL3 in this case. - */ - if (arm_is_secure(env) && !arm_el_is_aa64(env, 3) && target_el == 1) { - target_el = 3; - } - - return target_el; + CPUState *cs = do_raise_exception(env, excp, syndrome, target_el); + cpu_loop_exit_restore(cs, ra); } uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, void *vn, @@ -224,6 +238,25 @@ void arm_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, #endif /* !defined(CONFIG_USER_ONLY) */ +void HELPER(v8m_stackcheck)(CPUARMState *env, uint32_t newvalue) +{ + /* + * Perform the v8M stack limit check for SP updates from translated code, + * raising an exception if the limit is breached. + */ + if (newvalue < v7m_sp_limit(env)) { + CPUState *cs = CPU(arm_env_get_cpu(env)); + + /* + * Stack limit exceptions are a rare case, so rather than syncing + * PC/condbits before the call, we use cpu_restore_state() to + * get them right before raising the exception. + */ + cpu_restore_state(cs, GETPC(), true); + raise_exception(env, EXCP_STKOF, 0, 1); + } +} + uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b) { uint32_t res = a + b; @@ -395,9 +428,9 @@ static inline int check_wfx_trap(CPUARMState *env, bool is_wfe) * No need for ARM_FEATURE check as if HCR_EL2 doesn't exist the * bits will be zero indicating no trap. */ - if (cur_el < 2 && !arm_is_secure(env)) { - mask = (is_wfe) ? HCR_TWE : HCR_TWI; - if (env->cp15.hcr_el2 & mask) { + if (cur_el < 2) { + mask = is_wfe ? HCR_TWE : HCR_TWI; + if (arm_hcr_el2_eff(env) & mask) { return 2; } } @@ -597,6 +630,14 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, */ int curmode = env->uncached_cpsr & CPSR_M; + if (regno == 17) { + /* ELR_Hyp: a special case because access from tgtmode is OK */ + if (curmode != ARM_CPU_MODE_HYP && curmode != ARM_CPU_MODE_MON) { + goto undef; + } + return; + } + if (curmode == tgtmode) { goto undef; } @@ -624,17 +665,9 @@ static void msr_mrs_banked_exc_checks(CPUARMState *env, uint32_t tgtmode, } 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; + /* SPSR_Hyp, r13_hyp: accessible from Monitor mode only */ + if (curmode != ARM_CPU_MODE_MON) { + goto undef; } } @@ -661,7 +694,7 @@ void HELPER(msr_banked)(CPUARMState *env, uint32_t value, uint32_t tgtmode, env->banked_r13[bank_number(tgtmode)] = value; break; case 14: - env->banked_r14[bank_number(tgtmode)] = value; + env->banked_r14[r14_bank_number(tgtmode)] = value; break; case 8 ... 12: switch (tgtmode) { @@ -692,7 +725,7 @@ uint32_t HELPER(mrs_banked)(CPUARMState *env, uint32_t tgtmode, uint32_t regno) case 13: return env->banked_r13[bank_number(tgtmode)]; case 14: - return env->banked_r14[bank_number(tgtmode)]; + return env->banked_r14[r14_bank_number(tgtmode)]; case 8 ... 12: switch (tgtmode) { case ARM_CPU_MODE_USR: @@ -828,39 +861,6 @@ uint64_t HELPER(get_cp_reg64)(CPUARMState *env, void *rip) return res; } -void HELPER(msr_i_pstate)(CPUARMState *env, uint32_t op, uint32_t imm) -{ - /* MSR_i to update PSTATE. This is OK from EL0 only if UMA is set. - * Note that SPSel is never OK from EL0; we rely on handle_msr_i() - * to catch that case at translate time. - */ - if (arm_current_el(env) == 0 && !(env->cp15.sctlr_el[1] & SCTLR_UMA)) { - uint32_t syndrome = syn_aa64_sysregtrap(0, extract32(op, 0, 3), - extract32(op, 3, 3), 4, - imm, 0x1f, 0); - raise_exception(env, EXCP_UDEF, syndrome, exception_target_el(env)); - } - - switch (op) { - case 0x05: /* SPSel */ - update_spsel(env, imm); - break; - case 0x1e: /* DAIFSet */ - env->daif |= (imm << 6) & PSTATE_DAIF; - break; - case 0x1f: /* DAIFClear */ - env->daif &= ~((imm << 6) & PSTATE_DAIF); - break; - default: - g_assert_not_reached(); - } -} - -void HELPER(clear_pstate_ss)(CPUARMState *env) -{ - env->pstate &= ~PSTATE_SS; -} - void HELPER(pre_hvc)(CPUARMState *env) { ARMCPU *cpu = arm_env_get_cpu(env); @@ -906,7 +906,38 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) ARMCPU *cpu = arm_env_get_cpu(env); int cur_el = arm_current_el(env); bool secure = arm_is_secure(env); - bool smd = env->cp15.scr_el3 & SCR_SMD; + bool smd_flag = env->cp15.scr_el3 & SCR_SMD; + + /* + * SMC behaviour is summarized in the following table. + * This helper handles the "Trap to EL2" and "Undef insn" cases. + * The "Trap to EL3" and "PSCI call" cases are handled in the exception + * helper. + * + * -> ARM_FEATURE_EL3 and !SMD + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Trap to EL3 + * Conduit not SMC Trap to EL2 Trap to EL3 + * + * + * -> ARM_FEATURE_EL3 and SMD + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Undef insn + * Conduit not SMC Trap to EL2 Undef insn + * + * + * -> !ARM_FEATURE_EL3 + * HCR_TSC && NS EL1 !HCR_TSC || !NS EL1 + * + * Conduit SMC, valid call Trap to EL2 PSCI Call + * Conduit SMC, inval call Trap to EL2 Undef insn + * Conduit not SMC Undef insn Undef insn + */ + /* 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. @@ -914,7 +945,8 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * 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 = arm_feature(env, ARM_FEATURE_AARCH64) ? smd : smd && !secure; + bool smd = arm_feature(env, ARM_FEATURE_AARCH64) ? smd_flag + : smd_flag && !secure; if (!arm_feature(env, ARM_FEATURE_EL3) && cpu->psci_conduit != QEMU_PSCI_CONDUIT_SMC) { @@ -924,177 +956,32 @@ void HELPER(pre_smc)(CPUARMState *env, uint32_t syndrome) * to forbid its EL1 from making PSCI calls into QEMU's * "firmware" via HCR.TSC, so for these purposes treat * PSCI-via-SMC as implying an EL3. + * This handles the very last line of the previous table. */ - undef = true; - } else if (!secure && cur_el == 1 && (env->cp15.hcr_el2 & HCR_TSC)) { + raise_exception(env, EXCP_UDEF, syn_uncategorized(), + exception_target_el(env)); + } + + if (cur_el == 1 && (arm_hcr_el2_eff(env) & HCR_TSC)) { /* In NS EL1, HCR controlled routing to EL2 has priority over SMD. * We also want an EL2 guest to be able to forbid its EL1 from * making PSCI calls into QEMU's "firmware" via HCR.TSC. + * This handles all the "Trap to EL2" cases of the previous table. */ raise_exception(env, EXCP_HYP_TRAP, syndrome, 2); } - /* If PSCI is enabled and this looks like a valid PSCI call then - * suppress the UNDEF -- we'll catch the SMC exception and - * implement the PSCI call behaviour there. + /* Catch the two remaining "Undef insn" cases of the previous table: + * - PSCI conduit is SMC but we don't have a valid PCSI call, + * - We don't have EL3 or SMD is set. */ - if (undef && !arm_is_psci_call(cpu, EXCP_SMC)) { + if (!arm_is_psci_call(cpu, EXCP_SMC) && + (smd || !arm_feature(env, ARM_FEATURE_EL3))) { raise_exception(env, EXCP_UDEF, syn_uncategorized(), exception_target_el(env)); } } -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); - - arm_clear_exclusive(env); - - /* We must squash the PSTATE.SS bit to zero unless both of the - * following hold: - * 1. debug exceptions are currently disabled - * 2. singlestep will be active in the EL we return to - * We check 1 here and 2 after we've done the pstate/cpsr write() to - * transition to the EL we're going to. - */ - if (arm_generate_debug_exceptions(env)) { - spsr &= ~PSTATE_SS; - } - - 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; - } - - qemu_mutex_lock_iothread(); - arm_call_pre_el_change_hook(arm_env_get_cpu(env)); - qemu_mutex_unlock_iothread(); - - if (!return_to_aa64) { - env->aarch64 = 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); - - if (spsr & CPSR_T) { - env->regs[15] = env->elr_el[cur_el] & ~0x1; - } else { - env->regs[15] = env->elr_el[cur_el] & ~0x3; - } - qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " - "AArch32 EL%d PC 0x%" PRIx32 "\n", - cur_el, new_el, env->regs[15]); - } else { - env->aarch64 = 1; - pstate_write(env, spsr); - if (!arm_singlestep_active(env)) { - env->pstate &= ~PSTATE_SS; - } - aarch64_restore_sp(env, new_el); - env->pc = env->elr_el[cur_el]; - qemu_log_mask(CPU_LOG_INT, "Exception return from AArch64 EL%d to " - "AArch64 EL%d PC 0x%" PRIx64 "\n", - cur_el, new_el, env->pc); - } - - qemu_mutex_lock_iothread(); - arm_call_el_change_hook(arm_env_get_cpu(env)); - qemu_mutex_unlock_iothread(); - - return; - -illegal_return: - /* Illegal return events of various kinds have architecturally - * mandated behaviour: - * restore NZCV and DAIF from SPSR_ELx - * set PSTATE.IL - * restore PC from ELR_ELx - * no change to exception level, execution state or stack pointer - */ - env->pstate |= PSTATE_IL; - env->pc = env->elr_el[cur_el]; - spsr &= PSTATE_NZCV | PSTATE_DAIF; - spsr |= pstate_read(env) & ~(PSTATE_NZCV | PSTATE_DAIF); - pstate_write(env, spsr); - if (!arm_singlestep_active(env)) { - env->pstate &= ~PSTATE_SS; - } - qemu_log_mask(LOG_GUEST_ERROR, "Illegal exception return at EL%d: " - "resuming execution at 0x%" PRIx64 "\n", cur_el, env->pc); -} - /* Return true if the linked breakpoint entry lbn passes its checks */ static bool linked_bp_matches(ARMCPU *cpu, int lbn) { diff --git a/target/arm/pauth_helper.c b/target/arm/pauth_helper.c new file mode 100644 index 0000000000..d750f96edf --- /dev/null +++ b/target/arm/pauth_helper.c @@ -0,0 +1,497 @@ +/* + * ARM v8.3-PAuth Operations + * + * Copyright (c) 2019 Linaro, Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 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 "internals.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" +#include "exec/helper-proto.h" +#include "tcg/tcg-gvec-desc.h" + + +static uint64_t pac_cell_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= extract64(i, 52, 4); + o |= extract64(i, 24, 4) << 4; + o |= extract64(i, 44, 4) << 8; + o |= extract64(i, 0, 4) << 12; + + o |= extract64(i, 28, 4) << 16; + o |= extract64(i, 48, 4) << 20; + o |= extract64(i, 4, 4) << 24; + o |= extract64(i, 40, 4) << 28; + + o |= extract64(i, 32, 4) << 32; + o |= extract64(i, 12, 4) << 36; + o |= extract64(i, 56, 4) << 40; + o |= extract64(i, 20, 4) << 44; + + o |= extract64(i, 8, 4) << 48; + o |= extract64(i, 36, 4) << 52; + o |= extract64(i, 16, 4) << 56; + o |= extract64(i, 60, 4) << 60; + + return o; +} + +static uint64_t pac_cell_inv_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= extract64(i, 12, 4); + o |= extract64(i, 24, 4) << 4; + o |= extract64(i, 48, 4) << 8; + o |= extract64(i, 36, 4) << 12; + + o |= extract64(i, 56, 4) << 16; + o |= extract64(i, 44, 4) << 20; + o |= extract64(i, 4, 4) << 24; + o |= extract64(i, 16, 4) << 28; + + o |= i & MAKE_64BIT_MASK(32, 4); + o |= extract64(i, 52, 4) << 36; + o |= extract64(i, 28, 4) << 40; + o |= extract64(i, 8, 4) << 44; + + o |= extract64(i, 20, 4) << 48; + o |= extract64(i, 0, 4) << 52; + o |= extract64(i, 40, 4) << 56; + o |= i & MAKE_64BIT_MASK(60, 4); + + return o; +} + +static uint64_t pac_sub(uint64_t i) +{ + static const uint8_t sub[16] = { + 0xb, 0x6, 0x8, 0xf, 0xc, 0x0, 0x9, 0xe, + 0x3, 0x7, 0x4, 0x5, 0xd, 0x2, 0x1, 0xa, + }; + uint64_t o = 0; + int b; + + for (b = 0; b < 64; b += 16) { + o |= (uint64_t)sub[(i >> b) & 0xf] << b; + } + return o; +} + +static uint64_t pac_inv_sub(uint64_t i) +{ + static const uint8_t inv_sub[16] = { + 0x5, 0xe, 0xd, 0x8, 0xa, 0xb, 0x1, 0x9, + 0x2, 0x6, 0xf, 0x0, 0x4, 0xc, 0x7, 0x3, + }; + uint64_t o = 0; + int b; + + for (b = 0; b < 64; b += 16) { + o |= (uint64_t)inv_sub[(i >> b) & 0xf] << b; + } + return o; +} + +static int rot_cell(int cell, int n) +{ + /* 4-bit rotate left by n. */ + cell |= cell << 4; + return extract32(cell, 4 - n, 4); +} + +static uint64_t pac_mult(uint64_t i) +{ + uint64_t o = 0; + int b; + + for (b = 0; b < 4 * 4; b += 4) { + int i0, i4, i8, ic, t0, t1, t2, t3; + + i0 = extract64(i, b, 4); + i4 = extract64(i, b + 4 * 4, 4); + i8 = extract64(i, b + 8 * 4, 4); + ic = extract64(i, b + 12 * 4, 4); + + t0 = rot_cell(i8, 1) ^ rot_cell(i4, 2) ^ rot_cell(i0, 1); + t1 = rot_cell(ic, 1) ^ rot_cell(i4, 1) ^ rot_cell(i0, 2); + t2 = rot_cell(ic, 2) ^ rot_cell(i8, 1) ^ rot_cell(i0, 1); + t3 = rot_cell(ic, 1) ^ rot_cell(i8, 2) ^ rot_cell(i4, 1); + + o |= (uint64_t)t3 << b; + o |= (uint64_t)t2 << (b + 4 * 4); + o |= (uint64_t)t1 << (b + 8 * 4); + o |= (uint64_t)t0 << (b + 12 * 4); + } + return o; +} + +static uint64_t tweak_cell_rot(uint64_t cell) +{ + return (cell >> 1) | (((cell ^ (cell >> 1)) & 1) << 3); +} + +static uint64_t tweak_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= extract64(i, 16, 4) << 0; + o |= extract64(i, 20, 4) << 4; + o |= tweak_cell_rot(extract64(i, 24, 4)) << 8; + o |= extract64(i, 28, 4) << 12; + + o |= tweak_cell_rot(extract64(i, 44, 4)) << 16; + o |= extract64(i, 8, 4) << 20; + o |= extract64(i, 12, 4) << 24; + o |= tweak_cell_rot(extract64(i, 32, 4)) << 28; + + o |= extract64(i, 48, 4) << 32; + o |= extract64(i, 52, 4) << 36; + o |= extract64(i, 56, 4) << 40; + o |= tweak_cell_rot(extract64(i, 60, 4)) << 44; + + o |= tweak_cell_rot(extract64(i, 0, 4)) << 48; + o |= extract64(i, 4, 4) << 52; + o |= tweak_cell_rot(extract64(i, 40, 4)) << 56; + o |= tweak_cell_rot(extract64(i, 36, 4)) << 60; + + return o; +} + +static uint64_t tweak_cell_inv_rot(uint64_t cell) +{ + return ((cell << 1) & 0xf) | ((cell & 1) ^ (cell >> 3)); +} + +static uint64_t tweak_inv_shuffle(uint64_t i) +{ + uint64_t o = 0; + + o |= tweak_cell_inv_rot(extract64(i, 48, 4)); + o |= extract64(i, 52, 4) << 4; + o |= extract64(i, 20, 4) << 8; + o |= extract64(i, 24, 4) << 12; + + o |= extract64(i, 0, 4) << 16; + o |= extract64(i, 4, 4) << 20; + o |= tweak_cell_inv_rot(extract64(i, 8, 4)) << 24; + o |= extract64(i, 12, 4) << 28; + + o |= tweak_cell_inv_rot(extract64(i, 28, 4)) << 32; + o |= tweak_cell_inv_rot(extract64(i, 60, 4)) << 36; + o |= tweak_cell_inv_rot(extract64(i, 56, 4)) << 40; + o |= tweak_cell_inv_rot(extract64(i, 16, 4)) << 44; + + o |= extract64(i, 32, 4) << 48; + o |= extract64(i, 36, 4) << 52; + o |= extract64(i, 40, 4) << 56; + o |= tweak_cell_inv_rot(extract64(i, 44, 4)) << 60; + + return o; +} + +static uint64_t pauth_computepac(uint64_t data, uint64_t modifier, + ARMPACKey key) +{ + static const uint64_t RC[5] = { + 0x0000000000000000ull, + 0x13198A2E03707344ull, + 0xA4093822299F31D0ull, + 0x082EFA98EC4E6C89ull, + 0x452821E638D01377ull, + }; + const uint64_t alpha = 0xC0AC29B7C97C50DDull; + /* + * Note that in the ARM pseudocode, key0 contains bits <127:64> + * and key1 contains bits <63:0> of the 128-bit key. + */ + uint64_t key0 = key.hi, key1 = key.lo; + uint64_t workingval, runningmod, roundkey, modk0; + int i; + + modk0 = (key0 << 63) | ((key0 >> 1) ^ (key0 >> 63)); + runningmod = modifier; + workingval = data ^ key0; + + for (i = 0; i <= 4; ++i) { + roundkey = key1 ^ runningmod; + workingval ^= roundkey; + workingval ^= RC[i]; + if (i > 0) { + workingval = pac_cell_shuffle(workingval); + workingval = pac_mult(workingval); + } + workingval = pac_sub(workingval); + runningmod = tweak_shuffle(runningmod); + } + roundkey = modk0 ^ runningmod; + workingval ^= roundkey; + workingval = pac_cell_shuffle(workingval); + workingval = pac_mult(workingval); + workingval = pac_sub(workingval); + workingval = pac_cell_shuffle(workingval); + workingval = pac_mult(workingval); + workingval ^= key1; + workingval = pac_cell_inv_shuffle(workingval); + workingval = pac_inv_sub(workingval); + workingval = pac_mult(workingval); + workingval = pac_cell_inv_shuffle(workingval); + workingval ^= key0; + workingval ^= runningmod; + for (i = 0; i <= 4; ++i) { + workingval = pac_inv_sub(workingval); + if (i < 4) { + workingval = pac_mult(workingval); + workingval = pac_cell_inv_shuffle(workingval); + } + runningmod = tweak_inv_shuffle(runningmod); + roundkey = key1 ^ runningmod; + workingval ^= RC[4 - i]; + workingval ^= roundkey; + workingval ^= alpha; + } + workingval ^= modk0; + + return workingval; +} + +static uint64_t pauth_addpac(CPUARMState *env, uint64_t ptr, uint64_t modifier, + ARMPACKey *key, bool data) +{ + ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); + ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); + uint64_t pac, ext_ptr, ext, test; + int bot_bit, top_bit; + + /* If tagged pointers are in use, use ptr<55>, otherwise ptr<63>. */ + if (param.tbi) { + ext = sextract64(ptr, 55, 1); + } else { + ext = sextract64(ptr, 63, 1); + } + + /* Build a pointer with known good extension bits. */ + top_bit = 64 - 8 * param.tbi; + bot_bit = 64 - param.tsz; + ext_ptr = deposit64(ptr, bot_bit, top_bit - bot_bit, ext); + + pac = pauth_computepac(ext_ptr, modifier, *key); + + /* + * Check if the ptr has good extension bits and corrupt the + * pointer authentication code if not. + */ + test = sextract64(ptr, bot_bit, top_bit - bot_bit); + if (test != 0 && test != -1) { + pac ^= MAKE_64BIT_MASK(top_bit - 1, 1); + } + + /* + * Preserve the determination between upper and lower at bit 55, + * and insert pointer authentication code. + */ + if (param.tbi) { + ptr &= ~MAKE_64BIT_MASK(bot_bit, 55 - bot_bit + 1); + pac &= MAKE_64BIT_MASK(bot_bit, 54 - bot_bit + 1); + } else { + ptr &= MAKE_64BIT_MASK(0, bot_bit); + pac &= ~(MAKE_64BIT_MASK(55, 1) | MAKE_64BIT_MASK(0, bot_bit)); + } + ext &= MAKE_64BIT_MASK(55, 1); + return pac | ext | ptr; +} + +static uint64_t pauth_original_ptr(uint64_t ptr, ARMVAParameters param) +{ + uint64_t extfield = -param.select; + int bot_pac_bit = 64 - param.tsz; + int top_pac_bit = 64 - 8 * param.tbi; + + return deposit64(ptr, bot_pac_bit, top_pac_bit - bot_pac_bit, extfield); +} + +static uint64_t pauth_auth(CPUARMState *env, uint64_t ptr, uint64_t modifier, + ARMPACKey *key, bool data, int keynumber) +{ + ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); + ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); + int bot_bit, top_bit; + uint64_t pac, orig_ptr, test; + + orig_ptr = pauth_original_ptr(ptr, param); + pac = pauth_computepac(orig_ptr, modifier, *key); + bot_bit = 64 - param.tsz; + top_bit = 64 - 8 * param.tbi; + + test = (pac ^ ptr) & ~MAKE_64BIT_MASK(55, 1); + if (unlikely(extract64(test, bot_bit, top_bit - bot_bit))) { + int error_code = (keynumber << 1) | (keynumber ^ 1); + if (param.tbi) { + return deposit64(ptr, 53, 2, error_code); + } else { + return deposit64(ptr, 61, 2, error_code); + } + } + return orig_ptr; +} + +static uint64_t pauth_strip(CPUARMState *env, uint64_t ptr, bool data) +{ + ARMMMUIdx mmu_idx = arm_stage1_mmu_idx(env); + ARMVAParameters param = aa64_va_parameters(env, ptr, mmu_idx, data); + + return pauth_original_ptr(ptr, param); +} + +static void QEMU_NORETURN pauth_trap(CPUARMState *env, int target_el, + uintptr_t ra) +{ + raise_exception_ra(env, EXCP_UDEF, syn_pactrap(), target_el, ra); +} + +static void pauth_check_trap(CPUARMState *env, int el, uintptr_t ra) +{ + if (el < 2 && arm_feature(env, ARM_FEATURE_EL2)) { + uint64_t hcr = arm_hcr_el2_eff(env); + bool trap = !(hcr & HCR_API); + /* FIXME: ARMv8.1-VHE: trap only applies to EL1&0 regime. */ + /* FIXME: ARMv8.3-NV: HCR_NV trap takes precedence for ERETA[AB]. */ + if (trap) { + pauth_trap(env, 2, ra); + } + } + if (el < 3 && arm_feature(env, ARM_FEATURE_EL3)) { + if (!(env->cp15.scr_el3 & SCR_API)) { + pauth_trap(env, 3, ra); + } + } +} + +static bool pauth_key_enabled(CPUARMState *env, int el, uint32_t bit) +{ + uint32_t sctlr; + if (el == 0) { + /* FIXME: ARMv8.1-VHE S2 translation regime. */ + sctlr = env->cp15.sctlr_el[1]; + } else { + sctlr = env->cp15.sctlr_el[el]; + } + return (sctlr & bit) != 0; +} + +uint64_t HELPER(pacia)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnIA)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->apia_key, false); +} + +uint64_t HELPER(pacib)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnIB)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->apib_key, false); +} + +uint64_t HELPER(pacda)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnDA)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->apda_key, true); +} + +uint64_t HELPER(pacdb)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnDB)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_addpac(env, x, y, &env->apdb_key, true); +} + +uint64_t HELPER(pacga)(CPUARMState *env, uint64_t x, uint64_t y) +{ + uint64_t pac; + + pauth_check_trap(env, arm_current_el(env), GETPC()); + pac = pauth_computepac(x, y, env->apga_key); + + return pac & 0xffffffff00000000ull; +} + +uint64_t HELPER(autia)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnIA)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_auth(env, x, y, &env->apia_key, false, 0); +} + +uint64_t HELPER(autib)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnIB)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_auth(env, x, y, &env->apib_key, false, 1); +} + +uint64_t HELPER(autda)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnDA)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_auth(env, x, y, &env->apda_key, true, 0); +} + +uint64_t HELPER(autdb)(CPUARMState *env, uint64_t x, uint64_t y) +{ + int el = arm_current_el(env); + if (!pauth_key_enabled(env, el, SCTLR_EnDB)) { + return x; + } + pauth_check_trap(env, el, GETPC()); + return pauth_auth(env, x, y, &env->apdb_key, true, 1); +} + +uint64_t HELPER(xpaci)(CPUARMState *env, uint64_t a) +{ + return pauth_strip(env, a, false); +} + +uint64_t HELPER(xpacd)(CPUARMState *env, uint64_t a) +{ + return pauth_strip(env, a, true); +} diff --git a/target/arm/sve.decode b/target/arm/sve.decode index e10b689454..4f580a25e7 100644 --- a/target/arm/sve.decode +++ b/target/arm/sve.decode @@ -99,6 +99,7 @@ # Two operand with governing predicate, flags setting @pd_pg_pn_s ........ . s:1 ...... .. pg:4 . rn:4 . rd:4 &rpr_s +@pd_pg_pn_s0 ........ . . ...... .. pg:4 . rn:4 . rd:4 &rpr_s s=0 # Three operand with unused vector element size @rd_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 &rrr_esz esz=0 @@ -667,8 +668,8 @@ BRKPB 00100101 0. 00 .... 11 .... 0 .... 1 .... @pd_pg_pn_pm_s # SVE partition break condition BRKA_z 00100101 0. 01000001 .... 0 .... 0 .... @pd_pg_pn_s BRKB_z 00100101 1. 01000001 .... 0 .... 0 .... @pd_pg_pn_s -BRKA_m 00100101 0. 01000001 .... 0 .... 1 .... @pd_pg_pn_s -BRKB_m 00100101 1. 01000001 .... 0 .... 1 .... @pd_pg_pn_s +BRKA_m 00100101 00 01000001 .... 0 .... 1 .... @pd_pg_pn_s0 +BRKB_m 00100101 10 01000001 .... 0 .... 1 .... @pd_pg_pn_s0 # SVE propagate break to next partition BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s diff --git a/target/arm/sve_helper.c b/target/arm/sve_helper.c index 54795c9194..bc847250dd 100644 --- a/target/arm/sve_helper.c +++ b/target/arm/sve_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "internals.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" @@ -1042,7 +1043,7 @@ void HELPER(sve_movz_d)(void *vd, void *vn, void *vg, uint32_t desc) uint64_t *d = vd, *n = vn; uint8_t *pg = vg; for (i = 0; i < opr_sz; i += 1) { - d[i] = n[1] & -(uint64_t)(pg[H1(i)] & 1); + d[i] = n[i] & -(uint64_t)(pg[H1(i)] & 1); } } @@ -1688,6 +1689,47 @@ static void swap_memmove(void *vd, void *vs, size_t n) } } +/* Similarly for memset of 0. */ +static void swap_memzero(void *vd, size_t n) +{ + uintptr_t d = (uintptr_t)vd; + uintptr_t o = (d | n) & 7; + size_t i; + + /* Usually, the first bit of a predicate is set, so N is 0. */ + if (likely(n == 0)) { + return; + } + +#ifndef HOST_WORDS_BIGENDIAN + o = 0; +#endif + switch (o) { + case 0: + memset(vd, 0, n); + break; + + case 4: + for (i = 0; i < n; i += 4) { + *(uint32_t *)H1_4(d + i) = 0; + } + break; + + case 2: + case 6: + for (i = 0; i < n; i += 2) { + *(uint16_t *)H1_2(d + i) = 0; + } + break; + + default: + for (i = 0; i < n; i++) { + *(uint8_t *)H1(d + i) = 0; + } + break; + } +} + void HELPER(sve_ext)(void *vd, void *vn, void *vm, uint32_t desc) { intptr_t opr_sz = simd_oprsz(desc); @@ -2436,13 +2478,13 @@ uint32_t HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ #define DO_CMP_PPZW_S(NAME, TYPE, TYPEW, OP) \ DO_CMP_PPZW(NAME, TYPE, TYPEW, OP, H1_4, 0x1111111111111111ull) -DO_CMP_PPZW_B(sve_cmpeq_ppzw_b, uint8_t, uint64_t, ==) -DO_CMP_PPZW_H(sve_cmpeq_ppzw_h, uint16_t, uint64_t, ==) -DO_CMP_PPZW_S(sve_cmpeq_ppzw_s, uint32_t, uint64_t, ==) +DO_CMP_PPZW_B(sve_cmpeq_ppzw_b, int8_t, uint64_t, ==) +DO_CMP_PPZW_H(sve_cmpeq_ppzw_h, int16_t, uint64_t, ==) +DO_CMP_PPZW_S(sve_cmpeq_ppzw_s, int32_t, uint64_t, ==) -DO_CMP_PPZW_B(sve_cmpne_ppzw_b, uint8_t, uint64_t, !=) -DO_CMP_PPZW_H(sve_cmpne_ppzw_h, uint16_t, uint64_t, !=) -DO_CMP_PPZW_S(sve_cmpne_ppzw_s, uint32_t, uint64_t, !=) +DO_CMP_PPZW_B(sve_cmpne_ppzw_b, int8_t, uint64_t, !=) +DO_CMP_PPZW_H(sve_cmpne_ppzw_h, int16_t, uint64_t, !=) +DO_CMP_PPZW_S(sve_cmpne_ppzw_s, int32_t, uint64_t, !=) DO_CMP_PPZW_B(sve_cmpgt_ppzw_b, int8_t, int64_t, >) DO_CMP_PPZW_H(sve_cmpgt_ppzw_h, int16_t, int64_t, >) @@ -2846,11 +2888,6 @@ uint32_t HELPER(sve_while)(void *vd, uint32_t count, uint32_t pred_desc) return flags; } - /* Scale from predicate element count to bits. */ - count <<= esz; - /* Bound to the bits in the predicate. */ - count = MIN(count, oprsz * 8); - /* Set all of the requested bits. */ for (i = 0; i < count / 64; ++i) { d->p[i] = esz_mask; @@ -3363,7 +3400,7 @@ static void do_fmla_zpzzz_h(CPUARMState *env, void *vg, uint32_t desc, e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; e2 = *(uint16_t *)(vm + H1_2(i)); e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; - r = float16_muladd(e1, e2, e3, 0, &env->vfp.fp_status); + r = float16_muladd(e1, e2, e3, 0, &env->vfp.fp_status_f16); *(uint16_t *)(vd + H1_2(i)) = r; } } while (i & 63); @@ -3932,162 +3969,472 @@ void HELPER(sve_fcmla_zpzzz_d)(CPUARMState *env, void *vg, uint32_t desc) /* * Load contiguous data, protected by a governing predicate. */ -#define DO_LD1(NAME, FN, TYPEE, TYPEM, H) \ -static void do_##NAME(CPUARMState *env, void *vd, void *vg, \ - target_ulong addr, intptr_t oprsz, \ - uintptr_t ra) \ -{ \ - intptr_t i = 0; \ - do { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m = 0; \ - if (pg & 1) { \ - m = FN(env, addr, ra); \ - } \ - *(TYPEE *)(vd + H(i)) = m; \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += sizeof(TYPEM); \ - } while (i & 15); \ - } while (i < oprsz); \ -} \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - do_##NAME(env, &env->vfp.zregs[simd_data(desc)], vg, \ - addr, simd_oprsz(desc), GETPC()); \ + +/* + * Load elements into @vd, controlled by @vg, from @host + @mem_ofs. + * Memory is valid through @host + @mem_max. The register element + * indicies are inferred from @mem_ofs, as modified by the types for + * which the helper is built. Return the @mem_ofs of the first element + * not loaded (which is @mem_max if they are all loaded). + * + * For softmmu, we have fully validated the guest page. For user-only, + * we cannot fully validate without taking the mmap lock, but since we + * know the access is within one host page, if any access is valid they + * all must be valid. However, when @vg is all false, it may be that + * no access is valid. + */ +typedef intptr_t sve_ld1_host_fn(void *vd, void *vg, void *host, + intptr_t mem_ofs, intptr_t mem_max); + +/* + * Load one element into @vd + @reg_off from (@env, @vaddr, @ra). + * The controlling predicate is known to be true. + */ +typedef void sve_ld1_tlb_fn(CPUARMState *env, void *vd, intptr_t reg_off, + target_ulong vaddr, TCGMemOpIdx oi, uintptr_t ra); +typedef sve_ld1_tlb_fn sve_st1_tlb_fn; + +/* + * Generate the above primitives. + */ + +#define DO_LD_HOST(NAME, H, TYPEE, TYPEM, HOST) \ +static intptr_t sve_##NAME##_host(void *vd, void *vg, void *host, \ + intptr_t mem_off, const intptr_t mem_max) \ +{ \ + intptr_t reg_off = mem_off * (sizeof(TYPEE) / sizeof(TYPEM)); \ + uint64_t *pg = vg; \ + while (mem_off + sizeof(TYPEM) <= mem_max) { \ + TYPEM val = 0; \ + if (likely((pg[reg_off >> 6] >> (reg_off & 63)) & 1)) { \ + val = HOST(host + mem_off); \ + } \ + *(TYPEE *)(vd + H(reg_off)) = val; \ + mem_off += sizeof(TYPEM), reg_off += sizeof(TYPEE); \ + } \ + return mem_off; \ } -#define DO_LD2(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *d1 = &env->vfp.zregs[rd]; \ - void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m1 = 0, m2 = 0; \ - if (pg & 1) { \ - m1 = FN(env, addr, ra); \ - m2 = FN(env, addr + sizeof(TYPEM), ra); \ - } \ - *(TYPEE *)(d1 + H(i)) = m1; \ - *(TYPEE *)(d2 + H(i)) = m2; \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += 2 * sizeof(TYPEM); \ - } while (i & 15); \ - } \ +#ifdef CONFIG_SOFTMMU +#define DO_LD_TLB(NAME, H, TYPEE, TYPEM, HOST, MOEND, TLB) \ +static void sve_##NAME##_tlb(CPUARMState *env, void *vd, intptr_t reg_off, \ + target_ulong addr, TCGMemOpIdx oi, uintptr_t ra) \ +{ \ + TYPEM val = TLB(env, addr, oi, ra); \ + *(TYPEE *)(vd + H(reg_off)) = val; \ +} +#else +#define DO_LD_TLB(NAME, H, TYPEE, TYPEM, HOST, MOEND, TLB) \ +static void sve_##NAME##_tlb(CPUARMState *env, void *vd, intptr_t reg_off, \ + target_ulong addr, TCGMemOpIdx oi, uintptr_t ra) \ +{ \ + TYPEM val = HOST(g2h(addr)); \ + *(TYPEE *)(vd + H(reg_off)) = val; \ +} +#endif + +#define DO_LD_PRIM_1(NAME, H, TE, TM) \ + DO_LD_HOST(NAME, H, TE, TM, ldub_p) \ + DO_LD_TLB(NAME, H, TE, TM, ldub_p, 0, helper_ret_ldub_mmu) + +DO_LD_PRIM_1(ld1bb, H1, uint8_t, uint8_t) +DO_LD_PRIM_1(ld1bhu, H1_2, uint16_t, uint8_t) +DO_LD_PRIM_1(ld1bhs, H1_2, uint16_t, int8_t) +DO_LD_PRIM_1(ld1bsu, H1_4, uint32_t, uint8_t) +DO_LD_PRIM_1(ld1bss, H1_4, uint32_t, int8_t) +DO_LD_PRIM_1(ld1bdu, , uint64_t, uint8_t) +DO_LD_PRIM_1(ld1bds, , uint64_t, int8_t) + +#define DO_LD_PRIM_2(NAME, end, MOEND, H, TE, TM, PH, PT) \ + DO_LD_HOST(NAME##_##end, H, TE, TM, PH##_##end##_p) \ + DO_LD_TLB(NAME##_##end, H, TE, TM, PH##_##end##_p, \ + MOEND, helper_##end##_##PT##_mmu) + +DO_LD_PRIM_2(ld1hh, le, MO_LE, H1_2, uint16_t, uint16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hsu, le, MO_LE, H1_4, uint32_t, uint16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hss, le, MO_LE, H1_4, uint32_t, int16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hdu, le, MO_LE, , uint64_t, uint16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hds, le, MO_LE, , uint64_t, int16_t, lduw, lduw) + +DO_LD_PRIM_2(ld1ss, le, MO_LE, H1_4, uint32_t, uint32_t, ldl, ldul) +DO_LD_PRIM_2(ld1sdu, le, MO_LE, , uint64_t, uint32_t, ldl, ldul) +DO_LD_PRIM_2(ld1sds, le, MO_LE, , uint64_t, int32_t, ldl, ldul) + +DO_LD_PRIM_2(ld1dd, le, MO_LE, , uint64_t, uint64_t, ldq, ldq) + +DO_LD_PRIM_2(ld1hh, be, MO_BE, H1_2, uint16_t, uint16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hsu, be, MO_BE, H1_4, uint32_t, uint16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hss, be, MO_BE, H1_4, uint32_t, int16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hdu, be, MO_BE, , uint64_t, uint16_t, lduw, lduw) +DO_LD_PRIM_2(ld1hds, be, MO_BE, , uint64_t, int16_t, lduw, lduw) + +DO_LD_PRIM_2(ld1ss, be, MO_BE, H1_4, uint32_t, uint32_t, ldl, ldul) +DO_LD_PRIM_2(ld1sdu, be, MO_BE, , uint64_t, uint32_t, ldl, ldul) +DO_LD_PRIM_2(ld1sds, be, MO_BE, , uint64_t, int32_t, ldl, ldul) + +DO_LD_PRIM_2(ld1dd, be, MO_BE, , uint64_t, uint64_t, ldq, ldq) + +#undef DO_LD_TLB +#undef DO_LD_HOST +#undef DO_LD_PRIM_1 +#undef DO_LD_PRIM_2 + +/* + * Skip through a sequence of inactive elements in the guarding predicate @vg, + * beginning at @reg_off bounded by @reg_max. Return the offset of the active + * element >= @reg_off, or @reg_max if there were no active elements at all. + */ +static intptr_t find_next_active(uint64_t *vg, intptr_t reg_off, + intptr_t reg_max, int esz) +{ + uint64_t pg_mask = pred_esz_masks[esz]; + uint64_t pg = (vg[reg_off >> 6] & pg_mask) >> (reg_off & 63); + + /* In normal usage, the first element is active. */ + if (likely(pg & 1)) { + return reg_off; + } + + if (pg == 0) { + reg_off &= -64; + do { + reg_off += 64; + if (unlikely(reg_off >= reg_max)) { + /* The entire predicate was false. */ + return reg_max; + } + pg = vg[reg_off >> 6] & pg_mask; + } while (pg == 0); + } + reg_off += ctz64(pg); + + /* We should never see an out of range predicate bit set. */ + tcg_debug_assert(reg_off < reg_max); + return reg_off; } -#define DO_LD3(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *d1 = &env->vfp.zregs[rd]; \ - void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ - void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m1 = 0, m2 = 0, m3 = 0; \ - if (pg & 1) { \ - m1 = FN(env, addr, ra); \ - m2 = FN(env, addr + sizeof(TYPEM), ra); \ - m3 = FN(env, addr + 2 * sizeof(TYPEM), ra); \ - } \ - *(TYPEE *)(d1 + H(i)) = m1; \ - *(TYPEE *)(d2 + H(i)) = m2; \ - *(TYPEE *)(d3 + H(i)) = m3; \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += 3 * sizeof(TYPEM); \ - } while (i & 15); \ - } \ +/* + * Return the maximum offset <= @mem_max which is still within the page + * referenced by @base + @mem_off. + */ +static intptr_t max_for_page(target_ulong base, intptr_t mem_off, + intptr_t mem_max) +{ + target_ulong addr = base + mem_off; + intptr_t split = -(intptr_t)(addr | TARGET_PAGE_MASK); + return MIN(split, mem_max - mem_off) + mem_off; } -#define DO_LD4(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *d1 = &env->vfp.zregs[rd]; \ - void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ - void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ - void *d4 = &env->vfp.zregs[(rd + 3) & 31]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m1 = 0, m2 = 0, m3 = 0, m4 = 0; \ - if (pg & 1) { \ - m1 = FN(env, addr, ra); \ - m2 = FN(env, addr + sizeof(TYPEM), ra); \ - m3 = FN(env, addr + 2 * sizeof(TYPEM), ra); \ - m4 = FN(env, addr + 3 * sizeof(TYPEM), ra); \ - } \ - *(TYPEE *)(d1 + H(i)) = m1; \ - *(TYPEE *)(d2 + H(i)) = m2; \ - *(TYPEE *)(d3 + H(i)) = m3; \ - *(TYPEE *)(d4 + H(i)) = m4; \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += 4 * sizeof(TYPEM); \ - } while (i & 15); \ - } \ +static inline void set_helper_retaddr(uintptr_t ra) +{ +#ifdef CONFIG_USER_ONLY + helper_retaddr = ra; +#endif } -DO_LD1(sve_ld1bhu_r, cpu_ldub_data_ra, uint16_t, uint8_t, H1_2) -DO_LD1(sve_ld1bhs_r, cpu_ldsb_data_ra, uint16_t, int8_t, H1_2) -DO_LD1(sve_ld1bsu_r, cpu_ldub_data_ra, uint32_t, uint8_t, H1_4) -DO_LD1(sve_ld1bss_r, cpu_ldsb_data_ra, uint32_t, int8_t, H1_4) -DO_LD1(sve_ld1bdu_r, cpu_ldub_data_ra, uint64_t, uint8_t, ) -DO_LD1(sve_ld1bds_r, cpu_ldsb_data_ra, uint64_t, int8_t, ) +/* + * The result of tlb_vaddr_to_host for user-only is just g2h(x), + * which is always non-null. Elide the useless test. + */ +static inline bool test_host_page(void *host) +{ +#ifdef CONFIG_USER_ONLY + return true; +#else + return likely(host != NULL); +#endif +} -DO_LD1(sve_ld1hsu_r, cpu_lduw_data_ra, uint32_t, uint16_t, H1_4) -DO_LD1(sve_ld1hss_r, cpu_ldsw_data_ra, uint32_t, int8_t, H1_4) -DO_LD1(sve_ld1hdu_r, cpu_lduw_data_ra, uint64_t, uint16_t, ) -DO_LD1(sve_ld1hds_r, cpu_ldsw_data_ra, uint64_t, int16_t, ) +/* + * Common helper for all contiguous one-register predicated loads. + */ +static void sve_ld1_r(CPUARMState *env, void *vg, const target_ulong addr, + uint32_t desc, const uintptr_t retaddr, + const int esz, const int msz, + sve_ld1_host_fn *host_fn, + sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int mmu_idx = get_mmuidx(oi); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + void *vd = &env->vfp.zregs[rd]; + const int diffsz = esz - msz; + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t mem_max = reg_max >> diffsz; + ARMVectorReg scratch; + void *host; + intptr_t split, reg_off, mem_off; -DO_LD1(sve_ld1sdu_r, cpu_ldl_data_ra, uint64_t, uint32_t, ) -DO_LD1(sve_ld1sds_r, cpu_ldl_data_ra, uint64_t, int32_t, ) + /* Find the first active element. */ + reg_off = find_next_active(vg, 0, reg_max, esz); + if (unlikely(reg_off == reg_max)) { + /* The entire predicate was false; no load occurs. */ + memset(vd, 0, reg_max); + return; + } + mem_off = reg_off >> diffsz; + set_helper_retaddr(retaddr); -DO_LD1(sve_ld1bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) -DO_LD2(sve_ld2bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) -DO_LD3(sve_ld3bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) -DO_LD4(sve_ld4bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) + /* + * If the (remaining) load is entirely within a single page, then: + * For softmmu, and the tlb hits, then no faults will occur; + * For user-only, either the first load will fault or none will. + * We can thus perform the load directly to the destination and + * Vd will be unmodified on any exception path. + */ + split = max_for_page(addr, mem_off, mem_max); + if (likely(split == mem_max)) { + host = tlb_vaddr_to_host(env, addr + mem_off, MMU_DATA_LOAD, mmu_idx); + if (test_host_page(host)) { + mem_off = host_fn(vd, vg, host - mem_off, mem_off, mem_max); + tcg_debug_assert(mem_off == mem_max); + set_helper_retaddr(0); + /* After having taken any fault, zero leading inactive elements. */ + swap_memzero(vd, reg_off); + return; + } + } -DO_LD1(sve_ld1hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) -DO_LD2(sve_ld2hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) -DO_LD3(sve_ld3hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) -DO_LD4(sve_ld4hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) + /* + * Perform the predicated read into a temporary, thus ensuring + * if the load of the last element faults, Vd is not modified. + */ +#ifdef CONFIG_USER_ONLY + swap_memzero(&scratch, reg_off); + host_fn(&scratch, vg, g2h(addr), mem_off, mem_max); +#else + memset(&scratch, 0, reg_max); + goto start; + while (1) { + reg_off = find_next_active(vg, reg_off, reg_max, esz); + if (reg_off >= reg_max) { + break; + } + mem_off = reg_off >> diffsz; + split = max_for_page(addr, mem_off, mem_max); -DO_LD1(sve_ld1ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) -DO_LD2(sve_ld2ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) -DO_LD3(sve_ld3ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) -DO_LD4(sve_ld4ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) + start: + if (split - mem_off >= (1 << msz)) { + /* At least one whole element on this page. */ + host = tlb_vaddr_to_host(env, addr + mem_off, + MMU_DATA_LOAD, mmu_idx); + if (host) { + mem_off = host_fn(&scratch, vg, host - mem_off, + mem_off, split); + reg_off = mem_off << diffsz; + continue; + } + } -DO_LD1(sve_ld1dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) -DO_LD2(sve_ld2dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) -DO_LD3(sve_ld3dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) -DO_LD4(sve_ld4dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) + /* + * Perform one normal read. This may fault, longjmping out to the + * main loop in order to raise an exception. It may succeed, and + * as a side-effect load the TLB entry for the next round. Finally, + * in the extremely unlikely case we're performing this operation + * on I/O memory, it may succeed but not bring in the TLB entry. + * But even then we have still made forward progress. + */ + tlb_fn(env, &scratch, reg_off, addr + mem_off, oi, retaddr); + reg_off += 1 << esz; + } +#endif -#undef DO_LD1 -#undef DO_LD2 -#undef DO_LD3 -#undef DO_LD4 + set_helper_retaddr(0); + memcpy(vd, &scratch, reg_max); +} + +#define DO_LD1_1(NAME, ESZ) \ +void HELPER(sve_##NAME##_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ld1_r(env, vg, addr, desc, GETPC(), ESZ, 0, \ + sve_##NAME##_host, sve_##NAME##_tlb); \ +} + +#define DO_LD1_2(NAME, ESZ, MSZ) \ +void HELPER(sve_##NAME##_le_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ld1_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, \ + sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ +} \ +void HELPER(sve_##NAME##_be_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ld1_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, \ + sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ +} + +DO_LD1_1(ld1bb, 0) +DO_LD1_1(ld1bhu, 1) +DO_LD1_1(ld1bhs, 1) +DO_LD1_1(ld1bsu, 2) +DO_LD1_1(ld1bss, 2) +DO_LD1_1(ld1bdu, 3) +DO_LD1_1(ld1bds, 3) + +DO_LD1_2(ld1hh, 1, 1) +DO_LD1_2(ld1hsu, 2, 1) +DO_LD1_2(ld1hss, 2, 1) +DO_LD1_2(ld1hdu, 3, 1) +DO_LD1_2(ld1hds, 3, 1) + +DO_LD1_2(ld1ss, 2, 2) +DO_LD1_2(ld1sdu, 3, 2) +DO_LD1_2(ld1sds, 3, 2) + +DO_LD1_2(ld1dd, 3, 3) + +#undef DO_LD1_1 +#undef DO_LD1_2 + +/* + * Common helpers for all contiguous 2,3,4-register predicated loads. + */ +static void sve_ld2_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, int size, uintptr_t ra, + sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + ARMVectorReg scratch[2] = { }; + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, &scratch[0], i, addr, oi, ra); + tlb_fn(env, &scratch[1], i, addr + size, oi, ra); + } + i += size, pg >>= size; + addr += 2 * size; + } while (i & 15); + } + set_helper_retaddr(0); + + /* Wait until all exceptions have been raised to write back. */ + memcpy(&env->vfp.zregs[rd], &scratch[0], oprsz); + memcpy(&env->vfp.zregs[(rd + 1) & 31], &scratch[1], oprsz); +} + +static void sve_ld3_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, int size, uintptr_t ra, + sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + ARMVectorReg scratch[3] = { }; + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, &scratch[0], i, addr, oi, ra); + tlb_fn(env, &scratch[1], i, addr + size, oi, ra); + tlb_fn(env, &scratch[2], i, addr + 2 * size, oi, ra); + } + i += size, pg >>= size; + addr += 3 * size; + } while (i & 15); + } + set_helper_retaddr(0); + + /* Wait until all exceptions have been raised to write back. */ + memcpy(&env->vfp.zregs[rd], &scratch[0], oprsz); + memcpy(&env->vfp.zregs[(rd + 1) & 31], &scratch[1], oprsz); + memcpy(&env->vfp.zregs[(rd + 2) & 31], &scratch[2], oprsz); +} + +static void sve_ld4_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, int size, uintptr_t ra, + sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + ARMVectorReg scratch[4] = { }; + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, &scratch[0], i, addr, oi, ra); + tlb_fn(env, &scratch[1], i, addr + size, oi, ra); + tlb_fn(env, &scratch[2], i, addr + 2 * size, oi, ra); + tlb_fn(env, &scratch[3], i, addr + 3 * size, oi, ra); + } + i += size, pg >>= size; + addr += 4 * size; + } while (i & 15); + } + set_helper_retaddr(0); + + /* Wait until all exceptions have been raised to write back. */ + memcpy(&env->vfp.zregs[rd], &scratch[0], oprsz); + memcpy(&env->vfp.zregs[(rd + 1) & 31], &scratch[1], oprsz); + memcpy(&env->vfp.zregs[(rd + 2) & 31], &scratch[2], oprsz); + memcpy(&env->vfp.zregs[(rd + 3) & 31], &scratch[3], oprsz); +} + +#define DO_LDN_1(N) \ +void QEMU_FLATTEN HELPER(sve_ld##N##bb_r) \ + (CPUARMState *env, void *vg, target_ulong addr, uint32_t desc) \ +{ \ + sve_ld##N##_r(env, vg, addr, desc, 1, GETPC(), sve_ld1bb_tlb); \ +} + +#define DO_LDN_2(N, SUFF, SIZE) \ +void QEMU_FLATTEN HELPER(sve_ld##N##SUFF##_le_r) \ + (CPUARMState *env, void *vg, target_ulong addr, uint32_t desc) \ +{ \ + sve_ld##N##_r(env, vg, addr, desc, SIZE, GETPC(), \ + sve_ld1##SUFF##_le_tlb); \ +} \ +void QEMU_FLATTEN HELPER(sve_ld##N##SUFF##_be_r) \ + (CPUARMState *env, void *vg, target_ulong addr, uint32_t desc) \ +{ \ + sve_ld##N##_r(env, vg, addr, desc, SIZE, GETPC(), \ + sve_ld1##SUFF##_be_tlb); \ +} + +DO_LDN_1(2) +DO_LDN_1(3) +DO_LDN_1(4) + +DO_LDN_2(2, hh, 2) +DO_LDN_2(3, hh, 2) +DO_LDN_2(4, hh, 2) + +DO_LDN_2(2, ss, 4) +DO_LDN_2(3, ss, 4) +DO_LDN_2(4, ss, 4) + +DO_LDN_2(2, dd, 8) +DO_LDN_2(3, dd, 8) +DO_LDN_2(4, dd, 8) + +#undef DO_LDN_1 +#undef DO_LDN_2 /* * Load contiguous data, first-fault and no-fault. + * + * For user-only, one could argue that we should hold the mmap_lock during + * the operation so that there is no race between page_check_range and the + * load operation. However, unmapping pages out from under a running thread + * is extraordinarily unlikely. This theoretical race condition also affects + * linux-user/ in its get_user/put_user macros. + * + * TODO: Construct some helpers, written in assembly, that interact with + * handle_cpu_signal to produce memory ops which can properly report errors + * without racing. */ -#ifdef CONFIG_USER_ONLY - /* Fault on byte I. All bits in FFR from I are cleared. The vector * result from I is CONSTRAINED UNPREDICTABLE; we choose the MERGE * option, which leaves subsequent data unchanged. @@ -4105,573 +4452,932 @@ static void record_fault(CPUARMState *env, uintptr_t i, uintptr_t oprsz) } } -/* Hold the mmap lock during the operation so that there is no race - * between page_check_range and the load operation. We expect the - * usual case to have no faults at all, so we check the whole range - * first and if successful defer to the normal load operation. - * - * TODO: Change mmap_lock to a rwlock so that multiple readers - * can run simultaneously. This will probably help other uses - * within QEMU as well. +/* + * Common helper for all contiguous first-fault loads. */ -#define DO_LDFF1(PART, FN, TYPEE, TYPEM, H) \ -static void do_sve_ldff1##PART(CPUARMState *env, void *vd, void *vg, \ - target_ulong addr, intptr_t oprsz, \ - bool first, uintptr_t ra) \ -{ \ - intptr_t i = 0; \ - do { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m = 0; \ - if (pg & 1) { \ - if (!first && \ - unlikely(page_check_range(addr, sizeof(TYPEM), \ - PAGE_READ))) { \ - record_fault(env, i, oprsz); \ - return; \ - } \ - m = FN(env, addr, ra); \ - first = false; \ - } \ - *(TYPEE *)(vd + H(i)) = m; \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += sizeof(TYPEM); \ - } while (i & 15); \ - } while (i < oprsz); \ -} \ -void HELPER(sve_ldff1##PART)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t oprsz = simd_oprsz(desc); \ - unsigned rd = simd_data(desc); \ - void *vd = &env->vfp.zregs[rd]; \ - mmap_lock(); \ - if (likely(page_check_range(addr, oprsz, PAGE_READ) == 0)) { \ - do_sve_ld1##PART(env, vd, vg, addr, oprsz, GETPC()); \ - } else { \ - do_sve_ldff1##PART(env, vd, vg, addr, oprsz, true, GETPC()); \ - } \ - mmap_unlock(); \ -} +static void sve_ldff1_r(CPUARMState *env, void *vg, const target_ulong addr, + uint32_t desc, const uintptr_t retaddr, + const int esz, const int msz, + sve_ld1_host_fn *host_fn, + sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int mmu_idx = get_mmuidx(oi); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + void *vd = &env->vfp.zregs[rd]; + const int diffsz = esz - msz; + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t mem_max = reg_max >> diffsz; + intptr_t split, reg_off, mem_off; + void *host; -/* No-fault loads are like first-fault loads without the - * first faulting special case. - */ -#define DO_LDNF1(PART) \ -void HELPER(sve_ldnf1##PART)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t oprsz = simd_oprsz(desc); \ - unsigned rd = simd_data(desc); \ - void *vd = &env->vfp.zregs[rd]; \ - mmap_lock(); \ - if (likely(page_check_range(addr, oprsz, PAGE_READ) == 0)) { \ - do_sve_ld1##PART(env, vd, vg, addr, oprsz, GETPC()); \ - } else { \ - do_sve_ldff1##PART(env, vd, vg, addr, oprsz, false, GETPC()); \ - } \ - mmap_unlock(); \ -} + /* Skip to the first active element. */ + reg_off = find_next_active(vg, 0, reg_max, esz); + if (unlikely(reg_off == reg_max)) { + /* The entire predicate was false; no load occurs. */ + memset(vd, 0, reg_max); + return; + } + mem_off = reg_off >> diffsz; + set_helper_retaddr(retaddr); + /* + * If the (remaining) load is entirely within a single page, then: + * For softmmu, and the tlb hits, then no faults will occur; + * For user-only, either the first load will fault or none will. + * We can thus perform the load directly to the destination and + * Vd will be unmodified on any exception path. + */ + split = max_for_page(addr, mem_off, mem_max); + if (likely(split == mem_max)) { + host = tlb_vaddr_to_host(env, addr + mem_off, MMU_DATA_LOAD, mmu_idx); + if (test_host_page(host)) { + mem_off = host_fn(vd, vg, host - mem_off, mem_off, mem_max); + tcg_debug_assert(mem_off == mem_max); + set_helper_retaddr(0); + /* After any fault, zero any leading inactive elements. */ + swap_memzero(vd, reg_off); + return; + } + } + +#ifdef CONFIG_USER_ONLY + /* + * The page(s) containing this first element at ADDR+MEM_OFF must + * be valid. Considering that this first element may be misaligned + * and cross a page boundary itself, take the rest of the page from + * the last byte of the element. + */ + split = max_for_page(addr, mem_off + (1 << msz) - 1, mem_max); + mem_off = host_fn(vd, vg, g2h(addr), mem_off, split); + + /* After any fault, zero any leading inactive elements. */ + swap_memzero(vd, reg_off); + reg_off = mem_off << diffsz; #else + /* + * Perform one normal read, which will fault or not. + * But it is likely to bring the page into the tlb. + */ + tlb_fn(env, vd, reg_off, addr + mem_off, oi, retaddr); -/* TODO: System mode is not yet supported. - * This would probably use tlb_vaddr_to_host. - */ -#define DO_LDFF1(PART, FN, TYPEE, TYPEM, H) \ -void HELPER(sve_ldff1##PART)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - g_assert_not_reached(); \ -} - -#define DO_LDNF1(PART) \ -void HELPER(sve_ldnf1##PART)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - g_assert_not_reached(); \ -} + /* After any fault, zero any leading predicated false elts. */ + swap_memzero(vd, reg_off); + mem_off += 1 << msz; + reg_off += 1 << esz; + /* Try again to read the balance of the page. */ + split = max_for_page(addr, mem_off - 1, mem_max); + if (split >= (1 << msz)) { + host = tlb_vaddr_to_host(env, addr + mem_off, MMU_DATA_LOAD, mmu_idx); + if (host) { + mem_off = host_fn(vd, vg, host - mem_off, mem_off, split); + reg_off = mem_off << diffsz; + } + } #endif -DO_LDFF1(bb_r, cpu_ldub_data_ra, uint8_t, uint8_t, H1) -DO_LDFF1(bhu_r, cpu_ldub_data_ra, uint16_t, uint8_t, H1_2) -DO_LDFF1(bhs_r, cpu_ldsb_data_ra, uint16_t, int8_t, H1_2) -DO_LDFF1(bsu_r, cpu_ldub_data_ra, uint32_t, uint8_t, H1_4) -DO_LDFF1(bss_r, cpu_ldsb_data_ra, uint32_t, int8_t, H1_4) -DO_LDFF1(bdu_r, cpu_ldub_data_ra, uint64_t, uint8_t, ) -DO_LDFF1(bds_r, cpu_ldsb_data_ra, uint64_t, int8_t, ) + set_helper_retaddr(0); + record_fault(env, reg_off, reg_max); +} -DO_LDFF1(hh_r, cpu_lduw_data_ra, uint16_t, uint16_t, H1_2) -DO_LDFF1(hsu_r, cpu_lduw_data_ra, uint32_t, uint16_t, H1_4) -DO_LDFF1(hss_r, cpu_ldsw_data_ra, uint32_t, int8_t, H1_4) -DO_LDFF1(hdu_r, cpu_lduw_data_ra, uint64_t, uint16_t, ) -DO_LDFF1(hds_r, cpu_ldsw_data_ra, uint64_t, int16_t, ) +/* + * Common helper for all contiguous no-fault loads. + */ +static void sve_ldnf1_r(CPUARMState *env, void *vg, const target_ulong addr, + uint32_t desc, const int esz, const int msz, + sve_ld1_host_fn *host_fn) +{ + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + void *vd = &env->vfp.zregs[rd]; + const int diffsz = esz - msz; + const intptr_t reg_max = simd_oprsz(desc); + const intptr_t mem_max = reg_max >> diffsz; + const int mmu_idx = cpu_mmu_index(env, false); + intptr_t split, reg_off, mem_off; + void *host; -DO_LDFF1(ss_r, cpu_ldl_data_ra, uint32_t, uint32_t, H1_4) -DO_LDFF1(sdu_r, cpu_ldl_data_ra, uint64_t, uint32_t, ) -DO_LDFF1(sds_r, cpu_ldl_data_ra, uint64_t, int32_t, ) +#ifdef CONFIG_USER_ONLY + host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, mmu_idx); + if (likely(page_check_range(addr, mem_max, PAGE_READ) == 0)) { + /* The entire operation is valid and will not fault. */ + host_fn(vd, vg, host, 0, mem_max); + return; + } +#endif -DO_LDFF1(dd_r, cpu_ldq_data_ra, uint64_t, uint64_t, ) + /* There will be no fault, so we may modify in advance. */ + memset(vd, 0, reg_max); -#undef DO_LDFF1 + /* Skip to the first active element. */ + reg_off = find_next_active(vg, 0, reg_max, esz); + if (unlikely(reg_off == reg_max)) { + /* The entire predicate was false; no load occurs. */ + return; + } + mem_off = reg_off >> diffsz; -DO_LDNF1(bb_r) -DO_LDNF1(bhu_r) -DO_LDNF1(bhs_r) -DO_LDNF1(bsu_r) -DO_LDNF1(bss_r) -DO_LDNF1(bdu_r) -DO_LDNF1(bds_r) +#ifdef CONFIG_USER_ONLY + if (page_check_range(addr + mem_off, 1 << msz, PAGE_READ) == 0) { + /* At least one load is valid; take the rest of the page. */ + split = max_for_page(addr, mem_off + (1 << msz) - 1, mem_max); + mem_off = host_fn(vd, vg, host, mem_off, split); + reg_off = mem_off << diffsz; + } +#else + /* + * If the address is not in the TLB, we have no way to bring the + * entry into the TLB without also risking a fault. Note that + * the corollary is that we never load from an address not in RAM. + * + * This last is out of spec, in a weird corner case. + * Per the MemNF/MemSingleNF pseudocode, a NF load from Device memory + * must not actually hit the bus -- it returns UNKNOWN data instead. + * But if you map non-RAM with Normal memory attributes and do a NF + * load then it should access the bus. (Nobody ought actually do this + * in the real world, obviously.) + * + * Then there are the annoying special cases with watchpoints... + * + * TODO: Add a form of tlb_fill that does not raise an exception, + * with a form of tlb_vaddr_to_host and a set of loads to match. + * The non_fault_vaddr_to_host would handle everything, usually, + * and the loads would handle the iomem path for watchpoints. + */ + host = tlb_vaddr_to_host(env, addr + mem_off, MMU_DATA_LOAD, mmu_idx); + split = max_for_page(addr, mem_off, mem_max); + if (host && split >= (1 << msz)) { + mem_off = host_fn(vd, vg, host - mem_off, mem_off, split); + reg_off = mem_off << diffsz; + } +#endif -DO_LDNF1(hh_r) -DO_LDNF1(hsu_r) -DO_LDNF1(hss_r) -DO_LDNF1(hdu_r) -DO_LDNF1(hds_r) + record_fault(env, reg_off, reg_max); +} -DO_LDNF1(ss_r) -DO_LDNF1(sdu_r) -DO_LDNF1(sds_r) +#define DO_LDFF1_LDNF1_1(PART, ESZ) \ +void HELPER(sve_ldff1##PART##_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ldff1_r(env, vg, addr, desc, GETPC(), ESZ, 0, \ + sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ +} \ +void HELPER(sve_ldnf1##PART##_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ldnf1_r(env, vg, addr, desc, ESZ, 0, sve_ld1##PART##_host); \ +} -DO_LDNF1(dd_r) +#define DO_LDFF1_LDNF1_2(PART, ESZ, MSZ) \ +void HELPER(sve_ldff1##PART##_le_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ldff1_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, \ + sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ +} \ +void HELPER(sve_ldnf1##PART##_le_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ldnf1_r(env, vg, addr, desc, ESZ, MSZ, sve_ld1##PART##_le_host); \ +} \ +void HELPER(sve_ldff1##PART##_be_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ldff1_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, \ + sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ +} \ +void HELPER(sve_ldnf1##PART##_be_r)(CPUARMState *env, void *vg, \ + target_ulong addr, uint32_t desc) \ +{ \ + sve_ldnf1_r(env, vg, addr, desc, ESZ, MSZ, sve_ld1##PART##_be_host); \ +} -#undef DO_LDNF1 +DO_LDFF1_LDNF1_1(bb, 0) +DO_LDFF1_LDNF1_1(bhu, 1) +DO_LDFF1_LDNF1_1(bhs, 1) +DO_LDFF1_LDNF1_1(bsu, 2) +DO_LDFF1_LDNF1_1(bss, 2) +DO_LDFF1_LDNF1_1(bdu, 3) +DO_LDFF1_LDNF1_1(bds, 3) + +DO_LDFF1_LDNF1_2(hh, 1, 1) +DO_LDFF1_LDNF1_2(hsu, 2, 1) +DO_LDFF1_LDNF1_2(hss, 2, 1) +DO_LDFF1_LDNF1_2(hdu, 3, 1) +DO_LDFF1_LDNF1_2(hds, 3, 1) + +DO_LDFF1_LDNF1_2(ss, 2, 2) +DO_LDFF1_LDNF1_2(sdu, 3, 2) +DO_LDFF1_LDNF1_2(sds, 3, 2) + +DO_LDFF1_LDNF1_2(dd, 3, 3) + +#undef DO_LDFF1_LDNF1_1 +#undef DO_LDFF1_LDNF1_2 /* * Store contiguous data, protected by a governing predicate. */ -#define DO_ST1(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *vd = &env->vfp.zregs[rd]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - if (pg & 1) { \ - TYPEM m = *(TYPEE *)(vd + H(i)); \ - FN(env, addr, m, ra); \ - } \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += sizeof(TYPEM); \ - } while (i & 15); \ - } \ + +#ifdef CONFIG_SOFTMMU +#define DO_ST_TLB(NAME, H, TYPEM, HOST, MOEND, TLB) \ +static void sve_##NAME##_tlb(CPUARMState *env, void *vd, intptr_t reg_off, \ + target_ulong addr, TCGMemOpIdx oi, uintptr_t ra) \ +{ \ + TLB(env, addr, *(TYPEM *)(vd + H(reg_off)), oi, ra); \ } - -#define DO_ST1_D(NAME, FN, TYPEM) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc) / 8; \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - uint64_t *d = &env->vfp.zregs[rd].d[0]; \ - uint8_t *pg = vg; \ - for (i = 0; i < oprsz; i += 1) { \ - if (pg[H1(i)] & 1) { \ - FN(env, addr, d[i], ra); \ - } \ - addr += sizeof(TYPEM); \ - } \ +#else +#define DO_ST_TLB(NAME, H, TYPEM, HOST, MOEND, TLB) \ +static void sve_##NAME##_tlb(CPUARMState *env, void *vd, intptr_t reg_off, \ + target_ulong addr, TCGMemOpIdx oi, uintptr_t ra) \ +{ \ + HOST(g2h(addr), *(TYPEM *)(vd + H(reg_off))); \ } +#endif -#define DO_ST2(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *d1 = &env->vfp.zregs[rd]; \ - void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - if (pg & 1) { \ - TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ - TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ - FN(env, addr, m1, ra); \ - FN(env, addr + sizeof(TYPEM), m2, ra); \ - } \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += 2 * sizeof(TYPEM); \ - } while (i & 15); \ - } \ -} +DO_ST_TLB(st1bb, H1, uint8_t, stb_p, 0, helper_ret_stb_mmu) +DO_ST_TLB(st1bh, H1_2, uint16_t, stb_p, 0, helper_ret_stb_mmu) +DO_ST_TLB(st1bs, H1_4, uint32_t, stb_p, 0, helper_ret_stb_mmu) +DO_ST_TLB(st1bd, , uint64_t, stb_p, 0, helper_ret_stb_mmu) -#define DO_ST3(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *d1 = &env->vfp.zregs[rd]; \ - void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ - void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - if (pg & 1) { \ - TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ - TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ - TYPEM m3 = *(TYPEE *)(d3 + H(i)); \ - FN(env, addr, m1, ra); \ - FN(env, addr + sizeof(TYPEM), m2, ra); \ - FN(env, addr + 2 * sizeof(TYPEM), m3, ra); \ - } \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += 3 * sizeof(TYPEM); \ - } while (i & 15); \ - } \ -} +DO_ST_TLB(st1hh_le, H1_2, uint16_t, stw_le_p, MO_LE, helper_le_stw_mmu) +DO_ST_TLB(st1hs_le, H1_4, uint32_t, stw_le_p, MO_LE, helper_le_stw_mmu) +DO_ST_TLB(st1hd_le, , uint64_t, stw_le_p, MO_LE, helper_le_stw_mmu) -#define DO_ST4(NAME, FN, TYPEE, TYPEM, H) \ -void HELPER(NAME)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - intptr_t ra = GETPC(); \ - unsigned rd = simd_data(desc); \ - void *d1 = &env->vfp.zregs[rd]; \ - void *d2 = &env->vfp.zregs[(rd + 1) & 31]; \ - void *d3 = &env->vfp.zregs[(rd + 2) & 31]; \ - void *d4 = &env->vfp.zregs[(rd + 3) & 31]; \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - if (pg & 1) { \ - TYPEM m1 = *(TYPEE *)(d1 + H(i)); \ - TYPEM m2 = *(TYPEE *)(d2 + H(i)); \ - TYPEM m3 = *(TYPEE *)(d3 + H(i)); \ - TYPEM m4 = *(TYPEE *)(d4 + H(i)); \ - FN(env, addr, m1, ra); \ - FN(env, addr + sizeof(TYPEM), m2, ra); \ - FN(env, addr + 2 * sizeof(TYPEM), m3, ra); \ - FN(env, addr + 3 * sizeof(TYPEM), m4, ra); \ - } \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - addr += 4 * sizeof(TYPEM); \ - } while (i & 15); \ - } \ -} +DO_ST_TLB(st1ss_le, H1_4, uint32_t, stl_le_p, MO_LE, helper_le_stl_mmu) +DO_ST_TLB(st1sd_le, , uint64_t, stl_le_p, MO_LE, helper_le_stl_mmu) -DO_ST1(sve_st1bh_r, cpu_stb_data_ra, uint16_t, uint8_t, H1_2) -DO_ST1(sve_st1bs_r, cpu_stb_data_ra, uint32_t, uint8_t, H1_4) -DO_ST1_D(sve_st1bd_r, cpu_stb_data_ra, uint8_t) +DO_ST_TLB(st1dd_le, , uint64_t, stq_le_p, MO_LE, helper_le_stq_mmu) -DO_ST1(sve_st1hs_r, cpu_stw_data_ra, uint32_t, uint16_t, H1_4) -DO_ST1_D(sve_st1hd_r, cpu_stw_data_ra, uint16_t) +DO_ST_TLB(st1hh_be, H1_2, uint16_t, stw_be_p, MO_BE, helper_be_stw_mmu) +DO_ST_TLB(st1hs_be, H1_4, uint32_t, stw_be_p, MO_BE, helper_be_stw_mmu) +DO_ST_TLB(st1hd_be, , uint64_t, stw_be_p, MO_BE, helper_be_stw_mmu) -DO_ST1_D(sve_st1sd_r, cpu_stl_data_ra, uint32_t) +DO_ST_TLB(st1ss_be, H1_4, uint32_t, stl_be_p, MO_BE, helper_be_stl_mmu) +DO_ST_TLB(st1sd_be, , uint64_t, stl_be_p, MO_BE, helper_be_stl_mmu) -DO_ST1(sve_st1bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) -DO_ST2(sve_st2bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) -DO_ST3(sve_st3bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) -DO_ST4(sve_st4bb_r, cpu_stb_data_ra, uint8_t, uint8_t, H1) +DO_ST_TLB(st1dd_be, , uint64_t, stq_be_p, MO_BE, helper_be_stq_mmu) -DO_ST1(sve_st1hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) -DO_ST2(sve_st2hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) -DO_ST3(sve_st3hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) -DO_ST4(sve_st4hh_r, cpu_stw_data_ra, uint16_t, uint16_t, H1_2) +#undef DO_ST_TLB -DO_ST1(sve_st1ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) -DO_ST2(sve_st2ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) -DO_ST3(sve_st3ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) -DO_ST4(sve_st4ss_r, cpu_stl_data_ra, uint32_t, uint32_t, H1_4) - -DO_ST1_D(sve_st1dd_r, cpu_stq_data_ra, uint64_t) - -void HELPER(sve_st2dd_r)(CPUARMState *env, void *vg, - target_ulong addr, uint32_t desc) +/* + * Common helpers for all contiguous 1,2,3,4-register predicated stores. + */ +static void sve_st1_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, const uintptr_t ra, + const int esize, const int msize, + sve_st1_tlb_fn *tlb_fn) { - intptr_t i, oprsz = simd_oprsz(desc) / 8; - intptr_t ra = GETPC(); - unsigned rd = simd_data(desc); - uint64_t *d1 = &env->vfp.zregs[rd].d[0]; - uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; - uint8_t *pg = vg; + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + void *vd = &env->vfp.zregs[rd]; - for (i = 0; i < oprsz; i += 1) { - if (pg[H1(i)] & 1) { - cpu_stq_data_ra(env, addr, d1[i], ra); - cpu_stq_data_ra(env, addr + 8, d2[i], ra); - } - addr += 2 * 8; + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, vd, i, addr, oi, ra); + } + i += esize, pg >>= esize; + addr += msize; + } while (i & 15); } + set_helper_retaddr(0); } -void HELPER(sve_st3dd_r)(CPUARMState *env, void *vg, - target_ulong addr, uint32_t desc) +static void sve_st2_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, const uintptr_t ra, + const int esize, const int msize, + sve_st1_tlb_fn *tlb_fn) { - intptr_t i, oprsz = simd_oprsz(desc) / 8; - intptr_t ra = GETPC(); - unsigned rd = simd_data(desc); - uint64_t *d1 = &env->vfp.zregs[rd].d[0]; - uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; - uint64_t *d3 = &env->vfp.zregs[(rd + 2) & 31].d[0]; - uint8_t *pg = vg; + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + void *d1 = &env->vfp.zregs[rd]; + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; - for (i = 0; i < oprsz; i += 1) { - if (pg[H1(i)] & 1) { - cpu_stq_data_ra(env, addr, d1[i], ra); - cpu_stq_data_ra(env, addr + 8, d2[i], ra); - cpu_stq_data_ra(env, addr + 16, d3[i], ra); - } - addr += 3 * 8; + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, d1, i, addr, oi, ra); + tlb_fn(env, d2, i, addr + msize, oi, ra); + } + i += esize, pg >>= esize; + addr += 2 * msize; + } while (i & 15); } + set_helper_retaddr(0); } -void HELPER(sve_st4dd_r)(CPUARMState *env, void *vg, - target_ulong addr, uint32_t desc) +static void sve_st3_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, const uintptr_t ra, + const int esize, const int msize, + sve_st1_tlb_fn *tlb_fn) { - intptr_t i, oprsz = simd_oprsz(desc) / 8; - intptr_t ra = GETPC(); - unsigned rd = simd_data(desc); - uint64_t *d1 = &env->vfp.zregs[rd].d[0]; - uint64_t *d2 = &env->vfp.zregs[(rd + 1) & 31].d[0]; - uint64_t *d3 = &env->vfp.zregs[(rd + 2) & 31].d[0]; - uint64_t *d4 = &env->vfp.zregs[(rd + 3) & 31].d[0]; - uint8_t *pg = vg; + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + void *d1 = &env->vfp.zregs[rd]; + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; - for (i = 0; i < oprsz; i += 1) { - if (pg[H1(i)] & 1) { - cpu_stq_data_ra(env, addr, d1[i], ra); - cpu_stq_data_ra(env, addr + 8, d2[i], ra); - cpu_stq_data_ra(env, addr + 16, d3[i], ra); - cpu_stq_data_ra(env, addr + 24, d4[i], ra); - } - addr += 4 * 8; + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, d1, i, addr, oi, ra); + tlb_fn(env, d2, i, addr + msize, oi, ra); + tlb_fn(env, d3, i, addr + 2 * msize, oi, ra); + } + i += esize, pg >>= esize; + addr += 3 * msize; + } while (i & 15); } + set_helper_retaddr(0); } -/* Loads with a vector index. */ +static void sve_st4_r(CPUARMState *env, void *vg, target_ulong addr, + uint32_t desc, const uintptr_t ra, + const int esize, const int msize, + sve_st1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const unsigned rd = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 5); + intptr_t i, oprsz = simd_oprsz(desc); + void *d1 = &env->vfp.zregs[rd]; + void *d2 = &env->vfp.zregs[(rd + 1) & 31]; + void *d3 = &env->vfp.zregs[(rd + 2) & 31]; + void *d4 = &env->vfp.zregs[(rd + 3) & 31]; -#define DO_LD1_ZPZ_S(NAME, TYPEI, TYPEM, FN) \ -void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ - target_ulong base, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - unsigned scale = simd_data(desc); \ - uintptr_t ra = GETPC(); \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m = 0; \ - if (pg & 1) { \ - target_ulong off = *(TYPEI *)(vm + H1_4(i)); \ - m = FN(env, base + (off << scale), ra); \ - } \ - *(uint32_t *)(vd + H1_4(i)) = m; \ - i += 4, pg >>= 4; \ - } while (i & 15); \ - } \ + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (pg & 1) { + tlb_fn(env, d1, i, addr, oi, ra); + tlb_fn(env, d2, i, addr + msize, oi, ra); + tlb_fn(env, d3, i, addr + 2 * msize, oi, ra); + tlb_fn(env, d4, i, addr + 3 * msize, oi, ra); + } + i += esize, pg >>= esize; + addr += 4 * msize; + } while (i & 15); + } + set_helper_retaddr(0); } -#define DO_LD1_ZPZ_D(NAME, TYPEI, TYPEM, FN) \ -void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ - target_ulong base, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc) / 8; \ - unsigned scale = simd_data(desc); \ - uintptr_t ra = GETPC(); \ - uint64_t *d = vd, *m = vm; uint8_t *pg = vg; \ - for (i = 0; i < oprsz; i++) { \ - TYPEM mm = 0; \ - if (pg[H1(i)] & 1) { \ - target_ulong off = (TYPEI)m[i]; \ - mm = FN(env, base + (off << scale), ra); \ - } \ - d[i] = mm; \ - } \ +#define DO_STN_1(N, NAME, ESIZE) \ +void QEMU_FLATTEN HELPER(sve_st##N##NAME##_r) \ + (CPUARMState *env, void *vg, target_ulong addr, uint32_t desc) \ +{ \ + sve_st##N##_r(env, vg, addr, desc, GETPC(), ESIZE, 1, \ + sve_st1##NAME##_tlb); \ } -DO_LD1_ZPZ_S(sve_ldbsu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_S(sve_ldhsu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_S(sve_ldssu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) -DO_LD1_ZPZ_S(sve_ldbss_zsu, uint32_t, int8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_S(sve_ldhss_zsu, uint32_t, int16_t, cpu_lduw_data_ra) +#define DO_STN_2(N, NAME, ESIZE, MSIZE) \ +void QEMU_FLATTEN HELPER(sve_st##N##NAME##_le_r) \ + (CPUARMState *env, void *vg, target_ulong addr, uint32_t desc) \ +{ \ + sve_st##N##_r(env, vg, addr, desc, GETPC(), ESIZE, MSIZE, \ + sve_st1##NAME##_le_tlb); \ +} \ +void QEMU_FLATTEN HELPER(sve_st##N##NAME##_be_r) \ + (CPUARMState *env, void *vg, target_ulong addr, uint32_t desc) \ +{ \ + sve_st##N##_r(env, vg, addr, desc, GETPC(), ESIZE, MSIZE, \ + sve_st1##NAME##_be_tlb); \ +} -DO_LD1_ZPZ_S(sve_ldbsu_zss, int32_t, uint8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_S(sve_ldhsu_zss, int32_t, uint16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_S(sve_ldssu_zss, int32_t, uint32_t, cpu_ldl_data_ra) -DO_LD1_ZPZ_S(sve_ldbss_zss, int32_t, int8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_S(sve_ldhss_zss, int32_t, int16_t, cpu_lduw_data_ra) +DO_STN_1(1, bb, 1) +DO_STN_1(1, bh, 2) +DO_STN_1(1, bs, 4) +DO_STN_1(1, bd, 8) +DO_STN_1(2, bb, 1) +DO_STN_1(3, bb, 1) +DO_STN_1(4, bb, 1) -DO_LD1_ZPZ_D(sve_ldbdu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_D(sve_ldhdu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_D(sve_ldsdu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) -DO_LD1_ZPZ_D(sve_ldddu_zsu, uint32_t, uint64_t, cpu_ldq_data_ra) -DO_LD1_ZPZ_D(sve_ldbds_zsu, uint32_t, int8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_D(sve_ldhds_zsu, uint32_t, int16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_D(sve_ldsds_zsu, uint32_t, int32_t, cpu_ldl_data_ra) +DO_STN_2(1, hh, 2, 2) +DO_STN_2(1, hs, 4, 2) +DO_STN_2(1, hd, 8, 2) +DO_STN_2(2, hh, 2, 2) +DO_STN_2(3, hh, 2, 2) +DO_STN_2(4, hh, 2, 2) -DO_LD1_ZPZ_D(sve_ldbdu_zss, int32_t, uint8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_D(sve_ldhdu_zss, int32_t, uint16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_D(sve_ldsdu_zss, int32_t, uint32_t, cpu_ldl_data_ra) -DO_LD1_ZPZ_D(sve_ldddu_zss, int32_t, uint64_t, cpu_ldq_data_ra) -DO_LD1_ZPZ_D(sve_ldbds_zss, int32_t, int8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_D(sve_ldhds_zss, int32_t, int16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_D(sve_ldsds_zss, int32_t, int32_t, cpu_ldl_data_ra) +DO_STN_2(1, ss, 4, 4) +DO_STN_2(1, sd, 8, 4) +DO_STN_2(2, ss, 4, 4) +DO_STN_2(3, ss, 4, 4) +DO_STN_2(4, ss, 4, 4) -DO_LD1_ZPZ_D(sve_ldbdu_zd, uint64_t, uint8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_D(sve_ldhdu_zd, uint64_t, uint16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_D(sve_ldsdu_zd, uint64_t, uint32_t, cpu_ldl_data_ra) -DO_LD1_ZPZ_D(sve_ldddu_zd, uint64_t, uint64_t, cpu_ldq_data_ra) -DO_LD1_ZPZ_D(sve_ldbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) -DO_LD1_ZPZ_D(sve_ldhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) -DO_LD1_ZPZ_D(sve_ldsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) +DO_STN_2(1, dd, 8, 8) +DO_STN_2(2, dd, 8, 8) +DO_STN_2(3, dd, 8, 8) +DO_STN_2(4, dd, 8, 8) + +#undef DO_STN_1 +#undef DO_STN_2 + +/* + * Loads with a vector index. + */ + +/* + * Load the element at @reg + @reg_ofs, sign or zero-extend as needed. + */ +typedef target_ulong zreg_off_fn(void *reg, intptr_t reg_ofs); + +static target_ulong off_zsu_s(void *reg, intptr_t reg_ofs) +{ + return *(uint32_t *)(reg + H1_4(reg_ofs)); +} + +static target_ulong off_zss_s(void *reg, intptr_t reg_ofs) +{ + return *(int32_t *)(reg + H1_4(reg_ofs)); +} + +static target_ulong off_zsu_d(void *reg, intptr_t reg_ofs) +{ + return (uint32_t)*(uint64_t *)(reg + reg_ofs); +} + +static target_ulong off_zss_d(void *reg, intptr_t reg_ofs) +{ + return (int32_t)*(uint64_t *)(reg + reg_ofs); +} + +static target_ulong off_zd_d(void *reg, intptr_t reg_ofs) +{ + return *(uint64_t *)(reg + reg_ofs); +} + +static void sve_ld1_zs(CPUARMState *env, void *vd, void *vg, void *vm, + target_ulong base, uint32_t desc, uintptr_t ra, + zreg_off_fn *off_fn, sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int scale = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 2); + intptr_t i, oprsz = simd_oprsz(desc); + ARMVectorReg scratch = { }; + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (likely(pg & 1)) { + target_ulong off = off_fn(vm, i); + tlb_fn(env, &scratch, i, base + (off << scale), oi, ra); + } + i += 4, pg >>= 4; + } while (i & 15); + } + set_helper_retaddr(0); + + /* Wait until all exceptions have been raised to write back. */ + memcpy(vd, &scratch, oprsz); +} + +static void sve_ld1_zd(CPUARMState *env, void *vd, void *vg, void *vm, + target_ulong base, uint32_t desc, uintptr_t ra, + zreg_off_fn *off_fn, sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int scale = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 2); + intptr_t i, oprsz = simd_oprsz(desc) / 8; + ARMVectorReg scratch = { }; + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; i++) { + uint8_t pg = *(uint8_t *)(vg + H1(i)); + if (likely(pg & 1)) { + target_ulong off = off_fn(vm, i * 8); + tlb_fn(env, &scratch, i * 8, base + (off << scale), oi, ra); + } + } + set_helper_retaddr(0); + + /* Wait until all exceptions have been raised to write back. */ + memcpy(vd, &scratch, oprsz * 8); +} + +#define DO_LD1_ZPZ_S(MEM, OFS) \ +void QEMU_FLATTEN HELPER(sve_ld##MEM##_##OFS) \ + (CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + sve_ld1_zs(env, vd, vg, vm, base, desc, GETPC(), \ + off_##OFS##_s, sve_ld1##MEM##_tlb); \ +} + +#define DO_LD1_ZPZ_D(MEM, OFS) \ +void QEMU_FLATTEN HELPER(sve_ld##MEM##_##OFS) \ + (CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + sve_ld1_zd(env, vd, vg, vm, base, desc, GETPC(), \ + off_##OFS##_d, sve_ld1##MEM##_tlb); \ +} + +DO_LD1_ZPZ_S(bsu, zsu) +DO_LD1_ZPZ_S(bsu, zss) +DO_LD1_ZPZ_D(bdu, zsu) +DO_LD1_ZPZ_D(bdu, zss) +DO_LD1_ZPZ_D(bdu, zd) + +DO_LD1_ZPZ_S(bss, zsu) +DO_LD1_ZPZ_S(bss, zss) +DO_LD1_ZPZ_D(bds, zsu) +DO_LD1_ZPZ_D(bds, zss) +DO_LD1_ZPZ_D(bds, zd) + +DO_LD1_ZPZ_S(hsu_le, zsu) +DO_LD1_ZPZ_S(hsu_le, zss) +DO_LD1_ZPZ_D(hdu_le, zsu) +DO_LD1_ZPZ_D(hdu_le, zss) +DO_LD1_ZPZ_D(hdu_le, zd) + +DO_LD1_ZPZ_S(hsu_be, zsu) +DO_LD1_ZPZ_S(hsu_be, zss) +DO_LD1_ZPZ_D(hdu_be, zsu) +DO_LD1_ZPZ_D(hdu_be, zss) +DO_LD1_ZPZ_D(hdu_be, zd) + +DO_LD1_ZPZ_S(hss_le, zsu) +DO_LD1_ZPZ_S(hss_le, zss) +DO_LD1_ZPZ_D(hds_le, zsu) +DO_LD1_ZPZ_D(hds_le, zss) +DO_LD1_ZPZ_D(hds_le, zd) + +DO_LD1_ZPZ_S(hss_be, zsu) +DO_LD1_ZPZ_S(hss_be, zss) +DO_LD1_ZPZ_D(hds_be, zsu) +DO_LD1_ZPZ_D(hds_be, zss) +DO_LD1_ZPZ_D(hds_be, zd) + +DO_LD1_ZPZ_S(ss_le, zsu) +DO_LD1_ZPZ_S(ss_le, zss) +DO_LD1_ZPZ_D(sdu_le, zsu) +DO_LD1_ZPZ_D(sdu_le, zss) +DO_LD1_ZPZ_D(sdu_le, zd) + +DO_LD1_ZPZ_S(ss_be, zsu) +DO_LD1_ZPZ_S(ss_be, zss) +DO_LD1_ZPZ_D(sdu_be, zsu) +DO_LD1_ZPZ_D(sdu_be, zss) +DO_LD1_ZPZ_D(sdu_be, zd) + +DO_LD1_ZPZ_D(sds_le, zsu) +DO_LD1_ZPZ_D(sds_le, zss) +DO_LD1_ZPZ_D(sds_le, zd) + +DO_LD1_ZPZ_D(sds_be, zsu) +DO_LD1_ZPZ_D(sds_be, zss) +DO_LD1_ZPZ_D(sds_be, zd) + +DO_LD1_ZPZ_D(dd_le, zsu) +DO_LD1_ZPZ_D(dd_le, zss) +DO_LD1_ZPZ_D(dd_le, zd) + +DO_LD1_ZPZ_D(dd_be, zsu) +DO_LD1_ZPZ_D(dd_be, zss) +DO_LD1_ZPZ_D(dd_be, zd) + +#undef DO_LD1_ZPZ_S +#undef DO_LD1_ZPZ_D /* First fault loads with a vector index. */ -#ifdef CONFIG_USER_ONLY +/* Load one element into VD+REG_OFF from (ENV,VADDR) without faulting. + * The controlling predicate is known to be true. Return true if the + * load was successful. + */ +typedef bool sve_ld1_nf_fn(CPUARMState *env, void *vd, intptr_t reg_off, + target_ulong vaddr, int mmu_idx); -#define DO_LDFF1_ZPZ(NAME, TYPEE, TYPEI, TYPEM, FN, H) \ -void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ - target_ulong base, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - unsigned scale = simd_data(desc); \ - uintptr_t ra = GETPC(); \ - bool first = true; \ - mmap_lock(); \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - TYPEM m = 0; \ - if (pg & 1) { \ - target_ulong off = *(TYPEI *)(vm + H(i)); \ - target_ulong addr = base + (off << scale); \ - if (!first && \ - page_check_range(addr, sizeof(TYPEM), PAGE_READ)) { \ - record_fault(env, i, oprsz); \ - goto exit; \ - } \ - m = FN(env, addr, ra); \ - first = false; \ - } \ - *(TYPEE *)(vd + H(i)) = m; \ - i += sizeof(TYPEE), pg >>= sizeof(TYPEE); \ - } while (i & 15); \ - } \ - exit: \ - mmap_unlock(); \ +#ifdef CONFIG_SOFTMMU +#define DO_LD_NF(NAME, H, TYPEE, TYPEM, HOST) \ +static bool sve_ld##NAME##_nf(CPUARMState *env, void *vd, intptr_t reg_off, \ + target_ulong addr, int mmu_idx) \ +{ \ + target_ulong next_page = -(addr | TARGET_PAGE_MASK); \ + if (likely(next_page - addr >= sizeof(TYPEM))) { \ + void *host = tlb_vaddr_to_host(env, addr, MMU_DATA_LOAD, mmu_idx); \ + if (likely(host)) { \ + TYPEM val = HOST(host); \ + *(TYPEE *)(vd + H(reg_off)) = val; \ + return true; \ + } \ + } \ + return false; \ } - #else - -#define DO_LDFF1_ZPZ(NAME, TYPEE, TYPEI, TYPEM, FN, H) \ -void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ - target_ulong base, uint32_t desc) \ -{ \ - g_assert_not_reached(); \ +#define DO_LD_NF(NAME, H, TYPEE, TYPEM, HOST) \ +static bool sve_ld##NAME##_nf(CPUARMState *env, void *vd, intptr_t reg_off, \ + target_ulong addr, int mmu_idx) \ +{ \ + if (likely(page_check_range(addr, sizeof(TYPEM), PAGE_READ))) { \ + TYPEM val = HOST(g2h(addr)); \ + *(TYPEE *)(vd + H(reg_off)) = val; \ + return true; \ + } \ + return false; \ } - #endif -#define DO_LDFF1_ZPZ_S(NAME, TYPEI, TYPEM, FN) \ - DO_LDFF1_ZPZ(NAME, uint32_t, TYPEI, TYPEM, FN, H1_4) -#define DO_LDFF1_ZPZ_D(NAME, TYPEI, TYPEM, FN) \ - DO_LDFF1_ZPZ(NAME, uint64_t, TYPEI, TYPEM, FN, ) +DO_LD_NF(bsu, H1_4, uint32_t, uint8_t, ldub_p) +DO_LD_NF(bss, H1_4, uint32_t, int8_t, ldsb_p) +DO_LD_NF(bdu, , uint64_t, uint8_t, ldub_p) +DO_LD_NF(bds, , uint64_t, int8_t, ldsb_p) -DO_LDFF1_ZPZ_S(sve_ldffbsu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffhsu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffssu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffbss_zsu, uint32_t, int8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffhss_zsu, uint32_t, int16_t, cpu_lduw_data_ra) +DO_LD_NF(hsu_le, H1_4, uint32_t, uint16_t, lduw_le_p) +DO_LD_NF(hss_le, H1_4, uint32_t, int16_t, ldsw_le_p) +DO_LD_NF(hsu_be, H1_4, uint32_t, uint16_t, lduw_be_p) +DO_LD_NF(hss_be, H1_4, uint32_t, int16_t, ldsw_be_p) +DO_LD_NF(hdu_le, , uint64_t, uint16_t, lduw_le_p) +DO_LD_NF(hds_le, , uint64_t, int16_t, ldsw_le_p) +DO_LD_NF(hdu_be, , uint64_t, uint16_t, lduw_be_p) +DO_LD_NF(hds_be, , uint64_t, int16_t, ldsw_be_p) -DO_LDFF1_ZPZ_S(sve_ldffbsu_zss, int32_t, uint8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffhsu_zss, int32_t, uint16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffssu_zss, int32_t, uint32_t, cpu_ldl_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffbss_zss, int32_t, int8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_S(sve_ldffhss_zss, int32_t, int16_t, cpu_lduw_data_ra) +DO_LD_NF(ss_le, H1_4, uint32_t, uint32_t, ldl_le_p) +DO_LD_NF(ss_be, H1_4, uint32_t, uint32_t, ldl_be_p) +DO_LD_NF(sdu_le, , uint64_t, uint32_t, ldl_le_p) +DO_LD_NF(sds_le, , uint64_t, int32_t, ldl_le_p) +DO_LD_NF(sdu_be, , uint64_t, uint32_t, ldl_be_p) +DO_LD_NF(sds_be, , uint64_t, int32_t, ldl_be_p) -DO_LDFF1_ZPZ_D(sve_ldffbdu_zsu, uint32_t, uint8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffhdu_zsu, uint32_t, uint16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffsdu_zsu, uint32_t, uint32_t, cpu_ldl_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffddu_zsu, uint32_t, uint64_t, cpu_ldq_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffbds_zsu, uint32_t, int8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffhds_zsu, uint32_t, int16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffsds_zsu, uint32_t, int32_t, cpu_ldl_data_ra) +DO_LD_NF(dd_le, , uint64_t, uint64_t, ldq_le_p) +DO_LD_NF(dd_be, , uint64_t, uint64_t, ldq_be_p) -DO_LDFF1_ZPZ_D(sve_ldffbdu_zss, int32_t, uint8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffhdu_zss, int32_t, uint16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffsdu_zss, int32_t, uint32_t, cpu_ldl_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffddu_zss, int32_t, uint64_t, cpu_ldq_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffbds_zss, int32_t, int8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffhds_zss, int32_t, int16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffsds_zss, int32_t, int32_t, cpu_ldl_data_ra) +/* + * Common helper for all gather first-faulting loads. + */ +static inline void sve_ldff1_zs(CPUARMState *env, void *vd, void *vg, void *vm, + target_ulong base, uint32_t desc, uintptr_t ra, + zreg_off_fn *off_fn, sve_ld1_tlb_fn *tlb_fn, + sve_ld1_nf_fn *nonfault_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int mmu_idx = get_mmuidx(oi); + const int scale = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 2); + intptr_t reg_off, reg_max = simd_oprsz(desc); + target_ulong addr; -DO_LDFF1_ZPZ_D(sve_ldffbdu_zd, uint64_t, uint8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffhdu_zd, uint64_t, uint16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffsdu_zd, uint64_t, uint32_t, cpu_ldl_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffddu_zd, uint64_t, uint64_t, cpu_ldq_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffbds_zd, uint64_t, int8_t, cpu_ldub_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffhds_zd, uint64_t, int16_t, cpu_lduw_data_ra) -DO_LDFF1_ZPZ_D(sve_ldffsds_zd, uint64_t, int32_t, cpu_ldl_data_ra) + /* Skip to the first true predicate. */ + reg_off = find_next_active(vg, 0, reg_max, MO_32); + if (likely(reg_off < reg_max)) { + /* Perform one normal read, which will fault or not. */ + set_helper_retaddr(ra); + addr = off_fn(vm, reg_off); + addr = base + (addr << scale); + tlb_fn(env, vd, reg_off, addr, oi, ra); + + /* The rest of the reads will be non-faulting. */ + set_helper_retaddr(0); + } + + /* After any fault, zero the leading predicated false elements. */ + swap_memzero(vd, reg_off); + + while (likely((reg_off += 4) < reg_max)) { + uint64_t pg = *(uint64_t *)(vg + (reg_off >> 6) * 8); + if (likely((pg >> (reg_off & 63)) & 1)) { + addr = off_fn(vm, reg_off); + addr = base + (addr << scale); + if (!nonfault_fn(env, vd, reg_off, addr, mmu_idx)) { + record_fault(env, reg_off, reg_max); + break; + } + } else { + *(uint32_t *)(vd + H1_4(reg_off)) = 0; + } + } +} + +static inline void sve_ldff1_zd(CPUARMState *env, void *vd, void *vg, void *vm, + target_ulong base, uint32_t desc, uintptr_t ra, + zreg_off_fn *off_fn, sve_ld1_tlb_fn *tlb_fn, + sve_ld1_nf_fn *nonfault_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int mmu_idx = get_mmuidx(oi); + const int scale = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 2); + intptr_t reg_off, reg_max = simd_oprsz(desc); + target_ulong addr; + + /* Skip to the first true predicate. */ + reg_off = find_next_active(vg, 0, reg_max, MO_64); + if (likely(reg_off < reg_max)) { + /* Perform one normal read, which will fault or not. */ + set_helper_retaddr(ra); + addr = off_fn(vm, reg_off); + addr = base + (addr << scale); + tlb_fn(env, vd, reg_off, addr, oi, ra); + + /* The rest of the reads will be non-faulting. */ + set_helper_retaddr(0); + } + + /* After any fault, zero the leading predicated false elements. */ + swap_memzero(vd, reg_off); + + while (likely((reg_off += 8) < reg_max)) { + uint8_t pg = *(uint8_t *)(vg + H1(reg_off >> 3)); + if (likely(pg & 1)) { + addr = off_fn(vm, reg_off); + addr = base + (addr << scale); + if (!nonfault_fn(env, vd, reg_off, addr, mmu_idx)) { + record_fault(env, reg_off, reg_max); + break; + } + } else { + *(uint64_t *)(vd + reg_off) = 0; + } + } +} + +#define DO_LDFF1_ZPZ_S(MEM, OFS) \ +void HELPER(sve_ldff##MEM##_##OFS) \ + (CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + sve_ldff1_zs(env, vd, vg, vm, base, desc, GETPC(), \ + off_##OFS##_s, sve_ld1##MEM##_tlb, sve_ld##MEM##_nf); \ +} + +#define DO_LDFF1_ZPZ_D(MEM, OFS) \ +void HELPER(sve_ldff##MEM##_##OFS) \ + (CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + sve_ldff1_zd(env, vd, vg, vm, base, desc, GETPC(), \ + off_##OFS##_d, sve_ld1##MEM##_tlb, sve_ld##MEM##_nf); \ +} + +DO_LDFF1_ZPZ_S(bsu, zsu) +DO_LDFF1_ZPZ_S(bsu, zss) +DO_LDFF1_ZPZ_D(bdu, zsu) +DO_LDFF1_ZPZ_D(bdu, zss) +DO_LDFF1_ZPZ_D(bdu, zd) + +DO_LDFF1_ZPZ_S(bss, zsu) +DO_LDFF1_ZPZ_S(bss, zss) +DO_LDFF1_ZPZ_D(bds, zsu) +DO_LDFF1_ZPZ_D(bds, zss) +DO_LDFF1_ZPZ_D(bds, zd) + +DO_LDFF1_ZPZ_S(hsu_le, zsu) +DO_LDFF1_ZPZ_S(hsu_le, zss) +DO_LDFF1_ZPZ_D(hdu_le, zsu) +DO_LDFF1_ZPZ_D(hdu_le, zss) +DO_LDFF1_ZPZ_D(hdu_le, zd) + +DO_LDFF1_ZPZ_S(hsu_be, zsu) +DO_LDFF1_ZPZ_S(hsu_be, zss) +DO_LDFF1_ZPZ_D(hdu_be, zsu) +DO_LDFF1_ZPZ_D(hdu_be, zss) +DO_LDFF1_ZPZ_D(hdu_be, zd) + +DO_LDFF1_ZPZ_S(hss_le, zsu) +DO_LDFF1_ZPZ_S(hss_le, zss) +DO_LDFF1_ZPZ_D(hds_le, zsu) +DO_LDFF1_ZPZ_D(hds_le, zss) +DO_LDFF1_ZPZ_D(hds_le, zd) + +DO_LDFF1_ZPZ_S(hss_be, zsu) +DO_LDFF1_ZPZ_S(hss_be, zss) +DO_LDFF1_ZPZ_D(hds_be, zsu) +DO_LDFF1_ZPZ_D(hds_be, zss) +DO_LDFF1_ZPZ_D(hds_be, zd) + +DO_LDFF1_ZPZ_S(ss_le, zsu) +DO_LDFF1_ZPZ_S(ss_le, zss) +DO_LDFF1_ZPZ_D(sdu_le, zsu) +DO_LDFF1_ZPZ_D(sdu_le, zss) +DO_LDFF1_ZPZ_D(sdu_le, zd) + +DO_LDFF1_ZPZ_S(ss_be, zsu) +DO_LDFF1_ZPZ_S(ss_be, zss) +DO_LDFF1_ZPZ_D(sdu_be, zsu) +DO_LDFF1_ZPZ_D(sdu_be, zss) +DO_LDFF1_ZPZ_D(sdu_be, zd) + +DO_LDFF1_ZPZ_D(sds_le, zsu) +DO_LDFF1_ZPZ_D(sds_le, zss) +DO_LDFF1_ZPZ_D(sds_le, zd) + +DO_LDFF1_ZPZ_D(sds_be, zsu) +DO_LDFF1_ZPZ_D(sds_be, zss) +DO_LDFF1_ZPZ_D(sds_be, zd) + +DO_LDFF1_ZPZ_D(dd_le, zsu) +DO_LDFF1_ZPZ_D(dd_le, zss) +DO_LDFF1_ZPZ_D(dd_le, zd) + +DO_LDFF1_ZPZ_D(dd_be, zsu) +DO_LDFF1_ZPZ_D(dd_be, zss) +DO_LDFF1_ZPZ_D(dd_be, zd) /* Stores with a vector index. */ -#define DO_ST1_ZPZ_S(NAME, TYPEI, FN) \ -void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ - target_ulong base, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc); \ - unsigned scale = simd_data(desc); \ - uintptr_t ra = GETPC(); \ - for (i = 0; i < oprsz; ) { \ - uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ - do { \ - if (likely(pg & 1)) { \ - target_ulong off = *(TYPEI *)(vm + H1_4(i)); \ - uint32_t d = *(uint32_t *)(vd + H1_4(i)); \ - FN(env, base + (off << scale), d, ra); \ - } \ - i += sizeof(uint32_t), pg >>= sizeof(uint32_t); \ - } while (i & 15); \ - } \ +static void sve_st1_zs(CPUARMState *env, void *vd, void *vg, void *vm, + target_ulong base, uint32_t desc, uintptr_t ra, + zreg_off_fn *off_fn, sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int scale = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 2); + intptr_t i, oprsz = simd_oprsz(desc); + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; ) { + uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); + do { + if (likely(pg & 1)) { + target_ulong off = off_fn(vm, i); + tlb_fn(env, vd, i, base + (off << scale), oi, ra); + } + i += 4, pg >>= 4; + } while (i & 15); + } + set_helper_retaddr(0); } -#define DO_ST1_ZPZ_D(NAME, TYPEI, FN) \ -void HELPER(NAME)(CPUARMState *env, void *vd, void *vg, void *vm, \ - target_ulong base, uint32_t desc) \ -{ \ - intptr_t i, oprsz = simd_oprsz(desc) / 8; \ - unsigned scale = simd_data(desc); \ - uintptr_t ra = GETPC(); \ - uint64_t *d = vd, *m = vm; uint8_t *pg = vg; \ - for (i = 0; i < oprsz; i++) { \ - if (likely(pg[H1(i)] & 1)) { \ - target_ulong off = (target_ulong)(TYPEI)m[i] << scale; \ - FN(env, base + off, d[i], ra); \ - } \ - } \ +static void sve_st1_zd(CPUARMState *env, void *vd, void *vg, void *vm, + target_ulong base, uint32_t desc, uintptr_t ra, + zreg_off_fn *off_fn, sve_ld1_tlb_fn *tlb_fn) +{ + const TCGMemOpIdx oi = extract32(desc, SIMD_DATA_SHIFT, MEMOPIDX_SHIFT); + const int scale = extract32(desc, SIMD_DATA_SHIFT + MEMOPIDX_SHIFT, 2); + intptr_t i, oprsz = simd_oprsz(desc) / 8; + + set_helper_retaddr(ra); + for (i = 0; i < oprsz; i++) { + uint8_t pg = *(uint8_t *)(vg + H1(i)); + if (likely(pg & 1)) { + target_ulong off = off_fn(vm, i * 8); + tlb_fn(env, vd, i * 8, base + (off << scale), oi, ra); + } + } + set_helper_retaddr(0); } -DO_ST1_ZPZ_S(sve_stbs_zsu, uint32_t, cpu_stb_data_ra) -DO_ST1_ZPZ_S(sve_sths_zsu, uint32_t, cpu_stw_data_ra) -DO_ST1_ZPZ_S(sve_stss_zsu, uint32_t, cpu_stl_data_ra) +#define DO_ST1_ZPZ_S(MEM, OFS) \ +void QEMU_FLATTEN HELPER(sve_st##MEM##_##OFS) \ + (CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + sve_st1_zs(env, vd, vg, vm, base, desc, GETPC(), \ + off_##OFS##_s, sve_st1##MEM##_tlb); \ +} -DO_ST1_ZPZ_S(sve_stbs_zss, int32_t, cpu_stb_data_ra) -DO_ST1_ZPZ_S(sve_sths_zss, int32_t, cpu_stw_data_ra) -DO_ST1_ZPZ_S(sve_stss_zss, int32_t, cpu_stl_data_ra) +#define DO_ST1_ZPZ_D(MEM, OFS) \ +void QEMU_FLATTEN HELPER(sve_st##MEM##_##OFS) \ + (CPUARMState *env, void *vd, void *vg, void *vm, \ + target_ulong base, uint32_t desc) \ +{ \ + sve_st1_zd(env, vd, vg, vm, base, desc, GETPC(), \ + off_##OFS##_d, sve_st1##MEM##_tlb); \ +} -DO_ST1_ZPZ_D(sve_stbd_zsu, uint32_t, cpu_stb_data_ra) -DO_ST1_ZPZ_D(sve_sthd_zsu, uint32_t, cpu_stw_data_ra) -DO_ST1_ZPZ_D(sve_stsd_zsu, uint32_t, cpu_stl_data_ra) -DO_ST1_ZPZ_D(sve_stdd_zsu, uint32_t, cpu_stq_data_ra) +DO_ST1_ZPZ_S(bs, zsu) +DO_ST1_ZPZ_S(hs_le, zsu) +DO_ST1_ZPZ_S(hs_be, zsu) +DO_ST1_ZPZ_S(ss_le, zsu) +DO_ST1_ZPZ_S(ss_be, zsu) -DO_ST1_ZPZ_D(sve_stbd_zss, int32_t, cpu_stb_data_ra) -DO_ST1_ZPZ_D(sve_sthd_zss, int32_t, cpu_stw_data_ra) -DO_ST1_ZPZ_D(sve_stsd_zss, int32_t, cpu_stl_data_ra) -DO_ST1_ZPZ_D(sve_stdd_zss, int32_t, cpu_stq_data_ra) +DO_ST1_ZPZ_S(bs, zss) +DO_ST1_ZPZ_S(hs_le, zss) +DO_ST1_ZPZ_S(hs_be, zss) +DO_ST1_ZPZ_S(ss_le, zss) +DO_ST1_ZPZ_S(ss_be, zss) -DO_ST1_ZPZ_D(sve_stbd_zd, uint64_t, cpu_stb_data_ra) -DO_ST1_ZPZ_D(sve_sthd_zd, uint64_t, cpu_stw_data_ra) -DO_ST1_ZPZ_D(sve_stsd_zd, uint64_t, cpu_stl_data_ra) -DO_ST1_ZPZ_D(sve_stdd_zd, uint64_t, cpu_stq_data_ra) +DO_ST1_ZPZ_D(bd, zsu) +DO_ST1_ZPZ_D(hd_le, zsu) +DO_ST1_ZPZ_D(hd_be, zsu) +DO_ST1_ZPZ_D(sd_le, zsu) +DO_ST1_ZPZ_D(sd_be, zsu) +DO_ST1_ZPZ_D(dd_le, zsu) +DO_ST1_ZPZ_D(dd_be, zsu) + +DO_ST1_ZPZ_D(bd, zss) +DO_ST1_ZPZ_D(hd_le, zss) +DO_ST1_ZPZ_D(hd_be, zss) +DO_ST1_ZPZ_D(sd_le, zss) +DO_ST1_ZPZ_D(sd_be, zss) +DO_ST1_ZPZ_D(dd_le, zss) +DO_ST1_ZPZ_D(dd_be, zss) + +DO_ST1_ZPZ_D(bd, zd) +DO_ST1_ZPZ_D(hd_le, zd) +DO_ST1_ZPZ_D(hd_be, zd) +DO_ST1_ZPZ_D(sd_le, zd) +DO_ST1_ZPZ_D(sd_be, zd) +DO_ST1_ZPZ_D(dd_le, zd) +DO_ST1_ZPZ_D(dd_be, zd) + +#undef DO_ST1_ZPZ_S +#undef DO_ST1_ZPZ_D diff --git a/target/arm/trace-events b/target/arm/trace-events index 6b759f9d4f..41c63d7570 100644 --- a/target/arm/trace-events +++ b/target/arm/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# target/arm/helper.c +# helper.c arm_gt_recalc(int timer, int irqstate, uint64_t nexttick) "gt recalc: timer %d irqstate %d next tick 0x%" PRIx64 arm_gt_recalc_disabled(int timer) "gt recalc: timer %d irqstate 0 timer disabled" arm_gt_cval_write(int timer, uint64_t value) "gt_cval_write: timer %d value 0x%" PRIx64 @@ -9,5 +9,5 @@ arm_gt_ctl_write(int timer, uint64_t value) "gt_ctl_write: timer %d value 0x%" P arm_gt_imask_toggle(int timer, int irqstate) "gt_ctl_write: timer %d IMASK toggle, new irqstate %d" arm_gt_cntvoff_write(uint64_t value) "gt_cntvoff_write: value 0x%" PRIx64 -# target/arm/kvm.c +# kvm.c kvm_arm_fixup_msi_route(uint64_t iova, uint64_t gpa) "MSI iova = 0x%"PRIx64" is translated into 0x%"PRIx64 diff --git a/target/arm/translate-a64.c b/target/arm/translate-a64.c index 45a6c2a3aa..dcdeb80176 100644 --- a/target/arm/translate-a64.c +++ b/target/arm/translate-a64.c @@ -37,6 +37,7 @@ #include "trace-tcg.h" #include "translate-a64.h" +#include "qemu/atomic128.h" static TCGv_i64 cpu_X[32]; static TCGv_i64 cpu_pc; @@ -127,6 +128,29 @@ static inline int get_a64_user_mem_index(DisasContext *s) return arm_to_core_mmu_idx(useridx); } +static void reset_btype(DisasContext *s) +{ + if (s->btype != 0) { + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_st_i32(zero, cpu_env, offsetof(CPUARMState, btype)); + tcg_temp_free_i32(zero); + s->btype = 0; + } +} + +static void set_btype(DisasContext *s, int val) +{ + TCGv_i32 tcg_val; + + /* BTYPE is a 2-bit field, and 0 should be done with reset_btype. */ + tcg_debug_assert(val >= 1 && val <= 3); + + tcg_val = tcg_const_i32(val); + tcg_gen_st_i32(tcg_val, cpu_env, offsetof(CPUARMState, btype)); + tcg_temp_free_i32(tcg_val); + s->btype = -1; +} + void aarch64_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -137,14 +161,13 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, 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]); - for (i = 0; i < 31; i++) { - cpu_fprintf(f, "X%02d=%016"PRIx64, i, env->xregs[i]); - if ((i % 4) == 3) { - cpu_fprintf(f, "\n"); + cpu_fprintf(f, " PC=%016" PRIx64 " ", env->pc); + for (i = 0; i < 32; i++) { + if (i == 31) { + cpu_fprintf(f, " SP=%016" PRIx64 "\n", env->xregs[i]); } else { - cpu_fprintf(f, " "); + cpu_fprintf(f, "X%02d=%016" PRIx64 "%s", i, env->xregs[i], + (i + 2) % 3 ? " " : "\n"); } } @@ -153,8 +176,7 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, } else { ns_status = ""; } - - cpu_fprintf(f, "\nPSTATE=%08x %c%c%c%c %sEL%d%c\n", + cpu_fprintf(f, "PSTATE=%08x %c%c%c%c %sEL%d%c", psr, psr & PSTATE_N ? 'N' : '-', psr & PSTATE_Z ? 'Z' : '-', @@ -164,17 +186,96 @@ void aarch64_cpu_dump_state(CPUState *cs, FILE *f, el, psr & PSTATE_SP ? 'h' : 't'); - if (flags & CPU_DUMP_FPU) { - int numvfpregs = 32; - for (i = 0; i < numvfpregs; i++) { - uint64_t *q = aa64_vfp_qreg(env, i); - uint64_t vlo = q[0]; - uint64_t vhi = q[1]; - cpu_fprintf(f, "q%02d=%016" PRIx64 ":%016" PRIx64 "%c", - i, vhi, vlo, (i & 1 ? '\n' : ' ')); + if (cpu_isar_feature(aa64_bti, cpu)) { + cpu_fprintf(f, " BTYPE=%d", (psr & PSTATE_BTYPE) >> 10); + } + if (!(flags & CPU_DUMP_FPU)) { + cpu_fprintf(f, "\n"); + return; + } + if (fp_exception_el(env, el) != 0) { + cpu_fprintf(f, " FPU disabled\n"); + return; + } + cpu_fprintf(f, " FPCR=%08x FPSR=%08x\n", + vfp_get_fpcr(env), vfp_get_fpsr(env)); + + if (cpu_isar_feature(aa64_sve, cpu) && sve_exception_el(env, el) == 0) { + int j, zcr_len = sve_zcr_len_for_el(env, el); + + for (i = 0; i <= FFR_PRED_NUM; i++) { + bool eol; + if (i == FFR_PRED_NUM) { + cpu_fprintf(f, "FFR="); + /* It's last, so end the line. */ + eol = true; + } else { + cpu_fprintf(f, "P%02d=", i); + switch (zcr_len) { + case 0: + eol = i % 8 == 7; + break; + case 1: + eol = i % 6 == 5; + break; + case 2: + case 3: + eol = i % 3 == 2; + break; + default: + /* More than one quadword per predicate. */ + eol = true; + break; + } + } + for (j = zcr_len / 4; j >= 0; j--) { + int digits; + if (j * 4 + 4 <= zcr_len + 1) { + digits = 16; + } else { + digits = (zcr_len % 4 + 1) * 4; + } + cpu_fprintf(f, "%0*" PRIx64 "%s", digits, + env->vfp.pregs[i].p[j], + j ? ":" : eol ? "\n" : " "); + } + } + + for (i = 0; i < 32; i++) { + if (zcr_len == 0) { + cpu_fprintf(f, "Z%02d=%016" PRIx64 ":%016" PRIx64 "%s", + i, env->vfp.zregs[i].d[1], + env->vfp.zregs[i].d[0], i & 1 ? "\n" : " "); + } else if (zcr_len == 1) { + cpu_fprintf(f, "Z%02d=%016" PRIx64 ":%016" PRIx64 + ":%016" PRIx64 ":%016" PRIx64 "\n", + i, env->vfp.zregs[i].d[3], env->vfp.zregs[i].d[2], + env->vfp.zregs[i].d[1], env->vfp.zregs[i].d[0]); + } else { + for (j = zcr_len; j >= 0; j--) { + bool odd = (zcr_len - j) % 2 != 0; + if (j == zcr_len) { + cpu_fprintf(f, "Z%02d[%x-%x]=", i, j, j - 1); + } else if (!odd) { + if (j > 0) { + cpu_fprintf(f, " [%x-%x]=", j, j - 1); + } else { + cpu_fprintf(f, " [%x]=", j); + } + } + cpu_fprintf(f, "%016" PRIx64 ":%016" PRIx64 "%s", + env->vfp.zregs[i].d[j * 2 + 1], + env->vfp.zregs[i].d[j * 2], + odd || j == 0 ? "\n" : ":"); + } + } + } + } else { + for (i = 0; i < 32; i++) { + uint64_t *q = aa64_vfp_qreg(env, i); + cpu_fprintf(f, "Q%02d=%016" PRIx64 ":%016" PRIx64 "%s", + i, q[1], q[0], (i & 1 ? "\n" : " ")); } - cpu_fprintf(f, "FPCR: %08x FPSR: %08x\n", - vfp_get_fpcr(env), vfp_get_fpsr(env)); } } @@ -183,10 +284,10 @@ void gen_a64_set_pc_im(uint64_t val) tcg_gen_movi_i64(cpu_pc, val); } -/* Load the PC from a generic TCG variable. +/* + * Handle Top Byte Ignore (TBI) bits. * - * If address tagging is enabled via the TCR TBI bits, then loading - * an address into the PC will clear out any tag in the it: + * If address tagging is enabled via the TCR TBI bits: * + for EL2 and EL3 there is only one TBI bit, and if it is set * then the address is zero-extended, clearing bits [63:56] * + for EL0 and EL1, TBI0 controls addresses with bit 55 == 0 @@ -194,65 +295,58 @@ void gen_a64_set_pc_im(uint64_t val) * If the appropriate TBI bit is set for the address then * the address is sign-extended from bit 55 into bits [63:56] * - * We can avoid doing this for relative-branches, because the - * PC + offset can never overflow into the tag bits (assuming - * that virtual addresses are less than 56 bits wide, as they - * are currently), but we must handle it for branch-to-register. + * Here We have concatenated TBI{1,0} into tbi. */ -static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) +static void gen_top_byte_ignore(DisasContext *s, TCGv_i64 dst, + TCGv_i64 src, int tbi) { + if (tbi == 0) { + /* Load unmodified address */ + tcg_gen_mov_i64(dst, src); + } else if (s->current_el >= 2) { + /* FIXME: ARMv8.1-VHE S2 translation regime. */ + /* Force tag byte to all zero */ + tcg_gen_extract_i64(dst, src, 0, 56); + } else { + /* Sign-extend from bit 55. */ + tcg_gen_sextract_i64(dst, src, 0, 56); - if (s->current_el <= 1) { - /* Test if NEITHER or BOTH TBI values are set. If so, no need to - * examine bit 55 of address, can just generate code. - * If mixed, then test via generated code - */ - if (s->tbi0 && s->tbi1) { - TCGv_i64 tmp_reg = tcg_temp_new_i64(); - /* Both bits set, sign extension from bit 55 into [63:56] will - * cover both cases + if (tbi != 3) { + TCGv_i64 tcg_zero = tcg_const_i64(0); + + /* + * The two TBI bits differ. + * If tbi0, then !tbi1: only use the extension if positive. + * if !tbi0, then tbi1: only use the extension if negative. */ - tcg_gen_shli_i64(tmp_reg, src, 8); - tcg_gen_sari_i64(cpu_pc, tmp_reg, 8); - tcg_temp_free_i64(tmp_reg); - } else if (!s->tbi0 && !s->tbi1) { - /* Neither bit set, just load it as-is */ - tcg_gen_mov_i64(cpu_pc, src); - } else { - TCGv_i64 tcg_tmpval = tcg_temp_new_i64(); - TCGv_i64 tcg_bit55 = tcg_temp_new_i64(); - TCGv_i64 tcg_zero = tcg_const_i64(0); - - tcg_gen_andi_i64(tcg_bit55, src, (1ull << 55)); - - if (s->tbi0) { - /* tbi0==1, tbi1==0, so 0-fill upper byte if bit 55 = 0 */ - tcg_gen_andi_i64(tcg_tmpval, src, - 0x00FFFFFFFFFFFFFFull); - tcg_gen_movcond_i64(TCG_COND_EQ, cpu_pc, tcg_bit55, tcg_zero, - tcg_tmpval, src); - } else { - /* tbi0==0, tbi1==1, so 1-fill upper byte if bit 55 = 1 */ - tcg_gen_ori_i64(tcg_tmpval, src, - 0xFF00000000000000ull); - tcg_gen_movcond_i64(TCG_COND_NE, cpu_pc, tcg_bit55, tcg_zero, - tcg_tmpval, src); - } + tcg_gen_movcond_i64(tbi == 1 ? TCG_COND_GE : TCG_COND_LT, + dst, dst, tcg_zero, dst, src); tcg_temp_free_i64(tcg_zero); - tcg_temp_free_i64(tcg_bit55); - tcg_temp_free_i64(tcg_tmpval); - } - } else { /* EL > 1 */ - if (s->tbi0) { - /* Force tag byte to all zero */ - tcg_gen_andi_i64(cpu_pc, src, 0x00FFFFFFFFFFFFFFull); - } else { - /* Load unmodified address */ - tcg_gen_mov_i64(cpu_pc, src); } } } +static void gen_a64_set_pc(DisasContext *s, TCGv_i64 src) +{ + /* + * If address tagging is enabled for instructions via the TCR TBI bits, + * then loading an address into the PC will clear out any tag. + */ + gen_top_byte_ignore(s, cpu_pc, src, s->tbii); +} + +/* + * Return a "clean" address for ADDR according to TBID. + * This is always a fresh temporary, as we need to be able to + * increment this independently of a dirty write-back address. + */ +static TCGv_i64 clean_data_tbi(DisasContext *s, TCGv_i64 addr) +{ + TCGv_i64 clean = new_tmp_a64(s); + gen_top_byte_ignore(s, clean, addr, s->tbid); + return clean; +} + typedef struct DisasCompare64 { TCGCond cond; TCGv_i64 value; @@ -327,17 +421,6 @@ static void gen_exception_bkpt_insn(DisasContext *s, int offset, s->base.is_jmp = DISAS_NORETURN; } -static void gen_ss_advance(DisasContext *s) -{ - /* If the singlestep state is Active-not-pending, advance to - * Active-pending. - */ - if (s->ss_active) { - s->pstate_ss = 0; - gen_helper_clear_pstate_ss(cpu_env); - } -} - static void gen_step_complete_exception(DisasContext *s) { /* We just completed step of an insn. Move from Active-not-pending @@ -1126,25 +1209,23 @@ static void write_vec_element_i32(DisasContext *s, TCGv_i32 tcg_src, /* Store from vector register to memory */ static void do_vec_st(DisasContext *s, int srcidx, int element, - TCGv_i64 tcg_addr, int size) + TCGv_i64 tcg_addr, int size, TCGMemOp endian) { - TCGMemOp memop = s->be_data + size; TCGv_i64 tcg_tmp = tcg_temp_new_i64(); read_vec_element(s, tcg_tmp, srcidx, element, size); - tcg_gen_qemu_st_i64(tcg_tmp, tcg_addr, get_mem_index(s), memop); + tcg_gen_qemu_st_i64(tcg_tmp, tcg_addr, get_mem_index(s), endian | size); tcg_temp_free_i64(tcg_tmp); } /* Load from memory to vector register */ static void do_vec_ld(DisasContext *s, int destidx, int element, - TCGv_i64 tcg_addr, int size) + TCGv_i64 tcg_addr, int size, TCGMemOp endian) { - 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); + tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, get_mem_index(s), endian | size); write_vec_element(s, tcg_tmp, destidx, element, size); tcg_temp_free_i64(tcg_tmp); @@ -1294,6 +1375,7 @@ static void disas_uncond_b_imm(DisasContext *s, uint32_t insn) } /* B Branch / BL Branch with link */ + reset_btype(s); gen_goto_tb(s, 0, addr); } @@ -1318,6 +1400,7 @@ static void disas_comp_b_imm(DisasContext *s, uint32_t insn) tcg_cmp = read_cpu_reg(s, rt, sf); label_match = gen_new_label(); + reset_btype(s); tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, tcg_cmp, 0, label_match); @@ -1347,6 +1430,8 @@ static void disas_test_b_imm(DisasContext *s, uint32_t insn) tcg_cmp = tcg_temp_new_i64(); tcg_gen_andi_i64(tcg_cmp, cpu_reg(s, rt), (1ULL << bit_pos)); label_match = gen_new_label(); + + reset_btype(s); tcg_gen_brcondi_i64(op ? TCG_COND_NE : TCG_COND_EQ, tcg_cmp, 0, label_match); tcg_temp_free_i64(tcg_cmp); @@ -1373,6 +1458,7 @@ static void disas_cond_b_imm(DisasContext *s, uint32_t insn) addr = s->pc + sextract32(insn, 5, 19) * 4 - 4; cond = extract32(insn, 0, 4); + reset_btype(s); if (cond < 0x0e) { /* genuinely conditional branches */ TCGLabel *label_match = gen_new_label(); @@ -1398,33 +1484,102 @@ static void handle_hint(DisasContext *s, uint32_t insn, } switch (selector) { - case 0: /* NOP */ - return; - case 3: /* WFI */ + case 0b00000: /* NOP */ + break; + case 0b00011: /* WFI */ s->base.is_jmp = DISAS_WFI; - return; + break; + case 0b00001: /* YIELD */ /* When running in MTTCG we don't generate jumps to the yield and * WFE helpers as it won't affect the scheduling of other vCPUs. * If we wanted to more completely model WFE/SEV so we don't busy * spin unnecessarily we would need to do something more involved. */ - case 1: /* YIELD */ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { s->base.is_jmp = DISAS_YIELD; } - return; - case 2: /* WFE */ + break; + case 0b00010: /* WFE */ if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { s->base.is_jmp = DISAS_WFE; } - return; - case 4: /* SEV */ - case 5: /* SEVL */ + break; + case 0b00100: /* SEV */ + case 0b00101: /* SEVL */ /* we treat all as NOP at least for now */ - return; + break; + case 0b00111: /* XPACLRI */ + if (s->pauth_active) { + gen_helper_xpaci(cpu_X[30], cpu_env, cpu_X[30]); + } + break; + case 0b01000: /* PACIA1716 */ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + break; + case 0b01010: /* PACIB1716 */ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + break; + case 0b01100: /* AUTIA1716 */ + if (s->pauth_active) { + gen_helper_autia(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + break; + case 0b01110: /* AUTIB1716 */ + if (s->pauth_active) { + gen_helper_autib(cpu_X[17], cpu_env, cpu_X[17], cpu_X[16]); + } + break; + case 0b11000: /* PACIAZ */ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], + new_tmp_a64_zero(s)); + } + break; + case 0b11001: /* PACIASP */ + if (s->pauth_active) { + gen_helper_pacia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + break; + case 0b11010: /* PACIBZ */ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], + new_tmp_a64_zero(s)); + } + break; + case 0b11011: /* PACIBSP */ + if (s->pauth_active) { + gen_helper_pacib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + break; + case 0b11100: /* AUTIAZ */ + if (s->pauth_active) { + gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], + new_tmp_a64_zero(s)); + } + break; + case 0b11101: /* AUTIASP */ + if (s->pauth_active) { + gen_helper_autia(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + break; + case 0b11110: /* AUTIBZ */ + if (s->pauth_active) { + gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], + new_tmp_a64_zero(s)); + } + break; + case 0b11111: /* AUTIBSP */ + if (s->pauth_active) { + gen_helper_autib(cpu_X[30], cpu_env, cpu_X[30], cpu_X[31]); + } + break; default: /* default specified as NOP equivalent */ - return; + break; } } @@ -1468,41 +1623,131 @@ static void handle_sync(DisasContext *s, uint32_t insn, * a self-modified code correctly and also to take * any pending interrupts immediately. */ + reset_btype(s); gen_goto_tb(s, 0, s->pc); return; + + case 7: /* SB */ + if (crm != 0 || !dc_isar_feature(aa64_sb, s)) { + goto do_unallocated; + } + /* + * TODO: There is no speculation barrier opcode for TCG; + * MB and end the TB instead. + */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + gen_goto_tb(s, 0, s->pc); + return; + default: + do_unallocated: unallocated_encoding(s); return; } } +static void gen_xaflag(void) +{ + TCGv_i32 z = tcg_temp_new_i32(); + + tcg_gen_setcondi_i32(TCG_COND_EQ, z, cpu_ZF, 0); + + /* + * (!C & !Z) << 31 + * (!(C | Z)) << 31 + * ~((C | Z) << 31) + * ~-(C | Z) + * (C | Z) - 1 + */ + tcg_gen_or_i32(cpu_NF, cpu_CF, z); + tcg_gen_subi_i32(cpu_NF, cpu_NF, 1); + + /* !(Z & C) */ + tcg_gen_and_i32(cpu_ZF, z, cpu_CF); + tcg_gen_xori_i32(cpu_ZF, cpu_ZF, 1); + + /* (!C & Z) << 31 -> -(Z & ~C) */ + tcg_gen_andc_i32(cpu_VF, z, cpu_CF); + tcg_gen_neg_i32(cpu_VF, cpu_VF); + + /* C | Z */ + tcg_gen_or_i32(cpu_CF, cpu_CF, z); + + tcg_temp_free_i32(z); +} + +static void gen_axflag(void) +{ + tcg_gen_sari_i32(cpu_VF, cpu_VF, 31); /* V ? -1 : 0 */ + tcg_gen_andc_i32(cpu_CF, cpu_CF, cpu_VF); /* C & !V */ + + /* !(Z | V) -> !(!ZF | V) -> ZF & !V -> ZF & ~VF */ + tcg_gen_andc_i32(cpu_ZF, cpu_ZF, cpu_VF); + + tcg_gen_movi_i32(cpu_NF, 0); + tcg_gen_movi_i32(cpu_VF, 0); +} + /* MSR (immediate) - move immediate to processor state field */ static void handle_msr_i(DisasContext *s, uint32_t insn, unsigned int op1, unsigned int op2, unsigned int crm) { + TCGv_i32 t1; int op = op1 << 3 | op2; + + /* End the TB by default, chaining is ok. */ + s->base.is_jmp = DISAS_TOO_MANY; + switch (op) { + case 0x00: /* CFINV */ + if (crm != 0 || !dc_isar_feature(aa64_condm_4, s)) { + goto do_unallocated; + } + tcg_gen_xori_i32(cpu_CF, cpu_CF, 1); + s->base.is_jmp = DISAS_NEXT; + break; + + case 0x01: /* XAFlag */ + if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) { + goto do_unallocated; + } + gen_xaflag(); + s->base.is_jmp = DISAS_NEXT; + break; + + case 0x02: /* AXFlag */ + if (crm != 0 || !dc_isar_feature(aa64_condm_5, s)) { + goto do_unallocated; + } + gen_axflag(); + s->base.is_jmp = DISAS_NEXT; + break; + case 0x05: /* SPSel */ if (s->current_el == 0) { - unallocated_encoding(s); - return; + goto do_unallocated; } - /* fall through */ - case 0x1e: /* DAIFSet */ - case 0x1f: /* DAIFClear */ - { - TCGv_i32 tcg_imm = tcg_const_i32(crm); - TCGv_i32 tcg_op = tcg_const_i32(op); - gen_a64_set_pc_im(s->pc - 4); - gen_helper_msr_i_pstate(cpu_env, tcg_op, tcg_imm); - tcg_temp_free_i32(tcg_imm); - tcg_temp_free_i32(tcg_op); - /* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs. */ - gen_a64_set_pc_im(s->pc); - s->base.is_jmp = (op == 0x1f ? DISAS_EXIT : DISAS_JUMP); + t1 = tcg_const_i32(crm & PSTATE_SP); + gen_helper_msr_i_spsel(cpu_env, t1); + tcg_temp_free_i32(t1); break; - } + + case 0x1e: /* DAIFSet */ + t1 = tcg_const_i32(crm); + gen_helper_msr_i_daifset(cpu_env, t1); + tcg_temp_free_i32(t1); + break; + + case 0x1f: /* DAIFClear */ + t1 = tcg_const_i32(crm); + gen_helper_msr_i_daifclear(cpu_env, t1); + tcg_temp_free_i32(t1); + /* For DAIFClear, exit the cpu loop to re-evaluate pending IRQs. */ + s->base.is_jmp = DISAS_UPDATE; + break; + default: + do_unallocated: unallocated_encoding(s); return; } @@ -1531,7 +1776,6 @@ static void gen_get_nzcv(TCGv_i64 tcg_rt) } static void gen_set_nzcv(TCGv_i64 tcg_rt) - { TCGv_i32 nzcv = tcg_temp_new_i32(); @@ -1839,6 +2083,9 @@ static void disas_exc(DisasContext *s, uint32_t insn) static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) { unsigned int opc, op2, op3, rn, op4; + unsigned btype_mod = 2; /* 0: BR, 1: BLR, 2: other */ + TCGv_i64 dst; + TCGv_i64 modifier; opc = extract32(insn, 21, 4); op2 = extract32(insn, 16, 5); @@ -1846,48 +2093,177 @@ static void disas_uncond_b_reg(DisasContext *s, uint32_t insn) rn = extract32(insn, 5, 5); op4 = extract32(insn, 0, 5); - if (op4 != 0x0 || op3 != 0x0 || op2 != 0x1f) { - unallocated_encoding(s); - return; + if (op2 != 0x1f) { + goto do_unallocated; } switch (opc) { case 0: /* BR */ case 1: /* BLR */ case 2: /* RET */ - gen_a64_set_pc(s, cpu_reg(s, rn)); + btype_mod = opc; + switch (op3) { + case 0: + /* BR, BLR, RET */ + if (op4 != 0) { + goto do_unallocated; + } + dst = cpu_reg(s, rn); + break; + + case 2: + case 3: + if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + if (opc == 2) { + /* RETAA, RETAB */ + if (rn != 0x1f || op4 != 0x1f) { + goto do_unallocated; + } + rn = 30; + modifier = cpu_X[31]; + } else { + /* BRAAZ, BRABZ, BLRAAZ, BLRABZ */ + if (op4 != 0x1f) { + goto do_unallocated; + } + modifier = new_tmp_a64_zero(s); + } + if (s->pauth_active) { + dst = new_tmp_a64(s); + if (op3 == 2) { + gen_helper_autia(dst, cpu_env, cpu_reg(s, rn), modifier); + } else { + gen_helper_autib(dst, cpu_env, cpu_reg(s, rn), modifier); + } + } else { + dst = cpu_reg(s, rn); + } + break; + + default: + goto do_unallocated; + } + gen_a64_set_pc(s, dst); /* BLR also needs to load return address */ if (opc == 1) { tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); } break; + + case 8: /* BRAA */ + case 9: /* BLRAA */ + if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + if ((op3 & ~1) != 2) { + goto do_unallocated; + } + btype_mod = opc & 1; + if (s->pauth_active) { + dst = new_tmp_a64(s); + modifier = cpu_reg_sp(s, op4); + if (op3 == 2) { + gen_helper_autia(dst, cpu_env, cpu_reg(s, rn), modifier); + } else { + gen_helper_autib(dst, cpu_env, cpu_reg(s, rn), modifier); + } + } else { + dst = cpu_reg(s, rn); + } + gen_a64_set_pc(s, dst); + /* BLRAA also needs to load return address */ + if (opc == 9) { + tcg_gen_movi_i64(cpu_reg(s, 30), s->pc); + } + break; + case 4: /* ERET */ if (s->current_el == 0) { - unallocated_encoding(s); - return; + goto do_unallocated; + } + switch (op3) { + case 0: /* ERET */ + if (op4 != 0) { + goto do_unallocated; + } + dst = tcg_temp_new_i64(); + tcg_gen_ld_i64(dst, cpu_env, + offsetof(CPUARMState, elr_el[s->current_el])); + break; + + case 2: /* ERETAA */ + case 3: /* ERETAB */ + if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + if (rn != 0x1f || op4 != 0x1f) { + goto do_unallocated; + } + dst = tcg_temp_new_i64(); + tcg_gen_ld_i64(dst, cpu_env, + offsetof(CPUARMState, elr_el[s->current_el])); + if (s->pauth_active) { + modifier = cpu_X[31]; + if (op3 == 2) { + gen_helper_autia(dst, cpu_env, dst, modifier); + } else { + gen_helper_autib(dst, cpu_env, dst, modifier); + } + } + break; + + default: + goto do_unallocated; } if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } - gen_helper_exception_return(cpu_env); + + gen_helper_exception_return(cpu_env, dst); + tcg_temp_free_i64(dst); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } /* Must exit loop to check un-masked IRQs */ s->base.is_jmp = DISAS_EXIT; return; + case 5: /* DRPS */ - if (rn != 0x1f) { - unallocated_encoding(s); + if (op3 != 0 || op4 != 0 || rn != 0x1f) { + goto do_unallocated; } else { unsupported_encoding(s, insn); } return; + default: + do_unallocated: unallocated_encoding(s); return; } + switch (btype_mod) { + case 0: /* BR */ + if (dc_isar_feature(aa64_bti, s)) { + /* BR to {x16,x17} or !guard -> 1, else 3. */ + set_btype(s, rn == 16 || rn == 17 || !s->guarded_page ? 1 : 3); + } + break; + + case 1: /* BLR */ + if (dc_isar_feature(aa64_bti, s)) { + /* BLR sets BTYPE to 2, regardless of source guarded page. */ + set_btype(s, 2); + } + break; + + default: /* RET or none of the above. */ + /* BTYPE will be set to 0 by normal end-of-insn processing. */ + break; + } + s->base.is_jmp = DISAS_JUMP; } @@ -1910,7 +2286,11 @@ static void disas_b_exc_sys(DisasContext *s, uint32_t insn) break; case 0x6a: /* Exception generation / System */ if (insn & (1 << 24)) { - disas_system(s, insn); + if (extract32(insn, 22, 2) == 0) { + disas_system(s, insn); + } else { + unallocated_encoding(s); + } } else { disas_exc(s, insn); } @@ -2012,26 +2392,27 @@ static void gen_store_exclusive(DisasContext *s, int rd, int rt, int rt2, get_mem_index(s), MO_64 | MO_ALIGN | s->be_data); tcg_gen_setcond_i64(TCG_COND_NE, tmp, tmp, cpu_exclusive_val); - } else if (s->be_data == MO_LE) { - if (tb_cflags(s->base.tb) & CF_PARALLEL) { + } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { + if (!HAVE_CMPXCHG128) { + gen_helper_exit_atomic(cpu_env); + s->base.is_jmp = DISAS_NORETURN; + } else if (s->be_data == MO_LE) { gen_helper_paired_cmpxchg64_le_parallel(tmp, cpu_env, cpu_exclusive_addr, cpu_reg(s, rt), cpu_reg(s, rt2)); } else { - gen_helper_paired_cmpxchg64_le(tmp, cpu_env, cpu_exclusive_addr, - cpu_reg(s, rt), cpu_reg(s, rt2)); - } - } else { - if (tb_cflags(s->base.tb) & CF_PARALLEL) { gen_helper_paired_cmpxchg64_be_parallel(tmp, cpu_env, cpu_exclusive_addr, cpu_reg(s, rt), cpu_reg(s, rt2)); - } else { - gen_helper_paired_cmpxchg64_be(tmp, cpu_env, cpu_exclusive_addr, - cpu_reg(s, rt), cpu_reg(s, rt2)); } + } else if (s->be_data == MO_LE) { + gen_helper_paired_cmpxchg64_le(tmp, cpu_env, cpu_exclusive_addr, + cpu_reg(s, rt), cpu_reg(s, rt2)); + } else { + gen_helper_paired_cmpxchg64_be(tmp, cpu_env, cpu_exclusive_addr, + cpu_reg(s, rt), cpu_reg(s, rt2)); } } else { tcg_gen_atomic_cmpxchg_i64(tmp, cpu_exclusive_addr, cpu_exclusive_val, @@ -2055,12 +2436,13 @@ static void gen_compare_and_swap(DisasContext *s, int rs, int rt, TCGv_i64 tcg_rs = cpu_reg(s, rs); TCGv_i64 tcg_rt = cpu_reg(s, rt); int memidx = get_mem_index(s); - TCGv_i64 addr = cpu_reg_sp(s, rn); + TCGv_i64 clean_addr; if (rn == 31) { gen_check_sp_alignment(s); } - tcg_gen_atomic_cmpxchg_i64(tcg_rs, addr, tcg_rs, tcg_rt, memidx, + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + tcg_gen_atomic_cmpxchg_i64(tcg_rs, clean_addr, tcg_rs, tcg_rt, memidx, size | MO_ALIGN | s->be_data); } @@ -2071,12 +2453,13 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, TCGv_i64 s2 = cpu_reg(s, rs + 1); TCGv_i64 t1 = cpu_reg(s, rt); TCGv_i64 t2 = cpu_reg(s, rt + 1); - TCGv_i64 addr = cpu_reg_sp(s, rn); + TCGv_i64 clean_addr; int memidx = get_mem_index(s); if (rn == 31) { gen_check_sp_alignment(s); } + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); if (size == 2) { TCGv_i64 cmp = tcg_temp_new_i64(); @@ -2090,7 +2473,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, tcg_gen_concat32_i64(cmp, s2, s1); } - tcg_gen_atomic_cmpxchg_i64(cmp, addr, cmp, val, memidx, + tcg_gen_atomic_cmpxchg_i64(cmp, clean_addr, cmp, val, memidx, MO_64 | MO_ALIGN | s->be_data); tcg_temp_free_i64(val); @@ -2101,14 +2484,20 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, } tcg_temp_free_i64(cmp); } else if (tb_cflags(s->base.tb) & CF_PARALLEL) { - TCGv_i32 tcg_rs = tcg_const_i32(rs); - - if (s->be_data == MO_LE) { - gen_helper_casp_le_parallel(cpu_env, tcg_rs, addr, t1, t2); + if (HAVE_CMPXCHG128) { + TCGv_i32 tcg_rs = tcg_const_i32(rs); + if (s->be_data == MO_LE) { + gen_helper_casp_le_parallel(cpu_env, tcg_rs, + clean_addr, t1, t2); + } else { + gen_helper_casp_be_parallel(cpu_env, tcg_rs, + clean_addr, t1, t2); + } + tcg_temp_free_i32(tcg_rs); } else { - gen_helper_casp_be_parallel(cpu_env, tcg_rs, addr, t1, t2); + gen_helper_exit_atomic(cpu_env); + s->base.is_jmp = DISAS_NORETURN; } - tcg_temp_free_i32(tcg_rs); } else { TCGv_i64 d1 = tcg_temp_new_i64(); TCGv_i64 d2 = tcg_temp_new_i64(); @@ -2118,10 +2507,10 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, TCGv_i64 zero = tcg_const_i64(0); /* Load the two words, in memory order. */ - tcg_gen_qemu_ld_i64(d1, addr, memidx, + tcg_gen_qemu_ld_i64(d1, clean_addr, memidx, MO_64 | MO_ALIGN_16 | s->be_data); - tcg_gen_addi_i64(a2, addr, 8); - tcg_gen_qemu_ld_i64(d2, addr, memidx, MO_64 | s->be_data); + tcg_gen_addi_i64(a2, clean_addr, 8); + tcg_gen_qemu_ld_i64(d2, a2, memidx, MO_64 | s->be_data); /* Compare the two words, also in memory order. */ tcg_gen_setcond_i64(TCG_COND_EQ, c1, d1, s1); @@ -2131,7 +2520,7 @@ static void gen_compare_and_swap_pair(DisasContext *s, int rs, int rt, /* If compare equal, write back new data, else write back old data. */ tcg_gen_movcond_i64(TCG_COND_NE, c1, c2, zero, t1, d1); tcg_gen_movcond_i64(TCG_COND_NE, c2, c2, zero, t2, d2); - tcg_gen_qemu_st_i64(c1, addr, memidx, MO_64 | s->be_data); + tcg_gen_qemu_st_i64(c1, clean_addr, memidx, MO_64 | s->be_data); tcg_gen_qemu_st_i64(c2, a2, memidx, MO_64 | s->be_data); tcg_temp_free_i64(a2); tcg_temp_free_i64(c1); @@ -2184,7 +2573,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) int is_lasr = extract32(insn, 15, 1); int o2_L_o1_o0 = extract32(insn, 21, 3) * 2 | is_lasr; int size = extract32(insn, 30, 2); - TCGv_i64 tcg_addr; + TCGv_i64 clean_addr; switch (o2_L_o1_o0) { case 0x0: /* STXR */ @@ -2195,8 +2584,8 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); - gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, false); + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + gen_store_exclusive(s, rs, rt, rt2, clean_addr, size, false); return; case 0x4: /* LDXR */ @@ -2204,32 +2593,44 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (rn == 31) { gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, tcg_addr, size, false); + gen_load_exclusive(s, rt, rt2, clean_addr, size, false); if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } return; + case 0x8: /* STLLR */ + if (!dc_isar_feature(aa64_lor, s)) { + break; + } + /* StoreLORelease is the same as Store-Release for QEMU. */ + /* fall through */ case 0x9: /* STLR */ /* Generate ISS for non-exclusive accesses including LASR. */ if (rn == 31) { gen_check_sp_alignment(s); } tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - tcg_addr = read_cpu_reg_sp(s, rn, 1); - do_gpr_st(s, cpu_reg(s, rt), tcg_addr, size, true, rt, + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + do_gpr_st(s, cpu_reg(s, rt), clean_addr, size, true, rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); return; + case 0xc: /* LDLAR */ + if (!dc_isar_feature(aa64_lor, s)) { + break; + } + /* LoadLOAcquire is the same as Load-Acquire for QEMU. */ + /* fall through */ case 0xd: /* LDAR */ /* Generate ISS for non-exclusive accesses including LASR. */ if (rn == 31) { gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); - do_gpr_ld(s, cpu_reg(s, rt), tcg_addr, size, false, false, true, rt, + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + do_gpr_ld(s, cpu_reg(s, rt), clean_addr, size, false, false, true, rt, disas_ldst_compute_iss_sf(size, false, 0), is_lasr); tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); return; @@ -2242,13 +2643,13 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); - gen_store_exclusive(s, rs, rt, rt2, tcg_addr, size, true); + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); + gen_store_exclusive(s, rs, rt, rt2, clean_addr, size, true); return; } if (rt2 == 31 && ((rt | rs) & 1) == 0 - && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + && dc_isar_feature(aa64_atomics, s)) { /* CASP / CASPL */ gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); return; @@ -2260,9 +2661,9 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) if (rn == 31) { gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); s->is_ldex = true; - gen_load_exclusive(s, rt, rt2, tcg_addr, size, true); + gen_load_exclusive(s, rt, rt2, clean_addr, size, true); if (is_lasr) { tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); } @@ -2270,7 +2671,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) } if (rt2 == 31 && ((rt | rs) & 1) == 0 - && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + && dc_isar_feature(aa64_atomics, s)) { /* CASPA / CASPAL */ gen_compare_and_swap_pair(s, rs, rt, rn, size | 2); return; @@ -2281,7 +2682,7 @@ static void disas_ldst_excl(DisasContext *s, uint32_t insn) case 0xb: /* CASL */ case 0xe: /* CASA */ case 0xf: /* CASAL */ - if (rt2 == 31 && arm_dc_feature(s, ARM_FEATURE_V8_ATOMICS)) { + if (rt2 == 31 && dc_isar_feature(aa64_atomics, s)) { gen_compare_and_swap(s, rs, rt, rn, size); return; } @@ -2311,7 +2712,7 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) int opc = extract32(insn, 30, 2); bool is_signed = false; int size = 2; - TCGv_i64 tcg_rt, tcg_addr; + TCGv_i64 tcg_rt, clean_addr; if (is_vector) { if (opc == 3) { @@ -2333,17 +2734,17 @@ static void disas_ld_lit(DisasContext *s, uint32_t insn) tcg_rt = cpu_reg(s, rt); - tcg_addr = tcg_const_i64((s->pc - 4) + imm); + clean_addr = tcg_const_i64((s->pc - 4) + imm); if (is_vector) { - do_fp_ld(s, rt, tcg_addr, size); + do_fp_ld(s, rt, clean_addr, size); } else { /* Only unsigned 32bit loads target 32bit registers. */ bool iss_sf = opc != 0; - do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, false, + do_gpr_ld(s, tcg_rt, clean_addr, size, is_signed, false, true, rt, iss_sf, false); } - tcg_temp_free_i64(tcg_addr); + tcg_temp_free_i64(clean_addr); } /* @@ -2389,7 +2790,8 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) bool postindex = false; bool wback = false; - TCGv_i64 tcg_addr; /* calculated address */ + TCGv_i64 clean_addr, dirty_addr; + int size; if (opc == 3) { @@ -2445,23 +2847,23 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); - + dirty_addr = read_cpu_reg_sp(s, rn, 1); if (!postindex) { - tcg_gen_addi_i64(tcg_addr, tcg_addr, offset); + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); } + clean_addr = clean_data_tbi(s, dirty_addr); if (is_vector) { if (is_load) { - do_fp_ld(s, rt, tcg_addr, size); + do_fp_ld(s, rt, clean_addr, size); } else { - do_fp_st(s, rt, tcg_addr, size); + do_fp_st(s, rt, clean_addr, size); } - tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size); + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); if (is_load) { - do_fp_ld(s, rt2, tcg_addr, size); + do_fp_ld(s, rt2, clean_addr, size); } else { - do_fp_st(s, rt2, tcg_addr, size); + do_fp_st(s, rt2, clean_addr, size); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); @@ -2473,30 +2875,28 @@ static void disas_ldst_pair(DisasContext *s, uint32_t insn) /* Do not modify tcg_rt before recognizing any exception * from the second load. */ - do_gpr_ld(s, tmp, tcg_addr, size, is_signed, false, + do_gpr_ld(s, tmp, clean_addr, size, is_signed, false, false, 0, false, false); - tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size); - do_gpr_ld(s, tcg_rt2, tcg_addr, size, is_signed, false, + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); + do_gpr_ld(s, tcg_rt2, clean_addr, size, is_signed, false, false, 0, false, false); tcg_gen_mov_i64(tcg_rt, tmp); tcg_temp_free_i64(tmp); } else { - do_gpr_st(s, tcg_rt, tcg_addr, size, + do_gpr_st(s, tcg_rt, clean_addr, size, false, 0, false, false); - tcg_gen_addi_i64(tcg_addr, tcg_addr, 1 << size); - do_gpr_st(s, tcg_rt2, tcg_addr, size, + tcg_gen_addi_i64(clean_addr, clean_addr, 1 << size); + do_gpr_st(s, tcg_rt2, clean_addr, size, false, 0, false, false); } } if (wback) { if (postindex) { - tcg_gen_addi_i64(tcg_addr, tcg_addr, offset - (1 << size)); - } else { - tcg_gen_subi_i64(tcg_addr, tcg_addr, 1 << size); + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); } - tcg_gen_mov_i64(cpu_reg_sp(s, rn), tcg_addr); + tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); } } @@ -2533,7 +2933,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, bool post_index; bool writeback; - TCGv_i64 tcg_addr; + TCGv_i64 clean_addr, dirty_addr; if (is_vector) { size |= (opc & 2) << 1; @@ -2548,7 +2948,7 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, } else { if (size == 3 && opc == 2) { /* PRFM - prefetch */ - if (is_unpriv) { + if (idx != 0) { unallocated_encoding(s); return; } @@ -2584,17 +2984,18 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, if (rn == 31) { gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); + dirty_addr = read_cpu_reg_sp(s, rn, 1); if (!post_index) { - tcg_gen_addi_i64(tcg_addr, tcg_addr, imm9); + tcg_gen_addi_i64(dirty_addr, dirty_addr, imm9); } + clean_addr = clean_data_tbi(s, dirty_addr); if (is_vector) { if (is_store) { - do_fp_st(s, rt, tcg_addr, size); + do_fp_st(s, rt, clean_addr, size); } else { - do_fp_ld(s, rt, tcg_addr, size); + do_fp_ld(s, rt, clean_addr, size); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); @@ -2602,10 +3003,10 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); if (is_store) { - do_gpr_st_memidx(s, tcg_rt, tcg_addr, size, memidx, + do_gpr_st_memidx(s, tcg_rt, clean_addr, size, memidx, iss_valid, rt, iss_sf, false); } else { - do_gpr_ld_memidx(s, tcg_rt, tcg_addr, size, + do_gpr_ld_memidx(s, tcg_rt, clean_addr, size, is_signed, is_extended, memidx, iss_valid, rt, iss_sf, false); } @@ -2614,9 +3015,9 @@ static void disas_ldst_reg_imm9(DisasContext *s, uint32_t insn, if (writeback) { TCGv_i64 tcg_rn = cpu_reg_sp(s, rn); if (post_index) { - tcg_gen_addi_i64(tcg_addr, tcg_addr, imm9); + tcg_gen_addi_i64(dirty_addr, dirty_addr, imm9); } - tcg_gen_mov_i64(tcg_rn, tcg_addr); + tcg_gen_mov_i64(tcg_rn, dirty_addr); } } @@ -2655,8 +3056,7 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, bool is_store = false; bool is_extended = false; - TCGv_i64 tcg_rm; - TCGv_i64 tcg_addr; + TCGv_i64 tcg_rm, clean_addr, dirty_addr; if (extract32(opt, 1, 1) == 0) { unallocated_encoding(s); @@ -2690,27 +3090,28 @@ static void disas_ldst_reg_roffset(DisasContext *s, uint32_t insn, if (rn == 31) { gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); + dirty_addr = read_cpu_reg_sp(s, rn, 1); tcg_rm = read_cpu_reg(s, rm, 1); ext_and_shift_reg(tcg_rm, tcg_rm, opt, shift ? size : 0); - tcg_gen_add_i64(tcg_addr, tcg_addr, tcg_rm); + tcg_gen_add_i64(dirty_addr, dirty_addr, tcg_rm); + clean_addr = clean_data_tbi(s, dirty_addr); if (is_vector) { if (is_store) { - do_fp_st(s, rt, tcg_addr, size); + do_fp_st(s, rt, clean_addr, size); } else { - do_fp_ld(s, rt, tcg_addr, size); + do_fp_ld(s, rt, clean_addr, size); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); if (is_store) { - do_gpr_st(s, tcg_rt, tcg_addr, size, + do_gpr_st(s, tcg_rt, clean_addr, size, true, rt, iss_sf, false); } else { - do_gpr_ld(s, tcg_rt, tcg_addr, size, + do_gpr_ld(s, tcg_rt, clean_addr, size, is_signed, is_extended, true, rt, iss_sf, false); } @@ -2744,7 +3145,7 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, unsigned int imm12 = extract32(insn, 10, 12); unsigned int offset; - TCGv_i64 tcg_addr; + TCGv_i64 clean_addr, dirty_addr; bool is_store; bool is_signed = false; @@ -2777,24 +3178,25 @@ static void disas_ldst_reg_unsigned_imm(DisasContext *s, uint32_t insn, if (rn == 31) { gen_check_sp_alignment(s); } - tcg_addr = read_cpu_reg_sp(s, rn, 1); + dirty_addr = read_cpu_reg_sp(s, rn, 1); offset = imm12 << size; - tcg_gen_addi_i64(tcg_addr, tcg_addr, offset); + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + clean_addr = clean_data_tbi(s, dirty_addr); if (is_vector) { if (is_store) { - do_fp_st(s, rt, tcg_addr, size); + do_fp_st(s, rt, clean_addr, size); } else { - do_fp_ld(s, rt, tcg_addr, size); + do_fp_ld(s, rt, clean_addr, size); } } else { TCGv_i64 tcg_rt = cpu_reg(s, rt); bool iss_sf = disas_ldst_compute_iss_sf(size, is_signed, opc); if (is_store) { - do_gpr_st(s, tcg_rt, tcg_addr, size, + do_gpr_st(s, tcg_rt, clean_addr, size, true, rt, iss_sf, false); } else { - do_gpr_ld(s, tcg_rt, tcg_addr, size, is_signed, is_extended, + do_gpr_ld(s, tcg_rt, clean_addr, size, is_signed, is_extended, true, rt, iss_sf, false); } } @@ -2820,11 +3222,10 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, int rs = extract32(insn, 16, 5); int rn = extract32(insn, 5, 5); int o3_opc = extract32(insn, 12, 4); - int feature = ARM_FEATURE_V8_ATOMICS; - TCGv_i64 tcg_rn, tcg_rs; + TCGv_i64 tcg_rs, clean_addr; AtomicThreeOpFn *fn; - if (is_vector) { + if (is_vector || !dc_isar_feature(aa64_atomics, s)) { unallocated_encoding(s); return; } @@ -2860,15 +3261,11 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, unallocated_encoding(s); return; } - if (!arm_dc_feature(s, feature)) { - unallocated_encoding(s); - return; - } if (rn == 31) { gen_check_sp_alignment(s); } - tcg_rn = cpu_reg_sp(s, rn); + clean_addr = clean_data_tbi(s, cpu_reg_sp(s, rn)); tcg_rs = read_cpu_reg(s, rs, true); if (o3_opc == 1) { /* LDCLR */ @@ -2878,10 +3275,70 @@ static void disas_ldst_atomic(DisasContext *s, uint32_t insn, /* The tcg atomic primitives are all full barriers. Therefore we * can ignore the Acquire and Release bits of this instruction. */ - fn(cpu_reg(s, rt), tcg_rn, tcg_rs, get_mem_index(s), + fn(cpu_reg(s, rt), clean_addr, tcg_rs, get_mem_index(s), s->be_data | size | MO_ALIGN); } +/* + * PAC memory operations + * + * 31 30 27 26 24 22 21 12 11 10 5 0 + * +------+-------+---+-----+-----+---+--------+---+---+----+-----+ + * | size | 1 1 1 | V | 0 0 | M S | 1 | imm9 | W | 1 | Rn | Rt | + * +------+-------+---+-----+-----+---+--------+---+---+----+-----+ + * + * Rt: the result register + * Rn: base address or SP + * V: vector flag (always 0 as of v8.3) + * M: clear for key DA, set for key DB + * W: pre-indexing flag + * S: sign for imm9. + */ +static void disas_ldst_pac(DisasContext *s, uint32_t insn, + int size, int rt, bool is_vector) +{ + int rn = extract32(insn, 5, 5); + bool is_wback = extract32(insn, 11, 1); + bool use_key_a = !extract32(insn, 23, 1); + int offset; + TCGv_i64 clean_addr, dirty_addr, tcg_rt; + + if (size != 3 || is_vector || !dc_isar_feature(aa64_pauth, s)) { + unallocated_encoding(s); + return; + } + + if (rn == 31) { + gen_check_sp_alignment(s); + } + dirty_addr = read_cpu_reg_sp(s, rn, 1); + + if (s->pauth_active) { + if (use_key_a) { + gen_helper_autda(dirty_addr, cpu_env, dirty_addr, cpu_X[31]); + } else { + gen_helper_autdb(dirty_addr, cpu_env, dirty_addr, cpu_X[31]); + } + } + + /* Form the 10-bit signed, scaled offset. */ + offset = (extract32(insn, 22, 1) << 9) | extract32(insn, 12, 9); + offset = sextract32(offset << size, 0, 10 + size); + tcg_gen_addi_i64(dirty_addr, dirty_addr, offset); + + /* Note that "clean" and "dirty" here refer to TBI not PAC. */ + clean_addr = clean_data_tbi(s, dirty_addr); + + tcg_rt = cpu_reg(s, rt); + do_gpr_ld(s, tcg_rt, clean_addr, size, /* is_signed */ false, + /* extend */ false, /* iss_valid */ !is_wback, + /* iss_srt */ rt, /* iss_sf */ true, /* iss_ar */ false); + + if (is_wback) { + tcg_gen_mov_i64(cpu_reg_sp(s, rn), dirty_addr); + } +} + /* Load/store register (all forms) */ static void disas_ldst_reg(DisasContext *s, uint32_t insn) { @@ -2907,6 +3364,9 @@ static void disas_ldst_reg(DisasContext *s, uint32_t insn) case 2: disas_ldst_reg_roffset(s, insn, opc, size, rt, is_vector); return; + default: + disas_ldst_pac(s, insn, size, rt, is_vector); + return; } break; case 1: @@ -2938,15 +3398,17 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) { int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); + int rm = extract32(insn, 16, 5); int size = extract32(insn, 10, 2); int opcode = extract32(insn, 12, 4); bool is_store = !extract32(insn, 22, 1); bool is_postidx = extract32(insn, 23, 1); bool is_q = extract32(insn, 30, 1); - TCGv_i64 tcg_addr, tcg_rn; + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + TCGMemOp endian = s->be_data; - int ebytes = 1 << size; - int elements = (is_q ? 128 : 64) / (8 << size); + int ebytes; /* bytes per element */ + int elements; /* elements per vector */ int rpt; /* num iterations */ int selem; /* structure elements */ int r; @@ -2956,6 +3418,11 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) return; } + if (!is_postidx && rm != 0) { + unallocated_encoding(s); + return; + } + /* From the shared decode logic */ switch (opcode) { case 0x0: @@ -3005,48 +3472,62 @@ static void disas_ldst_multiple_struct(DisasContext *s, uint32_t insn) gen_check_sp_alignment(s); } + /* For our purposes, bytes are always little-endian. */ + if (size == 0) { + endian = MO_LE; + } + + /* Consecutive little-endian elements from a single register + * can be promoted to a larger little-endian operation. + */ + if (selem == 1 && endian == MO_LE) { + size = 3; + } + ebytes = 1 << size; + elements = (is_q ? 16 : 8) / ebytes; + tcg_rn = cpu_reg_sp(s, rn); - tcg_addr = tcg_temp_new_i64(); - tcg_gen_mov_i64(tcg_addr, tcg_rn); + clean_addr = clean_data_tbi(s, tcg_rn); + tcg_ebytes = tcg_const_i64(ebytes); for (r = 0; r < rpt; r++) { int e; for (e = 0; e < elements; e++) { - int tt = (rt + r) % 32; int xs; for (xs = 0; xs < selem; xs++) { + int tt = (rt + r + xs) % 32; if (is_store) { - do_vec_st(s, tt, e, tcg_addr, size); + do_vec_st(s, tt, e, clean_addr, size, endian); } else { - do_vec_ld(s, tt, e, tcg_addr, size); - - /* For non-quad operations, setting a slice of the low - * 64 bits of the register clears the high 64 bits (in - * the ARM ARM pseudocode this is implicit in the fact - * that 'rval' is a 64 bit wide variable). - * For quad operations, we might still need to zero the - * high bits of SVE. We optimize by noticing that we only - * need to do this the first time we touch a register. - */ - if (e == 0 && (r == 0 || xs == selem - 1)) { - clear_vec_high(s, is_q, tt); - } + do_vec_ld(s, tt, e, clean_addr, size, endian); } - tcg_gen_addi_i64(tcg_addr, tcg_addr, ebytes); - tt = (tt + 1) % 32; + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); } } } + tcg_temp_free_i64(tcg_ebytes); + + if (!is_store) { + /* For non-quad operations, setting a slice of the low + * 64 bits of the register clears the high 64 bits (in + * the ARM ARM pseudocode this is implicit in the fact + * that 'rval' is a 64 bit wide variable). + * For quad operations, we might still need to zero the + * high bits of SVE. + */ + for (r = 0; r < rpt * selem; r++) { + int tt = (rt + r) % 32; + clear_vec_high(s, is_q, tt); + } + } if (is_postidx) { - int rm = extract32(insn, 16, 5); if (rm == 31) { - tcg_gen_mov_i64(tcg_rn, tcg_addr); + tcg_gen_addi_i64(tcg_rn, tcg_rn, rpt * elements * selem * ebytes); } else { tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); } } - tcg_temp_free_i64(tcg_addr); } /* AdvSIMD load/store single structure @@ -3075,6 +3556,7 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) { int rt = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); + int rm = extract32(insn, 16, 5); int size = extract32(insn, 10, 2); int S = extract32(insn, 12, 1); int opc = extract32(insn, 13, 3); @@ -3088,7 +3570,16 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) bool replicate = false; int index = is_q << 3 | S << 2 | size; int ebytes, xs; - TCGv_i64 tcg_addr, tcg_rn; + TCGv_i64 clean_addr, tcg_rn, tcg_ebytes; + + if (extract32(insn, 31, 1)) { + unallocated_encoding(s); + return; + } + if (!is_postidx && rm != 0) { + unallocated_encoding(s); + return; + } switch (scale) { case 3: @@ -3139,63 +3630,40 @@ static void disas_ldst_single_struct(DisasContext *s, uint32_t insn) } tcg_rn = cpu_reg_sp(s, rn); - tcg_addr = tcg_temp_new_i64(); - tcg_gen_mov_i64(tcg_addr, tcg_rn); + clean_addr = clean_data_tbi(s, tcg_rn); + tcg_ebytes = tcg_const_i64(ebytes); for (xs = 0; xs < selem; xs++) { if (replicate) { /* Load and replicate to all elements */ - uint64_t mulconst; TCGv_i64 tcg_tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(tcg_tmp, tcg_addr, + tcg_gen_qemu_ld_i64(tcg_tmp, clean_addr, get_mem_index(s), s->be_data + scale); - switch (scale) { - case 0: - mulconst = 0x0101010101010101ULL; - break; - case 1: - mulconst = 0x0001000100010001ULL; - break; - case 2: - mulconst = 0x0000000100000001ULL; - break; - case 3: - mulconst = 0; - break; - default: - g_assert_not_reached(); - } - if (mulconst) { - tcg_gen_muli_i64(tcg_tmp, tcg_tmp, mulconst); - } - write_vec_element(s, tcg_tmp, rt, 0, MO_64); - if (is_q) { - write_vec_element(s, tcg_tmp, rt, 1, MO_64); - } + tcg_gen_gvec_dup_i64(scale, vec_full_reg_offset(s, rt), + (is_q + 1) * 8, vec_full_reg_size(s), + tcg_tmp); tcg_temp_free_i64(tcg_tmp); - clear_vec_high(s, is_q, rt); } else { /* Load/store one element per register */ if (is_load) { - do_vec_ld(s, rt, index, tcg_addr, scale); + do_vec_ld(s, rt, index, clean_addr, scale, s->be_data); } else { - do_vec_st(s, rt, index, tcg_addr, scale); + do_vec_st(s, rt, index, clean_addr, scale, s->be_data); } } - tcg_gen_addi_i64(tcg_addr, tcg_addr, ebytes); + tcg_gen_add_i64(clean_addr, clean_addr, tcg_ebytes); rt = (rt + 1) % 32; } + tcg_temp_free_i64(tcg_ebytes); if (is_postidx) { - int rm = extract32(insn, 16, 5); if (rm == 31) { - tcg_gen_mov_i64(tcg_rn, tcg_addr); + tcg_gen_addi_i64(tcg_rn, tcg_rn, selem * ebytes); } else { tcg_gen_add_i64(tcg_rn, tcg_rn, cpu_reg(s, rm)); } } - tcg_temp_free_i64(tcg_addr); } /* Loads and stores */ @@ -3878,6 +4346,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) int imm3 = extract32(insn, 10, 3); int option = extract32(insn, 13, 3); int rm = extract32(insn, 16, 5); + int opt = extract32(insn, 22, 2); bool setflags = extract32(insn, 29, 1); bool sub_op = extract32(insn, 30, 1); bool sf = extract32(insn, 31, 1); @@ -3886,7 +4355,7 @@ static void disas_add_sub_ext_reg(DisasContext *s, uint32_t insn) TCGv_i64 tcg_rd; TCGv_i64 tcg_result; - if (imm3 > 4) { + if (imm3 > 4 || opt != 0) { unallocated_encoding(s); return; } @@ -4090,11 +4559,10 @@ static void disas_data_proc_3src(DisasContext *s, uint32_t insn) } /* Add/subtract (with carry) - * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0 - * +--+--+--+------------------------+------+---------+------+-----+ - * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | opcode2 | Rn | Rd | - * +--+--+--+------------------------+------+---------+------+-----+ - * [000000] + * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 10 9 5 4 0 + * +--+--+--+------------------------+------+-------------+------+-----+ + * |sf|op| S| 1 1 0 1 0 0 0 0 | rm | 0 0 0 0 0 0 | Rn | Rd | + * +--+--+--+------------------------+------+-------------+------+-----+ */ static void disas_adc_sbc(DisasContext *s, uint32_t insn) @@ -4102,11 +4570,6 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn) unsigned int sf, op, setflags, rm, rn, rd; TCGv_i64 tcg_y, tcg_rn, tcg_rd; - if (extract32(insn, 10, 6) != 0) { - unallocated_encoding(s); - return; - } - sf = extract32(insn, 31, 1); op = extract32(insn, 30, 1); setflags = extract32(insn, 29, 1); @@ -4131,6 +4594,84 @@ static void disas_adc_sbc(DisasContext *s, uint32_t insn) } } +/* + * Rotate right into flags + * 31 30 29 21 15 10 5 4 0 + * +--+--+--+-----------------+--------+-----------+------+--+------+ + * |sf|op| S| 1 1 0 1 0 0 0 0 | imm6 | 0 0 0 0 1 | Rn |o2| mask | + * +--+--+--+-----------------+--------+-----------+------+--+------+ + */ +static void disas_rotate_right_into_flags(DisasContext *s, uint32_t insn) +{ + int mask = extract32(insn, 0, 4); + int o2 = extract32(insn, 4, 1); + int rn = extract32(insn, 5, 5); + int imm6 = extract32(insn, 15, 6); + int sf_op_s = extract32(insn, 29, 3); + TCGv_i64 tcg_rn; + TCGv_i32 nzcv; + + if (sf_op_s != 5 || o2 != 0 || !dc_isar_feature(aa64_condm_4, s)) { + unallocated_encoding(s); + return; + } + + tcg_rn = read_cpu_reg(s, rn, 1); + tcg_gen_rotri_i64(tcg_rn, tcg_rn, imm6); + + nzcv = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(nzcv, tcg_rn); + + if (mask & 8) { /* N */ + tcg_gen_shli_i32(cpu_NF, nzcv, 31 - 3); + } + if (mask & 4) { /* Z */ + tcg_gen_not_i32(cpu_ZF, nzcv); + tcg_gen_andi_i32(cpu_ZF, cpu_ZF, 4); + } + if (mask & 2) { /* C */ + tcg_gen_extract_i32(cpu_CF, nzcv, 1, 1); + } + if (mask & 1) { /* V */ + tcg_gen_shli_i32(cpu_VF, nzcv, 31 - 0); + } + + tcg_temp_free_i32(nzcv); +} + +/* + * Evaluate into flags + * 31 30 29 21 15 14 10 5 4 0 + * +--+--+--+-----------------+---------+----+---------+------+--+------+ + * |sf|op| S| 1 1 0 1 0 0 0 0 | opcode2 | sz | 0 0 1 0 | Rn |o3| mask | + * +--+--+--+-----------------+---------+----+---------+------+--+------+ + */ +static void disas_evaluate_into_flags(DisasContext *s, uint32_t insn) +{ + int o3_mask = extract32(insn, 0, 5); + int rn = extract32(insn, 5, 5); + int o2 = extract32(insn, 15, 6); + int sz = extract32(insn, 14, 1); + int sf_op_s = extract32(insn, 29, 3); + TCGv_i32 tmp; + int shift; + + if (sf_op_s != 1 || o2 != 0 || o3_mask != 0xd || + !dc_isar_feature(aa64_condm_4, s)) { + unallocated_encoding(s); + return; + } + shift = sz ? 16 : 24; /* SETF16 or SETF8 */ + + tmp = tcg_temp_new_i32(); + tcg_gen_extrl_i64_i32(tmp, cpu_reg(s, rn)); + tcg_gen_shli_i32(cpu_NF, tmp, shift); + tcg_gen_shli_i32(cpu_VF, tmp, shift - 1); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); + tcg_gen_xor_i32(cpu_VF, cpu_VF, cpu_NF); + tcg_temp_free_i32(tmp); +} + /* Conditional compare (immediate / register) * 31 30 29 28 27 26 25 24 23 22 21 20 16 15 12 11 10 9 5 4 3 0 * +--+--+--+------------------------+--------+------+----+--+------+--+-----+ @@ -4411,38 +4952,197 @@ static void handle_rev16(DisasContext *s, unsigned int sf, */ static void disas_data_proc_1src(DisasContext *s, uint32_t insn) { - unsigned int sf, opcode, rn, rd; + unsigned int sf, opcode, opcode2, rn, rd; + TCGv_i64 tcg_rd; - if (extract32(insn, 29, 1) || extract32(insn, 16, 5)) { + if (extract32(insn, 29, 1)) { unallocated_encoding(s); return; } sf = extract32(insn, 31, 1); opcode = extract32(insn, 10, 6); + opcode2 = extract32(insn, 16, 5); rn = extract32(insn, 5, 5); rd = extract32(insn, 0, 5); - switch (opcode) { - case 0: /* RBIT */ +#define MAP(SF, O2, O1) ((SF) | (O1 << 1) | (O2 << 7)) + + switch (MAP(sf, opcode2, opcode)) { + case MAP(0, 0x00, 0x00): /* RBIT */ + case MAP(1, 0x00, 0x00): handle_rbit(s, sf, rn, rd); break; - case 1: /* REV16 */ + case MAP(0, 0x00, 0x01): /* REV16 */ + case MAP(1, 0x00, 0x01): handle_rev16(s, sf, rn, rd); break; - case 2: /* REV32 */ + case MAP(0, 0x00, 0x02): /* REV/REV32 */ + case MAP(1, 0x00, 0x02): handle_rev32(s, sf, rn, rd); break; - case 3: /* REV64 */ + case MAP(1, 0x00, 0x03): /* REV64 */ handle_rev64(s, sf, rn, rd); break; - case 4: /* CLZ */ + case MAP(0, 0x00, 0x04): /* CLZ */ + case MAP(1, 0x00, 0x04): handle_clz(s, sf, rn, rd); break; - case 5: /* CLS */ + case MAP(0, 0x00, 0x05): /* CLS */ + case MAP(1, 0x00, 0x05): handle_cls(s, sf, rn, rd); break; + case MAP(1, 0x01, 0x00): /* PACIA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacia(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x01): /* PACIB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacib(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x02): /* PACDA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacda(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x03): /* PACDB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacdb(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x04): /* AUTIA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autia(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x05): /* AUTIB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autib(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x06): /* AUTDA */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autda(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x07): /* AUTDB */ + if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autdb(tcg_rd, cpu_env, tcg_rd, cpu_reg_sp(s, rn)); + } else if (!dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + break; + case MAP(1, 0x01, 0x08): /* PACIZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacia(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x09): /* PACIZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacib(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x0a): /* PACDZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacda(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x0b): /* PACDZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_pacdb(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x0c): /* AUTIZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autia(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x0d): /* AUTIZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autib(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x0e): /* AUTDZA */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autda(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x0f): /* AUTDZB */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_autdb(tcg_rd, cpu_env, tcg_rd, new_tmp_a64_zero(s)); + } + break; + case MAP(1, 0x01, 0x10): /* XPACI */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_xpaci(tcg_rd, cpu_env, tcg_rd); + } + break; + case MAP(1, 0x01, 0x11): /* XPACD */ + if (!dc_isar_feature(aa64_pauth, s) || rn != 31) { + goto do_unallocated; + } else if (s->pauth_active) { + tcg_rd = cpu_reg(s, rd); + gen_helper_xpacd(tcg_rd, cpu_env, tcg_rd); + } + break; + default: + do_unallocated: + unallocated_encoding(s); + break; } + +#undef MAP } static void handle_div(DisasContext *s, bool is_signed, unsigned int sf, @@ -4494,7 +5194,7 @@ static void handle_crc32(DisasContext *s, TCGv_i64 tcg_acc, tcg_val; TCGv_i32 tcg_bytes; - if (!arm_dc_feature(s, ARM_FEATURE_CRC) + if (!dc_isar_feature(aa64_crc32, s) || (sf == 1 && sz != 3) || (sf == 0 && sz == 3)) { unallocated_encoding(s); @@ -4573,6 +5273,13 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) case 11: /* RORV */ handle_shift_reg(s, A64_SHIFT_TYPE_ROR, sf, rm, rn, rd); break; + case 12: /* PACGA */ + if (sf == 0 || !dc_isar_feature(aa64_pauth, s)) { + goto do_unallocated; + } + gen_helper_pacga(cpu_reg(s, rd), cpu_env, + cpu_reg(s, rn), cpu_reg_sp(s, rm)); + break; case 16: case 17: case 18: @@ -4588,52 +5295,87 @@ static void disas_data_proc_2src(DisasContext *s, uint32_t insn) break; } default: + do_unallocated: unallocated_encoding(s); break; } } -/* Data processing - register */ +/* + * Data processing - register + * 31 30 29 28 25 21 20 16 10 0 + * +--+---+--+---+-------+-----+-------+-------+---------+ + * | |op0| |op1| 1 0 1 | op2 | | op3 | | + * +--+---+--+---+-------+-----+-------+-------+---------+ + */ static void disas_data_proc_reg(DisasContext *s, uint32_t insn) { - switch (extract32(insn, 24, 5)) { - case 0x0a: /* Logical (shifted register) */ - disas_logic_reg(s, insn); - break; - case 0x0b: /* Add/subtract */ - if (insn & (1 << 21)) { /* (extended register) */ - disas_add_sub_ext_reg(s, insn); + int op0 = extract32(insn, 30, 1); + int op1 = extract32(insn, 28, 1); + int op2 = extract32(insn, 21, 4); + int op3 = extract32(insn, 10, 6); + + if (!op1) { + if (op2 & 8) { + if (op2 & 1) { + /* Add/sub (extended register) */ + disas_add_sub_ext_reg(s, insn); + } else { + /* Add/sub (shifted register) */ + disas_add_sub_reg(s, insn); + } } else { - disas_add_sub_reg(s, insn); + /* Logical (shifted register) */ + disas_logic_reg(s, insn); } - break; - case 0x1b: /* Data-processing (3 source) */ - disas_data_proc_3src(s, insn); - break; - case 0x1a: - switch (extract32(insn, 21, 3)) { - case 0x0: /* Add/subtract (with carry) */ + return; + } + + switch (op2) { + case 0x0: + switch (op3) { + case 0x00: /* Add/subtract (with carry) */ disas_adc_sbc(s, insn); break; - case 0x2: /* Conditional compare */ - disas_cc(s, insn); /* both imm and reg forms */ + + case 0x01: /* Rotate right into flags */ + case 0x21: + disas_rotate_right_into_flags(s, insn); break; - case 0x4: /* Conditional select */ - disas_cond_select(s, insn); - break; - case 0x6: /* Data-processing */ - if (insn & (1 << 30)) { /* (1 source) */ - disas_data_proc_1src(s, insn); - } else { /* (2 source) */ - disas_data_proc_2src(s, insn); - } + + case 0x02: /* Evaluate into flags */ + case 0x12: + case 0x22: + case 0x32: + disas_evaluate_into_flags(s, insn); break; + default: - unallocated_encoding(s); - break; + goto do_unallocated; } break; + + case 0x2: /* Conditional compare */ + disas_cc(s, insn); /* both imm and reg forms */ + break; + + case 0x4: /* Conditional select */ + disas_cond_select(s, insn); + break; + + case 0x6: /* Data-processing */ + if (op0) { /* (1 source) */ + disas_data_proc_1src(s, insn); + } else { /* (2 source) */ + disas_data_proc_2src(s, insn); + } + break; + case 0x8 ... 0xf: /* (3 source) */ + disas_data_proc_3src(s, insn); + break; + default: + do_unallocated: unallocated_encoding(s); break; } @@ -4736,7 +5478,7 @@ static void disas_fp_compare(DisasContext *s, uint32_t insn) break; case 3: size = MO_16; - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (dc_isar_feature(aa64_fp16, s)) { break; } /* fallthru */ @@ -4787,7 +5529,7 @@ static void disas_fp_ccomp(DisasContext *s, uint32_t insn) break; case 3: size = MO_16; - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (dc_isar_feature(aa64_fp16, s)) { break; } /* fallthru */ @@ -4853,7 +5595,7 @@ static void disas_fp_csel(DisasContext *s, uint32_t insn) break; case 3: sz = MO_16; - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (dc_isar_feature(aa64_fp16, s)) { break; } /* fallthru */ @@ -4946,55 +5688,73 @@ static void handle_fp_1src_half(DisasContext *s, int opcode, int rd, int rn) /* Floating-point data-processing (1 source) - single precision */ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) { + void (*gen_fpst)(TCGv_i32, TCGv_i32, TCGv_ptr); + TCGv_i32 tcg_op, tcg_res; TCGv_ptr fpst; - TCGv_i32 tcg_op; - TCGv_i32 tcg_res; + int rmode = -1; - fpst = get_fpstatus_ptr(false); tcg_op = read_fp_sreg(s, rn); tcg_res = tcg_temp_new_i32(); switch (opcode) { case 0x0: /* FMOV */ tcg_gen_mov_i32(tcg_res, tcg_op); - break; + goto done; case 0x1: /* FABS */ gen_helper_vfp_abss(tcg_res, tcg_op); - break; + goto done; case 0x2: /* FNEG */ gen_helper_vfp_negs(tcg_res, tcg_op); - break; + goto done; case 0x3: /* FSQRT */ gen_helper_vfp_sqrts(tcg_res, tcg_op, cpu_env); - break; + goto done; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ case 0xb: /* FRINTZ */ case 0xc: /* FRINTA */ - { - TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - gen_helper_rints(tcg_res, tcg_op, fpst); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); + rmode = arm_rmode_to_sf(opcode & 7); + gen_fpst = gen_helper_rints; break; - } case 0xe: /* FRINTX */ - gen_helper_rints_exact(tcg_res, tcg_op, fpst); + gen_fpst = gen_helper_rints_exact; break; case 0xf: /* FRINTI */ - gen_helper_rints(tcg_res, tcg_op, fpst); + gen_fpst = gen_helper_rints; + break; + case 0x10: /* FRINT32Z */ + rmode = float_round_to_zero; + gen_fpst = gen_helper_frint32_s; + break; + case 0x11: /* FRINT32X */ + gen_fpst = gen_helper_frint32_s; + break; + case 0x12: /* FRINT64Z */ + rmode = float_round_to_zero; + gen_fpst = gen_helper_frint64_s; + break; + case 0x13: /* FRINT64X */ + gen_fpst = gen_helper_frint64_s; break; default: - abort(); + g_assert_not_reached(); } - write_fp_sreg(s, rd, tcg_res); - + fpst = get_fpstatus_ptr(false); + if (rmode >= 0) { + TCGv_i32 tcg_rmode = tcg_const_i32(rmode); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + gen_fpst(tcg_res, tcg_op, fpst); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_temp_free_i32(tcg_rmode); + } else { + gen_fpst(tcg_res, tcg_op, fpst); + } tcg_temp_free_ptr(fpst); + + done: + write_fp_sreg(s, rd, tcg_res); tcg_temp_free_i32(tcg_op); tcg_temp_free_i32(tcg_res); } @@ -5002,9 +5762,10 @@ static void handle_fp_1src_single(DisasContext *s, int opcode, int rd, int rn) /* Floating-point data-processing (1 source) - double precision */ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) { + void (*gen_fpst)(TCGv_i64, TCGv_i64, TCGv_ptr); + TCGv_i64 tcg_op, tcg_res; TCGv_ptr fpst; - TCGv_i64 tcg_op; - TCGv_i64 tcg_res; + int rmode = -1; switch (opcode) { case 0x0: /* FMOV */ @@ -5012,48 +5773,65 @@ static void handle_fp_1src_double(DisasContext *s, int opcode, int rd, int rn) return; } - fpst = get_fpstatus_ptr(false); tcg_op = read_fp_dreg(s, rn); tcg_res = tcg_temp_new_i64(); switch (opcode) { case 0x1: /* FABS */ gen_helper_vfp_absd(tcg_res, tcg_op); - break; + goto done; case 0x2: /* FNEG */ gen_helper_vfp_negd(tcg_res, tcg_op); - break; + goto done; case 0x3: /* FSQRT */ gen_helper_vfp_sqrtd(tcg_res, tcg_op, cpu_env); - break; + goto done; case 0x8: /* FRINTN */ case 0x9: /* FRINTP */ case 0xa: /* FRINTM */ case 0xb: /* FRINTZ */ case 0xc: /* FRINTA */ - { - TCGv_i32 tcg_rmode = tcg_const_i32(arm_rmode_to_sf(opcode & 7)); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - gen_helper_rintd(tcg_res, tcg_op, fpst); - - gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); - tcg_temp_free_i32(tcg_rmode); + rmode = arm_rmode_to_sf(opcode & 7); + gen_fpst = gen_helper_rintd; break; - } case 0xe: /* FRINTX */ - gen_helper_rintd_exact(tcg_res, tcg_op, fpst); + gen_fpst = gen_helper_rintd_exact; break; case 0xf: /* FRINTI */ - gen_helper_rintd(tcg_res, tcg_op, fpst); + gen_fpst = gen_helper_rintd; + break; + case 0x10: /* FRINT32Z */ + rmode = float_round_to_zero; + gen_fpst = gen_helper_frint32_d; + break; + case 0x11: /* FRINT32X */ + gen_fpst = gen_helper_frint32_d; + break; + case 0x12: /* FRINT64Z */ + rmode = float_round_to_zero; + gen_fpst = gen_helper_frint64_d; + break; + case 0x13: /* FRINT64X */ + gen_fpst = gen_helper_frint64_d; break; default: - abort(); + g_assert_not_reached(); } - write_fp_dreg(s, rd, tcg_res); - + fpst = get_fpstatus_ptr(false); + if (rmode >= 0) { + TCGv_i32 tcg_rmode = tcg_const_i32(rmode); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + gen_fpst(tcg_res, tcg_op, fpst); + gen_helper_set_rmode(tcg_rmode, tcg_rmode, fpst); + tcg_temp_free_i32(tcg_rmode); + } else { + gen_fpst(tcg_res, tcg_op, fpst); + } tcg_temp_free_ptr(fpst); + + done: + write_fp_dreg(s, rd, tcg_res); tcg_temp_free_i64(tcg_op); tcg_temp_free_i64(tcg_res); } @@ -5145,11 +5923,17 @@ static void handle_fp_fcvt(DisasContext *s, int opcode, */ static void disas_fp_1src(DisasContext *s, uint32_t insn) { + int mos = extract32(insn, 29, 3); int type = extract32(insn, 22, 2); int opcode = extract32(insn, 15, 6); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); + if (mos) { + unallocated_encoding(s); + return; + } + switch (opcode) { case 0x4: case 0x5: case 0x7: { @@ -5166,6 +5950,13 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) handle_fp_fcvt(s, opcode, rd, rn, dtype, type); break; } + + case 0x10 ... 0x13: /* FRINT{32,64}{X,Z} */ + if (type > 1 || !dc_isar_feature(aa64_frint, s)) { + unallocated_encoding(s); + return; + } + /* fall through */ case 0x0 ... 0x3: case 0x8 ... 0xc: case 0xe ... 0xf: @@ -5175,18 +5966,16 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) if (!fp_access_check(s)) { return; } - handle_fp_1src_single(s, opcode, rd, rn); break; case 1: if (!fp_access_check(s)) { return; } - handle_fp_1src_double(s, opcode, rd, rn); break; case 3: - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -5194,13 +5983,13 @@ static void disas_fp_1src(DisasContext *s, uint32_t insn) if (!fp_access_check(s)) { return; } - handle_fp_1src_half(s, opcode, rd, rn); break; default: unallocated_encoding(s); } break; + default: unallocated_encoding(s); break; @@ -5376,13 +6165,14 @@ static void handle_fp_2src_half(DisasContext *s, int opcode, */ static void disas_fp_2src(DisasContext *s, uint32_t insn) { + int mos = extract32(insn, 29, 3); int type = extract32(insn, 22, 2); int rd = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int rm = extract32(insn, 16, 5); int opcode = extract32(insn, 12, 4); - if (opcode > 8) { + if (opcode > 8 || mos) { unallocated_encoding(s); return; } @@ -5401,7 +6191,7 @@ static void disas_fp_2src(DisasContext *s, uint32_t insn) handle_fp_2src_double(s, opcode, rd, rn, rm); break; case 3: - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -5537,6 +6327,7 @@ static void handle_fp_3src_half(DisasContext *s, bool o0, bool o1, */ static void disas_fp_3src(DisasContext *s, uint32_t insn) { + int mos = extract32(insn, 29, 3); int type = extract32(insn, 22, 2); int rd = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); @@ -5545,6 +6336,11 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) bool o0 = extract32(insn, 15, 1); bool o1 = extract32(insn, 21, 1); + if (mos) { + unallocated_encoding(s); + return; + } + switch (type) { case 0: if (!fp_access_check(s)) { @@ -5559,7 +6355,7 @@ static void disas_fp_3src(DisasContext *s, uint32_t insn) handle_fp_3src_double(s, o0, o1, rd, rn, rm, ra); break; case 3: - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -5614,12 +6410,19 @@ uint64_t vfp_expand_imm(int size, uint8_t imm8) static void disas_fp_imm(DisasContext *s, uint32_t insn) { int rd = extract32(insn, 0, 5); + int imm5 = extract32(insn, 5, 5); int imm8 = extract32(insn, 13, 8); int type = extract32(insn, 22, 2); + int mos = extract32(insn, 29, 3); uint64_t imm; TCGv_i64 tcg_res; TCGMemOp sz; + if (mos || imm5) { + unallocated_encoding(s); + return; + } + switch (type) { case 0: sz = MO_32; @@ -5629,7 +6432,7 @@ static void disas_fp_imm(DisasContext *s, uint32_t insn) break; case 3: sz = MO_16; - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (dc_isar_feature(aa64_fp16, s)) { break; } /* fallthru */ @@ -5854,7 +6657,7 @@ static void disas_fp_fixed_conv(DisasContext *s, uint32_t insn) case 1: /* float64 */ break; case 3: /* float16 */ - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (dc_isar_feature(aa64_fp16, s)) { break; } /* fallthru */ @@ -5947,6 +6750,24 @@ static void handle_fmov(DisasContext *s, int rd, int rn, int type, bool itof) } } +static void handle_fjcvtzs(DisasContext *s, int rd, int rn) +{ + TCGv_i64 t = read_fp_dreg(s, rn); + TCGv_ptr fpstatus = get_fpstatus_ptr(false); + + gen_helper_fjcvtzs(t, t, fpstatus); + + tcg_temp_free_ptr(fpstatus); + + tcg_gen_ext32u_i64(cpu_reg(s, rd), t); + tcg_gen_extrh_i64_i32(cpu_ZF, t); + tcg_gen_movi_i32(cpu_CF, 0); + tcg_gen_movi_i32(cpu_NF, 0); + tcg_gen_movi_i32(cpu_VF, 0); + + tcg_temp_free_i64(t); +} + /* Floating point <-> integer conversions * 31 30 29 28 24 23 22 21 20 19 18 16 15 10 9 5 4 0 * +----+---+---+-----------+------+---+-------+-----+-------------+----+----+ @@ -5962,68 +6783,80 @@ static void disas_fp_int_conv(DisasContext *s, uint32_t insn) int type = extract32(insn, 22, 2); bool sbit = extract32(insn, 29, 1); bool sf = extract32(insn, 31, 1); + bool itof = false; if (sbit) { - unallocated_encoding(s); - return; + goto do_unallocated; } - if (opcode > 5) { - /* FMOV */ - bool itof = opcode & 1; - - if (rmode >= 2) { - unallocated_encoding(s); - return; - } - - switch (sf << 3 | type << 1 | rmode) { - case 0x0: /* 32 bit */ - case 0xa: /* 64 bit */ - case 0xd: /* 64 bit to top half of quad */ - break; - case 0x6: /* 16-bit float, 32-bit int */ - case 0xe: /* 16-bit float, 64-bit int */ - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { - break; - } - /* fallthru */ - default: - /* all other sf/type/rmode combinations are invalid */ - unallocated_encoding(s); - return; - } - - if (!fp_access_check(s)) { - return; - } - handle_fmov(s, rd, rn, type, itof); - } else { - /* actual FP conversions */ - bool itof = extract32(opcode, 1, 1); - - if (rmode != 0 && opcode > 1) { - unallocated_encoding(s); - return; + switch (opcode) { + case 2: /* SCVTF */ + case 3: /* UCVTF */ + itof = true; + /* fallthru */ + case 4: /* FCVTAS */ + case 5: /* FCVTAU */ + if (rmode != 0) { + goto do_unallocated; } + /* fallthru */ + case 0: /* FCVT[NPMZ]S */ + case 1: /* FCVT[NPMZ]U */ switch (type) { case 0: /* float32 */ case 1: /* float64 */ break; case 3: /* float16 */ - if (arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { - break; + if (!dc_isar_feature(aa64_fp16, s)) { + goto do_unallocated; } - /* fallthru */ + break; default: - unallocated_encoding(s); - return; + goto do_unallocated; } - if (!fp_access_check(s)) { return; } handle_fpfpcvt(s, rd, rn, opcode, itof, rmode, 64, sf, type); + break; + + default: + switch (sf << 7 | type << 5 | rmode << 3 | opcode) { + case 0b01100110: /* FMOV half <-> 32-bit int */ + case 0b01100111: + case 0b11100110: /* FMOV half <-> 64-bit int */ + case 0b11100111: + if (!dc_isar_feature(aa64_fp16, s)) { + goto do_unallocated; + } + /* fallthru */ + case 0b00000110: /* FMOV 32-bit */ + case 0b00000111: + case 0b10100110: /* FMOV 64-bit */ + case 0b10100111: + case 0b11001110: /* FMOV top half of 128-bit */ + case 0b11001111: + if (!fp_access_check(s)) { + return; + } + itof = opcode & 1; + handle_fmov(s, rd, rn, type, itof); + break; + + case 0b00111110: /* FJCVTZS */ + if (!dc_isar_feature(aa64_jscvt, s)) { + goto do_unallocated; + } else if (fp_access_check(s)) { + handle_fjcvtzs(s, rd, rn); + } + break; + + default: + do_unallocated: + unallocated_encoding(s); + return; + } + break; } } @@ -6448,7 +7281,7 @@ static void disas_simd_across_lanes(DisasContext *s, uint32_t insn) */ is_min = extract32(size, 1, 1); is_fp = true; - if (!is_u && arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!is_u && dc_isar_feature(aa64_fp16, s)) { size = 1; } else if (!is_u || !is_q || extract32(size, 0, 1)) { unallocated_encoding(s); @@ -6844,7 +7677,7 @@ static void disas_simd_mod_imm(DisasContext *s, uint32_t insn) if (o2 != 0 || ((cmode == 0xf) && is_neg && !is_q)) { /* Check for FMOV (vector, immediate) - half-precision */ - if (!(arm_dc_feature(s, ARM_FEATURE_V8_FP16) && o2 && cmode == 0xf)) { + if (!(dc_isar_feature(aa64_fp16, s) && o2 && cmode == 0xf)) { unallocated_encoding(s); return; } @@ -7011,7 +7844,7 @@ static void disas_simd_scalar_pairwise(DisasContext *s, uint32_t insn) case 0x2f: /* FMINP */ /* FP op, size[0] is 32 or 64 bit*/ if (!u) { - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } else { @@ -7656,7 +8489,7 @@ static void handle_simd_shift_intfp_conv(DisasContext *s, bool is_scalar, size = MO_32; } else if (immh & 2) { size = MO_16; - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -7701,7 +8534,7 @@ static void handle_simd_shift_fpint_conv(DisasContext *s, bool is_scalar, size = MO_32; } else if (immh & 0x2) { size = MO_16; - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -7966,28 +8799,6 @@ static void disas_simd_scalar_three_reg_diff(DisasContext *s, uint32_t insn) } } -/* CMTST : test is "if (X & Y != 0)". */ -static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_and_i32(d, a, b); - tcg_gen_setcondi_i32(TCG_COND_NE, d, d, 0); - tcg_gen_neg_i32(d, d); -} - -static void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_and_i64(d, a, b); - tcg_gen_setcondi_i64(TCG_COND_NE, d, d, 0); - tcg_gen_neg_i64(d, d); -} - -static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_and_vec(vece, d, a, b); - tcg_gen_dupi_vec(vece, a, 0); - tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a); -} - static void handle_3same_64(DisasContext *s, int opcode, bool u, TCGv_i64 tcg_rd, TCGv_i64 tcg_rn, TCGv_i64 tcg_rm) { @@ -8465,7 +9276,7 @@ static void disas_simd_scalar_three_reg_same_fp16(DisasContext *s, return; } - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); } @@ -8538,7 +9349,7 @@ static void disas_simd_scalar_three_reg_same_extra(DisasContext *s, bool u = extract32(insn, 29, 1); TCGv_i32 ele1, ele2, ele3; TCGv_i64 res; - int feature; + bool feature; switch (u * 16 + opcode) { case 0x10: /* SQRDMLAH (vector) */ @@ -8547,13 +9358,13 @@ static void disas_simd_scalar_three_reg_same_extra(DisasContext *s, unallocated_encoding(s); return; } - feature = ARM_FEATURE_V8_RDM; + feature = dc_isar_feature(aa64_rdm, s); break; default: unallocated_encoding(s); return; } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -8706,6 +9517,14 @@ static void handle_2misc_64(DisasContext *s, int opcode, bool u, case 0x59: /* FRINTX */ gen_helper_rintd_exact(tcg_rd, tcg_rn, tcg_fpstatus); break; + case 0x1e: /* FRINT32Z */ + case 0x5e: /* FRINT32X */ + gen_helper_frint32_d(tcg_rd, tcg_rn, tcg_fpstatus); + break; + case 0x1f: /* FRINT64Z */ + case 0x5f: /* FRINT64X */ + gen_helper_frint64_d(tcg_rd, tcg_rn, tcg_fpstatus); + break; default: g_assert_not_reached(); } @@ -9327,191 +10146,10 @@ static void disas_simd_scalar_two_reg_misc(DisasContext *s, uint32_t insn) } } -static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_sar8i_i64(a, a, shift); - tcg_gen_vec_add8_i64(d, d, a); -} - -static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_sar16i_i64(a, a, shift); - tcg_gen_vec_add16_i64(d, d, a); -} - -static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_sari_i32(a, a, shift); - tcg_gen_add_i32(d, d, a); -} - -static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_sari_i64(a, a, shift); - tcg_gen_add_i64(d, d, a); -} - -static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - tcg_gen_sari_vec(vece, a, a, sh); - tcg_gen_add_vec(vece, d, d, a); -} - -static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_shr8i_i64(a, a, shift); - tcg_gen_vec_add8_i64(d, d, a); -} - -static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_vec_shr16i_i64(a, a, shift); - tcg_gen_vec_add16_i64(d, d, a); -} - -static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_shri_i32(a, a, shift); - tcg_gen_add_i32(d, d, a); -} - -static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_shri_i64(a, a, shift); - tcg_gen_add_i64(d, d, a); -} - -static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - tcg_gen_shri_vec(vece, a, a, sh); - tcg_gen_add_vec(vece, d, d, a); -} - -static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_8, 0xff >> shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); -} - -static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_16, 0xffff >> shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shri_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); -} - -static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_shri_i32(a, a, shift); - tcg_gen_deposit_i32(d, d, a, 0, 32 - shift); -} - -static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_shri_i64(a, a, shift); - tcg_gen_deposit_i64(d, d, a, 0, 64 - shift); -} - -static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - uint64_t mask = (2ull << ((8 << vece) - 1)) - 1; - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_temp_new_vec_matching(d); - - tcg_gen_dupi_vec(vece, m, mask ^ (mask >> sh)); - tcg_gen_shri_vec(vece, t, a, sh); - tcg_gen_and_vec(vece, d, d, m); - tcg_gen_or_vec(vece, d, d, t); - - tcg_temp_free_vec(t); - tcg_temp_free_vec(m); -} - /* SSHR[RA]/USHR[RA] - Vector shift right (optional rounding/accumulate) */ static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u, int immh, int immb, int opcode, int rn, int rd) { - static const GVecGen2i ssra_op[4] = { - { .fni8 = gen_ssra8_i64, - .fniv = gen_ssra_vec, - .load_dest = true, - .opc = INDEX_op_sari_vec, - .vece = MO_8 }, - { .fni8 = gen_ssra16_i64, - .fniv = gen_ssra_vec, - .load_dest = true, - .opc = INDEX_op_sari_vec, - .vece = MO_16 }, - { .fni4 = gen_ssra32_i32, - .fniv = gen_ssra_vec, - .load_dest = true, - .opc = INDEX_op_sari_vec, - .vece = MO_32 }, - { .fni8 = gen_ssra64_i64, - .fniv = gen_ssra_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opc = INDEX_op_sari_vec, - .vece = MO_64 }, - }; - static const GVecGen2i usra_op[4] = { - { .fni8 = gen_usra8_i64, - .fniv = gen_usra_vec, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_8, }, - { .fni8 = gen_usra16_i64, - .fniv = gen_usra_vec, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_16, }, - { .fni4 = gen_usra32_i32, - .fniv = gen_usra_vec, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_32, }, - { .fni8 = gen_usra64_i64, - .fniv = gen_usra_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_64, }, - }; - static const GVecGen2i sri_op[4] = { - { .fni8 = gen_shr8_ins_i64, - .fniv = gen_shr_ins_vec, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_8 }, - { .fni8 = gen_shr16_ins_i64, - .fniv = gen_shr_ins_vec, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_16 }, - { .fni4 = gen_shr32_ins_i32, - .fniv = gen_shr_ins_vec, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_32 }, - { .fni8 = gen_shr64_ins_i64, - .fniv = gen_shr_ins_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .opc = INDEX_op_shri_vec, - .vece = MO_64 }, - }; - int size = 32 - clz32(immh) - 1; int immhb = immh << 3 | immb; int shift = 2 * (8 << size) - immhb; @@ -9607,95 +10245,18 @@ static void handle_vec_simd_shri(DisasContext *s, bool is_q, bool is_u, clear_vec_high(s, is_q, rd); } -static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_8, 0xff << shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); -} - -static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - uint64_t mask = dup_const(MO_16, 0xffff << shift); - TCGv_i64 t = tcg_temp_new_i64(); - - tcg_gen_shli_i64(t, a, shift); - tcg_gen_andi_i64(t, t, mask); - tcg_gen_andi_i64(d, d, ~mask); - tcg_gen_or_i64(d, d, t); - tcg_temp_free_i64(t); -} - -static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) -{ - tcg_gen_deposit_i32(d, d, a, shift, 32 - shift); -} - -static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) -{ - tcg_gen_deposit_i64(d, d, a, shift, 64 - shift); -} - -static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) -{ - uint64_t mask = (1ull << sh) - 1; - TCGv_vec t = tcg_temp_new_vec_matching(d); - TCGv_vec m = tcg_temp_new_vec_matching(d); - - tcg_gen_dupi_vec(vece, m, mask); - tcg_gen_shli_vec(vece, t, a, sh); - tcg_gen_and_vec(vece, d, d, m); - tcg_gen_or_vec(vece, d, d, t); - - tcg_temp_free_vec(t); - tcg_temp_free_vec(m); -} - /* SHL/SLI - Vector shift left */ static void handle_vec_simd_shli(DisasContext *s, bool is_q, bool insert, int immh, int immb, int opcode, int rn, int rd) { - static const GVecGen2i shi_op[4] = { - { .fni8 = gen_shl8_ins_i64, - .fniv = gen_shl_ins_vec, - .opc = INDEX_op_shli_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .vece = MO_8 }, - { .fni8 = gen_shl16_ins_i64, - .fniv = gen_shl_ins_vec, - .opc = INDEX_op_shli_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_shl32_ins_i32, - .fniv = gen_shl_ins_vec, - .opc = INDEX_op_shli_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_shl64_ins_i64, - .fniv = gen_shl_ins_vec, - .opc = INDEX_op_shli_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .vece = MO_64 }, - }; int size = 32 - clz32(immh) - 1; int immhb = immh << 3 | immb; int shift = immhb - (8 << size); - if (extract32(immh, 3, 1) && !is_q) { - unallocated_encoding(s); - return; - } + /* Range of size is limited by decode: immh is a non-zero 4 bit field */ + assert(size >= 0 && size <= 3); - if (size > 3 && !is_q) { + if (extract32(immh, 3, 1) && !is_q) { unallocated_encoding(s); return; } @@ -9705,7 +10266,7 @@ static void handle_vec_simd_shli(DisasContext *s, bool is_q, bool insert, } if (insert) { - gen_gvec_op2i(s, is_q, rd, rn, shift, &shi_op[size]); + gen_gvec_op2i(s, is_q, rd, rn, shift, &sli_op[size]); } else { gen_gvec_fn2i(s, is_q, rd, rn, shift, tcg_gen_gvec_shli, size); } @@ -10282,7 +10843,7 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) return; } if (size == 3) { - if (!arm_dc_feature(s, ARM_FEATURE_V8_PMULL)) { + if (!dc_isar_feature(aa64_pmull, s)) { unallocated_encoding(s); return; } @@ -10327,70 +10888,9 @@ static void disas_simd_three_reg_diff(DisasContext *s, uint32_t insn) } } -static void gen_bsl_i64(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) -{ - tcg_gen_xor_i64(rn, rn, rm); - tcg_gen_and_i64(rn, rn, rd); - tcg_gen_xor_i64(rd, rm, rn); -} - -static void gen_bit_i64(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) -{ - tcg_gen_xor_i64(rn, rn, rd); - tcg_gen_and_i64(rn, rn, rm); - tcg_gen_xor_i64(rd, rd, rn); -} - -static void gen_bif_i64(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) -{ - tcg_gen_xor_i64(rn, rn, rd); - tcg_gen_andc_i64(rn, rn, rm); - tcg_gen_xor_i64(rd, rd, rn); -} - -static void gen_bsl_vec(unsigned vece, TCGv_vec rd, TCGv_vec rn, TCGv_vec rm) -{ - tcg_gen_xor_vec(vece, rn, rn, rm); - tcg_gen_and_vec(vece, rn, rn, rd); - tcg_gen_xor_vec(vece, rd, rm, rn); -} - -static void gen_bit_vec(unsigned vece, TCGv_vec rd, TCGv_vec rn, TCGv_vec rm) -{ - tcg_gen_xor_vec(vece, rn, rn, rd); - tcg_gen_and_vec(vece, rn, rn, rm); - tcg_gen_xor_vec(vece, rd, rd, rn); -} - -static void gen_bif_vec(unsigned vece, TCGv_vec rd, TCGv_vec rn, TCGv_vec rm) -{ - tcg_gen_xor_vec(vece, rn, rn, rd); - tcg_gen_andc_vec(vece, rn, rn, rm); - tcg_gen_xor_vec(vece, rd, rd, rn); -} - /* Logic op (opcode == 3) subgroup of C3.6.16. */ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) { - static const GVecGen3 bsl_op = { - .fni8 = gen_bsl_i64, - .fniv = gen_bsl_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true - }; - static const GVecGen3 bit_op = { - .fni8 = gen_bit_i64, - .fniv = gen_bit_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true - }; - static const GVecGen3 bif_op = { - .fni8 = gen_bif_i64, - .fniv = gen_bif_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true - }; - int rd = extract32(insn, 0, 5); int rn = extract32(insn, 5, 5); int rm = extract32(insn, 16, 5); @@ -10410,11 +10910,7 @@ static void disas_simd_3same_logic(DisasContext *s, uint32_t insn) gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_andc, 0); return; case 2: /* ORR */ - if (rn == rm) { /* MOV */ - gen_gvec_fn2(s, is_q, rd, rn, tcg_gen_gvec_mov, 0); - } else { - gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_or, 0); - } + gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_or, 0); return; case 3: /* ORN */ gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_orc, 0); @@ -10653,140 +11149,38 @@ static void disas_simd_3same_float(DisasContext *s, uint32_t insn) if (!fp_access_check(s)) { return; } - handle_3same_float(s, size, elements, fpopcode, rd, rn, rm); return; + + case 0x1d: /* FMLAL */ + case 0x3d: /* FMLSL */ + case 0x59: /* FMLAL2 */ + case 0x79: /* FMLSL2 */ + if (size & 1 || !dc_isar_feature(aa64_fhm, s)) { + unallocated_encoding(s); + return; + } + if (fp_access_check(s)) { + int is_s = extract32(insn, 23, 1); + int is_2 = extract32(insn, 29, 1); + int data = (is_2 << 1) | is_s; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), cpu_env, + is_q ? 16 : 8, vec_full_reg_size(s), + data, gen_helper_gvec_fmlal_a64); + } + return; + default: unallocated_encoding(s); return; } } -static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u8(a, a, b); - gen_helper_neon_add_u8(d, d, a); -} - -static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u16(a, a, b); - gen_helper_neon_add_u16(d, d, a); -} - -static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mul_i32(a, a, b); - tcg_gen_add_i32(d, d, a); -} - -static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mul_i64(a, a, b); - tcg_gen_add_i64(d, d, a); -} - -static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_mul_vec(vece, a, a, b); - tcg_gen_add_vec(vece, d, d, a); -} - -static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u8(a, a, b); - gen_helper_neon_sub_u8(d, d, a); -} - -static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - gen_helper_neon_mul_u16(a, a, b); - gen_helper_neon_sub_u16(d, d, a); -} - -static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) -{ - tcg_gen_mul_i32(a, a, b); - tcg_gen_sub_i32(d, d, a); -} - -static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) -{ - tcg_gen_mul_i64(a, a, b); - tcg_gen_sub_i64(d, d, a); -} - -static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) -{ - tcg_gen_mul_vec(vece, a, a, b); - tcg_gen_sub_vec(vece, d, d, a); -} - /* Integer op subgroup of C3.6.16. */ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) { - static const GVecGen3 cmtst_op[4] = { - { .fni4 = gen_helper_neon_tst_u8, - .fniv = gen_cmtst_vec, - .vece = MO_8 }, - { .fni4 = gen_helper_neon_tst_u16, - .fniv = gen_cmtst_vec, - .vece = MO_16 }, - { .fni4 = gen_cmtst_i32, - .fniv = gen_cmtst_vec, - .vece = MO_32 }, - { .fni8 = gen_cmtst_i64, - .fniv = gen_cmtst_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .vece = MO_64 }, - }; - static const GVecGen3 mla_op[4] = { - { .fni4 = gen_mla8_i32, - .fniv = gen_mla_vec, - .opc = INDEX_op_mul_vec, - .load_dest = true, - .vece = MO_8 }, - { .fni4 = gen_mla16_i32, - .fniv = gen_mla_vec, - .opc = INDEX_op_mul_vec, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_mla32_i32, - .fniv = gen_mla_vec, - .opc = INDEX_op_mul_vec, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_mla64_i64, - .fniv = gen_mla_vec, - .opc = INDEX_op_mul_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .vece = MO_64 }, - }; - static const GVecGen3 mls_op[4] = { - { .fni4 = gen_mls8_i32, - .fniv = gen_mls_vec, - .opc = INDEX_op_mul_vec, - .load_dest = true, - .vece = MO_8 }, - { .fni4 = gen_mls16_i32, - .fniv = gen_mls_vec, - .opc = INDEX_op_mul_vec, - .load_dest = true, - .vece = MO_16 }, - { .fni4 = gen_mls32_i32, - .fniv = gen_mls_vec, - .opc = INDEX_op_mul_vec, - .load_dest = true, - .vece = MO_32 }, - { .fni8 = gen_mls64_i64, - .fniv = gen_mls_vec, - .opc = INDEX_op_mul_vec, - .prefer_i64 = TCG_TARGET_REG_BITS == 64, - .load_dest = true, - .vece = MO_64 }, - }; - int is_q = extract32(insn, 30, 1); int u = extract32(insn, 29, 1); int size = extract32(insn, 22, 2); @@ -10836,6 +11230,36 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) } switch (opcode) { + case 0x01: /* SQADD, UQADD */ + tcg_gen_gvec_4(vec_full_reg_offset(s, rd), + offsetof(CPUARMState, vfp.qc), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + is_q ? 16 : 8, vec_full_reg_size(s), + (u ? uqadd_op : sqadd_op) + size); + return; + case 0x05: /* SQSUB, UQSUB */ + tcg_gen_gvec_4(vec_full_reg_offset(s, rd), + offsetof(CPUARMState, vfp.qc), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), + is_q ? 16 : 8, vec_full_reg_size(s), + (u ? uqsub_op : sqsub_op) + size); + return; + case 0x0c: /* SMAX, UMAX */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umax, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_smax, size); + } + return; + case 0x0d: /* SMIN, UMIN */ + if (u) { + gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_umin, size); + } else { + gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_smin, size); + } + return; case 0x10: /* ADD, SUB */ if (u) { gen_gvec_fn3(s, is_q, rd, rn, rm, tcg_gen_gvec_sub, size); @@ -10917,16 +11341,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0x1: /* SQADD, UQADD */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qadd_s8, gen_helper_neon_qadd_u8 }, - { gen_helper_neon_qadd_s16, gen_helper_neon_qadd_u16 }, - { gen_helper_neon_qadd_s32, gen_helper_neon_qadd_u32 }, - }; - genenvfn = fns[size][u]; - break; - } case 0x2: /* SRHADD, URHADD */ { static NeonGenTwoOpFn * const fns[3][2] = { @@ -10947,16 +11361,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genfn = fns[size][u]; break; } - case 0x5: /* SQSUB, UQSUB */ - { - static NeonGenTwoOpEnvFn * const fns[3][2] = { - { gen_helper_neon_qsub_s8, gen_helper_neon_qsub_u8 }, - { gen_helper_neon_qsub_s16, gen_helper_neon_qsub_u16 }, - { gen_helper_neon_qsub_s32, gen_helper_neon_qsub_u32 }, - }; - genenvfn = fns[size][u]; - break; - } case 0x8: /* SSHL, USHL */ { static NeonGenTwoOpFn * const fns[3][2] = { @@ -10997,27 +11401,6 @@ static void disas_simd_3same_int(DisasContext *s, uint32_t insn) genenvfn = fns[size][u]; break; } - case 0xc: /* SMAX, UMAX */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_max_s8, gen_helper_neon_max_u8 }, - { gen_helper_neon_max_s16, gen_helper_neon_max_u16 }, - { tcg_gen_smax_i32, tcg_gen_umax_i32 }, - }; - genfn = fns[size][u]; - break; - } - - case 0xd: /* SMIN, UMIN */ - { - static NeonGenTwoOpFn * const fns[3][2] = { - { gen_helper_neon_min_s8, gen_helper_neon_min_u8 }, - { gen_helper_neon_min_s16, gen_helper_neon_min_u16 }, - { tcg_gen_smin_i32, tcg_gen_umin_i32 }, - }; - genfn = fns[size][u]; - break; - } case 0xe: /* SABD, UABD */ case 0xf: /* SABA, UABA */ { @@ -11146,7 +11529,7 @@ static void disas_simd_three_reg_same_fp16(DisasContext *s, uint32_t insn) TCGv_ptr fpst; bool pairwise = false; - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -11334,7 +11717,8 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) int size = extract32(insn, 22, 2); bool u = extract32(insn, 29, 1); bool is_q = extract32(insn, 30, 1); - int feature, rot; + bool feature; + int rot; switch (u * 16 + opcode) { case 0x10: /* SQRDMLAH (vector) */ @@ -11343,7 +11727,7 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - feature = ARM_FEATURE_V8_RDM; + feature = dc_isar_feature(aa64_rdm, s); break; case 0x02: /* SDOT (vector) */ case 0x12: /* UDOT (vector) */ @@ -11351,27 +11735,27 @@ static void disas_simd_three_reg_same_extra(DisasContext *s, uint32_t insn) unallocated_encoding(s); return; } - feature = ARM_FEATURE_V8_DOTPROD; + feature = dc_isar_feature(aa64_dp, s); break; - case 0x8: /* FCMLA, #0 */ - case 0x9: /* FCMLA, #90 */ - case 0xa: /* FCMLA, #180 */ - case 0xb: /* FCMLA, #270 */ - case 0xc: /* FCADD, #90 */ - case 0xe: /* FCADD, #270 */ + case 0x18: /* FCMLA, #0 */ + case 0x19: /* FCMLA, #90 */ + case 0x1a: /* FCMLA, #180 */ + case 0x1b: /* FCMLA, #270 */ + case 0x1c: /* FCADD, #90 */ + case 0x1e: /* FCADD, #270 */ if (size == 0 - || (size == 1 && !arm_dc_feature(s, ARM_FEATURE_V8_FP16)) + || (size == 1 && !dc_isar_feature(aa64_fp16, s)) || (size == 3 && !is_q)) { unallocated_encoding(s); return; } - feature = ARM_FEATURE_V8_FCMA; + feature = dc_isar_feature(aa64_fcma, s); break; default: unallocated_encoding(s); return; } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -11791,8 +12175,7 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } break; case 0xc ... 0xf: - case 0x16 ... 0x1d: - case 0x1f: + case 0x16 ... 0x1f: { /* Floating point: U, size[1] and opcode indicate operation; * size[0] indicates single or double precision. @@ -11935,6 +12318,19 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) } need_fpstatus = true; break; + case 0x1e: /* FRINT32Z */ + case 0x1f: /* FRINT64Z */ + need_rmode = true; + rmode = FPROUNDING_ZERO; + /* fall through */ + case 0x5e: /* FRINT32X */ + case 0x5f: /* FRINT64X */ + need_fpstatus = true; + if ((size == 3 && !is_q) || !dc_isar_feature(aa64_frint, s)) { + unallocated_encoding(s); + return; + } + break; default: unallocated_encoding(s); return; @@ -12100,6 +12496,14 @@ static void disas_simd_two_reg_misc(DisasContext *s, uint32_t insn) case 0x7c: /* URSQRTE */ gen_helper_rsqrte_u32(tcg_res, tcg_op, tcg_fpstatus); break; + case 0x1e: /* FRINT32Z */ + case 0x5e: /* FRINT32X */ + gen_helper_frint32_s(tcg_res, tcg_op, tcg_fpstatus); + break; + case 0x1f: /* FRINT64Z */ + case 0x5f: /* FRINT64X */ + gen_helper_frint64_s(tcg_res, tcg_op, tcg_fpstatus); + break; default: g_assert_not_reached(); } @@ -12240,7 +12644,7 @@ static void disas_simd_two_reg_misc_fp16(DisasContext *s, uint32_t insn) bool need_fpst = true; int rmode; - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -12585,14 +12989,14 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) break; case 0x1d: /* SQRDMLAH */ case 0x1f: /* SQRDMLSH */ - if (!arm_dc_feature(s, ARM_FEATURE_V8_RDM)) { + if (!dc_isar_feature(aa64_rdm, s)) { unallocated_encoding(s); return; } break; case 0x0e: /* SDOT */ case 0x1e: /* UDOT */ - if (size != MO_32 || !arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_dp, s)) { unallocated_encoding(s); return; } @@ -12601,12 +13005,23 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 0x13: /* FCMLA #90 */ case 0x15: /* FCMLA #180 */ case 0x17: /* FCMLA #270 */ - if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)) { + if (is_scalar || !dc_isar_feature(aa64_fcma, s)) { unallocated_encoding(s); return; } is_fp = 2; break; + case 0x00: /* FMLAL */ + case 0x04: /* FMLSL */ + case 0x18: /* FMLAL2 */ + case 0x1c: /* FMLSL2 */ + if (is_scalar || size != MO_32 || !dc_isar_feature(aa64_fhm, s)) { + unallocated_encoding(s); + return; + } + size = MO_16; + /* is_fp, but we pass cpu_env not fp_status. */ + break; default: unallocated_encoding(s); return; @@ -12631,7 +13046,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) case 2: /* complex fp */ /* Each indexable element is a complex pair. */ - size <<= 1; + size += 1; switch (size) { case MO_32: if (h && !is_q) { @@ -12657,7 +13072,7 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) } break; } - if (is_fp16 && !arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (is_fp16 && !dc_isar_feature(aa64_fp16, s)) { unallocated_encoding(s); return; } @@ -12717,6 +13132,22 @@ static void disas_simd_indexed(DisasContext *s, uint32_t insn) tcg_temp_free_ptr(fpst); } return; + + case 0x00: /* FMLAL */ + case 0x04: /* FMLSL */ + case 0x18: /* FMLAL2 */ + case 0x1c: /* FMLSL2 */ + { + int is_s = extract32(opcode, 2, 1); + int is_2 = u; + int data = (index << 2) | (is_2 << 1) | is_s; + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, rd), + vec_full_reg_offset(s, rn), + vec_full_reg_offset(s, rm), cpu_env, + is_q ? 16 : 8, vec_full_reg_size(s), + data, gen_helper_gvec_fmlal_idx_a64); + } + return; } if (size == 3) { @@ -13128,8 +13559,7 @@ static void disas_crypto_aes(DisasContext *s, uint32_t insn) TCGv_i32 tcg_decrypt; CryptoThreeOpIntFn *genfn; - if (!arm_dc_feature(s, ARM_FEATURE_V8_AES) - || size != 0) { + if (!dc_isar_feature(aa64_aes, s) || size != 0) { unallocated_encoding(s); return; } @@ -13186,7 +13616,7 @@ static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn) int rd = extract32(insn, 0, 5); CryptoThreeOpFn *genfn; TCGv_ptr tcg_rd_ptr, tcg_rn_ptr, tcg_rm_ptr; - int feature = ARM_FEATURE_V8_SHA256; + bool feature; if (size != 0) { unallocated_encoding(s); @@ -13199,23 +13629,26 @@ static void disas_crypto_three_reg_sha(DisasContext *s, uint32_t insn) case 2: /* SHA1M */ case 3: /* SHA1SU0 */ genfn = NULL; - feature = ARM_FEATURE_V8_SHA1; + feature = dc_isar_feature(aa64_sha1, s); break; case 4: /* SHA256H */ genfn = gen_helper_crypto_sha256h; + feature = dc_isar_feature(aa64_sha256, s); break; case 5: /* SHA256H2 */ genfn = gen_helper_crypto_sha256h2; + feature = dc_isar_feature(aa64_sha256, s); break; case 6: /* SHA256SU1 */ genfn = gen_helper_crypto_sha256su1; + feature = dc_isar_feature(aa64_sha256, s); break; default: unallocated_encoding(s); return; } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -13256,7 +13689,7 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); CryptoTwoOpFn *genfn; - int feature; + bool feature; TCGv_ptr tcg_rd_ptr, tcg_rn_ptr; if (size != 0) { @@ -13266,15 +13699,15 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn) switch (opcode) { case 0: /* SHA1H */ - feature = ARM_FEATURE_V8_SHA1; + feature = dc_isar_feature(aa64_sha1, s); genfn = gen_helper_crypto_sha1h; break; case 1: /* SHA1SU1 */ - feature = ARM_FEATURE_V8_SHA1; + feature = dc_isar_feature(aa64_sha1, s); genfn = gen_helper_crypto_sha1su1; break; case 2: /* SHA256SU0 */ - feature = ARM_FEATURE_V8_SHA256; + feature = dc_isar_feature(aa64_sha256, s); genfn = gen_helper_crypto_sha256su0; break; default: @@ -13282,7 +13715,7 @@ static void disas_crypto_two_reg_sha(DisasContext *s, uint32_t insn) return; } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -13313,40 +13746,40 @@ static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn) int rm = extract32(insn, 16, 5); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - int feature; + bool feature; CryptoThreeOpFn *genfn; if (o == 0) { switch (opcode) { case 0: /* SHA512H */ - feature = ARM_FEATURE_V8_SHA512; + feature = dc_isar_feature(aa64_sha512, s); genfn = gen_helper_crypto_sha512h; break; case 1: /* SHA512H2 */ - feature = ARM_FEATURE_V8_SHA512; + feature = dc_isar_feature(aa64_sha512, s); genfn = gen_helper_crypto_sha512h2; break; case 2: /* SHA512SU1 */ - feature = ARM_FEATURE_V8_SHA512; + feature = dc_isar_feature(aa64_sha512, s); genfn = gen_helper_crypto_sha512su1; break; case 3: /* RAX1 */ - feature = ARM_FEATURE_V8_SHA3; + feature = dc_isar_feature(aa64_sha3, s); genfn = NULL; break; } } else { switch (opcode) { case 0: /* SM3PARTW1 */ - feature = ARM_FEATURE_V8_SM3; + feature = dc_isar_feature(aa64_sm3, s); genfn = gen_helper_crypto_sm3partw1; break; case 1: /* SM3PARTW2 */ - feature = ARM_FEATURE_V8_SM3; + feature = dc_isar_feature(aa64_sm3, s); genfn = gen_helper_crypto_sm3partw2; break; case 2: /* SM4EKEY */ - feature = ARM_FEATURE_V8_SM4; + feature = dc_isar_feature(aa64_sm4, s); genfn = gen_helper_crypto_sm4ekey; break; default: @@ -13355,7 +13788,7 @@ static void disas_crypto_three_reg_sha512(DisasContext *s, uint32_t insn) } } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -13414,16 +13847,16 @@ static void disas_crypto_two_reg_sha512(DisasContext *s, uint32_t insn) int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); TCGv_ptr tcg_rd_ptr, tcg_rn_ptr; - int feature; + bool feature; CryptoTwoOpFn *genfn; switch (opcode) { case 0: /* SHA512SU0 */ - feature = ARM_FEATURE_V8_SHA512; + feature = dc_isar_feature(aa64_sha512, s); genfn = gen_helper_crypto_sha512su0; break; case 1: /* SM4E */ - feature = ARM_FEATURE_V8_SM4; + feature = dc_isar_feature(aa64_sm4, s); genfn = gen_helper_crypto_sm4e; break; default: @@ -13431,7 +13864,7 @@ static void disas_crypto_two_reg_sha512(DisasContext *s, uint32_t insn) return; } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -13462,22 +13895,22 @@ static void disas_crypto_four_reg(DisasContext *s, uint32_t insn) int ra = extract32(insn, 10, 5); int rn = extract32(insn, 5, 5); int rd = extract32(insn, 0, 5); - int feature; + bool feature; switch (op0) { case 0: /* EOR3 */ case 1: /* BCAX */ - feature = ARM_FEATURE_V8_SHA3; + feature = dc_isar_feature(aa64_sha3, s); break; case 2: /* SM3SS1 */ - feature = ARM_FEATURE_V8_SM3; + feature = dc_isar_feature(aa64_sm3, s); break; default: unallocated_encoding(s); return; } - if (!arm_dc_feature(s, feature)) { + if (!feature) { unallocated_encoding(s); return; } @@ -13564,7 +13997,7 @@ static void disas_crypto_xar(DisasContext *s, uint32_t insn) TCGv_i64 tcg_op1, tcg_op2, tcg_res[2]; int pass; - if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA3)) { + if (!dc_isar_feature(aa64_sha3, s)) { unallocated_encoding(s); return; } @@ -13610,7 +14043,7 @@ static void disas_crypto_three_reg_imm2(DisasContext *s, uint32_t insn) TCGv_ptr tcg_rd_ptr, tcg_rn_ptr, tcg_rm_ptr; TCGv_i32 tcg_imm2, tcg_opcode; - if (!arm_dc_feature(s, ARM_FEATURE_V8_SM3)) { + if (!dc_isar_feature(aa64_sm3, s)) { unallocated_encoding(s); return; } @@ -13702,6 +14135,90 @@ static void disas_data_proc_simd_fp(DisasContext *s, uint32_t insn) } } +/** + * is_guarded_page: + * @env: The cpu environment + * @s: The DisasContext + * + * Return true if the page is guarded. + */ +static bool is_guarded_page(CPUARMState *env, DisasContext *s) +{ +#ifdef CONFIG_USER_ONLY + return false; /* FIXME */ +#else + uint64_t addr = s->base.pc_first; + int mmu_idx = arm_to_core_mmu_idx(s->mmu_idx); + unsigned int index = tlb_index(env, mmu_idx, addr); + CPUTLBEntry *entry = tlb_entry(env, mmu_idx, addr); + + /* + * We test this immediately after reading an insn, which means + * that any normal page must be in the TLB. The only exception + * would be for executing from flash or device memory, which + * does not retain the TLB entry. + * + * FIXME: Assume false for those, for now. We could use + * arm_cpu_get_phys_page_attrs_debug to re-read the page + * table entry even for that case. + */ + return (tlb_hit(entry->addr_code, addr) && + env->iotlb[mmu_idx][index].attrs.target_tlb_bit0); +#endif +} + +/** + * btype_destination_ok: + * @insn: The instruction at the branch destination + * @bt: SCTLR_ELx.BT + * @btype: PSTATE.BTYPE, and is non-zero + * + * On a guarded page, there are a limited number of insns + * that may be present at the branch target: + * - branch target identifiers, + * - paciasp, pacibsp, + * - BRK insn + * - HLT insn + * Anything else causes a Branch Target Exception. + * + * Return true if the branch is compatible, false to raise BTITRAP. + */ +static bool btype_destination_ok(uint32_t insn, bool bt, int btype) +{ + if ((insn & 0xfffff01fu) == 0xd503201fu) { + /* HINT space */ + switch (extract32(insn, 5, 7)) { + case 0b011001: /* PACIASP */ + case 0b011011: /* PACIBSP */ + /* + * If SCTLR_ELx.BT, then PACI*SP are not compatible + * with btype == 3. Otherwise all btype are ok. + */ + return !bt || btype != 3; + case 0b100000: /* BTI */ + /* Not compatible with any btype. */ + return false; + case 0b100010: /* BTI c */ + /* Not compatible with btype == 3 */ + return btype != 3; + case 0b100100: /* BTI j */ + /* Not compatible with btype == 2 */ + return btype != 2; + case 0b100110: /* BTI jc */ + /* Compatible with any btype. */ + return true; + } + } else { + switch (insn & 0xffe0001fu) { + case 0xd4200000u: /* BRK */ + case 0xd4400000u: /* HLT */ + /* Give priority to the breakpoint exception. */ + return true; + } + } + return false; +} + /* C3.1 A64 instruction index by encoding */ static void disas_a64_insn(CPUARMState *env, DisasContext *s) { @@ -13713,12 +14230,49 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) s->fp_access_checked = false; + if (dc_isar_feature(aa64_bti, s)) { + if (s->base.num_insns == 1) { + /* + * At the first insn of the TB, compute s->guarded_page. + * We delayed computing this until successfully reading + * the first insn of the TB, above. This (mostly) ensures + * that the softmmu tlb entry has been populated, and the + * page table GP bit is available. + * + * Note that we need to compute this even if btype == 0, + * because this value is used for BR instructions later + * where ENV is not available. + */ + s->guarded_page = is_guarded_page(env, s); + + /* First insn can have btype set to non-zero. */ + tcg_debug_assert(s->btype >= 0); + + /* + * Note that the Branch Target Exception has fairly high + * priority -- below debugging exceptions but above most + * everything else. This allows us to handle this now + * instead of waiting until the insn is otherwise decoded. + */ + if (s->btype != 0 + && s->guarded_page + && !btype_destination_ok(insn, s->bt, s->btype)) { + gen_exception_insn(s, 4, EXCP_UDEF, syn_btitrap(s->btype), + default_exception_el(s)); + return; + } + } else { + /* Not the first insn: btype must be 0. */ + tcg_debug_assert(s->btype == 0); + } + } + switch (extract32(insn, 25, 4)) { case 0x0: case 0x1: case 0x3: /* UNALLOCATED */ unallocated_encoding(s); break; case 0x2: - if (!arm_dc_feature(s, ARM_FEATURE_SVE) || !disas_sve(s, insn)) { + if (!dc_isar_feature(aa64_sve, s) || !disas_sve(s, insn)) { unallocated_encoding(s); } break; @@ -13749,6 +14303,14 @@ static void disas_a64_insn(CPUARMState *env, DisasContext *s) /* if we allocated any temporaries, free them here */ free_tmp_a64(s); + + /* + * After execution of most insns, btype is reset to 0. + * Note that we set btype == -1 when the insn sets btype. + */ + if (s->btype > 0 && s->base.is_jmp != DISAS_NORETURN) { + reset_btype(s); + } } static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, @@ -13757,8 +14319,10 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cpu->env_ptr; ARMCPU *arm_cpu = arm_env_get_cpu(env); - int bound; + uint32_t tb_flags = dc->base.tb->flags; + int bound, core_mmu_idx; + dc->isar = &arm_cpu->isar; dc->pc = dc->base.pc_first; dc->condjmp = 0; @@ -13770,19 +14334,23 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, !arm_el_is_aa64(env, 3); dc->thumb = 0; dc->sctlr_b = 0; - dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE; + dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; dc->condexec_mask = 0; dc->condexec_cond = 0; - dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags)); - dc->tbi0 = ARM_TBFLAG_TBI0(dc->base.tb->flags); - dc->tbi1 = ARM_TBFLAG_TBI1(dc->base.tb->flags); + core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX); + dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); + dc->tbii = FIELD_EX32(tb_flags, TBFLAG_A64, TBII); + dc->tbid = FIELD_EX32(tb_flags, TBFLAG_A64, TBID); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags); - dc->sve_excp_el = ARM_TBFLAG_SVEEXC_EL(dc->base.tb->flags); - dc->sve_len = (ARM_TBFLAG_ZCR_LEN(dc->base.tb->flags) + 1) * 16; + dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL); + dc->sve_excp_el = FIELD_EX32(tb_flags, TBFLAG_A64, SVEEXC_EL); + dc->sve_len = (FIELD_EX32(tb_flags, TBFLAG_A64, ZCR_LEN) + 1) * 16; + dc->pauth_active = FIELD_EX32(tb_flags, TBFLAG_A64, PAUTH_ACTIVE); + dc->bt = FIELD_EX32(tb_flags, TBFLAG_A64, BT); + dc->btype = FIELD_EX32(tb_flags, TBFLAG_A64, BTYPE); dc->vec_len = 0; dc->vec_stride = 0; dc->cp_regs = arm_cpu->cp_regs; @@ -13803,8 +14371,8 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, * emit code to generate a software step exception * end the TB */ - dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags); - dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags); + dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE); + dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS); dc->is_ldex = false; dc->ss_same_el = (arm_debug_target_el(env) == dc->current_el); @@ -13822,7 +14390,6 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, static void aarch64_tr_tb_start(DisasContextBase *db, CPUState *cpu) { - tcg_clear_temp_count(); } static void aarch64_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) diff --git a/target/arm/translate-sve.c b/target/arm/translate-sve.c index 374051cd20..245cd82621 100644 --- a/target/arm/translate-sve.c +++ b/target/arm/translate-sve.c @@ -273,26 +273,22 @@ const uint64_t pred_esz_masks[4] = { *** SVE Logical - Unpredicated Group */ -static bool trans_AND_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_AND_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_and, 0, a->rd, a->rn, a->rm); } -static bool trans_ORR_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_ORR_zzz(DisasContext *s, arg_rrr_esz *a) { - if (a->rn == a->rm) { /* MOV */ - return do_mov_z(s, a->rd, a->rn); - } else { - return do_vector3_z(s, tcg_gen_gvec_or, 0, a->rd, a->rn, a->rm); - } + return do_vector3_z(s, tcg_gen_gvec_or, 0, a->rd, a->rn, a->rm); } -static bool trans_EOR_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_EOR_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_xor, 0, a->rd, a->rn, a->rm); } -static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_andc, 0, a->rd, a->rn, a->rm); } @@ -301,32 +297,32 @@ static bool trans_BIC_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) *** SVE Integer Arithmetic - Unpredicated Group */ -static bool trans_ADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_ADD_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_add, a->esz, a->rd, a->rn, a->rm); } -static bool trans_SUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_SUB_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_sub, a->esz, a->rd, a->rn, a->rm); } -static bool trans_SQADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_SQADD_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_ssadd, a->esz, a->rd, a->rn, a->rm); } -static bool trans_SQSUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_SQSUB_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_sssub, a->esz, a->rd, a->rn, a->rm); } -static bool trans_UQADD_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_UQADD_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_usadd, a->esz, a->rd, a->rn, a->rm); } -static bool trans_UQSUB_zzz(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_UQSUB_zzz(DisasContext *s, arg_rrr_esz *a) { return do_vector3_z(s, tcg_gen_gvec_ussub, a->esz, a->rd, a->rn, a->rm); } @@ -369,8 +365,7 @@ static void do_sel_z(DisasContext *s, int rd, int rn, int rm, int pg, int esz) } #define DO_ZPZZ(NAME, name) \ -static bool trans_##NAME##_zpzz(DisasContext *s, arg_rprr_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_zpzz(DisasContext *s, arg_rprr_esz *a) \ { \ static gen_helper_gvec_4 * const fns[4] = { \ gen_helper_sve_##name##_zpzz_b, gen_helper_sve_##name##_zpzz_h, \ @@ -402,7 +397,7 @@ DO_ZPZZ(ASR, asr) DO_ZPZZ(LSR, lsr) DO_ZPZZ(LSL, lsl) -static bool trans_SDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_SDIV_zpzz(DisasContext *s, arg_rprr_esz *a) { static gen_helper_gvec_4 * const fns[4] = { NULL, NULL, gen_helper_sve_sdiv_zpzz_s, gen_helper_sve_sdiv_zpzz_d @@ -410,7 +405,7 @@ static bool trans_SDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) return do_zpzz_ool(s, a, fns[a->esz]); } -static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a) { static gen_helper_gvec_4 * const fns[4] = { NULL, NULL, gen_helper_sve_udiv_zpzz_s, gen_helper_sve_udiv_zpzz_d @@ -418,7 +413,7 @@ static bool trans_UDIV_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) return do_zpzz_ool(s, a, fns[a->esz]); } -static bool trans_SEL_zpzz(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_SEL_zpzz(DisasContext *s, arg_rprr_esz *a) { if (sve_access_check(s)) { do_sel_z(s, a->rd, a->rn, a->rm, a->pg, a->esz); @@ -448,7 +443,7 @@ static bool do_zpz_ool(DisasContext *s, arg_rpr_esz *a, gen_helper_gvec_3 *fn) } #define DO_ZPZ(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a) \ { \ static gen_helper_gvec_3 * const fns[4] = { \ gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ @@ -465,7 +460,7 @@ DO_ZPZ(NOT_zpz, not_zpz) DO_ZPZ(ABS, abs) DO_ZPZ(NEG, neg) -static bool trans_FABS(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FABS(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -476,7 +471,7 @@ static bool trans_FABS(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_FNEG(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FNEG(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -487,7 +482,7 @@ static bool trans_FNEG(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_SXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SXTB(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -498,7 +493,7 @@ static bool trans_SXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_UXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UXTB(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -509,7 +504,7 @@ static bool trans_UXTB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_SXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SXTH(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, NULL, @@ -519,7 +514,7 @@ static bool trans_SXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_UXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UXTH(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, NULL, @@ -529,12 +524,12 @@ static bool trans_UXTH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_SXTW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SXTW(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_sxtw_d : NULL); } -static bool trans_UXTW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UXTW(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_uxtw_d : NULL); } @@ -579,7 +574,7 @@ static bool do_vpz_ool(DisasContext *s, arg_rpr_esz *a, } #define DO_VPZ(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a) \ { \ static gen_helper_gvec_reduc * const fns[4] = { \ gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ @@ -598,7 +593,7 @@ DO_VPZ(UMAXV, umaxv) DO_VPZ(SMINV, sminv) DO_VPZ(UMINV, uminv) -static bool trans_SADDV(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SADDV(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_reduc * const fns[4] = { gen_helper_sve_saddv_b, gen_helper_sve_saddv_h, @@ -659,7 +654,7 @@ static bool do_zpzi_ool(DisasContext *s, arg_rpri_esz *a, return true; } -static bool trans_ASR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +static bool trans_ASR_zpzi(DisasContext *s, arg_rpri_esz *a) { static gen_helper_gvec_3 * const fns[4] = { gen_helper_sve_asr_zpzi_b, gen_helper_sve_asr_zpzi_h, @@ -675,7 +670,7 @@ static bool trans_ASR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) return do_zpzi_ool(s, a, fns[a->esz]); } -static bool trans_LSR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +static bool trans_LSR_zpzi(DisasContext *s, arg_rpri_esz *a) { static gen_helper_gvec_3 * const fns[4] = { gen_helper_sve_lsr_zpzi_b, gen_helper_sve_lsr_zpzi_h, @@ -693,7 +688,7 @@ static bool trans_LSR_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) } } -static bool trans_LSL_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +static bool trans_LSL_zpzi(DisasContext *s, arg_rpri_esz *a) { static gen_helper_gvec_3 * const fns[4] = { gen_helper_sve_lsl_zpzi_b, gen_helper_sve_lsl_zpzi_h, @@ -711,7 +706,7 @@ static bool trans_LSL_zpzi(DisasContext *s, arg_rpri_esz *a, uint32_t insn) } } -static bool trans_ASRD(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +static bool trans_ASRD(DisasContext *s, arg_rpri_esz *a) { static gen_helper_gvec_3 * const fns[4] = { gen_helper_sve_asrd_b, gen_helper_sve_asrd_h, @@ -734,8 +729,7 @@ static bool trans_ASRD(DisasContext *s, arg_rpri_esz *a, uint32_t insn) */ #define DO_ZPZW(NAME, name) \ -static bool trans_##NAME##_zpzw(DisasContext *s, arg_rprr_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_zpzw(DisasContext *s, arg_rprr_esz *a) \ { \ static gen_helper_gvec_4 * const fns[3] = { \ gen_helper_sve_##name##_zpzw_b, gen_helper_sve_##name##_zpzw_h, \ @@ -784,17 +778,17 @@ static bool do_shift_imm(DisasContext *s, arg_rri_esz *a, bool asr, return true; } -static bool trans_ASR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_ASR_zzi(DisasContext *s, arg_rri_esz *a) { return do_shift_imm(s, a, true, tcg_gen_gvec_sari); } -static bool trans_LSR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_LSR_zzi(DisasContext *s, arg_rri_esz *a) { return do_shift_imm(s, a, false, tcg_gen_gvec_shri); } -static bool trans_LSL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_LSL_zzi(DisasContext *s, arg_rri_esz *a) { return do_shift_imm(s, a, false, tcg_gen_gvec_shli); } @@ -815,8 +809,7 @@ static bool do_zzw_ool(DisasContext *s, arg_rrr_esz *a, gen_helper_gvec_3 *fn) } #define DO_ZZW(NAME, name) \ -static bool trans_##NAME##_zzw(DisasContext *s, arg_rrr_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_zzw(DisasContext *s, arg_rrr_esz *a) \ { \ static gen_helper_gvec_3 * const fns[4] = { \ gen_helper_sve_##name##_zzw_b, gen_helper_sve_##name##_zzw_h, \ @@ -851,7 +844,7 @@ static bool do_zpzzz_ool(DisasContext *s, arg_rprrr_esz *a, } #define DO_ZPZZZ(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a) \ { \ static gen_helper_gvec_5 * const fns[4] = { \ gen_helper_sve_##name##_b, gen_helper_sve_##name##_h, \ @@ -900,7 +893,7 @@ static void do_index(DisasContext *s, int esz, int rd, tcg_temp_free_i32(desc); } -static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a, uint32_t insn) +static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a) { if (sve_access_check(s)) { TCGv_i64 start = tcg_const_i64(a->imm1); @@ -912,7 +905,7 @@ static bool trans_INDEX_ii(DisasContext *s, arg_INDEX_ii *a, uint32_t insn) return true; } -static bool trans_INDEX_ir(DisasContext *s, arg_INDEX_ir *a, uint32_t insn) +static bool trans_INDEX_ir(DisasContext *s, arg_INDEX_ir *a) { if (sve_access_check(s)) { TCGv_i64 start = tcg_const_i64(a->imm); @@ -923,7 +916,7 @@ static bool trans_INDEX_ir(DisasContext *s, arg_INDEX_ir *a, uint32_t insn) return true; } -static bool trans_INDEX_ri(DisasContext *s, arg_INDEX_ri *a, uint32_t insn) +static bool trans_INDEX_ri(DisasContext *s, arg_INDEX_ri *a) { if (sve_access_check(s)) { TCGv_i64 start = cpu_reg(s, a->rn); @@ -934,7 +927,7 @@ static bool trans_INDEX_ri(DisasContext *s, arg_INDEX_ri *a, uint32_t insn) return true; } -static bool trans_INDEX_rr(DisasContext *s, arg_INDEX_rr *a, uint32_t insn) +static bool trans_INDEX_rr(DisasContext *s, arg_INDEX_rr *a) { if (sve_access_check(s)) { TCGv_i64 start = cpu_reg(s, a->rn); @@ -948,26 +941,32 @@ static bool trans_INDEX_rr(DisasContext *s, arg_INDEX_rr *a, uint32_t insn) *** SVE Stack Allocation Group */ -static bool trans_ADDVL(DisasContext *s, arg_ADDVL *a, uint32_t insn) +static bool trans_ADDVL(DisasContext *s, arg_ADDVL *a) { - TCGv_i64 rd = cpu_reg_sp(s, a->rd); - TCGv_i64 rn = cpu_reg_sp(s, a->rn); - tcg_gen_addi_i64(rd, rn, a->imm * vec_full_reg_size(s)); + if (sve_access_check(s)) { + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * vec_full_reg_size(s)); + } return true; } -static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a, uint32_t insn) +static bool trans_ADDPL(DisasContext *s, arg_ADDPL *a) { - TCGv_i64 rd = cpu_reg_sp(s, a->rd); - TCGv_i64 rn = cpu_reg_sp(s, a->rn); - tcg_gen_addi_i64(rd, rn, a->imm * pred_full_reg_size(s)); + if (sve_access_check(s)) { + TCGv_i64 rd = cpu_reg_sp(s, a->rd); + TCGv_i64 rn = cpu_reg_sp(s, a->rn); + tcg_gen_addi_i64(rd, rn, a->imm * pred_full_reg_size(s)); + } return true; } -static bool trans_RDVL(DisasContext *s, arg_RDVL *a, uint32_t insn) +static bool trans_RDVL(DisasContext *s, arg_RDVL *a) { - TCGv_i64 reg = cpu_reg(s, a->rd); - tcg_gen_movi_i64(reg, a->imm * vec_full_reg_size(s)); + if (sve_access_check(s)) { + TCGv_i64 reg = cpu_reg(s, a->rd); + tcg_gen_movi_i64(reg, a->imm * vec_full_reg_size(s)); + } return true; } @@ -987,22 +986,22 @@ static bool do_adr(DisasContext *s, arg_rrri *a, gen_helper_gvec_3 *fn) return true; } -static bool trans_ADR_p32(DisasContext *s, arg_rrri *a, uint32_t insn) +static bool trans_ADR_p32(DisasContext *s, arg_rrri *a) { return do_adr(s, a, gen_helper_sve_adr_p32); } -static bool trans_ADR_p64(DisasContext *s, arg_rrri *a, uint32_t insn) +static bool trans_ADR_p64(DisasContext *s, arg_rrri *a) { return do_adr(s, a, gen_helper_sve_adr_p64); } -static bool trans_ADR_s32(DisasContext *s, arg_rrri *a, uint32_t insn) +static bool trans_ADR_s32(DisasContext *s, arg_rrri *a) { return do_adr(s, a, gen_helper_sve_adr_s32); } -static bool trans_ADR_u32(DisasContext *s, arg_rrri *a, uint32_t insn) +static bool trans_ADR_u32(DisasContext *s, arg_rrri *a) { return do_adr(s, a, gen_helper_sve_adr_u32); } @@ -1011,7 +1010,7 @@ static bool trans_ADR_u32(DisasContext *s, arg_rrri *a, uint32_t insn) *** SVE Integer Misc - Unpredicated Group */ -static bool trans_FEXPA(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_FEXPA(DisasContext *s, arg_rr_esz *a) { static gen_helper_gvec_2 * const fns[4] = { NULL, @@ -1031,7 +1030,7 @@ static bool trans_FEXPA(DisasContext *s, arg_rr_esz *a, uint32_t insn) return true; } -static bool trans_FTSSEL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_FTSSEL(DisasContext *s, arg_rrr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -1120,7 +1119,7 @@ static void gen_and_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_and_vec(vece, pd, pd, pg); } -static bool trans_AND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_AND_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_and_pg_i64, @@ -1156,7 +1155,7 @@ static void gen_bic_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_and_vec(vece, pd, pd, pg); } -static bool trans_BIC_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_BIC_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_bic_pg_i64, @@ -1186,7 +1185,7 @@ static void gen_eor_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_and_vec(vece, pd, pd, pg); } -static bool trans_EOR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_EOR_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_eor_pg_i64, @@ -1216,7 +1215,7 @@ static void gen_sel_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_or_vec(vece, pd, pn, pm); } -static bool trans_SEL_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_SEL_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_sel_pg_i64, @@ -1244,7 +1243,7 @@ static void gen_orr_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_and_vec(vece, pd, pd, pg); } -static bool trans_ORR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_ORR_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_orr_pg_i64, @@ -1274,7 +1273,7 @@ static void gen_orn_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_and_vec(vece, pd, pd, pg); } -static bool trans_ORN_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_ORN_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_orn_pg_i64, @@ -1302,7 +1301,7 @@ static void gen_nor_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_andc_vec(vece, pd, pg, pd); } -static bool trans_NOR_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_NOR_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_nor_pg_i64, @@ -1330,7 +1329,7 @@ static void gen_nand_pg_vec(unsigned vece, TCGv_vec pd, TCGv_vec pn, tcg_gen_andc_vec(vece, pd, pg, pd); } -static bool trans_NAND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_NAND_pppp(DisasContext *s, arg_rprr_s *a) { static const GVecGen4 op = { .fni8 = gen_nand_pg_i64, @@ -1349,7 +1348,7 @@ static bool trans_NAND_pppp(DisasContext *s, arg_rprr_s *a, uint32_t insn) *** SVE Predicate Misc Group */ -static bool trans_PTEST(DisasContext *s, arg_PTEST *a, uint32_t insn) +static bool trans_PTEST(DisasContext *s, arg_PTEST *a) { if (sve_access_check(s)) { int nofs = pred_full_reg_offset(s, a->rn); @@ -1491,24 +1490,24 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) return true; } -static bool trans_PTRUE(DisasContext *s, arg_PTRUE *a, uint32_t insn) +static bool trans_PTRUE(DisasContext *s, arg_PTRUE *a) { return do_predset(s, a->esz, a->rd, a->pat, a->s); } -static bool trans_SETFFR(DisasContext *s, arg_SETFFR *a, uint32_t insn) +static bool trans_SETFFR(DisasContext *s, arg_SETFFR *a) { /* Note pat == 31 is #all, to set all elements. */ return do_predset(s, 0, FFR_PRED_NUM, 31, false); } -static bool trans_PFALSE(DisasContext *s, arg_PFALSE *a, uint32_t insn) +static bool trans_PFALSE(DisasContext *s, arg_PFALSE *a) { /* Note pat == 32 is #unimp, to set no elements. */ return do_predset(s, 0, a->rd, 32, false); } -static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a, uint32_t insn) +static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a) { /* The path through do_pppp_flags is complicated enough to want to avoid * duplication. Frob the arguments into the form of a predicated AND. @@ -1517,15 +1516,15 @@ static bool trans_RDFFR_p(DisasContext *s, arg_RDFFR_p *a, uint32_t insn) .rd = a->rd, .pg = a->pg, .s = a->s, .rn = FFR_PRED_NUM, .rm = FFR_PRED_NUM, }; - return trans_AND_pppp(s, &alt_a, insn); + return trans_AND_pppp(s, &alt_a); } -static bool trans_RDFFR(DisasContext *s, arg_RDFFR *a, uint32_t insn) +static bool trans_RDFFR(DisasContext *s, arg_RDFFR *a) { return do_mov_p(s, a->rd, FFR_PRED_NUM); } -static bool trans_WRFFR(DisasContext *s, arg_WRFFR *a, uint32_t insn) +static bool trans_WRFFR(DisasContext *s, arg_WRFFR *a) { return do_mov_p(s, FFR_PRED_NUM, a->rn); } @@ -1559,12 +1558,12 @@ static bool do_pfirst_pnext(DisasContext *s, arg_rr_esz *a, return true; } -static bool trans_PFIRST(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_PFIRST(DisasContext *s, arg_rr_esz *a) { return do_pfirst_pnext(s, a, gen_helper_sve_pfirst); } -static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_PNEXT(DisasContext *s, arg_rr_esz *a) { return do_pfirst_pnext(s, a, gen_helper_sve_pnext); } @@ -1625,7 +1624,7 @@ static void do_sat_addsub_64(TCGv_i64 reg, TCGv_i64 val, bool u, bool d) /* Detect signed overflow for subtraction. */ tcg_gen_xor_i64(t0, reg, val); tcg_gen_sub_i64(t1, reg, val); - tcg_gen_xor_i64(reg, reg, t0); + tcg_gen_xor_i64(reg, reg, t1); tcg_gen_and_i64(t0, t0, reg); /* Bound the result. */ @@ -1735,7 +1734,7 @@ static void do_sat_addsub_vec(DisasContext *s, int esz, int rd, int rn, tcg_temp_free_i32(desc); } -static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a, uint32_t insn) +static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a) { if (sve_access_check(s)) { unsigned fullsz = vec_full_reg_size(s); @@ -1745,7 +1744,7 @@ static bool trans_CNT_r(DisasContext *s, arg_CNT_r *a, uint32_t insn) return true; } -static bool trans_INCDEC_r(DisasContext *s, arg_incdec_cnt *a, uint32_t insn) +static bool trans_INCDEC_r(DisasContext *s, arg_incdec_cnt *a) { if (sve_access_check(s)) { unsigned fullsz = vec_full_reg_size(s); @@ -1758,8 +1757,7 @@ static bool trans_INCDEC_r(DisasContext *s, arg_incdec_cnt *a, uint32_t insn) return true; } -static bool trans_SINCDEC_r_32(DisasContext *s, arg_incdec_cnt *a, - uint32_t insn) +static bool trans_SINCDEC_r_32(DisasContext *s, arg_incdec_cnt *a) { if (!sve_access_check(s)) { return true; @@ -1785,8 +1783,7 @@ static bool trans_SINCDEC_r_32(DisasContext *s, arg_incdec_cnt *a, return true; } -static bool trans_SINCDEC_r_64(DisasContext *s, arg_incdec_cnt *a, - uint32_t insn) +static bool trans_SINCDEC_r_64(DisasContext *s, arg_incdec_cnt *a) { if (!sve_access_check(s)) { return true; @@ -1805,7 +1802,7 @@ static bool trans_SINCDEC_r_64(DisasContext *s, arg_incdec_cnt *a, return true; } -static bool trans_INCDEC_v(DisasContext *s, arg_incdec2_cnt *a, uint32_t insn) +static bool trans_INCDEC_v(DisasContext *s, arg_incdec2_cnt *a) { if (a->esz == 0) { return false; @@ -1829,8 +1826,7 @@ static bool trans_INCDEC_v(DisasContext *s, arg_incdec2_cnt *a, uint32_t insn) return true; } -static bool trans_SINCDEC_v(DisasContext *s, arg_incdec2_cnt *a, - uint32_t insn) +static bool trans_SINCDEC_v(DisasContext *s, arg_incdec2_cnt *a) { if (a->esz == 0) { return false; @@ -1872,22 +1868,22 @@ static bool do_zz_dbm(DisasContext *s, arg_rr_dbm *a, GVecGen2iFn *gvec_fn) return true; } -static bool trans_AND_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +static bool trans_AND_zzi(DisasContext *s, arg_rr_dbm *a) { return do_zz_dbm(s, a, tcg_gen_gvec_andi); } -static bool trans_ORR_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +static bool trans_ORR_zzi(DisasContext *s, arg_rr_dbm *a) { return do_zz_dbm(s, a, tcg_gen_gvec_ori); } -static bool trans_EOR_zzi(DisasContext *s, arg_rr_dbm *a, uint32_t insn) +static bool trans_EOR_zzi(DisasContext *s, arg_rr_dbm *a) { return do_zz_dbm(s, a, tcg_gen_gvec_xori); } -static bool trans_DUPM(DisasContext *s, arg_DUPM *a, uint32_t insn) +static bool trans_DUPM(DisasContext *s, arg_DUPM *a) { uint64_t imm; if (!logic_imm_decode_wmask(&imm, extract32(a->dbm, 12, 1), @@ -1934,7 +1930,7 @@ static void do_cpy_m(DisasContext *s, int esz, int rd, int rn, int pg, tcg_temp_free_i32(desc); } -static bool trans_FCPY(DisasContext *s, arg_FCPY *a, uint32_t insn) +static bool trans_FCPY(DisasContext *s, arg_FCPY *a) { if (a->esz == 0) { return false; @@ -1949,9 +1945,9 @@ static bool trans_FCPY(DisasContext *s, arg_FCPY *a, uint32_t insn) return true; } -static bool trans_CPY_m_i(DisasContext *s, arg_rpri_esz *a, uint32_t insn) +static bool trans_CPY_m_i(DisasContext *s, arg_rpri_esz *a) { - if (a->esz == 0 && extract32(insn, 13, 1)) { + if (a->esz == 0 && extract32(s->insn, 13, 1)) { return false; } if (sve_access_check(s)) { @@ -1962,14 +1958,14 @@ static bool trans_CPY_m_i(DisasContext *s, arg_rpri_esz *a, uint32_t insn) return true; } -static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a, uint32_t insn) +static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a) { static gen_helper_gvec_2i * const fns[4] = { gen_helper_sve_cpy_z_b, gen_helper_sve_cpy_z_h, gen_helper_sve_cpy_z_s, gen_helper_sve_cpy_z_d, }; - if (a->esz == 0 && extract32(insn, 13, 1)) { + if (a->esz == 0 && extract32(s->insn, 13, 1)) { return false; } if (sve_access_check(s)) { @@ -1987,7 +1983,7 @@ static bool trans_CPY_z_i(DisasContext *s, arg_CPY_z_i *a, uint32_t insn) *** SVE Permute Extract Group */ -static bool trans_EXT(DisasContext *s, arg_EXT *a, uint32_t insn) +static bool trans_EXT(DisasContext *s, arg_EXT *a) { if (!sve_access_check(s)) { return true; @@ -2021,7 +2017,7 @@ static bool trans_EXT(DisasContext *s, arg_EXT *a, uint32_t insn) *** SVE Permute - Unpredicated Group */ -static bool trans_DUP_s(DisasContext *s, arg_DUP_s *a, uint32_t insn) +static bool trans_DUP_s(DisasContext *s, arg_DUP_s *a) { if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); @@ -2031,7 +2027,7 @@ static bool trans_DUP_s(DisasContext *s, arg_DUP_s *a, uint32_t insn) return true; } -static bool trans_DUP_x(DisasContext *s, arg_DUP_x *a, uint32_t insn) +static bool trans_DUP_x(DisasContext *s, arg_DUP_x *a) { if ((a->imm & 0x1f) == 0) { return false; @@ -2076,7 +2072,7 @@ static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) tcg_temp_free_i32(desc); } -static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a) { if (sve_access_check(s)) { TCGv_i64 t = tcg_temp_new_i64(); @@ -2087,7 +2083,7 @@ static bool trans_INSR_f(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return true; } -static bool trans_INSR_r(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_INSR_r(DisasContext *s, arg_rrr_esz *a) { if (sve_access_check(s)) { do_insr_i64(s, a, cpu_reg(s, a->rm)); @@ -2095,7 +2091,7 @@ static bool trans_INSR_r(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return true; } -static bool trans_REV_v(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_REV_v(DisasContext *s, arg_rr_esz *a) { static gen_helper_gvec_2 * const fns[4] = { gen_helper_sve_rev_b, gen_helper_sve_rev_h, @@ -2111,7 +2107,7 @@ static bool trans_REV_v(DisasContext *s, arg_rr_esz *a, uint32_t insn) return true; } -static bool trans_TBL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_TBL(DisasContext *s, arg_rrr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { gen_helper_sve_tbl_b, gen_helper_sve_tbl_h, @@ -2128,7 +2124,7 @@ static bool trans_TBL(DisasContext *s, arg_rrr_esz *a, uint32_t insn) return true; } -static bool trans_UNPK(DisasContext *s, arg_UNPK *a, uint32_t insn) +static bool trans_UNPK(DisasContext *s, arg_UNPK *a) { static gen_helper_gvec_2 * const fns[4][2] = { { NULL, NULL }, @@ -2225,47 +2221,47 @@ static bool do_perm_pred2(DisasContext *s, arg_rr_esz *a, bool high_odd, return true; } -static bool trans_ZIP1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_ZIP1_p(DisasContext *s, arg_rrr_esz *a) { return do_perm_pred3(s, a, 0, gen_helper_sve_zip_p); } -static bool trans_ZIP2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_ZIP2_p(DisasContext *s, arg_rrr_esz *a) { return do_perm_pred3(s, a, 1, gen_helper_sve_zip_p); } -static bool trans_UZP1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_UZP1_p(DisasContext *s, arg_rrr_esz *a) { return do_perm_pred3(s, a, 0, gen_helper_sve_uzp_p); } -static bool trans_UZP2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_UZP2_p(DisasContext *s, arg_rrr_esz *a) { return do_perm_pred3(s, a, 1, gen_helper_sve_uzp_p); } -static bool trans_TRN1_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_TRN1_p(DisasContext *s, arg_rrr_esz *a) { return do_perm_pred3(s, a, 0, gen_helper_sve_trn_p); } -static bool trans_TRN2_p(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_TRN2_p(DisasContext *s, arg_rrr_esz *a) { return do_perm_pred3(s, a, 1, gen_helper_sve_trn_p); } -static bool trans_REV_p(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_REV_p(DisasContext *s, arg_rr_esz *a) { return do_perm_pred2(s, a, 0, gen_helper_sve_rev_p); } -static bool trans_PUNPKLO(DisasContext *s, arg_PUNPKLO *a, uint32_t insn) +static bool trans_PUNPKLO(DisasContext *s, arg_PUNPKLO *a) { return do_perm_pred2(s, a, 0, gen_helper_sve_punpk_p); } -static bool trans_PUNPKHI(DisasContext *s, arg_PUNPKHI *a, uint32_t insn) +static bool trans_PUNPKHI(DisasContext *s, arg_PUNPKHI *a) { return do_perm_pred2(s, a, 1, gen_helper_sve_punpk_p); } @@ -2305,12 +2301,12 @@ static bool do_zzz_data_ool(DisasContext *s, arg_rrr_esz *a, int data, return true; } -static bool trans_ZIP1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_ZIP1_z(DisasContext *s, arg_rrr_esz *a) { return do_zip(s, a, false); } -static bool trans_ZIP2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_ZIP2_z(DisasContext *s, arg_rrr_esz *a) { return do_zip(s, a, true); } @@ -2320,12 +2316,12 @@ static gen_helper_gvec_3 * const uzp_fns[4] = { gen_helper_sve_uzp_s, gen_helper_sve_uzp_d, }; -static bool trans_UZP1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_UZP1_z(DisasContext *s, arg_rrr_esz *a) { return do_zzz_data_ool(s, a, 0, uzp_fns[a->esz]); } -static bool trans_UZP2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_UZP2_z(DisasContext *s, arg_rrr_esz *a) { return do_zzz_data_ool(s, a, 1 << a->esz, uzp_fns[a->esz]); } @@ -2335,12 +2331,12 @@ static gen_helper_gvec_3 * const trn_fns[4] = { gen_helper_sve_trn_s, gen_helper_sve_trn_d, }; -static bool trans_TRN1_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_TRN1_z(DisasContext *s, arg_rrr_esz *a) { return do_zzz_data_ool(s, a, 0, trn_fns[a->esz]); } -static bool trans_TRN2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) +static bool trans_TRN2_z(DisasContext *s, arg_rrr_esz *a) { return do_zzz_data_ool(s, a, 1 << a->esz, trn_fns[a->esz]); } @@ -2349,7 +2345,7 @@ static bool trans_TRN2_z(DisasContext *s, arg_rrr_esz *a, uint32_t insn) *** SVE Permute Vector - Predicated Group */ -static bool trans_COMPACT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_COMPACT(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, NULL, gen_helper_sve_compact_s, gen_helper_sve_compact_d @@ -2516,12 +2512,12 @@ static bool do_clast_vector(DisasContext *s, arg_rprr_esz *a, bool before) return true; } -static bool trans_CLASTA_z(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_CLASTA_z(DisasContext *s, arg_rprr_esz *a) { return do_clast_vector(s, a, false); } -static bool trans_CLASTB_z(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_CLASTB_z(DisasContext *s, arg_rprr_esz *a) { return do_clast_vector(s, a, true); } @@ -2574,12 +2570,12 @@ static bool do_clast_fp(DisasContext *s, arg_rpr_esz *a, bool before) return true; } -static bool trans_CLASTA_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_CLASTA_v(DisasContext *s, arg_rpr_esz *a) { return do_clast_fp(s, a, false); } -static bool trans_CLASTB_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_CLASTB_v(DisasContext *s, arg_rpr_esz *a) { return do_clast_fp(s, a, true); } @@ -2614,12 +2610,12 @@ static bool do_clast_general(DisasContext *s, arg_rpr_esz *a, bool before) return true; } -static bool trans_CLASTA_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_CLASTA_r(DisasContext *s, arg_rpr_esz *a) { return do_clast_general(s, a, false); } -static bool trans_CLASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_CLASTB_r(DisasContext *s, arg_rpr_esz *a) { return do_clast_general(s, a, true); } @@ -2654,12 +2650,12 @@ static bool do_last_fp(DisasContext *s, arg_rpr_esz *a, bool before) return true; } -static bool trans_LASTA_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_LASTA_v(DisasContext *s, arg_rpr_esz *a) { return do_last_fp(s, a, false); } -static bool trans_LASTB_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_LASTB_v(DisasContext *s, arg_rpr_esz *a) { return do_last_fp(s, a, true); } @@ -2675,17 +2671,17 @@ static bool do_last_general(DisasContext *s, arg_rpr_esz *a, bool before) return true; } -static bool trans_LASTA_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_LASTA_r(DisasContext *s, arg_rpr_esz *a) { return do_last_general(s, a, false); } -static bool trans_LASTB_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_LASTB_r(DisasContext *s, arg_rpr_esz *a) { return do_last_general(s, a, true); } -static bool trans_CPY_m_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_CPY_m_r(DisasContext *s, arg_rpr_esz *a) { if (sve_access_check(s)) { do_cpy_m(s, a->esz, a->rd, a->rd, a->pg, cpu_reg_sp(s, a->rn)); @@ -2693,7 +2689,7 @@ static bool trans_CPY_m_r(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return true; } -static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a) { if (sve_access_check(s)) { int ofs = vec_reg_offset(s, a->rn, 0, a->esz); @@ -2704,7 +2700,7 @@ static bool trans_CPY_m_v(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return true; } -static bool trans_REVB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_REVB(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -2715,7 +2711,7 @@ static bool trans_REVB(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_REVH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_REVH(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { NULL, @@ -2726,12 +2722,12 @@ static bool trans_REVH(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_REVW(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_REVW(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ool(s, a, a->esz == 3 ? gen_helper_sve_revw_d : NULL); } -static bool trans_RBIT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_RBIT(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3 * const fns[4] = { gen_helper_sve_rbit_b, @@ -2742,7 +2738,7 @@ static bool trans_RBIT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ool(s, a, fns[a->esz]); } -static bool trans_SPLICE(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_SPLICE(DisasContext *s, arg_rprr_esz *a) { if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); @@ -2799,8 +2795,7 @@ static bool do_ppzz_flags(DisasContext *s, arg_rprr_esz *a, } #define DO_PPZZ(NAME, name) \ -static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a) \ { \ static gen_helper_gvec_flags_4 * const fns[4] = { \ gen_helper_sve_##name##_ppzz_b, gen_helper_sve_##name##_ppzz_h, \ @@ -2819,8 +2814,7 @@ DO_PPZZ(CMPHS, cmphs) #undef DO_PPZZ #define DO_PPZW(NAME, name) \ -static bool trans_##NAME##_ppzw(DisasContext *s, arg_rprr_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_ppzw(DisasContext *s, arg_rprr_esz *a) \ { \ static gen_helper_gvec_flags_4 * const fns[4] = { \ gen_helper_sve_##name##_ppzw_b, gen_helper_sve_##name##_ppzw_h, \ @@ -2883,8 +2877,7 @@ static bool do_ppzi_flags(DisasContext *s, arg_rpri_esz *a, } #define DO_PPZI(NAME, name) \ -static bool trans_##NAME##_ppzi(DisasContext *s, arg_rpri_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_ppzi(DisasContext *s, arg_rpri_esz *a) \ { \ static gen_helper_gvec_flags_3 * const fns[4] = { \ gen_helper_sve_##name##_ppzi_b, gen_helper_sve_##name##_ppzi_h, \ @@ -2977,37 +2970,37 @@ static bool do_brk2(DisasContext *s, arg_rpr_s *a, return true; } -static bool trans_BRKPA(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_BRKPA(DisasContext *s, arg_rprr_s *a) { return do_brk3(s, a, gen_helper_sve_brkpa, gen_helper_sve_brkpas); } -static bool trans_BRKPB(DisasContext *s, arg_rprr_s *a, uint32_t insn) +static bool trans_BRKPB(DisasContext *s, arg_rprr_s *a) { return do_brk3(s, a, gen_helper_sve_brkpb, gen_helper_sve_brkpbs); } -static bool trans_BRKA_m(DisasContext *s, arg_rpr_s *a, uint32_t insn) +static bool trans_BRKA_m(DisasContext *s, arg_rpr_s *a) { return do_brk2(s, a, gen_helper_sve_brka_m, gen_helper_sve_brkas_m); } -static bool trans_BRKB_m(DisasContext *s, arg_rpr_s *a, uint32_t insn) +static bool trans_BRKB_m(DisasContext *s, arg_rpr_s *a) { return do_brk2(s, a, gen_helper_sve_brkb_m, gen_helper_sve_brkbs_m); } -static bool trans_BRKA_z(DisasContext *s, arg_rpr_s *a, uint32_t insn) +static bool trans_BRKA_z(DisasContext *s, arg_rpr_s *a) { return do_brk2(s, a, gen_helper_sve_brka_z, gen_helper_sve_brkas_z); } -static bool trans_BRKB_z(DisasContext *s, arg_rpr_s *a, uint32_t insn) +static bool trans_BRKB_z(DisasContext *s, arg_rpr_s *a) { return do_brk2(s, a, gen_helper_sve_brkb_z, gen_helper_sve_brkbs_z); } -static bool trans_BRKN(DisasContext *s, arg_rpr_s *a, uint32_t insn) +static bool trans_BRKN(DisasContext *s, arg_rpr_s *a) { return do_brk2(s, a, gen_helper_sve_brkn, gen_helper_sve_brkns); } @@ -3058,7 +3051,7 @@ static void do_cntp(DisasContext *s, TCGv_i64 val, int esz, int pn, int pg) } } -static bool trans_CNTP(DisasContext *s, arg_CNTP *a, uint32_t insn) +static bool trans_CNTP(DisasContext *s, arg_CNTP *a) { if (sve_access_check(s)) { do_cntp(s, cpu_reg(s, a->rd), a->esz, a->rn, a->pg); @@ -3066,8 +3059,7 @@ static bool trans_CNTP(DisasContext *s, arg_CNTP *a, uint32_t insn) return true; } -static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a, - uint32_t insn) +static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a) { if (sve_access_check(s)) { TCGv_i64 reg = cpu_reg(s, a->rd); @@ -3084,8 +3076,7 @@ static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a, return true; } -static bool trans_INCDECP_z(DisasContext *s, arg_incdec2_pred *a, - uint32_t insn) +static bool trans_INCDECP_z(DisasContext *s, arg_incdec2_pred *a) { if (a->esz == 0) { return false; @@ -3102,8 +3093,7 @@ static bool trans_INCDECP_z(DisasContext *s, arg_incdec2_pred *a, return true; } -static bool trans_SINCDECP_r_32(DisasContext *s, arg_incdec_pred *a, - uint32_t insn) +static bool trans_SINCDECP_r_32(DisasContext *s, arg_incdec_pred *a) { if (sve_access_check(s)) { TCGv_i64 reg = cpu_reg(s, a->rd); @@ -3115,8 +3105,7 @@ static bool trans_SINCDECP_r_32(DisasContext *s, arg_incdec_pred *a, return true; } -static bool trans_SINCDECP_r_64(DisasContext *s, arg_incdec_pred *a, - uint32_t insn) +static bool trans_SINCDECP_r_64(DisasContext *s, arg_incdec_pred *a) { if (sve_access_check(s)) { TCGv_i64 reg = cpu_reg(s, a->rd); @@ -3128,8 +3117,7 @@ static bool trans_SINCDECP_r_64(DisasContext *s, arg_incdec_pred *a, return true; } -static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a, - uint32_t insn) +static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a) { if (a->esz == 0) { return false; @@ -3146,7 +3134,7 @@ static bool trans_SINCDECP_z(DisasContext *s, arg_incdec2_pred *a, *** SVE Integer Compare Scalars Group */ -static bool trans_CTERM(DisasContext *s, arg_CTERM *a, uint32_t insn) +static bool trans_CTERM(DisasContext *s, arg_CTERM *a) { if (!sve_access_check(s)) { return true; @@ -3171,21 +3159,21 @@ static bool trans_CTERM(DisasContext *s, arg_CTERM *a, uint32_t insn) return true; } -static bool trans_WHILE(DisasContext *s, arg_WHILE *a, uint32_t insn) +static bool trans_WHILE(DisasContext *s, arg_WHILE *a) { - if (!sve_access_check(s)) { - return true; - } - - TCGv_i64 op0 = read_cpu_reg(s, a->rn, 1); - TCGv_i64 op1 = read_cpu_reg(s, a->rm, 1); - TCGv_i64 t0 = tcg_temp_new_i64(); - TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 op0, op1, t0, t1, tmax; TCGv_i32 t2, t3; TCGv_ptr ptr; unsigned desc, vsz = vec_full_reg_size(s); TCGCond cond; + if (!sve_access_check(s)) { + return true; + } + + op0 = read_cpu_reg(s, a->rn, 1); + op1 = read_cpu_reg(s, a->rm, 1); + if (!a->sf) { if (a->u) { tcg_gen_ext32u_i64(op0, op0); @@ -3198,32 +3186,47 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a, uint32_t insn) /* For the helper, compress the different conditions into a computation * of how many iterations for which the condition is true. - * - * This is slightly complicated by 0 <= UINT64_MAX, which is nominally - * 2**64 iterations, overflowing to 0. Of course, predicate registers - * aren't that large, so any value >= predicate size is sufficient. */ + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); tcg_gen_sub_i64(t0, op1, op0); - /* t0 = MIN(op1 - op0, vsz). */ - tcg_gen_movi_i64(t1, vsz); - tcg_gen_umin_i64(t0, t0, t1); + tmax = tcg_const_i64(vsz >> a->esz); if (a->eq) { /* Equality means one more iteration. */ tcg_gen_addi_i64(t0, t0, 1); + + /* If op1 is max (un)signed integer (and the only time the addition + * above could overflow), then we produce an all-true predicate by + * setting the count to the vector length. This is because the + * pseudocode is described as an increment + compare loop, and the + * max integer would always compare true. + */ + tcg_gen_movi_i64(t1, (a->sf + ? (a->u ? UINT64_MAX : INT64_MAX) + : (a->u ? UINT32_MAX : INT32_MAX))); + tcg_gen_movcond_i64(TCG_COND_EQ, t0, op1, t1, tmax, t0); } - /* t0 = (condition true ? t0 : 0). */ + /* Bound to the maximum. */ + tcg_gen_umin_i64(t0, t0, tmax); + tcg_temp_free_i64(tmax); + + /* Set the count to zero if the condition is false. */ cond = (a->u ? (a->eq ? TCG_COND_LEU : TCG_COND_LTU) : (a->eq ? TCG_COND_LE : TCG_COND_LT)); tcg_gen_movi_i64(t1, 0); tcg_gen_movcond_i64(cond, t0, op0, op1, t0, t1); + tcg_temp_free_i64(t1); + /* Since we're bounded, pass as a 32-bit type. */ t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, t0); tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + + /* Scale elements to bits. */ + tcg_gen_shli_i32(t2, t2, a->esz); desc = (vsz / 8) - 2; desc = deposit32(desc, SIMD_DATA_SHIFT, 2, a->esz); @@ -3245,7 +3248,7 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a, uint32_t insn) *** SVE Integer Wide Immediate - Unpredicated Group */ -static bool trans_FDUP(DisasContext *s, arg_FDUP *a, uint32_t insn) +static bool trans_FDUP(DisasContext *s, arg_FDUP *a) { if (a->esz == 0) { return false; @@ -3264,9 +3267,9 @@ static bool trans_FDUP(DisasContext *s, arg_FDUP *a, uint32_t insn) return true; } -static bool trans_DUP_i(DisasContext *s, arg_DUP_i *a, uint32_t insn) +static bool trans_DUP_i(DisasContext *s, arg_DUP_i *a) { - if (a->esz == 0 && extract32(insn, 13, 1)) { + if (a->esz == 0 && extract32(s->insn, 13, 1)) { return false; } if (sve_access_check(s)) { @@ -3278,9 +3281,9 @@ static bool trans_DUP_i(DisasContext *s, arg_DUP_i *a, uint32_t insn) return true; } -static bool trans_ADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_ADD_zzi(DisasContext *s, arg_rri_esz *a) { - if (a->esz == 0 && extract32(insn, 13, 1)) { + if (a->esz == 0 && extract32(s->insn, 13, 1)) { return false; } if (sve_access_check(s)) { @@ -3291,13 +3294,13 @@ static bool trans_ADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) return true; } -static bool trans_SUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_SUB_zzi(DisasContext *s, arg_rri_esz *a) { a->imm = -a->imm; - return trans_ADD_zzi(s, a, insn); + return trans_ADD_zzi(s, a); } -static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a) { static const GVecGen2s op[4] = { { .fni8 = tcg_gen_vec_sub8_i64, @@ -3327,7 +3330,7 @@ static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) .scalar_first = true } }; - if (a->esz == 0 && extract32(insn, 13, 1)) { + if (a->esz == 0 && extract32(s->insn, 13, 1)) { return false; } if (sve_access_check(s)) { @@ -3341,7 +3344,7 @@ static bool trans_SUBR_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) return true; } -static bool trans_MUL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_MUL_zzi(DisasContext *s, arg_rri_esz *a) { if (sve_access_check(s)) { unsigned vsz = vec_full_reg_size(s); @@ -3351,10 +3354,9 @@ static bool trans_MUL_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) return true; } -static bool do_zzi_sat(DisasContext *s, arg_rri_esz *a, uint32_t insn, - bool u, bool d) +static bool do_zzi_sat(DisasContext *s, arg_rri_esz *a, bool u, bool d) { - if (a->esz == 0 && extract32(insn, 13, 1)) { + if (a->esz == 0 && extract32(s->insn, 13, 1)) { return false; } if (sve_access_check(s)) { @@ -3365,24 +3367,24 @@ static bool do_zzi_sat(DisasContext *s, arg_rri_esz *a, uint32_t insn, return true; } -static bool trans_SQADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_SQADD_zzi(DisasContext *s, arg_rri_esz *a) { - return do_zzi_sat(s, a, insn, false, false); + return do_zzi_sat(s, a, false, false); } -static bool trans_UQADD_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_UQADD_zzi(DisasContext *s, arg_rri_esz *a) { - return do_zzi_sat(s, a, insn, true, false); + return do_zzi_sat(s, a, true, false); } -static bool trans_SQSUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_SQSUB_zzi(DisasContext *s, arg_rri_esz *a) { - return do_zzi_sat(s, a, insn, false, true); + return do_zzi_sat(s, a, false, true); } -static bool trans_UQSUB_zzi(DisasContext *s, arg_rri_esz *a, uint32_t insn) +static bool trans_UQSUB_zzi(DisasContext *s, arg_rri_esz *a) { - return do_zzi_sat(s, a, insn, true, true); + return do_zzi_sat(s, a, true, true); } static bool do_zzi_ool(DisasContext *s, arg_rri_esz *a, gen_helper_gvec_2i *fn) @@ -3400,8 +3402,7 @@ static bool do_zzi_ool(DisasContext *s, arg_rri_esz *a, gen_helper_gvec_2i *fn) } #define DO_ZZI(NAME, name) \ -static bool trans_##NAME##_zzi(DisasContext *s, arg_rri_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_zzi(DisasContext *s, arg_rri_esz *a) \ { \ static gen_helper_gvec_2i * const fns[4] = { \ gen_helper_sve_##name##i_b, gen_helper_sve_##name##i_h, \ @@ -3417,7 +3418,7 @@ DO_ZZI(UMIN, umin) #undef DO_ZZI -static bool trans_DOT_zzz(DisasContext *s, arg_DOT_zzz *a, uint32_t insn) +static bool trans_DOT_zzz(DisasContext *s, arg_DOT_zzz *a) { static gen_helper_gvec_3 * const fns[2][2] = { { gen_helper_gvec_sdot_b, gen_helper_gvec_sdot_h }, @@ -3434,7 +3435,7 @@ static bool trans_DOT_zzz(DisasContext *s, arg_DOT_zzz *a, uint32_t insn) return true; } -static bool trans_DOT_zzx(DisasContext *s, arg_DOT_zzx *a, uint32_t insn) +static bool trans_DOT_zzx(DisasContext *s, arg_DOT_zzx *a) { static gen_helper_gvec_3 * const fns[2][2] = { { gen_helper_gvec_sdot_idx_b, gen_helper_gvec_sdot_idx_h }, @@ -3456,7 +3457,7 @@ static bool trans_DOT_zzx(DisasContext *s, arg_DOT_zzx *a, uint32_t insn) *** SVE Floating Point Multiply-Add Indexed Group */ -static bool trans_FMLA_zzxz(DisasContext *s, arg_FMLA_zzxz *a, uint32_t insn) +static bool trans_FMLA_zzxz(DisasContext *s, arg_FMLA_zzxz *a) { static gen_helper_gvec_4_ptr * const fns[3] = { gen_helper_gvec_fmla_idx_h, @@ -3482,7 +3483,7 @@ static bool trans_FMLA_zzxz(DisasContext *s, arg_FMLA_zzxz *a, uint32_t insn) *** SVE Floating Point Multiply Indexed Group */ -static bool trans_FMUL_zzx(DisasContext *s, arg_FMUL_zzx *a, uint32_t insn) +static bool trans_FMUL_zzx(DisasContext *s, arg_FMUL_zzx *a) { static gen_helper_gvec_3_ptr * const fns[3] = { gen_helper_gvec_fmul_idx_h, @@ -3537,7 +3538,7 @@ static void do_reduce(DisasContext *s, arg_rpr_esz *a, } #define DO_VPZ(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a) \ { \ static gen_helper_fp_reduce * const fns[3] = { \ gen_helper_sve_##name##_h, \ @@ -3574,7 +3575,7 @@ static void do_zz_fp(DisasContext *s, arg_rr_esz *a, gen_helper_gvec_2_ptr *fn) tcg_temp_free_ptr(status); } -static bool trans_FRECPE(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_FRECPE(DisasContext *s, arg_rr_esz *a) { static gen_helper_gvec_2_ptr * const fns[3] = { gen_helper_gvec_frecpe_h, @@ -3590,7 +3591,7 @@ static bool trans_FRECPE(DisasContext *s, arg_rr_esz *a, uint32_t insn) return true; } -static bool trans_FRSQRTE(DisasContext *s, arg_rr_esz *a, uint32_t insn) +static bool trans_FRSQRTE(DisasContext *s, arg_rr_esz *a) { static gen_helper_gvec_2_ptr * const fns[3] = { gen_helper_gvec_frsqrte_h, @@ -3624,7 +3625,7 @@ static void do_ppz_fp(DisasContext *s, arg_rpr_esz *a, } #define DO_PPZ(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rpr_esz *a) \ { \ static gen_helper_gvec_3_ptr * const fns[3] = { \ gen_helper_sve_##name##_h, \ @@ -3653,7 +3654,7 @@ DO_PPZ(FCMNE_ppz0, fcmne0) *** SVE floating-point trig multiply-add coefficient */ -static bool trans_FTMAD(DisasContext *s, arg_FTMAD *a, uint32_t insn) +static bool trans_FTMAD(DisasContext *s, arg_FTMAD *a) { static gen_helper_gvec_3_ptr * const fns[3] = { gen_helper_sve_ftmad_h, @@ -3680,7 +3681,7 @@ static bool trans_FTMAD(DisasContext *s, arg_FTMAD *a, uint32_t insn) *** SVE Floating Point Accumulating Reduction Group */ -static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a, uint32_t insn) +static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) { typedef void fadda_fn(TCGv_i64, TCGv_i64, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); @@ -3745,7 +3746,7 @@ static bool do_zzz_fp(DisasContext *s, arg_rrr_esz *a, #define DO_FP3(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rrr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rrr_esz *a) \ { \ static gen_helper_gvec_3_ptr * const fns[4] = { \ NULL, gen_helper_gvec_##name##_h, \ @@ -3787,7 +3788,7 @@ static bool do_zpzz_fp(DisasContext *s, arg_rprr_esz *a, } #define DO_FP3(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rprr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rprr_esz *a) \ { \ static gen_helper_gvec_4_ptr * const fns[4] = { \ NULL, gen_helper_sve_##name##_h, \ @@ -3847,8 +3848,7 @@ static void do_fp_imm(DisasContext *s, arg_rpri_esz *a, uint64_t imm, } #define DO_FP_IMM(NAME, name, const0, const1) \ -static bool trans_##NAME##_zpzi(DisasContext *s, arg_rpri_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_zpzi(DisasContext *s, arg_rpri_esz *a) \ { \ static gen_helper_sve_fp2scalar * const fns[3] = { \ gen_helper_sve_##name##_h, \ @@ -3904,8 +3904,7 @@ static bool do_fp_cmp(DisasContext *s, arg_rprr_esz *a, } #define DO_FPCMP(NAME, name) \ -static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a, \ - uint32_t insn) \ +static bool trans_##NAME##_ppzz(DisasContext *s, arg_rprr_esz *a) \ { \ static gen_helper_gvec_4_ptr * const fns[4] = { \ NULL, gen_helper_sve_##name##_h, \ @@ -3924,7 +3923,7 @@ DO_FPCMP(FACGT, facgt) #undef DO_FPCMP -static bool trans_FCADD(DisasContext *s, arg_FCADD *a, uint32_t insn) +static bool trans_FCADD(DisasContext *s, arg_FCADD *a) { static gen_helper_gvec_4_ptr * const fns[3] = { gen_helper_sve_fcadd_h, @@ -3981,7 +3980,7 @@ static bool do_fmla(DisasContext *s, arg_rprrr_esz *a, gen_helper_sve_fmla *fn) } #define DO_FMLA(NAME, name) \ -static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a, uint32_t insn) \ +static bool trans_##NAME(DisasContext *s, arg_rprrr_esz *a) \ { \ static gen_helper_sve_fmla * const fns[4] = { \ NULL, gen_helper_sve_##name##_h, \ @@ -3997,8 +3996,7 @@ DO_FMLA(FNMLS_zpzzz, fnmls_zpzzz) #undef DO_FMLA -static bool trans_FCMLA_zpzzz(DisasContext *s, - arg_FCMLA_zpzzz *a, uint32_t insn) +static bool trans_FCMLA_zpzzz(DisasContext *s, arg_FCMLA_zpzzz *a) { static gen_helper_sve_fmla * const fns[3] = { gen_helper_sve_fcmla_zpzzz_h, @@ -4034,7 +4032,7 @@ static bool trans_FCMLA_zpzzz(DisasContext *s, return true; } -static bool trans_FCMLA_zzxz(DisasContext *s, arg_FCMLA_zzxz *a, uint32_t insn) +static bool trans_FCMLA_zzxz(DisasContext *s, arg_FCMLA_zzxz *a) { static gen_helper_gvec_3_ptr * const fns[2] = { gen_helper_gvec_fcmlah_idx, @@ -4076,102 +4074,102 @@ static bool do_zpz_ptr(DisasContext *s, int rd, int rn, int pg, return true; } -static bool trans_FCVT_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVT_sh(DisasContext *s, arg_rpr_esz *a) { - return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvt_sh); + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_sh); } -static bool trans_FCVT_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVT_hs(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_hs); } -static bool trans_FCVT_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVT_dh(DisasContext *s, arg_rpr_esz *a) { - return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvt_dh); + return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_dh); } -static bool trans_FCVT_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVT_hd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_hd); } -static bool trans_FCVT_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVT_ds(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_ds); } -static bool trans_FCVT_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVT_sd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvt_sd); } -static bool trans_FCVTZS_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_hh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hh); } -static bool trans_FCVTZU_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_hh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hh); } -static bool trans_FCVTZS_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_hs(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hs); } -static bool trans_FCVTZU_hs(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_hs(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hs); } -static bool trans_FCVTZS_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_hd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzs_hd); } -static bool trans_FCVTZU_hd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_hd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_fcvtzu_hd); } -static bool trans_FCVTZS_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_ss(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_ss); } -static bool trans_FCVTZU_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_ss(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_ss); } -static bool trans_FCVTZS_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_sd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_sd); } -static bool trans_FCVTZU_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_sd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_sd); } -static bool trans_FCVTZS_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_ds(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_ds); } -static bool trans_FCVTZU_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_ds(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_ds); } -static bool trans_FCVTZS_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZS_dd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzs_dd); } -static bool trans_FCVTZU_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FCVTZU_dd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_fcvtzu_dd); } @@ -4182,7 +4180,7 @@ static gen_helper_gvec_3_ptr * const frint_fns[3] = { gen_helper_sve_frint_d }; -static bool trans_FRINTI(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTI(DisasContext *s, arg_rpr_esz *a) { if (a->esz == 0) { return false; @@ -4191,7 +4189,7 @@ static bool trans_FRINTI(DisasContext *s, arg_rpr_esz *a, uint32_t insn) frint_fns[a->esz - 1]); } -static bool trans_FRINTX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTX(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3_ptr * const fns[3] = { gen_helper_sve_frintx_h, @@ -4228,32 +4226,32 @@ static bool do_frint_mode(DisasContext *s, arg_rpr_esz *a, int mode) return true; } -static bool trans_FRINTN(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTN(DisasContext *s, arg_rpr_esz *a) { return do_frint_mode(s, a, float_round_nearest_even); } -static bool trans_FRINTP(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTP(DisasContext *s, arg_rpr_esz *a) { return do_frint_mode(s, a, float_round_up); } -static bool trans_FRINTM(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTM(DisasContext *s, arg_rpr_esz *a) { return do_frint_mode(s, a, float_round_down); } -static bool trans_FRINTZ(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTZ(DisasContext *s, arg_rpr_esz *a) { return do_frint_mode(s, a, float_round_to_zero); } -static bool trans_FRINTA(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRINTA(DisasContext *s, arg_rpr_esz *a) { return do_frint_mode(s, a, float_round_ties_away); } -static bool trans_FRECPX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FRECPX(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3_ptr * const fns[3] = { gen_helper_sve_frecpx_h, @@ -4266,7 +4264,7 @@ static bool trans_FRECPX(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); } -static bool trans_FSQRT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_FSQRT(DisasContext *s, arg_rpr_esz *a) { static gen_helper_gvec_3_ptr * const fns[3] = { gen_helper_sve_fsqrt_h, @@ -4279,72 +4277,72 @@ static bool trans_FSQRT(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return do_zpz_ptr(s, a->rd, a->rn, a->pg, a->esz == MO_16, fns[a->esz - 1]); } -static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_hh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_hh); } -static bool trans_SCVTF_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_sh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_sh); } -static bool trans_SCVTF_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_dh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_scvt_dh); } -static bool trans_SCVTF_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_ss(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_ss); } -static bool trans_SCVTF_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_ds(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_ds); } -static bool trans_SCVTF_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_sd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_sd); } -static bool trans_SCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_SCVTF_dd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_scvt_dd); } -static bool trans_UCVTF_hh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_hh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_hh); } -static bool trans_UCVTF_sh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_sh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_sh); } -static bool trans_UCVTF_dh(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_dh(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, true, gen_helper_sve_ucvt_dh); } -static bool trans_UCVTF_ss(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_ss(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_ss); } -static bool trans_UCVTF_ds(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_ds(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_ds); } -static bool trans_UCVTF_sd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_sd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_sd); } -static bool trans_UCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_UCVTF_dd(DisasContext *s, arg_rpr_esz *a) { return do_zpz_ptr(s, a->rd, a->rn, a->pg, false, gen_helper_sve_ucvt_dd); } @@ -4357,12 +4355,11 @@ static bool trans_UCVTF_dd(DisasContext *s, arg_rpr_esz *a, uint32_t insn) * The load should begin at the address Rn + IMM. */ -static void do_ldr(DisasContext *s, uint32_t vofs, uint32_t len, - int rn, int imm) +static void do_ldr(DisasContext *s, uint32_t vofs, int len, int rn, int imm) { - uint32_t len_align = QEMU_ALIGN_DOWN(len, 8); - uint32_t len_remain = len % 8; - uint32_t nparts = len / 8 + ctpop8(len_remain); + int len_align = QEMU_ALIGN_DOWN(len, 8); + int len_remain = len % 8; + int nparts = len / 8 + ctpop8(len_remain); int midx = get_mem_index(s); TCGv_i64 addr, t0, t1; @@ -4443,12 +4440,11 @@ static void do_ldr(DisasContext *s, uint32_t vofs, uint32_t len, } /* Similarly for stores. */ -static void do_str(DisasContext *s, uint32_t vofs, uint32_t len, - int rn, int imm) +static void do_str(DisasContext *s, uint32_t vofs, int len, int rn, int imm) { - uint32_t len_align = QEMU_ALIGN_DOWN(len, 8); - uint32_t len_remain = len % 8; - uint32_t nparts = len / 8 + ctpop8(len_remain); + int len_align = QEMU_ALIGN_DOWN(len, 8); + int len_remain = len % 8; + int nparts = len / 8 + ctpop8(len_remain); int midx = get_mem_index(s); TCGv_i64 addr, t0; @@ -4525,7 +4521,7 @@ static void do_str(DisasContext *s, uint32_t vofs, uint32_t len, tcg_temp_free_i64(t0); } -static bool trans_LDR_zri(DisasContext *s, arg_rri *a, uint32_t insn) +static bool trans_LDR_zri(DisasContext *s, arg_rri *a) { if (sve_access_check(s)) { int size = vec_full_reg_size(s); @@ -4535,7 +4531,7 @@ static bool trans_LDR_zri(DisasContext *s, arg_rri *a, uint32_t insn) return true; } -static bool trans_LDR_pri(DisasContext *s, arg_rri *a, uint32_t insn) +static bool trans_LDR_pri(DisasContext *s, arg_rri *a) { if (sve_access_check(s)) { int size = pred_full_reg_size(s); @@ -4545,7 +4541,7 @@ static bool trans_LDR_pri(DisasContext *s, arg_rri *a, uint32_t insn) return true; } -static bool trans_STR_zri(DisasContext *s, arg_rri *a, uint32_t insn) +static bool trans_STR_zri(DisasContext *s, arg_rri *a) { if (sve_access_check(s)) { int size = vec_full_reg_size(s); @@ -4555,7 +4551,7 @@ static bool trans_STR_zri(DisasContext *s, arg_rri *a, uint32_t insn) return true; } -static bool trans_STR_pri(DisasContext *s, arg_rri *a, uint32_t insn) +static bool trans_STR_pri(DisasContext *s, arg_rri *a) { if (sve_access_check(s)) { int size = pred_full_reg_size(s); @@ -4587,80 +4583,114 @@ static const uint8_t dtype_esz[16] = { 3, 2, 1, 3 }; +static TCGMemOpIdx sve_memopidx(DisasContext *s, int dtype) +{ + return make_memop_idx(s->be_data | dtype_mop[dtype], get_mem_index(s)); +} + static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, - gen_helper_gvec_mem *fn) + int dtype, gen_helper_gvec_mem *fn) { unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; - TCGv_i32 desc; + TCGv_i32 t_desc; + int desc; /* For e.g. LD4, there are not enough arguments to pass all 4 * registers as pointers, so encode the regno into the data field. * For consistency, do this even for LD1. */ - desc = tcg_const_i32(simd_desc(vsz, vsz, zt)); + desc = sve_memopidx(s, dtype); + desc |= zt << MEMOPIDX_SHIFT; + desc = simd_desc(vsz, vsz, desc); + t_desc = tcg_const_i32(desc); t_pg = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); - fn(cpu_env, t_pg, addr, desc); + fn(cpu_env, t_pg, addr, t_desc); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(desc); + tcg_temp_free_i32(t_desc); } static void do_ld_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype, int nreg) { - static gen_helper_gvec_mem * const fns[16][4] = { - { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, - gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, - { gen_helper_sve_ld1bhu_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1bsu_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1bdu_r, NULL, NULL, NULL }, + static gen_helper_gvec_mem * const fns[2][16][4] = { + /* Little-endian */ + { { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, + gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, + { gen_helper_sve_ld1bhu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bsu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bdu_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1sds_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1hh_r, gen_helper_sve_ld2hh_r, - gen_helper_sve_ld3hh_r, gen_helper_sve_ld4hh_r }, - { gen_helper_sve_ld1hsu_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1hdu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1sds_le_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hh_le_r, gen_helper_sve_ld2hh_le_r, + gen_helper_sve_ld3hh_le_r, gen_helper_sve_ld4hh_le_r }, + { gen_helper_sve_ld1hsu_le_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hdu_le_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1hds_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1hss_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1ss_r, gen_helper_sve_ld2ss_r, - gen_helper_sve_ld3ss_r, gen_helper_sve_ld4ss_r }, - { gen_helper_sve_ld1sdu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hds_le_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hss_le_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1ss_le_r, gen_helper_sve_ld2ss_le_r, + gen_helper_sve_ld3ss_le_r, gen_helper_sve_ld4ss_le_r }, + { gen_helper_sve_ld1sdu_le_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1bds_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, - { gen_helper_sve_ld1dd_r, gen_helper_sve_ld2dd_r, - gen_helper_sve_ld3dd_r, gen_helper_sve_ld4dd_r }, + { gen_helper_sve_ld1bds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1dd_le_r, gen_helper_sve_ld2dd_le_r, + gen_helper_sve_ld3dd_le_r, gen_helper_sve_ld4dd_le_r } }, + + /* Big-endian */ + { { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, + gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, + { gen_helper_sve_ld1bhu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bsu_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bdu_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1sds_be_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hh_be_r, gen_helper_sve_ld2hh_be_r, + gen_helper_sve_ld3hh_be_r, gen_helper_sve_ld4hh_be_r }, + { gen_helper_sve_ld1hsu_be_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hdu_be_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1hds_be_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1hss_be_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1ss_be_r, gen_helper_sve_ld2ss_be_r, + gen_helper_sve_ld3ss_be_r, gen_helper_sve_ld4ss_be_r }, + { gen_helper_sve_ld1sdu_be_r, NULL, NULL, NULL }, + + { gen_helper_sve_ld1bds_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1dd_be_r, gen_helper_sve_ld2dd_be_r, + gen_helper_sve_ld3dd_be_r, gen_helper_sve_ld4dd_be_r } } }; - gen_helper_gvec_mem *fn = fns[dtype][nreg]; + gen_helper_gvec_mem *fn = fns[s->be_data == MO_BE][dtype][nreg]; /* While there are holes in the table, they are not * accessible via the instruction encoding. */ assert(fn != NULL); - do_mem_zpa(s, zt, pg, addr, fn); + do_mem_zpa(s, zt, pg, addr, dtype, fn); } -static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) { if (a->rm == 31) { return false; } if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); - tcg_gen_muli_i64(addr, cpu_reg(s, a->rm), - (a->nreg + 1) << dtype_msz(a->dtype)); + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_ld_zpa(s, a->rd, a->pg, addr, a->dtype, a->nreg); } return true; } -static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a) { if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); @@ -4675,61 +4705,106 @@ static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) return true; } -static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +static bool trans_LDFF1_zprr(DisasContext *s, arg_rprr_load *a) { - static gen_helper_gvec_mem * const fns[16] = { - gen_helper_sve_ldff1bb_r, - gen_helper_sve_ldff1bhu_r, - gen_helper_sve_ldff1bsu_r, - gen_helper_sve_ldff1bdu_r, + static gen_helper_gvec_mem * const fns[2][16] = { + /* Little-endian */ + { gen_helper_sve_ldff1bb_r, + gen_helper_sve_ldff1bhu_r, + gen_helper_sve_ldff1bsu_r, + gen_helper_sve_ldff1bdu_r, - gen_helper_sve_ldff1sds_r, - gen_helper_sve_ldff1hh_r, - gen_helper_sve_ldff1hsu_r, - gen_helper_sve_ldff1hdu_r, + gen_helper_sve_ldff1sds_le_r, + gen_helper_sve_ldff1hh_le_r, + gen_helper_sve_ldff1hsu_le_r, + gen_helper_sve_ldff1hdu_le_r, - gen_helper_sve_ldff1hds_r, - gen_helper_sve_ldff1hss_r, - gen_helper_sve_ldff1ss_r, - gen_helper_sve_ldff1sdu_r, + gen_helper_sve_ldff1hds_le_r, + gen_helper_sve_ldff1hss_le_r, + gen_helper_sve_ldff1ss_le_r, + gen_helper_sve_ldff1sdu_le_r, - gen_helper_sve_ldff1bds_r, - gen_helper_sve_ldff1bss_r, - gen_helper_sve_ldff1bhs_r, - gen_helper_sve_ldff1dd_r, + gen_helper_sve_ldff1bds_r, + gen_helper_sve_ldff1bss_r, + gen_helper_sve_ldff1bhs_r, + gen_helper_sve_ldff1dd_le_r }, + + /* Big-endian */ + { gen_helper_sve_ldff1bb_r, + gen_helper_sve_ldff1bhu_r, + gen_helper_sve_ldff1bsu_r, + gen_helper_sve_ldff1bdu_r, + + gen_helper_sve_ldff1sds_be_r, + gen_helper_sve_ldff1hh_be_r, + gen_helper_sve_ldff1hsu_be_r, + gen_helper_sve_ldff1hdu_be_r, + + gen_helper_sve_ldff1hds_be_r, + gen_helper_sve_ldff1hss_be_r, + gen_helper_sve_ldff1ss_be_r, + gen_helper_sve_ldff1sdu_be_r, + + gen_helper_sve_ldff1bds_r, + gen_helper_sve_ldff1bss_r, + gen_helper_sve_ldff1bhs_r, + gen_helper_sve_ldff1dd_be_r }, }; if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); - do_mem_zpa(s, a->rd, a->pg, addr, fns[a->dtype]); + do_mem_zpa(s, a->rd, a->pg, addr, a->dtype, + fns[s->be_data == MO_BE][a->dtype]); } return true; } -static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a) { - static gen_helper_gvec_mem * const fns[16] = { - gen_helper_sve_ldnf1bb_r, - gen_helper_sve_ldnf1bhu_r, - gen_helper_sve_ldnf1bsu_r, - gen_helper_sve_ldnf1bdu_r, + static gen_helper_gvec_mem * const fns[2][16] = { + /* Little-endian */ + { gen_helper_sve_ldnf1bb_r, + gen_helper_sve_ldnf1bhu_r, + gen_helper_sve_ldnf1bsu_r, + gen_helper_sve_ldnf1bdu_r, - gen_helper_sve_ldnf1sds_r, - gen_helper_sve_ldnf1hh_r, - gen_helper_sve_ldnf1hsu_r, - gen_helper_sve_ldnf1hdu_r, + gen_helper_sve_ldnf1sds_le_r, + gen_helper_sve_ldnf1hh_le_r, + gen_helper_sve_ldnf1hsu_le_r, + gen_helper_sve_ldnf1hdu_le_r, - gen_helper_sve_ldnf1hds_r, - gen_helper_sve_ldnf1hss_r, - gen_helper_sve_ldnf1ss_r, - gen_helper_sve_ldnf1sdu_r, + gen_helper_sve_ldnf1hds_le_r, + gen_helper_sve_ldnf1hss_le_r, + gen_helper_sve_ldnf1ss_le_r, + gen_helper_sve_ldnf1sdu_le_r, - gen_helper_sve_ldnf1bds_r, - gen_helper_sve_ldnf1bss_r, - gen_helper_sve_ldnf1bhs_r, - gen_helper_sve_ldnf1dd_r, + gen_helper_sve_ldnf1bds_r, + gen_helper_sve_ldnf1bss_r, + gen_helper_sve_ldnf1bhs_r, + gen_helper_sve_ldnf1dd_le_r }, + + /* Big-endian */ + { gen_helper_sve_ldnf1bb_r, + gen_helper_sve_ldnf1bhu_r, + gen_helper_sve_ldnf1bsu_r, + gen_helper_sve_ldnf1bdu_r, + + gen_helper_sve_ldnf1sds_be_r, + gen_helper_sve_ldnf1hh_be_r, + gen_helper_sve_ldnf1hsu_be_r, + gen_helper_sve_ldnf1hdu_be_r, + + gen_helper_sve_ldnf1hds_be_r, + gen_helper_sve_ldnf1hss_be_r, + gen_helper_sve_ldnf1ss_be_r, + gen_helper_sve_ldnf1sdu_be_r, + + gen_helper_sve_ldnf1bds_r, + gen_helper_sve_ldnf1bss_r, + gen_helper_sve_ldnf1bhs_r, + gen_helper_sve_ldnf1dd_be_r }, }; if (sve_access_check(s)) { @@ -4739,30 +4814,57 @@ static bool trans_LDNF1_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) TCGv_i64 addr = new_tmp_a64(s); tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), off); - do_mem_zpa(s, a->rd, a->pg, addr, fns[a->dtype]); + do_mem_zpa(s, a->rd, a->pg, addr, a->dtype, + fns[s->be_data == MO_BE][a->dtype]); } return true; } static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz) { - static gen_helper_gvec_mem * const fns[4] = { - gen_helper_sve_ld1bb_r, gen_helper_sve_ld1hh_r, - gen_helper_sve_ld1ss_r, gen_helper_sve_ld1dd_r, + static gen_helper_gvec_mem * const fns[2][4] = { + { gen_helper_sve_ld1bb_r, gen_helper_sve_ld1hh_le_r, + gen_helper_sve_ld1ss_le_r, gen_helper_sve_ld1dd_le_r }, + { gen_helper_sve_ld1bb_r, gen_helper_sve_ld1hh_be_r, + gen_helper_sve_ld1ss_be_r, gen_helper_sve_ld1dd_be_r }, }; unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; - TCGv_i32 desc; + TCGv_i32 t_desc; + int desc, poff; /* Load the first quadword using the normal predicated load helpers. */ - desc = tcg_const_i32(simd_desc(16, 16, zt)); - t_pg = tcg_temp_new_ptr(); + desc = sve_memopidx(s, msz_dtype(msz)); + desc |= zt << MEMOPIDX_SHIFT; + desc = simd_desc(16, 16, desc); + t_desc = tcg_const_i32(desc); - tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); - fns[msz](cpu_env, t_pg, addr, desc); + poff = pred_full_reg_offset(s, pg); + if (vsz > 16) { + /* + * Zero-extend the first 16 bits of the predicate into a temporary. + * This avoids triggering an assert making sure we don't have bits + * set within a predicate beyond VQ, but we have lowered VQ to 1 + * for this load operation. + */ + TCGv_i64 tmp = tcg_temp_new_i64(); +#ifdef HOST_WORDS_BIGENDIAN + poff += 6; +#endif + tcg_gen_ld16u_i64(tmp, cpu_env, poff); + + poff = offsetof(CPUARMState, vfp.preg_tmp); + tcg_gen_st_i64(tmp, cpu_env, poff); + tcg_temp_free_i64(tmp); + } + + t_pg = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(t_pg, cpu_env, poff); + + fns[s->be_data == MO_BE][msz](cpu_env, t_pg, addr, t_desc); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(desc); + tcg_temp_free_i32(t_desc); /* Replicate that first quadword. */ if (vsz > 16) { @@ -4771,7 +4873,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz) } } -static bool trans_LD1RQ_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) +static bool trans_LD1RQ_zprr(DisasContext *s, arg_rprr_load *a) { if (a->rm == 31) { return false; @@ -4786,7 +4888,7 @@ static bool trans_LD1RQ_zprr(DisasContext *s, arg_rprr_load *a, uint32_t insn) return true; } -static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a) { if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); @@ -4797,7 +4899,7 @@ static bool trans_LD1RQ_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) } /* Load and broadcast element. */ -static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) +static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a) { if (!sve_access_check(s)) { return true; @@ -4806,6 +4908,7 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) unsigned vsz = vec_full_reg_size(s); unsigned psz = pred_full_reg_size(s); unsigned esz = dtype_esz[a->dtype]; + unsigned msz = dtype_msz(a->dtype); TCGLabel *over = gen_new_label(); TCGv_i64 temp; @@ -4829,7 +4932,7 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) /* Load the data. */ temp = tcg_temp_new_i64(); - tcg_gen_addi_i64(temp, cpu_reg_sp(s, a->rn), a->imm << esz); + tcg_gen_addi_i64(temp, cpu_reg_sp(s, a->rn), a->imm << msz); tcg_gen_qemu_ld_i64(temp, temp, get_mem_index(s), s->be_data | dtype_mop[a->dtype]); @@ -4847,52 +4950,90 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a, uint32_t insn) static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz, int esz, int nreg) { - static gen_helper_gvec_mem * const fn_single[4][4] = { - { gen_helper_sve_st1bb_r, gen_helper_sve_st1bh_r, - gen_helper_sve_st1bs_r, gen_helper_sve_st1bd_r }, - { NULL, gen_helper_sve_st1hh_r, - gen_helper_sve_st1hs_r, gen_helper_sve_st1hd_r }, - { NULL, NULL, - gen_helper_sve_st1ss_r, gen_helper_sve_st1sd_r }, - { NULL, NULL, NULL, gen_helper_sve_st1dd_r }, + static gen_helper_gvec_mem * const fn_single[2][4][4] = { + { { gen_helper_sve_st1bb_r, + gen_helper_sve_st1bh_r, + gen_helper_sve_st1bs_r, + gen_helper_sve_st1bd_r }, + { NULL, + gen_helper_sve_st1hh_le_r, + gen_helper_sve_st1hs_le_r, + gen_helper_sve_st1hd_le_r }, + { NULL, NULL, + gen_helper_sve_st1ss_le_r, + gen_helper_sve_st1sd_le_r }, + { NULL, NULL, NULL, + gen_helper_sve_st1dd_le_r } }, + { { gen_helper_sve_st1bb_r, + gen_helper_sve_st1bh_r, + gen_helper_sve_st1bs_r, + gen_helper_sve_st1bd_r }, + { NULL, + gen_helper_sve_st1hh_be_r, + gen_helper_sve_st1hs_be_r, + gen_helper_sve_st1hd_be_r }, + { NULL, NULL, + gen_helper_sve_st1ss_be_r, + gen_helper_sve_st1sd_be_r }, + { NULL, NULL, NULL, + gen_helper_sve_st1dd_be_r } }, }; - static gen_helper_gvec_mem * const fn_multiple[3][4] = { - { gen_helper_sve_st2bb_r, gen_helper_sve_st2hh_r, - gen_helper_sve_st2ss_r, gen_helper_sve_st2dd_r }, - { gen_helper_sve_st3bb_r, gen_helper_sve_st3hh_r, - gen_helper_sve_st3ss_r, gen_helper_sve_st3dd_r }, - { gen_helper_sve_st4bb_r, gen_helper_sve_st4hh_r, - gen_helper_sve_st4ss_r, gen_helper_sve_st4dd_r }, + static gen_helper_gvec_mem * const fn_multiple[2][3][4] = { + { { gen_helper_sve_st2bb_r, + gen_helper_sve_st2hh_le_r, + gen_helper_sve_st2ss_le_r, + gen_helper_sve_st2dd_le_r }, + { gen_helper_sve_st3bb_r, + gen_helper_sve_st3hh_le_r, + gen_helper_sve_st3ss_le_r, + gen_helper_sve_st3dd_le_r }, + { gen_helper_sve_st4bb_r, + gen_helper_sve_st4hh_le_r, + gen_helper_sve_st4ss_le_r, + gen_helper_sve_st4dd_le_r } }, + { { gen_helper_sve_st2bb_r, + gen_helper_sve_st2hh_be_r, + gen_helper_sve_st2ss_be_r, + gen_helper_sve_st2dd_be_r }, + { gen_helper_sve_st3bb_r, + gen_helper_sve_st3hh_be_r, + gen_helper_sve_st3ss_be_r, + gen_helper_sve_st3dd_be_r }, + { gen_helper_sve_st4bb_r, + gen_helper_sve_st4hh_be_r, + gen_helper_sve_st4ss_be_r, + gen_helper_sve_st4dd_be_r } }, }; gen_helper_gvec_mem *fn; + int be = s->be_data == MO_BE; if (nreg == 0) { /* ST1 */ - fn = fn_single[msz][esz]; + fn = fn_single[be][msz][esz]; } else { /* ST2, ST3, ST4 -- msz == esz, enforced by encoding */ assert(msz == esz); - fn = fn_multiple[nreg - 1][msz]; + fn = fn_multiple[be][nreg - 1][msz]; } assert(fn != NULL); - do_mem_zpa(s, zt, pg, addr, fn); + do_mem_zpa(s, zt, pg, addr, msz_dtype(msz), fn); } -static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a, uint32_t insn) +static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) { if (a->rm == 31 || a->msz > a->esz) { return false; } if (sve_access_check(s)) { TCGv_i64 addr = new_tmp_a64(s); - tcg_gen_muli_i64(addr, cpu_reg(s, a->rm), (a->nreg + 1) << a->msz); + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->msz); tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); do_st_zpa(s, a->rd, a->pg, addr, a->msz, a->esz, a->nreg); } return true; } -static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a, uint32_t insn) +static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a) { if (a->msz > a->esz) { return false; @@ -4913,111 +5054,203 @@ static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a, uint32_t insn) *** SVE gather loads / scatter stores */ -static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, int scale, - TCGv_i64 scalar, gen_helper_gvec_mem_scatter *fn) +static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, + int scale, TCGv_i64 scalar, int msz, + gen_helper_gvec_mem_scatter *fn) { unsigned vsz = vec_full_reg_size(s); - TCGv_i32 desc = tcg_const_i32(simd_desc(vsz, vsz, scale)); TCGv_ptr t_zm = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); TCGv_ptr t_zt = tcg_temp_new_ptr(); + TCGv_i32 t_desc; + int desc; + + desc = sve_memopidx(s, msz_dtype(msz)); + desc |= scale << MEMOPIDX_SHIFT; + desc = simd_desc(vsz, vsz, desc); + t_desc = tcg_const_i32(desc); tcg_gen_addi_ptr(t_pg, cpu_env, pred_full_reg_offset(s, pg)); tcg_gen_addi_ptr(t_zm, cpu_env, vec_full_reg_offset(s, zm)); tcg_gen_addi_ptr(t_zt, cpu_env, vec_full_reg_offset(s, zt)); - fn(cpu_env, t_zt, t_pg, t_zm, scalar, desc); + fn(cpu_env, t_zt, t_pg, t_zm, scalar, t_desc); tcg_temp_free_ptr(t_zt); tcg_temp_free_ptr(t_zm); tcg_temp_free_ptr(t_pg); - tcg_temp_free_i32(desc); + tcg_temp_free_i32(t_desc); } -/* Indexed by [ff][xs][u][msz]. */ -static gen_helper_gvec_mem_scatter * const gather_load_fn32[2][2][2][3] = { - { { { gen_helper_sve_ldbss_zsu, - gen_helper_sve_ldhss_zsu, - NULL, }, - { gen_helper_sve_ldbsu_zsu, - gen_helper_sve_ldhsu_zsu, - gen_helper_sve_ldssu_zsu, } }, - { { gen_helper_sve_ldbss_zss, - gen_helper_sve_ldhss_zss, - NULL, }, - { gen_helper_sve_ldbsu_zss, - gen_helper_sve_ldhsu_zss, - gen_helper_sve_ldssu_zss, } } }, +/* Indexed by [be][ff][xs][u][msz]. */ +static gen_helper_gvec_mem_scatter * const gather_load_fn32[2][2][2][2][3] = { + /* Little-endian */ + { { { { gen_helper_sve_ldbss_zsu, + gen_helper_sve_ldhss_le_zsu, + NULL, }, + { gen_helper_sve_ldbsu_zsu, + gen_helper_sve_ldhsu_le_zsu, + gen_helper_sve_ldss_le_zsu, } }, + { { gen_helper_sve_ldbss_zss, + gen_helper_sve_ldhss_le_zss, + NULL, }, + { gen_helper_sve_ldbsu_zss, + gen_helper_sve_ldhsu_le_zss, + gen_helper_sve_ldss_le_zss, } } }, - { { { gen_helper_sve_ldffbss_zsu, - gen_helper_sve_ldffhss_zsu, - NULL, }, - { gen_helper_sve_ldffbsu_zsu, - gen_helper_sve_ldffhsu_zsu, - gen_helper_sve_ldffssu_zsu, } }, - { { gen_helper_sve_ldffbss_zss, - gen_helper_sve_ldffhss_zss, - NULL, }, - { gen_helper_sve_ldffbsu_zss, - gen_helper_sve_ldffhsu_zss, - gen_helper_sve_ldffssu_zss, } } } + /* First-fault */ + { { { gen_helper_sve_ldffbss_zsu, + gen_helper_sve_ldffhss_le_zsu, + NULL, }, + { gen_helper_sve_ldffbsu_zsu, + gen_helper_sve_ldffhsu_le_zsu, + gen_helper_sve_ldffss_le_zsu, } }, + { { gen_helper_sve_ldffbss_zss, + gen_helper_sve_ldffhss_le_zss, + NULL, }, + { gen_helper_sve_ldffbsu_zss, + gen_helper_sve_ldffhsu_le_zss, + gen_helper_sve_ldffss_le_zss, } } } }, + + /* Big-endian */ + { { { { gen_helper_sve_ldbss_zsu, + gen_helper_sve_ldhss_be_zsu, + NULL, }, + { gen_helper_sve_ldbsu_zsu, + gen_helper_sve_ldhsu_be_zsu, + gen_helper_sve_ldss_be_zsu, } }, + { { gen_helper_sve_ldbss_zss, + gen_helper_sve_ldhss_be_zss, + NULL, }, + { gen_helper_sve_ldbsu_zss, + gen_helper_sve_ldhsu_be_zss, + gen_helper_sve_ldss_be_zss, } } }, + + /* First-fault */ + { { { gen_helper_sve_ldffbss_zsu, + gen_helper_sve_ldffhss_be_zsu, + NULL, }, + { gen_helper_sve_ldffbsu_zsu, + gen_helper_sve_ldffhsu_be_zsu, + gen_helper_sve_ldffss_be_zsu, } }, + { { gen_helper_sve_ldffbss_zss, + gen_helper_sve_ldffhss_be_zss, + NULL, }, + { gen_helper_sve_ldffbsu_zss, + gen_helper_sve_ldffhsu_be_zss, + gen_helper_sve_ldffss_be_zss, } } } }, }; /* Note that we overload xs=2 to indicate 64-bit offset. */ -static gen_helper_gvec_mem_scatter * const gather_load_fn64[2][3][2][4] = { - { { { gen_helper_sve_ldbds_zsu, - gen_helper_sve_ldhds_zsu, - gen_helper_sve_ldsds_zsu, - NULL, }, - { gen_helper_sve_ldbdu_zsu, - gen_helper_sve_ldhdu_zsu, - gen_helper_sve_ldsdu_zsu, - gen_helper_sve_ldddu_zsu, } }, - { { gen_helper_sve_ldbds_zss, - gen_helper_sve_ldhds_zss, - gen_helper_sve_ldsds_zss, - NULL, }, - { gen_helper_sve_ldbdu_zss, - gen_helper_sve_ldhdu_zss, - gen_helper_sve_ldsdu_zss, - gen_helper_sve_ldddu_zss, } }, - { { gen_helper_sve_ldbds_zd, - gen_helper_sve_ldhds_zd, - gen_helper_sve_ldsds_zd, - NULL, }, - { gen_helper_sve_ldbdu_zd, - gen_helper_sve_ldhdu_zd, - gen_helper_sve_ldsdu_zd, - gen_helper_sve_ldddu_zd, } } }, +static gen_helper_gvec_mem_scatter * const gather_load_fn64[2][2][3][2][4] = { + /* Little-endian */ + { { { { gen_helper_sve_ldbds_zsu, + gen_helper_sve_ldhds_le_zsu, + gen_helper_sve_ldsds_le_zsu, + NULL, }, + { gen_helper_sve_ldbdu_zsu, + gen_helper_sve_ldhdu_le_zsu, + gen_helper_sve_ldsdu_le_zsu, + gen_helper_sve_lddd_le_zsu, } }, + { { gen_helper_sve_ldbds_zss, + gen_helper_sve_ldhds_le_zss, + gen_helper_sve_ldsds_le_zss, + NULL, }, + { gen_helper_sve_ldbdu_zss, + gen_helper_sve_ldhdu_le_zss, + gen_helper_sve_ldsdu_le_zss, + gen_helper_sve_lddd_le_zss, } }, + { { gen_helper_sve_ldbds_zd, + gen_helper_sve_ldhds_le_zd, + gen_helper_sve_ldsds_le_zd, + NULL, }, + { gen_helper_sve_ldbdu_zd, + gen_helper_sve_ldhdu_le_zd, + gen_helper_sve_ldsdu_le_zd, + gen_helper_sve_lddd_le_zd, } } }, - { { { gen_helper_sve_ldffbds_zsu, - gen_helper_sve_ldffhds_zsu, - gen_helper_sve_ldffsds_zsu, - NULL, }, - { gen_helper_sve_ldffbdu_zsu, - gen_helper_sve_ldffhdu_zsu, - gen_helper_sve_ldffsdu_zsu, - gen_helper_sve_ldffddu_zsu, } }, - { { gen_helper_sve_ldffbds_zss, - gen_helper_sve_ldffhds_zss, - gen_helper_sve_ldffsds_zss, - NULL, }, - { gen_helper_sve_ldffbdu_zss, - gen_helper_sve_ldffhdu_zss, - gen_helper_sve_ldffsdu_zss, - gen_helper_sve_ldffddu_zss, } }, - { { gen_helper_sve_ldffbds_zd, - gen_helper_sve_ldffhds_zd, - gen_helper_sve_ldffsds_zd, - NULL, }, - { gen_helper_sve_ldffbdu_zd, - gen_helper_sve_ldffhdu_zd, - gen_helper_sve_ldffsdu_zd, - gen_helper_sve_ldffddu_zd, } } } + /* First-fault */ + { { { gen_helper_sve_ldffbds_zsu, + gen_helper_sve_ldffhds_le_zsu, + gen_helper_sve_ldffsds_le_zsu, + NULL, }, + { gen_helper_sve_ldffbdu_zsu, + gen_helper_sve_ldffhdu_le_zsu, + gen_helper_sve_ldffsdu_le_zsu, + gen_helper_sve_ldffdd_le_zsu, } }, + { { gen_helper_sve_ldffbds_zss, + gen_helper_sve_ldffhds_le_zss, + gen_helper_sve_ldffsds_le_zss, + NULL, }, + { gen_helper_sve_ldffbdu_zss, + gen_helper_sve_ldffhdu_le_zss, + gen_helper_sve_ldffsdu_le_zss, + gen_helper_sve_ldffdd_le_zss, } }, + { { gen_helper_sve_ldffbds_zd, + gen_helper_sve_ldffhds_le_zd, + gen_helper_sve_ldffsds_le_zd, + NULL, }, + { gen_helper_sve_ldffbdu_zd, + gen_helper_sve_ldffhdu_le_zd, + gen_helper_sve_ldffsdu_le_zd, + gen_helper_sve_ldffdd_le_zd, } } } }, + + /* Big-endian */ + { { { { gen_helper_sve_ldbds_zsu, + gen_helper_sve_ldhds_be_zsu, + gen_helper_sve_ldsds_be_zsu, + NULL, }, + { gen_helper_sve_ldbdu_zsu, + gen_helper_sve_ldhdu_be_zsu, + gen_helper_sve_ldsdu_be_zsu, + gen_helper_sve_lddd_be_zsu, } }, + { { gen_helper_sve_ldbds_zss, + gen_helper_sve_ldhds_be_zss, + gen_helper_sve_ldsds_be_zss, + NULL, }, + { gen_helper_sve_ldbdu_zss, + gen_helper_sve_ldhdu_be_zss, + gen_helper_sve_ldsdu_be_zss, + gen_helper_sve_lddd_be_zss, } }, + { { gen_helper_sve_ldbds_zd, + gen_helper_sve_ldhds_be_zd, + gen_helper_sve_ldsds_be_zd, + NULL, }, + { gen_helper_sve_ldbdu_zd, + gen_helper_sve_ldhdu_be_zd, + gen_helper_sve_ldsdu_be_zd, + gen_helper_sve_lddd_be_zd, } } }, + + /* First-fault */ + { { { gen_helper_sve_ldffbds_zsu, + gen_helper_sve_ldffhds_be_zsu, + gen_helper_sve_ldffsds_be_zsu, + NULL, }, + { gen_helper_sve_ldffbdu_zsu, + gen_helper_sve_ldffhdu_be_zsu, + gen_helper_sve_ldffsdu_be_zsu, + gen_helper_sve_ldffdd_be_zsu, } }, + { { gen_helper_sve_ldffbds_zss, + gen_helper_sve_ldffhds_be_zss, + gen_helper_sve_ldffsds_be_zss, + NULL, }, + { gen_helper_sve_ldffbdu_zss, + gen_helper_sve_ldffhdu_be_zss, + gen_helper_sve_ldffsdu_be_zss, + gen_helper_sve_ldffdd_be_zss, } }, + { { gen_helper_sve_ldffbds_zd, + gen_helper_sve_ldffhds_be_zd, + gen_helper_sve_ldffsds_be_zd, + NULL, }, + { gen_helper_sve_ldffbdu_zd, + gen_helper_sve_ldffhdu_be_zd, + gen_helper_sve_ldffsdu_be_zd, + gen_helper_sve_ldffdd_be_zd, } } } }, }; -static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a, uint32_t insn) +static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) { gen_helper_gvec_mem_scatter *fn = NULL; + int be = s->be_data == MO_BE; if (!sve_access_check(s)) { return true; @@ -5025,22 +5258,23 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a, uint32_t insn) switch (a->esz) { case MO_32: - fn = gather_load_fn32[a->ff][a->xs][a->u][a->msz]; + fn = gather_load_fn32[be][a->ff][a->xs][a->u][a->msz]; break; case MO_64: - fn = gather_load_fn64[a->ff][a->xs][a->u][a->msz]; + fn = gather_load_fn64[be][a->ff][a->xs][a->u][a->msz]; break; } assert(fn != NULL); do_mem_zpz(s, a->rd, a->pg, a->rm, a->scale * a->msz, - cpu_reg_sp(s, a->rn), fn); + cpu_reg_sp(s, a->rn), a->msz, fn); return true; } -static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a, uint32_t insn) +static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) { gen_helper_gvec_mem_scatter *fn = NULL; + int be = s->be_data == MO_BE; TCGv_i64 imm; if (a->esz < a->msz || (a->esz == a->msz && !a->u)) { @@ -5052,10 +5286,10 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a, uint32_t insn) switch (a->esz) { case MO_32: - fn = gather_load_fn32[a->ff][0][a->u][a->msz]; + fn = gather_load_fn32[be][a->ff][0][a->u][a->msz]; break; case MO_64: - fn = gather_load_fn64[a->ff][2][a->u][a->msz]; + fn = gather_load_fn64[be][a->ff][2][a->u][a->msz]; break; } assert(fn != NULL); @@ -5064,40 +5298,63 @@ static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a, uint32_t insn) * by loading the immediate into the scalar parameter. */ imm = tcg_const_i64(a->imm << a->msz); - do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, fn); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, a->msz, fn); tcg_temp_free_i64(imm); return true; } -/* Indexed by [xs][msz]. */ -static gen_helper_gvec_mem_scatter * const scatter_store_fn32[2][3] = { - { gen_helper_sve_stbs_zsu, - gen_helper_sve_sths_zsu, - gen_helper_sve_stss_zsu, }, - { gen_helper_sve_stbs_zss, - gen_helper_sve_sths_zss, - gen_helper_sve_stss_zss, }, +/* Indexed by [be][xs][msz]. */ +static gen_helper_gvec_mem_scatter * const scatter_store_fn32[2][2][3] = { + /* Little-endian */ + { { gen_helper_sve_stbs_zsu, + gen_helper_sve_sths_le_zsu, + gen_helper_sve_stss_le_zsu, }, + { gen_helper_sve_stbs_zss, + gen_helper_sve_sths_le_zss, + gen_helper_sve_stss_le_zss, } }, + /* Big-endian */ + { { gen_helper_sve_stbs_zsu, + gen_helper_sve_sths_be_zsu, + gen_helper_sve_stss_be_zsu, }, + { gen_helper_sve_stbs_zss, + gen_helper_sve_sths_be_zss, + gen_helper_sve_stss_be_zss, } }, }; /* Note that we overload xs=2 to indicate 64-bit offset. */ -static gen_helper_gvec_mem_scatter * const scatter_store_fn64[3][4] = { - { gen_helper_sve_stbd_zsu, - gen_helper_sve_sthd_zsu, - gen_helper_sve_stsd_zsu, - gen_helper_sve_stdd_zsu, }, - { gen_helper_sve_stbd_zss, - gen_helper_sve_sthd_zss, - gen_helper_sve_stsd_zss, - gen_helper_sve_stdd_zss, }, - { gen_helper_sve_stbd_zd, - gen_helper_sve_sthd_zd, - gen_helper_sve_stsd_zd, - gen_helper_sve_stdd_zd, }, +static gen_helper_gvec_mem_scatter * const scatter_store_fn64[2][3][4] = { + /* Little-endian */ + { { gen_helper_sve_stbd_zsu, + gen_helper_sve_sthd_le_zsu, + gen_helper_sve_stsd_le_zsu, + gen_helper_sve_stdd_le_zsu, }, + { gen_helper_sve_stbd_zss, + gen_helper_sve_sthd_le_zss, + gen_helper_sve_stsd_le_zss, + gen_helper_sve_stdd_le_zss, }, + { gen_helper_sve_stbd_zd, + gen_helper_sve_sthd_le_zd, + gen_helper_sve_stsd_le_zd, + gen_helper_sve_stdd_le_zd, } }, + /* Big-endian */ + { { gen_helper_sve_stbd_zsu, + gen_helper_sve_sthd_be_zsu, + gen_helper_sve_stsd_be_zsu, + gen_helper_sve_stdd_be_zsu, }, + { gen_helper_sve_stbd_zss, + gen_helper_sve_sthd_be_zss, + gen_helper_sve_stsd_be_zss, + gen_helper_sve_stdd_be_zss, }, + { gen_helper_sve_stbd_zd, + gen_helper_sve_sthd_be_zd, + gen_helper_sve_stsd_be_zd, + gen_helper_sve_stdd_be_zd, } }, }; -static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) +static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) { gen_helper_gvec_mem_scatter *fn; + int be = s->be_data == MO_BE; if (a->esz < a->msz || (a->msz == 0 && a->scale)) { return false; @@ -5107,22 +5364,23 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a, uint32_t insn) } switch (a->esz) { case MO_32: - fn = scatter_store_fn32[a->xs][a->msz]; + fn = scatter_store_fn32[be][a->xs][a->msz]; break; case MO_64: - fn = scatter_store_fn64[a->xs][a->msz]; + fn = scatter_store_fn64[be][a->xs][a->msz]; break; default: g_assert_not_reached(); } do_mem_zpz(s, a->rd, a->pg, a->rm, a->scale * a->msz, - cpu_reg_sp(s, a->rn), fn); + cpu_reg_sp(s, a->rn), a->msz, fn); return true; } -static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) +static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) { gen_helper_gvec_mem_scatter *fn = NULL; + int be = s->be_data == MO_BE; TCGv_i64 imm; if (a->esz < a->msz) { @@ -5134,10 +5392,10 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) switch (a->esz) { case MO_32: - fn = scatter_store_fn32[0][a->msz]; + fn = scatter_store_fn32[be][0][a->msz]; break; case MO_64: - fn = scatter_store_fn64[2][a->msz]; + fn = scatter_store_fn64[be][2][a->msz]; break; } assert(fn != NULL); @@ -5146,7 +5404,7 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) * by loading the immediate into the scalar parameter. */ imm = tcg_const_i64(a->imm << a->msz); - do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, fn); + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, imm, a->msz, fn); tcg_temp_free_i64(imm); return true; } @@ -5155,14 +5413,14 @@ static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a, uint32_t insn) * Prefetches */ -static bool trans_PRF(DisasContext *s, arg_PRF *a, uint32_t insn) +static bool trans_PRF(DisasContext *s, arg_PRF *a) { /* Prefetch is a nop within QEMU. */ (void)sve_access_check(s); return true; } -static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a, uint32_t insn) +static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a) { if (a->rm == 31) { return false; @@ -5186,12 +5444,12 @@ static bool trans_PRF_rr(DisasContext *s, arg_PRF_rr *a, uint32_t insn) * In the meantime, just emit the moves. */ -static bool trans_MOVPRFX(DisasContext *s, arg_MOVPRFX *a, uint32_t insn) +static bool trans_MOVPRFX(DisasContext *s, arg_MOVPRFX *a) { return do_mov_z(s, a->rd, a->rn); } -static bool trans_MOVPRFX_m(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_MOVPRFX_m(DisasContext *s, arg_rpr_esz *a) { if (sve_access_check(s)) { do_sel_z(s, a->rd, a->rn, a->rd, a->pg, a->esz); @@ -5199,7 +5457,7 @@ static bool trans_MOVPRFX_m(DisasContext *s, arg_rpr_esz *a, uint32_t insn) return true; } -static bool trans_MOVPRFX_z(DisasContext *s, arg_rpr_esz *a, uint32_t insn) +static bool trans_MOVPRFX_z(DisasContext *s, arg_rpr_esz *a) { if (sve_access_check(s)) { do_movz_zpz(s, a->rd, a->rn, a->pg, a->esz); diff --git a/target/arm/translate.c b/target/arm/translate.c index f845da7c63..d408e4d7ef 100644 --- a/target/arm/translate.c +++ b/target/arm/translate.c @@ -42,7 +42,7 @@ #define ENABLE_ARCH_5 arm_dc_feature(s, ARM_FEATURE_V5) /* currently all emulated v5 cores are also v5TE, so don't bother */ #define ENABLE_ARCH_5TE arm_dc_feature(s, ARM_FEATURE_V5) -#define ENABLE_ARCH_5J arm_dc_feature(s, ARM_FEATURE_JAZELLE) +#define ENABLE_ARCH_5J dc_isar_feature(jazelle, s) #define ENABLE_ARCH_6 arm_dc_feature(s, ARM_FEATURE_V6) #define ENABLE_ARCH_6K arm_dc_feature(s, ARM_FEATURE_V6K) #define ENABLE_ARCH_6T2 arm_dc_feature(s, ARM_FEATURE_THUMB2) @@ -72,7 +72,7 @@ static TCGv_i64 cpu_F0d, cpu_F1d; #include "exec/gen-icount.h" -static const char *regnames[] = +static const char * const regnames[] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" }; @@ -239,6 +239,23 @@ static void store_reg(DisasContext *s, int reg, TCGv_i32 var) tcg_temp_free_i32(var); } +/* + * Variant of store_reg which applies v8M stack-limit checks before updating + * SP. If the check fails this will result in an exception being taken. + * We disable the stack checks for CONFIG_USER_ONLY because we have + * no idea what the stack limits should be in that case. + * If stack checking is not being done this just acts like store_reg(). + */ +static void store_sp_checked(DisasContext *s, TCGv_i32 var) +{ +#ifndef CONFIG_USER_ONLY + if (s->v8m_stackcheck) { + gen_helper_v8m_stackcheck(cpu_env, var); + } +#endif + store_reg(s, 13, var); +} + /* Value extensions. */ #define gen_uxtb(var) tcg_gen_ext8u_i32(var, var) #define gen_uxth(var) tcg_gen_ext16u_i32(var, var) @@ -281,17 +298,6 @@ static void gen_exception(int excp, uint32_t syndrome, uint32_t target_el) tcg_temp_free_i32(tcg_excp); } -static void gen_ss_advance(DisasContext *s) -{ - /* If the singlestep state is Active-not-pending, advance to - * Active-pending. - */ - if (s->ss_active) { - s->pstate_ss = 0; - gen_helper_clear_pstate_ss(cpu_env); - } -} - static void gen_step_complete_exception(DisasContext *s) { /* We just completed step of an insn. Move from Active-not-pending @@ -1568,6 +1574,25 @@ neon_reg_offset (int reg, int n) return vfp_reg_offset(0, sreg); } +/* Return the offset of a 2**SIZE piece of a NEON register, at index ELE, + * where 0 is the least significant end of the register. + */ +static inline long +neon_element_offset(int reg, int element, TCGMemOp size) +{ + int element_size = 1 << size; + int ofs = element * element_size; +#ifdef HOST_WORDS_BIGENDIAN + /* Calculate the offset assuming fully little-endian, + * then XOR to account for the order of the 8-byte units. + */ + if (element_size < 8) { + ofs ^= 8 - element_size; + } +#endif + return neon_reg_offset(reg, 0) + ofs; +} + static TCGv_i32 neon_load_reg(int reg, int pass) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -1575,12 +1600,94 @@ static TCGv_i32 neon_load_reg(int reg, int pass) return tmp; } +static void neon_load_element(TCGv_i32 var, int reg, int ele, TCGMemOp mop) +{ + long offset = neon_element_offset(reg, ele, mop & MO_SIZE); + + switch (mop) { + case MO_UB: + tcg_gen_ld8u_i32(var, cpu_env, offset); + break; + case MO_UW: + tcg_gen_ld16u_i32(var, cpu_env, offset); + break; + case MO_UL: + tcg_gen_ld_i32(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + +static void neon_load_element64(TCGv_i64 var, int reg, int ele, TCGMemOp mop) +{ + long offset = neon_element_offset(reg, ele, mop & MO_SIZE); + + switch (mop) { + case MO_UB: + tcg_gen_ld8u_i64(var, cpu_env, offset); + break; + case MO_UW: + tcg_gen_ld16u_i64(var, cpu_env, offset); + break; + case MO_UL: + tcg_gen_ld32u_i64(var, cpu_env, offset); + break; + case MO_Q: + tcg_gen_ld_i64(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + static void neon_store_reg(int reg, int pass, TCGv_i32 var) { tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass)); tcg_temp_free_i32(var); } +static void neon_store_element(int reg, int ele, TCGMemOp size, TCGv_i32 var) +{ + long offset = neon_element_offset(reg, ele, size); + + switch (size) { + case MO_8: + tcg_gen_st8_i32(var, cpu_env, offset); + break; + case MO_16: + tcg_gen_st16_i32(var, cpu_env, offset); + break; + case MO_32: + tcg_gen_st_i32(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + +static void neon_store_element64(int reg, int ele, TCGMemOp size, TCGv_i64 var) +{ + long offset = neon_element_offset(reg, ele, size); + + switch (size) { + case MO_8: + tcg_gen_st8_i64(var, cpu_env, offset); + break; + case MO_16: + tcg_gen_st16_i64(var, cpu_env, offset); + break; + case MO_32: + tcg_gen_st32_i64(var, cpu_env, offset); + break; + case MO_64: + tcg_gen_st_i64(var, cpu_env, offset); + break; + default: + g_assert_not_reached(); + } +} + static inline void neon_load_reg64(TCGv_i64 var, int reg) { tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg)); @@ -1627,7 +1734,7 @@ static inline void gen_mov_vreg_F0(int dp, int reg) tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg)); } -#define ARM_CP_RW_BIT (1 << 20) +#define ARM_CP_RW_BIT (1 << 20) static inline void iwmmxt_load_reg(TCGv_i64 var, int reg) { @@ -1861,12 +1968,12 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) wrd = insn & 0xf; rdlo = (insn >> 12) & 0xf; rdhi = (insn >> 16) & 0xf; - if (insn & ARM_CP_RW_BIT) { /* TMRRC */ + if (insn & ARM_CP_RW_BIT) { /* TMRRC */ iwmmxt_load_reg(cpu_V0, wrd); tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); tcg_gen_extrl_i64_i32(cpu_R[rdhi], cpu_V0); - } else { /* TMCRR */ + } else { /* TMCRR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, wrd); gen_op_iwmmxt_set_mup(); @@ -1881,25 +1988,25 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; } if (insn & ARM_CP_RW_BIT) { - if ((insn >> 28) == 0xf) { /* WLDRW wCx */ + if ((insn >> 28) == 0xf) { /* WLDRW wCx */ tmp = tcg_temp_new_i32(); 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 */ + if (insn & (1 << 22)) { /* WLDRD */ gen_aa32_ld64(s, cpu_M0, addr, get_mem_index(s)); i = 0; - } else { /* WLDRW wRd */ + } else { /* WLDRW wRd */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); } } else { tmp = tcg_temp_new_i32(); - if (insn & (1 << 22)) { /* WLDRH */ + if (insn & (1 << 22)) { /* WLDRH */ gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - } else { /* WLDRB */ + } else { /* WLDRB */ gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); } } @@ -1910,24 +2017,24 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); } } else { - if ((insn >> 28) == 0xf) { /* WSTRW wCx */ + if ((insn >> 28) == 0xf) { /* WSTRW wCx */ tmp = iwmmxt_load_creg(wrd); 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 */ + if (insn & (1 << 22)) { /* WSTRD */ gen_aa32_st64(s, cpu_M0, addr, get_mem_index(s)); - } else { /* WSTRW wRd */ + } else { /* WSTRW wRd */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); } } else { - if (insn & (1 << 22)) { /* WSTRH */ + if (insn & (1 << 22)) { /* WSTRH */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - } else { /* WSTRB */ + } else { /* WSTRB */ tcg_gen_extrl_i64_i32(tmp, cpu_M0); gen_aa32_st8(s, tmp, addr, get_mem_index(s)); } @@ -1943,7 +2050,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) { - case 0x000: /* WOR */ + case 0x000: /* WOR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -1954,7 +2061,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x011: /* TMCR */ + case 0x011: /* TMCR */ if (insn & 0xf) return 1; rd = (insn >> 12) & 0xf; @@ -1985,7 +2092,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) return 1; } break; - case 0x100: /* WXOR */ + case 0x100: /* WXOR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -1996,7 +2103,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x111: /* TMRC */ + case 0x111: /* TMRC */ if (insn & 0xf) return 1; rd = (insn >> 12) & 0xf; @@ -2004,7 +2111,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = iwmmxt_load_creg(wrd); store_reg(s, rd, tmp); break; - case 0x300: /* WANDN */ + case 0x300: /* WANDN */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2016,7 +2123,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x200: /* WAND */ + case 0x200: /* WAND */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2027,7 +2134,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x810: case 0xa10: /* WMADD */ + case 0x810: case 0xa10: /* WMADD */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 0) & 0xf; rd1 = (insn >> 16) & 0xf; @@ -2039,7 +2146,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ + case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2061,7 +2168,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ + case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2083,7 +2190,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ + case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2097,7 +2204,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ + case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2116,7 +2223,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ + case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2132,7 +2239,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ + case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2154,7 +2261,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ + case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2174,7 +2281,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ + case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; rd1 = (insn >> 0) & 0xf; @@ -2187,7 +2294,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ + case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */ if (((insn >> 6) & 3) == 3) return 1; rd = (insn >> 12) & 0xf; @@ -2218,7 +2325,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ + case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */ rd = (insn >> 12) & 0xf; wrd = (insn >> 16) & 0xf; if (rd == 15 || ((insn >> 22) & 3) == 3) @@ -2251,7 +2358,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } store_reg(s, rd, tmp); break; - case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ + case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */ if ((insn & 0x000ff008) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2270,7 +2377,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_set_nzcv(tmp); tcg_temp_free_i32(tmp); break; - case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ + case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */ if (((insn >> 6) & 3) == 3) return 1; rd = (insn >> 12) & 0xf; @@ -2291,7 +2398,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ + case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2319,7 +2426,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; - case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ + case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */ wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; gen_op_iwmmxt_movq_M0_wRn(rd0); @@ -2339,7 +2446,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ + case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */ if ((insn & 0x000ff00f) != 0x0003f000 || ((insn >> 22) & 3) == 3) return 1; tmp = iwmmxt_load_creg(ARM_IWMMXT_wCASF); @@ -2367,7 +2474,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; - case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ + case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */ rd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; if ((insn & 0xf) != 0 || ((insn >> 22) & 3) == 3) @@ -2387,7 +2494,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) } store_reg(s, rd, tmp); break; - case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ + case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */ case 0x906: case 0xb06: case 0xd06: case 0xf06: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2419,7 +2526,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ + case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */ case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2450,7 +2557,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ + case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */ case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2481,7 +2588,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ + case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */ case 0x214: case 0x614: case 0xa14: case 0xe14: if (((insn >> 22) & 3) == 0) return 1; @@ -2509,7 +2616,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ + case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */ case 0x014: case 0x414: case 0x814: case 0xc14: if (((insn >> 22) & 3) == 0) return 1; @@ -2537,7 +2644,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ + case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */ case 0x114: case 0x514: case 0x914: case 0xd14: if (((insn >> 22) & 3) == 0) return 1; @@ -2565,7 +2672,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ + case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */ case 0x314: case 0x714: case 0xb14: case 0xf14: if (((insn >> 22) & 3) == 0) return 1; @@ -2601,7 +2708,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ + case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */ case 0x916: case 0xb16: case 0xd16: case 0xf16: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2632,7 +2739,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ + case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */ case 0x816: case 0xa16: case 0xc16: case 0xe16: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2663,7 +2770,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ + case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */ case 0x402: case 0x502: case 0x602: case 0x702: wrd = (insn >> 12) & 0xf; rd0 = (insn >> 16) & 0xf; @@ -2676,7 +2783,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_movq_wRn_M0(wrd); gen_op_iwmmxt_set_mup(); break; - case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ + case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */ case 0x41a: case 0x51a: case 0x61a: case 0x71a: case 0x81a: case 0x91a: case 0xa1a: case 0xb1a: case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a: @@ -2719,7 +2826,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ + case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */ case 0x41e: case 0x51e: case 0x61e: case 0x71e: case 0x81e: case 0x91e: case 0xa1e: case 0xb1e: case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e: @@ -2733,7 +2840,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ + case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */ case 0x418: case 0x518: case 0x618: case 0x718: case 0x818: case 0x918: case 0xa18: case 0xb18: case 0xc18: case 0xd18: case 0xe18: case 0xf18: @@ -2776,7 +2883,7 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) gen_op_iwmmxt_set_mup(); gen_op_iwmmxt_set_cup(); break; - case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ + case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */ case 0x408: case 0x508: case 0x608: case 0x708: case 0x808: case 0x908: case 0xa08: case 0xb08: case 0xc08: case 0xd08: case 0xe08: case 0xf08: @@ -2823,13 +2930,13 @@ static int disas_iwmmxt_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd0); tmp2 = load_reg(s, rd1); switch ((insn >> 16) & 0xf) { - case 0x0: /* TMIA */ + case 0x0: /* TMIA */ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0x8: /* TMIAPH */ + case 0x8: /* TMIAPH */ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ + case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */ if (insn & (1 << 16)) tcg_gen_shri_i32(tmp, tmp, 16); if (insn & (1 << 17)) @@ -2872,16 +2979,16 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd0); tmp2 = load_reg(s, rd1); switch ((insn >> 16) & 0xf) { - case 0x0: /* MIA */ + case 0x0: /* MIA */ gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0x8: /* MIAPH */ + case 0x8: /* MIAPH */ gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, tmp, tmp2); break; - case 0xc: /* MIABB */ - case 0xd: /* MIABT */ - case 0xe: /* MIATB */ - case 0xf: /* MIATT */ + case 0xc: /* MIABB */ + case 0xd: /* MIABT */ + case 0xe: /* MIATB */ + case 0xf: /* MIATT */ if (insn & (1 << 16)) tcg_gen_shri_i32(tmp, tmp, 16); if (insn & (1 << 17)) @@ -2907,13 +3014,13 @@ static int disas_dsp_insn(DisasContext *s, uint32_t insn) if (acc != 0) return 1; - if (insn & ARM_CP_RW_BIT) { /* MRA */ + if (insn & ARM_CP_RW_BIT) { /* MRA */ iwmmxt_load_reg(cpu_V0, acc); tcg_gen_extrl_i64_i32(cpu_R[rdlo], cpu_V0); tcg_gen_shri_i64(cpu_V0, cpu_V0, 32); 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 */ + } else { /* MAR */ tcg_gen_concat_i32_i64(cpu_V0, cpu_R[rdlo], cpu_R[rdhi]); iwmmxt_store_reg(cpu_V0, acc); } @@ -2957,19 +3064,6 @@ static void gen_vfp_msr(TCGv_i32 tmp) tcg_temp_free_i32(tmp); } -static void gen_neon_dup_u8(TCGv_i32 var, int shift) -{ - TCGv_i32 tmp = tcg_temp_new_i32(); - if (shift) - tcg_gen_shri_i32(var, var, shift); - tcg_gen_ext8u_i32(var, var); - tcg_gen_shli_i32(tmp, var, 8); - tcg_gen_or_i32(var, var, tmp); - tcg_gen_shli_i32(tmp, var, 16); - tcg_gen_or_i32(var, var, tmp); - tcg_temp_free_i32(tmp); -} - static void gen_neon_dup_low16(TCGv_i32 var) { TCGv_i32 tmp = tcg_temp_new_i32(); @@ -2988,28 +3082,6 @@ static void gen_neon_dup_high16(TCGv_i32 var) tcg_temp_free_i32(tmp); } -static TCGv_i32 gen_load_and_replicate(DisasContext *s, TCGv_i32 addr, int size) -{ - /* Load a single Neon element and replicate into a 32 bit TCG reg */ - TCGv_i32 tmp = tcg_temp_new_i32(); - switch (size) { - case 0: - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - gen_neon_dup_u8(tmp, 0); - break; - case 1: - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - gen_neon_dup_low16(tmp); - break; - case 2: - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - break; - default: /* Avoid compiler warnings. */ - abort(); - } - return tmp; -} - static int handle_vsel(uint32_t insn, uint32_t rd, uint32_t rn, uint32_t rm, uint32_t dp) { @@ -3274,14 +3346,10 @@ static const uint8_t fp_decode_rm[] = { FPROUNDING_NEGINF, }; -static int disas_vfp_v8_insn(DisasContext *s, uint32_t insn) +static int disas_vfp_misc_insn(DisasContext *s, uint32_t insn) { uint32_t rd, rn, rm, dp = extract32(insn, 8, 1); - if (!arm_dc_feature(s, ARM_FEATURE_V8)) { - return 1; - } - if (dp) { VFP_DREG_D(rd, insn); VFP_DREG_N(rn, insn); @@ -3292,15 +3360,18 @@ static int disas_vfp_v8_insn(DisasContext *s, uint32_t insn) rm = VFP_SREG_M(insn); } - if ((insn & 0x0f800e50) == 0x0e000a00) { + if ((insn & 0x0f800e50) == 0x0e000a00 && dc_isar_feature(aa32_vsel, s)) { return handle_vsel(insn, rd, rn, rm, dp); - } else if ((insn & 0x0fb00e10) == 0x0e800a00) { + } else if ((insn & 0x0fb00e10) == 0x0e800a00 && + dc_isar_feature(aa32_vminmaxnm, s)) { return handle_vminmaxnm(insn, rd, rn, rm, dp); - } else if ((insn & 0x0fbc0ed0) == 0x0eb80a40) { + } else if ((insn & 0x0fbc0ed0) == 0x0eb80a40 && + dc_isar_feature(aa32_vrint, s)) { /* VRINTA, VRINTN, VRINTP, VRINTM */ int rounding = fp_decode_rm[extract32(insn, 16, 2)]; return handle_vrint(insn, rd, rm, dp, rounding); - } else if ((insn & 0x0fbc0e50) == 0x0ebc0a40) { + } else if ((insn & 0x0fbc0e50) == 0x0ebc0a40 && + dc_isar_feature(aa32_vcvt_dr, s)) { /* VCVTA, VCVTN, VCVTP, VCVTM */ int rounding = fp_decode_rm[extract32(insn, 16, 2)]; return handle_vcvt(insn, rd, rm, dp, rounding); @@ -3344,10 +3415,12 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) } if (extract32(insn, 28, 4) == 0xf) { - /* Encodings with T=1 (Thumb) or unconditional (ARM): - * only used in v8 and above. + /* + * Encodings with T=1 (Thumb) or unconditional (ARM): + * only used for the "miscellaneous VFP features" added in v8A + * and v7M (and gated on the MVFR2.FPMisc field). */ - return disas_vfp_v8_insn(s, insn); + return disas_vfp_misc_insn(s, insn); } dp = ((insn & 0xf00) == 0xb00); @@ -3415,17 +3488,10 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) tmp = load_reg(s, rd); if (insn & (1 << 23)) { /* VDUP */ - if (size == 0) { - gen_neon_dup_u8(tmp, 0); - } else if (size == 1) { - gen_neon_dup_low16(tmp); - } - for (n = 0; n <= pass * 2; n++) { - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - neon_store_reg(rn, n, tmp2); - } - neon_store_reg(rn, n, tmp); + int vec_size = pass ? 16 : 8; + tcg_gen_gvec_dup_i32(size, neon_reg_offset(rn, 0), + vec_size, vec_size, tmp); + tcg_temp_free_i32(tmp); } else { /* VMOV */ switch (size) { @@ -3563,52 +3629,125 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) } } else { /* data processing */ + bool rd_is_dp = dp; + bool rm_is_dp = dp; + bool no_output = false; + /* The opcode is in bits 23, 21, 20 and 6. */ op = ((insn >> 20) & 8) | ((insn >> 19) & 6) | ((insn >> 6) & 1); - if (dp) { - if (op == 15) { - /* rn is opcode */ - rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1); - } else { - /* rn is register number */ - VFP_DREG_N(rn, insn); - } + rn = VFP_SREG_N(insn); - if (op == 15 && (rn == 15 || ((rn & 0x1c) == 0x18) || - ((rn & 0x1e) == 0x6))) { - /* Integer or single/half precision destination. */ - rd = VFP_SREG_D(insn); - } else { - VFP_DREG_D(rd, insn); - } - if (op == 15 && - (((rn & 0x1c) == 0x10) || ((rn & 0x14) == 0x14) || - ((rn & 0x1e) == 0x4))) { - /* VCVT from int or half precision is always from S reg - * regardless of dp bit. VCVT with immediate frac_bits - * has same format as SREG_M. + if (op == 15) { + /* rn is opcode, encoded as per VFP_SREG_N. */ + switch (rn) { + case 0x00: /* vmov */ + case 0x01: /* vabs */ + case 0x02: /* vneg */ + case 0x03: /* vsqrt */ + break; + + case 0x04: /* vcvtb.f64.f16, vcvtb.f32.f16 */ + case 0x05: /* vcvtt.f64.f16, vcvtt.f32.f16 */ + /* + * VCVTB, VCVTT: only present with the halfprec extension + * UNPREDICTABLE if bit 8 is set prior to ARMv8 + * (we choose to UNDEF) */ - rm = VFP_SREG_M(insn); - } else { - VFP_DREG_M(rm, insn); + if (dp) { + if (!dc_isar_feature(aa32_fp16_dpconv, s)) { + return 1; + } + } else { + if (!dc_isar_feature(aa32_fp16_spconv, s)) { + return 1; + } + } + rm_is_dp = false; + break; + case 0x06: /* vcvtb.f16.f32, vcvtb.f16.f64 */ + case 0x07: /* vcvtt.f16.f32, vcvtt.f16.f64 */ + if (dp) { + if (!dc_isar_feature(aa32_fp16_dpconv, s)) { + return 1; + } + } else { + if (!dc_isar_feature(aa32_fp16_spconv, s)) { + return 1; + } + } + rd_is_dp = false; + break; + + case 0x08: case 0x0a: /* vcmp, vcmpz */ + case 0x09: case 0x0b: /* vcmpe, vcmpez */ + no_output = true; + break; + + case 0x0c: /* vrintr */ + case 0x0d: /* vrintz */ + case 0x0e: /* vrintx */ + break; + + case 0x0f: /* vcvt double<->single */ + rd_is_dp = !dp; + break; + + case 0x10: /* vcvt.fxx.u32 */ + case 0x11: /* vcvt.fxx.s32 */ + rm_is_dp = false; + break; + case 0x18: /* vcvtr.u32.fxx */ + case 0x19: /* vcvtz.u32.fxx */ + case 0x1a: /* vcvtr.s32.fxx */ + case 0x1b: /* vcvtz.s32.fxx */ + rd_is_dp = false; + break; + + case 0x14: /* vcvt fp <-> fixed */ + case 0x15: + case 0x16: + case 0x17: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: + if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { + return 1; + } + /* Immediate frac_bits has same format as SREG_M. */ + rm_is_dp = false; + break; + + case 0x13: /* vjcvt */ + if (!dp || !dc_isar_feature(aa32_jscvt, s)) { + return 1; + } + rd_is_dp = false; + break; + + default: + return 1; } + } else if (dp) { + /* rn is register number */ + VFP_DREG_N(rn, insn); + } + + if (rd_is_dp) { + VFP_DREG_D(rd, insn); + } else { + rd = VFP_SREG_D(insn); + } + if (rm_is_dp) { + VFP_DREG_M(rm, insn); } else { - rn = VFP_SREG_N(insn); - if (op == 15 && rn == 15) { - /* Double precision destination. */ - VFP_DREG_D(rd, insn); - } else { - rd = VFP_SREG_D(insn); - } - /* NB that we implicitly rely on the encoding for the frac_bits - * in VCVT of fixed to float being the same as that of an SREG_M - */ rm = VFP_SREG_M(insn); } veclen = s->vec_len; - if (op == 15 && rn > 3) + if (op == 15 && rn > 3) { veclen = 0; + } /* Shut up compiler warnings. */ delta_m = 0; @@ -3644,55 +3783,28 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) /* Load the initial operands. */ if (op == 15) { switch (rn) { - case 16: - case 17: - /* Integer source */ - gen_mov_F0_vreg(0, rm); - break; - case 8: - case 9: - /* Compare */ + case 0x08: case 0x09: /* Compare */ gen_mov_F0_vreg(dp, rd); gen_mov_F1_vreg(dp, rm); break; - case 10: - case 11: - /* Compare with zero */ + case 0x0a: case 0x0b: /* Compare with zero */ gen_mov_F0_vreg(dp, rd); gen_vfp_F1_ld0(dp); break; - case 20: - case 21: - case 22: - case 23: - case 28: - case 29: - case 30: - case 31: + case 0x14: /* vcvt fp <-> fixed */ + case 0x15: + case 0x16: + case 0x17: + case 0x1c: + case 0x1d: + case 0x1e: + case 0x1f: /* Source and destination the same. */ gen_mov_F0_vreg(dp, rd); break; - case 4: - case 5: - case 6: - case 7: - /* VCVTB, VCVTT: only present with the halfprec extension - * UNPREDICTABLE if bit 8 is set prior to ARMv8 - * (we choose to UNDEF) - */ - if ((dp && !arm_dc_feature(s, ARM_FEATURE_V8)) || - !arm_dc_feature(s, ARM_FEATURE_VFP_FP16)) { - return 1; - } - if (!extract32(rn, 1, 1)) { - /* Half precision source. */ - gen_mov_F0_vreg(0, rm); - break; - } - /* Otherwise fall through */ default: /* One source operand. */ - gen_mov_F0_vreg(dp, rm); + gen_mov_F0_vreg(rm_is_dp, rm); break; } } else { @@ -3971,10 +4083,11 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) break; } case 15: /* single<->double conversion */ - if (dp) + if (dp) { gen_helper_vfp_fcvtsd(cpu_F0s, cpu_F0d, cpu_env); - else + } else { gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env); + } break; case 16: /* fuito */ gen_vfp_uito(dp, 0); @@ -3982,28 +4095,19 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) case 17: /* fsito */ gen_vfp_sito(dp, 0); break; + case 19: /* vjcvt */ + gen_helper_vjcvt(cpu_F0s, cpu_F0d, cpu_env); + break; case 20: /* fshto */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_shto(dp, 16 - rm, 0); break; case 21: /* fslto */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_slto(dp, 32 - rm, 0); break; case 22: /* fuhto */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_uhto(dp, 16 - rm, 0); break; case 23: /* fulto */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_ulto(dp, 32 - rm, 0); break; case 24: /* ftoui */ @@ -4019,57 +4123,34 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) gen_vfp_tosiz(dp, 0); break; case 28: /* ftosh */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_tosh(dp, 16 - rm, 0); break; case 29: /* ftosl */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_tosl(dp, 32 - rm, 0); break; case 30: /* ftouh */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_touh(dp, 16 - rm, 0); break; case 31: /* ftoul */ - if (!arm_dc_feature(s, ARM_FEATURE_VFP3)) { - return 1; - } gen_vfp_toul(dp, 32 - rm, 0); break; default: /* undefined */ - return 1; + g_assert_not_reached(); } break; default: /* undefined */ return 1; } - /* Write back the result. */ - if (op == 15 && (rn >= 8 && rn <= 11)) { - /* Comparison, do nothing. */ - } else if (op == 15 && dp && ((rn & 0x1c) == 0x18 || - (rn & 0x1e) == 0x6)) { - /* VCVT double to int: always integer result. - * VCVT double to half precision is always a single - * precision result. - */ - gen_mov_vreg_F0(0, rd); - } else if (op == 15 && rn == 15) { - /* conversion */ - gen_mov_vreg_F0(!dp, rd); - } else { - gen_mov_vreg_F0(dp, rd); + /* Write back the result, if any. */ + if (!no_output) { + gen_mov_vreg_F0(rd_is_dp, rd); } /* break out of the loop if we have finished */ - if (veclen == 0) + if (veclen == 0) { break; + } if (op == 15 && delta_m == 0) { /* single source one-many */ @@ -4212,6 +4293,18 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn) if (insn & (1 << 24)) /* pre-decrement */ tcg_gen_addi_i32(addr, addr, -((insn & 0xff) << 2)); + if (s->v8m_stackcheck && rn == 13 && w) { + /* + * Here 'addr' is the lowest address we will store to, + * and is either the old SP (if post-increment) or + * the new SP (if pre-decrement). For post-increment + * where the old value is below the limit and the new + * value is above, it is UNKNOWN whether the limit check + * triggers; we choose to trigger. + */ + gen_helper_v8m_stackcheck(cpu_env, addr); + } + if (dp) offset = 8; else @@ -4506,10 +4599,14 @@ static bool msr_banked_access_decode(DisasContext *s, int r, int sysm, int rn, } break; case ARM_CPU_MODE_HYP: - /* Note that we can forbid accesses from EL2 here because they - * must be from Hyp mode itself + /* + * SPSR_hyp and r13_hyp can only be accessed from Monitor mode + * (and so we can forbid accesses from EL2 or below). elr_hyp + * can be accessed also from Hyp mode, so forbid accesses from + * EL0 or EL1. */ - if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 3) { + if (!arm_dc_feature(s, ARM_FEATURE_EL2) || s->current_el < 2 || + (s->current_el < 3 && *regno != 17)) { goto undef; } break; @@ -4668,10 +4765,10 @@ static inline void gen_neon_rsb(int size, TCGv_i32 t0, TCGv_i32 t1) } /* 32-bit pairwise ops end up the same as the elementwise versions. */ -#define gen_helper_neon_pmax_s32 gen_helper_neon_max_s32 -#define gen_helper_neon_pmax_u32 gen_helper_neon_max_u32 -#define gen_helper_neon_pmin_s32 gen_helper_neon_min_s32 -#define gen_helper_neon_pmin_u32 gen_helper_neon_min_u32 +#define gen_helper_neon_pmax_s32 tcg_gen_smax_i32 +#define gen_helper_neon_pmax_u32 tcg_gen_umax_i32 +#define gen_helper_neon_pmin_s32 tcg_gen_smin_i32 +#define gen_helper_neon_pmin_u32 tcg_gen_umin_i32 #define GEN_NEON_INTEGER_OP_ENV(name) do { \ switch ((size << 1) | u) { \ @@ -4874,17 +4971,17 @@ static struct { int nregs; int interleave; int spacing; -} neon_ls_element_type[11] = { - {4, 4, 1}, - {4, 4, 2}, +} const neon_ls_element_type[11] = { + {1, 4, 1}, + {1, 4, 2}, {4, 1, 1}, - {4, 2, 1}, - {3, 3, 1}, - {3, 3, 2}, + {2, 2, 2}, + {1, 3, 1}, + {1, 3, 2}, {3, 1, 1}, {1, 1, 1}, - {2, 2, 1}, - {2, 2, 2}, + {1, 2, 1}, + {1, 2, 2}, {2, 1, 1} }; @@ -4900,10 +4997,11 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) int stride; int size; int reg; - int pass; int load; - int shift; int n; + int vec_size; + int mmu_idx; + TCGMemOp endian; TCGv_i32 addr; TCGv_i32 tmp; TCGv_i32 tmp2; @@ -4915,7 +5013,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, false), s->fp_excp_el); + syn_simd_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } @@ -4925,6 +5023,8 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) rn = (insn >> 16) & 0xf; rm = insn & 0xf; load = (insn & (1 << 21)) != 0; + endian = s->be_data; + mmu_idx = get_mem_index(s); if ((insn & (1 << 23)) == 0) { /* Load store all elements. */ op = (insn >> 8) & 0xf; @@ -4949,104 +5049,44 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) nregs = neon_ls_element_type[op].nregs; interleave = neon_ls_element_type[op].interleave; spacing = neon_ls_element_type[op].spacing; - if (size == 3 && (interleave | spacing) != 1) + if (size == 3 && (interleave | spacing) != 1) { return 1; + } + /* For our purposes, bytes are always little-endian. */ + if (size == 0) { + endian = MO_LE; + } + /* Consecutive little-endian elements from a single register + * can be promoted to a larger little-endian operation. + */ + if (interleave == 1 && endian == MO_LE) { + size = 3; + } + tmp64 = tcg_temp_new_i64(); addr = tcg_temp_new_i32(); + tmp2 = tcg_const_i32(1 << size); load_reg_var(s, addr, rn); - stride = (1 << size) * interleave; for (reg = 0; reg < nregs; reg++) { - if (interleave > 2 || (interleave == 2 && nregs == 2)) { - load_reg_var(s, addr, rn); - tcg_gen_addi_i32(addr, addr, (1 << size) * reg); - } else if (interleave == 2 && nregs == 4 && reg == 2) { - load_reg_var(s, addr, rn); - tcg_gen_addi_i32(addr, addr, 1 << size); - } - if (size == 3) { - tmp64 = tcg_temp_new_i64(); - if (load) { - gen_aa32_ld64(s, tmp64, addr, get_mem_index(s)); - neon_store_reg64(tmp64, rd); - } else { - neon_load_reg64(tmp64, rd); - gen_aa32_st64(s, tmp64, addr, get_mem_index(s)); - } - tcg_temp_free_i64(tmp64); - tcg_gen_addi_i32(addr, addr, stride); - } else { - for (pass = 0; pass < 2; pass++) { - if (size == 2) { - if (load) { - tmp = tcg_temp_new_i32(); - 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(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(s, tmp, addr, get_mem_index(s)); - tcg_gen_addi_i32(addr, addr, stride); - tmp2 = tcg_temp_new_i32(); - 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); - tcg_temp_free_i32(tmp2); - neon_store_reg(rd, pass, tmp); - } else { - tmp = neon_load_reg(rd, pass); - tmp2 = tcg_temp_new_i32(); - tcg_gen_shri_i32(tmp2, tmp, 16); - 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(s, tmp2, addr, get_mem_index(s)); - tcg_temp_free_i32(tmp2); - tcg_gen_addi_i32(addr, addr, stride); - } - } else /* size == 0 */ { - if (load) { - tmp2 = NULL; - for (n = 0; n < 4; n++) { - tmp = tcg_temp_new_i32(); - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - tcg_gen_addi_i32(addr, addr, stride); - if (n == 0) { - tmp2 = tmp; - } else { - tcg_gen_shli_i32(tmp, tmp, n * 8); - tcg_gen_or_i32(tmp2, tmp2, tmp); - tcg_temp_free_i32(tmp); - } - } - neon_store_reg(rd, pass, tmp2); - } else { - tmp2 = neon_load_reg(rd, pass); - for (n = 0; n < 4; n++) { - tmp = tcg_temp_new_i32(); - if (n == 0) { - tcg_gen_mov_i32(tmp, tmp2); - } else { - tcg_gen_shri_i32(tmp, tmp2, n * 8); - } - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); - tcg_temp_free_i32(tmp); - tcg_gen_addi_i32(addr, addr, stride); - } - tcg_temp_free_i32(tmp2); - } + for (n = 0; n < 8 >> size; n++) { + int xs; + for (xs = 0; xs < interleave; xs++) { + int tt = rd + reg + spacing * xs; + + if (load) { + gen_aa32_ld_i64(s, tmp64, addr, mmu_idx, endian | size); + neon_store_element64(tt, n, size, tmp64); + } else { + neon_load_element64(tmp64, tt, n, size); + gen_aa32_st_i64(s, tmp64, addr, mmu_idx, endian | size); } + tcg_gen_add_i32(addr, addr, tmp2); } } - rd += spacing; } tcg_temp_free_i32(addr); - stride = nregs * 8; + tcg_temp_free_i32(tmp2); + tcg_temp_free_i64(tmp64); + stride = nregs * interleave * 8; } else { size = (insn >> 10) & 3; if (size == 3) { @@ -5073,45 +5113,50 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) } addr = tcg_temp_new_i32(); load_reg_var(s, addr, rn); - if (nregs == 1) { - /* VLD1 to all lanes: bit 5 indicates how many Dregs to write */ - tmp = gen_load_and_replicate(s, addr, size); - tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 0)); - tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 1)); - if (insn & (1 << 5)) { - tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd + 1, 0)); - tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd + 1, 1)); - } - tcg_temp_free_i32(tmp); - } else { - /* VLD2/3/4 to all lanes: bit 5 indicates register stride */ - stride = (insn & (1 << 5)) ? 2 : 1; - for (reg = 0; reg < nregs; reg++) { - tmp = gen_load_and_replicate(s, addr, size); - tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 0)); - tcg_gen_st_i32(tmp, cpu_env, neon_reg_offset(rd, 1)); - tcg_temp_free_i32(tmp); - tcg_gen_addi_i32(addr, addr, 1 << size); - rd += stride; + + /* VLD1 to all lanes: bit 5 indicates how many Dregs to write. + * VLD2/3/4 to all lanes: bit 5 indicates register stride. + */ + stride = (insn & (1 << 5)) ? 2 : 1; + vec_size = nregs == 1 ? stride * 8 : 8; + + tmp = tcg_temp_new_i32(); + for (reg = 0; reg < nregs; reg++) { + gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), + s->be_data | size); + if ((rd & 1) && vec_size == 16) { + /* We cannot write 16 bytes at once because the + * destination is unaligned. + */ + tcg_gen_gvec_dup_i32(size, neon_reg_offset(rd, 0), + 8, 8, tmp); + tcg_gen_gvec_mov(0, neon_reg_offset(rd + 1, 0), + neon_reg_offset(rd, 0), 8, 8); + } else { + tcg_gen_gvec_dup_i32(size, neon_reg_offset(rd, 0), + vec_size, vec_size, tmp); } + tcg_gen_addi_i32(addr, addr, 1 << size); + rd += stride; } + tcg_temp_free_i32(tmp); tcg_temp_free_i32(addr); stride = (1 << size) * nregs; } else { /* Single element. */ int idx = (insn >> 4) & 0xf; - pass = (insn >> 7) & 1; + int reg_idx; switch (size) { case 0: - shift = ((insn >> 5) & 3) * 8; + reg_idx = (insn >> 5) & 7; stride = 1; break; case 1: - shift = ((insn >> 6) & 1) * 16; + reg_idx = (insn >> 6) & 3; stride = (insn & (1 << 5)) ? 2 : 1; break; case 2: - shift = 0; + reg_idx = (insn >> 7) & 1; stride = (insn & (1 << 6)) ? 2 : 1; break; default: @@ -5151,52 +5196,24 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) */ return 1; } + tmp = tcg_temp_new_i32(); addr = tcg_temp_new_i32(); load_reg_var(s, addr, rn); for (reg = 0; reg < nregs; reg++) { if (load) { - tmp = tcg_temp_new_i32(); - switch (size) { - case 0: - gen_aa32_ld8u(s, tmp, addr, get_mem_index(s)); - break; - case 1: - gen_aa32_ld16u(s, tmp, addr, get_mem_index(s)); - break; - case 2: - gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); - break; - default: /* Avoid compiler warnings. */ - abort(); - } - if (size != 2) { - tmp2 = neon_load_reg(rd, pass); - tcg_gen_deposit_i32(tmp, tmp2, tmp, - shift, size ? 16 : 8); - tcg_temp_free_i32(tmp2); - } - neon_store_reg(rd, pass, tmp); + gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s), + s->be_data | size); + neon_store_element(rd, reg_idx, size, tmp); } else { /* Store */ - tmp = neon_load_reg(rd, pass); - if (shift) - tcg_gen_shri_i32(tmp, tmp, shift); - switch (size) { - case 0: - gen_aa32_st8(s, tmp, addr, get_mem_index(s)); - break; - case 1: - gen_aa32_st16(s, tmp, addr, get_mem_index(s)); - break; - case 2: - gen_aa32_st32(s, tmp, addr, get_mem_index(s)); - break; - } - tcg_temp_free_i32(tmp); + neon_load_element(tmp, rd, reg_idx, size); + gen_aa32_st_i32(s, tmp, addr, get_mem_index(s), + s->be_data | size); } rd += stride; tcg_gen_addi_i32(addr, addr, 1 << size); } tcg_temp_free_i32(addr); + tcg_temp_free_i32(tmp); stride = nregs * (1 << size); } } @@ -5217,14 +5234,6 @@ static int disas_neon_ls_insn(DisasContext *s, uint32_t insn) return 0; } -/* Bitwise select. dest = c ? t : f. Clobbers T and F. */ -static void gen_neon_bsl(TCGv_i32 dest, TCGv_i32 t, TCGv_i32 f, TCGv_i32 c) -{ - tcg_gen_and_i32(t, t, c); - tcg_gen_andc_i32(f, f, c); - tcg_gen_or_i32(dest, t, f); -} - static inline void gen_neon_narrow(int size, TCGv_i32 dest, TCGv_i64 src) { switch (size) { @@ -5431,7 +5440,7 @@ static void gen_neon_narrow_op(int op, int u, int size, #define NEON_3R_VABA 15 #define NEON_3R_VADD_VSUB 16 #define NEON_3R_VTST_VCEQ 17 -#define NEON_3R_VML 18 /* VMLA, VMLAL, VMLS, VMLSL */ +#define NEON_3R_VML 18 /* VMLA, VMLS */ #define NEON_3R_VMUL 19 #define NEON_3R_VPMAX 20 #define NEON_3R_VPMIN 21 @@ -5656,7 +5665,7 @@ static const uint8_t neon_2rm_sizes[] = { static int do_v81_helper(DisasContext *s, gen_helper_gvec_3_ptr *fn, int q, int rd, int rn, int rm) { - if (arm_dc_feature(s, ARM_FEATURE_V8_RDM)) { + if (dc_isar_feature(aa32_rdm, s)) { int opr_sz = (1 + q) * 8; tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), vfp_reg_offset(1, rn), @@ -5667,6 +5676,619 @@ static int do_v81_helper(DisasContext *s, gen_helper_gvec_3_ptr *fn, return 1; } +/* + * Expanders for VBitOps_VBIF, VBIT, VBSL. + */ +static void gen_bsl_i64(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) +{ + tcg_gen_xor_i64(rn, rn, rm); + tcg_gen_and_i64(rn, rn, rd); + tcg_gen_xor_i64(rd, rm, rn); +} + +static void gen_bit_i64(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) +{ + tcg_gen_xor_i64(rn, rn, rd); + tcg_gen_and_i64(rn, rn, rm); + tcg_gen_xor_i64(rd, rd, rn); +} + +static void gen_bif_i64(TCGv_i64 rd, TCGv_i64 rn, TCGv_i64 rm) +{ + tcg_gen_xor_i64(rn, rn, rd); + tcg_gen_andc_i64(rn, rn, rm); + tcg_gen_xor_i64(rd, rd, rn); +} + +static void gen_bsl_vec(unsigned vece, TCGv_vec rd, TCGv_vec rn, TCGv_vec rm) +{ + tcg_gen_xor_vec(vece, rn, rn, rm); + tcg_gen_and_vec(vece, rn, rn, rd); + tcg_gen_xor_vec(vece, rd, rm, rn); +} + +static void gen_bit_vec(unsigned vece, TCGv_vec rd, TCGv_vec rn, TCGv_vec rm) +{ + tcg_gen_xor_vec(vece, rn, rn, rd); + tcg_gen_and_vec(vece, rn, rn, rm); + tcg_gen_xor_vec(vece, rd, rd, rn); +} + +static void gen_bif_vec(unsigned vece, TCGv_vec rd, TCGv_vec rn, TCGv_vec rm) +{ + tcg_gen_xor_vec(vece, rn, rn, rd); + tcg_gen_andc_vec(vece, rn, rn, rm); + tcg_gen_xor_vec(vece, rd, rd, rn); +} + +const GVecGen3 bsl_op = { + .fni8 = gen_bsl_i64, + .fniv = gen_bsl_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true +}; + +const GVecGen3 bit_op = { + .fni8 = gen_bit_i64, + .fniv = gen_bit_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true +}; + +const GVecGen3 bif_op = { + .fni8 = gen_bif_i64, + .fniv = gen_bif_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true +}; + +static void gen_ssra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_sar8i_i64(a, a, shift); + tcg_gen_vec_add8_i64(d, d, a); +} + +static void gen_ssra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_sar16i_i64(a, a, shift); + tcg_gen_vec_add16_i64(d, d, a); +} + +static void gen_ssra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_sari_i32(a, a, shift); + tcg_gen_add_i32(d, d, a); +} + +static void gen_ssra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_sari_i64(a, a, shift); + tcg_gen_add_i64(d, d, a); +} + +static void gen_ssra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + tcg_gen_sari_vec(vece, a, a, sh); + tcg_gen_add_vec(vece, d, d, a); +} + +const GVecGen2i ssra_op[4] = { + { .fni8 = gen_ssra8_i64, + .fniv = gen_ssra_vec, + .load_dest = true, + .opc = INDEX_op_sari_vec, + .vece = MO_8 }, + { .fni8 = gen_ssra16_i64, + .fniv = gen_ssra_vec, + .load_dest = true, + .opc = INDEX_op_sari_vec, + .vece = MO_16 }, + { .fni4 = gen_ssra32_i32, + .fniv = gen_ssra_vec, + .load_dest = true, + .opc = INDEX_op_sari_vec, + .vece = MO_32 }, + { .fni8 = gen_ssra64_i64, + .fniv = gen_ssra_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opc = INDEX_op_sari_vec, + .vece = MO_64 }, +}; + +static void gen_usra8_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_shr8i_i64(a, a, shift); + tcg_gen_vec_add8_i64(d, d, a); +} + +static void gen_usra16_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_vec_shr16i_i64(a, a, shift); + tcg_gen_vec_add16_i64(d, d, a); +} + +static void gen_usra32_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_shri_i32(a, a, shift); + tcg_gen_add_i32(d, d, a); +} + +static void gen_usra64_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_shri_i64(a, a, shift); + tcg_gen_add_i64(d, d, a); +} + +static void gen_usra_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + tcg_gen_shri_vec(vece, a, a, sh); + tcg_gen_add_vec(vece, d, d, a); +} + +const GVecGen2i usra_op[4] = { + { .fni8 = gen_usra8_i64, + .fniv = gen_usra_vec, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_8, }, + { .fni8 = gen_usra16_i64, + .fniv = gen_usra_vec, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_16, }, + { .fni4 = gen_usra32_i32, + .fniv = gen_usra_vec, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_32, }, + { .fni8 = gen_usra64_i64, + .fniv = gen_usra_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_64, }, +}; + +static void gen_shr8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_8, 0xff >> shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); + tcg_temp_free_i64(t); +} + +static void gen_shr16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_16, 0xffff >> shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shri_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); + tcg_temp_free_i64(t); +} + +static void gen_shr32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_shri_i32(a, a, shift); + tcg_gen_deposit_i32(d, d, a, 0, 32 - shift); +} + +static void gen_shr64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_shri_i64(a, a, shift); + tcg_gen_deposit_i64(d, d, a, 0, 64 - shift); +} + +static void gen_shr_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + if (sh == 0) { + tcg_gen_mov_vec(d, a); + } else { + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_temp_new_vec_matching(d); + + tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK((8 << vece) - sh, sh)); + tcg_gen_shri_vec(vece, t, a, sh); + tcg_gen_and_vec(vece, d, d, m); + tcg_gen_or_vec(vece, d, d, t); + + tcg_temp_free_vec(t); + tcg_temp_free_vec(m); + } +} + +const GVecGen2i sri_op[4] = { + { .fni8 = gen_shr8_ins_i64, + .fniv = gen_shr_ins_vec, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_8 }, + { .fni8 = gen_shr16_ins_i64, + .fniv = gen_shr_ins_vec, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_16 }, + { .fni4 = gen_shr32_ins_i32, + .fniv = gen_shr_ins_vec, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_32 }, + { .fni8 = gen_shr64_ins_i64, + .fniv = gen_shr_ins_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opc = INDEX_op_shri_vec, + .vece = MO_64 }, +}; + +static void gen_shl8_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_8, 0xff << shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); + tcg_temp_free_i64(t); +} + +static void gen_shl16_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + uint64_t mask = dup_const(MO_16, 0xffff << shift); + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_shli_i64(t, a, shift); + tcg_gen_andi_i64(t, t, mask); + tcg_gen_andi_i64(d, d, ~mask); + tcg_gen_or_i64(d, d, t); + tcg_temp_free_i64(t); +} + +static void gen_shl32_ins_i32(TCGv_i32 d, TCGv_i32 a, int32_t shift) +{ + tcg_gen_deposit_i32(d, d, a, shift, 32 - shift); +} + +static void gen_shl64_ins_i64(TCGv_i64 d, TCGv_i64 a, int64_t shift) +{ + tcg_gen_deposit_i64(d, d, a, shift, 64 - shift); +} + +static void gen_shl_ins_vec(unsigned vece, TCGv_vec d, TCGv_vec a, int64_t sh) +{ + if (sh == 0) { + tcg_gen_mov_vec(d, a); + } else { + TCGv_vec t = tcg_temp_new_vec_matching(d); + TCGv_vec m = tcg_temp_new_vec_matching(d); + + tcg_gen_dupi_vec(vece, m, MAKE_64BIT_MASK(0, sh)); + tcg_gen_shli_vec(vece, t, a, sh); + tcg_gen_and_vec(vece, d, d, m); + tcg_gen_or_vec(vece, d, d, t); + + tcg_temp_free_vec(t); + tcg_temp_free_vec(m); + } +} + +const GVecGen2i sli_op[4] = { + { .fni8 = gen_shl8_ins_i64, + .fniv = gen_shl_ins_vec, + .load_dest = true, + .opc = INDEX_op_shli_vec, + .vece = MO_8 }, + { .fni8 = gen_shl16_ins_i64, + .fniv = gen_shl_ins_vec, + .load_dest = true, + .opc = INDEX_op_shli_vec, + .vece = MO_16 }, + { .fni4 = gen_shl32_ins_i32, + .fniv = gen_shl_ins_vec, + .load_dest = true, + .opc = INDEX_op_shli_vec, + .vece = MO_32 }, + { .fni8 = gen_shl64_ins_i64, + .fniv = gen_shl_ins_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .opc = INDEX_op_shli_vec, + .vece = MO_64 }, +}; + +static void gen_mla8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u8(a, a, b); + gen_helper_neon_add_u8(d, d, a); +} + +static void gen_mls8_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u8(a, a, b); + gen_helper_neon_sub_u8(d, d, a); +} + +static void gen_mla16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u16(a, a, b); + gen_helper_neon_add_u16(d, d, a); +} + +static void gen_mls16_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + gen_helper_neon_mul_u16(a, a, b); + gen_helper_neon_sub_u16(d, d, a); +} + +static void gen_mla32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mul_i32(a, a, b); + tcg_gen_add_i32(d, d, a); +} + +static void gen_mls32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_mul_i32(a, a, b); + tcg_gen_sub_i32(d, d, a); +} + +static void gen_mla64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mul_i64(a, a, b); + tcg_gen_add_i64(d, d, a); +} + +static void gen_mls64_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_mul_i64(a, a, b); + tcg_gen_sub_i64(d, d, a); +} + +static void gen_mla_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_mul_vec(vece, a, a, b); + tcg_gen_add_vec(vece, d, d, a); +} + +static void gen_mls_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_mul_vec(vece, a, a, b); + tcg_gen_sub_vec(vece, d, d, a); +} + +/* Note that while NEON does not support VMLA and VMLS as 64-bit ops, + * these tables are shared with AArch64 which does support them. + */ +const GVecGen3 mla_op[4] = { + { .fni4 = gen_mla8_i32, + .fniv = gen_mla_vec, + .opc = INDEX_op_mul_vec, + .load_dest = true, + .vece = MO_8 }, + { .fni4 = gen_mla16_i32, + .fniv = gen_mla_vec, + .opc = INDEX_op_mul_vec, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_mla32_i32, + .fniv = gen_mla_vec, + .opc = INDEX_op_mul_vec, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_mla64_i64, + .fniv = gen_mla_vec, + .opc = INDEX_op_mul_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .vece = MO_64 }, +}; + +const GVecGen3 mls_op[4] = { + { .fni4 = gen_mls8_i32, + .fniv = gen_mls_vec, + .opc = INDEX_op_mul_vec, + .load_dest = true, + .vece = MO_8 }, + { .fni4 = gen_mls16_i32, + .fniv = gen_mls_vec, + .opc = INDEX_op_mul_vec, + .load_dest = true, + .vece = MO_16 }, + { .fni4 = gen_mls32_i32, + .fniv = gen_mls_vec, + .opc = INDEX_op_mul_vec, + .load_dest = true, + .vece = MO_32 }, + { .fni8 = gen_mls64_i64, + .fniv = gen_mls_vec, + .opc = INDEX_op_mul_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .load_dest = true, + .vece = MO_64 }, +}; + +/* CMTST : test is "if (X & Y != 0)". */ +static void gen_cmtst_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +{ + tcg_gen_and_i32(d, a, b); + tcg_gen_setcondi_i32(TCG_COND_NE, d, d, 0); + tcg_gen_neg_i32(d, d); +} + +void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +{ + tcg_gen_and_i64(d, a, b); + tcg_gen_setcondi_i64(TCG_COND_NE, d, d, 0); + tcg_gen_neg_i64(d, d); +} + +static void gen_cmtst_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b) +{ + tcg_gen_and_vec(vece, d, a, b); + tcg_gen_dupi_vec(vece, a, 0); + tcg_gen_cmp_vec(TCG_COND_NE, vece, d, d, a); +} + +const GVecGen3 cmtst_op[4] = { + { .fni4 = gen_helper_neon_tst_u8, + .fniv = gen_cmtst_vec, + .vece = MO_8 }, + { .fni4 = gen_helper_neon_tst_u16, + .fniv = gen_cmtst_vec, + .vece = MO_16 }, + { .fni4 = gen_cmtst_i32, + .fniv = gen_cmtst_vec, + .vece = MO_32 }, + { .fni8 = gen_cmtst_i64, + .fniv = gen_cmtst_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + .vece = MO_64 }, +}; + +static void gen_uqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_add_vec(vece, x, a, b); + tcg_gen_usadd_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); + tcg_temp_free_vec(x); +} + +const GVecGen4 uqadd_op[4] = { + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_b, + .opc = INDEX_op_usadd_vec, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_h, + .opc = INDEX_op_usadd_vec, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_s, + .opc = INDEX_op_usadd_vec, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_uqadd_vec, + .fno = gen_helper_gvec_uqadd_d, + .opc = INDEX_op_usadd_vec, + .write_aofs = true, + .vece = MO_64 }, +}; + +static void gen_sqadd_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_add_vec(vece, x, a, b); + tcg_gen_ssadd_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); + tcg_temp_free_vec(x); +} + +const GVecGen4 sqadd_op[4] = { + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_b, + .opc = INDEX_op_ssadd_vec, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_h, + .opc = INDEX_op_ssadd_vec, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_s, + .opc = INDEX_op_ssadd_vec, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_sqadd_vec, + .fno = gen_helper_gvec_sqadd_d, + .opc = INDEX_op_ssadd_vec, + .write_aofs = true, + .vece = MO_64 }, +}; + +static void gen_uqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_sub_vec(vece, x, a, b); + tcg_gen_ussub_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); + tcg_temp_free_vec(x); +} + +const GVecGen4 uqsub_op[4] = { + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_b, + .opc = INDEX_op_ussub_vec, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_h, + .opc = INDEX_op_ussub_vec, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_s, + .opc = INDEX_op_ussub_vec, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_uqsub_vec, + .fno = gen_helper_gvec_uqsub_d, + .opc = INDEX_op_ussub_vec, + .write_aofs = true, + .vece = MO_64 }, +}; + +static void gen_sqsub_vec(unsigned vece, TCGv_vec t, TCGv_vec sat, + TCGv_vec a, TCGv_vec b) +{ + TCGv_vec x = tcg_temp_new_vec_matching(t); + tcg_gen_sub_vec(vece, x, a, b); + tcg_gen_sssub_vec(vece, t, a, b); + tcg_gen_cmp_vec(TCG_COND_NE, vece, x, x, t); + tcg_gen_or_vec(vece, sat, sat, x); + tcg_temp_free_vec(x); +} + +const GVecGen4 sqsub_op[4] = { + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_b, + .opc = INDEX_op_sssub_vec, + .write_aofs = true, + .vece = MO_8 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_h, + .opc = INDEX_op_sssub_vec, + .write_aofs = true, + .vece = MO_16 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_s, + .opc = INDEX_op_sssub_vec, + .write_aofs = true, + .vece = MO_32 }, + { .fniv = gen_sqsub_vec, + .fno = gen_helper_gvec_sqsub_d, + .opc = INDEX_op_sssub_vec, + .write_aofs = true, + .vece = MO_64 }, +}; + /* Translate a NEON data processing instruction. Return nonzero if the instruction is invalid. We process data in a mixture of 32-bit and 64-bit chunks. @@ -5676,14 +6298,15 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) { int op; int q; - int rd, rn, rm; + int rd, rn, rm, rd_ofs, rn_ofs, rm_ofs; int size; int shift; int pass; int count; int pairwise; int u; - uint32_t imm, mask; + int vec_size; + uint32_t imm; TCGv_i32 tmp, tmp2, tmp3, tmp4, tmp5; TCGv_ptr ptr1, ptr2, ptr3; TCGv_i64 tmp64; @@ -5694,7 +6317,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, false), s->fp_excp_el); + syn_simd_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } @@ -5706,6 +6329,11 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) VFP_DREG_N(rn, insn); VFP_DREG_M(rm, insn); size = (insn >> 20) & 3; + vec_size = q ? 16 : 8; + rd_ofs = neon_reg_offset(rd, 0); + rn_ofs = neon_reg_offset(rn, 0); + rm_ofs = neon_reg_offset(rm, 0); + if ((insn & (1 << 23)) == 0) { /* Three register same length. */ op = ((insn >> 7) & 0x1e) | ((insn >> 4) & 1); @@ -5730,7 +6358,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) return 1; } if (!u) { /* SHA-1 */ - if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA1)) { + if (!dc_isar_feature(aa32_sha1, s)) { return 1; } ptr1 = vfp_reg_ptr(true, rd); @@ -5740,7 +6368,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) gen_helper_crypto_sha1_3reg(ptr1, ptr2, ptr3, tmp4); tcg_temp_free_i32(tmp4); } else { /* SHA-256 */ - if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA256) || size == 3) { + if (!dc_isar_feature(aa32_sha2, s) || size == 3) { return 1; } ptr1 = vfp_reg_ptr(true, rd); @@ -5796,31 +6424,130 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) q, rd, rn, rm); } return 1; + + case NEON_3R_LOGIC: /* Logic ops. */ + switch ((u << 2) | size) { + case 0: /* VAND */ + tcg_gen_gvec_and(0, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + break; + case 1: /* VBIC */ + tcg_gen_gvec_andc(0, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + break; + case 2: /* VORR */ + tcg_gen_gvec_or(0, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + break; + case 3: /* VORN */ + tcg_gen_gvec_orc(0, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + break; + case 4: /* VEOR */ + tcg_gen_gvec_xor(0, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + break; + case 5: /* VBSL */ + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size, &bsl_op); + break; + case 6: /* VBIT */ + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size, &bit_op); + break; + case 7: /* VBIF */ + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size, &bif_op); + break; + } + return 0; + + case NEON_3R_VADD_VSUB: + if (u) { + tcg_gen_gvec_sub(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } else { + tcg_gen_gvec_add(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } + return 0; + + case NEON_3R_VQADD: + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, vec_size, vec_size, + (u ? uqadd_op : sqadd_op) + size); + break; + + case NEON_3R_VQSUB: + tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), + rn_ofs, rm_ofs, vec_size, vec_size, + (u ? uqsub_op : sqsub_op) + size); + break; + + case NEON_3R_VMUL: /* VMUL */ + if (u) { + /* Polynomial case allows only P8 and is handled below. */ + if (size != 0) { + return 1; + } + } else { + tcg_gen_gvec_mul(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + return 0; + } + break; + + case NEON_3R_VML: /* VMLA, VMLS */ + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, vec_size, vec_size, + u ? &mls_op[size] : &mla_op[size]); + return 0; + + case NEON_3R_VTST_VCEQ: + if (u) { /* VCEQ */ + tcg_gen_gvec_cmp(TCG_COND_EQ, size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } else { /* VTST */ + tcg_gen_gvec_3(rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size, &cmtst_op[size]); + } + return 0; + + case NEON_3R_VCGT: + tcg_gen_gvec_cmp(u ? TCG_COND_GTU : TCG_COND_GT, size, + rd_ofs, rn_ofs, rm_ofs, vec_size, vec_size); + return 0; + + case NEON_3R_VCGE: + tcg_gen_gvec_cmp(u ? TCG_COND_GEU : TCG_COND_GE, size, + rd_ofs, rn_ofs, rm_ofs, vec_size, vec_size); + return 0; + + case NEON_3R_VMAX: + if (u) { + tcg_gen_gvec_umax(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } else { + tcg_gen_gvec_smax(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } + return 0; + case NEON_3R_VMIN: + if (u) { + tcg_gen_gvec_umin(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } else { + tcg_gen_gvec_smin(size, rd_ofs, rn_ofs, rm_ofs, + vec_size, vec_size); + } + return 0; } - if (size == 3 && op != NEON_3R_LOGIC) { + + if (size == 3) { /* 64-bit element instructions. */ for (pass = 0; pass < (q ? 2 : 1); pass++) { neon_load_reg64(cpu_V0, rn + pass); neon_load_reg64(cpu_V1, rm + pass); switch (op) { - case NEON_3R_VQADD: - if (u) { - gen_helper_neon_qadd_u64(cpu_V0, cpu_env, - cpu_V0, cpu_V1); - } else { - gen_helper_neon_qadd_s64(cpu_V0, cpu_env, - cpu_V0, cpu_V1); - } - break; - case NEON_3R_VQSUB: - if (u) { - gen_helper_neon_qsub_u64(cpu_V0, cpu_env, - cpu_V0, cpu_V1); - } else { - gen_helper_neon_qsub_s64(cpu_V0, cpu_env, - cpu_V0, cpu_V1); - } - break; case NEON_3R_VSHL: if (u) { gen_helper_neon_shl_u64(cpu_V0, cpu_V1, cpu_V0); @@ -5853,13 +6580,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) cpu_V1, cpu_V0); } break; - case NEON_3R_VADD_VSUB: - if (u) { - tcg_gen_sub_i64(CPU_V001); - } else { - tcg_gen_add_i64(CPU_V001); - } - break; default: abort(); } @@ -5909,12 +6629,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) return 1; } break; - case NEON_3R_VMUL: - if (u && (size != 0)) { - /* UNDEF on invalid size for polynomial subcase */ - return 1; - } - break; case NEON_3R_VFM_VQRDMLSH: if (!arm_dc_feature(s, ARM_FEATURE_VFP4)) { return 1; @@ -5949,58 +6663,12 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case NEON_3R_VHADD: GEN_NEON_INTEGER_OP(hadd); break; - case NEON_3R_VQADD: - GEN_NEON_INTEGER_OP_ENV(qadd); - break; case NEON_3R_VRHADD: GEN_NEON_INTEGER_OP(rhadd); break; - case NEON_3R_LOGIC: /* Logic ops. */ - switch ((u << 2) | size) { - case 0: /* VAND */ - tcg_gen_and_i32(tmp, tmp, tmp2); - break; - case 1: /* BIC */ - tcg_gen_andc_i32(tmp, tmp, tmp2); - break; - case 2: /* VORR */ - tcg_gen_or_i32(tmp, tmp, tmp2); - break; - case 3: /* VORN */ - tcg_gen_orc_i32(tmp, tmp, tmp2); - break; - case 4: /* VEOR */ - tcg_gen_xor_i32(tmp, tmp, tmp2); - break; - case 5: /* VBSL */ - tmp3 = neon_load_reg(rd, pass); - gen_neon_bsl(tmp, tmp, tmp2, tmp3); - tcg_temp_free_i32(tmp3); - break; - case 6: /* VBIT */ - tmp3 = neon_load_reg(rd, pass); - gen_neon_bsl(tmp, tmp, tmp3, tmp2); - tcg_temp_free_i32(tmp3); - break; - case 7: /* VBIF */ - tmp3 = neon_load_reg(rd, pass); - gen_neon_bsl(tmp, tmp3, tmp, tmp2); - tcg_temp_free_i32(tmp3); - break; - } - break; case NEON_3R_VHSUB: GEN_NEON_INTEGER_OP(hsub); break; - case NEON_3R_VQSUB: - GEN_NEON_INTEGER_OP_ENV(qsub); - break; - case NEON_3R_VCGT: - GEN_NEON_INTEGER_OP(cgt); - break; - case NEON_3R_VCGE: - GEN_NEON_INTEGER_OP(cge); - break; case NEON_3R_VSHL: GEN_NEON_INTEGER_OP(shl); break; @@ -6013,12 +6681,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case NEON_3R_VQRSHL: GEN_NEON_INTEGER_OP_ENV(qrshl); break; - case NEON_3R_VMAX: - GEN_NEON_INTEGER_OP(max); - break; - case NEON_3R_VMIN: - GEN_NEON_INTEGER_OP(min); - break; case NEON_3R_VABD: GEN_NEON_INTEGER_OP(abd); break; @@ -6028,61 +6690,9 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tmp2 = neon_load_reg(rd, pass); gen_neon_add(size, tmp, tmp2); break; - case NEON_3R_VADD_VSUB: - if (!u) { /* VADD */ - gen_neon_add(size, tmp, tmp2); - } else { /* VSUB */ - switch (size) { - case 0: gen_helper_neon_sub_u8(tmp, tmp, tmp2); break; - case 1: gen_helper_neon_sub_u16(tmp, tmp, tmp2); break; - case 2: tcg_gen_sub_i32(tmp, tmp, tmp2); break; - default: abort(); - } - } - break; - case NEON_3R_VTST_VCEQ: - if (!u) { /* VTST */ - switch (size) { - case 0: gen_helper_neon_tst_u8(tmp, tmp, tmp2); break; - case 1: gen_helper_neon_tst_u16(tmp, tmp, tmp2); break; - case 2: gen_helper_neon_tst_u32(tmp, tmp, tmp2); break; - default: abort(); - } - } else { /* VCEQ */ - switch (size) { - case 0: gen_helper_neon_ceq_u8(tmp, tmp, tmp2); break; - case 1: gen_helper_neon_ceq_u16(tmp, tmp, tmp2); break; - case 2: gen_helper_neon_ceq_u32(tmp, tmp, tmp2); break; - default: abort(); - } - } - break; - case NEON_3R_VML: /* VMLA, VMLAL, VMLS,VMLSL */ - switch (size) { - case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break; - case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break; - case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break; - default: abort(); - } - tcg_temp_free_i32(tmp2); - tmp2 = neon_load_reg(rd, pass); - if (u) { /* VMLS */ - gen_neon_rsb(size, tmp, tmp2); - } else { /* VMLA */ - gen_neon_add(size, tmp, tmp2); - } - break; case NEON_3R_VMUL: - if (u) { /* polynomial */ - gen_helper_neon_mul_p8(tmp, tmp, tmp2); - } else { /* Integer */ - switch (size) { - case 0: gen_helper_neon_mul_u8(tmp, tmp, tmp2); break; - case 1: gen_helper_neon_mul_u16(tmp, tmp, tmp2); break; - case 2: tcg_gen_mul_i32(tmp, tmp, tmp2); break; - default: abort(); - } - } + /* VMUL.P8; other cases already eliminated. */ + gen_helper_neon_mul_p8(tmp, tmp, tmp2); break; case NEON_3R_VPMAX: GEN_NEON_INTEGER_OP(pmax); @@ -6264,8 +6874,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) size--; } shift = (insn >> 16) & ((1 << (3 + size)) - 1); - /* To avoid excessive duplication of ops we implement shift - by immediate using the variable shift operations. */ if (op < 8) { /* Shift by immediate: VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VQSHL, VQSHLU. */ @@ -6277,43 +6885,99 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } /* Right shifts are encoded as N - shift, where N is the element size in bits. */ - if (op <= 4) + if (op <= 4) { shift = shift - (1 << (size + 3)); + } + + switch (op) { + case 0: /* VSHR */ + /* Right shift comes here negative. */ + shift = -shift; + /* Shifts larger than the element size are architecturally + * valid. Unsigned results in all zeros; signed results + * in all sign bits. + */ + if (!u) { + tcg_gen_gvec_sari(size, rd_ofs, rm_ofs, + MIN(shift, (8 << size) - 1), + vec_size, vec_size); + } else if (shift >= 8 << size) { + tcg_gen_gvec_dup8i(rd_ofs, vec_size, vec_size, 0); + } else { + tcg_gen_gvec_shri(size, rd_ofs, rm_ofs, shift, + vec_size, vec_size); + } + return 0; + + case 1: /* VSRA */ + /* Right shift comes here negative. */ + shift = -shift; + /* Shifts larger than the element size are architecturally + * valid. Unsigned results in all zeros; signed results + * in all sign bits. + */ + if (!u) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, vec_size, vec_size, + MIN(shift, (8 << size) - 1), + &ssra_op[size]); + } else if (shift >= 8 << size) { + /* rd += 0 */ + } else { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, vec_size, vec_size, + shift, &usra_op[size]); + } + return 0; + + case 4: /* VSRI */ + if (!u) { + return 1; + } + /* Right shift comes here negative. */ + shift = -shift; + /* Shift out of range leaves destination unchanged. */ + if (shift < 8 << size) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, vec_size, vec_size, + shift, &sri_op[size]); + } + return 0; + + case 5: /* VSHL, VSLI */ + if (u) { /* VSLI */ + /* Shift out of range leaves destination unchanged. */ + if (shift < 8 << size) { + tcg_gen_gvec_2i(rd_ofs, rm_ofs, vec_size, + vec_size, shift, &sli_op[size]); + } + } else { /* VSHL */ + /* Shifts larger than the element size are + * architecturally valid and results in zero. + */ + if (shift >= 8 << size) { + tcg_gen_gvec_dup8i(rd_ofs, vec_size, vec_size, 0); + } else { + tcg_gen_gvec_shli(size, rd_ofs, rm_ofs, shift, + vec_size, vec_size); + } + } + return 0; + } + if (size == 3) { count = q + 1; } else { count = q ? 4: 2; } - switch (size) { - case 0: - imm = (uint8_t) shift; - imm |= imm << 8; - imm |= imm << 16; - break; - case 1: - imm = (uint16_t) shift; - imm |= imm << 16; - break; - case 2: - case 3: - imm = shift; - break; - default: - abort(); - } + + /* To avoid excessive duplication of ops we implement shift + * by immediate using the variable shift operations. + */ + imm = dup_const(size, shift); for (pass = 0; pass < count; pass++) { if (size == 3) { neon_load_reg64(cpu_V0, rm + pass); tcg_gen_movi_i64(cpu_V1, imm); switch (op) { - case 0: /* VSHR */ - case 1: /* VSRA */ - if (u) - gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); - else - gen_helper_neon_shl_s64(cpu_V0, cpu_V0, cpu_V1); - break; case 2: /* VRSHR */ case 3: /* VRSRA */ if (u) @@ -6321,10 +6985,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) else gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, cpu_V1); break; - case 4: /* VSRI */ - case 5: /* VSHL, VSLI */ - gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1); - break; case 6: /* VQSHLU */ gen_helper_neon_qshlu_s64(cpu_V0, cpu_env, cpu_V0, cpu_V1); @@ -6338,26 +6998,13 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) cpu_V0, cpu_V1); } break; + default: + g_assert_not_reached(); } - if (op == 1 || op == 3) { + if (op == 3) { /* Accumulate. */ neon_load_reg64(cpu_V1, rd + pass); tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1); - } else if (op == 4 || (op == 5 && u)) { - /* Insert */ - neon_load_reg64(cpu_V1, rd + pass); - uint64_t mask; - if (shift < -63 || shift > 63) { - mask = 0; - } else { - if (op == 4) { - mask = 0xffffffffffffffffull >> -shift; - } else { - mask = 0xffffffffffffffffull << shift; - } - } - tcg_gen_andi_i64(cpu_V1, cpu_V1, ~mask); - tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1); } neon_store_reg64(cpu_V0, rd + pass); } else { /* size < 3 */ @@ -6366,23 +7013,10 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, imm); switch (op) { - case 0: /* VSHR */ - case 1: /* VSRA */ - GEN_NEON_INTEGER_OP(shl); - break; case 2: /* VRSHR */ case 3: /* VRSRA */ GEN_NEON_INTEGER_OP(rshl); break; - case 4: /* VSRI */ - case 5: /* VSHL, VSLI */ - switch (size) { - case 0: gen_helper_neon_shl_u8(tmp, tmp, tmp2); break; - case 1: gen_helper_neon_shl_u16(tmp, tmp, tmp2); break; - case 2: gen_helper_neon_shl_u32(tmp, tmp, tmp2); break; - default: abort(); - } - break; case 6: /* VQSHLU */ switch (size) { case 0: @@ -6404,50 +7038,16 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case 7: /* VQSHL */ GEN_NEON_INTEGER_OP_ENV(qshl); break; + default: + g_assert_not_reached(); } tcg_temp_free_i32(tmp2); - if (op == 1 || op == 3) { + if (op == 3) { /* Accumulate. */ tmp2 = neon_load_reg(rd, pass); gen_neon_add(size, tmp, tmp2); tcg_temp_free_i32(tmp2); - } else if (op == 4 || (op == 5 && u)) { - /* Insert */ - switch (size) { - case 0: - if (op == 4) - mask = 0xff >> -shift; - else - mask = (uint8_t)(0xff << shift); - mask |= mask << 8; - mask |= mask << 16; - break; - case 1: - if (op == 4) - mask = 0xffff >> -shift; - else - mask = (uint16_t)(0xffff << shift); - mask |= mask << 16; - break; - case 2: - if (shift < -31 || shift > 31) { - mask = 0; - } else { - if (op == 4) - mask = 0xffffffffu >> -shift; - else - mask = 0xffffffffu << shift; - } - break; - default: - abort(); - } - tmp2 = neon_load_reg(rd, pass); - tcg_gen_andi_i32(tmp, tmp, mask); - tcg_gen_andi_i32(tmp2, tmp2, ~mask); - tcg_gen_or_i32(tmp, tmp, tmp2); - tcg_temp_free_i32(tmp2); } neon_store_reg(rd, pass, tmp); } @@ -6596,7 +7196,8 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) return 1; } } else { /* (insn & 0x00380080) == 0 */ - int invert; + int invert, reg_ofs, vec_size; + if (q && (rd & 1)) { return 1; } @@ -6636,8 +7237,9 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) break; case 14: imm |= (imm << 8) | (imm << 16) | (imm << 24); - if (invert) + if (invert) { imm = ~imm; + } break; case 15: if (invert) { @@ -6647,36 +7249,45 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) | ((imm & 0x40) ? (0x1f << 25) : (1 << 30)); break; } - if (invert) + if (invert) { imm = ~imm; + } - for (pass = 0; pass < (q ? 4 : 2); pass++) { - if (op & 1 && op < 12) { - tmp = neon_load_reg(rd, pass); - if (invert) { - /* The immediate value has already been inverted, so - BIC becomes AND. */ - tcg_gen_andi_i32(tmp, tmp, imm); - } else { - tcg_gen_ori_i32(tmp, tmp, imm); - } + reg_ofs = neon_reg_offset(rd, 0); + vec_size = q ? 16 : 8; + + if (op & 1 && op < 12) { + if (invert) { + /* The immediate value has already been inverted, + * so BIC becomes AND. + */ + tcg_gen_gvec_andi(MO_32, reg_ofs, reg_ofs, imm, + vec_size, vec_size); } else { - /* VMOV, VMVN. */ - tmp = tcg_temp_new_i32(); - if (op == 14 && invert) { - int n; - uint32_t val; - val = 0; - for (n = 0; n < 4; n++) { - if (imm & (1 << (n + (pass & 1) * 4))) - val |= 0xff << (n * 8); - } - tcg_gen_movi_i32(tmp, val); - } else { - tcg_gen_movi_i32(tmp, imm); - } + tcg_gen_gvec_ori(MO_32, reg_ofs, reg_ofs, imm, + vec_size, vec_size); + } + } else { + /* VMOV, VMVN. */ + if (op == 14 && invert) { + TCGv_i64 t64 = tcg_temp_new_i64(); + + for (pass = 0; pass <= q; ++pass) { + uint64_t val = 0; + int n; + + for (n = 0; n < 8; n++) { + if (imm & (1 << (n + pass * 8))) { + val |= 0xffull << (n * 8); + } + } + tcg_gen_movi_i64(t64, val); + neon_store_reg64(t64, rd + pass); + } + tcg_temp_free_i64(t64); + } else { + tcg_gen_gvec_dup32i(reg_ofs, vec_size, vec_size, imm); } - neon_store_reg(rd, pass, tmp); } } } else { /* (insn & 0x00800010 == 0x00800000) */ @@ -6735,7 +7346,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) if (op == 14 && size == 2) { TCGv_i64 tcg_rn, tcg_rm, tcg_rd; - if (!arm_dc_feature(s, ARM_FEATURE_V8_PMULL)) { + if (!dc_isar_feature(aa32_pmull, s)) { return 1; } tcg_rn = tcg_temp_new_i64(); @@ -7052,7 +7663,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) { NeonGenThreeOpEnvFn *fn; - if (!arm_dc_feature(s, ARM_FEATURE_V8_RDM)) { + if (!dc_isar_feature(aa32_rdm, s)) { return 1; } if (u && ((rd | rn) & 1)) { @@ -7265,7 +7876,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) TCGv_ptr fpst; TCGv_i32 ahp; - if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || + if (!dc_isar_feature(aa32_fp16_spconv, s) || q || (rm & 1)) { return 1; } @@ -7297,7 +7908,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) { TCGv_ptr fpst; TCGv_i32 ahp; - if (!arm_dc_feature(s, ARM_FEATURE_VFP_FP16) || + if (!dc_isar_feature(aa32_fp16_spconv, s) || q || (rd & 1)) { return 1; } @@ -7326,8 +7937,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) break; } case NEON_2RM_AESE: case NEON_2RM_AESMC: - if (!arm_dc_feature(s, ARM_FEATURE_V8_AES) - || ((rm | rd) & 1)) { + if (!dc_isar_feature(aa32_aes, s) || ((rm | rd) & 1)) { return 1; } ptr1 = vfp_reg_ptr(true, rd); @@ -7348,8 +7958,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp3); break; case NEON_2RM_SHA1H: - if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA1) - || ((rm | rd) & 1)) { + if (!dc_isar_feature(aa32_sha1, s) || ((rm | rd) & 1)) { return 1; } ptr1 = vfp_reg_ptr(true, rd); @@ -7366,10 +7975,10 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) } /* bit 6 (q): set -> SHA256SU0, cleared -> SHA1SU1 */ if (q) { - if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA256)) { + if (!dc_isar_feature(aa32_sha2, s)) { return 1; } - } else if (!arm_dc_feature(s, ARM_FEATURE_V8_SHA1)) { + } else if (!dc_isar_feature(aa32_sha1, s)) { return 1; } ptr1 = vfp_reg_ptr(true, rd); @@ -7382,6 +7991,14 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tcg_temp_free_ptr(ptr1); tcg_temp_free_ptr(ptr2); break; + + case NEON_2RM_VMVN: + tcg_gen_gvec_not(0, rd_ofs, rm_ofs, vec_size, vec_size); + break; + case NEON_2RM_VNEG: + tcg_gen_gvec_neg(size, rd_ofs, rm_ofs, vec_size, vec_size); + break; + default: elementwise: for (pass = 0; pass < (q ? 4 : 2); pass++) { @@ -7422,9 +8039,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) case NEON_2RM_VCNT: gen_helper_neon_cnt_u8(tmp, tmp); break; - case NEON_2RM_VMVN: - tcg_gen_not_i32(tmp, tmp); - break; case NEON_2RM_VQABS: switch (size) { case 0: @@ -7497,11 +8111,6 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) default: abort(); } break; - case NEON_2RM_VNEG: - tmp2 = tcg_const_i32(0); - gen_neon_rsb(size, tmp, tmp2); - tcg_temp_free_i32(tmp2); - break; case NEON_2RM_VCGT0_F: { TCGv_ptr fpstatus = get_fpstatus_ptr(1); @@ -7724,28 +8333,25 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn) tcg_temp_free_i32(tmp); } else if ((insn & 0x380) == 0) { /* VDUP */ + int element; + TCGMemOp size; + if ((insn & (7 << 16)) == 0 || (q && (rd & 1))) { return 1; } - if (insn & (1 << 19)) { - tmp = neon_load_reg(rm, 1); - } else { - tmp = neon_load_reg(rm, 0); - } if (insn & (1 << 16)) { - gen_neon_dup_u8(tmp, ((insn >> 17) & 3) * 8); + size = MO_8; + element = (insn >> 17) & 7; } else if (insn & (1 << 17)) { - if ((insn >> 18) & 1) - gen_neon_dup_high16(tmp); - else - gen_neon_dup_low16(tmp); + size = MO_16; + element = (insn >> 18) & 3; + } else { + size = MO_32; + element = (insn >> 19) & 1; } - for (pass = 0; pass < (q ? 4 : 2); pass++) { - tmp2 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp2, tmp); - neon_store_reg(rd, pass, tmp2); - } - tcg_temp_free_i32(tmp); + tcg_gen_gvec_dup_mem(size, neon_reg_offset(rd, 0), + neon_element_offset(rm, element, size), + q ? 16 : 8, q ? 16 : 8); } else { return 1; } @@ -7766,22 +8372,16 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL; int rd, rn, rm, opr_sz; int data = 0; - bool q; - - q = extract32(insn, 6, 1); - VFP_DREG_D(rd, insn); - VFP_DREG_N(rn, insn); - VFP_DREG_M(rm, insn); - if ((rd | rn | rm) & q) { - return 1; - } + int off_rn, off_rm; + bool is_long = false, q = extract32(insn, 6, 1); + bool ptr_is_env = false; if ((insn & 0xfe200f10) == 0xfc200800) { /* VCMLA -- 1111 110R R.1S .... .... 1000 ...0 .... */ int size = extract32(insn, 20, 1); data = extract32(insn, 23, 2); /* rot */ - if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) - || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + if (!dc_isar_feature(aa32_vcma, s) + || (!size && !dc_isar_feature(aa32_fp16_arith, s))) { return 1; } fn_gvec_ptr = size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah; @@ -7789,25 +8389,54 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) /* VCADD -- 1111 110R 1.0S .... .... 1000 ...0 .... */ int size = extract32(insn, 20, 1); data = extract32(insn, 24, 1); /* rot */ - if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA) - || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) { + if (!dc_isar_feature(aa32_vcma, s) + || (!size && !dc_isar_feature(aa32_fp16_arith, s))) { return 1; } fn_gvec_ptr = size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh; } else if ((insn & 0xfeb00f00) == 0xfc200d00) { /* V[US]DOT -- 1111 1100 0.10 .... .... 1101 .Q.U .... */ bool u = extract32(insn, 4, 1); - if (!arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + if (!dc_isar_feature(aa32_dp, s)) { return 1; } fn_gvec = u ? gen_helper_gvec_udot_b : gen_helper_gvec_sdot_b; + } else if ((insn & 0xff300f10) == 0xfc200810) { + /* VFM[AS]L -- 1111 1100 S.10 .... .... 1000 .Q.1 .... */ + int is_s = extract32(insn, 23, 1); + if (!dc_isar_feature(aa32_fhm, s)) { + return 1; + } + is_long = true; + data = is_s; /* is_2 == 0 */ + fn_gvec_ptr = gen_helper_gvec_fmlal_a32; + ptr_is_env = true; } else { return 1; } + VFP_DREG_D(rd, insn); + if (rd & q) { + return 1; + } + if (q || !is_long) { + VFP_DREG_N(rn, insn); + VFP_DREG_M(rm, insn); + if ((rn | rm) & q & !is_long) { + return 1; + } + off_rn = vfp_reg_offset(1, rn); + off_rm = vfp_reg_offset(1, rm); + } else { + rn = VFP_SREG_N(insn); + rm = VFP_SREG_M(insn); + off_rn = vfp_reg_offset(0, rn); + off_rm = vfp_reg_offset(0, rm); + } + if (s->fp_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); + syn_simd_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } if (!s->vfp_enabled) { @@ -7816,16 +8445,19 @@ static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn) opr_sz = (1 + q) * 8; if (fn_gvec_ptr) { - TCGv_ptr fpst = get_fpstatus_ptr(1); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), fpst, + TCGv_ptr ptr; + if (ptr_is_env) { + ptr = cpu_env; + } else { + ptr = get_fpstatus_ptr(1); + } + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), off_rn, off_rm, ptr, opr_sz, opr_sz, data, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); + if (!ptr_is_env) { + tcg_temp_free_ptr(ptr); + } } else { - tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), + tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), off_rn, off_rm, opr_sz, opr_sz, data, fn_gvec); } return 0; @@ -7844,14 +8476,9 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) gen_helper_gvec_3 *fn_gvec = NULL; gen_helper_gvec_3_ptr *fn_gvec_ptr = NULL; int rd, rn, rm, opr_sz, data; - bool q; - - q = extract32(insn, 6, 1); - VFP_DREG_D(rd, insn); - VFP_DREG_N(rn, insn); - if ((rd | rn) & q) { - return 1; - } + int off_rn, off_rm; + bool is_long = false, q = extract32(insn, 6, 1); + bool ptr_is_env = false; if ((insn & 0xff000f10) == 0xfe000800) { /* VCMLA (indexed) -- 1111 1110 S.RR .... .... 1000 ...0 .... */ @@ -7859,11 +8486,11 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) int size = extract32(insn, 23, 1); int index; - if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)) { + if (!dc_isar_feature(aa32_vcma, s)) { return 1; } if (size == 0) { - if (!arm_dc_feature(s, ARM_FEATURE_V8_FP16)) { + if (!dc_isar_feature(aa32_fp16_arith, s)) { return 1; } /* For fp16, rm is just Vm, and index is M. */ @@ -7880,20 +8507,59 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) } else if ((insn & 0xffb00f00) == 0xfe200d00) { /* V[US]DOT -- 1111 1110 0.10 .... .... 1101 .Q.U .... */ int u = extract32(insn, 4, 1); - if (!arm_dc_feature(s, ARM_FEATURE_V8_DOTPROD)) { + + if (!dc_isar_feature(aa32_dp, s)) { return 1; } fn_gvec = u ? gen_helper_gvec_udot_idx_b : gen_helper_gvec_sdot_idx_b; /* rm is just Vm, and index is M. */ data = extract32(insn, 5, 1); /* index */ rm = extract32(insn, 0, 4); + } else if ((insn & 0xffa00f10) == 0xfe000810) { + /* VFM[AS]L -- 1111 1110 0.0S .... .... 1000 .Q.1 .... */ + int is_s = extract32(insn, 20, 1); + int vm20 = extract32(insn, 0, 3); + int vm3 = extract32(insn, 3, 1); + int m = extract32(insn, 5, 1); + int index; + + if (!dc_isar_feature(aa32_fhm, s)) { + return 1; + } + if (q) { + rm = vm20; + index = m * 2 + vm3; + } else { + rm = vm20 * 2 + m; + index = vm3; + } + is_long = true; + data = (index << 2) | is_s; /* is_2 == 0 */ + fn_gvec_ptr = gen_helper_gvec_fmlal_idx_a32; + ptr_is_env = true; } else { return 1; } + VFP_DREG_D(rd, insn); + if (rd & q) { + return 1; + } + if (q || !is_long) { + VFP_DREG_N(rn, insn); + if (rn & q & !is_long) { + return 1; + } + off_rn = vfp_reg_offset(1, rn); + off_rm = vfp_reg_offset(1, rm); + } else { + rn = VFP_SREG_N(insn); + off_rn = vfp_reg_offset(0, rn); + off_rm = vfp_reg_offset(0, rm); + } if (s->fp_excp_el) { gen_exception_insn(s, 4, EXCP_UDEF, - syn_fp_access_trap(1, 0xe, false), s->fp_excp_el); + syn_simd_access_trap(1, 0xe, false), s->fp_excp_el); return 0; } if (!s->vfp_enabled) { @@ -7902,16 +8568,19 @@ static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn) opr_sz = (1 + q) * 8; if (fn_gvec_ptr) { - TCGv_ptr fpst = get_fpstatus_ptr(1); - tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), fpst, + TCGv_ptr ptr; + if (ptr_is_env) { + ptr = cpu_env; + } else { + ptr = get_fpstatus_ptr(1); + } + tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd), off_rn, off_rm, ptr, opr_sz, opr_sz, data, fn_gvec_ptr); - tcg_temp_free_ptr(fpst); + if (!ptr_is_env) { + tcg_temp_free_ptr(ptr); + } } else { - tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), - vfp_reg_offset(1, rn), - vfp_reg_offset(1, rm), + tcg_gen_gvec_3_ool(vfp_reg_offset(1, rd), off_rn, off_rm, opr_sz, opr_sz, data, fn_gvec); } return 0; @@ -8480,6 +9149,22 @@ static void gen_srs(DisasContext *s, s->base.is_jmp = DISAS_UPDATE; } +/* Generate a label used for skipping this instruction */ +static void arm_gen_condlabel(DisasContext *s) +{ + if (!s->condjmp) { + s->condlabel = gen_new_label(); + s->condjmp = 1; + } +} + +/* Skip this instruction if the ARM condition is false */ +static void arm_skip_unless(DisasContext *s, uint32_t cond) +{ + arm_gen_condlabel(s); + arm_gen_test_cc(cond ^ 1, s->condlabel); +} + static void disas_arm_insn(DisasContext *s, unsigned int insn) { unsigned int cond, val, op1, i, shift, rm, rs, rn, rd, sh; @@ -8586,6 +9271,17 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) */ gen_goto_tb(s, 0, s->pc & ~1); return; + case 7: /* sb */ + if ((insn & 0xf) || !dc_isar_feature(aa32_sb, s)) { + goto illegal_op; + } + /* + * TODO: There is no speculation barrier opcode + * for TCG; MB and end the TB instead. + */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + gen_goto_tb(s, 0, s->pc & ~1); + return; default: goto illegal_op; } @@ -8709,9 +9405,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) if (cond != 0xe) { /* if not always execute, we generate a conditional jump to next instruction */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, cond); } if ((insn & 0x0f900000) == 0x03000000) { if ((insn & (1 << 21)) == 0) { @@ -8842,8 +9536,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) * op1 == 3 is UNPREDICTABLE but handle as UNDEFINED. * Bits 8, 10 and 11 should be zero. */ - if (!arm_dc_feature(s, ARM_FEATURE_CRC) || op1 == 0x3 || - (c & 0xd) != 0) { + if (!dc_isar_feature(aa32_crc32, s) || op1 == 0x3 || (c & 0xd) != 0) { goto illegal_op; } @@ -8883,6 +9576,25 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; + case 0x6: /* ERET */ + if (op1 != 3) { + goto illegal_op; + } + if (!arm_dc_feature(s, ARM_FEATURE_V7VE)) { + goto illegal_op; + } + if ((insn & 0x000fff0f) != 0x0000000e) { + /* UNPREDICTABLE; we choose to UNDEF */ + goto illegal_op; + } + + if (s->current_el == 2) { + tmp = load_cpu_field(elr_el[2]); + } else { + tmp = load_reg(s, 14); + } + gen_exception_return(s, tmp); + break; case 7: { int imm16 = extract32(insn, 0, 4) | (extract32(insn, 8, 12) << 4); @@ -9231,6 +9943,8 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) rd = (insn >> 12) & 0xf; if (insn & (1 << 23)) { /* load/store exclusive */ + bool is_ld = extract32(insn, 20, 1); + bool is_lasr = !extract32(insn, 8, 1); int op2 = (insn >> 8) & 3; op1 = (insn >> 21) & 0x3; @@ -9258,11 +9972,12 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); - /* Since the emulation does not have barriers, - the acquire/release semantics need no special - handling */ + if (is_lasr && !is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + if (op2 == 0) { - if (insn & (1 << 20)) { + if (is_ld) { tmp = tcg_temp_new_i32(); switch (op1) { case 0: /* lda */ @@ -9308,7 +10023,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } tcg_temp_free_i32(tmp); } - } else if (insn & (1 << 20)) { + } else if (is_ld) { switch (op1) { case 0: /* ldrex */ gen_load_exclusive(s, rd, 15, addr, 2); @@ -9345,6 +10060,10 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } } tcg_temp_free_i32(addr); + + if (is_lasr && is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } } else if ((insn & 0x00300f00) == 0) { /* 0bcccc_0001_0x00_xxxx_xxxx_0000_1001_xxxx * - SWP, SWPB @@ -9692,7 +10411,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) case 1: case 3: /* SDIV, UDIV */ - if (!arm_dc_feature(s, ARM_FEATURE_ARM_DIV)) { + if (!dc_isar_feature(arm_div, s)) { goto illegal_op; } if (((insn >> 5) & 7) || (rd != 15)) { @@ -9893,7 +10612,7 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn) } else if (i == rn) { loaded_var = tmp; loaded_base = 1; - } else if (rn == 15 && exc_return) { + } else if (i == 15 && exc_return) { store_pc_exc_ret(s, tmp); } else { store_reg_from_load(s, i, tmp); @@ -10224,6 +10943,8 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) * 0b1111_1001_x11x_xxxx_xxxx_xxxx_xxxx_xxxx * - load/store dual (pre-indexed) */ + bool wback = extract32(insn, 21, 1); + if (rn == 15) { if (insn & (1 << 21)) { /* UNPREDICTABLE */ @@ -10235,8 +10956,29 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) addr = load_reg(s, rn); } offset = (insn & 0xff) * 4; - if ((insn & (1 << 23)) == 0) + if ((insn & (1 << 23)) == 0) { offset = -offset; + } + + if (s->v8m_stackcheck && rn == 13 && wback) { + /* + * Here 'addr' is the current SP; if offset is +ve we're + * moving SP up, else down. It is UNKNOWN whether the limit + * check triggers when SP starts below the limit and ends + * up above it; check whichever of the current and final + * SP is lower, so QEMU will trigger in that situation. + */ + if ((int32_t)offset < 0) { + TCGv_i32 newsp = tcg_temp_new_i32(); + + tcg_gen_addi_i32(newsp, addr, offset); + gen_helper_v8m_stackcheck(cpu_env, newsp); + tcg_temp_free_i32(newsp); + } else { + gen_helper_v8m_stackcheck(cpu_env, addr); + } + } + if (insn & (1 << 24)) { tcg_gen_addi_i32(addr, addr, offset); offset = 0; @@ -10260,7 +11002,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } - if (insn & (1 << 21)) { + if (wback) { /* Base writeback. */ tcg_gen_addi_i32(addr, addr, offset - 4); store_reg(s, rn, addr); @@ -10337,6 +11079,8 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) tcg_gen_addi_i32(tmp, tmp, s->pc); store_reg(s, 15, tmp); } else { + bool is_lasr = false; + bool is_ld = extract32(insn, 20, 1); int op2 = (insn >> 6) & 0x3; op = (insn >> 4) & 0x3; switch (op2) { @@ -10358,12 +11102,18 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) case 3: /* Load-acquire/store-release exclusive */ ARCH(8); + is_lasr = true; break; } + + if (is_lasr && !is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); if (!(op2 & 1)) { - if (insn & (1 << 20)) { + if (is_ld) { tmp = tcg_temp_new_i32(); switch (op) { case 0: /* ldab */ @@ -10402,12 +11152,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) } tcg_temp_free_i32(tmp); } - } else if (insn & (1 << 20)) { + } else if (is_ld) { gen_load_exclusive(s, rs, rd, addr, op); } else { gen_store_exclusive(s, rm, rs, rd, addr, op); } tcg_temp_free_i32(addr); + + if (is_lasr && is_ld) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } } } else { /* Load/store multiple, RFE, SRS. */ @@ -10447,6 +11201,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) } else { int i, loaded_base = 0; TCGv_i32 loaded_var; + bool wback = extract32(insn, 21, 1); /* Load/store multiple. */ addr = load_reg(s, rn); offset = 0; @@ -10454,10 +11209,26 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (insn & (1 << i)) offset += 4; } + if (insn & (1 << 24)) { tcg_gen_addi_i32(addr, addr, -offset); } + if (s->v8m_stackcheck && rn == 13 && wback) { + /* + * If the writeback is incrementing SP rather than + * decrementing it, and the initial SP is below the + * stack limit but the final written-back SP would + * be above, then then we must not perform any memory + * accesses, but it is IMPDEF whether we generate + * an exception. We choose to do so in this case. + * At this point 'addr' is the lowest address, so + * either the original SP (if incrementing) or our + * final SP (if decrementing), so that's what we check. + */ + gen_helper_v8m_stackcheck(cpu_env, addr); + } + loaded_var = NULL; for (i = 0; i < 16; i++) { if ((insn & (1 << i)) == 0) @@ -10485,7 +11256,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (loaded_base) { store_reg(s, rn, loaded_var); } - if (insn & (1 << 21)) { + if (wback) { /* Base register writeback. */ if (insn & (1 << 24)) { tcg_gen_addi_i32(addr, addr, -offset); @@ -10546,7 +11317,13 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (gen_thumb2_data_op(s, op, conds, 0, tmp, tmp2)) goto illegal_op; tcg_temp_free_i32(tmp2); - if (rd != 15) { + if (rd == 13 && + ((op == 2 && rn == 15) || + (op == 8 && rn == 13) || + (op == 13 && rn == 13))) { + /* MOV SP, ... or ADD SP, SP, ... or SUB SP, SP, ... */ + store_sp_checked(s, tmp); + } else if (rd != 15) { store_reg(s, rd, tmp); } else { tcg_temp_free_i32(tmp); @@ -10563,6 +11340,10 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) tmp2 = load_reg(s, rm); if ((insn & 0x70) != 0) goto illegal_op; + /* + * 0b1111_1010_0xxx_xxxx_1111_xxxx_0000_xxxx: + * - MOV, MOVS (register-shifted register), flagsetting + */ op = (insn >> 21) & 3; logic_cc = (insn & (1 << 20)) != 0; gen_arm_shift_reg(tmp, op, tmp2, logic_cc); @@ -10669,7 +11450,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) case 0x28: case 0x29: case 0x2a: - if (!arm_dc_feature(s, ARM_FEATURE_CRC)) { + if (!dc_isar_feature(aa32_crc32, s)) { goto illegal_op; } break; @@ -10850,7 +11631,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) tmp2 = load_reg(s, rm); if ((op & 0x50) == 0x10) { /* sdiv, udiv */ - if (!arm_dc_feature(s, ARM_FEATURE_THUMB_DIV)) { + if (!dc_isar_feature(thumb_div, s)) { goto illegal_op; } if (op & 0x20) @@ -11119,6 +11900,17 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) */ gen_goto_tb(s, 0, s->pc & ~1); break; + case 7: /* sb */ + if ((insn & 0xf) || !dc_isar_feature(aa32_sb, s)) { + goto illegal_op; + } + /* + * TODO: There is no speculation barrier opcode + * for TCG; MB and end the TB instead. + */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + gen_goto_tb(s, 0, s->pc & ~1); + break; default: goto illegal_op; } @@ -11140,8 +11932,16 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) if (rn != 14 || rd != 15) { goto illegal_op; } - tmp = load_reg(s, rn); - tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + if (s->current_el == 2) { + /* ERET from Hyp uses ELR_Hyp, not LR */ + if (insn & 0xff) { + goto illegal_op; + } + tmp = load_cpu_field(elr_el[2]); + } else { + tmp = load_reg(s, rn); + tcg_gen_subi_i32(tmp, tmp, insn & 0xff); + } gen_exception_return(s, tmp); break; case 6: /* MRS */ @@ -11205,9 +12005,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) /* Conditional branch. */ op = (insn >> 22) & 0xf; /* Generate a conditional jump to next instruction. */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(op ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, op); /* offset[11:1] = insn[10:0] */ offset = (insn & 0x7ff) << 1; @@ -11224,8 +12022,15 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) gen_jmp(s, s->pc + offset); } } else { - /* Data processing immediate. */ + /* + * 0b1111_0xxx_xxxx_0xxx_xxxx_xxxx + * - Data-processing (modified immediate, plain binary immediate) + */ if (insn & (1 << 25)) { + /* + * 0b1111_0x1x_xxxx_0xxx_xxxx_xxxx + * - Data-processing (plain binary immediate) + */ if (insn & (1 << 24)) { if (insn & (1 << 20)) goto illegal_op; @@ -11321,6 +12126,7 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, imm); } + store_reg(s, rd, tmp); } else { /* Add/sub 12-bit immediate. */ if (rn == 15) { @@ -11331,17 +12137,27 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) offset += imm; tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, offset); + store_reg(s, rd, tmp); } else { tmp = load_reg(s, rn); if (insn & (1 << 23)) tcg_gen_subi_i32(tmp, tmp, imm); else tcg_gen_addi_i32(tmp, tmp, imm); + if (rn == 13 && rd == 13) { + /* ADD SP, SP, imm or SUB SP, SP, imm */ + store_sp_checked(s, tmp); + } else { + store_reg(s, rd, tmp); + } } } - store_reg(s, rd, tmp); } } else { + /* + * 0b1111_0x0x_xxxx_0xxx_xxxx_xxxx + * - Data-processing (modified immediate) + */ int shifter_out = 0; /* modified 12-bit immediate. */ shift = ((insn & 0x04000000) >> 23) | ((insn & 0x7000) >> 12); @@ -11383,7 +12199,11 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) goto illegal_op; tcg_temp_free_i32(tmp2); rd = (insn >> 8) & 0xf; - if (rd != 15) { + if (rd == 13 && rn == 13 + && (op == 8 || op == 13)) { + /* ADD(S) SP, SP, imm or SUB(S) SP, SP, imm */ + store_sp_checked(s, tmp); + } else if (rd != 15) { store_reg(s, rd, tmp); } else { tcg_temp_free_i32(tmp); @@ -11492,7 +12312,6 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) imm = -imm; /* Fall through. */ case 0xf: /* Pre-increment. */ - tcg_gen_addi_i32(addr, addr, imm); writeback = 1; break; default: @@ -11504,6 +12323,28 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn) issinfo = writeback ? ISSInvalid : rs; + if (s->v8m_stackcheck && rn == 13 && writeback) { + /* + * Stackcheck. Here we know 'addr' is the current SP; + * if imm is +ve we're moving SP up, else down. It is + * UNKNOWN whether the limit check triggers when SP starts + * below the limit and ends up above it; we chose to do so. + */ + if ((int32_t)imm < 0) { + TCGv_i32 newsp = tcg_temp_new_i32(); + + tcg_gen_addi_i32(newsp, addr, imm); + gen_helper_v8m_stackcheck(cpu_env, newsp); + tcg_temp_free_i32(newsp); + } else { + gen_helper_v8m_stackcheck(cpu_env, addr); + } + } + + if (writeback && !postinc) { + tcg_gen_addi_i32(addr, addr, imm); + } + if (insn & (1 << 20)) { /* Load. */ tmp = tcg_temp_new_i32(); @@ -11586,7 +12427,11 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) rd = insn & 7; op = (insn >> 11) & 3; if (op == 3) { - /* add/subtract */ + /* + * 0b0001_1xxx_xxxx_xxxx + * - Add, subtract (three low registers) + * - Add, subtract (two low registers and immediate) + */ rn = (insn >> 3) & 7; tmp = load_reg(s, rn); if (insn & (1 << 10)) { @@ -11623,7 +12468,10 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) } break; case 2: case 3: - /* arithmetic large immediate */ + /* + * 0b001x_xxxx_xxxx_xxxx + * - Add, subtract, compare, move (one low register and immediate) + */ op = (insn >> 11) & 3; rd = (insn >> 8) & 0x7; if (op == 0) { /* mov */ @@ -11689,7 +12537,12 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) tmp2 = load_reg(s, rm); tcg_gen_add_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); - store_reg(s, rd, tmp); + if (rd == 13) { + /* ADD SP, SP, reg */ + store_sp_checked(s, tmp); + } else { + store_reg(s, rd, tmp); + } break; case 1: /* cmp */ tmp = load_reg(s, rd); @@ -11700,7 +12553,12 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; case 2: /* mov/cpy */ tmp = load_reg(s, rm); - store_reg(s, rd, tmp); + if (rd == 13) { + /* MOV SP, reg */ + store_sp_checked(s, tmp); + } else { + store_reg(s, rd, tmp); + } break; case 3: { @@ -11750,7 +12608,10 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; } - /* data processing register */ + /* + * 0b0100_00xx_xxxx_xxxx + * - Data-processing (two low registers) + */ rd = insn & 7; rm = (insn >> 3) & 7; op = (insn >> 6) & 0xf; @@ -12028,7 +12889,10 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; case 10: - /* add to high reg */ + /* + * 0b1010_xxxx_xxxx_xxxx + * - Add PC/SP (immediate) + */ rd = (insn >> 8) & 7; if (insn & (1 << 11)) { /* SP */ @@ -12048,13 +12912,17 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) op = (insn >> 8) & 0xf; switch (op) { case 0: - /* adjust stack pointer */ + /* + * 0b1011_0000_xxxx_xxxx + * - ADD (SP plus immediate) + * - SUB (SP minus immediate) + */ tmp = load_reg(s, 13); val = (insn & 0x7f) * 4; if (insn & (1 << 7)) val = -(int32_t)val; tcg_gen_addi_i32(tmp, tmp, val); - store_reg(s, 13, tmp); + store_sp_checked(s, tmp); break; case 2: /* sign/zero extend. */ @@ -12071,7 +12939,10 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) store_reg(s, rd, tmp); break; case 4: case 5: case 0xc: case 0xd: - /* push/pop */ + /* + * 0b1011_x10x_xxxx_xxxx + * - push/pop + */ addr = load_reg(s, 13); if (insn & (1 << 8)) offset = 4; @@ -12084,6 +12955,17 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) if ((insn & (1 << 11)) == 0) { tcg_gen_addi_i32(addr, addr, -offset); } + + if (s->v8m_stackcheck) { + /* + * Here 'addr' is the lower of "old SP" and "new SP"; + * if this is a pop that starts below the limit and ends + * above it, it is UNKNOWN whether the limit check triggers; + * we choose to trigger. + */ + gen_helper_v8m_stackcheck(cpu_env, addr); + } + for (i = 0; i < 8; i++) { if (insn & (1 << i)) { if (insn & (1 << 11)) { @@ -12131,8 +13013,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) case 1: case 3: case 9: case 11: /* czb */ rm = insn & 7; tmp = load_reg(s, rm); - s->condlabel = gen_new_label(); - s->condjmp = 1; + arm_gen_condlabel(s); if (insn & (1 << 11)) tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel); else @@ -12295,9 +13176,7 @@ static void disas_thumb_insn(DisasContext *s, uint32_t insn) break; } /* generate a conditional jump to next instruction */ - s->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, s->condlabel); - s->condjmp = 1; + arm_skip_unless(s, cond); /* jump to the offset */ val = (uint32_t)s->pc + 2; @@ -12382,7 +13261,10 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) DisasContext *dc = container_of(dcbase, DisasContext, base); CPUARMState *env = cs->env_ptr; ARMCPU *cpu = arm_env_get_cpu(env); + uint32_t tb_flags = dc->base.tb->flags; + uint32_t condexec, core_mmu_idx; + dc->isar = &cpu->isar; dc->pc = dc->base.pc_first; dc->condjmp = 0; @@ -12392,25 +13274,28 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) */ dc->secure_routed_to_el3 = arm_feature(env, ARM_FEATURE_EL3) && !arm_el_is_aa64(env, 3); - dc->thumb = ARM_TBFLAG_THUMB(dc->base.tb->flags); - dc->sctlr_b = ARM_TBFLAG_SCTLR_B(dc->base.tb->flags); - dc->be_data = ARM_TBFLAG_BE_DATA(dc->base.tb->flags) ? MO_BE : MO_LE; - dc->condexec_mask = (ARM_TBFLAG_CONDEXEC(dc->base.tb->flags) & 0xf) << 1; - dc->condexec_cond = ARM_TBFLAG_CONDEXEC(dc->base.tb->flags) >> 4; - dc->mmu_idx = core_to_arm_mmu_idx(env, ARM_TBFLAG_MMUIDX(dc->base.tb->flags)); + dc->thumb = FIELD_EX32(tb_flags, TBFLAG_A32, THUMB); + dc->sctlr_b = FIELD_EX32(tb_flags, TBFLAG_A32, SCTLR_B); + dc->be_data = FIELD_EX32(tb_flags, TBFLAG_ANY, BE_DATA) ? MO_BE : MO_LE; + condexec = FIELD_EX32(tb_flags, TBFLAG_A32, CONDEXEC); + dc->condexec_mask = (condexec & 0xf) << 1; + dc->condexec_cond = condexec >> 4; + core_mmu_idx = FIELD_EX32(tb_flags, TBFLAG_ANY, MMUIDX); + dc->mmu_idx = core_to_arm_mmu_idx(env, core_mmu_idx); dc->current_el = arm_mmu_idx_to_el(dc->mmu_idx); #if !defined(CONFIG_USER_ONLY) dc->user = (dc->current_el == 0); #endif - dc->ns = ARM_TBFLAG_NS(dc->base.tb->flags); - dc->fp_excp_el = ARM_TBFLAG_FPEXC_EL(dc->base.tb->flags); - dc->vfp_enabled = ARM_TBFLAG_VFPEN(dc->base.tb->flags); - dc->vec_len = ARM_TBFLAG_VECLEN(dc->base.tb->flags); - dc->vec_stride = ARM_TBFLAG_VECSTRIDE(dc->base.tb->flags); - dc->c15_cpar = ARM_TBFLAG_XSCALE_CPAR(dc->base.tb->flags); - dc->v7m_handler_mode = ARM_TBFLAG_HANDLER(dc->base.tb->flags); + dc->ns = FIELD_EX32(tb_flags, TBFLAG_A32, NS); + dc->fp_excp_el = FIELD_EX32(tb_flags, TBFLAG_ANY, FPEXC_EL); + dc->vfp_enabled = FIELD_EX32(tb_flags, TBFLAG_A32, VFPEN); + dc->vec_len = FIELD_EX32(tb_flags, TBFLAG_A32, VECLEN); + dc->vec_stride = FIELD_EX32(tb_flags, TBFLAG_A32, VECSTRIDE); + dc->c15_cpar = FIELD_EX32(tb_flags, TBFLAG_A32, XSCALE_CPAR); + dc->v7m_handler_mode = FIELD_EX32(tb_flags, TBFLAG_A32, HANDLER); dc->v8m_secure = arm_feature(env, ARM_FEATURE_M_SECURITY) && regime_is_secure(env, dc->mmu_idx); + dc->v8m_stackcheck = FIELD_EX32(tb_flags, TBFLAG_A32, STACKCHECK); dc->cp_regs = cpu->cp_regs; dc->features = env->features; @@ -12429,8 +13314,8 @@ static void arm_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) * emit code to generate a software step exception * end the TB */ - dc->ss_active = ARM_TBFLAG_SS_ACTIVE(dc->base.tb->flags); - dc->pstate_ss = ARM_TBFLAG_PSTATE_SS(dc->base.tb->flags); + dc->ss_active = FIELD_EX32(tb_flags, TBFLAG_ANY, SS_ACTIVE); + dc->pstate_ss = FIELD_EX32(tb_flags, TBFLAG_ANY, PSTATE_SS); dc->is_ldex = false; dc->ss_same_el = false; /* Can't be true since EL_d must be AArch64 */ @@ -12499,7 +13384,6 @@ static void arm_tr_tb_start(DisasContextBase *dcbase, CPUState *cpu) tcg_gen_movi_i32(tmp, 0); store_cpu_field(tmp, condexec_bits); } - tcg_clear_temp_count(); } static void arm_tr_insn_start(DisasContextBase *dcbase, CPUState *cpu) @@ -12676,9 +13560,7 @@ static void thumb_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) uint32_t cond = dc->condexec_cond; if (cond != 0x0e) { /* Skip conditional when condition is AL. */ - dc->condlabel = gen_new_label(); - arm_gen_test_cc(cond ^ 1, dc->condlabel); - dc->condjmp = 1; + arm_skip_unless(dc, cond); } } @@ -12878,11 +13760,11 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) DisasContext dc; const TranslatorOps *ops = &arm_translator_ops; - if (ARM_TBFLAG_THUMB(tb->flags)) { + if (FIELD_EX32(tb->flags, TBFLAG_A32, THUMB)) { ops = &thumb_translator_ops; } #ifdef TARGET_AARCH64 - if (ARM_TBFLAG_AARCH64_STATE(tb->flags)) { + if (FIELD_EX32(tb->flags, TBFLAG_ANY, AARCH64_STATE)) { ops = &aarch64_translator_ops; } #endif @@ -12890,11 +13772,6 @@ void gen_intermediate_code(CPUState *cpu, TranslationBlock *tb) translator_loop(ops, &dc.base, cpu, tb); } -static const char *cpu_mode_names[16] = { - "usr", "fiq", "irq", "svc", "???", "???", "mon", "abt", - "???", "???", "hyp", "und", "???", "???", "???", "sys" -}; - void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -12960,7 +13837,7 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, psr & CPSR_V ? 'V' : '-', psr & CPSR_T ? 'T' : 'A', ns_status, - cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26); + aarch32_mode_name(psr), (psr & 0x10) ? 32 : 26); } if (flags & CPU_DUMP_FPU) { @@ -12978,7 +13855,7 @@ void arm_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, i * 2 + 1, (uint32_t)(v >> 32), i, v); } - cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.xregs[ARM_VFP_FPSCR]); + cpu_fprintf(f, "FPSCR: %08x\n", vfp_get_fpscr(env)); } } diff --git a/target/arm/translate.h b/target/arm/translate.h index 45f04244be..912cc2a4a5 100644 --- a/target/arm/translate.h +++ b/target/arm/translate.h @@ -7,6 +7,7 @@ /* internal defines */ typedef struct DisasContext { DisasContextBase base; + const ARMISARegisters *isar; target_ulong pc; target_ulong page_start; @@ -25,8 +26,8 @@ typedef struct DisasContext { int user; #endif ARMMMUIdx mmu_idx; /* MMU index to use for normal loads/stores */ - bool tbi0; /* TBI0 for EL0/1 or TBI for EL2/3 */ - bool tbi1; /* TBI1 for EL0/1, not used for EL2/3 */ + uint8_t tbii; /* TBI1|TBI0 for insns */ + uint8_t tbid; /* TBI1|TBI0 for data */ bool ns; /* Use non-secure CPREG bank on access */ int fp_excp_el; /* FP exception EL or 0 if enabled */ int sve_excp_el; /* SVE exception EL or 0 if enabled */ @@ -38,6 +39,7 @@ typedef struct DisasContext { int vec_stride; bool v7m_handler_mode; bool v8m_secure; /* true if v8M and we're in Secure mode */ + bool v8m_stackcheck; /* true if we need to perform v8M stack limit checks */ /* Immediate value in AArch32 SVC insn; must be set if is_jmp == DISAS_SWI * so that top level loop can generate correct syndrome information. */ @@ -66,6 +68,17 @@ typedef struct DisasContext { bool is_ldex; /* True if a single-step exception will be taken to the current EL */ bool ss_same_el; + /* True if v8.3-PAuth is active. */ + bool pauth_active; + /* True with v8.5-BTI and SCTLR_ELx.BT* set. */ + bool bt; + /* + * >= 0, a copy of PSTATE.BTYPE, which will be 0 without v8.5-BTI. + * < 0, set by the current instruction. + */ + int8_t btype; + /* True if this page is guarded. */ + bool guarded_page; /* Bottom two bits of XScale c15_cpar coprocessor access control reg */ int c15_cpar; /* TCG op of the current insn_start. */ @@ -189,4 +202,62 @@ static inline TCGv_i32 get_ahp_flag(void) return ret; } +/* Set bits within PSTATE. */ +static inline void set_pstate_bits(uint32_t bits) +{ + TCGv_i32 p = tcg_temp_new_i32(); + + tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); + + tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_ori_i32(p, p, bits); + tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_temp_free_i32(p); +} + +/* Clear bits within PSTATE. */ +static inline void clear_pstate_bits(uint32_t bits) +{ + TCGv_i32 p = tcg_temp_new_i32(); + + tcg_debug_assert(!(bits & CACHED_PSTATE_BITS)); + + tcg_gen_ld_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_gen_andi_i32(p, p, ~bits); + tcg_gen_st_i32(p, cpu_env, offsetof(CPUARMState, pstate)); + tcg_temp_free_i32(p); +} + +/* If the singlestep state is Active-not-pending, advance to Active-pending. */ +static inline void gen_ss_advance(DisasContext *s) +{ + if (s->ss_active) { + s->pstate_ss = 0; + clear_pstate_bits(PSTATE_SS); + } +} + +/* Vector operations shared between ARM and AArch64. */ +extern const GVecGen3 bsl_op; +extern const GVecGen3 bit_op; +extern const GVecGen3 bif_op; +extern const GVecGen3 mla_op[4]; +extern const GVecGen3 mls_op[4]; +extern const GVecGen3 cmtst_op[4]; +extern const GVecGen2i ssra_op[4]; +extern const GVecGen2i usra_op[4]; +extern const GVecGen2i sri_op[4]; +extern const GVecGen2i sli_op[4]; +extern const GVecGen4 uqadd_op[4]; +extern const GVecGen4 sqadd_op[4]; +extern const GVecGen4 uqsub_op[4]; +extern const GVecGen4 sqsub_op[4]; +void gen_cmtst_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b); + +/* + * Forward to the isar_feature_* tests given a DisasContext pointer. + */ +#define dc_isar_feature(name, ctx) \ + ({ DisasContext *ctx_ = (ctx); isar_feature_##name(ctx_->isar); }) + #endif /* TARGET_ARM_TRANSLATE_H */ diff --git a/target/arm/vec_helper.c b/target/arm/vec_helper.c index 37f338732e..dedef62403 100644 --- a/target/arm/vec_helper.c +++ b/target/arm/vec_helper.c @@ -36,7 +36,7 @@ #define H4(x) (x) #endif -#define SET_QC() env->vfp.xregs[ARM_VFP_FPSCR] |= CPSR_Q +#define SET_QC() env->vfp.qc[0] = 1 static void clear_tail(void *vd, uintptr_t opr_sz, uintptr_t max_sz) { @@ -638,6 +638,7 @@ void HELPER(NAME)(void *vd, void *vn, void *stat, uint32_t desc) \ for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ d[i] = FUNC(n[i], stat); \ } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ } DO_2OP(gvec_frecpe_h, helper_recpe_f16, float16) @@ -688,6 +689,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *stat, uint32_t desc) \ for (i = 0; i < oprsz / sizeof(TYPE); i++) { \ d[i] = FUNC(n[i], m[i], stat); \ } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ } DO_3OP(gvec_fadd_h, float16_add, float16) @@ -766,3 +768,281 @@ DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4) DO_FMLA_IDX(gvec_fmla_idx_d, float64, ) #undef DO_FMLA_IDX + +#define DO_SAT(NAME, WTYPE, TYPEN, TYPEM, OP, MIN, MAX) \ +void HELPER(NAME)(void *vd, void *vq, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t i, oprsz = simd_oprsz(desc); \ + TYPEN *d = vd, *n = vn; TYPEM *m = vm; \ + bool q = false; \ + for (i = 0; i < oprsz / sizeof(TYPEN); i++) { \ + WTYPE dd = (WTYPE)n[i] OP m[i]; \ + if (dd < MIN) { \ + dd = MIN; \ + q = true; \ + } else if (dd > MAX) { \ + dd = MAX; \ + q = true; \ + } \ + d[i] = dd; \ + } \ + if (q) { \ + uint32_t *qc = vq; \ + qc[0] = 1; \ + } \ + clear_tail(d, oprsz, simd_maxsz(desc)); \ +} + +DO_SAT(gvec_uqadd_b, int, uint8_t, uint8_t, +, 0, UINT8_MAX) +DO_SAT(gvec_uqadd_h, int, uint16_t, uint16_t, +, 0, UINT16_MAX) +DO_SAT(gvec_uqadd_s, int64_t, uint32_t, uint32_t, +, 0, UINT32_MAX) + +DO_SAT(gvec_sqadd_b, int, int8_t, int8_t, +, INT8_MIN, INT8_MAX) +DO_SAT(gvec_sqadd_h, int, int16_t, int16_t, +, INT16_MIN, INT16_MAX) +DO_SAT(gvec_sqadd_s, int64_t, int32_t, int32_t, +, INT32_MIN, INT32_MAX) + +DO_SAT(gvec_uqsub_b, int, uint8_t, uint8_t, -, 0, UINT8_MAX) +DO_SAT(gvec_uqsub_h, int, uint16_t, uint16_t, -, 0, UINT16_MAX) +DO_SAT(gvec_uqsub_s, int64_t, uint32_t, uint32_t, -, 0, UINT32_MAX) + +DO_SAT(gvec_sqsub_b, int, int8_t, int8_t, -, INT8_MIN, INT8_MAX) +DO_SAT(gvec_sqsub_h, int, int16_t, int16_t, -, INT16_MIN, INT16_MAX) +DO_SAT(gvec_sqsub_s, int64_t, int32_t, int32_t, -, INT32_MIN, INT32_MAX) + +#undef DO_SAT + +void HELPER(gvec_uqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + uint64_t nn = n[i], mm = m[i], dd = nn + mm; + if (dd < nn) { + dd = UINT64_MAX; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_uqsub_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + uint64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + uint64_t nn = n[i], mm = m[i], dd = nn - mm; + if (nn < mm) { + dd = 0; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_sqadd_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + int64_t nn = n[i], mm = m[i], dd = nn + mm; + if (((dd ^ nn) & ~(nn ^ mm)) & INT64_MIN) { + dd = (nn >> 63) ^ ~INT64_MIN; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_sqsub_d)(void *vd, void *vq, void *vn, + void *vm, uint32_t desc) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int64_t *d = vd, *n = vn, *m = vm; + bool q = false; + + for (i = 0; i < oprsz / 8; i++) { + int64_t nn = n[i], mm = m[i], dd = nn - mm; + if (((dd ^ nn) & (nn ^ mm)) & INT64_MIN) { + dd = (nn >> 63) ^ ~INT64_MIN; + q = true; + } + d[i] = dd; + } + if (q) { + uint32_t *qc = vq; + qc[0] = 1; + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +/* + * Convert float16 to float32, raising no exceptions and + * preserving exceptional values, including SNaN. + * This is effectively an unpack+repack operation. + */ +static float32 float16_to_float32_by_bits(uint32_t f16, bool fz16) +{ + const int f16_bias = 15; + const int f32_bias = 127; + uint32_t sign = extract32(f16, 15, 1); + uint32_t exp = extract32(f16, 10, 5); + uint32_t frac = extract32(f16, 0, 10); + + if (exp == 0x1f) { + /* Inf or NaN */ + exp = 0xff; + } else if (exp == 0) { + /* Zero or denormal. */ + if (frac != 0) { + if (fz16) { + frac = 0; + } else { + /* + * Denormal; these are all normal float32. + * Shift the fraction so that the msb is at bit 11, + * then remove bit 11 as the implicit bit of the + * normalized float32. Note that we still go through + * the shift for normal numbers below, to put the + * float32 fraction at the right place. + */ + int shift = clz32(frac) - 21; + frac = (frac << shift) & 0x3ff; + exp = f32_bias - f16_bias - shift + 1; + } + } + } else { + /* Normal number; adjust the bias. */ + exp += f32_bias - f16_bias; + } + sign <<= 31; + exp <<= 23; + frac <<= 23 - 10; + + return sign | exp | frac; +} + +static uint64_t load4_f16(uint64_t *ptr, int is_q, int is_2) +{ + /* + * Branchless load of u32[0], u64[0], u32[1], or u64[1]. + * Load the 2nd qword iff is_q & is_2. + * Shift to the 2nd dword iff !is_q & is_2. + * For !is_q & !is_2, the upper bits of the result are garbage. + */ + return ptr[is_q & is_2] >> ((is_2 & ~is_q) << 5); +} + +/* + * Note that FMLAL requires oprsz == 8 or oprsz == 16, + * as there is not yet SVE versions that might use blocking. + */ + +static void do_fmlal(float32 *d, void *vn, void *vm, float_status *fpst, + uint32_t desc, bool fz16) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + int is_q = oprsz == 16; + uint64_t n_4, m_4; + + /* Pre-load all of the f16 data, avoiding overlap issues. */ + n_4 = load4_f16(vn, is_q, is_2); + m_4 = load4_f16(vm, is_q, is_2); + + /* Negate all inputs for FMLSL at once. */ + if (is_s) { + n_4 ^= 0x8000800080008000ull; + } + + for (i = 0; i < oprsz / 4; i++) { + float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); + float32 m_1 = float16_to_float32_by_bits(m_4 >> (i * 16), fz16); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_fmlal_a32)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal(vd, vn, vm, &env->vfp.standard_fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +void HELPER(gvec_fmlal_a64)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal(vd, vn, vm, &env->vfp.fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +static void do_fmlal_idx(float32 *d, void *vn, void *vm, float_status *fpst, + uint32_t desc, bool fz16) +{ + intptr_t i, oprsz = simd_oprsz(desc); + int is_s = extract32(desc, SIMD_DATA_SHIFT, 1); + int is_2 = extract32(desc, SIMD_DATA_SHIFT + 1, 1); + int index = extract32(desc, SIMD_DATA_SHIFT + 2, 3); + int is_q = oprsz == 16; + uint64_t n_4; + float32 m_1; + + /* Pre-load all of the f16 data, avoiding overlap issues. */ + n_4 = load4_f16(vn, is_q, is_2); + + /* Negate all inputs for FMLSL at once. */ + if (is_s) { + n_4 ^= 0x8000800080008000ull; + } + + m_1 = float16_to_float32_by_bits(((float16 *)vm)[H2(index)], fz16); + + for (i = 0; i < oprsz / 4; i++) { + float32 n_1 = float16_to_float32_by_bits(n_4 >> (i * 16), fz16); + d[H4(i)] = float32_muladd(n_1, m_1, d[H4(i)], 0, fpst); + } + clear_tail(d, oprsz, simd_maxsz(desc)); +} + +void HELPER(gvec_fmlal_idx_a32)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal_idx(vd, vn, vm, &env->vfp.standard_fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} + +void HELPER(gvec_fmlal_idx_a64)(void *vd, void *vn, void *vm, + void *venv, uint32_t desc) +{ + CPUARMState *env = venv; + do_fmlal_idx(vd, vn, vm, &env->vfp.fp_status, desc, + get_flush_inputs_to_zero(&env->vfp.fp_status_f16)); +} diff --git a/target/arm/vfp_helper.c b/target/arm/vfp_helper.c new file mode 100644 index 0000000000..2468fc1629 --- /dev/null +++ b/target/arm/vfp_helper.c @@ -0,0 +1,1272 @@ +/* + * ARM VFP floating-point operations + * + * 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.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 "qemu/log.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "fpu/softfloat.h" +#include "internals.h" + + +/* VFP support. We follow the convention used for VFP instructions: + Single precision routines have a "s" suffix, double precision a + "d" suffix. */ + +/* Convert host exception flags to vfp form. */ +static inline int vfp_exceptbits_from_host(int host_bits) +{ + int target_bits = 0; + + if (host_bits & float_flag_invalid) + target_bits |= 1; + if (host_bits & float_flag_divbyzero) + target_bits |= 2; + if (host_bits & float_flag_overflow) + target_bits |= 4; + if (host_bits & (float_flag_underflow | float_flag_output_denormal)) + target_bits |= 8; + if (host_bits & float_flag_inexact) + target_bits |= 0x10; + if (host_bits & float_flag_input_denormal) + target_bits |= 0x80; + return target_bits; +} + +uint32_t HELPER(vfp_get_fpscr)(CPUARMState *env) +{ + uint32_t i, fpscr; + + fpscr = env->vfp.xregs[ARM_VFP_FPSCR] + | (env->vfp.vec_len << 16) + | (env->vfp.vec_stride << 20); + + i = get_float_exception_flags(&env->vfp.fp_status); + i |= get_float_exception_flags(&env->vfp.standard_fp_status); + /* FZ16 does not generate an input denormal exception. */ + i |= (get_float_exception_flags(&env->vfp.fp_status_f16) + & ~float_flag_input_denormal); + fpscr |= vfp_exceptbits_from_host(i); + + i = env->vfp.qc[0] | env->vfp.qc[1] | env->vfp.qc[2] | env->vfp.qc[3]; + fpscr |= i ? FPCR_QC : 0; + + return fpscr; +} + +uint32_t vfp_get_fpscr(CPUARMState *env) +{ + return HELPER(vfp_get_fpscr)(env); +} + +/* Convert vfp exception flags to target form. */ +static inline int vfp_exceptbits_to_host(int target_bits) +{ + int host_bits = 0; + + if (target_bits & 1) + host_bits |= float_flag_invalid; + if (target_bits & 2) + host_bits |= float_flag_divbyzero; + if (target_bits & 4) + host_bits |= float_flag_overflow; + if (target_bits & 8) + host_bits |= float_flag_underflow; + if (target_bits & 0x10) + host_bits |= float_flag_inexact; + if (target_bits & 0x80) + host_bits |= float_flag_input_denormal; + return host_bits; +} + +void HELPER(vfp_set_fpscr)(CPUARMState *env, uint32_t val) +{ + int i; + uint32_t changed = env->vfp.xregs[ARM_VFP_FPSCR]; + + /* When ARMv8.2-FP16 is not supported, FZ16 is RES0. */ + if (!cpu_isar_feature(aa64_fp16, arm_env_get_cpu(env))) { + val &= ~FPCR_FZ16; + } + + /* + * We don't implement trapped exception handling, so the + * trap enable bits, IDE|IXE|UFE|OFE|DZE|IOE are all RAZ/WI (not RES0!) + * + * If we exclude the exception flags, IOC|DZC|OFC|UFC|IXC|IDC + * (which are stored in fp_status), and the other RES0 bits + * in between, then we clear all of the low 16 bits. + */ + env->vfp.xregs[ARM_VFP_FPSCR] = val & 0xf7c80000; + env->vfp.vec_len = (val >> 16) & 7; + env->vfp.vec_stride = (val >> 20) & 3; + + /* + * The bit we set within fpscr_q is arbitrary; the register as a + * whole being zero/non-zero is what counts. + */ + env->vfp.qc[0] = val & FPCR_QC; + env->vfp.qc[1] = 0; + env->vfp.qc[2] = 0; + env->vfp.qc[3] = 0; + + changed ^= val; + if (changed & (3 << 22)) { + i = (val >> 22) & 3; + switch (i) { + case FPROUNDING_TIEEVEN: + i = float_round_nearest_even; + break; + case FPROUNDING_POSINF: + i = float_round_up; + break; + case FPROUNDING_NEGINF: + i = float_round_down; + break; + case FPROUNDING_ZERO: + i = float_round_to_zero; + break; + } + set_float_rounding_mode(i, &env->vfp.fp_status); + set_float_rounding_mode(i, &env->vfp.fp_status_f16); + } + if (changed & FPCR_FZ16) { + bool ftz_enabled = val & FPCR_FZ16; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status_f16); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status_f16); + } + if (changed & FPCR_FZ) { + bool ftz_enabled = val & FPCR_FZ; + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status); + } + if (changed & FPCR_DN) { + bool dnan_enabled = val & FPCR_DN; + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status); + set_default_nan_mode(dnan_enabled, &env->vfp.fp_status_f16); + } + + /* The exception flags are ORed together when we read fpscr so we + * only need to preserve the current state in one of our + * float_status values. + */ + i = vfp_exceptbits_to_host(val); + set_float_exception_flags(i, &env->vfp.fp_status); + set_float_exception_flags(0, &env->vfp.fp_status_f16); + set_float_exception_flags(0, &env->vfp.standard_fp_status); +} + +void vfp_set_fpscr(CPUARMState *env, uint32_t val) +{ + HELPER(vfp_set_fpscr)(env, val); +} + +#define VFP_HELPER(name, p) HELPER(glue(glue(vfp_,name),p)) + +#define VFP_BINOP(name) \ +float32 VFP_HELPER(name, s)(float32 a, float32 b, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return float32_ ## name(a, b, fpst); \ +} \ +float64 VFP_HELPER(name, d)(float64 a, float64 b, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return float64_ ## name(a, b, fpst); \ +} +VFP_BINOP(add) +VFP_BINOP(sub) +VFP_BINOP(mul) +VFP_BINOP(div) +VFP_BINOP(min) +VFP_BINOP(max) +VFP_BINOP(minnum) +VFP_BINOP(maxnum) +#undef VFP_BINOP + +float32 VFP_HELPER(neg, s)(float32 a) +{ + return float32_chs(a); +} + +float64 VFP_HELPER(neg, d)(float64 a) +{ + return float64_chs(a); +} + +float32 VFP_HELPER(abs, s)(float32 a) +{ + return float32_abs(a); +} + +float64 VFP_HELPER(abs, d)(float64 a) +{ + return float64_abs(a); +} + +float32 VFP_HELPER(sqrt, s)(float32 a, CPUARMState *env) +{ + return float32_sqrt(a, &env->vfp.fp_status); +} + +float64 VFP_HELPER(sqrt, d)(float64 a, CPUARMState *env) +{ + return float64_sqrt(a, &env->vfp.fp_status); +} + +static void softfloat_to_vfp_compare(CPUARMState *env, int cmp) +{ + uint32_t flags; + switch (cmp) { + case float_relation_equal: + flags = 0x6; + break; + case float_relation_less: + flags = 0x8; + break; + case float_relation_greater: + flags = 0x2; + break; + case float_relation_unordered: + flags = 0x3; + break; + default: + g_assert_not_reached(); + } + env->vfp.xregs[ARM_VFP_FPSCR] = + deposit32(env->vfp.xregs[ARM_VFP_FPSCR], 28, 4, flags); +} + +/* XXX: check quiet/signaling case */ +#define DO_VFP_cmp(p, type) \ +void VFP_HELPER(cmp, p)(type a, type b, CPUARMState *env) \ +{ \ + softfloat_to_vfp_compare(env, \ + type ## _compare_quiet(a, b, &env->vfp.fp_status)); \ +} \ +void VFP_HELPER(cmpe, p)(type a, type b, CPUARMState *env) \ +{ \ + softfloat_to_vfp_compare(env, \ + type ## _compare(a, b, &env->vfp.fp_status)); \ +} +DO_VFP_cmp(s, float32) +DO_VFP_cmp(d, float64) +#undef DO_VFP_cmp + +/* Integer to float and float to integer conversions */ + +#define CONV_ITOF(name, ftype, fsz, sign) \ +ftype HELPER(name)(uint32_t x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + return sign##int32_to_##float##fsz((sign##int32_t)x, fpst); \ +} + +#define CONV_FTOI(name, ftype, fsz, sign, round) \ +sign##int32_t HELPER(name)(ftype x, void *fpstp) \ +{ \ + float_status *fpst = fpstp; \ + if (float##fsz##_is_any_nan(x)) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##sign##int32##round(x, fpst); \ +} + +#define FLOAT_CONVS(name, p, ftype, fsz, sign) \ + CONV_ITOF(vfp_##name##to##p, ftype, fsz, sign) \ + CONV_FTOI(vfp_to##name##p, ftype, fsz, sign, ) \ + CONV_FTOI(vfp_to##name##z##p, ftype, fsz, sign, _round_to_zero) + +FLOAT_CONVS(si, h, uint32_t, 16, ) +FLOAT_CONVS(si, s, float32, 32, ) +FLOAT_CONVS(si, d, float64, 64, ) +FLOAT_CONVS(ui, h, uint32_t, 16, u) +FLOAT_CONVS(ui, s, float32, 32, u) +FLOAT_CONVS(ui, d, float64, 64, u) + +#undef CONV_ITOF +#undef CONV_FTOI +#undef FLOAT_CONVS + +/* floating point conversion */ +float64 VFP_HELPER(fcvtd, s)(float32 x, CPUARMState *env) +{ + return float32_to_float64(x, &env->vfp.fp_status); +} + +float32 VFP_HELPER(fcvts, d)(float64 x, CPUARMState *env) +{ + return float64_to_float32(x, &env->vfp.fp_status); +} + +/* VFP3 fixed point conversion. */ +#define VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ +float##fsz HELPER(vfp_##name##to##p)(uint##isz##_t x, uint32_t shift, \ + void *fpstp) \ +{ return itype##_to_##float##fsz##_scalbn(x, -shift, fpstp); } + +#define VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, ROUND, suff) \ +uint##isz##_t HELPER(vfp_to##name##p##suff)(float##fsz x, uint32_t shift, \ + void *fpst) \ +{ \ + if (unlikely(float##fsz##_is_any_nan(x))) { \ + float_raise(float_flag_invalid, fpst); \ + return 0; \ + } \ + return float##fsz##_to_##itype##_scalbn(x, ROUND, shift, fpst); \ +} + +#define VFP_CONV_FIX(name, p, fsz, isz, itype) \ +VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + float_round_to_zero, _round_to_zero) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + get_float_rounding_mode(fpst), ) + +#define VFP_CONV_FIX_A64(name, p, fsz, isz, itype) \ +VFP_CONV_FIX_FLOAT(name, p, fsz, isz, itype) \ +VFP_CONV_FLOAT_FIX_ROUND(name, p, fsz, isz, itype, \ + get_float_rounding_mode(fpst), ) + +VFP_CONV_FIX(sh, d, 64, 64, int16) +VFP_CONV_FIX(sl, d, 64, 64, int32) +VFP_CONV_FIX_A64(sq, d, 64, 64, int64) +VFP_CONV_FIX(uh, d, 64, 64, uint16) +VFP_CONV_FIX(ul, d, 64, 64, uint32) +VFP_CONV_FIX_A64(uq, d, 64, 64, uint64) +VFP_CONV_FIX(sh, s, 32, 32, int16) +VFP_CONV_FIX(sl, s, 32, 32, int32) +VFP_CONV_FIX_A64(sq, s, 32, 64, int64) +VFP_CONV_FIX(uh, s, 32, 32, uint16) +VFP_CONV_FIX(ul, s, 32, 32, uint32) +VFP_CONV_FIX_A64(uq, s, 32, 64, uint64) + +#undef VFP_CONV_FIX +#undef VFP_CONV_FIX_FLOAT +#undef VFP_CONV_FLOAT_FIX_ROUND +#undef VFP_CONV_FIX_A64 + +uint32_t HELPER(vfp_sltoh)(uint32_t x, uint32_t shift, void *fpst) +{ + return int32_to_float16_scalbn(x, -shift, fpst); +} + +uint32_t HELPER(vfp_ultoh)(uint32_t x, uint32_t shift, void *fpst) +{ + return uint32_to_float16_scalbn(x, -shift, fpst); +} + +uint32_t HELPER(vfp_sqtoh)(uint64_t x, uint32_t shift, void *fpst) +{ + return int64_to_float16_scalbn(x, -shift, fpst); +} + +uint32_t HELPER(vfp_uqtoh)(uint64_t x, uint32_t shift, void *fpst) +{ + return uint64_to_float16_scalbn(x, -shift, fpst); +} + +uint32_t HELPER(vfp_toshh)(uint32_t x, uint32_t shift, void *fpst) +{ + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int16_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); +} + +uint32_t HELPER(vfp_touhh)(uint32_t x, uint32_t shift, void *fpst) +{ + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint16_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); +} + +uint32_t HELPER(vfp_toslh)(uint32_t x, uint32_t shift, void *fpst) +{ + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int32_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); +} + +uint32_t HELPER(vfp_toulh)(uint32_t x, uint32_t shift, void *fpst) +{ + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint32_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); +} + +uint64_t HELPER(vfp_tosqh)(uint32_t x, uint32_t shift, void *fpst) +{ + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_int64_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); +} + +uint64_t HELPER(vfp_touqh)(uint32_t x, uint32_t shift, void *fpst) +{ + if (unlikely(float16_is_any_nan(x))) { + float_raise(float_flag_invalid, fpst); + return 0; + } + return float16_to_uint64_scalbn(x, get_float_rounding_mode(fpst), + shift, fpst); +} + +/* Set the current fp rounding mode and return the old one. + * The argument is a softfloat float_round_ value. + */ +uint32_t HELPER(set_rmode)(uint32_t rmode, void *fpstp) +{ + float_status *fp_status = fpstp; + + uint32_t prev_rmode = get_float_rounding_mode(fp_status); + set_float_rounding_mode(rmode, fp_status); + + return prev_rmode; +} + +/* Set the current fp rounding mode in the standard fp status and return + * the old one. This is for NEON instructions that need to change the + * rounding mode but wish to use the standard FPSCR values for everything + * else. Always set the rounding mode back to the correct value after + * modifying it. + * The argument is a softfloat float_round_ value. + */ +uint32_t HELPER(set_neon_rmode)(uint32_t rmode, CPUARMState *env) +{ + float_status *fp_status = &env->vfp.standard_fp_status; + + uint32_t prev_rmode = get_float_rounding_mode(fp_status); + set_float_rounding_mode(rmode, fp_status); + + return prev_rmode; +} + +/* Half precision conversions. */ +float32 HELPER(vfp_fcvt_f16_to_f32)(uint32_t a, void *fpstp, uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float32 r = float16_to_float32(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f32_to_f16)(float32 a, void *fpstp, uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float32_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; +} + +float64 HELPER(vfp_fcvt_f16_to_f64)(uint32_t a, void *fpstp, uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing input denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_inputs_to_zero(fpst); + set_flush_inputs_to_zero(false, fpst); + float64 r = float16_to_float64(a, !ahp_mode, fpst); + set_flush_inputs_to_zero(save, fpst); + return r; +} + +uint32_t HELPER(vfp_fcvt_f64_to_f16)(float64 a, void *fpstp, uint32_t ahp_mode) +{ + /* Squash FZ16 to 0 for the duration of conversion. In this case, + * it would affect flushing output denormals. + */ + float_status *fpst = fpstp; + flag save = get_flush_to_zero(fpst); + set_flush_to_zero(false, fpst); + float16 r = float64_to_float16(a, !ahp_mode, fpst); + set_flush_to_zero(save, fpst); + return r; +} + +#define float32_two make_float32(0x40000000) +#define float32_three make_float32(0x40400000) +#define float32_one_point_five make_float32(0x3fc00000) + +float32 HELPER(recps_f32)(float32 a, float32 b, CPUARMState *env) +{ + float_status *s = &env->vfp.standard_fp_status; + if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || + (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { + if (!(float32_is_zero(a) || float32_is_zero(b))) { + float_raise(float_flag_input_denormal, s); + } + return float32_two; + } + return float32_sub(float32_two, float32_mul(a, b, s), s); +} + +float32 HELPER(rsqrts_f32)(float32 a, float32 b, CPUARMState *env) +{ + float_status *s = &env->vfp.standard_fp_status; + float32 product; + if ((float32_is_infinity(a) && float32_is_zero_or_denormal(b)) || + (float32_is_infinity(b) && float32_is_zero_or_denormal(a))) { + if (!(float32_is_zero(a) || float32_is_zero(b))) { + float_raise(float_flag_input_denormal, s); + } + return float32_one_point_five; + } + product = float32_mul(a, b, s); + return float32_div(float32_sub(float32_three, product, s), float32_two, s); +} + +/* NEON helpers. */ + +/* Constants 256 and 512 are used in some helpers; we avoid relying on + * int->float conversions at run-time. */ +#define float64_256 make_float64(0x4070000000000000LL) +#define float64_512 make_float64(0x4080000000000000LL) +#define float16_maxnorm make_float16(0x7bff) +#define float32_maxnorm make_float32(0x7f7fffff) +#define float64_maxnorm make_float64(0x7fefffffffffffffLL) + +/* Reciprocal functions + * + * The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM, see FPRecipEstimate()/RecipEstimate + */ + +/* See RecipEstimate() + * + * input is a 9 bit fixed point number + * input range 256 .. 511 for a number from 0.5 <= x < 1.0. + * result range 256 .. 511 for a number from 1.0 to 511/256. + */ + +static int recip_estimate(int input) +{ + int a, b, r; + assert(256 <= input && input < 512); + a = (input * 2) + 1; + b = (1 << 19) / a; + r = (b + 1) >> 1; + assert(256 <= r && r < 512); + return r; +} + +/* + * Common wrapper to call recip_estimate + * + * The parameters are exponent and 64 bit fraction (without implicit + * bit) where the binary point is nominally at bit 52. Returns a + * float64 which can then be rounded to the appropriate size by the + * callee. + */ + +static uint64_t call_recip_estimate(int *exp, int exp_off, uint64_t frac) +{ + uint32_t scaled, estimate; + uint64_t result_frac; + int result_exp; + + /* Handle sub-normals */ + if (*exp == 0) { + if (extract64(frac, 51, 1) == 0) { + *exp = -1; + frac <<= 2; + } else { + frac <<= 1; + } + } + + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + estimate = recip_estimate(scaled); + + result_exp = exp_off - *exp; + result_frac = deposit64(0, 44, 8, estimate); + if (result_exp == 0) { + result_frac = deposit64(result_frac >> 1, 51, 1, 1); + } else if (result_exp == -1) { + result_frac = deposit64(result_frac >> 2, 50, 2, 1); + result_exp = 0; + } + + *exp = result_exp; + + return result_frac; +} + +static bool round_to_inf(float_status *fpst, bool sign_bit) +{ + switch (fpst->float_rounding_mode) { + case float_round_nearest_even: /* Round to Nearest */ + return true; + case float_round_up: /* Round to +Inf */ + return !sign_bit; + case float_round_down: /* Round to -Inf */ + return sign_bit; + case float_round_to_zero: /* Round to Zero */ + return false; + } + + g_assert_not_reached(); +} + +uint32_t HELPER(recpe_f16)(uint32_t input, void *fpstp) +{ + float_status *fpst = fpstp; + float16 f16 = float16_squash_input_denormal(input, fpst); + uint32_t f16_val = float16_val(f16); + uint32_t f16_sign = float16_is_neg(f16); + int f16_exp = extract32(f16_val, 10, 5); + uint32_t f16_frac = extract32(f16_val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, fpst)) { + float_raise(float_flag_invalid, fpst); + nan = float16_silence_nan(f16, fpst); + } + if (fpst->default_nan_mode) { + nan = float16_default_nan(fpst); + } + return nan; + } else if (float16_is_infinity(f16)) { + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, fpst); + return float16_set_sign(float16_infinity, float16_is_neg(f16)); + } else if (float16_abs(f16) < (1 << 8)) { + /* Abs(value) < 2.0^-16 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f16_sign)) { + return float16_set_sign(float16_infinity, f16_sign); + } else { + return float16_set_sign(float16_maxnorm, f16_sign); + } + } else if (f16_exp >= 29 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float16_set_sign(float16_zero, float16_is_neg(f16)); + } + + f64_frac = call_recip_estimate(&f16_exp, 29, + ((uint64_t) f16_frac) << (52 - 10)); + + /* result = sign : result_exp<4:0> : fraction<51:42> */ + f16_val = deposit32(0, 15, 1, f16_sign); + f16_val = deposit32(f16_val, 10, 5, f16_exp); + f16_val = deposit32(f16_val, 0, 10, extract64(f64_frac, 52 - 10, 10)); + return make_float16(f16_val); +} + +float32 HELPER(recpe_f32)(float32 input, void *fpstp) +{ + float_status *fpst = fpstp; + float32 f32 = float32_squash_input_denormal(input, fpst); + uint32_t f32_val = float32_val(f32); + bool f32_sign = float32_is_neg(f32); + int f32_exp = extract32(f32_val, 23, 8); + uint32_t f32_frac = extract32(f32_val, 0, 23); + uint64_t f64_frac; + + if (float32_is_any_nan(f32)) { + float32 nan = f32; + if (float32_is_signaling_nan(f32, fpst)) { + float_raise(float_flag_invalid, fpst); + nan = float32_silence_nan(f32, fpst); + } + if (fpst->default_nan_mode) { + nan = float32_default_nan(fpst); + } + return nan; + } else if (float32_is_infinity(f32)) { + return float32_set_sign(float32_zero, float32_is_neg(f32)); + } else if (float32_is_zero(f32)) { + float_raise(float_flag_divbyzero, fpst); + return float32_set_sign(float32_infinity, float32_is_neg(f32)); + } else if (float32_abs(f32) < (1ULL << 21)) { + /* Abs(value) < 2.0^-128 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f32_sign)) { + return float32_set_sign(float32_infinity, f32_sign); + } else { + return float32_set_sign(float32_maxnorm, f32_sign); + } + } else if (f32_exp >= 253 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float32_set_sign(float32_zero, float32_is_neg(f32)); + } + + f64_frac = call_recip_estimate(&f32_exp, 253, + ((uint64_t) f32_frac) << (52 - 23)); + + /* result = sign : result_exp<7:0> : fraction<51:29> */ + f32_val = deposit32(0, 31, 1, f32_sign); + f32_val = deposit32(f32_val, 23, 8, f32_exp); + f32_val = deposit32(f32_val, 0, 23, extract64(f64_frac, 52 - 23, 23)); + return make_float32(f32_val); +} + +float64 HELPER(recpe_f64)(float64 input, void *fpstp) +{ + float_status *fpst = fpstp; + float64 f64 = float64_squash_input_denormal(input, fpst); + uint64_t f64_val = float64_val(f64); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(f64_val, 52, 11); + uint64_t f64_frac = extract64(f64_val, 0, 52); + + /* Deal with any special cases */ + if (float64_is_any_nan(f64)) { + float64 nan = f64; + if (float64_is_signaling_nan(f64, fpst)) { + float_raise(float_flag_invalid, fpst); + nan = float64_silence_nan(f64, fpst); + } + if (fpst->default_nan_mode) { + nan = float64_default_nan(fpst); + } + return nan; + } else if (float64_is_infinity(f64)) { + return float64_set_sign(float64_zero, float64_is_neg(f64)); + } else if (float64_is_zero(f64)) { + float_raise(float_flag_divbyzero, fpst); + return float64_set_sign(float64_infinity, float64_is_neg(f64)); + } else if ((f64_val & ~(1ULL << 63)) < (1ULL << 50)) { + /* Abs(value) < 2.0^-1024 */ + float_raise(float_flag_overflow | float_flag_inexact, fpst); + if (round_to_inf(fpst, f64_sign)) { + return float64_set_sign(float64_infinity, f64_sign); + } else { + return float64_set_sign(float64_maxnorm, f64_sign); + } + } else if (f64_exp >= 2045 && fpst->flush_to_zero) { + float_raise(float_flag_underflow, fpst); + return float64_set_sign(float64_zero, float64_is_neg(f64)); + } + + f64_frac = call_recip_estimate(&f64_exp, 2045, f64_frac); + + /* result = sign : result_exp<10:0> : fraction<51:0>; */ + f64_val = deposit64(0, 63, 1, f64_sign); + f64_val = deposit64(f64_val, 52, 11, f64_exp); + f64_val = deposit64(f64_val, 0, 52, f64_frac); + return make_float64(f64_val); +} + +/* The algorithm that must be used to calculate the estimate + * is specified by the ARM ARM. + */ + +static int do_recip_sqrt_estimate(int a) +{ + int b, estimate; + + assert(128 <= a && a < 512); + if (a < 256) { + a = a * 2 + 1; + } else { + a = (a >> 1) << 1; + a = (a + 1) * 2; + } + b = 512; + while (a * (b + 1) * (b + 1) < (1 << 28)) { + b += 1; + } + estimate = (b + 1) / 2; + assert(256 <= estimate && estimate < 512); + + return estimate; +} + + +static uint64_t recip_sqrt_estimate(int *exp , int exp_off, uint64_t frac) +{ + int estimate; + uint32_t scaled; + + if (*exp == 0) { + while (extract64(frac, 51, 1) == 0) { + frac = frac << 1; + *exp -= 1; + } + frac = extract64(frac, 0, 51) << 1; + } + + if (*exp & 1) { + /* scaled = UInt('01':fraction<51:45>) */ + scaled = deposit32(1 << 7, 0, 7, extract64(frac, 45, 7)); + } else { + /* scaled = UInt('1':fraction<51:44>) */ + scaled = deposit32(1 << 8, 0, 8, extract64(frac, 44, 8)); + } + estimate = do_recip_sqrt_estimate(scaled); + + *exp = (exp_off - *exp) / 2; + return extract64(estimate, 0, 8) << 44; +} + +uint32_t HELPER(rsqrte_f16)(uint32_t input, void *fpstp) +{ + float_status *s = fpstp; + float16 f16 = float16_squash_input_denormal(input, s); + uint16_t val = float16_val(f16); + bool f16_sign = float16_is_neg(f16); + int f16_exp = extract32(val, 10, 5); + uint16_t f16_frac = extract32(val, 0, 10); + uint64_t f64_frac; + + if (float16_is_any_nan(f16)) { + float16 nan = f16; + if (float16_is_signaling_nan(f16, s)) { + float_raise(float_flag_invalid, s); + nan = float16_silence_nan(f16, s); + } + if (s->default_nan_mode) { + nan = float16_default_nan(s); + } + return nan; + } else if (float16_is_zero(f16)) { + float_raise(float_flag_divbyzero, s); + return float16_set_sign(float16_infinity, f16_sign); + } else if (f16_sign) { + float_raise(float_flag_invalid, s); + return float16_default_nan(s); + } else if (float16_is_infinity(f16)) { + return float16_zero; + } + + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f16_frac) << (52 - 10); + + f64_frac = recip_sqrt_estimate(&f16_exp, 44, f64_frac); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(2) */ + val = deposit32(0, 15, 1, f16_sign); + val = deposit32(val, 10, 5, f16_exp); + val = deposit32(val, 2, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float16(val); +} + +float32 HELPER(rsqrte_f32)(float32 input, void *fpstp) +{ + float_status *s = fpstp; + float32 f32 = float32_squash_input_denormal(input, s); + uint32_t val = float32_val(f32); + uint32_t f32_sign = float32_is_neg(f32); + int f32_exp = extract32(val, 23, 8); + uint32_t f32_frac = extract32(val, 0, 23); + uint64_t f64_frac; + + if (float32_is_any_nan(f32)) { + float32 nan = f32; + if (float32_is_signaling_nan(f32, s)) { + float_raise(float_flag_invalid, s); + nan = float32_silence_nan(f32, s); + } + if (s->default_nan_mode) { + nan = float32_default_nan(s); + } + return nan; + } else if (float32_is_zero(f32)) { + float_raise(float_flag_divbyzero, s); + return float32_set_sign(float32_infinity, float32_is_neg(f32)); + } else if (float32_is_neg(f32)) { + float_raise(float_flag_invalid, s); + return float32_default_nan(s); + } else if (float32_is_infinity(f32)) { + return float32_zero; + } + + /* Scale and normalize to a double-precision value between 0.25 and 1.0, + * preserving the parity of the exponent. */ + + f64_frac = ((uint64_t) f32_frac) << 29; + + f64_frac = recip_sqrt_estimate(&f32_exp, 380, f64_frac); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(15) */ + val = deposit32(0, 31, 1, f32_sign); + val = deposit32(val, 23, 8, f32_exp); + val = deposit32(val, 15, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float32(val); +} + +float64 HELPER(rsqrte_f64)(float64 input, void *fpstp) +{ + float_status *s = fpstp; + float64 f64 = float64_squash_input_denormal(input, s); + uint64_t val = float64_val(f64); + bool f64_sign = float64_is_neg(f64); + int f64_exp = extract64(val, 52, 11); + uint64_t f64_frac = extract64(val, 0, 52); + + if (float64_is_any_nan(f64)) { + float64 nan = f64; + if (float64_is_signaling_nan(f64, s)) { + float_raise(float_flag_invalid, s); + nan = float64_silence_nan(f64, s); + } + if (s->default_nan_mode) { + nan = float64_default_nan(s); + } + return nan; + } else if (float64_is_zero(f64)) { + float_raise(float_flag_divbyzero, s); + return float64_set_sign(float64_infinity, float64_is_neg(f64)); + } else if (float64_is_neg(f64)) { + float_raise(float_flag_invalid, s); + return float64_default_nan(s); + } else if (float64_is_infinity(f64)) { + return float64_zero; + } + + f64_frac = recip_sqrt_estimate(&f64_exp, 3068, f64_frac); + + /* result = sign : result_exp<4:0> : estimate<7:0> : Zeros(44) */ + val = deposit64(0, 61, 1, f64_sign); + val = deposit64(val, 52, 11, f64_exp); + val = deposit64(val, 44, 8, extract64(f64_frac, 52 - 8, 8)); + return make_float64(val); +} + +uint32_t HELPER(recpe_u32)(uint32_t a, void *fpstp) +{ + /* float_status *s = fpstp; */ + int input, estimate; + + if ((a & 0x80000000) == 0) { + return 0xffffffff; + } + + input = extract32(a, 23, 9); + estimate = recip_estimate(input); + + return deposit32(0, (32 - 9), 9, estimate); +} + +uint32_t HELPER(rsqrte_u32)(uint32_t a, void *fpstp) +{ + int estimate; + + if ((a & 0xc0000000) == 0) { + return 0xffffffff; + } + + estimate = do_recip_sqrt_estimate(extract32(a, 23, 9)); + + return deposit32(0, 23, 9, estimate); +} + +/* VFPv4 fused multiply-accumulate */ +float32 VFP_HELPER(muladd, s)(float32 a, float32 b, float32 c, void *fpstp) +{ + float_status *fpst = fpstp; + return float32_muladd(a, b, c, 0, fpst); +} + +float64 VFP_HELPER(muladd, d)(float64 a, float64 b, float64 c, void *fpstp) +{ + float_status *fpst = fpstp; + return float64_muladd(a, b, c, 0, fpst); +} + +/* ARMv8 round to integral */ +float32 HELPER(rints_exact)(float32 x, void *fp_status) +{ + return float32_round_to_int(x, fp_status); +} + +float64 HELPER(rintd_exact)(float64 x, void *fp_status) +{ + return float64_round_to_int(x, fp_status); +} + +float32 HELPER(rints)(float32 x, void *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float32 ret; + + ret = float32_round_to_int(x, fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +float64 HELPER(rintd)(float64 x, void *fp_status) +{ + int old_flags = get_float_exception_flags(fp_status), new_flags; + float64 ret; + + ret = float64_round_to_int(x, fp_status); + + new_flags = get_float_exception_flags(fp_status); + + /* Suppress any inexact exceptions the conversion produced */ + if (!(old_flags & float_flag_inexact)) { + new_flags = get_float_exception_flags(fp_status); + set_float_exception_flags(new_flags & ~float_flag_inexact, fp_status); + } + + return ret; +} + +/* Convert ARM rounding mode to softfloat */ +int arm_rmode_to_sf(int rmode) +{ + switch (rmode) { + case FPROUNDING_TIEAWAY: + rmode = float_round_ties_away; + break; + case FPROUNDING_ODD: + /* FIXME: add support for TIEAWAY and ODD */ + qemu_log_mask(LOG_UNIMP, "arm: unimplemented rounding mode: %d\n", + rmode); + /* fall through for now */ + case FPROUNDING_TIEEVEN: + default: + rmode = float_round_nearest_even; + break; + case FPROUNDING_POSINF: + rmode = float_round_up; + break; + case FPROUNDING_NEGINF: + rmode = float_round_down; + break; + case FPROUNDING_ZERO: + rmode = float_round_to_zero; + break; + } + return rmode; +} + +/* + * Implement float64 to int32_t conversion without saturation; + * the result is supplied modulo 2^32. + */ +uint64_t HELPER(fjcvtzs)(float64 value, void *vstatus) +{ + float_status *status = vstatus; + uint32_t exp, sign; + uint64_t frac; + uint32_t inexact = 1; /* !Z */ + + sign = extract64(value, 63, 1); + exp = extract64(value, 52, 11); + frac = extract64(value, 0, 52); + + if (exp == 0) { + /* While not inexact for IEEE FP, -0.0 is inexact for JavaScript. */ + inexact = sign; + if (frac != 0) { + if (status->flush_inputs_to_zero) { + float_raise(float_flag_input_denormal, status); + } else { + float_raise(float_flag_inexact, status); + inexact = 1; + } + } + frac = 0; + } else if (exp == 0x7ff) { + /* This operation raises Invalid for both NaN and overflow (Inf). */ + float_raise(float_flag_invalid, status); + frac = 0; + } else { + int true_exp = exp - 1023; + int shift = true_exp - 52; + + /* Restore implicit bit. */ + frac |= 1ull << 52; + + /* Shift the fraction into place. */ + if (shift >= 0) { + /* The number is so large we must shift the fraction left. */ + if (shift >= 64) { + /* The fraction is shifted out entirely. */ + frac = 0; + } else { + frac <<= shift; + } + } else if (shift > -64) { + /* Normal case -- shift right and notice if bits shift out. */ + inexact = (frac << (64 + shift)) != 0; + frac >>= -shift; + } else { + /* The fraction is shifted out entirely. */ + frac = 0; + } + + /* Notice overflow or inexact exceptions. */ + if (true_exp > 31 || frac > (sign ? 0x80000000ull : 0x7fffffff)) { + /* Overflow, for which this operation raises invalid. */ + float_raise(float_flag_invalid, status); + inexact = 1; + } else if (inexact) { + float_raise(float_flag_inexact, status); + } + + /* Honor the sign. */ + if (sign) { + frac = -frac; + } + } + + /* Pack the result and the env->ZF representation of Z together. */ + return deposit64(frac, 32, 32, inexact); +} + +uint32_t HELPER(vjcvt)(float64 value, CPUARMState *env) +{ + uint64_t pair = HELPER(fjcvtzs)(value, &env->vfp.fp_status); + uint32_t result = pair; + uint32_t z = (pair >> 32) == 0; + + /* Store Z, clear NCV, in FPSCR.NZCV. */ + env->vfp.xregs[ARM_VFP_FPSCR] + = (env->vfp.xregs[ARM_VFP_FPSCR] & ~CPSR_NZCV) | (z * CPSR_Z); + + return result; +} + +/* Round a float32 to an integer that fits in int32_t or int64_t. */ +static float32 frint_s(float32 f, float_status *fpst, int intsize) +{ + int old_flags = get_float_exception_flags(fpst); + uint32_t exp = extract32(f, 23, 8); + + if (unlikely(exp == 0xff)) { + /* NaN or Inf. */ + goto overflow; + } + + /* Round and re-extract the exponent. */ + f = float32_round_to_int(f, fpst); + exp = extract32(f, 23, 8); + + /* Validate the range of the result. */ + if (exp < 126 + intsize) { + /* abs(F) <= INT{N}_MAX */ + return f; + } + if (exp == 126 + intsize) { + uint32_t sign = extract32(f, 31, 1); + uint32_t frac = extract32(f, 0, 23); + if (sign && frac == 0) { + /* F == INT{N}_MIN */ + return f; + } + } + + overflow: + /* + * Raise Invalid and return INT{N}_MIN as a float. Revert any + * inexact exception float32_round_to_int may have raised. + */ + set_float_exception_flags(old_flags | float_flag_invalid, fpst); + return (0x100u + 126u + intsize) << 23; +} + +float32 HELPER(frint32_s)(float32 f, void *fpst) +{ + return frint_s(f, fpst, 32); +} + +float32 HELPER(frint64_s)(float32 f, void *fpst) +{ + return frint_s(f, fpst, 64); +} + +/* Round a float64 to an integer that fits in int32_t or int64_t. */ +static float64 frint_d(float64 f, float_status *fpst, int intsize) +{ + int old_flags = get_float_exception_flags(fpst); + uint32_t exp = extract64(f, 52, 11); + + if (unlikely(exp == 0x7ff)) { + /* NaN or Inf. */ + goto overflow; + } + + /* Round and re-extract the exponent. */ + f = float64_round_to_int(f, fpst); + exp = extract64(f, 52, 11); + + /* Validate the range of the result. */ + if (exp < 1022 + intsize) { + /* abs(F) <= INT{N}_MAX */ + return f; + } + if (exp == 1022 + intsize) { + uint64_t sign = extract64(f, 63, 1); + uint64_t frac = extract64(f, 0, 52); + if (sign && frac == 0) { + /* F == INT{N}_MIN */ + return f; + } + } + + overflow: + /* + * Raise Invalid and return INT{N}_MIN as a float. Revert any + * inexact exception float64_round_to_int may have raised. + */ + set_float_exception_flags(old_flags | float_flag_invalid, fpst); + return (uint64_t)(0x800 + 1022 + intsize) << 52; +} + +float64 HELPER(frint32_d)(float64 f, void *fpst) +{ + return frint_d(f, fpst, 32); +} + +float64 HELPER(frint64_d)(float64 f, void *fpst) +{ + return frint_d(f, fpst, 64); +} diff --git a/target/cris/helper.c b/target/cris/helper.c index d2ec349191..b2dbb2075c 100644 --- a/target/cris/helper.c +++ b/target/cris/helper.c @@ -240,7 +240,7 @@ void cris_cpu_do_interrupt(CPUState *cs) /* Exception starts with dslot cleared. */ env->dslot = 0; } - + if (env->pregs[PR_CCS] & U_FLAG) { /* Swap stack pointers. */ env->pregs[PR_USP] = env->regs[R_SP]; diff --git a/target/cris/mmu.h b/target/cris/mmu.h index 8e249e812b..0217f476de 100644 --- a/target/cris/mmu.h +++ b/target/cris/mmu.h @@ -5,13 +5,13 @@ struct cris_mmu_result { - uint32_t phy; - int prot; - int bf_vec; + uint32_t phy; + int prot; + int bf_vec; }; void cris_mmu_init(CPUCRISState *env); void cris_mmu_flush_pid(CPUCRISState *env, uint32_t pid); int cris_mmu_translate(struct cris_mmu_result *res, - CPUCRISState *env, uint32_t vaddr, - int rw, int mmu_idx, int debug); + CPUCRISState *env, uint32_t vaddr, + int rw, int mmu_idx, int debug); diff --git a/target/cris/translate.c b/target/cris/translate.c index 4ae1c04daf..11b2c11174 100644 --- a/target/cris/translate.c +++ b/target/cris/translate.c @@ -137,11 +137,7 @@ typedef struct DisasContext { static void gen_BUG(DisasContext *dc, const char *file, int 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); + cpu_abort(CPU(dc->cpu), "%s:%d pc=%x\n", file, line, dc->pc); } static const char *regnames_v32[] = diff --git a/target/cris/translate_v10.inc.c b/target/cris/translate_v10.inc.c index fce78825cc..a87b8bb281 100644 --- a/target/cris/translate_v10.inc.c +++ b/target/cris/translate_v10.inc.c @@ -384,7 +384,7 @@ static unsigned int dec10_setclrf(DisasContext *dc) } static inline void dec10_reg_prep_sext(DisasContext *dc, int size, int sext, - TCGv dd, TCGv ds, TCGv sd, TCGv ss) + TCGv dd, TCGv ds, TCGv sd, TCGv ss) { if (sext) { t_gen_sext(dd, sd, size); diff --git a/target/hppa/Makefile.objs b/target/hppa/Makefile.objs index 3359da5341..174f50a96c 100644 --- a/target/hppa/Makefile.objs +++ b/target/hppa/Makefile.objs @@ -1,3 +1,11 @@ obj-y += translate.o helper.o cpu.o op_helper.o gdbstub.o mem_helper.o obj-y += int_helper.o obj-$(CONFIG_SOFTMMU) += machine.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +target/hppa/decode.inc.c: $(SRC_PATH)/target/hppa/insns.decode $(DECODETREE) + $(call quiet-command,\ + $(PYTHON) $(DECODETREE) -o $@ $<, "GEN", $(TARGET_DIR)$@) + +target/hppa/translate.o: target/hppa/decode.inc.c diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 861bbb1f16..c062c7969c 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -143,6 +143,10 @@ #endif #define CR_RC 0 +#define CR_PID1 8 +#define CR_PID2 9 +#define CR_PID3 12 +#define CR_PID4 13 #define CR_SCRCCR 10 #define CR_SAR 11 #define CR_IVA 14 @@ -341,6 +345,12 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env); void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg); void cpu_hppa_loaded_fr0(CPUHPPAState *env); +#ifdef CONFIG_USER_ONLY +static inline void cpu_hppa_change_prot_id(CPUHPPAState *env) { } +#else +void cpu_hppa_change_prot_id(CPUHPPAState *env); +#endif + #define cpu_signal_handler cpu_hppa_signal_handler int cpu_hppa_signal_handler(int host_signum, void *pinfo, void *puc); diff --git a/target/hppa/gdbstub.c b/target/hppa/gdbstub.c index e2e9c4d77f..983bf92aaf 100644 --- a/target/hppa/gdbstub.c +++ b/target/hppa/gdbstub.c @@ -93,19 +93,19 @@ int hppa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) val = env->cr[CR_RC]; break; case 52: - val = env->cr[8]; + val = env->cr[CR_PID1]; break; case 53: - val = env->cr[9]; + val = env->cr[CR_PID2]; break; case 54: val = env->cr[CR_SCRCCR]; break; case 55: - val = env->cr[12]; + val = env->cr[CR_PID3]; break; case 56: - val = env->cr[13]; + val = env->cr[CR_PID4]; break; case 57: val = env->cr[24]; @@ -224,19 +224,23 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->cr[CR_RC] = val; break; case 52: - env->cr[8] = val; + env->cr[CR_PID1] = val; + cpu_hppa_change_prot_id(env); break; case 53: - env->cr[9] = val; + env->cr[CR_PID2] = val; + cpu_hppa_change_prot_id(env); break; case 54: env->cr[CR_SCRCCR] = val; break; case 55: - env->cr[12] = val; + env->cr[CR_PID3] = val; + cpu_hppa_change_prot_id(env); break; case 56: - env->cr[13] = val; + env->cr[CR_PID4] = val; + cpu_hppa_change_prot_id(env); break; case 57: env->cr[24] = val; @@ -266,7 +270,7 @@ int hppa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) case 65 ... 127: { uint64_t *fr = &env->fr[(n - 64) / 2]; - *fr = deposit64(*fr, val, (n & 1 ? 0 : 32), 32); + *fr = deposit64(*fr, (n & 1 ? 0 : 32), 32, val); } break; default: diff --git a/target/hppa/helper.c b/target/hppa/helper.c index 6539061e52..ac750b62ef 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "fpu/softfloat.h" +#include "exec/exec-all.h" #include "exec/helper-proto.h" target_ureg cpu_hppa_get_psw(CPUHPPAState *env) @@ -49,6 +50,7 @@ target_ureg cpu_hppa_get_psw(CPUHPPAState *env) void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) { + target_ureg old_psw = env->psw; target_ureg cb = 0; env->psw = psw & ~(PSW_N | PSW_V | PSW_CB); @@ -64,6 +66,14 @@ void cpu_hppa_put_psw(CPUHPPAState *env, target_ureg psw) cb |= ((psw >> 9) & 1) << 8; cb |= ((psw >> 8) & 1) << 4; env->psw_cb = cb; + + /* If PSW_P changes, it affects how we translate addresses. */ + if ((psw ^ old_psw) & PSW_P) { +#ifndef CONFIG_USER_ONLY + CPUState *src = CPU(hppa_env_get_cpu(env)); + tlb_flush_by_mmuidx(src, 0xf); +#endif + } } void hppa_cpu_dump_state(CPUState *cs, FILE *f, diff --git a/target/hppa/helper.h b/target/hppa/helper.h index bfe0dd1db1..38d834ef6b 100644 --- a/target/hppa/helper.h +++ b/target/hppa/helper.h @@ -92,4 +92,5 @@ DEF_HELPER_FLAGS_3(itlbp, TCG_CALL_NO_RWG, void, env, tl, tr) DEF_HELPER_FLAGS_2(ptlb, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_1(ptlbe, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(lpa, TCG_CALL_NO_WG, tr, env, tl) +DEF_HELPER_FLAGS_1(change_prot_id, TCG_CALL_NO_RWG, void, env) #endif diff --git a/target/hppa/insns.decode b/target/hppa/insns.decode new file mode 100644 index 0000000000..098370c2f0 --- /dev/null +++ b/target/hppa/insns.decode @@ -0,0 +1,530 @@ +# +# HPPA instruction decode definitions. +# +# Copyright (c) 2018 Richard Henderson +# +# 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 . +# + +#### +# Field definitions +#### + +%assemble_sr3 13:1 14:2 +%assemble_sr3x 13:1 14:2 !function=expand_sr3x + +%assemble_11a 0:s1 4:10 !function=expand_shl3 +%assemble_12 0:s1 2:1 3:10 !function=expand_shl2 +%assemble_12a 0:s1 3:11 !function=expand_shl2 +%assemble_17 0:s1 16:5 2:1 3:10 !function=expand_shl2 +%assemble_22 0:s1 16:10 2:1 3:10 !function=expand_shl2 + +%assemble_21 0:s1 1:11 14:2 16:5 12:2 !function=expand_shl11 + +%lowsign_11 0:s1 1:10 +%lowsign_14 0:s1 1:13 + +%sm_imm 16:10 !function=expand_sm_imm + +%rm64 1:1 16:5 +%rt64 6:1 0:5 +%ra64 7:1 21:5 +%rb64 12:1 16:5 +%rc64 8:1 13:3 9:2 +%rc32 13:3 9:2 + +%im5_0 0:s1 1:4 +%im5_16 16:s1 17:4 +%ma_to_m 5:1 13:1 !function=ma_to_m +%ma2_to_m 2:2 !function=ma_to_m +%pos_to_m 0:1 !function=pos_to_m +%neg_to_m 0:1 !function=neg_to_m +%a_to_m 2:1 !function=neg_to_m + +#### +# Argument set definitions +#### + +# All insns that need to form a virtual address should use this set. +&ldst t b x disp sp m scale size + +&rr_cf t r cf +&rrr_cf t r1 r2 cf +&rrr_cf_sh t r1 r2 cf sh +&rri_cf t r i cf + +&rrb_c_f disp n c f r1 r2 +&rib_c_f disp n c f r i + +#### +# Format definitions +#### + +@rr_cf ...... r:5 ..... cf:4 ....... t:5 &rr_cf +@rrr_cf ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf +@rrr_cf_sh ...... r2:5 r1:5 cf:4 .... sh:2 . t:5 &rrr_cf_sh +@rrr_cf_sh0 ...... r2:5 r1:5 cf:4 ....... t:5 &rrr_cf_sh sh=0 +@rri_cf ...... r:5 t:5 cf:4 . ........... &rri_cf i=%lowsign_11 + +@rrb_cf ...... r2:5 r1:5 c:3 ........... n:1 . \ + &rrb_c_f disp=%assemble_12 +@rib_cf ...... r:5 ..... c:3 ........... n:1 . \ + &rib_c_f disp=%assemble_12 i=%im5_16 + +#### +# System +#### + +break 000000 ----- ----- --- 00000000 ----- + +mtsp 000000 ----- r:5 ... 11000001 00000 sp=%assemble_sr3 +mtctl 000000 t:5 r:5 --- 11000010 00000 +mtsarcm 000000 01011 r:5 --- 11000110 00000 +mtsm 000000 00000 r:5 000 11000011 00000 + +mfia 000000 ----- 00000 --- 10100101 t:5 +mfsp 000000 ----- 00000 ... 00100101 t:5 sp=%assemble_sr3 +mfctl 000000 r:5 00000- e:1 -01000101 t:5 + +sync 000000 ----- ----- 000 00100000 00000 # sync, syncdma + +ldsid 000000 b:5 ----- sp:2 0 10000101 t:5 + +rsm 000000 .......... 000 01110011 t:5 i=%sm_imm +ssm 000000 .......... 000 01101011 t:5 i=%sm_imm + +rfi 000000 ----- ----- --- 01100000 00000 +rfi_r 000000 ----- ----- --- 01100101 00000 + +# These are artificial instructions used by QEMU firmware. +# They are allocated from the unassigned instruction space. +halt 1111 1111 1111 1101 1110 1010 1101 0000 +reset 1111 1111 1111 1101 1110 1010 1101 0001 + +#### +# Memory Management +#### + +@addrx ...... b:5 x:5 .. ........ m:1 ..... \ + &ldst disp=0 scale=0 t=0 sp=0 size=0 + +nop 000001 ----- ----- -- 11001010 0 ----- # fdc, disp +nop_addrx 000001 ..... ..... -- 01001010 . ----- @addrx # fdc, index +nop_addrx 000001 ..... ..... -- 01001011 . ----- @addrx # fdce +nop_addrx 000001 ..... ..... --- 0001010 . ----- @addrx # fic 0x0a +nop_addrx 000001 ..... ..... -- 01001111 . 00000 @addrx # fic 0x4f +nop_addrx 000001 ..... ..... --- 0001011 . ----- @addrx # fice +nop_addrx 000001 ..... ..... -- 01001110 . 00000 @addrx # pdc + +probe 000001 b:5 ri:5 sp:2 imm:1 100011 write:1 0 t:5 + +ixtlbx 000001 b:5 r:5 sp:2 0100000 addr:1 0 00000 data=1 +ixtlbx 000001 b:5 r:5 ... 000000 addr:1 0 00000 \ + sp=%assemble_sr3x data=0 + +pxtlbx 000001 b:5 x:5 sp:2 0100100 local:1 m:1 ----- data=1 +pxtlbx 000001 b:5 x:5 ... 000100 local:1 m:1 ----- \ + sp=%assemble_sr3x data=0 + +lpa 000001 b:5 x:5 sp:2 01001101 m:1 t:5 \ + &ldst disp=0 scale=0 size=0 + +lci 000001 ----- ----- -- 01001100 0 t:5 + +#### +# Arith/Log +#### + +andcm 000010 ..... ..... .... 000000 0 ..... @rrr_cf +and 000010 ..... ..... .... 001000 0 ..... @rrr_cf +or 000010 ..... ..... .... 001001 0 ..... @rrr_cf +xor 000010 ..... ..... .... 001010 0 ..... @rrr_cf +uxor 000010 ..... ..... .... 001110 0 ..... @rrr_cf +ds 000010 ..... ..... .... 010001 0 ..... @rrr_cf +cmpclr 000010 ..... ..... .... 100010 0 ..... @rrr_cf +uaddcm 000010 ..... ..... .... 100110 0 ..... @rrr_cf +uaddcm_tc 000010 ..... ..... .... 100111 0 ..... @rrr_cf +dcor 000010 ..... 00000 .... 101110 0 ..... @rr_cf +dcor_i 000010 ..... 00000 .... 101111 0 ..... @rr_cf + +add 000010 ..... ..... .... 0110.. 0 ..... @rrr_cf_sh +add_l 000010 ..... ..... .... 1010.. 0 ..... @rrr_cf_sh +add_tsv 000010 ..... ..... .... 1110.. 0 ..... @rrr_cf_sh +add_c 000010 ..... ..... .... 011100 0 ..... @rrr_cf_sh0 +add_c_tsv 000010 ..... ..... .... 111100 0 ..... @rrr_cf_sh0 + +sub 000010 ..... ..... .... 010000 0 ..... @rrr_cf +sub_tsv 000010 ..... ..... .... 110000 0 ..... @rrr_cf +sub_tc 000010 ..... ..... .... 010011 0 ..... @rrr_cf +sub_tsv_tc 000010 ..... ..... .... 110011 0 ..... @rrr_cf +sub_b 000010 ..... ..... .... 010100 0 ..... @rrr_cf +sub_b_tsv 000010 ..... ..... .... 110100 0 ..... @rrr_cf + +ldil 001000 t:5 ..................... i=%assemble_21 +addil 001010 r:5 ..................... i=%assemble_21 +ldo 001101 b:5 t:5 -- .............. i=%lowsign_14 + +addi 101101 ..... ..... .... 0 ........... @rri_cf +addi_tsv 101101 ..... ..... .... 1 ........... @rri_cf +addi_tc 101100 ..... ..... .... 0 ........... @rri_cf +addi_tc_tsv 101100 ..... ..... .... 1 ........... @rri_cf + +subi 100101 ..... ..... .... 0 ........... @rri_cf +subi_tsv 100101 ..... ..... .... 1 ........... @rri_cf + +cmpiclr 100100 ..... ..... .... 0 ........... @rri_cf + +#### +# Index Mem +#### + +@ldstx ...... b:5 x:5 sp:2 scale:1 ....... m:1 t:5 &ldst disp=0 +@ldim5 ...... b:5 ..... sp:2 ......... t:5 \ + &ldst disp=%im5_16 x=0 scale=0 m=%ma_to_m +@stim5 ...... b:5 t:5 sp:2 ......... ..... \ + &ldst disp=%im5_0 x=0 scale=0 m=%ma_to_m + +ld 000011 ..... ..... .. . 1 -- 00 size:2 ...... @ldim5 +ld 000011 ..... ..... .. . 0 -- 00 size:2 ...... @ldstx +st 000011 ..... ..... .. . 1 -- 10 size:2 ...... @stim5 +ldc 000011 ..... ..... .. . 1 -- 0111 ...... @ldim5 size=2 +ldc 000011 ..... ..... .. . 0 -- 0111 ...... @ldstx size=2 +lda 000011 ..... ..... .. . 1 -- 0110 ...... @ldim5 size=2 +lda 000011 ..... ..... .. . 0 -- 0110 ...... @ldstx size=2 +sta 000011 ..... ..... .. . 1 -- 1110 ...... @stim5 size=2 +stby 000011 b:5 r:5 sp:2 a:1 1 -- 1100 m:1 ..... disp=%im5_0 + +@fldstwx ...... b:5 x:5 sp:2 scale:1 ....... m:1 ..... \ + &ldst t=%rt64 disp=0 size=2 +@fldstwi ...... b:5 ..... sp:2 . ....... . ..... \ + &ldst t=%rt64 disp=%im5_16 m=%ma_to_m x=0 scale=0 size=2 + +fldw 001001 ..... ..... .. . 0 -- 000 . . ..... @fldstwx +fldw 001001 ..... ..... .. . 1 -- 000 . . ..... @fldstwi +fstw 001001 ..... ..... .. . 0 -- 100 . . ..... @fldstwx +fstw 001001 ..... ..... .. . 1 -- 100 . . ..... @fldstwi + +@fldstdx ...... b:5 x:5 sp:2 scale:1 ....... m:1 t:5 \ + &ldst disp=0 size=3 +@fldstdi ...... b:5 ..... sp:2 . ....... . t:5 \ + &ldst disp=%im5_16 m=%ma_to_m x=0 scale=0 size=3 + +fldd 001011 ..... ..... .. . 0 -- 000 0 . ..... @fldstdx +fldd 001011 ..... ..... .. . 1 -- 000 0 . ..... @fldstdi +fstd 001011 ..... ..... .. . 0 -- 100 0 . ..... @fldstdx +fstd 001011 ..... ..... .. . 1 -- 100 0 . ..... @fldstdi + +#### +# Offset Mem +#### + +@ldstim14 ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%lowsign_14 x=0 scale=0 m=0 +@ldstim14m ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%lowsign_14 x=0 scale=0 m=%neg_to_m +@ldstim12m ...... b:5 t:5 sp:2 .............. \ + &ldst disp=%assemble_12a x=0 scale=0 m=%pos_to_m + +# LDB, LDH, LDW, LDWM +ld 010000 ..... ..... .. .............. @ldstim14 size=0 +ld 010001 ..... ..... .. .............. @ldstim14 size=1 +ld 010010 ..... ..... .. .............. @ldstim14 size=2 +ld 010011 ..... ..... .. .............. @ldstim14m size=2 +ld 010111 ..... ..... .. ...........10. @ldstim12m size=2 + +# STB, STH, STW, STWM +st 011000 ..... ..... .. .............. @ldstim14 size=0 +st 011001 ..... ..... .. .............. @ldstim14 size=1 +st 011010 ..... ..... .. .............. @ldstim14 size=2 +st 011011 ..... ..... .. .............. @ldstim14m size=2 +st 011111 ..... ..... .. ...........10. @ldstim12m size=2 + +fldw 010110 b:5 ..... sp:2 .............. \ + &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2 +fldw 010111 b:5 ..... sp:2 ...........0.. \ + &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 + +fstw 011110 b:5 ..... sp:2 .............. \ + &ldst disp=%assemble_12a t=%rm64 m=%a_to_m x=0 scale=0 size=2 +fstw 011111 b:5 ..... sp:2 ...........0.. \ + &ldst disp=%assemble_12a t=%rm64 m=0 x=0 scale=0 size=2 + +fldd 010100 b:5 t:5 sp:2 .......... .. 1 . \ + &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 + +fstd 011100 b:5 t:5 sp:2 .......... .. 1 . \ + &ldst disp=%assemble_11a m=%ma2_to_m x=0 scale=0 size=3 + +#### +# Floating-point Multiply Add +#### + +&mpyadd rm1 rm2 ta ra tm +@mpyadd ...... rm1:5 rm2:5 ta:5 ra:5 . tm:5 &mpyadd + +fmpyadd_f 000110 ..... ..... ..... ..... 0 ..... @mpyadd +fmpyadd_d 000110 ..... ..... ..... ..... 1 ..... @mpyadd +fmpysub_f 100110 ..... ..... ..... ..... 0 ..... @mpyadd +fmpysub_d 100110 ..... ..... ..... ..... 1 ..... @mpyadd + +#### +# Conditional Branches +#### + +bb_sar 110000 00000 r:5 c:1 10 ........... n:1 . disp=%assemble_12 +bb_imm 110001 p:5 r:5 c:1 10 ........... n:1 . disp=%assemble_12 + +movb 110010 ..... ..... ... ........... . . @rrb_cf f=0 +movbi 110011 ..... ..... ... ........... . . @rib_cf f=0 + +cmpb 100000 ..... ..... ... ........... . . @rrb_cf f=0 +cmpb 100010 ..... ..... ... ........... . . @rrb_cf f=1 +cmpbi 100001 ..... ..... ... ........... . . @rib_cf f=0 +cmpbi 100011 ..... ..... ... ........... . . @rib_cf f=1 + +addb 101000 ..... ..... ... ........... . . @rrb_cf f=0 +addb 101010 ..... ..... ... ........... . . @rrb_cf f=1 +addbi 101001 ..... ..... ... ........... . . @rib_cf f=0 +addbi 101011 ..... ..... ... ........... . . @rib_cf f=1 + +#### +# Shift, Extract, Deposit +#### + +shrpw_sar 110100 r2:5 r1:5 c:3 00 0 00000 t:5 +shrpw_imm 110100 r2:5 r1:5 c:3 01 0 cpos:5 t:5 + +extrw_sar 110100 r:5 t:5 c:3 10 se:1 00000 clen:5 +extrw_imm 110100 r:5 t:5 c:3 11 se:1 pos:5 clen:5 + +depw_sar 110101 t:5 r:5 c:3 00 nz:1 00000 clen:5 +depw_imm 110101 t:5 r:5 c:3 01 nz:1 cpos:5 clen:5 +depwi_sar 110101 t:5 ..... c:3 10 nz:1 00000 clen:5 i=%im5_16 +depwi_imm 110101 t:5 ..... c:3 11 nz:1 cpos:5 clen:5 i=%im5_16 + +#### +# Branch External +#### + +&BE b l n disp sp +@be ...... b:5 ..... ... ........... n:1 . \ + &BE disp=%assemble_17 sp=%assemble_sr3 + +be 111000 ..... ..... ... ........... . . @be l=0 +be 111001 ..... ..... ... ........... . . @be l=31 + +#### +# Branch +#### + +&BL l n disp +@bl ...... l:5 ..... ... ........... n:1 . &BL disp=%assemble_17 + +# B,L and B,L,PUSH +bl 111010 ..... ..... 000 ........... . . @bl +bl 111010 ..... ..... 100 ........... . . @bl +# B,L (long displacement) +bl 111010 ..... ..... 101 ........... n:1 . &BL l=2 \ + disp=%assemble_22 +b_gate 111010 ..... ..... 001 ........... . . @bl +blr 111010 l:5 x:5 010 00000000000 n:1 0 +bv 111010 b:5 x:5 110 00000000000 n:1 0 +bve 111010 b:5 00000 110 10000000000 n:1 - l=0 +bve 111010 b:5 00000 111 10000000000 n:1 - l=2 + +#### +# FP Fused Multiple-Add +#### + +fmpyfadd_f 101110 ..... ..... ... . 0 ... . . neg:1 ..... \ + rm1=%ra64 rm2=%rb64 ra3=%rc64 t=%rt64 +fmpyfadd_d 101110 rm1:5 rm2:5 ... 0 1 ..0 0 0 neg:1 t:5 ra3=%rc32 + +#### +# FP operations +#### + +&fclass01 r t +&fclass2 r1 r2 c y +&fclass3 r1 r2 t + +@f0c_0 ...... r:5 00000 ..... 00 000 0 t:5 &fclass01 +@f0c_1 ...... r:5 000.. ..... 01 000 0 t:5 &fclass01 +@f0c_2 ...... r1:5 r2:5 y:3 .. 10 000 . c:5 &fclass2 +@f0c_3 ...... r1:5 r2:5 ..... 11 000 0 t:5 &fclass3 + +@f0e_f_0 ...... ..... 00000 ... 0 0 000 .. 0 ..... \ + &fclass01 r=%ra64 t=%rt64 +@f0e_d_0 ...... r:5 00000 ... 0 1 000 00 0 t:5 &fclass01 + +@f0e_ff_1 ...... ..... 000 ... 0000 010 .. 0 ..... \ + &fclass01 r=%ra64 t=%rt64 +@f0e_fd_1 ...... ..... 000 ... 0100 010 .0 0 t:5 &fclass01 r=%ra64 +@f0e_df_1 ...... r:5 000 ... 0001 010 0. 0 ..... &fclass01 t=%rt64 +@f0e_dd_1 ...... r:5 000 ... 0101 010 00 0 t:5 &fclass01 + +@f0e_f_2 ...... ..... ..... y:3 .0 100 .00 c:5 \ + &fclass2 r1=%ra64 r2=%rb64 +@f0e_d_2 ...... r1:5 r2:5 y:3 01 100 000 c:5 &fclass2 + +@f0e_f_3 ...... ..... ..... ... .0 110 ..0 ..... \ + &fclass3 r1=%ra64 r2=%rb64 t=%rt64 +@f0e_d_3 ...... r1:5 r2:5 ... 01 110 000 t:5 + +# Floating point class 0 + +# FID. With r = t = 0, which via fcpy puts 0 into fr0. +# This is machine/revision = 0, which is reserved for simulator. +fcpy_f 001100 00000 00000 00000 000000 00000 \ + &fclass01 r=0 t=0 + +fcpy_f 001100 ..... ..... 010 00 ...... ..... @f0c_0 +fabs_f 001100 ..... ..... 011 00 ...... ..... @f0c_0 +fsqrt_f 001100 ..... ..... 100 00 ...... ..... @f0c_0 +frnd_f 001100 ..... ..... 101 00 ...... ..... @f0c_0 +fneg_f 001100 ..... ..... 110 00 ...... ..... @f0c_0 +fnegabs_f 001100 ..... ..... 111 00 ...... ..... @f0c_0 + +fcpy_d 001100 ..... ..... 010 01 ...... ..... @f0c_0 +fabs_d 001100 ..... ..... 011 01 ...... ..... @f0c_0 +fsqrt_d 001100 ..... ..... 100 01 ...... ..... @f0c_0 +frnd_d 001100 ..... ..... 101 01 ...... ..... @f0c_0 +fneg_d 001100 ..... ..... 110 01 ...... ..... @f0c_0 +fnegabs_d 001100 ..... ..... 111 01 ...... ..... @f0c_0 + +fcpy_f 001110 ..... ..... 010 ........ ..... @f0e_f_0 +fabs_f 001110 ..... ..... 011 ........ ..... @f0e_f_0 +fsqrt_f 001110 ..... ..... 100 ........ ..... @f0e_f_0 +frnd_f 001110 ..... ..... 101 ........ ..... @f0e_f_0 +fneg_f 001110 ..... ..... 110 ........ ..... @f0e_f_0 +fnegabs_f 001110 ..... ..... 111 ........ ..... @f0e_f_0 + +fcpy_d 001110 ..... ..... 010 ........ ..... @f0e_d_0 +fabs_d 001110 ..... ..... 011 ........ ..... @f0e_d_0 +fsqrt_d 001110 ..... ..... 100 ........ ..... @f0e_d_0 +frnd_d 001110 ..... ..... 101 ........ ..... @f0e_d_0 +fneg_d 001110 ..... ..... 110 ........ ..... @f0e_d_0 +fnegabs_d 001110 ..... ..... 111 ........ ..... @f0e_d_0 + +# Floating point class 1 + +# float/float +fcnv_d_f 001100 ..... ... 000 00 01 ...... ..... @f0c_1 +fcnv_f_d 001100 ..... ... 000 01 00 ...... ..... @f0c_1 + +fcnv_d_f 001110 ..... ... 000 .......... ..... @f0e_df_1 +fcnv_f_d 001110 ..... ... 000 .......... ..... @f0e_fd_1 + +# int/float +fcnv_w_f 001100 ..... ... 001 00 00 ...... ..... @f0c_1 +fcnv_q_f 001100 ..... ... 001 00 01 ...... ..... @f0c_1 +fcnv_w_d 001100 ..... ... 001 01 00 ...... ..... @f0c_1 +fcnv_q_d 001100 ..... ... 001 01 01 ...... ..... @f0c_1 + +fcnv_w_f 001110 ..... ... 001 .......... ..... @f0e_ff_1 +fcnv_q_f 001110 ..... ... 001 .......... ..... @f0e_df_1 +fcnv_w_d 001110 ..... ... 001 .......... ..... @f0e_fd_1 +fcnv_q_d 001110 ..... ... 001 .......... ..... @f0e_dd_1 + +# float/int +fcnv_f_w 001100 ..... ... 010 00 00 ...... ..... @f0c_1 +fcnv_d_w 001100 ..... ... 010 00 01 ...... ..... @f0c_1 +fcnv_f_q 001100 ..... ... 010 01 00 ...... ..... @f0c_1 +fcnv_d_q 001100 ..... ... 010 01 01 ...... ..... @f0c_1 + +fcnv_f_w 001110 ..... ... 010 .......... ..... @f0e_ff_1 +fcnv_d_w 001110 ..... ... 010 .......... ..... @f0e_df_1 +fcnv_f_q 001110 ..... ... 010 .......... ..... @f0e_fd_1 +fcnv_d_q 001110 ..... ... 010 .......... ..... @f0e_dd_1 + +# float/int truncate +fcnv_t_f_w 001100 ..... ... 011 00 00 ...... ..... @f0c_1 +fcnv_t_d_w 001100 ..... ... 011 00 01 ...... ..... @f0c_1 +fcnv_t_f_q 001100 ..... ... 011 01 00 ...... ..... @f0c_1 +fcnv_t_d_q 001100 ..... ... 011 01 01 ...... ..... @f0c_1 + +fcnv_t_f_w 001110 ..... ... 011 .......... ..... @f0e_ff_1 +fcnv_t_d_w 001110 ..... ... 011 .......... ..... @f0e_df_1 +fcnv_t_f_q 001110 ..... ... 011 .......... ..... @f0e_fd_1 +fcnv_t_d_q 001110 ..... ... 011 .......... ..... @f0e_dd_1 + +# uint/float +fcnv_uw_f 001100 ..... ... 101 00 00 ...... ..... @f0c_1 +fcnv_uq_f 001100 ..... ... 101 00 01 ...... ..... @f0c_1 +fcnv_uw_d 001100 ..... ... 101 01 00 ...... ..... @f0c_1 +fcnv_uq_d 001100 ..... ... 101 01 01 ...... ..... @f0c_1 + +fcnv_uw_f 001110 ..... ... 101 .......... ..... @f0e_ff_1 +fcnv_uq_f 001110 ..... ... 101 .......... ..... @f0e_df_1 +fcnv_uw_d 001110 ..... ... 101 .......... ..... @f0e_fd_1 +fcnv_uq_d 001110 ..... ... 101 .......... ..... @f0e_dd_1 + +# float/int +fcnv_f_uw 001100 ..... ... 110 00 00 ...... ..... @f0c_1 +fcnv_d_uw 001100 ..... ... 110 00 01 ...... ..... @f0c_1 +fcnv_f_uq 001100 ..... ... 110 01 00 ...... ..... @f0c_1 +fcnv_d_uq 001100 ..... ... 110 01 01 ...... ..... @f0c_1 + +fcnv_f_uw 001110 ..... ... 110 .......... ..... @f0e_ff_1 +fcnv_d_uw 001110 ..... ... 110 .......... ..... @f0e_df_1 +fcnv_f_uq 001110 ..... ... 110 .......... ..... @f0e_fd_1 +fcnv_d_uq 001110 ..... ... 110 .......... ..... @f0e_dd_1 + +# float/int truncate +fcnv_t_f_uw 001100 ..... ... 111 00 00 ...... ..... @f0c_1 +fcnv_t_d_uw 001100 ..... ... 111 00 01 ...... ..... @f0c_1 +fcnv_t_f_uq 001100 ..... ... 111 01 00 ...... ..... @f0c_1 +fcnv_t_d_uq 001100 ..... ... 111 01 01 ...... ..... @f0c_1 + +fcnv_t_f_uw 001110 ..... ... 111 .......... ..... @f0e_ff_1 +fcnv_t_d_uw 001110 ..... ... 111 .......... ..... @f0e_df_1 +fcnv_t_f_uq 001110 ..... ... 111 .......... ..... @f0e_fd_1 +fcnv_t_d_uq 001110 ..... ... 111 .......... ..... @f0e_dd_1 + +# Floating point class 2 + +ftest 001100 00000 00000 y:3 00 10000 1 c:5 + +fcmp_f 001100 ..... ..... ... 00 ..... 0 ..... @f0c_2 +fcmp_d 001100 ..... ..... ... 01 ..... 0 ..... @f0c_2 + +fcmp_f 001110 ..... ..... ... ..... ... ..... @f0e_f_2 +fcmp_d 001110 ..... ..... ... ..... ... ..... @f0e_d_2 + +# Floating point class 3 + +fadd_f 001100 ..... ..... 000 00 ...... ..... @f0c_3 +fsub_f 001100 ..... ..... 001 00 ...... ..... @f0c_3 +fmpy_f 001100 ..... ..... 010 00 ...... ..... @f0c_3 +fdiv_f 001100 ..... ..... 011 00 ...... ..... @f0c_3 + +fadd_d 001100 ..... ..... 000 01 ...... ..... @f0c_3 +fsub_d 001100 ..... ..... 001 01 ...... ..... @f0c_3 +fmpy_d 001100 ..... ..... 010 01 ...... ..... @f0c_3 +fdiv_d 001100 ..... ..... 011 01 ...... ..... @f0c_3 + +fadd_f 001110 ..... ..... 000 ..... ... ..... @f0e_f_3 +fsub_f 001110 ..... ..... 001 ..... ... ..... @f0e_f_3 +fmpy_f 001110 ..... ..... 010 ..... ... ..... @f0e_f_3 +fdiv_f 001110 ..... ..... 011 ..... ... ..... @f0e_f_3 + +fadd_d 001110 ..... ..... 000 ..... ... ..... @f0e_d_3 +fsub_d 001110 ..... ..... 001 ..... ... ..... @f0e_d_3 +fmpy_d 001110 ..... ..... 010 ..... ... ..... @f0e_d_3 +fdiv_d 001110 ..... ..... 011 ..... ... ..... @f0e_d_3 + +xmpyu 001110 ..... ..... 010 .0111 .00 t:5 r1=%ra64 r2=%rb64 + +# diag +diag 000101 ----- ----- ---- ---- ---- ---- diff --git a/target/hppa/machine.c b/target/hppa/machine.c index 8e077788c3..a1bee9796f 100644 --- a/target/hppa/machine.c +++ b/target/hppa/machine.c @@ -46,7 +46,8 @@ VMSTATE_UINTTR_ARRAY_V(_f, _s, _n, 0) -static int get_psw(QEMUFile *f, void *opaque, size_t size, VMStateField *field) +static int get_psw(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) { CPUHPPAState *env = opaque; cpu_hppa_put_psw(env, qemu_get_betr(f)); @@ -54,7 +55,7 @@ static int get_psw(QEMUFile *f, void *opaque, size_t size, VMStateField *field) } static int put_psw(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { CPUHPPAState *env = opaque; qemu_put_betr(f, cpu_hppa_get_psw(env)); @@ -68,7 +69,8 @@ static const VMStateInfo vmstate_psw = { }; /* FIXME: Use the PA2.0 format, which is a superset of the PA1.1 format. */ -static int get_tlb(QEMUFile *f, void *opaque, size_t size, VMStateField *field) +static int get_tlb(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) { hppa_tlb_entry *ent = opaque; uint32_t val; @@ -94,7 +96,7 @@ static int get_tlb(QEMUFile *f, void *opaque, size_t size, VMStateField *field) } static int put_tlb(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { hppa_tlb_entry *ent = opaque; uint32_t val = 0; diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index ab160c2a74..c9b57d07c3 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -22,6 +22,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qom/cpu.h" +#include "trace.h" #ifdef CONFIG_USER_ONLY int hppa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, @@ -43,9 +44,12 @@ static hppa_tlb_entry *hppa_find_tlb(CPUHPPAState *env, vaddr addr) for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { hppa_tlb_entry *ent = &env->tlb[i]; if (ent->va_b <= addr && addr <= ent->va_e) { + trace_hppa_tlb_find_entry(env, ent + i, ent->entry_valid, + ent->va_b, ent->va_e, ent->pa); return ent; } } + trace_hppa_tlb_find_entry_not_found(env, addr); return NULL; } @@ -55,6 +59,8 @@ static void hppa_flush_tlb_ent(CPUHPPAState *env, hppa_tlb_entry *ent) unsigned i, n = 1 << (2 * ent->page_size); uint64_t addr = ent->va_b; + trace_hppa_tlb_flush_ent(env, ent, ent->va_b, ent->va_e, ent->pa); + for (i = 0; i < n; ++i, addr += TARGET_PAGE_SIZE) { /* Do not flush MMU_PHYS_IDX. */ tlb_flush_page_by_mmuidx(cs, addr, 0xf); @@ -96,9 +102,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, if (ent == NULL || !ent->entry_valid) { phys = 0; prot = 0; - /* ??? Unconditionally report data tlb miss, - even if this is an instruction fetch. */ - ret = EXCP_DTLB_MISS; + ret = (type == PAGE_EXEC) ? EXCP_ITLB_MISS : EXCP_DTLB_MISS; goto egress; } @@ -127,7 +131,20 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, break; } - /* ??? Check PSW_P and ent->access_prot. This can remove PAGE_WRITE. */ + /* access_id == 0 means public page and no check is performed */ + if ((env->psw & PSW_P) && ent->access_id) { + /* If bits [31:1] match, and bit 0 is set, suppress write. */ + int match = ent->access_id * 2 + 1; + + if (match == env->cr[CR_PID1] || match == env->cr[CR_PID2] || + match == env->cr[CR_PID3] || match == env->cr[CR_PID4]) { + prot &= PAGE_READ | PAGE_EXEC; + if (type == PAGE_WRITE) { + ret = EXCP_DMPI; + goto egress; + } + } + } /* No guest access type indicates a non-architectural access from within QEMU. Bypass checks for access, D, B and T bits. */ @@ -137,7 +154,8 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, if (unlikely(!(prot & type))) { /* The access isn't allowed -- Inst/Data Memory Protection Fault. */ - ret = (type & PAGE_EXEC ? EXCP_IMP : EXCP_DMP); + ret = (type & PAGE_EXEC ? EXCP_IMP : + prot & PAGE_READ ? EXCP_DMP : EXCP_DMAR); goto egress; } @@ -170,6 +188,7 @@ int hppa_get_physical_address(CPUHPPAState *env, vaddr addr, int mmu_idx, egress: *pphys = phys; *pprot = prot; + trace_hppa_tlb_get_physical_address(env, ret, prot, addr, phys); return ret; } @@ -199,6 +218,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, MMUAccessType type, int mmu_idx, uintptr_t retaddr) { HPPACPU *cpu = HPPA_CPU(cs); + CPUHPPAState *env = &cpu->env; int prot, excp, a_prot; hwaddr phys; @@ -214,9 +234,10 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, break; } - excp = hppa_get_physical_address(&cpu->env, addr, mmu_idx, + excp = hppa_get_physical_address(env, addr, mmu_idx, a_prot, &phys, &prot); if (unlikely(excp >= 0)) { + trace_hppa_tlb_fill_excp(env, addr, size, type, mmu_idx); /* Failure. Raise the indicated exception. */ cs->exception_index = excp; if (cpu->env.psw & PSW_Q) { @@ -227,6 +248,8 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, cpu_loop_exit_restore(cs, retaddr); } + trace_hppa_tlb_fill_success(env, addr & TARGET_PAGE_MASK, + phys & TARGET_PAGE_MASK, size, type, mmu_idx); /* Success! Store the translation into the QEMU TLB. */ tlb_set_page(cs, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, prot, mmu_idx, TARGET_PAGE_SIZE); @@ -241,11 +264,13 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) /* Zap any old entries covering ADDR; notice empty entries on the way. */ for (i = 0; i < ARRAY_SIZE(env->tlb); ++i) { hppa_tlb_entry *ent = &env->tlb[i]; - if (!ent->entry_valid) { - empty = ent; - } else if (ent->va_b <= addr && addr <= ent->va_e) { - hppa_flush_tlb_ent(env, ent); - empty = ent; + if (ent->va_b <= addr && addr <= ent->va_e) { + if (ent->entry_valid) { + hppa_flush_tlb_ent(env, ent); + } + if (!empty) { + empty = ent; + } } } @@ -258,6 +283,7 @@ void HELPER(itlba)(CPUHPPAState *env, target_ulong addr, target_ureg reg) empty->va_b = addr & TARGET_PAGE_MASK; empty->va_e = empty->va_b + TARGET_PAGE_SIZE - 1; empty->pa = extract32(reg, 5, 20) << TARGET_PAGE_BITS; + trace_hppa_tlb_itlba(env, empty, empty->va_b, empty->va_e, empty->pa); } /* Insert (Insn/Data) TLB Protection. Note this is PA 1.1 only. */ @@ -265,7 +291,7 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) { hppa_tlb_entry *ent = hppa_find_tlb(env, addr); - if (unlikely(ent == NULL || ent->entry_valid)) { + if (unlikely(ent == NULL)) { qemu_log_mask(LOG_GUEST_ERROR, "ITLBP not following ITLBA\n"); return; } @@ -279,6 +305,8 @@ void HELPER(itlbp)(CPUHPPAState *env, target_ulong addr, target_ureg reg) ent->d = extract32(reg, 28, 1); ent->t = extract32(reg, 29, 1); ent->entry_valid = 1; + trace_hppa_tlb_itlbp(env, ent, ent->access_id, ent->u, ent->ar_pl2, + ent->ar_pl1, ent->ar_type, ent->b, ent->d, ent->t); } /* Purge (Insn/Data) TLB. This is explicitly page-based, and is @@ -298,6 +326,7 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) { CPUState *src = CPU(hppa_env_get_cpu(env)); CPUState *cpu; + trace_hppa_tlb_ptlb(env); run_on_cpu_data data = RUN_ON_CPU_TARGET_PTR(addr); CPU_FOREACH(cpu) { @@ -313,11 +342,24 @@ void HELPER(ptlb)(CPUHPPAState *env, target_ulong addr) void HELPER(ptlbe)(CPUHPPAState *env) { CPUState *src = CPU(hppa_env_get_cpu(env)); - + trace_hppa_tlb_ptlbe(env); memset(env->tlb, 0, sizeof(env->tlb)); tlb_flush_by_mmuidx(src, 0xf); } +void cpu_hppa_change_prot_id(CPUHPPAState *env) +{ + if (env->psw & PSW_P) { + CPUState *src = CPU(hppa_env_get_cpu(env)); + tlb_flush_by_mmuidx(src, 0xf); + } +} + +void HELPER(change_prot_id)(CPUHPPAState *env) +{ + cpu_hppa_change_prot_id(env); +} + target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr) { hwaddr phys; @@ -334,8 +376,10 @@ target_ureg HELPER(lpa)(CPUHPPAState *env, target_ulong addr) if (excp == EXCP_DTLB_MISS) { excp = EXCP_NA_DTLB_MISS; } + trace_hppa_tlb_lpa_failed(env, addr); hppa_dynamic_excp(env, excp, GETPC()); } + trace_hppa_tlb_lpa_success(env, addr, phys); return phys; } diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 912e8d5be4..a55a5dfc02 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -25,6 +25,7 @@ #include "sysemu/sysemu.h" #include "qemu/timer.h" #include "fpu/softfloat.h" +#include "trace.h" void QEMU_NORETURN HELPER(excp)(CPUHPPAState *env, int excp) { @@ -81,10 +82,8 @@ static void atomic_store_3(CPUHPPAState *env, target_ulong addr, uint32_t val, } static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, - bool parallel) + bool parallel, uintptr_t ra) { - uintptr_t ra = GETPC(); - switch (addr & 3) { case 3: cpu_stb_data_ra(env, addr, val, ra); @@ -109,20 +108,18 @@ static void do_stby_b(CPUHPPAState *env, target_ulong addr, target_ureg val, void HELPER(stby_b)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_b(env, addr, val, false); + do_stby_b(env, addr, val, false, GETPC()); } void HELPER(stby_b_parallel)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_b(env, addr, val, true); + do_stby_b(env, addr, val, true, GETPC()); } static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, - bool parallel) + bool parallel, uintptr_t ra) { - uintptr_t ra = GETPC(); - switch (addr & 3) { case 3: /* The 3 byte store must appear atomic. */ @@ -151,13 +148,13 @@ static void do_stby_e(CPUHPPAState *env, target_ulong addr, target_ureg val, void HELPER(stby_e)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_e(env, addr, val, false); + do_stby_e(env, addr, val, false, GETPC()); } void HELPER(stby_e_parallel)(CPUHPPAState *env, target_ulong addr, target_ureg val) { - do_stby_e(env, addr, val, true); + do_stby_e(env, addr, val, true, GETPC()); } target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, @@ -169,6 +166,7 @@ target_ureg HELPER(probe)(CPUHPPAState *env, target_ulong addr, int prot, excp; hwaddr phys; + trace_hppa_tlb_probe(addr, level, want); /* Fail if the requested privilege level is higher than current. */ if (level < (env->iaoq_f & 3)) { return 0; @@ -665,22 +663,21 @@ void HELPER(reset)(CPUHPPAState *env) target_ureg HELPER(swap_system_mask)(CPUHPPAState *env, target_ureg nsm) { target_ulong psw = env->psw; - /* ??? On second reading this condition simply seems - to be undefined rather than a diagnosed trap. */ - if (nsm & ~psw & PSW_Q) { - hppa_dynamic_excp(env, EXCP_ILL, GETPC()); - } + /* + * Setting the PSW Q bit to 1, if it was not already 1, is an + * undefined operation. + * + * However, HP-UX 10.20 does this with the SSM instruction. + * Tested this on HP9000/712 and HP9000/785/C3750 and both + * machines set the Q bit from 0 to 1 without an exception, + * so let this go without comment. + */ env->psw = (psw & ~PSW_SM) | (nsm & PSW_SM); return psw & PSW_SM; } void HELPER(rfi)(CPUHPPAState *env) { - /* ??? On second reading this condition simply seems - to be undefined rather than a diagnosed trap. */ - if (env->psw & (PSW_I | PSW_R | PSW_Q)) { - helper_excp(env, EXCP_ILL); - } env->iasq_f = (uint64_t)env->cr[CR_IIASQ] << 32; env->iasq_b = (uint64_t)env->cr_back[0] << 32; env->iaoq_f = env->cr[CR_IIAOQ]; diff --git a/target/hppa/trace-events b/target/hppa/trace-events new file mode 100644 index 0000000000..0731ce7ce1 --- /dev/null +++ b/target/hppa/trace-events @@ -0,0 +1,18 @@ +# See docs/devel/tracing.txt for syntax documentation. + +# mem_helper.c +disable hppa_tlb_flush_ent(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx" +disable hppa_tlb_find_entry(void *env, void *ent, int valid, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p valid=%d va_b=0x%lx va_e=0x%lx pa=0x%lx" +disable hppa_tlb_find_entry_not_found(void *env, uint64_t addr) "env=%p addr=%08lx" +disable hppa_tlb_get_physical_address(void *env, int ret, int prot, uint64_t addr, uint64_t phys) "env=%p ret=%d prot=%d addr=0x%lx phys=0x%lx" +disable hppa_tlb_fill_excp(void *env, uint64_t addr, int size, int type, int mmu_idx) "env=%p addr=0x%lx size=%d type=%d mmu_idx=%d" +disable hppa_tlb_fill_success(void *env, uint64_t addr, uint64_t phys, int size, int type, int mmu_idx) "env=%p addr=0x%lx phys=0x%lx size=%d type=%d mmu_idx=%d" +disable hppa_tlb_itlba(void *env, void *ent, uint64_t va_b, uint64_t va_e, uint64_t pa) "env=%p ent=%p va_b=0x%lx va_e=0x%lx pa=0x%lx" +disable hppa_tlb_itlbp(void *env, void *ent, int access_id, int u, int pl2, int pl1, int type, int b, int d, int t) "env=%p ent=%p access_id=%x u=%d pl2=%d pl1=%d type=%d b=%d d=%d t=%d" +disable hppa_tlb_ptlb(void *env) "env=%p" +disable hppa_tlb_ptlbe(void *env) "env=%p" +disable hppa_tlb_lpa_success(void *env, uint64_t addr, uint64_t phys) "env=%p addr=0x%lx phys=0x%lx" +disable hppa_tlb_lpa_failed(void *env, uint64_t addr) "env=%p addr=0x%lx" + +# op_helper.c +disable hppa_tlb_probe(uint64_t addr, int level, int want) "addr=0x%lx level=%d want=%d" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index ce05d5619d..43b74367ea 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -278,9 +278,63 @@ typedef struct DisasContext { bool psw_n_nonzero; } DisasContext; -/* Target-specific return values from translate_one, indicating the - state of the TB. Note that DISAS_NEXT indicates that we are not - exiting the TB. */ +/* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ +static int expand_sm_imm(int val) +{ + if (val & PSW_SM_E) { + val = (val & ~PSW_SM_E) | PSW_E; + } + if (val & PSW_SM_W) { + val = (val & ~PSW_SM_W) | PSW_W; + } + return val; +} + +/* Inverted space register indicates 0 means sr0 not inferred from base. */ +static int expand_sr3x(int val) +{ + return ~val; +} + +/* Convert the M:A bits within a memory insn to the tri-state value + we use for the final M. */ +static int ma_to_m(int val) +{ + return val & 2 ? (val & 1 ? -1 : 1) : 0; +} + +/* Convert the sign of the displacement to a pre or post-modify. */ +static int pos_to_m(int val) +{ + return val ? 1 : -1; +} + +static int neg_to_m(int val) +{ + return val ? -1 : 1; +} + +/* Used for branch targets and fp memory ops. */ +static int expand_shl2(int val) +{ + return val << 2; +} + +/* Used for fp memory ops. */ +static int expand_shl3(int val) +{ + return val << 3; +} + +/* Used for assemble_21. */ +static int expand_shl11(int val) +{ + return val << 11; +} + + +/* Include the auto-generated decoder. */ +#include "decode.inc.c" /* We are not using a goto_tb (for whatever reason), but have updated the iaq (for whatever reason), so don't do it again on exit. */ @@ -293,21 +347,7 @@ typedef struct DisasContext { /* Similarly, but we want to return to the main loop immediately to recognize unmasked interrupts. */ #define DISAS_IAQ_N_STALE_EXIT DISAS_TARGET_2 - -typedef struct DisasInsn { - uint32_t insn, mask; - DisasJumpType (*trans)(DisasContext *ctx, uint32_t insn, - const struct DisasInsn *f); - union { - void (*ttt)(TCGv_reg, TCGv_reg, TCGv_reg); - void (*weww)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32); - void (*dedd)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64); - void (*wew)(TCGv_i32, TCGv_env, TCGv_i32); - void (*ded)(TCGv_i64, TCGv_env, TCGv_i64); - void (*wed)(TCGv_i32, TCGv_env, TCGv_i64); - void (*dew)(TCGv_i64, TCGv_env, TCGv_i32); - } f; -} DisasInsn; +#define DISAS_EXIT DISAS_TARGET_3 /* global register indexes */ static TCGv_reg cpu_gr[32]; @@ -393,6 +433,15 @@ static DisasCond cond_make_f(void) }; } +static DisasCond cond_make_t(void) +{ + return (DisasCond){ + .c = TCG_COND_ALWAYS, + .a0 = NULL, + .a1 = NULL, + }; +} + static DisasCond cond_make_n(void) { return (DisasCond){ @@ -404,15 +453,19 @@ static DisasCond cond_make_n(void) }; } +static DisasCond cond_make_0_tmp(TCGCond c, TCGv_reg a0) +{ + assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); + return (DisasCond){ + .c = c, .a0 = a0, .a1_is_0 = true + }; +} + static DisasCond cond_make_0(TCGCond c, TCGv_reg a0) { - DisasCond r = { .c = c, .a1 = NULL, .a1_is_0 = true }; - - assert (c != TCG_COND_NEVER && c != TCG_COND_ALWAYS); - r.a0 = tcg_temp_new(); - tcg_gen_mov_reg(r.a0, a0); - - return r; + TCGv_reg tmp = tcg_temp_new(); + tcg_gen_mov_reg(tmp, a0); + return cond_make_0_tmp(c, tmp); } static DisasCond cond_make(TCGCond c, TCGv_reg a0, TCGv_reg a1) @@ -665,10 +718,12 @@ static void nullify_set(DisasContext *ctx, bool x) } /* Mark the end of an instruction that may have been nullified. - This is the pair to nullify_over. */ -static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status) + This is the pair to nullify_over. Always returns true so that + it may be tail-called from a translate function. */ +static bool nullify_end(DisasContext *ctx) { TCGLabel *null_lab = ctx->null_lab; + DisasJumpType status = ctx->base.is_jmp; /* For NEXT, NORETURN, STALE, we can easily continue (or exit). For UPDATED, we cannot update on the nullified path. */ @@ -678,7 +733,7 @@ static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status) /* The current insn wasn't conditional or handled the condition applied to it without a branch, so the (new) setting of NULL_COND can be applied directly to the next insn. */ - return status; + return true; } ctx->null_lab = NULL; @@ -696,9 +751,9 @@ static DisasJumpType nullify_end(DisasContext *ctx, DisasJumpType status) ctx->null_cond = cond_make_n(); } if (status == DISAS_NORETURN) { - status = DISAS_NEXT; + ctx->base.is_jmp = DISAS_NEXT; } - return status; + return true; } static void copy_iaoq_entry(TCGv_reg dest, target_ureg ival, TCGv_reg vval) @@ -722,44 +777,50 @@ static void gen_excp_1(int exception) tcg_temp_free_i32(t); } -static DisasJumpType gen_excp(DisasContext *ctx, int exception) +static void gen_excp(DisasContext *ctx, int exception) { copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_f, cpu_iaoq_f); copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_b, cpu_iaoq_b); nullify_save(ctx); gen_excp_1(exception); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } -static DisasJumpType gen_excp_iir(DisasContext *ctx, int exc) +static bool gen_excp_iir(DisasContext *ctx, int exc) { - TCGv_reg tmp = tcg_const_reg(ctx->insn); + TCGv_reg tmp; + + nullify_over(ctx); + tmp = tcg_const_reg(ctx->insn); tcg_gen_st_reg(tmp, cpu_env, offsetof(CPUHPPAState, cr[CR_IIR])); tcg_temp_free(tmp); - return gen_excp(ctx, exc); + gen_excp(ctx, exc); + return nullify_end(ctx); } -static DisasJumpType gen_illegal(DisasContext *ctx) +static bool gen_illegal(DisasContext *ctx) { - nullify_over(ctx); - return nullify_end(ctx, gen_excp_iir(ctx, EXCP_ILL)); + return gen_excp_iir(ctx, EXCP_ILL); } -#define CHECK_MOST_PRIVILEGED(EXCP) \ - do { \ - if (ctx->privilege != 0) { \ - nullify_over(ctx); \ - return nullify_end(ctx, gen_excp_iir(ctx, EXCP)); \ - } \ +#ifdef CONFIG_USER_ONLY +#define CHECK_MOST_PRIVILEGED(EXCP) \ + return gen_excp_iir(ctx, EXCP) +#else +#define CHECK_MOST_PRIVILEGED(EXCP) \ + do { \ + if (ctx->privilege != 0) { \ + return gen_excp_iir(ctx, EXCP); \ + } \ } while (0) +#endif static bool use_goto_tb(DisasContext *ctx, target_ureg dest) { - /* Suppress goto_tb in the case of single-steping and IO. */ - if ((tb_cflags(ctx->base.tb) & CF_LAST_IO) || ctx->base.singlestep_enabled) { - return false; - } - return true; + /* Suppress goto_tb for page crossing, IO, or single-steping. */ + return !(((ctx->base.pc_first ^ dest) & TARGET_PAGE_MASK) + || (tb_cflags(ctx->base.tb) & CF_LAST_IO) + || ctx->base.singlestep_enabled); } /* If the next insn is to be nullified, and it's on the same page, @@ -791,111 +852,20 @@ static void gen_goto_tb(DisasContext *ctx, int which, } } -/* PA has a habit of taking the LSB of a field and using that as the sign, - with the rest of the field becoming the least significant bits. */ -static target_sreg low_sextract(uint32_t val, int pos, int len) +static bool cond_need_sv(int c) { - target_ureg x = -(target_ureg)extract32(val, pos, 1); - x = (x << (len - 1)) | extract32(val, pos + 1, len - 1); - return x; + return c == 2 || c == 3 || c == 6; } -static unsigned assemble_rt64(uint32_t insn) +static bool cond_need_cb(int c) { - unsigned r1 = extract32(insn, 6, 1); - unsigned r0 = extract32(insn, 0, 5); - return r1 * 32 + r0; + return c == 4 || c == 5; } -static unsigned assemble_ra64(uint32_t insn) -{ - unsigned r1 = extract32(insn, 7, 1); - unsigned r0 = extract32(insn, 21, 5); - return r1 * 32 + r0; -} - -static unsigned assemble_rb64(uint32_t insn) -{ - unsigned r1 = extract32(insn, 12, 1); - unsigned r0 = extract32(insn, 16, 5); - return r1 * 32 + r0; -} - -static unsigned assemble_rc64(uint32_t insn) -{ - unsigned r2 = extract32(insn, 8, 1); - unsigned r1 = extract32(insn, 13, 3); - unsigned r0 = extract32(insn, 9, 2); - return r2 * 32 + r1 * 4 + r0; -} - -static unsigned assemble_sr3(uint32_t insn) -{ - unsigned s2 = extract32(insn, 13, 1); - unsigned s0 = extract32(insn, 14, 2); - return s2 * 4 + s0; -} - -static target_sreg assemble_12(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 1) | extract32(insn, 2, 1); - x = (x << 10) | extract32(insn, 3, 10); - return x; -} - -static target_sreg assemble_16(uint32_t insn) -{ - /* Take the name from PA2.0, which produces a 16-bit number - only with wide mode; otherwise a 14-bit number. Since we don't - implement wide mode, this is always the 14-bit number. */ - return low_sextract(insn, 0, 14); -} - -static target_sreg assemble_16a(uint32_t insn) -{ - /* Take the name from PA2.0, which produces a 14-bit shifted number - only with wide mode; otherwise a 12-bit shifted number. Since we - don't implement wide mode, this is always the 12-bit number. */ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 11) | extract32(insn, 2, 11); - return x << 2; -} - -static target_sreg assemble_17(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 5) | extract32(insn, 16, 5); - x = (x << 1) | extract32(insn, 2, 1); - x = (x << 10) | extract32(insn, 3, 10); - return x << 2; -} - -static target_sreg assemble_21(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 11) | extract32(insn, 1, 11); - x = (x << 2) | extract32(insn, 14, 2); - x = (x << 5) | extract32(insn, 16, 5); - x = (x << 2) | extract32(insn, 12, 2); - return x << 11; -} - -static target_sreg assemble_22(uint32_t insn) -{ - target_ureg x = -(target_ureg)(insn & 1); - x = (x << 10) | extract32(insn, 16, 10); - x = (x << 1) | extract32(insn, 2, 1); - x = (x << 10) | extract32(insn, 3, 10); - return x << 2; -} - -/* The parisc documentation describes only the general interpretation of - the conditions, without describing their exact implementation. The - interpretations do not stand up well when considering ADD,C and SUB,B. - However, considering the Addition, Subtraction and Logical conditions - as a whole it would appear that these relations are similar to what - a traditional NZCV set of flags would produce. */ +/* + * Compute conditional for arithmetic. See Page 5-3, Table 5-1, of + * the Parisc 1.1 Architecture Reference Manual for details. + */ static DisasCond do_cond(unsigned cf, TCGv_reg res, TCGv_reg cb_msb, TCGv_reg sv) @@ -904,17 +874,32 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, TCGv_reg tmp; switch (cf >> 1) { - case 0: /* Never / TR */ + case 0: /* Never / TR (0 / 1) */ cond = cond_make_f(); break; case 1: /* = / <> (Z / !Z) */ cond = cond_make_0(TCG_COND_EQ, res); break; - case 2: /* < / >= (N / !N) */ - cond = cond_make_0(TCG_COND_LT, res); + case 2: /* < / >= (N ^ V / !(N ^ V) */ + tmp = tcg_temp_new(); + tcg_gen_xor_reg(tmp, res, sv); + cond = cond_make_0_tmp(TCG_COND_LT, tmp); break; - case 3: /* <= / > (N | Z / !N & !Z) */ - cond = cond_make_0(TCG_COND_LE, res); + case 3: /* <= / > (N ^ V) | Z / !((N ^ V) | Z) */ + /* + * Simplify: + * (N ^ V) | Z + * ((res < 0) ^ (sv < 0)) | !res + * ((res ^ sv) < 0) | !res + * (~(res ^ sv) >= 0) | !res + * !(~(res ^ sv) >> 31) | !res + * !(~(res ^ sv) >> 31 & res) + */ + tmp = tcg_temp_new(); + tcg_gen_eqv_reg(tmp, res, sv); + tcg_gen_sari_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); + tcg_gen_and_reg(tmp, tmp, res); + cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 4: /* NUV / UV (!C / C) */ cond = cond_make_0(TCG_COND_EQ, cb_msb); @@ -923,8 +908,7 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, tmp = tcg_temp_new(); tcg_gen_neg_reg(tmp, cb_msb); tcg_gen_and_reg(tmp, tmp, res); - cond = cond_make_0(TCG_COND_EQ, tmp); - tcg_temp_free(tmp); + cond = cond_make_0_tmp(TCG_COND_EQ, tmp); break; case 6: /* SV / NSV (V / !V) */ cond = cond_make_0(TCG_COND_LT, sv); @@ -932,8 +916,7 @@ static DisasCond do_cond(unsigned cf, TCGv_reg res, case 7: /* OD / EV */ tmp = tcg_temp_new(); tcg_gen_andi_reg(tmp, res, 1); - cond = cond_make_0(TCG_COND_NE, tmp); - tcg_temp_free(tmp); + cond = cond_make_0_tmp(TCG_COND_NE, tmp); break; default: g_assert_not_reached(); @@ -971,7 +954,7 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, cond = cond_make(TCG_COND_LEU, in1, in2); break; default: - return do_cond(cf, res, sv, sv); + return do_cond(cf, res, NULL, sv); } if (cf & 1) { cond.c = tcg_invert_cond(cond.c); @@ -980,17 +963,50 @@ static DisasCond do_sub_cond(unsigned cf, TCGv_reg res, return cond; } -/* Similar, but for logicals, where the carry and overflow bits are not - computed, and use of them is undefined. */ +/* + * Similar, but for logicals, where the carry and overflow bits are not + * computed, and use of them is undefined. + * + * Undefined or not, hardware does not trap. It seems reasonable to + * assume hardware treats cases c={4,5,6} as if C=0 & V=0, since that's + * how cases c={2,3} are treated. + */ static DisasCond do_log_cond(unsigned cf, TCGv_reg res) { - switch (cf >> 1) { - case 4: case 5: case 6: - cf &= 1; - break; + switch (cf) { + case 0: /* never */ + case 9: /* undef, C */ + case 11: /* undef, C & !Z */ + case 12: /* undef, V */ + return cond_make_f(); + + case 1: /* true */ + case 8: /* undef, !C */ + case 10: /* undef, !C | Z */ + case 13: /* undef, !V */ + return cond_make_t(); + + case 2: /* == */ + return cond_make_0(TCG_COND_EQ, res); + case 3: /* <> */ + return cond_make_0(TCG_COND_NE, res); + case 4: /* < */ + return cond_make_0(TCG_COND_LT, res); + case 5: /* >= */ + return cond_make_0(TCG_COND_GE, res); + case 6: /* <= */ + return cond_make_0(TCG_COND_LE, res); + case 7: /* > */ + return cond_make_0(TCG_COND_GT, res); + + case 14: /* OD */ + case 15: /* EV */ + return do_cond(cf, res, NULL, NULL); + + default: + g_assert_not_reached(); } - return do_cond(cf, res, res, res); } /* Similar, but for shift/extract/deposit conditions. */ @@ -1119,9 +1135,9 @@ static TCGv_reg do_sub_sv(DisasContext *ctx, TCGv_reg res, return sv; } -static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned shift, bool is_l, - bool is_tsv, bool is_tc, bool is_c, unsigned cf) +static void do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned shift, bool is_l, + bool is_tsv, bool is_tc, bool is_c, unsigned cf) { TCGv_reg dest, cb, cb_msb, sv, tmp; unsigned c = cf >> 1; @@ -1137,7 +1153,7 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, in1 = tmp; } - if (!is_l || c == 4 || c == 5) { + if (!is_l || cond_need_cb(c)) { TCGv_reg zero = tcg_const_reg(0); cb_msb = get_temp(ctx); tcg_gen_add2_reg(dest, cb_msb, in1, zero, in2, zero); @@ -1159,7 +1175,7 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute signed overflow if required. */ sv = NULL; - if (is_tsv || c == 6) { + if (is_tsv || cond_need_sv(c)) { sv = do_add_sv(ctx, dest, in1, in2); if (is_tsv) { /* ??? Need to include overflow from shift. */ @@ -1188,12 +1204,39 @@ static DisasJumpType do_add(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; - return DISAS_NEXT; } -static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, bool is_tsv, bool is_b, - bool is_tc, unsigned cf) +static bool do_add_reg(DisasContext *ctx, arg_rrr_cf_sh *a, + bool is_l, bool is_tsv, bool is_tc, bool is_c) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_add(ctx, a->t, tcg_r1, tcg_r2, a->sh, is_l, is_tsv, is_tc, is_c, a->cf); + return nullify_end(ctx); +} + +static bool do_add_imm(DisasContext *ctx, arg_rri_cf *a, + bool is_tsv, bool is_tc) +{ + TCGv_reg tcg_im, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_im = load_const(ctx, a->i); + tcg_r2 = load_gpr(ctx, a->r); + do_add(ctx, a->t, tcg_im, tcg_r2, 0, 0, is_tsv, is_tc, 0, a->cf); + return nullify_end(ctx); +} + +static void do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, bool is_tsv, bool is_b, + bool is_tc, unsigned cf) { TCGv_reg dest, sv, cb, cb_msb, zero, tmp; unsigned c = cf >> 1; @@ -1223,7 +1266,7 @@ static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute signed overflow if required. */ sv = NULL; - if (is_tsv || c == 6) { + if (is_tsv || cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); if (is_tsv) { gen_helper_tsv(cpu_env, sv); @@ -1255,11 +1298,37 @@ static DisasJumpType do_sub(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; - return DISAS_NEXT; } -static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf) +static bool do_sub_reg(DisasContext *ctx, arg_rrr_cf *a, + bool is_tsv, bool is_b, bool is_tc) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_sub(ctx, a->t, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, a->cf); + return nullify_end(ctx); +} + +static bool do_sub_imm(DisasContext *ctx, arg_rri_cf *a, bool is_tsv) +{ + TCGv_reg tcg_im, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_im = load_const(ctx, a->i); + tcg_r2 = load_gpr(ctx, a->r); + do_sub(ctx, a->t, tcg_im, tcg_r2, is_tsv, 0, 0, a->cf); + return nullify_end(ctx); +} + +static void do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned cf) { TCGv_reg dest, sv; DisasCond cond; @@ -1269,7 +1338,7 @@ static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Compute signed overflow if required. */ sv = NULL; - if ((cf >> 1) == 6) { + if (cond_need_sv(cf >> 1)) { sv = do_sub_sv(ctx, dest, in1, in2); } @@ -1284,12 +1353,11 @@ static DisasJumpType do_cmpclr(DisasContext *ctx, unsigned rt, TCGv_reg in1, /* Install the new nullification. */ cond_free(&ctx->null_cond); ctx->null_cond = cond; - return DISAS_NEXT; } -static DisasJumpType do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static void do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned cf, + void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest = dest_gpr(ctx, rt); @@ -1302,12 +1370,25 @@ static DisasJumpType do_log(DisasContext *ctx, unsigned rt, TCGv_reg in1, if (cf) { ctx->null_cond = do_log_cond(cf, dest); } - return DISAS_NEXT; } -static DisasJumpType do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, - TCGv_reg in2, unsigned cf, bool is_tc, - void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +static bool do_log_reg(DisasContext *ctx, arg_rrr_cf *a, + void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { + nullify_over(ctx); + } + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_log(ctx, a->t, tcg_r1, tcg_r2, a->cf, fn); + return nullify_end(ctx); +} + +static void do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, + TCGv_reg in2, unsigned cf, bool is_tc, + void (*fn)(TCGv_reg, TCGv_reg, TCGv_reg)) { TCGv_reg dest; DisasCond cond; @@ -1335,7 +1416,6 @@ static DisasJumpType do_unit(DisasContext *ctx, unsigned rt, TCGv_reg in1, cond_free(&ctx->null_cond); ctx->null_cond = cond; } - return DISAS_NEXT; } #ifndef CONFIG_USER_ONLY @@ -1498,9 +1578,9 @@ static void do_store_64(DisasContext *ctx, TCGv_i64 src, unsigned rb, #define do_store_reg do_store_32 #endif -static DisasJumpType do_load(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify, TCGMemOp mop) +static bool do_load(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify, TCGMemOp mop) { TCGv_reg dest; @@ -1516,12 +1596,12 @@ static DisasJumpType do_load(DisasContext *ctx, unsigned rt, unsigned rb, do_load_reg(ctx, dest, rb, rx, scale, disp, sp, modify, mop); save_gpr(ctx, rt, dest); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i32 tmp; @@ -1536,12 +1616,18 @@ static DisasJumpType do_floadw(DisasContext *ctx, unsigned rt, unsigned rb, gen_helper_loaded_fr0(cpu_env); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool trans_fldw(DisasContext *ctx, arg_ldst *a) +{ + return do_floadw(ctx, a->t, a->b, a->x, a->scale ? 2 : 0, + a->disp, a->sp, a->m); +} + +static bool do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i64 tmp; @@ -1556,21 +1642,27 @@ static DisasJumpType do_floadd(DisasContext *ctx, unsigned rt, unsigned rb, gen_helper_loaded_fr0(cpu_env); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_store(DisasContext *ctx, unsigned rt, unsigned rb, - target_sreg disp, unsigned sp, - int modify, TCGMemOp mop) +static bool trans_fldd(DisasContext *ctx, arg_ldst *a) +{ + return do_floadd(ctx, a->t, a->b, a->x, a->scale ? 3 : 0, + a->disp, a->sp, a->m); +} + +static bool do_store(DisasContext *ctx, unsigned rt, unsigned rb, + target_sreg disp, unsigned sp, + int modify, TCGMemOp mop) { nullify_over(ctx); do_store_reg(ctx, load_gpr(ctx, rt), rb, 0, 0, disp, sp, modify, mop); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i32 tmp; @@ -1580,12 +1672,18 @@ static DisasJumpType do_fstorew(DisasContext *ctx, unsigned rt, unsigned rb, do_store_32(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEUL); tcg_temp_free_i32(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, - unsigned rx, int scale, target_sreg disp, - unsigned sp, int modify) +static bool trans_fstw(DisasContext *ctx, arg_ldst *a) +{ + return do_fstorew(ctx, a->t, a->b, a->x, a->scale ? 2 : 0, + a->disp, a->sp, a->m); +} + +static bool do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, + unsigned rx, int scale, target_sreg disp, + unsigned sp, int modify) { TCGv_i64 tmp; @@ -1595,11 +1693,17 @@ static DisasJumpType do_fstored(DisasContext *ctx, unsigned rt, unsigned rb, do_store_64(ctx, tmp, rb, rx, scale, disp, sp, modify, MO_TEQ); tcg_temp_free_i64(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) +static bool trans_fstd(DisasContext *ctx, arg_ldst *a) +{ + return do_fstored(ctx, a->t, a->b, a->x, a->scale ? 3 : 0, + a->disp, a->sp, a->m); +} + +static bool do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32)) { TCGv_i32 tmp; @@ -1610,11 +1714,11 @@ static DisasJumpType do_fop_wew(DisasContext *ctx, unsigned rt, unsigned ra, save_frw_i32(rt, tmp); tcg_temp_free_i32(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) +static bool do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i32, TCGv_env, TCGv_i64)) { TCGv_i32 dst; TCGv_i64 src; @@ -1628,11 +1732,11 @@ static DisasJumpType do_fop_wed(DisasContext *ctx, unsigned rt, unsigned ra, tcg_temp_free_i64(src); save_frw_i32(rt, dst); tcg_temp_free_i32(dst); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) +static bool do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64)) { TCGv_i64 tmp; @@ -1643,11 +1747,11 @@ static DisasJumpType do_fop_ded(DisasContext *ctx, unsigned rt, unsigned ra, save_frd(rt, tmp); tcg_temp_free_i64(tmp); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, - void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) +static bool do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, + void (*func)(TCGv_i64, TCGv_env, TCGv_i32)) { TCGv_i32 src; TCGv_i64 dst; @@ -1661,13 +1765,12 @@ static DisasJumpType do_fop_dew(DisasContext *ctx, unsigned rt, unsigned ra, tcg_temp_free_i32(src); save_frd(rt, dst); tcg_temp_free_i64(dst); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_weww(DisasContext *ctx, unsigned rt, - unsigned ra, unsigned rb, - void (*func)(TCGv_i32, TCGv_env, - TCGv_i32, TCGv_i32)) +static bool do_fop_weww(DisasContext *ctx, unsigned rt, + unsigned ra, unsigned rb, + void (*func)(TCGv_i32, TCGv_env, TCGv_i32, TCGv_i32)) { TCGv_i32 a, b; @@ -1680,13 +1783,12 @@ static DisasJumpType do_fop_weww(DisasContext *ctx, unsigned rt, tcg_temp_free_i32(b); save_frw_i32(rt, a); tcg_temp_free_i32(a); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType do_fop_dedd(DisasContext *ctx, unsigned rt, - unsigned ra, unsigned rb, - void (*func)(TCGv_i64, TCGv_env, - TCGv_i64, TCGv_i64)) +static bool do_fop_dedd(DisasContext *ctx, unsigned rt, + unsigned ra, unsigned rb, + void (*func)(TCGv_i64, TCGv_env, TCGv_i64, TCGv_i64)) { TCGv_i64 a, b; @@ -1699,13 +1801,13 @@ static DisasJumpType do_fop_dedd(DisasContext *ctx, unsigned rt, tcg_temp_free_i64(b); save_frd(rt, a); tcg_temp_free_i64(a); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } /* Emit an unconditional branch to a direct target, which may or may not have already had nullification handled. */ -static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest, - unsigned link, bool is_n) +static bool do_dbranch(DisasContext *ctx, target_ureg dest, + unsigned link, bool is_n) { if (ctx->null_cond.c == TCG_COND_NEVER && ctx->null_lab == NULL) { if (link != 0) { @@ -1715,7 +1817,6 @@ static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest, if (is_n) { ctx->null_cond.c = TCG_COND_ALWAYS; } - return DISAS_NEXT; } else { nullify_over(ctx); @@ -1731,18 +1832,19 @@ static DisasJumpType do_dbranch(DisasContext *ctx, target_ureg dest, gen_goto_tb(ctx, 0, ctx->iaoq_b, dest); } - nullify_end(ctx, DISAS_NEXT); + nullify_end(ctx); nullify_set(ctx, 0); gen_goto_tb(ctx, 1, ctx->iaoq_b, ctx->iaoq_n); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } + return true; } /* Emit a conditional branch to a direct target. If the branch itself is nullified, we should have already used nullify_over. */ -static DisasJumpType do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, - DisasCond *cond) +static bool do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, + DisasCond *cond) { target_ureg dest = iaoq_dest(ctx, disp); TCGLabel *taken = NULL; @@ -1799,16 +1901,17 @@ static DisasJumpType do_cbranch(DisasContext *ctx, target_sreg disp, bool is_n, if (ctx->null_lab) { gen_set_label(ctx->null_lab); ctx->null_lab = NULL; - return DISAS_IAQ_N_STALE; + ctx->base.is_jmp = DISAS_IAQ_N_STALE; } else { - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; } + return true; } /* Emit an unconditional branch to an indirect target. This handles nullification of the branch itself. */ -static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, - unsigned link, bool is_n) +static bool do_ibranch(DisasContext *ctx, TCGv_reg dest, + unsigned link, bool is_n) { TCGv_reg a0, a1, next, tmp; TCGCond c; @@ -1826,7 +1929,8 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, tcg_gen_mov_reg(cpu_iaoq_f, next); tcg_gen_addi_reg(cpu_iaoq_b, next, 4); nullify_set(ctx, 0); - return DISAS_IAQ_N_UPDATED; + ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + return true; } ctx->null_cond.c = TCG_COND_ALWAYS; } @@ -1853,7 +1957,7 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, tcg_gen_movi_reg(cpu_gr[link], ctx->iaoq_n); } tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } else { cond_prep(&ctx->null_cond); c = ctx->null_cond.c; @@ -1884,8 +1988,7 @@ static DisasJumpType do_ibranch(DisasContext *ctx, TCGv_reg dest, cond_free(&ctx->null_cond); } } - - return DISAS_NEXT; + return true; } /* Implement @@ -1903,16 +2006,15 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) /* Privilege 0 is maximum and is allowed to decrease. */ return offset; case 3: - /* Privilege 3 is minimum and is never allowed increase. */ + /* Privilege 3 is minimum and is never allowed to increase. */ dest = get_temp(ctx); tcg_gen_ori_reg(dest, offset, 3); break; default: - dest = tcg_temp_new(); + dest = get_temp(ctx); tcg_gen_andi_reg(dest, offset, -4); tcg_gen_ori_reg(dest, dest, ctx->privilege); tcg_gen_movcond_reg(TCG_COND_GTU, dest, dest, offset, dest, offset); - tcg_temp_free(dest); break; } return dest; @@ -1926,7 +2028,7 @@ static TCGv_reg do_ibranch_priv(DisasContext *ctx, TCGv_reg offset) in than the "be disp(sr2,r0)" instruction that probably sent us here, is the easiest way to handle the branch delay slot on the aforementioned BE. */ -static DisasJumpType do_page_zero(DisasContext *ctx) +static void do_page_zero(DisasContext *ctx) { /* If by some means we get here with PSW[N]=1, that implies that the B,GATE instruction would be skipped, and we'd fault on the @@ -1954,71 +2056,70 @@ static DisasJumpType do_page_zero(DisasContext *ctx) switch (ctx->iaoq_f & -4) { case 0x00: /* Null pointer call */ gen_excp_1(EXCP_IMP); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; case 0xb0: /* LWS */ gen_excp_1(EXCP_SYSCALL_LWS); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; case 0xe0: /* SET_THREAD_POINTER */ tcg_gen_st_reg(cpu_gr[26], cpu_env, offsetof(CPUHPPAState, cr[27])); tcg_gen_ori_reg(cpu_iaoq_f, cpu_gr[31], 3); tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); - return DISAS_IAQ_N_UPDATED; + ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; + break; case 0x100: /* SYSCALL */ gen_excp_1(EXCP_SYSCALL); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; default: do_sigill: gen_excp_1(EXCP_ILL); - return DISAS_NORETURN; + ctx->base.is_jmp = DISAS_NORETURN; + break; } } #endif -static DisasJumpType trans_nop(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_nop(DisasContext *ctx, arg_nop *a) { cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_break(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_break(DisasContext *ctx, arg_break *a) { - nullify_over(ctx); - return nullify_end(ctx, gen_excp_iir(ctx, EXCP_BREAK)); + return gen_excp_iir(ctx, EXCP_BREAK); } -static DisasJumpType trans_sync(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_sync(DisasContext *ctx, arg_sync *a) { /* No point in nullifying the memory barrier. */ tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mfia(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mfia(DisasContext *ctx, arg_mfia *a) { - unsigned rt = extract32(insn, 0, 5); + unsigned rt = a->t; TCGv_reg tmp = dest_gpr(ctx, rt); tcg_gen_movi_reg(tmp, ctx->iaoq_f); save_gpr(ctx, rt, tmp); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mfsp(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mfsp(DisasContext *ctx, arg_mfsp *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned rs = assemble_sr3(insn); + unsigned rt = a->t; + unsigned rs = a->sp; TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_reg t1 = tcg_temp_new(); @@ -2031,21 +2132,19 @@ static DisasJumpType trans_mfsp(DisasContext *ctx, uint32_t insn, tcg_temp_free_i64(t0); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mfctl(DisasContext *ctx, arg_mfctl *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned ctl = extract32(insn, 21, 5); + unsigned rt = a->t; + unsigned ctl = a->r; TCGv_reg tmp; - DisasJumpType ret; switch (ctl) { case CR_SAR: #ifdef TARGET_HPPA64 - if (extract32(insn, 14, 1) == 0) { + if (a->e == 0) { /* MFSAR without ,W masks low 5 bits. */ tmp = dest_gpr(ctx, rt); tcg_gen_andi_reg(tmp, cpu_sar, 31); @@ -2059,17 +2158,16 @@ static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn, /* FIXME: Respect PSW_S bit. */ nullify_over(ctx); tmp = dest_gpr(ctx, rt); - if (ctx->base.tb->cflags & CF_USE_ICOUNT) { + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); gen_helper_read_interval_timer(tmp); gen_io_end(); - ret = DISAS_IAQ_N_STALE; + ctx->base.is_jmp = DISAS_IAQ_N_STALE; } else { gen_helper_read_interval_timer(tmp); - ret = DISAS_NEXT; } save_gpr(ctx, rt, tmp); - return nullify_end(ctx, ret); + return nullify_end(ctx); case 26: case 27: break; @@ -2085,14 +2183,13 @@ static DisasJumpType trans_mfctl(DisasContext *ctx, uint32_t insn, done: cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_mtsp(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtsp(DisasContext *ctx, arg_mtsp *a) { - unsigned rr = extract32(insn, 16, 5); - unsigned rs = assemble_sr3(insn); + unsigned rr = a->r; + unsigned rs = a->sp; TCGv_i64 t64; if (rs >= 5) { @@ -2112,15 +2209,13 @@ static DisasJumpType trans_mtsp(DisasContext *ctx, uint32_t insn, } tcg_temp_free_i64(t64); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtctl(DisasContext *ctx, arg_mtctl *a) { - unsigned rin = extract32(insn, 16, 5); - unsigned ctl = extract32(insn, 21, 5); - TCGv_reg reg = load_gpr(ctx, rin); + unsigned ctl = a->t; + TCGv_reg reg = load_gpr(ctx, a->r); TCGv_reg tmp; if (ctl == CR_SAR) { @@ -2130,17 +2225,13 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, tcg_temp_free(tmp); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } /* All other control registers are privileged or read-only. */ CHECK_MOST_PRIVILEGED(EXCP_PRIV_REG); -#ifdef CONFIG_USER_ONLY - g_assert_not_reached(); -#else - DisasJumpType ret = DISAS_NEXT; - +#ifndef CONFIG_USER_ONLY nullify_over(ctx); switch (ctl) { case CR_IT: @@ -2151,7 +2242,7 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, break; case CR_EIEM: gen_helper_write_eiem(cpu_env, reg); - ret = DISAS_IAQ_N_STALE_EXIT; + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; break; case CR_IIASQ: @@ -2166,339 +2257,294 @@ static DisasJumpType trans_mtctl(DisasContext *ctx, uint32_t insn, offsetof(CPUHPPAState, cr_back[ctl - CR_IIASQ])); break; + case CR_PID1: + case CR_PID2: + case CR_PID3: + case CR_PID4: + tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl])); +#ifndef CONFIG_USER_ONLY + gen_helper_change_prot_id(cpu_env); +#endif + break; + default: tcg_gen_st_reg(reg, cpu_env, offsetof(CPUHPPAState, cr[ctl])); break; } - return nullify_end(ctx, ret); + return nullify_end(ctx); #endif } -static DisasJumpType trans_mtsarcm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtsarcm(DisasContext *ctx, arg_mtsarcm *a) { - unsigned rin = extract32(insn, 16, 5); TCGv_reg tmp = tcg_temp_new(); - tcg_gen_not_reg(tmp, load_gpr(ctx, rin)); + tcg_gen_not_reg(tmp, load_gpr(ctx, a->r)); tcg_gen_andi_reg(tmp, tmp, TARGET_REGISTER_BITS - 1); save_or_nullify(ctx, cpu_sar, tmp); tcg_temp_free(tmp); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_ldsid(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ldsid(DisasContext *ctx, arg_ldsid *a) { - unsigned rt = extract32(insn, 0, 5); - TCGv_reg dest = dest_gpr(ctx, rt); + TCGv_reg dest = dest_gpr(ctx, a->t); #ifdef CONFIG_USER_ONLY /* We don't implement space registers in user mode. */ tcg_gen_movi_reg(dest, 0); #else - unsigned rb = extract32(insn, 21, 5); - unsigned sp = extract32(insn, 14, 2); TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_mov_i64(t0, space_select(ctx, sp, load_gpr(ctx, rb))); + tcg_gen_mov_i64(t0, space_select(ctx, a->sp, load_gpr(ctx, a->b))); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_trunc_i64_reg(dest, t0); tcg_temp_free_i64(t0); #endif - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } +static bool trans_rsm(DisasContext *ctx, arg_rsm *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); #ifndef CONFIG_USER_ONLY -/* Note that ssm/rsm instructions number PSW_W and PSW_E differently. */ -static target_ureg extract_sm_imm(uint32_t insn) -{ - target_ureg val = extract32(insn, 16, 10); - - if (val & PSW_SM_E) { - val = (val & ~PSW_SM_E) | PSW_E; - } - if (val & PSW_SM_W) { - val = (val & ~PSW_SM_W) | PSW_W; - } - return val; -} - -static DisasJumpType trans_rsm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - target_ureg sm = extract_sm_imm(insn); TCGv_reg tmp; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); tmp = get_temp(ctx); tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw)); - tcg_gen_andi_reg(tmp, tmp, ~sm); + tcg_gen_andi_reg(tmp, tmp, ~a->i); gen_helper_swap_system_mask(tmp, cpu_env, tmp); - save_gpr(ctx, rt, tmp); + save_gpr(ctx, a->t, tmp); /* Exit the TB to recognize new interrupts, e.g. PSW_M. */ - return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT); + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + return nullify_end(ctx); +#endif } -static DisasJumpType trans_ssm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ssm(DisasContext *ctx, arg_ssm *a) { - unsigned rt = extract32(insn, 0, 5); - target_ureg sm = extract_sm_imm(insn); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_reg tmp; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); tmp = get_temp(ctx); tcg_gen_ld_reg(tmp, cpu_env, offsetof(CPUHPPAState, psw)); - tcg_gen_ori_reg(tmp, tmp, sm); + tcg_gen_ori_reg(tmp, tmp, a->i); gen_helper_swap_system_mask(tmp, cpu_env, tmp); - save_gpr(ctx, rt, tmp); + save_gpr(ctx, a->t, tmp); /* Exit the TB to recognize new interrupts, e.g. PSW_I. */ - return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT); + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + return nullify_end(ctx); +#endif } -static DisasJumpType trans_mtsm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_mtsm(DisasContext *ctx, arg_mtsm *a) { - unsigned rr = extract32(insn, 16, 5); - TCGv_reg tmp, reg; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + TCGv_reg tmp, reg; nullify_over(ctx); - reg = load_gpr(ctx, rr); + reg = load_gpr(ctx, a->r); tmp = get_temp(ctx); gen_helper_swap_system_mask(tmp, cpu_env, reg); /* Exit the TB to recognize new interrupts. */ - return nullify_end(ctx, DISAS_IAQ_N_STALE_EXIT); + ctx->base.is_jmp = DISAS_IAQ_N_STALE_EXIT; + return nullify_end(ctx); +#endif } -static DisasJumpType trans_rfi(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_rfi(DisasContext *ctx, bool rfi_r) { - unsigned comp = extract32(insn, 5, 4); - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY nullify_over(ctx); - if (comp == 5) { + if (rfi_r) { gen_helper_rfi_r(cpu_env); } else { gen_helper_rfi(cpu_env); } + /* Exit the TB to recognize new interrupts. */ if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); } else { tcg_gen_exit_tb(NULL, 0); } + ctx->base.is_jmp = DISAS_NORETURN; - /* Exit the TB to recognize new interrupts. */ - return nullify_end(ctx, DISAS_NORETURN); + return nullify_end(ctx); +#endif } -static DisasJumpType gen_hlt(DisasContext *ctx, int reset) +static bool trans_rfi(DisasContext *ctx, arg_rfi *a) +{ + return do_rfi(ctx, false); +} + +static bool trans_rfi_r(DisasContext *ctx, arg_rfi_r *a) +{ + return do_rfi(ctx, true); +} + +static bool trans_halt(DisasContext *ctx, arg_halt *a) { CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - nullify_over(ctx); - if (reset) { - gen_helper_reset(cpu_env); - } else { - gen_helper_halt(cpu_env); - } - return nullify_end(ctx, DISAS_NORETURN); -} -#endif /* !CONFIG_USER_ONLY */ - -static const DisasInsn table_system[] = { - { 0x00000000u, 0xfc001fe0u, trans_break }, - { 0x00001820u, 0xffe01fffu, trans_mtsp }, - { 0x00001840u, 0xfc00ffffu, trans_mtctl }, - { 0x016018c0u, 0xffe0ffffu, trans_mtsarcm }, - { 0x000014a0u, 0xffffffe0u, trans_mfia }, - { 0x000004a0u, 0xffff1fe0u, trans_mfsp }, - { 0x000008a0u, 0xfc1fbfe0u, trans_mfctl }, - { 0x00000400u, 0xffffffffu, trans_sync }, /* sync */ - { 0x00100400u, 0xffffffffu, trans_sync }, /* syncdma */ - { 0x000010a0u, 0xfc1f3fe0u, trans_ldsid }, #ifndef CONFIG_USER_ONLY - { 0x00000e60u, 0xfc00ffe0u, trans_rsm }, - { 0x00000d60u, 0xfc00ffe0u, trans_ssm }, - { 0x00001860u, 0xffe0ffffu, trans_mtsm }, - { 0x00000c00u, 0xfffffe1fu, trans_rfi }, + nullify_over(ctx); + gen_helper_halt(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); #endif -}; - -static DisasJumpType trans_base_idx_mod(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rb = extract32(insn, 21, 5); - unsigned rx = extract32(insn, 16, 5); - TCGv_reg dest = dest_gpr(ctx, rb); - TCGv_reg src1 = load_gpr(ctx, rb); - TCGv_reg src2 = load_gpr(ctx, rx); - - /* The only thing we need to do is the base register modification. */ - tcg_gen_add_reg(dest, src1, src2); - save_gpr(ctx, rb, dest); - - cond_free(&ctx->null_cond); - return DISAS_NEXT; } -static DisasJumpType trans_probe(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_reset(DisasContext *ctx, arg_reset *a) +{ + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY + nullify_over(ctx); + gen_helper_reset(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); +#endif +} + +static bool trans_nop_addrx(DisasContext *ctx, arg_ldst *a) +{ + if (a->m) { + TCGv_reg dest = dest_gpr(ctx, a->b); + TCGv_reg src1 = load_gpr(ctx, a->b); + TCGv_reg src2 = load_gpr(ctx, a->x); + + /* The only thing we need to do is the base register modification. */ + tcg_gen_add_reg(dest, src1, src2); + save_gpr(ctx, a->b, dest); + } + cond_free(&ctx->null_cond); + return true; +} + +static bool trans_probe(DisasContext *ctx, arg_probe *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned sp = extract32(insn, 14, 2); - unsigned rr = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned is_write = extract32(insn, 6, 1); - unsigned is_imm = extract32(insn, 13, 1); TCGv_reg dest, ofs; TCGv_i32 level, want; TCGv_tl addr; nullify_over(ctx); - dest = dest_gpr(ctx, rt); - form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false); + dest = dest_gpr(ctx, a->t); + form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); - if (is_imm) { - level = tcg_const_i32(extract32(insn, 16, 2)); + if (a->imm) { + level = tcg_const_i32(a->ri); } else { level = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(level, load_gpr(ctx, rr)); + tcg_gen_trunc_reg_i32(level, load_gpr(ctx, a->ri)); tcg_gen_andi_i32(level, level, 3); } - want = tcg_const_i32(is_write ? PAGE_WRITE : PAGE_READ); + want = tcg_const_i32(a->write ? PAGE_WRITE : PAGE_READ); gen_helper_probe(dest, cpu_env, addr, level, want); tcg_temp_free_i32(want); tcg_temp_free_i32(level); - save_gpr(ctx, rt, dest); - return nullify_end(ctx, DISAS_NEXT); + save_gpr(ctx, a->t, dest); + return nullify_end(ctx); } -#ifndef CONFIG_USER_ONLY -static DisasJumpType trans_ixtlbx(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ixtlbx(DisasContext *ctx, arg_ixtlbx *a) { - unsigned sp; - unsigned rr = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned is_data = insn & 0x1000; - unsigned is_addr = insn & 0x40; + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_tl addr; TCGv_reg ofs, reg; - if (is_data) { - sp = extract32(insn, 14, 2); - } else { - sp = ~assemble_sr3(insn); - } - - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); - form_gva(ctx, &addr, &ofs, rb, 0, 0, 0, sp, 0, false); - reg = load_gpr(ctx, rr); - if (is_addr) { + form_gva(ctx, &addr, &ofs, a->b, 0, 0, 0, a->sp, 0, false); + reg = load_gpr(ctx, a->r); + if (a->addr) { gen_helper_itlba(cpu_env, addr, reg); } else { gen_helper_itlbp(cpu_env, addr, reg); } - /* Exit TB for ITLB change if mmu is enabled. This *should* not be - the case, since the OS TLB fill handler runs with mmu disabled. */ - return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C) - ? DISAS_IAQ_N_STALE : DISAS_NEXT); + /* Exit TB for TLB change if mmu is enabled. */ + if (ctx->tb_flags & PSW_C) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif } -static DisasJumpType trans_pxtlbx(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_pxtlbx(DisasContext *ctx, arg_pxtlbx *a) { - unsigned m = extract32(insn, 5, 1); - unsigned sp; - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned is_data = insn & 0x1000; - unsigned is_local = insn & 0x40; + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_tl addr; TCGv_reg ofs; - if (is_data) { - sp = extract32(insn, 14, 2); - } else { - sp = ~assemble_sr3(insn); - } - - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); - form_gva(ctx, &addr, &ofs, rb, rx, 0, 0, sp, m, false); - if (m) { - save_gpr(ctx, rb, ofs); + form_gva(ctx, &addr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); + if (a->m) { + save_gpr(ctx, a->b, ofs); } - if (is_local) { + if (a->local) { gen_helper_ptlbe(cpu_env); } else { gen_helper_ptlb(cpu_env, addr); } /* Exit TB for TLB change if mmu is enabled. */ - return nullify_end(ctx, !is_data && (ctx->tb_flags & PSW_C) - ? DISAS_IAQ_N_STALE : DISAS_NEXT); + if (ctx->tb_flags & PSW_C) { + ctx->base.is_jmp = DISAS_IAQ_N_STALE; + } + return nullify_end(ctx); +#endif } -static DisasJumpType trans_lpa(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_lpa(DisasContext *ctx, arg_ldst *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); + CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); +#ifndef CONFIG_USER_ONLY TCGv_tl vaddr; TCGv_reg ofs, paddr; - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); nullify_over(ctx); - form_gva(ctx, &vaddr, &ofs, rb, rx, 0, 0, sp, m, false); + form_gva(ctx, &vaddr, &ofs, a->b, a->x, 0, 0, a->sp, a->m, false); paddr = tcg_temp_new(); gen_helper_lpa(paddr, cpu_env, vaddr); /* Note that physical address result overrides base modification. */ - if (m) { - save_gpr(ctx, rb, ofs); + if (a->m) { + save_gpr(ctx, a->b, ofs); } - save_gpr(ctx, rt, paddr); + save_gpr(ctx, a->t, paddr); tcg_temp_free(paddr); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); +#endif } -static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_lci(DisasContext *ctx, arg_lci *a) { - unsigned rt = extract32(insn, 0, 5); TCGv_reg ci; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); @@ -2508,238 +2554,193 @@ static DisasJumpType trans_lci(DisasContext *ctx, uint32_t insn, view of the cache. Our implementation is to return 0 for all, since the entire address space is coherent. */ ci = tcg_const_reg(0); - save_gpr(ctx, rt, ci); + save_gpr(ctx, a->t, ci); tcg_temp_free(ci); - return DISAS_NEXT; -} -#endif /* !CONFIG_USER_ONLY */ - -static const DisasInsn table_mem_mgmt[] = { - { 0x04003280u, 0xfc003fffu, trans_nop }, /* fdc, disp */ - { 0x04001280u, 0xfc003fffu, trans_nop }, /* fdc, index */ - { 0x040012a0u, 0xfc003fffu, trans_base_idx_mod }, /* fdc, index, base mod */ - { 0x040012c0u, 0xfc003fffu, trans_nop }, /* fdce */ - { 0x040012e0u, 0xfc003fffu, trans_base_idx_mod }, /* fdce, base mod */ - { 0x04000280u, 0xfc001fffu, trans_nop }, /* fic 0a */ - { 0x040002a0u, 0xfc001fffu, trans_base_idx_mod }, /* fic 0a, base mod */ - { 0x040013c0u, 0xfc003fffu, trans_nop }, /* fic 4f */ - { 0x040013e0u, 0xfc003fffu, trans_base_idx_mod }, /* fic 4f, base mod */ - { 0x040002c0u, 0xfc001fffu, trans_nop }, /* fice */ - { 0x040002e0u, 0xfc001fffu, trans_base_idx_mod }, /* fice, base mod */ - { 0x04002700u, 0xfc003fffu, trans_nop }, /* pdc */ - { 0x04002720u, 0xfc003fffu, trans_base_idx_mod }, /* pdc, base mod */ - { 0x04001180u, 0xfc003fa0u, trans_probe }, /* probe */ - { 0x04003180u, 0xfc003fa0u, trans_probe }, /* probei */ -#ifndef CONFIG_USER_ONLY - { 0x04000000u, 0xfc001fffu, trans_ixtlbx }, /* iitlbp */ - { 0x04000040u, 0xfc001fffu, trans_ixtlbx }, /* iitlba */ - { 0x04001000u, 0xfc001fffu, trans_ixtlbx }, /* idtlbp */ - { 0x04001040u, 0xfc001fffu, trans_ixtlbx }, /* idtlba */ - { 0x04000200u, 0xfc001fdfu, trans_pxtlbx }, /* pitlb */ - { 0x04000240u, 0xfc001fdfu, trans_pxtlbx }, /* pitlbe */ - { 0x04001200u, 0xfc001fdfu, trans_pxtlbx }, /* pdtlb */ - { 0x04001240u, 0xfc001fdfu, trans_pxtlbx }, /* pdtlbe */ - { 0x04001340u, 0xfc003fc0u, trans_lpa }, - { 0x04001300u, 0xfc003fe0u, trans_lci }, -#endif -}; - -static DisasJumpType trans_add(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned ext = extract32(insn, 8, 4); - unsigned shift = extract32(insn, 6, 2); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - bool is_c = false; - bool is_l = false; - bool is_tc = false; - bool is_tsv = false; - DisasJumpType ret; - - switch (ext) { - case 0x6: /* ADD, SHLADD */ - break; - case 0xa: /* ADD,L, SHLADD,L */ - is_l = true; - break; - case 0xe: /* ADD,TSV, SHLADD,TSV (1) */ - is_tsv = true; - break; - case 0x7: /* ADD,C */ - is_c = true; - break; - case 0xf: /* ADD,C,TSV */ - is_c = is_tsv = true; - break; - default: - return gen_illegal(ctx); - } - - if (cf) { - nullify_over(ctx); - } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_add(ctx, rt, tcg_r1, tcg_r2, shift, is_l, is_tsv, is_tc, is_c, cf); - return nullify_end(ctx, ret); -} - -static DisasJumpType trans_sub(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned ext = extract32(insn, 6, 6); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - bool is_b = false; - bool is_tc = false; - bool is_tsv = false; - DisasJumpType ret; - - switch (ext) { - case 0x10: /* SUB */ - break; - case 0x30: /* SUB,TSV */ - is_tsv = true; - break; - case 0x14: /* SUB,B */ - is_b = true; - break; - case 0x34: /* SUB,B,TSV */ - is_b = is_tsv = true; - break; - case 0x13: /* SUB,TC */ - is_tc = true; - break; - case 0x33: /* SUB,TSV,TC */ - is_tc = is_tsv = true; - break; - default: - return gen_illegal(ctx); - } - - if (cf) { - nullify_over(ctx); - } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_sub(ctx, rt, tcg_r1, tcg_r2, is_tsv, is_b, is_tc, cf); - return nullify_end(ctx, ret); -} - -static DisasJumpType trans_log(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - DisasJumpType ret; - - if (cf) { - nullify_over(ctx); - } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_log(ctx, rt, tcg_r1, tcg_r2, cf, di->f.ttt); - return nullify_end(ctx, ret); -} - -/* OR r,0,t -> COPY (according to gas) */ -static DisasJumpType trans_copy(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned r1 = extract32(insn, 16, 5); - unsigned rt = extract32(insn, 0, 5); - - if (r1 == 0) { - TCGv_reg dest = dest_gpr(ctx, rt); - tcg_gen_movi_reg(dest, 0); - save_gpr(ctx, rt, dest); - } else { - save_gpr(ctx, rt, cpu_gr[r1]); - } cond_free(&ctx->null_cond); - return DISAS_NEXT; + return true; } -static DisasJumpType trans_cmpclr(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_add(DisasContext *ctx, arg_rrr_cf_sh *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); - TCGv_reg tcg_r1, tcg_r2; - DisasJumpType ret; + return do_add_reg(ctx, a, false, false, false, false); +} - if (cf) { +static bool trans_add_l(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, true, false, false, false); +} + +static bool trans_add_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, false, true, false, false); +} + +static bool trans_add_c(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, false, false, false, true); +} + +static bool trans_add_c_tsv(DisasContext *ctx, arg_rrr_cf_sh *a) +{ + return do_add_reg(ctx, a, false, true, false, true); +} + +static bool trans_sub(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, false, false, false); +} + +static bool trans_sub_tsv(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, true, false, false); +} + +static bool trans_sub_tc(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, false, false, true); +} + +static bool trans_sub_tsv_tc(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, true, false, true); +} + +static bool trans_sub_b(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, false, true, false); +} + +static bool trans_sub_b_tsv(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_sub_reg(ctx, a, true, true, false); +} + +static bool trans_andcm(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_log_reg(ctx, a, tcg_gen_andc_reg); +} + +static bool trans_and(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_log_reg(ctx, a, tcg_gen_and_reg); +} + +static bool trans_or(DisasContext *ctx, arg_rrr_cf *a) +{ + if (a->cf == 0) { + unsigned r2 = a->r2; + unsigned r1 = a->r1; + unsigned rt = a->t; + + if (rt == 0) { /* NOP */ + cond_free(&ctx->null_cond); + return true; + } + if (r2 == 0) { /* COPY */ + if (r1 == 0) { + TCGv_reg dest = dest_gpr(ctx, rt); + tcg_gen_movi_reg(dest, 0); + save_gpr(ctx, rt, dest); + } else { + save_gpr(ctx, rt, cpu_gr[r1]); + } + cond_free(&ctx->null_cond); + return true; + } +#ifndef CONFIG_USER_ONLY + /* These are QEMU extensions and are nops in the real architecture: + * + * or %r10,%r10,%r10 -- idle loop; wait for interrupt + * or %r31,%r31,%r31 -- death loop; offline cpu + * currently implemented as idle. + */ + if ((rt == 10 || rt == 31) && r1 == rt && r2 == rt) { /* PAUSE */ + TCGv_i32 tmp; + + /* No need to check for supervisor, as userland can only pause + until the next timer interrupt. */ + nullify_over(ctx); + + /* Advance the instruction queue. */ + copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); + copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + nullify_set(ctx, 0); + + /* Tell the qemu main loop to halt until this cpu has work. */ + tmp = tcg_const_i32(1); + tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) + + offsetof(CPUState, halted)); + tcg_temp_free_i32(tmp); + gen_excp_1(EXCP_HALTED); + ctx->base.is_jmp = DISAS_NORETURN; + + return nullify_end(ctx); + } +#endif + } + return do_log_reg(ctx, a, tcg_gen_or_reg); +} + +static bool trans_xor(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_log_reg(ctx, a, tcg_gen_xor_reg); +} + +static bool trans_cmpclr(DisasContext *ctx, arg_rrr_cf *a) +{ + TCGv_reg tcg_r1, tcg_r2; + + if (a->cf) { nullify_over(ctx); } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_cmpclr(ctx, rt, tcg_r1, tcg_r2, cf); - return nullify_end(ctx, ret); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_cmpclr(ctx, a->t, tcg_r1, tcg_r2, a->cf); + return nullify_end(ctx); } -static DisasJumpType trans_uxor(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_uxor(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tcg_r1, tcg_r2; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); - ret = do_unit(ctx, rt, tcg_r1, tcg_r2, cf, false, tcg_gen_xor_reg); - return nullify_end(ctx, ret); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); + do_unit(ctx, a->t, tcg_r1, tcg_r2, a->cf, false, tcg_gen_xor_reg); + return nullify_end(ctx); } -static DisasJumpType trans_uaddcm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_uaddcm(DisasContext *ctx, arg_rrr_cf *a, bool is_tc) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned is_tc = extract32(insn, 6, 1); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tcg_r1, tcg_r2, tmp; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_r1 = load_gpr(ctx, r1); - tcg_r2 = load_gpr(ctx, r2); + tcg_r1 = load_gpr(ctx, a->r1); + tcg_r2 = load_gpr(ctx, a->r2); tmp = get_temp(ctx); tcg_gen_not_reg(tmp, tcg_r2); - ret = do_unit(ctx, rt, tcg_r1, tmp, cf, is_tc, tcg_gen_add_reg); - return nullify_end(ctx, ret); + do_unit(ctx, a->t, tcg_r1, tmp, a->cf, is_tc, tcg_gen_add_reg); + return nullify_end(ctx); } -static DisasJumpType trans_dcor(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_uaddcm(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_uaddcm(ctx, a, false); +} + +static bool trans_uaddcm_tc(DisasContext *ctx, arg_rrr_cf *a) +{ + return do_uaddcm(ctx, a, true); +} + +static bool do_dcor(DisasContext *ctx, arg_rr_cf *a, bool is_i) { - unsigned r2 = extract32(insn, 21, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned is_i = extract32(insn, 6, 1); - unsigned rt = extract32(insn, 0, 5); TCGv_reg tmp; - DisasJumpType ret; nullify_over(ctx); @@ -2750,25 +2751,29 @@ static DisasJumpType trans_dcor(DisasContext *ctx, uint32_t insn, } tcg_gen_andi_reg(tmp, tmp, 0x11111111); tcg_gen_muli_reg(tmp, tmp, 6); - ret = do_unit(ctx, rt, tmp, load_gpr(ctx, r2), cf, false, - is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); - - return nullify_end(ctx, ret); + do_unit(ctx, a->t, load_gpr(ctx, a->r), tmp, a->cf, false, + is_i ? tcg_gen_add_reg : tcg_gen_sub_reg); + return nullify_end(ctx); } -static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_dcor(DisasContext *ctx, arg_rr_cf *a) +{ + return do_dcor(ctx, a, false); +} + +static bool trans_dcor_i(DisasContext *ctx, arg_rr_cf *a) +{ + return do_dcor(ctx, a, true); +} + +static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) { - unsigned r2 = extract32(insn, 21, 5); - unsigned r1 = extract32(insn, 16, 5); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 0, 5); TCGv_reg dest, add1, add2, addc, zero, in1, in2; nullify_over(ctx); - in1 = load_gpr(ctx, r1); - in2 = load_gpr(ctx, r2); + in1 = load_gpr(ctx, a->r1); + in2 = load_gpr(ctx, a->r2); add1 = tcg_temp_new(); add2 = tcg_temp_new(); @@ -2795,7 +2800,7 @@ static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn, tcg_temp_free(zero); /* Write back the result register. */ - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Write back PSW[CB]. */ tcg_gen_xor_reg(cpu_psw_cb, add1, add2); @@ -2806,251 +2811,118 @@ static DisasJumpType trans_ds(DisasContext *ctx, uint32_t insn, tcg_gen_xor_reg(cpu_psw_v, cpu_psw_v, in2); /* Install the new nullification. */ - if (cf) { + if (a->cf) { TCGv_reg sv = NULL; - if (cf >> 1 == 6) { + if (cond_need_sv(a->cf >> 1)) { /* ??? The lshift is supposed to contribute to overflow. */ sv = do_add_sv(ctx, dest, add1, add2); } - ctx->null_cond = do_cond(cf, dest, cpu_psw_cb_msb, sv); + ctx->null_cond = do_cond(a->cf, dest, cpu_psw_cb_msb, sv); } tcg_temp_free(add1); tcg_temp_free(add2); tcg_temp_free(dest); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -#ifndef CONFIG_USER_ONLY -/* These are QEMU extensions and are nops in the real architecture: - * - * or %r10,%r10,%r10 -- idle loop; wait for interrupt - * or %r31,%r31,%r31 -- death loop; offline cpu - * currently implemented as idle. - */ -static DisasJumpType trans_pause(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_addi(DisasContext *ctx, arg_rri_cf *a) { - TCGv_i32 tmp; - - /* No need to check for supervisor, as userland can only pause - until the next timer interrupt. */ - nullify_over(ctx); - - /* Advance the instruction queue. */ - copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); - nullify_set(ctx, 0); - - /* Tell the qemu main loop to halt until this cpu has work. */ - tmp = tcg_const_i32(1); - tcg_gen_st_i32(tmp, cpu_env, -offsetof(HPPACPU, env) + - offsetof(CPUState, halted)); - tcg_temp_free_i32(tmp); - gen_excp_1(EXCP_HALTED); - - return nullify_end(ctx, DISAS_NORETURN); + return do_add_imm(ctx, a, false, false); } -#endif -static const DisasInsn table_arith_log[] = { - { 0x08000240u, 0xfc00ffffu, trans_nop }, /* or x,y,0 */ - { 0x08000240u, 0xffe0ffe0u, trans_copy }, /* or x,0,t */ -#ifndef CONFIG_USER_ONLY - { 0x094a024au, 0xffffffffu, trans_pause }, /* or r10,r10,r10 */ - { 0x0bff025fu, 0xffffffffu, trans_pause }, /* or r31,r31,r31 */ -#endif - { 0x08000000u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_andc_reg }, - { 0x08000200u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_and_reg }, - { 0x08000240u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_or_reg }, - { 0x08000280u, 0xfc000fe0u, trans_log, .f.ttt = tcg_gen_xor_reg }, - { 0x08000880u, 0xfc000fe0u, trans_cmpclr }, - { 0x08000380u, 0xfc000fe0u, trans_uxor }, - { 0x08000980u, 0xfc000fa0u, trans_uaddcm }, - { 0x08000b80u, 0xfc1f0fa0u, trans_dcor }, - { 0x08000440u, 0xfc000fe0u, trans_ds }, - { 0x08000700u, 0xfc0007e0u, trans_add }, /* add */ - { 0x08000400u, 0xfc0006e0u, trans_sub }, /* sub; sub,b; sub,tsv */ - { 0x080004c0u, 0xfc0007e0u, trans_sub }, /* sub,tc; sub,tsv,tc */ - { 0x08000200u, 0xfc000320u, trans_add }, /* shladd */ -}; - -static DisasJumpType trans_addi(DisasContext *ctx, uint32_t insn) +static bool trans_addi_tsv(DisasContext *ctx, arg_rri_cf *a) +{ + return do_add_imm(ctx, a, true, false); +} + +static bool trans_addi_tc(DisasContext *ctx, arg_rri_cf *a) +{ + return do_add_imm(ctx, a, false, true); +} + +static bool trans_addi_tc_tsv(DisasContext *ctx, arg_rri_cf *a) +{ + return do_add_imm(ctx, a, true, true); +} + +static bool trans_subi(DisasContext *ctx, arg_rri_cf *a) +{ + return do_sub_imm(ctx, a, false); +} + +static bool trans_subi_tsv(DisasContext *ctx, arg_rri_cf *a) +{ + return do_sub_imm(ctx, a, true); +} + +static bool trans_cmpiclr(DisasContext *ctx, arg_rri_cf *a) { - target_sreg im = low_sextract(insn, 0, 11); - unsigned e1 = extract32(insn, 11, 1); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - unsigned o1 = extract32(insn, 26, 1); TCGv_reg tcg_im, tcg_r2; - DisasJumpType ret; - if (cf) { + if (a->cf) { nullify_over(ctx); } - tcg_im = load_const(ctx, im); - tcg_r2 = load_gpr(ctx, r2); - ret = do_add(ctx, rt, tcg_im, tcg_r2, 0, false, e1, !o1, false, cf); + tcg_im = load_const(ctx, a->i); + tcg_r2 = load_gpr(ctx, a->r); + do_cmpclr(ctx, a->t, tcg_im, tcg_r2, a->cf); - return nullify_end(ctx, ret); + return nullify_end(ctx); } -static DisasJumpType trans_subi(DisasContext *ctx, uint32_t insn) +static bool trans_ld(DisasContext *ctx, arg_ldst *a) { - target_sreg im = low_sextract(insn, 0, 11); - unsigned e1 = extract32(insn, 11, 1); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - TCGv_reg tcg_im, tcg_r2; - DisasJumpType ret; - - if (cf) { - nullify_over(ctx); - } - - tcg_im = load_const(ctx, im); - tcg_r2 = load_gpr(ctx, r2); - ret = do_sub(ctx, rt, tcg_im, tcg_r2, e1, false, false, cf); - - return nullify_end(ctx, ret); + return do_load(ctx, a->t, a->b, a->x, a->scale ? a->size : 0, + a->disp, a->sp, a->m, a->size | MO_TE); } -static DisasJumpType trans_cmpiclr(DisasContext *ctx, uint32_t insn) +static bool trans_st(DisasContext *ctx, arg_ldst *a) { - target_sreg im = low_sextract(insn, 0, 11); - unsigned cf = extract32(insn, 12, 4); - unsigned rt = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - TCGv_reg tcg_im, tcg_r2; - DisasJumpType ret; - - if (cf) { - nullify_over(ctx); - } - - tcg_im = load_const(ctx, im); - tcg_r2 = load_gpr(ctx, r2); - ret = do_cmpclr(ctx, rt, tcg_im, tcg_r2, cf); - - return nullify_end(ctx, ret); + assert(a->x == 0 && a->scale == 0); + return do_store(ctx, a->t, a->b, a->disp, a->sp, a->m, a->size | MO_TE); } -static DisasJumpType trans_ld_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ldc(DisasContext *ctx, arg_ldst *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sz = extract32(insn, 6, 2); - unsigned a = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - int disp = low_sextract(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - int modify = (m ? (a ? -1 : 1) : 0); - TCGMemOp mop = MO_TE | sz; - - return do_load(ctx, rt, rb, 0, 0, disp, sp, modify, mop); -} - -static DisasJumpType trans_ld_idx_x(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sz = extract32(insn, 6, 2); - unsigned u = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - TCGMemOp mop = MO_TE | sz; - - return do_load(ctx, rt, rb, rx, u ? sz : 0, 0, sp, m, mop); -} - -static DisasJumpType trans_st_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - int disp = low_sextract(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned sz = extract32(insn, 6, 2); - unsigned a = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rr = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - int modify = (m ? (a ? -1 : 1) : 0); - TCGMemOp mop = MO_TE | sz; - - return do_store(ctx, rr, rb, disp, sp, modify, mop); -} - -static DisasJumpType trans_ldcw(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned i = extract32(insn, 12, 1); - unsigned au = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - TCGMemOp mop = MO_TEUL | MO_ALIGN_16; + TCGMemOp mop = MO_TEUL | MO_ALIGN_16 | a->size; TCGv_reg zero, dest, ofs; TCGv_tl addr; - int modify, disp = 0, scale = 0; nullify_over(ctx); - if (i) { - modify = (m ? (au ? -1 : 1) : 0); - disp = low_sextract(rx, 0, 5); - rx = 0; - } else { - modify = m; - if (au) { - scale = mop & MO_SIZE; - } - } - if (modify) { + if (a->m) { /* Base register modification. Make sure if RT == RB, we see the result of the load. */ dest = get_temp(ctx); } else { - dest = dest_gpr(ctx, rt); + dest = dest_gpr(ctx, a->t); } - form_gva(ctx, &addr, &ofs, rb, rx, scale, disp, sp, modify, - ctx->mmu_idx == MMU_PHYS_IDX); + form_gva(ctx, &addr, &ofs, a->b, a->x, a->scale ? a->size : 0, + a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX); zero = tcg_const_reg(0); tcg_gen_atomic_xchg_reg(dest, addr, zero, ctx->mmu_idx, mop); - if (modify) { - save_gpr(ctx, rb, ofs); + if (a->m) { + save_gpr(ctx, a->b, ofs); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_stby(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_stby(DisasContext *ctx, arg_stby *a) { - target_sreg disp = low_sextract(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned a = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rt = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); TCGv_reg ofs, val; TCGv_tl addr; nullify_over(ctx); - form_gva(ctx, &addr, &ofs, rb, 0, 0, disp, sp, m, + form_gva(ctx, &addr, &ofs, a->b, 0, 0, a->disp, a->sp, a->m, ctx->mmu_idx == MMU_PHYS_IDX); - val = load_gpr(ctx, rt); - if (a) { + val = load_gpr(ctx, a->r); + if (a->a) { if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { gen_helper_stby_e_parallel(cpu_env, addr, val); } else { @@ -3063,433 +2935,224 @@ static DisasJumpType trans_stby(DisasContext *ctx, uint32_t insn, gen_helper_stby_b(cpu_env, addr, val); } } - - if (m) { + if (a->m) { tcg_gen_andi_reg(ofs, ofs, ~3); - save_gpr(ctx, rb, ofs); + save_gpr(ctx, a->b, ofs); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -#ifndef CONFIG_USER_ONLY -static DisasJumpType trans_ldwa_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_lda(DisasContext *ctx, arg_ldst *a) { int hold_mmu_idx = ctx->mmu_idx; - DisasJumpType ret; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - - /* ??? needs fixing for hppa64 -- ldda does not follow the same - format wrt the sub-opcode in bits 6:9. */ ctx->mmu_idx = MMU_PHYS_IDX; - ret = trans_ld_idx_i(ctx, insn, di); + trans_ld(ctx, a); ctx->mmu_idx = hold_mmu_idx; - return ret; + return true; } -static DisasJumpType trans_ldwa_idx_x(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_sta(DisasContext *ctx, arg_ldst *a) { int hold_mmu_idx = ctx->mmu_idx; - DisasJumpType ret; CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - - /* ??? needs fixing for hppa64 -- ldda does not follow the same - format wrt the sub-opcode in bits 6:9. */ ctx->mmu_idx = MMU_PHYS_IDX; - ret = trans_ld_idx_x(ctx, insn, di); + trans_st(ctx, a); ctx->mmu_idx = hold_mmu_idx; - return ret; + return true; } -static DisasJumpType trans_stwa_idx_i(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ldil(DisasContext *ctx, arg_ldil *a) { - int hold_mmu_idx = ctx->mmu_idx; - DisasJumpType ret; + TCGv_reg tcg_rt = dest_gpr(ctx, a->t); - CHECK_MOST_PRIVILEGED(EXCP_PRIV_OPR); - - /* ??? needs fixing for hppa64 -- ldda does not follow the same - format wrt the sub-opcode in bits 6:9. */ - ctx->mmu_idx = MMU_PHYS_IDX; - ret = trans_st_idx_i(ctx, insn, di); - ctx->mmu_idx = hold_mmu_idx; - return ret; -} -#endif - -static const DisasInsn table_index_mem[] = { - { 0x0c001000u, 0xfc001300, trans_ld_idx_i }, /* LD[BHWD], im */ - { 0x0c000000u, 0xfc001300, trans_ld_idx_x }, /* LD[BHWD], rx */ - { 0x0c001200u, 0xfc001300, trans_st_idx_i }, /* ST[BHWD] */ - { 0x0c0001c0u, 0xfc0003c0, trans_ldcw }, - { 0x0c001300u, 0xfc0013c0, trans_stby }, -#ifndef CONFIG_USER_ONLY - { 0x0c000180u, 0xfc00d3c0, trans_ldwa_idx_x }, /* LDWA, rx */ - { 0x0c001180u, 0xfc00d3c0, trans_ldwa_idx_i }, /* LDWA, im */ - { 0x0c001380u, 0xfc00d3c0, trans_stwa_idx_i }, /* STWA, im */ -#endif -}; - -static DisasJumpType trans_ldil(DisasContext *ctx, uint32_t insn) -{ - unsigned rt = extract32(insn, 21, 5); - target_sreg i = assemble_21(insn); - TCGv_reg tcg_rt = dest_gpr(ctx, rt); - - tcg_gen_movi_reg(tcg_rt, i); - save_gpr(ctx, rt, tcg_rt); + tcg_gen_movi_reg(tcg_rt, a->i); + save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); - - return DISAS_NEXT; + return true; } -static DisasJumpType trans_addil(DisasContext *ctx, uint32_t insn) +static bool trans_addil(DisasContext *ctx, arg_addil *a) { - unsigned rt = extract32(insn, 21, 5); - target_sreg i = assemble_21(insn); - TCGv_reg tcg_rt = load_gpr(ctx, rt); + TCGv_reg tcg_rt = load_gpr(ctx, a->r); TCGv_reg tcg_r1 = dest_gpr(ctx, 1); - tcg_gen_addi_reg(tcg_r1, tcg_rt, i); + tcg_gen_addi_reg(tcg_r1, tcg_rt, a->i); save_gpr(ctx, 1, tcg_r1); cond_free(&ctx->null_cond); - - return DISAS_NEXT; + return true; } -static DisasJumpType trans_ldo(DisasContext *ctx, uint32_t insn) +static bool trans_ldo(DisasContext *ctx, arg_ldo *a) { - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - target_sreg i = assemble_16(insn); - TCGv_reg tcg_rt = dest_gpr(ctx, rt); + TCGv_reg tcg_rt = dest_gpr(ctx, a->t); /* Special case rb == 0, for the LDI pseudo-op. The COPY pseudo-op is handled for free within tcg_gen_addi_tl. */ - if (rb == 0) { - tcg_gen_movi_reg(tcg_rt, i); + if (a->b == 0) { + tcg_gen_movi_reg(tcg_rt, a->i); } else { - tcg_gen_addi_reg(tcg_rt, cpu_gr[rb], i); + tcg_gen_addi_reg(tcg_rt, cpu_gr[a->b], a->i); } - save_gpr(ctx, rt, tcg_rt); + save_gpr(ctx, a->t, tcg_rt); cond_free(&ctx->null_cond); - - return DISAS_NEXT; + return true; } -static DisasJumpType trans_load(DisasContext *ctx, uint32_t insn, - bool is_mod, TCGMemOp mop) +static bool do_cmpb(DisasContext *ctx, unsigned r, TCGv_reg in1, + unsigned c, unsigned f, unsigned n, int disp) { - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16(insn); - - return do_load(ctx, rt, rb, 0, 0, i, sp, - is_mod ? (i < 0 ? -1 : 1) : 0, mop); -} - -static DisasJumpType trans_load_w(DisasContext *ctx, uint32_t insn) -{ - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16a(insn); - unsigned ext2 = extract32(insn, 1, 2); - - switch (ext2) { - case 0: - case 1: - /* FLDW without modification. */ - return do_floadw(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0); - case 2: - /* LDW with modification. Note that the sign of I selects - post-dec vs pre-inc. */ - return do_load(ctx, rt, rb, 0, 0, i, sp, (i < 0 ? 1 : -1), MO_TEUL); - default: - return gen_illegal(ctx); - } -} - -static DisasJumpType trans_fload_mod(DisasContext *ctx, uint32_t insn) -{ - target_sreg i = assemble_16a(insn); - unsigned t1 = extract32(insn, 1, 1); - unsigned a = extract32(insn, 2, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned t0 = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - - /* FLDW with modification. */ - return do_floadw(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1)); -} - -static DisasJumpType trans_store(DisasContext *ctx, uint32_t insn, - bool is_mod, TCGMemOp mop) -{ - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16(insn); - - return do_store(ctx, rt, rb, i, sp, is_mod ? (i < 0 ? -1 : 1) : 0, mop); -} - -static DisasJumpType trans_store_w(DisasContext *ctx, uint32_t insn) -{ - unsigned rb = extract32(insn, 21, 5); - unsigned rt = extract32(insn, 16, 5); - unsigned sp = extract32(insn, 14, 2); - target_sreg i = assemble_16a(insn); - unsigned ext2 = extract32(insn, 1, 2); - - switch (ext2) { - case 0: - case 1: - /* FSTW without modification. */ - return do_fstorew(ctx, ext2 * 32 + rt, rb, 0, 0, i, sp, 0); - case 2: - /* STW with modification. */ - return do_store(ctx, rt, rb, i, sp, (i < 0 ? 1 : -1), MO_TEUL); - default: - return gen_illegal(ctx); - } -} - -static DisasJumpType trans_fstore_mod(DisasContext *ctx, uint32_t insn) -{ - target_sreg i = assemble_16a(insn); - unsigned t1 = extract32(insn, 1, 1); - unsigned a = extract32(insn, 2, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned t0 = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - - /* FSTW with modification. */ - return do_fstorew(ctx, t1 * 32 + t0, rb, 0, 0, i, sp, (a ? -1 : 1)); -} - -static DisasJumpType trans_copr_w(DisasContext *ctx, uint32_t insn) -{ - unsigned t0 = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned t1 = extract32(insn, 6, 1); - unsigned ext3 = extract32(insn, 7, 3); - /* unsigned cc = extract32(insn, 10, 2); */ - unsigned i = extract32(insn, 12, 1); - unsigned ua = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - unsigned rt = t1 * 32 + t0; - int modify = (m ? (ua ? -1 : 1) : 0); - int disp, scale; - - if (i == 0) { - scale = (ua ? 2 : 0); - disp = 0; - modify = m; - } else { - disp = low_sextract(rx, 0, 5); - scale = 0; - rx = 0; - modify = (m ? (ua ? -1 : 1) : 0); - } - - switch (ext3) { - case 0: /* FLDW */ - return do_floadw(ctx, rt, rb, rx, scale, disp, sp, modify); - case 4: /* FSTW */ - return do_fstorew(ctx, rt, rb, rx, scale, disp, sp, modify); - } - return gen_illegal(ctx); -} - -static DisasJumpType trans_copr_dw(DisasContext *ctx, uint32_t insn) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned m = extract32(insn, 5, 1); - unsigned ext4 = extract32(insn, 6, 4); - /* unsigned cc = extract32(insn, 10, 2); */ - unsigned i = extract32(insn, 12, 1); - unsigned ua = extract32(insn, 13, 1); - unsigned sp = extract32(insn, 14, 2); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); - int modify = (m ? (ua ? -1 : 1) : 0); - int disp, scale; - - if (i == 0) { - scale = (ua ? 3 : 0); - disp = 0; - modify = m; - } else { - disp = low_sextract(rx, 0, 5); - scale = 0; - rx = 0; - modify = (m ? (ua ? -1 : 1) : 0); - } - - switch (ext4) { - case 0: /* FLDD */ - return do_floadd(ctx, rt, rb, rx, scale, disp, sp, modify); - case 8: /* FSTD */ - return do_fstored(ctx, rt, rb, rx, scale, disp, sp, modify); - default: - return gen_illegal(ctx); - } -} - -static DisasJumpType trans_cmpb(DisasContext *ctx, uint32_t insn, - bool is_true, bool is_imm, bool is_dw) -{ - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 13, 3); - unsigned r = extract32(insn, 21, 5); - unsigned cf = c * 2 + !is_true; - TCGv_reg dest, in1, in2, sv; + TCGv_reg dest, in2, sv; DisasCond cond; - nullify_over(ctx); - - if (is_imm) { - in1 = load_const(ctx, low_sextract(insn, 16, 5)); - } else { - in1 = load_gpr(ctx, extract32(insn, 16, 5)); - } in2 = load_gpr(ctx, r); dest = get_temp(ctx); tcg_gen_sub_reg(dest, in1, in2); sv = NULL; - if (c == 6) { + if (cond_need_sv(c)) { sv = do_sub_sv(ctx, dest, in1, in2); } - cond = do_sub_cond(cf, dest, in1, in2, sv); + cond = do_sub_cond(c * 2 + f, dest, in1, in2, sv); return do_cbranch(ctx, disp, n, &cond); } -static DisasJumpType trans_addb(DisasContext *ctx, uint32_t insn, - bool is_true, bool is_imm) +static bool trans_cmpb(DisasContext *ctx, arg_cmpb *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 13, 3); - unsigned r = extract32(insn, 21, 5); - unsigned cf = c * 2 + !is_true; - TCGv_reg dest, in1, in2, sv, cb_msb; + nullify_over(ctx); + return do_cmpb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp); +} + +static bool trans_cmpbi(DisasContext *ctx, arg_cmpbi *a) +{ + nullify_over(ctx); + return do_cmpb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); +} + +static bool do_addb(DisasContext *ctx, unsigned r, TCGv_reg in1, + unsigned c, unsigned f, unsigned n, int disp) +{ + TCGv_reg dest, in2, sv, cb_msb; DisasCond cond; - nullify_over(ctx); - - if (is_imm) { - in1 = load_const(ctx, low_sextract(insn, 16, 5)); - } else { - in1 = load_gpr(ctx, extract32(insn, 16, 5)); - } in2 = load_gpr(ctx, r); - dest = dest_gpr(ctx, r); + dest = tcg_temp_new(); sv = NULL; cb_msb = NULL; - switch (c) { - default: - tcg_gen_add_reg(dest, in1, in2); - break; - case 4: case 5: + if (cond_need_cb(c)) { cb_msb = get_temp(ctx); tcg_gen_movi_reg(cb_msb, 0); tcg_gen_add2_reg(dest, cb_msb, in1, cb_msb, in2, cb_msb); - break; - case 6: + } else { tcg_gen_add_reg(dest, in1, in2); + } + if (cond_need_sv(c)) { sv = do_add_sv(ctx, dest, in1, in2); - break; } - cond = do_cond(cf, dest, cb_msb, sv); + cond = do_cond(c * 2 + f, dest, cb_msb, sv); + save_gpr(ctx, r, dest); + tcg_temp_free(dest); return do_cbranch(ctx, disp, n, &cond); } -static DisasJumpType trans_bb(DisasContext *ctx, uint32_t insn) +static bool trans_addb(DisasContext *ctx, arg_addb *a) +{ + nullify_over(ctx); + return do_addb(ctx, a->r2, load_gpr(ctx, a->r1), a->c, a->f, a->n, a->disp); +} + +static bool trans_addbi(DisasContext *ctx, arg_addbi *a) +{ + nullify_over(ctx); + return do_addb(ctx, a->r, load_const(ctx, a->i), a->c, a->f, a->n, a->disp); +} + +static bool trans_bb_sar(DisasContext *ctx, arg_bb_sar *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 15, 1); - unsigned r = extract32(insn, 16, 5); - unsigned p = extract32(insn, 21, 5); - unsigned i = extract32(insn, 26, 1); TCGv_reg tmp, tcg_r; DisasCond cond; nullify_over(ctx); tmp = tcg_temp_new(); - tcg_r = load_gpr(ctx, r); - if (i) { - tcg_gen_shli_reg(tmp, tcg_r, p); - } else { - tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); - } + tcg_r = load_gpr(ctx, a->r); + tcg_gen_shl_reg(tmp, tcg_r, cpu_sar); - cond = cond_make_0(c ? TCG_COND_GE : TCG_COND_LT, tmp); + cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); tcg_temp_free(tmp); - return do_cbranch(ctx, disp, n, &cond); + return do_cbranch(ctx, a->disp, a->n, &cond); } -static DisasJumpType trans_movb(DisasContext *ctx, uint32_t insn, bool is_imm) +static bool trans_bb_imm(DisasContext *ctx, arg_bb_imm *a) +{ + TCGv_reg tmp, tcg_r; + DisasCond cond; + + nullify_over(ctx); + + tmp = tcg_temp_new(); + tcg_r = load_gpr(ctx, a->r); + tcg_gen_shli_reg(tmp, tcg_r, a->p); + + cond = cond_make_0(a->c ? TCG_COND_GE : TCG_COND_LT, tmp); + tcg_temp_free(tmp); + return do_cbranch(ctx, a->disp, a->n, &cond); +} + +static bool trans_movb(DisasContext *ctx, arg_movb *a) { - target_sreg disp = assemble_12(insn) * 4; - unsigned n = extract32(insn, 1, 1); - unsigned c = extract32(insn, 13, 3); - unsigned t = extract32(insn, 16, 5); - unsigned r = extract32(insn, 21, 5); TCGv_reg dest; DisasCond cond; nullify_over(ctx); - dest = dest_gpr(ctx, r); - if (is_imm) { - tcg_gen_movi_reg(dest, low_sextract(t, 0, 5)); - } else if (t == 0) { + dest = dest_gpr(ctx, a->r2); + if (a->r1 == 0) { tcg_gen_movi_reg(dest, 0); } else { - tcg_gen_mov_reg(dest, cpu_gr[t]); + tcg_gen_mov_reg(dest, cpu_gr[a->r1]); } - cond = do_sed_cond(c, dest); - return do_cbranch(ctx, disp, n, &cond); + cond = do_sed_cond(a->c, dest); + return do_cbranch(ctx, a->disp, a->n, &cond); } -static DisasJumpType trans_shrpw_sar(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_movbi(DisasContext *ctx, arg_movbi *a) +{ + TCGv_reg dest; + DisasCond cond; + + nullify_over(ctx); + + dest = dest_gpr(ctx, a->r); + tcg_gen_movi_reg(dest, a->i); + + cond = do_sed_cond(a->c, dest); + return do_cbranch(ctx, a->disp, a->n, &cond); +} + +static bool trans_shrpw_sar(DisasContext *ctx, arg_shrpw_sar *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned c = extract32(insn, 13, 3); - unsigned r1 = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); TCGv_reg dest; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - if (r1 == 0) { - tcg_gen_ext32u_reg(dest, load_gpr(ctx, r2)); + dest = dest_gpr(ctx, a->t); + if (a->r1 == 0) { + tcg_gen_ext32u_reg(dest, load_gpr(ctx, a->r2)); tcg_gen_shr_reg(dest, dest, cpu_sar); - } else if (r1 == r2) { + } else if (a->r1 == a->r2) { TCGv_i32 t32 = tcg_temp_new_i32(); - tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, r2)); + tcg_gen_trunc_reg_i32(t32, load_gpr(ctx, a->r2)); tcg_gen_rotr_i32(t32, t32, cpu_sar); tcg_gen_extu_i32_reg(dest, t32); tcg_temp_free_i32(t32); @@ -3497,7 +3160,7 @@ static DisasJumpType trans_shrpw_sar(DisasContext *ctx, uint32_t insn, TCGv_i64 t = tcg_temp_new_i64(); TCGv_i64 s = tcg_temp_new_i64(); - tcg_gen_concat_reg_i64(t, load_gpr(ctx, r2), load_gpr(ctx, r1)); + tcg_gen_concat_reg_i64(t, load_gpr(ctx, a->r2), load_gpr(ctx, a->r1)); tcg_gen_extu_reg_i64(s, cpu_sar); tcg_gen_shr_i64(t, t, s); tcg_gen_trunc_i64_reg(dest, t); @@ -3505,79 +3168,67 @@ static DisasJumpType trans_shrpw_sar(DisasContext *ctx, uint32_t insn, tcg_temp_free_i64(t); tcg_temp_free_i64(s); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_shrpw_imm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_shrpw_imm(DisasContext *ctx, arg_shrpw_imm *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned cpos = extract32(insn, 5, 5); - unsigned c = extract32(insn, 13, 3); - unsigned r1 = extract32(insn, 16, 5); - unsigned r2 = extract32(insn, 21, 5); - unsigned sa = 31 - cpos; + unsigned sa = 31 - a->cpos; TCGv_reg dest, t2; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - t2 = load_gpr(ctx, r2); - if (r1 == r2) { + dest = dest_gpr(ctx, a->t); + t2 = load_gpr(ctx, a->r2); + if (a->r1 == a->r2) { TCGv_i32 t32 = tcg_temp_new_i32(); tcg_gen_trunc_reg_i32(t32, t2); tcg_gen_rotri_i32(t32, t32, sa); tcg_gen_extu_i32_reg(dest, t32); tcg_temp_free_i32(t32); - } else if (r1 == 0) { + } else if (a->r1 == 0) { tcg_gen_extract_reg(dest, t2, sa, 32 - sa); } else { TCGv_reg t0 = tcg_temp_new(); tcg_gen_extract_reg(t0, t2, sa, 32 - sa); - tcg_gen_deposit_reg(dest, t0, cpu_gr[r1], 32 - sa, sa); + tcg_gen_deposit_reg(dest, t0, cpu_gr[a->r1], 32 - sa, sa); tcg_temp_free(t0); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_extrw_sar(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_extrw_sar(DisasContext *ctx, arg_extrw_sar *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned is_se = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rt = extract32(insn, 16, 5); - unsigned rr = extract32(insn, 21, 5); - unsigned len = 32 - clen; + unsigned len = 32 - a->clen; TCGv_reg dest, src, tmp; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - src = load_gpr(ctx, rr); + dest = dest_gpr(ctx, a->t); + src = load_gpr(ctx, a->r); tmp = tcg_temp_new(); /* Recall that SAR is using big-endian bit numbering. */ tcg_gen_xori_reg(tmp, cpu_sar, TARGET_REGISTER_BITS - 1); - if (is_se) { + if (a->se) { tcg_gen_sar_reg(dest, src, tmp); tcg_gen_sextract_reg(dest, dest, 0, len); } else { @@ -3585,83 +3236,62 @@ static DisasJumpType trans_extrw_sar(DisasContext *ctx, uint32_t insn, tcg_gen_extract_reg(dest, dest, 0, len); } tcg_temp_free(tmp); - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_extrw_imm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_extrw_imm(DisasContext *ctx, arg_extrw_imm *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned pos = extract32(insn, 5, 5); - unsigned is_se = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rt = extract32(insn, 16, 5); - unsigned rr = extract32(insn, 21, 5); - unsigned len = 32 - clen; - unsigned cpos = 31 - pos; + unsigned len = 32 - a->clen; + unsigned cpos = 31 - a->pos; TCGv_reg dest, src; - if (c) { + if (a->c) { nullify_over(ctx); } - dest = dest_gpr(ctx, rt); - src = load_gpr(ctx, rr); - if (is_se) { + dest = dest_gpr(ctx, a->t); + src = load_gpr(ctx, a->r); + if (a->se) { tcg_gen_sextract_reg(dest, src, cpos, len); } else { tcg_gen_extract_reg(dest, src, cpos, len); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static const DisasInsn table_sh_ex[] = { - { 0xd0000000u, 0xfc001fe0u, trans_shrpw_sar }, - { 0xd0000800u, 0xfc001c00u, trans_shrpw_imm }, - { 0xd0001000u, 0xfc001be0u, trans_extrw_sar }, - { 0xd0001800u, 0xfc001800u, trans_extrw_imm }, -}; - -static DisasJumpType trans_depw_imm_c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_depwi_imm(DisasContext *ctx, arg_depwi_imm *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned cpos = extract32(insn, 5, 5); - unsigned nz = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - target_sreg val = low_sextract(insn, 16, 5); - unsigned rt = extract32(insn, 21, 5); - unsigned len = 32 - clen; + unsigned len = 32 - a->clen; target_sreg mask0, mask1; TCGv_reg dest; - if (c) { + if (a->c) { nullify_over(ctx); } - if (cpos + len > 32) { - len = 32 - cpos; + if (a->cpos + len > 32) { + len = 32 - a->cpos; } - dest = dest_gpr(ctx, rt); - mask0 = deposit64(0, cpos, len, val); - mask1 = deposit64(-1, cpos, len, val); + dest = dest_gpr(ctx, a->t); + mask0 = deposit64(0, a->cpos, len, a->i); + mask1 = deposit64(-1, a->cpos, len, a->i); - if (nz) { - TCGv_reg src = load_gpr(ctx, rt); + if (a->nz) { + TCGv_reg src = load_gpr(ctx, a->t); if (mask1 != -1) { tcg_gen_andi_reg(dest, src, mask1); src = dest; @@ -3670,75 +3300,58 @@ static DisasJumpType trans_depw_imm_c(DisasContext *ctx, uint32_t insn, } else { tcg_gen_movi_reg(dest, mask0); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_depw_imm(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_depw_imm(DisasContext *ctx, arg_depw_imm *a) { - unsigned clen = extract32(insn, 0, 5); - unsigned cpos = extract32(insn, 5, 5); - unsigned nz = extract32(insn, 10, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rr = extract32(insn, 16, 5); - unsigned rt = extract32(insn, 21, 5); - unsigned rs = nz ? rt : 0; - unsigned len = 32 - clen; + unsigned rs = a->nz ? a->t : 0; + unsigned len = 32 - a->clen; TCGv_reg dest, val; - if (c) { + if (a->c) { nullify_over(ctx); } - if (cpos + len > 32) { - len = 32 - cpos; + if (a->cpos + len > 32) { + len = 32 - a->cpos; } - dest = dest_gpr(ctx, rt); - val = load_gpr(ctx, rr); + dest = dest_gpr(ctx, a->t); + val = load_gpr(ctx, a->r); if (rs == 0) { - tcg_gen_deposit_z_reg(dest, val, cpos, len); + tcg_gen_deposit_z_reg(dest, val, a->cpos, len); } else { - tcg_gen_deposit_reg(dest, cpu_gr[rs], val, cpos, len); + tcg_gen_deposit_reg(dest, cpu_gr[rs], val, a->cpos, len); } - save_gpr(ctx, rt, dest); + save_gpr(ctx, a->t, dest); /* Install the new nullification. */ cond_free(&ctx->null_cond); - if (c) { - ctx->null_cond = do_sed_cond(c, dest); + if (a->c) { + ctx->null_cond = do_sed_cond(a->c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_depw_sar(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool do_depw_sar(DisasContext *ctx, unsigned rt, unsigned c, + unsigned nz, unsigned clen, TCGv_reg val) { - unsigned clen = extract32(insn, 0, 5); - unsigned nz = extract32(insn, 10, 1); - unsigned i = extract32(insn, 12, 1); - unsigned c = extract32(insn, 13, 3); - unsigned rt = extract32(insn, 21, 5); unsigned rs = nz ? rt : 0; unsigned len = 32 - clen; - TCGv_reg val, mask, tmp, shift, dest; + TCGv_reg mask, tmp, shift, dest; unsigned msb = 1U << (len - 1); if (c) { nullify_over(ctx); } - if (i) { - val = load_const(ctx, low_sextract(insn, 16, 5)); - } else { - val = load_gpr(ctx, extract32(insn, 16, 5)); - } dest = dest_gpr(ctx, rt); shift = tcg_temp_new(); tmp = tcg_temp_new(); @@ -3766,20 +3379,21 @@ static DisasJumpType trans_depw_sar(DisasContext *ctx, uint32_t insn, if (c) { ctx->null_cond = do_sed_cond(c, dest); } - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static const DisasInsn table_depw[] = { - { 0xd4000000u, 0xfc000be0u, trans_depw_sar }, - { 0xd4000800u, 0xfc001800u, trans_depw_imm }, - { 0xd4001800u, 0xfc001800u, trans_depw_imm_c }, -}; - -static DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l) +static bool trans_depw_sar(DisasContext *ctx, arg_depw_sar *a) +{ + return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_gpr(ctx, a->r)); +} + +static bool trans_depwi_sar(DisasContext *ctx, arg_depwi_sar *a) +{ + return do_depw_sar(ctx, a->t, a->c, a->nz, a->clen, load_const(ctx, a->i)); +} + +static bool trans_be(DisasContext *ctx, arg_be *a) { - unsigned n = extract32(insn, 1, 1); - unsigned b = extract32(insn, 21, 5); - target_sreg disp = assemble_17(insn); TCGv_reg tmp; #ifdef CONFIG_USER_ONLY @@ -3791,29 +3405,28 @@ static DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l) /* Since we don't implement spaces, just branch. Do notice the special case of "be disp(*,r0)" using a direct branch to disp, so that we can goto_tb to the TB containing the syscall. */ - if (b == 0) { - return do_dbranch(ctx, disp, is_l ? 31 : 0, n); + if (a->b == 0) { + return do_dbranch(ctx, a->disp, a->l, a->n); } #else - int sp = assemble_sr3(insn); nullify_over(ctx); #endif tmp = get_temp(ctx); - tcg_gen_addi_reg(tmp, load_gpr(ctx, b), disp); + tcg_gen_addi_reg(tmp, load_gpr(ctx, a->b), a->disp); tmp = do_ibranch_priv(ctx, tmp); #ifdef CONFIG_USER_ONLY - return do_ibranch(ctx, tmp, is_l ? 31 : 0, n); + return do_ibranch(ctx, tmp, a->l, a->n); #else TCGv_i64 new_spc = tcg_temp_new_i64(); - load_spr(ctx, new_spc, sp); - if (is_l) { + load_spr(ctx, new_spc, a->sp); + if (a->l) { copy_iaoq_entry(cpu_gr[31], ctx->iaoq_n, ctx->iaoq_n_var); tcg_gen_mov_i64(cpu_sr[0], cpu_iasq_f); } - if (n && use_nullify_skip(ctx)) { + if (a->n && use_nullify_skip(ctx)) { tcg_gen_mov_reg(cpu_iaoq_f, tmp); tcg_gen_addi_reg(cpu_iaoq_b, cpu_iaoq_f, 4); tcg_gen_mov_i64(cpu_iasq_f, new_spc); @@ -3825,31 +3438,25 @@ static DisasJumpType trans_be(DisasContext *ctx, uint32_t insn, bool is_l) } tcg_gen_mov_reg(cpu_iaoq_b, tmp); tcg_gen_mov_i64(cpu_iasq_b, new_spc); - nullify_set(ctx, n); + nullify_set(ctx, a->n); } tcg_temp_free_i64(new_spc); tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx, DISAS_NORETURN); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); #endif } -static DisasJumpType trans_bl(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_bl(DisasContext *ctx, arg_bl *a) { - unsigned n = extract32(insn, 1, 1); - unsigned link = extract32(insn, 21, 5); - target_sreg disp = assemble_17(insn); - - return do_dbranch(ctx, iaoq_dest(ctx, disp), link, n); + return do_dbranch(ctx, iaoq_dest(ctx, a->disp), a->l, a->n); } -static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_b_gate(DisasContext *ctx, arg_b_gate *a) { - unsigned n = extract32(insn, 1, 1); - unsigned link = extract32(insn, 21, 5); - target_sreg disp = assemble_17(insn); - target_ureg dest = iaoq_dest(ctx, disp); + target_ureg dest = iaoq_dest(ctx, a->disp); + + nullify_over(ctx); /* Make sure the caller hasn't done something weird with the queue. * ??? This is not quite the same as the PSW[B] bit, which would be @@ -3876,7 +3483,8 @@ static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn, we will re-translate, at which point we *will* be able to find the TLB entry and determine if this is in fact a gateway page. */ if (type < 0) { - return gen_excp(ctx, EXCP_ITLB_MISS); + gen_excp(ctx, EXCP_ITLB_MISS); + return true; } /* No change for non-gateway pages or for priv decrease. */ if (type >= 4 && type - 4 < ctx->privilege) { @@ -3887,65 +3495,57 @@ static DisasJumpType trans_b_gate(DisasContext *ctx, uint32_t insn, } #endif - return do_dbranch(ctx, dest, link, n); + if (a->l) { + TCGv_reg tmp = dest_gpr(ctx, a->l); + if (ctx->privilege < 3) { + tcg_gen_andi_reg(tmp, tmp, -4); + } + tcg_gen_ori_reg(tmp, tmp, ctx->privilege); + save_gpr(ctx, a->l, tmp); + } + + return do_dbranch(ctx, dest, 0, a->n); } -static DisasJumpType trans_bl_long(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_blr(DisasContext *ctx, arg_blr *a) { - unsigned n = extract32(insn, 1, 1); - target_sreg disp = assemble_22(insn); - - return do_dbranch(ctx, iaoq_dest(ctx, disp), 2, n); + if (a->x) { + TCGv_reg tmp = get_temp(ctx); + tcg_gen_shli_reg(tmp, load_gpr(ctx, a->x), 3); + tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8); + /* The computation here never changes privilege level. */ + return do_ibranch(ctx, tmp, a->l, a->n); + } else { + /* BLR R0,RX is a good way to load PC+8 into RX. */ + return do_dbranch(ctx, ctx->iaoq_f + 8, a->l, a->n); + } } -static DisasJumpType trans_blr(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_bv(DisasContext *ctx, arg_bv *a) { - unsigned n = extract32(insn, 1, 1); - unsigned rx = extract32(insn, 16, 5); - unsigned link = extract32(insn, 21, 5); - TCGv_reg tmp = get_temp(ctx); - - tcg_gen_shli_reg(tmp, load_gpr(ctx, rx), 3); - tcg_gen_addi_reg(tmp, tmp, ctx->iaoq_f + 8); - /* The computation here never changes privilege level. */ - return do_ibranch(ctx, tmp, link, n); -} - -static DisasJumpType trans_bv(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned n = extract32(insn, 1, 1); - unsigned rx = extract32(insn, 16, 5); - unsigned rb = extract32(insn, 21, 5); TCGv_reg dest; - if (rx == 0) { - dest = load_gpr(ctx, rb); + if (a->x == 0) { + dest = load_gpr(ctx, a->b); } else { dest = get_temp(ctx); - tcg_gen_shli_reg(dest, load_gpr(ctx, rx), 3); - tcg_gen_add_reg(dest, dest, load_gpr(ctx, rb)); + tcg_gen_shli_reg(dest, load_gpr(ctx, a->x), 3); + tcg_gen_add_reg(dest, dest, load_gpr(ctx, a->b)); } dest = do_ibranch_priv(ctx, dest); - return do_ibranch(ctx, dest, 0, n); + return do_ibranch(ctx, dest, 0, a->n); } -static DisasJumpType trans_bve(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_bve(DisasContext *ctx, arg_bve *a) { - unsigned n = extract32(insn, 1, 1); - unsigned rb = extract32(insn, 21, 5); - unsigned link = extract32(insn, 13, 1) ? 2 : 0; TCGv_reg dest; #ifdef CONFIG_USER_ONLY - dest = do_ibranch_priv(ctx, load_gpr(ctx, rb)); - return do_ibranch(ctx, dest, link, n); + dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); + return do_ibranch(ctx, dest, a->l, a->n); #else nullify_over(ctx); - dest = do_ibranch_priv(ctx, load_gpr(ctx, rb)); + dest = do_ibranch_priv(ctx, load_gpr(ctx, a->b)); copy_iaoq_entry(cpu_iaoq_f, ctx->iaoq_b, cpu_iaoq_b); if (ctx->iaoq_b == -1) { @@ -3953,158 +3553,268 @@ static DisasJumpType trans_bve(DisasContext *ctx, uint32_t insn, } copy_iaoq_entry(cpu_iaoq_b, -1, dest); tcg_gen_mov_i64(cpu_iasq_b, space_select(ctx, 0, dest)); - if (link) { - copy_iaoq_entry(cpu_gr[link], ctx->iaoq_n, ctx->iaoq_n_var); + if (a->l) { + copy_iaoq_entry(cpu_gr[a->l], ctx->iaoq_n, ctx->iaoq_n_var); } - nullify_set(ctx, n); + nullify_set(ctx, a->n); tcg_gen_lookup_and_goto_ptr(); - return nullify_end(ctx, DISAS_NORETURN); + ctx->base.is_jmp = DISAS_NORETURN; + return nullify_end(ctx); #endif } -static const DisasInsn table_branch[] = { - { 0xe8000000u, 0xfc006000u, trans_bl }, /* B,L and B,L,PUSH */ - { 0xe800a000u, 0xfc00e000u, trans_bl_long }, - { 0xe8004000u, 0xfc00fffdu, trans_blr }, - { 0xe800c000u, 0xfc00fffdu, trans_bv }, - { 0xe800d000u, 0xfc00dffcu, trans_bve }, - { 0xe8002000u, 0xfc00e000u, trans_b_gate }, -}; +/* + * Float class 0 + */ -static DisasJumpType trans_fop_wew_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_wew(ctx, rt, ra, di->f.wew); -} - -static DisasJumpType trans_fop_wew_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = assemble_rt64(insn); - unsigned ra = assemble_ra64(insn); - return do_fop_wew(ctx, rt, ra, di->f.wew); -} - -static DisasJumpType trans_fop_ded(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_ded(ctx, rt, ra, di->f.ded); -} - -static DisasJumpType trans_fop_wed_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_wed(ctx, rt, ra, di->f.wed); -} - -static DisasJumpType trans_fop_wed_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = assemble_rt64(insn); - unsigned ra = extract32(insn, 21, 5); - return do_fop_wed(ctx, rt, ra, di->f.wed); -} - -static DisasJumpType trans_fop_dew_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_dew(ctx, rt, ra, di->f.dew); -} - -static DisasJumpType trans_fop_dew_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned ra = assemble_ra64(insn); - return do_fop_dew(ctx, rt, ra, di->f.dew); -} - -static DisasJumpType trans_fop_weww_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_weww(ctx, rt, ra, rb, di->f.weww); -} - -static DisasJumpType trans_fop_weww_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = assemble_rt64(insn); - unsigned rb = assemble_rb64(insn); - unsigned ra = assemble_ra64(insn); - return do_fop_weww(ctx, rt, ra, rb, di->f.weww); -} - -static DisasJumpType trans_fop_dedd(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned rt = extract32(insn, 0, 5); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fop_dedd(ctx, rt, ra, rb, di->f.dedd); -} - -static void gen_fcpy_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static void gen_fcpy_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { tcg_gen_mov_i32(dst, src); } +static bool trans_fcpy_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_fcpy_f); +} + static void gen_fcpy_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { tcg_gen_mov_i64(dst, src); } -static void gen_fabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static bool trans_fcpy_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_fcpy_d); +} + +static void gen_fabs_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { tcg_gen_andi_i32(dst, src, INT32_MAX); } +static bool trans_fabs_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_fabs_f); +} + static void gen_fabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { tcg_gen_andi_i64(dst, src, INT64_MAX); } -static void gen_fneg_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static bool trans_fabs_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_fabs_d); +} + +static bool trans_fsqrt_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fsqrt_s); +} + +static bool trans_fsqrt_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fsqrt_d); +} + +static bool trans_frnd_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_frnd_s); +} + +static bool trans_frnd_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_frnd_d); +} + +static void gen_fneg_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { tcg_gen_xori_i32(dst, src, INT32_MIN); } +static bool trans_fneg_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_fneg_f); +} + static void gen_fneg_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { tcg_gen_xori_i64(dst, src, INT64_MIN); } -static void gen_fnegabs_s(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) +static bool trans_fneg_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_fneg_d); +} + +static void gen_fnegabs_f(TCGv_i32 dst, TCGv_env unused, TCGv_i32 src) { tcg_gen_ori_i32(dst, src, INT32_MIN); } +static bool trans_fnegabs_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_fnegabs_f); +} + static void gen_fnegabs_d(TCGv_i64 dst, TCGv_env unused, TCGv_i64 src) { tcg_gen_ori_i64(dst, src, INT64_MIN); } -static DisasJumpType do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb, - unsigned y, unsigned c) +static bool trans_fnegabs_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_fnegabs_d); +} + +/* + * Float class 1 + */ + +static bool trans_fcnv_d_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_s); +} + +static bool trans_fcnv_f_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_d); +} + +static bool trans_fcnv_w_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_w_s); +} + +static bool trans_fcnv_q_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_dw_s); +} + +static bool trans_fcnv_w_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_w_d); +} + +static bool trans_fcnv_q_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_dw_d); +} + +static bool trans_fcnv_f_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_w); +} + +static bool trans_fcnv_d_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_w); +} + +static bool trans_fcnv_f_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_dw); +} + +static bool trans_fcnv_d_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_d_dw); +} + +static bool trans_fcnv_t_f_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_t_s_w); +} + +static bool trans_fcnv_t_d_w(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_t_d_w); +} + +static bool trans_fcnv_t_f_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_t_s_dw); +} + +static bool trans_fcnv_t_d_q(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_t_d_dw); +} + +static bool trans_fcnv_uw_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_uw_s); +} + +static bool trans_fcnv_uq_f(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_udw_s); +} + +static bool trans_fcnv_uw_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_uw_d); +} + +static bool trans_fcnv_uq_d(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_udw_d); +} + +static bool trans_fcnv_f_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_s_uw); +} + +static bool trans_fcnv_d_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_d_uw); +} + +static bool trans_fcnv_f_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_s_udw); +} + +static bool trans_fcnv_d_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_d_udw); +} + +static bool trans_fcnv_t_f_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wew(ctx, a->t, a->r, gen_helper_fcnv_t_s_uw); +} + +static bool trans_fcnv_t_d_uw(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_wed(ctx, a->t, a->r, gen_helper_fcnv_t_d_uw); +} + +static bool trans_fcnv_t_f_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_dew(ctx, a->t, a->r, gen_helper_fcnv_t_s_udw); +} + +static bool trans_fcnv_t_d_uq(DisasContext *ctx, arg_fclass01 *a) +{ + return do_fop_ded(ctx, a->t, a->r, gen_helper_fcnv_t_d_udw); +} + +/* + * Float class 2 + */ + +static bool trans_fcmp_f(DisasContext *ctx, arg_fclass2 *a) { TCGv_i32 ta, tb, tc, ty; nullify_over(ctx); - ta = load_frw0_i32(ra); - tb = load_frw0_i32(rb); - ty = tcg_const_i32(y); - tc = tcg_const_i32(c); + ta = load_frw0_i32(a->r1); + tb = load_frw0_i32(a->r2); + ty = tcg_const_i32(a->y); + tc = tcg_const_i32(a->c); gen_helper_fcmp_s(cpu_env, ta, tb, ty, tc); @@ -4113,45 +3823,20 @@ static DisasJumpType do_fcmp_s(DisasContext *ctx, unsigned ra, unsigned rb, tcg_temp_free_i32(ty); tcg_temp_free_i32(tc); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_fcmp_s_0c(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fcmp_d(DisasContext *ctx, arg_fclass2 *a) { - unsigned c = extract32(insn, 0, 5); - unsigned y = extract32(insn, 13, 3); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); - return do_fcmp_s(ctx, ra, rb, y, c); -} - -static DisasJumpType trans_fcmp_s_0e(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned c = extract32(insn, 0, 5); - unsigned y = extract32(insn, 13, 3); - unsigned rb = assemble_rb64(insn); - unsigned ra = assemble_ra64(insn); - return do_fcmp_s(ctx, ra, rb, y, c); -} - -static DisasJumpType trans_fcmp_d(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned c = extract32(insn, 0, 5); - unsigned y = extract32(insn, 13, 3); - unsigned rb = extract32(insn, 16, 5); - unsigned ra = extract32(insn, 21, 5); TCGv_i64 ta, tb; TCGv_i32 tc, ty; nullify_over(ctx); - ta = load_frd0(ra); - tb = load_frd0(rb); - ty = tcg_const_i32(y); - tc = tcg_const_i32(c); + ta = load_frd0(a->r1); + tb = load_frd0(a->r2); + ty = tcg_const_i32(a->y); + tc = tcg_const_i32(a->c); gen_helper_fcmp_d(cpu_env, ta, tb, ty, tc); @@ -4160,513 +3845,235 @@ static DisasJumpType trans_fcmp_d(DisasContext *ctx, uint32_t insn, tcg_temp_free_i32(ty); tcg_temp_free_i32(tc); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_ftest_t(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_ftest(DisasContext *ctx, arg_ftest *a) { - unsigned y = extract32(insn, 13, 3); - unsigned cbit = (y ^ 1) - 1; TCGv_reg t; nullify_over(ctx); - t = tcg_temp_new(); - tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); - tcg_gen_extract_reg(t, t, 21 - cbit, 1); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); - tcg_temp_free(t); - - return nullify_end(ctx, DISAS_NEXT); -} - -static DisasJumpType trans_ftest_q(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) -{ - unsigned c = extract32(insn, 0, 5); - int mask; - bool inv = false; - TCGv_reg t; - - nullify_over(ctx); - - t = tcg_temp_new(); + t = get_temp(ctx); tcg_gen_ld32u_reg(t, cpu_env, offsetof(CPUHPPAState, fr0_shadow)); - switch (c) { - case 0: /* simple */ - tcg_gen_andi_reg(t, t, 0x4000000); - ctx->null_cond = cond_make_0(TCG_COND_NE, t); - goto done; - case 2: /* rej */ - inv = true; - /* fallthru */ - case 1: /* acc */ - mask = 0x43ff800; - break; - case 6: /* rej8 */ - inv = true; - /* fallthru */ - case 5: /* acc8 */ - mask = 0x43f8000; - break; - case 9: /* acc6 */ - mask = 0x43e0000; - break; - case 13: /* acc4 */ - mask = 0x4380000; - break; - case 17: /* acc2 */ - mask = 0x4200000; - break; - default: - return gen_illegal(ctx); - } - if (inv) { - TCGv_reg c = load_const(ctx, mask); - tcg_gen_or_reg(t, t, c); - ctx->null_cond = cond_make(TCG_COND_EQ, t, c); + if (a->y == 1) { + int mask; + bool inv = false; + + switch (a->c) { + case 0: /* simple */ + tcg_gen_andi_reg(t, t, 0x4000000); + ctx->null_cond = cond_make_0(TCG_COND_NE, t); + goto done; + case 2: /* rej */ + inv = true; + /* fallthru */ + case 1: /* acc */ + mask = 0x43ff800; + break; + case 6: /* rej8 */ + inv = true; + /* fallthru */ + case 5: /* acc8 */ + mask = 0x43f8000; + break; + case 9: /* acc6 */ + mask = 0x43e0000; + break; + case 13: /* acc4 */ + mask = 0x4380000; + break; + case 17: /* acc2 */ + mask = 0x4200000; + break; + default: + gen_illegal(ctx); + return true; + } + if (inv) { + TCGv_reg c = load_const(ctx, mask); + tcg_gen_or_reg(t, t, c); + ctx->null_cond = cond_make(TCG_COND_EQ, t, c); + } else { + tcg_gen_andi_reg(t, t, mask); + ctx->null_cond = cond_make_0(TCG_COND_EQ, t); + } } else { - tcg_gen_andi_reg(t, t, mask); - ctx->null_cond = cond_make_0(TCG_COND_EQ, t); + unsigned cbit = (a->y ^ 1) - 1; + + tcg_gen_extract_reg(t, t, 21 - cbit, 1); + ctx->null_cond = cond_make_0(TCG_COND_NE, t); + tcg_temp_free(t); } + done: - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_xmpyu(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +/* + * Float class 2 + */ + +static bool trans_fadd_f(DisasContext *ctx, arg_fclass3 *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned rb = assemble_rb64(insn); - unsigned ra = assemble_ra64(insn); - TCGv_i64 a, b; + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fadd_s); +} + +static bool trans_fadd_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fadd_d); +} + +static bool trans_fsub_f(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fsub_s); +} + +static bool trans_fsub_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fsub_d); +} + +static bool trans_fmpy_f(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fmpy_s); +} + +static bool trans_fmpy_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fmpy_d); +} + +static bool trans_fdiv_f(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_weww(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_s); +} + +static bool trans_fdiv_d(DisasContext *ctx, arg_fclass3 *a) +{ + return do_fop_dedd(ctx, a->t, a->r1, a->r2, gen_helper_fdiv_d); +} + +static bool trans_xmpyu(DisasContext *ctx, arg_xmpyu *a) +{ + TCGv_i64 x, y; nullify_over(ctx); - a = load_frw0_i64(ra); - b = load_frw0_i64(rb); - tcg_gen_mul_i64(a, a, b); - save_frd(rt, a); - tcg_temp_free_i64(a); - tcg_temp_free_i64(b); + x = load_frw0_i64(a->r1); + y = load_frw0_i64(a->r2); + tcg_gen_mul_i64(x, x, y); + save_frd(a->t, x); + tcg_temp_free_i64(x); + tcg_temp_free_i64(y); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -#define FOP_DED trans_fop_ded, .f.ded -#define FOP_DEDD trans_fop_dedd, .f.dedd - -#define FOP_WEW trans_fop_wew_0c, .f.wew -#define FOP_DEW trans_fop_dew_0c, .f.dew -#define FOP_WED trans_fop_wed_0c, .f.wed -#define FOP_WEWW trans_fop_weww_0c, .f.weww - -static const DisasInsn table_float_0c[] = { - /* floating point class zero */ - { 0x30004000, 0xfc1fffe0, FOP_WEW = gen_fcpy_s }, - { 0x30006000, 0xfc1fffe0, FOP_WEW = gen_fabs_s }, - { 0x30008000, 0xfc1fffe0, FOP_WEW = gen_helper_fsqrt_s }, - { 0x3000a000, 0xfc1fffe0, FOP_WEW = gen_helper_frnd_s }, - { 0x3000c000, 0xfc1fffe0, FOP_WEW = gen_fneg_s }, - { 0x3000e000, 0xfc1fffe0, FOP_WEW = gen_fnegabs_s }, - - { 0x30004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d }, - { 0x30006800, 0xfc1fffe0, FOP_DED = gen_fabs_d }, - { 0x30008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d }, - { 0x3000a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d }, - { 0x3000c800, 0xfc1fffe0, FOP_DED = gen_fneg_d }, - { 0x3000e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d }, - - /* floating point class three */ - { 0x30000600, 0xfc00ffe0, FOP_WEWW = gen_helper_fadd_s }, - { 0x30002600, 0xfc00ffe0, FOP_WEWW = gen_helper_fsub_s }, - { 0x30004600, 0xfc00ffe0, FOP_WEWW = gen_helper_fmpy_s }, - { 0x30006600, 0xfc00ffe0, FOP_WEWW = gen_helper_fdiv_s }, - - { 0x30000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d }, - { 0x30002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d }, - { 0x30004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d }, - { 0x30006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d }, - - /* floating point class one */ - /* float/float */ - { 0x30000a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_s }, - { 0x30002200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_d }, - /* int/float */ - { 0x30008200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_w_s }, - { 0x30008a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_dw_s }, - { 0x3000a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_w_d }, - { 0x3000aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d }, - /* float/int */ - { 0x30010200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_w }, - { 0x30010a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_w }, - { 0x30012200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_dw }, - { 0x30012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw }, - /* float/int truncate */ - { 0x30018200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_w }, - { 0x30018a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_w }, - { 0x3001a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_dw }, - { 0x3001aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw }, - /* uint/float */ - { 0x30028200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_uw_s }, - { 0x30028a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_udw_s }, - { 0x3002a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_uw_d }, - { 0x3002aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d }, - /* float/uint */ - { 0x30030200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_s_uw }, - { 0x30030a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_d_uw }, - { 0x30032200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_s_udw }, - { 0x30032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw }, - /* float/uint truncate */ - { 0x30038200, 0xfc1fffe0, FOP_WEW = gen_helper_fcnv_t_s_uw }, - { 0x30038a00, 0xfc1fffe0, FOP_WED = gen_helper_fcnv_t_d_uw }, - { 0x3003a200, 0xfc1fffe0, FOP_DEW = gen_helper_fcnv_t_s_udw }, - { 0x3003aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw }, - - /* floating point class two */ - { 0x30000400, 0xfc001fe0, trans_fcmp_s_0c }, - { 0x30000c00, 0xfc001fe0, trans_fcmp_d }, - { 0x30002420, 0xffffffe0, trans_ftest_q }, - { 0x30000420, 0xffff1fff, trans_ftest_t }, - - /* FID. Note that ra == rt == 0, which via fcpy puts 0 into fr0. - This is machine/revision == 0, which is reserved for simulator. */ - { 0x30000000, 0xffffffff, FOP_WEW = gen_fcpy_s }, -}; - -#undef FOP_WEW -#undef FOP_DEW -#undef FOP_WED -#undef FOP_WEWW -#define FOP_WEW trans_fop_wew_0e, .f.wew -#define FOP_DEW trans_fop_dew_0e, .f.dew -#define FOP_WED trans_fop_wed_0e, .f.wed -#define FOP_WEWW trans_fop_weww_0e, .f.weww - -static const DisasInsn table_float_0e[] = { - /* floating point class zero */ - { 0x38004000, 0xfc1fff20, FOP_WEW = gen_fcpy_s }, - { 0x38006000, 0xfc1fff20, FOP_WEW = gen_fabs_s }, - { 0x38008000, 0xfc1fff20, FOP_WEW = gen_helper_fsqrt_s }, - { 0x3800a000, 0xfc1fff20, FOP_WEW = gen_helper_frnd_s }, - { 0x3800c000, 0xfc1fff20, FOP_WEW = gen_fneg_s }, - { 0x3800e000, 0xfc1fff20, FOP_WEW = gen_fnegabs_s }, - - { 0x38004800, 0xfc1fffe0, FOP_DED = gen_fcpy_d }, - { 0x38006800, 0xfc1fffe0, FOP_DED = gen_fabs_d }, - { 0x38008800, 0xfc1fffe0, FOP_DED = gen_helper_fsqrt_d }, - { 0x3800a800, 0xfc1fffe0, FOP_DED = gen_helper_frnd_d }, - { 0x3800c800, 0xfc1fffe0, FOP_DED = gen_fneg_d }, - { 0x3800e800, 0xfc1fffe0, FOP_DED = gen_fnegabs_d }, - - /* floating point class three */ - { 0x38000600, 0xfc00ef20, FOP_WEWW = gen_helper_fadd_s }, - { 0x38002600, 0xfc00ef20, FOP_WEWW = gen_helper_fsub_s }, - { 0x38004600, 0xfc00ef20, FOP_WEWW = gen_helper_fmpy_s }, - { 0x38006600, 0xfc00ef20, FOP_WEWW = gen_helper_fdiv_s }, - - { 0x38000e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fadd_d }, - { 0x38002e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fsub_d }, - { 0x38004e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fmpy_d }, - { 0x38006e00, 0xfc00ffe0, FOP_DEDD = gen_helper_fdiv_d }, - - { 0x38004700, 0xfc00ef60, trans_xmpyu }, - - /* floating point class one */ - /* float/float */ - { 0x38000a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_s }, - { 0x38002200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_d }, - /* int/float */ - { 0x38008200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_w_s }, - { 0x38008a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_dw_s }, - { 0x3800a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_w_d }, - { 0x3800aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_dw_d }, - /* float/int */ - { 0x38010200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_w }, - { 0x38010a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_w }, - { 0x38012200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_dw }, - { 0x38012a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_dw }, - /* float/int truncate */ - { 0x38018200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_w }, - { 0x38018a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_w }, - { 0x3801a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_dw }, - { 0x3801aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_dw }, - /* uint/float */ - { 0x38028200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_uw_s }, - { 0x38028a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_udw_s }, - { 0x3802a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_uw_d }, - { 0x3802aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_udw_d }, - /* float/uint */ - { 0x38030200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_s_uw }, - { 0x38030a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_d_uw }, - { 0x38032200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_s_udw }, - { 0x38032a00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_d_udw }, - /* float/uint truncate */ - { 0x38038200, 0xfc1ffe20, FOP_WEW = gen_helper_fcnv_t_s_uw }, - { 0x38038a00, 0xfc1fffa0, FOP_WED = gen_helper_fcnv_t_d_uw }, - { 0x3803a200, 0xfc1fff60, FOP_DEW = gen_helper_fcnv_t_s_udw }, - { 0x3803aa00, 0xfc1fffe0, FOP_DED = gen_helper_fcnv_t_d_udw }, - - /* floating point class two */ - { 0x38000400, 0xfc000f60, trans_fcmp_s_0e }, - { 0x38000c00, 0xfc001fe0, trans_fcmp_d }, -}; - -#undef FOP_WEW -#undef FOP_DEW -#undef FOP_WED -#undef FOP_WEWW -#undef FOP_DED -#undef FOP_DEDD - /* Convert the fmpyadd single-precision register encodings to standard. */ static inline int fmpyadd_s_reg(unsigned r) { return (r & 16) * 2 + 16 + (r & 15); } -static DisasJumpType trans_fmpyadd(DisasContext *ctx, - uint32_t insn, bool is_sub) +static bool do_fmpyadd_s(DisasContext *ctx, arg_mpyadd *a, bool is_sub) { - unsigned tm = extract32(insn, 0, 5); - unsigned f = extract32(insn, 5, 1); - unsigned ra = extract32(insn, 6, 5); - unsigned ta = extract32(insn, 11, 5); - unsigned rm2 = extract32(insn, 16, 5); - unsigned rm1 = extract32(insn, 21, 5); + int tm = fmpyadd_s_reg(a->tm); + int ra = fmpyadd_s_reg(a->ra); + int ta = fmpyadd_s_reg(a->ta); + int rm2 = fmpyadd_s_reg(a->rm2); + int rm1 = fmpyadd_s_reg(a->rm1); nullify_over(ctx); - /* Independent multiply & add/sub, with undefined behaviour - if outputs overlap inputs. */ - if (f == 0) { - tm = fmpyadd_s_reg(tm); - ra = fmpyadd_s_reg(ra); - ta = fmpyadd_s_reg(ta); - rm2 = fmpyadd_s_reg(rm2); - rm1 = fmpyadd_s_reg(rm1); - do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s); - do_fop_weww(ctx, ta, ta, ra, - is_sub ? gen_helper_fsub_s : gen_helper_fadd_s); - } else { - do_fop_dedd(ctx, tm, rm1, rm2, gen_helper_fmpy_d); - do_fop_dedd(ctx, ta, ta, ra, - is_sub ? gen_helper_fsub_d : gen_helper_fadd_d); - } + do_fop_weww(ctx, tm, rm1, rm2, gen_helper_fmpy_s); + do_fop_weww(ctx, ta, ta, ra, + is_sub ? gen_helper_fsub_s : gen_helper_fadd_s); - return nullify_end(ctx, DISAS_NEXT); + return nullify_end(ctx); } -static DisasJumpType trans_fmpyfadd_s(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fmpyadd_f(DisasContext *ctx, arg_mpyadd *a) { - unsigned rt = assemble_rt64(insn); - unsigned neg = extract32(insn, 5, 1); - unsigned rm1 = assemble_ra64(insn); - unsigned rm2 = assemble_rb64(insn); - unsigned ra3 = assemble_rc64(insn); - TCGv_i32 a, b, c; + return do_fmpyadd_s(ctx, a, false); +} + +static bool trans_fmpysub_f(DisasContext *ctx, arg_mpyadd *a) +{ + return do_fmpyadd_s(ctx, a, true); +} + +static bool do_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a, bool is_sub) +{ + nullify_over(ctx); + + do_fop_dedd(ctx, a->tm, a->rm1, a->rm2, gen_helper_fmpy_d); + do_fop_dedd(ctx, a->ta, a->ta, a->ra, + is_sub ? gen_helper_fsub_d : gen_helper_fadd_d); + + return nullify_end(ctx); +} + +static bool trans_fmpyadd_d(DisasContext *ctx, arg_mpyadd *a) +{ + return do_fmpyadd_d(ctx, a, false); +} + +static bool trans_fmpysub_d(DisasContext *ctx, arg_mpyadd *a) +{ + return do_fmpyadd_d(ctx, a, true); +} + +static bool trans_fmpyfadd_f(DisasContext *ctx, arg_fmpyfadd_f *a) +{ + TCGv_i32 x, y, z; nullify_over(ctx); - a = load_frw0_i32(rm1); - b = load_frw0_i32(rm2); - c = load_frw0_i32(ra3); + x = load_frw0_i32(a->rm1); + y = load_frw0_i32(a->rm2); + z = load_frw0_i32(a->ra3); - if (neg) { - gen_helper_fmpynfadd_s(a, cpu_env, a, b, c); + if (a->neg) { + gen_helper_fmpynfadd_s(x, cpu_env, x, y, z); } else { - gen_helper_fmpyfadd_s(a, cpu_env, a, b, c); + gen_helper_fmpyfadd_s(x, cpu_env, x, y, z); } - tcg_temp_free_i32(b); - tcg_temp_free_i32(c); - save_frw_i32(rt, a); - tcg_temp_free_i32(a); - return nullify_end(ctx, DISAS_NEXT); + tcg_temp_free_i32(y); + tcg_temp_free_i32(z); + save_frw_i32(a->t, x); + tcg_temp_free_i32(x); + return nullify_end(ctx); } -static DisasJumpType trans_fmpyfadd_d(DisasContext *ctx, uint32_t insn, - const DisasInsn *di) +static bool trans_fmpyfadd_d(DisasContext *ctx, arg_fmpyfadd_d *a) { - unsigned rt = extract32(insn, 0, 5); - unsigned neg = extract32(insn, 5, 1); - unsigned rm1 = extract32(insn, 21, 5); - unsigned rm2 = extract32(insn, 16, 5); - unsigned ra3 = assemble_rc64(insn); - TCGv_i64 a, b, c; + TCGv_i64 x, y, z; nullify_over(ctx); - a = load_frd0(rm1); - b = load_frd0(rm2); - c = load_frd0(ra3); + x = load_frd0(a->rm1); + y = load_frd0(a->rm2); + z = load_frd0(a->ra3); - if (neg) { - gen_helper_fmpynfadd_d(a, cpu_env, a, b, c); + if (a->neg) { + gen_helper_fmpynfadd_d(x, cpu_env, x, y, z); } else { - gen_helper_fmpyfadd_d(a, cpu_env, a, b, c); + gen_helper_fmpyfadd_d(x, cpu_env, x, y, z); } - tcg_temp_free_i64(b); - tcg_temp_free_i64(c); - save_frd(rt, a); - tcg_temp_free_i64(a); - return nullify_end(ctx, DISAS_NEXT); + tcg_temp_free_i64(y); + tcg_temp_free_i64(z); + save_frd(a->t, x); + tcg_temp_free_i64(x); + return nullify_end(ctx); } -static const DisasInsn table_fp_fused[] = { - { 0xb8000000u, 0xfc000800u, trans_fmpyfadd_s }, - { 0xb8000800u, 0xfc0019c0u, trans_fmpyfadd_d } -}; - -static DisasJumpType translate_table_int(DisasContext *ctx, uint32_t insn, - const DisasInsn table[], size_t n) +static bool trans_diag(DisasContext *ctx, arg_diag *a) { - size_t i; - for (i = 0; i < n; ++i) { - if ((insn & table[i].mask) == table[i].insn) { - return table[i].trans(ctx, insn, &table[i]); - } - } - qemu_log_mask(LOG_UNIMP, "UNIMP insn %08x @ " TARGET_FMT_lx "\n", - insn, ctx->base.pc_next); - return gen_illegal(ctx); -} - -#define translate_table(ctx, insn, table) \ - translate_table_int(ctx, insn, table, ARRAY_SIZE(table)) - -static DisasJumpType translate_one(DisasContext *ctx, uint32_t insn) -{ - uint32_t opc = extract32(insn, 26, 6); - - switch (opc) { - case 0x00: /* system op */ - return translate_table(ctx, insn, table_system); - case 0x01: - return translate_table(ctx, insn, table_mem_mgmt); - case 0x02: - return translate_table(ctx, insn, table_arith_log); - case 0x03: - return translate_table(ctx, insn, table_index_mem); - case 0x06: - return trans_fmpyadd(ctx, insn, false); - case 0x08: - return trans_ldil(ctx, insn); - case 0x09: - return trans_copr_w(ctx, insn); - case 0x0A: - return trans_addil(ctx, insn); - case 0x0B: - return trans_copr_dw(ctx, insn); - case 0x0C: - return translate_table(ctx, insn, table_float_0c); - case 0x0D: - return trans_ldo(ctx, insn); - case 0x0E: - return translate_table(ctx, insn, table_float_0e); - - case 0x10: - return trans_load(ctx, insn, false, MO_UB); - case 0x11: - return trans_load(ctx, insn, false, MO_TEUW); - case 0x12: - return trans_load(ctx, insn, false, MO_TEUL); - case 0x13: - return trans_load(ctx, insn, true, MO_TEUL); - case 0x16: - return trans_fload_mod(ctx, insn); - case 0x17: - return trans_load_w(ctx, insn); - case 0x18: - return trans_store(ctx, insn, false, MO_UB); - case 0x19: - return trans_store(ctx, insn, false, MO_TEUW); - case 0x1A: - return trans_store(ctx, insn, false, MO_TEUL); - case 0x1B: - return trans_store(ctx, insn, true, MO_TEUL); - case 0x1E: - return trans_fstore_mod(ctx, insn); - case 0x1F: - return trans_store_w(ctx, insn); - - case 0x20: - return trans_cmpb(ctx, insn, true, false, false); - case 0x21: - return trans_cmpb(ctx, insn, true, true, false); - case 0x22: - return trans_cmpb(ctx, insn, false, false, false); - case 0x23: - return trans_cmpb(ctx, insn, false, true, false); - case 0x24: - return trans_cmpiclr(ctx, insn); - case 0x25: - return trans_subi(ctx, insn); - case 0x26: - return trans_fmpyadd(ctx, insn, true); - case 0x27: - return trans_cmpb(ctx, insn, true, false, true); - case 0x28: - return trans_addb(ctx, insn, true, false); - case 0x29: - return trans_addb(ctx, insn, true, true); - case 0x2A: - return trans_addb(ctx, insn, false, false); - case 0x2B: - return trans_addb(ctx, insn, false, true); - case 0x2C: - case 0x2D: - return trans_addi(ctx, insn); - case 0x2E: - return translate_table(ctx, insn, table_fp_fused); - case 0x2F: - return trans_cmpb(ctx, insn, false, false, true); - - case 0x30: - case 0x31: - return trans_bb(ctx, insn); - case 0x32: - return trans_movb(ctx, insn, false); - case 0x33: - return trans_movb(ctx, insn, true); - case 0x34: - return translate_table(ctx, insn, table_sh_ex); - case 0x35: - return translate_table(ctx, insn, table_depw); - case 0x38: - return trans_be(ctx, insn, false); - case 0x39: - return trans_be(ctx, insn, true); - case 0x3A: - return translate_table(ctx, insn, table_branch); - - case 0x04: /* spopn */ - case 0x05: /* diag */ - case 0x0F: /* product specific */ - break; - - case 0x07: /* unassigned */ - case 0x15: /* unassigned */ - case 0x1D: /* unassigned */ - case 0x37: /* unassigned */ - break; - case 0x3F: -#ifndef CONFIG_USER_ONLY - /* Unassigned, but use as system-halt. */ - if (insn == 0xfffdead0) { - return gen_hlt(ctx, 0); /* halt system */ - } - if (insn == 0xfffdead1) { - return gen_hlt(ctx, 1); /* reset system */ - } -#endif - break; - default: - break; - } - return gen_illegal(ctx); + qemu_log_mask(LOG_UNIMP, "DIAG opcode ignored\n"); + cond_free(&ctx->null_cond); + return true; } static void hppa_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) @@ -4733,7 +4140,7 @@ static bool hppa_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cs, { DisasContext *ctx = container_of(dcbase, DisasContext, base); - ctx->base.is_jmp = gen_excp(ctx, EXCP_DEBUG); + gen_excp(ctx, EXCP_DEBUG); ctx->base.pc_next += 4; return true; } @@ -4748,7 +4155,8 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) /* Execute one insn. */ #ifdef CONFIG_USER_ONLY if (ctx->base.pc_next < TARGET_PAGE_SIZE) { - ret = do_page_zero(ctx); + do_page_zero(ctx); + ret = ctx->base.is_jmp; assert(ret != DISAS_NEXT); } else #endif @@ -4773,7 +4181,10 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ret = DISAS_NEXT; } else { ctx->insn = insn; - ret = translate_one(ctx, insn); + if (!decode(ctx, insn)) { + gen_illegal(ctx); + } + ret = ctx->base.is_jmp; assert(ctx->null_lab == NULL); } } @@ -4799,29 +4210,40 @@ static void hppa_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) || ctx->null_cond.c == TCG_COND_ALWAYS)) { nullify_set(ctx, ctx->null_cond.c == TCG_COND_ALWAYS); gen_goto_tb(ctx, 0, ctx->iaoq_b, ctx->iaoq_n); - ret = DISAS_NORETURN; + ctx->base.is_jmp = ret = DISAS_NORETURN; } else { - ret = DISAS_IAQ_N_STALE; + ctx->base.is_jmp = ret = DISAS_IAQ_N_STALE; } } ctx->iaoq_f = ctx->iaoq_b; ctx->iaoq_b = ctx->iaoq_n; - ctx->base.is_jmp = ret; ctx->base.pc_next += 4; - if (ret == DISAS_NORETURN || ret == DISAS_IAQ_N_UPDATED) { - return; - } - if (ctx->iaoq_f == -1) { - tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b); - copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); + switch (ret) { + case DISAS_NORETURN: + case DISAS_IAQ_N_UPDATED: + break; + + case DISAS_NEXT: + case DISAS_IAQ_N_STALE: + case DISAS_IAQ_N_STALE_EXIT: + if (ctx->iaoq_f == -1) { + tcg_gen_mov_reg(cpu_iaoq_f, cpu_iaoq_b); + copy_iaoq_entry(cpu_iaoq_b, ctx->iaoq_n, ctx->iaoq_n_var); #ifndef CONFIG_USER_ONLY - tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); + tcg_gen_mov_i64(cpu_iasq_f, cpu_iasq_b); #endif - nullify_save(ctx); - ctx->base.is_jmp = DISAS_IAQ_N_UPDATED; - } else if (ctx->iaoq_b == -1) { - tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var); + nullify_save(ctx); + ctx->base.is_jmp = (ret == DISAS_IAQ_N_STALE_EXIT + ? DISAS_EXIT + : DISAS_IAQ_N_UPDATED); + } else if (ctx->iaoq_b == -1) { + tcg_gen_mov_reg(cpu_iaoq_b, ctx->iaoq_n_var); + } + break; + + default: + g_assert_not_reached(); } } @@ -4843,11 +4265,12 @@ static void hppa_tr_tb_stop(DisasContextBase *dcbase, CPUState *cs) case DISAS_IAQ_N_UPDATED: if (ctx->base.singlestep_enabled) { gen_excp_1(EXCP_DEBUG); - } else if (is_jmp == DISAS_IAQ_N_STALE_EXIT) { - tcg_gen_exit_tb(NULL, 0); - } else { + } else if (is_jmp != DISAS_IAQ_N_STALE_EXIT) { tcg_gen_lookup_and_goto_ptr(); } + /* FALLTHRU */ + case DISAS_EXIT: + tcg_gen_exit_tb(NULL, 0); break; default: g_assert_not_reached(); diff --git a/target/i386/Makefile.objs b/target/i386/Makefile.objs index 04678f5503..48e0c28434 100644 --- a/target/i386/Makefile.objs +++ b/target/i386/Makefile.objs @@ -3,17 +3,20 @@ obj-$(CONFIG_TCG) += translate.o obj-$(CONFIG_TCG) += bpt_helper.o cc_helper.o excp_helper.o fpu_helper.o obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o mpx_helper.o obj-$(CONFIG_TCG) += seg_helper.o smm_helper.o svm_helper.o -obj-$(CONFIG_SOFTMMU) += machine.o arch_memory_mapping.o arch_dump.o monitor.o -obj-$(CONFIG_KVM) += kvm.o hyperv.o -obj-$(CONFIG_SEV) += sev.o obj-$(call lnot,$(CONFIG_KVM)) += kvm-stub.o -obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o -# HAX support -ifdef CONFIG_WIN32 +ifeq ($(CONFIG_SOFTMMU),y) +obj-y += machine.o arch_memory_mapping.o arch_dump.o monitor.o +obj-$(CONFIG_KVM) += kvm.o +obj-$(CONFIG_HYPERV) += hyperv.o +obj-$(call lnot,$(CONFIG_HYPERV)) += hyperv-stub.o +ifeq ($(CONFIG_WIN32),y) obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-windows.o endif -ifdef CONFIG_DARWIN -obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-darwin.o -obj-$(CONFIG_HVF) += hvf/ +ifeq ($(CONFIG_POSIX),y) +obj-$(CONFIG_HAX) += hax-all.o hax-mem.o hax-posix.o endif +obj-$(CONFIG_HVF) += hvf/ obj-$(CONFIG_WHPX) += whpx-all.o +endif +obj-$(CONFIG_SEV) += sev.o +obj-$(call lnot,$(CONFIG_SEV)) += sev-stub.o diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 723e02221e..d6bb57d210 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -41,6 +41,7 @@ #include "qapi/visitor.h" #include "qom/qom-qobject.h" #include "sysemu/arch_init.h" +#include "qapi/qapi-commands-target.h" #include "standard-headers/asm-x86/kvm_para.h" @@ -770,17 +771,36 @@ static void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, /* missing: CPUID_XSAVE_XSAVEC, CPUID_XSAVE_XSAVES */ +typedef enum FeatureWordType { + CPUID_FEATURE_WORD, + MSR_FEATURE_WORD, +} FeatureWordType; + typedef struct FeatureWordInfo { + FeatureWordType type; /* feature flags names are taken from "Intel Processor Identification and * the CPUID Instruction" and AMD's "CPUID Specification". * In cases of disagreement between feature naming conventions, * aliases may be added. */ const char *feat_names[32]; - uint32_t cpuid_eax; /* Input EAX for CPUID */ - bool cpuid_needs_ecx; /* CPUID instruction uses ECX as input */ - uint32_t cpuid_ecx; /* Input ECX value for CPUID */ - int cpuid_reg; /* output register (R_* constant) */ + union { + /* If type==CPUID_FEATURE_WORD */ + struct { + uint32_t eax; /* Input EAX for CPUID */ + bool needs_ecx; /* CPUID instruction uses ECX as input */ + uint32_t ecx; /* Input ECX value for CPUID */ + int reg; /* output register (R_* constant) */ + } cpuid; + /* If type==MSR_FEATURE_WORD */ + struct { + uint32_t index; + struct { /*CPUID that enumerate this MSR*/ + FeatureWord cpuid_class; + uint32_t cpuid_flag; + } cpuid_dep; + } msr; + }; uint32_t tcg_features; /* Feature flags supported by TCG */ uint32_t unmigratable_flags; /* Feature flags known to be unmigratable */ uint32_t migratable_flags; /* Feature flags known to be migratable */ @@ -790,6 +810,7 @@ typedef struct FeatureWordInfo { static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_1_EDX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "fpu", "vme", "de", "pse", "tsc", "msr", "pae", "mce", @@ -800,10 +821,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "fxsr", "sse", "sse2", "ss", "ht" /* Intel htt */, "tm", "ia64", "pbe", }, - .cpuid_eax = 1, .cpuid_reg = R_EDX, + .cpuid = {.eax = 1, .reg = R_EDX, }, .tcg_features = TCG_FEATURES, }, [FEAT_1_ECX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "pni" /* Intel,AMD sse3 */, "pclmulqdq", "dtes64", "monitor", "ds-cpl", "vmx", "smx", "est", @@ -814,7 +836,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "tsc-deadline", "aes", "xsave", NULL /* osxsave */, "avx", "f16c", "rdrand", "hypervisor", }, - .cpuid_eax = 1, .cpuid_reg = R_ECX, + .cpuid = { .eax = 1, .reg = R_ECX, }, .tcg_features = TCG_EXT_FEATURES, }, /* Feature names that are already defined on feature_name[] but @@ -823,6 +845,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { * to features[FEAT_8000_0001_EDX] if and only if CPU vendor is AMD. */ [FEAT_8000_0001_EDX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL /* fpu */, NULL /* vme */, NULL /* de */, NULL /* pse */, NULL /* tsc */, NULL /* msr */, NULL /* pae */, NULL /* mce */, @@ -833,10 +856,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL /* fxsr */, "fxsr-opt", "pdpe1gb", "rdtscp", NULL, "lm", "3dnowext", "3dnow", }, - .cpuid_eax = 0x80000001, .cpuid_reg = R_EDX, + .cpuid = { .eax = 0x80000001, .reg = R_EDX, }, .tcg_features = TCG_EXT2_FEATURES, }, [FEAT_8000_0001_ECX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "lahf-lm", "cmp-legacy", "svm", "extapic", "cr8legacy", "abm", "sse4a", "misalignsse", @@ -847,10 +871,17 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "perfctr-nb", NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x80000001, .cpuid_reg = R_ECX, + .cpuid = { .eax = 0x80000001, .reg = R_ECX, }, .tcg_features = TCG_EXT3_FEATURES, + /* + * TOPOEXT is always allowed but can't be enabled blindly by + * "-cpu host", as it requires consistent cache topology info + * to be provided so it doesn't confuse guests. + */ + .no_autoenable_flags = CPUID_EXT3_TOPOEXT, }, [FEAT_C000_0001_EDX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, "xstore", "xstore-en", NULL, NULL, "xcrypt", "xcrypt-en", @@ -861,24 +892,26 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0xC0000001, .cpuid_reg = R_EDX, + .cpuid = { .eax = 0xC0000001, .reg = R_EDX, }, .tcg_features = TCG_EXT4_FEATURES, }, [FEAT_KVM] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "kvmclock", "kvm-nopiodelay", "kvm-mmu", "kvmclock", "kvm-asyncpf", "kvm-steal-time", "kvm-pv-eoi", "kvm-pv-unhalt", - NULL, "kvm-pv-tlb-flush", NULL, NULL, + NULL, "kvm-pv-tlb-flush", NULL, "kvm-pv-ipi", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "kvmclock-stable-bit", NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EAX, + .cpuid = { .eax = KVM_CPUID_FEATURES, .reg = R_EAX, }, .tcg_features = TCG_KVM_FEATURES, }, [FEAT_KVM_HINTS] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "kvm-hint-dedicated", NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -889,7 +922,7 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = KVM_CPUID_FEATURES, .cpuid_reg = R_EDX, + .cpuid = { .eax = KVM_CPUID_FEATURES, .reg = R_EDX, }, .tcg_features = TCG_KVM_FEATURES, /* * KVM hints aren't auto-enabled by -cpu host, they need to be @@ -897,7 +930,15 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { */ .no_autoenable_flags = ~0U, }, + /* + * .feat_names are commented out for Hyper-V enlightenments because we + * don't want to have two different ways for enabling them on QEMU command + * line. Some features (e.g. "hyperv_time", "hyperv_vapic", ...) require + * enabling several feature bits simultaneously, exposing these bits + * individually may just confuse guests. + */ [FEAT_HYPERV_EAX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL /* hv_msr_vp_runtime_access */, NULL /* hv_msr_time_refcount_access */, NULL /* hv_msr_synic_access */, NULL /* hv_msr_stimer_access */, @@ -912,9 +953,10 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x40000003, .cpuid_reg = R_EAX, + .cpuid = { .eax = 0x40000003, .reg = R_EAX, }, }, [FEAT_HYPERV_EBX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL /* hv_create_partitions */, NULL /* hv_access_partition_id */, NULL /* hv_access_memory_pool */, NULL /* hv_adjust_message_buffers */, @@ -928,9 +970,10 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x40000003, .cpuid_reg = R_EBX, + .cpuid = { .eax = 0x40000003, .reg = R_EBX, }, }, [FEAT_HYPERV_EDX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL /* hv_mwait */, NULL /* hv_guest_debugging */, NULL /* hv_perf_monitor */, NULL /* hv_cpu_dynamic_part */, @@ -943,9 +986,40 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x40000003, .cpuid_reg = R_EDX, + .cpuid = { .eax = 0x40000003, .reg = R_EDX, }, + }, + [FEAT_HV_RECOMM_EAX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL /* hv_recommend_pv_as_switch */, + NULL /* hv_recommend_pv_tlbflush_local */, + NULL /* hv_recommend_pv_tlbflush_remote */, + NULL /* hv_recommend_msr_apic_access */, + NULL /* hv_recommend_msr_reset */, + NULL /* hv_recommend_relaxed_timing */, + NULL /* hv_recommend_dma_remapping */, + NULL /* hv_recommend_int_remapping */, + NULL /* hv_recommend_x2apic_msrs */, + NULL /* hv_recommend_autoeoi_deprecation */, + NULL /* hv_recommend_pv_ipi */, + NULL /* hv_recommend_ex_hypercalls */, + NULL /* hv_hypervisor_is_nested */, + NULL /* hv_recommend_int_mbec */, + NULL /* hv_recommend_evmcs */, + NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { .eax = 0x40000004, .reg = R_EAX, }, + }, + [FEAT_HV_NESTED_EAX] = { + .type = CPUID_FEATURE_WORD, + .cpuid = { .eax = 0x4000000A, .reg = R_EAX, }, }, [FEAT_SVM] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "npt", "lbrv", "svm-lock", "nrip-save", "tsc-scale", "vmcb-clean", "flushbyasid", "decodeassists", @@ -956,10 +1030,11 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x8000000A, .cpuid_reg = R_EDX, + .cpuid = { .eax = 0x8000000A, .reg = R_EDX, }, .tcg_features = TCG_SVM_FEATURES, }, [FEAT_7_0_EBX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "fsgsbase", "tsc-adjust", NULL, "bmi1", "hle", "avx2", NULL, "smep", @@ -970,12 +1045,15 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "clwb", "intel-pt", "avx512pf", "avx512er", "avx512cd", "sha-ni", "avx512bw", "avx512vl", }, - .cpuid_eax = 7, - .cpuid_needs_ecx = true, .cpuid_ecx = 0, - .cpuid_reg = R_EBX, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 0, + .reg = R_EBX, + }, .tcg_features = TCG_7_0_EBX_FEATURES, }, [FEAT_7_0_ECX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL, "avx512vbmi", "umip", "pku", NULL /* ospke */, NULL, "avx512vbmi2", NULL, @@ -983,15 +1061,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { "avx512bitalg", NULL, "avx512-vpopcntdq", NULL, "la57", NULL, NULL, NULL, NULL, NULL, "rdpid", NULL, - NULL, "cldemote", NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "cldemote", NULL, "movdiri", + "movdir64b", NULL, NULL, NULL, + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 0, + .reg = R_ECX, }, - .cpuid_eax = 7, - .cpuid_needs_ecx = true, .cpuid_ecx = 0, - .cpuid_reg = R_ECX, .tcg_features = TCG_7_0_ECX_FEATURES, }, [FEAT_7_0_EDX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, "avx512-4vnniw", "avx512-4fmaps", NULL, NULL, NULL, NULL, @@ -999,15 +1080,18 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, "spec-ctrl", NULL, - NULL, NULL, NULL, "ssbd", + NULL, NULL, "spec-ctrl", "stibp", + NULL, "arch-capabilities", NULL, "ssbd", + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 0, + .reg = R_EDX, }, - .cpuid_eax = 7, - .cpuid_needs_ecx = true, .cpuid_ecx = 0, - .cpuid_reg = R_EDX, .tcg_features = TCG_7_0_EDX_FEATURES, }, [FEAT_8000_0007_EDX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, @@ -1018,28 +1102,28 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x80000007, - .cpuid_reg = R_EDX, + .cpuid = { .eax = 0x80000007, .reg = R_EDX, }, .tcg_features = TCG_APM_FEATURES, .unmigratable_flags = CPUID_APM_INVTSC, }, [FEAT_8000_0008_EBX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "wbnoinvd", NULL, NULL, "ibpb", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "amd-ssbd", "virt-ssbd", "amd-no-ssb", NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0x80000008, - .cpuid_reg = R_EBX, + .cpuid = { .eax = 0x80000008, .reg = R_EBX, }, .tcg_features = 0, .unmigratable_flags = 0, }, [FEAT_XSAVE] = { + .type = CPUID_FEATURE_WORD, .feat_names = { "xsaveopt", "xsavec", "xgetbv1", "xsaves", NULL, NULL, NULL, NULL, @@ -1050,12 +1134,15 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 0xd, - .cpuid_needs_ecx = true, .cpuid_ecx = 1, - .cpuid_reg = R_EAX, + .cpuid = { + .eax = 0xd, + .needs_ecx = true, .ecx = 1, + .reg = R_EAX, + }, .tcg_features = TCG_XSAVE_FEATURES, }, [FEAT_6_EAX] = { + .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, "arat", NULL, NULL, NULL, NULL, NULL, @@ -1066,13 +1153,16 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, }, - .cpuid_eax = 6, .cpuid_reg = R_EAX, + .cpuid = { .eax = 6, .reg = R_EAX, }, .tcg_features = TCG_6_EAX_FEATURES, }, [FEAT_XSAVE_COMP_LO] = { - .cpuid_eax = 0xD, - .cpuid_needs_ecx = true, .cpuid_ecx = 0, - .cpuid_reg = R_EAX, + .type = CPUID_FEATURE_WORD, + .cpuid = { + .eax = 0xD, + .needs_ecx = true, .ecx = 0, + .reg = R_EAX, + }, .tcg_features = ~0U, .migratable_flags = XSTATE_FP_MASK | XSTATE_SSE_MASK | XSTATE_YMM_MASK | XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK | @@ -1080,11 +1170,35 @@ static FeatureWordInfo feature_word_info[FEATURE_WORDS] = { XSTATE_PKRU_MASK, }, [FEAT_XSAVE_COMP_HI] = { - .cpuid_eax = 0xD, - .cpuid_needs_ecx = true, .cpuid_ecx = 0, - .cpuid_reg = R_EDX, + .type = CPUID_FEATURE_WORD, + .cpuid = { + .eax = 0xD, + .needs_ecx = true, .ecx = 0, + .reg = R_EDX, + }, .tcg_features = ~0U, }, + /*Below are MSR exposed features*/ + [FEAT_ARCH_CAPABILITIES] = { + .type = MSR_FEATURE_WORD, + .feat_names = { + "rdctl-no", "ibrs-all", "rsba", "skip-l1dfl-vmentry", + "ssb-no", 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, + }, + .msr = { + .index = MSR_IA32_ARCH_CAPABILITIES, + .cpuid_dep = { + FEAT_7_0_EDX, + CPUID_7_0_EDX_ARCH_CAPABILITIES + } + }, + }, }; typedef struct X86RegisterInfo32 { @@ -2219,7 +2333,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | - CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX, + CPUID_7_0_EBX_SMAP, /* Missing: XSAVES (not supported by some Linux versions, * including v4.1 to v4.12). * KVM doesn't yet expose any XSAVES state save component, @@ -2266,7 +2380,7 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | - CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX, + CPUID_7_0_EBX_SMAP, /* Missing: XSAVES (not supported by some Linux versions, * including v4.1 to v4.12). * KVM doesn't yet expose any XSAVES state save component, @@ -2311,10 +2425,12 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | - CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_PKU, /* Missing: XSAVES (not supported by some Linux versions, * including v4.1 to v4.12). * KVM doesn't yet expose any XSAVES state save component, @@ -2361,10 +2477,12 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_7_0_EBX_HLE | CPUID_7_0_EBX_AVX2 | CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | - CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_MPX | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_AVX512VL, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_PKU, /* Missing: XSAVES (not supported by some Linux versions, * including v4.1 to v4.12). * KVM doesn't yet expose any XSAVES state save component, @@ -2379,6 +2497,172 @@ static X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x80000008, .model_id = "Intel Xeon Processor (Skylake, IBRS)", }, + { + .name = "Cascadelake-Server", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 85, + .stepping = 6, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + 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 | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | + CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_AVX512VNNI, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_SPEC_CTRL_SSBD, + /* Missing: XSAVES (not supported by some Linux versions, + * including v4.1 to v4.12). + * KVM doesn't yet expose any XSAVES state save component, + * and the only one defined in Skylake (processor tracing) + * probably will block migration anyway. + */ + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (Cascadelake)", + }, + { + .name = "Icelake-Client", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 126, + .stepping = 0, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_WBNOINVD, + .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 | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_VBMI2 | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG | + CPUID_7_0_ECX_AVX512_VPOPCNTDQ, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_SPEC_CTRL_SSBD, + /* Missing: XSAVES (not supported by some Linux versions, + * including v4.1 to v4.12). + * KVM doesn't yet expose any XSAVES state save component, + * and the only one defined in Skylake (processor tracing) + * probably will block migration anyway. + */ + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Core Processor (Icelake)", + }, + { + .name = "Icelake-Server", + .level = 0xd, + .vendor = CPUID_VENDOR_INTEL, + .family = 6, + .model = 134, + .stepping = 0, + .features[FEAT_1_EDX] = + CPUID_VME | CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | + CPUID_CLFLUSH | CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | + CPUID_PGE | CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | + CPUID_MCE | CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | + CPUID_DE | CPUID_FP87, + .features[FEAT_1_ECX] = + CPUID_EXT_AVX | CPUID_EXT_XSAVE | CPUID_EXT_AES | + CPUID_EXT_POPCNT | CPUID_EXT_X2APIC | CPUID_EXT_SSE42 | + CPUID_EXT_SSE41 | CPUID_EXT_CX16 | CPUID_EXT_SSSE3 | + CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSE3 | + CPUID_EXT_TSC_DEADLINE_TIMER | CPUID_EXT_FMA | CPUID_EXT_MOVBE | + CPUID_EXT_PCID | CPUID_EXT_F16C | CPUID_EXT_RDRAND, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_PDPE1GB | CPUID_EXT2_RDTSCP | + CPUID_EXT2_NX | CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_ABM | CPUID_EXT3_LAHF_LM | CPUID_EXT3_3DNOWPREFETCH, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_WBNOINVD, + .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 | + CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_AVX512F | CPUID_7_0_EBX_AVX512DQ | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512CD | + CPUID_7_0_EBX_AVX512VL | CPUID_7_0_EBX_CLFLUSHOPT, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_VBMI2 | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG | + CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_SPEC_CTRL | CPUID_7_0_EDX_SPEC_CTRL_SSBD, + /* Missing: XSAVES (not supported by some Linux versions, + * including v4.1 to v4.12). + * KVM doesn't yet expose any XSAVES state save component, + * and the only one defined in Skylake (processor tracing) + * probably will block migration anyway. + */ + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .xlevel = 0x80000008, + .model_id = "Intel Xeon Processor (Icelake)", + }, { .name = "KnightsMill", .level = 0xd, @@ -2456,7 +2740,6 @@ 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_NX | CPUID_EXT2_SYSCALL, .features[FEAT_8000_0001_ECX] = @@ -2480,9 +2763,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_NX | CPUID_EXT2_SYSCALL, + CPUID_EXT2_LM | CPUID_EXT2_NX | CPUID_EXT2_SYSCALL | + CPUID_EXT2_RDTSCP, .features[FEAT_8000_0001_ECX] = CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, @@ -2507,15 +2790,16 @@ 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_PDPE1GB | CPUID_EXT2_NX | - CPUID_EXT2_SYSCALL, + CPUID_EXT2_SYSCALL | CPUID_EXT2_RDTSCP, .features[FEAT_8000_0001_ECX] = CPUID_EXT3_FMA4 | CPUID_EXT3_XOP | CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, /* no xsaveopt! */ .xlevel = 0x8000001A, .model_id = "AMD Opteron 62xx class CPU", @@ -2538,15 +2822,16 @@ 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_PDPE1GB | CPUID_EXT2_NX | - CPUID_EXT2_SYSCALL, + CPUID_EXT2_SYSCALL | CPUID_EXT2_RDTSCP, .features[FEAT_8000_0001_ECX] = CPUID_EXT3_TBM | CPUID_EXT3_FMA4 | CPUID_EXT3_XOP | CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, /* no xsaveopt! */ .xlevel = 0x8000001A, .model_id = "AMD Opteron 63xx class CPU", @@ -2593,6 +2878,8 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_XSAVE_XGETBV1, .features[FEAT_6_EAX] = CPUID_6_EAX_ARAT, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor", .cache_info = &epyc_cache_info, @@ -2641,6 +2928,8 @@ static X86CPUDefinition builtin_x86_defs[] = { CPUID_XSAVE_XGETBV1, .features[FEAT_6_EAX] = CPUID_6_EAX_ARAT, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_NRIPSAVE, .xlevel = 0x8000001E, .model_id = "AMD EPYC Processor (with IBPB)", .cache_info = &epyc_cache_info, @@ -2853,21 +3142,41 @@ static const TypeInfo host_x86_cpu_type_info = { #endif +static char *feature_word_description(FeatureWordInfo *f, uint32_t bit) +{ + assert(f->type == CPUID_FEATURE_WORD || f->type == MSR_FEATURE_WORD); + + switch (f->type) { + case CPUID_FEATURE_WORD: + { + const char *reg = get_register_name_32(f->cpuid.reg); + assert(reg); + return g_strdup_printf("CPUID.%02XH:%s", + f->cpuid.eax, reg); + } + case MSR_FEATURE_WORD: + return g_strdup_printf("MSR(%02XH)", + f->msr.index); + } + + return NULL; +} + static void report_unavailable_features(FeatureWord w, uint32_t mask) { FeatureWordInfo *f = &feature_word_info[w]; int i; + char *feat_word_str; for (i = 0; i < 32; ++i) { if ((1UL << i) & mask) { - const char *reg = get_register_name_32(f->cpuid_reg); - assert(reg); - warn_report("%s doesn't support requested feature: " - "CPUID.%02XH:%s%s%s [bit %d]", + feat_word_str = feature_word_description(f, i); + warn_report("%s doesn't support requested feature: %s%s%s [bit %d]", accel_uses_host_cpuid() ? "host" : "TCG", - f->cpuid_eax, reg, + feat_word_str, f->feat_names[i] ? "." : "", f->feat_names[i] ? f->feat_names[i] : "", i); + g_free(feat_word_str); } } } @@ -3111,11 +3420,18 @@ static void x86_cpu_get_feature_words(Object *obj, Visitor *v, for (w = 0; w < FEATURE_WORDS; w++) { FeatureWordInfo *wi = &feature_word_info[w]; + /* + * We didn't have MSR features when "feature-words" was + * introduced. Therefore skipped other type entries. + */ + if (wi->type != CPUID_FEATURE_WORD) { + continue; + } X86CPUFeatureWordInfo *qwi = &word_infos[w]; - qwi->cpuid_input_eax = wi->cpuid_eax; - qwi->has_cpuid_input_ecx = wi->cpuid_needs_ecx; - qwi->cpuid_input_ecx = wi->cpuid_ecx; - qwi->cpuid_register = x86_reg_info_32[wi->cpuid_reg].qapi_enum; + qwi->cpuid_input_eax = wi->cpuid.eax; + qwi->has_cpuid_input_ecx = wi->cpuid.needs_ecx; + qwi->cpuid_input_ecx = wi->cpuid.ecx; + qwi->cpuid_register = x86_reg_info_32[wi->cpuid.reg].qapi_enum; qwi->features = array[w]; /* List will be in reverse order, but order shouldn't matter */ @@ -3291,7 +3607,6 @@ static void x86_cpu_parse_featurestr(const char *typename, char *features, prop->driver = typename; prop->property = g_strdup(name); prop->value = g_strdup(val); - prop->errp = &error_fatal; qdev_prop_register_global(prop); } @@ -3475,7 +3790,7 @@ static void x86_cpu_definition_entry(gpointer data, gpointer user_data) *cpu_list = entry; } -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) { CpuDefinitionInfoList *cpu_list = NULL; GSList *list = get_sorted_cpu_model_list(); @@ -3488,16 +3803,27 @@ static uint32_t x86_cpu_get_supported_feature_word(FeatureWord w, bool migratable_only) { FeatureWordInfo *wi = &feature_word_info[w]; - uint32_t r; + uint32_t r = 0; if (kvm_enabled()) { - r = kvm_arch_get_supported_cpuid(kvm_state, wi->cpuid_eax, - wi->cpuid_ecx, - wi->cpuid_reg); + switch (wi->type) { + case CPUID_FEATURE_WORD: + r = kvm_arch_get_supported_cpuid(kvm_state, wi->cpuid.eax, + wi->cpuid.ecx, + wi->cpuid.reg); + break; + case MSR_FEATURE_WORD: + r = kvm_arch_get_supported_msr_feature(kvm_state, + wi->msr.index); + break; + } } else if (hvf_enabled()) { - r = hvf_get_supported_cpuid(wi->cpuid_eax, - wi->cpuid_ecx, - wi->cpuid_reg); + if (wi->type != CPUID_FEATURE_WORD) { + return 0; + } + r = hvf_get_supported_cpuid(wi->cpuid.eax, + wi->cpuid.ecx, + wi->cpuid.reg); } else if (tcg_enabled()) { r = wi->tcg_features; } else { @@ -3592,6 +3918,7 @@ static void x86_cpu_load_def(X86CPU *cpu, X86CPUDefinition *def, Error **errp) } +#ifndef CONFIG_USER_ONLY /* Return a QDict containing keys for all properties that can be included * in static expansion of CPU models. All properties set by x86_cpu_load_def() * must be included in the dictionary. @@ -3739,7 +4066,7 @@ out: } CpuModelExpansionInfo * -arch_query_cpu_model_expansion(CpuModelExpansionType type, +qmp_query_cpu_model_expansion(CpuModelExpansionType type, CpuModelInfo *model, Error **errp) { @@ -3758,6 +4085,9 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, } props = qdict_new(); + ret->model = g_new0(CpuModelInfo, 1); + ret->model->props = QOBJECT(props); + ret->model->has_props = true; switch (type) { case CPU_MODEL_EXPANSION_TYPE_STATIC: @@ -3774,19 +4104,13 @@ arch_query_cpu_model_expansion(CpuModelExpansionType type, x86_cpu_to_dict_full(xc, props); break; default: - error_setg(&err, "Unsupportted expansion type"); + error_setg(&err, "Unsupported expansion type"); goto out; } - if (!props) { - props = qdict_new(); - } x86_cpu_to_dict(xc, props); - ret->model = g_new0(CpuModelInfo, 1); ret->model->name = g_strdup(base_name); - ret->model->props = QOBJECT(props); - ret->model->has_props = true; out: object_unref(OBJECT(xc)); @@ -3797,6 +4121,7 @@ out: } return ret; } +#endif /* !CONFIG_USER_ONLY */ static gchar *x86_gdb_arch_name(CPUState *cs) { @@ -4059,7 +4384,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = xsave_area_size(x86_cpu_xsave_components(cpu)); *eax = env->features[FEAT_XSAVE_COMP_LO]; *edx = env->features[FEAT_XSAVE_COMP_HI]; - *ebx = *ecx; + *ebx = xsave_area_size(env->xcr0); } else if (count == 1) { *eax = env->features[FEAT_XSAVE]; } else if (count < ARRAY_SIZE(x86_ext_save_areas)) { @@ -4561,9 +4886,10 @@ static void x86_cpu_adjust_feat_level(X86CPU *cpu, FeatureWord w) { CPUX86State *env = &cpu->env; FeatureWordInfo *fi = &feature_word_info[w]; - uint32_t eax = fi->cpuid_eax; + uint32_t eax = fi->cpuid.eax; uint32_t region = eax & 0xF0000000; + assert(feature_word_info[w].type == CPUID_FEATURE_WORD); if (!env->features[w]) { return; } @@ -4704,6 +5030,13 @@ static void x86_cpu_expand_features(X86CPU *cpu, Error **errp) x86_cpu_adjust_feat_level(cpu, FEAT_C000_0001_EDX); x86_cpu_adjust_feat_level(cpu, FEAT_SVM); x86_cpu_adjust_feat_level(cpu, FEAT_XSAVE); + + /* Intel Processor Trace requires CPUID[0x14] */ + if ((env->features[FEAT_7_0_EBX] & CPUID_7_0_EBX_INTEL_PT) && + kvm_enabled() && cpu->intel_pt_auto_level) { + x86_cpu_adjust_level(cpu, &cpu->env.cpuid_min_level, 0x14); + } + /* SVM requires CPUID[0x8000000A] */ if (env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_SVM) { x86_cpu_adjust_level(cpu, &env->cpuid_min_xlevel, 0x8000000A); @@ -4866,6 +5199,10 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (cpu->host_phys_bits) { /* The user asked for us to use the host physical bits */ cpu->phys_bits = host_phys_bits; + if (cpu->host_phys_bits_limit && + cpu->phys_bits > cpu->host_phys_bits_limit) { + cpu->phys_bits = cpu->host_phys_bits_limit; + } } /* Print a warning if the user set it to a value that's not the @@ -5004,14 +5341,15 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) * NOTE: the following code has to follow qemu_init_vcpu(). Otherwise * cs->nr_threads hasn't be populated yet and the checking is incorrect. */ - if (IS_AMD_CPU(env) && - !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && - cs->nr_threads > 1 && !ht_warned) { - error_report("This family of AMD CPU doesn't support " - "hyperthreading(%d). Please configure -smp " - "options properly or try enabling topoext feature.", - cs->nr_threads); - ht_warned = true; + if (IS_AMD_CPU(env) && + !(env->features[FEAT_8000_0001_ECX] & CPUID_EXT3_TOPOEXT) && + cs->nr_threads > 1 && !ht_warned) { + warn_report("This family of AMD CPU doesn't support " + "hyperthreading(%d)", + cs->nr_threads); + error_printf("Please configure -smp options properly" + " or try enabling topoext feature.\n"); + ht_warned = true; } x86_cpu_apic_realize(cpu, &local_err); @@ -5310,20 +5648,51 @@ static void x86_cpu_synchronize_from_tb(CPUState *cs, TranslationBlock *tb) cpu->env.eip = tb->pc - tb->cs_base; } -static bool x86_cpu_has_work(CPUState *cs) +int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - return ((cs->interrupt_request & (CPU_INTERRUPT_HARD | - CPU_INTERRUPT_POLL)) && - (env->eflags & IF_MASK)) || - (cs->interrupt_request & (CPU_INTERRUPT_NMI | - CPU_INTERRUPT_INIT | - CPU_INTERRUPT_SIPI | - CPU_INTERRUPT_MCE)) || - ((cs->interrupt_request & CPU_INTERRUPT_SMI) && - !(env->hflags & HF_SMM_MASK)); +#if !defined(CONFIG_USER_ONLY) + if (interrupt_request & CPU_INTERRUPT_POLL) { + return CPU_INTERRUPT_POLL; + } +#endif + if (interrupt_request & CPU_INTERRUPT_SIPI) { + return CPU_INTERRUPT_SIPI; + } + + if (env->hflags2 & HF2_GIF_MASK) { + if ((interrupt_request & CPU_INTERRUPT_SMI) && + !(env->hflags & HF_SMM_MASK)) { + return CPU_INTERRUPT_SMI; + } else if ((interrupt_request & CPU_INTERRUPT_NMI) && + !(env->hflags2 & HF2_NMI_MASK)) { + return CPU_INTERRUPT_NMI; + } else if (interrupt_request & CPU_INTERRUPT_MCE) { + return CPU_INTERRUPT_MCE; + } else if ((interrupt_request & CPU_INTERRUPT_HARD) && + (((env->hflags2 & HF2_VINTR_MASK) && + (env->hflags2 & HF2_HIF_MASK)) || + (!(env->hflags2 & HF2_VINTR_MASK) && + (env->eflags & IF_MASK && + !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { + return CPU_INTERRUPT_HARD; +#if !defined(CONFIG_USER_ONLY) + } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) && + (env->eflags & IF_MASK) && + !(env->hflags & HF_INHIBIT_IRQ_MASK)) { + return CPU_INTERRUPT_VIRQ; +#endif + } + } + + return 0; +} + +static bool x86_cpu_has_work(CPUState *cs) +{ + return x86_cpu_pending_interrupt(cs, cs->interrupt_request) != 0; } static void x86_disas_set_info(CPUState *cs, disassemble_info *info) @@ -5414,11 +5783,14 @@ static Property x86_cpu_properties[] = { DEFINE_PROP_BOOL("hv-frequencies", X86CPU, hyperv_frequencies, false), DEFINE_PROP_BOOL("hv-reenlightenment", X86CPU, hyperv_reenlightenment, false), DEFINE_PROP_BOOL("hv-tlbflush", X86CPU, hyperv_tlbflush, false), + DEFINE_PROP_BOOL("hv-evmcs", X86CPU, hyperv_evmcs, false), + DEFINE_PROP_BOOL("hv-ipi", X86CPU, hyperv_ipi, 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("phys-bits", X86CPU, phys_bits, 0), DEFINE_PROP_BOOL("host-phys-bits", X86CPU, host_phys_bits, false), + DEFINE_PROP_UINT8("host-phys-bits-limit", X86CPU, host_phys_bits_limit, 0), DEFINE_PROP_BOOL("fill-mtrr-mask", X86CPU, fill_mtrr_mask, true), DEFINE_PROP_UINT32("level", X86CPU, env.cpuid_level, UINT32_MAX), DEFINE_PROP_UINT32("xlevel", X86CPU, env.cpuid_xlevel, UINT32_MAX), @@ -5456,6 +5828,10 @@ static Property x86_cpu_properties[] = { * to the specific Windows version being used." */ DEFINE_PROP_INT32("x-hv-max-vps", X86CPU, hv_max_vps, -1), + DEFINE_PROP_BOOL("x-hv-synic-kvm-only", X86CPU, hyperv_synic_kvm_only, + false), + DEFINE_PROP_BOOL("x-intel-pt-auto-level", X86CPU, intel_pt_auto_level, + true), DEFINE_PROP_END_OF_LIST() }; @@ -5505,10 +5881,10 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->gdb_arch_name = x86_gdb_arch_name; #ifdef TARGET_X86_64 cc->gdb_core_xml_file = "i386-64bit.xml"; - cc->gdb_num_core_regs = 57; + cc->gdb_num_core_regs = 66; #else cc->gdb_core_xml_file = "i386-32bit.xml"; - cc->gdb_num_core_regs = 41; + cc->gdb_num_core_regs = 50; #endif #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) cc->debug_excp_handler = breakpoint_handler; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c18863ec7a..83fb522554 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -171,7 +171,7 @@ typedef enum X86Seg { #define HF_AC_SHIFT 18 /* must be same as eflags */ #define HF_SMM_SHIFT 19 /* CPU in SMM mode */ #define HF_SVME_SHIFT 20 /* SVME enabled (copy of EFER.SVME) */ -#define HF_SVMI_SHIFT 21 /* SVM intercepts are active */ +#define HF_GUEST_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 */ @@ -196,7 +196,7 @@ typedef enum X86Seg { #define HF_AC_MASK (1 << HF_AC_SHIFT) #define HF_SMM_MASK (1 << HF_SMM_SHIFT) #define HF_SVME_MASK (1 << HF_SVME_SHIFT) -#define HF_SVMI_MASK (1 << HF_SVMI_SHIFT) +#define HF_GUEST_MASK (1 << HF_GUEST_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) @@ -354,6 +354,8 @@ typedef enum X86Seg { #define MSR_TSC_ADJUST 0x0000003b #define MSR_IA32_SPEC_CTRL 0x48 #define MSR_VIRT_SSBD 0xc001011f +#define MSR_IA32_PRED_CMD 0x49 +#define MSR_IA32_ARCH_CAPABILITIES 0x10a #define MSR_IA32_TSCDEADLINE 0x6e0 #define FEATURE_CONTROL_LOCKED (1<<0) @@ -495,11 +497,14 @@ typedef enum FeatureWord { FEAT_HYPERV_EAX, /* CPUID[4000_0003].EAX */ FEAT_HYPERV_EBX, /* CPUID[4000_0003].EBX */ FEAT_HYPERV_EDX, /* CPUID[4000_0003].EDX */ + FEAT_HV_RECOMM_EAX, /* CPUID[4000_0004].EAX */ + FEAT_HV_NESTED_EAX, /* CPUID[4000_000A].EAX */ FEAT_SVM, /* CPUID[8000_000A].EDX */ FEAT_XSAVE, /* CPUID[EAX=0xd,ECX=1].EAX */ FEAT_6_EAX, /* CPUID[6].EAX */ FEAT_XSAVE_COMP_LO, /* CPUID[EAX=0xd,ECX=0].EAX */ FEAT_XSAVE_COMP_HI, /* CPUID[EAX=0xd,ECX=0].EDX */ + FEAT_ARCH_CAPABILITIES, FEATURE_WORDS, } FeatureWord; @@ -684,12 +689,17 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_7_0_ECX_LA57 (1U << 16) #define CPUID_7_0_ECX_RDPID (1U << 22) #define CPUID_7_0_ECX_CLDEMOTE (1U << 25) /* CLDEMOTE Instruction */ +#define CPUID_7_0_ECX_MOVDIRI (1U << 27) /* MOVDIRI Instruction */ +#define CPUID_7_0_ECX_MOVDIR64B (1U << 28) /* MOVDIR64B Instruction */ #define CPUID_7_0_EDX_AVX512_4VNNIW (1U << 2) /* AVX512 Neural Network Instructions */ #define CPUID_7_0_EDX_AVX512_4FMAPS (1U << 3) /* AVX512 Multiply Accumulation Single Precision */ #define CPUID_7_0_EDX_SPEC_CTRL (1U << 26) /* Speculation Control */ +#define CPUID_7_0_EDX_ARCH_CAPABILITIES (1U << 29) /*Arch Capabilities*/ #define CPUID_7_0_EDX_SPEC_CTRL_SSBD (1U << 31) /* Speculative Store Bypass Disable */ +#define CPUID_8000_0008_EBX_WBNOINVD (1U << 9) /* Write back and + do not invalidate cache */ #define CPUID_8000_0008_EBX_IBPB (1U << 12) /* Indirect Branch Prediction Barrier */ #define CPUID_XSAVE_XSAVEOPT (1U << 0) @@ -724,6 +734,13 @@ typedef uint32_t FeatureWordArray[FEATURE_WORDS]; #define CPUID_TOPOLOGY_LEVEL_SMT (1U << 8) #define CPUID_TOPOLOGY_LEVEL_CORE (2U << 8) +/* MSR Feature Bits */ +#define MSR_ARCH_CAP_RDCL_NO (1U << 0) +#define MSR_ARCH_CAP_IBRS_ALL (1U << 1) +#define MSR_ARCH_CAP_RSBA (1U << 2) +#define MSR_ARCH_CAP_SKIP_L1DFL_VMENTRY (1U << 3) +#define MSR_ARCH_CAP_SSB_NO (1U << 4) + #ifndef HYPERV_SPINLOCK_NEVER_RETRY #define HYPERV_SPINLOCK_NEVER_RETRY 0xFFFFFFFF #endif @@ -1321,7 +1338,9 @@ typedef struct CPUX86State { bool tsc_valid; int64_t tsc_khz; int64_t user_tsc_khz; /* for sanity check only */ - void *kvm_xsave_buf; +#if defined(CONFIG_KVM) || defined(CONFIG_HVF) + void *xsave_buf; +#endif #if defined(CONFIG_HVF) HVFX86EmulatorState *hvf_emul; #endif @@ -1370,10 +1389,13 @@ struct X86CPU { bool hyperv_vpindex; bool hyperv_runtime; bool hyperv_synic; + bool hyperv_synic_kvm_only; bool hyperv_stimer; bool hyperv_frequencies; bool hyperv_reenlightenment; bool hyperv_tlbflush; + bool hyperv_evmcs; + bool hyperv_ipi; bool check_cpuid; bool enforce_cpuid; bool expose_kvm; @@ -1432,12 +1454,18 @@ struct X86CPU { /* Enable auto level-increase for all CPUID leaves */ bool full_cpuid_auto_level; + /* Enable auto level-increase for Intel Processor Trace leave */ + bool intel_pt_auto_level; + /* if true fill the top bits of the MTRR_PHYSMASKn variable range */ bool fill_mtrr_mask; /* if true override the phys_bits value with a value read from the host */ bool host_phys_bits; + /* if set, limit maximum value for phys_bits when host_phys_bits is true */ + uint8_t host_phys_bits_limit; + /* Stop SMI delivery for migration compatibility with old machines */ bool kvm_no_smi_migration; @@ -1479,6 +1507,7 @@ extern struct VMStateDescription vmstate_x86_cpu; */ void x86_cpu_do_interrupt(CPUState *cpu); bool x86_cpu_exec_interrupt(CPUState *cpu, int int_req); +int x86_cpu_pending_interrupt(CPUState *cs, int interrupt_request); int x86_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cpu, int cpuid, void *opaque); @@ -1509,6 +1538,8 @@ int cpu_x86_support_mca_broadcast(CPUX86State *env); int cpu_get_pic_interrupt(CPUX86State *s); /* MSDOS compatibility mode FPU exception support */ void cpu_set_ferr(CPUX86State *s); +/* mpx_helper.c */ +void cpu_sync_bndcs_hflags(CPUX86State *env); /* this function must always be used to load data in the segment cache: it synchronizes the hflags with the segment cache values */ @@ -1551,6 +1582,8 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env, #error HF_CPL_MASK is hardcoded #endif env->hflags = (env->hflags & ~HF_CPL_MASK) | cpl; + /* Possibly switch between BNDCFGS and BNDCFGU */ + cpu_sync_bndcs_hflags(env); } new_hflags = (env->segs[R_SS].flags & DESC_B_MASK) >> (DESC_B_SHIFT - HF_SS32_SHIFT); @@ -1883,9 +1916,6 @@ void apic_handle_tpr_access_report(DeviceState *d, target_ulong ip, */ 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); diff --git a/target/i386/excp_helper.c b/target/i386/excp_helper.c index 37a33d5ae0..49231f6b69 100644 --- a/target/i386/excp_helper.c +++ b/target/i386/excp_helper.c @@ -53,7 +53,7 @@ static int check_exception(CPUX86State *env, int intno, int *error_code, #if !defined(CONFIG_USER_ONLY) if (env->old_exception == EXCP08_DBLE) { - if (env->hflags & HF_SVMI_MASK) { + if (env->hflags & HF_GUEST_MASK) { cpu_vmexit(env, SVM_EXIT_SHUTDOWN, 0, retaddr); /* does not return */ } diff --git a/target/i386/gdbstub.c b/target/i386/gdbstub.c index 9b94ab852c..1221433bc7 100644 --- a/target/i386/gdbstub.c +++ b/target/i386/gdbstub.c @@ -32,18 +32,61 @@ static const int gpr_map[16] = { #endif static const int gpr_map32[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; +/* + * Keep these in sync with assignment to + * gdb_num_core_regs in target/i386/cpu.c + * and with the machine description + */ + +/* + * SEG: 6 segments, plus fs_base, gs_base, kernel_gs_base + */ + +/* + * general regs -----> 8 or 16 + */ +#define IDX_NB_IP 1 +#define IDX_NB_FLAGS 1 +#define IDX_NB_SEG (6 + 3) +#define IDX_NB_CTL 6 +#define IDX_NB_FP 16 +/* + * fpu regs ----------> 8 or 16 + */ +#define IDX_NB_MXCSR 1 +/* + * total ----> 8+1+1+9+6+16+8+1=50 or 16+1+1+9+6+16+16+1=66 + */ + #define IDX_IP_REG CPU_NB_REGS -#define IDX_FLAGS_REG (IDX_IP_REG + 1) -#define IDX_SEG_REGS (IDX_FLAGS_REG + 1) -#define IDX_FP_REGS (IDX_SEG_REGS + 6) -#define IDX_XMM_REGS (IDX_FP_REGS + 16) +#define IDX_FLAGS_REG (IDX_IP_REG + IDX_NB_IP) +#define IDX_SEG_REGS (IDX_FLAGS_REG + IDX_NB_FLAGS) +#define IDX_CTL_REGS (IDX_SEG_REGS + IDX_NB_SEG) +#define IDX_FP_REGS (IDX_CTL_REGS + IDX_NB_CTL) +#define IDX_XMM_REGS (IDX_FP_REGS + IDX_NB_FP) #define IDX_MXCSR_REG (IDX_XMM_REGS + CPU_NB_REGS) +#define IDX_CTL_CR0_REG (IDX_CTL_REGS + 0) +#define IDX_CTL_CR2_REG (IDX_CTL_REGS + 1) +#define IDX_CTL_CR3_REG (IDX_CTL_REGS + 2) +#define IDX_CTL_CR4_REG (IDX_CTL_REGS + 3) +#define IDX_CTL_CR8_REG (IDX_CTL_REGS + 4) +#define IDX_CTL_EFER_REG (IDX_CTL_REGS + 5) + +#ifdef TARGET_X86_64 +#define GDB_FORCE_64 1 +#else +#define GDB_FORCE_64 0 +#endif + + int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; + uint64_t tpr; + /* N.B. GDB can't deal with changes in registers or sizes in the middle of a session. So if we're in 32-bit mode on a 64-bit cpu, still act as if we're on a 64-bit cpu. */ @@ -105,6 +148,28 @@ int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) case IDX_SEG_REGS + 5: return gdb_get_reg32(mem_buf, env->segs[R_GS].selector); + case IDX_SEG_REGS + 6: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->segs[R_FS].base); + } + return gdb_get_reg32(mem_buf, env->segs[R_FS].base); + + case IDX_SEG_REGS + 7: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->segs[R_GS].base); + } + return gdb_get_reg32(mem_buf, env->segs[R_GS].base); + + case IDX_SEG_REGS + 8: +#ifdef TARGET_X86_64 + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->kernelgsbase); + } + return gdb_get_reg32(mem_buf, env->kernelgsbase); +#else + return gdb_get_reg32(mem_buf, 0); +#endif + case IDX_FP_REGS + 8: return gdb_get_reg32(mem_buf, env->fpuc); case IDX_FP_REGS + 9: @@ -125,6 +190,47 @@ int x86_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) case IDX_MXCSR_REG: return gdb_get_reg32(mem_buf, env->mxcsr); + + case IDX_CTL_CR0_REG: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->cr[0]); + } + return gdb_get_reg32(mem_buf, env->cr[0]); + + case IDX_CTL_CR2_REG: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->cr[2]); + } + return gdb_get_reg32(mem_buf, env->cr[2]); + + case IDX_CTL_CR3_REG: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->cr[3]); + } + return gdb_get_reg32(mem_buf, env->cr[3]); + + case IDX_CTL_CR4_REG: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->cr[4]); + } + return gdb_get_reg32(mem_buf, env->cr[4]); + + case IDX_CTL_CR8_REG: +#ifdef CONFIG_SOFTMMU + tpr = cpu_get_apic_tpr(cpu->apic_state); +#else + tpr = 0; +#endif + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, tpr); + } + return gdb_get_reg32(mem_buf, tpr); + + case IDX_CTL_EFER_REG: + if ((env->hflags & HF_CS64_MASK) || GDB_FORCE_64) { + return gdb_get_reg64(mem_buf, env->efer); + } + return gdb_get_reg32(mem_buf, env->efer); } } return 0; @@ -229,6 +335,32 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) case IDX_SEG_REGS + 5: return x86_cpu_gdb_load_seg(cpu, R_GS, mem_buf); + case IDX_SEG_REGS + 6: + if (env->hflags & HF_CS64_MASK) { + env->segs[R_FS].base = ldq_p(mem_buf); + return 8; + } + env->segs[R_FS].base = ldl_p(mem_buf); + return 4; + + case IDX_SEG_REGS + 7: + if (env->hflags & HF_CS64_MASK) { + env->segs[R_GS].base = ldq_p(mem_buf); + return 8; + } + env->segs[R_GS].base = ldl_p(mem_buf); + return 4; + +#ifdef TARGET_X86_64 + case IDX_SEG_REGS + 8: + if (env->hflags & HF_CS64_MASK) { + env->kernelgsbase = ldq_p(mem_buf); + return 8; + } + env->kernelgsbase = ldl_p(mem_buf); + return 4; +#endif + case IDX_FP_REGS + 8: cpu_set_fpuc(env, ldl_p(mem_buf)); return 4; @@ -253,6 +385,59 @@ int x86_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) case IDX_MXCSR_REG: cpu_set_mxcsr(env, ldl_p(mem_buf)); return 4; + + case IDX_CTL_CR0_REG: + if (env->hflags & HF_CS64_MASK) { + cpu_x86_update_cr0(env, ldq_p(mem_buf)); + return 8; + } + cpu_x86_update_cr0(env, ldl_p(mem_buf)); + return 4; + + case IDX_CTL_CR2_REG: + if (env->hflags & HF_CS64_MASK) { + env->cr[2] = ldq_p(mem_buf); + return 8; + } + env->cr[2] = ldl_p(mem_buf); + return 4; + + case IDX_CTL_CR3_REG: + if (env->hflags & HF_CS64_MASK) { + cpu_x86_update_cr3(env, ldq_p(mem_buf)); + return 8; + } + cpu_x86_update_cr3(env, ldl_p(mem_buf)); + return 4; + + case IDX_CTL_CR4_REG: + if (env->hflags & HF_CS64_MASK) { + cpu_x86_update_cr4(env, ldq_p(mem_buf)); + return 8; + } + cpu_x86_update_cr4(env, ldl_p(mem_buf)); + return 4; + + case IDX_CTL_CR8_REG: + if (env->hflags & HF_CS64_MASK) { +#ifdef CONFIG_SOFTMMU + cpu_set_apic_tpr(cpu->apic_state, ldq_p(mem_buf)); +#endif + return 8; + } +#ifdef CONFIG_SOFTMMU + cpu_set_apic_tpr(cpu->apic_state, ldl_p(mem_buf)); +#endif + return 4; + + case IDX_CTL_EFER_REG: + if (env->hflags & HF_CS64_MASK) { + cpu_load_efer(env, ldq_p(mem_buf)); + return 8; + } + cpu_load_efer(env, ldl_p(mem_buf)); + return 4; + } } /* Unrecognised register. */ diff --git a/target/i386/hax-all.c b/target/i386/hax-all.c index d2e512856b..b978a9b821 100644 --- a/target/i386/hax-all.c +++ b/target/i386/hax-all.c @@ -154,13 +154,7 @@ int hax_vcpu_create(int id) return 0; } - vcpu = g_malloc(sizeof(struct hax_vcpu_state)); - if (!vcpu) { - fprintf(stderr, "Failed to alloc vcpu state\n"); - return -ENOMEM; - } - - memset(vcpu, 0, sizeof(struct hax_vcpu_state)); + vcpu = g_new0(struct hax_vcpu_state, 1); ret = hax_host_create_vcpu(hax_global.vm->fd, id); if (ret) { @@ -211,7 +205,7 @@ int hax_vcpu_destroy(CPUState *cpu) } /* - * 1. The hax_tunnel is also destroied when vcpu destroy + * 1. The hax_tunnel is also destroyed when vcpu is destroyed * 2. close fd will cause hax module vcpu be cleaned */ hax_close_fd(vcpu->fd); @@ -250,11 +244,8 @@ struct hax_vm *hax_vm_create(struct hax_state *hax) return hax->vm; } - vm = g_malloc(sizeof(struct hax_vm)); - if (!vm) { - return NULL; - } - memset(vm, 0, sizeof(struct hax_vm)); + vm = g_new0(struct hax_vm, 1); + ret = hax_host_create_vm(hax, &vm_id); if (ret) { fprintf(stderr, "Failed to create vm %x\n", ret); diff --git a/target/i386/hax-i386.h b/target/i386/hax-i386.h index 6abc156f88..f13fa4638f 100644 --- a/target/i386/hax-i386.h +++ b/target/i386/hax-i386.h @@ -16,7 +16,7 @@ #include "cpu.h" #include "sysemu/hax.h" -#ifdef CONFIG_DARWIN +#ifdef CONFIG_POSIX typedef int hax_fd; #endif @@ -82,8 +82,8 @@ hax_fd hax_mod_open(void); void hax_memory_init(void); -#ifdef CONFIG_DARWIN -#include "target/i386/hax-darwin.h" +#ifdef CONFIG_POSIX +#include "target/i386/hax-posix.h" #endif #ifdef CONFIG_WIN32 diff --git a/target/i386/hax-mem.c b/target/i386/hax-mem.c index 504b2563b5..5742d6590d 100644 --- a/target/i386/hax-mem.c +++ b/target/i386/hax-mem.c @@ -56,7 +56,7 @@ typedef struct HAXMapping { * send to the kernel only the removal of the pages from the MMIO hole after * having computed locally the result of the deletion and additions. */ -static QTAILQ_HEAD(HAXMappingListHead, HAXMapping) mappings = +static QTAILQ_HEAD(, HAXMapping) mappings = QTAILQ_HEAD_INITIALIZER(mappings); /** diff --git a/target/i386/hax-darwin.c b/target/i386/hax-posix.c similarity index 100% rename from target/i386/hax-darwin.c rename to target/i386/hax-posix.c diff --git a/target/i386/hax-darwin.h b/target/i386/hax-posix.h similarity index 100% rename from target/i386/hax-darwin.h rename to target/i386/hax-posix.h diff --git a/target/i386/hvf/README.md b/target/i386/hvf/README.md index 0d27a0d52b..2d33477aca 100644 --- a/target/i386/hvf/README.md +++ b/target/i386/hvf/README.md @@ -2,6 +2,6 @@ These sources (and ../hvf-all.c) are adapted from Veertu Inc's vdhh (Veertu Desktop Hosted Hypervisor) (last known location: https://github.com/veertuinc/vdhh) with some minor changes, the most significant of which were: -1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, kvm_xsave_buf) due to historical differences + QEMU needing to handle more emulation targets. +1. Adapt to our current QEMU's `CPUState` structure and `address_space_rw` API; many struct members have been moved around (emulated x86 state, xsave_buf) due to historical differences + QEMU needing to handle more emulation targets. 2. Removal of `apic_page` and hyperv-related functionality. 3. More relaxed use of `qemu_mutex_lock_iothread`. diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index fbe6b6ac5f..f8be390ec5 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -13,10 +13,10 @@ * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, see . + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . * * This file contain code under public domain from the hvdos project: * https://github.com/mist64/hvdos @@ -72,9 +72,7 @@ #include "sysemu/sysemu.h" #include "target/i386/cpu.h" -pthread_rwlock_t mem_lock = PTHREAD_RWLOCK_INITIALIZER; HVFState *hvf_state; -int hvf_disabled = 1; static void assert_hvf_ok(hv_return_t ret) { @@ -502,7 +500,6 @@ void hvf_reset_vcpu(CPUState *cpu) { } hv_vm_sync_tsc(0); - cpu->halted = 0; hv_vcpu_invalidate_tlb(cpu->hvf_fd); hv_vcpu_flush(cpu->hvf_fd); } @@ -585,10 +582,8 @@ int hvf_init_vcpu(CPUState *cpu) wvmcs(cpu->hvf_fd, VMCS_TPR_THRESHOLD, 0); - hvf_reset_vcpu(cpu); - x86cpu = X86_CPU(cpu); - x86cpu->env.kvm_xsave_buf = qemu_memalign(4096, 4096); + x86cpu->env.xsave_buf = qemu_memalign(4096, 4096); hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_STAR, 1); hv_vcpu_enable_native_msr(cpu->hvf_fd, MSR_LSTAR, 1); @@ -606,11 +601,6 @@ int hvf_init_vcpu(CPUState *cpu) return 0; } -void hvf_disable(int shouldDisable) -{ - hvf_disabled = shouldDisable; -} - static void hvf_store_events(CPUState *cpu, uint32_t ins_len, uint64_t idtvec_info) { X86CPU *x86_cpu = X86_CPU(cpu); @@ -667,10 +657,6 @@ int hvf_vcpu_exec(CPUState *cpu) int ret = 0; uint64_t rip = 0; - cpu->halted = 0; - - // printf("hvf_vcpu_exec\n"); - if (hvf_process_events(cpu)) { return EXCP_HLT; } @@ -940,7 +926,7 @@ int hvf_vcpu_exec(CPUState *cpu) return ret; } -static bool hvf_allowed; +bool hvf_allowed; static int hvf_accel_init(MachineState *ms) { @@ -948,7 +934,6 @@ static int hvf_accel_init(MachineState *ms) hv_return_t ret; HVFState *s; - hvf_disable(0); ret = hv_vm_create(HV_VM_DEFAULT); assert_hvf_ok(ret); diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index 9874a46e92..4d957fe896 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -38,16 +38,6 @@ static uint64_t xgetbv(uint32_t xcr) return (((uint64_t)edx) << 32) | eax; } -static bool vmx_mpx_supported() -{ - uint64_t cap_exit, cap_entry; - - hv_vmx_read_capability(HV_VMX_CAP_ENTRY, &cap_entry); - hv_vmx_read_capability(HV_VMX_CAP_EXIT, &cap_exit); - - return ((cap_exit & (1 << 23)) && (cap_entry & (1 << 16))); -} - uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg) { @@ -92,11 +82,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_SHA_NI | CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL | - CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_MPX; + CPUID_7_0_EBX_INVPCID; - if (!vmx_mpx_supported()) { - ebx &= ~CPUID_7_0_EBX_MPX; - } hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap); if (!(cap & CPU_BASED2_INVPCID)) { ebx &= ~CPUID_7_0_EBX_INVPCID; @@ -119,9 +106,6 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, XSTATE_BNDCSR_MASK | XSTATE_OPMASK_MASK | XSTATE_ZMM_Hi256_MASK | XSTATE_Hi16_ZMM_MASK); eax &= supp_xcr0; - if (!vmx_mpx_supported()) { - eax &= ~(XSTATE_BNDREGS_MASK | XSTATE_BNDCSR_MASK); - } } else if (idx == 1) { hv_vmx_read_capability(HV_VMX_CAP_PROCBASED2, &cap); eax &= CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XGETBV1; diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index 3b5f8d5ca3..9ed59307f8 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -113,8 +113,8 @@ static void decode_modrm_reg(CPUX86State *env, struct x86_decode *decode, { op->type = X86_VAR_REG; op->reg = decode->modrm.reg; - op->ptr = get_reg_ref(env, op->reg, decode->rex.r, - decode->rex.unused == 4, decode->operand_size); + op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.r, + decode->operand_size); } static void decode_rax(CPUX86State *env, struct x86_decode *decode, @@ -122,7 +122,8 @@ static void decode_rax(CPUX86State *env, struct x86_decode *decode, { op->type = X86_VAR_REG; op->reg = R_EAX; - op->ptr = get_reg_ref(env, op->reg, 0, 0, decode->operand_size); + op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, 0, + decode->operand_size); } static inline void decode_immediate(CPUX86State *env, struct x86_decode *decode, @@ -264,16 +265,16 @@ static void decode_incgroup(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0x40; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); } static void decode_decgroup(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0x48; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); } static void decode_incgroup2(CPUX86State *env, struct x86_decode *decode) @@ -289,16 +290,16 @@ static void decode_pushgroup(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0x50; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); } static void decode_popgroup(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0x58; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); } static void decode_jxx(CPUX86State *env, struct x86_decode *decode) @@ -379,16 +380,16 @@ static void decode_xchgroup(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0x90; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); } static void decode_movgroup(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0xb8; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); decode_immediate(env, decode, &decode->op[1], decode->operand_size); } @@ -403,8 +404,8 @@ static void decode_movgroup8(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[0] - 0xb0; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); decode_immediate(env, decode, &decode->op[1], decode->operand_size); } @@ -413,7 +414,8 @@ static void decode_rcx(CPUX86State *env, struct x86_decode *decode, { op->type = X86_VAR_REG; op->reg = R_ECX; - op->ptr = get_reg_ref(env, op->reg, decode->rex.b, decode->rex.unused == 4, decode->operand_size); + op->ptr = get_reg_ref(env, op->reg, decode->rex.rex, decode->rex.b, + decode->operand_size); } struct decode_tbl { @@ -523,8 +525,6 @@ static void decode_sldtgroup(CPUX86State *env, struct x86_decode *decode) X86_DECODE_CMD_INVL }; decode->cmd = group[decode->modrm.reg]; - printf("%llx: decode_sldtgroup: %d\n", env->hvf_emul->fetch_rip, - decode->modrm.reg); } static void decode_lidtgroup(CPUX86State *env, struct x86_decode *decode) @@ -640,8 +640,8 @@ static void decode_bswap(CPUX86State *env, struct x86_decode *decode) { decode->op[0].type = X86_VAR_REG; decode->op[0].reg = decode->opcode[1] - 0xc8; - decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + decode->op[0].ptr = get_reg_ref(env, decode->op[0].reg, decode->rex.rex, + decode->rex.b, decode->operand_size); } static void decode_d9_4(CPUX86State *env, struct x86_decode *decode) @@ -1642,7 +1642,7 @@ void calc_modrm_operand16(CPUX86State *env, struct x86_decode *decode, X86Seg seg = R_DS; if (!decode->modrm.mod && 6 == decode->modrm.rm) { - op->ptr = (uint16_t)decode->displacement; + ptr = decode->displacement; goto calc_addr; } @@ -1687,7 +1687,8 @@ calc_addr: } } -uintptr_t get_reg_ref(CPUX86State *env, int reg, int is_extended, int rex, int size) +target_ulong get_reg_ref(CPUX86State *env, int reg, int rex, int is_extended, + int size) { uintptr_t ptr = 0; int which = 0; @@ -1699,7 +1700,7 @@ uintptr_t get_reg_ref(CPUX86State *env, int reg, int is_extended, int rex, int s switch (size) { case 1: - if (rex || is_extended || reg < 4) { + if (is_extended || reg < 4 || rex) { which = 1; ptr = (uintptr_t)&RL(env, reg); } else { @@ -1715,10 +1716,12 @@ uintptr_t get_reg_ref(CPUX86State *env, int reg, int is_extended, int rex, int s return ptr; } -target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int rex, int size) + +target_ulong get_reg_val(CPUX86State *env, int reg, int rex, int is_extended, + int size) { target_ulong val = 0; - memcpy(&val, (void *)get_reg_ref(env, reg, is_extended, rex, size), size); + memcpy(&val, (void *)get_reg_ref(env, reg, rex, is_extended, size), size); return val; } @@ -1740,7 +1743,8 @@ static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode, if (base_reg == R_ESP || base_reg == R_EBP) { *sel = R_SS; } - base = get_reg_val(env, decode->sib.base, decode->rex.b, decode->rex.unused == 4, addr_size); + base = get_reg_val(env, decode->sib.base, decode->rex.rex, + decode->rex.b, addr_size); } if (decode->rex.x) { @@ -1748,8 +1752,9 @@ static target_ulong get_sib_val(CPUX86State *env, struct x86_decode *decode, } if (index_reg != R_ESP) { - scaled_index = get_reg_val(env, index_reg, decode->rex.x, - decode->rex.unused == 4, addr_size) << decode->sib.scale; + scaled_index = get_reg_val(env, index_reg, decode->rex.rex, + decode->rex.x, addr_size) << + decode->sib.scale; } return base + scaled_index; } @@ -1777,7 +1782,8 @@ void calc_modrm_operand32(CPUX86State *env, struct x86_decode *decode, if (decode->modrm.rm == R_EBP || decode->modrm.rm == R_ESP) { seg = R_SS; } - ptr += get_reg_val(env, decode->modrm.rm, decode->rex.b, decode->rex.unused == 4, addr_size); + ptr += get_reg_val(env, decode->modrm.rm, decode->rex.rex, + decode->rex.b, addr_size); } if (X86_DECODE_CMD_LEA == decode->cmd) { @@ -1806,7 +1812,8 @@ void calc_modrm_operand64(CPUX86State *env, struct x86_decode *decode, } else if (0 == mod && 5 == rm) { ptr = RIP(env) + decode->len + (int32_t) offset; } else { - ptr = get_reg_val(env, src, decode->rex.b, decode->rex.unused == 4, 8) + (int64_t) offset; + ptr = get_reg_val(env, src, decode->rex.rex, decode->rex.b, 8) + + (int64_t) offset; } if (X86_DECODE_CMD_LEA == decode->cmd) { @@ -1823,8 +1830,8 @@ void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode, if (3 == decode->modrm.mod) { op->reg = decode->modrm.reg; op->type = X86_VAR_REG; - op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.b, - decode->rex.unused == 4, decode->operand_size); + op->ptr = get_reg_ref(env, decode->modrm.rm, decode->rex.rex, + decode->rex.b, decode->operand_size); return; } diff --git a/target/i386/hvf/x86_decode.h b/target/i386/hvf/x86_decode.h index 8b8fd41589..c5773b780e 100644 --- a/target/i386/hvf/x86_decode.h +++ b/target/i386/hvf/x86_decode.h @@ -303,8 +303,10 @@ uint64_t sign(uint64_t val, int size); uint32_t decode_instruction(CPUX86State *env, struct x86_decode *decode); -uintptr_t get_reg_ref(CPUX86State *env, int reg, int is_extended, int rex, int size); -target_ulong get_reg_val(CPUX86State *env, int reg, int is_extended, int rex, int size); +target_ulong get_reg_ref(CPUX86State *env, int reg, int rex, int is_extended, + int size); +target_ulong get_reg_val(CPUX86State *env, int reg, int rex, int is_extended, + int size); void calc_modrm_operand(CPUX86State *env, struct x86_decode *decode, struct x86_decode_op *op); target_ulong decode_linear_addr(CPUX86State *env, struct x86_decode *decode, diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 6f0d1e1964..5c1dcf4973 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -75,7 +75,7 @@ void hvf_put_xsave(CPUState *cpu_state) struct X86XSaveArea *xsave; - xsave = X86_CPU(cpu_state)->env.kvm_xsave_buf; + xsave = X86_CPU(cpu_state)->env.xsave_buf; x86_cpu_xsave_all_areas(X86_CPU(cpu_state), xsave); @@ -165,7 +165,7 @@ void hvf_get_xsave(CPUState *cpu_state) { struct X86XSaveArea *xsave; - xsave = X86_CPU(cpu_state)->env.kvm_xsave_buf; + xsave = X86_CPU(cpu_state)->env.xsave_buf; if (hv_vcpu_read_fpstate(cpu_state->hvf_fd, (void*)xsave, 4096)) { abort(); diff --git a/target/i386/hyperv-proto.h b/target/i386/hyperv-proto.h index d6d5a79293..c0272b3a01 100644 --- a/target/i386/hyperv-proto.h +++ b/target/i386/hyperv-proto.h @@ -1,7 +1,7 @@ /* - * Definitions for Hyper-V guest/hypervisor interaction + * Definitions for Hyper-V guest/hypervisor interaction - x86-specific part * - * Copyright (C) 2017 Parallels International GmbH + * Copyright (c) 2017-2018 Virtuozzo International GmbH. * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. @@ -10,7 +10,7 @@ #ifndef TARGET_I386_HYPERV_PROTO_H #define TARGET_I386_HYPERV_PROTO_H -#include "qemu/bitmap.h" +#include "hw/hyperv/hyperv-proto.h" #define HV_CPUID_VENDOR_AND_MAX_FUNCTIONS 0x40000000 #define HV_CPUID_INTERFACE 0x40000001 @@ -18,6 +18,7 @@ #define HV_CPUID_FEATURES 0x40000003 #define HV_CPUID_ENLIGHTMENT_INFO 0x40000004 #define HV_CPUID_IMPLEMENT_LIMITS 0x40000005 +#define HV_CPUID_NESTED_FEATURES 0x4000000A #define HV_CPUID_MIN 0x40000005 #define HV_CPUID_MAX 0x4000ffff #define HV_HYPERVISOR_PRESENT_BIT 0x80000000 @@ -58,7 +59,9 @@ #define HV_APIC_ACCESS_RECOMMENDED (1u << 3) #define HV_SYSTEM_RESET_RECOMMENDED (1u << 4) #define HV_RELAXED_TIMING_RECOMMENDED (1u << 5) +#define HV_CLUSTER_IPI_RECOMMENDED (1u << 10) #define HV_EX_PROCESSOR_MASKS_RECOMMENDED (1u << 11) +#define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) /* * Basic virtualized MSRs @@ -137,25 +140,6 @@ #define HV_X64_MSR_TSC_EMULATION_CONTROL 0x40000107 #define HV_X64_MSR_TSC_EMULATION_STATUS 0x40000108 -/* - * 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_INVALID_PARAMETER 5 -#define HV_STATUS_INSUFFICIENT_MEMORY 11 -#define HV_STATUS_INVALID_CONNECTION_ID 18 -#define HV_STATUS_INSUFFICIENT_BUFFERS 19 - -/* - * Hypercall numbers - */ -#define HV_POST_MESSAGE 0x005c -#define HV_SIGNAL_EVENT 0x005d -#define HV_HYPERCALL_FAST (1u << 16) - /* * Hypercall MSR bits */ @@ -165,7 +149,6 @@ * Synthetic interrupt controller definitions */ #define HV_SYNIC_VERSION 1 -#define HV_SINT_COUNT 16 #define HV_SYNIC_ENABLE (1u << 0) #define HV_SIMP_ENABLE (1u << 0) #define HV_SIEFP_ENABLE (1u << 0) @@ -175,94 +158,5 @@ #define HV_STIMER_COUNT 4 -/* - * Message size - */ -#define HV_MESSAGE_PAYLOAD_SIZE 240 - -/* - * Message types - */ -#define HV_MESSAGE_NONE 0x00000000 -#define HV_MESSAGE_VMBUS 0x00000001 -#define HV_MESSAGE_UNMAPPED_GPA 0x80000000 -#define HV_MESSAGE_GPA_INTERCEPT 0x80000001 -#define HV_MESSAGE_TIMER_EXPIRED 0x80000010 -#define HV_MESSAGE_INVALID_VP_REGISTER_VALUE 0x80000020 -#define HV_MESSAGE_UNRECOVERABLE_EXCEPTION 0x80000021 -#define HV_MESSAGE_UNSUPPORTED_FEATURE 0x80000022 -#define HV_MESSAGE_EVENTLOG_BUFFERCOMPLETE 0x80000040 -#define HV_MESSAGE_X64_IOPORT_INTERCEPT 0x80010000 -#define HV_MESSAGE_X64_MSR_INTERCEPT 0x80010001 -#define HV_MESSAGE_X64_CPUID_INTERCEPT 0x80010002 -#define HV_MESSAGE_X64_EXCEPTION_INTERCEPT 0x80010003 -#define HV_MESSAGE_X64_APIC_EOI 0x80010004 -#define HV_MESSAGE_X64_LEGACY_FP_ERROR 0x80010005 - -/* - * Message flags - */ -#define HV_MESSAGE_FLAG_PENDING 0x1 - -/* - * Event flags number per SINT - */ -#define HV_EVENT_FLAGS_COUNT (256 * 8) - -/* - * Connection id valid bits - */ -#define HV_CONNECTION_ID_MASK 0x00ffffff - -/* - * Input structure for POST_MESSAGE hypercall - */ -struct hyperv_post_message_input { - uint32_t connection_id; - uint32_t _reserved; - uint32_t message_type; - uint32_t payload_size; - uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE]; -}; - -/* - * Input structure for SIGNAL_EVENT hypercall - */ -struct hyperv_signal_event_input { - uint32_t connection_id; - uint16_t flag_number; - uint16_t _reserved_zero; -}; - -/* - * SynIC message structures - */ -struct hyperv_message_header { - uint32_t message_type; - uint8_t payload_size; - uint8_t message_flags; /* HV_MESSAGE_FLAG_XX */ - uint8_t _reserved[2]; - uint64_t sender; -}; - -struct hyperv_message { - struct hyperv_message_header header; - uint8_t payload[HV_MESSAGE_PAYLOAD_SIZE]; -}; - -struct hyperv_message_page { - struct hyperv_message slot[HV_SINT_COUNT]; -}; - -/* - * SynIC event flags structures - */ -struct hyperv_event_flags { - DECLARE_BITMAP(flags, HV_EVENT_FLAGS_COUNT); -}; - -struct hyperv_event_flags_page { - struct hyperv_event_flags slot[HV_SINT_COUNT]; -}; #endif diff --git a/target/i386/hyperv-stub.c b/target/i386/hyperv-stub.c new file mode 100644 index 0000000000..fe548cbae2 --- /dev/null +++ b/target/i386/hyperv-stub.c @@ -0,0 +1,48 @@ +/* + * Stubs for CONFIG_HYPERV=n + * + * Copyright (c) 2015-2018 Virtuozzo International GmbH. + * + * 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" + +#ifdef CONFIG_KVM +int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) +{ + switch (exit->type) { + case KVM_EXIT_HYPERV_SYNIC: + if (!cpu->hyperv_synic) { + return -1; + } + + /* + * Tracking the changes in the MSRs is unnecessary as there are no + * users for them beside save/load, which is handled nicely by the + * generic MSR save/load code + */ + return 0; + case KVM_EXIT_HYPERV_HCALL: + exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; + return 0; + default: + return -1; + } +} +#endif + +int hyperv_x86_synic_add(X86CPU *cpu) +{ + return -ENOSYS; +} + +void hyperv_x86_synic_reset(X86CPU *cpu) +{ +} + +void hyperv_x86_synic_update(X86CPU *cpu) +{ +} diff --git a/target/i386/hyperv.c b/target/i386/hyperv.c index 3065d765ed..b264a28620 100644 --- a/target/i386/hyperv.c +++ b/target/i386/hyperv.c @@ -14,16 +14,36 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "hyperv.h" +#include "hw/hyperv/hyperv.h" #include "hyperv-proto.h" -uint32_t hyperv_vp_index(X86CPU *cpu) +int hyperv_x86_synic_add(X86CPU *cpu) { - return CPU(cpu)->cpu_index; + hyperv_synic_add(CPU(cpu)); + return 0; } -X86CPU *hyperv_find_vcpu(uint32_t vp_index) +void hyperv_x86_synic_reset(X86CPU *cpu) { - return X86_CPU(qemu_get_cpu(vp_index)); + hyperv_synic_reset(CPU(cpu)); +} + +void hyperv_x86_synic_update(X86CPU *cpu) +{ + CPUX86State *env = &cpu->env; + bool enable = env->msr_hv_synic_control & HV_SYNIC_ENABLE; + hwaddr msg_page_addr = (env->msr_hv_synic_msg_page & HV_SIMP_ENABLE) ? + (env->msr_hv_synic_msg_page & TARGET_PAGE_MASK) : 0; + hwaddr event_page_addr = (env->msr_hv_synic_evt_page & HV_SIEFP_ENABLE) ? + (env->msr_hv_synic_evt_page & TARGET_PAGE_MASK) : 0; + hyperv_synic_update(CPU(cpu), enable, msg_page_addr, event_page_addr); +} + +static void async_synic_update(CPUState *cs, run_on_cpu_data data) +{ + qemu_mutex_lock_iothread(); + hyperv_x86_synic_update(X86_CPU(cs)); + qemu_mutex_unlock_iothread(); } int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) @@ -36,11 +56,6 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) 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; @@ -54,98 +69,33 @@ int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit) default: return -1; } + + /* + * this will run in this cpu thread before it returns to KVM, but in a + * safe environment (i.e. when all cpus are quiescent) -- this is + * necessary because memory hierarchy is being changed + */ + async_safe_run_on_cpu(CPU(cpu), async_synic_update, RUN_ON_CPU_NULL); + return 0; case KVM_EXIT_HYPERV_HCALL: { - uint16_t code; + uint16_t code = exit->u.hcall.input & 0xffff; + bool fast = exit->u.hcall.input & HV_HYPERCALL_FAST; + uint64_t param = exit->u.hcall.params[0]; - code = exit->u.hcall.input & 0xffff; switch (code) { case HV_POST_MESSAGE: + exit->u.hcall.result = hyperv_hcall_post_message(param, fast); + break; case HV_SIGNAL_EVENT: + exit->u.hcall.result = hyperv_hcall_signal_event(param, fast); + break; default: exit->u.hcall.result = HV_STATUS_INVALID_HYPERCALL_CODE; - return 0; } + 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 vp_index, 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, - kvm_hv_sint_ack_handler); - - gsi = kvm_irqchip_add_hv_sint_route(kvm_state, vp_index, 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->vp_index = vp_index; - 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, 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, 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/target/i386/hyperv.h b/target/i386/hyperv.h index 00c9b454bb..67543296c3 100644 --- a/target/i386/hyperv.h +++ b/target/i386/hyperv.h @@ -16,30 +16,14 @@ #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 vp_index; - int gsi; - EventNotifier sint_set_notifier; - EventNotifier sint_ack_notifier; - HvSintAckClb sint_ack_clb; -}; +#include "hw/hyperv/hyperv.h" +#ifdef CONFIG_KVM int kvm_hv_handle_exit(X86CPU *cpu, struct kvm_hyperv_exit *exit); +#endif -HvSintRoute *kvm_hv_sint_route_create(uint32_t vp_index, 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); - -uint32_t hyperv_vp_index(X86CPU *cpu); -X86CPU *hyperv_find_vcpu(uint32_t vp_index); +int hyperv_x86_synic_add(X86CPU *cpu); +void hyperv_x86_synic_reset(X86CPU *cpu); +void hyperv_x86_synic_update(X86CPU *cpu); #endif diff --git a/target/i386/kvm.c b/target/i386/kvm.c index 9313602d3d..3b29ce5c0d 100644 --- a/target/i386/kvm.c +++ b/target/i386/kvm.c @@ -95,6 +95,7 @@ static bool has_msr_xss; static bool has_msr_spec_ctrl; static bool has_msr_virt_ssbd; static bool has_msr_smi_count; +static bool has_msr_arch_capabs; static uint32_t has_architectural_pmu_version; static uint32_t num_architectural_pmu_gp_counters; @@ -107,6 +108,7 @@ static int has_pit_state2; static bool has_msr_mcg_ext_ctl; static struct kvm_cpuid2 *cpuid_cache; +static struct kvm_msr_list *kvm_feature_msrs; int kvm_has_pit_state2(void) { @@ -387,6 +389,15 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, if (host_tsx_blacklisted()) { ret &= ~(CPUID_7_0_EBX_RTM | CPUID_7_0_EBX_HLE); } + } else if (function == 7 && index == 0 && reg == R_EDX) { + /* + * Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts. + * We can detect the bug by checking if MSR_IA32_ARCH_CAPABILITIES is + * returned by KVM_GET_MSR_INDEX_LIST. + */ + if (!has_msr_arch_capabs) { + ret &= ~CPUID_7_0_EDX_ARCH_CAPABILITIES; + } } else if (function == 0x80000001 && reg == R_ECX) { /* * It's safe to enable TOPOEXT even if it's not returned by @@ -420,6 +431,42 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, return ret; } +uint32_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index) +{ + struct { + struct kvm_msrs info; + struct kvm_msr_entry entries[1]; + } msr_data; + uint32_t ret; + + if (kvm_feature_msrs == NULL) { /* Host doesn't support feature MSRs */ + return 0; + } + + /* Check if requested MSR is supported feature MSR */ + int i; + for (i = 0; i < kvm_feature_msrs->nmsrs; i++) + if (kvm_feature_msrs->indices[i] == index) { + break; + } + if (i == kvm_feature_msrs->nmsrs) { + return 0; /* if the feature MSR is not supported, simply return 0 */ + } + + msr_data.info.nmsrs = 1; + msr_data.entries[0].index = index; + + ret = kvm_ioctl(s, KVM_GET_MSRS, &msr_data); + if (ret != 1) { + error_report("KVM get MSR (index=0x%x) feature failed, %s", + index, strerror(-ret)); + exit(1); + } + + return msr_data.entries[0].data; +} + + typedef struct HWPoisonPage { ram_addr_t ram_addr; QLIST_ENTRY(HWPoisonPage) list; @@ -608,7 +655,8 @@ static bool hyperv_enabled(X86CPU *cpu) cpu->hyperv_synic || cpu->hyperv_stimer || cpu->hyperv_reenlightenment || - cpu->hyperv_tlbflush); + cpu->hyperv_tlbflush || + cpu->hyperv_ipi); } static int kvm_arch_set_tsc_khz(CPUState *cs) @@ -733,9 +781,20 @@ static int hyperv_handle_properties(CPUState *cs) env->features[FEAT_HYPERV_EAX] |= HV_VP_RUNTIME_AVAILABLE; } if (cpu->hyperv_synic) { - 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"); + unsigned int cap = KVM_CAP_HYPERV_SYNIC; + if (!cpu->hyperv_synic_kvm_only) { + if (!cpu->hyperv_vpindex) { + fprintf(stderr, "Hyper-V SynIC " + "(requested by 'hv-synic' cpu flag) " + "requires Hyper-V VP_INDEX ('hv-vpindex')\n"); + return -ENOSYS; + } + cap = KVM_CAP_HYPERV_SYNIC2; + } + + if (!has_msr_hv_synic || !kvm_check_extension(cs->kvm_state, cap)) { + fprintf(stderr, "Hyper-V SynIC (requested by 'hv-synic' cpu flag) " + "is not supported by kernel\n"); return -ENOSYS; } @@ -748,17 +807,61 @@ static int hyperv_handle_properties(CPUState *cs) } env->features[FEAT_HYPERV_EAX] |= HV_SYNTIMERS_AVAILABLE; } + if (cpu->hyperv_relaxed_timing) { + env->features[FEAT_HV_RECOMM_EAX] |= HV_RELAXED_TIMING_RECOMMENDED; + } + if (cpu->hyperv_vapic) { + env->features[FEAT_HV_RECOMM_EAX] |= HV_APIC_ACCESS_RECOMMENDED; + } + if (cpu->hyperv_tlbflush) { + if (kvm_check_extension(cs->kvm_state, + KVM_CAP_HYPERV_TLBFLUSH) <= 0) { + fprintf(stderr, "Hyper-V TLB flush support " + "(requested by 'hv-tlbflush' cpu flag) " + " is not supported by kernel\n"); + return -ENOSYS; + } + env->features[FEAT_HV_RECOMM_EAX] |= HV_REMOTE_TLB_FLUSH_RECOMMENDED; + env->features[FEAT_HV_RECOMM_EAX] |= HV_EX_PROCESSOR_MASKS_RECOMMENDED; + } + if (cpu->hyperv_ipi) { + if (kvm_check_extension(cs->kvm_state, + KVM_CAP_HYPERV_SEND_IPI) <= 0) { + fprintf(stderr, "Hyper-V IPI send support " + "(requested by 'hv-ipi' cpu flag) " + " is not supported by kernel\n"); + return -ENOSYS; + } + env->features[FEAT_HV_RECOMM_EAX] |= HV_CLUSTER_IPI_RECOMMENDED; + env->features[FEAT_HV_RECOMM_EAX] |= HV_EX_PROCESSOR_MASKS_RECOMMENDED; + } + if (cpu->hyperv_evmcs) { + uint16_t evmcs_version; + + if (kvm_vcpu_enable_cap(cs, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, 0, + (uintptr_t)&evmcs_version)) { + fprintf(stderr, "Hyper-V Enlightened VMCS " + "(requested by 'hv-evmcs' cpu flag) " + "is not supported by kernel\n"); + return -ENOSYS; + } + env->features[FEAT_HV_RECOMM_EAX] |= HV_ENLIGHTENED_VMCS_RECOMMENDED; + env->features[FEAT_HV_NESTED_EAX] = evmcs_version; + } + return 0; } static int hyperv_init_vcpu(X86CPU *cpu) { + CPUState *cs = CPU(cpu); + int ret; + if (cpu->hyperv_vpindex && !hv_vpindex_settable) { /* * the kernel doesn't support setting vp_index; assert that its value * is in sync */ - int ret; struct { struct kvm_msrs info; struct kvm_msr_entry entries[1]; @@ -767,22 +870,43 @@ static int hyperv_init_vcpu(X86CPU *cpu) .entries[0].index = HV_X64_MSR_VP_INDEX, }; - ret = kvm_vcpu_ioctl(CPU(cpu), KVM_GET_MSRS, &msr_data); + ret = kvm_vcpu_ioctl(cs, KVM_GET_MSRS, &msr_data); if (ret < 0) { return ret; } assert(ret == 1); - if (msr_data.entries[0].data != hyperv_vp_index(cpu)) { + if (msr_data.entries[0].data != hyperv_vp_index(CPU(cpu))) { error_report("kernel's vp_index != QEMU's vp_index"); return -ENXIO; } } + if (cpu->hyperv_synic) { + uint32_t synic_cap = cpu->hyperv_synic_kvm_only ? + KVM_CAP_HYPERV_SYNIC : KVM_CAP_HYPERV_SYNIC2; + ret = kvm_vcpu_enable_cap(cs, synic_cap, 0); + if (ret < 0) { + error_report("failed to turn on HyperV SynIC in KVM: %s", + strerror(-ret)); + return ret; + } + + if (!cpu->hyperv_synic_kvm_only) { + ret = hyperv_x86_synic_add(cpu); + if (ret < 0) { + error_report("failed to create HyperV SynIC: %s", + strerror(-ret)); + return ret; + } + } + } + return 0; } static Error *invtsc_mig_blocker; +static Error *vmx_mig_blocker; #define KVM_MAX_CPUID_ENTRIES 100 @@ -791,7 +915,15 @@ int kvm_arch_init_vcpu(CPUState *cs) struct { struct kvm_cpuid2 cpuid; struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; - } QEMU_PACKED cpuid_data; + } cpuid_data; + /* + * The kernel defines these structs with padding fields so there + * should be no extra padding in our cpuid_data struct. + */ + QEMU_BUILD_BUG_ON(sizeof(cpuid_data) != + sizeof(struct kvm_cpuid2) + + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; uint32_t limit, i, j, cpuid_i; @@ -841,7 +973,8 @@ int kvm_arch_init_vcpu(CPUState *cs) memset(signature, 0, 12); memcpy(signature, cpu->hyperv_vendor_id, len); } - c->eax = HV_CPUID_MIN; + c->eax = cpu->hyperv_evmcs ? + HV_CPUID_NESTED_FEATURES : HV_CPUID_IMPLEMENT_LIMITS; c->ebx = signature[0]; c->ecx = signature[1]; c->edx = signature[2]; @@ -871,24 +1004,8 @@ int kvm_arch_init_vcpu(CPUState *cs) c = &cpuid_data.entries[cpuid_i++]; c->function = HV_CPUID_ENLIGHTMENT_INFO; - if (cpu->hyperv_relaxed_timing) { - c->eax |= HV_RELAXED_TIMING_RECOMMENDED; - } - if (cpu->hyperv_vapic) { - c->eax |= HV_APIC_ACCESS_RECOMMENDED; - } - if (cpu->hyperv_tlbflush) { - if (kvm_check_extension(cs->kvm_state, - KVM_CAP_HYPERV_TLBFLUSH) <= 0) { - fprintf(stderr, "Hyper-V TLB flush support " - "(requested by 'hv-tlbflush' cpu flag) " - " is not supported by kernel\n"); - return -ENOSYS; - } - c->eax |= HV_REMOTE_TLB_FLUSH_RECOMMENDED; - c->eax |= HV_EX_PROCESSOR_MASKS_RECOMMENDED; - } + c->eax = env->features[FEAT_HV_RECOMM_EAX]; c->ebx = cpu->hyperv_spinlock_attempts; c = &cpuid_data.entries[cpuid_i++]; @@ -899,6 +1016,21 @@ int kvm_arch_init_vcpu(CPUState *cs) kvm_base = KVM_CPUID_SIGNATURE_NEXT; has_msr_hv_hypercall = true; + + if (cpu->hyperv_evmcs) { + __u32 function; + + /* Create zeroed 0x40000006..0x40000009 leaves */ + for (function = HV_CPUID_IMPLEMENT_LIMITS + 1; + function < HV_CPUID_NESTED_FEATURES; function++) { + c = &cpuid_data.entries[cpuid_i++]; + c->function = function; + } + + c = &cpuid_data.entries[cpuid_i++]; + c->function = HV_CPUID_NESTED_FEATURES; + c->eax = env->features[FEAT_HV_NESTED_EAX]; + } } if (cpu->expose_kvm) { @@ -1138,6 +1270,17 @@ int kvm_arch_init_vcpu(CPUState *cs) !!(c->ecx & CPUID_EXT_SMX); } + if ((env->features[FEAT_1_ECX] & CPUID_EXT_VMX) && !vmx_mig_blocker) { + error_setg(&vmx_mig_blocker, + "Nested VMX virtualization does not support live migration yet"); + r = migrate_add_blocker(vmx_mig_blocker, &local_err); + if (local_err) { + error_report_err(local_err); + error_free(vmx_mig_blocker); + return r; + } + } + if (env->mcg_cap & MCG_LMCE_P) { has_msr_mcg_ext_ctl = has_msr_feature_control = true; } @@ -1145,7 +1288,6 @@ int kvm_arch_init_vcpu(CPUState *cs) if (!env->user_tsc_khz) { if ((env->features[FEAT_8000_0007_EDX] & CPUID_APM_INVTSC) && invtsc_mig_blocker == NULL) { - /* for migration */ error_setg(&invtsc_mig_blocker, "State blocked by non-migratable CPU device" " (invtsc flag)"); @@ -1153,10 +1295,8 @@ int kvm_arch_init_vcpu(CPUState *cs) if (local_err) { error_report_err(local_err); error_free(invtsc_mig_blocker); - goto fail; + return r; } - /* for savevm */ - vmstate_x86_cpu.unmigratable = 1; } } @@ -1189,7 +1329,7 @@ int kvm_arch_init_vcpu(CPUState *cs) } if (has_xsave) { - env->kvm_xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave)); + env->xsave_buf = qemu_memalign(4096, sizeof(struct kvm_xsave)); } cpu->kvm_msr_buf = g_malloc0(MSR_BUF_SIZE); @@ -1226,6 +1366,8 @@ void kvm_arch_reset_vcpu(X86CPU *cpu) for (i = 0; i < ARRAY_SIZE(env->msr_hv_synic_sint); i++) { env->msr_hv_synic_sint[i] = HV_SINT_MASKED; } + + hyperv_x86_synic_reset(cpu); } } @@ -1239,6 +1381,47 @@ void kvm_arch_do_init_vcpu(X86CPU *cpu) } } +static int kvm_get_supported_feature_msrs(KVMState *s) +{ + int ret = 0; + + if (kvm_feature_msrs != NULL) { + return 0; + } + + if (!kvm_check_extension(s, KVM_CAP_GET_MSR_FEATURES)) { + return 0; + } + + struct kvm_msr_list msr_list; + + msr_list.nmsrs = 0; + ret = kvm_ioctl(s, KVM_GET_MSR_FEATURE_INDEX_LIST, &msr_list); + if (ret < 0 && ret != -E2BIG) { + error_report("Fetch KVM feature MSR list failed: %s", + strerror(-ret)); + return ret; + } + + assert(msr_list.nmsrs > 0); + kvm_feature_msrs = (struct kvm_msr_list *) \ + g_malloc0(sizeof(msr_list) + + msr_list.nmsrs * sizeof(msr_list.indices[0])); + + kvm_feature_msrs->nmsrs = msr_list.nmsrs; + ret = kvm_ioctl(s, KVM_GET_MSR_FEATURE_INDEX_LIST, kvm_feature_msrs); + + if (ret < 0) { + error_report("Fetch KVM feature MSR list failed: %s", + strerror(-ret)); + g_free(kvm_feature_msrs); + kvm_feature_msrs = NULL; + return ret; + } + + return 0; +} + static int kvm_get_supported_msrs(KVMState *s) { static int kvm_supported_msrs; @@ -1330,6 +1513,9 @@ static int kvm_get_supported_msrs(KVMState *s) case MSR_VIRT_SSBD: has_msr_virt_ssbd = true; break; + case MSR_IA32_ARCH_CAPABILITIES: + has_msr_arch_capabs = true; + break; } } } @@ -1381,17 +1567,9 @@ 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 hv_vpindex_settable = kvm_check_extension(s, KVM_CAP_HYPERV_VP_INDEX); @@ -1400,6 +1578,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) return ret; } + kvm_get_supported_feature_msrs(s); + uname(&utsname); lm_capable_kernel = strcmp(utsname.machine, "x86_64") == 0; @@ -1647,7 +1827,7 @@ ASSERT_OFFSET(XSAVE_PKRU, pkru_state); static int kvm_put_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; - X86XSaveArea *xsave = env->kvm_xsave_buf; + X86XSaveArea *xsave = env->xsave_buf; if (!has_xsave) { return kvm_put_fpu(cpu); @@ -1856,6 +2036,12 @@ static int kvm_put_msrs(X86CPU *cpu, int level) } #endif + /* If host supports feature MSR, write down. */ + if (has_msr_arch_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, + env->features[FEAT_ARCH_CAPABILITIES]); + } + /* * The following MSRs have side effects on the guest or are too heavy * for normal writeback. Limit them to reset or full state updates. @@ -1945,7 +2131,8 @@ static int kvm_put_msrs(X86CPU *cpu, int level) kvm_msr_entry_add(cpu, HV_X64_MSR_VP_RUNTIME, env->msr_hv_runtime); } if (cpu->hyperv_vpindex && hv_vpindex_settable) { - kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX, hyperv_vp_index(cpu)); + kvm_msr_entry_add(cpu, HV_X64_MSR_VP_INDEX, + hyperv_vp_index(CPU(cpu))); } if (cpu->hyperv_synic) { int j; @@ -2089,7 +2276,7 @@ static int kvm_get_fpu(X86CPU *cpu) static int kvm_get_xsave(X86CPU *cpu) { CPUX86State *env = &cpu->env; - X86XSaveArea *xsave = env->kvm_xsave_buf; + X86XSaveArea *xsave = env->xsave_buf; int ret; if (!has_xsave) { @@ -2694,7 +2881,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) events.exception.nr = env->exception_injected; events.exception.has_error_code = env->has_error_code; events.exception.error_code = env->error_code; - events.exception.pad = 0; events.interrupt.injected = (env->interrupt_injected >= 0); events.interrupt.nr = env->interrupt_injected; @@ -2703,7 +2889,6 @@ static int kvm_put_vcpu_events(X86CPU *cpu, int level) events.nmi.injected = env->nmi_injected; events.nmi.pending = env->nmi_pending; events.nmi.masked = !!(env->hflags2 & HF2_NMI_MASK); - events.nmi.pad = 0; events.sipi_vector = env->sipi_vector; events.flags = 0; @@ -3677,6 +3862,10 @@ int kvm_arch_fixup_msi_route(struct kvm_irq_routing_entry *route, MSIMessage src, dst; X86IOMMUClass *class = X86_IOMMU_GET_CLASS(iommu); + if (!class->int_remap) { + return 0; + } + src.address = route->u.msi.address_hi; src.address <<= VTD_MSI_ADDR_HI_SHIFT; src.address |= route->u.msi.address_lo; @@ -3714,7 +3903,7 @@ static QLIST_HEAD(, MSIRouteEntry) msi_route_list = \ static void kvm_update_msi_routes_all(void *private, bool global, uint32_t index, uint32_t mask) { - int cnt = 0; + int cnt = 0, vector; MSIRouteEntry *entry; MSIMessage msg; PCIDevice *dev; @@ -3722,11 +3911,19 @@ static void kvm_update_msi_routes_all(void *private, bool global, /* TODO: explicit route update */ QLIST_FOREACH(entry, &msi_route_list, list) { cnt++; + vector = entry->vector; dev = entry->dev; - if (!msix_enabled(dev) && !msi_enabled(dev)) { + if (msix_enabled(dev) && !msix_is_masked(dev, vector)) { + msg = msix_get_message(dev, vector); + } else if (msi_enabled(dev) && !msi_is_masked(dev, vector)) { + msg = msi_get_message(dev, vector); + } else { + /* + * Either MSI/MSIX is disabled for the device, or the + * specific message was masked out. Skip this one. + */ continue; } - msg = pci_get_msi_message(dev, entry->vector); kvm_irqchip_update_msi_route(kvm_state, entry->virq, msg, dev); } kvm_irqchip_commit_routes(kvm_state); diff --git a/target/i386/machine.c b/target/i386/machine.c index 084c2c73a8..225b5d433b 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -7,6 +7,7 @@ #include "hw/i386/pc.h" #include "hw/isa/isa.h" #include "migration/cpu.h" +#include "hyperv.h" #include "sysemu/kvm.h" @@ -672,11 +673,19 @@ static bool hyperv_synic_enable_needed(void *opaque) return false; } +static int hyperv_synic_post_load(void *opaque, int version_id) +{ + X86CPU *cpu = opaque; + hyperv_x86_synic_update(cpu); + return 0; +} + static const VMStateDescription vmstate_msr_hyperv_synic = { .name = "cpu/msr_hyperv_synic", .version_id = 1, .minimum_version_id = 1, .needed = hyperv_synic_enable_needed, + .post_load = hyperv_synic_post_load, .fields = (VMStateField[]) { VMSTATE_UINT64(env.msr_hv_synic_control, X86CPU), VMSTATE_UINT64(env.msr_hv_synic_evt_page, X86CPU), diff --git a/target/i386/mem_helper.c b/target/i386/mem_helper.c index 30c26b9d9c..6cc53bcb40 100644 --- a/target/i386/mem_helper.c +++ b/target/i386/mem_helper.c @@ -23,6 +23,7 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "qemu/int128.h" +#include "qemu/atomic128.h" #include "tcg.h" void helper_cmpxchg8b_unlocked(CPUX86State *env, target_ulong a0) @@ -137,10 +138,7 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) if ((a0 & 0xf) != 0) { raise_exception_ra(env, EXCP0D_GPF, ra); - } else { -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else + } else if (HAVE_CMPXCHG128) { int eflags = cpu_cc_compute_all(env, CC_OP); Int128 cmpv = int128_make128(env->regs[R_EAX], env->regs[R_EDX]); @@ -159,7 +157,8 @@ void helper_cmpxchg16b(CPUX86State *env, target_ulong a0) eflags &= ~CC_Z; } CC_SRC = eflags; -#endif + } else { + cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); } } #endif diff --git a/target/i386/seg_helper.c b/target/i386/seg_helper.c index 00301a0c04..63e265cb38 100644 --- a/target/i386/seg_helper.c +++ b/target/i386/seg_helper.c @@ -518,6 +518,11 @@ static void switch_tss(CPUX86State *env, int tss_selector, static inline unsigned int get_sp_mask(unsigned int e2) { +#ifdef TARGET_X86_64 + if (e2 & DESC_L_MASK) { + return 0; + } else +#endif if (e2 & DESC_B_MASK) { return 0xffffffff; } else { @@ -986,11 +991,11 @@ void helper_syscall(CPUX86State *env, int next_eip_addend) int code64; env->regs[R_ECX] = env->eip + next_eip_addend; - env->regs[11] = cpu_compute_eflags(env); + env->regs[11] = cpu_compute_eflags(env) & ~RF_MASK; code64 = env->hflags & HF_CS64_MASK; - env->eflags &= ~env->fmask; + env->eflags &= ~(env->fmask | RF_MASK); cpu_load_eflags(env, env->eflags, 0); cpu_x86_load_seg_cache(env, R_CS, selector & 0xfffc, 0, 0xffffffff, @@ -1239,7 +1244,7 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, } if (env->cr[0] & CR0_PE_MASK) { #if !defined(CONFIG_USER_ONLY) - if (env->hflags & HF_SVMI_MASK) { + if (env->hflags & HF_GUEST_MASK) { handle_even_inj(env, intno, is_int, error_code, is_hw, 0); } #endif @@ -1254,7 +1259,7 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, } } else { #if !defined(CONFIG_USER_ONLY) - if (env->hflags & HF_SVMI_MASK) { + if (env->hflags & HF_GUEST_MASK) { handle_even_inj(env, intno, is_int, error_code, is_hw, 1); } #endif @@ -1262,7 +1267,7 @@ static void do_interrupt_all(X86CPU *cpu, int intno, int is_int, } #if !defined(CONFIG_USER_ONLY) - if (env->hflags & HF_SVMI_MASK) { + if (env->hflags & HF_GUEST_MASK) { CPUState *cs = CPU(cpu); uint32_t event_inj = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, @@ -1314,74 +1319,66 @@ bool x86_cpu_exec_interrupt(CPUState *cs, int interrupt_request) { X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - bool ret = false; + int intno; + interrupt_request = x86_cpu_pending_interrupt(cs, interrupt_request); + if (!interrupt_request) { + return false; + } + + /* Don't process multiple interrupt requests in a single call. + * This is required to make icount-driven execution deterministic. + */ + switch (interrupt_request) { #if !defined(CONFIG_USER_ONLY) - if (interrupt_request & CPU_INTERRUPT_POLL) { + case 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; - } + break; #endif - if (interrupt_request & CPU_INTERRUPT_SIPI) { + case CPU_INTERRUPT_SIPI: do_cpu_sipi(cpu); - ret = true; - } else if (env->hflags2 & HF2_GIF_MASK) { - if ((interrupt_request & CPU_INTERRUPT_SMI) && - !(env->hflags & HF_SMM_MASK)) { - cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_SMI; - do_smm_enter(cpu); - ret = true; - } else if ((interrupt_request & CPU_INTERRUPT_NMI) && - !(env->hflags2 & HF2_NMI_MASK)) { - cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); - cs->interrupt_request &= ~CPU_INTERRUPT_NMI; - env->hflags2 |= HF2_NMI_MASK; - do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); - ret = true; - } else if (interrupt_request & CPU_INTERRUPT_MCE) { - cs->interrupt_request &= ~CPU_INTERRUPT_MCE; - do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); - ret = true; - } else if ((interrupt_request & CPU_INTERRUPT_HARD) && - (((env->hflags2 & HF2_VINTR_MASK) && - (env->hflags2 & HF2_HIF_MASK)) || - (!(env->hflags2 & HF2_VINTR_MASK) && - (env->eflags & IF_MASK && - !(env->hflags & HF_INHIBIT_IRQ_MASK))))) { - int intno; - cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); - cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | - CPU_INTERRUPT_VIRQ); - intno = cpu_get_pic_interrupt(env); - qemu_log_mask(CPU_LOG_TB_IN_ASM, - "Servicing hardware INT=0x%02x\n", intno); - do_interrupt_x86_hardirq(env, intno, 1); - /* ensure that no TB jump will be modified as - the program flow was changed */ - ret = true; + break; + case CPU_INTERRUPT_SMI: + cpu_svm_check_intercept_param(env, SVM_EXIT_SMI, 0, 0); + cs->interrupt_request &= ~CPU_INTERRUPT_SMI; + do_smm_enter(cpu); + break; + case CPU_INTERRUPT_NMI: + cpu_svm_check_intercept_param(env, SVM_EXIT_NMI, 0, 0); + cs->interrupt_request &= ~CPU_INTERRUPT_NMI; + env->hflags2 |= HF2_NMI_MASK; + do_interrupt_x86_hardirq(env, EXCP02_NMI, 1); + break; + case CPU_INTERRUPT_MCE: + cs->interrupt_request &= ~CPU_INTERRUPT_MCE; + do_interrupt_x86_hardirq(env, EXCP12_MCHK, 0); + break; + case CPU_INTERRUPT_HARD: + cpu_svm_check_intercept_param(env, SVM_EXIT_INTR, 0, 0); + cs->interrupt_request &= ~(CPU_INTERRUPT_HARD | + CPU_INTERRUPT_VIRQ); + intno = cpu_get_pic_interrupt(env); + qemu_log_mask(CPU_LOG_TB_IN_ASM, + "Servicing hardware INT=0x%02x\n", intno); + do_interrupt_x86_hardirq(env, intno, 1); + break; #if !defined(CONFIG_USER_ONLY) - } else if ((interrupt_request & CPU_INTERRUPT_VIRQ) && - (env->eflags & IF_MASK) && - !(env->hflags & HF_INHIBIT_IRQ_MASK)) { - int intno; - /* FIXME: this should respect TPR */ - cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0); - intno = x86_ldl_phys(cs, env->vm_vmcb + case CPU_INTERRUPT_VIRQ: + /* FIXME: this should respect TPR */ + cpu_svm_check_intercept_param(env, SVM_EXIT_VINTR, 0, 0); + intno = x86_ldl_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.int_vector)); - qemu_log_mask(CPU_LOG_TB_IN_ASM, - "Servicing virtual hardware INT=0x%02x\n", intno); - do_interrupt_x86_hardirq(env, intno, 1); - cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; - ret = true; + qemu_log_mask(CPU_LOG_TB_IN_ASM, + "Servicing virtual hardware INT=0x%02x\n", intno); + do_interrupt_x86_hardirq(env, intno, 1); + cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; + break; #endif - } } - return ret; + /* Ensure that no TB jump will be modified as the program flow was changed. */ + return true; } void helper_lldt(CPUX86State *env, int selector) @@ -1628,8 +1625,8 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, } limit = get_seg_limit(e1, e2); if (new_eip > limit && - !(env->hflags & HF_LMA_MASK) && !(e2 & DESC_L_MASK)) { - raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { + raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl, get_seg_base(e1, e2), limit, e2); @@ -1640,6 +1637,14 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, rpl = new_cs & 3; cpl = env->hflags & HF_CPL_MASK; type = (e2 >> DESC_TYPE_SHIFT) & 0xf; + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (type != 12) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } +#endif switch (type) { case 1: /* 286 TSS */ case 9: /* 386 TSS */ @@ -1662,6 +1667,23 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (type == 12) { new_eip |= (e2 & 0xffff0000); } + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + /* load the upper 8 bytes of the 64-bit call gate */ + if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + if (type != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + new_eip |= ((target_ulong)e1) << 32; + } +#endif + if (load_segment_ra(env, &e1, &e2, gate_cs, GETPC()) != 0) { raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } @@ -1675,11 +1697,22 @@ void helper_ljmp_protected(CPUX86State *env, int new_cs, target_ulong new_eip, (!(e2 & DESC_C_MASK) && (dpl != cpl))) { raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (!(e2 & DESC_L_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + if (e2 & DESC_B_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); + } + } +#endif if (!(e2 & DESC_P_MASK)) { raise_exception_err_ra(env, EXCP0D_GPF, gate_cs & 0xfffc, GETPC()); } limit = get_seg_limit(e1, e2); - if (new_eip > limit) { + if (new_eip > limit && + (!(env->hflags & HF_LMA_MASK) || !(e2 & DESC_L_MASK))) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } cpu_x86_load_seg_cache(env, R_CS, (gate_cs & 0xfffc) | cpl, @@ -1724,12 +1757,12 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, 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 e1, e2, cpl, dpl, rpl, selector, param_count; + uint32_t ss = 0, ss_e1 = 0, ss_e2 = 0, type, ss_dpl, sp_mask; uint32_t val, limit, old_sp_mask; - target_ulong ssp, old_ssp; + target_ulong ssp, old_ssp, offset, sp; - LOG_PCALL("lcall %04x:%08x s=%d\n", new_cs, (uint32_t)new_eip, shift); + LOG_PCALL("lcall %04x:" TARGET_FMT_lx " s=%d\n", new_cs, new_eip, shift); LOG_PCALL_STATE(CPU(x86_env_get_cpu(env))); if ((new_cs & 0xfffc) == 0) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); @@ -1807,6 +1840,15 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; dpl = (e2 >> DESC_DPL_SHIFT) & 3; rpl = new_cs & 3; + +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (type != 12) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, GETPC()); + } + } +#endif + switch (type) { case 1: /* available 286 TSS */ case 9: /* available 386 TSS */ @@ -1833,8 +1875,23 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, raise_exception_err_ra(env, EXCP0B_NOSEG, new_cs & 0xfffc, GETPC()); } selector = e1 >> 16; - offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); param_count = e2 & 0x1f; + offset = (e2 & 0xffff0000) | (e1 & 0x0000ffff); +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + /* load the upper 8 bytes of the 64-bit call gate */ + if (load_segment_ra(env, &e1, &e2, new_cs + 8, GETPC())) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; + if (type != 0) { + raise_exception_err_ra(env, EXCP0D_GPF, new_cs & 0xfffc, + GETPC()); + } + offset |= ((target_ulong)e1) << 32; + } +#endif if ((selector & 0xfffc) == 0) { raise_exception_err_ra(env, EXCP0D_GPF, 0, GETPC()); } @@ -1849,46 +1906,80 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, if (dpl > cpl) { raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); } +#ifdef TARGET_X86_64 + if (env->efer & MSR_EFER_LMA) { + if (!(e2 & DESC_L_MASK)) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + if (e2 & DESC_B_MASK) { + raise_exception_err_ra(env, EXCP0D_GPF, selector & 0xfffc, GETPC()); + } + shift++; + } +#endif if (!(e2 & DESC_P_MASK)) { 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, 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_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if ((ss & 3) != dpl) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - 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_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_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); - } - if (!(ss_e2 & DESC_P_MASK)) { - raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); +#ifdef TARGET_X86_64 + if (shift == 2) { + sp = get_rsp_from_tss(env, dpl); + ss = dpl; /* SS = NULL selector with RPL = new CPL */ + new_stack = 1; + sp_mask = 0; + ssp = 0; /* SS base is always zero in IA-32e mode */ + LOG_PCALL("new ss:rsp=%04x:%016llx env->regs[R_ESP]=" + TARGET_FMT_lx "\n", ss, sp, env->regs[R_ESP]); + } else +#endif + { + uint32_t sp32; + get_ss_esp_from_tss(env, &ss, &sp32, dpl, GETPC()); + LOG_PCALL("new ss:esp=%04x:%08x param_count=%d env->regs[R_ESP]=" + TARGET_FMT_lx "\n", ss, sp32, param_count, + env->regs[R_ESP]); + sp = sp32; + if ((ss & 0xfffc) == 0) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if ((ss & 3) != dpl) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + 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_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_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + if (!(ss_e2 & DESC_P_MASK)) { + raise_exception_err_ra(env, EXCP0A_TSS, ss & 0xfffc, GETPC()); + } + + sp_mask = get_sp_mask(ss_e2); + ssp = get_seg_base(ss_e1, ss_e2); } /* push_size = ((param_count * 2) + 8) << shift; */ old_sp_mask = get_sp_mask(env->segs[R_SS].flags); old_ssp = env->segs[R_SS].base; - - sp_mask = get_sp_mask(ss_e2); - ssp = get_seg_base(ss_e1, ss_e2); - if (shift) { +#ifdef TARGET_X86_64 + if (shift == 2) { + /* XXX: verify if new stack address is canonical */ + PUSHQ_RA(sp, env->segs[R_SS].selector, GETPC()); + PUSHQ_RA(sp, env->regs[R_ESP], GETPC()); + /* parameters aren't supported for 64-bit call gates */ + } else +#endif + if (shift == 1) { 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--) { @@ -1917,7 +2008,13 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, new_stack = 0; } - if (shift) { +#ifdef TARGET_X86_64 + if (shift == 2) { + PUSHQ_RA(sp, env->segs[R_CS].selector, GETPC()); + PUSHQ_RA(sp, next_eip, GETPC()); + } else +#endif + if (shift == 1) { PUSHL_RA(ssp, sp, sp_mask, env->segs[R_CS].selector, GETPC()); PUSHL_RA(ssp, sp, sp_mask, next_eip, GETPC()); } else { @@ -1928,11 +2025,18 @@ void helper_lcall_protected(CPUX86State *env, int new_cs, target_ulong new_eip, /* from this point, not restartable */ if (new_stack) { - ss = (ss & ~3) | dpl; - cpu_x86_load_seg_cache(env, R_SS, ss, - ssp, - get_seg_limit(ss_e1, ss_e2), - ss_e2); +#ifdef TARGET_X86_64 + if (shift == 2) { + cpu_x86_load_seg_cache(env, R_SS, ss, 0, 0, 0); + } else +#endif + { + ss = (ss & ~3) | dpl; + cpu_x86_load_seg_cache(env, R_SS, ss, + ssp, + get_seg_limit(ss_e1, ss_e2), + ss_e2); + } } selector = (selector & ~3) | dpl; diff --git a/target/i386/sev.c b/target/i386/sev.c index 2395171acf..cd77f6b5d4 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -11,12 +11,13 @@ * */ +#include "qemu/osdep.h" + #include #include #include -#include "qemu/osdep.h" #include "qapi/error.h" #include "qom/object_interfaces.h" #include "qemu/base64.h" @@ -130,6 +131,17 @@ sev_ram_block_added(RAMBlockNotifier *n, void *host, size_t size) { int r; struct kvm_enc_region range; + ram_addr_t offset; + MemoryRegion *mr; + + /* + * The RAM device presents a memory region that should be treated + * as IO region and should not be pinned. + */ + mr = memory_region_from_host(host, &offset); + if (mr && memory_region_is_ram_device(mr)) { + return; + } range.addr = (__u64)(unsigned long)host; range.size = size; diff --git a/target/i386/sev_i386.h b/target/i386/sev_i386.h index b8622dfb1e..c0f9373beb 100644 --- a/target/i386/sev_i386.h +++ b/target/i386/sev_i386.h @@ -19,7 +19,7 @@ #include "sysemu/kvm.h" #include "sysemu/sev.h" #include "qemu/error-report.h" -#include "qapi/qapi-commands-misc.h" +#include "qapi/qapi-commands-target.h" #define SEV_POLICY_NODBG 0x1 #define SEV_POLICY_NOKS 0x2 diff --git a/target/i386/svm_helper.c b/target/i386/svm_helper.c index 342ece082f..9fd22a883b 100644 --- a/target/i386/svm_helper.c +++ b/target/i386/svm_helper.c @@ -228,7 +228,7 @@ void helper_vmrun(CPUX86State *env, int aflag, int next_eip_addend) } /* enable intercepts */ - env->hflags |= HF_SVMI_MASK; + env->hflags |= HF_GUEST_MASK; env->tsc_offset = x86_ldq_phys(cs, env->vm_vmcb + offsetof(struct vmcb, control.tsc_offset)); @@ -503,7 +503,7 @@ void cpu_svm_check_intercept_param(CPUX86State *env, uint32_t type, { CPUState *cs = CPU(x86_env_get_cpu(env)); - if (likely(!(env->hflags & HF_SVMI_MASK))) { + if (likely(!(env->hflags & HF_GUEST_MASK))) { return; } switch (type) { @@ -697,7 +697,7 @@ void do_vmexit(CPUX86State *env, uint32_t exit_code, uint64_t exit_info_1) /* Reload the host state from vm_hsave */ env->hflags2 &= ~(HF2_HIF_MASK | HF2_VINTR_MASK); - env->hflags &= ~HF_SVMI_MASK; + env->hflags &= ~HF_GUEST_MASK; env->intercept = 0; env->intercept_exceptions = 0; cs->interrupt_request &= ~CPU_INTERRUPT_VIRQ; diff --git a/target/i386/trace-events b/target/i386/trace-events index 6a19a69af5..789c700d4a 100644 --- a/target/i386/trace-events +++ b/target/i386/trace-events @@ -1,12 +1,12 @@ # See docs/devel/tracing.txt for syntax documentation. -# target/i386/kvm.c +# kvm.c kvm_x86_fixup_msi_error(uint32_t gsi) "VT-d failed to remap interrupt for GSI %" PRIu32 kvm_x86_add_msi_route(int virq) "Adding route entry for virq %d" kvm_x86_remove_msi_route(int virq) "Removing route entry for virq %d" kvm_x86_update_msi_routes(int num) "Updated %d MSI routes" -# target/i386/sev.c +# sev.c kvm_sev_init(void) "" kvm_memcrypt_register_region(void *addr, size_t len) "addr %p len 0x%zu" kvm_memcrypt_unregister_region(void *addr, size_t len) "addr %p len 0x%zu" diff --git a/target/i386/translate.c b/target/i386/translate.c index 07d185e7b6..b725bec37c 100644 --- a/target/i386/translate.c +++ b/target/i386/translate.c @@ -72,27 +72,15 @@ //#define MACRO_TEST 1 /* global register indexes */ -static TCGv cpu_A0; -static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2, cpu_cc_srcT; +static TCGv cpu_cc_dst, cpu_cc_src, cpu_cc_src2; 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_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; #include "exec/gen-icount.h" -#ifdef TARGET_X86_64 -static int x86_64_hregs; -#endif - typedef struct DisasContext { DisasContextBase base; @@ -117,6 +105,9 @@ typedef struct DisasContext { int ss32; /* 32 bit stack segment */ CCOp cc_op; /* current CC operation */ bool cc_op_dirty; +#ifdef TARGET_X86_64 + bool x86_64_hregs; +#endif int addseg; /* non zero if either DS/ES/SS have a non zero base */ int f_st; /* currently unused */ int vm86; /* vm86 mode */ @@ -135,6 +126,22 @@ typedef struct DisasContext { int cpuid_ext3_features; int cpuid_7_0_ebx_features; int cpuid_xsave_features; + + /* TCG local temps */ + TCGv cc_srcT; + TCGv A0; + TCGv T0; + TCGv T1; + + /* TCG local register indexes (only used inside old micro ops) */ + TCGv tmp0; + TCGv tmp4; + TCGv_ptr ptr0; + TCGv_ptr ptr1; + TCGv_i32 tmp2_i32; + TCGv_i32 tmp3_i32; + TCGv_i64 tmp1_i64; + sigjmp_buf jmpbuf; } DisasContext; @@ -244,7 +251,7 @@ static void set_cc_op(DisasContext *s, CCOp op) tcg_gen_discard_tl(cpu_cc_src2); } if (dead & USES_CC_SRCT) { - tcg_gen_discard_tl(cpu_cc_srcT); + tcg_gen_discard_tl(s->cc_srcT); } if (op == CC_OP_DYNAMIC) { @@ -299,13 +306,13 @@ static void gen_update_cc_op(DisasContext *s) * [AH, CH, DH, BH], ie "bits 15..8 of register N-4". Return * true for this special case, false otherwise. */ -static inline bool byte_reg_is_xH(int reg) +static inline bool byte_reg_is_xH(DisasContext *s, int reg) { if (reg < 4) { return false; } #ifdef TARGET_X86_64 - if (reg >= 8 || x86_64_hregs) { + if (reg >= 8 || s->x86_64_hregs) { return false; } #endif @@ -352,11 +359,11 @@ static inline TCGMemOp mo_b_d32(int b, TCGMemOp ot) return b & 1 ? (ot == MO_16 ? MO_16 : MO_32) : MO_8; } -static void gen_op_mov_reg_v(TCGMemOp ot, int reg, TCGv t0) +static void gen_op_mov_reg_v(DisasContext *s, TCGMemOp ot, int reg, TCGv t0) { switch(ot) { case MO_8: - if (!byte_reg_is_xH(reg)) { + if (!byte_reg_is_xH(s, reg)) { tcg_gen_deposit_tl(cpu_regs[reg], cpu_regs[reg], t0, 0, 8); } else { tcg_gen_deposit_tl(cpu_regs[reg - 4], cpu_regs[reg - 4], t0, 8, 8); @@ -380,9 +387,10 @@ static void gen_op_mov_reg_v(TCGMemOp ot, int reg, TCGv t0) } } -static inline void gen_op_mov_v_reg(TCGMemOp ot, TCGv t0, int reg) +static inline +void gen_op_mov_v_reg(DisasContext *s, TCGMemOp ot, TCGv t0, int reg) { - if (ot == MO_8 && byte_reg_is_xH(reg)) { + if (ot == MO_8 && byte_reg_is_xH(s, reg)) { tcg_gen_extract_tl(t0, cpu_regs[reg - 4], 8, 8); } else { tcg_gen_mov_tl(t0, cpu_regs[reg]); @@ -391,9 +399,9 @@ static inline void gen_op_mov_v_reg(TCGMemOp ot, TCGv t0, int reg) static void gen_add_A0_im(DisasContext *s, int val) { - tcg_gen_addi_tl(cpu_A0, cpu_A0, val); + tcg_gen_addi_tl(s->A0, s->A0, val); if (!CODE64(s)) { - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + tcg_gen_ext32u_tl(s->A0, s->A0); } } @@ -402,16 +410,17 @@ static inline void gen_op_jmp_v(TCGv dest) tcg_gen_st_tl(dest, cpu_env, offsetof(CPUX86State, eip)); } -static inline void gen_op_add_reg_im(TCGMemOp size, int reg, int32_t val) +static inline +void gen_op_add_reg_im(DisasContext *s, TCGMemOp size, int reg, int32_t val) { - tcg_gen_addi_tl(cpu_tmp0, cpu_regs[reg], val); - gen_op_mov_reg_v(size, reg, cpu_tmp0); + tcg_gen_addi_tl(s->tmp0, cpu_regs[reg], val); + gen_op_mov_reg_v(s, size, reg, s->tmp0); } -static inline void gen_op_add_reg_T0(TCGMemOp size, int reg) +static inline void gen_op_add_reg_T0(DisasContext *s, TCGMemOp size, int reg) { - tcg_gen_add_tl(cpu_tmp0, cpu_regs[reg], cpu_T0); - gen_op_mov_reg_v(size, reg, cpu_tmp0); + tcg_gen_add_tl(s->tmp0, cpu_regs[reg], s->T0); + gen_op_mov_reg_v(s, size, reg, s->tmp0); } static inline void gen_op_ld_v(DisasContext *s, int idx, TCGv t0, TCGv a0) @@ -427,16 +436,16 @@ 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_T0, cpu_A0); + gen_op_st_v(s, idx, s->T0, s->A0); } else { - gen_op_mov_reg_v(idx, d, cpu_T0); + gen_op_mov_reg_v(s, idx, d, s->T0); } } -static inline void gen_jmp_im(target_ulong pc) +static inline void gen_jmp_im(DisasContext *s, target_ulong pc) { - tcg_gen_movi_tl(cpu_tmp0, pc); - gen_op_jmp_v(cpu_tmp0); + tcg_gen_movi_tl(s->tmp0, pc); + gen_op_jmp_v(s->tmp0); } /* Compute SEG:REG into A0. SEG is selected from the override segment @@ -449,7 +458,7 @@ static void gen_lea_v_seg(DisasContext *s, TCGMemOp aflag, TCGv a0, #ifdef TARGET_X86_64 case MO_64: if (ovr_seg < 0) { - tcg_gen_mov_tl(cpu_A0, a0); + tcg_gen_mov_tl(s->A0, a0); return; } break; @@ -460,14 +469,14 @@ static void gen_lea_v_seg(DisasContext *s, TCGMemOp aflag, TCGv a0, ovr_seg = def_seg; } if (ovr_seg < 0) { - tcg_gen_ext32u_tl(cpu_A0, a0); + tcg_gen_ext32u_tl(s->A0, a0); return; } break; case MO_16: /* 16 bit address */ - tcg_gen_ext16u_tl(cpu_A0, a0); - a0 = cpu_A0; + tcg_gen_ext16u_tl(s->A0, a0); + a0 = s->A0; if (ovr_seg < 0) { if (s->addseg) { ovr_seg = def_seg; @@ -484,13 +493,13 @@ static void gen_lea_v_seg(DisasContext *s, TCGMemOp aflag, TCGv a0, TCGv seg = cpu_seg_base[ovr_seg]; if (aflag == MO_64) { - tcg_gen_add_tl(cpu_A0, a0, seg); + tcg_gen_add_tl(s->A0, a0, seg); } else if (CODE64(s)) { - tcg_gen_ext32u_tl(cpu_A0, a0); - tcg_gen_add_tl(cpu_A0, cpu_A0, seg); + tcg_gen_ext32u_tl(s->A0, a0); + tcg_gen_add_tl(s->A0, s->A0, seg); } else { - tcg_gen_add_tl(cpu_A0, a0, seg); - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + tcg_gen_add_tl(s->A0, a0, seg); + tcg_gen_ext32u_tl(s->A0, s->A0); } } } @@ -505,10 +514,10 @@ 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) +static inline void gen_op_movl_T0_Dshift(DisasContext *s, TCGMemOp ot) { - tcg_gen_ld32s_tl(cpu_T0, cpu_env, offsetof(CPUX86State, df)); - tcg_gen_shli_tl(cpu_T0, cpu_T0, ot); + tcg_gen_ld32s_tl(s->T0, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_shli_tl(s->T0, s->T0, ot); }; static TCGv gen_ext_tl(TCGv dst, TCGv src, TCGMemOp size, bool sign) @@ -552,18 +561,20 @@ static void gen_exts(TCGMemOp ot, TCGv reg) gen_ext_tl(reg, reg, ot, true); } -static inline void gen_op_jnz_ecx(TCGMemOp size, TCGLabel *label1) +static inline +void gen_op_jnz_ecx(DisasContext *s, TCGMemOp size, TCGLabel *label1) { - tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]); - gen_extu(size, cpu_tmp0); - tcg_gen_brcondi_tl(TCG_COND_NE, cpu_tmp0, 0, label1); + tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); + gen_extu(size, s->tmp0); + tcg_gen_brcondi_tl(TCG_COND_NE, s->tmp0, 0, label1); } -static inline void gen_op_jz_ecx(TCGMemOp size, TCGLabel *label1) +static inline +void gen_op_jz_ecx(DisasContext *s, TCGMemOp size, TCGLabel *label1) { - tcg_gen_mov_tl(cpu_tmp0, cpu_regs[R_ECX]); - gen_extu(size, cpu_tmp0); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1); + tcg_gen_mov_tl(s->tmp0, cpu_regs[R_ECX]); + gen_extu(size, s->tmp0); + tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); } static void gen_helper_in_func(TCGMemOp ot, TCGv v, TCGv_i32 n) @@ -606,28 +617,28 @@ static void gen_check_io(DisasContext *s, TCGMemOp ot, target_ulong cur_eip, target_ulong next_eip; if (s->pe && (s->cpl > s->iopl || s->vm86)) { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); switch (ot) { case MO_8: - gen_helper_check_iob(cpu_env, cpu_tmp2_i32); + gen_helper_check_iob(cpu_env, s->tmp2_i32); break; case MO_16: - gen_helper_check_iow(cpu_env, cpu_tmp2_i32); + gen_helper_check_iow(cpu_env, s->tmp2_i32); break; case MO_32: - gen_helper_check_iol(cpu_env, cpu_tmp2_i32); + gen_helper_check_iol(cpu_env, s->tmp2_i32); break; default: tcg_abort(); } } - if(s->flags & HF_SVMI_MASK) { + if(s->flags & HF_GUEST_MASK) { gen_update_cc_op(s); - gen_jmp_im(cur_eip); + gen_jmp_im(s, cur_eip); svm_flags |= (1 << (4 + ot)); next_eip = s->pc - s->cs_base; - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_svm_check_io(cpu_env, cpu_tmp2_i32, + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_svm_check_io(cpu_env, s->tmp2_i32, tcg_const_i32(svm_flags), tcg_const_i32(next_eip - cur_eip)); } @@ -636,42 +647,42 @@ 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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); gen_string_movl_A0_EDI(s); - 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); + gen_op_st_v(s, ot, s->T0, s->A0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_op_add_reg_T0(s, s->aflag, R_EDI); } -static void gen_op_update1_cc(void) +static void gen_op_update1_cc(DisasContext *s) { - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); } -static void gen_op_update2_cc(void) +static void gen_op_update2_cc(DisasContext *s) { - tcg_gen_mov_tl(cpu_cc_src, cpu_T1); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_mov_tl(cpu_cc_src, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); } -static void gen_op_update3_cc(TCGv reg) +static void gen_op_update3_cc(DisasContext *s, TCGv reg) { tcg_gen_mov_tl(cpu_cc_src2, reg); - tcg_gen_mov_tl(cpu_cc_src, cpu_T1); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_mov_tl(cpu_cc_src, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); } -static inline void gen_op_testl_T0_T1_cc(void) +static inline void gen_op_testl_T0_T1_cc(DisasContext *s) { - tcg_gen_and_tl(cpu_cc_dst, cpu_T0, cpu_T1); + tcg_gen_and_tl(cpu_cc_dst, s->T0, s->T1); } -static void gen_op_update_neg_cc(void) +static void gen_op_update_neg_cc(DisasContext *s) { - 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); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_neg_tl(cpu_cc_src, s->T0); + tcg_gen_movi_tl(s->cc_srcT, 0); } /* compute all eflags to cc_src */ @@ -739,17 +750,17 @@ static CCPrepare gen_prepare_eflags_c(DisasContext *s, TCGv reg) case CC_OP_SUBB ... CC_OP_SUBQ: /* (DATA_TYPE)CC_SRCT < (DATA_TYPE)CC_SRC */ size = s->cc_op - CC_OP_SUBB; - t1 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); + t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); /* If no temporary was used, be careful not to alias t1 and t0. */ - t0 = t1 == cpu_cc_src ? cpu_tmp0 : reg; - tcg_gen_mov_tl(t0, cpu_cc_srcT); + t0 = t1 == cpu_cc_src ? s->tmp0 : reg; + tcg_gen_mov_tl(t0, s->cc_srcT); gen_extu(size, t0); goto add_sub; case CC_OP_ADDB ... CC_OP_ADDQ: /* (DATA_TYPE)CC_DST < (DATA_TYPE)CC_SRC */ size = s->cc_op - CC_OP_ADDB; - t1 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); + t1 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); t0 = gen_ext_tl(reg, cpu_cc_dst, size, false); add_sub: return (CCPrepare) { .cond = TCG_COND_LTU, .reg = t0, @@ -899,10 +910,10 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) size = s->cc_op - CC_OP_SUBB; switch (jcc_op) { case JCC_BE: - tcg_gen_mov_tl(cpu_tmp4, cpu_cc_srcT); - gen_extu(size, cpu_tmp4); - t0 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, false); - cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = cpu_tmp4, + tcg_gen_mov_tl(s->tmp4, s->cc_srcT); + gen_extu(size, s->tmp4); + t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, false); + cc = (CCPrepare) { .cond = TCG_COND_LEU, .reg = s->tmp4, .reg2 = t0, .mask = -1, .use_reg2 = true }; break; @@ -912,10 +923,10 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) case JCC_LE: cond = TCG_COND_LE; fast_jcc_l: - tcg_gen_mov_tl(cpu_tmp4, cpu_cc_srcT); - gen_exts(size, cpu_tmp4); - t0 = gen_ext_tl(cpu_tmp0, cpu_cc_src, size, true); - cc = (CCPrepare) { .cond = cond, .reg = cpu_tmp4, + tcg_gen_mov_tl(s->tmp4, s->cc_srcT); + gen_exts(size, s->tmp4); + t0 = gen_ext_tl(s->tmp0, cpu_cc_src, size, true); + cc = (CCPrepare) { .cond = cond, .reg = s->tmp4, .reg2 = t0, .mask = -1, .use_reg2 = true }; break; @@ -951,7 +962,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) case JCC_L: gen_compute_eflags(s); if (reg == cpu_cc_src) { - reg = cpu_tmp0; + reg = s->tmp0; } tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ tcg_gen_xor_tl(reg, reg, cpu_cc_src); @@ -962,7 +973,7 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) case JCC_LE: gen_compute_eflags(s); if (reg == cpu_cc_src) { - reg = cpu_tmp0; + reg = s->tmp0; } tcg_gen_shri_tl(reg, cpu_cc_src, 4); /* CC_O -> CC_S */ tcg_gen_xor_tl(reg, reg, cpu_cc_src); @@ -1018,11 +1029,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_T0); + CCPrepare cc = gen_prepare_cc(s, b, s->T0); if (cc.mask != -1) { - tcg_gen_andi_tl(cpu_T0, cc.reg, cc.mask); - cc.reg = cpu_T0; + tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); + cc.reg = s->T0; } if (cc.use_reg2) { tcg_gen_brcond_tl(cc.cond, cc.reg, cc.reg2, l1); @@ -1036,12 +1047,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_T0); + CCPrepare cc = gen_prepare_cc(s, b, s->T0); gen_update_cc_op(s); if (cc.mask != -1) { - tcg_gen_andi_tl(cpu_T0, cc.reg, cc.mask); - cc.reg = cpu_T0; + tcg_gen_andi_tl(s->T0, cc.reg, cc.mask); + cc.reg = s->T0; } set_cc_op(s, CC_OP_DYNAMIC); if (cc.use_reg2) { @@ -1057,7 +1068,7 @@ static TCGLabel *gen_jz_ecx_string(DisasContext *s, target_ulong next_eip) { TCGLabel *l1 = gen_new_label(); TCGLabel *l2 = gen_new_label(); - gen_op_jnz_ecx(s->aflag, l1); + gen_op_jnz_ecx(s, s->aflag, l1); gen_set_label(l2); gen_jmp_tb(s, next_eip, 1); gen_set_label(l1); @@ -1066,40 +1077,40 @@ 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_T0, R_EAX); + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); gen_string_movl_A0_EDI(s); - 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_op_st_v(s, ot, s->T0, s->A0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_EDI); } static inline void gen_lods(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_ESI(s); - 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); + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_op_mov_reg_v(s, ot, R_EAX, s->T0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); } static inline void gen_scas(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_EDI(s); - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_ld_v(s, ot, s->T1, s->A0); gen_op(s, OP_CMPL, ot, R_EAX); - gen_op_movl_T0_Dshift(ot); - gen_op_add_reg_T0(s->aflag, R_EDI); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_EDI); } static inline void gen_cmps(DisasContext *s, TCGMemOp ot) { gen_string_movl_A0_EDI(s); - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_ld_v(s, ot, s->T1, s->A0); gen_string_movl_A0_ESI(s); gen_op(s, OP_CMPL, ot, OR_TMP0); - gen_op_movl_T0_Dshift(ot); - gen_op_add_reg_T0(s->aflag, R_ESI); - gen_op_add_reg_T0(s->aflag, R_EDI); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_op_add_reg_T0(s, s->aflag, R_EDI); } static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) @@ -1123,15 +1134,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_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_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); + tcg_gen_movi_tl(s->T0, 0); + gen_op_st_v(s, ot, s->T0, s->A0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); + tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); + gen_helper_in_func(ot, s->T0, s->tmp2_i32); + gen_op_st_v(s, ot, s->T0, s->A0); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_EDI); + gen_bpt_io(s, s->tmp2_i32, ot); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } @@ -1143,15 +1154,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->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_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); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); + tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T0); + gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + gen_op_movl_T0_Dshift(s, ot); + gen_op_add_reg_T0(s, s->aflag, R_ESI); + gen_bpt_io(s, s->tmp2_i32, ot); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } @@ -1167,11 +1178,11 @@ static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot, \ gen_update_cc_op(s); \ l2 = gen_jz_ecx_string(s, next_eip); \ gen_ ## op(s, ot); \ - gen_op_add_reg_im(s->aflag, R_ECX, -1); \ + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ /* a loop would cause two single step exceptions if ECX = 1 \ before rep string_insn */ \ if (s->repz_opt) \ - gen_op_jz_ecx(s->aflag, l2); \ + gen_op_jz_ecx(s, s->aflag, l2); \ gen_jmp(s, cur_eip); \ } @@ -1185,11 +1196,11 @@ static inline void gen_repz_ ## op(DisasContext *s, TCGMemOp ot, \ gen_update_cc_op(s); \ l2 = gen_jz_ecx_string(s, next_eip); \ gen_ ## op(s, ot); \ - gen_op_add_reg_im(s->aflag, R_ECX, -1); \ + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); \ gen_update_cc_op(s); \ gen_jcc1(s, (JCC_Z << 1) | (nz ^ 1), l2); \ if (s->repz_opt) \ - gen_op_jz_ecx(s->aflag, l2); \ + gen_op_jz_ecx(s, s->aflag, l2); \ gen_jmp(s, cur_eip); \ } @@ -1257,107 +1268,127 @@ static void gen_helper_fp_arith_STN_ST0(int op, int opreg) } } +static void gen_exception(DisasContext *s, int trapno, target_ulong cur_eip) +{ + gen_update_cc_op(s); + gen_jmp_im(s, cur_eip); + gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); + s->base.is_jmp = DISAS_NORETURN; +} + +/* 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); +} + /* if d == OR_TMP0, it means memory operand (address in A0) */ static void gen_op(DisasContext *s1, int op, TCGMemOp ot, int d) { if (d != OR_TMP0) { - gen_op_mov_v_reg(ot, cpu_T0, d); + if (s1->prefix & PREFIX_LOCK) { + /* Lock prefix when destination is not memory. */ + gen_illegal_opcode(s1); + return; + } + gen_op_mov_v_reg(s1, ot, s1->T0, d); } else if (!(s1->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s1, ot, s1->T0, s1->A0); } switch(op) { case OP_ADCL: - gen_compute_eflags_c(s1, cpu_tmp4); + gen_compute_eflags_c(s1, s1->tmp4); if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(cpu_T0, cpu_tmp4, cpu_T1); - tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + tcg_gen_add_tl(s1->T0, s1->tmp4, s1->T1); + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, s1->mem_index, ot | MO_LE); } else { - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_tmp4); + tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); + tcg_gen_add_tl(s1->T0, s1->T0, s1->tmp4); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update3_cc(cpu_tmp4); + gen_op_update3_cc(s1, s1->tmp4); set_cc_op(s1, CC_OP_ADCB + ot); break; case OP_SBBL: - gen_compute_eflags_c(s1, cpu_tmp4); + gen_compute_eflags_c(s1, s1->tmp4); if (s1->prefix & PREFIX_LOCK) { - tcg_gen_add_tl(cpu_T0, cpu_T1, cpu_tmp4); - tcg_gen_neg_tl(cpu_T0, cpu_T0); - tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + tcg_gen_add_tl(s1->T0, s1->T1, s1->tmp4); + tcg_gen_neg_tl(s1->T0, s1->T0); + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, s1->mem_index, ot | MO_LE); } else { - tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); - tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_tmp4); + tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); + tcg_gen_sub_tl(s1->T0, s1->T0, s1->tmp4); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update3_cc(cpu_tmp4); + gen_op_update3_cc(s1, s1->tmp4); set_cc_op(s1, CC_OP_SBBB + ot); break; case OP_ADDL: if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T1, s1->mem_index, ot | MO_LE); } else { - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_add_tl(s1->T0, s1->T0, s1->T1); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update2_cc(); + gen_op_update2_cc(s1); set_cc_op(s1, CC_OP_ADDB + ot); break; case OP_SUBL: if (s1->prefix & PREFIX_LOCK) { - tcg_gen_neg_tl(cpu_T0, cpu_T1); - tcg_gen_atomic_fetch_add_tl(cpu_cc_srcT, cpu_A0, cpu_T0, + tcg_gen_neg_tl(s1->T0, s1->T1); + tcg_gen_atomic_fetch_add_tl(s1->cc_srcT, s1->A0, s1->T0, s1->mem_index, ot | MO_LE); - tcg_gen_sub_tl(cpu_T0, cpu_cc_srcT, cpu_T1); + tcg_gen_sub_tl(s1->T0, s1->cc_srcT, s1->T1); } else { - tcg_gen_mov_tl(cpu_cc_srcT, cpu_T0); - tcg_gen_sub_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_mov_tl(s1->cc_srcT, s1->T0); + tcg_gen_sub_tl(s1->T0, s1->T0, s1->T1); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update2_cc(); + gen_op_update2_cc(s1); set_cc_op(s1, CC_OP_SUBB + ot); break; default: case OP_ANDL: if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_and_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + tcg_gen_atomic_and_fetch_tl(s1->T0, s1->A0, s1->T1, s1->mem_index, ot | MO_LE); } else { - tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_and_tl(s1->T0, s1->T0, s1->T1); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update1_cc(); + gen_op_update1_cc(s1); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_ORL: if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_or_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + tcg_gen_atomic_or_fetch_tl(s1->T0, s1->A0, s1->T1, s1->mem_index, ot | MO_LE); } else { - tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_or_tl(s1->T0, s1->T0, s1->T1); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update1_cc(); + gen_op_update1_cc(s1); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_XORL: if (s1->prefix & PREFIX_LOCK) { - tcg_gen_atomic_xor_fetch_tl(cpu_T0, cpu_A0, cpu_T1, + tcg_gen_atomic_xor_fetch_tl(s1->T0, s1->A0, s1->T1, s1->mem_index, ot | MO_LE); } else { - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_xor_tl(s1->T0, s1->T0, s1->T1); gen_op_st_rm_T0_A0(s1, ot, d); } - gen_op_update1_cc(); + gen_op_update1_cc(s1); set_cc_op(s1, CC_OP_LOGICB + ot); break; case OP_CMPL: - 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); + tcg_gen_mov_tl(cpu_cc_src, s1->T1); + tcg_gen_mov_tl(s1->cc_srcT, s1->T0); + tcg_gen_sub_tl(cpu_cc_dst, s1->T0, s1->T1); set_cc_op(s1, CC_OP_SUBB + ot); break; } @@ -1367,21 +1398,26 @@ 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 (s1->prefix & PREFIX_LOCK) { - tcg_gen_movi_tl(cpu_T0, c > 0 ? 1 : -1); - tcg_gen_atomic_add_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + if (d != OR_TMP0) { + /* Lock prefix when destination is not memory */ + gen_illegal_opcode(s1); + return; + } + tcg_gen_movi_tl(s1->T0, c > 0 ? 1 : -1); + tcg_gen_atomic_add_fetch_tl(s1->T0, s1->A0, s1->T0, s1->mem_index, ot | MO_LE); } else { if (d != OR_TMP0) { - gen_op_mov_v_reg(ot, cpu_T0, d); + gen_op_mov_v_reg(s1, ot, s1->T0, d); } else { - gen_op_ld_v(s1, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s1, ot, s1->T0, s1->A0); } - tcg_gen_addi_tl(cpu_T0, cpu_T0, (c > 0 ? 1 : -1)); + tcg_gen_addi_tl(s1->T0, s1->T0, (c > 0 ? 1 : -1)); gen_op_st_rm_T0_A0(s1, ot, d); } gen_compute_eflags_c(s1, cpu_cc_src); - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, s1->T0); set_cc_op(s1, (c > 0 ? CC_OP_INCB : CC_OP_DECB) + ot); } @@ -1410,19 +1446,19 @@ static void gen_shift_flags(DisasContext *s, TCGMemOp ot, TCGv result, tcg_temp_free(z_tl); /* Get the two potential CC_OP values into temporaries. */ - tcg_gen_movi_i32(cpu_tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); + tcg_gen_movi_i32(s->tmp2_i32, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); if (s->cc_op == CC_OP_DYNAMIC) { oldop = cpu_cc_op; } else { - tcg_gen_movi_i32(cpu_tmp3_i32, s->cc_op); - oldop = cpu_tmp3_i32; + tcg_gen_movi_i32(s->tmp3_i32, s->cc_op); + oldop = s->tmp3_i32; } /* Conditionally store the CC_OP value. */ z32 = tcg_const_i32(0); s32 = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(s32, count); - tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, cpu_tmp2_i32, oldop); + tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, s32, z32, s->tmp2_i32, oldop); tcg_temp_free_i32(z32); tcg_temp_free_i32(s32); @@ -1437,33 +1473,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_v_reg(ot, cpu_T0, op1); + gen_op_mov_v_reg(s, ot, s->T0, op1); } - tcg_gen_andi_tl(cpu_T1, cpu_T1, mask); - tcg_gen_subi_tl(cpu_tmp0, cpu_T1, 1); + tcg_gen_andi_tl(s->T1, s->T1, mask); + tcg_gen_subi_tl(s->tmp0, s->T1, 1); if (is_right) { if (is_arith) { - gen_exts(ot, cpu_T0); - tcg_gen_sar_tl(cpu_tmp0, cpu_T0, cpu_tmp0); - tcg_gen_sar_tl(cpu_T0, cpu_T0, cpu_T1); + gen_exts(ot, s->T0); + tcg_gen_sar_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_sar_tl(s->T0, s->T0, s->T1); } else { - gen_extu(ot, cpu_T0); - tcg_gen_shr_tl(cpu_tmp0, cpu_T0, cpu_tmp0); - tcg_gen_shr_tl(cpu_T0, cpu_T0, cpu_T1); + gen_extu(ot, s->T0); + tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); } } else { - tcg_gen_shl_tl(cpu_tmp0, cpu_T0, cpu_tmp0); - tcg_gen_shl_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_tl(s->T0, s->T0, s->T1); } /* store */ gen_op_st_rm_T0_A0(s, ot, op1); - gen_shift_flags(s, ot, cpu_T0, cpu_tmp0, cpu_T1, is_right); + gen_shift_flags(s, ot, s->T0, s->tmp0, s->T1, is_right); } static void gen_shift_rm_im(DisasContext *s, TCGMemOp ot, int op1, int op2, @@ -1473,25 +1509,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); else - gen_op_mov_v_reg(ot, cpu_T0, op1); + gen_op_mov_v_reg(s, ot, s->T0, op1); op2 &= mask; if (op2 != 0) { if (is_right) { if (is_arith) { - gen_exts(ot, cpu_T0); - tcg_gen_sari_tl(cpu_tmp4, cpu_T0, op2 - 1); - tcg_gen_sari_tl(cpu_T0, cpu_T0, op2); + gen_exts(ot, s->T0); + tcg_gen_sari_tl(s->tmp4, s->T0, op2 - 1); + tcg_gen_sari_tl(s->T0, s->T0, op2); } else { - gen_extu(ot, cpu_T0); - tcg_gen_shri_tl(cpu_tmp4, cpu_T0, op2 - 1); - tcg_gen_shri_tl(cpu_T0, cpu_T0, op2); + gen_extu(ot, s->T0); + tcg_gen_shri_tl(s->tmp4, s->T0, op2 - 1); + tcg_gen_shri_tl(s->T0, s->T0, op2); } } else { - tcg_gen_shli_tl(cpu_tmp4, cpu_T0, op2 - 1); - tcg_gen_shli_tl(cpu_T0, cpu_T0, op2); + tcg_gen_shli_tl(s->tmp4, s->T0, op2 - 1); + tcg_gen_shli_tl(s->T0, s->T0, op2); } } @@ -1500,8 +1536,8 @@ 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_T0); + tcg_gen_mov_tl(cpu_cc_src, s->tmp4); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); set_cc_op(s, (is_right ? CC_OP_SARB : CC_OP_SHLB) + ot); } } @@ -1513,41 +1549,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_v_reg(ot, cpu_T0, op1); + gen_op_mov_v_reg(s, ot, s->T0, op1); } - tcg_gen_andi_tl(cpu_T1, cpu_T1, mask); + tcg_gen_andi_tl(s->T1, s->T1, mask); switch (ot) { case MO_8: /* Replicate the 8-bit input so that a 32-bit rotate works. */ - tcg_gen_ext8u_tl(cpu_T0, cpu_T0); - tcg_gen_muli_tl(cpu_T0, cpu_T0, 0x01010101); + tcg_gen_ext8u_tl(s->T0, s->T0); + tcg_gen_muli_tl(s->T0, s->T0, 0x01010101); goto do_long; case MO_16: /* Replicate the 16-bit input so that a 32-bit rotate works. */ - tcg_gen_deposit_tl(cpu_T0, cpu_T0, cpu_T0, 16, 16); + tcg_gen_deposit_tl(s->T0, s->T0, s->T0, 16, 16); goto do_long; do_long: #ifdef TARGET_X86_64 case MO_32: - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - tcg_gen_trunc_tl_i32(cpu_tmp3_i32, cpu_T1); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); if (is_right) { - tcg_gen_rotr_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_rotr_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); } else { - tcg_gen_rotl_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); + tcg_gen_rotl_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); } - tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); break; #endif default: if (is_right) { - tcg_gen_rotr_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_rotr_tl(s->T0, s->T0, s->T1); } else { - tcg_gen_rotl_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_rotl_tl(s->T0, s->T0, s->T1); } break; } @@ -1563,12 +1599,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_T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, cpu_T0, mask); + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); } else { - tcg_gen_shri_tl(cpu_cc_src2, cpu_T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_T0, 1); + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); } tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); @@ -1579,11 +1615,11 @@ 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_T1); - tcg_gen_movi_i32(cpu_tmp2_i32, CC_OP_ADCOX); - tcg_gen_movi_i32(cpu_tmp3_i32, CC_OP_EFLAGS); + tcg_gen_trunc_tl_i32(t1, s->T1); + tcg_gen_movi_i32(s->tmp2_i32, CC_OP_ADCOX); + tcg_gen_movi_i32(s->tmp3_i32, CC_OP_EFLAGS); tcg_gen_movcond_i32(TCG_COND_NE, cpu_cc_op, t1, t0, - cpu_tmp2_i32, cpu_tmp3_i32); + s->tmp2_i32, s->tmp3_i32); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -1599,9 +1635,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_v_reg(ot, cpu_T0, op1); + gen_op_mov_v_reg(s, ot, s->T0, op1); } op2 &= mask; @@ -1609,20 +1645,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_T0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); if (is_right) { - tcg_gen_rotri_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); + tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, op2); } else { - tcg_gen_rotli_i32(cpu_tmp2_i32, cpu_tmp2_i32, op2); + tcg_gen_rotli_i32(s->tmp2_i32, s->tmp2_i32, op2); } - tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); break; #endif default: if (is_right) { - tcg_gen_rotri_tl(cpu_T0, cpu_T0, op2); + tcg_gen_rotri_tl(s->T0, s->T0, op2); } else { - tcg_gen_rotli_tl(cpu_T0, cpu_T0, op2); + tcg_gen_rotli_tl(s->T0, s->T0, op2); } break; case MO_8: @@ -1635,10 +1671,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_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); + gen_extu(ot, s->T0); + tcg_gen_shli_tl(s->tmp0, s->T0, shift); + tcg_gen_shri_tl(s->T0, s->T0, mask + 1 - shift); + tcg_gen_or_tl(s->T0, s->T0, s->tmp0); break; } } @@ -1655,12 +1691,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_T0, mask - 1); - tcg_gen_shri_tl(cpu_cc_dst, cpu_T0, mask); + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask - 1); + tcg_gen_shri_tl(cpu_cc_dst, s->T0, mask); tcg_gen_andi_tl(cpu_cc_dst, cpu_cc_dst, 1); } else { - tcg_gen_shri_tl(cpu_cc_src2, cpu_T0, mask); - tcg_gen_andi_tl(cpu_cc_dst, cpu_T0, 1); + tcg_gen_shri_tl(cpu_cc_src2, s->T0, mask); + tcg_gen_andi_tl(cpu_cc_dst, s->T0, 1); } tcg_gen_andi_tl(cpu_cc_src2, cpu_cc_src2, 1); tcg_gen_xor_tl(cpu_cc_src2, cpu_cc_src2, cpu_cc_dst); @@ -1677,24 +1713,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); else - gen_op_mov_v_reg(ot, cpu_T0, op1); + gen_op_mov_v_reg(s, ot, s->T0, op1); if (is_right) { switch (ot) { case MO_8: - gen_helper_rcrb(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rcrb(s->T0, cpu_env, s->T0, s->T1); break; case MO_16: - gen_helper_rcrw(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rcrw(s->T0, cpu_env, s->T0, s->T1); break; case MO_32: - gen_helper_rcrl(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rcrl(s->T0, cpu_env, s->T0, s->T1); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_rcrq(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rcrq(s->T0, cpu_env, s->T0, s->T1); break; #endif default: @@ -1703,17 +1739,17 @@ static void gen_rotc_rm_T1(DisasContext *s, TCGMemOp ot, int op1, } else { switch (ot) { case MO_8: - gen_helper_rclb(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rclb(s->T0, cpu_env, s->T0, s->T1); break; case MO_16: - gen_helper_rclw(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rclw(s->T0, cpu_env, s->T0, s->T1); break; case MO_32: - gen_helper_rcll(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rcll(s->T0, cpu_env, s->T0, s->T1); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_rclq(cpu_T0, cpu_env, cpu_T0, cpu_T1); + gen_helper_rclq(s->T0, cpu_env, s->T0, s->T1); break; #endif default: @@ -1733,9 +1769,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_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_v_reg(ot, cpu_T0, op1); + gen_op_mov_v_reg(s, ot, s->T0, op1); } count = tcg_temp_new(); @@ -1747,69 +1783,69 @@ 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_T0, cpu_T1, 16, 16); - tcg_gen_mov_tl(cpu_T1, cpu_T0); - tcg_gen_mov_tl(cpu_T0, cpu_tmp0); + tcg_gen_deposit_tl(s->tmp0, s->T0, s->T1, 16, 16); + tcg_gen_mov_tl(s->T1, s->T0); + tcg_gen_mov_tl(s->T0, s->tmp0); } else { - tcg_gen_deposit_tl(cpu_T1, cpu_T0, cpu_T1, 16, 16); + tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 16, 16); } /* FALLTHRU */ #ifdef TARGET_X86_64 case MO_32: /* Concatenate the two 32-bit values and use a 64-bit shift. */ - tcg_gen_subi_tl(cpu_tmp0, count, 1); + tcg_gen_subi_tl(s->tmp0, count, 1); if (is_right) { - 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); + tcg_gen_concat_tl_i64(s->T0, s->T0, s->T1); + tcg_gen_shr_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_i64(s->T0, s->T0, count); } else { - 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_T0, cpu_T0, 32); + tcg_gen_concat_tl_i64(s->T0, s->T1, s->T0); + tcg_gen_shl_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_i64(s->T0, s->T0, count); + tcg_gen_shri_i64(s->tmp0, s->tmp0, 32); + tcg_gen_shri_i64(s->T0, s->T0, 32); } break; #endif default: - tcg_gen_subi_tl(cpu_tmp0, count, 1); + tcg_gen_subi_tl(s->tmp0, count, 1); if (is_right) { - tcg_gen_shr_tl(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); - tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); - tcg_gen_shr_tl(cpu_T0, cpu_T0, count); - tcg_gen_shl_tl(cpu_T1, cpu_T1, cpu_tmp4); + tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + tcg_gen_shr_tl(s->T0, s->T0, count); + tcg_gen_shl_tl(s->T1, s->T1, s->tmp4); } else { - tcg_gen_shl_tl(cpu_tmp0, cpu_T0, cpu_tmp0); + tcg_gen_shl_tl(s->tmp0, s->T0, s->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_T1, cpu_tmp4); - tcg_gen_or_tl(cpu_tmp0, cpu_tmp0, cpu_tmp4); + tcg_gen_subfi_tl(s->tmp4, 33, count); + tcg_gen_shr_tl(s->tmp4, s->T1, s->tmp4); + tcg_gen_or_tl(s->tmp0, s->tmp0, s->tmp4); } - tcg_gen_subfi_tl(cpu_tmp4, mask + 1, count); - tcg_gen_shl_tl(cpu_T0, cpu_T0, count); - tcg_gen_shr_tl(cpu_T1, cpu_T1, cpu_tmp4); + tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + tcg_gen_shl_tl(s->T0, s->T0, count); + tcg_gen_shr_tl(s->T1, s->T1, s->tmp4); } - tcg_gen_movi_tl(cpu_tmp4, 0); - 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); + tcg_gen_movi_tl(s->tmp4, 0); + tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, count, s->tmp4, + s->tmp4, s->T1); + tcg_gen_or_tl(s->T0, s->T0, s->T1); break; } /* store */ gen_op_st_rm_T0_A0(s, ot, op1); - gen_shift_flags(s, ot, cpu_T0, cpu_tmp0, count, is_right); + gen_shift_flags(s, ot, s->T0, s->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_T1, s); + gen_op_mov_v_reg(s1, ot, s1->T1, s); switch(op) { case OP_ROL: gen_rot_rm_T1(s1, ot, d, 0); @@ -1857,7 +1893,7 @@ static void gen_shifti(DisasContext *s1, int op, TCGMemOp ot, int d, int c) break; default: /* currently not optimized */ - tcg_gen_movi_tl(cpu_T1, c); + tcg_gen_movi_tl(s1->T1, c); gen_shift(s1, op, ot, d, OR_TMP1); break; } @@ -2048,7 +2084,7 @@ static AddressParts gen_lea_modrm_0(CPUX86State *env, DisasContext *s, } /* Compute the address, with a minimum number of TCG ops. */ -static TCGv gen_lea_modrm_1(AddressParts a) +static TCGv gen_lea_modrm_1(DisasContext *s, AddressParts a) { TCGv ea = NULL; @@ -2056,22 +2092,22 @@ static TCGv gen_lea_modrm_1(AddressParts a) 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; + tcg_gen_shli_tl(s->A0, cpu_regs[a.index], a.scale); + ea = s->A0; } if (a.base >= 0) { - tcg_gen_add_tl(cpu_A0, ea, cpu_regs[a.base]); - ea = cpu_A0; + tcg_gen_add_tl(s->A0, ea, cpu_regs[a.base]); + ea = s->A0; } } else if (a.base >= 0) { ea = cpu_regs[a.base]; } if (!ea) { - tcg_gen_movi_tl(cpu_A0, a.disp); - ea = cpu_A0; + tcg_gen_movi_tl(s->A0, a.disp); + ea = s->A0; } else if (a.disp != 0) { - tcg_gen_addi_tl(cpu_A0, ea, a.disp); - ea = cpu_A0; + tcg_gen_addi_tl(s->A0, ea, a.disp); + ea = s->A0; } return ea; @@ -2080,7 +2116,7 @@ static TCGv gen_lea_modrm_1(AddressParts a) 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); + TCGv ea = gen_lea_modrm_1(s, a); gen_lea_v_seg(s, s->aflag, ea, a.def_seg, s->override); } @@ -2093,21 +2129,21 @@ static void gen_nop_modrm(CPUX86State *env, DisasContext *s, int modrm) 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)); + TCGv ea = gen_lea_modrm_1(s, gen_lea_modrm_0(env, s, modrm)); - tcg_gen_extu_tl_i64(cpu_tmp1_i64, ea); + tcg_gen_extu_tl_i64(s->tmp1_i64, ea); if (!CODE64(s)) { - tcg_gen_ext32u_i64(cpu_tmp1_i64, cpu_tmp1_i64); + tcg_gen_ext32u_i64(s->tmp1_i64, s->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); + tcg_gen_setcond_i64(cond, s->tmp1_i64, s->tmp1_i64, bndv); + tcg_gen_extrl_i64_i32(s->tmp2_i32, s->tmp1_i64); + gen_helper_bndck(cpu_env, s->tmp2_i32); } /* used for LEA and MOV AX, mem */ static void gen_add_A0_ds_seg(DisasContext *s) { - gen_lea_v_seg(s, s->aflag, cpu_A0, R_DS, s->override); + gen_lea_v_seg(s, s->aflag, s->A0, R_DS, s->override); } /* generate modrm memory load or store of 'reg'. TMP0 is used if reg == @@ -2122,23 +2158,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_T0, reg); - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_op_mov_reg_v(s, ot, rm, s->T0); } else { - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); if (reg != OR_TMP0) - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); } } else { gen_lea_modrm(env, s, modrm); if (is_store) { if (reg != OR_TMP0) - gen_op_mov_v_reg(ot, cpu_T0, reg); - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_op_st_v(s, ot, s->T0, s->A0); } else { - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); if (reg != OR_TMP0) - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); } } } @@ -2192,13 +2228,13 @@ static inline void gen_goto_tb(DisasContext *s, int tb_num, target_ulong eip) if (use_goto_tb(s, pc)) { /* jump to same page: we can use a direct jump */ tcg_gen_goto_tb(tb_num); - gen_jmp_im(eip); + gen_jmp_im(s, eip); tcg_gen_exit_tb(s->base.tb, tb_num); s->base.is_jmp = DISAS_NORETURN; } else { /* jump to another page */ - gen_jmp_im(eip); - gen_jr(s, cpu_tmp0); + gen_jmp_im(s, eip); + gen_jr(s, s->tmp0); } } @@ -2220,11 +2256,11 @@ static inline void gen_jcc(DisasContext *s, int b, l2 = gen_new_label(); gen_jcc1(s, b, l1); - gen_jmp_im(next_eip); + gen_jmp_im(s, next_eip); tcg_gen_br(l2); gen_set_label(l1); - gen_jmp_im(val); + gen_jmp_im(s, val); gen_set_label(l2); gen_eob(s); } @@ -2237,7 +2273,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_T1); + cc = gen_prepare_cc(s, b, s->T1); if (cc.mask != -1) { TCGv t0 = tcg_temp_new(); tcg_gen_andi_tl(t0, cc.reg, cc.mask); @@ -2247,9 +2283,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_T0, cc.reg, cc.reg2, - cpu_T0, cpu_regs[reg]); - gen_op_mov_reg_v(ot, reg, cpu_T0); + tcg_gen_movcond_tl(cc.cond, s->T0, cc.reg, cc.reg2, + s->T0, cpu_regs[reg]); + gen_op_mov_reg_v(s, ot, reg, s->T0); if (cc.mask != -1) { tcg_temp_free(cc.reg); @@ -2259,18 +2295,18 @@ static void gen_cmovcc1(CPUX86State *env, DisasContext *s, TCGMemOp ot, int b, } } -static inline void gen_op_movl_T0_seg(int seg_reg) +static inline void gen_op_movl_T0_seg(DisasContext *s, int seg_reg) { - tcg_gen_ld32u_tl(cpu_T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State,segs[seg_reg].selector)); } -static inline void gen_op_movl_seg_T0_vm(int seg_reg) +static inline void gen_op_movl_seg_T0_vm(DisasContext *s, int seg_reg) { - tcg_gen_ext16u_tl(cpu_T0, cpu_T0); - tcg_gen_st32_tl(cpu_T0, cpu_env, + tcg_gen_ext16u_tl(s->T0, s->T0); + tcg_gen_st32_tl(s->T0, cpu_env, offsetof(CPUX86State,segs[seg_reg].selector)); - tcg_gen_shli_tl(cpu_seg_base[seg_reg], cpu_T0, 4); + tcg_gen_shli_tl(cpu_seg_base[seg_reg], s->T0, 4); } /* move T0 to seg_reg and compute if the CPU state may change. Never @@ -2278,8 +2314,8 @@ static inline void gen_op_movl_seg_T0_vm(int seg_reg) static void gen_movl_seg_T0(DisasContext *s, int seg_reg) { if (s->pe && !s->vm86) { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_load_seg(cpu_env, tcg_const_i32(seg_reg), s->tmp2_i32); /* abort translation because the addseg value may change or because ss32 may change. For R_SS, translation must always stop as a special handling must be done to disable hardware @@ -2288,7 +2324,7 @@ static void gen_movl_seg_T0(DisasContext *s, int seg_reg) s->base.is_jmp = DISAS_TOO_MANY; } } else { - gen_op_movl_seg_T0_vm(seg_reg); + gen_op_movl_seg_T0_vm(s, seg_reg); if (seg_reg == R_SS) { s->base.is_jmp = DISAS_TOO_MANY; } @@ -2305,10 +2341,10 @@ gen_svm_check_intercept_param(DisasContext *s, target_ulong pc_start, uint32_t type, uint64_t param) { /* no SVM activated; fast case */ - if (likely(!(s->flags & HF_SVMI_MASK))) + if (likely(!(s->flags & HF_GUEST_MASK))) return; gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_svm_check_intercept_param(cpu_env, tcg_const_i32(type), tcg_const_i64(param)); } @@ -2321,7 +2357,7 @@ gen_svm_check_intercept(DisasContext *s, target_ulong pc_start, uint64_t type) static inline void gen_stack_update(DisasContext *s, int addend) { - gen_op_add_reg_im(mo_stacksize(s), R_ESP, addend); + gen_op_add_reg_im(s, mo_stacksize(s), R_ESP, addend); } /* Generate a push. It depends on ss32, addseg and dflag. */ @@ -2330,20 +2366,20 @@ static void gen_push_v(DisasContext *s, TCGv val) TCGMemOp d_ot = mo_pushpop(s, s->dflag); TCGMemOp a_ot = mo_stacksize(s); int size = 1 << d_ot; - TCGv new_esp = cpu_A0; + TCGv new_esp = s->A0; - tcg_gen_subi_tl(cpu_A0, cpu_regs[R_ESP], size); + tcg_gen_subi_tl(s->A0, cpu_regs[R_ESP], size); if (!CODE64(s)) { if (s->addseg) { - new_esp = cpu_tmp4; - tcg_gen_mov_tl(new_esp, cpu_A0); + new_esp = s->tmp4; + tcg_gen_mov_tl(new_esp, s->A0); } - gen_lea_v_seg(s, a_ot, cpu_A0, R_SS, -1); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); } - gen_op_st_v(s, d_ot, val, cpu_A0); - gen_op_mov_reg_v(a_ot, R_ESP, new_esp); + gen_op_st_v(s, d_ot, val, s->A0); + gen_op_mov_reg_v(s, a_ot, R_ESP, new_esp); } /* two step pop is necessary for precise exceptions */ @@ -2352,7 +2388,7 @@ static TCGMemOp gen_pop_T0(DisasContext *s) TCGMemOp d_ot = mo_pushpop(s, s->dflag); 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, s->T0, s->A0); return d_ot; } @@ -2375,9 +2411,9 @@ static void gen_pusha(DisasContext *s) int i; 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); + tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], (i - 8) * size); + gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_regs[7 - i], s->A0); } gen_stack_update(s, -8 * size); @@ -2395,10 +2431,10 @@ static void gen_popa(DisasContext *s) if (7 - i == R_ESP) { continue; } - 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); + tcg_gen_addi_tl(s->A0, cpu_regs[R_ESP], i * size); + gen_lea_v_seg(s, s_ot, s->A0, R_SS, -1); + gen_op_ld_v(s, d_ot, s->T0, s->A0); + gen_op_mov_reg_v(s, d_ot, 7 - i, s->T0); } gen_stack_update(s, 8 * size); @@ -2411,9 +2447,9 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) int size = 1 << d_ot; /* 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); + tcg_gen_subi_tl(s->T1, cpu_regs[R_ESP], size); + gen_lea_v_seg(s, a_ot, s->T1, R_SS, -1); + gen_op_st_v(s, d_ot, cpu_regs[R_EBP], s->A0); level &= 31; if (level != 0) { @@ -2421,27 +2457,27 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) /* 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(s->A0, cpu_regs[R_EBP], size * i); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_op_ld_v(s, d_ot, s->tmp0, s->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); + tcg_gen_subi_tl(s->A0, s->T1, size * i); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_op_st_v(s, d_ot, s->tmp0, s->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); + tcg_gen_subi_tl(s->A0, s->T1, size * level); + gen_lea_v_seg(s, a_ot, s->A0, R_SS, -1); + gen_op_st_v(s, d_ot, s->T1, s->A0); } /* Copy the FrameTemp value to EBP. */ - gen_op_mov_reg_v(a_ot, R_EBP, cpu_T1); + gen_op_mov_reg_v(s, a_ot, R_EBP, s->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); + tcg_gen_subi_tl(s->T1, s->T1, esp_addend + size * level); + gen_op_mov_reg_v(s, a_ot, R_ESP, s->T1); } static void gen_leave(DisasContext *s) @@ -2450,27 +2486,12 @@ static void gen_leave(DisasContext *s) 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); + gen_op_ld_v(s, d_ot, s->T0, s->A0); - tcg_gen_addi_tl(cpu_T1, cpu_regs[R_EBP], 1 << d_ot); + tcg_gen_addi_tl(s->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) -{ - gen_update_cc_op(s); - gen_jmp_im(cur_eip); - gen_helper_raise_exception(cpu_env, tcg_const_i32(trapno)); - s->base.is_jmp = DISAS_NORETURN; -} - -/* 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); + gen_op_mov_reg_v(s, d_ot, R_EBP, s->T0); + gen_op_mov_reg_v(s, a_ot, R_ESP, s->T1); } /* Similarly, except that the assumption here is that we don't decode @@ -2498,7 +2519,7 @@ static void gen_interrupt(DisasContext *s, int intno, target_ulong cur_eip, target_ulong next_eip) { gen_update_cc_op(s); - gen_jmp_im(cur_eip); + gen_jmp_im(s, cur_eip); gen_helper_raise_interrupt(cpu_env, tcg_const_i32(intno), tcg_const_i32(next_eip - cur_eip)); s->base.is_jmp = DISAS_NORETURN; @@ -2507,7 +2528,7 @@ static void gen_interrupt(DisasContext *s, int intno, static void gen_debug(DisasContext *s, target_ulong cur_eip) { gen_update_cc_op(s); - gen_jmp_im(cur_eip); + gen_jmp_im(s, cur_eip); gen_helper_debug(cpu_env); s->base.is_jmp = DISAS_NORETURN; } @@ -2617,7 +2638,7 @@ static void gen_jmp_tb(DisasContext *s, target_ulong eip, int tb_num) if (s->jmp_opt) { gen_goto_tb(s, tb_num, eip); } else { - gen_jmp_im(eip); + gen_jmp_im(s, eip); gen_eob(s); } } @@ -2629,60 +2650,60 @@ static void gen_jmp(DisasContext *s, target_ulong eip) static inline void gen_ldq_env_A0(DisasContext *s, int offset) { - tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, offset); + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset); } static inline void gen_stq_env_A0(DisasContext *s, int offset) { - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, offset); - tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); } 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(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(ZMMReg, ZMM_Q(1))); + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, mem_index, MO_LEQ); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_addi_tl(s->tmp0, s->A0, 8); + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEQ); + tcg_gen_st_i64(s->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(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(ZMMReg, ZMM_Q(1))); - tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_tmp0, mem_index, MO_LEQ); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, mem_index, MO_LEQ); + tcg_gen_addi_tl(s->tmp0, s->A0, 8); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offset + offsetof(ZMMReg, ZMM_Q(1))); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->tmp0, mem_index, MO_LEQ); } -static inline void gen_op_movo(int d_offset, int s_offset) +static inline void gen_op_movo(DisasContext *s, int d_offset, int s_offset) { - 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))); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(0))); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset + offsetof(ZMMReg, ZMM_Q(1))); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset + offsetof(ZMMReg, ZMM_Q(1))); } -static inline void gen_op_movq(int d_offset, int s_offset) +static inline void gen_op_movq(DisasContext *s, int d_offset, int s_offset) { - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, s_offset); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset); + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, s_offset); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); } -static inline void gen_op_movl(int d_offset, int s_offset) +static inline void gen_op_movl(DisasContext *s, int d_offset, int s_offset) { - tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, s_offset); - tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, d_offset); + tcg_gen_ld_i32(s->tmp2_i32, cpu_env, s_offset); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, d_offset); } -static inline void gen_op_movq_env_0(int d_offset) +static inline void gen_op_movq_env_0(DisasContext *s, int d_offset) { - tcg_gen_movi_i64(cpu_tmp1_i64, 0); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, d_offset); + tcg_gen_movi_i64(s->tmp1_i64, 0); + tcg_gen_st_i64(s->tmp1_i64, cpu_env, d_offset); } typedef void (*SSEFunc_i_ep)(TCGv_i32 val, TCGv_ptr env, TCGv_ptr reg); @@ -3122,41 +3143,42 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_stq_env_A0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0))); } else { - tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); - gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); + gen_op_st_v(s, MO_32, s->T0, s->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_T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); + tcg_gen_st_tl(s->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, + tcg_gen_addi_ptr(s->ptr0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_movl_mm_T0_mmx(cpu_ptr0, cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_movl_mm_T0_mmx(s->ptr0, s->tmp2_i32); } break; case 0x16e: /* movd xmm, ea */ #ifdef TARGET_X86_64 if (s->dflag == MO_64) { gen_ldst_modrm(env, s, modrm, MO_64, OR_TMP0, 0); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(s->ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); - gen_helper_movq_mm_T0_xmm(cpu_ptr0, cpu_T0); + gen_helper_movq_mm_T0_xmm(s->ptr0, s->T0); } else #endif { gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 0); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(s->ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_movl_mm_T0_xmm(cpu_ptr0, cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_movl_mm_T0_xmm(s->ptr0, s->tmp2_i32); } break; case 0x6f: /* movq mm, ea */ @@ -3165,9 +3187,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); } else { rm = (modrm & 7); - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offsetof(CPUX86State,fpregs[rm].mmx)); - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx)); } break; @@ -3182,22 +3204,26 @@ 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_movo(offsetof(CPUX86State,xmm_regs[reg]), + gen_op_movo(s, offsetof(CPUX86State, xmm_regs[reg]), offsetof(CPUX86State,xmm_regs[rm])); } break; case 0x210: /* movss xmm, ea */ if (mod != 3) { gen_lea_modrm(env, s, modrm); - 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))); + gen_op_ld_v(s, MO_32, s->T0, s->A0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(1))); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); + tcg_gen_st32_tl(s->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].ZMM_L(0)), + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0)), offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0))); } break; @@ -3206,12 +3232,14 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_lea_modrm(env, s, modrm); gen_ldq_env_A0(s, offsetof(CPUX86State, 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))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2))); + tcg_gen_st32_tl(s->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].ZMM_Q(0)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } break; @@ -3224,7 +3252,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } else { /* movhlps */ rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(1))); } break; @@ -3234,14 +3262,14 @@ 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].ZMM_L(0)), + gen_op_movl(s, 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)), + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), offsetof(CPUX86State,xmm_regs[rm].ZMM_L(2))); } - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(1)), + gen_op_movl(s, 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)), + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), offsetof(CPUX86State,xmm_regs[reg].ZMM_L(2))); break; case 0x312: /* movddup */ @@ -3251,10 +3279,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); break; case 0x016: /* movhps */ @@ -3266,7 +3294,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } else { /* movlhps */ rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1)), offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); } break; @@ -3276,14 +3304,14 @@ 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].ZMM_L(1)), + gen_op_movl(s, 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)), + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(3)), offsetof(CPUX86State,xmm_regs[rm].ZMM_L(3))); } - gen_op_movl(offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0)), + gen_op_movl(s, 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)), + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(2)), offsetof(CPUX86State,xmm_regs[reg].ZMM_L(3))); break; case 0x178: @@ -3295,14 +3323,14 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, goto illegal_op; field_length = x86_ldub_code(env, s) & 0x3F; bit_index = x86_ldub_code(env, s) & 0x3F; - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(s->ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[reg])); if (b1 == 1) - gen_helper_extrq_i(cpu_env, cpu_ptr0, + gen_helper_extrq_i(cpu_env, s->ptr0, tcg_const_i32(bit_index), tcg_const_i32(field_length)); else - gen_helper_insertq_i(cpu_env, cpu_ptr0, + gen_helper_insertq_i(cpu_env, s->ptr0, tcg_const_i32(bit_index), tcg_const_i32(field_length)); } @@ -3310,13 +3338,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_T0, cpu_env, + tcg_gen_ld_i64(s->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_T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx.MMX_L(0))); gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); } @@ -3324,13 +3352,13 @@ 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_T0, cpu_env, + tcg_gen_ld_i64(s->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_T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); gen_ldst_modrm(env, s, modrm, MO_32, OR_TMP0, 1); } @@ -3342,10 +3370,10 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0)), + gen_op_movq(s, 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].ZMM_Q(1))); + gen_op_movq_env_0(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(1))); break; case 0x7f: /* movq ea, mm */ if (mod != 3) { @@ -3353,7 +3381,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_stq_env_A0(s, offsetof(CPUX86State, fpregs[reg].mmx)); } else { rm = (modrm & 7); - gen_op_movq(offsetof(CPUX86State,fpregs[rm].mmx), + gen_op_movq(s, offsetof(CPUX86State, fpregs[rm].mmx), offsetof(CPUX86State,fpregs[reg].mmx)); } break; @@ -3368,18 +3396,19 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_sto_env_A0(s, offsetof(CPUX86State, xmm_regs[reg])); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movo(offsetof(CPUX86State,xmm_regs[rm]), + gen_op_movo(s, offsetof(CPUX86State, xmm_regs[rm]), offsetof(CPUX86State,xmm_regs[reg])); } break; case 0x211: /* movss ea, xmm */ if (mod != 3) { gen_lea_modrm(env, s, modrm); - 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); + tcg_gen_ld32u_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_regs[reg].ZMM_L(0))); + gen_op_st_v(s, MO_32, s->T0, s->A0); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movl(offsetof(CPUX86State,xmm_regs[rm].ZMM_L(0)), + gen_op_movl(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_L(0)), offsetof(CPUX86State,xmm_regs[reg].ZMM_L(0))); } break; @@ -3390,7 +3419,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[rm].ZMM_Q(0)), offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(0))); } break; @@ -3421,20 +3450,24 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x172: case 0x173: if (b1 >= 2) { - goto unknown_op; + goto unknown_op; } val = x86_ldub_code(env, s); if (is_xmm) { - 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))); + tcg_gen_movi_tl(s->T0, val); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_t0.ZMM_L(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_t0.ZMM_L(1))); op1_offset = offsetof(CPUX86State,xmm_t0); } else { - 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))); + tcg_gen_movi_tl(s->T0, val); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, mmx_t0.MMX_L(0))); + tcg_gen_movi_tl(s->T0, 0); + tcg_gen_st32_tl(s->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 + @@ -3449,23 +3482,23 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, rm = (modrm & 7); op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); } - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op1_offset); - sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op1_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); break; case 0x050: /* movmskps */ rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(s->ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_movmskps(cpu_tmp2_i32, cpu_env, cpu_ptr0); - tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); + gen_helper_movmskps(s->tmp2_i32, cpu_env, s->ptr0); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); break; case 0x150: /* movmskpd */ rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, + tcg_gen_addi_ptr(s->ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_movmskpd(cpu_tmp2_i32, cpu_env, cpu_ptr0); - tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); + gen_helper_movmskpd(s->tmp2_i32, cpu_env, s->ptr0); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); break; case 0x02a: /* cvtpi2ps */ case 0x12a: /* cvtpi2pd */ @@ -3479,15 +3512,15 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, op2_offset = offsetof(CPUX86State,fpregs[rm].mmx); } op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); switch(b >> 8) { case 0x0: - gen_helper_cvtpi2ps(cpu_env, cpu_ptr0, cpu_ptr1); + gen_helper_cvtpi2ps(cpu_env, s->ptr0, s->ptr1); break; default: case 0x1: - gen_helper_cvtpi2pd(cpu_env, cpu_ptr0, cpu_ptr1); + gen_helper_cvtpi2pd(cpu_env, s->ptr0, s->ptr1); break; } break; @@ -3496,15 +3529,15 @@ 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); op1_offset = offsetof(CPUX86State,xmm_regs[reg]); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->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_T0); - sse_fn_epi(cpu_env, cpu_ptr0, cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + sse_fn_epi(cpu_env, s->ptr0, s->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_T0); + sse_fn_epl(cpu_env, s->ptr0, s->T0); #else goto illegal_op; #endif @@ -3524,20 +3557,20 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, op2_offset = offsetof(CPUX86State,xmm_regs[rm]); } op1_offset = offsetof(CPUX86State,fpregs[reg & 7].mmx); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); switch(b) { case 0x02c: - gen_helper_cvttps2pi(cpu_env, cpu_ptr0, cpu_ptr1); + gen_helper_cvttps2pi(cpu_env, s->ptr0, s->ptr1); break; case 0x12c: - gen_helper_cvttpd2pi(cpu_env, cpu_ptr0, cpu_ptr1); + gen_helper_cvttpd2pi(cpu_env, s->ptr0, s->ptr1); break; case 0x02d: - gen_helper_cvtps2pi(cpu_env, cpu_ptr0, cpu_ptr1); + gen_helper_cvtps2pi(cpu_env, s->ptr0, s->ptr1); break; case 0x12d: - gen_helper_cvtpd2pi(cpu_env, cpu_ptr0, cpu_ptr1); + gen_helper_cvtpd2pi(cpu_env, s->ptr0, s->ptr1); break; } break; @@ -3551,30 +3584,31 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if ((b >> 8) & 1) { gen_ldq_env_A0(s, offsetof(CPUX86State, xmm_t0.ZMM_Q(0))); } else { - 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))); + gen_op_ld_v(s, MO_32, s->T0, s->A0); + tcg_gen_st32_tl(s->T0, cpu_env, + offsetof(CPUX86State, xmm_t0.ZMM_L(0))); } op2_offset = offsetof(CPUX86State,xmm_t0); } else { rm = (modrm & 7) | REX_B(s); op2_offset = offsetof(CPUX86State,xmm_regs[rm]); } - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op2_offset); if (ot == MO_32) { SSEFunc_i_ep sse_fn_i_ep = sse_op_table3bi[((b >> 7) & 2) | (b & 1)]; - sse_fn_i_ep(cpu_tmp2_i32, cpu_env, cpu_ptr0); - tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); + sse_fn_i_ep(s->tmp2_i32, cpu_env, s->ptr0); + tcg_gen_extu_i32_tl(s->T0, s->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_T0, cpu_env, cpu_ptr0); + sse_fn_l_ep(s->T0, cpu_env, s->ptr0); #else goto illegal_op; #endif } - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; case 0xc4: /* pinsrw */ case 0x1c4: @@ -3583,11 +3617,11 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, val = x86_ldub_code(env, s); if (b1) { val &= 7; - tcg_gen_st16_tl(cpu_T0, cpu_env, + tcg_gen_st16_tl(s->T0, cpu_env, offsetof(CPUX86State,xmm_regs[reg].ZMM_W(val))); } else { val &= 3; - tcg_gen_st16_tl(cpu_T0, cpu_env, + tcg_gen_st16_tl(s->T0, cpu_env, offsetof(CPUX86State,fpregs[reg].mmx.MMX_W(val))); } break; @@ -3600,16 +3634,16 @@ 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_T0, cpu_env, + tcg_gen_ld16u_tl(s->T0, cpu_env, offsetof(CPUX86State,xmm_regs[rm].ZMM_W(val))); } else { val &= 3; rm = (modrm & 7); - tcg_gen_ld16u_tl(cpu_T0, cpu_env, + tcg_gen_ld16u_tl(s->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_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; case 0x1d6: /* movq ea, xmm */ if (mod != 3) { @@ -3618,22 +3652,23 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, xmm_regs[reg].ZMM_Q(0))); } else { rm = (modrm & 7) | REX_B(s); - gen_op_movq(offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0)), + gen_op_movq(s, 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))); + gen_op_movq_env_0(s, + 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].ZMM_Q(0)), + gen_op_movq(s, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(0)), offsetof(CPUX86State,fpregs[rm].mmx)); - gen_op_movq_env_0(offsetof(CPUX86State,xmm_regs[reg].ZMM_Q(1))); + gen_op_movq_env_0(s, 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), + gen_op_movq(s, offsetof(CPUX86State, fpregs[reg & 7].mmx), offsetof(CPUX86State,xmm_regs[rm].ZMM_Q(0))); break; case 0xd7: /* pmovmskb */ @@ -3642,15 +3677,17 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, goto illegal_op; if (b1) { rm = (modrm & 7) | REX_B(s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,xmm_regs[rm])); - gen_helper_pmovmskb_xmm(cpu_tmp2_i32, cpu_env, cpu_ptr0); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State, xmm_regs[rm])); + gen_helper_pmovmskb_xmm(s->tmp2_i32, cpu_env, s->ptr0); } else { rm = (modrm & 7); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, offsetof(CPUX86State,fpregs[rm].mmx)); - gen_helper_pmovmskb_mmx(cpu_tmp2_i32, cpu_env, cpu_ptr0); + tcg_gen_addi_ptr(s->ptr0, cpu_env, + offsetof(CPUX86State, fpregs[rm].mmx)); + gen_helper_pmovmskb_mmx(s->tmp2_i32, cpu_env, s->ptr0); } reg = ((modrm >> 3) & 7) | rex_r; - tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); break; case 0x138: @@ -3690,15 +3727,15 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, break; case 0x21: case 0x31: /* pmovsxbd, pmovzxbd */ case 0x24: case 0x34: /* pmovsxwq, pmovzxwq */ - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, op2_offset + + tcg_gen_st_i32(s->tmp2_i32, cpu_env, op2_offset + offsetof(ZMMReg, ZMM_L(0))); break; case 0x22: case 0x32: /* pmovsxbq, pmovzxbq */ - tcg_gen_qemu_ld_tl(cpu_tmp0, cpu_A0, + tcg_gen_qemu_ld_tl(s->tmp0, s->A0, s->mem_index, MO_LEUW); - tcg_gen_st16_tl(cpu_tmp0, cpu_env, op2_offset + + tcg_gen_st16_tl(s->tmp0, cpu_env, op2_offset + offsetof(ZMMReg, ZMM_W(0))); break; case 0x2a: /* movntqda */ @@ -3722,9 +3759,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, goto unknown_op; } - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); if (b == 0x17) { set_cc_op(s, CC_OP_EFLAGS); @@ -3754,13 +3791,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, ot = MO_64; } - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[reg]); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[reg]); gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_helper_crc32(cpu_T0, cpu_tmp2_i32, - cpu_T0, tcg_const_i32(8 << ot)); + gen_helper_crc32(s->T0, s->tmp2_i32, + s->T0, tcg_const_i32(8 << ot)); ot = mo_64_32(s->dflag); - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; case 0x1f0: /* crc32 or movbe */ @@ -3785,11 +3822,11 @@ 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_T0, cpu_A0, + tcg_gen_qemu_ld_tl(s->T0, s->A0, s->mem_index, ot | MO_BE); - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); } else { - tcg_gen_qemu_st_tl(cpu_regs[reg], cpu_A0, + tcg_gen_qemu_st_tl(cpu_regs[reg], s->A0, s->mem_index, ot | MO_BE); } break; @@ -3802,9 +3839,9 @@ 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_T0, cpu_T0, cpu_regs[s->vex_v]); - gen_op_mov_reg_v(ot, reg, cpu_T0); - gen_op_update1_cc(); + tcg_gen_andc_tl(s->T0, s->T0, cpu_regs[s->vex_v]); + gen_op_mov_reg_v(s, ot, reg, s->T0); + gen_op_update1_cc(s); set_cc_op(s, CC_OP_LOGICB + ot); break; @@ -3821,28 +3858,28 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); /* 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_T0, cpu_T0, cpu_A0); + tcg_gen_ext8u_tl(s->A0, cpu_regs[s->vex_v]); + tcg_gen_shr_tl(s->T0, s->T0, s->A0); bound = tcg_const_tl(ot == MO_64 ? 63 : 31); zero = tcg_const_tl(0); - tcg_gen_movcond_tl(TCG_COND_LEU, cpu_T0, cpu_A0, bound, - cpu_T0, zero); + tcg_gen_movcond_tl(TCG_COND_LEU, s->T0, s->A0, bound, + s->T0, zero); tcg_temp_free(zero); /* Extract the LEN into a mask. Lengths larger than operand size get all ones. */ - tcg_gen_extract_tl(cpu_A0, cpu_regs[s->vex_v], 8, 8); - tcg_gen_movcond_tl(TCG_COND_LEU, cpu_A0, cpu_A0, bound, - cpu_A0, bound); + tcg_gen_extract_tl(s->A0, cpu_regs[s->vex_v], 8, 8); + tcg_gen_movcond_tl(TCG_COND_LEU, s->A0, s->A0, bound, + s->A0, bound); tcg_temp_free(bound); - 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); + tcg_gen_movi_tl(s->T1, 1); + tcg_gen_shl_tl(s->T1, s->T1, s->A0); + tcg_gen_subi_tl(s->T1, s->T1, 1); + tcg_gen_and_tl(s->T0, s->T0, s->T1); - gen_op_mov_reg_v(ot, reg, cpu_T0); - gen_op_update1_cc(); + gen_op_mov_reg_v(s, ot, reg, s->T0); + gen_op_update1_cc(s); set_cc_op(s, CC_OP_LOGICB + ot); } break; @@ -3855,22 +3892,22 @@ 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_T1, cpu_regs[s->vex_v]); + tcg_gen_ext8u_tl(s->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_T1, bound); - tcg_gen_movcond_tl(TCG_COND_GT, cpu_T1, cpu_T1, - bound, bound, cpu_T1); + s->T1, bound); + tcg_gen_movcond_tl(TCG_COND_GT, s->T1, s->T1, + bound, bound, s->T1); tcg_temp_free(bound); } - tcg_gen_movi_tl(cpu_A0, -1); - 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(); + tcg_gen_movi_tl(s->A0, -1); + tcg_gen_shl_tl(s->A0, s->A0, s->T1); + tcg_gen_andc_tl(s->T0, s->T0, s->A0); + gen_op_mov_reg_v(s, ot, reg, s->T0); + gen_op_update1_cc(s); set_cc_op(s, CC_OP_BMILGB + ot); break; @@ -3884,19 +3921,19 @@ 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_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); - tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], cpu_tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[reg], cpu_tmp3_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EDX]); + tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp3_i32); break; #ifdef TARGET_X86_64 case MO_64: - 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); + tcg_gen_mulu2_i64(s->T0, s->T1, + s->T0, cpu_regs[R_EDX]); + tcg_gen_mov_i64(cpu_regs[s->vex_v], s->T0); + tcg_gen_mov_i64(cpu_regs[reg], s->T1); break; #endif } @@ -3913,11 +3950,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_T1, cpu_regs[s->vex_v]); + tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); } else { - tcg_gen_ext32u_tl(cpu_T1, cpu_regs[s->vex_v]); + tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); } - gen_helper_pdep(cpu_regs[reg], cpu_T0, cpu_T1); + gen_helper_pdep(cpu_regs[reg], s->T0, s->T1); break; case 0x2f5: /* pext Gy, By, Ey */ @@ -3931,11 +3968,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_T1, cpu_regs[s->vex_v]); + tcg_gen_mov_tl(s->T1, cpu_regs[s->vex_v]); } else { - tcg_gen_ext32u_tl(cpu_T1, cpu_regs[s->vex_v]); + tcg_gen_ext32u_tl(s->T1, cpu_regs[s->vex_v]); } - gen_helper_pext(cpu_regs[reg], cpu_T0, cpu_T1); + gen_helper_pext(cpu_regs[reg], s->T0, s->T1); break; case 0x1f6: /* adcx Gy, Ey */ @@ -3982,7 +4019,7 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { gen_compute_eflags(s); } - carry_in = cpu_tmp0; + carry_in = s->tmp0; tcg_gen_extract_tl(carry_in, cpu_cc_src, ctz32(b == 0x1f6 ? CC_C : CC_O), 1); } @@ -3993,22 +4030,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_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); + tcg_gen_ext32u_i64(s->T0, s->T0); + tcg_gen_add_i64(s->T0, s->T0, cpu_regs[reg]); + tcg_gen_add_i64(s->T0, s->T0, carry_in); + tcg_gen_ext32u_i64(cpu_regs[reg], s->T0); + tcg_gen_shri_i64(carry_out, s->T0, 32); break; #endif default: /* Otherwise compute the carry-out in two steps. */ zero = tcg_const_tl(0); - tcg_gen_add2_tl(cpu_T0, carry_out, - cpu_T0, zero, + tcg_gen_add2_tl(s->T0, carry_out, + s->T0, zero, carry_in, zero); tcg_gen_add2_tl(cpu_regs[reg], carry_out, cpu_regs[reg], carry_out, - cpu_T0, zero); + s->T0, zero); tcg_temp_free(zero); break; } @@ -4027,24 +4064,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_T1, cpu_regs[s->vex_v], 63); + tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 63); } else { - tcg_gen_andi_tl(cpu_T1, cpu_regs[s->vex_v], 31); + tcg_gen_andi_tl(s->T1, cpu_regs[s->vex_v], 31); } if (b == 0x1f7) { - tcg_gen_shl_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_shl_tl(s->T0, s->T0, s->T1); } else if (b == 0x2f7) { if (ot != MO_64) { - tcg_gen_ext32s_tl(cpu_T0, cpu_T0); + tcg_gen_ext32s_tl(s->T0, s->T0); } - tcg_gen_sar_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_sar_tl(s->T0, s->T0, s->T1); } else { if (ot != MO_64) { - tcg_gen_ext32u_tl(cpu_T0, cpu_T0); + tcg_gen_ext32u_tl(s->T0, s->T0); } - tcg_gen_shr_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_shr_tl(s->T0, s->T0, s->T1); } - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; case 0x0f3: @@ -4059,25 +4096,25 @@ 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_mov_tl(cpu_cc_src, cpu_T0); + tcg_gen_mov_tl(cpu_cc_src, s->T0); switch (reg & 7) { case 1: /* blsr By,Ey */ - tcg_gen_subi_tl(cpu_T1, cpu_T0, 1); - tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_subi_tl(s->T1, s->T0, 1); + tcg_gen_and_tl(s->T0, s->T0, s->T1); break; case 2: /* blsmsk By,Ey */ - tcg_gen_subi_tl(cpu_T1, cpu_T0, 1); - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_subi_tl(s->T1, s->T0, 1); + tcg_gen_xor_tl(s->T0, s->T0, s->T1); break; case 3: /* blsi By, Ey */ - tcg_gen_neg_tl(cpu_T1, cpu_T0); - tcg_gen_and_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_neg_tl(s->T1, s->T0); + tcg_gen_and_tl(s->T0, s->T0, s->T1); break; default: goto unknown_op; } - tcg_gen_mov_tl(cpu_cc_dst, cpu_T0); - gen_op_mov_reg_v(ot, s->vex_v, cpu_T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + gen_op_mov_reg_v(s, ot, s->vex_v, s->T0); set_cc_op(s, CC_OP_BMILGB + ot); break; @@ -4115,45 +4152,45 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, val = x86_ldub_code(env, s); switch (b) { case 0x14: /* pextrb */ - tcg_gen_ld8u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + tcg_gen_ld8u_tl(s->T0, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_B(val & 15))); if (mod == 3) { - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } else { - tcg_gen_qemu_st_tl(cpu_T0, cpu_A0, + tcg_gen_qemu_st_tl(s->T0, s->A0, s->mem_index, MO_UB); } break; case 0x15: /* pextrw */ - tcg_gen_ld16u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + tcg_gen_ld16u_tl(s->T0, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_W(val & 7))); if (mod == 3) { - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } else { - tcg_gen_qemu_st_tl(cpu_T0, cpu_A0, + tcg_gen_qemu_st_tl(s->T0, s->A0, s->mem_index, MO_LEUW); } break; case 0x16: if (ot == MO_32) { /* pextrd */ - tcg_gen_ld_i32(cpu_tmp2_i32, cpu_env, + tcg_gen_ld_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(val & 3))); if (mod == 3) { - tcg_gen_extu_i32_tl(cpu_regs[rm], cpu_tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[rm], s->tmp2_i32); } else { - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); } } else { /* pextrq */ #ifdef TARGET_X86_64 - tcg_gen_ld_i64(cpu_tmp1_i64, cpu_env, + tcg_gen_ld_i64(s->tmp1_i64, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(val & 1))); if (mod == 3) { - tcg_gen_mov_i64(cpu_regs[rm], cpu_tmp1_i64); + tcg_gen_mov_i64(cpu_regs[rm], s->tmp1_i64); } else { - tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); } #else @@ -4162,35 +4199,35 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } break; case 0x17: /* extractps */ - tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(val & 3))); if (mod == 3) { - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } else { - tcg_gen_qemu_st_tl(cpu_T0, cpu_A0, + tcg_gen_qemu_st_tl(s->T0, s->A0, s->mem_index, MO_LEUL); } break; case 0x20: /* pinsrb */ if (mod == 3) { - gen_op_mov_v_reg(MO_32, cpu_T0, rm); + gen_op_mov_v_reg(s, MO_32, s->T0, rm); } else { - tcg_gen_qemu_ld_tl(cpu_T0, cpu_A0, + tcg_gen_qemu_ld_tl(s->T0, s->A0, s->mem_index, MO_UB); } - tcg_gen_st8_tl(cpu_T0, cpu_env, offsetof(CPUX86State, + tcg_gen_st8_tl(s->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, + tcg_gen_ld_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State,xmm_regs[rm] .ZMM_L((val >> 6) & 3))); } else { - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); } - tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, + tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State,xmm_regs[reg] .ZMM_L((val >> 4) & 3))); if ((val >> 0) & 1) @@ -4213,23 +4250,23 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, case 0x22: if (ot == MO_32) { /* pinsrd */ if (mod == 3) { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_regs[rm]); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[rm]); } else { - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); } - tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, + tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_L(val & 3))); } else { /* pinsrq */ #ifdef TARGET_X86_64 if (mod == 3) { - gen_op_mov_v_reg(ot, cpu_tmp1_i64, rm); + gen_op_mov_v_reg(s, ot, s->tmp1_i64, rm); } else { - tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); } - tcg_gen_st_i64(cpu_tmp1_i64, cpu_env, + tcg_gen_st_i64(s->tmp1_i64, cpu_env, offsetof(CPUX86State, xmm_regs[reg].ZMM_Q(val & 1))); #else @@ -4271,9 +4308,9 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, } } - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); - sse_fn_eppi(cpu_env, cpu_ptr0, cpu_ptr1, tcg_const_i32(val)); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_eppi(cpu_env, s->ptr0, s->ptr1, tcg_const_i32(val)); break; case 0x33a: @@ -4293,13 +4330,13 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); b = x86_ldub_code(env, s); if (ot == MO_64) { - tcg_gen_rotri_tl(cpu_T0, cpu_T0, b & 63); + tcg_gen_rotri_tl(s->T0, s->T0, b & 63); } else { - 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_T0, cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, b & 31); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); } - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; default: @@ -4356,8 +4393,8 @@ 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_T0, cpu_A0); - tcg_gen_st32_tl(cpu_T0, cpu_env, + gen_op_ld_v(s, MO_32, s->T0, s->A0); + tcg_gen_st32_tl(s->T0, cpu_env, offsetof(CPUX86State,xmm_t0.ZMM_L(0))); break; case 3: @@ -4394,18 +4431,18 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, if (!(s->cpuid_ext2_features & CPUID_EXT2_3DNOW)) { goto illegal_op; } - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); break; case 0x70: /* pshufx insn */ case 0xc6: /* pshufx insn */ val = x86_ldub_code(env, s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); /* XXX: introduce a new table? */ sse_fn_ppi = (SSEFunc_0_ppi)sse_fn_epp; - sse_fn_ppi(cpu_ptr0, cpu_ptr1, tcg_const_i32(val)); + sse_fn_ppi(s->ptr0, s->ptr1, tcg_const_i32(val)); break; case 0xc2: /* compare insns */ @@ -4414,28 +4451,28 @@ static void gen_sse(CPUX86State *env, DisasContext *s, int b, goto unknown_op; sse_fn_epp = sse_op_table4[val][b1]; - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); break; case 0xf7: /* maskmov : we must prepare A0 */ if (mod != 3) goto illegal_op; - tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EDI]); - gen_extu(s->aflag, cpu_A0); + tcg_gen_mov_tl(s->A0, cpu_regs[R_EDI]); + gen_extu(s->aflag, s->A0); gen_add_A0_ds_seg(s); - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); /* XXX: introduce a new table? */ sse_fn_eppt = (SSEFunc_0_eppt)sse_fn_epp; - sse_fn_eppt(cpu_env, cpu_ptr0, cpu_ptr1, cpu_A0); + sse_fn_eppt(cpu_env, s->ptr0, s->ptr1, s->A0); break; default: - tcg_gen_addi_ptr(cpu_ptr0, cpu_env, op1_offset); - tcg_gen_addi_ptr(cpu_ptr1, cpu_env, op2_offset); - sse_fn_epp(cpu_env, cpu_ptr0, cpu_ptr1); + tcg_gen_addi_ptr(s->ptr0, cpu_env, op1_offset); + tcg_gen_addi_ptr(s->ptr1, cpu_env, op2_offset); + sse_fn_epp(cpu_env, s->ptr0, s->ptr1); break; } if (b == 0x2e || b == 0x2f) { @@ -4462,7 +4499,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) #ifdef TARGET_X86_64 s->rex_x = 0; s->rex_b = 0; - x86_64_hregs = 0; + s->x86_64_hregs = false; #endif s->rip_offset = 0; /* for relative ip address */ s->vex_l = 0; @@ -4521,7 +4558,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) rex_r = (b & 0x4) << 1; s->rex_x = (b & 0x2) << 2; REX_B(s) = (b & 0x1) << 3; - x86_64_hregs = 1; /* select uniform byte register addressing */ + /* select uniform byte register addressing */ + s->x86_64_hregs = true; goto next_byte; } break; @@ -4549,7 +4587,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } #ifdef TARGET_X86_64 - if (x86_64_hregs) { + if (s->x86_64_hregs) { goto illegal_op; } #endif @@ -4653,13 +4691,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) xor_zero: /* xor reg, reg optimisation */ set_cc_op(s, CC_OP_CLR); - tcg_gen_movi_tl(cpu_T0, 0); - gen_op_mov_reg_v(ot, reg, cpu_T0); + tcg_gen_movi_tl(s->T0, 0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; } else { opreg = rm; } - gen_op_mov_v_reg(ot, cpu_T1, reg); + gen_op_mov_v_reg(s, ot, s->T1, reg); gen_op(s, op, ot, opreg); break; case 1: /* OP Gv, Ev */ @@ -4669,17 +4707,17 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) rm = (modrm & 7) | REX_B(s); if (mod != 3) { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_ld_v(s, ot, s->T1, s->A0); } else if (op == OP_XORL && rm == reg) { goto xor_zero; } else { - gen_op_mov_v_reg(ot, cpu_T1, rm); + gen_op_mov_v_reg(s, ot, s->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_T1, val); + tcg_gen_movi_tl(s->T1, val); gen_op(s, op, ot, OR_EAX); break; } @@ -4689,6 +4727,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x82: if (CODE64(s)) goto illegal_op; + /* fall through */ case 0x80: /* GRP1 */ case 0x81: case 0x83: @@ -4724,7 +4763,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) val = (int8_t)insn_get(env, s, MO_8); break; } - tcg_gen_movi_tl(cpu_T1, val); + tcg_gen_movi_tl(s->T1, val); gen_op(s, op, ot, opreg); } break; @@ -4755,17 +4794,17 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* For those below that handle locked memory, don't load here. */ if (!(s->prefix & PREFIX_LOCK) || op != 2) { - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } } else { - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); } switch(op) { case 0: /* test */ val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T1, val); - gen_op_testl_T0_T1_cc(); + tcg_gen_movi_tl(s->T1, val); + gen_op_testl_T0_T1_cc(s); set_cc_op(s, CC_OP_LOGICB + ot); break; case 2: /* not */ @@ -4773,15 +4812,15 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod == 3) { goto illegal_op; } - tcg_gen_movi_tl(cpu_T0, ~0); - tcg_gen_atomic_xor_fetch_tl(cpu_T0, cpu_A0, cpu_T0, + tcg_gen_movi_tl(s->T0, ~0); + tcg_gen_atomic_xor_fetch_tl(s->T0, s->A0, s->T0, s->mem_index, ot | MO_LE); } else { - tcg_gen_not_tl(cpu_T0, cpu_T0); + tcg_gen_not_tl(s->T0, s->T0); if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_st_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } } break; @@ -4797,8 +4836,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) t0 = tcg_temp_local_new(); label1 = gen_new_label(); - tcg_gen_mov_tl(a0, cpu_A0); - tcg_gen_mov_tl(t0, cpu_T0); + tcg_gen_mov_tl(a0, s->A0); + tcg_gen_mov_tl(t0, s->T0); gen_set_label(label1); t1 = tcg_temp_new(); @@ -4812,53 +4851,53 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_temp_free(t2); tcg_temp_free(a0); - tcg_gen_mov_tl(cpu_T0, t0); + tcg_gen_mov_tl(s->T0, t0); tcg_temp_free(t0); } else { - tcg_gen_neg_tl(cpu_T0, cpu_T0); + tcg_gen_neg_tl(s->T0, s->T0); if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_st_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } } - gen_op_update_neg_cc(); + gen_op_update_neg_cc(s); set_cc_op(s, CC_OP_SUBB + ot); break; case 4: /* mul */ switch(ot) { case MO_8: - gen_op_mov_v_reg(MO_8, cpu_T1, R_EAX); - tcg_gen_ext8u_tl(cpu_T0, cpu_T0); - tcg_gen_ext8u_tl(cpu_T1, cpu_T1); + gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); + tcg_gen_ext8u_tl(s->T0, s->T0); + tcg_gen_ext8u_tl(s->T1, s->T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(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); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_andi_tl(cpu_cc_src, s->T0, 0xff00); set_cc_op(s, CC_OP_MULB); break; case MO_16: - 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); + gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); + tcg_gen_ext16u_tl(s->T0, s->T0); + tcg_gen_ext16u_tl(s->T1, s->T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(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); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_shri_tl(s->T0, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); + tcg_gen_mov_tl(cpu_cc_src, s->T0); set_cc_op(s, CC_OP_MULW); break; default: case MO_32: - 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); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], cpu_tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], cpu_tmp3_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); + tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); set_cc_op(s, CC_OP_MULL); @@ -4866,7 +4905,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) #ifdef TARGET_X86_64 case MO_64: tcg_gen_mulu2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - cpu_T0, cpu_regs[R_EAX]); + s->T0, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_src, cpu_regs[R_EDX]); set_cc_op(s, CC_OP_MULQ); @@ -4877,49 +4916,49 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 5: /* imul */ switch(ot) { case MO_8: - 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); + gen_op_mov_v_reg(s, MO_8, s->T1, R_EAX); + tcg_gen_ext8s_tl(s->T0, s->T0); + tcg_gen_ext8s_tl(s->T1, s->T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(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); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_ext8s_tl(s->tmp0, s->T0); + tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); set_cc_op(s, CC_OP_MULB); break; case MO_16: - 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); + gen_op_mov_v_reg(s, MO_16, s->T1, R_EAX); + tcg_gen_ext16s_tl(s->T0, s->T0); + tcg_gen_ext16s_tl(s->T1, s->T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(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); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_ext16s_tl(s->tmp0, s->T0); + tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); + tcg_gen_shri_tl(s->T0, s->T0, 16); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); set_cc_op(s, CC_OP_MULW); break; default: case MO_32: - 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); - tcg_gen_extu_i32_tl(cpu_regs[R_EAX], cpu_tmp2_i32); - tcg_gen_extu_i32_tl(cpu_regs[R_EDX], cpu_tmp3_i32); - tcg_gen_sari_i32(cpu_tmp2_i32, cpu_tmp2_i32, 31); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, cpu_regs[R_EAX]); + tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EAX], s->tmp2_i32); + tcg_gen_extu_i32_tl(cpu_regs[R_EDX], s->tmp3_i32); + tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); - tcg_gen_sub_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); + tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); set_cc_op(s, CC_OP_MULL); break; #ifdef TARGET_X86_64 case MO_64: tcg_gen_muls2_i64(cpu_regs[R_EAX], cpu_regs[R_EDX], - cpu_T0, cpu_regs[R_EAX]); + s->T0, cpu_regs[R_EAX]); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[R_EAX]); tcg_gen_sari_tl(cpu_cc_src, cpu_regs[R_EAX], 63); tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_regs[R_EDX]); @@ -4931,18 +4970,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 6: /* div */ switch(ot) { case MO_8: - gen_helper_divb_AL(cpu_env, cpu_T0); + gen_helper_divb_AL(cpu_env, s->T0); break; case MO_16: - gen_helper_divw_AX(cpu_env, cpu_T0); + gen_helper_divw_AX(cpu_env, s->T0); break; default: case MO_32: - gen_helper_divl_EAX(cpu_env, cpu_T0); + gen_helper_divl_EAX(cpu_env, s->T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_divq_EAX(cpu_env, cpu_T0); + gen_helper_divq_EAX(cpu_env, s->T0); break; #endif } @@ -4950,18 +4989,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 7: /* idiv */ switch(ot) { case MO_8: - gen_helper_idivb_AL(cpu_env, cpu_T0); + gen_helper_idivb_AL(cpu_env, s->T0); break; case MO_16: - gen_helper_idivw_AX(cpu_env, cpu_T0); + gen_helper_idivw_AX(cpu_env, s->T0); break; default: case MO_32: - gen_helper_idivl_EAX(cpu_env, cpu_T0); + gen_helper_idivl_EAX(cpu_env, s->T0); break; #ifdef TARGET_X86_64 case MO_64: - gen_helper_idivq_EAX(cpu_env, cpu_T0); + gen_helper_idivq_EAX(cpu_env, s->T0); break; #endif } @@ -4996,9 +5035,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod != 3) { gen_lea_modrm(env, s, modrm); if (op >= 2 && op != 3 && op != 5) - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); } switch(op) { @@ -5019,60 +5058,60 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 2: /* call Ev */ /* XXX: optimize if memory (no 'and' is necessary) */ if (dflag == MO_16) { - tcg_gen_ext16u_tl(cpu_T0, cpu_T0); + tcg_gen_ext16u_tl(s->T0, s->T0); } next_eip = s->pc - s->cs_base; - tcg_gen_movi_tl(cpu_T1, next_eip); - gen_push_v(s, cpu_T1); - gen_op_jmp_v(cpu_T0); + tcg_gen_movi_tl(s->T1, next_eip); + gen_push_v(s, s->T1); + gen_op_jmp_v(s->T0); gen_bnd_jmp(s); - gen_jr(s, cpu_T0); + gen_jr(s, s->T0); break; case 3: /* lcall Ev */ - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_ld_v(s, ot, s->T1, s->A0); gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, cpu_T0, cpu_A0); + gen_op_ld_v(s, MO_16, s->T0, s->A0); do_lcall: if (s->pe && !s->vm86) { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_lcall_protected(cpu_env, cpu_tmp2_i32, cpu_T1, + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_lcall_protected(cpu_env, s->tmp2_i32, s->T1, tcg_const_i32(dflag - 1), tcg_const_tl(s->pc - s->cs_base)); } else { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_lcall_real(cpu_env, cpu_tmp2_i32, cpu_T1, + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_lcall_real(cpu_env, s->tmp2_i32, s->T1, tcg_const_i32(dflag - 1), tcg_const_i32(s->pc - s->cs_base)); } - tcg_gen_ld_tl(cpu_tmp4, cpu_env, offsetof(CPUX86State, eip)); - gen_jr(s, cpu_tmp4); + tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); + gen_jr(s, s->tmp4); break; case 4: /* jmp Ev */ if (dflag == MO_16) { - tcg_gen_ext16u_tl(cpu_T0, cpu_T0); + tcg_gen_ext16u_tl(s->T0, s->T0); } - gen_op_jmp_v(cpu_T0); + gen_op_jmp_v(s->T0); gen_bnd_jmp(s); - gen_jr(s, cpu_T0); + gen_jr(s, s->T0); break; case 5: /* ljmp Ev */ - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_ld_v(s, ot, s->T1, s->A0); gen_add_A0_im(s, 1 << ot); - gen_op_ld_v(s, MO_16, cpu_T0, cpu_A0); + gen_op_ld_v(s, MO_16, s->T0, s->A0); do_ljmp: if (s->pe && !s->vm86) { - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_ljmp_protected(cpu_env, cpu_tmp2_i32, cpu_T1, + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_ljmp_protected(cpu_env, s->tmp2_i32, s->T1, tcg_const_tl(s->pc - s->cs_base)); } else { - gen_op_movl_seg_T0_vm(R_CS); - gen_op_jmp_v(cpu_T1); + gen_op_movl_seg_T0_vm(s, R_CS); + gen_op_jmp_v(s->T1); } - tcg_gen_ld_tl(cpu_tmp4, cpu_env, offsetof(CPUX86State, eip)); - gen_jr(s, cpu_tmp4); + tcg_gen_ld_tl(s->tmp4, cpu_env, offsetof(CPUX86State, eip)); + gen_jr(s, s->tmp4); break; case 6: /* push Ev */ - gen_push_v(s, cpu_T0); + gen_push_v(s, s->T0); break; default: goto unknown_op; @@ -5087,8 +5126,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_v_reg(ot, cpu_T1, reg); - gen_op_testl_T0_T1_cc(); + gen_op_mov_v_reg(s, ot, s->T1, reg); + gen_op_testl_T0_T1_cc(s); set_cc_op(s, CC_OP_LOGICB + ot); break; @@ -5097,9 +5136,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) ot = mo_b_d(b, dflag); val = insn_get(env, s, ot); - gen_op_mov_v_reg(ot, cpu_T0, OR_EAX); - tcg_gen_movi_tl(cpu_T1, val); - gen_op_testl_T0_T1_cc(); + gen_op_mov_v_reg(s, ot, s->T0, OR_EAX); + tcg_gen_movi_tl(s->T1, val); + gen_op_testl_T0_T1_cc(s); set_cc_op(s, CC_OP_LOGICB + ot); break; @@ -5107,20 +5146,20 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) switch (dflag) { #ifdef TARGET_X86_64 case MO_64: - 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); + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); + tcg_gen_ext32s_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_64, R_EAX, s->T0); break; #endif case MO_32: - 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); + gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); + tcg_gen_ext16s_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_32, R_EAX, s->T0); break; case MO_16: - 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); + gen_op_mov_v_reg(s, MO_8, s->T0, R_EAX); + tcg_gen_ext8s_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); break; default: tcg_abort(); @@ -5130,22 +5169,22 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) switch (dflag) { #ifdef TARGET_X86_64 case MO_64: - 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); + gen_op_mov_v_reg(s, MO_64, s->T0, R_EAX); + tcg_gen_sari_tl(s->T0, s->T0, 63); + gen_op_mov_reg_v(s, MO_64, R_EDX, s->T0); break; #endif case MO_32: - 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); + gen_op_mov_v_reg(s, MO_32, s->T0, R_EAX); + tcg_gen_ext32s_tl(s->T0, s->T0); + tcg_gen_sari_tl(s->T0, s->T0, 31); + gen_op_mov_reg_v(s, MO_32, R_EDX, s->T0); break; case MO_16: - 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); + gen_op_mov_v_reg(s, MO_16, s->T0, R_EAX); + tcg_gen_ext16s_tl(s->T0, s->T0); + tcg_gen_sari_tl(s->T0, s->T0, 15); + gen_op_mov_reg_v(s, MO_16, R_EDX, s->T0); break; default: tcg_abort(); @@ -5164,42 +5203,42 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); if (b == 0x69) { val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T1, val); + tcg_gen_movi_tl(s->T1, val); } else if (b == 0x6b) { val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(cpu_T1, val); + tcg_gen_movi_tl(s->T1, val); } else { - gen_op_mov_v_reg(ot, cpu_T1, reg); + gen_op_mov_v_reg(s, ot, s->T1, reg); } switch (ot) { #ifdef TARGET_X86_64 case MO_64: - tcg_gen_muls2_i64(cpu_regs[reg], cpu_T1, cpu_T0, cpu_T1); + tcg_gen_muls2_i64(cpu_regs[reg], s->T1, s->T0, s->T1); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); tcg_gen_sari_tl(cpu_cc_src, cpu_cc_dst, 63); - tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, cpu_T1); + tcg_gen_sub_tl(cpu_cc_src, cpu_cc_src, s->T1); break; #endif case MO_32: - 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); - tcg_gen_sari_i32(cpu_tmp2_i32, cpu_tmp2_i32, 31); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + tcg_gen_muls2_i32(s->tmp2_i32, s->tmp3_i32, + s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_regs[reg], s->tmp2_i32); + tcg_gen_sari_i32(s->tmp2_i32, s->tmp2_i32, 31); tcg_gen_mov_tl(cpu_cc_dst, cpu_regs[reg]); - tcg_gen_sub_i32(cpu_tmp2_i32, cpu_tmp2_i32, cpu_tmp3_i32); - tcg_gen_extu_i32_tl(cpu_cc_src, cpu_tmp2_i32); + tcg_gen_sub_i32(s->tmp2_i32, s->tmp2_i32, s->tmp3_i32); + tcg_gen_extu_i32_tl(cpu_cc_src, s->tmp2_i32); break; default: - tcg_gen_ext16s_tl(cpu_T0, cpu_T0); - tcg_gen_ext16s_tl(cpu_T1, cpu_T1); + tcg_gen_ext16s_tl(s->T0, s->T0); + tcg_gen_ext16s_tl(s->T1, s->T1); /* XXX: use 32 bit mul which could be faster */ - tcg_gen_mul_tl(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); + tcg_gen_mul_tl(s->T0, s->T0, s->T1); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); + tcg_gen_ext16s_tl(s->tmp0, s->T0); + tcg_gen_sub_tl(cpu_cc_src, s->T0, s->tmp0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; } set_cc_op(s, CC_OP_MULB + ot); @@ -5210,27 +5249,27 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; - gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_mov_v_reg(s, ot, s->T0, reg); if (mod == 3) { rm = (modrm & 7) | REX_B(s); - 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); + gen_op_mov_v_reg(s, ot, s->T1, rm); + tcg_gen_add_tl(s->T0, s->T0, s->T1); + gen_op_mov_reg_v(s, ot, reg, s->T1); + gen_op_mov_reg_v(s, ot, rm, s->T0); } else { gen_lea_modrm(env, s, modrm); if (s->prefix & PREFIX_LOCK) { - tcg_gen_atomic_fetch_add_tl(cpu_T1, cpu_A0, cpu_T0, + tcg_gen_atomic_fetch_add_tl(s->T1, s->A0, s->T0, s->mem_index, ot | MO_LE); - tcg_gen_add_tl(cpu_T0, cpu_T0, cpu_T1); + tcg_gen_add_tl(s->T0, s->T0, s->T1); } else { - 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_ld_v(s, ot, s->T1, s->A0); + tcg_gen_add_tl(s->T0, s->T0, s->T1); + gen_op_st_v(s, ot, s->T0, s->A0); } - gen_op_mov_reg_v(ot, reg, cpu_T1); + gen_op_mov_reg_v(s, ot, reg, s->T1); } - gen_op_update2_cc(); + gen_op_update2_cc(s); set_cc_op(s, CC_OP_ADDB + ot); break; case 0x1b0: @@ -5245,7 +5284,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) oldv = tcg_temp_new(); newv = tcg_temp_new(); cmpv = tcg_temp_new(); - gen_op_mov_v_reg(ot, newv, reg); + gen_op_mov_v_reg(s, ot, newv, reg); tcg_gen_mov_tl(cmpv, cpu_regs[R_EAX]); if (s->prefix & PREFIX_LOCK) { @@ -5253,16 +5292,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_lea_modrm(env, s, modrm); - tcg_gen_atomic_cmpxchg_tl(oldv, cpu_A0, cmpv, newv, + tcg_gen_atomic_cmpxchg_tl(oldv, s->A0, cmpv, newv, s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(ot, R_EAX, oldv); + gen_op_mov_reg_v(s, ot, R_EAX, oldv); } else { if (mod == 3) { rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(ot, oldv, rm); + gen_op_mov_v_reg(s, ot, oldv, rm); } else { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, oldv, cpu_A0); + gen_op_ld_v(s, ot, oldv, s->A0); rm = 0; /* avoid warning */ } gen_extu(ot, oldv); @@ -5270,19 +5309,19 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* store value = (old == cmp ? new : old); */ tcg_gen_movcond_tl(TCG_COND_EQ, newv, oldv, cmpv, newv, oldv); if (mod == 3) { - gen_op_mov_reg_v(ot, R_EAX, oldv); - gen_op_mov_reg_v(ot, rm, newv); + gen_op_mov_reg_v(s, ot, R_EAX, oldv); + gen_op_mov_reg_v(s, ot, rm, newv); } else { /* Perform an unconditional store cycle like physical cpu; must be before changing accumulator to ensure idempotency if the store faults and the instruction is restarted */ - gen_op_st_v(s, ot, newv, cpu_A0); - gen_op_mov_reg_v(ot, R_EAX, oldv); + gen_op_st_v(s, ot, newv, s->A0); + gen_op_mov_reg_v(s, ot, R_EAX, oldv); } } tcg_gen_mov_tl(cpu_cc_src, oldv); - tcg_gen_mov_tl(cpu_cc_srcT, cmpv); + tcg_gen_mov_tl(s->cc_srcT, cmpv); tcg_gen_sub_tl(cpu_cc_dst, cmpv, oldv); set_cc_op(s, CC_OP_SUBB + ot); tcg_temp_free(oldv); @@ -5301,9 +5340,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; gen_lea_modrm(env, s, modrm); if ((s->prefix & PREFIX_LOCK) && (tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cmpxchg16b(cpu_env, cpu_A0); + gen_helper_cmpxchg16b(cpu_env, s->A0); } else { - gen_helper_cmpxchg16b_unlocked(cpu_env, cpu_A0); + gen_helper_cmpxchg16b_unlocked(cpu_env, s->A0); } } else #endif @@ -5312,9 +5351,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; gen_lea_modrm(env, s, modrm); if ((s->prefix & PREFIX_LOCK) && (tb_cflags(s->base.tb) & CF_PARALLEL)) { - gen_helper_cmpxchg8b(cpu_env, cpu_A0); + gen_helper_cmpxchg8b(cpu_env, s->A0); } else { - gen_helper_cmpxchg8b_unlocked(cpu_env, cpu_A0); + gen_helper_cmpxchg8b_unlocked(cpu_env, s->A0); } } set_cc_op(s, CC_OP_EFLAGS); @@ -5323,14 +5362,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /**************************/ /* push/pop */ case 0x50 ... 0x57: /* push */ - gen_op_mov_v_reg(MO_32, cpu_T0, (b & 7) | REX_B(s)); - gen_push_v(s, cpu_T0); + gen_op_mov_v_reg(s, MO_32, s->T0, (b & 7) | REX_B(s)); + gen_push_v(s, s->T0); break; case 0x58 ... 0x5f: /* pop */ ot = gen_pop_T0(s); /* NOTE: order is important for pop %sp */ gen_pop_update(s, ot); - gen_op_mov_reg_v(ot, (b & 7) | REX_B(s), cpu_T0); + gen_op_mov_reg_v(s, ot, (b & 7) | REX_B(s), s->T0); break; case 0x60: /* pusha */ if (CODE64(s)) @@ -5349,8 +5388,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) val = insn_get(env, s, ot); else val = (int8_t)insn_get(env, s, MO_8); - tcg_gen_movi_tl(cpu_T0, val); - gen_push_v(s, cpu_T0); + tcg_gen_movi_tl(s->T0, val); + gen_push_v(s, s->T0); break; case 0x8f: /* pop Ev */ modrm = x86_ldub_code(env, s); @@ -5360,7 +5399,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* 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_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } else { /* NOTE: order is important too for MMU exceptions */ s->popl_esp_hack = 1 << ot; @@ -5386,13 +5425,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x1e: /* push ds */ if (CODE64(s)) goto illegal_op; - gen_op_movl_T0_seg(b >> 3); - gen_push_v(s, cpu_T0); + gen_op_movl_T0_seg(s, b >> 3); + gen_push_v(s, s->T0); break; case 0x1a0: /* push fs */ case 0x1a8: /* push gs */ - gen_op_movl_T0_seg((b >> 3) & 7); - gen_push_v(s, cpu_T0); + gen_op_movl_T0_seg(s, (b >> 3) & 7); + gen_push_v(s, s->T0); break; case 0x07: /* pop es */ case 0x17: /* pop ss */ @@ -5405,7 +5444,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_pop_update(s, ot); /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ if (s->base.is_jmp) { - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); if (reg == R_SS) { s->tf = 0; gen_eob_inhibit_irq(s, true); @@ -5420,7 +5459,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_movl_seg_T0(s, (b >> 3) & 7); gen_pop_update(s, ot); if (s->base.is_jmp) { - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); } break; @@ -5446,11 +5485,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_lea_modrm(env, s, modrm); } val = insn_get(env, s, ot); - tcg_gen_movi_tl(cpu_T0, val); + tcg_gen_movi_tl(s->T0, val); if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_st_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_reg_v(ot, (modrm & 7) | REX_B(s), cpu_T0); + gen_op_mov_reg_v(s, ot, (modrm & 7) | REX_B(s), s->T0); } break; case 0x8a: @@ -5460,7 +5499,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; case 0x8e: /* mov seg, Gv */ modrm = x86_ldub_code(env, s); @@ -5471,7 +5510,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_movl_seg_T0(s, reg); /* Note that reg == R_SS in gen_movl_seg_T0 always sets is_jmp. */ if (s->base.is_jmp) { - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); if (reg == R_SS) { s->tf = 0; gen_eob_inhibit_irq(s, true); @@ -5486,7 +5525,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) mod = (modrm >> 6) & 3; if (reg >= 6) goto illegal_op; - gen_op_movl_T0_seg(reg); + gen_op_movl_T0_seg(s, reg); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); break; @@ -5512,31 +5551,31 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) rm = (modrm & 7) | REX_B(s); if (mod == 3) { - if (s_ot == MO_SB && byte_reg_is_xH(rm)) { - tcg_gen_sextract_tl(cpu_T0, cpu_regs[rm - 4], 8, 8); + if (s_ot == MO_SB && byte_reg_is_xH(s, rm)) { + tcg_gen_sextract_tl(s->T0, cpu_regs[rm - 4], 8, 8); } else { - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); switch (s_ot) { case MO_UB: - tcg_gen_ext8u_tl(cpu_T0, cpu_T0); + tcg_gen_ext8u_tl(s->T0, s->T0); break; case MO_SB: - tcg_gen_ext8s_tl(cpu_T0, cpu_T0); + tcg_gen_ext8s_tl(s->T0, s->T0); break; case MO_UW: - tcg_gen_ext16u_tl(cpu_T0, cpu_T0); + tcg_gen_ext16u_tl(s->T0, s->T0); break; default: case MO_SW: - tcg_gen_ext16s_tl(cpu_T0, cpu_T0); + tcg_gen_ext16s_tl(s->T0, s->T0); break; } } - gen_op_mov_reg_v(d_ot, reg, cpu_T0); + gen_op_mov_reg_v(s, d_ot, reg, s->T0); } else { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, s_ot, cpu_T0, cpu_A0); - gen_op_mov_reg_v(d_ot, reg, cpu_T0); + gen_op_ld_v(s, s_ot, s->T0, s->A0); + gen_op_mov_reg_v(s, d_ot, reg, s->T0); } } break; @@ -5549,9 +5588,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) reg = ((modrm >> 3) & 7) | rex_r; { AddressParts a = gen_lea_modrm_0(env, s, modrm); - TCGv ea = gen_lea_modrm_1(a); + TCGv ea = gen_lea_modrm_1(s, a); gen_lea_v_seg(s, s->aflag, ea, -1, -1); - gen_op_mov_reg_v(dflag, reg, cpu_A0); + gen_op_mov_reg_v(s, dflag, reg, s->A0); } break; @@ -5573,30 +5612,30 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) offset_addr = insn_get(env, s, s->aflag); break; } - tcg_gen_movi_tl(cpu_A0, offset_addr); + tcg_gen_movi_tl(s->A0, offset_addr); gen_add_A0_ds_seg(s); if ((b & 2) == 0) { - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); - gen_op_mov_reg_v(ot, R_EAX, cpu_T0); + gen_op_ld_v(s, ot, s->T0, s->A0); + gen_op_mov_reg_v(s, ot, R_EAX, s->T0); } else { - gen_op_mov_v_reg(ot, cpu_T0, R_EAX); - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_mov_v_reg(s, ot, s->T0, R_EAX); + gen_op_st_v(s, ot, s->T0, s->A0); } } break; case 0xd7: /* xlat */ - tcg_gen_mov_tl(cpu_A0, cpu_regs[R_EBX]); - 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); + tcg_gen_mov_tl(s->A0, cpu_regs[R_EBX]); + tcg_gen_ext8u_tl(s->T0, cpu_regs[R_EAX]); + tcg_gen_add_tl(s->A0, s->A0, s->T0); + gen_extu(s->aflag, s->A0); gen_add_A0_ds_seg(s); - gen_op_ld_v(s, MO_8, cpu_T0, cpu_A0); - gen_op_mov_reg_v(MO_8, R_EAX, cpu_T0); + gen_op_ld_v(s, MO_8, s->T0, s->A0); + gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); break; case 0xb0 ... 0xb7: /* mov R, Ib */ val = insn_get(env, s, MO_8); - tcg_gen_movi_tl(cpu_T0, val); - gen_op_mov_reg_v(MO_8, (b & 7) | REX_B(s), cpu_T0); + tcg_gen_movi_tl(s->T0, val); + gen_op_mov_reg_v(s, MO_8, (b & 7) | REX_B(s), s->T0); break; case 0xb8 ... 0xbf: /* mov R, Iv */ #ifdef TARGET_X86_64 @@ -5605,16 +5644,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* 64 bit case */ tmp = x86_ldq_code(env, s); reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(cpu_T0, tmp); - gen_op_mov_reg_v(MO_64, reg, cpu_T0); + tcg_gen_movi_tl(s->T0, tmp); + gen_op_mov_reg_v(s, MO_64, reg, s->T0); } else #endif { ot = dflag; val = insn_get(env, s, ot); reg = (b & 7) | REX_B(s); - tcg_gen_movi_tl(cpu_T0, val); - gen_op_mov_reg_v(ot, reg, cpu_T0); + tcg_gen_movi_tl(s->T0, val); + gen_op_mov_reg_v(s, ot, reg, s->T0); } break; @@ -5633,17 +5672,17 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod == 3) { rm = (modrm & 7) | REX_B(s); do_xchg_reg: - 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); + gen_op_mov_v_reg(s, ot, s->T0, reg); + gen_op_mov_v_reg(s, ot, s->T1, rm); + gen_op_mov_reg_v(s, ot, rm, s->T0); + gen_op_mov_reg_v(s, ot, reg, s->T1); } else { gen_lea_modrm(env, s, modrm); - gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_mov_v_reg(s, ot, s->T0, reg); /* for xchg, lock is implicit */ - tcg_gen_atomic_xchg_tl(cpu_T1, cpu_A0, cpu_T0, + tcg_gen_atomic_xchg_tl(s->T1, s->A0, s->T0, s->mem_index, ot | MO_LE); - gen_op_mov_reg_v(ot, reg, cpu_T1); + gen_op_mov_reg_v(s, ot, reg, s->T1); } break; case 0xc4: /* les Gv */ @@ -5670,15 +5709,15 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (mod == 3) goto illegal_op; gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, cpu_T1, cpu_A0); + gen_op_ld_v(s, ot, s->T1, s->A0); gen_add_A0_im(s, 1 << ot); /* load the segment first to handle exceptions properly */ - gen_op_ld_v(s, MO_16, cpu_T0, cpu_A0); + gen_op_ld_v(s, MO_16, s->T0, s->A0); gen_movl_seg_T0(s, op); /* then put the data */ - gen_op_mov_reg_v(ot, reg, cpu_T1); + gen_op_mov_reg_v(s, ot, reg, s->T1); if (s->base.is_jmp) { - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); } break; @@ -5755,7 +5794,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { opreg = rm; } - gen_op_mov_v_reg(ot, cpu_T1, reg); + gen_op_mov_v_reg(s, ot, s->T1, reg); if (shift) { TCGv imm = tcg_const_tl(x86_ldub_code(env, s)); @@ -5793,25 +5832,25 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) switch(op >> 4) { case 0: - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_flds_FT0(cpu_env, cpu_tmp2_i32); + gen_helper_flds_FT0(cpu_env, s->tmp2_i32); break; case 1: - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_fildl_FT0(cpu_env, cpu_tmp2_i32); + gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); break; case 2: - tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); - gen_helper_fldl_FT0(cpu_env, cpu_tmp1_i64); + gen_helper_fldl_FT0(cpu_env, s->tmp1_i64); break; case 3: default: - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LESW); - gen_helper_fildl_FT0(cpu_env, cpu_tmp2_i32); + gen_helper_fildl_FT0(cpu_env, s->tmp2_i32); break; } @@ -5832,25 +5871,25 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0: switch(op >> 4) { case 0: - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_flds_ST0(cpu_env, cpu_tmp2_i32); + gen_helper_flds_ST0(cpu_env, s->tmp2_i32); break; case 1: - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_helper_fildl_ST0(cpu_env, cpu_tmp2_i32); + gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); break; case 2: - tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); - gen_helper_fldl_ST0(cpu_env, cpu_tmp1_i64); + gen_helper_fldl_ST0(cpu_env, s->tmp1_i64); break; case 3: default: - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LESW); - gen_helper_fildl_ST0(cpu_env, cpu_tmp2_i32); + gen_helper_fildl_ST0(cpu_env, s->tmp2_i32); break; } break; @@ -5858,19 +5897,19 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* XXX: the corresponding CPUID bit must be tested ! */ switch(op >> 4) { case 1: - gen_helper_fisttl_ST0(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fisttl_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); break; case 2: - gen_helper_fisttll_ST0(cpu_tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, + gen_helper_fisttll_ST0(s->tmp1_i64, cpu_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); break; case 3: default: - gen_helper_fistt_ST0(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fistt_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); break; } @@ -5879,24 +5918,24 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) default: switch(op >> 4) { case 0: - gen_helper_fsts_ST0(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fsts_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); break; case 1: - gen_helper_fistl_ST0(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fistl_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); break; case 2: - gen_helper_fstl_ST0(cpu_tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, + gen_helper_fstl_ST0(s->tmp1_i64, cpu_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); break; case 3: default: - gen_helper_fist_ST0(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fist_ST0(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); break; } @@ -5906,53 +5945,53 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } break; case 0x0c: /* fldenv mem */ - gen_helper_fldenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); + gen_helper_fldenv(cpu_env, s->A0, tcg_const_i32(dflag - 1)); break; case 0x0d: /* fldcw mem */ - tcg_gen_qemu_ld_i32(cpu_tmp2_i32, cpu_A0, + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); - gen_helper_fldcw(cpu_env, cpu_tmp2_i32); + gen_helper_fldcw(cpu_env, s->tmp2_i32); break; case 0x0e: /* fnstenv mem */ - gen_helper_fstenv(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); + gen_helper_fstenv(cpu_env, s->A0, tcg_const_i32(dflag - 1)); break; case 0x0f: /* fnstcw mem */ - gen_helper_fnstcw(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fnstcw(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); break; case 0x1d: /* fldt mem */ - gen_helper_fldt_ST0(cpu_env, cpu_A0); + gen_helper_fldt_ST0(cpu_env, s->A0); break; case 0x1f: /* fstpt mem */ - gen_helper_fstt_ST0(cpu_env, cpu_A0); + gen_helper_fstt_ST0(cpu_env, s->A0); gen_helper_fpop(cpu_env); break; case 0x2c: /* frstor mem */ - gen_helper_frstor(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); + gen_helper_frstor(cpu_env, s->A0, tcg_const_i32(dflag - 1)); break; case 0x2e: /* fnsave mem */ - gen_helper_fsave(cpu_env, cpu_A0, tcg_const_i32(dflag - 1)); + gen_helper_fsave(cpu_env, s->A0, tcg_const_i32(dflag - 1)); break; case 0x2f: /* fnstsw mem */ - gen_helper_fnstsw(cpu_tmp2_i32, cpu_env); - tcg_gen_qemu_st_i32(cpu_tmp2_i32, cpu_A0, + gen_helper_fnstsw(s->tmp2_i32, cpu_env); + tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUW); break; case 0x3c: /* fbld */ - gen_helper_fbld_ST0(cpu_env, cpu_A0); + gen_helper_fbld_ST0(cpu_env, s->A0); break; case 0x3e: /* fbstp */ - gen_helper_fbst_ST0(cpu_env, cpu_A0); + gen_helper_fbst_ST0(cpu_env, s->A0); gen_helper_fpop(cpu_env); break; case 0x3d: /* fildll */ - tcg_gen_qemu_ld_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); - gen_helper_fildll_ST0(cpu_env, cpu_tmp1_i64); + tcg_gen_qemu_ld_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); + gen_helper_fildll_ST0(cpu_env, s->tmp1_i64); break; case 0x3f: /* fistpll */ - gen_helper_fistll_ST0(cpu_tmp1_i64, cpu_env); - tcg_gen_qemu_st_i64(cpu_tmp1_i64, cpu_A0, s->mem_index, MO_LEQ); + gen_helper_fistll_ST0(s->tmp1_i64, cpu_env); + tcg_gen_qemu_st_i64(s->tmp1_i64, s->A0, s->mem_index, MO_LEQ); gen_helper_fpop(cpu_env); break; default: @@ -6214,9 +6253,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x3c: /* df/4 */ switch(rm) { case 0: - gen_helper_fnstsw(cpu_tmp2_i32, cpu_env); - tcg_gen_extu_i32_tl(cpu_T0, cpu_tmp2_i32); - gen_op_mov_reg_v(MO_16, R_EAX, cpu_T0); + gen_helper_fnstsw(s->tmp2_i32, cpu_env); + tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + gen_op_mov_reg_v(s, MO_16, R_EAX, s->T0); break; default: goto unknown_op; @@ -6326,7 +6365,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x6c: /* insS */ case 0x6d: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(s->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)) { @@ -6341,7 +6380,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x6e: /* outsS */ case 0x6f: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(s->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)) { @@ -6361,16 +6400,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xe5: ot = mo_b_d32(b, dflag); val = x86_ldub_code(env, s); - tcg_gen_movi_tl(cpu_T0, val); + tcg_gen_movi_tl(s->T0, val); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); - } - tcg_gen_movi_i32(cpu_tmp2_i32, val); - 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); + } + tcg_gen_movi_i32(s->tmp2_i32, val); + gen_helper_in_func(ot, s->T1, s->tmp2_i32); + gen_op_mov_reg_v(s, ot, R_EAX, s->T1); + gen_bpt_io(s, s->tmp2_i32, ot); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6380,18 +6419,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xe7: ot = mo_b_d32(b, dflag); val = x86_ldub_code(env, s); - tcg_gen_movi_tl(cpu_T0, val); + tcg_gen_movi_tl(s->T0, val); gen_check_io(s, ot, pc_start - s->cs_base, svm_is_rep(prefixes)); - gen_op_mov_v_reg(ot, cpu_T1, R_EAX); + gen_op_mov_v_reg(s, ot, s->T1, R_EAX); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); - } - tcg_gen_movi_i32(cpu_tmp2_i32, val); - 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); + } + tcg_gen_movi_i32(s->tmp2_i32, val); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + gen_bpt_io(s, s->tmp2_i32, ot); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6400,16 +6439,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xec: case 0xed: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(s->T0, cpu_regs[R_EDX]); gen_check_io(s, ot, pc_start - s->cs_base, SVM_IOIO_TYPE_MASK | svm_is_rep(prefixes)); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); - } - 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); + } + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_in_func(ot, s->T1, s->tmp2_i32); + gen_op_mov_reg_v(s, ot, R_EAX, s->T1); + gen_bpt_io(s, s->tmp2_i32, ot); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6418,18 +6457,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0xee: case 0xef: ot = mo_b_d32(b, dflag); - tcg_gen_ext16u_tl(cpu_T0, cpu_regs[R_EDX]); + tcg_gen_ext16u_tl(s->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_T1, R_EAX); + gen_op_mov_v_reg(s, ot, s->T1, R_EAX); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); - } - 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); + } + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); + gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + gen_bpt_io(s, s->tmp2_i32, ot); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); gen_jmp(s, s->pc - s->cs_base); @@ -6443,37 +6482,37 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_T0); + gen_op_jmp_v(s->T0); gen_bnd_jmp(s); - gen_jr(s, cpu_T0); + gen_jr(s, s->T0); 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_T0); + gen_op_jmp_v(s->T0); gen_bnd_jmp(s); - gen_jr(s, cpu_T0); + gen_jr(s, s->T0); break; case 0xca: /* lret im */ val = x86_ldsw_code(env, s); do_lret: if (s->pe && !s->vm86) { gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_lret_protected(cpu_env, tcg_const_i32(dflag - 1), tcg_const_i32(val)); } else { gen_stack_A0(s); /* pop offset */ - gen_op_ld_v(s, dflag, cpu_T0, cpu_A0); + gen_op_ld_v(s, dflag, s->T0, s->A0); /* NOTE: keeping EIP updated is not a problem in case of exception */ - gen_op_jmp_v(cpu_T0); + gen_op_jmp_v(s->T0); /* pop selector */ 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); + gen_op_ld_v(s, dflag, s->T0, s->A0); + gen_op_movl_seg_T0_vm(s, R_CS); /* add stack offset */ gen_stack_update(s, val + (2 << dflag)); } @@ -6516,8 +6555,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else if (!CODE64(s)) { tval &= 0xffffffff; } - tcg_gen_movi_tl(cpu_T0, next_eip); - gen_push_v(s, cpu_T0); + tcg_gen_movi_tl(s->T0, next_eip); + gen_push_v(s, s->T0); gen_bnd_jmp(s); gen_jmp(s, tval); } @@ -6532,8 +6571,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) offset = insn_get(env, s, ot); selector = insn_get(env, s, MO_16); - tcg_gen_movi_tl(cpu_T0, selector); - tcg_gen_movi_tl(cpu_T1, offset); + tcg_gen_movi_tl(s->T0, selector); + tcg_gen_movi_tl(s->T1, offset); } goto do_lcall; case 0xe9: /* jmp im */ @@ -6561,8 +6600,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) offset = insn_get(env, s, ot); selector = insn_get(env, s, MO_16); - tcg_gen_movi_tl(cpu_T0, selector); - tcg_gen_movi_tl(cpu_T1, offset); + tcg_gen_movi_tl(s->T0, selector); + tcg_gen_movi_tl(s->T1, offset); } goto do_ljmp; case 0xeb: /* jmp Jb */ @@ -6594,7 +6633,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x190 ... 0x19f: /* setcc Gv */ modrm = x86_ldub_code(env, s); - gen_setcc1(s, b, cpu_T0); + gen_setcc1(s, b, s->T0); gen_ldst_modrm(env, s, modrm, MO_8, OR_TMP0, 1); break; case 0x140 ... 0x14f: /* cmov Gv, Ev */ @@ -6615,8 +6654,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { gen_update_cc_op(s); - gen_helper_read_eflags(cpu_T0, cpu_env); - gen_push_v(s, cpu_T0); + gen_helper_read_eflags(s->T0, cpu_env); + gen_push_v(s, s->T0); } break; case 0x9d: /* popf */ @@ -6627,13 +6666,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) ot = gen_pop_T0(s); if (s->cpl == 0) { if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, cpu_T0, + gen_helper_write_eflags(cpu_env, s->T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK))); } else { - gen_helper_write_eflags(cpu_env, cpu_T0, + gen_helper_write_eflags(cpu_env, s->T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK | IOPL_MASK) @@ -6642,14 +6681,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { if (s->cpl <= s->iopl) { if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, cpu_T0, + gen_helper_write_eflags(cpu_env, s->T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK | IF_MASK))); } else { - gen_helper_write_eflags(cpu_env, cpu_T0, + gen_helper_write_eflags(cpu_env, s->T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | @@ -6659,11 +6698,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } } else { if (dflag != MO_16) { - gen_helper_write_eflags(cpu_env, cpu_T0, + gen_helper_write_eflags(cpu_env, s->T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK))); } else { - gen_helper_write_eflags(cpu_env, cpu_T0, + gen_helper_write_eflags(cpu_env, s->T0, tcg_const_i32((TF_MASK | AC_MASK | ID_MASK | NT_MASK) & 0xffff)); @@ -6673,26 +6712,26 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_pop_update(s, ot); set_cc_op(s, CC_OP_EFLAGS); /* abort translation because TF/AC flag may change */ - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); } break; 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_T0, R_AH); + gen_op_mov_v_reg(s, MO_8, s->T0, R_AH); gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, CC_O); - 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); + tcg_gen_andi_tl(s->T0, s->T0, CC_S | CC_Z | CC_A | CC_P | CC_C); + tcg_gen_or_tl(cpu_cc_src, cpu_cc_src, s->T0); break; case 0x9f: /* lahf */ if (CODE64(s) && !(s->cpuid_ext3_features & CPUID_EXT3_LAHF_LM)) goto illegal_op; gen_compute_eflags(s); /* Note: gen_compute_eflags() only gives the condition codes */ - tcg_gen_ori_tl(cpu_T0, cpu_cc_src, 0x02); - gen_op_mov_reg_v(MO_8, R_AH, cpu_T0); + tcg_gen_ori_tl(s->T0, cpu_cc_src, 0x02); + gen_op_mov_reg_v(s, MO_8, R_AH, s->T0); break; case 0xf5: /* cmc */ gen_compute_eflags(s); @@ -6707,12 +6746,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) tcg_gen_ori_tl(cpu_cc_src, cpu_cc_src, CC_C); break; case 0xfc: /* cld */ - tcg_gen_movi_i32(cpu_tmp2_i32, 1); - tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_movi_i32(s->tmp2_i32, 1); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); break; case 0xfd: /* std */ - tcg_gen_movi_i32(cpu_tmp2_i32, -1); - tcg_gen_st_i32(cpu_tmp2_i32, cpu_env, offsetof(CPUX86State, df)); + tcg_gen_movi_i32(s->tmp2_i32, -1); + tcg_gen_st_i32(s->tmp2_i32, cpu_env, offsetof(CPUX86State, df)); break; /************************/ @@ -6727,14 +6766,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) s->rip_offset = 1; gen_lea_modrm(env, s, modrm); if (!(s->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } } else { - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); } /* load shift */ val = x86_ldub_code(env, s); - tcg_gen_movi_tl(cpu_T1, val); + tcg_gen_movi_tl(s->T1, val); if (op < 4) goto unknown_op; op -= 4; @@ -6756,70 +6795,70 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) reg = ((modrm >> 3) & 7) | rex_r; mod = (modrm >> 6) & 3; rm = (modrm & 7) | REX_B(s); - gen_op_mov_v_reg(MO_32, cpu_T1, reg); + gen_op_mov_v_reg(s, MO_32, s->T1, reg); if (mod != 3) { AddressParts a = gen_lea_modrm_0(env, s, modrm); /* specific case: we need to add a displacement */ - 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, gen_lea_modrm_1(a), cpu_tmp0); - gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + gen_exts(ot, s->T1); + tcg_gen_sari_tl(s->tmp0, s->T1, 3 + ot); + tcg_gen_shli_tl(s->tmp0, s->tmp0, ot); + tcg_gen_add_tl(s->A0, gen_lea_modrm_1(s, a), s->tmp0); + gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); if (!(s->prefix & PREFIX_LOCK)) { - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); } } else { - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); } bt_op: - tcg_gen_andi_tl(cpu_T1, cpu_T1, (1 << (3 + ot)) - 1); - tcg_gen_movi_tl(cpu_tmp0, 1); - tcg_gen_shl_tl(cpu_tmp0, cpu_tmp0, cpu_T1); + tcg_gen_andi_tl(s->T1, s->T1, (1 << (3 + ot)) - 1); + tcg_gen_movi_tl(s->tmp0, 1); + tcg_gen_shl_tl(s->tmp0, s->tmp0, s->T1); if (s->prefix & PREFIX_LOCK) { switch (op) { case 0: /* bt */ /* Needs no atomic ops; we surpressed the normal memory load for LOCK above so do it now. */ - gen_op_ld_v(s, ot, cpu_T0, cpu_A0); + gen_op_ld_v(s, ot, s->T0, s->A0); break; case 1: /* bts */ - tcg_gen_atomic_fetch_or_tl(cpu_T0, cpu_A0, cpu_tmp0, + tcg_gen_atomic_fetch_or_tl(s->T0, s->A0, s->tmp0, s->mem_index, ot | MO_LE); break; case 2: /* btr */ - tcg_gen_not_tl(cpu_tmp0, cpu_tmp0); - tcg_gen_atomic_fetch_and_tl(cpu_T0, cpu_A0, cpu_tmp0, + tcg_gen_not_tl(s->tmp0, s->tmp0); + tcg_gen_atomic_fetch_and_tl(s->T0, s->A0, s->tmp0, s->mem_index, ot | MO_LE); break; default: case 3: /* btc */ - tcg_gen_atomic_fetch_xor_tl(cpu_T0, cpu_A0, cpu_tmp0, + tcg_gen_atomic_fetch_xor_tl(s->T0, s->A0, s->tmp0, s->mem_index, ot | MO_LE); break; } - tcg_gen_shr_tl(cpu_tmp4, cpu_T0, cpu_T1); + tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); } else { - tcg_gen_shr_tl(cpu_tmp4, cpu_T0, cpu_T1); + tcg_gen_shr_tl(s->tmp4, s->T0, s->T1); switch (op) { case 0: /* bt */ /* Data already loaded; nothing to do. */ break; case 1: /* bts */ - tcg_gen_or_tl(cpu_T0, cpu_T0, cpu_tmp0); + tcg_gen_or_tl(s->T0, s->T0, s->tmp0); break; case 2: /* btr */ - tcg_gen_andc_tl(cpu_T0, cpu_T0, cpu_tmp0); + tcg_gen_andc_tl(s->T0, s->T0, s->tmp0); break; default: case 3: /* btc */ - tcg_gen_xor_tl(cpu_T0, cpu_T0, cpu_tmp0); + tcg_gen_xor_tl(s->T0, s->T0, s->tmp0); break; } if (op != 0) { if (mod != 3) { - gen_op_st_v(s, ot, cpu_T0, cpu_A0); + gen_op_st_v(s, ot, s->T0, s->A0); } else { - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_op_mov_reg_v(s, ot, rm, s->T0); } } } @@ -6843,13 +6882,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) We can get that same Z value (and the new C value) by leaving CC_DST alone, setting CC_SRC, and using a CC_OP_SAR of the same width. */ - tcg_gen_mov_tl(cpu_cc_src, cpu_tmp4); + tcg_gen_mov_tl(cpu_cc_src, s->tmp4); set_cc_op(s, ((s->cc_op - CC_OP_MULB) & 3) + CC_OP_SARB); break; default: /* Otherwise, generate EFLAGS and replace the C bit. */ gen_compute_eflags(s); - tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, cpu_tmp4, + tcg_gen_deposit_tl(cpu_cc_src, cpu_cc_src, s->tmp4, ctz32(CC_C), 1); break; } @@ -6860,7 +6899,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) modrm = x86_ldub_code(env, s); reg = ((modrm >> 3) & 7) | rex_r; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, cpu_T0); + gen_extu(ot, s->T0); /* Note that lzcnt and tzcnt are in different extensions. */ if ((prefixes & PREFIX_REPZ) @@ -6869,23 +6908,23 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) : s->cpuid_7_0_ebx_features & CPUID_7_0_EBX_BMI1)) { int size = 8 << ot; /* For lzcnt/tzcnt, C bit is defined related to the input. */ - tcg_gen_mov_tl(cpu_cc_src, cpu_T0); + tcg_gen_mov_tl(cpu_cc_src, s->T0); if (b & 1) { /* For lzcnt, reduce the target_ulong result by the number of zeros that we expect to find at the top. */ - tcg_gen_clzi_tl(cpu_T0, cpu_T0, TARGET_LONG_BITS); - tcg_gen_subi_tl(cpu_T0, cpu_T0, TARGET_LONG_BITS - size); + tcg_gen_clzi_tl(s->T0, s->T0, TARGET_LONG_BITS); + tcg_gen_subi_tl(s->T0, s->T0, TARGET_LONG_BITS - size); } else { /* For tzcnt, a zero input must return the operand size. */ - tcg_gen_ctzi_tl(cpu_T0, cpu_T0, size); + tcg_gen_ctzi_tl(s->T0, s->T0, size); } /* For lzcnt/tzcnt, Z bit is defined related to the result. */ - gen_op_update1_cc(); + gen_op_update1_cc(s); set_cc_op(s, CC_OP_BMILGB + ot); } 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_T0); + tcg_gen_mov_tl(cpu_cc_dst, s->T0); set_cc_op(s, CC_OP_LOGICB + ot); /* ??? The manual says that the output is undefined when the @@ -6895,14 +6934,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (b & 1) { /* For bsr, return the bit index of the first 1 bit, not the count of leading zeros. */ - tcg_gen_xori_tl(cpu_T1, cpu_regs[reg], TARGET_LONG_BITS - 1); - tcg_gen_clz_tl(cpu_T0, cpu_T0, cpu_T1); - tcg_gen_xori_tl(cpu_T0, cpu_T0, TARGET_LONG_BITS - 1); + tcg_gen_xori_tl(s->T1, cpu_regs[reg], TARGET_LONG_BITS - 1); + tcg_gen_clz_tl(s->T0, s->T0, s->T1); + tcg_gen_xori_tl(s->T0, s->T0, TARGET_LONG_BITS - 1); } else { - tcg_gen_ctz_tl(cpu_T0, cpu_T0, cpu_regs[reg]); + tcg_gen_ctz_tl(s->T0, s->T0, cpu_regs[reg]); } } - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); break; /************************/ /* bcd */ @@ -6965,7 +7004,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } if (prefixes & PREFIX_REPZ) { gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_pause(cpu_env, tcg_const_i32(s->pc - pc_start)); s->base.is_jmp = DISAS_NORETURN; } @@ -6993,19 +7032,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (CODE64(s)) goto illegal_op; gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_into(cpu_env, tcg_const_i32(s->pc - pc_start)); break; #ifdef WANT_ICEBP case 0xf1: /* icebp (undocumented, exits to external debugger) */ gen_svm_check_intercept(s, pc_start, SVM_EXIT_ICEBP); -#if 1 gen_debug(s, pc_start - s->cs_base); -#else - /* start debug */ - tb_flush(CPU(x86_env_get_cpu(env))); - qemu_set_log(CPU_LOG_INT | CPU_LOG_TB_IN_ASM); -#endif break; #endif case 0xfa: /* cli */ @@ -7027,7 +7060,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_jmp_im(s, s->pc - s->cs_base); gen_eob_inhibit_irq(s, true); } else { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); @@ -7042,37 +7075,37 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) mod = (modrm >> 6) & 3; if (mod == 3) goto illegal_op; - gen_op_mov_v_reg(ot, cpu_T0, reg); + gen_op_mov_v_reg(s, ot, s->T0, reg); gen_lea_modrm(env, s, modrm); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); if (ot == MO_16) { - gen_helper_boundw(cpu_env, cpu_A0, cpu_tmp2_i32); + gen_helper_boundw(cpu_env, s->A0, s->tmp2_i32); } else { - gen_helper_boundl(cpu_env, cpu_A0, cpu_tmp2_i32); + gen_helper_boundl(cpu_env, s->A0, s->tmp2_i32); } break; case 0x1c8 ... 0x1cf: /* bswap reg */ reg = (b & 7) | REX_B(s); #ifdef TARGET_X86_64 if (dflag == MO_64) { - 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); + gen_op_mov_v_reg(s, MO_64, s->T0, reg); + tcg_gen_bswap64_i64(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_64, reg, s->T0); } else #endif { - 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); + gen_op_mov_v_reg(s, MO_32, s->T0, reg); + tcg_gen_ext32u_tl(s->T0, s->T0); + tcg_gen_bswap32_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_32, reg, s->T0); } break; case 0xd6: /* salc */ if (CODE64(s)) goto illegal_op; - 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); + gen_compute_eflags_c(s, s->T0); + tcg_gen_neg_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, MO_8, R_EAX, s->T0); break; case 0xe0: /* loopnz */ case 0xe1: /* loopz */ @@ -7095,26 +7128,26 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) switch(b) { case 0: /* loopnz */ case 1: /* loopz */ - gen_op_add_reg_im(s->aflag, R_ECX, -1); - gen_op_jz_ecx(s->aflag, l3); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jz_ecx(s, s->aflag, l3); gen_jcc1(s, (JCC_Z << 1) | (b ^ 1), l1); break; case 2: /* loop */ - gen_op_add_reg_im(s->aflag, R_ECX, -1); - gen_op_jnz_ecx(s->aflag, l1); + gen_op_add_reg_im(s, s->aflag, R_ECX, -1); + gen_op_jnz_ecx(s, s->aflag, l1); break; default: case 3: /* jcxz */ - gen_op_jz_ecx(s->aflag, l1); + gen_op_jz_ecx(s, s->aflag, l1); break; } gen_set_label(l3); - gen_jmp_im(next_eip); + gen_jmp_im(s, next_eip); tcg_gen_br(l2); gen_set_label(l1); - gen_jmp_im(tval); + gen_jmp_im(s, tval); gen_set_label(l2); gen_eob(s); } @@ -7125,7 +7158,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_jmp_im(s, pc_start - s->cs_base); if (b & 2) { gen_helper_rdmsr(cpu_env); } else { @@ -7135,10 +7168,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; case 0x131: /* rdtsc */ gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); - } + } gen_helper_rdtsc(cpu_env); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); @@ -7147,7 +7180,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; case 0x133: /* rdpmc */ gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_rdpmc(cpu_env); break; case 0x134: /* sysenter */ @@ -7176,7 +7209,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 0x105: /* syscall */ /* XXX: is it usable in real mode ? */ gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_syscall(cpu_env, tcg_const_i32(s->pc - pc_start)); /* TF handling for the syscall insn is different. The TF bit is checked after the syscall insn completes. This allows #DB to not be @@ -7202,7 +7235,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) #endif case 0x1a2: /* cpuid */ gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_cpuid(cpu_env); break; case 0xf4: /* hlt */ @@ -7210,7 +7243,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_jmp_im(s, pc_start - s->cs_base); gen_helper_hlt(cpu_env, tcg_const_i32(s->pc - pc_start)); s->base.is_jmp = DISAS_NORETURN; } @@ -7224,7 +7257,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!s->pe || s->vm86) goto illegal_op; gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_READ); - tcg_gen_ld32u_tl(cpu_T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, ldt.selector)); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); @@ -7237,15 +7270,15 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_LDTR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_lldt(cpu_env, cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_lldt(cpu_env, s->tmp2_i32); } break; case 1: /* str */ if (!s->pe || s->vm86) goto illegal_op; gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_READ); - tcg_gen_ld32u_tl(cpu_T0, cpu_env, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, tr.selector)); ot = mod == 3 ? dflag : MO_16; gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 1); @@ -7258,8 +7291,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_TR_WRITE); gen_ldst_modrm(env, s, modrm, MO_16, OR_TMP0, 0); - tcg_gen_trunc_tl_i32(cpu_tmp2_i32, cpu_T0); - gen_helper_ltr(cpu_env, cpu_tmp2_i32); + tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); + gen_helper_ltr(cpu_env, s->tmp2_i32); } break; case 4: /* verr */ @@ -7269,9 +7302,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_T0); + gen_helper_verr(cpu_env, s->T0); } else { - gen_helper_verw(cpu_env, cpu_T0); + gen_helper_verw(cpu_env, s->T0); } set_cc_op(s, CC_OP_EFLAGS); break; @@ -7286,15 +7319,15 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_T0, + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.limit)); - gen_op_st_v(s, MO_16, cpu_T0, cpu_A0); + gen_op_st_v(s, MO_16, s->T0, s->A0); gen_add_A0_im(s, 2); - tcg_gen_ld_tl(cpu_T0, cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); } - gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); break; case 0xc8: /* monitor */ @@ -7302,11 +7335,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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_jmp_im(s, pc_start - s->cs_base); + tcg_gen_mov_tl(s->A0, cpu_regs[R_EAX]); + gen_extu(s->aflag, s->A0); gen_add_A0_ds_seg(s); - gen_helper_monitor(cpu_env, cpu_A0); + gen_helper_monitor(cpu_env, s->A0); break; case 0xc9: /* mwait */ @@ -7314,7 +7347,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_mwait(cpu_env, tcg_const_i32(s->pc - pc_start)); gen_eob(s); break; @@ -7325,7 +7358,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_helper_clac(cpu_env); - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; @@ -7335,21 +7368,21 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_helper_stac(cpu_env); - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; 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); + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.limit)); + gen_op_st_v(s, MO_16, s->T0, s->A0); gen_add_A0_im(s, 2); - tcg_gen_ld_tl(cpu_T0, cpu_env, offsetof(CPUX86State, idt.base)); + tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + tcg_gen_andi_tl(s->T0, s->T0, 0xffffff); } - gen_op_st_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + gen_op_st_v(s, CODE64(s) + MO_32, s->T0, s->A0); break; case 0xd0: /* xgetbv */ @@ -7358,9 +7391,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) | 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); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xgetbv(s->tmp1_i64, cpu_env, s->tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->tmp1_i64); break; case 0xd1: /* xsetbv */ @@ -7373,12 +7406,12 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); break; } - tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + tcg_gen_concat_tl_i64(s->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); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_xsetbv(cpu_env, s->tmp2_i32, s->tmp1_i64); /* End TB because translation flags may change. */ - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; @@ -7391,7 +7424,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, 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(NULL, 0); @@ -7403,7 +7436,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_vmmcall(cpu_env); break; @@ -7416,7 +7449,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_vmload(cpu_env, tcg_const_i32(s->aflag - 1)); break; @@ -7429,7 +7462,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_vmsave(cpu_env, tcg_const_i32(s->aflag - 1)); break; @@ -7445,7 +7478,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } gen_update_cc_op(s); gen_helper_stgi(cpu_env); - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; @@ -7458,7 +7491,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_clgi(cpu_env); break; @@ -7469,7 +7502,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_skinit(cpu_env); break; @@ -7482,7 +7515,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); gen_helper_invlpga(cpu_env, tcg_const_i32(s->aflag - 1)); break; @@ -7493,14 +7526,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } 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_op_ld_v(s, MO_16, s->T1, s->A0); gen_add_A0_im(s, 2); - gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + tcg_gen_andi_tl(s->T0, s->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)); + tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, gdt.base)); + tcg_gen_st32_tl(s->T1, cpu_env, offsetof(CPUX86State, gdt.limit)); break; CASE_MODRM_MEM_OP(3): /* lidt */ @@ -7510,19 +7543,19 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } 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_op_ld_v(s, MO_16, s->T1, s->A0); gen_add_A0_im(s, 2); - gen_op_ld_v(s, CODE64(s) + MO_32, cpu_T0, cpu_A0); + gen_op_ld_v(s, CODE64(s) + MO_32, s->T0, s->A0); if (dflag == MO_16) { - tcg_gen_andi_tl(cpu_T0, cpu_T0, 0xffffff); + tcg_gen_andi_tl(s->T0, s->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)); + tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, idt.base)); + tcg_gen_st32_tl(s->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])); + tcg_gen_ld_tl(s->T0, cpu_env, offsetof(CPUX86State, cr[0])); if (CODE64(s)) { mod = (modrm >> 6) & 3; ot = (mod != 3 ? MO_16 : s->dflag); @@ -7535,18 +7568,18 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_rdpkru(s->tmp1_i64, cpu_env, s->tmp2_i32); + tcg_gen_extr_i64_tl(cpu_regs[R_EAX], cpu_regs[R_EDX], s->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], + tcg_gen_concat_tl_i64(s->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); + tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_ECX]); + gen_helper_wrpkru(cpu_env, s->tmp2_i32, s->tmp1_i64); break; CASE_MODRM_OP(6): /* lmsw */ if (s->cpl != 0) { @@ -7555,8 +7588,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } 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_helper_lmsw(cpu_env, s->T0); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; @@ -7566,10 +7599,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, 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_helper_invlpg(cpu_env, s->A0); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; @@ -7579,10 +7612,10 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (s->cpl != 0) { gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); } else { - tcg_gen_mov_tl(cpu_T0, cpu_seg_base[R_GS]); + tcg_gen_mov_tl(s->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, + tcg_gen_st_tl(s->T0, cpu_env, offsetof(CPUX86State, kernelgsbase)); } break; @@ -7595,7 +7628,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } @@ -7633,16 +7666,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) rm = (modrm & 7) | REX_B(s); if (mod == 3) { - gen_op_mov_v_reg(MO_32, cpu_T0, rm); + gen_op_mov_v_reg(s, MO_32, s->T0, rm); /* sign extend */ if (d_ot == MO_64) { - tcg_gen_ext32s_tl(cpu_T0, cpu_T0); + tcg_gen_ext32s_tl(s->T0, s->T0); } - gen_op_mov_reg_v(d_ot, reg, cpu_T0); + gen_op_mov_reg_v(s, d_ot, reg, s->T0); } else { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, MO_32 | MO_SIGN, cpu_T0, cpu_A0); - gen_op_mov_reg_v(d_ot, reg, cpu_T0); + gen_op_ld_v(s, MO_32 | MO_SIGN, s->T0, s->A0); + gen_op_mov_reg_v(s, d_ot, reg, s->T0); } } else #endif @@ -7662,19 +7695,19 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) rm = modrm & 7; if (mod != 3) { gen_lea_modrm(env, s, modrm); - gen_op_ld_v(s, ot, t0, cpu_A0); + gen_op_ld_v(s, ot, t0, s->A0); a0 = tcg_temp_local_new(); - tcg_gen_mov_tl(a0, cpu_A0); + tcg_gen_mov_tl(a0, s->A0); } else { - gen_op_mov_v_reg(ot, t0, rm); + gen_op_mov_v_reg(s, ot, t0, rm); a0 = NULL; } - gen_op_mov_v_reg(ot, t1, reg); - tcg_gen_andi_tl(cpu_tmp0, t0, 3); + gen_op_mov_v_reg(s, ot, t1, reg); + tcg_gen_andi_tl(s->tmp0, t0, 3); tcg_gen_andi_tl(t1, t1, 3); tcg_gen_movi_tl(t2, 0); label1 = gen_new_label(); - tcg_gen_brcond_tl(TCG_COND_GE, cpu_tmp0, t1, label1); + tcg_gen_brcond_tl(TCG_COND_GE, s->tmp0, t1, label1); tcg_gen_andi_tl(t0, t0, ~3); tcg_gen_or_tl(t0, t0, t1); tcg_gen_movi_tl(t2, CC_Z); @@ -7683,7 +7716,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_op_st_v(s, ot, t0, a0); tcg_temp_free(a0); } else { - gen_op_mov_reg_v(ot, rm, t0); + gen_op_mov_reg_v(s, ot, rm, t0); } gen_compute_eflags(s); tcg_gen_andi_tl(cpu_cc_src, cpu_cc_src, ~CC_Z); @@ -7707,14 +7740,14 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) t0 = tcg_temp_local_new(); gen_update_cc_op(s); if (b == 0x102) { - gen_helper_lar(t0, cpu_env, cpu_T0); + gen_helper_lar(t0, cpu_env, s->T0); } else { - gen_helper_lsl(t0, cpu_env, cpu_T0); + gen_helper_lsl(t0, cpu_env, s->T0); } - tcg_gen_andi_tl(cpu_tmp0, cpu_cc_src, CC_Z); + tcg_gen_andi_tl(s->tmp0, cpu_cc_src, CC_Z); label1 = gen_new_label(); - tcg_gen_brcondi_tl(TCG_COND_EQ, cpu_tmp0, 0, label1); - gen_op_mov_reg_v(ot, reg, t0); + tcg_gen_brcondi_tl(TCG_COND_EQ, s->tmp0, 0, label1); + gen_op_mov_reg_v(s, ot, reg, t0); gen_set_label(label1); set_cc_op(s, CC_OP_EFLAGS); tcg_temp_free(t0); @@ -7780,16 +7813,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { gen_lea_modrm(env, s, modrm); if (CODE64(s)) { - tcg_gen_qemu_ld_i64(cpu_bndl[reg], cpu_A0, + tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->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, + tcg_gen_addi_tl(s->A0, s->A0, 8); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], s->A0, s->mem_index, MO_LEQ); } else { - tcg_gen_qemu_ld_i64(cpu_bndl[reg], cpu_A0, + tcg_gen_qemu_ld_i64(cpu_bndl[reg], s->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, + tcg_gen_addi_tl(s->A0, s->A0, 4); + tcg_gen_qemu_ld_i64(cpu_bndu[reg], s->A0, s->mem_index, MO_LEUL); } /* bnd registers are now in-use */ @@ -7805,22 +7838,22 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } if (a.base >= 0) { - tcg_gen_addi_tl(cpu_A0, cpu_regs[a.base], a.disp); + tcg_gen_addi_tl(s->A0, cpu_regs[a.base], a.disp); } else { - tcg_gen_movi_tl(cpu_A0, 0); + tcg_gen_movi_tl(s->A0, 0); } - gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); if (a.index >= 0) { - tcg_gen_mov_tl(cpu_T0, cpu_regs[a.index]); + tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); } else { - tcg_gen_movi_tl(cpu_T0, 0); + tcg_gen_movi_tl(s->T0, 0); } if (CODE64(s)) { - gen_helper_bndldx64(cpu_bndl[reg], cpu_env, cpu_A0, cpu_T0); + gen_helper_bndldx64(cpu_bndl[reg], cpu_env, s->A0, s->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); + gen_helper_bndldx32(cpu_bndu[reg], cpu_env, s->A0, s->T0); tcg_gen_ext32u_i64(cpu_bndl[reg], cpu_bndu[reg]); tcg_gen_shri_i64(cpu_bndu[reg], cpu_bndu[reg], 32); } @@ -7854,11 +7887,11 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) /* rip-relative generates #ud */ goto illegal_op; } - tcg_gen_not_tl(cpu_A0, gen_lea_modrm_1(a)); + tcg_gen_not_tl(s->A0, gen_lea_modrm_1(s, a)); if (!CODE64(s)) { - tcg_gen_ext32u_tl(cpu_A0, cpu_A0); + tcg_gen_ext32u_tl(s->A0, s->A0); } - tcg_gen_extu_tl_i64(cpu_bndu[reg], cpu_A0); + tcg_gen_extu_tl_i64(cpu_bndu[reg], s->A0); /* bnd registers are now in-use */ gen_set_hflag(s, HF_MPX_IU_MASK); break; @@ -7887,16 +7920,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } else { gen_lea_modrm(env, s, modrm); if (CODE64(s)) { - tcg_gen_qemu_st_i64(cpu_bndl[reg], cpu_A0, + tcg_gen_qemu_st_i64(cpu_bndl[reg], s->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, + tcg_gen_addi_tl(s->A0, s->A0, 8); + tcg_gen_qemu_st_i64(cpu_bndu[reg], s->A0, s->mem_index, MO_LEQ); } else { - tcg_gen_qemu_st_i64(cpu_bndl[reg], cpu_A0, + tcg_gen_qemu_st_i64(cpu_bndl[reg], s->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, + tcg_gen_addi_tl(s->A0, s->A0, 4); + tcg_gen_qemu_st_i64(cpu_bndu[reg], s->A0, s->mem_index, MO_LEUL); } } @@ -7910,21 +7943,21 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } if (a.base >= 0) { - tcg_gen_addi_tl(cpu_A0, cpu_regs[a.base], a.disp); + tcg_gen_addi_tl(s->A0, cpu_regs[a.base], a.disp); } else { - tcg_gen_movi_tl(cpu_A0, 0); + tcg_gen_movi_tl(s->A0, 0); } - gen_lea_v_seg(s, s->aflag, cpu_A0, a.def_seg, s->override); + gen_lea_v_seg(s, s->aflag, s->A0, a.def_seg, s->override); if (a.index >= 0) { - tcg_gen_mov_tl(cpu_T0, cpu_regs[a.index]); + tcg_gen_mov_tl(s->T0, cpu_regs[a.index]); } else { - tcg_gen_movi_tl(cpu_T0, 0); + tcg_gen_movi_tl(s->T0, 0); } if (CODE64(s)) { - gen_helper_bndstx64(cpu_env, cpu_A0, cpu_T0, + gen_helper_bndstx64(cpu_env, s->A0, s->T0, cpu_bndl[reg], cpu_bndu[reg]); } else { - gen_helper_bndstx32(cpu_env, cpu_A0, cpu_T0, + gen_helper_bndstx32(cpu_env, s->A0, s->T0, cpu_bndl[reg], cpu_bndu[reg]); } } @@ -7963,25 +7996,25 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) case 4: case 8: gen_update_cc_op(s); - gen_jmp_im(pc_start - s->cs_base); + gen_jmp_im(s, pc_start - s->cs_base); if (b & 2) { if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } - gen_op_mov_v_reg(ot, cpu_T0, rm); + gen_op_mov_v_reg(s, ot, s->T0, rm); gen_helper_write_crN(cpu_env, tcg_const_i32(reg), - cpu_T0); + s->T0); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); } else { if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_start(); } - gen_helper_read_crN(cpu_T0, cpu_env, tcg_const_i32(reg)); - gen_op_mov_reg_v(ot, rm, cpu_T0); + gen_helper_read_crN(s->T0, cpu_env, tcg_const_i32(reg)); + gen_op_mov_reg_v(s, ot, rm, s->T0); if (tb_cflags(s->base.tb) & CF_USE_ICOUNT) { gen_io_end(); } @@ -8014,16 +8047,16 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } if (b & 2) { gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_DR0 + reg); - 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_op_mov_v_reg(s, ot, s->T0, rm); + tcg_gen_movi_i32(s->tmp2_i32, reg); + gen_helper_set_dr(cpu_env, s->tmp2_i32, s->T0); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); } else { gen_svm_check_intercept(s, pc_start, SVM_EXIT_READ_DR0 + reg); - 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); + tcg_gen_movi_i32(s->tmp2_i32, reg); + gen_helper_get_dr(s->T0, cpu_env, s->tmp2_i32); + gen_op_mov_reg_v(s, ot, rm, s->T0); } } break; @@ -8034,7 +8067,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) gen_svm_check_intercept(s, pc_start, SVM_EXIT_WRITE_CR0); gen_helper_clts(cpu_env); /* abort block because static cpu state changed */ - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_eob(s); } break; @@ -8064,7 +8097,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_lea_modrm(env, s, modrm); - gen_helper_fxsave(cpu_env, cpu_A0); + gen_helper_fxsave(cpu_env, s->A0); break; CASE_MODRM_MEM_OP(1): /* fxrstor */ @@ -8077,7 +8110,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_lea_modrm(env, s, modrm); - gen_helper_fxrstor(cpu_env, cpu_A0); + gen_helper_fxrstor(cpu_env, s->A0); break; CASE_MODRM_MEM_OP(2): /* ldmxcsr */ @@ -8089,8 +8122,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } 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); + tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + gen_helper_ldmxcsr(cpu_env, s->tmp2_i32); break; CASE_MODRM_MEM_OP(3): /* stmxcsr */ @@ -8102,8 +8135,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) break; } gen_lea_modrm(env, s, modrm); - tcg_gen_ld32u_tl(cpu_T0, cpu_env, offsetof(CPUX86State, mxcsr)); - gen_op_st_v(s, MO_32, cpu_T0, cpu_A0); + tcg_gen_ld32u_tl(s->T0, cpu_env, offsetof(CPUX86State, mxcsr)); + gen_op_st_v(s, MO_32, s->T0, s->A0); break; CASE_MODRM_MEM_OP(4): /* xsave */ @@ -8113,9 +8146,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); - gen_helper_xsave(cpu_env, cpu_A0, cpu_tmp1_i64); + gen_helper_xsave(cpu_env, s->A0, s->tmp1_i64); break; CASE_MODRM_MEM_OP(5): /* xrstor */ @@ -8125,13 +8158,13 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); - gen_helper_xrstor(cpu_env, cpu_A0, cpu_tmp1_i64); + gen_helper_xrstor(cpu_env, s->A0, s->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_jmp_im(s, s->pc - s->cs_base); gen_eob(s); break; @@ -8153,9 +8186,9 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) goto illegal_op; } gen_lea_modrm(env, s, modrm); - tcg_gen_concat_tl_i64(cpu_tmp1_i64, cpu_regs[R_EAX], + tcg_gen_concat_tl_i64(s->tmp1_i64, cpu_regs[R_EAX], cpu_regs[R_EDX]); - gen_helper_xsaveopt(cpu_env, cpu_A0, cpu_tmp1_i64); + gen_helper_xsaveopt(cpu_env, s->A0, s->tmp1_i64); } break; @@ -8189,8 +8222,8 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) 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); + tcg_gen_movi_i32(s->tmp2_i32, CR4_FSGSBASE_MASK); + gen_helper_cr4_testbit(cpu_env, s->tmp2_i32); base = cpu_seg_base[modrm & 8 ? R_GS : R_FS]; treg = cpu_regs[(modrm & 7) | REX_B(s)]; @@ -8261,7 +8294,7 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) if (!(s->flags & HF_SMM_MASK)) goto illegal_op; gen_update_cc_op(s); - gen_jmp_im(s->pc - s->cs_base); + gen_jmp_im(s, s->pc - s->cs_base); gen_helper_rsm(cpu_env); gen_eob(s); break; @@ -8282,16 +8315,17 @@ static target_ulong disas_insn(DisasContext *s, CPUState *cpu) } gen_ldst_modrm(env, s, modrm, ot, OR_TMP0, 0); - gen_extu(ot, cpu_T0); - tcg_gen_mov_tl(cpu_cc_src, cpu_T0); - tcg_gen_ctpop_tl(cpu_T0, cpu_T0); - gen_op_mov_reg_v(ot, reg, cpu_T0); + gen_extu(ot, s->T0); + tcg_gen_mov_tl(cpu_cc_src, s->T0); + tcg_gen_ctpop_tl(s->T0, s->T0); + gen_op_mov_reg_v(s, ot, reg, s->T0); set_cc_op(s, CC_OP_POPCNT); break; case 0x10e ... 0x10f: /* 3DNow! instructions, ignore prefixes */ s->prefix &= ~(PREFIX_REPZ | PREFIX_REPNZ | PREFIX_DATA); + /* fall through */ case 0x110 ... 0x117: case 0x128 ... 0x12f: case 0x138 ... 0x13a: @@ -8450,18 +8484,18 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) printf("ERROR addseg\n"); #endif - cpu_T0 = tcg_temp_new(); - cpu_T1 = tcg_temp_new(); - cpu_A0 = tcg_temp_new(); + dc->T0 = tcg_temp_new(); + dc->T1 = tcg_temp_new(); + dc->A0 = tcg_temp_new(); - cpu_tmp0 = tcg_temp_new(); - cpu_tmp1_i64 = tcg_temp_new_i64(); - cpu_tmp2_i32 = tcg_temp_new_i32(); - cpu_tmp3_i32 = tcg_temp_new_i32(); - cpu_tmp4 = tcg_temp_new(); - cpu_ptr0 = tcg_temp_new_ptr(); - cpu_ptr1 = tcg_temp_new_ptr(); - cpu_cc_srcT = tcg_temp_local_new(); + dc->tmp0 = tcg_temp_new(); + dc->tmp1_i64 = tcg_temp_new_i64(); + dc->tmp2_i32 = tcg_temp_new_i32(); + dc->tmp3_i32 = tcg_temp_new_i32(); + dc->tmp4 = tcg_temp_new(); + dc->ptr0 = tcg_temp_new_ptr(); + dc->ptr1 = tcg_temp_new_ptr(); + dc->cc_srcT = tcg_temp_local_new(); } static void i386_tr_tb_start(DisasContextBase *db, CPUState *cpu) @@ -8508,10 +8542,10 @@ static void i386_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) chance to happen */ dc->base.is_jmp = DISAS_TOO_MANY; } else if ((tb_cflags(dc->base.tb) & CF_USE_ICOUNT) - && ((dc->base.pc_next & TARGET_PAGE_MASK) - != ((dc->base.pc_next + TARGET_MAX_INSN_SIZE - 1) + && ((pc_next & TARGET_PAGE_MASK) + != ((pc_next + TARGET_MAX_INSN_SIZE - 1) & TARGET_PAGE_MASK) - || (dc->base.pc_next & ~TARGET_PAGE_MASK) == 0)) { + || (pc_next & ~TARGET_PAGE_MASK) == 0)) { /* Do not cross the boundary of the pages in icount mode, it can cause an exception. Do it only when boundary is crossed by the first instruction in the block. @@ -8531,7 +8565,7 @@ static void i386_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) DisasContext *dc = container_of(dcbase, DisasContext, base); if (dc->base.is_jmp == DISAS_TOO_MANY) { - gen_jmp_im(dc->base.pc_next - dc->cs_base); + gen_jmp_im(dc, dc->base.pc_next - dc->cs_base); gen_eob(dc); } } diff --git a/target/i386/whp-dispatch.h b/target/i386/whp-dispatch.h index d8d3485976..4ae3cc8fa5 100644 --- a/target/i386/whp-dispatch.h +++ b/target/i386/whp-dispatch.h @@ -1,5 +1,4 @@ #include "windows.h" -#include #include #include diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index c63adf772f..f154565117 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -7,12 +7,12 @@ * 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. + * 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 - * General Public License for more details. + * 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 . @@ -70,7 +70,6 @@ #define EXCP_MMU_CONF 56 /* MMU Configuration Error */ #define EXCP_MMU_ILLEGAL 57 /* MMU Illegal Operation Error */ #define EXCP_MMU_ACCESS 58 /* MMU Access Level Violation Error */ -#define EXCP_UNSUPPORTED 61 #define EXCP_RTE 0x100 #define EXCP_HALT_INSN 0x101 diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 6eeffdf9bb..b35489ba4e 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -7,12 +7,12 @@ * 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. + * 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 - * General Public License for more details. + * 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 . diff --git a/target/m68k/gdbstub.c b/target/m68k/gdbstub.c index 99e5be8132..fd2bb46c42 100644 --- a/target/m68k/gdbstub.c +++ b/target/m68k/gdbstub.c @@ -7,7 +7,7 @@ * 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. + * 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 diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 917d46efcc..3e26d337bf 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -7,12 +7,12 @@ * 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. + * 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 - * General Public License for more details. + * 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 . diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 8d09ed91c4..76f439985a 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/target/m68k/translate.c b/target/m68k/translate.c index ae3651b867..6217a683f1 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -7,12 +7,12 @@ * 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. + * 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 - * General Public License for more details. + * 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 . @@ -1553,7 +1553,7 @@ DISAS_INSN(undef) but actually illegal for CPU32 or pre-68020. */ qemu_log_mask(LOG_UNIMP, "Illegal instruction: %04x @ %08x\n", insn, s->base.pc_next); - gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); } DISAS_INSN(mulw) @@ -2800,7 +2800,7 @@ DISAS_INSN(mull) if (ext & 0x400) { if (!m68k_feature(s->env, M68K_FEATURE_QUAD_MULDIV)) { - gen_exception(s, s->base.pc_next, EXCP_UNSUPPORTED); + gen_exception(s, s->base.pc_next, EXCP_ILLEGAL); return; } @@ -4510,7 +4510,7 @@ DISAS_INSN(strldsr) addr = s->pc - 2; ext = read_im16(env, s); if (ext != 0x46FC) { - gen_exception(s, addr, EXCP_UNSUPPORTED); + gen_exception(s, addr, EXCP_ILLEGAL); return; } ext = read_im16(env, s); diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 9b546a2c18..5596cd5485 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -202,7 +202,11 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) (cpu->cfg.use_barrel ? PVR2_USE_BARREL_MASK : 0) | (cpu->cfg.use_div ? PVR2_USE_DIV_MASK : 0) | (cpu->cfg.use_msr_instr ? PVR2_USE_MSR_INSTR : 0) | - (cpu->cfg.use_pcmp_instr ? PVR2_USE_PCMP_INSTR : 0); + (cpu->cfg.use_pcmp_instr ? PVR2_USE_PCMP_INSTR : 0) | + (cpu->cfg.dopb_bus_exception ? + PVR2_DOPB_BUS_EXC_MASK : 0) | + (cpu->cfg.iopb_bus_exception ? + PVR2_IOPB_BUS_EXC_MASK : 0); env->pvr.regs[5] |= cpu->cfg.dcache_writeback ? PVR5_DCACHE_WRITEBACK_MASK : 0; @@ -265,6 +269,12 @@ static Property mb_properties[] = { DEFINE_PROP_BOOL("dcache-writeback", MicroBlazeCPU, cfg.dcache_writeback, false), DEFINE_PROP_BOOL("endianness", MicroBlazeCPU, cfg.endi, false), + /* Enables bus exceptions on failed data accesses (load/stores). */ + DEFINE_PROP_BOOL("dopb-bus-exception", MicroBlazeCPU, + cfg.dopb_bus_exception, false), + /* Enables bus exceptions on failed instruction fetches. */ + DEFINE_PROP_BOOL("iopb-bus-exception", MicroBlazeCPU, + cfg.iopb_bus_exception, false), DEFINE_PROP_STRING("version", MicroBlazeCPU, cfg.version), DEFINE_PROP_UINT8("pvr", MicroBlazeCPU, cfg.pvr, C_PVR_FULL), DEFINE_PROP_END_OF_LIST(), @@ -297,7 +307,7 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) #ifdef CONFIG_USER_ONLY cc->handle_mmu_fault = mb_cpu_handle_mmu_fault; #else - cc->do_unassigned_access = mb_cpu_unassigned_access; + cc->do_transaction_failed = mb_cpu_transaction_failed; cc->get_phys_page_debug = mb_cpu_get_phys_page_debug; #endif dc->vmsd = &vmstate_mb_cpu; diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 3c4e0ba80a..792bbc97c7 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -308,6 +308,8 @@ struct MicroBlazeCPU { bool use_mmu; bool dcache_writeback; bool endi; + bool dopb_bus_exception; + bool iopb_bus_exception; char *version; uint8_t pvr; } cfg; @@ -388,9 +390,10 @@ static inline void cpu_get_tb_cpu_state(CPUMBState *env, target_ulong *pc, } #if !defined(CONFIG_USER_ONLY) -void mb_cpu_unassigned_access(CPUState *cpu, hwaddr addr, - bool is_write, bool is_exec, int is_asi, - unsigned size); +void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, + unsigned size, MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr); #endif #endif diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 7cdbbcccae..e23dcfdc20 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -486,26 +486,28 @@ void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) mmu_write(env, ext, rn, v); } -void mb_cpu_unassigned_access(CPUState *cs, hwaddr addr, - bool is_write, bool is_exec, int is_asi, - unsigned size) +void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, + unsigned size, MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) { MicroBlazeCPU *cpu; CPUMBState *env; - - qemu_log_mask(CPU_LOG_INT, "Unassigned " TARGET_FMT_plx " wr=%d exe=%d\n", - addr, is_write ? 1 : 0, is_exec ? 1 : 0); - if (cs == NULL) { - return; - } + qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx + " physaddr 0x" TARGET_FMT_plx " size %d access type %s\n", + addr, physaddr, size, + access_type == MMU_INST_FETCH ? "INST_FETCH" : + (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE")); cpu = MICROBLAZE_CPU(cs); env = &cpu->env; + + cpu_restore_state(cs, retaddr, true); if (!(env->sregs[SR_MSR] & MSR_EE)) { return; } env->sregs[SR_EAR] = addr; - if (is_exec) { + if (access_type == MMU_INST_FETCH) { if ((env->pvr.regs[2] & PVR2_IOPB_BUS_EXC_MASK)) { env->sregs[SR_ESR] = ESR_EC_INSN_BUS; helper_raise_exception(env, EXCP_HW_EXCP); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 497706b669..e217fb3e36 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -113,11 +113,20 @@ static void mips_cpu_reset(CPUState *s) } static void mips_cpu_disas_set_info(CPUState *s, disassemble_info *info) { + MIPSCPU *cpu = MIPS_CPU(s); + CPUMIPSState *env = &cpu->env; + + if (!(env->insn_flags & ISA_NANOMIPS32)) { #ifdef TARGET_WORDS_BIGENDIAN - info->print_insn = print_insn_big_mips; + info->print_insn = print_insn_big_mips; #else - info->print_insn = print_insn_little_mips; + info->print_insn = print_insn_little_mips; #endif + } else { +#if defined(CONFIG_NANOMIPS_DIS) + info->print_insn = print_insn_nanomips; +#endif + } } static void mips_cpu_realizefn(DeviceState *dev, Error **errp) diff --git a/target/mips/cpu.h b/target/mips/cpu.h index cfe1735e0e..a10eeb0de3 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -11,6 +11,8 @@ #include "exec/cpu-defs.h" #include "fpu/softfloat.h" +#define TCG_GUEST_DEFAULT_MO (0) + struct CPUMIPSState; typedef struct CPUMIPSTLBContext CPUMIPSTLBContext; @@ -123,6 +125,264 @@ typedef struct mips_def_t mips_def_t; #define MIPS_KSCRATCH_NUM 6 #define MIPS_MAAR_MAX 16 /* Must be an even number. */ + +/* + * Summary of CP0 registers + * ======================== + * + * + * Register 0 Register 1 Register 2 Register 3 + * ---------- ---------- ---------- ---------- + * + * 0 Index Random EntryLo0 EntryLo1 + * 1 MVPControl VPEControl TCStatus GlobalNumber + * 2 MVPConf0 VPEConf0 TCBind + * 3 MVPConf1 VPEConf1 TCRestart + * 4 VPControl YQMask TCHalt + * 5 VPESchedule TCContext + * 6 VPEScheFBack TCSchedule + * 7 VPEOpt TCScheFBack TCOpt + * + * + * Register 4 Register 5 Register 6 Register 7 + * ---------- ---------- ---------- ---------- + * + * 0 Context PageMask Wired HWREna + * 1 ContextConfig PageGrain SRSConf0 + * 2 UserLocal SegCtl0 SRSConf1 + * 3 XContextConfig SegCtl1 SRSConf2 + * 4 DebugContextID SegCtl2 SRSConf3 + * 5 MemoryMapID PWBase SRSConf4 + * 6 PWField PWCtl + * 7 PWSize + * + * + * Register 8 Register 9 Register 10 Register 11 + * ---------- ---------- ----------- ----------- + * + * 0 BadVAddr Count EntryHi Compare + * 1 BadInstr + * 2 BadInstrP + * 3 BadInstrX + * 4 GuestCtl1 GuestCtl0Ext + * 5 GuestCtl2 + * 6 SAARI GuestCtl3 + * 7 SAAR + * + * + * Register 12 Register 13 Register 14 Register 15 + * ----------- ----------- ----------- ----------- + * + * 0 Status Cause EPC PRId + * 1 IntCtl EBase + * 2 SRSCtl NestedEPC CDMMBase + * 3 SRSMap CMGCRBase + * 4 View_IPL View_RIPL BEVVA + * 5 SRSMap2 NestedExc + * 6 GuestCtl0 + * 7 GTOffset + * + * + * Register 16 Register 17 Register 18 Register 19 + * ----------- ----------- ----------- ----------- + * + * 0 Config LLAddr WatchLo WatchHi + * 1 Config1 MAAR WatchLo WatchHi + * 2 Config2 MAARI WatchLo WatchHi + * 3 Config3 WatchLo WatchHi + * 4 Config4 WatchLo WatchHi + * 5 Config5 WatchLo WatchHi + * 6 WatchLo WatchHi + * 7 WatchLo WatchHi + * + * + * Register 20 Register 21 Register 22 Register 23 + * ----------- ----------- ----------- ----------- + * + * 0 XContext Debug + * 1 TraceControl + * 2 TraceControl2 + * 3 UserTraceData1 + * 4 TraceIBPC + * 5 TraceDBPC + * 6 Debug2 + * 7 + * + * + * Register 24 Register 25 Register 26 Register 27 + * ----------- ----------- ----------- ----------- + * + * 0 DEPC PerfCnt ErrCtl CacheErr + * 1 PerfCnt + * 2 TraceControl3 PerfCnt + * 3 UserTraceData2 PerfCnt + * 4 PerfCnt + * 5 PerfCnt + * 6 PerfCnt + * 7 PerfCnt + * + * + * Register 28 Register 29 Register 30 Register 31 + * ----------- ----------- ----------- ----------- + * + * 0 DataLo DataHi ErrorEPC DESAVE + * 1 TagLo TagHi + * 2 DataLo DataHi KScratch + * 3 TagLo TagHi KScratch + * 4 DataLo DataHi KScratch + * 5 TagLo TagHi KScratch + * 6 DataLo DataHi KScratch + * 7 TagLo TagHi KScratch + * + */ +#define CP0_REGISTER_00 0 +#define CP0_REGISTER_01 1 +#define CP0_REGISTER_02 2 +#define CP0_REGISTER_03 3 +#define CP0_REGISTER_04 4 +#define CP0_REGISTER_05 5 +#define CP0_REGISTER_06 6 +#define CP0_REGISTER_07 7 +#define CP0_REGISTER_08 8 +#define CP0_REGISTER_09 9 +#define CP0_REGISTER_10 10 +#define CP0_REGISTER_11 11 +#define CP0_REGISTER_12 12 +#define CP0_REGISTER_13 13 +#define CP0_REGISTER_14 14 +#define CP0_REGISTER_15 15 +#define CP0_REGISTER_16 16 +#define CP0_REGISTER_17 17 +#define CP0_REGISTER_18 18 +#define CP0_REGISTER_19 19 +#define CP0_REGISTER_20 20 +#define CP0_REGISTER_21 21 +#define CP0_REGISTER_22 22 +#define CP0_REGISTER_23 23 +#define CP0_REGISTER_24 24 +#define CP0_REGISTER_25 25 +#define CP0_REGISTER_26 26 +#define CP0_REGISTER_27 27 +#define CP0_REGISTER_28 28 +#define CP0_REGISTER_29 29 +#define CP0_REGISTER_30 30 +#define CP0_REGISTER_31 31 + + +/* CP0 Register 00 */ +#define CP0_REG00__INDEX 0 +#define CP0_REG00__VPCONTROL 4 +/* CP0 Register 01 */ +/* CP0 Register 02 */ +#define CP0_REG02__ENTRYLO0 0 +/* CP0 Register 03 */ +#define CP0_REG03__ENTRYLO1 0 +#define CP0_REG03__GLOBALNUM 1 +/* CP0 Register 04 */ +#define CP0_REG04__CONTEXT 0 +#define CP0_REG04__USERLOCAL 2 +#define CP0_REG04__DBGCONTEXTID 4 +#define CP0_REG00__MMID 5 +/* CP0 Register 05 */ +#define CP0_REG05__PAGEMASK 0 +#define CP0_REG05__PAGEGRAIN 1 +/* CP0 Register 06 */ +#define CP0_REG06__WIRED 0 +/* CP0 Register 07 */ +#define CP0_REG07__HWRENA 0 +/* CP0 Register 08 */ +#define CP0_REG08__BADVADDR 0 +#define CP0_REG08__BADINSTR 1 +#define CP0_REG08__BADINSTRP 2 +/* CP0 Register 09 */ +#define CP0_REG09__COUNT 0 +#define CP0_REG09__SAARI 6 +#define CP0_REG09__SAAR 7 +/* CP0 Register 10 */ +#define CP0_REG10__ENTRYHI 0 +#define CP0_REG10__GUESTCTL1 4 +#define CP0_REG10__GUESTCTL2 5 +/* CP0 Register 11 */ +#define CP0_REG11__COMPARE 0 +#define CP0_REG11__GUESTCTL0EXT 4 +/* CP0 Register 12 */ +#define CP0_REG12__STATUS 0 +#define CP0_REG12__INTCTL 1 +#define CP0_REG12__SRSCTL 2 +#define CP0_REG12__GUESTCTL0 6 +#define CP0_REG12__GTOFFSET 7 +/* CP0 Register 13 */ +#define CP0_REG13__CAUSE 0 +/* CP0 Register 14 */ +#define CP0_REG14__EPC 0 +/* CP0 Register 15 */ +#define CP0_REG15__PRID 0 +#define CP0_REG15__EBASE 1 +#define CP0_REG15__CDMMBASE 2 +#define CP0_REG15__CMGCRBASE 3 +/* CP0 Register 16 */ +#define CP0_REG16__CONFIG 0 +#define CP0_REG16__CONFIG1 1 +#define CP0_REG16__CONFIG2 2 +#define CP0_REG16__CONFIG3 3 +#define CP0_REG16__CONFIG4 4 +#define CP0_REG16__CONFIG5 5 +#define CP0_REG00__CONFIG7 7 +/* CP0 Register 17 */ +#define CP0_REG17__LLADDR 0 +#define CP0_REG17__MAAR 1 +#define CP0_REG17__MAARI 2 +/* CP0 Register 18 */ +#define CP0_REG18__WATCHLO0 0 +#define CP0_REG18__WATCHLO1 1 +#define CP0_REG18__WATCHLO2 2 +#define CP0_REG18__WATCHLO3 3 +/* CP0 Register 19 */ +#define CP0_REG19__WATCHHI0 0 +#define CP0_REG19__WATCHHI1 1 +#define CP0_REG19__WATCHHI2 2 +#define CP0_REG19__WATCHHI3 3 +/* CP0 Register 20 */ +#define CP0_REG20__XCONTEXT 0 +/* CP0 Register 21 */ +/* CP0 Register 22 */ +/* CP0 Register 23 */ +#define CP0_REG23__DEBUG 0 +/* CP0 Register 24 */ +#define CP0_REG24__DEPC 0 +/* CP0 Register 25 */ +#define CP0_REG25__PERFCTL0 0 +#define CP0_REG25__PERFCNT0 1 +#define CP0_REG25__PERFCTL1 2 +#define CP0_REG25__PERFCNT1 3 +#define CP0_REG25__PERFCTL2 4 +#define CP0_REG25__PERFCNT2 5 +#define CP0_REG25__PERFCTL3 6 +#define CP0_REG25__PERFCNT3 7 +/* CP0 Register 26 */ +#define CP0_REG00__ERRCTL 0 +/* CP0 Register 27 */ +#define CP0_REG27__CACHERR 0 +/* CP0 Register 28 */ +#define CP0_REG28__ITAGLO 0 +#define CP0_REG28__IDATALO 1 +#define CP0_REG28__DTAGLO 2 +#define CP0_REG28__DDATALO 3 +/* CP0 Register 29 */ +#define CP0_REG29__IDATAHI 1 +#define CP0_REG29__DDATAHI 3 +/* CP0 Register 30 */ +#define CP0_REG30__ERROREPC 0 +/* CP0 Register 31 */ +#define CP0_REG31__DESAVE 0 +#define CP0_REG31__KSCRATCH1 2 +#define CP0_REG31__KSCRATCH2 3 +#define CP0_REG31__KSCRATCH3 4 +#define CP0_REG31__KSCRATCH4 5 +#define CP0_REG31__KSCRATCH5 6 +#define CP0_REG31__KSCRATCH6 7 + + typedef struct TCState TCState; struct TCState { target_ulong gpr[32]; @@ -132,23 +392,23 @@ struct TCState { target_ulong ACX[MIPS_DSP_ACC]; target_ulong DSPControl; int32_t CP0_TCStatus; -#define CP0TCSt_TCU3 31 -#define CP0TCSt_TCU2 30 -#define CP0TCSt_TCU1 29 -#define CP0TCSt_TCU0 28 -#define CP0TCSt_TMX 27 -#define CP0TCSt_RNST 23 -#define CP0TCSt_TDS 21 -#define CP0TCSt_DT 20 -#define CP0TCSt_DA 15 -#define CP0TCSt_A 13 -#define CP0TCSt_TKSU 11 -#define CP0TCSt_IXMT 10 -#define CP0TCSt_TASID 0 +#define CP0TCSt_TCU3 31 +#define CP0TCSt_TCU2 30 +#define CP0TCSt_TCU1 29 +#define CP0TCSt_TCU0 28 +#define CP0TCSt_TMX 27 +#define CP0TCSt_RNST 23 +#define CP0TCSt_TDS 21 +#define CP0TCSt_DT 20 +#define CP0TCSt_DA 15 +#define CP0TCSt_A 13 +#define CP0TCSt_TKSU 11 +#define CP0TCSt_IXMT 10 +#define CP0TCSt_TASID 0 int32_t CP0_TCBind; -#define CP0TCBd_CurTC 21 -#define CP0TCBd_TBE 17 -#define CP0TCBd_CurVPE 0 +#define CP0TCBd_CurTC 21 +#define CP0TCBd_TBE 17 +#define CP0TCBd_CurVPE 0 target_ulong CP0_TCHalt; target_ulong CP0_TCContext; target_ulong CP0_TCSchedule; @@ -170,8 +430,22 @@ struct TCState { MSACSR_FS_MASK) float_status msa_fp_status; + + /* Upper 64-bit MMRs (multimedia registers); the lower 64-bit are GPRs */ + uint64_t mmr[32]; + +#define NUMBER_OF_MXU_REGISTERS 16 + target_ulong mxu_gpr[NUMBER_OF_MXU_REGISTERS - 1]; + target_ulong mxu_cr; +#define MXU_CR_LC 31 +#define MXU_CR_RC 30 +#define MXU_CR_BIAS 2 +#define MXU_CR_RD_EN 1 +#define MXU_CR_MXU_EN 0 + }; +struct MIPSITUState; typedef struct CPUMIPSState CPUMIPSState; struct CPUMIPSState { TCState active_tc; @@ -195,10 +469,16 @@ struct CPUMIPSState { #define MSAIR_ProcID 8 #define MSAIR_Rev 0 +/* + * CP0 Register 0 + */ int32_t CP0_Index; /* CP0_MVP* are per MVP registers. */ int32_t CP0_VPControl; #define CP0VPCtl_DIS 0 +/* + * CP0 Register 1 + */ int32_t CP0_Random; int32_t CP0_VPEControl; #define CP0VPECo_YSI 21 @@ -239,7 +519,13 @@ struct CPUMIPSState { #define CP0VPEOpt_DWX2 2 #define CP0VPEOpt_DWX1 1 #define CP0VPEOpt_DWX0 0 +/* + * CP0 Register 2 + */ uint64_t CP0_EntryLo0; +/* + * CP0 Register 3 + */ uint64_t CP0_EntryLo1; #if defined(TARGET_MIPS64) # define CP0EnLo_RI 63 @@ -250,8 +536,15 @@ struct CPUMIPSState { #endif int32_t CP0_GlobalNumber; #define CP0GN_VPId 0 +/* + * CP0 Register 4 + */ target_ulong CP0_Context; target_ulong CP0_KScratch[MIPS_KSCRATCH_NUM]; + int32_t CP0_MemoryMapID; +/* + * CP0 Register 5 + */ int32_t CP0_PageMask; int32_t CP0_PageGrain_rw_bitmask; int32_t CP0_PageGrain; @@ -289,7 +582,47 @@ struct CPUMIPSState { #define CP0SC2_XR 56 #define CP0SC2_XR_MASK (0xFFULL << CP0SC2_XR) #define CP0SC2_MASK (CP0SC_1GMASK | (CP0SC_1GMASK << 16) | CP0SC2_XR_MASK) + target_ulong CP0_PWBase; + target_ulong CP0_PWField; +#if defined(TARGET_MIPS64) +#define CP0PF_BDI 32 /* 37..32 */ +#define CP0PF_GDI 24 /* 29..24 */ +#define CP0PF_UDI 18 /* 23..18 */ +#define CP0PF_MDI 12 /* 17..12 */ +#define CP0PF_PTI 6 /* 11..6 */ +#define CP0PF_PTEI 0 /* 5..0 */ +#else +#define CP0PF_GDW 24 /* 29..24 */ +#define CP0PF_UDW 18 /* 23..18 */ +#define CP0PF_MDW 12 /* 17..12 */ +#define CP0PF_PTW 6 /* 11..6 */ +#define CP0PF_PTEW 0 /* 5..0 */ +#endif + target_ulong CP0_PWSize; +#if defined(TARGET_MIPS64) +#define CP0PS_BDW 32 /* 37..32 */ +#endif +#define CP0PS_PS 30 +#define CP0PS_GDW 24 /* 29..24 */ +#define CP0PS_UDW 18 /* 23..18 */ +#define CP0PS_MDW 12 /* 17..12 */ +#define CP0PS_PTW 6 /* 11..6 */ +#define CP0PS_PTEW 0 /* 5..0 */ +/* + * CP0 Register 6 + */ int32_t CP0_Wired; + int32_t CP0_PWCtl; +#define CP0PC_PWEN 31 +#if defined(TARGET_MIPS64) +#define CP0PC_PWDIREXT 30 +#define CP0PC_XK 28 +#define CP0PC_XS 27 +#define CP0PC_XU 26 +#endif +#define CP0PC_DPH 7 +#define CP0PC_HUGEPG 6 +#define CP0PC_PSN 0 /* 5..0 */ int32_t CP0_SRSConf0_rw_bitmask; int32_t CP0_SRSConf0; #define CP0SRSC0_M 31 @@ -319,15 +652,40 @@ struct CPUMIPSState { #define CP0SRSC4_SRS15 20 #define CP0SRSC4_SRS14 10 #define CP0SRSC4_SRS13 0 +/* + * CP0 Register 7 + */ int32_t CP0_HWREna; +/* + * CP0 Register 8 + */ target_ulong CP0_BadVAddr; uint32_t CP0_BadInstr; uint32_t CP0_BadInstrP; + uint32_t CP0_BadInstrX; +/* + * CP0 Register 9 + */ int32_t CP0_Count; + uint32_t CP0_SAARI; +#define CP0SAARI_TARGET 0 /* 5..0 */ + uint64_t CP0_SAAR[2]; +#define CP0SAAR_BASE 12 /* 43..12 */ +#define CP0SAAR_SIZE 1 /* 5..1 */ +#define CP0SAAR_EN 0 +/* + * CP0 Register 10 + */ target_ulong CP0_EntryHi; #define CP0EnHi_EHINV 10 target_ulong CP0_EntryHi_ASID_mask; +/* + * CP0 Register 11 + */ int32_t CP0_Compare; +/* + * CP0 Register 12 + */ int32_t CP0_Status; #define CP0St_CU3 31 #define CP0St_CU2 30 @@ -369,6 +727,9 @@ struct CPUMIPSState { #define CP0SRSMap_SSV2 8 #define CP0SRSMap_SSV1 4 #define CP0SRSMap_SSV0 0 +/* + * CP0 Register 13 + */ int32_t CP0_Cause; #define CP0Ca_BD 31 #define CP0Ca_TI 30 @@ -380,34 +741,44 @@ struct CPUMIPSState { #define CP0Ca_IP 8 #define CP0Ca_IP_mask 0x0000FF00 #define CP0Ca_EC 2 +/* + * CP0 Register 14 + */ target_ulong CP0_EPC; +/* + * CP0 Register 15 + */ int32_t CP0_PRid; target_ulong CP0_EBase; target_ulong CP0_EBaseWG_rw_bitmask; #define CP0EBase_WG 11 target_ulong CP0_CMGCRBase; +/* + * CP0 Register 16 + */ int32_t CP0_Config0; #define CP0C0_M 31 -#define CP0C0_K23 28 -#define CP0C0_KU 25 +#define CP0C0_K23 28 /* 30..28 */ +#define CP0C0_KU 25 /* 27..25 */ #define CP0C0_MDU 20 #define CP0C0_MM 18 #define CP0C0_BM 16 +#define CP0C0_Impl 16 /* 24..16 */ #define CP0C0_BE 15 -#define CP0C0_AT 13 -#define CP0C0_AR 10 -#define CP0C0_MT 7 +#define CP0C0_AT 13 /* 14..13 */ +#define CP0C0_AR 10 /* 12..10 */ +#define CP0C0_MT 7 /* 9..7 */ #define CP0C0_VI 3 -#define CP0C0_K0 0 +#define CP0C0_K0 0 /* 2..0 */ int32_t CP0_Config1; #define CP0C1_M 31 -#define CP0C1_MMU 25 -#define CP0C1_IS 22 -#define CP0C1_IL 19 -#define CP0C1_IA 16 -#define CP0C1_DS 13 -#define CP0C1_DL 10 -#define CP0C1_DA 7 +#define CP0C1_MMU 25 /* 30..25 */ +#define CP0C1_IS 22 /* 24..22 */ +#define CP0C1_IL 19 /* 21..19 */ +#define CP0C1_IA 16 /* 18..16 */ +#define CP0C1_DS 13 /* 15..13 */ +#define CP0C1_DL 10 /* 12..10 */ +#define CP0C1_DA 7 /* 9..7 */ #define CP0C1_C2 6 #define CP0C1_MD 5 #define CP0C1_PC 4 @@ -417,83 +788,117 @@ struct CPUMIPSState { #define CP0C1_FP 0 int32_t CP0_Config2; #define CP0C2_M 31 -#define CP0C2_TU 28 -#define CP0C2_TS 24 -#define CP0C2_TL 20 -#define CP0C2_TA 16 -#define CP0C2_SU 12 -#define CP0C2_SS 8 -#define CP0C2_SL 4 -#define CP0C2_SA 0 +#define CP0C2_TU 28 /* 30..28 */ +#define CP0C2_TS 24 /* 27..24 */ +#define CP0C2_TL 20 /* 23..20 */ +#define CP0C2_TA 16 /* 19..16 */ +#define CP0C2_SU 12 /* 15..12 */ +#define CP0C2_SS 8 /* 11..8 */ +#define CP0C2_SL 4 /* 7..4 */ +#define CP0C2_SA 0 /* 3..0 */ int32_t CP0_Config3; -#define CP0C3_M 31 -#define CP0C3_BPG 30 -#define CP0C3_CMGCR 29 -#define CP0C3_MSAP 28 -#define CP0C3_BP 27 -#define CP0C3_BI 26 -#define CP0C3_SC 25 -#define CP0C3_IPLW 21 -#define CP0C3_MMAR 18 -#define CP0C3_MCU 17 -#define CP0C3_ISA_ON_EXC 16 -#define CP0C3_ISA 14 -#define CP0C3_ULRI 13 -#define CP0C3_RXI 12 -#define CP0C3_DSP2P 11 -#define CP0C3_DSPP 10 -#define CP0C3_LPA 7 -#define CP0C3_VEIC 6 -#define CP0C3_VInt 5 -#define CP0C3_SP 4 -#define CP0C3_CDMM 3 -#define CP0C3_MT 2 -#define CP0C3_SM 1 -#define CP0C3_TL 0 +#define CP0C3_M 31 +#define CP0C3_BPG 30 +#define CP0C3_CMGCR 29 +#define CP0C3_MSAP 28 +#define CP0C3_BP 27 +#define CP0C3_BI 26 +#define CP0C3_SC 25 +#define CP0C3_PW 24 +#define CP0C3_VZ 23 +#define CP0C3_IPLV 21 /* 22..21 */ +#define CP0C3_MMAR 18 /* 20..18 */ +#define CP0C3_MCU 17 +#define CP0C3_ISA_ON_EXC 16 +#define CP0C3_ISA 14 /* 15..14 */ +#define CP0C3_ULRI 13 +#define CP0C3_RXI 12 +#define CP0C3_DSP2P 11 +#define CP0C3_DSPP 10 +#define CP0C3_CTXTC 9 +#define CP0C3_ITL 8 +#define CP0C3_LPA 7 +#define CP0C3_VEIC 6 +#define CP0C3_VInt 5 +#define CP0C3_SP 4 +#define CP0C3_CDMM 3 +#define CP0C3_MT 2 +#define CP0C3_SM 1 +#define CP0C3_TL 0 int32_t CP0_Config4; int32_t CP0_Config4_rw_bitmask; -#define CP0C4_M 31 -#define CP0C4_IE 29 -#define CP0C4_AE 28 -#define CP0C4_KScrExist 16 -#define CP0C4_MMUExtDef 14 -#define CP0C4_FTLBPageSize 8 -#define CP0C4_FTLBWays 4 -#define CP0C4_FTLBSets 0 -#define CP0C4_MMUSizeExt 0 +#define CP0C4_M 31 +#define CP0C4_IE 29 /* 30..29 */ +#define CP0C4_AE 28 +#define CP0C4_VTLBSizeExt 24 /* 27..24 */ +#define CP0C4_KScrExist 16 +#define CP0C4_MMUExtDef 14 +#define CP0C4_FTLBPageSize 8 /* 12..8 */ +/* bit layout if MMUExtDef=1 */ +#define CP0C4_MMUSizeExt 0 /* 7..0 */ +/* bit layout if MMUExtDef=2 */ +#define CP0C4_FTLBWays 4 /* 7..4 */ +#define CP0C4_FTLBSets 0 /* 3..0 */ int32_t CP0_Config5; int32_t CP0_Config5_rw_bitmask; -#define CP0C5_M 31 -#define CP0C5_K 30 -#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 +#define CP0C5_M 31 +#define CP0C5_K 30 +#define CP0C5_CV 29 +#define CP0C5_EVA 28 +#define CP0C5_MSAEn 27 +#define CP0C5_PMJ 23 /* 25..23 */ +#define CP0C5_WR2 22 +#define CP0C5_NMS 21 +#define CP0C5_ULS 20 +#define CP0C5_XPA 19 +#define CP0C5_CRCP 18 +#define CP0C5_MI 17 +#define CP0C5_GI 15 /* 16..15 */ +#define CP0C5_CA2 14 +#define CP0C5_XNP 13 +#define CP0C5_DEC 11 +#define CP0C5_L2C 10 +#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_LLAddr; uint64_t CP0_MAAR[MIPS_MAAR_MAX]; int32_t CP0_MAARI; /* XXX: Maybe make LLAddr per-TC? */ - uint64_t lladdr; +/* + * CP0 Register 17 + */ + target_ulong lladdr; /* LL virtual address compared against SC */ target_ulong llval; - target_ulong llnewval; - target_ulong llreg; + uint64_t llval_wp; + uint32_t llnewval_wp; uint64_t CP0_LLAddr_rw_bitmask; int CP0_LLAddr_shift; +/* + * CP0 Register 18 + */ target_ulong CP0_WatchLo[8]; +/* + * CP0 Register 19 + */ int32_t CP0_WatchHi[8]; #define CP0WH_ASID 16 +/* + * CP0 Register 20 + */ target_ulong CP0_XContext; int32_t CP0_Framemask; +/* + * CP0 Register 23 + */ int32_t CP0_Debug; #define CP0DB_DBD 31 #define CP0DB_DM 30 @@ -513,18 +918,40 @@ struct CPUMIPSState { #define CP0DB_DDBL 2 #define CP0DB_DBp 1 #define CP0DB_DSS 0 +/* + * CP0 Register 24 + */ target_ulong CP0_DEPC; +/* + * CP0 Register 25 + */ int32_t CP0_Performance0; +/* + * CP0 Register 26 + */ int32_t CP0_ErrCtl; #define CP0EC_WST 29 #define CP0EC_SPR 28 #define CP0EC_ITC 26 +/* + * CP0 Register 28 + */ uint64_t CP0_TagLo; int32_t CP0_DataLo; +/* + * CP0 Register 29 + */ int32_t CP0_TagHi; int32_t CP0_DataHi; +/* + * CP0 Register 30 + */ target_ulong CP0_ErrorEPC; +/* + * CP0 Register 31 + */ int32_t CP0_DESAVE; + /* We waste some space so we can handle shadow registers like TCs. */ TCState tcs[MIPS_SHADOW_SET_MAX]; CPUMIPSFPUContext fpus[MIPS_FPU_MAX]; @@ -574,8 +1001,9 @@ struct CPUMIPSState { #define MIPS_HFLAG_BX 0x40000 /* branch exchanges execution mode */ #define MIPS_HFLAG_BMASK (MIPS_HFLAG_BMASK_BASE | MIPS_HFLAG_BMASK_EXT) /* MIPS DSP resources access. */ -#define MIPS_HFLAG_DSP 0x080000 /* Enable access to MIPS DSP resources. */ -#define MIPS_HFLAG_DSPR2 0x100000 /* Enable access to MIPS DSPR2 resources. */ +#define MIPS_HFLAG_DSP 0x080000 /* Enable access to DSP resources. */ +#define MIPS_HFLAG_DSP_R2 0x100000 /* Enable access to DSP R2 resources. */ +#define MIPS_HFLAG_DSP_R3 0x20000000 /* Enable access to DSP R3 resources. */ /* Extra flag about HWREna register. */ #define MIPS_HFLAG_HWRENA_ULR 0x200000 /* ULR bit from HWREna is set. */ #define MIPS_HFLAG_SBRI 0x400000 /* R6 SDBBP causes RI excpt. in user mode */ @@ -592,7 +1020,8 @@ struct CPUMIPSState { int CCRes; /* Cycle count resolution/divisor */ uint32_t CP0_Status_rw_bitmask; /* Read/write bits in CP0_Status */ uint32_t CP0_TCStatus_rw_bitmask; /* Read/write bits in CP0_TCStatus */ - int insn_flags; /* Supported instruction set */ + uint64_t insn_flags; /* Supported instruction set */ + int saarp; /* Fields up to this point are cleared by a CPU reset */ struct {} end_reset_fields; @@ -608,6 +1037,7 @@ struct CPUMIPSState { const mips_def_t *cpu_model; void *irq[8]; QEMUTimer *timer; /* Internal timer */ + struct MIPSITUState *itu; MemoryRegion *itc_tag; /* ITC Configuration Tags */ target_ulong exception_base; /* ExceptionBase input to the core */ }; @@ -726,8 +1156,6 @@ enum { EXCP_LAST = EXCP_TLBRI, }; -/* Dummy exception for conditional stores. */ -#define EXCP_SC 0x100 /* * This is an internally generated WAKE request line. @@ -744,12 +1172,15 @@ int cpu_mips_signal_handler(int host_signum, void *pinfo, void *puc); #define CPU_RESOLVING_TYPE TYPE_MIPS_CPU bool cpu_supports_cps_smp(const char *cpu_type); -bool cpu_supports_isa(const char *cpu_type, unsigned int isa); +bool cpu_supports_isa(const char *cpu_type, uint64_t isa); void cpu_set_exception_base(int vp_index, target_ulong address); /* mips_int.c */ void cpu_mips_soft_irq(CPUMIPSState *env, int irq, int level); +/* mips_itu.c */ +void itc_reconfigure(struct MIPSITUState *tag); + /* helper.c */ target_ulong exception_resume_pc (CPUMIPSState *env); diff --git a/target/mips/helper.c b/target/mips/helper.c index 8cf91ce339..c44cdca3b5 100644 --- a/target/mips/helper.c +++ b/target/mips/helper.c @@ -24,6 +24,7 @@ #include "exec/cpu_ldst.h" #include "exec/log.h" #include "hw/mips/cpudevs.h" +#include "qapi/qapi-commands-target.h" enum { TLBRET_XI = -6, @@ -502,7 +503,9 @@ static void raise_mmu_exception(CPUMIPSState *env, target_ulong address, break; } /* Raise exception */ - env->CP0_BadVAddr = address; + if (!(env->hflags & MIPS_HFLAG_DM)) { + env->CP0_BadVAddr = address; + } env->CP0_Context = (env->CP0_Context & ~0x007fffff) | ((address >> 9) & 0x007ffff0); env->CP0_EntryHi = (env->CP0_EntryHi & env->CP0_EntryHi_ASID_mask) | @@ -535,6 +538,342 @@ hwaddr mips_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } #endif +#if !defined(CONFIG_USER_ONLY) +#if !defined(TARGET_MIPS64) + +/* + * Perform hardware page table walk + * + * Memory accesses are performed using the KERNEL privilege level. + * Synchronous exceptions detected on memory accesses cause a silent exit + * from page table walking, resulting in a TLB or XTLB Refill exception. + * + * Implementations are not required to support page table walk memory + * accesses from mapped memory regions. When an unsupported access is + * attempted, a silent exit is taken, resulting in a TLB or XTLB Refill + * exception. + * + * Note that if an exception is caused by AddressTranslation or LoadMemory + * functions, the exception is not taken, a silent exit is taken, + * resulting in a TLB or XTLB Refill exception. + */ + +static bool get_pte(CPUMIPSState *env, uint64_t vaddr, int entry_size, + uint64_t *pte) +{ + if ((vaddr & ((entry_size >> 3) - 1)) != 0) { + return false; + } + if (entry_size == 64) { + *pte = cpu_ldq_code(env, vaddr); + } else { + *pte = cpu_ldl_code(env, vaddr); + } + return true; +} + +static uint64_t get_tlb_entry_layout(CPUMIPSState *env, uint64_t entry, + int entry_size, int ptei) +{ + uint64_t result = entry; + uint64_t rixi; + if (ptei > entry_size) { + ptei -= 32; + } + result >>= (ptei - 2); + rixi = result & 3; + result >>= 2; + result |= rixi << CP0EnLo_XI; + return result; +} + +static int walk_directory(CPUMIPSState *env, uint64_t *vaddr, + int directory_index, bool *huge_page, bool *hgpg_directory_hit, + uint64_t *pw_entrylo0, uint64_t *pw_entrylo1) +{ + int dph = (env->CP0_PWCtl >> CP0PC_DPH) & 0x1; + int psn = (env->CP0_PWCtl >> CP0PC_PSN) & 0x3F; + int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; + int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; + int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; + int directory_shift = (ptew > 1) ? -1 : + (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; + int leaf_shift = (ptew > 1) ? -1 : + (ptew == 1) ? native_shift + 1 : native_shift; + uint32_t direntry_size = 1 << (directory_shift + 3); + uint32_t leafentry_size = 1 << (leaf_shift + 3); + uint64_t entry; + uint64_t paddr; + int prot; + uint64_t lsb = 0; + uint64_t w = 0; + + if (get_physical_address(env, &paddr, &prot, *vaddr, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) != + TLBRET_MATCH) { + /* wrong base address */ + return 0; + } + if (!get_pte(env, *vaddr, direntry_size, &entry)) { + return 0; + } + + if ((entry & (1 << psn)) && hugepg) { + *huge_page = true; + *hgpg_directory_hit = true; + entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); + w = directory_index - 1; + if (directory_index & 0x1) { + /* Generate adjacent page from same PTE for odd TLB page */ + lsb = (1 << w) >> 6; + *pw_entrylo0 = entry & ~lsb; /* even page */ + *pw_entrylo1 = entry | lsb; /* odd page */ + } else if (dph) { + int oddpagebit = 1 << leaf_shift; + uint64_t vaddr2 = *vaddr ^ oddpagebit; + if (*vaddr & oddpagebit) { + *pw_entrylo1 = entry; + } else { + *pw_entrylo0 = entry; + } + if (get_physical_address(env, &paddr, &prot, vaddr2, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) != + TLBRET_MATCH) { + return 0; + } + if (!get_pte(env, vaddr2, leafentry_size, &entry)) { + return 0; + } + entry = get_tlb_entry_layout(env, entry, leafentry_size, pf_ptew); + if (*vaddr & oddpagebit) { + *pw_entrylo0 = entry; + } else { + *pw_entrylo1 = entry; + } + } else { + return 0; + } + return 1; + } else { + *vaddr = entry; + return 2; + } +} + +static bool page_table_walk_refill(CPUMIPSState *env, vaddr address, int rw, + int mmu_idx) +{ + int gdw = (env->CP0_PWSize >> CP0PS_GDW) & 0x3F; + int udw = (env->CP0_PWSize >> CP0PS_UDW) & 0x3F; + int mdw = (env->CP0_PWSize >> CP0PS_MDW) & 0x3F; + int ptw = (env->CP0_PWSize >> CP0PS_PTW) & 0x3F; + int ptew = (env->CP0_PWSize >> CP0PS_PTEW) & 0x3F; + + /* Initial values */ + bool huge_page = false; + bool hgpg_bdhit = false; + bool hgpg_gdhit = false; + bool hgpg_udhit = false; + bool hgpg_mdhit = false; + + int32_t pw_pagemask = 0; + target_ulong pw_entryhi = 0; + uint64_t pw_entrylo0 = 0; + uint64_t pw_entrylo1 = 0; + + /* Native pointer size */ + /*For the 32-bit architectures, this bit is fixed to 0.*/ + int native_shift = (((env->CP0_PWSize >> CP0PS_PS) & 1) == 0) ? 2 : 3; + + /* Indices from PWField */ + int pf_gdw = (env->CP0_PWField >> CP0PF_GDW) & 0x3F; + int pf_udw = (env->CP0_PWField >> CP0PF_UDW) & 0x3F; + int pf_mdw = (env->CP0_PWField >> CP0PF_MDW) & 0x3F; + int pf_ptw = (env->CP0_PWField >> CP0PF_PTW) & 0x3F; + int pf_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + + /* Indices computed from faulting address */ + int gindex = (address >> pf_gdw) & ((1 << gdw) - 1); + int uindex = (address >> pf_udw) & ((1 << udw) - 1); + int mindex = (address >> pf_mdw) & ((1 << mdw) - 1); + int ptindex = (address >> pf_ptw) & ((1 << ptw) - 1); + + /* Other HTW configs */ + int hugepg = (env->CP0_PWCtl >> CP0PC_HUGEPG) & 0x1; + + /* HTW Shift values (depend on entry size) */ + int directory_shift = (ptew > 1) ? -1 : + (hugepg && (ptew == 1)) ? native_shift + 1 : native_shift; + int leaf_shift = (ptew > 1) ? -1 : + (ptew == 1) ? native_shift + 1 : native_shift; + + /* Offsets into tables */ + int goffset = gindex << directory_shift; + int uoffset = uindex << directory_shift; + int moffset = mindex << directory_shift; + int ptoffset0 = (ptindex >> 1) << (leaf_shift + 1); + int ptoffset1 = ptoffset0 | (1 << (leaf_shift)); + + uint32_t leafentry_size = 1 << (leaf_shift + 3); + + /* Starting address - Page Table Base */ + uint64_t vaddr = env->CP0_PWBase; + + uint64_t dir_entry; + uint64_t paddr; + int prot; + int m; + + if (!(env->CP0_Config3 & (1 << CP0C3_PW))) { + /* walker is unimplemented */ + return false; + } + if (!(env->CP0_PWCtl & (1 << CP0PC_PWEN))) { + /* walker is disabled */ + return false; + } + if (!(gdw > 0 || udw > 0 || mdw > 0)) { + /* no structure to walk */ + return false; + } + if ((directory_shift == -1) || (leaf_shift == -1)) { + return false; + } + + /* Global Directory */ + if (gdw > 0) { + vaddr |= goffset; + switch (walk_directory(env, &vaddr, pf_gdw, &huge_page, &hgpg_gdhit, + &pw_entrylo0, &pw_entrylo1)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Upper directory */ + if (udw > 0) { + vaddr |= uoffset; + switch (walk_directory(env, &vaddr, pf_udw, &huge_page, &hgpg_udhit, + &pw_entrylo0, &pw_entrylo1)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Middle directory */ + if (mdw > 0) { + vaddr |= moffset; + switch (walk_directory(env, &vaddr, pf_mdw, &huge_page, &hgpg_mdhit, + &pw_entrylo0, &pw_entrylo1)) + { + case 0: + return false; + case 1: + goto refill; + case 2: + default: + break; + } + } + + /* Leaf Level Page Table - First half of PTE pair */ + vaddr |= ptoffset0; + if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) != + TLBRET_MATCH) { + return false; + } + if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { + return false; + } + dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); + pw_entrylo0 = dir_entry; + + /* Leaf Level Page Table - Second half of PTE pair */ + vaddr |= ptoffset1; + if (get_physical_address(env, &paddr, &prot, vaddr, MMU_DATA_LOAD, + ACCESS_INT, cpu_mmu_index(env, false)) != + TLBRET_MATCH) { + return false; + } + if (!get_pte(env, vaddr, leafentry_size, &dir_entry)) { + return false; + } + dir_entry = get_tlb_entry_layout(env, dir_entry, leafentry_size, pf_ptew); + pw_entrylo1 = dir_entry; + +refill: + + m = (1 << pf_ptw) - 1; + + if (huge_page) { + switch (hgpg_bdhit << 3 | hgpg_gdhit << 2 | hgpg_udhit << 1 | + hgpg_mdhit) + { + case 4: + m = (1 << pf_gdw) - 1; + if (pf_gdw & 1) { + m >>= 1; + } + break; + case 2: + m = (1 << pf_udw) - 1; + if (pf_udw & 1) { + m >>= 1; + } + break; + case 1: + m = (1 << pf_mdw) - 1; + if (pf_mdw & 1) { + m >>= 1; + } + break; + } + } + pw_pagemask = m >> 12; + update_pagemask(env, pw_pagemask << 13, &pw_pagemask); + pw_entryhi = (address & ~0x1fff) | (env->CP0_EntryHi & 0xFF); + { + target_ulong tmp_entryhi = env->CP0_EntryHi; + int32_t tmp_pagemask = env->CP0_PageMask; + uint64_t tmp_entrylo0 = env->CP0_EntryLo0; + uint64_t tmp_entrylo1 = env->CP0_EntryLo1; + + env->CP0_EntryHi = pw_entryhi; + env->CP0_PageMask = pw_pagemask; + env->CP0_EntryLo0 = pw_entrylo0; + env->CP0_EntryLo1 = pw_entrylo1; + + /* + * The hardware page walker inserts a page into the TLB in a manner + * identical to a TLBWR instruction as executed by the software refill + * handler. + */ + r4k_helper_tlbwr(env); + + env->CP0_EntryHi = tmp_entryhi; + env->CP0_PageMask = tmp_pagemask; + env->CP0_EntryLo0 = tmp_entrylo0; + env->CP0_EntryLo1 = tmp_entrylo1; + } + return true; +} +#endif +#endif + int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, int mmu_idx) { @@ -556,8 +895,7 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, /* data access */ #if !defined(CONFIG_USER_ONLY) - /* XXX: put correct access by using cpu_restore_state() - correctly */ + /* XXX: put correct access by using cpu_restore_state() correctly */ access_type = ACCESS_INT; ret = get_physical_address(env, &physical, &prot, address, rw, access_type, mmu_idx); @@ -581,6 +919,32 @@ int mips_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, } else if (ret < 0) #endif { +#if !defined(CONFIG_USER_ONLY) +#if !defined(TARGET_MIPS64) + if ((ret == TLBRET_NOMATCH) && (env->tlb->nb_tlb > 1)) { + /* + * Memory reads during hardware page table walking are performed + * as if they were kernel-mode load instructions. + */ + int mode = (env->hflags & MIPS_HFLAG_KSU); + bool ret_walker; + env->hflags &= ~MIPS_HFLAG_KSU; + ret_walker = page_table_walk_refill(env, address, rw, mmu_idx); + env->hflags |= mode; + if (ret_walker) { + ret = get_physical_address(env, &physical, &prot, + address, rw, access_type, mmu_idx); + if (ret == TLBRET_MATCH) { + tlb_set_page(cs, address & TARGET_PAGE_MASK, + physical & TARGET_PAGE_MASK, prot | PAGE_EXEC, + mmu_idx, TARGET_PAGE_SIZE); + ret = 0; + return ret; + } + } + } +#endif +#endif raise_mmu_exception(env, address, rw, ret); ret = 1; } @@ -680,6 +1044,22 @@ static void set_hflags_for_handler (CPUMIPSState *env) static inline void set_badinstr_registers(CPUMIPSState *env) { + if (env->insn_flags & ISA_NANOMIPS32) { + if (env->CP0_Config3 & (1 << CP0C3_BI)) { + uint32_t instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16; + if ((instr & 0x10000000) == 0) { + instr |= cpu_lduw_code(env, env->active_tc.PC + 2); + } + env->CP0_BadInstr = instr; + + if ((instr & 0xFC000000) == 0x60000000) { + instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16; + env->CP0_BadInstrX = instr; + } + } + return; + } + if (env->hflags & MIPS_HFLAG_M16) { /* TODO: add BadInstr support for microMIPS */ return; @@ -1084,12 +1464,42 @@ void QEMU_NORETURN do_raise_exception_err(CPUMIPSState *env, { 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); - } + 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 void mips_cpu_add_definition(gpointer data, gpointer user_data) +{ + ObjectClass *oc = data; + CpuDefinitionInfoList **cpu_list = user_data; + CpuDefinitionInfoList *entry; + CpuDefinitionInfo *info; + const char *typename; + + typename = object_class_get_name(oc); + info = g_malloc0(sizeof(*info)); + info->name = g_strndup(typename, + strlen(typename) - strlen("-" TYPE_MIPS_CPU)); + info->q_typename = g_strdup(typename); + + entry = g_malloc0(sizeof(*entry)); + entry->value = info; + entry->next = *cpu_list; + *cpu_list = entry; +} + +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) +{ + CpuDefinitionInfoList *cpu_list = NULL; + GSList *list; + + list = object_class_get_list(TYPE_MIPS_CPU, false); + g_slist_foreach(list, mips_cpu_add_definition, &cpu_list); + g_slist_free(list); + + return cpu_list; +} diff --git a/target/mips/helper.h b/target/mips/helper.h index 5f492348dd..a6d687e340 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -13,10 +13,8 @@ DEF_HELPER_4(swr, void, env, tl, tl, int) #ifndef CONFIG_USER_ONLY DEF_HELPER_3(ll, tl, env, tl, int) -DEF_HELPER_4(sc, tl, env, tl, tl, int) #ifdef TARGET_MIPS64 DEF_HELPER_3(lld, tl, env, tl, int) -DEF_HELPER_4(scd, tl, env, tl, tl, int) #endif #endif @@ -40,6 +38,8 @@ DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) #endif +DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) + #ifndef CONFIG_USER_ONLY /* CP0 helpers */ DEF_HELPER_1(mfc0_mvpcontrol, tl, env) @@ -63,6 +63,8 @@ DEF_HELPER_1(mftc0_tcschedule, tl, env) DEF_HELPER_1(mfc0_tcschefback, tl, env) DEF_HELPER_1(mftc0_tcschefback, tl, env) DEF_HELPER_1(mfc0_count, tl, env) +DEF_HELPER_1(mfc0_saar, tl, env) +DEF_HELPER_1(mfhc0_saar, tl, env) DEF_HELPER_1(mftc0_entryhi, tl, env) DEF_HELPER_1(mftc0_status, tl, env) DEF_HELPER_1(mftc0_cause, tl, env) @@ -85,6 +87,7 @@ 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) +DEF_HELPER_1(dmfc0_saar, tl, env) #endif /* TARGET_MIPS64 */ DEF_HELPER_2(mtc0_index, void, env, tl) @@ -118,6 +121,8 @@ DEF_HELPER_2(mtc0_pagegrain, void, env, tl) DEF_HELPER_2(mtc0_segctl0, void, env, tl) DEF_HELPER_2(mtc0_segctl1, void, env, tl) DEF_HELPER_2(mtc0_segctl2, void, env, tl) +DEF_HELPER_2(mtc0_pwfield, void, env, tl) +DEF_HELPER_2(mtc0_pwsize, void, env, tl) DEF_HELPER_2(mtc0_wired, void, env, tl) DEF_HELPER_2(mtc0_srsconf0, void, env, tl) DEF_HELPER_2(mtc0_srsconf1, void, env, tl) @@ -125,7 +130,11 @@ DEF_HELPER_2(mtc0_srsconf2, void, env, tl) DEF_HELPER_2(mtc0_srsconf3, void, env, tl) DEF_HELPER_2(mtc0_srsconf4, void, env, tl) DEF_HELPER_2(mtc0_hwrena, void, env, tl) +DEF_HELPER_2(mtc0_pwctl, void, env, tl) DEF_HELPER_2(mtc0_count, void, env, tl) +DEF_HELPER_2(mtc0_saari, void, env, tl) +DEF_HELPER_2(mtc0_saar, void, env, tl) +DEF_HELPER_2(mthc0_saar, void, env, tl) DEF_HELPER_2(mtc0_entryhi, void, env, tl) DEF_HELPER_2(mttc0_entryhi, void, env, tl) DEF_HELPER_2(mtc0_compare, void, env, tl) diff --git a/target/mips/internal.h b/target/mips/internal.h index e41051f8e6..8f6fc919d5 100644 --- a/target/mips/internal.h +++ b/target/mips/internal.h @@ -59,8 +59,9 @@ struct mips_def_t { int32_t CP0_PageGrain_rw_bitmask; int32_t CP0_PageGrain; target_ulong CP0_EBaseWG_rw_bitmask; - int insn_flags; + uint64_t insn_flags; enum mips_mmu_types mmu_type; + int32_t SAARP; }; extern const struct mips_def_t mips_defs[]; @@ -211,6 +212,7 @@ uint64_t float_class_d(uint64_t arg, float_status *fst); extern unsigned int ieee_rm[]; int ieee_ex_to_mips(int xcpt); +void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask); static inline void restore_rounding_mode(CPUMIPSState *env) { @@ -306,9 +308,9 @@ static inline void compute_hflags(CPUMIPSState *env) { env->hflags &= ~(MIPS_HFLAG_COP1X | MIPS_HFLAG_64 | MIPS_HFLAG_CP0 | MIPS_HFLAG_F64 | MIPS_HFLAG_FPU | MIPS_HFLAG_KSU | - MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2 | - MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | MIPS_HFLAG_FRE | - MIPS_HFLAG_ELPA | MIPS_HFLAG_ERL); + MIPS_HFLAG_AWRAP | MIPS_HFLAG_DSP | MIPS_HFLAG_DSP_R2 | + MIPS_HFLAG_DSP_R3 | MIPS_HFLAG_SBRI | MIPS_HFLAG_MSA | + MIPS_HFLAG_FRE | MIPS_HFLAG_ELPA | MIPS_HFLAG_ERL); if (env->CP0_Status & (1 << CP0St_ERL)) { env->hflags |= MIPS_HFLAG_ERL; } @@ -355,16 +357,29 @@ static inline void compute_hflags(CPUMIPSState *env) (env->CP0_Config5 & (1 << CP0C5_SBRI))) { env->hflags |= MIPS_HFLAG_SBRI; } - if (env->insn_flags & ASE_DSPR2) { - /* Enables access MIPS DSP resources, now our cpu is DSP ASER2, - so enable to access DSPR2 resources. */ + if (env->insn_flags & ASE_DSP_R3) { + /* + * Our cpu supports DSP R3 ASE, so enable + * access to DSP R3 resources. + */ if (env->CP0_Status & (1 << CP0St_MX)) { - env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSPR2; + env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSP_R2 | + MIPS_HFLAG_DSP_R3; + } + } else if (env->insn_flags & ASE_DSP_R2) { + /* + * Our cpu supports DSP R2 ASE, so enable + * access to DSP R2 resources. + */ + if (env->CP0_Status & (1 << CP0St_MX)) { + env->hflags |= MIPS_HFLAG_DSP | MIPS_HFLAG_DSP_R2; } } else if (env->insn_flags & ASE_DSP) { - /* Enables access MIPS DSP resources, now our cpu is DSP ASE, - so enable to access DSP resources. */ + /* + * Our cpu supports DSP ASE, so enable + * access to DSP resources. + */ if (env->CP0_Status & (1 << CP0St_MX)) { env->hflags |= MIPS_HFLAG_DSP; } diff --git a/target/mips/machine.c b/target/mips/machine.c index 20100d5adb..70d277db51 100644 --- a/target/mips/machine.c +++ b/target/mips/machine.c @@ -20,7 +20,8 @@ static int cpu_post_load(void *opaque, int version_id) /* FPU state */ -static int get_fpr(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_fpr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { int i; fpr_t *v = pv; @@ -31,8 +32,8 @@ static int get_fpr(QEMUFile *f, void *pv, size_t size, VMStateField *field) return 0; } -static int put_fpr(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_fpr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { int i; fpr_t *v = pv; @@ -128,7 +129,8 @@ const VMStateDescription vmstate_mvp = { /* TLB state */ -static int get_tlb(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_tlb(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { r4k_tlb_t *v = pv; uint16_t flags; @@ -155,8 +157,8 @@ static int get_tlb(QEMUFile *f, void *pv, size_t size, VMStateField *field) return 0; } -static int put_tlb(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_tlb(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { r4k_tlb_t *v = pv; @@ -212,8 +214,8 @@ const VMStateDescription vmstate_tlb = { const VMStateDescription vmstate_mips_cpu = { .name = "cpu", - .version_id = 10, - .minimum_version_id = 10, + .version_id = 18, + .minimum_version_id = 18, .post_load = cpu_post_load, .fields = (VMStateField[]) { /* Active TC */ @@ -251,12 +253,17 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_UINT64(env.CP0_EntryLo0, MIPSCPU), VMSTATE_UINT64(env.CP0_EntryLo1, MIPSCPU), VMSTATE_UINTTL(env.CP0_Context, MIPSCPU), + VMSTATE_INT32(env.CP0_MemoryMapID, MIPSCPU), VMSTATE_INT32(env.CP0_PageMask, MIPSCPU), VMSTATE_INT32(env.CP0_PageGrain, MIPSCPU), VMSTATE_UINTTL(env.CP0_SegCtl0, MIPSCPU), VMSTATE_UINTTL(env.CP0_SegCtl1, MIPSCPU), VMSTATE_UINTTL(env.CP0_SegCtl2, MIPSCPU), + VMSTATE_UINTTL(env.CP0_PWBase, MIPSCPU), + VMSTATE_UINTTL(env.CP0_PWField, MIPSCPU), + VMSTATE_UINTTL(env.CP0_PWSize, MIPSCPU), VMSTATE_INT32(env.CP0_Wired, MIPSCPU), + VMSTATE_INT32(env.CP0_PWCtl, MIPSCPU), VMSTATE_INT32(env.CP0_SRSConf0, MIPSCPU), VMSTATE_INT32(env.CP0_SRSConf1, MIPSCPU), VMSTATE_INT32(env.CP0_SRSConf2, MIPSCPU), @@ -266,7 +273,10 @@ const VMStateDescription vmstate_mips_cpu = { VMSTATE_UINTTL(env.CP0_BadVAddr, MIPSCPU), VMSTATE_UINT32(env.CP0_BadInstr, MIPSCPU), VMSTATE_UINT32(env.CP0_BadInstrP, MIPSCPU), + VMSTATE_UINT32(env.CP0_BadInstrX, MIPSCPU), VMSTATE_INT32(env.CP0_Count, MIPSCPU), + VMSTATE_UINT32(env.CP0_SAARI, MIPSCPU), + VMSTATE_UINT64_ARRAY(env.CP0_SAAR, MIPSCPU, 2), VMSTATE_UINTTL(env.CP0_EntryHi, MIPSCPU), VMSTATE_INT32(env.CP0_Compare, MIPSCPU), VMSTATE_INT32(env.CP0_Status, MIPSCPU), @@ -283,9 +293,10 @@ 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(env.CP0_LLAddr, MIPSCPU), VMSTATE_UINT64_ARRAY(env.CP0_MAAR, MIPSCPU, MIPS_MAAR_MAX), VMSTATE_INT32(env.CP0_MAARI, MIPSCPU), - VMSTATE_UINT64(env.lladdr, MIPSCPU), + VMSTATE_UINTTL(env.lladdr, MIPSCPU), VMSTATE_UINTTL_ARRAY(env.CP0_WatchLo, MIPSCPU, 8), VMSTATE_INT32_ARRAY(env.CP0_WatchHi, MIPSCPU, 8), VMSTATE_UINTTL(env.CP0_XContext, MIPSCPU), diff --git a/target/mips/mips-defs.h b/target/mips/mips-defs.h index d239069975..dbdb4b2b2d 100644 --- a/target/mips/mips-defs.h +++ b/target/mips/mips-defs.h @@ -22,39 +22,54 @@ #endif #endif -/* Masks used to mark instructions to indicate which ISA level they - were introduced in. */ -#define ISA_MIPS1 0x00000001 -#define ISA_MIPS2 0x00000002 -#define ISA_MIPS3 0x00000004 -#define ISA_MIPS4 0x00000008 -#define ISA_MIPS5 0x00000010 -#define ISA_MIPS32 0x00000020 -#define ISA_MIPS32R2 0x00000040 -#define ISA_MIPS64 0x00000080 -#define ISA_MIPS64R2 0x00000100 -#define ISA_MIPS32R3 0x00000200 -#define ISA_MIPS64R3 0x00000400 -#define ISA_MIPS32R5 0x00000800 -#define ISA_MIPS64R5 0x00001000 -#define ISA_MIPS32R6 0x00002000 -#define ISA_MIPS64R6 0x00004000 - -/* MIPS ASEs. */ -#define ASE_MIPS16 0x00010000 -#define ASE_MIPS3D 0x00020000 -#define ASE_MDMX 0x00040000 -#define ASE_DSP 0x00080000 -#define ASE_DSPR2 0x00100000 -#define ASE_MT 0x00200000 -#define ASE_SMARTMIPS 0x00400000 -#define ASE_MICROMIPS 0x00800000 -#define ASE_MSA 0x01000000 - -/* Chip specific instructions. */ -#define INSN_LOONGSON2E 0x20000000 -#define INSN_LOONGSON2F 0x40000000 -#define INSN_VR54XX 0x80000000 +/* + * bit definitions for insn_flags (ISAs/ASEs flags) + * ------------------------------------------------ + */ +/* + * bits 0-31: MIPS base instruction sets + */ +#define ISA_MIPS1 0x0000000000000001ULL +#define ISA_MIPS2 0x0000000000000002ULL +#define ISA_MIPS3 0x0000000000000004ULL +#define ISA_MIPS4 0x0000000000000008ULL +#define ISA_MIPS5 0x0000000000000010ULL +#define ISA_MIPS32 0x0000000000000020ULL +#define ISA_MIPS32R2 0x0000000000000040ULL +#define ISA_MIPS64 0x0000000000000080ULL +#define ISA_MIPS64R2 0x0000000000000100ULL +#define ISA_MIPS32R3 0x0000000000000200ULL +#define ISA_MIPS64R3 0x0000000000000400ULL +#define ISA_MIPS32R5 0x0000000000000800ULL +#define ISA_MIPS64R5 0x0000000000001000ULL +#define ISA_MIPS32R6 0x0000000000002000ULL +#define ISA_MIPS64R6 0x0000000000004000ULL +#define ISA_NANOMIPS32 0x0000000000008000ULL +/* + * bits 32-47: MIPS ASEs + */ +#define ASE_MIPS16 0x0000000100000000ULL +#define ASE_MIPS3D 0x0000000200000000ULL +#define ASE_MDMX 0x0000000400000000ULL +#define ASE_DSP 0x0000000800000000ULL +#define ASE_DSP_R2 0x0000001000000000ULL +#define ASE_DSP_R3 0x0000002000000000ULL +#define ASE_MT 0x0000004000000000ULL +#define ASE_SMARTMIPS 0x0000008000000000ULL +#define ASE_MICROMIPS 0x0000010000000000ULL +#define ASE_MSA 0x0000020000000000ULL +/* + * bits 48-55: vendor-specific base instruction sets + */ +#define INSN_LOONGSON2E 0x0001000000000000ULL +#define INSN_LOONGSON2F 0x0002000000000000ULL +#define INSN_VR54XX 0x0004000000000000ULL +#define INSN_R5900 0x0008000000000000ULL +/* + * bits 56-63: vendor-specific ASEs + */ +#define ASE_MMI 0x0100000000000000ULL +#define ASE_MXU 0x0200000000000000ULL /* MIPS CPU defines. */ #define CPU_MIPS1 (ISA_MIPS1) @@ -62,6 +77,7 @@ #define CPU_MIPS3 (CPU_MIPS2 | ISA_MIPS3) #define CPU_MIPS4 (CPU_MIPS3 | ISA_MIPS4) #define CPU_VR54XX (CPU_MIPS4 | INSN_VR54XX) +#define CPU_R5900 (CPU_MIPS3 | INSN_R5900) #define CPU_LOONGSON2E (CPU_MIPS3 | INSN_LOONGSON2E) #define CPU_LOONGSON2F (CPU_MIPS3 | INSN_LOONGSON2F) @@ -87,6 +103,9 @@ #define CPU_MIPS32R6 (CPU_MIPS32R5 | ISA_MIPS32R6) #define CPU_MIPS64R6 (CPU_MIPS64R5 | CPU_MIPS32R6 | ISA_MIPS64R6) +/* Wave Computing: "nanoMIPS" */ +#define CPU_NANOMIPS32 (CPU_MIPS32R6 | ISA_NANOMIPS32) + /* Strictly follow the architecture standard: - Disallow "special" instruction handling for PMON/SPIM. Note that we still maintain Count/Compare to match the host clock. */ diff --git a/target/mips/op_helper.c b/target/mips/op_helper.c index 41d3634289..0f272a5b93 100644 --- a/target/mips/op_helper.c +++ b/target/mips/op_helper.c @@ -249,21 +249,115 @@ target_ulong helper_bitswap(target_ulong rt) return (int32_t)bitswap(rt); } +target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, + uint32_t stripe) +{ + int i; + uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); + uint64_t tmp1 = tmp0; + for (i = 0; i <= 46; i++) { + int s; + if (i & 0x8) { + s = shift; + } else { + s = shiftx; + } + + if (stripe != 0 && !(i & 0x4)) { + s = ~s; + } + if (s & 0x10) { + if (tmp0 & (1LL << (i + 16))) { + tmp1 |= 1LL << i; + } else { + tmp1 &= ~(1LL << i); + } + } + } + + uint64_t tmp2 = tmp1; + for (i = 0; i <= 38; i++) { + int s; + if (i & 0x4) { + s = shift; + } else { + s = shiftx; + } + + if (s & 0x8) { + if (tmp1 & (1LL << (i + 8))) { + tmp2 |= 1LL << i; + } else { + tmp2 &= ~(1LL << i); + } + } + } + + uint64_t tmp3 = tmp2; + for (i = 0; i <= 34; i++) { + int s; + if (i & 0x2) { + s = shift; + } else { + s = shiftx; + } + if (s & 0x4) { + if (tmp2 & (1LL << (i + 4))) { + tmp3 |= 1LL << i; + } else { + tmp3 &= ~(1LL << i); + } + } + } + + uint64_t tmp4 = tmp3; + for (i = 0; i <= 32; i++) { + int s; + if (i & 0x1) { + s = shift; + } else { + s = shiftx; + } + if (s & 0x2) { + if (tmp3 & (1LL << (i + 2))) { + tmp4 |= 1LL << i; + } else { + tmp4 &= ~(1LL << i); + } + } + } + + uint64_t tmp5 = tmp4; + for (i = 0; i <= 31; i++) { + int s; + s = shift; + if (s & 0x1) { + if (tmp4 & (1LL << (i + 1))) { + tmp5 |= 1LL << i; + } else { + tmp5 &= ~(1LL << i); + } + } + } + + return (int64_t)(int32_t)(uint32_t)tmp5; +} + #ifndef CONFIG_USER_ONLY static inline hwaddr do_translate_address(CPUMIPSState *env, target_ulong address, int rw, uintptr_t retaddr) { - hwaddr lladdr; + hwaddr paddr; CPUState *cs = CPU(mips_env_get_cpu(env)); - lladdr = cpu_mips_translate_address(env, address, rw); + paddr = cpu_mips_translate_address(env, address, rw); - if (lladdr == -1LL) { + if (paddr == -1LL) { cpu_loop_exit_restore(cs, retaddr); } else { - return lladdr; + return paddr; } } @@ -271,10 +365,13 @@ static inline hwaddr do_translate_address(CPUMIPSState *env, target_ulong helper_##name(CPUMIPSState *env, target_ulong arg, int mem_idx) \ { \ if (arg & almask) { \ - env->CP0_BadVAddr = arg; \ + if (!(env->hflags & MIPS_HFLAG_DM)) { \ + env->CP0_BadVAddr = arg; \ + } \ do_raise_exception(env, EXCP_AdEL, GETPC()); \ } \ - env->lladdr = do_translate_address(env, arg, 0, GETPC()); \ + env->CP0_LLAddr = do_translate_address(env, arg, 0, GETPC()); \ + env->lladdr = arg; \ env->llval = do_##insn(env, arg, mem_idx, GETPC()); \ return env->llval; \ } @@ -283,31 +380,6 @@ HELPER_LD_ATOMIC(ll, lw, 0x3) HELPER_LD_ATOMIC(lld, ld, 0x7) #endif #undef HELPER_LD_ATOMIC - -#define HELPER_ST_ATOMIC(name, ld_insn, st_insn, almask) \ -target_ulong helper_##name(CPUMIPSState *env, target_ulong arg1, \ - target_ulong arg2, int mem_idx) \ -{ \ - target_long tmp; \ - \ - if (arg2 & almask) { \ - env->CP0_BadVAddr = arg2; \ - do_raise_exception(env, EXCP_AdES, GETPC()); \ - } \ - 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, GETPC()); \ - return 1; \ - } \ - } \ - return 0; \ -} -HELPER_ST_ATOMIC(sc, lw, sw, 0x3) -#ifdef TARGET_MIPS64 -HELPER_ST_ATOMIC(scd, ld, sd, 0x7) -#endif -#undef HELPER_ST_ATOMIC #endif #ifdef TARGET_WORDS_BIGENDIAN @@ -567,7 +639,9 @@ static inline void mips_vpe_wake(MIPSCPU *c) /* Don't set ->halted = 0 directly, let it be done via cpu_has_work because there might be other conditions that state that c should be sleeping. */ + qemu_mutex_lock_iothread(); cpu_interrupt(CPU(c), CPU_INTERRUPT_WAKE); + qemu_mutex_unlock_iothread(); } static inline void mips_vpe_sleep(MIPSCPU *cpu) @@ -833,11 +907,23 @@ target_ulong helper_mftc0_tcschefback(CPUMIPSState *env) target_ulong helper_mfc0_count(CPUMIPSState *env) { - int32_t count; - qemu_mutex_lock_iothread(); - count = (int32_t) cpu_mips_get_count(env); - qemu_mutex_unlock_iothread(); - return count; + return (int32_t)cpu_mips_get_count(env); +} + +target_ulong helper_mfc0_saar(CPUMIPSState *env) +{ + if ((env->CP0_SAARI & 0x3f) < 2) { + return (int32_t) env->CP0_SAAR[env->CP0_SAARI & 0x3f]; + } + return 0; +} + +target_ulong helper_mfhc0_saar(CPUMIPSState *env) +{ + if ((env->CP0_SAARI & 0x3f) < 2) { + return env->CP0_SAAR[env->CP0_SAARI & 0x3f] >> 32; + } + return 0; } target_ulong helper_mftc0_entryhi(CPUMIPSState *env) @@ -873,7 +959,7 @@ target_ulong helper_mftc0_status(CPUMIPSState *env) target_ulong helper_mfc0_lladdr(CPUMIPSState *env) { - return (int32_t)(env->lladdr >> env->CP0_LLAddr_shift); + return (int32_t)(env->CP0_LLAddr >> env->CP0_LLAddr_shift); } target_ulong helper_mfc0_maar(CPUMIPSState *env) @@ -949,7 +1035,7 @@ target_ulong helper_dmfc0_tcschefback(CPUMIPSState *env) target_ulong helper_dmfc0_lladdr(CPUMIPSState *env) { - return env->lladdr >> env->CP0_LLAddr_shift; + return env->CP0_LLAddr >> env->CP0_LLAddr_shift; } target_ulong helper_dmfc0_maar(CPUMIPSState *env) @@ -961,6 +1047,14 @@ target_ulong helper_dmfc0_watchlo(CPUMIPSState *env, uint32_t sel) { return env->CP0_WatchLo[sel]; } + +target_ulong helper_dmfc0_saar(CPUMIPSState *env) +{ + if ((env->CP0_SAARI & 0x3f) < 2) { + return env->CP0_SAAR[env->CP0_SAARI & 0x3f]; + } + return 0; +} #endif /* TARGET_MIPS64 */ void helper_mtc0_index(CPUMIPSState *env, target_ulong arg1) @@ -1177,7 +1271,8 @@ void helper_mtc0_tcrestart(CPUMIPSState *env, target_ulong arg1) { env->active_tc.PC = arg1; env->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); - env->lladdr = 0ULL; + env->CP0_LLAddr = 0; + env->lladdr = 0; /* MIPS16 not implemented. */ } @@ -1189,12 +1284,14 @@ void helper_mttc0_tcrestart(CPUMIPSState *env, target_ulong arg1) if (other_tc == other->current_tc) { other->active_tc.PC = arg1; other->active_tc.CP0_TCStatus &= ~(1 << CP0TCSt_TDS); - other->lladdr = 0ULL; + other->CP0_LLAddr = 0; + other->lladdr = 0; /* MIPS16 not implemented. */ } else { other->tcs[other_tc].PC = arg1; other->tcs[other_tc].CP0_TCStatus &= ~(1 << CP0TCSt_TDS); - other->lladdr = 0ULL; + other->CP0_LLAddr = 0; + other->lladdr = 0; /* MIPS16 not implemented. */ } } @@ -1302,7 +1399,7 @@ void helper_mtc0_context(CPUMIPSState *env, target_ulong arg1) env->CP0_Context = (env->CP0_Context & 0x007FFFFF) | (arg1 & ~0x007FFFFF); } -void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) +void update_pagemask(CPUMIPSState *env, target_ulong arg1, int32_t *pagemask) { uint64_t mask = arg1 >> (TARGET_PAGE_BITS + 1); if (!(env->insn_flags & ISA_MIPS32R6) || (arg1 == ~0) || @@ -1313,6 +1410,11 @@ void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) } } +void helper_mtc0_pagemask(CPUMIPSState *env, target_ulong arg1) +{ + update_pagemask(env, arg1, &env->CP0_PageMask); +} + void helper_mtc0_pagegrain(CPUMIPSState *env, target_ulong arg1) { /* SmartMIPS not implemented */ @@ -1347,6 +1449,77 @@ void helper_mtc0_segctl2(CPUMIPSState *env, target_ulong arg1) tlb_flush(cs); } +void helper_mtc0_pwfield(CPUMIPSState *env, target_ulong arg1) +{ +#if defined(TARGET_MIPS64) + uint64_t mask = 0x3F3FFFFFFFULL; + uint32_t old_ptei = (env->CP0_PWField >> CP0PF_PTEI) & 0x3FULL; + uint32_t new_ptei = (arg1 >> CP0PF_PTEI) & 0x3FULL; + + if ((env->insn_flags & ISA_MIPS32R6)) { + if (((arg1 >> CP0PF_BDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_BDI); + } + if (((arg1 >> CP0PF_GDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_GDI); + } + if (((arg1 >> CP0PF_UDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_UDI); + } + if (((arg1 >> CP0PF_MDI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_MDI); + } + if (((arg1 >> CP0PF_PTI) & 0x3FULL) < 12) { + mask &= ~(0x3FULL << CP0PF_PTI); + } + } + env->CP0_PWField = arg1 & mask; + + if ((new_ptei >= 32) || + ((env->insn_flags & ISA_MIPS32R6) && + (new_ptei == 0 || new_ptei == 1))) { + env->CP0_PWField = (env->CP0_PWField & ~0x3FULL) | + (old_ptei << CP0PF_PTEI); + } +#else + uint32_t mask = 0x3FFFFFFF; + uint32_t old_ptew = (env->CP0_PWField >> CP0PF_PTEW) & 0x3F; + uint32_t new_ptew = (arg1 >> CP0PF_PTEW) & 0x3F; + + if ((env->insn_flags & ISA_MIPS32R6)) { + if (((arg1 >> CP0PF_GDW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_GDW); + } + if (((arg1 >> CP0PF_UDW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_UDW); + } + if (((arg1 >> CP0PF_MDW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_MDW); + } + if (((arg1 >> CP0PF_PTW) & 0x3F) < 12) { + mask &= ~(0x3F << CP0PF_PTW); + } + } + env->CP0_PWField = arg1 & mask; + + if ((new_ptew >= 32) || + ((env->insn_flags & ISA_MIPS32R6) && + (new_ptew == 0 || new_ptew == 1))) { + env->CP0_PWField = (env->CP0_PWField & ~0x3F) | + (old_ptew << CP0PF_PTEW); + } +#endif +} + +void helper_mtc0_pwsize(CPUMIPSState *env, target_ulong arg1) +{ +#if defined(TARGET_MIPS64) + env->CP0_PWSize = arg1 & 0x3F7FFFFFFFULL; +#else + env->CP0_PWSize = arg1 & 0x3FFFFFFF; +#endif +} + void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) { if (env->insn_flags & ISA_MIPS32R6) { @@ -1358,6 +1531,16 @@ void helper_mtc0_wired(CPUMIPSState *env, target_ulong arg1) } } +void helper_mtc0_pwctl(CPUMIPSState *env, target_ulong arg1) +{ +#if defined(TARGET_MIPS64) + /* PWEn = 0. Hardware page table walking is not implemented. */ + env->CP0_PWCtl = (env->CP0_PWCtl & 0x000000C0) | (arg1 & 0x5C00003F); +#else + env->CP0_PWCtl = (arg1 & 0x800000FF); +#endif +} + void helper_mtc0_srsconf0(CPUMIPSState *env, target_ulong arg1) { env->CP0_SRSConf0 |= arg1 & env->CP0_SRSConf0_rw_bitmask; @@ -1409,9 +1592,47 @@ void helper_mtc0_hwrena(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_count(CPUMIPSState *env, target_ulong arg1) { - qemu_mutex_lock_iothread(); cpu_mips_store_count(env, arg1); - qemu_mutex_unlock_iothread(); +} + +void helper_mtc0_saari(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t target = arg1 & 0x3f; + if (target <= 1) { + env->CP0_SAARI = target; + } +} + +void helper_mtc0_saar(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t target = env->CP0_SAARI & 0x3f; + if (target < 2) { + env->CP0_SAAR[target] = arg1 & 0x00000ffffffff03fULL; + switch (target) { + case 0: + if (env->itu) { + itc_reconfigure(env->itu); + } + break; + } + } +} + +void helper_mthc0_saar(CPUMIPSState *env, target_ulong arg1) +{ + uint32_t target = env->CP0_SAARI & 0x3f; + if (target < 2) { + env->CP0_SAAR[target] = + (((uint64_t) arg1 << 32) & 0x00000fff00000000ULL) | + (env->CP0_SAAR[target] & 0x00000000ffffffffULL); + switch (target) { + case 0: + if (env->itu) { + itc_reconfigure(env->itu); + } + break; + } + } } void helper_mtc0_entryhi(CPUMIPSState *env, target_ulong arg1) @@ -1460,9 +1681,7 @@ void helper_mttc0_entryhi(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_compare(CPUMIPSState *env, target_ulong arg1) { - qemu_mutex_lock_iothread(); cpu_mips_store_compare(env, arg1); - qemu_mutex_unlock_iothread(); } void helper_mtc0_status(CPUMIPSState *env, target_ulong arg1) @@ -1516,9 +1735,7 @@ void helper_mtc0_srsctl(CPUMIPSState *env, target_ulong arg1) void helper_mtc0_cause(CPUMIPSState *env, target_ulong arg1) { - qemu_mutex_lock_iothread(); cpu_mips_store_cause(env, arg1); - qemu_mutex_unlock_iothread(); } void helper_mttc0_cause(CPUMIPSState *env, target_ulong arg1) @@ -1620,7 +1837,7 @@ void helper_mtc0_lladdr(CPUMIPSState *env, target_ulong arg1) { target_long mask = env->CP0_LLAddr_rw_bitmask; arg1 = arg1 << env->CP0_LLAddr_shift; - env->lladdr = (env->lladdr & ~mask) | (arg1 & mask); + env->CP0_LLAddr = (env->CP0_LLAddr & ~mask) | (arg1 & mask); } #define MTC0_MAAR_MASK(env) \ @@ -2318,6 +2535,7 @@ static inline void exception_return(CPUMIPSState *env) void helper_eret(CPUMIPSState *env) { exception_return(env); + env->CP0_LLAddr = 1; env->lladdr = 1; } @@ -2329,10 +2547,12 @@ void helper_eretnc(CPUMIPSState *env) void helper_deret(CPUMIPSState *env) { debug_pre_eret(env); - set_pc(env, env->CP0_DEPC); env->hflags &= ~MIPS_HFLAG_DM; compute_hflags(env); + + set_pc(env, env->CP0_DEPC); + debug_post_eret(env); } #endif /* !CONFIG_USER_ONLY */ @@ -2359,16 +2579,12 @@ target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) target_ulong helper_rdhwr_cc(CPUMIPSState *env) { - int32_t count; check_hwrena(env, 2, GETPC()); #ifdef CONFIG_USER_ONLY - count = env->CP0_Count; + return env->CP0_Count; #else - qemu_mutex_lock_iothread(); - count = (int32_t)cpu_mips_get_count(env); - qemu_mutex_unlock_iothread(); + return (int32_t)cpu_mips_get_count(env); #endif - return count; } target_ulong helper_rdhwr_ccres(CPUMIPSState *env) @@ -2437,7 +2653,9 @@ void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, int error_code = 0; int excp; - env->CP0_BadVAddr = addr; + if (!(env->hflags & MIPS_HFLAG_DM)) { + env->CP0_BadVAddr = addr; + } if (access_type == MMU_DATA_STORE) { excp = EXCP_AdES; diff --git a/target/mips/trace-events b/target/mips/trace-events index 05eafd7870..ba87fe6062 100644 --- a/target/mips/trace-events +++ b/target/mips/trace-events @@ -1,5 +1,5 @@ # See docs/devel/tracing.txt for syntax documentation. -# target/mips/translate.c +# translate.c mips_translate_c0(const char *instr, const char *rn, int reg, int sel) "%s %s (reg %d sel %d)" mips_translate_tr(const char *instr, int rt, int u, int sel, int h) "%s (reg %d u %d sel %d h %d)" diff --git a/target/mips/translate.c b/target/mips/translate.c index 20b43c0337..364bd6dc4f 100644 --- a/target/mips/translate.c +++ b/target/mips/translate.c @@ -1,5 +1,5 @@ /* - * MIPS32 emulation for qemu: main translation routines. + * MIPS emulation for QEMU - main translation routines * * Copyright (c) 2004-2005 Jocelyn Mayer * Copyright (c) 2006 Marius Groeger (FPU operations) @@ -463,8 +463,10 @@ enum { OPC_WSBH = (0x02 << 6) | OPC_BSHFL, OPC_SEB = (0x10 << 6) | OPC_BSHFL, OPC_SEH = (0x18 << 6) | OPC_BSHFL, - OPC_ALIGN = (0x08 << 6) | OPC_BSHFL, /* 010.bp */ - OPC_ALIGN_END = (0x0B << 6) | OPC_BSHFL, /* 010.00 to 010.11 */ + OPC_ALIGN = (0x08 << 6) | OPC_BSHFL, /* 010.bp (010.00 to 010.11) */ + OPC_ALIGN_1 = (0x09 << 6) | OPC_BSHFL, + OPC_ALIGN_2 = (0x0A << 6) | OPC_BSHFL, + OPC_ALIGN_3 = (0x0B << 6) | OPC_BSHFL, OPC_BITSWAP = (0x00 << 6) | OPC_BSHFL /* 00000 */ }; @@ -474,8 +476,14 @@ enum { enum { OPC_DSBH = (0x02 << 6) | OPC_DBSHFL, OPC_DSHD = (0x05 << 6) | OPC_DBSHFL, - OPC_DALIGN = (0x08 << 6) | OPC_DBSHFL, /* 01.bp */ - OPC_DALIGN_END = (0x0F << 6) | OPC_DBSHFL, /* 01.000 to 01.111 */ + OPC_DALIGN = (0x08 << 6) | OPC_DBSHFL, /* 01.bp (01.000 to 01.111) */ + OPC_DALIGN_1 = (0x09 << 6) | OPC_DBSHFL, + OPC_DALIGN_2 = (0x0A << 6) | OPC_DBSHFL, + OPC_DALIGN_3 = (0x0B << 6) | OPC_DBSHFL, + OPC_DALIGN_4 = (0x0C << 6) | OPC_DBSHFL, + OPC_DALIGN_5 = (0x0D << 6) | OPC_DBSHFL, + OPC_DALIGN_6 = (0x0E << 6) | OPC_DBSHFL, + OPC_DALIGN_7 = (0x0F << 6) | OPC_DBSHFL, OPC_DBITSWAP = (0x00 << 6) | OPC_DBSHFL, /* 00000 */ }; @@ -902,8 +910,21 @@ enum { OPC_MTTR = (0x0C << 21) | OPC_CP0, OPC_WRPGPR = (0x0E << 21) | OPC_CP0, OPC_C0 = (0x10 << 21) | OPC_CP0, - OPC_C0_FIRST = (0x10 << 21) | OPC_CP0, - OPC_C0_LAST = (0x1F << 21) | OPC_CP0, + OPC_C0_1 = (0x11 << 21) | OPC_CP0, + OPC_C0_2 = (0x12 << 21) | OPC_CP0, + OPC_C0_3 = (0x13 << 21) | OPC_CP0, + OPC_C0_4 = (0x14 << 21) | OPC_CP0, + OPC_C0_5 = (0x15 << 21) | OPC_CP0, + OPC_C0_6 = (0x16 << 21) | OPC_CP0, + OPC_C0_7 = (0x17 << 21) | OPC_CP0, + OPC_C0_8 = (0x18 << 21) | OPC_CP0, + OPC_C0_9 = (0x19 << 21) | OPC_CP0, + OPC_C0_A = (0x1A << 21) | OPC_CP0, + OPC_C0_B = (0x1B << 21) | OPC_CP0, + OPC_C0_C = (0x1C << 21) | OPC_CP0, + OPC_C0_D = (0x1D << 21) | OPC_CP0, + OPC_C0_E = (0x1E << 21) | OPC_CP0, + OPC_C0_F = (0x1F << 21) | OPC_CP0, }; /* MFMC0 opcodes */ @@ -1376,15 +1397,1076 @@ enum { OPC_BINSRI_df = (0x7 << 23) | OPC_MSA_BIT_09, }; + +/* + * + * AN OVERVIEW OF MXU EXTENSION INSTRUCTION SET + * ============================================ + * + * + * MXU (full name: MIPS eXtension/enhanced Unit) is a SIMD extension of MIPS32 + * instructions set. It is designed to fit the needs of signal, graphical and + * video processing applications. MXU instruction set is used in Xburst family + * of microprocessors by Ingenic. + * + * MXU unit contains 17 registers called X0-X16. X0 is always zero, and X16 is + * the control register. + * + * + * The notation used in MXU assembler mnemonics + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Register operands: + * + * XRa, XRb, XRc, XRd - MXU registers + * Rb, Rc, Rd, Rs, Rt - general purpose MIPS registers + * + * Non-register operands: + * + * aptn1 - 1-bit accumulate add/subtract pattern + * aptn2 - 2-bit accumulate add/subtract pattern + * eptn2 - 2-bit execute add/subtract pattern + * optn2 - 2-bit operand pattern + * optn3 - 3-bit operand pattern + * sft4 - 4-bit shift amount + * strd2 - 2-bit stride amount + * + * Prefixes: + * + * Level of parallelism: Operand size: + * S - single operation at a time 32 - word + * D - two operations in parallel 16 - half word + * Q - four operations in parallel 8 - byte + * + * Operations: + * + * ADD - Add or subtract + * ADDC - Add with carry-in + * ACC - Accumulate + * ASUM - Sum together then accumulate (add or subtract) + * ASUMC - Sum together then accumulate (add or subtract) with carry-in + * AVG - Average between 2 operands + * ABD - Absolute difference + * ALN - Align data + * AND - Logical bitwise 'and' operation + * CPS - Copy sign + * EXTR - Extract bits + * I2M - Move from GPR register to MXU register + * LDD - Load data from memory to XRF + * LDI - Load data from memory to XRF (and increase the address base) + * LUI - Load unsigned immediate + * MUL - Multiply + * MULU - Unsigned multiply + * MADD - 64-bit operand add 32x32 product + * MSUB - 64-bit operand subtract 32x32 product + * MAC - Multiply and accumulate (add or subtract) + * MAD - Multiply and add or subtract + * MAX - Maximum between 2 operands + * MIN - Minimum between 2 operands + * M2I - Move from MXU register to GPR register + * MOVZ - Move if zero + * MOVN - Move if non-zero + * NOR - Logical bitwise 'nor' operation + * OR - Logical bitwise 'or' operation + * STD - Store data from XRF to memory + * SDI - Store data from XRF to memory (and increase the address base) + * SLT - Set of less than comparison + * SAD - Sum of absolute differences + * SLL - Logical shift left + * SLR - Logical shift right + * SAR - Arithmetic shift right + * SAT - Saturation + * SFL - Shuffle + * SCOP - Calculate x’s scope (-1, means x<0; 0, means x==0; 1, means x>0) + * XOR - Logical bitwise 'exclusive or' operation + * + * Suffixes: + * + * E - Expand results + * F - Fixed point multiplication + * L - Low part result + * R - Doing rounding + * V - Variable instead of immediate + * W - Combine above L and V + * + * + * The list of MXU instructions grouped by functionality + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * Load/Store instructions Multiplication instructions + * ----------------------- --------------------------- + * + * S32LDD XRa, Rb, s12 S32MADD XRa, XRd, Rs, Rt + * S32STD XRa, Rb, s12 S32MADDU XRa, XRd, Rs, Rt + * S32LDDV XRa, Rb, rc, strd2 S32MSUB XRa, XRd, Rs, Rt + * S32STDV XRa, Rb, rc, strd2 S32MSUBU XRa, XRd, Rs, Rt + * S32LDI XRa, Rb, s12 S32MUL XRa, XRd, Rs, Rt + * S32SDI XRa, Rb, s12 S32MULU XRa, XRd, Rs, Rt + * S32LDIV XRa, Rb, rc, strd2 D16MUL XRa, XRb, XRc, XRd, optn2 + * S32SDIV XRa, Rb, rc, strd2 D16MULE XRa, XRb, XRc, optn2 + * S32LDDR XRa, Rb, s12 D16MULF XRa, XRb, XRc, optn2 + * S32STDR XRa, Rb, s12 D16MAC XRa, XRb, XRc, XRd, aptn2, optn2 + * S32LDDVR XRa, Rb, rc, strd2 D16MACE XRa, XRb, XRc, XRd, aptn2, optn2 + * S32STDVR XRa, Rb, rc, strd2 D16MACF XRa, XRb, XRc, XRd, aptn2, optn2 + * S32LDIR XRa, Rb, s12 D16MADL XRa, XRb, XRc, XRd, aptn2, optn2 + * S32SDIR XRa, Rb, s12 S16MAD XRa, XRb, XRc, XRd, aptn1, optn2 + * S32LDIVR XRa, Rb, rc, strd2 Q8MUL XRa, XRb, XRc, XRd + * S32SDIVR XRa, Rb, rc, strd2 Q8MULSU XRa, XRb, XRc, XRd + * S16LDD XRa, Rb, s10, eptn2 Q8MAC XRa, XRb, XRc, XRd, aptn2 + * S16STD XRa, Rb, s10, eptn2 Q8MACSU XRa, XRb, XRc, XRd, aptn2 + * S16LDI XRa, Rb, s10, eptn2 Q8MADL XRa, XRb, XRc, XRd, aptn2 + * S16SDI XRa, Rb, s10, eptn2 + * S8LDD XRa, Rb, s8, eptn3 + * S8STD XRa, Rb, s8, eptn3 Addition and subtraction instructions + * S8LDI XRa, Rb, s8, eptn3 ------------------------------------- + * S8SDI XRa, Rb, s8, eptn3 + * LXW Rd, Rs, Rt, strd2 D32ADD XRa, XRb, XRc, XRd, eptn2 + * LXH Rd, Rs, Rt, strd2 D32ADDC XRa, XRb, XRc, XRd + * LXHU Rd, Rs, Rt, strd2 D32ACC XRa, XRb, XRc, XRd, eptn2 + * LXB Rd, Rs, Rt, strd2 D32ACCM XRa, XRb, XRc, XRd, eptn2 + * LXBU Rd, Rs, Rt, strd2 D32ASUM XRa, XRb, XRc, XRd, eptn2 + * S32CPS XRa, XRb, XRc + * Q16ADD XRa, XRb, XRc, XRd, eptn2, optn2 + * Comparison instructions Q16ACC XRa, XRb, XRc, XRd, eptn2 + * ----------------------- Q16ACCM XRa, XRb, XRc, XRd, eptn2 + * D16ASUM XRa, XRb, XRc, XRd, eptn2 + * S32MAX XRa, XRb, XRc D16CPS XRa, XRb, + * S32MIN XRa, XRb, XRc D16AVG XRa, XRb, XRc + * S32SLT XRa, XRb, XRc D16AVGR XRa, XRb, XRc + * S32MOVZ XRa, XRb, XRc Q8ADD XRa, XRb, XRc, eptn2 + * S32MOVN XRa, XRb, XRc Q8ADDE XRa, XRb, XRc, XRd, eptn2 + * D16MAX XRa, XRb, XRc Q8ACCE XRa, XRb, XRc, XRd, eptn2 + * D16MIN XRa, XRb, XRc Q8ABD XRa, XRb, XRc + * D16SLT XRa, XRb, XRc Q8SAD XRa, XRb, XRc, XRd + * D16MOVZ XRa, XRb, XRc Q8AVG XRa, XRb, XRc + * D16MOVN XRa, XRb, XRc Q8AVGR XRa, XRb, XRc + * Q8MAX XRa, XRb, XRc D8SUM XRa, XRb, XRc, XRd + * Q8MIN XRa, XRb, XRc D8SUMC XRa, XRb, XRc, XRd + * Q8SLT XRa, XRb, XRc + * Q8SLTU XRa, XRb, XRc + * Q8MOVZ XRa, XRb, XRc Shift instructions + * Q8MOVN XRa, XRb, XRc ------------------ + * + * D32SLL XRa, XRb, XRc, XRd, sft4 + * Bitwise instructions D32SLR XRa, XRb, XRc, XRd, sft4 + * -------------------- D32SAR XRa, XRb, XRc, XRd, sft4 + * D32SARL XRa, XRb, XRc, sft4 + * S32NOR XRa, XRb, XRc D32SLLV XRa, XRb, Rb + * S32AND XRa, XRb, XRc D32SLRV XRa, XRb, Rb + * S32XOR XRa, XRb, XRc D32SARV XRa, XRb, Rb + * S32OR XRa, XRb, XRc D32SARW XRa, XRb, XRc, Rb + * Q16SLL XRa, XRb, XRc, XRd, sft4 + * Q16SLR XRa, XRb, XRc, XRd, sft4 + * Miscellaneous instructions Q16SAR XRa, XRb, XRc, XRd, sft4 + * ------------------------- Q16SLLV XRa, XRb, Rb + * Q16SLRV XRa, XRb, Rb + * S32SFL XRa, XRb, XRc, XRd, optn2 Q16SARV XRa, XRb, Rb + * S32ALN XRa, XRb, XRc, Rb + * S32ALNI XRa, XRb, XRc, s3 + * S32LUI XRa, s8, optn3 Move instructions + * S32EXTR XRa, XRb, Rb, bits5 ----------------- + * S32EXTRV XRa, XRb, Rs, Rt + * Q16SCOP XRa, XRb, XRc, XRd S32M2I XRa, Rb + * Q16SAT XRa, XRb, XRc S32I2M XRa, Rb + * + * + * The opcode organization of MXU instructions + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * The bits 31..26 of all MXU instructions are equal to 0x1C (also referred + * as opcode SPECIAL2 in the base MIPS ISA). The organization and meaning of + * other bits up to the instruction level is as follows: + * + * bits + * 05..00 + * + * ┌─ 000000 ─ OPC_MXU_S32MADD + * ├─ 000001 ─ OPC_MXU_S32MADDU + * ├─ 000010 ─ (non-MXU OPC_MUL) + * │ + * │ 20..18 + * ├─ 000011 ─ OPC_MXU__POOL00 ─┬─ 000 ─ OPC_MXU_S32MAX + * │ ├─ 001 ─ OPC_MXU_S32MIN + * │ ├─ 010 ─ OPC_MXU_D16MAX + * │ ├─ 011 ─ OPC_MXU_D16MIN + * │ ├─ 100 ─ OPC_MXU_Q8MAX + * │ ├─ 101 ─ OPC_MXU_Q8MIN + * │ ├─ 110 ─ OPC_MXU_Q8SLT + * │ └─ 111 ─ OPC_MXU_Q8SLTU + * ├─ 000100 ─ OPC_MXU_S32MSUB + * ├─ 000101 ─ OPC_MXU_S32MSUBU 20..18 + * ├─ 000110 ─ OPC_MXU__POOL01 ─┬─ 000 ─ OPC_MXU_S32SLT + * │ ├─ 001 ─ OPC_MXU_D16SLT + * │ ├─ 010 ─ OPC_MXU_D16AVG + * │ ├─ 011 ─ OPC_MXU_D16AVGR + * │ ├─ 100 ─ OPC_MXU_Q8AVG + * │ ├─ 101 ─ OPC_MXU_Q8AVGR + * │ └─ 111 ─ OPC_MXU_Q8ADD + * │ + * │ 20..18 + * ├─ 000111 ─ OPC_MXU__POOL02 ─┬─ 000 ─ OPC_MXU_S32CPS + * │ ├─ 010 ─ OPC_MXU_D16CPS + * │ ├─ 100 ─ OPC_MXU_Q8ABD + * │ └─ 110 ─ OPC_MXU_Q16SAT + * ├─ 001000 ─ OPC_MXU_D16MUL + * │ 25..24 + * ├─ 001001 ─ OPC_MXU__POOL03 ─┬─ 00 ─ OPC_MXU_D16MULF + * │ └─ 01 ─ OPC_MXU_D16MULE + * ├─ 001010 ─ OPC_MXU_D16MAC + * ├─ 001011 ─ OPC_MXU_D16MACF + * ├─ 001100 ─ OPC_MXU_D16MADL + * ├─ 001101 ─ OPC_MXU_S16MAD + * ├─ 001110 ─ OPC_MXU_Q16ADD + * ├─ 001111 ─ OPC_MXU_D16MACE 23 + * │ ┌─ 0 ─ OPC_MXU_S32LDD + * ├─ 010000 ─ OPC_MXU__POOL04 ─┴─ 1 ─ OPC_MXU_S32LDDR + * │ + * │ 23 + * ├─ 010001 ─ OPC_MXU__POOL05 ─┬─ 0 ─ OPC_MXU_S32STD + * │ └─ 1 ─ OPC_MXU_S32STDR + * │ + * │ 13..10 + * ├─ 010010 ─ OPC_MXU__POOL06 ─┬─ 0000 ─ OPC_MXU_S32LDDV + * │ └─ 0001 ─ OPC_MXU_S32LDDVR + * │ + * │ 13..10 + * ├─ 010011 ─ OPC_MXU__POOL07 ─┬─ 0000 ─ OPC_MXU_S32STDV + * │ └─ 0001 ─ OPC_MXU_S32STDVR + * │ + * │ 23 + * ├─ 010100 ─ OPC_MXU__POOL08 ─┬─ 0 ─ OPC_MXU_S32LDI + * │ └─ 1 ─ OPC_MXU_S32LDIR + * │ + * │ 23 + * ├─ 010101 ─ OPC_MXU__POOL09 ─┬─ 0 ─ OPC_MXU_S32SDI + * │ └─ 1 ─ OPC_MXU_S32SDIR + * │ + * │ 13..10 + * ├─ 010110 ─ OPC_MXU__POOL10 ─┬─ 0000 ─ OPC_MXU_S32LDIV + * │ └─ 0001 ─ OPC_MXU_S32LDIVR + * │ + * │ 13..10 + * ├─ 010111 ─ OPC_MXU__POOL11 ─┬─ 0000 ─ OPC_MXU_S32SDIV + * │ └─ 0001 ─ OPC_MXU_S32SDIVR + * ├─ 011000 ─ OPC_MXU_D32ADD + * │ 23..22 + * MXU ├─ 011001 ─ OPC_MXU__POOL12 ─┬─ 00 ─ OPC_MXU_D32ACC + * opcodes ─┤ ├─ 01 ─ OPC_MXU_D32ACCM + * │ └─ 10 ─ OPC_MXU_D32ASUM + * ├─ 011010 ─ + * │ 23..22 + * ├─ 011011 ─ OPC_MXU__POOL13 ─┬─ 00 ─ OPC_MXU_Q16ACC + * │ ├─ 01 ─ OPC_MXU_Q16ACCM + * │ └─ 10 ─ OPC_MXU_Q16ASUM + * │ + * │ 23..22 + * ├─ 011100 ─ OPC_MXU__POOL14 ─┬─ 00 ─ OPC_MXU_Q8ADDE + * │ ├─ 01 ─ OPC_MXU_D8SUM + * ├─ 011101 ─ OPC_MXU_Q8ACCE └─ 10 ─ OPC_MXU_D8SUMC + * ├─ 011110 ─ + * ├─ 011111 ─ + * ├─ 100000 ─ (overlaps with CLZ) + * ├─ 100001 ─ (overlaps with CLO) + * ├─ 100010 ─ OPC_MXU_S8LDD + * ├─ 100011 ─ OPC_MXU_S8STD 15..14 + * ├─ 100100 ─ OPC_MXU_S8LDI ┌─ 00 ─ OPC_MXU_S32MUL + * ├─ 100101 ─ OPC_MXU_S8SDI ├─ 00 ─ OPC_MXU_S32MULU + * │ ├─ 00 ─ OPC_MXU_S32EXTR + * ├─ 100110 ─ OPC_MXU__POOL15 ─┴─ 00 ─ OPC_MXU_S32EXTRV + * │ + * │ 20..18 + * ├─ 100111 ─ OPC_MXU__POOL16 ─┬─ 000 ─ OPC_MXU_D32SARW + * │ ├─ 001 ─ OPC_MXU_S32ALN + * │ ├─ 010 ─ OPC_MXU_S32ALNI + * │ ├─ 011 ─ OPC_MXU_S32LUI + * │ ├─ 100 ─ OPC_MXU_S32NOR + * │ ├─ 101 ─ OPC_MXU_S32AND + * │ ├─ 110 ─ OPC_MXU_S32OR + * │ └─ 111 ─ OPC_MXU_S32XOR + * │ + * │ 7..5 + * ├─ 101000 ─ OPC_MXU__POOL17 ─┬─ 000 ─ OPC_MXU_LXB + * │ ├─ 001 ─ OPC_MXU_LXH + * ├─ 101001 ─ ├─ 011 ─ OPC_MXU_LXW + * ├─ 101010 ─ OPC_MXU_S16LDD ├─ 100 ─ OPC_MXU_LXBU + * ├─ 101011 ─ OPC_MXU_S16STD └─ 101 ─ OPC_MXU_LXHU + * ├─ 101100 ─ OPC_MXU_S16LDI + * ├─ 101101 ─ OPC_MXU_S16SDI + * ├─ 101110 ─ OPC_MXU_S32M2I + * ├─ 101111 ─ OPC_MXU_S32I2M + * ├─ 110000 ─ OPC_MXU_D32SLL + * ├─ 110001 ─ OPC_MXU_D32SLR 20..18 + * ├─ 110010 ─ OPC_MXU_D32SARL ┌─ 000 ─ OPC_MXU_D32SLLV + * ├─ 110011 ─ OPC_MXU_D32SAR ├─ 001 ─ OPC_MXU_D32SLRV + * ├─ 110100 ─ OPC_MXU_Q16SLL ├─ 010 ─ OPC_MXU_D32SARV + * ├─ 110101 ─ OPC_MXU_Q16SLR ├─ 011 ─ OPC_MXU_Q16SLLV + * │ ├─ 100 ─ OPC_MXU_Q16SLRV + * ├─ 110110 ─ OPC_MXU__POOL18 ─┴─ 101 ─ OPC_MXU_Q16SARV + * │ + * ├─ 110111 ─ OPC_MXU_Q16SAR + * │ 23..22 + * ├─ 111000 ─ OPC_MXU__POOL19 ─┬─ 00 ─ OPC_MXU_Q8MUL + * │ └─ 01 ─ OPC_MXU_Q8MULSU + * │ + * │ 20..18 + * ├─ 111001 ─ OPC_MXU__POOL20 ─┬─ 000 ─ OPC_MXU_Q8MOVZ + * │ ├─ 001 ─ OPC_MXU_Q8MOVN + * │ ├─ 010 ─ OPC_MXU_D16MOVZ + * │ ├─ 011 ─ OPC_MXU_D16MOVN + * │ ├─ 100 ─ OPC_MXU_S32MOVZ + * │ └─ 101 ─ OPC_MXU_S32MOVN + * │ + * │ 23..22 + * ├─ 111010 ─ OPC_MXU__POOL21 ─┬─ 00 ─ OPC_MXU_Q8MAC + * │ └─ 10 ─ OPC_MXU_Q8MACSU + * ├─ 111011 ─ OPC_MXU_Q16SCOP + * ├─ 111100 ─ OPC_MXU_Q8MADL + * ├─ 111101 ─ OPC_MXU_S32SFL + * ├─ 111110 ─ OPC_MXU_Q8SAD + * └─ 111111 ─ (overlaps with SDBBP) + * + * + * Compiled after: + * + * "XBurst® Instruction Set Architecture MIPS eXtension/enhanced Unit + * Programming Manual", Ingenic Semiconductor Co, Ltd., revision June 2, 2017 + */ + +enum { + OPC_MXU_S32MADD = 0x00, + OPC_MXU_S32MADDU = 0x01, + OPC__MXU_MUL = 0x02, + OPC_MXU__POOL00 = 0x03, + OPC_MXU_S32MSUB = 0x04, + OPC_MXU_S32MSUBU = 0x05, + OPC_MXU__POOL01 = 0x06, + OPC_MXU__POOL02 = 0x07, + OPC_MXU_D16MUL = 0x08, + OPC_MXU__POOL03 = 0x09, + OPC_MXU_D16MAC = 0x0A, + OPC_MXU_D16MACF = 0x0B, + OPC_MXU_D16MADL = 0x0C, + OPC_MXU_S16MAD = 0x0D, + OPC_MXU_Q16ADD = 0x0E, + OPC_MXU_D16MACE = 0x0F, + OPC_MXU__POOL04 = 0x10, + OPC_MXU__POOL05 = 0x11, + OPC_MXU__POOL06 = 0x12, + OPC_MXU__POOL07 = 0x13, + OPC_MXU__POOL08 = 0x14, + OPC_MXU__POOL09 = 0x15, + OPC_MXU__POOL10 = 0x16, + OPC_MXU__POOL11 = 0x17, + OPC_MXU_D32ADD = 0x18, + OPC_MXU__POOL12 = 0x19, + /* not assigned 0x1A */ + OPC_MXU__POOL13 = 0x1B, + OPC_MXU__POOL14 = 0x1C, + OPC_MXU_Q8ACCE = 0x1D, + /* not assigned 0x1E */ + /* not assigned 0x1F */ + /* not assigned 0x20 */ + /* not assigned 0x21 */ + OPC_MXU_S8LDD = 0x22, + OPC_MXU_S8STD = 0x23, + OPC_MXU_S8LDI = 0x24, + OPC_MXU_S8SDI = 0x25, + OPC_MXU__POOL15 = 0x26, + OPC_MXU__POOL16 = 0x27, + OPC_MXU__POOL17 = 0x28, + /* not assigned 0x29 */ + OPC_MXU_S16LDD = 0x2A, + OPC_MXU_S16STD = 0x2B, + OPC_MXU_S16LDI = 0x2C, + OPC_MXU_S16SDI = 0x2D, + OPC_MXU_S32M2I = 0x2E, + OPC_MXU_S32I2M = 0x2F, + OPC_MXU_D32SLL = 0x30, + OPC_MXU_D32SLR = 0x31, + OPC_MXU_D32SARL = 0x32, + OPC_MXU_D32SAR = 0x33, + OPC_MXU_Q16SLL = 0x34, + OPC_MXU_Q16SLR = 0x35, + OPC_MXU__POOL18 = 0x36, + OPC_MXU_Q16SAR = 0x37, + OPC_MXU__POOL19 = 0x38, + OPC_MXU__POOL20 = 0x39, + OPC_MXU__POOL21 = 0x3A, + OPC_MXU_Q16SCOP = 0x3B, + OPC_MXU_Q8MADL = 0x3C, + OPC_MXU_S32SFL = 0x3D, + OPC_MXU_Q8SAD = 0x3E, + /* not assigned 0x3F */ +}; + + +/* + * MXU pool 00 + */ +enum { + OPC_MXU_S32MAX = 0x00, + OPC_MXU_S32MIN = 0x01, + OPC_MXU_D16MAX = 0x02, + OPC_MXU_D16MIN = 0x03, + OPC_MXU_Q8MAX = 0x04, + OPC_MXU_Q8MIN = 0x05, + OPC_MXU_Q8SLT = 0x06, + OPC_MXU_Q8SLTU = 0x07, +}; + +/* + * MXU pool 01 + */ +enum { + OPC_MXU_S32SLT = 0x00, + OPC_MXU_D16SLT = 0x01, + OPC_MXU_D16AVG = 0x02, + OPC_MXU_D16AVGR = 0x03, + OPC_MXU_Q8AVG = 0x04, + OPC_MXU_Q8AVGR = 0x05, + OPC_MXU_Q8ADD = 0x07, +}; + +/* + * MXU pool 02 + */ +enum { + OPC_MXU_S32CPS = 0x00, + OPC_MXU_D16CPS = 0x02, + OPC_MXU_Q8ABD = 0x04, + OPC_MXU_Q16SAT = 0x06, +}; + +/* + * MXU pool 03 + */ +enum { + OPC_MXU_D16MULF = 0x00, + OPC_MXU_D16MULE = 0x01, +}; + +/* + * MXU pool 04 + */ +enum { + OPC_MXU_S32LDD = 0x00, + OPC_MXU_S32LDDR = 0x01, +}; + +/* + * MXU pool 05 + */ +enum { + OPC_MXU_S32STD = 0x00, + OPC_MXU_S32STDR = 0x01, +}; + +/* + * MXU pool 06 + */ +enum { + OPC_MXU_S32LDDV = 0x00, + OPC_MXU_S32LDDVR = 0x01, +}; + +/* + * MXU pool 07 + */ +enum { + OPC_MXU_S32STDV = 0x00, + OPC_MXU_S32STDVR = 0x01, +}; + +/* + * MXU pool 08 + */ +enum { + OPC_MXU_S32LDI = 0x00, + OPC_MXU_S32LDIR = 0x01, +}; + +/* + * MXU pool 09 + */ +enum { + OPC_MXU_S32SDI = 0x00, + OPC_MXU_S32SDIR = 0x01, +}; + +/* + * MXU pool 10 + */ +enum { + OPC_MXU_S32LDIV = 0x00, + OPC_MXU_S32LDIVR = 0x01, +}; + +/* + * MXU pool 11 + */ +enum { + OPC_MXU_S32SDIV = 0x00, + OPC_MXU_S32SDIVR = 0x01, +}; + +/* + * MXU pool 12 + */ +enum { + OPC_MXU_D32ACC = 0x00, + OPC_MXU_D32ACCM = 0x01, + OPC_MXU_D32ASUM = 0x02, +}; + +/* + * MXU pool 13 + */ +enum { + OPC_MXU_Q16ACC = 0x00, + OPC_MXU_Q16ACCM = 0x01, + OPC_MXU_Q16ASUM = 0x02, +}; + +/* + * MXU pool 14 + */ +enum { + OPC_MXU_Q8ADDE = 0x00, + OPC_MXU_D8SUM = 0x01, + OPC_MXU_D8SUMC = 0x02, +}; + +/* + * MXU pool 15 + */ +enum { + OPC_MXU_S32MUL = 0x00, + OPC_MXU_S32MULU = 0x01, + OPC_MXU_S32EXTR = 0x02, + OPC_MXU_S32EXTRV = 0x03, +}; + +/* + * MXU pool 16 + */ +enum { + OPC_MXU_D32SARW = 0x00, + OPC_MXU_S32ALN = 0x01, + OPC_MXU_S32ALNI = 0x02, + OPC_MXU_S32LUI = 0x03, + OPC_MXU_S32NOR = 0x04, + OPC_MXU_S32AND = 0x05, + OPC_MXU_S32OR = 0x06, + OPC_MXU_S32XOR = 0x07, +}; + +/* + * MXU pool 17 + */ +enum { + OPC_MXU_LXB = 0x00, + OPC_MXU_LXH = 0x01, + OPC_MXU_LXW = 0x03, + OPC_MXU_LXBU = 0x04, + OPC_MXU_LXHU = 0x05, +}; + +/* + * MXU pool 18 + */ +enum { + OPC_MXU_D32SLLV = 0x00, + OPC_MXU_D32SLRV = 0x01, + OPC_MXU_D32SARV = 0x03, + OPC_MXU_Q16SLLV = 0x04, + OPC_MXU_Q16SLRV = 0x05, + OPC_MXU_Q16SARV = 0x07, +}; + +/* + * MXU pool 19 + */ +enum { + OPC_MXU_Q8MUL = 0x00, + OPC_MXU_Q8MULSU = 0x01, +}; + +/* + * MXU pool 20 + */ +enum { + OPC_MXU_Q8MOVZ = 0x00, + OPC_MXU_Q8MOVN = 0x01, + OPC_MXU_D16MOVZ = 0x02, + OPC_MXU_D16MOVN = 0x03, + OPC_MXU_S32MOVZ = 0x04, + OPC_MXU_S32MOVN = 0x05, +}; + +/* + * MXU pool 21 + */ +enum { + OPC_MXU_Q8MAC = 0x00, + OPC_MXU_Q8MACSU = 0x01, +}; + +/* + * Overview of the TX79-specific instruction set + * ============================================= + * + * The R5900 and the C790 have 128-bit wide GPRs, where the upper 64 bits + * are only used by the specific quadword (128-bit) LQ/SQ load/store + * instructions and certain multimedia instructions (MMIs). These MMIs + * configure the 128-bit data path as two 64-bit, four 32-bit, eight 16-bit + * or sixteen 8-bit paths. + * + * Reference: + * + * The Toshiba TX System RISC TX79 Core Architecture manual, + * https://wiki.qemu.org/File:C790.pdf + * + * Three-Operand Multiply and Multiply-Add (4 instructions) + * -------------------------------------------------------- + * MADD [rd,] rs, rt Multiply/Add + * MADDU [rd,] rs, rt Multiply/Add Unsigned + * MULT [rd,] rs, rt Multiply (3-operand) + * MULTU [rd,] rs, rt Multiply Unsigned (3-operand) + * + * Multiply Instructions for Pipeline 1 (10 instructions) + * ------------------------------------------------------ + * MULT1 [rd,] rs, rt Multiply Pipeline 1 + * MULTU1 [rd,] rs, rt Multiply Unsigned Pipeline 1 + * DIV1 rs, rt Divide Pipeline 1 + * DIVU1 rs, rt Divide Unsigned Pipeline 1 + * MADD1 [rd,] rs, rt Multiply-Add Pipeline 1 + * MADDU1 [rd,] rs, rt Multiply-Add Unsigned Pipeline 1 + * MFHI1 rd Move From HI1 Register + * MFLO1 rd Move From LO1 Register + * MTHI1 rs Move To HI1 Register + * MTLO1 rs Move To LO1 Register + * + * Arithmetic (19 instructions) + * ---------------------------- + * PADDB rd, rs, rt Parallel Add Byte + * PSUBB rd, rs, rt Parallel Subtract Byte + * PADDH rd, rs, rt Parallel Add Halfword + * PSUBH rd, rs, rt Parallel Subtract Halfword + * PADDW rd, rs, rt Parallel Add Word + * PSUBW rd, rs, rt Parallel Subtract Word + * PADSBH rd, rs, rt Parallel Add/Subtract Halfword + * PADDSB rd, rs, rt Parallel Add with Signed Saturation Byte + * PSUBSB rd, rs, rt Parallel Subtract with Signed Saturation Byte + * PADDSH rd, rs, rt Parallel Add with Signed Saturation Halfword + * PSUBSH rd, rs, rt Parallel Subtract with Signed Saturation Halfword + * PADDSW rd, rs, rt Parallel Add with Signed Saturation Word + * PSUBSW rd, rs, rt Parallel Subtract with Signed Saturation Word + * PADDUB rd, rs, rt Parallel Add with Unsigned saturation Byte + * PSUBUB rd, rs, rt Parallel Subtract with Unsigned saturation Byte + * PADDUH rd, rs, rt Parallel Add with Unsigned saturation Halfword + * PSUBUH rd, rs, rt Parallel Subtract with Unsigned saturation Halfword + * PADDUW rd, rs, rt Parallel Add with Unsigned saturation Word + * PSUBUW rd, rs, rt Parallel Subtract with Unsigned saturation Word + * + * Min/Max (4 instructions) + * ------------------------ + * PMAXH rd, rs, rt Parallel Maximum Halfword + * PMINH rd, rs, rt Parallel Minimum Halfword + * PMAXW rd, rs, rt Parallel Maximum Word + * PMINW rd, rs, rt Parallel Minimum Word + * + * Absolute (2 instructions) + * ------------------------- + * PABSH rd, rt Parallel Absolute Halfword + * PABSW rd, rt Parallel Absolute Word + * + * Logical (4 instructions) + * ------------------------ + * PAND rd, rs, rt Parallel AND + * POR rd, rs, rt Parallel OR + * PXOR rd, rs, rt Parallel XOR + * PNOR rd, rs, rt Parallel NOR + * + * Shift (9 instructions) + * ---------------------- + * PSLLH rd, rt, sa Parallel Shift Left Logical Halfword + * PSRLH rd, rt, sa Parallel Shift Right Logical Halfword + * PSRAH rd, rt, sa Parallel Shift Right Arithmetic Halfword + * PSLLW rd, rt, sa Parallel Shift Left Logical Word + * PSRLW rd, rt, sa Parallel Shift Right Logical Word + * PSRAW rd, rt, sa Parallel Shift Right Arithmetic Word + * PSLLVW rd, rt, rs Parallel Shift Left Logical Variable Word + * PSRLVW rd, rt, rs Parallel Shift Right Logical Variable Word + * PSRAVW rd, rt, rs Parallel Shift Right Arithmetic Variable Word + * + * Compare (6 instructions) + * ------------------------ + * PCGTB rd, rs, rt Parallel Compare for Greater Than Byte + * PCEQB rd, rs, rt Parallel Compare for Equal Byte + * PCGTH rd, rs, rt Parallel Compare for Greater Than Halfword + * PCEQH rd, rs, rt Parallel Compare for Equal Halfword + * PCGTW rd, rs, rt Parallel Compare for Greater Than Word + * PCEQW rd, rs, rt Parallel Compare for Equal Word + * + * LZC (1 instruction) + * ------------------- + * PLZCW rd, rs Parallel Leading Zero or One Count Word + * + * Quadword Load and Store (2 instructions) + * ---------------------------------------- + * LQ rt, offset(base) Load Quadword + * SQ rt, offset(base) Store Quadword + * + * Multiply and Divide (19 instructions) + * ------------------------------------- + * PMULTW rd, rs, rt Parallel Multiply Word + * PMULTUW rd, rs, rt Parallel Multiply Unsigned Word + * PDIVW rs, rt Parallel Divide Word + * PDIVUW rs, rt Parallel Divide Unsigned Word + * PMADDW rd, rs, rt Parallel Multiply-Add Word + * PMADDUW rd, rs, rt Parallel Multiply-Add Unsigned Word + * PMSUBW rd, rs, rt Parallel Multiply-Subtract Word + * PMULTH rd, rs, rt Parallel Multiply Halfword + * PMADDH rd, rs, rt Parallel Multiply-Add Halfword + * PMSUBH rd, rs, rt Parallel Multiply-Subtract Halfword + * PHMADH rd, rs, rt Parallel Horizontal Multiply-Add Halfword + * PHMSBH rd, rs, rt Parallel Horizontal Multiply-Subtract Halfword + * PDIVBW rs, rt Parallel Divide Broadcast Word + * PMFHI rd Parallel Move From HI Register + * PMFLO rd Parallel Move From LO Register + * PMTHI rs Parallel Move To HI Register + * PMTLO rs Parallel Move To LO Register + * PMFHL rd Parallel Move From HI/LO Register + * PMTHL rs Parallel Move To HI/LO Register + * + * Pack/Extend (11 instructions) + * ----------------------------- + * PPAC5 rd, rt Parallel Pack to 5 bits + * PPACB rd, rs, rt Parallel Pack to Byte + * PPACH rd, rs, rt Parallel Pack to Halfword + * PPACW rd, rs, rt Parallel Pack to Word + * PEXT5 rd, rt Parallel Extend Upper from 5 bits + * PEXTUB rd, rs, rt Parallel Extend Upper from Byte + * PEXTLB rd, rs, rt Parallel Extend Lower from Byte + * PEXTUH rd, rs, rt Parallel Extend Upper from Halfword + * PEXTLH rd, rs, rt Parallel Extend Lower from Halfword + * PEXTUW rd, rs, rt Parallel Extend Upper from Word + * PEXTLW rd, rs, rt Parallel Extend Lower from Word + * + * Others (16 instructions) + * ------------------------ + * PCPYH rd, rt Parallel Copy Halfword + * PCPYLD rd, rs, rt Parallel Copy Lower Doubleword + * PCPYUD rd, rs, rt Parallel Copy Upper Doubleword + * PREVH rd, rt Parallel Reverse Halfword + * PINTH rd, rs, rt Parallel Interleave Halfword + * PINTEH rd, rs, rt Parallel Interleave Even Halfword + * PEXEH rd, rt Parallel Exchange Even Halfword + * PEXCH rd, rt Parallel Exchange Center Halfword + * PEXEW rd, rt Parallel Exchange Even Word + * PEXCW rd, rt Parallel Exchange Center Word + * QFSRV rd, rs, rt Quadword Funnel Shift Right Variable + * MFSA rd Move from Shift Amount Register + * MTSA rs Move to Shift Amount Register + * MTSAB rs, immediate Move Byte Count to Shift Amount Register + * MTSAH rs, immediate Move Halfword Count to Shift Amount Register + * PROT3W rd, rt Parallel Rotate 3 Words + * + * MMI (MultiMedia Instruction) encodings + * ====================================== + * + * MMI instructions encoding table keys: + * + * * This code is reserved for future use. An attempt to execute it + * causes a Reserved Instruction exception. + * % This code indicates an instruction class. The instruction word + * must be further decoded by examining additional tables that show + * the values for other instruction fields. + * # This code is reserved for the unsupported instructions DMULT, + * DMULTU, DDIV, DDIVU, LL, LLD, SC, SCD, LWC2 and SWC2. An attempt + * to execute it causes a Reserved Instruction exception. + * + * MMI instructions encoded by opcode field (MMI, LQ, SQ): + * + * 31 26 0 + * +--------+----------------------------------------+ + * | opcode | | + * +--------+----------------------------------------+ + * + * opcode bits 28..26 + * bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 + * 31..29 | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 + * -------+-------+-------+-------+-------+-------+-------+-------+------- + * 0 000 |SPECIAL| REGIMM| J | JAL | BEQ | BNE | BLEZ | BGTZ + * 1 001 | ADDI | ADDIU | SLTI | SLTIU | ANDI | ORI | XORI | LUI + * 2 010 | COP0 | COP1 | * | * | BEQL | BNEL | BLEZL | BGTZL + * 3 011 | DADDI | DADDIU| LDL | LDR | MMI% | * | LQ | SQ + * 4 100 | LB | LH | LWL | LW | LBU | LHU | LWR | LWU + * 5 101 | SB | SH | SWL | SW | SDL | SDR | SWR | CACHE + * 6 110 | # | LWC1 | # | PREF | # | LDC1 | # | LD + * 7 111 | # | SWC1 | # | * | # | SDC1 | # | SD + */ + +enum { + MMI_OPC_CLASS_MMI = 0x1C << 26, /* Same as OPC_SPECIAL2 */ + MMI_OPC_LQ = 0x1E << 26, /* Same as OPC_MSA */ + MMI_OPC_SQ = 0x1F << 26, /* Same as OPC_SPECIAL3 */ +}; + +/* + * MMI instructions with opcode field = MMI: + * + * 31 26 5 0 + * +--------+-------------------------------+--------+ + * | MMI | |function| + * +--------+-------------------------------+--------+ + * + * function bits 2..0 + * bits | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 + * 5..3 | 000 | 001 | 010 | 011 | 100 | 101 | 110 | 111 + * -------+-------+-------+-------+-------+-------+-------+-------+------- + * 0 000 | MADD | MADDU | * | * | PLZCW | * | * | * + * 1 001 | MMI0% | MMI2% | * | * | * | * | * | * + * 2 010 | MFHI1 | MTHI1 | MFLO1 | MTLO1 | * | * | * | * + * 3 011 | MULT1 | MULTU1| DIV1 | DIVU1 | * | * | * | * + * 4 100 | MADD1 | MADDU1| * | * | * | * | * | * + * 5 101 | MMI1% | MMI3% | * | * | * | * | * | * + * 6 110 | PMFHL | PMTHL | * | * | PSLLH | * | PSRLH | PSRAH + * 7 111 | * | * | * | * | PSLLW | * | PSRLW | PSRAW + */ + +#define MASK_MMI(op) (MASK_OP_MAJOR(op) | ((op) & 0x3F)) +enum { + MMI_OPC_MADD = 0x00 | MMI_OPC_CLASS_MMI, /* Same as OPC_MADD */ + MMI_OPC_MADDU = 0x01 | MMI_OPC_CLASS_MMI, /* Same as OPC_MADDU */ + MMI_OPC_PLZCW = 0x04 | MMI_OPC_CLASS_MMI, + MMI_OPC_CLASS_MMI0 = 0x08 | MMI_OPC_CLASS_MMI, + MMI_OPC_CLASS_MMI2 = 0x09 | MMI_OPC_CLASS_MMI, + MMI_OPC_MFHI1 = 0x10 | MMI_OPC_CLASS_MMI, /* Same minor as OPC_MFHI */ + MMI_OPC_MTHI1 = 0x11 | MMI_OPC_CLASS_MMI, /* Same minor as OPC_MTHI */ + MMI_OPC_MFLO1 = 0x12 | MMI_OPC_CLASS_MMI, /* Same minor as OPC_MFLO */ + MMI_OPC_MTLO1 = 0x13 | MMI_OPC_CLASS_MMI, /* Same minor as OPC_MTLO */ + MMI_OPC_MULT1 = 0x18 | MMI_OPC_CLASS_MMI, /* Same minor as OPC_MULT */ + MMI_OPC_MULTU1 = 0x19 | MMI_OPC_CLASS_MMI, /* Same min. as OPC_MULTU */ + MMI_OPC_DIV1 = 0x1A | MMI_OPC_CLASS_MMI, /* Same minor as OPC_DIV */ + MMI_OPC_DIVU1 = 0x1B | MMI_OPC_CLASS_MMI, /* Same minor as OPC_DIVU */ + MMI_OPC_MADD1 = 0x20 | MMI_OPC_CLASS_MMI, + MMI_OPC_MADDU1 = 0x21 | MMI_OPC_CLASS_MMI, + MMI_OPC_CLASS_MMI1 = 0x28 | MMI_OPC_CLASS_MMI, + MMI_OPC_CLASS_MMI3 = 0x29 | MMI_OPC_CLASS_MMI, + MMI_OPC_PMFHL = 0x30 | MMI_OPC_CLASS_MMI, + MMI_OPC_PMTHL = 0x31 | MMI_OPC_CLASS_MMI, + MMI_OPC_PSLLH = 0x34 | MMI_OPC_CLASS_MMI, + MMI_OPC_PSRLH = 0x36 | MMI_OPC_CLASS_MMI, + MMI_OPC_PSRAH = 0x37 | MMI_OPC_CLASS_MMI, + MMI_OPC_PSLLW = 0x3C | MMI_OPC_CLASS_MMI, + MMI_OPC_PSRLW = 0x3E | MMI_OPC_CLASS_MMI, + MMI_OPC_PSRAW = 0x3F | MMI_OPC_CLASS_MMI, +}; + +/* + * MMI instructions with opcode field = MMI and bits 5..0 = MMI0: + * + * 31 26 10 6 5 0 + * +--------+----------------------+--------+--------+ + * | MMI | |function| MMI0 | + * +--------+----------------------+--------+--------+ + * + * function bits 7..6 + * bits | 0 | 1 | 2 | 3 + * 10..8 | 00 | 01 | 10 | 11 + * -------+-------+-------+-------+------- + * 0 000 | PADDW | PSUBW | PCGTW | PMAXW + * 1 001 | PADDH | PSUBH | PCGTH | PMAXH + * 2 010 | PADDB | PSUBB | PCGTB | * + * 3 011 | * | * | * | * + * 4 100 | PADDSW| PSUBSW| PEXTLW| PPACW + * 5 101 | PADDSH| PSUBSH| PEXTLH| PPACH + * 6 110 | PADDSB| PSUBSB| PEXTLB| PPACB + * 7 111 | * | * | PEXT5 | PPAC5 + */ + +#define MASK_MMI0(op) (MASK_OP_MAJOR(op) | ((op) & 0x7FF)) +enum { + MMI_OPC_0_PADDW = (0x00 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PSUBW = (0x01 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PCGTW = (0x02 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PMAXW = (0x03 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PADDH = (0x04 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PSUBH = (0x05 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PCGTH = (0x06 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PMAXH = (0x07 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PADDB = (0x08 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PSUBB = (0x09 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PCGTB = (0x0A << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PADDSW = (0x10 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PSUBSW = (0x11 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PEXTLW = (0x12 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PPACW = (0x13 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PADDSH = (0x14 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PSUBSH = (0x15 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PEXTLH = (0x16 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PPACH = (0x17 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PADDSB = (0x18 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PSUBSB = (0x19 << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PEXTLB = (0x1A << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PPACB = (0x1B << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PEXT5 = (0x1E << 6) | MMI_OPC_CLASS_MMI0, + MMI_OPC_0_PPAC5 = (0x1F << 6) | MMI_OPC_CLASS_MMI0, +}; + +/* + * MMI instructions with opcode field = MMI and bits 5..0 = MMI1: + * + * 31 26 10 6 5 0 + * +--------+----------------------+--------+--------+ + * | MMI | |function| MMI1 | + * +--------+----------------------+--------+--------+ + * + * function bits 7..6 + * bits | 0 | 1 | 2 | 3 + * 10..8 | 00 | 01 | 10 | 11 + * -------+-------+-------+-------+------- + * 0 000 | * | PABSW | PCEQW | PMINW + * 1 001 | PADSBH| PABSH | PCEQH | PMINH + * 2 010 | * | * | PCEQB | * + * 3 011 | * | * | * | * + * 4 100 | PADDUW| PSUBUW| PEXTUW| * + * 5 101 | PADDUH| PSUBUH| PEXTUH| * + * 6 110 | PADDUB| PSUBUB| PEXTUB| QFSRV + * 7 111 | * | * | * | * + */ + +#define MASK_MMI1(op) (MASK_OP_MAJOR(op) | ((op) & 0x7FF)) +enum { + MMI_OPC_1_PABSW = (0x01 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PCEQW = (0x02 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PMINW = (0x03 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PADSBH = (0x04 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PABSH = (0x05 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PCEQH = (0x06 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PMINH = (0x07 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PCEQB = (0x0A << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PADDUW = (0x10 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PSUBUW = (0x11 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PEXTUW = (0x12 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PADDUH = (0x14 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PSUBUH = (0x15 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PEXTUH = (0x16 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PADDUB = (0x18 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PSUBUB = (0x19 << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_PEXTUB = (0x1A << 6) | MMI_OPC_CLASS_MMI1, + MMI_OPC_1_QFSRV = (0x1B << 6) | MMI_OPC_CLASS_MMI1, +}; + +/* + * MMI instructions with opcode field = MMI and bits 5..0 = MMI2: + * + * 31 26 10 6 5 0 + * +--------+----------------------+--------+--------+ + * | MMI | |function| MMI2 | + * +--------+----------------------+--------+--------+ + * + * function bits 7..6 + * bits | 0 | 1 | 2 | 3 + * 10..8 | 00 | 01 | 10 | 11 + * -------+-------+-------+-------+------- + * 0 000 | PMADDW| * | PSLLVW| PSRLVW + * 1 001 | PMSUBW| * | * | * + * 2 010 | PMFHI | PMFLO | PINTH | * + * 3 011 | PMULTW| PDIVW | PCPYLD| * + * 4 100 | PMADDH| PHMADH| PAND | PXOR + * 5 101 | PMSUBH| PHMSBH| * | * + * 6 110 | * | * | PEXEH | PREVH + * 7 111 | PMULTH| PDIVBW| PEXEW | PROT3W + */ + +#define MASK_MMI2(op) (MASK_OP_MAJOR(op) | ((op) & 0x7FF)) +enum { + MMI_OPC_2_PMADDW = (0x00 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PSLLVW = (0x02 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PSRLVW = (0x03 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMSUBW = (0x04 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMFHI = (0x08 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMFLO = (0x09 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PINTH = (0x0A << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMULTW = (0x0C << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PDIVW = (0x0D << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PCPYLD = (0x0E << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMADDH = (0x10 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PHMADH = (0x11 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PAND = (0x12 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PXOR = (0x13 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMSUBH = (0x14 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PHMSBH = (0x15 << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PEXEH = (0x1A << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PREVH = (0x1B << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PMULTH = (0x1C << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PDIVBW = (0x1D << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PEXEW = (0x1E << 6) | MMI_OPC_CLASS_MMI2, + MMI_OPC_2_PROT3W = (0x1F << 6) | MMI_OPC_CLASS_MMI2, +}; + +/* + * MMI instructions with opcode field = MMI and bits 5..0 = MMI3: + * + * 31 26 10 6 5 0 + * +--------+----------------------+--------+--------+ + * | MMI | |function| MMI3 | + * +--------+----------------------+--------+--------+ + * + * function bits 7..6 + * bits | 0 | 1 | 2 | 3 + * 10..8 | 00 | 01 | 10 | 11 + * -------+-------+-------+-------+------- + * 0 000 |PMADDUW| * | * | PSRAVW + * 1 001 | * | * | * | * + * 2 010 | PMTHI | PMTLO | PINTEH| * + * 3 011 |PMULTUW| PDIVUW| PCPYUD| * + * 4 100 | * | * | POR | PNOR + * 5 101 | * | * | * | * + * 6 110 | * | * | PEXCH | PCPYH + * 7 111 | * | * | PEXCW | * + */ + +#define MASK_MMI3(op) (MASK_OP_MAJOR(op) | ((op) & 0x7FF)) +enum { + MMI_OPC_3_PMADDUW = (0x00 << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PSRAVW = (0x03 << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PMTHI = (0x08 << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PMTLO = (0x09 << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PINTEH = (0x0A << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PMULTUW = (0x0C << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PDIVUW = (0x0D << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PCPYUD = (0x0E << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_POR = (0x12 << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PNOR = (0x13 << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PEXCH = (0x1A << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PCPYH = (0x1B << 6) | MMI_OPC_CLASS_MMI3, + MMI_OPC_3_PEXCW = (0x1E << 6) | MMI_OPC_CLASS_MMI3, +}; + /* global register indices */ 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; +static TCGv cpu_lladdr, cpu_llval; static TCGv_i32 hflags; static TCGv_i32 fpu_fcr0, fpu_fcr31; static TCGv_i64 fpu_f64[32]; static TCGv_i64 msa_wr_d[64]; +#if defined(TARGET_MIPS64) +/* Upper halves of R5900's 128-bit registers: MMRs (multimedia registers) */ +static TCGv_i64 cpu_mmr[32]; +#endif + +#if !defined(TARGET_MIPS64) +/* MXU registers */ +static TCGv mxu_gpr[NUMBER_OF_MXU_REGISTERS - 1]; +static TCGv mxu_CR; +#endif + #include "exec/gen-icount.h" #define gen_helper_0e0i(name, arg) do { \ @@ -1434,8 +2516,11 @@ typedef struct DisasContext { target_ulong saved_pc; target_ulong page_start; uint32_t opcode; - int insn_flags; + uint64_t insn_flags; int32_t CP0_Config1; + int32_t CP0_Config2; + int32_t CP0_Config3; + int32_t CP0_Config5; /* Routine used to access memory */ int mem_idx; TCGMemOp default_tcg_memop_mask; @@ -1458,6 +2543,7 @@ typedef struct DisasContext { bool mrp; bool nan2008; bool abs2008; + bool saar; } DisasContext; #define DISAS_STOP DISAS_TARGET_0 @@ -1504,6 +2590,13 @@ static const char * const msaregnames[] = { "w30.d0", "w30.d1", "w31.d0", "w31.d1", }; +#if !defined(TARGET_MIPS64) +static const char * const mxuregnames[] = { + "XR1", "XR2", "XR3", "XR4", "XR5", "XR6", "XR7", "XR8", + "XR9", "XR10", "XR11", "XR12", "XR13", "XR14", "XR15", "MXU_CR", +}; +#endif + #define LOG_DISAS(...) \ do { \ if (MIPS_DEBUG_DISAS) { \ @@ -1585,6 +2678,38 @@ static inline void gen_store_srsgpr (int from, int to) } } +#if !defined(TARGET_MIPS64) +/* MXU General purpose registers moves. */ +static inline void gen_load_mxu_gpr(TCGv t, unsigned int reg) +{ + if (reg == 0) { + tcg_gen_movi_tl(t, 0); + } else if (reg <= 15) { + tcg_gen_mov_tl(t, mxu_gpr[reg - 1]); + } +} + +static inline void gen_store_mxu_gpr(TCGv t, unsigned int reg) +{ + if (reg > 0 && reg <= 15) { + tcg_gen_mov_tl(mxu_gpr[reg - 1], t); + } +} + +/* MXU control register moves. */ +static inline void gen_load_mxu_cr(TCGv t) +{ + tcg_gen_mov_tl(t, mxu_CR); +} + +static inline void gen_store_mxu_cr(TCGv t) +{ + /* TODO: Add handling of RW rules for MXU_CR. */ + tcg_gen_mov_tl(mxu_CR, t); +} +#endif + + /* Tests */ static inline void gen_save_pc(target_ulong pc) { @@ -1733,6 +2858,18 @@ static inline void gen_op_addr_add (DisasContext *ctx, TCGv ret, TCGv arg0, TCGv #endif } +static inline void gen_op_addr_addi(DisasContext *ctx, TCGv ret, TCGv base, + target_long ofs) +{ + tcg_gen_addi_tl(ret, base, ofs); + +#if defined(TARGET_MIPS64) + if (ctx->hflags & MIPS_HFLAG_AWRAP) { + tcg_gen_ext32s_i64(ret, ret); + } +#endif +} + /* Addresses computation (translation time) */ static target_long addr_add(DisasContext *ctx, target_long base, target_long offset) @@ -1830,9 +2967,20 @@ static inline void check_dsp(DisasContext *ctx) } } -static inline void check_dspr2(DisasContext *ctx) +static inline void check_dsp_r2(DisasContext *ctx) { - if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSPR2))) { + if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSP_R2))) { + if (ctx->insn_flags & ASE_DSP) { + generate_exception_end(ctx, EXCP_DSPDIS); + } else { + generate_exception_end(ctx, EXCP_RI); + } + } +} + +static inline void check_dsp_r3(DisasContext *ctx) +{ + if (unlikely(!(ctx->hflags & MIPS_HFLAG_DSP_R3))) { if (ctx->insn_flags & ASE_DSP) { generate_exception_end(ctx, EXCP_DSPDIS); } else { @@ -1843,7 +2991,7 @@ static inline void check_dspr2(DisasContext *ctx) /* This code generates a "reserved instruction" exception if the CPU does not support the instruction set corresponding to flags. */ -static inline void check_insn(DisasContext *ctx, int flags) +static inline void check_insn(DisasContext *ctx, uint64_t flags) { if (unlikely(!(ctx->insn_flags & flags))) { generate_exception_end(ctx, EXCP_RI); @@ -1853,13 +3001,28 @@ static inline void check_insn(DisasContext *ctx, int flags) /* This code generates a "reserved instruction" exception if the CPU has corresponding flag set which indicates that the instruction has been removed. */ -static inline void check_insn_opc_removed(DisasContext *ctx, int flags) +static inline void check_insn_opc_removed(DisasContext *ctx, uint64_t flags) { if (unlikely(ctx->insn_flags & flags)) { generate_exception_end(ctx, EXCP_RI); } } +/* + * The Linux kernel traps certain reserved instruction exceptions to + * emulate the corresponding instructions. QEMU is the kernel in user + * mode, so those traps are emulated by accepting the instructions. + * + * A reserved instruction exception is generated for flagged CPUs if + * QEMU runs in system mode. + */ +static inline void check_insn_opc_user_only(DisasContext *ctx, uint64_t flags) +{ +#ifndef CONFIG_USER_ONLY + check_insn_opc_removed(ctx, flags); +#endif +} + /* This code generates a "reserved instruction" exception if the CPU does not support 64-bit paired-single (PS) floating point data type */ static inline void check_ps(DisasContext *ctx) @@ -1889,6 +3052,101 @@ static inline void check_mvh(DisasContext *ctx) } #endif +/* + * This code generates a "reserved instruction" exception if the + * Config5 XNP bit is set. + */ +static inline void check_xnp(DisasContext *ctx) +{ + if (unlikely(ctx->CP0_Config5 & (1 << CP0C5_XNP))) { + generate_exception_end(ctx, EXCP_RI); + } +} + +#ifndef CONFIG_USER_ONLY +/* + * This code generates a "reserved instruction" exception if the + * Config3 PW bit is NOT set. + */ +static inline void check_pw(DisasContext *ctx) +{ + if (unlikely(!(ctx->CP0_Config3 & (1 << CP0C3_PW)))) { + generate_exception_end(ctx, EXCP_RI); + } +} +#endif + +/* + * This code generates a "reserved instruction" exception if the + * Config3 MT bit is NOT set. + */ +static inline void check_mt(DisasContext *ctx) +{ + if (unlikely(!(ctx->CP0_Config3 & (1 << CP0C3_MT)))) { + generate_exception_end(ctx, EXCP_RI); + } +} + +#ifndef CONFIG_USER_ONLY +/* + * This code generates a "coprocessor unusable" exception if CP0 is not + * available, and, if that is not the case, generates a "reserved instruction" + * exception if the Config5 MT bit is NOT set. This is needed for availability + * control of some of MT ASE instructions. + */ +static inline void check_cp0_mt(DisasContext *ctx) +{ + if (unlikely(!(ctx->hflags & MIPS_HFLAG_CP0))) { + generate_exception_err(ctx, EXCP_CpU, 0); + } else { + if (unlikely(!(ctx->CP0_Config3 & (1 << CP0C3_MT)))) { + generate_exception_err(ctx, EXCP_RI, 0); + } + } +} +#endif + +/* + * This code generates a "reserved instruction" exception if the + * Config5 NMS bit is set. + */ +static inline void check_nms(DisasContext *ctx) +{ + if (unlikely(ctx->CP0_Config5 & (1 << CP0C5_NMS))) { + generate_exception_end(ctx, EXCP_RI); + } +} + +/* + * This code generates a "reserved instruction" exception if the + * Config5 NMS bit is set, and Config1 DL, Config1 IL, Config2 SL, + * Config2 TL, and Config5 L2C are unset. + */ +static inline void check_nms_dl_il_sl_tl_l2c(DisasContext *ctx) +{ + if (unlikely(ctx->CP0_Config5 & (1 << CP0C5_NMS)) && + !(ctx->CP0_Config1 & (1 << CP0C1_DL)) && + !(ctx->CP0_Config1 & (1 << CP0C1_IL)) && + !(ctx->CP0_Config2 & (1 << CP0C2_SL)) && + !(ctx->CP0_Config2 & (1 << CP0C2_TL)) && + !(ctx->CP0_Config5 & (1 << CP0C5_L2C))) + { + generate_exception_end(ctx, EXCP_RI); + } +} + +/* + * This code generates a "reserved instruction" exception if the + * Config5 EVA bit is NOT set. + */ +static inline void check_eva(DisasContext *ctx) +{ + if (unlikely(!(ctx->CP0_Config5 & (1 << CP0C5_EVA)))) { + generate_exception_end(ctx, EXCP_RI); + } +} + + /* Define small wrappers for gen_load_fpr* so that we have a uniform calling interface for 32 and 64-bit FPRs. No sense in changing all callers for gen_load_fpr32 when we need the CTX parameter for @@ -2069,48 +3327,6 @@ OP_LD_ATOMIC(lld,ld64); #endif #undef OP_LD_ATOMIC -#ifdef CONFIG_USER_ONLY -#define OP_ST_ATOMIC(insn,fname,ldname,almask) \ -static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, int mem_idx, \ - DisasContext *ctx) \ -{ \ - TCGv t0 = tcg_temp_new(); \ - TCGLabel *l1 = gen_new_label(); \ - TCGLabel *l2 = gen_new_label(); \ - \ - tcg_gen_andi_tl(t0, arg2, almask); \ - tcg_gen_brcondi_tl(TCG_COND_EQ, t0, 0, l1); \ - tcg_gen_st_tl(arg2, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); \ - generate_exception(ctx, EXCP_AdES); \ - gen_set_label(l1); \ - tcg_gen_ld_tl(t0, cpu_env, offsetof(CPUMIPSState, lladdr)); \ - tcg_gen_brcond_tl(TCG_COND_NE, arg2, t0, l2); \ - 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)); \ - generate_exception_end(ctx, EXCP_SC); \ - gen_set_label(l2); \ - tcg_gen_movi_tl(t0, 0); \ - gen_store_gpr(t0, rt); \ - tcg_temp_free(t0); \ -} -#else -#define OP_ST_ATOMIC(insn,fname,ldname,almask) \ -static inline void op_st_##insn(TCGv arg1, TCGv arg2, int rt, int mem_idx, \ - DisasContext *ctx) \ -{ \ - TCGv t0 = tcg_temp_new(); \ - gen_helper_1e2i(insn, t0, arg1, arg2, mem_idx); \ - gen_store_gpr(t0, rt); \ - tcg_temp_free(t0); \ -} -#endif -OP_ST_ATOMIC(sc,st32,ld32s,0x3); -#if defined(TARGET_MIPS64) -OP_ST_ATOMIC(scd,st64,ld64,0x7); -#endif -#undef OP_ST_ATOMIC - static void gen_base_offset_addr (DisasContext *ctx, TCGv addr, int base, int offset) { @@ -2335,6 +3551,31 @@ static void gen_ld(DisasContext *ctx, uint32_t opc, tcg_temp_free(t0); } +static void gen_llwp(DisasContext *ctx, uint32_t base, int16_t offset, + uint32_t reg1, uint32_t reg2) +{ + TCGv taddr = tcg_temp_new(); + TCGv_i64 tval = tcg_temp_new_i64(); + TCGv tmp1 = tcg_temp_new(); + TCGv tmp2 = tcg_temp_new(); + + gen_base_offset_addr(ctx, taddr, base, offset); + tcg_gen_qemu_ld64(tval, taddr, ctx->mem_idx); +#ifdef TARGET_WORDS_BIGENDIAN + tcg_gen_extr_i64_tl(tmp2, tmp1, tval); +#else + tcg_gen_extr_i64_tl(tmp1, tmp2, tval); +#endif + gen_store_gpr(tmp1, reg1); + tcg_temp_free(tmp1); + gen_store_gpr(tmp2, reg2); + tcg_temp_free(tmp2); + tcg_gen_st_i64(tval, cpu_env, offsetof(CPUMIPSState, llval_wp)); + tcg_temp_free_i64(tval); + tcg_gen_st_tl(taddr, cpu_env, offsetof(CPUMIPSState, lladdr)); + tcg_temp_free(taddr); +} + /* Store */ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, int base, int offset) @@ -2397,40 +3638,83 @@ static void gen_st (DisasContext *ctx, uint32_t opc, int rt, /* Store conditional */ -static void gen_st_cond (DisasContext *ctx, uint32_t opc, int rt, - int base, int16_t offset) +static void gen_st_cond(DisasContext *ctx, int rt, int base, int offset, + TCGMemOp tcg_mo, bool eva) { - TCGv t0, t1; - int mem_idx = ctx->mem_idx; + TCGv addr, t0, val; + TCGLabel *l1 = gen_new_label(); + TCGLabel *done = gen_new_label(); -#ifdef CONFIG_USER_ONLY - t0 = tcg_temp_local_new(); - t1 = tcg_temp_local_new(); -#else t0 = tcg_temp_new(); - t1 = tcg_temp_new(); -#endif - gen_base_offset_addr(ctx, t0, base, offset); - gen_load_gpr(t1, rt); - switch (opc) { -#if defined(TARGET_MIPS64) - case OPC_SCD: - case R6_OPC_SCD: - op_st_scd(t1, t0, rt, mem_idx, ctx); - break; -#endif - case OPC_SCE: - mem_idx = MIPS_HFLAG_UM; - /* fall through */ - case OPC_SC: - case R6_OPC_SC: - op_st_sc(t1, t0, rt, mem_idx, ctx); - break; - } - tcg_temp_free(t1); + addr = tcg_temp_new(); + /* compare the address against that of the preceeding LL */ + gen_base_offset_addr(ctx, addr, base, offset); + tcg_gen_brcond_tl(TCG_COND_EQ, addr, cpu_lladdr, l1); + tcg_temp_free(addr); + tcg_gen_movi_tl(t0, 0); + gen_store_gpr(t0, rt); + tcg_gen_br(done); + + gen_set_label(l1); + /* generate cmpxchg */ + val = tcg_temp_new(); + gen_load_gpr(val, rt); + tcg_gen_atomic_cmpxchg_tl(t0, cpu_lladdr, cpu_llval, val, + eva ? MIPS_HFLAG_UM : ctx->mem_idx, tcg_mo); + tcg_gen_setcond_tl(TCG_COND_EQ, t0, t0, cpu_llval); + gen_store_gpr(t0, rt); + tcg_temp_free(val); + + gen_set_label(done); tcg_temp_free(t0); } + +static void gen_scwp(DisasContext *ctx, uint32_t base, int16_t offset, + uint32_t reg1, uint32_t reg2, bool eva) +{ + TCGv taddr = tcg_temp_local_new(); + TCGv lladdr = tcg_temp_local_new(); + TCGv_i64 tval = tcg_temp_new_i64(); + TCGv_i64 llval = tcg_temp_new_i64(); + TCGv_i64 val = tcg_temp_new_i64(); + TCGv tmp1 = tcg_temp_new(); + TCGv tmp2 = tcg_temp_new(); + TCGLabel *lab_fail = gen_new_label(); + TCGLabel *lab_done = gen_new_label(); + + gen_base_offset_addr(ctx, taddr, base, offset); + + tcg_gen_ld_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); + tcg_gen_brcond_tl(TCG_COND_NE, taddr, lladdr, lab_fail); + + gen_load_gpr(tmp1, reg1); + gen_load_gpr(tmp2, reg2); + +#ifdef TARGET_WORDS_BIGENDIAN + tcg_gen_concat_tl_i64(tval, tmp2, tmp1); +#else + tcg_gen_concat_tl_i64(tval, tmp1, tmp2); +#endif + + tcg_gen_ld_i64(llval, cpu_env, offsetof(CPUMIPSState, llval_wp)); + tcg_gen_atomic_cmpxchg_i64(val, taddr, llval, tval, + eva ? MIPS_HFLAG_UM : ctx->mem_idx, MO_64); + if (reg1 != 0) { + tcg_gen_movi_tl(cpu_gpr[reg1], 1); + } + tcg_gen_brcond_i64(TCG_COND_EQ, val, llval, lab_done); + + gen_set_label(lab_fail); + + if (reg1 != 0) { + tcg_gen_movi_tl(cpu_gpr[reg1], 0); + } + gen_set_label(lab_done); + tcg_gen_movi_tl(lladdr, -1); + tcg_gen_st_tl(lladdr, cpu_env, offsetof(CPUMIPSState, lladdr)); +} + /* Load and store */ static void gen_flt_ldst (DisasContext *ctx, uint32_t opc, int ft, TCGv t0) @@ -3078,6 +4362,44 @@ static void gen_shift(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } +#if defined(TARGET_MIPS64) +/* Copy GPR to and from TX79 HI1/LO1 register. */ +static void gen_HILO1_tx79(DisasContext *ctx, uint32_t opc, int reg) +{ + if (reg == 0 && (opc == MMI_OPC_MFHI1 || opc == MMI_OPC_MFLO1)) { + /* Treat as NOP. */ + return; + } + + switch (opc) { + case MMI_OPC_MFHI1: + tcg_gen_mov_tl(cpu_gpr[reg], cpu_HI[1]); + break; + case MMI_OPC_MFLO1: + tcg_gen_mov_tl(cpu_gpr[reg], cpu_LO[1]); + break; + case MMI_OPC_MTHI1: + if (reg != 0) { + tcg_gen_mov_tl(cpu_HI[1], cpu_gpr[reg]); + } else { + tcg_gen_movi_tl(cpu_HI[1], 0); + } + break; + case MMI_OPC_MTLO1: + if (reg != 0) { + tcg_gen_mov_tl(cpu_LO[1], cpu_gpr[reg]); + } else { + tcg_gen_movi_tl(cpu_LO[1], 0); + } + break; + default: + MIPS_INVAL("mfthilo1 TX79"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} +#endif + /* Arithmetic on HI/LO registers */ static void gen_HILO(DisasContext *ctx, uint32_t opc, int acc, int reg) { @@ -3426,6 +4748,65 @@ static void gen_r6_muldiv(DisasContext *ctx, int opc, int rd, int rs, int rt) tcg_temp_free(t1); } +#if defined(TARGET_MIPS64) +static void gen_div1_tx79(DisasContext *ctx, uint32_t opc, int rs, int rt) +{ + TCGv t0, t1; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + switch (opc) { + case MMI_OPC_DIV1: + { + TCGv t2 = tcg_temp_new(); + TCGv t3 = tcg_temp_new(); + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_ext32s_tl(t1, t1); + tcg_gen_setcondi_tl(TCG_COND_EQ, t2, t0, INT_MIN); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, -1); + tcg_gen_and_tl(t2, t2, t3); + tcg_gen_setcondi_tl(TCG_COND_EQ, t3, t1, 0); + tcg_gen_or_tl(t2, t2, t3); + tcg_gen_movi_tl(t3, 0); + tcg_gen_movcond_tl(TCG_COND_NE, t1, t2, t3, t2, t1); + tcg_gen_div_tl(cpu_LO[1], t0, t1); + tcg_gen_rem_tl(cpu_HI[1], t0, t1); + tcg_gen_ext32s_tl(cpu_LO[1], cpu_LO[1]); + tcg_gen_ext32s_tl(cpu_HI[1], cpu_HI[1]); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + break; + case MMI_OPC_DIVU1: + { + TCGv t2 = tcg_const_tl(0); + TCGv t3 = tcg_const_tl(1); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_movcond_tl(TCG_COND_EQ, t1, t1, t2, t3, t1); + tcg_gen_divu_tl(cpu_LO[1], t0, t1); + tcg_gen_remu_tl(cpu_HI[1], t0, t1); + tcg_gen_ext32s_tl(cpu_LO[1], cpu_LO[1]); + tcg_gen_ext32s_tl(cpu_HI[1], cpu_HI[1]); + tcg_temp_free(t3); + tcg_temp_free(t2); + } + break; + default: + MIPS_INVAL("div1 TX79"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} +#endif + static void gen_muldiv(DisasContext *ctx, uint32_t opc, int acc, int rs, int rt) { @@ -3618,6 +4999,138 @@ static void gen_muldiv(DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } +/* + * These MULT[U] and MADD[U] instructions implemented in for example + * the Toshiba/Sony R5900 and the Toshiba TX19, TX39 and TX79 core + * architectures are special three-operand variants with the syntax + * + * MULT[U][1] rd, rs, rt + * + * such that + * + * (rd, LO, HI) <- rs * rt + * + * and + * + * MADD[U][1] rd, rs, rt + * + * such that + * + * (rd, LO, HI) <- (LO, HI) + rs * rt + * + * where the low-order 32-bits of the result is placed into both the + * GPR rd and the special register LO. The high-order 32-bits of the + * result is placed into the special register HI. + * + * If the GPR rd is omitted in assembly language, it is taken to be 0, + * which is the zero register that always reads as 0. + */ +static void gen_mul_txx9(DisasContext *ctx, uint32_t opc, + int rd, int rs, int rt) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + int acc = 0; + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + switch (opc) { + case MMI_OPC_MULT1: + acc = 1; + /* Fall through */ + case OPC_MULT: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_muls2_i32(t2, t3, t2, t3); + if (rd) { + tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); + } + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + break; + case MMI_OPC_MULTU1: + acc = 1; + /* Fall through */ + case OPC_MULTU: + { + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mulu2_i32(t2, t3, t2, t3); + if (rd) { + tcg_gen_ext_i32_tl(cpu_gpr[rd], t2); + } + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + break; + case MMI_OPC_MADD1: + acc = 1; + /* Fall through */ + case MMI_OPC_MADD: + { + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_ext_tl_i64(t2, t0); + tcg_gen_ext_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_add_i64(t2, t2, t3); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + if (rd) { + gen_move_low32(cpu_gpr[rd], t2); + } + tcg_temp_free_i64(t2); + } + break; + case MMI_OPC_MADDU1: + acc = 1; + /* Fall through */ + case MMI_OPC_MADDU: + { + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_extu_tl_i64(t2, t0); + tcg_gen_extu_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_add_i64(t2, t2, t3); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + if (rd) { + gen_move_low32(cpu_gpr[rd], t2); + } + tcg_temp_free_i64(t2); + } + break; + default: + MIPS_INVAL("mul/madd TXx9"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + static void gen_mul_vr54xx (DisasContext *ctx, uint32_t opc, int rd, int rs, int rt) { @@ -4551,6 +6064,128 @@ static void gen_compute_branch (DisasContext *ctx, uint32_t opc, tcg_temp_free(t1); } + +/* nanoMIPS Branches */ +static void gen_compute_branch_nm(DisasContext *ctx, uint32_t opc, + int insn_bytes, + int rs, int rt, int32_t offset) +{ + target_ulong btgt = -1; + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* Load needed operands */ + switch (opc) { + case OPC_BEQ: + case OPC_BNE: + /* Compare two registers */ + if (rs != rt) { + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + } + btgt = ctx->base.pc_next + insn_bytes + offset; + break; + case OPC_BGEZAL: + /* Compare to zero */ + if (rs != 0) { + gen_load_gpr(t0, rs); + bcond_compute = 1; + } + btgt = ctx->base.pc_next + insn_bytes + offset; + break; + case OPC_BPOSGE32: + tcg_gen_andi_tl(t0, cpu_dspctrl, 0x3F); + bcond_compute = 1; + btgt = ctx->base.pc_next + insn_bytes + offset; + break; + case OPC_JR: + case OPC_JALR: + /* Jump to register */ + if (offset != 0 && offset != 16) { + /* Hint = 0 is JR/JALR, hint 16 is JR.HB/JALR.HB, the + others are reserved. */ + MIPS_INVAL("jump hint"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + gen_load_gpr(btarget, rs); + break; + default: + MIPS_INVAL("branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + if (bcond_compute == 0) { + /* No condition to be computed */ + switch (opc) { + case OPC_BEQ: /* rx == rx */ + /* Always take */ + ctx->hflags |= MIPS_HFLAG_B; + break; + case OPC_BGEZAL: /* 0 >= 0 */ + /* Always take and link */ + tcg_gen_movi_tl(cpu_gpr[31], + ctx->base.pc_next + insn_bytes); + ctx->hflags |= MIPS_HFLAG_B; + break; + case OPC_BNE: /* rx != rx */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 8); + /* Skip the instruction in the delay slot */ + ctx->base.pc_next += 4; + goto out; + case OPC_JR: + ctx->hflags |= MIPS_HFLAG_BR; + break; + case OPC_JALR: + if (rt > 0) { + tcg_gen_movi_tl(cpu_gpr[rt], + ctx->base.pc_next + insn_bytes); + } + ctx->hflags |= MIPS_HFLAG_BR; + break; + default: + MIPS_INVAL("branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + } else { + switch (opc) { + case OPC_BEQ: + tcg_gen_setcond_tl(TCG_COND_EQ, bcond, t0, t1); + goto not_likely; + case OPC_BNE: + tcg_gen_setcond_tl(TCG_COND_NE, bcond, t0, t1); + goto not_likely; + case OPC_BGEZAL: + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 0); + tcg_gen_movi_tl(cpu_gpr[31], + ctx->base.pc_next + insn_bytes); + goto not_likely; + case OPC_BPOSGE32: + tcg_gen_setcondi_tl(TCG_COND_GE, bcond, t0, 32); + not_likely: + ctx->hflags |= MIPS_HFLAG_BC; + break; + default: + MIPS_INVAL("conditional branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + } + + ctx->btarget = btgt; + + out: + if (insn_bytes == 2) { + ctx->hflags |= MIPS_HFLAG_B16; + } + tcg_temp_free(t0); + tcg_temp_free(t1); +} + + /* special3 bitfield operations */ static void gen_bitops (DisasContext *ctx, uint32_t opc, int rt, int rs, int lsb, int msb) @@ -4723,8 +6358,8 @@ static void gen_lsa(DisasContext *ctx, int opc, int rd, int rs, int rt, return; } -static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, - int bp) +static void gen_align_bits(DisasContext *ctx, int wordsz, int rd, int rs, + int rt, int bits) { TCGv t0; if (rd == 0) { @@ -4732,35 +6367,40 @@ static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, return; } t0 = tcg_temp_new(); - gen_load_gpr(t0, rt); - if (bp == 0) { - switch (opc) { - case OPC_ALIGN: + if (bits == 0 || bits == wordsz) { + if (bits == 0) { + gen_load_gpr(t0, rt); + } else { + gen_load_gpr(t0, rs); + } + switch (wordsz) { + case 32: tcg_gen_ext32s_tl(cpu_gpr[rd], t0); break; #if defined(TARGET_MIPS64) - case OPC_DALIGN: + case 64: tcg_gen_mov_tl(cpu_gpr[rd], t0); break; #endif } } else { TCGv t1 = tcg_temp_new(); + gen_load_gpr(t0, rt); gen_load_gpr(t1, rs); - switch (opc) { - case OPC_ALIGN: + switch (wordsz) { + case 32: { TCGv_i64 t2 = tcg_temp_new_i64(); tcg_gen_concat_tl_i64(t2, t1, t0); - tcg_gen_shri_i64(t2, t2, 8 * (4 - bp)); + tcg_gen_shri_i64(t2, t2, 32 - bits); gen_move_low32(cpu_gpr[rd], t2); tcg_temp_free_i64(t2); } break; #if defined(TARGET_MIPS64) - case OPC_DALIGN: - tcg_gen_shli_tl(t0, t0, 8 * bp); - tcg_gen_shri_tl(t1, t1, 8 * (8 - bp)); + case 64: + tcg_gen_shli_tl(t0, t0, bits); + tcg_gen_shri_tl(t1, t1, 64 - bits); tcg_gen_or_tl(cpu_gpr[rd], t1, t0); break; #endif @@ -4771,6 +6411,18 @@ static void gen_align(DisasContext *ctx, int opc, int rd, int rs, int rt, tcg_temp_free(t0); } +static void gen_align(DisasContext *ctx, int wordsz, int rd, int rs, int rt, + int bp) +{ + gen_align_bits(ctx, wordsz, rd, rs, rt, bp * 8); +} + +static void gen_ext(DisasContext *ctx, int wordsz, int rd, int rs, int rt, + int shift) +{ + gen_align_bits(ctx, wordsz, rd, rs, rt, wordsz - shift); +} + static void gen_bitswap(DisasContext *ctx, int opc, int rd, int rt) { TCGv t0; @@ -4882,55 +6534,66 @@ static inline void gen_mtc0_store32 (TCGv arg, target_ulong off) static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) { - const char *rn = "invalid"; - - CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); + const char *register_name = "invalid"; switch (reg) { - case 2: + case CP0_REGISTER_02: switch (sel) { case 0: + CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0)); - rn = "EntryLo0"; + register_name = "EntryLo0"; break; default: goto cp0_unimplemented; } break; - case 3: + case CP0_REGISTER_03: switch (sel) { case 0: + CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); gen_mfhc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1)); - rn = "EntryLo1"; + register_name = "EntryLo1"; break; default: goto cp0_unimplemented; } break; - case 17: + case CP0_REGISTER_09: + switch (sel) { + case 7: + CP0_CHECK(ctx->saar); + gen_helper_mfhc0_saar(arg, cpu_env); + register_name = "SAAR"; + break; + default: + goto cp0_unimplemented; + } + break; + case CP0_REGISTER_17: switch (sel) { case 0: - gen_mfhc0_load64(arg, offsetof(CPUMIPSState, lladdr), + gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_LLAddr), ctx->CP0_LLAddr_shift); - rn = "LLAddr"; + register_name = "LLAddr"; break; case 1: CP0_CHECK(ctx->mrp); gen_helper_mfhc0_maar(arg, cpu_env); - rn = "MAAR"; + register_name = "MAAR"; break; default: goto cp0_unimplemented; } break; - case 28: + case CP0_REGISTER_28: switch (sel) { case 0: case 2: case 4: case 6: gen_mfhc0_load64(arg, offsetof(CPUMIPSState, CP0_TagLo), 0); - rn = "TagLo"; + register_name = "TagLo"; break; default: goto cp0_unimplemented; @@ -4939,63 +6602,74 @@ static void gen_mfhc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } - trace_mips_translate_c0("mfhc0", rn, reg, sel); + trace_mips_translate_c0("mfhc0", register_name, reg, sel); return; cp0_unimplemented: - qemu_log_mask(LOG_UNIMP, "mfhc0 %s (reg %d sel %d)\n", rn, reg, sel); + qemu_log_mask(LOG_UNIMP, "mfhc0 %s (reg %d sel %d)\n", + register_name, reg, sel); tcg_gen_movi_tl(arg, 0); } static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) { - const char *rn = "invalid"; + const char *register_name = "invalid"; uint64_t mask = ctx->PAMask >> 36; - CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); - switch (reg) { - case 2: + case CP0_REGISTER_02: switch (sel) { case 0: + CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); tcg_gen_andi_tl(arg, arg, mask); gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo0)); - rn = "EntryLo0"; + register_name = "EntryLo0"; break; default: goto cp0_unimplemented; } break; - case 3: + case CP0_REGISTER_03: switch (sel) { case 0: + CP0_CHECK(ctx->hflags & MIPS_HFLAG_ELPA); tcg_gen_andi_tl(arg, arg, mask); gen_mthc0_entrylo(arg, offsetof(CPUMIPSState, CP0_EntryLo1)); - rn = "EntryLo1"; + register_name = "EntryLo1"; break; default: goto cp0_unimplemented; } break; - case 17: + case CP0_REGISTER_09: + switch (sel) { + case 7: + CP0_CHECK(ctx->saar); + gen_helper_mthc0_saar(cpu_env, arg); + register_name = "SAAR"; + break; + default: + goto cp0_unimplemented; + } + case CP0_REGISTER_17: switch (sel) { case 0: /* LLAddr is read-only (the only exception is bit 0 if LLB is supported); the CP0_LLAddr_rw_bitmask does not seem to be relevant for modern MIPS cores supporting MTHC0, therefore treating MTHC0 to LLAddr as NOP. */ - rn = "LLAddr"; + register_name = "LLAddr"; break; case 1: CP0_CHECK(ctx->mrp); gen_helper_mthc0_maar(cpu_env, arg); - rn = "MAAR"; + register_name = "MAAR"; break; default: goto cp0_unimplemented; } break; - case 28: + case CP0_REGISTER_28: switch (sel) { case 0: case 2: @@ -5003,7 +6677,7 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) case 6: tcg_gen_andi_tl(arg, arg, mask); gen_mthc0_store64(arg, offsetof(CPUMIPSState, CP0_TagLo)); - rn = "TagLo"; + register_name = "TagLo"; break; default: goto cp0_unimplemented; @@ -5012,10 +6686,11 @@ static void gen_mthc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } - trace_mips_translate_c0("mthc0", rn, reg, sel); + trace_mips_translate_c0("mthc0", register_name, reg, sel); cp0_unimplemented: - qemu_log_mask(LOG_UNIMP, "mthc0 %s (reg %d sel %d)\n", rn, reg, sel); + qemu_log_mask(LOG_UNIMP, "mthc0 %s (reg %d sel %d)\n", + register_name, reg, sel); } static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) @@ -5029,89 +6704,89 @@ static inline void gen_mfc0_unimplemented(DisasContext *ctx, TCGv arg) static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { - const char *rn = "invalid"; + const char *register_name = "invalid"; if (sel != 0) check_insn(ctx, ISA_MIPS32); switch (reg) { - case 0: + case CP0_REGISTER_00: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Index)); - rn = "Index"; + register_name = "Index"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); - rn = "MVPControl"; + register_name = "MVPControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); - rn = "MVPConf0"; + register_name = "MVPConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); - rn = "MVPConf1"; + register_name = "MVPConf1"; break; case 4: CP0_CHECK(ctx->vp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl)); - rn = "VPControl"; + register_name = "VPControl"; break; default: goto cp0_unimplemented; } break; - case 1: + case CP0_REGISTER_01: switch (sel) { case 0: CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); gen_helper_mfc0_random(arg, cpu_env); - rn = "Random"; + register_name = "Random"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); - rn = "VPEControl"; + register_name = "VPEControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); - rn = "VPEConf0"; + register_name = "VPEConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); - rn = "VPEConf1"; + register_name = "VPEConf1"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_YQMask)); - rn = "YQMask"; + register_name = "YQMask"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPESchedule)); - rn = "VPESchedule"; + register_name = "VPESchedule"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load64(arg, offsetof(CPUMIPSState, CP0_VPEScheFBack)); - rn = "VPEScheFBack"; + register_name = "VPEScheFBack"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); - rn = "VPEOpt"; + register_name = "VPEOpt"; break; default: goto cp0_unimplemented; } break; - case 2: + case CP0_REGISTER_02: switch (sel) { case 0: { @@ -5128,48 +6803,48 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_move_low32(arg, tmp); tcg_temp_free_i64(tmp); } - rn = "EntryLo0"; + register_name = "EntryLo0"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); - rn = "TCStatus"; + register_name = "TCStatus"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); - rn = "TCBind"; + register_name = "TCBind"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcrestart(arg, cpu_env); - rn = "TCRestart"; + register_name = "TCRestart"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tchalt(arg, cpu_env); - rn = "TCHalt"; + register_name = "TCHalt"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tccontext(arg, cpu_env); - rn = "TCContext"; + register_name = "TCContext"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcschedule(arg, cpu_env); - rn = "TCSchedule"; + register_name = "TCSchedule"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcschefback(arg, cpu_env); - rn = "TCScheFBack"; + register_name = "TCScheFBack"; break; default: goto cp0_unimplemented; } break; - case 3: + case CP0_REGISTER_03: switch (sel) { case 0: { @@ -5186,146 +6861,172 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_move_low32(arg, tmp); tcg_temp_free_i64(tmp); } - rn = "EntryLo1"; + register_name = "EntryLo1"; break; case 1: CP0_CHECK(ctx->vp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber)); - rn = "GlobalNumber"; + register_name = "GlobalNumber"; break; default: goto cp0_unimplemented; } break; - case 4: + case CP0_REGISTER_04: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context)); tcg_gen_ext32s_tl(arg, arg); - rn = "Context"; + register_name = "Context"; break; case 1: // gen_helper_mfc0_contextconfig(arg); /* SmartMIPS ASE */ - rn = "ContextConfig"; + register_name = "ContextConfig"; goto cp0_unimplemented; case 2: CP0_CHECK(ctx->ulri); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); tcg_gen_ext32s_tl(arg, arg); - rn = "UserLocal"; + register_name = "UserLocal"; break; default: goto cp0_unimplemented; } break; - case 5: + case CP0_REGISTER_05: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageMask)); - rn = "PageMask"; + register_name = "PageMask"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); - rn = "PageGrain"; + register_name = "PageGrain"; break; case 2: CP0_CHECK(ctx->sc); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl0)); tcg_gen_ext32s_tl(arg, arg); - rn = "SegCtl0"; + register_name = "SegCtl0"; break; case 3: CP0_CHECK(ctx->sc); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl1)); tcg_gen_ext32s_tl(arg, arg); - rn = "SegCtl1"; + register_name = "SegCtl1"; break; case 4: CP0_CHECK(ctx->sc); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl2)); tcg_gen_ext32s_tl(arg, arg); - rn = "SegCtl2"; + register_name = "SegCtl2"; + break; + case 5: + check_pw(ctx); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWBase)); + register_name = "PWBase"; + break; + case 6: + check_pw(ctx); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWField)); + register_name = "PWField"; + break; + case 7: + check_pw(ctx); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWSize)); + register_name = "PWSize"; break; default: goto cp0_unimplemented; } break; - case 6: + case CP0_REGISTER_06: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Wired)); - rn = "Wired"; + register_name = "Wired"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); - rn = "SRSConf0"; + register_name = "SRSConf0"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); - rn = "SRSConf1"; + register_name = "SRSConf1"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); - rn = "SRSConf2"; + register_name = "SRSConf2"; break; case 4: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); - rn = "SRSConf3"; + register_name = "SRSConf3"; break; case 5: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); - rn = "SRSConf4"; + register_name = "SRSConf4"; + break; + case 6: + check_pw(ctx); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWCtl)); + register_name = "PWCtl"; break; default: goto cp0_unimplemented; } break; - case 7: + case CP0_REGISTER_07: switch (sel) { case 0: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); - rn = "HWREna"; + register_name = "HWREna"; break; default: goto cp0_unimplemented; } break; - case 8: + case CP0_REGISTER_08: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); tcg_gen_ext32s_tl(arg, arg); - rn = "BadVAddr"; + register_name = "BadVAddr"; break; case 1: CP0_CHECK(ctx->bi); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr)); - rn = "BadInstr"; + register_name = "BadInstr"; break; case 2: CP0_CHECK(ctx->bp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP)); - rn = "BadInstrP"; + register_name = "BadInstrP"; break; - default: + case 3: + CP0_CHECK(ctx->bi); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrX)); + tcg_gen_andi_tl(arg, arg, ~0xffff); + register_name = "BadInstrX"; + break; + default: goto cp0_unimplemented; } break; - case 9: + case CP0_REGISTER_09: switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_start(); - } + } gen_helper_mfc0_count(arg, cpu_env); if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { gen_io_end(); @@ -5335,310 +7036,338 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) ensure we break completely out of translated code. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Count"; + register_name = "Count"; + break; + case 6: + CP0_CHECK(ctx->saar); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SAARI)); + register_name = "SAARI"; + break; + case 7: + CP0_CHECK(ctx->saar); + gen_helper_mfc0_saar(arg, cpu_env); + register_name = "SAAR"; break; - /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } break; - case 10: + case CP0_REGISTER_10: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi)); tcg_gen_ext32s_tl(arg, arg); - rn = "EntryHi"; + register_name = "EntryHi"; break; default: goto cp0_unimplemented; } break; - case 11: + case CP0_REGISTER_11: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare)); - rn = "Compare"; + register_name = "Compare"; break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } break; - case 12: + case CP0_REGISTER_12: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Status)); - rn = "Status"; + register_name = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); - rn = "IntCtl"; + register_name = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); - rn = "SRSCtl"; + register_name = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); - rn = "SRSMap"; + register_name = "SRSMap"; break; default: goto cp0_unimplemented; } break; - case 13: + case CP0_REGISTER_13: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Cause)); - rn = "Cause"; + register_name = "Cause"; break; default: goto cp0_unimplemented; } break; - case 14: + case CP0_REGISTER_14: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); tcg_gen_ext32s_tl(arg, arg); - rn = "EPC"; + register_name = "EPC"; break; default: goto cp0_unimplemented; } break; - case 15: + case CP0_REGISTER_15: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PRid)); - rn = "PRid"; + register_name = "PRid"; break; case 1: check_insn(ctx, ISA_MIPS32R2); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EBase)); tcg_gen_ext32s_tl(arg, arg); - rn = "EBase"; + register_name = "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"; + register_name = "CMGCRBase"; break; default: goto cp0_unimplemented; } break; - case 16: + case CP0_REGISTER_16: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config0)); - rn = "Config"; + register_name = "Config"; break; case 1: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config1)); - rn = "Config1"; + register_name = "Config1"; break; case 2: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config2)); - rn = "Config2"; + register_name = "Config2"; break; case 3: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3)); - rn = "Config3"; + register_name = "Config3"; break; case 4: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4)); - rn = "Config4"; + register_name = "Config4"; break; case 5: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5)); - rn = "Config5"; + register_name = "Config5"; break; /* 6,7 are implementation dependent */ case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6)); - rn = "Config6"; + register_name = "Config6"; break; case 7: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config7)); - rn = "Config7"; + register_name = "Config7"; break; default: goto cp0_unimplemented; } break; - case 17: + case CP0_REGISTER_17: switch (sel) { case 0: gen_helper_mfc0_lladdr(arg, cpu_env); - rn = "LLAddr"; + register_name = "LLAddr"; break; case 1: CP0_CHECK(ctx->mrp); gen_helper_mfc0_maar(arg, cpu_env); - rn = "MAAR"; + register_name = "MAAR"; break; case 2: CP0_CHECK(ctx->mrp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI)); - rn = "MAARI"; + register_name = "MAARI"; break; default: goto cp0_unimplemented; } break; - case 18: + case CP0_REGISTER_18: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_1e0i(mfc0_watchlo, arg, sel); - rn = "WatchLo"; + register_name = "WatchLo"; break; default: goto cp0_unimplemented; } break; - case 19: + case CP0_REGISTER_19: switch (sel) { - case 0 ...7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_1e0i(mfc0_watchhi, arg, sel); - rn = "WatchHi"; + register_name = "WatchHi"; break; default: goto cp0_unimplemented; } break; - case 20: + case CP0_REGISTER_20: switch (sel) { case 0: #if defined(TARGET_MIPS64) check_insn(ctx, ISA_MIPS3); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); tcg_gen_ext32s_tl(arg, arg); - rn = "XContext"; + register_name = "XContext"; break; #endif default: goto cp0_unimplemented; } break; - case 21: + case CP0_REGISTER_21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask)); - rn = "Framemask"; + register_name = "Framemask"; break; default: goto cp0_unimplemented; } break; - case 22: + case CP0_REGISTER_22: tcg_gen_movi_tl(arg, 0); /* unimplemented */ - rn = "'Diagnostic"; /* implementation dependent */ + register_name = "'Diagnostic"; /* implementation dependent */ break; - case 23: + case CP0_REGISTER_23: switch (sel) { case 0: gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */ - rn = "Debug"; + register_name = "Debug"; break; case 1: // gen_helper_mfc0_tracecontrol(arg); /* PDtrace support */ - rn = "TraceControl"; + register_name = "TraceControl"; goto cp0_unimplemented; case 2: // gen_helper_mfc0_tracecontrol2(arg); /* PDtrace support */ - rn = "TraceControl2"; + register_name = "TraceControl2"; goto cp0_unimplemented; case 3: // gen_helper_mfc0_usertracedata(arg); /* PDtrace support */ - rn = "UserTraceData"; + register_name = "UserTraceData"; goto cp0_unimplemented; case 4: // gen_helper_mfc0_tracebpc(arg); /* PDtrace support */ - rn = "TraceBPC"; + register_name = "TraceBPC"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 24: + case CP0_REGISTER_24: switch (sel) { case 0: /* EJTAG support */ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); tcg_gen_ext32s_tl(arg, arg); - rn = "DEPC"; + register_name = "DEPC"; break; default: goto cp0_unimplemented; } break; - case 25: + case CP0_REGISTER_25: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Performance0)); - rn = "Performance0"; + register_name = "Performance0"; break; case 1: // gen_helper_mfc0_performance1(arg); - rn = "Performance1"; + register_name = "Performance1"; goto cp0_unimplemented; case 2: // gen_helper_mfc0_performance2(arg); - rn = "Performance2"; + register_name = "Performance2"; goto cp0_unimplemented; case 3: // gen_helper_mfc0_performance3(arg); - rn = "Performance3"; + register_name = "Performance3"; goto cp0_unimplemented; case 4: // gen_helper_mfc0_performance4(arg); - rn = "Performance4"; + register_name = "Performance4"; goto cp0_unimplemented; case 5: // gen_helper_mfc0_performance5(arg); - rn = "Performance5"; + register_name = "Performance5"; goto cp0_unimplemented; case 6: // gen_helper_mfc0_performance6(arg); - rn = "Performance6"; + register_name = "Performance6"; goto cp0_unimplemented; case 7: // gen_helper_mfc0_performance7(arg); - rn = "Performance7"; + register_name = "Performance7"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 26: + case CP0_REGISTER_26: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl)); - rn = "ErrCtl"; + register_name = "ErrCtl"; break; default: goto cp0_unimplemented; } break; - case 27: + case CP0_REGISTER_27: switch (sel) { - case 0 ... 3: + case 0: + case 1: + case 2: + case 3: tcg_gen_movi_tl(arg, 0); /* unimplemented */ - rn = "CacheErr"; + register_name = "CacheErr"; break; default: goto cp0_unimplemented; } break; - case 28: + case CP0_REGISTER_28: switch (sel) { case 0: case 2: @@ -5650,63 +7379,68 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) gen_move_low32(arg, tmp); tcg_temp_free_i64(tmp); } - rn = "TagLo"; + register_name = "TagLo"; break; case 1: case 3: case 5: case 7: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataLo)); - rn = "DataLo"; + register_name = "DataLo"; break; default: goto cp0_unimplemented; } break; - case 29: + case CP0_REGISTER_29: switch (sel) { case 0: case 2: case 4: case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagHi)); - rn = "TagHi"; + register_name = "TagHi"; break; case 1: case 3: case 5: case 7: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataHi)); - rn = "DataHi"; + register_name = "DataHi"; break; default: goto cp0_unimplemented; } break; - case 30: + case CP0_REGISTER_30: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); tcg_gen_ext32s_tl(arg, arg); - rn = "ErrorEPC"; + register_name = "ErrorEPC"; break; default: goto cp0_unimplemented; } break; - case 31: + case CP0_REGISTER_31: switch (sel) { case 0: /* EJTAG support */ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); - rn = "DESAVE"; + register_name = "DESAVE"; break; - case 2 ... 7: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: CP0_CHECK(ctx->kscrexist & (1 << sel)); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_KScratch[sel-2])); tcg_gen_ext32s_tl(arg, arg); - rn = "KScratch"; + register_name = "KScratch"; break; default: goto cp0_unimplemented; @@ -5715,17 +7449,18 @@ static void gen_mfc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } - trace_mips_translate_c0("mfc0", rn, reg, sel); + trace_mips_translate_c0("mfc0", register_name, reg, sel); return; cp0_unimplemented: - qemu_log_mask(LOG_UNIMP, "mfc0 %s (reg %d sel %d)\n", rn, reg, sel); + qemu_log_mask(LOG_UNIMP, "mfc0 %s (reg %d sel %d)\n", + register_name, reg, sel); gen_mfc0_unimplemented(ctx, arg); } static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { - const char *rn = "invalid"; + const char *register_name = "invalid"; if (sel != 0) check_insn(ctx, ISA_MIPS32); @@ -5735,292 +7470,325 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) } switch (reg) { - case 0: + case CP0_REGISTER_00: switch (sel) { case 0: gen_helper_mtc0_index(cpu_env, arg); - rn = "Index"; + register_name = "Index"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); - rn = "MVPControl"; + register_name = "MVPControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ - rn = "MVPConf0"; + register_name = "MVPConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ - rn = "MVPConf1"; + register_name = "MVPConf1"; break; case 4: CP0_CHECK(ctx->vp); /* ignored */ - rn = "VPControl"; + register_name = "VPControl"; break; default: goto cp0_unimplemented; } break; - case 1: + case CP0_REGISTER_01: switch (sel) { case 0: /* ignored */ - rn = "Random"; + register_name = "Random"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); - rn = "VPEControl"; + register_name = "VPEControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); - rn = "VPEConf0"; + register_name = "VPEConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); - rn = "VPEConf1"; + register_name = "VPEConf1"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); - rn = "YQMask"; + register_name = "YQMask"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); - rn = "VPESchedule"; + register_name = "VPESchedule"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); - rn = "VPEScheFBack"; + register_name = "VPEScheFBack"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); - rn = "VPEOpt"; + register_name = "VPEOpt"; break; default: goto cp0_unimplemented; } break; - case 2: + case CP0_REGISTER_02: switch (sel) { case 0: gen_helper_mtc0_entrylo0(cpu_env, arg); - rn = "EntryLo0"; + register_name = "EntryLo0"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); - rn = "TCStatus"; + register_name = "TCStatus"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); - rn = "TCBind"; + register_name = "TCBind"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); - rn = "TCRestart"; + register_name = "TCRestart"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); - rn = "TCHalt"; + register_name = "TCHalt"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); - rn = "TCContext"; + register_name = "TCContext"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); - rn = "TCSchedule"; + register_name = "TCSchedule"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); - rn = "TCScheFBack"; + register_name = "TCScheFBack"; break; default: goto cp0_unimplemented; } break; - case 3: + case CP0_REGISTER_03: switch (sel) { case 0: gen_helper_mtc0_entrylo1(cpu_env, arg); - rn = "EntryLo1"; + register_name = "EntryLo1"; break; case 1: CP0_CHECK(ctx->vp); /* ignored */ - rn = "GlobalNumber"; + register_name = "GlobalNumber"; break; default: goto cp0_unimplemented; } break; - case 4: + case CP0_REGISTER_04: switch (sel) { case 0: gen_helper_mtc0_context(cpu_env, arg); - rn = "Context"; + register_name = "Context"; break; case 1: // gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ - rn = "ContextConfig"; + register_name = "ContextConfig"; goto cp0_unimplemented; case 2: CP0_CHECK(ctx->ulri); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); - rn = "UserLocal"; + register_name = "UserLocal"; break; default: goto cp0_unimplemented; } break; - case 5: + case CP0_REGISTER_05: switch (sel) { case 0: gen_helper_mtc0_pagemask(cpu_env, arg); - rn = "PageMask"; + register_name = "PageMask"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); - rn = "PageGrain"; + register_name = "PageGrain"; ctx->base.is_jmp = DISAS_STOP; break; case 2: CP0_CHECK(ctx->sc); gen_helper_mtc0_segctl0(cpu_env, arg); - rn = "SegCtl0"; + register_name = "SegCtl0"; break; case 3: CP0_CHECK(ctx->sc); gen_helper_mtc0_segctl1(cpu_env, arg); - rn = "SegCtl1"; + register_name = "SegCtl1"; break; case 4: CP0_CHECK(ctx->sc); gen_helper_mtc0_segctl2(cpu_env, arg); - rn = "SegCtl2"; + register_name = "SegCtl2"; + break; + case 5: + check_pw(ctx); + gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_PWBase)); + register_name = "PWBase"; + break; + case 6: + check_pw(ctx); + gen_helper_mtc0_pwfield(cpu_env, arg); + register_name = "PWField"; + break; + case 7: + check_pw(ctx); + gen_helper_mtc0_pwsize(cpu_env, arg); + register_name = "PWSize"; break; default: goto cp0_unimplemented; } break; - case 6: + case CP0_REGISTER_06: switch (sel) { case 0: gen_helper_mtc0_wired(cpu_env, arg); - rn = "Wired"; + register_name = "Wired"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf0(cpu_env, arg); - rn = "SRSConf0"; + register_name = "SRSConf0"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf1(cpu_env, arg); - rn = "SRSConf1"; + register_name = "SRSConf1"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf2(cpu_env, arg); - rn = "SRSConf2"; + register_name = "SRSConf2"; break; case 4: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf3(cpu_env, arg); - rn = "SRSConf3"; + register_name = "SRSConf3"; break; case 5: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf4(cpu_env, arg); - rn = "SRSConf4"; + register_name = "SRSConf4"; + break; + case 6: + check_pw(ctx); + gen_helper_mtc0_pwctl(cpu_env, arg); + register_name = "PWCtl"; break; default: goto cp0_unimplemented; } break; - case 7: + case CP0_REGISTER_07: switch (sel) { case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); ctx->base.is_jmp = DISAS_STOP; - rn = "HWREna"; + register_name = "HWREna"; break; default: goto cp0_unimplemented; } break; - case 8: + case CP0_REGISTER_08: switch (sel) { case 0: /* ignored */ - rn = "BadVAddr"; + register_name = "BadVAddr"; break; case 1: /* ignored */ - rn = "BadInstr"; + register_name = "BadInstr"; break; case 2: /* ignored */ - rn = "BadInstrP"; + register_name = "BadInstrP"; + break; + case 3: + /* ignored */ + register_name = "BadInstrX"; break; default: goto cp0_unimplemented; } break; - case 9: + case CP0_REGISTER_09: switch (sel) { case 0: gen_helper_mtc0_count(cpu_env, arg); - rn = "Count"; + register_name = "Count"; + break; + case 6: + CP0_CHECK(ctx->saar); + gen_helper_mtc0_saari(cpu_env, arg); + register_name = "SAARI"; + break; + case 7: + CP0_CHECK(ctx->saar); + gen_helper_mtc0_saar(cpu_env, arg); + register_name = "SAAR"; break; - /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } break; - case 10: + case CP0_REGISTER_10: switch (sel) { case 0: gen_helper_mtc0_entryhi(cpu_env, arg); - rn = "EntryHi"; + register_name = "EntryHi"; break; default: goto cp0_unimplemented; } break; - case 11: + case CP0_REGISTER_11: switch (sel) { case 0: gen_helper_mtc0_compare(cpu_env, arg); - rn = "Compare"; + register_name = "Compare"; break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } break; - case 12: + case CP0_REGISTER_12: switch (sel) { case 0: save_cpu_state(ctx, 1); @@ -6028,34 +7796,34 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Status"; + register_name = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "IntCtl"; + register_name = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "SRSCtl"; + register_name = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "SRSMap"; + register_name = "SRSMap"; break; default: goto cp0_unimplemented; } break; - case 13: + case CP0_REGISTER_13: switch (sel) { case 0: save_cpu_state(ctx, 1); @@ -6065,173 +7833,189 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) * translated code to check for pending interrupts. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Cause"; + register_name = "Cause"; break; default: goto cp0_unimplemented; } break; - case 14: + case CP0_REGISTER_14: switch (sel) { case 0: tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); - rn = "EPC"; + register_name = "EPC"; break; default: goto cp0_unimplemented; } break; - case 15: + case CP0_REGISTER_15: switch (sel) { case 0: /* ignored */ - rn = "PRid"; + register_name = "PRid"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_ebase(cpu_env, arg); - rn = "EBase"; + register_name = "EBase"; break; default: goto cp0_unimplemented; } break; - case 16: + case CP0_REGISTER_16: switch (sel) { case 0: gen_helper_mtc0_config0(cpu_env, arg); - rn = "Config"; + register_name = "Config"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ - rn = "Config1"; + register_name = "Config1"; break; case 2: gen_helper_mtc0_config2(cpu_env, arg); - rn = "Config2"; + register_name = "Config2"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); - rn = "Config3"; + register_name = "Config3"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case 4: gen_helper_mtc0_config4(cpu_env, arg); - rn = "Config4"; + register_name = "Config4"; ctx->base.is_jmp = DISAS_STOP; break; case 5: gen_helper_mtc0_config5(cpu_env, arg); - rn = "Config5"; + register_name = "Config5"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ case 6: /* ignored */ - rn = "Config6"; + register_name = "Config6"; break; case 7: /* ignored */ - rn = "Config7"; + register_name = "Config7"; break; default: - rn = "Invalid config selector"; + register_name = "Invalid config selector"; goto cp0_unimplemented; } break; - case 17: + case CP0_REGISTER_17: switch (sel) { case 0: gen_helper_mtc0_lladdr(cpu_env, arg); - rn = "LLAddr"; + register_name = "LLAddr"; break; case 1: CP0_CHECK(ctx->mrp); gen_helper_mtc0_maar(cpu_env, arg); - rn = "MAAR"; + register_name = "MAAR"; break; case 2: CP0_CHECK(ctx->mrp); gen_helper_mtc0_maari(cpu_env, arg); - rn = "MAARI"; + register_name = "MAARI"; break; default: goto cp0_unimplemented; } break; - case 18: + case CP0_REGISTER_18: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_0e1i(mtc0_watchlo, arg, sel); - rn = "WatchLo"; + register_name = "WatchLo"; break; default: goto cp0_unimplemented; } break; - case 19: + case CP0_REGISTER_19: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_0e1i(mtc0_watchhi, arg, sel); - rn = "WatchHi"; + register_name = "WatchHi"; break; default: goto cp0_unimplemented; } break; - case 20: + case CP0_REGISTER_20: switch (sel) { case 0: #if defined(TARGET_MIPS64) check_insn(ctx, ISA_MIPS3); gen_helper_mtc0_xcontext(cpu_env, arg); - rn = "XContext"; + register_name = "XContext"; break; #endif default: goto cp0_unimplemented; } break; - case 21: + case CP0_REGISTER_21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_helper_mtc0_framemask(cpu_env, arg); - rn = "Framemask"; + register_name = "Framemask"; break; default: goto cp0_unimplemented; } break; - case 22: + case CP0_REGISTER_22: /* ignored */ - rn = "Diagnostic"; /* implementation dependent */ + register_name = "Diagnostic"; /* implementation dependent */ break; - case 23: + case CP0_REGISTER_23: switch (sel) { case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Debug"; + register_name = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ - rn = "TraceControl"; + register_name = "TraceControl"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ - rn = "TraceControl2"; + register_name = "TraceControl2"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; @@ -6239,7 +8023,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ - rn = "UserTraceData"; + register_name = "UserTraceData"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; goto cp0_unimplemented; @@ -6247,145 +8031,153 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "TraceBPC"; + register_name = "TraceBPC"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 24: + case CP0_REGISTER_24: switch (sel) { case 0: /* EJTAG support */ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); - rn = "DEPC"; + register_name = "DEPC"; break; default: goto cp0_unimplemented; } break; - case 25: + case CP0_REGISTER_25: switch (sel) { case 0: gen_helper_mtc0_performance0(cpu_env, arg); - rn = "Performance0"; + register_name = "Performance0"; break; case 1: // gen_helper_mtc0_performance1(arg); - rn = "Performance1"; + register_name = "Performance1"; goto cp0_unimplemented; case 2: // gen_helper_mtc0_performance2(arg); - rn = "Performance2"; + register_name = "Performance2"; goto cp0_unimplemented; case 3: // gen_helper_mtc0_performance3(arg); - rn = "Performance3"; + register_name = "Performance3"; goto cp0_unimplemented; case 4: // gen_helper_mtc0_performance4(arg); - rn = "Performance4"; + register_name = "Performance4"; goto cp0_unimplemented; case 5: // gen_helper_mtc0_performance5(arg); - rn = "Performance5"; + register_name = "Performance5"; goto cp0_unimplemented; case 6: // gen_helper_mtc0_performance6(arg); - rn = "Performance6"; + register_name = "Performance6"; goto cp0_unimplemented; case 7: // gen_helper_mtc0_performance7(arg); - rn = "Performance7"; + register_name = "Performance7"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 26: + case CP0_REGISTER_26: switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); ctx->base.is_jmp = DISAS_STOP; - rn = "ErrCtl"; + register_name = "ErrCtl"; break; default: goto cp0_unimplemented; } break; - case 27: + case CP0_REGISTER_27: switch (sel) { - case 0 ... 3: + case 0: + case 1: + case 2: + case 3: /* ignored */ - rn = "CacheErr"; + register_name = "CacheErr"; break; default: goto cp0_unimplemented; } break; - case 28: + case CP0_REGISTER_28: switch (sel) { case 0: case 2: case 4: case 6: gen_helper_mtc0_taglo(cpu_env, arg); - rn = "TagLo"; + register_name = "TagLo"; break; case 1: case 3: case 5: case 7: gen_helper_mtc0_datalo(cpu_env, arg); - rn = "DataLo"; + register_name = "DataLo"; break; default: goto cp0_unimplemented; } break; - case 29: + case CP0_REGISTER_29: switch (sel) { case 0: case 2: case 4: case 6: gen_helper_mtc0_taghi(cpu_env, arg); - rn = "TagHi"; + register_name = "TagHi"; break; case 1: case 3: case 5: case 7: gen_helper_mtc0_datahi(cpu_env, arg); - rn = "DataHi"; + register_name = "DataHi"; break; default: - rn = "invalid sel"; + register_name = "invalid sel"; goto cp0_unimplemented; } break; - case 30: + case CP0_REGISTER_30: switch (sel) { case 0: tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); - rn = "ErrorEPC"; + register_name = "ErrorEPC"; break; default: goto cp0_unimplemented; } break; - case 31: + case CP0_REGISTER_31: switch (sel) { case 0: /* EJTAG support */ gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); - rn = "DESAVE"; + register_name = "DESAVE"; break; - case 2 ... 7: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: CP0_CHECK(ctx->kscrexist & (1 << sel)); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_KScratch[sel-2])); - rn = "KScratch"; + register_name = "KScratch"; break; default: goto cp0_unimplemented; @@ -6394,7 +8186,7 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } - trace_mips_translate_c0("mtc0", rn, reg, sel); + trace_mips_translate_c0("mtc0", register_name, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { @@ -6407,271 +8199,298 @@ static void gen_mtc0(DisasContext *ctx, TCGv arg, int reg, int sel) return; cp0_unimplemented: - qemu_log_mask(LOG_UNIMP, "mtc0 %s (reg %d sel %d)\n", rn, reg, sel); + qemu_log_mask(LOG_UNIMP, "mtc0 %s (reg %d sel %d)\n", + register_name, reg, sel); } #if defined(TARGET_MIPS64) static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) { - const char *rn = "invalid"; + const char *register_name = "invalid"; if (sel != 0) check_insn(ctx, ISA_MIPS64); switch (reg) { - case 0: + case CP0_REGISTER_00: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Index)); - rn = "Index"; + register_name = "Index"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpcontrol(arg, cpu_env); - rn = "MVPControl"; + register_name = "MVPControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf0(arg, cpu_env); - rn = "MVPConf0"; + register_name = "MVPConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_mvpconf1(arg, cpu_env); - rn = "MVPConf1"; + register_name = "MVPConf1"; break; case 4: CP0_CHECK(ctx->vp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPControl)); - rn = "VPControl"; + register_name = "VPControl"; break; default: goto cp0_unimplemented; } break; - case 1: + case CP0_REGISTER_01: switch (sel) { case 0: CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); gen_helper_mfc0_random(arg, cpu_env); - rn = "Random"; + register_name = "Random"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEControl)); - rn = "VPEControl"; + register_name = "VPEControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf0)); - rn = "VPEConf0"; + register_name = "VPEConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEConf1)); - rn = "VPEConf1"; + register_name = "VPEConf1"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_YQMask)); - rn = "YQMask"; + register_name = "YQMask"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); - rn = "VPESchedule"; + register_name = "VPESchedule"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); - rn = "VPEScheFBack"; + register_name = "VPEScheFBack"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_VPEOpt)); - rn = "VPEOpt"; + register_name = "VPEOpt"; break; default: goto cp0_unimplemented; } break; - case 2: + case CP0_REGISTER_02: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo0)); - rn = "EntryLo0"; + register_name = "EntryLo0"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcstatus(arg, cpu_env); - rn = "TCStatus"; + register_name = "TCStatus"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mfc0_tcbind(arg, cpu_env); - rn = "TCBind"; + register_name = "TCBind"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tcrestart(arg, cpu_env); - rn = "TCRestart"; + register_name = "TCRestart"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tchalt(arg, cpu_env); - rn = "TCHalt"; + register_name = "TCHalt"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tccontext(arg, cpu_env); - rn = "TCContext"; + register_name = "TCContext"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tcschedule(arg, cpu_env); - rn = "TCSchedule"; + register_name = "TCSchedule"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_dmfc0_tcschefback(arg, cpu_env); - rn = "TCScheFBack"; + register_name = "TCScheFBack"; break; default: goto cp0_unimplemented; } break; - case 3: + case CP0_REGISTER_03: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryLo1)); - rn = "EntryLo1"; + register_name = "EntryLo1"; break; case 1: CP0_CHECK(ctx->vp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_GlobalNumber)); - rn = "GlobalNumber"; + register_name = "GlobalNumber"; break; default: goto cp0_unimplemented; } break; - case 4: + case CP0_REGISTER_04: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_Context)); - rn = "Context"; + register_name = "Context"; break; case 1: // gen_helper_dmfc0_contextconfig(arg); /* SmartMIPS ASE */ - rn = "ContextConfig"; + register_name = "ContextConfig"; goto cp0_unimplemented; case 2: CP0_CHECK(ctx->ulri); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); - rn = "UserLocal"; + register_name = "UserLocal"; break; default: goto cp0_unimplemented; } break; - case 5: + case CP0_REGISTER_05: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageMask)); - rn = "PageMask"; + register_name = "PageMask"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PageGrain)); - rn = "PageGrain"; + register_name = "PageGrain"; break; case 2: CP0_CHECK(ctx->sc); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl0)); - rn = "SegCtl0"; + register_name = "SegCtl0"; break; case 3: CP0_CHECK(ctx->sc); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl1)); - rn = "SegCtl1"; + register_name = "SegCtl1"; break; case 4: CP0_CHECK(ctx->sc); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_SegCtl2)); - rn = "SegCtl2"; + register_name = "SegCtl2"; + break; + case 5: + check_pw(ctx); + tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWBase)); + register_name = "PWBase"; + break; + case 6: + check_pw(ctx); + tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWField)); + register_name = "PWField"; + break; + case 7: + check_pw(ctx); + tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWSize)); + register_name = "PWSize"; break; default: goto cp0_unimplemented; } break; - case 6: + case CP0_REGISTER_06: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Wired)); - rn = "Wired"; + register_name = "Wired"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf0)); - rn = "SRSConf0"; + register_name = "SRSConf0"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf1)); - rn = "SRSConf1"; + register_name = "SRSConf1"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf2)); - rn = "SRSConf2"; + register_name = "SRSConf2"; break; case 4: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf3)); - rn = "SRSConf3"; + register_name = "SRSConf3"; break; case 5: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSConf4)); - rn = "SRSConf4"; + register_name = "SRSConf4"; + break; + case 6: + check_pw(ctx); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PWCtl)); + register_name = "PWCtl"; break; default: goto cp0_unimplemented; } break; - case 7: + case CP0_REGISTER_07: switch (sel) { case 0: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_HWREna)); - rn = "HWREna"; + register_name = "HWREna"; break; default: goto cp0_unimplemented; } break; - case 8: + case CP0_REGISTER_08: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); - rn = "BadVAddr"; + register_name = "BadVAddr"; break; case 1: CP0_CHECK(ctx->bi); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstr)); - rn = "BadInstr"; + register_name = "BadInstr"; break; case 2: CP0_CHECK(ctx->bp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrP)); - rn = "BadInstrP"; + register_name = "BadInstrP"; + break; + case 3: + CP0_CHECK(ctx->bi); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_BadInstrX)); + tcg_gen_andi_tl(arg, arg, ~0xffff); + register_name = "BadInstrX"; break; default: goto cp0_unimplemented; } break; - case 9: + case CP0_REGISTER_09: switch (sel) { case 0: /* Mark as an IO operation because we read the time. */ @@ -6687,364 +8506,397 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) ensure we break completely out of translated code. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Count"; + register_name = "Count"; + break; + case 6: + CP0_CHECK(ctx->saar); + gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SAARI)); + register_name = "SAARI"; + break; + case 7: + CP0_CHECK(ctx->saar); + gen_helper_dmfc0_saar(arg, cpu_env); + register_name = "SAAR"; break; - /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } break; - case 10: + case CP0_REGISTER_10: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EntryHi)); - rn = "EntryHi"; + register_name = "EntryHi"; break; default: goto cp0_unimplemented; } break; - case 11: + case CP0_REGISTER_11: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Compare)); - rn = "Compare"; + register_name = "Compare"; break; /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } break; - case 12: + case CP0_REGISTER_12: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Status)); - rn = "Status"; + register_name = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_IntCtl)); - rn = "IntCtl"; + register_name = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSCtl)); - rn = "SRSCtl"; + register_name = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); - rn = "SRSMap"; + register_name = "SRSMap"; break; default: goto cp0_unimplemented; } break; - case 13: + case CP0_REGISTER_13: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Cause)); - rn = "Cause"; + register_name = "Cause"; break; default: goto cp0_unimplemented; } break; - case 14: + case CP0_REGISTER_14: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); - rn = "EPC"; + register_name = "EPC"; break; default: goto cp0_unimplemented; } break; - case 15: + case CP0_REGISTER_15: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_PRid)); - rn = "PRid"; + register_name = "PRid"; break; case 1: check_insn(ctx, ISA_MIPS32R2); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EBase)); - rn = "EBase"; + register_name = "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"; + register_name = "CMGCRBase"; break; default: goto cp0_unimplemented; } break; - case 16: + case CP0_REGISTER_16: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config0)); - rn = "Config"; + register_name = "Config"; break; case 1: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config1)); - rn = "Config1"; + register_name = "Config1"; break; case 2: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config2)); - rn = "Config2"; + register_name = "Config2"; break; case 3: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config3)); - rn = "Config3"; + register_name = "Config3"; break; case 4: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config4)); - rn = "Config4"; + register_name = "Config4"; break; case 5: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config5)); - rn = "Config5"; + register_name = "Config5"; break; /* 6,7 are implementation dependent */ case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config6)); - rn = "Config6"; + register_name = "Config6"; break; case 7: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Config7)); - rn = "Config7"; + register_name = "Config7"; break; default: goto cp0_unimplemented; } break; - case 17: + case CP0_REGISTER_17: switch (sel) { case 0: gen_helper_dmfc0_lladdr(arg, cpu_env); - rn = "LLAddr"; + register_name = "LLAddr"; break; case 1: CP0_CHECK(ctx->mrp); gen_helper_dmfc0_maar(arg, cpu_env); - rn = "MAAR"; + register_name = "MAAR"; break; case 2: CP0_CHECK(ctx->mrp); gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_MAARI)); - rn = "MAARI"; + register_name = "MAARI"; break; default: goto cp0_unimplemented; } break; - case 18: + case CP0_REGISTER_18: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_1e0i(dmfc0_watchlo, arg, sel); - rn = "WatchLo"; + register_name = "WatchLo"; break; default: goto cp0_unimplemented; } break; - case 19: + case CP0_REGISTER_19: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_1e0i(mfc0_watchhi, arg, sel); - rn = "WatchHi"; + register_name = "WatchHi"; break; default: goto cp0_unimplemented; } break; - case 20: + case CP0_REGISTER_20: switch (sel) { case 0: check_insn(ctx, ISA_MIPS3); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_XContext)); - rn = "XContext"; + register_name = "XContext"; break; default: goto cp0_unimplemented; } break; - case 21: + case CP0_REGISTER_21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Framemask)); - rn = "Framemask"; + register_name = "Framemask"; break; default: goto cp0_unimplemented; } break; - case 22: + case CP0_REGISTER_22: tcg_gen_movi_tl(arg, 0); /* unimplemented */ - rn = "'Diagnostic"; /* implementation dependent */ + register_name = "'Diagnostic"; /* implementation dependent */ break; - case 23: + case CP0_REGISTER_23: switch (sel) { case 0: gen_helper_mfc0_debug(arg, cpu_env); /* EJTAG support */ - rn = "Debug"; + register_name = "Debug"; break; case 1: // gen_helper_dmfc0_tracecontrol(arg, cpu_env); /* PDtrace support */ - rn = "TraceControl"; + register_name = "TraceControl"; goto cp0_unimplemented; case 2: // gen_helper_dmfc0_tracecontrol2(arg, cpu_env); /* PDtrace support */ - rn = "TraceControl2"; + register_name = "TraceControl2"; goto cp0_unimplemented; case 3: // gen_helper_dmfc0_usertracedata(arg, cpu_env); /* PDtrace support */ - rn = "UserTraceData"; + register_name = "UserTraceData"; goto cp0_unimplemented; case 4: // gen_helper_dmfc0_tracebpc(arg, cpu_env); /* PDtrace support */ - rn = "TraceBPC"; + register_name = "TraceBPC"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 24: + case CP0_REGISTER_24: switch (sel) { case 0: /* EJTAG support */ tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); - rn = "DEPC"; + register_name = "DEPC"; break; default: goto cp0_unimplemented; } break; - case 25: + case CP0_REGISTER_25: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_Performance0)); - rn = "Performance0"; + register_name = "Performance0"; break; case 1: // gen_helper_dmfc0_performance1(arg); - rn = "Performance1"; + register_name = "Performance1"; goto cp0_unimplemented; case 2: // gen_helper_dmfc0_performance2(arg); - rn = "Performance2"; + register_name = "Performance2"; goto cp0_unimplemented; case 3: // gen_helper_dmfc0_performance3(arg); - rn = "Performance3"; + register_name = "Performance3"; goto cp0_unimplemented; case 4: // gen_helper_dmfc0_performance4(arg); - rn = "Performance4"; + register_name = "Performance4"; goto cp0_unimplemented; case 5: // gen_helper_dmfc0_performance5(arg); - rn = "Performance5"; + register_name = "Performance5"; goto cp0_unimplemented; case 6: // gen_helper_dmfc0_performance6(arg); - rn = "Performance6"; + register_name = "Performance6"; goto cp0_unimplemented; case 7: // gen_helper_dmfc0_performance7(arg); - rn = "Performance7"; + register_name = "Performance7"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 26: + case CP0_REGISTER_26: switch (sel) { case 0: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_ErrCtl)); - rn = "ErrCtl"; + register_name = "ErrCtl"; break; default: goto cp0_unimplemented; } break; - case 27: + case CP0_REGISTER_27: switch (sel) { /* ignored */ - case 0 ... 3: + case 0: + case 1: + case 2: + case 3: tcg_gen_movi_tl(arg, 0); /* unimplemented */ - rn = "CacheErr"; + register_name = "CacheErr"; break; default: goto cp0_unimplemented; } break; - case 28: + case CP0_REGISTER_28: switch (sel) { case 0: case 2: case 4: case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagLo)); - rn = "TagLo"; + register_name = "TagLo"; break; case 1: case 3: case 5: case 7: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataLo)); - rn = "DataLo"; + register_name = "DataLo"; break; default: goto cp0_unimplemented; } break; - case 29: + case CP0_REGISTER_29: switch (sel) { case 0: case 2: case 4: case 6: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_TagHi)); - rn = "TagHi"; + register_name = "TagHi"; break; case 1: case 3: case 5: case 7: gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DataHi)); - rn = "DataHi"; + register_name = "DataHi"; break; default: goto cp0_unimplemented; } break; - case 30: + case CP0_REGISTER_30: switch (sel) { case 0: tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); - rn = "ErrorEPC"; + register_name = "ErrorEPC"; break; default: goto cp0_unimplemented; } break; - case 31: + case CP0_REGISTER_31: switch (sel) { case 0: /* EJTAG support */ gen_mfc0_load32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); - rn = "DESAVE"; + register_name = "DESAVE"; break; - case 2 ... 7: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: CP0_CHECK(ctx->kscrexist & (1 << sel)); tcg_gen_ld_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_KScratch[sel-2])); - rn = "KScratch"; + register_name = "KScratch"; break; default: goto cp0_unimplemented; @@ -7053,17 +8905,18 @@ static void gen_dmfc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } - trace_mips_translate_c0("dmfc0", rn, reg, sel); + trace_mips_translate_c0("dmfc0", register_name, reg, sel); return; cp0_unimplemented: - qemu_log_mask(LOG_UNIMP, "dmfc0 %s (reg %d sel %d)\n", rn, reg, sel); + qemu_log_mask(LOG_UNIMP, "dmfc0 %s (reg %d sel %d)\n", + register_name, reg, sel); gen_mfc0_unimplemented(ctx, arg); } static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) { - const char *rn = "invalid"; + const char *register_name = "invalid"; if (sel != 0) check_insn(ctx, ISA_MIPS64); @@ -7073,284 +8926,317 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) } switch (reg) { - case 0: + case CP0_REGISTER_00: switch (sel) { case 0: gen_helper_mtc0_index(cpu_env, arg); - rn = "Index"; + register_name = "Index"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_mvpcontrol(cpu_env, arg); - rn = "MVPControl"; + register_name = "MVPControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ - rn = "MVPConf0"; + register_name = "MVPConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); /* ignored */ - rn = "MVPConf1"; + register_name = "MVPConf1"; break; case 4: CP0_CHECK(ctx->vp); /* ignored */ - rn = "VPControl"; + register_name = "VPControl"; break; default: goto cp0_unimplemented; } break; - case 1: + case CP0_REGISTER_01: switch (sel) { case 0: /* ignored */ - rn = "Random"; + register_name = "Random"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpecontrol(cpu_env, arg); - rn = "VPEControl"; + register_name = "VPEControl"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf0(cpu_env, arg); - rn = "VPEConf0"; + register_name = "VPEConf0"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeconf1(cpu_env, arg); - rn = "VPEConf1"; + register_name = "VPEConf1"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_yqmask(cpu_env, arg); - rn = "YQMask"; + register_name = "YQMask"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPESchedule)); - rn = "VPESchedule"; + register_name = "VPESchedule"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_VPEScheFBack)); - rn = "VPEScheFBack"; + register_name = "VPEScheFBack"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_vpeopt(cpu_env, arg); - rn = "VPEOpt"; + register_name = "VPEOpt"; break; default: goto cp0_unimplemented; } break; - case 2: + case CP0_REGISTER_02: switch (sel) { case 0: gen_helper_dmtc0_entrylo0(cpu_env, arg); - rn = "EntryLo0"; + register_name = "EntryLo0"; break; case 1: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcstatus(cpu_env, arg); - rn = "TCStatus"; + register_name = "TCStatus"; break; case 2: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcbind(cpu_env, arg); - rn = "TCBind"; + register_name = "TCBind"; break; case 3: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcrestart(cpu_env, arg); - rn = "TCRestart"; + register_name = "TCRestart"; break; case 4: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tchalt(cpu_env, arg); - rn = "TCHalt"; + register_name = "TCHalt"; break; case 5: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tccontext(cpu_env, arg); - rn = "TCContext"; + register_name = "TCContext"; break; case 6: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschedule(cpu_env, arg); - rn = "TCSchedule"; + register_name = "TCSchedule"; break; case 7: CP0_CHECK(ctx->insn_flags & ASE_MT); gen_helper_mtc0_tcschefback(cpu_env, arg); - rn = "TCScheFBack"; + register_name = "TCScheFBack"; break; default: goto cp0_unimplemented; } break; - case 3: + case CP0_REGISTER_03: switch (sel) { case 0: gen_helper_dmtc0_entrylo1(cpu_env, arg); - rn = "EntryLo1"; + register_name = "EntryLo1"; break; case 1: CP0_CHECK(ctx->vp); /* ignored */ - rn = "GlobalNumber"; + register_name = "GlobalNumber"; break; default: goto cp0_unimplemented; } break; - case 4: + case CP0_REGISTER_04: switch (sel) { case 0: gen_helper_mtc0_context(cpu_env, arg); - rn = "Context"; + register_name = "Context"; break; case 1: // gen_helper_mtc0_contextconfig(cpu_env, arg); /* SmartMIPS ASE */ - rn = "ContextConfig"; + register_name = "ContextConfig"; goto cp0_unimplemented; case 2: CP0_CHECK(ctx->ulri); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, active_tc.CP0_UserLocal)); - rn = "UserLocal"; + register_name = "UserLocal"; break; default: goto cp0_unimplemented; } break; - case 5: + case CP0_REGISTER_05: switch (sel) { case 0: gen_helper_mtc0_pagemask(cpu_env, arg); - rn = "PageMask"; + register_name = "PageMask"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_pagegrain(cpu_env, arg); - rn = "PageGrain"; + register_name = "PageGrain"; break; case 2: CP0_CHECK(ctx->sc); gen_helper_mtc0_segctl0(cpu_env, arg); - rn = "SegCtl0"; + register_name = "SegCtl0"; break; case 3: CP0_CHECK(ctx->sc); gen_helper_mtc0_segctl1(cpu_env, arg); - rn = "SegCtl1"; + register_name = "SegCtl1"; break; case 4: CP0_CHECK(ctx->sc); gen_helper_mtc0_segctl2(cpu_env, arg); - rn = "SegCtl2"; + register_name = "SegCtl2"; + break; + case 5: + check_pw(ctx); + tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_PWBase)); + register_name = "PWBase"; + break; + case 6: + check_pw(ctx); + gen_helper_mtc0_pwfield(cpu_env, arg); + register_name = "PWField"; + break; + case 7: + check_pw(ctx); + gen_helper_mtc0_pwsize(cpu_env, arg); + register_name = "PWSize"; break; default: goto cp0_unimplemented; } break; - case 6: + case CP0_REGISTER_06: switch (sel) { case 0: gen_helper_mtc0_wired(cpu_env, arg); - rn = "Wired"; + register_name = "Wired"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf0(cpu_env, arg); - rn = "SRSConf0"; + register_name = "SRSConf0"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf1(cpu_env, arg); - rn = "SRSConf1"; + register_name = "SRSConf1"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf2(cpu_env, arg); - rn = "SRSConf2"; + register_name = "SRSConf2"; break; case 4: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf3(cpu_env, arg); - rn = "SRSConf3"; + register_name = "SRSConf3"; break; case 5: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsconf4(cpu_env, arg); - rn = "SRSConf4"; + register_name = "SRSConf4"; + break; + case 6: + check_pw(ctx); + gen_helper_mtc0_pwctl(cpu_env, arg); + register_name = "PWCtl"; break; default: goto cp0_unimplemented; } break; - case 7: + case CP0_REGISTER_07: switch (sel) { case 0: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_hwrena(cpu_env, arg); ctx->base.is_jmp = DISAS_STOP; - rn = "HWREna"; + register_name = "HWREna"; break; default: goto cp0_unimplemented; } break; - case 8: + case CP0_REGISTER_08: switch (sel) { case 0: /* ignored */ - rn = "BadVAddr"; + register_name = "BadVAddr"; break; case 1: /* ignored */ - rn = "BadInstr"; + register_name = "BadInstr"; break; case 2: /* ignored */ - rn = "BadInstrP"; + register_name = "BadInstrP"; + break; + case 3: + /* ignored */ + register_name = "BadInstrX"; break; default: goto cp0_unimplemented; } break; - case 9: + case CP0_REGISTER_09: switch (sel) { case 0: gen_helper_mtc0_count(cpu_env, arg); - rn = "Count"; + register_name = "Count"; + break; + case 6: + CP0_CHECK(ctx->saar); + gen_helper_mtc0_saari(cpu_env, arg); + register_name = "SAARI"; + break; + case 7: + CP0_CHECK(ctx->saar); + gen_helper_mtc0_saar(cpu_env, arg); + register_name = "SAAR"; break; - /* 6,7 are implementation dependent */ default: goto cp0_unimplemented; } /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; - case 10: + case CP0_REGISTER_10: switch (sel) { case 0: gen_helper_mtc0_entryhi(cpu_env, arg); - rn = "EntryHi"; + register_name = "EntryHi"; break; default: goto cp0_unimplemented; } break; - case 11: + case CP0_REGISTER_11: switch (sel) { case 0: gen_helper_mtc0_compare(cpu_env, arg); - rn = "Compare"; + register_name = "Compare"; break; /* 6,7 are implementation dependent */ default: @@ -7359,7 +9245,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; - case 12: + case CP0_REGISTER_12: switch (sel) { case 0: save_cpu_state(ctx, 1); @@ -7367,34 +9253,34 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Status"; + register_name = "Status"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_intctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "IntCtl"; + register_name = "IntCtl"; break; case 2: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_srsctl(cpu_env, arg); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "SRSCtl"; + register_name = "SRSCtl"; break; case 3: check_insn(ctx, ISA_MIPS32R2); gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_SRSMap)); /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "SRSMap"; + register_name = "SRSMap"; break; default: goto cp0_unimplemented; } break; - case 13: + case CP0_REGISTER_13: switch (sel) { case 0: save_cpu_state(ctx, 1); @@ -7404,314 +9290,338 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) * translated code to check for pending interrupts. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Cause"; + register_name = "Cause"; break; default: goto cp0_unimplemented; } break; - case 14: + case CP0_REGISTER_14: switch (sel) { case 0: tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_EPC)); - rn = "EPC"; + register_name = "EPC"; break; default: goto cp0_unimplemented; } break; - case 15: + case CP0_REGISTER_15: switch (sel) { case 0: /* ignored */ - rn = "PRid"; + register_name = "PRid"; break; case 1: check_insn(ctx, ISA_MIPS32R2); gen_helper_mtc0_ebase(cpu_env, arg); - rn = "EBase"; + register_name = "EBase"; break; default: goto cp0_unimplemented; } break; - case 16: + case CP0_REGISTER_16: switch (sel) { case 0: gen_helper_mtc0_config0(cpu_env, arg); - rn = "Config"; + register_name = "Config"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case 1: /* ignored, read only */ - rn = "Config1"; + register_name = "Config1"; break; case 2: gen_helper_mtc0_config2(cpu_env, arg); - rn = "Config2"; + register_name = "Config2"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case 3: gen_helper_mtc0_config3(cpu_env, arg); - rn = "Config3"; + register_name = "Config3"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; case 4: /* currently ignored */ - rn = "Config4"; + register_name = "Config4"; break; case 5: gen_helper_mtc0_config5(cpu_env, arg); - rn = "Config5"; + register_name = "Config5"; /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; break; /* 6,7 are implementation dependent */ default: - rn = "Invalid config selector"; + register_name = "Invalid config selector"; goto cp0_unimplemented; } break; - case 17: + case CP0_REGISTER_17: switch (sel) { case 0: gen_helper_mtc0_lladdr(cpu_env, arg); - rn = "LLAddr"; + register_name = "LLAddr"; break; case 1: CP0_CHECK(ctx->mrp); gen_helper_mtc0_maar(cpu_env, arg); - rn = "MAAR"; + register_name = "MAAR"; break; case 2: CP0_CHECK(ctx->mrp); gen_helper_mtc0_maari(cpu_env, arg); - rn = "MAARI"; + register_name = "MAARI"; break; default: goto cp0_unimplemented; } break; - case 18: + case CP0_REGISTER_18: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_0e1i(mtc0_watchlo, arg, sel); - rn = "WatchLo"; + register_name = "WatchLo"; break; default: goto cp0_unimplemented; } break; - case 19: + case CP0_REGISTER_19: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + CP0_CHECK(ctx->CP0_Config1 & (1 << CP0C1_WR)); gen_helper_0e1i(mtc0_watchhi, arg, sel); - rn = "WatchHi"; + register_name = "WatchHi"; break; default: goto cp0_unimplemented; } break; - case 20: + case CP0_REGISTER_20: switch (sel) { case 0: check_insn(ctx, ISA_MIPS3); gen_helper_mtc0_xcontext(cpu_env, arg); - rn = "XContext"; + register_name = "XContext"; break; default: goto cp0_unimplemented; } break; - case 21: + case CP0_REGISTER_21: /* Officially reserved, but sel 0 is used for R1x000 framemask */ CP0_CHECK(!(ctx->insn_flags & ISA_MIPS32R6)); switch (sel) { case 0: gen_helper_mtc0_framemask(cpu_env, arg); - rn = "Framemask"; + register_name = "Framemask"; break; default: goto cp0_unimplemented; } break; - case 22: + case CP0_REGISTER_22: /* ignored */ - rn = "Diagnostic"; /* implementation dependent */ + register_name = "Diagnostic"; /* implementation dependent */ break; - case 23: + case CP0_REGISTER_23: switch (sel) { case 0: gen_helper_mtc0_debug(cpu_env, arg); /* EJTAG support */ /* DISAS_STOP isn't good enough here, hflags may have changed. */ gen_save_pc(ctx->base.pc_next + 4); ctx->base.is_jmp = DISAS_EXIT; - rn = "Debug"; + register_name = "Debug"; break; case 1: // gen_helper_mtc0_tracecontrol(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "TraceControl"; + register_name = "TraceControl"; goto cp0_unimplemented; case 2: // gen_helper_mtc0_tracecontrol2(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "TraceControl2"; + register_name = "TraceControl2"; goto cp0_unimplemented; case 3: // gen_helper_mtc0_usertracedata(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "UserTraceData"; + register_name = "UserTraceData"; goto cp0_unimplemented; case 4: // gen_helper_mtc0_tracebpc(cpu_env, arg); /* PDtrace support */ /* Stop translation as we may have switched the execution mode */ ctx->base.is_jmp = DISAS_STOP; - rn = "TraceBPC"; + register_name = "TraceBPC"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 24: + case CP0_REGISTER_24: switch (sel) { case 0: /* EJTAG support */ tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_DEPC)); - rn = "DEPC"; + register_name = "DEPC"; break; default: goto cp0_unimplemented; } break; - case 25: + case CP0_REGISTER_25: switch (sel) { case 0: gen_helper_mtc0_performance0(cpu_env, arg); - rn = "Performance0"; + register_name = "Performance0"; break; case 1: // gen_helper_mtc0_performance1(cpu_env, arg); - rn = "Performance1"; + register_name = "Performance1"; goto cp0_unimplemented; case 2: // gen_helper_mtc0_performance2(cpu_env, arg); - rn = "Performance2"; + register_name = "Performance2"; goto cp0_unimplemented; case 3: // gen_helper_mtc0_performance3(cpu_env, arg); - rn = "Performance3"; + register_name = "Performance3"; goto cp0_unimplemented; case 4: // gen_helper_mtc0_performance4(cpu_env, arg); - rn = "Performance4"; + register_name = "Performance4"; goto cp0_unimplemented; case 5: // gen_helper_mtc0_performance5(cpu_env, arg); - rn = "Performance5"; + register_name = "Performance5"; goto cp0_unimplemented; case 6: // gen_helper_mtc0_performance6(cpu_env, arg); - rn = "Performance6"; + register_name = "Performance6"; goto cp0_unimplemented; case 7: // gen_helper_mtc0_performance7(cpu_env, arg); - rn = "Performance7"; + register_name = "Performance7"; goto cp0_unimplemented; default: goto cp0_unimplemented; } break; - case 26: + case CP0_REGISTER_26: switch (sel) { case 0: gen_helper_mtc0_errctl(cpu_env, arg); ctx->base.is_jmp = DISAS_STOP; - rn = "ErrCtl"; + register_name = "ErrCtl"; break; default: goto cp0_unimplemented; } break; - case 27: + case CP0_REGISTER_27: switch (sel) { - case 0 ... 3: + case 0: + case 1: + case 2: + case 3: /* ignored */ - rn = "CacheErr"; + register_name = "CacheErr"; break; default: goto cp0_unimplemented; } break; - case 28: + case CP0_REGISTER_28: switch (sel) { case 0: case 2: case 4: case 6: gen_helper_mtc0_taglo(cpu_env, arg); - rn = "TagLo"; + register_name = "TagLo"; break; case 1: case 3: case 5: case 7: gen_helper_mtc0_datalo(cpu_env, arg); - rn = "DataLo"; + register_name = "DataLo"; break; default: goto cp0_unimplemented; } break; - case 29: + case CP0_REGISTER_29: switch (sel) { case 0: case 2: case 4: case 6: gen_helper_mtc0_taghi(cpu_env, arg); - rn = "TagHi"; + register_name = "TagHi"; break; case 1: case 3: case 5: case 7: gen_helper_mtc0_datahi(cpu_env, arg); - rn = "DataHi"; + register_name = "DataHi"; break; default: - rn = "invalid sel"; + register_name = "invalid sel"; goto cp0_unimplemented; } break; - case 30: + case CP0_REGISTER_30: switch (sel) { case 0: tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_ErrorEPC)); - rn = "ErrorEPC"; + register_name = "ErrorEPC"; break; default: goto cp0_unimplemented; } break; - case 31: + case CP0_REGISTER_31: switch (sel) { case 0: /* EJTAG support */ gen_mtc0_store32(arg, offsetof(CPUMIPSState, CP0_DESAVE)); - rn = "DESAVE"; + register_name = "DESAVE"; break; - case 2 ... 7: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: CP0_CHECK(ctx->kscrexist & (1 << sel)); tcg_gen_st_tl(arg, cpu_env, offsetof(CPUMIPSState, CP0_KScratch[sel-2])); - rn = "KScratch"; + register_name = "KScratch"; break; default: goto cp0_unimplemented; @@ -7720,7 +9630,7 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) default: goto cp0_unimplemented; } - trace_mips_translate_c0("dmtc0", rn, reg, sel); + trace_mips_translate_c0("dmtc0", register_name, reg, sel); /* For simplicity assume that all writes can cause interrupts. */ if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { @@ -7733,7 +9643,8 @@ static void gen_dmtc0(DisasContext *ctx, TCGv arg, int reg, int sel) return; cp0_unimplemented: - qemu_log_mask(LOG_UNIMP, "dmtc0 %s (reg %d sel %d)\n", rn, reg, sel); + qemu_log_mask(LOG_UNIMP, "dmtc0 %s (reg %d sel %d)\n", + register_name, reg, sel); } #endif /* TARGET_MIPS64 */ @@ -7843,7 +9754,14 @@ static void gen_mftr(CPUMIPSState *env, DisasContext *ctx, int rt, int rd, break; case 16: switch (sel) { - case 0 ... 7: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: gen_helper_mftc0_configx(t0, cpu_env, tcg_const_tl(sel)); break; default: @@ -8224,7 +10142,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "mthc0"; break; case OPC_MFTR: - check_insn(ctx, ASE_MT); + check_cp0_enabled(ctx); if (rd == 0) { /* Treat as NOP. */ return; @@ -8234,7 +10152,7 @@ static void gen_cp0 (CPUMIPSState *env, DisasContext *ctx, uint32_t opc, int rt, opn = "mftr"; break; case OPC_MTTR: - check_insn(ctx, ASE_MT); + check_cp0_enabled(ctx); gen_mttr(env, ctx, rd, rt, (ctx->opcode >> 5) & 1, ctx->opcode & 0x7, (ctx->opcode >> 4) & 1); opn = "mttr"; @@ -12395,10 +14313,22 @@ enum { /* PCREL Instructions perform PC-Relative address calculation. bits 20..16 */ enum { ADDIUPC_00 = 0x00, + ADDIUPC_01 = 0x01, + ADDIUPC_02 = 0x02, + ADDIUPC_03 = 0x03, + ADDIUPC_04 = 0x04, + ADDIUPC_05 = 0x05, + ADDIUPC_06 = 0x06, ADDIUPC_07 = 0x07, AUIPC = 0x1e, ALUIPC = 0x1f, LWPC_08 = 0x08, + LWPC_09 = 0x09, + LWPC_0A = 0x0A, + LWPC_0B = 0x0B, + LWPC_0C = 0x0C, + LWPC_0D = 0x0D, + LWPC_0E = 0x0E, LWPC_0F = 0x0F, }; @@ -12833,12 +14763,16 @@ enum { R6_LWM16 = 0x02, R6_JRC16 = 0x03, MOVEP = 0x04, + MOVEP_05 = 0x05, + MOVEP_06 = 0x06, MOVEP_07 = 0x07, R6_XOR16 = 0x08, R6_OR16 = 0x09, R6_SWM16 = 0x0a, JALRC16 = 0x0b, MOVEP_0C = 0x0c, + MOVEP_0D = 0x0d, + MOVEP_0E = 0x0e, MOVEP_0F = 0x0f, JRCADDIUSP = 0x13, R6_BREAK16 = 0x1b, @@ -13152,12 +15086,18 @@ static void gen_pool16c_r6_insn(DisasContext *ctx) gen_arith_imm(ctx, OPC_ADDIU, 29, 29, imm << 2); } else { /* JRC16 */ - int rs = extract32(ctx->opcode, 5, 5); + rs = extract32(ctx->opcode, 5, 5); gen_compute_branch(ctx, OPC_JR, 2, rs, 0, 0, 0); } break; - case MOVEP ... MOVEP_07: - case MOVEP_0C ... MOVEP_0F: + case MOVEP: + case MOVEP_05: + case MOVEP_06: + case MOVEP_07: + case MOVEP_0C: + case MOVEP_0D: + case MOVEP_0E: + case MOVEP_0F: { int enc_dest = uMIPS_RD(ctx->opcode); int enc_rt = uMIPS_RS2(ctx->opcode); @@ -14118,8 +16058,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) break; case ALIGN: check_insn(ctx, ISA_MIPS32R6); - gen_align(ctx, OPC_ALIGN, rd, rs, rt, - extract32(ctx->opcode, 9, 2)); + gen_align(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 9, 2)); break; case EXT: gen_bitops(ctx, OPC_EXT, rt, rs, rr, rd); @@ -14160,8 +16099,8 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) case SDP: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - /* Fallthrough */ #endif + /* fall through */ case LWP: case SWP: gen_ldst_pair(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); @@ -14171,8 +16110,8 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) case SDM: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - /* Fallthrough */ #endif + /* fall through */ case LWM32: case SWM32: gen_ldst_multiple(ctx, minor, rt, rs, SIMM(ctx->opcode, 0, 12)); @@ -14566,15 +16505,15 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) case 0x38: /* cmovs */ switch ((ctx->opcode >> 6) & 0x7) { - case MOVN_FMT: /* SELNEZ_FMT */ + case MOVN_FMT: /* SELEQZ_FMT */ if (ctx->insn_flags & ISA_MIPS32R6) { - /* SELNEZ_FMT */ + /* SELEQZ_FMT */ switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: - gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); + gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); break; case FMT_SDPS_D: - gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); + gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); break; default: goto pool32f_invalid; @@ -14588,15 +16527,15 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) check_insn_opc_removed(ctx, ISA_MIPS32R6); FINSN_3ARG_SDPS(MOVN); break; - case MOVZ_FMT: /* SELEQZ_FMT */ + case MOVZ_FMT: /* SELNEZ_FMT */ if (ctx->insn_flags & ISA_MIPS32R6) { - /* SELEQZ_FMT */ + /* SELNEZ_FMT */ switch ((ctx->opcode >> 9) & 0x3) { case FMT_SDPS_S: - gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); + gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); break; case FMT_SDPS_D: - gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); + gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); break; default: goto pool32f_invalid; @@ -14886,13 +16825,13 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) gen_st(ctx, mips32_op, rt, rs, offset); break; case SC: - gen_st_cond(ctx, OPC_SC, rt, rs, offset); + gen_st_cond(ctx, rt, rs, offset, MO_TESL, false); break; #if defined(TARGET_MIPS64) case SCD: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); - gen_st_cond(ctx, OPC_SCD, rt, rs, offset); + gen_st_cond(ctx, rt, rs, offset, MO_TEQ, false); break; #endif case LD_EVA: @@ -14973,7 +16912,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) mips32_op = OPC_SHE; goto do_st_lr; case SCE: - gen_st_cond(ctx, OPC_SCE, rt, rs, offset); + gen_st_cond(ctx, rt, rs, offset, MO_TESL, true); break; case SWE: mips32_op = OPC_SWE; @@ -15135,7 +17074,14 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) if (ctx->insn_flags & ISA_MIPS32R6) { /* PCREL: ADDIUPC, AUIPC, ALUIPC, LWPC */ switch ((ctx->opcode >> 16) & 0x1f) { - case ADDIUPC_00 ... ADDIUPC_07: + case ADDIUPC_00: + case ADDIUPC_01: + case ADDIUPC_02: + case ADDIUPC_03: + case ADDIUPC_04: + case ADDIUPC_05: + case ADDIUPC_06: + case ADDIUPC_07: gen_pcrel(ctx, OPC_ADDIUPC, ctx->base.pc_next & ~0x3, rt); break; case AUIPC: @@ -15144,7 +17090,14 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) case ALUIPC: gen_pcrel(ctx, OPC_ALUIPC, ctx->base.pc_next, rt); break; - case LWPC_08 ... LWPC_0F: + case LWPC_08: + case LWPC_09: + case LWPC_0A: + case LWPC_0B: + case LWPC_0C: + case LWPC_0D: + case LWPC_0E: + case LWPC_0F: gen_pcrel(ctx, R6_OPC_LWPC, ctx->base.pc_next & ~0x3, rt); break; default: @@ -15154,7 +17107,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) } else { /* ADDIUPC */ int reg = mmreg(ZIMM(ctx->opcode, 23, 3)); - int offset = SIMM(ctx->opcode, 0, 23) << 2; + offset = SIMM(ctx->opcode, 0, 23) << 2; gen_addiupc(ctx, reg, offset, 0, 0); } @@ -15529,6 +17482,4717 @@ static int decode_micromips_opc (CPUMIPSState *env, DisasContext *ctx) return 2; } +/* + * + * nanoMIPS opcodes + * + */ + +/* MAJOR, P16, and P32 pools opcodes */ +enum { + NM_P_ADDIU = 0x00, + NM_ADDIUPC = 0x01, + NM_MOVE_BALC = 0x02, + NM_P16_MV = 0x04, + NM_LW16 = 0x05, + NM_BC16 = 0x06, + NM_P16_SR = 0x07, + + NM_POOL32A = 0x08, + NM_P_BAL = 0x0a, + NM_P16_SHIFT = 0x0c, + NM_LWSP16 = 0x0d, + NM_BALC16 = 0x0e, + NM_P16_4X4 = 0x0f, + + NM_P_GP_W = 0x10, + NM_P_GP_BH = 0x11, + NM_P_J = 0x12, + NM_P16C = 0x14, + NM_LWGP16 = 0x15, + NM_P16_LB = 0x17, + + NM_P48I = 0x18, + NM_P16_A1 = 0x1c, + NM_LW4X4 = 0x1d, + NM_P16_LH = 0x1f, + + NM_P_U12 = 0x20, + NM_P_LS_U12 = 0x21, + NM_P_BR1 = 0x22, + NM_P16_A2 = 0x24, + NM_SW16 = 0x25, + NM_BEQZC16 = 0x26, + + NM_POOL32F = 0x28, + NM_P_LS_S9 = 0x29, + NM_P_BR2 = 0x2a, + + NM_P16_ADDU = 0x2c, + NM_SWSP16 = 0x2d, + NM_BNEZC16 = 0x2e, + NM_MOVEP = 0x2f, + + NM_POOL32S = 0x30, + NM_P_BRI = 0x32, + NM_LI16 = 0x34, + NM_SWGP16 = 0x35, + NM_P16_BR = 0x36, + + NM_P_LUI = 0x38, + NM_ANDI16 = 0x3c, + NM_SW4X4 = 0x3d, + NM_MOVEPREV = 0x3f, +}; + +/* POOL32A instruction pool */ +enum { + NM_POOL32A0 = 0x00, + NM_SPECIAL2 = 0x01, + NM_COP2_1 = 0x02, + NM_UDI = 0x03, + NM_POOL32A5 = 0x05, + NM_POOL32A7 = 0x07, +}; + +/* P.GP.W instruction pool */ +enum { + NM_ADDIUGP_W = 0x00, + NM_LWGP = 0x02, + NM_SWGP = 0x03, +}; + +/* P48I instruction pool */ +enum { + NM_LI48 = 0x00, + NM_ADDIU48 = 0x01, + NM_ADDIUGP48 = 0x02, + NM_ADDIUPC48 = 0x03, + NM_LWPC48 = 0x0b, + NM_SWPC48 = 0x0f, +}; + +/* P.U12 instruction pool */ +enum { + NM_ORI = 0x00, + NM_XORI = 0x01, + NM_ANDI = 0x02, + NM_P_SR = 0x03, + NM_SLTI = 0x04, + NM_SLTIU = 0x05, + NM_SEQI = 0x06, + NM_ADDIUNEG = 0x08, + NM_P_SHIFT = 0x0c, + NM_P_ROTX = 0x0d, + NM_P_INS = 0x0e, + NM_P_EXT = 0x0f, +}; + +/* POOL32F instruction pool */ +enum { + NM_POOL32F_0 = 0x00, + NM_POOL32F_3 = 0x03, + NM_POOL32F_5 = 0x05, +}; + +/* POOL32S instruction pool */ +enum { + NM_POOL32S_0 = 0x00, + NM_POOL32S_4 = 0x04, +}; + +/* P.LUI instruction pool */ +enum { + NM_LUI = 0x00, + NM_ALUIPC = 0x01, +}; + +/* P.GP.BH instruction pool */ +enum { + NM_LBGP = 0x00, + NM_SBGP = 0x01, + NM_LBUGP = 0x02, + NM_ADDIUGP_B = 0x03, + NM_P_GP_LH = 0x04, + NM_P_GP_SH = 0x05, + NM_P_GP_CP1 = 0x06, +}; + +/* P.LS.U12 instruction pool */ +enum { + NM_LB = 0x00, + NM_SB = 0x01, + NM_LBU = 0x02, + NM_P_PREFU12 = 0x03, + NM_LH = 0x04, + NM_SH = 0x05, + NM_LHU = 0x06, + NM_LWU = 0x07, + NM_LW = 0x08, + NM_SW = 0x09, + NM_LWC1 = 0x0a, + NM_SWC1 = 0x0b, + NM_LDC1 = 0x0e, + NM_SDC1 = 0x0f, +}; + +/* P.LS.S9 instruction pool */ +enum { + NM_P_LS_S0 = 0x00, + NM_P_LS_S1 = 0x01, + NM_P_LS_E0 = 0x02, + NM_P_LS_WM = 0x04, + NM_P_LS_UAWM = 0x05, +}; + +/* P.BAL instruction pool */ +enum { + NM_BC = 0x00, + NM_BALC = 0x01, +}; + +/* P.J instruction pool */ +enum { + NM_JALRC = 0x00, + NM_JALRC_HB = 0x01, + NM_P_BALRSC = 0x08, +}; + +/* P.BR1 instruction pool */ +enum { + NM_BEQC = 0x00, + NM_P_BR3A = 0x01, + NM_BGEC = 0x02, + NM_BGEUC = 0x03, +}; + +/* P.BR2 instruction pool */ +enum { + NM_BNEC = 0x00, + NM_BLTC = 0x02, + NM_BLTUC = 0x03, +}; + +/* P.BRI instruction pool */ +enum { + NM_BEQIC = 0x00, + NM_BBEQZC = 0x01, + NM_BGEIC = 0x02, + NM_BGEIUC = 0x03, + NM_BNEIC = 0x04, + NM_BBNEZC = 0x05, + NM_BLTIC = 0x06, + NM_BLTIUC = 0x07, +}; + +/* P16.SHIFT instruction pool */ +enum { + NM_SLL16 = 0x00, + NM_SRL16 = 0x01, +}; + +/* POOL16C instruction pool */ +enum { + NM_POOL16C_0 = 0x00, + NM_LWXS16 = 0x01, +}; + +/* P16.A1 instruction pool */ +enum { + NM_ADDIUR1SP = 0x01, +}; + +/* P16.A2 instruction pool */ +enum { + NM_ADDIUR2 = 0x00, + NM_P_ADDIURS5 = 0x01, +}; + +/* P16.ADDU instruction pool */ +enum { + NM_ADDU16 = 0x00, + NM_SUBU16 = 0x01, +}; + +/* P16.SR instruction pool */ +enum { + NM_SAVE16 = 0x00, + NM_RESTORE_JRC16 = 0x01, +}; + +/* P16.4X4 instruction pool */ +enum { + NM_ADDU4X4 = 0x00, + NM_MUL4X4 = 0x01, +}; + +/* P16.LB instruction pool */ +enum { + NM_LB16 = 0x00, + NM_SB16 = 0x01, + NM_LBU16 = 0x02, +}; + +/* P16.LH instruction pool */ +enum { + NM_LH16 = 0x00, + NM_SH16 = 0x01, + NM_LHU16 = 0x02, +}; + +/* P.RI instruction pool */ +enum { + NM_SIGRIE = 0x00, + NM_P_SYSCALL = 0x01, + NM_BREAK = 0x02, + NM_SDBBP = 0x03, +}; + +/* POOL32A0 instruction pool */ +enum { + NM_P_TRAP = 0x00, + NM_SEB = 0x01, + NM_SLLV = 0x02, + NM_MUL = 0x03, + NM_MFC0 = 0x06, + NM_MFHC0 = 0x07, + NM_SEH = 0x09, + NM_SRLV = 0x0a, + NM_MUH = 0x0b, + NM_MTC0 = 0x0e, + NM_MTHC0 = 0x0f, + NM_SRAV = 0x12, + NM_MULU = 0x13, + NM_ROTRV = 0x1a, + NM_MUHU = 0x1b, + NM_ADD = 0x22, + NM_DIV = 0x23, + NM_ADDU = 0x2a, + NM_MOD = 0x2b, + NM_SUB = 0x32, + NM_DIVU = 0x33, + NM_RDHWR = 0x38, + NM_SUBU = 0x3a, + NM_MODU = 0x3b, + NM_P_CMOVE = 0x42, + NM_FORK = 0x45, + NM_MFTR = 0x46, + NM_MFHTR = 0x47, + NM_AND = 0x4a, + NM_YIELD = 0x4d, + NM_MTTR = 0x4e, + NM_MTHTR = 0x4f, + NM_OR = 0x52, + NM_D_E_MT_VPE = 0x56, + NM_NOR = 0x5a, + NM_XOR = 0x62, + NM_SLT = 0x6a, + NM_P_SLTU = 0x72, + NM_SOV = 0x7a, +}; + +/* CRC32 instruction pool */ +enum { + NM_CRC32B = 0x00, + NM_CRC32H = 0x01, + NM_CRC32W = 0x02, + NM_CRC32CB = 0x04, + NM_CRC32CH = 0x05, + NM_CRC32CW = 0x06, +}; + +/* POOL32A5 instruction pool */ +enum { + NM_CMP_EQ_PH = 0x00, + NM_CMP_LT_PH = 0x08, + NM_CMP_LE_PH = 0x10, + NM_CMPGU_EQ_QB = 0x18, + NM_CMPGU_LT_QB = 0x20, + NM_CMPGU_LE_QB = 0x28, + NM_CMPGDU_EQ_QB = 0x30, + NM_CMPGDU_LT_QB = 0x38, + NM_CMPGDU_LE_QB = 0x40, + NM_CMPU_EQ_QB = 0x48, + NM_CMPU_LT_QB = 0x50, + NM_CMPU_LE_QB = 0x58, + NM_ADDQ_S_W = 0x60, + NM_SUBQ_S_W = 0x68, + NM_ADDSC = 0x70, + NM_ADDWC = 0x78, + + NM_ADDQ_S_PH = 0x01, + NM_ADDQH_R_PH = 0x09, + NM_ADDQH_R_W = 0x11, + NM_ADDU_S_QB = 0x19, + NM_ADDU_S_PH = 0x21, + NM_ADDUH_R_QB = 0x29, + NM_SHRAV_R_PH = 0x31, + NM_SHRAV_R_QB = 0x39, + NM_SUBQ_S_PH = 0x41, + NM_SUBQH_R_PH = 0x49, + NM_SUBQH_R_W = 0x51, + NM_SUBU_S_QB = 0x59, + NM_SUBU_S_PH = 0x61, + NM_SUBUH_R_QB = 0x69, + NM_SHLLV_S_PH = 0x71, + NM_PRECR_SRA_R_PH_W = 0x79, + + NM_MULEU_S_PH_QBL = 0x12, + NM_MULEU_S_PH_QBR = 0x1a, + NM_MULQ_RS_PH = 0x22, + NM_MULQ_S_PH = 0x2a, + NM_MULQ_RS_W = 0x32, + NM_MULQ_S_W = 0x3a, + NM_APPEND = 0x42, + NM_MODSUB = 0x52, + NM_SHRAV_R_W = 0x5a, + NM_SHRLV_PH = 0x62, + NM_SHRLV_QB = 0x6a, + NM_SHLLV_QB = 0x72, + NM_SHLLV_S_W = 0x7a, + + NM_SHILO = 0x03, + + NM_MULEQ_S_W_PHL = 0x04, + NM_MULEQ_S_W_PHR = 0x0c, + + NM_MUL_S_PH = 0x05, + NM_PRECR_QB_PH = 0x0d, + NM_PRECRQ_QB_PH = 0x15, + NM_PRECRQ_PH_W = 0x1d, + NM_PRECRQ_RS_PH_W = 0x25, + NM_PRECRQU_S_QB_PH = 0x2d, + NM_PACKRL_PH = 0x35, + NM_PICK_QB = 0x3d, + NM_PICK_PH = 0x45, + + NM_SHRA_R_W = 0x5e, + NM_SHRA_R_PH = 0x66, + NM_SHLL_S_PH = 0x76, + NM_SHLL_S_W = 0x7e, + + NM_REPL_PH = 0x07 +}; + +/* POOL32A7 instruction pool */ +enum { + NM_P_LSX = 0x00, + NM_LSA = 0x01, + NM_EXTW = 0x03, + NM_POOL32AXF = 0x07, +}; + +/* P.SR instruction pool */ +enum { + NM_PP_SR = 0x00, + NM_P_SR_F = 0x01, +}; + +/* P.SHIFT instruction pool */ +enum { + NM_P_SLL = 0x00, + NM_SRL = 0x02, + NM_SRA = 0x04, + NM_ROTR = 0x06, +}; + +/* P.ROTX instruction pool */ +enum { + NM_ROTX = 0x00, +}; + +/* P.INS instruction pool */ +enum { + NM_INS = 0x00, +}; + +/* P.EXT instruction pool */ +enum { + NM_EXT = 0x00, +}; + +/* POOL32F_0 (fmt) instruction pool */ +enum { + NM_RINT_S = 0x04, + NM_RINT_D = 0x44, + NM_ADD_S = 0x06, + NM_SELEQZ_S = 0x07, + NM_SELEQZ_D = 0x47, + NM_CLASS_S = 0x0c, + NM_CLASS_D = 0x4c, + NM_SUB_S = 0x0e, + NM_SELNEZ_S = 0x0f, + NM_SELNEZ_D = 0x4f, + NM_MUL_S = 0x16, + NM_SEL_S = 0x17, + NM_SEL_D = 0x57, + NM_DIV_S = 0x1e, + NM_ADD_D = 0x26, + NM_SUB_D = 0x2e, + NM_MUL_D = 0x36, + NM_MADDF_S = 0x37, + NM_MADDF_D = 0x77, + NM_DIV_D = 0x3e, + NM_MSUBF_S = 0x3f, + NM_MSUBF_D = 0x7f, +}; + +/* POOL32F_3 instruction pool */ +enum { + NM_MIN_FMT = 0x00, + NM_MAX_FMT = 0x01, + NM_MINA_FMT = 0x04, + NM_MAXA_FMT = 0x05, + NM_POOL32FXF = 0x07, +}; + +/* POOL32F_5 instruction pool */ +enum { + NM_CMP_CONDN_S = 0x00, + NM_CMP_CONDN_D = 0x02, +}; + +/* P.GP.LH instruction pool */ +enum { + NM_LHGP = 0x00, + NM_LHUGP = 0x01, +}; + +/* P.GP.SH instruction pool */ +enum { + NM_SHGP = 0x00, +}; + +/* P.GP.CP1 instruction pool */ +enum { + NM_LWC1GP = 0x00, + NM_SWC1GP = 0x01, + NM_LDC1GP = 0x02, + NM_SDC1GP = 0x03, +}; + +/* P.LS.S0 instruction pool */ +enum { + NM_LBS9 = 0x00, + NM_LHS9 = 0x04, + NM_LWS9 = 0x08, + NM_LDS9 = 0x0c, + + NM_SBS9 = 0x01, + NM_SHS9 = 0x05, + NM_SWS9 = 0x09, + NM_SDS9 = 0x0d, + + NM_LBUS9 = 0x02, + NM_LHUS9 = 0x06, + NM_LWC1S9 = 0x0a, + NM_LDC1S9 = 0x0e, + + NM_P_PREFS9 = 0x03, + NM_LWUS9 = 0x07, + NM_SWC1S9 = 0x0b, + NM_SDC1S9 = 0x0f, +}; + +/* P.LS.S1 instruction pool */ +enum { + NM_ASET_ACLR = 0x02, + NM_UALH = 0x04, + NM_UASH = 0x05, + NM_CACHE = 0x07, + NM_P_LL = 0x0a, + NM_P_SC = 0x0b, +}; + +/* P.LS.E0 instruction pool */ +enum { + NM_LBE = 0x00, + NM_SBE = 0x01, + NM_LBUE = 0x02, + NM_P_PREFE = 0x03, + NM_LHE = 0x04, + NM_SHE = 0x05, + NM_LHUE = 0x06, + NM_CACHEE = 0x07, + NM_LWE = 0x08, + NM_SWE = 0x09, + NM_P_LLE = 0x0a, + NM_P_SCE = 0x0b, +}; + +/* P.PREFE instruction pool */ +enum { + NM_SYNCIE = 0x00, + NM_PREFE = 0x01, +}; + +/* P.LLE instruction pool */ +enum { + NM_LLE = 0x00, + NM_LLWPE = 0x01, +}; + +/* P.SCE instruction pool */ +enum { + NM_SCE = 0x00, + NM_SCWPE = 0x01, +}; + +/* P.LS.WM instruction pool */ +enum { + NM_LWM = 0x00, + NM_SWM = 0x01, +}; + +/* P.LS.UAWM instruction pool */ +enum { + NM_UALWM = 0x00, + NM_UASWM = 0x01, +}; + +/* P.BR3A instruction pool */ +enum { + NM_BC1EQZC = 0x00, + NM_BC1NEZC = 0x01, + NM_BC2EQZC = 0x02, + NM_BC2NEZC = 0x03, + NM_BPOSGE32C = 0x04, +}; + +/* P16.RI instruction pool */ +enum { + NM_P16_SYSCALL = 0x01, + NM_BREAK16 = 0x02, + NM_SDBBP16 = 0x03, +}; + +/* POOL16C_0 instruction pool */ +enum { + NM_POOL16C_00 = 0x00, +}; + +/* P16.JRC instruction pool */ +enum { + NM_JRC = 0x00, + NM_JALRC16 = 0x01, +}; + +/* P.SYSCALL instruction pool */ +enum { + NM_SYSCALL = 0x00, + NM_HYPCALL = 0x01, +}; + +/* P.TRAP instruction pool */ +enum { + NM_TEQ = 0x00, + NM_TNE = 0x01, +}; + +/* P.CMOVE instruction pool */ +enum { + NM_MOVZ = 0x00, + NM_MOVN = 0x01, +}; + +/* POOL32Axf instruction pool */ +enum { + NM_POOL32AXF_1 = 0x01, + NM_POOL32AXF_2 = 0x02, + NM_POOL32AXF_4 = 0x04, + NM_POOL32AXF_5 = 0x05, + NM_POOL32AXF_7 = 0x07, +}; + +/* POOL32Axf_1 instruction pool */ +enum { + NM_POOL32AXF_1_0 = 0x00, + NM_POOL32AXF_1_1 = 0x01, + NM_POOL32AXF_1_3 = 0x03, + NM_POOL32AXF_1_4 = 0x04, + NM_POOL32AXF_1_5 = 0x05, + NM_POOL32AXF_1_7 = 0x07, +}; + +/* POOL32Axf_2 instruction pool */ +enum { + NM_POOL32AXF_2_0_7 = 0x00, + NM_POOL32AXF_2_8_15 = 0x01, + NM_POOL32AXF_2_16_23 = 0x02, + NM_POOL32AXF_2_24_31 = 0x03, +}; + +/* POOL32Axf_7 instruction pool */ +enum { + NM_SHRA_R_QB = 0x0, + NM_SHRL_PH = 0x1, + NM_REPL_QB = 0x2, +}; + +/* POOL32Axf_1_0 instruction pool */ +enum { + NM_MFHI = 0x0, + NM_MFLO = 0x1, + NM_MTHI = 0x2, + NM_MTLO = 0x3, +}; + +/* POOL32Axf_1_1 instruction pool */ +enum { + NM_MTHLIP = 0x0, + NM_SHILOV = 0x1, +}; + +/* POOL32Axf_1_3 instruction pool */ +enum { + NM_RDDSP = 0x0, + NM_WRDSP = 0x1, + NM_EXTP = 0x2, + NM_EXTPDP = 0x3, +}; + +/* POOL32Axf_1_4 instruction pool */ +enum { + NM_SHLL_QB = 0x0, + NM_SHRL_QB = 0x1, +}; + +/* POOL32Axf_1_5 instruction pool */ +enum { + NM_MAQ_S_W_PHR = 0x0, + NM_MAQ_S_W_PHL = 0x1, + NM_MAQ_SA_W_PHR = 0x2, + NM_MAQ_SA_W_PHL = 0x3, +}; + +/* POOL32Axf_1_7 instruction pool */ +enum { + NM_EXTR_W = 0x0, + NM_EXTR_R_W = 0x1, + NM_EXTR_RS_W = 0x2, + NM_EXTR_S_H = 0x3, +}; + +/* POOL32Axf_2_0_7 instruction pool */ +enum { + NM_DPA_W_PH = 0x0, + NM_DPAQ_S_W_PH = 0x1, + NM_DPS_W_PH = 0x2, + NM_DPSQ_S_W_PH = 0x3, + NM_BALIGN = 0x4, + NM_MADD = 0x5, + NM_MULT = 0x6, + NM_EXTRV_W = 0x7, +}; + +/* POOL32Axf_2_8_15 instruction pool */ +enum { + NM_DPAX_W_PH = 0x0, + NM_DPAQ_SA_L_W = 0x1, + NM_DPSX_W_PH = 0x2, + NM_DPSQ_SA_L_W = 0x3, + NM_MADDU = 0x5, + NM_MULTU = 0x6, + NM_EXTRV_R_W = 0x7, +}; + +/* POOL32Axf_2_16_23 instruction pool */ +enum { + NM_DPAU_H_QBL = 0x0, + NM_DPAQX_S_W_PH = 0x1, + NM_DPSU_H_QBL = 0x2, + NM_DPSQX_S_W_PH = 0x3, + NM_EXTPV = 0x4, + NM_MSUB = 0x5, + NM_MULSA_W_PH = 0x6, + NM_EXTRV_RS_W = 0x7, +}; + +/* POOL32Axf_2_24_31 instruction pool */ +enum { + NM_DPAU_H_QBR = 0x0, + NM_DPAQX_SA_W_PH = 0x1, + NM_DPSU_H_QBR = 0x2, + NM_DPSQX_SA_W_PH = 0x3, + NM_EXTPDPV = 0x4, + NM_MSUBU = 0x5, + NM_MULSAQ_S_W_PH = 0x6, + NM_EXTRV_S_H = 0x7, +}; + +/* POOL32Axf_{4, 5} instruction pool */ +enum { + NM_CLO = 0x25, + NM_CLZ = 0x2d, + + NM_TLBP = 0x01, + NM_TLBR = 0x09, + NM_TLBWI = 0x11, + NM_TLBWR = 0x19, + NM_TLBINV = 0x03, + NM_TLBINVF = 0x0b, + NM_DI = 0x23, + NM_EI = 0x2b, + NM_RDPGPR = 0x70, + NM_WRPGPR = 0x78, + NM_WAIT = 0x61, + NM_DERET = 0x71, + NM_ERETX = 0x79, + + /* nanoMIPS DSP instructions */ + NM_ABSQ_S_QB = 0x00, + NM_ABSQ_S_PH = 0x08, + NM_ABSQ_S_W = 0x10, + NM_PRECEQ_W_PHL = 0x28, + NM_PRECEQ_W_PHR = 0x30, + NM_PRECEQU_PH_QBL = 0x38, + NM_PRECEQU_PH_QBR = 0x48, + NM_PRECEU_PH_QBL = 0x58, + NM_PRECEU_PH_QBR = 0x68, + NM_PRECEQU_PH_QBLA = 0x39, + NM_PRECEQU_PH_QBRA = 0x49, + NM_PRECEU_PH_QBLA = 0x59, + NM_PRECEU_PH_QBRA = 0x69, + NM_REPLV_PH = 0x01, + NM_REPLV_QB = 0x09, + NM_BITREV = 0x18, + NM_INSV = 0x20, + NM_RADDU_W_QB = 0x78, + + NM_BITSWAP = 0x05, + NM_WSBH = 0x3d, +}; + +/* PP.SR instruction pool */ +enum { + NM_SAVE = 0x00, + NM_RESTORE = 0x02, + NM_RESTORE_JRC = 0x03, +}; + +/* P.SR.F instruction pool */ +enum { + NM_SAVEF = 0x00, + NM_RESTOREF = 0x01, +}; + +/* P16.SYSCALL instruction pool */ +enum { + NM_SYSCALL16 = 0x00, + NM_HYPCALL16 = 0x01, +}; + +/* POOL16C_00 instruction pool */ +enum { + NM_NOT16 = 0x00, + NM_XOR16 = 0x01, + NM_AND16 = 0x02, + NM_OR16 = 0x03, +}; + +/* PP.LSX and PP.LSXS instruction pool */ +enum { + NM_LBX = 0x00, + NM_LHX = 0x04, + NM_LWX = 0x08, + NM_LDX = 0x0c, + + NM_SBX = 0x01, + NM_SHX = 0x05, + NM_SWX = 0x09, + NM_SDX = 0x0d, + + NM_LBUX = 0x02, + NM_LHUX = 0x06, + NM_LWC1X = 0x0a, + NM_LDC1X = 0x0e, + + NM_LWUX = 0x07, + NM_SWC1X = 0x0b, + NM_SDC1X = 0x0f, + + NM_LHXS = 0x04, + NM_LWXS = 0x08, + NM_LDXS = 0x0c, + + NM_SHXS = 0x05, + NM_SWXS = 0x09, + NM_SDXS = 0x0d, + + NM_LHUXS = 0x06, + NM_LWC1XS = 0x0a, + NM_LDC1XS = 0x0e, + + NM_LWUXS = 0x07, + NM_SWC1XS = 0x0b, + NM_SDC1XS = 0x0f, +}; + +/* ERETx instruction pool */ +enum { + NM_ERET = 0x00, + NM_ERETNC = 0x01, +}; + +/* POOL32FxF_{0, 1} insturction pool */ +enum { + NM_CFC1 = 0x40, + NM_CTC1 = 0x60, + NM_MFC1 = 0x80, + NM_MTC1 = 0xa0, + NM_MFHC1 = 0xc0, + NM_MTHC1 = 0xe0, + + NM_CVT_S_PL = 0x84, + NM_CVT_S_PU = 0xa4, + + NM_CVT_L_S = 0x004, + NM_CVT_L_D = 0x104, + NM_CVT_W_S = 0x024, + NM_CVT_W_D = 0x124, + + NM_RSQRT_S = 0x008, + NM_RSQRT_D = 0x108, + + NM_SQRT_S = 0x028, + NM_SQRT_D = 0x128, + + NM_RECIP_S = 0x048, + NM_RECIP_D = 0x148, + + NM_FLOOR_L_S = 0x00c, + NM_FLOOR_L_D = 0x10c, + + NM_FLOOR_W_S = 0x02c, + NM_FLOOR_W_D = 0x12c, + + NM_CEIL_L_S = 0x04c, + NM_CEIL_L_D = 0x14c, + NM_CEIL_W_S = 0x06c, + NM_CEIL_W_D = 0x16c, + NM_TRUNC_L_S = 0x08c, + NM_TRUNC_L_D = 0x18c, + NM_TRUNC_W_S = 0x0ac, + NM_TRUNC_W_D = 0x1ac, + NM_ROUND_L_S = 0x0cc, + NM_ROUND_L_D = 0x1cc, + NM_ROUND_W_S = 0x0ec, + NM_ROUND_W_D = 0x1ec, + + NM_MOV_S = 0x01, + NM_MOV_D = 0x81, + NM_ABS_S = 0x0d, + NM_ABS_D = 0x8d, + NM_NEG_S = 0x2d, + NM_NEG_D = 0xad, + NM_CVT_D_S = 0x04d, + NM_CVT_D_W = 0x0cd, + NM_CVT_D_L = 0x14d, + NM_CVT_S_D = 0x06d, + NM_CVT_S_W = 0x0ed, + NM_CVT_S_L = 0x16d, +}; + +/* P.LL instruction pool */ +enum { + NM_LL = 0x00, + NM_LLWP = 0x01, +}; + +/* P.SC instruction pool */ +enum { + NM_SC = 0x00, + NM_SCWP = 0x01, +}; + +/* P.DVP instruction pool */ +enum { + NM_DVP = 0x00, + NM_EVP = 0x01, +}; + + +/* + * + * nanoMIPS decoding engine + * + */ + + +/* extraction utilities */ + +#define NANOMIPS_EXTRACT_RT3(op) ((op >> 7) & 0x7) +#define NANOMIPS_EXTRACT_RS3(op) ((op >> 4) & 0x7) +#define NANOMIPS_EXTRACT_RD3(op) ((op >> 1) & 0x7) +#define NANOMIPS_EXTRACT_RD5(op) ((op >> 5) & 0x1f) +#define NANOMIPS_EXTRACT_RS5(op) (op & 0x1f) + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr3'). */ +static inline int decode_gpr_gpr3(int r) +{ + static const int map[] = { 16, 17, 18, 19, 4, 5, 6, 7 }; + + return map[r & 0x7]; +} + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr3.src.store'). */ +static inline int decode_gpr_gpr3_src_store(int r) +{ + static const int map[] = { 0, 17, 18, 19, 4, 5, 6, 7 }; + + return map[r & 0x7]; +} + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr4'). */ +static inline int decode_gpr_gpr4(int r) +{ + static const int map[] = { 8, 9, 10, 11, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }; + + return map[r & 0xf]; +} + +/* Implement nanoMIPS pseudocode decode_gpr(encoded_gpr, 'gpr4.zero'). */ +static inline int decode_gpr_gpr4_zero(int r) +{ + static const int map[] = { 8, 9, 10, 0, 4, 5, 6, 7, + 16, 17, 18, 19, 20, 21, 22, 23 }; + + return map[r & 0xf]; +} + + +static void gen_adjust_sp(DisasContext *ctx, int u) +{ + gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], u); +} + +static void gen_save(DisasContext *ctx, uint8_t rt, uint8_t count, + uint8_t gp, uint16_t u) +{ + int counter = 0; + TCGv va = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + while (counter != count) { + bool use_gp = gp && (counter == count - 1); + int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); + int this_offset = -((counter + 1) << 2); + gen_base_offset_addr(ctx, va, 29, this_offset); + gen_load_gpr(t0, this_rt); + tcg_gen_qemu_st_tl(t0, va, ctx->mem_idx, + (MO_TEUL | ctx->default_tcg_memop_mask)); + counter++; + } + + /* adjust stack pointer */ + gen_adjust_sp(ctx, -u); + + tcg_temp_free(t0); + tcg_temp_free(va); +} + +static void gen_restore(DisasContext *ctx, uint8_t rt, uint8_t count, + uint8_t gp, uint16_t u) +{ + int counter = 0; + TCGv va = tcg_temp_new(); + TCGv t0 = tcg_temp_new(); + + while (counter != count) { + bool use_gp = gp && (counter == count - 1); + int this_rt = use_gp ? 28 : (rt & 0x10) | ((rt + counter) & 0x1f); + int this_offset = u - ((counter + 1) << 2); + gen_base_offset_addr(ctx, va, 29, this_offset); + tcg_gen_qemu_ld_tl(t0, va, ctx->mem_idx, MO_TESL | + ctx->default_tcg_memop_mask); + tcg_gen_ext32s_tl(t0, t0); + gen_store_gpr(t0, this_rt); + counter++; + } + + /* adjust stack pointer */ + gen_adjust_sp(ctx, u); + + tcg_temp_free(t0); + tcg_temp_free(va); +} + +static void gen_pool16c_nanomips_insn(DisasContext *ctx) +{ + int rt = decode_gpr_gpr3(NANOMIPS_EXTRACT_RT3(ctx->opcode)); + int rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS3(ctx->opcode)); + + switch (extract32(ctx->opcode, 2, 2)) { + case NM_NOT16: + gen_logic(ctx, OPC_NOR, rt, rs, 0); + break; + case NM_AND16: + gen_logic(ctx, OPC_AND, rt, rt, rs); + break; + case NM_XOR16: + gen_logic(ctx, OPC_XOR, rt, rt, rs); + break; + case NM_OR16: + gen_logic(ctx, OPC_OR, rt, rt, rs); + break; + } +} + +static void gen_pool32a0_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) +{ + int rt = extract32(ctx->opcode, 21, 5); + int rs = extract32(ctx->opcode, 16, 5); + int rd = extract32(ctx->opcode, 11, 5); + + switch (extract32(ctx->opcode, 3, 7)) { + case NM_P_TRAP: + switch (extract32(ctx->opcode, 10, 1)) { + case NM_TEQ: + check_nms(ctx); + gen_trap(ctx, OPC_TEQ, rs, rt, -1); + break; + case NM_TNE: + check_nms(ctx); + gen_trap(ctx, OPC_TNE, rs, rt, -1); + break; + } + break; + case NM_RDHWR: + check_nms(ctx); + gen_rdhwr(ctx, rt, rs, extract32(ctx->opcode, 11, 3)); + break; + case NM_SEB: + check_nms(ctx); + gen_bshfl(ctx, OPC_SEB, rs, rt); + break; + case NM_SEH: + gen_bshfl(ctx, OPC_SEH, rs, rt); + break; + case NM_SLLV: + gen_shift(ctx, OPC_SLLV, rd, rt, rs); + break; + case NM_SRLV: + gen_shift(ctx, OPC_SRLV, rd, rt, rs); + break; + case NM_SRAV: + gen_shift(ctx, OPC_SRAV, rd, rt, rs); + break; + case NM_ROTRV: + gen_shift(ctx, OPC_ROTRV, rd, rt, rs); + break; + case NM_ADD: + gen_arith(ctx, OPC_ADD, rd, rs, rt); + break; + case NM_ADDU: + gen_arith(ctx, OPC_ADDU, rd, rs, rt); + break; + case NM_SUB: + check_nms(ctx); + gen_arith(ctx, OPC_SUB, rd, rs, rt); + break; + case NM_SUBU: + gen_arith(ctx, OPC_SUBU, rd, rs, rt); + break; + case NM_P_CMOVE: + switch (extract32(ctx->opcode, 10, 1)) { + case NM_MOVZ: + gen_cond_move(ctx, OPC_MOVZ, rd, rs, rt); + break; + case NM_MOVN: + gen_cond_move(ctx, OPC_MOVN, rd, rs, rt); + break; + } + break; + case NM_AND: + gen_logic(ctx, OPC_AND, rd, rs, rt); + break; + case NM_OR: + gen_logic(ctx, OPC_OR, rd, rs, rt); + break; + case NM_NOR: + gen_logic(ctx, OPC_NOR, rd, rs, rt); + break; + case NM_XOR: + gen_logic(ctx, OPC_XOR, rd, rs, rt); + break; + case NM_SLT: + gen_slt(ctx, OPC_SLT, rd, rs, rt); + break; + case NM_P_SLTU: + if (rd == 0) { + /* P_DVP */ +#ifndef CONFIG_USER_ONLY + TCGv t0 = tcg_temp_new(); + switch (extract32(ctx->opcode, 10, 1)) { + case NM_DVP: + if (ctx->vp) { + check_cp0_enabled(ctx); + gen_helper_dvp(t0, cpu_env); + gen_store_gpr(t0, rt); + } + break; + case NM_EVP: + if (ctx->vp) { + check_cp0_enabled(ctx); + gen_helper_evp(t0, cpu_env); + gen_store_gpr(t0, rt); + } + break; + } + tcg_temp_free(t0); +#endif + } else { + gen_slt(ctx, OPC_SLTU, rd, rs, rt); + } + break; + case NM_SOV: + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + + gen_load_gpr(t1, rs); + gen_load_gpr(t2, rt); + tcg_gen_add_tl(t0, t1, t2); + tcg_gen_ext32s_tl(t0, t0); + tcg_gen_xor_tl(t1, t1, t2); + tcg_gen_xor_tl(t2, t0, t2); + tcg_gen_andc_tl(t1, t2, t1); + + /* operands of same sign, result different sign */ + tcg_gen_setcondi_tl(TCG_COND_LT, t0, t1, 0); + gen_store_gpr(t0, rd); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + } + break; + case NM_MUL: + gen_r6_muldiv(ctx, R6_OPC_MUL, rd, rs, rt); + break; + case NM_MUH: + gen_r6_muldiv(ctx, R6_OPC_MUH, rd, rs, rt); + break; + case NM_MULU: + gen_r6_muldiv(ctx, R6_OPC_MULU, rd, rs, rt); + break; + case NM_MUHU: + gen_r6_muldiv(ctx, R6_OPC_MUHU, rd, rs, rt); + break; + case NM_DIV: + gen_r6_muldiv(ctx, R6_OPC_DIV, rd, rs, rt); + break; + case NM_MOD: + gen_r6_muldiv(ctx, R6_OPC_MOD, rd, rs, rt); + break; + case NM_DIVU: + gen_r6_muldiv(ctx, R6_OPC_DIVU, rd, rs, rt); + break; + case NM_MODU: + gen_r6_muldiv(ctx, R6_OPC_MODU, rd, rs, rt); + break; +#ifndef CONFIG_USER_ONLY + case NM_MFC0: + check_cp0_enabled(ctx); + if (rt == 0) { + /* Treat as NOP. */ + break; + } + gen_mfc0(ctx, cpu_gpr[rt], rs, extract32(ctx->opcode, 11, 3)); + break; + case NM_MTC0: + check_cp0_enabled(ctx); + { + TCGv t0 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_mtc0(ctx, t0, rs, extract32(ctx->opcode, 11, 3)); + tcg_temp_free(t0); + } + break; + case NM_D_E_MT_VPE: + { + uint8_t sc = extract32(ctx->opcode, 10, 1); + TCGv t0 = tcg_temp_new(); + + switch (sc) { + case 0: + if (rs == 1) { + /* DMT */ + check_cp0_mt(ctx); + gen_helper_dmt(t0); + gen_store_gpr(t0, rt); + } else if (rs == 0) { + /* DVPE */ + check_cp0_mt(ctx); + gen_helper_dvpe(t0, cpu_env); + gen_store_gpr(t0, rt); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + case 1: + if (rs == 1) { + /* EMT */ + check_cp0_mt(ctx); + gen_helper_emt(t0); + gen_store_gpr(t0, rt); + } else if (rs == 0) { + /* EVPE */ + check_cp0_mt(ctx); + gen_helper_evpe(t0, cpu_env); + gen_store_gpr(t0, rt); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + } + + tcg_temp_free(t0); + } + break; + case NM_FORK: + check_mt(ctx); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + gen_helper_fork(t0, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + case NM_MFTR: + case NM_MFHTR: + check_cp0_enabled(ctx); + if (rd == 0) { + /* Treat as NOP. */ + return; + } + gen_mftr(env, ctx, rs, rt, extract32(ctx->opcode, 10, 1), + extract32(ctx->opcode, 11, 5), extract32(ctx->opcode, 3, 1)); + break; + case NM_MTTR: + case NM_MTHTR: + check_cp0_enabled(ctx); + gen_mttr(env, ctx, rs, rt, extract32(ctx->opcode, 10, 1), + extract32(ctx->opcode, 11, 5), extract32(ctx->opcode, 3, 1)); + break; + case NM_YIELD: + check_mt(ctx); + { + TCGv t0 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_helper_yield(t0, cpu_env, t0); + gen_store_gpr(t0, rt); + tcg_temp_free(t0); + } + break; +#endif + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* dsp */ +static void gen_pool32axf_1_5_nanomips_insn(DisasContext *ctx, uint32_t opc, + int ret, int v1, int v2) +{ + TCGv_i32 t0; + TCGv v0_t; + TCGv v1_t; + + t0 = tcg_temp_new_i32(); + + v0_t = tcg_temp_new(); + v1_t = tcg_temp_new(); + + tcg_gen_movi_i32(t0, v2 >> 3); + + gen_load_gpr(v0_t, ret); + gen_load_gpr(v1_t, v1); + + switch (opc) { + case NM_MAQ_S_W_PHR: + check_dsp(ctx); + gen_helper_maq_s_w_phr(t0, v1_t, v0_t, cpu_env); + break; + case NM_MAQ_S_W_PHL: + check_dsp(ctx); + gen_helper_maq_s_w_phl(t0, v1_t, v0_t, cpu_env); + break; + case NM_MAQ_SA_W_PHR: + check_dsp(ctx); + gen_helper_maq_sa_w_phr(t0, v1_t, v0_t, cpu_env); + break; + case NM_MAQ_SA_W_PHL: + check_dsp(ctx); + gen_helper_maq_sa_w_phl(t0, v1_t, v0_t, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(t0); + + tcg_temp_free(v0_t); + tcg_temp_free(v1_t); +} + + +static void gen_pool32axf_1_nanomips_insn(DisasContext *ctx, uint32_t opc, + int ret, int v1, int v2) +{ + int16_t imm; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv v0_t = tcg_temp_new(); + + gen_load_gpr(v0_t, v1); + + switch (opc) { + case NM_POOL32AXF_1_0: + check_dsp(ctx); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_MFHI: + gen_HILO(ctx, OPC_MFHI, v2 >> 3, ret); + break; + case NM_MFLO: + gen_HILO(ctx, OPC_MFLO, v2 >> 3, ret); + break; + case NM_MTHI: + gen_HILO(ctx, OPC_MTHI, v2 >> 3, v1); + break; + case NM_MTLO: + gen_HILO(ctx, OPC_MTLO, v2 >> 3, v1); + break; + } + break; + case NM_POOL32AXF_1_1: + check_dsp(ctx); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_MTHLIP: + tcg_gen_movi_tl(t0, v2); + gen_helper_mthlip(t0, v0_t, cpu_env); + break; + case NM_SHILOV: + tcg_gen_movi_tl(t0, v2 >> 3); + gen_helper_shilo(t0, v0_t, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_1_3: + check_dsp(ctx); + imm = extract32(ctx->opcode, 14, 7); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_RDDSP: + tcg_gen_movi_tl(t0, imm); + gen_helper_rddsp(t0, t0, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_WRDSP: + gen_load_gpr(t0, ret); + tcg_gen_movi_tl(t1, imm); + gen_helper_wrdsp(t0, t1, cpu_env); + break; + case NM_EXTP: + tcg_gen_movi_tl(t0, v2 >> 3); + tcg_gen_movi_tl(t1, v1); + gen_helper_extp(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTPDP: + tcg_gen_movi_tl(t0, v2 >> 3); + tcg_gen_movi_tl(t1, v1); + gen_helper_extpdp(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_1_4: + check_dsp(ctx); + tcg_gen_movi_tl(t0, v2 >> 2); + switch (extract32(ctx->opcode, 12, 1)) { + case NM_SHLL_QB: + gen_helper_shll_qb(t0, t0, v0_t, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_SHRL_QB: + gen_helper_shrl_qb(t0, t0, v0_t); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_1_5: + opc = extract32(ctx->opcode, 12, 2); + gen_pool32axf_1_5_nanomips_insn(ctx, opc, ret, v1, v2); + break; + case NM_POOL32AXF_1_7: + check_dsp(ctx); + tcg_gen_movi_tl(t0, v2 >> 3); + tcg_gen_movi_tl(t1, v1); + switch (extract32(ctx->opcode, 12, 2)) { + case NM_EXTR_W: + gen_helper_extr_w(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTR_R_W: + gen_helper_extr_r_w(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTR_RS_W: + gen_helper_extr_rs_w(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_EXTR_S_H: + gen_helper_extr_s_h(t0, t0, t1, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(v0_t); +} + +static void gen_pool32axf_2_multiply(DisasContext *ctx, uint32_t opc, + TCGv v0, TCGv v1, int rd) +{ + TCGv_i32 t0; + + t0 = tcg_temp_new_i32(); + + tcg_gen_movi_i32(t0, rd >> 3); + + switch (opc) { + case NM_POOL32AXF_2_0_7: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPA_W_PH: + check_dsp_r2(ctx); + gen_helper_dpa_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPAQ_S_W_PH: + check_dsp(ctx); + gen_helper_dpaq_s_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPS_W_PH: + check_dsp_r2(ctx); + gen_helper_dps_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPSQ_S_W_PH: + check_dsp(ctx); + gen_helper_dpsq_s_w_ph(t0, v1, v0, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_8_15: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAX_W_PH: + check_dsp_r2(ctx); + gen_helper_dpax_w_ph(t0, v0, v1, cpu_env); + break; + case NM_DPAQ_SA_L_W: + check_dsp(ctx); + gen_helper_dpaq_sa_l_w(t0, v0, v1, cpu_env); + break; + case NM_DPSX_W_PH: + check_dsp_r2(ctx); + gen_helper_dpsx_w_ph(t0, v0, v1, cpu_env); + break; + case NM_DPSQ_SA_L_W: + check_dsp(ctx); + gen_helper_dpsq_sa_l_w(t0, v0, v1, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_16_23: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBL: + check_dsp(ctx); + gen_helper_dpau_h_qbl(t0, v0, v1, cpu_env); + break; + case NM_DPAQX_S_W_PH: + check_dsp_r2(ctx); + gen_helper_dpaqx_s_w_ph(t0, v0, v1, cpu_env); + break; + case NM_DPSU_H_QBL: + check_dsp(ctx); + gen_helper_dpsu_h_qbl(t0, v0, v1, cpu_env); + break; + case NM_DPSQX_S_W_PH: + check_dsp_r2(ctx); + gen_helper_dpsqx_s_w_ph(t0, v0, v1, cpu_env); + break; + case NM_MULSA_W_PH: + check_dsp_r2(ctx); + gen_helper_mulsa_w_ph(t0, v0, v1, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_24_31: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBR: + check_dsp(ctx); + gen_helper_dpau_h_qbr(t0, v1, v0, cpu_env); + break; + case NM_DPAQX_SA_W_PH: + check_dsp_r2(ctx); + gen_helper_dpaqx_sa_w_ph(t0, v1, v0, cpu_env); + break; + case NM_DPSU_H_QBR: + check_dsp(ctx); + gen_helper_dpsu_h_qbr(t0, v1, v0, cpu_env); + break; + case NM_DPSQX_SA_W_PH: + check_dsp_r2(ctx); + gen_helper_dpsqx_sa_w_ph(t0, v1, v0, cpu_env); + break; + case NM_MULSAQ_S_W_PH: + check_dsp(ctx); + gen_helper_mulsaq_s_w_ph(t0, v1, v0, cpu_env); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free_i32(t0); +} + +static void gen_pool32axf_2_nanomips_insn(DisasContext *ctx, uint32_t opc, + int rt, int rs, int rd) +{ + int ret = rt; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv v0_t = tcg_temp_new(); + TCGv v1_t = tcg_temp_new(); + + gen_load_gpr(v0_t, rt); + gen_load_gpr(v1_t, rs); + + switch (opc) { + case NM_POOL32AXF_2_0_7: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPA_W_PH: + case NM_DPAQ_S_W_PH: + case NM_DPS_W_PH: + case NM_DPSQ_S_W_PH: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_BALIGN: + check_dsp_r2(ctx); + if (rt != 0) { + gen_load_gpr(t0, rs); + rd &= 3; + if (rd != 0 && rd != 2) { + tcg_gen_shli_tl(cpu_gpr[ret], cpu_gpr[ret], 8 * rd); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_shri_tl(t0, t0, 8 * (4 - rd)); + tcg_gen_or_tl(cpu_gpr[ret], cpu_gpr[ret], t0); + } + tcg_gen_ext32s_tl(cpu_gpr[ret], cpu_gpr[ret]); + } + break; + case NM_MADD: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + tcg_gen_ext_tl_i64(t2, t0); + tcg_gen_ext_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_add_i64(t2, t2, t3); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_MULT: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_muls2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + break; + case NM_EXTRV_W: + check_dsp(ctx); + gen_load_gpr(v1_t, rs); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_w(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_2_8_15: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAX_W_PH: + case NM_DPAQ_SA_L_W: + case NM_DPSX_W_PH: + case NM_DPSQ_SA_L_W: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_MADDU: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_extu_tl_i64(t2, t0); + tcg_gen_extu_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_add_i64(t2, t2, t3); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_MULTU: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i32 t2 = tcg_temp_new_i32(); + TCGv_i32 t3 = tcg_temp_new_i32(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_trunc_tl_i32(t2, t0); + tcg_gen_trunc_tl_i32(t3, t1); + tcg_gen_mulu2_i32(t2, t3, t2, t3); + tcg_gen_ext_i32_tl(cpu_LO[acc], t2); + tcg_gen_ext_i32_tl(cpu_HI[acc], t3); + tcg_temp_free_i32(t2); + tcg_temp_free_i32(t3); + } + break; + case NM_EXTRV_R_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_r_w(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_2_16_23: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBL: + case NM_DPAQX_S_W_PH: + case NM_DPSU_H_QBL: + case NM_DPSQX_S_W_PH: + case NM_MULSA_W_PH: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_EXTPV: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extp(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_MSUB: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext_tl_i64(t2, t0); + tcg_gen_ext_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_sub_i64(t2, t3, t2); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_EXTRV_RS_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_rs_w(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + case NM_POOL32AXF_2_24_31: + switch (extract32(ctx->opcode, 9, 3)) { + case NM_DPAU_H_QBR: + case NM_DPAQX_SA_W_PH: + case NM_DPSU_H_QBR: + case NM_DPSQX_SA_W_PH: + case NM_MULSAQ_S_W_PH: + gen_pool32axf_2_multiply(ctx, opc, v0_t, v1_t, rd); + break; + case NM_EXTPDPV: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extpdp(t0, t0, v1_t, cpu_env); + gen_store_gpr(t0, ret); + break; + case NM_MSUBU: + check_dsp(ctx); + { + int acc = extract32(ctx->opcode, 14, 2); + TCGv_i64 t2 = tcg_temp_new_i64(); + TCGv_i64 t3 = tcg_temp_new_i64(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_ext32u_tl(t0, t0); + tcg_gen_ext32u_tl(t1, t1); + tcg_gen_extu_tl_i64(t2, t0); + tcg_gen_extu_tl_i64(t3, t1); + tcg_gen_mul_i64(t2, t2, t3); + tcg_gen_concat_tl_i64(t3, cpu_LO[acc], cpu_HI[acc]); + tcg_gen_sub_i64(t2, t3, t2); + tcg_temp_free_i64(t3); + gen_move_low32(cpu_LO[acc], t2); + gen_move_high32(cpu_HI[acc], t2); + tcg_temp_free_i64(t2); + } + break; + case NM_EXTRV_S_H: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 3); + gen_helper_extr_s_h(t0, t0, v0_t, cpu_env); + gen_store_gpr(t0, ret); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(t0); + tcg_temp_free(t1); + + tcg_temp_free(v0_t); + tcg_temp_free(v1_t); +} + +static void gen_pool32axf_4_nanomips_insn(DisasContext *ctx, uint32_t opc, + int rt, int rs) +{ + int ret = rt; + TCGv t0 = tcg_temp_new(); + TCGv v0_t = tcg_temp_new(); + + gen_load_gpr(v0_t, rs); + + switch (opc) { + case NM_ABSQ_S_QB: + check_dsp_r2(ctx); + gen_helper_absq_s_qb(v0_t, v0_t, cpu_env); + gen_store_gpr(v0_t, ret); + break; + case NM_ABSQ_S_PH: + check_dsp(ctx); + gen_helper_absq_s_ph(v0_t, v0_t, cpu_env); + gen_store_gpr(v0_t, ret); + break; + case NM_ABSQ_S_W: + check_dsp(ctx); + gen_helper_absq_s_w(v0_t, v0_t, cpu_env); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQ_W_PHL: + check_dsp(ctx); + tcg_gen_andi_tl(v0_t, v0_t, 0xFFFF0000); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQ_W_PHR: + check_dsp(ctx); + tcg_gen_andi_tl(v0_t, v0_t, 0x0000FFFF); + tcg_gen_shli_tl(v0_t, v0_t, 16); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBL: + check_dsp(ctx); + gen_helper_precequ_ph_qbl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBR: + check_dsp(ctx); + gen_helper_precequ_ph_qbr(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBLA: + check_dsp(ctx); + gen_helper_precequ_ph_qbla(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEQU_PH_QBRA: + check_dsp(ctx); + gen_helper_precequ_ph_qbra(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBL: + check_dsp(ctx); + gen_helper_preceu_ph_qbl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBR: + check_dsp(ctx); + gen_helper_preceu_ph_qbr(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBLA: + check_dsp(ctx); + gen_helper_preceu_ph_qbla(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_PRECEU_PH_QBRA: + check_dsp(ctx); + gen_helper_preceu_ph_qbra(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_REPLV_PH: + check_dsp(ctx); + tcg_gen_ext16u_tl(v0_t, v0_t); + tcg_gen_shli_tl(t0, v0_t, 16); + tcg_gen_or_tl(v0_t, v0_t, t0); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_REPLV_QB: + check_dsp(ctx); + tcg_gen_ext8u_tl(v0_t, v0_t); + tcg_gen_shli_tl(t0, v0_t, 8); + tcg_gen_or_tl(v0_t, v0_t, t0); + tcg_gen_shli_tl(t0, v0_t, 16); + tcg_gen_or_tl(v0_t, v0_t, t0); + tcg_gen_ext32s_tl(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_BITREV: + check_dsp(ctx); + gen_helper_bitrev(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_INSV: + check_dsp(ctx); + { + TCGv tv0 = tcg_temp_new(); + + gen_load_gpr(tv0, rt); + gen_helper_insv(v0_t, cpu_env, v0_t, tv0); + gen_store_gpr(v0_t, ret); + tcg_temp_free(tv0); + } + break; + case NM_RADDU_W_QB: + check_dsp(ctx); + gen_helper_raddu_w_qb(v0_t, v0_t); + gen_store_gpr(v0_t, ret); + break; + case NM_BITSWAP: + gen_bitswap(ctx, OPC_BITSWAP, ret, rs); + break; + case NM_CLO: + check_nms(ctx); + gen_cl(ctx, OPC_CLO, ret, rs); + break; + case NM_CLZ: + check_nms(ctx); + gen_cl(ctx, OPC_CLZ, ret, rs); + break; + case NM_WSBH: + gen_bshfl(ctx, OPC_WSBH, ret, rs); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(v0_t); + tcg_temp_free(t0); +} + +static void gen_pool32axf_7_nanomips_insn(DisasContext *ctx, uint32_t opc, + int rt, int rs, int rd) +{ + TCGv t0 = tcg_temp_new(); + TCGv rs_t = tcg_temp_new(); + + gen_load_gpr(rs_t, rs); + + switch (opc) { + case NM_SHRA_R_QB: + check_dsp_r2(ctx); + tcg_gen_movi_tl(t0, rd >> 2); + switch (extract32(ctx->opcode, 12, 1)) { + case 0: + /* NM_SHRA_QB */ + gen_helper_shra_qb(t0, t0, rs_t); + gen_store_gpr(t0, rt); + break; + case 1: + /* NM_SHRA_R_QB */ + gen_helper_shra_r_qb(t0, t0, rs_t); + gen_store_gpr(t0, rt); + break; + } + break; + case NM_SHRL_PH: + check_dsp_r2(ctx); + tcg_gen_movi_tl(t0, rd >> 1); + gen_helper_shrl_ph(t0, t0, rs_t); + gen_store_gpr(t0, rt); + break; + case NM_REPL_QB: + check_dsp(ctx); + { + int16_t imm; + target_long result; + imm = extract32(ctx->opcode, 13, 8); + result = (uint32_t)imm << 24 | + (uint32_t)imm << 16 | + (uint32_t)imm << 8 | + (uint32_t)imm; + result = (int32_t)result; + tcg_gen_movi_tl(t0, result); + gen_store_gpr(t0, rt); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + tcg_temp_free(t0); + tcg_temp_free(rs_t); +} + + +static void gen_pool32axf_nanomips_insn(CPUMIPSState *env, DisasContext *ctx) +{ + int rt = extract32(ctx->opcode, 21, 5); + int rs = extract32(ctx->opcode, 16, 5); + int rd = extract32(ctx->opcode, 11, 5); + + switch (extract32(ctx->opcode, 6, 3)) { + case NM_POOL32AXF_1: + { + int32_t op1 = extract32(ctx->opcode, 9, 3); + gen_pool32axf_1_nanomips_insn(ctx, op1, rt, rs, rd); + } + break; + case NM_POOL32AXF_2: + { + int32_t op1 = extract32(ctx->opcode, 12, 2); + gen_pool32axf_2_nanomips_insn(ctx, op1, rt, rs, rd); + } + break; + case NM_POOL32AXF_4: + { + int32_t op1 = extract32(ctx->opcode, 9, 7); + gen_pool32axf_4_nanomips_insn(ctx, op1, rt, rs); + } + break; + case NM_POOL32AXF_5: + switch (extract32(ctx->opcode, 9, 7)) { +#ifndef CONFIG_USER_ONLY + case NM_TLBP: + gen_cp0(env, ctx, OPC_TLBP, 0, 0); + break; + case NM_TLBR: + gen_cp0(env, ctx, OPC_TLBR, 0, 0); + break; + case NM_TLBWI: + gen_cp0(env, ctx, OPC_TLBWI, 0, 0); + break; + case NM_TLBWR: + gen_cp0(env, ctx, OPC_TLBWR, 0, 0); + break; + case NM_TLBINV: + gen_cp0(env, ctx, OPC_TLBINV, 0, 0); + break; + case NM_TLBINVF: + gen_cp0(env, ctx, OPC_TLBINVF, 0, 0); + break; + case NM_DI: + check_cp0_enabled(ctx); + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_helper_di(t0, cpu_env); + gen_store_gpr(t0, rt); + /* Stop translation as we may have switched the execution mode */ + ctx->base.is_jmp = DISAS_STOP; + tcg_temp_free(t0); + } + break; + case NM_EI: + check_cp0_enabled(ctx); + { + TCGv t0 = tcg_temp_new(); + + save_cpu_state(ctx, 1); + gen_helper_ei(t0, cpu_env); + gen_store_gpr(t0, rt); + /* Stop translation as we may have switched the execution mode */ + ctx->base.is_jmp = DISAS_STOP; + tcg_temp_free(t0); + } + break; + case NM_RDPGPR: + gen_load_srsgpr(rs, rt); + break; + case NM_WRPGPR: + gen_store_srsgpr(rs, rt); + break; + case NM_WAIT: + gen_cp0(env, ctx, OPC_WAIT, 0, 0); + break; + case NM_DERET: + gen_cp0(env, ctx, OPC_DERET, 0, 0); + break; + case NM_ERETX: + gen_cp0(env, ctx, OPC_ERET, 0, 0); + break; +#endif + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32AXF_7: + { + int32_t op1 = extract32(ctx->opcode, 9, 3); + gen_pool32axf_7_nanomips_insn(ctx, op1, rt, rs, rd); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* Immediate Value Compact Branches */ +static void gen_compute_imm_branch(DisasContext *ctx, uint32_t opc, + int rt, int32_t imm, int32_t offset) +{ + TCGCond cond; + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + tcg_gen_movi_tl(t1, imm); + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + + /* Load needed operands and calculate btarget */ + switch (opc) { + case NM_BEQIC: + if (rt == 0 && imm == 0) { + /* Unconditional branch */ + } else if (rt == 0 && imm != 0) { + /* Treat as NOP */ + goto out; + } else { + bcond_compute = 1; + cond = TCG_COND_EQ; + } + break; + case NM_BBEQZC: + case NM_BBNEZC: + check_nms(ctx); + if (imm >= 32 && !(ctx->hflags & MIPS_HFLAG_64)) { + generate_exception_end(ctx, EXCP_RI); + goto out; + } else if (rt == 0 && opc == NM_BBEQZC) { + /* Unconditional branch */ + } else if (rt == 0 && opc == NM_BBNEZC) { + /* Treat as NOP */ + goto out; + } else { + tcg_gen_shri_tl(t0, t0, imm); + tcg_gen_andi_tl(t0, t0, 1); + tcg_gen_movi_tl(t1, 0); + bcond_compute = 1; + if (opc == NM_BBEQZC) { + cond = TCG_COND_EQ; + } else { + cond = TCG_COND_NE; + } + } + break; + case NM_BNEIC: + if (rt == 0 && imm == 0) { + /* Treat as NOP */ + goto out; + } else if (rt == 0 && imm != 0) { + /* Unconditional branch */ + } else { + bcond_compute = 1; + cond = TCG_COND_NE; + } + break; + case NM_BGEIC: + if (rt == 0 && imm == 0) { + /* Unconditional branch */ + } else { + bcond_compute = 1; + cond = TCG_COND_GE; + } + break; + case NM_BLTIC: + bcond_compute = 1; + cond = TCG_COND_LT; + break; + case NM_BGEIUC: + if (rt == 0 && imm == 0) { + /* Unconditional branch */ + } else { + bcond_compute = 1; + cond = TCG_COND_GEU; + } + break; + case NM_BLTIUC: + bcond_compute = 1; + cond = TCG_COND_LTU; + break; + default: + MIPS_INVAL("Immediate Value Compact branch"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + /* branch completion */ + clear_branch_hflags(ctx); + ctx->base.is_jmp = DISAS_NORETURN; + + if (bcond_compute == 0) { + /* Uncoditional compact branch */ + gen_goto_tb(ctx, 0, ctx->btarget); + } else { + /* Conditional compact branch */ + TCGLabel *fs = gen_new_label(); + + tcg_gen_brcond_tl(tcg_invert_cond(cond), t0, t1, fs); + + gen_goto_tb(ctx, 1, ctx->btarget); + gen_set_label(fs); + + gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); + } + +out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* P.BALRSC type nanoMIPS R6 branches: BALRSC and BRSC */ +static void gen_compute_nanomips_pbalrsc_branch(DisasContext *ctx, int rs, + int rt) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* load rs */ + gen_load_gpr(t0, rs); + + /* link */ + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], ctx->base.pc_next + 4); + } + + /* calculate btarget */ + tcg_gen_shli_tl(t0, t0, 1); + tcg_gen_movi_tl(t1, ctx->base.pc_next + 4); + gen_op_addr_add(ctx, btarget, t1, t0); + + /* branch completion */ + clear_branch_hflags(ctx); + ctx->base.is_jmp = DISAS_NORETURN; + + /* unconditional branch to register */ + tcg_gen_mov_tl(cpu_PC, btarget); + tcg_gen_lookup_and_goto_ptr(); + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* nanoMIPS Branches */ +static void gen_compute_compact_branch_nm(DisasContext *ctx, uint32_t opc, + int rs, int rt, int32_t offset) +{ + int bcond_compute = 0; + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + /* Load needed operands and calculate btarget */ + switch (opc) { + /* compact branch */ + case OPC_BGEC: + case OPC_BLTC: + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + break; + case OPC_BGEUC: + case OPC_BLTUC: + if (rs == 0 || rs == rt) { + /* OPC_BLEZALC, OPC_BGEZALC */ + /* OPC_BGTZALC, OPC_BLTZALC */ + tcg_gen_movi_tl(cpu_gpr[31], ctx->base.pc_next + 4); + } + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + break; + case OPC_BC: + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + break; + case OPC_BEQZC: + if (rs != 0) { + /* OPC_BEQZC, OPC_BNEZC */ + gen_load_gpr(t0, rs); + bcond_compute = 1; + ctx->btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + } else { + /* OPC_JIC, OPC_JIALC */ + TCGv tbase = tcg_temp_new(); + TCGv toffset = tcg_temp_new(); + + gen_load_gpr(tbase, rt); + tcg_gen_movi_tl(toffset, offset); + gen_op_addr_add(ctx, btarget, tbase, toffset); + tcg_temp_free(tbase); + tcg_temp_free(toffset); + } + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + if (bcond_compute == 0) { + /* Uncoditional compact branch */ + switch (opc) { + case OPC_BC: + gen_goto_tb(ctx, 0, ctx->btarget); + break; + default: + MIPS_INVAL("Compact branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + } else { + /* Conditional compact branch */ + TCGLabel *fs = gen_new_label(); + + switch (opc) { + case OPC_BGEUC: + if (rs == 0 && rt != 0) { + /* OPC_BLEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); + } else { + /* OPC_BGEUC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GEU), t0, t1, fs); + } + break; + case OPC_BLTUC: + if (rs == 0 && rt != 0) { + /* OPC_BGTZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZALC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); + } else { + /* OPC_BLTUC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LTU), t0, t1, fs); + } + break; + case OPC_BGEC: + if (rs == 0 && rt != 0) { + /* OPC_BLEZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LE), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BGEZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GE), t1, 0, fs); + } else { + /* OPC_BGEC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_GE), t0, t1, fs); + } + break; + case OPC_BLTC: + if (rs == 0 && rt != 0) { + /* OPC_BGTZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_GT), t1, 0, fs); + } else if (rs != 0 && rt != 0 && rs == rt) { + /* OPC_BLTZC */ + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_LT), t1, 0, fs); + } else { + /* OPC_BLTC */ + tcg_gen_brcond_tl(tcg_invert_cond(TCG_COND_LT), t0, t1, fs); + } + break; + case OPC_BEQZC: + tcg_gen_brcondi_tl(tcg_invert_cond(TCG_COND_EQ), t0, 0, fs); + break; + default: + MIPS_INVAL("Compact conditional branch/jump"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + /* branch completion */ + clear_branch_hflags(ctx); + ctx->base.is_jmp = DISAS_NORETURN; + + /* Generating branch here as compact branches don't have delay slot */ + gen_goto_tb(ctx, 1, ctx->btarget); + gen_set_label(fs); + + gen_goto_tb(ctx, 0, ctx->base.pc_next + 4); + } + +out: + tcg_temp_free(t0); + tcg_temp_free(t1); +} + + +/* nanoMIPS CP1 Branches */ +static void gen_compute_branch_cp1_nm(DisasContext *ctx, uint32_t op, + int32_t ft, int32_t offset) +{ + target_ulong btarget; + TCGv_i64 t0 = tcg_temp_new_i64(); + + gen_load_fpr64(ctx, t0, ft); + tcg_gen_andi_i64(t0, t0, 1); + + btarget = addr_add(ctx, ctx->base.pc_next + 4, offset); + + switch (op) { + case NM_BC1EQZC: + tcg_gen_xori_i64(t0, t0, 1); + ctx->hflags |= MIPS_HFLAG_BC; + break; + case NM_BC1NEZC: + /* t0 already set */ + ctx->hflags |= MIPS_HFLAG_BC; + break; + default: + MIPS_INVAL("cp1 cond branch"); + generate_exception_end(ctx, EXCP_RI); + goto out; + } + + tcg_gen_trunc_i64_tl(bcond, t0); + + ctx->btarget = btarget; + +out: + tcg_temp_free_i64(t0); +} + + +static void gen_p_lsx(DisasContext *ctx, int rd, int rs, int rt) +{ + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + + if ((extract32(ctx->opcode, 6, 1)) == 1) { + /* PP.LSXS instructions require shifting */ + switch (extract32(ctx->opcode, 7, 4)) { + case NM_SHXS: + check_nms(ctx); + case NM_LHXS: + case NM_LHUXS: + tcg_gen_shli_tl(t0, t0, 1); + break; + case NM_SWXS: + check_nms(ctx); + case NM_LWXS: + case NM_LWC1XS: + case NM_SWC1XS: + tcg_gen_shli_tl(t0, t0, 2); + break; + case NM_LDC1XS: + case NM_SDC1XS: + tcg_gen_shli_tl(t0, t0, 3); + break; + } + } + gen_op_addr_add(ctx, t0, t0, t1); + + switch (extract32(ctx->opcode, 7, 4)) { + case NM_LBX: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_SB); + gen_store_gpr(t0, rd); + break; + case NM_LHX: + /*case NM_LHXS:*/ + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_TESW); + gen_store_gpr(t0, rd); + break; + case NM_LWX: + /*case NM_LWXS:*/ + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_TESL); + gen_store_gpr(t0, rd); + break; + case NM_LBUX: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_UB); + gen_store_gpr(t0, rd); + break; + case NM_LHUX: + /*case NM_LHUXS:*/ + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, + MO_TEUW); + gen_store_gpr(t0, rd); + break; + case NM_SBX: + check_nms(ctx); + gen_load_gpr(t1, rd); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_8); + break; + case NM_SHX: + /*case NM_SHXS:*/ + check_nms(ctx); + gen_load_gpr(t1, rd); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_TEUW); + break; + case NM_SWX: + /*case NM_SWXS:*/ + check_nms(ctx); + gen_load_gpr(t1, rd); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, + MO_TEUL); + break; + case NM_LWC1X: + /*case NM_LWC1XS:*/ + case NM_LDC1X: + /*case NM_LDC1XS:*/ + case NM_SWC1X: + /*case NM_SWC1XS:*/ + case NM_SDC1X: + /*case NM_SDC1XS:*/ + if (ctx->CP0_Config1 & (1 << CP0C1_FP)) { + check_cp1_enabled(ctx); + switch (extract32(ctx->opcode, 7, 4)) { + case NM_LWC1X: + /*case NM_LWC1XS:*/ + gen_flt_ldst(ctx, OPC_LWC1, rd, t0); + break; + case NM_LDC1X: + /*case NM_LDC1XS:*/ + gen_flt_ldst(ctx, OPC_LDC1, rd, t0); + break; + case NM_SWC1X: + /*case NM_SWC1XS:*/ + gen_flt_ldst(ctx, OPC_SWC1, rd, t0); + break; + case NM_SDC1X: + /*case NM_SDC1XS:*/ + gen_flt_ldst(ctx, OPC_SDC1, rd, t0); + break; + } + } else { + generate_exception_err(ctx, EXCP_CpU, 1); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +static void gen_pool32f_nanomips_insn(DisasContext *ctx) +{ + int rt, rs, rd; + + rt = extract32(ctx->opcode, 21, 5); + rs = extract32(ctx->opcode, 16, 5); + rd = extract32(ctx->opcode, 11, 5); + + if (!(ctx->CP0_Config1 & (1 << CP0C1_FP))) { + generate_exception_end(ctx, EXCP_RI); + return; + } + check_cp1_enabled(ctx); + switch (extract32(ctx->opcode, 0, 3)) { + case NM_POOL32F_0: + switch (extract32(ctx->opcode, 3, 7)) { + case NM_RINT_S: + gen_farith(ctx, OPC_RINT_S, 0, rt, rs, 0); + break; + case NM_RINT_D: + gen_farith(ctx, OPC_RINT_D, 0, rt, rs, 0); + break; + case NM_CLASS_S: + gen_farith(ctx, OPC_CLASS_S, 0, rt, rs, 0); + break; + case NM_CLASS_D: + gen_farith(ctx, OPC_CLASS_D, 0, rt, rs, 0); + break; + case NM_ADD_S: + gen_farith(ctx, OPC_ADD_S, rt, rs, rd, 0); + break; + case NM_ADD_D: + gen_farith(ctx, OPC_ADD_D, rt, rs, rd, 0); + break; + case NM_SUB_S: + gen_farith(ctx, OPC_SUB_S, rt, rs, rd, 0); + break; + case NM_SUB_D: + gen_farith(ctx, OPC_SUB_D, rt, rs, rd, 0); + break; + case NM_MUL_S: + gen_farith(ctx, OPC_MUL_S, rt, rs, rd, 0); + break; + case NM_MUL_D: + gen_farith(ctx, OPC_MUL_D, rt, rs, rd, 0); + break; + case NM_DIV_S: + gen_farith(ctx, OPC_DIV_S, rt, rs, rd, 0); + break; + case NM_DIV_D: + gen_farith(ctx, OPC_DIV_D, rt, rs, rd, 0); + break; + case NM_SELEQZ_S: + gen_sel_s(ctx, OPC_SELEQZ_S, rd, rt, rs); + break; + case NM_SELEQZ_D: + gen_sel_d(ctx, OPC_SELEQZ_D, rd, rt, rs); + break; + case NM_SELNEZ_S: + gen_sel_s(ctx, OPC_SELNEZ_S, rd, rt, rs); + break; + case NM_SELNEZ_D: + gen_sel_d(ctx, OPC_SELNEZ_D, rd, rt, rs); + break; + case NM_SEL_S: + gen_sel_s(ctx, OPC_SEL_S, rd, rt, rs); + break; + case NM_SEL_D: + gen_sel_d(ctx, OPC_SEL_D, rd, rt, rs); + break; + case NM_MADDF_S: + gen_farith(ctx, OPC_MADDF_S, rt, rs, rd, 0); + break; + case NM_MADDF_D: + gen_farith(ctx, OPC_MADDF_D, rt, rs, rd, 0); + break; + case NM_MSUBF_S: + gen_farith(ctx, OPC_MSUBF_S, rt, rs, rd, 0); + break; + case NM_MSUBF_D: + gen_farith(ctx, OPC_MSUBF_D, rt, rs, rd, 0); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32F_3: + switch (extract32(ctx->opcode, 3, 3)) { + case NM_MIN_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MIN_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MIN_D, rt, rs, rd, 0); + break; + } + break; + case NM_MAX_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MAX_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MAX_D, rt, rs, rd, 0); + break; + } + break; + case NM_MINA_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MINA_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MINA_D, rt, rs, rd, 0); + break; + } + break; + case NM_MAXA_FMT: + switch (extract32(ctx->opcode, 9, 1)) { + case FMT_SDPS_S: + gen_farith(ctx, OPC_MAXA_S, rt, rs, rd, 0); + break; + case FMT_SDPS_D: + gen_farith(ctx, OPC_MAXA_D, rt, rs, rd, 0); + break; + } + break; + case NM_POOL32FXF: + switch (extract32(ctx->opcode, 6, 8)) { + case NM_CFC1: + gen_cp1(ctx, OPC_CFC1, rt, rs); + break; + case NM_CTC1: + gen_cp1(ctx, OPC_CTC1, rt, rs); + break; + case NM_MFC1: + gen_cp1(ctx, OPC_MFC1, rt, rs); + break; + case NM_MTC1: + gen_cp1(ctx, OPC_MTC1, rt, rs); + break; + case NM_MFHC1: + gen_cp1(ctx, OPC_MFHC1, rt, rs); + break; + case NM_MTHC1: + gen_cp1(ctx, OPC_MTHC1, rt, rs); + break; + case NM_CVT_S_PL: + gen_farith(ctx, OPC_CVT_S_PL, -1, rs, rt, 0); + break; + case NM_CVT_S_PU: + gen_farith(ctx, OPC_CVT_S_PU, -1, rs, rt, 0); + break; + default: + switch (extract32(ctx->opcode, 6, 9)) { + case NM_CVT_L_S: + gen_farith(ctx, OPC_CVT_L_S, -1, rs, rt, 0); + break; + case NM_CVT_L_D: + gen_farith(ctx, OPC_CVT_L_D, -1, rs, rt, 0); + break; + case NM_CVT_W_S: + gen_farith(ctx, OPC_CVT_W_S, -1, rs, rt, 0); + break; + case NM_CVT_W_D: + gen_farith(ctx, OPC_CVT_W_D, -1, rs, rt, 0); + break; + case NM_RSQRT_S: + gen_farith(ctx, OPC_RSQRT_S, -1, rs, rt, 0); + break; + case NM_RSQRT_D: + gen_farith(ctx, OPC_RSQRT_D, -1, rs, rt, 0); + break; + case NM_SQRT_S: + gen_farith(ctx, OPC_SQRT_S, -1, rs, rt, 0); + break; + case NM_SQRT_D: + gen_farith(ctx, OPC_SQRT_D, -1, rs, rt, 0); + break; + case NM_RECIP_S: + gen_farith(ctx, OPC_RECIP_S, -1, rs, rt, 0); + break; + case NM_RECIP_D: + gen_farith(ctx, OPC_RECIP_D, -1, rs, rt, 0); + break; + case NM_FLOOR_L_S: + gen_farith(ctx, OPC_FLOOR_L_S, -1, rs, rt, 0); + break; + case NM_FLOOR_L_D: + gen_farith(ctx, OPC_FLOOR_L_D, -1, rs, rt, 0); + break; + case NM_FLOOR_W_S: + gen_farith(ctx, OPC_FLOOR_W_S, -1, rs, rt, 0); + break; + case NM_FLOOR_W_D: + gen_farith(ctx, OPC_FLOOR_W_D, -1, rs, rt, 0); + break; + case NM_CEIL_L_S: + gen_farith(ctx, OPC_CEIL_L_S, -1, rs, rt, 0); + break; + case NM_CEIL_L_D: + gen_farith(ctx, OPC_CEIL_L_D, -1, rs, rt, 0); + break; + case NM_CEIL_W_S: + gen_farith(ctx, OPC_CEIL_W_S, -1, rs, rt, 0); + break; + case NM_CEIL_W_D: + gen_farith(ctx, OPC_CEIL_W_D, -1, rs, rt, 0); + break; + case NM_TRUNC_L_S: + gen_farith(ctx, OPC_TRUNC_L_S, -1, rs, rt, 0); + break; + case NM_TRUNC_L_D: + gen_farith(ctx, OPC_TRUNC_L_D, -1, rs, rt, 0); + break; + case NM_TRUNC_W_S: + gen_farith(ctx, OPC_TRUNC_W_S, -1, rs, rt, 0); + break; + case NM_TRUNC_W_D: + gen_farith(ctx, OPC_TRUNC_W_D, -1, rs, rt, 0); + break; + case NM_ROUND_L_S: + gen_farith(ctx, OPC_ROUND_L_S, -1, rs, rt, 0); + break; + case NM_ROUND_L_D: + gen_farith(ctx, OPC_ROUND_L_D, -1, rs, rt, 0); + break; + case NM_ROUND_W_S: + gen_farith(ctx, OPC_ROUND_W_S, -1, rs, rt, 0); + break; + case NM_ROUND_W_D: + gen_farith(ctx, OPC_ROUND_W_D, -1, rs, rt, 0); + break; + case NM_MOV_S: + gen_farith(ctx, OPC_MOV_S, -1, rs, rt, 0); + break; + case NM_MOV_D: + gen_farith(ctx, OPC_MOV_D, -1, rs, rt, 0); + break; + case NM_ABS_S: + gen_farith(ctx, OPC_ABS_S, -1, rs, rt, 0); + break; + case NM_ABS_D: + gen_farith(ctx, OPC_ABS_D, -1, rs, rt, 0); + break; + case NM_NEG_S: + gen_farith(ctx, OPC_NEG_S, -1, rs, rt, 0); + break; + case NM_NEG_D: + gen_farith(ctx, OPC_NEG_D, -1, rs, rt, 0); + break; + case NM_CVT_D_S: + gen_farith(ctx, OPC_CVT_D_S, -1, rs, rt, 0); + break; + case NM_CVT_D_W: + gen_farith(ctx, OPC_CVT_D_W, -1, rs, rt, 0); + break; + case NM_CVT_D_L: + gen_farith(ctx, OPC_CVT_D_L, -1, rs, rt, 0); + break; + case NM_CVT_S_D: + gen_farith(ctx, OPC_CVT_S_D, -1, rs, rt, 0); + break; + case NM_CVT_S_W: + gen_farith(ctx, OPC_CVT_S_W, -1, rs, rt, 0); + break; + case NM_CVT_S_L: + gen_farith(ctx, OPC_CVT_S_L, -1, rs, rt, 0); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + } + break; + } + break; + case NM_POOL32F_5: + switch (extract32(ctx->opcode, 3, 3)) { + case NM_CMP_CONDN_S: + gen_r6_cmp_s(ctx, extract32(ctx->opcode, 6, 5), rt, rs, rd); + break; + case NM_CMP_CONDN_D: + gen_r6_cmp_d(ctx, extract32(ctx->opcode, 6, 5), rt, rs, rd); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void gen_pool32a5_nanomips_insn(DisasContext *ctx, int opc, + int rd, int rs, int rt) +{ + int ret = rd; + TCGv t0 = tcg_temp_new(); + TCGv v1_t = tcg_temp_new(); + TCGv v2_t = tcg_temp_new(); + + gen_load_gpr(v1_t, rs); + gen_load_gpr(v2_t, rt); + + switch (opc) { + case NM_CMP_EQ_PH: + check_dsp(ctx); + gen_helper_cmp_eq_ph(v1_t, v2_t, cpu_env); + break; + case NM_CMP_LT_PH: + check_dsp(ctx); + gen_helper_cmp_lt_ph(v1_t, v2_t, cpu_env); + break; + case NM_CMP_LE_PH: + check_dsp(ctx); + gen_helper_cmp_le_ph(v1_t, v2_t, cpu_env); + break; + case NM_CMPU_EQ_QB: + check_dsp(ctx); + gen_helper_cmpu_eq_qb(v1_t, v2_t, cpu_env); + break; + case NM_CMPU_LT_QB: + check_dsp(ctx); + gen_helper_cmpu_lt_qb(v1_t, v2_t, cpu_env); + break; + case NM_CMPU_LE_QB: + check_dsp(ctx); + gen_helper_cmpu_le_qb(v1_t, v2_t, cpu_env); + break; + case NM_CMPGU_EQ_QB: + check_dsp(ctx); + gen_helper_cmpgu_eq_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGU_LT_QB: + check_dsp(ctx); + gen_helper_cmpgu_lt_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGU_LE_QB: + check_dsp(ctx); + gen_helper_cmpgu_le_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGDU_EQ_QB: + check_dsp_r2(ctx); + gen_helper_cmpgu_eq_qb(v1_t, v1_t, v2_t); + tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGDU_LT_QB: + check_dsp_r2(ctx); + gen_helper_cmpgu_lt_qb(v1_t, v1_t, v2_t); + tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); + gen_store_gpr(v1_t, ret); + break; + case NM_CMPGDU_LE_QB: + check_dsp_r2(ctx); + gen_helper_cmpgu_le_qb(v1_t, v1_t, v2_t); + tcg_gen_deposit_tl(cpu_dspctrl, cpu_dspctrl, v1_t, 24, 4); + gen_store_gpr(v1_t, ret); + break; + case NM_PACKRL_PH: + check_dsp(ctx); + gen_helper_packrl_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PICK_QB: + check_dsp(ctx); + gen_helper_pick_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_PICK_PH: + check_dsp(ctx); + gen_helper_pick_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDQ_S_W: + check_dsp(ctx); + gen_helper_addq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SUBQ_S_W: + check_dsp(ctx); + gen_helper_subq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDSC: + check_dsp(ctx); + gen_helper_addsc(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDWC: + check_dsp(ctx); + gen_helper_addwc(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_ADDQ_S_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDQ_PH */ + gen_helper_addq_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDQ_S_PH */ + gen_helper_addq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDQH_R_PH: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDQH_PH */ + gen_helper_addqh_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDQH_R_PH */ + gen_helper_addqh_r_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDQH_R_W: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDQH_W */ + gen_helper_addqh_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDQH_R_W */ + gen_helper_addqh_r_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDU_S_QB: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDU_QB */ + gen_helper_addu_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDU_S_QB */ + gen_helper_addu_s_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDU_S_PH: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDU_PH */ + gen_helper_addu_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDU_S_PH */ + gen_helper_addu_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_ADDUH_R_QB: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* ADDUH_QB */ + gen_helper_adduh_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* ADDUH_R_QB */ + gen_helper_adduh_r_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SHRAV_R_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHRAV_PH */ + gen_helper_shra_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SHRAV_R_PH */ + gen_helper_shra_r_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SHRAV_R_QB: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHRAV_QB */ + gen_helper_shra_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SHRAV_R_QB */ + gen_helper_shra_r_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBQ_S_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBQ_PH */ + gen_helper_subq_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBQ_S_PH */ + gen_helper_subq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBQH_R_PH: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBQH_PH */ + gen_helper_subqh_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBQH_R_PH */ + gen_helper_subqh_r_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBQH_R_W: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBQH_W */ + gen_helper_subqh_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBQH_R_W */ + gen_helper_subqh_r_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBU_S_QB: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBU_QB */ + gen_helper_subu_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBU_S_QB */ + gen_helper_subu_s_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBU_S_PH: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBU_PH */ + gen_helper_subu_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBU_S_PH */ + gen_helper_subu_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SUBUH_R_QB: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SUBUH_QB */ + gen_helper_subuh_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SUBUH_R_QB */ + gen_helper_subuh_r_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_SHLLV_S_PH: + check_dsp(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHLLV_PH */ + gen_helper_shll_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* SHLLV_S_PH */ + gen_helper_shll_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_PRECR_SRA_R_PH_W: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* PRECR_SRA_PH_W */ + { + TCGv_i32 sa_t = tcg_const_i32(rd); + gen_helper_precr_sra_ph_w(v1_t, sa_t, v1_t, + cpu_gpr[rt]); + gen_store_gpr(v1_t, rt); + tcg_temp_free_i32(sa_t); + } + break; + case 1: + /* PRECR_SRA_R_PH_W */ + { + TCGv_i32 sa_t = tcg_const_i32(rd); + gen_helper_precr_sra_r_ph_w(v1_t, sa_t, v1_t, + cpu_gpr[rt]); + gen_store_gpr(v1_t, rt); + tcg_temp_free_i32(sa_t); + } + break; + } + break; + case NM_MULEU_S_PH_QBL: + check_dsp(ctx); + gen_helper_muleu_s_ph_qbl(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULEU_S_PH_QBR: + check_dsp(ctx); + gen_helper_muleu_s_ph_qbr(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_RS_PH: + check_dsp(ctx); + gen_helper_mulq_rs_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_S_PH: + check_dsp_r2(ctx); + gen_helper_mulq_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_RS_W: + check_dsp_r2(ctx); + gen_helper_mulq_rs_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULQ_S_W: + check_dsp_r2(ctx); + gen_helper_mulq_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_APPEND: + check_dsp_r2(ctx); + gen_load_gpr(t0, rs); + if (rd != 0) { + tcg_gen_deposit_tl(cpu_gpr[rt], t0, cpu_gpr[rt], rd, 32 - rd); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + break; + case NM_MODSUB: + check_dsp(ctx); + gen_helper_modsub(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRAV_R_W: + check_dsp(ctx); + gen_helper_shra_r_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRLV_PH: + check_dsp_r2(ctx); + gen_helper_shrl_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRLV_QB: + check_dsp(ctx); + gen_helper_shrl_qb(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_SHLLV_QB: + check_dsp(ctx); + gen_helper_shll_qb(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SHLLV_S_W: + check_dsp(ctx); + gen_helper_shll_s_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SHILO: + check_dsp(ctx); + { + TCGv tv0 = tcg_temp_new(); + TCGv tv1 = tcg_temp_new(); + int16_t imm = extract32(ctx->opcode, 16, 7); + + tcg_gen_movi_tl(tv0, rd >> 3); + tcg_gen_movi_tl(tv1, imm); + gen_helper_shilo(tv0, tv1, cpu_env); + } + break; + case NM_MULEQ_S_W_PHL: + check_dsp(ctx); + gen_helper_muleq_s_w_phl(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MULEQ_S_W_PHR: + check_dsp(ctx); + gen_helper_muleq_s_w_phr(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_MUL_S_PH: + check_dsp_r2(ctx); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* MUL_PH */ + gen_helper_mul_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case 1: + /* MUL_S_PH */ + gen_helper_mul_s_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + } + break; + case NM_PRECR_QB_PH: + check_dsp_r2(ctx); + gen_helper_precr_qb_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQ_QB_PH: + check_dsp(ctx); + gen_helper_precrq_qb_ph(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQ_PH_W: + check_dsp(ctx); + gen_helper_precrq_ph_w(v1_t, v1_t, v2_t); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQ_RS_PH_W: + check_dsp(ctx); + gen_helper_precrq_rs_ph_w(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_PRECRQU_S_QB_PH: + check_dsp(ctx); + gen_helper_precrqu_s_qb_ph(v1_t, v1_t, v2_t, cpu_env); + gen_store_gpr(v1_t, ret); + break; + case NM_SHRA_R_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd); + gen_helper_shra_r_w(v1_t, t0, v1_t); + gen_store_gpr(v1_t, rt); + break; + case NM_SHRA_R_PH: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 1); + switch (extract32(ctx->opcode, 10, 1)) { + case 0: + /* SHRA_PH */ + gen_helper_shra_ph(v1_t, t0, v1_t); + gen_store_gpr(v1_t, rt); + break; + case 1: + /* SHRA_R_PH */ + gen_helper_shra_r_ph(v1_t, t0, v1_t); + gen_store_gpr(v1_t, rt); + break; + } + break; + case NM_SHLL_S_PH: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd >> 1); + switch (extract32(ctx->opcode, 10, 2)) { + case 0: + /* SHLL_PH */ + gen_helper_shll_ph(v1_t, t0, v1_t, cpu_env); + gen_store_gpr(v1_t, rt); + break; + case 2: + /* SHLL_S_PH */ + gen_helper_shll_s_ph(v1_t, t0, v1_t, cpu_env); + gen_store_gpr(v1_t, rt); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_SHLL_S_W: + check_dsp(ctx); + tcg_gen_movi_tl(t0, rd); + gen_helper_shll_s_w(v1_t, t0, v1_t, cpu_env); + gen_store_gpr(v1_t, rt); + break; + case NM_REPL_PH: + check_dsp(ctx); + { + int16_t imm; + imm = sextract32(ctx->opcode, 11, 11); + imm = (int16_t)(imm << 6) >> 6; + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], dup_const(MO_16, imm)); + } + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) +{ + uint16_t insn; + uint32_t op; + int rt, rs, rd; + int offset; + int imm; + + insn = cpu_lduw_code(env, ctx->base.pc_next + 2); + ctx->opcode = (ctx->opcode << 16) | insn; + + rt = extract32(ctx->opcode, 21, 5); + rs = extract32(ctx->opcode, 16, 5); + rd = extract32(ctx->opcode, 11, 5); + + op = extract32(ctx->opcode, 26, 6); + switch (op) { + case NM_P_ADDIU: + if (rt == 0) { + /* P.RI */ + switch (extract32(ctx->opcode, 19, 2)) { + case NM_SIGRIE: + default: + generate_exception_end(ctx, EXCP_RI); + break; + case NM_P_SYSCALL: + if ((extract32(ctx->opcode, 18, 1)) == NM_SYSCALL) { + generate_exception_end(ctx, EXCP_SYSCALL); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + case NM_BREAK: + generate_exception_end(ctx, EXCP_BREAK); + break; + case NM_SDBBP: + if (is_uhi(extract32(ctx->opcode, 0, 19))) { + gen_helper_do_semihosting(cpu_env); + } else { + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception_end(ctx, EXCP_RI); + } else { + generate_exception_end(ctx, EXCP_DBp); + } + } + break; + } + } else { + /* NM_ADDIU */ + imm = extract32(ctx->opcode, 0, 16); + if (rs != 0) { + tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rs], imm); + } else { + tcg_gen_movi_tl(cpu_gpr[rt], imm); + } + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + } + break; + case NM_ADDIUPC: + if (rt != 0) { + offset = sextract32(ctx->opcode, 0, 1) << 21 | + extract32(ctx->opcode, 1, 20) << 1; + target_long addr = addr_add(ctx, ctx->base.pc_next + 4, offset); + tcg_gen_movi_tl(cpu_gpr[rt], addr); + } + break; + case NM_POOL32A: + switch (ctx->opcode & 0x07) { + case NM_POOL32A0: + gen_pool32a0_nanomips_insn(env, ctx); + break; + case NM_POOL32A5: + { + int32_t op1 = extract32(ctx->opcode, 3, 7); + gen_pool32a5_nanomips_insn(ctx, op1, rd, rs, rt); + } + break; + case NM_POOL32A7: + switch (extract32(ctx->opcode, 3, 3)) { + case NM_P_LSX: + gen_p_lsx(ctx, rd, rs, rt); + break; + case NM_LSA: + /* In nanoMIPS, the shift field directly encodes the shift + * amount, meaning that the supported shift values are in + * the range 0 to 3 (instead of 1 to 4 in MIPSR6). */ + gen_lsa(ctx, OPC_LSA, rd, rs, rt, + extract32(ctx->opcode, 9, 2) - 1); + break; + case NM_EXTW: + gen_ext(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 6, 5)); + break; + case NM_POOL32AXF: + gen_pool32axf_nanomips_insn(env, ctx); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_GP_W: + switch (ctx->opcode & 0x03) { + case NM_ADDIUGP_W: + if (rt != 0) { + offset = extract32(ctx->opcode, 0, 21); + gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], offset); + } + break; + case NM_LWGP: + gen_ld(ctx, OPC_LW, rt, 28, extract32(ctx->opcode, 2, 19) << 2); + break; + case NM_SWGP: + gen_st(ctx, OPC_SW, rt, 28, extract32(ctx->opcode, 2, 19) << 2); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P48I: + { + insn = cpu_lduw_code(env, ctx->base.pc_next + 4); + target_long addr_off = extract32(ctx->opcode, 0, 16) | insn << 16; + switch (extract32(ctx->opcode, 16, 5)) { + case NM_LI48: + check_nms(ctx); + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], addr_off); + } + break; + case NM_ADDIU48: + check_nms(ctx); + if (rt != 0) { + tcg_gen_addi_tl(cpu_gpr[rt], cpu_gpr[rt], addr_off); + tcg_gen_ext32s_tl(cpu_gpr[rt], cpu_gpr[rt]); + } + break; + case NM_ADDIUGP48: + check_nms(ctx); + if (rt != 0) { + gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], addr_off); + } + break; + case NM_ADDIUPC48: + check_nms(ctx); + if (rt != 0) { + target_long addr = addr_add(ctx, ctx->base.pc_next + 6, + addr_off); + + tcg_gen_movi_tl(cpu_gpr[rt], addr); + } + break; + case NM_LWPC48: + check_nms(ctx); + if (rt != 0) { + TCGv t0; + t0 = tcg_temp_new(); + + target_long addr = addr_add(ctx, ctx->base.pc_next + 6, + addr_off); + + tcg_gen_movi_tl(t0, addr); + tcg_gen_qemu_ld_tl(cpu_gpr[rt], t0, ctx->mem_idx, MO_TESL); + tcg_temp_free(t0); + } + break; + case NM_SWPC48: + check_nms(ctx); + { + TCGv t0, t1; + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + target_long addr = addr_add(ctx, ctx->base.pc_next + 6, + addr_off); + + tcg_gen_movi_tl(t0, addr); + gen_load_gpr(t1, rt); + + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUL); + + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + return 6; + } + case NM_P_U12: + switch (extract32(ctx->opcode, 12, 4)) { + case NM_ORI: + gen_logic_imm(ctx, OPC_ORI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_XORI: + gen_logic_imm(ctx, OPC_XORI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_ANDI: + gen_logic_imm(ctx, OPC_ANDI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_P_SR: + switch (extract32(ctx->opcode, 20, 1)) { + case NM_PP_SR: + switch (ctx->opcode & 3) { + case NM_SAVE: + gen_save(ctx, rt, extract32(ctx->opcode, 16, 4), + extract32(ctx->opcode, 2, 1), + extract32(ctx->opcode, 3, 9) << 3); + break; + case NM_RESTORE: + case NM_RESTORE_JRC: + gen_restore(ctx, rt, extract32(ctx->opcode, 16, 4), + extract32(ctx->opcode, 2, 1), + extract32(ctx->opcode, 3, 9) << 3); + if ((ctx->opcode & 3) == NM_RESTORE_JRC) { + gen_compute_branch_nm(ctx, OPC_JR, 2, 31, 0, 0); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_SR_F: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_SLTI: + gen_slt_imm(ctx, OPC_SLTI, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_SLTIU: + gen_slt_imm(ctx, OPC_SLTIU, rt, rs, extract32(ctx->opcode, 0, 12)); + break; + case NM_SEQI: + { + TCGv t0 = tcg_temp_new(); + + imm = extract32(ctx->opcode, 0, 12); + gen_load_gpr(t0, rs); + tcg_gen_setcondi_tl(TCG_COND_EQ, t0, t0, imm); + gen_store_gpr(t0, rt); + + tcg_temp_free(t0); + } + break; + case NM_ADDIUNEG: + imm = (int16_t) extract32(ctx->opcode, 0, 12); + gen_arith_imm(ctx, OPC_ADDIU, rt, rs, -imm); + break; + case NM_P_SHIFT: + { + int shift = extract32(ctx->opcode, 0, 5); + switch (extract32(ctx->opcode, 5, 4)) { + case NM_P_SLL: + if (rt == 0 && shift == 0) { + /* NOP */ + } else if (rt == 0 && shift == 3) { + /* EHB - treat as NOP */ + } else if (rt == 0 && shift == 5) { + /* PAUSE - treat as NOP */ + } else if (rt == 0 && shift == 6) { + /* SYNC */ + gen_sync(extract32(ctx->opcode, 16, 5)); + } else { + /* SLL */ + gen_shift_imm(ctx, OPC_SLL, rt, rs, + extract32(ctx->opcode, 0, 5)); + } + break; + case NM_SRL: + gen_shift_imm(ctx, OPC_SRL, rt, rs, + extract32(ctx->opcode, 0, 5)); + break; + case NM_SRA: + gen_shift_imm(ctx, OPC_SRA, rt, rs, + extract32(ctx->opcode, 0, 5)); + break; + case NM_ROTR: + gen_shift_imm(ctx, OPC_ROTR, rt, rs, + extract32(ctx->opcode, 0, 5)); + break; + } + } + break; + case NM_P_ROTX: + check_nms(ctx); + if (rt != 0) { + TCGv t0 = tcg_temp_new(); + TCGv_i32 shift = tcg_const_i32(extract32(ctx->opcode, 0, 5)); + TCGv_i32 shiftx = tcg_const_i32(extract32(ctx->opcode, 7, 4) + << 1); + TCGv_i32 stripe = tcg_const_i32(extract32(ctx->opcode, 6, 1)); + + gen_load_gpr(t0, rs); + gen_helper_rotx(cpu_gpr[rt], t0, shift, shiftx, stripe); + tcg_temp_free(t0); + + tcg_temp_free_i32(shift); + tcg_temp_free_i32(shiftx); + tcg_temp_free_i32(stripe); + } + break; + case NM_P_INS: + switch (((ctx->opcode >> 10) & 2) | + (extract32(ctx->opcode, 5, 1))) { + case NM_INS: + check_nms(ctx); + gen_bitops(ctx, OPC_INS, rt, rs, extract32(ctx->opcode, 0, 5), + extract32(ctx->opcode, 6, 5)); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_EXT: + switch (((ctx->opcode >> 10) & 2) | + (extract32(ctx->opcode, 5, 1))) { + case NM_EXT: + check_nms(ctx); + gen_bitops(ctx, OPC_EXT, rt, rs, extract32(ctx->opcode, 0, 5), + extract32(ctx->opcode, 6, 5)); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_POOL32F: + gen_pool32f_nanomips_insn(ctx); + break; + case NM_POOL32S: + break; + case NM_P_LUI: + switch (extract32(ctx->opcode, 1, 1)) { + case NM_LUI: + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], + sextract32(ctx->opcode, 0, 1) << 31 | + extract32(ctx->opcode, 2, 10) << 21 | + extract32(ctx->opcode, 12, 9) << 12); + } + break; + case NM_ALUIPC: + if (rt != 0) { + offset = sextract32(ctx->opcode, 0, 1) << 31 | + extract32(ctx->opcode, 2, 10) << 21 | + extract32(ctx->opcode, 12, 9) << 12; + target_long addr; + addr = ~0xFFF & addr_add(ctx, ctx->base.pc_next + 4, offset); + tcg_gen_movi_tl(cpu_gpr[rt], addr); + } + break; + } + break; + case NM_P_GP_BH: + { + uint32_t u = extract32(ctx->opcode, 0, 18); + + switch (extract32(ctx->opcode, 18, 3)) { + case NM_LBGP: + gen_ld(ctx, OPC_LB, rt, 28, u); + break; + case NM_SBGP: + gen_st(ctx, OPC_SB, rt, 28, u); + break; + case NM_LBUGP: + gen_ld(ctx, OPC_LBU, rt, 28, u); + break; + case NM_ADDIUGP_B: + if (rt != 0) { + gen_op_addr_addi(ctx, cpu_gpr[rt], cpu_gpr[28], u); + } + break; + case NM_P_GP_LH: + u &= ~1; + switch (ctx->opcode & 1) { + case NM_LHGP: + gen_ld(ctx, OPC_LH, rt, 28, u); + break; + case NM_LHUGP: + gen_ld(ctx, OPC_LHU, rt, 28, u); + break; + } + break; + case NM_P_GP_SH: + u &= ~1; + switch (ctx->opcode & 1) { + case NM_SHGP: + gen_st(ctx, OPC_SH, rt, 28, u); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_GP_CP1: + u &= ~0x3; + switch (ctx->opcode & 0x3) { + case NM_LWC1GP: + gen_cop1_ldst(ctx, OPC_LWC1, rt, 28, u); + break; + case NM_LDC1GP: + gen_cop1_ldst(ctx, OPC_LDC1, rt, 28, u); + break; + case NM_SWC1GP: + gen_cop1_ldst(ctx, OPC_SWC1, rt, 28, u); + break; + case NM_SDC1GP: + gen_cop1_ldst(ctx, OPC_SDC1, rt, 28, u); + break; + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P_LS_U12: + { + uint32_t u = extract32(ctx->opcode, 0, 12); + + switch (extract32(ctx->opcode, 12, 4)) { + case NM_P_PREFU12: + if (rt == 31) { + /* SYNCI */ + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->base.is_jmp = DISAS_STOP; + } else { + /* PREF */ + /* Treat as NOP. */ + } + break; + case NM_LB: + gen_ld(ctx, OPC_LB, rt, rs, u); + break; + case NM_LH: + gen_ld(ctx, OPC_LH, rt, rs, u); + break; + case NM_LW: + gen_ld(ctx, OPC_LW, rt, rs, u); + break; + case NM_LBU: + gen_ld(ctx, OPC_LBU, rt, rs, u); + break; + case NM_LHU: + gen_ld(ctx, OPC_LHU, rt, rs, u); + break; + case NM_SB: + gen_st(ctx, OPC_SB, rt, rs, u); + break; + case NM_SH: + gen_st(ctx, OPC_SH, rt, rs, u); + break; + case NM_SW: + gen_st(ctx, OPC_SW, rt, rs, u); + break; + case NM_LWC1: + gen_cop1_ldst(ctx, OPC_LWC1, rt, rs, u); + break; + case NM_LDC1: + gen_cop1_ldst(ctx, OPC_LDC1, rt, rs, u); + break; + case NM_SWC1: + gen_cop1_ldst(ctx, OPC_SWC1, rt, rs, u); + break; + case NM_SDC1: + gen_cop1_ldst(ctx, OPC_SDC1, rt, rs, u); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P_LS_S9: + { + int32_t s = (sextract32(ctx->opcode, 15, 1) << 8) | + extract32(ctx->opcode, 0, 8); + + switch (extract32(ctx->opcode, 8, 3)) { + case NM_P_LS_S0: + switch (extract32(ctx->opcode, 11, 4)) { + case NM_LBS9: + gen_ld(ctx, OPC_LB, rt, rs, s); + break; + case NM_LHS9: + gen_ld(ctx, OPC_LH, rt, rs, s); + break; + case NM_LWS9: + gen_ld(ctx, OPC_LW, rt, rs, s); + break; + case NM_LBUS9: + gen_ld(ctx, OPC_LBU, rt, rs, s); + break; + case NM_LHUS9: + gen_ld(ctx, OPC_LHU, rt, rs, s); + break; + case NM_SBS9: + gen_st(ctx, OPC_SB, rt, rs, s); + break; + case NM_SHS9: + gen_st(ctx, OPC_SH, rt, rs, s); + break; + case NM_SWS9: + gen_st(ctx, OPC_SW, rt, rs, s); + break; + case NM_LWC1S9: + gen_cop1_ldst(ctx, OPC_LWC1, rt, rs, s); + break; + case NM_LDC1S9: + gen_cop1_ldst(ctx, OPC_LDC1, rt, rs, s); + break; + case NM_SWC1S9: + gen_cop1_ldst(ctx, OPC_SWC1, rt, rs, s); + break; + case NM_SDC1S9: + gen_cop1_ldst(ctx, OPC_SDC1, rt, rs, s); + break; + case NM_P_PREFS9: + if (rt == 31) { + /* SYNCI */ + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->base.is_jmp = DISAS_STOP; + } else { + /* PREF */ + /* Treat as NOP. */ + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_LS_S1: + switch (extract32(ctx->opcode, 11, 4)) { + case NM_UALH: + case NM_UASH: + check_nms(ctx); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + + gen_base_offset_addr(ctx, t0, rs, s); + + switch (extract32(ctx->opcode, 11, 4)) { + case NM_UALH: + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_TESW | + MO_UNALN); + gen_store_gpr(t0, rt); + break; + case NM_UASH: + gen_load_gpr(t1, rt); + tcg_gen_qemu_st_tl(t1, t0, ctx->mem_idx, MO_TEUW | + MO_UNALN); + break; + } + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + case NM_P_LL: + switch (ctx->opcode & 0x03) { + case NM_LL: + gen_ld(ctx, OPC_LL, rt, rs, s); + break; + case NM_LLWP: + check_xnp(ctx); + gen_llwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); + break; + } + break; + case NM_P_SC: + switch (ctx->opcode & 0x03) { + case NM_SC: + gen_st_cond(ctx, rt, rs, s, MO_TESL, false); + break; + case NM_SCWP: + check_xnp(ctx); + gen_scwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5), + false); + break; + } + break; + case NM_CACHE: + check_cp0_enabled(ctx); + if (ctx->hflags & MIPS_HFLAG_ITC_CACHE) { + gen_cache_operation(ctx, rt, rs, s); + } + break; + } + break; + case NM_P_LS_E0: + switch (extract32(ctx->opcode, 11, 4)) { + case NM_LBE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_ld(ctx, OPC_LBE, rt, rs, s); + break; + case NM_SBE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_st(ctx, OPC_SBE, rt, rs, s); + break; + case NM_LBUE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_ld(ctx, OPC_LBUE, rt, rs, s); + break; + case NM_P_PREFE: + if (rt == 31) { + /* case NM_SYNCIE */ + check_eva(ctx); + check_cp0_enabled(ctx); + /* Break the TB to be able to sync copied instructions + immediately */ + ctx->base.is_jmp = DISAS_STOP; + } else { + /* case NM_PREFE */ + check_eva(ctx); + check_cp0_enabled(ctx); + /* Treat as NOP. */ + } + break; + case NM_LHE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_ld(ctx, OPC_LHE, rt, rs, s); + break; + case NM_SHE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_st(ctx, OPC_SHE, rt, rs, s); + break; + case NM_LHUE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_ld(ctx, OPC_LHUE, rt, rs, s); + break; + case NM_CACHEE: + check_nms_dl_il_sl_tl_l2c(ctx); + gen_cache_operation(ctx, rt, rs, s); + break; + case NM_LWE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_ld(ctx, OPC_LWE, rt, rs, s); + break; + case NM_SWE: + check_eva(ctx); + check_cp0_enabled(ctx); + gen_st(ctx, OPC_SWE, rt, rs, s); + break; + case NM_P_LLE: + switch (extract32(ctx->opcode, 2, 2)) { + case NM_LLE: + check_xnp(ctx); + check_eva(ctx); + check_cp0_enabled(ctx); + gen_ld(ctx, OPC_LLE, rt, rs, s); + break; + case NM_LLWPE: + check_xnp(ctx); + check_eva(ctx); + check_cp0_enabled(ctx); + gen_llwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5)); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_SCE: + switch (extract32(ctx->opcode, 2, 2)) { + case NM_SCE: + check_xnp(ctx); + check_eva(ctx); + check_cp0_enabled(ctx); + gen_st_cond(ctx, rt, rs, s, MO_TESL, true); + break; + case NM_SCWPE: + check_xnp(ctx); + check_eva(ctx); + check_cp0_enabled(ctx); + gen_scwp(ctx, rs, 0, rt, extract32(ctx->opcode, 3, 5), + true); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + } + break; + case NM_P_LS_WM: + case NM_P_LS_UAWM: + check_nms(ctx); + { + int count = extract32(ctx->opcode, 12, 3); + int counter = 0; + + offset = sextract32(ctx->opcode, 15, 1) << 8 | + extract32(ctx->opcode, 0, 8); + TCGv va = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGMemOp memop = (extract32(ctx->opcode, 8, 3)) == + NM_P_LS_UAWM ? MO_UNALN : 0; + + count = (count == 0) ? 8 : count; + while (counter != count) { + int this_rt = ((rt + counter) & 0x1f) | (rt & 0x10); + int this_offset = offset + (counter << 2); + + gen_base_offset_addr(ctx, va, rs, this_offset); + + switch (extract32(ctx->opcode, 11, 1)) { + case NM_LWM: + tcg_gen_qemu_ld_tl(t1, va, ctx->mem_idx, + memop | MO_TESL); + gen_store_gpr(t1, this_rt); + if ((this_rt == rs) && + (counter != (count - 1))) { + /* UNPREDICTABLE */ + } + break; + case NM_SWM: + this_rt = (rt == 0) ? 0 : this_rt; + gen_load_gpr(t1, this_rt); + tcg_gen_qemu_st_tl(t1, va, ctx->mem_idx, + memop | MO_TEUL); + break; + } + counter++; + } + tcg_temp_free(va); + tcg_temp_free(t1); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_MOVE_BALC: + check_nms(ctx); + { + TCGv t0 = tcg_temp_new(); + int32_t s = sextract32(ctx->opcode, 0, 1) << 21 | + extract32(ctx->opcode, 1, 20) << 1; + rd = (extract32(ctx->opcode, 24, 1)) == 0 ? 4 : 5; + rt = decode_gpr_gpr4_zero(extract32(ctx->opcode, 25, 1) << 3 | + extract32(ctx->opcode, 21, 3)); + gen_load_gpr(t0, rt); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); + tcg_temp_free(t0); + } + break; + case NM_P_BAL: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 25 | + extract32(ctx->opcode, 1, 24) << 1; + + if ((extract32(ctx->opcode, 25, 1)) == 0) { + /* BC */ + gen_compute_branch_nm(ctx, OPC_BEQ, 4, 0, 0, s); + } else { + /* BALC */ + gen_compute_branch_nm(ctx, OPC_BGEZAL, 4, 0, 0, s); + } + } + break; + case NM_P_J: + switch (extract32(ctx->opcode, 12, 4)) { + case NM_JALRC: + case NM_JALRC_HB: + gen_compute_branch_nm(ctx, OPC_JALR, 4, rs, rt, 0); + break; + case NM_P_BALRSC: + gen_compute_nanomips_pbalrsc_branch(ctx, rs, rt); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P_BR1: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 14 | + extract32(ctx->opcode, 1, 13) << 1; + switch (extract32(ctx->opcode, 14, 2)) { + case NM_BEQC: + check_nms(ctx); + gen_compute_branch_nm(ctx, OPC_BEQ, 4, rs, rt, s); + break; + case NM_P_BR3A: + s = sextract32(ctx->opcode, 0, 1) << 14 | + extract32(ctx->opcode, 1, 13) << 1; + check_cp1_enabled(ctx); + switch (extract32(ctx->opcode, 16, 5)) { + case NM_BC1EQZC: + gen_compute_branch_cp1_nm(ctx, OPC_BC1EQZ, rt, s); + break; + case NM_BC1NEZC: + gen_compute_branch_cp1_nm(ctx, OPC_BC1NEZ, rt, s); + break; + case NM_BPOSGE32C: + check_dsp_r3(ctx); + { + int32_t imm = extract32(ctx->opcode, 1, 13) | + extract32(ctx->opcode, 0, 1) << 13; + + gen_compute_branch_nm(ctx, OPC_BPOSGE32, 4, -1, -2, + imm); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_BGEC: + if (rs == rt) { + gen_compute_compact_branch_nm(ctx, OPC_BC, rs, rt, s); + } else { + gen_compute_compact_branch_nm(ctx, OPC_BGEC, rs, rt, s); + } + break; + case NM_BGEUC: + if (rs == rt || rt == 0) { + gen_compute_compact_branch_nm(ctx, OPC_BC, 0, 0, s); + } else if (rs == 0) { + gen_compute_compact_branch_nm(ctx, OPC_BEQZC, rt, 0, s); + } else { + gen_compute_compact_branch_nm(ctx, OPC_BGEUC, rs, rt, s); + } + break; + } + } + break; + case NM_P_BR2: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 14 | + extract32(ctx->opcode, 1, 13) << 1; + switch (extract32(ctx->opcode, 14, 2)) { + case NM_BNEC: + check_nms(ctx); + gen_compute_branch_nm(ctx, OPC_BNE, 4, rs, rt, s); + break; + case NM_BLTC: + if (rs != 0 && rt != 0 && rs == rt) { + /* NOP */ + ctx->hflags |= MIPS_HFLAG_FBNSLOT; + } else { + gen_compute_compact_branch_nm(ctx, OPC_BLTC, rs, rt, s); + } + break; + case NM_BLTUC: + if (rs == 0 || rs == rt) { + /* NOP */ + ctx->hflags |= MIPS_HFLAG_FBNSLOT; + } else { + gen_compute_compact_branch_nm(ctx, OPC_BLTUC, rs, rt, s); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P_BRI: + { + int32_t s = sextract32(ctx->opcode, 0, 1) << 11 | + extract32(ctx->opcode, 1, 10) << 1; + uint32_t u = extract32(ctx->opcode, 11, 7); + + gen_compute_imm_branch(ctx, extract32(ctx->opcode, 18, 3), + rt, u, s); + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + return 4; +} + +static int decode_nanomips_opc(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t op; + int rt = decode_gpr_gpr3(NANOMIPS_EXTRACT_RT3(ctx->opcode)); + int rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS3(ctx->opcode)); + int rd = decode_gpr_gpr3(NANOMIPS_EXTRACT_RD3(ctx->opcode)); + int offset; + int imm; + + /* make sure instructions are on a halfword boundary */ + if (ctx->base.pc_next & 0x1) { + TCGv tmp = tcg_const_tl(ctx->base.pc_next); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPUMIPSState, CP0_BadVAddr)); + tcg_temp_free(tmp); + generate_exception_end(ctx, EXCP_AdEL); + return 2; + } + + op = extract32(ctx->opcode, 10, 6); + switch (op) { + case NM_P16_MV: + rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); + if (rt != 0) { + /* MOVE */ + rs = NANOMIPS_EXTRACT_RS5(ctx->opcode); + gen_arith(ctx, OPC_ADDU, rt, rs, 0); + } else { + /* P16.RI */ + switch (extract32(ctx->opcode, 3, 2)) { + case NM_P16_SYSCALL: + if (extract32(ctx->opcode, 2, 1) == 0) { + generate_exception_end(ctx, EXCP_SYSCALL); + } else { + generate_exception_end(ctx, EXCP_RI); + } + break; + case NM_BREAK16: + generate_exception_end(ctx, EXCP_BREAK); + break; + case NM_SDBBP16: + if (is_uhi(extract32(ctx->opcode, 0, 3))) { + gen_helper_do_semihosting(cpu_env); + } else { + if (ctx->hflags & MIPS_HFLAG_SBRI) { + generate_exception_end(ctx, EXCP_RI); + } else { + generate_exception_end(ctx, EXCP_DBp); + } + } + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + } + break; + case NM_P16_SHIFT: + { + int shift = extract32(ctx->opcode, 0, 3); + uint32_t opc = 0; + shift = (shift == 0) ? 8 : shift; + + switch (extract32(ctx->opcode, 3, 1)) { + case NM_SLL16: + opc = OPC_SLL; + break; + case NM_SRL16: + opc = OPC_SRL; + break; + } + gen_shift_imm(ctx, opc, rt, rs, shift); + } + break; + case NM_P16C: + switch (ctx->opcode & 1) { + case NM_POOL16C_0: + gen_pool16c_nanomips_insn(ctx); + break; + case NM_LWXS16: + gen_ldxs(ctx, rt, rs, rd); + break; + } + break; + case NM_P16_A1: + switch (extract32(ctx->opcode, 6, 1)) { + case NM_ADDIUR1SP: + imm = extract32(ctx->opcode, 0, 6) << 2; + gen_arith_imm(ctx, OPC_ADDIU, rt, 29, imm); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P16_A2: + switch (extract32(ctx->opcode, 3, 1)) { + case NM_ADDIUR2: + imm = extract32(ctx->opcode, 0, 3) << 2; + gen_arith_imm(ctx, OPC_ADDIU, rt, rs, imm); + break; + case NM_P_ADDIURS5: + rt = extract32(ctx->opcode, 5, 5); + if (rt != 0) { + /* imm = sign_extend(s[3] . s[2:0] , from_nbits = 4) */ + imm = (sextract32(ctx->opcode, 4, 1) << 3) | + (extract32(ctx->opcode, 0, 3)); + gen_arith_imm(ctx, OPC_ADDIU, rt, rt, imm); + } + break; + } + break; + case NM_P16_ADDU: + switch (ctx->opcode & 0x1) { + case NM_ADDU16: + gen_arith(ctx, OPC_ADDU, rd, rs, rt); + break; + case NM_SUBU16: + gen_arith(ctx, OPC_SUBU, rd, rs, rt); + break; + } + break; + case NM_P16_4X4: + rt = (extract32(ctx->opcode, 9, 1) << 3) | + extract32(ctx->opcode, 5, 3); + rs = (extract32(ctx->opcode, 4, 1) << 3) | + extract32(ctx->opcode, 0, 3); + rt = decode_gpr_gpr4(rt); + rs = decode_gpr_gpr4(rs); + switch ((extract32(ctx->opcode, 7, 2) & 0x2) | + (extract32(ctx->opcode, 3, 1))) { + case NM_ADDU4X4: + check_nms(ctx); + gen_arith(ctx, OPC_ADDU, rt, rs, rt); + break; + case NM_MUL4X4: + check_nms(ctx); + gen_r6_muldiv(ctx, R6_OPC_MUL, rt, rs, rt); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_LI16: + { + int imm = extract32(ctx->opcode, 0, 7); + imm = (imm == 0x7f ? -1 : imm); + if (rt != 0) { + tcg_gen_movi_tl(cpu_gpr[rt], imm); + } + } + break; + case NM_ANDI16: + { + uint32_t u = extract32(ctx->opcode, 0, 4); + u = (u == 12) ? 0xff : + (u == 13) ? 0xffff : u; + gen_logic_imm(ctx, OPC_ANDI, rt, rs, u); + } + break; + case NM_P16_LB: + offset = extract32(ctx->opcode, 0, 2); + switch (extract32(ctx->opcode, 2, 2)) { + case NM_LB16: + gen_ld(ctx, OPC_LB, rt, rs, offset); + break; + case NM_SB16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RT3(ctx->opcode)); + gen_st(ctx, OPC_SB, rt, rs, offset); + break; + case NM_LBU16: + gen_ld(ctx, OPC_LBU, rt, rs, offset); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_P16_LH: + offset = extract32(ctx->opcode, 1, 2) << 1; + switch ((extract32(ctx->opcode, 3, 1) << 1) | (ctx->opcode & 1)) { + case NM_LH16: + gen_ld(ctx, OPC_LH, rt, rs, offset); + break; + case NM_SH16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RT3(ctx->opcode)); + gen_st(ctx, OPC_SH, rt, rs, offset); + break; + case NM_LHU16: + gen_ld(ctx, OPC_LHU, rt, rs, offset); + break; + default: + generate_exception_end(ctx, EXCP_RI); + break; + } + break; + case NM_LW16: + offset = extract32(ctx->opcode, 0, 4) << 2; + gen_ld(ctx, OPC_LW, rt, rs, offset); + break; + case NM_LWSP16: + rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); + offset = extract32(ctx->opcode, 0, 5) << 2; + gen_ld(ctx, OPC_LW, rt, 29, offset); + break; + case NM_LW4X4: + check_nms(ctx); + rt = (extract32(ctx->opcode, 9, 1) << 3) | + extract32(ctx->opcode, 5, 3); + rs = (extract32(ctx->opcode, 4, 1) << 3) | + extract32(ctx->opcode, 0, 3); + offset = (extract32(ctx->opcode, 3, 1) << 3) | + (extract32(ctx->opcode, 8, 1) << 2); + rt = decode_gpr_gpr4(rt); + rs = decode_gpr_gpr4(rs); + gen_ld(ctx, OPC_LW, rt, rs, offset); + break; + case NM_SW4X4: + check_nms(ctx); + rt = (extract32(ctx->opcode, 9, 1) << 3) | + extract32(ctx->opcode, 5, 3); + rs = (extract32(ctx->opcode, 4, 1) << 3) | + extract32(ctx->opcode, 0, 3); + offset = (extract32(ctx->opcode, 3, 1) << 3) | + (extract32(ctx->opcode, 8, 1) << 2); + rt = decode_gpr_gpr4_zero(rt); + rs = decode_gpr_gpr4(rs); + gen_st(ctx, OPC_SW, rt, rs, offset); + break; + case NM_LWGP16: + offset = extract32(ctx->opcode, 0, 7) << 2; + gen_ld(ctx, OPC_LW, rt, 28, offset); + break; + case NM_SWSP16: + rt = NANOMIPS_EXTRACT_RD5(ctx->opcode); + offset = extract32(ctx->opcode, 0, 5) << 2; + gen_st(ctx, OPC_SW, rt, 29, offset); + break; + case NM_SW16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RT3(ctx->opcode)); + rs = decode_gpr_gpr3(NANOMIPS_EXTRACT_RS3(ctx->opcode)); + offset = extract32(ctx->opcode, 0, 4) << 2; + gen_st(ctx, OPC_SW, rt, rs, offset); + break; + case NM_SWGP16: + rt = decode_gpr_gpr3_src_store( + NANOMIPS_EXTRACT_RT3(ctx->opcode)); + offset = extract32(ctx->opcode, 0, 7) << 2; + gen_st(ctx, OPC_SW, rt, 28, offset); + break; + case NM_BC16: + gen_compute_branch_nm(ctx, OPC_BEQ, 2, 0, 0, + (sextract32(ctx->opcode, 0, 1) << 10) | + (extract32(ctx->opcode, 1, 9) << 1)); + break; + case NM_BALC16: + gen_compute_branch_nm(ctx, OPC_BGEZAL, 2, 0, 0, + (sextract32(ctx->opcode, 0, 1) << 10) | + (extract32(ctx->opcode, 1, 9) << 1)); + break; + case NM_BEQZC16: + gen_compute_branch_nm(ctx, OPC_BEQ, 2, rt, 0, + (sextract32(ctx->opcode, 0, 1) << 7) | + (extract32(ctx->opcode, 1, 6) << 1)); + break; + case NM_BNEZC16: + gen_compute_branch_nm(ctx, OPC_BNE, 2, rt, 0, + (sextract32(ctx->opcode, 0, 1) << 7) | + (extract32(ctx->opcode, 1, 6) << 1)); + break; + case NM_P16_BR: + switch (ctx->opcode & 0xf) { + case 0: + /* P16.JRC */ + switch (extract32(ctx->opcode, 4, 1)) { + case NM_JRC: + gen_compute_branch_nm(ctx, OPC_JR, 2, + extract32(ctx->opcode, 5, 5), 0, 0); + break; + case NM_JALRC16: + gen_compute_branch_nm(ctx, OPC_JALR, 2, + extract32(ctx->opcode, 5, 5), 31, 0); + break; + } + break; + default: + { + /* P16.BRI */ + uint32_t opc = extract32(ctx->opcode, 4, 3) < + extract32(ctx->opcode, 7, 3) ? OPC_BEQ : OPC_BNE; + gen_compute_branch_nm(ctx, opc, 2, rs, rt, + extract32(ctx->opcode, 0, 4) << 1); + } + break; + } + break; + case NM_P16_SR: + { + int count = extract32(ctx->opcode, 0, 4); + int u = extract32(ctx->opcode, 4, 4) << 4; + + rt = 30 + extract32(ctx->opcode, 9, 1); + switch (extract32(ctx->opcode, 8, 1)) { + case NM_SAVE16: + gen_save(ctx, rt, count, 0, u); + break; + case NM_RESTORE_JRC16: + gen_restore(ctx, rt, count, 0, u); + gen_compute_branch_nm(ctx, OPC_JR, 2, 31, 0, 0); + break; + } + } + break; + case NM_MOVEP: + case NM_MOVEPREV: + check_nms(ctx); + { + static const int gpr2reg1[] = {4, 5, 6, 7}; + static const int gpr2reg2[] = {5, 6, 7, 8}; + int re; + int rd2 = extract32(ctx->opcode, 3, 1) << 1 | + extract32(ctx->opcode, 8, 1); + int r1 = gpr2reg1[rd2]; + int r2 = gpr2reg2[rd2]; + int r3 = extract32(ctx->opcode, 4, 1) << 3 | + extract32(ctx->opcode, 0, 3); + int r4 = extract32(ctx->opcode, 9, 1) << 3 | + extract32(ctx->opcode, 5, 3); + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + if (op == NM_MOVEP) { + rd = r1; + re = r2; + rs = decode_gpr_gpr4_zero(r3); + rt = decode_gpr_gpr4_zero(r4); + } else { + rd = decode_gpr_gpr4(r3); + re = decode_gpr_gpr4(r4); + rs = r1; + rt = r2; + } + gen_load_gpr(t0, rs); + gen_load_gpr(t1, rt); + tcg_gen_mov_tl(cpu_gpr[rd], t0); + tcg_gen_mov_tl(cpu_gpr[re], t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + } + break; + default: + return decode_nanomips_32_48_opc(env, ctx); + } + + return 2; +} + + /* SmartMIPS extension to MIPS32 */ #if defined(TARGET_MIPS64) @@ -15597,7 +22261,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, switch (op1) { /* OPC_MULT_G_2E is equal OPC_ADDUH_QB_DSP */ case OPC_MULT_G_2E: - check_dspr2(ctx); + check_dsp_r2(ctx); switch (op2) { case OPC_ADDUH_QB: gen_helper_adduh_qb(cpu_gpr[ret], v1_t, v2_t); @@ -15640,7 +22304,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, case OPC_ABSQ_S_PH_DSP: switch (op2) { case OPC_ABSQ_S_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_absq_s_qb(cpu_gpr[ret], v2_t, cpu_env); break; case OPC_ABSQ_S_PH: @@ -15719,11 +22383,11 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_addu_s_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_ADDU_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_addu_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_ADDU_S_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_addu_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_SUBQ_PH: @@ -15747,11 +22411,11 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_subu_s_qb(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_SUBU_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_subu_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_SUBU_S_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_subu_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_ADDSC: @@ -15775,7 +22439,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, case OPC_CMPU_EQ_QB_DSP: switch (op2) { case OPC_PRECR_QB_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_precr_qb_ph(cpu_gpr[ret], v1_t, v2_t); break; case OPC_PRECRQ_QB_PH: @@ -15783,7 +22447,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_precrq_qb_ph(cpu_gpr[ret], v1_t, v2_t); break; case OPC_PRECR_SRA_PH_W: - check_dspr2(ctx); + check_dsp_r2(ctx); { TCGv_i32 sa_t = tcg_const_i32(v2); gen_helper_precr_sra_ph_w(cpu_gpr[ret], sa_t, v1_t, @@ -15792,7 +22456,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; } case OPC_PRECR_SRA_R_PH_W: - check_dspr2(ctx); + check_dsp_r2(ctx); { TCGv_i32 sa_t = tcg_const_i32(v2); gen_helper_precr_sra_r_ph_w(cpu_gpr[ret], sa_t, v1_t, @@ -15874,7 +22538,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_preceu_qh_obra(cpu_gpr[ret], v2_t); break; case OPC_ABSQ_S_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_absq_s_ob(cpu_gpr[ret], v2_t, cpu_env); break; case OPC_ABSQ_S_PW: @@ -15918,19 +22582,19 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_subu_s_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_SUBU_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_subu_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_SUBU_S_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_subu_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_SUBUH_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_subuh_ob(cpu_gpr[ret], v1_t, v2_t); break; case OPC_SUBUH_R_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_subuh_r_ob(cpu_gpr[ret], v1_t, v2_t); break; case OPC_ADDQ_PW: @@ -15958,19 +22622,19 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_addu_s_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_ADDU_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_addu_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_ADDU_S_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_addu_s_qh(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_ADDUH_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_adduh_ob(cpu_gpr[ret], v1_t, v2_t); break; case OPC_ADDUH_R_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_adduh_r_ob(cpu_gpr[ret], v1_t, v2_t); break; } @@ -15978,11 +22642,11 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, case OPC_CMPU_EQ_OB_DSP: switch (op2) { case OPC_PRECR_OB_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_precr_ob_qh(cpu_gpr[ret], v1_t, v2_t); break; case OPC_PRECR_SRA_QH_PW: - check_dspr2(ctx); + check_dsp_r2(ctx); { TCGv_i32 ret_t = tcg_const_i32(ret); gen_helper_precr_sra_qh_pw(v2_t, v1_t, v2_t, ret_t); @@ -15990,7 +22654,7 @@ static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, break; } case OPC_PRECR_SRA_R_QH_PW: - check_dspr2(ctx); + check_dsp_r2(ctx); { TCGv_i32 sa_v = tcg_const_i32(ret); gen_helper_precr_sra_r_qh_pw(v2_t, v1_t, v2_t, sa_v); @@ -16093,27 +22757,27 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, gen_helper_shrl_qb(cpu_gpr[ret], v1_t, v2_t); break; case OPC_SHRL_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shrl_ph(cpu_gpr[ret], t0, v2_t); break; case OPC_SHRLV_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shrl_ph(cpu_gpr[ret], v1_t, v2_t); break; case OPC_SHRA_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_qb(cpu_gpr[ret], t0, v2_t); break; case OPC_SHRA_R_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_r_qb(cpu_gpr[ret], t0, v2_t); break; case OPC_SHRAV_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_qb(cpu_gpr[ret], v1_t, v2_t); break; case OPC_SHRAV_R_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_r_qb(cpu_gpr[ret], v1_t, v2_t); break; case OPC_SHRA_PH: @@ -16192,19 +22856,19 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, gen_helper_shll_s_qh(cpu_gpr[ret], v2_t, v1_t, cpu_env); break; case OPC_SHRA_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_ob(cpu_gpr[ret], v2_t, t0); break; case OPC_SHRAV_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_ob(cpu_gpr[ret], v2_t, v1_t); break; case OPC_SHRA_R_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_r_ob(cpu_gpr[ret], v2_t, t0); break; case OPC_SHRAV_R_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shra_r_ob(cpu_gpr[ret], v2_t, v1_t); break; case OPC_SHRA_PW: @@ -16248,11 +22912,11 @@ static void gen_mipsdsp_shift(DisasContext *ctx, uint32_t opc, gen_helper_shrl_ob(cpu_gpr[ret], v2_t, v1_t); break; case OPC_SHRL_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shrl_qh(cpu_gpr[ret], v2_t, t0); break; case OPC_SHRLV_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_shrl_qh(cpu_gpr[ret], v2_t, v1_t); break; default: /* Invalid */ @@ -16293,7 +22957,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have * the same mask and op1. */ case OPC_MULT_G_2E: - check_dspr2(ctx); + check_dsp_r2(ctx); switch (op2) { case OPC_MUL_PH: gen_helper_mul_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); @@ -16328,11 +22992,11 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_dpsu_h_qbr(t0, v1_t, v2_t, cpu_env); break; case OPC_DPA_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpa_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPAX_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpax_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPAQ_S_W_PH: @@ -16340,19 +23004,19 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_dpaq_s_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPAQX_S_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpaqx_s_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPAQX_SA_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpaqx_sa_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPS_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dps_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPSX_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpsx_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPSQ_S_W_PH: @@ -16360,11 +23024,11 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_dpsq_s_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPSQX_S_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpsqx_s_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_DPSQX_SA_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpsqx_sa_w_ph(t0, v1_t, v2_t, cpu_env); break; case OPC_MULSAQ_S_W_PH: @@ -16396,7 +23060,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_maq_sa_w_phr(t0, v1_t, v2_t, cpu_env); break; case OPC_MULSA_W_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_mulsa_w_ph(t0, v1_t, v2_t, cpu_env); break; } @@ -16425,7 +23089,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_dmsubu(v1_t, v2_t, t0, cpu_env); break; case OPC_DPA_W_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dpa_w_qh(v1_t, v2_t, t0, cpu_env); break; case OPC_DPAQ_S_W_QH: @@ -16445,7 +23109,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_dpau_h_obr(v1_t, v2_t, t0, cpu_env); break; case OPC_DPS_W_QH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_dps_w_qh(v1_t, v2_t, t0, cpu_env); break; case OPC_DPSQ_S_W_QH: @@ -16539,7 +23203,7 @@ static void gen_mipsdsp_multiply(DisasContext *ctx, uint32_t op1, uint32_t op2, gen_helper_muleq_s_w_phr(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_MULQ_S_PH: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_mulq_s_ph(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; } @@ -16763,7 +23427,7 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, gen_helper_cmpgu_le_qb(cpu_gpr[ret], v1_t, v2_t); break; case OPC_CMPGDU_EQ_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_cmpgu_eq_qb(t1, v1_t, v2_t); tcg_gen_mov_tl(cpu_gpr[ret], t1); tcg_gen_andi_tl(cpu_dspctrl, cpu_dspctrl, 0xF0FFFFFF); @@ -16771,7 +23435,7 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, tcg_gen_or_tl(cpu_dspctrl, cpu_dspctrl, t1); break; case OPC_CMPGDU_LT_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_cmpgu_lt_qb(t1, v1_t, v2_t); tcg_gen_mov_tl(cpu_gpr[ret], t1); tcg_gen_andi_tl(cpu_dspctrl, cpu_dspctrl, 0xF0FFFFFF); @@ -16779,7 +23443,7 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, tcg_gen_or_tl(cpu_dspctrl, cpu_dspctrl, t1); break; case OPC_CMPGDU_LE_QB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_cmpgu_le_qb(t1, v1_t, v2_t); tcg_gen_mov_tl(cpu_gpr[ret], t1); tcg_gen_andi_tl(cpu_dspctrl, cpu_dspctrl, 0xF0FFFFFF); @@ -16840,15 +23504,15 @@ static void gen_mipsdsp_add_cmp_pick(DisasContext *ctx, gen_helper_cmp_le_qh(v1_t, v2_t, cpu_env); break; case OPC_CMPGDU_EQ_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_cmpgdu_eq_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_CMPGDU_LT_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_cmpgdu_lt_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_CMPGDU_LE_OB: - check_dspr2(ctx); + check_dsp_r2(ctx); gen_helper_cmpgdu_le_ob(cpu_gpr[ret], v1_t, v2_t, cpu_env); break; case OPC_CMPGU_EQ_OB: @@ -16906,7 +23570,7 @@ static void gen_mipsdsp_append(CPUMIPSState *env, DisasContext *ctx, { TCGv t0; - check_dspr2(ctx); + check_dsp_r2(ctx); if (rt == 0) { /* Treat as NOP. */ @@ -17231,7 +23895,10 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) case OPC_LSA: gen_lsa(ctx, op1, rd, rs, rt, extract32(ctx->opcode, 6, 2)); break; - case OPC_MULT ... OPC_DIVU: + case OPC_MULT: + case OPC_MULTU: + case OPC_DIV: + case OPC_DIVU: op2 = MASK_R6_MULDIV(ctx->opcode); switch (op2) { case R6_OPC_MUL: @@ -17291,7 +23958,11 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) generate_exception_end(ctx, EXCP_RI); } break; - case OPC_DMULT ... OPC_DDIVU: + case OPC_DMULT: + case OPC_DMULTU: + case OPC_DDIV: + case OPC_DDIVU: + op2 = MASK_R6_MULDIV(ctx->opcode); switch (op2) { case R6_OPC_DMUL: @@ -17319,6 +23990,53 @@ static void decode_opc_special_r6(CPUMIPSState *env, DisasContext *ctx) } } +static void decode_opc_special_tx79(CPUMIPSState *env, DisasContext *ctx) +{ + int rs = extract32(ctx->opcode, 21, 5); + int rt = extract32(ctx->opcode, 16, 5); + int rd = extract32(ctx->opcode, 11, 5); + uint32_t op1 = MASK_SPECIAL(ctx->opcode); + + switch (op1) { + case OPC_MOVN: /* Conditional move */ + case OPC_MOVZ: + gen_cond_move(ctx, op1, rd, rs, rt); + break; + case OPC_MFHI: /* Move from HI/LO */ + case OPC_MFLO: + gen_HILO(ctx, op1, 0, rd); + break; + case OPC_MTHI: + case OPC_MTLO: /* Move to HI/LO */ + gen_HILO(ctx, op1, 0, rs); + break; + case OPC_MULT: + case OPC_MULTU: + gen_mul_txx9(ctx, op1, rd, rs, rt); + break; + case OPC_DIV: + case OPC_DIVU: + gen_muldiv(ctx, op1, 0, rs, rt); + break; +#if defined(TARGET_MIPS64) + case OPC_DMULT: + case OPC_DMULTU: + case OPC_DDIV: + case OPC_DDIVU: + check_insn_opc_user_only(ctx, INSN_R5900); + gen_muldiv(ctx, op1, 0, rs, rt); + break; +#endif + case OPC_JR: + gen_compute_branch(ctx, op1, 4, rs, 0, 0, 4); + break; + default: /* Invalid */ + MIPS_INVAL("special_tx79"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) { int rs, rt, rd, sa; @@ -17370,7 +24088,10 @@ static void decode_opc_special_legacy(CPUMIPSState *env, DisasContext *ctx) gen_muldiv(ctx, op1, 0, rs, rt); break; #if defined(TARGET_MIPS64) - case OPC_DMULT ... OPC_DDIVU: + case OPC_DMULT: + case OPC_DMULTU: + case OPC_DDIV: + case OPC_DDIVU: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_muldiv(ctx, op1, 0, rs, rt); @@ -17437,7 +24158,10 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) break; } break; - case OPC_ADD ... OPC_SUBU: + case OPC_ADD: + case OPC_ADDU: + case OPC_SUB: + case OPC_SUBU: gen_arith(ctx, op1, rd, rs, rt); break; case OPC_SLLV: /* Shifts */ @@ -17473,7 +24197,11 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) case OPC_JALR: gen_compute_branch(ctx, op1, 4, rs, rd, sa, 4); break; - case OPC_TGE ... OPC_TEQ: /* Traps */ + case OPC_TGE: /* Traps */ + case OPC_TGEU: + case OPC_TLT: + case OPC_TLTU: + case OPC_TEQ: case OPC_TNE: check_insn(ctx, ISA_MIPS2); gen_trap(ctx, op1, rs, rt, -1); @@ -17549,7 +24277,10 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) break; } break; - case OPC_DADD ... OPC_DSUBU: + case OPC_DADD: + case OPC_DADDU: + case OPC_DSUB: + case OPC_DSUBU: check_insn(ctx, ISA_MIPS3); check_mips_64(ctx); gen_arith(ctx, op1, rd, rs, rt); @@ -17588,12 +24319,2269 @@ static void decode_opc_special(CPUMIPSState *env, DisasContext *ctx) default: if (ctx->insn_flags & ISA_MIPS32R6) { decode_opc_special_r6(env, ctx); + } else if (ctx->insn_flags & INSN_R5900) { + decode_opc_special_tx79(env, ctx); } else { decode_opc_special_legacy(env, ctx); } } } + +#if defined(TARGET_MIPS64) + +/* + * + * MMI (MultiMedia Interface) ASE instructions + * =========================================== + */ + +/* + * MMI instructions category: data communication + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * PCPYH PEXCH PEXTLB PINTH PPACB PEXT5 PREVH + * PCPYLD PEXCW PEXTLH PINTEH PPACH PPAC5 PROT3W + * PCPYUD PEXEH PEXTLW PPACW + * PEXEW PEXTUB + * PEXTUH + * PEXTUW + */ + +#endif + + +#if !defined(TARGET_MIPS64) + +/* MXU accumulate add/subtract 1-bit pattern 'aptn1' */ +#define MXU_APTN1_A 0 +#define MXU_APTN1_S 1 + +/* MXU accumulate add/subtract 2-bit pattern 'aptn2' */ +#define MXU_APTN2_AA 0 +#define MXU_APTN2_AS 1 +#define MXU_APTN2_SA 2 +#define MXU_APTN2_SS 3 + +/* MXU execute add/subtract 2-bit pattern 'eptn2' */ +#define MXU_EPTN2_AA 0 +#define MXU_EPTN2_AS 1 +#define MXU_EPTN2_SA 2 +#define MXU_EPTN2_SS 3 + +/* MXU operand getting pattern 'optn2' */ +#define MXU_OPTN2_PTN0 0 +#define MXU_OPTN2_PTN1 1 +#define MXU_OPTN2_PTN2 2 +#define MXU_OPTN2_PTN3 3 +/* alternative naming scheme for 'optn2' */ +#define MXU_OPTN2_WW 0 +#define MXU_OPTN2_LW 1 +#define MXU_OPTN2_HW 2 +#define MXU_OPTN2_XW 3 + +/* MXU operand getting pattern 'optn3' */ +#define MXU_OPTN3_PTN0 0 +#define MXU_OPTN3_PTN1 1 +#define MXU_OPTN3_PTN2 2 +#define MXU_OPTN3_PTN3 3 +#define MXU_OPTN3_PTN4 4 +#define MXU_OPTN3_PTN5 5 +#define MXU_OPTN3_PTN6 6 +#define MXU_OPTN3_PTN7 7 + + +/* + * S32I2M XRa, rb - Register move from GRF to XRF + */ +static void gen_mxu_s32i2m(DisasContext *ctx) +{ + TCGv t0; + uint32_t XRa, Rb; + + t0 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 5); + Rb = extract32(ctx->opcode, 16, 5); + + gen_load_gpr(t0, Rb); + if (XRa <= 15) { + gen_store_mxu_gpr(t0, XRa); + } else if (XRa == 16) { + gen_store_mxu_cr(t0); + } + + tcg_temp_free(t0); +} + +/* + * S32M2I XRa, rb - Register move from XRF to GRF + */ +static void gen_mxu_s32m2i(DisasContext *ctx) +{ + TCGv t0; + uint32_t XRa, Rb; + + t0 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 5); + Rb = extract32(ctx->opcode, 16, 5); + + if (XRa <= 15) { + gen_load_mxu_gpr(t0, XRa); + } else if (XRa == 16) { + gen_load_mxu_cr(t0); + } + + gen_store_gpr(t0, Rb); + + tcg_temp_free(t0); +} + +/* + * S8LDD XRa, Rb, s8, optn3 - Load a byte from memory to XRF + */ +static void gen_mxu_s8ldd(DisasContext *ctx) +{ + TCGv t0, t1; + uint32_t XRa, Rb, s8, optn3; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + s8 = extract32(ctx->opcode, 10, 8); + optn3 = extract32(ctx->opcode, 18, 3); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + tcg_gen_addi_tl(t0, t0, (int8_t)s8); + + switch (optn3) { + /* XRa[7:0] = tmp8 */ + case MXU_OPTN3_PTN0: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + gen_load_mxu_gpr(t0, XRa); + tcg_gen_deposit_tl(t0, t0, t1, 0, 8); + break; + /* XRa[15:8] = tmp8 */ + case MXU_OPTN3_PTN1: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + gen_load_mxu_gpr(t0, XRa); + tcg_gen_deposit_tl(t0, t0, t1, 8, 8); + break; + /* XRa[23:16] = tmp8 */ + case MXU_OPTN3_PTN2: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + gen_load_mxu_gpr(t0, XRa); + tcg_gen_deposit_tl(t0, t0, t1, 16, 8); + break; + /* XRa[31:24] = tmp8 */ + case MXU_OPTN3_PTN3: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + gen_load_mxu_gpr(t0, XRa); + tcg_gen_deposit_tl(t0, t0, t1, 24, 8); + break; + /* XRa = {8'b0, tmp8, 8'b0, tmp8} */ + case MXU_OPTN3_PTN4: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + tcg_gen_deposit_tl(t0, t1, t1, 16, 16); + break; + /* XRa = {tmp8, 8'b0, tmp8, 8'b0} */ + case MXU_OPTN3_PTN5: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + tcg_gen_shli_tl(t1, t1, 8); + tcg_gen_deposit_tl(t0, t1, t1, 16, 16); + break; + /* XRa = {{8{sign of tmp8}}, tmp8, {8{sign of tmp8}}, tmp8} */ + case MXU_OPTN3_PTN6: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_SB); + tcg_gen_mov_tl(t0, t1); + tcg_gen_andi_tl(t0, t0, 0xFF00FFFF); + tcg_gen_shli_tl(t1, t1, 16); + tcg_gen_or_tl(t0, t0, t1); + break; + /* XRa = {tmp8, tmp8, tmp8, tmp8} */ + case MXU_OPTN3_PTN7: + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, MO_UB); + tcg_gen_deposit_tl(t1, t1, t1, 8, 8); + tcg_gen_deposit_tl(t0, t1, t1, 16, 16); + break; + } + + gen_store_mxu_gpr(t0, XRa); + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + +/* + * D16MUL XRa, XRb, XRc, XRd, optn2 - Signed 16 bit pattern multiplication + */ +static void gen_mxu_d16mul(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3; + uint32_t XRa, XRb, XRc, XRd, optn2; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + optn2 = extract32(ctx->opcode, 22, 2); + + gen_load_mxu_gpr(t1, XRb); + tcg_gen_sextract_tl(t0, t1, 0, 16); + tcg_gen_sextract_tl(t1, t1, 16, 16); + gen_load_mxu_gpr(t3, XRc); + tcg_gen_sextract_tl(t2, t3, 0, 16); + tcg_gen_sextract_tl(t3, t3, 16, 16); + + switch (optn2) { + case MXU_OPTN2_WW: /* XRB.H*XRC.H == lop, XRB.L*XRC.L == rop */ + tcg_gen_mul_tl(t3, t1, t3); + tcg_gen_mul_tl(t2, t0, t2); + break; + case MXU_OPTN2_LW: /* XRB.L*XRC.H == lop, XRB.L*XRC.L == rop */ + tcg_gen_mul_tl(t3, t0, t3); + tcg_gen_mul_tl(t2, t0, t2); + break; + case MXU_OPTN2_HW: /* XRB.H*XRC.H == lop, XRB.H*XRC.L == rop */ + tcg_gen_mul_tl(t3, t1, t3); + tcg_gen_mul_tl(t2, t1, t2); + break; + case MXU_OPTN2_XW: /* XRB.L*XRC.H == lop, XRB.H*XRC.L == rop */ + tcg_gen_mul_tl(t3, t0, t3); + tcg_gen_mul_tl(t2, t1, t2); + break; + } + gen_store_mxu_gpr(t3, XRa); + gen_store_mxu_gpr(t2, XRd); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_temp_free(t3); +} + +/* + * D16MAC XRa, XRb, XRc, XRd, aptn2, optn2 - Signed 16 bit pattern multiply + * and accumulate + */ +static void gen_mxu_d16mac(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3; + uint32_t XRa, XRb, XRc, XRd, optn2, aptn2; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + optn2 = extract32(ctx->opcode, 22, 2); + aptn2 = extract32(ctx->opcode, 24, 2); + + gen_load_mxu_gpr(t1, XRb); + tcg_gen_sextract_tl(t0, t1, 0, 16); + tcg_gen_sextract_tl(t1, t1, 16, 16); + + gen_load_mxu_gpr(t3, XRc); + tcg_gen_sextract_tl(t2, t3, 0, 16); + tcg_gen_sextract_tl(t3, t3, 16, 16); + + switch (optn2) { + case MXU_OPTN2_WW: /* XRB.H*XRC.H == lop, XRB.L*XRC.L == rop */ + tcg_gen_mul_tl(t3, t1, t3); + tcg_gen_mul_tl(t2, t0, t2); + break; + case MXU_OPTN2_LW: /* XRB.L*XRC.H == lop, XRB.L*XRC.L == rop */ + tcg_gen_mul_tl(t3, t0, t3); + tcg_gen_mul_tl(t2, t0, t2); + break; + case MXU_OPTN2_HW: /* XRB.H*XRC.H == lop, XRB.H*XRC.L == rop */ + tcg_gen_mul_tl(t3, t1, t3); + tcg_gen_mul_tl(t2, t1, t2); + break; + case MXU_OPTN2_XW: /* XRB.L*XRC.H == lop, XRB.H*XRC.L == rop */ + tcg_gen_mul_tl(t3, t0, t3); + tcg_gen_mul_tl(t2, t1, t2); + break; + } + gen_load_mxu_gpr(t0, XRa); + gen_load_mxu_gpr(t1, XRd); + + switch (aptn2) { + case MXU_APTN2_AA: + tcg_gen_add_tl(t3, t0, t3); + tcg_gen_add_tl(t2, t1, t2); + break; + case MXU_APTN2_AS: + tcg_gen_add_tl(t3, t0, t3); + tcg_gen_sub_tl(t2, t1, t2); + break; + case MXU_APTN2_SA: + tcg_gen_sub_tl(t3, t0, t3); + tcg_gen_add_tl(t2, t1, t2); + break; + case MXU_APTN2_SS: + tcg_gen_sub_tl(t3, t0, t3); + tcg_gen_sub_tl(t2, t1, t2); + break; + } + gen_store_mxu_gpr(t3, XRa); + gen_store_mxu_gpr(t2, XRd); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_temp_free(t3); +} + +/* + * Q8MUL XRa, XRb, XRc, XRd - Parallel unsigned 8 bit pattern multiply + * Q8MULSU XRa, XRb, XRc, XRd - Parallel signed 8 bit pattern multiply + */ +static void gen_mxu_q8mul_q8mulsu(DisasContext *ctx) +{ + TCGv t0, t1, t2, t3, t4, t5, t6, t7; + uint32_t XRa, XRb, XRc, XRd, sel; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + t2 = tcg_temp_new(); + t3 = tcg_temp_new(); + t4 = tcg_temp_new(); + t5 = tcg_temp_new(); + t6 = tcg_temp_new(); + t7 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRc = extract32(ctx->opcode, 14, 4); + XRd = extract32(ctx->opcode, 18, 4); + sel = extract32(ctx->opcode, 22, 2); + + gen_load_mxu_gpr(t3, XRb); + gen_load_mxu_gpr(t7, XRc); + + if (sel == 0x2) { + /* Q8MULSU */ + tcg_gen_ext8s_tl(t0, t3); + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_ext8s_tl(t1, t3); + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_ext8s_tl(t2, t3); + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_ext8s_tl(t3, t3); + } else { + /* Q8MUL */ + tcg_gen_ext8u_tl(t0, t3); + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_ext8u_tl(t1, t3); + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_ext8u_tl(t2, t3); + tcg_gen_shri_tl(t3, t3, 8); + tcg_gen_ext8u_tl(t3, t3); + } + + tcg_gen_ext8u_tl(t4, t7); + tcg_gen_shri_tl(t7, t7, 8); + tcg_gen_ext8u_tl(t5, t7); + tcg_gen_shri_tl(t7, t7, 8); + tcg_gen_ext8u_tl(t6, t7); + tcg_gen_shri_tl(t7, t7, 8); + tcg_gen_ext8u_tl(t7, t7); + + tcg_gen_mul_tl(t0, t0, t4); + tcg_gen_mul_tl(t1, t1, t5); + tcg_gen_mul_tl(t2, t2, t6); + tcg_gen_mul_tl(t3, t3, t7); + + tcg_gen_andi_tl(t0, t0, 0xFFFF); + tcg_gen_andi_tl(t1, t1, 0xFFFF); + tcg_gen_andi_tl(t2, t2, 0xFFFF); + tcg_gen_andi_tl(t3, t3, 0xFFFF); + + tcg_gen_shli_tl(t1, t1, 16); + tcg_gen_shli_tl(t3, t3, 16); + + tcg_gen_or_tl(t0, t0, t1); + tcg_gen_or_tl(t1, t2, t3); + + gen_store_mxu_gpr(t0, XRd); + gen_store_mxu_gpr(t1, XRa); + + tcg_temp_free(t0); + tcg_temp_free(t1); + tcg_temp_free(t2); + tcg_temp_free(t3); + tcg_temp_free(t4); + tcg_temp_free(t5); + tcg_temp_free(t6); + tcg_temp_free(t7); +} + +/* + * S32LDD XRa, Rb, S12 - Load a word from memory to XRF + * S32LDDR XRa, Rb, S12 - Load a word from memory to XRF, reversed byte seq. + */ +static void gen_mxu_s32ldd_s32lddr(DisasContext *ctx) +{ + TCGv t0, t1; + uint32_t XRa, Rb, s12, sel; + + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + XRa = extract32(ctx->opcode, 6, 4); + s12 = extract32(ctx->opcode, 10, 10); + sel = extract32(ctx->opcode, 20, 1); + Rb = extract32(ctx->opcode, 21, 5); + + gen_load_gpr(t0, Rb); + + tcg_gen_movi_tl(t1, s12); + tcg_gen_shli_tl(t1, t1, 2); + if (s12 & 0x200) { + tcg_gen_ori_tl(t1, t1, 0xFFFFF000); + } + tcg_gen_add_tl(t1, t0, t1); + tcg_gen_qemu_ld_tl(t1, t1, ctx->mem_idx, MO_SL); + + if (sel == 1) { + /* S32LDDR */ + tcg_gen_bswap32_tl(t1, t1); + } + gen_store_mxu_gpr(t1, XRa); + + tcg_temp_free(t0); + tcg_temp_free(t1); +} + + +/* + * MXU instruction category: logic + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * S32NOR S32AND S32OR S32XOR + */ + +/* + * S32NOR XRa, XRb, XRc + * Update XRa with the result of logical bitwise 'nor' operation + * applied to the content of XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_S32NOR(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to all 1s */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0xFFFFFFFF); + } else if (unlikely(XRb == 0)) { + /* XRb zero register -> just set destination to the negation of XRc */ + tcg_gen_not_i32(mxu_gpr[XRa - 1], mxu_gpr[XRc - 1]); + } else if (unlikely(XRc == 0)) { + /* XRa zero register -> just set destination to the negation of XRb */ + tcg_gen_not_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to the negation of XRb */ + tcg_gen_not_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + tcg_gen_nor_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], mxu_gpr[XRc - 1]); + } +} + +/* + * S32AND XRa, XRb, XRc + * Update XRa with the result of logical bitwise 'and' operation + * applied to the content of XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_S32AND(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) || (XRc == 0))) { + /* one of operands zero register -> just set destination to all 0s */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to one of them */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + tcg_gen_and_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], mxu_gpr[XRc - 1]); + } +} + +/* + * S32OR XRa, XRb, XRc + * Update XRa with the result of logical bitwise 'or' operation + * applied to the content of XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_S32OR(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to all 0s */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == 0)) { + /* XRb zero register -> just set destination to the content of XRc */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRc - 1]); + } else if (unlikely(XRc == 0)) { + /* XRc zero register -> just set destination to the content of XRb */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to one of them */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], mxu_gpr[XRc - 1]); + } +} + +/* + * S32XOR XRa, XRb, XRc + * Update XRa with the result of logical bitwise 'xor' operation + * applied to the content of XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_S32XOR(DisasContext *ctx) +{ + uint32_t pad, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to all 0s */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == 0)) { + /* XRb zero register -> just set destination to the content of XRc */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRc - 1]); + } else if (unlikely(XRc == 0)) { + /* XRc zero register -> just set destination to the content of XRb */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to all 0s */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else { + /* the most general case */ + tcg_gen_xor_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], mxu_gpr[XRc - 1]); + } +} + + +/* + * MXU instruction category max/min + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * S32MAX D16MAX Q8MAX + * S32MIN D16MIN Q8MIN + */ + +/* + * S32MAX XRa, XRb, XRc + * Update XRa with the maximum of signed 32-bit integers contained + * in XRb and XRc. + * + * S32MIN XRa, XRb, XRc + * Update XRa with the minimum of signed 32-bit integers contained + * in XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL00| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_S32MAX_S32MIN(DisasContext *ctx) +{ + uint32_t pad, opc, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + opc = extract32(ctx->opcode, 18, 3); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely((XRb == 0) || (XRc == 0))) { + /* exactly one operand is zero register - find which one is not...*/ + uint32_t XRx = XRb ? XRb : XRc; + /* ...and do max/min operation with one operand 0 */ + if (opc == OPC_MXU_S32MAX) { + tcg_gen_smax_i32(mxu_gpr[XRa - 1], mxu_gpr[XRx - 1], 0); + } else { + tcg_gen_smin_i32(mxu_gpr[XRa - 1], mxu_gpr[XRx - 1], 0); + } + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to one of them */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + if (opc == OPC_MXU_S32MAX) { + tcg_gen_smax_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], + mxu_gpr[XRc - 1]); + } else { + tcg_gen_smin_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], + mxu_gpr[XRc - 1]); + } + } +} + +/* + * D16MAX + * Update XRa with the 16-bit-wise maximums of signed integers + * contained in XRb and XRc. + * + * D16MIN + * Update XRa with the 16-bit-wise minimums of signed integers + * contained in XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL00| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_D16MAX_D16MIN(DisasContext *ctx) +{ + uint32_t pad, opc, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + opc = extract32(ctx->opcode, 18, 3); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRc == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRa == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_i32(mxu_gpr[XRc - 1], 0); + } else if (unlikely((XRb == 0) || (XRa == 0))) { + /* exactly one operand is zero register - find which one is not...*/ + uint32_t XRx = XRb ? XRb : XRc; + /* ...and do half-word-wise max/min with one operand 0 */ + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_const_i32(0); + + /* the left half-word first */ + tcg_gen_andi_i32(t0, mxu_gpr[XRx - 1], 0xFFFF0000); + if (opc == OPC_MXU_D16MAX) { + tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + } else { + tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + } + + /* the right half-word */ + tcg_gen_andi_i32(t0, mxu_gpr[XRx - 1], 0x0000FFFF); + /* move half-words to the leftmost position */ + tcg_gen_shli_i32(t0, t0, 16); + /* t0 will be max/min of t0 and t1 */ + if (opc == OPC_MXU_D16MAX) { + tcg_gen_smax_i32(t0, t0, t1); + } else { + tcg_gen_smin_i32(t0, t0, t1); + } + /* return resulting half-words to its original position */ + tcg_gen_shri_i32(t0, t0, 16); + /* finaly update the destination */ + tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + + tcg_temp_free(t1); + tcg_temp_free(t0); + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to one of them */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new(); + + /* the left half-word first */ + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0xFFFF0000); + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFFFF0000); + if (opc == OPC_MXU_D16MAX) { + tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + } else { + tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + } + + /* the right half-word */ + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0x0000FFFF); + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0x0000FFFF); + /* move half-words to the leftmost position */ + tcg_gen_shli_i32(t0, t0, 16); + tcg_gen_shli_i32(t1, t1, 16); + /* t0 will be max/min of t0 and t1 */ + if (opc == OPC_MXU_D16MAX) { + tcg_gen_smax_i32(t0, t0, t1); + } else { + tcg_gen_smin_i32(t0, t0, t1); + } + /* return resulting half-words to its original position */ + tcg_gen_shri_i32(t0, t0, 16); + /* finaly update the destination */ + tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + + tcg_temp_free(t1); + tcg_temp_free(t0); + } +} + +/* + * Q8MAX + * Update XRa with the 8-bit-wise maximums of signed integers + * contained in XRb and XRc. + * + * Q8MIN + * Update XRa with the 8-bit-wise minimums of signed integers + * contained in XRb and XRc. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0| opc | XRc | XRb | XRa |MXU__POOL00| + * +-----------+---------+-----+-------+-------+-------+-----------+ + */ +static void gen_mxu_Q8MAX_Q8MIN(DisasContext *ctx) +{ + uint32_t pad, opc, XRc, XRb, XRa; + + pad = extract32(ctx->opcode, 21, 5); + opc = extract32(ctx->opcode, 18, 3); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to zero */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely((XRb == 0) || (XRc == 0))) { + /* exactly one operand is zero register - make it be the first...*/ + uint32_t XRx = XRb ? XRb : XRc; + /* ...and do byte-wise max/min with one operand 0 */ + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_const_i32(0); + int32_t i; + + /* the leftmost byte (byte 3) first */ + tcg_gen_andi_i32(t0, mxu_gpr[XRx - 1], 0xFF000000); + if (opc == OPC_MXU_Q8MAX) { + tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + } else { + tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + } + + /* bytes 2, 1, 0 */ + for (i = 2; i >= 0; i--) { + /* extract the byte */ + tcg_gen_andi_i32(t0, mxu_gpr[XRx - 1], 0xFF << (8 * i)); + /* move the byte to the leftmost position */ + tcg_gen_shli_i32(t0, t0, 8 * (3 - i)); + /* t0 will be max/min of t0 and t1 */ + if (opc == OPC_MXU_Q8MAX) { + tcg_gen_smax_i32(t0, t0, t1); + } else { + tcg_gen_smin_i32(t0, t0, t1); + } + /* return resulting byte to its original position */ + tcg_gen_shri_i32(t0, t0, 8 * (3 - i)); + /* finaly update the destination */ + tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } + + tcg_temp_free(t1); + tcg_temp_free(t0); + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just set destination to one of them */ + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } else { + /* the most general case */ + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new(); + int32_t i; + + /* the leftmost bytes (bytes 3) first */ + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0xFF000000); + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFF000000); + if (opc == OPC_MXU_Q8MAX) { + tcg_gen_smax_i32(mxu_gpr[XRa - 1], t0, t1); + } else { + tcg_gen_smin_i32(mxu_gpr[XRa - 1], t0, t1); + } + + /* bytes 2, 1, 0 */ + for (i = 2; i >= 0; i--) { + /* extract corresponding bytes */ + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0xFF << (8 * i)); + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFF << (8 * i)); + /* move the bytes to the leftmost position */ + tcg_gen_shli_i32(t0, t0, 8 * (3 - i)); + tcg_gen_shli_i32(t1, t1, 8 * (3 - i)); + /* t0 will be max/min of t0 and t1 */ + if (opc == OPC_MXU_Q8MAX) { + tcg_gen_smax_i32(t0, t0, t1); + } else { + tcg_gen_smin_i32(t0, t0, t1); + } + /* return resulting byte to its original position */ + tcg_gen_shri_i32(t0, t0, 8 * (3 - i)); + /* finaly update the destination */ + tcg_gen_or_i32(mxu_gpr[XRa - 1], mxu_gpr[XRa - 1], t0); + } + + tcg_temp_free(t1); + tcg_temp_free(t0); + } +} + + +/* + * MXU instruction category: align + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * S32ALN S32ALNI + */ + +/* + * S32ALNI XRc, XRb, XRa, optn3 + * Arrange bytes from XRb and XRc according to one of five sets of + * rules determined by optn3, and place the result in XRa. + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+-----+---+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |optn3|0 0|x x x| XRc | XRb | XRa |MXU__POOL16| + * +-----------+-----+---+-----+-------+-------+-------+-----------+ + * + */ +static void gen_mxu_S32ALNI(DisasContext *ctx) +{ + uint32_t optn3, pad, XRc, XRb, XRa; + + optn3 = extract32(ctx->opcode, 23, 3); + pad = extract32(ctx->opcode, 21, 2); + XRc = extract32(ctx->opcode, 14, 4); + XRb = extract32(ctx->opcode, 10, 4); + XRa = extract32(ctx->opcode, 6, 4); + + if (unlikely(pad != 0)) { + /* opcode padding incorrect -> do nothing */ + } else if (unlikely(XRa == 0)) { + /* destination is zero register -> do nothing */ + } else if (unlikely((XRb == 0) && (XRc == 0))) { + /* both operands zero registers -> just set destination to all 0s */ + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + } else if (unlikely(XRb == 0)) { + /* XRb zero register -> just appropriatelly shift XRc into XRa */ + switch (optn3) { + case MXU_OPTN3_PTN0: + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + break; + case MXU_OPTN3_PTN1: + case MXU_OPTN3_PTN2: + case MXU_OPTN3_PTN3: + tcg_gen_shri_i32(mxu_gpr[XRa - 1], mxu_gpr[XRc - 1], + 8 * (4 - optn3)); + break; + case MXU_OPTN3_PTN4: + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRc - 1]); + break; + } + } else if (unlikely(XRc == 0)) { + /* XRc zero register -> just appropriatelly shift XRb into XRa */ + switch (optn3) { + case MXU_OPTN3_PTN0: + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + break; + case MXU_OPTN3_PTN1: + case MXU_OPTN3_PTN2: + case MXU_OPTN3_PTN3: + tcg_gen_shri_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], 8 * optn3); + break; + case MXU_OPTN3_PTN4: + tcg_gen_movi_i32(mxu_gpr[XRa - 1], 0); + break; + } + } else if (unlikely(XRb == XRc)) { + /* both operands same -> just rotation or moving from any of them */ + switch (optn3) { + case MXU_OPTN3_PTN0: + case MXU_OPTN3_PTN4: + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + break; + case MXU_OPTN3_PTN1: + case MXU_OPTN3_PTN2: + case MXU_OPTN3_PTN3: + tcg_gen_rotli_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1], 8 * optn3); + break; + } + } else { + /* the most general case */ + switch (optn3) { + case MXU_OPTN3_PTN0: + { + /* */ + /* XRb XRc */ + /* +---------------+ */ + /* | A B C D | E F G H */ + /* +-------+-------+ */ + /* | */ + /* XRa */ + /* */ + + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRb - 1]); + } + break; + case MXU_OPTN3_PTN1: + { + /* */ + /* XRb XRc */ + /* +-------------------+ */ + /* A | B C D E | F G H */ + /* +---------+---------+ */ + /* | */ + /* XRa */ + /* */ + + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new(); + + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0x00FFFFFF); + tcg_gen_shli_i32(t0, t0, 8); + + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFF000000); + tcg_gen_shri_i32(t1, t1, 24); + + tcg_gen_or_i32(mxu_gpr[XRa - 1], t0, t1); + + tcg_temp_free(t1); + tcg_temp_free(t0); + } + break; + case MXU_OPTN3_PTN2: + { + /* */ + /* XRb XRc */ + /* +-------------------+ */ + /* A B | C D E F | G H */ + /* +---------+---------+ */ + /* | */ + /* XRa */ + /* */ + + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new(); + + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0x0000FFFF); + tcg_gen_shli_i32(t0, t0, 16); + + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFFFF0000); + tcg_gen_shri_i32(t1, t1, 16); + + tcg_gen_or_i32(mxu_gpr[XRa - 1], t0, t1); + + tcg_temp_free(t1); + tcg_temp_free(t0); + } + break; + case MXU_OPTN3_PTN3: + { + /* */ + /* XRb XRc */ + /* +-------------------+ */ + /* A B C | D E F G | H */ + /* +---------+---------+ */ + /* | */ + /* XRa */ + /* */ + + TCGv_i32 t0 = tcg_temp_new(); + TCGv_i32 t1 = tcg_temp_new(); + + tcg_gen_andi_i32(t0, mxu_gpr[XRb - 1], 0x000000FF); + tcg_gen_shli_i32(t0, t0, 24); + + tcg_gen_andi_i32(t1, mxu_gpr[XRc - 1], 0xFFFFFF00); + tcg_gen_shri_i32(t1, t1, 8); + + tcg_gen_or_i32(mxu_gpr[XRa - 1], t0, t1); + + tcg_temp_free(t1); + tcg_temp_free(t0); + } + break; + case MXU_OPTN3_PTN4: + { + /* */ + /* XRb XRc */ + /* +---------------+ */ + /* A B C D | E F G H | */ + /* +-------+-------+ */ + /* | */ + /* XRa */ + /* */ + + tcg_gen_mov_i32(mxu_gpr[XRa - 1], mxu_gpr[XRc - 1]); + } + break; + } + } +} + + +/* + * Decoding engine for MXU + * ======================= + */ + +/* + * + * Decode MXU pool00 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0|x x x| XRc | XRb | XRa |MXU__POOL00| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool00(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_S32MAX: + case OPC_MXU_S32MIN: + gen_mxu_S32MAX_S32MIN(ctx); + break; + case OPC_MXU_D16MAX: + case OPC_MXU_D16MIN: + gen_mxu_D16MAX_D16MIN(ctx); + break; + case OPC_MXU_Q8MAX: + case OPC_MXU_Q8MIN: + gen_mxu_Q8MAX_Q8MIN(ctx); + break; + case OPC_MXU_Q8SLT: + /* TODO: Implement emulation of Q8SLT instruction. */ + MIPS_INVAL("OPC_MXU_Q8SLT"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8SLTU: + /* TODO: Implement emulation of Q8SLTU instruction. */ + MIPS_INVAL("OPC_MXU_Q8SLTU"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool01 + * + * S32SLT, D16SLT, D16AVG, D16AVGR, Q8AVG, Q8AVGR: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0|x x x| XRc | XRb | XRa |MXU__POOL01| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + * Q8ADD: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+-----+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |en2|0 0 0|x x x| XRc | XRb | XRa |MXU__POOL01| + * +-----------+---+-----+-----+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool01(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_S32SLT: + /* TODO: Implement emulation of S32SLT instruction. */ + MIPS_INVAL("OPC_MXU_S32SLT"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16SLT: + /* TODO: Implement emulation of D16SLT instruction. */ + MIPS_INVAL("OPC_MXU_D16SLT"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16AVG: + /* TODO: Implement emulation of D16AVG instruction. */ + MIPS_INVAL("OPC_MXU_D16AVG"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16AVGR: + /* TODO: Implement emulation of D16AVGR instruction. */ + MIPS_INVAL("OPC_MXU_D16AVGR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8AVG: + /* TODO: Implement emulation of Q8AVG instruction. */ + MIPS_INVAL("OPC_MXU_Q8AVG"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8AVGR: + /* TODO: Implement emulation of Q8AVGR instruction. */ + MIPS_INVAL("OPC_MXU_Q8AVGR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8ADD: + /* TODO: Implement emulation of Q8ADD instruction. */ + MIPS_INVAL("OPC_MXU_Q8ADD"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool02 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0|x x x| XRc | XRb | XRa |MXU__POOL02| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool02(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_S32CPS: + /* TODO: Implement emulation of S32CPS instruction. */ + MIPS_INVAL("OPC_MXU_S32CPS"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16CPS: + /* TODO: Implement emulation of D16CPS instruction. */ + MIPS_INVAL("OPC_MXU_D16CPS"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8ABD: + /* TODO: Implement emulation of Q8ABD instruction. */ + MIPS_INVAL("OPC_MXU_Q8ABD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16SAT: + /* TODO: Implement emulation of Q16SAT instruction. */ + MIPS_INVAL("OPC_MXU_Q16SAT"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool03 + * + * D16MULF: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |x x|on2|0 0 0 0| XRc | XRb | XRa |MXU__POOL03| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + * D16MULE: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |x x|on2| Xd | XRc | XRb | XRa |MXU__POOL03| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool03(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 24, 2); + + switch (opcode) { + case OPC_MXU_D16MULF: + /* TODO: Implement emulation of D16MULF instruction. */ + MIPS_INVAL("OPC_MXU_D16MULF"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16MULE: + /* TODO: Implement emulation of D16MULE instruction. */ + MIPS_INVAL("OPC_MXU_D16MULE"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool04 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-+-------------------+-------+-----------+ + * | SPECIAL2 | rb |x| s12 | XRa |MXU__POOL04| + * +-----------+---------+-+-------------------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool04(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 20, 1); + + switch (opcode) { + case OPC_MXU_S32LDD: + case OPC_MXU_S32LDDR: + gen_mxu_s32ldd_s32lddr(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool05 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-+-------------------+-------+-----------+ + * | SPECIAL2 | rb |x| s12 | XRa |MXU__POOL05| + * +-----------+---------+-+-------------------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool05(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 20, 1); + + switch (opcode) { + case OPC_MXU_S32STD: + /* TODO: Implement emulation of S32STD instruction. */ + MIPS_INVAL("OPC_MXU_S32STD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32STDR: + /* TODO: Implement emulation of S32STDR instruction. */ + MIPS_INVAL("OPC_MXU_S32STDR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool06 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+-------+-------+-----------+ + * | SPECIAL2 | rb | rc |st2|x x x x| XRa |MXU__POOL06| + * +-----------+---------+---------+---+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool06(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + switch (opcode) { + case OPC_MXU_S32LDDV: + /* TODO: Implement emulation of S32LDDV instruction. */ + MIPS_INVAL("OPC_MXU_S32LDDV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32LDDVR: + /* TODO: Implement emulation of S32LDDVR instruction. */ + MIPS_INVAL("OPC_MXU_S32LDDVR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool07 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+-------+-------+-----------+ + * | SPECIAL2 | rb | rc |st2|x x x x| XRa |MXU__POOL07| + * +-----------+---------+---------+---+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool07(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + switch (opcode) { + case OPC_MXU_S32STDV: + /* TODO: Implement emulation of S32TDV instruction. */ + MIPS_INVAL("OPC_MXU_S32TDV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32STDVR: + /* TODO: Implement emulation of S32TDVR instruction. */ + MIPS_INVAL("OPC_MXU_S32TDVR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool08 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-+-------------------+-------+-----------+ + * | SPECIAL2 | rb |x| s12 | XRa |MXU__POOL08| + * +-----------+---------+-+-------------------+-------+-----------+ + * +*/ +static void decode_opc_mxu__pool08(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 20, 1); + + switch (opcode) { + case OPC_MXU_S32LDI: + /* TODO: Implement emulation of S32LDI instruction. */ + MIPS_INVAL("OPC_MXU_S32LDI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32LDIR: + /* TODO: Implement emulation of S32LDIR instruction. */ + MIPS_INVAL("OPC_MXU_S32LDIR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool09 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-+-------------------+-------+-----------+ + * | SPECIAL2 | rb |x| s12 | XRa |MXU__POOL09| + * +-----------+---------+-+-------------------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool09(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 5, 0); + + switch (opcode) { + case OPC_MXU_S32SDI: + /* TODO: Implement emulation of S32SDI instruction. */ + MIPS_INVAL("OPC_MXU_S32SDI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32SDIR: + /* TODO: Implement emulation of S32SDIR instruction. */ + MIPS_INVAL("OPC_MXU_S32SDIR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool10 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+-------+-------+-----------+ + * | SPECIAL2 | rb | rc |st2|x x x x| XRa |MXU__POOL10| + * +-----------+---------+---------+---+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool10(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 5, 0); + + switch (opcode) { + case OPC_MXU_S32LDIV: + /* TODO: Implement emulation of S32LDIV instruction. */ + MIPS_INVAL("OPC_MXU_S32LDIV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32LDIVR: + /* TODO: Implement emulation of S32LDIVR instruction. */ + MIPS_INVAL("OPC_MXU_S32LDIVR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool11 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+-------+-------+-----------+ + * | SPECIAL2 | rb | rc |st2|x x x x| XRa |MXU__POOL11| + * +-----------+---------+---------+---+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool11(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 10, 4); + + switch (opcode) { + case OPC_MXU_S32SDIV: + /* TODO: Implement emulation of S32SDIV instruction. */ + MIPS_INVAL("OPC_MXU_S32SDIV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32SDIVR: + /* TODO: Implement emulation of S32SDIVR instruction. */ + MIPS_INVAL("OPC_MXU_S32SDIVR"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool12 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |an2|x x| Xd | XRc | XRb | XRa |MXU__POOL12| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool12(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_D32ACC: + /* TODO: Implement emulation of D32ACC instruction. */ + MIPS_INVAL("OPC_MXU_D32ACC"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32ACCM: + /* TODO: Implement emulation of D32ACCM instruction. */ + MIPS_INVAL("OPC_MXU_D32ACCM"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32ASUM: + /* TODO: Implement emulation of D32ASUM instruction. */ + MIPS_INVAL("OPC_MXU_D32ASUM"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool13 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |en2|x x|0 0 0 0| XRc | XRb | XRa |MXU__POOL13| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool13(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q16ACC: + /* TODO: Implement emulation of Q16ACC instruction. */ + MIPS_INVAL("OPC_MXU_Q16ACC"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16ACCM: + /* TODO: Implement emulation of Q16ACCM instruction. */ + MIPS_INVAL("OPC_MXU_Q16ACCM"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16ASUM: + /* TODO: Implement emulation of Q16ASUM instruction. */ + MIPS_INVAL("OPC_MXU_Q16ASUM"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool14 + * + * Q8ADDE, Q8ACCE: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0|x x| XRd | XRc | XRb | XRa |MXU__POOL14| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + * D8SUM, D8SUMC: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |en2|x x|0 0 0 0| XRc | XRb | XRa |MXU__POOL14| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool14(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q8ADDE: + /* TODO: Implement emulation of Q8ADDE instruction. */ + MIPS_INVAL("OPC_MXU_Q8ADDE"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D8SUM: + /* TODO: Implement emulation of D8SUM instruction. */ + MIPS_INVAL("OPC_MXU_D8SUM"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D8SUMC: + /* TODO: Implement emulation of D8SUMC instruction. */ + MIPS_INVAL("OPC_MXU_D8SUMC"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool15 + * + * S32MUL, S32MULU, S32EXTRV: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+-------+-------+-----------+ + * | SPECIAL2 | rs | rt |x x| XRd | XRa |MXU__POOL15| + * +-----------+---------+---------+---+-------+-------+-----------+ + * + * S32EXTR: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+-------+-------+-----------+ + * | SPECIAL2 | rb | sft5 |x x| XRd | XRa |MXU__POOL15| + * +-----------+---------+---------+---+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool15(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 14, 2); + + switch (opcode) { + case OPC_MXU_S32MUL: + /* TODO: Implement emulation of S32MUL instruction. */ + MIPS_INVAL("OPC_MXU_S32MUL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32MULU: + /* TODO: Implement emulation of S32MULU instruction. */ + MIPS_INVAL("OPC_MXU_S32MULU"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32EXTR: + /* TODO: Implement emulation of S32EXTR instruction. */ + MIPS_INVAL("OPC_MXU_S32EXTR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32EXTRV: + /* TODO: Implement emulation of S32EXTRV instruction. */ + MIPS_INVAL("OPC_MXU_S32EXTRV"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool16 + * + * D32SARW: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 | rb |x x x| XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + * S32ALN: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 | rs |x x x| XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + * S32ALNI: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+-----+---+-----+-------+-------+-------+-----------+ + * | SPECIAL2 | s3 |0 0|x x x| XRc | XRb | XRa |MXU__POOL16| + * +-----------+-----+---+-----+-------+-------+-------+-----------+ + * + * S32LUI: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+-----+---+-----+-------+---------------+-----------+ + * | SPECIAL2 |optn3|0 0|x x x| XRc | s8 |MXU__POOL16| + * +-----------+-----+---+-----+-------+---------------+-----------+ + * + * S32NOR, S32AND, S32OR, S32XOR: + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0|x x x| XRc | XRb | XRa |MXU__POOL16| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool16(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_D32SARW: + /* TODO: Implement emulation of D32SARW instruction. */ + MIPS_INVAL("OPC_MXU_D32SARW"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32ALN: + /* TODO: Implement emulation of S32ALN instruction. */ + MIPS_INVAL("OPC_MXU_S32ALN"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32ALNI: + gen_mxu_S32ALNI(ctx); + break; + case OPC_MXU_S32LUI: + /* TODO: Implement emulation of S32LUI instruction. */ + MIPS_INVAL("OPC_MXU_S32LUI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32NOR: + gen_mxu_S32NOR(ctx); + break; + case OPC_MXU_S32AND: + gen_mxu_S32AND(ctx); + break; + case OPC_MXU_S32OR: + gen_mxu_S32OR(ctx); + break; + case OPC_MXU_S32XOR: + gen_mxu_S32XOR(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool17 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+---------+---+---------+-----+-----------+ + * | SPECIAL2 | rs | rt |0 0| rd |x x x|MXU__POOL15| + * +-----------+---------+---------+---+---------+-----+-----------+ + * + */ +static void decode_opc_mxu__pool17(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 6, 2); + + switch (opcode) { + case OPC_MXU_LXW: + /* TODO: Implement emulation of LXW instruction. */ + MIPS_INVAL("OPC_MXU_LXW"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_LXH: + /* TODO: Implement emulation of LXH instruction. */ + MIPS_INVAL("OPC_MXU_LXH"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_LXHU: + /* TODO: Implement emulation of LXHU instruction. */ + MIPS_INVAL("OPC_MXU_LXHU"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_LXB: + /* TODO: Implement emulation of LXB instruction. */ + MIPS_INVAL("OPC_MXU_LXB"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_LXBU: + /* TODO: Implement emulation of LXBU instruction. */ + MIPS_INVAL("OPC_MXU_LXBU"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} +/* + * + * Decode MXU pool18 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 | rb |x x x| XRd | XRa |0 0 0 0|MXU__POOL18| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool18(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_D32SLLV: + /* TODO: Implement emulation of D32SLLV instruction. */ + MIPS_INVAL("OPC_MXU_D32SLLV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32SLRV: + /* TODO: Implement emulation of D32SLRV instruction. */ + MIPS_INVAL("OPC_MXU_D32SLRV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32SARV: + /* TODO: Implement emulation of D32SARV instruction. */ + MIPS_INVAL("OPC_MXU_D32SARV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16SLLV: + /* TODO: Implement emulation of Q16SLLV instruction. */ + MIPS_INVAL("OPC_MXU_Q16SLLV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16SLRV: + /* TODO: Implement emulation of Q16SLRV instruction. */ + MIPS_INVAL("OPC_MXU_Q16SLRV"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16SARV: + /* TODO: Implement emulation of Q16SARV instruction. */ + MIPS_INVAL("OPC_MXU_Q16SARV"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool19 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0|x x| XRd | XRc | XRb | XRa |MXU__POOL19| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool19(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q8MUL: + case OPC_MXU_Q8MULSU: + gen_mxu_q8mul_q8mulsu(ctx); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool20 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------+-----+-------+-------+-------+-----------+ + * | SPECIAL2 |0 0 0 0 0|x x x| XRc | XRb | XRa |MXU__POOL20| + * +-----------+---------+-----+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool20(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 18, 3); + + switch (opcode) { + case OPC_MXU_Q8MOVZ: + /* TODO: Implement emulation of Q8MOVZ instruction. */ + MIPS_INVAL("OPC_MXU_Q8MOVZ"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8MOVN: + /* TODO: Implement emulation of Q8MOVN instruction. */ + MIPS_INVAL("OPC_MXU_Q8MOVN"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16MOVZ: + /* TODO: Implement emulation of D16MOVZ instruction. */ + MIPS_INVAL("OPC_MXU_D16MOVZ"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16MOVN: + /* TODO: Implement emulation of D16MOVN instruction. */ + MIPS_INVAL("OPC_MXU_D16MOVN"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32MOVZ: + /* TODO: Implement emulation of S32MOVZ instruction. */ + MIPS_INVAL("OPC_MXU_S32MOVZ"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32MOVN: + /* TODO: Implement emulation of S32MOVN instruction. */ + MIPS_INVAL("OPC_MXU_S32MOVN"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +/* + * + * Decode MXU pool21 + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * | SPECIAL2 |an2|x x| XRd | XRc | XRb | XRa |MXU__POOL21| + * +-----------+---+---+-------+-------+-------+-------+-----------+ + * + */ +static void decode_opc_mxu__pool21(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opcode = extract32(ctx->opcode, 22, 2); + + switch (opcode) { + case OPC_MXU_Q8MAC: + /* TODO: Implement emulation of Q8MAC instruction. */ + MIPS_INVAL("OPC_MXU_Q8MAC"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8MACSU: + /* TODO: Implement emulation of Q8MACSU instruction. */ + MIPS_INVAL("OPC_MXU_Q8MACSU"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + + +/* + * Main MXU decoding function + * + * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------+---------------------------------------+-----------+ + * | SPECIAL2 | |x x x x x x| + * +-----------+---------------------------------------+-----------+ + * + */ +static void decode_opc_mxu(CPUMIPSState *env, DisasContext *ctx) +{ + /* + * TODO: Investigate necessity of including handling of + * CLZ, CLO, SDBB in this function, as they belong to + * SPECIAL2 opcode space for regular pre-R6 MIPS ISAs. + */ + uint32_t opcode = extract32(ctx->opcode, 0, 6); + + if (opcode == OPC__MXU_MUL) { + uint32_t rs, rt, rd, op1; + + rs = extract32(ctx->opcode, 21, 5); + rt = extract32(ctx->opcode, 16, 5); + rd = extract32(ctx->opcode, 11, 5); + op1 = MASK_SPECIAL2(ctx->opcode); + + gen_arith(ctx, op1, rd, rs, rt); + + return; + } + + if (opcode == OPC_MXU_S32M2I) { + gen_mxu_s32m2i(ctx); + return; + } + + if (opcode == OPC_MXU_S32I2M) { + gen_mxu_s32i2m(ctx); + return; + } + + { + TCGv t_mxu_cr = tcg_temp_new(); + TCGLabel *l_exit = gen_new_label(); + + gen_load_mxu_cr(t_mxu_cr); + tcg_gen_andi_tl(t_mxu_cr, t_mxu_cr, MXU_CR_MXU_EN); + tcg_gen_brcondi_tl(TCG_COND_NE, t_mxu_cr, MXU_CR_MXU_EN, l_exit); + + switch (opcode) { + case OPC_MXU_S32MADD: + /* TODO: Implement emulation of S32MADD instruction. */ + MIPS_INVAL("OPC_MXU_S32MADD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32MADDU: + /* TODO: Implement emulation of S32MADDU instruction. */ + MIPS_INVAL("OPC_MXU_S32MADDU"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL00: + decode_opc_mxu__pool00(env, ctx); + break; + case OPC_MXU_S32MSUB: + /* TODO: Implement emulation of S32MSUB instruction. */ + MIPS_INVAL("OPC_MXU_S32MSUB"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32MSUBU: + /* TODO: Implement emulation of S32MSUBU instruction. */ + MIPS_INVAL("OPC_MXU_S32MSUBU"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL01: + decode_opc_mxu__pool01(env, ctx); + break; + case OPC_MXU__POOL02: + decode_opc_mxu__pool02(env, ctx); + break; + case OPC_MXU_D16MUL: + gen_mxu_d16mul(ctx); + break; + case OPC_MXU__POOL03: + decode_opc_mxu__pool03(env, ctx); + break; + case OPC_MXU_D16MAC: + gen_mxu_d16mac(ctx); + break; + case OPC_MXU_D16MACF: + /* TODO: Implement emulation of D16MACF instruction. */ + MIPS_INVAL("OPC_MXU_D16MACF"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16MADL: + /* TODO: Implement emulation of D16MADL instruction. */ + MIPS_INVAL("OPC_MXU_D16MADL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S16MAD: + /* TODO: Implement emulation of S16MAD instruction. */ + MIPS_INVAL("OPC_MXU_S16MAD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16ADD: + /* TODO: Implement emulation of Q16ADD instruction. */ + MIPS_INVAL("OPC_MXU_Q16ADD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D16MACE: + /* TODO: Implement emulation of D16MACE instruction. */ + MIPS_INVAL("OPC_MXU_D16MACE"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL04: + decode_opc_mxu__pool04(env, ctx); + break; + case OPC_MXU__POOL05: + decode_opc_mxu__pool05(env, ctx); + break; + case OPC_MXU__POOL06: + decode_opc_mxu__pool06(env, ctx); + break; + case OPC_MXU__POOL07: + decode_opc_mxu__pool07(env, ctx); + break; + case OPC_MXU__POOL08: + decode_opc_mxu__pool08(env, ctx); + break; + case OPC_MXU__POOL09: + decode_opc_mxu__pool09(env, ctx); + break; + case OPC_MXU__POOL10: + decode_opc_mxu__pool10(env, ctx); + break; + case OPC_MXU__POOL11: + decode_opc_mxu__pool11(env, ctx); + break; + case OPC_MXU_D32ADD: + /* TODO: Implement emulation of D32ADD instruction. */ + MIPS_INVAL("OPC_MXU_D32ADD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL12: + decode_opc_mxu__pool12(env, ctx); + break; + case OPC_MXU__POOL13: + decode_opc_mxu__pool13(env, ctx); + break; + case OPC_MXU__POOL14: + decode_opc_mxu__pool14(env, ctx); + break; + case OPC_MXU_Q8ACCE: + /* TODO: Implement emulation of Q8ACCE instruction. */ + MIPS_INVAL("OPC_MXU_Q8ACCE"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S8LDD: + gen_mxu_s8ldd(ctx); + break; + case OPC_MXU_S8STD: + /* TODO: Implement emulation of S8STD instruction. */ + MIPS_INVAL("OPC_MXU_S8STD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S8LDI: + /* TODO: Implement emulation of S8LDI instruction. */ + MIPS_INVAL("OPC_MXU_S8LDI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S8SDI: + /* TODO: Implement emulation of S8SDI instruction. */ + MIPS_INVAL("OPC_MXU_S8SDI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL15: + decode_opc_mxu__pool15(env, ctx); + break; + case OPC_MXU__POOL16: + decode_opc_mxu__pool16(env, ctx); + break; + case OPC_MXU__POOL17: + decode_opc_mxu__pool17(env, ctx); + break; + case OPC_MXU_S16LDD: + /* TODO: Implement emulation of S16LDD instruction. */ + MIPS_INVAL("OPC_MXU_S16LDD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S16STD: + /* TODO: Implement emulation of S16STD instruction. */ + MIPS_INVAL("OPC_MXU_S16STD"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S16LDI: + /* TODO: Implement emulation of S16LDI instruction. */ + MIPS_INVAL("OPC_MXU_S16LDI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S16SDI: + /* TODO: Implement emulation of S16SDI instruction. */ + MIPS_INVAL("OPC_MXU_S16SDI"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32SLL: + /* TODO: Implement emulation of D32SLL instruction. */ + MIPS_INVAL("OPC_MXU_D32SLL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32SLR: + /* TODO: Implement emulation of D32SLR instruction. */ + MIPS_INVAL("OPC_MXU_D32SLR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32SARL: + /* TODO: Implement emulation of D32SARL instruction. */ + MIPS_INVAL("OPC_MXU_D32SARL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_D32SAR: + /* TODO: Implement emulation of D32SAR instruction. */ + MIPS_INVAL("OPC_MXU_D32SAR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16SLL: + /* TODO: Implement emulation of Q16SLL instruction. */ + MIPS_INVAL("OPC_MXU_Q16SLL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q16SLR: + /* TODO: Implement emulation of Q16SLR instruction. */ + MIPS_INVAL("OPC_MXU_Q16SLR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL18: + decode_opc_mxu__pool18(env, ctx); + break; + case OPC_MXU_Q16SAR: + /* TODO: Implement emulation of Q16SAR instruction. */ + MIPS_INVAL("OPC_MXU_Q16SAR"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU__POOL19: + decode_opc_mxu__pool19(env, ctx); + break; + case OPC_MXU__POOL20: + decode_opc_mxu__pool20(env, ctx); + break; + case OPC_MXU__POOL21: + decode_opc_mxu__pool21(env, ctx); + break; + case OPC_MXU_Q16SCOP: + /* TODO: Implement emulation of Q16SCOP instruction. */ + MIPS_INVAL("OPC_MXU_Q16SCOP"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8MADL: + /* TODO: Implement emulation of Q8MADL instruction. */ + MIPS_INVAL("OPC_MXU_Q8MADL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_S32SFL: + /* TODO: Implement emulation of S32SFL instruction. */ + MIPS_INVAL("OPC_MXU_S32SFL"); + generate_exception_end(ctx, EXCP_RI); + break; + case OPC_MXU_Q8SAD: + /* TODO: Implement emulation of Q8SAD instruction. */ + MIPS_INVAL("OPC_MXU_Q8SAD"); + generate_exception_end(ctx, EXCP_RI); + break; + default: + MIPS_INVAL("decode_opc_mxu"); + generate_exception_end(ctx, EXCP_RI); + } + + gen_set_label(l_exit); + tcg_temp_free(t_mxu_cr); + } +} + +#endif /* !defined(TARGET_MIPS64) */ + + static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) { int rs, rt, rd; @@ -17607,8 +26595,10 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) op1 = MASK_SPECIAL2(ctx->opcode); switch (op1) { - case OPC_MADD ... OPC_MADDU: /* Multiply and add/sub */ - case OPC_MSUB ... OPC_MSUBU: + case OPC_MADD: /* Multiply and add/sub */ + case OPC_MADDU: + case OPC_MSUB: + case OPC_MSUBU: check_insn(ctx, ISA_MIPS32); gen_muldiv(ctx, op1, rd & 3, rs, rt); break; @@ -17692,7 +26682,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) } break; case R6_OPC_SC: - gen_st_cond(ctx, op1, rt, rs, imm); + gen_st_cond(ctx, rt, rs, imm, MO_TESL, false); break; case R6_OPC_LL: gen_ld(ctx, op1, rt, rs, imm); @@ -17705,8 +26695,11 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) } op2 = MASK_BSHFL(ctx->opcode); switch (op2) { - case OPC_ALIGN ... OPC_ALIGN_END: - gen_align(ctx, OPC_ALIGN, rd, rs, rt, sa & 3); + case OPC_ALIGN: + case OPC_ALIGN_1: + case OPC_ALIGN_2: + case OPC_ALIGN_3: + gen_align(ctx, 32, rd, rs, rt, sa & 3); break; case OPC_BITSWAP: gen_bitswap(ctx, op2, rd, rt); @@ -17716,7 +26709,7 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) break; #if defined(TARGET_MIPS64) case R6_OPC_SCD: - gen_st_cond(ctx, op1, rt, rs, imm); + gen_st_cond(ctx, rt, rs, imm, MO_TEQ, false); break; case R6_OPC_LLD: gen_ld(ctx, op1, rt, rs, imm); @@ -17730,8 +26723,15 @@ static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) } op2 = MASK_DBSHFL(ctx->opcode); switch (op2) { - case OPC_DALIGN ... OPC_DALIGN_END: - gen_align(ctx, OPC_DALIGN, rd, rs, rt, sa & 7); + case OPC_DALIGN: + case OPC_DALIGN_1: + case OPC_DALIGN_2: + case OPC_DALIGN_3: + case OPC_DALIGN_4: + case OPC_DALIGN_5: + case OPC_DALIGN_6: + case OPC_DALIGN_7: + gen_align(ctx, 64, rd, rs, rt, sa & 7); break; case OPC_DBITSWAP: gen_bitswap(ctx, op2, rd, rt); @@ -17759,12 +26759,15 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) op1 = MASK_SPECIAL3(ctx->opcode); switch (op1) { - case OPC_DIV_G_2E ... OPC_DIVU_G_2E: - case OPC_MOD_G_2E ... OPC_MODU_G_2E: - case OPC_MULT_G_2E ... OPC_MULTU_G_2E: + case OPC_DIV_G_2E: + case OPC_DIVU_G_2E: + case OPC_MOD_G_2E: + case OPC_MODU_G_2E: + case OPC_MULT_G_2E: + case OPC_MULTU_G_2E: /* OPC_MULT_G_2E, OPC_ADDUH_QB_DSP, OPC_MUL_PH_DSP have * the same mask and op1. */ - if ((ctx->insn_flags & ASE_DSPR2) && (op1 == OPC_MULT_G_2E)) { + if ((ctx->insn_flags & ASE_DSP_R2) && (op1 == OPC_MULT_G_2E)) { op2 = MASK_ADDUH_QB(ctx->opcode); switch (op2) { case OPC_ADDUH_QB: @@ -18025,9 +27028,12 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } break; #if defined(TARGET_MIPS64) - case OPC_DDIV_G_2E ... OPC_DDIVU_G_2E: - case OPC_DMULT_G_2E ... OPC_DMULTU_G_2E: - case OPC_DMOD_G_2E ... OPC_DMODU_G_2E: + case OPC_DDIV_G_2E: + case OPC_DDIVU_G_2E: + case OPC_DMULT_G_2E: + case OPC_DMULTU_G_2E: + case OPC_DMOD_G_2E: + case OPC_DMODU_G_2E: check_insn(ctx, INSN_LOONGSON2E); gen_loongson_integer(ctx, op1, rd, rs, rt); break; @@ -18268,6 +27274,255 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } } + +#if defined(TARGET_MIPS64) + +static void decode_mmi0(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opc = MASK_MMI0(ctx->opcode); + + switch (opc) { + case MMI_OPC_0_PADDW: /* TODO: MMI_OPC_0_PADDW */ + case MMI_OPC_0_PSUBW: /* TODO: MMI_OPC_0_PSUBW */ + case MMI_OPC_0_PCGTW: /* TODO: MMI_OPC_0_PCGTW */ + case MMI_OPC_0_PMAXW: /* TODO: MMI_OPC_0_PMAXW */ + case MMI_OPC_0_PADDH: /* TODO: MMI_OPC_0_PADDH */ + case MMI_OPC_0_PSUBH: /* TODO: MMI_OPC_0_PSUBH */ + case MMI_OPC_0_PCGTH: /* TODO: MMI_OPC_0_PCGTH */ + case MMI_OPC_0_PMAXH: /* TODO: MMI_OPC_0_PMAXH */ + case MMI_OPC_0_PADDB: /* TODO: MMI_OPC_0_PADDB */ + case MMI_OPC_0_PSUBB: /* TODO: MMI_OPC_0_PSUBB */ + case MMI_OPC_0_PCGTB: /* TODO: MMI_OPC_0_PCGTB */ + case MMI_OPC_0_PADDSW: /* TODO: MMI_OPC_0_PADDSW */ + case MMI_OPC_0_PSUBSW: /* TODO: MMI_OPC_0_PSUBSW */ + case MMI_OPC_0_PEXTLW: /* TODO: MMI_OPC_0_PEXTLW */ + case MMI_OPC_0_PPACW: /* TODO: MMI_OPC_0_PPACW */ + case MMI_OPC_0_PADDSH: /* TODO: MMI_OPC_0_PADDSH */ + case MMI_OPC_0_PSUBSH: /* TODO: MMI_OPC_0_PSUBSH */ + case MMI_OPC_0_PEXTLH: /* TODO: MMI_OPC_0_PEXTLH */ + case MMI_OPC_0_PPACH: /* TODO: MMI_OPC_0_PPACH */ + case MMI_OPC_0_PADDSB: /* TODO: MMI_OPC_0_PADDSB */ + case MMI_OPC_0_PSUBSB: /* TODO: MMI_OPC_0_PSUBSB */ + case MMI_OPC_0_PEXTLB: /* TODO: MMI_OPC_0_PEXTLB */ + case MMI_OPC_0_PPACB: /* TODO: MMI_OPC_0_PPACB */ + case MMI_OPC_0_PEXT5: /* TODO: MMI_OPC_0_PEXT5 */ + case MMI_OPC_0_PPAC5: /* TODO: MMI_OPC_0_PPAC5 */ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_CLASS_MMI0 */ + break; + default: + MIPS_INVAL("TX79 MMI class MMI0"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void decode_mmi1(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opc = MASK_MMI1(ctx->opcode); + + switch (opc) { + case MMI_OPC_1_PABSW: /* TODO: MMI_OPC_1_PABSW */ + case MMI_OPC_1_PCEQW: /* TODO: MMI_OPC_1_PCEQW */ + case MMI_OPC_1_PMINW: /* TODO: MMI_OPC_1_PMINW */ + case MMI_OPC_1_PADSBH: /* TODO: MMI_OPC_1_PADSBH */ + case MMI_OPC_1_PABSH: /* TODO: MMI_OPC_1_PABSH */ + case MMI_OPC_1_PCEQH: /* TODO: MMI_OPC_1_PCEQH */ + case MMI_OPC_1_PMINH: /* TODO: MMI_OPC_1_PMINH */ + case MMI_OPC_1_PCEQB: /* TODO: MMI_OPC_1_PCEQB */ + case MMI_OPC_1_PADDUW: /* TODO: MMI_OPC_1_PADDUW */ + case MMI_OPC_1_PSUBUW: /* TODO: MMI_OPC_1_PSUBUW */ + case MMI_OPC_1_PEXTUW: /* TODO: MMI_OPC_1_PEXTUW */ + case MMI_OPC_1_PADDUH: /* TODO: MMI_OPC_1_PADDUH */ + case MMI_OPC_1_PSUBUH: /* TODO: MMI_OPC_1_PSUBUH */ + case MMI_OPC_1_PEXTUH: /* TODO: MMI_OPC_1_PEXTUH */ + case MMI_OPC_1_PADDUB: /* TODO: MMI_OPC_1_PADDUB */ + case MMI_OPC_1_PSUBUB: /* TODO: MMI_OPC_1_PSUBUB */ + case MMI_OPC_1_PEXTUB: /* TODO: MMI_OPC_1_PEXTUB */ + case MMI_OPC_1_QFSRV: /* TODO: MMI_OPC_1_QFSRV */ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_CLASS_MMI1 */ + break; + default: + MIPS_INVAL("TX79 MMI class MMI1"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void decode_mmi2(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opc = MASK_MMI2(ctx->opcode); + + switch (opc) { + case MMI_OPC_2_PMADDW: /* TODO: MMI_OPC_2_PMADDW */ + case MMI_OPC_2_PSLLVW: /* TODO: MMI_OPC_2_PSLLVW */ + case MMI_OPC_2_PSRLVW: /* TODO: MMI_OPC_2_PSRLVW */ + case MMI_OPC_2_PMSUBW: /* TODO: MMI_OPC_2_PMSUBW */ + case MMI_OPC_2_PMFHI: /* TODO: MMI_OPC_2_PMFHI */ + case MMI_OPC_2_PMFLO: /* TODO: MMI_OPC_2_PMFLO */ + case MMI_OPC_2_PINTH: /* TODO: MMI_OPC_2_PINTH */ + case MMI_OPC_2_PMULTW: /* TODO: MMI_OPC_2_PMULTW */ + case MMI_OPC_2_PDIVW: /* TODO: MMI_OPC_2_PDIVW */ + case MMI_OPC_2_PCPYLD: /* TODO: MMI_OPC_2_PCPYLD */ + case MMI_OPC_2_PMADDH: /* TODO: MMI_OPC_2_PMADDH */ + case MMI_OPC_2_PHMADH: /* TODO: MMI_OPC_2_PHMADH */ + case MMI_OPC_2_PAND: /* TODO: MMI_OPC_2_PAND */ + case MMI_OPC_2_PXOR: /* TODO: MMI_OPC_2_PXOR */ + case MMI_OPC_2_PMSUBH: /* TODO: MMI_OPC_2_PMSUBH */ + case MMI_OPC_2_PHMSBH: /* TODO: MMI_OPC_2_PHMSBH */ + case MMI_OPC_2_PEXEH: /* TODO: MMI_OPC_2_PEXEH */ + case MMI_OPC_2_PREVH: /* TODO: MMI_OPC_2_PREVH */ + case MMI_OPC_2_PMULTH: /* TODO: MMI_OPC_2_PMULTH */ + case MMI_OPC_2_PDIVBW: /* TODO: MMI_OPC_2_PDIVBW */ + case MMI_OPC_2_PEXEW: /* TODO: MMI_OPC_2_PEXEW */ + case MMI_OPC_2_PROT3W: /* TODO: MMI_OPC_2_PROT3W */ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_CLASS_MMI2 */ + break; + default: + MIPS_INVAL("TX79 MMI class MMI2"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void decode_mmi3(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opc = MASK_MMI3(ctx->opcode); + + switch (opc) { + case MMI_OPC_3_PMADDUW: /* TODO: MMI_OPC_3_PMADDUW */ + case MMI_OPC_3_PSRAVW: /* TODO: MMI_OPC_3_PSRAVW */ + case MMI_OPC_3_PMTHI: /* TODO: MMI_OPC_3_PMTHI */ + case MMI_OPC_3_PMTLO: /* TODO: MMI_OPC_3_PMTLO */ + case MMI_OPC_3_PINTEH: /* TODO: MMI_OPC_3_PINTEH */ + case MMI_OPC_3_PMULTUW: /* TODO: MMI_OPC_3_PMULTUW */ + case MMI_OPC_3_PDIVUW: /* TODO: MMI_OPC_3_PDIVUW */ + case MMI_OPC_3_PCPYUD: /* TODO: MMI_OPC_3_PCPYUD */ + case MMI_OPC_3_POR: /* TODO: MMI_OPC_3_POR */ + case MMI_OPC_3_PNOR: /* TODO: MMI_OPC_3_PNOR */ + case MMI_OPC_3_PEXCH: /* TODO: MMI_OPC_3_PEXCH */ + case MMI_OPC_3_PCPYH: /* TODO: MMI_OPC_3_PCPYH */ + case MMI_OPC_3_PEXCW: /* TODO: MMI_OPC_3_PEXCW */ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_CLASS_MMI3 */ + break; + default: + MIPS_INVAL("TX79 MMI class MMI3"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void decode_mmi(CPUMIPSState *env, DisasContext *ctx) +{ + uint32_t opc = MASK_MMI(ctx->opcode); + int rs = extract32(ctx->opcode, 21, 5); + int rt = extract32(ctx->opcode, 16, 5); + int rd = extract32(ctx->opcode, 11, 5); + + switch (opc) { + case MMI_OPC_CLASS_MMI0: + decode_mmi0(env, ctx); + break; + case MMI_OPC_CLASS_MMI1: + decode_mmi1(env, ctx); + break; + case MMI_OPC_CLASS_MMI2: + decode_mmi2(env, ctx); + break; + case MMI_OPC_CLASS_MMI3: + decode_mmi3(env, ctx); + break; + case MMI_OPC_MULT1: + case MMI_OPC_MULTU1: + case MMI_OPC_MADD: + case MMI_OPC_MADDU: + case MMI_OPC_MADD1: + case MMI_OPC_MADDU1: + gen_mul_txx9(ctx, opc, rd, rs, rt); + break; + case MMI_OPC_DIV1: + case MMI_OPC_DIVU1: + gen_div1_tx79(ctx, opc, rs, rt); + break; + case MMI_OPC_MTLO1: + case MMI_OPC_MTHI1: + gen_HILO1_tx79(ctx, opc, rs); + break; + case MMI_OPC_MFLO1: + case MMI_OPC_MFHI1: + gen_HILO1_tx79(ctx, opc, rd); + break; + case MMI_OPC_PLZCW: /* TODO: MMI_OPC_PLZCW */ + case MMI_OPC_PMFHL: /* TODO: MMI_OPC_PMFHL */ + case MMI_OPC_PMTHL: /* TODO: MMI_OPC_PMTHL */ + case MMI_OPC_PSLLH: /* TODO: MMI_OPC_PSLLH */ + case MMI_OPC_PSRLH: /* TODO: MMI_OPC_PSRLH */ + case MMI_OPC_PSRAH: /* TODO: MMI_OPC_PSRAH */ + case MMI_OPC_PSLLW: /* TODO: MMI_OPC_PSLLW */ + case MMI_OPC_PSRLW: /* TODO: MMI_OPC_PSRLW */ + case MMI_OPC_PSRAW: /* TODO: MMI_OPC_PSRAW */ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_CLASS_MMI */ + break; + default: + MIPS_INVAL("TX79 MMI class"); + generate_exception_end(ctx, EXCP_RI); + break; + } +} + +static void gen_mmi_lq(CPUMIPSState *env, DisasContext *ctx) +{ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_LQ */ +} + +static void gen_mmi_sq(DisasContext *ctx, int base, int rt, int offset) +{ + generate_exception_end(ctx, EXCP_RI); /* TODO: MMI_OPC_SQ */ +} + +/* + * The TX79-specific instruction Store Quadword + * + * +--------+-------+-------+------------------------+ + * | 011111 | base | rt | offset | SQ + * +--------+-------+-------+------------------------+ + * 6 5 5 16 + * + * has the same opcode as the Read Hardware Register instruction + * + * +--------+-------+-------+-------+-------+--------+ + * | 011111 | 00000 | rt | rd | 00000 | 111011 | RDHWR + * +--------+-------+-------+-------+-------+--------+ + * 6 5 5 5 5 6 + * + * that is required, trapped and emulated by the Linux kernel. However, all + * RDHWR encodings yield address error exceptions on the TX79 since the SQ + * offset is odd. Therefore all valid SQ instructions can execute normally. + * In user mode, QEMU must verify the upper and lower 11 bits to distinguish + * between SQ and RDHWR, as the Linux kernel does. + */ +static void decode_mmi_sq(CPUMIPSState *env, DisasContext *ctx) +{ + int base = extract32(ctx->opcode, 21, 5); + int rt = extract32(ctx->opcode, 16, 5); + int offset = extract32(ctx->opcode, 0, 16); + +#ifdef CONFIG_USER_ONLY + uint32_t op1 = MASK_SPECIAL3(ctx->opcode); + uint32_t op2 = extract32(ctx->opcode, 6, 5); + + if (base == 0 && op2 == 0 && op1 == OPC_RDHWR) { + int rd = extract32(ctx->opcode, 11, 5); + + gen_rdhwr(ctx, rt, rd, 0); + return; + } +#endif + + gen_mmi_sq(ctx, base, rt, offset); +} + +#endif + static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) { int rs, rt, rd, sa; @@ -18289,25 +27544,32 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) */ if (ctx->eva) { switch (op1) { - case OPC_LWLE ... OPC_LWRE: + case OPC_LWLE: + case OPC_LWRE: check_insn_opc_removed(ctx, ISA_MIPS32R6); /* fall through */ - case OPC_LBUE ... OPC_LHUE: - case OPC_LBE ... OPC_LWE: + case OPC_LBUE: + case OPC_LHUE: + case OPC_LBE: + case OPC_LHE: + case OPC_LLE: + case OPC_LWE: check_cp0_enabled(ctx); gen_ld(ctx, op1, rt, rs, imm); return; - case OPC_SWLE ... OPC_SWRE: + case OPC_SWLE: + case OPC_SWRE: check_insn_opc_removed(ctx, ISA_MIPS32R6); /* fall through */ - case OPC_SBE ... OPC_SHE: + case OPC_SBE: + case OPC_SHE: case OPC_SWE: check_cp0_enabled(ctx); gen_st(ctx, op1, rt, rs, imm); return; case OPC_SCE: check_cp0_enabled(ctx); - gen_st_cond(ctx, op1, rt, rs, imm); + gen_st_cond(ctx, rt, rs, imm, MO_TESL, true); return; case OPC_CACHEE: check_cp0_enabled(ctx); @@ -18332,7 +27594,10 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) case OPC_BSHFL: op2 = MASK_BSHFL(ctx->opcode); switch (op2) { - case OPC_ALIGN ... OPC_ALIGN_END: + case OPC_ALIGN: + case OPC_ALIGN_1: + case OPC_ALIGN_2: + case OPC_ALIGN_3: case OPC_BITSWAP: check_insn(ctx, ISA_MIPS32R6); decode_opc_special3_r6(env, ctx); @@ -18344,8 +27609,12 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) } break; #if defined(TARGET_MIPS64) - case OPC_DEXTM ... OPC_DEXT: - case OPC_DINSM ... OPC_DINS: + case OPC_DEXTM: + case OPC_DEXTU: + case OPC_DEXT: + case OPC_DINSM: + case OPC_DINSU: + case OPC_DINS: check_insn(ctx, ISA_MIPS64R2); check_mips_64(ctx); gen_bitops(ctx, op1, rt, rs, sa, rd); @@ -18353,7 +27622,14 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) case OPC_DBSHFL: op2 = MASK_DBSHFL(ctx->opcode); switch (op2) { - case OPC_DALIGN ... OPC_DALIGN_END: + case OPC_DALIGN: + case OPC_DALIGN_1: + case OPC_DALIGN_2: + case OPC_DALIGN_3: + case OPC_DALIGN_4: + case OPC_DALIGN_5: + case OPC_DALIGN_6: + case OPC_DALIGN_7: case OPC_DBITSWAP: check_insn(ctx, ISA_MIPS32R6); decode_opc_special3_r6(env, ctx); @@ -18371,7 +27647,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) gen_rdhwr(ctx, rt, rd, extract32(ctx->opcode, 6, 3)); break; case OPC_FORK: - check_insn(ctx, ASE_MT); + check_mt(ctx); { TCGv t0 = tcg_temp_new(); TCGv t1 = tcg_temp_new(); @@ -18384,7 +27660,7 @@ static void decode_opc_special3(CPUMIPSState *env, DisasContext *ctx) } break; case OPC_YIELD: - check_insn(ctx, ASE_MT); + check_mt(ctx); { TCGv t0 = tcg_temp_new(); @@ -19552,10 +28828,27 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) decode_opc_special(env, ctx); break; case OPC_SPECIAL2: - decode_opc_special2_legacy(env, ctx); +#if defined(TARGET_MIPS64) + if ((ctx->insn_flags & INSN_R5900) && (ctx->insn_flags & ASE_MMI)) { + decode_mmi(env, ctx); +#else + if (ctx->insn_flags & ASE_MXU) { + decode_opc_mxu(env, ctx); +#endif + } else { + decode_opc_special2_legacy(env, ctx); + } break; case OPC_SPECIAL3: +#if defined(TARGET_MIPS64) + if (ctx->insn_flags & INSN_R5900) { + decode_mmi_sq(env, ctx); /* MMI_OPC_SQ */ + } else { + decode_opc_special3(env, ctx); + } +#else decode_opc_special3(env, ctx); +#endif break; case OPC_REGIMM: op1 = MASK_REGIMM(ctx->opcode); @@ -19584,7 +28877,12 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_compute_branch(ctx, op1, 4, rs, -1, imm << 2, 4); } break; - case OPC_TGEI ... OPC_TEQI: /* REGIMM traps */ + case OPC_TGEI: /* REGIMM traps */ + case OPC_TGEIU: + case OPC_TLTI: + case OPC_TLTIU: + case OPC_TEQI: + case OPC_TNEI: check_insn(ctx, ISA_MIPS2); check_insn_opc_removed(ctx, ISA_MIPS32R6); @@ -19647,7 +28945,22 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) gen_cp0(env, ctx, op1, rt, rd); #endif /* !CONFIG_USER_ONLY */ break; - case OPC_C0_FIRST ... OPC_C0_LAST: + case OPC_C0: + case OPC_C0_1: + case OPC_C0_2: + case OPC_C0_3: + case OPC_C0_4: + case OPC_C0_5: + case OPC_C0_6: + case OPC_C0_7: + case OPC_C0_8: + case OPC_C0_9: + case OPC_C0_A: + case OPC_C0_B: + case OPC_C0_C: + case OPC_C0_D: + case OPC_C0_E: + case OPC_C0_F: #ifndef CONFIG_USER_ONLY gen_cp0(env, ctx, MASK_C0(ctx->opcode), rt, rd); #endif /* !CONFIG_USER_ONLY */ @@ -19661,22 +28974,22 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) op2 = MASK_MFMC0(ctx->opcode); switch (op2) { case OPC_DMT: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_dmt(t0); gen_store_gpr(t0, rt); break; case OPC_EMT: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_emt(t0); gen_store_gpr(t0, rt); break; case OPC_DVPE: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_dvpe(t0, cpu_env); gen_store_gpr(t0, rt); break; case OPC_EVPE: - check_insn(ctx, ASE_MT); + check_cp0_mt(ctx); gen_helper_evpe(t0, cpu_env); gen_store_gpr(t0, rt); break; @@ -19759,7 +29072,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) case OPC_XORI: gen_logic_imm(ctx, op, rt, rs, imm); break; - case OPC_J ... OPC_JAL: /* Jump */ + case OPC_J: /* Jump */ + case OPC_JAL: offset = (int32_t)(ctx->opcode & 0x3FFFFFF) << 2; gen_compute_branch(ctx, op, 4, rs, rt, offset, 4); break; @@ -19821,28 +29135,39 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; case OPC_LL: /* Load and stores */ check_insn(ctx, ISA_MIPS2); + if (ctx->insn_flags & INSN_R5900) { + check_insn_opc_user_only(ctx, INSN_R5900); + } /* Fallthrough */ case OPC_LWL: case OPC_LWR: check_insn_opc_removed(ctx, ISA_MIPS32R6); /* Fallthrough */ - case OPC_LB ... OPC_LH: - case OPC_LW ... OPC_LHU: + case OPC_LB: + case OPC_LH: + case OPC_LW: + case OPC_LWPC: + case OPC_LBU: + case OPC_LHU: gen_ld(ctx, op, rt, rs, imm); break; case OPC_SWL: case OPC_SWR: check_insn_opc_removed(ctx, ISA_MIPS32R6); /* fall through */ - case OPC_SB ... OPC_SH: + case OPC_SB: + case OPC_SH: case OPC_SW: gen_st(ctx, op, rt, rs, imm); break; case OPC_SC: check_insn(ctx, ISA_MIPS2); check_insn_opc_removed(ctx, ISA_MIPS32R6); - gen_st_cond(ctx, op, rt, rs, imm); - break; + if (ctx->insn_flags & INSN_R5900) { + check_insn_opc_user_only(ctx, INSN_R5900); + } + gen_st_cond(ctx, rt, rs, imm, MO_TESL, false); + break; case OPC_CACHE: check_insn_opc_removed(ctx, ISA_MIPS32R6); check_cp0_enabled(ctx); @@ -19854,8 +29179,12 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) break; case OPC_PREF: check_insn_opc_removed(ctx, ISA_MIPS32R6); - check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); - /* Treat as NOP. */ + if (ctx->insn_flags & INSN_R5900) { + /* Treat as NOP. */ + } else { + check_insn(ctx, ISA_MIPS4 | ISA_MIPS32); + /* Treat as NOP. */ + } break; /* Floating point (COP1). */ @@ -19874,6 +29203,7 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) case OPC_MTHC1: check_cp1_enabled(ctx); check_insn(ctx, ISA_MIPS32R2); + /* fall through */ case OPC_MFC1: case OPC_CFC1: case OPC_MTC1: @@ -20105,8 +29435,13 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) #if defined(TARGET_MIPS64) /* MIPS64 opcodes */ - case OPC_LDL ... OPC_LDR: case OPC_LLD: + if (ctx->insn_flags & INSN_R5900) { + check_insn_opc_user_only(ctx, INSN_R5900); + } + /* fall through */ + case OPC_LDL: + case OPC_LDR: check_insn_opc_removed(ctx, ISA_MIPS32R6); /* fall through */ case OPC_LWU: @@ -20115,7 +29450,8 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) check_mips_64(ctx); gen_ld(ctx, op, rt, rs, imm); break; - case OPC_SDL ... OPC_SDR: + case OPC_SDL: + case OPC_SDR: check_insn_opc_removed(ctx, ISA_MIPS32R6); /* fall through */ case OPC_SD: @@ -20126,8 +29462,11 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) case OPC_SCD: check_insn_opc_removed(ctx, ISA_MIPS32R6); check_insn(ctx, ISA_MIPS3); + if (ctx->insn_flags & INSN_R5900) { + check_insn_opc_user_only(ctx, INSN_R5900); + } check_mips_64(ctx); - gen_st_cond(ctx, op, rt, rs, imm); + gen_st_cond(ctx, rt, rs, imm, MO_TEQ, false); break; case OPC_BNVC: /* OPC_BNEZALC, OPC_BNEC, OPC_DADDI */ if (ctx->insn_flags & ISA_MIPS32R6) { @@ -20180,8 +29519,14 @@ static void decode_opc(CPUMIPSState *env, DisasContext *ctx) } break; case OPC_MSA: /* OPC_MDMX */ - /* MDMX: Not implemented. */ - gen_msa(env, ctx); + if (ctx->insn_flags & INSN_R5900) { +#if defined(TARGET_MIPS64) + gen_mmi_lq(env, ctx); /* MMI_OPC_LQ */ +#endif + } else { + /* MDMX: Not implemented. */ + gen_msa(env, ctx); + } break; case OPC_PCREL: check_insn(ctx, ISA_MIPS32R6); @@ -20203,6 +29548,9 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->saved_pc = -1; ctx->insn_flags = env->insn_flags; ctx->CP0_Config1 = env->CP0_Config1; + ctx->CP0_Config2 = env->CP0_Config2; + ctx->CP0_Config3 = env->CP0_Config3; + ctx->CP0_Config5 = env->CP0_Config5; ctx->btarget = 0; ctx->kscrexist = (env->CP0_Config4 >> CP0C4_KScrExist) & 0xff; ctx->rxi = (env->CP0_Config3 >> CP0C3_RXI) & 1; @@ -20273,7 +29621,10 @@ static void mips_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) int is_slot; is_slot = ctx->hflags & MIPS_HFLAG_BMASK; - if (!(ctx->hflags & MIPS_HFLAG_M16)) { + if (ctx->insn_flags & ISA_NANOMIPS32) { + ctx->opcode = cpu_lduw_code(env, ctx->base.pc_next); + insn_bytes = decode_nanomips_opc(env, ctx); + } else if (!(ctx->hflags & MIPS_HFLAG_M16)) { ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); insn_bytes = 4; decode_opc(env, ctx); @@ -20440,7 +29791,7 @@ void mips_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, env->CP0_Status, env->CP0_Cause, env->CP0_EPC); cpu_fprintf(f, " Config0 0x%08x Config1 0x%08x LLAddr 0x%016" PRIx64 "\n", - env->CP0_Config0, env->CP0_Config1, env->lladdr); + env->CP0_Config0, env->CP0_Config1, env->CP0_LLAddr); cpu_fprintf(f, " Config2 0x%08x Config3 0x%08x\n", env->CP0_Config2, env->CP0_Config3); cpu_fprintf(f, " Config4 0x%08x Config5 0x%08x\n", @@ -20498,6 +29849,33 @@ void mips_tcg_init(void) fpu_fcr31 = tcg_global_mem_new_i32(cpu_env, offsetof(CPUMIPSState, active_fpu.fcr31), "fcr31"); + cpu_lladdr = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, lladdr), + "lladdr"); + cpu_llval = tcg_global_mem_new(cpu_env, offsetof(CPUMIPSState, llval), + "llval"); + +#if defined(TARGET_MIPS64) + cpu_mmr[0] = NULL; + for (i = 1; i < 32; i++) { + cpu_mmr[i] = tcg_global_mem_new_i64(cpu_env, + offsetof(CPUMIPSState, + active_tc.mmr[i]), + regnames[i]); + } +#endif + +#if !defined(TARGET_MIPS64) + for (i = 0; i < NUMBER_OF_MXU_REGISTERS - 1; i++) { + mxu_gpr[i] = tcg_global_mem_new(cpu_env, + offsetof(CPUMIPSState, + active_tc.mxu_gpr[i]), + mxuregnames[i]); + } + + mxu_CR = tcg_global_mem_new(cpu_env, + offsetof(CPUMIPSState, active_tc.mxu_cr), + mxuregnames[NUMBER_OF_MXU_REGISTERS - 1]); +#endif } #include "translate_init.inc.c" @@ -20519,7 +29897,7 @@ bool cpu_supports_cps_smp(const char *cpu_type) return (mcc->cpu_def->CP0_Config3 & (1 << CP0C3_CMGCR)) != 0; } -bool cpu_supports_isa(const char *cpu_type, unsigned int isa) +bool cpu_supports_isa(const char *cpu_type, uint64_t isa) { const MIPSCPUClass *mcc = MIPS_CPU_CLASS(object_class_by_name(cpu_type)); return (mcc->cpu_def->insn_flags & isa) != 0; @@ -20712,8 +30090,26 @@ void cpu_state_reset(CPUMIPSState *env) env->CP0_Status |= (1 << CP0St_FR); } - if (env->CP0_Config3 & (1 << CP0C3_ISA)) { - /* microMIPS on reset when Config3.ISA == {1, 3} */ + if (env->insn_flags & ISA_MIPS32R6) { + /* PTW = 1 */ + env->CP0_PWSize = 0x40; + /* GDI = 12 */ + /* UDI = 12 */ + /* MDI = 12 */ + /* PRI = 12 */ + /* PTEI = 2 */ + env->CP0_PWField = 0x0C30C302; + } else { + /* GDI = 0 */ + /* UDI = 0 */ + /* MDI = 0 */ + /* PRI = 0 */ + /* PTEI = 2 */ + env->CP0_PWField = 0x02; + } + + if (env->CP0_Config3 & (1 << CP0C3_ISA) & (1 << (CP0C3_ISA + 1))) { + /* microMIPS on reset when Config3.ISA is 3 */ env->hflags |= MIPS_HFLAG_M16; } diff --git a/target/mips/translate_init.inc.c b/target/mips/translate_init.inc.c index c7ba6ee5f9..bf559aff08 100644 --- a/target/mips/translate_init.inc.c +++ b/target/mips/translate_init.inc.c @@ -320,7 +320,7 @@ const mips_def_t mips_defs[] = .CP1_fcr31_rw_bitmask = 0xFF83FFFF, .SEGBITS = 32, .PABITS = 32, - .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_DSPR2, + .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_DSP_R2, .mmu_type = MMU_TYPE_R4000, }, { @@ -449,6 +449,46 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS32R6 | ASE_MICROMIPS, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "I7200", + .CP0_PRid = 0x00010000, + .CP0_Config0 = MIPS_CONFIG0 | (1 << CP0C0_MM) | (0x2 << CP0C0_AR) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = (1U << CP0C1_M) | (15 << 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_EP), + .CP0_Config2 = MIPS_CONFIG2, + .CP0_Config3 = MIPS_CONFIG3 | (1U << CP0C3_M) | (1 << CP0C3_CMGCR) | + (1 << CP0C3_BI) | (1 << CP0C3_SC) | (3 << CP0C3_MMAR) | + (1 << CP0C3_ISA_ON_EXC) | (1 << CP0C3_ISA) | + (1 << CP0C3_ULRI) | (1 << CP0C3_RXI) | + (1 << CP0C3_DSP2P) | (1 << CP0C3_DSPP) | + (1 << CP0C3_CTXTC) | (1 << CP0C3_VInt) | + (1 << CP0C3_CDMM) | (1 << CP0C3_MT) | (1 << CP0C3_TL), + .CP0_Config4 = MIPS_CONFIG4 | (0xfc << CP0C4_KScrExist) | + (2 << CP0C4_IE) | (1U << CP0C4_M), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_MVH) | (1 << CP0C5_LLB), + .CP0_Config5_rw_bitmask = (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | + (1 << CP0C5_UFE), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 0, + .SYNCI_Step = 32, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x3158FF1F, + .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | + (1U << CP0PG_RIE), + .CP0_PageGrain_rw_bitmask = 0, + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x02 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), + .SEGBITS = 32, + .PABITS = 32, + .insn_flags = CPU_NANOMIPS32 | ASE_DSP | ASE_DSP_R2 | ASE_DSP_R3 | + ASE_MT, + .mmu_type = MMU_TYPE_R4000, + }, #if defined(TARGET_MIPS64) { .name = "R4000", @@ -681,6 +721,46 @@ const mips_def_t mips_defs[] = .insn_flags = CPU_MIPS64R6 | ASE_MSA, .mmu_type = MMU_TYPE_R4000, }, + { + .name = "I6500", + .CP0_PRid = 0x1B000, + .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) | (0x2 << CP0C0_AT) | + (MMU_TYPE_R4000 << CP0C0_MT), + .CP0_Config1 = MIPS_CONFIG1 | (1 << CP0C1_FP) | (15 << CP0C1_MMU) | + (2 << CP0C1_IS) | (5 << CP0C1_IL) | (3 << CP0C1_IA) | + (2 << CP0C1_DS) | (5 << 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_CMGCR) | (1 << CP0C3_MSAP) | + (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) | (3 << CP0C4_IE) | + (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | + (1 << CP0C5_LLB) | (1 << CP0C5_MRP), + .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | + (1 << CP0C5_FRE) | (1 << CP0C5_UFE), + .CP0_LLAddr_rw_bitmask = 0, + .CP0_LLAddr_shift = 0, + .SYNCI_Step = 64, + .CCRes = 2, + .CP0_Status_rw_bitmask = 0x30D8FFFF, + .CP0_PageGrain = (1 << CP0PG_IEC) | (1 << CP0PG_XIE) | + (1U << CP0PG_RIE), + .CP0_PageGrain_rw_bitmask = (1 << CP0PG_ELPA), + .CP0_EBaseWG_rw_bitmask = (1 << CP0EBase_WG), + .CP1_fcr0 = (1 << FCR0_FREP) | (1 << FCR0_HAS2008) | (1 << FCR0_F64) | + (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) | + (1 << FCR0_S) | (0x03 << FCR0_PRID) | (0x0 << FCR0_REV), + .CP1_fcr31 = (1 << FCR31_ABS2008) | (1 << FCR31_NAN2008), + .CP1_fcr31_rw_bitmask = 0x0103FFFF, + .MSAIR = 0x03 << MSAIR_ProcID, + .SEGBITS = 48, + .PABITS = 48, + .insn_flags = CPU_MIPS64R6 | ASE_MSA, + .mmu_type = MMU_TYPE_R4000, + }, { .name = "Loongson-2E", .CP0_PRid = 0x6302, @@ -722,7 +802,7 @@ const mips_def_t mips_defs[] = .mmu_type = MMU_TYPE_R4000, }, { - /* A generic CPU providing MIPS64 ASE DSP 2 features. + /* A generic CPU providing MIPS64 DSP R2 ASE features. FIXME: Eventually this should be replaced by a real CPU model. */ .name = "mips64dspr2", .CP0_PRid = 0x00010000, @@ -747,7 +827,7 @@ const mips_def_t mips_defs[] = .CP1_fcr31_rw_bitmask = 0xFF83FFFF, .SEGBITS = 42, .PABITS = 36, - .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSPR2, + .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSP_R2, .mmu_type = MMU_TYPE_R4000, }, diff --git a/target/moxie/cpu.c b/target/moxie/cpu.c index 8d67eb6727..46434e65ba 100644 --- a/target/moxie/cpu.c +++ b/target/moxie/cpu.c @@ -13,7 +13,7 @@ * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ diff --git a/target/moxie/cpu.h b/target/moxie/cpu.h index d40f1e6c45..080df4ee6f 100644 --- a/target/moxie/cpu.h +++ b/target/moxie/cpu.h @@ -6,14 +6,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ diff --git a/target/moxie/helper.c b/target/moxie/helper.c index 5b1532b837..f3d8ee7d6b 100644 --- a/target/moxie/helper.c +++ b/target/moxie/helper.c @@ -6,14 +6,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ diff --git a/target/moxie/mmu.c b/target/moxie/mmu.c index bd90b1eebc..87783a36f8 100644 --- a/target/moxie/mmu.c +++ b/target/moxie/mmu.c @@ -6,14 +6,14 @@ * 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. + * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ diff --git a/target/moxie/translate.c b/target/moxie/translate.c index 29da02bc05..68ca223e22 100644 --- a/target/moxie/translate.c +++ b/target/moxie/translate.c @@ -5,7 +5,7 @@ * * 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 + * 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 @@ -13,7 +13,7 @@ * 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 General Public License + * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index fb7cb5c507..541b2a66c7 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/target/openrisc/disas.c b/target/openrisc/disas.c index 4bfd2dd8a6..bc63093ee9 100644 --- a/target/openrisc/disas.c +++ b/target/openrisc/disas.c @@ -51,12 +51,11 @@ int print_insn_or1k(bfd_vma addr, disassemble_info *info) return 4; } -#define INSN(opcode, format, ...) \ -static bool trans_l_##opcode(disassemble_info *info, \ - arg_l_##opcode *a, uint32_t insn) \ -{ \ - output("l." #opcode, format, ##__VA_ARGS__); \ - return true; \ +#define INSN(opcode, format, ...) \ +static bool trans_l_##opcode(disassemble_info *info, arg_l_##opcode *a) \ +{ \ + output("l." #opcode, format, ##__VA_ARGS__); \ + return true; \ } INSN(add, "r%d, r%d, r%d", a->d, a->a, a->b) @@ -146,12 +145,12 @@ INSN(psync, "") INSN(csync, "") INSN(rfe, "") -#define FP_INSN(opcode, suffix, format, ...) \ -static bool trans_lf_##opcode##_##suffix(disassemble_info *info, \ - arg_lf_##opcode##_##suffix *a, uint32_t insn) \ -{ \ - output("lf." #opcode "." #suffix, format, ##__VA_ARGS__); \ - return true; \ +#define FP_INSN(opcode, suffix, format, ...) \ +static bool trans_lf_##opcode##_##suffix(disassemble_info *info, \ + arg_lf_##opcode##_##suffix *a) \ +{ \ + output("lf." #opcode "." #suffix, format, ##__VA_ARGS__); \ + return true; \ } FP_INSN(add, s, "r%d, r%d, r%d", a->d, a->a, a->b) diff --git a/target/openrisc/exception.h b/target/openrisc/exception.h index 4ec56b4653..c9c4e471d2 100644 --- a/target/openrisc/exception.h +++ b/target/openrisc/exception.h @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/target/openrisc/gdbstub.c b/target/openrisc/gdbstub.c index f9af6507f3..43b1a18d4b 100644 --- a/target/openrisc/gdbstub.c +++ b/target/openrisc/gdbstub.c @@ -7,7 +7,7 @@ * 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. + * 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 diff --git a/target/openrisc/helper.h b/target/openrisc/helper.h index 9db9bf3963..96d79a8113 100644 --- a/target/openrisc/helper.h +++ b/target/openrisc/helper.h @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index 9c5489f5f7..ab4ea88b69 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 diff --git a/target/openrisc/machine.c b/target/openrisc/machine.c index 1eedbf3dbe..5d822f7ab1 100644 --- a/target/openrisc/machine.c +++ b/target/openrisc/machine.c @@ -49,7 +49,8 @@ static const VMStateDescription vmstate_cpu_tlb = { } }; -static int get_sr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) +static int get_sr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) { CPUOpenRISCState *env = opaque; cpu_set_sr(env, qemu_get_be32(f)); @@ -57,7 +58,7 @@ static int get_sr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) } static int put_sr(QEMUFile *f, void *opaque, size_t size, - VMStateField *field, QJSON *vmdesc) + const VMStateField *field, QJSON *vmdesc) { CPUOpenRISCState *env = opaque; qemu_put_be32(f, cpu_get_sr(env)); diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index b66a45c1e0..05f66c455b 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index a271cd3903..89680f882d 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -434,105 +434,105 @@ static void gen_msbu(DisasContext *dc, TCGv srca, TCGv srcb) gen_ove_cy(dc); } -static bool trans_l_add(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_add(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_add(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_addc(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_addc(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_addc(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sub(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_sub(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_sub(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_and(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_and(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_and_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_or(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_or(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_or_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_xor(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_xor(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_xor_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sll(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_sll(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_shl_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_srl(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_srl(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_shr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sra(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_sra(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_sar_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_ror(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_ror(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); tcg_gen_rotr_tl(cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_exths(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_l_exths(DisasContext *dc, arg_da *a) { check_r0_write(a->d); tcg_gen_ext16s_tl(cpu_R[a->d], cpu_R[a->a]); return true; } -static bool trans_l_extbs(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_l_extbs(DisasContext *dc, arg_da *a) { check_r0_write(a->d); tcg_gen_ext8s_tl(cpu_R[a->d], cpu_R[a->a]); return true; } -static bool trans_l_exthz(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_l_exthz(DisasContext *dc, arg_da *a) { check_r0_write(a->d); tcg_gen_ext16u_tl(cpu_R[a->d], cpu_R[a->a]); return true; } -static bool trans_l_extbz(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_l_extbz(DisasContext *dc, arg_da *a) { check_r0_write(a->d); tcg_gen_ext8u_tl(cpu_R[a->d], cpu_R[a->a]); return true; } -static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_cmov(DisasContext *dc, arg_dab *a) { TCGv zero; @@ -544,7 +544,7 @@ static bool trans_l_cmov(DisasContext *dc, arg_dab *a, uint32_t insn) return true; } -static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_l_ff1(DisasContext *dc, arg_da *a) { check_r0_write(a->d); tcg_gen_ctzi_tl(cpu_R[a->d], cpu_R[a->a], -1); @@ -552,7 +552,7 @@ static bool trans_l_ff1(DisasContext *dc, arg_da *a, uint32_t insn) return true; } -static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_l_fl1(DisasContext *dc, arg_da *a) { check_r0_write(a->d); tcg_gen_clzi_tl(cpu_R[a->d], cpu_R[a->a], TARGET_LONG_BITS); @@ -560,47 +560,47 @@ static bool trans_l_fl1(DisasContext *dc, arg_da *a, uint32_t insn) return true; } -static bool trans_l_mul(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_mul(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_mul(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_mulu(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_mulu(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_mulu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_div(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_div(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_div(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_divu(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_l_divu(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_divu(dc, cpu_R[a->d], cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_muld(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_l_muld(DisasContext *dc, arg_ab *a) { gen_muld(dc, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_muldu(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_l_muldu(DisasContext *dc, arg_ab *a) { gen_muldu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) +static bool trans_l_j(DisasContext *dc, arg_l_j *a) { target_ulong tmp_pc = dc->base.pc_next + a->n * 4; @@ -610,7 +610,7 @@ static bool trans_l_j(DisasContext *dc, arg_l_j *a, uint32_t insn) return true; } -static bool trans_l_jal(DisasContext *dc, arg_l_jal *a, uint32_t insn) +static bool trans_l_jal(DisasContext *dc, arg_l_jal *a) { target_ulong tmp_pc = dc->base.pc_next + a->n * 4; target_ulong ret_pc = dc->base.pc_next + 8; @@ -640,26 +640,26 @@ static void do_bf(DisasContext *dc, arg_l_bf *a, TCGCond cond) dc->delayed_branch = 2; } -static bool trans_l_bf(DisasContext *dc, arg_l_bf *a, uint32_t insn) +static bool trans_l_bf(DisasContext *dc, arg_l_bf *a) { do_bf(dc, a, TCG_COND_NE); return true; } -static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a, uint32_t insn) +static bool trans_l_bnf(DisasContext *dc, arg_l_bf *a) { do_bf(dc, a, TCG_COND_EQ); return true; } -static bool trans_l_jr(DisasContext *dc, arg_l_jr *a, uint32_t insn) +static bool trans_l_jr(DisasContext *dc, arg_l_jr *a) { tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); dc->delayed_branch = 2; return true; } -static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) +static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a) { tcg_gen_mov_tl(jmp_pc, cpu_R[a->b]); tcg_gen_movi_tl(cpu_R[9], dc->base.pc_next + 8); @@ -667,7 +667,7 @@ static bool trans_l_jalr(DisasContext *dc, arg_l_jalr *a, uint32_t insn) return true; } -static bool trans_l_lwa(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lwa(DisasContext *dc, arg_load *a) { TCGv ea; @@ -692,43 +692,43 @@ static void do_load(DisasContext *dc, arg_load *a, TCGMemOp mop) tcg_temp_free(ea); } -static bool trans_l_lwz(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lwz(DisasContext *dc, arg_load *a) { do_load(dc, a, MO_TEUL); return true; } -static bool trans_l_lws(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lws(DisasContext *dc, arg_load *a) { do_load(dc, a, MO_TESL); return true; } -static bool trans_l_lbz(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lbz(DisasContext *dc, arg_load *a) { do_load(dc, a, MO_UB); return true; } -static bool trans_l_lbs(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lbs(DisasContext *dc, arg_load *a) { do_load(dc, a, MO_SB); return true; } -static bool trans_l_lhz(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lhz(DisasContext *dc, arg_load *a) { do_load(dc, a, MO_TEUW); return true; } -static bool trans_l_lhs(DisasContext *dc, arg_load *a, uint32_t insn) +static bool trans_l_lhs(DisasContext *dc, arg_load *a) { do_load(dc, a, MO_TESW); return true; } -static bool trans_l_swa(DisasContext *dc, arg_store *a, uint32_t insn) +static bool trans_l_swa(DisasContext *dc, arg_store *a) { TCGv ea, val; TCGLabel *lab_fail, *lab_done; @@ -771,30 +771,30 @@ static void do_store(DisasContext *dc, arg_store *a, TCGMemOp mop) tcg_temp_free(t0); } -static bool trans_l_sw(DisasContext *dc, arg_store *a, uint32_t insn) +static bool trans_l_sw(DisasContext *dc, arg_store *a) { do_store(dc, a, MO_TEUL); return true; } -static bool trans_l_sb(DisasContext *dc, arg_store *a, uint32_t insn) +static bool trans_l_sb(DisasContext *dc, arg_store *a) { do_store(dc, a, MO_UB); return true; } -static bool trans_l_sh(DisasContext *dc, arg_store *a, uint32_t insn) +static bool trans_l_sh(DisasContext *dc, arg_store *a) { do_store(dc, a, MO_TEUW); return true; } -static bool trans_l_nop(DisasContext *dc, arg_l_nop *a, uint32_t insn) +static bool trans_l_nop(DisasContext *dc, arg_l_nop *a) { return true; } -static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn) +static bool trans_l_addi(DisasContext *dc, arg_rri *a) { TCGv t0; @@ -805,7 +805,7 @@ static bool trans_l_addi(DisasContext *dc, arg_rri *a, uint32_t insn) return true; } -static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn) +static bool trans_l_addic(DisasContext *dc, arg_rri *a) { TCGv t0; @@ -816,7 +816,7 @@ static bool trans_l_addic(DisasContext *dc, arg_rri *a, uint32_t insn) return true; } -static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn) +static bool trans_l_muli(DisasContext *dc, arg_rri *a) { TCGv t0; @@ -827,7 +827,7 @@ static bool trans_l_muli(DisasContext *dc, arg_rri *a, uint32_t insn) return true; } -static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) +static bool trans_l_maci(DisasContext *dc, arg_l_maci *a) { TCGv t0; @@ -837,28 +837,28 @@ static bool trans_l_maci(DisasContext *dc, arg_l_maci *a, uint32_t insn) return true; } -static bool trans_l_andi(DisasContext *dc, arg_rrk *a, uint32_t insn) +static bool trans_l_andi(DisasContext *dc, arg_rrk *a) { check_r0_write(a->d); tcg_gen_andi_tl(cpu_R[a->d], cpu_R[a->a], a->k); return true; } -static bool trans_l_ori(DisasContext *dc, arg_rrk *a, uint32_t insn) +static bool trans_l_ori(DisasContext *dc, arg_rrk *a) { check_r0_write(a->d); tcg_gen_ori_tl(cpu_R[a->d], cpu_R[a->a], a->k); return true; } -static bool trans_l_xori(DisasContext *dc, arg_rri *a, uint32_t insn) +static bool trans_l_xori(DisasContext *dc, arg_rri *a) { check_r0_write(a->d); tcg_gen_xori_tl(cpu_R[a->d], cpu_R[a->a], a->i); return true; } -static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) +static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a) { check_r0_write(a->d); @@ -873,7 +873,7 @@ static bool trans_l_mfspr(DisasContext *dc, arg_l_mfspr *a, uint32_t insn) return true; } -static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) +static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a) { if (is_user(dc)) { gen_illegal_exception(dc); @@ -901,66 +901,66 @@ static bool trans_l_mtspr(DisasContext *dc, arg_l_mtspr *a, uint32_t insn) return true; } -static bool trans_l_mac(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_l_mac(DisasContext *dc, arg_ab *a) { gen_mac(dc, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_msb(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_l_msb(DisasContext *dc, arg_ab *a) { gen_msb(dc, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_macu(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_l_macu(DisasContext *dc, arg_ab *a) { gen_macu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_msbu(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_l_msbu(DisasContext *dc, arg_ab *a) { gen_msbu(dc, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_slli(DisasContext *dc, arg_dal *a, uint32_t insn) +static bool trans_l_slli(DisasContext *dc, arg_dal *a) { check_r0_write(a->d); tcg_gen_shli_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; } -static bool trans_l_srli(DisasContext *dc, arg_dal *a, uint32_t insn) +static bool trans_l_srli(DisasContext *dc, arg_dal *a) { check_r0_write(a->d); tcg_gen_shri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; } -static bool trans_l_srai(DisasContext *dc, arg_dal *a, uint32_t insn) +static bool trans_l_srai(DisasContext *dc, arg_dal *a) { check_r0_write(a->d); tcg_gen_sari_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; } -static bool trans_l_rori(DisasContext *dc, arg_dal *a, uint32_t insn) +static bool trans_l_rori(DisasContext *dc, arg_dal *a) { check_r0_write(a->d); tcg_gen_rotri_tl(cpu_R[a->d], cpu_R[a->a], a->l & (TARGET_LONG_BITS - 1)); return true; } -static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a, uint32_t insn) +static bool trans_l_movhi(DisasContext *dc, arg_l_movhi *a) { check_r0_write(a->d); tcg_gen_movi_tl(cpu_R[a->d], a->k << 16); return true; } -static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) +static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a) { check_r0_write(a->d); tcg_gen_trunc_i64_tl(cpu_R[a->d], cpu_mac); @@ -968,127 +968,127 @@ static bool trans_l_macrc(DisasContext *dc, arg_l_macrc *a, uint32_t insn) return true; } -static bool trans_l_sfeq(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfeq(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfne(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfne(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfgtu(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfgeu(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfltu(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfltu(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfleu(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfleu(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfgts(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfgts(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfges(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfges(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sflts(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sflts(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfles(DisasContext *dc, arg_ab *a, TCGCond cond) +static bool trans_l_sfles(DisasContext *dc, arg_ab *a) { tcg_gen_setcond_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], cpu_R[a->b]); return true; } -static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfeqi(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_EQ, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfnei(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfnei(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_NE, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfgtui(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_GTU, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfgeui(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_GEU, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfltui(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfltui(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_LTU, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfleui(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfleui(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_LEU, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfgtsi(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_GT, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfgesi(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_GE, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sfltsi(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_LT, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sflesi(DisasContext *dc, arg_ai *a, TCGCond cond) +static bool trans_l_sflesi(DisasContext *dc, arg_ai *a) { tcg_gen_setcondi_tl(TCG_COND_LE, cpu_sr_f, cpu_R[a->a], a->i); return true; } -static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) +static bool trans_l_sys(DisasContext *dc, arg_l_sys *a) { tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_SYSCALL); @@ -1096,7 +1096,7 @@ static bool trans_l_sys(DisasContext *dc, arg_l_sys *a, uint32_t insn) return true; } -static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) +static bool trans_l_trap(DisasContext *dc, arg_l_trap *a) { tcg_gen_movi_tl(cpu_pc, dc->base.pc_next); gen_exception(dc, EXCP_TRAP); @@ -1104,23 +1104,23 @@ static bool trans_l_trap(DisasContext *dc, arg_l_trap *a, uint32_t insn) return true; } -static bool trans_l_msync(DisasContext *dc, arg_l_msync *a, uint32_t insn) +static bool trans_l_msync(DisasContext *dc, arg_l_msync *a) { tcg_gen_mb(TCG_MO_ALL); return true; } -static bool trans_l_psync(DisasContext *dc, arg_l_psync *a, uint32_t insn) +static bool trans_l_psync(DisasContext *dc, arg_l_psync *a) { return true; } -static bool trans_l_csync(DisasContext *dc, arg_l_csync *a, uint32_t insn) +static bool trans_l_csync(DisasContext *dc, arg_l_csync *a) { return true; } -static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a, uint32_t insn) +static bool trans_l_rfe(DisasContext *dc, arg_l_rfe *a) { if (is_user(dc)) { gen_illegal_exception(dc); @@ -1162,49 +1162,49 @@ static void do_fpcmp(DisasContext *dc, arg_ab *a, gen_helper_update_fpcsr(cpu_env); } -static bool trans_lf_add_s(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_lf_add_s(DisasContext *dc, arg_dab *a) { do_fp3(dc, a, gen_helper_float_add_s); return true; } -static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_lf_sub_s(DisasContext *dc, arg_dab *a) { do_fp3(dc, a, gen_helper_float_sub_s); return true; } -static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_lf_mul_s(DisasContext *dc, arg_dab *a) { do_fp3(dc, a, gen_helper_float_mul_s); return true; } -static bool trans_lf_div_s(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_lf_div_s(DisasContext *dc, arg_dab *a) { do_fp3(dc, a, gen_helper_float_div_s); return true; } -static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_lf_rem_s(DisasContext *dc, arg_dab *a) { do_fp3(dc, a, gen_helper_float_rem_s); return true; } -static bool trans_lf_itof_s(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_lf_itof_s(DisasContext *dc, arg_da *a) { do_fp2(dc, a, gen_helper_itofs); return true; } -static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a, uint32_t insn) +static bool trans_lf_ftoi_s(DisasContext *dc, arg_da *a) { do_fp2(dc, a, gen_helper_ftois); return true; } -static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) +static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a) { check_r0_write(a->d); gen_helper_float_madd_s(cpu_R[a->d], cpu_env, cpu_R[a->d], @@ -1213,37 +1213,37 @@ static bool trans_lf_madd_s(DisasContext *dc, arg_dab *a, uint32_t insn) return true; } -static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_lf_sfeq_s(DisasContext *dc, arg_ab *a) { do_fpcmp(dc, a, gen_helper_float_eq_s, false, false); return true; } -static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_lf_sfne_s(DisasContext *dc, arg_ab *a) { do_fpcmp(dc, a, gen_helper_float_eq_s, true, false); return true; } -static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_lf_sfgt_s(DisasContext *dc, arg_ab *a) { do_fpcmp(dc, a, gen_helper_float_lt_s, false, true); return true; } -static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_lf_sfge_s(DisasContext *dc, arg_ab *a) { do_fpcmp(dc, a, gen_helper_float_le_s, false, true); return true; } -static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_lf_sflt_s(DisasContext *dc, arg_ab *a) { do_fpcmp(dc, a, gen_helper_float_lt_s, false, false); return true; } -static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a, uint32_t insn) +static bool trans_lf_sfle_s(DisasContext *dc, arg_ab *a) { do_fpcmp(dc, a, gen_helper_float_le_s, false, false); return true; diff --git a/target/ppc/arch_dump.c b/target/ppc/arch_dump.c index cc1460e4e3..9ab04b2c38 100644 --- a/target/ppc/arch_dump.c +++ b/target/ppc/arch_dump.c @@ -17,6 +17,7 @@ #include "elf.h" #include "sysemu/dump.h" #include "sysemu/kvm.h" +#include "exec/helper-proto.h" #ifdef TARGET_PPC64 #define ELFCLASS ELFCLASS64 @@ -140,7 +141,8 @@ static void ppc_write_elf_fpregset(NoteFuncArg *arg, PowerPCCPU *cpu) memset(fpregset, 0, sizeof(*fpregset)); for (i = 0; i < 32; i++) { - fpregset->fpr[i] = cpu_to_dump64(s, cpu->env.fpr[i]); + uint64_t *fpr = cpu_fpr_ptr(&cpu->env, i); + fpregset->fpr[i] = cpu_to_dump64(s, *fpr); } fpregset->fpscr = cpu_to_dump_reg(s, cpu->env.fpscr); } @@ -158,6 +160,7 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) for (i = 0; i < 32; i++) { bool needs_byteswap; + ppc_avr_t *avr = cpu_avr_ptr(&cpu->env, i); #ifdef HOST_WORDS_BIGENDIAN needs_byteswap = s->dump_info.d_endian == ELFDATA2LSB; @@ -166,14 +169,14 @@ static void ppc_write_elf_vmxregset(NoteFuncArg *arg, PowerPCCPU *cpu) #endif if (needs_byteswap) { - vmxregset->avr[i].u64[0] = bswap64(cpu->env.avr[i].u64[1]); - vmxregset->avr[i].u64[1] = bswap64(cpu->env.avr[i].u64[0]); + vmxregset->avr[i].u64[0] = bswap64(avr->u64[1]); + vmxregset->avr[i].u64[1] = bswap64(avr->u64[0]); } else { - vmxregset->avr[i].u64[0] = cpu->env.avr[i].u64[0]; - vmxregset->avr[i].u64[1] = cpu->env.avr[i].u64[1]; + vmxregset->avr[i].u64[0] = avr->u64[0]; + vmxregset->avr[i].u64[1] = avr->u64[1]; } } - vmxregset->vscr.u32[3] = cpu_to_dump32(s, cpu->env.vscr); + vmxregset->vscr.u32[3] = cpu_to_dump32(s, helper_mfvscr(&cpu->env)); } static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) @@ -188,7 +191,8 @@ static void ppc_write_elf_vsxregset(NoteFuncArg *arg, PowerPCCPU *cpu) memset(vsxregset, 0, sizeof(*vsxregset)); for (i = 0; i < 32; i++) { - vsxregset->vsr[i] = cpu_to_dump64(s, cpu->env.vsr[i]); + uint64_t *vsrl = cpu_vsrl_ptr(&cpu->env, i); + vsxregset->vsr[i] = cpu_to_dump64(s, *vsrl); } } diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index 6c9bfde2d2..7c75963e3c 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -741,26 +741,8 @@ "PowerPC 7457A v1.2 (G4)") /* 64 bits PowerPC */ #if defined (TARGET_PPC64) - POWERPC_DEF("power5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, - "POWER5+ v2.1") - POWERPC_DEF("power7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, - "POWER7 v2.3") - POWERPC_DEF("power7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, - "POWER7+ v2.1") - 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("power9_v1.0", CPU_POWERPC_POWER9_DD1, POWER9, - "POWER9 v1.0") - POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9, - "POWER9 v2.0") - POWERPC_DEF("970fx_v1.0", CPU_POWERPC_970FX_v10, 970, "PowerPC 970FX v1.0 (G5)") POWERPC_DEF("970fx_v2.0", CPU_POWERPC_970FX_v20, 970, @@ -775,6 +757,22 @@ "PowerPC 970MP v1.0") POWERPC_DEF("970mp_v1.1", CPU_POWERPC_970MP_v11, 970, "PowerPC 970MP v1.1") + POWERPC_DEF("power5+_v2.1", CPU_POWERPC_POWER5P_v21, POWER5P, + "POWER5+ v2.1") + POWERPC_DEF("power7_v2.3", CPU_POWERPC_POWER7_v23, POWER7, + "POWER7 v2.3") + POWERPC_DEF("power7+_v2.1", CPU_POWERPC_POWER7P_v21, POWER7, + "POWER7+ v2.1") + 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("power9_v1.0", CPU_POWERPC_POWER9_DD1, POWER9, + "POWER9 v1.0") + POWERPC_DEF("power9_v2.0", CPU_POWERPC_POWER9_DD20, POWER9, + "POWER9 v2.0") #endif /* defined (TARGET_PPC64) */ /***************************************************************************/ @@ -940,6 +938,9 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "7457a", "7457a_v1.2" }, { "apollo7pm", "7457a_v1.0" }, #if defined(TARGET_PPC64) + { "970", "970_v2.2" }, + { "970fx", "970fx_v3.1" }, + { "970mp", "970mp_v1.1" }, { "power5+", "power5+_v2.1" }, { "power5gs", "power5+_v2.1" }, { "power7", "power7_v2.3" }, @@ -948,9 +949,6 @@ PowerPCCPUAlias ppc_cpu_aliases[] = { { "power8", "power8_v2.0" }, { "power8nvl", "power8nvl_v1.0" }, { "power9", "power9_v2.0" }, - { "970", "970_v2.2" }, - { "970fx", "970fx_v3.1" }, - { "970mp", "970mp_v1.1" }, #endif /* Generic PowerPCs */ diff --git a/target/ppc/cpu-qom.h b/target/ppc/cpu-qom.h index 433a71e484..be9b4c30c3 100644 --- a/target/ppc/cpu-qom.h +++ b/target/ppc/cpu-qom.h @@ -24,8 +24,6 @@ #ifdef TARGET_PPC64 #define TYPE_POWERPC_CPU "powerpc64-cpu" -#elif defined(TARGET_PPCEMB) -#define TYPE_POWERPC_CPU "embedded-powerpc-cpu" #else #define TYPE_POWERPC_CPU "powerpc-cpu" #endif @@ -115,6 +113,8 @@ enum powerpc_excp_t { POWERPC_EXCP_POWER7, /* POWER8 exception model */ POWERPC_EXCP_POWER8, + /* POWER9 exception model */ + POWERPC_EXCP_POWER9, }; /*****************************************************************************/ @@ -124,6 +124,7 @@ typedef enum { PPC_PM_NAP, PPC_PM_SLEEP, PPC_PM_RVWINKLE, + PPC_PM_STOP, } powerpc_pm_insn_t; /*****************************************************************************/ @@ -141,6 +142,8 @@ enum powerpc_input_t { PPC_FLAGS_INPUT_970, /* PowerPC POWER7 bus */ PPC_FLAGS_INPUT_POWER7, + /* PowerPC POWER9 bus */ + PPC_FLAGS_INPUT_POWER9, /* PowerPC 401 bus */ PPC_FLAGS_INPUT_401, /* Freescale RCPU bus */ @@ -181,8 +184,13 @@ typedef struct PowerPCCPUClass { uint32_t flags; int bfd_mach; uint32_t l1_dcache_size, l1_icache_size; +#ifndef CONFIG_USER_ONLY + unsigned int gdb_num_sprs; + const char *gdb_spr_xml; +#endif const PPCHash64Options *hash64_opts; struct ppc_radix_page_info *radix_page_info; + uint32_t lrg_decr_bits; void (*init_proc)(CPUPPCState *env); int (*check_pow)(CPUPPCState *env); int (*handle_mmu_fault)(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 4edcf62cf7..0707177584 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -52,23 +52,7 @@ #else /* defined (TARGET_PPC64) */ /* PowerPC 32 definitions */ #define TARGET_LONG_BITS 32 - -#if defined(TARGET_PPCEMB) -/* Specific definitions for PowerPC embedded */ -/* BookE have 36 bits physical address space */ -#if defined(CONFIG_USER_ONLY) -/* It looks like a lot of Linux programs assume page size - * is 4kB long. This is evil, but we have to deal with it... - */ #define TARGET_PAGE_BITS 12 -#else /* defined(CONFIG_USER_ONLY) */ -/* Pages can be 1 kB small */ -#define TARGET_PAGE_BITS 10 -#endif /* defined(CONFIG_USER_ONLY) */ -#else /* defined(TARGET_PPCEMB) */ -/* "standard" PowerPC 32 definitions */ -#define TARGET_PAGE_BITS 12 -#endif /* defined(TARGET_PPCEMB) */ #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 @@ -86,26 +70,14 @@ #define PPC_ELF_MACHINE EM_PPC #endif -#define PPC_BIT(bit) (0x8000000000000000UL >> (bit)) -#define PPC_BIT32(bit) (0x80000000UL >> (bit)) -#define PPC_BIT8(bit) (0x80UL >> (bit)) +#define PPC_BIT(bit) (0x8000000000000000ULL >> (bit)) +#define PPC_BIT32(bit) (0x80000000 >> (bit)) +#define PPC_BIT8(bit) (0x80 >> (bit)) #define PPC_BITMASK(bs, be) ((PPC_BIT(bs) - PPC_BIT(be)) | PPC_BIT(bs)) #define PPC_BITMASK32(bs, be) ((PPC_BIT32(bs) - PPC_BIT32(be)) | \ PPC_BIT32(bs)) #define PPC_BITMASK8(bs, be) ((PPC_BIT8(bs) - PPC_BIT8(be)) | PPC_BIT8(bs)) -#if HOST_LONG_BITS == 32 -# define MASK_TO_LSH(m) (__builtin_ffsll(m) - 1) -#elif HOST_LONG_BITS == 64 -# define MASK_TO_LSH(m) (__builtin_ffsl(m) - 1) -#else -# error Unknown sizeof long -#endif - -#define GETFIELD(m, v) (((v) & (m)) >> MASK_TO_LSH(m)) -#define SETFIELD(m, v, val) \ - (((v) & ~(m)) | ((((typeof(v))(val)) << MASK_TO_LSH(m)) & (m))) - /*****************************************************************************/ /* Exception vectors definitions */ enum { @@ -188,8 +160,10 @@ enum { /* Server doorbell variants */ POWERPC_EXCP_SDOOR = 99, POWERPC_EXCP_SDOOR_HV = 100, + /* ISA 3.00 additions */ + POWERPC_EXCP_HVIRT = 101, /* EOL */ - POWERPC_EXCP_NB = 101, + POWERPC_EXCP_NB = 102, /* QEMU exceptions: used internally during code translation */ POWERPC_EXCP_STOP = 0x200, /* stop translation */ POWERPC_EXCP_BRANCH = 0x201, /* branch instruction */ @@ -246,7 +220,6 @@ typedef struct opc_handler_t opc_handler_t; /* Types used to describe some PowerPC registers etc. */ typedef struct DisasContext DisasContext; typedef struct ppc_spr_t ppc_spr_t; -typedef union ppc_avr_t ppc_avr_t; typedef union ppc_tlb_t ppc_tlb_t; typedef struct ppc_hash_pte64 ppc_hash_pte64_t; @@ -259,6 +232,7 @@ struct ppc_spr_t { void (*oea_write)(DisasContext *ctx, int spr_num, int gpr_num); void (*hea_read)(DisasContext *ctx, int gpr_num, int spr_num); void (*hea_write)(DisasContext *ctx, int spr_num, int gpr_num); + unsigned int gdb_id; #endif const char *name; target_ulong default_value; @@ -270,22 +244,26 @@ struct ppc_spr_t { #endif }; -/* Altivec registers (128 bits) */ -union ppc_avr_t { - float32 f[4]; +/* VSX/Altivec registers (128 bits) */ +typedef union _ppc_vsr_t { uint8_t u8[16]; uint16_t u16[8]; uint32_t u32[4]; + uint64_t u64[2]; int8_t s8[16]; int16_t s16[8]; int32_t s32[4]; - uint64_t u64[2]; int64_t s64[2]; + float32 f32[4]; + float64 f64[2]; + float128 f128; #ifdef CONFIG_INT128 __uint128_t u128; #endif - Int128 s128; -}; + Int128 s128; +} ppc_vsr_t; + +typedef ppc_vsr_t ppc_avr_t; #if !defined(CONFIG_USER_ONLY) /* Software TLB cache */ @@ -342,6 +320,10 @@ struct ppc_slb_t { #define SEGMENT_SHIFT_1T 40 #define SEGMENT_MASK_1T (~((1ULL << SEGMENT_SHIFT_1T) - 1)) +typedef struct ppc_v3_pate_t { + uint64_t dw0; + uint64_t dw1; +} ppc_v3_pate_t; /*****************************************************************************/ /* Machine state register bits definition */ @@ -410,6 +392,7 @@ struct ppc_slb_t { #define LPCR_AIL (3ull << LPCR_AIL_SHIFT) #define LPCR_UPRT PPC_BIT(41) /* Use Process Table */ #define LPCR_EVIRT PPC_BIT(42) /* Enhanced Virtualisation */ +#define LPCR_HR PPC_BIT(43) /* Host Radix */ #define LPCR_ONL PPC_BIT(45) #define LPCR_LD PPC_BIT(46) /* Large Decrementer */ #define LPCR_P7_PECE0 PPC_BIT(49) @@ -438,6 +421,10 @@ struct ppc_slb_t { #define LPCR_HVICE PPC_BIT(62) /* HV Virtualisation Int Enable */ #define LPCR_HDICE PPC_BIT(63) +/* PSSCR bits */ +#define PSSCR_ESL PPC_BIT(42) /* Enable State Loss */ +#define PSSCR_EC PPC_BIT(43) /* Exit Criterion */ + #define msr_sf ((env->msr >> MSR_SF) & 1) #define msr_isf ((env->msr >> MSR_ISF) & 1) #define msr_shv ((env->msr >> MSR_SHV) & 1) @@ -481,6 +468,11 @@ struct ppc_slb_t { #define msr_ts ((env->msr >> MSR_TS1) & 3) #define msr_tm ((env->msr >> MSR_TM) & 1) +#define DBCR0_ICMP (1 << 27) +#define DBCR0_BRT (1 << 26) +#define DBSR_ICMP (1 << 27) +#define DBSR_BRT (1 << 26) + /* Hypervisor bit is more specific */ #if defined(TARGET_PPC64) #define MSR_HVB (1ULL << MSR_SHV) @@ -708,8 +700,6 @@ enum { /* Vector status and control register */ #define VSCR_NJ 16 /* Vector non-java */ #define VSCR_SAT 0 /* Vector saturation */ -#define vscr_nj (((env->vscr) >> VSCR_NJ) & 0x1) -#define vscr_sat (((env->vscr) >> VSCR_SAT) & 0x1) /*****************************************************************************/ /* BookE e500 MMU registers */ @@ -929,6 +919,19 @@ enum { /* number of possible TLBs */ #define BOOKE206_MAX_TLBN 4 +#define EPID_EPID_SHIFT 0x0 +#define EPID_EPID 0xFF +#define EPID_ELPID_SHIFT 0x10 +#define EPID_ELPID 0x3F0000 +#define EPID_EGS 0x20000000 +#define EPID_EGS_SHIFT 29 +#define EPID_EAS 0x40000000 +#define EPID_EAS_SHIFT 30 +#define EPID_EPR 0x80000000 +#define EPID_EPR_SHIFT 31 +/* We don't support EGS and ELPID */ +#define EPID_MASK (EPID_EPID | EPID_EAS | EPID_EPR) + /*****************************************************************************/ /* Server and Embedded Processor Control */ @@ -958,7 +961,16 @@ struct ppc_radix_page_info { /*****************************************************************************/ /* The whole PowerPC CPU context */ -#define NB_MMU_MODES 8 + +/* PowerPC needs eight modes for different hypervisor/supervisor/guest + + * real/paged mode combinations. The other two modes are for external PID + * load/store. + */ +#define NB_MMU_MODES 10 +#define MMU_MODE8_SUFFIX _epl +#define MMU_MODE9_SUFFIX _eps +#define PPC_TLB_EPID_LOAD 8 +#define PPC_TLB_EPID_STORE 9 #define PPC_CPU_OPCODES_LEN 0x40 #define PPC_CPU_INDIRECT_OPCODES_LEN 0x20 @@ -1002,8 +1014,6 @@ struct CPUPPCState { /* Floating point execution context */ float_status fp_status; - /* floating point registers */ - float64 fpr[32]; /* floating point status and control register */ target_ulong fpscr; @@ -1053,11 +1063,12 @@ struct CPUPPCState { /* Special purpose registers */ target_ulong spr[1024]; ppc_spr_t spr_cb[1024]; - /* Altivec registers */ - ppc_avr_t avr[32]; + /* Vector status and control register, minus VSCR_SAT. */ uint32_t vscr; - /* VSX registers */ - uint64_t vsr[32]; + /* VSX registers (including FP and AVR) */ + ppc_vsr_t vsr[64] QEMU_ALIGNED(16); + /* Non-zero if and only if VSCR_SAT should be set. */ + ppc_vsr_t vscr_sat QEMU_ALIGNED(16); /* SPE registers */ uint64_t spe_acc; uint32_t spe_fscr; @@ -1110,11 +1121,13 @@ struct CPUPPCState { * instructions and SPRs are diallowed if MSR:HV is 0 */ bool has_hv_mode; - /* On P7/P8, set when in PM state, we need to handle resume - * in a special way (such as routing some resume causes to - * 0x100), so flag this here. + + /* + * On P7/P8/P9, set when in PM state, we need to handle resume in + * a special way (such as routing some resume causes to 0x100, ie, + * sreset), so flag this here. */ - bool in_pm_state; + bool resume_as_sreset; #endif /* Those resources are used only during code translation */ @@ -1196,7 +1209,6 @@ struct PowerPCCPU { int vcpu_id; uint32_t compat_pvr; PPCVirtualHypervisor *vhyp; - Object *intc; void *machine_data; int32_t node_id; /* NUMA node this CPU belongs to */ PPCHash64Options *hash64_opts; @@ -1240,7 +1252,7 @@ struct PPCVirtualHypervisorClass { hwaddr ptex, int n); void (*store_hpte)(PPCVirtualHypervisor *vhyp, hwaddr ptex, uint64_t pte0, uint64_t pte1); - uint64_t (*get_patbe)(PPCVirtualHypervisor *vhyp); + void (*get_pate)(PPCVirtualHypervisor *vhyp, ppc_v3_pate_t *entry); target_ulong (*encode_hpt_for_kvm_pr)(PPCVirtualHypervisor *vhyp); }; @@ -1265,6 +1277,10 @@ 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); +#ifndef CONFIG_USER_ONLY +void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu); +const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name); +#endif int ppc64_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, void *opaque); int ppc32_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, @@ -1305,10 +1321,10 @@ uint32_t cpu_ppc_load_atbu (CPUPPCState *env); void cpu_ppc_store_atbl (CPUPPCState *env, uint32_t value); void cpu_ppc_store_atbu (CPUPPCState *env, uint32_t value); bool ppc_decr_clear_on_delivery(CPUPPCState *env); -uint32_t cpu_ppc_load_decr (CPUPPCState *env); -void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value); -uint32_t cpu_ppc_load_hdecr (CPUPPCState *env); -void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value); +target_ulong cpu_ppc_load_decr(CPUPPCState *env); +void cpu_ppc_store_decr(CPUPPCState *env, target_ulong value); +target_ulong cpu_ppc_load_hdecr(CPUPPCState *env); +void cpu_ppc_store_hdecr(CPUPPCState *env, target_ulong value); uint64_t cpu_ppc_load_purr (CPUPPCState *env); uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env); uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env); @@ -2316,6 +2332,13 @@ enum { * them */ POWER7_INPUT_NB, }; + +enum { + /* POWER9 input pins */ + POWER9_INPUT_INT = 0, + POWER9_INPUT_HINT = 1, + POWER9_INPUT_NB, +}; #endif /* Hardware exceptions definitions */ @@ -2340,6 +2363,7 @@ enum { PPC_INTERRUPT_PERFM, /* Performance monitor interrupt */ PPC_INTERRUPT_HMI, /* Hypervisor Maintainance interrupt */ PPC_INTERRUPT_HDOORBELL, /* Hypervisor Doorbell interrupt */ + PPC_INTERRUPT_HVIRT, /* Hypervisor virtualization interrupt */ }; /* Processor Compatibility mask (PCR) */ @@ -2385,6 +2409,12 @@ enum { target_ulong cpu_read_xer(CPUPPCState *env); void cpu_write_xer(CPUPPCState *env, target_ulong xer); +/* + * All 64-bit server processors compliant with arch 2.x, ie. 970 and newer, + * have PPC_SEGMENT_64B. + */ +#define is_book3s_arch2x(ctx) (!!((ctx)->insns_flags & PPC_SEGMENT_64B)) + static inline void cpu_get_tb_cpu_state(CPUPPCState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) { @@ -2538,6 +2568,67 @@ static inline bool lsw_reg_in_range(int start, int nregs, int rx) (start + nregs > 32 && (rx >= start || rx < start + nregs - 32)); } +/* Accessors for FP, VMX and VSX registers */ +#if defined(HOST_WORDS_BIGENDIAN) +#define VsrB(i) u8[i] +#define VsrSB(i) s8[i] +#define VsrH(i) u16[i] +#define VsrSH(i) s16[i] +#define VsrW(i) u32[i] +#define VsrSW(i) s32[i] +#define VsrD(i) u64[i] +#define VsrSD(i) s64[i] +#else +#define VsrB(i) u8[15 - (i)] +#define VsrSB(i) s8[15 - (i)] +#define VsrH(i) u16[7 - (i)] +#define VsrSH(i) s16[7 - (i)] +#define VsrW(i) u32[3 - (i)] +#define VsrSW(i) s32[3 - (i)] +#define VsrD(i) u64[1 - (i)] +#define VsrSD(i) s64[1 - (i)] +#endif + +static inline int vsr64_offset(int i, bool high) +{ + return offsetof(CPUPPCState, vsr[i].VsrD(high ? 0 : 1)); +} + +static inline int vsr_full_offset(int i) +{ + return offsetof(CPUPPCState, vsr[i].u64[0]); +} + +static inline int fpr_offset(int i) +{ + return vsr64_offset(i, true); +} + +static inline uint64_t *cpu_fpr_ptr(CPUPPCState *env, int i) +{ + return (uint64_t *)((uintptr_t)env + fpr_offset(i)); +} + +static inline uint64_t *cpu_vsrl_ptr(CPUPPCState *env, int i) +{ + return (uint64_t *)((uintptr_t)env + vsr64_offset(i, false)); +} + +static inline long avr64_offset(int i, bool high) +{ + return vsr64_offset(i + 32, high); +} + +static inline int avr_full_offset(int i) +{ + return vsr_full_offset(i + 32); +} + +static inline ppc_avr_t *cpu_avr_ptr(CPUPPCState *env, int i) +{ + return (ppc_avr_t *)((uintptr_t)env + avr_full_offset(i)); +} + void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env); void ppc_maybe_bswap_register(CPUPPCState *env, uint8_t *mem_buf, int len); diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index d6e97a90e0..beafcf1ebd 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -65,6 +65,67 @@ static inline void dump_syscall(CPUPPCState *env) ppc_dump_gpr(env, 6), env->nip); } +static int powerpc_reset_wakeup(CPUState *cs, CPUPPCState *env, int excp, + target_ulong *msr) +{ + /* We no longer are in a PM state */ + env->resume_as_sreset = false; + + /* Pretend to be returning from doze always as we don't lose state */ + *msr |= (0x1ull << (63 - 47)); + + /* Machine checks are sent normally */ + if (excp == POWERPC_EXCP_MCHECK) { + return excp; + } + switch (excp) { + case POWERPC_EXCP_RESET: + *msr |= 0x4ull << (63 - 45); + break; + case POWERPC_EXCP_EXTERNAL: + *msr |= 0x8ull << (63 - 45); + break; + case POWERPC_EXCP_DECR: + *msr |= 0x6ull << (63 - 45); + break; + case POWERPC_EXCP_SDOOR: + *msr |= 0x5ull << (63 - 45); + break; + case POWERPC_EXCP_SDOOR_HV: + *msr |= 0x3ull << (63 - 45); + break; + case POWERPC_EXCP_HV_MAINT: + *msr |= 0xaull << (63 - 45); + break; + case POWERPC_EXCP_HVIRT: + *msr |= 0x9ull << (63 - 45); + break; + default: + cpu_abort(cs, "Unsupported exception %d in Power Save mode\n", + excp); + } + return POWERPC_EXCP_RESET; +} + +static uint64_t ppc_excp_vector_offset(CPUState *cs, int ail) +{ + uint64_t offset = 0; + + switch (ail) { + case AIL_0001_8000: + offset = 0x18000; + break; + case AIL_C000_0000_0000_4000: + offset = 0xc000000000004000ull; + break; + default: + cpu_abort(cs, "Invalid AIL combination %d\n", ail); + break; + } + + return offset; +} + /* Note that this function should be greatly optimized * when called with a constant excp, from ppc_hw_interrupt */ @@ -97,47 +158,17 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) asrr0 = -1; asrr1 = -1; - /* check for special resume at 0x100 from doze/nap/sleep/winkle on P7/P8 */ - if (env->in_pm_state) { - env->in_pm_state = false; - - /* Pretend to be returning from doze always as we don't lose state */ - msr |= (0x1ull << (63 - 47)); - - /* Non-machine check are routed to 0x100 with a wakeup cause - * encoded in SRR1 - */ - if (excp != POWERPC_EXCP_MCHECK) { - switch (excp) { - case POWERPC_EXCP_RESET: - msr |= 0x4ull << (63 - 45); - break; - case POWERPC_EXCP_EXTERNAL: - msr |= 0x8ull << (63 - 45); - break; - case POWERPC_EXCP_DECR: - msr |= 0x6ull << (63 - 45); - break; - case POWERPC_EXCP_SDOOR: - msr |= 0x5ull << (63 - 45); - break; - case POWERPC_EXCP_SDOOR_HV: - msr |= 0x3ull << (63 - 45); - break; - case POWERPC_EXCP_HV_MAINT: - msr |= 0xaull << (63 - 45); - break; - default: - cpu_abort(cs, "Unsupported exception %d in Power Save mode\n", - excp); - } - excp = POWERPC_EXCP_RESET; - } + /* + * check for special resume at 0x100 from doze/nap/sleep/winkle on + * P7/P8/P9 + */ + if (env->resume_as_sreset) { + excp = powerpc_reset_wakeup(cs, env, excp, &msr); } /* Exception targetting modifiers * - * LPES0 is supported on POWER7/8 + * LPES0 is supported on POWER7/8/9 * LPES1 is not supported (old iSeries mode) * * On anything else, we behave as if LPES0 is 1 @@ -148,9 +179,10 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) */ #if defined(TARGET_PPC64) if (excp_model == POWERPC_EXCP_POWER7 || - excp_model == POWERPC_EXCP_POWER8) { + excp_model == POWERPC_EXCP_POWER8 || + excp_model == POWERPC_EXCP_POWER9) { lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - if (excp_model == POWERPC_EXCP_POWER8) { + if (excp_model != POWERPC_EXCP_POWER7) { ail = (env->spr[SPR_LPCR] & LPCR_AIL) >> LPCR_AIL_SHIFT; } else { ail = 0; @@ -348,19 +380,16 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) case POWERPC_EXCP_ITLB: /* Instruction TLB error */ break; case POWERPC_EXCP_DEBUG: /* Debug interrupt */ - switch (excp_model) { - case POWERPC_EXCP_BOOKE: + if (env->flags & POWERPC_FLAG_DE) { /* FIXME: choose one or the other based on CPU type */ srr0 = SPR_BOOKE_DSRR0; srr1 = SPR_BOOKE_DSRR1; asrr0 = SPR_BOOKE_CSRR0; asrr1 = SPR_BOOKE_CSRR1; - break; - default: - break; + /* DBSR already modified by caller */ + } else { + cpu_abort(cs, "Debug exception triggered on unsupported model\n"); } - /* XXX: TODO */ - cpu_abort(cs, "Debug exception is not implemented yet !\n"); break; case POWERPC_EXCP_SPEU: /* SPE/embedded floating-point unavailable */ env->spr[SPR_BOOKE_ESR] = ESR_SPV; @@ -419,6 +448,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) case POWERPC_EXCP_HISEG: /* Hypervisor instruction segment exception */ case POWERPC_EXCP_SDOOR_HV: /* Hypervisor Doorbell interrupt */ case POWERPC_EXCP_HV_EMU: + case POWERPC_EXCP_HVIRT: /* Hypervisor virtualization */ srr0 = SPR_HSRR0; srr1 = SPR_HSRR1; new_msr |= (target_ulong)MSR_HVB; @@ -655,7 +685,15 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) } } else if (excp_model == POWERPC_EXCP_POWER8) { if (new_msr & MSR_HVB) { - if (env->spr[SPR_HID0] & (HID0_HILE | HID0_POWER9_HILE)) { + if (env->spr[SPR_HID0] & HID0_HILE) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (env->spr[SPR_LPCR] & LPCR_ILE) { + new_msr |= (target_ulong)1 << MSR_LE; + } + } else if (excp_model == POWERPC_EXCP_POWER9) { + if (new_msr & MSR_HVB) { + if (env->spr[SPR_HID0] & HID0_POWER9_HILE) { new_msr |= (target_ulong)1 << MSR_LE; } } else if (env->spr[SPR_LPCR] & LPCR_ILE) { @@ -688,17 +726,7 @@ static inline void powerpc_excp(PowerPCCPU *cpu, int excp_model, int excp) /* 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; - } + vector |= ppc_excp_vector_offset(cs, ail); } #if defined(TARGET_PPC64) @@ -751,13 +779,8 @@ void ppc_cpu_do_interrupt(CPUState *cs) static void ppc_hw_interrupt(CPUPPCState *env) { PowerPCCPU *cpu = ppc_env_get_cpu(env); -#if 0 - CPUState *cs = CPU(cpu); + bool async_deliver; - qemu_log_mask(CPU_LOG_INT, "%s: %p pending %08x req %08x me %d ee %d\n", - __func__, env, env->pending_interrupts, - cs->interrupt_request, (int)msr_me, (int)msr_ee); -#endif /* External reset */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_RESET)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_RESET); @@ -778,21 +801,44 @@ static void ppc_hw_interrupt(CPUPPCState *env) return; } #endif + + /* + * For interrupts that gate on MSR:EE, we need to do something a + * bit more subtle, as we need to let them through even when EE is + * clear when coming out of some power management states (in order + * for them to become a 0x100). + */ + async_deliver = (msr_ee != 0) || env->resume_as_sreset; + /* Hypervisor decrementer exception */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_HDECR)) { /* LPCR will be clear when not supported so this will work */ bool hdice = !!(env->spr[SPR_LPCR] & LPCR_HDICE); - if ((msr_ee != 0 || msr_hv == 0) && hdice) { + if ((async_deliver || msr_hv == 0) && hdice) { /* HDEC clears on delivery */ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HDECR); return; } } - /* Extermal interrupt can ignore MSR:EE under some circumstances */ + + /* Hypervisor virtualization interrupt */ + if (env->pending_interrupts & (1 << PPC_INTERRUPT_HVIRT)) { + /* LPCR will be clear when not supported so this will work */ + bool hvice = !!(env->spr[SPR_LPCR] & LPCR_HVICE); + if ((async_deliver || msr_hv == 0) && hvice) { + powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_HVIRT); + return; + } + } + + /* External interrupt can ignore MSR:EE under some circumstances */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_EXT)) { bool lpes0 = !!(env->spr[SPR_LPCR] & LPCR_LPES0); - if (msr_ee != 0 || (env->has_hv_mode && msr_hv == 0 && !lpes0)) { + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + /* HEIC blocks delivery to the hypervisor */ + if ((async_deliver && !(heic && msr_hv && !msr_pr)) || + (env->has_hv_mode && msr_hv == 0 && !lpes0)) { powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_EXTERNAL); return; } @@ -800,17 +846,11 @@ static void ppc_hw_interrupt(CPUPPCState *env) if (msr_ce != 0) { /* External critical interrupt */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_CEXT)) { - /* Taking a critical external interrupt does not clear the external - * critical interrupt status - */ -#if 0 - env->pending_interrupts &= ~(1 << PPC_INTERRUPT_CEXT); -#endif powerpc_excp(cpu, env->excp_model, POWERPC_EXCP_CRITICAL); return; } } - if (msr_ee != 0) { + if (async_deliver != 0) { /* Watchdog timer on embedded PowerPC */ if (env->pending_interrupts & (1 << PPC_INTERRUPT_WDT)) { env->pending_interrupts &= ~(1 << PPC_INTERRUPT_WDT); @@ -864,6 +904,22 @@ static void ppc_hw_interrupt(CPUPPCState *env) return; } } + + if (env->resume_as_sreset) { + /* + * This is a bug ! It means that has_work took us out of halt without + * anything to deliver while in a PM state that requires getting + * out via a 0x100 + * + * This means we will incorrectly execute past the power management + * instruction instead of triggering a reset. + * + * It generally means a discrepancy between the wakup conditions in the + * processor has_work implementation and the logic in this function. + */ + cpu_abort(CPU(ppc_env_get_cpu(env)), + "Wakeup from PM state but interrupt Undelivered"); + } } void ppc_cpu_do_system_reset(CPUState *cs) @@ -958,22 +1014,15 @@ void helper_pminsn(CPUPPCState *env, powerpc_pm_insn_t insn) cs = CPU(ppc_env_get_cpu(env)); cs->halted = 1; - env->in_pm_state = true; /* The architecture specifies that HDEC interrupts are * discarded in PM states */ env->pending_interrupts &= ~(1 << PPC_INTERRUPT_HDECR); - /* Technically, nap doesn't set EE, but if we don't set it - * then ppc_hw_interrupt() won't deliver. We could add some - * other tests there based on LPCR but it's simpler to just - * whack EE in. It will be cleared by the 0x100 at wakeup - * anyway. It will still be observable by the guest in SRR1 - * but this doesn't seem to be a problem. - */ - env->msr |= (1ull << MSR_EE); - raise_exception(env, EXCP_HLT); + /* Condition for waking up at 0x100 */ + env->resume_as_sreset = (insn != PPC_PM_STOP) || + (env->spr[SPR_PSSCR] & PSSCR_EC); } #endif /* defined(TARGET_PPC64) */ diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index 8675d931b6..2ed4f42275 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -36,26 +36,72 @@ static inline float128 float128_snan_to_qnan(float128 x) #define float32_snan_to_qnan(x) ((x) | 0x00400000) #define float16_snan_to_qnan(x) ((x) | 0x0200) -/*****************************************************************************/ -/* Floating point operations helpers */ -uint64_t helper_float32_to_float64(CPUPPCState *env, uint32_t arg) +static inline bool fp_exceptions_enabled(CPUPPCState *env) { - CPU_FloatU f; - CPU_DoubleU d; - - f.l = arg; - d.d = float32_to_float64(f.f, &env->fp_status); - return d.ll; +#ifdef CONFIG_USER_ONLY + return true; +#else + return (env->msr & ((1U << MSR_FE0) | (1U << MSR_FE1))) != 0; +#endif } -uint32_t helper_float64_to_float32(CPUPPCState *env, uint64_t arg) -{ - CPU_FloatU f; - CPU_DoubleU d; +/*****************************************************************************/ +/* Floating point operations helpers */ - d.ll = arg; - f.f = float64_to_float32(d.d, &env->fp_status); - return f.l; +/* + * This is the non-arithmatic conversion that happens e.g. on loads. + * In the Power ISA pseudocode, this is called DOUBLE. + */ +uint64_t helper_todouble(uint32_t arg) +{ + uint32_t abs_arg = arg & 0x7fffffff; + uint64_t ret; + + if (likely(abs_arg >= 0x00800000)) { + /* Normalized operand, or Inf, or NaN. */ + ret = (uint64_t)extract32(arg, 30, 2) << 62; + ret |= ((extract32(arg, 30, 1) ^ 1) * (uint64_t)7) << 59; + ret |= (uint64_t)extract32(arg, 0, 30) << 29; + } else { + /* Zero or Denormalized operand. */ + ret = (uint64_t)extract32(arg, 31, 1) << 63; + if (unlikely(abs_arg != 0)) { + /* Denormalized operand. */ + int shift = clz32(abs_arg) - 9; + int exp = -126 - shift + 1023; + ret |= (uint64_t)exp << 52; + ret |= abs_arg << (shift + 29); + } + } + return ret; +} + +/* + * This is the non-arithmatic conversion that happens e.g. on stores. + * In the Power ISA pseudocode, this is called SINGLE. + */ +uint32_t helper_tosingle(uint64_t arg) +{ + int exp = extract64(arg, 52, 11); + uint32_t ret; + + if (likely(exp > 896)) { + /* No denormalization required (includes Inf, NaN). */ + ret = extract64(arg, 62, 2) << 30; + ret |= extract64(arg, 29, 30); + } else { + /* Zero or Denormal result. If the exponent is in bounds for + * a single-precision denormal result, extract the proper bits. + * If the input is not zero, and the exponent is out of bounds, + * then the result is undefined; this underflows to zero. + */ + ret = extract64(arg, 63, 1) << 31; + if (unlikely(exp >= 874)) { + /* Denormal result. */ + ret |= ((1ULL << 52) | extract64(arg, 0, 52)) >> (896 + 30 - exp); + } + } + return ret; } static inline int ppc_float32_get_unbiased_exp(float32 f) @@ -68,54 +114,62 @@ static inline int ppc_float64_get_unbiased_exp(float64 f) return ((f >> 52) & 0x7FF) - 1023; } -#define COMPUTE_FPRF(tp) \ -void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ +/* Classify a floating-point number. */ +enum { + is_normal = 1, + is_zero = 2, + is_denormal = 4, + is_inf = 8, + is_qnan = 16, + is_snan = 32, + is_neg = 64, +}; + +#define COMPUTE_CLASS(tp) \ +static int tp##_classify(tp arg) \ { \ - int isneg; \ - int fprf; \ - \ - isneg = tp##_is_neg(arg); \ + int ret = tp##_is_neg(arg) * is_neg; \ if (unlikely(tp##_is_any_nan(arg))) { \ - if (tp##_is_signaling_nan(arg, &env->fp_status)) { \ - /* Signaling NaN: flags are undefined */ \ - fprf = 0x00; \ - } else { \ - /* Quiet NaN */ \ - fprf = 0x11; \ - } \ + float_status dummy = { }; /* snan_bit_is_one = 0 */ \ + ret |= (tp##_is_signaling_nan(arg, &dummy) \ + ? is_snan : is_qnan); \ } else if (unlikely(tp##_is_infinity(arg))) { \ - /* +/- infinity */ \ - if (isneg) { \ - fprf = 0x09; \ - } else { \ - fprf = 0x05; \ - } \ + ret |= is_inf; \ + } else if (tp##_is_zero(arg)) { \ + ret |= is_zero; \ + } else if (tp##_is_zero_or_denormal(arg)) { \ + ret |= is_denormal; \ } else { \ - if (tp##_is_zero(arg)) { \ - /* +/- zero */ \ - if (isneg) { \ - fprf = 0x12; \ - } else { \ - fprf = 0x02; \ - } \ - } else { \ - if (tp##_is_zero_or_denormal(arg)) { \ - /* Denormalized numbers */ \ - fprf = 0x10; \ - } else { \ - /* Normalized numbers */ \ - fprf = 0x00; \ - } \ - if (isneg) { \ - fprf |= 0x08; \ - } else { \ - fprf |= 0x04; \ - } \ - } \ + ret |= is_normal; \ } \ - /* We update FPSCR_FPRF */ \ - env->fpscr &= ~(0x1F << FPSCR_FPRF); \ - env->fpscr |= fprf << FPSCR_FPRF; \ + return ret; \ +} + +COMPUTE_CLASS(float16) +COMPUTE_CLASS(float32) +COMPUTE_CLASS(float64) +COMPUTE_CLASS(float128) + +static void set_fprf_from_class(CPUPPCState *env, int class) +{ + static const uint8_t fprf[6][2] = { + { 0x04, 0x08 }, /* normalized */ + { 0x02, 0x12 }, /* zero */ + { 0x14, 0x18 }, /* denormalized */ + { 0x05, 0x09 }, /* infinity */ + { 0x11, 0x11 }, /* qnan */ + { 0x00, 0x00 }, /* snan -- flags are undefined */ + }; + bool isneg = class & is_neg; + + env->fpscr &= ~(0x1F << FPSCR_FPRF); + env->fpscr |= fprf[ctz32(class)][isneg] << FPSCR_FPRF; +} + +#define COMPUTE_FPRF(tp) \ +void helper_compute_fprf_##tp(CPUPPCState *env, tp arg) \ +{ \ + set_fprf_from_class(env, tp##_classify(arg)); \ } COMPUTE_FPRF(float16) @@ -124,96 +178,120 @@ COMPUTE_FPRF(float64) COMPUTE_FPRF(float128) /* Floating-point invalid operations exception */ -static inline __attribute__((__always_inline__)) -uint64_t float_invalid_op_excp(CPUPPCState *env, int op, int set_fpcc) +static void finish_invalid_op_excp(CPUPPCState *env, int op, uintptr_t retaddr) { - CPUState *cs = CPU(ppc_env_get_cpu(env)); - uint64_t ret = 0; - int ve; + /* Update the floating-point invalid operation summary */ + env->fpscr |= 1 << FPSCR_VX; + /* Update the floating-point exception summary */ + env->fpscr |= FP_FX; + if (fpscr_ve != 0) { + /* Update the floating-point enabled exception summary */ + env->fpscr |= 1 << FPSCR_FEX; + if (fp_exceptions_enabled(env)) { + raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, + POWERPC_EXCP_FP | op, retaddr); + } + } +} - ve = fpscr_ve; - switch (op) { - case POWERPC_EXCP_FP_VXSNAN: - env->fpscr |= 1 << FPSCR_VXSNAN; - break; - case POWERPC_EXCP_FP_VXSOFT: - env->fpscr |= 1 << FPSCR_VXSOFT; - break; - case POWERPC_EXCP_FP_VXISI: - /* Magnitude subtraction of infinities */ - env->fpscr |= 1 << FPSCR_VXISI; - goto update_arith; - case POWERPC_EXCP_FP_VXIDI: - /* Division of infinity by infinity */ - env->fpscr |= 1 << FPSCR_VXIDI; - goto update_arith; - case POWERPC_EXCP_FP_VXZDZ: - /* Division of zero by zero */ - env->fpscr |= 1 << FPSCR_VXZDZ; - goto update_arith; - case POWERPC_EXCP_FP_VXIMZ: - /* Multiplication of zero by infinity */ - env->fpscr |= 1 << FPSCR_VXIMZ; - goto update_arith; - case POWERPC_EXCP_FP_VXVC: - /* Ordered comparison of NaN */ - env->fpscr |= 1 << FPSCR_VXVC; +static void finish_invalid_op_arith(CPUPPCState *env, int op, + bool set_fpcc, uintptr_t retaddr) +{ + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + if (fpscr_ve == 0) { if (set_fpcc) { env->fpscr &= ~(0xF << FPSCR_FPCC); env->fpscr |= 0x11 << FPSCR_FPCC; } - /* We must update the target FPR before raising the exception */ - if (ve != 0) { - cs->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; - /* Update the floating-point enabled exception summary */ - env->fpscr |= 1 << FPSCR_FEX; - /* Exception is differed */ - ve = 0; - } - break; - case POWERPC_EXCP_FP_VXSQRT: - /* Square root of a negative number */ - env->fpscr |= 1 << FPSCR_VXSQRT; - update_arith: - env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); - if (ve == 0) { - /* Set the result to quiet NaN */ - ret = 0x7FF8000000000000ULL; - if (set_fpcc) { - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; - } - } - break; - case POWERPC_EXCP_FP_VXCVI: - /* Invalid conversion */ - env->fpscr |= 1 << FPSCR_VXCVI; - env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); - if (ve == 0) { - /* Set the result to quiet NaN */ - ret = 0x7FF8000000000000ULL; - if (set_fpcc) { - env->fpscr &= ~(0xF << FPSCR_FPCC); - env->fpscr |= 0x11 << FPSCR_FPCC; - } - } - break; + } + finish_invalid_op_excp(env, op, retaddr); +} + +/* Signalling NaN */ +static void float_invalid_op_vxsnan(CPUPPCState *env, uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXSNAN; + finish_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, retaddr); +} + +/* Magnitude subtraction of infinities */ +static void float_invalid_op_vxisi(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXISI; + finish_invalid_op_arith(env, POWERPC_EXCP_FP_VXISI, set_fpcc, retaddr); +} + +/* Division of infinity by infinity */ +static void float_invalid_op_vxidi(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXIDI; + finish_invalid_op_arith(env, POWERPC_EXCP_FP_VXIDI, set_fpcc, retaddr); +} + +/* Division of zero by zero */ +static void float_invalid_op_vxzdz(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXZDZ; + finish_invalid_op_arith(env, POWERPC_EXCP_FP_VXZDZ, set_fpcc, retaddr); +} + +/* Multiplication of zero by infinity */ +static void float_invalid_op_vximz(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXIMZ; + finish_invalid_op_arith(env, POWERPC_EXCP_FP_VXIMZ, set_fpcc, retaddr); +} + +/* Square root of a negative number */ +static void float_invalid_op_vxsqrt(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXSQRT; + finish_invalid_op_arith(env, POWERPC_EXCP_FP_VXSQRT, set_fpcc, retaddr); +} + +/* Ordered comparison of NaN */ +static void float_invalid_op_vxvc(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXVC; + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; } /* Update the floating-point invalid operation summary */ env->fpscr |= 1 << FPSCR_VX; /* Update the floating-point exception summary */ env->fpscr |= FP_FX; - if (ve != 0) { + /* We must update the target FPR before raising the exception */ + if (fpscr_ve != 0) { + CPUState *cs = CPU(ppc_env_get_cpu(env)); + + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC; /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; - if (msr_fe0 != 0 || msr_fe1 != 0) { - /* GETPC() works here because this is inline */ - raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, - POWERPC_EXCP_FP | op, GETPC()); + /* Exception is differed */ + } +} + +/* Invalid conversion */ +static void float_invalid_op_vxcvi(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr) +{ + env->fpscr |= 1 << FPSCR_VXCVI; + env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI)); + if (fpscr_ve == 0) { + if (set_fpcc) { + env->fpscr &= ~(0xF << FPSCR_FPCC); + env->fpscr |= 0x11 << FPSCR_FPCC; } } - return ret; + finish_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, retaddr); } static inline void float_zero_divide_excp(CPUPPCState *env, uintptr_t raddr) @@ -225,7 +303,7 @@ static inline void float_zero_divide_excp(CPUPPCState *env, uintptr_t raddr) if (fpscr_ze != 0) { /* Update the floating-point enabled exception summary */ env->fpscr |= 1 << FPSCR_FEX; - if (msr_fe0 != 0 || msr_fe1 != 0) { + if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX, raddr); @@ -536,9 +614,7 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) int status = get_float_exception_flags(&env->fp_status); bool inexact_happened = false; - if (status & float_flag_divbyzero) { - float_zero_divide_excp(env, raddr); - } else if (status & float_flag_overflow) { + if (status & float_flag_overflow) { float_overflow_excp(env); } else if (status & float_flag_underflow) { float_underflow_excp(env); @@ -555,20 +631,13 @@ static void do_float_check_status(CPUPPCState *env, uintptr_t raddr) if (cs->exception_index == POWERPC_EXCP_PROGRAM && (env->error_code & POWERPC_EXCP_FP)) { /* Differred floating-point exception after target FPR update */ - if (msr_fe0 != 0 || msr_fe1 != 0) { + if (fp_exceptions_enabled(env)) { raise_exception_err_ra(env, cs->exception_index, env->error_code, raddr); } } } -static inline __attribute__((__always_inline__)) -void float_check_status(CPUPPCState *env) -{ - /* GETPC() works here because this is inline */ - do_float_check_status(env, GETPC()); -} - void helper_float_check_status(CPUPPCState *env) { do_float_check_status(env, GETPC()); @@ -579,129 +648,132 @@ void helper_reset_fpstatus(CPUPPCState *env) set_float_exception_flags(0, &env->fp_status); } -/* fadd - fadd. */ -uint64_t helper_fadd(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +static void float_invalid_op_addsub(CPUPPCState *env, bool set_fpcc, + uintptr_t retaddr, int classes) { - CPU_DoubleU farg1, farg2; - - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) != float64_is_neg(farg2.d))) { + if ((classes & ~is_neg) == is_inf) { /* Magnitude subtraction of infinities */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { - /* sNaN addition */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } - farg1.d = float64_add(farg1.d, farg2.d, &env->fp_status); + float_invalid_op_vxisi(env, set_fpcc, retaddr); + } else if (classes & is_snan) { + float_invalid_op_vxsnan(env, retaddr); + } +} + +/* fadd - fadd. */ +float64 helper_fadd(CPUPPCState *env, float64 arg1, float64 arg2) +{ + float64 ret = float64_add(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); + + if (unlikely(status & float_flag_invalid)) { + float_invalid_op_addsub(env, 1, GETPC(), + float64_classify(arg1) | + float64_classify(arg2)); } - return farg1.ll; + return ret; } /* fsub - fsub. */ -uint64_t helper_fsub(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fsub(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; + float64 ret = float64_sub(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_infinity(farg1.d) && float64_is_infinity(farg2.d) && - float64_is_neg(farg1.d) == float64_is_neg(farg2.d))) { - /* Magnitude subtraction of infinities */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { - /* sNaN subtraction */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } - farg1.d = float64_sub(farg1.d, farg2.d, &env->fp_status); + if (unlikely(status & float_flag_invalid)) { + float_invalid_op_addsub(env, 1, GETPC(), + float64_classify(arg1) | + float64_classify(arg2)); } - return farg1.ll; + return ret; +} + +static void float_invalid_op_mul(CPUPPCState *env, bool set_fprc, + uintptr_t retaddr, int classes) +{ + if ((classes & (is_zero | is_inf)) == (is_zero | is_inf)) { + /* Multiplication of zero by infinity */ + float_invalid_op_vximz(env, set_fprc, retaddr); + } else if (classes & is_snan) { + float_invalid_op_vxsnan(env, retaddr); + } } /* fmul - fmul. */ -uint64_t helper_fmul(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fmul(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; + float64 ret = float64_mul(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely((float64_is_infinity(farg1.d) && float64_is_zero(farg2.d)) || - (float64_is_zero(farg1.d) && float64_is_infinity(farg2.d)))) { - /* Multiplication of zero by infinity */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { - /* sNaN multiplication */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } - farg1.d = float64_mul(farg1.d, farg2.d, &env->fp_status); + if (unlikely(status & float_flag_invalid)) { + float_invalid_op_mul(env, 1, GETPC(), + float64_classify(arg1) | + float64_classify(arg2)); } - return farg1.ll; + return ret; +} + +static void float_invalid_op_div(CPUPPCState *env, bool set_fprc, + uintptr_t retaddr, int classes) +{ + classes &= ~is_neg; + if (classes == is_inf) { + /* Division of infinity by infinity */ + float_invalid_op_vxidi(env, set_fprc, retaddr); + } else if (classes == is_zero) { + /* Division of zero by zero */ + float_invalid_op_vxzdz(env, set_fprc, retaddr); + } else if (classes & is_snan) { + float_invalid_op_vxsnan(env, retaddr); + } } /* fdiv - fdiv. */ -uint64_t helper_fdiv(CPUPPCState *env, uint64_t arg1, uint64_t arg2) +float64 helper_fdiv(CPUPPCState *env, float64 arg1, float64 arg2) { - CPU_DoubleU farg1, farg2; + float64 ret = float64_div(arg1, arg2, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - farg1.ll = arg1; - farg2.ll = arg2; - - if (unlikely(float64_is_infinity(farg1.d) && - float64_is_infinity(farg2.d))) { - /* Division of infinity by infinity */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); - } else if (unlikely(float64_is_zero(farg1.d) && float64_is_zero(farg2.d))) { - /* Division of zero by zero */ - farg1.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); - } else { - if (unlikely(float64_is_signaling_nan(farg1.d, &env->fp_status) || - float64_is_signaling_nan(farg2.d, &env->fp_status))) { - /* sNaN division */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + if (unlikely(status)) { + if (status & float_flag_invalid) { + float_invalid_op_div(env, 1, GETPC(), + float64_classify(arg1) | + float64_classify(arg2)); + } + if (status & float_flag_divbyzero) { + float_zero_divide_excp(env, GETPC()); } - farg1.d = float64_div(farg1.d, farg2.d, &env->fp_status); } - return farg1.ll; + return ret; } +static void float_invalid_cvt(CPUPPCState *env, bool set_fprc, + uintptr_t retaddr, int class1) +{ + float_invalid_op_vxcvi(env, set_fprc, retaddr); + if (class1 & is_snan) { + float_invalid_op_vxsnan(env, retaddr); + } +} #define FPU_FCTI(op, cvt, nanval) \ -uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ +uint64_t helper_##op(CPUPPCState *env, float64 arg) \ { \ - CPU_DoubleU farg; \ + uint64_t ret = float64_to_##cvt(arg, &env->fp_status); \ + int status = get_float_exception_flags(&env->fp_status); \ \ - farg.ll = arg; \ - farg.ll = float64_to_##cvt(farg.d, &env->fp_status); \ - \ - if (unlikely(env->fp_status.float_exception_flags)) { \ - if (float64_is_any_nan(arg)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ - if (float64_is_signaling_nan(arg, &env->fp_status)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); \ - } \ - farg.ll = nanval; \ - } else if (env->fp_status.float_exception_flags & \ - float_flag_invalid) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 1); \ + if (unlikely(status)) { \ + if (status & float_flag_invalid) { \ + float_invalid_cvt(env, 1, GETPC(), float64_classify(arg)); \ + ret = nanval; \ } \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } \ - return farg.ll; \ - } + return ret; \ +} FPU_FCTI(fctiw, int32, 0x80000000U) FPU_FCTI(fctiwz, int32_round_to_zero, 0x80000000U) @@ -723,7 +795,7 @@ uint64_t helper_##op(CPUPPCState *env, uint64_t arg) \ } else { \ farg.d = cvtr(arg, &env->fp_status); \ } \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ return farg.ll; \ } @@ -741,7 +813,7 @@ static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { /* sNaN round */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + float_invalid_op_vxsnan(env, GETPC()); farg.ll = arg | 0x0008000000000000ULL; } else { int inexact = get_float_exception_flags(&env->fp_status) & @@ -756,7 +828,7 @@ static inline uint64_t do_fri(CPUPPCState *env, uint64_t arg, env->fp_status.float_exception_flags &= ~float_flag_inexact; } } - float_check_status(env); + do_float_check_status(env, GETPC()); return farg.ll; } @@ -782,18 +854,18 @@ uint64_t helper_frim(CPUPPCState *env, uint64_t arg) #define FPU_MADDSUB_UPDATE(NAME, TP) \ static void NAME(CPUPPCState *env, TP arg1, TP arg2, TP arg3, \ - unsigned int madd_flags) \ + unsigned int madd_flags, uintptr_t retaddr) \ { \ if (TP##_is_signaling_nan(arg1, &env->fp_status) || \ TP##_is_signaling_nan(arg2, &env->fp_status) || \ TP##_is_signaling_nan(arg3, &env->fp_status)) { \ /* sNaN operation */ \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); \ + float_invalid_op_vxsnan(env, retaddr); \ } \ if ((TP##_is_infinity(arg1) && TP##_is_zero(arg2)) || \ (TP##_is_zero(arg1) && TP##_is_infinity(arg2))) { \ /* Multiplication of zero by infinity */ \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); \ + float_invalid_op_vximz(env, 1, retaddr); \ } \ if ((TP##_is_infinity(arg1) || TP##_is_infinity(arg2)) && \ TP##_is_infinity(arg3)) { \ @@ -806,7 +878,7 @@ static void NAME(CPUPPCState *env, TP arg1, TP arg2, TP arg3, \ cSign ^= 1; \ } \ if (aSign ^ bSign ^ cSign) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); \ + float_invalid_op_vxisi(env, 1, retaddr); \ } \ } \ } @@ -824,9 +896,9 @@ uint64_t helper_##op(CPUPPCState *env, uint64_t arg1, \ if (flags) { \ if (flags & float_flag_invalid) { \ float64_maddsub_update_excp(env, arg1, arg2, arg3, \ - madd_flags); \ + madd_flags, GETPC()); \ } \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } \ return ret; \ } @@ -850,8 +922,7 @@ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) farg.ll = arg; if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN square root */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + float_invalid_op_vxsnan(env, GETPC()); } f32 = float64_to_float32(farg.d, &env->fp_status); farg.d = float32_to_float64(f32, &env->fp_status); @@ -860,40 +931,48 @@ uint64_t helper_frsp(CPUPPCState *env, uint64_t arg) } /* fsqrt - fsqrt. */ -uint64_t helper_fsqrt(CPUPPCState *env, uint64_t arg) +float64 helper_fsqrt(CPUPPCState *env, float64 arg) { - CPU_DoubleU farg; + float64 ret = float64_sqrt(arg, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - farg.ll = arg; - - if (unlikely(float64_is_any_nan(farg.d))) { - if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN reciprocal square root */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - farg.ll = float64_snan_to_qnan(farg.ll); + if (unlikely(status & float_flag_invalid)) { + if (unlikely(float64_is_any_nan(arg))) { + if (unlikely(float64_is_signaling_nan(arg, &env->fp_status))) { + /* sNaN square root */ + float_invalid_op_vxsnan(env, GETPC()); + } + } else { + /* Square root of a negative nonzero number */ + float_invalid_op_vxsqrt(env, 1, GETPC()); } - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Square root of a negative nonzero number */ - farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - } else { - farg.d = float64_sqrt(farg.d, &env->fp_status); } - return farg.ll; + + return ret; } /* fre - fre. */ -uint64_t helper_fre(CPUPPCState *env, uint64_t arg) +float64 helper_fre(CPUPPCState *env, float64 arg) { - CPU_DoubleU farg; + /* "Estimate" the reciprocal with actual division. */ + float64 ret = float64_div(float64_one, arg, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - farg.ll = arg; - - if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN reciprocal */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + if (unlikely(status)) { + if (status & float_flag_invalid) { + if (float64_is_signaling_nan(arg, &env->fp_status)) { + /* sNaN reciprocal */ + float_invalid_op_vxsnan(env, GETPC()); + } + } + if (status & float_flag_divbyzero) { + float_zero_divide_excp(env, GETPC()); + /* For FPSCR.ZE == 0, the result is 1/2. */ + ret = float64_set_sign(float64_half, float64_is_neg(arg)); + } } - farg.d = float64_div(float64_one, farg.d, &env->fp_status); - return farg.d; + + return ret; } /* fres - fres. */ @@ -906,7 +985,7 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg) if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { /* sNaN reciprocal */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + float_invalid_op_vxsnan(env, GETPC()); } farg.d = float64_div(float64_one, farg.d, &env->fp_status); f32 = float64_to_float32(farg.d, &env->fp_status); @@ -916,27 +995,30 @@ uint64_t helper_fres(CPUPPCState *env, uint64_t arg) } /* frsqrte - frsqrte. */ -uint64_t helper_frsqrte(CPUPPCState *env, uint64_t arg) +float64 helper_frsqrte(CPUPPCState *env, float64 arg) { - CPU_DoubleU farg; + /* "Estimate" the reciprocal with actual division. */ + float64 rets = float64_sqrt(arg, &env->fp_status); + float64 retd = float64_div(float64_one, rets, &env->fp_status); + int status = get_float_exception_flags(&env->fp_status); - farg.ll = arg; - - if (unlikely(float64_is_any_nan(farg.d))) { - if (unlikely(float64_is_signaling_nan(farg.d, &env->fp_status))) { - /* sNaN reciprocal square root */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - farg.ll = float64_snan_to_qnan(farg.ll); + if (unlikely(status)) { + if (status & float_flag_invalid) { + if (float64_is_signaling_nan(arg, &env->fp_status)) { + /* sNaN reciprocal */ + float_invalid_op_vxsnan(env, GETPC()); + } else { + /* Square root of a negative nonzero number */ + float_invalid_op_vxsqrt(env, 1, GETPC()); + } + } + if (status & float_flag_divbyzero) { + /* Reciprocal of (square root of) zero. */ + float_zero_divide_excp(env, GETPC()); } - } else if (unlikely(float64_is_neg(farg.d) && !float64_is_zero(farg.d))) { - /* Reciprocal square root of a negative nonzero number */ - farg.ll = float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); - } else { - farg.d = float64_sqrt(farg.d, &env->fp_status); - farg.d = float64_div(float64_one, farg.d, &env->fp_status); } - return farg.ll; + return retd; } /* fsel - fsel. */ @@ -1049,7 +1131,7 @@ void helper_fcmpu(CPUPPCState *env, uint64_t arg1, uint64_t arg2, && (float64_is_signaling_nan(farg1.d, &env->fp_status) || float64_is_signaling_nan(farg2.d, &env->fp_status)))) { /* sNaN comparison */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + float_invalid_op_vxsnan(env, GETPC()); } } @@ -1077,14 +1159,11 @@ void helper_fcmpo(CPUPPCState *env, uint64_t arg1, uint64_t arg2, env->fpscr |= ret << FPSCR_FPRF; env->crf[crfD] = ret; if (unlikely(ret == 0x01UL)) { + float_invalid_op_vxvc(env, 1, GETPC()); if (float64_is_signaling_nan(farg1.d, &env->fp_status) || float64_is_signaling_nan(farg2.d, &env->fp_status)) { /* sNaN comparison */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN | - POWERPC_EXCP_FP_VXVC, 1); - } else { - /* qNaN comparison */ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 1); + float_invalid_op_vxsnan(env, GETPC()); } } } @@ -1736,12 +1815,9 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ - if (tp##_is_infinity(xa.fld) && tp##_is_infinity(xb.fld)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, sfprf); \ - } else if (tp##_is_signaling_nan(xa.fld, &tstat) || \ - tp##_is_signaling_nan(xb.fld, &tstat)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ - } \ + float_invalid_op_addsub(env, sfprf, GETPC(), \ + tp##_classify(xa.fld) | \ + tp##_classify(xb.fld)); \ } \ \ if (r2sp) { \ @@ -1753,7 +1829,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ } \ } \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_ADD_SUB(xsadddp, add, 1, float64, VsrD(0), 1, 0) @@ -1785,18 +1861,15 @@ void helper_xsaddqp(CPUPPCState *env, uint32_t opcode) env->fp_status.float_exception_flags |= tstat.float_exception_flags; if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { - if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); - } else if (float128_is_signaling_nan(xa.f128, &tstat) || - float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } + float_invalid_op_addsub(env, 1, GETPC(), + float128_classify(xa.f128) | + float128_classify(xb.f128)); } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } /* VSX_MUL - VSX floating point multiply @@ -1824,13 +1897,9 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ - if ((tp##_is_infinity(xa.fld) && tp##_is_zero(xb.fld)) || \ - (tp##_is_infinity(xb.fld) && tp##_is_zero(xa.fld))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, sfprf); \ - } else if (tp##_is_signaling_nan(xa.fld, &tstat) || \ - tp##_is_signaling_nan(xb.fld, &tstat)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ - } \ + float_invalid_op_mul(env, sfprf, GETPC(), \ + tp##_classify(xa.fld) | \ + tp##_classify(xb.fld)); \ } \ \ if (r2sp) { \ @@ -1843,7 +1912,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_MUL(xsmuldp, 1, float64, VsrD(0), 1, 0) @@ -1871,18 +1940,14 @@ void helper_xsmulqp(CPUPPCState *env, uint32_t opcode) env->fp_status.float_exception_flags |= tstat.float_exception_flags; if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { - if ((float128_is_infinity(xa.f128) && float128_is_zero(xb.f128)) || - (float128_is_infinity(xb.f128) && float128_is_zero(xa.f128))) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIMZ, 1); - } else if (float128_is_signaling_nan(xa.f128, &tstat) || - float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } + float_invalid_op_mul(env, 1, GETPC(), + float128_classify(xa.f128) | + float128_classify(xb.f128)); } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } /* VSX_DIV - VSX floating point divide @@ -1910,15 +1975,12 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ - if (tp##_is_infinity(xa.fld) && tp##_is_infinity(xb.fld)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, sfprf); \ - } else if (tp##_is_zero(xa.fld) && \ - tp##_is_zero(xb.fld)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, sfprf); \ - } else if (tp##_is_signaling_nan(xa.fld, &tstat) || \ - tp##_is_signaling_nan(xb.fld, &tstat)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ - } \ + float_invalid_op_div(env, sfprf, GETPC(), \ + tp##_classify(xa.fld) | \ + tp##_classify(xb.fld)); \ + } \ + if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) { \ + float_zero_divide_excp(env, GETPC()); \ } \ \ if (r2sp) { \ @@ -1931,7 +1993,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_DIV(xsdivdp, 1, float64, VsrD(0), 1, 0) @@ -1959,20 +2021,17 @@ void helper_xsdivqp(CPUPPCState *env, uint32_t opcode) env->fp_status.float_exception_flags |= tstat.float_exception_flags; if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { - if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXIDI, 1); - } else if (float128_is_zero(xa.f128) && - float128_is_zero(xb.f128)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXZDZ, 1); - } else if (float128_is_signaling_nan(xa.f128, &tstat) || - float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } + float_invalid_op_div(env, 1, GETPC(), + float128_classify(xa.f128) | + float128_classify(xb.f128)); + } + if (unlikely(tstat.float_exception_flags & float_flag_divbyzero)) { + float_zero_divide_excp(env, GETPC()); } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } /* VSX_RE - VSX floating point reciprocal estimate @@ -1994,7 +2053,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ \ for (i = 0; i < nels; i++) { \ if (unlikely(tp##_is_signaling_nan(xb.fld, &env->fp_status))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ xt.fld = tp##_div(tp##_one, xb.fld, &env->fp_status); \ \ @@ -2008,7 +2067,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_RE(xsredp, 1, float64, VsrD(0), 1, 0) @@ -2041,9 +2100,9 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ if (tp##_is_neg(xb.fld) && !tp##_is_zero(xb.fld)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + float_invalid_op_vxsqrt(env, sfprf, GETPC()); \ } else if (tp##_is_signaling_nan(xb.fld, &tstat)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ } \ \ @@ -2057,7 +2116,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_SQRT(xssqrtdp, 1, float64, VsrD(0), 1, 0) @@ -2091,9 +2150,9 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ if (tp##_is_neg(xb.fld) && !tp##_is_zero(xb.fld)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, sfprf); \ + float_invalid_op_vxsqrt(env, sfprf, GETPC()); \ } else if (tp##_is_signaling_nan(xb.fld, &tstat)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, sfprf); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ } \ \ @@ -2107,7 +2166,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_RSQRTE(xsrsqrtedp, 1, float64, VsrD(0), 1, 0) @@ -2277,7 +2336,8 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ env->fp_status.float_exception_flags |= tstat.float_exception_flags; \ \ if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { \ - tp##_maddsub_update_excp(env, xa.fld, b->fld, c->fld, maddflgs); \ + tp##_maddsub_update_excp(env, xa.fld, b->fld, \ + c->fld, maddflgs, GETPC()); \ } \ \ if (r2sp) { \ @@ -2289,7 +2349,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ } \ putVSR(xT(opcode), &xt_out, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_MADD(xsmaddadp, 1, float64, VsrD(0), MADD_FLGS, 1, 1, 0) @@ -2355,10 +2415,10 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ float64_is_quiet_nan(xb.VsrD(0), &env->fp_status); \ } \ if (vxsnan_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ if (vxvc_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + float_invalid_op_vxvc(env, 0, GETPC()); \ } \ vex_flag = fpscr_ve && (vxvc_flag || vxsnan_flag); \ \ @@ -2372,7 +2432,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ } \ putVSR(xT(opcode), &xt, env); \ - helper_float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_SCALAR_CMP_DP(xscmpeqdp, eq, 1, 0) @@ -2409,7 +2469,7 @@ void helper_xscmpexpdp(CPUPPCState *env, uint32_t opcode) env->fpscr |= cc << FPSCR_FPRF; env->crf[BF(opcode)] = cc; - helper_float_check_status(env); + do_float_check_status(env, GETPC()); } void helper_xscmpexpqp(CPUPPCState *env, uint32_t opcode) @@ -2441,7 +2501,7 @@ void helper_xscmpexpqp(CPUPPCState *env, uint32_t opcode) env->fpscr |= cc << FPSCR_FPRF; env->crf[BF(opcode)] = cc; - helper_float_check_status(env); + do_float_check_status(env, GETPC()); } #define VSX_SCALAR_CMP(op, ordered) \ @@ -2470,10 +2530,10 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ } \ if (vxsnan_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ if (vxvc_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + float_invalid_op_vxvc(env, 0, GETPC()); \ } \ \ if (float64_lt(xa.VsrD(0), xb.VsrD(0), &env->fp_status)) { \ @@ -2488,7 +2548,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ env->fpscr |= cc << FPSCR_FPRF; \ env->crf[BF(opcode)] = cc; \ \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_SCALAR_CMP(xscmpodp, 1) @@ -2520,10 +2580,10 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ } \ if (vxsnan_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ if (vxvc_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + float_invalid_op_vxvc(env, 0, GETPC()); \ } \ \ if (float128_lt(xa.f128, xb.f128, &env->fp_status)) { \ @@ -2538,7 +2598,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ env->fpscr |= cc << FPSCR_FPRF; \ env->crf[BF(opcode)] = cc; \ \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_SCALAR_CMPQ(xscmpoqp, 1) @@ -2565,12 +2625,12 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ xt.fld = tp##_##op(xa.fld, xb.fld, &env->fp_status); \ if (unlikely(tp##_is_signaling_nan(xa.fld, &env->fp_status) || \ tp##_is_signaling_nan(xb.fld, &env->fp_status))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_MAX_MIN(xsmaxdp, maxnum, 1, float64, VsrD(0)) @@ -2608,7 +2668,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ \ vex_flag = fpscr_ve & vxsnan_flag; \ if (vxsnan_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ if (!vex_flag) { \ putVSR(rD(opcode) + 32, &xt, env); \ @@ -2663,7 +2723,7 @@ void helper_##name(CPUPPCState *env, uint32_t opcode) \ \ vex_flag = fpscr_ve & vxsnan_flag; \ if (vxsnan_flag) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ if (!vex_flag) { \ putVSR(rD(opcode) + 32, &xt, env); \ @@ -2699,10 +2759,10 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ tp##_is_any_nan(xb.fld))) { \ if (tp##_is_signaling_nan(xa.fld, &env->fp_status) || \ tp##_is_signaling_nan(xb.fld, &env->fp_status)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ } \ if (svxvc) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXVC, 0); \ + float_invalid_op_vxvc(env, 0, GETPC()); \ } \ xt.fld = 0; \ all_true = 0; \ @@ -2721,7 +2781,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ if ((opcode >> (31-21)) & 1) { \ env->crf[6] = (all_true ? 0x8 : 0) | (all_false ? 0x2 : 0); \ } \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CMP(xvcmpeqdp, 2, float64, VsrD(i), eq, 0, 1) @@ -2755,7 +2815,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb.sfld, \ &env->fp_status))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ } \ if (sfprf) { \ @@ -2764,7 +2824,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_FP_TO_FP(xscvdpsp, 1, float64, float32, VsrD(0), VsrW(0), 1) @@ -2794,7 +2854,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.tfld = stp##_to_##ttp(xb.sfld, &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb.sfld, \ &env->fp_status))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ } \ if (sfprf) { \ @@ -2803,7 +2863,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(rD(opcode) + 32, &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_FP_TO_FP_VECTOR(xscvdpqp, 1, float64, float128, VsrD(0), f128, 1) @@ -2831,7 +2891,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ xt.tfld = stp##_to_##ttp(xb.sfld, 1, &env->fp_status); \ if (unlikely(stp##_is_signaling_nan(xb.sfld, \ &env->fp_status))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ xt.tfld = ttp##_snan_to_qnan(xt.tfld); \ } \ if (sfprf) { \ @@ -2840,7 +2900,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_FP_TO_FP_HP(xscvdphp, 1, float64, float16, VsrD(0), VsrH(3), 1) @@ -2867,15 +2927,14 @@ void helper_xscvqpdp(CPUPPCState *env, uint32_t opcode) xt.VsrD(0) = float128_to_float64(xb.f128, &tstat); env->fp_status.float_exception_flags |= tstat.float_exception_flags; - if (unlikely(float128_is_signaling_nan(xb.f128, - &tstat))) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); + if (unlikely(float128_is_signaling_nan(xb.f128, &tstat))) { + float_invalid_op_vxsnan(env, GETPC()); xt.VsrD(0) = float64_snan_to_qnan(xt.VsrD(0)); } helper_compute_fprf_float64(env, xt.VsrD(0)); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } uint64_t helper_xscvdpspn(CPUPPCState *env, uint64_t xb) @@ -2906,6 +2965,7 @@ uint64_t helper_xscvspdpn(CPUPPCState *env, uint64_t xb) #define VSX_CVT_FP_TO_INT(op, nels, stp, ttp, sfld, tfld, rnan) \ void helper_##op(CPUPPCState *env, uint32_t opcode) \ { \ + int all_flags = env->fp_status.float_exception_flags, flags; \ ppc_vsr_t xt, xb; \ int i; \ \ @@ -2913,23 +2973,19 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ getVSR(xT(opcode), &xt, env); \ \ for (i = 0; i < nels; i++) { \ - if (unlikely(stp##_is_any_nan(xb.sfld))) { \ - if (stp##_is_signaling_nan(xb.sfld, &env->fp_status)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ - } \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + env->fp_status.float_exception_flags = 0; \ + xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld, &env->fp_status); \ + flags = env->fp_status.float_exception_flags; \ + if (unlikely(flags & float_flag_invalid)) { \ + float_invalid_cvt(env, 0, GETPC(), stp##_classify(xb.sfld)); \ xt.tfld = rnan; \ - } else { \ - xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld, \ - &env->fp_status); \ - if (env->fp_status.float_exception_flags & float_flag_invalid) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ - } \ } \ + all_flags |= flags; \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + env->fp_status.float_exception_flags = all_flags; \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_FP_TO_INT(xscvdpsxds, 1, float64, int64, VsrD(0), VsrD(0), \ @@ -2966,22 +3022,14 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ getVSR(rB(opcode) + 32, &xb, env); \ memset(&xt, 0, sizeof(xt)); \ \ - if (unlikely(stp##_is_any_nan(xb.sfld))) { \ - if (stp##_is_signaling_nan(xb.sfld, &env->fp_status)) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ - } \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ + xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld, &env->fp_status); \ + if (env->fp_status.float_exception_flags & float_flag_invalid) { \ + float_invalid_cvt(env, 0, GETPC(), stp##_classify(xb.sfld)); \ xt.tfld = rnan; \ - } else { \ - xt.tfld = stp##_to_##ttp##_round_to_zero(xb.sfld, \ - &env->fp_status); \ - if (env->fp_status.float_exception_flags & float_flag_invalid) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXCVI, 0); \ - } \ } \ \ putVSR(rD(opcode) + 32, &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_FP_TO_INT_VECTOR(xscvqpsdz, float128, int64, f128, VsrD(0), \ @@ -3022,7 +3070,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_INT_TO_FP(xscvsxddp, 1, int64, float64, VsrD(0), VsrD(0), 1, 0) @@ -3057,7 +3105,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ helper_compute_fprf_##ttp(env, xt.tfld); \ \ putVSR(xT(opcode) + 32, &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_CVT_INT_TO_FP_VECTOR(xscvsdqp, int64, float128, VsrD(0), f128) @@ -3092,7 +3140,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ for (i = 0; i < nels; i++) { \ if (unlikely(tp##_is_signaling_nan(xb.fld, \ &env->fp_status))) { \ - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); \ + float_invalid_op_vxsnan(env, GETPC()); \ xt.fld = tp##_snan_to_qnan(xb.fld); \ } else { \ xt.fld = tp##_round_to_int(xb.fld, &env->fp_status); \ @@ -3111,7 +3159,7 @@ void helper_##op(CPUPPCState *env, uint32_t opcode) \ } \ \ putVSR(xT(opcode), &xt, env); \ - float_check_status(env); \ + do_float_check_status(env, GETPC()); \ } VSX_ROUND(xsrdpi, 1, float64, VsrD(0), float_round_ties_away, 1) @@ -3139,7 +3187,7 @@ uint64_t helper_xsrsp(CPUPPCState *env, uint64_t xb) uint64_t xt = helper_frsp(env, xb); helper_compute_fprf_float64(env, xt); - float_check_status(env); + do_float_check_status(env, GETPC()); return xt; } @@ -3321,7 +3369,7 @@ void helper_xsrqpi(CPUPPCState *env, uint32_t opcode) if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { if (float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); + float_invalid_op_vxsnan(env, GETPC()); xt.f128 = float128_snan_to_qnan(xt.f128); } } @@ -3331,7 +3379,7 @@ void helper_xsrqpi(CPUPPCState *env, uint32_t opcode) } helper_compute_fprf_float128(env, xt.f128); - float_check_status(env); + do_float_check_status(env, GETPC()); putVSR(rD(opcode) + 32, &xt, env); } @@ -3381,14 +3429,14 @@ void helper_xsrqpxp(CPUPPCState *env, uint32_t opcode) if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { if (float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 0); + float_invalid_op_vxsnan(env, GETPC()); xt.f128 = float128_snan_to_qnan(xt.f128); } } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } void helper_xssqrtqp(CPUPPCState *env, uint32_t opcode) @@ -3412,19 +3460,19 @@ void helper_xssqrtqp(CPUPPCState *env, uint32_t opcode) if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { if (float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); + float_invalid_op_vxsnan(env, GETPC()); xt.f128 = float128_snan_to_qnan(xb.f128); } else if (float128_is_quiet_nan(xb.f128, &tstat)) { xt.f128 = xb.f128; } else if (float128_is_neg(xb.f128) && !float128_is_zero(xb.f128)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSQRT, 1); + float_invalid_op_vxsqrt(env, 1, GETPC()); xt.f128 = float128_default_nan(&env->fp_status); } } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } void helper_xssubqp(CPUPPCState *env, uint32_t opcode) @@ -3447,15 +3495,12 @@ void helper_xssubqp(CPUPPCState *env, uint32_t opcode) env->fp_status.float_exception_flags |= tstat.float_exception_flags; if (unlikely(tstat.float_exception_flags & float_flag_invalid)) { - if (float128_is_infinity(xa.f128) && float128_is_infinity(xb.f128)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXISI, 1); - } else if (float128_is_signaling_nan(xa.f128, &tstat) || - float128_is_signaling_nan(xb.f128, &tstat)) { - float_invalid_op_excp(env, POWERPC_EXCP_FP_VXSNAN, 1); - } + float_invalid_op_addsub(env, 1, GETPC(), + float128_classify(xa.f128) | + float128_classify(xb.f128)); } helper_compute_fprf_float128(env, xt.f128); putVSR(rD(opcode) + 32, &xt, env); - float_check_status(env); + do_float_check_status(env, GETPC()); } diff --git a/target/ppc/gdbstub.c b/target/ppc/gdbstub.c index b6f6693583..fbf3821f4b 100644 --- a/target/ppc/gdbstub.c +++ b/target/ppc/gdbstub.c @@ -126,7 +126,7 @@ int ppc_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) gdb_get_regl(mem_buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, env->fpr[n-32]); + stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); } else { switch (n) { case 64: @@ -178,7 +178,7 @@ int ppc_cpu_gdb_read_register_apple(CPUState *cs, uint8_t *mem_buf, int n) gdb_get_reg64(mem_buf, env->gpr[n]); } else if (n < 64) { /* fprs */ - stfq_p(mem_buf, env->fpr[n-32]); + stfq_p(mem_buf, *cpu_fpr_ptr(env, n - 32)); } else if (n < 96) { /* Altivec */ stq_p(mem_buf, n - 64); @@ -234,7 +234,7 @@ int ppc_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->gpr[n] = ldtul_p(mem_buf); } else if (n < 64) { /* fprs */ - env->fpr[n-32] = ldfq_p(mem_buf); + *cpu_fpr_ptr(env, n - 32) = ldfq_p(mem_buf); } else { switch (n) { case 64: @@ -284,7 +284,7 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) env->gpr[n] = ldq_p(mem_buf); } else if (n < 64) { /* fprs */ - env->fpr[n-32] = ldfq_p(mem_buf); + *cpu_fpr_ptr(env, n - 32) = ldfq_p(mem_buf); } else { switch (n) { case 64 + 32: @@ -319,3 +319,64 @@ int ppc_cpu_gdb_write_register_apple(CPUState *cs, uint8_t *mem_buf, int n) } return r; } + +#ifndef CONFIG_USER_ONLY +void ppc_gdb_gen_spr_xml(PowerPCCPU *cpu) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cpu); + CPUPPCState *env = &cpu->env; + GString *xml; + char *spr_name; + unsigned int num_regs = 0; + int i; + + if (pcc->gdb_spr_xml) { + return; + } + + xml = g_string_new(""); + g_string_append(xml, ""); + g_string_append(xml, ""); + + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (!spr->name) { + continue; + } + + spr_name = g_ascii_strdown(spr->name, -1); + g_string_append_printf(xml, ""); + + /* + * GDB identifies registers based on the order they are + * presented in the XML. These ids will not match QEMU's + * representation (which follows the PowerISA). + * + * Store the position of the current register description so + * we can make the correspondence later. + */ + spr->gdb_id = num_regs; + num_regs++; + } + + g_string_append(xml, ""); + + pcc->gdb_num_sprs = num_regs; + pcc->gdb_spr_xml = g_string_free(xml, false); +} + +const char *ppc_gdb_get_dynamic_xml(CPUState *cs, const char *xml_name) +{ + PowerPCCPUClass *pcc = POWERPC_CPU_GET_CLASS(cs); + + if (strcmp(xml_name, "power-spr.xml") == 0) { + return pcc->gdb_spr_xml; + } + return NULL; +} +#endif diff --git a/target/ppc/helper.h b/target/ppc/helper.h index 5706c2497f..638a6e99c4 100644 --- a/target/ppc/helper.h +++ b/target/ppc/helper.h @@ -29,7 +29,9 @@ DEF_HELPER_4(lsw, void, env, tl, i32, i32) DEF_HELPER_5(lswx, void, env, tl, i32, i32, i32) DEF_HELPER_FLAGS_4(stsw, TCG_CALL_NO_WG, void, env, tl, i32, i32) DEF_HELPER_FLAGS_3(dcbz, TCG_CALL_NO_WG, void, env, tl, i32) +DEF_HELPER_FLAGS_3(dcbzep, TCG_CALL_NO_WG, void, env, tl, i32) DEF_HELPER_FLAGS_2(icbi, TCG_CALL_NO_WG, void, env, tl) +DEF_HELPER_FLAGS_2(icbiep, TCG_CALL_NO_WG, void, env, tl) DEF_HELPER_5(lscbx, tl, env, tl, i32, i32, i32) #if defined(TARGET_PPC64) @@ -61,8 +63,8 @@ DEF_HELPER_2(compute_fprf_float64, void, env, i64) DEF_HELPER_3(store_fpscr, void, env, i64, i32) DEF_HELPER_2(fpscr_clrbit, void, env, i32) DEF_HELPER_2(fpscr_setbit, void, env, i32) -DEF_HELPER_2(float64_to_float32, i32, env, i64) -DEF_HELPER_2(float32_to_float64, i64, env, i32) +DEF_HELPER_FLAGS_1(todouble, TCG_CALL_NO_RWG_SE, i64, i32) +DEF_HELPER_FLAGS_1(tosingle, TCG_CALL_NO_RWG_SE, i32, i64) DEF_HELPER_4(fcmpo, void, env, i64, i64, i32) DEF_HELPER_4(fcmpu, void, env, i64, i64, i32) @@ -85,15 +87,15 @@ DEF_HELPER_2(friz, i64, env, i64) DEF_HELPER_2(frip, i64, env, i64) DEF_HELPER_2(frim, i64, env, i64) -DEF_HELPER_3(fadd, i64, env, i64, i64) -DEF_HELPER_3(fsub, i64, env, i64, i64) -DEF_HELPER_3(fmul, i64, env, i64, i64) -DEF_HELPER_3(fdiv, i64, env, i64, i64) +DEF_HELPER_3(fadd, f64, env, f64, f64) +DEF_HELPER_3(fsub, f64, env, f64, f64) +DEF_HELPER_3(fmul, f64, env, f64, f64) +DEF_HELPER_3(fdiv, f64, env, f64, f64) DEF_HELPER_4(fmadd, i64, env, i64, i64, i64) DEF_HELPER_4(fmsub, i64, env, i64, i64, i64) DEF_HELPER_4(fnmadd, i64, env, i64, i64, i64) DEF_HELPER_4(fnmsub, i64, env, i64, i64, i64) -DEF_HELPER_2(fsqrt, i64, env, i64) +DEF_HELPER_2(fsqrt, f64, env, f64) DEF_HELPER_2(fre, i64, env, i64) DEF_HELPER_2(fres, i64, env, i64) DEF_HELPER_2(frsqrte, i64, env, i64) @@ -106,14 +108,6 @@ DEF_HELPER_FLAGS_1(ftsqrt, TCG_CALL_NO_RWG_SE, i32, i64) #define dh_ctype_avr ppc_avr_t * #define dh_is_signed_avr dh_is_signed_ptr -DEF_HELPER_3(vaddubm, void, avr, avr, avr) -DEF_HELPER_3(vadduhm, void, avr, avr, avr) -DEF_HELPER_3(vadduwm, void, avr, avr, avr) -DEF_HELPER_3(vaddudm, void, avr, avr, avr) -DEF_HELPER_3(vsububm, void, avr, avr, avr) -DEF_HELPER_3(vsubuhm, void, avr, avr, avr) -DEF_HELPER_3(vsubuwm, void, avr, avr, avr) -DEF_HELPER_3(vsubudm, void, avr, avr, avr) DEF_HELPER_3(vavgub, void, avr, avr, avr) DEF_HELPER_3(vavguh, void, avr, avr, avr) DEF_HELPER_3(vavguw, void, avr, avr, avr) @@ -123,22 +117,6 @@ DEF_HELPER_3(vabsduw, void, avr, avr, avr) DEF_HELPER_3(vavgsb, void, avr, avr, avr) DEF_HELPER_3(vavgsh, void, avr, avr, avr) DEF_HELPER_3(vavgsw, void, avr, avr, avr) -DEF_HELPER_3(vminsb, void, avr, avr, avr) -DEF_HELPER_3(vminsh, void, avr, avr, avr) -DEF_HELPER_3(vminsw, void, avr, avr, avr) -DEF_HELPER_3(vminsd, void, avr, avr, avr) -DEF_HELPER_3(vmaxsb, void, avr, avr, avr) -DEF_HELPER_3(vmaxsh, void, avr, avr, avr) -DEF_HELPER_3(vmaxsw, void, avr, avr, avr) -DEF_HELPER_3(vmaxsd, void, avr, avr, avr) -DEF_HELPER_3(vminub, void, avr, avr, avr) -DEF_HELPER_3(vminuh, void, avr, avr, avr) -DEF_HELPER_3(vminuw, void, avr, avr, avr) -DEF_HELPER_3(vminud, void, avr, avr, avr) -DEF_HELPER_3(vmaxub, void, avr, avr, avr) -DEF_HELPER_3(vmaxuh, void, avr, avr, avr) -DEF_HELPER_3(vmaxuw, void, avr, avr, avr) -DEF_HELPER_3(vmaxud, void, avr, avr, avr) DEF_HELPER_4(vcmpequb, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequh, void, env, avr, avr, avr) DEF_HELPER_4(vcmpequw, void, env, avr, avr, avr) @@ -225,18 +203,18 @@ DEF_HELPER_2(vprtybq, void, avr, avr) DEF_HELPER_3(vsubcuw, void, avr, avr, avr) DEF_HELPER_2(lvsl, void, avr, tl) DEF_HELPER_2(lvsr, void, avr, tl) -DEF_HELPER_4(vaddsbs, void, env, avr, avr, avr) -DEF_HELPER_4(vaddshs, void, env, avr, avr, avr) -DEF_HELPER_4(vaddsws, void, env, avr, avr, avr) -DEF_HELPER_4(vsubsbs, void, env, avr, avr, avr) -DEF_HELPER_4(vsubshs, void, env, avr, avr, avr) -DEF_HELPER_4(vsubsws, void, env, avr, avr, avr) -DEF_HELPER_4(vaddubs, void, env, avr, avr, avr) -DEF_HELPER_4(vadduhs, void, env, avr, avr, avr) -DEF_HELPER_4(vadduws, void, env, avr, avr, avr) -DEF_HELPER_4(vsububs, void, env, avr, avr, avr) -DEF_HELPER_4(vsubuhs, void, env, avr, avr, avr) -DEF_HELPER_4(vsubuws, void, env, avr, avr, avr) +DEF_HELPER_FLAGS_5(vaddsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vaddshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vaddsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vsubsbs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vsubshs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vsubsws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vaddubs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vadduhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vadduws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vsububs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vsubuhs, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) +DEF_HELPER_FLAGS_5(vsubuws, TCG_CALL_NO_RWG, void, avr, avr, avr, avr, i32) DEF_HELPER_3(vadduqm, void, avr, avr, avr) DEF_HELPER_4(vaddecuq, void, avr, avr, avr, avr) DEF_HELPER_4(vaddeuqm, void, avr, avr, avr, avr) @@ -252,12 +230,6 @@ DEF_HELPER_3(vrld, void, avr, avr, avr) DEF_HELPER_3(vsl, void, avr, avr, avr) DEF_HELPER_3(vsr, void, avr, avr, avr) DEF_HELPER_4(vsldoi, void, avr, avr, avr, i32) -DEF_HELPER_2(vspltisb, void, avr, i32) -DEF_HELPER_2(vspltish, void, avr, i32) -DEF_HELPER_2(vspltisw, void, avr, i32) -DEF_HELPER_3(vspltb, void, avr, avr, i32) -DEF_HELPER_3(vsplth, void, avr, avr, i32) -DEF_HELPER_3(vspltw, void, avr, avr, i32) DEF_HELPER_3(vextractub, void, avr, avr, i32) DEF_HELPER_3(vextractuh, void, avr, avr, i32) DEF_HELPER_3(vextractuw, void, avr, avr, i32) @@ -306,7 +278,8 @@ DEF_HELPER_5(vmsumuhs, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmsumshm, void, env, avr, avr, avr, avr) DEF_HELPER_5(vmsumshs, void, env, avr, avr, avr, avr) DEF_HELPER_4(vmladduhm, void, avr, avr, avr, avr) -DEF_HELPER_2(mtvscr, void, env, avr) +DEF_HELPER_FLAGS_2(mtvscr, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_1(mfvscr, TCG_CALL_NO_RWG, i32, env) DEF_HELPER_3(lvebx, void, env, avr, tl) DEF_HELPER_3(lvehx, void, env, avr, tl) DEF_HELPER_3(lvewx, void, env, avr, tl) @@ -658,6 +631,8 @@ DEF_HELPER_2(booke206_tlbilx1, void, env, tl) DEF_HELPER_2(booke206_tlbilx3, void, env, tl) DEF_HELPER_2(booke206_tlbflush, void, env, tl) DEF_HELPER_3(booke_setpid, void, env, i32, tl) +DEF_HELPER_2(booke_set_eplc, void, env, tl) +DEF_HELPER_2(booke_set_epsc, void, env, tl) DEF_HELPER_2(6xx_tlbd, void, env, tl) DEF_HELPER_2(6xx_tlbi, void, env, tl) DEF_HELPER_2(74xx_tlbd, void, env, tl) @@ -714,6 +689,7 @@ DEF_HELPER_2(store_ptcr, void, env, tl) #endif DEF_HELPER_2(store_sdr1, void, env, tl) DEF_HELPER_2(store_pidr, void, env, tl) +DEF_HELPER_2(store_lpidr, void, env, tl) DEF_HELPER_FLAGS_2(store_tbl, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_tbu, TCG_CALL_NO_RWG, void, env, tl) DEF_HELPER_FLAGS_2(store_atbl, TCG_CALL_NO_RWG, void, env, tl) @@ -800,7 +776,7 @@ DEF_HELPER_4(dscliq, void, env, fprp, fprp, i32) DEF_HELPER_1(tbegin, void, env) DEF_HELPER_FLAGS_1(fixup_thrm, TCG_CALL_NO_RWG, void, env) -#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) +#ifdef TARGET_PPC64 DEF_HELPER_FLAGS_3(lq_le_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) DEF_HELPER_FLAGS_3(lq_be_parallel, TCG_CALL_NO_WG, i64, env, tl, i32) DEF_HELPER_FLAGS_5(stq_le_parallel, TCG_CALL_NO_WG, diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 5efd18049e..c863abc0bf 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -152,7 +152,7 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, * - 64-bit embedded implementations do not need any operation to be * performed when PR is set. */ - if ((env->insns_flags & PPC_SEGMENT_64B) && ((value >> MSR_PR) & 1)) { + if (is_book3s_arch2x(env) && ((value >> MSR_PR) & 1)) { value |= (1 << MSR_EE) | (1 << MSR_DR) | (1 << MSR_IR); } #endif @@ -174,26 +174,19 @@ static inline int hreg_store_msr(CPUPPCState *env, target_ulong value, static inline void check_tlb_flush(CPUPPCState *env, bool global) { CPUState *cs = CPU(ppc_env_get_cpu(env)); - if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) { - tlb_flush(cs); + + /* Handle global flushes first */ + if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { + env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; + tlb_flush_all_cpus_synced(cs); + return; } - /* Propagate TLB invalidations to other CPUs when the guest uses broadcast - * TLB invalidation instructions. - */ - if (global && (env->tlb_need_flush & TLB_NEED_GLOBAL_FLUSH)) { - CPUState *other_cs; - CPU_FOREACH(other_cs) { - if (other_cs != cs) { - PowerPCCPU *cpu = POWERPC_CPU(other_cs); - CPUPPCState *other_env = &cpu->env; - - other_env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; - tlb_flush(other_cs); - } - } - env->tlb_need_flush &= ~TLB_NEED_GLOBAL_FLUSH; + /* Then handle local ones */ + if (env->tlb_need_flush & TLB_NEED_LOCAL_FLUSH) { + env->tlb_need_flush &= ~TLB_NEED_LOCAL_FLUSH; + tlb_flush(cs); } } #else diff --git a/target/ppc/int_helper.c b/target/ppc/int_helper.c index d52338ed71..162add561e 100644 --- a/target/ppc/int_helper.c +++ b/target/ppc/int_helper.c @@ -388,18 +388,6 @@ target_ulong helper_602_mfrom(target_ulong arg) /*****************************************************************************/ /* Altivec extension helpers */ -#if defined(HOST_WORDS_BIGENDIAN) -#define HI_IDX 0 -#define LO_IDX 1 -#define AVRB(i) u8[i] -#define AVRW(i) u32[i] -#else -#define HI_IDX 1 -#define LO_IDX 0 -#define AVRB(i) u8[15-(i)] -#define AVRW(i) u32[3-(i)] -#endif - #if defined(HOST_WORDS_BIGENDIAN) #define VECTOR_FOR_INORDER_I(index, element) \ for (index = 0; index < ARRAY_SIZE(r->element); index++) @@ -455,8 +443,8 @@ void helper_lvsl(ppc_avr_t *r, target_ulong sh) { int i, j = (sh & 0xf); - VECTOR_FOR_INORDER_I(i, u8) { - r->u8[i] = j++; + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + r->VsrB(i) = j++; } } @@ -464,19 +452,30 @@ void helper_lvsr(ppc_avr_t *r, target_ulong sh) { int i, j = 0x10 - (sh & 0xf); - VECTOR_FOR_INORDER_I(i, u8) { - r->u8[i] = j++; + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + r->VsrB(i) = j++; } } -void helper_mtvscr(CPUPPCState *env, ppc_avr_t *r) +void helper_mtvscr(CPUPPCState *env, uint32_t vscr) { -#if defined(HOST_WORDS_BIGENDIAN) - env->vscr = r->u32[3]; -#else - env->vscr = r->u32[0]; -#endif - set_flush_to_zero(vscr_nj, &env->vec_status); + env->vscr = vscr & ~(1u << VSCR_SAT); + /* Which bit we set is completely arbitrary, but clear the rest. */ + env->vscr_sat.u64[0] = vscr & (1u << VSCR_SAT); + env->vscr_sat.u64[1] = 0; + set_flush_to_zero((vscr >> VSCR_NJ) & 1, &env->vec_status); +} + +uint32_t helper_mfvscr(CPUPPCState *env) +{ + uint32_t sat = (env->vscr_sat.u64[0] | env->vscr_sat.u64[1]) != 0; + return env->vscr | (sat << VSCR_SAT); +} + +static inline void set_vscr_sat(CPUPPCState *env) +{ + /* The choice of non-zero value is arbitrary. */ + env->vscr_sat.u32[0] = 1; } void helper_vaddcuw(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) @@ -518,8 +517,8 @@ void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b) res ^= res >> 32; res ^= res >> 16; res ^= res >> 8; - r->u64[LO_IDX] = res & 1; - r->u64[HI_IDX] = 0; + r->VsrD(1) = res & 1; + r->VsrD(0) = 0; } #define VARITH_DO(name, op, element) \ @@ -531,13 +530,6 @@ void helper_vprtybq(ppc_avr_t *r, ppc_avr_t *b) r->element[i] = a->element[i] op b->element[i]; \ } \ } -#define VARITH(suffix, element) \ - VARITH_DO(add##suffix, +, element) \ - VARITH_DO(sub##suffix, -, element) -VARITH(ubm, u8) -VARITH(uhm, u16) -VARITH(uwm, u32) -VARITH(udm, u64) VARITH_DO(muluwm, *, u32) #undef VARITH_DO #undef VARITH @@ -548,8 +540,8 @@ VARITH_DO(muluwm, *, u32) { \ int i; \ \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - r->f[i] = func(a->f[i], b->f[i], &env->vec_status); \ + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { \ + r->f32[i] = func(a->f32[i], b->f32[i], &env->vec_status); \ } \ } VARITHFP(addfp, float32_add) @@ -563,9 +555,9 @@ VARITHFP(maxfp, float32_max) ppc_avr_t *b, ppc_avr_t *c) \ { \ int i; \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - r->f[i] = float32_muladd(a->f[i], c->f[i], b->f[i], \ - type, &env->vec_status); \ + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { \ + r->f32[i] = float32_muladd(a->f32[i], c->f32[i], b->f32[i], \ + type, &env->vec_status); \ } \ } VARITHFPFMA(maddfp, 0); @@ -579,27 +571,17 @@ VARITHFPFMA(nmsubfp, float_muladd_negate_result | float_muladd_negate_c); } #define VARITHSAT_DO(name, op, optype, cvt, element) \ - void helper_v##name(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, \ - ppc_avr_t *b) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *vscr_sat, \ + ppc_avr_t *a, ppc_avr_t *b, uint32_t desc) \ { \ int sat = 0; \ int i; \ \ for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - switch (sizeof(r->element[0])) { \ - case 1: \ - VARITHSAT_CASE(optype, op, cvt, element); \ - break; \ - case 2: \ - VARITHSAT_CASE(optype, op, cvt, element); \ - break; \ - case 4: \ - VARITHSAT_CASE(optype, op, cvt, element); \ - break; \ - } \ + VARITHSAT_CASE(optype, op, cvt, element); \ } \ if (sat) { \ - env->vscr |= (1 << VSCR_SAT); \ + vscr_sat->u32[0] = 1; \ } \ } #define VARITHSAT_SIGNED(suffix, element, optype, cvt) \ @@ -670,9 +652,9 @@ VABSDU(w, u32) { \ int i; \ \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { \ float32 t = cvt(b->element[i], &env->vec_status); \ - r->f[i] = float32_scalbn(t, -uim, &env->vec_status); \ + r->f32[i] = float32_scalbn(t, -uim, &env->vec_status); \ } \ } VCF(ux, uint32_to_float32, u32) @@ -782,9 +764,9 @@ VCMPNE(w, u32, uint32_t, 0) uint32_t none = 0; \ int i; \ \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { \ uint32_t result; \ - int rel = float32_compare_quiet(a->f[i], b->f[i], \ + int rel = float32_compare_quiet(a->f32[i], b->f32[i], \ &env->vec_status); \ if (rel == float_relation_unordered) { \ result = 0; \ @@ -816,14 +798,16 @@ static inline void vcmpbfp_internal(CPUPPCState *env, ppc_avr_t *r, int i; int all_in = 0; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - int le_rel = float32_compare_quiet(a->f[i], b->f[i], &env->vec_status); + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { + int le_rel = float32_compare_quiet(a->f32[i], b->f32[i], + &env->vec_status); if (le_rel == float_relation_unordered) { r->u32[i] = 0xc0000000; all_in = 1; } else { - float32 bneg = float32_chs(b->f[i]); - int ge_rel = float32_compare_quiet(a->f[i], bneg, &env->vec_status); + float32 bneg = float32_chs(b->f32[i]); + int ge_rel = float32_compare_quiet(a->f32[i], bneg, + &env->vec_status); int le = le_rel != float_relation_greater; int ge = ge_rel != float_relation_less; @@ -856,11 +840,11 @@ void helper_vcmpbfp_dot(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, float_status s = env->vec_status; \ \ set_float_rounding_mode(float_round_to_zero, &s); \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - if (float32_is_any_nan(b->f[i])) { \ + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { \ + if (float32_is_any_nan(b->f32[i])) { \ r->element[i] = 0; \ } else { \ - float64 t = float32_to_float64(b->f[i], &s); \ + float64 t = float32_to_float64(b->f32[i], &s); \ int64_t j; \ \ t = float64_scalbn(t, uim, &s); \ @@ -869,7 +853,7 @@ void helper_vcmpbfp_dot(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } \ } \ if (sat) { \ - env->vscr |= (1 << VSCR_SAT); \ + set_vscr_sat(env); \ } \ } VCT(uxs, cvtsduw, u32) @@ -880,8 +864,8 @@ target_ulong helper_vclzlsbb(ppc_avr_t *r) { target_ulong count = 0; int i; - VECTOR_FOR_INORDER_I(i, u8) { - if (r->u8[i] & 0x01) { + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + if (r->VsrB(i) & 0x01) { break; } count++; @@ -893,12 +877,8 @@ target_ulong helper_vctzlsbb(ppc_avr_t *r) { target_ulong count = 0; int i; -#if defined(HOST_WORDS_BIGENDIAN) for (i = ARRAY_SIZE(r->u8) - 1; i >= 0; i--) { -#else - for (i = 0; i < ARRAY_SIZE(r->u8); i++) { -#endif - if (r->u8[i] & 0x01) { + if (r->VsrB(i) & 0x01) { break; } count++; @@ -920,7 +900,7 @@ void helper_vmhaddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -937,37 +917,10 @@ void helper_vmhraddshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } -#define VMINMAX_DO(name, compare, element) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - if (a->element[i] compare b->element[i]) { \ - r->element[i] = b->element[i]; \ - } else { \ - r->element[i] = a->element[i]; \ - } \ - } \ - } -#define VMINMAX(suffix, element) \ - VMINMAX_DO(min##suffix, >, element) \ - VMINMAX_DO(max##suffix, <, element) -VMINMAX(sb, s8) -VMINMAX(sh, s16) -VMINMAX(sw, s32) -VMINMAX(sd, s64) -VMINMAX(ub, u8) -VMINMAX(uh, u16) -VMINMAX(uw, u32) -VMINMAX(ud, u64) -#undef VMINMAX_DO -#undef VMINMAX - void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) { int i; @@ -978,43 +931,27 @@ void helper_vmladduhm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) } } -#define VMRG_DO(name, element, highp) \ - void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ - { \ - ppc_avr_t result; \ - int i; \ - size_t n_elems = ARRAY_SIZE(r->element); \ - \ - for (i = 0; i < n_elems / 2; i++) { \ - if (highp) { \ - result.element[i*2+HI_IDX] = a->element[i]; \ - result.element[i*2+LO_IDX] = b->element[i]; \ - } else { \ - result.element[n_elems - i * 2 - (1 + HI_IDX)] = \ - b->element[n_elems - i - 1]; \ - result.element[n_elems - i * 2 - (1 + LO_IDX)] = \ - a->element[n_elems - i - 1]; \ - } \ - } \ - *r = result; \ +#define VMRG_DO(name, element, access, ofs) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + ppc_avr_t result; \ + int i, half = ARRAY_SIZE(r->element) / 2; \ + \ + for (i = 0; i < half; i++) { \ + result.access(i * 2 + 0) = a->access(i + ofs); \ + result.access(i * 2 + 1) = b->access(i + ofs); \ + } \ + *r = result; \ } -#if defined(HOST_WORDS_BIGENDIAN) -#define MRGHI 0 -#define MRGLO 1 -#else -#define MRGHI 1 -#define MRGLO 0 -#endif -#define VMRG(suffix, element) \ - VMRG_DO(mrgl##suffix, element, MRGHI) \ - VMRG_DO(mrgh##suffix, element, MRGLO) -VMRG(b, u8) -VMRG(h, u16) -VMRG(w, u32) + +#define VMRG(suffix, element, access) \ + VMRG_DO(mrgl##suffix, element, access, half) \ + VMRG_DO(mrgh##suffix, element, access, 0) +VMRG(b, u8, VsrB) +VMRG(h, u16, VsrH) +VMRG(w, u32, VsrW) #undef VMRG_DO #undef VMRG -#undef MRGHI -#undef MRGLO void helper_vmsummbm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) @@ -1065,7 +1002,7 @@ void helper_vmsumshs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -1118,37 +1055,43 @@ void helper_vmsumuhs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } -#define VMUL_DO(name, mul_element, prod_element, cast, evenp) \ +#define VMUL_DO_EVN(name, mul_element, mul_access, prod_access, cast) \ void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ int i; \ \ - VECTOR_FOR_INORDER_I(i, prod_element) { \ - if (evenp) { \ - r->prod_element[i] = \ - (cast)a->mul_element[i * 2 + HI_IDX] * \ - (cast)b->mul_element[i * 2 + HI_IDX]; \ - } else { \ - r->prod_element[i] = \ - (cast)a->mul_element[i * 2 + LO_IDX] * \ - (cast)b->mul_element[i * 2 + LO_IDX]; \ - } \ + for (i = 0; i < ARRAY_SIZE(r->mul_element); i += 2) { \ + r->prod_access(i >> 1) = (cast)a->mul_access(i) * \ + (cast)b->mul_access(i); \ } \ } -#define VMUL(suffix, mul_element, prod_element, cast) \ - VMUL_DO(mule##suffix, mul_element, prod_element, cast, 1) \ - VMUL_DO(mulo##suffix, mul_element, prod_element, cast, 0) -VMUL(sb, s8, s16, int16_t) -VMUL(sh, s16, s32, int32_t) -VMUL(sw, s32, s64, int64_t) -VMUL(ub, u8, u16, uint16_t) -VMUL(uh, u16, u32, uint32_t) -VMUL(uw, u32, u64, uint64_t) -#undef VMUL_DO + +#define VMUL_DO_ODD(name, mul_element, mul_access, prod_access, cast) \ + void helper_v##name(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ + { \ + int i; \ + \ + for (i = 0; i < ARRAY_SIZE(r->mul_element); i += 2) { \ + r->prod_access(i >> 1) = (cast)a->mul_access(i + 1) * \ + (cast)b->mul_access(i + 1); \ + } \ + } + +#define VMUL(suffix, mul_element, mul_access, prod_access, cast) \ + VMUL_DO_EVN(mule##suffix, mul_element, mul_access, prod_access, cast) \ + VMUL_DO_ODD(mulo##suffix, mul_element, mul_access, prod_access, cast) +VMUL(sb, s8, VsrSB, VsrSH, int16_t) +VMUL(sh, s16, VsrSH, VsrSW, int32_t) +VMUL(sw, s32, VsrSW, VsrSD, int64_t) +VMUL(ub, u8, VsrB, VsrH, uint16_t) +VMUL(uh, u16, VsrH, VsrW, uint32_t) +VMUL(uw, u32, VsrW, VsrD, uint64_t) +#undef VMUL_DO_EVN +#undef VMUL_DO_ODD #undef VMUL void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, @@ -1157,18 +1100,14 @@ void helper_vperm(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t result; int i; - VECTOR_FOR_INORDER_I(i, u8) { - int s = c->u8[i] & 0x1f; -#if defined(HOST_WORDS_BIGENDIAN) + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int s = c->VsrB(i) & 0x1f; int index = s & 0xf; -#else - int index = 15 - (s & 0xf); -#endif if (s & 0x10) { - result.u8[i] = b->u8[index]; + result.VsrB(i) = b->VsrB(index); } else { - result.u8[i] = a->u8[index]; + result.VsrB(i) = a->VsrB(index); } } *r = result; @@ -1180,18 +1119,14 @@ void helper_vpermr(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t result; int i; - VECTOR_FOR_INORDER_I(i, u8) { - int s = c->u8[i] & 0x1f; -#if defined(HOST_WORDS_BIGENDIAN) + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int s = c->VsrB(i) & 0x1f; int index = 15 - (s & 0xf); -#else - int index = s & 0xf; -#endif if (s & 0x10) { - result.u8[i] = a->u8[index]; + result.VsrB(i) = a->VsrB(index); } else { - result.u8[i] = b->u8[index]; + result.VsrB(i) = b->VsrB(index); } } *r = result; @@ -1241,8 +1176,8 @@ void helper_vbpermq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } } - r->u64[HI_IDX] = perm; - r->u64[LO_IDX] = 0; + r->VsrD(0) = perm; + r->VsrD(1) = 0; } #undef VBPERMQ_INDEX @@ -1571,25 +1506,25 @@ void helper_vpmsumd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) ppc_avr_t prod[2]; VECTOR_FOR_INORDER_I(i, u64) { - prod[i].u64[LO_IDX] = prod[i].u64[HI_IDX] = 0; + prod[i].VsrD(1) = prod[i].VsrD(0) = 0; for (j = 0; j < 64; j++) { if (a->u64[i] & (1ull<u64[i]; + bshift.VsrD(0) = 0; + bshift.VsrD(1) = b->u64[i]; } else { - bshift.u64[HI_IDX] = b->u64[i] >> (64-j); - bshift.u64[LO_IDX] = b->u64[i] << j; + bshift.VsrD(0) = b->u64[i] >> (64 - j); + bshift.VsrD(1) = b->u64[i] << j; } - prod[i].u64[LO_IDX] ^= bshift.u64[LO_IDX]; - prod[i].u64[HI_IDX] ^= bshift.u64[HI_IDX]; + prod[i].VsrD(1) ^= bshift.VsrD(1); + prod[i].VsrD(0) ^= bshift.VsrD(0); } } } - r->u64[LO_IDX] = prod[0].u64[LO_IDX] ^ prod[1].u64[LO_IDX]; - r->u64[HI_IDX] = prod[0].u64[HI_IDX] ^ prod[1].u64[HI_IDX]; + r->VsrD(1) = prod[0].VsrD(1) ^ prod[1].VsrD(1); + r->VsrD(0) = prod[0].VsrD(0) ^ prod[1].VsrD(0); #endif } @@ -1637,7 +1572,7 @@ void helper_vpkpx(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } \ *r = result; \ if (dosat && sat) { \ - env->vscr |= (1 << VSCR_SAT); \ + set_vscr_sat(env); \ } \ } #define I(x, y) (x) @@ -1661,8 +1596,8 @@ void helper_vrefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) { int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - r->f[i] = float32_div(float32_one, b->f[i], &env->vec_status); + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { + r->f32[i] = float32_div(float32_one, b->f32[i], &env->vec_status); } } @@ -1674,8 +1609,8 @@ void helper_vrefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) float_status s = env->vec_status; \ \ set_float_rounding_mode(rounding, &s); \ - for (i = 0; i < ARRAY_SIZE(r->f); i++) { \ - r->f[i] = float32_round_to_int (b->f[i], &s); \ + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { \ + r->f32[i] = float32_round_to_int (b->f32[i], &s); \ } \ } VRFI(n, float_round_nearest_even) @@ -1705,10 +1640,10 @@ void helper_vrsqrtefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) { int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - float32 t = float32_sqrt(b->f[i], &env->vec_status); + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { + float32 t = float32_sqrt(b->f32[i], &env->vec_status); - r->f[i] = float32_div(float32_one, t, &env->vec_status); + r->f32[i] = float32_div(float32_one, t, &env->vec_status); } } @@ -1751,8 +1686,8 @@ void helper_vexptefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) { int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - r->f[i] = float32_exp2(b->f[i], &env->vec_status); + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { + r->f32[i] = float32_exp2(b->f32[i], &env->vec_status); } } @@ -1760,8 +1695,8 @@ void helper_vlogefp(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *b) { int i; - for (i = 0; i < ARRAY_SIZE(r->f); i++) { - r->f[i] = float32_log2(b->f[i], &env->vec_status); + for (i = 0; i < ARRAY_SIZE(r->f32); i++) { + r->f32[i] = float32_log2(b->f32[i], &env->vec_status); } } @@ -1807,7 +1742,7 @@ VEXTU_X_DO(vextuwrx, 32, 0) #define VSHIFT(suffix, leftp) \ void helper_vs##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ - int shift = b->u8[LO_IDX*15] & 0x7; \ + int shift = b->VsrB(15) & 0x7; \ int doit = 1; \ int i; \ \ @@ -1818,15 +1753,15 @@ VEXTU_X_DO(vextuwrx, 32, 0) if (shift == 0) { \ *r = *a; \ } else if (leftp) { \ - uint64_t carry = a->u64[LO_IDX] >> (64 - shift); \ + uint64_t carry = a->VsrD(1) >> (64 - shift); \ \ - r->u64[HI_IDX] = (a->u64[HI_IDX] << shift) | carry; \ - r->u64[LO_IDX] = a->u64[LO_IDX] << shift; \ + r->VsrD(0) = (a->VsrD(0) << shift) | carry; \ + r->VsrD(1) = a->VsrD(1) << shift; \ } else { \ - uint64_t carry = a->u64[HI_IDX] << (64 - shift); \ + uint64_t carry = a->VsrD(0) << (64 - shift); \ \ - r->u64[LO_IDX] = (a->u64[LO_IDX] >> shift) | carry; \ - r->u64[HI_IDX] = a->u64[HI_IDX] >> shift; \ + r->VsrD(1) = (a->VsrD(1) >> shift) | carry; \ + r->VsrD(0) = a->VsrD(0) >> shift; \ } \ } \ } @@ -1888,31 +1823,20 @@ void helper_vsldoi(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t shift) int i; ppc_avr_t result; -#if defined(HOST_WORDS_BIGENDIAN) for (i = 0; i < ARRAY_SIZE(r->u8); i++) { int index = sh + i; if (index > 0xf) { - result.u8[i] = b->u8[index - 0x10]; + result.VsrB(i) = b->VsrB(index - 0x10); } else { - result.u8[i] = a->u8[index]; + result.VsrB(i) = a->VsrB(index); } } -#else - for (i = 0; i < ARRAY_SIZE(r->u8); i++) { - int index = (16 - sh) + i; - if (index > 0xf) { - result.u8[i] = a->u8[index - 0x10]; - } else { - result.u8[i] = b->u8[index]; - } - } -#endif *r = result; } void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - int sh = (b->u8[LO_IDX*0xf] >> 3) & 0xf; + int sh = (b->VsrB(0xf) >> 3) & 0xf; #if defined(HOST_WORDS_BIGENDIAN) memmove(&r->u8[0], &a->u8[sh], 16 - sh); @@ -1923,30 +1847,6 @@ void helper_vslo(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) #endif } -/* Experimental testing shows that hardware masks the immediate. */ -#define _SPLAT_MASKED(element) (splat & (ARRAY_SIZE(r->element) - 1)) -#if defined(HOST_WORDS_BIGENDIAN) -#define SPLAT_ELEMENT(element) _SPLAT_MASKED(element) -#else -#define SPLAT_ELEMENT(element) \ - (ARRAY_SIZE(r->element) - 1 - _SPLAT_MASKED(element)) -#endif -#define VSPLT(suffix, element) \ - void helper_vsplt##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t splat) \ - { \ - uint32_t s = b->element[SPLAT_ELEMENT(element)]; \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = s; \ - } \ - } -VSPLT(b, u8) -VSPLT(h, u16) -VSPLT(w, u32) -#undef VSPLT -#undef SPLAT_ELEMENT -#undef _SPLAT_MASKED #if defined(HOST_WORDS_BIGENDIAN) #define VINSERT(suffix, element) \ void helper_vinsert##suffix(ppc_avr_t *r, ppc_avr_t *b, uint32_t index) \ @@ -2004,17 +1904,10 @@ void helper_xxextractuw(CPUPPCState *env, target_ulong xtn, getVSR(xbn, &xb, env); memset(&xt, 0, sizeof(xt)); -#if defined(HOST_WORDS_BIGENDIAN) ext_index = index; for (i = 0; i < es; i++, ext_index++) { - xt.u8[8 - es + i] = xb.u8[ext_index % 16]; + xt.VsrB(8 - es + i) = xb.VsrB(ext_index % 16); } -#else - ext_index = 15 - index; - for (i = es - 1; i >= 0; i--, ext_index--) { - xt.u8[8 + i] = xb.u8[ext_index % 16]; - } -#endif putVSR(xtn, &xt, env); } @@ -2029,41 +1922,34 @@ void helper_xxinsertw(CPUPPCState *env, target_ulong xtn, getVSR(xbn, &xb, env); getVSR(xtn, &xt, env); -#if defined(HOST_WORDS_BIGENDIAN) ins_index = index; for (i = 0; i < es && ins_index < 16; i++, ins_index++) { - xt.u8[ins_index] = xb.u8[8 - es + i]; + xt.VsrB(ins_index) = xb.VsrB(8 - es + i); } -#else - ins_index = 15 - index; - for (i = es - 1; i >= 0 && ins_index >= 0; i--, ins_index--) { - xt.u8[ins_index] = xb.u8[8 + i]; - } -#endif putVSR(xtn, &xt, env); } -#define VEXT_SIGNED(name, element, mask, cast, recast) \ +#define VEXT_SIGNED(name, element, cast) \ void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ { \ int i; \ - VECTOR_FOR_INORDER_I(i, element) { \ - r->element[i] = (recast)((cast)(b->element[i] & mask)); \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ + r->element[i] = (cast)b->element[i]; \ } \ } -VEXT_SIGNED(vextsb2w, s32, UINT8_MAX, int8_t, int32_t) -VEXT_SIGNED(vextsb2d, s64, UINT8_MAX, int8_t, int64_t) -VEXT_SIGNED(vextsh2w, s32, UINT16_MAX, int16_t, int32_t) -VEXT_SIGNED(vextsh2d, s64, UINT16_MAX, int16_t, int64_t) -VEXT_SIGNED(vextsw2d, s64, UINT32_MAX, int32_t, int64_t) +VEXT_SIGNED(vextsb2w, s32, int8_t) +VEXT_SIGNED(vextsb2d, s64, int8_t) +VEXT_SIGNED(vextsh2w, s32, int16_t) +VEXT_SIGNED(vextsh2d, s64, int16_t) +VEXT_SIGNED(vextsw2d, s64, int32_t) #undef VEXT_SIGNED #define VNEG(name, element) \ void helper_##name(ppc_avr_t *r, ppc_avr_t *b) \ { \ int i; \ - VECTOR_FOR_INORDER_I(i, element) { \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ r->element[i] = -b->element[i]; \ } \ } @@ -2071,21 +1957,6 @@ VNEG(vnegw, s32) VNEG(vnegd, s64) #undef VNEG -#define VSPLTI(suffix, element, splat_type) \ - void helper_vspltis##suffix(ppc_avr_t *r, uint32_t splat) \ - { \ - splat_type x = (int8_t)(splat << 3) >> 3; \ - int i; \ - \ - for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ - r->element[i] = x; \ - } \ - } -VSPLTI(b, s8, int8_t) -VSPLTI(h, s16, int16_t) -VSPLTI(w, s32, int32_t) -#undef VSPLTI - #define VSR(suffix, element, mask) \ void helper_vsr##suffix(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) \ { \ @@ -2108,7 +1979,7 @@ VSR(d, u64, 0x3F) void helper_vsro(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) { - int sh = (b->u8[LO_IDX * 0xf] >> 3) & 0xf; + int sh = (b->VsrB(0xf) >> 3) & 0xf; #if defined(HOST_WORDS_BIGENDIAN) memmove(&r->u8[sh], &a->u8[0], 16 - sh); @@ -2135,21 +2006,17 @@ void helper_vsumsws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) ppc_avr_t result; int sat = 0; -#if defined(HOST_WORDS_BIGENDIAN) - upper = ARRAY_SIZE(r->s32)-1; -#else - upper = 0; -#endif - t = (int64_t)b->s32[upper]; + upper = ARRAY_SIZE(r->s32) - 1; + t = (int64_t)b->VsrSW(upper); for (i = 0; i < ARRAY_SIZE(r->s32); i++) { - t += a->s32[i]; - result.s32[i] = 0; + t += a->VsrSW(i); + result.VsrSW(i) = 0; } - result.s32[upper] = cvtsdsw(t, &sat); + result.VsrSW(upper) = cvtsdsw(t, &sat); *r = result; if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -2159,24 +2026,20 @@ void helper_vsum2sws(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) ppc_avr_t result; int sat = 0; -#if defined(HOST_WORDS_BIGENDIAN) upper = 1; -#else - upper = 0; -#endif for (i = 0; i < ARRAY_SIZE(r->u64); i++) { - int64_t t = (int64_t)b->s32[upper + i * 2]; + int64_t t = (int64_t)b->VsrSW(upper + i * 2); - result.u64[i] = 0; + result.VsrW(i) = 0; for (j = 0; j < ARRAY_SIZE(r->u64); j++) { - t += a->s32[2 * i + j]; + t += a->VsrSW(2 * i + j); } - result.s32[upper + i * 2] = cvtsdsw(t, &sat); + result.VsrSW(upper + i * 2) = cvtsdsw(t, &sat); } *r = result; if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -2195,7 +2058,7 @@ void helper_vsum4sbs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -2212,7 +2075,7 @@ void helper_vsum4shs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -2231,7 +2094,7 @@ void helper_vsum4ubs(CPUPPCState *env, ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) } if (sat) { - env->vscr |= (1 << VSCR_SAT); + set_vscr_sat(env); } } @@ -2296,7 +2159,7 @@ VUPK(lsw, s64, s32, UPKLO) { \ int i; \ \ - VECTOR_FOR_INORDER_I(i, element) { \ + for (i = 0; i < ARRAY_SIZE(r->element); i++) { \ r->element[i] = name(b->element[i]); \ } \ } @@ -2364,13 +2227,13 @@ static inline void avr_qw_not(ppc_avr_t *t, ppc_avr_t a) static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) { - if (a.u64[HI_IDX] < b.u64[HI_IDX]) { + if (a.VsrD(0) < b.VsrD(0)) { return -1; - } else if (a.u64[HI_IDX] > b.u64[HI_IDX]) { + } else if (a.VsrD(0) > b.VsrD(0)) { return 1; - } else if (a.u64[LO_IDX] < b.u64[LO_IDX]) { + } else if (a.VsrD(1) < b.VsrD(1)) { return -1; - } else if (a.u64[LO_IDX] > b.u64[LO_IDX]) { + } else if (a.VsrD(1) > b.VsrD(1)) { return 1; } else { return 0; @@ -2379,17 +2242,17 @@ static int avr_qw_cmpu(ppc_avr_t a, ppc_avr_t b) static void avr_qw_add(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) { - t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; - t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + - (~a.u64[LO_IDX] < b.u64[LO_IDX]); + t->VsrD(1) = a.VsrD(1) + b.VsrD(1); + t->VsrD(0) = a.VsrD(0) + b.VsrD(0) + + (~a.VsrD(1) < b.VsrD(1)); } static int avr_qw_addc(ppc_avr_t *t, ppc_avr_t a, ppc_avr_t b) { ppc_avr_t not_a; - t->u64[LO_IDX] = a.u64[LO_IDX] + b.u64[LO_IDX]; - t->u64[HI_IDX] = a.u64[HI_IDX] + b.u64[HI_IDX] + - (~a.u64[LO_IDX] < b.u64[LO_IDX]); + t->VsrD(1) = a.VsrD(1) + b.VsrD(1); + t->VsrD(0) = a.VsrD(0) + b.VsrD(0) + + (~a.VsrD(1) < b.VsrD(1)); avr_qw_not(¬_a, a); return avr_qw_cmpu(not_a, b) < 0; } @@ -2411,11 +2274,11 @@ void helper_vaddeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) r->u128 = a->u128 + b->u128 + (c->u128 & 1); #else - if (c->u64[LO_IDX] & 1) { + if (c->VsrD(1) & 1) { ppc_avr_t tmp; - tmp.u64[HI_IDX] = 0; - tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + tmp.VsrD(0) = 0; + tmp.VsrD(1) = c->VsrD(1) & 1; avr_qw_add(&tmp, *a, tmp); avr_qw_add(r, tmp, *b); } else { @@ -2433,8 +2296,8 @@ void helper_vaddcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) avr_qw_not(¬_a, *a); - r->u64[HI_IDX] = 0; - r->u64[LO_IDX] = (avr_qw_cmpu(not_a, *b) < 0); + r->VsrD(0) = 0; + r->VsrD(1) = (avr_qw_cmpu(not_a, *b) < 0); #endif } @@ -2449,7 +2312,7 @@ void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) r->u128 = carry_out; #else - int carry_in = c->u64[LO_IDX] & 1; + int carry_in = c->VsrD(1) & 1; int carry_out = 0; ppc_avr_t tmp; @@ -2459,8 +2322,8 @@ void helper_vaddecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) ppc_avr_t one = QW_ONE; carry_out = avr_qw_addc(&tmp, tmp, one); } - r->u64[HI_IDX] = 0; - r->u64[LO_IDX] = carry_out; + r->VsrD(0) = 0; + r->VsrD(1) = carry_out; #endif } @@ -2488,8 +2351,8 @@ void helper_vsubeuqm(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) avr_qw_not(&tmp, *b); avr_qw_add(&sum, *a, tmp); - tmp.u64[HI_IDX] = 0; - tmp.u64[LO_IDX] = c->u64[LO_IDX] & 1; + tmp.VsrD(0) = 0; + tmp.VsrD(1) = c->VsrD(1) & 1; avr_qw_add(r, sum, tmp); #endif } @@ -2505,10 +2368,10 @@ void helper_vsubcuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) ppc_avr_t tmp; avr_qw_not(&tmp, *b); avr_qw_add(&tmp, *a, tmp); - carry = ((tmp.s64[HI_IDX] == -1ull) && (tmp.s64[LO_IDX] == -1ull)); + carry = ((tmp.VsrSD(0) == -1ull) && (tmp.VsrSD(1) == -1ull)); } - r->u64[HI_IDX] = 0; - r->u64[LO_IDX] = carry; + r->VsrD(0) = 0; + r->VsrD(1) = carry; #endif } @@ -2519,17 +2382,17 @@ void helper_vsubecuq(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, ppc_avr_t *c) (~a->u128 < ~b->u128) || ((c->u128 & 1) && (a->u128 + ~b->u128 == (__uint128_t)-1)); #else - int carry_in = c->u64[LO_IDX] & 1; + int carry_in = c->VsrD(1) & 1; int carry_out = (avr_qw_cmpu(*a, *b) > 0); if (!carry_out && carry_in) { ppc_avr_t tmp; avr_qw_not(&tmp, *b); avr_qw_add(&tmp, *a, tmp); - carry_out = ((tmp.u64[HI_IDX] == -1ull) && (tmp.u64[LO_IDX] == -1ull)); + carry_out = ((tmp.VsrD(0) == -1ull) && (tmp.VsrD(1) == -1ull)); } - r->u64[HI_IDX] = 0; - r->u64[LO_IDX] = carry_out; + r->VsrD(0) = 0; + r->VsrD(1) = carry_out; #endif } @@ -2627,7 +2490,7 @@ static bool bcd_is_valid(ppc_avr_t *bcd) static int bcd_cmp_zero(ppc_avr_t *bcd) { - if (bcd->u64[HI_IDX] == 0 && (bcd->u64[LO_IDX] >> 4) == 0) { + if (bcd->VsrD(0) == 0 && (bcd->VsrD(1) >> 4) == 0) { return CRF_EQ; } else { return (bcd_get_sgn(bcd) == 1) ? CRF_GT : CRF_LT; @@ -2636,20 +2499,12 @@ static int bcd_cmp_zero(ppc_avr_t *bcd) static uint16_t get_national_digit(ppc_avr_t *reg, int n) { -#if defined(HOST_WORDS_BIGENDIAN) - return reg->u16[7 - n]; -#else - return reg->u16[n]; -#endif + return reg->VsrH(7 - n); } static void set_national_digit(ppc_avr_t *reg, uint8_t val, int n) { -#if defined(HOST_WORDS_BIGENDIAN) - reg->u16[7 - n] = val; -#else - reg->u16[n] = val; -#endif + reg->VsrH(7 - n) = val; } static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) @@ -2671,16 +2526,14 @@ static int bcd_cmp_mag(ppc_avr_t *a, ppc_avr_t *b) return 0; } -static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, +static void bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, int *overflow) { int carry = 0; int i; - int is_zero = 1; for (i = 1; i <= 31; i++) { uint8_t digit = bcd_get_digit(a, i, invalid) + bcd_get_digit(b, i, invalid) + carry; - is_zero &= (digit == 0); if (digit > 9) { carry = 1; digit -= 10; @@ -2689,26 +2542,20 @@ static int bcd_add_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, } bcd_put_digit(t, digit, i); - - if (unlikely(*invalid)) { - return -1; - } } *overflow = carry; - return is_zero; } -static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, +static void bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, int *overflow) { int carry = 0; int i; - int is_zero = 1; + for (i = 1; i <= 31; i++) { uint8_t digit = bcd_get_digit(a, i, invalid) - bcd_get_digit(b, i, invalid) + carry; - is_zero &= (digit == 0); if (digit & 0x80) { carry = -1; digit += 10; @@ -2717,14 +2564,9 @@ static int bcd_sub_mag(ppc_avr_t *t, ppc_avr_t *a, ppc_avr_t *b, int *invalid, } bcd_put_digit(t, digit, i); - - if (unlikely(*invalid)) { - return -1; - } } *overflow = carry; - return is_zero; } uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) @@ -2734,33 +2576,36 @@ uint32_t helper_bcdadd(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) int sgnb = bcd_get_sgn(b); int invalid = (sgna == 0) || (sgnb == 0); int overflow = 0; - int zero = 0; uint32_t cr = 0; ppc_avr_t result = { .u64 = { 0, 0 } }; if (!invalid) { if (sgna == sgnb) { result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); - zero = bcd_add_mag(&result, a, b, &invalid, &overflow); - cr = (sgna > 0) ? CRF_GT : CRF_LT; - } else if (bcd_cmp_mag(a, b) > 0) { - result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); - zero = bcd_sub_mag(&result, a, b, &invalid, &overflow); - cr = (sgna > 0) ? CRF_GT : CRF_LT; + bcd_add_mag(&result, a, b, &invalid, &overflow); + cr = bcd_cmp_zero(&result); } else { - result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); - zero = bcd_sub_mag(&result, b, a, &invalid, &overflow); - cr = (sgnb > 0) ? CRF_GT : CRF_LT; + int magnitude = bcd_cmp_mag(a, b); + if (magnitude > 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgna, ps); + bcd_sub_mag(&result, a, b, &invalid, &overflow); + cr = (sgna > 0) ? CRF_GT : CRF_LT; + } else if (magnitude < 0) { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(sgnb, ps); + bcd_sub_mag(&result, b, a, &invalid, &overflow); + cr = (sgnb > 0) ? CRF_GT : CRF_LT; + } else { + result.u8[BCD_DIG_BYTE(0)] = bcd_preferred_sgn(0, ps); + cr = CRF_EQ; + } } } if (unlikely(invalid)) { - result.u64[HI_IDX] = result.u64[LO_IDX] = -1; + result.VsrD(0) = result.VsrD(1) = -1; cr = CRF_SO; } else if (overflow) { cr |= CRF_SO; - } else if (zero) { - cr = CRF_EQ; } *r = result; @@ -2826,7 +2671,7 @@ uint32_t helper_bcdctn(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) int invalid = (sgnb == 0); ppc_avr_t ret = { .u64 = { 0, 0 } }; - int ox_flag = (b->u64[HI_IDX] != 0) || ((b->u64[LO_IDX] >> 32) != 0); + int ox_flag = (b->VsrD(0) != 0) || ((b->VsrD(1) >> 32) != 0); for (i = 1; i < 8; i++) { set_national_digit(&ret, 0x30 + bcd_get_digit(b, i, &invalid), i); @@ -2906,7 +2751,7 @@ uint32_t helper_bcdctz(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) int invalid = (sgnb == 0); ppc_avr_t ret = { .u64 = { 0, 0 } }; - int ox_flag = ((b->u64[HI_IDX] >> 4) != 0); + int ox_flag = ((b->VsrD(0) >> 4) != 0); for (i = 0; i < 16; i++) { digit = bcd_get_digit(b, i + 1, &invalid); @@ -2947,13 +2792,13 @@ uint32_t helper_bcdcfsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) uint64_t hi_value; ppc_avr_t ret = { .u64 = { 0, 0 } }; - if (b->s64[HI_IDX] < 0) { - lo_value = -b->s64[LO_IDX]; - hi_value = ~b->u64[HI_IDX] + !lo_value; + if (b->VsrSD(0) < 0) { + lo_value = -b->VsrSD(1); + hi_value = ~b->VsrD(0) + !lo_value; bcd_put_digit(&ret, 0xD, 0); } else { - lo_value = b->u64[LO_IDX]; - hi_value = b->u64[HI_IDX]; + lo_value = b->VsrD(1); + hi_value = b->VsrD(0); bcd_put_digit(&ret, bcd_preferred_sgn(0, ps), 0); } @@ -3001,11 +2846,11 @@ uint32_t helper_bcdctsq(ppc_avr_t *r, ppc_avr_t *b, uint32_t ps) } if (sgnb == -1) { - r->s64[LO_IDX] = -lo_value; - r->s64[HI_IDX] = ~hi_value + !r->s64[LO_IDX]; + r->VsrSD(1) = -lo_value; + r->VsrSD(0) = ~hi_value + !r->VsrSD(1); } else { - r->s64[LO_IDX] = lo_value; - r->s64[HI_IDX] = hi_value; + r->VsrSD(1) = lo_value; + r->VsrSD(0) = hi_value; } cr = bcd_cmp_zero(b); @@ -3065,7 +2910,7 @@ uint32_t helper_bcds(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) bool ox_flag = false; int sgnb = bcd_get_sgn(b); ppc_avr_t ret = *b; - ret.u64[LO_IDX] &= ~0xf; + ret.VsrD(1) &= ~0xf; if (bcd_is_valid(b) == false) { return CRF_SO; @@ -3078,9 +2923,9 @@ uint32_t helper_bcds(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) } if (i > 0) { - ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag); + ulshift(&ret.VsrD(1), &ret.VsrD(0), i * 4, &ox_flag); } else { - urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4); + urshift(&ret.VsrD(1), &ret.VsrD(0), -i * 4); } bcd_put_digit(&ret, bcd_preferred_sgn(sgnb, ps), 0); @@ -3117,13 +2962,13 @@ uint32_t helper_bcdus(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) #endif if (i >= 32) { ox_flag = true; - ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0; + ret.VsrD(1) = ret.VsrD(0) = 0; } else if (i <= -32) { - ret.u64[LO_IDX] = ret.u64[HI_IDX] = 0; + ret.VsrD(1) = ret.VsrD(0) = 0; } else if (i > 0) { - ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag); + ulshift(&ret.VsrD(1), &ret.VsrD(0), i * 4, &ox_flag); } else { - urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4); + urshift(&ret.VsrD(1), &ret.VsrD(0), -i * 4); } *r = ret; @@ -3143,7 +2988,7 @@ uint32_t helper_bcdsr(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) bool ox_flag = false; int sgnb = bcd_get_sgn(b); ppc_avr_t ret = *b; - ret.u64[LO_IDX] &= ~0xf; + ret.VsrD(1) &= ~0xf; #if defined(HOST_WORDS_BIGENDIAN) int i = a->s8[7]; @@ -3164,9 +3009,9 @@ uint32_t helper_bcdsr(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) } if (i > 0) { - ulshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], i * 4, &ox_flag); + ulshift(&ret.VsrD(1), &ret.VsrD(0), i * 4, &ox_flag); } else { - urshift(&ret.u64[LO_IDX], &ret.u64[HI_IDX], -i * 4); + urshift(&ret.VsrD(1), &ret.VsrD(0), -i * 4); if (bcd_get_digit(&ret, 0, &invalid) >= 5) { bcd_add_mag(&ret, &ret, &bcd_one, &invalid, &unused); @@ -3200,19 +3045,19 @@ uint32_t helper_bcdtrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) if (i > 16 && i < 32) { mask = (uint64_t)-1 >> (128 - i * 4); - if (ret.u64[HI_IDX] & ~mask) { + if (ret.VsrD(0) & ~mask) { ox_flag = CRF_SO; } - ret.u64[HI_IDX] &= mask; + ret.VsrD(0) &= mask; } else if (i >= 0 && i <= 16) { mask = (uint64_t)-1 >> (64 - i * 4); - if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) { + if (ret.VsrD(0) || (ret.VsrD(1) & ~mask)) { ox_flag = CRF_SO; } - ret.u64[LO_IDX] &= mask; - ret.u64[HI_IDX] = 0; + ret.VsrD(1) &= mask; + ret.VsrD(0) = 0; } bcd_put_digit(&ret, bcd_preferred_sgn(bcd_get_sgn(b), ps), 0); *r = ret; @@ -3243,28 +3088,28 @@ uint32_t helper_bcdutrunc(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b, uint32_t ps) #endif if (i > 16 && i < 33) { mask = (uint64_t)-1 >> (128 - i * 4); - if (ret.u64[HI_IDX] & ~mask) { + if (ret.VsrD(0) & ~mask) { ox_flag = CRF_SO; } - ret.u64[HI_IDX] &= mask; + ret.VsrD(0) &= mask; } else if (i > 0 && i <= 16) { mask = (uint64_t)-1 >> (64 - i * 4); - if (ret.u64[HI_IDX] || (ret.u64[LO_IDX] & ~mask)) { + if (ret.VsrD(0) || (ret.VsrD(1) & ~mask)) { ox_flag = CRF_SO; } - ret.u64[LO_IDX] &= mask; - ret.u64[HI_IDX] = 0; + ret.VsrD(1) &= mask; + ret.VsrD(0) = 0; } else if (i == 0) { - if (ret.u64[HI_IDX] || ret.u64[LO_IDX]) { + if (ret.VsrD(0) || ret.VsrD(1)) { ox_flag = CRF_SO; } - ret.u64[HI_IDX] = ret.u64[LO_IDX] = 0; + ret.VsrD(0) = ret.VsrD(1) = 0; } *r = ret; - if (r->u64[HI_IDX] == 0 && r->u64[LO_IDX] == 0) { + if (r->VsrD(0) == 0 && r->VsrD(1) == 0) { return ox_flag | CRF_EQ; } @@ -3285,11 +3130,11 @@ void helper_vcipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) int i; VECTOR_FOR_INORDER_I(i, u32) { - 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])]); + result.VsrW(i) = b->VsrW(i) ^ + (AES_Te0[a->VsrB(AES_shifts[4 * i + 0])] ^ + AES_Te1[a->VsrB(AES_shifts[4 * i + 1])] ^ + AES_Te2[a->VsrB(AES_shifts[4 * i + 2])] ^ + AES_Te3[a->VsrB(AES_shifts[4 * i + 3])]); } *r = result; } @@ -3300,7 +3145,7 @@ void helper_vcipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) int i; VECTOR_FOR_INORDER_I(i, u8) { - result.AVRB(i) = b->AVRB(i) ^ (AES_sbox[a->AVRB(AES_shifts[i])]); + result.VsrB(i) = b->VsrB(i) ^ (AES_sbox[a->VsrB(AES_shifts[i])]); } *r = result; } @@ -3313,15 +3158,15 @@ void helper_vncipher(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) ppc_avr_t tmp; VECTOR_FOR_INORDER_I(i, u8) { - tmp.AVRB(i) = b->AVRB(i) ^ AES_isbox[a->AVRB(AES_ishifts[i])]; + tmp.VsrB(i) = b->VsrB(i) ^ AES_isbox[a->VsrB(AES_ishifts[i])]; } VECTOR_FOR_INORDER_I(i, u32) { - r->AVRW(i) = - AES_imc[tmp.AVRB(4*i + 0)][0] ^ - AES_imc[tmp.AVRB(4*i + 1)][1] ^ - AES_imc[tmp.AVRB(4*i + 2)][2] ^ - AES_imc[tmp.AVRB(4*i + 3)][3]; + r->VsrW(i) = + AES_imc[tmp.VsrB(4 * i + 0)][0] ^ + AES_imc[tmp.VsrB(4 * i + 1)][1] ^ + AES_imc[tmp.VsrB(4 * i + 2)][2] ^ + AES_imc[tmp.VsrB(4 * i + 3)][3]; } } @@ -3331,113 +3176,88 @@ void helper_vncipherlast(ppc_avr_t *r, ppc_avr_t *a, ppc_avr_t *b) int i; VECTOR_FOR_INORDER_I(i, u8) { - result.AVRB(i) = b->AVRB(i) ^ (AES_isbox[a->AVRB(AES_ishifts[i])]); + result.VsrB(i) = b->VsrB(i) ^ (AES_isbox[a->VsrB(AES_ishifts[i])]); } *r = result; } -#define ROTRu32(v, n) (((v) >> (n)) | ((v) << (32-n))) -#if defined(HOST_WORDS_BIGENDIAN) -#define EL_IDX(i) (i) -#else -#define EL_IDX(i) (3 - (i)) -#endif - void helper_vshasigmaw(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) { int st = (st_six & 0x10) != 0; int six = st_six & 0xF; int i; - VECTOR_FOR_INORDER_I(i, u32) { + for (i = 0; i < ARRAY_SIZE(r->u32); i++) { if (st == 0) { if ((six & (0x8 >> i)) == 0) { - r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 7) ^ - ROTRu32(a->u32[EL_IDX(i)], 18) ^ - (a->u32[EL_IDX(i)] >> 3); + r->VsrW(i) = ror32(a->VsrW(i), 7) ^ + ror32(a->VsrW(i), 18) ^ + (a->VsrW(i) >> 3); } else { /* six.bit[i] == 1 */ - r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 17) ^ - ROTRu32(a->u32[EL_IDX(i)], 19) ^ - (a->u32[EL_IDX(i)] >> 10); + r->VsrW(i) = ror32(a->VsrW(i), 17) ^ + ror32(a->VsrW(i), 19) ^ + (a->VsrW(i) >> 10); } } else { /* st == 1 */ if ((six & (0x8 >> i)) == 0) { - r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 2) ^ - ROTRu32(a->u32[EL_IDX(i)], 13) ^ - ROTRu32(a->u32[EL_IDX(i)], 22); + r->VsrW(i) = ror32(a->VsrW(i), 2) ^ + ror32(a->VsrW(i), 13) ^ + ror32(a->VsrW(i), 22); } else { /* six.bit[i] == 1 */ - r->u32[EL_IDX(i)] = ROTRu32(a->u32[EL_IDX(i)], 6) ^ - ROTRu32(a->u32[EL_IDX(i)], 11) ^ - ROTRu32(a->u32[EL_IDX(i)], 25); + r->VsrW(i) = ror32(a->VsrW(i), 6) ^ + ror32(a->VsrW(i), 11) ^ + ror32(a->VsrW(i), 25); } } } } -#undef ROTRu32 -#undef EL_IDX - -#define ROTRu64(v, n) (((v) >> (n)) | ((v) << (64-n))) -#if defined(HOST_WORDS_BIGENDIAN) -#define EL_IDX(i) (i) -#else -#define EL_IDX(i) (1 - (i)) -#endif - void helper_vshasigmad(ppc_avr_t *r, ppc_avr_t *a, uint32_t st_six) { int st = (st_six & 0x10) != 0; int six = st_six & 0xF; int i; - VECTOR_FOR_INORDER_I(i, u64) { + for (i = 0; i < ARRAY_SIZE(r->u64); i++) { if (st == 0) { if ((six & (0x8 >> (2*i))) == 0) { - r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 1) ^ - ROTRu64(a->u64[EL_IDX(i)], 8) ^ - (a->u64[EL_IDX(i)] >> 7); + r->VsrD(i) = ror64(a->VsrD(i), 1) ^ + ror64(a->VsrD(i), 8) ^ + (a->VsrD(i) >> 7); } else { /* six.bit[2*i] == 1 */ - r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 19) ^ - ROTRu64(a->u64[EL_IDX(i)], 61) ^ - (a->u64[EL_IDX(i)] >> 6); + r->VsrD(i) = ror64(a->VsrD(i), 19) ^ + ror64(a->VsrD(i), 61) ^ + (a->VsrD(i) >> 6); } } else { /* st == 1 */ if ((six & (0x8 >> (2*i))) == 0) { - r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 28) ^ - ROTRu64(a->u64[EL_IDX(i)], 34) ^ - ROTRu64(a->u64[EL_IDX(i)], 39); + r->VsrD(i) = ror64(a->VsrD(i), 28) ^ + ror64(a->VsrD(i), 34) ^ + ror64(a->VsrD(i), 39); } else { /* six.bit[2*i] == 1 */ - r->u64[EL_IDX(i)] = ROTRu64(a->u64[EL_IDX(i)], 14) ^ - ROTRu64(a->u64[EL_IDX(i)], 18) ^ - ROTRu64(a->u64[EL_IDX(i)], 41); + r->VsrD(i) = ror64(a->VsrD(i), 14) ^ + ror64(a->VsrD(i), 18) ^ + ror64(a->VsrD(i), 41); } } } } -#undef ROTRu64 -#undef EL_IDX - 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) - result.u8[i] = a->u8[indexA] ^ b->u8[indexB]; -#else - result.u8[i] = a->u8[15-indexA] ^ b->u8[15-indexB]; -#endif + for (i = 0; i < ARRAY_SIZE(r->u8); i++) { + int indexA = c->VsrB(i) >> 4; + int indexB = c->VsrB(i) & 0xF; + + result.VsrB(i) = a->VsrB(indexA) ^ b->VsrB(indexB); } *r = result; } #undef VECTOR_FOR_INORDER_I -#undef HI_IDX -#undef LO_IDX /*****************************************************************************/ /* SPE extension helpers */ diff --git a/target/ppc/internal.h b/target/ppc/internal.h index a9bcadff42..fb6f64ed1e 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -52,20 +52,20 @@ FUNC_MASK(mask_u64, uint64_t, 64, UINT64_MAX); #define EXTRACT_HELPER(name, shift, nb) \ static inline uint32_t name(uint32_t opcode) \ { \ - return (opcode >> (shift)) & ((1 << (nb)) - 1); \ + return extract32(opcode, shift, nb); \ } #define EXTRACT_SHELPER(name, shift, nb) \ static inline int32_t name(uint32_t opcode) \ { \ - return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \ + return sextract32(opcode, shift, nb); \ } #define EXTRACT_HELPER_SPLIT(name, shift1, nb1, shift2, nb2) \ static inline uint32_t name(uint32_t opcode) \ { \ - return (((opcode >> (shift1)) & ((1 << (nb1)) - 1)) << nb2) | \ - ((opcode >> (shift2)) & ((1 << (nb2)) - 1)); \ + return extract32(opcode, shift1, nb1) << nb2 | \ + extract32(opcode, shift2, nb2); \ } #define EXTRACT_HELPER_SPLIT_3(name, \ @@ -124,7 +124,7 @@ EXTRACT_SHELPER(SIMM, 0, 16); /* 16 bits unsigned immediate value */ EXTRACT_HELPER(UIMM, 0, 16); /* 5 bits signed immediate value */ -EXTRACT_HELPER(SIMM5, 16, 5); +EXTRACT_SHELPER(SIMM5, 16, 5); /* 5 bits signed immediate value */ EXTRACT_HELPER(UIMM5, 16, 5); /* 4 bits unsigned immediate value */ @@ -204,49 +204,16 @@ EXTRACT_HELPER(IMM8, 11, 8); EXTRACT_HELPER(DCMX, 16, 7); EXTRACT_HELPER_SPLIT_3(DCMX_XV, 5, 16, 0, 1, 2, 5, 1, 6, 6); -typedef union _ppc_vsr_t { - uint8_t u8[16]; - uint16_t u16[8]; - uint32_t u32[4]; - uint64_t u64[2]; - float32 f32[4]; - float64 f64[2]; - float128 f128; - Int128 s128; -} ppc_vsr_t; - -#if defined(HOST_WORDS_BIGENDIAN) -#define VsrB(i) u8[i] -#define VsrH(i) u16[i] -#define VsrW(i) u32[i] -#define VsrD(i) u64[i] -#else -#define VsrB(i) u8[15 - (i)] -#define VsrH(i) u16[7 - (i)] -#define VsrW(i) u32[3 - (i)] -#define VsrD(i) u64[1 - (i)] -#endif - static inline void getVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) { - if (n < 32) { - vsr->VsrD(0) = env->fpr[n]; - vsr->VsrD(1) = env->vsr[n]; - } else { - vsr->u64[0] = env->avr[n - 32].u64[0]; - vsr->u64[1] = env->avr[n - 32].u64[1]; - } + vsr->VsrD(0) = env->vsr[n].VsrD(0); + vsr->VsrD(1) = env->vsr[n].VsrD(1); } static inline void putVSR(int n, ppc_vsr_t *vsr, CPUPPCState *env) { - if (n < 32) { - env->fpr[n] = vsr->VsrD(0); - env->vsr[n] = vsr->VsrD(1); - } else { - env->avr[n - 32].u64[0] = vsr->u64[0]; - env->avr[n - 32].u64[1] = vsr->u64[1]; - } + env->vsr[n].VsrD(0) = vsr->VsrD(0); + env->vsr[n].VsrD(1) = vsr->VsrD(1); } void helper_compute_fprf_float16(CPUPPCState *env, float16 arg); diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 9211ee2ee1..2427c8ee13 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -36,7 +36,6 @@ #include "hw/sysbus.h" #include "hw/ppc/spapr.h" -#include "hw/ppc/spapr_vio.h" #include "hw/ppc/spapr_cpu_core.h" #include "hw/ppc/ppc.h" #include "sysemu/watchdog.h" @@ -91,6 +90,9 @@ static int cap_ppc_pvr_compat; static int cap_ppc_safe_cache; static int cap_ppc_safe_bounds_check; static int cap_ppc_safe_indirect_branch; +static int cap_ppc_count_cache_flush_assist; +static int cap_ppc_nested_kvm_hv; +static int cap_large_decr; static uint32_t debug_inst_opcode; @@ -124,6 +126,7 @@ static bool kvmppc_is_pr(KVMState *ks) static int kvm_ppc_register_host_cpu_type(MachineState *ms); static void kvmppc_get_cpu_characteristics(KVMState *s); +static int kvmppc_get_dec_bits(void); int kvm_arch_init(MachineState *ms, KVMState *s) { @@ -150,6 +153,8 @@ int kvm_arch_init(MachineState *ms, KVMState *s) cap_mmu_hash_v3 = kvm_vm_check_extension(s, KVM_CAP_PPC_MMU_HASH_V3); cap_resize_hpt = kvm_vm_check_extension(s, KVM_CAP_SPAPR_RESIZE_HPT); kvmppc_get_cpu_characteristics(s); + cap_ppc_nested_kvm_hv = kvm_vm_check_extension(s, KVM_CAP_PPC_NESTED_HV); + cap_large_decr = kvmppc_get_dec_bits(); /* * Note: setting it to false because there is not such capability * in KVM at this moment. @@ -627,13 +632,15 @@ static int kvm_put_fp(CPUState *cs) for (i = 0; i < 32; i++) { uint64_t vsr[2]; + uint64_t *fpr = cpu_fpr_ptr(&cpu->env, i); + uint64_t *vsrl = cpu_vsrl_ptr(&cpu->env, i); #ifdef HOST_WORDS_BIGENDIAN - vsr[0] = float64_val(env->fpr[i]); - vsr[1] = env->vsr[i]; + vsr[0] = float64_val(*fpr); + vsr[1] = *vsrl; #else - vsr[0] = env->vsr[i]; - vsr[1] = float64_val(env->fpr[i]); + vsr[0] = *vsrl; + vsr[1] = float64_val(*fpr); #endif reg.addr = (uintptr_t) &vsr; reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); @@ -658,7 +665,7 @@ static int kvm_put_fp(CPUState *cs) for (i = 0; i < 32; i++) { reg.id = KVM_REG_PPC_VR(i); - reg.addr = (uintptr_t)&env->avr[i]; + reg.addr = (uintptr_t)cpu_avr_ptr(env, i); ret = kvm_vcpu_ioctl(cs, KVM_SET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to set VR%d to KVM: %s\n", i, strerror(errno)); @@ -694,6 +701,8 @@ static int kvm_get_fp(CPUState *cs) for (i = 0; i < 32; i++) { uint64_t vsr[2]; + uint64_t *fpr = cpu_fpr_ptr(&cpu->env, i); + uint64_t *vsrl = cpu_vsrl_ptr(&cpu->env, i); reg.addr = (uintptr_t) &vsr; reg.id = vsx ? KVM_REG_PPC_VSR(i) : KVM_REG_PPC_FPR(i); @@ -705,14 +714,14 @@ static int kvm_get_fp(CPUState *cs) return ret; } else { #ifdef HOST_WORDS_BIGENDIAN - env->fpr[i] = vsr[0]; + *fpr = vsr[0]; if (vsx) { - env->vsr[i] = vsr[1]; + *vsrl = vsr[1]; } #else - env->fpr[i] = vsr[1]; + *fpr = vsr[1]; if (vsx) { - env->vsr[i] = vsr[0]; + *vsrl = vsr[0]; } #endif } @@ -730,7 +739,7 @@ static int kvm_get_fp(CPUState *cs) for (i = 0; i < 32; i++) { reg.id = KVM_REG_PPC_VR(i); - reg.addr = (uintptr_t)&env->avr[i]; + reg.addr = (uintptr_t)cpu_avr_ptr(env, i); ret = kvm_vcpu_ioctl(cs, KVM_GET_ONE_REG, ®); if (ret < 0) { DPRINTF("Unable to get VR%d from KVM: %s\n", @@ -747,7 +756,7 @@ static int kvm_get_fp(CPUState *cs) static int kvm_get_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); struct kvm_one_reg reg; int ret; @@ -787,7 +796,7 @@ static int kvm_get_vpa(CPUState *cs) static int kvm_put_vpa(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); - sPAPRCPUState *spapr_cpu = spapr_cpu_state(cpu); + SpaprCpuState *spapr_cpu = spapr_cpu_state(cpu); struct kvm_one_reg reg; int ret; @@ -1315,9 +1324,7 @@ int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) return 0; } -#if defined(TARGET_PPCEMB) -#define PPC_INPUT_INT PPC40x_INPUT_INT -#elif defined(TARGET_PPC64) +#if defined(TARGET_PPC64) #define PPC_INPUT_INT PPC970_INPUT_INT #else #define PPC_INPUT_INT PPC6xx_INPUT_INT @@ -1590,70 +1597,93 @@ void kvm_arch_update_guest_debug(CPUState *cs, struct kvm_guest_debug *dbg) } } +static int kvm_handle_hw_breakpoint(CPUState *cs, + struct kvm_debug_exit_arch *arch_info) +{ + int handle = 0; + int n; + int flag = 0; + + if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { + if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { + n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); + if (n >= 0) { + handle = 1; + } + } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | + KVMPPC_DEBUG_WATCH_WRITE)) { + n = find_hw_watchpoint(arch_info->address, &flag); + if (n >= 0) { + handle = 1; + cs->watchpoint_hit = &hw_watchpoint; + hw_watchpoint.vaddr = hw_debug_points[n].addr; + hw_watchpoint.flags = flag; + } + } + } + return handle; +} + +static int kvm_handle_singlestep(void) +{ + return 1; +} + +static int kvm_handle_sw_breakpoint(void) +{ + return 1; +} + static int kvm_handle_debug(PowerPCCPU *cpu, struct kvm_run *run) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; struct kvm_debug_exit_arch *arch_info = &run->debug.arch; - int handle = 0; - int n; - int flag = 0; if (cs->singlestep_enabled) { - handle = 1; - } else if (arch_info->status) { - if (nb_hw_breakpoint + nb_hw_watchpoint > 0) { - if (arch_info->status & KVMPPC_DEBUG_BREAKPOINT) { - n = find_hw_breakpoint(arch_info->address, GDB_BREAKPOINT_HW); - if (n >= 0) { - handle = 1; - } - } else if (arch_info->status & (KVMPPC_DEBUG_WATCH_READ | - KVMPPC_DEBUG_WATCH_WRITE)) { - n = find_hw_watchpoint(arch_info->address, &flag); - if (n >= 0) { - handle = 1; - cs->watchpoint_hit = &hw_watchpoint; - hw_watchpoint.vaddr = hw_debug_points[n].addr; - hw_watchpoint.flags = flag; - } - } - } - } else if (kvm_find_sw_breakpoint(cs, arch_info->address)) { - handle = 1; - } else { - /* QEMU is not able to handle debug exception, so inject - * program exception to guest; - * Yes program exception NOT debug exception !! - * When QEMU is using debug resources then debug exception must - * be always set. To achieve this we set MSR_DE and also set - * MSRP_DEP so guest cannot change MSR_DE. - * When emulating debug resource for guest we want guest - * to control MSR_DE (enable/disable debug interrupt on need). - * Supporting both configurations are NOT possible. - * So the result is that we cannot share debug resources - * between QEMU and Guest on BOOKE architecture. - * In the current design QEMU gets the priority over guest, - * this means that if QEMU is using debug resources then guest - * cannot use them; - * For software breakpoint QEMU uses a privileged instruction; - * So there cannot be any reason that we are here for guest - * set debug exception, only possibility is guest executed a - * privileged / illegal instruction and that's why we are - * injecting a program interrupt. - */ - - cpu_synchronize_state(cs); - /* env->nip is PC, so increment this by 4 to use - * ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4. - */ - env->nip += 4; - cs->exception_index = POWERPC_EXCP_PROGRAM; - env->error_code = POWERPC_EXCP_INVAL; - ppc_cpu_do_interrupt(cs); + return kvm_handle_singlestep(); } - return handle; + if (arch_info->status) { + return kvm_handle_hw_breakpoint(cs, arch_info); + } + + if (kvm_find_sw_breakpoint(cs, arch_info->address)) { + return kvm_handle_sw_breakpoint(); + } + + /* + * QEMU is not able to handle debug exception, so inject + * program exception to guest; + * Yes program exception NOT debug exception !! + * When QEMU is using debug resources then debug exception must + * be always set. To achieve this we set MSR_DE and also set + * MSRP_DEP so guest cannot change MSR_DE. + * When emulating debug resource for guest we want guest + * to control MSR_DE (enable/disable debug interrupt on need). + * Supporting both configurations are NOT possible. + * So the result is that we cannot share debug resources + * between QEMU and Guest on BOOKE architecture. + * In the current design QEMU gets the priority over guest, + * this means that if QEMU is using debug resources then guest + * cannot use them; + * For software breakpoint QEMU uses a privileged instruction; + * So there cannot be any reason that we are here for guest + * set debug exception, only possibility is guest executed a + * privileged / illegal instruction and that's why we are + * injecting a program interrupt. + */ + cpu_synchronize_state(cs); + /* + * env->nip is PC, so increment this by 4 to use + * ppc_cpu_do_interrupt(), which set srr0 = env->nip - 4. + */ + env->nip += 4; + cs->exception_index = POWERPC_EXCP_PROGRAM; + env->error_code = POWERPC_EXCP_INVAL; + ppc_cpu_do_interrupt(cs); + + return 0; } int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) @@ -1924,6 +1954,16 @@ uint64_t kvmppc_get_clockfreq(void) return kvmppc_read_int_cpu_dt("clock-frequency"); } +static int kvmppc_get_dec_bits(void) +{ + int nr_bits = kvmppc_read_int_cpu_dt("ibm,dec-bits"); + + if (nr_bits > 0) { + return nr_bits; + } + return 0; +} + static int kvmppc_get_pvinfo(CPUPPCState *env, struct kvm_ppc_pvinfo *pvinfo) { PowerPCCPU *cpu = ppc_env_get_cpu(env); @@ -2004,6 +2044,11 @@ void kvmppc_enable_clear_ref_mod_hcalls(void) kvmppc_enable_hcall(kvm_state, H_CLEAR_MOD); } +void kvmppc_enable_h_page_init(void) +{ + kvmppc_enable_hcall(kvm_state, H_PAGE_INIT); +} + void kvmppc_set_papr(PowerPCCPU *cpu) { CPUState *cs = CPU(cpu); @@ -2376,7 +2421,13 @@ static int parse_cap_ppc_safe_bounds_check(struct kvm_ppc_cpu_char c) static int parse_cap_ppc_safe_indirect_branch(struct kvm_ppc_cpu_char c) { - if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { + if ((~c.behaviour & c.behaviour_mask & H_CPU_BEHAV_FLUSH_COUNT_CACHE) && + (~c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) && + (~c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED)) { + return SPAPR_CAP_FIXED_NA; + } else if (c.behaviour & c.behaviour_mask & H_CPU_BEHAV_FLUSH_COUNT_CACHE) { + return SPAPR_CAP_WORKAROUND; + } else if (c.character & c.character_mask & H_CPU_CHAR_CACHE_COUNT_DIS) { return SPAPR_CAP_FIXED_CCD; } else if (c.character & c.character_mask & H_CPU_CHAR_BCCTRL_SERIALISED) { return SPAPR_CAP_FIXED_IBS; @@ -2385,6 +2436,14 @@ static int parse_cap_ppc_safe_indirect_branch(struct kvm_ppc_cpu_char c) return 0; } +static int parse_cap_ppc_count_cache_flush_assist(struct kvm_ppc_cpu_char c) +{ + if (c.character & c.character_mask & H_CPU_CHAR_BCCTR_FLUSH_ASSIST) { + return 1; + } + return 0; +} + static void kvmppc_get_cpu_characteristics(KVMState *s) { struct kvm_ppc_cpu_char c; @@ -2407,6 +2466,8 @@ static void kvmppc_get_cpu_characteristics(KVMState *s) cap_ppc_safe_cache = parse_cap_ppc_safe_cache(c); cap_ppc_safe_bounds_check = parse_cap_ppc_safe_bounds_check(c); cap_ppc_safe_indirect_branch = parse_cap_ppc_safe_indirect_branch(c); + cap_ppc_count_cache_flush_assist = + parse_cap_ppc_count_cache_flush_assist(c); } int kvmppc_get_cap_safe_cache(void) @@ -2424,11 +2485,55 @@ int kvmppc_get_cap_safe_indirect_branch(void) return cap_ppc_safe_indirect_branch; } +int kvmppc_get_cap_count_cache_flush_assist(void) +{ + return cap_ppc_count_cache_flush_assist; +} + +bool kvmppc_has_cap_nested_kvm_hv(void) +{ + return !!cap_ppc_nested_kvm_hv; +} + +int kvmppc_set_cap_nested_kvm_hv(int enable) +{ + return kvm_vm_enable_cap(kvm_state, KVM_CAP_PPC_NESTED_HV, 0, enable); +} + bool kvmppc_has_cap_spapr_vfio(void) { return cap_spapr_vfio; } +int kvmppc_get_cap_large_decr(void) +{ + return cap_large_decr; +} + +int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable) +{ + CPUState *cs = CPU(cpu); + uint64_t lpcr; + + kvm_get_one_reg(cs, KVM_REG_PPC_LPCR_64, &lpcr); + /* Do we need to modify the LPCR? */ + if (!!(lpcr & LPCR_LD) != !!enable) { + if (enable) { + lpcr |= LPCR_LD; + } else { + lpcr &= ~LPCR_LD; + } + kvm_set_one_reg(cs, KVM_REG_PPC_LPCR_64, &lpcr); + kvm_get_one_reg(cs, KVM_REG_PPC_LPCR_64, &lpcr); + + if (!!(lpcr & LPCR_LD) != !!enable) { + return -1; + } + } + + return 0; +} + PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) { uint32_t host_pvr = mfpvr(); @@ -2785,3 +2890,12 @@ bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu) return !kvmppc_is_pr(cs->kvm_state); } + +void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) +{ + CPUState *cs = CPU(cpu); + + if (kvm_enabled()) { + kvm_set_one_reg(cs, KVM_REG_PPC_ONLINE, &online); + } +} diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index 657582bb32..2c2ea30e87 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -23,6 +23,7 @@ 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_enable_clear_ref_mod_hcalls(void); +void kvmppc_enable_h_page_init(void); void kvmppc_set_papr(PowerPCCPU *cpu); int kvmppc_set_compat(PowerPCCPU *cpu, uint32_t compat_pvr); void kvmppc_set_mpic_proxy(PowerPCCPU *cpu, int mpic_proxy); @@ -62,6 +63,11 @@ bool kvmppc_has_cap_mmu_hash_v3(void); int kvmppc_get_cap_safe_cache(void); int kvmppc_get_cap_safe_bounds_check(void); int kvmppc_get_cap_safe_indirect_branch(void); +int kvmppc_get_cap_count_cache_flush_assist(void); +bool kvmppc_has_cap_nested_kvm_hv(void); +int kvmppc_set_cap_nested_kvm_hv(int enable); +int kvmppc_get_cap_large_decr(void); +int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable); int kvmppc_enable_hwrng(void); int kvmppc_put_books_sregs(PowerPCCPU *cpu); PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void); @@ -72,6 +78,7 @@ bool kvmppc_pvr_workaround_required(PowerPCCPU *cpu); bool kvmppc_hpt_needs_host_contiguous_pages(void); void kvm_check_mmu(PowerPCCPU *cpu, Error **errp); +void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online); #else @@ -132,6 +139,10 @@ static inline void kvmppc_enable_clear_ref_mod_hcalls(void) { } +static inline void kvmppc_enable_h_page_init(void) +{ +} + static inline void kvmppc_set_papr(PowerPCCPU *cpu) { } @@ -187,6 +198,12 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, return 0; } +static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, + unsigned int online) +{ + return; +} + #ifndef CONFIG_USER_ONLY static inline bool kvmppc_spapr_use_multitce(void) { @@ -313,6 +330,31 @@ static inline int kvmppc_get_cap_safe_indirect_branch(void) return 0; } +static inline int kvmppc_get_cap_count_cache_flush_assist(void) +{ + return 0; +} + +static inline bool kvmppc_has_cap_nested_kvm_hv(void) +{ + return false; +} + +static inline int kvmppc_set_cap_nested_kvm_hv(int enable) +{ + return -1; +} + +static inline int kvmppc_get_cap_large_decr(void) +{ + return 0; +} + +static inline int kvmppc_enable_cap_large_decr(PowerPCCPU *cpu, int enable) +{ + return -1; +} + static inline int kvmppc_enable_hwrng(void) { return -1; diff --git a/target/ppc/machine.c b/target/ppc/machine.c index b2745ec4e5..a92d0ad3a3 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -10,6 +10,7 @@ #include "migration/cpu.h" #include "qapi/error.h" #include "kvm_ppc.h" +#include "exec/helper-proto.h" static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) { @@ -17,7 +18,7 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) CPUPPCState *env = &cpu->env; unsigned int i, j; target_ulong sdr1; - uint32_t fpscr; + uint32_t fpscr, vscr; #if defined(TARGET_PPC64) int32_t slb_nr; #endif @@ -45,7 +46,7 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) uint64_t l; } u; u.l = qemu_get_be64(f); - env->fpr[i] = u.d; + *cpu_fpr_ptr(env, i) = u.d; } qemu_get_be32s(f, &fpscr); env->fpscr = fpscr; @@ -84,7 +85,8 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) if (!cpu->vhyp) { ppc_store_sdr1(env, sdr1); } - qemu_get_be32s(f, &env->vscr); + qemu_get_be32s(f, &vscr); + helper_mtvscr(env, vscr); qemu_get_be64s(f, &env->spe_acc); qemu_get_be32s(f, &env->spe_fscr); qemu_get_betls(f, &env->msr_mask); @@ -110,7 +112,8 @@ static int cpu_load_old(QEMUFile *f, void *opaque, int version_id) return 0; } -static int get_avr(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_avr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { ppc_avr_t *v = pv; @@ -120,8 +123,8 @@ static int get_avr(QEMUFile *f, void *pv, size_t size, VMStateField *field) return 0; } -static int put_avr(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_avr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { ppc_avr_t *v = pv; @@ -137,11 +140,73 @@ static const VMStateInfo vmstate_info_avr = { }; #define VMSTATE_AVR_ARRAY_V(_f, _s, _n, _v) \ - VMSTATE_ARRAY(_f, _s, _n, _v, vmstate_info_avr, ppc_avr_t) + VMSTATE_SUB_ARRAY(_f, _s, 32, _n, _v, vmstate_info_avr, ppc_avr_t) #define VMSTATE_AVR_ARRAY(_f, _s, _n) \ VMSTATE_AVR_ARRAY_V(_f, _s, _n, 0) +static int get_fpr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + ppc_vsr_t *v = pv; + + v->VsrD(0) = qemu_get_be64(f); + + return 0; +} + +static int put_fpr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + ppc_vsr_t *v = pv; + + qemu_put_be64(f, v->VsrD(0)); + return 0; +} + +static const VMStateInfo vmstate_info_fpr = { + .name = "fpr", + .get = get_fpr, + .put = put_fpr, +}; + +#define VMSTATE_FPR_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_SUB_ARRAY(_f, _s, 0, _n, _v, vmstate_info_fpr, ppc_vsr_t) + +#define VMSTATE_FPR_ARRAY(_f, _s, _n) \ + VMSTATE_FPR_ARRAY_V(_f, _s, _n, 0) + +static int get_vsr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + ppc_vsr_t *v = pv; + + v->VsrD(1) = qemu_get_be64(f); + + return 0; +} + +static int put_vsr(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + ppc_vsr_t *v = pv; + + qemu_put_be64(f, v->VsrD(1)); + return 0; +} + +static const VMStateInfo vmstate_info_vsr = { + .name = "vsr", + .get = get_vsr, + .put = put_vsr, +}; + +#define VMSTATE_VSR_ARRAY_V(_f, _s, _n, _v) \ + VMSTATE_SUB_ARRAY(_f, _s, 0, _n, _v, vmstate_info_vsr, ppc_vsr_t) + +#define VMSTATE_VSR_ARRAY(_f, _s, _n) \ + VMSTATE_VSR_ARRAY_V(_f, _s, _n, 0) + static bool cpu_pre_2_8_migration(void *opaque, int version_id) { PowerPCCPU *cpu = opaque; @@ -353,7 +418,7 @@ static const VMStateDescription vmstate_fpu = { .minimum_version_id = 1, .needed = fpu_needed, .fields = (VMStateField[]) { - VMSTATE_FLOAT64_ARRAY(env.fpr, PowerPCCPU, 32), + VMSTATE_FPR_ARRAY(env.vsr, PowerPCCPU, 32), VMSTATE_UINTTL(env.fpscr, PowerPCCPU), VMSTATE_END_OF_LIST() }, @@ -366,14 +431,50 @@ static bool altivec_needed(void *opaque) return (cpu->env.insns_flags & PPC_ALTIVEC); } +static int get_vscr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) +{ + PowerPCCPU *cpu = opaque; + helper_mtvscr(&cpu->env, qemu_get_be32(f)); + return 0; +} + +static int put_vscr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, QJSON *vmdesc) +{ + PowerPCCPU *cpu = opaque; + qemu_put_be32(f, helper_mfvscr(&cpu->env)); + return 0; +} + +static const VMStateInfo vmstate_vscr = { + .name = "cpu/altivec/vscr", + .get = get_vscr, + .put = put_vscr, +}; + static const VMStateDescription vmstate_altivec = { .name = "cpu/altivec", .version_id = 1, .minimum_version_id = 1, .needed = altivec_needed, .fields = (VMStateField[]) { - VMSTATE_AVR_ARRAY(env.avr, PowerPCCPU, 32), - VMSTATE_UINT32(env.vscr, PowerPCCPU), + VMSTATE_AVR_ARRAY(env.vsr, PowerPCCPU, 32), + /* + * Save the architecture value of the vscr, not the internally + * expanded version. Since this architecture value does not + * exist in memory to be stored, this requires a but of hoop + * jumping. We want OFFSET=0 so that we effectively pass CPU + * to the helper functions. + */ + { + .name = "vscr", + .version_id = 0, + .size = sizeof(uint32_t), + .info = &vmstate_vscr, + .flags = VMS_SINGLE, + .offset = 0 + }, VMSTATE_END_OF_LIST() }, }; @@ -391,7 +492,7 @@ static const VMStateDescription vmstate_vsx = { .minimum_version_id = 1, .needed = vsx_needed, .fields = (VMStateField[]) { - VMSTATE_UINT64_ARRAY(env.vsr, PowerPCCPU, 32), + VMSTATE_VSR_ARRAY(env.vsr, PowerPCCPU, 32), VMSTATE_END_OF_LIST() }, }; @@ -452,7 +553,8 @@ static const VMStateDescription vmstate_sr = { }; #ifdef TARGET_PPC64 -static int get_slbe(QEMUFile *f, void *pv, size_t size, VMStateField *field) +static int get_slbe(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) { ppc_slb_t *v = pv; @@ -462,8 +564,8 @@ static int get_slbe(QEMUFile *f, void *pv, size_t size, VMStateField *field) return 0; } -static int put_slbe(QEMUFile *f, void *pv, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_slbe(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, QJSON *vmdesc) { ppc_slb_t *v = pv; diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 8f0d86d104..9c5a68579e 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -25,6 +25,7 @@ #include "exec/cpu_ldst.h" #include "tcg.h" #include "internal.h" +#include "qemu/atomic128.h" //#define DEBUG_OP @@ -141,11 +142,13 @@ void helper_stsw(CPUPPCState *env, target_ulong addr, uint32_t nb, } } -void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) +static void dcbz_common(CPUPPCState *env, target_ulong addr, + uint32_t opcode, bool epid, uintptr_t retaddr) { target_ulong mask, dcbz_size = env->dcache_line_size; uint32_t i; void *haddr; + int mmu_idx = epid ? PPC_TLB_EPID_STORE : env->dmmu_idx; #if defined(TARGET_PPC64) /* Check for dcbz vs dcbzl on 970 */ @@ -165,17 +168,34 @@ void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) } /* Try fast path translate */ - haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, env->dmmu_idx); + haddr = tlb_vaddr_to_host(env, addr, MMU_DATA_STORE, mmu_idx); if (haddr) { memset(haddr, 0, dcbz_size); } else { /* Slow path */ for (i = 0; i < dcbz_size; i += 8) { - cpu_stq_data_ra(env, addr + i, 0, GETPC()); + if (epid) { +#if !defined(CONFIG_USER_ONLY) + /* Does not make sense on USER_ONLY config */ + cpu_stq_eps_ra(env, addr + i, 0, retaddr); +#endif + } else { + cpu_stq_data_ra(env, addr + i, 0, retaddr); + } } } } +void helper_dcbz(CPUPPCState *env, target_ulong addr, uint32_t opcode) +{ + dcbz_common(env, addr, opcode, false, GETPC()); +} + +void helper_dcbzep(CPUPPCState *env, target_ulong addr, uint32_t opcode) +{ + dcbz_common(env, addr, opcode, true, GETPC()); +} + void helper_icbi(CPUPPCState *env, target_ulong addr) { addr &= ~(env->dcache_line_size - 1); @@ -187,6 +207,15 @@ void helper_icbi(CPUPPCState *env, target_ulong addr) cpu_ldl_data_ra(env, addr, GETPC()); } +void helper_icbiep(CPUPPCState *env, target_ulong addr) +{ +#if !defined(CONFIG_USER_ONLY) + /* See comments above */ + addr &= ~(env->dcache_line_size - 1); + cpu_ldl_epl_ra(env, addr, GETPC()); +#endif +} + /* XXX: to be tested */ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, uint32_t ra, uint32_t rb) @@ -215,11 +244,15 @@ target_ulong helper_lscbx(CPUPPCState *env, target_ulong addr, uint32_t reg, return i; } -#if defined(TARGET_PPC64) && defined(CONFIG_ATOMIC128) +#ifdef TARGET_PPC64 uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr, uint32_t opidx) { - Int128 ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC()); + Int128 ret; + + /* We will have raised EXCP_ATOMIC from the translator. */ + assert(HAVE_ATOMIC128); + ret = helper_atomic_ldo_le_mmu(env, addr, opidx, GETPC()); env->retxh = int128_gethi(ret); return int128_getlo(ret); } @@ -227,7 +260,11 @@ uint64_t helper_lq_le_parallel(CPUPPCState *env, target_ulong addr, uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr, uint32_t opidx) { - Int128 ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC()); + Int128 ret; + + /* We will have raised EXCP_ATOMIC from the translator. */ + assert(HAVE_ATOMIC128); + ret = helper_atomic_ldo_be_mmu(env, addr, opidx, GETPC()); env->retxh = int128_gethi(ret); return int128_getlo(ret); } @@ -235,14 +272,22 @@ uint64_t helper_lq_be_parallel(CPUPPCState *env, target_ulong addr, void helper_stq_le_parallel(CPUPPCState *env, target_ulong addr, uint64_t lo, uint64_t hi, uint32_t opidx) { - Int128 val = int128_make128(lo, hi); + Int128 val; + + /* We will have raised EXCP_ATOMIC from the translator. */ + assert(HAVE_ATOMIC128); + val = int128_make128(lo, hi); helper_atomic_sto_le_mmu(env, addr, val, opidx, GETPC()); } void helper_stq_be_parallel(CPUPPCState *env, target_ulong addr, uint64_t lo, uint64_t hi, uint32_t opidx) { - Int128 val = int128_make128(lo, hi); + Int128 val; + + /* We will have raised EXCP_ATOMIC from the translator. */ + assert(HAVE_ATOMIC128); + val = int128_make128(lo, hi); helper_atomic_sto_be_mmu(env, addr, val, opidx, GETPC()); } @@ -252,6 +297,9 @@ uint32_t helper_stqcx_le_parallel(CPUPPCState *env, target_ulong addr, { bool success = false; + /* We will have raised EXCP_ATOMIC from the translator. */ + assert(HAVE_CMPXCHG128); + if (likely(addr == env->reserve_addr)) { Int128 oldv, cmpv, newv; @@ -271,6 +319,9 @@ uint32_t helper_stqcx_be_parallel(CPUPPCState *env, target_ulong addr, { bool success = false; + /* We will have raised EXCP_ATOMIC from the translator. */ + assert(HAVE_CMPXCHG128); + if (likely(addr == env->reserve_addr)) { Int128 oldv, cmpv, newv; diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index b884930096..c65d1ade15 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -117,6 +117,21 @@ void helper_store_pidr(CPUPPCState *env, target_ulong val) tlb_flush(CPU(cpu)); } +void helper_store_lpidr(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + + env->spr[SPR_LPIDR] = val; + + /* + * We need to flush the TLB on LPID changes as we only tag HV vs + * guest in TCG TLB. Also the quadrants means the HV will + * potentially access and cache entries for the current LPID as + * well. + */ + tlb_flush(CPU(cpu)); +} + void helper_store_hid0_601(CPUPPCState *env, target_ulong val) { target_ulong hid0; diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index b60df4408f..32b8c166b5 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -26,9 +26,36 @@ int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx) { - if (ppc64_radix_guest(cpu)) { /* Guest uses radix */ + if (ppc64_v3_radix(cpu)) { /* Guest uses radix */ return ppc_radix64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx); } else { /* Guest uses hash */ return ppc_hash64_handle_mmu_fault(cpu, eaddr, rwx, mmu_idx); } } + +hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr) +{ + if (ppc64_v3_radix(cpu)) { + return ppc_radix64_get_phys_page_debug(cpu, eaddr); + } else { + return ppc_hash64_get_phys_page_debug(cpu, eaddr); + } +} + +bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, ppc_v3_pate_t *entry) +{ + uint64_t patb = cpu->env.spr[SPR_PTCR] & PTCR_PATB; + uint64_t pats = cpu->env.spr[SPR_PTCR] & PTCR_PATS; + + /* Calculate number of entries */ + pats = 1ull << (pats + 12 - 4); + if (pats <= lpid) { + return false; + } + + /* Grab entry */ + patb += 16 * lpid; + entry->dw0 = ldq_phys(CPU(cpu)->as, patb); + entry->dw1 = ldq_phys(CPU(cpu)->as, patb + 8); + return true; +} diff --git a/target/ppc/mmu-book3s-v3.h b/target/ppc/mmu-book3s-v3.h index fdf80987d7..ee8288e32d 100644 --- a/target/ppc/mmu-book3s-v3.h +++ b/target/ppc/mmu-book3s-v3.h @@ -17,8 +17,10 @@ * License along with this library; if not, see . */ -#ifndef MMU_H -#define MMU_H +#ifndef MMU_BOOOK3S_V3_H +#define MMU_BOOOK3S_V3_H + +#include "mmu-hash64.h" #ifndef CONFIG_USER_ONLY @@ -29,7 +31,16 @@ #define PTCR_PATS 0x000000000000001FULL /* Partition Table Size */ /* Partition Table Entry Fields */ -#define PATBE1_GR 0x8000000000000000 +#define PATE0_HR 0x8000000000000000 + +/* + * WARNING: This field doesn't actually exist in the final version of + * the architecture and is unused by hardware. However, qemu uses it + * as an indication of a radix guest in the pseudo-PATB entry that it + * maintains for SPAPR guests and in the migration stream, so we need + * to keep it around + */ +#define PATE1_GR 0x8000000000000000 /* Process Table Entry */ struct prtb_entry { @@ -43,19 +54,68 @@ static inline bool ppc64_use_proc_tbl(PowerPCCPU *cpu) return !!(cpu->env.spr[SPR_LPCR] & LPCR_UPRT); } -static inline bool ppc64_radix_guest(PowerPCCPU *cpu) -{ - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); +bool ppc64_v3_get_pate(PowerPCCPU *cpu, target_ulong lpid, + ppc_v3_pate_t *entry); - return !!(vhc->get_patbe(cpu->vhyp) & PATBE1_GR); +/* + * The LPCR:HR bit is a shortcut that avoids having to + * dig out the partition table in the fast path. This is + * also how the HW uses it. + */ +static inline bool ppc64_v3_radix(PowerPCCPU *cpu) +{ + return !!(cpu->env.spr[SPR_LPCR] & LPCR_HR); } +hwaddr ppc64_v3_get_phys_page_debug(PowerPCCPU *cpu, vaddr eaddr); + int ppc64_v3_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx); +static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + return 0; + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return base & SDR_64_HTABORG; +} + +static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) +{ + uint64_t base; + + if (cpu->vhyp) { + PPCVirtualHypervisorClass *vhc = + PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + return vhc->hpt_mask(cpu->vhyp); + } + if (cpu->env.mmu_model == POWERPC_MMU_3_00) { + ppc_v3_pate_t pate; + + if (!ppc64_v3_get_pate(cpu, cpu->env.spr[SPR_LPIDR], &pate)) { + return 0; + } + base = pate.dw0; + } else { + base = cpu->env.spr[SPR_SDR1]; + } + return (1ULL << ((base & SDR_64_HTABSIZE) + 18 - 7)) - 1; +} + #endif /* TARGET_PPC64 */ #endif /* CONFIG_USER_ONLY */ -#endif /* MMU_H */ +#endif /* MMU_BOOOK3S_V3_H */ diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 03ae3c1279..e8562a7c87 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -319,6 +319,12 @@ static hwaddr ppc_hash32_pteg_search(PowerPCCPU *cpu, hwaddr pteg_off, for (i = 0; i < HPTES_PER_GROUP; i++) { pte0 = ppc_hash32_load_hpte0(cpu, pte_offset); + /* + * pte0 contains the valid bit and must be read before pte1, + * otherwise we might see an old pte1 with a new valid bit and + * thus an inconsistent hpte value + */ + smp_rmb(); pte1 = ppc_hash32_load_hpte1(cpu, pte_offset); if ((pte0 & HPTE32_V_VALID) diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 276d9015e7..a2b1ec5040 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -417,7 +417,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, hwaddr ptex, int n) { hwaddr pte_offset = ptex * HASH_PTE_SIZE_64; - hwaddr base = ppc_hash64_hpt_base(cpu); + hwaddr base; hwaddr plen = n * HASH_PTE_SIZE_64; const ppc_hash_pte64_t *hptes; @@ -426,6 +426,7 @@ const ppc_hash_pte64_t *ppc_hash64_map_hptes(PowerPCCPU *cpu, PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); return vhc->map_hptes(cpu->vhyp, ptex, n); } + base = ppc_hash64_hpt_base(cpu); if (!base) { return NULL; @@ -490,6 +491,18 @@ static unsigned hpte_page_shift(const PPCHash64SegmentPageSizes *sps, return 0; /* Bad page size encoding */ } +static void ppc64_v3_new_to_old_hpte(target_ulong *pte0, target_ulong *pte1) +{ + /* Insert B into pte0 */ + *pte0 = (*pte0 & HPTE64_V_COMMON_BITS) | + ((*pte1 & HPTE64_R_3_0_SSIZE_MASK) << + (HPTE64_V_SSIZE_SHIFT - HPTE64_R_3_0_SSIZE_SHIFT)); + + /* Remove B from pte1 */ + *pte1 = *pte1 & ~HPTE64_R_3_0_SSIZE_MASK; +} + + static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, const PPCHash64SegmentPageSizes *sps, target_ulong ptem, @@ -507,8 +520,19 @@ static hwaddr ppc_hash64_pteg_search(PowerPCCPU *cpu, hwaddr hash, } for (i = 0; i < HPTES_PER_GROUP; i++) { pte0 = ppc_hash64_hpte0(cpu, pteg, i); + /* + * pte0 contains the valid bit and must be read before pte1, + * otherwise we might see an old pte1 with a new valid bit and + * thus an inconsistent hpte value + */ + smp_rmb(); pte1 = ppc_hash64_hpte1(cpu, pteg, i); + /* Convert format if necessary */ + if (cpu->env.mmu_model == POWERPC_MMU_3_00 && !cpu->vhyp) { + ppc64_v3_new_to_old_hpte(&pte0, &pte1); + } + /* This compares V, B, H (secondary) and the AVPN */ if (HPTE64_V_COMPARE(pte0, ptem)) { *pshift = hpte_page_shift(sps, pte0, pte1); @@ -918,7 +942,7 @@ hwaddr ppc_hash64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong addr) void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex, uint64_t pte0, uint64_t pte1) { - hwaddr base = ppc_hash64_hpt_base(cpu); + hwaddr base; hwaddr offset = ptex * HASH_PTE_SIZE_64; if (cpu->vhyp) { @@ -927,6 +951,7 @@ void ppc_hash64_store_hpte(PowerPCCPU *cpu, hwaddr ptex, vhc->store_hpte(cpu->vhyp, ptex, pte0, pte1); return; } + base = ppc_hash64_hpt_base(cpu); stq_phys(CPU(cpu)->as, base + offset, pte0); stq_phys(CPU(cpu)->as, base + offset + HASH_PTE_SIZE_64 / 2, pte1); @@ -1084,10 +1109,18 @@ void ppc_store_lpcr(PowerPCCPU *cpu, target_ulong val) case POWERPC_MMU_3_00: /* P9 */ lpcr = val & (LPCR_VPM1 | LPCR_ISL | LPCR_KBV | LPCR_DPFD | (LPCR_PECE_U_MASK & LPCR_HVEE) | LPCR_ILE | LPCR_AIL | - LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | + LPCR_UPRT | LPCR_EVIRT | LPCR_ONL | LPCR_HR | LPCR_LD | (LPCR_PECE_L_MASK & (LPCR_PDEE | LPCR_HDEE | LPCR_EEE | LPCR_DEE | LPCR_OEE)) | LPCR_MER | LPCR_GTSE | LPCR_TC | LPCR_HEIC | LPCR_LPES0 | LPCR_HVICE | LPCR_HDICE); + /* + * If we have a virtual hypervisor, we need to bring back RMLS. It + * doesn't exist on an actual P9 but that's all we know how to + * configure with softmmu at the moment + */ + if (cpu->vhyp) { + lpcr |= (val & LPCR_RMLS); + } break; default: ; diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index f11efc9cbc..6b555b7220 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -63,6 +63,7 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, #define SDR_64_HTABORG 0x0FFFFFFFFFFC0000ULL #define SDR_64_HTABSIZE 0x000000000000001FULL +#define PATE0_HTABORG 0x0FFFFFFFFFFC0000ULL #define HPTES_PER_GROUP 8 #define HASH_PTE_SIZE_64 16 #define HASH_PTEG_SIZE_64 (HASH_PTE_SIZE_64 * HPTES_PER_GROUP) @@ -102,23 +103,10 @@ void ppc_hash64_filter_pagesizes(PowerPCCPU *cpu, #define HPTE64_V_1TB_SEG 0x4000000000000000ULL #define HPTE64_V_VRMA_MASK 0x4001ffffff000000ULL -static inline hwaddr ppc_hash64_hpt_base(PowerPCCPU *cpu) -{ - if (cpu->vhyp) { - return 0; - } - return cpu->env.spr[SPR_SDR1] & SDR_64_HTABORG; -} - -static inline hwaddr ppc_hash64_hpt_mask(PowerPCCPU *cpu) -{ - if (cpu->vhyp) { - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); - return vhc->hpt_mask(cpu->vhyp); - } - return (1ULL << ((cpu->env.spr[SPR_SDR1] & SDR_64_HTABSIZE) + 18 - 7)) - 1; -} +/* Format changes for ARCH v3 */ +#define HPTE64_V_COMMON_BITS 0x000fffffffffffffULL +#define HPTE64_R_3_0_SSIZE_SHIFT 58 +#define HPTE64_R_3_0_SSIZE_MASK (3ULL << HPTE64_R_3_0_SSIZE_SHIFT) struct ppc_hash_pte64 { uint64_t pte0, pte1; diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index ab76cbc835..ca1fb2673f 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -31,10 +31,26 @@ static bool ppc_radix64_get_fully_qualified_addr(CPUPPCState *env, vaddr eaddr, uint64_t *lpid, uint64_t *pid) { - /* We don't have HV support yet and shouldn't get here with it set anyway */ - assert(!msr_hv); - - if (!msr_hv) { /* !MSR[HV] -> Guest */ + if (msr_hv) { /* MSR[HV] -> Hypervisor/bare metal */ + switch (eaddr & R_EADDR_QUADRANT) { + case R_EADDR_QUADRANT0: + *lpid = 0; + *pid = env->spr[SPR_BOOKS_PID]; + break; + case R_EADDR_QUADRANT1: + *lpid = env->spr[SPR_LPIDR]; + *pid = env->spr[SPR_BOOKS_PID]; + break; + case R_EADDR_QUADRANT2: + *lpid = env->spr[SPR_LPIDR]; + *pid = 0; + break; + case R_EADDR_QUADRANT3: + *lpid = 0; + *pid = 0; + break; + } + } else { /* !MSR[HV] -> Guest */ switch (eaddr & R_EADDR_QUADRANT) { case R_EADDR_QUADRANT0: /* Guest application */ *lpid = env->spr[SPR_LPIDR]; @@ -186,20 +202,32 @@ static uint64_t ppc_radix64_walk_tree(PowerPCCPU *cpu, vaddr eaddr, raddr, psize, fault_cause, pte_addr); } +static bool validate_pate(PowerPCCPU *cpu, uint64_t lpid, ppc_v3_pate_t *pate) +{ + CPUPPCState *env = &cpu->env; + + if (!(pate->dw0 & PATE0_HR)) { + return false; + } + if (lpid == 0 && !msr_hv) { + return false; + } + /* More checks ... */ + return true; +} + int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, int mmu_idx) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + PPCVirtualHypervisorClass *vhc; hwaddr raddr, pte_addr; - uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte; + uint64_t lpid = 0, pid = 0, offset, size, prtbe0, pte; int page_size, prot, fault_cause = 0; + ppc_v3_pate_t pate; assert((rwx == 0) || (rwx == 1) || (rwx == 2)); - assert(!msr_hv); /* For now there is no Radix PowerNV Support */ - assert(cpu->vhyp); assert(ppc64_use_proc_tbl(cpu)); /* Real Mode Access */ @@ -220,17 +248,33 @@ int ppc_radix64_handle_mmu_fault(PowerPCCPU *cpu, vaddr eaddr, int rwx, } /* Get Process Table */ - patbe = vhc->get_patbe(cpu->vhyp); + if (cpu->vhyp) { + vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->get_pate(cpu->vhyp, &pate); + } else { + if (!ppc64_v3_get_pate(cpu, lpid, &pate)) { + ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE); + return 1; + } + if (!validate_pate(cpu, lpid, &pate)) { + ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_R_BADCONFIG); + } + /* We don't support guest mode yet */ + if (lpid != 0) { + error_report("PowerNV guest support Unimplemented"); + exit(1); + } + } /* Index Process Table by PID to Find Corresponding Process Table Entry */ offset = pid * sizeof(struct prtb_entry); - size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12); + size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12); if (offset >= size) { /* offset exceeds size of the process table */ ppc_radix64_raise_si(cpu, rwx, eaddr, DSISR_NOPTE); return 1; } - prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset); + prtbe0 = ldq_phys(cs->as, (pate.dw1 & PATE1_R_PRTB) + offset); /* Walk Radix Tree from Process Table Entry to Convert EA to RA */ page_size = PRTBE_R_GET_RTS(prtbe0); @@ -255,11 +299,11 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) { CPUState *cs = CPU(cpu); CPUPPCState *env = &cpu->env; - PPCVirtualHypervisorClass *vhc = - PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + PPCVirtualHypervisorClass *vhc; hwaddr raddr, pte_addr; - uint64_t lpid = 0, pid = 0, offset, size, patbe, prtbe0, pte; + uint64_t lpid = 0, pid = 0, offset, size, prtbe0, pte; int page_size, fault_cause = 0; + ppc_v3_pate_t pate; /* Handle Real Mode */ if (msr_dr == 0) { @@ -273,16 +317,31 @@ hwaddr ppc_radix64_get_phys_page_debug(PowerPCCPU *cpu, target_ulong eaddr) } /* Get Process Table */ - patbe = vhc->get_patbe(cpu->vhyp); + if (cpu->vhyp) { + vhc = PPC_VIRTUAL_HYPERVISOR_GET_CLASS(cpu->vhyp); + vhc->get_pate(cpu->vhyp, &pate); + } else { + if (!ppc64_v3_get_pate(cpu, lpid, &pate)) { + return -1; + } + if (!validate_pate(cpu, lpid, &pate)) { + return -1; + } + /* We don't support guest mode yet */ + if (lpid != 0) { + error_report("PowerNV guest support Unimplemented"); + exit(1); + } + } /* Index Process Table by PID to Find Corresponding Process Table Entry */ offset = pid * sizeof(struct prtb_entry); - size = 1ULL << ((patbe & PATBE1_R_PRTS) + 12); + size = 1ULL << ((pate.dw1 & PATE1_R_PRTS) + 12); if (offset >= size) { /* offset exceeds size of the process table */ return -1; } - prtbe0 = ldq_phys(cs->as, (patbe & PATBE1_R_PRTB) + offset); + prtbe0 = ldq_phys(cs->as, (pate.dw1 & PATE1_R_PRTB) + offset); /* Walk Radix Tree from Process Table Entry to Convert EA to RA */ page_size = PRTBE_R_GET_RTS(prtbe0); diff --git a/target/ppc/mmu-radix64.h b/target/ppc/mmu-radix64.h index 0ecf063a17..96228546aa 100644 --- a/target/ppc/mmu-radix64.h +++ b/target/ppc/mmu-radix64.h @@ -12,8 +12,8 @@ #define R_EADDR_QUADRANT3 0xC000000000000000 /* Radix Partition Table Entry Fields */ -#define PATBE1_R_PRTB 0x0FFFFFFFFFFFF000 -#define PATBE1_R_PRTS 0x000000000000001F +#define PATE1_R_PRTB 0x0FFFFFFFFFFFF000 +#define PATE1_R_PRTS 0x000000000000001F /* Radix Process Table Entry Fields */ #define PRTBE_R_GET_RTS(rts) \ diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index e6739e6c24..4a6be4d63b 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -924,29 +924,84 @@ static int ppcmas_tlb_check(CPUPPCState *env, ppcmas_tlb_t *tlb, return 0; } +static bool is_epid_mmu(int mmu_idx) +{ + return mmu_idx == PPC_TLB_EPID_STORE || mmu_idx == PPC_TLB_EPID_LOAD; +} + +static uint32_t mmubooke206_esr(int mmu_idx, bool rw) +{ + uint32_t esr = 0; + if (rw) { + esr |= ESR_ST; + } + if (is_epid_mmu(mmu_idx)) { + esr |= ESR_EPID; + } + return esr; +} + +/* Get EPID register given the mmu_idx. If this is regular load, + * construct the EPID access bits from current processor state */ + +/* Get the effective AS and PR bits and the PID. The PID is returned only if + * EPID load is requested, otherwise the caller must detect the correct EPID. + * Return true if valid EPID is returned. */ +static bool mmubooke206_get_as(CPUPPCState *env, + int mmu_idx, uint32_t *epid_out, + bool *as_out, bool *pr_out) +{ + if (is_epid_mmu(mmu_idx)) { + uint32_t epidr; + if (mmu_idx == PPC_TLB_EPID_STORE) { + epidr = env->spr[SPR_BOOKE_EPSC]; + } else { + epidr = env->spr[SPR_BOOKE_EPLC]; + } + *epid_out = (epidr & EPID_EPID) >> EPID_EPID_SHIFT; + *as_out = !!(epidr & EPID_EAS); + *pr_out = !!(epidr & EPID_EPR); + return true; + } else { + *as_out = msr_ds; + *pr_out = msr_pr; + return false; + } +} + +/* Check if the tlb found by hashing really matches */ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, hwaddr *raddr, int *prot, target_ulong address, int rw, - int access_type) + int access_type, int mmu_idx) { int ret; int prot2 = 0; + uint32_t epid; + bool as, pr; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); - if (ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID]) >= 0) { - goto found_tlb; - } + if (!use_epid) { + if (ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID]) >= 0) { + goto found_tlb; + } - if (env->spr[SPR_BOOKE_PID1] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID1]) >= 0) { - goto found_tlb; - } + if (env->spr[SPR_BOOKE_PID1] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID1]) >= 0) { + goto found_tlb; + } - if (env->spr[SPR_BOOKE_PID2] && - ppcmas_tlb_check(env, tlb, raddr, address, - env->spr[SPR_BOOKE_PID2]) >= 0) { - goto found_tlb; + if (env->spr[SPR_BOOKE_PID2] && + ppcmas_tlb_check(env, tlb, raddr, address, + env->spr[SPR_BOOKE_PID2]) >= 0) { + goto found_tlb; + } + } else { + if (ppcmas_tlb_check(env, tlb, raddr, address, epid) >= 0) { + goto found_tlb; + } } LOG_SWTLB("%s: TLB entry not found\n", __func__); @@ -954,7 +1009,7 @@ static int mmubooke206_check_tlb(CPUPPCState *env, ppcmas_tlb_t *tlb, found_tlb: - if (msr_pr != 0) { + if (pr) { if (tlb->mas7_3 & MAS3_UR) { prot2 |= PAGE_READ; } @@ -978,6 +1033,8 @@ found_tlb: /* Check the address space and permissions */ if (access_type == ACCESS_CODE) { + /* There is no way to fetch code using epid load */ + assert(!use_epid); if (msr_ir != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { LOG_SWTLB("%s: AS doesn't match\n", __func__); return -1; @@ -992,7 +1049,7 @@ found_tlb: LOG_SWTLB("%s: no PAGE_EXEC: %x\n", __func__, prot2); ret = -3; } else { - if (msr_dr != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { + if (as != ((tlb->mas1 & MAS1_TS) >> MAS1_TS_SHIFT)) { LOG_SWTLB("%s: AS doesn't match\n", __func__); return -1; } @@ -1012,7 +1069,7 @@ found_tlb: static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, target_ulong address, int rw, - int access_type) + int access_type, int mmu_idx) { ppcmas_tlb_t *tlb; hwaddr raddr; @@ -1030,7 +1087,7 @@ static int mmubooke206_get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, continue; } ret = mmubooke206_check_tlb(env, tlb, &raddr, &ctx->prot, address, - rw, access_type); + rw, access_type, mmu_idx); if (ret != -1) { goto found_tlb; } @@ -1285,7 +1342,7 @@ void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUPPCState *env) dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env)); break; case POWERPC_MMU_3_00: - if (ppc64_radix_guest(ppc_env_get_cpu(env))) { + if (ppc64_v3_radix(ppc_env_get_cpu(env))) { /* TODO - Unsupported */ } else { dump_slb(f, cpu_fprintf, ppc_env_get_cpu(env)); @@ -1348,18 +1405,16 @@ static inline int check_physical(CPUPPCState *env, mmu_ctx_t *ctx, return ret; } -static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, - target_ulong eaddr, int rw, int access_type) +static int get_physical_address_wtlb( + CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type, + int mmu_idx) { PowerPCCPU *cpu = ppc_env_get_cpu(env); int ret = -1; bool real_mode = (access_type == ACCESS_CODE && msr_ir == 0) || (access_type != ACCESS_CODE && msr_dr == 0); -#if 0 - qemu_log("%s\n", __func__); -#endif - switch (env->mmu_model) { case POWERPC_MMU_SOFT_6xx: case POWERPC_MMU_SOFT_74xx: @@ -1392,7 +1447,7 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, break; case POWERPC_MMU_BOOKE206: ret = mmubooke206_get_physical_address(env, ctx, eaddr, rw, - access_type); + access_type, mmu_idx); break; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ @@ -1409,14 +1464,17 @@ static int get_physical_address(CPUPPCState *env, mmu_ctx_t *ctx, cpu_abort(CPU(cpu), "Unknown or invalid MMU model\n"); return -1; } -#if 0 - qemu_log("%s address " TARGET_FMT_lx " => %d " TARGET_FMT_plx "\n", - __func__, eaddr, ret, ctx->raddr); -#endif return ret; } +static int get_physical_address( + CPUPPCState *env, mmu_ctx_t *ctx, + target_ulong eaddr, int rw, int access_type) +{ + return get_physical_address_wtlb(env, ctx, eaddr, rw, access_type, 0); +} + hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -1431,12 +1489,7 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) case POWERPC_MMU_2_07: return ppc_hash64_get_phys_page_debug(cpu, addr); case POWERPC_MMU_3_00: - if (ppc64_radix_guest(ppc_env_get_cpu(env))) { - return ppc_radix64_get_phys_page_debug(cpu, addr); - } else { - return ppc_hash64_get_phys_page_debug(cpu, addr); - } - break; + return ppc64_v3_get_phys_page_debug(cpu, addr); #endif case POWERPC_MMU_32B: @@ -1463,8 +1516,15 @@ hwaddr ppc_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) } static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, - int rw) + int rw, int mmu_idx) { + uint32_t epid; + bool as, pr; + uint32_t missed_tid = 0; + bool use_epid = mmubooke206_get_as(env, mmu_idx, &epid, &as, &pr); + if (rw == 2) { + as = msr_ir; + } env->spr[SPR_BOOKE_MAS0] = env->spr[SPR_BOOKE_MAS4] & MAS4_TLBSELD_MASK; env->spr[SPR_BOOKE_MAS1] = env->spr[SPR_BOOKE_MAS4] & MAS4_TSIZED_MASK; env->spr[SPR_BOOKE_MAS2] = env->spr[SPR_BOOKE_MAS4] & MAS4_WIMGED_MASK; @@ -1473,7 +1533,7 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, env->spr[SPR_BOOKE_MAS7] = 0; /* AS */ - if (((rw == 2) && msr_ir) || ((rw != 2) && msr_dr)) { + if (as) { env->spr[SPR_BOOKE_MAS1] |= MAS1_TS; env->spr[SPR_BOOKE_MAS6] |= MAS6_SAS; } @@ -1481,19 +1541,25 @@ static void booke206_update_mas_tlb_miss(CPUPPCState *env, target_ulong address, env->spr[SPR_BOOKE_MAS1] |= MAS1_VALID; env->spr[SPR_BOOKE_MAS2] |= address & MAS2_EPN_MASK; - switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { - case MAS4_TIDSELD_PID0: - env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID] << MAS1_TID_SHIFT; - break; - case MAS4_TIDSELD_PID1: - env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID1] << MAS1_TID_SHIFT; - break; - case MAS4_TIDSELD_PID2: - env->spr[SPR_BOOKE_MAS1] |= env->spr[SPR_BOOKE_PID2] << MAS1_TID_SHIFT; - break; + if (!use_epid) { + switch (env->spr[SPR_BOOKE_MAS4] & MAS4_TIDSELD_PIDZ) { + case MAS4_TIDSELD_PID0: + missed_tid = env->spr[SPR_BOOKE_PID]; + break; + case MAS4_TIDSELD_PID1: + missed_tid = env->spr[SPR_BOOKE_PID1]; + break; + case MAS4_TIDSELD_PID2: + missed_tid = env->spr[SPR_BOOKE_PID2]; + break; + } + env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; + } else { + missed_tid = epid; + env->spr[SPR_BOOKE_MAS6] |= missed_tid << 16; } + env->spr[SPR_BOOKE_MAS1] |= (missed_tid << MAS1_TID_SHIFT); - env->spr[SPR_BOOKE_MAS6] |= env->spr[SPR_BOOKE_PID] << 16; /* next victim logic */ env->spr[SPR_BOOKE_MAS0] |= env->last_way << MAS0_ESEL_SHIFT; @@ -1520,7 +1586,8 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, /* data access */ access_type = env->access_type; } - ret = get_physical_address(env, &ctx, address, rw, access_type); + ret = get_physical_address_wtlb(env, &ctx, address, rw, + access_type, mmu_idx); if (ret == 0) { tlb_set_page(cs, address & TARGET_PAGE_MASK, ctx.raddr & TARGET_PAGE_MASK, ctx.prot, @@ -1550,12 +1617,13 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, env->spr[SPR_40x_ESR] = 0x00000000; break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, 2); + booke206_update_mas_tlb_miss(env, address, 2, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_ITLB; env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = address; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, 0); return -1; case POWERPC_MMU_MPC8xx: /* XXX: TODO */ @@ -1642,13 +1710,13 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, cpu_abort(cs, "MPC8xx MMU model is not implemented\n"); break; case POWERPC_MMU_BOOKE206: - booke206_update_mas_tlb_miss(env, address, rw); + booke206_update_mas_tlb_miss(env, address, rw, mmu_idx); /* fall through */ case POWERPC_MMU_BOOKE: cs->exception_index = POWERPC_EXCP_DTLB; env->error_code = 0; env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, rw); return -1; case POWERPC_MMU_REAL: cpu_abort(cs, "PowerPC in real mode should never raise " @@ -1672,7 +1740,7 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, } else if ((env->mmu_model == POWERPC_MMU_BOOKE) || (env->mmu_model == POWERPC_MMU_BOOKE206)) { env->spr[SPR_BOOKE_DEAR] = address; - env->spr[SPR_BOOKE_ESR] = rw ? ESR_ST : 0; + env->spr[SPR_BOOKE_ESR] = mmubooke206_esr(mmu_idx, rw); } else { env->spr[SPR_DAR] = address; if (rw == 1) { @@ -1724,10 +1792,6 @@ static int cpu_ppc_handle_mmu_fault(CPUPPCState *env, target_ulong address, break; } } -#if 0 - printf("%s: set exception to %d %02x\n", __func__, - cs->exception, env->error_code); -#endif ret = 1; } @@ -2363,12 +2427,12 @@ void helper_4xx_tlbwe_hi(CPUPPCState *env, target_ulong entry, tlb->size = booke_tlb_to_page_size((val >> PPC4XX_TLBHI_SIZE_SHIFT) & PPC4XX_TLBHI_SIZE_MASK); /* We cannot handle TLB size < TARGET_PAGE_SIZE. - * If this ever occurs, one should use the ppcemb target instead - * of the ppc or ppc64 one + * If this ever occurs, we should implement TARGET_PAGE_BITS_VARY */ if ((val & PPC4XX_TLBHI_V) && tlb->size < TARGET_PAGE_SIZE) { cpu_abort(cs, "TLB size " TARGET_FMT_lu " < %u " - "are not supported (%d)\n", + "are not supported (%d)\n" + "Please implement TARGET_PAGE_BITS_VARY\n", tlb->size, TARGET_PAGE_SIZE, (int)((val >> 7) & 0x7)); } tlb->EPN = val & ~(tlb->size - 1); @@ -2598,6 +2662,19 @@ void helper_booke_setpid(CPUPPCState *env, uint32_t pidn, target_ulong pid) tlb_flush(CPU(cpu)); } +void helper_booke_set_eplc(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + env->spr[SPR_BOOKE_EPLC] = val & EPID_MASK; + tlb_flush_by_mmuidx(CPU(cpu), 1 << PPC_TLB_EPID_LOAD); +} +void helper_booke_set_epsc(CPUPPCState *env, target_ulong val) +{ + PowerPCCPU *cpu = ppc_env_get_cpu(env); + env->spr[SPR_BOOKE_EPSC] = val & EPID_MASK; + tlb_flush_by_mmuidx(CPU(cpu), 1 << PPC_TLB_EPID_STORE); +} + static inline void flush_page(CPUPPCState *env, ppcmas_tlb_t *tlb) { PowerPCCPU *cpu = ppc_env_get_cpu(env); diff --git a/target/ppc/monitor.c b/target/ppc/monitor.c index 14915119fc..04deec8030 100644 --- a/target/ppc/monitor.c +++ b/target/ppc/monitor.c @@ -123,8 +123,8 @@ int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) /* Floating point registers */ if ((qemu_tolower(name[0]) == 'f') && - ppc_cpu_get_reg_num(name + 1, ARRAY_SIZE(env->fpr), ®num)) { - *pval = env->fpr[regnum]; + ppc_cpu_get_reg_num(name + 1, 32, ®num)) { + *pval = *cpu_fpr_ptr(env, regnum); return 0; } diff --git a/target/ppc/trace-events b/target/ppc/trace-events index 35ee898566..3858f97dff 100644 --- a/target/ppc/trace-events +++ b/target/ppc/trace-events @@ -1,5 +1,5 @@ # See docs/devel/tracing.txt for syntax documentation. -# target/ppc/kvm.c +# kvm.c kvm_failed_spr_set(int str, const char *msg) "Warning: Unable to set SPR %d to KVM: %s" kvm_failed_spr_get(int str, const char *msg) "Warning: Unable to retrieve SPR %d from KVM: %s" diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 9eaa10b421..badc1ae1a3 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -24,6 +24,7 @@ #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg-op.h" +#include "tcg-op-gvec.h" #include "qemu/host-utils.h" #include "exec/cpu_ldst.h" @@ -33,6 +34,7 @@ #include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" +#include "qemu/atomic128.h" #define CPU_SINGLE_STEP 0x1 @@ -54,15 +56,9 @@ /* global register indexes */ static char cpu_reg_names[10*3 + 22*4 /* GPR */ + 10*4 + 22*5 /* SPE GPRh */ - + 10*4 + 22*5 /* FPR */ - + 2*(10*6 + 22*7) /* AVRh, AVRl */ - + 10*5 + 22*6 /* VSR */ + 8*5 /* CRF */]; static TCGv cpu_gpr[32]; static TCGv cpu_gprh[32]; -static TCGv_i64 cpu_fpr[32]; -static TCGv_i64 cpu_avrh[32], cpu_avrl[32]; -static TCGv_i64 cpu_vsr[32]; static TCGv_i32 cpu_crf[8]; static TCGv cpu_nip; static TCGv cpu_msr; @@ -107,39 +103,6 @@ void ppc_translate_init(void) 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(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(cpu_env, - offsetof(CPUPPCState, avr[i].u64[0]), p); -#else - cpu_avrh[i] = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUPPCState, avr[i].u64[1]), p); -#endif - p += (i < 10) ? 6 : 7; - cpu_reg_names_size -= (i < 10) ? 6 : 7; - - snprintf(p, cpu_reg_names_size, "avr%dL", i); -#ifdef HOST_WORDS_BIGENDIAN - 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(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(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(cpu_env, @@ -211,6 +174,7 @@ struct DisasContext { bool gtse; ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */ int singlestep_enabled; + uint32_t flags; uint64_t insns_flags; uint64_t insns_flags2; }; @@ -251,6 +215,17 @@ struct opc_handler_t { #endif }; +/* SPR load/store helpers */ +static inline void gen_load_spr(TCGv t, int reg) +{ + tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); +} + +static inline void gen_store_spr(int reg, TCGv t) +{ + tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); +} + static inline void gen_set_access_type(DisasContext *ctx, int access_type) { if (ctx->need_access_type && ctx->access_type != access_type) { @@ -313,6 +288,34 @@ static void gen_exception_nip(DisasContext *ctx, uint32_t excp, ctx->exception = (excp); } +/* + * Tells the caller what is the appropriate exception to generate and prepares + * SPR registers for this exception. + * + * The exception can be either POWERPC_EXCP_TRACE (on most PowerPCs) or + * POWERPC_EXCP_DEBUG (on BookE). + */ +static uint32_t gen_prep_dbgex(DisasContext *ctx) +{ + if (ctx->flags & POWERPC_FLAG_DE) { + target_ulong dbsr = 0; + if (ctx->singlestep_enabled & CPU_SINGLE_STEP) { + dbsr = DBCR0_ICMP; + } else { + /* Must have been branch */ + dbsr = DBCR0_BRT; + } + TCGv t0 = tcg_temp_new(); + gen_load_spr(t0, SPR_BOOKE_DBSR); + tcg_gen_ori_tl(t0, t0, dbsr); + gen_store_spr(SPR_BOOKE_DBSR, t0); + tcg_temp_free(t0); + return POWERPC_EXCP_DEBUG; + } else { + return POWERPC_EXCP_TRACE; + } +} + static void gen_debug_exception(DisasContext *ctx) { TCGv_i32 t0; @@ -575,17 +578,6 @@ typedef struct opcode_t { } #endif -/* SPR load/store helpers */ -static inline void gen_load_spr(TCGv t, int reg) -{ - tcg_gen_ld_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); -} - -static inline void gen_store_spr(int reg, TCGv t) -{ - tcg_gen_st_tl(t, cpu_env, offsetof(CPUPPCState, spr[reg])); -} - /* Invalid instruction */ static void gen_invalid(DisasContext *ctx) { @@ -815,7 +807,7 @@ static inline void gen_op_arith_compute_ov(DisasContext *ctx, TCGv arg0, static inline void gen_op_arith_compute_ca32(DisasContext *ctx, TCGv res, TCGv arg0, TCGv arg1, - int sub) + TCGv ca32, int sub) { TCGv t0; @@ -830,13 +822,14 @@ static inline void gen_op_arith_compute_ca32(DisasContext *ctx, tcg_gen_xor_tl(t0, arg0, arg1); } tcg_gen_xor_tl(t0, t0, res); - tcg_gen_extract_tl(cpu_ca32, t0, 32, 1); + tcg_gen_extract_tl(ca32, t0, 32, 1); tcg_temp_free(t0); } /* Common add function */ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, - TCGv arg2, bool add_ca, bool compute_ca, + TCGv arg2, TCGv ca, TCGv ca32, + bool add_ca, bool compute_ca, bool compute_ov, bool compute_rc0) { TCGv t0 = ret; @@ -854,29 +847,29 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_xor_tl(t1, arg1, arg2); /* add without carry */ tcg_gen_add_tl(t0, arg1, arg2); if (add_ca) { - tcg_gen_add_tl(t0, t0, cpu_ca); + tcg_gen_add_tl(t0, t0, ca); } - tcg_gen_xor_tl(cpu_ca, t0, t1); /* bits changed w/ carry */ + tcg_gen_xor_tl(ca, t0, t1); /* bits changed w/ carry */ tcg_temp_free(t1); - tcg_gen_extract_tl(cpu_ca, cpu_ca, 32, 1); + tcg_gen_extract_tl(ca, ca, 32, 1); if (is_isa300(ctx)) { - tcg_gen_mov_tl(cpu_ca32, cpu_ca); + tcg_gen_mov_tl(ca32, ca); } } else { TCGv zero = tcg_const_tl(0); if (add_ca) { - tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, cpu_ca, zero); - tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, arg2, zero); + tcg_gen_add2_tl(t0, ca, arg1, zero, ca, zero); + tcg_gen_add2_tl(t0, ca, t0, ca, arg2, zero); } else { - tcg_gen_add2_tl(t0, cpu_ca, arg1, zero, arg2, zero); + tcg_gen_add2_tl(t0, ca, arg1, zero, arg2, zero); } - gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, 0); + gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, ca32, 0); tcg_temp_free(zero); } } else { tcg_gen_add_tl(t0, arg1, arg2); if (add_ca) { - tcg_gen_add_tl(t0, t0, cpu_ca); + tcg_gen_add_tl(t0, t0, ca); } } @@ -893,40 +886,44 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, } } /* Add functions with two operands */ -#define GEN_INT_ARITH_ADD(name, opc3, add_ca, compute_ca, compute_ov) \ +#define GEN_INT_ARITH_ADD(name, opc3, ca, add_ca, compute_ca, compute_ov) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], cpu_gpr[rB(ctx->opcode)], \ + ca, glue(ca, 32), \ add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ } /* Add functions with one operand and one immediate */ -#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, \ +#define GEN_INT_ARITH_ADD_CONST(name, opc3, const_val, ca, \ add_ca, compute_ca, compute_ov) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ TCGv t0 = tcg_const_tl(const_val); \ gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], \ cpu_gpr[rA(ctx->opcode)], t0, \ + ca, glue(ca, 32), \ add_ca, compute_ca, compute_ov, Rc(ctx->opcode)); \ tcg_temp_free(t0); \ } /* add add. addo addo. */ -GEN_INT_ARITH_ADD(add, 0x08, 0, 0, 0) -GEN_INT_ARITH_ADD(addo, 0x18, 0, 0, 1) +GEN_INT_ARITH_ADD(add, 0x08, cpu_ca, 0, 0, 0) +GEN_INT_ARITH_ADD(addo, 0x18, cpu_ca, 0, 0, 1) /* addc addc. addco addco. */ -GEN_INT_ARITH_ADD(addc, 0x00, 0, 1, 0) -GEN_INT_ARITH_ADD(addco, 0x10, 0, 1, 1) +GEN_INT_ARITH_ADD(addc, 0x00, cpu_ca, 0, 1, 0) +GEN_INT_ARITH_ADD(addco, 0x10, cpu_ca, 0, 1, 1) /* adde adde. addeo addeo. */ -GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0) -GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1) +GEN_INT_ARITH_ADD(adde, 0x04, cpu_ca, 1, 1, 0) +GEN_INT_ARITH_ADD(addeo, 0x14, cpu_ca, 1, 1, 1) /* addme addme. addmeo addmeo. */ -GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0) -GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1) +GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, cpu_ca, 1, 1, 0) +GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, cpu_ca, 1, 1, 1) +/* addex */ +GEN_INT_ARITH_ADD(addex, 0x05, cpu_ov, 1, 1, 0); /* addze addze. addzeo addzeo.*/ -GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0) -GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1) +GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, cpu_ca, 1, 1, 0) +GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, cpu_ca, 1, 1, 1) /* addi */ static void gen_addi(DisasContext *ctx) { @@ -945,7 +942,7 @@ static inline void gen_op_addic(DisasContext *ctx, bool compute_rc0) { TCGv c = tcg_const_tl(SIMM(ctx->opcode)); gen_op_arith_add(ctx, cpu_gpr[rD(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], - c, 0, 1, 0, compute_rc0); + c, cpu_ca, cpu_ca32, 0, 1, 0, compute_rc0); tcg_temp_free(c); } @@ -1398,13 +1395,13 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, zero = tcg_const_tl(0); tcg_gen_add2_tl(t0, cpu_ca, arg2, zero, cpu_ca, zero); tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, inv1, zero); - gen_op_arith_compute_ca32(ctx, t0, inv1, arg2, 0); + gen_op_arith_compute_ca32(ctx, t0, inv1, arg2, cpu_ca32, 0); tcg_temp_free(zero); tcg_temp_free(inv1); } else { tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); tcg_gen_sub_tl(t0, arg2, arg1); - gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, 1); + gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, cpu_ca32, 1); } } else if (add_ca) { /* Since we're ignoring carry-out, we can simplify the @@ -2545,6 +2542,26 @@ GEN_LDS(lha, ld16s, 0x0A, PPC_INTEGER); GEN_LDS(lhz, ld16u, 0x08, PPC_INTEGER); /* lwz lwzu lwzux lwzx */ GEN_LDS(lwz, ld32u, 0x00, PPC_INTEGER); + +#define GEN_LDEPX(name, ldop, opc2, opc3) \ +static void glue(gen_, name##epx)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + CHK_SV; \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + tcg_gen_qemu_ld_tl(cpu_gpr[rD(ctx->opcode)], EA, PPC_TLB_EPID_LOAD, ldop);\ + tcg_temp_free(EA); \ +} + +GEN_LDEPX(lb, DEF_MEMOP(MO_UB), 0x1F, 0x02) +GEN_LDEPX(lh, DEF_MEMOP(MO_UW), 0x1F, 0x08) +GEN_LDEPX(lw, DEF_MEMOP(MO_UL), 0x1F, 0x00) +#if defined(TARGET_PPC64) +GEN_LDEPX(ld, DEF_MEMOP(MO_Q), 0x1D, 0x00) +#endif + #if defined(TARGET_PPC64) /* lwaux */ GEN_LDUX(lwa, ld32s, 0x15, 0x0B, PPC_64B); @@ -2621,22 +2638,22 @@ static void gen_lq(DisasContext *ctx) hi = cpu_gpr[rd]; if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { -#ifdef CONFIG_ATOMIC128 - TCGv_i32 oi = tcg_temp_new_i32(); - if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); - gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + if (HAVE_ATOMIC128) { + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + } + tcg_temp_free_i32(oi); + tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); - gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; } - tcg_temp_free_i32(oi); - tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); -#else - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; -#endif } else if (ctx->le_mode) { tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ); gen_addr_add(ctx, EA, EA, 8); @@ -2726,6 +2743,27 @@ GEN_STS(stb, st8, 0x06, PPC_INTEGER); GEN_STS(sth, st16, 0x0C, PPC_INTEGER); /* stw stwu stwux stwx */ GEN_STS(stw, st32, 0x04, PPC_INTEGER); + +#define GEN_STEPX(name, stop, opc2, opc3) \ +static void glue(gen_, name##epx)(DisasContext *ctx) \ +{ \ + TCGv EA; \ + CHK_SV; \ + gen_set_access_type(ctx, ACCESS_INT); \ + EA = tcg_temp_new(); \ + gen_addr_reg_index(ctx, EA); \ + tcg_gen_qemu_st_tl( \ + cpu_gpr[rD(ctx->opcode)], EA, PPC_TLB_EPID_STORE, stop); \ + tcg_temp_free(EA); \ +} + +GEN_STEPX(stb, DEF_MEMOP(MO_UB), 0x1F, 0x06) +GEN_STEPX(sth, DEF_MEMOP(MO_UW), 0x1F, 0x0C) +GEN_STEPX(stw, DEF_MEMOP(MO_UL), 0x1F, 0x04) +#if defined(TARGET_PPC64) +GEN_STEPX(std, DEF_MEMOP(MO_Q), 0x1d, 0x04) +#endif + #if defined(TARGET_PPC64) GEN_STUX(std, st64_i64, 0x15, 0x05, PPC_64B); GEN_STX(std, st64_i64, 0x15, 0x04, PPC_64B); @@ -2772,21 +2810,21 @@ static void gen_std(DisasContext *ctx) hi = cpu_gpr[rs]; if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { -#ifdef CONFIG_ATOMIC128 - TCGv_i32 oi = tcg_temp_new_i32(); - if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); - gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi); + if (HAVE_ATOMIC128) { + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ, ctx->mem_idx)); + gen_helper_stq_le_parallel(cpu_env, EA, lo, hi, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); + gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi); + } + tcg_temp_free_i32(oi); } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ, ctx->mem_idx)); - gen_helper_stq_be_parallel(cpu_env, EA, lo, hi, oi); + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; } - tcg_temp_free_i32(oi); -#else - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; -#endif } else if (ctx->le_mode) { tcg_gen_qemu_st_i64(lo, EA, ctx->mem_idx, MO_LEQ); gen_addr_add(ctx, EA, EA, 8); @@ -3371,26 +3409,26 @@ static void gen_lqarx(DisasContext *ctx) hi = cpu_gpr[rd]; if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { -#ifdef CONFIG_ATOMIC128 - TCGv_i32 oi = tcg_temp_new_i32(); - if (ctx->le_mode) { - tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16, - ctx->mem_idx)); - gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + if (HAVE_ATOMIC128) { + TCGv_i32 oi = tcg_temp_new_i32(); + if (ctx->le_mode) { + tcg_gen_movi_i32(oi, make_memop_idx(MO_LEQ | MO_ALIGN_16, + ctx->mem_idx)); + gen_helper_lq_le_parallel(lo, cpu_env, EA, oi); + } else { + tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16, + ctx->mem_idx)); + gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + } + tcg_temp_free_i32(oi); + tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); } else { - tcg_gen_movi_i32(oi, make_memop_idx(MO_BEQ | MO_ALIGN_16, - ctx->mem_idx)); - gen_helper_lq_be_parallel(lo, cpu_env, EA, oi); + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; + tcg_temp_free(EA); + return; } - tcg_temp_free_i32(oi); - tcg_gen_ld_i64(hi, cpu_env, offsetof(CPUPPCState, retxh)); -#else - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; - tcg_temp_free(EA); - return; -#endif } else if (ctx->le_mode) { tcg_gen_qemu_ld_i64(lo, EA, ctx->mem_idx, MO_LEQ | MO_ALIGN_16); tcg_gen_mov_tl(cpu_reserve, EA); @@ -3428,20 +3466,22 @@ static void gen_stqcx_(DisasContext *ctx) hi = cpu_gpr[rs]; if (tb_cflags(ctx->base.tb) & CF_PARALLEL) { - TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16); -#ifdef CONFIG_ATOMIC128 - if (ctx->le_mode) { - gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); + if (HAVE_CMPXCHG128) { + TCGv_i32 oi = tcg_const_i32(DEF_MEMOP(MO_Q) | MO_ALIGN_16); + if (ctx->le_mode) { + gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, + EA, lo, hi, oi); + } else { + gen_helper_stqcx_be_parallel(cpu_crf[0], cpu_env, + EA, lo, hi, oi); + } + tcg_temp_free_i32(oi); } else { - gen_helper_stqcx_le_parallel(cpu_crf[0], cpu_env, EA, lo, hi, oi); + /* Restart with exclusive lock. */ + gen_helper_exit_atomic(cpu_env); + ctx->base.is_jmp = DISAS_NORETURN; } -#else - /* Restart with exclusive lock. */ - gen_helper_exit_atomic(cpu_env); - ctx->base.is_jmp = DISAS_NORETURN; -#endif tcg_temp_free(EA); - tcg_temp_free_i32(oi); } else { TCGLabel *lab_fail = gen_new_label(); TCGLabel *lab_over = gen_new_label(); @@ -3526,7 +3566,8 @@ static void gen_doze(DisasContext *ctx) t = tcg_const_i32(PPC_PM_DOZE); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); - gen_stop_exception(ctx); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -3541,13 +3582,25 @@ static void gen_nap(DisasContext *ctx) t = tcg_const_i32(PPC_PM_NAP); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); - gen_stop_exception(ctx); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ } static void gen_stop(DisasContext *ctx) { - gen_nap(ctx); +#if defined(CONFIG_USER_ONLY) + GEN_PRIV; +#else + TCGv_i32 t; + + CHK_HV; + t = tcg_const_i32(PPC_PM_STOP); + gen_helper_pminsn(cpu_env, t); + tcg_temp_free_i32(t); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); +#endif /* defined(CONFIG_USER_ONLY) */ } static void gen_sleep(DisasContext *ctx) @@ -3561,7 +3614,8 @@ static void gen_sleep(DisasContext *ctx) t = tcg_const_i32(PPC_PM_SLEEP); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); - gen_stop_exception(ctx); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ } @@ -3576,7 +3630,8 @@ static void gen_rvwinkle(DisasContext *ctx) t = tcg_const_i32(PPC_PM_RVWINKLE); gen_helper_pminsn(cpu_env, t); tcg_temp_free_i32(t); - gen_stop_exception(ctx); + /* Stop translation, as the CPU is supposed to sleep from now */ + gen_exception_nip(ctx, EXCP_HLT, ctx->base.pc_next); #endif /* defined(CONFIG_USER_ONLY) */ } #endif /* #if defined(TARGET_PPC64) */ @@ -3602,6 +3657,22 @@ static inline bool use_goto_tb(DisasContext *ctx, target_ulong dest) #endif } +static void gen_lookup_and_goto_ptr(DisasContext *ctx) +{ + int sse = ctx->singlestep_enabled; + if (unlikely(sse)) { + if (sse & GDBSTUB_SINGLE_STEP) { + gen_debug_exception(ctx); + } else if (sse & (CPU_SINGLE_STEP | CPU_BRANCH_STEP)) { + uint32_t excp = gen_prep_dbgex(ctx); + gen_exception(ctx, excp); + } + tcg_gen_exit_tb(NULL, 0); + } else { + tcg_gen_lookup_and_goto_ptr(); + } +} + /*** Branch ***/ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) { @@ -3614,18 +3685,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) tcg_gen_exit_tb(ctx->base.tb, n); } else { tcg_gen_movi_tl(cpu_nip, dest & ~3); - if (unlikely(ctx->singlestep_enabled)) { - if ((ctx->singlestep_enabled & - (CPU_BRANCH_STEP | CPU_SINGLE_STEP)) && - (ctx->exception == POWERPC_EXCP_BRANCH || - ctx->exception == POWERPC_EXCP_TRACE)) { - gen_exception_nip(ctx, POWERPC_EXCP_TRACE, dest); - } - if (ctx->singlestep_enabled & GDBSTUB_SINGLE_STEP) { - gen_debug_exception(ctx); - } - } - tcg_gen_lookup_and_goto_ptr(); + gen_lookup_and_goto_ptr(ctx); } } @@ -3668,8 +3728,8 @@ static void gen_bcond(DisasContext *ctx, int type) uint32_t bo = BO(ctx->opcode); TCGLabel *l1; TCGv target; - ctx->exception = POWERPC_EXCP_BRANCH; + if (type == BCOND_LR || type == BCOND_CTR || type == BCOND_TAR) { target = tcg_temp_local_new(); if (type == BCOND_CTR) @@ -3687,20 +3747,52 @@ static void gen_bcond(DisasContext *ctx, int type) if ((bo & 0x4) == 0) { /* Decrement and test CTR */ TCGv temp = tcg_temp_new(); - if (unlikely(type == BCOND_CTR)) { - gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); - return; - } - tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1); - if (NARROW_MODE(ctx)) { - tcg_gen_ext32u_tl(temp, cpu_ctr); + + if (type == BCOND_CTR) { + /* + * All ISAs up to v3 describe this form of bcctr as invalid but + * some processors, ie. 64-bit server processors compliant with + * arch 2.x, do implement a "test and decrement" logic instead, + * as described in their respective UMs. This logic involves CTR + * to act as both the branch target and a counter, which makes + * it basically useless and thus never used in real code. + * + * This form was hence chosen to trigger extra micro-architectural + * side-effect on real HW needed for the Spectre v2 workaround. + * It is up to guests that implement such workaround, ie. linux, to + * use this form in a way it just triggers the side-effect without + * doing anything else harmful. + */ + if (unlikely(!is_book3s_arch2x(ctx))) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + tcg_temp_free(temp); + tcg_temp_free(target); + return; + } + + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(temp, cpu_ctr); + } else { + tcg_gen_mov_tl(temp, cpu_ctr); + } + if (bo & 0x2) { + tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1); + } else { + tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); + } + tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1); } else { - tcg_gen_mov_tl(temp, cpu_ctr); - } - if (bo & 0x2) { - tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1); - } else { - tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); + tcg_gen_subi_tl(cpu_ctr, cpu_ctr, 1); + if (NARROW_MODE(ctx)) { + tcg_gen_ext32u_tl(temp, cpu_ctr); + } else { + tcg_gen_mov_tl(temp, cpu_ctr); + } + if (bo & 0x2) { + tcg_gen_brcondi_tl(TCG_COND_NE, temp, 0, l1); + } else { + tcg_gen_brcondi_tl(TCG_COND_EQ, temp, 0, l1); + } } tcg_temp_free(temp); } @@ -3733,10 +3825,11 @@ static void gen_bcond(DisasContext *ctx, int type) } else { tcg_gen_andi_tl(cpu_nip, target, ~3); } - tcg_gen_lookup_and_goto_ptr(); + gen_lookup_and_goto_ptr(ctx); tcg_temp_free(target); } if ((bo & 0x14) != 0x14) { + /* fallthrough case */ gen_set_label(l1); gen_goto_tb(ctx, 1, ctx->base.pc_next); } @@ -3828,15 +3921,21 @@ static void gen_rfi(DisasContext *ctx) /* This instruction doesn't exist anymore on 64-bit server * processors compliant with arch 2.x */ - if (ctx->insns_flags & PPC_SEGMENT_64B) { + if (is_book3s_arch2x(ctx)) { gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); return; } /* Restore CPU state */ CHK_SV; + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_update_cfar(ctx, ctx->base.pc_next - 4); gen_helper_rfi(cpu_env); gen_sync_exception(ctx); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } #endif } @@ -3848,9 +3947,15 @@ static void gen_rfid(DisasContext *ctx) #else /* Restore CPU state */ CHK_SV; + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_update_cfar(ctx, ctx->base.pc_next - 4); gen_helper_rfid(cpu_env); gen_sync_exception(ctx); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } #endif } @@ -4213,11 +4318,17 @@ static void gen_mtmsrd(DisasContext *ctx) * if we enter power saving mode, we will exit the loop * directly from ppc_store_msr */ + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_update_nip(ctx, ctx->base.pc_next); gen_helper_store_msr(cpu_env, cpu_gpr[rS(ctx->opcode)]); /* Must stop the translation as machine state (may have) changed */ /* Note that mtmsr is not always defined as context-synchronizing */ gen_stop_exception(ctx); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } } #endif /* !defined(CONFIG_USER_ONLY) */ } @@ -4242,6 +4353,9 @@ static void gen_mtmsr(DisasContext *ctx) * if we enter power saving mode, we will exit the loop * directly from ppc_store_msr */ + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } gen_update_nip(ctx, ctx->base.pc_next); #if defined(TARGET_PPC64) tcg_gen_deposit_tl(msr, cpu_msr, cpu_gpr[rS(ctx->opcode)], 0, 32); @@ -4249,6 +4363,9 @@ static void gen_mtmsr(DisasContext *ctx) tcg_gen_mov_tl(msr, cpu_gpr[rS(ctx->opcode)]); #endif gen_helper_store_msr(cpu_env, msr); + if (tb_cflags(ctx->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); + } tcg_temp_free(msr); /* Must stop the translation as machine state (may have) changed */ /* Note that mtmsr is not always defined as context-synchronizing */ @@ -4348,6 +4465,19 @@ static void gen_dcbf(DisasContext *ctx) tcg_temp_free(t0); } +/* dcbfep (external PID dcbf) */ +static void gen_dcbfep(DisasContext *ctx) +{ + /* XXX: specification says this is treated as a load by the MMU */ + TCGv t0; + CHK_SV; + gen_set_access_type(ctx, ACCESS_CACHE); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + tcg_gen_qemu_ld_tl(t0, t0, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UB)); + tcg_temp_free(t0); +} + /* dcbi (Supervisor only) */ static void gen_dcbi(DisasContext *ctx) { @@ -4381,6 +4511,18 @@ static void gen_dcbst(DisasContext *ctx) tcg_temp_free(t0); } +/* dcbstep (dcbstep External PID version) */ +static void gen_dcbstep(DisasContext *ctx) +{ + /* XXX: specification say this is treated as a load by the MMU */ + TCGv t0; + gen_set_access_type(ctx, ACCESS_CACHE); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + tcg_gen_qemu_ld_tl(t0, t0, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_UB)); + tcg_temp_free(t0); +} + /* dcbt */ static void gen_dcbt(DisasContext *ctx) { @@ -4390,6 +4532,15 @@ static void gen_dcbt(DisasContext *ctx) */ } +/* dcbtep */ +static void gen_dcbtep(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a load by the MMU + * but does not generate any exception + */ +} + /* dcbtst */ static void gen_dcbtst(DisasContext *ctx) { @@ -4399,6 +4550,15 @@ static void gen_dcbtst(DisasContext *ctx) */ } +/* dcbtstep */ +static void gen_dcbtstep(DisasContext *ctx) +{ + /* interpreted as no-op */ + /* XXX: specification say this is treated as a load by the MMU + * but does not generate any exception + */ +} + /* dcbtls */ static void gen_dcbtls(DisasContext *ctx) { @@ -4425,6 +4585,21 @@ static void gen_dcbz(DisasContext *ctx) tcg_temp_free_i32(tcgv_op); } +/* dcbzep */ +static void gen_dcbzep(DisasContext *ctx) +{ + TCGv tcgv_addr; + TCGv_i32 tcgv_op; + + gen_set_access_type(ctx, ACCESS_CACHE); + tcgv_addr = tcg_temp_new(); + tcgv_op = tcg_const_i32(ctx->opcode & 0x03FF000); + gen_addr_reg_index(ctx, tcgv_addr); + gen_helper_dcbzep(cpu_env, tcgv_addr, tcgv_op); + tcg_temp_free(tcgv_addr); + tcg_temp_free_i32(tcgv_op); +} + /* dst / dstt */ static void gen_dst(DisasContext *ctx) { @@ -4463,6 +4638,17 @@ static void gen_icbi(DisasContext *ctx) tcg_temp_free(t0); } +/* icbiep */ +static void gen_icbiep(DisasContext *ctx) +{ + TCGv t0; + gen_set_access_type(ctx, ACCESS_CACHE); + t0 = tcg_temp_new(); + gen_addr_reg_index(ctx, t0); + gen_helper_icbiep(cpu_env, t0); + tcg_temp_free(t0); +} + /* Optional: */ /* dcba */ static void gen_dcba(DisasContext *ctx) @@ -6332,7 +6518,12 @@ static void gen_mbar(DisasContext *ctx) /* msync replaces sync on 440 */ static void gen_msync_4xx(DisasContext *ctx) { - /* interpreted as no-op */ + /* Only e500 seems to treat reserved bits as invalid */ + if ((ctx->insns_flags2 & PPC2_BOOKE206) && + (ctx->opcode & 0x03FFF801)) { + gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); + } + /* otherwise interpreted as no-op */ } /* icbt */ @@ -6352,8 +6543,7 @@ static void gen_msgclr(DisasContext *ctx) GEN_PRIV; #else CHK_HV; - /* 64-bit server processors compliant with arch 2.x */ - if (ctx->insns_flags & PPC_SEGMENT_64B) { + if (is_book3s_arch2x(ctx)) { gen_helper_book3s_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); } else { gen_helper_msgclr(cpu_env, cpu_gpr[rB(ctx->opcode)]); @@ -6367,8 +6557,7 @@ static void gen_msgsnd(DisasContext *ctx) GEN_PRIV; #else CHK_HV; - /* 64-bit server processors compliant with arch 2.x */ - if (ctx->insns_flags & PPC_SEGMENT_64B) { + if (is_book3s_arch2x(ctx)) { gen_helper_book3s_msgsnd(cpu_gpr[rB(ctx->opcode)]); } else { gen_helper_msgsnd(cpu_gpr[rB(ctx->opcode)]); @@ -6516,6 +6705,26 @@ static inline void gen_##name(DisasContext *ctx) \ GEN_TM_PRIV_NOOP(treclaim); GEN_TM_PRIV_NOOP(trechkpt); +static inline void get_fpr(TCGv_i64 dst, int regno) +{ + tcg_gen_ld_i64(dst, cpu_env, fpr_offset(regno)); +} + +static inline void set_fpr(int regno, TCGv_i64 src) +{ + tcg_gen_st_i64(src, cpu_env, fpr_offset(regno)); +} + +static inline void get_avr64(TCGv_i64 dst, int regno, bool high) +{ + tcg_gen_ld_i64(dst, cpu_env, avr64_offset(regno, high)); +} + +static inline void set_avr64(int regno, TCGv_i64 src, bool high) +{ + tcg_gen_st_i64(src, cpu_env, avr64_offset(regno, high)); +} + #include "translate/fp-impl.inc.c" #include "translate/vmx-impl.inc.c" @@ -6730,16 +6939,22 @@ GEN_HANDLER_E(mcrxrx, 0x1F, 0x00, 0x12, 0x007FF801, PPC_NONE, PPC2_ISA300), GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001EF801, PPC_MISC), GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000000, PPC_MISC), GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03C00001, PPC_CACHE), +GEN_HANDLER_E(dcbfep, 0x1F, 0x1F, 0x03, 0x03C00001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE), GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE), +GEN_HANDLER_E(dcbstep, 0x1F, 0x1F, 0x01, 0x03E00001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x00000001, PPC_CACHE), +GEN_HANDLER_E(dcbtep, 0x1F, 0x1F, 0x09, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x00000001, PPC_CACHE), +GEN_HANDLER_E(dcbtstep, 0x1F, 0x1F, 0x07, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(dcbtls, 0x1F, 0x06, 0x05, 0x02000001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03C00001, PPC_CACHE_DCBZ), +GEN_HANDLER_E(dcbzep, 0x1F, 0x1F, 0x1F, 0x03C00001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dst, 0x1F, 0x16, 0x0A, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dstst, 0x1F, 0x16, 0x0B, 0x01800001, PPC_ALTIVEC), GEN_HANDLER(dss, 0x1F, 0x16, 0x19, 0x019FF801, PPC_ALTIVEC), GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE_ICBI), +GEN_HANDLER_E(icbiep, 0x1F, 0x1F, 0x1E, 0x03E00001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_DCBA), GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT), GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT), @@ -6872,11 +7087,11 @@ GEN_HANDLER(wrteei, 0x1F, 0x03, 0x05, 0x000E7C01, PPC_WRTEE), GEN_HANDLER(dlmzb, 0x1F, 0x0E, 0x02, 0x00000000, PPC_440_SPEC), GEN_HANDLER_E(mbar, 0x1F, 0x16, 0x1a, 0x001FF801, PPC_BOOKE, PPC2_BOOKE206), -GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x03FFF801, PPC_BOOKE), +GEN_HANDLER(msync_4xx, 0x1F, 0x16, 0x12, 0x039FF801, PPC_BOOKE), GEN_HANDLER2_E(icbt_440, "icbt", 0x1F, 0x16, 0x00, 0x03E00001, PPC_BOOKE, PPC2_BOOKE206), GEN_HANDLER2(icbt_440, "icbt", 0x1F, 0x06, 0x08, 0x03E00001, - PPC_440_SPEC), + PPC_440_SPEC), GEN_HANDLER(lvsl, 0x1f, 0x06, 0x00, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(lvsr, 0x1f, 0x06, 0x01, 0x00000001, PPC_ALTIVEC), GEN_HANDLER(mfvscr, 0x04, 0x2, 0x18, 0x001ff800, PPC_ALTIVEC), @@ -6903,6 +7118,7 @@ GEN_INT_ARITH_ADD(adde, 0x04, 1, 1, 0) GEN_INT_ARITH_ADD(addeo, 0x14, 1, 1, 1) GEN_INT_ARITH_ADD_CONST(addme, 0x07, -1LL, 1, 1, 0) GEN_INT_ARITH_ADD_CONST(addmeo, 0x17, -1LL, 1, 1, 1) +GEN_HANDLER_E(addex, 0x1F, 0x0A, 0x05, 0x00000000, PPC_NONE, PPC2_ISA300), GEN_INT_ARITH_ADD_CONST(addze, 0x06, 0, 1, 1, 0) GEN_INT_ARITH_ADD_CONST(addzeo, 0x16, 0, 1, 1, 1) @@ -7042,6 +7258,19 @@ GEN_LDX_HVRM(lbzcix, ld8u, 0x15, 0x1a, PPC_CILDST) GEN_LDX(lhbr, ld16ur, 0x16, 0x18, PPC_INTEGER) GEN_LDX(lwbr, ld32ur, 0x16, 0x10, PPC_INTEGER) +/* External PID based load */ +#undef GEN_LDEPX +#define GEN_LDEPX(name, ldop, opc2, opc3) \ +GEN_HANDLER_E(name##epx, 0x1F, opc2, opc3, \ + 0x00000001, PPC_NONE, PPC2_BOOKE206), + +GEN_LDEPX(lb, DEF_MEMOP(MO_UB), 0x1F, 0x02) +GEN_LDEPX(lh, DEF_MEMOP(MO_UW), 0x1F, 0x08) +GEN_LDEPX(lw, DEF_MEMOP(MO_UL), 0x1F, 0x00) +#if defined(TARGET_PPC64) +GEN_LDEPX(ld, DEF_MEMOP(MO_Q), 0x1D, 0x00) +#endif + #undef GEN_ST #undef GEN_STU #undef GEN_STUX @@ -7076,6 +7305,18 @@ GEN_STX_HVRM(stbcix, st8, 0x15, 0x1e, PPC_CILDST) GEN_STX(sthbr, st16r, 0x16, 0x1C, PPC_INTEGER) GEN_STX(stwbr, st32r, 0x16, 0x14, PPC_INTEGER) +#undef GEN_STEPX +#define GEN_STEPX(name, ldop, opc2, opc3) \ +GEN_HANDLER_E(name##epx, 0x1F, opc2, opc3, \ + 0x00000001, PPC_NONE, PPC2_BOOKE206), + +GEN_STEPX(stb, DEF_MEMOP(MO_UB), 0x1F, 0x06) +GEN_STEPX(sth, DEF_MEMOP(MO_UW), 0x1F, 0x0C) +GEN_STEPX(stw, DEF_MEMOP(MO_UL), 0x1F, 0x04) +#if defined(TARGET_PPC64) +GEN_STEPX(std, DEF_MEMOP(MO_Q), 0x1D, 0x04) +#endif + #undef GEN_CRLOGIC #define GEN_CRLOGIC(name, tcg_op, opc) \ GEN_HANDLER(name, 0x13, 0x01, opc, 0x00000001, PPC_INTEGER) @@ -7194,7 +7435,7 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, #if !defined(NO_TIMER_DUMP) cpu_fprintf(f, "TB %08" PRIu32 " %08" PRIu64 #if !defined(CONFIG_USER_ONLY) - " DECR %08" PRIu32 + " DECR " TARGET_FMT_lu #endif "\n", cpu_ppc_load_tbu(env), cpu_ppc_load_tbl(env) @@ -7232,7 +7473,7 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, if ((i & (RFPL - 1)) == 0) { cpu_fprintf(f, "FPR%02d", i); } - cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i])); + cpu_fprintf(f, " %016" PRIx64, *cpu_fpr_ptr(env, i)); if ((i & (RFPL - 1)) == (RFPL - 1)) { cpu_fprintf(f, "\n"); } @@ -7258,7 +7499,8 @@ void ppc_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, #if defined(TARGET_PPC64) if (env->excp_model == POWERPC_EXCP_POWER7 || - env->excp_model == POWERPC_EXCP_POWER8) { + env->excp_model == POWERPC_EXCP_POWER8 || + env->excp_model == POWERPC_EXCP_POWER9) { cpu_fprintf(f, "HSRR0 " TARGET_FMT_lx " HSRR1 " TARGET_FMT_lx "\n", env->spr[SPR_HSRR0], env->spr[SPR_HSRR1]); } @@ -7419,6 +7661,7 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->need_access_type = !(env->mmu_model & POWERPC_MMU_64B); ctx->le_mode = !!(env->hflags & (1 << MSR_LE)); ctx->default_tcg_memop_mask = ctx->le_mode ? MO_LE : MO_BE; + ctx->flags = env->flags; #if defined(TARGET_PPC64) ctx->sf_mode = msr_is_64bit(env, env->msr); ctx->has_cfar = !!(env->flags & POWERPC_FLAG_CFAR); @@ -7455,6 +7698,17 @@ static void ppc_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->singlestep_enabled = 0; if ((env->flags & POWERPC_FLAG_BE) && msr_be) ctx->singlestep_enabled |= CPU_BRANCH_STEP; + if ((env->flags & POWERPC_FLAG_DE) && msr_de) { + ctx->singlestep_enabled = 0; + target_ulong dbcr0 = env->spr[SPR_BOOKE_DBCR0]; + if (dbcr0 & DBCR0_ICMP) { + ctx->singlestep_enabled |= CPU_SINGLE_STEP; + } + if (dbcr0 & DBCR0_BRT) { + ctx->singlestep_enabled |= CPU_BRANCH_STEP; + } + + } if (unlikely(ctx->base.singlestep_enabled)) { ctx->singlestep_enabled |= GDBSTUB_SINGLE_STEP; } @@ -7565,7 +7819,8 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs) ctx->exception != POWERPC_SYSCALL && ctx->exception != POWERPC_EXCP_TRAP && ctx->exception != POWERPC_EXCP_BRANCH)) { - gen_exception_nip(ctx, POWERPC_EXCP_TRACE, ctx->base.pc_next); + uint32_t excp = gen_prep_dbgex(ctx); + gen_exception_nip(ctx, excp, ctx->base.pc_next); } if (tcg_check_temp_count()) { diff --git a/target/ppc/translate/dfp-impl.inc.c b/target/ppc/translate/dfp-impl.inc.c index 634ef73b8a..6c556dc2e1 100644 --- a/target/ppc/translate/dfp-impl.inc.c +++ b/target/ppc/translate/dfp-impl.inc.c @@ -3,7 +3,7 @@ static inline TCGv_ptr gen_fprp_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, fpr[reg])); + tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, vsr[reg].u64[0])); return r; } diff --git a/target/ppc/translate/fp-impl.inc.c b/target/ppc/translate/fp-impl.inc.c index 2fbd4d4f38..0f21a4e477 100644 --- a/target/ppc/translate/fp-impl.inc.c +++ b/target/ppc/translate/fp-impl.inc.c @@ -34,24 +34,37 @@ static void gen_set_cr1_from_fpscr(DisasContext *ctx) #define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ { \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ + TCGv_i64 t2; \ + TCGv_i64 t3; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + t2 = tcg_temp_new_i64(); \ + t3 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ - gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rA(ctx->opcode)], \ - cpu_fpr[rC(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); \ + get_fpr(t0, rA(ctx->opcode)); \ + get_fpr(t1, rC(ctx->opcode)); \ + get_fpr(t2, rB(ctx->opcode)); \ + gen_helper_f##op(t3, cpu_env, t0, t1, t2); \ if (isfloat) { \ - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rD(ctx->opcode)]); \ + gen_helper_frsp(t3, cpu_env, t3); \ } \ + set_fpr(rD(ctx->opcode), t3); \ if (set_fprf) { \ - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(t3); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ + tcg_temp_free_i64(t2); \ + tcg_temp_free_i64(t3); \ } #define GEN_FLOAT_ACB(name, op2, set_fprf, type) \ @@ -61,24 +74,33 @@ _GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type); #define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ { \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ + TCGv_i64 t2; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + t2 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ - gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rA(ctx->opcode)], \ - cpu_fpr[rB(ctx->opcode)]); \ + get_fpr(t0, rA(ctx->opcode)); \ + get_fpr(t1, rB(ctx->opcode)); \ + gen_helper_f##op(t2, cpu_env, t0, t1); \ if (isfloat) { \ - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rD(ctx->opcode)]); \ + gen_helper_frsp(t2, cpu_env, t2); \ } \ + set_fpr(rD(ctx->opcode), t2); \ if (set_fprf) { \ - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(t2); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ + tcg_temp_free_i64(t2); \ } #define GEN_FLOAT_AB(name, op2, inval, set_fprf, type) \ _GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ @@ -87,24 +109,33 @@ _GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type); #define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ { \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ + TCGv_i64 t2; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + t2 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ - gen_helper_f##op(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rA(ctx->opcode)], \ - cpu_fpr[rC(ctx->opcode)]); \ + get_fpr(t0, rA(ctx->opcode)); \ + get_fpr(t1, rC(ctx->opcode)); \ + gen_helper_f##op(t2, cpu_env, t0, t1); \ if (isfloat) { \ - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rD(ctx->opcode)]); \ + gen_helper_frsp(t2, cpu_env, t2); \ } \ + set_fpr(rD(ctx->opcode), t2); \ if (set_fprf) { \ - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(t2); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ + tcg_temp_free_i64(t2); \ } #define GEN_FLOAT_AC(name, op2, inval, set_fprf, type) \ _GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type); \ @@ -113,37 +144,51 @@ _GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type); #define GEN_FLOAT_B(name, op2, op3, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ { \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ - gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rB(ctx->opcode)]); \ + get_fpr(t0, rB(ctx->opcode)); \ + gen_helper_f##name(t1, cpu_env, t0); \ + set_fpr(rD(ctx->opcode), t1); \ if (set_fprf) { \ - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(t1); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ } #define GEN_FLOAT_BS(name, op1, op2, set_fprf, type) \ static void gen_f##name(DisasContext *ctx) \ { \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ gen_reset_fpstatus(); \ - gen_helper_f##name(cpu_fpr[rD(ctx->opcode)], cpu_env, \ - cpu_fpr[rB(ctx->opcode)]); \ + get_fpr(t0, rB(ctx->opcode)); \ + gen_helper_f##name(t1, cpu_env, t0); \ + set_fpr(rD(ctx->opcode), t1); \ if (set_fprf) { \ - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); \ + gen_compute_fprf_float64(t1); \ } \ if (unlikely(Rc(ctx->opcode) != 0)) { \ gen_set_cr1_from_fpscr(ctx); \ } \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ } /* fadd - fadds */ @@ -165,19 +210,25 @@ GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE); /* frsqrtes */ static void gen_frsqrtes(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - gen_helper_frsqrte(cpu_fpr[rD(ctx->opcode)], cpu_env, - cpu_fpr[rB(ctx->opcode)]); - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, - cpu_fpr[rD(ctx->opcode)]); - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); + get_fpr(t0, rB(ctx->opcode)); + gen_helper_frsqrte(t1, cpu_env, t0); + gen_helper_frsp(t1, cpu_env, t1); + set_fpr(rD(ctx->opcode), t1); + gen_compute_fprf_float64(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /* fsel */ @@ -189,34 +240,47 @@ GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT); /* fsqrt */ static void gen_fsqrt(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, - cpu_fpr[rB(ctx->opcode)]); - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); + get_fpr(t0, rB(ctx->opcode)); + gen_helper_fsqrt(t1, cpu_env, t0); + set_fpr(rD(ctx->opcode), t1); + gen_compute_fprf_float64(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } static void gen_fsqrts(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); - gen_helper_fsqrt(cpu_fpr[rD(ctx->opcode)], cpu_env, - cpu_fpr[rB(ctx->opcode)]); - gen_helper_frsp(cpu_fpr[rD(ctx->opcode)], cpu_env, - cpu_fpr[rD(ctx->opcode)]); - gen_compute_fprf_float64(cpu_fpr[rD(ctx->opcode)]); + get_fpr(t0, rB(ctx->opcode)); + gen_helper_fsqrt(t1, cpu_env, t0); + gen_helper_frsp(t1, cpu_env, t1); + set_fpr(rD(ctx->opcode), t1); + gen_compute_fprf_float64(t1); if (unlikely(Rc(ctx->opcode) != 0)) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /*** Floating-Point multiply-and-add ***/ @@ -268,21 +332,32 @@ GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT); static void gen_ftdiv(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], - cpu_fpr[rB(ctx->opcode)]); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + get_fpr(t0, rA(ctx->opcode)); + get_fpr(t1, rB(ctx->opcode)); + gen_helper_ftdiv(cpu_crf[crfD(ctx->opcode)], t0, t1); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } static void gen_ftsqrt(DisasContext *ctx) { + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); + t0 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + gen_helper_ftsqrt(cpu_crf[crfD(ctx->opcode)], t0); + tcg_temp_free_i64(t0); } @@ -293,32 +368,46 @@ static void gen_ftsqrt(DisasContext *ctx) static void gen_fcmpo(DisasContext *ctx) { TCGv_i32 crf; + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); crf = tcg_const_i32(crfD(ctx->opcode)); - gen_helper_fcmpo(cpu_env, cpu_fpr[rA(ctx->opcode)], - cpu_fpr[rB(ctx->opcode)], crf); + get_fpr(t0, rA(ctx->opcode)); + get_fpr(t1, rB(ctx->opcode)); + gen_helper_fcmpo(cpu_env, t0, t1, crf); tcg_temp_free_i32(crf); gen_helper_float_check_status(cpu_env); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /* fcmpu */ static void gen_fcmpu(DisasContext *ctx) { TCGv_i32 crf; + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_reset_fpstatus(); crf = tcg_const_i32(crfD(ctx->opcode)); - gen_helper_fcmpu(cpu_env, cpu_fpr[rA(ctx->opcode)], - cpu_fpr[rB(ctx->opcode)], crf); + get_fpr(t0, rA(ctx->opcode)); + get_fpr(t1, rB(ctx->opcode)); + gen_helper_fcmpu(cpu_env, t0, t1, crf); tcg_temp_free_i32(crf); gen_helper_float_check_status(cpu_env); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /*** Floating-point move ***/ @@ -326,100 +415,153 @@ static void gen_fcmpu(DisasContext *ctx) /* XXX: beware that fabs never checks for NaNs nor update FPSCR */ static void gen_fabs(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - tcg_gen_andi_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], - ~(1ULL << 63)); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + tcg_gen_andi_i64(t1, t0, ~(1ULL << 63)); + set_fpr(rD(ctx->opcode), t1); if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /* fmr - fmr. */ /* XXX: beware that fmr never checks for NaNs nor update FPSCR */ static void gen_fmr(DisasContext *ctx) { + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - tcg_gen_mov_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)]); + t0 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + set_fpr(rD(ctx->opcode), t0); if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); } /* fnabs */ /* XXX: beware that fnabs never checks for NaNs nor update FPSCR */ static void gen_fnabs(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - tcg_gen_ori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], - 1ULL << 63); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + tcg_gen_ori_i64(t1, t0, 1ULL << 63); + set_fpr(rD(ctx->opcode), t1); if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /* fneg */ /* XXX: beware that fneg never checks for NaNs nor update FPSCR */ static void gen_fneg(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - tcg_gen_xori_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rB(ctx->opcode)], - 1ULL << 63); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + tcg_gen_xori_i64(t1, t0, 1ULL << 63); + set_fpr(rD(ctx->opcode), t1); if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } /* fcpsgn: PowerPC 2.05 specification */ /* XXX: beware that fcpsgn never checks for NaNs nor update FPSCR */ static void gen_fcpsgn(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; + TCGv_i64 t2; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], - cpu_fpr[rB(ctx->opcode)], 0, 63); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + get_fpr(t0, rA(ctx->opcode)); + get_fpr(t1, rB(ctx->opcode)); + tcg_gen_deposit_i64(t2, t0, t1, 0, 63); + set_fpr(rD(ctx->opcode), t2); if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); } static void gen_fmrgew(DisasContext *ctx) { TCGv_i64 b0; + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } b0 = tcg_temp_new_i64(); - tcg_gen_shri_i64(b0, cpu_fpr[rB(ctx->opcode)], 32); - tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpr[rA(ctx->opcode)], - b0, 0, 32); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + tcg_gen_shri_i64(b0, t0, 32); + get_fpr(t0, rA(ctx->opcode)); + tcg_gen_deposit_i64(t1, t0, b0, 0, 32); + set_fpr(rD(ctx->opcode), t1); tcg_temp_free_i64(b0); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } static void gen_fmrgow(DisasContext *ctx) { + TCGv_i64 t0; + TCGv_i64 t1; + TCGv_i64 t2; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } - tcg_gen_deposit_i64(cpu_fpr[rD(ctx->opcode)], - cpu_fpr[rB(ctx->opcode)], - cpu_fpr[rA(ctx->opcode)], - 32, 32); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + t2 = tcg_temp_new_i64(); + get_fpr(t0, rB(ctx->opcode)); + get_fpr(t1, rA(ctx->opcode)); + tcg_gen_deposit_i64(t2, t0, t1, 32, 32); + set_fpr(rD(ctx->opcode), t2); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); } /*** Floating-Point status & ctrl register ***/ @@ -458,15 +600,19 @@ static void gen_mcrfs(DisasContext *ctx) /* mffs */ static void gen_mffs(DisasContext *ctx) { + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } + t0 = tcg_temp_new_i64(); gen_reset_fpstatus(); - tcg_gen_extu_tl_i64(cpu_fpr[rD(ctx->opcode)], cpu_fpscr); + tcg_gen_extu_tl_i64(t0, cpu_fpscr); + set_fpr(rD(ctx->opcode), t0); if (unlikely(Rc(ctx->opcode))) { gen_set_cr1_from_fpscr(ctx); } + tcg_temp_free_i64(t0); } /* mtfsb0 */ @@ -522,6 +668,7 @@ static void gen_mtfsb1(DisasContext *ctx) static void gen_mtfsf(DisasContext *ctx) { TCGv_i32 t0; + TCGv_i64 t1; int flm, l, w; if (unlikely(!ctx->fpu_enabled)) { @@ -541,7 +688,9 @@ static void gen_mtfsf(DisasContext *ctx) } else { t0 = tcg_const_i32(flm << (w * 8)); } - gen_helper_store_fpscr(cpu_env, cpu_fpr[rB(ctx->opcode)], t0); + t1 = tcg_temp_new_i64(); + get_fpr(t1, rB(ctx->opcode)); + gen_helper_store_fpscr(cpu_env, t1, t0); tcg_temp_free_i32(t0); if (unlikely(Rc(ctx->opcode) != 0)) { tcg_gen_trunc_tl_i32(cpu_crf[1], cpu_fpscr); @@ -549,6 +698,7 @@ static void gen_mtfsf(DisasContext *ctx) } /* We can raise a differed exception */ gen_helper_float_check_status(cpu_env); + tcg_temp_free_i64(t1); } /* mtfsfi */ @@ -588,21 +738,26 @@ static void gen_mtfsfi(DisasContext *ctx) static void glue(gen_, name)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_imm_index(ctx, EA, 0); \ - gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + gen_qemu_##ldop(ctx, t0, EA); \ + set_fpr(rD(ctx->opcode), t0); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_LDUF(name, ldop, opc, type) \ static void glue(gen_, name##u)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ @@ -613,20 +768,25 @@ static void glue(gen_, name##u)(DisasContext *ctx) } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_imm_index(ctx, EA, 0); \ - gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + gen_qemu_##ldop(ctx, t0, EA); \ + set_fpr(rD(ctx->opcode), t0); \ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_LDUXF(name, ldop, opc, type) \ static void glue(gen_, name##ux)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ if (unlikely(rA(ctx->opcode) == 0)) { \ gen_inval_exception(ctx, POWERPC_EXCP_INVAL_INVAL); \ return; \ @@ -634,24 +794,30 @@ static void glue(gen_, name##ux)(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ - gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + gen_qemu_##ldop(ctx, t0, EA); \ + set_fpr(rD(ctx->opcode), t0); \ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_LDXF(name, ldop, opc2, opc3, type) \ static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_reg_index(ctx, EA); \ - gen_qemu_##ldop(ctx, cpu_fpr[rD(ctx->opcode)], EA); \ + gen_qemu_##ldop(ctx, t0, EA); \ + set_fpr(rD(ctx->opcode), t0); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_LDFS(name, ldop, op, type) \ @@ -660,15 +826,12 @@ GEN_LDUF(name, ldop, op | 0x21, type); \ GEN_LDUXF(name, ldop, op | 0x01, type); \ GEN_LDXF(name, ldop, 0x17, op | 0x00, type) -static inline void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +static void gen_qemu_ld32fs(DisasContext *ctx, TCGv_i64 dest, TCGv addr) { - TCGv t0 = tcg_temp_new(); - TCGv_i32 t1 = tcg_temp_new_i32(); - gen_qemu_ld32u(ctx, t0, arg2); - tcg_gen_trunc_tl_i32(t1, t0); - tcg_temp_free(t0); - gen_helper_float32_to_float64(arg1, cpu_env, t1); - tcg_temp_free_i32(t1); + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_qemu_ld_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL)); + gen_helper_todouble(dest, tmp); + tcg_temp_free_i32(tmp); } /* lfd lfdu lfdux lfdx */ @@ -676,10 +839,31 @@ GEN_LDFS(lfd, ld64_i64, 0x12, PPC_FLOAT); /* lfs lfsu lfsux lfsx */ GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT); +/* lfdepx (external PID lfdx) */ +static void gen_lfdepx(DisasContext *ctx) +{ + TCGv EA; + TCGv_i64 t0; + CHK_SV; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + t0 = tcg_temp_new_i64(); + gen_addr_reg_index(ctx, EA); + tcg_gen_qemu_ld_i64(t0, EA, PPC_TLB_EPID_LOAD, DEF_MEMOP(MO_Q)); + set_fpr(rD(ctx->opcode), t0); + tcg_temp_free(EA); + tcg_temp_free_i64(t0); +} + /* lfdp */ static void gen_lfdp(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; @@ -687,24 +871,31 @@ static void gen_lfdp(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_FLOAT); EA = tcg_temp_new(); gen_addr_imm_index(ctx, EA, 0); + t0 = tcg_temp_new_i64(); /* We only need to swap high and low halves. gen_qemu_ld64_i64 does necessary 64-bit byteswap already. */ if (unlikely(ctx->le_mode)) { - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode) + 1, t0); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode), t0); } else { - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode), t0); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode) + 1, t0); } tcg_temp_free(EA); + tcg_temp_free_i64(t0); } /* lfdpx */ static void gen_lfdpx(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; @@ -712,18 +903,24 @@ static void gen_lfdpx(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_FLOAT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); + t0 = tcg_temp_new_i64(); /* We only need to swap high and low halves. gen_qemu_ld64_i64 does necessary 64-bit byteswap already. */ if (unlikely(ctx->le_mode)) { - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode) + 1, t0); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode), t0); } else { - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode), t0); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode) + 1, t0); } tcg_temp_free(EA); + tcg_temp_free_i64(t0); } /* lfiwax */ @@ -731,6 +928,7 @@ static void gen_lfiwax(DisasContext *ctx) { TCGv EA; TCGv t0; + TCGv_i64 t1; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; @@ -738,47 +936,59 @@ static void gen_lfiwax(DisasContext *ctx) gen_set_access_type(ctx, ACCESS_FLOAT); EA = tcg_temp_new(); t0 = tcg_temp_new(); + t1 = tcg_temp_new_i64(); gen_addr_reg_index(ctx, EA); gen_qemu_ld32s(ctx, t0, EA); - tcg_gen_ext_tl_i64(cpu_fpr[rD(ctx->opcode)], t0); + tcg_gen_ext_tl_i64(t1, t0); + set_fpr(rD(ctx->opcode), t1); tcg_temp_free(EA); tcg_temp_free(t0); + tcg_temp_free_i64(t1); } /* lfiwzx */ static void gen_lfiwzx(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } gen_set_access_type(ctx, ACCESS_FLOAT); EA = tcg_temp_new(); + t0 = tcg_temp_new_i64(); gen_addr_reg_index(ctx, EA); - gen_qemu_ld32u_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + gen_qemu_ld32u_i64(ctx, t0, EA); + set_fpr(rD(ctx->opcode), t0); tcg_temp_free(EA); + tcg_temp_free_i64(t0); } /*** Floating-point store ***/ #define GEN_STF(name, stop, opc, type) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_imm_index(ctx, EA, 0); \ - gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + get_fpr(t0, rS(ctx->opcode)); \ + gen_qemu_##stop(ctx, t0, EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_STUF(name, stop, opc, type) \ static void glue(gen_, name##u)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ @@ -789,16 +999,20 @@ static void glue(gen_, name##u)(DisasContext *ctx) } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_imm_index(ctx, EA, 0); \ - gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + get_fpr(t0, rS(ctx->opcode)); \ + gen_qemu_##stop(ctx, t0, EA); \ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_STUXF(name, stop, opc, type) \ static void glue(gen_, name##ux)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ @@ -809,25 +1023,32 @@ static void glue(gen_, name##ux)(DisasContext *ctx) } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_reg_index(ctx, EA); \ - gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + get_fpr(t0, rS(ctx->opcode)); \ + gen_qemu_##stop(ctx, t0, EA); \ tcg_gen_mov_tl(cpu_gpr[rA(ctx->opcode)], EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_STXF(name, stop, opc2, opc3, type) \ static void glue(gen_, name##x)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->fpu_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_FPU); \ return; \ } \ gen_set_access_type(ctx, ACCESS_FLOAT); \ EA = tcg_temp_new(); \ + t0 = tcg_temp_new_i64(); \ gen_addr_reg_index(ctx, EA); \ - gen_qemu_##stop(ctx, cpu_fpr[rS(ctx->opcode)], EA); \ + get_fpr(t0, rS(ctx->opcode)); \ + gen_qemu_##stop(ctx, t0, EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } #define GEN_STFS(name, stop, op, type) \ @@ -836,15 +1057,12 @@ GEN_STUF(name, stop, op | 0x21, type); \ GEN_STUXF(name, stop, op | 0x01, type); \ GEN_STXF(name, stop, 0x17, op | 0x00, type) -static inline void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 arg1, TCGv arg2) +static void gen_qemu_st32fs(DisasContext *ctx, TCGv_i64 src, TCGv addr) { - TCGv_i32 t0 = tcg_temp_new_i32(); - TCGv t1 = tcg_temp_new(); - gen_helper_float64_to_float32(t0, cpu_env, arg1); - tcg_gen_extu_i32_tl(t1, t0); - tcg_temp_free_i32(t0); - gen_qemu_st32(ctx, t1, arg2); - tcg_temp_free(t1); + TCGv_i32 tmp = tcg_temp_new_i32(); + gen_helper_tosingle(tmp, src); + tcg_gen_qemu_st_i32(tmp, addr, ctx->mem_idx, DEF_MEMOP(MO_UL)); + tcg_temp_free_i32(tmp); } /* stfd stfdu stfdux stfdx */ @@ -852,54 +1070,88 @@ GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT); /* stfs stfsu stfsux stfsx */ GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT); -/* stfdp */ -static void gen_stfdp(DisasContext *ctx) +/* stfdepx (external PID lfdx) */ +static void gen_stfdepx(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; + CHK_SV; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } gen_set_access_type(ctx, ACCESS_FLOAT); EA = tcg_temp_new(); + t0 = tcg_temp_new_i64(); + gen_addr_reg_index(ctx, EA); + get_fpr(t0, rD(ctx->opcode)); + tcg_gen_qemu_st_i64(t0, EA, PPC_TLB_EPID_STORE, DEF_MEMOP(MO_Q)); + tcg_temp_free(EA); + tcg_temp_free_i64(t0); +} + +/* stfdp */ +static void gen_stfdp(DisasContext *ctx) +{ + TCGv EA; + TCGv_i64 t0; + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + gen_set_access_type(ctx, ACCESS_FLOAT); + EA = tcg_temp_new(); + t0 = tcg_temp_new_i64(); gen_addr_imm_index(ctx, EA, 0); /* We only need to swap high and low halves. gen_qemu_st64_i64 does necessary 64-bit byteswap already. */ if (unlikely(ctx->le_mode)) { - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + get_fpr(t0, rD(ctx->opcode) + 1); + gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + get_fpr(t0, rD(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, EA); } else { - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + get_fpr(t0, rD(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + get_fpr(t0, rD(ctx->opcode) + 1); + gen_qemu_st64_i64(ctx, t0, EA); } tcg_temp_free(EA); + tcg_temp_free_i64(t0); } /* stfdpx */ static void gen_stfdpx(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; if (unlikely(!ctx->fpu_enabled)) { gen_exception(ctx, POWERPC_EXCP_FPU); return; } gen_set_access_type(ctx, ACCESS_FLOAT); EA = tcg_temp_new(); + t0 = tcg_temp_new_i64(); gen_addr_reg_index(ctx, EA); /* We only need to swap high and low halves. gen_qemu_st64_i64 does necessary 64-bit byteswap already. */ if (unlikely(ctx->le_mode)) { - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + get_fpr(t0, rD(ctx->opcode) + 1); + gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + get_fpr(t0, rD(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, EA); } else { - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode)], EA); + get_fpr(t0, rD(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[rD(ctx->opcode) + 1], EA); + get_fpr(t0, rD(ctx->opcode) + 1); + gen_qemu_st64_i64(ctx, t0, EA); } tcg_temp_free(EA); + tcg_temp_free_i64(t0); } /* Optional: */ @@ -921,13 +1173,18 @@ static void gen_lfq(DisasContext *ctx) { int rd = rD(ctx->opcode); TCGv t0; + TCGv_i64 t1; gen_set_access_type(ctx, ACCESS_FLOAT); t0 = tcg_temp_new(); + t1 = tcg_temp_new_i64(); gen_addr_imm_index(ctx, t0, 0); - gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_qemu_ld64_i64(ctx, t1, t0); + set_fpr(rd, t1); gen_addr_add(ctx, t0, t0, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + gen_qemu_ld64_i64(ctx, t1, t0); + set_fpr((rd + 1) % 32, t1); tcg_temp_free(t0); + tcg_temp_free_i64(t1); } /* lfqu */ @@ -936,17 +1193,22 @@ static void gen_lfqu(DisasContext *ctx) int ra = rA(ctx->opcode); int rd = rD(ctx->opcode); TCGv t0, t1; + TCGv_i64 t2; gen_set_access_type(ctx, ACCESS_FLOAT); t0 = tcg_temp_new(); t1 = tcg_temp_new(); + t2 = tcg_temp_new_i64(); gen_addr_imm_index(ctx, t0, 0); - gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_qemu_ld64_i64(ctx, t2, t0); + set_fpr(rd, t2); gen_addr_add(ctx, t1, t0, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + gen_qemu_ld64_i64(ctx, t2, t1); + set_fpr((rd + 1) % 32, t2); if (ra != 0) tcg_gen_mov_tl(cpu_gpr[ra], t0); tcg_temp_free(t0); tcg_temp_free(t1); + tcg_temp_free_i64(t2); } /* lfqux */ @@ -956,16 +1218,21 @@ static void gen_lfqux(DisasContext *ctx) int rd = rD(ctx->opcode); gen_set_access_type(ctx, ACCESS_FLOAT); TCGv t0, t1; + TCGv_i64 t2; + t2 = tcg_temp_new_i64(); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_qemu_ld64_i64(ctx, t2, t0); + set_fpr(rd, t2); t1 = tcg_temp_new(); gen_addr_add(ctx, t1, t0, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + gen_qemu_ld64_i64(ctx, t2, t1); + set_fpr((rd + 1) % 32, t2); tcg_temp_free(t1); if (ra != 0) tcg_gen_mov_tl(cpu_gpr[ra], t0); tcg_temp_free(t0); + tcg_temp_free_i64(t2); } /* lfqx */ @@ -973,13 +1240,18 @@ static void gen_lfqx(DisasContext *ctx) { int rd = rD(ctx->opcode); TCGv t0; + TCGv_i64 t1; gen_set_access_type(ctx, ACCESS_FLOAT); t0 = tcg_temp_new(); + t1 = tcg_temp_new_i64(); gen_addr_reg_index(ctx, t0); - gen_qemu_ld64_i64(ctx, cpu_fpr[rd], t0); + gen_qemu_ld64_i64(ctx, t1, t0); + set_fpr(rd, t1); gen_addr_add(ctx, t0, t0, 8); - gen_qemu_ld64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + gen_qemu_ld64_i64(ctx, t1, t0); + set_fpr((rd + 1) % 32, t1); tcg_temp_free(t0); + tcg_temp_free_i64(t1); } /* stfq */ @@ -987,13 +1259,18 @@ static void gen_stfq(DisasContext *ctx) { int rd = rD(ctx->opcode); TCGv t0; + TCGv_i64 t1; gen_set_access_type(ctx, ACCESS_FLOAT); t0 = tcg_temp_new(); + t1 = tcg_temp_new_i64(); gen_addr_imm_index(ctx, t0, 0); - gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + get_fpr(t1, rd); + gen_qemu_st64_i64(ctx, t1, t0); gen_addr_add(ctx, t0, t0, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + get_fpr(t1, (rd + 1) % 32); + gen_qemu_st64_i64(ctx, t1, t0); tcg_temp_free(t0); + tcg_temp_free_i64(t1); } /* stfqu */ @@ -1002,17 +1279,23 @@ static void gen_stfqu(DisasContext *ctx) int ra = rA(ctx->opcode); int rd = rD(ctx->opcode); TCGv t0, t1; + TCGv_i64 t2; gen_set_access_type(ctx, ACCESS_FLOAT); + t2 = tcg_temp_new_i64(); t0 = tcg_temp_new(); gen_addr_imm_index(ctx, t0, 0); - gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + get_fpr(t2, rd); + gen_qemu_st64_i64(ctx, t2, t0); t1 = tcg_temp_new(); gen_addr_add(ctx, t1, t0, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + get_fpr(t2, (rd + 1) % 32); + gen_qemu_st64_i64(ctx, t2, t1); tcg_temp_free(t1); - if (ra != 0) + if (ra != 0) { tcg_gen_mov_tl(cpu_gpr[ra], t0); + } tcg_temp_free(t0); + tcg_temp_free_i64(t2); } /* stfqux */ @@ -1021,17 +1304,23 @@ static void gen_stfqux(DisasContext *ctx) int ra = rA(ctx->opcode); int rd = rD(ctx->opcode); TCGv t0, t1; + TCGv_i64 t2; gen_set_access_type(ctx, ACCESS_FLOAT); + t2 = tcg_temp_new_i64(); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + get_fpr(t2, rd); + gen_qemu_st64_i64(ctx, t2, t0); t1 = tcg_temp_new(); gen_addr_add(ctx, t1, t0, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t1); + get_fpr(t2, (rd + 1) % 32); + gen_qemu_st64_i64(ctx, t2, t1); tcg_temp_free(t1); - if (ra != 0) + if (ra != 0) { tcg_gen_mov_tl(cpu_gpr[ra], t0); + } tcg_temp_free(t0); + tcg_temp_free_i64(t2); } /* stfqx */ @@ -1039,13 +1328,18 @@ static void gen_stfqx(DisasContext *ctx) { int rd = rD(ctx->opcode); TCGv t0; + TCGv_i64 t1; gen_set_access_type(ctx, ACCESS_FLOAT); + t1 = tcg_temp_new_i64(); t0 = tcg_temp_new(); gen_addr_reg_index(ctx, t0); - gen_qemu_st64_i64(ctx, cpu_fpr[rd], t0); + get_fpr(t1, rd); + gen_qemu_st64_i64(ctx, t1, t0); gen_addr_add(ctx, t0, t0, 8); - gen_qemu_st64_i64(ctx, cpu_fpr[(rd + 1) % 32], t0); + get_fpr(t1, (rd + 1) % 32); + gen_qemu_st64_i64(ctx, t1, t0); tcg_temp_free(t0); + tcg_temp_free_i64(t1); } #undef _GEN_FLOAT_ACB diff --git a/target/ppc/translate/fp-ops.inc.c b/target/ppc/translate/fp-ops.inc.c index 3c6d05a074..621f6bfe0c 100644 --- a/target/ppc/translate/fp-ops.inc.c +++ b/target/ppc/translate/fp-ops.inc.c @@ -66,6 +66,7 @@ GEN_LDXF(name, ldop, 0x17, op | 0x00, type) GEN_LDFS(lfd, ld64, 0x12, PPC_FLOAT) GEN_LDFS(lfs, ld32fs, 0x10, PPC_FLOAT) +GEN_HANDLER_E(lfdepx, 0x1F, 0x1F, 0x12, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(lfiwax, 0x1f, 0x17, 0x1a, 0x00000001, PPC_NONE, PPC2_ISA205), GEN_HANDLER_E(lfiwzx, 0x1f, 0x17, 0x1b, 0x1, PPC_NONE, PPC2_FP_CVT_ISA206), GEN_HANDLER_E(lfdpx, 0x1F, 0x17, 0x18, 0x00200001, PPC_NONE, PPC2_ISA205), @@ -87,6 +88,7 @@ GEN_STXF(name, stop, 0x17, op | 0x00, type) GEN_STFS(stfd, st64_i64, 0x16, PPC_FLOAT) GEN_STFS(stfs, st32fs, 0x14, PPC_FLOAT) GEN_STXF(stfiw, st32fiw, 0x17, 0x1E, PPC_FLOAT_STFIWX) +GEN_HANDLER_E(stfdepx, 0x1F, 0x1F, 0x16, 0x00000001, PPC_NONE, PPC2_BOOKE206), GEN_HANDLER_E(stfdpx, 0x1F, 0x17, 0x1C, 0x00200001, PPC_NONE, PPC2_ISA205), GEN_HANDLER(frsqrtes, 0x3B, 0x1A, 0xFF, 0x001F07C0, PPC_FLOAT_FRSQRTES), diff --git a/target/ppc/translate/vmx-impl.inc.c b/target/ppc/translate/vmx-impl.inc.c index 3cb6fc2926..eb10c533ca 100644 --- a/target/ppc/translate/vmx-impl.inc.c +++ b/target/ppc/translate/vmx-impl.inc.c @@ -10,7 +10,7 @@ static inline TCGv_ptr gen_avr_ptr(int reg) { TCGv_ptr r = tcg_temp_new_ptr(); - tcg_gen_addi_ptr(r, cpu_env, offsetof(CPUPPCState, avr[reg])); + tcg_gen_addi_ptr(r, cpu_env, avr_full_offset(reg)); return r; } @@ -18,52 +18,66 @@ static inline TCGv_ptr gen_avr_ptr(int reg) static void glue(gen_, name)(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 avr; \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ gen_set_access_type(ctx, ACCESS_INT); \ + avr = tcg_temp_new_i64(); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ tcg_gen_andi_tl(EA, EA, ~0xf); \ /* We only need to swap high and low halves. gen_qemu_ld64_i64 does \ necessary 64-bit byteswap already. */ \ if (ctx->le_mode) { \ - gen_qemu_ld64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + gen_qemu_ld64_i64(ctx, avr, EA); \ + set_avr64(rD(ctx->opcode), avr, false); \ tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + gen_qemu_ld64_i64(ctx, avr, EA); \ + set_avr64(rD(ctx->opcode), avr, true); \ } else { \ - gen_qemu_ld64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + gen_qemu_ld64_i64(ctx, avr, EA); \ + set_avr64(rD(ctx->opcode), avr, true); \ tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_ld64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + gen_qemu_ld64_i64(ctx, avr, EA); \ + set_avr64(rD(ctx->opcode), avr, false); \ } \ tcg_temp_free(EA); \ + tcg_temp_free_i64(avr); \ } #define GEN_VR_STX(name, opc2, opc3) \ static void gen_st##name(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 avr; \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ gen_set_access_type(ctx, ACCESS_INT); \ + avr = tcg_temp_new_i64(); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ tcg_gen_andi_tl(EA, EA, ~0xf); \ /* We only need to swap high and low halves. gen_qemu_st64_i64 does \ necessary 64-bit byteswap already. */ \ if (ctx->le_mode) { \ - gen_qemu_st64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + get_avr64(avr, rD(ctx->opcode), false); \ + gen_qemu_st64_i64(ctx, avr, EA); \ tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_st64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + get_avr64(avr, rD(ctx->opcode), true); \ + gen_qemu_st64_i64(ctx, avr, EA); \ } else { \ - gen_qemu_st64_i64(ctx, cpu_avrh[rD(ctx->opcode)], EA); \ + get_avr64(avr, rD(ctx->opcode), true); \ + gen_qemu_st64_i64(ctx, avr, EA); \ tcg_gen_addi_tl(EA, EA, 8); \ - gen_qemu_st64_i64(ctx, cpu_avrl[rD(ctx->opcode)], EA); \ + get_avr64(avr, rD(ctx->opcode), false); \ + gen_qemu_st64_i64(ctx, avr, EA); \ } \ tcg_temp_free(EA); \ + tcg_temp_free_i64(avr); \ } #define GEN_VR_LVE(name, opc2, opc3, size) \ @@ -159,35 +173,50 @@ static void gen_lvsr(DisasContext *ctx) static void gen_mfvscr(DisasContext *ctx) { TCGv_i32 t; + TCGv_i64 avr; if (unlikely(!ctx->altivec_enabled)) { gen_exception(ctx, POWERPC_EXCP_VPU); return; } - tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); + avr = tcg_temp_new_i64(); + tcg_gen_movi_i64(avr, 0); + set_avr64(rD(ctx->opcode), avr, true); t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUPPCState, vscr)); - tcg_gen_extu_i32_i64(cpu_avrl[rD(ctx->opcode)], t); + gen_helper_mfvscr(t, cpu_env); + tcg_gen_extu_i32_i64(avr, t); + set_avr64(rD(ctx->opcode), avr, false); tcg_temp_free_i32(t); + tcg_temp_free_i64(avr); } static void gen_mtvscr(DisasContext *ctx) { - TCGv_ptr p; + TCGv_i32 val; + int bofs; + if (unlikely(!ctx->altivec_enabled)) { gen_exception(ctx, POWERPC_EXCP_VPU); return; } - p = gen_avr_ptr(rB(ctx->opcode)); - gen_helper_mtvscr(cpu_env, p); - tcg_temp_free_ptr(p); + + val = tcg_temp_new_i32(); + bofs = avr_full_offset(rB(ctx->opcode)); +#ifdef HOST_WORDS_BIGENDIAN + bofs += 3 * 4; +#endif + + tcg_gen_ld_i32(val, cpu_env, bofs); + gen_helper_mtvscr(cpu_env, val); + tcg_temp_free_i32(val); } #define GEN_VX_VMUL10(name, add_cin, ret_carry) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ - TCGv_i64 t0 = tcg_temp_new_i64(); \ - TCGv_i64 t1 = tcg_temp_new_i64(); \ - TCGv_i64 t2 = tcg_temp_new_i64(); \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ + TCGv_i64 t2; \ + TCGv_i64 avr; \ TCGv_i64 ten, z; \ \ if (unlikely(!ctx->altivec_enabled)) { \ @@ -195,30 +224,43 @@ static void glue(gen_, name)(DisasContext *ctx) \ return; \ } \ \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + t2 = tcg_temp_new_i64(); \ + avr = tcg_temp_new_i64(); \ ten = tcg_const_i64(10); \ z = tcg_const_i64(0); \ \ if (add_cin) { \ - tcg_gen_mulu2_i64(t0, t1, cpu_avrl[rA(ctx->opcode)], ten); \ - tcg_gen_andi_i64(t2, cpu_avrl[rB(ctx->opcode)], 0xF); \ - tcg_gen_add2_i64(cpu_avrl[rD(ctx->opcode)], t2, t0, t1, t2, z); \ + get_avr64(avr, rA(ctx->opcode), false); \ + tcg_gen_mulu2_i64(t0, t1, avr, ten); \ + get_avr64(avr, rB(ctx->opcode), false); \ + tcg_gen_andi_i64(t2, avr, 0xF); \ + tcg_gen_add2_i64(avr, t2, t0, t1, t2, z); \ + set_avr64(rD(ctx->opcode), avr, false); \ } else { \ - tcg_gen_mulu2_i64(cpu_avrl[rD(ctx->opcode)], t2, \ - cpu_avrl[rA(ctx->opcode)], ten); \ + get_avr64(avr, rA(ctx->opcode), false); \ + tcg_gen_mulu2_i64(avr, t2, avr, ten); \ + set_avr64(rD(ctx->opcode), avr, false); \ } \ \ if (ret_carry) { \ - tcg_gen_mulu2_i64(t0, t1, cpu_avrh[rA(ctx->opcode)], ten); \ - tcg_gen_add2_i64(t0, cpu_avrl[rD(ctx->opcode)], t0, t1, t2, z); \ - tcg_gen_movi_i64(cpu_avrh[rD(ctx->opcode)], 0); \ + get_avr64(avr, rA(ctx->opcode), true); \ + tcg_gen_mulu2_i64(t0, t1, avr, ten); \ + tcg_gen_add2_i64(t0, avr, t0, t1, t2, z); \ + set_avr64(rD(ctx->opcode), avr, false); \ + set_avr64(rD(ctx->opcode), z, true); \ } else { \ - tcg_gen_mul_i64(t0, cpu_avrh[rA(ctx->opcode)], ten); \ - tcg_gen_add_i64(cpu_avrh[rD(ctx->opcode)], t0, t2); \ + get_avr64(avr, rA(ctx->opcode), true); \ + tcg_gen_mul_i64(t0, avr, ten); \ + tcg_gen_add_i64(avr, t0, t2); \ + set_avr64(rD(ctx->opcode), avr, true); \ } \ \ tcg_temp_free_i64(t0); \ tcg_temp_free_i64(t1); \ tcg_temp_free_i64(t2); \ + tcg_temp_free_i64(avr); \ tcg_temp_free_i64(ten); \ tcg_temp_free_i64(z); \ } \ @@ -228,26 +270,30 @@ GEN_VX_VMUL10(vmul10euq, 1, 0); GEN_VX_VMUL10(vmul10cuq, 0, 1); GEN_VX_VMUL10(vmul10ecuq, 1, 1); -/* Logical operations */ -#define GEN_VX_LOGICAL(name, tcg_op, opc2, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ +#define GEN_VXFORM_V(name, vece, tcg_op, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) \ { \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ - tcg_op(cpu_avrh[rD(ctx->opcode)], cpu_avrh[rA(ctx->opcode)], cpu_avrh[rB(ctx->opcode)]); \ - tcg_op(cpu_avrl[rD(ctx->opcode)], cpu_avrl[rA(ctx->opcode)], cpu_avrl[rB(ctx->opcode)]); \ + \ + tcg_op(vece, \ + avr_full_offset(rD(ctx->opcode)), \ + avr_full_offset(rA(ctx->opcode)), \ + avr_full_offset(rB(ctx->opcode)), \ + 16, 16); \ } -GEN_VX_LOGICAL(vand, tcg_gen_and_i64, 2, 16); -GEN_VX_LOGICAL(vandc, tcg_gen_andc_i64, 2, 17); -GEN_VX_LOGICAL(vor, tcg_gen_or_i64, 2, 18); -GEN_VX_LOGICAL(vxor, tcg_gen_xor_i64, 2, 19); -GEN_VX_LOGICAL(vnor, tcg_gen_nor_i64, 2, 20); -GEN_VX_LOGICAL(veqv, tcg_gen_eqv_i64, 2, 26); -GEN_VX_LOGICAL(vnand, tcg_gen_nand_i64, 2, 22); -GEN_VX_LOGICAL(vorc, tcg_gen_orc_i64, 2, 21); +/* Logical operations */ +GEN_VXFORM_V(vand, MO_64, tcg_gen_gvec_and, 2, 16); +GEN_VXFORM_V(vandc, MO_64, tcg_gen_gvec_andc, 2, 17); +GEN_VXFORM_V(vor, MO_64, tcg_gen_gvec_or, 2, 18); +GEN_VXFORM_V(vxor, MO_64, tcg_gen_gvec_xor, 2, 19); +GEN_VXFORM_V(vnor, MO_64, tcg_gen_gvec_nor, 2, 20); +GEN_VXFORM_V(veqv, MO_64, tcg_gen_gvec_eqv, 2, 26); +GEN_VXFORM_V(vnand, MO_64, tcg_gen_gvec_nand, 2, 22); +GEN_VXFORM_V(vorc, MO_64, tcg_gen_gvec_orc, 2, 21); #define GEN_VXFORM(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -353,34 +399,34 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_temp_free_ptr(rb); \ } -GEN_VXFORM(vaddubm, 0, 0); +GEN_VXFORM_V(vaddubm, MO_8, tcg_gen_gvec_add, 0, 0); GEN_VXFORM_DUAL_EXT(vaddubm, PPC_ALTIVEC, PPC_NONE, 0, \ vmul10cuq, PPC_NONE, PPC2_ISA300, 0x0000F800) -GEN_VXFORM(vadduhm, 0, 1); +GEN_VXFORM_V(vadduhm, MO_16, tcg_gen_gvec_add, 0, 1); GEN_VXFORM_DUAL(vadduhm, PPC_ALTIVEC, PPC_NONE, \ vmul10ecuq, PPC_NONE, PPC2_ISA300) -GEN_VXFORM(vadduwm, 0, 2); -GEN_VXFORM(vaddudm, 0, 3); -GEN_VXFORM(vsububm, 0, 16); -GEN_VXFORM(vsubuhm, 0, 17); -GEN_VXFORM(vsubuwm, 0, 18); -GEN_VXFORM(vsubudm, 0, 19); -GEN_VXFORM(vmaxub, 1, 0); -GEN_VXFORM(vmaxuh, 1, 1); -GEN_VXFORM(vmaxuw, 1, 2); -GEN_VXFORM(vmaxud, 1, 3); -GEN_VXFORM(vmaxsb, 1, 4); -GEN_VXFORM(vmaxsh, 1, 5); -GEN_VXFORM(vmaxsw, 1, 6); -GEN_VXFORM(vmaxsd, 1, 7); -GEN_VXFORM(vminub, 1, 8); -GEN_VXFORM(vminuh, 1, 9); -GEN_VXFORM(vminuw, 1, 10); -GEN_VXFORM(vminud, 1, 11); -GEN_VXFORM(vminsb, 1, 12); -GEN_VXFORM(vminsh, 1, 13); -GEN_VXFORM(vminsw, 1, 14); -GEN_VXFORM(vminsd, 1, 15); +GEN_VXFORM_V(vadduwm, MO_32, tcg_gen_gvec_add, 0, 2); +GEN_VXFORM_V(vaddudm, MO_64, tcg_gen_gvec_add, 0, 3); +GEN_VXFORM_V(vsububm, MO_8, tcg_gen_gvec_sub, 0, 16); +GEN_VXFORM_V(vsubuhm, MO_16, tcg_gen_gvec_sub, 0, 17); +GEN_VXFORM_V(vsubuwm, MO_32, tcg_gen_gvec_sub, 0, 18); +GEN_VXFORM_V(vsubudm, MO_64, tcg_gen_gvec_sub, 0, 19); +GEN_VXFORM_V(vmaxub, MO_8, tcg_gen_gvec_umax, 1, 0); +GEN_VXFORM_V(vmaxuh, MO_16, tcg_gen_gvec_umax, 1, 1); +GEN_VXFORM_V(vmaxuw, MO_32, tcg_gen_gvec_umax, 1, 2); +GEN_VXFORM_V(vmaxud, MO_64, tcg_gen_gvec_umax, 1, 3); +GEN_VXFORM_V(vmaxsb, MO_8, tcg_gen_gvec_smax, 1, 4); +GEN_VXFORM_V(vmaxsh, MO_16, tcg_gen_gvec_smax, 1, 5); +GEN_VXFORM_V(vmaxsw, MO_32, tcg_gen_gvec_smax, 1, 6); +GEN_VXFORM_V(vmaxsd, MO_64, tcg_gen_gvec_smax, 1, 7); +GEN_VXFORM_V(vminub, MO_8, tcg_gen_gvec_umin, 1, 8); +GEN_VXFORM_V(vminuh, MO_16, tcg_gen_gvec_umin, 1, 9); +GEN_VXFORM_V(vminuw, MO_32, tcg_gen_gvec_umin, 1, 10); +GEN_VXFORM_V(vminud, MO_64, tcg_gen_gvec_umin, 1, 11); +GEN_VXFORM_V(vminsb, MO_8, tcg_gen_gvec_smin, 1, 12); +GEN_VXFORM_V(vminsh, MO_16, tcg_gen_gvec_smin, 1, 13); +GEN_VXFORM_V(vminsw, MO_32, tcg_gen_gvec_smin, 1, 14); +GEN_VXFORM_V(vminsd, MO_64, tcg_gen_gvec_smin, 1, 15); GEN_VXFORM(vavgub, 1, 16); GEN_VXFORM(vabsdub, 1, 16); GEN_VXFORM_DUAL(vavgub, PPC_ALTIVEC, PPC_NONE, \ @@ -406,6 +452,7 @@ GEN_VXFORM(vmrglw, 6, 6); static void gen_vmrgew(DisasContext *ctx) { TCGv_i64 tmp; + TCGv_i64 avr; int VT, VA, VB; if (unlikely(!ctx->altivec_enabled)) { gen_exception(ctx, POWERPC_EXCP_VPU); @@ -415,15 +462,28 @@ static void gen_vmrgew(DisasContext *ctx) VA = rA(ctx->opcode); VB = rB(ctx->opcode); tmp = tcg_temp_new_i64(); - tcg_gen_shri_i64(tmp, cpu_avrh[VB], 32); - tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VA], tmp, 0, 32); - tcg_gen_shri_i64(tmp, cpu_avrl[VB], 32); - tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VA], tmp, 0, 32); + avr = tcg_temp_new_i64(); + + get_avr64(avr, VB, true); + tcg_gen_shri_i64(tmp, avr, 32); + get_avr64(avr, VA, true); + tcg_gen_deposit_i64(avr, avr, tmp, 0, 32); + set_avr64(VT, avr, true); + + get_avr64(avr, VB, false); + tcg_gen_shri_i64(tmp, avr, 32); + get_avr64(avr, VA, false); + tcg_gen_deposit_i64(avr, avr, tmp, 0, 32); + set_avr64(VT, avr, false); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(avr); } static void gen_vmrgow(DisasContext *ctx) { + TCGv_i64 t0, t1; + TCGv_i64 avr; int VT, VA, VB; if (unlikely(!ctx->altivec_enabled)) { gen_exception(ctx, POWERPC_EXCP_VPU); @@ -432,9 +492,23 @@ static void gen_vmrgow(DisasContext *ctx) VT = rD(ctx->opcode); VA = rA(ctx->opcode); VB = rB(ctx->opcode); + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + avr = tcg_temp_new_i64(); - tcg_gen_deposit_i64(cpu_avrh[VT], cpu_avrh[VB], cpu_avrh[VA], 32, 32); - tcg_gen_deposit_i64(cpu_avrl[VT], cpu_avrl[VB], cpu_avrl[VA], 32, 32); + get_avr64(t0, VB, true); + get_avr64(t1, VA, true); + tcg_gen_deposit_i64(avr, t0, t1, 32, 32); + set_avr64(VT, avr, true); + + get_avr64(t0, VB, false); + get_avr64(t1, VA, false); + tcg_gen_deposit_i64(avr, t0, t1, 32, 32); + set_avr64(VT, avr, false); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + tcg_temp_free_i64(avr); } GEN_VXFORM(vmuloub, 4, 0); @@ -473,22 +547,55 @@ GEN_VXFORM(vslo, 6, 16); GEN_VXFORM(vsro, 6, 17); GEN_VXFORM(vaddcuw, 0, 6); GEN_VXFORM(vsubcuw, 0, 22); -GEN_VXFORM_ENV(vaddubs, 0, 8); + +#define GEN_VXFORM_SAT(NAME, VECE, NORM, SAT, OPC2, OPC3) \ +static void glue(glue(gen_, NAME), _vec)(unsigned vece, TCGv_vec t, \ + TCGv_vec sat, TCGv_vec a, \ + TCGv_vec b) \ +{ \ + TCGv_vec x = tcg_temp_new_vec_matching(t); \ + glue(glue(tcg_gen_, NORM), _vec)(VECE, x, a, b); \ + glue(glue(tcg_gen_, SAT), _vec)(VECE, t, a, b); \ + tcg_gen_cmp_vec(TCG_COND_NE, VECE, x, x, t); \ + tcg_gen_or_vec(VECE, sat, sat, x); \ + tcg_temp_free_vec(x); \ +} \ +static void glue(gen_, NAME)(DisasContext *ctx) \ +{ \ + static const GVecGen4 g = { \ + .fniv = glue(glue(gen_, NAME), _vec), \ + .fno = glue(gen_helper_, NAME), \ + .opc = glue(glue(INDEX_op_, SAT), _vec), \ + .write_aofs = true, \ + .vece = VECE, \ + }; \ + if (unlikely(!ctx->altivec_enabled)) { \ + gen_exception(ctx, POWERPC_EXCP_VPU); \ + return; \ + } \ + tcg_gen_gvec_4(avr_full_offset(rD(ctx->opcode)), \ + offsetof(CPUPPCState, vscr_sat), \ + avr_full_offset(rA(ctx->opcode)), \ + avr_full_offset(rB(ctx->opcode)), \ + 16, 16, &g); \ +} + +GEN_VXFORM_SAT(vaddubs, MO_8, add, usadd, 0, 8); GEN_VXFORM_DUAL_EXT(vaddubs, PPC_ALTIVEC, PPC_NONE, 0, \ vmul10uq, PPC_NONE, PPC2_ISA300, 0x0000F800) -GEN_VXFORM_ENV(vadduhs, 0, 9); +GEN_VXFORM_SAT(vadduhs, MO_16, add, usadd, 0, 9); GEN_VXFORM_DUAL(vadduhs, PPC_ALTIVEC, PPC_NONE, \ vmul10euq, PPC_NONE, PPC2_ISA300) -GEN_VXFORM_ENV(vadduws, 0, 10); -GEN_VXFORM_ENV(vaddsbs, 0, 12); -GEN_VXFORM_ENV(vaddshs, 0, 13); -GEN_VXFORM_ENV(vaddsws, 0, 14); -GEN_VXFORM_ENV(vsububs, 0, 24); -GEN_VXFORM_ENV(vsubuhs, 0, 25); -GEN_VXFORM_ENV(vsubuws, 0, 26); -GEN_VXFORM_ENV(vsubsbs, 0, 28); -GEN_VXFORM_ENV(vsubshs, 0, 29); -GEN_VXFORM_ENV(vsubsws, 0, 30); +GEN_VXFORM_SAT(vadduws, MO_32, add, usadd, 0, 10); +GEN_VXFORM_SAT(vaddsbs, MO_8, add, ssadd, 0, 12); +GEN_VXFORM_SAT(vaddshs, MO_16, add, ssadd, 0, 13); +GEN_VXFORM_SAT(vaddsws, MO_32, add, ssadd, 0, 14); +GEN_VXFORM_SAT(vsububs, MO_8, sub, ussub, 0, 24); +GEN_VXFORM_SAT(vsubuhs, MO_16, sub, ussub, 0, 25); +GEN_VXFORM_SAT(vsubuws, MO_32, sub, ussub, 0, 26); +GEN_VXFORM_SAT(vsubsbs, MO_8, sub, sssub, 0, 28); +GEN_VXFORM_SAT(vsubshs, MO_16, sub, sssub, 0, 29); +GEN_VXFORM_SAT(vsubsws, MO_32, sub, sssub, 0, 30); GEN_VXFORM(vadduqm, 0, 4); GEN_VXFORM(vaddcuq, 0, 5); GEN_VXFORM3(vaddeuqm, 30, 0); @@ -634,25 +741,21 @@ GEN_VXRFORM_DUAL(vcmpbfp, PPC_ALTIVEC, PPC_NONE, \ GEN_VXRFORM_DUAL(vcmpgtfp, PPC_ALTIVEC, PPC_NONE, \ vcmpgtud, PPC_NONE, PPC2_ALTIVEC_207) -#define GEN_VXFORM_SIMM(name, opc2, opc3) \ +#define GEN_VXFORM_DUPI(name, tcg_op, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ { \ - TCGv_ptr rd; \ - TCGv_i32 simm; \ + int simm; \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ - simm = tcg_const_i32(SIMM5(ctx->opcode)); \ - rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##name (rd, simm); \ - tcg_temp_free_i32(simm); \ - tcg_temp_free_ptr(rd); \ + simm = SIMM5(ctx->opcode); \ + tcg_op(avr_full_offset(rD(ctx->opcode)), 16, 16, simm); \ } -GEN_VXFORM_SIMM(vspltisb, 6, 12); -GEN_VXFORM_SIMM(vspltish, 6, 13); -GEN_VXFORM_SIMM(vspltisw, 6, 14); +GEN_VXFORM_DUPI(vspltisb, tcg_gen_gvec_dup8i, 6, 12); +GEN_VXFORM_DUPI(vspltish, tcg_gen_gvec_dup16i, 6, 13); +GEN_VXFORM_DUPI(vspltisw, tcg_gen_gvec_dup32i, 6, 14); #define GEN_VXFORM_NOA(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -732,39 +835,31 @@ GEN_VXFORM_NOA(vprtybw, 1, 24); GEN_VXFORM_NOA(vprtybd, 1, 24); GEN_VXFORM_NOA(vprtybq, 1, 24); -#define GEN_VXFORM_SIMM(name, opc2, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ - { \ - TCGv_ptr rd; \ - TCGv_i32 simm; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - simm = tcg_const_i32(SIMM5(ctx->opcode)); \ - rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##name (rd, simm); \ - tcg_temp_free_i32(simm); \ - tcg_temp_free_ptr(rd); \ +static void gen_vsplt(DisasContext *ctx, int vece) +{ + int uimm, dofs, bofs; + + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; } -#define GEN_VXFORM_UIMM(name, opc2, opc3) \ -static void glue(gen_, name)(DisasContext *ctx) \ - { \ - TCGv_ptr rb, rd; \ - TCGv_i32 uimm; \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - uimm = tcg_const_i32(UIMM5(ctx->opcode)); \ - rb = gen_avr_ptr(rB(ctx->opcode)); \ - rd = gen_avr_ptr(rD(ctx->opcode)); \ - gen_helper_##name (rd, rb, uimm); \ - tcg_temp_free_i32(uimm); \ - tcg_temp_free_ptr(rb); \ - tcg_temp_free_ptr(rd); \ - } + uimm = UIMM5(ctx->opcode); + bofs = avr_full_offset(rB(ctx->opcode)); + dofs = avr_full_offset(rD(ctx->opcode)); + + /* Experimental testing shows that hardware masks the immediate. */ + bofs += (uimm << vece) & 15; +#ifndef HOST_WORDS_BIGENDIAN + bofs ^= 15; + bofs &= ~((1 << vece) - 1); +#endif + + tcg_gen_gvec_dup_mem(vece, dofs, bofs, 16, 16); +} + +#define GEN_VXFORM_VSPLT(name, vece, opc2, opc3) \ +static void glue(gen_, name)(DisasContext *ctx) { gen_vsplt(ctx, vece); } #define GEN_VXFORM_UIMM_ENV(name, opc2, opc3) \ static void glue(gen_, name)(DisasContext *ctx) \ @@ -790,7 +885,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ { \ TCGv_ptr rb, rd; \ uint8_t uimm = UIMM4(ctx->opcode); \ - TCGv_i32 t0 = tcg_temp_new_i32(); \ + TCGv_i32 t0; \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ @@ -798,6 +893,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ if (uimm > splat_max) { \ uimm = 0; \ } \ + t0 = tcg_temp_new_i32(); \ tcg_gen_movi_i32(t0, uimm); \ rb = gen_avr_ptr(rB(ctx->opcode)); \ rd = gen_avr_ptr(rD(ctx->opcode)); \ @@ -807,9 +903,9 @@ static void glue(gen_, name)(DisasContext *ctx) \ tcg_temp_free_ptr(rd); \ } -GEN_VXFORM_UIMM(vspltb, 6, 8); -GEN_VXFORM_UIMM(vsplth, 6, 9); -GEN_VXFORM_UIMM(vspltw, 6, 10); +GEN_VXFORM_VSPLT(vspltb, MO_8, 6, 8); +GEN_VXFORM_VSPLT(vsplth, MO_16, 6, 9); +GEN_VXFORM_VSPLT(vspltw, MO_32, 6, 10); GEN_VXFORM_UIMM_SPLAT(vextractub, 6, 8, 15); GEN_VXFORM_UIMM_SPLAT(vextractuh, 6, 9, 14); GEN_VXFORM_UIMM_SPLAT(vextractuw, 6, 10, 12); @@ -1169,7 +1265,7 @@ GEN_VXFORM_DUAL(vsldoi, PPC_ALTIVEC, PPC_NONE, #undef GEN_VXRFORM_DUAL #undef GEN_VXRFORM1 #undef GEN_VXRFORM -#undef GEN_VXFORM_SIMM +#undef GEN_VXFORM_DUPI #undef GEN_VXFORM_NOA #undef GEN_VXFORM_UIMM #undef GEN_VAFORM_PAIRED diff --git a/target/ppc/translate/vmx-ops.inc.c b/target/ppc/translate/vmx-ops.inc.c index 139f80cb24..84e05fb827 100644 --- a/target/ppc/translate/vmx-ops.inc.c +++ b/target/ppc/translate/vmx-ops.inc.c @@ -143,7 +143,7 @@ GEN_VXFORM(vaddsws, 0, 14), GEN_VXFORM_DUAL(vsububs, bcdadd, 0, 24, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_DUAL(vsubuhs, bcdsub, 0, 25, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM(vsubuws, 0, 26), -GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_NONE, PPC2_ISA300), +GEN_VXFORM_DUAL(vsubsbs, bcdtrunc, 0, 28, PPC_ALTIVEC, PPC2_ISA300), GEN_VXFORM(vsubshs, 0, 29), GEN_VXFORM_DUAL(vsubsws, xpnd04_2, 0, 30, PPC_ALTIVEC, PPC_NONE), GEN_VXFORM_207(vadduqm, 0, 4), diff --git a/target/ppc/translate/vsx-impl.inc.c b/target/ppc/translate/vsx-impl.inc.c index 85ed135d44..489b2436e4 100644 --- a/target/ppc/translate/vsx-impl.inc.c +++ b/target/ppc/translate/vsx-impl.inc.c @@ -1,37 +1,43 @@ /*** VSX extension ***/ -static inline TCGv_i64 cpu_vsrh(int n) +static inline void get_cpu_vsrh(TCGv_i64 dst, int n) { - if (n < 32) { - return cpu_fpr[n]; - } else { - return cpu_avrh[n-32]; - } + tcg_gen_ld_i64(dst, cpu_env, vsr64_offset(n, true)); } -static inline TCGv_i64 cpu_vsrl(int n) +static inline void get_cpu_vsrl(TCGv_i64 dst, int n) { - if (n < 32) { - return cpu_vsr[n]; - } else { - return cpu_avrl[n-32]; - } + tcg_gen_ld_i64(dst, cpu_env, vsr64_offset(n, false)); +} + +static inline void set_cpu_vsrh(int n, TCGv_i64 src) +{ + tcg_gen_st_i64(src, cpu_env, vsr64_offset(n, true)); +} + +static inline void set_cpu_vsrl(int n, TCGv_i64 src) +{ + tcg_gen_st_i64(src, cpu_env, vsr64_offset(n, false)); } #define VSX_LOAD_SCALAR(name, operation) \ static void gen_##name(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ - gen_qemu_##operation(ctx, cpu_vsrh(xT(ctx->opcode)), EA); \ + gen_qemu_##operation(ctx, t0, EA); \ + set_cpu_vsrh(xT(ctx->opcode), t0); \ /* NOTE: cpu_vsrl is undefined */ \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } VSX_LOAD_SCALAR(lxsdx, ld64_i64) @@ -44,43 +50,60 @@ VSX_LOAD_SCALAR(lxsspx, ld32fs) static void gen_lxvd2x(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - gen_qemu_ld64_i64(ctx, cpu_vsrh(xT(ctx->opcode)), EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsrh(xT(ctx->opcode), t0); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_ld64_i64(ctx, cpu_vsrl(xT(ctx->opcode)), EA); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsrl(xT(ctx->opcode), t0); tcg_temp_free(EA); + tcg_temp_free_i64(t0); } static void gen_lxvdsx(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; + TCGv_i64 t1; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - gen_qemu_ld64_i64(ctx, cpu_vsrh(xT(ctx->opcode)), EA); - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xT(ctx->opcode))); + gen_qemu_ld64_i64(ctx, t0, EA); + set_cpu_vsrh(xT(ctx->opcode), t0); + tcg_gen_mov_i64(t1, t0); + set_cpu_vsrl(xT(ctx->opcode), t1); tcg_temp_free(EA); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); } static void gen_lxvw4x(DisasContext *ctx) { TCGv EA; - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + get_cpu_vsrh(xth, xT(ctx->opcode)); + get_cpu_vsrl(xtl, xT(ctx->opcode)); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); @@ -104,6 +127,8 @@ static void gen_lxvw4x(DisasContext *ctx) tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); } static void gen_bswap16x8(TCGv_i64 outh, TCGv_i64 outl, @@ -151,13 +176,17 @@ static void gen_bswap32x4(TCGv_i64 outh, TCGv_i64 outl, static void gen_lxvh8x(DisasContext *ctx) { TCGv EA; - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + get_cpu_vsrh(xth, xT(ctx->opcode)); + get_cpu_vsrl(xtl, xT(ctx->opcode)); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); @@ -169,18 +198,24 @@ static void gen_lxvh8x(DisasContext *ctx) gen_bswap16x8(xth, xtl, xth, xtl); } tcg_temp_free(EA); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); } static void gen_lxvb16x(DisasContext *ctx) { TCGv EA; - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + get_cpu_vsrh(xth, xT(ctx->opcode)); + get_cpu_vsrl(xtl, xT(ctx->opcode)); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -188,6 +223,8 @@ static void gen_lxvb16x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_ld_i64(xtl, EA, ctx->mem_idx, MO_BEQ); tcg_temp_free(EA); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); } #define VSX_VECTOR_LOAD_STORE(name, op, indexed) \ @@ -195,15 +232,14 @@ static void gen_##name(DisasContext *ctx) \ { \ int xt; \ TCGv EA; \ - TCGv_i64 xth, xtl; \ + TCGv_i64 xth; \ + TCGv_i64 xtl; \ \ if (indexed) { \ xt = xT(ctx->opcode); \ } else { \ xt = DQxT(ctx->opcode); \ } \ - xth = cpu_vsrh(xt); \ - xtl = cpu_vsrl(xt); \ \ if (xt < 32) { \ if (unlikely(!ctx->vsx_enabled)) { \ @@ -216,6 +252,10 @@ static void gen_##name(DisasContext *ctx) \ return; \ } \ } \ + xth = tcg_temp_new_i64(); \ + xtl = tcg_temp_new_i64(); \ + get_cpu_vsrh(xth, xt); \ + get_cpu_vsrl(xtl, xt); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ if (indexed) { \ @@ -225,14 +265,20 @@ static void gen_##name(DisasContext *ctx) \ } \ if (ctx->le_mode) { \ tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_LEQ); \ + set_cpu_vsrl(xt, xtl); \ tcg_gen_addi_tl(EA, EA, 8); \ tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_LEQ); \ + set_cpu_vsrh(xt, xth); \ } else { \ tcg_gen_qemu_##op(xth, EA, ctx->mem_idx, MO_BEQ); \ + set_cpu_vsrh(xt, xth); \ tcg_gen_addi_tl(EA, EA, 8); \ tcg_gen_qemu_##op(xtl, EA, ctx->mem_idx, MO_BEQ); \ + set_cpu_vsrl(xt, xtl); \ } \ tcg_temp_free(EA); \ + tcg_temp_free_i64(xth); \ + tcg_temp_free_i64(xtl); \ } VSX_VECTOR_LOAD_STORE(lxv, ld_i64, 0) @@ -276,18 +322,22 @@ VSX_VECTOR_LOAD_STORE_LENGTH(stxvll) static void gen_##name(DisasContext *ctx) \ { \ TCGv EA; \ - TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); \ + TCGv_i64 xth; \ \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ + xth = tcg_temp_new_i64(); \ + get_cpu_vsrh(xth, rD(ctx->opcode) + 32); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_imm_index(ctx, EA, 0x03); \ gen_qemu_##operation(ctx, xth, EA); \ + set_cpu_vsrh(rD(ctx->opcode) + 32, xth); \ /* NOTE: cpu_vsrl is undefined */ \ tcg_temp_free(EA); \ + tcg_temp_free_i64(xth); \ } VSX_LOAD_SCALAR_DS(lxsd, ld64_i64) @@ -297,15 +347,19 @@ VSX_LOAD_SCALAR_DS(lxssp, ld32fs) static void gen_##name(DisasContext *ctx) \ { \ TCGv EA; \ + TCGv_i64 t0; \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ + t0 = tcg_temp_new_i64(); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_reg_index(ctx, EA); \ - gen_qemu_##operation(ctx, cpu_vsrh(xS(ctx->opcode)), EA); \ + get_cpu_vsrh(t0, xS(ctx->opcode)); \ + gen_qemu_##operation(ctx, t0, EA); \ tcg_temp_free(EA); \ + tcg_temp_free_i64(t0); \ } VSX_STORE_SCALAR(stxsdx, st64_i64) @@ -318,28 +372,38 @@ VSX_STORE_SCALAR(stxsspx, st32fs) static void gen_stxvd2x(DisasContext *ctx) { TCGv EA; + TCGv_i64 t0; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + t0 = tcg_temp_new_i64(); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); - gen_qemu_st64_i64(ctx, cpu_vsrh(xS(ctx->opcode)), EA); + get_cpu_vsrh(t0, xS(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, EA); tcg_gen_addi_tl(EA, EA, 8); - gen_qemu_st64_i64(ctx, cpu_vsrl(xS(ctx->opcode)), EA); + get_cpu_vsrl(t0, xS(ctx->opcode)); + gen_qemu_st64_i64(ctx, t0, EA); tcg_temp_free(EA); + tcg_temp_free_i64(t0); } static void gen_stxvw4x(DisasContext *ctx) { - TCGv_i64 xsh = cpu_vsrh(xS(ctx->opcode)); - TCGv_i64 xsl = cpu_vsrl(xS(ctx->opcode)); TCGv EA; + TCGv_i64 xsh; + TCGv_i64 xsl; + if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xsh = tcg_temp_new_i64(); + xsl = tcg_temp_new_i64(); + get_cpu_vsrh(xsh, xS(ctx->opcode)); + get_cpu_vsrl(xsl, xS(ctx->opcode)); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -362,18 +426,24 @@ static void gen_stxvw4x(DisasContext *ctx) tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); + tcg_temp_free_i64(xsh); + tcg_temp_free_i64(xsl); } static void gen_stxvh8x(DisasContext *ctx) { - TCGv_i64 xsh = cpu_vsrh(xS(ctx->opcode)); - TCGv_i64 xsl = cpu_vsrl(xS(ctx->opcode)); TCGv EA; + TCGv_i64 xsh; + TCGv_i64 xsl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xsh = tcg_temp_new_i64(); + xsl = tcg_temp_new_i64(); + get_cpu_vsrh(xsh, xS(ctx->opcode)); + get_cpu_vsrl(xsl, xS(ctx->opcode)); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -393,18 +463,24 @@ static void gen_stxvh8x(DisasContext *ctx) tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEQ); } tcg_temp_free(EA); + tcg_temp_free_i64(xsh); + tcg_temp_free_i64(xsl); } static void gen_stxvb16x(DisasContext *ctx) { - TCGv_i64 xsh = cpu_vsrh(xS(ctx->opcode)); - TCGv_i64 xsl = cpu_vsrl(xS(ctx->opcode)); TCGv EA; + TCGv_i64 xsh; + TCGv_i64 xsl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xsh = tcg_temp_new_i64(); + xsl = tcg_temp_new_i64(); + get_cpu_vsrh(xsh, xS(ctx->opcode)); + get_cpu_vsrl(xsl, xS(ctx->opcode)); gen_set_access_type(ctx, ACCESS_INT); EA = tcg_temp_new(); gen_addr_reg_index(ctx, EA); @@ -412,80 +488,144 @@ static void gen_stxvb16x(DisasContext *ctx) tcg_gen_addi_tl(EA, EA, 8); tcg_gen_qemu_st_i64(xsl, EA, ctx->mem_idx, MO_BEQ); tcg_temp_free(EA); + tcg_temp_free_i64(xsh); + tcg_temp_free_i64(xsl); } #define VSX_STORE_SCALAR_DS(name, operation) \ static void gen_##name(DisasContext *ctx) \ { \ TCGv EA; \ - TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); \ + TCGv_i64 xth; \ \ if (unlikely(!ctx->altivec_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VPU); \ return; \ } \ + xth = tcg_temp_new_i64(); \ + get_cpu_vsrh(xth, rD(ctx->opcode) + 32); \ gen_set_access_type(ctx, ACCESS_INT); \ EA = tcg_temp_new(); \ gen_addr_imm_index(ctx, EA, 0x03); \ gen_qemu_##operation(ctx, xth, EA); \ /* NOTE: cpu_vsrl is undefined */ \ tcg_temp_free(EA); \ + tcg_temp_free_i64(xth); \ } VSX_LOAD_SCALAR_DS(stxsd, st64_i64) VSX_LOAD_SCALAR_DS(stxssp, st32fs) -#define MV_VSRW(name, tcgop1, tcgop2, target, source) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - if (xS(ctx->opcode) < 32) { \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - TCGv_i64 tmp = tcg_temp_new_i64(); \ - tcg_gen_##tcgop1(tmp, source); \ - tcg_gen_##tcgop2(target, tmp); \ - tcg_temp_free_i64(tmp); \ +static void gen_mfvsrwz(DisasContext *ctx) +{ + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 xsh = tcg_temp_new_i64(); + get_cpu_vsrh(xsh, xS(ctx->opcode)); + tcg_gen_ext32u_i64(tmp, xsh); + tcg_gen_trunc_i64_tl(cpu_gpr[rA(ctx->opcode)], tmp); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(xsh); } +static void gen_mtvsrwa(DisasContext *ctx) +{ + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 xsh = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32s_i64(xsh, tmp); + set_cpu_vsrh(xT(ctx->opcode), xsh); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(xsh); +} -MV_VSRW(mfvsrwz, ext32u_i64, trunc_i64_tl, cpu_gpr[rA(ctx->opcode)], \ - cpu_vsrh(xS(ctx->opcode))) -MV_VSRW(mtvsrwa, extu_tl_i64, ext32s_i64, cpu_vsrh(xT(ctx->opcode)), \ - cpu_gpr[rA(ctx->opcode)]) -MV_VSRW(mtvsrwz, extu_tl_i64, ext32u_i64, cpu_vsrh(xT(ctx->opcode)), \ - cpu_gpr[rA(ctx->opcode)]) +static void gen_mtvsrwz(DisasContext *ctx) +{ + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + TCGv_i64 tmp = tcg_temp_new_i64(); + TCGv_i64 xsh = tcg_temp_new_i64(); + tcg_gen_extu_tl_i64(tmp, cpu_gpr[rA(ctx->opcode)]); + tcg_gen_ext32u_i64(xsh, tmp); + set_cpu_vsrh(xT(ctx->opcode), xsh); + tcg_temp_free_i64(tmp); + tcg_temp_free_i64(xsh); +} #if defined(TARGET_PPC64) -#define MV_VSRD(name, target, source) \ -static void gen_##name(DisasContext *ctx) \ -{ \ - if (xS(ctx->opcode) < 32) { \ - if (unlikely(!ctx->fpu_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_FPU); \ - return; \ - } \ - } else { \ - if (unlikely(!ctx->altivec_enabled)) { \ - gen_exception(ctx, POWERPC_EXCP_VPU); \ - return; \ - } \ - } \ - tcg_gen_mov_i64(target, source); \ +static void gen_mfvsrd(DisasContext *ctx) +{ + TCGv_i64 t0; + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + t0 = tcg_temp_new_i64(); + get_cpu_vsrh(t0, xS(ctx->opcode)); + tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], t0); + tcg_temp_free_i64(t0); } -MV_VSRD(mfvsrd, cpu_gpr[rA(ctx->opcode)], cpu_vsrh(xS(ctx->opcode))) -MV_VSRD(mtvsrd, cpu_vsrh(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)]) +static void gen_mtvsrd(DisasContext *ctx) +{ + TCGv_i64 t0; + if (xS(ctx->opcode) < 32) { + if (unlikely(!ctx->fpu_enabled)) { + gen_exception(ctx, POWERPC_EXCP_FPU); + return; + } + } else { + if (unlikely(!ctx->altivec_enabled)) { + gen_exception(ctx, POWERPC_EXCP_VPU); + return; + } + } + t0 = tcg_temp_new_i64(); + tcg_gen_mov_i64(t0, cpu_gpr[rA(ctx->opcode)]); + set_cpu_vsrh(xT(ctx->opcode), t0); + tcg_temp_free_i64(t0); +} static void gen_mfvsrld(DisasContext *ctx) { + TCGv_i64 t0; if (xS(ctx->opcode) < 32) { if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); @@ -497,12 +637,15 @@ static void gen_mfvsrld(DisasContext *ctx) return; } } - - tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], cpu_vsrl(xS(ctx->opcode))); + t0 = tcg_temp_new_i64(); + get_cpu_vsrl(t0, xS(ctx->opcode)); + tcg_gen_mov_i64(cpu_gpr[rA(ctx->opcode)], t0); + tcg_temp_free_i64(t0); } static void gen_mtvsrdd(DisasContext *ctx) { + TCGv_i64 t0; if (xT(ctx->opcode) < 32) { if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); @@ -515,17 +658,22 @@ static void gen_mtvsrdd(DisasContext *ctx) } } + t0 = tcg_temp_new_i64(); if (!rA(ctx->opcode)) { - tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), 0); + tcg_gen_movi_i64(t0, 0); } else { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)]); + tcg_gen_mov_i64(t0, cpu_gpr[rA(ctx->opcode)]); } + set_cpu_vsrh(xT(ctx->opcode), t0); - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_gpr[rB(ctx->opcode)]); + tcg_gen_mov_i64(t0, cpu_gpr[rB(ctx->opcode)]); + set_cpu_vsrl(xT(ctx->opcode), t0); + tcg_temp_free_i64(t0); } static void gen_mtvsrws(DisasContext *ctx) { + TCGv_i64 t0; if (xT(ctx->opcode) < 32) { if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); @@ -538,55 +686,61 @@ static void gen_mtvsrws(DisasContext *ctx) } } - tcg_gen_deposit_i64(cpu_vsrl(xT(ctx->opcode)), cpu_gpr[rA(ctx->opcode)], + t0 = tcg_temp_new_i64(); + tcg_gen_deposit_i64(t0, cpu_gpr[rA(ctx->opcode)], cpu_gpr[rA(ctx->opcode)], 32, 32); - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xT(ctx->opcode))); + set_cpu_vsrl(xT(ctx->opcode), t0); + set_cpu_vsrh(xT(ctx->opcode), t0); + tcg_temp_free_i64(t0); } #endif static void gen_xxpermdi(DisasContext *ctx) { + TCGv_i64 xh, xl; + if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xh = tcg_temp_new_i64(); + xl = tcg_temp_new_i64(); + if (unlikely((xT(ctx->opcode) == xA(ctx->opcode)) || (xT(ctx->opcode) == xB(ctx->opcode)))) { - TCGv_i64 xh, xl; - - xh = tcg_temp_new_i64(); - xl = tcg_temp_new_i64(); - if ((DM(ctx->opcode) & 2) == 0) { - tcg_gen_mov_i64(xh, cpu_vsrh(xA(ctx->opcode))); + get_cpu_vsrh(xh, xA(ctx->opcode)); } else { - tcg_gen_mov_i64(xh, cpu_vsrl(xA(ctx->opcode))); + get_cpu_vsrl(xh, xA(ctx->opcode)); } if ((DM(ctx->opcode) & 1) == 0) { - tcg_gen_mov_i64(xl, cpu_vsrh(xB(ctx->opcode))); + get_cpu_vsrh(xl, xB(ctx->opcode)); } else { - tcg_gen_mov_i64(xl, cpu_vsrl(xB(ctx->opcode))); + get_cpu_vsrl(xl, xB(ctx->opcode)); } - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xh); - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xl); - - tcg_temp_free_i64(xh); - tcg_temp_free_i64(xl); + set_cpu_vsrh(xT(ctx->opcode), xh); + set_cpu_vsrl(xT(ctx->opcode), xl); } else { if ((DM(ctx->opcode) & 2) == 0) { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode))); + get_cpu_vsrh(xh, xA(ctx->opcode)); + set_cpu_vsrh(xT(ctx->opcode), xh); } else { - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode))); + get_cpu_vsrl(xh, xA(ctx->opcode)); + set_cpu_vsrh(xT(ctx->opcode), xh); } if ((DM(ctx->opcode) & 1) == 0) { - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xB(ctx->opcode))); + get_cpu_vsrh(xl, xB(ctx->opcode)); + set_cpu_vsrl(xT(ctx->opcode), xl); } else { - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xB(ctx->opcode))); + get_cpu_vsrl(xl, xB(ctx->opcode)); + set_cpu_vsrl(xT(ctx->opcode), xl); } } + tcg_temp_free_i64(xh); + tcg_temp_free_i64(xl); } #define OP_ABS 1 @@ -606,7 +760,7 @@ static void glue(gen_, name)(DisasContext * ctx) \ } \ xb = tcg_temp_new_i64(); \ sgm = tcg_temp_new_i64(); \ - tcg_gen_mov_i64(xb, cpu_vsrh(xB(ctx->opcode))); \ + get_cpu_vsrh(xb, xB(ctx->opcode)); \ tcg_gen_movi_i64(sgm, sgn_mask); \ switch (op) { \ case OP_ABS: { \ @@ -623,7 +777,7 @@ static void glue(gen_, name)(DisasContext * ctx) \ } \ case OP_CPSGN: { \ TCGv_i64 xa = tcg_temp_new_i64(); \ - tcg_gen_mov_i64(xa, cpu_vsrh(xA(ctx->opcode))); \ + get_cpu_vsrh(xa, xA(ctx->opcode)); \ tcg_gen_and_i64(xa, xa, sgm); \ tcg_gen_andc_i64(xb, xb, sgm); \ tcg_gen_or_i64(xb, xb, xa); \ @@ -631,7 +785,7 @@ static void glue(gen_, name)(DisasContext * ctx) \ break; \ } \ } \ - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xb); \ + set_cpu_vsrh(xT(ctx->opcode), xb); \ tcg_temp_free_i64(xb); \ tcg_temp_free_i64(sgm); \ } @@ -647,7 +801,7 @@ static void glue(gen_, name)(DisasContext *ctx) \ int xa; \ int xt = rD(ctx->opcode) + 32; \ int xb = rB(ctx->opcode) + 32; \ - TCGv_i64 xah, xbh, xbl, sgm; \ + TCGv_i64 xah, xbh, xbl, sgm, tmp; \ \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ @@ -656,8 +810,9 @@ static void glue(gen_, name)(DisasContext *ctx) \ xbh = tcg_temp_new_i64(); \ xbl = tcg_temp_new_i64(); \ sgm = tcg_temp_new_i64(); \ - tcg_gen_mov_i64(xbh, cpu_vsrh(xb)); \ - tcg_gen_mov_i64(xbl, cpu_vsrl(xb)); \ + tmp = tcg_temp_new_i64(); \ + get_cpu_vsrh(xbh, xb); \ + get_cpu_vsrl(xbl, xb); \ tcg_gen_movi_i64(sgm, sgn_mask); \ switch (op) { \ case OP_ABS: \ @@ -672,17 +827,19 @@ static void glue(gen_, name)(DisasContext *ctx) \ case OP_CPSGN: \ xah = tcg_temp_new_i64(); \ xa = rA(ctx->opcode) + 32; \ - tcg_gen_and_i64(xah, cpu_vsrh(xa), sgm); \ + get_cpu_vsrh(tmp, xa); \ + tcg_gen_and_i64(xah, tmp, sgm); \ tcg_gen_andc_i64(xbh, xbh, sgm); \ tcg_gen_or_i64(xbh, xbh, xah); \ tcg_temp_free_i64(xah); \ break; \ } \ - tcg_gen_mov_i64(cpu_vsrh(xt), xbh); \ - tcg_gen_mov_i64(cpu_vsrl(xt), xbl); \ + set_cpu_vsrh(xt, xbh); \ + set_cpu_vsrl(xt, xbl); \ tcg_temp_free_i64(xbl); \ tcg_temp_free_i64(xbh); \ tcg_temp_free_i64(sgm); \ + tcg_temp_free_i64(tmp); \ } VSX_SCALAR_MOVE_QP(xsabsqp, OP_ABS, SGN_MASK_DP) @@ -701,8 +858,8 @@ static void glue(gen_, name)(DisasContext * ctx) \ xbh = tcg_temp_new_i64(); \ xbl = tcg_temp_new_i64(); \ sgm = tcg_temp_new_i64(); \ - tcg_gen_mov_i64(xbh, cpu_vsrh(xB(ctx->opcode))); \ - tcg_gen_mov_i64(xbl, cpu_vsrl(xB(ctx->opcode))); \ + set_cpu_vsrh(xB(ctx->opcode), xbh); \ + set_cpu_vsrl(xB(ctx->opcode), xbl); \ tcg_gen_movi_i64(sgm, sgn_mask); \ switch (op) { \ case OP_ABS: { \ @@ -723,8 +880,8 @@ static void glue(gen_, name)(DisasContext * ctx) \ case OP_CPSGN: { \ TCGv_i64 xah = tcg_temp_new_i64(); \ TCGv_i64 xal = tcg_temp_new_i64(); \ - tcg_gen_mov_i64(xah, cpu_vsrh(xA(ctx->opcode))); \ - tcg_gen_mov_i64(xal, cpu_vsrl(xA(ctx->opcode))); \ + get_cpu_vsrh(xah, xA(ctx->opcode)); \ + get_cpu_vsrl(xal, xA(ctx->opcode)); \ tcg_gen_and_i64(xah, xah, sgm); \ tcg_gen_and_i64(xal, xal, sgm); \ tcg_gen_andc_i64(xbh, xbh, sgm); \ @@ -736,8 +893,8 @@ static void glue(gen_, name)(DisasContext * ctx) \ break; \ } \ } \ - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xbh); \ - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xbl); \ + set_cpu_vsrh(xT(ctx->opcode), xbh); \ + set_cpu_vsrl(xT(ctx->opcode), xbl); \ tcg_temp_free_i64(xbh); \ tcg_temp_free_i64(xbl); \ tcg_temp_free_i64(sgm); \ @@ -768,12 +925,19 @@ static void gen_##name(DisasContext * ctx) \ #define GEN_VSX_HELPER_XT_XB_ENV(name, op1, op2, inval, type) \ static void gen_##name(DisasContext * ctx) \ { \ + TCGv_i64 t0; \ + TCGv_i64 t1; \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - gen_helper_##name(cpu_vsrh(xT(ctx->opcode)), cpu_env, \ - cpu_vsrh(xB(ctx->opcode))); \ + t0 = tcg_temp_new_i64(); \ + t1 = tcg_temp_new_i64(); \ + get_cpu_vsrh(t0, xB(ctx->opcode)); \ + gen_helper_##name(t1, cpu_env, t0); \ + set_cpu_vsrh(xT(ctx->opcode), t1); \ + tcg_temp_free_i64(t0); \ + tcg_temp_free_i64(t1); \ } GEN_VSX_HELPER_2(xsadddp, 0x00, 0x04, 0, PPC2_VSX) @@ -949,91 +1113,147 @@ GEN_VSX_HELPER_2(xxpermr, 0x08, 0x07, 0, PPC2_ISA300) static void gen_xxbrd(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + tcg_gen_bswap64_i64(xth, xbh); tcg_gen_bswap64_i64(xtl, xbl); + set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsrl(xT(ctx->opcode), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } static void gen_xxbrh(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + gen_bswap16x8(xth, xtl, xbh, xbl); + set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsrl(xT(ctx->opcode), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } static void gen_xxbrq(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); - TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; + TCGv_i64 t0; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + t0 = tcg_temp_new_i64(); + tcg_gen_bswap64_i64(t0, xbl); tcg_gen_bswap64_i64(xtl, xbh); + set_cpu_vsrl(xT(ctx->opcode), xtl); tcg_gen_mov_i64(xth, t0); + set_cpu_vsrl(xT(ctx->opcode), xth); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } static void gen_xxbrw(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + gen_bswap32x4(xth, xtl, xbh, xbl); + set_cpu_vsrl(xT(ctx->opcode), xth); + set_cpu_vsrl(xT(ctx->opcode), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } -#define VSX_LOGICAL(name, tcg_op) \ +#define VSX_LOGICAL(name, vece, tcg_op) \ static void glue(gen_, name)(DisasContext * ctx) \ { \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ } \ - tcg_op(cpu_vsrh(xT(ctx->opcode)), cpu_vsrh(xA(ctx->opcode)), \ - cpu_vsrh(xB(ctx->opcode))); \ - tcg_op(cpu_vsrl(xT(ctx->opcode)), cpu_vsrl(xA(ctx->opcode)), \ - cpu_vsrl(xB(ctx->opcode))); \ + tcg_op(vece, vsr_full_offset(xT(ctx->opcode)), \ + vsr_full_offset(xA(ctx->opcode)), \ + vsr_full_offset(xB(ctx->opcode)), 16, 16); \ } -VSX_LOGICAL(xxland, tcg_gen_and_i64) -VSX_LOGICAL(xxlandc, tcg_gen_andc_i64) -VSX_LOGICAL(xxlor, tcg_gen_or_i64) -VSX_LOGICAL(xxlxor, tcg_gen_xor_i64) -VSX_LOGICAL(xxlnor, tcg_gen_nor_i64) -VSX_LOGICAL(xxleqv, tcg_gen_eqv_i64) -VSX_LOGICAL(xxlnand, tcg_gen_nand_i64) -VSX_LOGICAL(xxlorc, tcg_gen_orc_i64) +VSX_LOGICAL(xxland, MO_64, tcg_gen_gvec_and) +VSX_LOGICAL(xxlandc, MO_64, tcg_gen_gvec_andc) +VSX_LOGICAL(xxlor, MO_64, tcg_gen_gvec_or) +VSX_LOGICAL(xxlxor, MO_64, tcg_gen_gvec_xor) +VSX_LOGICAL(xxlnor, MO_64, tcg_gen_gvec_nor) +VSX_LOGICAL(xxleqv, MO_64, tcg_gen_gvec_eqv) +VSX_LOGICAL(xxlnand, MO_64, tcg_gen_gvec_nand) +VSX_LOGICAL(xxlorc, MO_64, tcg_gen_gvec_orc) #define VSX_XXMRG(name, high) \ static void glue(gen_, name)(DisasContext * ctx) \ { \ - TCGv_i64 a0, a1, b0, b1; \ + TCGv_i64 a0, a1, b0, b1, tmp; \ if (unlikely(!ctx->vsx_enabled)) { \ gen_exception(ctx, POWERPC_EXCP_VSXU); \ return; \ @@ -1042,99 +1262,99 @@ static void glue(gen_, name)(DisasContext * ctx) \ a1 = tcg_temp_new_i64(); \ b0 = tcg_temp_new_i64(); \ b1 = tcg_temp_new_i64(); \ + tmp = tcg_temp_new_i64(); \ if (high) { \ - tcg_gen_mov_i64(a0, cpu_vsrh(xA(ctx->opcode))); \ - tcg_gen_mov_i64(a1, cpu_vsrh(xA(ctx->opcode))); \ - tcg_gen_mov_i64(b0, cpu_vsrh(xB(ctx->opcode))); \ - tcg_gen_mov_i64(b1, cpu_vsrh(xB(ctx->opcode))); \ + get_cpu_vsrh(a0, xA(ctx->opcode)); \ + get_cpu_vsrh(a1, xA(ctx->opcode)); \ + get_cpu_vsrh(b0, xB(ctx->opcode)); \ + get_cpu_vsrh(b1, xB(ctx->opcode)); \ } else { \ - tcg_gen_mov_i64(a0, cpu_vsrl(xA(ctx->opcode))); \ - tcg_gen_mov_i64(a1, cpu_vsrl(xA(ctx->opcode))); \ - tcg_gen_mov_i64(b0, cpu_vsrl(xB(ctx->opcode))); \ - tcg_gen_mov_i64(b1, cpu_vsrl(xB(ctx->opcode))); \ + get_cpu_vsrl(a0, xA(ctx->opcode)); \ + get_cpu_vsrl(a1, xA(ctx->opcode)); \ + get_cpu_vsrl(b0, xB(ctx->opcode)); \ + get_cpu_vsrl(b1, xB(ctx->opcode)); \ } \ tcg_gen_shri_i64(a0, a0, 32); \ tcg_gen_shri_i64(b0, b0, 32); \ - tcg_gen_deposit_i64(cpu_vsrh(xT(ctx->opcode)), \ - b0, a0, 32, 32); \ - tcg_gen_deposit_i64(cpu_vsrl(xT(ctx->opcode)), \ - b1, a1, 32, 32); \ + tcg_gen_deposit_i64(tmp, b0, a0, 32, 32); \ + set_cpu_vsrh(xT(ctx->opcode), tmp); \ + tcg_gen_deposit_i64(tmp, b1, a1, 32, 32); \ + set_cpu_vsrl(xT(ctx->opcode), tmp); \ tcg_temp_free_i64(a0); \ tcg_temp_free_i64(a1); \ tcg_temp_free_i64(b0); \ tcg_temp_free_i64(b1); \ + tcg_temp_free_i64(tmp); \ } VSX_XXMRG(xxmrghw, 1) VSX_XXMRG(xxmrglw, 0) -static void gen_xxsel(DisasContext * ctx) +static void xxsel_i64(TCGv_i64 t, TCGv_i64 a, TCGv_i64 b, TCGv_i64 c) { - TCGv_i64 a, b, c; + tcg_gen_and_i64(b, b, c); + tcg_gen_andc_i64(a, a, c); + tcg_gen_or_i64(t, a, b); +} + +static void xxsel_vec(unsigned vece, TCGv_vec t, TCGv_vec a, + TCGv_vec b, TCGv_vec c) +{ + tcg_gen_and_vec(vece, b, b, c); + tcg_gen_andc_vec(vece, a, a, c); + tcg_gen_or_vec(vece, t, a, b); +} + +static void gen_xxsel(DisasContext *ctx) +{ + static const GVecGen4 g = { + .fni8 = xxsel_i64, + .fniv = xxsel_vec, + .vece = MO_64, + }; + int rt = xT(ctx->opcode); + int ra = xA(ctx->opcode); + int rb = xB(ctx->opcode); + int rc = xC(ctx->opcode); + if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } - a = tcg_temp_new_i64(); - b = tcg_temp_new_i64(); - c = tcg_temp_new_i64(); - - tcg_gen_mov_i64(a, cpu_vsrh(xA(ctx->opcode))); - tcg_gen_mov_i64(b, cpu_vsrh(xB(ctx->opcode))); - tcg_gen_mov_i64(c, cpu_vsrh(xC(ctx->opcode))); - - tcg_gen_and_i64(b, b, c); - tcg_gen_andc_i64(a, a, c); - tcg_gen_or_i64(cpu_vsrh(xT(ctx->opcode)), a, b); - - tcg_gen_mov_i64(a, cpu_vsrl(xA(ctx->opcode))); - tcg_gen_mov_i64(b, cpu_vsrl(xB(ctx->opcode))); - tcg_gen_mov_i64(c, cpu_vsrl(xC(ctx->opcode))); - - tcg_gen_and_i64(b, b, c); - tcg_gen_andc_i64(a, a, c); - tcg_gen_or_i64(cpu_vsrl(xT(ctx->opcode)), a, b); - - tcg_temp_free_i64(a); - tcg_temp_free_i64(b); - tcg_temp_free_i64(c); + tcg_gen_gvec_4(vsr_full_offset(rt), vsr_full_offset(ra), + vsr_full_offset(rb), vsr_full_offset(rc), 16, 16, &g); } static void gen_xxspltw(DisasContext *ctx) { - TCGv_i64 b, b2; - TCGv_i64 vsr = (UIM(ctx->opcode) & 2) ? - cpu_vsrl(xB(ctx->opcode)) : - cpu_vsrh(xB(ctx->opcode)); + int rt = xT(ctx->opcode); + int rb = xB(ctx->opcode); + int uim = UIM(ctx->opcode); + int tofs, bofs; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } - b = tcg_temp_new_i64(); - b2 = tcg_temp_new_i64(); + tofs = vsr_full_offset(rt); + bofs = vsr_full_offset(rb); + bofs += uim << MO_32; +#ifndef HOST_WORDS_BIG_ENDIAN + bofs ^= 8 | 4; +#endif - if (UIM(ctx->opcode) & 1) { - tcg_gen_ext32u_i64(b, vsr); - } else { - tcg_gen_shri_i64(b, vsr, 32); - } - - tcg_gen_shli_i64(b2, b, 32); - tcg_gen_or_i64(cpu_vsrh(xT(ctx->opcode)), b, b2); - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), cpu_vsrh(xT(ctx->opcode))); - - tcg_temp_free_i64(b); - tcg_temp_free_i64(b2); + tcg_gen_gvec_dup_mem(MO_32, tofs, bofs, 16, 16); } #define pattern(x) (((x) & 0xff) * (~(uint64_t)0 / 0xff)) static void gen_xxspltib(DisasContext *ctx) { - unsigned char uim8 = IMM8(ctx->opcode); - if (xS(ctx->opcode) < 32) { + uint8_t uim8 = IMM8(ctx->opcode); + int rt = xT(ctx->opcode); + + if (rt < 32) { if (unlikely(!ctx->altivec_enabled)) { gen_exception(ctx, POWERPC_EXCP_VPU); return; @@ -1145,8 +1365,7 @@ static void gen_xxspltib(DisasContext *ctx) return; } } - tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), pattern(uim8)); - tcg_gen_movi_i64(cpu_vsrl(xT(ctx->opcode)), pattern(uim8)); + tcg_gen_gvec_dup8i(vsr_full_offset(rt), 16, 16, uim8); } static void gen_xxsldwi(DisasContext *ctx) @@ -1161,40 +1380,40 @@ static void gen_xxsldwi(DisasContext *ctx) switch (SHW(ctx->opcode)) { case 0: { - tcg_gen_mov_i64(xth, cpu_vsrh(xA(ctx->opcode))); - tcg_gen_mov_i64(xtl, cpu_vsrl(xA(ctx->opcode))); + get_cpu_vsrh(xth, xA(ctx->opcode)); + get_cpu_vsrl(xtl, xA(ctx->opcode)); break; } case 1: { TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_mov_i64(xth, cpu_vsrh(xA(ctx->opcode))); + get_cpu_vsrh(xth, xA(ctx->opcode)); tcg_gen_shli_i64(xth, xth, 32); - tcg_gen_mov_i64(t0, cpu_vsrl(xA(ctx->opcode))); + get_cpu_vsrl(t0, xA(ctx->opcode)); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xth, xth, t0); - tcg_gen_mov_i64(xtl, cpu_vsrl(xA(ctx->opcode))); + get_cpu_vsrl(xtl, xA(ctx->opcode)); tcg_gen_shli_i64(xtl, xtl, 32); - tcg_gen_mov_i64(t0, cpu_vsrh(xB(ctx->opcode))); + get_cpu_vsrh(t0, xB(ctx->opcode)); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xtl, xtl, t0); tcg_temp_free_i64(t0); break; } case 2: { - tcg_gen_mov_i64(xth, cpu_vsrl(xA(ctx->opcode))); - tcg_gen_mov_i64(xtl, cpu_vsrh(xB(ctx->opcode))); + get_cpu_vsrl(xth, xA(ctx->opcode)); + get_cpu_vsrh(xtl, xB(ctx->opcode)); break; } case 3: { TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_mov_i64(xth, cpu_vsrl(xA(ctx->opcode))); + get_cpu_vsrl(xth, xA(ctx->opcode)); tcg_gen_shli_i64(xth, xth, 32); - tcg_gen_mov_i64(t0, cpu_vsrh(xB(ctx->opcode))); + get_cpu_vsrh(t0, xB(ctx->opcode)); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xth, xth, t0); - tcg_gen_mov_i64(xtl, cpu_vsrh(xB(ctx->opcode))); + get_cpu_vsrh(xtl, xB(ctx->opcode)); tcg_gen_shli_i64(xtl, xtl, 32); - tcg_gen_mov_i64(t0, cpu_vsrl(xB(ctx->opcode))); + get_cpu_vsrl(t0, xB(ctx->opcode)); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_or_i64(xtl, xtl, t0); tcg_temp_free_i64(t0); @@ -1202,8 +1421,8 @@ static void gen_xxsldwi(DisasContext *ctx) } } - tcg_gen_mov_i64(cpu_vsrh(xT(ctx->opcode)), xth); - tcg_gen_mov_i64(cpu_vsrl(xT(ctx->opcode)), xtl); + set_cpu_vsrh(xT(ctx->opcode), xth); + set_cpu_vsrl(xT(ctx->opcode), xtl); tcg_temp_free_i64(xth); tcg_temp_free_i64(xtl); @@ -1213,7 +1432,8 @@ static void gen_xxsldwi(DisasContext *ctx) static void gen_##name(DisasContext *ctx) \ { \ TCGv xt, xb; \ - TCGv_i32 t0 = tcg_temp_new_i32(); \ + TCGv_i32 t0; \ + TCGv_i64 t1; \ uint8_t uimm = UIMM4(ctx->opcode); \ \ if (unlikely(!ctx->vsx_enabled)) { \ @@ -1222,12 +1442,15 @@ static void gen_##name(DisasContext *ctx) \ } \ xt = tcg_const_tl(xT(ctx->opcode)); \ xb = tcg_const_tl(xB(ctx->opcode)); \ + t0 = tcg_temp_new_i32(); \ + t1 = tcg_temp_new_i64(); \ /* uimm > 15 out of bound and for \ * uimm > 12 handle as per hardware in helper \ */ \ if (uimm > 15) { \ - tcg_gen_movi_i64(cpu_vsrh(xT(ctx->opcode)), 0); \ - tcg_gen_movi_i64(cpu_vsrl(xT(ctx->opcode)), 0); \ + tcg_gen_movi_i64(t1, 0); \ + set_cpu_vsrh(xT(ctx->opcode), t1); \ + set_cpu_vsrl(xT(ctx->opcode), t1); \ return; \ } \ tcg_gen_movi_i32(t0, uimm); \ @@ -1235,6 +1458,7 @@ static void gen_##name(DisasContext *ctx) \ tcg_temp_free(xb); \ tcg_temp_free(xt); \ tcg_temp_free_i32(t0); \ + tcg_temp_free_i64(t1); \ } VSX_EXTRACT_INSERT(xxextractuw) @@ -1244,30 +1468,45 @@ VSX_EXTRACT_INSERT(xxinsertw) static void gen_xsxexpdp(DisasContext *ctx) { TCGv rt = cpu_gpr[rD(ctx->opcode)]; + TCGv_i64 t0; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } - tcg_gen_extract_i64(rt, cpu_vsrh(xB(ctx->opcode)), 52, 11); + t0 = tcg_temp_new_i64(); + get_cpu_vsrh(t0, xB(ctx->opcode)); + tcg_gen_extract_i64(rt, t0, 52, 11); + tcg_temp_free_i64(t0); } static void gen_xsxexpqp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); - TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32); - TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, rB(ctx->opcode) + 32); + tcg_gen_extract_i64(xth, xbh, 48, 15); + set_cpu_vsrh(rD(ctx->opcode) + 32, xth); tcg_gen_movi_i64(xtl, 0); + set_cpu_vsrl(rD(ctx->opcode) + 32, xtl); + + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); } static void gen_xsiexpdp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); + TCGv_i64 xth; TCGv ra = cpu_gpr[rA(ctx->opcode)]; TCGv rb = cpu_gpr[rB(ctx->opcode)]; TCGv_i64 t0; @@ -1277,40 +1516,60 @@ static void gen_xsiexpdp(DisasContext *ctx) return; } t0 = tcg_temp_new_i64(); + xth = tcg_temp_new_i64(); tcg_gen_andi_i64(xth, ra, 0x800FFFFFFFFFFFFF); tcg_gen_andi_i64(t0, rb, 0x7FF); tcg_gen_shli_i64(t0, t0, 52); tcg_gen_or_i64(xth, xth, t0); + set_cpu_vsrh(xT(ctx->opcode), xth); /* dword[1] is undefined */ tcg_temp_free_i64(t0); + tcg_temp_free_i64(xth); } static void gen_xsiexpqp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); - TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32); - TCGv_i64 xah = cpu_vsrh(rA(ctx->opcode) + 32); - TCGv_i64 xal = cpu_vsrl(rA(ctx->opcode) + 32); - TCGv_i64 xbh = cpu_vsrh(rB(ctx->opcode) + 32); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xah; + TCGv_i64 xal; + TCGv_i64 xbh; TCGv_i64 t0; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xah = tcg_temp_new_i64(); + xal = tcg_temp_new_i64(); + get_cpu_vsrh(xah, rA(ctx->opcode) + 32); + get_cpu_vsrl(xal, rA(ctx->opcode) + 32); + xbh = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, rB(ctx->opcode) + 32); t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(xth, xah, 0x8000FFFFFFFFFFFF); tcg_gen_andi_i64(t0, xbh, 0x7FFF); tcg_gen_shli_i64(t0, t0, 48); tcg_gen_or_i64(xth, xth, t0); + set_cpu_vsrh(rD(ctx->opcode) + 32, xth); tcg_gen_mov_i64(xtl, xal); + set_cpu_vsrl(rD(ctx->opcode) + 32, xtl); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xah); + tcg_temp_free_i64(xal); + tcg_temp_free_i64(xbh); } static void gen_xsxsigdp(DisasContext *ctx) { TCGv rt = cpu_gpr[rD(ctx->opcode)]; - TCGv_i64 t0, zr, nan, exp; + TCGv_i64 t0, t1, zr, nan, exp; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); @@ -1318,17 +1577,20 @@ static void gen_xsxsigdp(DisasContext *ctx) } exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); zr = tcg_const_i64(0); nan = tcg_const_i64(2047); - tcg_gen_extract_i64(exp, cpu_vsrh(xB(ctx->opcode)), 52, 11); + get_cpu_vsrh(t1, xB(ctx->opcode)); + tcg_gen_extract_i64(exp, t1, 52, 11); tcg_gen_movi_i64(t0, 0x0010000000000000); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); - tcg_gen_andi_i64(rt, cpu_vsrh(xB(ctx->opcode)), 0x000FFFFFFFFFFFFF); - tcg_gen_or_i64(rt, rt, t0); + get_cpu_vsrh(t1, xB(ctx->opcode)); + tcg_gen_deposit_i64(rt, t0, t1, 0, 52); tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); tcg_temp_free_i64(exp); tcg_temp_free_i64(zr); tcg_temp_free_i64(nan); @@ -1337,132 +1599,210 @@ static void gen_xsxsigdp(DisasContext *ctx) static void gen_xsxsigqp(DisasContext *ctx) { TCGv_i64 t0, zr, nan, exp; - TCGv_i64 xth = cpu_vsrh(rD(ctx->opcode) + 32); - TCGv_i64 xtl = cpu_vsrl(rD(ctx->opcode) + 32); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, rB(ctx->opcode) + 32); + get_cpu_vsrl(xbl, rB(ctx->opcode) + 32); exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); zr = tcg_const_i64(0); nan = tcg_const_i64(32767); - tcg_gen_extract_i64(exp, cpu_vsrh(rB(ctx->opcode) + 32), 48, 15); + tcg_gen_extract_i64(exp, xbh, 48, 15); tcg_gen_movi_i64(t0, 0x0001000000000000); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); - tcg_gen_andi_i64(xth, cpu_vsrh(rB(ctx->opcode) + 32), 0x0000FFFFFFFFFFFF); - tcg_gen_or_i64(xth, xth, t0); - tcg_gen_mov_i64(xtl, cpu_vsrl(rB(ctx->opcode) + 32)); + tcg_gen_deposit_i64(xth, t0, xbh, 0, 48); + set_cpu_vsrh(rD(ctx->opcode) + 32, xth); + tcg_gen_mov_i64(xtl, xbl); + set_cpu_vsrl(rD(ctx->opcode) + 32, xtl); tcg_temp_free_i64(t0); tcg_temp_free_i64(exp); tcg_temp_free_i64(zr); tcg_temp_free_i64(nan); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } #endif static void gen_xviexpsp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode)); - TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xah; + TCGv_i64 xal; + TCGv_i64 xbh; + TCGv_i64 xbl; TCGv_i64 t0; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xah = tcg_temp_new_i64(); + xal = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xah, xA(ctx->opcode)); + get_cpu_vsrl(xal, xA(ctx->opcode)); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(xth, xah, 0x807FFFFF807FFFFF); tcg_gen_andi_i64(t0, xbh, 0xFF000000FF); tcg_gen_shli_i64(t0, t0, 23); tcg_gen_or_i64(xth, xth, t0); + set_cpu_vsrh(xT(ctx->opcode), xth); tcg_gen_andi_i64(xtl, xal, 0x807FFFFF807FFFFF); tcg_gen_andi_i64(t0, xbl, 0xFF000000FF); tcg_gen_shli_i64(t0, t0, 23); tcg_gen_or_i64(xtl, xtl, t0); + set_cpu_vsrl(xT(ctx->opcode), xtl); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xah); + tcg_temp_free_i64(xal); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } static void gen_xviexpdp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xah = cpu_vsrh(xA(ctx->opcode)); - TCGv_i64 xal = cpu_vsrl(xA(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); - TCGv_i64 t0; + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xah; + TCGv_i64 xal; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } - t0 = tcg_temp_new_i64(); - tcg_gen_andi_i64(xth, xah, 0x800FFFFFFFFFFFFF); - tcg_gen_andi_i64(t0, xbh, 0x7FF); - tcg_gen_shli_i64(t0, t0, 52); - tcg_gen_or_i64(xth, xth, t0); - tcg_gen_andi_i64(xtl, xal, 0x800FFFFFFFFFFFFF); - tcg_gen_andi_i64(t0, xbl, 0x7FF); - tcg_gen_shli_i64(t0, t0, 52); - tcg_gen_or_i64(xtl, xtl, t0); - tcg_temp_free_i64(t0); + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xah = tcg_temp_new_i64(); + xal = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xah, xA(ctx->opcode)); + get_cpu_vsrl(xal, xA(ctx->opcode)); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + + tcg_gen_deposit_i64(xth, xah, xbh, 52, 11); + set_cpu_vsrh(xT(ctx->opcode), xth); + + tcg_gen_deposit_i64(xtl, xal, xbl, 52, 11); + set_cpu_vsrl(xT(ctx->opcode), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xah); + tcg_temp_free_i64(xal); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } static void gen_xvxexpsp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + tcg_gen_shri_i64(xth, xbh, 23); tcg_gen_andi_i64(xth, xth, 0xFF000000FF); + set_cpu_vsrh(xT(ctx->opcode), xth); tcg_gen_shri_i64(xtl, xbl, 23); tcg_gen_andi_i64(xtl, xtl, 0xFF000000FF); + set_cpu_vsrl(xT(ctx->opcode), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } static void gen_xvxexpdp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); + tcg_gen_extract_i64(xth, xbh, 52, 11); + set_cpu_vsrh(xT(ctx->opcode), xth); tcg_gen_extract_i64(xtl, xbl, 52, 11); + set_cpu_vsrl(xT(ctx->opcode), xtl); + + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } GEN_VSX_HELPER_2(xvxsigsp, 0x00, 0x04, 0, PPC2_ISA300) static void gen_xvxsigdp(DisasContext *ctx) { - TCGv_i64 xth = cpu_vsrh(xT(ctx->opcode)); - TCGv_i64 xtl = cpu_vsrl(xT(ctx->opcode)); - TCGv_i64 xbh = cpu_vsrh(xB(ctx->opcode)); - TCGv_i64 xbl = cpu_vsrl(xB(ctx->opcode)); - + TCGv_i64 xth; + TCGv_i64 xtl; + TCGv_i64 xbh; + TCGv_i64 xbl; TCGv_i64 t0, zr, nan, exp; if (unlikely(!ctx->vsx_enabled)) { gen_exception(ctx, POWERPC_EXCP_VSXU); return; } + xth = tcg_temp_new_i64(); + xtl = tcg_temp_new_i64(); + xbh = tcg_temp_new_i64(); + xbl = tcg_temp_new_i64(); + get_cpu_vsrh(xbh, xB(ctx->opcode)); + get_cpu_vsrl(xbl, xB(ctx->opcode)); exp = tcg_temp_new_i64(); t0 = tcg_temp_new_i64(); zr = tcg_const_i64(0); @@ -1472,20 +1812,24 @@ static void gen_xvxsigdp(DisasContext *ctx) tcg_gen_movi_i64(t0, 0x0010000000000000); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); - tcg_gen_andi_i64(xth, xbh, 0x000FFFFFFFFFFFFF); - tcg_gen_or_i64(xth, xth, t0); + tcg_gen_deposit_i64(xth, t0, xbh, 0, 52); + set_cpu_vsrh(xT(ctx->opcode), xth); tcg_gen_extract_i64(exp, xbl, 52, 11); tcg_gen_movi_i64(t0, 0x0010000000000000); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, zr, zr, t0); tcg_gen_movcond_i64(TCG_COND_EQ, t0, exp, nan, zr, t0); - tcg_gen_andi_i64(xtl, xbl, 0x000FFFFFFFFFFFFF); - tcg_gen_or_i64(xtl, xtl, t0); + tcg_gen_deposit_i64(xth, t0, xbl, 0, 52); + set_cpu_vsrl(xT(ctx->opcode), xtl); tcg_temp_free_i64(t0); tcg_temp_free_i64(exp); tcg_temp_free_i64(zr); tcg_temp_free_i64(nan); + tcg_temp_free_i64(xth); + tcg_temp_free_i64(xtl); + tcg_temp_free_i64(xbh); + tcg_temp_free_i64(xbl); } #undef GEN_XX2FORM diff --git a/target/ppc/translate_init.inc.c b/target/ppc/translate_init.inc.c index 7813b1b004..0bd555eb19 100644 --- a/target/ppc/translate_init.inc.c +++ b/target/ppc/translate_init.inc.c @@ -38,6 +38,7 @@ #include "qemu/cutils.h" #include "disas/capstone.h" #include "fpu/softfloat.h" +#include "qapi/qapi-commands-target.h" //#define PPC_DUMP_CPU //#define PPC_DEBUG_SPR @@ -407,6 +408,11 @@ static void spr_write_pidr(DisasContext *ctx, int sprn, int gprn) gen_helper_store_pidr(cpu_env, cpu_gpr[gprn]); } +static void spr_write_lpidr(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_store_lpidr(cpu_env, cpu_gpr[gprn]); +} + static void spr_read_hior(DisasContext *ctx, int gprn, int sprn) { tcg_gen_ld_tl(cpu_gpr[gprn], cpu_env, offsetof(CPUPPCState, excp_prefix)); @@ -498,6 +504,7 @@ static void spr_write_40x_pit(DisasContext *ctx, int sprn, int gprn) static void spr_write_40x_dbcr0(DisasContext *ctx, int sprn, int gprn) { + gen_store_spr(sprn, cpu_gpr[gprn]); gen_helper_store_40x_dbcr0(cpu_env, cpu_gpr[gprn]); /* We must stop translation as we may have rebooted */ gen_stop_exception(ctx); @@ -600,10 +607,9 @@ static void spr_write_excp_vector(DisasContext *ctx, int sprn, int gprn) static inline void vscr_init(CPUPPCState *env, uint32_t val) { - env->vscr = val; /* Altivec always uses round-to-nearest */ set_float_rounding_mode(float_round_nearest_even, &env->vec_status); - set_flush_to_zero(vscr_nj, &env->vec_status); + helper_mtvscr(env, val); } #ifdef CONFIG_USER_ONLY @@ -1652,6 +1658,15 @@ static void spr_write_booke_pid(DisasContext *ctx, int sprn, int gprn) gen_helper_booke_setpid(cpu_env, t0, cpu_gpr[gprn]); tcg_temp_free_i32(t0); } +static void spr_write_eplc(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_booke_set_eplc(cpu_env, cpu_gpr[gprn]); +} +static void spr_write_epsc(DisasContext *ctx, int sprn, int gprn) +{ + gen_helper_booke_set_epsc(cpu_env, cpu_gpr[gprn]); +} + #endif static void gen_spr_usprg3(CPUPPCState *env) @@ -1769,6 +1784,14 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + spr_register(env, SPR_BOOKE_DSRR0, "DSRR0", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_DSRR1, "DSRR1", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); /* XXX : not implemented */ spr_register(env, SPR_BOOKE_DBSR, "DBSR", SPR_NOACCESS, SPR_NOACCESS, @@ -1841,6 +1864,14 @@ static void gen_spr_BookE(CPUPPCState *env, uint64_t ivor_mask) SPR_NOACCESS, SPR_NOACCESS, &spr_read_generic, &spr_write_generic, 0x00000000); + spr_register(env, SPR_BOOKE_SPRG8, "SPRG8", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); + spr_register(env, SPR_BOOKE_SPRG9, "SPRG9", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_generic, + 0x00000000); } static inline uint32_t gen_tlbncfg(uint32_t assoc, uint32_t minsize, @@ -1895,6 +1926,16 @@ static void gen_spr_BookE206(CPUPPCState *env, uint32_t mas_mask, &spr_read_generic, &spr_write_booke_pid, 0x00000000); } + + spr_register(env, SPR_BOOKE_EPLC, "EPLC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_eplc, + 0x00000000); + spr_register(env, SPR_BOOKE_EPSC, "EPSC", + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_epsc, + 0x00000000); + /* XXX : not implemented */ spr_register(env, SPR_MMUCFG, "MMUCFG", SPR_NOACCESS, SPR_NOACCESS, @@ -2780,8 +2821,6 @@ static void gen_spr_8xx(CPUPPCState *env) * perf => 768-783 (Power 2.04) * perf => 784-799 (Power 2.04) * PPR => SPR 896 (Power 2.04) - * EPLC => SPR 947 (Power 2.04 emb) - * EPSC => SPR 948 (Power 2.04 emb) * DABRX => 1015 (Power 2.04 hypv) * FPECR => SPR 1022 (?) * ... and more (thermal management, performance counters, ...) @@ -3279,6 +3318,15 @@ static void init_excp_POWER8(CPUPPCState *env) #endif } +static void init_excp_POWER9(CPUPPCState *env) +{ + init_excp_POWER8(env); + +#if !defined(CONFIG_USER_ONLY) + env->excp_vectors[POWERPC_EXCP_HVIRT] = 0x00000EA0; +#endif +} + #endif /*****************************************************************************/ @@ -4913,14 +4961,6 @@ static void init_proc_e500(CPUPPCState *env, int version) } if (version == fsl_e6500) { - spr_register(env, SPR_BOOKE_SPRG8, "SPRG8", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); - spr_register(env, SPR_BOOKE_SPRG9, "SPRG9", - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, - 0x00000000); /* Thread identification */ spr_register(env, SPR_TIR, "TIR", SPR_NOACCESS, SPR_NOACCESS, @@ -7850,7 +7890,7 @@ static void gen_spr_book3s_ids(CPUPPCState *env) spr_register_hv(env, SPR_LPIDR, "LPIDR", SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_generic, + &spr_read_generic, &spr_write_lpidr, 0x00000000); spr_register_hv(env, SPR_HFSCR, "HFSCR", SPR_NOACCESS, SPR_NOACCESS, @@ -8180,11 +8220,11 @@ static void gen_spr_power9_mmu(CPUPPCState *env) { #if !defined(CONFIG_USER_ONLY) /* Partition Table Control */ - spr_register_hv(env, SPR_PTCR, "PTCR", - SPR_NOACCESS, SPR_NOACCESS, - SPR_NOACCESS, SPR_NOACCESS, - &spr_read_generic, &spr_write_ptcr, - 0x00000000); + spr_register_kvm_hv(env, SPR_PTCR, "PTCR", + SPR_NOACCESS, SPR_NOACCESS, + SPR_NOACCESS, SPR_NOACCESS, + &spr_read_generic, &spr_write_ptcr, + KVM_REG_PPC_PTCR, 0x00000000); #endif } @@ -8336,6 +8376,7 @@ POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_basic; + pcc->lrg_decr_bits = 32; #endif pcc->excp_model = POWERPC_EXCP_970; pcc->bus_model = PPC_FLAGS_INPUT_970; @@ -8364,8 +8405,8 @@ static void getset_compat_deprecated(Object *obj, Visitor *v, const char *name, QNull *null = NULL; if (!qtest_enabled()) { - error_report("CPU 'compat' property is deprecated and has no effect; " - "use max-cpu-compat machine property instead"); + warn_report("CPU 'compat' property is deprecated and has no effect; " + "use max-cpu-compat machine property instead"); } visit_type_null(v, name, &null, NULL); qobject_unref(null); @@ -8510,6 +8551,7 @@ POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_POWER7; + pcc->lrg_decr_bits = 32; #endif pcc->excp_model = POWERPC_EXCP_POWER7; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8678,6 +8720,7 @@ POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) #if defined(CONFIG_SOFTMMU) pcc->handle_mmu_fault = ppc_hash64_handle_mmu_fault; pcc->hash64_opts = &ppc_hash64_opts_POWER7; + pcc->lrg_decr_bits = 32; #endif pcc->excp_model = POWERPC_EXCP_POWER8; pcc->bus_model = PPC_FLAGS_INPUT_POWER7; @@ -8757,8 +8800,8 @@ static void init_proc_POWER9(CPUPPCState *env) env->icache_line_size = 128; /* Allocate hardware IRQ controller */ - init_excp_POWER8(env); - ppcPOWER7_irq_init(ppc_env_get_cpu(env)); + init_excp_POWER9(env); + ppcPOWER9_irq_init(ppc_env_get_cpu(env)); } static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr) @@ -8775,13 +8818,23 @@ static bool cpu_has_work_POWER9(CPUState *cs) CPUPPCState *env = &cpu->env; if (cs->halted) { + uint64_t psscr = env->spr[SPR_PSSCR]; + if (!(cs->interrupt_request & CPU_INTERRUPT_HARD)) { return false; } + + /* If EC is clear, just return true on any pending interrupt */ + if (!(psscr & PSSCR_EC)) { + return true; + } /* External Exception */ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_EXT)) && (env->spr[SPR_LPCR] & LPCR_EEE)) { - return true; + bool heic = !!(env->spr[SPR_LPCR] & LPCR_HEIC); + if (heic == 0 || !msr_hv || msr_pr) { + return true; + } } /* Decrementer Exception */ if ((env->pending_interrupts & (1u << PPC_INTERRUPT_DECR)) && @@ -8803,6 +8856,11 @@ static bool cpu_has_work_POWER9(CPUState *cs) (env->spr[SPR_LPCR] & LPCR_HDEE)) { return true; } + /* Hypervisor virtualization exception */ + if ((env->pending_interrupts & (1u << PPC_INTERRUPT_HVIRT)) && + (env->spr[SPR_LPCR] & LPCR_HVEE)) { + return true; + } if (env->pending_interrupts & (1u << PPC_INTERRUPT_RESET)) { return true; } @@ -8837,7 +8895,7 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC_CACHE | PPC_CACHE_ICBI | PPC_CACHE_DCBZ | PPC_MEM_SYNC | PPC_MEM_EIEIO | PPC_MEM_TLBSYNC | - PPC_64B | PPC_64BX | PPC_ALTIVEC | + PPC_64B | PPC_64H | PPC_64BX | PPC_ALTIVEC | PPC_SEGMENT_64B | PPC_SLBI | PPC_POPCNTB | PPC_POPCNTWD | PPC_CILDST; @@ -8847,8 +8905,9 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) PPC2_FP_TST_ISA206 | PPC2_BCTAR_ISA207 | PPC2_LSQ_ISA207 | PPC2_ALTIVEC_207 | PPC2_ISA205 | PPC2_ISA207S | PPC2_FP_CVT_S64 | - PPC2_TM | PPC2_PM_ISA206 | PPC2_ISA300 | PPC2_PRCNTL; + PPC2_TM | PPC2_ISA300 | PPC2_PRCNTL; pcc->msr_mask = (1ull << MSR_SF) | + (1ull << MSR_SHV) | (1ull << MSR_TM) | (1ull << MSR_VR) | (1ull << MSR_VSX) | @@ -8871,9 +8930,10 @@ POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) /* segment page size remain the same */ pcc->hash64_opts = &ppc_hash64_opts_POWER7; pcc->radix_page_info = &POWER9_radix_page_info; + pcc->lrg_decr_bits = 56; #endif - pcc->excp_model = POWERPC_EXCP_POWER8; - pcc->bus_model = PPC_FLAGS_INPUT_POWER7; + pcc->excp_model = POWERPC_EXCP_POWER9; + pcc->bus_model = PPC_FLAGS_INPUT_POWER9; pcc->bfd_mach = bfd_mach_ppc64; pcc->flags = POWERPC_FLAG_VRE | POWERPC_FLAG_SE | POWERPC_FLAG_BE | POWERPC_FLAG_PMM | @@ -8953,6 +9013,10 @@ static void init_ppc_proc(PowerPCCPU *cpu) /* PowerPC implementation specific initialisations (SPRs, timers, ...) */ (*pcc->init_proc)(env); +#if !defined(CONFIG_USER_ONLY) + ppc_gdb_gen_spr_xml(cpu); +#endif + /* MSR bits & flags consistency checks */ if (env->msr_mask & (1 << 25)) { switch (env->flags & (POWERPC_FLAG_SPE | POWERPC_FLAG_VRE)) { @@ -9047,13 +9111,13 @@ static void init_ppc_proc(PowerPCCPU *cpu) nb_tlb *= 2; switch (env->tlb_type) { case TLB_6XX: - env->tlb.tlb6 = g_malloc0(nb_tlb * sizeof(ppc6xx_tlb_t)); + env->tlb.tlb6 = g_new0(ppc6xx_tlb_t, nb_tlb); break; case TLB_EMB: - env->tlb.tlbe = g_malloc0(nb_tlb * sizeof(ppcemb_tlb_t)); + env->tlb.tlbe = g_new0(ppcemb_tlb_t, nb_tlb); break; case TLB_MAS: - env->tlb.tlbm = g_malloc0(nb_tlb * sizeof(ppcmas_tlb_t)); + env->tlb.tlbm = g_new0(ppcmas_tlb_t, nb_tlb); break; } /* Pre-compute some useful values */ @@ -9449,10 +9513,59 @@ static bool avr_need_swap(CPUPPCState *env) #endif } +#if !defined(CONFIG_USER_ONLY) +static int gdb_find_spr_idx(CPUPPCState *env, int n) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(env->spr_cb); i++) { + ppc_spr_t *spr = &env->spr_cb[i]; + + if (spr->name && spr->gdb_id == n) { + return i; + } + } + return -1; +} + +static int gdb_get_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + int reg; + int len; + + reg = gdb_find_spr_idx(env, n); + if (reg < 0) { + return 0; + } + + len = TARGET_LONG_SIZE; + stn_p(mem_buf, len, env->spr[reg]); + ppc_maybe_bswap_register(env, mem_buf, len); + return len; +} + +static int gdb_set_spr_reg(CPUPPCState *env, uint8_t *mem_buf, int n) +{ + int reg; + int len; + + reg = gdb_find_spr_idx(env, n); + if (reg < 0) { + return 0; + } + + len = TARGET_LONG_SIZE; + ppc_maybe_bswap_register(env, mem_buf, len); + env->spr[reg] = ldn_p(mem_buf, len); + + return len; +} +#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]); + stfq_p(mem_buf, *cpu_fpr_ptr(env, n)); ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } @@ -9468,7 +9581,7 @@ 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); + *cpu_fpr_ptr(env, n) = ldfq_p(mem_buf); return 8; } if (n == 32) { @@ -9482,19 +9595,20 @@ 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) { + ppc_avr_t *avr = cpu_avr_ptr(env, n); if (!avr_need_swap(env)) { - stq_p(mem_buf, env->avr[n].u64[0]); - stq_p(mem_buf+8, env->avr[n].u64[1]); + stq_p(mem_buf, avr->u64[0]); + stq_p(mem_buf + 8, avr->u64[1]); } else { - stq_p(mem_buf, env->avr[n].u64[1]); - stq_p(mem_buf+8, env->avr[n].u64[0]); + stq_p(mem_buf, avr->u64[1]); + stq_p(mem_buf + 8, avr->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); + stl_p(mem_buf, helper_mfvscr(env)); ppc_maybe_bswap_register(env, mem_buf, 4); return 4; } @@ -9509,20 +9623,21 @@ 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) { + ppc_avr_t *avr = cpu_avr_ptr(env, n); 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); + avr->u64[0] = ldq_p(mem_buf); + avr->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); + avr->u64[1] = ldq_p(mem_buf); + avr->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); + helper_mtvscr(env, ldl_p(mem_buf)); return 4; } if (n == 33) { @@ -9589,7 +9704,7 @@ static int gdb_set_spe_reg(CPUPPCState *env, uint8_t *mem_buf, int n) static int gdb_get_vsx_reg(CPUPPCState *env, uint8_t *mem_buf, int n) { if (n < 32) { - stq_p(mem_buf, env->vsr[n]); + stq_p(mem_buf, *cpu_vsrl_ptr(env, n)); ppc_maybe_bswap_register(env, mem_buf, 8); return 8; } @@ -9600,7 +9715,7 @@ 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); + *cpu_vsrl_ptr(env, n) = ldq_p(mem_buf); return 8; } return 0; @@ -9630,17 +9745,6 @@ static int ppc_fixup_cpu(PowerPCCPU *cpu) return 0; } -static inline bool ppc_cpu_is_valid(PowerPCCPUClass *pcc) -{ -#ifdef TARGET_PPCEMB - return pcc->mmu_model == POWERPC_MMU_BOOKE || - pcc->mmu_model == POWERPC_MMU_SOFT_4xx || - pcc->mmu_model == POWERPC_MMU_SOFT_4xx_Z; -#else - return true; -#endif -} - static void ppc_cpu_realize(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); @@ -9664,8 +9768,6 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) } } - assert(ppc_cpu_is_valid(pcc)); - create_ppc_opcodes(cpu, &local_err); if (local_err != NULL) { error_propagate(errp, local_err); @@ -9689,7 +9791,10 @@ static void ppc_cpu_realize(DeviceState *dev, Error **errp) gdb_register_coprocessor(cs, gdb_get_vsx_reg, gdb_set_vsx_reg, 32, "power-vsx.xml", 0); } - +#ifndef CONFIG_USER_ONLY + gdb_register_coprocessor(cs, gdb_get_spr_reg, gdb_set_spr_reg, + pcc->gdb_num_sprs, "power-spr.xml", 0); +#endif qemu_init_vcpu(cs); pcc->parent_realize(dev, errp); @@ -9916,10 +10021,6 @@ static gint ppc_cpu_compare_class_pvr(gconstpointer a, gconstpointer b) return -1; } - if (!ppc_cpu_is_valid(pcc)) { - return -1; - } - return pcc->pvr == pvr ? 0 : -1; } @@ -9950,10 +10051,6 @@ static gint ppc_cpu_compare_class_pvr_mask(gconstpointer a, gconstpointer b) return -1; } - if (!ppc_cpu_is_valid(pcc)) { - return -1; - } - if (pcc->pvr_match(pcc, pvr)) { return 0; } @@ -10019,11 +10116,7 @@ static ObjectClass *ppc_cpu_class_by_name(const char *name) g_free(typename); g_free(cpu_model); - if (oc && ppc_cpu_is_valid(POWERPC_CPU_CLASS(oc))) { - return oc; - } - - return NULL; + return oc; } static void ppc_cpu_parse_featurestr(const char *type, char *features, @@ -10129,9 +10222,6 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) char *name; int i; - if (!ppc_cpu_is_valid(pcc)) { - return; - } if (unlikely(strcmp(typename, TYPE_HOST_POWERPC_CPU) == 0)) { return; } @@ -10189,11 +10279,6 @@ static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) const char *typename; CpuDefinitionInfoList *entry; CpuDefinitionInfo *info; - PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); - - if (!ppc_cpu_is_valid(pcc)) { - return; - } typename = object_class_get_name(oc); info = g_malloc0(sizeof(*info)); @@ -10206,7 +10291,7 @@ static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) *first = entry; } -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) { CpuDefinitionInfoList *cpu_list = NULL; GSList *list; @@ -10278,6 +10363,8 @@ static void ppc_cpu_reset(CPUState *s) #endif #if defined(CONFIG_USER_ONLY) msr |= (target_ulong)1 << MSR_FP; /* Allow floating point usage */ + msr |= (target_ulong)1 << MSR_FE0; /* Allow floating point exceptions */ + msr |= (target_ulong)1 << MSR_FE1; msr |= (target_ulong)1 << MSR_VR; /* Allow altivec usage */ msr |= (target_ulong)1 << MSR_VSX; /* Allow VSX usage */ msr |= (target_ulong)1 << MSR_SPE; /* Allow SPE usage */ @@ -10470,7 +10557,9 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) #endif cc->gdb_num_core_regs = 71; - +#ifndef CONFIG_USER_ONLY + cc->gdb_get_dynamic_xml = ppc_gdb_get_dynamic_xml; +#endif #ifdef USE_APPLE_GDB cc->gdb_read_register = ppc_cpu_gdb_read_register_apple; cc->gdb_write_register = ppc_cpu_gdb_write_register_apple; diff --git a/target/riscv/Makefile.objs b/target/riscv/Makefile.objs index abd0a7cde3..9c6c109327 100644 --- a/target/riscv/Makefile.objs +++ b/target/riscv/Makefile.objs @@ -1 +1,20 @@ -obj-y += translate.o op_helper.o helper.o cpu.o fpu_helper.o gdbstub.o pmp.o +obj-y += translate.o op_helper.o cpu_helper.o cpu.o csr.o fpu_helper.o gdbstub.o pmp.o + +DECODETREE = $(SRC_PATH)/scripts/decodetree.py + +decode32-y = $(SRC_PATH)/target/riscv/insn32.decode +decode32-$(TARGET_RISCV64) += $(SRC_PATH)/target/riscv/insn32-64.decode + +target/riscv/decode_insn32.inc.c: $(decode32-y) $(DECODETREE) + $(call quiet-command, \ + $(PYTHON) $(DECODETREE) -o $@ --decode decode_insn32 $(decode32-y), \ + "GEN", $(TARGET_DIR)$@) + +target/riscv/decode_insn16.inc.c: \ + $(SRC_PATH)/target/riscv/insn16.decode $(DECODETREE) + $(call quiet-command, \ + $(PYTHON) $(DECODETREE) -o $@ --decode decode_insn16 --insnwidth 16 $<, \ + "GEN", $(TARGET_DIR)$@) + +target/riscv/translate.o: target/riscv/decode_insn32.inc.c \ + target/riscv/decode_insn16.inc.c diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d630e8fd6c..d61bce6d55 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -74,19 +74,15 @@ const char * const riscv_intr_names[] = { "s_external", "h_external", "m_external", - "coprocessor", - "host" + "reserved", + "reserved", + "reserved", + "reserved" }; -typedef struct RISCVCPUInfo { - const int bit_widths; - const char *name; - void (*initfn)(Object *obj); -} RISCVCPUInfo; - static void set_misa(CPURISCVState *env, target_ulong misa) { - env->misa = misa; + env->misa_mask = env->misa = misa; } static void set_versions(CPURISCVState *env, int user_ver, int priv_ver) @@ -124,6 +120,7 @@ static void rv32gcsu_priv1_09_1_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) @@ -133,6 +130,7 @@ static void rv32gcsu_priv1_10_0_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv32imacu_nommu_cpu_init(Object *obj) @@ -141,6 +139,7 @@ static void rv32imacu_nommu_cpu_init(Object *obj) set_misa(env, RV32 | RVI | RVM | RVA | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_PMP); } #elif defined(TARGET_RISCV64) @@ -152,6 +151,7 @@ static void rv64gcsu_priv1_09_1_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_09_1); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) @@ -161,6 +161,7 @@ static void rv64gcsu_priv1_10_0_cpu_init(Object *obj) set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); set_feature(env, RISCV_FEATURE_MMU); + set_feature(env, RISCV_FEATURE_PMP); } static void rv64imacu_nommu_cpu_init(Object *obj) @@ -169,6 +170,7 @@ static void rv64imacu_nommu_cpu_init(Object *obj) set_misa(env, RV64 | RVI | RVM | RVA | RVC | RVU); set_versions(env, USER_VERSION_2_02_0, PRIV_VERSION_1_10_0); set_resetvec(env, DEFAULT_RSTVEC); + set_feature(env, RISCV_FEATURE_PMP); } #endif @@ -303,6 +305,8 @@ static void riscv_cpu_realize(DeviceState *dev, Error **errp) return; } + riscv_cpu_register_gdb_regs_for_features(cs); + qemu_init_vcpu(cs); cpu_reset(cs); @@ -328,8 +332,8 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) CPUClass *cc = CPU_CLASS(c); DeviceClass *dc = DEVICE_CLASS(c); - mcc->parent_realize = dc->realize; - dc->realize = riscv_cpu_realize; + device_class_set_parent_realize(dc, riscv_cpu_realize, + &mcc->parent_realize); mcc->parent_reset = cc->reset; cc->reset = riscv_cpu_reset; @@ -343,7 +347,12 @@ static void riscv_cpu_class_init(ObjectClass *c, void *data) cc->synchronize_from_tb = riscv_cpu_synchronize_from_tb; cc->gdb_read_register = riscv_cpu_gdb_read_register; cc->gdb_write_register = riscv_cpu_gdb_write_register; - cc->gdb_num_core_regs = 65; + cc->gdb_num_core_regs = 33; +#if defined(TARGET_RISCV32) + cc->gdb_core_xml_file = "riscv-32bit-cpu.xml"; +#elif defined(TARGET_RISCV64) + cc->gdb_core_xml_file = "riscv-64bit-cpu.xml"; +#endif cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = riscv_cpu_disas_set_info; #ifdef CONFIG_USER_ONLY diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 34abc383e3..20bce8742e 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -24,12 +24,12 @@ #define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ #if defined(TARGET_RISCV64) #define TARGET_LONG_BITS 64 -#define TARGET_PHYS_ADDR_SPACE_BITS 50 -#define TARGET_VIRT_ADDR_SPACE_BITS 39 +#define TARGET_PHYS_ADDR_SPACE_BITS 56 /* 44-bit PPN */ +#define TARGET_VIRT_ADDR_SPACE_BITS 48 /* sv48 */ #elif defined(TARGET_RISCV32) #define TARGET_LONG_BITS 32 -#define TARGET_PHYS_ADDR_SPACE_BITS 34 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_PHYS_ADDR_SPACE_BITS 34 /* 22-bit PPN */ +#define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */ #endif #define TCG_GUEST_DEFAULT_MO 0 @@ -83,9 +83,11 @@ /* S extension denotes that Supervisor mode exists, however it is possible to have a core that support S mode but does not have an MMU and there is currently no bit in misa to indicate whether an MMU exists or not - so a cpu features bitfield is required */ + so a cpu features bitfield is required, likewise for optional PMP support */ enum { - RISCV_FEATURE_MMU + RISCV_FEATURE_MMU, + RISCV_FEATURE_PMP, + RISCV_FEATURE_MISA }; #define USER_VERSION_2_02_0 0x00020200 @@ -117,22 +119,33 @@ struct CPURISCVState { target_ulong user_ver; target_ulong priv_ver; target_ulong misa; + target_ulong misa_mask; uint32_t features; +#ifdef CONFIG_USER_ONLY + uint32_t elf_flags; +#endif + #ifndef CONFIG_USER_ONLY target_ulong priv; target_ulong resetvec; target_ulong mhartid; target_ulong mstatus; + /* * CAUTION! Unlike the rest of this struct, mip is accessed asynchonously - * by I/O threads and other vCPUs, so hold the iothread mutex before - * operating on it. CPU_INTERRUPT_HARD should be in effect iff this is - * non-zero. Use riscv_cpu_set_local_interrupt. + * by I/O threads. It should be read with atomic_read. It should be updated + * using riscv_cpu_update_mip with the iothread mutex held. The iothread + * mutex must be held because mip must be consistent with the CPU inturrept + * state. riscv_cpu_update_mip calls cpu_interrupt or cpu_reset_interrupt + * wuth the invariant that CPU_INTERRUPT_HARD is set iff mip is non-zero. + * mip is 32-bits to allow atomic_read on 32-bit hosts. */ - uint32_t mip; /* allow atomic_read for >= 32-bit hosts */ + uint32_t mip; + uint32_t miclaim; + target_ulong mie; target_ulong mideleg; @@ -164,6 +177,9 @@ struct CPURISCVState { /* physical memory protection */ pmp_table_t pmp_state; + + /* True if in debugger mode. */ + bool debugger; #endif float_status fp_status; @@ -247,28 +263,30 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, uintptr_t retaddr); int riscv_cpu_handle_mmu_fault(CPUState *cpu, vaddr address, int size, int rw, int mmu_idx); - char *riscv_isa_string(RISCVCPU *cpu); void riscv_cpu_list(FILE *f, fprintf_function cpu_fprintf); -#define cpu_init(cpu_model) cpu_generic_init(TYPE_RISCV_CPU, cpu_model) -#define cpu_signal_handler cpu_riscv_signal_handler +#define cpu_signal_handler riscv_cpu_signal_handler #define cpu_list riscv_cpu_list #define cpu_mmu_index riscv_cpu_mmu_index -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv); +#ifndef CONFIG_USER_ONLY +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts); +uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value); +#define BOOL_TO_MASK(x) (-!!(x)) /* helper for riscv_cpu_update_mip value */ +#endif +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv); void riscv_translate_init(void); -RISCVCPU *cpu_riscv_init(const char *cpu_model); -int cpu_riscv_signal_handler(int host_signum, void *pinfo, void *puc); -void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, - uint32_t exception, uintptr_t pc); +int riscv_cpu_signal_handler(int host_signum, void *pinfo, void *puc); +void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, + uint32_t exception, uintptr_t pc); -target_ulong cpu_riscv_get_fflags(CPURISCVState *env); -void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong); +target_ulong riscv_cpu_get_fflags(CPURISCVState *env); +void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); -#define TB_FLAGS_MMU_MASK 3 -#define TB_FLAGS_FP_ENABLE MSTATUS_FS +#define TB_FLAGS_MMU_MASK 3 +#define TB_FLAGS_MSTATUS_FS MSTATUS_FS static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) @@ -276,19 +294,49 @@ static inline void cpu_get_tb_cpu_state(CPURISCVState *env, target_ulong *pc, *pc = env->pc; *cs_base = 0; #ifdef CONFIG_USER_ONLY - *flags = TB_FLAGS_FP_ENABLE; + *flags = TB_FLAGS_MSTATUS_FS; #else *flags = cpu_mmu_index(env, 0) | (env->mstatus & MSTATUS_FS); #endif } -void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, - target_ulong csrno); -target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno); +int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask); +int riscv_csrrw_debug(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask); -#ifndef CONFIG_USER_ONLY -void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value); -#endif +static inline void riscv_csr_write(CPURISCVState *env, int csrno, + target_ulong val) +{ + riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS)); +} + +static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) +{ + target_ulong val = 0; + riscv_csrrw(env, csrno, &val, 0, 0); + return val; +} + +typedef int (*riscv_csr_predicate_fn)(CPURISCVState *env, int csrno); +typedef int (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, + target_ulong *ret_value); +typedef int (*riscv_csr_write_fn)(CPURISCVState *env, int csrno, + target_ulong new_value); +typedef int (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, + target_ulong *ret_value, target_ulong new_value, target_ulong write_mask); + +typedef struct { + riscv_csr_predicate_fn predicate; + riscv_csr_read_fn read; + riscv_csr_write_fn write; + riscv_csr_op_fn op; +} riscv_csr_operations; + +void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops); +void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops); + +void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); #include "exec/cpu-all.h" diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index 64aa097181..7180fccf54 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -6,242 +6,314 @@ (((target_ulong)(val) * ((mask) & ~((mask) << 1))) & \ (target_ulong)(mask))) -#define PGSHIFT 12 +/* Floating point round mode */ +#define FSR_RD_SHIFT 5 +#define FSR_RD (0x7 << FSR_RD_SHIFT) -#define FSR_RD_SHIFT 5 -#define FSR_RD (0x7 << FSR_RD_SHIFT) +/* Floating point accrued exception flags */ +#define FPEXC_NX 0x01 +#define FPEXC_UF 0x02 +#define FPEXC_OF 0x04 +#define FPEXC_DZ 0x08 +#define FPEXC_NV 0x10 -#define FPEXC_NX 0x01 -#define FPEXC_UF 0x02 -#define FPEXC_OF 0x04 -#define FPEXC_DZ 0x08 -#define FPEXC_NV 0x10 +/* Floating point status register bits */ +#define FSR_AEXC_SHIFT 0 +#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT) +#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT) +#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT) +#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT) +#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) +#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) -#define FSR_AEXC_SHIFT 0 -#define FSR_NVA (FPEXC_NV << FSR_AEXC_SHIFT) -#define FSR_OFA (FPEXC_OF << FSR_AEXC_SHIFT) -#define FSR_UFA (FPEXC_UF << FSR_AEXC_SHIFT) -#define FSR_DZA (FPEXC_DZ << FSR_AEXC_SHIFT) -#define FSR_NXA (FPEXC_NX << FSR_AEXC_SHIFT) -#define FSR_AEXC (FSR_NVA | FSR_OFA | FSR_UFA | FSR_DZA | FSR_NXA) +/* Control and Status Registers */ -/* CSR numbers */ -#define CSR_FFLAGS 0x1 -#define CSR_FRM 0x2 -#define CSR_FCSR 0x3 -#define CSR_CYCLE 0xc00 -#define CSR_TIME 0xc01 -#define CSR_INSTRET 0xc02 -#define CSR_HPMCOUNTER3 0xc03 -#define CSR_HPMCOUNTER4 0xc04 -#define CSR_HPMCOUNTER5 0xc05 -#define CSR_HPMCOUNTER6 0xc06 -#define CSR_HPMCOUNTER7 0xc07 -#define CSR_HPMCOUNTER8 0xc08 -#define CSR_HPMCOUNTER9 0xc09 -#define CSR_HPMCOUNTER10 0xc0a -#define CSR_HPMCOUNTER11 0xc0b -#define CSR_HPMCOUNTER12 0xc0c -#define CSR_HPMCOUNTER13 0xc0d -#define CSR_HPMCOUNTER14 0xc0e -#define CSR_HPMCOUNTER15 0xc0f -#define CSR_HPMCOUNTER16 0xc10 -#define CSR_HPMCOUNTER17 0xc11 -#define CSR_HPMCOUNTER18 0xc12 -#define CSR_HPMCOUNTER19 0xc13 -#define CSR_HPMCOUNTER20 0xc14 -#define CSR_HPMCOUNTER21 0xc15 -#define CSR_HPMCOUNTER22 0xc16 -#define CSR_HPMCOUNTER23 0xc17 -#define CSR_HPMCOUNTER24 0xc18 -#define CSR_HPMCOUNTER25 0xc19 -#define CSR_HPMCOUNTER26 0xc1a -#define CSR_HPMCOUNTER27 0xc1b -#define CSR_HPMCOUNTER28 0xc1c -#define CSR_HPMCOUNTER29 0xc1d -#define CSR_HPMCOUNTER30 0xc1e -#define CSR_HPMCOUNTER31 0xc1f -#define CSR_SSTATUS 0x100 -#define CSR_SIE 0x104 -#define CSR_STVEC 0x105 -#define CSR_SCOUNTEREN 0x106 -#define CSR_SSCRATCH 0x140 -#define CSR_SEPC 0x141 -#define CSR_SCAUSE 0x142 -#define CSR_SBADADDR 0x143 -#define CSR_SIP 0x144 -#define CSR_SPTBR 0x180 -#define CSR_SATP 0x180 -#define CSR_MSTATUS 0x300 -#define CSR_MISA 0x301 -#define CSR_MEDELEG 0x302 -#define CSR_MIDELEG 0x303 -#define CSR_MIE 0x304 -#define CSR_MTVEC 0x305 -#define CSR_MCOUNTEREN 0x306 -#define CSR_MSCRATCH 0x340 -#define CSR_MEPC 0x341 -#define CSR_MCAUSE 0x342 -#define CSR_MBADADDR 0x343 -#define CSR_MIP 0x344 -#define CSR_PMPCFG0 0x3a0 -#define CSR_PMPCFG1 0x3a1 -#define CSR_PMPCFG2 0x3a2 -#define CSR_PMPCFG3 0x3a3 -#define CSR_PMPADDR0 0x3b0 -#define CSR_PMPADDR1 0x3b1 -#define CSR_PMPADDR2 0x3b2 -#define CSR_PMPADDR3 0x3b3 -#define CSR_PMPADDR4 0x3b4 -#define CSR_PMPADDR5 0x3b5 -#define CSR_PMPADDR6 0x3b6 -#define CSR_PMPADDR7 0x3b7 -#define CSR_PMPADDR8 0x3b8 -#define CSR_PMPADDR9 0x3b9 -#define CSR_PMPADDR10 0x3ba -#define CSR_PMPADDR11 0x3bb -#define CSR_PMPADDR12 0x3bc -#define CSR_PMPADDR13 0x3bd -#define CSR_PMPADDR14 0x3be -#define CSR_PMPADDR15 0x3bf -#define CSR_TSELECT 0x7a0 -#define CSR_TDATA1 0x7a1 -#define CSR_TDATA2 0x7a2 -#define CSR_TDATA3 0x7a3 -#define CSR_DCSR 0x7b0 -#define CSR_DPC 0x7b1 -#define CSR_DSCRATCH 0x7b2 -#define CSR_MCYCLE 0xb00 -#define CSR_MINSTRET 0xb02 -#define CSR_MHPMCOUNTER3 0xb03 -#define CSR_MHPMCOUNTER4 0xb04 -#define CSR_MHPMCOUNTER5 0xb05 -#define CSR_MHPMCOUNTER6 0xb06 -#define CSR_MHPMCOUNTER7 0xb07 -#define CSR_MHPMCOUNTER8 0xb08 -#define CSR_MHPMCOUNTER9 0xb09 -#define CSR_MHPMCOUNTER10 0xb0a -#define CSR_MHPMCOUNTER11 0xb0b -#define CSR_MHPMCOUNTER12 0xb0c -#define CSR_MHPMCOUNTER13 0xb0d -#define CSR_MHPMCOUNTER14 0xb0e -#define CSR_MHPMCOUNTER15 0xb0f -#define CSR_MHPMCOUNTER16 0xb10 -#define CSR_MHPMCOUNTER17 0xb11 -#define CSR_MHPMCOUNTER18 0xb12 -#define CSR_MHPMCOUNTER19 0xb13 -#define CSR_MHPMCOUNTER20 0xb14 -#define CSR_MHPMCOUNTER21 0xb15 -#define CSR_MHPMCOUNTER22 0xb16 -#define CSR_MHPMCOUNTER23 0xb17 -#define CSR_MHPMCOUNTER24 0xb18 -#define CSR_MHPMCOUNTER25 0xb19 -#define CSR_MHPMCOUNTER26 0xb1a -#define CSR_MHPMCOUNTER27 0xb1b -#define CSR_MHPMCOUNTER28 0xb1c -#define CSR_MHPMCOUNTER29 0xb1d -#define CSR_MHPMCOUNTER30 0xb1e -#define CSR_MHPMCOUNTER31 0xb1f -#define CSR_MUCOUNTEREN 0x320 -#define CSR_MSCOUNTEREN 0x321 -#define CSR_MHPMEVENT3 0x323 -#define CSR_MHPMEVENT4 0x324 -#define CSR_MHPMEVENT5 0x325 -#define CSR_MHPMEVENT6 0x326 -#define CSR_MHPMEVENT7 0x327 -#define CSR_MHPMEVENT8 0x328 -#define CSR_MHPMEVENT9 0x329 -#define CSR_MHPMEVENT10 0x32a -#define CSR_MHPMEVENT11 0x32b -#define CSR_MHPMEVENT12 0x32c -#define CSR_MHPMEVENT13 0x32d -#define CSR_MHPMEVENT14 0x32e -#define CSR_MHPMEVENT15 0x32f -#define CSR_MHPMEVENT16 0x330 -#define CSR_MHPMEVENT17 0x331 -#define CSR_MHPMEVENT18 0x332 -#define CSR_MHPMEVENT19 0x333 -#define CSR_MHPMEVENT20 0x334 -#define CSR_MHPMEVENT21 0x335 -#define CSR_MHPMEVENT22 0x336 -#define CSR_MHPMEVENT23 0x337 -#define CSR_MHPMEVENT24 0x338 -#define CSR_MHPMEVENT25 0x339 -#define CSR_MHPMEVENT26 0x33a -#define CSR_MHPMEVENT27 0x33b -#define CSR_MHPMEVENT28 0x33c -#define CSR_MHPMEVENT29 0x33d -#define CSR_MHPMEVENT30 0x33e -#define CSR_MHPMEVENT31 0x33f -#define CSR_MVENDORID 0xf11 -#define CSR_MARCHID 0xf12 -#define CSR_MIMPID 0xf13 -#define CSR_MHARTID 0xf14 -#define CSR_CYCLEH 0xc80 -#define CSR_TIMEH 0xc81 -#define CSR_INSTRETH 0xc82 -#define CSR_HPMCOUNTER3H 0xc83 -#define CSR_HPMCOUNTER4H 0xc84 -#define CSR_HPMCOUNTER5H 0xc85 -#define CSR_HPMCOUNTER6H 0xc86 -#define CSR_HPMCOUNTER7H 0xc87 -#define CSR_HPMCOUNTER8H 0xc88 -#define CSR_HPMCOUNTER9H 0xc89 -#define CSR_HPMCOUNTER10H 0xc8a -#define CSR_HPMCOUNTER11H 0xc8b -#define CSR_HPMCOUNTER12H 0xc8c -#define CSR_HPMCOUNTER13H 0xc8d -#define CSR_HPMCOUNTER14H 0xc8e -#define CSR_HPMCOUNTER15H 0xc8f -#define CSR_HPMCOUNTER16H 0xc90 -#define CSR_HPMCOUNTER17H 0xc91 -#define CSR_HPMCOUNTER18H 0xc92 -#define CSR_HPMCOUNTER19H 0xc93 -#define CSR_HPMCOUNTER20H 0xc94 -#define CSR_HPMCOUNTER21H 0xc95 -#define CSR_HPMCOUNTER22H 0xc96 -#define CSR_HPMCOUNTER23H 0xc97 -#define CSR_HPMCOUNTER24H 0xc98 -#define CSR_HPMCOUNTER25H 0xc99 -#define CSR_HPMCOUNTER26H 0xc9a -#define CSR_HPMCOUNTER27H 0xc9b -#define CSR_HPMCOUNTER28H 0xc9c -#define CSR_HPMCOUNTER29H 0xc9d -#define CSR_HPMCOUNTER30H 0xc9e -#define CSR_HPMCOUNTER31H 0xc9f -#define CSR_MCYCLEH 0xb80 -#define CSR_MINSTRETH 0xb82 -#define CSR_MHPMCOUNTER3H 0xb83 -#define CSR_MHPMCOUNTER4H 0xb84 -#define CSR_MHPMCOUNTER5H 0xb85 -#define CSR_MHPMCOUNTER6H 0xb86 -#define CSR_MHPMCOUNTER7H 0xb87 -#define CSR_MHPMCOUNTER8H 0xb88 -#define CSR_MHPMCOUNTER9H 0xb89 -#define CSR_MHPMCOUNTER10H 0xb8a -#define CSR_MHPMCOUNTER11H 0xb8b -#define CSR_MHPMCOUNTER12H 0xb8c -#define CSR_MHPMCOUNTER13H 0xb8d -#define CSR_MHPMCOUNTER14H 0xb8e -#define CSR_MHPMCOUNTER15H 0xb8f -#define CSR_MHPMCOUNTER16H 0xb90 -#define CSR_MHPMCOUNTER17H 0xb91 -#define CSR_MHPMCOUNTER18H 0xb92 -#define CSR_MHPMCOUNTER19H 0xb93 -#define CSR_MHPMCOUNTER20H 0xb94 -#define CSR_MHPMCOUNTER21H 0xb95 -#define CSR_MHPMCOUNTER22H 0xb96 -#define CSR_MHPMCOUNTER23H 0xb97 -#define CSR_MHPMCOUNTER24H 0xb98 -#define CSR_MHPMCOUNTER25H 0xb99 -#define CSR_MHPMCOUNTER26H 0xb9a -#define CSR_MHPMCOUNTER27H 0xb9b -#define CSR_MHPMCOUNTER28H 0xb9c -#define CSR_MHPMCOUNTER29H 0xb9d -#define CSR_MHPMCOUNTER30H 0xb9e -#define CSR_MHPMCOUNTER31H 0xb9f +/* User Trap Setup */ +#define CSR_USTATUS 0x000 +#define CSR_UIE 0x004 +#define CSR_UTVEC 0x005 -/* mstatus bits */ +/* User Trap Handling */ +#define CSR_USCRATCH 0x040 +#define CSR_UEPC 0x041 +#define CSR_UCAUSE 0x042 +#define CSR_UTVAL 0x043 +#define CSR_UIP 0x044 + +/* User Floating-Point CSRs */ +#define CSR_FFLAGS 0x001 +#define CSR_FRM 0x002 +#define CSR_FCSR 0x003 + +/* User Timers and Counters */ +#define CSR_CYCLE 0xc00 +#define CSR_TIME 0xc01 +#define CSR_INSTRET 0xc02 +#define CSR_HPMCOUNTER3 0xc03 +#define CSR_HPMCOUNTER4 0xc04 +#define CSR_HPMCOUNTER5 0xc05 +#define CSR_HPMCOUNTER6 0xc06 +#define CSR_HPMCOUNTER7 0xc07 +#define CSR_HPMCOUNTER8 0xc08 +#define CSR_HPMCOUNTER9 0xc09 +#define CSR_HPMCOUNTER10 0xc0a +#define CSR_HPMCOUNTER11 0xc0b +#define CSR_HPMCOUNTER12 0xc0c +#define CSR_HPMCOUNTER13 0xc0d +#define CSR_HPMCOUNTER14 0xc0e +#define CSR_HPMCOUNTER15 0xc0f +#define CSR_HPMCOUNTER16 0xc10 +#define CSR_HPMCOUNTER17 0xc11 +#define CSR_HPMCOUNTER18 0xc12 +#define CSR_HPMCOUNTER19 0xc13 +#define CSR_HPMCOUNTER20 0xc14 +#define CSR_HPMCOUNTER21 0xc15 +#define CSR_HPMCOUNTER22 0xc16 +#define CSR_HPMCOUNTER23 0xc17 +#define CSR_HPMCOUNTER24 0xc18 +#define CSR_HPMCOUNTER25 0xc19 +#define CSR_HPMCOUNTER26 0xc1a +#define CSR_HPMCOUNTER27 0xc1b +#define CSR_HPMCOUNTER28 0xc1c +#define CSR_HPMCOUNTER29 0xc1d +#define CSR_HPMCOUNTER30 0xc1e +#define CSR_HPMCOUNTER31 0xc1f +#define CSR_CYCLEH 0xc80 +#define CSR_TIMEH 0xc81 +#define CSR_INSTRETH 0xc82 +#define CSR_HPMCOUNTER3H 0xc83 +#define CSR_HPMCOUNTER4H 0xc84 +#define CSR_HPMCOUNTER5H 0xc85 +#define CSR_HPMCOUNTER6H 0xc86 +#define CSR_HPMCOUNTER7H 0xc87 +#define CSR_HPMCOUNTER8H 0xc88 +#define CSR_HPMCOUNTER9H 0xc89 +#define CSR_HPMCOUNTER10H 0xc8a +#define CSR_HPMCOUNTER11H 0xc8b +#define CSR_HPMCOUNTER12H 0xc8c +#define CSR_HPMCOUNTER13H 0xc8d +#define CSR_HPMCOUNTER14H 0xc8e +#define CSR_HPMCOUNTER15H 0xc8f +#define CSR_HPMCOUNTER16H 0xc90 +#define CSR_HPMCOUNTER17H 0xc91 +#define CSR_HPMCOUNTER18H 0xc92 +#define CSR_HPMCOUNTER19H 0xc93 +#define CSR_HPMCOUNTER20H 0xc94 +#define CSR_HPMCOUNTER21H 0xc95 +#define CSR_HPMCOUNTER22H 0xc96 +#define CSR_HPMCOUNTER23H 0xc97 +#define CSR_HPMCOUNTER24H 0xc98 +#define CSR_HPMCOUNTER25H 0xc99 +#define CSR_HPMCOUNTER26H 0xc9a +#define CSR_HPMCOUNTER27H 0xc9b +#define CSR_HPMCOUNTER28H 0xc9c +#define CSR_HPMCOUNTER29H 0xc9d +#define CSR_HPMCOUNTER30H 0xc9e +#define CSR_HPMCOUNTER31H 0xc9f + +/* Machine Timers and Counters */ +#define CSR_MCYCLE 0xb00 +#define CSR_MINSTRET 0xb02 +#define CSR_MCYCLEH 0xb80 +#define CSR_MINSTRETH 0xb82 + +/* Machine Information Registers */ +#define CSR_MVENDORID 0xf11 +#define CSR_MARCHID 0xf12 +#define CSR_MIMPID 0xf13 +#define CSR_MHARTID 0xf14 + +/* Machine Trap Setup */ +#define CSR_MSTATUS 0x300 +#define CSR_MISA 0x301 +#define CSR_MEDELEG 0x302 +#define CSR_MIDELEG 0x303 +#define CSR_MIE 0x304 +#define CSR_MTVEC 0x305 +#define CSR_MCOUNTEREN 0x306 + +/* Legacy Counter Setup (priv v1.9.1) */ +#define CSR_MUCOUNTEREN 0x320 +#define CSR_MSCOUNTEREN 0x321 +#define CSR_MHCOUNTEREN 0x322 + +/* Machine Trap Handling */ +#define CSR_MSCRATCH 0x340 +#define CSR_MEPC 0x341 +#define CSR_MCAUSE 0x342 +#define CSR_MTVAL 0x343 +#define CSR_MIP 0x344 + +/* Legacy Machine Trap Handling (priv v1.9.1) */ +#define CSR_MBADADDR 0x343 + +/* Supervisor Trap Setup */ +#define CSR_SSTATUS 0x100 +#define CSR_SEDELEG 0x102 +#define CSR_SIDELEG 0x103 +#define CSR_SIE 0x104 +#define CSR_STVEC 0x105 +#define CSR_SCOUNTEREN 0x106 + +/* Supervisor Trap Handling */ +#define CSR_SSCRATCH 0x140 +#define CSR_SEPC 0x141 +#define CSR_SCAUSE 0x142 +#define CSR_STVAL 0x143 +#define CSR_SIP 0x144 + +/* Legacy Supervisor Trap Handling (priv v1.9.1) */ +#define CSR_SBADADDR 0x143 + +/* Supervisor Protection and Translation */ +#define CSR_SPTBR 0x180 +#define CSR_SATP 0x180 + +/* Physical Memory Protection */ +#define CSR_PMPCFG0 0x3a0 +#define CSR_PMPCFG1 0x3a1 +#define CSR_PMPCFG2 0x3a2 +#define CSR_PMPCFG3 0x3a3 +#define CSR_PMPADDR0 0x3b0 +#define CSR_PMPADDR1 0x3b1 +#define CSR_PMPADDR2 0x3b2 +#define CSR_PMPADDR3 0x3b3 +#define CSR_PMPADDR4 0x3b4 +#define CSR_PMPADDR5 0x3b5 +#define CSR_PMPADDR6 0x3b6 +#define CSR_PMPADDR7 0x3b7 +#define CSR_PMPADDR8 0x3b8 +#define CSR_PMPADDR9 0x3b9 +#define CSR_PMPADDR10 0x3ba +#define CSR_PMPADDR11 0x3bb +#define CSR_PMPADDR12 0x3bc +#define CSR_PMPADDR13 0x3bd +#define CSR_PMPADDR14 0x3be +#define CSR_PMPADDR15 0x3bf + +/* Debug/Trace Registers (shared with Debug Mode) */ +#define CSR_TSELECT 0x7a0 +#define CSR_TDATA1 0x7a1 +#define CSR_TDATA2 0x7a2 +#define CSR_TDATA3 0x7a3 + +/* Debug Mode Registers */ +#define CSR_DCSR 0x7b0 +#define CSR_DPC 0x7b1 +#define CSR_DSCRATCH 0x7b2 + +/* Performance Counters */ +#define CSR_MHPMCOUNTER3 0xb03 +#define CSR_MHPMCOUNTER4 0xb04 +#define CSR_MHPMCOUNTER5 0xb05 +#define CSR_MHPMCOUNTER6 0xb06 +#define CSR_MHPMCOUNTER7 0xb07 +#define CSR_MHPMCOUNTER8 0xb08 +#define CSR_MHPMCOUNTER9 0xb09 +#define CSR_MHPMCOUNTER10 0xb0a +#define CSR_MHPMCOUNTER11 0xb0b +#define CSR_MHPMCOUNTER12 0xb0c +#define CSR_MHPMCOUNTER13 0xb0d +#define CSR_MHPMCOUNTER14 0xb0e +#define CSR_MHPMCOUNTER15 0xb0f +#define CSR_MHPMCOUNTER16 0xb10 +#define CSR_MHPMCOUNTER17 0xb11 +#define CSR_MHPMCOUNTER18 0xb12 +#define CSR_MHPMCOUNTER19 0xb13 +#define CSR_MHPMCOUNTER20 0xb14 +#define CSR_MHPMCOUNTER21 0xb15 +#define CSR_MHPMCOUNTER22 0xb16 +#define CSR_MHPMCOUNTER23 0xb17 +#define CSR_MHPMCOUNTER24 0xb18 +#define CSR_MHPMCOUNTER25 0xb19 +#define CSR_MHPMCOUNTER26 0xb1a +#define CSR_MHPMCOUNTER27 0xb1b +#define CSR_MHPMCOUNTER28 0xb1c +#define CSR_MHPMCOUNTER29 0xb1d +#define CSR_MHPMCOUNTER30 0xb1e +#define CSR_MHPMCOUNTER31 0xb1f +#define CSR_MHPMEVENT3 0x323 +#define CSR_MHPMEVENT4 0x324 +#define CSR_MHPMEVENT5 0x325 +#define CSR_MHPMEVENT6 0x326 +#define CSR_MHPMEVENT7 0x327 +#define CSR_MHPMEVENT8 0x328 +#define CSR_MHPMEVENT9 0x329 +#define CSR_MHPMEVENT10 0x32a +#define CSR_MHPMEVENT11 0x32b +#define CSR_MHPMEVENT12 0x32c +#define CSR_MHPMEVENT13 0x32d +#define CSR_MHPMEVENT14 0x32e +#define CSR_MHPMEVENT15 0x32f +#define CSR_MHPMEVENT16 0x330 +#define CSR_MHPMEVENT17 0x331 +#define CSR_MHPMEVENT18 0x332 +#define CSR_MHPMEVENT19 0x333 +#define CSR_MHPMEVENT20 0x334 +#define CSR_MHPMEVENT21 0x335 +#define CSR_MHPMEVENT22 0x336 +#define CSR_MHPMEVENT23 0x337 +#define CSR_MHPMEVENT24 0x338 +#define CSR_MHPMEVENT25 0x339 +#define CSR_MHPMEVENT26 0x33a +#define CSR_MHPMEVENT27 0x33b +#define CSR_MHPMEVENT28 0x33c +#define CSR_MHPMEVENT29 0x33d +#define CSR_MHPMEVENT30 0x33e +#define CSR_MHPMEVENT31 0x33f +#define CSR_MHPMCOUNTER3H 0xb83 +#define CSR_MHPMCOUNTER4H 0xb84 +#define CSR_MHPMCOUNTER5H 0xb85 +#define CSR_MHPMCOUNTER6H 0xb86 +#define CSR_MHPMCOUNTER7H 0xb87 +#define CSR_MHPMCOUNTER8H 0xb88 +#define CSR_MHPMCOUNTER9H 0xb89 +#define CSR_MHPMCOUNTER10H 0xb8a +#define CSR_MHPMCOUNTER11H 0xb8b +#define CSR_MHPMCOUNTER12H 0xb8c +#define CSR_MHPMCOUNTER13H 0xb8d +#define CSR_MHPMCOUNTER14H 0xb8e +#define CSR_MHPMCOUNTER15H 0xb8f +#define CSR_MHPMCOUNTER16H 0xb90 +#define CSR_MHPMCOUNTER17H 0xb91 +#define CSR_MHPMCOUNTER18H 0xb92 +#define CSR_MHPMCOUNTER19H 0xb93 +#define CSR_MHPMCOUNTER20H 0xb94 +#define CSR_MHPMCOUNTER21H 0xb95 +#define CSR_MHPMCOUNTER22H 0xb96 +#define CSR_MHPMCOUNTER23H 0xb97 +#define CSR_MHPMCOUNTER24H 0xb98 +#define CSR_MHPMCOUNTER25H 0xb99 +#define CSR_MHPMCOUNTER26H 0xb9a +#define CSR_MHPMCOUNTER27H 0xb9b +#define CSR_MHPMCOUNTER28H 0xb9c +#define CSR_MHPMCOUNTER29H 0xb9d +#define CSR_MHPMCOUNTER30H 0xb9e +#define CSR_MHPMCOUNTER31H 0xb9f + +/* Legacy Hypervisor Trap Setup (priv v1.9.1) */ +#define CSR_HSTATUS 0x200 +#define CSR_HEDELEG 0x202 +#define CSR_HIDELEG 0x203 +#define CSR_HIE 0x204 +#define CSR_HTVEC 0x205 + +/* Legacy Hypervisor Trap Handling (priv v1.9.1) */ +#define CSR_HSCRATCH 0x240 +#define CSR_HEPC 0x241 +#define CSR_HCAUSE 0x242 +#define CSR_HBADADDR 0x243 +#define CSR_HIP 0x244 + +/* Legacy Machine Protection and Translation (priv v1.9.1) */ +#define CSR_MBASE 0x380 +#define CSR_MBOUND 0x381 +#define CSR_MIBASE 0x382 +#define CSR_MIBOUND 0x383 +#define CSR_MDBASE 0x384 +#define CSR_MDBOUND 0x385 + +/* mstatus CSR bits */ #define MSTATUS_UIE 0x00000001 #define MSTATUS_SIE 0x00000002 #define MSTATUS_HIE 0x00000004 @@ -270,13 +342,24 @@ #define MSTATUS32_SD 0x80000000 #define MSTATUS64_SD 0x8000000000000000ULL +#define MISA32_MXL 0xC0000000 +#define MISA64_MXL 0xC000000000000000ULL + +#define MXL_RV32 1 +#define MXL_RV64 2 +#define MXL_RV128 3 + #if defined(TARGET_RISCV32) #define MSTATUS_SD MSTATUS32_SD +#define MISA_MXL MISA32_MXL +#define MXL_VAL MXL_RV32 #elif defined(TARGET_RISCV64) #define MSTATUS_SD MSTATUS64_SD +#define MISA_MXL MISA64_MXL +#define MXL_VAL MXL_RV64 #endif -/* sstatus bits */ +/* sstatus CSR bits */ #define SSTATUS_UIE 0x00000001 #define SSTATUS_SIE 0x00000002 #define SSTATUS_UPIE 0x00000010 @@ -297,83 +380,71 @@ #define SSTATUS_SD SSTATUS64_SD #endif -/* irqs */ -#define MIP_SSIP (1 << IRQ_S_SOFT) -#define MIP_HSIP (1 << IRQ_H_SOFT) -#define MIP_MSIP (1 << IRQ_M_SOFT) -#define MIP_STIP (1 << IRQ_S_TIMER) -#define MIP_HTIP (1 << IRQ_H_TIMER) -#define MIP_MTIP (1 << IRQ_M_TIMER) -#define MIP_SEIP (1 << IRQ_S_EXT) -#define MIP_HEIP (1 << IRQ_H_EXT) -#define MIP_MEIP (1 << IRQ_M_EXT) - -#define SIP_SSIP MIP_SSIP -#define SIP_STIP MIP_STIP -#define SIP_SEIP MIP_SEIP - +/* Privilege modes */ #define PRV_U 0 #define PRV_S 1 #define PRV_H 2 #define PRV_M 3 -/* privileged ISA 1.9.1 VM modes (mstatus.vm) */ -#define VM_1_09_MBARE 0 -#define VM_1_09_MBB 1 -#define VM_1_09_MBBID 2 -#define VM_1_09_SV32 8 -#define VM_1_09_SV39 9 -#define VM_1_09_SV48 10 +/* RV32 satp CSR field masks */ +#define SATP32_MODE 0x80000000 +#define SATP32_ASID 0x7fc00000 +#define SATP32_PPN 0x003fffff -/* privileged ISA 1.10.0 VM modes (satp.mode) */ -#define VM_1_10_MBARE 0 -#define VM_1_10_SV32 1 -#define VM_1_10_SV39 8 -#define VM_1_10_SV48 9 -#define VM_1_10_SV57 10 -#define VM_1_10_SV64 11 - -/* privileged ISA interrupt causes */ -#define IRQ_U_SOFT 0 /* since: priv-1.10 */ -#define IRQ_S_SOFT 1 -#define IRQ_H_SOFT 2 /* until: priv-1.9.1 */ -#define IRQ_M_SOFT 3 /* until: priv-1.9.1 */ -#define IRQ_U_TIMER 4 /* since: priv-1.10 */ -#define IRQ_S_TIMER 5 -#define IRQ_H_TIMER 6 /* until: priv-1.9.1 */ -#define IRQ_M_TIMER 7 /* until: priv-1.9.1 */ -#define IRQ_U_EXT 8 /* since: priv-1.10 */ -#define IRQ_S_EXT 9 -#define IRQ_H_EXT 10 /* until: priv-1.9.1 */ -#define IRQ_M_EXT 11 /* until: priv-1.9.1 */ -#define IRQ_X_COP 12 /* non-standard */ - -/* Default addresses */ -#define DEFAULT_RSTVEC 0x00001000 - -/* RV32 satp field masks */ -#define SATP32_MODE 0x80000000 -#define SATP32_ASID 0x7fc00000 -#define SATP32_PPN 0x003fffff - -/* RV64 satp field masks */ -#define SATP64_MODE 0xF000000000000000ULL -#define SATP64_ASID 0x0FFFF00000000000ULL -#define SATP64_PPN 0x00000FFFFFFFFFFFULL +/* RV64 satp CSR field masks */ +#define SATP64_MODE 0xF000000000000000ULL +#define SATP64_ASID 0x0FFFF00000000000ULL +#define SATP64_PPN 0x00000FFFFFFFFFFFULL #if defined(TARGET_RISCV32) -#define SATP_MODE SATP32_MODE -#define SATP_ASID SATP32_ASID -#define SATP_PPN SATP32_PPN +#define SATP_MODE SATP32_MODE +#define SATP_ASID SATP32_ASID +#define SATP_PPN SATP32_PPN #endif #if defined(TARGET_RISCV64) -#define SATP_MODE SATP64_MODE -#define SATP_ASID SATP64_ASID -#define SATP_PPN SATP64_PPN +#define SATP_MODE SATP64_MODE +#define SATP_ASID SATP64_ASID +#define SATP_PPN SATP64_PPN #endif -/* RISCV Exception Codes */ -#define EXCP_NONE -1 /* not a real RISCV exception code */ +/* VM modes (mstatus.vm) privileged ISA 1.9.1 */ +#define VM_1_09_MBARE 0 +#define VM_1_09_MBB 1 +#define VM_1_09_MBBID 2 +#define VM_1_09_SV32 8 +#define VM_1_09_SV39 9 +#define VM_1_09_SV48 10 + +/* VM modes (satp.mode) privileged ISA 1.10 */ +#define VM_1_10_MBARE 0 +#define VM_1_10_SV32 1 +#define VM_1_10_SV39 8 +#define VM_1_10_SV48 9 +#define VM_1_10_SV57 10 +#define VM_1_10_SV64 11 + +/* Page table entry (PTE) fields */ +#define PTE_V 0x001 /* Valid */ +#define PTE_R 0x002 /* Read */ +#define PTE_W 0x004 /* Write */ +#define PTE_X 0x008 /* Execute */ +#define PTE_U 0x010 /* User */ +#define PTE_G 0x020 /* Global */ +#define PTE_A 0x040 /* Accessed */ +#define PTE_D 0x080 /* Dirty */ +#define PTE_SOFT 0x300 /* Reserved for Software */ + +/* Page table PPN shift amount */ +#define PTE_PPN_SHIFT 10 + +/* Leaf page shift amount */ +#define PGSHIFT 12 + +/* Default Reset Vector adress */ +#define DEFAULT_RSTVEC 0x1000 + +/* Exception causes */ +#define EXCP_NONE -1 /* sentinel value */ #define RISCV_EXCP_INST_ADDR_MIS 0x0 #define RISCV_EXCP_INST_ACCESS_FAULT 0x1 #define RISCV_EXCP_ILLEGAL_INST 0x2 @@ -382,9 +453,7 @@ #define RISCV_EXCP_LOAD_ACCESS_FAULT 0x5 #define RISCV_EXCP_STORE_AMO_ADDR_MIS 0x6 #define RISCV_EXCP_STORE_AMO_ACCESS_FAULT 0x7 -#define RISCV_EXCP_U_ECALL 0x8 /* for convenience, report all - ECALLs as this, handler - fixes */ +#define RISCV_EXCP_U_ECALL 0x8 #define RISCV_EXCP_S_ECALL 0x9 #define RISCV_EXCP_H_ECALL 0xa #define RISCV_EXCP_M_ECALL 0xb @@ -395,17 +464,35 @@ #define RISCV_EXCP_INT_FLAG 0x80000000 #define RISCV_EXCP_INT_MASK 0x7fffffff -/* page table entry (PTE) fields */ -#define PTE_V 0x001 /* Valid */ -#define PTE_R 0x002 /* Read */ -#define PTE_W 0x004 /* Write */ -#define PTE_X 0x008 /* Execute */ -#define PTE_U 0x010 /* User */ -#define PTE_G 0x020 /* Global */ -#define PTE_A 0x040 /* Accessed */ -#define PTE_D 0x080 /* Dirty */ -#define PTE_SOFT 0x300 /* Reserved for Software */ +/* Interrupt causes */ +#define IRQ_U_SOFT 0 +#define IRQ_S_SOFT 1 +#define IRQ_H_SOFT 2 /* reserved */ +#define IRQ_M_SOFT 3 +#define IRQ_U_TIMER 4 +#define IRQ_S_TIMER 5 +#define IRQ_H_TIMER 6 /* reserved */ +#define IRQ_M_TIMER 7 +#define IRQ_U_EXT 8 +#define IRQ_S_EXT 9 +#define IRQ_H_EXT 10 /* reserved */ +#define IRQ_M_EXT 11 -#define PTE_PPN_SHIFT 10 +/* mip masks */ +#define MIP_USIP (1 << IRQ_U_SOFT) +#define MIP_SSIP (1 << IRQ_S_SOFT) +#define MIP_HSIP (1 << IRQ_H_SOFT) +#define MIP_MSIP (1 << IRQ_M_SOFT) +#define MIP_UTIP (1 << IRQ_U_TIMER) +#define MIP_STIP (1 << IRQ_S_TIMER) +#define MIP_HTIP (1 << IRQ_H_TIMER) +#define MIP_MTIP (1 << IRQ_M_TIMER) +#define MIP_UEIP (1 << IRQ_U_EXT) +#define MIP_SEIP (1 << IRQ_S_EXT) +#define MIP_HEIP (1 << IRQ_H_EXT) +#define MIP_MEIP (1 << IRQ_M_EXT) -#define PTE_TABLE(PTE) (((PTE) & (PTE_V | PTE_R | PTE_W | PTE_X)) == PTE_V) +/* sip masks */ +#define SIP_SSIP MIP_SSIP +#define SIP_STIP MIP_STIP +#define SIP_SEIP MIP_SEIP diff --git a/target/riscv/helper.c b/target/riscv/cpu_helper.c similarity index 63% rename from target/riscv/helper.c rename to target/riscv/cpu_helper.c index 29e1a603dc..b17f169681 100644 --- a/target/riscv/helper.c +++ b/target/riscv/cpu_helper.c @@ -1,5 +1,5 @@ /* - * RISC-V emulation helpers for qemu. + * RISC-V CPU helpers for qemu. * * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu * Copyright (c) 2017-2018 SiFive, Inc. @@ -22,8 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg-op.h" - -#define RISCV_DEBUG_INTERRUPT 0 +#include "trace.h" int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) { @@ -35,28 +34,18 @@ int riscv_cpu_mmu_index(CPURISCVState *env, bool ifetch) } #ifndef CONFIG_USER_ONLY -/* - * Return RISC-V IRQ number if an interrupt should be taken, else -1. - * Used in cpu-exec.c - * - * Adapted from Spike's processor_t::take_interrupt() - */ -static int riscv_cpu_hw_interrupts_pending(CPURISCVState *env) +static int riscv_cpu_local_irq_pending(CPURISCVState *env) { - target_ulong pending_interrupts = atomic_read(&env->mip) & env->mie; + target_ulong mstatus_mie = get_field(env->mstatus, MSTATUS_MIE); + target_ulong mstatus_sie = get_field(env->mstatus, MSTATUS_SIE); + target_ulong pending = atomic_read(&env->mip) & env->mie; + target_ulong mie = env->priv < PRV_M || (env->priv == PRV_M && mstatus_mie); + target_ulong sie = env->priv < PRV_S || (env->priv == PRV_S && mstatus_sie); + target_ulong irqs = (pending & ~env->mideleg & -mie) | + (pending & env->mideleg & -sie); - target_ulong mie = get_field(env->mstatus, MSTATUS_MIE); - target_ulong m_enabled = env->priv < PRV_M || (env->priv == PRV_M && mie); - target_ulong enabled_interrupts = pending_interrupts & - ~env->mideleg & -m_enabled; - - target_ulong sie = get_field(env->mstatus, MSTATUS_SIE); - target_ulong s_enabled = env->priv < PRV_S || (env->priv == PRV_S && sie); - enabled_interrupts |= pending_interrupts & env->mideleg & - -s_enabled; - - if (enabled_interrupts) { - return ctz64(enabled_interrupts); /* since non-zero */ + if (irqs) { + return ctz64(irqs); /* since non-zero */ } else { return EXCP_NONE; /* indicates no pending interrupt */ } @@ -69,7 +58,7 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) if (interrupt_request & CPU_INTERRUPT_HARD) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - int interruptno = riscv_cpu_hw_interrupts_pending(env); + int interruptno = riscv_cpu_local_irq_pending(env); if (interruptno >= 0) { cs->exception_index = RISCV_EXCP_INT_FLAG | interruptno; riscv_cpu_do_interrupt(cs); @@ -82,6 +71,50 @@ bool riscv_cpu_exec_interrupt(CPUState *cs, int interrupt_request) #if !defined(CONFIG_USER_ONLY) +int riscv_cpu_claim_interrupts(RISCVCPU *cpu, uint32_t interrupts) +{ + CPURISCVState *env = &cpu->env; + if (env->miclaim & interrupts) { + return -1; + } else { + env->miclaim |= interrupts; + return 0; + } +} + +/* iothread_mutex must be held */ +uint32_t riscv_cpu_update_mip(RISCVCPU *cpu, uint32_t mask, uint32_t value) +{ + CPURISCVState *env = &cpu->env; + uint32_t old, new, cmp = atomic_read(&env->mip); + + do { + old = cmp; + new = (old & ~mask) | (value & mask); + cmp = atomic_cmpxchg(&env->mip, old, new); + } while (old != cmp); + + if (new) { + cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } else { + cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); + } + + return old; +} + +void riscv_cpu_set_mode(CPURISCVState *env, target_ulong newpriv) +{ + if (newpriv > PRV_M) { + g_assert_not_reached(); + } + if (newpriv == PRV_H) { + newpriv = PRV_U; + } + /* tlb_flush is unnecessary as mode is contained in mmu_idx */ + env->priv = newpriv; +} + /* get_physical_address - get the physical address for this virtual address * * Do a page table walk to obtain the physical address corresponding to a @@ -185,16 +218,39 @@ restart: #endif target_ulong ppn = pte >> PTE_PPN_SHIFT; - if (PTE_TABLE(pte)) { /* next level of page table */ + if (!(pte & PTE_V)) { + /* Invalid PTE */ + return TRANSLATE_FAIL; + } else if (!(pte & (PTE_R | PTE_W | PTE_X))) { + /* Inner PTE, continue walking */ base = ppn << PGSHIFT; - } else if ((pte & PTE_U) ? (mode == PRV_S) && !sum : !(mode == PRV_S)) { - break; - } else if (!(pte & PTE_V) || (!(pte & PTE_R) && (pte & PTE_W))) { - break; - } else if (access_type == MMU_INST_FETCH ? !(pte & PTE_X) : - access_type == MMU_DATA_LOAD ? !(pte & PTE_R) && - !(mxr && (pte & PTE_X)) : !((pte & PTE_R) && (pte & PTE_W))) { - break; + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == PTE_W) { + /* Reserved leaf PTE flags: PTE_W */ + return TRANSLATE_FAIL; + } else if ((pte & (PTE_R | PTE_W | PTE_X)) == (PTE_W | PTE_X)) { + /* Reserved leaf PTE flags: PTE_W + PTE_X */ + return TRANSLATE_FAIL; + } else if ((pte & PTE_U) && ((mode != PRV_U) && + (!sum || access_type == MMU_INST_FETCH))) { + /* User PTE flags when not U mode and mstatus.SUM is not set, + or the access type is an instruction fetch */ + return TRANSLATE_FAIL; + } else if (!(pte & PTE_U) && (mode != PRV_S)) { + /* Supervisor PTE flags when not S mode */ + return TRANSLATE_FAIL; + } else if (ppn & ((1ULL << ptshift) - 1)) { + /* Misaligned PPN */ + return TRANSLATE_FAIL; + } else if (access_type == MMU_DATA_LOAD && !((pte & PTE_R) || + ((pte & PTE_X) && mxr))) { + /* Read access check failed */ + return TRANSLATE_FAIL; + } else if (access_type == MMU_DATA_STORE && !(pte & PTE_W)) { + /* Write access check failed */ + return TRANSLATE_FAIL; + } else if (access_type == MMU_INST_FETCH && !(pte & PTE_X)) { + /* Fetch access check failed */ + return TRANSLATE_FAIL; } else { /* if necessary, set accessed and dirty bits. */ target_ulong updated_pte = pte | PTE_A | @@ -202,16 +258,19 @@ restart: /* Page table updates need to be atomic with MTTCG enabled */ if (updated_pte != pte) { - /* if accessed or dirty bits need updating, and the PTE is - * in RAM, then we do so atomically with a compare and swap. - * if the PTE is in IO space, then it can't be updated. - * if the PTE changed, then we must re-walk the page table - as the PTE is no longer valid */ + /* + * - if accessed or dirty bits need updating, and the PTE is + * in RAM, then we do so atomically with a compare and swap. + * - if the PTE is in IO space or ROM, then it can't be updated + * and we return TRANSLATE_FAIL. + * - if the PTE changed by the time we went to update it, then + * it is no longer valid and we must re-walk the page table. + */ MemoryRegion *mr; hwaddr l = sizeof(target_ulong), addr1; mr = address_space_translate(cs->as, pte_addr, &addr1, &l, false, MEMTXATTRS_UNSPECIFIED); - if (memory_access_is_direct(mr, true)) { + if (memory_region_is_ram(mr)) { target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); #if TCG_OVERSIZED_GUEST @@ -239,15 +298,15 @@ restart: target_ulong vpn = addr >> PGSHIFT; *physical = (ppn | (vpn & ((1L << ptshift) - 1))) << PGSHIFT; - if ((pte & PTE_R)) { + /* set permissions on the TLB entry */ + if ((pte & PTE_R) || ((pte & PTE_X) && mxr)) { *prot |= PAGE_READ; } if ((pte & PTE_X)) { *prot |= PAGE_EXEC; } - /* only add write permission on stores or if the page - is already dirty, so that we don't miss further - page table walks to update the dirty bit */ + /* add write permission on stores or if the page is already dirty, + so that we TLB miss on later writes to update the dirty bit */ if ((pte & PTE_W) && (access_type == MMU_DATA_STORE || (pte & PTE_D))) { *prot |= PAGE_WRITE; @@ -317,7 +376,7 @@ void riscv_cpu_do_unaligned_access(CPUState *cs, vaddr addr, g_assert_not_reached(); } env->badaddr = addr; - do_raise_exception_err(env, cs->exception_index, retaddr); + riscv_raise_exception(env, cs->exception_index, retaddr); } /* called by qemu's softmmu to fill the qemu tlb */ @@ -329,7 +388,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, int size, if (ret == TRANSLATE_FAIL) { RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - do_raise_exception_err(env, cs->exception_index, retaddr); + riscv_raise_exception(env, cs->exception_index, retaddr); } } @@ -355,7 +414,8 @@ int riscv_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, qemu_log_mask(CPU_LOG_MMU, "%s address=%" VADDR_PRIx " ret %d physical " TARGET_FMT_plx " prot %d\n", __func__, address, ret, pa, prot); - if (!pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) { + if (riscv_feature(env, RISCV_FEATURE_PMP) && + !pmp_hart_has_privs(env, pa, TARGET_PAGE_SIZE, 1 << rw)) { ret = TRANSLATE_FAIL; } if (ret == TRANSLATE_SUCCESS) { @@ -393,119 +453,85 @@ void riscv_cpu_do_interrupt(CPUState *cs) RISCVCPU *cpu = RISCV_CPU(cs); CPURISCVState *env = &cpu->env; - if (RISCV_DEBUG_INTERRUPT) { - int log_cause = cs->exception_index & RISCV_EXCP_INT_MASK; - if (cs->exception_index & RISCV_EXCP_INT_FLAG) { - qemu_log_mask(LOG_TRACE, "core 0: trap %s, epc 0x" TARGET_FMT_lx, - riscv_intr_names[log_cause], env->pc); - } else { - qemu_log_mask(LOG_TRACE, "core 0: intr %s, epc 0x" TARGET_FMT_lx, - riscv_excp_names[log_cause], env->pc); + /* cs->exception is 32-bits wide unlike mcause which is XLEN-bits wide + * so we mask off the MSB and separate into trap type and cause. + */ + bool async = !!(cs->exception_index & RISCV_EXCP_INT_FLAG); + target_ulong cause = cs->exception_index & RISCV_EXCP_INT_MASK; + target_ulong deleg = async ? env->mideleg : env->medeleg; + target_ulong tval = 0; + + static const int ecall_cause_map[] = { + [PRV_U] = RISCV_EXCP_U_ECALL, + [PRV_S] = RISCV_EXCP_S_ECALL, + [PRV_H] = RISCV_EXCP_H_ECALL, + [PRV_M] = RISCV_EXCP_M_ECALL + }; + + if (!async) { + /* set tval to badaddr for traps with address information */ + switch (cause) { + case RISCV_EXCP_INST_ADDR_MIS: + case RISCV_EXCP_INST_ACCESS_FAULT: + case RISCV_EXCP_LOAD_ADDR_MIS: + case RISCV_EXCP_STORE_AMO_ADDR_MIS: + case RISCV_EXCP_LOAD_ACCESS_FAULT: + case RISCV_EXCP_STORE_AMO_ACCESS_FAULT: + case RISCV_EXCP_INST_PAGE_FAULT: + case RISCV_EXCP_LOAD_PAGE_FAULT: + case RISCV_EXCP_STORE_PAGE_FAULT: + tval = env->badaddr; + break; + default: + break; + } + /* ecall is dispatched as one cause so translate based on mode */ + if (cause == RISCV_EXCP_U_ECALL) { + assert(env->priv <= 3); + cause = ecall_cause_map[env->priv]; } } - target_ulong fixed_cause = 0; - if (cs->exception_index & (RISCV_EXCP_INT_FLAG)) { - /* hacky for now. the MSB (bit 63) indicates interrupt but cs->exception - index is only 32 bits wide */ - fixed_cause = cs->exception_index & RISCV_EXCP_INT_MASK; - fixed_cause |= ((target_ulong)1) << (TARGET_LONG_BITS - 1); - } else { - /* fixup User ECALL -> correct priv ECALL */ - if (cs->exception_index == RISCV_EXCP_U_ECALL) { - switch (env->priv) { - case PRV_U: - fixed_cause = RISCV_EXCP_U_ECALL; - break; - case PRV_S: - fixed_cause = RISCV_EXCP_S_ECALL; - break; - case PRV_H: - fixed_cause = RISCV_EXCP_H_ECALL; - break; - case PRV_M: - fixed_cause = RISCV_EXCP_M_ECALL; - break; - } - } else { - fixed_cause = cs->exception_index; - } - } + trace_riscv_trap(env->mhartid, async, cause, env->pc, tval, cause < 16 ? + (async ? riscv_intr_names : riscv_excp_names)[cause] : "(unknown)"); - target_ulong backup_epc = env->pc; - - target_ulong bit = fixed_cause; - target_ulong deleg = env->medeleg; - - int hasbadaddr = - (fixed_cause == RISCV_EXCP_INST_ADDR_MIS) || - (fixed_cause == RISCV_EXCP_INST_ACCESS_FAULT) || - (fixed_cause == RISCV_EXCP_LOAD_ADDR_MIS) || - (fixed_cause == RISCV_EXCP_STORE_AMO_ADDR_MIS) || - (fixed_cause == RISCV_EXCP_LOAD_ACCESS_FAULT) || - (fixed_cause == RISCV_EXCP_STORE_AMO_ACCESS_FAULT) || - (fixed_cause == RISCV_EXCP_INST_PAGE_FAULT) || - (fixed_cause == RISCV_EXCP_LOAD_PAGE_FAULT) || - (fixed_cause == RISCV_EXCP_STORE_PAGE_FAULT); - - if (bit & ((target_ulong)1 << (TARGET_LONG_BITS - 1))) { - deleg = env->mideleg; - bit &= ~((target_ulong)1 << (TARGET_LONG_BITS - 1)); - } - - if (env->priv <= PRV_S && bit < 64 && ((deleg >> bit) & 1)) { + if (env->priv <= PRV_S && + cause < TARGET_LONG_BITS && ((deleg >> cause) & 1)) { /* handle the trap in S-mode */ - /* No need to check STVEC for misaligned - lower 2 bits cannot be set */ - env->pc = env->stvec; - env->scause = fixed_cause; - env->sepc = backup_epc; - - if (hasbadaddr) { - if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld - ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); - } - env->sbadaddr = env->badaddr; - } else { - /* otherwise we must clear sbadaddr/stval - * todo: support populating stval on illegal instructions */ - env->sbadaddr = 0; - } - target_ulong s = env->mstatus; s = set_field(s, MSTATUS_SPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_SIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_SPP, env->priv); s = set_field(s, MSTATUS_SIE, 0); - csr_write_helper(env, s, CSR_MSTATUS); - riscv_set_mode(env, PRV_S); + env->mstatus = s; + env->scause = cause | ~(((target_ulong)-1) >> async); + env->sepc = env->pc; + env->sbadaddr = tval; + env->pc = (env->stvec >> 2 << 2) + + ((async && (env->stvec & 3) == 1) ? cause * 4 : 0); + riscv_cpu_set_mode(env, PRV_S); } else { - /* No need to check MTVEC for misaligned - lower 2 bits cannot be set */ - env->pc = env->mtvec; - env->mepc = backup_epc; - env->mcause = fixed_cause; - - if (hasbadaddr) { - if (RISCV_DEBUG_INTERRUPT) { - qemu_log_mask(LOG_TRACE, "core " TARGET_FMT_ld - ": badaddr 0x" TARGET_FMT_lx, env->mhartid, env->badaddr); - } - env->mbadaddr = env->badaddr; - } else { - /* otherwise we must clear mbadaddr/mtval - * todo: support populating mtval on illegal instructions */ - env->mbadaddr = 0; - } - + /* handle the trap in M-mode */ target_ulong s = env->mstatus; s = set_field(s, MSTATUS_MPIE, env->priv_ver >= PRIV_VERSION_1_10_0 ? get_field(s, MSTATUS_MIE) : get_field(s, MSTATUS_UIE << env->priv)); s = set_field(s, MSTATUS_MPP, env->priv); s = set_field(s, MSTATUS_MIE, 0); - csr_write_helper(env, s, CSR_MSTATUS); - riscv_set_mode(env, PRV_M); + env->mstatus = s; + env->mcause = cause | ~(((target_ulong)-1) >> async); + env->mepc = env->pc; + env->mbadaddr = tval; + env->pc = (env->mtvec >> 2 << 2) + + ((async && (env->mtvec & 3) == 1) ? cause * 4 : 0); + riscv_cpu_set_mode(env, PRV_M); } - /* TODO yield load reservation */ + + /* NOTE: it is not necessary to yield load reservations here. It is only + * necessary for an SC from "another hart" to cause a load reservation + * to be yielded. Refer to the memory consistency model section of the + * RISC-V ISA Specification. + */ + #endif cs->exception_index = EXCP_NONE; /* mark handled to qemu */ } diff --git a/target/riscv/cpu_user.h b/target/riscv/cpu_user.h index c2199610ab..52d380aa98 100644 --- a/target/riscv/cpu_user.h +++ b/target/riscv/cpu_user.h @@ -10,4 +10,5 @@ #define xA4 14 #define xA5 15 #define xA6 16 -#define xA7 17 /* syscall number goes here */ +#define xA7 17 /* syscall number for RVI ABI */ +#define xT0 5 /* syscall number for RVE ABI */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c new file mode 100644 index 0000000000..e1d91b6c60 --- /dev/null +++ b/target/riscv/csr.c @@ -0,0 +1,930 @@ +/* + * RISC-V Control and Status Registers. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2017-2018 SiFive, Inc. + * + * 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 "qemu/log.h" +#include "cpu.h" +#include "qemu/main-loop.h" +#include "exec/exec-all.h" + +/* CSR function table */ +static riscv_csr_operations csr_ops[]; + +/* CSR function table constants */ +enum { + CSR_TABLE_SIZE = 0x1000 +}; + +/* CSR function table public API */ +void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) +{ + *ops = csr_ops[csrno & (CSR_TABLE_SIZE - 1)]; +} + +void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) +{ + csr_ops[csrno & (CSR_TABLE_SIZE - 1)] = *ops; +} + +/* Predicates */ +static int fs(CPURISCVState *env, int csrno) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + return 0; +} + +static int ctr(CPURISCVState *env, int csrno) +{ +#if !defined(CONFIG_USER_ONLY) + uint32_t ctr_en = ~0u; + + if (env->priv < PRV_M) { + ctr_en &= env->mcounteren; + } + if (env->priv < PRV_S) { + ctr_en &= env->scounteren; + } + if (!(ctr_en & (1u << (csrno & 31)))) { + return -1; + } +#endif + return 0; +} + +#if !defined(CONFIG_USER_ONLY) +static int any(CPURISCVState *env, int csrno) +{ + return 0; +} + +static int smode(CPURISCVState *env, int csrno) +{ + return -!riscv_has_ext(env, RVS); +} + +static int pmp(CPURISCVState *env, int csrno) +{ + return -!riscv_feature(env, RISCV_FEATURE_PMP); +} +#endif + +/* User Floating-Point CSRs */ +static int read_fflags(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + *val = riscv_cpu_get_fflags(env); + return 0; +} + +static int write_fflags(CPURISCVState *env, int csrno, target_ulong val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } + env->mstatus |= MSTATUS_FS; +#endif + riscv_cpu_set_fflags(env, val & (FSR_AEXC >> FSR_AEXC_SHIFT)); + return 0; +} + +static int read_frm(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + *val = env->frm; + return 0; +} + +static int write_frm(CPURISCVState *env, int csrno, target_ulong val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } + env->mstatus |= MSTATUS_FS; +#endif + env->frm = val & (FSR_RD >> FSR_RD_SHIFT); + return 0; +} + +static int read_fcsr(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } +#endif + *val = (riscv_cpu_get_fflags(env) << FSR_AEXC_SHIFT) + | (env->frm << FSR_RD_SHIFT); + return 0; +} + +static int write_fcsr(CPURISCVState *env, int csrno, target_ulong val) +{ +#if !defined(CONFIG_USER_ONLY) + if (!env->debugger && !(env->mstatus & MSTATUS_FS)) { + return -1; + } + env->mstatus |= MSTATUS_FS; +#endif + env->frm = (val & FSR_RD) >> FSR_RD_SHIFT; + riscv_cpu_set_fflags(env, (val & FSR_AEXC) >> FSR_AEXC_SHIFT); + return 0; +} + +/* User Timers and Counters */ +static int read_instret(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + *val = cpu_get_icount(); + } else { + *val = cpu_get_host_ticks(); + } +#else + *val = cpu_get_host_ticks(); +#endif + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_instreth(CPURISCVState *env, int csrno, target_ulong *val) +{ +#if !defined(CONFIG_USER_ONLY) + if (use_icount) { + *val = cpu_get_icount() >> 32; + } else { + *val = cpu_get_host_ticks() >> 32; + } +#else + *val = cpu_get_host_ticks() >> 32; +#endif + return 0; +} +#endif /* TARGET_RISCV32 */ + +#if defined(CONFIG_USER_ONLY) +static int read_time(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = cpu_get_host_ticks(); + return 0; +} + +#if defined(TARGET_RISCV32) +static int read_timeh(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = cpu_get_host_ticks() >> 32; + return 0; +} +#endif + +#else /* CONFIG_USER_ONLY */ + +/* Machine constants */ + +#define M_MODE_INTERRUPTS (MIP_MSIP | MIP_MTIP | MIP_MEIP) +#define S_MODE_INTERRUPTS (MIP_SSIP | MIP_STIP | MIP_SEIP) + +static const target_ulong delegable_ints = S_MODE_INTERRUPTS; +static const target_ulong all_ints = M_MODE_INTERRUPTS | S_MODE_INTERRUPTS; +static const target_ulong delegable_excps = + (1ULL << (RISCV_EXCP_INST_ADDR_MIS)) | + (1ULL << (RISCV_EXCP_INST_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_ILLEGAL_INST)) | + (1ULL << (RISCV_EXCP_BREAKPOINT)) | + (1ULL << (RISCV_EXCP_LOAD_ADDR_MIS)) | + (1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS)) | + (1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT)) | + (1ULL << (RISCV_EXCP_U_ECALL)) | + (1ULL << (RISCV_EXCP_S_ECALL)) | + (1ULL << (RISCV_EXCP_H_ECALL)) | + (1ULL << (RISCV_EXCP_M_ECALL)) | + (1ULL << (RISCV_EXCP_INST_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT)) | + (1ULL << (RISCV_EXCP_STORE_PAGE_FAULT)); +static const target_ulong sstatus_v1_9_mask = SSTATUS_SIE | SSTATUS_SPIE | + SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | + SSTATUS_SUM | SSTATUS_SD; +static const target_ulong sstatus_v1_10_mask = SSTATUS_SIE | SSTATUS_SPIE | + SSTATUS_UIE | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS | + SSTATUS_SUM | SSTATUS_MXR | SSTATUS_SD; + +#if defined(TARGET_RISCV32) +static const char valid_vm_1_09[16] = { + [VM_1_09_MBARE] = 1, + [VM_1_09_SV32] = 1, +}; +static const char valid_vm_1_10[16] = { + [VM_1_10_MBARE] = 1, + [VM_1_10_SV32] = 1 +}; +#elif defined(TARGET_RISCV64) +static const char valid_vm_1_09[16] = { + [VM_1_09_MBARE] = 1, + [VM_1_09_SV39] = 1, + [VM_1_09_SV48] = 1, +}; +static const char valid_vm_1_10[16] = { + [VM_1_10_MBARE] = 1, + [VM_1_10_SV39] = 1, + [VM_1_10_SV48] = 1, + [VM_1_10_SV57] = 1 +}; +#endif /* CONFIG_USER_ONLY */ + +/* Machine Information Registers */ +static int read_zero(CPURISCVState *env, int csrno, target_ulong *val) +{ + return *val = 0; +} + +static int read_mhartid(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mhartid; + return 0; +} + +/* Machine Trap Setup */ +static int read_mstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mstatus; + return 0; +} + +static int validate_vm(CPURISCVState *env, target_ulong vm) +{ + return (env->priv_ver >= PRIV_VERSION_1_10_0) ? + valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; +} + +static int write_mstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong mstatus = env->mstatus; + target_ulong mask = 0; + target_ulong mpp = get_field(val, MSTATUS_MPP); + + /* flush tlb on mstatus fields that affect VM */ + if (env->priv_ver <= PRIV_VERSION_1_09_1) { + if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | + MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { + tlb_flush(CPU(riscv_env_get_cpu(env))); + } + mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | + MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_MPP | MSTATUS_MXR | + (validate_vm(env, get_field(val, MSTATUS_VM)) ? + MSTATUS_VM : 0); + } + if (env->priv_ver >= PRIV_VERSION_1_10_0) { + if ((val ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | + MSTATUS_MPRV | MSTATUS_SUM)) { + tlb_flush(CPU(riscv_env_get_cpu(env))); + } + mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | + MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | + MSTATUS_MPP | MSTATUS_MXR | MSTATUS_TVM | MSTATUS_TSR | + MSTATUS_TW; + } + + /* silenty discard mstatus.mpp writes for unsupported modes */ + if (mpp == PRV_H || + (!riscv_has_ext(env, RVS) && mpp == PRV_S) || + (!riscv_has_ext(env, RVU) && mpp == PRV_U)) { + mask &= ~MSTATUS_MPP; + } + + mstatus = (mstatus & ~mask) | (val & mask); + + int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | + ((mstatus & MSTATUS_XS) == MSTATUS_XS); + mstatus = set_field(mstatus, MSTATUS_SD, dirty); + env->mstatus = mstatus; + + return 0; +} + +static int read_misa(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->misa; + return 0; +} + +static int write_misa(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MISA)) { + /* drop write to misa */ + return 0; + } + + /* 'I' or 'E' must be present */ + if (!(val & (RVI | RVE))) { + /* It is not, drop write to misa */ + return 0; + } + + /* 'E' excludes all other extensions */ + if (val & RVE) { + /* when we support 'E' we can do "val = RVE;" however + * for now we just drop writes if 'E' is present. + */ + return 0; + } + + /* Mask extensions that are not supported by this hart */ + val &= env->misa_mask; + + /* Mask extensions that are not supported by QEMU */ + val &= (RVI | RVE | RVM | RVA | RVF | RVD | RVC | RVS | RVU); + + /* 'D' depends on 'F', so clear 'D' if 'F' is not present */ + if ((val & RVD) && !(val & RVF)) { + val &= ~RVD; + } + + /* Suppress 'C' if next instruction is not aligned + * TODO: this should check next_pc + */ + if ((val & RVC) && (GETPC() & ~3) != 0) { + val &= ~RVC; + } + + /* misa.MXL writes are not supported by QEMU */ + val = (env->misa & MISA_MXL) | (val & ~MISA_MXL); + + /* flush translation cache */ + if (val != env->misa) { + tb_flush(CPU(riscv_env_get_cpu(env))); + } + + env->misa = val; + + return 0; +} + +static int read_medeleg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->medeleg; + return 0; +} + +static int write_medeleg(CPURISCVState *env, int csrno, target_ulong val) +{ + env->medeleg = (env->medeleg & ~delegable_excps) | (val & delegable_excps); + return 0; +} + +static int read_mideleg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mideleg; + return 0; +} + +static int write_mideleg(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mideleg = (env->mideleg & ~delegable_ints) | (val & delegable_ints); + return 0; +} + +static int read_mie(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mie; + return 0; +} + +static int write_mie(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mie = (env->mie & ~all_ints) | (val & all_ints); + return 0; +} + +static int read_mtvec(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mtvec; + return 0; +} + +static int write_mtvec(CPURISCVState *env, int csrno, target_ulong val) +{ + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val & 3) < 2) { + env->mtvec = val; + } else { + qemu_log_mask(LOG_UNIMP, "CSR_MTVEC: reserved mode not supported\n"); + } + return 0; +} + +static int read_mcounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + *val = env->mcounteren; + return 0; +} + +static int write_mcounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + env->mcounteren = val; + return 0; +} + +static int read_mscounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + *val = env->mcounteren; + return 0; +} + +static int write_mscounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + env->mcounteren = val; + return 0; +} + +static int read_mucounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + *val = env->scounteren; + return 0; +} + +static int write_mucounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver > PRIV_VERSION_1_09_1) { + return -1; + } + env->scounteren = val; + return 0; +} + +/* Machine Trap Handling */ +static int read_mscratch(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mscratch; + return 0; +} + +static int write_mscratch(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mscratch = val; + return 0; +} + +static int read_mepc(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mepc; + return 0; +} + +static int write_mepc(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mepc = val; + return 0; +} + +static int read_mcause(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mcause; + return 0; +} + +static int write_mcause(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mcause = val; + return 0; +} + +static int read_mbadaddr(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mbadaddr; + return 0; +} + +static int write_mbadaddr(CPURISCVState *env, int csrno, target_ulong val) +{ + env->mbadaddr = val; + return 0; +} + +static int rmw_mip(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + RISCVCPU *cpu = riscv_env_get_cpu(env); + /* Allow software control of delegable interrupts not claimed by hardware */ + target_ulong mask = write_mask & delegable_ints & ~env->miclaim; + uint32_t old_mip; + + if (mask) { + qemu_mutex_lock_iothread(); + old_mip = riscv_cpu_update_mip(cpu, mask, (new_value & mask)); + qemu_mutex_unlock_iothread(); + } else { + old_mip = atomic_read(&env->mip); + } + + if (ret_value) { + *ret_value = old_mip; + } + + return 0; +} + +/* Supervisor Trap Setup */ +static int read_sstatus(CPURISCVState *env, int csrno, target_ulong *val) +{ + target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ? + sstatus_v1_10_mask : sstatus_v1_9_mask); + *val = env->mstatus & mask; + return 0; +} + +static int write_sstatus(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong mask = ((env->priv_ver >= PRIV_VERSION_1_10_0) ? + sstatus_v1_10_mask : sstatus_v1_9_mask); + target_ulong newval = (env->mstatus & ~mask) | (val & mask); + return write_mstatus(env, CSR_MSTATUS, newval); +} + +static int read_sie(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->mie & env->mideleg; + return 0; +} + +static int write_sie(CPURISCVState *env, int csrno, target_ulong val) +{ + target_ulong newval = (env->mie & ~env->mideleg) | (val & env->mideleg); + return write_mie(env, CSR_MIE, newval); +} + +static int read_stvec(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->stvec; + return 0; +} + +static int write_stvec(CPURISCVState *env, int csrno, target_ulong val) +{ + /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ + if ((val & 3) < 2) { + env->stvec = val; + } else { + qemu_log_mask(LOG_UNIMP, "CSR_STVEC: reserved mode not supported\n"); + } + return 0; +} + +static int read_scounteren(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + *val = env->scounteren; + return 0; +} + +static int write_scounteren(CPURISCVState *env, int csrno, target_ulong val) +{ + if (env->priv_ver < PRIV_VERSION_1_10_0) { + return -1; + } + env->scounteren = val; + return 0; +} + +/* Supervisor Trap Handling */ +static int read_sscratch(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->sscratch; + return 0; +} + +static int write_sscratch(CPURISCVState *env, int csrno, target_ulong val) +{ + env->sscratch = val; + return 0; +} + +static int read_sepc(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->sepc; + return 0; +} + +static int write_sepc(CPURISCVState *env, int csrno, target_ulong val) +{ + env->sepc = val; + return 0; +} + +static int read_scause(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->scause; + return 0; +} + +static int write_scause(CPURISCVState *env, int csrno, target_ulong val) +{ + env->scause = val; + return 0; +} + +static int read_sbadaddr(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = env->sbadaddr; + return 0; +} + +static int write_sbadaddr(CPURISCVState *env, int csrno, target_ulong val) +{ + env->sbadaddr = val; + return 0; +} + +static int rmw_sip(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + return rmw_mip(env, CSR_MSTATUS, ret_value, new_value, + write_mask & env->mideleg); +} + +/* Supervisor Protection and Translation */ +static int read_satp(CPURISCVState *env, int csrno, target_ulong *val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + *val = 0; + } else if (env->priv_ver >= PRIV_VERSION_1_10_0) { + if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { + return -1; + } else { + *val = env->satp; + } + } else { + *val = env->sptbr; + } + return 0; +} + +static int write_satp(CPURISCVState *env, int csrno, target_ulong val) +{ + if (!riscv_feature(env, RISCV_FEATURE_MMU)) { + return 0; + } + if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val ^ env->sptbr)) { + tlb_flush(CPU(riscv_env_get_cpu(env))); + env->sptbr = val & (((target_ulong) + 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); + } + if (env->priv_ver >= PRIV_VERSION_1_10_0 && + validate_vm(env, get_field(val, SATP_MODE)) && + ((val ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) + { + if (env->priv == PRV_S && get_field(env->mstatus, MSTATUS_TVM)) { + return -1; + } else { + tlb_flush(CPU(riscv_env_get_cpu(env))); + env->satp = val; + } + } + return 0; +} + +/* Physical Memory Protection */ +static int read_pmpcfg(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); + return 0; +} + +static int write_pmpcfg(CPURISCVState *env, int csrno, target_ulong val) +{ + pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val); + return 0; +} + +static int read_pmpaddr(CPURISCVState *env, int csrno, target_ulong *val) +{ + *val = pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); + return 0; +} + +static int write_pmpaddr(CPURISCVState *env, int csrno, target_ulong val) +{ + pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val); + return 0; +} + +#endif + +/* + * riscv_csrrw - read and/or update control and status register + * + * csrr <-> riscv_csrrw(env, csrno, ret_value, 0, 0); + * csrrw <-> riscv_csrrw(env, csrno, ret_value, value, -1); + * csrrs <-> riscv_csrrw(env, csrno, ret_value, -1, value); + * csrrc <-> riscv_csrrw(env, csrno, ret_value, 0, value); + */ + +int riscv_csrrw(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + int ret; + target_ulong old_value; + + /* check privileges and return -1 if check fails */ +#if !defined(CONFIG_USER_ONLY) + int csr_priv = get_field(csrno, 0x300); + int read_only = get_field(csrno, 0xC00) == 3; + if ((write_mask && read_only) || (env->priv < csr_priv)) { + return -1; + } +#endif + + /* check predicate */ + if (!csr_ops[csrno].predicate || csr_ops[csrno].predicate(env, csrno) < 0) { + return -1; + } + + /* execute combined read/write operation if it exists */ + if (csr_ops[csrno].op) { + return csr_ops[csrno].op(env, csrno, ret_value, new_value, write_mask); + } + + /* if no accessor exists then return failure */ + if (!csr_ops[csrno].read) { + return -1; + } + + /* read old value */ + ret = csr_ops[csrno].read(env, csrno, &old_value); + if (ret < 0) { + return ret; + } + + /* write value if writable and write mask set, otherwise drop writes */ + if (write_mask) { + new_value = (old_value & ~write_mask) | (new_value & write_mask); + if (csr_ops[csrno].write) { + ret = csr_ops[csrno].write(env, csrno, new_value); + if (ret < 0) { + return ret; + } + } + } + + /* return old value */ + if (ret_value) { + *ret_value = old_value; + } + + return 0; +} + +/* + * Debugger support. If not in user mode, set env->debugger before the + * riscv_csrrw call and clear it after the call. + */ +int riscv_csrrw_debug(CPURISCVState *env, int csrno, target_ulong *ret_value, + target_ulong new_value, target_ulong write_mask) +{ + int ret; +#if !defined(CONFIG_USER_ONLY) + env->debugger = true; +#endif + ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); +#if !defined(CONFIG_USER_ONLY) + env->debugger = false; +#endif + return ret; +} + +/* Control and Status Register function table */ +static riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { + /* User Floating-Point CSRs */ + [CSR_FFLAGS] = { fs, read_fflags, write_fflags }, + [CSR_FRM] = { fs, read_frm, write_frm }, + [CSR_FCSR] = { fs, read_fcsr, write_fcsr }, + + /* User Timers and Counters */ + [CSR_CYCLE] = { ctr, read_instret }, + [CSR_INSTRET] = { ctr, read_instret }, +#if defined(TARGET_RISCV32) + [CSR_CYCLEH] = { ctr, read_instreth }, + [CSR_INSTRETH] = { ctr, read_instreth }, +#endif + + /* User-level time CSRs are only available in linux-user + * In privileged mode, the monitor emulates these CSRs */ +#if defined(CONFIG_USER_ONLY) + [CSR_TIME] = { ctr, read_time }, +#if defined(TARGET_RISCV32) + [CSR_TIMEH] = { ctr, read_timeh }, +#endif +#endif + +#if !defined(CONFIG_USER_ONLY) + /* Machine Timers and Counters */ + [CSR_MCYCLE] = { any, read_instret }, + [CSR_MINSTRET] = { any, read_instret }, +#if defined(TARGET_RISCV32) + [CSR_MCYCLEH] = { any, read_instreth }, + [CSR_MINSTRETH] = { any, read_instreth }, +#endif + + /* Machine Information Registers */ + [CSR_MVENDORID] = { any, read_zero }, + [CSR_MARCHID] = { any, read_zero }, + [CSR_MIMPID] = { any, read_zero }, + [CSR_MHARTID] = { any, read_mhartid }, + + /* Machine Trap Setup */ + [CSR_MSTATUS] = { any, read_mstatus, write_mstatus }, + [CSR_MISA] = { any, read_misa, write_misa }, + [CSR_MIDELEG] = { any, read_mideleg, write_mideleg }, + [CSR_MEDELEG] = { any, read_medeleg, write_medeleg }, + [CSR_MIE] = { any, read_mie, write_mie }, + [CSR_MTVEC] = { any, read_mtvec, write_mtvec }, + [CSR_MCOUNTEREN] = { any, read_mcounteren, write_mcounteren }, + + /* Legacy Counter Setup (priv v1.9.1) */ + [CSR_MUCOUNTEREN] = { any, read_mucounteren, write_mucounteren }, + [CSR_MSCOUNTEREN] = { any, read_mscounteren, write_mscounteren }, + + /* Machine Trap Handling */ + [CSR_MSCRATCH] = { any, read_mscratch, write_mscratch }, + [CSR_MEPC] = { any, read_mepc, write_mepc }, + [CSR_MCAUSE] = { any, read_mcause, write_mcause }, + [CSR_MBADADDR] = { any, read_mbadaddr, write_mbadaddr }, + [CSR_MIP] = { any, NULL, NULL, rmw_mip }, + + /* Supervisor Trap Setup */ + [CSR_SSTATUS] = { smode, read_sstatus, write_sstatus }, + [CSR_SIE] = { smode, read_sie, write_sie }, + [CSR_STVEC] = { smode, read_stvec, write_stvec }, + [CSR_SCOUNTEREN] = { smode, read_scounteren, write_scounteren }, + + /* Supervisor Trap Handling */ + [CSR_SSCRATCH] = { smode, read_sscratch, write_sscratch }, + [CSR_SEPC] = { smode, read_sepc, write_sepc }, + [CSR_SCAUSE] = { smode, read_scause, write_scause }, + [CSR_SBADADDR] = { smode, read_sbadaddr, write_sbadaddr }, + [CSR_SIP] = { smode, NULL, NULL, rmw_sip }, + + /* Supervisor Protection and Translation */ + [CSR_SATP] = { smode, read_satp, write_satp }, + + /* Physical Memory Protection */ + [CSR_PMPCFG0 ... CSR_PMPADDR9] = { pmp, read_pmpcfg, write_pmpcfg }, + [CSR_PMPADDR0 ... CSR_PMPADDR15] = { pmp, read_pmpaddr, write_pmpaddr }, + + /* Performance Counters */ + [CSR_HPMCOUNTER3 ... CSR_HPMCOUNTER31] = { ctr, read_zero }, + [CSR_MHPMCOUNTER3 ... CSR_MHPMCOUNTER31] = { any, read_zero }, + [CSR_MHPMEVENT3 ... CSR_MHPMEVENT31] = { any, read_zero }, +#if defined(TARGET_RISCV32) + [CSR_HPMCOUNTER3H ... CSR_HPMCOUNTER31H] = { ctr, read_zero }, + [CSR_MHPMCOUNTER3H ... CSR_MHPMCOUNTER31H] = { any, read_zero }, +#endif +#endif /* !CONFIG_USER_ONLY */ +}; diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index fdb87d8d82..b4f818a646 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -17,13 +17,12 @@ */ #include "qemu/osdep.h" -#include #include "cpu.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -target_ulong cpu_riscv_get_fflags(CPURISCVState *env) +target_ulong riscv_cpu_get_fflags(CPURISCVState *env) { int soft = get_float_exception_flags(&env->fp_status); target_ulong hard = 0; @@ -37,7 +36,7 @@ target_ulong cpu_riscv_get_fflags(CPURISCVState *env) return hard; } -void cpu_riscv_set_fflags(CPURISCVState *env, target_ulong hard) +void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong hard) { int soft = 0; @@ -74,7 +73,7 @@ void helper_set_rounding_mode(CPURISCVState *env, uint32_t rm) softrm = float_round_ties_away; break; default: - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } set_float_rounding_mode(softrm, &env->fp_status); diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 4f919b6c34..dfcdd834cf 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -21,6 +21,255 @@ #include "exec/gdbstub.h" #include "cpu.h" +/* + * The GDB CSR xml files list them in documentation order, not numerical order, + * and are missing entries for unnamed CSRs. So we need to map the gdb numbers + * to the hardware numbers. + */ + +static int csr_register_map[] = { + CSR_USTATUS, + CSR_UIE, + CSR_UTVEC, + CSR_USCRATCH, + CSR_UEPC, + CSR_UCAUSE, + CSR_UTVAL, + CSR_UIP, + CSR_FFLAGS, + CSR_FRM, + CSR_FCSR, + CSR_CYCLE, + CSR_TIME, + CSR_INSTRET, + CSR_HPMCOUNTER3, + CSR_HPMCOUNTER4, + CSR_HPMCOUNTER5, + CSR_HPMCOUNTER6, + CSR_HPMCOUNTER7, + CSR_HPMCOUNTER8, + CSR_HPMCOUNTER9, + CSR_HPMCOUNTER10, + CSR_HPMCOUNTER11, + CSR_HPMCOUNTER12, + CSR_HPMCOUNTER13, + CSR_HPMCOUNTER14, + CSR_HPMCOUNTER15, + CSR_HPMCOUNTER16, + CSR_HPMCOUNTER17, + CSR_HPMCOUNTER18, + CSR_HPMCOUNTER19, + CSR_HPMCOUNTER20, + CSR_HPMCOUNTER21, + CSR_HPMCOUNTER22, + CSR_HPMCOUNTER23, + CSR_HPMCOUNTER24, + CSR_HPMCOUNTER25, + CSR_HPMCOUNTER26, + CSR_HPMCOUNTER27, + CSR_HPMCOUNTER28, + CSR_HPMCOUNTER29, + CSR_HPMCOUNTER30, + CSR_HPMCOUNTER31, + CSR_CYCLEH, + CSR_TIMEH, + CSR_INSTRETH, + CSR_HPMCOUNTER3H, + CSR_HPMCOUNTER4H, + CSR_HPMCOUNTER5H, + CSR_HPMCOUNTER6H, + CSR_HPMCOUNTER7H, + CSR_HPMCOUNTER8H, + CSR_HPMCOUNTER9H, + CSR_HPMCOUNTER10H, + CSR_HPMCOUNTER11H, + CSR_HPMCOUNTER12H, + CSR_HPMCOUNTER13H, + CSR_HPMCOUNTER14H, + CSR_HPMCOUNTER15H, + CSR_HPMCOUNTER16H, + CSR_HPMCOUNTER17H, + CSR_HPMCOUNTER18H, + CSR_HPMCOUNTER19H, + CSR_HPMCOUNTER20H, + CSR_HPMCOUNTER21H, + CSR_HPMCOUNTER22H, + CSR_HPMCOUNTER23H, + CSR_HPMCOUNTER24H, + CSR_HPMCOUNTER25H, + CSR_HPMCOUNTER26H, + CSR_HPMCOUNTER27H, + CSR_HPMCOUNTER28H, + CSR_HPMCOUNTER29H, + CSR_HPMCOUNTER30H, + CSR_HPMCOUNTER31H, + CSR_SSTATUS, + CSR_SEDELEG, + CSR_SIDELEG, + CSR_SIE, + CSR_STVEC, + CSR_SCOUNTEREN, + CSR_SSCRATCH, + CSR_SEPC, + CSR_SCAUSE, + CSR_STVAL, + CSR_SIP, + CSR_SATP, + CSR_MVENDORID, + CSR_MARCHID, + CSR_MIMPID, + CSR_MHARTID, + CSR_MSTATUS, + CSR_MISA, + CSR_MEDELEG, + CSR_MIDELEG, + CSR_MIE, + CSR_MTVEC, + CSR_MCOUNTEREN, + CSR_MSCRATCH, + CSR_MEPC, + CSR_MCAUSE, + CSR_MTVAL, + CSR_MIP, + CSR_PMPCFG0, + CSR_PMPCFG1, + CSR_PMPCFG2, + CSR_PMPCFG3, + CSR_PMPADDR0, + CSR_PMPADDR1, + CSR_PMPADDR2, + CSR_PMPADDR3, + CSR_PMPADDR4, + CSR_PMPADDR5, + CSR_PMPADDR6, + CSR_PMPADDR7, + CSR_PMPADDR8, + CSR_PMPADDR9, + CSR_PMPADDR10, + CSR_PMPADDR11, + CSR_PMPADDR12, + CSR_PMPADDR13, + CSR_PMPADDR14, + CSR_PMPADDR15, + CSR_MCYCLE, + CSR_MINSTRET, + CSR_MHPMCOUNTER3, + CSR_MHPMCOUNTER4, + CSR_MHPMCOUNTER5, + CSR_MHPMCOUNTER6, + CSR_MHPMCOUNTER7, + CSR_MHPMCOUNTER8, + CSR_MHPMCOUNTER9, + CSR_MHPMCOUNTER10, + CSR_MHPMCOUNTER11, + CSR_MHPMCOUNTER12, + CSR_MHPMCOUNTER13, + CSR_MHPMCOUNTER14, + CSR_MHPMCOUNTER15, + CSR_MHPMCOUNTER16, + CSR_MHPMCOUNTER17, + CSR_MHPMCOUNTER18, + CSR_MHPMCOUNTER19, + CSR_MHPMCOUNTER20, + CSR_MHPMCOUNTER21, + CSR_MHPMCOUNTER22, + CSR_MHPMCOUNTER23, + CSR_MHPMCOUNTER24, + CSR_MHPMCOUNTER25, + CSR_MHPMCOUNTER26, + CSR_MHPMCOUNTER27, + CSR_MHPMCOUNTER28, + CSR_MHPMCOUNTER29, + CSR_MHPMCOUNTER30, + CSR_MHPMCOUNTER31, + CSR_MCYCLEH, + CSR_MINSTRETH, + CSR_MHPMCOUNTER3H, + CSR_MHPMCOUNTER4H, + CSR_MHPMCOUNTER5H, + CSR_MHPMCOUNTER6H, + CSR_MHPMCOUNTER7H, + CSR_MHPMCOUNTER8H, + CSR_MHPMCOUNTER9H, + CSR_MHPMCOUNTER10H, + CSR_MHPMCOUNTER11H, + CSR_MHPMCOUNTER12H, + CSR_MHPMCOUNTER13H, + CSR_MHPMCOUNTER14H, + CSR_MHPMCOUNTER15H, + CSR_MHPMCOUNTER16H, + CSR_MHPMCOUNTER17H, + CSR_MHPMCOUNTER18H, + CSR_MHPMCOUNTER19H, + CSR_MHPMCOUNTER20H, + CSR_MHPMCOUNTER21H, + CSR_MHPMCOUNTER22H, + CSR_MHPMCOUNTER23H, + CSR_MHPMCOUNTER24H, + CSR_MHPMCOUNTER25H, + CSR_MHPMCOUNTER26H, + CSR_MHPMCOUNTER27H, + CSR_MHPMCOUNTER28H, + CSR_MHPMCOUNTER29H, + CSR_MHPMCOUNTER30H, + CSR_MHPMCOUNTER31H, + CSR_MHPMEVENT3, + CSR_MHPMEVENT4, + CSR_MHPMEVENT5, + CSR_MHPMEVENT6, + CSR_MHPMEVENT7, + CSR_MHPMEVENT8, + CSR_MHPMEVENT9, + CSR_MHPMEVENT10, + CSR_MHPMEVENT11, + CSR_MHPMEVENT12, + CSR_MHPMEVENT13, + CSR_MHPMEVENT14, + CSR_MHPMEVENT15, + CSR_MHPMEVENT16, + CSR_MHPMEVENT17, + CSR_MHPMEVENT18, + CSR_MHPMEVENT19, + CSR_MHPMEVENT20, + CSR_MHPMEVENT21, + CSR_MHPMEVENT22, + CSR_MHPMEVENT23, + CSR_MHPMEVENT24, + CSR_MHPMEVENT25, + CSR_MHPMEVENT26, + CSR_MHPMEVENT27, + CSR_MHPMEVENT28, + CSR_MHPMEVENT29, + CSR_MHPMEVENT30, + CSR_MHPMEVENT31, + CSR_TSELECT, + CSR_TDATA1, + CSR_TDATA2, + CSR_TDATA3, + CSR_DCSR, + CSR_DPC, + CSR_DSCRATCH, + CSR_HSTATUS, + CSR_HEDELEG, + CSR_HIDELEG, + CSR_HIE, + CSR_HTVEC, + CSR_HSCRATCH, + CSR_HEPC, + CSR_HCAUSE, + CSR_HBADADDR, + CSR_HIP, + CSR_MBASE, + CSR_MBOUND, + CSR_MIBASE, + CSR_MIBOUND, + CSR_MDBASE, + CSR_MDBOUND, + CSR_MUCOUNTEREN, + CSR_MSCOUNTEREN, + CSR_MHCOUNTEREN, +}; + int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) { RISCVCPU *cpu = RISCV_CPU(cs); @@ -30,10 +279,6 @@ int riscv_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return gdb_get_regl(mem_buf, env->gpr[n]); } else if (n == 32) { return gdb_get_regl(mem_buf, env->pc); - } else if (n < 65) { - return gdb_get_reg64(mem_buf, env->fpr[n - 33]); - } else if (n < 4096 + 65) { - return gdb_get_regl(mem_buf, csr_read_helper(env, n - 65)); } return 0; } @@ -52,11 +297,100 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) } else if (n == 32) { env->pc = ldtul_p(mem_buf); return sizeof(target_ulong); - } else if (n < 65) { - env->fpr[n - 33] = ldq_p(mem_buf); /* always 64-bit */ - return sizeof(uint64_t); - } else if (n < 4096 + 65) { - csr_write_helper(env, ldtul_p(mem_buf), n - 65); } return 0; } + +static int riscv_gdb_get_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + return gdb_get_reg64(mem_buf, env->fpr[n]); + /* there is hole between ft11 and fflags in fpu.xml */ + } else if (n < 36 && n > 32) { + target_ulong val = 0; + int result; + /* + * CSR_FFLAGS is at index 8 in csr_register, and gdb says it is FP + * register 33, so we recalculate the map index. + * This also works for CSR_FRM and CSR_FCSR. + */ + result = riscv_csrrw_debug(env, n - 33 + 8, &val, 0, 0); + if (result == 0) { + return gdb_get_regl(mem_buf, val); + } + } + return 0; +} + +static int riscv_gdb_set_fpu(CPURISCVState *env, uint8_t *mem_buf, int n) +{ + if (n < 32) { + env->fpr[n] = ldq_p(mem_buf); /* always 64-bit */ + return sizeof(uint64_t); + /* there is hole between ft11 and fflags in fpu.xml */ + } else if (n < 36 && n > 32) { + target_ulong val = ldtul_p(mem_buf); + int result; + /* + * CSR_FFLAGS is at index 8 in csr_register, and gdb says it is FP + * register 33, so we recalculate the map index. + * This also works for CSR_FRM and CSR_FCSR. + */ + result = riscv_csrrw_debug(env, n - 33 + 8, NULL, val, -1); + if (result == 0) { + return sizeof(target_ulong); + } + } + return 0; +} + +static int riscv_gdb_get_csr(CPURISCVState *env, uint8_t *mem_buf, int n) +{ + if (n < ARRAY_SIZE(csr_register_map)) { + target_ulong val = 0; + int result; + + result = riscv_csrrw_debug(env, csr_register_map[n], &val, 0, 0); + if (result == 0) { + return gdb_get_regl(mem_buf, val); + } + } + return 0; +} + +static int riscv_gdb_set_csr(CPURISCVState *env, uint8_t *mem_buf, int n) +{ + if (n < ARRAY_SIZE(csr_register_map)) { + target_ulong val = ldtul_p(mem_buf); + int result; + + result = riscv_csrrw_debug(env, csr_register_map[n], NULL, val, -1); + if (result == 0) { + return sizeof(target_ulong); + } + } + return 0; +} + +void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) +{ + RISCVCPU *cpu = RISCV_CPU(cs); + CPURISCVState *env = &cpu->env; +#if defined(TARGET_RISCV32) + if (env->misa & RVF) { + gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, + 36, "riscv-32bit-fpu.xml", 0); + } + + gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, + 4096, "riscv-32bit-csr.xml", 0); +#elif defined(TARGET_RISCV64) + if (env->misa & RVF) { + gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu, + 36, "riscv-64bit-fpu.xml", 0); + } + + gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr, + 4096, "riscv-64bit-csr.xml", 0); +#endif +} diff --git a/target/riscv/insn16.decode b/target/riscv/insn16.decode new file mode 100644 index 0000000000..17cc52cf2a --- /dev/null +++ b/target/riscv/insn16.decode @@ -0,0 +1,129 @@ +# +# RISC-V translation routines for the RVXI Base Integer Instruction Set. +# +# Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de +# Bastian Koppelmann, kbastian@mail.uni-paderborn.de +# +# 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 . + +# Fields: +%rd 7:5 +%rs1_3 7:3 !function=ex_rvc_register +%rs2_3 2:3 !function=ex_rvc_register +%rs2_5 2:5 + +# Immediates: +%imm_ci 12:s1 2:5 +%nzuimm_ciw 7:4 11:2 5:1 6:1 !function=ex_shift_2 +%uimm_cl_d 5:2 10:3 !function=ex_shift_3 +%uimm_cl_w 5:1 10:3 6:1 !function=ex_shift_2 +%imm_cb 12:s1 5:2 2:1 10:2 3:2 !function=ex_shift_1 +%imm_cj 12:s1 8:1 9:2 6:1 7:1 2:1 11:1 3:3 !function=ex_shift_1 + +%nzuimm_6bit 12:1 2:5 +%uimm_6bit_ld 2:3 12:1 5:2 !function=ex_shift_3 +%uimm_6bit_lw 2:2 12:1 4:3 !function=ex_shift_2 +%uimm_6bit_sd 7:3 10:3 !function=ex_shift_3 +%uimm_6bit_sw 7:2 9:4 !function=ex_shift_2 + +%imm_addi16sp 12:s1 3:2 5:1 2:1 6:1 !function=ex_shift_4 +%imm_lui 12:s1 2:5 !function=ex_shift_12 + + + +# Argument sets: +&cl rs1 rd +&cl_dw uimm rs1 rd +&ci imm rd +&ciw nzuimm rd +&cs rs1 rs2 +&cs_dw uimm rs1 rs2 +&cb imm rs1 +&cr rd rs2 +&cj imm +&c_shift shamt rd + +&c_ld uimm rd +&c_sd uimm rs2 + +&caddi16sp_lui imm_lui imm_addi16sp rd +&cflwsp_ldsp uimm_flwsp uimm_ldsp rd +&cfswsp_sdsp uimm_fswsp uimm_sdsp rs2 + +# Formats 16: +@cr .... ..... ..... .. &cr rs2=%rs2_5 %rd +@ci ... . ..... ..... .. &ci imm=%imm_ci %rd +@ciw ... ........ ... .. &ciw nzuimm=%nzuimm_ciw rd=%rs2_3 +@cl_d ... ... ... .. ... .. &cl_dw uimm=%uimm_cl_d rs1=%rs1_3 rd=%rs2_3 +@cl_w ... ... ... .. ... .. &cl_dw uimm=%uimm_cl_w rs1=%rs1_3 rd=%rs2_3 +@cl ... ... ... .. ... .. &cl rs1=%rs1_3 rd=%rs2_3 +@cs ... ... ... .. ... .. &cs rs1=%rs1_3 rs2=%rs2_3 +@cs_2 ... ... ... .. ... .. &cr rd=%rs1_3 rs2=%rs2_3 +@cs_d ... ... ... .. ... .. &cs_dw uimm=%uimm_cl_d rs1=%rs1_3 rs2=%rs2_3 +@cs_w ... ... ... .. ... .. &cs_dw uimm=%uimm_cl_w rs1=%rs1_3 rs2=%rs2_3 +@cb ... ... ... .. ... .. &cb imm=%imm_cb rs1=%rs1_3 +@cj ... ........... .. &cj imm=%imm_cj + +@c_ld ... . ..... ..... .. &c_ld uimm=%uimm_6bit_ld %rd +@c_lw ... . ..... ..... .. &c_ld uimm=%uimm_6bit_lw %rd +@c_sd ... . ..... ..... .. &c_sd uimm=%uimm_6bit_sd rs2=%rs2_5 +@c_sw ... . ..... ..... .. &c_sd uimm=%uimm_6bit_sw rs2=%rs2_5 + +@c_addi16sp_lui ... . ..... ..... .. &caddi16sp_lui %imm_lui %imm_addi16sp %rd +@c_flwsp_ldsp ... . ..... ..... .. &cflwsp_ldsp uimm_flwsp=%uimm_6bit_lw \ + uimm_ldsp=%uimm_6bit_ld %rd +@c_fswsp_sdsp ... . ..... ..... .. &cfswsp_sdsp uimm_fswsp=%uimm_6bit_sw \ + uimm_sdsp=%uimm_6bit_sd rs2=%rs2_5 + +@c_shift ... . .. ... ..... .. &c_shift rd=%rs1_3 shamt=%nzuimm_6bit +@c_shift2 ... . .. ... ..... .. &c_shift rd=%rd shamt=%nzuimm_6bit + +@c_andi ... . .. ... ..... .. &ci imm=%imm_ci rd=%rs1_3 + +# *** RV64C Standard Extension (Quadrant 0) *** +c_addi4spn 000 ........ ... 00 @ciw +c_fld 001 ... ... .. ... 00 @cl_d +c_lw 010 ... ... .. ... 00 @cl_w +c_flw_ld 011 --- ... -- ... 00 @cl #Note: Must parse uimm manually +c_fsd 101 ... ... .. ... 00 @cs_d +c_sw 110 ... ... .. ... 00 @cs_w +c_fsw_sd 111 --- ... -- ... 00 @cs #Note: Must parse uimm manually + +# *** RV64C Standard Extension (Quadrant 1) *** +c_addi 000 . ..... ..... 01 @ci +c_jal_addiw 001 . ..... ..... 01 @ci #Note: parse rd and/or imm manually +c_li 010 . ..... ..... 01 @ci +c_addi16sp_lui 011 . ..... ..... 01 @c_addi16sp_lui # shares opc with C.LUI +c_srli 100 . 00 ... ..... 01 @c_shift +c_srai 100 . 01 ... ..... 01 @c_shift +c_andi 100 . 10 ... ..... 01 @c_andi +c_sub 100 0 11 ... 00 ... 01 @cs_2 +c_xor 100 0 11 ... 01 ... 01 @cs_2 +c_or 100 0 11 ... 10 ... 01 @cs_2 +c_and 100 0 11 ... 11 ... 01 @cs_2 +c_subw 100 1 11 ... 00 ... 01 @cs_2 +c_addw 100 1 11 ... 01 ... 01 @cs_2 +c_j 101 ........... 01 @cj +c_beqz 110 ... ... ..... 01 @cb +c_bnez 111 ... ... ..... 01 @cb + +# *** RV64C Standard Extension (Quadrant 2) *** +c_slli 000 . ..... ..... 10 @c_shift2 +c_fldsp 001 . ..... ..... 10 @c_ld +c_lwsp 010 . ..... ..... 10 @c_lw +c_flwsp_ldsp 011 . ..... ..... 10 @c_flwsp_ldsp #C.LDSP:RV64;C.FLWSP:RV32 +c_jr_mv 100 0 ..... ..... 10 @cr +c_ebreak_jalr_add 100 1 ..... ..... 10 @cr +c_fsdsp 101 ...... ..... 10 @c_sd +c_swsp 110 . ..... ..... 10 @c_sw +c_fswsp_sdsp 111 . ..... ..... 10 @c_fswsp_sdsp #C.SDSP:RV64;C.FSWSP:RV32 diff --git a/target/riscv/insn32-64.decode b/target/riscv/insn32-64.decode new file mode 100644 index 0000000000..380bf791bc --- /dev/null +++ b/target/riscv/insn32-64.decode @@ -0,0 +1,72 @@ +# +# RISC-V translation routines for the RV Instruction Set. +# +# Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de +# Bastian Koppelmann, kbastian@mail.uni-paderborn.de +# +# 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 . + +# This is concatenated with insn32.decode for risc64 targets. +# Most of the fields and formats are there. + +%sh5 20:5 + +@sh5 ....... ..... ..... ... ..... ....... &shift shamt=%sh5 %rs1 %rd + +# *** RV64I Base Instruction Set (in addition to RV32I) *** +lwu ............ ..... 110 ..... 0000011 @i +ld ............ ..... 011 ..... 0000011 @i +sd ....... ..... ..... 011 ..... 0100011 @s +addiw ............ ..... 000 ..... 0011011 @i +slliw 0000000 ..... ..... 001 ..... 0011011 @sh5 +srliw 0000000 ..... ..... 101 ..... 0011011 @sh5 +sraiw 0100000 ..... ..... 101 ..... 0011011 @sh5 +addw 0000000 ..... ..... 000 ..... 0111011 @r +subw 0100000 ..... ..... 000 ..... 0111011 @r +sllw 0000000 ..... ..... 001 ..... 0111011 @r +srlw 0000000 ..... ..... 101 ..... 0111011 @r +sraw 0100000 ..... ..... 101 ..... 0111011 @r + +# *** RV64M Standard Extension (in addition to RV32M) *** +mulw 0000001 ..... ..... 000 ..... 0111011 @r +divw 0000001 ..... ..... 100 ..... 0111011 @r +divuw 0000001 ..... ..... 101 ..... 0111011 @r +remw 0000001 ..... ..... 110 ..... 0111011 @r +remuw 0000001 ..... ..... 111 ..... 0111011 @r + +# *** RV64A Standard Extension (in addition to RV32A) *** +lr_d 00010 . . 00000 ..... 011 ..... 0101111 @atom_ld +sc_d 00011 . . ..... ..... 011 ..... 0101111 @atom_st +amoswap_d 00001 . . ..... ..... 011 ..... 0101111 @atom_st +amoadd_d 00000 . . ..... ..... 011 ..... 0101111 @atom_st +amoxor_d 00100 . . ..... ..... 011 ..... 0101111 @atom_st +amoand_d 01100 . . ..... ..... 011 ..... 0101111 @atom_st +amoor_d 01000 . . ..... ..... 011 ..... 0101111 @atom_st +amomin_d 10000 . . ..... ..... 011 ..... 0101111 @atom_st +amomax_d 10100 . . ..... ..... 011 ..... 0101111 @atom_st +amominu_d 11000 . . ..... ..... 011 ..... 0101111 @atom_st +amomaxu_d 11100 . . ..... ..... 011 ..... 0101111 @atom_st + +# *** RV64F Standard Extension (in addition to RV32F) *** +fcvt_l_s 1100000 00010 ..... ... ..... 1010011 @r2_rm +fcvt_lu_s 1100000 00011 ..... ... ..... 1010011 @r2_rm +fcvt_s_l 1101000 00010 ..... ... ..... 1010011 @r2_rm +fcvt_s_lu 1101000 00011 ..... ... ..... 1010011 @r2_rm + +# *** RV64D Standard Extension (in addition to RV32D) *** +fcvt_l_d 1100001 00010 ..... ... ..... 1010011 @r2_rm +fcvt_lu_d 1100001 00011 ..... ... ..... 1010011 @r2_rm +fmv_x_d 1110001 00000 ..... 000 ..... 1010011 @r2 +fcvt_d_l 1101001 00010 ..... ... ..... 1010011 @r2_rm +fcvt_d_lu 1101001 00011 ..... ... ..... 1010011 @r2_rm +fmv_d_x 1111001 00000 ..... 000 ..... 1010011 @r2 diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode new file mode 100644 index 0000000000..6f3ab7aa52 --- /dev/null +++ b/target/riscv/insn32.decode @@ -0,0 +1,201 @@ +# +# RISC-V translation routines for the RVXI Base Integer Instruction Set. +# +# Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de +# Bastian Koppelmann, kbastian@mail.uni-paderborn.de +# +# 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 . + +# Fields: +%rs3 27:5 +%rs2 20:5 +%rs1 15:5 +%rd 7:5 + +%sh10 20:10 +%csr 20:12 +%rm 12:3 + +# immediates: +%imm_i 20:s12 +%imm_s 25:s7 7:5 +%imm_b 31:s1 7:1 25:6 8:4 !function=ex_shift_1 +%imm_j 31:s1 12:8 20:1 21:10 !function=ex_shift_1 +%imm_u 12:s20 !function=ex_shift_12 + +# Argument sets: +&b imm rs2 rs1 +&i imm rs1 rd +&r rd rs1 rs2 +&shift shamt rs1 rd +&atomic aq rl rs2 rs1 rd + +# Formats 32: +@r ....... ..... ..... ... ..... ....... &r %rs2 %rs1 %rd +@i ............ ..... ... ..... ....... &i imm=%imm_i %rs1 %rd +@b ....... ..... ..... ... ..... ....... &b imm=%imm_b %rs2 %rs1 +@s ....... ..... ..... ... ..... ....... imm=%imm_s %rs2 %rs1 +@u .................... ..... ....... imm=%imm_u %rd +@j .................... ..... ....... imm=%imm_j %rd + +@sh ...... ...... ..... ... ..... ....... &shift shamt=%sh10 %rs1 %rd +@csr ............ ..... ... ..... ....... %csr %rs1 %rd + +@atom_ld ..... aq:1 rl:1 ..... ........ ..... ....... &atomic rs2=0 %rs1 %rd +@atom_st ..... aq:1 rl:1 ..... ........ ..... ....... &atomic %rs2 %rs1 %rd + +@r4_rm ..... .. ..... ..... ... ..... ....... %rs3 %rs2 %rs1 %rm %rd +@r_rm ....... ..... ..... ... ..... ....... %rs2 %rs1 %rm %rd +@r2_rm ....... ..... ..... ... ..... ....... %rs1 %rm %rd +@r2 ....... ..... ..... ... ..... ....... %rs1 %rd + +@sfence_vma ....... ..... ..... ... ..... ....... %rs2 %rs1 +@sfence_vm ....... ..... ..... ... ..... ....... %rs1 + + +# *** Privileged Instructions *** +ecall 000000000000 00000 000 00000 1110011 +ebreak 000000000001 00000 000 00000 1110011 +uret 0000000 00010 00000 000 00000 1110011 +sret 0001000 00010 00000 000 00000 1110011 +hret 0010000 00010 00000 000 00000 1110011 +mret 0011000 00010 00000 000 00000 1110011 +wfi 0001000 00101 00000 000 00000 1110011 +sfence_vma 0001001 ..... ..... 000 00000 1110011 @sfence_vma +sfence_vm 0001000 00100 ..... 000 00000 1110011 @sfence_vm + +# *** RV32I Base Instruction Set *** +lui .................... ..... 0110111 @u +auipc .................... ..... 0010111 @u +jal .................... ..... 1101111 @j +jalr ............ ..... 000 ..... 1100111 @i +beq ....... ..... ..... 000 ..... 1100011 @b +bne ....... ..... ..... 001 ..... 1100011 @b +blt ....... ..... ..... 100 ..... 1100011 @b +bge ....... ..... ..... 101 ..... 1100011 @b +bltu ....... ..... ..... 110 ..... 1100011 @b +bgeu ....... ..... ..... 111 ..... 1100011 @b +lb ............ ..... 000 ..... 0000011 @i +lh ............ ..... 001 ..... 0000011 @i +lw ............ ..... 010 ..... 0000011 @i +lbu ............ ..... 100 ..... 0000011 @i +lhu ............ ..... 101 ..... 0000011 @i +sb ....... ..... ..... 000 ..... 0100011 @s +sh ....... ..... ..... 001 ..... 0100011 @s +sw ....... ..... ..... 010 ..... 0100011 @s +addi ............ ..... 000 ..... 0010011 @i +slti ............ ..... 010 ..... 0010011 @i +sltiu ............ ..... 011 ..... 0010011 @i +xori ............ ..... 100 ..... 0010011 @i +ori ............ ..... 110 ..... 0010011 @i +andi ............ ..... 111 ..... 0010011 @i +slli 00.... ...... ..... 001 ..... 0010011 @sh +srli 00.... ...... ..... 101 ..... 0010011 @sh +srai 01.... ...... ..... 101 ..... 0010011 @sh +add 0000000 ..... ..... 000 ..... 0110011 @r +sub 0100000 ..... ..... 000 ..... 0110011 @r +sll 0000000 ..... ..... 001 ..... 0110011 @r +slt 0000000 ..... ..... 010 ..... 0110011 @r +sltu 0000000 ..... ..... 011 ..... 0110011 @r +xor 0000000 ..... ..... 100 ..... 0110011 @r +srl 0000000 ..... ..... 101 ..... 0110011 @r +sra 0100000 ..... ..... 101 ..... 0110011 @r +or 0000000 ..... ..... 110 ..... 0110011 @r +and 0000000 ..... ..... 111 ..... 0110011 @r +fence ---- pred:4 succ:4 ----- 000 ----- 0001111 +fence_i ---- ---- ---- ----- 001 ----- 0001111 +csrrw ............ ..... 001 ..... 1110011 @csr +csrrs ............ ..... 010 ..... 1110011 @csr +csrrc ............ ..... 011 ..... 1110011 @csr +csrrwi ............ ..... 101 ..... 1110011 @csr +csrrsi ............ ..... 110 ..... 1110011 @csr +csrrci ............ ..... 111 ..... 1110011 @csr + +# *** RV32M Standard Extension *** +mul 0000001 ..... ..... 000 ..... 0110011 @r +mulh 0000001 ..... ..... 001 ..... 0110011 @r +mulhsu 0000001 ..... ..... 010 ..... 0110011 @r +mulhu 0000001 ..... ..... 011 ..... 0110011 @r +div 0000001 ..... ..... 100 ..... 0110011 @r +divu 0000001 ..... ..... 101 ..... 0110011 @r +rem 0000001 ..... ..... 110 ..... 0110011 @r +remu 0000001 ..... ..... 111 ..... 0110011 @r + +# *** RV32A Standard Extension *** +lr_w 00010 . . 00000 ..... 010 ..... 0101111 @atom_ld +sc_w 00011 . . ..... ..... 010 ..... 0101111 @atom_st +amoswap_w 00001 . . ..... ..... 010 ..... 0101111 @atom_st +amoadd_w 00000 . . ..... ..... 010 ..... 0101111 @atom_st +amoxor_w 00100 . . ..... ..... 010 ..... 0101111 @atom_st +amoand_w 01100 . . ..... ..... 010 ..... 0101111 @atom_st +amoor_w 01000 . . ..... ..... 010 ..... 0101111 @atom_st +amomin_w 10000 . . ..... ..... 010 ..... 0101111 @atom_st +amomax_w 10100 . . ..... ..... 010 ..... 0101111 @atom_st +amominu_w 11000 . . ..... ..... 010 ..... 0101111 @atom_st +amomaxu_w 11100 . . ..... ..... 010 ..... 0101111 @atom_st + +# *** RV32F Standard Extension *** +flw ............ ..... 010 ..... 0000111 @i +fsw ....... ..... ..... 010 ..... 0100111 @s +fmadd_s ..... 00 ..... ..... ... ..... 1000011 @r4_rm +fmsub_s ..... 00 ..... ..... ... ..... 1000111 @r4_rm +fnmsub_s ..... 00 ..... ..... ... ..... 1001011 @r4_rm +fnmadd_s ..... 00 ..... ..... ... ..... 1001111 @r4_rm +fadd_s 0000000 ..... ..... ... ..... 1010011 @r_rm +fsub_s 0000100 ..... ..... ... ..... 1010011 @r_rm +fmul_s 0001000 ..... ..... ... ..... 1010011 @r_rm +fdiv_s 0001100 ..... ..... ... ..... 1010011 @r_rm +fsqrt_s 0101100 00000 ..... ... ..... 1010011 @r2_rm +fsgnj_s 0010000 ..... ..... 000 ..... 1010011 @r +fsgnjn_s 0010000 ..... ..... 001 ..... 1010011 @r +fsgnjx_s 0010000 ..... ..... 010 ..... 1010011 @r +fmin_s 0010100 ..... ..... 000 ..... 1010011 @r +fmax_s 0010100 ..... ..... 001 ..... 1010011 @r +fcvt_w_s 1100000 00000 ..... ... ..... 1010011 @r2_rm +fcvt_wu_s 1100000 00001 ..... ... ..... 1010011 @r2_rm +fmv_x_w 1110000 00000 ..... 000 ..... 1010011 @r2 +feq_s 1010000 ..... ..... 010 ..... 1010011 @r +flt_s 1010000 ..... ..... 001 ..... 1010011 @r +fle_s 1010000 ..... ..... 000 ..... 1010011 @r +fclass_s 1110000 00000 ..... 001 ..... 1010011 @r2 +fcvt_s_w 1101000 00000 ..... ... ..... 1010011 @r2_rm +fcvt_s_wu 1101000 00001 ..... ... ..... 1010011 @r2_rm +fmv_w_x 1111000 00000 ..... 000 ..... 1010011 @r2 + +# *** RV32D Standard Extension *** +fld ............ ..... 011 ..... 0000111 @i +fsd ....... ..... ..... 011 ..... 0100111 @s +fmadd_d ..... 01 ..... ..... ... ..... 1000011 @r4_rm +fmsub_d ..... 01 ..... ..... ... ..... 1000111 @r4_rm +fnmsub_d ..... 01 ..... ..... ... ..... 1001011 @r4_rm +fnmadd_d ..... 01 ..... ..... ... ..... 1001111 @r4_rm +fadd_d 0000001 ..... ..... ... ..... 1010011 @r_rm +fsub_d 0000101 ..... ..... ... ..... 1010011 @r_rm +fmul_d 0001001 ..... ..... ... ..... 1010011 @r_rm +fdiv_d 0001101 ..... ..... ... ..... 1010011 @r_rm +fsqrt_d 0101101 00000 ..... ... ..... 1010011 @r2_rm +fsgnj_d 0010001 ..... ..... 000 ..... 1010011 @r +fsgnjn_d 0010001 ..... ..... 001 ..... 1010011 @r +fsgnjx_d 0010001 ..... ..... 010 ..... 1010011 @r +fmin_d 0010101 ..... ..... 000 ..... 1010011 @r +fmax_d 0010101 ..... ..... 001 ..... 1010011 @r +fcvt_s_d 0100000 00001 ..... ... ..... 1010011 @r2_rm +fcvt_d_s 0100001 00000 ..... ... ..... 1010011 @r2_rm +feq_d 1010001 ..... ..... 010 ..... 1010011 @r +flt_d 1010001 ..... ..... 001 ..... 1010011 @r +fle_d 1010001 ..... ..... 000 ..... 1010011 @r +fclass_d 1110001 00000 ..... 001 ..... 1010011 @r2 +fcvt_w_d 1100001 00000 ..... ... ..... 1010011 @r2_rm +fcvt_wu_d 1100001 00001 ..... ... ..... 1010011 @r2_rm +fcvt_d_w 1101001 00000 ..... ... ..... 1010011 @r2_rm +fcvt_d_wu 1101001 00001 ..... ... ..... 1010011 @r2_rm diff --git a/target/riscv/insn_trans/trans_privileged.inc.c b/target/riscv/insn_trans/trans_privileged.inc.c new file mode 100644 index 0000000000..acb605923e --- /dev/null +++ b/target/riscv/insn_trans/trans_privileged.inc.c @@ -0,0 +1,110 @@ +/* + * RISC-V translation routines for the RISC-V privileged instructions. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + +static bool trans_ecall(DisasContext *ctx, arg_ecall *a) +{ + /* always generates U-level ECALL, fixed in do_interrupt handler */ + generate_exception(ctx, RISCV_EXCP_U_ECALL); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +static bool trans_ebreak(DisasContext *ctx, arg_ebreak *a) +{ + generate_exception(ctx, RISCV_EXCP_BREAKPOINT); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +static bool trans_uret(DisasContext *ctx, arg_uret *a) +{ + return false; +} + +static bool trans_sret(DisasContext *ctx, arg_sret *a) +{ +#ifndef CONFIG_USER_ONLY + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + + if (has_ext(ctx, RVS)) { + gen_helper_sret(cpu_pc, cpu_env, cpu_pc); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; + } else { + return false; + } + return true; +#else + return false; +#endif +} + +static bool trans_hret(DisasContext *ctx, arg_hret *a) +{ + return false; +} + +static bool trans_mret(DisasContext *ctx, arg_mret *a) +{ +#ifndef CONFIG_USER_ONLY + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); + gen_helper_mret(cpu_pc, cpu_env, cpu_pc); + tcg_gen_exit_tb(NULL, 0); /* no chaining */ + ctx->base.is_jmp = DISAS_NORETURN; + return true; +#else + return false; +#endif +} + +static bool trans_wfi(DisasContext *ctx, arg_wfi *a) +{ +#ifndef CONFIG_USER_ONLY + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); + gen_helper_wfi(cpu_env); + return true; +#else + return false; +#endif +} + +static bool trans_sfence_vma(DisasContext *ctx, arg_sfence_vma *a) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->priv_ver == PRIV_VERSION_1_10_0) { + gen_helper_tlb_flush(cpu_env); + return true; + } +#endif + return false; +} + +static bool trans_sfence_vm(DisasContext *ctx, arg_sfence_vm *a) +{ +#ifndef CONFIG_USER_ONLY + if (ctx->priv_ver <= PRIV_VERSION_1_09_1) { + gen_helper_tlb_flush(cpu_env); + return true; + } +#endif + return false; +} diff --git a/target/riscv/insn_trans/trans_rva.inc.c b/target/riscv/insn_trans/trans_rva.inc.c new file mode 100644 index 0000000000..f6dbbc065e --- /dev/null +++ b/target/riscv/insn_trans/trans_rva.inc.c @@ -0,0 +1,218 @@ +/* + * RISC-V translation routines for the RV64A Standard Extension. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + +static inline bool gen_lr(DisasContext *ctx, arg_atomic *a, TCGMemOp mop) +{ + TCGv src1 = tcg_temp_new(); + /* Put addr in load_res, data in load_val. */ + gen_get_gpr(src1, a->rs1); + if (a->rl) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); + } + tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop); + if (a->aq) { + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); + } + tcg_gen_mov_tl(load_res, src1); + gen_set_gpr(a->rd, load_val); + + tcg_temp_free(src1); + return true; +} + +static inline bool gen_sc(DisasContext *ctx, arg_atomic *a, TCGMemOp mop) +{ + TCGv src1 = tcg_temp_new(); + TCGv src2 = tcg_temp_new(); + TCGv dat = tcg_temp_new(); + TCGLabel *l1 = gen_new_label(); + TCGLabel *l2 = gen_new_label(); + + gen_get_gpr(src1, a->rs1); + tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); + + gen_get_gpr(src2, a->rs2); + /* + * Note that the TCG atomic primitives are SC, + * so we can ignore AQ/RL along this path. + */ + tcg_gen_atomic_cmpxchg_tl(src1, load_res, load_val, src2, + ctx->mem_idx, mop); + tcg_gen_setcond_tl(TCG_COND_NE, dat, src1, load_val); + gen_set_gpr(a->rd, dat); + tcg_gen_br(l2); + + gen_set_label(l1); + /* + * Address comparion failure. However, we still need to + * provide the memory barrier implied by AQ/RL. + */ + tcg_gen_mb(TCG_MO_ALL + a->aq * TCG_BAR_LDAQ + a->rl * TCG_BAR_STRL); + tcg_gen_movi_tl(dat, 1); + gen_set_gpr(a->rd, dat); + + gen_set_label(l2); + tcg_temp_free(dat); + tcg_temp_free(src1); + tcg_temp_free(src2); + return true; +} + +static bool gen_amo(DisasContext *ctx, arg_atomic *a, + void(*func)(TCGv, TCGv, TCGv, TCGArg, TCGMemOp), + TCGMemOp mop) +{ + TCGv src1 = tcg_temp_new(); + TCGv src2 = tcg_temp_new(); + + gen_get_gpr(src1, a->rs1); + gen_get_gpr(src2, a->rs2); + + (*func)(src2, src1, src2, ctx->mem_idx, mop); + + gen_set_gpr(a->rd, src2); + tcg_temp_free(src1); + tcg_temp_free(src2); + return true; +} + +static bool trans_lr_w(DisasContext *ctx, arg_lr_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_lr(ctx, a, (MO_ALIGN | MO_TESL)); +} + +static bool trans_sc_w(DisasContext *ctx, arg_sc_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_sc(ctx, a, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amoswap_w(DisasContext *ctx, arg_amoswap_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amoadd_w(DisasContext *ctx, arg_amoadd_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amoxor_w(DisasContext *ctx, arg_amoxor_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amoand_w(DisasContext *ctx, arg_amoand_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amoor_w(DisasContext *ctx, arg_amoor_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amomin_w(DisasContext *ctx, arg_amomin_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amomax_w(DisasContext *ctx, arg_amomax_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amominu_w(DisasContext *ctx, arg_amominu_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TESL)); +} + +static bool trans_amomaxu_w(DisasContext *ctx, arg_amomaxu_w *a) +{ + REQUIRE_EXT(ctx, RVA); + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TESL)); +} + +#ifdef TARGET_RISCV64 + +static bool trans_lr_d(DisasContext *ctx, arg_lr_d *a) +{ + return gen_lr(ctx, a, MO_ALIGN | MO_TEQ); +} + +static bool trans_sc_d(DisasContext *ctx, arg_sc_d *a) +{ + return gen_sc(ctx, a, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amoswap_d(DisasContext *ctx, arg_amoswap_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_xchg_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amoadd_d(DisasContext *ctx, arg_amoadd_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_add_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amoxor_d(DisasContext *ctx, arg_amoxor_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_xor_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amoand_d(DisasContext *ctx, arg_amoand_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_and_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amoor_d(DisasContext *ctx, arg_amoor_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_or_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amomin_d(DisasContext *ctx, arg_amomin_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smin_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amomax_d(DisasContext *ctx, arg_amomax_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_smax_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amominu_d(DisasContext *ctx, arg_amominu_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umin_tl, (MO_ALIGN | MO_TEQ)); +} + +static bool trans_amomaxu_d(DisasContext *ctx, arg_amomaxu_d *a) +{ + return gen_amo(ctx, a, &tcg_gen_atomic_fetch_umax_tl, (MO_ALIGN | MO_TEQ)); +} +#endif diff --git a/target/riscv/insn_trans/trans_rvc.inc.c b/target/riscv/insn_trans/trans_rvc.inc.c new file mode 100644 index 0000000000..ebcd977b2f --- /dev/null +++ b/target/riscv/insn_trans/trans_rvc.inc.c @@ -0,0 +1,347 @@ +/* + * RISC-V translation routines for the RVC Compressed Instruction Set. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + +static bool trans_c_addi4spn(DisasContext *ctx, arg_c_addi4spn *a) +{ + if (a->nzuimm == 0) { + /* Reserved in ISA */ + return false; + } + arg_addi arg = { .rd = a->rd, .rs1 = 2, .imm = a->nzuimm }; + return trans_addi(ctx, &arg); +} + +static bool trans_c_fld(DisasContext *ctx, arg_c_fld *a) +{ + arg_fld arg = { .rd = a->rd, .rs1 = a->rs1, .imm = a->uimm }; + return trans_fld(ctx, &arg); +} + +static bool trans_c_lw(DisasContext *ctx, arg_c_lw *a) +{ + arg_lw arg = { .rd = a->rd, .rs1 = a->rs1, .imm = a->uimm }; + return trans_lw(ctx, &arg); +} + +static bool trans_c_flw_ld(DisasContext *ctx, arg_c_flw_ld *a) +{ +#ifdef TARGET_RISCV32 + /* C.FLW ( RV32FC-only ) */ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + arg_c_lw tmp; + decode_insn16_extract_cl_w(&tmp, ctx->opcode); + arg_flw arg = { .rd = tmp.rd, .rs1 = tmp.rs1, .imm = tmp.uimm }; + return trans_flw(ctx, &arg); +#else + /* C.LD ( RV64C/RV128C-only ) */ + arg_c_fld tmp; + decode_insn16_extract_cl_d(&tmp, ctx->opcode); + arg_ld arg = { .rd = tmp.rd, .rs1 = tmp.rs1, .imm = tmp.uimm }; + return trans_ld(ctx, &arg); +#endif +} + +static bool trans_c_fsd(DisasContext *ctx, arg_c_fsd *a) +{ + arg_fsd arg = { .rs1 = a->rs1, .rs2 = a->rs2, .imm = a->uimm }; + return trans_fsd(ctx, &arg); +} + +static bool trans_c_sw(DisasContext *ctx, arg_c_sw *a) +{ + arg_sw arg = { .rs1 = a->rs1, .rs2 = a->rs2, .imm = a->uimm }; + return trans_sw(ctx, &arg); +} + +static bool trans_c_fsw_sd(DisasContext *ctx, arg_c_fsw_sd *a) +{ +#ifdef TARGET_RISCV32 + /* C.FSW ( RV32FC-only ) */ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + arg_c_sw tmp; + decode_insn16_extract_cs_w(&tmp, ctx->opcode); + arg_fsw arg = { .rs1 = tmp.rs1, .rs2 = tmp.rs2, .imm = tmp.uimm }; + return trans_fsw(ctx, &arg); +#else + /* C.SD ( RV64C/RV128C-only ) */ + arg_c_fsd tmp; + decode_insn16_extract_cs_d(&tmp, ctx->opcode); + arg_sd arg = { .rs1 = tmp.rs1, .rs2 = tmp.rs2, .imm = tmp.uimm }; + return trans_sd(ctx, &arg); +#endif +} + +static bool trans_c_addi(DisasContext *ctx, arg_c_addi *a) +{ + if (a->imm == 0) { + /* Hint: insn is valid but does not affect state */ + return true; + } + arg_addi arg = { .rd = a->rd, .rs1 = a->rd, .imm = a->imm }; + return trans_addi(ctx, &arg); +} + +static bool trans_c_jal_addiw(DisasContext *ctx, arg_c_jal_addiw *a) +{ +#ifdef TARGET_RISCV32 + /* C.JAL */ + arg_c_j tmp; + decode_insn16_extract_cj(&tmp, ctx->opcode); + arg_jal arg = { .rd = 1, .imm = tmp.imm }; + return trans_jal(ctx, &arg); +#else + /* C.ADDIW */ + arg_addiw arg = { .rd = a->rd, .rs1 = a->rd, .imm = a->imm }; + return trans_addiw(ctx, &arg); +#endif +} + +static bool trans_c_li(DisasContext *ctx, arg_c_li *a) +{ + if (a->rd == 0) { + /* Hint: insn is valid but does not affect state */ + return true; + } + arg_addi arg = { .rd = a->rd, .rs1 = 0, .imm = a->imm }; + return trans_addi(ctx, &arg); +} + +static bool trans_c_addi16sp_lui(DisasContext *ctx, arg_c_addi16sp_lui *a) +{ + if (a->rd == 2) { + /* C.ADDI16SP */ + arg_addi arg = { .rd = 2, .rs1 = 2, .imm = a->imm_addi16sp }; + return trans_addi(ctx, &arg); + } else if (a->imm_lui != 0) { + /* C.LUI */ + if (a->rd == 0) { + /* Hint: insn is valid but does not affect state */ + return true; + } + arg_lui arg = { .rd = a->rd, .imm = a->imm_lui }; + return trans_lui(ctx, &arg); + } + return false; +} + +static bool trans_c_srli(DisasContext *ctx, arg_c_srli *a) +{ + int shamt = a->shamt; + if (shamt == 0) { + /* For RV128 a shamt of 0 means a shift by 64 */ + shamt = 64; + } + /* Ensure, that shamt[5] is zero for RV32 */ + if (shamt >= TARGET_LONG_BITS) { + return false; + } + + arg_srli arg = { .rd = a->rd, .rs1 = a->rd, .shamt = a->shamt }; + return trans_srli(ctx, &arg); +} + +static bool trans_c_srai(DisasContext *ctx, arg_c_srai *a) +{ + int shamt = a->shamt; + if (shamt == 0) { + /* For RV128 a shamt of 0 means a shift by 64 */ + shamt = 64; + } + /* Ensure, that shamt[5] is zero for RV32 */ + if (shamt >= TARGET_LONG_BITS) { + return false; + } + + arg_srai arg = { .rd = a->rd, .rs1 = a->rd, .shamt = a->shamt }; + return trans_srai(ctx, &arg); +} + +static bool trans_c_andi(DisasContext *ctx, arg_c_andi *a) +{ + arg_andi arg = { .rd = a->rd, .rs1 = a->rd, .imm = a->imm }; + return trans_andi(ctx, &arg); +} + +static bool trans_c_sub(DisasContext *ctx, arg_c_sub *a) +{ + arg_sub arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_sub(ctx, &arg); +} + +static bool trans_c_xor(DisasContext *ctx, arg_c_xor *a) +{ + arg_xor arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_xor(ctx, &arg); +} + +static bool trans_c_or(DisasContext *ctx, arg_c_or *a) +{ + arg_or arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_or(ctx, &arg); +} + +static bool trans_c_and(DisasContext *ctx, arg_c_and *a) +{ + arg_and arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_and(ctx, &arg); +} + +static bool trans_c_subw(DisasContext *ctx, arg_c_subw *a) +{ +#ifdef TARGET_RISCV64 + arg_subw arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_subw(ctx, &arg); +#else + return false; +#endif +} + +static bool trans_c_addw(DisasContext *ctx, arg_c_addw *a) +{ +#ifdef TARGET_RISCV64 + arg_addw arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_addw(ctx, &arg); +#else + return false; +#endif +} + +static bool trans_c_j(DisasContext *ctx, arg_c_j *a) +{ + arg_jal arg = { .rd = 0, .imm = a->imm }; + return trans_jal(ctx, &arg); +} + +static bool trans_c_beqz(DisasContext *ctx, arg_c_beqz *a) +{ + arg_beq arg = { .rs1 = a->rs1, .rs2 = 0, .imm = a->imm }; + return trans_beq(ctx, &arg); +} + +static bool trans_c_bnez(DisasContext *ctx, arg_c_bnez *a) +{ + arg_bne arg = { .rs1 = a->rs1, .rs2 = 0, .imm = a->imm }; + return trans_bne(ctx, &arg); +} + +static bool trans_c_slli(DisasContext *ctx, arg_c_slli *a) +{ + int shamt = a->shamt; + if (shamt == 0) { + /* For RV128 a shamt of 0 means a shift by 64 */ + shamt = 64; + } + /* Ensure, that shamt[5] is zero for RV32 */ + if (shamt >= TARGET_LONG_BITS) { + return false; + } + + arg_slli arg = { .rd = a->rd, .rs1 = a->rd, .shamt = a->shamt }; + return trans_slli(ctx, &arg); +} + +static bool trans_c_fldsp(DisasContext *ctx, arg_c_fldsp *a) +{ + arg_fld arg = { .rd = a->rd, .rs1 = 2, .imm = a->uimm }; + return trans_fld(ctx, &arg); +} + +static bool trans_c_lwsp(DisasContext *ctx, arg_c_lwsp *a) +{ + arg_lw arg = { .rd = a->rd, .rs1 = 2, .imm = a->uimm }; + return trans_lw(ctx, &arg); +} + +static bool trans_c_flwsp_ldsp(DisasContext *ctx, arg_c_flwsp_ldsp *a) +{ +#ifdef TARGET_RISCV32 + /* C.FLWSP */ + arg_flw arg_flw = { .rd = a->rd, .rs1 = 2, .imm = a->uimm_flwsp }; + return trans_flw(ctx, &arg_flw); +#else + /* C.LDSP */ + arg_ld arg_ld = { .rd = a->rd, .rs1 = 2, .imm = a->uimm_ldsp }; + return trans_ld(ctx, &arg_ld); +#endif + return false; +} + +static bool trans_c_jr_mv(DisasContext *ctx, arg_c_jr_mv *a) +{ + if (a->rd != 0 && a->rs2 == 0) { + /* C.JR */ + arg_jalr arg = { .rd = 0, .rs1 = a->rd, .imm = 0 }; + return trans_jalr(ctx, &arg); + } else if (a->rd != 0 && a->rs2 != 0) { + /* C.MV */ + arg_add arg = { .rd = a->rd, .rs1 = 0, .rs2 = a->rs2 }; + return trans_add(ctx, &arg); + } + return false; +} + +static bool trans_c_ebreak_jalr_add(DisasContext *ctx, arg_c_ebreak_jalr_add *a) +{ + if (a->rd == 0 && a->rs2 == 0) { + /* C.EBREAK */ + arg_ebreak arg = { }; + return trans_ebreak(ctx, &arg); + } else if (a->rd != 0) { + if (a->rs2 == 0) { + /* C.JALR */ + arg_jalr arg = { .rd = 1, .rs1 = a->rd, .imm = 0 }; + return trans_jalr(ctx, &arg); + } else { + /* C.ADD */ + arg_add arg = { .rd = a->rd, .rs1 = a->rd, .rs2 = a->rs2 }; + return trans_add(ctx, &arg); + } + } + return false; +} + +static bool trans_c_fsdsp(DisasContext *ctx, arg_c_fsdsp *a) +{ + arg_fsd arg = { .rs1 = 2, .rs2 = a->rs2, .imm = a->uimm }; + return trans_fsd(ctx, &arg); +} + +static bool trans_c_swsp(DisasContext *ctx, arg_c_swsp *a) +{ + arg_sw arg = { .rs1 = 2, .rs2 = a->rs2, .imm = a->uimm }; + return trans_sw(ctx, &arg); +} + +static bool trans_c_fswsp_sdsp(DisasContext *ctx, arg_c_fswsp_sdsp *a) +{ +#ifdef TARGET_RISCV32 + /* C.FSWSP */ + arg_fsw a_fsw = { .rs1 = 2, .rs2 = a->rs2, .imm = a->uimm_fswsp }; + return trans_fsw(ctx, &a_fsw); +#else + /* C.SDSP */ + arg_sd a_sd = { .rs1 = 2, .rs2 = a->rs2, .imm = a->uimm_sdsp }; + return trans_sd(ctx, &a_sd); +#endif +} diff --git a/target/riscv/insn_trans/trans_rvd.inc.c b/target/riscv/insn_trans/trans_rvd.inc.c new file mode 100644 index 0000000000..393fa0248c --- /dev/null +++ b/target/riscv/insn_trans/trans_rvd.inc.c @@ -0,0 +1,442 @@ +/* + * RISC-V translation routines for the RV64D Standard Extension. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + +static bool trans_fld(DisasContext *ctx, arg_fld *a) +{ + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + tcg_gen_addi_tl(t0, t0, a->imm); + + tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEQ); + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + return true; +} + +static bool trans_fsd(DisasContext *ctx, arg_fsd *a) +{ + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + tcg_gen_addi_tl(t0, t0, a->imm); + + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEQ); + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + return true; +} + +static bool trans_fmadd_d(DisasContext *ctx, arg_fmadd_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + gen_set_rm(ctx, a->rm); + gen_helper_fmadd_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmsub_d(DisasContext *ctx, arg_fmsub_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + gen_set_rm(ctx, a->rm); + gen_helper_fmsub_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fnmsub_d(DisasContext *ctx, arg_fnmsub_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + gen_set_rm(ctx, a->rm); + gen_helper_fnmsub_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fnmadd_d(DisasContext *ctx, arg_fnmadd_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + gen_set_rm(ctx, a->rm); + gen_helper_fnmadd_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fadd_d(DisasContext *ctx, arg_fadd_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fadd_d(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsub_d(DisasContext *ctx, arg_fsub_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fsub_d(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmul_d(DisasContext *ctx, arg_fmul_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fmul_d(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fdiv_d(DisasContext *ctx, arg_fdiv_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fdiv_d(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsqrt_d(DisasContext *ctx, arg_fsqrt_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fsqrt_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsgnj_d(DisasContext *ctx, arg_fsgnj_d *a) +{ + if (a->rs1 == a->rs2) { /* FMOV */ + tcg_gen_mov_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1]); + } else { + tcg_gen_deposit_i64(cpu_fpr[a->rd], cpu_fpr[a->rs2], + cpu_fpr[a->rs1], 0, 63); + } + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsgnjn_d(DisasContext *ctx, arg_fsgnjn_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + if (a->rs1 == a->rs2) { /* FNEG */ + tcg_gen_xori_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], INT64_MIN); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, cpu_fpr[a->rs2]); + tcg_gen_deposit_i64(cpu_fpr[a->rd], t0, cpu_fpr[a->rs1], 0, 63); + tcg_temp_free_i64(t0); + } + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsgnjx_d(DisasContext *ctx, arg_fsgnjx_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + if (a->rs1 == a->rs2) { /* FABS */ + tcg_gen_andi_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], ~INT64_MIN); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(t0, cpu_fpr[a->rs2], INT64_MIN); + tcg_gen_xor_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], t0); + tcg_temp_free_i64(t0); + } + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmin_d(DisasContext *ctx, arg_fmin_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_helper_fmin_d(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmax_d(DisasContext *ctx, arg_fmax_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_helper_fmax_d(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_s_d(DisasContext *ctx, arg_fcvt_s_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_d(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_d_s(DisasContext *ctx, arg_fcvt_d_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_d_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_feq_d(DisasContext *ctx, arg_feq_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_helper_feq_d(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_flt_d(DisasContext *ctx, arg_flt_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_helper_flt_d(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fle_d(DisasContext *ctx, arg_fle_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_helper_fle_d(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fclass_d(DisasContext *ctx, arg_fclass_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_helper_fclass_d(t0, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fcvt_w_d(DisasContext *ctx, arg_fcvt_w_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fcvt_wu_d(DisasContext *ctx, arg_fcvt_wu_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fcvt_d_w(DisasContext *ctx, arg_fcvt_d_w *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_d_w(cpu_fpr[a->rd], cpu_env, t0); + tcg_temp_free(t0); + + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_d_wu(DisasContext *ctx, arg_fcvt_d_wu *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_d_wu(cpu_fpr[a->rd], cpu_env, t0); + tcg_temp_free(t0); + + mark_fs_dirty(ctx); + return true; +} + +#ifdef TARGET_RISCV64 + +static bool trans_fcvt_l_d(DisasContext *ctx, arg_fcvt_l_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fcvt_lu_d(DisasContext *ctx, arg_fcvt_lu_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fmv_x_d(DisasContext *ctx, arg_fmv_x_d *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + gen_set_gpr(a->rd, cpu_fpr[a->rs1]); + return true; +} + +static bool trans_fcvt_d_l(DisasContext *ctx, arg_fcvt_d_l *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_d_l(cpu_fpr[a->rd], cpu_env, t0); + tcg_temp_free(t0); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_d_lu(DisasContext *ctx, arg_fcvt_d_lu *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_d_lu(cpu_fpr[a->rd], cpu_env, t0); + tcg_temp_free(t0); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmv_d_x(DisasContext *ctx, arg_fmv_d_x *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVD); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + tcg_gen_mov_tl(cpu_fpr[a->rd], t0); + tcg_temp_free(t0); + mark_fs_dirty(ctx); + return true; +} +#endif diff --git a/target/riscv/insn_trans/trans_rvf.inc.c b/target/riscv/insn_trans/trans_rvf.inc.c new file mode 100644 index 0000000000..172dbfa919 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvf.inc.c @@ -0,0 +1,439 @@ +/* + * RISC-V translation routines for the RV64F Standard Extension. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + +#define REQUIRE_FPU do {\ + if (ctx->mstatus_fs == 0) \ + return false; \ +} while (0) + +static bool trans_flw(DisasContext *ctx, arg_flw *a) +{ + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + tcg_gen_addi_tl(t0, t0, a->imm); + + tcg_gen_qemu_ld_i64(cpu_fpr[a->rd], t0, ctx->mem_idx, MO_TEUL); + /* RISC-V requires NaN-boxing of narrower width floating point values */ + tcg_gen_ori_i64(cpu_fpr[a->rd], cpu_fpr[a->rd], 0xffffffff00000000ULL); + + tcg_temp_free(t0); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsw(DisasContext *ctx, arg_fsw *a) +{ + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + tcg_gen_addi_tl(t0, t0, a->imm); + + tcg_gen_qemu_st_i64(cpu_fpr[a->rs2], t0, ctx->mem_idx, MO_TEUL); + + tcg_temp_free(t0); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmadd_s(DisasContext *ctx, arg_fmadd_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + gen_set_rm(ctx, a->rm); + gen_helper_fmadd_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmsub_s(DisasContext *ctx, arg_fmsub_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + gen_set_rm(ctx, a->rm); + gen_helper_fmsub_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fnmsub_s(DisasContext *ctx, arg_fnmsub_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + gen_set_rm(ctx, a->rm); + gen_helper_fnmsub_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fnmadd_s(DisasContext *ctx, arg_fnmadd_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + gen_set_rm(ctx, a->rm); + gen_helper_fnmadd_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2], cpu_fpr[a->rs3]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fadd_s(DisasContext *ctx, arg_fadd_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_set_rm(ctx, a->rm); + gen_helper_fadd_s(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsub_s(DisasContext *ctx, arg_fsub_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_set_rm(ctx, a->rm); + gen_helper_fsub_s(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmul_s(DisasContext *ctx, arg_fmul_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_set_rm(ctx, a->rm); + gen_helper_fmul_s(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fdiv_s(DisasContext *ctx, arg_fdiv_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_set_rm(ctx, a->rm); + gen_helper_fdiv_s(cpu_fpr[a->rd], cpu_env, + cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsqrt_s(DisasContext *ctx, arg_fsqrt_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_set_rm(ctx, a->rm); + gen_helper_fsqrt_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsgnj_s(DisasContext *ctx, arg_fsgnj_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + if (a->rs1 == a->rs2) { /* FMOV */ + tcg_gen_mov_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1]); + } else { /* FSGNJ */ + tcg_gen_deposit_i64(cpu_fpr[a->rd], cpu_fpr[a->rs2], cpu_fpr[a->rs1], + 0, 31); + } + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsgnjn_s(DisasContext *ctx, arg_fsgnjn_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + if (a->rs1 == a->rs2) { /* FNEG */ + tcg_gen_xori_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], INT32_MIN); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_not_i64(t0, cpu_fpr[a->rs2]); + tcg_gen_deposit_i64(cpu_fpr[a->rd], t0, cpu_fpr[a->rs1], 0, 31); + tcg_temp_free_i64(t0); + } + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fsgnjx_s(DisasContext *ctx, arg_fsgnjx_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + if (a->rs1 == a->rs2) { /* FABS */ + tcg_gen_andi_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], ~INT32_MIN); + } else { + TCGv_i64 t0 = tcg_temp_new_i64(); + tcg_gen_andi_i64(t0, cpu_fpr[a->rs2], INT32_MIN); + tcg_gen_xor_i64(cpu_fpr[a->rd], cpu_fpr[a->rs1], t0); + tcg_temp_free_i64(t0); + } + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmin_s(DisasContext *ctx, arg_fmin_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_helper_fmin_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fmax_s(DisasContext *ctx, arg_fmax_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + gen_helper_fmax_s(cpu_fpr[a->rd], cpu_env, cpu_fpr[a->rs1], + cpu_fpr[a->rs2]); + mark_fs_dirty(ctx); + return true; +} + +static bool trans_fcvt_w_s(DisasContext *ctx, arg_fcvt_w_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_w_s(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fcvt_wu_s(DisasContext *ctx, arg_fcvt_wu_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_wu_s(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fmv_x_w(DisasContext *ctx, arg_fmv_x_w *a) +{ + /* NOTE: This was FMV.X.S in an earlier version of the ISA spec! */ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + +#if defined(TARGET_RISCV64) + tcg_gen_ext32s_tl(t0, cpu_fpr[a->rs1]); +#else + tcg_gen_extrl_i64_i32(t0, cpu_fpr[a->rs1]); +#endif + + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_feq_s(DisasContext *ctx, arg_feq_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + TCGv t0 = tcg_temp_new(); + gen_helper_feq_s(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_flt_s(DisasContext *ctx, arg_flt_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + TCGv t0 = tcg_temp_new(); + gen_helper_flt_s(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fle_s(DisasContext *ctx, arg_fle_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + TCGv t0 = tcg_temp_new(); + gen_helper_fle_s(t0, cpu_env, cpu_fpr[a->rs1], cpu_fpr[a->rs2]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fclass_s(DisasContext *ctx, arg_fclass_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + + gen_helper_fclass_s(t0, cpu_fpr[a->rs1]); + + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fcvt_s_w(DisasContext *ctx, arg_fcvt_s_w *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_w(cpu_fpr[a->rd], cpu_env, t0); + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fcvt_s_wu(DisasContext *ctx, arg_fcvt_s_wu *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_wu(cpu_fpr[a->rd], cpu_env, t0); + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + + return true; +} + +static bool trans_fmv_w_x(DisasContext *ctx, arg_fmv_w_x *a) +{ + /* NOTE: This was FMV.S.X in an earlier version of the ISA spec! */ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + +#if defined(TARGET_RISCV64) + tcg_gen_mov_i64(cpu_fpr[a->rd], t0); +#else + tcg_gen_extu_i32_i64(cpu_fpr[a->rd], t0); +#endif + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + + return true; +} + +#ifdef TARGET_RISCV64 +static bool trans_fcvt_l_s(DisasContext *ctx, arg_fcvt_l_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_l_s(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fcvt_lu_s(DisasContext *ctx, arg_fcvt_lu_s *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_lu_s(t0, cpu_env, cpu_fpr[a->rs1]); + gen_set_gpr(a->rd, t0); + tcg_temp_free(t0); + return true; +} + +static bool trans_fcvt_s_l(DisasContext *ctx, arg_fcvt_s_l *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_l(cpu_fpr[a->rd], cpu_env, t0); + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + return true; +} + +static bool trans_fcvt_s_lu(DisasContext *ctx, arg_fcvt_s_lu *a) +{ + REQUIRE_FPU; + REQUIRE_EXT(ctx, RVF); + + TCGv t0 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + + gen_set_rm(ctx, a->rm); + gen_helper_fcvt_s_lu(cpu_fpr[a->rd], cpu_env, t0); + + mark_fs_dirty(ctx); + tcg_temp_free(t0); + return true; +} +#endif diff --git a/target/riscv/insn_trans/trans_rvi.inc.c b/target/riscv/insn_trans/trans_rvi.inc.c new file mode 100644 index 0000000000..d420a4d8b2 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvi.inc.c @@ -0,0 +1,568 @@ +/* + * RISC-V translation routines for the RVXI Base Integer Instruction Set. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + +static bool trans_lui(DisasContext *ctx, arg_lui *a) +{ + if (a->rd != 0) { + tcg_gen_movi_tl(cpu_gpr[a->rd], a->imm); + } + return true; +} + +static bool trans_auipc(DisasContext *ctx, arg_auipc *a) +{ + if (a->rd != 0) { + tcg_gen_movi_tl(cpu_gpr[a->rd], a->imm + ctx->base.pc_next); + } + return true; +} + +static bool trans_jal(DisasContext *ctx, arg_jal *a) +{ + gen_jal(ctx, a->rd, a->imm); + return true; +} + +static bool trans_jalr(DisasContext *ctx, arg_jalr *a) +{ + /* no chaining with JALR */ + TCGLabel *misaligned = NULL; + TCGv t0 = tcg_temp_new(); + + + gen_get_gpr(cpu_pc, a->rs1); + tcg_gen_addi_tl(cpu_pc, cpu_pc, a->imm); + tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); + + if (!has_ext(ctx, RVC)) { + misaligned = gen_new_label(); + tcg_gen_andi_tl(t0, cpu_pc, 0x2); + tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); + } + + if (a->rd != 0) { + tcg_gen_movi_tl(cpu_gpr[a->rd], ctx->pc_succ_insn); + } + tcg_gen_lookup_and_goto_ptr(); + + if (misaligned) { + gen_set_label(misaligned); + gen_exception_inst_addr_mis(ctx); + } + ctx->base.is_jmp = DISAS_NORETURN; + + tcg_temp_free(t0); + return true; +} + +static bool gen_branch(DisasContext *ctx, arg_b *a, TCGCond cond) +{ + TCGLabel *l = gen_new_label(); + TCGv source1, source2; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + tcg_gen_brcond_tl(cond, source1, source2, l); + gen_goto_tb(ctx, 1, ctx->pc_succ_insn); + gen_set_label(l); /* branch taken */ + + if (!has_ext(ctx, RVC) && ((ctx->base.pc_next + a->imm) & 0x3)) { + /* misaligned */ + gen_exception_inst_addr_mis(ctx); + } else { + gen_goto_tb(ctx, 0, ctx->base.pc_next + a->imm); + } + ctx->base.is_jmp = DISAS_NORETURN; + + tcg_temp_free(source1); + tcg_temp_free(source2); + + return true; +} + +static bool trans_beq(DisasContext *ctx, arg_beq *a) +{ + return gen_branch(ctx, a, TCG_COND_EQ); +} + +static bool trans_bne(DisasContext *ctx, arg_bne *a) +{ + return gen_branch(ctx, a, TCG_COND_NE); +} + +static bool trans_blt(DisasContext *ctx, arg_blt *a) +{ + return gen_branch(ctx, a, TCG_COND_LT); +} + +static bool trans_bge(DisasContext *ctx, arg_bge *a) +{ + return gen_branch(ctx, a, TCG_COND_GE); +} + +static bool trans_bltu(DisasContext *ctx, arg_bltu *a) +{ + return gen_branch(ctx, a, TCG_COND_LTU); +} + +static bool trans_bgeu(DisasContext *ctx, arg_bgeu *a) +{ + return gen_branch(ctx, a, TCG_COND_GEU); +} + +static bool gen_load(DisasContext *ctx, arg_lb *a, TCGMemOp memop) +{ + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + tcg_gen_addi_tl(t0, t0, a->imm); + + tcg_gen_qemu_ld_tl(t1, t0, ctx->mem_idx, memop); + gen_set_gpr(a->rd, t1); + tcg_temp_free(t0); + tcg_temp_free(t1); + return true; +} + +static bool trans_lb(DisasContext *ctx, arg_lb *a) +{ + return gen_load(ctx, a, MO_SB); +} + +static bool trans_lh(DisasContext *ctx, arg_lh *a) +{ + return gen_load(ctx, a, MO_TESW); +} + +static bool trans_lw(DisasContext *ctx, arg_lw *a) +{ + return gen_load(ctx, a, MO_TESL); +} + +static bool trans_lbu(DisasContext *ctx, arg_lbu *a) +{ + return gen_load(ctx, a, MO_UB); +} + +static bool trans_lhu(DisasContext *ctx, arg_lhu *a) +{ + return gen_load(ctx, a, MO_TEUW); +} + +static bool gen_store(DisasContext *ctx, arg_sb *a, TCGMemOp memop) +{ + TCGv t0 = tcg_temp_new(); + TCGv dat = tcg_temp_new(); + gen_get_gpr(t0, a->rs1); + tcg_gen_addi_tl(t0, t0, a->imm); + gen_get_gpr(dat, a->rs2); + + tcg_gen_qemu_st_tl(dat, t0, ctx->mem_idx, memop); + tcg_temp_free(t0); + tcg_temp_free(dat); + return true; +} + + +static bool trans_sb(DisasContext *ctx, arg_sb *a) +{ + return gen_store(ctx, a, MO_SB); +} + +static bool trans_sh(DisasContext *ctx, arg_sh *a) +{ + return gen_store(ctx, a, MO_TESW); +} + +static bool trans_sw(DisasContext *ctx, arg_sw *a) +{ + return gen_store(ctx, a, MO_TESL); +} + +#ifdef TARGET_RISCV64 +static bool trans_lwu(DisasContext *ctx, arg_lwu *a) +{ + return gen_load(ctx, a, MO_TEUL); +} + +static bool trans_ld(DisasContext *ctx, arg_ld *a) +{ + return gen_load(ctx, a, MO_TEQ); +} + +static bool trans_sd(DisasContext *ctx, arg_sd *a) +{ + return gen_store(ctx, a, MO_TEQ); +} +#endif + +static bool trans_addi(DisasContext *ctx, arg_addi *a) +{ + return gen_arith_imm(ctx, a, &tcg_gen_add_tl); +} + +static void gen_slt(TCGv ret, TCGv s1, TCGv s2) +{ + tcg_gen_setcond_tl(TCG_COND_LT, ret, s1, s2); +} + +static void gen_sltu(TCGv ret, TCGv s1, TCGv s2) +{ + tcg_gen_setcond_tl(TCG_COND_LTU, ret, s1, s2); +} + + +static bool trans_slti(DisasContext *ctx, arg_slti *a) +{ + return gen_arith_imm(ctx, a, &gen_slt); +} + +static bool trans_sltiu(DisasContext *ctx, arg_sltiu *a) +{ + return gen_arith_imm(ctx, a, &gen_sltu); +} + +static bool trans_xori(DisasContext *ctx, arg_xori *a) +{ + return gen_arith_imm(ctx, a, &tcg_gen_xor_tl); +} +static bool trans_ori(DisasContext *ctx, arg_ori *a) +{ + return gen_arith_imm(ctx, a, &tcg_gen_or_tl); +} +static bool trans_andi(DisasContext *ctx, arg_andi *a) +{ + return gen_arith_imm(ctx, a, &tcg_gen_and_tl); +} +static bool trans_slli(DisasContext *ctx, arg_slli *a) +{ + if (a->shamt >= TARGET_LONG_BITS) { + return false; + } + + if (a->rd != 0) { + TCGv t = tcg_temp_new(); + gen_get_gpr(t, a->rs1); + + tcg_gen_shli_tl(t, t, a->shamt); + + gen_set_gpr(a->rd, t); + tcg_temp_free(t); + } /* NOP otherwise */ + return true; +} + +static bool trans_srli(DisasContext *ctx, arg_srli *a) +{ + if (a->shamt >= TARGET_LONG_BITS) { + return false; + } + + if (a->rd != 0) { + TCGv t = tcg_temp_new(); + gen_get_gpr(t, a->rs1); + + tcg_gen_shri_tl(t, t, a->shamt); + gen_set_gpr(a->rd, t); + tcg_temp_free(t); + } /* NOP otherwise */ + return true; +} + +static bool trans_srai(DisasContext *ctx, arg_srai *a) +{ + if (a->shamt >= TARGET_LONG_BITS) { + return false; + } + + if (a->rd != 0) { + TCGv t = tcg_temp_new(); + gen_get_gpr(t, a->rs1); + + tcg_gen_sari_tl(t, t, a->shamt); + gen_set_gpr(a->rd, t); + tcg_temp_free(t); + } /* NOP otherwise */ + return true; +} + +static bool trans_add(DisasContext *ctx, arg_add *a) +{ + return gen_arith(ctx, a, &tcg_gen_add_tl); +} + +static bool trans_sub(DisasContext *ctx, arg_sub *a) +{ + return gen_arith(ctx, a, &tcg_gen_sub_tl); +} + +static bool trans_sll(DisasContext *ctx, arg_sll *a) +{ + return gen_shift(ctx, a, &tcg_gen_shl_tl); +} + +static bool trans_slt(DisasContext *ctx, arg_slt *a) +{ + return gen_arith(ctx, a, &gen_slt); +} + +static bool trans_sltu(DisasContext *ctx, arg_sltu *a) +{ + return gen_arith(ctx, a, &gen_sltu); +} + +static bool trans_xor(DisasContext *ctx, arg_xor *a) +{ + return gen_arith(ctx, a, &tcg_gen_xor_tl); +} + +static bool trans_srl(DisasContext *ctx, arg_srl *a) +{ + return gen_shift(ctx, a, &tcg_gen_shr_tl); +} + +static bool trans_sra(DisasContext *ctx, arg_sra *a) +{ + return gen_shift(ctx, a, &tcg_gen_sar_tl); +} + +static bool trans_or(DisasContext *ctx, arg_or *a) +{ + return gen_arith(ctx, a, &tcg_gen_or_tl); +} + +static bool trans_and(DisasContext *ctx, arg_and *a) +{ + return gen_arith(ctx, a, &tcg_gen_and_tl); +} + +#ifdef TARGET_RISCV64 +static bool trans_addiw(DisasContext *ctx, arg_addiw *a) +{ + return gen_arith_imm(ctx, a, &gen_addw); +} + +static bool trans_slliw(DisasContext *ctx, arg_slliw *a) +{ + TCGv source1; + source1 = tcg_temp_new(); + gen_get_gpr(source1, a->rs1); + + tcg_gen_shli_tl(source1, source1, a->shamt); + tcg_gen_ext32s_tl(source1, source1); + gen_set_gpr(a->rd, source1); + + tcg_temp_free(source1); + return true; +} + +static bool trans_srliw(DisasContext *ctx, arg_srliw *a) +{ + TCGv t = tcg_temp_new(); + gen_get_gpr(t, a->rs1); + tcg_gen_extract_tl(t, t, a->shamt, 32 - a->shamt); + /* sign-extend for W instructions */ + tcg_gen_ext32s_tl(t, t); + gen_set_gpr(a->rd, t); + tcg_temp_free(t); + return true; +} + +static bool trans_sraiw(DisasContext *ctx, arg_sraiw *a) +{ + TCGv t = tcg_temp_new(); + gen_get_gpr(t, a->rs1); + tcg_gen_sextract_tl(t, t, a->shamt, 32 - a->shamt); + gen_set_gpr(a->rd, t); + tcg_temp_free(t); + return true; +} + +static bool trans_addw(DisasContext *ctx, arg_addw *a) +{ + return gen_arith(ctx, a, &gen_addw); +} + +static bool trans_subw(DisasContext *ctx, arg_subw *a) +{ + return gen_arith(ctx, a, &gen_subw); +} + +static bool trans_sllw(DisasContext *ctx, arg_sllw *a) +{ + TCGv source1 = tcg_temp_new(); + TCGv source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + tcg_gen_andi_tl(source2, source2, 0x1F); + tcg_gen_shl_tl(source1, source1, source2); + + tcg_gen_ext32s_tl(source1, source1); + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +static bool trans_srlw(DisasContext *ctx, arg_srlw *a) +{ + TCGv source1 = tcg_temp_new(); + TCGv source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + /* clear upper 32 */ + tcg_gen_ext32u_tl(source1, source1); + tcg_gen_andi_tl(source2, source2, 0x1F); + tcg_gen_shr_tl(source1, source1, source2); + + tcg_gen_ext32s_tl(source1, source1); + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +static bool trans_sraw(DisasContext *ctx, arg_sraw *a) +{ + TCGv source1 = tcg_temp_new(); + TCGv source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + /* + * first, trick to get it to act like working on 32 bits (get rid of + * upper 32, sign extend to fill space) + */ + tcg_gen_ext32s_tl(source1, source1); + tcg_gen_andi_tl(source2, source2, 0x1F); + tcg_gen_sar_tl(source1, source1, source2); + + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + + return true; +} +#endif + +static bool trans_fence(DisasContext *ctx, arg_fence *a) +{ + /* FENCE is a full memory barrier. */ + tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); + return true; +} + +static bool trans_fence_i(DisasContext *ctx, arg_fence_i *a) +{ + /* + * FENCE_I is a no-op in QEMU, + * however we need to end the translation block + */ + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); + tcg_gen_exit_tb(NULL, 0); + ctx->base.is_jmp = DISAS_NORETURN; + return true; +} + +#define RISCV_OP_CSR_PRE do {\ + source1 = tcg_temp_new(); \ + csr_store = tcg_temp_new(); \ + dest = tcg_temp_new(); \ + rs1_pass = tcg_temp_new(); \ + gen_get_gpr(source1, a->rs1); \ + tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); \ + tcg_gen_movi_tl(rs1_pass, a->rs1); \ + tcg_gen_movi_tl(csr_store, a->csr); \ + gen_io_start();\ +} while (0) + +#define RISCV_OP_CSR_POST do {\ + gen_io_end(); \ + gen_set_gpr(a->rd, dest); \ + tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); \ + tcg_gen_exit_tb(NULL, 0); \ + ctx->base.is_jmp = DISAS_NORETURN; \ + tcg_temp_free(source1); \ + tcg_temp_free(csr_store); \ + tcg_temp_free(dest); \ + tcg_temp_free(rs1_pass); \ +} while (0) + + +static bool trans_csrrw(DisasContext *ctx, arg_csrrw *a) +{ + TCGv source1, csr_store, dest, rs1_pass; + RISCV_OP_CSR_PRE; + gen_helper_csrrw(dest, cpu_env, source1, csr_store); + RISCV_OP_CSR_POST; + return true; +} + +static bool trans_csrrs(DisasContext *ctx, arg_csrrs *a) +{ + TCGv source1, csr_store, dest, rs1_pass; + RISCV_OP_CSR_PRE; + gen_helper_csrrs(dest, cpu_env, source1, csr_store, rs1_pass); + RISCV_OP_CSR_POST; + return true; +} + +static bool trans_csrrc(DisasContext *ctx, arg_csrrc *a) +{ + TCGv source1, csr_store, dest, rs1_pass; + RISCV_OP_CSR_PRE; + gen_helper_csrrc(dest, cpu_env, source1, csr_store, rs1_pass); + RISCV_OP_CSR_POST; + return true; +} + +static bool trans_csrrwi(DisasContext *ctx, arg_csrrwi *a) +{ + TCGv source1, csr_store, dest, rs1_pass; + RISCV_OP_CSR_PRE; + gen_helper_csrrw(dest, cpu_env, rs1_pass, csr_store); + RISCV_OP_CSR_POST; + return true; +} + +static bool trans_csrrsi(DisasContext *ctx, arg_csrrsi *a) +{ + TCGv source1, csr_store, dest, rs1_pass; + RISCV_OP_CSR_PRE; + gen_helper_csrrs(dest, cpu_env, rs1_pass, csr_store, rs1_pass); + RISCV_OP_CSR_POST; + return true; +} + +static bool trans_csrrci(DisasContext *ctx, arg_csrrci *a) +{ + TCGv source1, csr_store, dest, rs1_pass; + RISCV_OP_CSR_PRE; + gen_helper_csrrc(dest, cpu_env, rs1_pass, csr_store, rs1_pass); + RISCV_OP_CSR_POST; + return true; +} diff --git a/target/riscv/insn_trans/trans_rvm.inc.c b/target/riscv/insn_trans/trans_rvm.inc.c new file mode 100644 index 0000000000..47cd6edc72 --- /dev/null +++ b/target/riscv/insn_trans/trans_rvm.inc.c @@ -0,0 +1,120 @@ +/* + * RISC-V translation routines for the RV64M Standard Extension. + * + * Copyright (c) 2016-2017 Sagar Karandikar, sagark@eecs.berkeley.edu + * Copyright (c) 2018 Peer Adelt, peer.adelt@hni.uni-paderborn.de + * Bastian Koppelmann, kbastian@mail.uni-paderborn.de + * + * 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 . + */ + + +static bool trans_mul(DisasContext *ctx, arg_mul *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &tcg_gen_mul_tl); +} + +static bool trans_mulh(DisasContext *ctx, arg_mulh *a) +{ + REQUIRE_EXT(ctx, RVM); + TCGv source1 = tcg_temp_new(); + TCGv source2 = tcg_temp_new(); + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + tcg_gen_muls2_tl(source2, source1, source1, source2); + + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +static bool trans_mulhsu(DisasContext *ctx, arg_mulhsu *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &gen_mulhsu); +} + +static bool trans_mulhu(DisasContext *ctx, arg_mulhu *a) +{ + REQUIRE_EXT(ctx, RVM); + TCGv source1 = tcg_temp_new(); + TCGv source2 = tcg_temp_new(); + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + tcg_gen_mulu2_tl(source2, source1, source1, source2); + + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +static bool trans_div(DisasContext *ctx, arg_div *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &gen_div); +} + +static bool trans_divu(DisasContext *ctx, arg_divu *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &gen_divu); +} + +static bool trans_rem(DisasContext *ctx, arg_rem *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &gen_rem); +} + +static bool trans_remu(DisasContext *ctx, arg_remu *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &gen_remu); +} + +#ifdef TARGET_RISCV64 +static bool trans_mulw(DisasContext *ctx, arg_mulw *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith(ctx, a, &gen_mulw); +} + +static bool trans_divw(DisasContext *ctx, arg_divw *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith_div_w(ctx, a, &gen_div); +} + +static bool trans_divuw(DisasContext *ctx, arg_divuw *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith_div_uw(ctx, a, &gen_divu); +} + +static bool trans_remw(DisasContext *ctx, arg_remw *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith_div_w(ctx, a, &gen_rem); +} + +static bool trans_remuw(DisasContext *ctx, arg_remuw *a) +{ + REQUIRE_EXT(ctx, RVM); + return gen_arith_div_uw(ctx, a, &gen_remu); +} +#endif diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index aec7558e1b..b7dc18a41e 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -24,41 +24,8 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" -#ifndef CONFIG_USER_ONLY - -#if defined(TARGET_RISCV32) -static const char valid_vm_1_09[16] = { - [VM_1_09_MBARE] = 1, - [VM_1_09_SV32] = 1, -}; -static const char valid_vm_1_10[16] = { - [VM_1_10_MBARE] = 1, - [VM_1_10_SV32] = 1 -}; -#elif defined(TARGET_RISCV64) -static const char valid_vm_1_09[16] = { - [VM_1_09_MBARE] = 1, - [VM_1_09_SV39] = 1, - [VM_1_09_SV48] = 1, -}; -static const char valid_vm_1_10[16] = { - [VM_1_10_MBARE] = 1, - [VM_1_10_SV39] = 1, - [VM_1_10_SV48] = 1, - [VM_1_10_SV57] = 1 -}; -#endif - -static int validate_vm(CPURISCVState *env, target_ulong vm) -{ - return (env->priv_ver >= PRIV_VERSION_1_10_0) ? - valid_vm_1_10[vm & 0xf] : valid_vm_1_09[vm & 0xf]; -} - -#endif - /* Exceptions processing helpers */ -void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, +void QEMU_NORETURN riscv_raise_exception(CPURISCVState *env, uint32_t exception, uintptr_t pc) { CPUState *cs = CPU(riscv_env_get_cpu(env)); @@ -69,627 +36,55 @@ void QEMU_NORETURN do_raise_exception_err(CPURISCVState *env, void helper_raise_exception(CPURISCVState *env, uint32_t exception) { - do_raise_exception_err(env, exception, 0); -} - -static void validate_mstatus_fs(CPURISCVState *env, uintptr_t ra) -{ -#ifndef CONFIG_USER_ONLY - if (!(env->mstatus & MSTATUS_FS)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra); - } -#endif -} - -/* - * Handle writes to CSRs and any resulting special behavior - * - * Adapted from Spike's processor_t::set_csr - */ -void csr_write_helper(CPURISCVState *env, target_ulong val_to_write, - target_ulong csrno) -{ -#ifndef CONFIG_USER_ONLY - uint64_t delegable_ints = MIP_SSIP | MIP_STIP | MIP_SEIP | (1 << IRQ_X_COP); - uint64_t all_ints = delegable_ints | MIP_MSIP | MIP_MTIP; -#endif - - switch (csrno) { - case CSR_FFLAGS: - validate_mstatus_fs(env, GETPC()); - cpu_riscv_set_fflags(env, val_to_write & (FSR_AEXC >> FSR_AEXC_SHIFT)); - break; - case CSR_FRM: - validate_mstatus_fs(env, GETPC()); - env->frm = val_to_write & (FSR_RD >> FSR_RD_SHIFT); - break; - case CSR_FCSR: - validate_mstatus_fs(env, GETPC()); - env->frm = (val_to_write & FSR_RD) >> FSR_RD_SHIFT; - cpu_riscv_set_fflags(env, (val_to_write & FSR_AEXC) >> FSR_AEXC_SHIFT); - break; -#ifndef CONFIG_USER_ONLY - case CSR_MSTATUS: { - target_ulong mstatus = env->mstatus; - target_ulong mask = 0; - target_ulong mpp = get_field(val_to_write, MSTATUS_MPP); - - /* flush tlb on mstatus fields that affect VM */ - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | - MSTATUS_MPRV | MSTATUS_SUM | MSTATUS_VM)) { - helper_tlb_flush(env); - } - mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | - MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | - MSTATUS_MPP | MSTATUS_MXR | - (validate_vm(env, get_field(val_to_write, MSTATUS_VM)) ? - MSTATUS_VM : 0); - } - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - if ((val_to_write ^ mstatus) & (MSTATUS_MXR | MSTATUS_MPP | - MSTATUS_MPRV | MSTATUS_SUM)) { - helper_tlb_flush(env); - } - mask = MSTATUS_SIE | MSTATUS_SPIE | MSTATUS_MIE | MSTATUS_MPIE | - MSTATUS_SPP | MSTATUS_FS | MSTATUS_MPRV | MSTATUS_SUM | - MSTATUS_MPP | MSTATUS_MXR; - } - - /* silenty discard mstatus.mpp writes for unsupported modes */ - if (mpp == PRV_H || - (!riscv_has_ext(env, RVS) && mpp == PRV_S) || - (!riscv_has_ext(env, RVU) && mpp == PRV_U)) { - mask &= ~MSTATUS_MPP; - } - - mstatus = (mstatus & ~mask) | (val_to_write & mask); - - /* Note: this is a workaround for an issue where mstatus.FS - does not report dirty after floating point operations - that modify floating point state. This workaround is - technically compliant with the RISC-V Privileged - specification as it is legal to return only off, or dirty. - at the expense of extra floating point save/restore. */ - - /* FP is always dirty or off */ - if (mstatus & MSTATUS_FS) { - mstatus |= MSTATUS_FS; - } - - int dirty = ((mstatus & MSTATUS_FS) == MSTATUS_FS) | - ((mstatus & MSTATUS_XS) == MSTATUS_XS); - mstatus = set_field(mstatus, MSTATUS_SD, dirty); - env->mstatus = mstatus; - break; - } - case CSR_MIP: { - /* - * Since the writeable bits in MIP are not set asynchrously by the - * CLINT, no additional locking is needed for read-modifiy-write - * CSR operations - */ - qemu_mutex_lock_iothread(); - RISCVCPU *cpu = riscv_env_get_cpu(env); - riscv_set_local_interrupt(cpu, MIP_SSIP, - (val_to_write & MIP_SSIP) != 0); - riscv_set_local_interrupt(cpu, MIP_STIP, - (val_to_write & MIP_STIP) != 0); - /* - * csrs, csrc on mip.SEIP is not decomposable into separate read and - * write steps, so a different implementation is needed - */ - qemu_mutex_unlock_iothread(); - break; - } - case CSR_MIE: { - env->mie = (env->mie & ~all_ints) | - (val_to_write & all_ints); - break; - } - case CSR_MIDELEG: - env->mideleg = (env->mideleg & ~delegable_ints) - | (val_to_write & delegable_ints); - break; - case CSR_MEDELEG: { - target_ulong mask = 0; - mask |= 1ULL << (RISCV_EXCP_INST_ADDR_MIS); - mask |= 1ULL << (RISCV_EXCP_INST_ACCESS_FAULT); - mask |= 1ULL << (RISCV_EXCP_ILLEGAL_INST); - mask |= 1ULL << (RISCV_EXCP_BREAKPOINT); - mask |= 1ULL << (RISCV_EXCP_LOAD_ADDR_MIS); - mask |= 1ULL << (RISCV_EXCP_LOAD_ACCESS_FAULT); - mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ADDR_MIS); - mask |= 1ULL << (RISCV_EXCP_STORE_AMO_ACCESS_FAULT); - mask |= 1ULL << (RISCV_EXCP_U_ECALL); - mask |= 1ULL << (RISCV_EXCP_S_ECALL); - mask |= 1ULL << (RISCV_EXCP_H_ECALL); - mask |= 1ULL << (RISCV_EXCP_M_ECALL); - mask |= 1ULL << (RISCV_EXCP_INST_PAGE_FAULT); - mask |= 1ULL << (RISCV_EXCP_LOAD_PAGE_FAULT); - mask |= 1ULL << (RISCV_EXCP_STORE_PAGE_FAULT); - env->medeleg = (env->medeleg & ~mask) - | (val_to_write & mask); - break; - } - case CSR_MINSTRET: - /* minstret is WARL so unsupported writes are ignored */ - break; - case CSR_MCYCLE: - /* mcycle is WARL so unsupported writes are ignored */ - break; -#if defined(TARGET_RISCV32) - case CSR_MINSTRETH: - /* minstreth is WARL so unsupported writes are ignored */ - break; - case CSR_MCYCLEH: - /* mcycleh is WARL so unsupported writes are ignored */ - break; -#endif - case CSR_MUCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - env->scounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_MSCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - env->mcounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_SSTATUS: { - target_ulong ms = env->mstatus; - target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE - | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_SD; - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - mask |= SSTATUS_MXR; - } - ms = (ms & ~mask) | (val_to_write & mask); - csr_write_helper(env, ms, CSR_MSTATUS); - break; - } - case CSR_SIP: { - qemu_mutex_lock_iothread(); - target_ulong next_mip = (env->mip & ~env->mideleg) - | (val_to_write & env->mideleg); - qemu_mutex_unlock_iothread(); - csr_write_helper(env, next_mip, CSR_MIP); - break; - } - case CSR_SIE: { - target_ulong next_mie = (env->mie & ~env->mideleg) - | (val_to_write & env->mideleg); - csr_write_helper(env, next_mie, CSR_MIE); - break; - } - case CSR_SATP: /* CSR_SPTBR */ { - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { - break; - } - if (env->priv_ver <= PRIV_VERSION_1_09_1 && (val_to_write ^ env->sptbr)) - { - helper_tlb_flush(env); - env->sptbr = val_to_write & (((target_ulong) - 1 << (TARGET_PHYS_ADDR_SPACE_BITS - PGSHIFT)) - 1); - } - if (env->priv_ver >= PRIV_VERSION_1_10_0 && - validate_vm(env, get_field(val_to_write, SATP_MODE)) && - ((val_to_write ^ env->satp) & (SATP_MODE | SATP_ASID | SATP_PPN))) - { - helper_tlb_flush(env); - env->satp = val_to_write; - } - break; - } - case CSR_SEPC: - env->sepc = val_to_write; - break; - case CSR_STVEC: - /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ - if ((val_to_write & 3) == 0) { - env->stvec = val_to_write >> 2 << 2; - } else { - qemu_log_mask(LOG_UNIMP, - "CSR_STVEC: vectored traps not supported\n"); - } - break; - case CSR_SCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - env->scounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_SSCRATCH: - env->sscratch = val_to_write; - break; - case CSR_SCAUSE: - env->scause = val_to_write; - break; - case CSR_SBADADDR: - env->sbadaddr = val_to_write; - break; - case CSR_MEPC: - env->mepc = val_to_write; - break; - case CSR_MTVEC: - /* bits [1:0] indicate mode; 0 = direct, 1 = vectored, 2 >= reserved */ - if ((val_to_write & 3) == 0) { - env->mtvec = val_to_write >> 2 << 2; - } else { - qemu_log_mask(LOG_UNIMP, - "CSR_MTVEC: vectored traps not supported\n"); - } - break; - case CSR_MCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - env->mcounteren = val_to_write; - break; - } else { - goto do_illegal; - } - case CSR_MSCRATCH: - env->mscratch = val_to_write; - break; - case CSR_MCAUSE: - env->mcause = val_to_write; - break; - case CSR_MBADADDR: - env->mbadaddr = val_to_write; - break; - case CSR_MISA: - /* misa is WARL so unsupported writes are ignored */ - break; - case CSR_PMPCFG0: - case CSR_PMPCFG1: - case CSR_PMPCFG2: - case CSR_PMPCFG3: - pmpcfg_csr_write(env, csrno - CSR_PMPCFG0, val_to_write); - break; - case CSR_PMPADDR0: - case CSR_PMPADDR1: - case CSR_PMPADDR2: - case CSR_PMPADDR3: - case CSR_PMPADDR4: - case CSR_PMPADDR5: - case CSR_PMPADDR6: - case CSR_PMPADDR7: - case CSR_PMPADDR8: - case CSR_PMPADDR9: - case CSR_PMPADDR10: - case CSR_PMPADDR11: - case CSR_PMPADDR12: - case CSR_PMPADDR13: - case CSR_PMPADDR14: - case CSR_PMPADDR15: - pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val_to_write); - break; -#endif -#if !defined(CONFIG_USER_ONLY) - do_illegal: -#endif - default: - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); - } -} - -/* - * Handle reads to CSRs and any resulting special behavior - * - * Adapted from Spike's processor_t::get_csr - */ -target_ulong csr_read_helper(CPURISCVState *env, target_ulong csrno) -{ -#ifndef CONFIG_USER_ONLY - target_ulong ctr_en = env->priv == PRV_U ? env->scounteren : - env->priv == PRV_S ? env->mcounteren : -1U; -#else - target_ulong ctr_en = -1; -#endif - target_ulong ctr_ok = (ctr_en >> (csrno & 31)) & 1; - - if (csrno >= CSR_HPMCOUNTER3 && csrno <= CSR_HPMCOUNTER31) { - if (ctr_ok) { - return 0; - } - } -#if defined(TARGET_RISCV32) - if (csrno >= CSR_HPMCOUNTER3H && csrno <= CSR_HPMCOUNTER31H) { - if (ctr_ok) { - return 0; - } - } -#endif - if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { - return 0; - } -#if defined(TARGET_RISCV32) - if (csrno >= CSR_MHPMCOUNTER3 && csrno <= CSR_MHPMCOUNTER31) { - return 0; - } -#endif - if (csrno >= CSR_MHPMEVENT3 && csrno <= CSR_MHPMEVENT31) { - return 0; - } - - switch (csrno) { - case CSR_FFLAGS: - validate_mstatus_fs(env, GETPC()); - return cpu_riscv_get_fflags(env); - case CSR_FRM: - validate_mstatus_fs(env, GETPC()); - return env->frm; - case CSR_FCSR: - validate_mstatus_fs(env, GETPC()); - return (cpu_riscv_get_fflags(env) << FSR_AEXC_SHIFT) - | (env->frm << FSR_RD_SHIFT); - /* rdtime/rdtimeh is trapped and emulated by bbl in system mode */ -#ifdef CONFIG_USER_ONLY - case CSR_TIME: - return cpu_get_host_ticks(); -#if defined(TARGET_RISCV32) - case CSR_TIMEH: - return cpu_get_host_ticks() >> 32; -#endif -#endif - case CSR_INSTRET: - case CSR_CYCLE: - if (ctr_ok) { -#if !defined(CONFIG_USER_ONLY) - if (use_icount) { - return cpu_get_icount(); - } else { - return cpu_get_host_ticks(); - } -#else - return cpu_get_host_ticks(); -#endif - } - break; -#if defined(TARGET_RISCV32) - case CSR_INSTRETH: - case CSR_CYCLEH: - if (ctr_ok) { -#if !defined(CONFIG_USER_ONLY) - if (use_icount) { - return cpu_get_icount() >> 32; - } else { - return cpu_get_host_ticks() >> 32; - } -#else - return cpu_get_host_ticks() >> 32; -#endif - } - break; -#endif -#ifndef CONFIG_USER_ONLY - case CSR_MINSTRET: - case CSR_MCYCLE: - if (use_icount) { - return cpu_get_icount(); - } else { - return cpu_get_host_ticks(); - } - case CSR_MINSTRETH: - case CSR_MCYCLEH: -#if defined(TARGET_RISCV32) - if (use_icount) { - return cpu_get_icount() >> 32; - } else { - return cpu_get_host_ticks() >> 32; - } -#endif - break; - case CSR_MUCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - return env->scounteren; - } else { - break; /* illegal instruction */ - } - case CSR_MSCOUNTEREN: - if (env->priv_ver <= PRIV_VERSION_1_09_1) { - return env->mcounteren; - } else { - break; /* illegal instruction */ - } - case CSR_SSTATUS: { - target_ulong mask = SSTATUS_SIE | SSTATUS_SPIE | SSTATUS_UIE - | SSTATUS_UPIE | SSTATUS_SPP | SSTATUS_FS | SSTATUS_XS - | SSTATUS_SUM | SSTATUS_SD; - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - mask |= SSTATUS_MXR; - } - return env->mstatus & mask; - } - case CSR_SIP: { - qemu_mutex_lock_iothread(); - target_ulong tmp = env->mip & env->mideleg; - qemu_mutex_unlock_iothread(); - return tmp; - } - case CSR_SIE: - return env->mie & env->mideleg; - case CSR_SEPC: - return env->sepc; - case CSR_SBADADDR: - return env->sbadaddr; - case CSR_STVEC: - return env->stvec; - case CSR_SCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - return env->scounteren; - } else { - break; /* illegal instruction */ - } - case CSR_SCAUSE: - return env->scause; - case CSR_SATP: /* CSR_SPTBR */ - if (!riscv_feature(env, RISCV_FEATURE_MMU)) { - return 0; - } - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - return env->satp; - } else { - return env->sptbr; - } - case CSR_SSCRATCH: - return env->sscratch; - case CSR_MSTATUS: - return env->mstatus; - case CSR_MIP: { - qemu_mutex_lock_iothread(); - target_ulong tmp = env->mip; - qemu_mutex_unlock_iothread(); - return tmp; - } - case CSR_MIE: - return env->mie; - case CSR_MEPC: - return env->mepc; - case CSR_MSCRATCH: - return env->mscratch; - case CSR_MCAUSE: - return env->mcause; - case CSR_MBADADDR: - return env->mbadaddr; - case CSR_MISA: - return env->misa; - case CSR_MARCHID: - return 0; /* as spike does */ - case CSR_MIMPID: - return 0; /* as spike does */ - case CSR_MVENDORID: - return 0; /* as spike does */ - case CSR_MHARTID: - return env->mhartid; - case CSR_MTVEC: - return env->mtvec; - case CSR_MCOUNTEREN: - if (env->priv_ver >= PRIV_VERSION_1_10_0) { - return env->mcounteren; - } else { - break; /* illegal instruction */ - } - case CSR_MEDELEG: - return env->medeleg; - case CSR_MIDELEG: - return env->mideleg; - case CSR_PMPCFG0: - case CSR_PMPCFG1: - case CSR_PMPCFG2: - case CSR_PMPCFG3: - return pmpcfg_csr_read(env, csrno - CSR_PMPCFG0); - case CSR_PMPADDR0: - case CSR_PMPADDR1: - case CSR_PMPADDR2: - case CSR_PMPADDR3: - case CSR_PMPADDR4: - case CSR_PMPADDR5: - case CSR_PMPADDR6: - case CSR_PMPADDR7: - case CSR_PMPADDR8: - case CSR_PMPADDR9: - case CSR_PMPADDR10: - case CSR_PMPADDR11: - case CSR_PMPADDR12: - case CSR_PMPADDR13: - case CSR_PMPADDR14: - case CSR_PMPADDR15: - return pmpaddr_csr_read(env, csrno - CSR_PMPADDR0); -#endif - } - /* used by e.g. MTIME read */ - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); -} - -/* - * Check that CSR access is allowed. - * - * Adapted from Spike's decode.h:validate_csr - */ -static void validate_csr(CPURISCVState *env, uint64_t which, - uint64_t write, uintptr_t ra) -{ -#ifndef CONFIG_USER_ONLY - unsigned csr_priv = get_field((which), 0x300); - unsigned csr_read_only = get_field((which), 0xC00) == 3; - if (((write) && csr_read_only) || (env->priv < csr_priv)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, ra); - } -#endif + riscv_raise_exception(env, exception, 0); } target_ulong helper_csrrw(CPURISCVState *env, target_ulong src, target_ulong csr) { - validate_csr(env, csr, 1, GETPC()); - uint64_t csr_backup = csr_read_helper(env, csr); - csr_write_helper(env, src, csr); - return csr_backup; + target_ulong val = 0; + if (riscv_csrrw(env, csr, &val, src, -1) < 0) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } + return val; } target_ulong helper_csrrs(CPURISCVState *env, target_ulong src, target_ulong csr, target_ulong rs1_pass) { - validate_csr(env, csr, rs1_pass != 0, GETPC()); - uint64_t csr_backup = csr_read_helper(env, csr); - if (rs1_pass != 0) { - csr_write_helper(env, src | csr_backup, csr); + target_ulong val = 0; + if (riscv_csrrw(env, csr, &val, -1, rs1_pass ? src : 0) < 0) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - return csr_backup; + return val; } target_ulong helper_csrrc(CPURISCVState *env, target_ulong src, target_ulong csr, target_ulong rs1_pass) { - validate_csr(env, csr, rs1_pass != 0, GETPC()); - uint64_t csr_backup = csr_read_helper(env, csr); - if (rs1_pass != 0) { - csr_write_helper(env, (~src) & csr_backup, csr); + target_ulong val = 0; + if (riscv_csrrw(env, csr, &val, 0, rs1_pass ? src : 0) < 0) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - return csr_backup; + return val; } #ifndef CONFIG_USER_ONLY -/* iothread_mutex must be held */ -void riscv_set_local_interrupt(RISCVCPU *cpu, target_ulong mask, int value) -{ - target_ulong old_mip = cpu->env.mip; - cpu->env.mip = (old_mip & ~mask) | (value ? mask : 0); - - if (cpu->env.mip && !old_mip) { - cpu_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); - } else if (!cpu->env.mip && old_mip) { - cpu_reset_interrupt(CPU(cpu), CPU_INTERRUPT_HARD); - } -} - -void riscv_set_mode(CPURISCVState *env, target_ulong newpriv) -{ - if (newpriv > PRV_M) { - g_assert_not_reached(); - } - if (newpriv == PRV_H) { - newpriv = PRV_U; - } - /* tlb_flush is unnecessary as mode is contained in mmu_idx */ - env->priv = newpriv; -} - target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_S)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->sepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { - do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + } + + if (env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TSR)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong mstatus = env->mstatus; @@ -700,8 +95,8 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_SPIE)); mstatus = set_field(mstatus, MSTATUS_SPIE, 0); mstatus = set_field(mstatus, MSTATUS_SPP, PRV_U); - riscv_set_mode(env, prev_priv); - csr_write_helper(env, mstatus, CSR_MSTATUS); + riscv_cpu_set_mode(env, prev_priv); + env->mstatus = mstatus; return retpc; } @@ -709,12 +104,12 @@ target_ulong helper_sret(CPURISCVState *env, target_ulong cpu_pc_deb) target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) { if (!(env->priv >= PRV_M)) { - do_raise_exception_err(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } target_ulong retpc = env->mepc; if (!riscv_has_ext(env, RVC) && (retpc & 0x3)) { - do_raise_exception_err(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); } target_ulong mstatus = env->mstatus; @@ -725,27 +120,38 @@ target_ulong helper_mret(CPURISCVState *env, target_ulong cpu_pc_deb) get_field(mstatus, MSTATUS_MPIE)); mstatus = set_field(mstatus, MSTATUS_MPIE, 0); mstatus = set_field(mstatus, MSTATUS_MPP, PRV_U); - riscv_set_mode(env, prev_priv); - csr_write_helper(env, mstatus, CSR_MSTATUS); + riscv_cpu_set_mode(env, prev_priv); + env->mstatus = mstatus; return retpc; } - void helper_wfi(CPURISCVState *env) { CPUState *cs = CPU(riscv_env_get_cpu(env)); - cs->halted = 1; - cs->exception_index = EXCP_HLT; - cpu_loop_exit(cs); + if (env->priv == PRV_S && + env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TW)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else { + cs->halted = 1; + cs->exception_index = EXCP_HLT; + cpu_loop_exit(cs); + } } void helper_tlb_flush(CPURISCVState *env) { RISCVCPU *cpu = riscv_env_get_cpu(env); CPUState *cs = CPU(cpu); - tlb_flush(cs); + if (env->priv == PRV_S && + env->priv_ver >= PRIV_VERSION_1_10_0 && + get_field(env->mstatus, MSTATUS_TVM)) { + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + } else { + tlb_flush(cs); + } } #endif /* !CONFIG_USER_ONLY */ diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index f432f3b759..b11c4ae22f 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -113,10 +113,11 @@ static void pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) env->pmp_state.pmp[pmp_index].cfg_reg = val; pmp_update_rule(env, pmp_index); } else { - PMP_DEBUG("ignoring write - locked"); + qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); } } else { - PMP_DEBUG("ignoring write - out of bounds"); + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpcfg write - out of bounds\n"); } } @@ -138,7 +139,7 @@ static void pmp_decode_napot(target_ulong a, target_ulong *sa, target_ulong *ea) return; } else { target_ulong t1 = ctz64(~a); - target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 3; + target_ulong base = (a & ~(((target_ulong)1 << t1) - 1)) << 2; target_ulong range = ((target_ulong)1 << (t1 + 3)) - 1; *sa = base; *ea = base + range; @@ -249,7 +250,8 @@ bool pmp_hart_has_privs(CPURISCVState *env, target_ulong addr, /* partially inside */ if ((s + e) == 1) { - PMP_DEBUG("pmp violation - access is partially inside"); + qemu_log_mask(LOG_GUEST_ERROR, + "pmp violation - access is partially inside\n"); ret = 0; break; } @@ -306,7 +308,8 @@ void pmpcfg_csr_write(CPURISCVState *env, uint32_t reg_index, env->mhartid, reg_index, val); if ((reg_index & 1) && (sizeof(target_ulong) == 8)) { - PMP_DEBUG("ignoring write - incorrect address"); + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpcfg write - incorrect address\n"); return; } @@ -325,7 +328,7 @@ target_ulong pmpcfg_csr_read(CPURISCVState *env, uint32_t reg_index) { int i; target_ulong cfg_val = 0; - uint8_t val = 0; + target_ulong val = 0; for (i = 0; i < sizeof(target_ulong); i++) { val = pmp_read_cfg(env, (reg_index * sizeof(target_ulong)) + i); @@ -353,10 +356,12 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, env->pmp_state.pmp[addr_index].addr_reg = val; pmp_update_rule(env, addr_index); } else { - PMP_DEBUG("ignoring write - locked"); + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpaddr write - locked\n"); } } else { - PMP_DEBUG("ignoring write - out of bounds"); + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpaddr write - out of bounds\n"); } } @@ -372,7 +377,8 @@ target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) if (addr_index < MAX_RISCV_PMPS) { return env->pmp_state.pmp[addr_index].addr_reg; } else { - PMP_DEBUG("ignoring read - out of bounds"); + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpaddr read - out of bounds\n"); return 0; } } diff --git a/target/riscv/trace-events b/target/riscv/trace-events new file mode 100644 index 0000000000..48af0373df --- /dev/null +++ b/target/riscv/trace-events @@ -0,0 +1,2 @@ +# target/riscv/cpu_helper.c +riscv_trap(uint64_t hartid, bool async, uint64_t cause, uint64_t epc, uint64_t tval, const char *desc) "hart:%"PRId64", async:%d, cause:%"PRId64", epc:0x%"PRIx64", tval:0x%"PRIx64", desc=%s" diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 0b6be74f2d..dd763647ea 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -43,8 +43,10 @@ typedef struct DisasContext { DisasContextBase base; /* pc_succ_insn points to the instruction following base.pc_next */ target_ulong pc_succ_insn; + target_ulong priv_ver; uint32_t opcode; - uint32_t flags; + uint32_t mstatus_fs; + uint32_t misa; uint32_t mem_idx; /* Remember the rounding mode encoded in the previous fp instruction, which we have already installed into env->fp_status. Or -1 for @@ -54,6 +56,7 @@ typedef struct DisasContext { int frm; } DisasContext; +#ifdef TARGET_RISCV64 /* convert riscv funct3 to qemu memop for load/store */ static const int tcg_memop_lookup[8] = { [0 ... 7] = -1, @@ -67,6 +70,7 @@ static const int tcg_memop_lookup[8] = { [6] = MO_TEUL, #endif }; +#endif #ifdef TARGET_RISCV64 #define CASE_OP_32_64(X) case X: case glue(X, W) @@ -74,6 +78,11 @@ static const int tcg_memop_lookup[8] = { #define CASE_OP_32_64(X) case X #endif +static inline bool has_ext(DisasContext *ctx, uint32_t ext) +{ + return ctx->misa & ext; +} + static void generate_exception(DisasContext *ctx, int excp) { tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); @@ -135,7 +144,7 @@ static void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest) if (ctx->base.singlestep_enabled) { gen_exception_debug(); } else { - tcg_gen_exit_tb(NULL, 0); + tcg_gen_lookup_and_goto_ptr(); } } } @@ -179,340 +188,121 @@ static void gen_mulhsu(TCGv ret, TCGv arg1, TCGv arg2) tcg_temp_free(rh); } -static void gen_fsgnj(DisasContext *ctx, uint32_t rd, uint32_t rs1, - uint32_t rs2, int rm, uint64_t min) +static void gen_div(TCGv ret, TCGv source1, TCGv source2) { - switch (rm) { - case 0: /* fsgnj */ - if (rs1 == rs2) { /* FMOV */ - tcg_gen_mov_i64(cpu_fpr[rd], cpu_fpr[rs1]); - } else { - tcg_gen_deposit_i64(cpu_fpr[rd], cpu_fpr[rs2], cpu_fpr[rs1], - 0, min == INT32_MIN ? 31 : 63); - } - break; - case 1: /* fsgnjn */ - if (rs1 == rs2) { /* FNEG */ - tcg_gen_xori_i64(cpu_fpr[rd], cpu_fpr[rs1], min); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_not_i64(t0, cpu_fpr[rs2]); - tcg_gen_deposit_i64(cpu_fpr[rd], t0, cpu_fpr[rs1], - 0, min == INT32_MIN ? 31 : 63); - tcg_temp_free_i64(t0); - } - break; - case 2: /* fsgnjx */ - if (rs1 == rs2) { /* FABS */ - tcg_gen_andi_i64(cpu_fpr[rd], cpu_fpr[rs1], ~min); - } else { - TCGv_i64 t0 = tcg_temp_new_i64(); - tcg_gen_andi_i64(t0, cpu_fpr[rs2], min); - tcg_gen_xor_i64(cpu_fpr[rd], cpu_fpr[rs1], t0); - tcg_temp_free_i64(t0); - } - break; - default: - gen_exception_illegal(ctx); - } + TCGv cond1, cond2, zeroreg, resultopt1; + /* + * Handle by altering args to tcg_gen_div to produce req'd results: + * For overflow: want source1 in source1 and 1 in source2 + * For div by zero: want -1 in source1 and 1 in source2 -> -1 result + */ + cond1 = tcg_temp_new(); + cond2 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); + + tcg_gen_movi_tl(resultopt1, (target_ulong)-1); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L)); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, + ((target_ulong)1) << (TARGET_LONG_BITS - 1)); + tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */ + /* if div by zero, set source1 to -1, otherwise don't change */ + tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1, + resultopt1); + /* if overflow or div by zero, set source2 to 1, else don't change */ + tcg_gen_or_tl(cond1, cond1, cond2); + tcg_gen_movi_tl(resultopt1, (target_ulong)1); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, + resultopt1); + tcg_gen_div_tl(ret, source1, source2); + + tcg_temp_free(cond1); + tcg_temp_free(cond2); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); } -static void gen_arith(DisasContext *ctx, uint32_t opc, int rd, int rs1, - int rs2) +static void gen_divu(TCGv ret, TCGv source1, TCGv source2) { - TCGv source1, source2, cond1, cond2, zeroreg, resultopt1; - source1 = tcg_temp_new(); - source2 = tcg_temp_new(); - gen_get_gpr(source1, rs1); - gen_get_gpr(source2, rs2); + TCGv cond1, zeroreg, resultopt1; + cond1 = tcg_temp_new(); - switch (opc) { - CASE_OP_32_64(OPC_RISC_ADD): - tcg_gen_add_tl(source1, source1, source2); - break; - CASE_OP_32_64(OPC_RISC_SUB): - tcg_gen_sub_tl(source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SLLW: - tcg_gen_andi_tl(source2, source2, 0x1F); - tcg_gen_shl_tl(source1, source1, source2); - break; -#endif - case OPC_RISC_SLL: - tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); - tcg_gen_shl_tl(source1, source1, source2); - break; - case OPC_RISC_SLT: - tcg_gen_setcond_tl(TCG_COND_LT, source1, source1, source2); - break; - case OPC_RISC_SLTU: - tcg_gen_setcond_tl(TCG_COND_LTU, source1, source1, source2); - break; - case OPC_RISC_XOR: - tcg_gen_xor_tl(source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SRLW: - /* clear upper 32 */ - tcg_gen_ext32u_tl(source1, source1); - tcg_gen_andi_tl(source2, source2, 0x1F); - tcg_gen_shr_tl(source1, source1, source2); - break; -#endif - case OPC_RISC_SRL: - tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); - tcg_gen_shr_tl(source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SRAW: - /* first, trick to get it to act like working on 32 bits (get rid of - upper 32, sign extend to fill space) */ - tcg_gen_ext32s_tl(source1, source1); - tcg_gen_andi_tl(source2, source2, 0x1F); - tcg_gen_sar_tl(source1, source1, source2); - break; -#endif - case OPC_RISC_SRA: - tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); - tcg_gen_sar_tl(source1, source1, source2); - break; - case OPC_RISC_OR: - tcg_gen_or_tl(source1, source1, source2); - break; - case OPC_RISC_AND: - tcg_gen_and_tl(source1, source1, source2); - break; - CASE_OP_32_64(OPC_RISC_MUL): - tcg_gen_mul_tl(source1, source1, source2); - break; - case OPC_RISC_MULH: - tcg_gen_muls2_tl(source2, source1, source1, source2); - break; - case OPC_RISC_MULHSU: - gen_mulhsu(source1, source1, source2); - break; - case OPC_RISC_MULHU: - tcg_gen_mulu2_tl(source2, source1, source1, source2); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_DIVW: - tcg_gen_ext32s_tl(source1, source1); - tcg_gen_ext32s_tl(source2, source2); - /* fall through to DIV */ -#endif - case OPC_RISC_DIV: - /* Handle by altering args to tcg_gen_div to produce req'd results: - * For overflow: want source1 in source1 and 1 in source2 - * For div by zero: want -1 in source1 and 1 in source2 -> -1 result */ - cond1 = tcg_temp_new(); - cond2 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); - tcg_gen_movi_tl(resultopt1, (target_ulong)-1); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)(~0L)); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, - ((target_ulong)1) << (TARGET_LONG_BITS - 1)); - tcg_gen_and_tl(cond1, cond1, cond2); /* cond1 = overflow */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, 0); /* cond2 = div 0 */ - /* if div by zero, set source1 to -1, otherwise don't change */ - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond2, zeroreg, source1, - resultopt1); - /* if overflow or div by zero, set source2 to 1, else don't change */ - tcg_gen_or_tl(cond1, cond1, cond2); - tcg_gen_movi_tl(resultopt1, (target_ulong)1); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, - resultopt1); - tcg_gen_div_tl(source1, source1, source2); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); + tcg_gen_movi_tl(resultopt1, (target_ulong)-1); + tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1, + resultopt1); + tcg_gen_movi_tl(resultopt1, (target_ulong)1); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, + resultopt1); + tcg_gen_divu_tl(ret, source1, source2); - tcg_temp_free(cond1); - tcg_temp_free(cond2); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_DIVUW: - tcg_gen_ext32u_tl(source1, source1); - tcg_gen_ext32u_tl(source2, source2); - /* fall through to DIVU */ -#endif - case OPC_RISC_DIVU: - cond1 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); - tcg_gen_movi_tl(resultopt1, (target_ulong)-1); - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, source1, - resultopt1); - tcg_gen_movi_tl(resultopt1, (target_ulong)1); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, - resultopt1); - tcg_gen_divu_tl(source1, source1, source2); - - tcg_temp_free(cond1); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_REMW: - tcg_gen_ext32s_tl(source1, source1); - tcg_gen_ext32s_tl(source2, source2); - /* fall through to REM */ -#endif - case OPC_RISC_REM: - cond1 = tcg_temp_new(); - cond2 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_movi_tl(resultopt1, 1L); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, - (target_ulong)1 << (TARGET_LONG_BITS - 1)); - tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */ - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */ - /* if overflow or div by zero, set source2 to 1, else don't change */ - tcg_gen_or_tl(cond2, cond1, cond2); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2, - resultopt1); - tcg_gen_rem_tl(resultopt1, source1, source2); - /* if div by zero, just return the original dividend */ - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1, - source1); - - tcg_temp_free(cond1); - tcg_temp_free(cond2); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_REMUW: - tcg_gen_ext32u_tl(source1, source1); - tcg_gen_ext32u_tl(source2, source2); - /* fall through to REMU */ -#endif - case OPC_RISC_REMU: - cond1 = tcg_temp_new(); - zeroreg = tcg_const_tl(0); - resultopt1 = tcg_temp_new(); - - tcg_gen_movi_tl(resultopt1, (target_ulong)1); - tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, - resultopt1); - tcg_gen_remu_tl(resultopt1, source1, source2); - /* if div by zero, just return the original dividend */ - tcg_gen_movcond_tl(TCG_COND_EQ, source1, cond1, zeroreg, resultopt1, - source1); - - tcg_temp_free(cond1); - tcg_temp_free(zeroreg); - tcg_temp_free(resultopt1); - break; - default: - gen_exception_illegal(ctx); - return; - } - - if (opc & 0x8) { /* sign extend for W instructions */ - tcg_gen_ext32s_tl(source1, source1); - } - - gen_set_gpr(rd, source1); - tcg_temp_free(source1); - tcg_temp_free(source2); + tcg_temp_free(cond1); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); } -static void gen_arith_imm(DisasContext *ctx, uint32_t opc, int rd, - int rs1, target_long imm) +static void gen_rem(TCGv ret, TCGv source1, TCGv source2) { - TCGv source1 = tcg_temp_new(); - int shift_len = TARGET_LONG_BITS; - int shift_a; + TCGv cond1, cond2, zeroreg, resultopt1; - gen_get_gpr(source1, rs1); + cond1 = tcg_temp_new(); + cond2 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); - switch (opc) { - case OPC_RISC_ADDI: -#if defined(TARGET_RISCV64) - case OPC_RISC_ADDIW: -#endif - tcg_gen_addi_tl(source1, source1, imm); - break; - case OPC_RISC_SLTI: - tcg_gen_setcondi_tl(TCG_COND_LT, source1, source1, imm); - break; - case OPC_RISC_SLTIU: - tcg_gen_setcondi_tl(TCG_COND_LTU, source1, source1, imm); - break; - case OPC_RISC_XORI: - tcg_gen_xori_tl(source1, source1, imm); - break; - case OPC_RISC_ORI: - tcg_gen_ori_tl(source1, source1, imm); - break; - case OPC_RISC_ANDI: - tcg_gen_andi_tl(source1, source1, imm); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SLLIW: - shift_len = 32; - /* FALLTHRU */ -#endif - case OPC_RISC_SLLI: - if (imm >= shift_len) { - goto do_illegal; - } - tcg_gen_shli_tl(source1, source1, imm); - break; -#if defined(TARGET_RISCV64) - case OPC_RISC_SHIFT_RIGHT_IW: - shift_len = 32; - /* FALLTHRU */ -#endif - case OPC_RISC_SHIFT_RIGHT_I: - /* differentiate on IMM */ - shift_a = imm & 0x400; - imm &= 0x3ff; - if (imm >= shift_len) { - goto do_illegal; - } - if (imm != 0) { - if (shift_a) { - /* SRAI[W] */ - tcg_gen_sextract_tl(source1, source1, imm, shift_len - imm); - } else { - /* SRLI[W] */ - tcg_gen_extract_tl(source1, source1, imm, shift_len - imm); - } - /* No further sign-extension needed for W instructions. */ - opc &= ~0x8; - } - break; - default: - do_illegal: - gen_exception_illegal(ctx); - return; - } + tcg_gen_movi_tl(resultopt1, 1L); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond2, source2, (target_ulong)-1); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source1, + (target_ulong)1 << (TARGET_LONG_BITS - 1)); + tcg_gen_and_tl(cond2, cond1, cond2); /* cond1 = overflow */ + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); /* cond2 = div 0 */ + /* if overflow or div by zero, set source2 to 1, else don't change */ + tcg_gen_or_tl(cond2, cond1, cond2); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond2, zeroreg, source2, + resultopt1); + tcg_gen_rem_tl(resultopt1, source1, source2); + /* if div by zero, just return the original dividend */ + tcg_gen_movcond_tl(TCG_COND_EQ, ret, cond1, zeroreg, resultopt1, + source1); - if (opc & 0x8) { /* sign-extend for W instructions */ - tcg_gen_ext32s_tl(source1, source1); - } - - gen_set_gpr(rd, source1); - tcg_temp_free(source1); + tcg_temp_free(cond1); + tcg_temp_free(cond2); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); } -static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, - target_ulong imm) +static void gen_remu(TCGv ret, TCGv source1, TCGv source2) +{ + TCGv cond1, zeroreg, resultopt1; + cond1 = tcg_temp_new(); + zeroreg = tcg_const_tl(0); + resultopt1 = tcg_temp_new(); + + tcg_gen_movi_tl(resultopt1, (target_ulong)1); + tcg_gen_setcondi_tl(TCG_COND_EQ, cond1, source2, 0); + tcg_gen_movcond_tl(TCG_COND_EQ, source2, cond1, zeroreg, source2, + resultopt1); + tcg_gen_remu_tl(resultopt1, source1, source2); + /* if div by zero, just return the original dividend */ + tcg_gen_movcond_tl(TCG_COND_EQ, ret, cond1, zeroreg, resultopt1, + source1); + + tcg_temp_free(cond1); + tcg_temp_free(zeroreg); + tcg_temp_free(resultopt1); +} + +static void gen_jal(DisasContext *ctx, int rd, target_ulong imm) { target_ulong next_pc; /* check misaligned: */ next_pc = ctx->base.pc_next + imm; - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { if ((next_pc & 0x3) != 0) { gen_exception_inst_addr_mis(ctx); return; @@ -526,92 +316,8 @@ static void gen_jal(CPURISCVState *env, DisasContext *ctx, int rd, ctx->base.is_jmp = DISAS_NORETURN; } -static void gen_jalr(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, target_long imm) -{ - /* no chaining with JALR */ - TCGLabel *misaligned = NULL; - TCGv t0 = tcg_temp_new(); - - switch (opc) { - case OPC_RISC_JALR: - gen_get_gpr(cpu_pc, rs1); - tcg_gen_addi_tl(cpu_pc, cpu_pc, imm); - tcg_gen_andi_tl(cpu_pc, cpu_pc, (target_ulong)-2); - - if (!riscv_has_ext(env, RVC)) { - misaligned = gen_new_label(); - tcg_gen_andi_tl(t0, cpu_pc, 0x2); - tcg_gen_brcondi_tl(TCG_COND_NE, t0, 0x0, misaligned); - } - - if (rd != 0) { - tcg_gen_movi_tl(cpu_gpr[rd], ctx->pc_succ_insn); - } - tcg_gen_exit_tb(NULL, 0); - - if (misaligned) { - gen_set_label(misaligned); - gen_exception_inst_addr_mis(ctx); - } - ctx->base.is_jmp = DISAS_NORETURN; - break; - - default: - gen_exception_illegal(ctx); - break; - } - tcg_temp_free(t0); -} - -static void gen_branch(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rs1, int rs2, target_long bimm) -{ - TCGLabel *l = gen_new_label(); - TCGv source1, source2; - source1 = tcg_temp_new(); - source2 = tcg_temp_new(); - gen_get_gpr(source1, rs1); - gen_get_gpr(source2, rs2); - - switch (opc) { - case OPC_RISC_BEQ: - tcg_gen_brcond_tl(TCG_COND_EQ, source1, source2, l); - break; - case OPC_RISC_BNE: - tcg_gen_brcond_tl(TCG_COND_NE, source1, source2, l); - break; - case OPC_RISC_BLT: - tcg_gen_brcond_tl(TCG_COND_LT, source1, source2, l); - break; - case OPC_RISC_BGE: - tcg_gen_brcond_tl(TCG_COND_GE, source1, source2, l); - break; - case OPC_RISC_BLTU: - tcg_gen_brcond_tl(TCG_COND_LTU, source1, source2, l); - break; - case OPC_RISC_BGEU: - tcg_gen_brcond_tl(TCG_COND_GEU, source1, source2, l); - break; - default: - gen_exception_illegal(ctx); - return; - } - tcg_temp_free(source1); - tcg_temp_free(source2); - - gen_goto_tb(ctx, 1, ctx->pc_succ_insn); - gen_set_label(l); /* branch taken */ - if (!riscv_has_ext(env, RVC) && ((ctx->base.pc_next + bimm) & 0x3)) { - /* misaligned */ - gen_exception_inst_addr_mis(ctx); - } else { - gen_goto_tb(ctx, 0, ctx->base.pc_next + bimm); - } - ctx->base.is_jmp = DISAS_NORETURN; -} - -static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, +#ifdef TARGET_RISCV64 +static void gen_load_c(DisasContext *ctx, uint32_t opc, int rd, int rs1, target_long imm) { TCGv t0 = tcg_temp_new(); @@ -631,7 +337,7 @@ static void gen_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, tcg_temp_free(t1); } -static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2, +static void gen_store_c(DisasContext *ctx, uint32_t opc, int rs1, int rs2, target_long imm) { TCGv t0 = tcg_temp_new(); @@ -650,13 +356,40 @@ static void gen_store(DisasContext *ctx, uint32_t opc, int rs1, int rs2, tcg_temp_free(t0); tcg_temp_free(dat); } +#endif +#ifndef CONFIG_USER_ONLY +/* The states of mstatus_fs are: + * 0 = disabled, 1 = initial, 2 = clean, 3 = dirty + * We will have already diagnosed disabled state, + * and need to turn initial/clean into dirty. + */ +static void mark_fs_dirty(DisasContext *ctx) +{ + TCGv tmp; + if (ctx->mstatus_fs == MSTATUS_FS) { + return; + } + /* Remember the state change for the rest of the TB. */ + ctx->mstatus_fs = MSTATUS_FS; + + tmp = tcg_temp_new(); + tcg_gen_ld_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_gen_ori_tl(tmp, tmp, MSTATUS_FS); + tcg_gen_st_tl(tmp, cpu_env, offsetof(CPURISCVState, mstatus)); + tcg_temp_free(tmp); +} +#else +static inline void mark_fs_dirty(DisasContext *ctx) { } +#endif + +#if !defined(TARGET_RISCV64) static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, int rs1, target_long imm) { TCGv t0; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { gen_exception_illegal(ctx); return; } @@ -667,18 +400,27 @@ static void gen_fp_load(DisasContext *ctx, uint32_t opc, int rd, switch (opc) { case OPC_RISC_FLW: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEUL); /* RISC-V requires NaN-boxing of narrower width floating point values */ tcg_gen_ori_i64(cpu_fpr[rd], cpu_fpr[rd], 0xffffffff00000000ULL); break; case OPC_RISC_FLD: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } tcg_gen_qemu_ld_i64(cpu_fpr[rd], t0, ctx->mem_idx, MO_TEQ); break; + do_illegal: default: gen_exception_illegal(ctx); break; } tcg_temp_free(t0); + + mark_fs_dirty(ctx); } static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, @@ -686,7 +428,7 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, { TCGv t0; - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { + if (ctx->mstatus_fs == 0) { gen_exception_illegal(ctx); return; } @@ -697,11 +439,18 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, switch (opc) { case OPC_RISC_FSW: + if (!has_ext(ctx, RVF)) { + goto do_illegal; + } tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEUL); break; case OPC_RISC_FSD: + if (!has_ext(ctx, RVD)) { + goto do_illegal; + } tcg_gen_qemu_st_i64(cpu_fpr[rs2], t0, ctx->mem_idx, MO_TEQ); break; + do_illegal: default: gen_exception_illegal(ctx); break; @@ -709,143 +458,7 @@ static void gen_fp_store(DisasContext *ctx, uint32_t opc, int rs1, tcg_temp_free(t0); } - -static void gen_atomic(DisasContext *ctx, uint32_t opc, - int rd, int rs1, int rs2) -{ - TCGv src1, src2, dat; - TCGLabel *l1, *l2; - TCGMemOp mop; - bool aq, rl; - - /* Extract the size of the atomic operation. */ - switch (extract32(opc, 12, 3)) { - case 2: /* 32-bit */ - mop = MO_ALIGN | MO_TESL; - break; -#if defined(TARGET_RISCV64) - case 3: /* 64-bit */ - mop = MO_ALIGN | MO_TEQ; - break; #endif - default: - gen_exception_illegal(ctx); - return; - } - rl = extract32(opc, 25, 1); - aq = extract32(opc, 26, 1); - - src1 = tcg_temp_new(); - src2 = tcg_temp_new(); - - switch (MASK_OP_ATOMIC_NO_AQ_RL_SZ(opc)) { - case OPC_RISC_LR: - /* Put addr in load_res, data in load_val. */ - gen_get_gpr(src1, rs1); - if (rl) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_STRL); - } - tcg_gen_qemu_ld_tl(load_val, src1, ctx->mem_idx, mop); - if (aq) { - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_LDAQ); - } - tcg_gen_mov_tl(load_res, src1); - gen_set_gpr(rd, load_val); - break; - - case OPC_RISC_SC: - l1 = gen_new_label(); - l2 = gen_new_label(); - dat = tcg_temp_new(); - - gen_get_gpr(src1, rs1); - tcg_gen_brcond_tl(TCG_COND_NE, load_res, src1, l1); - - gen_get_gpr(src2, rs2); - /* Note that the TCG atomic primitives are SC, - so we can ignore AQ/RL along this path. */ - tcg_gen_atomic_cmpxchg_tl(src1, load_res, load_val, src2, - ctx->mem_idx, mop); - tcg_gen_setcond_tl(TCG_COND_NE, dat, src1, load_val); - gen_set_gpr(rd, dat); - tcg_gen_br(l2); - - gen_set_label(l1); - /* Address comparion failure. However, we still need to - provide the memory barrier implied by AQ/RL. */ - tcg_gen_mb(TCG_MO_ALL + aq * TCG_BAR_LDAQ + rl * TCG_BAR_STRL); - tcg_gen_movi_tl(dat, 1); - gen_set_gpr(rd, dat); - - gen_set_label(l2); - tcg_temp_free(dat); - break; - - case OPC_RISC_AMOSWAP: - /* Note that the TCG atomic primitives are SC, - so we can ignore AQ/RL along this path. */ - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_xchg_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOADD: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_add_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOXOR: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_xor_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOAND: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_and_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOOR: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_or_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMIN: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_smin_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMAX: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_smax_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMINU: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_umin_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - case OPC_RISC_AMOMAXU: - gen_get_gpr(src1, rs1); - gen_get_gpr(src2, rs2); - tcg_gen_atomic_fetch_umax_tl(src2, src1, src2, ctx->mem_idx, mop); - gen_set_gpr(rd, src2); - break; - - default: - gen_exception_illegal(ctx); - break; - } - - tcg_temp_free(src1); - tcg_temp_free(src2); -} static void gen_set_rm(DisasContext *ctx, int rm) { @@ -860,535 +473,6 @@ static void gen_set_rm(DisasContext *ctx, int rm) tcg_temp_free_i32(t0); } -static void gen_fp_fmadd(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) -{ - switch (opc) { - case OPC_RISC_FMADD_S: - gen_set_rm(ctx, rm); - gen_helper_fmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FMADD_D: - gen_set_rm(ctx, rm); - gen_helper_fmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; - } -} - -static void gen_fp_fmsub(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) -{ - switch (opc) { - case OPC_RISC_FMSUB_S: - gen_set_rm(ctx, rm); - gen_helper_fmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FMSUB_D: - gen_set_rm(ctx, rm); - gen_helper_fmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; - } -} - -static void gen_fp_fnmsub(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) -{ - switch (opc) { - case OPC_RISC_FNMSUB_S: - gen_set_rm(ctx, rm); - gen_helper_fnmsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FNMSUB_D: - gen_set_rm(ctx, rm); - gen_helper_fnmsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; - } -} - -static void gen_fp_fnmadd(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rs3, int rm) -{ - switch (opc) { - case OPC_RISC_FNMADD_S: - gen_set_rm(ctx, rm); - gen_helper_fnmadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - case OPC_RISC_FNMADD_D: - gen_set_rm(ctx, rm); - gen_helper_fnmadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], - cpu_fpr[rs2], cpu_fpr[rs3]); - break; - default: - gen_exception_illegal(ctx); - break; - } -} - -static void gen_fp_arith(DisasContext *ctx, uint32_t opc, int rd, - int rs1, int rs2, int rm) -{ - TCGv t0 = NULL; - - if (!(ctx->flags & TB_FLAGS_FP_ENABLE)) { - goto do_illegal; - } - - switch (opc) { - case OPC_RISC_FADD_S: - gen_set_rm(ctx, rm); - gen_helper_fadd_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSUB_S: - gen_set_rm(ctx, rm); - gen_helper_fsub_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FMUL_S: - gen_set_rm(ctx, rm); - gen_helper_fmul_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FDIV_S: - gen_set_rm(ctx, rm); - gen_helper_fdiv_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSQRT_S: - gen_set_rm(ctx, rm); - gen_helper_fsqrt_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - case OPC_RISC_FSGNJ_S: - gen_fsgnj(ctx, rd, rs1, rs2, rm, INT32_MIN); - break; - - case OPC_RISC_FMIN_S: - /* also handles: OPC_RISC_FMAX_S */ - switch (rm) { - case 0x0: - gen_helper_fmin_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 0x1: - gen_helper_fmax_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - break; - - case OPC_RISC_FEQ_S: - /* also handles: OPC_RISC_FLT_S, OPC_RISC_FLE_S */ - t0 = tcg_temp_new(); - switch (rm) { - case 0x0: - gen_helper_fle_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 0x1: - gen_helper_flt_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 0x2: - gen_helper_feq_s(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - - case OPC_RISC_FCVT_W_S: - /* also OPC_RISC_FCVT_WU_S, OPC_RISC_FCVT_L_S, OPC_RISC_FCVT_LU_S */ - t0 = tcg_temp_new(); - switch (rs2) { - case 0: /* FCVT_W_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_w_s(t0, cpu_env, cpu_fpr[rs1]); - break; - case 1: /* FCVT_WU_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_wu_s(t0, cpu_env, cpu_fpr[rs1]); - break; -#if defined(TARGET_RISCV64) - case 2: /* FCVT_L_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_l_s(t0, cpu_env, cpu_fpr[rs1]); - break; - case 3: /* FCVT_LU_S */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_lu_s(t0, cpu_env, cpu_fpr[rs1]); - break; -#endif - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - - case OPC_RISC_FCVT_S_W: - /* also OPC_RISC_FCVT_S_WU, OPC_RISC_FCVT_S_L, OPC_RISC_FCVT_S_LU */ - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - switch (rs2) { - case 0: /* FCVT_S_W */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_w(cpu_fpr[rd], cpu_env, t0); - break; - case 1: /* FCVT_S_WU */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_wu(cpu_fpr[rd], cpu_env, t0); - break; -#if defined(TARGET_RISCV64) - case 2: /* FCVT_S_L */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_l(cpu_fpr[rd], cpu_env, t0); - break; - case 3: /* FCVT_S_LU */ - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_lu(cpu_fpr[rd], cpu_env, t0); - break; -#endif - default: - goto do_illegal; - } - tcg_temp_free(t0); - break; - - case OPC_RISC_FMV_X_S: - /* also OPC_RISC_FCLASS_S */ - t0 = tcg_temp_new(); - switch (rm) { - case 0: /* FMV */ -#if defined(TARGET_RISCV64) - tcg_gen_ext32s_tl(t0, cpu_fpr[rs1]); -#else - tcg_gen_extrl_i64_i32(t0, cpu_fpr[rs1]); -#endif - break; - case 1: - gen_helper_fclass_s(t0, cpu_fpr[rs1]); - break; - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - - case OPC_RISC_FMV_S_X: - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); -#if defined(TARGET_RISCV64) - tcg_gen_mov_i64(cpu_fpr[rd], t0); -#else - tcg_gen_extu_i32_i64(cpu_fpr[rd], t0); -#endif - tcg_temp_free(t0); - break; - - /* double */ - case OPC_RISC_FADD_D: - gen_set_rm(ctx, rm); - gen_helper_fadd_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSUB_D: - gen_set_rm(ctx, rm); - gen_helper_fsub_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FMUL_D: - gen_set_rm(ctx, rm); - gen_helper_fmul_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FDIV_D: - gen_set_rm(ctx, rm); - gen_helper_fdiv_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case OPC_RISC_FSQRT_D: - gen_set_rm(ctx, rm); - gen_helper_fsqrt_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - case OPC_RISC_FSGNJ_D: - gen_fsgnj(ctx, rd, rs1, rs2, rm, INT64_MIN); - break; - - case OPC_RISC_FMIN_D: - /* also OPC_RISC_FMAX_D */ - switch (rm) { - case 0: - gen_helper_fmin_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 1: - gen_helper_fmax_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - break; - - case OPC_RISC_FCVT_S_D: - switch (rs2) { - case 1: - gen_set_rm(ctx, rm); - gen_helper_fcvt_s_d(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - default: - goto do_illegal; - } - break; - - case OPC_RISC_FCVT_D_S: - switch (rs2) { - case 0: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_s(cpu_fpr[rd], cpu_env, cpu_fpr[rs1]); - break; - default: - goto do_illegal; - } - break; - - case OPC_RISC_FEQ_D: - /* also OPC_RISC_FLT_D, OPC_RISC_FLE_D */ - t0 = tcg_temp_new(); - switch (rm) { - case 0: - gen_helper_fle_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 1: - gen_helper_flt_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - case 2: - gen_helper_feq_d(t0, cpu_env, cpu_fpr[rs1], cpu_fpr[rs2]); - break; - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - - case OPC_RISC_FCVT_W_D: - /* also OPC_RISC_FCVT_WU_D, OPC_RISC_FCVT_L_D, OPC_RISC_FCVT_LU_D */ - t0 = tcg_temp_new(); - switch (rs2) { - case 0: - gen_set_rm(ctx, rm); - gen_helper_fcvt_w_d(t0, cpu_env, cpu_fpr[rs1]); - break; - case 1: - gen_set_rm(ctx, rm); - gen_helper_fcvt_wu_d(t0, cpu_env, cpu_fpr[rs1]); - break; -#if defined(TARGET_RISCV64) - case 2: - gen_set_rm(ctx, rm); - gen_helper_fcvt_l_d(t0, cpu_env, cpu_fpr[rs1]); - break; - case 3: - gen_set_rm(ctx, rm); - gen_helper_fcvt_lu_d(t0, cpu_env, cpu_fpr[rs1]); - break; -#endif - default: - goto do_illegal; - } - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - - case OPC_RISC_FCVT_D_W: - /* also OPC_RISC_FCVT_D_WU, OPC_RISC_FCVT_D_L, OPC_RISC_FCVT_D_LU */ - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - switch (rs2) { - case 0: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_w(cpu_fpr[rd], cpu_env, t0); - break; - case 1: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_wu(cpu_fpr[rd], cpu_env, t0); - break; -#if defined(TARGET_RISCV64) - case 2: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_l(cpu_fpr[rd], cpu_env, t0); - break; - case 3: - gen_set_rm(ctx, rm); - gen_helper_fcvt_d_lu(cpu_fpr[rd], cpu_env, t0); - break; -#endif - default: - goto do_illegal; - } - tcg_temp_free(t0); - break; - -#if defined(TARGET_RISCV64) - case OPC_RISC_FMV_X_D: - /* also OPC_RISC_FCLASS_D */ - switch (rm) { - case 0: /* FMV */ - gen_set_gpr(rd, cpu_fpr[rs1]); - break; - case 1: - t0 = tcg_temp_new(); - gen_helper_fclass_d(t0, cpu_fpr[rs1]); - gen_set_gpr(rd, t0); - tcg_temp_free(t0); - break; - default: - goto do_illegal; - } - break; - - case OPC_RISC_FMV_D_X: - t0 = tcg_temp_new(); - gen_get_gpr(t0, rs1); - tcg_gen_mov_tl(cpu_fpr[rd], t0); - tcg_temp_free(t0); - break; -#endif - - default: - do_illegal: - if (t0) { - tcg_temp_free(t0); - } - gen_exception_illegal(ctx); - break; - } -} - -static void gen_system(CPURISCVState *env, DisasContext *ctx, uint32_t opc, - int rd, int rs1, int csr) -{ - TCGv source1, csr_store, dest, rs1_pass, imm_rs1; - source1 = tcg_temp_new(); - csr_store = tcg_temp_new(); - dest = tcg_temp_new(); - rs1_pass = tcg_temp_new(); - imm_rs1 = tcg_temp_new(); - gen_get_gpr(source1, rs1); - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - tcg_gen_movi_tl(rs1_pass, rs1); - tcg_gen_movi_tl(csr_store, csr); /* copy into temp reg to feed to helper */ - -#ifndef CONFIG_USER_ONLY - /* Extract funct7 value and check whether it matches SFENCE.VMA */ - if ((opc == OPC_RISC_ECALL) && ((csr >> 5) == 9)) { - /* sfence.vma */ - /* TODO: handle ASID specific fences */ - gen_helper_tlb_flush(cpu_env); - return; - } -#endif - - switch (opc) { - case OPC_RISC_ECALL: - switch (csr) { - case 0x0: /* ECALL */ - /* always generates U-level ECALL, fixed in do_interrupt handler */ - generate_exception(ctx, RISCV_EXCP_U_ECALL); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; - case 0x1: /* EBREAK */ - generate_exception(ctx, RISCV_EXCP_BREAKPOINT); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; -#ifndef CONFIG_USER_ONLY - case 0x002: /* URET */ - gen_exception_illegal(ctx); - break; - case 0x102: /* SRET */ - if (riscv_has_ext(env, RVS)) { - gen_helper_sret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - } else { - gen_exception_illegal(ctx); - } - break; - case 0x202: /* HRET */ - gen_exception_illegal(ctx); - break; - case 0x302: /* MRET */ - gen_helper_mret(cpu_pc, cpu_env, cpu_pc); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; - case 0x7b2: /* DRET */ - gen_exception_illegal(ctx); - break; - case 0x105: /* WFI */ - tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - gen_helper_wfi(cpu_env); - break; - case 0x104: /* SFENCE.VM */ - gen_helper_tlb_flush(cpu_env); - break; -#endif - default: - gen_exception_illegal(ctx); - break; - } - break; - default: - tcg_gen_movi_tl(imm_rs1, rs1); - gen_io_start(); - switch (opc) { - case OPC_RISC_CSRRW: - gen_helper_csrrw(dest, cpu_env, source1, csr_store); - break; - case OPC_RISC_CSRRS: - gen_helper_csrrs(dest, cpu_env, source1, csr_store, rs1_pass); - break; - case OPC_RISC_CSRRC: - gen_helper_csrrc(dest, cpu_env, source1, csr_store, rs1_pass); - break; - case OPC_RISC_CSRRWI: - gen_helper_csrrw(dest, cpu_env, imm_rs1, csr_store); - break; - case OPC_RISC_CSRRSI: - gen_helper_csrrs(dest, cpu_env, imm_rs1, csr_store, rs1_pass); - break; - case OPC_RISC_CSRRCI: - gen_helper_csrrc(dest, cpu_env, imm_rs1, csr_store, rs1_pass); - break; - default: - gen_exception_illegal(ctx); - return; - } - gen_io_end(); - gen_set_gpr(rd, dest); - /* end tb since we may be changing priv modes, to get mmu_index right */ - tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - tcg_gen_exit_tb(NULL, 0); /* no chaining */ - ctx->base.is_jmp = DISAS_NORETURN; - break; - } - tcg_temp_free(source1); - tcg_temp_free(csr_store); - tcg_temp_free(dest); - tcg_temp_free(rs1_pass); - tcg_temp_free(imm_rs1); -} - static void decode_RV32_64C0(DisasContext *ctx) { uint8_t funct3 = extract32(ctx->opcode, 13, 3); @@ -1396,31 +480,10 @@ static void decode_RV32_64C0(DisasContext *ctx) uint8_t rs1s = GET_C_RS1S(ctx->opcode); switch (funct3) { - case 0: - /* illegal */ - if (ctx->opcode == 0) { - gen_exception_illegal(ctx); - } else { - /* C.ADDI4SPN -> addi rd', x2, zimm[9:2]*/ - gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs2, 2, - GET_C_ADDI4SPN_IMM(ctx->opcode)); - } - break; - case 1: - /* C.FLD -> fld rd', offset[7:3](rs1')*/ - gen_fp_load(ctx, OPC_RISC_FLD, rd_rs2, rs1s, - GET_C_LD_IMM(ctx->opcode)); - /* C.LQ(RV128) */ - break; - case 2: - /* C.LW -> lw rd', offset[6:2](rs1') */ - gen_load(ctx, OPC_RISC_LW, rd_rs2, rs1s, - GET_C_LW_IMM(ctx->opcode)); - break; case 3: #if defined(TARGET_RISCV64) /* C.LD(RV64/128) -> ld rd', offset[7:3](rs1')*/ - gen_load(ctx, OPC_RISC_LD, rd_rs2, rs1s, + gen_load_c(ctx, OPC_RISC_LD, rd_rs2, rs1s, GET_C_LD_IMM(ctx->opcode)); #else /* C.FLW (RV32) -> flw rd', offset[6:2](rs1')*/ @@ -1428,25 +491,10 @@ static void decode_RV32_64C0(DisasContext *ctx) GET_C_LW_IMM(ctx->opcode)); #endif break; - case 4: - /* reserved */ - gen_exception_illegal(ctx); - break; - case 5: - /* C.FSD(RV32/64) -> fsd rs2', offset[7:3](rs1') */ - gen_fp_store(ctx, OPC_RISC_FSD, rs1s, rd_rs2, - GET_C_LD_IMM(ctx->opcode)); - /* C.SQ (RV128) */ - break; - case 6: - /* C.SW -> sw rs2', offset[6:2](rs1')*/ - gen_store(ctx, OPC_RISC_SW, rs1s, rd_rs2, - GET_C_LW_IMM(ctx->opcode)); - break; case 7: #if defined(TARGET_RISCV64) /* C.SD (RV64/128) -> sd rs2', offset[7:3](rs1')*/ - gen_store(ctx, OPC_RISC_SD, rs1s, rd_rs2, + gen_store_c(ctx, OPC_RISC_SD, rs1s, rd_rs2, GET_C_LD_IMM(ctx->opcode)); #else /* C.FSW (RV32) -> fsw rs2', offset[6:2](rs1')*/ @@ -1457,199 +505,7 @@ static void decode_RV32_64C0(DisasContext *ctx) } } -static void decode_RV32_64C1(CPURISCVState *env, DisasContext *ctx) -{ - uint8_t funct3 = extract32(ctx->opcode, 13, 3); - uint8_t rd_rs1 = GET_C_RS1(ctx->opcode); - uint8_t rs1s, rs2s; - uint8_t funct2; - - switch (funct3) { - case 0: - /* C.ADDI -> addi rd, rd, nzimm[5:0] */ - gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, rd_rs1, - GET_C_IMM(ctx->opcode)); - break; - case 1: -#if defined(TARGET_RISCV64) - /* C.ADDIW (RV64/128) -> addiw rd, rd, imm[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_ADDIW, rd_rs1, rd_rs1, - GET_C_IMM(ctx->opcode)); -#else - /* C.JAL(RV32) -> jal x1, offset[11:1] */ - gen_jal(env, ctx, 1, GET_C_J_IMM(ctx->opcode)); -#endif - break; - case 2: - /* C.LI -> addi rd, x0, imm[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_ADDI, rd_rs1, 0, GET_C_IMM(ctx->opcode)); - break; - case 3: - if (rd_rs1 == 2) { - /* C.ADDI16SP -> addi x2, x2, nzimm[9:4]*/ - gen_arith_imm(ctx, OPC_RISC_ADDI, 2, 2, - GET_C_ADDI16SP_IMM(ctx->opcode)); - } else if (rd_rs1 != 0) { - /* C.LUI (rs1/rd =/= {0,2}) -> lui rd, nzimm[17:12]*/ - tcg_gen_movi_tl(cpu_gpr[rd_rs1], - GET_C_IMM(ctx->opcode) << 12); - } - break; - case 4: - funct2 = extract32(ctx->opcode, 10, 2); - rs1s = GET_C_RS1S(ctx->opcode); - switch (funct2) { - case 0: /* C.SRLI(RV32) -> srli rd', rd', shamt[5:0] */ - gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s, - GET_C_ZIMM(ctx->opcode)); - /* C.SRLI64(RV128) */ - break; - case 1: - /* C.SRAI -> srai rd', rd', shamt[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_SHIFT_RIGHT_I, rs1s, rs1s, - GET_C_ZIMM(ctx->opcode) | 0x400); - /* C.SRAI64(RV128) */ - break; - case 2: - /* C.ANDI -> andi rd', rd', imm[5:0]*/ - gen_arith_imm(ctx, OPC_RISC_ANDI, rs1s, rs1s, - GET_C_IMM(ctx->opcode)); - break; - case 3: - funct2 = extract32(ctx->opcode, 5, 2); - rs2s = GET_C_RS2S(ctx->opcode); - switch (funct2) { - case 0: - /* C.SUB -> sub rd', rd', rs2' */ - if (extract32(ctx->opcode, 12, 1) == 0) { - gen_arith(ctx, OPC_RISC_SUB, rs1s, rs1s, rs2s); - } -#if defined(TARGET_RISCV64) - else { - gen_arith(ctx, OPC_RISC_SUBW, rs1s, rs1s, rs2s); - } -#endif - break; - case 1: - /* C.XOR -> xor rs1', rs1', rs2' */ - if (extract32(ctx->opcode, 12, 1) == 0) { - gen_arith(ctx, OPC_RISC_XOR, rs1s, rs1s, rs2s); - } -#if defined(TARGET_RISCV64) - else { - /* C.ADDW (RV64/128) */ - gen_arith(ctx, OPC_RISC_ADDW, rs1s, rs1s, rs2s); - } -#endif - break; - case 2: - /* C.OR -> or rs1', rs1', rs2' */ - gen_arith(ctx, OPC_RISC_OR, rs1s, rs1s, rs2s); - break; - case 3: - /* C.AND -> and rs1', rs1', rs2' */ - gen_arith(ctx, OPC_RISC_AND, rs1s, rs1s, rs2s); - break; - } - break; - } - break; - case 5: - /* C.J -> jal x0, offset[11:1]*/ - gen_jal(env, ctx, 0, GET_C_J_IMM(ctx->opcode)); - break; - case 6: - /* C.BEQZ -> beq rs1', x0, offset[8:1]*/ - rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BEQ, rs1s, 0, GET_C_B_IMM(ctx->opcode)); - break; - case 7: - /* C.BNEZ -> bne rs1', x0, offset[8:1]*/ - rs1s = GET_C_RS1S(ctx->opcode); - gen_branch(env, ctx, OPC_RISC_BNE, rs1s, 0, GET_C_B_IMM(ctx->opcode)); - break; - } -} - -static void decode_RV32_64C2(CPURISCVState *env, DisasContext *ctx) -{ - uint8_t rd, rs2; - uint8_t funct3 = extract32(ctx->opcode, 13, 3); - - - rd = GET_RD(ctx->opcode); - - switch (funct3) { - case 0: /* C.SLLI -> slli rd, rd, shamt[5:0] - C.SLLI64 -> */ - gen_arith_imm(ctx, OPC_RISC_SLLI, rd, rd, GET_C_ZIMM(ctx->opcode)); - break; - case 1: /* C.FLDSP(RV32/64DC) -> fld rd, offset[8:3](x2) */ - gen_fp_load(ctx, OPC_RISC_FLD, rd, 2, GET_C_LDSP_IMM(ctx->opcode)); - break; - case 2: /* C.LWSP -> lw rd, offset[7:2](x2) */ - gen_load(ctx, OPC_RISC_LW, rd, 2, GET_C_LWSP_IMM(ctx->opcode)); - break; - case 3: -#if defined(TARGET_RISCV64) - /* C.LDSP(RVC64) -> ld rd, offset[8:3](x2) */ - gen_load(ctx, OPC_RISC_LD, rd, 2, GET_C_LDSP_IMM(ctx->opcode)); -#else - /* C.FLWSP(RV32FC) -> flw rd, offset[7:2](x2) */ - gen_fp_load(ctx, OPC_RISC_FLW, rd, 2, GET_C_LWSP_IMM(ctx->opcode)); -#endif - break; - case 4: - rs2 = GET_C_RS2(ctx->opcode); - - if (extract32(ctx->opcode, 12, 1) == 0) { - if (rs2 == 0) { - /* C.JR -> jalr x0, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 0, rd, 0); - } else { - /* C.MV -> add rd, x0, rs2 */ - gen_arith(ctx, OPC_RISC_ADD, rd, 0, rs2); - } - } else { - if (rd == 0) { - /* C.EBREAK -> ebreak*/ - gen_system(env, ctx, OPC_RISC_ECALL, 0, 0, 0x1); - } else { - if (rs2 == 0) { - /* C.JALR -> jalr x1, rs1, 0*/ - gen_jalr(env, ctx, OPC_RISC_JALR, 1, rd, 0); - } else { - /* C.ADD -> add rd, rd, rs2 */ - gen_arith(ctx, OPC_RISC_ADD, rd, rd, rs2); - } - } - } - break; - case 5: - /* C.FSDSP -> fsd rs2, offset[8:3](x2)*/ - gen_fp_store(ctx, OPC_RISC_FSD, 2, GET_C_RS2(ctx->opcode), - GET_C_SDSP_IMM(ctx->opcode)); - /* C.SQSP */ - break; - case 6: /* C.SWSP -> sw rs2, offset[7:2](x2)*/ - gen_store(ctx, OPC_RISC_SW, 2, GET_C_RS2(ctx->opcode), - GET_C_SWSP_IMM(ctx->opcode)); - break; - case 7: -#if defined(TARGET_RISCV64) - /* C.SDSP(Rv64/128) -> sd rs2, offset[8:3](x2)*/ - gen_store(ctx, OPC_RISC_SD, 2, GET_C_RS2(ctx->opcode), - GET_C_SDSP_IMM(ctx->opcode)); -#else - /* C.FSWSP(RV32) -> fsw rs2, offset[7:2](x2) */ - gen_fp_store(ctx, OPC_RISC_FSW, 2, GET_C_RS2(ctx->opcode), - GET_C_SWSP_IMM(ctx->opcode)); -#endif - break; - } -} - -static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) +static void decode_RV32_64C(DisasContext *ctx) { uint8_t op = extract32(ctx->opcode, 0, 2); @@ -1657,161 +513,196 @@ static void decode_RV32_64C(CPURISCVState *env, DisasContext *ctx) case 0: decode_RV32_64C0(ctx); break; - case 1: - decode_RV32_64C1(env, ctx); - break; - case 2: - decode_RV32_64C2(env, ctx); - break; } } -static void decode_RV32_64G(CPURISCVState *env, DisasContext *ctx) +#define EX_SH(amount) \ + static int ex_shift_##amount(int imm) \ + { \ + return imm << amount; \ + } +EX_SH(1) +EX_SH(2) +EX_SH(3) +EX_SH(4) +EX_SH(12) + +#define REQUIRE_EXT(ctx, ext) do { \ + if (!has_ext(ctx, ext)) { \ + return false; \ + } \ +} while (0) + +static int ex_rvc_register(int reg) { - int rs1; - int rs2; - int rd; - uint32_t op; - target_long imm; - - /* We do not do misaligned address check here: the address should never be - * misaligned at this point. Instructions that set PC must do the check, - * since epc must be the address of the instruction that caused us to - * perform the misaligned instruction fetch */ - - op = MASK_OP_MAJOR(ctx->opcode); - rs1 = GET_RS1(ctx->opcode); - rs2 = GET_RS2(ctx->opcode); - rd = GET_RD(ctx->opcode); - imm = GET_IMM(ctx->opcode); - - switch (op) { - case OPC_RISC_LUI: - if (rd == 0) { - break; /* NOP */ - } - tcg_gen_movi_tl(cpu_gpr[rd], sextract64(ctx->opcode, 12, 20) << 12); - break; - case OPC_RISC_AUIPC: - if (rd == 0) { - break; /* NOP */ - } - tcg_gen_movi_tl(cpu_gpr[rd], (sextract64(ctx->opcode, 12, 20) << 12) + - ctx->base.pc_next); - break; - case OPC_RISC_JAL: - imm = GET_JAL_IMM(ctx->opcode); - gen_jal(env, ctx, rd, imm); - break; - case OPC_RISC_JALR: - gen_jalr(env, ctx, MASK_OP_JALR(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_BRANCH: - gen_branch(env, ctx, MASK_OP_BRANCH(ctx->opcode), rs1, rs2, - GET_B_IMM(ctx->opcode)); - break; - case OPC_RISC_LOAD: - gen_load(ctx, MASK_OP_LOAD(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_STORE: - gen_store(ctx, MASK_OP_STORE(ctx->opcode), rs1, rs2, - GET_STORE_IMM(ctx->opcode)); - break; - case OPC_RISC_ARITH_IMM: -#if defined(TARGET_RISCV64) - case OPC_RISC_ARITH_IMM_W: -#endif - if (rd == 0) { - break; /* NOP */ - } - gen_arith_imm(ctx, MASK_OP_ARITH_IMM(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_ARITH: -#if defined(TARGET_RISCV64) - case OPC_RISC_ARITH_W: -#endif - if (rd == 0) { - break; /* NOP */ - } - gen_arith(ctx, MASK_OP_ARITH(ctx->opcode), rd, rs1, rs2); - break; - case OPC_RISC_FP_LOAD: - gen_fp_load(ctx, MASK_OP_FP_LOAD(ctx->opcode), rd, rs1, imm); - break; - case OPC_RISC_FP_STORE: - gen_fp_store(ctx, MASK_OP_FP_STORE(ctx->opcode), rs1, rs2, - GET_STORE_IMM(ctx->opcode)); - break; - case OPC_RISC_ATOMIC: - gen_atomic(ctx, MASK_OP_ATOMIC(ctx->opcode), rd, rs1, rs2); - break; - case OPC_RISC_FMADD: - gen_fp_fmadd(ctx, MASK_OP_FP_FMADD(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FMSUB: - gen_fp_fmsub(ctx, MASK_OP_FP_FMSUB(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FNMSUB: - gen_fp_fnmsub(ctx, MASK_OP_FP_FNMSUB(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FNMADD: - gen_fp_fnmadd(ctx, MASK_OP_FP_FNMADD(ctx->opcode), rd, rs1, rs2, - GET_RS3(ctx->opcode), GET_RM(ctx->opcode)); - break; - case OPC_RISC_FP_ARITH: - gen_fp_arith(ctx, MASK_OP_FP_ARITH(ctx->opcode), rd, rs1, rs2, - GET_RM(ctx->opcode)); - break; - case OPC_RISC_FENCE: -#ifndef CONFIG_USER_ONLY - if (ctx->opcode & 0x1000) { - /* FENCE_I is a no-op in QEMU, - * however we need to end the translation block */ - tcg_gen_movi_tl(cpu_pc, ctx->pc_succ_insn); - tcg_gen_exit_tb(NULL, 0); - ctx->base.is_jmp = DISAS_NORETURN; - } else { - /* FENCE is a full memory barrier. */ - tcg_gen_mb(TCG_MO_ALL | TCG_BAR_SC); - } -#endif - break; - case OPC_RISC_SYSTEM: - gen_system(env, ctx, MASK_OP_SYSTEM(ctx->opcode), rd, rs1, - (ctx->opcode & 0xFFF00000) >> 20); - break; - default: - gen_exception_illegal(ctx); - break; - } + return 8 + reg; } -static void decode_opc(CPURISCVState *env, DisasContext *ctx) +bool decode_insn32(DisasContext *ctx, uint32_t insn); +/* Include the auto-generated decoder for 32 bit insn */ +#include "decode_insn32.inc.c" + +static bool gen_arith_imm(DisasContext *ctx, arg_i *a, + void(*func)(TCGv, TCGv, TCGv)) +{ + TCGv source1, source2; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + tcg_gen_movi_tl(source2, a->imm); + + (*func)(source1, source1, source2); + + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +#ifdef TARGET_RISCV64 +static void gen_addw(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_add_tl(ret, arg1, arg2); + tcg_gen_ext32s_tl(ret, ret); +} + +static void gen_subw(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_sub_tl(ret, arg1, arg2); + tcg_gen_ext32s_tl(ret, ret); +} + +static void gen_mulw(TCGv ret, TCGv arg1, TCGv arg2) +{ + tcg_gen_mul_tl(ret, arg1, arg2); + tcg_gen_ext32s_tl(ret, ret); +} + +static bool gen_arith_div_w(DisasContext *ctx, arg_r *a, + void(*func)(TCGv, TCGv, TCGv)) +{ + TCGv source1, source2; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + tcg_gen_ext32s_tl(source1, source1); + tcg_gen_ext32s_tl(source2, source2); + + (*func)(source1, source1, source2); + + tcg_gen_ext32s_tl(source1, source1); + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +static bool gen_arith_div_uw(DisasContext *ctx, arg_r *a, + void(*func)(TCGv, TCGv, TCGv)) +{ + TCGv source1, source2; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + tcg_gen_ext32u_tl(source1, source1); + tcg_gen_ext32u_tl(source2, source2); + + (*func)(source1, source1, source2); + + tcg_gen_ext32s_tl(source1, source1); + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +#endif + +static bool gen_arith(DisasContext *ctx, arg_r *a, + void(*func)(TCGv, TCGv, TCGv)) +{ + TCGv source1, source2; + source1 = tcg_temp_new(); + source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + (*func)(source1, source1, source2); + + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +static bool gen_shift(DisasContext *ctx, arg_r *a, + void(*func)(TCGv, TCGv, TCGv)) +{ + TCGv source1 = tcg_temp_new(); + TCGv source2 = tcg_temp_new(); + + gen_get_gpr(source1, a->rs1); + gen_get_gpr(source2, a->rs2); + + tcg_gen_andi_tl(source2, source2, TARGET_LONG_BITS - 1); + (*func)(source1, source1, source2); + + gen_set_gpr(a->rd, source1); + tcg_temp_free(source1); + tcg_temp_free(source2); + return true; +} + +/* Include insn module translation function */ +#include "insn_trans/trans_rvi.inc.c" +#include "insn_trans/trans_rvm.inc.c" +#include "insn_trans/trans_rva.inc.c" +#include "insn_trans/trans_rvf.inc.c" +#include "insn_trans/trans_rvd.inc.c" +#include "insn_trans/trans_privileged.inc.c" + +bool decode_insn16(DisasContext *ctx, uint16_t insn); +/* auto-generated decoder*/ +#include "decode_insn16.inc.c" +#include "insn_trans/trans_rvc.inc.c" + +static void decode_opc(DisasContext *ctx) { /* check for compressed insn */ if (extract32(ctx->opcode, 0, 2) != 3) { - if (!riscv_has_ext(env, RVC)) { + if (!has_ext(ctx, RVC)) { gen_exception_illegal(ctx); } else { ctx->pc_succ_insn = ctx->base.pc_next + 2; - decode_RV32_64C(env, ctx); + if (!decode_insn16(ctx, ctx->opcode)) { + /* fall back to old decoder */ + decode_RV32_64C(ctx); + } } } else { ctx->pc_succ_insn = ctx->base.pc_next + 4; - decode_RV32_64G(env, ctx); + if (!decode_insn32(ctx, ctx->opcode)) { + gen_exception_illegal(ctx); + } } } static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) { DisasContext *ctx = container_of(dcbase, DisasContext, base); + CPURISCVState *env = cs->env_ptr; ctx->pc_succ_insn = ctx->base.pc_first; - ctx->flags = ctx->base.tb->flags; ctx->mem_idx = ctx->base.tb->flags & TB_FLAGS_MMU_MASK; + ctx->mstatus_fs = ctx->base.tb->flags & TB_FLAGS_MSTATUS_FS; + ctx->priv_ver = env->priv_ver; + ctx->misa = env->misa; ctx->frm = -1; /* unknown rounding mode */ } @@ -1842,14 +733,13 @@ static bool riscv_tr_breakpoint_check(DisasContextBase *dcbase, CPUState *cpu, return true; } - static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu->env_ptr; ctx->opcode = cpu_ldl_code(env, ctx->base.pc_next); - decode_opc(env, ctx); + decode_opc(ctx); ctx->base.pc_next = ctx->pc_succ_insn; if (ctx->base.is_jmp == DISAS_NEXT) { @@ -1868,12 +758,7 @@ static void riscv_tr_tb_stop(DisasContextBase *dcbase, CPUState *cpu) switch (ctx->base.is_jmp) { case DISAS_TOO_MANY: - tcg_gen_movi_tl(cpu_pc, ctx->base.pc_next); - if (ctx->base.singlestep_enabled) { - gen_exception_debug(); - } else { - tcg_gen_exit_tb(NULL, 0); - } + gen_goto_tb(ctx, 0, ctx->base.pc_next); break; case DISAS_NORETURN: break; diff --git a/target/s390x/Makefile.objs b/target/s390x/Makefile.objs index 22a9a9927a..68eeee3d2f 100644 --- a/target/s390x/Makefile.objs +++ b/target/s390x/Makefile.objs @@ -1,6 +1,7 @@ obj-y += cpu.o cpu_models.o cpu_features.o gdbstub.o interrupt.o helper.o obj-$(CONFIG_TCG) += translate.o cc_helper.o excp_helper.o fpu_helper.o obj-$(CONFIG_TCG) += int_helper.o mem_helper.o misc_helper.o crypto_helper.o +obj-$(CONFIG_TCG) += vec_helper.o obj-$(CONFIG_SOFTMMU) += machine.o ioinst.o arch_dump.o mmu_helper.o diag.o obj-$(CONFIG_SOFTMMU) += sigp.o obj-$(CONFIG_KVM) += kvm.o diff --git a/target/s390x/cc_helper.c b/target/s390x/cc_helper.c index 5d91e458a8..0e467bf2b6 100644 --- a/target/s390x/cc_helper.c +++ b/target/s390x/cc_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -397,6 +397,11 @@ static uint32_t cc_calc_flogr(uint64_t dst) return dst ? 2 : 0; } +static uint32_t cc_calc_lcbb(uint64_t dst) +{ + return dst == 16 ? 0 : 3; +} + static uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, uint64_t vr) { @@ -506,6 +511,9 @@ static uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op, case CC_OP_FLOGR: r = cc_calc_flogr(dst); break; + case CC_OP_LCBB: + r = cc_calc_lcbb(dst); + break; case CC_OP_NZ_F32: r = set_cc_nz_f32(dst); diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 271c5ce652..698dd9cb82 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -6,21 +6,18 @@ * Copyright (c) 2012 SUSE LINUX Products GmbH * Copyright (c) 2012 IBM Corp. * - * 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 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. + * 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 - * - * Contributions after 2012-12-11 are licensed under the terms of the - * GNU GPL, version 2 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" @@ -145,6 +142,11 @@ static void s390_cpu_full_reset(CPUState *s) env->cregs[0] = CR0_RESET; env->cregs[14] = CR14_RESET; +#if defined(CONFIG_USER_ONLY) + /* user mode should always be allowed to use the full FPU */ + env->cregs[0] |= CR0_AFP; +#endif + /* architectured initial value for Breaking-Event-Address register */ env->gbea = 1; @@ -421,16 +423,6 @@ void s390_crypto_reset(void) } } -bool s390_get_squash_mcss(void) -{ - if (object_property_get_bool(OBJECT(qdev_get_machine()), "s390-squash-mcss", - NULL)) { - return true; - } - - return false; -} - void s390_enable_css_support(S390CPU *cpu) { if (kvm_enabled()) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 2c3dd2d189..cb6d77053a 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -2,22 +2,20 @@ * S/390 virtual CPU header * * Copyright (c) 2009 Ulrich Hecht + * Copyright IBM Corp. 2012, 2018 * - * 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 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * Contributions after 2012-10-29 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - * - * You should have received a copy of the GNU (Lesser) General Public - * License along with this library; if not, see . + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . */ #ifndef S390X_CPU_H @@ -34,6 +32,10 @@ #define CPUArchState struct CPUS390XState #include "exec/cpu-defs.h" + +/* The z/Architecture has a strong memory model with some store-after-load re-ordering */ +#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) + #define TARGET_PAGE_BITS 12 #define TARGET_PHYS_ADDR_SPACE_BITS 64 @@ -68,6 +70,8 @@ struct CPUS390XState { uint32_t aregs[16]; /* access registers */ uint8_t riccb[64]; /* runtime instrumentation control */ uint64_t gscb[4]; /* guarded storage control */ + uint64_t etoken; /* etoken */ + uint64_t etoken_extension; /* etoken extension */ /* Fields up to this point are not cleared by initial CPU reset */ struct {} start_initial_reset_fields; @@ -252,6 +256,8 @@ extern const struct VMStateDescription vmstate_s390_cpu; /* PSW defines */ #undef PSW_MASK_PER +#undef PSW_MASK_UNUSED_2 +#undef PSW_MASK_UNUSED_3 #undef PSW_MASK_DAT #undef PSW_MASK_IO #undef PSW_MASK_EXT @@ -270,6 +276,8 @@ extern const struct VMStateDescription vmstate_s390_cpu; #undef PSW_MASK_ESA_ADDR #define PSW_MASK_PER 0x4000000000000000ULL +#define PSW_MASK_UNUSED_2 0x2000000000000000ULL +#define PSW_MASK_UNUSED_3 0x1000000000000000ULL #define PSW_MASK_DAT 0x0400000000000000ULL #define PSW_MASK_IO 0x0200000000000000ULL #define PSW_MASK_EXT 0x0100000000000000ULL @@ -315,10 +323,16 @@ extern const struct VMStateDescription vmstate_s390_cpu; #define FLAG_MASK_PSW (FLAG_MASK_PER | FLAG_MASK_DAT | FLAG_MASK_PSTATE \ | FLAG_MASK_ASC | FLAG_MASK_64 | FLAG_MASK_32) +/* we'll use some unused PSW positions to store CR flags in tb flags */ +#define FLAG_MASK_AFP (PSW_MASK_UNUSED_2 >> FLAG_MASK_PSW_SHIFT) +#define FLAG_MASK_VECTOR (PSW_MASK_UNUSED_3 >> FLAG_MASK_PSW_SHIFT) + /* Control register 0 bits */ #define CR0_LOWPROT 0x0000000010000000ULL #define CR0_SECONDARY 0x0000000004000000ULL #define CR0_EDAT 0x0000000000800000ULL +#define CR0_AFP 0x0000000000040000ULL +#define CR0_VECTOR 0x0000000000020000ULL #define CR0_EMERGENCY_SIGNAL_SC 0x0000000000004000ULL #define CR0_EXTERNAL_CALL_SC 0x0000000000002000ULL #define CR0_CKC_SC 0x0000000000000800ULL @@ -360,6 +374,12 @@ static inline void cpu_get_tb_cpu_state(CPUS390XState* env, target_ulong *pc, *pc = env->psw.addr; *cs_base = env->ex_value; *flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; + if (env->cregs[0] & CR0_AFP) { + *flags |= FLAG_MASK_AFP; + } + if (env->cregs[0] & CR0_VECTOR) { + *flags |= FLAG_MASK_VECTOR; + } } /* PER bits from control register 9 */ @@ -713,7 +733,6 @@ static inline void s390_do_cpu_load_normal(CPUState *cs, run_on_cpu_data arg) /* cpu.c */ void s390_crypto_reset(void); -bool s390_get_squash_mcss(void); int s390_set_memory_limit(uint64_t new_limit, uint64_t *hw_limit); void s390_cmma_reset(void); void s390_enable_css_support(S390CPU *cpu); diff --git a/target/s390x/cpu_features.c b/target/s390x/cpu_features.c index 3b9e2745e9..1843c84aaa 100644 --- a/target/s390x/cpu_features.c +++ b/target/s390x/cpu_features.c @@ -1,7 +1,7 @@ /* * CPU features/facilities for s390x * - * Copyright 2016 IBM Corp. + * Copyright IBM Corp. 2016, 2018 * * Author(s): David Hildenbrand * @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "cpu_features.h" -#include "gen-features.h" #define FEAT_INIT(_name, _type, _bit, _desc) \ { \ @@ -40,8 +39,10 @@ static const S390FeatDef s390_features[] = { FEAT_INIT("srs", S390_FEAT_TYPE_STFL, 9, "Sense-running-status facility"), FEAT_INIT("csske", S390_FEAT_TYPE_STFL, 10, "Conditional-SSKE facility"), FEAT_INIT("ctop", S390_FEAT_TYPE_STFL, 11, "Configuration-topology facility"), + FEAT_INIT("apqci", S390_FEAT_TYPE_STFL, 12, "Query AP Configuration Information facility"), FEAT_INIT("ipter", S390_FEAT_TYPE_STFL, 13, "IPTE-range facility"), FEAT_INIT("nonqks", S390_FEAT_TYPE_STFL, 14, "Nonquiescing key-setting facility"), + FEAT_INIT("apft", S390_FEAT_TYPE_STFL, 15, "AP Facilities Test facility"), FEAT_INIT("etf2", S390_FEAT_TYPE_STFL, 16, "Extended-translation facility 2"), FEAT_INIT("msa-base", S390_FEAT_TYPE_STFL, 17, "Message-security-assist facility (excluding subfunctions)"), FEAT_INIT("ldisp", S390_FEAT_TYPE_STFL, 18, "Long-displacement facility"), @@ -106,6 +107,7 @@ static const S390FeatDef s390_features[] = { FEAT_INIT("irbm", S390_FEAT_TYPE_STFL, 145, "Insert-reference-bits-multiple facility"), FEAT_INIT("msa8-base", S390_FEAT_TYPE_STFL, 146, "Message-security-assist-extension-8 facility (excluding subfunctions)"), FEAT_INIT("cmmnt", S390_FEAT_TYPE_STFL, 147, "CMM: ESSA-enhancement (no translate) facility"), + FEAT_INIT("etoken", S390_FEAT_TYPE_STFL, 156, "Etoken facility"), /* SCLP SCCB Byte 80 - 98 (bit numbers relative to byte-80) */ FEAT_INIT("gsls", S390_FEAT_TYPE_SCLP_CONF_CHAR, 40, "SIE: Guest-storage-limit-suppression facility"), @@ -129,6 +131,7 @@ static const S390FeatDef s390_features[] = { FEAT_INIT_MISC("dateh2", "DAT-enhancement facility 2"), FEAT_INIT_MISC("cmm", "Collaborative-memory-management facility"), + FEAT_INIT_MISC("ap", "AP instructions installed"), FEAT_INIT("plo-cl", S390_FEAT_TYPE_PLO, 0, "PLO Compare and load (32 bit in general registers)"), FEAT_INIT("plo-clg", S390_FEAT_TYPE_PLO, 1, "PLO Compare and load (64 bit in parameter list)"), @@ -453,7 +456,6 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("plo", PLO, "Perform-locked-operation facility"), FEAT_GROUP_INIT("tods", TOD_CLOCK_STEERING, "Tod-clock-steering facility"), FEAT_GROUP_INIT("gen13ptff", GEN13_PTFF, "PTFF enhancements introduced with z13"), - FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), FEAT_GROUP_INIT("msa", MSA, "Message-security-assist facility"), FEAT_GROUP_INIT("msa1", MSA_EXT_1, "Message-security-assist-extension 1 facility"), FEAT_GROUP_INIT("msa2", MSA_EXT_2, "Message-security-assist-extension 2 facility"), @@ -463,6 +465,7 @@ static S390FeatGroupDef s390_feature_groups[] = { FEAT_GROUP_INIT("msa6", MSA_EXT_6, "Message-security-assist-extension 6 facility"), FEAT_GROUP_INIT("msa7", MSA_EXT_7, "Message-security-assist-extension 7 facility"), FEAT_GROUP_INIT("msa8", MSA_EXT_8, "Message-security-assist-extension 8 facility"), + FEAT_GROUP_INIT("mepochptff", MULTIPLE_EPOCH_PTFF, "PTFF enhancements introduced with Multiple-epoch facility"), }; const S390FeatGroupDef *s390_feat_group_def(S390FeatGroup group) diff --git a/target/s390x/cpu_features.h b/target/s390x/cpu_features.h index 968b12fdfe..effe790271 100644 --- a/target/s390x/cpu_features.h +++ b/target/s390x/cpu_features.h @@ -16,6 +16,7 @@ #include "qemu/bitmap.h" #include "cpu_features_def.h" +#include "gen-features.h" /* CPU features are announced via different ways */ typedef enum { @@ -64,24 +65,6 @@ void s390_add_from_feat_block(S390FeatBitmap features, S390FeatType type, void s390_feat_bitmap_to_ascii(const S390FeatBitmap features, void *opaque, void (*fn)(const char *name, void *opaque)); -/* static groups that will never change */ -typedef enum { - S390_FEAT_GROUP_PLO, - S390_FEAT_GROUP_TOD_CLOCK_STEERING, - S390_FEAT_GROUP_GEN13_PTFF_ENH, - S390_FEAT_GROUP_MSA, - S390_FEAT_GROUP_MSA_EXT_1, - S390_FEAT_GROUP_MSA_EXT_2, - S390_FEAT_GROUP_MSA_EXT_3, - S390_FEAT_GROUP_MSA_EXT_4, - S390_FEAT_GROUP_MSA_EXT_5, - S390_FEAT_GROUP_MSA_EXT_6, - S390_FEAT_GROUP_MSA_EXT_7, - S390_FEAT_GROUP_MSA_EXT_8, - S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF, - S390_FEAT_GROUP_MAX, -} S390FeatGroup; - /* Definition of a CPU feature group */ typedef struct { const char *name; /* name exposed to the user */ diff --git a/target/s390x/cpu_features_def.h b/target/s390x/cpu_features_def.h index 7c5915c7b2..5fc7e7bf01 100644 --- a/target/s390x/cpu_features_def.h +++ b/target/s390x/cpu_features_def.h @@ -1,7 +1,7 @@ /* * CPU features/facilities for s390 * - * Copyright 2016 IBM Corp. + * Copyright IBM Corp. 2016, 2018 * * Author(s): Michael Mueller * David Hildenbrand @@ -27,8 +27,10 @@ typedef enum { S390_FEAT_SENSE_RUNNING_STATUS, S390_FEAT_CONDITIONAL_SSKE, S390_FEAT_CONFIGURATION_TOPOLOGY, + S390_FEAT_AP_QUERY_CONFIG_INFO, S390_FEAT_IPTE_RANGE, S390_FEAT_NONQ_KEY_SETTING, + S390_FEAT_AP_FACILITIES_TEST, S390_FEAT_EXTENDED_TRANSLATION_2, S390_FEAT_MSA, S390_FEAT_LONG_DISPLACEMENT, @@ -93,6 +95,7 @@ typedef enum { S390_FEAT_INSERT_REFERENCE_BITS_MULT, S390_FEAT_MSA_EXT_8, S390_FEAT_CMM_NT, + S390_FEAT_ETOKEN, /* Sclp Conf Char */ S390_FEAT_SIE_GSLS, @@ -118,6 +121,7 @@ typedef enum { /* Misc */ S390_FEAT_DAT_ENH_2, S390_FEAT_CMM, + S390_FEAT_AP, /* PLO */ S390_FEAT_PLO_CL, diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 604898a882..eb125d4d0d 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -25,6 +25,7 @@ #include "sysemu/arch_init.h" #include "hw/pci/pci.h" #endif +#include "qapi/qapi-commands-target.h" #define CPUDEF_INIT(_type, _gen, _ec_ga, _mha_pow, _hmfai, _name, _desc) \ { \ @@ -79,6 +80,7 @@ static S390CPUDef s390_cpu_defs[] = { CPUDEF_INIT(0x2964, 13, 2, 47, 0x08000000U, "z13.2", "IBM z13 GA2"), CPUDEF_INIT(0x2965, 13, 2, 47, 0x08000000U, "z13s", "IBM z13s GA1"), CPUDEF_INIT(0x3906, 14, 1, 47, 0x08000000U, "z14", "IBM z14 GA1"), + CPUDEF_INIT(0x3906, 14, 2, 47, 0x08000000U, "z14.2", "IBM z14 GA2"), CPUDEF_INIT(0x3907, 14, 1, 47, 0x08000000U, "z14ZR1", "IBM z14 Model ZR1 GA1"), }; @@ -117,6 +119,30 @@ void s390_cpudef_featoff_greater(uint8_t gen, uint8_t ec_ga, S390Feat feat) } } +void s390_cpudef_group_featoff_greater(uint8_t gen, uint8_t ec_ga, + S390FeatGroup group) +{ + const S390FeatGroupDef *group_def = s390_feat_group_def(group); + S390FeatBitmap group_def_off; + int i; + + bitmap_complement(group_def_off, group_def->feat, S390_FEAT_MAX); + + for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) { + const S390CPUDef *cpu_def = &s390_cpu_defs[i]; + + if (cpu_def->gen < gen) { + continue; + } + if (cpu_def->gen == gen && cpu_def->ec_ga < ec_ga) { + continue; + } + + bitmap_and((unsigned long *)&cpu_def->default_feat, + cpu_def->default_feat, group_def_off, S390_FEAT_MAX); + } +} + uint32_t s390_get_hmfai(void) { static S390CPU *cpu; @@ -307,7 +333,10 @@ static gint s390_cpu_list_compare(gconstpointer a, gconstpointer b) const char *name_a = object_class_get_name((ObjectClass *)a); const char *name_b = object_class_get_name((ObjectClass *)b); - /* move qemu and host to the top of the list, qemu first, host second */ + /* + * Move qemu, host and max to the top of the list, qemu first, host second, + * max third. + */ if (name_a[0] == 'q') { return -1; } else if (name_b[0] == 'q') { @@ -316,6 +345,10 @@ static gint s390_cpu_list_compare(gconstpointer a, gconstpointer b) return -1; } else if (name_b[0] == 'h') { return 1; + } else if (name_a[0] == 'm') { + return -1; + } else if (name_b[0] == 'm') { + return 1; } /* keep the same order we have in our table (sorted by release date) */ @@ -425,7 +458,7 @@ static void create_cpu_model_list(ObjectClass *klass, void *opaque) *cpu_list = entry; } -CpuDefinitionInfoList *arch_query_cpu_definitions(Error **errp) +CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) { struct CpuDefinitionInfoListData list_data = { .list = NULL, @@ -559,7 +592,7 @@ static void cpu_info_from_model(CpuModelInfo *info, const S390CPUModel *model, } } -CpuModelExpansionInfo *arch_query_cpu_model_expansion(CpuModelExpansionType type, +CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, CpuModelInfo *model, Error **errp) { @@ -598,7 +631,7 @@ static void list_add_feat(const char *name, void *opaque) *last = entry; } -CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *infoa, +CpuModelCompareInfo *qmp_query_cpu_model_comparison(CpuModelInfo *infoa, CpuModelInfo *infob, Error **errp) { @@ -671,7 +704,7 @@ CpuModelCompareInfo *arch_query_cpu_model_comparison(CpuModelInfo *infoa, return compare_info; } -CpuModelBaselineInfo *arch_query_cpu_model_baseline(CpuModelInfo *infoa, +CpuModelBaselineInfo *qmp_query_cpu_model_baseline(CpuModelInfo *infoa, CpuModelInfo *infob, Error **errp) { @@ -779,6 +812,12 @@ static void check_consistency(const S390CPUModel *model) { S390_FEAT_PRNO_TRNG_QRTCR, S390_FEAT_MSA_EXT_5 }, { S390_FEAT_PRNO_TRNG, S390_FEAT_MSA_EXT_5 }, { S390_FEAT_SIE_KSS, S390_FEAT_SIE_F2 }, + { S390_FEAT_AP_QUERY_CONFIG_INFO, S390_FEAT_AP }, + { S390_FEAT_AP_FACILITIES_TEST, S390_FEAT_AP }, + { S390_FEAT_PTFF_QSIE, S390_FEAT_MULTIPLE_EPOCH }, + { S390_FEAT_PTFF_QTOUE, S390_FEAT_MULTIPLE_EPOCH }, + { S390_FEAT_PTFF_STOE, S390_FEAT_MULTIPLE_EPOCH }, + { S390_FEAT_PTFF_STOUE, S390_FEAT_MULTIPLE_EPOCH }, }; int i; @@ -1077,27 +1116,6 @@ static void s390_cpu_model_initfn(Object *obj) } } -#ifdef CONFIG_KVM -static void s390_host_cpu_model_initfn(Object *obj) -{ - S390CPU *cpu = S390_CPU(obj); - Error *err = NULL; - - if (!kvm_enabled() || !kvm_s390_cpu_models_supported()) { - return; - } - - cpu->model = g_malloc0(sizeof(*cpu->model)); - kvm_s390_get_host_cpu_model(cpu->model, &err); - if (err) { - error_report_err(err); - g_free(cpu->model); - /* fallback to unsupported cpu models */ - cpu->model = NULL; - } -} -#endif - static S390CPUDef s390_qemu_cpu_def; static S390CPUModel s390_qemu_cpu_model; @@ -1110,7 +1128,7 @@ void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, const S390CPUDef *def = s390_find_cpu_def(type, gen, ec_ga, NULL); g_assert(def); - g_assert(QTAILQ_EMPTY(&cpus)); + g_assert(QTAILQ_EMPTY_RCU(&cpus)); /* TCG emulates some features that can usually not be enabled with * the emulated machine generation. Make sure they can be enabled @@ -1136,6 +1154,31 @@ static void s390_qemu_cpu_model_initfn(Object *obj) memcpy(cpu->model, &s390_qemu_cpu_model, sizeof(*cpu->model)); } +static void s390_max_cpu_model_initfn(Object *obj) +{ + const S390CPUModel *max_model; + S390CPU *cpu = S390_CPU(obj); + Error *local_err = NULL; + + if (kvm_enabled() && !kvm_s390_cpu_models_supported()) { + /* "max" and "host" always work, even without CPU model support */ + return; + } + + max_model = get_max_cpu_model(&local_err); + if (local_err) { + /* we expect errors only under KVM, when actually querying the kernel */ + g_assert(kvm_enabled()); + error_report_err(local_err); + /* fallback to unsupported CPU models */ + return; + } + + cpu->model = g_new(S390CPUModel, 1); + /* copy the CPU model so we can modify it */ + memcpy(cpu->model, max_model, sizeof(*cpu->model)); +} + static void s390_cpu_model_finalize(Object *obj) { S390CPU *cpu = S390_CPU(obj); @@ -1209,6 +1252,20 @@ static void s390_qemu_cpu_model_class_init(ObjectClass *oc, void *data) qemu_hw_version()); } +static void s390_max_cpu_model_class_init(ObjectClass *oc, void *data) +{ + S390CPUClass *xcc = S390_CPU_CLASS(oc); + + /* + * The "max" model is neither static nor migration safe. Under KVM + * it represents the "host" model. Under TCG it represents some kind of + * "qemu" CPU model without compat handling and maybe with some additional + * CPU features that are not yet unlocked in the "qemu" model. + */ + xcc->desc = + "Enables all features supported by the accelerator in the current host"; +} + /* Generate type name for a cpu model. Caller has to free the string. */ static char *s390_cpu_type_name(const char *model_name) { @@ -1239,12 +1296,18 @@ static const TypeInfo qemu_s390_cpu_type_info = { .class_init = s390_qemu_cpu_model_class_init, }; +static const TypeInfo max_s390_cpu_type_info = { + .name = S390_CPU_TYPE_NAME("max"), + .parent = TYPE_S390_CPU, + .instance_init = s390_max_cpu_model_initfn, + .instance_finalize = s390_cpu_model_finalize, + .class_init = s390_max_cpu_model_class_init, +}; + #ifdef CONFIG_KVM static const TypeInfo host_s390_cpu_type_info = { .name = S390_CPU_TYPE_NAME("host"), - .parent = TYPE_S390_CPU, - .instance_init = s390_host_cpu_model_initfn, - .instance_finalize = s390_cpu_model_finalize, + .parent = S390_CPU_TYPE_NAME("max"), .class_init = s390_host_cpu_model_class_init, }; #endif @@ -1281,11 +1344,6 @@ static void register_types(void) /* init all bitmaps from gnerated data initially */ s390_init_feat_bitmap(qemu_max_cpu_feat_init, qemu_max_cpu_feat); -#ifndef CONFIG_USER_ONLY - if (!pci_available) { - clear_bit(S390_FEAT_ZPCI, qemu_max_cpu_feat); - } -#endif for (i = 0; i < ARRAY_SIZE(s390_cpu_defs); i++) { s390_init_feat_bitmap(s390_cpu_defs[i].base_init, s390_cpu_defs[i].base_feat); @@ -1326,6 +1384,7 @@ static void register_types(void) } type_register_static(&qemu_s390_cpu_type_info); + type_register_static(&max_s390_cpu_type_info); #ifdef CONFIG_KVM type_register_static(&host_s390_cpu_type_info); #endif diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index 11cf5386fb..174a99e561 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -75,6 +75,8 @@ struct S390CPUModel { void s390_cpudef_featoff(uint8_t gen, uint8_t ec_ga, S390Feat feat); void s390_cpudef_featoff_greater(uint8_t gen, uint8_t ec_ga, S390Feat feat); +void s390_cpudef_group_featoff_greater(uint8_t gen, uint8_t ec_ga, + S390FeatGroup group); uint32_t s390_get_hmfai(void); uint8_t s390_get_mha_pow(void); uint32_t s390_get_ibc_val(void); diff --git a/target/s390x/diag.c b/target/s390x/diag.c index acb0f3d4af..aafa740f61 100644 --- a/target/s390x/diag.c +++ b/target/s390x/diag.c @@ -130,7 +130,7 @@ out: } return; default: - hw_error("Unhandled diag308 subcode %" PRIx64, subcode); + s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, ra); break; } } diff --git a/target/s390x/excp_helper.c b/target/s390x/excp_helper.c index f0ce60cff2..f84bfb1284 100644 --- a/target/s390x/excp_helper.c +++ b/target/s390x/excp_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -21,33 +21,52 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" +#include "exec/helper-proto.h" #include "qemu/timer.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "hw/s390x/ioinst.h" #include "exec/address-spaces.h" +#include "tcg_s390x.h" #ifndef CONFIG_USER_ONLY #include "sysemu/sysemu.h" #include "hw/s390x/s390_flic.h" #endif -/* #define DEBUG_S390 */ -/* #define DEBUG_S390_STDOUT */ +void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, + int ilen, uintptr_t ra) +{ + CPUState *cs = CPU(s390_env_get_cpu(env)); -#ifdef DEBUG_S390 -#ifdef DEBUG_S390_STDOUT -#define DPRINTF(fmt, ...) \ - do { fprintf(stderr, fmt, ## __VA_ARGS__); \ - if (qemu_log_separate()) { qemu_log(fmt, ##__VA_ARGS__); } } while (0) -#else -#define DPRINTF(fmt, ...) \ - do { qemu_log(fmt, ## __VA_ARGS__); } while (0) -#endif -#else -#define DPRINTF(fmt, ...) \ - do { } while (0) + cpu_restore_state(cs, ra, true); + qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", + env->psw.addr); + trigger_pgm_exception(env, code, ilen); + cpu_loop_exit(cs); +} + +void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, + uintptr_t ra) +{ + g_assert(dxc <= 0xff); +#if !defined(CONFIG_USER_ONLY) + /* Store the DXC into the lowcore */ + stl_phys(CPU(s390_env_get_cpu(env))->as, + env->psa + offsetof(LowCore, data_exc_code), dxc); #endif + /* Store the DXC into the FPC if AFP is enabled */ + if (env->cregs[0] & CR0_AFP) { + env->fpc = deposit32(env->fpc, 8, 8, dxc); + } + tcg_s390_program_interrupt(env, PGM_DATA, ILEN_AUTO, ra); +} + +void HELPER(data_exception)(CPUS390XState *env, uint32_t dxc) +{ + tcg_s390_data_exception(env, dxc, GETPC()); +} + #if defined(CONFIG_USER_ONLY) void s390_cpu_do_interrupt(CPUState *cs) @@ -92,8 +111,8 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, uint64_t asc; int prot; - DPRINTF("%s: address 0x%" VADDR_PRIx " rw %d mmu_idx %d\n", - __func__, orig_vaddr, rw, mmu_idx); + qemu_log_mask(CPU_LOG_MMU, "%s: addr 0x%" VADDR_PRIx " rw %d mmu_idx %d\n", + __func__, orig_vaddr, rw, mmu_idx); vaddr = orig_vaddr; @@ -122,8 +141,9 @@ int s390_cpu_handle_mmu_fault(CPUState *cs, vaddr orig_vaddr, int size, if (!address_space_access_valid(&address_space_memory, raddr, TARGET_PAGE_SIZE, rw, MEMTXATTRS_UNSPECIFIED)) { - DPRINTF("%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", __func__, - (uint64_t)raddr, (uint64_t)ram_size); + qemu_log_mask(CPU_LOG_MMU, + "%s: raddr %" PRIx64 " > ram_size %" PRIx64 "\n", + __func__, (uint64_t)raddr, (uint64_t)ram_size); trigger_pgm_exception(env, PGM_ADDRESSING, ILEN_AUTO); return 1; } @@ -181,8 +201,10 @@ static void do_program_interrupt(CPUS390XState *env) break; } - qemu_log_mask(CPU_LOG_INT, "%s: code=0x%x ilen=%d\n", - __func__, env->int_pgm_code, ilen); + qemu_log_mask(CPU_LOG_INT, + "%s: code=0x%x ilen=%d psw: %" PRIx64 " %" PRIx64 "\n", + __func__, env->int_pgm_code, ilen, env->psw.mask, + env->psw.addr); lowcore = cpu_map_lowcore(env); @@ -204,10 +226,6 @@ static void do_program_interrupt(CPUS390XState *env) cpu_unmap_lowcore(lowcore); - DPRINTF("%s: %x %x %" PRIx64 " %" PRIx64 "\n", __func__, - env->int_pgm_code, ilen, env->psw.mask, - env->psw.addr); - load_psw(env, mask, addr); } @@ -298,9 +316,6 @@ static void do_ext_interrupt(CPUS390XState *env) cpu_unmap_lowcore(lowcore); - DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, - env->psw.mask, env->psw.addr); - load_psw(env, mask, addr); } @@ -329,15 +344,44 @@ static void do_io_interrupt(CPUS390XState *env) cpu_unmap_lowcore(lowcore); g_free(io); - DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, env->psw.mask, - env->psw.addr); load_psw(env, mask, addr); } +typedef struct MchkExtSaveArea { + uint64_t vregs[32][2]; /* 0x0000 */ + uint8_t pad_0x0200[0x0400 - 0x0200]; /* 0x0200 */ +} MchkExtSaveArea; +QEMU_BUILD_BUG_ON(sizeof(MchkExtSaveArea) != 1024); + +static int mchk_store_vregs(CPUS390XState *env, uint64_t mcesao) +{ + hwaddr len = sizeof(MchkExtSaveArea); + MchkExtSaveArea *sa; + int i; + + sa = cpu_physical_memory_map(mcesao, &len, 1); + if (!sa) { + return -EFAULT; + } + if (len != sizeof(MchkExtSaveArea)) { + cpu_physical_memory_unmap(sa, len, 1, 0); + return -EFAULT; + } + + for (i = 0; i < 32; i++) { + sa->vregs[i][0] = cpu_to_be64(env->vregs[i][0].ll); + sa->vregs[i][1] = cpu_to_be64(env->vregs[i][1].ll); + } + + cpu_physical_memory_unmap(sa, len, 1, len); + return 0; +} + static void do_mchk_interrupt(CPUS390XState *env) { QEMUS390FLICState *flic = QEMU_S390_FLIC(s390_get_flic()); - uint64_t mask, addr; + uint64_t mcic = s390_build_validity_mcic() | MCIC_SC_CP; + uint64_t mask, addr, mcesao = 0; LowCore *lowcore; int i; @@ -349,6 +393,17 @@ static void do_mchk_interrupt(CPUS390XState *env) lowcore = cpu_map_lowcore(env); + /* extended save area */ + if (mcic & MCIC_VB_VR) { + /* length and alignment is 1024 bytes */ + mcesao = be64_to_cpu(lowcore->mcesad) & ~0x3ffull; + } + + /* try to store vector registers */ + if (!mcesao || mchk_store_vregs(env, mcesao)) { + mcic &= ~MCIC_VB_VR; + } + /* we are always in z/Architecture mode */ lowcore->ar_access_id = 1; @@ -364,7 +419,7 @@ static void do_mchk_interrupt(CPUS390XState *env) lowcore->cpu_timer_save_area = cpu_to_be64(env->cputm); lowcore->clock_comp_save_area = cpu_to_be64(env->ckc >> 8); - lowcore->mcic = cpu_to_be64(s390_build_validity_mcic() | MCIC_SC_CP); + lowcore->mcic = cpu_to_be64(mcic); lowcore->mcck_old_psw.mask = cpu_to_be64(get_psw_mask(env)); lowcore->mcck_old_psw.addr = cpu_to_be64(env->psw.addr); mask = be64_to_cpu(lowcore->mcck_new_psw.mask); @@ -372,9 +427,6 @@ static void do_mchk_interrupt(CPUS390XState *env) cpu_unmap_lowcore(lowcore); - DPRINTF("%s: %" PRIx64 " %" PRIx64 "\n", __func__, - env->psw.mask, env->psw.addr); - load_psw(env, mask, addr); } @@ -385,8 +437,8 @@ void s390_cpu_do_interrupt(CPUState *cs) CPUS390XState *env = &cpu->env; bool stopped = false; - qemu_log_mask(CPU_LOG_INT, "%s: %d at pc=%" PRIx64 "\n", - __func__, cs->exception_index, env->psw.addr); + qemu_log_mask(CPU_LOG_INT, "%s: %d at psw=%" PRIx64 ":%" PRIx64 "\n", + __func__, cs->exception_index, env->psw.mask, env->psw.addr); try_deliver: /* handle machine checks */ diff --git a/target/s390x/fpu_helper.c b/target/s390x/fpu_helper.c index 5c5b451b3b..1be68bafea 100644 --- a/target/s390x/fpu_helper.c +++ b/target/s390x/fpu_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" +#include "tcg_s390x.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" @@ -35,21 +36,21 @@ #define RET128(F) (env->retxl = F.low, F.high) -#define convert_bit(mask, from, to) \ - (to < from \ - ? (mask / (from / to)) & to \ - : (mask & from) * (to / from)) - -static void ieee_exception(CPUS390XState *env, uint32_t dxc, uintptr_t retaddr) +uint8_t s390_softfloat_exc_to_ieee(unsigned int exc) { - /* Install the DXC code. */ - env->fpc = (env->fpc & ~0xff00) | (dxc << 8); - /* Trap. */ - s390_program_interrupt(env, PGM_DATA, ILEN_AUTO, retaddr); + uint8_t s390_exc = 0; + + s390_exc |= (exc & float_flag_invalid) ? S390_IEEE_MASK_INVALID : 0; + s390_exc |= (exc & float_flag_divbyzero) ? S390_IEEE_MASK_DIVBYZERO : 0; + s390_exc |= (exc & float_flag_overflow) ? S390_IEEE_MASK_OVERFLOW : 0; + s390_exc |= (exc & float_flag_underflow) ? S390_IEEE_MASK_UNDERFLOW : 0; + s390_exc |= (exc & float_flag_inexact) ? S390_IEEE_MASK_INEXACT : 0; + + return s390_exc; } /* Should be called after any operation that may raise IEEE exceptions. */ -static void handle_exceptions(CPUS390XState *env, uintptr_t retaddr) +static void handle_exceptions(CPUS390XState *env, bool XxC, uintptr_t retaddr) { unsigned s390_exc, qemu_exc; @@ -60,22 +61,54 @@ static void handle_exceptions(CPUS390XState *env, uintptr_t retaddr) return; } env->fpu_status.float_exception_flags = 0; + s390_exc = s390_softfloat_exc_to_ieee(qemu_exc); - /* Convert softfloat exception bits to s390 exception bits. */ - s390_exc = 0; - s390_exc |= convert_bit(qemu_exc, float_flag_invalid, 0x80); - s390_exc |= convert_bit(qemu_exc, float_flag_divbyzero, 0x40); - s390_exc |= convert_bit(qemu_exc, float_flag_overflow, 0x20); - s390_exc |= convert_bit(qemu_exc, float_flag_underflow, 0x10); - s390_exc |= convert_bit(qemu_exc, float_flag_inexact, 0x08); + /* + * IEEE-Underflow exception recognition exists if a tininess condition + * (underflow) exists and + * - The mask bit in the FPC is zero and the result is inexact + * - The mask bit in the FPC is one + * So tininess conditions that are not inexact don't trigger any + * underflow action in case the mask bit is not one. + */ + if (!(s390_exc & S390_IEEE_MASK_INEXACT) && + !((env->fpc >> 24) & S390_IEEE_MASK_UNDERFLOW)) { + s390_exc &= ~S390_IEEE_MASK_UNDERFLOW; + } - /* Install the exceptions that we raised. */ - env->fpc |= s390_exc << 16; + /* + * FIXME: + * 1. Right now, all inexact conditions are inidicated as + * "truncated" (0) and never as "incremented" (1) in the DXC. + * 2. Only traps due to invalid/divbyzero are suppressing. Other traps + * are completing, meaning the target register has to be written! + * This, however will mean that we have to write the register before + * triggering the trap - impossible right now. + */ - /* Send signals for enabled exceptions. */ - s390_exc &= env->fpc >> 24; - if (s390_exc) { - ieee_exception(env, s390_exc, retaddr); + /* + * invalid/divbyzero cannot coexist with other conditions. + * overflow/underflow however can coexist with inexact, we have to + * handle it separatly. + */ + if (s390_exc & ~S390_IEEE_MASK_INEXACT) { + if (s390_exc & ~S390_IEEE_MASK_INEXACT & env->fpc >> 24) { + /* trap condition - inexact reported along */ + tcg_s390_data_exception(env, s390_exc, retaddr); + } + /* nontrap condition - inexact handled differently */ + env->fpc |= (s390_exc & ~S390_IEEE_MASK_INEXACT) << 16; + } + + /* inexact handling */ + if (s390_exc & S390_IEEE_MASK_INEXACT && !XxC) { + /* trap condition - overflow/underflow _not_ reported along */ + if (s390_exc & S390_IEEE_MASK_INEXACT & env->fpc >> 24) { + tcg_s390_data_exception(env, s390_exc & S390_IEEE_MASK_INEXACT, + retaddr); + } + /* nontrap condition */ + env->fpc |= (s390_exc & S390_IEEE_MASK_INEXACT) << 16; } } @@ -137,11 +170,22 @@ uint32_t set_cc_nz_f128(float128 v) } } +static inline uint8_t round_from_m34(uint32_t m34) +{ + return extract32(m34, 0, 4); +} + +static inline bool xxc_from_m34(uint32_t m34) +{ + /* XxC is bit 1 of m4 */ + return extract32(m34, 4 + 3 - 1, 1); +} + /* 32-bit FP addition */ uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float32 ret = float32_add(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -149,7 +193,7 @@ uint64_t HELPER(aeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) uint64_t HELPER(adb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float64 ret = float64_add(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -160,7 +204,7 @@ uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al, float128 ret = float128_add(make_float128(ah, al), make_float128(bh, bl), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -168,7 +212,7 @@ uint64_t HELPER(axb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float32 ret = float32_sub(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -176,7 +220,7 @@ uint64_t HELPER(seb)(CPUS390XState *env, uint64_t f1, uint64_t f2) uint64_t HELPER(sdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float64 ret = float64_sub(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -187,7 +231,7 @@ uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al, float128 ret = float128_sub(make_float128(ah, al), make_float128(bh, bl), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -195,7 +239,7 @@ uint64_t HELPER(sxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float32 ret = float32_div(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -203,7 +247,7 @@ uint64_t HELPER(deb)(CPUS390XState *env, uint64_t f1, uint64_t f2) uint64_t HELPER(ddb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float64 ret = float64_div(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -214,7 +258,7 @@ uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al, float128 ret = float128_div(make_float128(ah, al), make_float128(bh, bl), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -222,7 +266,7 @@ uint64_t HELPER(dxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float32 ret = float32_mul(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -230,7 +274,7 @@ uint64_t HELPER(meeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) uint64_t HELPER(mdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float64 ret = float64_mul(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -239,7 +283,7 @@ uint64_t HELPER(mdeb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { float64 ret = float32_to_float64(f2, &env->fpu_status); ret = float64_mul(f1, ret, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -250,7 +294,7 @@ uint64_t HELPER(mxb)(CPUS390XState *env, uint64_t ah, uint64_t al, float128 ret = float128_mul(make_float128(ah, al), make_float128(bh, bl), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -260,7 +304,7 @@ uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al, { float128 ret = float64_to_float128(f2, &env->fpu_status); ret = float128_mul(make_float128(ah, al), ret, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -268,15 +312,19 @@ uint64_t HELPER(mxdb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t HELPER(ldeb)(CPUS390XState *env, uint64_t f2) { float64 ret = float32_to_float64(f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } /* convert 128-bit float to 64-bit float */ -uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al) +uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint32_t m34) { + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float64 ret = float128_to_float64(make_float128(ah, al), &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } @@ -284,7 +332,7 @@ uint64_t HELPER(ldxb)(CPUS390XState *env, uint64_t ah, uint64_t al) uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) { float128 ret = float64_to_float128(f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } @@ -292,23 +340,30 @@ uint64_t HELPER(lxdb)(CPUS390XState *env, uint64_t f2) uint64_t HELPER(lxeb)(CPUS390XState *env, uint64_t f2) { float128 ret = float32_to_float128(f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } /* convert 64-bit float to 32-bit float */ -uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2) +uint64_t HELPER(ledb)(CPUS390XState *env, uint64_t f2, uint32_t m34) { + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float32 ret = float64_to_float32(f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 128-bit float to 32-bit float */ -uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al) +uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint32_t m34) { + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float32 ret = float128_to_float32(make_float128(ah, al), &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } @@ -316,7 +371,7 @@ uint64_t HELPER(lexb)(CPUS390XState *env, uint64_t ah, uint64_t al) uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { int cmp = float32_compare_quiet(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); } @@ -324,7 +379,7 @@ uint32_t HELPER(ceb)(CPUS390XState *env, uint64_t f1, uint64_t f2) uint32_t HELPER(cdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { int cmp = float64_compare_quiet(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); } @@ -335,21 +390,28 @@ uint32_t HELPER(cxb)(CPUS390XState *env, uint64_t ah, uint64_t al, int cmp = float128_compare_quiet(make_float128(ah, al), make_float128(bh, bl), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); } -static int swap_round_mode(CPUS390XState *env, int m3) +int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3) { int ret = env->fpu_status.float_rounding_mode; + switch (m3) { case 0: /* current mode */ break; case 1: - /* biased round no nearest */ + /* round to nearest with ties away from 0 */ + set_float_rounding_mode(float_round_ties_away, &env->fpu_status); + break; + case 3: + /* round to prepare for shorter precision */ + set_float_rounding_mode(float_round_to_odd, &env->fpu_status); + break; case 4: - /* round to nearest */ + /* round to nearest with ties to even */ set_float_rounding_mode(float_round_nearest_even, &env->fpu_status); break; case 5: @@ -364,226 +426,251 @@ static int swap_round_mode(CPUS390XState *env, int m3) /* round to -inf */ set_float_rounding_mode(float_round_down, &env->fpu_status); break; + default: + g_assert_not_reached(); } return ret; } -/* convert 64-bit int to 32-bit float */ -uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m3) +void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode) { - int hold = swap_round_mode(env, m3); + set_float_rounding_mode(old_mode, &env->fpu_status); +} + +/* convert 64-bit int to 32-bit float */ +uint64_t HELPER(cegb)(CPUS390XState *env, int64_t v2, uint32_t m34) +{ + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float32 ret = int64_to_float32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit int to 64-bit float */ -uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m3) +uint64_t HELPER(cdgb)(CPUS390XState *env, int64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float64 ret = int64_to_float64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit int to 128-bit float */ -uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m3) +uint64_t HELPER(cxgb)(CPUS390XState *env, int64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 ret = int64_to_float128(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return RET128(ret); } /* convert 64-bit uint to 32-bit float */ -uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(celgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float32 ret = uint64_to_float32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit uint to 64-bit float */ -uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(cdlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float64 ret = uint64_to_float64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit uint to 128-bit float */ -uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(cxlgb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 ret = uint64_to_float128(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return RET128(ret); } /* convert 32-bit float to 64-bit int */ -uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(cgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int64_t ret = float32_to_int64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit float to 64-bit int */ -uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(cgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int64_t ret = float64_to_int64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 128-bit float to 64-bit int */ -uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) +uint64_t HELPER(cgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 v2 = make_float128(h, l); int64_t ret = float128_to_int64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 32-bit float to 32-bit int */ -uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(cfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int32_t ret = float32_to_int32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit float to 32-bit int */ -uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(cfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); int32_t ret = float64_to_int32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 128-bit float to 32-bit int */ -uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) +uint64_t HELPER(cfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 v2 = make_float128(h, l); int32_t ret = float128_to_int32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 32-bit float to 64-bit uint */ -uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(clgeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint64_t ret; + v2 = float32_to_float64(v2, &env->fpu_status); ret = float64_to_uint64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit float to 64-bit uint */ -uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(clgdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint64_t ret = float64_to_uint64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 128-bit float to 64-bit uint */ -uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) +uint64_t HELPER(clgxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) { - int hold = swap_round_mode(env, m3); - float128 v2 = make_float128(h, l); - /* ??? Not 100% correct. */ - uint64_t ret = float128_to_int64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); + uint64_t ret = float128_to_uint64(make_float128(h, l), &env->fpu_status); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 32-bit float to 32-bit uint */ -uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(clfeb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint32_t ret = float32_to_uint32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 64-bit float to 32-bit uint */ -uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m3) +uint64_t HELPER(clfdb)(CPUS390XState *env, uint64_t v2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); uint32_t ret = float64_to_uint32(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* convert 128-bit float to 32-bit uint */ -uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m3) +uint64_t HELPER(clfxb)(CPUS390XState *env, uint64_t h, uint64_t l, uint32_t m34) { - int hold = swap_round_mode(env, m3); - float128 v2 = make_float128(h, l); - /* Not 100% correct. */ - uint32_t ret = float128_to_int64(v2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); + uint32_t ret = float128_to_uint32(make_float128(h, l), &env->fpu_status); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* round to integer 32-bit */ -uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m3) +uint64_t HELPER(fieb)(CPUS390XState *env, uint64_t f2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float32 ret = float32_round_to_int(f2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* round to integer 64-bit */ -uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m3) +uint64_t HELPER(fidb)(CPUS390XState *env, uint64_t f2, uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float64 ret = float64_round_to_int(f2, &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return ret; } /* round to integer 128-bit */ -uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3) +uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, + uint32_t m34) { - int hold = swap_round_mode(env, m3); + int old_mode = s390_swap_bfp_rounding_mode(env, round_from_m34(m34)); float128 ret = float128_round_to_int(make_float128(ah, al), &env->fpu_status); - set_float_rounding_mode(hold, &env->fpu_status); - handle_exceptions(env, GETPC()); + + s390_restore_bfp_rounding_mode(env, old_mode); + handle_exceptions(env, xxc_from_m34(m34), GETPC()); return RET128(ret); } @@ -591,7 +678,7 @@ uint64_t HELPER(fixb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint32_t m3) uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { int cmp = float32_compare(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); } @@ -599,7 +686,7 @@ uint32_t HELPER(keb)(CPUS390XState *env, uint64_t f1, uint64_t f2) uint32_t HELPER(kdb)(CPUS390XState *env, uint64_t f1, uint64_t f2) { int cmp = float64_compare(f1, f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); } @@ -610,7 +697,7 @@ uint32_t HELPER(kxb)(CPUS390XState *env, uint64_t ah, uint64_t al, int cmp = float128_compare(make_float128(ah, al), make_float128(bh, bl), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return float_comp_to_cc(env, cmp); } @@ -619,7 +706,7 @@ uint64_t HELPER(maeb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { float32 ret = float32_muladd(f2, f3, f1, 0, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -628,7 +715,7 @@ uint64_t HELPER(madb)(CPUS390XState *env, uint64_t f1, uint64_t f2, uint64_t f3) { float64 ret = float64_muladd(f2, f3, f1, 0, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -638,7 +725,7 @@ uint64_t HELPER(mseb)(CPUS390XState *env, uint64_t f1, { float32 ret = float32_muladd(f2, f3, f1, float_muladd_negate_c, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -648,78 +735,63 @@ uint64_t HELPER(msdb)(CPUS390XState *env, uint64_t f1, { float64 ret = float64_muladd(f2, f3, f1, float_muladd_negate_c, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } +/* The rightmost bit has the number 11. */ +static inline uint16_t dcmask(int bit, bool neg) +{ + return 1 << (11 - bit - neg); +} + +#define DEF_FLOAT_DCMASK(_TYPE) \ +static uint16_t _TYPE##_dcmask(CPUS390XState *env, _TYPE f1) \ +{ \ + const bool neg = _TYPE##_is_neg(f1); \ + \ + /* Sorted by most common cases - only one class is possible */ \ + if (_TYPE##_is_normal(f1)) { \ + return dcmask(2, neg); \ + } else if (_TYPE##_is_zero(f1)) { \ + return dcmask(0, neg); \ + } else if (_TYPE##_is_denormal(f1)) { \ + return dcmask(4, neg); \ + } else if (_TYPE##_is_infinity(f1)) { \ + return dcmask(6, neg); \ + } else if (_TYPE##_is_quiet_nan(f1, &env->fpu_status)) { \ + return dcmask(8, neg); \ + } \ + /* signaling nan, as last remaining case */ \ + return dcmask(10, neg); \ +} +DEF_FLOAT_DCMASK(float32) +DEF_FLOAT_DCMASK(float64) +DEF_FLOAT_DCMASK(float128) + /* test data class 32-bit */ uint32_t HELPER(tceb)(CPUS390XState *env, uint64_t f1, uint64_t m2) { - float32 v1 = f1; - int neg = float32_is_neg(v1); - uint32_t cc = 0; - - if ((float32_is_zero(v1) && (m2 & (1 << (11-neg)))) || - (float32_is_infinity(v1) && (m2 & (1 << (5-neg)))) || - (float32_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || - (float32_is_signaling_nan(v1, &env->fpu_status) && - (m2 & (1 << (1-neg))))) { - cc = 1; - } else if (m2 & (1 << (9-neg))) { - /* assume normalized number */ - cc = 1; - } - /* FIXME: denormalized? */ - return cc; + return (m2 & float32_dcmask(env, f1)) != 0; } /* test data class 64-bit */ uint32_t HELPER(tcdb)(CPUS390XState *env, uint64_t v1, uint64_t m2) { - int neg = float64_is_neg(v1); - uint32_t cc = 0; - - if ((float64_is_zero(v1) && (m2 & (1 << (11-neg)))) || - (float64_is_infinity(v1) && (m2 & (1 << (5-neg)))) || - (float64_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || - (float64_is_signaling_nan(v1, &env->fpu_status) && - (m2 & (1 << (1-neg))))) { - cc = 1; - } else if (m2 & (1 << (9-neg))) { - /* assume normalized number */ - cc = 1; - } - /* FIXME: denormalized? */ - return cc; + return (m2 & float64_dcmask(env, v1)) != 0; } /* test data class 128-bit */ -uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah, - uint64_t al, uint64_t m2) +uint32_t HELPER(tcxb)(CPUS390XState *env, uint64_t ah, uint64_t al, uint64_t m2) { - float128 v1 = make_float128(ah, al); - int neg = float128_is_neg(v1); - uint32_t cc = 0; - - if ((float128_is_zero(v1) && (m2 & (1 << (11-neg)))) || - (float128_is_infinity(v1) && (m2 & (1 << (5-neg)))) || - (float128_is_any_nan(v1) && (m2 & (1 << (3-neg)))) || - (float128_is_signaling_nan(v1, &env->fpu_status) && - (m2 & (1 << (1-neg))))) { - cc = 1; - } else if (m2 & (1 << (9-neg))) { - /* assume normalized number */ - cc = 1; - } - /* FIXME: denormalized? */ - return cc; + return (m2 & float128_dcmask(env, make_float128(ah, al))) != 0; } /* square root 32-bit */ uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2) { float32 ret = float32_sqrt(f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -727,7 +799,7 @@ uint64_t HELPER(sqeb)(CPUS390XState *env, uint64_t f2) uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2) { float64 ret = float64_sqrt(f2, &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return ret; } @@ -735,44 +807,84 @@ uint64_t HELPER(sqdb)(CPUS390XState *env, uint64_t f2) uint64_t HELPER(sqxb)(CPUS390XState *env, uint64_t ah, uint64_t al) { float128 ret = float128_sqrt(make_float128(ah, al), &env->fpu_status); - handle_exceptions(env, GETPC()); + handle_exceptions(env, false, GETPC()); return RET128(ret); } -static const int fpc_to_rnd[4] = { +static const int fpc_to_rnd[8] = { float_round_nearest_even, float_round_to_zero, float_round_up, - float_round_down + float_round_down, + -1, + -1, + -1, + float_round_to_odd, }; /* set fpc */ void HELPER(sfpc)(CPUS390XState *env, uint64_t fpc) { + if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u || + (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { + s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + } + /* Install everything in the main FPC. */ env->fpc = fpc; /* Install the rounding mode in the shadow fpu_status. */ - set_float_rounding_mode(fpc_to_rnd[fpc & 3], &env->fpu_status); + set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status); } /* set fpc and signal */ -void HELPER(sfas)(CPUS390XState *env, uint64_t val) +void HELPER(sfas)(CPUS390XState *env, uint64_t fpc) { uint32_t signalling = env->fpc; - uint32_t source = val; uint32_t s390_exc; - /* The contents of the source operand are placed in the FPC register; - then the flags in the FPC register are set to the logical OR of the - signalling flags and the source flags. */ - env->fpc = source | (signalling & 0x00ff0000); - set_float_rounding_mode(fpc_to_rnd[source & 3], &env->fpu_status); + if (fpc_to_rnd[fpc & 0x7] == -1 || fpc & 0x03030088u || + (!s390_has_feat(S390_FEAT_FLOATING_POINT_EXT) && fpc & 0x4)) { + s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + } - /* If any signalling flag is 1 and the corresponding source mask - is also 1, a simulated-iee-exception trap occurs. */ - s390_exc = (signalling >> 16) & (source >> 24); + /* + * FPC is set to the FPC operand with a bitwise OR of the signalling + * flags. + */ + env->fpc = fpc | (signalling & 0x00ff0000); + set_float_rounding_mode(fpc_to_rnd[fpc & 0x7], &env->fpu_status); + + /* + * If any signaling flag is enabled in the new FPC mask, a + * simulated-iee-exception exception occurs. + */ + s390_exc = (signalling >> 16) & (fpc >> 24); if (s390_exc) { - ieee_exception(env, s390_exc | 3, GETPC()); + if (s390_exc & S390_IEEE_MASK_INVALID) { + s390_exc = S390_IEEE_MASK_INVALID; + } else if (s390_exc & S390_IEEE_MASK_DIVBYZERO) { + s390_exc = S390_IEEE_MASK_DIVBYZERO; + } else if (s390_exc & S390_IEEE_MASK_OVERFLOW) { + s390_exc &= (S390_IEEE_MASK_OVERFLOW | S390_IEEE_MASK_INEXACT); + } else if (s390_exc & S390_IEEE_MASK_UNDERFLOW) { + s390_exc &= (S390_IEEE_MASK_UNDERFLOW | S390_IEEE_MASK_INEXACT); + } else if (s390_exc & S390_IEEE_MASK_INEXACT) { + s390_exc = S390_IEEE_MASK_INEXACT; + } else if (s390_exc & S390_IEEE_MASK_QUANTUM) { + s390_exc = S390_IEEE_MASK_QUANTUM; + } + tcg_s390_data_exception(env, s390_exc | 3, GETPC()); } } + +/* set bfp rounding mode */ +void HELPER(srnm)(CPUS390XState *env, uint64_t rnd) +{ + if (rnd > 0x7 || fpc_to_rnd[rnd & 0x7] == -1) { + s390_program_interrupt(env, PGM_SPECIFICATION, ILEN_AUTO, GETPC()); + } + + env->fpc = deposit32(env->fpc, 0, 3, rnd); + set_float_rounding_mode(fpc_to_rnd[rnd & 0x7], &env->fpu_status); +} diff --git a/target/s390x/gdbstub.c b/target/s390x/gdbstub.c index b8c81dadcf..df147596ce 100644 --- a/target/s390x/gdbstub.c +++ b/target/s390x/gdbstub.c @@ -7,7 +7,7 @@ * 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. + * 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 diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index 6626b6f565..e4739a6b9f 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -1,7 +1,7 @@ /* * S390 feature list generator * - * Copyright 2016 IBM Corp. + * Copyright IBM Corp. 2016, 2018 * * Author(s): Michael Mueller * David Hildenbrand @@ -353,6 +353,8 @@ static uint16_t base_GEN14_GA1[] = { S390_FEAT_ORDER_PRESERVING_COMPRESSION, }; +#define base_GEN14_GA2 EmptyFeat + /* Full features (in order of release) * Automatically includes corresponding base features. * Full features are all features this hardware supports even if kvm/QEMU do not @@ -447,6 +449,9 @@ static uint16_t full_GEN12_GA1[] = { S390_FEAT_ADAPTER_INT_SUPPRESSION, S390_FEAT_EDAT_2, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2, + S390_FEAT_AP_QUERY_CONFIG_INFO, + S390_FEAT_AP_FACILITIES_TEST, + S390_FEAT_AP, }; static uint16_t full_GEN12_GA2[] = { @@ -471,11 +476,14 @@ static uint16_t full_GEN14_GA1[] = { S390_FEAT_GROUP_MSA_EXT_7, S390_FEAT_GROUP_MSA_EXT_8, S390_FEAT_CMM_NT, + S390_FEAT_ETOKEN, S390_FEAT_HPMA2, S390_FEAT_SIE_KSS, S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF, }; +#define full_GEN14_GA2 EmptyFeat + /* Default features (in order of release) * Automatically includes corresponding base features. * Default features are all features this version of QEMU supports for this @@ -546,8 +554,12 @@ static uint16_t default_GEN14_GA1[] = { S390_FEAT_GROUP_MSA_EXT_6, S390_FEAT_GROUP_MSA_EXT_7, S390_FEAT_GROUP_MSA_EXT_8, + S390_FEAT_MULTIPLE_EPOCH, + S390_FEAT_GROUP_MULTIPLE_EPOCH_PTFF, }; +#define default_GEN14_GA2 EmptyFeat + /* QEMU (CPU model) features */ static uint16_t qemu_V2_11[] = { @@ -556,7 +568,7 @@ static uint16_t qemu_V2_11[] = { S390_FEAT_ZARCH, }; -static uint16_t qemu_LATEST[] = { +static uint16_t qemu_V3_1[] = { S390_FEAT_DAT_ENH, S390_FEAT_IDTE_SEGMENT, S390_FEAT_STFLE, @@ -588,14 +600,21 @@ static uint16_t qemu_LATEST[] = { S390_FEAT_MSA_EXT_4, }; +static uint16_t qemu_LATEST[] = { + /* + * Only BFP bits are implemented (HFP, DFP, PFPO and DIVIDE TO INTEGER not + * implemented yet). + */ + S390_FEAT_FLOATING_POINT_EXT, + S390_FEAT_ZPCI, +}; + /* add all new definitions before this point */ static uint16_t qemu_MAX[] = { /* z13+ features */ S390_FEAT_STFLE_53, /* generates a dependency warning, leave it out for now */ S390_FEAT_MSA_EXT_5, - /* only with CONFIG_PCI */ - S390_FEAT_ZPCI, }; /****** END FEATURE DEFS ******/ @@ -656,11 +675,13 @@ static CpuFeatDefSpec CpuFeatDef[] = { CPU_FEAT_INITIALIZER(GEN13_GA1), CPU_FEAT_INITIALIZER(GEN13_GA2), CPU_FEAT_INITIALIZER(GEN14_GA1), + CPU_FEAT_INITIALIZER(GEN14_GA2), }; #define FEAT_GROUP_INITIALIZER(_name) \ { \ .name = "S390_FEAT_GROUP_LIST_" #_name, \ + .enum_name = "S390_FEAT_GROUP_" #_name, \ .bits = \ { .data = group_##_name, \ .len = ARRAY_SIZE(group_##_name) }, \ @@ -668,6 +689,7 @@ static CpuFeatDefSpec CpuFeatDef[] = { typedef struct { const char *name; + const char *enum_name; BitSpec bits; } FeatGroupDefSpec; @@ -678,7 +700,6 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(PLO), FEAT_GROUP_INITIALIZER(TOD_CLOCK_STEERING), FEAT_GROUP_INITIALIZER(GEN13_PTFF), - FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), FEAT_GROUP_INITIALIZER(MSA), FEAT_GROUP_INITIALIZER(MSA_EXT_1), FEAT_GROUP_INITIALIZER(MSA_EXT_2), @@ -688,6 +709,7 @@ static FeatGroupDefSpec FeatGroupDef[] = { FEAT_GROUP_INITIALIZER(MSA_EXT_6), FEAT_GROUP_INITIALIZER(MSA_EXT_7), FEAT_GROUP_INITIALIZER(MSA_EXT_8), + FEAT_GROUP_INITIALIZER(MULTIPLE_EPOCH_PTFF), }; #define QEMU_FEAT_INITIALIZER(_name) \ @@ -703,6 +725,7 @@ static FeatGroupDefSpec FeatGroupDef[] = { *******************************/ static FeatGroupDefSpec QemuFeatDef[] = { QEMU_FEAT_INITIALIZER(V2_11), + QEMU_FEAT_INITIALIZER(V3_1), QEMU_FEAT_INITIALIZER(LATEST), QEMU_FEAT_INITIALIZER(MAX), }; @@ -810,6 +833,19 @@ static void print_feature_group_defs(void) } } +static void print_feature_group_enum_type(void) +{ + int i; + + printf("\n/* CPU feature group enum type */\n" + "typedef enum {\n"); + for (i = 0; i < ARRAY_SIZE(FeatGroupDef); i++) { + printf("\t%s,\n", FeatGroupDef[i].enum_name); + } + printf("\tS390_FEAT_GROUP_MAX,\n" + "} S390FeatGroup;\n"); +} + int main(int argc, char *argv[]) { printf("/*\n" @@ -826,6 +862,7 @@ int main(int argc, char *argv[]) print_feature_defs(); print_feature_group_defs(); print_qemu_feature_defs(); + print_feature_group_enum_type(); printf("\n#endif\n"); return 0; } diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 254631693d..8e9573221c 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -211,7 +211,7 @@ void s390_cpu_recompute_watchpoints(CPUState *cs) } } -struct sigp_save_area { +typedef struct SigpSaveArea { uint64_t fprs[16]; /* 0x0000 */ uint64_t grs[16]; /* 0x0080 */ PSW psw; /* 0x0100 */ @@ -225,13 +225,13 @@ struct sigp_save_area { uint8_t pad_0x0138[0x0140 - 0x0138]; /* 0x0138 */ uint32_t ars[16]; /* 0x0140 */ uint64_t crs[16]; /* 0x0384 */ -}; -QEMU_BUILD_BUG_ON(sizeof(struct sigp_save_area) != 512); +} SigpSaveArea; +QEMU_BUILD_BUG_ON(sizeof(SigpSaveArea) != 512); int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) { static const uint8_t ar_id = 1; - struct sigp_save_area *sa; + SigpSaveArea *sa; hwaddr len = sizeof(*sa); int i; @@ -272,32 +272,43 @@ int s390_store_status(S390CPU *cpu, hwaddr addr, bool store_arch) return 0; } -#define ADTL_GS_OFFSET 1024 /* offset of GS data in adtl save area */ +typedef struct SigpAdtlSaveArea { + uint64_t vregs[32][2]; /* 0x0000 */ + uint8_t pad_0x0200[0x0400 - 0x0200]; /* 0x0200 */ + uint64_t gscb[4]; /* 0x0400 */ + uint8_t pad_0x0420[0x1000 - 0x0420]; /* 0x0420 */ +} SigpAdtlSaveArea; +QEMU_BUILD_BUG_ON(sizeof(SigpAdtlSaveArea) != 4096); + #define ADTL_GS_MIN_SIZE 2048 /* minimal size of adtl save area for GS */ int s390_store_adtl_status(S390CPU *cpu, hwaddr addr, hwaddr len) { + SigpAdtlSaveArea *sa; hwaddr save = len; - void *mem; + int i; - mem = cpu_physical_memory_map(addr, &save, 1); - if (!mem) { + sa = cpu_physical_memory_map(addr, &save, 1); + if (!sa) { return -EFAULT; } if (save != len) { - cpu_physical_memory_unmap(mem, len, 1, 0); + cpu_physical_memory_unmap(sa, len, 1, 0); return -EFAULT; } - /* FIXME: as soon as TCG supports these features, convert cpu->be */ if (s390_has_feat(S390_FEAT_VECTOR)) { - memcpy(mem, &cpu->env.vregs, 512); + for (i = 0; i < 32; i++) { + sa->vregs[i][0] = cpu_to_be64(cpu->env.vregs[i][0].ll); + sa->vregs[i][1] = cpu_to_be64(cpu->env.vregs[i][1].ll); + } } if (s390_has_feat(S390_FEAT_GUARDED_STORAGE) && len >= ADTL_GS_MIN_SIZE) { - memcpy(mem + ADTL_GS_OFFSET, &cpu->env.gscb, 32); + for (i = 0; i < 4; i++) { + sa->gscb[i] = cpu_to_be64(cpu->env.gscb[i]); + } } - cpu_physical_memory_unmap(mem, len, 1, len); - + cpu_physical_memory_unmap(sa, len, 1, len); return 0; } #endif /* CONFIG_USER_ONLY */ @@ -406,6 +417,7 @@ const char *cc_name(enum cc_op cc_op) [CC_OP_SLA_32] = "CC_OP_SLA_32", [CC_OP_SLA_64] = "CC_OP_SLA_64", [CC_OP_FLOGR] = "CC_OP_FLOGR", + [CC_OP_LCBB] = "CC_OP_LCBB", }; return cc_names[cc_op]; diff --git a/target/s390x/helper.h b/target/s390x/helper.h index 97c60ca7bc..0b494a2fd2 100644 --- a/target/s390x/helper.h +++ b/target/s390x/helper.h @@ -1,4 +1,5 @@ DEF_HELPER_2(exception, noreturn, env, i32) +DEF_HELPER_2(data_exception, noreturn, env, i32) DEF_HELPER_FLAGS_4(nc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_4(oc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) DEF_HELPER_FLAGS_4(xc, TCG_CALL_NO_WG, i32, env, i32, i64, i64) @@ -52,11 +53,11 @@ DEF_HELPER_FLAGS_3(mdb, TCG_CALL_NO_WG, i64, env, i64, i64) DEF_HELPER_FLAGS_5(mxb, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i64) DEF_HELPER_FLAGS_4(mxdb, TCG_CALL_NO_WG, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_2(ldeb, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_3(ldxb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_4(ldxb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) DEF_HELPER_FLAGS_2(lxdb, TCG_CALL_NO_WG, i64, env, i64) DEF_HELPER_FLAGS_2(lxeb, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(ledb, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_3(lexb, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(ledb, TCG_CALL_NO_WG, i64, env, i64, i32) +DEF_HELPER_FLAGS_4(lexb, TCG_CALL_NO_WG, i64, env, i64, i64, i32) DEF_HELPER_FLAGS_3(ceb, TCG_CALL_NO_WG_SE, i32, env, i64, i64) DEF_HELPER_FLAGS_3(cdb, TCG_CALL_NO_WG_SE, i32, env, i64, i64) DEF_HELPER_FLAGS_5(cxb, TCG_CALL_NO_WG_SE, i32, env, i64, i64, i64, i64) @@ -103,8 +104,9 @@ DEF_HELPER_4(trtr, i32, env, i32, i64, i64) DEF_HELPER_5(trXX, i32, env, i32, i32, i32, i32) DEF_HELPER_4(cksm, i64, env, i64, i64, i64) DEF_HELPER_FLAGS_5(calc_cc, TCG_CALL_NO_RWG_SE, i32, env, i32, i64, i64, i64) -DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_RWG, void, env, i64) +DEF_HELPER_FLAGS_2(sfpc, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_2(sfas, TCG_CALL_NO_WG, void, env, i64) +DEF_HELPER_FLAGS_2(srnm, TCG_CALL_NO_WG, void, env, i64) DEF_HELPER_FLAGS_1(popcnt, TCG_CALL_NO_RWG_SE, i64, i64) DEF_HELPER_2(stfle, i32, env, i64) DEF_HELPER_FLAGS_2(lpq, TCG_CALL_NO_WG, i64, env, i64) @@ -120,13 +122,34 @@ DEF_HELPER_4(cu41, i32, env, i32, i32, i32) DEF_HELPER_4(cu42, i32, env, i32, i32, i32) DEF_HELPER_5(msa, i32, env, i32, i32, i32, i32) DEF_HELPER_FLAGS_1(stpt, TCG_CALL_NO_RWG, i64, env) +DEF_HELPER_FLAGS_1(stck, TCG_CALL_NO_RWG_SE, i64, env) +DEF_HELPER_FLAGS_3(probe_write_access, TCG_CALL_NO_WG, void, env, i64, i64) + +/* === Vector Support Instructions === */ +DEF_HELPER_FLAGS_4(vll, TCG_CALL_NO_WG, void, env, ptr, i64, i64) +DEF_HELPER_FLAGS_4(gvec_vpk16, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpk32, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpk64, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpks16, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpks32, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpks64, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_5(gvec_vpks_cc16, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_5(gvec_vpks_cc32, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_5(gvec_vpks_cc64, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_FLAGS_4(gvec_vpkls16, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpkls32, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(gvec_vpkls64, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, i32) +DEF_HELPER_5(gvec_vpkls_cc16, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_5(gvec_vpkls_cc32, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_5(gvec_vpkls_cc64, void, ptr, cptr, cptr, env, i32) +DEF_HELPER_FLAGS_5(gvec_vperm, TCG_CALL_NO_RWG, void, ptr, cptr, cptr, cptr, i32) +DEF_HELPER_FLAGS_4(vstl, TCG_CALL_NO_WG, void, env, cptr, i64, i64) #ifndef CONFIG_USER_ONLY DEF_HELPER_3(servc, i32, env, i64, i64) DEF_HELPER_4(diag, void, env, i32, i32, i32) DEF_HELPER_3(load_psw, noreturn, env, i64, i64) DEF_HELPER_FLAGS_2(spx, TCG_CALL_NO_RWG, void, env, i64) -DEF_HELPER_FLAGS_1(stck, TCG_CALL_NO_RWG_SE, i64, env) DEF_HELPER_FLAGS_2(sck, TCG_CALL_NO_RWG, i32, env, i64) DEF_HELPER_FLAGS_2(sckc, TCG_CALL_NO_RWG, void, env, i64) DEF_HELPER_FLAGS_2(sckpf, TCG_CALL_NO_RWG, void, env, i64) diff --git a/target/s390x/insn-data.def b/target/s390x/insn-data.def index 5c6f33ed9c..71fa9b8d6c 100644 --- a/target/s390x/insn-data.def +++ b/target/s390x/insn-data.def @@ -3,6 +3,8 @@ * * C(OPC, NAME, FMT, FAC, I1, I2, P, W, OP, CC) * D(OPC, NAME, FMT, FAC, I1, I2, P, W, OP, CC, DATA) + * E(OPC, NAME, FMT, FAC, I1, I2, P, W, OP, CC, DATA, FLAGS) + * F(OPC, NAME, FMT, FAC, I1, I2, P, W, OP, CC, FLAGS) * * OPC = (op << 8) | op2 where op is the major, op2 the minor opcode * NAME = name of the opcode, used internally @@ -15,6 +17,7 @@ * OP = func op_xx does the bulk of the operation * CC = func cout_xx defines how cc should get set * DATA = immediate argument to op_xx function + * FLAGS = categorize the type of instruction (e.g. for advanced checks) * * The helpers get called in order: I1, I2, P, OP, W, CC */ @@ -29,11 +32,11 @@ C(0xb9e8, AGRK, RRF_a, DO, r2, r3, r1, 0, add, adds64) C(0xe308, AG, RXY_a, Z, r1, m2_64, r1, 0, add, adds64) C(0xe318, AGF, RXY_a, Z, r1, m2_32s, r1, 0, add, adds64) - C(0xb30a, AEBR, RRE, Z, e1, e2, new, e1, aeb, f32) - C(0xb31a, ADBR, RRE, Z, f1_o, f2_o, f1, 0, adb, f64) - C(0xb34a, AXBR, RRE, Z, 0, x2_o, x1, 0, axb, f128) - C(0xed0a, AEB, RXE, Z, e1, m2_32u, new, e1, aeb, f32) - C(0xed1a, ADB, RXE, Z, f1_o, m2_64, f1, 0, adb, f64) + F(0xb30a, AEBR, RRE, Z, e1, e2, new, e1, aeb, f32, IF_BFP) + F(0xb31a, ADBR, RRE, Z, f1, f2, new, f1, adb, f64, IF_BFP) + F(0xb34a, AXBR, RRE, Z, x2h, x2l, x1, x1, axb, f128, IF_BFP) + F(0xed0a, AEB, RXE, Z, e1, m2_32u, new, e1, aeb, f32, IF_BFP) + F(0xed1a, ADB, RXE, Z, f1, m2_64, new, f1, adb, f64, IF_BFP) /* ADD HIGH */ C(0xb9c8, AHHHR, RRF_a, HW, r2_sr32, r3_sr32, new, r1_32h, add, adds32) C(0xb9d8, AHHLR, RRF_a, HW, r2_sr32, r3, new, r1_32h, add, adds32) @@ -102,6 +105,9 @@ D(0x9400, NI, SI, Z, la1, i2_8u, new, 0, ni, nz64, MO_UB) D(0xeb54, NIY, SIY, LD, la1, i2_8u, new, 0, ni, nz64, MO_UB) +/* BRANCH AND LINK */ + C(0x0500, BALR, RR_a, Z, 0, r2_nz, r1, 0, bal, 0) + C(0x4500, BAL, RX_a, Z, 0, a2, r1, 0, bal, 0) /* BRANCH AND SAVE */ C(0x0d00, BASR, RR_a, Z, 0, r2_nz, r1, 0, bas, 0) C(0x4d00, BAS, RX_a, Z, 0, a2, r1, 0, bas, 0) @@ -148,7 +154,7 @@ C(0xb241, CKSM, RRE, Z, r1_o, ra2, new, r1_32, cksm, 0) /* COPY SIGN */ - C(0xb372, CPSDR, RRF_b, FPSSH, f3_o, f2_o, f1, 0, cps, 0) + F(0xb372, CPSDR, RRF_b, FPSSH, f3, f2, new, f1, cps, 0, IF_AFP1 | IF_AFP2 | IF_AFP3) /* COMPARE */ C(0x1900, CR, RR_a, Z, r1_o, r2_o, 0, 0, 0, cmps32) @@ -158,17 +164,17 @@ C(0xb930, CGFR, RRE, Z, r1_o, r2_32s, 0, 0, 0, cmps64) C(0xe320, CG, RXY_a, Z, r1_o, m2_64, 0, 0, 0, cmps64) C(0xe330, CGF, RXY_a, Z, r1_o, m2_32s, 0, 0, 0, cmps64) - C(0xb309, CEBR, RRE, Z, e1, e2, 0, 0, ceb, 0) - C(0xb319, CDBR, RRE, Z, f1_o, f2_o, 0, 0, cdb, 0) - C(0xb349, CXBR, RRE, Z, x1_o, x2_o, 0, 0, cxb, 0) - C(0xed09, CEB, RXE, Z, e1, m2_32u, 0, 0, ceb, 0) - C(0xed19, CDB, RXE, Z, f1_o, m2_64, 0, 0, cdb, 0) + F(0xb309, CEBR, RRE, Z, e1, e2, 0, 0, ceb, 0, IF_BFP) + F(0xb319, CDBR, RRE, Z, f1, f2, 0, 0, cdb, 0, IF_BFP) + F(0xb349, CXBR, RRE, Z, x2h, x2l, x1, 0, cxb, 0, IF_BFP) + F(0xed09, CEB, RXE, Z, e1, m2_32u, 0, 0, ceb, 0, IF_BFP) + F(0xed19, CDB, RXE, Z, f1, m2_64, 0, 0, cdb, 0, IF_BFP) /* COMPARE AND SIGNAL */ - C(0xb308, KEBR, RRE, Z, e1, e2, 0, 0, keb, 0) - C(0xb318, KDBR, RRE, Z, f1_o, f2_o, 0, 0, kdb, 0) - C(0xb348, KXBR, RRE, Z, x1_o, x2_o, 0, 0, kxb, 0) - C(0xed08, KEB, RXE, Z, e1, m2_32u, 0, 0, keb, 0) - C(0xed18, KDB, RXE, Z, f1_o, m2_64, 0, 0, kdb, 0) + F(0xb308, KEBR, RRE, Z, e1, e2, 0, 0, keb, 0, IF_BFP) + F(0xb318, KDBR, RRE, Z, f1, f2, 0, 0, kdb, 0, IF_BFP) + F(0xb348, KXBR, RRE, Z, x2h, x2l, x1, 0, kxb, 0, IF_BFP) + F(0xed08, KEB, RXE, Z, e1, m2_32u, 0, 0, keb, 0, IF_BFP) + F(0xed18, KDB, RXE, Z, f1, m2_64, 0, 0, kdb, 0, IF_BFP) /* COMPARE IMMEDIATE */ C(0xc20d, CFI, RIL_a, EI, r1, i2, 0, 0, 0, cmps32) C(0xc20c, CGFI, RIL_a, EI, r1, i2, 0, 0, 0, cmps64) @@ -285,33 +291,33 @@ C(0x4e00, CVD, RX_a, Z, r1_o, a2, 0, 0, cvd, 0) C(0xe326, CVDY, RXY_a, LD, r1_o, a2, 0, 0, cvd, 0) /* CONVERT TO FIXED */ - C(0xb398, CFEBR, RRF_e, Z, 0, e2, new, r1_32, cfeb, 0) - C(0xb399, CFDBR, RRF_e, Z, 0, f2_o, new, r1_32, cfdb, 0) - C(0xb39a, CFXBR, RRF_e, Z, 0, x2_o, new, r1_32, cfxb, 0) - C(0xb3a8, CGEBR, RRF_e, Z, 0, e2, r1, 0, cgeb, 0) - C(0xb3a9, CGDBR, RRF_e, Z, 0, f2_o, r1, 0, cgdb, 0) - C(0xb3aa, CGXBR, RRF_e, Z, 0, x2_o, r1, 0, cgxb, 0) + F(0xb398, CFEBR, RRF_e, Z, 0, e2, new, r1_32, cfeb, 0, IF_BFP) + F(0xb399, CFDBR, RRF_e, Z, 0, f2, new, r1_32, cfdb, 0, IF_BFP) + F(0xb39a, CFXBR, RRF_e, Z, x2h, x2l, new, r1_32, cfxb, 0, IF_BFP) + F(0xb3a8, CGEBR, RRF_e, Z, 0, e2, r1, 0, cgeb, 0, IF_BFP) + F(0xb3a9, CGDBR, RRF_e, Z, 0, f2, r1, 0, cgdb, 0, IF_BFP) + F(0xb3aa, CGXBR, RRF_e, Z, x2h, x2l, r1, 0, cgxb, 0, IF_BFP) /* CONVERT FROM FIXED */ - C(0xb394, CEFBR, RRF_e, Z, 0, r2_32s, new, e1, cegb, 0) - C(0xb395, CDFBR, RRF_e, Z, 0, r2_32s, f1, 0, cdgb, 0) - C(0xb396, CXFBR, RRF_e, Z, 0, r2_32s, x1, 0, cxgb, 0) - C(0xb3a4, CEGBR, RRF_e, Z, 0, r2_o, new, e1, cegb, 0) - C(0xb3a5, CDGBR, RRF_e, Z, 0, r2_o, f1, 0, cdgb, 0) - C(0xb3a6, CXGBR, RRF_e, Z, 0, r2_o, x1, 0, cxgb, 0) + F(0xb394, CEFBR, RRF_e, Z, 0, r2_32s, new, e1, cegb, 0, IF_BFP) + F(0xb395, CDFBR, RRF_e, Z, 0, r2_32s, new, f1, cdgb, 0, IF_BFP) + F(0xb396, CXFBR, RRF_e, Z, 0, r2_32s, new_P, x1, cxgb, 0, IF_BFP) + F(0xb3a4, CEGBR, RRF_e, Z, 0, r2_o, new, e1, cegb, 0, IF_BFP) + F(0xb3a5, CDGBR, RRF_e, Z, 0, r2_o, new, f1, cdgb, 0, IF_BFP) + F(0xb3a6, CXGBR, RRF_e, Z, 0, r2_o, new_P, x1, cxgb, 0, IF_BFP) /* CONVERT TO LOGICAL */ - C(0xb39c, CLFEBR, RRF_e, FPE, 0, e2, new, r1_32, clfeb, 0) - C(0xb39d, CLFDBR, RRF_e, FPE, 0, f2_o, new, r1_32, clfdb, 0) - C(0xb39e, CLFXBR, RRF_e, FPE, 0, x2_o, new, r1_32, clfxb, 0) - C(0xb3ac, CLGEBR, RRF_e, FPE, 0, e2, r1, 0, clgeb, 0) - C(0xb3ad, CLGDBR, RRF_e, FPE, 0, f2_o, r1, 0, clgdb, 0) - C(0xb3ae, CLGXBR, RRF_e, FPE, 0, x2_o, r1, 0, clgxb, 0) + F(0xb39c, CLFEBR, RRF_e, FPE, 0, e2, new, r1_32, clfeb, 0, IF_BFP) + F(0xb39d, CLFDBR, RRF_e, FPE, 0, f2, new, r1_32, clfdb, 0, IF_BFP) + F(0xb39e, CLFXBR, RRF_e, FPE, x2h, x2l, new, r1_32, clfxb, 0, IF_BFP) + F(0xb3ac, CLGEBR, RRF_e, FPE, 0, e2, r1, 0, clgeb, 0, IF_BFP) + F(0xb3ad, CLGDBR, RRF_e, FPE, 0, f2, r1, 0, clgdb, 0, IF_BFP) + F(0xb3ae, CLGXBR, RRF_e, FPE, x2h, x2l, r1, 0, clgxb, 0, IF_BFP) /* CONVERT FROM LOGICAL */ - C(0xb390, CELFBR, RRF_e, FPE, 0, r2_32u, new, e1, celgb, 0) - C(0xb391, CDLFBR, RRF_e, FPE, 0, r2_32u, f1, 0, cdlgb, 0) - C(0xb392, CXLFBR, RRF_e, FPE, 0, r2_32u, x1, 0, cxlgb, 0) - C(0xb3a0, CELGBR, RRF_e, FPE, 0, r2_o, new, e1, celgb, 0) - C(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, f1, 0, cdlgb, 0) - C(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, x1, 0, cxlgb, 0) + F(0xb390, CELFBR, RRF_e, FPE, 0, r2_32u, new, e1, celgb, 0, IF_BFP) + F(0xb391, CDLFBR, RRF_e, FPE, 0, r2_32u, new, f1, cdlgb, 0, IF_BFP) + F(0xb392, CXLFBR, RRF_e, FPE, 0, r2_32u, new_P, x1, cxlgb, 0, IF_BFP) + F(0xb3a0, CELGBR, RRF_e, FPE, 0, r2_o, new, e1, celgb, 0, IF_BFP) + F(0xb3a1, CDLGBR, RRF_e, FPE, 0, r2_o, new, f1, cdlgb, 0, IF_BFP) + F(0xb3a2, CXLGBR, RRF_e, FPE, 0, r2_o, new_P, x1, cxlgb, 0, IF_BFP) /* CONVERT UTF-8 TO UTF-16 */ D(0xb2a7, CU12, RRF_c, Z, 0, 0, 0, 0, cuXX, 0, 12) @@ -329,11 +335,11 @@ /* DIVIDE */ C(0x1d00, DR, RR_a, Z, r1_D32, r2_32s, new_P, r1_P32, divs32, 0) C(0x5d00, D, RX_a, Z, r1_D32, m2_32s, new_P, r1_P32, divs32, 0) - C(0xb30d, DEBR, RRE, Z, e1, e2, new, e1, deb, 0) - C(0xb31d, DDBR, RRE, Z, f1_o, f2_o, f1, 0, ddb, 0) - C(0xb34d, DXBR, RRE, Z, 0, x2_o, x1, 0, dxb, 0) - C(0xed0d, DEB, RXE, Z, e1, m2_32u, new, e1, deb, 0) - C(0xed1d, DDB, RXE, Z, f1_o, m2_64, f1, 0, ddb, 0) + F(0xb30d, DEBR, RRE, Z, e1, e2, new, e1, deb, 0, IF_BFP) + F(0xb31d, DDBR, RRE, Z, f1, f2, new, f1, ddb, 0, IF_BFP) + F(0xb34d, DXBR, RRE, Z, x2h, x2l, x1, x1, dxb, 0, IF_BFP) + F(0xed0d, DEB, RXE, Z, e1, m2_32u, new, e1, deb, 0, IF_BFP) + F(0xed1d, DDB, RXE, Z, f1, m2_64, new, f1, ddb, 0, IF_BFP) /* DIVIDE LOGICAL */ C(0xb997, DLR, RRE, Z, r1_D32, r2_32u, new_P, r1_P32, divu32, 0) C(0xe397, DL, RXY_a, Z, r1_D32, m2_32u, new_P, r1_P32, divu32, 0) @@ -372,7 +378,7 @@ /* EXTRACT CPU TIME */ C(0xc801, ECTG, SSF, ECT, 0, 0, 0, 0, ectg, 0) /* EXTRACT FPC */ - C(0xb38c, EFPC, RRE, Z, 0, 0, new, r1_32, efpc, 0) + F(0xb38c, EFPC, RRE, Z, 0, 0, new, r1_32, efpc, 0, IF_BFP) /* EXTRACT PSW */ C(0xb98d, EPSW, RRE, Z, 0, 0, 0, 0, epsw, 0) @@ -404,13 +410,13 @@ C(0xb914, LGFR, RRE, Z, 0, r2_32s, 0, r1, mov2, 0) C(0xe304, LG, RXY_a, Z, 0, a2, r1, 0, ld64, 0) C(0xe314, LGF, RXY_a, Z, 0, a2, r1, 0, ld32s, 0) - C(0x2800, LDR, RR_a, Z, 0, f2_o, 0, f1, mov2, 0) - C(0x6800, LD, RX_a, Z, 0, m2_64, 0, f1, mov2, 0) - C(0xed65, LDY, RXY_a, LD, 0, m2_64, 0, f1, mov2, 0) - C(0x3800, LER, RR_a, Z, 0, e2, 0, cond_e1e2, mov2, 0) - C(0x7800, LE, RX_a, Z, 0, m2_32u, 0, e1, mov2, 0) - C(0xed64, LEY, RXY_a, LD, 0, m2_32u, 0, e1, mov2, 0) - C(0xb365, LXR, RRE, Z, 0, x2_o, 0, x1, movx, 0) + F(0x2800, LDR, RR_a, Z, 0, f2, 0, f1, mov2, 0, IF_AFP1 | IF_AFP2) + F(0x6800, LD, RX_a, Z, 0, m2_64, 0, f1, mov2, 0, IF_AFP1) + F(0xed65, LDY, RXY_a, LD, 0, m2_64, 0, f1, mov2, 0, IF_AFP1) + F(0x3800, LER, RR_a, Z, 0, e2, 0, cond_e1e2, mov2, 0, IF_AFP1 | IF_AFP2) + F(0x7800, LE, RX_a, Z, 0, m2_32u, 0, e1, mov2, 0, IF_AFP1) + F(0xed64, LEY, RXY_a, LD, 0, m2_32u, 0, e1, mov2, 0, IF_AFP1) + F(0xb365, LXR, RRE, Z, x2h, x2l, 0, x1, movx, 0, IF_AFP1) /* LOAD IMMEDIATE */ C(0xc001, LGFI, RIL_a, EI, 0, i2, 0, r1, mov2, 0) /* LOAD RELATIVE LONG */ @@ -447,9 +453,9 @@ C(0xe312, LT, RXY_a, EI, 0, a2, new, r1_32, ld32s, s64) C(0xe302, LTG, RXY_a, EI, 0, a2, r1, 0, ld64, s64) C(0xe332, LTGF, RXY_a, GIE, 0, a2, r1, 0, ld32s, s64) - C(0xb302, LTEBR, RRE, Z, 0, e2, 0, cond_e1e2, mov2, f32) - C(0xb312, LTDBR, RRE, Z, 0, f2_o, 0, f1, mov2, f64) - C(0xb342, LTXBR, RRE, Z, 0, x2_o, 0, x1, movx, f128) + F(0xb302, LTEBR, RRE, Z, 0, e2, 0, cond_e1e2, mov2, f32, IF_BFP) + F(0xb312, LTDBR, RRE, Z, 0, f2, 0, f1, mov2, f64, IF_BFP) + F(0xb342, LTXBR, RRE, Z, x2h, x2l, 0, x1, movx, f128, IF_BFP) /* LOAD AND TRAP */ C(0xe39f, LAT, RXY_a, LAT, 0, m2_32u, r1, 0, lat, 0) C(0xe385, LGAT, RXY_a, LAT, 0, a2, r1, 0, lgat, 0) @@ -469,10 +475,12 @@ C(0x1300, LCR, RR_a, Z, 0, r2, new, r1_32, neg, neg32) C(0xb903, LCGR, RRE, Z, 0, r2, r1, 0, neg, neg64) C(0xb913, LCGFR, RRE, Z, 0, r2_32s, r1, 0, neg, neg64) - C(0xb303, LCEBR, RRE, Z, 0, e2, new, e1, negf32, f32) - C(0xb313, LCDBR, RRE, Z, 0, f2_o, f1, 0, negf64, f64) - C(0xb343, LCXBR, RRE, Z, 0, x2_o, x1, 0, negf128, f128) - C(0xb373, LCDFR, RRE, FPSSH, 0, f2_o, f1, 0, negf64, 0) + F(0xb303, LCEBR, RRE, Z, 0, e2, new, e1, negf32, f32, IF_BFP) + F(0xb313, LCDBR, RRE, Z, 0, f2, new, f1, negf64, f64, IF_BFP) + F(0xb343, LCXBR, RRE, Z, x2h, x2l, new_P, x1, negf128, f128, IF_BFP) + F(0xb373, LCDFR, RRE, FPSSH, 0, f2, new, f1, negf64, 0, IF_AFP1 | IF_AFP2) +/* LOAD COUNT TO BLOCK BOUNDARY */ + C(0xe727, LCBB, RXE, V, la2, 0, r1, 0, lcbb, 0) /* LOAD HALFWORD */ C(0xb927, LHR, RRE, EI, 0, r2_16s, 0, r1_32, mov2, 0) C(0xb907, LGHR, RRE, EI, 0, r2_16s, 0, r1, mov2, 0) @@ -529,17 +537,17 @@ C(0xe39c, LLGTAT, RXY_a, LAT, 0, m2_32u, r1, 0, llgtat, 0) /* LOAD FPR FROM GR */ - C(0xb3c1, LDGR, RRE, FPRGR, 0, r2_o, 0, f1, mov2, 0) + F(0xb3c1, LDGR, RRE, FPRGR, 0, r2_o, 0, f1, mov2, 0, IF_AFP1) /* LOAD GR FROM FPR */ - C(0xb3cd, LGDR, RRE, FPRGR, 0, f2_o, 0, r1, mov2, 0) + F(0xb3cd, LGDR, RRE, FPRGR, 0, f2, 0, r1, mov2, 0, IF_AFP2) /* LOAD NEGATIVE */ C(0x1100, LNR, RR_a, Z, 0, r2_32s, new, r1_32, nabs, nabs32) C(0xb901, LNGR, RRE, Z, 0, r2, r1, 0, nabs, nabs64) C(0xb911, LNGFR, RRE, Z, 0, r2_32s, r1, 0, nabs, nabs64) - C(0xb301, LNEBR, RRE, Z, 0, e2, new, e1, nabsf32, f32) - C(0xb311, LNDBR, RRE, Z, 0, f2_o, f1, 0, nabsf64, f64) - C(0xb341, LNXBR, RRE, Z, 0, x2_o, x1, 0, nabsf128, f128) - C(0xb371, LNDFR, RRE, FPSSH, 0, f2_o, f1, 0, nabsf64, 0) + F(0xb301, LNEBR, RRE, Z, 0, e2, new, e1, nabsf32, f32, IF_BFP) + F(0xb311, LNDBR, RRE, Z, 0, f2, new, f1, nabsf64, f64, IF_BFP) + F(0xb341, LNXBR, RRE, Z, x2h, x2l, new_P, x1, nabsf128, f128, IF_BFP) + F(0xb371, LNDFR, RRE, FPSSH, 0, f2, new, f1, nabsf64, 0, IF_AFP1 | IF_AFP2) /* LOAD ON CONDITION */ C(0xb9f2, LOCR, RRF_c, LOC, r1, r2, new, r1_32, loc, 0) C(0xb9e2, LOCGR, RRF_c, LOC, r1, r2, r1, 0, loc, 0) @@ -561,10 +569,10 @@ C(0x1000, LPR, RR_a, Z, 0, r2_32s, new, r1_32, abs, abs32) C(0xb900, LPGR, RRE, Z, 0, r2, r1, 0, abs, abs64) C(0xb910, LPGFR, RRE, Z, 0, r2_32s, r1, 0, abs, abs64) - C(0xb300, LPEBR, RRE, Z, 0, e2, new, e1, absf32, f32) - C(0xb310, LPDBR, RRE, Z, 0, f2_o, f1, 0, absf64, f64) - C(0xb340, LPXBR, RRE, Z, 0, x2_o, x1, 0, absf128, f128) - C(0xb370, LPDFR, RRE, FPSSH, 0, f2_o, f1, 0, absf64, 0) + F(0xb300, LPEBR, RRE, Z, 0, e2, new, e1, absf32, f32, IF_BFP) + F(0xb310, LPDBR, RRE, Z, 0, f2, new, f1, absf64, f64, IF_BFP) + F(0xb340, LPXBR, RRE, Z, x2h, x2l, new_P, x1, absf128, f128, IF_BFP) + F(0xb370, LPDFR, RRE, FPSSH, 0, f2, new, f1, absf64, 0, IF_AFP1 | IF_AFP2) /* LOAD REVERSED */ C(0xb91f, LRVR, RRE, Z, 0, r2_32u, new, r1_32, rev32, 0) C(0xb90f, LRVGR, RRE, Z, 0, r2_o, r1, 0, rev64, 0) @@ -572,30 +580,32 @@ C(0xe31e, LRV, RXY_a, Z, 0, m2_32u, new, r1_32, rev32, 0) C(0xe30f, LRVG, RXY_a, Z, 0, m2_64, r1, 0, rev64, 0) /* LOAD ZERO */ - C(0xb374, LZER, RRE, Z, 0, 0, 0, e1, zero, 0) - C(0xb375, LZDR, RRE, Z, 0, 0, 0, f1, zero, 0) - C(0xb376, LZXR, RRE, Z, 0, 0, 0, x1, zero2, 0) + F(0xb374, LZER, RRE, Z, 0, 0, 0, e1, zero, 0, IF_AFP1) + F(0xb375, LZDR, RRE, Z, 0, 0, 0, f1, zero, 0, IF_AFP1) + F(0xb376, LZXR, RRE, Z, 0, 0, 0, x1, zero2, 0, IF_AFP1) /* LOAD FPC */ - C(0xb29d, LFPC, S, Z, 0, m2_32u, 0, 0, sfpc, 0) + F(0xb29d, LFPC, S, Z, 0, m2_32u, 0, 0, sfpc, 0, IF_BFP) /* LOAD FPC AND SIGNAL */ - C(0xb2bd, LFAS, S, IEEEE_SIM, 0, m2_32u, 0, 0, sfas, 0) + F(0xb2bd, LFAS, S, IEEEE_SIM, 0, m2_32u, 0, 0, sfas, 0, IF_DFP) /* LOAD FP INTEGER */ - C(0xb357, FIEBR, RRF_e, Z, 0, e2, new, e1, fieb, 0) - C(0xb35f, FIDBR, RRF_e, Z, 0, f2_o, f1, 0, fidb, 0) - C(0xb347, FIXBR, RRF_e, Z, 0, x2_o, x1, 0, fixb, 0) + F(0xb357, FIEBR, RRF_e, Z, 0, e2, new, e1, fieb, 0, IF_BFP) + F(0xb35f, FIDBR, RRF_e, Z, 0, f2, new, f1, fidb, 0, IF_BFP) + F(0xb347, FIXBR, RRF_e, Z, x2h, x2l, new_P, x1, fixb, 0, IF_BFP) /* LOAD LENGTHENED */ - C(0xb304, LDEBR, RRE, Z, 0, e2, f1, 0, ldeb, 0) - C(0xb305, LXDBR, RRE, Z, 0, f2_o, x1, 0, lxdb, 0) - C(0xb306, LXEBR, RRE, Z, 0, e2, x1, 0, lxeb, 0) - C(0xed04, LDEB, RXE, Z, 0, m2_32u, f1, 0, ldeb, 0) - C(0xed05, LXDB, RXE, Z, 0, m2_64, x1, 0, lxdb, 0) - C(0xed06, LXEB, RXE, Z, 0, m2_32u, x1, 0, lxeb, 0) + F(0xb304, LDEBR, RRE, Z, 0, e2, new, f1, ldeb, 0, IF_BFP) + F(0xb305, LXDBR, RRE, Z, 0, f2, new_P, x1, lxdb, 0, IF_BFP) + F(0xb306, LXEBR, RRE, Z, 0, e2, new_P, x1, lxeb, 0, IF_BFP) + F(0xed04, LDEB, RXE, Z, 0, m2_32u, new, f1, ldeb, 0, IF_BFP) + F(0xed05, LXDB, RXE, Z, 0, m2_64, new_P, x1, lxdb, 0, IF_BFP) + F(0xed06, LXEB, RXE, Z, 0, m2_32u, new_P, x1, lxeb, 0, IF_BFP) + F(0xb324, LDER, RXE, Z, 0, e2, new, f1, lde, 0, IF_AFP1) + F(0xed24, LDE, RXE, Z, 0, m2_32u, new, f1, lde, 0, IF_AFP1) /* LOAD ROUNDED */ - C(0xb344, LEDBR, RRE, Z, 0, f2_o, new, e1, ledb, 0) - C(0xb345, LDXBR, RRE, Z, 0, x2_o, f1, 0, ldxb, 0) - C(0xb346, LEXBR, RRE, Z, 0, x2_o, new, e1, lexb, 0) + F(0xb344, LEDBR, RRF_e, Z, 0, f2, new, e1, ledb, 0, IF_BFP) + F(0xb345, LDXBR, RRF_e, Z, x2h, x2l, new, f1, ldxb, 0, IF_BFP) + F(0xb346, LEXBR, RRF_e, Z, x2h, x2l, new, e1, lexb, 0, IF_BFP) /* LOAD MULTIPLE */ C(0x9800, LM, RS_a, Z, 0, a2, 0, 0, lm32, 0) @@ -641,15 +651,15 @@ C(0x1c00, MR, RR_a, Z, r1p1_32s, r2_32s, new, r1_D32, mul, 0) C(0x5c00, M, RX_a, Z, r1p1_32s, m2_32s, new, r1_D32, mul, 0) C(0xe35c, MFY, RXY_a, GIE, r1p1_32s, m2_32s, new, r1_D32, mul, 0) - C(0xb317, MEEBR, RRE, Z, e1, e2, new, e1, meeb, 0) - C(0xb31c, MDBR, RRE, Z, f1_o, f2_o, f1, 0, mdb, 0) - C(0xb34c, MXBR, RRE, Z, 0, x2_o, x1, 0, mxb, 0) - C(0xb30c, MDEBR, RRE, Z, f1_o, e2, f1, 0, mdeb, 0) - C(0xb307, MXDBR, RRE, Z, 0, f2_o, x1, 0, mxdb, 0) - C(0xed17, MEEB, RXE, Z, e1, m2_32u, new, e1, meeb, 0) - C(0xed1c, MDB, RXE, Z, f1_o, m2_64, f1, 0, mdb, 0) - C(0xed0c, MDEB, RXE, Z, f1_o, m2_32u, f1, 0, mdeb, 0) - C(0xed07, MXDB, RXE, Z, 0, m2_64, x1, 0, mxdb, 0) + F(0xb317, MEEBR, RRE, Z, e1, e2, new, e1, meeb, 0, IF_BFP) + F(0xb31c, MDBR, RRE, Z, f1, f2, new, f1, mdb, 0, IF_BFP) + F(0xb34c, MXBR, RRE, Z, x2h, x2l, x1, x1, mxb, 0, IF_BFP) + F(0xb30c, MDEBR, RRE, Z, f1, e2, new, f1, mdeb, 0, IF_BFP) + F(0xb307, MXDBR, RRE, Z, 0, f2, x1, x1, mxdb, 0, IF_BFP) + F(0xed17, MEEB, RXE, Z, e1, m2_32u, new, e1, meeb, 0, IF_BFP) + F(0xed1c, MDB, RXE, Z, f1, m2_64, new, f1, mdb, 0, IF_BFP) + F(0xed0c, MDEB, RXE, Z, f1, m2_32u, new, f1, mdeb, 0, IF_BFP) + F(0xed07, MXDB, RXE, Z, 0, m2_64, x1, x1, mxdb, 0, IF_BFP) /* MULTIPLY HALFWORD */ C(0x4c00, MH, RX_a, Z, r1_o, m2_16s, new, r1_32, mul, 0) C(0xe37c, MHY, RXY_a, GIE, r1_o, m2_16s, new, r1_32, mul, 0) @@ -674,15 +684,15 @@ C(0xc200, MSGFI, RIL_a, GIE, r1_o, i2, r1, 0, mul, 0) /* MULTIPLY AND ADD */ - C(0xb30e, MAEBR, RRD, Z, e1, e2, new, e1, maeb, 0) - C(0xb31e, MADBR, RRD, Z, f1_o, f2_o, f1, 0, madb, 0) - C(0xed0e, MAEB, RXF, Z, e1, m2_32u, new, e1, maeb, 0) - C(0xed1e, MADB, RXF, Z, f1_o, m2_64, f1, 0, madb, 0) + F(0xb30e, MAEBR, RRD, Z, e1, e2, new, e1, maeb, 0, IF_BFP) + F(0xb31e, MADBR, RRD, Z, f1, f2, new, f1, madb, 0, IF_BFP) + F(0xed0e, MAEB, RXF, Z, e1, m2_32u, new, e1, maeb, 0, IF_BFP) + F(0xed1e, MADB, RXF, Z, f1, m2_64, new, f1, madb, 0, IF_BFP) /* MULTIPLY AND SUBTRACT */ - C(0xb30f, MSEBR, RRD, Z, e1, e2, new, e1, mseb, 0) - C(0xb31f, MSDBR, RRD, Z, f1_o, f2_o, f1, 0, msdb, 0) - C(0xed0f, MSEB, RXF, Z, e1, m2_32u, new, e1, mseb, 0) - C(0xed1f, MSDB, RXF, Z, f1_o, m2_64, f1, 0, msdb, 0) + F(0xb30f, MSEBR, RRD, Z, e1, e2, new, e1, mseb, 0, IF_BFP) + F(0xb31f, MSDBR, RRD, Z, f1, f2, new, f1, msdb, 0, IF_BFP) + F(0xed0f, MSEB, RXF, Z, e1, m2_32u, new, e1, mseb, 0, IF_BFP) + F(0xed1f, MSDB, RXF, Z, f1, m2_64, new, f1, msdb, 0, IF_BFP) /* OR */ C(0x1600, OR, RR_a, Z, r1, r2, new, r1_32, or, nz32) @@ -749,14 +759,14 @@ D(0x010d, SAM31, E, Z, 0, 0, 0, 0, sam, 0, 1) D(0x010e, SAM64, E, Z, 0, 0, 0, 0, sam, 0, 3) /* SET FPC */ - C(0xb384, SFPC, RRE, Z, 0, r1_o, 0, 0, sfpc, 0) + F(0xb384, SFPC, RRE, Z, 0, r1_o, 0, 0, sfpc, 0, IF_BFP) /* SET FPC AND SIGNAL */ - C(0xb385, SFASR, RRE, IEEEE_SIM, 0, r1_o, 0, 0, sfas, 0) + F(0xb385, SFASR, RRE, IEEEE_SIM, 0, r1_o, 0, 0, sfas, 0, IF_DFP) /* SET BFP ROUNDING MODE */ - C(0xb299, SRNM, S, Z, 0, 0, 0, 0, srnm, 0) - C(0xb2b8, SRNMB, S, FPE, 0, 0, 0, 0, srnm, 0) + F(0xb299, SRNM, S, Z, la2, 0, 0, 0, srnm, 0, IF_BFP) + F(0xb2b8, SRNMB, S, FPE, la2, 0, 0, 0, srnmb, 0, IF_BFP) /* SET DFP ROUNDING MODE */ - C(0xb2b9, SRNMT, S, DFPR, 0, 0, 0, 0, srnm, 0) + F(0xb2b9, SRNMT, S, DFPR, la2, 0, 0, 0, srnmt, 0, IF_DFP) /* SET PROGRAM MASK */ C(0x0400, SPM, RR_a, Z, r1, 0, 0, 0, spm, 0) @@ -786,20 +796,20 @@ C(0x8c00, SRDL, RS_a, Z, r1_D32, sh64, new, r1_D32, srl, 0) /* SQUARE ROOT */ - C(0xb314, SQEBR, RRE, Z, 0, e2, new, e1, sqeb, 0) - C(0xb315, SQDBR, RRE, Z, 0, f2_o, f1, 0, sqdb, 0) - C(0xb316, SQXBR, RRE, Z, 0, x2_o, x1, 0, sqxb, 0) - C(0xed14, SQEB, RXE, Z, 0, m2_32u, new, e1, sqeb, 0) - C(0xed15, SQDB, RXE, Z, 0, m2_64, f1, 0, sqdb, 0) + F(0xb314, SQEBR, RRE, Z, 0, e2, new, e1, sqeb, 0, IF_BFP) + F(0xb315, SQDBR, RRE, Z, 0, f2, new, f1, sqdb, 0, IF_BFP) + F(0xb316, SQXBR, RRE, Z, x2h, x2l, new, x1, sqxb, 0, IF_BFP) + F(0xed14, SQEB, RXE, Z, 0, m2_32u, new, e1, sqeb, 0, IF_BFP) + F(0xed15, SQDB, RXE, Z, 0, m2_64, new, f1, sqdb, 0, IF_BFP) /* STORE */ C(0x5000, ST, RX_a, Z, r1_o, a2, 0, 0, st32, 0) C(0xe350, STY, RXY_a, LD, r1_o, a2, 0, 0, st32, 0) C(0xe324, STG, RXY_a, Z, r1_o, a2, 0, 0, st64, 0) - C(0x6000, STD, RX_a, Z, f1_o, a2, 0, 0, st64, 0) - C(0xed67, STDY, RXY_a, LD, f1_o, a2, 0, 0, st64, 0) - C(0x7000, STE, RX_a, Z, e1, a2, 0, 0, st32, 0) - C(0xed66, STEY, RXY_a, LD, e1, a2, 0, 0, st32, 0) + F(0x6000, STD, RX_a, Z, f1, a2, 0, 0, st64, 0, IF_AFP1) + F(0xed67, STDY, RXY_a, LD, f1, a2, 0, 0, st64, 0, IF_AFP1) + F(0x7000, STE, RX_a, Z, e1, a2, 0, 0, st32, 0, IF_AFP1) + F(0xed66, STEY, RXY_a, LD, e1, a2, 0, 0, st32, 0, IF_AFP1) /* STORE RELATIVE LONG */ C(0xc40f, STRL, RIL_b, GIE, r1_o, ri2, 0, 0, st32, 0) C(0xc40b, STGRL, RIL_b, GIE, r1_o, ri2, 0, 0, st64, 0) @@ -831,10 +841,16 @@ C(0xe33e, STRV, RXY_a, Z, la2, r1_32u, new, m1_32, rev32, 0) C(0xe32f, STRVG, RXY_a, Z, la2, r1_o, new, m1_64, rev64, 0) +/* STORE CLOCK */ + C(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0) + C(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0) +/* STORE CLOCK EXTENDED */ + C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0) + /* STORE FACILITY LIST EXTENDED */ C(0xb2b0, STFLE, S, SFLE, 0, a2, 0, 0, stfle, 0) /* STORE FPC */ - C(0xb29c, STFPC, S, Z, 0, a2, new, m2_32, efpc, 0) + F(0xb29c, STFPC, S, Z, 0, a2, new, m2_32, efpc, 0, IF_BFP) /* STORE MULTIPLE */ D(0x9000, STM, RS_a, Z, 0, a2, 0, 0, stm, 0, 4) @@ -858,11 +874,11 @@ C(0xb9e9, SGRK, RRF_a, DO, r2, r3, r1, 0, sub, subs64) C(0xe309, SG, RXY_a, Z, r1, m2_64, r1, 0, sub, subs64) C(0xe319, SGF, RXY_a, Z, r1, m2_32s, r1, 0, sub, subs64) - C(0xb30b, SEBR, RRE, Z, e1, e2, new, e1, seb, f32) - C(0xb31b, SDBR, RRE, Z, f1_o, f2_o, f1, 0, sdb, f64) - C(0xb34b, SXBR, RRE, Z, 0, x2_o, x1, 0, sxb, f128) - C(0xed0b, SEB, RXE, Z, e1, m2_32u, new, e1, seb, f32) - C(0xed1b, SDB, RXE, Z, f1_o, m2_64, f1, 0, sdb, f64) + F(0xb30b, SEBR, RRE, Z, e1, e2, new, e1, seb, f32, IF_BFP) + F(0xb31b, SDBR, RRE, Z, f1, f2, new, f1, sdb, f64, IF_BFP) + F(0xb34b, SXBR, RRE, Z, x2h, x2l, x1, x1, sxb, f128, IF_BFP) + F(0xed0b, SEB, RXE, Z, e1, m2_32u, new, e1, seb, f32, IF_BFP) + F(0xed1b, SDB, RXE, Z, f1, m2_64, new, f1, sdb, f64, IF_BFP) /* SUBTRACT HALFWORD */ C(0x4b00, SH, RX_a, Z, r1, m2_16s, new, r1_32, sub, subs32) C(0xe37b, SHY, RXY_a, LD, r1, m2_16s, new, r1_32, sub, subs32) @@ -901,9 +917,9 @@ C(0x9300, TS, S, Z, 0, a2, 0, 0, ts, 0) /* TEST DATA CLASS */ - C(0xed10, TCEB, RXE, Z, e1, a2, 0, 0, tceb, 0) - C(0xed11, TCDB, RXE, Z, f1_o, a2, 0, 0, tcdb, 0) - C(0xed12, TCXB, RXE, Z, x1_o, a2, 0, 0, tcxb, 0) + F(0xed10, TCEB, RXE, Z, e1, a2, 0, 0, tceb, 0, IF_BFP) + F(0xed11, TCDB, RXE, Z, f1, a2, 0, 0, tcdb, 0, IF_BFP) + F(0xed12, TCXB, RXE, Z, 0, a2, x1, 0, tcxb, 0, IF_BFP) /* TEST DECIMAL */ C(0xebc0, TP, RSL, E2, la1, 0, 0, 0, tp, 0) @@ -956,128 +972,205 @@ D(0xb93e, KIMD, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KIMD) D(0xb93f, KLMD, RRE, MSA, 0, 0, 0, 0, msa, 0, S390_FEAT_TYPE_KLMD) +/* === Vector Support Instructions === */ + +/* VECTOR GATHER ELEMENT */ + E(0xe713, VGEF, VRV, V, la2, 0, 0, 0, vge, 0, ES_32, IF_VEC) + E(0xe712, VGEG, VRV, V, la2, 0, 0, 0, vge, 0, ES_64, IF_VEC) +/* VECTOR GENERATE BYTE MASK */ + F(0xe744, VGBM, VRI_a, V, 0, 0, 0, 0, vgbm, 0, IF_VEC) +/* VECTOR GENERATE MASK */ + F(0xe746, VGM, VRI_b, V, 0, 0, 0, 0, vgm, 0, IF_VEC) +/* VECTOR LOAD */ + F(0xe706, VL, VRX, V, la2, 0, 0, 0, vl, 0, IF_VEC) + F(0xe756, VLR, VRR_a, V, 0, 0, 0, 0, vlr, 0, IF_VEC) +/* VECTOR LOAD AND REPLICATE */ + F(0xe705, VLREP, VRX, V, la2, 0, 0, 0, vlrep, 0, IF_VEC) +/* VECTOR LOAD ELEMENT */ + E(0xe700, VLEB, VRX, V, la2, 0, 0, 0, vle, 0, ES_8, IF_VEC) + E(0xe701, VLEH, VRX, V, la2, 0, 0, 0, vle, 0, ES_16, IF_VEC) + E(0xe703, VLEF, VRX, V, la2, 0, 0, 0, vle, 0, ES_32, IF_VEC) + E(0xe702, VLEG, VRX, V, la2, 0, 0, 0, vle, 0, ES_64, IF_VEC) +/* VECTOR LOAD ELEMENT IMMEDIATE */ + E(0xe740, VLEIB, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_8, IF_VEC) + E(0xe741, VLEIH, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_16, IF_VEC) + E(0xe743, VLEIF, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_32, IF_VEC) + E(0xe742, VLEIG, VRI_a, V, 0, 0, 0, 0, vlei, 0, ES_64, IF_VEC) +/* VECTOR LOAD GR FROM VR ELEMENT */ + F(0xe721, VLGV, VRS_c, V, la2, 0, r1, 0, vlgv, 0, IF_VEC) +/* VECTOR LOAD LOGICAL ELEMENT AND ZERO */ + F(0xe704, VLLEZ, VRX, V, la2, 0, 0, 0, vllez, 0, IF_VEC) +/* VECTOR LOAD MULTIPLE */ + F(0xe736, VLM, VRS_a, V, la2, 0, 0, 0, vlm, 0, IF_VEC) +/* VECTOR LOAD TO BLOCK BOUNDARY */ + F(0xe707, VLBB, VRX, V, la2, 0, 0, 0, vlbb, 0, IF_VEC) +/* VECTOR LOAD VR ELEMENT FROM GR */ + F(0xe722, VLVG, VRS_b, V, la2, r3, 0, 0, vlvg, 0, IF_VEC) +/* VECTOR LOAD VR FROM GRS DISJOINT */ + F(0xe762, VLVGP, VRR_f, V, r2, r3, 0, 0, vlvgp, 0, IF_VEC) +/* VECTOR LOAD WITH LENGTH */ + F(0xe737, VLL, VRS_b, V, la2, r3_32u, 0, 0, vll, 0, IF_VEC) +/* VECTOR MERGE HIGH */ + F(0xe761, VMRH, VRR_c, V, 0, 0, 0, 0, vmr, 0, IF_VEC) +/* VECTOR MERGE LOW */ + F(0xe760, VMRL, VRR_c, V, 0, 0, 0, 0, vmr, 0, IF_VEC) +/* VECTOR PACK */ + F(0xe794, VPK, VRR_c, V, 0, 0, 0, 0, vpk, 0, IF_VEC) +/* VECTOR PACK SATURATE */ + F(0xe797, VPKS, VRR_b, V, 0, 0, 0, 0, vpk, 0, IF_VEC) +/* VECTOR PACK LOGICAL SATURATE */ + F(0xe795, VPKLS, VRR_b, V, 0, 0, 0, 0, vpk, 0, IF_VEC) + F(0xe78c, VPERM, VRR_e, V, 0, 0, 0, 0, vperm, 0, IF_VEC) +/* VECTOR PERMUTE DOUBLEWORD IMMEDIATE */ + F(0xe784, VPDI, VRR_c, V, 0, 0, 0, 0, vpdi, 0, IF_VEC) +/* VECTOR REPLICATE */ + F(0xe74d, VREP, VRI_c, V, 0, 0, 0, 0, vrep, 0, IF_VEC) +/* VECTOR REPLICATE IMMEDIATE */ + F(0xe745, VREPI, VRI_a, V, 0, 0, 0, 0, vrepi, 0, IF_VEC) +/* VECTOR SCATTER ELEMENT */ + E(0xe71b, VSCEF, VRV, V, la2, 0, 0, 0, vsce, 0, ES_32, IF_VEC) + E(0xe71a, VSCEG, VRV, V, la2, 0, 0, 0, vsce, 0, ES_64, IF_VEC) +/* VECTOR SELECT */ + F(0xe78d, VSEL, VRR_e, V, 0, 0, 0, 0, vsel, 0, IF_VEC) +/* VECTOR SIGN EXTEND TO DOUBLEWORD */ + F(0xe75f, VSEG, VRR_a, V, 0, 0, 0, 0, vseg, 0, IF_VEC) +/* VECTOR STORE */ + F(0xe70e, VST, VRX, V, la2, 0, 0, 0, vst, 0, IF_VEC) +/* VECTOR STORE ELEMENT */ + E(0xe708, VSTEB, VRX, V, la2, 0, 0, 0, vste, 0, ES_8, IF_VEC) + E(0xe709, VSTEH, VRX, V, la2, 0, 0, 0, vste, 0, ES_16, IF_VEC) + E(0xe70b, VSTEF, VRX, V, la2, 0, 0, 0, vste, 0, ES_32, IF_VEC) + E(0xe70a, VSTEG, VRX, V, la2, 0, 0, 0, vste, 0, ES_64, IF_VEC) +/* VECTOR STORE MULTIPLE */ + F(0xe73e, VSTM, VRS_a, V, la2, 0, 0, 0, vstm, 0, IF_VEC) +/* VECTOR STORE WITH LENGTH */ + F(0xe73f, VSTL, VRS_b, V, la2, r3_32u, 0, 0, vstl, 0, IF_VEC) +/* VECTOR UNPACK HIGH */ + F(0xe7d7, VUPH, VRR_a, V, 0, 0, 0, 0, vup, 0, IF_VEC) +/* VECTOR UNPACK LOGICAL HIGH */ + F(0xe7d5, VUPLH, VRR_a, V, 0, 0, 0, 0, vup, 0, IF_VEC) +/* VECTOR UNPACK LOW */ + F(0xe7d6, VUPL, VRR_a, V, 0, 0, 0, 0, vup, 0, IF_VEC) +/* VECTOR UNPACK LOGICAL LOW */ + F(0xe7d4, VUPLL, VRR_a, V, 0, 0, 0, 0, vup, 0, IF_VEC) + #ifndef CONFIG_USER_ONLY /* COMPARE AND SWAP AND PURGE */ - D(0xb250, CSP, RRE, Z, r1_32u, ra2, r1_P, 0, csp, 0, MO_TEUL) - D(0xb98a, CSPG, RRE, DAT_ENH, r1_o, ra2, r1_P, 0, csp, 0, MO_TEQ) + E(0xb250, CSP, RRE, Z, r1_32u, ra2, r1_P, 0, csp, 0, MO_TEUL, IF_PRIV) + E(0xb98a, CSPG, RRE, DAT_ENH, r1_o, ra2, r1_P, 0, csp, 0, MO_TEQ, IF_PRIV) /* DIAGNOSE (KVM hypercall) */ - C(0x8300, DIAG, RSI, Z, 0, 0, 0, 0, diag, 0) + F(0x8300, DIAG, RSI, Z, 0, 0, 0, 0, diag, 0, IF_PRIV) /* INSERT STORAGE KEY EXTENDED */ - C(0xb229, ISKE, RRE, Z, 0, r2_o, new, r1_8, iske, 0) + F(0xb229, ISKE, RRE, Z, 0, r2_o, new, r1_8, iske, 0, IF_PRIV) /* INVALIDATE DAT TABLE ENTRY */ - C(0xb98e, IPDE, RRF_b, Z, r1_o, r2_o, 0, 0, idte, 0) + F(0xb98e, IPDE, RRF_b, Z, r1_o, r2_o, 0, 0, idte, 0, IF_PRIV) /* INVALIDATE PAGE TABLE ENTRY */ - C(0xb221, IPTE, RRF_a, Z, r1_o, r2_o, 0, 0, ipte, 0) + F(0xb221, IPTE, RRF_a, Z, r1_o, r2_o, 0, 0, ipte, 0, IF_PRIV) /* LOAD CONTROL */ - C(0xb700, LCTL, RS_a, Z, 0, a2, 0, 0, lctl, 0) - C(0xeb2f, LCTLG, RSY_a, Z, 0, a2, 0, 0, lctlg, 0) + F(0xb700, LCTL, RS_a, Z, 0, a2, 0, 0, lctl, 0, IF_PRIV) + F(0xeb2f, LCTLG, RSY_a, Z, 0, a2, 0, 0, lctlg, 0, IF_PRIV) /* LOAD PROGRAM PARAMETER */ - C(0xb280, LPP, S, LPP, 0, m2_64, 0, 0, lpp, 0) + F(0xb280, LPP, S, LPP, 0, m2_64, 0, 0, lpp, 0, IF_PRIV) /* LOAD PSW */ - C(0x8200, LPSW, S, Z, 0, a2, 0, 0, lpsw, 0) + F(0x8200, LPSW, S, Z, 0, a2, 0, 0, lpsw, 0, IF_PRIV) /* LOAD PSW EXTENDED */ - C(0xb2b2, LPSWE, S, Z, 0, a2, 0, 0, lpswe, 0) + F(0xb2b2, LPSWE, S, Z, 0, a2, 0, 0, lpswe, 0, IF_PRIV) /* LOAD REAL ADDRESS */ - C(0xb100, LRA, RX_a, Z, 0, a2, r1, 0, lra, 0) - C(0xe313, LRAY, RXY_a, LD, 0, a2, r1, 0, lra, 0) - C(0xe303, LRAG, RXY_a, Z, 0, a2, r1, 0, lra, 0) + F(0xb100, LRA, RX_a, Z, 0, a2, r1, 0, lra, 0, IF_PRIV) + F(0xe313, LRAY, RXY_a, LD, 0, a2, r1, 0, lra, 0, IF_PRIV) + F(0xe303, LRAG, RXY_a, Z, 0, a2, r1, 0, lra, 0, IF_PRIV) /* LOAD USING REAL ADDRESS */ - C(0xb24b, LURA, RRE, Z, 0, r2, new, r1_32, lura, 0) - C(0xb905, LURAG, RRE, Z, 0, r2, r1, 0, lurag, 0) + F(0xb24b, LURA, RRE, Z, 0, r2, new, r1_32, lura, 0, IF_PRIV) + F(0xb905, LURAG, RRE, Z, 0, r2, r1, 0, lurag, 0, IF_PRIV) /* MOVE TO PRIMARY */ - C(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0) + F(0xda00, MVCP, SS_d, Z, la1, a2, 0, 0, mvcp, 0, IF_PRIV) /* MOVE TO SECONDARY */ - C(0xdb00, MVCS, SS_d, Z, la1, a2, 0, 0, mvcs, 0) + F(0xdb00, MVCS, SS_d, Z, la1, a2, 0, 0, mvcs, 0, IF_PRIV) /* PURGE TLB */ - C(0xb20d, PTLB, S, Z, 0, 0, 0, 0, ptlb, 0) + F(0xb20d, PTLB, S, Z, 0, 0, 0, 0, ptlb, 0, IF_PRIV) /* RESET REFERENCE BIT EXTENDED */ - C(0xb22a, RRBE, RRE, Z, 0, r2_o, 0, 0, rrbe, 0) + F(0xb22a, RRBE, RRE, Z, 0, r2_o, 0, 0, rrbe, 0, IF_PRIV) /* SERVICE CALL LOGICAL PROCESSOR (PV hypercall) */ - C(0xb220, SERVC, RRE, Z, r1_o, r2_o, 0, 0, servc, 0) + F(0xb220, SERVC, RRE, Z, r1_o, r2_o, 0, 0, servc, 0, IF_PRIV) /* SET ADDRESS SPACE CONTROL FAST */ - C(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0) + F(0xb279, SACF, S, Z, 0, a2, 0, 0, sacf, 0, IF_PRIV) /* SET CLOCK */ - C(0xb204, SCK, S, Z, la2, 0, 0, 0, sck, 0) + F(0xb204, SCK, S, Z, la2, 0, 0, 0, sck, 0, IF_PRIV) /* SET CLOCK COMPARATOR */ - C(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0) + F(0xb206, SCKC, S, Z, 0, m2_64a, 0, 0, sckc, 0, IF_PRIV) /* SET CLOCK PROGRAMMABLE FIELD */ - C(0x0107, SCKPF, E, Z, 0, 0, 0, 0, sckpf, 0) + F(0x0107, SCKPF, E, Z, 0, 0, 0, 0, sckpf, 0, IF_PRIV) /* SET CPU TIMER */ - C(0xb208, SPT, S, Z, 0, m2_64a, 0, 0, spt, 0) + F(0xb208, SPT, S, Z, 0, m2_64a, 0, 0, spt, 0, IF_PRIV) /* SET PREFIX */ - C(0xb210, SPX, S, Z, 0, m2_32ua, 0, 0, spx, 0) + F(0xb210, SPX, S, Z, 0, m2_32ua, 0, 0, spx, 0, IF_PRIV) /* SET PSW KEY FROM ADDRESS */ - C(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0) + F(0xb20a, SPKA, S, Z, 0, a2, 0, 0, spka, 0, IF_PRIV) /* SET STORAGE KEY EXTENDED */ - C(0xb22b, SSKE, RRF_c, Z, r1_o, r2_o, 0, 0, sske, 0) + F(0xb22b, SSKE, RRF_c, Z, r1_o, r2_o, 0, 0, sske, 0, IF_PRIV) /* SET SYSTEM MASK */ - C(0x8000, SSM, S, Z, 0, m2_8u, 0, 0, ssm, 0) + F(0x8000, SSM, S, Z, 0, m2_8u, 0, 0, ssm, 0, IF_PRIV) /* SIGNAL PROCESSOR */ - C(0xae00, SIGP, RS_a, Z, 0, a2, 0, 0, sigp, 0) -/* STORE CLOCK */ - C(0xb205, STCK, S, Z, la2, 0, new, m1_64, stck, 0) - C(0xb27c, STCKF, S, SCF, la2, 0, new, m1_64, stck, 0) -/* STORE CLOCK EXTENDED */ - C(0xb278, STCKE, S, Z, 0, a2, 0, 0, stcke, 0) + F(0xae00, SIGP, RS_a, Z, 0, a2, 0, 0, sigp, 0, IF_PRIV) /* STORE CLOCK COMPARATOR */ - C(0xb207, STCKC, S, Z, la2, 0, new, m1_64a, stckc, 0) + F(0xb207, STCKC, S, Z, la2, 0, new, m1_64a, stckc, 0, IF_PRIV) /* STORE CONTROL */ - C(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0) - C(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0) + F(0xb600, STCTL, RS_a, Z, 0, a2, 0, 0, stctl, 0, IF_PRIV) + F(0xeb25, STCTG, RSY_a, Z, 0, a2, 0, 0, stctg, 0, IF_PRIV) /* STORE CPU ADDRESS */ - C(0xb212, STAP, S, Z, la2, 0, new, m1_16a, stap, 0) + F(0xb212, STAP, S, Z, la2, 0, new, m1_16a, stap, 0, IF_PRIV) /* STORE CPU ID */ - C(0xb202, STIDP, S, Z, la2, 0, new, m1_64a, stidp, 0) + F(0xb202, STIDP, S, Z, la2, 0, new, m1_64a, stidp, 0, IF_PRIV) /* STORE CPU TIMER */ - C(0xb209, STPT, S, Z, la2, 0, new, m1_64a, stpt, 0) + F(0xb209, STPT, S, Z, la2, 0, new, m1_64a, stpt, 0, IF_PRIV) /* STORE FACILITY LIST */ - C(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0) + F(0xb2b1, STFL, S, Z, 0, 0, 0, 0, stfl, 0, IF_PRIV) /* STORE PREFIX */ - C(0xb211, STPX, S, Z, la2, 0, new, m1_32a, stpx, 0) + F(0xb211, STPX, S, Z, la2, 0, new, m1_32a, stpx, 0, IF_PRIV) /* STORE SYSTEM INFORMATION */ - C(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0) + F(0xb27d, STSI, S, Z, 0, a2, 0, 0, stsi, 0, IF_PRIV) /* STORE THEN AND SYSTEM MASK */ - C(0xac00, STNSM, SI, Z, la1, 0, 0, 0, stnosm, 0) + F(0xac00, STNSM, SI, Z, la1, 0, 0, 0, stnosm, 0, IF_PRIV) /* STORE THEN OR SYSTEM MASK */ - C(0xad00, STOSM, SI, Z, la1, 0, 0, 0, stnosm, 0) + F(0xad00, STOSM, SI, Z, la1, 0, 0, 0, stnosm, 0, IF_PRIV) /* STORE USING REAL ADDRESS */ - C(0xb246, STURA, RRE, Z, r1_o, r2_o, 0, 0, stura, 0) - C(0xb925, STURG, RRE, Z, r1_o, r2_o, 0, 0, sturg, 0) + F(0xb246, STURA, RRE, Z, r1_o, r2_o, 0, 0, stura, 0, IF_PRIV) + F(0xb925, STURG, RRE, Z, r1_o, r2_o, 0, 0, sturg, 0, IF_PRIV) /* TEST BLOCK */ - C(0xb22c, TB, RRE, Z, 0, r2_o, 0, 0, testblock, 0) + F(0xb22c, TB, RRE, Z, 0, r2_o, 0, 0, testblock, 0, IF_PRIV) /* TEST PROTECTION */ C(0xe501, TPROT, SSE, Z, la1, a2, 0, 0, tprot, 0) /* CCW I/O Instructions */ - C(0xb276, XSCH, S, Z, 0, 0, 0, 0, xsch, 0) - C(0xb230, CSCH, S, Z, 0, 0, 0, 0, csch, 0) - C(0xb231, HSCH, S, Z, 0, 0, 0, 0, hsch, 0) - C(0xb232, MSCH, S, Z, 0, insn, 0, 0, msch, 0) - C(0xb23b, RCHP, S, Z, 0, 0, 0, 0, rchp, 0) - C(0xb238, RSCH, S, Z, 0, 0, 0, 0, rsch, 0) - C(0xb237, SAL, S, Z, 0, 0, 0, 0, sal, 0) - C(0xb23c, SCHM, S, Z, 0, insn, 0, 0, schm, 0) - C(0xb274, SIGA, S, Z, 0, 0, 0, 0, siga, 0) - C(0xb23a, STCPS, S, Z, 0, 0, 0, 0, stcps, 0) - C(0xb233, SSCH, S, Z, 0, insn, 0, 0, ssch, 0) - C(0xb239, STCRW, S, Z, 0, insn, 0, 0, stcrw, 0) - C(0xb234, STSCH, S, Z, 0, insn, 0, 0, stsch, 0) - C(0xb236, TPI , S, Z, la2, 0, 0, 0, tpi, 0) - C(0xb235, TSCH, S, Z, 0, insn, 0, 0, tsch, 0) + F(0xb276, XSCH, S, Z, 0, 0, 0, 0, xsch, 0, IF_PRIV) + F(0xb230, CSCH, S, Z, 0, 0, 0, 0, csch, 0, IF_PRIV) + F(0xb231, HSCH, S, Z, 0, 0, 0, 0, hsch, 0, IF_PRIV) + F(0xb232, MSCH, S, Z, 0, insn, 0, 0, msch, 0, IF_PRIV) + F(0xb23b, RCHP, S, Z, 0, 0, 0, 0, rchp, 0, IF_PRIV) + F(0xb238, RSCH, S, Z, 0, 0, 0, 0, rsch, 0, IF_PRIV) + F(0xb237, SAL, S, Z, 0, 0, 0, 0, sal, 0, IF_PRIV) + F(0xb23c, SCHM, S, Z, 0, insn, 0, 0, schm, 0, IF_PRIV) + F(0xb274, SIGA, S, Z, 0, 0, 0, 0, siga, 0, IF_PRIV) + F(0xb23a, STCPS, S, Z, 0, 0, 0, 0, stcps, 0, IF_PRIV) + F(0xb233, SSCH, S, Z, 0, insn, 0, 0, ssch, 0, IF_PRIV) + F(0xb239, STCRW, S, Z, 0, insn, 0, 0, stcrw, 0, IF_PRIV) + F(0xb234, STSCH, S, Z, 0, insn, 0, 0, stsch, 0, IF_PRIV) + F(0xb236, TPI , S, Z, la2, 0, 0, 0, tpi, 0, IF_PRIV) + F(0xb235, TSCH, S, Z, 0, insn, 0, 0, tsch, 0, IF_PRIV) /* ??? Not listed in PoO ninth edition, but there's a linux driver that uses it: "A CHSC subchannel is usually present on LPAR only." */ - C(0xb25f, CHSC, RRE, Z, 0, insn, 0, 0, chsc, 0) + F(0xb25f, CHSC, RRE, Z, 0, insn, 0, 0, chsc, 0, IF_PRIV) /* zPCI Instructions */ /* None of these instructions are documented in the PoP, so this is all based upon target/s390x/kvm.c and Linux code and likely incomplete */ - C(0xebd0, PCISTB, RSY_a, PCI, la2, 0, 0, 0, pcistb, 0) - C(0xebd1, SIC, RSY_a, AIS, r1, r3, 0, 0, sic, 0) - C(0xb9a0, CLP, RRF_c, PCI, 0, 0, 0, 0, clp, 0) - C(0xb9d0, PCISTG, RRE, PCI, 0, 0, 0, 0, pcistg, 0) - C(0xb9d2, PCILG, RRE, PCI, 0, 0, 0, 0, pcilg, 0) - C(0xb9d3, RPCIT, RRE, PCI, 0, 0, 0, 0, rpcit, 0) - C(0xe3d0, MPCIFC, RXY_a, PCI, la2, 0, 0, 0, mpcifc, 0) - C(0xe3d4, STPCIFC, RXY_a, PCI, la2, 0, 0, 0, stpcifc, 0) + F(0xebd0, PCISTB, RSY_a, PCI, la2, 0, 0, 0, pcistb, 0, IF_PRIV) + F(0xebd1, SIC, RSY_a, AIS, r1, r3, 0, 0, sic, 0, IF_PRIV) + F(0xb9a0, CLP, RRF_c, PCI, 0, 0, 0, 0, clp, 0, IF_PRIV) + F(0xb9d0, PCISTG, RRE, PCI, 0, 0, 0, 0, pcistg, 0, IF_PRIV) + F(0xb9d2, PCILG, RRE, PCI, 0, 0, 0, 0, pcilg, 0, IF_PRIV) + F(0xb9d3, RPCIT, RRE, PCI, 0, 0, 0, 0, rpcit, 0, IF_PRIV) + F(0xe3d0, MPCIFC, RXY_a, PCI, la2, 0, 0, 0, mpcifc, 0, IF_PRIV) + F(0xe3d4, STPCIFC, RXY_a, PCI, la2, 0, 0, 0, stpcifc, 0, IF_PRIV) #endif /* CONFIG_USER_ONLY */ diff --git a/target/s390x/insn-format.def b/target/s390x/insn-format.def index a412d90fb7..6253edbd19 100644 --- a/target/s390x/insn-format.def +++ b/target/s390x/insn-format.def @@ -36,7 +36,7 @@ F3(RSY_a, R(1, 8), BDL(2), R(3,12)) F3(RSY_b, R(1, 8), BDL(2), M(3,12)) F2(RX_a, R(1, 8), BXD(2)) F2(RX_b, M(1, 8), BXD(2)) -F2(RXE, R(1, 8), BXD(2)) +F3(RXE, R(1, 8), BXD(2), M(3,32)) F3(RXF, R(1,32), BXD(2), R(3, 8)) F2(RXY_a, R(1, 8), BXDL(2)) F2(RXY_b, M(1, 8), BXDL(2)) @@ -54,3 +54,28 @@ F4(SS_e, R(1, 8), BD(2,16,20), R(3,12), BD(4,32,36)) F3(SS_f, BD(1,16,20), L(2,8,8), BD(2,32,36)) F2(SSE, BD(1,16,20), BD(2,32,36)) F3(SSF, BD(1,16,20), BD(2,32,36), R(3,8)) +F3(VRI_a, V(1,8), I(2,16,16), M(3,32)) +F4(VRI_b, V(1,8), I(2,16,8), I(3,24,8), M(4,32)) +F4(VRI_c, V(1,8), V(3,12), I(2,16,16), M(4,32)) +F5(VRI_d, V(1,8), V(2,12), V(3,16), I(4,24,8), M(5,32)) +F5(VRI_e, V(1,8), V(2,12), I(3,16,12), M(5,28), M(4,32)) +F5(VRI_f, V(1,8), V(2,12), V(3,16), M(5,24), I(4,28,8)) +F5(VRI_g, V(1,8), V(2,12), I(4,16,8), M(5,24), I(3,28,8)) +F3(VRI_h, V(1,8), I(2,16,16), I(3,32,4)) +F4(VRI_i, V(1,8), R(2,12), M(4,24), I(3,28,8)) +F5(VRR_a, V(1,8), V(2,12), M(5,24), M(4,28), M(3,32)) +F5(VRR_b, V(1,8), V(2,12), V(3,16), M(5,24), M(4,32)) +F6(VRR_c, V(1,8), V(2,12), V(3,16), M(6,24), M(5,28), M(4,32)) +F6(VRR_d, V(1,8), V(2,12), V(3,16), M(5,20), M(6,24), V(4,32)) +F6(VRR_e, V(1,8), V(2,12), V(3,16), M(6,20), M(5,28), V(4,32)) +F3(VRR_f, V(1,8), R(2,12), R(3,16)) +F1(VRR_g, V(1,12)) +F3(VRR_h, V(1,12), V(2,16), M(3,24)) +F3(VRR_i, R(1,8), V(2,12), M(3,24)) +F4(VRS_a, V(1,8), V(3,12), BD(2,16,20), M(4,32)) +F4(VRS_b, V(1,8), R(3,12), BD(2,16,20), M(4,32)) +F4(VRS_c, R(1,8), V(3,12), BD(2,16,20), M(4,32)) +F3(VRS_d, R(3,12), BD(2,16,20), V(1,32)) +F4(VRV, V(1,8), V(2,12), BD(2,16,20), M(3,32)) +F3(VRX, V(1,8), BXD(2), M(3,32)) +F3(VSI, I(3,8,8), BD(2,16,20), V(1,32)) diff --git a/target/s390x/int_helper.c b/target/s390x/int_helper.c index abf77a94e6..abbbc20d9c 100644 --- a/target/s390x/int_helper.c +++ b/target/s390x/int_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 diff --git a/target/s390x/internal.h b/target/s390x/internal.h index f2a771e2b4..3b4855c175 100644 --- a/target/s390x/internal.h +++ b/target/s390x/internal.h @@ -63,45 +63,9 @@ typedef struct LowCore { PSW program_new_psw; /* 0x1d0 */ PSW mcck_new_psw; /* 0x1e0 */ PSW io_new_psw; /* 0x1f0 */ - PSW return_psw; /* 0x200 */ - uint8_t irb[64]; /* 0x210 */ - uint64_t sync_enter_timer; /* 0x250 */ - uint64_t async_enter_timer; /* 0x258 */ - uint64_t exit_timer; /* 0x260 */ - uint64_t last_update_timer; /* 0x268 */ - uint64_t user_timer; /* 0x270 */ - uint64_t system_timer; /* 0x278 */ - uint64_t last_update_clock; /* 0x280 */ - uint64_t steal_clock; /* 0x288 */ - PSW return_mcck_psw; /* 0x290 */ - uint8_t pad9[0xc00 - 0x2a0]; /* 0x2a0 */ - /* System info area */ - uint64_t save_area[16]; /* 0xc00 */ - uint8_t pad10[0xd40 - 0xc80]; /* 0xc80 */ - uint64_t kernel_stack; /* 0xd40 */ - uint64_t thread_info; /* 0xd48 */ - uint64_t async_stack; /* 0xd50 */ - uint64_t kernel_asce; /* 0xd58 */ - uint64_t user_asce; /* 0xd60 */ - uint64_t panic_stack; /* 0xd68 */ - uint64_t user_exec_asce; /* 0xd70 */ - uint8_t pad11[0xdc0 - 0xd78]; /* 0xd78 */ + uint8_t pad13[0x11b0 - 0x200]; /* 0x200 */ - /* SMP info area: defined by DJB */ - uint64_t clock_comparator; /* 0xdc0 */ - uint64_t ext_call_fast; /* 0xdc8 */ - uint64_t percpu_offset; /* 0xdd0 */ - uint64_t current_task; /* 0xdd8 */ - uint32_t softirq_pending; /* 0xde0 */ - uint32_t pad_0x0de4; /* 0xde4 */ - uint64_t int_clock; /* 0xde8 */ - uint8_t pad12[0xe00 - 0xdf0]; /* 0xdf0 */ - - /* 0xe00 is used as indicator for dump tools */ - /* whether the kernel died with panic() or not */ - uint32_t panic_magic; /* 0xe00 */ - - uint8_t pad13[0x11b8 - 0xe04]; /* 0xe04 */ + uint64_t mcesad; /* 0x11B0 */ /* 64 bit extparam used for pfault, diag 250 etc */ uint64_t ext_params2; /* 0x11B8 */ @@ -128,6 +92,7 @@ typedef struct LowCore { uint8_t pad18[0x2000 - 0x1400]; /* 0x1400 */ } QEMU_PACKED LowCore; +QEMU_BUILD_BUG_ON(sizeof(LowCore) != 8192); #endif /* CONFIG_USER_ONLY */ #define MAX_ILEN 6 @@ -234,6 +199,7 @@ enum cc_op { CC_OP_SLA_32, /* Calculate shift left signed (32bit) */ CC_OP_SLA_64, /* Calculate shift left signed (64bit) */ CC_OP_FLOGR, /* find leftmost one */ + CC_OP_LCBB, /* load count to block boundary */ CC_OP_MAX }; @@ -308,6 +274,15 @@ void s390x_cpu_do_unaligned_access(CPUState *cs, vaddr addr, uint32_t set_cc_nz_f32(float32 v); uint32_t set_cc_nz_f64(float64 v); uint32_t set_cc_nz_f128(float128 v); +#define S390_IEEE_MASK_INVALID 0x80 +#define S390_IEEE_MASK_DIVBYZERO 0x40 +#define S390_IEEE_MASK_OVERFLOW 0x20 +#define S390_IEEE_MASK_UNDERFLOW 0x10 +#define S390_IEEE_MASK_INEXACT 0x08 +#define S390_IEEE_MASK_QUANTUM 0x04 +uint8_t s390_softfloat_exc_to_ieee(unsigned int exc); +int s390_swap_bfp_rounding_mode(CPUS390XState *env, int m3); +void s390_restore_bfp_rounding_mode(CPUS390XState *env, int old_mode); /* gdbstub.c */ @@ -374,6 +349,8 @@ void ioinst_handle_sal(S390CPU *cpu, uint64_t reg1, uintptr_t ra); /* mem_helper.c */ target_ulong mmu_real2abs(CPUS390XState *env, target_ulong raddr); +void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len, + uintptr_t ra); /* mmu_helper.c */ diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index 25cfb3eef8..a17eff5ebc 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -15,6 +15,7 @@ #include "exec/exec-all.h" #include "sysemu/kvm.h" #include "hw/s390x/ioinst.h" +#include "tcg_s390x.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/s390_flic.h" #endif @@ -29,25 +30,11 @@ void trigger_pgm_exception(CPUS390XState *env, uint32_t code, uint32_t ilen) env->int_pgm_ilen = ilen; } -static void tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, - int ilen, uintptr_t ra) -{ -#ifdef CONFIG_TCG - trigger_pgm_exception(env, code, ilen); - cpu_loop_exit_restore(CPU(s390_env_get_cpu(env)), ra); -#else - g_assert_not_reached(); -#endif -} - void s390_program_interrupt(CPUS390XState *env, uint32_t code, int ilen, uintptr_t ra) { S390CPU *cpu = s390_env_get_cpu(env); - qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", - env->psw.addr); - if (kvm_enabled()) { kvm_s390_program_interrupt(cpu, code); } else if (tcg_enabled()) { diff --git a/target/s390x/kvm.c b/target/s390x/kvm.c index d923cf4240..19530fb94e 100644 --- a/target/s390x/kvm.c +++ b/target/s390x/kvm.c @@ -4,21 +4,18 @@ * Copyright (c) 2009 Alexander Graf * Copyright IBM Corp. 2012 * - * 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 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. + * General Public License for more details. * - * Contributions after 2012-10-29 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - * - * You should have received a copy of the GNU (Lesser) General Public - * License along with this library; if not, see . + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . */ #include "qemu/osdep.h" @@ -34,11 +31,15 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/timer.h" +#include "qemu/units.h" +#include "qemu/mmap-alloc.h" +#include "qemu/log.h" #include "sysemu/sysemu.h" #include "sysemu/hw_accel.h" #include "hw/hw.h" #include "sysemu/device_tree.h" #include "exec/gdbstub.h" +#include "exec/ram_addr.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" @@ -139,6 +140,7 @@ static int cap_mem_op; static int cap_s390_irq; static int cap_ri; static int cap_gs; +static int cap_hpage_1m; static int active_cmma; @@ -220,9 +222,9 @@ static void kvm_s390_enable_cmma(void) .attr = KVM_S390_VM_MEM_ENABLE_CMMA, }; - if (mem_path) { + if (cap_hpage_1m) { warn_report("CMM will not be enabled because it is not " - "compatible with hugetlbfs."); + "compatible with huge memory backings."); return; } rc = kvm_vm_ioctl(kvm_state, KVM_SET_DEVICE_ATTR, &attr); @@ -281,10 +283,44 @@ void kvm_s390_crypto_reset(void) } } +static int kvm_s390_configure_mempath_backing(KVMState *s) +{ + size_t path_psize = qemu_getrampagesize(); + + if (path_psize == 4 * KiB) { + return 0; + } + + if (!hpage_1m_allowed()) { + error_report("This QEMU machine does not support huge page " + "mappings"); + return -EINVAL; + } + + if (path_psize != 1 * MiB) { + error_report("Memory backing with 2G pages was specified, " + "but KVM does not support this memory backing"); + return -EINVAL; + } + + if (kvm_vm_enable_cap(s, KVM_CAP_S390_HPAGE_1M, 0)) { + error_report("Memory backing with 1M pages was specified, " + "but KVM does not support this memory backing"); + return -EINVAL; + } + + cap_hpage_1m = 1; + return 0; +} + int kvm_arch_init(MachineState *ms, KVMState *s) { MachineClass *mc = MACHINE_GET_CLASS(ms); + if (kvm_s390_configure_mempath_backing(s)) { + return -EINVAL; + } + mc->default_cpu_type = S390_CPU_TYPE_NAME("host"); cap_sync_regs = kvm_check_extension(s, KVM_CAP_SYNC_REGS); cap_async_pf = kvm_check_extension(s, KVM_CAP_ASYNC_PF); @@ -493,6 +529,12 @@ int kvm_arch_put_registers(CPUState *cs, int level) cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_BPBC; } + if (can_sync_regs(cs, KVM_SYNC_ETOKEN)) { + cs->kvm_run->s.regs.etoken = env->etoken; + cs->kvm_run->s.regs.etoken_extension = env->etoken_extension; + cs->kvm_run->kvm_dirty_regs |= KVM_SYNC_ETOKEN; + } + /* Finally the prefix */ if (can_sync_regs(cs, KVM_SYNC_PREFIX)) { cs->kvm_run->s.regs.prefix = env->psa; @@ -607,6 +649,11 @@ int kvm_arch_get_registers(CPUState *cs) env->bpbc = cs->kvm_run->s.regs.bpbc; } + if (can_sync_regs(cs, KVM_SYNC_ETOKEN)) { + env->etoken = cs->kvm_run->s.regs.etoken; + env->etoken_extension = cs->kvm_run->s.regs.etoken_extension; + } + /* pfault parameters */ if (can_sync_regs(cs, KVM_SYNC_PFAULT)) { env->pfault_token = cs->kvm_run->s.regs.pft; @@ -1067,7 +1114,8 @@ void kvm_s390_program_interrupt(S390CPU *cpu, uint16_t code) .type = KVM_S390_PROGRAM_INT, .u.pgm.code = code, }; - + qemu_log_mask(CPU_LOG_INT, "program interrupt at %#" PRIx64 "\n", + cpu->env.psw.addr); kvm_s390_vcpu_interrupt(cpu, &irq); } @@ -1838,6 +1886,8 @@ int kvm_s390_assign_subch_ioeventfd(EventNotifier *notifier, uint32_t sch, .addr = sch, .len = 8, }; + trace_kvm_assign_subch_ioeventfd(kick.fd, kick.addr, assign, + kick.datamatch); if (!kvm_check_extension(kvm_state, KVM_CAP_IOEVENTFD)) { return -ENOSYS; } @@ -2230,9 +2280,7 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) } /* We emulate a zPCI bus and AEN, therefore we don't need HW support */ - if (pci_available) { - set_bit(S390_FEAT_ZPCI, model->features); - } + set_bit(S390_FEAT_ZPCI, model->features); set_bit(S390_FEAT_ADAPTER_EVENT_NOTIFICATION, model->features); if (s390_known_cpu_type(cpu_type)) { @@ -2249,11 +2297,26 @@ void kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp) error_setg(errp, "KVM: host CPU model could not be identified"); return; } + /* for now, we can only provide the AP feature with HW support */ + if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, + KVM_S390_VM_CRYPTO_ENABLE_APIE)) { + set_bit(S390_FEAT_AP, model->features); + } /* strip of features that are not part of the maximum model */ bitmap_and(model->features, model->features, model->def->full_feat, S390_FEAT_MAX); } +static void kvm_s390_configure_apie(bool interpret) +{ + uint64_t attr = interpret ? KVM_S390_VM_CRYPTO_ENABLE_APIE : + KVM_S390_VM_CRYPTO_DISABLE_APIE; + + if (kvm_vm_check_attr(kvm_state, KVM_S390_VM_CRYPTO, attr)) { + kvm_s390_set_attr(attr); + } +} + void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) { struct kvm_s390_vm_cpu_processor prop = { @@ -2303,6 +2366,10 @@ void kvm_s390_apply_cpu_model(const S390CPUModel *model, Error **errp) if (test_bit(S390_FEAT_CMM, model->features)) { kvm_s390_enable_cmma(); } + + if (test_bit(S390_FEAT_AP, model->features)) { + kvm_s390_configure_apie(true); + } } void kvm_s390_restart_interrupt(S390CPU *cpu) diff --git a/target/s390x/machine.c b/target/s390x/machine.c index bd3230d027..cb792aa103 100644 --- a/target/s390x/machine.c +++ b/target/s390x/machine.c @@ -1,7 +1,7 @@ /* * S390x machine definitions and functions * - * Copyright IBM Corp. 2014 + * Copyright IBM Corp. 2014, 2018 * * Authors: * Thomas Huth @@ -216,6 +216,23 @@ const VMStateDescription vmstate_bpbc = { } }; +static bool etoken_needed(void *opaque) +{ + return s390_has_feat(S390_FEAT_ETOKEN); +} + +const VMStateDescription vmstate_etoken = { + .name = "cpu/etoken", + .version_id = 1, + .minimum_version_id = 1, + .needed = etoken_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64(env.etoken, S390CPU), + VMSTATE_UINT64(env.etoken_extension, S390CPU), + VMSTATE_END_OF_LIST() + } +}; + const VMStateDescription vmstate_s390_cpu = { .name = "cpu", .post_load = cpu_post_load, @@ -251,6 +268,7 @@ const VMStateDescription vmstate_s390_cpu = { &vmstate_exval, &vmstate_gscb, &vmstate_bpbc, + &vmstate_etoken, NULL }, }; diff --git a/target/s390x/mem_helper.c b/target/s390x/mem_helper.c index e21a47fb4d..3f76a8abfd 100644 --- a/target/s390x/mem_helper.c +++ b/target/s390x/mem_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -25,6 +25,7 @@ #include "exec/exec-all.h" #include "exec/cpu_ldst.h" #include "qemu/int128.h" +#include "qemu/atomic128.h" #if !defined(CONFIG_USER_ONLY) #include "hw/s390x/storage-keys.h" @@ -1019,15 +1020,15 @@ void HELPER(pack)(CPUS390XState *env, uint32_t len, uint64_t dest, uint64_t src) len_src--; /* now pack every value */ - while (len_dest >= 0) { + while (len_dest > 0) { b = 0; - if (len_src > 0) { + if (len_src >= 0) { b = cpu_ldub_data_ra(env, src, ra) & 0x0f; src--; len_src--; } - if (len_src > 0) { + if (len_src >= 0) { b |= cpu_ldub_data_ra(env, src, ra) << 4; src--; len_src--; @@ -1299,12 +1300,26 @@ static inline uint32_t do_helper_trt(CPUS390XState *env, int len, return 0; } +static uint32_t do_helper_trt_fwd(CPUS390XState *env, uint32_t len, + uint64_t array, uint64_t trans, + uintptr_t ra) +{ + return do_helper_trt(env, len, array, trans, 1, ra); +} + uint32_t HELPER(trt)(CPUS390XState *env, uint32_t len, uint64_t array, uint64_t trans) { return do_helper_trt(env, len, array, trans, 1, GETPC()); } +static uint32_t do_helper_trt_bkwd(CPUS390XState *env, uint32_t len, + uint64_t array, uint64_t trans, + uintptr_t ra) +{ + return do_helper_trt(env, len, array, trans, -1, ra); +} + uint32_t HELPER(trtr)(CPUS390XState *env, uint32_t len, uint64_t array, uint64_t trans) { @@ -1365,65 +1380,62 @@ uint32_t HELPER(trXX)(CPUS390XState *env, uint32_t r1, uint32_t r2, return cc; } -static void do_cdsg(CPUS390XState *env, uint64_t addr, - uint32_t r1, uint32_t r3, bool parallel) +void HELPER(cdsg)(CPUS390XState *env, uint64_t addr, + uint32_t r1, uint32_t r3) { uintptr_t ra = GETPC(); Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); Int128 oldv; + uint64_t oldh, oldl; bool fail; - if (parallel) { -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); - fail = !int128_eq(oldv, cmpv); -#endif - } else { - uint64_t oldh, oldl; + check_alignment(env, addr, 16, ra); - check_alignment(env, addr, 16, ra); + oldh = cpu_ldq_data_ra(env, addr + 0, ra); + oldl = cpu_ldq_data_ra(env, addr + 8, ra); - oldh = cpu_ldq_data_ra(env, addr + 0, ra); - oldl = cpu_ldq_data_ra(env, addr + 8, ra); - - oldv = int128_make128(oldl, oldh); - fail = !int128_eq(oldv, cmpv); - if (fail) { - newv = oldv; - } - - cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra); - cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra); + oldv = int128_make128(oldl, oldh); + fail = !int128_eq(oldv, cmpv); + if (fail) { + newv = oldv; } + cpu_stq_data_ra(env, addr + 0, int128_gethi(newv), ra); + cpu_stq_data_ra(env, addr + 8, int128_getlo(newv), ra); + env->cc_op = fail; env->regs[r1] = int128_gethi(oldv); env->regs[r1 + 1] = int128_getlo(oldv); } -void HELPER(cdsg)(CPUS390XState *env, uint64_t addr, - uint32_t r1, uint32_t r3) -{ - do_cdsg(env, addr, r1, r3, false); -} - void HELPER(cdsg_parallel)(CPUS390XState *env, uint64_t addr, uint32_t r1, uint32_t r3) { - do_cdsg(env, addr, r1, r3, true); + uintptr_t ra = GETPC(); + Int128 cmpv = int128_make128(env->regs[r1 + 1], env->regs[r1]); + Int128 newv = int128_make128(env->regs[r3 + 1], env->regs[r3]); + int mem_idx; + TCGMemOpIdx oi; + Int128 oldv; + bool fail; + + assert(HAVE_CMPXCHG128); + + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + oldv = helper_atomic_cmpxchgo_be_mmu(env, addr, cmpv, newv, oi, ra); + fail = !int128_eq(oldv, cmpv); + + env->cc_op = fail; + env->regs[r1] = int128_gethi(oldv); + env->regs[r1 + 1] = int128_getlo(oldv); } static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, uint64_t a2, bool parallel) { -#if !defined(CONFIG_USER_ONLY) || defined(CONFIG_ATOMIC128) uint32_t mem_idx = cpu_mmu_index(env, false); -#endif uintptr_t ra = GETPC(); uint32_t fc = extract32(env->regs[0], 0, 8); uint32_t sc = extract32(env->regs[0], 8, 8); @@ -1442,7 +1454,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, } /* Sanity check the alignments. */ - if (extract32(a1, 0, 4 << fc) || extract32(a2, 0, 1 << sc)) { + if (extract32(a1, 0, fc + 2) || extract32(a2, 0, sc)) { goto spec_exception; } @@ -1451,18 +1463,20 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, probe_write(env, a2, 0, mem_idx, ra); #endif - /* Note that the compare-and-swap is atomic, and the store is atomic, but - the complete operation is not. Therefore we do not need to assert serial - context in order to implement this. That said, restart early if we can't - support either operation that is supposed to be atomic. */ + /* + * Note that the compare-and-swap is atomic, and the store is atomic, + * but the complete operation is not. Therefore we do not need to + * assert serial context in order to implement this. That said, + * restart early if we can't support either operation that is supposed + * to be atomic. + */ if (parallel) { - int mask = 0; -#if !defined(CONFIG_ATOMIC64) - mask = -8; -#elif !defined(CONFIG_ATOMIC128) - mask = -16; + uint32_t max = 2; +#ifdef CONFIG_ATOMIC64 + max = 3; #endif - if (((4 << fc) | (1 << sc)) & mask) { + if ((HAVE_CMPXCHG128 ? 0 : fc + 2 > max) || + (HAVE_ATOMIC128 ? 0 : sc > max)) { cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); } } @@ -1532,16 +1546,7 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, Int128 cv = int128_make128(env->regs[r3 + 1], env->regs[r3]); Int128 ov; - if (parallel) { -#ifdef CONFIG_ATOMIC128 - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); - cc = !int128_eq(ov, cv); -#else - /* Note that we asserted !parallel above. */ - g_assert_not_reached(); -#endif - } else { + if (!parallel) { uint64_t oh = cpu_ldq_data_ra(env, a1 + 0, ra); uint64_t ol = cpu_ldq_data_ra(env, a1 + 8, ra); @@ -1553,6 +1558,13 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, cpu_stq_data_ra(env, a1 + 0, int128_gethi(nv), ra); cpu_stq_data_ra(env, a1 + 8, int128_getlo(nv), ra); + } else if (HAVE_CMPXCHG128) { + TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + ov = helper_atomic_cmpxchgo_be_mmu(env, a1, cv, nv, oi, ra); + cc = !int128_eq(ov, cv); + } else { + /* Note that we asserted !parallel above. */ + g_assert_not_reached(); } env->regs[r3 + 0] = int128_gethi(ov); @@ -1582,18 +1594,16 @@ static uint32_t do_csst(CPUS390XState *env, uint32_t r3, uint64_t a1, cpu_stq_data_ra(env, a2, svh, ra); break; case 4: - if (parallel) { -#ifdef CONFIG_ATOMIC128 + if (!parallel) { + cpu_stq_data_ra(env, a2 + 0, svh, ra); + cpu_stq_data_ra(env, a2 + 8, svl, ra); + } else if (HAVE_ATOMIC128) { TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); Int128 sv = int128_make128(svl, svh); helper_atomic_sto_be_mmu(env, a2, sv, oi, ra); -#else + } else { /* Note that we asserted !parallel above. */ g_assert_not_reached(); -#endif - } else { - cpu_stq_data_ra(env, a2 + 0, svh, ra); - cpu_stq_data_ra(env, a2 + 8, svl, ra); } break; default: @@ -2086,76 +2096,64 @@ uint64_t HELPER(lra)(CPUS390XState *env, uint64_t addr) #endif /* load pair from quadword */ -static uint64_t do_lpq(CPUS390XState *env, uint64_t addr, bool parallel) +uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr) { uintptr_t ra = GETPC(); uint64_t hi, lo; - if (parallel) { -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - Int128 v = helper_atomic_ldo_be_mmu(env, addr, oi, ra); - hi = int128_gethi(v); - lo = int128_getlo(v); -#endif - } else { - check_alignment(env, addr, 16, ra); - - hi = cpu_ldq_data_ra(env, addr + 0, ra); - lo = cpu_ldq_data_ra(env, addr + 8, ra); - } + check_alignment(env, addr, 16, ra); + hi = cpu_ldq_data_ra(env, addr + 0, ra); + lo = cpu_ldq_data_ra(env, addr + 8, ra); env->retxl = lo; return hi; } -uint64_t HELPER(lpq)(CPUS390XState *env, uint64_t addr) -{ - return do_lpq(env, addr, false); -} - uint64_t HELPER(lpq_parallel)(CPUS390XState *env, uint64_t addr) { - return do_lpq(env, addr, true); + uintptr_t ra = GETPC(); + uint64_t hi, lo; + int mem_idx; + TCGMemOpIdx oi; + Int128 v; + + assert(HAVE_ATOMIC128); + + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + v = helper_atomic_ldo_be_mmu(env, addr, oi, ra); + hi = int128_gethi(v); + lo = int128_getlo(v); + + env->retxl = lo; + return hi; } /* store pair to quadword */ -static void do_stpq(CPUS390XState *env, uint64_t addr, - uint64_t low, uint64_t high, bool parallel) -{ - uintptr_t ra = GETPC(); - - if (parallel) { -#ifndef CONFIG_ATOMIC128 - cpu_loop_exit_atomic(ENV_GET_CPU(env), ra); -#else - int mem_idx = cpu_mmu_index(env, false); - TCGMemOpIdx oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); - - Int128 v = int128_make128(low, high); - helper_atomic_sto_be_mmu(env, addr, v, oi, ra); -#endif - } else { - check_alignment(env, addr, 16, ra); - - cpu_stq_data_ra(env, addr + 0, high, ra); - cpu_stq_data_ra(env, addr + 8, low, ra); - } -} - void HELPER(stpq)(CPUS390XState *env, uint64_t addr, uint64_t low, uint64_t high) { - do_stpq(env, addr, low, high, false); + uintptr_t ra = GETPC(); + + check_alignment(env, addr, 16, ra); + cpu_stq_data_ra(env, addr + 0, high, ra); + cpu_stq_data_ra(env, addr + 8, low, ra); } void HELPER(stpq_parallel)(CPUS390XState *env, uint64_t addr, uint64_t low, uint64_t high) { - do_stpq(env, addr, low, high, true); + uintptr_t ra = GETPC(); + int mem_idx; + TCGMemOpIdx oi; + Int128 v; + + assert(HAVE_ATOMIC128); + + mem_idx = cpu_mmu_index(env, false); + oi = make_memop_idx(MO_TEQ | MO_ALIGN_16, mem_idx); + v = int128_make128(low, high); + helper_atomic_sto_be_mmu(env, addr, v, oi, ra); } /* Execute instruction. This instruction executes an insn modified with @@ -2193,12 +2191,14 @@ void HELPER(ex)(CPUS390XState *env, uint32_t ilen, uint64_t r1, uint64_t addr) typedef uint32_t (*dx_helper)(CPUS390XState *, uint32_t, uint64_t, uint64_t, uintptr_t); static const dx_helper dx[16] = { + [0x0] = do_helper_trt_bkwd, [0x2] = do_helper_mvc, [0x4] = do_helper_nc, [0x5] = do_helper_clc, [0x6] = do_helper_oc, [0x7] = do_helper_xc, [0xc] = do_helper_tr, + [0xd] = do_helper_trt_fwd, }; dx_helper helper = dx[opc & 0xf]; @@ -2623,3 +2623,29 @@ uint32_t HELPER(cu42)(CPUS390XState *env, uint32_t r1, uint32_t r2, uint32_t m3) return convert_unicode(env, r1, r2, m3, GETPC(), decode_utf32, encode_utf16); } + +void probe_write_access(CPUS390XState *env, uint64_t addr, uint64_t len, + uintptr_t ra) +{ +#ifdef CONFIG_USER_ONLY + if (!h2g_valid(addr) || !h2g_valid(addr + len - 1) || + page_check_range(addr, len, PAGE_WRITE) < 0) { + s390_program_interrupt(env, PGM_ADDRESSING, ILEN_AUTO, ra); + } +#else + /* test the actual access, not just any access to the page due to LAP */ + while (len) { + const uint64_t pagelen = -(addr | -TARGET_PAGE_MASK); + const uint64_t curlen = MIN(pagelen, len); + + probe_write(env, addr, curlen, cpu_mmu_index(env, false), ra); + addr = wrap_address(env, addr + curlen); + len -= curlen; + } +#endif +} + +void HELPER(probe_write_access)(CPUS390XState *env, uint64_t addr, uint64_t len) +{ + probe_write_access(env, addr, len, GETPC()); +} diff --git a/target/s390x/misc_helper.c b/target/s390x/misc_helper.c index 3f91579570..ee67c1fa0c 100644 --- a/target/s390x/misc_helper.c +++ b/target/s390x/misc_helper.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -30,6 +30,7 @@ #include "exec/cpu_ldst.h" #include "qapi/error.h" #include "tcg_s390x.h" +#include "s390-tod.h" #if !defined(CONFIG_USER_ONLY) #include "sysemu/cpus.h" @@ -76,8 +77,28 @@ uint64_t HELPER(stpt)(CPUS390XState *env) #endif } -#ifndef CONFIG_USER_ONLY +/* Store Clock */ +uint64_t HELPER(stck)(CPUS390XState *env) +{ +#ifdef CONFIG_USER_ONLY + struct timespec ts; + uint64_t ns; + clock_gettime(CLOCK_REALTIME, &ts); + ns = ts.tv_sec * NANOSECONDS_PER_SECOND + ts.tv_nsec; + + return TOD_UNIX_EPOCH + time2tod(ns); +#else + S390TODState *td = s390_get_todstate(); + S390TODClass *tdc = S390_TOD_GET_CLASS(td); + S390TOD tod; + + tdc->get(td, &tod, &error_abort); + return tod.low; +#endif +} + +#ifndef CONFIG_USER_ONLY /* SCLP service call */ uint32_t HELPER(servc)(CPUS390XState *env, uint64_t r1, uint64_t r2) { @@ -138,17 +159,6 @@ void HELPER(spx)(CPUS390XState *env, uint64_t a1) tlb_flush_page(cs, TARGET_PAGE_SIZE); } -/* Store Clock */ -uint64_t HELPER(stck)(CPUS390XState *env) -{ - S390TODState *td = s390_get_todstate(); - S390TODClass *tdc = S390_TOD_GET_CLASS(td); - S390TOD tod; - - tdc->get(td, &tod, &error_abort); - return tod.low; -} - static void update_ckc_timer(CPUS390XState *env) { S390TODState *td = s390_get_todstate(); diff --git a/target/s390x/s390-tod.h b/target/s390x/s390-tod.h new file mode 100644 index 0000000000..8b74d6a6d8 --- /dev/null +++ b/target/s390x/s390-tod.h @@ -0,0 +1,29 @@ +/* + * TOD (Time Of Day) clock + * + * Copyright 2018 Red Hat, Inc. + * Author(s): David Hildenbrand + * + * 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 TARGET_S390_TOD_H +#define TARGET_S390_TOD_H + +/* The value of the TOD clock for 1.1.1970. */ +#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL + +/* Converts ns to s390's clock format */ +static inline uint64_t time2tod(uint64_t ns) +{ + return (ns << 9) / 125 + (((ns & 0xff80000000000000ull) / 125) << 9); +} + +/* Converts s390's clock format to ns */ +static inline uint64_t tod2time(uint64_t t) +{ + return ((t >> 9) * 125) + (((t & 0x1ff) * 125) >> 9); +} + +#endif diff --git a/target/s390x/tcg-stub.c b/target/s390x/tcg-stub.c index c93501db0b..32adb7276a 100644 --- a/target/s390x/tcg-stub.c +++ b/target/s390x/tcg-stub.c @@ -18,3 +18,13 @@ void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque) { } +void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, + int ilen, uintptr_t ra) +{ + g_assert_not_reached(); +} +void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, + uintptr_t ra) +{ + g_assert_not_reached(); +} diff --git a/target/s390x/tcg_s390x.h b/target/s390x/tcg_s390x.h index 4e308aa0ce..ab2c4ba703 100644 --- a/target/s390x/tcg_s390x.h +++ b/target/s390x/tcg_s390x.h @@ -14,5 +14,9 @@ #define TCG_S390X_H void tcg_s390_tod_updated(CPUState *cs, run_on_cpu_data opaque); +void QEMU_NORETURN tcg_s390_program_interrupt(CPUS390XState *env, uint32_t code, + int ilen, uintptr_t ra); +void QEMU_NORETURN tcg_s390_data_exception(CPUS390XState *env, uint32_t dxc, + uintptr_t ra); #endif /* TCG_S390X_H */ diff --git a/target/s390x/trace-events b/target/s390x/trace-events index a84e316e49..fda1ee8220 100644 --- a/target/s390x/trace-events +++ b/target/s390x/trace-events @@ -1,24 +1,25 @@ # See docs/devel/tracing.txt for syntax documentation. -# target/s390x/mmu_helper.c +# 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.c ioinst(const char *insn) "IOINST: %s" ioinst_sch_id(const char *insn, int cssid, int ssid, int schid) "IOINST: %s (%x.%x.%04x)" ioinst_chp_id(const char *insn, int cssid, int chpid) "IOINST: %s (%x.%02x)" ioinst_chsc_cmd(uint16_t cmd, uint16_t len) "IOINST: chsc command 0x%04x, len 0x%04x" -# target/s390x/kvm.c +# kvm.c kvm_enable_cmma(int rc) "CMMA: enabling with result code %d" kvm_clear_cmma(int rc) "CMMA: clearing with result code %d" kvm_failed_cpu_state_set(int cpu_index, uint8_t state, const char *msg) "Warning: Unable to set cpu %d state %" PRIu8 " to KVM: %s" +kvm_assign_subch_ioeventfd(int fd, uint32_t addr, bool assign, int datamatch) "fd: %d sch: @0x%x assign: %d vq: %d" -# target/s390x/cpu.c +# cpu.c cpu_set_state(int cpu_index, uint8_t state) "setting cpu %d state to %" PRIu8 cpu_halt(int cpu_index) "halting cpu %d" cpu_unhalt(int cpu_index) "unhalting cpu %d" -# target/s390x/sigp.c +# sigp.c sigp_finished(uint8_t order, int cpu_index, int dst_index, int cc) "SIGP: Finished order %u on cpu %d -> cpu %d with cc=%d" diff --git a/target/s390x/translate.c b/target/s390x/translate.c index 57c03cbf58..0afa8f7ca5 100644 --- a/target/s390x/translate.c +++ b/target/s390x/translate.c @@ -7,7 +7,7 @@ * 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. + * 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 @@ -34,6 +34,7 @@ #include "disas/disas.h" #include "exec/exec-all.h" #include "tcg-op.h" +#include "tcg-op-gvec.h" #include "qemu/log.h" #include "qemu/host-utils.h" #include "exec/cpu_ldst.h" @@ -44,6 +45,7 @@ #include "trace-tcg.h" #include "exec/translator.h" #include "exec/log.h" +#include "qemu/atomic128.h" /* Information that (most) every instruction needs to manipulate. */ @@ -84,14 +86,21 @@ static uint64_t inline_branch_hit[CC_OP_MAX]; static uint64_t inline_branch_miss[CC_OP_MAX]; #endif -static uint64_t pc_to_link_info(DisasContext *s, uint64_t pc) +static void pc_to_link_info(TCGv_i64 out, DisasContext *s, uint64_t pc) { - if (!(s->base.tb->flags & FLAG_MASK_64)) { - if (s->base.tb->flags & FLAG_MASK_32) { - return pc | 0x80000000; + TCGv_i64 tmp; + + if (s->base.tb->flags & FLAG_MASK_32) { + if (s->base.tb->flags & FLAG_MASK_64) { + tcg_gen_movi_i64(out, pc); + return; } + pc |= 0x80000000; } - return pc; + assert(!(s->base.tb->flags & FLAG_MASK_64)); + tmp = tcg_const_i64(pc); + tcg_gen_deposit_i64(out, out, tmp, 0, 32); + tcg_temp_free_i64(tmp); } static TCGv_i64 psw_addr; @@ -103,9 +112,8 @@ static TCGv_i64 cc_src; static TCGv_i64 cc_dst; static TCGv_i64 cc_vr; -static char cpu_reg_names[32][4]; +static char cpu_reg_names[16][4]; static TCGv_i64 regs[16]; -static TCGv_i64 fregs[16]; void s390x_translate_init(void) { @@ -136,13 +144,59 @@ void s390x_translate_init(void) 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(cpu_env, - offsetof(CPUS390XState, vregs[i][0].d), - cpu_reg_names[i + 16]); - } +static inline int vec_full_reg_offset(uint8_t reg) +{ + g_assert(reg < 32); + return offsetof(CPUS390XState, vregs[reg][0].d); +} + +static inline int vec_reg_offset(uint8_t reg, uint8_t enr, TCGMemOp es) +{ + /* Convert element size (es) - e.g. MO_8 - to bytes */ + const uint8_t bytes = 1 << es; + int offs = enr * bytes; + + /* + * vregs[n][0] is the lowest 8 byte and vregs[n][1] the highest 8 byte + * of the 16 byte vector, on both, little and big endian systems. + * + * Big Endian (target/possible host) + * B: [ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7] - [ 8][ 9][10][11][12][13][14][15] + * HW: [ 0][ 1][ 2][ 3] - [ 4][ 5][ 6][ 7] + * W: [ 0][ 1] - [ 2][ 3] + * DW: [ 0] - [ 1] + * + * Little Endian (possible host) + * B: [ 7][ 6][ 5][ 4][ 3][ 2][ 1][ 0] - [15][14][13][12][11][10][ 9][ 8] + * HW: [ 3][ 2][ 1][ 0] - [ 7][ 6][ 5][ 4] + * W: [ 1][ 0] - [ 3][ 2] + * DW: [ 0] - [ 1] + * + * For 16 byte elements, the two 8 byte halves will not form a host + * int128 if the host is little endian, since they're in the wrong order. + * Some operations (e.g. xor) do not care. For operations like addition, + * the two 8 byte elements have to be loaded separately. Let's force all + * 16 byte operations to handle it in a special way. + */ + g_assert(es <= MO_64); +#ifndef HOST_WORDS_BIGENDIAN + offs ^= (8 - bytes); +#endif + return offs + vec_full_reg_offset(reg); +} + +static inline int freg64_offset(uint8_t reg) +{ + g_assert(reg < 16); + return vec_reg_offset(reg, 0, MO_64); +} + +static inline int freg32_offset(uint8_t reg) +{ + g_assert(reg < 16); + return vec_reg_offset(reg, 0, MO_32); } static TCGv_i64 load_reg(int reg) @@ -152,10 +206,19 @@ static TCGv_i64 load_reg(int reg) return r; } +static TCGv_i64 load_freg(int reg) +{ + TCGv_i64 r = tcg_temp_new_i64(); + + tcg_gen_ld_i64(r, cpu_env, freg64_offset(reg)); + return r; +} + static TCGv_i64 load_freg32_i64(int reg) { TCGv_i64 r = tcg_temp_new_i64(); - tcg_gen_shri_i64(r, fregs[reg], 32); + + tcg_gen_ld32u_i64(r, cpu_env, freg32_offset(reg)); return r; } @@ -166,7 +229,7 @@ static void store_reg(int reg, TCGv_i64 v) static void store_freg(int reg, TCGv_i64 v) { - tcg_gen_mov_i64(fregs[reg], v); + tcg_gen_st_i64(v, cpu_env, freg64_offset(reg)); } static void store_reg32_i64(int reg, TCGv_i64 v) @@ -182,7 +245,7 @@ static void store_reg32h_i64(int reg, TCGv_i64 v) static void store_freg32_i64(int reg, TCGv_i64 v) { - tcg_gen_deposit_i64(fregs[reg], fregs[reg], v, 32, 32); + tcg_gen_st32_i64(v, cpu_env, freg32_offset(reg)); } static void return_low128(TCGv_i64 dest) @@ -307,55 +370,56 @@ static inline void gen_illegal_opcode(DisasContext *s) gen_program_exception(s, PGM_OPERATION); } +static inline void gen_data_exception(uint8_t dxc) +{ + TCGv_i32 tmp = tcg_const_i32(dxc); + gen_helper_data_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); +} + static inline void gen_trap(DisasContext *s) { - TCGv_i32 t; - - /* Set DXC to 0xff. */ - t = tcg_temp_new_i32(); - tcg_gen_ld_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_gen_ori_i32(t, t, 0xff00); - tcg_gen_st_i32(t, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_temp_free_i32(t); - - gen_program_exception(s, PGM_DATA); + /* Set DXC to 0xff */ + gen_data_exception(0xff); } -#ifndef CONFIG_USER_ONLY -static void check_privileged(DisasContext *s) +static void gen_addi_and_wrap_i64(DisasContext *s, TCGv_i64 dst, TCGv_i64 src, + int64_t imm) { - if (s->base.tb->flags & FLAG_MASK_PSTATE) { - gen_program_exception(s, PGM_PRIVILEGED); + tcg_gen_addi_i64(dst, src, imm); + if (!(s->base.tb->flags & FLAG_MASK_64)) { + if (s->base.tb->flags & FLAG_MASK_32) { + tcg_gen_andi_i64(dst, dst, 0x7fffffff); + } else { + tcg_gen_andi_i64(dst, dst, 0x00ffffff); + } } } -#endif static TCGv_i64 get_address(DisasContext *s, int x2, int b2, int d2) { TCGv_i64 tmp = tcg_temp_new_i64(); - bool need_31 = !(s->base.tb->flags & FLAG_MASK_64); - /* Note that d2 is limited to 20 bits, signed. If we crop negative - displacements early we create larger immedate addends. */ - - /* Note that addi optimizes the imm==0 case. */ + /* + * Note that d2 is limited to 20 bits, signed. If we crop negative + * displacements early we create larger immedate addends. + */ if (b2 && x2) { tcg_gen_add_i64(tmp, regs[b2], regs[x2]); - tcg_gen_addi_i64(tmp, tmp, d2); + gen_addi_and_wrap_i64(s, tmp, tmp, d2); } else if (b2) { - tcg_gen_addi_i64(tmp, regs[b2], d2); + gen_addi_and_wrap_i64(s, tmp, regs[b2], d2); } else if (x2) { - tcg_gen_addi_i64(tmp, regs[x2], d2); - } else { - if (need_31) { - d2 &= 0x7fffffff; - need_31 = false; + gen_addi_and_wrap_i64(s, tmp, regs[x2], d2); + } else if (!(s->base.tb->flags & FLAG_MASK_64)) { + if (s->base.tb->flags & FLAG_MASK_32) { + tcg_gen_movi_i64(tmp, d2 & 0x7fffffff); + } else { + tcg_gen_movi_i64(tmp, d2 & 0x00ffffff); } + } else { tcg_gen_movi_i64(tmp, d2); } - if (need_31) { - tcg_gen_andi_i64(tmp, tmp, 0x7fffffff); - } return tmp; } @@ -494,6 +558,7 @@ static void gen_op_calc_cc(DisasContext *s) case CC_OP_NZ_F32: case CC_OP_NZ_F64: case CC_OP_FLOGR: + case CC_OP_LCBB: /* 1 argument */ gen_helper_calc_cc(cc_op, cpu_env, local_cc_op, dummy, cc_dst, dummy); break; @@ -835,7 +900,7 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) cond = TCG_COND_NE; c->u.s32.b = tcg_const_i32(1); break; - case 0x8 | 0x2: /* cc == 0 || cc == 2 => (cc & 1) == 0 */ + case 0x8 | 0x2: /* cc == 0 || cc == 2 => (cc & 1) == 0 */ cond = TCG_COND_EQ; c->g1 = false; c->u.s32.a = tcg_temp_new_i32(); @@ -854,7 +919,7 @@ static void disas_jcc(DisasContext *s, DisasCompare *c, uint32_t mask) cond = TCG_COND_NE; c->u.s32.b = tcg_const_i32(0); break; - case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ + case 0x4 | 0x1: /* cc == 1 || cc == 3 => (cc & 1) != 0 */ cond = TCG_COND_NE; c->g1 = false; c->u.s32.a = tcg_temp_new_i32(); @@ -921,6 +986,7 @@ static void free_compare(DisasCompare *c) #define F3(N, X1, X2, X3) F0(N) #define F4(N, X1, X2, X3, X4) F0(N) #define F5(N, X1, X2, X3, X4, X5) F0(N) +#define F6(N, X1, X2, X3, X4, X5, X6) F0(N) typedef enum { #include "insn-format.def" @@ -932,6 +998,7 @@ typedef enum { #undef F3 #undef F4 #undef F5 +#undef F6 /* Define a structure to hold the decoded fields. We'll store each inside an array indexed by an enum. In order to conserve memory, we'll arrange @@ -946,6 +1013,8 @@ enum DisasFieldIndexO { FLD_O_m1, FLD_O_m3, FLD_O_m4, + FLD_O_m5, + FLD_O_m6, FLD_O_b1, FLD_O_b2, FLD_O_b4, @@ -959,7 +1028,11 @@ enum DisasFieldIndexO { FLD_O_i2, FLD_O_i3, FLD_O_i4, - FLD_O_i5 + FLD_O_i5, + FLD_O_v1, + FLD_O_v2, + FLD_O_v3, + FLD_O_v4, }; enum DisasFieldIndexC { @@ -967,6 +1040,7 @@ enum DisasFieldIndexC { FLD_C_m1 = 0, FLD_C_b1 = 0, FLD_C_i1 = 0, + FLD_C_v1 = 0, FLD_C_r2 = 1, FLD_C_b2 = 1, @@ -975,20 +1049,25 @@ enum DisasFieldIndexC { FLD_C_r3 = 2, FLD_C_m3 = 2, FLD_C_i3 = 2, + FLD_C_v3 = 2, FLD_C_m4 = 3, FLD_C_b4 = 3, FLD_C_i4 = 3, FLD_C_l1 = 3, + FLD_C_v4 = 3, FLD_C_i5 = 4, FLD_C_d1 = 4, + FLD_C_m5 = 4, FLD_C_d2 = 5, + FLD_C_m6 = 5, FLD_C_d4 = 6, FLD_C_x2 = 6, FLD_C_l2 = 6, + FLD_C_v2 = 6, NUM_C_FIELD = 7 }; @@ -1033,6 +1112,7 @@ typedef struct DisasFormatInfo { #define R(N, B) { B, 4, 0, FLD_C_r##N, FLD_O_r##N } #define M(N, B) { B, 4, 0, FLD_C_m##N, FLD_O_m##N } +#define V(N, B) { B, 4, 3, FLD_C_v##N, FLD_O_v##N } #define BD(N, BB, BD) { BB, 4, 0, FLD_C_b##N, FLD_O_b##N }, \ { BD, 12, 0, FLD_C_d##N, FLD_O_d##N } #define BXD(N) { 16, 4, 0, FLD_C_b##N, FLD_O_b##N }, \ @@ -1052,6 +1132,7 @@ typedef struct DisasFormatInfo { #define F3(N, X1, X2, X3) { { X1, X2, X3 } }, #define F4(N, X1, X2, X3, X4) { { X1, X2, X3, X4 } }, #define F5(N, X1, X2, X3, X4, X5) { { X1, X2, X3, X4, X5 } }, +#define F6(N, X1, X2, X3, X4, X5, X6) { { X1, X2, X3, X4, X5, X6 } }, static const DisasFormatInfo format_info[] = { #include "insn-format.def" @@ -1063,8 +1144,10 @@ static const DisasFormatInfo format_info[] = { #undef F3 #undef F4 #undef F5 +#undef F6 #undef R #undef M +#undef V #undef BD #undef BXD #undef BDL @@ -1113,19 +1196,38 @@ typedef struct { /* We are exiting the TB to the main loop. */ #define DISAS_PC_STALE_NOCHAIN DISAS_TARGET_4 + +/* Instruction flags */ +#define IF_AFP1 0x0001 /* r1 is a fp reg for HFP/FPS instructions */ +#define IF_AFP2 0x0002 /* r2 is a fp reg for HFP/FPS instructions */ +#define IF_AFP3 0x0004 /* r3 is a fp reg for HFP/FPS instructions */ +#define IF_BFP 0x0008 /* binary floating point instruction */ +#define IF_DFP 0x0010 /* decimal floating point instruction */ +#define IF_PRIV 0x0020 /* privileged instruction */ +#define IF_VEC 0x0040 /* vector instruction */ + struct DisasInsn { unsigned opc:16; + unsigned flags:16; DisasFormat fmt:8; unsigned fac:8; unsigned spec:8; const char *name; + /* Pre-process arguments before HELP_OP. */ void (*help_in1)(DisasContext *, DisasFields *, DisasOps *); void (*help_in2)(DisasContext *, DisasFields *, DisasOps *); void (*help_prep)(DisasContext *, DisasFields *, DisasOps *); + + /* + * Post-process output after HELP_OP. + * Note that these are not called if HELP_OP returns DISAS_NORETURN. + */ void (*help_wout)(DisasContext *, DisasFields *, DisasOps *); void (*help_cout)(DisasContext *, DisasOps *); + + /* Implement the operation itself. */ DisasJumpType (*help_op)(DisasContext *, DisasOps *); uint64_t data; @@ -1453,7 +1555,40 @@ static DisasJumpType op_ni(DisasContext *s, DisasOps *o) static DisasJumpType op_bas(DisasContext *s, DisasOps *o) { - tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->pc_tmp)); + pc_to_link_info(o->out, s, s->pc_tmp); + if (o->in2) { + tcg_gen_mov_i64(psw_addr, o->in2); + per_branch(s, false); + return DISAS_PC_UPDATED; + } else { + return DISAS_NEXT; + } +} + +static void save_link_info(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t; + + if (s->base.tb->flags & (FLAG_MASK_32 | FLAG_MASK_64)) { + pc_to_link_info(o->out, s, s->pc_tmp); + return; + } + gen_op_calc_cc(s); + tcg_gen_andi_i64(o->out, o->out, 0xffffffff00000000ull); + tcg_gen_ori_i64(o->out, o->out, ((s->ilen / 2) << 30) | s->pc_tmp); + t = tcg_temp_new_i64(); + tcg_gen_shri_i64(t, psw_mask, 16); + tcg_gen_andi_i64(t, t, 0x0f000000); + tcg_gen_or_i64(o->out, o->out, t); + tcg_gen_extu_i32_i64(t, cc_op); + tcg_gen_shli_i64(t, t, 28); + tcg_gen_or_i64(o->out, o->out, t); + tcg_temp_free_i64(t); +} + +static DisasJumpType op_bal(DisasContext *s, DisasOps *o) +{ + save_link_info(s, o); if (o->in2) { tcg_gen_mov_i64(psw_addr, o->in2); per_branch(s, false); @@ -1465,7 +1600,7 @@ static DisasJumpType op_bas(DisasContext *s, DisasOps *o) static DisasJumpType op_basi(DisasContext *s, DisasOps *o) { - tcg_gen_movi_i64(o->out, pc_to_link_info(s, s->pc_tmp)); + pc_to_link_info(o->out, s, s->pc_tmp); return help_goto_direct(s, s->base.pc_next + 2 * get_field(s->fields, i2)); } @@ -1661,160 +1796,257 @@ static DisasJumpType op_cxb(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static TCGv_i32 fpinst_extract_m34(DisasContext *s, bool m3_with_fpe, + bool m4_with_fpe) +{ + const bool fpe = s390_has_feat(S390_FEAT_FLOATING_POINT_EXT); + uint8_t m3 = get_field(s->fields, m3); + uint8_t m4 = get_field(s->fields, m4); + + /* m3 field was introduced with FPE */ + if (!fpe && m3_with_fpe) { + m3 = 0; + } + /* m4 field was introduced with FPE */ + if (!fpe && m4_with_fpe) { + m4 = 0; + } + + /* Check for valid rounding modes. Mode 3 was introduced later. */ + if (m3 == 2 || m3 > 7 || (!fpe && m3 == 3)) { + gen_program_exception(s, PGM_SPECIFICATION); + return NULL; + } + + return tcg_const_i32(deposit32(m3, 4, 4, m4)); +} + static DisasJumpType op_cfeb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cfeb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cfeb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f32(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_cfdb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cfdb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cfdb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f64(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_cfxb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cfxb(o->out, cpu_env, o->in1, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f128(s, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_cgeb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cgeb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cgeb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f32(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_cgdb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cgdb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cgdb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f64(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_cgxb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cgxb(o->out, cpu_env, o->in1, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f128(s, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_clfeb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_clfeb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_clfeb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f32(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_clfdb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_clfdb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_clfdb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f64(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_clfxb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_clfxb(o->out, cpu_env, o->in1, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f128(s, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_clgeb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_clgeb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_clgeb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f32(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_clgdb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_clgdb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_clgdb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f64(s, o->in2); return DISAS_NEXT; } static DisasJumpType op_clgxb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_clgxb(o->out, cpu_env, o->in1, o->in2, m34); + tcg_temp_free_i32(m34); gen_set_cc_nz_f128(s, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_cegb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cegb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, true, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cegb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_cdgb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cdgb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, true, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cdgb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_cxgb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cxgb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, true, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cxgb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return_low128(o->out2); return DISAS_NEXT; } static DisasJumpType op_celgb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_celgb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_celgb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_cdlgb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cdlgb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cdlgb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_cxlgb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_cxlgb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, false); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_cxlgb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return_low128(o->out2); return DISAS_NEXT; } @@ -1992,6 +2224,7 @@ static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) int r3 = get_field(s->fields, r3); int d2 = get_field(s->fields, d2); int b2 = get_field(s->fields, b2); + DisasJumpType ret = DISAS_NEXT; TCGv_i64 addr; TCGv_i32 t_r1, t_r3; @@ -1999,17 +2232,20 @@ static DisasJumpType op_cdsg(DisasContext *s, DisasOps *o) addr = get_address(s, 0, b2, d2); t_r1 = tcg_const_i32(r1); t_r3 = tcg_const_i32(r3); - if (tb_cflags(s->base.tb) & CF_PARALLEL) { + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_helper_cdsg(cpu_env, addr, t_r1, t_r3); + } else if (HAVE_CMPXCHG128) { gen_helper_cdsg_parallel(cpu_env, addr, t_r1, t_r3); } else { - gen_helper_cdsg(cpu_env, addr, t_r1, t_r3); + gen_helper_exit_atomic(cpu_env); + ret = DISAS_NORETURN; } tcg_temp_free_i64(addr); tcg_temp_free_i32(t_r1); tcg_temp_free_i32(t_r3); set_cc_static(s); - return DISAS_NEXT; + return ret; } static DisasJumpType op_csst(DisasContext *s, DisasOps *o) @@ -2018,9 +2254,9 @@ static DisasJumpType op_csst(DisasContext *s, DisasOps *o) TCGv_i32 t_r3 = tcg_const_i32(r3); if (tb_cflags(s->base.tb) & CF_PARALLEL) { - gen_helper_csst_parallel(cc_op, cpu_env, t_r3, o->in1, o->in2); + gen_helper_csst_parallel(cc_op, cpu_env, t_r3, o->addr1, o->in2); } else { - gen_helper_csst(cc_op, cpu_env, t_r3, o->in1, o->in2); + gen_helper_csst(cc_op, cpu_env, t_r3, o->addr1, o->in2); } tcg_temp_free_i32(t_r3); @@ -2038,7 +2274,6 @@ static DisasJumpType op_csp(DisasContext *s, DisasOps *o) /* Note that in1 = R1 (zero-extended expected value), out = R1 (original reg), out2 = R1+1 (new value). */ - check_privileged(s); addr = tcg_temp_new_i64(); old = tcg_temp_new_i64(); tcg_gen_andi_i64(addr, o->in2, -1ULL << (mop & MO_SIZE)); @@ -2162,7 +2397,6 @@ static DisasJumpType op_diag(DisasContext *s, DisasOps *o) TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); TCGv_i32 func_code = tcg_const_i32(get_field(s->fields, i2)); - check_privileged(s); gen_helper_diag(cpu_env, r1, r3, func_code); tcg_temp_free_i32(func_code); @@ -2291,26 +2525,38 @@ static DisasJumpType op_ex(DisasContext *s, DisasOps *o) static DisasJumpType op_fieb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_fieb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_fieb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_fidb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_fidb(o->out, cpu_env, o->in2, m3); - tcg_temp_free_i32(m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_fidb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_fixb(DisasContext *s, DisasOps *o) { - TCGv_i32 m3 = tcg_const_i32(get_field(s->fields, m3)); - gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m3); + TCGv_i32 m34 = fpinst_extract_m34(s, false, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_fixb(o->out, cpu_env, o->in1, o->in2, m34); return_low128(o->out2); - tcg_temp_free_i32(m3); + tcg_temp_free_i32(m34); return DISAS_NEXT; } @@ -2404,20 +2650,17 @@ static DisasJumpType op_insi(DisasContext *s, DisasOps *o) static DisasJumpType op_ipm(DisasContext *s, DisasOps *o) { - TCGv_i64 t1; + TCGv_i64 t1, t2; gen_op_calc_cc(s); - tcg_gen_andi_i64(o->out, o->out, ~0xff000000ull); - t1 = tcg_temp_new_i64(); - tcg_gen_shli_i64(t1, psw_mask, 20); - tcg_gen_shri_i64(t1, t1, 36); - tcg_gen_or_i64(o->out, o->out, t1); - - tcg_gen_extu_i32_i64(t1, cc_op); - tcg_gen_shli_i64(t1, t1, 28); - tcg_gen_or_i64(o->out, o->out, t1); + tcg_gen_extract_i64(t1, psw_mask, 40, 4); + t2 = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(t2, cc_op); + tcg_gen_deposit_i64(t1, t1, t2, 4, 60); + tcg_gen_deposit_i64(o->out, o->out, t1, 24, 8); tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); return DISAS_NEXT; } @@ -2426,7 +2669,6 @@ static DisasJumpType op_idte(DisasContext *s, DisasOps *o) { TCGv_i32 m4; - check_privileged(s); if (s390_has_feat(S390_FEAT_LOCAL_TLB_CLEARING)) { m4 = tcg_const_i32(get_field(s->fields, m4)); } else { @@ -2441,7 +2683,6 @@ static DisasJumpType op_ipte(DisasContext *s, DisasOps *o) { TCGv_i32 m4; - check_privileged(s); if (s390_has_feat(S390_FEAT_LOCAL_TLB_CLEARING)) { m4 = tcg_const_i32(get_field(s->fields, m4)); } else { @@ -2454,7 +2695,6 @@ static DisasJumpType op_ipte(DisasContext *s, DisasOps *o) static DisasJumpType op_iske(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_iske(o->out, cpu_env, o->in2); return DISAS_NEXT; } @@ -2585,19 +2825,37 @@ static DisasJumpType op_ldeb(DisasContext *s, DisasOps *o) static DisasJumpType op_ledb(DisasContext *s, DisasOps *o) { - gen_helper_ledb(o->out, cpu_env, o->in2); + TCGv_i32 m34 = fpinst_extract_m34(s, true, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_ledb(o->out, cpu_env, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_ldxb(DisasContext *s, DisasOps *o) { - gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2); + TCGv_i32 m34 = fpinst_extract_m34(s, true, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_ldxb(o->out, cpu_env, o->in1, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } static DisasJumpType op_lexb(DisasContext *s, DisasOps *o) { - gen_helper_lexb(o->out, cpu_env, o->in1, o->in2); + TCGv_i32 m34 = fpinst_extract_m34(s, true, true); + + if (!m34) { + return DISAS_NORETURN; + } + gen_helper_lexb(o->out, cpu_env, o->in1, o->in2, m34); + tcg_temp_free_i32(m34); return DISAS_NEXT; } @@ -2615,6 +2873,12 @@ static DisasJumpType op_lxeb(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_lde(DisasContext *s, DisasOps *o) +{ + tcg_gen_shli_i64(o->out, o->in2, 32); + return DISAS_NEXT; +} + static DisasJumpType op_llgt(DisasContext *s, DisasOps *o) { tcg_gen_andi_i64(o->out, o->in2, 0x7fffffff); @@ -2753,7 +3017,6 @@ static DisasJumpType op_lctl(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - check_privileged(s); gen_helper_lctl(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -2765,7 +3028,6 @@ static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - check_privileged(s); gen_helper_lctlg(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -2775,7 +3037,6 @@ static DisasJumpType op_lctlg(DisasContext *s, DisasOps *o) static DisasJumpType op_lra(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_lra(o->out, cpu_env, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -2783,8 +3044,6 @@ static DisasJumpType op_lra(DisasContext *s, DisasOps *o) static DisasJumpType op_lpp(DisasContext *s, DisasOps *o) { - check_privileged(s); - tcg_gen_st_i64(o->in2, cpu_env, offsetof(CPUS390XState, pp)); return DISAS_NEXT; } @@ -2793,12 +3052,12 @@ static DisasJumpType op_lpsw(DisasContext *s, DisasOps *o) { TCGv_i64 t1, t2; - check_privileged(s); per_breaking_event(s); t1 = tcg_temp_new_i64(); t2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld32u(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), + MO_TEUL | MO_ALIGN_8); tcg_gen_addi_i64(o->in2, o->in2, 4); tcg_gen_qemu_ld32u(t2, o->in2, get_mem_index(s)); /* Convert the 32-bit PSW_MASK into the 64-bit PSW_MASK. */ @@ -2813,12 +3072,12 @@ static DisasJumpType op_lpswe(DisasContext *s, DisasOps *o) { TCGv_i64 t1, t2; - check_privileged(s); per_breaking_event(s); t1 = tcg_temp_new_i64(); t2 = tcg_temp_new_i64(); - tcg_gen_qemu_ld64(t1, o->in2, get_mem_index(s)); + tcg_gen_qemu_ld_i64(t1, o->in2, get_mem_index(s), + MO_TEQ | MO_ALIGN_8); tcg_gen_addi_i64(o->in2, o->in2, 8); tcg_gen_qemu_ld64(t2, o->in2, get_mem_index(s)); gen_helper_load_psw(cpu_env, t1, t2); @@ -2999,10 +3258,13 @@ static DisasJumpType op_lpd(DisasContext *s, DisasOps *o) static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) { - if (tb_cflags(s->base.tb) & CF_PARALLEL) { + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_helper_lpq(o->out, cpu_env, o->in2); + } else if (HAVE_ATOMIC128) { gen_helper_lpq_parallel(o->out, cpu_env, o->in2); } else { - gen_helper_lpq(o->out, cpu_env, o->in2); + gen_helper_exit_atomic(cpu_env); + return DISAS_NORETURN; } return_low128(o->out2); return DISAS_NEXT; @@ -3011,14 +3273,12 @@ static DisasJumpType op_lpq(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_lura(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_lura(o->out, cpu_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_lurag(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_lurag(o->out, cpu_env, o->in2); return DISAS_NEXT; } @@ -3030,6 +3290,23 @@ static DisasJumpType op_lzrb(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +static DisasJumpType op_lcbb(DisasContext *s, DisasOps *o) +{ + const int64_t block_size = (1ull << (get_field(s->fields, m3) + 6)); + + if (get_field(s->fields, m3) > 6) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tcg_gen_ori_i64(o->addr1, o->addr1, -block_size); + tcg_gen_neg_i64(o->addr1, o->addr1); + tcg_gen_movi_i64(o->out, 16); + tcg_gen_umin_i64(o->out, o->out, o->addr1); + gen_op_update1_cc_i64(s, CC_OP_LCBB, o->out); + return DISAS_NEXT; +} + static DisasJumpType op_mov2(DisasContext *s, DisasOps *o) { o->out = o->in2; @@ -3177,7 +3454,6 @@ static DisasJumpType op_mvcos(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, l1); - check_privileged(s); gen_helper_mvcp(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -3186,7 +3462,6 @@ static DisasJumpType op_mvcp(DisasContext *s, DisasOps *o) static DisasJumpType op_mvcs(DisasContext *s, DisasOps *o) { int r1 = get_field(s->fields, l1); - check_privileged(s); gen_helper_mvcs(cc_op, cpu_env, regs[r1], o->addr1, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -3286,8 +3561,9 @@ static DisasJumpType op_maeb(DisasContext *s, DisasOps *o) static DisasJumpType op_madb(DisasContext *s, DisasOps *o) { - int r3 = get_field(s->fields, r3); - gen_helper_madb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); + TCGv_i64 r3 = load_freg(get_field(s->fields, r3)); + gen_helper_madb(o->out, cpu_env, o->in1, o->in2, r3); + tcg_temp_free_i64(r3); return DISAS_NEXT; } @@ -3301,8 +3577,9 @@ static DisasJumpType op_mseb(DisasContext *s, DisasOps *o) static DisasJumpType op_msdb(DisasContext *s, DisasOps *o) { - int r3 = get_field(s->fields, r3); - gen_helper_msdb(o->out, cpu_env, o->in1, o->in2, fregs[r3]); + TCGv_i64 r3 = load_freg(get_field(s->fields, r3)); + gen_helper_msdb(o->out, cpu_env, o->in1, o->in2, r3); + tcg_temp_free_i64(r3); return DISAS_NEXT; } @@ -3472,7 +3749,6 @@ static DisasJumpType op_popcnt(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_ptlb(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_ptlb(cpu_env); return DISAS_NEXT; } @@ -3663,7 +3939,6 @@ static DisasJumpType op_rll64(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_rrbe(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_rrbe(cc_op, cpu_env, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -3671,7 +3946,6 @@ static DisasJumpType op_rrbe(DisasContext *s, DisasOps *o) static DisasJumpType op_sacf(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sacf(cpu_env, o->in2); /* Addressing mode has changed, so end the block. */ return DISAS_PC_STALE; @@ -3761,7 +4035,6 @@ static DisasJumpType op_sqxb(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_servc(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_servc(cc_op, cpu_env, o->in2, o->in1); set_cc_static(s); return DISAS_NEXT; @@ -3771,7 +4044,6 @@ static DisasJumpType op_sigp(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - check_privileged(s); gen_helper_sigp(cc_op, cpu_env, o->in2, r1, r3); set_cc_static(s); tcg_temp_free_i32(r1); @@ -3871,41 +4143,33 @@ static DisasJumpType op_sfas(DisasContext *s, DisasOps *o) static DisasJumpType op_srnm(DisasContext *s, DisasOps *o) { - int b2 = get_field(s->fields, b2); - int d2 = get_field(s->fields, d2); - TCGv_i64 t1 = tcg_temp_new_i64(); - TCGv_i64 t2 = tcg_temp_new_i64(); - int mask, pos, len; + /* Bits other than 62 and 63 are ignored. Bit 29 is set to zero. */ + tcg_gen_andi_i64(o->addr1, o->addr1, 0x3ull); + gen_helper_srnm(cpu_env, o->addr1); + return DISAS_NEXT; +} - switch (s->fields->op2) { - case 0x99: /* SRNM */ - pos = 0, len = 2; - break; - case 0xb8: /* SRNMB */ - pos = 0, len = 3; - break; - case 0xb9: /* SRNMT */ - pos = 4, len = 3; - break; - default: - tcg_abort(); - } - mask = (1 << len) - 1; +static DisasJumpType op_srnmb(DisasContext *s, DisasOps *o) +{ + /* Bits 0-55 are are ignored. */ + tcg_gen_andi_i64(o->addr1, o->addr1, 0xffull); + gen_helper_srnm(cpu_env, o->addr1); + return DISAS_NEXT; +} - /* Insert the value into the appropriate field of the FPC. */ - if (b2 == 0) { - tcg_gen_movi_i64(t1, d2 & mask); - } else { - tcg_gen_addi_i64(t1, regs[b2], d2); - tcg_gen_andi_i64(t1, t1, mask); - } - tcg_gen_ld32u_i64(t2, cpu_env, offsetof(CPUS390XState, fpc)); - tcg_gen_deposit_i64(t2, t2, t1, pos, len); - tcg_temp_free_i64(t1); +static DisasJumpType op_srnmt(DisasContext *s, DisasOps *o) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); - /* Then install the new FPC to set the rounding mode in fpu_status. */ - gen_helper_sfpc(cpu_env, t2); - tcg_temp_free_i64(t2); + /* Bits other than 61-63 are ignored. */ + tcg_gen_andi_i64(o->addr1, o->addr1, 0x7ull); + + /* No need to call a helper, we don't implement dfp */ + tcg_gen_ld32u_i64(tmp, cpu_env, offsetof(CPUS390XState, fpc)); + tcg_gen_deposit_i64(tmp, tmp, o->addr1, 4, 3); + tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUS390XState, fpc)); + + tcg_temp_free_i64(tmp); return DISAS_NEXT; } @@ -3953,7 +4217,6 @@ static DisasJumpType op_ectg(DisasContext *s, DisasOps *o) #ifndef CONFIG_USER_ONLY static DisasJumpType op_spka(DisasContext *s, DisasOps *o) { - check_privileged(s); tcg_gen_shri_i64(o->in2, o->in2, 4); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, PSW_SHIFT_KEY, 4); return DISAS_NEXT; @@ -3961,14 +4224,12 @@ static DisasJumpType op_spka(DisasContext *s, DisasOps *o) static DisasJumpType op_sske(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sske(cpu_env, o->in1, o->in2); return DISAS_NEXT; } static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) { - check_privileged(s); tcg_gen_deposit_i64(psw_mask, psw_mask, o->in2, 56, 8); /* Exit to main loop to reevaluate s390_cpu_exec_interrupt. */ return DISAS_PC_STALE_NOCHAIN; @@ -3976,10 +4237,10 @@ static DisasJumpType op_ssm(DisasContext *s, DisasOps *o) static DisasJumpType op_stap(DisasContext *s, DisasOps *o) { - check_privileged(s); tcg_gen_ld32u_i64(o->out, cpu_env, offsetof(CPUS390XState, core_id)); return DISAS_NEXT; } +#endif static DisasJumpType op_stck(DisasContext *s, DisasOps *o) { @@ -4016,9 +4277,9 @@ static DisasJumpType op_stcke(DisasContext *s, DisasOps *o) return DISAS_NEXT; } +#ifndef CONFIG_USER_ONLY static DisasJumpType op_sck(DisasContext *s, DisasOps *o) { - check_privileged(s); tcg_gen_qemu_ld_i64(o->in1, o->addr1, get_mem_index(s), MO_TEQ | MO_ALIGN); gen_helper_sck(cc_op, cpu_env, o->in1); set_cc_static(s); @@ -4027,21 +4288,18 @@ static DisasJumpType op_sck(DisasContext *s, DisasOps *o) static DisasJumpType op_sckc(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sckc(cpu_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_sckpf(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sckpf(cpu_env, regs[0]); return DISAS_NEXT; } static DisasJumpType op_stckc(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stckc(o->out, cpu_env); return DISAS_NEXT; } @@ -4050,7 +4308,6 @@ static DisasJumpType op_stctg(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - check_privileged(s); gen_helper_stctg(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -4061,7 +4318,6 @@ static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) { TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); - check_privileged(s); gen_helper_stctl(cpu_env, r1, o->in2, r3); tcg_temp_free_i32(r1); tcg_temp_free_i32(r3); @@ -4070,35 +4326,30 @@ static DisasJumpType op_stctl(DisasContext *s, DisasOps *o) static DisasJumpType op_stidp(DisasContext *s, DisasOps *o) { - check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, cpuid)); return DISAS_NEXT; } static DisasJumpType op_spt(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_spt(cpu_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_stfl(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stfl(cpu_env); return DISAS_NEXT; } static DisasJumpType op_stpt(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stpt(o->out, cpu_env); return DISAS_NEXT; } static DisasJumpType op_stsi(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stsi(cc_op, cpu_env, o->in2, regs[0], regs[1]); set_cc_static(s); return DISAS_NEXT; @@ -4106,14 +4357,12 @@ static DisasJumpType op_stsi(DisasContext *s, DisasOps *o) static DisasJumpType op_spx(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_spx(cpu_env, o->in2); return DISAS_NEXT; } static DisasJumpType op_xsch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_xsch(cpu_env, regs[1]); set_cc_static(s); return DISAS_NEXT; @@ -4121,7 +4370,6 @@ static DisasJumpType op_xsch(DisasContext *s, DisasOps *o) static DisasJumpType op_csch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_csch(cpu_env, regs[1]); set_cc_static(s); return DISAS_NEXT; @@ -4129,7 +4377,6 @@ static DisasJumpType op_csch(DisasContext *s, DisasOps *o) static DisasJumpType op_hsch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_hsch(cpu_env, regs[1]); set_cc_static(s); return DISAS_NEXT; @@ -4137,7 +4384,6 @@ static DisasJumpType op_hsch(DisasContext *s, DisasOps *o) static DisasJumpType op_msch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_msch(cpu_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4145,7 +4391,6 @@ static DisasJumpType op_msch(DisasContext *s, DisasOps *o) static DisasJumpType op_rchp(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_rchp(cpu_env, regs[1]); set_cc_static(s); return DISAS_NEXT; @@ -4153,7 +4398,6 @@ static DisasJumpType op_rchp(DisasContext *s, DisasOps *o) static DisasJumpType op_rsch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_rsch(cpu_env, regs[1]); set_cc_static(s); return DISAS_NEXT; @@ -4161,21 +4405,18 @@ static DisasJumpType op_rsch(DisasContext *s, DisasOps *o) static DisasJumpType op_sal(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sal(cpu_env, regs[1]); return DISAS_NEXT; } static DisasJumpType op_schm(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_schm(cpu_env, regs[1], regs[2], o->in2); return DISAS_NEXT; } static DisasJumpType op_siga(DisasContext *s, DisasOps *o) { - check_privileged(s); /* From KVM code: Not provided, set CC = 3 for subchannel not operational */ gen_op_movi_cc(s, 3); return DISAS_NEXT; @@ -4183,14 +4424,12 @@ static DisasJumpType op_siga(DisasContext *s, DisasOps *o) static DisasJumpType op_stcps(DisasContext *s, DisasOps *o) { - check_privileged(s); /* The instruction is suppressed if not provided. */ return DISAS_NEXT; } static DisasJumpType op_ssch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_ssch(cpu_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4198,7 +4437,6 @@ static DisasJumpType op_ssch(DisasContext *s, DisasOps *o) static DisasJumpType op_stsch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stsch(cpu_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4206,7 +4444,6 @@ static DisasJumpType op_stsch(DisasContext *s, DisasOps *o) static DisasJumpType op_stcrw(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stcrw(cpu_env, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4214,7 +4451,6 @@ static DisasJumpType op_stcrw(DisasContext *s, DisasOps *o) static DisasJumpType op_tpi(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_tpi(cc_op, cpu_env, o->addr1); set_cc_static(s); return DISAS_NEXT; @@ -4222,7 +4458,6 @@ static DisasJumpType op_tpi(DisasContext *s, DisasOps *o) static DisasJumpType op_tsch(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_tsch(cpu_env, regs[1], o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4230,7 +4465,6 @@ static DisasJumpType op_tsch(DisasContext *s, DisasOps *o) static DisasJumpType op_chsc(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_chsc(cpu_env, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4238,7 +4472,6 @@ static DisasJumpType op_chsc(DisasContext *s, DisasOps *o) static DisasJumpType op_stpx(DisasContext *s, DisasOps *o) { - check_privileged(s); tcg_gen_ld_i64(o->out, cpu_env, offsetof(CPUS390XState, psa)); tcg_gen_andi_i64(o->out, o->out, 0x7fffe000); return DISAS_NEXT; @@ -4249,8 +4482,6 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) uint64_t i2 = get_field(s->fields, i2); TCGv_i64 t; - check_privileged(s); - /* It is important to do what the instruction name says: STORE THEN. If we let the output hook perform the store then if we fault and restart, we'll have the wrong SYSTEM MASK in place. */ @@ -4272,14 +4503,12 @@ static DisasJumpType op_stnosm(DisasContext *s, DisasOps *o) static DisasJumpType op_stura(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_stura(cpu_env, o->in2, o->in1); return DISAS_NEXT; } static DisasJumpType op_sturg(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sturg(cpu_env, o->in2, o->in1); return DISAS_NEXT; } @@ -4425,10 +4654,13 @@ static DisasJumpType op_stmh(DisasContext *s, DisasOps *o) static DisasJumpType op_stpq(DisasContext *s, DisasOps *o) { - if (tb_cflags(s->base.tb) & CF_PARALLEL) { + if (!(tb_cflags(s->base.tb) & CF_PARALLEL)) { + gen_helper_stpq(cpu_env, o->in2, o->out2, o->out); + } else if (HAVE_ATOMIC128) { gen_helper_stpq_parallel(cpu_env, o->in2, o->out2, o->out); } else { - gen_helper_stpq(cpu_env, o->in2, o->out2, o->out); + gen_helper_exit_atomic(cpu_env); + return DISAS_NORETURN; } return DISAS_NEXT; } @@ -4545,7 +4777,6 @@ static DisasJumpType op_tcxb(DisasContext *s, DisasOps *o) static DisasJumpType op_testblock(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_testblock(cc_op, cpu_env, o->in2); set_cc_static(s); return DISAS_NEXT; @@ -4803,7 +5034,6 @@ static DisasJumpType op_clp(DisasContext *s, DisasOps *o) { TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); - check_privileged(s); gen_helper_clp(cpu_env, r2); tcg_temp_free_i32(r2); set_cc_static(s); @@ -4815,7 +5045,6 @@ static DisasJumpType op_pcilg(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); - check_privileged(s); gen_helper_pcilg(cpu_env, r1, r2); tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); @@ -4828,7 +5057,6 @@ static DisasJumpType op_pcistg(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); - check_privileged(s); gen_helper_pcistg(cpu_env, r1, r2); tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); @@ -4841,7 +5069,6 @@ static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); - check_privileged(s); gen_helper_stpcifc(cpu_env, r1, o->addr1, ar); tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); @@ -4851,7 +5078,6 @@ static DisasJumpType op_stpcifc(DisasContext *s, DisasOps *o) static DisasJumpType op_sic(DisasContext *s, DisasOps *o) { - check_privileged(s); gen_helper_sic(cpu_env, o->in1, o->in2); return DISAS_NEXT; } @@ -4861,7 +5087,6 @@ static DisasJumpType op_rpcit(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 r2 = tcg_const_i32(get_field(s->fields, r2)); - check_privileged(s); gen_helper_rpcit(cpu_env, r1, r2); tcg_temp_free_i32(r1); tcg_temp_free_i32(r2); @@ -4875,7 +5100,6 @@ static DisasJumpType op_pcistb(DisasContext *s, DisasOps *o) TCGv_i32 r3 = tcg_const_i32(get_field(s->fields, r3)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); - check_privileged(s); gen_helper_pcistb(cpu_env, r1, r3, o->addr1, ar); tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); @@ -4889,7 +5113,6 @@ static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) TCGv_i32 r1 = tcg_const_i32(get_field(s->fields, r1)); TCGv_i32 ar = tcg_const_i32(get_field(s->fields, b2)); - check_privileged(s); gen_helper_mpcifc(cpu_env, r1, o->addr1, ar); tcg_temp_free_i32(ar); tcg_temp_free_i32(r1); @@ -4898,6 +5121,8 @@ static DisasJumpType op_mpcifc(DisasContext *s, DisasOps *o) } #endif +#include "translate_vx.inc.c" + /* ====================================================================== */ /* The "Cc OUTput" generators. Given the generated output (and in some cases the original inputs), update the various cc data structures in order to @@ -5094,19 +5319,11 @@ static void prep_r1_P(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_prep_r1_P SPEC_r1_even -static void prep_f1(DisasContext *s, DisasFields *f, DisasOps *o) -{ - o->out = fregs[get_field(f, r1)]; - o->g_out = true; -} -#define SPEC_prep_f1 0 - +/* Whenever we need x1 in addition to other inputs, we'll load it to out/out2 */ static void prep_x1(DisasContext *s, DisasFields *f, DisasOps *o) { - int r1 = get_field(f, r1); - o->out = fregs[r1]; - o->out2 = fregs[r1 + 2]; - o->g_out = o->g_out2 = true; + o->out = load_freg(get_field(f, r1)); + o->out2 = load_freg(get_field(f, r1) + 2); } #define SPEC_prep_x1 SPEC_r1_f128 @@ -5402,28 +5619,24 @@ static void in1_e1(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in1_e1 0 -static void in1_f1_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_f1(DisasContext *s, DisasFields *f, DisasOps *o) { - o->in1 = fregs[get_field(f, r1)]; - o->g_in1 = true; + o->in1 = load_freg(get_field(f, r1)); } -#define SPEC_in1_f1_o 0 +#define SPEC_in1_f1 0 -static void in1_x1_o(DisasContext *s, DisasFields *f, DisasOps *o) +/* Load the high double word of an extended (128-bit) format FP number */ +static void in1_x2h(DisasContext *s, DisasFields *f, DisasOps *o) { - int r1 = get_field(f, r1); - o->out = fregs[r1]; - o->out2 = fregs[r1 + 2]; - o->g_out = o->g_out2 = true; + o->in1 = load_freg(get_field(f, r2)); } -#define SPEC_in1_x1_o SPEC_r1_f128 +#define SPEC_in1_x2h SPEC_r2_f128 -static void in1_f3_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in1_f3(DisasContext *s, DisasFields *f, DisasOps *o) { - o->in1 = fregs[get_field(f, r3)]; - o->g_in1 = true; + o->in1 = load_freg(get_field(f, r3)); } -#define SPEC_in1_f3_o 0 +#define SPEC_in1_f3 0 static void in1_la1(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -5581,6 +5794,13 @@ static void in2_r3_sr32(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_r3_sr32 0 +static void in2_r3_32u(DisasContext *s, DisasFields *f, DisasOps *o) +{ + o->in2 = tcg_temp_new_i64(); + tcg_gen_ext32u_i64(o->in2, regs[get_field(f, r3)]); +} +#define SPEC_in2_r3_32u 0 + static void in2_r2_32s(DisasContext *s, DisasFields *f, DisasOps *o) { o->in2 = tcg_temp_new_i64(); @@ -5608,21 +5828,18 @@ static void in2_e2(DisasContext *s, DisasFields *f, DisasOps *o) } #define SPEC_in2_e2 0 -static void in2_f2_o(DisasContext *s, DisasFields *f, DisasOps *o) +static void in2_f2(DisasContext *s, DisasFields *f, DisasOps *o) { - o->in2 = fregs[get_field(f, r2)]; - o->g_in2 = true; + o->in2 = load_freg(get_field(f, r2)); } -#define SPEC_in2_f2_o 0 +#define SPEC_in2_f2 0 -static void in2_x2_o(DisasContext *s, DisasFields *f, DisasOps *o) +/* Load the low double word of an extended (128-bit) format FP number */ +static void in2_x2l(DisasContext *s, DisasFields *f, DisasOps *o) { - int r2 = get_field(f, r2); - o->in1 = fregs[r2]; - o->in2 = fregs[r2 + 2]; - o->g_in1 = o->g_in2 = true; + o->in2 = load_freg(get_field(f, r2) + 2); } -#define SPEC_in2_x2_o SPEC_r2_f128 +#define SPEC_in2_x2l SPEC_r2_f128 static void in2_ra2(DisasContext *s, DisasFields *f, DisasOps *o) { @@ -5797,17 +6014,24 @@ static void in2_insn(DisasContext *s, DisasFields *f, DisasOps *o) search tree, rather than us having to post-process the table. */ #define C(OPC, NM, FT, FC, I1, I2, P, W, OP, CC) \ - D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, 0) + E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, 0, 0) -#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) insn_ ## NM, +#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) \ + E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D, 0) + +#define F(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, FL) \ + E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, 0, FL) + +#define E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D, FL) insn_ ## NM, enum DisasInsnEnum { #include "insn-data.def" }; -#undef D -#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) { \ +#undef E +#define E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D, FL) { \ .opc = OPC, \ + .flags = FL, \ .fmt = FMT_##FT, \ .fac = FAC_##FC, \ .spec = SPEC_in1_##I1 | SPEC_in2_##I2 | SPEC_prep_##P | SPEC_wout_##W, \ @@ -5873,13 +6097,14 @@ enum DisasInsnEnum { #define FAC_ECT S390_FEAT_EXTRACT_CPU_TIME #define FAC_PCI S390_FEAT_ZPCI /* z/PCI facility */ #define FAC_AIS S390_FEAT_ADAPTER_INT_SUPPRESSION +#define FAC_V S390_FEAT_VECTOR /* vector facility */ static const DisasInsn insn_info[] = { #include "insn-data.def" }; -#undef D -#define D(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D) \ +#undef E +#define E(OPC, NM, FT, FC, I1, I2, P, W, OP, CC, D, FL) \ case OPC: return &insn_info[insn_ ## NM]; static const DisasInsn *lookup_opc(uint16_t opc) @@ -5891,6 +6116,8 @@ static const DisasInsn *lookup_opc(uint16_t opc) } } +#undef F +#undef E #undef D #undef C @@ -5921,6 +6148,25 @@ static void extract_field(DisasFields *o, const DisasField *f, uint64_t insn) case 2: /* dl+dh split, signed 20 bit. */ r = ((int8_t)r << 12) | (r >> 8); break; + case 3: /* MSB stored in RXB */ + g_assert(f->size == 4); + switch (f->beg) { + case 8: + r |= extract64(insn, 63 - 36, 1) << 4; + break; + case 12: + r |= extract64(insn, 63 - 37, 1) << 4; + break; + case 16: + r |= extract64(insn, 63 - 38, 1) << 4; + break; + case 32: + r |= extract64(insn, 63 - 39, 1) << 4; + break; + default: + g_assert_not_reached(); + } + break; default: abort(); } @@ -6038,12 +6284,23 @@ static const DisasInsn *extract_insn(CPUS390XState *env, DisasContext *s, return info; } +static bool is_afp_reg(int reg) +{ + return reg % 2 || reg > 6; +} + +static bool is_fp_pair(int reg) +{ + /* 0,1,4,5,8,9,12,13: to exclude the others, check for single bit */ + return !(reg & 0x2); +} + static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) { const DisasInsn *insn; DisasJumpType ret = DISAS_NEXT; DisasFields f; - DisasOps o; + DisasOps o = {}; /* Search for the insn in the table. */ insn = extract_insn(env, s, &f); @@ -6064,42 +6321,59 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) } #endif + /* process flags */ + if (insn->flags) { + /* privileged instruction */ + if ((s->base.tb->flags & FLAG_MASK_PSTATE) && (insn->flags & IF_PRIV)) { + gen_program_exception(s, PGM_PRIVILEGED); + return DISAS_NORETURN; + } + + /* if AFP is not enabled, instructions and registers are forbidden */ + if (!(s->base.tb->flags & FLAG_MASK_AFP)) { + uint8_t dxc = 0; + + if ((insn->flags & IF_AFP1) && is_afp_reg(get_field(&f, r1))) { + dxc = 1; + } + if ((insn->flags & IF_AFP2) && is_afp_reg(get_field(&f, r2))) { + dxc = 1; + } + if ((insn->flags & IF_AFP3) && is_afp_reg(get_field(&f, r3))) { + dxc = 1; + } + if (insn->flags & IF_BFP) { + dxc = 2; + } + if (insn->flags & IF_DFP) { + dxc = 3; + } + if (insn->flags & IF_VEC) { + dxc = 0xfe; + } + if (dxc) { + gen_data_exception(dxc); + return DISAS_NORETURN; + } + } + + /* if vector instructions not enabled, executing them is forbidden */ + if (insn->flags & IF_VEC) { + if (!((s->base.tb->flags & FLAG_MASK_VECTOR))) { + gen_data_exception(0xfe); + return DISAS_NORETURN; + } + } + } + /* Check for insn specification exceptions. */ if (insn->spec) { - int spec = insn->spec, excp = 0, r; - - if (spec & SPEC_r1_even) { - r = get_field(&f, r1); - if (r & 1) { - excp = PGM_SPECIFICATION; - } - } - if (spec & SPEC_r2_even) { - r = get_field(&f, r2); - if (r & 1) { - excp = PGM_SPECIFICATION; - } - } - if (spec & SPEC_r3_even) { - r = get_field(&f, r3); - if (r & 1) { - excp = PGM_SPECIFICATION; - } - } - if (spec & SPEC_r1_f128) { - r = get_field(&f, r1); - if (r > 13) { - excp = PGM_SPECIFICATION; - } - } - if (spec & SPEC_r2_f128) { - r = get_field(&f, r2); - if (r > 13) { - excp = PGM_SPECIFICATION; - } - } - if (excp) { - gen_program_exception(s, excp); + if ((insn->spec & SPEC_r1_even && get_field(&f, r1) & 1) || + (insn->spec & SPEC_r2_even && get_field(&f, r2) & 1) || + (insn->spec & SPEC_r3_even && get_field(&f, r3) & 1) || + (insn->spec & SPEC_r1_f128 && !is_fp_pair(get_field(&f, r1))) || + (insn->spec & SPEC_r2_f128 && !is_fp_pair(get_field(&f, r2)))) { + gen_program_exception(s, PGM_SPECIFICATION); return DISAS_NORETURN; } } @@ -6107,12 +6381,6 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) /* Set up the strutures we use to communicate with the helpers. */ s->insn = insn; s->fields = &f; - o.g_out = o.g_out2 = o.g_in1 = o.g_in2 = false; - o.out = NULL; - o.out2 = NULL; - o.in1 = NULL; - o.in2 = NULL; - o.addr1 = NULL; /* Implement the instruction. */ if (insn->help_in1) { @@ -6127,11 +6395,13 @@ static DisasJumpType translate_one(CPUS390XState *env, DisasContext *s) if (insn->help_op) { ret = insn->help_op(s, &o); } - if (insn->help_wout) { - insn->help_wout(s, &f, &o); - } - if (insn->help_cout) { - insn->help_cout(s, &o); + if (ret != DISAS_NORETURN) { + if (insn->help_wout) { + insn->help_wout(s, &f, &o); + } + if (insn->help_cout) { + insn->help_cout(s, &o); + } } /* Free any temporaries created by the helpers. */ diff --git a/target/s390x/translate_vx.inc.c b/target/s390x/translate_vx.inc.c new file mode 100644 index 0000000000..76f9a5d939 --- /dev/null +++ b/target/s390x/translate_vx.inc.c @@ -0,0 +1,935 @@ +/* + * QEMU TCG support -- s390x vector instruction translation functions + * + * Copyright (C) 2019 Red Hat Inc + * + * Authors: + * David Hildenbrand + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +/* + * For most instructions that use the same element size for reads and + * writes, we can use real gvec vector expansion, which potantially uses + * real host vector instructions. As they only work up to 64 bit elements, + * 128 bit elements (vector is a single element) have to be handled + * differently. Operations that are too complicated to encode via TCG ops + * are handled via gvec ool (out-of-line) handlers. + * + * As soon as instructions use different element sizes for reads and writes + * or access elements "out of their element scope" we expand them manually + * in fancy loops, as gvec expansion does not deal with actual element + * numbers and does also not support access to other elements. + * + * 128 bit elements: + * As we only have i32/i64, such elements have to be loaded into two + * i64 values and can then be processed e.g. by tcg_gen_add2_i64. + * + * Sizes: + * On s390x, the operand size (oprsz) and the maximum size (maxsz) are + * always 16 (128 bit). What gvec code calls "vece", s390x calls "es", + * a.k.a. "element size". These values nicely map to MO_8 ... MO_64. Only + * 128 bit element size has to be treated in a special way (MO_64 + 1). + * We will use ES_* instead of MO_* for this reason in this file. + * + * CC handling: + * As gvec ool-helpers can currently not return values (besides via + * pointers like vectors or cpu_env), whenever we have to set the CC and + * can't conclude the value from the result vector, we will directly + * set it in "env->cc_op" and mark it as static via set_cc_static()". + * Whenever this is done, the helper writes globals (cc_op). + */ + +#define NUM_VEC_ELEMENT_BYTES(es) (1 << (es)) +#define NUM_VEC_ELEMENTS(es) (16 / NUM_VEC_ELEMENT_BYTES(es)) +#define NUM_VEC_ELEMENT_BITS(es) (NUM_VEC_ELEMENT_BYTES(es) * BITS_PER_BYTE) + +#define ES_8 MO_8 +#define ES_16 MO_16 +#define ES_32 MO_32 +#define ES_64 MO_64 +#define ES_128 4 + +static inline bool valid_vec_element(uint8_t enr, TCGMemOp es) +{ + return !(enr & ~(NUM_VEC_ELEMENTS(es) - 1)); +} + +static void read_vec_element_i64(TCGv_i64 dst, uint8_t reg, uint8_t enr, + TCGMemOp memop) +{ + const int offs = vec_reg_offset(reg, enr, memop & MO_SIZE); + + switch (memop) { + case ES_8: + tcg_gen_ld8u_i64(dst, cpu_env, offs); + break; + case ES_16: + tcg_gen_ld16u_i64(dst, cpu_env, offs); + break; + case ES_32: + tcg_gen_ld32u_i64(dst, cpu_env, offs); + break; + case ES_8 | MO_SIGN: + tcg_gen_ld8s_i64(dst, cpu_env, offs); + break; + case ES_16 | MO_SIGN: + tcg_gen_ld16s_i64(dst, cpu_env, offs); + break; + case ES_32 | MO_SIGN: + tcg_gen_ld32s_i64(dst, cpu_env, offs); + break; + case ES_64: + case ES_64 | MO_SIGN: + tcg_gen_ld_i64(dst, cpu_env, offs); + break; + default: + g_assert_not_reached(); + } +} + +static void write_vec_element_i64(TCGv_i64 src, int reg, uint8_t enr, + TCGMemOp memop) +{ + const int offs = vec_reg_offset(reg, enr, memop & MO_SIZE); + + switch (memop) { + case ES_8: + tcg_gen_st8_i64(src, cpu_env, offs); + break; + case ES_16: + tcg_gen_st16_i64(src, cpu_env, offs); + break; + case ES_32: + tcg_gen_st32_i64(src, cpu_env, offs); + break; + case ES_64: + tcg_gen_st_i64(src, cpu_env, offs); + break; + default: + g_assert_not_reached(); + } +} + + +static void get_vec_element_ptr_i64(TCGv_ptr ptr, uint8_t reg, TCGv_i64 enr, + uint8_t es) +{ + TCGv_i64 tmp = tcg_temp_new_i64(); + + /* mask off invalid parts from the element nr */ + tcg_gen_andi_i64(tmp, enr, NUM_VEC_ELEMENTS(es) - 1); + + /* convert it to an element offset relative to cpu_env (vec_reg_offset() */ + tcg_gen_shli_i64(tmp, tmp, es); +#ifndef HOST_WORDS_BIGENDIAN + tcg_gen_xori_i64(tmp, tmp, 8 - NUM_VEC_ELEMENT_BYTES(es)); +#endif + tcg_gen_addi_i64(tmp, tmp, vec_full_reg_offset(reg)); + + /* generate the final ptr by adding cpu_env */ + tcg_gen_trunc_i64_ptr(ptr, tmp); + tcg_gen_add_ptr(ptr, ptr, cpu_env); + + tcg_temp_free_i64(tmp); +} + +#define gen_gvec_3_ool(v1, v2, v3, data, fn) \ + tcg_gen_gvec_3_ool(vec_full_reg_offset(v1), vec_full_reg_offset(v2), \ + vec_full_reg_offset(v3), 16, 16, data, fn) +#define gen_gvec_3_ptr(v1, v2, v3, ptr, data, fn) \ + tcg_gen_gvec_3_ptr(vec_full_reg_offset(v1), vec_full_reg_offset(v2), \ + vec_full_reg_offset(v3), ptr, 16, 16, data, fn) +#define gen_gvec_4(v1, v2, v3, v4, gen) \ + tcg_gen_gvec_4(vec_full_reg_offset(v1), vec_full_reg_offset(v2), \ + vec_full_reg_offset(v3), vec_full_reg_offset(v4), \ + 16, 16, gen) +#define gen_gvec_4_ool(v1, v2, v3, v4, data, fn) \ + tcg_gen_gvec_4_ool(vec_full_reg_offset(v1), vec_full_reg_offset(v2), \ + vec_full_reg_offset(v3), vec_full_reg_offset(v4), \ + 16, 16, data, fn) +#define gen_gvec_dup_i64(es, v1, c) \ + tcg_gen_gvec_dup_i64(es, vec_full_reg_offset(v1), 16, 16, c) +#define gen_gvec_mov(v1, v2) \ + tcg_gen_gvec_mov(0, vec_full_reg_offset(v1), vec_full_reg_offset(v2), 16, \ + 16) +#define gen_gvec_dup64i(v1, c) \ + tcg_gen_gvec_dup64i(vec_full_reg_offset(v1), 16, 16, c) + +static void gen_gvec_dupi(uint8_t es, uint8_t reg, uint64_t c) +{ + switch (es) { + case ES_8: + tcg_gen_gvec_dup8i(vec_full_reg_offset(reg), 16, 16, c); + break; + case ES_16: + tcg_gen_gvec_dup16i(vec_full_reg_offset(reg), 16, 16, c); + break; + case ES_32: + tcg_gen_gvec_dup32i(vec_full_reg_offset(reg), 16, 16, c); + break; + case ES_64: + gen_gvec_dup64i(reg, c); + break; + default: + g_assert_not_reached(); + } +} + +static void zero_vec(uint8_t reg) +{ + tcg_gen_gvec_dup8i(vec_full_reg_offset(reg), 16, 16, 0); +} + +static DisasJumpType op_vge(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s->fields, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + read_vec_element_i64(tmp, get_field(s->fields, v2), enr, es); + tcg_gen_add_i64(o->addr1, o->addr1, tmp); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 0); + + tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); + write_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static uint64_t generate_byte_mask(uint8_t mask) +{ + uint64_t r = 0; + int i; + + for (i = 0; i < 8; i++) { + if ((mask >> i) & 1) { + r |= 0xffull << (i * 8); + } + } + return r; +} + +static DisasJumpType op_vgbm(DisasContext *s, DisasOps *o) +{ + const uint16_t i2 = get_field(s->fields, i2); + + if (i2 == (i2 & 0xff) * 0x0101) { + /* + * Masks for both 64 bit elements of the vector are the same. + * Trust tcg to produce a good constant loading. + */ + gen_gvec_dup64i(get_field(s->fields, v1), + generate_byte_mask(i2 & 0xff)); + } else { + TCGv_i64 t = tcg_temp_new_i64(); + + tcg_gen_movi_i64(t, generate_byte_mask(i2 >> 8)); + write_vec_element_i64(t, get_field(s->fields, v1), 0, ES_64); + tcg_gen_movi_i64(t, generate_byte_mask(i2)); + write_vec_element_i64(t, get_field(s->fields, v1), 1, ES_64); + tcg_temp_free_i64(t); + } + return DISAS_NEXT; +} + +static DisasJumpType op_vgm(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s->fields, m4); + const uint8_t bits = NUM_VEC_ELEMENT_BITS(es); + const uint8_t i2 = get_field(s->fields, i2) & (bits - 1); + const uint8_t i3 = get_field(s->fields, i3) & (bits - 1); + uint64_t mask = 0; + int i; + + if (es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* generate the mask - take care of wrapping */ + for (i = i2; ; i = (i + 1) % bits) { + mask |= 1ull << (bits - i - 1); + if (i == i3) { + break; + } + } + + gen_gvec_dupi(es, get_field(s->fields, v1), mask); + return DISAS_NEXT; +} + +static DisasJumpType op_vl(DisasContext *s, DisasOps *o) +{ + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + tcg_gen_qemu_ld_i64(t0, o->addr1, get_mem_index(s), MO_TEQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_TEQ); + write_vec_element_i64(t0, get_field(s->fields, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s->fields, v1), 1, ES_64); + tcg_temp_free(t0); + tcg_temp_free(t1); + return DISAS_NEXT; +} + +static DisasJumpType op_vlr(DisasContext *s, DisasOps *o) +{ + gen_gvec_mov(get_field(s->fields, v1), get_field(s->fields, v2)); + return DISAS_NEXT; +} + +static DisasJumpType op_vlrep(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s->fields, m3); + TCGv_i64 tmp; + + if (es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); + gen_gvec_dup_i64(es, get_field(s->fields, v1), tmp); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vle(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s->fields, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); + write_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vlei(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s->fields, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_const_i64((int16_t)get_field(s->fields, i2)); + write_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vlgv(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s->fields, m4); + TCGv_ptr ptr; + + if (es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* fast path if we don't need the register content */ + if (!get_field(s->fields, b2)) { + uint8_t enr = get_field(s->fields, d2) & (NUM_VEC_ELEMENTS(es) - 1); + + read_vec_element_i64(o->out, get_field(s->fields, v3), enr, es); + return DISAS_NEXT; + } + + ptr = tcg_temp_new_ptr(); + get_vec_element_ptr_i64(ptr, get_field(s->fields, v3), o->addr1, es); + switch (es) { + case ES_8: + tcg_gen_ld8u_i64(o->out, ptr, 0); + break; + case ES_16: + tcg_gen_ld16u_i64(o->out, ptr, 0); + break; + case ES_32: + tcg_gen_ld32u_i64(o->out, ptr, 0); + break; + case ES_64: + tcg_gen_ld_i64(o->out, ptr, 0); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free_ptr(ptr); + + return DISAS_NEXT; +} + +static DisasJumpType op_vllez(DisasContext *s, DisasOps *o) +{ + uint8_t es = get_field(s->fields, m3); + uint8_t enr; + TCGv_i64 t; + + switch (es) { + /* rightmost sub-element of leftmost doubleword */ + case ES_8: + enr = 7; + break; + case ES_16: + enr = 3; + break; + case ES_32: + enr = 1; + break; + case ES_64: + enr = 0; + break; + /* leftmost sub-element of leftmost doubleword */ + case 6: + if (s390_has_feat(S390_FEAT_VECTOR_ENH)) { + es = ES_32; + enr = 0; + break; + } + default: + /* fallthrough */ + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + t = tcg_temp_new_i64(); + tcg_gen_qemu_ld_i64(t, o->addr1, get_mem_index(s), MO_TE | es); + zero_vec(get_field(s->fields, v1)); + write_vec_element_i64(t, get_field(s->fields, v1), enr, es); + tcg_temp_free_i64(t); + return DISAS_NEXT; +} + +static DisasJumpType op_vlm(DisasContext *s, DisasOps *o) +{ + const uint8_t v3 = get_field(s->fields, v3); + uint8_t v1 = get_field(s->fields, v1); + TCGv_i64 t0, t1; + + if (v3 < v1 || (v3 - v1 + 1) > 16) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* + * Check for possible access exceptions by trying to load the last + * element. The first element will be checked first next. + */ + t0 = tcg_temp_new_i64(); + t1 = tcg_temp_new_i64(); + gen_addi_and_wrap_i64(s, t0, o->addr1, (v3 - v1) * 16 + 8); + tcg_gen_qemu_ld_i64(t0, t0, get_mem_index(s), MO_TEQ); + + for (;; v1++) { + tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_TEQ); + write_vec_element_i64(t1, v1, 0, ES_64); + if (v1 == v3) { + break; + } + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + tcg_gen_qemu_ld_i64(t1, o->addr1, get_mem_index(s), MO_TEQ); + write_vec_element_i64(t1, v1, 1, ES_64); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + } + + /* Store the last element, loaded first */ + write_vec_element_i64(t0, v1, 1, ES_64); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + return DISAS_NEXT; +} + +static DisasJumpType op_vlbb(DisasContext *s, DisasOps *o) +{ + const int64_t block_size = (1ull << (get_field(s->fields, m3) + 6)); + const int v1_offs = vec_full_reg_offset(get_field(s->fields, v1)); + TCGv_ptr a0; + TCGv_i64 bytes; + + if (get_field(s->fields, m3) > 6) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + bytes = tcg_temp_new_i64(); + a0 = tcg_temp_new_ptr(); + /* calculate the number of bytes until the next block boundary */ + tcg_gen_ori_i64(bytes, o->addr1, -block_size); + tcg_gen_neg_i64(bytes, bytes); + + tcg_gen_addi_ptr(a0, cpu_env, v1_offs); + gen_helper_vll(cpu_env, a0, o->addr1, bytes); + tcg_temp_free_i64(bytes); + tcg_temp_free_ptr(a0); + return DISAS_NEXT; +} + +static DisasJumpType op_vlvg(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s->fields, m4); + TCGv_ptr ptr; + + if (es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* fast path if we don't need the register content */ + if (!get_field(s->fields, b2)) { + uint8_t enr = get_field(s->fields, d2) & (NUM_VEC_ELEMENTS(es) - 1); + + write_vec_element_i64(o->in2, get_field(s->fields, v1), enr, es); + return DISAS_NEXT; + } + + ptr = tcg_temp_new_ptr(); + get_vec_element_ptr_i64(ptr, get_field(s->fields, v1), o->addr1, es); + switch (es) { + case ES_8: + tcg_gen_st8_i64(o->in2, ptr, 0); + break; + case ES_16: + tcg_gen_st16_i64(o->in2, ptr, 0); + break; + case ES_32: + tcg_gen_st32_i64(o->in2, ptr, 0); + break; + case ES_64: + tcg_gen_st_i64(o->in2, ptr, 0); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free_ptr(ptr); + + return DISAS_NEXT; +} + +static DisasJumpType op_vlvgp(DisasContext *s, DisasOps *o) +{ + write_vec_element_i64(o->in1, get_field(s->fields, v1), 0, ES_64); + write_vec_element_i64(o->in2, get_field(s->fields, v1), 1, ES_64); + return DISAS_NEXT; +} + +static DisasJumpType op_vll(DisasContext *s, DisasOps *o) +{ + const int v1_offs = vec_full_reg_offset(get_field(s->fields, v1)); + TCGv_ptr a0 = tcg_temp_new_ptr(); + + /* convert highest index into an actual length */ + tcg_gen_addi_i64(o->in2, o->in2, 1); + tcg_gen_addi_ptr(a0, cpu_env, v1_offs); + gen_helper_vll(cpu_env, a0, o->addr1, o->in2); + tcg_temp_free_ptr(a0); + return DISAS_NEXT; +} + +static DisasJumpType op_vmr(DisasContext *s, DisasOps *o) +{ + const uint8_t v1 = get_field(s->fields, v1); + const uint8_t v2 = get_field(s->fields, v2); + const uint8_t v3 = get_field(s->fields, v3); + const uint8_t es = get_field(s->fields, m4); + int dst_idx, src_idx; + TCGv_i64 tmp; + + if (es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + if (s->fields->op2 == 0x61) { + /* iterate backwards to avoid overwriting data we might need later */ + for (dst_idx = NUM_VEC_ELEMENTS(es) - 1; dst_idx >= 0; dst_idx--) { + src_idx = dst_idx / 2; + if (dst_idx % 2 == 0) { + read_vec_element_i64(tmp, v2, src_idx, es); + } else { + read_vec_element_i64(tmp, v3, src_idx, es); + } + write_vec_element_i64(tmp, v1, dst_idx, es); + } + } else { + /* iterate forward to avoid overwriting data we might need later */ + for (dst_idx = 0; dst_idx < NUM_VEC_ELEMENTS(es); dst_idx++) { + src_idx = (dst_idx + NUM_VEC_ELEMENTS(es)) / 2; + if (dst_idx % 2 == 0) { + read_vec_element_i64(tmp, v2, src_idx, es); + } else { + read_vec_element_i64(tmp, v3, src_idx, es); + } + write_vec_element_i64(tmp, v1, dst_idx, es); + } + } + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vpk(DisasContext *s, DisasOps *o) +{ + const uint8_t v1 = get_field(s->fields, v1); + const uint8_t v2 = get_field(s->fields, v2); + const uint8_t v3 = get_field(s->fields, v3); + const uint8_t es = get_field(s->fields, m4); + static gen_helper_gvec_3 * const vpk[3] = { + gen_helper_gvec_vpk16, + gen_helper_gvec_vpk32, + gen_helper_gvec_vpk64, + }; + static gen_helper_gvec_3 * const vpks[3] = { + gen_helper_gvec_vpks16, + gen_helper_gvec_vpks32, + gen_helper_gvec_vpks64, + }; + static gen_helper_gvec_3_ptr * const vpks_cc[3] = { + gen_helper_gvec_vpks_cc16, + gen_helper_gvec_vpks_cc32, + gen_helper_gvec_vpks_cc64, + }; + static gen_helper_gvec_3 * const vpkls[3] = { + gen_helper_gvec_vpkls16, + gen_helper_gvec_vpkls32, + gen_helper_gvec_vpkls64, + }; + static gen_helper_gvec_3_ptr * const vpkls_cc[3] = { + gen_helper_gvec_vpkls_cc16, + gen_helper_gvec_vpkls_cc32, + gen_helper_gvec_vpkls_cc64, + }; + + if (es == ES_8 || es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + switch (s->fields->op2) { + case 0x97: + if (get_field(s->fields, m5) & 0x1) { + gen_gvec_3_ptr(v1, v2, v3, cpu_env, 0, vpks_cc[es - 1]); + set_cc_static(s); + } else { + gen_gvec_3_ool(v1, v2, v3, 0, vpks[es - 1]); + } + break; + case 0x95: + if (get_field(s->fields, m5) & 0x1) { + gen_gvec_3_ptr(v1, v2, v3, cpu_env, 0, vpkls_cc[es - 1]); + set_cc_static(s); + } else { + gen_gvec_3_ool(v1, v2, v3, 0, vpkls[es - 1]); + } + break; + case 0x94: + /* If sources and destination dont't overlap -> fast path */ + if (v1 != v2 && v1 != v3) { + const uint8_t src_es = get_field(s->fields, m4); + const uint8_t dst_es = src_es - 1; + TCGv_i64 tmp = tcg_temp_new_i64(); + int dst_idx, src_idx; + + for (dst_idx = 0; dst_idx < NUM_VEC_ELEMENTS(dst_es); dst_idx++) { + src_idx = dst_idx; + if (src_idx < NUM_VEC_ELEMENTS(src_es)) { + read_vec_element_i64(tmp, v2, src_idx, src_es); + } else { + src_idx -= NUM_VEC_ELEMENTS(src_es); + read_vec_element_i64(tmp, v3, src_idx, src_es); + } + write_vec_element_i64(tmp, v1, dst_idx, dst_es); + } + tcg_temp_free_i64(tmp); + } else { + gen_gvec_3_ool(v1, v2, v3, 0, vpk[es - 1]); + } + break; + default: + g_assert_not_reached(); + } + return DISAS_NEXT; +} + +static DisasJumpType op_vperm(DisasContext *s, DisasOps *o) +{ + gen_gvec_4_ool(get_field(s->fields, v1), get_field(s->fields, v2), + get_field(s->fields, v3), get_field(s->fields, v4), + 0, gen_helper_gvec_vperm); + return DISAS_NEXT; +} + +static DisasJumpType op_vpdi(DisasContext *s, DisasOps *o) +{ + const uint8_t i2 = extract32(get_field(s->fields, m4), 2, 1); + const uint8_t i3 = extract32(get_field(s->fields, m4), 0, 1); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + + read_vec_element_i64(t0, get_field(s->fields, v2), i2, ES_64); + read_vec_element_i64(t1, get_field(s->fields, v3), i3, ES_64); + write_vec_element_i64(t0, get_field(s->fields, v1), 0, ES_64); + write_vec_element_i64(t1, get_field(s->fields, v1), 1, ES_64); + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + return DISAS_NEXT; +} + +static DisasJumpType op_vrep(DisasContext *s, DisasOps *o) +{ + const uint8_t enr = get_field(s->fields, i2); + const uint8_t es = get_field(s->fields, m4); + + if (es > ES_64 || !valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tcg_gen_gvec_dup_mem(es, vec_full_reg_offset(get_field(s->fields, v1)), + vec_reg_offset(get_field(s->fields, v3), enr, es), + 16, 16); + return DISAS_NEXT; +} + +static DisasJumpType op_vrepi(DisasContext *s, DisasOps *o) +{ + const int64_t data = (int16_t)get_field(s->fields, i2); + const uint8_t es = get_field(s->fields, m3); + + if (es > ES_64) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + gen_gvec_dupi(es, get_field(s->fields, v1), data); + return DISAS_NEXT; +} + +static DisasJumpType op_vsce(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s->fields, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + read_vec_element_i64(tmp, get_field(s->fields, v2), enr, es); + tcg_gen_add_i64(o->addr1, o->addr1, tmp); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 0); + + read_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static void gen_sel_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b, TCGv_i64 c) +{ + TCGv_i64 t = tcg_temp_new_i64(); + + /* bit in c not set -> copy bit from b */ + tcg_gen_andc_i64(t, b, c); + /* bit in c set -> copy bit from a */ + tcg_gen_and_i64(d, a, c); + /* merge the results */ + tcg_gen_or_i64(d, d, t); + tcg_temp_free_i64(t); +} + +static void gen_sel_vec(unsigned vece, TCGv_vec d, TCGv_vec a, TCGv_vec b, + TCGv_vec c) +{ + TCGv_vec t = tcg_temp_new_vec_matching(d); + + tcg_gen_andc_vec(vece, t, b, c); + tcg_gen_and_vec(vece, d, a, c); + tcg_gen_or_vec(vece, d, d, t); + tcg_temp_free_vec(t); +} + +static DisasJumpType op_vsel(DisasContext *s, DisasOps *o) +{ + static const GVecGen4 gvec_op = { + .fni8 = gen_sel_i64, + .fniv = gen_sel_vec, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + + gen_gvec_4(get_field(s->fields, v1), get_field(s->fields, v2), + get_field(s->fields, v3), get_field(s->fields, v4), &gvec_op); + return DISAS_NEXT; +} + +static DisasJumpType op_vseg(DisasContext *s, DisasOps *o) +{ + const uint8_t es = get_field(s->fields, m3); + int idx1, idx2; + TCGv_i64 tmp; + + switch (es) { + case ES_8: + idx1 = 7; + idx2 = 15; + break; + case ES_16: + idx1 = 3; + idx2 = 7; + break; + case ES_32: + idx1 = 1; + idx2 = 3; + break; + default: + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + read_vec_element_i64(tmp, get_field(s->fields, v2), idx1, es | MO_SIGN); + write_vec_element_i64(tmp, get_field(s->fields, v1), 0, ES_64); + read_vec_element_i64(tmp, get_field(s->fields, v2), idx2, es | MO_SIGN); + write_vec_element_i64(tmp, get_field(s->fields, v1), 1, ES_64); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vst(DisasContext *s, DisasOps *o) +{ + TCGv_i64 tmp = tcg_const_i64(16); + + /* Probe write access before actually modifying memory */ + gen_helper_probe_write_access(cpu_env, o->addr1, tmp); + + read_vec_element_i64(tmp, get_field(s->fields, v1), 0, ES_64); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + read_vec_element_i64(tmp, get_field(s->fields, v1), 1, ES_64); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEQ); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vste(DisasContext *s, DisasOps *o) +{ + const uint8_t es = s->insn->data; + const uint8_t enr = get_field(s->fields, m3); + TCGv_i64 tmp; + + if (!valid_vec_element(enr, es)) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + read_vec_element_i64(tmp, get_field(s->fields, v1), enr, es); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TE | es); + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vstm(DisasContext *s, DisasOps *o) +{ + const uint8_t v3 = get_field(s->fields, v3); + uint8_t v1 = get_field(s->fields, v1); + TCGv_i64 tmp; + + while (v3 < v1 || (v3 - v1 + 1) > 16) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + /* Probe write access before actually modifying memory */ + tmp = tcg_const_i64((v3 - v1 + 1) * 16); + gen_helper_probe_write_access(cpu_env, o->addr1, tmp); + + for (;; v1++) { + read_vec_element_i64(tmp, v1, 0, ES_64); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEQ); + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + read_vec_element_i64(tmp, v1, 1, ES_64); + tcg_gen_qemu_st_i64(tmp, o->addr1, get_mem_index(s), MO_TEQ); + if (v1 == v3) { + break; + } + gen_addi_and_wrap_i64(s, o->addr1, o->addr1, 8); + } + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} + +static DisasJumpType op_vstl(DisasContext *s, DisasOps *o) +{ + const int v1_offs = vec_full_reg_offset(get_field(s->fields, v1)); + TCGv_ptr a0 = tcg_temp_new_ptr(); + + /* convert highest index into an actual length */ + tcg_gen_addi_i64(o->in2, o->in2, 1); + tcg_gen_addi_ptr(a0, cpu_env, v1_offs); + gen_helper_vstl(cpu_env, a0, o->addr1, o->in2); + tcg_temp_free_ptr(a0); + return DISAS_NEXT; +} + +static DisasJumpType op_vup(DisasContext *s, DisasOps *o) +{ + const bool logical = s->fields->op2 == 0xd4 || s->fields->op2 == 0xd5; + const uint8_t v1 = get_field(s->fields, v1); + const uint8_t v2 = get_field(s->fields, v2); + const uint8_t src_es = get_field(s->fields, m3); + const uint8_t dst_es = src_es + 1; + int dst_idx, src_idx; + TCGv_i64 tmp; + + if (src_es > ES_32) { + gen_program_exception(s, PGM_SPECIFICATION); + return DISAS_NORETURN; + } + + tmp = tcg_temp_new_i64(); + if (s->fields->op2 == 0xd7 || s->fields->op2 == 0xd5) { + /* iterate backwards to avoid overwriting data we might need later */ + for (dst_idx = NUM_VEC_ELEMENTS(dst_es) - 1; dst_idx >= 0; dst_idx--) { + src_idx = dst_idx; + read_vec_element_i64(tmp, v2, src_idx, + src_es | (logical ? 0 : MO_SIGN)); + write_vec_element_i64(tmp, v1, dst_idx, dst_es); + } + + } else { + /* iterate forward to avoid overwriting data we might need later */ + for (dst_idx = 0; dst_idx < NUM_VEC_ELEMENTS(dst_es); dst_idx++) { + src_idx = dst_idx + NUM_VEC_ELEMENTS(src_es) / 2; + read_vec_element_i64(tmp, v2, src_idx, + src_es | (logical ? 0 : MO_SIGN)); + write_vec_element_i64(tmp, v1, dst_idx, dst_es); + } + } + tcg_temp_free_i64(tmp); + return DISAS_NEXT; +} diff --git a/target/s390x/vec.h b/target/s390x/vec.h new file mode 100644 index 0000000000..3313fb43ee --- /dev/null +++ b/target/s390x/vec.h @@ -0,0 +1,101 @@ +/* + * QEMU TCG support -- s390x vector utilitites + * + * Copyright (C) 2019 Red Hat Inc + * + * Authors: + * David Hildenbrand + * + * 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 S390X_VEC_H +#define S390X_VEC_H + +typedef union S390Vector { + uint64_t doubleword[2]; + uint32_t word[4]; + uint16_t halfword[8]; + uint8_t byte[16]; +} S390Vector; + +/* + * Each vector is stored as two 64bit host values. So when talking about + * byte/halfword/word numbers, we have to take care of proper translation + * between element numbers. + * + * Big Endian (target/possible host) + * B: [ 0][ 1][ 2][ 3][ 4][ 5][ 6][ 7] - [ 8][ 9][10][11][12][13][14][15] + * HW: [ 0][ 1][ 2][ 3] - [ 4][ 5][ 6][ 7] + * W: [ 0][ 1] - [ 2][ 3] + * DW: [ 0] - [ 1] + * + * Little Endian (possible host) + * B: [ 7][ 6][ 5][ 4][ 3][ 2][ 1][ 0] - [15][14][13][12][11][10][ 9][ 8] + * HW: [ 3][ 2][ 1][ 0] - [ 7][ 6][ 5][ 4] + * W: [ 1][ 0] - [ 3][ 2] + * DW: [ 0] - [ 1] + */ +#ifndef HOST_WORDS_BIGENDIAN +#define H1(x) ((x) ^ 7) +#define H2(x) ((x) ^ 3) +#define H4(x) ((x) ^ 1) +#else +#define H1(x) (x) +#define H2(x) (x) +#define H4(x) (x) +#endif + +static inline uint8_t s390_vec_read_element8(const S390Vector *v, uint8_t enr) +{ + g_assert(enr < 16); + return v->byte[H1(enr)]; +} + +static inline uint16_t s390_vec_read_element16(const S390Vector *v, uint8_t enr) +{ + g_assert(enr < 8); + return v->halfword[H2(enr)]; +} + +static inline uint32_t s390_vec_read_element32(const S390Vector *v, uint8_t enr) +{ + g_assert(enr < 4); + return v->word[H4(enr)]; +} + +static inline uint64_t s390_vec_read_element64(const S390Vector *v, uint8_t enr) +{ + g_assert(enr < 2); + return v->doubleword[enr]; +} + +static inline void s390_vec_write_element8(S390Vector *v, uint8_t enr, + uint8_t data) +{ + g_assert(enr < 16); + v->byte[H1(enr)] = data; +} + +static inline void s390_vec_write_element16(S390Vector *v, uint8_t enr, + uint16_t data) +{ + g_assert(enr < 8); + v->halfword[H2(enr)] = data; +} + +static inline void s390_vec_write_element32(S390Vector *v, uint8_t enr, + uint32_t data) +{ + g_assert(enr < 4); + v->word[H4(enr)] = data; +} + +static inline void s390_vec_write_element64(S390Vector *v, uint8_t enr, + uint64_t data) +{ + g_assert(enr < 2); + v->doubleword[enr] = data; +} + +#endif /* S390X_VEC_H */ diff --git a/target/s390x/vec_helper.c b/target/s390x/vec_helper.c new file mode 100644 index 0000000000..bb4c9304f0 --- /dev/null +++ b/target/s390x/vec_helper.c @@ -0,0 +1,193 @@ +/* + * QEMU TCG support -- s390x vector support instructions + * + * Copyright (C) 2019 Red Hat Inc + * + * Authors: + * David Hildenbrand + * + * 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 "cpu.h" +#include "internal.h" +#include "vec.h" +#include "tcg/tcg.h" +#include "tcg/tcg-gvec-desc.h" +#include "exec/helper-proto.h" +#include "exec/cpu_ldst.h" +#include "exec/exec-all.h" + +void HELPER(vll)(CPUS390XState *env, void *v1, uint64_t addr, uint64_t bytes) +{ + if (likely(bytes >= 16)) { + uint64_t t0, t1; + + t0 = cpu_ldq_data_ra(env, addr, GETPC()); + addr = wrap_address(env, addr + 8); + t1 = cpu_ldq_data_ra(env, addr, GETPC()); + s390_vec_write_element64(v1, 0, t0); + s390_vec_write_element64(v1, 1, t1); + } else { + S390Vector tmp = {}; + int i; + + for (i = 0; i < bytes; i++) { + uint8_t byte = cpu_ldub_data_ra(env, addr, GETPC()); + + s390_vec_write_element8(&tmp, i, byte); + addr = wrap_address(env, addr + 1); + } + *(S390Vector *)v1 = tmp; + } +} + +#define DEF_VPK_HFN(BITS, TBITS) \ +typedef uint##TBITS##_t (*vpk##BITS##_fn)(uint##BITS##_t, int *); \ +static int vpk##BITS##_hfn(S390Vector *v1, const S390Vector *v2, \ + const S390Vector *v3, vpk##BITS##_fn fn) \ +{ \ + int i, saturated = 0; \ + S390Vector tmp; \ + \ + for (i = 0; i < (128 / TBITS); i++) { \ + uint##BITS##_t src; \ + \ + if (i < (128 / BITS)) { \ + src = s390_vec_read_element##BITS(v2, i); \ + } else { \ + src = s390_vec_read_element##BITS(v3, i - (128 / BITS)); \ + } \ + s390_vec_write_element##TBITS(&tmp, i, fn(src, &saturated)); \ + } \ + *v1 = tmp; \ + return saturated; \ +} +DEF_VPK_HFN(64, 32) +DEF_VPK_HFN(32, 16) +DEF_VPK_HFN(16, 8) + +#define DEF_VPK(BITS, TBITS) \ +static uint##TBITS##_t vpk##BITS##e(uint##BITS##_t src, int *saturated) \ +{ \ + return src; \ +} \ +void HELPER(gvec_vpk##BITS)(void *v1, const void *v2, const void *v3, \ + uint32_t desc) \ +{ \ + vpk##BITS##_hfn(v1, v2, v3, vpk##BITS##e); \ +} +DEF_VPK(64, 32) +DEF_VPK(32, 16) +DEF_VPK(16, 8) + +#define DEF_VPKS(BITS, TBITS) \ +static uint##TBITS##_t vpks##BITS##e(uint##BITS##_t src, int *saturated) \ +{ \ + if ((int##BITS##_t)src > INT##TBITS##_MAX) { \ + (*saturated)++; \ + return INT##TBITS##_MAX; \ + } else if ((int##BITS##_t)src < INT##TBITS##_MIN) { \ + (*saturated)++; \ + return INT##TBITS##_MIN; \ + } \ + return src; \ +} \ +void HELPER(gvec_vpks##BITS)(void *v1, const void *v2, const void *v3, \ + uint32_t desc) \ +{ \ + vpk##BITS##_hfn(v1, v2, v3, vpks##BITS##e); \ +} \ +void HELPER(gvec_vpks_cc##BITS)(void *v1, const void *v2, const void *v3, \ + CPUS390XState *env, uint32_t desc) \ +{ \ + int saturated = vpk##BITS##_hfn(v1, v2, v3, vpks##BITS##e); \ + \ + if (saturated == (128 / TBITS)) { \ + env->cc_op = 3; \ + } else if (saturated) { \ + env->cc_op = 1; \ + } else { \ + env->cc_op = 0; \ + } \ +} +DEF_VPKS(64, 32) +DEF_VPKS(32, 16) +DEF_VPKS(16, 8) + +#define DEF_VPKLS(BITS, TBITS) \ +static uint##TBITS##_t vpkls##BITS##e(uint##BITS##_t src, int *saturated) \ +{ \ + if (src > UINT##TBITS##_MAX) { \ + (*saturated)++; \ + return UINT##TBITS##_MAX; \ + } \ + return src; \ +} \ +void HELPER(gvec_vpkls##BITS)(void *v1, const void *v2, const void *v3, \ + uint32_t desc) \ +{ \ + vpk##BITS##_hfn(v1, v2, v3, vpkls##BITS##e); \ +} \ +void HELPER(gvec_vpkls_cc##BITS)(void *v1, const void *v2, const void *v3, \ + CPUS390XState *env, uint32_t desc) \ +{ \ + int saturated = vpk##BITS##_hfn(v1, v2, v3, vpkls##BITS##e); \ + \ + if (saturated == (128 / TBITS)) { \ + env->cc_op = 3; \ + } else if (saturated) { \ + env->cc_op = 1; \ + } else { \ + env->cc_op = 0; \ + } \ +} +DEF_VPKLS(64, 32) +DEF_VPKLS(32, 16) +DEF_VPKLS(16, 8) + +void HELPER(gvec_vperm)(void *v1, const void *v2, const void *v3, + const void *v4, uint32_t desc) +{ + S390Vector tmp; + int i; + + for (i = 0; i < 16; i++) { + const uint8_t selector = s390_vec_read_element8(v4, i) & 0x1f; + uint8_t byte; + + if (selector < 16) { + byte = s390_vec_read_element8(v2, selector); + } else { + byte = s390_vec_read_element8(v3, selector - 16); + } + s390_vec_write_element8(&tmp, i, byte); + } + *(S390Vector *)v1 = tmp; +} + +void HELPER(vstl)(CPUS390XState *env, const void *v1, uint64_t addr, + uint64_t bytes) +{ + /* Probe write access before actually modifying memory */ + probe_write_access(env, addr, bytes, GETPC()); + + if (likely(bytes >= 16)) { + cpu_stq_data_ra(env, addr, s390_vec_read_element64(v1, 0), GETPC()); + addr = wrap_address(env, addr + 8); + cpu_stq_data_ra(env, addr, s390_vec_read_element64(v1, 1), GETPC()); + } else { + S390Vector tmp = {}; + int i; + + for (i = 0; i < bytes; i++) { + uint8_t byte = s390_vec_read_element8(v1, i); + + cpu_stb_data_ra(env, addr, byte, GETPC()); + addr = wrap_address(env, addr + 1); + } + *(S390Vector *)v1 = tmp; + } +} diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 1b9a201d6d..ab254b0e8d 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -293,6 +293,7 @@ static void gen_conditional_jump(DisasContext *ctx, target_ulong dest, disallow it in use_goto_tb, but it handles exit + singlestep. */ gen_goto_tb(ctx, 0, dest); gen_set_label(l1); + ctx->base.is_jmp = DISAS_NEXT; return; } diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 0f090ece54..4a4445bdf5 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -111,7 +111,6 @@ cpu_add_feat_as_prop(const char *typename, const char *name, const char *val) prop->driver = typename; prop->property = g_strdup(name); prop->value = g_strdup(val); - prop->errp = &error_fatal; qdev_prop_register_global(prop); } diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 8ff9dea297..7791c84963 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -56,7 +56,8 @@ static const VMStateDescription vmstate_tlb_entry = { }; #endif -static int get_psr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) +static int get_psr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field) { SPARCCPU *cpu = opaque; CPUSPARCState *env = &cpu->env; @@ -69,8 +70,8 @@ static int get_psr(QEMUFile *f, void *opaque, size_t size, VMStateField *field) return 0; } -static int put_psr(QEMUFile *f, void *opaque, size_t size, VMStateField *field, - QJSON *vmdesc) +static int put_psr(QEMUFile *f, void *opaque, size_t size, + const VMStateField *field, QJSON *vmdesc) { SPARCCPU *cpu = opaque; CPUSPARCState *env = &cpu->env; diff --git a/target/sparc/trace-events b/target/sparc/trace-events index 764b1e5a02..6a064e2327 100644 --- a/target/sparc/trace-events +++ b/target/sparc/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# target/sparc/mmu_helper.c +# mmu_helper.c mmu_helper_dfault(uint64_t address, uint64_t context, int mmu_idx, uint32_t tl) "DFAULT at 0x%"PRIx64" context 0x%"PRIx64" mmu_idx=%d tl=%d" mmu_helper_dprot(uint64_t address, uint64_t context, int mmu_idx, uint32_t tl) "DPROT at 0x%"PRIx64" context 0x%"PRIx64" mmu_idx=%d tl=%d" mmu_helper_dmiss(uint64_t address, uint64_t context) "DMISS at 0x%"PRIx64" context 0x%"PRIx64 @@ -10,16 +10,16 @@ mmu_helper_get_phys_addr_code(uint32_t tl, int mmu_idx, uint64_t prim_context, u 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=0x%"PRIx64" secondary context=0x%"PRIx64" address=0x%"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 0x%"PRIx64" -> 0x%"PRIx64", mmu_idx=%d tl=%d primary context=0x%"PRIx64" secondary context=0x%"PRIx64 -# target/sparc/int64_helper.c +# int64_helper.c int_helper_set_softint(uint32_t softint) "new 0x%08x" int_helper_clear_softint(uint32_t softint) "new 0x%08x" int_helper_write_softint(uint32_t softint) "new 0x%08x" -# target/sparc/int32_helper.c +# int32_helper.c int_helper_icache_freeze(void) "Instruction cache: freeze" int_helper_dcache_freeze(void) "Data cache: freeze" -# target/sparc/win_helper.c +# win_helper.c win_helper_gregset_error(uint32_t pstate) "ERROR in get_gregset: active pstate bits=0x%x" win_helper_switch_pstate(uint32_t pstate_regs, uint32_t new_pstate_regs) "change_pstate: switching regs old=0x%x new=0x%x" win_helper_no_switch_pstate(uint32_t new_pstate_regs) "change_pstate: regs new=0x%x (unchanged)" diff --git a/target/tilegx/translate.c b/target/tilegx/translate.c index f201150fc7..df1e4d0fef 100644 --- a/target/tilegx/translate.c +++ b/target/tilegx/translate.c @@ -297,7 +297,7 @@ static TileExcp gen_st_opcode(DisasContext *dc, unsigned dest, unsigned srca, } tcg_gen_qemu_st_tl(load_gr(dc, srcb), load_gr(dc, srca), - dc->mmuidx, memop); + dc->mmuidx, memop); qemu_log_mask(CPU_LOG_TB_IN_ASM, "%s %s, %s", name, reg_names[srca], reg_names[srcb]); diff --git a/target/tricore/cpu-qom.h b/target/tricore/cpu-qom.h index 6a69756126..93c9d77fe3 100644 --- a/target/tricore/cpu-qom.h +++ b/target/tricore/cpu-qom.h @@ -4,7 +4,7 @@ * 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. + * 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 diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 2edaef1aef..e8d37e4040 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index c3665c6ddd..00e69dc154 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/target/tricore/fpu_helper.c b/target/tricore/fpu_helper.c index df162902d6..d8a6c0d25b 100644 --- a/target/tricore/fpu_helper.c +++ b/target/tricore/fpu_helper.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -44,11 +44,6 @@ static inline uint8_t f_get_excp_flags(CPUTriCoreState *env) | float_flag_inexact); } -static inline bool f_is_denormal(float32 arg) -{ - return float32_is_zero_or_denormal(arg) && !float32_is_zero(arg); -} - static inline float32 f_maddsub_nan_result(float32 arg1, float32 arg2, float32 arg3, float32 result, uint32_t muladd_negate_c) @@ -260,8 +255,8 @@ uint32_t helper_fcmp(CPUTriCoreState *env, uint32_t r1, uint32_t 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; + result |= float32_is_denormal(arg1) << 4; + result |= float32_is_denormal(arg2) << 5; flags = f_get_excp_flags(env); if (flags) { diff --git a/target/tricore/helper.c b/target/tricore/helper.c index dad7eea085..0769046993 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -4,7 +4,7 @@ * 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. + * 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 diff --git a/target/tricore/helper.h b/target/tricore/helper.h index e634d0c680..f60e81096b 100644 --- a/target/tricore/helper.h +++ b/target/tricore/helper.h @@ -4,7 +4,7 @@ * 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. + * 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 diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index b57f35387d..ed9dc0c83e 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -4,7 +4,7 @@ * 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. + * 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 diff --git a/target/tricore/translate.c b/target/tricore/translate.c index b5ab40d4a2..b12c391be5 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -5867,12 +5867,12 @@ static void decode_rcr_cond_select(CPUTriCoreState *env, DisasContext *ctx) switch (op2) { case OPC2_32_RCR_CADD: - gen_condi_add(TCG_COND_NE, cpu_gpr_d[r1], const9, cpu_gpr_d[r3], - cpu_gpr_d[r4]); + gen_condi_add(TCG_COND_NE, cpu_gpr_d[r1], const9, cpu_gpr_d[r4], + cpu_gpr_d[r3]); break; case OPC2_32_RCR_CADDN: - gen_condi_add(TCG_COND_EQ, cpu_gpr_d[r1], const9, cpu_gpr_d[r3], - cpu_gpr_d[r4]); + gen_condi_add(TCG_COND_EQ, cpu_gpr_d[r1], const9, cpu_gpr_d[r4], + cpu_gpr_d[r3]); break; case OPC2_32_RCR_SEL: temp = tcg_const_i32(0); diff --git a/target/tricore/tricore-defs.h b/target/tricore/tricore-defs.h index 40abfaac14..e871aa1c6b 100644 --- a/target/tricore/tricore-defs.h +++ b/target/tricore/tricore-defs.h @@ -4,7 +4,7 @@ * 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. + * 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 diff --git a/target/tricore/tricore-opcodes.h b/target/tricore/tricore-opcodes.h index 2c3baab694..40bc121ba4 100644 --- a/target/tricore/tricore-opcodes.h +++ b/target/tricore/tricore-opcodes.h @@ -4,7 +4,7 @@ * 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. + * 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 diff --git a/target/unicore32/cpu.c b/target/unicore32/cpu.c index 68f978d80b..2b49d1ca40 100644 --- a/target/unicore32/cpu.c +++ b/target/unicore32/cpu.c @@ -116,8 +116,6 @@ static void uc32_cpu_initfn(Object *obj) env->uncached_asr = ASR_MODE_PRIV; env->regs[31] = 0x03000000; #endif - - tlb_flush(cs); } static const VMStateDescription vmstate_uc32_cpu = { diff --git a/target/xtensa/Makefile.objs b/target/xtensa/Makefile.objs index 2fae785cb2..c7e7fe6063 100644 --- a/target/xtensa/Makefile.objs +++ b/target/xtensa/Makefile.objs @@ -3,7 +3,14 @@ obj-y += core-dc233c.o obj-y += core-de212.o obj-y += core-fsf.o obj-y += core-sample_controller.o +obj-y += core-test_kc705_be.o +obj-y += core-test_mmuhifi_c3.o obj-$(CONFIG_SOFTMMU) += monitor.o xtensa-semi.o obj-y += xtensa-isa.o obj-y += translate.o op_helper.o helper.o cpu.o +obj-$(CONFIG_SOFTMMU) += dbg_helper.o +obj-y += exc_helper.o +obj-y += fpu_helper.o obj-y += gdbstub.o +obj-$(CONFIG_SOFTMMU) += mmu_helper.o +obj-y += win_helper.o diff --git a/target/xtensa/core-dc232b.c b/target/xtensa/core-dc232b.c index 7131337840..7851bcb636 100644 --- a/target/xtensa/core-dc232b.c +++ b/target/xtensa/core-dc232b.c @@ -40,8 +40,6 @@ static XtensaConfig dc232b __attribute__((unused)) = { .name = "dc232b", .gdb_regmap = { - .num_regs = 120, - .num_core_regs = 52, .reg = { #include "core-dc232b/gdb-config.inc.c" } diff --git a/target/xtensa/core-dc232b/gdb-config.inc.c b/target/xtensa/core-dc232b/gdb-config.inc.c index 13aba5edec..d87168628b 100644 --- a/target/xtensa/core-dc232b/gdb-config.inc.c +++ b/target/xtensa/core-dc232b/gdb-config.inc.c @@ -259,3 +259,4 @@ 0, 0, 0, 0, 0, 0) XTREG(119, 476, 32, 4, 4, 0x000f, 0x0006, -2, 8, 0x0100, a15, 0, 0, 0, 0, 0, 0) + XTREG_END diff --git a/target/xtensa/core-dc233c.c b/target/xtensa/core-dc233c.c index d701e3f5de..8853bfd4d0 100644 --- a/target/xtensa/core-dc233c.c +++ b/target/xtensa/core-dc233c.c @@ -40,8 +40,6 @@ static XtensaConfig dc233c __attribute__((unused)) = { .name = "dc233c", .gdb_regmap = { - .num_regs = 121, - .num_core_regs = 52, .reg = { #include "core-dc233c/gdb-config.inc.c" } diff --git a/target/xtensa/core-dc233c/gdb-config.inc.c b/target/xtensa/core-dc233c/gdb-config.inc.c index b632341b28..7e8963227f 100644 --- a/target/xtensa/core-dc233c/gdb-config.inc.c +++ b/target/xtensa/core-dc233c/gdb-config.inc.c @@ -143,3 +143,4 @@ XTREG(117, 468, 32, 4, 4, 0x000c, 0x0006, -2, 8, 0x0100, a12, 0, 0, 0, 0 XTREG(118, 472, 32, 4, 4, 0x000d, 0x0006, -2, 8, 0x0100, a13, 0, 0, 0, 0, 0, 0) XTREG(119, 476, 32, 4, 4, 0x000e, 0x0006, -2, 8, 0x0100, a14, 0, 0, 0, 0, 0, 0) XTREG(120, 480, 32, 4, 4, 0x000f, 0x0006, -2, 8, 0x0100, a15, 0, 0, 0, 0, 0, 0) +XTREG_END diff --git a/target/xtensa/core-test_kc705_be.c b/target/xtensa/core-test_kc705_be.c new file mode 100644 index 0000000000..6fc20f3cdd --- /dev/null +++ b/target/xtensa/core-test_kc705_be.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2018, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/gdbstub.h" +#include "qemu-common.h" +#include "qemu/host-utils.h" + +#include "core-test_kc705_be/core-isa.h" +#include "overlay_tool.h" + +#define xtensa_modules xtensa_modules_test_kc705_be +#include "core-test_kc705_be/xtensa-modules.inc.c" + +static XtensaConfig test_kc705_be __attribute__((unused)) = { + .name = "test_kc705_be", + .gdb_regmap = { + .reg = { +#include "core-test_kc705_be/gdb-config.inc.c" + } + }, + .isa_internal = &xtensa_modules, + .clock_freq_khz = 40000, + DEFAULT_SECTIONS +}; + +REGISTER_CORE(test_kc705_be) diff --git a/target/xtensa/core-test_kc705_be/core-isa.h b/target/xtensa/core-test_kc705_be/core-isa.h new file mode 100644 index 0000000000..a4ebdf7197 --- /dev/null +++ b/target/xtensa/core-test_kc705_be/core-isa.h @@ -0,0 +1,575 @@ +/* + * xtensa/config/core-isa.h -- HAL definitions that are dependent on Xtensa + * processor CORE configuration + * + * See , which includes this file, for more details. + */ + +/* Xtensa processor core configuration information. + + Copyright (c) 1999-2015 Tensilica 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 _XTENSA_CORE_CONFIGURATION_H +#define _XTENSA_CORE_CONFIGURATION_H + + +/**************************************************************************** + Parameters Useful for Any Code, USER or PRIVILEGED + ****************************************************************************/ + +/* + * Note: Macros of the form XCHAL_HAVE_*** have a value of 1 if the option is + * configured, and a value of 0 otherwise. These macros are always defined. + */ + + +/*---------------------------------------------------------------------- + ISA + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_BE 1 /* big-endian byte ordering */ +#define XCHAL_HAVE_WINDOWED 1 /* windowed registers option */ +#define XCHAL_NUM_AREGS 32 /* num of physical addr regs */ +#define XCHAL_NUM_AREGS_LOG2 5 /* log2(XCHAL_NUM_AREGS) */ +#define XCHAL_MAX_INSTRUCTION_SIZE 8 /* max instr bytes (3..8) */ +#define XCHAL_HAVE_DEBUG 1 /* debug option */ +#define XCHAL_HAVE_DENSITY 1 /* 16-bit instructions */ +#define XCHAL_HAVE_LOOPS 1 /* zero-overhead loops */ +#define XCHAL_LOOP_BUFFER_SIZE 0 /* zero-ov. loop instr buffer size */ +#define XCHAL_HAVE_NSA 1 /* NSA/NSAU instructions */ +#define XCHAL_HAVE_MINMAX 1 /* MIN/MAX instructions */ +#define XCHAL_HAVE_SEXT 1 /* SEXT instruction */ +#define XCHAL_HAVE_DEPBITS 0 /* DEPBITS instruction */ +#define XCHAL_HAVE_CLAMPS 1 /* CLAMPS instruction */ +#define XCHAL_HAVE_MUL16 1 /* MUL16S/MUL16U instructions */ +#define XCHAL_HAVE_MUL32 1 /* MULL instruction */ +#define XCHAL_HAVE_MUL32_HIGH 1 /* MULUH/MULSH instructions */ +#define XCHAL_HAVE_DIV32 1 /* QUOS/QUOU/REMS/REMU instructions */ +#define XCHAL_HAVE_L32R 1 /* L32R instruction */ +#define XCHAL_HAVE_ABSOLUTE_LITERALS 0 /* non-PC-rel (extended) L32R */ +#define XCHAL_HAVE_CONST16 0 /* CONST16 instruction */ +#define XCHAL_HAVE_ADDX 1 /* ADDX#/SUBX# instructions */ +#define XCHAL_HAVE_WIDE_BRANCHES 0 /* B*.W18 or B*.W15 instr's */ +#define XCHAL_HAVE_PREDICTED_BRANCHES 0 /* B[EQ/EQZ/NE/NEZ]T instr's */ +#define XCHAL_HAVE_CALL4AND12 1 /* (obsolete option) */ +#define XCHAL_HAVE_ABS 1 /* ABS instruction */ +/*#define XCHAL_HAVE_POPC 0*/ /* POPC instruction */ +/*#define XCHAL_HAVE_CRC 0*/ /* CRC instruction */ +#define XCHAL_HAVE_RELEASE_SYNC 1 /* L32AI/S32RI instructions */ +#define XCHAL_HAVE_S32C1I 1 /* S32C1I instruction */ +#define XCHAL_HAVE_SPECULATION 0 /* speculation */ +#define XCHAL_HAVE_FULL_RESET 1 /* all regs/state reset */ +#define XCHAL_NUM_CONTEXTS 1 /* */ +#define XCHAL_NUM_MISC_REGS 2 /* num of scratch regs (0..4) */ +#define XCHAL_HAVE_TAP_MASTER 0 /* JTAG TAP control instr's */ +#define XCHAL_HAVE_PRID 1 /* processor ID register */ +#define XCHAL_HAVE_EXTERN_REGS 1 /* WER/RER instructions */ +#define XCHAL_HAVE_MX 0 /* MX core (Tensilica internal) */ +#define XCHAL_HAVE_MP_INTERRUPTS 0 /* interrupt distributor port */ +#define XCHAL_HAVE_MP_RUNSTALL 0 /* core RunStall control port */ +#define XCHAL_HAVE_PSO 0 /* Power Shut-Off */ +#define XCHAL_HAVE_PSO_CDM 0 /* core/debug/mem pwr domains */ +#define XCHAL_HAVE_PSO_FULL_RETENTION 0 /* all regs preserved on PSO */ +#define XCHAL_HAVE_THREADPTR 1 /* THREADPTR register */ +#define XCHAL_HAVE_BOOLEANS 1 /* boolean registers */ +#define XCHAL_HAVE_CP 1 /* CPENABLE reg (coprocessor) */ +#define XCHAL_CP_MAXCFG 8 /* max allowed cp id plus one */ +#define XCHAL_HAVE_MAC16 1 /* MAC16 package */ + +#define XCHAL_HAVE_FUSION 0 /* Fusion*/ +#define XCHAL_HAVE_FUSION_FP 0 /* Fusion FP option */ +#define XCHAL_HAVE_FUSION_LOW_POWER 0 /* Fusion Low Power option */ +#define XCHAL_HAVE_FUSION_AES 0 /* Fusion BLE/Wifi AES-128 CCM option */ +#define XCHAL_HAVE_FUSION_CONVENC 0 /* Fusion Conv Encode option */ +#define XCHAL_HAVE_FUSION_LFSR_CRC 0 /* Fusion LFSR-CRC option */ +#define XCHAL_HAVE_FUSION_BITOPS 0 /* Fusion Bit Operations Support option */ +#define XCHAL_HAVE_FUSION_AVS 0 /* Fusion AVS option */ +#define XCHAL_HAVE_FUSION_16BIT_BASEBAND 0 /* Fusion 16-bit Baseband option */ +#define XCHAL_HAVE_HIFIPRO 0 /* HiFiPro Audio Engine pkg */ +#define XCHAL_HAVE_HIFI4 0 /* HiFi4 Audio Engine pkg */ +#define XCHAL_HAVE_HIFI4_VFPU 0 /* HiFi4 Audio Engine VFPU option */ +#define XCHAL_HAVE_HIFI3 0 /* HiFi3 Audio Engine pkg */ +#define XCHAL_HAVE_HIFI3_VFPU 0 /* HiFi3 Audio Engine VFPU option */ +#define XCHAL_HAVE_HIFI2 1 /* HiFi2 Audio Engine pkg */ +#define XCHAL_HAVE_HIFI2_MUL32X24 1 /* HiFi2 and 32x24 MACs */ +#define XCHAL_HAVE_HIFI2EP 1 /* HiFi2EP */ +#define XCHAL_HAVE_HIFI_MINI 0 + + +#define XCHAL_HAVE_VECTORFPU2005 0 /* vector or user floating-point pkg */ +#define XCHAL_HAVE_USER_DPFPU 0 /* user DP floating-point pkg */ +#define XCHAL_HAVE_USER_SPFPU 0 /* user DP floating-point pkg */ +#define XCHAL_HAVE_FP 0 /* single prec floating point */ +#define XCHAL_HAVE_FP_DIV 0 /* FP with DIV instructions */ +#define XCHAL_HAVE_FP_RECIP 0 /* FP with RECIP instructions */ +#define XCHAL_HAVE_FP_SQRT 0 /* FP with SQRT instructions */ +#define XCHAL_HAVE_FP_RSQRT 0 /* FP with RSQRT instructions */ +#define XCHAL_HAVE_DFP 0 /* double precision FP pkg */ +#define XCHAL_HAVE_DFP_DIV 0 /* DFP with DIV instructions */ +#define XCHAL_HAVE_DFP_RECIP 0 /* DFP with RECIP instructions*/ +#define XCHAL_HAVE_DFP_SQRT 0 /* DFP with SQRT instructions */ +#define XCHAL_HAVE_DFP_RSQRT 0 /* DFP with RSQRT instructions*/ +#define XCHAL_HAVE_DFP_ACCEL 0 /* double precision FP acceleration pkg */ +#define XCHAL_HAVE_DFP_accel XCHAL_HAVE_DFP_ACCEL /* for backward compatibility */ + +#define XCHAL_HAVE_DFPU_SINGLE_ONLY 0 /* DFPU Coprocessor, single precision only */ +#define XCHAL_HAVE_DFPU_SINGLE_DOUBLE 0 /* DFPU Coprocessor, single and double precision */ +#define XCHAL_HAVE_VECTRA1 0 /* Vectra I pkg */ +#define XCHAL_HAVE_VECTRALX 0 /* Vectra LX pkg */ +#define XCHAL_HAVE_PDX4 0 /* PDX4 */ +#define XCHAL_HAVE_CONNXD2 0 /* ConnX D2 pkg */ +#define XCHAL_HAVE_CONNXD2_DUALLSFLIX 0 /* ConnX D2 & Dual LoadStore Flix */ +#define XCHAL_HAVE_BBE16 0 /* ConnX BBE16 pkg */ +#define XCHAL_HAVE_BBE16_RSQRT 0 /* BBE16 & vector recip sqrt */ +#define XCHAL_HAVE_BBE16_VECDIV 0 /* BBE16 & vector divide */ +#define XCHAL_HAVE_BBE16_DESPREAD 0 /* BBE16 & despread */ +#define XCHAL_HAVE_BBENEP 0 /* ConnX BBENEP pkgs */ +#define XCHAL_HAVE_BSP3 0 /* ConnX BSP3 pkg */ +#define XCHAL_HAVE_BSP3_TRANSPOSE 0 /* BSP3 & transpose32x32 */ +#define XCHAL_HAVE_SSP16 0 /* ConnX SSP16 pkg */ +#define XCHAL_HAVE_SSP16_VITERBI 0 /* SSP16 & viterbi */ +#define XCHAL_HAVE_TURBO16 0 /* ConnX Turbo16 pkg */ +#define XCHAL_HAVE_BBP16 0 /* ConnX BBP16 pkg */ +#define XCHAL_HAVE_FLIX3 0 /* basic 3-way FLIX option */ +#define XCHAL_HAVE_GRIVPEP 0 /* GRIVPEP is General Release of IVPEP */ +#define XCHAL_HAVE_GRIVPEP_HISTOGRAM 0 /* Histogram option on GRIVPEP */ + + +/*---------------------------------------------------------------------- + MISC + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_LOADSTORE_UNITS 1 /* load/store units */ +#define XCHAL_NUM_WRITEBUFFER_ENTRIES 8 /* size of write buffer */ +#define XCHAL_INST_FETCH_WIDTH 8 /* instr-fetch width in bytes */ +#define XCHAL_DATA_WIDTH 8 /* data width in bytes */ +#define XCHAL_DATA_PIPE_DELAY 1 /* d-side pipeline delay + (1 = 5-stage, 2 = 7-stage) */ +#define XCHAL_CLOCK_GATING_GLOBAL 0 /* global clock gating */ +#define XCHAL_CLOCK_GATING_FUNCUNIT 0 /* funct. unit clock gating */ +/* In T1050, applies to selected core load and store instructions (see ISA): */ +#define XCHAL_UNALIGNED_LOAD_EXCEPTION 1 /* unaligned loads cause exc. */ +#define XCHAL_UNALIGNED_STORE_EXCEPTION 1 /* unaligned stores cause exc.*/ +#define XCHAL_UNALIGNED_LOAD_HW 0 /* unaligned loads work in hw */ +#define XCHAL_UNALIGNED_STORE_HW 0 /* unaligned stores work in hw*/ + +#define XCHAL_SW_VERSION 1100002 /* sw version of this header */ + +#define XCHAL_CORE_ID "test_kc705_be" /* alphanum core name + (CoreID) set in the Xtensa + Processor Generator */ + +#define XCHAL_BUILD_UNIQUE_ID 0x00058D8C /* 22-bit sw build ID */ + +/* + * These definitions describe the hardware targeted by this software. + */ +#define XCHAL_HW_CONFIGID0 0xC1B3FFFF /* ConfigID hi 32 bits*/ +#define XCHAL_HW_CONFIGID1 0x1C858D8C /* ConfigID lo 32 bits*/ +#define XCHAL_HW_VERSION_NAME "LX6.0.2" /* full version name */ +#define XCHAL_HW_VERSION_MAJOR 2600 /* major ver# of targeted hw */ +#define XCHAL_HW_VERSION_MINOR 2 /* minor ver# of targeted hw */ +#define XCHAL_HW_VERSION 260002 /* major*100+minor */ +#define XCHAL_HW_REL_LX6 1 +#define XCHAL_HW_REL_LX6_0 1 +#define XCHAL_HW_REL_LX6_0_2 1 +#define XCHAL_HW_CONFIGID_RELIABLE 1 +/* If software targets a *range* of hardware versions, these are the bounds: */ +#define XCHAL_HW_MIN_VERSION_MAJOR 2600 /* major v of earliest tgt hw */ +#define XCHAL_HW_MIN_VERSION_MINOR 2 /* minor v of earliest tgt hw */ +#define XCHAL_HW_MIN_VERSION 260002 /* earliest targeted hw */ +#define XCHAL_HW_MAX_VERSION_MAJOR 2600 /* major v of latest tgt hw */ +#define XCHAL_HW_MAX_VERSION_MINOR 2 /* minor v of latest tgt hw */ +#define XCHAL_HW_MAX_VERSION 260002 /* latest targeted hw */ + + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +#define XCHAL_ICACHE_LINESIZE 32 /* I-cache line size in bytes */ +#define XCHAL_DCACHE_LINESIZE 32 /* D-cache line size in bytes */ +#define XCHAL_ICACHE_LINEWIDTH 5 /* log2(I line size in bytes) */ +#define XCHAL_DCACHE_LINEWIDTH 5 /* log2(D line size in bytes) */ + +#define XCHAL_ICACHE_SIZE 16384 /* I-cache size in bytes or 0 */ +#define XCHAL_DCACHE_SIZE 16384 /* D-cache size in bytes or 0 */ + +#define XCHAL_DCACHE_IS_WRITEBACK 1 /* writeback feature */ +#define XCHAL_DCACHE_IS_COHERENT 0 /* MP coherence feature */ + +#define XCHAL_HAVE_PREFETCH 1 /* PREFCTL register */ +#define XCHAL_HAVE_PREFETCH_L1 0 /* prefetch to L1 dcache */ +#define XCHAL_PREFETCH_CASTOUT_LINES 1 /* dcache pref. castout bufsz */ +#define XCHAL_PREFETCH_ENTRIES 8 /* cache prefetch entries */ +#define XCHAL_PREFETCH_BLOCK_ENTRIES 0 /* prefetch block streams */ +#define XCHAL_HAVE_CACHE_BLOCKOPS 0 /* block prefetch for caches */ +#define XCHAL_HAVE_ICACHE_TEST 1 /* Icache test instructions */ +#define XCHAL_HAVE_DCACHE_TEST 1 /* Dcache test instructions */ +#define XCHAL_HAVE_ICACHE_DYN_WAYS 0 /* Icache dynamic way support */ +#define XCHAL_HAVE_DCACHE_DYN_WAYS 0 /* Dcache dynamic way support */ + + + + +/**************************************************************************** + Parameters Useful for PRIVILEGED (Supervisory or Non-Virtualized) Code + ****************************************************************************/ + + +#ifndef XTENSA_HAL_NON_PRIVILEGED_ONLY + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_PIF 1 /* any outbound PIF present */ + +/* If present, cache size in bytes == (ways * 2^(linewidth + setwidth)). */ + +/* Number of cache sets in log2(lines per way): */ +#define XCHAL_ICACHE_SETWIDTH 7 +#define XCHAL_DCACHE_SETWIDTH 7 + +/* Cache set associativity (number of ways): */ +#define XCHAL_ICACHE_WAYS 4 +#define XCHAL_DCACHE_WAYS 4 + +/* Cache features: */ +#define XCHAL_ICACHE_LINE_LOCKABLE 1 +#define XCHAL_DCACHE_LINE_LOCKABLE 1 +#define XCHAL_ICACHE_ECC_PARITY 0 +#define XCHAL_DCACHE_ECC_PARITY 0 + +/* Cache access size in bytes (affects operation of SICW instruction): */ +#define XCHAL_ICACHE_ACCESS_SIZE 8 +#define XCHAL_DCACHE_ACCESS_SIZE 8 + +#define XCHAL_DCACHE_BANKS 1 /* number of banks */ + +/* Number of encoded cache attr bits (see for decoded bits): */ +#define XCHAL_CA_BITS 4 + +/* Whether MEMCTL register has anything useful */ +#define XCHAL_USE_MEMCTL (((XCHAL_LOOP_BUFFER_SIZE > 0) || \ + XCHAL_DCACHE_IS_COHERENT || \ + XCHAL_HAVE_ICACHE_DYN_WAYS || \ + XCHAL_HAVE_DCACHE_DYN_WAYS) && \ + (XCHAL_HW_MIN_VERSION >= XTENSA_HWVERSION_RE_2012_0)) + + +/*---------------------------------------------------------------------- + INTERNAL I/D RAM/ROMs and XLMI + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_INSTROM 0 /* number of core instr. ROMs */ +#define XCHAL_NUM_INSTRAM 0 /* number of core instr. RAMs */ +#define XCHAL_NUM_DATAROM 0 /* number of core data ROMs */ +#define XCHAL_NUM_DATARAM 0 /* number of core data RAMs */ +#define XCHAL_NUM_URAM 0 /* number of core unified RAMs*/ +#define XCHAL_NUM_XLMI 0 /* number of core XLMI ports */ + +#define XCHAL_HAVE_IMEM_LOADSTORE 1 /* can load/store to IROM/IRAM*/ + + +/*---------------------------------------------------------------------- + INTERRUPTS and TIMERS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_INTERRUPTS 1 /* interrupt option */ +#define XCHAL_HAVE_HIGHPRI_INTERRUPTS 1 /* med/high-pri. interrupts */ +#define XCHAL_HAVE_NMI 1 /* non-maskable interrupt */ +#define XCHAL_HAVE_CCOUNT 1 /* CCOUNT reg. (timer option) */ +#define XCHAL_NUM_TIMERS 3 /* number of CCOMPAREn regs */ +#define XCHAL_NUM_INTERRUPTS 22 /* number of interrupts */ +#define XCHAL_NUM_INTERRUPTS_LOG2 5 /* ceil(log2(NUM_INTERRUPTS)) */ +#define XCHAL_NUM_EXTINTERRUPTS 16 /* num of external interrupts */ +#define XCHAL_NUM_INTLEVELS 6 /* number of interrupt levels + (not including level zero) */ +#define XCHAL_EXCM_LEVEL 4 /* level masked by PS.EXCM */ + /* (always 1 in XEA1; levels 2 .. EXCM_LEVEL are "medium priority") */ + +/* Masks of interrupts at each interrupt level: */ +#define XCHAL_INTLEVEL1_MASK 0x001F00BF +#define XCHAL_INTLEVEL2_MASK 0x00000140 +#define XCHAL_INTLEVEL3_MASK 0x00200E00 +#define XCHAL_INTLEVEL4_MASK 0x00008000 +#define XCHAL_INTLEVEL5_MASK 0x00003000 +#define XCHAL_INTLEVEL6_MASK 0x00000000 +#define XCHAL_INTLEVEL7_MASK 0x00004000 + +/* Masks of interrupts at each range 1..n of interrupt levels: */ +#define XCHAL_INTLEVEL1_ANDBELOW_MASK 0x001F00BF +#define XCHAL_INTLEVEL2_ANDBELOW_MASK 0x001F01FF +#define XCHAL_INTLEVEL3_ANDBELOW_MASK 0x003F0FFF +#define XCHAL_INTLEVEL4_ANDBELOW_MASK 0x003F8FFF +#define XCHAL_INTLEVEL5_ANDBELOW_MASK 0x003FBFFF +#define XCHAL_INTLEVEL6_ANDBELOW_MASK 0x003FBFFF +#define XCHAL_INTLEVEL7_ANDBELOW_MASK 0x003FFFFF + +/* Level of each interrupt: */ +#define XCHAL_INT0_LEVEL 1 +#define XCHAL_INT1_LEVEL 1 +#define XCHAL_INT2_LEVEL 1 +#define XCHAL_INT3_LEVEL 1 +#define XCHAL_INT4_LEVEL 1 +#define XCHAL_INT5_LEVEL 1 +#define XCHAL_INT6_LEVEL 2 +#define XCHAL_INT7_LEVEL 1 +#define XCHAL_INT8_LEVEL 2 +#define XCHAL_INT9_LEVEL 3 +#define XCHAL_INT10_LEVEL 3 +#define XCHAL_INT11_LEVEL 3 +#define XCHAL_INT12_LEVEL 5 +#define XCHAL_INT13_LEVEL 5 +#define XCHAL_INT14_LEVEL 7 +#define XCHAL_INT15_LEVEL 4 +#define XCHAL_INT16_LEVEL 1 +#define XCHAL_INT17_LEVEL 1 +#define XCHAL_INT18_LEVEL 1 +#define XCHAL_INT19_LEVEL 1 +#define XCHAL_INT20_LEVEL 1 +#define XCHAL_INT21_LEVEL 3 +#define XCHAL_DEBUGLEVEL 6 /* debug interrupt level */ +#define XCHAL_HAVE_DEBUG_EXTERN_INT 1 /* OCD external db interrupt */ +#define XCHAL_NMILEVEL 7 /* NMI "level" (for use with + EXCSAVE/EPS/EPC_n, RFI n) */ + +/* Type of each interrupt: */ +#define XCHAL_INT0_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT1_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT2_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT3_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT4_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT5_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT6_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT7_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT8_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT9_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT10_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT11_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT12_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT13_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT14_TYPE XTHAL_INTTYPE_NMI +#define XCHAL_INT15_TYPE XTHAL_INTTYPE_PROFILING +#define XCHAL_INT16_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT17_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT18_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT19_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT20_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT21_TYPE XTHAL_INTTYPE_EXTERN_EDGE + +/* Masks of interrupts for each type of interrupt: */ +#define XCHAL_INTTYPE_MASK_UNCONFIGURED 0xFFC00000 +#define XCHAL_INTTYPE_MASK_SOFTWARE 0x00000880 +#define XCHAL_INTTYPE_MASK_EXTERN_EDGE 0x003F0000 +#define XCHAL_INTTYPE_MASK_EXTERN_LEVEL 0x0000133F +#define XCHAL_INTTYPE_MASK_TIMER 0x00002440 +#define XCHAL_INTTYPE_MASK_NMI 0x00004000 +#define XCHAL_INTTYPE_MASK_WRITE_ERROR 0x00000000 +#define XCHAL_INTTYPE_MASK_PROFILING 0x00008000 + +/* Interrupt numbers assigned to specific interrupt sources: */ +#define XCHAL_TIMER0_INTERRUPT 6 /* CCOMPARE0 */ +#define XCHAL_TIMER1_INTERRUPT 10 /* CCOMPARE1 */ +#define XCHAL_TIMER2_INTERRUPT 13 /* CCOMPARE2 */ +#define XCHAL_TIMER3_INTERRUPT XTHAL_TIMER_UNCONFIGURED +#define XCHAL_NMI_INTERRUPT 14 /* non-maskable interrupt */ +#define XCHAL_PROFILING_INTERRUPT 15 /* profiling interrupt */ + +/* Interrupt numbers for levels at which only one interrupt is configured: */ +#define XCHAL_INTLEVEL4_NUM 15 +#define XCHAL_INTLEVEL7_NUM 14 +/* (There are many interrupts each at level(s) 1, 2, 3, 5.) */ + + +/* + * External interrupt mapping. + * These macros describe how Xtensa processor interrupt numbers + * (as numbered internally, eg. in INTERRUPT and INTENABLE registers) + * map to external BInterrupt pins, for those interrupts + * configured as external (level-triggered, edge-triggered, or NMI). + * See the Xtensa processor databook for more details. + */ + +/* Core interrupt numbers mapped to each EXTERNAL BInterrupt pin number: */ +#define XCHAL_EXTINT0_NUM 0 /* (intlevel 1) */ +#define XCHAL_EXTINT1_NUM 1 /* (intlevel 1) */ +#define XCHAL_EXTINT2_NUM 2 /* (intlevel 1) */ +#define XCHAL_EXTINT3_NUM 3 /* (intlevel 1) */ +#define XCHAL_EXTINT4_NUM 4 /* (intlevel 1) */ +#define XCHAL_EXTINT5_NUM 5 /* (intlevel 1) */ +#define XCHAL_EXTINT6_NUM 8 /* (intlevel 2) */ +#define XCHAL_EXTINT7_NUM 9 /* (intlevel 3) */ +#define XCHAL_EXTINT8_NUM 12 /* (intlevel 5) */ +#define XCHAL_EXTINT9_NUM 14 /* (intlevel 7) */ +#define XCHAL_EXTINT10_NUM 16 /* (intlevel 1) */ +#define XCHAL_EXTINT11_NUM 17 /* (intlevel 1) */ +#define XCHAL_EXTINT12_NUM 18 /* (intlevel 1) */ +#define XCHAL_EXTINT13_NUM 19 /* (intlevel 1) */ +#define XCHAL_EXTINT14_NUM 20 /* (intlevel 1) */ +#define XCHAL_EXTINT15_NUM 21 /* (intlevel 3) */ +/* EXTERNAL BInterrupt pin numbers mapped to each core interrupt number: */ +#define XCHAL_INT0_EXTNUM 0 /* (intlevel 1) */ +#define XCHAL_INT1_EXTNUM 1 /* (intlevel 1) */ +#define XCHAL_INT2_EXTNUM 2 /* (intlevel 1) */ +#define XCHAL_INT3_EXTNUM 3 /* (intlevel 1) */ +#define XCHAL_INT4_EXTNUM 4 /* (intlevel 1) */ +#define XCHAL_INT5_EXTNUM 5 /* (intlevel 1) */ +#define XCHAL_INT8_EXTNUM 6 /* (intlevel 2) */ +#define XCHAL_INT9_EXTNUM 7 /* (intlevel 3) */ +#define XCHAL_INT12_EXTNUM 8 /* (intlevel 5) */ +#define XCHAL_INT14_EXTNUM 9 /* (intlevel 7) */ +#define XCHAL_INT16_EXTNUM 10 /* (intlevel 1) */ +#define XCHAL_INT17_EXTNUM 11 /* (intlevel 1) */ +#define XCHAL_INT18_EXTNUM 12 /* (intlevel 1) */ +#define XCHAL_INT19_EXTNUM 13 /* (intlevel 1) */ +#define XCHAL_INT20_EXTNUM 14 /* (intlevel 1) */ +#define XCHAL_INT21_EXTNUM 15 /* (intlevel 3) */ + + +/*---------------------------------------------------------------------- + EXCEPTIONS and VECTORS + ----------------------------------------------------------------------*/ + +#define XCHAL_XEA_VERSION 2 /* Xtensa Exception Architecture + number: 1 == XEA1 (old) + 2 == XEA2 (new) + 0 == XEAX (extern) or TX */ +#define XCHAL_HAVE_XEA1 0 /* Exception Architecture 1 */ +#define XCHAL_HAVE_XEA2 1 /* Exception Architecture 2 */ +#define XCHAL_HAVE_XEAX 0 /* External Exception Arch. */ +#define XCHAL_HAVE_EXCEPTIONS 1 /* exception option */ +#define XCHAL_HAVE_HALT 0 /* halt architecture option */ +#define XCHAL_HAVE_BOOTLOADER 0 /* boot loader (for TX) */ +#define XCHAL_HAVE_MEM_ECC_PARITY 0 /* local memory ECC/parity */ +#define XCHAL_HAVE_VECTOR_SELECT 1 /* relocatable vectors */ +#define XCHAL_HAVE_VECBASE 1 /* relocatable vectors */ +#define XCHAL_VECBASE_RESET_VADDR 0x00002000 /* VECBASE reset value */ +#define XCHAL_VECBASE_RESET_PADDR 0x00002000 +#define XCHAL_RESET_VECBASE_OVERLAP 0 + +#define XCHAL_RESET_VECTOR0_VADDR 0xFE000000 +#define XCHAL_RESET_VECTOR0_PADDR 0xFE000000 +#define XCHAL_RESET_VECTOR1_VADDR 0x00001000 +#define XCHAL_RESET_VECTOR1_PADDR 0x00001000 +#define XCHAL_RESET_VECTOR_VADDR 0xFE000000 +#define XCHAL_RESET_VECTOR_PADDR 0xFE000000 +#define XCHAL_USER_VECOFS 0x00000340 +#define XCHAL_USER_VECTOR_VADDR 0x00002340 +#define XCHAL_USER_VECTOR_PADDR 0x00002340 +#define XCHAL_KERNEL_VECOFS 0x00000300 +#define XCHAL_KERNEL_VECTOR_VADDR 0x00002300 +#define XCHAL_KERNEL_VECTOR_PADDR 0x00002300 +#define XCHAL_DOUBLEEXC_VECOFS 0x000003C0 +#define XCHAL_DOUBLEEXC_VECTOR_VADDR 0x000023C0 +#define XCHAL_DOUBLEEXC_VECTOR_PADDR 0x000023C0 +#define XCHAL_WINDOW_OF4_VECOFS 0x00000000 +#define XCHAL_WINDOW_UF4_VECOFS 0x00000040 +#define XCHAL_WINDOW_OF8_VECOFS 0x00000080 +#define XCHAL_WINDOW_UF8_VECOFS 0x000000C0 +#define XCHAL_WINDOW_OF12_VECOFS 0x00000100 +#define XCHAL_WINDOW_UF12_VECOFS 0x00000140 +#define XCHAL_WINDOW_VECTORS_VADDR 0x00002000 +#define XCHAL_WINDOW_VECTORS_PADDR 0x00002000 +#define XCHAL_INTLEVEL2_VECOFS 0x00000180 +#define XCHAL_INTLEVEL2_VECTOR_VADDR 0x00002180 +#define XCHAL_INTLEVEL2_VECTOR_PADDR 0x00002180 +#define XCHAL_INTLEVEL3_VECOFS 0x000001C0 +#define XCHAL_INTLEVEL3_VECTOR_VADDR 0x000021C0 +#define XCHAL_INTLEVEL3_VECTOR_PADDR 0x000021C0 +#define XCHAL_INTLEVEL4_VECOFS 0x00000200 +#define XCHAL_INTLEVEL4_VECTOR_VADDR 0x00002200 +#define XCHAL_INTLEVEL4_VECTOR_PADDR 0x00002200 +#define XCHAL_INTLEVEL5_VECOFS 0x00000240 +#define XCHAL_INTLEVEL5_VECTOR_VADDR 0x00002240 +#define XCHAL_INTLEVEL5_VECTOR_PADDR 0x00002240 +#define XCHAL_INTLEVEL6_VECOFS 0x00000280 +#define XCHAL_INTLEVEL6_VECTOR_VADDR 0x00002280 +#define XCHAL_INTLEVEL6_VECTOR_PADDR 0x00002280 +#define XCHAL_DEBUG_VECOFS XCHAL_INTLEVEL6_VECOFS +#define XCHAL_DEBUG_VECTOR_VADDR XCHAL_INTLEVEL6_VECTOR_VADDR +#define XCHAL_DEBUG_VECTOR_PADDR XCHAL_INTLEVEL6_VECTOR_PADDR +#define XCHAL_NMI_VECOFS 0x000002C0 +#define XCHAL_NMI_VECTOR_VADDR 0x000022C0 +#define XCHAL_NMI_VECTOR_PADDR 0x000022C0 +#define XCHAL_INTLEVEL7_VECOFS XCHAL_NMI_VECOFS +#define XCHAL_INTLEVEL7_VECTOR_VADDR XCHAL_NMI_VECTOR_VADDR +#define XCHAL_INTLEVEL7_VECTOR_PADDR XCHAL_NMI_VECTOR_PADDR + + +/*---------------------------------------------------------------------- + DEBUG MODULE + ----------------------------------------------------------------------*/ + +/* Misc */ +#define XCHAL_HAVE_DEBUG_ERI 1 /* ERI to debug module */ +#define XCHAL_HAVE_DEBUG_APB 0 /* APB to debug module */ +#define XCHAL_HAVE_DEBUG_JTAG 1 /* JTAG to debug module */ + +/* On-Chip Debug (OCD) */ +#define XCHAL_HAVE_OCD 1 /* OnChipDebug option */ +#define XCHAL_NUM_IBREAK 2 /* number of IBREAKn regs */ +#define XCHAL_NUM_DBREAK 2 /* number of DBREAKn regs */ +#define XCHAL_HAVE_OCD_DIR_ARRAY 0 /* faster OCD option (to LX4) */ +#define XCHAL_HAVE_OCD_LS32DDR 1 /* L32DDR/S32DDR (faster OCD) */ + +/* TRAX (in core) */ +#define XCHAL_HAVE_TRAX 1 /* TRAX in debug module */ +#define XCHAL_TRAX_MEM_SIZE 262144 /* TRAX memory size in bytes */ +#define XCHAL_TRAX_MEM_SHAREABLE 1 /* start/end regs; ready sig. */ +#define XCHAL_TRAX_ATB_WIDTH 0 /* ATB width (bits), 0=no ATB */ +#define XCHAL_TRAX_TIME_WIDTH 0 /* timestamp bitwidth, 0=none */ + +/* Perf counters */ +#define XCHAL_NUM_PERF_COUNTERS 8 /* performance counters */ + + +/*---------------------------------------------------------------------- + MMU + ----------------------------------------------------------------------*/ + +/* See core-matmap.h header file for more details. */ + +#define XCHAL_HAVE_TLBS 1 /* inverse of HAVE_CACHEATTR */ +#define XCHAL_HAVE_SPANNING_WAY 1 /* one way maps I+D 4GB vaddr */ +#define XCHAL_SPANNING_WAY 6 /* TLB spanning way number */ +#define XCHAL_HAVE_IDENTITY_MAP 0 /* vaddr == paddr always */ +#define XCHAL_HAVE_CACHEATTR 0 /* CACHEATTR register present */ +#define XCHAL_HAVE_MIMIC_CACHEATTR 0 /* region protection */ +#define XCHAL_HAVE_XLT_CACHEATTR 0 /* region prot. w/translation */ +#define XCHAL_HAVE_PTP_MMU 1 /* full MMU (with page table + [autorefill] and protection) + usable for an MMU-based OS */ +/* If none of the above last 4 are set, it's a custom TLB configuration. */ +#define XCHAL_ITLB_ARF_ENTRIES_LOG2 2 /* log2(autorefill way size) */ +#define XCHAL_DTLB_ARF_ENTRIES_LOG2 2 /* log2(autorefill way size) */ + +#define XCHAL_MMU_ASID_BITS 8 /* number of bits in ASIDs */ +#define XCHAL_MMU_RINGS 4 /* number of rings (1..4) */ +#define XCHAL_MMU_RING_BITS 2 /* num of bits in RING field */ + +#endif /* !XTENSA_HAL_NON_PRIVILEGED_ONLY */ + + +#endif /* _XTENSA_CORE_CONFIGURATION_H */ + diff --git a/target/xtensa/core-test_kc705_be/gdb-config.inc.c b/target/xtensa/core-test_kc705_be/gdb-config.inc.c new file mode 100644 index 0000000000..eb3e03cd52 --- /dev/null +++ b/target/xtensa/core-test_kc705_be/gdb-config.inc.c @@ -0,0 +1,259 @@ +/* Configuration for the Xtensa architecture for GDB, the GNU debugger. + + Copyright (c) 2003-2015 Tensilica 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. */ + XTREG( 0, 0,32, 4, 4,0x0020,0x0006,-2, 9,0x0100,pc, 0,0,0,0,0,0) + XTREG( 1, 4,32, 4, 4,0x0100,0x0006,-2, 1,0x0002,ar0, 0,0,0,0,0,0) + XTREG( 2, 8,32, 4, 4,0x0101,0x0006,-2, 1,0x0002,ar1, 0,0,0,0,0,0) + XTREG( 3, 12,32, 4, 4,0x0102,0x0006,-2, 1,0x0002,ar2, 0,0,0,0,0,0) + XTREG( 4, 16,32, 4, 4,0x0103,0x0006,-2, 1,0x0002,ar3, 0,0,0,0,0,0) + XTREG( 5, 20,32, 4, 4,0x0104,0x0006,-2, 1,0x0002,ar4, 0,0,0,0,0,0) + XTREG( 6, 24,32, 4, 4,0x0105,0x0006,-2, 1,0x0002,ar5, 0,0,0,0,0,0) + XTREG( 7, 28,32, 4, 4,0x0106,0x0006,-2, 1,0x0002,ar6, 0,0,0,0,0,0) + XTREG( 8, 32,32, 4, 4,0x0107,0x0006,-2, 1,0x0002,ar7, 0,0,0,0,0,0) + XTREG( 9, 36,32, 4, 4,0x0108,0x0006,-2, 1,0x0002,ar8, 0,0,0,0,0,0) + XTREG( 10, 40,32, 4, 4,0x0109,0x0006,-2, 1,0x0002,ar9, 0,0,0,0,0,0) + XTREG( 11, 44,32, 4, 4,0x010a,0x0006,-2, 1,0x0002,ar10, 0,0,0,0,0,0) + XTREG( 12, 48,32, 4, 4,0x010b,0x0006,-2, 1,0x0002,ar11, 0,0,0,0,0,0) + XTREG( 13, 52,32, 4, 4,0x010c,0x0006,-2, 1,0x0002,ar12, 0,0,0,0,0,0) + XTREG( 14, 56,32, 4, 4,0x010d,0x0006,-2, 1,0x0002,ar13, 0,0,0,0,0,0) + XTREG( 15, 60,32, 4, 4,0x010e,0x0006,-2, 1,0x0002,ar14, 0,0,0,0,0,0) + XTREG( 16, 64,32, 4, 4,0x010f,0x0006,-2, 1,0x0002,ar15, 0,0,0,0,0,0) + XTREG( 17, 68,32, 4, 4,0x0110,0x0006,-2, 1,0x0002,ar16, 0,0,0,0,0,0) + XTREG( 18, 72,32, 4, 4,0x0111,0x0006,-2, 1,0x0002,ar17, 0,0,0,0,0,0) + XTREG( 19, 76,32, 4, 4,0x0112,0x0006,-2, 1,0x0002,ar18, 0,0,0,0,0,0) + XTREG( 20, 80,32, 4, 4,0x0113,0x0006,-2, 1,0x0002,ar19, 0,0,0,0,0,0) + XTREG( 21, 84,32, 4, 4,0x0114,0x0006,-2, 1,0x0002,ar20, 0,0,0,0,0,0) + XTREG( 22, 88,32, 4, 4,0x0115,0x0006,-2, 1,0x0002,ar21, 0,0,0,0,0,0) + XTREG( 23, 92,32, 4, 4,0x0116,0x0006,-2, 1,0x0002,ar22, 0,0,0,0,0,0) + XTREG( 24, 96,32, 4, 4,0x0117,0x0006,-2, 1,0x0002,ar23, 0,0,0,0,0,0) + XTREG( 25,100,32, 4, 4,0x0118,0x0006,-2, 1,0x0002,ar24, 0,0,0,0,0,0) + XTREG( 26,104,32, 4, 4,0x0119,0x0006,-2, 1,0x0002,ar25, 0,0,0,0,0,0) + XTREG( 27,108,32, 4, 4,0x011a,0x0006,-2, 1,0x0002,ar26, 0,0,0,0,0,0) + XTREG( 28,112,32, 4, 4,0x011b,0x0006,-2, 1,0x0002,ar27, 0,0,0,0,0,0) + XTREG( 29,116,32, 4, 4,0x011c,0x0006,-2, 1,0x0002,ar28, 0,0,0,0,0,0) + XTREG( 30,120,32, 4, 4,0x011d,0x0006,-2, 1,0x0002,ar29, 0,0,0,0,0,0) + XTREG( 31,124,32, 4, 4,0x011e,0x0006,-2, 1,0x0002,ar30, 0,0,0,0,0,0) + XTREG( 32,128,32, 4, 4,0x011f,0x0006,-2, 1,0x0002,ar31, 0,0,0,0,0,0) + XTREG( 33,132,32, 4, 4,0x0200,0x0006,-2, 2,0x1100,lbeg, 0,0,0,0,0,0) + XTREG( 34,136,32, 4, 4,0x0201,0x0006,-2, 2,0x1100,lend, 0,0,0,0,0,0) + XTREG( 35,140,32, 4, 4,0x0202,0x0006,-2, 2,0x1100,lcount, 0,0,0,0,0,0) + XTREG( 36,144, 6, 4, 4,0x0203,0x0006,-2, 2,0x1100,sar, 0,0,0,0,0,0) + XTREG( 37,148,13, 4, 4,0x0228,0x0006,-2, 2,0x1100,prefctl, 0,0,0,0,0,0) + XTREG( 38,152, 3, 4, 4,0x0248,0x0006,-2, 2,0x1002,windowbase, 0,0,0,0,0,0) + XTREG( 39,156, 8, 4, 4,0x0249,0x0006,-2, 2,0x1002,windowstart, 0,0,0,0,0,0) + XTREG( 40,160,32, 4, 4,0x02b0,0x0002,-2, 2,0x1000,configid0, 0,0,0,0,0,0) + XTREG( 41,164,32, 4, 4,0x02d0,0x0002,-2, 2,0x1000,configid1, 0,0,0,0,0,0) + XTREG( 42,168,19, 4, 4,0x02e6,0x0006,-2, 2,0x1100,ps, 0,0,0,0,0,0) + XTREG( 43,172,32, 4, 4,0x03e7,0x0006,-2, 3,0x0110,threadptr, 0,0,0,0,0,0) + XTREG( 44,176,16, 4, 4,0x0204,0x0006,-1, 2,0x1100,br, 0,0,0,0,0,0) + XTREG( 45,180,32, 4, 4,0x020c,0x0006,-1, 2,0x1100,scompare1, 0,0,0,0,0,0) + XTREG( 46,184,32, 4, 4,0x0210,0x0006,-1, 2,0x1100,acclo, 0,0,0,0,0,0) + XTREG( 47,188, 8, 4, 4,0x0211,0x0006,-1, 2,0x1100,acchi, 0,0,0,0,0,0) + XTREG( 48,192,32, 4, 4,0x0220,0x0006,-1, 2,0x1100,m0, 0,0,0,0,0,0) + XTREG( 49,196,32, 4, 4,0x0221,0x0006,-1, 2,0x1100,m1, 0,0,0,0,0,0) + XTREG( 50,200,32, 4, 4,0x0222,0x0006,-1, 2,0x1100,m2, 0,0,0,0,0,0) + XTREG( 51,204,32, 4, 4,0x0223,0x0006,-1, 2,0x1100,m3, 0,0,0,0,0,0) + XTREG( 52,208,32, 4, 4,0x03e6,0x000e,-1, 3,0x0110,expstate, 0,0,0,0,0,0) + XTREG( 53,212,48, 8, 8,0x0060,0x0006, 1, 4,0x0101,aep0, + "03:40:48:2b","03:40:48:7a",0,0,0,0) + XTREG( 54,220,48, 8, 8,0x0061,0x0006, 1, 4,0x0101,aep1, + "03:40:49:2b","03:40:49:7a",0,0,0,0) + XTREG( 55,228,48, 8, 8,0x0062,0x0006, 1, 4,0x0101,aep2, + "03:40:4a:2b","03:40:4a:7a",0,0,0,0) + XTREG( 56,236,48, 8, 8,0x0063,0x0006, 1, 4,0x0101,aep3, + "03:40:4b:2b","03:40:4b:7a",0,0,0,0) + XTREG( 57,244,48, 8, 8,0x0064,0x0006, 1, 4,0x0101,aep4, + "03:40:4c:2b","03:40:4c:7a",0,0,0,0) + XTREG( 58,252,48, 8, 8,0x0065,0x0006, 1, 4,0x0101,aep5, + "03:40:4d:2b","03:40:4d:7a",0,0,0,0) + XTREG( 59,260,48, 8, 8,0x0066,0x0006, 1, 4,0x0101,aep6, + "03:40:4e:2b","03:40:4e:7a",0,0,0,0) + XTREG( 60,268,48, 8, 8,0x0067,0x0006, 1, 4,0x0101,aep7, + "03:40:4f:2b","03:40:4f:7a",0,0,0,0) + XTREG( 61,276,56, 8, 8,0x0068,0x0006, 1, 4,0x0101,aeq0, + "03:40:40:3c","03:40:40:1c",0,0,0,0) + XTREG( 62,284,56, 8, 8,0x0069,0x0006, 1, 4,0x0101,aeq1, + "03:40:41:3c","03:40:44:1c",0,0,0,0) + XTREG( 63,292,56, 8, 8,0x006a,0x0006, 1, 4,0x0101,aeq2, + "03:40:42:3c","03:40:48:1c",0,0,0,0) + XTREG( 64,300,56, 8, 8,0x006b,0x0006, 1, 4,0x0101,aeq3, + "03:40:43:3c","03:40:4c:1c",0,0,0,0) + XTREG( 65,308, 7, 4, 4,0x03f0,0x0006, 1, 3,0x0100,ae_ovf_sar, 0,0,0,0,0,0) + XTREG( 66,312,32, 4, 4,0x03f1,0x0006, 1, 3,0x0110,ae_bithead, 0,0,0,0,0,0) + XTREG( 67,316,16, 4, 4,0x03f2,0x0006, 1, 3,0x0100,ae_ts_fts_bu_bp,0,0,0,0,0,0) + XTREG( 68,320,28, 4, 4,0x03f3,0x0006, 1, 3,0x0100,ae_sd_no, 0,0,0,0,0,0) + XTREG( 69,324,32, 4, 4,0x03f6,0x0006, 1, 3,0x0110,ae_cbegin0, 0,0,0,0,0,0) + XTREG( 70,328,32, 4, 4,0x03f7,0x0006, 1, 3,0x0110,ae_cend0, 0,0,0,0,0,0) + XTREG( 71,332,32, 4, 4,0x0253,0x0007,-2, 2,0x1000,ptevaddr, 0,0,0,0,0,0) + XTREG( 72,336,32, 4, 4,0x0259,0x000d,-2, 2,0x1000,mmid, 0,0,0,0,0,0) + XTREG( 73,340,32, 4, 4,0x025a,0x0007,-2, 2,0x1000,rasid, 0,0,0,0,0,0) + XTREG( 74,344,25, 4, 4,0x025b,0x0007,-2, 2,0x1000,itlbcfg, 0,0,0,0,0,0) + XTREG( 75,348,25, 4, 4,0x025c,0x0007,-2, 2,0x1000,dtlbcfg, 0,0,0,0,0,0) + XTREG( 76,352, 2, 4, 4,0x0260,0x0007,-2, 2,0x1000,ibreakenable,0,0,0,0,0,0) + XTREG( 77,356, 6, 4, 4,0x0263,0x0007,-2, 2,0x1000,atomctl, 0,0,0,0,0,0) + XTREG( 78,360,32, 4, 4,0x0268,0x0007,-2, 2,0x1000,ddr, 0,0,0,0,0,0) + XTREG( 79,364,32, 4, 4,0x0280,0x0007,-2, 2,0x1000,ibreaka0, 0,0,0,0,0,0) + XTREG( 80,368,32, 4, 4,0x0281,0x0007,-2, 2,0x1000,ibreaka1, 0,0,0,0,0,0) + XTREG( 81,372,32, 4, 4,0x0290,0x0007,-2, 2,0x1000,dbreaka0, 0,0,0,0,0,0) + XTREG( 82,376,32, 4, 4,0x0291,0x0007,-2, 2,0x1000,dbreaka1, 0,0,0,0,0,0) + XTREG( 83,380,32, 4, 4,0x02a0,0x0007,-2, 2,0x1000,dbreakc0, 0,0,0,0,0,0) + XTREG( 84,384,32, 4, 4,0x02a1,0x0007,-2, 2,0x1000,dbreakc1, 0,0,0,0,0,0) + XTREG( 85,388,32, 4, 4,0x02b1,0x0007,-2, 2,0x1000,epc1, 0,0,0,0,0,0) + XTREG( 86,392,32, 4, 4,0x02b2,0x0007,-2, 2,0x1000,epc2, 0,0,0,0,0,0) + XTREG( 87,396,32, 4, 4,0x02b3,0x0007,-2, 2,0x1000,epc3, 0,0,0,0,0,0) + XTREG( 88,400,32, 4, 4,0x02b4,0x0007,-2, 2,0x1000,epc4, 0,0,0,0,0,0) + XTREG( 89,404,32, 4, 4,0x02b5,0x0007,-2, 2,0x1000,epc5, 0,0,0,0,0,0) + XTREG( 90,408,32, 4, 4,0x02b6,0x0007,-2, 2,0x1000,epc6, 0,0,0,0,0,0) + XTREG( 91,412,32, 4, 4,0x02b7,0x0007,-2, 2,0x1000,epc7, 0,0,0,0,0,0) + XTREG( 92,416,32, 4, 4,0x02c0,0x0007,-2, 2,0x1000,depc, 0,0,0,0,0,0) + XTREG( 93,420,19, 4, 4,0x02c2,0x0007,-2, 2,0x1000,eps2, 0,0,0,0,0,0) + XTREG( 94,424,19, 4, 4,0x02c3,0x0007,-2, 2,0x1000,eps3, 0,0,0,0,0,0) + XTREG( 95,428,19, 4, 4,0x02c4,0x0007,-2, 2,0x1000,eps4, 0,0,0,0,0,0) + XTREG( 96,432,19, 4, 4,0x02c5,0x0007,-2, 2,0x1000,eps5, 0,0,0,0,0,0) + XTREG( 97,436,19, 4, 4,0x02c6,0x0007,-2, 2,0x1000,eps6, 0,0,0,0,0,0) + XTREG( 98,440,19, 4, 4,0x02c7,0x0007,-2, 2,0x1000,eps7, 0,0,0,0,0,0) + XTREG( 99,444,32, 4, 4,0x02d1,0x0007,-2, 2,0x1000,excsave1, 0,0,0,0,0,0) + XTREG(100,448,32, 4, 4,0x02d2,0x0007,-2, 2,0x1000,excsave2, 0,0,0,0,0,0) + XTREG(101,452,32, 4, 4,0x02d3,0x0007,-2, 2,0x1000,excsave3, 0,0,0,0,0,0) + XTREG(102,456,32, 4, 4,0x02d4,0x0007,-2, 2,0x1000,excsave4, 0,0,0,0,0,0) + XTREG(103,460,32, 4, 4,0x02d5,0x0007,-2, 2,0x1000,excsave5, 0,0,0,0,0,0) + XTREG(104,464,32, 4, 4,0x02d6,0x0007,-2, 2,0x1000,excsave6, 0,0,0,0,0,0) + XTREG(105,468,32, 4, 4,0x02d7,0x0007,-2, 2,0x1000,excsave7, 0,0,0,0,0,0) + XTREG(106,472, 8, 4, 4,0x02e0,0x0007,-2, 2,0x1000,cpenable, 0,0,0,0,0,0) + XTREG(107,476,22, 4, 4,0x02e2,0x000b,-2, 2,0x1000,interrupt, 0,0,0,0,0,0) + XTREG(108,480,22, 4, 4,0x02e2,0x000d,-2, 2,0x1000,intset, 0,0,0,0,0,0) + XTREG(109,484,22, 4, 4,0x02e3,0x000d,-2, 2,0x1000,intclear, 0,0,0,0,0,0) + XTREG(110,488,22, 4, 4,0x02e4,0x0007,-2, 2,0x1000,intenable, 0,0,0,0,0,0) + XTREG(111,492,32, 4, 4,0x02e7,0x0007,-2, 2,0x1000,vecbase, 0,0,0,0,0,0) + XTREG(112,496, 6, 4, 4,0x02e8,0x0007,-2, 2,0x1000,exccause, 0,0,0,0,0,0) + XTREG(113,500,12, 4, 4,0x02e9,0x0003,-2, 2,0x1000,debugcause, 0,0,0,0,0,0) + XTREG(114,504,32, 4, 4,0x02ea,0x000f,-2, 2,0x1000,ccount, 0,0,0,0,0,0) + XTREG(115,508,32, 4, 4,0x02eb,0x0003,-2, 2,0x1000,prid, 0,0,0,0,0,0) + XTREG(116,512,32, 4, 4,0x02ec,0x000f,-2, 2,0x1000,icount, 0,0,0,0,0,0) + XTREG(117,516, 4, 4, 4,0x02ed,0x0007,-2, 2,0x1000,icountlevel, 0,0,0,0,0,0) + XTREG(118,520,32, 4, 4,0x02ee,0x0007,-2, 2,0x1000,excvaddr, 0,0,0,0,0,0) + XTREG(119,524,32, 4, 4,0x02f0,0x000f,-2, 2,0x1000,ccompare0, 0,0,0,0,0,0) + XTREG(120,528,32, 4, 4,0x02f1,0x000f,-2, 2,0x1000,ccompare1, 0,0,0,0,0,0) + XTREG(121,532,32, 4, 4,0x02f2,0x000f,-2, 2,0x1000,ccompare2, 0,0,0,0,0,0) + XTREG(122,536,32, 4, 4,0x02f4,0x0007,-2, 2,0x1000,misc0, 0,0,0,0,0,0) + XTREG(123,540,32, 4, 4,0x02f5,0x0007,-2, 2,0x1000,misc1, 0,0,0,0,0,0) + XTREG(124,544,32, 4, 4,0x0000,0x0006,-2, 8,0x0100,a0, 0,0,0,0,0,0) + XTREG(125,548,32, 4, 4,0x0001,0x0006,-2, 8,0x0100,a1, 0,0,0,0,0,0) + XTREG(126,552,32, 4, 4,0x0002,0x0006,-2, 8,0x0100,a2, 0,0,0,0,0,0) + XTREG(127,556,32, 4, 4,0x0003,0x0006,-2, 8,0x0100,a3, 0,0,0,0,0,0) + XTREG(128,560,32, 4, 4,0x0004,0x0006,-2, 8,0x0100,a4, 0,0,0,0,0,0) + XTREG(129,564,32, 4, 4,0x0005,0x0006,-2, 8,0x0100,a5, 0,0,0,0,0,0) + XTREG(130,568,32, 4, 4,0x0006,0x0006,-2, 8,0x0100,a6, 0,0,0,0,0,0) + XTREG(131,572,32, 4, 4,0x0007,0x0006,-2, 8,0x0100,a7, 0,0,0,0,0,0) + XTREG(132,576,32, 4, 4,0x0008,0x0006,-2, 8,0x0100,a8, 0,0,0,0,0,0) + XTREG(133,580,32, 4, 4,0x0009,0x0006,-2, 8,0x0100,a9, 0,0,0,0,0,0) + XTREG(134,584,32, 4, 4,0x000a,0x0006,-2, 8,0x0100,a10, 0,0,0,0,0,0) + XTREG(135,588,32, 4, 4,0x000b,0x0006,-2, 8,0x0100,a11, 0,0,0,0,0,0) + XTREG(136,592,32, 4, 4,0x000c,0x0006,-2, 8,0x0100,a12, 0,0,0,0,0,0) + XTREG(137,596,32, 4, 4,0x000d,0x0006,-2, 8,0x0100,a13, 0,0,0,0,0,0) + XTREG(138,600,32, 4, 4,0x000e,0x0006,-2, 8,0x0100,a14, 0,0,0,0,0,0) + XTREG(139,604,32, 4, 4,0x000f,0x0006,-2, 8,0x0100,a15, 0,0,0,0,0,0) + XTREG(140,608, 1, 1, 1,0x0010,0x0006,-2, 6,0x1010,b0, + 0,0,&xtensa_mask0,0,0,0) + XTREG(141,609, 1, 1, 1,0x0011,0x0006,-2, 6,0x1010,b1, + 0,0,&xtensa_mask1,0,0,0) + XTREG(142,610, 1, 1, 1,0x0012,0x0006,-2, 6,0x1010,b2, + 0,0,&xtensa_mask2,0,0,0) + XTREG(143,611, 1, 1, 1,0x0013,0x0006,-2, 6,0x1010,b3, + 0,0,&xtensa_mask3,0,0,0) + XTREG(144,612, 1, 1, 1,0x0014,0x0006,-2, 6,0x1010,b4, + 0,0,&xtensa_mask4,0,0,0) + XTREG(145,613, 1, 1, 1,0x0015,0x0006,-2, 6,0x1010,b5, + 0,0,&xtensa_mask5,0,0,0) + XTREG(146,614, 1, 1, 1,0x0016,0x0006,-2, 6,0x1010,b6, + 0,0,&xtensa_mask6,0,0,0) + XTREG(147,615, 1, 1, 1,0x0017,0x0006,-2, 6,0x1010,b7, + 0,0,&xtensa_mask7,0,0,0) + XTREG(148,616, 1, 1, 1,0x0018,0x0006,-2, 6,0x1010,b8, + 0,0,&xtensa_mask8,0,0,0) + XTREG(149,617, 1, 1, 1,0x0019,0x0006,-2, 6,0x1010,b9, + 0,0,&xtensa_mask9,0,0,0) + XTREG(150,618, 1, 1, 1,0x001a,0x0006,-2, 6,0x1010,b10, + 0,0,&xtensa_mask10,0,0,0) + XTREG(151,619, 1, 1, 1,0x001b,0x0006,-2, 6,0x1010,b11, + 0,0,&xtensa_mask11,0,0,0) + XTREG(152,620, 1, 1, 1,0x001c,0x0006,-2, 6,0x1010,b12, + 0,0,&xtensa_mask12,0,0,0) + XTREG(153,621, 1, 1, 1,0x001d,0x0006,-2, 6,0x1010,b13, + 0,0,&xtensa_mask13,0,0,0) + XTREG(154,622, 1, 1, 1,0x001e,0x0006,-2, 6,0x1010,b14, + 0,0,&xtensa_mask14,0,0,0) + XTREG(155,623, 1, 1, 1,0x001f,0x0006,-2, 6,0x1010,b15, + 0,0,&xtensa_mask15,0,0,0) + XTREG(156,624, 4, 4, 4,0x2008,0x0006,-2, 6,0x1010,psintlevel, + 0,0,&xtensa_mask16,0,0,0) + XTREG(157,628, 1, 4, 4,0x2009,0x0006,-2, 6,0x1010,psum, + 0,0,&xtensa_mask17,0,0,0) + XTREG(158,632, 1, 4, 4,0x200a,0x0006,-2, 6,0x1010,pswoe, + 0,0,&xtensa_mask18,0,0,0) + XTREG(159,636, 2, 4, 4,0x200b,0x0006,-2, 6,0x1010,psring, + 0,0,&xtensa_mask19,0,0,0) + XTREG(160,640, 1, 4, 4,0x200c,0x0006,-2, 6,0x1010,psexcm, + 0,0,&xtensa_mask20,0,0,0) + XTREG(161,644, 2, 4, 4,0x200d,0x0006,-2, 6,0x1010,pscallinc, + 0,0,&xtensa_mask21,0,0,0) + XTREG(162,648, 4, 4, 4,0x200e,0x0006,-2, 6,0x1010,psowb, + 0,0,&xtensa_mask22,0,0,0) + XTREG(163,652,40, 8, 4,0x200f,0x0006,-2, 6,0x1010,acc, + 0,0,&xtensa_mask23,0,0,0) + XTREG(164,660, 4, 4, 4,0x2014,0x0006,-2, 6,0x1010,dbnum, + 0,0,&xtensa_mask24,0,0,0) + XTREG(165,664, 8, 4, 4,0x2016,0x0006,-2, 6,0x1010,asid3, + 0,0,&xtensa_mask25,0,0,0) + XTREG(166,668, 8, 4, 4,0x2017,0x0006,-2, 6,0x1010,asid2, + 0,0,&xtensa_mask26,0,0,0) + XTREG(167,672, 8, 4, 4,0x2018,0x0006,-2, 6,0x1010,asid1, + 0,0,&xtensa_mask27,0,0,0) + XTREG(168,676, 1, 4, 4,0x2019,0x0006,-2, 6,0x1010,instpgszid6, + 0,0,&xtensa_mask28,0,0,0) + XTREG(169,680, 1, 4, 4,0x201a,0x0006,-2, 6,0x1010,instpgszid5, + 0,0,&xtensa_mask29,0,0,0) + XTREG(170,684, 2, 4, 4,0x201b,0x0006,-2, 6,0x1010,instpgszid4, + 0,0,&xtensa_mask30,0,0,0) + XTREG(171,688, 1, 4, 4,0x201c,0x0006,-2, 6,0x1010,datapgszid6, + 0,0,&xtensa_mask31,0,0,0) + XTREG(172,692, 1, 4, 4,0x201d,0x0006,-2, 6,0x1010,datapgszid5, + 0,0,&xtensa_mask32,0,0,0) + XTREG(173,696, 2, 4, 4,0x201e,0x0006,-2, 6,0x1010,datapgszid4, + 0,0,&xtensa_mask33,0,0,0) + XTREG(174,700,10, 4, 4,0x201f,0x0006,-2, 6,0x1010,ptbase, + 0,0,&xtensa_mask34,0,0,0) + XTREG(175,704, 1, 4, 4,0x2021,0x0006, 1, 5,0x1010,ae_overflow, + 0,0,&xtensa_mask35,0,0,0) + XTREG(176,708, 6, 4, 4,0x2022,0x0006, 1, 5,0x1010,ae_sar, + 0,0,&xtensa_mask36,0,0,0) + XTREG(177,712, 4, 4, 4,0x2023,0x0006, 1, 5,0x1010,ae_bitptr, + 0,0,&xtensa_mask37,0,0,0) + XTREG(178,716, 4, 4, 4,0x2024,0x0006, 1, 5,0x1010,ae_bitsused, + 0,0,&xtensa_mask38,0,0,0) + XTREG(179,720, 4, 4, 4,0x2025,0x0006, 1, 5,0x1010,ae_tablesize, + 0,0,&xtensa_mask39,0,0,0) + XTREG(180,724, 4, 4, 4,0x2026,0x0006, 1, 5,0x1010,ae_first_ts, + 0,0,&xtensa_mask40,0,0,0) + XTREG(181,728,27, 4, 4,0x2027,0x0006, 1, 5,0x1010,ae_nextoffset, + 0,0,&xtensa_mask41,0,0,0) + XTREG_END diff --git a/target/xtensa/core-test_kc705_be/xtensa-modules.inc.c b/target/xtensa/core-test_kc705_be/xtensa-modules.inc.c new file mode 100644 index 0000000000..bc7cf44828 --- /dev/null +++ b/target/xtensa/core-test_kc705_be/xtensa-modules.inc.c @@ -0,0 +1,45117 @@ +/* Xtensa configuration-specific ISA information. + + Copyright (c) 2003-2015 Tensilica 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 "xtensa-isa.h" +#include "xtensa-isa-internal.h" + + +/* Sysregs. */ + +static xtensa_sysreg_internal sysregs[] = { + { "LBEG", 0, 0 }, + { "LEND", 1, 0 }, + { "LCOUNT", 2, 0 }, + { "BR", 4, 0 }, + { "ACCLO", 16, 0 }, + { "ACCHI", 17, 0 }, + { "M0", 32, 0 }, + { "M1", 33, 0 }, + { "M2", 34, 0 }, + { "M3", 35, 0 }, + { "PTEVADDR", 83, 0 }, + { "MMID", 89, 0 }, + { "DDR", 104, 0 }, + { "CONFIGID0", 176, 0 }, + { "CONFIGID1", 208, 0 }, + { "INTERRUPT", 226, 0 }, + { "INTCLEAR", 227, 0 }, + { "CCOUNT", 234, 0 }, + { "PRID", 235, 0 }, + { "ICOUNT", 236, 0 }, + { "CCOMPARE0", 240, 0 }, + { "CCOMPARE1", 241, 0 }, + { "CCOMPARE2", 242, 0 }, + { "VECBASE", 231, 0 }, + { "EPC1", 177, 0 }, + { "EPC2", 178, 0 }, + { "EPC3", 179, 0 }, + { "EPC4", 180, 0 }, + { "EPC5", 181, 0 }, + { "EPC6", 182, 0 }, + { "EPC7", 183, 0 }, + { "EXCSAVE1", 209, 0 }, + { "EXCSAVE2", 210, 0 }, + { "EXCSAVE3", 211, 0 }, + { "EXCSAVE4", 212, 0 }, + { "EXCSAVE5", 213, 0 }, + { "EXCSAVE6", 214, 0 }, + { "EXCSAVE7", 215, 0 }, + { "EPS2", 194, 0 }, + { "EPS3", 195, 0 }, + { "EPS4", 196, 0 }, + { "EPS5", 197, 0 }, + { "EPS6", 198, 0 }, + { "EPS7", 199, 0 }, + { "EXCCAUSE", 232, 0 }, + { "DEPC", 192, 0 }, + { "EXCVADDR", 238, 0 }, + { "WINDOWBASE", 72, 0 }, + { "WINDOWSTART", 73, 0 }, + { "SAR", 3, 0 }, + { "PS", 230, 0 }, + { "MISC0", 244, 0 }, + { "MISC1", 245, 0 }, + { "INTENABLE", 228, 0 }, + { "DBREAKA0", 144, 0 }, + { "DBREAKC0", 160, 0 }, + { "DBREAKA1", 145, 0 }, + { "DBREAKC1", 161, 0 }, + { "IBREAKA0", 128, 0 }, + { "IBREAKA1", 129, 0 }, + { "IBREAKENABLE", 96, 0 }, + { "ICOUNTLEVEL", 237, 0 }, + { "DEBUGCAUSE", 233, 0 }, + { "PREFCTL", 40, 0 }, + { "RASID", 90, 0 }, + { "ITLBCFG", 91, 0 }, + { "DTLBCFG", 92, 0 }, + { "CPENABLE", 224, 0 }, + { "SCOMPARE1", 12, 0 }, + { "ATOMCTL", 99, 0 }, + { "THREADPTR", 231, 1 }, + { "AE_OVF_SAR", 240, 1 }, + { "AE_BITHEAD", 241, 1 }, + { "AE_TS_FTS_BU_BP", 242, 1 }, + { "AE_SD_NO", 243, 1 }, + { "AE_CBEGIN0", 246, 1 }, + { "AE_CEND0", 247, 1 }, + { "EXPSTATE", 230, 1 } +}; + +#define NUM_SYSREGS 78 +#define MAX_SPECIAL_REG 245 +#define MAX_USER_REG 247 + + +/* Processor states. */ + +static xtensa_state_internal states[] = { + { "LCOUNT", 32, 0 }, + { "PC", 32, 0 }, + { "ICOUNT", 32, 0 }, + { "DDR", 32, 0 }, + { "INTERRUPT", 22, 0 }, + { "CCOUNT", 32, 0 }, + { "XTSYNC", 1, 0 }, + { "VECBASE", 22, 0 }, + { "EPC1", 32, 0 }, + { "EPC2", 32, 0 }, + { "EPC3", 32, 0 }, + { "EPC4", 32, 0 }, + { "EPC5", 32, 0 }, + { "EPC6", 32, 0 }, + { "EPC7", 32, 0 }, + { "EXCSAVE1", 32, 0 }, + { "EXCSAVE2", 32, 0 }, + { "EXCSAVE3", 32, 0 }, + { "EXCSAVE4", 32, 0 }, + { "EXCSAVE5", 32, 0 }, + { "EXCSAVE6", 32, 0 }, + { "EXCSAVE7", 32, 0 }, + { "EPS2", 15, 0 }, + { "EPS3", 15, 0 }, + { "EPS4", 15, 0 }, + { "EPS5", 15, 0 }, + { "EPS6", 15, 0 }, + { "EPS7", 15, 0 }, + { "EXCCAUSE", 6, 0 }, + { "PSINTLEVEL", 4, 0 }, + { "PSUM", 1, 0 }, + { "PSWOE", 1, 0 }, + { "PSRING", 2, 0 }, + { "PSEXCM", 1, 0 }, + { "DEPC", 32, 0 }, + { "EXCVADDR", 32, 0 }, + { "WindowBase", 3, 0 }, + { "WindowStart", 8, 0 }, + { "PSCALLINC", 2, 0 }, + { "PSOWB", 4, 0 }, + { "LBEG", 32, 0 }, + { "LEND", 32, 0 }, + { "SAR", 6, 0 }, + { "THREADPTR", 32, 0 }, + { "MISC0", 32, 0 }, + { "MISC1", 32, 0 }, + { "ACC", 40, 0 }, + { "InOCDMode", 1, 0 }, + { "INTENABLE", 22, 0 }, + { "DBREAKA0", 32, 0 }, + { "DBREAKC0", 8, 0 }, + { "DBREAKA1", 32, 0 }, + { "DBREAKC1", 8, 0 }, + { "IBREAKA0", 32, 0 }, + { "IBREAKA1", 32, 0 }, + { "IBREAKENABLE", 2, 0 }, + { "ICOUNTLEVEL", 4, 0 }, + { "DEBUGCAUSE", 6, 0 }, + { "DBNUM", 4, 0 }, + { "CCOMPARE0", 32, 0 }, + { "CCOMPARE1", 32, 0 }, + { "CCOMPARE2", 32, 0 }, + { "PREFCTL", 9, 0 }, + { "ASID3", 8, 0 }, + { "ASID2", 8, 0 }, + { "ASID1", 8, 0 }, + { "INSTPGSZID6", 1, 0 }, + { "INSTPGSZID5", 1, 0 }, + { "INSTPGSZID4", 2, 0 }, + { "DATAPGSZID6", 1, 0 }, + { "DATAPGSZID5", 1, 0 }, + { "DATAPGSZID4", 2, 0 }, + { "PTBASE", 10, 0 }, + { "CPENABLE", 8, 0 }, + { "SCOMPARE1", 32, 0 }, + { "ATOMCTL", 6, 0 }, + { "ERI_RAW_INTERLOCK", 1, 0 }, + { "AE_OVERFLOW", 1, XTENSA_STATE_IS_SHARED_OR }, + { "AE_SAR", 6, 0 }, + { "AE_BITHEAD", 32, 0 }, + { "AE_BITPTR", 4, 0 }, + { "AE_BITSUSED", 4, 0 }, + { "AE_TABLESIZE", 4, 0 }, + { "AE_FIRST_TS", 4, 0 }, + { "AE_NEXTOFFSET", 27, 0 }, + { "AE_SEARCHDONE", 1, 0 }, + { "AE_CBEGIN0", 32, 0 }, + { "AE_CEND0", 32, 0 }, + { "EXPSTATE", 32, XTENSA_STATE_IS_EXPORTED } +}; + +#define NUM_STATES 89 + +enum xtensa_state_id { + STATE_LCOUNT, + STATE_PC, + STATE_ICOUNT, + STATE_DDR, + STATE_INTERRUPT, + STATE_CCOUNT, + STATE_XTSYNC, + STATE_VECBASE, + STATE_EPC1, + STATE_EPC2, + STATE_EPC3, + STATE_EPC4, + STATE_EPC5, + STATE_EPC6, + STATE_EPC7, + STATE_EXCSAVE1, + STATE_EXCSAVE2, + STATE_EXCSAVE3, + STATE_EXCSAVE4, + STATE_EXCSAVE5, + STATE_EXCSAVE6, + STATE_EXCSAVE7, + STATE_EPS2, + STATE_EPS3, + STATE_EPS4, + STATE_EPS5, + STATE_EPS6, + STATE_EPS7, + STATE_EXCCAUSE, + STATE_PSINTLEVEL, + STATE_PSUM, + STATE_PSWOE, + STATE_PSRING, + STATE_PSEXCM, + STATE_DEPC, + STATE_EXCVADDR, + STATE_WindowBase, + STATE_WindowStart, + STATE_PSCALLINC, + STATE_PSOWB, + STATE_LBEG, + STATE_LEND, + STATE_SAR, + STATE_THREADPTR, + STATE_MISC0, + STATE_MISC1, + STATE_ACC, + STATE_InOCDMode, + STATE_INTENABLE, + STATE_DBREAKA0, + STATE_DBREAKC0, + STATE_DBREAKA1, + STATE_DBREAKC1, + STATE_IBREAKA0, + STATE_IBREAKA1, + STATE_IBREAKENABLE, + STATE_ICOUNTLEVEL, + STATE_DEBUGCAUSE, + STATE_DBNUM, + STATE_CCOMPARE0, + STATE_CCOMPARE1, + STATE_CCOMPARE2, + STATE_PREFCTL, + STATE_ASID3, + STATE_ASID2, + STATE_ASID1, + STATE_INSTPGSZID6, + STATE_INSTPGSZID5, + STATE_INSTPGSZID4, + STATE_DATAPGSZID6, + STATE_DATAPGSZID5, + STATE_DATAPGSZID4, + STATE_PTBASE, + STATE_CPENABLE, + STATE_SCOMPARE1, + STATE_ATOMCTL, + STATE_ERI_RAW_INTERLOCK, + STATE_AE_OVERFLOW, + STATE_AE_SAR, + STATE_AE_BITHEAD, + STATE_AE_BITPTR, + STATE_AE_BITSUSED, + STATE_AE_TABLESIZE, + STATE_AE_FIRST_TS, + STATE_AE_NEXTOFFSET, + STATE_AE_SEARCHDONE, + STATE_AE_CBEGIN0, + STATE_AE_CEND0, + STATE_EXPSTATE +}; + + +/* Field definitions. */ + +static unsigned +Field_t_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_t_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_s_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_s_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_r_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_r_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_op2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_op2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_op1_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_op1_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_op0_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 8) >> 28); + return tie_t; +} + +static void +Field_op0_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00000) | (tie_t << 20); +} + +static unsigned +Field_n_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + return tie_t; +} + +static void +Field_n_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_m_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + return tie_t; +} + +static void +Field_m_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); +} + +static unsigned +Field_sr_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sr_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_st_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_st_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_thi3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 12) >> 29); + return tie_t; +} + +static void +Field_thi3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0000) | (tie_t << 17); +} + +static unsigned +Field_t3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_t3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_tlo_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + return tie_t; +} + +static void +Field_tlo_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); +} + +static unsigned +Field_w_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + return tie_t; +} + +static void +Field_w_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_r3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_r3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_rhi_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_rhi_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_ae_r3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_ae_r3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_ae_r10_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + return tie_t; +} + +static void +Field_ae_r10_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_ae_r32_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_ae_r32_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_ae_s3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_ae_s3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_ae_s_non_samt_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_ae_s_non_samt_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_s3to1_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 16) >> 29); + return tie_t; +} + +static void +Field_s3to1_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe000) | (tie_t << 13); +} + +static unsigned +Field_op0_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_op0_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_t_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_t_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_r_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_r_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_op0_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_op0_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_z_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + return tie_t; +} + +static void +Field_z_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); +} + +static unsigned +Field_i_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_i_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_s_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_s_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_ftsf42ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf42ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_op0_s3_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_op0_s3_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); +} + +static unsigned +Field_ftsf333ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 9) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf333ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x700000) | (tie_t << 20); +} + +static unsigned +Field_ftsf43ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf43ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_ftsf359ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf359ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); +} + +static unsigned +Field_ftsf45ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf45ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf32ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf32ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf33ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf33ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf31ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf31ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf30ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf30ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf60ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf60ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_ftsf355ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 10) >> 30); + return tie_t; +} + +static void +Field_ftsf355ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x300000) | (tie_t << 20); +} + +static unsigned +Field_ftsf58ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf58ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf354ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 11) >> 31); + return tie_t; +} + +static void +Field_ftsf354ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100000) | (tie_t << 20); +} + +static unsigned +Field_ftsf37ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf37ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_ftsf22ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf22ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_ftsf126ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 13) >> 30); + return tie_t; +} + +static void +Field_ftsf126ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x60000) | (tie_t << 17); +} + +static unsigned +Field_ftsf357ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + tie_t = (tie_t << 5) | ((insn[0] << 18) >> 27); + return tie_t; +} + +static void +Field_ftsf357ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x3e00) | (tie_t << 9); + tie_t = (val << 26) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf53ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf53ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf66ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf66ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 27) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf347ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ftsf347ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf64ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 18) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf64ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0x3c00) | (tie_t << 10); +} + +static unsigned +Field_ftsf345ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 22) >> 31); + return tie_t; +} + +static void +Field_ftsf345ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x200) | (tie_t << 9); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf63ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 18) >> 25); + return tie_t; +} + +static void +Field_ftsf63ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x3f80) | (tie_t << 7); +} + +static unsigned +Field_ftsf344ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_ftsf344ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf48ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf48ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf47ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf47ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf92ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf92ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 26) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); + tie_t = (val << 22) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf358ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 13) >> 28); + return tie_t; +} + +static void +Field_ftsf358ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x78000) | (tie_t << 15); +} + +static unsigned +Field_ftsf94ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf94ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 20) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf93ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf93ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ae_r10_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_ae_r10_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf90ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf90ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 26) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); + tie_t = (val << 22) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf55_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_ftsf55_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_ftsf121ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 9) >> 24); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf121ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 21) >> 24; + insn[0] = (insn[0] & ~0x7f8000) | (tie_t << 15); +} + +static unsigned +Field_ftsf91_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + return tie_t; +} + +static void +Field_ftsf91_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); +} + +static unsigned +Field_ftsf99ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 9) >> 27); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf99ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 22) >> 27; + insn[0] = (insn[0] & ~0x7c0000) | (tie_t << 18); +} + +static unsigned +Field_ftsf351_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + return tie_t; +} + +static void +Field_ftsf351_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); +} + +static unsigned +Field_ftsf97ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 6) | ((insn[0] << 9) >> 26); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf97ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 21) >> 26; + insn[0] = (insn[0] & ~0x7e0000) | (tie_t << 17); +} + +static unsigned +Field_ftsf348ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + return tie_t; +} + +static void +Field_ftsf348ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_ftsf96ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf96ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 20) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf23ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf23ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf34ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf34ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf40ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf40ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf38ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf38ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf24ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf24ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf26ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf26ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf20ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf20ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ftsf21ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf21ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ftsf25ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf25ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf35ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf35ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf41ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf41ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf54ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf54ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_ftsf356ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + return tie_t; +} + +static void +Field_ftsf356ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf29ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf29ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf27ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf27ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf28ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf28ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf36ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf36ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf57ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 9) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf57ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x700000) | (tie_t << 20); +} + +static unsigned +Field_ftsf62ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf62ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ae_s20_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 9) >> 29); + return tie_t; +} + +static void +Field_ae_s20_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700000) | (tie_t << 20); +} + +static unsigned +Field_ftsf56ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 9) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf56ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x700000) | (tie_t << 20); +} + +static unsigned +Field_ftsf127ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 13) >> 31); + return tie_t; +} + +static void +Field_ftsf127ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40000) | (tie_t << 18); +} + +static unsigned +Field_ftsf350ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf350ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf114ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf114ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf124ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf124ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf352ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_ftsf352ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf118ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 9) >> 23); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf118ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 20) >> 23; + insn[0] = (insn[0] & ~0x7fc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf111ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf111ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf113ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf113ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf106ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf106ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); +} + +static unsigned +Field_ftsf107ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf107ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); +} + +static unsigned +Field_ftsf109ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf109ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf115ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf115ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf120ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 9) >> 23); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf120ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 20) >> 23; + insn[0] = (insn[0] & ~0x7fc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf123ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 9) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf123ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); + tie_t = (val << 23) >> 29; + insn[0] = (insn[0] & ~0x700000) | (tie_t << 20); +} + +static unsigned +Field_ftsf349ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_ftsf349ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 29) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf110ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf110ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf117ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf117ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf112ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf112ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf116ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf116ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf68ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf68ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); +} + +static unsigned +Field_ftsf50ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf50ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf52ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf52ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf51ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf51ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf49ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + return tie_t; +} + +static void +Field_ftsf49ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ae_r20_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + return tie_t; +} + +static void +Field_ae_r20_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); +} + +static unsigned +Field_ftsf343ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 18) >> 27); + return tie_t; +} + +static void +Field_ftsf343ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x3e00) | (tie_t << 9); +} + +static unsigned +Field_ftsf125ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_ftsf125ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); + tie_t = (val << 28) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); +} + +static unsigned +Field_ftsf342ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ftsf342ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ftsf108ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf108ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); +} + +static unsigned +Field_ae_r32_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_ae_r32_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ae_mul32x24fld_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ae_mul32x24fld_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf161ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf161ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf155ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf155ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf176ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf176ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf159ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf159ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf156ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf156ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf168ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf168ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf158ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf158ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf154ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf154ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf164ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf164ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf157ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf157ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf153ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf153ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf162ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf162ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf134ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf134ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf192ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf192ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf143ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf143ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf133ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf133ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf160ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf160ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf142ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf142ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf131ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf131ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf144ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf144ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf141ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf141ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf61_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + return tie_t; +} + +static void +Field_ftsf61_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_ftsf334ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 10) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf334ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x380000) | (tie_t << 19); +} + +static unsigned +Field_ftsf136ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf136ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf139ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf139ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf177ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf177ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf171ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf171ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf185ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf185ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf175ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf175ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf172ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf172ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf183ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf183ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf174ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf174ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf170ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf170ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf182ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf182ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf173ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf173ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf169ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf169ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf181ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf181ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf140ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf140ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf152ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf152ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf138ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf138ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf148ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf148ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf137ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf137ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf146ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf146ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf135ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf135ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf145ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf145ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf179ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf179ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf189ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf189ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf184ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf184ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf187ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf187ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf180ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf180ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf188ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf188ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf178ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf178ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf186ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf186ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf76ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf76ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf75ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf75ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf79ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf79ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf78ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf78ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf80ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf80ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf81ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf81ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf83ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf83ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf82ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf82ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf69ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf69ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf103ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_ftsf103ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_ftsf337ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf337ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 25) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf71ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf71ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf70ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf70ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf77ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf77ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf73ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf73ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf74ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf74ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf72ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf72ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf85ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf85ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf84ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf84ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf86ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf86ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf102ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_ftsf102ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_ftsf336ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf336ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf89ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf89ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf87ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf87ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf88ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf88ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 23) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf101ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + tie_t = (tie_t << 5) | ((insn[0] << 20) >> 27); + return tie_t; +} + +static void +Field_ftsf101ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80) | (tie_t << 7); + tie_t = (val << 26) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_ftsf335_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 10) >> 29); + return tie_t; +} + +static void +Field_ftsf335_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x380000) | (tie_t << 19); +} + +static unsigned +Field_t_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + return tie_t; +} + +static void +Field_t_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf196ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf196ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf208ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf208ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf341ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_ftsf341ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf200ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf200ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf340ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf340ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf195ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf195ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf198ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf198ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf197ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf197ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf199ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf199ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf201ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf201ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf204ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf204ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf202ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf202ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf203ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf203ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf205ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf205ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf207ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf207ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf206ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf206ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf210ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf210ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf339ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_ftsf339ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf128ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf128ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf130ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf130ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf129ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf129ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf132ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf132ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf147ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf147ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf150ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf150ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf149ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf149ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf151ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf151ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf163ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf163ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf166ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf166ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf165ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf165ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf167ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf167ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf190ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf190ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf193ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf193ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf191ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf191ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_ftsf194ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf194ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld132ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld132ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld69_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld69_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_combined2c0b5f72_fld68_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld68_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_combined2c0b5f72_fld19_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld19_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); +} + +static unsigned +Field_combined2c0b5f72_fld22_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld22_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_op0_s3_s3_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_op0_s3_s3_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); +} + +static unsigned +Field_combined2c0b5f72_fld131ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld131ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld74_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 10) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld74_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x200000) | (tie_t << 21); +} + +static unsigned +Field_combined2c0b5f72_fld66_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld66_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld91_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 14) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld91_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x20000) | (tie_t << 17); +} + +static unsigned +Field_combined2c0b5f72_fld90_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld90_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_combined2c0b5f72_fld88_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld88_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_combined2c0b5f72_fld65_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld65_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); +} + +static unsigned +Field_combined2c0b5f72_fld24_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 22) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld24_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x200) | (tie_t << 9); +} + +static unsigned +Field_combined2c0b5f72_fld147ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld147ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_combined2c0b5f72_fld79_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld79_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_r_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 9) >> 28); + return tie_t; +} + +static void +Field_r_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x780000) | (tie_t << 19); +} + +static unsigned +Field_op0_s4_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_op0_s4_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); +} + +static unsigned +Field_imm8_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_imm8_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); +} + +static unsigned +Field_t_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + return tie_t; +} + +static void +Field_t_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf280_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 10) >> 29); + return tie_t; +} + +static void +Field_ftsf280_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x380000) | (tie_t << 19); +} + +static unsigned +Field_ftsf288_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 5) >> 31); + return tie_t; +} + +static void +Field_ftsf288_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000000) | (tie_t << 26); +} + +static unsigned +Field_ftsf360ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 6) >> 29); + return tie_t; +} + +static void +Field_ftsf360ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x3800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf213ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + return tie_t; +} + +static void +Field_ftsf213ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); +} + +static unsigned +Field_ftsf212ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 22) >> 29); + return tie_t; +} + +static void +Field_ftsf212ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x380) | (tie_t << 7); +} + +static unsigned +Field_ftsf211ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf211ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); +} + +static unsigned +Field_ftsf279ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 3) | ((insn[0] << 10) >> 29); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf279ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 21) >> 29; + insn[0] = (insn[0] & ~0x380000) | (tie_t << 19); + tie_t = (val << 17) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_s8_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_ftsf313ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 13) >> 20); + return tie_t; +} + +static void +Field_ftsf313ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0x7ff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf282ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 3) | ((insn[0] << 10) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf282ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x380000) | (tie_t << 19); + tie_t = (val << 18) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf361ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 9) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 13) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf361ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x78000) | (tie_t << 15); + tie_t = (val << 26) >> 31; + insn[0] = (insn[0] & ~0x400000) | (tie_t << 22); +} + +static unsigned +Field_ftsf281ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 3) | ((insn[0] << 10) >> 29); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf281ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 21) >> 29; + insn[0] = (insn[0] & ~0x380000) | (tie_t << 19); + tie_t = (val << 17) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf287ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 5) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf287ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 23) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); + tie_t = (val << 22) >> 31; + insn[0] = (insn[0] & ~0x4000000) | (tie_t << 26); +} + +static unsigned +Field_ftsf368ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 6) >> 27); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf368ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 26) >> 27; + insn[0] = (insn[0] & ~0x3e00000) | (tie_t << 21); +} + +static unsigned +Field_ftsf285ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 5) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf285ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 23) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); + tie_t = (val << 21) >> 30; + insn[0] = (insn[0] & ~0x6000000) | (tie_t << 25); +} + +static unsigned +Field_ftsf366ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 7) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf366ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x1e00000) | (tie_t << 21); +} + +static unsigned +Field_ftsf284ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 5) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf284ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 23) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); + tie_t = (val << 20) >> 29; + insn[0] = (insn[0] & ~0x7000000) | (tie_t << 24); +} + +static unsigned +Field_ftsf364ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 8) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf364ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 28) >> 29; + insn[0] = (insn[0] & ~0xe00000) | (tie_t << 21); +} + +static unsigned +Field_ftsf297ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 14) | ((insn[0] << 11) >> 18); + return tie_t; +} + +static void +Field_ftsf297ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 18) >> 18; + insn[0] = (insn[0] & ~0x1fff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf309_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 10) >> 31); + return tie_t; +} + +static void +Field_ftsf309_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x200000) | (tie_t << 21); +} + +static unsigned +Field_ftsf327ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 5) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf327ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); + tie_t = (val << 21) >> 30; + insn[0] = (insn[0] & ~0x6000000) | (tie_t << 25); +} + +static unsigned +Field_ftsf363ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 7) >> 31); + return tie_t; +} + +static void +Field_ftsf363ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1000000) | (tie_t << 24); +} + +static unsigned +Field_ftsf214ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 18) >> 25); + return tie_t; +} + +static void +Field_ftsf214ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x3f80) | (tie_t << 7); +} + +static unsigned +Field_ftsf298ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf298ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf373ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 11) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_ftsf373ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x100000) | (tie_t << 20); +} + +static unsigned +Field_ftsf302ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf302ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_ftsf376ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 10) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 14) >> 29); + return tie_t; +} + +static void +Field_ftsf376ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x38000) | (tie_t << 15); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x300000) | (tie_t << 20); +} + +static unsigned +Field_ftsf300ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 12) >> 29); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf300ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 21) >> 29; + insn[0] = (insn[0] & ~0xe0000) | (tie_t << 17); +} + +static unsigned +Field_ftsf370ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 10) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 15) >> 30); + return tie_t; +} + +static void +Field_ftsf370ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x18000) | (tie_t << 15); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x300000) | (tie_t << 20); +} + +static unsigned +Field_ae_s20_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 5) >> 29); + return tie_t; +} + +static void +Field_ae_s20_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7000000) | (tie_t << 24); +} + +static unsigned +Field_ftsf378ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 8) >> 27); + return tie_t; +} + +static void +Field_ftsf378ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf217ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf217ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf219ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf219ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf220ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf220ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf221ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf221ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf227ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf227ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf228ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf228ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf229ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf229ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf231ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf231ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf232ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf232ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf234ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf234ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf238ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf238ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf233ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf233ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf223ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf223ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf224ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf224ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf226ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf226ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf225ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf225ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf240ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf240ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf242ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf242ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf241ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf241ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf243ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf243ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf235ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf235ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf236ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf236ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf237ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf237ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf239ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf239ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf260ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf260ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf295ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf295ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf247ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf247ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf249ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf249ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf268ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf268ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf263ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf263ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf265ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf265ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf266ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf266ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf259ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf259ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf261ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf261ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf262ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf262ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf264ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf264ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf245ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf245ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf246ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf246ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf248ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf248ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf252ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf252ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf256ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf256ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf255ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf255ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf257ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf257ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf258ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf258ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf250ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf250ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf251ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf251ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf253ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf253ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf254ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf254ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf305ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf305ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf306ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf306ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf307ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf307ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf310ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 10) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf310ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x200000) | (tie_t << 21); +} + +static unsigned +Field_ftsf304ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf304ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf308ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 10) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf308ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x200000) | (tie_t << 21); +} + +static unsigned +Field_ae_r10_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + return tie_t; +} + +static void +Field_ae_r10_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf329ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 17) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_ftsf329ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); + tie_t = (val << 27) >> 29; + insn[0] = (insn[0] & ~0x7000) | (tie_t << 12); +} + +static unsigned +Field_ftsf379ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 22) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf379ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x200) | (tie_t << 9); + tie_t = (val << 29) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_ftsf272ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf272ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf273ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf273ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf274ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf274ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf276ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf276ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf269ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf269ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf271ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf271ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf275ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf275ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf270ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf270ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ftsf296ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 13) | ((insn[0] << 12) >> 19); + return tie_t; +} + +static void +Field_ftsf296ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 19) >> 19; + insn[0] = (insn[0] & ~0xfff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf315_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + return tie_t; +} + +static void +Field_ftsf315_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_combined2c0b5f72_fld52_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 14) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld52_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x20000) | (tie_t << 17); +} + +static unsigned +Field_combined1e9fefee_fld96_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + return tie_t; +} + +static void +Field_combined1e9fefee_fld96_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); +} + +static unsigned +Field_combined1e9fefee_fld98_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_combined1e9fefee_fld98_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld49_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld39_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); +} + +static unsigned +Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 22) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld50_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x200) | (tie_t << 9); +} + +static unsigned +Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld40_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); +} + +static unsigned +Field_ftsf362_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf362_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_op0_s4_s4_s4_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_op0_s4_s4_s4_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); +} + +static unsigned +Field_combined1e9fefee_fld109ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 15) >> 30); + return tie_t; +} + +static void +Field_combined1e9fefee_fld109ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x18000) | (tie_t << 15); +} + +static unsigned +Field_combined1e9fefee_fld108ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + return tie_t; +} + +static void +Field_combined1e9fefee_fld108ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); +} + +static unsigned +Field_combined2c0b5f72_fld47_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld47_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_combined1e9fefee_fld107ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 14) >> 29); + return tie_t; +} + +static void +Field_combined1e9fefee_fld107ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x38000) | (tie_t << 15); +} + +static unsigned +Field_combined1e9fefee_fld106ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 14) >> 29); + return tie_t; +} + +static void +Field_combined1e9fefee_fld106ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x38000) | (tie_t << 15); +} + +static unsigned +Field_ftsf244ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf244ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf267ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf267ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf290ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf290ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); + tie_t = (val << 19) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf289ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf289ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); + tie_t = (val << 19) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf230ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf230ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf222ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf222ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf218ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf218ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf215ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf215ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_ftsf314ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 12) | ((insn[0] << 13) >> 20); + return tie_t; +} + +static void +Field_ftsf314ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0x7ff80) | (tie_t << 7); + tie_t = (val << 19) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf323ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf323ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf322ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf322ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf311ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 10) >> 31); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf311ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 24) >> 31; + insn[0] = (insn[0] & ~0x200000) | (tie_t << 21); +} + +static unsigned +Field_ftsf386ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 5) >> 27); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf386ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 26) >> 27; + insn[0] = (insn[0] & ~0x7c00000) | (tie_t << 22); +} + +static unsigned +Field_ftsf278ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf278ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); + tie_t = (val << 18) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf292ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 5) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf292ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 24) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); + tie_t = (val << 21) >> 29; + insn[0] = (insn[0] & ~0x7000000) | (tie_t << 24); +} + +static unsigned +Field_ftsf382ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf382ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf291ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf291ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 24) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf294ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 5) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf294ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 24) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x4000000) | (tie_t << 26); +} + +static unsigned +Field_ftsf383ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 6) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf383ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 28) >> 29; + insn[0] = (insn[0] & ~0x3800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf293ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 5) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 7) | ((insn[0] << 17) >> 25); + return tie_t; +} + +static void +Field_ftsf293ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00) | (tie_t << 8); + tie_t = (val << 24) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x6000000) | (tie_t << 25); +} + +static unsigned +Field_ftsf384ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 7) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf384ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x1800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf312ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 10) | ((insn[0] << 15) >> 22); + return tie_t; +} + +static void +Field_ftsf312ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 22) >> 22; + insn[0] = (insn[0] & ~0x1ff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf320ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf320ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_ftsf387ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 13) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_ftsf387ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x60000) | (tie_t << 17); + tie_t = (val << 25) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf319ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 10) | ((insn[0] << 15) >> 22); + return tie_t; +} + +static void +Field_ftsf319ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 22) >> 22; + insn[0] = (insn[0] & ~0x1ff80) | (tie_t << 7); + tie_t = (val << 21) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf388ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 5) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 13) >> 30); + return tie_t; +} + +static void +Field_ftsf388ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x60000) | (tie_t << 17); + tie_t = (val << 27) >> 29; + insn[0] = (insn[0] & ~0x7000000) | (tie_t << 24); +} + +static unsigned +Field_ftsf317ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 13) >> 31); + tie_t = (tie_t << 10) | ((insn[0] << 15) >> 22); + return tie_t; +} + +static void +Field_ftsf317ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 22) >> 22; + insn[0] = (insn[0] & ~0x1ff80) | (tie_t << 7); + tie_t = (val << 21) >> 31; + insn[0] = (insn[0] & ~0x40000) | (tie_t << 18); + tie_t = (val << 20) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf389ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 5) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 14) >> 31); + return tie_t; +} + +static void +Field_ftsf389ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x20000) | (tie_t << 17); + tie_t = (val << 28) >> 29; + insn[0] = (insn[0] & ~0x7000000) | (tie_t << 24); +} + +static unsigned +Field_ftsf324ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf324ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf325ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf325ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf328ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 5) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf328ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x4000000) | (tie_t << 26); +} + +static unsigned +Field_ftsf316ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 12) | ((insn[0] << 13) >> 20); + return tie_t; +} + +static void +Field_ftsf316ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0x7ff80) | (tie_t << 7); + tie_t = (val << 19) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf326ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 5) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf326ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 23) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); + tie_t = (val << 21) >> 30; + insn[0] = (insn[0] & ~0x6000000) | (tie_t << 25); +} + +static unsigned +Field_ftsf277ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_ftsf277ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); + tie_t = (val << 22) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); + tie_t = (val << 18) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_combined2c0b5f72_fld123_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 6) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld123_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x2000000) | (tie_t << 25); +} + +static unsigned +Field_combined2c0b5f72_fld121_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 5) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld121_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000000) | (tie_t << 26); +} + +static unsigned +Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld28_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); +} + +static unsigned +Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld127_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_op0_s4_s4_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_op0_s4_s4_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); +} + +static unsigned +Field_combined2c0b5f72_fld149ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 17) >> 24); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld149ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0x7f80) | (tie_t << 7); +} + +static unsigned +Field_combined2c0b5f72_fld137ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld137ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld144ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld144ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld138ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld138ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld143ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld143ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld134ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld134ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld140ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld140ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld135ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld135ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld142ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld142ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld136ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld136ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld141ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld141ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld133ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld133ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld139ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld139ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld146ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld146ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld46_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 11) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld46_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100000) | (tie_t << 20); +} + +static unsigned +Field_combined2c0b5f72_fld145ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld145ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_t_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_t_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_bbi4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_bbi4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); +} + +static unsigned +Field_bbi_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_bbi_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); +} + +static unsigned +Field_bbi_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 5) >> 27); + return tie_t; +} + +static void +Field_bbi_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x7c00000) | (tie_t << 22); +} + +static unsigned +Field_imm12_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 20) >> 20); + return tie_t; +} + +static void +Field_imm12_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0xfff) | (tie_t << 0); +} + +static unsigned +Field_imm12_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 5) >> 24); + tie_t = (tie_t << 4) | ((insn[0] << 17) >> 28); + return tie_t; +} + +static void +Field_imm12_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x7800) | (tie_t << 11); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0x7f80000) | (tie_t << 19); +} + +static unsigned +Field_imm8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 24) >> 24); + return tie_t; +} + +static void +Field_imm8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff) | (tie_t << 0); +} + +static unsigned +Field_s_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_s_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_s_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 13) >> 28); + return tie_t; +} + +static void +Field_s_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x78000) | (tie_t << 15); +} + +static unsigned +Field_imm12b_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 24) >> 24); + return tie_t; +} + +static void +Field_imm12b_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff) | (tie_t << 0); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm12b_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 9) >> 20); + return tie_t; +} + +static void +Field_imm12b_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0x7ff800) | (tie_t << 11); +} + +static unsigned +Field_imm16_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 16) | ((insn[0] << 16) >> 16); + return tie_t; +} + +static void +Field_imm16_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 16) >> 16; + insn[0] = (insn[0] & ~0xffff) | (tie_t << 0); +} + +static unsigned +Field_imm16_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 16) | ((insn[0] << 9) >> 16); + return tie_t; +} + +static void +Field_imm16_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 16) >> 16; + insn[0] = (insn[0] & ~0x7fff80) | (tie_t << 7); +} + +static unsigned +Field_offset_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 18) | ((insn[0] << 14) >> 14); + return tie_t; +} + +static void +Field_offset_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 14) >> 14; + insn[0] = (insn[0] & ~0x3ffff) | (tie_t << 0); +} + +static unsigned +Field_offset_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 18) | ((insn[0] << 5) >> 14); + return tie_t; +} + +static void +Field_offset_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 14) >> 14; + insn[0] = (insn[0] & ~0x7fffe00) | (tie_t << 9); +} + +static unsigned +Field_op2_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 13) >> 28); + return tie_t; +} + +static void +Field_op2_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x78000) | (tie_t << 15); +} + +static unsigned +Field_r_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_r_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_sa4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_sa4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); +} + +static unsigned +Field_sae4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + return tie_t; +} + +static void +Field_sae4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_sae_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_sae_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_sae_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 17) >> 27); + return tie_t; +} + +static void +Field_sae_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x7c00) | (tie_t << 10); +} + +static unsigned +Field_sal_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_sal_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); +} + +static unsigned +Field_sal_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + return tie_t; +} + +static void +Field_sal_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_sargt_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_sargt_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); +} + +static unsigned +Field_sargt_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 13) >> 27); + return tie_t; +} + +static void +Field_sargt_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x7c000) | (tie_t << 14); +} + +static unsigned +Field_sas4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + return tie_t; +} + +static void +Field_sas4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_sas_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_sas_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_sas_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 5) >> 27); + return tie_t; +} + +static void +Field_sas_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x7c00000) | (tie_t << 22); +} + +static unsigned +Field_sr_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_sr_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_sr_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_sr_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_st_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_st_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_st_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_st_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_imm4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_imm4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_imm4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_imm4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_mn_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 14) >> 30); + return tie_t; +} + +static void +Field_mn_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30000) | (tie_t << 16); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_i_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_i_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_imm6lo_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm6lo_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_imm6lo_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm6lo_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_imm6hi_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + return tie_t; +} + +static void +Field_imm6hi_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_imm6hi_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + return tie_t; +} + +static void +Field_imm6hi_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_imm7lo_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm7lo_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_imm7lo_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm7lo_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_imm7hi_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + return tie_t; +} + +static void +Field_imm7hi_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); +} + +static unsigned +Field_imm7hi_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + return tie_t; +} + +static void +Field_imm7hi_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); +} + +static unsigned +Field_z_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + return tie_t; +} + +static void +Field_z_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); +} + +static unsigned +Field_imm6_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm6_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_imm6_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm6_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_imm7_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm7_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); +} + +static unsigned +Field_imm7_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_imm7_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); +} + +static unsigned +Field_rbit2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + return tie_t; +} + +static void +Field_rbit2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); +} + +static unsigned +Field_tbit2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 13) >> 31); + return tie_t; +} + +static void +Field_tbit2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40000) | (tie_t << 18); +} + +static unsigned +Field_y_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 13) >> 31); + return tie_t; +} + +static void +Field_y_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40000) | (tie_t << 18); +} + +static unsigned +Field_x_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 21) >> 31); + return tie_t; +} + +static void +Field_x_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x400) | (tie_t << 10); +} + +static unsigned +Field_t2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 12) >> 29); + return tie_t; +} + +static void +Field_t2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0000) | (tie_t << 17); +} + +static unsigned +Field_t2_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_t2_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_t2_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_t2_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_t2_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_t2_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); + tie_t = (val << 29) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_s2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 16) >> 29); + return tie_t; +} + +static void +Field_s2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe000) | (tie_t << 13); +} + +static unsigned +Field_s2_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_s2_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_s2_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_s2_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_r2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_r2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_r2_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 28) >> 29); + return tie_t; +} + +static void +Field_r2_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe) | (tie_t << 1); +} + +static unsigned +Field_r2_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 28) >> 29); + return tie_t; +} + +static void +Field_r2_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe) | (tie_t << 1); +} + +static unsigned +Field_t4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + return tie_t; +} + +static void +Field_t4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_t4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_t4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_t4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_t4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_s4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_s4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_s4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_s4_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 9) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600000) | (tie_t << 21); +} + +static unsigned +Field_r4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_r4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_r4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 28) >> 30); + return tie_t; +} + +static void +Field_r4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc) | (tie_t << 2); +} + +static unsigned +Field_r4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 28) >> 30); + return tie_t; +} + +static void +Field_r4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc) | (tie_t << 2); +} + +static unsigned +Field_t8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + return tie_t; +} + +static void +Field_t8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_t8_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_t8_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_t8_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_t8_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_s8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_s8_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_s8_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_r8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_r8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_r8_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_r8_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); +} + +static unsigned +Field_r8_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_r8_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); +} + +static unsigned +Field_xt_wbr15_imm_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 15) | ((insn[0] << 8) >> 17); + return tie_t; +} + +static void +Field_xt_wbr15_imm_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 17) >> 17; + insn[0] = (insn[0] & ~0xfffe00) | (tie_t << 9); +} + +static unsigned +Field_xt_wbr18_imm_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 18) | ((insn[0] << 8) >> 14); + return tie_t; +} + +static void +Field_xt_wbr18_imm_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 14) >> 14; + insn[0] = (insn[0] & ~0xffffc0) | (tie_t << 6); +} + +static unsigned +Field_ae_r32_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 11) >> 30); + return tie_t; +} + +static void +Field_ae_r32_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180000) | (tie_t << 19); +} + +static unsigned +Field_ae_samt_s_t_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 6) | ((insn[0] << 12) >> 26); + return tie_t; +} + +static void +Field_ae_samt_s_t_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 26) >> 26; + insn[0] = (insn[0] & ~0xfc000) | (tie_t << 14); +} + +static unsigned +Field_ae_samt_s_t_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + tie_t = (tie_t << 2) | ((insn[0] << 13) >> 30); + return tie_t; +} + +static void +Field_ae_samt_s_t_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x60000) | (tie_t << 17); + tie_t = (val << 26) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); +} + +static unsigned +Field_ae_r20_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + return tie_t; +} + +static void +Field_ae_r20_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); +} + +static unsigned +Field_ae_r20_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 9) >> 29); + return tie_t; +} + +static void +Field_ae_r20_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700000) | (tie_t << 20); +} + +static unsigned +Field_ae_s20_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 17) >> 29); + return tie_t; +} + +static void +Field_ae_s20_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7000) | (tie_t << 12); +} + +static unsigned +Field_ae_fld_ohba_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_ae_fld_ohba_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_ae_fld_ohba2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_ae_fld_ohba2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_ftsf11_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 12) >> 29); + return tie_t; +} + +static void +Field_ftsf11_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0000) | (tie_t << 17); +} + +static unsigned +Field_ftsf11_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_ftsf11_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf11_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 8) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + return tie_t; +} + +static void +Field_ftsf11_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); + tie_t = (val << 29) >> 31; + insn[0] = (insn[0] & ~0x800000) | (tie_t << 23); +} + +static unsigned +Field_ftsf12_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + return tie_t; +} + +static void +Field_ftsf12_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_ftsf12_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ftsf12_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); +} + +static unsigned +Field_ftsf13_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 12) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_ftsf13_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80000) | (tie_t << 19); +} + +static unsigned +Field_combined2c0b5f72_fld37_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld37_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_combined2c0b5f72_fld148ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 11) | ((insn[0] << 14) >> 21); + return tie_t; +} + +static void +Field_combined2c0b5f72_fld148ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 21) >> 21; + insn[0] = (insn[0] & ~0x3ff80) | (tie_t << 7); +} + +static unsigned +Field_bitindex_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 19) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_bitindex_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x1000) | (tie_t << 12); +} + +static unsigned +Field_bitindex_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_bitindex_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_bitindex_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_bitindex_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_bitindex_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 5) >> 28); + return tie_t; +} + +static void +Field_bitindex_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x7800000) | (tie_t << 23); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_s3to1_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_s3to1_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_s3to1_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_s3to1_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_s3to1_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 13) >> 29); + return tie_t; +} + +static void +Field_s3to1_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70000) | (tie_t << 16); +} + +static void +Implicit_Field_set (xtensa_insnbuf insn ATTRIBUTE_UNUSED, + uint32 val ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +static unsigned +Implicit_Field_ar0_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_ar4_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 4; +} + +static unsigned +Implicit_Field_ar8_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 8; +} + +static unsigned +Implicit_Field_ar12_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 12; +} + +static unsigned +Implicit_Field_mr0_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_mr1_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 1; +} + +static unsigned +Implicit_Field_mr2_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 2; +} + +static unsigned +Implicit_Field_mr3_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 3; +} + +static unsigned +Implicit_Field_bt16_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_bs16_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_br16_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_brall_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +enum xtensa_field_id { + FIELD_t, + FIELD_bbi4, + FIELD_bbi, + FIELD_imm12, + FIELD_imm8, + FIELD_s, + FIELD_imm12b, + FIELD_imm16, + FIELD_m, + FIELD_n, + FIELD_offset, + FIELD_op0, + FIELD_op1, + FIELD_op2, + FIELD_r, + FIELD_sa4, + FIELD_sae4, + FIELD_sae, + FIELD_sal, + FIELD_sargt, + FIELD_sas4, + FIELD_sas, + FIELD_sr, + FIELD_st, + FIELD_thi3, + FIELD_imm4, + FIELD_mn, + FIELD_i, + FIELD_imm6lo, + FIELD_imm6hi, + FIELD_imm7lo, + FIELD_imm7hi, + FIELD_z, + FIELD_imm6, + FIELD_imm7, + FIELD_r3, + FIELD_rbit2, + FIELD_rhi, + FIELD_t3, + FIELD_tbit2, + FIELD_tlo, + FIELD_w, + FIELD_y, + FIELD_x, + FIELD_t2, + FIELD_s2, + FIELD_r2, + FIELD_t4, + FIELD_s4, + FIELD_r4, + FIELD_t8, + FIELD_s8, + FIELD_r8, + FIELD_xt_wbr15_imm, + FIELD_xt_wbr18_imm, + FIELD_ae_r3, + FIELD_ae_s_non_samt, + FIELD_ae_s3, + FIELD_ae_r32, + FIELD_ae_samt_s_t, + FIELD_ae_r20, + FIELD_ae_r10, + FIELD_ae_s20, + FIELD_ae_fld_ohba, + FIELD_ae_fld_ohba2, + FIELD_op0_s3, + FIELD_ftsf11, + FIELD_ftsf12, + FIELD_ftsf13, + FIELD_ftsf20ae_slot1, + FIELD_ftsf21ae_slot1, + FIELD_ftsf22ae_slot1, + FIELD_ftsf23ae_slot1, + FIELD_ftsf24ae_slot1, + FIELD_ftsf25ae_slot1, + FIELD_ftsf26ae_slot1, + FIELD_ftsf27ae_slot1, + FIELD_ftsf28ae_slot1, + FIELD_ftsf29ae_slot1, + FIELD_ftsf30ae_slot1, + FIELD_ftsf31ae_slot1, + FIELD_ftsf32ae_slot1, + FIELD_ftsf33ae_slot1, + FIELD_ftsf34ae_slot1, + FIELD_ftsf35ae_slot1, + FIELD_ftsf36ae_slot1, + FIELD_ftsf37ae_slot1, + FIELD_ftsf38ae_slot1, + FIELD_ftsf40ae_slot1, + FIELD_ftsf41ae_slot1, + FIELD_ftsf42ae_slot1, + FIELD_ftsf43ae_slot1, + FIELD_ftsf45ae_slot1, + FIELD_ftsf47ae_slot1, + FIELD_ftsf48ae_slot1, + FIELD_ftsf49ae_slot1, + FIELD_ftsf50ae_slot1, + FIELD_ftsf51ae_slot1, + FIELD_ftsf52ae_slot1, + FIELD_ftsf53ae_slot1, + FIELD_ftsf54ae_slot1, + FIELD_ftsf55, + FIELD_ftsf56ae_slot1, + FIELD_ftsf57ae_slot1, + FIELD_ftsf58ae_slot1, + FIELD_ftsf60ae_slot1, + FIELD_ftsf61, + FIELD_ftsf62ae_slot1, + FIELD_ftsf63ae_slot1, + FIELD_ftsf64ae_slot1, + FIELD_ftsf66ae_slot1, + FIELD_ftsf68ae_slot1, + FIELD_ftsf69ae_slot1, + FIELD_ftsf70ae_slot1, + FIELD_ftsf71ae_slot1, + FIELD_ftsf72ae_slot1, + FIELD_ftsf73ae_slot1, + FIELD_ftsf74ae_slot1, + FIELD_ftsf75ae_slot1, + FIELD_ftsf76ae_slot1, + FIELD_ftsf77ae_slot1, + FIELD_ftsf78ae_slot1, + FIELD_ftsf79ae_slot1, + FIELD_ftsf80ae_slot1, + FIELD_ftsf81ae_slot1, + FIELD_ftsf82ae_slot1, + FIELD_ftsf83ae_slot1, + FIELD_ftsf84ae_slot1, + FIELD_ftsf85ae_slot1, + FIELD_ftsf86ae_slot1, + FIELD_ftsf87ae_slot1, + FIELD_ftsf88ae_slot1, + FIELD_ftsf89ae_slot1, + FIELD_ftsf90ae_slot1, + FIELD_ftsf91, + FIELD_ftsf92ae_slot1, + FIELD_ftsf93ae_slot1, + FIELD_ftsf94ae_slot1, + FIELD_ftsf96ae_slot1, + FIELD_ftsf97ae_slot1, + FIELD_ftsf99ae_slot1, + FIELD_ftsf101ae_slot1, + FIELD_ftsf102ae_slot1, + FIELD_ftsf103ae_slot1, + FIELD_ftsf106ae_slot1, + FIELD_ftsf107ae_slot1, + FIELD_ftsf108ae_slot1, + FIELD_ftsf109ae_slot1, + FIELD_ftsf110ae_slot1, + FIELD_ftsf111ae_slot1, + FIELD_ftsf112ae_slot1, + FIELD_ftsf113ae_slot1, + FIELD_ftsf114ae_slot1, + FIELD_ftsf115ae_slot1, + FIELD_ftsf116ae_slot1, + FIELD_ftsf117ae_slot1, + FIELD_ftsf118ae_slot1, + FIELD_ftsf120ae_slot1, + FIELD_ftsf121ae_slot1, + FIELD_ftsf123ae_slot1, + FIELD_ftsf124ae_slot1, + FIELD_ftsf125ae_slot1, + FIELD_ftsf126ae_slot1, + FIELD_ftsf127ae_slot1, + FIELD_ftsf128ae_slot1, + FIELD_ftsf129ae_slot1, + FIELD_ftsf130ae_slot1, + FIELD_ftsf131ae_slot1, + FIELD_ftsf132ae_slot1, + FIELD_ftsf133ae_slot1, + FIELD_ftsf134ae_slot1, + FIELD_ftsf135ae_slot1, + FIELD_ftsf136ae_slot1, + FIELD_ftsf137ae_slot1, + FIELD_ftsf138ae_slot1, + FIELD_ftsf139ae_slot1, + FIELD_ftsf140ae_slot1, + FIELD_ftsf141ae_slot1, + FIELD_ftsf142ae_slot1, + FIELD_ftsf143ae_slot1, + FIELD_ftsf144ae_slot1, + FIELD_ftsf145ae_slot1, + FIELD_ftsf146ae_slot1, + FIELD_ftsf147ae_slot1, + FIELD_ftsf148ae_slot1, + FIELD_ftsf149ae_slot1, + FIELD_ftsf150ae_slot1, + FIELD_ftsf151ae_slot1, + FIELD_ftsf152ae_slot1, + FIELD_ftsf153ae_slot1, + FIELD_ftsf154ae_slot1, + FIELD_ftsf155ae_slot1, + FIELD_ftsf156ae_slot1, + FIELD_ftsf157ae_slot1, + FIELD_ftsf158ae_slot1, + FIELD_ftsf159ae_slot1, + FIELD_ftsf160ae_slot1, + FIELD_ftsf161ae_slot1, + FIELD_ftsf162ae_slot1, + FIELD_ftsf163ae_slot1, + FIELD_ftsf164ae_slot1, + FIELD_ftsf165ae_slot1, + FIELD_ftsf166ae_slot1, + FIELD_ftsf167ae_slot1, + FIELD_ftsf168ae_slot1, + FIELD_ftsf169ae_slot1, + FIELD_ftsf170ae_slot1, + FIELD_ftsf171ae_slot1, + FIELD_ftsf172ae_slot1, + FIELD_ftsf173ae_slot1, + FIELD_ftsf174ae_slot1, + FIELD_ftsf175ae_slot1, + FIELD_ftsf176ae_slot1, + FIELD_ftsf177ae_slot1, + FIELD_ftsf178ae_slot1, + FIELD_ftsf179ae_slot1, + FIELD_ftsf180ae_slot1, + FIELD_ftsf181ae_slot1, + FIELD_ftsf182ae_slot1, + FIELD_ftsf183ae_slot1, + FIELD_ftsf184ae_slot1, + FIELD_ftsf185ae_slot1, + FIELD_ftsf186ae_slot1, + FIELD_ftsf187ae_slot1, + FIELD_ftsf188ae_slot1, + FIELD_ftsf189ae_slot1, + FIELD_ftsf190ae_slot1, + FIELD_ftsf191ae_slot1, + FIELD_ftsf192ae_slot1, + FIELD_ftsf193ae_slot1, + FIELD_ftsf194ae_slot1, + FIELD_ftsf195ae_slot1, + FIELD_ftsf196ae_slot1, + FIELD_ftsf197ae_slot1, + FIELD_ftsf198ae_slot1, + FIELD_ftsf199ae_slot1, + FIELD_ftsf200ae_slot1, + FIELD_ftsf201ae_slot1, + FIELD_ftsf202ae_slot1, + FIELD_ftsf203ae_slot1, + FIELD_ftsf204ae_slot1, + FIELD_ftsf205ae_slot1, + FIELD_ftsf206ae_slot1, + FIELD_ftsf207ae_slot1, + FIELD_ftsf208ae_slot1, + FIELD_ftsf210ae_slot1, + FIELD_ftsf333ae_slot1, + FIELD_ftsf334ae_slot1, + FIELD_ftsf335, + FIELD_ftsf336ae_slot1, + FIELD_ftsf337ae_slot1, + FIELD_ftsf339ae_slot1, + FIELD_ftsf340ae_slot1, + FIELD_ftsf341ae_slot1, + FIELD_ftsf342ae_slot1, + FIELD_ftsf343ae_slot1, + FIELD_ftsf344ae_slot1, + FIELD_ftsf345ae_slot1, + FIELD_ftsf347ae_slot1, + FIELD_ftsf348ae_slot1, + FIELD_ftsf349ae_slot1, + FIELD_ftsf350ae_slot1, + FIELD_ftsf351, + FIELD_ftsf352ae_slot1, + FIELD_ftsf354ae_slot1, + FIELD_ftsf355ae_slot1, + FIELD_ftsf356ae_slot1, + FIELD_ftsf357ae_slot1, + FIELD_ftsf358ae_slot1, + FIELD_ftsf359ae_slot1, + FIELD_op0_s4, + FIELD_ftsf211ae_slot0, + FIELD_ftsf212ae_slot0, + FIELD_ftsf213ae_slot0, + FIELD_ftsf214ae_slot0, + FIELD_ftsf215ae_slot0, + FIELD_ftsf217ae_slot0, + FIELD_ftsf218ae_slot0, + FIELD_ftsf219ae_slot0, + FIELD_ftsf220ae_slot0, + FIELD_ftsf221ae_slot0, + FIELD_ftsf222ae_slot0, + FIELD_ftsf223ae_slot0, + FIELD_ftsf224ae_slot0, + FIELD_ftsf225ae_slot0, + FIELD_ftsf226ae_slot0, + FIELD_ftsf227ae_slot0, + FIELD_ftsf228ae_slot0, + FIELD_ftsf229ae_slot0, + FIELD_ftsf230ae_slot0, + FIELD_ftsf231ae_slot0, + FIELD_ftsf232ae_slot0, + FIELD_ftsf233ae_slot0, + FIELD_ftsf234ae_slot0, + FIELD_ftsf235ae_slot0, + FIELD_ftsf236ae_slot0, + FIELD_ftsf237ae_slot0, + FIELD_ftsf238ae_slot0, + FIELD_ftsf239ae_slot0, + FIELD_ftsf240ae_slot0, + FIELD_ftsf241ae_slot0, + FIELD_ftsf242ae_slot0, + FIELD_ftsf243ae_slot0, + FIELD_ftsf244ae_slot0, + FIELD_ftsf245ae_slot0, + FIELD_ftsf246ae_slot0, + FIELD_ftsf247ae_slot0, + FIELD_ftsf248ae_slot0, + FIELD_ftsf249ae_slot0, + FIELD_ftsf250ae_slot0, + FIELD_ftsf251ae_slot0, + FIELD_ftsf252ae_slot0, + FIELD_ftsf253ae_slot0, + FIELD_ftsf254ae_slot0, + FIELD_ftsf255ae_slot0, + FIELD_ftsf256ae_slot0, + FIELD_ftsf257ae_slot0, + FIELD_ftsf258ae_slot0, + FIELD_ftsf259ae_slot0, + FIELD_ftsf260ae_slot0, + FIELD_ftsf261ae_slot0, + FIELD_ftsf262ae_slot0, + FIELD_ftsf263ae_slot0, + FIELD_ftsf264ae_slot0, + FIELD_ftsf265ae_slot0, + FIELD_ftsf266ae_slot0, + FIELD_ftsf267ae_slot0, + FIELD_ftsf268ae_slot0, + FIELD_ftsf269ae_slot0, + FIELD_ftsf270ae_slot0, + FIELD_ftsf271ae_slot0, + FIELD_ftsf272ae_slot0, + FIELD_ftsf273ae_slot0, + FIELD_ftsf274ae_slot0, + FIELD_ftsf275ae_slot0, + FIELD_ftsf276ae_slot0, + FIELD_ftsf277ae_slot0, + FIELD_ftsf278ae_slot0, + FIELD_ftsf279ae_slot0, + FIELD_ftsf280, + FIELD_ftsf281ae_slot0, + FIELD_ftsf282ae_slot0, + FIELD_ftsf284ae_slot0, + FIELD_ftsf285ae_slot0, + FIELD_ftsf287ae_slot0, + FIELD_ftsf288, + FIELD_ftsf289ae_slot0, + FIELD_ftsf290ae_slot0, + FIELD_ftsf291ae_slot0, + FIELD_ftsf292ae_slot0, + FIELD_ftsf293ae_slot0, + FIELD_ftsf294ae_slot0, + FIELD_ftsf295ae_slot0, + FIELD_ftsf296ae_slot0, + FIELD_ftsf297ae_slot0, + FIELD_ftsf298ae_slot0, + FIELD_ftsf300ae_slot0, + FIELD_ftsf302ae_slot0, + FIELD_ftsf304ae_slot0, + FIELD_ftsf305ae_slot0, + FIELD_ftsf306ae_slot0, + FIELD_ftsf307ae_slot0, + FIELD_ftsf308ae_slot0, + FIELD_ftsf309, + FIELD_ftsf310ae_slot0, + FIELD_ftsf311ae_slot0, + FIELD_ftsf312ae_slot0, + FIELD_ftsf313ae_slot0, + FIELD_ftsf314ae_slot0, + FIELD_ftsf315, + FIELD_ftsf316ae_slot0, + FIELD_ftsf317ae_slot0, + FIELD_ftsf319ae_slot0, + FIELD_ftsf320ae_slot0, + FIELD_ftsf322ae_slot0, + FIELD_ftsf323ae_slot0, + FIELD_ftsf324ae_slot0, + FIELD_ftsf325ae_slot0, + FIELD_ftsf326ae_slot0, + FIELD_ftsf327ae_slot0, + FIELD_ftsf328ae_slot0, + FIELD_ftsf329ae_slot0, + FIELD_ftsf360ae_slot0, + FIELD_ftsf361ae_slot0, + FIELD_ftsf362, + FIELD_ftsf363ae_slot0, + FIELD_ftsf364ae_slot0, + FIELD_ftsf366ae_slot0, + FIELD_ftsf368ae_slot0, + FIELD_ftsf370ae_slot0, + FIELD_ftsf373ae_slot0, + FIELD_ftsf376ae_slot0, + FIELD_ftsf378ae_slot0, + FIELD_ftsf379ae_slot0, + FIELD_ftsf382ae_slot0, + FIELD_ftsf383ae_slot0, + FIELD_ftsf384ae_slot0, + FIELD_ftsf386ae_slot0, + FIELD_ftsf387ae_slot0, + FIELD_ftsf388ae_slot0, + FIELD_ftsf389ae_slot0, + FIELD_ae_mul32x24fld, + FIELD_op0_s4_s4, + FIELD_combined2c0b5f72_fld28, + FIELD_combined2c0b5f72_fld37, + FIELD_combined2c0b5f72_fld39, + FIELD_combined2c0b5f72_fld40, + FIELD_combined2c0b5f72_fld46, + FIELD_combined2c0b5f72_fld47, + FIELD_combined2c0b5f72_fld49, + FIELD_combined2c0b5f72_fld50, + FIELD_combined2c0b5f72_fld52, + FIELD_combined2c0b5f72_fld121, + FIELD_combined2c0b5f72_fld123, + FIELD_combined2c0b5f72_fld127, + FIELD_combined2c0b5f72_fld133ae_slot0, + FIELD_combined2c0b5f72_fld134ae_slot0, + FIELD_combined2c0b5f72_fld135ae_slot0, + FIELD_combined2c0b5f72_fld136ae_slot0, + FIELD_combined2c0b5f72_fld137ae_slot0, + FIELD_combined2c0b5f72_fld138ae_slot0, + FIELD_combined2c0b5f72_fld139ae_slot0, + FIELD_combined2c0b5f72_fld140ae_slot0, + FIELD_combined2c0b5f72_fld141ae_slot0, + FIELD_combined2c0b5f72_fld142ae_slot0, + FIELD_combined2c0b5f72_fld143ae_slot0, + FIELD_combined2c0b5f72_fld144ae_slot0, + FIELD_combined2c0b5f72_fld145ae_slot0, + FIELD_combined2c0b5f72_fld146ae_slot0, + FIELD_combined2c0b5f72_fld148ae_slot0, + FIELD_combined2c0b5f72_fld149ae_slot0, + FIELD_op0_s4_s4_s4, + FIELD_combined1e9fefee_fld96, + FIELD_combined1e9fefee_fld98, + FIELD_combined1e9fefee_fld106ae_slot0, + FIELD_combined1e9fefee_fld107ae_slot0, + FIELD_combined1e9fefee_fld108ae_slot0, + FIELD_combined1e9fefee_fld109ae_slot0, + FIELD_op0_s3_s3, + FIELD_combined2c0b5f72_fld19, + FIELD_combined2c0b5f72_fld22, + FIELD_combined2c0b5f72_fld24, + FIELD_combined2c0b5f72_fld65, + FIELD_combined2c0b5f72_fld66, + FIELD_combined2c0b5f72_fld68, + FIELD_combined2c0b5f72_fld69, + FIELD_combined2c0b5f72_fld74, + FIELD_combined2c0b5f72_fld79, + FIELD_combined2c0b5f72_fld88, + FIELD_combined2c0b5f72_fld90, + FIELD_combined2c0b5f72_fld91, + FIELD_combined2c0b5f72_fld131ae_slot1, + FIELD_combined2c0b5f72_fld132ae_slot1, + FIELD_combined2c0b5f72_fld147ae_slot1, + FIELD_bitindex, + FIELD_s3to1, + FIELD__ar0, + FIELD__ar4, + FIELD__ar8, + FIELD__ar12, + FIELD__mr0, + FIELD__mr1, + FIELD__mr2, + FIELD__mr3, + FIELD__bt16, + FIELD__bs16, + FIELD__br16, + FIELD__brall +}; + + +/* Functional units. */ + +static xtensa_funcUnit_internal funcUnits[] = { + { "ae_add32", 1 }, + { "ae_shift32x4", 1 }, + { "ae_shift32x5", 1 }, + { "ae_subshift", 1 } +}; + +enum xtensa_funcUnit_id { + FUNCUNIT_ae_add32, + FUNCUNIT_ae_shift32x4, + FUNCUNIT_ae_shift32x5, + FUNCUNIT_ae_subshift +}; + + +/* Register files. */ + +enum xtensa_regfile_id { + REGFILE_AR, + REGFILE_MR, + REGFILE_BR, + REGFILE_AE_PR, + REGFILE_AE_QR, + REGFILE_BR2, + REGFILE_BR4, + REGFILE_BR8, + REGFILE_BR16 +}; + +static xtensa_regfile_internal regfiles[] = { + { "AR", "a", REGFILE_AR, 32, 32 }, + { "MR", "m", REGFILE_MR, 32, 4 }, + { "BR", "b", REGFILE_BR, 1, 16 }, + { "AE_PR", "aep", REGFILE_AE_PR, 48, 8 }, + { "AE_QR", "aeq", REGFILE_AE_QR, 56, 4 }, + { "BR2", "b", REGFILE_BR, 2, 8 }, + { "BR4", "b", REGFILE_BR, 4, 4 }, + { "BR8", "b", REGFILE_BR, 8, 2 }, + { "BR16", "b", REGFILE_BR, 16, 1 } +}; + + +/* Interfaces. */ + +static xtensa_interface_internal interfaces[] = { + { "ERI_RD_Out", 14, 0, 0, 'o' }, + { "ERI_RD_In", 32, 0, 1, 'i' }, + { "ERI_RD_Rdy", 1, 0, 0, 'i' }, + { "ERI_WR_Out", 46, 0, 2, 'o' }, + { "ERI_WR_In", 1, 0, 3, 'i' }, + { "IMPWIRE", 32, 0, 4, 'i' } +}; + +enum xtensa_interface_id { + INTERFACE_ERI_RD_Out, + INTERFACE_ERI_RD_In, + INTERFACE_ERI_RD_Rdy, + INTERFACE_ERI_WR_Out, + INTERFACE_ERI_WR_In, + INTERFACE_IMPWIRE +}; + + +/* Constant tables. */ + +/* constant table ai4c */ +static const unsigned CONST_TBL_ai4c_0[] = { + 0xffffffff, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0x9, + 0xa, + 0xb, + 0xc, + 0xd, + 0xe, + 0xf, + 0 +}; + +/* constant table b4c */ +static const unsigned CONST_TBL_b4c_0[] = { + 0xffffffff, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0xa, + 0xc, + 0x10, + 0x20, + 0x40, + 0x80, + 0x100, + 0 +}; + +/* constant table b4cu */ +static const unsigned CONST_TBL_b4cu_0[] = { + 0x8000, + 0x10000, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0xa, + 0xc, + 0x10, + 0x20, + 0x40, + 0x80, + 0x100, + 0 +}; + + +/* Instruction operands. */ + +static int +OperandSem_opnd_sem_MR_0_decode (uint32 *valp) +{ + *valp += 2; + return 0; +} + +static int +OperandSem_opnd_sem_MR_0_encode (uint32 *valp) +{ + int error; + error = ((*valp & ~0x3) != 0) || ((*valp & 0x2) == 0); + *valp = *valp & 1; + return error; +} + +static int +OperandSem_opnd_sem_soffsetx4_decode (uint32 *valp) +{ + unsigned soffsetx4_out_0; + unsigned soffsetx4_in_0; + soffsetx4_in_0 = *valp & 0x3ffff; + soffsetx4_out_0 = 0x4 + ((((int) soffsetx4_in_0 << 14) >> 14) << 2); + *valp = soffsetx4_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_soffsetx4_encode (uint32 *valp) +{ + unsigned soffsetx4_in_0; + unsigned soffsetx4_out_0; + soffsetx4_out_0 = *valp; + soffsetx4_in_0 = ((soffsetx4_out_0 - 0x4) >> 2) & 0x3ffff; + *valp = soffsetx4_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm12x8_decode (uint32 *valp) +{ + unsigned uimm12x8_out_0; + unsigned uimm12x8_in_0; + uimm12x8_in_0 = *valp & 0xfff; + uimm12x8_out_0 = uimm12x8_in_0 << 3; + *valp = uimm12x8_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm12x8_encode (uint32 *valp) +{ + unsigned uimm12x8_in_0; + unsigned uimm12x8_out_0; + uimm12x8_out_0 = *valp; + uimm12x8_in_0 = ((uimm12x8_out_0 >> 3) & 0xfff); + *valp = uimm12x8_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm4_decode (uint32 *valp) +{ + unsigned simm4_out_0; + unsigned simm4_in_0; + simm4_in_0 = *valp & 0xf; + simm4_out_0 = ((int) simm4_in_0 << 28) >> 28; + *valp = simm4_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm4_encode (uint32 *valp) +{ + unsigned simm4_in_0; + unsigned simm4_out_0; + simm4_out_0 = *valp; + simm4_in_0 = (simm4_out_0 & 0xf); + *valp = simm4_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_AR_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AR_encode (uint32 *valp) +{ + int error; + error = (*valp >= 32); + return error; +} + +static int +OperandSem_opnd_sem_AR_0_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AR_0_encode (uint32 *valp) +{ + int error; + error = (*valp >= 32); + return error; +} + +static int +OperandSem_opnd_sem_AR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AR_1_encode (uint32 *valp) +{ + int error; + error = (*valp >= 32); + return error; +} + +static int +OperandSem_opnd_sem_AR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AR_2_encode (uint32 *valp) +{ + int error; + error = (*valp >= 32); + return error; +} + +static int +OperandSem_opnd_sem_AR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AR_3_encode (uint32 *valp) +{ + int error; + error = (*valp >= 32); + return error; +} + +static int +OperandSem_opnd_sem_AR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AR_4_encode (uint32 *valp) +{ + int error; + error = (*valp >= 32); + return error; +} + +static int +OperandSem_opnd_sem_immrx4_decode (uint32 *valp) +{ + unsigned immrx4_out_0; + unsigned immrx4_in_0; + immrx4_in_0 = *valp & 0xf; + immrx4_out_0 = (((0xfffffff) << 4) | immrx4_in_0) << 2; + *valp = immrx4_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_immrx4_encode (uint32 *valp) +{ + unsigned immrx4_in_0; + unsigned immrx4_out_0; + immrx4_out_0 = *valp; + immrx4_in_0 = ((immrx4_out_0 >> 2) & 0xf); + *valp = immrx4_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_lsi4x4_decode (uint32 *valp) +{ + unsigned lsi4x4_out_0; + unsigned lsi4x4_in_0; + lsi4x4_in_0 = *valp & 0xf; + lsi4x4_out_0 = lsi4x4_in_0 << 2; + *valp = lsi4x4_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_lsi4x4_encode (uint32 *valp) +{ + unsigned lsi4x4_in_0; + unsigned lsi4x4_out_0; + lsi4x4_out_0 = *valp; + lsi4x4_in_0 = ((lsi4x4_out_0 >> 2) & 0xf); + *valp = lsi4x4_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm7_decode (uint32 *valp) +{ + unsigned simm7_out_0; + unsigned simm7_in_0; + simm7_in_0 = *valp & 0x7f; + simm7_out_0 = ((((-((((simm7_in_0 >> 6) & 1)) & (((simm7_in_0 >> 5) & 1)))) & 0x1ffffff)) << 7) | simm7_in_0; + *valp = simm7_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm7_encode (uint32 *valp) +{ + unsigned simm7_in_0; + unsigned simm7_out_0; + simm7_out_0 = *valp; + simm7_in_0 = (simm7_out_0 & 0x7f); + *valp = simm7_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm6_decode (uint32 *valp) +{ + unsigned uimm6_out_0; + unsigned uimm6_in_0; + uimm6_in_0 = *valp & 0x3f; + uimm6_out_0 = 0x4 + (((0) << 6) | uimm6_in_0); + *valp = uimm6_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm6_encode (uint32 *valp) +{ + unsigned uimm6_in_0; + unsigned uimm6_out_0; + uimm6_out_0 = *valp; + uimm6_in_0 = (uimm6_out_0 - 0x4) & 0x3f; + *valp = uimm6_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ai4const_decode (uint32 *valp) +{ + unsigned ai4const_out_0; + unsigned ai4const_in_0; + ai4const_in_0 = *valp & 0xf; + ai4const_out_0 = CONST_TBL_ai4c_0[ai4const_in_0 & 0xf]; + *valp = ai4const_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ai4const_encode (uint32 *valp) +{ + unsigned ai4const_in_0; + unsigned ai4const_out_0; + ai4const_out_0 = *valp; + switch (ai4const_out_0) + { + case 0xffffffff: ai4const_in_0 = 0; break; + case 0x1: ai4const_in_0 = 0x1; break; + case 0x2: ai4const_in_0 = 0x2; break; + case 0x3: ai4const_in_0 = 0x3; break; + case 0x4: ai4const_in_0 = 0x4; break; + case 0x5: ai4const_in_0 = 0x5; break; + case 0x6: ai4const_in_0 = 0x6; break; + case 0x7: ai4const_in_0 = 0x7; break; + case 0x8: ai4const_in_0 = 0x8; break; + case 0x9: ai4const_in_0 = 0x9; break; + case 0xa: ai4const_in_0 = 0xa; break; + case 0xb: ai4const_in_0 = 0xb; break; + case 0xc: ai4const_in_0 = 0xc; break; + case 0xd: ai4const_in_0 = 0xd; break; + case 0xe: ai4const_in_0 = 0xe; break; + default: ai4const_in_0 = 0xf; break; + } + *valp = ai4const_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_b4const_decode (uint32 *valp) +{ + unsigned b4const_out_0; + unsigned b4const_in_0; + b4const_in_0 = *valp & 0xf; + b4const_out_0 = CONST_TBL_b4c_0[b4const_in_0 & 0xf]; + *valp = b4const_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_b4const_encode (uint32 *valp) +{ + unsigned b4const_in_0; + unsigned b4const_out_0; + b4const_out_0 = *valp; + switch (b4const_out_0) + { + case 0xffffffff: b4const_in_0 = 0; break; + case 0x1: b4const_in_0 = 0x1; break; + case 0x2: b4const_in_0 = 0x2; break; + case 0x3: b4const_in_0 = 0x3; break; + case 0x4: b4const_in_0 = 0x4; break; + case 0x5: b4const_in_0 = 0x5; break; + case 0x6: b4const_in_0 = 0x6; break; + case 0x7: b4const_in_0 = 0x7; break; + case 0x8: b4const_in_0 = 0x8; break; + case 0xa: b4const_in_0 = 0x9; break; + case 0xc: b4const_in_0 = 0xa; break; + case 0x10: b4const_in_0 = 0xb; break; + case 0x20: b4const_in_0 = 0xc; break; + case 0x40: b4const_in_0 = 0xd; break; + case 0x80: b4const_in_0 = 0xe; break; + default: b4const_in_0 = 0xf; break; + } + *valp = b4const_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_b4constu_decode (uint32 *valp) +{ + unsigned b4constu_out_0; + unsigned b4constu_in_0; + b4constu_in_0 = *valp & 0xf; + b4constu_out_0 = CONST_TBL_b4cu_0[b4constu_in_0 & 0xf]; + *valp = b4constu_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_b4constu_encode (uint32 *valp) +{ + unsigned b4constu_in_0; + unsigned b4constu_out_0; + b4constu_out_0 = *valp; + switch (b4constu_out_0) + { + case 0x8000: b4constu_in_0 = 0; break; + case 0x10000: b4constu_in_0 = 0x1; break; + case 0x2: b4constu_in_0 = 0x2; break; + case 0x3: b4constu_in_0 = 0x3; break; + case 0x4: b4constu_in_0 = 0x4; break; + case 0x5: b4constu_in_0 = 0x5; break; + case 0x6: b4constu_in_0 = 0x6; break; + case 0x7: b4constu_in_0 = 0x7; break; + case 0x8: b4constu_in_0 = 0x8; break; + case 0xa: b4constu_in_0 = 0x9; break; + case 0xc: b4constu_in_0 = 0xa; break; + case 0x10: b4constu_in_0 = 0xb; break; + case 0x20: b4constu_in_0 = 0xc; break; + case 0x40: b4constu_in_0 = 0xd; break; + case 0x80: b4constu_in_0 = 0xe; break; + default: b4constu_in_0 = 0xf; break; + } + *valp = b4constu_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm8_decode (uint32 *valp) +{ + unsigned uimm8_out_0; + unsigned uimm8_in_0; + uimm8_in_0 = *valp & 0xff; + uimm8_out_0 = uimm8_in_0; + *valp = uimm8_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm8_encode (uint32 *valp) +{ + unsigned uimm8_in_0; + unsigned uimm8_out_0; + uimm8_out_0 = *valp; + uimm8_in_0 = (uimm8_out_0 & 0xff); + *valp = uimm8_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm8x2_decode (uint32 *valp) +{ + unsigned uimm8x2_out_0; + unsigned uimm8x2_in_0; + uimm8x2_in_0 = *valp & 0xff; + uimm8x2_out_0 = uimm8x2_in_0 << 1; + *valp = uimm8x2_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm8x2_encode (uint32 *valp) +{ + unsigned uimm8x2_in_0; + unsigned uimm8x2_out_0; + uimm8x2_out_0 = *valp; + uimm8x2_in_0 = ((uimm8x2_out_0 >> 1) & 0xff); + *valp = uimm8x2_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm8x4_decode (uint32 *valp) +{ + unsigned uimm8x4_out_0; + unsigned uimm8x4_in_0; + uimm8x4_in_0 = *valp & 0xff; + uimm8x4_out_0 = uimm8x4_in_0 << 2; + *valp = uimm8x4_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm8x4_encode (uint32 *valp) +{ + unsigned uimm8x4_in_0; + unsigned uimm8x4_out_0; + uimm8x4_out_0 = *valp; + uimm8x4_in_0 = ((uimm8x4_out_0 >> 2) & 0xff); + *valp = uimm8x4_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm4x16_decode (uint32 *valp) +{ + unsigned uimm4x16_out_0; + unsigned uimm4x16_in_0; + uimm4x16_in_0 = *valp & 0xf; + uimm4x16_out_0 = uimm4x16_in_0 << 4; + *valp = uimm4x16_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm4x16_encode (uint32 *valp) +{ + unsigned uimm4x16_in_0; + unsigned uimm4x16_out_0; + uimm4x16_out_0 = *valp; + uimm4x16_in_0 = ((uimm4x16_out_0 >> 4) & 0xf); + *valp = uimm4x16_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm8_decode (uint32 *valp) +{ + unsigned simm8_out_0; + unsigned simm8_in_0; + simm8_in_0 = *valp & 0xff; + simm8_out_0 = ((int) simm8_in_0 << 24) >> 24; + *valp = simm8_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm8_encode (uint32 *valp) +{ + unsigned simm8_in_0; + unsigned simm8_out_0; + simm8_out_0 = *valp; + simm8_in_0 = (simm8_out_0 & 0xff); + *valp = simm8_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm8x256_decode (uint32 *valp) +{ + unsigned simm8x256_out_0; + unsigned simm8x256_in_0; + simm8x256_in_0 = *valp & 0xff; + simm8x256_out_0 = (((int) simm8x256_in_0 << 24) >> 24) << 8; + *valp = simm8x256_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm8x256_encode (uint32 *valp) +{ + unsigned simm8x256_in_0; + unsigned simm8x256_out_0; + simm8x256_out_0 = *valp; + simm8x256_in_0 = ((simm8x256_out_0 >> 8) & 0xff); + *valp = simm8x256_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm12b_decode (uint32 *valp) +{ + unsigned simm12b_out_0; + unsigned simm12b_in_0; + simm12b_in_0 = *valp & 0xfff; + simm12b_out_0 = ((int) simm12b_in_0 << 20) >> 20; + *valp = simm12b_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_simm12b_encode (uint32 *valp) +{ + unsigned simm12b_in_0; + unsigned simm12b_out_0; + simm12b_out_0 = *valp; + simm12b_in_0 = (simm12b_out_0 & 0xfff); + *valp = simm12b_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_msalp32_decode (uint32 *valp) +{ + unsigned msalp32_out_0; + unsigned msalp32_in_0; + msalp32_in_0 = *valp & 0x1f; + msalp32_out_0 = 0x20 - msalp32_in_0; + *valp = msalp32_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_msalp32_encode (uint32 *valp) +{ + unsigned msalp32_in_0; + unsigned msalp32_out_0; + msalp32_out_0 = *valp; + msalp32_in_0 = (0x20 - msalp32_out_0) & 0x1f; + *valp = msalp32_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_op2p1_decode (uint32 *valp) +{ + unsigned op2p1_out_0; + unsigned op2p1_in_0; + op2p1_in_0 = *valp & 0xf; + op2p1_out_0 = op2p1_in_0 + 0x1; + *valp = op2p1_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_op2p1_encode (uint32 *valp) +{ + unsigned op2p1_in_0; + unsigned op2p1_out_0; + op2p1_out_0 = *valp; + op2p1_in_0 = (op2p1_out_0 - 0x1) & 0xf; + *valp = op2p1_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_label8_decode (uint32 *valp) +{ + unsigned label8_out_0; + unsigned label8_in_0; + label8_in_0 = *valp & 0xff; + label8_out_0 = 0x4 + (((int) label8_in_0 << 24) >> 24); + *valp = label8_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_label8_encode (uint32 *valp) +{ + unsigned label8_in_0; + unsigned label8_out_0; + label8_out_0 = *valp; + label8_in_0 = (label8_out_0 - 0x4) & 0xff; + *valp = label8_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ulabel8_decode (uint32 *valp) +{ + unsigned ulabel8_out_0; + unsigned ulabel8_in_0; + ulabel8_in_0 = *valp & 0xff; + ulabel8_out_0 = 0x4 + (((0) << 8) | ulabel8_in_0); + *valp = ulabel8_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ulabel8_encode (uint32 *valp) +{ + unsigned ulabel8_in_0; + unsigned ulabel8_out_0; + ulabel8_out_0 = *valp; + ulabel8_in_0 = (ulabel8_out_0 - 0x4) & 0xff; + *valp = ulabel8_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_label12_decode (uint32 *valp) +{ + unsigned label12_out_0; + unsigned label12_in_0; + label12_in_0 = *valp & 0xfff; + label12_out_0 = 0x4 + (((int) label12_in_0 << 20) >> 20); + *valp = label12_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_label12_encode (uint32 *valp) +{ + unsigned label12_in_0; + unsigned label12_out_0; + label12_out_0 = *valp; + label12_in_0 = (label12_out_0 - 0x4) & 0xfff; + *valp = label12_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_soffset_decode (uint32 *valp) +{ + unsigned soffset_out_0; + unsigned soffset_in_0; + soffset_in_0 = *valp & 0x3ffff; + soffset_out_0 = 0x4 + (((int) soffset_in_0 << 14) >> 14); + *valp = soffset_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_soffset_encode (uint32 *valp) +{ + unsigned soffset_in_0; + unsigned soffset_out_0; + soffset_out_0 = *valp; + soffset_in_0 = (soffset_out_0 - 0x4) & 0x3ffff; + *valp = soffset_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm16x4_decode (uint32 *valp) +{ + unsigned uimm16x4_out_0; + unsigned uimm16x4_in_0; + uimm16x4_in_0 = *valp & 0xffff; + uimm16x4_out_0 = (((0xffff) << 16) | uimm16x4_in_0) << 2; + *valp = uimm16x4_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_uimm16x4_encode (uint32 *valp) +{ + unsigned uimm16x4_in_0; + unsigned uimm16x4_out_0; + uimm16x4_out_0 = *valp; + uimm16x4_in_0 = (uimm16x4_out_0 >> 2) & 0xffff; + *valp = uimm16x4_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_bbi_decode (uint32 *valp) +{ + unsigned bbi_out_0; + unsigned bbi_in_0; + bbi_in_0 = *valp & 0x1f; + bbi_out_0 = (0 << 5) | bbi_in_0; + *valp = bbi_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_bbi_encode (uint32 *valp) +{ + unsigned bbi_in_0; + unsigned bbi_out_0; + bbi_out_0 = *valp; + bbi_in_0 = (bbi_out_0 & 0x1f); + *valp = bbi_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_s_decode (uint32 *valp) +{ + unsigned s_out_0; + unsigned s_in_0; + s_in_0 = *valp & 0xf; + s_out_0 = (0 << 4) | s_in_0; + *valp = s_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_s_encode (uint32 *valp) +{ + unsigned s_in_0; + unsigned s_out_0; + s_out_0 = *valp; + s_in_0 = (s_out_0 & 0xf); + *valp = s_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_MR_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_MR_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_MR_1_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_MR_1_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_MR_2_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_MR_2_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_MR_3_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_MR_3_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_MR_4_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_MR_4_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_MR_5_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_MR_5_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_immt_decode (uint32 *valp) +{ + unsigned immt_out_0; + unsigned immt_in_0; + immt_in_0 = *valp & 0xf; + immt_out_0 = immt_in_0; + *valp = immt_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_immt_encode (uint32 *valp) +{ + unsigned immt_in_0; + unsigned immt_out_0; + immt_out_0 = *valp; + immt_in_0 = immt_out_0 & 0xf; + *valp = immt_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_BR_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_BR_encode (uint32 *valp) +{ + int error; + error = (*valp >= 16); + return error; +} + +static int +OperandSem_opnd_sem_BR2_decode (uint32 *valp) +{ + *valp = *valp << 1; + return 0; +} + +static int +OperandSem_opnd_sem_BR2_encode (uint32 *valp) +{ + int error; + error = (*valp >= 16) || ((*valp & 1) != 0); + *valp = *valp >> 1; + return error; +} + +static int +OperandSem_opnd_sem_BR4_decode (uint32 *valp) +{ + *valp = *valp << 2; + return 0; +} + +static int +OperandSem_opnd_sem_BR4_encode (uint32 *valp) +{ + int error; + error = (*valp >= 16) || ((*valp & 3) != 0); + *valp = *valp >> 2; + return error; +} + +static int +OperandSem_opnd_sem_BR8_decode (uint32 *valp) +{ + *valp = *valp << 3; + return 0; +} + +static int +OperandSem_opnd_sem_BR8_encode (uint32 *valp) +{ + int error; + error = (*valp >= 16) || ((*valp & 7) != 0); + *valp = *valp >> 3; + return error; +} + +static int +OperandSem_opnd_sem_BR16_decode (uint32 *valp) +{ + *valp = *valp << 4; + return 0; +} + +static int +OperandSem_opnd_sem_BR16_encode (uint32 *valp) +{ + int error; + error = (*valp >= 16) || ((*valp & 15) != 0); + *valp = *valp >> 4; + return error; +} + +static int +OperandSem_opnd_sem_tp7_decode (uint32 *valp) +{ + unsigned tp7_out_0; + unsigned tp7_in_0; + tp7_in_0 = *valp & 0xf; + tp7_out_0 = tp7_in_0 + 0x7; + *valp = tp7_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_tp7_encode (uint32 *valp) +{ + unsigned tp7_in_0; + unsigned tp7_out_0; + tp7_out_0 = *valp; + tp7_in_0 = (tp7_out_0 - 0x7) & 0xf; + *valp = tp7_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_xt_wbr15_label_decode (uint32 *valp) +{ + unsigned xt_wbr15_label_out_0; + unsigned xt_wbr15_label_in_0; + xt_wbr15_label_in_0 = *valp & 0x7fff; + xt_wbr15_label_out_0 = 0x4 + (((int) xt_wbr15_label_in_0 << 17) >> 17); + *valp = xt_wbr15_label_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_xt_wbr15_label_encode (uint32 *valp) +{ + unsigned xt_wbr15_label_in_0; + unsigned xt_wbr15_label_out_0; + xt_wbr15_label_out_0 = *valp; + xt_wbr15_label_in_0 = (xt_wbr15_label_out_0 - 0x4) & 0x7fff; + *valp = xt_wbr15_label_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_samt32_decode (uint32 *valp) +{ + unsigned ae_samt32_out_0; + unsigned ae_samt32_in_0; + ae_samt32_in_0 = *valp & 0x1f; + ae_samt32_out_0 = (0 << 5) | ae_samt32_in_0; + *valp = ae_samt32_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_samt32_encode (uint32 *valp) +{ + unsigned ae_samt32_in_0; + unsigned ae_samt32_out_0; + ae_samt32_out_0 = *valp; + ae_samt32_in_0 = (ae_samt32_out_0 & 0x1f); + *valp = ae_samt32_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_AE_PR_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AE_PR_encode (uint32 *valp) +{ + int error; + error = (*valp >= 8); + return error; +} + +static int +OperandSem_opnd_sem_AE_QR_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +OperandSem_opnd_sem_AE_QR_encode (uint32 *valp) +{ + int error; + error = (*valp >= 4); + return error; +} + +static int +OperandSem_opnd_sem_ae_lsimm16_decode (uint32 *valp) +{ + unsigned ae_lsimm16_out_0; + unsigned ae_lsimm16_in_0; + ae_lsimm16_in_0 = *valp & 0xf; + ae_lsimm16_out_0 = (((int) ae_lsimm16_in_0 << 28) >> 28) << 1; + *valp = ae_lsimm16_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_lsimm16_encode (uint32 *valp) +{ + unsigned ae_lsimm16_in_0; + unsigned ae_lsimm16_out_0; + ae_lsimm16_out_0 = *valp; + ae_lsimm16_in_0 = ((ae_lsimm16_out_0 >> 1) & 0xf); + *valp = ae_lsimm16_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_lsimm32_decode (uint32 *valp) +{ + unsigned ae_lsimm32_out_0; + unsigned ae_lsimm32_in_0; + ae_lsimm32_in_0 = *valp & 0xf; + ae_lsimm32_out_0 = (((int) ae_lsimm32_in_0 << 28) >> 28) << 2; + *valp = ae_lsimm32_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_lsimm32_encode (uint32 *valp) +{ + unsigned ae_lsimm32_in_0; + unsigned ae_lsimm32_out_0; + ae_lsimm32_out_0 = *valp; + ae_lsimm32_in_0 = ((ae_lsimm32_out_0 >> 2) & 0xf); + *valp = ae_lsimm32_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_lsimm64_decode (uint32 *valp) +{ + unsigned ae_lsimm64_out_0; + unsigned ae_lsimm64_in_0; + ae_lsimm64_in_0 = *valp & 0xf; + ae_lsimm64_out_0 = (((int) ae_lsimm64_in_0 << 28) >> 28) << 3; + *valp = ae_lsimm64_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_lsimm64_encode (uint32 *valp) +{ + unsigned ae_lsimm64_in_0; + unsigned ae_lsimm64_out_0; + ae_lsimm64_out_0 = *valp; + ae_lsimm64_in_0 = ((ae_lsimm64_out_0 >> 3) & 0xf); + *valp = ae_lsimm64_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_samt64_decode (uint32 *valp) +{ + unsigned ae_samt64_out_0; + unsigned ae_samt64_in_0; + ae_samt64_in_0 = *valp & 0x3f; + ae_samt64_out_0 = (0 << 6) | ae_samt64_in_0; + *valp = ae_samt64_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_samt64_encode (uint32 *valp) +{ + unsigned ae_samt64_in_0; + unsigned ae_samt64_out_0; + ae_samt64_out_0 = *valp; + ae_samt64_in_0 = (ae_samt64_out_0 & 0x3f); + *valp = ae_samt64_in_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_ohba_decode (uint32 *valp) +{ + unsigned ae_ohba_out_0; + unsigned ae_ohba_in_0; + ae_ohba_in_0 = *valp & 0xf; + ae_ohba_out_0 = (0 << 5) | (((((ae_ohba_in_0 & 0xf))) == 0) << 4) | ((ae_ohba_in_0 & 0xf)); + *valp = ae_ohba_out_0; + return 0; +} + +static int +OperandSem_opnd_sem_ae_ohba_encode (uint32 *valp) +{ + unsigned ae_ohba_in_0; + unsigned ae_ohba_out_0; + ae_ohba_out_0 = *valp; + ae_ohba_in_0 = (ae_ohba_out_0 & 0xf); + *valp = ae_ohba_in_0; + return 0; +} + +static int +Operand_soffsetx4_ator (uint32 *valp, uint32 pc) +{ + *valp -= (pc & ~0x3); + return 0; +} + +static int +Operand_soffsetx4_rtoa (uint32 *valp, uint32 pc) +{ + *valp += (pc & ~0x3); + return 0; +} + +static int +Operand_uimm6_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_uimm6_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_label8_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_label8_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_ulabel8_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_ulabel8_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_label12_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_label12_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_soffset_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_soffset_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_uimm16x4_ator (uint32 *valp, uint32 pc) +{ + *valp -= ((pc + 3) & ~0x3); + return 0; +} + +static int +Operand_uimm16x4_rtoa (uint32 *valp, uint32 pc) +{ + *valp += ((pc + 3) & ~0x3); + return 0; +} + +static int +Operand_xt_wbr15_label_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_xt_wbr15_label_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_xt_wbr18_label_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_xt_wbr18_label_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static xtensa_operand_internal operands[] = { + { "soffsetx4", FIELD_offset, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_soffsetx4_encode, OperandSem_opnd_sem_soffsetx4_decode, + Operand_soffsetx4_ator, Operand_soffsetx4_rtoa }, + { "uimm12x8", FIELD_imm12, -1, 0, + 0, + OperandSem_opnd_sem_uimm12x8_encode, OperandSem_opnd_sem_uimm12x8_decode, + 0, 0 }, + { "simm4", FIELD_mn, -1, 0, + 0, + OperandSem_opnd_sem_simm4_encode, OperandSem_opnd_sem_simm4_decode, + 0, 0 }, + { "arr", FIELD_r, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AR_encode, OperandSem_opnd_sem_AR_decode, + 0, 0 }, + { "ars", FIELD_s, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AR_encode, OperandSem_opnd_sem_AR_decode, + 0, 0 }, + { "*ars_invisible", FIELD_s, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_AR_encode, OperandSem_opnd_sem_AR_decode, + 0, 0 }, + { "art", FIELD_t, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AR_encode, OperandSem_opnd_sem_AR_decode, + 0, 0 }, + { "ar0", FIELD__ar0, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_AR_0_encode, OperandSem_opnd_sem_AR_0_decode, + 0, 0 }, + { "ar4", FIELD__ar4, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_AR_1_encode, OperandSem_opnd_sem_AR_1_decode, + 0, 0 }, + { "ar8", FIELD__ar8, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_AR_2_encode, OperandSem_opnd_sem_AR_2_decode, + 0, 0 }, + { "ar12", FIELD__ar12, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_AR_3_encode, OperandSem_opnd_sem_AR_3_decode, + 0, 0 }, + { "ars_entry", FIELD_s, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AR_4_encode, OperandSem_opnd_sem_AR_4_decode, + 0, 0 }, + { "immrx4", FIELD_r, -1, 0, + 0, + OperandSem_opnd_sem_immrx4_encode, OperandSem_opnd_sem_immrx4_decode, + 0, 0 }, + { "lsi4x4", FIELD_r, -1, 0, + 0, + OperandSem_opnd_sem_lsi4x4_encode, OperandSem_opnd_sem_lsi4x4_decode, + 0, 0 }, + { "simm7", FIELD_imm7, -1, 0, + 0, + OperandSem_opnd_sem_simm7_encode, OperandSem_opnd_sem_simm7_decode, + 0, 0 }, + { "uimm6", FIELD_imm6, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_uimm6_encode, OperandSem_opnd_sem_uimm6_decode, + Operand_uimm6_ator, Operand_uimm6_rtoa }, + { "ai4const", FIELD_t, -1, 0, + 0, + OperandSem_opnd_sem_ai4const_encode, OperandSem_opnd_sem_ai4const_decode, + 0, 0 }, + { "b4const", FIELD_r, -1, 0, + 0, + OperandSem_opnd_sem_b4const_encode, OperandSem_opnd_sem_b4const_decode, + 0, 0 }, + { "b4constu", FIELD_r, -1, 0, + 0, + OperandSem_opnd_sem_b4constu_encode, OperandSem_opnd_sem_b4constu_decode, + 0, 0 }, + { "uimm8", FIELD_imm8, -1, 0, + 0, + OperandSem_opnd_sem_uimm8_encode, OperandSem_opnd_sem_uimm8_decode, + 0, 0 }, + { "uimm8x2", FIELD_imm8, -1, 0, + 0, + OperandSem_opnd_sem_uimm8x2_encode, OperandSem_opnd_sem_uimm8x2_decode, + 0, 0 }, + { "uimm8x4", FIELD_imm8, -1, 0, + 0, + OperandSem_opnd_sem_uimm8x4_encode, OperandSem_opnd_sem_uimm8x4_decode, + 0, 0 }, + { "uimm4x16", FIELD_op2, -1, 0, + 0, + OperandSem_opnd_sem_uimm4x16_encode, OperandSem_opnd_sem_uimm4x16_decode, + 0, 0 }, + { "uimmrx4", FIELD_r, -1, 0, + 0, + OperandSem_opnd_sem_lsi4x4_encode, OperandSem_opnd_sem_lsi4x4_decode, + 0, 0 }, + { "simm8", FIELD_imm8, -1, 0, + 0, + OperandSem_opnd_sem_simm8_encode, OperandSem_opnd_sem_simm8_decode, + 0, 0 }, + { "simm8x256", FIELD_imm8, -1, 0, + 0, + OperandSem_opnd_sem_simm8x256_encode, OperandSem_opnd_sem_simm8x256_decode, + 0, 0 }, + { "simm12b", FIELD_imm12b, -1, 0, + 0, + OperandSem_opnd_sem_simm12b_encode, OperandSem_opnd_sem_simm12b_decode, + 0, 0 }, + { "msalp32", FIELD_sal, -1, 0, + 0, + OperandSem_opnd_sem_msalp32_encode, OperandSem_opnd_sem_msalp32_decode, + 0, 0 }, + { "op2p1", FIELD_op2, -1, 0, + 0, + OperandSem_opnd_sem_op2p1_encode, OperandSem_opnd_sem_op2p1_decode, + 0, 0 }, + { "label8", FIELD_imm8, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_label8_encode, OperandSem_opnd_sem_label8_decode, + Operand_label8_ator, Operand_label8_rtoa }, + { "ulabel8", FIELD_imm8, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_ulabel8_encode, OperandSem_opnd_sem_ulabel8_decode, + Operand_ulabel8_ator, Operand_ulabel8_rtoa }, + { "label12", FIELD_imm12, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_label12_encode, OperandSem_opnd_sem_label12_decode, + Operand_label12_ator, Operand_label12_rtoa }, + { "soffset", FIELD_offset, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_soffset_encode, OperandSem_opnd_sem_soffset_decode, + Operand_soffset_ator, Operand_soffset_rtoa }, + { "uimm16x4", FIELD_imm16, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_uimm16x4_encode, OperandSem_opnd_sem_uimm16x4_decode, + Operand_uimm16x4_ator, Operand_uimm16x4_rtoa }, + { "bbi", FIELD_bbi, -1, 0, + 0, + OperandSem_opnd_sem_bbi_encode, OperandSem_opnd_sem_bbi_decode, + 0, 0 }, + { "sae", FIELD_sae, -1, 0, + 0, + OperandSem_opnd_sem_bbi_encode, OperandSem_opnd_sem_bbi_decode, + 0, 0 }, + { "sas", FIELD_sas, -1, 0, + 0, + OperandSem_opnd_sem_bbi_encode, OperandSem_opnd_sem_bbi_decode, + 0, 0 }, + { "sargt", FIELD_sargt, -1, 0, + 0, + OperandSem_opnd_sem_bbi_encode, OperandSem_opnd_sem_bbi_decode, + 0, 0 }, + { "s", FIELD_s, -1, 0, + 0, + OperandSem_opnd_sem_s_encode, OperandSem_opnd_sem_s_decode, + 0, 0 }, + { "mx", FIELD_x, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_UNKNOWN, + OperandSem_opnd_sem_MR_encode, OperandSem_opnd_sem_MR_decode, + 0, 0 }, + { "my", FIELD_y, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_UNKNOWN, + OperandSem_opnd_sem_MR_0_encode, OperandSem_opnd_sem_MR_0_decode, + 0, 0 }, + { "mw", FIELD_w, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_MR_1_encode, OperandSem_opnd_sem_MR_1_decode, + 0, 0 }, + { "mr0", FIELD__mr0, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_MR_2_encode, OperandSem_opnd_sem_MR_2_decode, + 0, 0 }, + { "mr1", FIELD__mr1, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_MR_3_encode, OperandSem_opnd_sem_MR_3_decode, + 0, 0 }, + { "mr2", FIELD__mr2, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_MR_4_encode, OperandSem_opnd_sem_MR_4_decode, + 0, 0 }, + { "mr3", FIELD__mr3, REGFILE_MR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_MR_5_encode, OperandSem_opnd_sem_MR_5_decode, + 0, 0 }, + { "immt", FIELD_t, -1, 0, + 0, + OperandSem_opnd_sem_immt_encode, OperandSem_opnd_sem_immt_decode, + 0, 0 }, + { "imms", FIELD_s, -1, 0, + 0, + OperandSem_opnd_sem_immt_encode, OperandSem_opnd_sem_immt_decode, + 0, 0 }, + { "bt", FIELD_t, REGFILE_BR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR_encode, OperandSem_opnd_sem_BR_decode, + 0, 0 }, + { "bs", FIELD_s, REGFILE_BR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR_encode, OperandSem_opnd_sem_BR_decode, + 0, 0 }, + { "br", FIELD_r, REGFILE_BR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR_encode, OperandSem_opnd_sem_BR_decode, + 0, 0 }, + { "bt2", FIELD_t2, REGFILE_BR, 2, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR2_encode, OperandSem_opnd_sem_BR2_decode, + 0, 0 }, + { "bs2", FIELD_s2, REGFILE_BR, 2, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR2_encode, OperandSem_opnd_sem_BR2_decode, + 0, 0 }, + { "br2", FIELD_r2, REGFILE_BR, 2, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR2_encode, OperandSem_opnd_sem_BR2_decode, + 0, 0 }, + { "bt4", FIELD_t4, REGFILE_BR, 4, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR4_encode, OperandSem_opnd_sem_BR4_decode, + 0, 0 }, + { "bs4", FIELD_s4, REGFILE_BR, 4, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR4_encode, OperandSem_opnd_sem_BR4_decode, + 0, 0 }, + { "br4", FIELD_r4, REGFILE_BR, 4, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR4_encode, OperandSem_opnd_sem_BR4_decode, + 0, 0 }, + { "bt8", FIELD_t8, REGFILE_BR, 8, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR8_encode, OperandSem_opnd_sem_BR8_decode, + 0, 0 }, + { "bs8", FIELD_s8, REGFILE_BR, 8, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR8_encode, OperandSem_opnd_sem_BR8_decode, + 0, 0 }, + { "br8", FIELD_r8, REGFILE_BR, 8, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR8_encode, OperandSem_opnd_sem_BR8_decode, + 0, 0 }, + { "bt16", FIELD__bt16, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR16_encode, OperandSem_opnd_sem_BR16_decode, + 0, 0 }, + { "bs16", FIELD__bs16, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR16_encode, OperandSem_opnd_sem_BR16_decode, + 0, 0 }, + { "br16", FIELD__br16, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_BR16_encode, OperandSem_opnd_sem_BR16_decode, + 0, 0 }, + { "brall", FIELD__brall, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + OperandSem_opnd_sem_BR16_encode, OperandSem_opnd_sem_BR16_decode, + 0, 0 }, + { "tp7", FIELD_t, -1, 0, + 0, + OperandSem_opnd_sem_tp7_encode, OperandSem_opnd_sem_tp7_decode, + 0, 0 }, + { "xt_wbr15_label", FIELD_xt_wbr15_imm, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_xt_wbr15_label_encode, OperandSem_opnd_sem_xt_wbr15_label_decode, + Operand_xt_wbr15_label_ator, Operand_xt_wbr15_label_rtoa }, + { "xt_wbr18_label", FIELD_xt_wbr18_imm, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + OperandSem_opnd_sem_soffset_encode, OperandSem_opnd_sem_soffset_decode, + Operand_xt_wbr18_label_ator, Operand_xt_wbr18_label_rtoa }, + { "ae_samt32", FIELD_ftsf13, -1, 0, + 0, + OperandSem_opnd_sem_ae_samt32_encode, OperandSem_opnd_sem_ae_samt32_decode, + 0, 0 }, + { "pr0", FIELD_ftsf11, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_PR_encode, OperandSem_opnd_sem_AE_PR_decode, + 0, 0 }, + { "qr0", FIELD_ftsf12, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_QR_encode, OperandSem_opnd_sem_AE_QR_decode, + 0, 0 }, + { "mac_qr0", FIELD_ftsf12, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_QR_encode, OperandSem_opnd_sem_AE_QR_decode, + 0, 0 }, + { "ae_lsimm16", FIELD_t, -1, 0, + 0, + OperandSem_opnd_sem_ae_lsimm16_encode, OperandSem_opnd_sem_ae_lsimm16_decode, + 0, 0 }, + { "ae_lsimm32", FIELD_t, -1, 0, + 0, + OperandSem_opnd_sem_ae_lsimm32_encode, OperandSem_opnd_sem_ae_lsimm32_decode, + 0, 0 }, + { "ae_lsimm64", FIELD_t, -1, 0, + 0, + OperandSem_opnd_sem_ae_lsimm64_encode, OperandSem_opnd_sem_ae_lsimm64_decode, + 0, 0 }, + { "ae_samt64", FIELD_ae_samt_s_t, -1, 0, + 0, + OperandSem_opnd_sem_ae_samt64_encode, OperandSem_opnd_sem_ae_samt64_decode, + 0, 0 }, + { "ae_ohba", FIELD_ae_fld_ohba, -1, 0, + 0, + OperandSem_opnd_sem_ae_ohba_encode, OperandSem_opnd_sem_ae_ohba_decode, + 0, 0 }, + { "ae_ohba2", FIELD_ae_fld_ohba2, -1, 0, + 0, + OperandSem_opnd_sem_ae_ohba_encode, OperandSem_opnd_sem_ae_ohba_decode, + 0, 0 }, + { "pr", FIELD_ae_r20, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_PR_encode, OperandSem_opnd_sem_AE_PR_decode, + 0, 0 }, + { "cvt_pr", FIELD_ae_r20, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_PR_encode, OperandSem_opnd_sem_AE_PR_decode, + 0, 0 }, + { "qr0_rw", FIELD_ae_r10, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_QR_encode, OperandSem_opnd_sem_AE_QR_decode, + 0, 0 }, + { "mac_qr0_rw", FIELD_ae_r10, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_QR_encode, OperandSem_opnd_sem_AE_QR_decode, + 0, 0 }, + { "qr1_w", FIELD_ae_r32, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_QR_encode, OperandSem_opnd_sem_AE_QR_decode, + 0, 0 }, + { "mac_qr1_w", FIELD_ae_r32, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_QR_encode, OperandSem_opnd_sem_AE_QR_decode, + 0, 0 }, + { "ps", FIELD_ae_s20, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_PR_encode, OperandSem_opnd_sem_AE_PR_decode, + 0, 0 }, + { "alupppb_ps", FIELD_ae_s20, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + OperandSem_opnd_sem_AE_PR_encode, OperandSem_opnd_sem_AE_PR_decode, + 0, 0 }, + { "bitindex", FIELD_bitindex, -1, 0, + 0, + OperandSem_opnd_sem_bbi_encode, OperandSem_opnd_sem_bbi_decode, + 0, 0 }, + { "t", FIELD_t, -1, 0, 0, 0, 0, 0, 0 }, + { "bbi4", FIELD_bbi4, -1, 0, 0, 0, 0, 0, 0 }, + { "imm12", FIELD_imm12, -1, 0, 0, 0, 0, 0, 0 }, + { "imm8", FIELD_imm8, -1, 0, 0, 0, 0, 0, 0 }, + { "imm12b", FIELD_imm12b, -1, 0, 0, 0, 0, 0, 0 }, + { "imm16", FIELD_imm16, -1, 0, 0, 0, 0, 0, 0 }, + { "m", FIELD_m, -1, 0, 0, 0, 0, 0, 0 }, + { "n", FIELD_n, -1, 0, 0, 0, 0, 0, 0 }, + { "offset", FIELD_offset, -1, 0, 0, 0, 0, 0, 0 }, + { "op0", FIELD_op0, -1, 0, 0, 0, 0, 0, 0 }, + { "op1", FIELD_op1, -1, 0, 0, 0, 0, 0, 0 }, + { "op2", FIELD_op2, -1, 0, 0, 0, 0, 0, 0 }, + { "r", FIELD_r, -1, 0, 0, 0, 0, 0, 0 }, + { "sa4", FIELD_sa4, -1, 0, 0, 0, 0, 0, 0 }, + { "sae4", FIELD_sae4, -1, 0, 0, 0, 0, 0, 0 }, + { "sal", FIELD_sal, -1, 0, 0, 0, 0, 0, 0 }, + { "sas4", FIELD_sas4, -1, 0, 0, 0, 0, 0, 0 }, + { "sr", FIELD_sr, -1, 0, 0, 0, 0, 0, 0 }, + { "st", FIELD_st, -1, 0, 0, 0, 0, 0, 0 }, + { "thi3", FIELD_thi3, -1, 0, 0, 0, 0, 0, 0 }, + { "imm4", FIELD_imm4, -1, 0, 0, 0, 0, 0, 0 }, + { "mn", FIELD_mn, -1, 0, 0, 0, 0, 0, 0 }, + { "i", FIELD_i, -1, 0, 0, 0, 0, 0, 0 }, + { "imm6lo", FIELD_imm6lo, -1, 0, 0, 0, 0, 0, 0 }, + { "imm6hi", FIELD_imm6hi, -1, 0, 0, 0, 0, 0, 0 }, + { "imm7lo", FIELD_imm7lo, -1, 0, 0, 0, 0, 0, 0 }, + { "imm7hi", FIELD_imm7hi, -1, 0, 0, 0, 0, 0, 0 }, + { "z", FIELD_z, -1, 0, 0, 0, 0, 0, 0 }, + { "imm6", FIELD_imm6, -1, 0, 0, 0, 0, 0, 0 }, + { "imm7", FIELD_imm7, -1, 0, 0, 0, 0, 0, 0 }, + { "r3", FIELD_r3, -1, 0, 0, 0, 0, 0, 0 }, + { "rbit2", FIELD_rbit2, -1, 0, 0, 0, 0, 0, 0 }, + { "rhi", FIELD_rhi, -1, 0, 0, 0, 0, 0, 0 }, + { "t3", FIELD_t3, -1, 0, 0, 0, 0, 0, 0 }, + { "tbit2", FIELD_tbit2, -1, 0, 0, 0, 0, 0, 0 }, + { "tlo", FIELD_tlo, -1, 0, 0, 0, 0, 0, 0 }, + { "w", FIELD_w, -1, 0, 0, 0, 0, 0, 0 }, + { "y", FIELD_y, -1, 0, 0, 0, 0, 0, 0 }, + { "x", FIELD_x, -1, 0, 0, 0, 0, 0, 0 }, + { "t2", FIELD_t2, -1, 0, 0, 0, 0, 0, 0 }, + { "s2", FIELD_s2, -1, 0, 0, 0, 0, 0, 0 }, + { "r2", FIELD_r2, -1, 0, 0, 0, 0, 0, 0 }, + { "t4", FIELD_t4, -1, 0, 0, 0, 0, 0, 0 }, + { "s4", FIELD_s4, -1, 0, 0, 0, 0, 0, 0 }, + { "r4", FIELD_r4, -1, 0, 0, 0, 0, 0, 0 }, + { "t8", FIELD_t8, -1, 0, 0, 0, 0, 0, 0 }, + { "s8", FIELD_s8, -1, 0, 0, 0, 0, 0, 0 }, + { "r8", FIELD_r8, -1, 0, 0, 0, 0, 0, 0 }, + { "xt_wbr15_imm", FIELD_xt_wbr15_imm, -1, 0, 0, 0, 0, 0, 0 }, + { "xt_wbr18_imm", FIELD_xt_wbr18_imm, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r3", FIELD_ae_r3, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_s_non_samt", FIELD_ae_s_non_samt, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_s3", FIELD_ae_s3, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r32", FIELD_ae_r32, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_samt_s_t", FIELD_ae_samt_s_t, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r20", FIELD_ae_r20, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r10", FIELD_ae_r10, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_s20", FIELD_ae_s20, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_fld_ohba", FIELD_ae_fld_ohba, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_fld_ohba2", FIELD_ae_fld_ohba2, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s3", FIELD_op0_s3, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf11", FIELD_ftsf11, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf12", FIELD_ftsf12, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf13", FIELD_ftsf13, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf20ae_slot1", FIELD_ftsf20ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf21ae_slot1", FIELD_ftsf21ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf22ae_slot1", FIELD_ftsf22ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf23ae_slot1", FIELD_ftsf23ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf24ae_slot1", FIELD_ftsf24ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf25ae_slot1", FIELD_ftsf25ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf26ae_slot1", FIELD_ftsf26ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf27ae_slot1", FIELD_ftsf27ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf28ae_slot1", FIELD_ftsf28ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf29ae_slot1", FIELD_ftsf29ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf30ae_slot1", FIELD_ftsf30ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf31ae_slot1", FIELD_ftsf31ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf32ae_slot1", FIELD_ftsf32ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf33ae_slot1", FIELD_ftsf33ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf34ae_slot1", FIELD_ftsf34ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf35ae_slot1", FIELD_ftsf35ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf36ae_slot1", FIELD_ftsf36ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf37ae_slot1", FIELD_ftsf37ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf38ae_slot1", FIELD_ftsf38ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf40ae_slot1", FIELD_ftsf40ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf41ae_slot1", FIELD_ftsf41ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf42ae_slot1", FIELD_ftsf42ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf43ae_slot1", FIELD_ftsf43ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf45ae_slot1", FIELD_ftsf45ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf47ae_slot1", FIELD_ftsf47ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf48ae_slot1", FIELD_ftsf48ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf49ae_slot1", FIELD_ftsf49ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf50ae_slot1", FIELD_ftsf50ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf51ae_slot1", FIELD_ftsf51ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf52ae_slot1", FIELD_ftsf52ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf53ae_slot1", FIELD_ftsf53ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf54ae_slot1", FIELD_ftsf54ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf55", FIELD_ftsf55, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf56ae_slot1", FIELD_ftsf56ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf57ae_slot1", FIELD_ftsf57ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf58ae_slot1", FIELD_ftsf58ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf60ae_slot1", FIELD_ftsf60ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf61", FIELD_ftsf61, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf62ae_slot1", FIELD_ftsf62ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf63ae_slot1", FIELD_ftsf63ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf64ae_slot1", FIELD_ftsf64ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf66ae_slot1", FIELD_ftsf66ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf68ae_slot1", FIELD_ftsf68ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf69ae_slot1", FIELD_ftsf69ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf70ae_slot1", FIELD_ftsf70ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf71ae_slot1", FIELD_ftsf71ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf72ae_slot1", FIELD_ftsf72ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf73ae_slot1", FIELD_ftsf73ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf74ae_slot1", FIELD_ftsf74ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf75ae_slot1", FIELD_ftsf75ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf76ae_slot1", FIELD_ftsf76ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf77ae_slot1", FIELD_ftsf77ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf78ae_slot1", FIELD_ftsf78ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf79ae_slot1", FIELD_ftsf79ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf80ae_slot1", FIELD_ftsf80ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf81ae_slot1", FIELD_ftsf81ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf82ae_slot1", FIELD_ftsf82ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf83ae_slot1", FIELD_ftsf83ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf84ae_slot1", FIELD_ftsf84ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf85ae_slot1", FIELD_ftsf85ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf86ae_slot1", FIELD_ftsf86ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf87ae_slot1", FIELD_ftsf87ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf88ae_slot1", FIELD_ftsf88ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf89ae_slot1", FIELD_ftsf89ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf90ae_slot1", FIELD_ftsf90ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf91", FIELD_ftsf91, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf92ae_slot1", FIELD_ftsf92ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf93ae_slot1", FIELD_ftsf93ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf94ae_slot1", FIELD_ftsf94ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf96ae_slot1", FIELD_ftsf96ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf97ae_slot1", FIELD_ftsf97ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf99ae_slot1", FIELD_ftsf99ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf101ae_slot1", FIELD_ftsf101ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf102ae_slot1", FIELD_ftsf102ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf103ae_slot1", FIELD_ftsf103ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf106ae_slot1", FIELD_ftsf106ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf107ae_slot1", FIELD_ftsf107ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf108ae_slot1", FIELD_ftsf108ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf109ae_slot1", FIELD_ftsf109ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf110ae_slot1", FIELD_ftsf110ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf111ae_slot1", FIELD_ftsf111ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf112ae_slot1", FIELD_ftsf112ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf113ae_slot1", FIELD_ftsf113ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf114ae_slot1", FIELD_ftsf114ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf115ae_slot1", FIELD_ftsf115ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf116ae_slot1", FIELD_ftsf116ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf117ae_slot1", FIELD_ftsf117ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf118ae_slot1", FIELD_ftsf118ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf120ae_slot1", FIELD_ftsf120ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf121ae_slot1", FIELD_ftsf121ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf123ae_slot1", FIELD_ftsf123ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf124ae_slot1", FIELD_ftsf124ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf125ae_slot1", FIELD_ftsf125ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf126ae_slot1", FIELD_ftsf126ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf127ae_slot1", FIELD_ftsf127ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf128ae_slot1", FIELD_ftsf128ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf129ae_slot1", FIELD_ftsf129ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf130ae_slot1", FIELD_ftsf130ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf131ae_slot1", FIELD_ftsf131ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf132ae_slot1", FIELD_ftsf132ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf133ae_slot1", FIELD_ftsf133ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf134ae_slot1", FIELD_ftsf134ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf135ae_slot1", FIELD_ftsf135ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf136ae_slot1", FIELD_ftsf136ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf137ae_slot1", FIELD_ftsf137ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf138ae_slot1", FIELD_ftsf138ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf139ae_slot1", FIELD_ftsf139ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf140ae_slot1", FIELD_ftsf140ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf141ae_slot1", FIELD_ftsf141ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf142ae_slot1", FIELD_ftsf142ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf143ae_slot1", FIELD_ftsf143ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf144ae_slot1", FIELD_ftsf144ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf145ae_slot1", FIELD_ftsf145ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf146ae_slot1", FIELD_ftsf146ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf147ae_slot1", FIELD_ftsf147ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf148ae_slot1", FIELD_ftsf148ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf149ae_slot1", FIELD_ftsf149ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf150ae_slot1", FIELD_ftsf150ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf151ae_slot1", FIELD_ftsf151ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf152ae_slot1", FIELD_ftsf152ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf153ae_slot1", FIELD_ftsf153ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf154ae_slot1", FIELD_ftsf154ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf155ae_slot1", FIELD_ftsf155ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf156ae_slot1", FIELD_ftsf156ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf157ae_slot1", FIELD_ftsf157ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf158ae_slot1", FIELD_ftsf158ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf159ae_slot1", FIELD_ftsf159ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf160ae_slot1", FIELD_ftsf160ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf161ae_slot1", FIELD_ftsf161ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf162ae_slot1", FIELD_ftsf162ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf163ae_slot1", FIELD_ftsf163ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf164ae_slot1", FIELD_ftsf164ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf165ae_slot1", FIELD_ftsf165ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf166ae_slot1", FIELD_ftsf166ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf167ae_slot1", FIELD_ftsf167ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf168ae_slot1", FIELD_ftsf168ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf169ae_slot1", FIELD_ftsf169ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf170ae_slot1", FIELD_ftsf170ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf171ae_slot1", FIELD_ftsf171ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf172ae_slot1", FIELD_ftsf172ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf173ae_slot1", FIELD_ftsf173ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf174ae_slot1", FIELD_ftsf174ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf175ae_slot1", FIELD_ftsf175ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf176ae_slot1", FIELD_ftsf176ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf177ae_slot1", FIELD_ftsf177ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf178ae_slot1", FIELD_ftsf178ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf179ae_slot1", FIELD_ftsf179ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf180ae_slot1", FIELD_ftsf180ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf181ae_slot1", FIELD_ftsf181ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf182ae_slot1", FIELD_ftsf182ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf183ae_slot1", FIELD_ftsf183ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf184ae_slot1", FIELD_ftsf184ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf185ae_slot1", FIELD_ftsf185ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf186ae_slot1", FIELD_ftsf186ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf187ae_slot1", FIELD_ftsf187ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf188ae_slot1", FIELD_ftsf188ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf189ae_slot1", FIELD_ftsf189ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf190ae_slot1", FIELD_ftsf190ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf191ae_slot1", FIELD_ftsf191ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf192ae_slot1", FIELD_ftsf192ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf193ae_slot1", FIELD_ftsf193ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf194ae_slot1", FIELD_ftsf194ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf195ae_slot1", FIELD_ftsf195ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf196ae_slot1", FIELD_ftsf196ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf197ae_slot1", FIELD_ftsf197ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf198ae_slot1", FIELD_ftsf198ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf199ae_slot1", FIELD_ftsf199ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf200ae_slot1", FIELD_ftsf200ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf201ae_slot1", FIELD_ftsf201ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf202ae_slot1", FIELD_ftsf202ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf203ae_slot1", FIELD_ftsf203ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf204ae_slot1", FIELD_ftsf204ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf205ae_slot1", FIELD_ftsf205ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf206ae_slot1", FIELD_ftsf206ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf207ae_slot1", FIELD_ftsf207ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf208ae_slot1", FIELD_ftsf208ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf210ae_slot1", FIELD_ftsf210ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf333ae_slot1", FIELD_ftsf333ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf334ae_slot1", FIELD_ftsf334ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf335", FIELD_ftsf335, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf336ae_slot1", FIELD_ftsf336ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf337ae_slot1", FIELD_ftsf337ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf339ae_slot1", FIELD_ftsf339ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf340ae_slot1", FIELD_ftsf340ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf341ae_slot1", FIELD_ftsf341ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf342ae_slot1", FIELD_ftsf342ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf343ae_slot1", FIELD_ftsf343ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf344ae_slot1", FIELD_ftsf344ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf345ae_slot1", FIELD_ftsf345ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf347ae_slot1", FIELD_ftsf347ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf348ae_slot1", FIELD_ftsf348ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf349ae_slot1", FIELD_ftsf349ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf350ae_slot1", FIELD_ftsf350ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf351", FIELD_ftsf351, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf352ae_slot1", FIELD_ftsf352ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf354ae_slot1", FIELD_ftsf354ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf355ae_slot1", FIELD_ftsf355ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf356ae_slot1", FIELD_ftsf356ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf357ae_slot1", FIELD_ftsf357ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf358ae_slot1", FIELD_ftsf358ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf359ae_slot1", FIELD_ftsf359ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s4", FIELD_op0_s4, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf211ae_slot0", FIELD_ftsf211ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf212ae_slot0", FIELD_ftsf212ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf213ae_slot0", FIELD_ftsf213ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf214ae_slot0", FIELD_ftsf214ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf215ae_slot0", FIELD_ftsf215ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf217ae_slot0", FIELD_ftsf217ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf218ae_slot0", FIELD_ftsf218ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf219ae_slot0", FIELD_ftsf219ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf220ae_slot0", FIELD_ftsf220ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf221ae_slot0", FIELD_ftsf221ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf222ae_slot0", FIELD_ftsf222ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf223ae_slot0", FIELD_ftsf223ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf224ae_slot0", FIELD_ftsf224ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf225ae_slot0", FIELD_ftsf225ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf226ae_slot0", FIELD_ftsf226ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf227ae_slot0", FIELD_ftsf227ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf228ae_slot0", FIELD_ftsf228ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf229ae_slot0", FIELD_ftsf229ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf230ae_slot0", FIELD_ftsf230ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf231ae_slot0", FIELD_ftsf231ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf232ae_slot0", FIELD_ftsf232ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf233ae_slot0", FIELD_ftsf233ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf234ae_slot0", FIELD_ftsf234ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf235ae_slot0", FIELD_ftsf235ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf236ae_slot0", FIELD_ftsf236ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf237ae_slot0", FIELD_ftsf237ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf238ae_slot0", FIELD_ftsf238ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf239ae_slot0", FIELD_ftsf239ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf240ae_slot0", FIELD_ftsf240ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf241ae_slot0", FIELD_ftsf241ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf242ae_slot0", FIELD_ftsf242ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf243ae_slot0", FIELD_ftsf243ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf244ae_slot0", FIELD_ftsf244ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf245ae_slot0", FIELD_ftsf245ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf246ae_slot0", FIELD_ftsf246ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf247ae_slot0", FIELD_ftsf247ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf248ae_slot0", FIELD_ftsf248ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf249ae_slot0", FIELD_ftsf249ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf250ae_slot0", FIELD_ftsf250ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf251ae_slot0", FIELD_ftsf251ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf252ae_slot0", FIELD_ftsf252ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf253ae_slot0", FIELD_ftsf253ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf254ae_slot0", FIELD_ftsf254ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf255ae_slot0", FIELD_ftsf255ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf256ae_slot0", FIELD_ftsf256ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf257ae_slot0", FIELD_ftsf257ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf258ae_slot0", FIELD_ftsf258ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf259ae_slot0", FIELD_ftsf259ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf260ae_slot0", FIELD_ftsf260ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf261ae_slot0", FIELD_ftsf261ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf262ae_slot0", FIELD_ftsf262ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf263ae_slot0", FIELD_ftsf263ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf264ae_slot0", FIELD_ftsf264ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf265ae_slot0", FIELD_ftsf265ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf266ae_slot0", FIELD_ftsf266ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf267ae_slot0", FIELD_ftsf267ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf268ae_slot0", FIELD_ftsf268ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf269ae_slot0", FIELD_ftsf269ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf270ae_slot0", FIELD_ftsf270ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf271ae_slot0", FIELD_ftsf271ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf272ae_slot0", FIELD_ftsf272ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf273ae_slot0", FIELD_ftsf273ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf274ae_slot0", FIELD_ftsf274ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf275ae_slot0", FIELD_ftsf275ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf276ae_slot0", FIELD_ftsf276ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf277ae_slot0", FIELD_ftsf277ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf278ae_slot0", FIELD_ftsf278ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf279ae_slot0", FIELD_ftsf279ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf280", FIELD_ftsf280, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf281ae_slot0", FIELD_ftsf281ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf282ae_slot0", FIELD_ftsf282ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf284ae_slot0", FIELD_ftsf284ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf285ae_slot0", FIELD_ftsf285ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf287ae_slot0", FIELD_ftsf287ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf288", FIELD_ftsf288, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf289ae_slot0", FIELD_ftsf289ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf290ae_slot0", FIELD_ftsf290ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf291ae_slot0", FIELD_ftsf291ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf292ae_slot0", FIELD_ftsf292ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf293ae_slot0", FIELD_ftsf293ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf294ae_slot0", FIELD_ftsf294ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf295ae_slot0", FIELD_ftsf295ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf296ae_slot0", FIELD_ftsf296ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf297ae_slot0", FIELD_ftsf297ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf298ae_slot0", FIELD_ftsf298ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf300ae_slot0", FIELD_ftsf300ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf302ae_slot0", FIELD_ftsf302ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf304ae_slot0", FIELD_ftsf304ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf305ae_slot0", FIELD_ftsf305ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf306ae_slot0", FIELD_ftsf306ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf307ae_slot0", FIELD_ftsf307ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf308ae_slot0", FIELD_ftsf308ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf309", FIELD_ftsf309, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf310ae_slot0", FIELD_ftsf310ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf311ae_slot0", FIELD_ftsf311ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf312ae_slot0", FIELD_ftsf312ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf313ae_slot0", FIELD_ftsf313ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf314ae_slot0", FIELD_ftsf314ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf315", FIELD_ftsf315, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf316ae_slot0", FIELD_ftsf316ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf317ae_slot0", FIELD_ftsf317ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf319ae_slot0", FIELD_ftsf319ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf320ae_slot0", FIELD_ftsf320ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf322ae_slot0", FIELD_ftsf322ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf323ae_slot0", FIELD_ftsf323ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf324ae_slot0", FIELD_ftsf324ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf325ae_slot0", FIELD_ftsf325ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf326ae_slot0", FIELD_ftsf326ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf327ae_slot0", FIELD_ftsf327ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf328ae_slot0", FIELD_ftsf328ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf329ae_slot0", FIELD_ftsf329ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf360ae_slot0", FIELD_ftsf360ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf361ae_slot0", FIELD_ftsf361ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf362", FIELD_ftsf362, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf363ae_slot0", FIELD_ftsf363ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf364ae_slot0", FIELD_ftsf364ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf366ae_slot0", FIELD_ftsf366ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf368ae_slot0", FIELD_ftsf368ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf370ae_slot0", FIELD_ftsf370ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf373ae_slot0", FIELD_ftsf373ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf376ae_slot0", FIELD_ftsf376ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf378ae_slot0", FIELD_ftsf378ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf379ae_slot0", FIELD_ftsf379ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf382ae_slot0", FIELD_ftsf382ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf383ae_slot0", FIELD_ftsf383ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf384ae_slot0", FIELD_ftsf384ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf386ae_slot0", FIELD_ftsf386ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf387ae_slot0", FIELD_ftsf387ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf388ae_slot0", FIELD_ftsf388ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf389ae_slot0", FIELD_ftsf389ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_mul32x24fld", FIELD_ae_mul32x24fld, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s4_s4", FIELD_op0_s4_s4, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld28", FIELD_combined2c0b5f72_fld28, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld37", FIELD_combined2c0b5f72_fld37, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld39", FIELD_combined2c0b5f72_fld39, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld40", FIELD_combined2c0b5f72_fld40, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld46", FIELD_combined2c0b5f72_fld46, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld47", FIELD_combined2c0b5f72_fld47, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld49", FIELD_combined2c0b5f72_fld49, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld50", FIELD_combined2c0b5f72_fld50, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld52", FIELD_combined2c0b5f72_fld52, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld121", FIELD_combined2c0b5f72_fld121, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld123", FIELD_combined2c0b5f72_fld123, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld127", FIELD_combined2c0b5f72_fld127, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld133ae_slot0", FIELD_combined2c0b5f72_fld133ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld134ae_slot0", FIELD_combined2c0b5f72_fld134ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld135ae_slot0", FIELD_combined2c0b5f72_fld135ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld136ae_slot0", FIELD_combined2c0b5f72_fld136ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld137ae_slot0", FIELD_combined2c0b5f72_fld137ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld138ae_slot0", FIELD_combined2c0b5f72_fld138ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld139ae_slot0", FIELD_combined2c0b5f72_fld139ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld140ae_slot0", FIELD_combined2c0b5f72_fld140ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld141ae_slot0", FIELD_combined2c0b5f72_fld141ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld142ae_slot0", FIELD_combined2c0b5f72_fld142ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld143ae_slot0", FIELD_combined2c0b5f72_fld143ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld144ae_slot0", FIELD_combined2c0b5f72_fld144ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld145ae_slot0", FIELD_combined2c0b5f72_fld145ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld146ae_slot0", FIELD_combined2c0b5f72_fld146ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld148ae_slot0", FIELD_combined2c0b5f72_fld148ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld149ae_slot0", FIELD_combined2c0b5f72_fld149ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s4_s4_s4", FIELD_op0_s4_s4_s4, -1, 0, 0, 0, 0, 0, 0 }, + { "combined1e9fefee_fld96", FIELD_combined1e9fefee_fld96, -1, 0, 0, 0, 0, 0, 0 }, + { "combined1e9fefee_fld98", FIELD_combined1e9fefee_fld98, -1, 0, 0, 0, 0, 0, 0 }, + { "combined1e9fefee_fld106ae_slot0", FIELD_combined1e9fefee_fld106ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined1e9fefee_fld107ae_slot0", FIELD_combined1e9fefee_fld107ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined1e9fefee_fld108ae_slot0", FIELD_combined1e9fefee_fld108ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "combined1e9fefee_fld109ae_slot0", FIELD_combined1e9fefee_fld109ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s3_s3", FIELD_op0_s3_s3, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld19", FIELD_combined2c0b5f72_fld19, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld22", FIELD_combined2c0b5f72_fld22, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld24", FIELD_combined2c0b5f72_fld24, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld65", FIELD_combined2c0b5f72_fld65, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld66", FIELD_combined2c0b5f72_fld66, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld68", FIELD_combined2c0b5f72_fld68, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld69", FIELD_combined2c0b5f72_fld69, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld74", FIELD_combined2c0b5f72_fld74, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld79", FIELD_combined2c0b5f72_fld79, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld88", FIELD_combined2c0b5f72_fld88, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld90", FIELD_combined2c0b5f72_fld90, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld91", FIELD_combined2c0b5f72_fld91, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld131ae_slot1", FIELD_combined2c0b5f72_fld131ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld132ae_slot1", FIELD_combined2c0b5f72_fld132ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "combined2c0b5f72_fld147ae_slot1", FIELD_combined2c0b5f72_fld147ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "s3to1", FIELD_s3to1, -1, 0, 0, 0, 0, 0, 0 } +}; + +enum xtensa_operand_id { + OPERAND_soffsetx4, + OPERAND_uimm12x8, + OPERAND_simm4, + OPERAND_arr, + OPERAND_ars, + OPERAND__ars_invisible, + OPERAND_art, + OPERAND_ar0, + OPERAND_ar4, + OPERAND_ar8, + OPERAND_ar12, + OPERAND_ars_entry, + OPERAND_immrx4, + OPERAND_lsi4x4, + OPERAND_simm7, + OPERAND_uimm6, + OPERAND_ai4const, + OPERAND_b4const, + OPERAND_b4constu, + OPERAND_uimm8, + OPERAND_uimm8x2, + OPERAND_uimm8x4, + OPERAND_uimm4x16, + OPERAND_uimmrx4, + OPERAND_simm8, + OPERAND_simm8x256, + OPERAND_simm12b, + OPERAND_msalp32, + OPERAND_op2p1, + OPERAND_label8, + OPERAND_ulabel8, + OPERAND_label12, + OPERAND_soffset, + OPERAND_uimm16x4, + OPERAND_bbi, + OPERAND_sae, + OPERAND_sas, + OPERAND_sargt, + OPERAND_s, + OPERAND_mx, + OPERAND_my, + OPERAND_mw, + OPERAND_mr0, + OPERAND_mr1, + OPERAND_mr2, + OPERAND_mr3, + OPERAND_immt, + OPERAND_imms, + OPERAND_bt, + OPERAND_bs, + OPERAND_br, + OPERAND_bt2, + OPERAND_bs2, + OPERAND_br2, + OPERAND_bt4, + OPERAND_bs4, + OPERAND_br4, + OPERAND_bt8, + OPERAND_bs8, + OPERAND_br8, + OPERAND_bt16, + OPERAND_bs16, + OPERAND_br16, + OPERAND_brall, + OPERAND_tp7, + OPERAND_xt_wbr15_label, + OPERAND_xt_wbr18_label, + OPERAND_ae_samt32, + OPERAND_pr0, + OPERAND_qr0, + OPERAND_mac_qr0, + OPERAND_ae_lsimm16, + OPERAND_ae_lsimm32, + OPERAND_ae_lsimm64, + OPERAND_ae_samt64, + OPERAND_ae_ohba, + OPERAND_ae_ohba2, + OPERAND_pr, + OPERAND_cvt_pr, + OPERAND_qr0_rw, + OPERAND_mac_qr0_rw, + OPERAND_qr1_w, + OPERAND_mac_qr1_w, + OPERAND_ps, + OPERAND_alupppb_ps, + OPERAND_bitindex, + OPERAND_t, + OPERAND_bbi4, + OPERAND_imm12, + OPERAND_imm8, + OPERAND_imm12b, + OPERAND_imm16, + OPERAND_m, + OPERAND_n, + OPERAND_offset, + OPERAND_op0, + OPERAND_op1, + OPERAND_op2, + OPERAND_r, + OPERAND_sa4, + OPERAND_sae4, + OPERAND_sal, + OPERAND_sas4, + OPERAND_sr, + OPERAND_st, + OPERAND_thi3, + OPERAND_imm4, + OPERAND_mn, + OPERAND_i, + OPERAND_imm6lo, + OPERAND_imm6hi, + OPERAND_imm7lo, + OPERAND_imm7hi, + OPERAND_z, + OPERAND_imm6, + OPERAND_imm7, + OPERAND_r3, + OPERAND_rbit2, + OPERAND_rhi, + OPERAND_t3, + OPERAND_tbit2, + OPERAND_tlo, + OPERAND_w, + OPERAND_y, + OPERAND_x, + OPERAND_t2, + OPERAND_s2, + OPERAND_r2, + OPERAND_t4, + OPERAND_s4, + OPERAND_r4, + OPERAND_t8, + OPERAND_s8, + OPERAND_r8, + OPERAND_xt_wbr15_imm, + OPERAND_xt_wbr18_imm, + OPERAND_ae_r3, + OPERAND_ae_s_non_samt, + OPERAND_ae_s3, + OPERAND_ae_r32, + OPERAND_ae_samt_s_t, + OPERAND_ae_r20, + OPERAND_ae_r10, + OPERAND_ae_s20, + OPERAND_ae_fld_ohba, + OPERAND_ae_fld_ohba2, + OPERAND_op0_s3, + OPERAND_ftsf11, + OPERAND_ftsf12, + OPERAND_ftsf13, + OPERAND_ftsf20ae_slot1, + OPERAND_ftsf21ae_slot1, + OPERAND_ftsf22ae_slot1, + OPERAND_ftsf23ae_slot1, + OPERAND_ftsf24ae_slot1, + OPERAND_ftsf25ae_slot1, + OPERAND_ftsf26ae_slot1, + OPERAND_ftsf27ae_slot1, + OPERAND_ftsf28ae_slot1, + OPERAND_ftsf29ae_slot1, + OPERAND_ftsf30ae_slot1, + OPERAND_ftsf31ae_slot1, + OPERAND_ftsf32ae_slot1, + OPERAND_ftsf33ae_slot1, + OPERAND_ftsf34ae_slot1, + OPERAND_ftsf35ae_slot1, + OPERAND_ftsf36ae_slot1, + OPERAND_ftsf37ae_slot1, + OPERAND_ftsf38ae_slot1, + OPERAND_ftsf40ae_slot1, + OPERAND_ftsf41ae_slot1, + OPERAND_ftsf42ae_slot1, + OPERAND_ftsf43ae_slot1, + OPERAND_ftsf45ae_slot1, + OPERAND_ftsf47ae_slot1, + OPERAND_ftsf48ae_slot1, + OPERAND_ftsf49ae_slot1, + OPERAND_ftsf50ae_slot1, + OPERAND_ftsf51ae_slot1, + OPERAND_ftsf52ae_slot1, + OPERAND_ftsf53ae_slot1, + OPERAND_ftsf54ae_slot1, + OPERAND_ftsf55, + OPERAND_ftsf56ae_slot1, + OPERAND_ftsf57ae_slot1, + OPERAND_ftsf58ae_slot1, + OPERAND_ftsf60ae_slot1, + OPERAND_ftsf61, + OPERAND_ftsf62ae_slot1, + OPERAND_ftsf63ae_slot1, + OPERAND_ftsf64ae_slot1, + OPERAND_ftsf66ae_slot1, + OPERAND_ftsf68ae_slot1, + OPERAND_ftsf69ae_slot1, + OPERAND_ftsf70ae_slot1, + OPERAND_ftsf71ae_slot1, + OPERAND_ftsf72ae_slot1, + OPERAND_ftsf73ae_slot1, + OPERAND_ftsf74ae_slot1, + OPERAND_ftsf75ae_slot1, + OPERAND_ftsf76ae_slot1, + OPERAND_ftsf77ae_slot1, + OPERAND_ftsf78ae_slot1, + OPERAND_ftsf79ae_slot1, + OPERAND_ftsf80ae_slot1, + OPERAND_ftsf81ae_slot1, + OPERAND_ftsf82ae_slot1, + OPERAND_ftsf83ae_slot1, + OPERAND_ftsf84ae_slot1, + OPERAND_ftsf85ae_slot1, + OPERAND_ftsf86ae_slot1, + OPERAND_ftsf87ae_slot1, + OPERAND_ftsf88ae_slot1, + OPERAND_ftsf89ae_slot1, + OPERAND_ftsf90ae_slot1, + OPERAND_ftsf91, + OPERAND_ftsf92ae_slot1, + OPERAND_ftsf93ae_slot1, + OPERAND_ftsf94ae_slot1, + OPERAND_ftsf96ae_slot1, + OPERAND_ftsf97ae_slot1, + OPERAND_ftsf99ae_slot1, + OPERAND_ftsf101ae_slot1, + OPERAND_ftsf102ae_slot1, + OPERAND_ftsf103ae_slot1, + OPERAND_ftsf106ae_slot1, + OPERAND_ftsf107ae_slot1, + OPERAND_ftsf108ae_slot1, + OPERAND_ftsf109ae_slot1, + OPERAND_ftsf110ae_slot1, + OPERAND_ftsf111ae_slot1, + OPERAND_ftsf112ae_slot1, + OPERAND_ftsf113ae_slot1, + OPERAND_ftsf114ae_slot1, + OPERAND_ftsf115ae_slot1, + OPERAND_ftsf116ae_slot1, + OPERAND_ftsf117ae_slot1, + OPERAND_ftsf118ae_slot1, + OPERAND_ftsf120ae_slot1, + OPERAND_ftsf121ae_slot1, + OPERAND_ftsf123ae_slot1, + OPERAND_ftsf124ae_slot1, + OPERAND_ftsf125ae_slot1, + OPERAND_ftsf126ae_slot1, + OPERAND_ftsf127ae_slot1, + OPERAND_ftsf128ae_slot1, + OPERAND_ftsf129ae_slot1, + OPERAND_ftsf130ae_slot1, + OPERAND_ftsf131ae_slot1, + OPERAND_ftsf132ae_slot1, + OPERAND_ftsf133ae_slot1, + OPERAND_ftsf134ae_slot1, + OPERAND_ftsf135ae_slot1, + OPERAND_ftsf136ae_slot1, + OPERAND_ftsf137ae_slot1, + OPERAND_ftsf138ae_slot1, + OPERAND_ftsf139ae_slot1, + OPERAND_ftsf140ae_slot1, + OPERAND_ftsf141ae_slot1, + OPERAND_ftsf142ae_slot1, + OPERAND_ftsf143ae_slot1, + OPERAND_ftsf144ae_slot1, + OPERAND_ftsf145ae_slot1, + OPERAND_ftsf146ae_slot1, + OPERAND_ftsf147ae_slot1, + OPERAND_ftsf148ae_slot1, + OPERAND_ftsf149ae_slot1, + OPERAND_ftsf150ae_slot1, + OPERAND_ftsf151ae_slot1, + OPERAND_ftsf152ae_slot1, + OPERAND_ftsf153ae_slot1, + OPERAND_ftsf154ae_slot1, + OPERAND_ftsf155ae_slot1, + OPERAND_ftsf156ae_slot1, + OPERAND_ftsf157ae_slot1, + OPERAND_ftsf158ae_slot1, + OPERAND_ftsf159ae_slot1, + OPERAND_ftsf160ae_slot1, + OPERAND_ftsf161ae_slot1, + OPERAND_ftsf162ae_slot1, + OPERAND_ftsf163ae_slot1, + OPERAND_ftsf164ae_slot1, + OPERAND_ftsf165ae_slot1, + OPERAND_ftsf166ae_slot1, + OPERAND_ftsf167ae_slot1, + OPERAND_ftsf168ae_slot1, + OPERAND_ftsf169ae_slot1, + OPERAND_ftsf170ae_slot1, + OPERAND_ftsf171ae_slot1, + OPERAND_ftsf172ae_slot1, + OPERAND_ftsf173ae_slot1, + OPERAND_ftsf174ae_slot1, + OPERAND_ftsf175ae_slot1, + OPERAND_ftsf176ae_slot1, + OPERAND_ftsf177ae_slot1, + OPERAND_ftsf178ae_slot1, + OPERAND_ftsf179ae_slot1, + OPERAND_ftsf180ae_slot1, + OPERAND_ftsf181ae_slot1, + OPERAND_ftsf182ae_slot1, + OPERAND_ftsf183ae_slot1, + OPERAND_ftsf184ae_slot1, + OPERAND_ftsf185ae_slot1, + OPERAND_ftsf186ae_slot1, + OPERAND_ftsf187ae_slot1, + OPERAND_ftsf188ae_slot1, + OPERAND_ftsf189ae_slot1, + OPERAND_ftsf190ae_slot1, + OPERAND_ftsf191ae_slot1, + OPERAND_ftsf192ae_slot1, + OPERAND_ftsf193ae_slot1, + OPERAND_ftsf194ae_slot1, + OPERAND_ftsf195ae_slot1, + OPERAND_ftsf196ae_slot1, + OPERAND_ftsf197ae_slot1, + OPERAND_ftsf198ae_slot1, + OPERAND_ftsf199ae_slot1, + OPERAND_ftsf200ae_slot1, + OPERAND_ftsf201ae_slot1, + OPERAND_ftsf202ae_slot1, + OPERAND_ftsf203ae_slot1, + OPERAND_ftsf204ae_slot1, + OPERAND_ftsf205ae_slot1, + OPERAND_ftsf206ae_slot1, + OPERAND_ftsf207ae_slot1, + OPERAND_ftsf208ae_slot1, + OPERAND_ftsf210ae_slot1, + OPERAND_ftsf333ae_slot1, + OPERAND_ftsf334ae_slot1, + OPERAND_ftsf335, + OPERAND_ftsf336ae_slot1, + OPERAND_ftsf337ae_slot1, + OPERAND_ftsf339ae_slot1, + OPERAND_ftsf340ae_slot1, + OPERAND_ftsf341ae_slot1, + OPERAND_ftsf342ae_slot1, + OPERAND_ftsf343ae_slot1, + OPERAND_ftsf344ae_slot1, + OPERAND_ftsf345ae_slot1, + OPERAND_ftsf347ae_slot1, + OPERAND_ftsf348ae_slot1, + OPERAND_ftsf349ae_slot1, + OPERAND_ftsf350ae_slot1, + OPERAND_ftsf351, + OPERAND_ftsf352ae_slot1, + OPERAND_ftsf354ae_slot1, + OPERAND_ftsf355ae_slot1, + OPERAND_ftsf356ae_slot1, + OPERAND_ftsf357ae_slot1, + OPERAND_ftsf358ae_slot1, + OPERAND_ftsf359ae_slot1, + OPERAND_op0_s4, + OPERAND_ftsf211ae_slot0, + OPERAND_ftsf212ae_slot0, + OPERAND_ftsf213ae_slot0, + OPERAND_ftsf214ae_slot0, + OPERAND_ftsf215ae_slot0, + OPERAND_ftsf217ae_slot0, + OPERAND_ftsf218ae_slot0, + OPERAND_ftsf219ae_slot0, + OPERAND_ftsf220ae_slot0, + OPERAND_ftsf221ae_slot0, + OPERAND_ftsf222ae_slot0, + OPERAND_ftsf223ae_slot0, + OPERAND_ftsf224ae_slot0, + OPERAND_ftsf225ae_slot0, + OPERAND_ftsf226ae_slot0, + OPERAND_ftsf227ae_slot0, + OPERAND_ftsf228ae_slot0, + OPERAND_ftsf229ae_slot0, + OPERAND_ftsf230ae_slot0, + OPERAND_ftsf231ae_slot0, + OPERAND_ftsf232ae_slot0, + OPERAND_ftsf233ae_slot0, + OPERAND_ftsf234ae_slot0, + OPERAND_ftsf235ae_slot0, + OPERAND_ftsf236ae_slot0, + OPERAND_ftsf237ae_slot0, + OPERAND_ftsf238ae_slot0, + OPERAND_ftsf239ae_slot0, + OPERAND_ftsf240ae_slot0, + OPERAND_ftsf241ae_slot0, + OPERAND_ftsf242ae_slot0, + OPERAND_ftsf243ae_slot0, + OPERAND_ftsf244ae_slot0, + OPERAND_ftsf245ae_slot0, + OPERAND_ftsf246ae_slot0, + OPERAND_ftsf247ae_slot0, + OPERAND_ftsf248ae_slot0, + OPERAND_ftsf249ae_slot0, + OPERAND_ftsf250ae_slot0, + OPERAND_ftsf251ae_slot0, + OPERAND_ftsf252ae_slot0, + OPERAND_ftsf253ae_slot0, + OPERAND_ftsf254ae_slot0, + OPERAND_ftsf255ae_slot0, + OPERAND_ftsf256ae_slot0, + OPERAND_ftsf257ae_slot0, + OPERAND_ftsf258ae_slot0, + OPERAND_ftsf259ae_slot0, + OPERAND_ftsf260ae_slot0, + OPERAND_ftsf261ae_slot0, + OPERAND_ftsf262ae_slot0, + OPERAND_ftsf263ae_slot0, + OPERAND_ftsf264ae_slot0, + OPERAND_ftsf265ae_slot0, + OPERAND_ftsf266ae_slot0, + OPERAND_ftsf267ae_slot0, + OPERAND_ftsf268ae_slot0, + OPERAND_ftsf269ae_slot0, + OPERAND_ftsf270ae_slot0, + OPERAND_ftsf271ae_slot0, + OPERAND_ftsf272ae_slot0, + OPERAND_ftsf273ae_slot0, + OPERAND_ftsf274ae_slot0, + OPERAND_ftsf275ae_slot0, + OPERAND_ftsf276ae_slot0, + OPERAND_ftsf277ae_slot0, + OPERAND_ftsf278ae_slot0, + OPERAND_ftsf279ae_slot0, + OPERAND_ftsf280, + OPERAND_ftsf281ae_slot0, + OPERAND_ftsf282ae_slot0, + OPERAND_ftsf284ae_slot0, + OPERAND_ftsf285ae_slot0, + OPERAND_ftsf287ae_slot0, + OPERAND_ftsf288, + OPERAND_ftsf289ae_slot0, + OPERAND_ftsf290ae_slot0, + OPERAND_ftsf291ae_slot0, + OPERAND_ftsf292ae_slot0, + OPERAND_ftsf293ae_slot0, + OPERAND_ftsf294ae_slot0, + OPERAND_ftsf295ae_slot0, + OPERAND_ftsf296ae_slot0, + OPERAND_ftsf297ae_slot0, + OPERAND_ftsf298ae_slot0, + OPERAND_ftsf300ae_slot0, + OPERAND_ftsf302ae_slot0, + OPERAND_ftsf304ae_slot0, + OPERAND_ftsf305ae_slot0, + OPERAND_ftsf306ae_slot0, + OPERAND_ftsf307ae_slot0, + OPERAND_ftsf308ae_slot0, + OPERAND_ftsf309, + OPERAND_ftsf310ae_slot0, + OPERAND_ftsf311ae_slot0, + OPERAND_ftsf312ae_slot0, + OPERAND_ftsf313ae_slot0, + OPERAND_ftsf314ae_slot0, + OPERAND_ftsf315, + OPERAND_ftsf316ae_slot0, + OPERAND_ftsf317ae_slot0, + OPERAND_ftsf319ae_slot0, + OPERAND_ftsf320ae_slot0, + OPERAND_ftsf322ae_slot0, + OPERAND_ftsf323ae_slot0, + OPERAND_ftsf324ae_slot0, + OPERAND_ftsf325ae_slot0, + OPERAND_ftsf326ae_slot0, + OPERAND_ftsf327ae_slot0, + OPERAND_ftsf328ae_slot0, + OPERAND_ftsf329ae_slot0, + OPERAND_ftsf360ae_slot0, + OPERAND_ftsf361ae_slot0, + OPERAND_ftsf362, + OPERAND_ftsf363ae_slot0, + OPERAND_ftsf364ae_slot0, + OPERAND_ftsf366ae_slot0, + OPERAND_ftsf368ae_slot0, + OPERAND_ftsf370ae_slot0, + OPERAND_ftsf373ae_slot0, + OPERAND_ftsf376ae_slot0, + OPERAND_ftsf378ae_slot0, + OPERAND_ftsf379ae_slot0, + OPERAND_ftsf382ae_slot0, + OPERAND_ftsf383ae_slot0, + OPERAND_ftsf384ae_slot0, + OPERAND_ftsf386ae_slot0, + OPERAND_ftsf387ae_slot0, + OPERAND_ftsf388ae_slot0, + OPERAND_ftsf389ae_slot0, + OPERAND_ae_mul32x24fld, + OPERAND_op0_s4_s4, + OPERAND_combined2c0b5f72_fld28, + OPERAND_combined2c0b5f72_fld37, + OPERAND_combined2c0b5f72_fld39, + OPERAND_combined2c0b5f72_fld40, + OPERAND_combined2c0b5f72_fld46, + OPERAND_combined2c0b5f72_fld47, + OPERAND_combined2c0b5f72_fld49, + OPERAND_combined2c0b5f72_fld50, + OPERAND_combined2c0b5f72_fld52, + OPERAND_combined2c0b5f72_fld121, + OPERAND_combined2c0b5f72_fld123, + OPERAND_combined2c0b5f72_fld127, + OPERAND_combined2c0b5f72_fld133ae_slot0, + OPERAND_combined2c0b5f72_fld134ae_slot0, + OPERAND_combined2c0b5f72_fld135ae_slot0, + OPERAND_combined2c0b5f72_fld136ae_slot0, + OPERAND_combined2c0b5f72_fld137ae_slot0, + OPERAND_combined2c0b5f72_fld138ae_slot0, + OPERAND_combined2c0b5f72_fld139ae_slot0, + OPERAND_combined2c0b5f72_fld140ae_slot0, + OPERAND_combined2c0b5f72_fld141ae_slot0, + OPERAND_combined2c0b5f72_fld142ae_slot0, + OPERAND_combined2c0b5f72_fld143ae_slot0, + OPERAND_combined2c0b5f72_fld144ae_slot0, + OPERAND_combined2c0b5f72_fld145ae_slot0, + OPERAND_combined2c0b5f72_fld146ae_slot0, + OPERAND_combined2c0b5f72_fld148ae_slot0, + OPERAND_combined2c0b5f72_fld149ae_slot0, + OPERAND_op0_s4_s4_s4, + OPERAND_combined1e9fefee_fld96, + OPERAND_combined1e9fefee_fld98, + OPERAND_combined1e9fefee_fld106ae_slot0, + OPERAND_combined1e9fefee_fld107ae_slot0, + OPERAND_combined1e9fefee_fld108ae_slot0, + OPERAND_combined1e9fefee_fld109ae_slot0, + OPERAND_op0_s3_s3, + OPERAND_combined2c0b5f72_fld19, + OPERAND_combined2c0b5f72_fld22, + OPERAND_combined2c0b5f72_fld24, + OPERAND_combined2c0b5f72_fld65, + OPERAND_combined2c0b5f72_fld66, + OPERAND_combined2c0b5f72_fld68, + OPERAND_combined2c0b5f72_fld69, + OPERAND_combined2c0b5f72_fld74, + OPERAND_combined2c0b5f72_fld79, + OPERAND_combined2c0b5f72_fld88, + OPERAND_combined2c0b5f72_fld90, + OPERAND_combined2c0b5f72_fld91, + OPERAND_combined2c0b5f72_fld131ae_slot1, + OPERAND_combined2c0b5f72_fld132ae_slot1, + OPERAND_combined2c0b5f72_fld147ae_slot1, + OPERAND_s3to1 +}; + + +/* Iclass table. */ + +static xtensa_arg_internal Iclass_xt_iclass_rfe_stateArgs[] = { + { { STATE_PSRING }, 'i' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_EPC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfde_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call12_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar12 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call12_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call8_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar8 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call8_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call4_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call4_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx12_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar12 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx12_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx8_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar8 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx8_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx4_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx4_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_entry_args[] = { + { { OPERAND_ars_entry }, 's' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm12x8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_entry_stateArgs[] = { + { { STATE_PSCALLINC }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSWOE }, 'i' }, + { { STATE_WindowBase }, 'm' }, + { { STATE_WindowStart }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movsp_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movsp_stateArgs[] = { + { { STATE_WindowBase }, 'i' }, + { { STATE_WindowStart }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rotw_args[] = { + { { OPERAND_simm4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rotw_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_retw_args[] = { + { { OPERAND__ars_invisible }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_retw_stateArgs[] = { + { { STATE_WindowBase }, 'm' }, + { { STATE_WindowStart }, 'm' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSWOE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfwou_stateArgs[] = { + { { STATE_EPC1 }, 'i' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'm' }, + { { STATE_WindowStart }, 'm' }, + { { STATE_PSOWB }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32e_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_immrx4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32e_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32e_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_immrx4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32e_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowbase_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowbase_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowbase_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowstart_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowstart_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowStart }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowstart_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowstart_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowStart }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowstart_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowstart_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowStart }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_add_n_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addi_n_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ai4const }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bz6_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm6 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loadi4_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_lsi4x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mov_n_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movi_n_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_simm7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_retn_args[] = { + { { OPERAND__ars_invisible }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_storei4_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_lsi4x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_threadptr_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_threadptr_stateArgs[] = { + { { STATE_THREADPTR }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_threadptr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_threadptr_stateArgs[] = { + { { STATE_THREADPTR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addi_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_simm8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addmi_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_simm8x256 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addsub_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bit_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsi8_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_b4const }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsi8b_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_bbi }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsi8u_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_b4constu }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bst8_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsz12_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_label12 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call0_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx0_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_exti_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_sae }, 'i' }, + { { OPERAND_op2p1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_jump_args[] = { + { { OPERAND_soffset }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_jumpx_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l16ui_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l16si_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32i_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32r_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_uimm16x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l8i_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loop_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ulabel8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loop_stateArgs[] = { + { { STATE_LBEG }, 'o' }, + { { STATE_LEND }, 'o' }, + { { STATE_LCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loopz_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ulabel8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loopz_stateArgs[] = { + { { STATE_LBEG }, 'o' }, + { { STATE_LEND }, 'o' }, + { { STATE_LCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movi_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_simm12b }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movz_args[] = { + { { OPERAND_arr }, 'm' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_neg_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_return_args[] = { + { { OPERAND__ars_invisible }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s16i_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32i_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32nb_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimmrx4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s8i_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sar_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sar_stateArgs[] = { + { { STATE_SAR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sari_args[] = { + { { OPERAND_sas }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sari_stateArgs[] = { + { { STATE_SAR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shifts_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shifts_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftst_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftst_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftt_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftt_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_slli_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_msalp32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_srai_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_sargt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_srli_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sync_stateArgs[] = { + { { STATE_XTSYNC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsil_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsil_stateArgs[] = { + { { STATE_PSWOE }, 'i' }, + { { STATE_PSCALLINC }, 'i' }, + { { STATE_PSOWB }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PSUM }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lend_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lend_stateArgs[] = { + { { STATE_LEND }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lend_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lend_stateArgs[] = { + { { STATE_LEND }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lend_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lend_stateArgs[] = { + { { STATE_LEND }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lcount_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lcount_stateArgs[] = { + { { STATE_LCOUNT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lcount_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lcount_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_LCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lcount_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lcount_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_LCOUNT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lbeg_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lbeg_stateArgs[] = { + { { STATE_LBEG }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lbeg_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lbeg_stateArgs[] = { + { { STATE_LBEG }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lbeg_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lbeg_stateArgs[] = { + { { STATE_LBEG }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_sar_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_sar_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_sar_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_sar_stateArgs[] = { + { { STATE_SAR }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_sar_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_sar_stateArgs[] = { + { { STATE_SAR }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_memctl_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_memctl_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_memctl_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_litbase_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_litbase_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_litbase_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_configid0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_configid0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_configid0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_configid0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_configid1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_configid1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_243_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_243_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ps_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ps_stateArgs[] = { + { { STATE_PSWOE }, 'i' }, + { { STATE_PSCALLINC }, 'i' }, + { { STATE_PSOWB }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PSUM }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ps_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ps_stateArgs[] = { + { { STATE_PSWOE }, 'o' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSOWB }, 'o' }, + { { STATE_PSRING }, 'm' }, + { { STATE_PSUM }, 'o' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSINTLEVEL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ps_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ps_stateArgs[] = { + { { STATE_PSWOE }, 'm' }, + { { STATE_PSCALLINC }, 'm' }, + { { STATE_PSOWB }, 'm' }, + { { STATE_PSRING }, 'm' }, + { { STATE_PSUM }, 'm' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSINTLEVEL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc3_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC3 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc3_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC3 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc3_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC3 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave3_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE3 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave3_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE3 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave3_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE3 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc4_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc4_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc4_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave4_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave4_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave4_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc5_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC5 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc5_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC5 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc5_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC5 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave5_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE5 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave5_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE5 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave5_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE5 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc6_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC6 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc6_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC6 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc6_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC6 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave6_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE6 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave6_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE6 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave6_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE6 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc7_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc7_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC7 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc7_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC7 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave7_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave7_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE7 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave7_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE7 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps3_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS3 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps3_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS3 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps3_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps3_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS3 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps4_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps4_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps4_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps4_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps5_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS5 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps5_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS5 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps5_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps5_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS5 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps6_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS6 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps6_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS6 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps6_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps6_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS6 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps7_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps7_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS7 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps7_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps7_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS7 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excvaddr_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excvaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excvaddr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excvaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCVADDR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excvaddr_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excvaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCVADDR }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_depc_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_depc_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_depc_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_depc_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_depc_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_depc_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_exccause_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_exccause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCCAUSE }, 'i' }, + { { STATE_XTSYNC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_exccause_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_exccause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCCAUSE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_exccause_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_exccause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCCAUSE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC0 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_prid_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_prid_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_vecbase_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_vecbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_VECBASE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_vecbase_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_vecbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_VECBASE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_vecbase_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_vecbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_VECBASE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_mul16_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_mul32_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_mul32h_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_aa_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_aa_stateArgs[] = { + { { STATE_ACC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_ad_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_my }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_ad_stateArgs[] = { + { { STATE_ACC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_da_args[] = { + { { OPERAND_mx }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_da_stateArgs[] = { + { { STATE_ACC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_dd_args[] = { + { { OPERAND_mx }, 'i' }, + { { OPERAND_my }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_dd_stateArgs[] = { + { { STATE_ACC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_aa_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_aa_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_ad_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_my }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_ad_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_da_args[] = { + { { OPERAND_mx }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_da_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_dd_args[] = { + { { OPERAND_mx }, 'i' }, + { { OPERAND_my }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16a_dd_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16al_da_args[] = { + { { OPERAND_mw }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_mx }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16al_da_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16al_dd_args[] = { + { { OPERAND_mw }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_mx }, 'i' }, + { { OPERAND_my }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16al_dd_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mac16_l_args[] = { + { { OPERAND_mw }, 'o' }, + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_m0_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_mr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_m0_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_mr0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_m0_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_mr0 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_m1_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_mr1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_m1_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_mr1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_m1_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_mr1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_m2_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_mr2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_m2_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_mr2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_m2_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_mr2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_m3_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_mr3 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_m3_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_mr3 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_m3_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_mr3 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_acclo_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_acclo_stateArgs[] = { + { { STATE_ACC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_acclo_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_acclo_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_acclo_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_acclo_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_acchi_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_acchi_stateArgs[] = { + { { STATE_ACC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_acchi_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_acchi_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_acchi_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_acchi_stateArgs[] = { + { { STATE_ACC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfi_args[] = { + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfi_stateArgs[] = { + { { STATE_PSWOE }, 'o' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSOWB }, 'o' }, + { { STATE_PSRING }, 'm' }, + { { STATE_PSUM }, 'o' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSINTLEVEL }, 'o' }, + { { STATE_EPC1 }, 'i' }, + { { STATE_EPC2 }, 'i' }, + { { STATE_EPC3 }, 'i' }, + { { STATE_EPC4 }, 'i' }, + { { STATE_EPC5 }, 'i' }, + { { STATE_EPC6 }, 'i' }, + { { STATE_EPC7 }, 'i' }, + { { STATE_EPS2 }, 'i' }, + { { STATE_EPS3 }, 'i' }, + { { STATE_EPS4 }, 'i' }, + { { STATE_EPS5 }, 'i' }, + { { STATE_EPS6 }, 'i' }, + { { STATE_EPS7 }, 'i' }, + { { STATE_InOCDMode }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wait_args[] = { + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wait_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PSINTLEVEL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_interrupt_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_interrupt_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTERRUPT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intset_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intset_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intclear_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intclear_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_intenable_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_intenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intenable_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTENABLE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_intenable_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_intenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTENABLE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_args[] = { + { { OPERAND_imms }, 'i' }, + { { OPERAND_immt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_n_args[] = { + { { OPERAND_imms }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_n_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreaka0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreaka0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKA0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreaka0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreaka0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKA0 }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreaka0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreaka0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKA0 }, 'm' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreakc0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreakc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKC0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreakc0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreakc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKC0 }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreakc0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreakc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKC0 }, 'm' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreaka1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreaka1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKA1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreaka1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreaka1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKA1 }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreaka1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreaka1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKA1 }, 'm' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreakc1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dbreakc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreakc1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dbreakc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKC1 }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreakc1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dbreakc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DBREAKC1 }, 'm' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ibreaka0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ibreaka0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKA0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ibreaka0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ibreaka0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKA0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ibreaka0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ibreaka0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKA0 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ibreaka1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ibreaka1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKA1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ibreaka1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ibreaka1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKA1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ibreaka1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ibreaka1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKA1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ibreakenable_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ibreakenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ibreakenable_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ibreakenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKENABLE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ibreakenable_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ibreakenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_IBREAKENABLE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_debugcause_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_debugcause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEBUGCAUSE }, 'i' }, + { { STATE_DBNUM }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_debugcause_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_debugcause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEBUGCAUSE }, 'o' }, + { { STATE_DBNUM }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_debugcause_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_debugcause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEBUGCAUSE }, 'm' }, + { { STATE_DBNUM }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icount_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icount_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_ICOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icount_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_ICOUNT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icountlevel_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icountlevel_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icountlevel_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icountlevel_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNTLEVEL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icountlevel_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icountlevel_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNTLEVEL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ddr_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ddr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_DDR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ddr_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_DDR }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_lddr32_p_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_lddr32_p_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_InOCDMode }, 'i' }, + { { STATE_DDR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sddr32_p_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sddr32_p_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_InOCDMode }, 'i' }, + { { STATE_DDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfdo_args[] = { + { { OPERAND_imms }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfdo_stateArgs[] = { + { { STATE_InOCDMode }, 'm' }, + { { STATE_EPC6 }, 'i' }, + { { STATE_PSWOE }, 'o' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSOWB }, 'o' }, + { { STATE_PSRING }, 'o' }, + { { STATE_PSUM }, 'o' }, + { { STATE_PSEXCM }, 'o' }, + { { STATE_PSINTLEVEL }, 'o' }, + { { STATE_EPS6 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfdd_stateArgs[] = { + { { STATE_InOCDMode }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_mmid_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_mmid_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbool1_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_bs }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbool4_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_bs4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbool8_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_bs8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbranch_args[] = { + { { OPERAND_bs }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bmove_args[] = { + { { OPERAND_arr }, 'm' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_RSR_BR_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_brall }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_WSR_BR_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_brall }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_XSR_BR_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_brall }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccount_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOUNT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccount_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_CCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccount_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_CCOUNT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE0 }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE0 }, 'm' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE1 }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE1 }, 'm' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE2 }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE2 }, 'm' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_lock_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm4x16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_lock_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_inv_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_inv_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_licx_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_licx_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sicx_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sicx_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_dyn_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_dyn_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_ind_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm4x16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_ind_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_inv_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_inv_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dpf_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_lock_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm4x16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_lock_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sdct_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sdct_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ldct_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ldct_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_prefctl_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_prefctl_stateArgs[] = { + { { STATE_PREFCTL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_prefctl_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_prefctl_stateArgs[] = { + { { STATE_PREFCTL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_prefctl_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_prefctl_stateArgs[] = { + { { STATE_PREFCTL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ptevaddr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ptevaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PTBASE }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ptevaddr_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ptevaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PTBASE }, 'i' }, + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ptevaddr_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ptevaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PTBASE }, 'm' }, + { { STATE_EXCVADDR }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_rasid_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_rasid_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ASID3 }, 'i' }, + { { STATE_ASID2 }, 'i' }, + { { STATE_ASID1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_rasid_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_rasid_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ASID3 }, 'o' }, + { { STATE_ASID2 }, 'o' }, + { { STATE_ASID1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_rasid_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_rasid_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ASID3 }, 'm' }, + { { STATE_ASID2 }, 'm' }, + { { STATE_ASID1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_itlbcfg_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_itlbcfg_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INSTPGSZID6 }, 'i' }, + { { STATE_INSTPGSZID5 }, 'i' }, + { { STATE_INSTPGSZID4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_itlbcfg_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_itlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INSTPGSZID6 }, 'o' }, + { { STATE_INSTPGSZID5 }, 'o' }, + { { STATE_INSTPGSZID4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_itlbcfg_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_itlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INSTPGSZID6 }, 'm' }, + { { STATE_INSTPGSZID5 }, 'm' }, + { { STATE_INSTPGSZID4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dtlbcfg_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dtlbcfg_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DATAPGSZID6 }, 'i' }, + { { STATE_DATAPGSZID5 }, 'i' }, + { { STATE_DATAPGSZID4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dtlbcfg_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dtlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DATAPGSZID6 }, 'o' }, + { { STATE_DATAPGSZID5 }, 'o' }, + { { STATE_DATAPGSZID4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dtlbcfg_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dtlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DATAPGSZID6 }, 'm' }, + { { STATE_DATAPGSZID5 }, 'm' }, + { { STATE_DATAPGSZID4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_idtlb_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_idtlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rdtlb_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rdtlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wdtlb_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wdtlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_iitlb_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_iitlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ritlb_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ritlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_witlb_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_witlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ldpte_stateArgs[] = { + { { STATE_PTBASE }, 'i' }, + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_hwwitlba_stateArgs[] = { + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_hwwdtlba_stateArgs[] = { + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_cpenable_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_cpenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_cpenable_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_cpenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CPENABLE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_cpenable_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_cpenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CPENABLE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_clamp_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_tp7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_minmax_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_nsa_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sx_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_tp7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32ai_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32ri_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32c1i_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32c1i_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'i' }, + { { STATE_XTSYNC }, 'i' }, + { { STATE_SCOMPARE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_scompare1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_scompare1_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_scompare1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_scompare1_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_scompare1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_scompare1_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_atomctl_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_atomctl_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ATOMCTL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_atomctl_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_atomctl_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ATOMCTL }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_atomctl_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_atomctl_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ATOMCTL }, 'm' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_div_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rer_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rer_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_ERI_RAW_INTERLOCK }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_interface Iclass_xt_iclass_rer_intfArgs[] = { + INTERFACE_ERI_RD_In, + INTERFACE_ERI_RD_Out +}; + +static xtensa_arg_internal Iclass_xt_iclass_wer_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wer_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_ERI_RAW_INTERLOCK }, 'o' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_interface Iclass_xt_iclass_wer_intfArgs[] = { + INTERFACE_ERI_WR_In, + INTERFACE_ERI_WR_Out +}; + +static xtensa_arg_internal Iclass_rur_ae_ovf_sar_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ovf_sar_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'i' }, + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ovf_sar_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ovf_sar_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'o' }, + { { STATE_AE_SAR }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_bithead_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_bithead_stateArgs[] = { + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_bithead_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_bithead_stateArgs[] = { + { { STATE_AE_BITHEAD }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ts_fts_bu_bp_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ts_fts_bu_bp_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_TABLESIZE }, 'i' }, + { { STATE_AE_FIRST_TS }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ts_fts_bu_bp_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ts_fts_bu_bp_stateArgs[] = { + { { STATE_AE_BITPTR }, 'o' }, + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_TABLESIZE }, 'o' }, + { { STATE_AE_FIRST_TS }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_sd_no_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_sd_no_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'i' }, + { { STATE_AE_SEARCHDONE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_sd_no_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_sd_no_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_overflow_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_overflow_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_overflow_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_overflow_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_sar_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_sar_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_sar_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_sar_stateArgs[] = { + { { STATE_AE_SAR }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitptr_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitptr_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitptr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitptr_stateArgs[] = { + { { STATE_AE_BITPTR }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitsused_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitsused_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitsused_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitsused_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_tablesize_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_tablesize_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_tablesize_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_tablesize_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_first_ts_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_first_ts_stateArgs[] = { + { { STATE_AE_FIRST_TS }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_first_ts_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_first_ts_stateArgs[] = { + { { STATE_AE_FIRST_TS }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_nextoffset_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_nextoffset_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_nextoffset_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_nextoffset_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_searchdone_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_searchdone_stateArgs[] = { + { { STATE_AE_SEARCHDONE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_searchdone_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_searchdone_stateArgs[] = { + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_i_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_iu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_x_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_xu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_i_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_iu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_x_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_xu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_i_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_iu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_x_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_xu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_i_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_iu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_x_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_xu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zerop48_args[] = { + { { OPERAND_ps }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zerop48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_ll_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_lh_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hl_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hh_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp24x2_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp24x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp24x2_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp24x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp48_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp48_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movpa24x2_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movpa24x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24a32x2_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24a32x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_l_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_h_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_ll_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_lh_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hl_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hh_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24q48x2_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24q48x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp16_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp16_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48sym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48asym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48sym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48asym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16sym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16asym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zeroq56_args[] = { + { { OPERAND_qr1_w }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zeroq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtq56_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bs }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfq56_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bs }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48a32s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48a32s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_l_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_cvt_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_h_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_cvt_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_satq48s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_satq48s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncq32_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncq32_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32sym_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32asym_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca32q48_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca32q48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_l_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_h_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_l_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_h_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbp24s_args[] = { + { { OPERAND_alupppb_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbp24s_args[] = { + { { OPERAND_alupppb_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltp24s_args[] = { + { { OPERAND_bt2 }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lep24s_args[] = { + { { OPERAND_bt2 }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lep24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqp24_args[] = { + { { OPERAND_bt2 }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bt }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bt }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllip24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllip24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlip24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlip24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraip24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraip24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsp24_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsp24_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasp24_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_slliq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_slliq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srliq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srliq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraiq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraiq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsq56_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsq56_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasq56_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllaq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllaq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlaq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlaq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraaq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraaq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllasq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllasq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltq56s_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_leq56s_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_leq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqq56_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nsaq56s_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nsaq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsrfq32sp24s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsrfq32sp24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsrfq32sp24s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsrfq32sp24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mularfq32sp24s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mularfq32sp24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mularfq32sp24s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mularfq32sp24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulrfq32sp24s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulrfq32sp24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulrfq32sp24s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulrfq32sp24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp24s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp24s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp24s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp24s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp24s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp24s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hl_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_l_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_h_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_l_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_h_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_l_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_h_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_hh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_mac_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_mac_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hh_ll_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hl_lh_args[] = { + { { OPERAND_mac_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sha32_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl32t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl32t_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'm' }, + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'm' }, + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16t_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'm' }, + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'm' }, + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16c_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16c_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'm' }, + { { STATE_AE_TABLESIZE }, 'm' }, + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_AE_FIRST_TS }, 'i' }, + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_SEARCHDONE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldsht_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldsht_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_AE_FIRST_TS }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_AE_TABLESIZE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lb_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lb_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbi_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ae_ohba2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbi_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbk_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbk_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbki_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_ohba2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbki_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_db_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_db_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_dbi_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_ohba }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_dbi_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel32t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'm' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel32t_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel16t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'm' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel16t_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sb_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sb_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbi_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_ae_ohba }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbi_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vles16c_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vles16c_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_NEXTOFFSET }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbf_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbf_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SLAASQ56S_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SLAASQ56S_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_ADDBRBA32_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MINABSSP24S_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MINABSSP24S_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MAXABSSP24S_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MAXABSSP24S_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MINABSSQ56S_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MINABSSQ56S_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MAXABSSQ56S_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_MAXABSSQ56S_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_cbegin0_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_cbegin0_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_cbegin0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_cbegin0_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_cend0_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_cend0_stateArgs[] = { + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_cend0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_cend0_stateArgs[] = { + { { STATE_AE_CEND0 }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24X2_C_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24X2_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24X2S_C_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24X2S_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24X2F_C_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24X2F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24X2F_C_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24X2F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP16X2F_C_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP16X2F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP16X2F_C_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP16X2F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24_C_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24S_L_C_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24S_L_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24F_C_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP24F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24F_L_C_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP24F_L_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP16F_C_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LP16F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP16F_L_C_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SP16F_L_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LQ56_C_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LQ56_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SQ56S_C_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SQ56S_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LQ32F_C_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_LQ32F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SQ32F_C_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_icls_AE_SQ32F_C_stateArgs[] = { + { { STATE_AE_CBEGIN0 }, 'i' }, + { { STATE_AE_CEND0 }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_expstate_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_expstate_stateArgs[] = { + { { STATE_EXPSTATE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_expstate_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_expstate_stateArgs[] = { + { { STATE_EXPSTATE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_iclass_READ_IMPWIRE_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_iclass_READ_IMPWIRE_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_interface Iclass_iclass_READ_IMPWIRE_intfArgs[] = { + INTERFACE_IMPWIRE +}; + +static xtensa_arg_internal Iclass_iclass_SETB_EXPSTATE_args[] = { + { { OPERAND_bitindex }, 'i' } +}; + +static xtensa_arg_internal Iclass_iclass_SETB_EXPSTATE_stateArgs[] = { + { { STATE_EXPSTATE }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_iclass_CLRB_EXPSTATE_args[] = { + { { OPERAND_bitindex }, 'i' } +}; + +static xtensa_arg_internal Iclass_iclass_CLRB_EXPSTATE_stateArgs[] = { + { { STATE_EXPSTATE }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_iclass_WRMSK_EXPSTATE_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_iclass_WRMSK_EXPSTATE_stateArgs[] = { + { { STATE_EXPSTATE }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_iclass_internal iclasses[] = { + { 0, 0 /* xt_iclass_excw */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_rfe */, + 3, Iclass_xt_iclass_rfe_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_rfde */, + 3, Iclass_xt_iclass_rfde_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_syscall */, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_call12_args, + 1, Iclass_xt_iclass_call12_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_call8_args, + 1, Iclass_xt_iclass_call8_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_call4_args, + 1, Iclass_xt_iclass_call4_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_callx12_args, + 1, Iclass_xt_iclass_callx12_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_callx8_args, + 1, Iclass_xt_iclass_callx8_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_callx4_args, + 1, Iclass_xt_iclass_callx4_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_entry_args, + 5, Iclass_xt_iclass_entry_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_movsp_args, + 2, Iclass_xt_iclass_movsp_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rotw_args, + 3, Iclass_xt_iclass_rotw_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_retw_args, + 5, Iclass_xt_iclass_retw_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_rfwou */, + 6, Iclass_xt_iclass_rfwou_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_l32e_args, + 2, Iclass_xt_iclass_l32e_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_s32e_args, + 2, Iclass_xt_iclass_s32e_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_windowbase_args, + 3, Iclass_xt_iclass_rsr_windowbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_windowbase_args, + 3, Iclass_xt_iclass_wsr_windowbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_windowbase_args, + 3, Iclass_xt_iclass_xsr_windowbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_windowstart_args, + 3, Iclass_xt_iclass_rsr_windowstart_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_windowstart_args, + 3, Iclass_xt_iclass_wsr_windowstart_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_windowstart_args, + 3, Iclass_xt_iclass_xsr_windowstart_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_add_n_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_addi_n_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bz6_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_ill_n */, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_loadi4_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_mov_n_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_movi_n_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_nopn */, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_retn_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_storei4_args, + 0, 0, 0, 0 }, + { 1, Iclass_rur_threadptr_args, + 1, Iclass_rur_threadptr_stateArgs, 0, 0 }, + { 1, Iclass_wur_threadptr_args, + 1, Iclass_wur_threadptr_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_addi_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_addmi_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_addsub_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bit_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bsi8_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bsi8b_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bsi8u_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bst8_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bsz12_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_call0_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_callx0_args, + 0, 0, 0, 0 }, + { 4, Iclass_xt_iclass_exti_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_ill */, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_jump_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_jumpx_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l16ui_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l16si_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l32i_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_l32r_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l8i_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_loop_args, + 3, Iclass_xt_iclass_loop_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_loopz_args, + 3, Iclass_xt_iclass_loopz_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_movi_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_movz_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_neg_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_nop */, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_return_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_simcall */, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s16i_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32i_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32nb_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s8i_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_sar_args, + 1, Iclass_xt_iclass_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_sari_args, + 1, Iclass_xt_iclass_sari_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_shifts_args, + 1, Iclass_xt_iclass_shifts_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_shiftst_args, + 1, Iclass_xt_iclass_shiftst_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_shiftt_args, + 1, Iclass_xt_iclass_shiftt_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_slli_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_srai_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_srli_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_memw */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_extw */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_isync */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_sync */, + 1, Iclass_xt_iclass_sync_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_rsil_args, + 7, Iclass_xt_iclass_rsil_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_lend_args, + 1, Iclass_xt_iclass_rsr_lend_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_lend_args, + 1, Iclass_xt_iclass_wsr_lend_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_lend_args, + 1, Iclass_xt_iclass_xsr_lend_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_lcount_args, + 1, Iclass_xt_iclass_rsr_lcount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_lcount_args, + 2, Iclass_xt_iclass_wsr_lcount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_lcount_args, + 2, Iclass_xt_iclass_xsr_lcount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_lbeg_args, + 1, Iclass_xt_iclass_rsr_lbeg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_lbeg_args, + 1, Iclass_xt_iclass_wsr_lbeg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_lbeg_args, + 1, Iclass_xt_iclass_xsr_lbeg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_sar_args, + 1, Iclass_xt_iclass_rsr_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_sar_args, + 2, Iclass_xt_iclass_wsr_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_sar_args, + 1, Iclass_xt_iclass_xsr_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_memctl_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_memctl_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_memctl_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_litbase_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_litbase_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_litbase_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_configid0_args, + 2, Iclass_xt_iclass_rsr_configid0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_configid0_args, + 2, Iclass_xt_iclass_wsr_configid0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_configid1_args, + 2, Iclass_xt_iclass_rsr_configid1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_243_args, + 2, Iclass_xt_iclass_rsr_243_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ps_args, + 7, Iclass_xt_iclass_rsr_ps_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ps_args, + 7, Iclass_xt_iclass_wsr_ps_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ps_args, + 7, Iclass_xt_iclass_xsr_ps_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc1_args, + 3, Iclass_xt_iclass_rsr_epc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc1_args, + 3, Iclass_xt_iclass_wsr_epc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc1_args, + 3, Iclass_xt_iclass_xsr_epc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave1_args, + 3, Iclass_xt_iclass_rsr_excsave1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave1_args, + 3, Iclass_xt_iclass_wsr_excsave1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave1_args, + 3, Iclass_xt_iclass_xsr_excsave1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc2_args, + 3, Iclass_xt_iclass_rsr_epc2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc2_args, + 3, Iclass_xt_iclass_wsr_epc2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc2_args, + 3, Iclass_xt_iclass_xsr_epc2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave2_args, + 3, Iclass_xt_iclass_rsr_excsave2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave2_args, + 3, Iclass_xt_iclass_wsr_excsave2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave2_args, + 3, Iclass_xt_iclass_xsr_excsave2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc3_args, + 3, Iclass_xt_iclass_rsr_epc3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc3_args, + 3, Iclass_xt_iclass_wsr_epc3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc3_args, + 3, Iclass_xt_iclass_xsr_epc3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave3_args, + 3, Iclass_xt_iclass_rsr_excsave3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave3_args, + 3, Iclass_xt_iclass_wsr_excsave3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave3_args, + 3, Iclass_xt_iclass_xsr_excsave3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc4_args, + 3, Iclass_xt_iclass_rsr_epc4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc4_args, + 3, Iclass_xt_iclass_wsr_epc4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc4_args, + 3, Iclass_xt_iclass_xsr_epc4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave4_args, + 3, Iclass_xt_iclass_rsr_excsave4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave4_args, + 3, Iclass_xt_iclass_wsr_excsave4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave4_args, + 3, Iclass_xt_iclass_xsr_excsave4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc5_args, + 3, Iclass_xt_iclass_rsr_epc5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc5_args, + 3, Iclass_xt_iclass_wsr_epc5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc5_args, + 3, Iclass_xt_iclass_xsr_epc5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave5_args, + 3, Iclass_xt_iclass_rsr_excsave5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave5_args, + 3, Iclass_xt_iclass_wsr_excsave5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave5_args, + 3, Iclass_xt_iclass_xsr_excsave5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc6_args, + 3, Iclass_xt_iclass_rsr_epc6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc6_args, + 3, Iclass_xt_iclass_wsr_epc6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc6_args, + 3, Iclass_xt_iclass_xsr_epc6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave6_args, + 3, Iclass_xt_iclass_rsr_excsave6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave6_args, + 3, Iclass_xt_iclass_wsr_excsave6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave6_args, + 3, Iclass_xt_iclass_xsr_excsave6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc7_args, + 3, Iclass_xt_iclass_rsr_epc7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc7_args, + 3, Iclass_xt_iclass_wsr_epc7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc7_args, + 3, Iclass_xt_iclass_xsr_epc7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave7_args, + 3, Iclass_xt_iclass_rsr_excsave7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave7_args, + 3, Iclass_xt_iclass_wsr_excsave7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave7_args, + 3, Iclass_xt_iclass_xsr_excsave7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps2_args, + 3, Iclass_xt_iclass_rsr_eps2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps2_args, + 3, Iclass_xt_iclass_wsr_eps2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps2_args, + 3, Iclass_xt_iclass_xsr_eps2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps3_args, + 3, Iclass_xt_iclass_rsr_eps3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps3_args, + 3, Iclass_xt_iclass_wsr_eps3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps3_args, + 3, Iclass_xt_iclass_xsr_eps3_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps4_args, + 3, Iclass_xt_iclass_rsr_eps4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps4_args, + 3, Iclass_xt_iclass_wsr_eps4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps4_args, + 3, Iclass_xt_iclass_xsr_eps4_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps5_args, + 3, Iclass_xt_iclass_rsr_eps5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps5_args, + 3, Iclass_xt_iclass_wsr_eps5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps5_args, + 3, Iclass_xt_iclass_xsr_eps5_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps6_args, + 3, Iclass_xt_iclass_rsr_eps6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps6_args, + 3, Iclass_xt_iclass_wsr_eps6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps6_args, + 3, Iclass_xt_iclass_xsr_eps6_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps7_args, + 3, Iclass_xt_iclass_rsr_eps7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps7_args, + 3, Iclass_xt_iclass_wsr_eps7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps7_args, + 3, Iclass_xt_iclass_xsr_eps7_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excvaddr_args, + 3, Iclass_xt_iclass_rsr_excvaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excvaddr_args, + 3, Iclass_xt_iclass_wsr_excvaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excvaddr_args, + 3, Iclass_xt_iclass_xsr_excvaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_depc_args, + 3, Iclass_xt_iclass_rsr_depc_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_depc_args, + 3, Iclass_xt_iclass_wsr_depc_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_depc_args, + 3, Iclass_xt_iclass_xsr_depc_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_exccause_args, + 4, Iclass_xt_iclass_rsr_exccause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_exccause_args, + 3, Iclass_xt_iclass_wsr_exccause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_exccause_args, + 3, Iclass_xt_iclass_xsr_exccause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_misc0_args, + 3, Iclass_xt_iclass_rsr_misc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_misc0_args, + 3, Iclass_xt_iclass_wsr_misc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_misc0_args, + 3, Iclass_xt_iclass_xsr_misc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_misc1_args, + 3, Iclass_xt_iclass_rsr_misc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_misc1_args, + 3, Iclass_xt_iclass_wsr_misc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_misc1_args, + 3, Iclass_xt_iclass_xsr_misc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_prid_args, + 2, Iclass_xt_iclass_rsr_prid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_vecbase_args, + 3, Iclass_xt_iclass_rsr_vecbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_vecbase_args, + 3, Iclass_xt_iclass_wsr_vecbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_vecbase_args, + 3, Iclass_xt_iclass_xsr_vecbase_stateArgs, 0, 0 }, + { 3, Iclass_xt_mul16_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_mul32_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_mul32h_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_mac16_aa_args, + 1, Iclass_xt_iclass_mac16_aa_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16_ad_args, + 1, Iclass_xt_iclass_mac16_ad_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16_da_args, + 1, Iclass_xt_iclass_mac16_da_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16_dd_args, + 1, Iclass_xt_iclass_mac16_dd_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16a_aa_args, + 1, Iclass_xt_iclass_mac16a_aa_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16a_ad_args, + 1, Iclass_xt_iclass_mac16a_ad_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16a_da_args, + 1, Iclass_xt_iclass_mac16a_da_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16a_dd_args, + 1, Iclass_xt_iclass_mac16a_dd_stateArgs, 0, 0 }, + { 4, Iclass_xt_iclass_mac16al_da_args, + 1, Iclass_xt_iclass_mac16al_da_stateArgs, 0, 0 }, + { 4, Iclass_xt_iclass_mac16al_dd_args, + 1, Iclass_xt_iclass_mac16al_dd_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_mac16_l_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_rsr_m0_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_wsr_m0_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_xsr_m0_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_rsr_m1_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_wsr_m1_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_xsr_m1_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_rsr_m2_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_wsr_m2_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_xsr_m2_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_rsr_m3_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_wsr_m3_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_xsr_m3_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_acclo_args, + 1, Iclass_xt_iclass_rsr_acclo_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_acclo_args, + 1, Iclass_xt_iclass_wsr_acclo_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_acclo_args, + 1, Iclass_xt_iclass_xsr_acclo_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_acchi_args, + 1, Iclass_xt_iclass_rsr_acchi_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_acchi_args, + 1, Iclass_xt_iclass_wsr_acchi_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_acchi_args, + 1, Iclass_xt_iclass_xsr_acchi_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rfi_args, + 21, Iclass_xt_iclass_rfi_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wait_args, + 3, Iclass_xt_iclass_wait_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_interrupt_args, + 3, Iclass_xt_iclass_rsr_interrupt_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_intset_args, + 4, Iclass_xt_iclass_wsr_intset_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_intclear_args, + 4, Iclass_xt_iclass_wsr_intclear_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_intenable_args, + 3, Iclass_xt_iclass_rsr_intenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_intenable_args, + 3, Iclass_xt_iclass_wsr_intenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_intenable_args, + 3, Iclass_xt_iclass_xsr_intenable_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_break_args, + 2, Iclass_xt_iclass_break_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_break_n_args, + 2, Iclass_xt_iclass_break_n_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_dbreaka0_args, + 3, Iclass_xt_iclass_rsr_dbreaka0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_dbreaka0_args, + 4, Iclass_xt_iclass_wsr_dbreaka0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_dbreaka0_args, + 4, Iclass_xt_iclass_xsr_dbreaka0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_dbreakc0_args, + 3, Iclass_xt_iclass_rsr_dbreakc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_dbreakc0_args, + 4, Iclass_xt_iclass_wsr_dbreakc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_dbreakc0_args, + 4, Iclass_xt_iclass_xsr_dbreakc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_dbreaka1_args, + 3, Iclass_xt_iclass_rsr_dbreaka1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_dbreaka1_args, + 4, Iclass_xt_iclass_wsr_dbreaka1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_dbreaka1_args, + 4, Iclass_xt_iclass_xsr_dbreaka1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_dbreakc1_args, + 3, Iclass_xt_iclass_rsr_dbreakc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_dbreakc1_args, + 4, Iclass_xt_iclass_wsr_dbreakc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_dbreakc1_args, + 4, Iclass_xt_iclass_xsr_dbreakc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ibreaka0_args, + 3, Iclass_xt_iclass_rsr_ibreaka0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ibreaka0_args, + 3, Iclass_xt_iclass_wsr_ibreaka0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ibreaka0_args, + 3, Iclass_xt_iclass_xsr_ibreaka0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ibreaka1_args, + 3, Iclass_xt_iclass_rsr_ibreaka1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ibreaka1_args, + 3, Iclass_xt_iclass_wsr_ibreaka1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ibreaka1_args, + 3, Iclass_xt_iclass_xsr_ibreaka1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ibreakenable_args, + 3, Iclass_xt_iclass_rsr_ibreakenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ibreakenable_args, + 3, Iclass_xt_iclass_wsr_ibreakenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ibreakenable_args, + 3, Iclass_xt_iclass_xsr_ibreakenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_debugcause_args, + 4, Iclass_xt_iclass_rsr_debugcause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_debugcause_args, + 4, Iclass_xt_iclass_wsr_debugcause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_debugcause_args, + 4, Iclass_xt_iclass_xsr_debugcause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_icount_args, + 3, Iclass_xt_iclass_rsr_icount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_icount_args, + 4, Iclass_xt_iclass_wsr_icount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_icount_args, + 4, Iclass_xt_iclass_xsr_icount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_icountlevel_args, + 3, Iclass_xt_iclass_rsr_icountlevel_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_icountlevel_args, + 3, Iclass_xt_iclass_wsr_icountlevel_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_icountlevel_args, + 3, Iclass_xt_iclass_xsr_icountlevel_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ddr_args, + 3, Iclass_xt_iclass_rsr_ddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ddr_args, + 4, Iclass_xt_iclass_wsr_ddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ddr_args, + 4, Iclass_xt_iclass_xsr_ddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_lddr32_p_args, + 5, Iclass_xt_iclass_lddr32_p_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_sddr32_p_args, + 4, Iclass_xt_iclass_sddr32_p_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rfdo_args, + 10, Iclass_xt_iclass_rfdo_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_rfdd */, + 1, Iclass_xt_iclass_rfdd_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_mmid_args, + 3, Iclass_xt_iclass_wsr_mmid_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_bbool1_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bbool4_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bbool8_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bbranch_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bmove_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_RSR_BR_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_WSR_BR_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_XSR_BR_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccount_args, + 3, Iclass_xt_iclass_rsr_ccount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccount_args, + 4, Iclass_xt_iclass_wsr_ccount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccount_args, + 4, Iclass_xt_iclass_xsr_ccount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccompare0_args, + 3, Iclass_xt_iclass_rsr_ccompare0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccompare0_args, + 4, Iclass_xt_iclass_wsr_ccompare0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccompare0_args, + 4, Iclass_xt_iclass_xsr_ccompare0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccompare1_args, + 3, Iclass_xt_iclass_rsr_ccompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccompare1_args, + 4, Iclass_xt_iclass_wsr_ccompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccompare1_args, + 4, Iclass_xt_iclass_xsr_ccompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccompare2_args, + 3, Iclass_xt_iclass_rsr_ccompare2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccompare2_args, + 4, Iclass_xt_iclass_wsr_ccompare2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccompare2_args, + 4, Iclass_xt_iclass_xsr_ccompare2_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_icache_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_icache_lock_args, + 2, Iclass_xt_iclass_icache_lock_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_icache_inv_args, + 2, Iclass_xt_iclass_icache_inv_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_licx_args, + 2, Iclass_xt_iclass_licx_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_sicx_args, + 2, Iclass_xt_iclass_sicx_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_dcache_dyn_args, + 2, Iclass_xt_iclass_dcache_dyn_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_ind_args, + 2, Iclass_xt_iclass_dcache_ind_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_inv_args, + 2, Iclass_xt_iclass_dcache_inv_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dpf_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_lock_args, + 2, Iclass_xt_iclass_dcache_lock_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_sdct_args, + 2, Iclass_xt_iclass_sdct_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_ldct_args, + 2, Iclass_xt_iclass_ldct_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_prefctl_args, + 1, Iclass_xt_iclass_rsr_prefctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_prefctl_args, + 1, Iclass_xt_iclass_wsr_prefctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_prefctl_args, + 1, Iclass_xt_iclass_xsr_prefctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ptevaddr_args, + 4, Iclass_xt_iclass_wsr_ptevaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ptevaddr_args, + 4, Iclass_xt_iclass_rsr_ptevaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ptevaddr_args, + 5, Iclass_xt_iclass_xsr_ptevaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_rasid_args, + 5, Iclass_xt_iclass_rsr_rasid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_rasid_args, + 6, Iclass_xt_iclass_wsr_rasid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_rasid_args, + 6, Iclass_xt_iclass_xsr_rasid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_itlbcfg_args, + 5, Iclass_xt_iclass_rsr_itlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_itlbcfg_args, + 6, Iclass_xt_iclass_wsr_itlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_itlbcfg_args, + 6, Iclass_xt_iclass_xsr_itlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_dtlbcfg_args, + 5, Iclass_xt_iclass_rsr_dtlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_dtlbcfg_args, + 6, Iclass_xt_iclass_wsr_dtlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_dtlbcfg_args, + 6, Iclass_xt_iclass_xsr_dtlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_idtlb_args, + 3, Iclass_xt_iclass_idtlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_rdtlb_args, + 2, Iclass_xt_iclass_rdtlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_wdtlb_args, + 3, Iclass_xt_iclass_wdtlb_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_iitlb_args, + 2, Iclass_xt_iclass_iitlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_ritlb_args, + 2, Iclass_xt_iclass_ritlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_witlb_args, + 2, Iclass_xt_iclass_witlb_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_ldpte */, + 2, Iclass_xt_iclass_ldpte_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_hwwitlba */, + 1, Iclass_xt_iclass_hwwitlba_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_hwwdtlba */, + 1, Iclass_xt_iclass_hwwdtlba_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_cpenable_args, + 3, Iclass_xt_iclass_rsr_cpenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_cpenable_args, + 3, Iclass_xt_iclass_wsr_cpenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_cpenable_args, + 3, Iclass_xt_iclass_xsr_cpenable_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_clamp_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_minmax_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_nsa_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_sx_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l32ai_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32ri_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32c1i_args, + 3, Iclass_xt_iclass_s32c1i_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_scompare1_args, + 1, Iclass_xt_iclass_rsr_scompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_scompare1_args, + 1, Iclass_xt_iclass_wsr_scompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_scompare1_args, + 1, Iclass_xt_iclass_xsr_scompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_atomctl_args, + 3, Iclass_xt_iclass_rsr_atomctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_atomctl_args, + 4, Iclass_xt_iclass_wsr_atomctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_atomctl_args, + 4, Iclass_xt_iclass_xsr_atomctl_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_div_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_rer_args, + 3, Iclass_xt_iclass_rer_stateArgs, 2, Iclass_xt_iclass_rer_intfArgs }, + { 2, Iclass_xt_iclass_wer_args, + 3, Iclass_xt_iclass_wer_stateArgs, 2, Iclass_xt_iclass_wer_intfArgs }, + { 1, Iclass_rur_ae_ovf_sar_args, + 3, Iclass_rur_ae_ovf_sar_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_ovf_sar_args, + 3, Iclass_wur_ae_ovf_sar_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_bithead_args, + 2, Iclass_rur_ae_bithead_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_bithead_args, + 2, Iclass_wur_ae_bithead_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_ts_fts_bu_bp_args, + 5, Iclass_rur_ae_ts_fts_bu_bp_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_ts_fts_bu_bp_args, + 5, Iclass_wur_ae_ts_fts_bu_bp_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_sd_no_args, + 3, Iclass_rur_ae_sd_no_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_sd_no_args, + 3, Iclass_wur_ae_sd_no_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_overflow_args, + 2, Iclass_ae_iclass_rur_ae_overflow_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_overflow_args, + 2, Iclass_ae_iclass_wur_ae_overflow_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_sar_args, + 2, Iclass_ae_iclass_rur_ae_sar_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_sar_args, + 2, Iclass_ae_iclass_wur_ae_sar_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_bitptr_args, + 2, Iclass_ae_iclass_rur_ae_bitptr_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_bitptr_args, + 2, Iclass_ae_iclass_wur_ae_bitptr_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_bitsused_args, + 2, Iclass_ae_iclass_rur_ae_bitsused_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_bitsused_args, + 2, Iclass_ae_iclass_wur_ae_bitsused_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_tablesize_args, + 2, Iclass_ae_iclass_rur_ae_tablesize_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_tablesize_args, + 2, Iclass_ae_iclass_wur_ae_tablesize_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_first_ts_args, + 2, Iclass_ae_iclass_rur_ae_first_ts_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_first_ts_args, + 2, Iclass_ae_iclass_wur_ae_first_ts_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_nextoffset_args, + 2, Iclass_ae_iclass_rur_ae_nextoffset_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_nextoffset_args, + 2, Iclass_ae_iclass_wur_ae_nextoffset_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_searchdone_args, + 2, Iclass_ae_iclass_rur_ae_searchdone_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_searchdone_args, + 2, Iclass_ae_iclass_wur_ae_searchdone_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_i_args, + 1, Iclass_ae_iclass_lp16f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_iu_args, + 1, Iclass_ae_iclass_lp16f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_x_args, + 1, Iclass_ae_iclass_lp16f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_xu_args, + 1, Iclass_ae_iclass_lp16f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_i_args, + 1, Iclass_ae_iclass_lp24_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_iu_args, + 1, Iclass_ae_iclass_lp24_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_x_args, + 1, Iclass_ae_iclass_lp24_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_xu_args, + 1, Iclass_ae_iclass_lp24_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_i_args, + 1, Iclass_ae_iclass_lp24f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_iu_args, + 1, Iclass_ae_iclass_lp24f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_x_args, + 1, Iclass_ae_iclass_lp24f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_xu_args, + 1, Iclass_ae_iclass_lp24f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_i_args, + 1, Iclass_ae_iclass_lp16x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_iu_args, + 1, Iclass_ae_iclass_lp16x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_x_args, + 1, Iclass_ae_iclass_lp16x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_xu_args, + 1, Iclass_ae_iclass_lp16x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_i_args, + 1, Iclass_ae_iclass_lp24x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_iu_args, + 1, Iclass_ae_iclass_lp24x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_x_args, + 1, Iclass_ae_iclass_lp24x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_xu_args, + 1, Iclass_ae_iclass_lp24x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_i_args, + 1, Iclass_ae_iclass_lp24x2_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_iu_args, + 1, Iclass_ae_iclass_lp24x2_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_x_args, + 1, Iclass_ae_iclass_lp24x2_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_xu_args, + 1, Iclass_ae_iclass_lp24x2_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_i_args, + 1, Iclass_ae_iclass_sp16x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_iu_args, + 1, Iclass_ae_iclass_sp16x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_x_args, + 1, Iclass_ae_iclass_sp16x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_xu_args, + 1, Iclass_ae_iclass_sp16x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_i_args, + 1, Iclass_ae_iclass_sp24x2s_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_iu_args, + 1, Iclass_ae_iclass_sp24x2s_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_x_args, + 1, Iclass_ae_iclass_sp24x2s_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_xu_args, + 1, Iclass_ae_iclass_sp24x2s_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_i_args, + 1, Iclass_ae_iclass_sp24x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_iu_args, + 1, Iclass_ae_iclass_sp24x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_x_args, + 1, Iclass_ae_iclass_sp24x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_xu_args, + 1, Iclass_ae_iclass_sp24x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_i_args, + 1, Iclass_ae_iclass_sp16f_l_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_iu_args, + 1, Iclass_ae_iclass_sp16f_l_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_x_args, + 1, Iclass_ae_iclass_sp16f_l_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_xu_args, + 1, Iclass_ae_iclass_sp16f_l_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_i_args, + 1, Iclass_ae_iclass_sp24s_l_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_iu_args, + 1, Iclass_ae_iclass_sp24s_l_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_x_args, + 1, Iclass_ae_iclass_sp24s_l_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_xu_args, + 1, Iclass_ae_iclass_sp24s_l_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_i_args, + 1, Iclass_ae_iclass_sp24f_l_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_iu_args, + 1, Iclass_ae_iclass_sp24f_l_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_x_args, + 1, Iclass_ae_iclass_sp24f_l_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_xu_args, + 1, Iclass_ae_iclass_sp24f_l_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_i_args, + 1, Iclass_ae_iclass_lq56_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_iu_args, + 1, Iclass_ae_iclass_lq56_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_x_args, + 1, Iclass_ae_iclass_lq56_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_xu_args, + 1, Iclass_ae_iclass_lq56_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_i_args, + 1, Iclass_ae_iclass_lq32f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_iu_args, + 1, Iclass_ae_iclass_lq32f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_x_args, + 1, Iclass_ae_iclass_lq32f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_xu_args, + 1, Iclass_ae_iclass_lq32f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_i_args, + 1, Iclass_ae_iclass_sq56s_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_iu_args, + 1, Iclass_ae_iclass_sq56s_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_x_args, + 1, Iclass_ae_iclass_sq56s_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_xu_args, + 1, Iclass_ae_iclass_sq56s_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_i_args, + 1, Iclass_ae_iclass_sq32f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_iu_args, + 1, Iclass_ae_iclass_sq32f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_x_args, + 1, Iclass_ae_iclass_sq32f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_xu_args, + 1, Iclass_ae_iclass_sq32f_xu_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_zerop48_args, + 1, Iclass_ae_iclass_zerop48_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movp48_args, + 1, Iclass_ae_iclass_movp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_ll_args, + 1, Iclass_ae_iclass_selp24_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_lh_args, + 1, Iclass_ae_iclass_selp24_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_hl_args, + 1, Iclass_ae_iclass_selp24_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_hh_args, + 1, Iclass_ae_iclass_selp24_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movtp24x2_args, + 1, Iclass_ae_iclass_movtp24x2_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movfp24x2_args, + 1, Iclass_ae_iclass_movfp24x2_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movtp48_args, + 1, Iclass_ae_iclass_movtp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movfp48_args, + 1, Iclass_ae_iclass_movfp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movpa24x2_args, + 1, Iclass_ae_iclass_movpa24x2_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_truncp24a32x2_args, + 1, Iclass_ae_iclass_truncp24a32x2_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvta32p24_l_args, + 1, Iclass_ae_iclass_cvta32p24_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvta32p24_h_args, + 1, Iclass_ae_iclass_cvta32p24_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_ll_args, + 1, Iclass_ae_iclass_cvtp24a16x2_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_lh_args, + 1, Iclass_ae_iclass_cvtp24a16x2_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_hl_args, + 1, Iclass_ae_iclass_cvtp24a16x2_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_hh_args, + 1, Iclass_ae_iclass_cvtp24a16x2_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_truncp24q48x2_args, + 1, Iclass_ae_iclass_truncp24q48x2_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_truncp16_args, + 1, Iclass_ae_iclass_truncp16_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp24q48sym_args, + 2, Iclass_ae_iclass_roundsp24q48sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp24q48asym_args, + 2, Iclass_ae_iclass_roundsp24q48asym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16q48sym_args, + 2, Iclass_ae_iclass_roundsp16q48sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16q48asym_args, + 2, Iclass_ae_iclass_roundsp16q48asym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16sym_args, + 2, Iclass_ae_iclass_roundsp16sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16asym_args, + 2, Iclass_ae_iclass_roundsp16asym_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_zeroq56_args, + 1, Iclass_ae_iclass_zeroq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movq56_args, + 1, Iclass_ae_iclass_movq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movtq56_args, + 1, Iclass_ae_iclass_movtq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movfq56_args, + 1, Iclass_ae_iclass_movfq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvtq48a32s_args, + 1, Iclass_ae_iclass_cvtq48a32s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvtq48p24s_l_args, + 1, Iclass_ae_iclass_cvtq48p24s_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvtq48p24s_h_args, + 1, Iclass_ae_iclass_cvtq48p24s_h_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_satq48s_args, + 2, Iclass_ae_iclass_satq48s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_truncq32_args, + 1, Iclass_ae_iclass_truncq32_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsq32sym_args, + 2, Iclass_ae_iclass_roundsq32sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsq32asym_args, + 2, Iclass_ae_iclass_roundsq32asym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_trunca32q48_args, + 1, Iclass_ae_iclass_trunca32q48_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movap24s_l_args, + 1, Iclass_ae_iclass_movap24s_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movap24s_h_args, + 1, Iclass_ae_iclass_movap24s_h_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_trunca16p24s_l_args, + 1, Iclass_ae_iclass_trunca16p24s_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_trunca16p24s_h_args, + 1, Iclass_ae_iclass_trunca16p24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addp24_args, + 1, Iclass_ae_iclass_addp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subp24_args, + 1, Iclass_ae_iclass_subp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negp24_args, + 1, Iclass_ae_iclass_negp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_absp24_args, + 1, Iclass_ae_iclass_absp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_maxp24s_args, + 1, Iclass_ae_iclass_maxp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_minp24s_args, + 1, Iclass_ae_iclass_minp24s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_maxbp24s_args, + 1, Iclass_ae_iclass_maxbp24s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_minbp24s_args, + 1, Iclass_ae_iclass_minbp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addsp24s_args, + 2, Iclass_ae_iclass_addsp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subsp24s_args, + 2, Iclass_ae_iclass_subsp24s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negsp24s_args, + 2, Iclass_ae_iclass_negsp24s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_abssp24s_args, + 2, Iclass_ae_iclass_abssp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_andp48_args, + 1, Iclass_ae_iclass_andp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_nandp48_args, + 1, Iclass_ae_iclass_nandp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_orp48_args, + 1, Iclass_ae_iclass_orp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_xorp48_args, + 1, Iclass_ae_iclass_xorp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_ltp24s_args, + 1, Iclass_ae_iclass_ltp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lep24s_args, + 1, Iclass_ae_iclass_lep24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_eqp24_args, + 1, Iclass_ae_iclass_eqp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addq56_args, + 1, Iclass_ae_iclass_addq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subq56_args, + 1, Iclass_ae_iclass_subq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negq56_args, + 1, Iclass_ae_iclass_negq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_absq56_args, + 1, Iclass_ae_iclass_absq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_maxq56s_args, + 1, Iclass_ae_iclass_maxq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_minq56s_args, + 1, Iclass_ae_iclass_minq56s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_maxbq56s_args, + 1, Iclass_ae_iclass_maxbq56s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_minbq56s_args, + 1, Iclass_ae_iclass_minbq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addsq56s_args, + 2, Iclass_ae_iclass_addsq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subsq56s_args, + 2, Iclass_ae_iclass_subsq56s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negsq56s_args, + 2, Iclass_ae_iclass_negsq56s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_abssq56s_args, + 2, Iclass_ae_iclass_abssq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_andq56_args, + 1, Iclass_ae_iclass_andq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_nandq56_args, + 1, Iclass_ae_iclass_nandq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_orq56_args, + 1, Iclass_ae_iclass_orq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_xorq56_args, + 1, Iclass_ae_iclass_xorq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllip24_args, + 1, Iclass_ae_iclass_sllip24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_srlip24_args, + 1, Iclass_ae_iclass_srlip24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sraip24_args, + 1, Iclass_ae_iclass_sraip24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllsp24_args, + 2, Iclass_ae_iclass_sllsp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srlsp24_args, + 2, Iclass_ae_iclass_srlsp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srasp24_args, + 2, Iclass_ae_iclass_srasp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllisp24s_args, + 2, Iclass_ae_iclass_sllisp24s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllssp24s_args, + 3, Iclass_ae_iclass_sllssp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_slliq56_args, + 1, Iclass_ae_iclass_slliq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_srliq56_args, + 1, Iclass_ae_iclass_srliq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sraiq56_args, + 1, Iclass_ae_iclass_sraiq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllsq56_args, + 2, Iclass_ae_iclass_sllsq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srlsq56_args, + 2, Iclass_ae_iclass_srlsq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srasq56_args, + 2, Iclass_ae_iclass_srasq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllaq56_args, + 1, Iclass_ae_iclass_sllaq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_srlaq56_args, + 1, Iclass_ae_iclass_srlaq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sraaq56_args, + 1, Iclass_ae_iclass_sraaq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllisq56s_args, + 2, Iclass_ae_iclass_sllisq56s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllssq56s_args, + 3, Iclass_ae_iclass_sllssq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllasq56s_args, + 2, Iclass_ae_iclass_sllasq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_ltq56s_args, + 1, Iclass_ae_iclass_ltq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_leq56s_args, + 1, Iclass_ae_iclass_leq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_eqq56_args, + 1, Iclass_ae_iclass_eqq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_nsaq56s_args, + 1, Iclass_ae_iclass_nsaq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsrfq32sp24s_h_args, + 1, Iclass_ae_iclass_mulsrfq32sp24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsrfq32sp24s_l_args, + 1, Iclass_ae_iclass_mulsrfq32sp24s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mularfq32sp24s_h_args, + 1, Iclass_ae_iclass_mularfq32sp24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mularfq32sp24s_l_args, + 1, Iclass_ae_iclass_mularfq32sp24s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulrfq32sp24s_h_args, + 1, Iclass_ae_iclass_mulrfq32sp24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulrfq32sp24s_l_args, + 1, Iclass_ae_iclass_mulrfq32sp24s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp24s_h_args, + 1, Iclass_ae_iclass_mulsfq32sp24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp24s_l_args, + 1, Iclass_ae_iclass_mulsfq32sp24s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp24s_h_args, + 1, Iclass_ae_iclass_mulafq32sp24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp24s_l_args, + 1, Iclass_ae_iclass_mulafq32sp24s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp24s_h_args, + 1, Iclass_ae_iclass_mulfq32sp24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp24s_l_args, + 1, Iclass_ae_iclass_mulfq32sp24s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_ll_args, + 2, Iclass_ae_iclass_mulfs32p16s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_ll_args, + 1, Iclass_ae_iclass_mulfp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_ll_args, + 1, Iclass_ae_iclass_mulp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_lh_args, + 2, Iclass_ae_iclass_mulfs32p16s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_lh_args, + 1, Iclass_ae_iclass_mulfp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_lh_args, + 1, Iclass_ae_iclass_mulp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_hl_args, + 2, Iclass_ae_iclass_mulfs32p16s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_hl_args, + 1, Iclass_ae_iclass_mulfp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_hl_args, + 1, Iclass_ae_iclass_mulp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_hh_args, + 2, Iclass_ae_iclass_mulfs32p16s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_hh_args, + 1, Iclass_ae_iclass_mulfp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_hh_args, + 1, Iclass_ae_iclass_mulp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_ll_args, + 2, Iclass_ae_iclass_mulafs32p16s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_ll_args, + 1, Iclass_ae_iclass_mulafp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_ll_args, + 1, Iclass_ae_iclass_mulap24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_lh_args, + 2, Iclass_ae_iclass_mulafs32p16s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_lh_args, + 1, Iclass_ae_iclass_mulafp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_lh_args, + 1, Iclass_ae_iclass_mulap24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_hl_args, + 2, Iclass_ae_iclass_mulafs32p16s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_hl_args, + 1, Iclass_ae_iclass_mulafp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_hl_args, + 1, Iclass_ae_iclass_mulap24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_hh_args, + 2, Iclass_ae_iclass_mulafs32p16s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_hh_args, + 1, Iclass_ae_iclass_mulafp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_hh_args, + 1, Iclass_ae_iclass_mulap24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_ll_args, + 2, Iclass_ae_iclass_mulsfs32p16s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_ll_args, + 1, Iclass_ae_iclass_mulsfp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_ll_args, + 1, Iclass_ae_iclass_mulsp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_lh_args, + 2, Iclass_ae_iclass_mulsfs32p16s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_lh_args, + 1, Iclass_ae_iclass_mulsfp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_lh_args, + 1, Iclass_ae_iclass_mulsp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_hl_args, + 2, Iclass_ae_iclass_mulsfs32p16s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_hl_args, + 1, Iclass_ae_iclass_mulsfp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_hl_args, + 1, Iclass_ae_iclass_mulsp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_hh_args, + 2, Iclass_ae_iclass_mulsfs32p16s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_hh_args, + 1, Iclass_ae_iclass_mulsfp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_hh_args, + 1, Iclass_ae_iclass_mulsp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_ll_args, + 2, Iclass_ae_iclass_mulafs56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_ll_args, + 2, Iclass_ae_iclass_mulas56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_lh_args, + 2, Iclass_ae_iclass_mulafs56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_lh_args, + 2, Iclass_ae_iclass_mulas56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_hl_args, + 2, Iclass_ae_iclass_mulafs56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_hl_args, + 2, Iclass_ae_iclass_mulas56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_hh_args, + 2, Iclass_ae_iclass_mulafs56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_hh_args, + 2, Iclass_ae_iclass_mulas56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_ll_args, + 2, Iclass_ae_iclass_mulsfs56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_ll_args, + 2, Iclass_ae_iclass_mulss56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_lh_args, + 2, Iclass_ae_iclass_mulsfs56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_lh_args, + 2, Iclass_ae_iclass_mulss56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_hl_args, + 2, Iclass_ae_iclass_mulsfs56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_hl_args, + 2, Iclass_ae_iclass_mulss56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_hh_args, + 2, Iclass_ae_iclass_mulsfs56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_hh_args, + 2, Iclass_ae_iclass_mulss56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16s_l_args, + 1, Iclass_ae_iclass_mulfq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16s_h_args, + 1, Iclass_ae_iclass_mulfq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16u_l_args, + 1, Iclass_ae_iclass_mulfq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16u_h_args, + 1, Iclass_ae_iclass_mulfq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16s_l_args, + 1, Iclass_ae_iclass_mulq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16s_h_args, + 1, Iclass_ae_iclass_mulq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16u_l_args, + 1, Iclass_ae_iclass_mulq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16u_h_args, + 1, Iclass_ae_iclass_mulq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16s_l_args, + 1, Iclass_ae_iclass_mulafq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16s_h_args, + 1, Iclass_ae_iclass_mulafq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16u_l_args, + 1, Iclass_ae_iclass_mulafq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16u_h_args, + 1, Iclass_ae_iclass_mulafq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16s_l_args, + 1, Iclass_ae_iclass_mulaq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16s_h_args, + 1, Iclass_ae_iclass_mulaq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16u_l_args, + 1, Iclass_ae_iclass_mulaq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16u_h_args, + 1, Iclass_ae_iclass_mulaq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16s_l_args, + 1, Iclass_ae_iclass_mulsfq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16s_h_args, + 1, Iclass_ae_iclass_mulsfq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16u_l_args, + 1, Iclass_ae_iclass_mulsfq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16u_h_args, + 1, Iclass_ae_iclass_mulsfq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16s_l_args, + 1, Iclass_ae_iclass_mulsq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16s_h_args, + 1, Iclass_ae_iclass_mulsq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16u_l_args, + 1, Iclass_ae_iclass_mulsq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16u_h_args, + 1, Iclass_ae_iclass_mulsq32sp16u_h_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzaaq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzaafq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzaaq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzaafq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzasq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzasfq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzasq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzasfq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzasq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzasq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzasq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzasq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzsaq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzsafq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzsaq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzsafq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzssq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzssfq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzssq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzssfq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzssq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzssq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzssq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzssq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16u_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzaafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzaap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzaafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzaap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzasfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzasp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzasfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzasp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzsafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzsap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzsafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzsap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzssfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzssp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzssfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzssp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulaafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulaap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulaafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulaap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulasfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulasp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulasfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulasp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulsafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulsap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulsafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulsap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulssfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulssp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulssfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulssp24s_hl_lh_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sha32_args, + 0, 0, 0, 0 }, + { 3, Iclass_ae_iclass_vldl32t_args, + 5, Iclass_ae_iclass_vldl32t_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_vldl16t_args, + 5, Iclass_ae_iclass_vldl16t_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_vldl16c_args, + 8, Iclass_ae_iclass_vldl16c_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_vldsht_args, + 6, Iclass_ae_iclass_vldsht_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_lb_args, + 3, Iclass_ae_iclass_lb_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_lbi_args, + 3, Iclass_ae_iclass_lbi_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lbk_args, + 3, Iclass_ae_iclass_lbk_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lbki_args, + 3, Iclass_ae_iclass_lbki_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_db_args, + 3, Iclass_ae_iclass_db_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_dbi_args, + 3, Iclass_ae_iclass_dbi_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_vlel32t_args, + 3, Iclass_ae_iclass_vlel32t_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_vlel16t_args, + 3, Iclass_ae_iclass_vlel16t_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sb_args, + 4, Iclass_ae_iclass_sb_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sbi_args, + 3, Iclass_ae_iclass_sbi_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_vles16c_args, + 5, Iclass_ae_iclass_vles16c_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_sbf_args, + 3, Iclass_ae_iclass_sbf_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SLAASQ56S_args, + 2, Iclass_icls_AE_SLAASQ56S_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_ADDBRBA32_args, + 0, 0, 0, 0 }, + { 3, Iclass_icls_AE_MINABSSP24S_args, + 2, Iclass_icls_AE_MINABSSP24S_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_MAXABSSP24S_args, + 2, Iclass_icls_AE_MAXABSSP24S_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_MINABSSQ56S_args, + 2, Iclass_icls_AE_MINABSSQ56S_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_MAXABSSQ56S_args, + 2, Iclass_icls_AE_MAXABSSQ56S_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_cbegin0_args, + 2, Iclass_rur_ae_cbegin0_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_cbegin0_args, + 2, Iclass_wur_ae_cbegin0_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_cend0_args, + 2, Iclass_rur_ae_cend0_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_cend0_args, + 2, Iclass_wur_ae_cend0_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LP24X2_C_args, + 3, Iclass_icls_AE_LP24X2_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SP24X2S_C_args, + 3, Iclass_icls_AE_SP24X2S_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LP24X2F_C_args, + 3, Iclass_icls_AE_LP24X2F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SP24X2F_C_args, + 3, Iclass_icls_AE_SP24X2F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LP16X2F_C_args, + 3, Iclass_icls_AE_LP16X2F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SP16X2F_C_args, + 3, Iclass_icls_AE_SP16X2F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LP24_C_args, + 3, Iclass_icls_AE_LP24_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SP24S_L_C_args, + 3, Iclass_icls_AE_SP24S_L_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LP24F_C_args, + 3, Iclass_icls_AE_LP24F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SP24F_L_C_args, + 3, Iclass_icls_AE_SP24F_L_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LP16F_C_args, + 3, Iclass_icls_AE_LP16F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SP16F_L_C_args, + 3, Iclass_icls_AE_SP16F_L_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LQ56_C_args, + 3, Iclass_icls_AE_LQ56_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SQ56S_C_args, + 3, Iclass_icls_AE_SQ56S_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_LQ32F_C_args, + 3, Iclass_icls_AE_LQ32F_C_stateArgs, 0, 0 }, + { 3, Iclass_icls_AE_SQ32F_C_args, + 3, Iclass_icls_AE_SQ32F_C_stateArgs, 0, 0 }, + { 1, Iclass_rur_expstate_args, + 2, Iclass_rur_expstate_stateArgs, 0, 0 }, + { 1, Iclass_wur_expstate_args, + 2, Iclass_wur_expstate_stateArgs, 0, 0 }, + { 1, Iclass_iclass_READ_IMPWIRE_args, + 1, Iclass_iclass_READ_IMPWIRE_stateArgs, 1, Iclass_iclass_READ_IMPWIRE_intfArgs }, + { 1, Iclass_iclass_SETB_EXPSTATE_args, + 2, Iclass_iclass_SETB_EXPSTATE_stateArgs, 0, 0 }, + { 1, Iclass_iclass_CLRB_EXPSTATE_args, + 2, Iclass_iclass_CLRB_EXPSTATE_stateArgs, 0, 0 }, + { 2, Iclass_iclass_WRMSK_EXPSTATE_args, + 2, Iclass_iclass_WRMSK_EXPSTATE_stateArgs, 0, 0 } +}; + +enum xtensa_iclass_id { + ICLASS_xt_iclass_excw, + ICLASS_xt_iclass_rfe, + ICLASS_xt_iclass_rfde, + ICLASS_xt_iclass_syscall, + ICLASS_xt_iclass_call12, + ICLASS_xt_iclass_call8, + ICLASS_xt_iclass_call4, + ICLASS_xt_iclass_callx12, + ICLASS_xt_iclass_callx8, + ICLASS_xt_iclass_callx4, + ICLASS_xt_iclass_entry, + ICLASS_xt_iclass_movsp, + ICLASS_xt_iclass_rotw, + ICLASS_xt_iclass_retw, + ICLASS_xt_iclass_rfwou, + ICLASS_xt_iclass_l32e, + ICLASS_xt_iclass_s32e, + ICLASS_xt_iclass_rsr_windowbase, + ICLASS_xt_iclass_wsr_windowbase, + ICLASS_xt_iclass_xsr_windowbase, + ICLASS_xt_iclass_rsr_windowstart, + ICLASS_xt_iclass_wsr_windowstart, + ICLASS_xt_iclass_xsr_windowstart, + ICLASS_xt_iclass_add_n, + ICLASS_xt_iclass_addi_n, + ICLASS_xt_iclass_bz6, + ICLASS_xt_iclass_ill_n, + ICLASS_xt_iclass_loadi4, + ICLASS_xt_iclass_mov_n, + ICLASS_xt_iclass_movi_n, + ICLASS_xt_iclass_nopn, + ICLASS_xt_iclass_retn, + ICLASS_xt_iclass_storei4, + ICLASS_rur_threadptr, + ICLASS_wur_threadptr, + ICLASS_xt_iclass_addi, + ICLASS_xt_iclass_addmi, + ICLASS_xt_iclass_addsub, + ICLASS_xt_iclass_bit, + ICLASS_xt_iclass_bsi8, + ICLASS_xt_iclass_bsi8b, + ICLASS_xt_iclass_bsi8u, + ICLASS_xt_iclass_bst8, + ICLASS_xt_iclass_bsz12, + ICLASS_xt_iclass_call0, + ICLASS_xt_iclass_callx0, + ICLASS_xt_iclass_exti, + ICLASS_xt_iclass_ill, + ICLASS_xt_iclass_jump, + ICLASS_xt_iclass_jumpx, + ICLASS_xt_iclass_l16ui, + ICLASS_xt_iclass_l16si, + ICLASS_xt_iclass_l32i, + ICLASS_xt_iclass_l32r, + ICLASS_xt_iclass_l8i, + ICLASS_xt_iclass_loop, + ICLASS_xt_iclass_loopz, + ICLASS_xt_iclass_movi, + ICLASS_xt_iclass_movz, + ICLASS_xt_iclass_neg, + ICLASS_xt_iclass_nop, + ICLASS_xt_iclass_return, + ICLASS_xt_iclass_simcall, + ICLASS_xt_iclass_s16i, + ICLASS_xt_iclass_s32i, + ICLASS_xt_iclass_s32nb, + ICLASS_xt_iclass_s8i, + ICLASS_xt_iclass_sar, + ICLASS_xt_iclass_sari, + ICLASS_xt_iclass_shifts, + ICLASS_xt_iclass_shiftst, + ICLASS_xt_iclass_shiftt, + ICLASS_xt_iclass_slli, + ICLASS_xt_iclass_srai, + ICLASS_xt_iclass_srli, + ICLASS_xt_iclass_memw, + ICLASS_xt_iclass_extw, + ICLASS_xt_iclass_isync, + ICLASS_xt_iclass_sync, + ICLASS_xt_iclass_rsil, + ICLASS_xt_iclass_rsr_lend, + ICLASS_xt_iclass_wsr_lend, + ICLASS_xt_iclass_xsr_lend, + ICLASS_xt_iclass_rsr_lcount, + ICLASS_xt_iclass_wsr_lcount, + ICLASS_xt_iclass_xsr_lcount, + ICLASS_xt_iclass_rsr_lbeg, + ICLASS_xt_iclass_wsr_lbeg, + ICLASS_xt_iclass_xsr_lbeg, + ICLASS_xt_iclass_rsr_sar, + ICLASS_xt_iclass_wsr_sar, + ICLASS_xt_iclass_xsr_sar, + ICLASS_xt_iclass_rsr_memctl, + ICLASS_xt_iclass_wsr_memctl, + ICLASS_xt_iclass_xsr_memctl, + ICLASS_xt_iclass_rsr_litbase, + ICLASS_xt_iclass_wsr_litbase, + ICLASS_xt_iclass_xsr_litbase, + ICLASS_xt_iclass_rsr_configid0, + ICLASS_xt_iclass_wsr_configid0, + ICLASS_xt_iclass_rsr_configid1, + ICLASS_xt_iclass_rsr_243, + ICLASS_xt_iclass_rsr_ps, + ICLASS_xt_iclass_wsr_ps, + ICLASS_xt_iclass_xsr_ps, + ICLASS_xt_iclass_rsr_epc1, + ICLASS_xt_iclass_wsr_epc1, + ICLASS_xt_iclass_xsr_epc1, + ICLASS_xt_iclass_rsr_excsave1, + ICLASS_xt_iclass_wsr_excsave1, + ICLASS_xt_iclass_xsr_excsave1, + ICLASS_xt_iclass_rsr_epc2, + ICLASS_xt_iclass_wsr_epc2, + ICLASS_xt_iclass_xsr_epc2, + ICLASS_xt_iclass_rsr_excsave2, + ICLASS_xt_iclass_wsr_excsave2, + ICLASS_xt_iclass_xsr_excsave2, + ICLASS_xt_iclass_rsr_epc3, + ICLASS_xt_iclass_wsr_epc3, + ICLASS_xt_iclass_xsr_epc3, + ICLASS_xt_iclass_rsr_excsave3, + ICLASS_xt_iclass_wsr_excsave3, + ICLASS_xt_iclass_xsr_excsave3, + ICLASS_xt_iclass_rsr_epc4, + ICLASS_xt_iclass_wsr_epc4, + ICLASS_xt_iclass_xsr_epc4, + ICLASS_xt_iclass_rsr_excsave4, + ICLASS_xt_iclass_wsr_excsave4, + ICLASS_xt_iclass_xsr_excsave4, + ICLASS_xt_iclass_rsr_epc5, + ICLASS_xt_iclass_wsr_epc5, + ICLASS_xt_iclass_xsr_epc5, + ICLASS_xt_iclass_rsr_excsave5, + ICLASS_xt_iclass_wsr_excsave5, + ICLASS_xt_iclass_xsr_excsave5, + ICLASS_xt_iclass_rsr_epc6, + ICLASS_xt_iclass_wsr_epc6, + ICLASS_xt_iclass_xsr_epc6, + ICLASS_xt_iclass_rsr_excsave6, + ICLASS_xt_iclass_wsr_excsave6, + ICLASS_xt_iclass_xsr_excsave6, + ICLASS_xt_iclass_rsr_epc7, + ICLASS_xt_iclass_wsr_epc7, + ICLASS_xt_iclass_xsr_epc7, + ICLASS_xt_iclass_rsr_excsave7, + ICLASS_xt_iclass_wsr_excsave7, + ICLASS_xt_iclass_xsr_excsave7, + ICLASS_xt_iclass_rsr_eps2, + ICLASS_xt_iclass_wsr_eps2, + ICLASS_xt_iclass_xsr_eps2, + ICLASS_xt_iclass_rsr_eps3, + ICLASS_xt_iclass_wsr_eps3, + ICLASS_xt_iclass_xsr_eps3, + ICLASS_xt_iclass_rsr_eps4, + ICLASS_xt_iclass_wsr_eps4, + ICLASS_xt_iclass_xsr_eps4, + ICLASS_xt_iclass_rsr_eps5, + ICLASS_xt_iclass_wsr_eps5, + ICLASS_xt_iclass_xsr_eps5, + ICLASS_xt_iclass_rsr_eps6, + ICLASS_xt_iclass_wsr_eps6, + ICLASS_xt_iclass_xsr_eps6, + ICLASS_xt_iclass_rsr_eps7, + ICLASS_xt_iclass_wsr_eps7, + ICLASS_xt_iclass_xsr_eps7, + ICLASS_xt_iclass_rsr_excvaddr, + ICLASS_xt_iclass_wsr_excvaddr, + ICLASS_xt_iclass_xsr_excvaddr, + ICLASS_xt_iclass_rsr_depc, + ICLASS_xt_iclass_wsr_depc, + ICLASS_xt_iclass_xsr_depc, + ICLASS_xt_iclass_rsr_exccause, + ICLASS_xt_iclass_wsr_exccause, + ICLASS_xt_iclass_xsr_exccause, + ICLASS_xt_iclass_rsr_misc0, + ICLASS_xt_iclass_wsr_misc0, + ICLASS_xt_iclass_xsr_misc0, + ICLASS_xt_iclass_rsr_misc1, + ICLASS_xt_iclass_wsr_misc1, + ICLASS_xt_iclass_xsr_misc1, + ICLASS_xt_iclass_rsr_prid, + ICLASS_xt_iclass_rsr_vecbase, + ICLASS_xt_iclass_wsr_vecbase, + ICLASS_xt_iclass_xsr_vecbase, + ICLASS_xt_mul16, + ICLASS_xt_mul32, + ICLASS_xt_mul32h, + ICLASS_xt_iclass_mac16_aa, + ICLASS_xt_iclass_mac16_ad, + ICLASS_xt_iclass_mac16_da, + ICLASS_xt_iclass_mac16_dd, + ICLASS_xt_iclass_mac16a_aa, + ICLASS_xt_iclass_mac16a_ad, + ICLASS_xt_iclass_mac16a_da, + ICLASS_xt_iclass_mac16a_dd, + ICLASS_xt_iclass_mac16al_da, + ICLASS_xt_iclass_mac16al_dd, + ICLASS_xt_iclass_mac16_l, + ICLASS_xt_iclass_rsr_m0, + ICLASS_xt_iclass_wsr_m0, + ICLASS_xt_iclass_xsr_m0, + ICLASS_xt_iclass_rsr_m1, + ICLASS_xt_iclass_wsr_m1, + ICLASS_xt_iclass_xsr_m1, + ICLASS_xt_iclass_rsr_m2, + ICLASS_xt_iclass_wsr_m2, + ICLASS_xt_iclass_xsr_m2, + ICLASS_xt_iclass_rsr_m3, + ICLASS_xt_iclass_wsr_m3, + ICLASS_xt_iclass_xsr_m3, + ICLASS_xt_iclass_rsr_acclo, + ICLASS_xt_iclass_wsr_acclo, + ICLASS_xt_iclass_xsr_acclo, + ICLASS_xt_iclass_rsr_acchi, + ICLASS_xt_iclass_wsr_acchi, + ICLASS_xt_iclass_xsr_acchi, + ICLASS_xt_iclass_rfi, + ICLASS_xt_iclass_wait, + ICLASS_xt_iclass_rsr_interrupt, + ICLASS_xt_iclass_wsr_intset, + ICLASS_xt_iclass_wsr_intclear, + ICLASS_xt_iclass_rsr_intenable, + ICLASS_xt_iclass_wsr_intenable, + ICLASS_xt_iclass_xsr_intenable, + ICLASS_xt_iclass_break, + ICLASS_xt_iclass_break_n, + ICLASS_xt_iclass_rsr_dbreaka0, + ICLASS_xt_iclass_wsr_dbreaka0, + ICLASS_xt_iclass_xsr_dbreaka0, + ICLASS_xt_iclass_rsr_dbreakc0, + ICLASS_xt_iclass_wsr_dbreakc0, + ICLASS_xt_iclass_xsr_dbreakc0, + ICLASS_xt_iclass_rsr_dbreaka1, + ICLASS_xt_iclass_wsr_dbreaka1, + ICLASS_xt_iclass_xsr_dbreaka1, + ICLASS_xt_iclass_rsr_dbreakc1, + ICLASS_xt_iclass_wsr_dbreakc1, + ICLASS_xt_iclass_xsr_dbreakc1, + ICLASS_xt_iclass_rsr_ibreaka0, + ICLASS_xt_iclass_wsr_ibreaka0, + ICLASS_xt_iclass_xsr_ibreaka0, + ICLASS_xt_iclass_rsr_ibreaka1, + ICLASS_xt_iclass_wsr_ibreaka1, + ICLASS_xt_iclass_xsr_ibreaka1, + ICLASS_xt_iclass_rsr_ibreakenable, + ICLASS_xt_iclass_wsr_ibreakenable, + ICLASS_xt_iclass_xsr_ibreakenable, + ICLASS_xt_iclass_rsr_debugcause, + ICLASS_xt_iclass_wsr_debugcause, + ICLASS_xt_iclass_xsr_debugcause, + ICLASS_xt_iclass_rsr_icount, + ICLASS_xt_iclass_wsr_icount, + ICLASS_xt_iclass_xsr_icount, + ICLASS_xt_iclass_rsr_icountlevel, + ICLASS_xt_iclass_wsr_icountlevel, + ICLASS_xt_iclass_xsr_icountlevel, + ICLASS_xt_iclass_rsr_ddr, + ICLASS_xt_iclass_wsr_ddr, + ICLASS_xt_iclass_xsr_ddr, + ICLASS_xt_iclass_lddr32_p, + ICLASS_xt_iclass_sddr32_p, + ICLASS_xt_iclass_rfdo, + ICLASS_xt_iclass_rfdd, + ICLASS_xt_iclass_wsr_mmid, + ICLASS_xt_iclass_bbool1, + ICLASS_xt_iclass_bbool4, + ICLASS_xt_iclass_bbool8, + ICLASS_xt_iclass_bbranch, + ICLASS_xt_iclass_bmove, + ICLASS_xt_iclass_RSR_BR, + ICLASS_xt_iclass_WSR_BR, + ICLASS_xt_iclass_XSR_BR, + ICLASS_xt_iclass_rsr_ccount, + ICLASS_xt_iclass_wsr_ccount, + ICLASS_xt_iclass_xsr_ccount, + ICLASS_xt_iclass_rsr_ccompare0, + ICLASS_xt_iclass_wsr_ccompare0, + ICLASS_xt_iclass_xsr_ccompare0, + ICLASS_xt_iclass_rsr_ccompare1, + ICLASS_xt_iclass_wsr_ccompare1, + ICLASS_xt_iclass_xsr_ccompare1, + ICLASS_xt_iclass_rsr_ccompare2, + ICLASS_xt_iclass_wsr_ccompare2, + ICLASS_xt_iclass_xsr_ccompare2, + ICLASS_xt_iclass_icache, + ICLASS_xt_iclass_icache_lock, + ICLASS_xt_iclass_icache_inv, + ICLASS_xt_iclass_licx, + ICLASS_xt_iclass_sicx, + ICLASS_xt_iclass_dcache, + ICLASS_xt_iclass_dcache_dyn, + ICLASS_xt_iclass_dcache_ind, + ICLASS_xt_iclass_dcache_inv, + ICLASS_xt_iclass_dpf, + ICLASS_xt_iclass_dcache_lock, + ICLASS_xt_iclass_sdct, + ICLASS_xt_iclass_ldct, + ICLASS_xt_iclass_rsr_prefctl, + ICLASS_xt_iclass_wsr_prefctl, + ICLASS_xt_iclass_xsr_prefctl, + ICLASS_xt_iclass_wsr_ptevaddr, + ICLASS_xt_iclass_rsr_ptevaddr, + ICLASS_xt_iclass_xsr_ptevaddr, + ICLASS_xt_iclass_rsr_rasid, + ICLASS_xt_iclass_wsr_rasid, + ICLASS_xt_iclass_xsr_rasid, + ICLASS_xt_iclass_rsr_itlbcfg, + ICLASS_xt_iclass_wsr_itlbcfg, + ICLASS_xt_iclass_xsr_itlbcfg, + ICLASS_xt_iclass_rsr_dtlbcfg, + ICLASS_xt_iclass_wsr_dtlbcfg, + ICLASS_xt_iclass_xsr_dtlbcfg, + ICLASS_xt_iclass_idtlb, + ICLASS_xt_iclass_rdtlb, + ICLASS_xt_iclass_wdtlb, + ICLASS_xt_iclass_iitlb, + ICLASS_xt_iclass_ritlb, + ICLASS_xt_iclass_witlb, + ICLASS_xt_iclass_ldpte, + ICLASS_xt_iclass_hwwitlba, + ICLASS_xt_iclass_hwwdtlba, + ICLASS_xt_iclass_rsr_cpenable, + ICLASS_xt_iclass_wsr_cpenable, + ICLASS_xt_iclass_xsr_cpenable, + ICLASS_xt_iclass_clamp, + ICLASS_xt_iclass_minmax, + ICLASS_xt_iclass_nsa, + ICLASS_xt_iclass_sx, + ICLASS_xt_iclass_l32ai, + ICLASS_xt_iclass_s32ri, + ICLASS_xt_iclass_s32c1i, + ICLASS_xt_iclass_rsr_scompare1, + ICLASS_xt_iclass_wsr_scompare1, + ICLASS_xt_iclass_xsr_scompare1, + ICLASS_xt_iclass_rsr_atomctl, + ICLASS_xt_iclass_wsr_atomctl, + ICLASS_xt_iclass_xsr_atomctl, + ICLASS_xt_iclass_div, + ICLASS_xt_iclass_rer, + ICLASS_xt_iclass_wer, + ICLASS_rur_ae_ovf_sar, + ICLASS_wur_ae_ovf_sar, + ICLASS_rur_ae_bithead, + ICLASS_wur_ae_bithead, + ICLASS_rur_ae_ts_fts_bu_bp, + ICLASS_wur_ae_ts_fts_bu_bp, + ICLASS_rur_ae_sd_no, + ICLASS_wur_ae_sd_no, + ICLASS_ae_iclass_rur_ae_overflow, + ICLASS_ae_iclass_wur_ae_overflow, + ICLASS_ae_iclass_rur_ae_sar, + ICLASS_ae_iclass_wur_ae_sar, + ICLASS_ae_iclass_rur_ae_bitptr, + ICLASS_ae_iclass_wur_ae_bitptr, + ICLASS_ae_iclass_rur_ae_bitsused, + ICLASS_ae_iclass_wur_ae_bitsused, + ICLASS_ae_iclass_rur_ae_tablesize, + ICLASS_ae_iclass_wur_ae_tablesize, + ICLASS_ae_iclass_rur_ae_first_ts, + ICLASS_ae_iclass_wur_ae_first_ts, + ICLASS_ae_iclass_rur_ae_nextoffset, + ICLASS_ae_iclass_wur_ae_nextoffset, + ICLASS_ae_iclass_rur_ae_searchdone, + ICLASS_ae_iclass_wur_ae_searchdone, + ICLASS_ae_iclass_lp16f_i, + ICLASS_ae_iclass_lp16f_iu, + ICLASS_ae_iclass_lp16f_x, + ICLASS_ae_iclass_lp16f_xu, + ICLASS_ae_iclass_lp24_i, + ICLASS_ae_iclass_lp24_iu, + ICLASS_ae_iclass_lp24_x, + ICLASS_ae_iclass_lp24_xu, + ICLASS_ae_iclass_lp24f_i, + ICLASS_ae_iclass_lp24f_iu, + ICLASS_ae_iclass_lp24f_x, + ICLASS_ae_iclass_lp24f_xu, + ICLASS_ae_iclass_lp16x2f_i, + ICLASS_ae_iclass_lp16x2f_iu, + ICLASS_ae_iclass_lp16x2f_x, + ICLASS_ae_iclass_lp16x2f_xu, + ICLASS_ae_iclass_lp24x2f_i, + ICLASS_ae_iclass_lp24x2f_iu, + ICLASS_ae_iclass_lp24x2f_x, + ICLASS_ae_iclass_lp24x2f_xu, + ICLASS_ae_iclass_lp24x2_i, + ICLASS_ae_iclass_lp24x2_iu, + ICLASS_ae_iclass_lp24x2_x, + ICLASS_ae_iclass_lp24x2_xu, + ICLASS_ae_iclass_sp16x2f_i, + ICLASS_ae_iclass_sp16x2f_iu, + ICLASS_ae_iclass_sp16x2f_x, + ICLASS_ae_iclass_sp16x2f_xu, + ICLASS_ae_iclass_sp24x2s_i, + ICLASS_ae_iclass_sp24x2s_iu, + ICLASS_ae_iclass_sp24x2s_x, + ICLASS_ae_iclass_sp24x2s_xu, + ICLASS_ae_iclass_sp24x2f_i, + ICLASS_ae_iclass_sp24x2f_iu, + ICLASS_ae_iclass_sp24x2f_x, + ICLASS_ae_iclass_sp24x2f_xu, + ICLASS_ae_iclass_sp16f_l_i, + ICLASS_ae_iclass_sp16f_l_iu, + ICLASS_ae_iclass_sp16f_l_x, + ICLASS_ae_iclass_sp16f_l_xu, + ICLASS_ae_iclass_sp24s_l_i, + ICLASS_ae_iclass_sp24s_l_iu, + ICLASS_ae_iclass_sp24s_l_x, + ICLASS_ae_iclass_sp24s_l_xu, + ICLASS_ae_iclass_sp24f_l_i, + ICLASS_ae_iclass_sp24f_l_iu, + ICLASS_ae_iclass_sp24f_l_x, + ICLASS_ae_iclass_sp24f_l_xu, + ICLASS_ae_iclass_lq56_i, + ICLASS_ae_iclass_lq56_iu, + ICLASS_ae_iclass_lq56_x, + ICLASS_ae_iclass_lq56_xu, + ICLASS_ae_iclass_lq32f_i, + ICLASS_ae_iclass_lq32f_iu, + ICLASS_ae_iclass_lq32f_x, + ICLASS_ae_iclass_lq32f_xu, + ICLASS_ae_iclass_sq56s_i, + ICLASS_ae_iclass_sq56s_iu, + ICLASS_ae_iclass_sq56s_x, + ICLASS_ae_iclass_sq56s_xu, + ICLASS_ae_iclass_sq32f_i, + ICLASS_ae_iclass_sq32f_iu, + ICLASS_ae_iclass_sq32f_x, + ICLASS_ae_iclass_sq32f_xu, + ICLASS_ae_iclass_zerop48, + ICLASS_ae_iclass_movp48, + ICLASS_ae_iclass_selp24_ll, + ICLASS_ae_iclass_selp24_lh, + ICLASS_ae_iclass_selp24_hl, + ICLASS_ae_iclass_selp24_hh, + ICLASS_ae_iclass_movtp24x2, + ICLASS_ae_iclass_movfp24x2, + ICLASS_ae_iclass_movtp48, + ICLASS_ae_iclass_movfp48, + ICLASS_ae_iclass_movpa24x2, + ICLASS_ae_iclass_truncp24a32x2, + ICLASS_ae_iclass_cvta32p24_l, + ICLASS_ae_iclass_cvta32p24_h, + ICLASS_ae_iclass_cvtp24a16x2_ll, + ICLASS_ae_iclass_cvtp24a16x2_lh, + ICLASS_ae_iclass_cvtp24a16x2_hl, + ICLASS_ae_iclass_cvtp24a16x2_hh, + ICLASS_ae_iclass_truncp24q48x2, + ICLASS_ae_iclass_truncp16, + ICLASS_ae_iclass_roundsp24q48sym, + ICLASS_ae_iclass_roundsp24q48asym, + ICLASS_ae_iclass_roundsp16q48sym, + ICLASS_ae_iclass_roundsp16q48asym, + ICLASS_ae_iclass_roundsp16sym, + ICLASS_ae_iclass_roundsp16asym, + ICLASS_ae_iclass_zeroq56, + ICLASS_ae_iclass_movq56, + ICLASS_ae_iclass_movtq56, + ICLASS_ae_iclass_movfq56, + ICLASS_ae_iclass_cvtq48a32s, + ICLASS_ae_iclass_cvtq48p24s_l, + ICLASS_ae_iclass_cvtq48p24s_h, + ICLASS_ae_iclass_satq48s, + ICLASS_ae_iclass_truncq32, + ICLASS_ae_iclass_roundsq32sym, + ICLASS_ae_iclass_roundsq32asym, + ICLASS_ae_iclass_trunca32q48, + ICLASS_ae_iclass_movap24s_l, + ICLASS_ae_iclass_movap24s_h, + ICLASS_ae_iclass_trunca16p24s_l, + ICLASS_ae_iclass_trunca16p24s_h, + ICLASS_ae_iclass_addp24, + ICLASS_ae_iclass_subp24, + ICLASS_ae_iclass_negp24, + ICLASS_ae_iclass_absp24, + ICLASS_ae_iclass_maxp24s, + ICLASS_ae_iclass_minp24s, + ICLASS_ae_iclass_maxbp24s, + ICLASS_ae_iclass_minbp24s, + ICLASS_ae_iclass_addsp24s, + ICLASS_ae_iclass_subsp24s, + ICLASS_ae_iclass_negsp24s, + ICLASS_ae_iclass_abssp24s, + ICLASS_ae_iclass_andp48, + ICLASS_ae_iclass_nandp48, + ICLASS_ae_iclass_orp48, + ICLASS_ae_iclass_xorp48, + ICLASS_ae_iclass_ltp24s, + ICLASS_ae_iclass_lep24s, + ICLASS_ae_iclass_eqp24, + ICLASS_ae_iclass_addq56, + ICLASS_ae_iclass_subq56, + ICLASS_ae_iclass_negq56, + ICLASS_ae_iclass_absq56, + ICLASS_ae_iclass_maxq56s, + ICLASS_ae_iclass_minq56s, + ICLASS_ae_iclass_maxbq56s, + ICLASS_ae_iclass_minbq56s, + ICLASS_ae_iclass_addsq56s, + ICLASS_ae_iclass_subsq56s, + ICLASS_ae_iclass_negsq56s, + ICLASS_ae_iclass_abssq56s, + ICLASS_ae_iclass_andq56, + ICLASS_ae_iclass_nandq56, + ICLASS_ae_iclass_orq56, + ICLASS_ae_iclass_xorq56, + ICLASS_ae_iclass_sllip24, + ICLASS_ae_iclass_srlip24, + ICLASS_ae_iclass_sraip24, + ICLASS_ae_iclass_sllsp24, + ICLASS_ae_iclass_srlsp24, + ICLASS_ae_iclass_srasp24, + ICLASS_ae_iclass_sllisp24s, + ICLASS_ae_iclass_sllssp24s, + ICLASS_ae_iclass_slliq56, + ICLASS_ae_iclass_srliq56, + ICLASS_ae_iclass_sraiq56, + ICLASS_ae_iclass_sllsq56, + ICLASS_ae_iclass_srlsq56, + ICLASS_ae_iclass_srasq56, + ICLASS_ae_iclass_sllaq56, + ICLASS_ae_iclass_srlaq56, + ICLASS_ae_iclass_sraaq56, + ICLASS_ae_iclass_sllisq56s, + ICLASS_ae_iclass_sllssq56s, + ICLASS_ae_iclass_sllasq56s, + ICLASS_ae_iclass_ltq56s, + ICLASS_ae_iclass_leq56s, + ICLASS_ae_iclass_eqq56, + ICLASS_ae_iclass_nsaq56s, + ICLASS_ae_iclass_mulsrfq32sp24s_h, + ICLASS_ae_iclass_mulsrfq32sp24s_l, + ICLASS_ae_iclass_mularfq32sp24s_h, + ICLASS_ae_iclass_mularfq32sp24s_l, + ICLASS_ae_iclass_mulrfq32sp24s_h, + ICLASS_ae_iclass_mulrfq32sp24s_l, + ICLASS_ae_iclass_mulsfq32sp24s_h, + ICLASS_ae_iclass_mulsfq32sp24s_l, + ICLASS_ae_iclass_mulafq32sp24s_h, + ICLASS_ae_iclass_mulafq32sp24s_l, + ICLASS_ae_iclass_mulfq32sp24s_h, + ICLASS_ae_iclass_mulfq32sp24s_l, + ICLASS_ae_iclass_mulfs32p16s_ll, + ICLASS_ae_iclass_mulfp24s_ll, + ICLASS_ae_iclass_mulp24s_ll, + ICLASS_ae_iclass_mulfs32p16s_lh, + ICLASS_ae_iclass_mulfp24s_lh, + ICLASS_ae_iclass_mulp24s_lh, + ICLASS_ae_iclass_mulfs32p16s_hl, + ICLASS_ae_iclass_mulfp24s_hl, + ICLASS_ae_iclass_mulp24s_hl, + ICLASS_ae_iclass_mulfs32p16s_hh, + ICLASS_ae_iclass_mulfp24s_hh, + ICLASS_ae_iclass_mulp24s_hh, + ICLASS_ae_iclass_mulafs32p16s_ll, + ICLASS_ae_iclass_mulafp24s_ll, + ICLASS_ae_iclass_mulap24s_ll, + ICLASS_ae_iclass_mulafs32p16s_lh, + ICLASS_ae_iclass_mulafp24s_lh, + ICLASS_ae_iclass_mulap24s_lh, + ICLASS_ae_iclass_mulafs32p16s_hl, + ICLASS_ae_iclass_mulafp24s_hl, + ICLASS_ae_iclass_mulap24s_hl, + ICLASS_ae_iclass_mulafs32p16s_hh, + ICLASS_ae_iclass_mulafp24s_hh, + ICLASS_ae_iclass_mulap24s_hh, + ICLASS_ae_iclass_mulsfs32p16s_ll, + ICLASS_ae_iclass_mulsfp24s_ll, + ICLASS_ae_iclass_mulsp24s_ll, + ICLASS_ae_iclass_mulsfs32p16s_lh, + ICLASS_ae_iclass_mulsfp24s_lh, + ICLASS_ae_iclass_mulsp24s_lh, + ICLASS_ae_iclass_mulsfs32p16s_hl, + ICLASS_ae_iclass_mulsfp24s_hl, + ICLASS_ae_iclass_mulsp24s_hl, + ICLASS_ae_iclass_mulsfs32p16s_hh, + ICLASS_ae_iclass_mulsfp24s_hh, + ICLASS_ae_iclass_mulsp24s_hh, + ICLASS_ae_iclass_mulafs56p24s_ll, + ICLASS_ae_iclass_mulas56p24s_ll, + ICLASS_ae_iclass_mulafs56p24s_lh, + ICLASS_ae_iclass_mulas56p24s_lh, + ICLASS_ae_iclass_mulafs56p24s_hl, + ICLASS_ae_iclass_mulas56p24s_hl, + ICLASS_ae_iclass_mulafs56p24s_hh, + ICLASS_ae_iclass_mulas56p24s_hh, + ICLASS_ae_iclass_mulsfs56p24s_ll, + ICLASS_ae_iclass_mulss56p24s_ll, + ICLASS_ae_iclass_mulsfs56p24s_lh, + ICLASS_ae_iclass_mulss56p24s_lh, + ICLASS_ae_iclass_mulsfs56p24s_hl, + ICLASS_ae_iclass_mulss56p24s_hl, + ICLASS_ae_iclass_mulsfs56p24s_hh, + ICLASS_ae_iclass_mulss56p24s_hh, + ICLASS_ae_iclass_mulfq32sp16s_l, + ICLASS_ae_iclass_mulfq32sp16s_h, + ICLASS_ae_iclass_mulfq32sp16u_l, + ICLASS_ae_iclass_mulfq32sp16u_h, + ICLASS_ae_iclass_mulq32sp16s_l, + ICLASS_ae_iclass_mulq32sp16s_h, + ICLASS_ae_iclass_mulq32sp16u_l, + ICLASS_ae_iclass_mulq32sp16u_h, + ICLASS_ae_iclass_mulafq32sp16s_l, + ICLASS_ae_iclass_mulafq32sp16s_h, + ICLASS_ae_iclass_mulafq32sp16u_l, + ICLASS_ae_iclass_mulafq32sp16u_h, + ICLASS_ae_iclass_mulaq32sp16s_l, + ICLASS_ae_iclass_mulaq32sp16s_h, + ICLASS_ae_iclass_mulaq32sp16u_l, + ICLASS_ae_iclass_mulaq32sp16u_h, + ICLASS_ae_iclass_mulsfq32sp16s_l, + ICLASS_ae_iclass_mulsfq32sp16s_h, + ICLASS_ae_iclass_mulsfq32sp16u_l, + ICLASS_ae_iclass_mulsfq32sp16u_h, + ICLASS_ae_iclass_mulsq32sp16s_l, + ICLASS_ae_iclass_mulsq32sp16s_h, + ICLASS_ae_iclass_mulsq32sp16u_l, + ICLASS_ae_iclass_mulsq32sp16u_h, + ICLASS_ae_iclass_mulzaaq32sp16s_ll, + ICLASS_ae_iclass_mulzaafq32sp16s_ll, + ICLASS_ae_iclass_mulzaaq32sp16u_ll, + ICLASS_ae_iclass_mulzaafq32sp16u_ll, + ICLASS_ae_iclass_mulzaaq32sp16s_hh, + ICLASS_ae_iclass_mulzaafq32sp16s_hh, + ICLASS_ae_iclass_mulzaaq32sp16u_hh, + ICLASS_ae_iclass_mulzaafq32sp16u_hh, + ICLASS_ae_iclass_mulzaaq32sp16s_lh, + ICLASS_ae_iclass_mulzaafq32sp16s_lh, + ICLASS_ae_iclass_mulzaaq32sp16u_lh, + ICLASS_ae_iclass_mulzaafq32sp16u_lh, + ICLASS_ae_iclass_mulzasq32sp16s_ll, + ICLASS_ae_iclass_mulzasfq32sp16s_ll, + ICLASS_ae_iclass_mulzasq32sp16u_ll, + ICLASS_ae_iclass_mulzasfq32sp16u_ll, + ICLASS_ae_iclass_mulzasq32sp16s_hh, + ICLASS_ae_iclass_mulzasfq32sp16s_hh, + ICLASS_ae_iclass_mulzasq32sp16u_hh, + ICLASS_ae_iclass_mulzasfq32sp16u_hh, + ICLASS_ae_iclass_mulzasq32sp16s_lh, + ICLASS_ae_iclass_mulzasfq32sp16s_lh, + ICLASS_ae_iclass_mulzasq32sp16u_lh, + ICLASS_ae_iclass_mulzasfq32sp16u_lh, + ICLASS_ae_iclass_mulzsaq32sp16s_ll, + ICLASS_ae_iclass_mulzsafq32sp16s_ll, + ICLASS_ae_iclass_mulzsaq32sp16u_ll, + ICLASS_ae_iclass_mulzsafq32sp16u_ll, + ICLASS_ae_iclass_mulzsaq32sp16s_hh, + ICLASS_ae_iclass_mulzsafq32sp16s_hh, + ICLASS_ae_iclass_mulzsaq32sp16u_hh, + ICLASS_ae_iclass_mulzsafq32sp16u_hh, + ICLASS_ae_iclass_mulzsaq32sp16s_lh, + ICLASS_ae_iclass_mulzsafq32sp16s_lh, + ICLASS_ae_iclass_mulzsaq32sp16u_lh, + ICLASS_ae_iclass_mulzsafq32sp16u_lh, + ICLASS_ae_iclass_mulzssq32sp16s_ll, + ICLASS_ae_iclass_mulzssfq32sp16s_ll, + ICLASS_ae_iclass_mulzssq32sp16u_ll, + ICLASS_ae_iclass_mulzssfq32sp16u_ll, + ICLASS_ae_iclass_mulzssq32sp16s_hh, + ICLASS_ae_iclass_mulzssfq32sp16s_hh, + ICLASS_ae_iclass_mulzssq32sp16u_hh, + ICLASS_ae_iclass_mulzssfq32sp16u_hh, + ICLASS_ae_iclass_mulzssq32sp16s_lh, + ICLASS_ae_iclass_mulzssfq32sp16s_lh, + ICLASS_ae_iclass_mulzssq32sp16u_lh, + ICLASS_ae_iclass_mulzssfq32sp16u_lh, + ICLASS_ae_iclass_mulzaafp24s_hh_ll, + ICLASS_ae_iclass_mulzaap24s_hh_ll, + ICLASS_ae_iclass_mulzaafp24s_hl_lh, + ICLASS_ae_iclass_mulzaap24s_hl_lh, + ICLASS_ae_iclass_mulzasfp24s_hh_ll, + ICLASS_ae_iclass_mulzasp24s_hh_ll, + ICLASS_ae_iclass_mulzasfp24s_hl_lh, + ICLASS_ae_iclass_mulzasp24s_hl_lh, + ICLASS_ae_iclass_mulzsafp24s_hh_ll, + ICLASS_ae_iclass_mulzsap24s_hh_ll, + ICLASS_ae_iclass_mulzsafp24s_hl_lh, + ICLASS_ae_iclass_mulzsap24s_hl_lh, + ICLASS_ae_iclass_mulzssfp24s_hh_ll, + ICLASS_ae_iclass_mulzssp24s_hh_ll, + ICLASS_ae_iclass_mulzssfp24s_hl_lh, + ICLASS_ae_iclass_mulzssp24s_hl_lh, + ICLASS_ae_iclass_mulaafp24s_hh_ll, + ICLASS_ae_iclass_mulaap24s_hh_ll, + ICLASS_ae_iclass_mulaafp24s_hl_lh, + ICLASS_ae_iclass_mulaap24s_hl_lh, + ICLASS_ae_iclass_mulasfp24s_hh_ll, + ICLASS_ae_iclass_mulasp24s_hh_ll, + ICLASS_ae_iclass_mulasfp24s_hl_lh, + ICLASS_ae_iclass_mulasp24s_hl_lh, + ICLASS_ae_iclass_mulsafp24s_hh_ll, + ICLASS_ae_iclass_mulsap24s_hh_ll, + ICLASS_ae_iclass_mulsafp24s_hl_lh, + ICLASS_ae_iclass_mulsap24s_hl_lh, + ICLASS_ae_iclass_mulssfp24s_hh_ll, + ICLASS_ae_iclass_mulssp24s_hh_ll, + ICLASS_ae_iclass_mulssfp24s_hl_lh, + ICLASS_ae_iclass_mulssp24s_hl_lh, + ICLASS_ae_iclass_sha32, + ICLASS_ae_iclass_vldl32t, + ICLASS_ae_iclass_vldl16t, + ICLASS_ae_iclass_vldl16c, + ICLASS_ae_iclass_vldsht, + ICLASS_ae_iclass_lb, + ICLASS_ae_iclass_lbi, + ICLASS_ae_iclass_lbk, + ICLASS_ae_iclass_lbki, + ICLASS_ae_iclass_db, + ICLASS_ae_iclass_dbi, + ICLASS_ae_iclass_vlel32t, + ICLASS_ae_iclass_vlel16t, + ICLASS_ae_iclass_sb, + ICLASS_ae_iclass_sbi, + ICLASS_ae_iclass_vles16c, + ICLASS_ae_iclass_sbf, + ICLASS_icls_AE_SLAASQ56S, + ICLASS_icls_AE_ADDBRBA32, + ICLASS_icls_AE_MINABSSP24S, + ICLASS_icls_AE_MAXABSSP24S, + ICLASS_icls_AE_MINABSSQ56S, + ICLASS_icls_AE_MAXABSSQ56S, + ICLASS_rur_ae_cbegin0, + ICLASS_wur_ae_cbegin0, + ICLASS_rur_ae_cend0, + ICLASS_wur_ae_cend0, + ICLASS_icls_AE_LP24X2_C, + ICLASS_icls_AE_SP24X2S_C, + ICLASS_icls_AE_LP24X2F_C, + ICLASS_icls_AE_SP24X2F_C, + ICLASS_icls_AE_LP16X2F_C, + ICLASS_icls_AE_SP16X2F_C, + ICLASS_icls_AE_LP24_C, + ICLASS_icls_AE_SP24S_L_C, + ICLASS_icls_AE_LP24F_C, + ICLASS_icls_AE_SP24F_L_C, + ICLASS_icls_AE_LP16F_C, + ICLASS_icls_AE_SP16F_L_C, + ICLASS_icls_AE_LQ56_C, + ICLASS_icls_AE_SQ56S_C, + ICLASS_icls_AE_LQ32F_C, + ICLASS_icls_AE_SQ32F_C, + ICLASS_rur_expstate, + ICLASS_wur_expstate, + ICLASS_iclass_READ_IMPWIRE, + ICLASS_iclass_SETB_EXPSTATE, + ICLASS_iclass_CLRB_EXPSTATE, + ICLASS_iclass_WRMSK_EXPSTATE +}; + + +/* Opcode encodings. */ + +static void +Opcode_excw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80200; +} + +static void +Opcode_rfe_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300; +} + +static void +Opcode_rfde_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2300; +} + +static void +Opcode_syscall_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500; +} + +static void +Opcode_call12_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5c0000; +} + +static void +Opcode_call8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x580000; +} + +static void +Opcode_call4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x540000; +} + +static void +Opcode_callx12_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf0000; +} + +static void +Opcode_callx8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb0000; +} + +static void +Opcode_callx4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x70000; +} + +static void +Opcode_entry_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6c0000; +} + +static void +Opcode_movsp_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100; +} + +static void +Opcode_rotw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x804; +} + +static void +Opcode_retw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60000; +} + +static void +Opcode_retw_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd10f; +} + +static void +Opcode_rfwo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4300; +} + +static void +Opcode_rfwu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5300; +} + +static void +Opcode_l32e_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x90; +} + +static void +Opcode_s32e_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x94; +} + +static void +Opcode_rsr_windowbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4830; +} + +static void +Opcode_wsr_windowbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4831; +} + +static void +Opcode_xsr_windowbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4816; +} + +static void +Opcode_rsr_windowstart_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4930; +} + +static void +Opcode_wsr_windowstart_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4931; +} + +static void +Opcode_xsr_windowstart_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4916; +} + +static void +Opcode_add_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa000; +} + +static void +Opcode_addi_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb000; +} + +static void +Opcode_beqz_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc800; +} + +static void +Opcode_bnez_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xcc00; +} + +static void +Opcode_ill_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd60f; +} + +static void +Opcode_l32i_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8000; +} + +static void +Opcode_mov_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd000; +} + +static void +Opcode_movi_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc000; +} + +static void +Opcode_nop_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd30f; +} + +static void +Opcode_ret_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd00f; +} + +static void +Opcode_s32i_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9000; +} + +static void +Opcode_rur_threadptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7e03e; +} + +static void +Opcode_wur_threadptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe73f; +} + +static void +Opcode_addi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200c00; +} + +static void +Opcode_addi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100002; +} + +static void +Opcode_addmi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200d00; +} + +static void +Opcode_addmi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200002; +} + +static void +Opcode_add_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8; +} + +static void +Opcode_add_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb81; +} + +static void +Opcode_sub_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc; +} + +static void +Opcode_sub_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf01; +} + +static void +Opcode_addx2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9; +} + +static void +Opcode_addx2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1381; +} + +static void +Opcode_addx4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa; +} + +static void +Opcode_addx4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2381; +} + +static void +Opcode_addx8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb; +} + +static void +Opcode_addx8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4b81; +} + +static void +Opcode_subx2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd; +} + +static void +Opcode_subx2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4d01; +} + +static void +Opcode_subx4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe; +} + +static void +Opcode_subx4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf81; +} + +static void +Opcode_subx8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf; +} + +static void +Opcode_subx8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4d81; +} + +static void +Opcode_and_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1; +} + +static void +Opcode_and_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b81; +} + +static void +Opcode_or_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2; +} + +static void +Opcode_or_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2501; +} + +static void +Opcode_xor_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3; +} + +static void +Opcode_xor_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4f01; +} + +static void +Opcode_beqi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680000; +} + +static void +Opcode_beqi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3; +} + +static void +Opcode_bnei_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x690000; +} + +static void +Opcode_bnei_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1800003; +} + +static void +Opcode_bgei_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6b0000; +} + +static void +Opcode_bgei_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x800003; +} + +static void +Opcode_blti_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6a0000; +} + +static void +Opcode_blti_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000003; +} + +static void +Opcode_bbci_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700600; +} + +static void +Opcode_bbci_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2; +} + +static void +Opcode_bbsi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700e00; +} + +static void +Opcode_bbsi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80002; +} + +static void +Opcode_bgeui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6f0000; +} + +static void +Opcode_bgeui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1000003; +} + +static void +Opcode_bltui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6e0000; +} + +static void +Opcode_bltui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000003; +} + +static void +Opcode_beq_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700100; +} + +static void +Opcode_beq_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300002; +} + +static void +Opcode_bne_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700900; +} + +static void +Opcode_bne_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4; +} + +static void +Opcode_bge_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700a00; +} + +static void +Opcode_bge_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600002; +} + +static void +Opcode_blt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700200; +} + +static void +Opcode_blt_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680002; +} + +static void +Opcode_bgeu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700b00; +} + +static void +Opcode_bgeu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380002; +} + +static void +Opcode_bltu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700300; +} + +static void +Opcode_bltu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700002; +} + +static void +Opcode_bany_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700800; +} + +static void +Opcode_bany_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500002; +} + +static void +Opcode_bnone_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700000; +} + +static void +Opcode_bnone_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80004; +} + +static void +Opcode_ball_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700400; +} + +static void +Opcode_ball_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180002; +} + +static void +Opcode_bnall_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700c00; +} + +static void +Opcode_bnall_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x780002; +} + +static void +Opcode_bbc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700500; +} + +static void +Opcode_bbc_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x580002; +} + +static void +Opcode_bbs_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700d00; +} + +static void +Opcode_bbs_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280002; +} + +static void +Opcode_beqz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x640000; +} + +static void +Opcode_beqz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x101; +} + +static void +Opcode_bnez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x650000; +} + +static void +Opcode_bnez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x181; +} + +static void +Opcode_bgez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x670000; +} + +static void +Opcode_bgez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x281; +} + +static void +Opcode_bltz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x660000; +} + +static void +Opcode_bltz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x681; +} + +static void +Opcode_call0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500000; +} + +static void +Opcode_callx0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30000; +} + +static void +Opcode_extui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40; +} + +static void +Opcode_extui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81; +} + +static void +Opcode_ill_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0; +} + +static void +Opcode_j_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600000; +} + +static void +Opcode_j_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1; +} + +static void +Opcode_jx_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa0000; +} + +static void +Opcode_jx_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x983d01; +} + +static void +Opcode_l16ui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200100; +} + +static void +Opcode_l16ui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200004; +} + +static void +Opcode_l16si_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200900; +} + +static void +Opcode_l16si_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100004; +} + +static void +Opcode_l32i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200200; +} + +static void +Opcode_l32i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400004; +} + +static void +Opcode_l32r_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100000; +} + +static void +Opcode_l32r_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5; +} + +static void +Opcode_l8ui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200000; +} + +static void +Opcode_l8ui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180004; +} + +static void +Opcode_loop_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d0800; +} + +static void +Opcode_loopnez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d0900; +} + +static void +Opcode_loopgtz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d0a00; +} + +static void +Opcode_movi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200a00; +} + +static void +Opcode_movi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x301; +} + +static void +Opcode_moveqz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x38; +} + +static void +Opcode_moveqz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7381; +} + +static void +Opcode_movnez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x39; +} + +static void +Opcode_movnez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd01; +} + +static void +Opcode_movltz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3a; +} + +static void +Opcode_movltz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x701; +} + +static void +Opcode_movgez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3b; +} + +static void +Opcode_movgez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x581; +} + +static void +Opcode_neg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6; +} + +static void +Opcode_neg_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3ed81; +} + +static void +Opcode_abs_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1006; +} + +static void +Opcode_abs_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ed81; +} + +static void +Opcode_nop_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf0200; +} + +static void +Opcode_nop_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x36001; +} + +static void +Opcode_nop_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb83d01; +} + +static void +Opcode_ret_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20000; +} + +static void +Opcode_simcall_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1500; +} + +static void +Opcode_s16i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200500; +} + +static void +Opcode_s16i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280004; +} + +static void +Opcode_s32i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200600; +} + +static void +Opcode_s32i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300004; +} + +static void +Opcode_s32nb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x95; +} + +static void +Opcode_s8i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200400; +} + +static void +Opcode_s8i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380004; +} + +static void +Opcode_ssr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4; +} + +static void +Opcode_ssr_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x983d81; +} + +static void +Opcode_ssl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x104; +} + +static void +Opcode_ssl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4183d01; +} + +static void +Opcode_ssa8l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x204; +} + +static void +Opcode_ssa8l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2183d01; +} + +static void +Opcode_ssa8b_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x304; +} + +static void +Opcode_ssa8b_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1183d01; +} + +static void +Opcode_ssai_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x404; +} + +static void +Opcode_ssai_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18e501; +} + +static void +Opcode_sll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1a; +} + +static void +Opcode_sll_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2806f81; +} + +static void +Opcode_src_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18; +} + +static void +Opcode_src_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4781; +} + +static void +Opcode_srl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x19; +} + +static void +Opcode_srl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7ed81; +} + +static void +Opcode_sra_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b; +} + +static void +Opcode_sra_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5ed81; +} + +static void +Opcode_slli_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10; +} + +static void +Opcode_slli_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x501; +} + +static void +Opcode_srai_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x12; +} + +static void +Opcode_srai_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x381; +} + +static void +Opcode_srli_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x14; +} + +static void +Opcode_srli_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd81; +} + +static void +Opcode_memw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc0200; +} + +static void +Opcode_extw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd0200; +} + +static void +Opcode_isync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200; +} + +static void +Opcode_rsync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10200; +} + +static void +Opcode_esync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20200; +} + +static void +Opcode_dsync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30200; +} + +static void +Opcode_rsil_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600; +} + +static void +Opcode_rsr_lend_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130; +} + +static void +Opcode_wsr_lend_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x131; +} + +static void +Opcode_xsr_lend_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x116; +} + +static void +Opcode_rsr_lcount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x230; +} + +static void +Opcode_wsr_lcount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x231; +} + +static void +Opcode_xsr_lcount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x216; +} + +static void +Opcode_rsr_lbeg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30; +} + +static void +Opcode_wsr_lbeg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x31; +} + +static void +Opcode_xsr_lbeg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16; +} + +static void +Opcode_rsr_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x330; +} + +static void +Opcode_wsr_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x331; +} + +static void +Opcode_xsr_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x316; +} + +static void +Opcode_rsr_memctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6130; +} + +static void +Opcode_wsr_memctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6131; +} + +static void +Opcode_xsr_memctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6116; +} + +static void +Opcode_rsr_litbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x530; +} + +static void +Opcode_wsr_litbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x531; +} + +static void +Opcode_xsr_litbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x516; +} + +static void +Opcode_rsr_configid0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb030; +} + +static void +Opcode_wsr_configid0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb031; +} + +static void +Opcode_rsr_configid1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd030; +} + +static void +Opcode_rsr_243_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf330; +} + +static void +Opcode_rsr_ps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe630; +} + +static void +Opcode_wsr_ps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe631; +} + +static void +Opcode_xsr_ps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe616; +} + +static void +Opcode_rsr_epc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb130; +} + +static void +Opcode_wsr_epc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb131; +} + +static void +Opcode_xsr_epc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb116; +} + +static void +Opcode_rsr_excsave1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd130; +} + +static void +Opcode_wsr_excsave1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd131; +} + +static void +Opcode_xsr_excsave1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd116; +} + +static void +Opcode_rsr_epc2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb230; +} + +static void +Opcode_wsr_epc2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb231; +} + +static void +Opcode_xsr_epc2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb216; +} + +static void +Opcode_rsr_excsave2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd230; +} + +static void +Opcode_wsr_excsave2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd231; +} + +static void +Opcode_xsr_excsave2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd216; +} + +static void +Opcode_rsr_epc3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb330; +} + +static void +Opcode_wsr_epc3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb331; +} + +static void +Opcode_xsr_epc3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb316; +} + +static void +Opcode_rsr_excsave3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd330; +} + +static void +Opcode_wsr_excsave3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd331; +} + +static void +Opcode_xsr_excsave3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd316; +} + +static void +Opcode_rsr_epc4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb430; +} + +static void +Opcode_wsr_epc4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb431; +} + +static void +Opcode_xsr_epc4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb416; +} + +static void +Opcode_rsr_excsave4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd430; +} + +static void +Opcode_wsr_excsave4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd431; +} + +static void +Opcode_xsr_excsave4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd416; +} + +static void +Opcode_rsr_epc5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb530; +} + +static void +Opcode_wsr_epc5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb531; +} + +static void +Opcode_xsr_epc5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb516; +} + +static void +Opcode_rsr_excsave5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd530; +} + +static void +Opcode_wsr_excsave5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd531; +} + +static void +Opcode_xsr_excsave5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd516; +} + +static void +Opcode_rsr_epc6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb630; +} + +static void +Opcode_wsr_epc6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb631; +} + +static void +Opcode_xsr_epc6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb616; +} + +static void +Opcode_rsr_excsave6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd630; +} + +static void +Opcode_wsr_excsave6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd631; +} + +static void +Opcode_xsr_excsave6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd616; +} + +static void +Opcode_rsr_epc7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb730; +} + +static void +Opcode_wsr_epc7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb731; +} + +static void +Opcode_xsr_epc7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb716; +} + +static void +Opcode_rsr_excsave7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd730; +} + +static void +Opcode_wsr_excsave7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd731; +} + +static void +Opcode_xsr_excsave7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd716; +} + +static void +Opcode_rsr_eps2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc230; +} + +static void +Opcode_wsr_eps2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc231; +} + +static void +Opcode_xsr_eps2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc216; +} + +static void +Opcode_rsr_eps3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc330; +} + +static void +Opcode_wsr_eps3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc331; +} + +static void +Opcode_xsr_eps3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc316; +} + +static void +Opcode_rsr_eps4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc430; +} + +static void +Opcode_wsr_eps4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc431; +} + +static void +Opcode_xsr_eps4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc416; +} + +static void +Opcode_rsr_eps5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc530; +} + +static void +Opcode_wsr_eps5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc531; +} + +static void +Opcode_xsr_eps5_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc516; +} + +static void +Opcode_rsr_eps6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc630; +} + +static void +Opcode_wsr_eps6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc631; +} + +static void +Opcode_xsr_eps6_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc616; +} + +static void +Opcode_rsr_eps7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc730; +} + +static void +Opcode_wsr_eps7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc731; +} + +static void +Opcode_xsr_eps7_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc716; +} + +static void +Opcode_rsr_excvaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xee30; +} + +static void +Opcode_wsr_excvaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xee31; +} + +static void +Opcode_xsr_excvaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xee16; +} + +static void +Opcode_rsr_depc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc030; +} + +static void +Opcode_wsr_depc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc031; +} + +static void +Opcode_xsr_depc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc016; +} + +static void +Opcode_rsr_exccause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe830; +} + +static void +Opcode_wsr_exccause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe831; +} + +static void +Opcode_xsr_exccause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe816; +} + +static void +Opcode_rsr_misc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf430; +} + +static void +Opcode_wsr_misc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf431; +} + +static void +Opcode_xsr_misc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf416; +} + +static void +Opcode_rsr_misc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf530; +} + +static void +Opcode_wsr_misc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf531; +} + +static void +Opcode_xsr_misc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf516; +} + +static void +Opcode_rsr_prid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xeb30; +} + +static void +Opcode_rsr_vecbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe730; +} + +static void +Opcode_wsr_vecbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe731; +} + +static void +Opcode_xsr_vecbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe716; +} + +static void +Opcode_mul16u_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c; +} + +static void +Opcode_mul16s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d; +} + +static void +Opcode_mull_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x28; +} + +static void +Opcode_muluh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2a; +} + +static void +Opcode_mulsh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2b; +} + +static void +Opcode_mul_aa_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400047; +} + +static void +Opcode_mul_aa_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400057; +} + +static void +Opcode_mul_aa_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400067; +} + +static void +Opcode_mul_aa_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400077; +} + +static void +Opcode_umul_aa_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400007; +} + +static void +Opcode_umul_aa_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400017; +} + +static void +Opcode_umul_aa_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400027; +} + +static void +Opcode_umul_aa_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400037; +} + +static void +Opcode_mul_ad_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400043; +} + +static void +Opcode_mul_ad_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400053; +} + +static void +Opcode_mul_ad_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400063; +} + +static void +Opcode_mul_ad_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400073; +} + +static void +Opcode_mul_da_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400046; +} + +static void +Opcode_mul_da_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400056; +} + +static void +Opcode_mul_da_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400066; +} + +static void +Opcode_mul_da_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400076; +} + +static void +Opcode_mul_dd_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400042; +} + +static void +Opcode_mul_dd_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400052; +} + +static void +Opcode_mul_dd_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400062; +} + +static void +Opcode_mul_dd_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400072; +} + +static void +Opcode_mula_aa_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400087; +} + +static void +Opcode_mula_aa_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400097; +} + +static void +Opcode_mula_aa_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a7; +} + +static void +Opcode_mula_aa_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b7; +} + +static void +Opcode_muls_aa_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000c7; +} + +static void +Opcode_muls_aa_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000d7; +} + +static void +Opcode_muls_aa_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000e7; +} + +static void +Opcode_muls_aa_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000f7; +} + +static void +Opcode_mula_ad_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400083; +} + +static void +Opcode_mula_ad_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400093; +} + +static void +Opcode_mula_ad_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a3; +} + +static void +Opcode_mula_ad_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b3; +} + +static void +Opcode_muls_ad_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000c3; +} + +static void +Opcode_muls_ad_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000d3; +} + +static void +Opcode_muls_ad_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000e3; +} + +static void +Opcode_muls_ad_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000f3; +} + +static void +Opcode_mula_da_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400086; +} + +static void +Opcode_mula_da_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400096; +} + +static void +Opcode_mula_da_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a6; +} + +static void +Opcode_mula_da_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b6; +} + +static void +Opcode_muls_da_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000c6; +} + +static void +Opcode_muls_da_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000d6; +} + +static void +Opcode_muls_da_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000e6; +} + +static void +Opcode_muls_da_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000f6; +} + +static void +Opcode_mula_dd_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400082; +} + +static void +Opcode_mula_dd_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400092; +} + +static void +Opcode_mula_dd_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a2; +} + +static void +Opcode_mula_dd_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b2; +} + +static void +Opcode_muls_dd_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000c2; +} + +static void +Opcode_muls_dd_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000d2; +} + +static void +Opcode_muls_dd_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000e2; +} + +static void +Opcode_muls_dd_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000f2; +} + +static void +Opcode_mula_da_ll_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400085; +} + +static void +Opcode_mula_da_ll_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400084; +} + +static void +Opcode_mula_da_hl_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400095; +} + +static void +Opcode_mula_da_hl_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400094; +} + +static void +Opcode_mula_da_lh_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a5; +} + +static void +Opcode_mula_da_lh_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a4; +} + +static void +Opcode_mula_da_hh_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b5; +} + +static void +Opcode_mula_da_hh_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b4; +} + +static void +Opcode_mula_dd_ll_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400081; +} + +static void +Opcode_mula_dd_ll_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400080; +} + +static void +Opcode_mula_dd_hl_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400091; +} + +static void +Opcode_mula_dd_hl_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400090; +} + +static void +Opcode_mula_dd_lh_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a1; +} + +static void +Opcode_mula_dd_lh_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a0; +} + +static void +Opcode_mula_dd_hh_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b1; +} + +static void +Opcode_mula_dd_hh_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b0; +} + +static void +Opcode_lddec_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400009; +} + +static void +Opcode_ldinc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400008; +} + +static void +Opcode_rsr_m0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2030; +} + +static void +Opcode_wsr_m0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2031; +} + +static void +Opcode_xsr_m0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2016; +} + +static void +Opcode_rsr_m1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2130; +} + +static void +Opcode_wsr_m1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2131; +} + +static void +Opcode_xsr_m1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2116; +} + +static void +Opcode_rsr_m2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2230; +} + +static void +Opcode_wsr_m2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2231; +} + +static void +Opcode_xsr_m2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2216; +} + +static void +Opcode_rsr_m3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2330; +} + +static void +Opcode_wsr_m3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2331; +} + +static void +Opcode_xsr_m3_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2316; +} + +static void +Opcode_rsr_acclo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1030; +} + +static void +Opcode_wsr_acclo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1031; +} + +static void +Opcode_xsr_acclo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1016; +} + +static void +Opcode_rsr_acchi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1130; +} + +static void +Opcode_wsr_acchi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1131; +} + +static void +Opcode_xsr_acchi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1116; +} + +static void +Opcode_rfi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10300; +} + +static void +Opcode_waiti_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700; +} + +static void +Opcode_rsr_interrupt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe230; +} + +static void +Opcode_wsr_intset_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe231; +} + +static void +Opcode_wsr_intclear_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe331; +} + +static void +Opcode_rsr_intenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe430; +} + +static void +Opcode_wsr_intenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe431; +} + +static void +Opcode_xsr_intenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe416; +} + +static void +Opcode_break_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400; +} + +static void +Opcode_break_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd20f; +} + +static void +Opcode_rsr_dbreaka0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9030; +} + +static void +Opcode_wsr_dbreaka0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9031; +} + +static void +Opcode_xsr_dbreaka0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9016; +} + +static void +Opcode_rsr_dbreakc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa030; +} + +static void +Opcode_wsr_dbreakc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa031; +} + +static void +Opcode_xsr_dbreakc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa016; +} + +static void +Opcode_rsr_dbreaka1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9130; +} + +static void +Opcode_wsr_dbreaka1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9131; +} + +static void +Opcode_xsr_dbreaka1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9116; +} + +static void +Opcode_rsr_dbreakc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa130; +} + +static void +Opcode_wsr_dbreakc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa131; +} + +static void +Opcode_xsr_dbreakc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa116; +} + +static void +Opcode_rsr_ibreaka0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8030; +} + +static void +Opcode_wsr_ibreaka0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8031; +} + +static void +Opcode_xsr_ibreaka0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8016; +} + +static void +Opcode_rsr_ibreaka1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8130; +} + +static void +Opcode_wsr_ibreaka1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8131; +} + +static void +Opcode_xsr_ibreaka1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8116; +} + +static void +Opcode_rsr_ibreakenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6030; +} + +static void +Opcode_wsr_ibreakenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6031; +} + +static void +Opcode_xsr_ibreakenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6016; +} + +static void +Opcode_rsr_debugcause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe930; +} + +static void +Opcode_wsr_debugcause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe931; +} + +static void +Opcode_xsr_debugcause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe916; +} + +static void +Opcode_rsr_icount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xec30; +} + +static void +Opcode_wsr_icount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xec31; +} + +static void +Opcode_xsr_icount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xec16; +} + +static void +Opcode_rsr_icountlevel_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xed30; +} + +static void +Opcode_wsr_icountlevel_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xed31; +} + +static void +Opcode_xsr_icountlevel_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xed16; +} + +static void +Opcode_rsr_ddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6830; +} + +static void +Opcode_wsr_ddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6831; +} + +static void +Opcode_xsr_ddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6816; +} + +static void +Opcode_lddr32_p_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe0700; +} + +static void +Opcode_sddr32_p_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf0700; +} + +static void +Opcode_rfdo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe1f; +} + +static void +Opcode_rfdd_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10e1f; +} + +static void +Opcode_wsr_mmid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5931; +} + +static void +Opcode_andb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20; +} + +static void +Opcode_andb_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5381; +} + +static void +Opcode_andbc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x21; +} + +static void +Opcode_andbc_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5b81; +} + +static void +Opcode_orb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x22; +} + +static void +Opcode_orb_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4581; +} + +static void +Opcode_orbc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x23; +} + +static void +Opcode_orbc_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x781; +} + +static void +Opcode_xorb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x24; +} + +static void +Opcode_xorb_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4f81; +} + +static void +Opcode_any4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x800; +} + +static void +Opcode_any4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x96501; +} + +static void +Opcode_all4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x900; +} + +static void +Opcode_all4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8e501; +} + +static void +Opcode_any8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa00; +} + +static void +Opcode_any8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc6501; +} + +static void +Opcode_all8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb00; +} + +static void +Opcode_all8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa6501; +} + +static void +Opcode_bf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d0000; +} + +static void +Opcode_bf_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2800003; +} + +static void +Opcode_bt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d0100; +} + +static void +Opcode_bt_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000003; +} + +static void +Opcode_movf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3c; +} + +static void +Opcode_movf_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7b81; +} + +static void +Opcode_movt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3d; +} + +static void +Opcode_movt_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1501; +} + +static void +Opcode_rsr_br_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x430; +} + +static void +Opcode_wsr_br_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x431; +} + +static void +Opcode_xsr_br_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x416; +} + +static void +Opcode_rsr_ccount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xea30; +} + +static void +Opcode_wsr_ccount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xea31; +} + +static void +Opcode_xsr_ccount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xea16; +} + +static void +Opcode_rsr_ccompare0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf030; +} + +static void +Opcode_wsr_ccompare0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf031; +} + +static void +Opcode_xsr_ccompare0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf016; +} + +static void +Opcode_rsr_ccompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf130; +} + +static void +Opcode_wsr_ccompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf131; +} + +static void +Opcode_xsr_ccompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf116; +} + +static void +Opcode_rsr_ccompare2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf230; +} + +static void +Opcode_wsr_ccompare2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf231; +} + +static void +Opcode_xsr_ccompare2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf216; +} + +static void +Opcode_ipf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2c0700; +} + +static void +Opcode_ihi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2e0700; +} + +static void +Opcode_ipfl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2d0700; +} + +static void +Opcode_ihu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2d0720; +} + +static void +Opcode_iiu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2d0730; +} + +static void +Opcode_iii_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2f0700; +} + +static void +Opcode_lict_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f; +} + +static void +Opcode_licw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x21f; +} + +static void +Opcode_sict_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x11f; +} + +static void +Opcode_sicw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x31f; +} + +static void +Opcode_dhwb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x240700; +} + +static void +Opcode_dhwbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x250700; +} + +static void +Opcode_diwbui_p_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2807f0; +} + +static void +Opcode_diwb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280740; +} + +static void +Opcode_diwbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280750; +} + +static void +Opcode_dhi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x260700; +} + +static void +Opcode_dii_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x270700; +} + +static void +Opcode_dpfr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200700; +} + +static void +Opcode_dpfw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x210700; +} + +static void +Opcode_dpfro_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x220700; +} + +static void +Opcode_dpfwo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x230700; +} + +static void +Opcode_dpfl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280700; +} + +static void +Opcode_dhu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280720; +} + +static void +Opcode_diu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280730; +} + +static void +Opcode_sdct_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x91f; +} + +static void +Opcode_ldct_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81f; +} + +static void +Opcode_rsr_prefctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2830; +} + +static void +Opcode_wsr_prefctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2831; +} + +static void +Opcode_xsr_prefctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2816; +} + +static void +Opcode_wsr_ptevaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5331; +} + +static void +Opcode_rsr_ptevaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5330; +} + +static void +Opcode_xsr_ptevaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5316; +} + +static void +Opcode_rsr_rasid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5a30; +} + +static void +Opcode_wsr_rasid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5a31; +} + +static void +Opcode_xsr_rasid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5a16; +} + +static void +Opcode_rsr_itlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5b30; +} + +static void +Opcode_wsr_itlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5b31; +} + +static void +Opcode_xsr_itlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5b16; +} + +static void +Opcode_rsr_dtlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5c30; +} + +static void +Opcode_wsr_dtlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5c31; +} + +static void +Opcode_xsr_dtlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5c16; +} + +static void +Opcode_idtlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc05; +} + +static void +Opcode_pdtlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd05; +} + +static void +Opcode_rdtlb0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb05; +} + +static void +Opcode_rdtlb1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf05; +} + +static void +Opcode_wdtlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe05; +} + +static void +Opcode_iitlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x405; +} + +static void +Opcode_pitlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x505; +} + +static void +Opcode_ritlb0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x305; +} + +static void +Opcode_ritlb1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x705; +} + +static void +Opcode_witlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x605; +} + +static void +Opcode_ldpte_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf1f; +} + +static void +Opcode_hwwitlba_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x105; +} + +static void +Opcode_hwwdtlba_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x905; +} + +static void +Opcode_rsr_cpenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe030; +} + +static void +Opcode_wsr_cpenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe031; +} + +static void +Opcode_xsr_cpenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe016; +} + +static void +Opcode_clamps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x33; +} + +static void +Opcode_clamps_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2b81; +} + +static void +Opcode_min_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x34; +} + +static void +Opcode_min_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3b81; +} + +static void +Opcode_max_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x35; +} + +static void +Opcode_max_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3381; +} + +static void +Opcode_minu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x36; +} + +static void +Opcode_minu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6b81; +} + +static void +Opcode_maxu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x37; +} + +static void +Opcode_maxu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6381; +} + +static void +Opcode_nsa_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe04; +} + +static void +Opcode_nsau_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf04; +} + +static void +Opcode_sext_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x32; +} + +static void +Opcode_sext_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4701; +} + +static void +Opcode_l32ai_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200b00; +} + +static void +Opcode_s32ri_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200f00; +} + +static void +Opcode_s32c1i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200e00; +} + +static void +Opcode_rsr_scompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc30; +} + +static void +Opcode_wsr_scompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc31; +} + +static void +Opcode_xsr_scompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc16; +} + +static void +Opcode_rsr_atomctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6330; +} + +static void +Opcode_wsr_atomctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6331; +} + +static void +Opcode_xsr_atomctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6316; +} + +static void +Opcode_quou_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2c; +} + +static void +Opcode_quos_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2d; +} + +static void +Opcode_remu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2e; +} + +static void +Opcode_rems_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2f; +} + +static void +Opcode_rer_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x604; +} + +static void +Opcode_wer_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x704; +} + +static void +Opcode_rur_ae_ovf_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf03e; +} + +static void +Opcode_wur_ae_ovf_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf03f; +} + +static void +Opcode_rur_ae_bithead_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f03e; +} + +static void +Opcode_wur_ae_bithead_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf13f; +} + +static void +Opcode_rur_ae_ts_fts_bu_bp_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2f03e; +} + +static void +Opcode_wur_ae_ts_fts_bu_bp_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf23f; +} + +static void +Opcode_rur_ae_sd_no_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f03e; +} + +static void +Opcode_wur_ae_sd_no_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf33f; +} + +static void +Opcode_rur_ae_overflow_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40809c; +} + +static void +Opcode_wur_ae_overflow_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000ac; +} + +static void +Opcode_rur_ae_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40909c; +} + +static void +Opcode_wur_ae_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4001ac; +} + +static void +Opcode_rur_ae_bitptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40a09c; +} + +static void +Opcode_wur_ae_bitptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4002ac; +} + +static void +Opcode_rur_ae_bitsused_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40b09c; +} + +static void +Opcode_wur_ae_bitsused_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4003ac; +} + +static void +Opcode_rur_ae_tablesize_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40c09c; +} + +static void +Opcode_wur_ae_tablesize_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4004ac; +} + +static void +Opcode_rur_ae_first_ts_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40d09c; +} + +static void +Opcode_wur_ae_first_ts_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4005ac; +} + +static void +Opcode_rur_ae_nextoffset_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40e09c; +} + +static void +Opcode_wur_ae_nextoffset_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4006ac; +} + +static void +Opcode_rur_ae_searchdone_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40f09c; +} + +static void +Opcode_wur_ae_searchdone_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4007ac; +} + +static void +Opcode_ae_lp16f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81581; +} + +static void +Opcode_ae_lp16f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40005a; +} + +static void +Opcode_ae_lp16f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1781; +} + +static void +Opcode_ae_lp16f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40009a; +} + +static void +Opcode_ae_lp16f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81701; +} + +static void +Opcode_ae_lp16f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000ca; +} + +static void +Opcode_ae_lp16f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81781; +} + +static void +Opcode_ae_lp16f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000fa; +} + +static void +Opcode_ae_lp24_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81d81; +} + +static void +Opcode_ae_lp24_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40085a; +} + +static void +Opcode_ae_lp24_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81f01; +} + +static void +Opcode_ae_lp24_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40089a; +} + +static void +Opcode_ae_lp24_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81f81; +} + +static void +Opcode_ae_lp24_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008ca; +} + +static void +Opcode_ae_lp24_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5581; +} + +static void +Opcode_ae_lp24_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008fa; +} + +static void +Opcode_ae_lp24f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5701; +} + +static void +Opcode_ae_lp24f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40006a; +} + +static void +Opcode_ae_lp24f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5d01; +} + +static void +Opcode_ae_lp24f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000aa; +} + +static void +Opcode_ae_lp24f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85501; +} + +static void +Opcode_ae_lp24f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000da; +} + +static void +Opcode_ae_lp24f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5781; +} + +static void +Opcode_ae_lp24f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000b; +} + +static void +Opcode_ae_lp16x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d81; +} + +static void +Opcode_ae_lp16x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40086a; +} + +static void +Opcode_ae_lp16x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f01; +} + +static void +Opcode_ae_lp16x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008aa; +} + +static void +Opcode_ae_lp16x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81d01; +} + +static void +Opcode_ae_lp16x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008da; +} + +static void +Opcode_ae_lp16x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f81; +} + +static void +Opcode_ae_lp16x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40080b; +} + +static void +Opcode_ae_lp24x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85701; +} + +static void +Opcode_ae_lp24x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40007a; +} + +static void +Opcode_ae_lp24x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85d01; +} + +static void +Opcode_ae_lp24x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000ba; +} + +static void +Opcode_ae_lp24x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85781; +} + +static void +Opcode_ae_lp24x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000ea; +} + +static void +Opcode_ae_lp24x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85d81; +} + +static void +Opcode_ae_lp24x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40001b; +} + +static void +Opcode_ae_lp24x2_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5d81; +} + +static void +Opcode_ae_lp24x2_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40087a; +} + +static void +Opcode_ae_lp24x2_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5f01; +} + +static void +Opcode_ae_lp24x2_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008ba; +} + +static void +Opcode_ae_lp24x2_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5f81; +} + +static void +Opcode_ae_lp24x2_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008ea; +} + +static void +Opcode_ae_lp24x2_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85581; +} + +static void +Opcode_ae_lp24x2_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40081b; +} + +static void +Opcode_ae_sp16x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3501; +} + +static void +Opcode_ae_sp16x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40002b; +} + +static void +Opcode_ae_sp16x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6501; +} + +static void +Opcode_ae_sp16x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40005b; +} + +static void +Opcode_ae_sp16x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82581; +} + +static void +Opcode_ae_sp16x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40008b; +} + +static void +Opcode_ae_sp16x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2781; +} + +static void +Opcode_ae_sp16x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000bb; +} + +static void +Opcode_ae_sp24x2s_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83501; +} + +static void +Opcode_ae_sp24x2s_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40082b; +} + +static void +Opcode_ae_sp24x2s_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3781; +} + +static void +Opcode_ae_sp24x2s_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40085b; +} + +static void +Opcode_ae_sp24x2s_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3d81; +} + +static void +Opcode_ae_sp24x2s_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40088b; +} + +static void +Opcode_ae_sp24x2s_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f01; +} + +static void +Opcode_ae_sp24x2s_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008bb; +} + +static void +Opcode_ae_sp24x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82f81; +} + +static void +Opcode_ae_sp24x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40003b; +} + +static void +Opcode_ae_sp24x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3581; +} + +static void +Opcode_ae_sp24x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40006b; +} + +static void +Opcode_ae_sp24x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3701; +} + +static void +Opcode_ae_sp24x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40009b; +} + +static void +Opcode_ae_sp24x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3d01; +} + +static void +Opcode_ae_sp24x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000cb; +} + +static void +Opcode_ae_sp16f_l_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85f81; +} + +static void +Opcode_ae_sp16f_l_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40083b; +} + +static void +Opcode_ae_sp16f_l_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2581; +} + +static void +Opcode_ae_sp16f_l_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40086b; +} + +static void +Opcode_ae_sp16f_l_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2701; +} + +static void +Opcode_ae_sp16f_l_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40089b; +} + +static void +Opcode_ae_sp16f_l_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2d01; +} + +static void +Opcode_ae_sp16f_l_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008cb; +} + +static void +Opcode_ae_sp24s_l_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82d01; +} + +static void +Opcode_ae_sp24s_l_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40004b; +} + +static void +Opcode_ae_sp24s_l_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2f81; +} + +static void +Opcode_ae_sp24s_l_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40007b; +} + +static void +Opcode_ae_sp24s_l_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82d81; +} + +static void +Opcode_ae_sp24s_l_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000ab; +} + +static void +Opcode_ae_sp24s_l_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82f01; +} + +static void +Opcode_ae_sp24s_l_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000db; +} + +static void +Opcode_ae_sp24f_l_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82701; +} + +static void +Opcode_ae_sp24f_l_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40084b; +} + +static void +Opcode_ae_sp24f_l_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x82781; +} + +static void +Opcode_ae_sp24f_l_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40087b; +} + +static void +Opcode_ae_sp24f_l_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2d81; +} + +static void +Opcode_ae_sp24f_l_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008ab; +} + +static void +Opcode_ae_sp24f_l_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2f01; +} + +static void +Opcode_ae_sp24f_l_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008db; +} + +static void +Opcode_ae_lq56_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x206581; +} + +static void +Opcode_ae_lq56_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40001c; +} + +static void +Opcode_ae_lq56_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x406581; +} + +static void +Opcode_ae_lq56_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40021c; +} + +static void +Opcode_ae_lq56_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x606581; +} + +static void +Opcode_ae_lq56_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40002c; +} + +static void +Opcode_ae_lq56_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6781; +} + +static void +Opcode_ae_lq56_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40022c; +} + +static void +Opcode_ae_lq32f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6581; +} + +static void +Opcode_ae_lq32f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40011c; +} + +static void +Opcode_ae_lq32f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6701; +} + +static void +Opcode_ae_lq32f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40031c; +} + +static void +Opcode_ae_lq32f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d01; +} + +static void +Opcode_ae_lq32f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40012c; +} + +static void +Opcode_ae_lq32f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7501; +} + +static void +Opcode_ae_lq32f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40032c; +} + +static void +Opcode_ae_sq56s_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83781; +} + +static void +Opcode_ae_sq56s_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40003c; +} + +static void +Opcode_ae_sq56s_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x183701; +} + +static void +Opcode_ae_sq56s_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40083c; +} + +static void +Opcode_ae_sq56s_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x183781; +} + +static void +Opcode_ae_sq56s_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40004c; +} + +static void +Opcode_ae_sq56s_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83d81; +} + +static void +Opcode_ae_sq56s_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40084c; +} + +static void +Opcode_ae_sq32f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83581; +} + +static void +Opcode_ae_sq32f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40043c; +} + +static void +Opcode_ae_sq32f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83701; +} + +static void +Opcode_ae_sq32f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400c3c; +} + +static void +Opcode_ae_sq32f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83d01; +} + +static void +Opcode_ae_sq32f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40044c; +} + +static void +Opcode_ae_sq32f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x183581; +} + +static void +Opcode_ae_sq32f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400c4c; +} + +static void +Opcode_ae_zerop48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x46001; +} + +static void +Opcode_ae_movp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86001; +} + +static void +Opcode_ae_movp48_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86501; +} + +static void +Opcode_ae_movp48_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40009c; +} + +static void +Opcode_ae_selp24_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd081; +} + +static void +Opcode_ae_selp24_ll_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x24000; +} + +static void +Opcode_ae_selp24_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x89001; +} + +static void +Opcode_ae_selp24_lh_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x14000; +} + +static void +Opcode_ae_selp24_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd001; +} + +static void +Opcode_ae_selp24_hl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc000; +} + +static void +Opcode_ae_selp24_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9081; +} + +static void +Opcode_ae_selp24_hh_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000; +} + +static void +Opcode_ae_movtp24x2_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x402081; +} + +static void +Opcode_ae_movfp24x2_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x202081; +} + +static void +Opcode_ae_movtp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2001; +} + +static void +Opcode_ae_movfp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1001; +} + +static void +Opcode_ae_movpa24x2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85f01; +} + +static void +Opcode_ae_movpa24x2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000c; +} + +static void +Opcode_ae_truncp24a32x2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f81; +} + +static void +Opcode_ae_truncp24a32x2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40080c; +} + +static void +Opcode_ae_cvta32p24_l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83f81; +} + +static void +Opcode_ae_cvta32p24_l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000bc; +} + +static void +Opcode_ae_cvta32p24_h_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x83f01; +} + +static void +Opcode_ae_cvta32p24_h_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008bc; +} + +static void +Opcode_ae_cvtp24a16x2_ll_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5501; +} + +static void +Opcode_ae_cvtp24a16x2_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000eb; +} + +static void +Opcode_ae_cvtp24a16x2_lh_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d01; +} + +static void +Opcode_ae_cvtp24a16x2_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008eb; +} + +static void +Opcode_ae_cvtp24a16x2_hl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1701; +} + +static void +Opcode_ae_cvtp24a16x2_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000fb; +} + +static void +Opcode_ae_cvtp24a16x2_hh_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1581; +} + +static void +Opcode_ae_cvtp24a16x2_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008fb; +} + +static void +Opcode_ae_truncp24q48x2_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20005; +} + +static void +Opcode_ae_truncp16_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86e01; +} + +static void +Opcode_ae_roundsp24q48sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3881; +} + +static void +Opcode_ae_roundsp24q48asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3481; +} + +static void +Opcode_ae_roundsp16q48sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3281; +} + +static void +Opcode_ae_roundsp16q48asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3081; +} + +static void +Opcode_ae_roundsp16sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86401; +} + +static void +Opcode_ae_roundsp16asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86201; +} + +static void +Opcode_ae_zeroq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x284581; +} + +static void +Opcode_ae_movq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380581; +} + +static void +Opcode_ae_movq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xef01; +} + +static void +Opcode_ae_movq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41409c; +} + +static void +Opcode_ae_movtq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x806f81; +} + +static void +Opcode_ae_movtq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41005e; +} + +static void +Opcode_ae_movfq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6f81; +} + +static void +Opcode_ae_movfq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41006e; +} + +static void +Opcode_ae_cvtq48a32s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x206701; +} + +static void +Opcode_ae_cvtq48a32s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x43027e; +} + +static void +Opcode_ae_cvtq48p24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300581; +} + +static void +Opcode_ae_cvtq48p24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280581; +} + +static void +Opcode_ae_satq48s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x488605; +} + +static void +Opcode_ae_truncq32_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3c0581; +} + +static void +Opcode_ae_roundsq32sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3a0581; +} + +static void +Opcode_ae_roundsq32asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x390581; +} + +static void +Opcode_ae_trunca32q48_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x183d81; +} + +static void +Opcode_ae_trunca32q48_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41007e; +} + +static void +Opcode_ae_movap24s_l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1083f01; +} + +static void +Opcode_ae_movap24s_l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40007c; +} + +static void +Opcode_ae_movap24s_h_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x883f01; +} + +static void +Opcode_ae_movap24s_h_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40087c; +} + +static void +Opcode_ae_trunca16p24s_l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4083f01; +} + +static void +Opcode_ae_trunca16p24s_l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40008c; +} + +static void +Opcode_ae_trunca16p24s_h_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2083f01; +} + +static void +Opcode_ae_trunca16p24s_h_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40088c; +} + +static void +Opcode_ae_addp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1081; +} + +static void +Opcode_ae_subp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x89081; +} + +static void +Opcode_ae_negp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16001; +} + +static void +Opcode_ae_absp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6001; +} + +static void +Opcode_ae_maxp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81081; +} + +static void +Opcode_ae_minp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5081; +} + +static void +Opcode_ae_maxbp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1; +} + +static void +Opcode_ae_minbp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x81; +} + +static void +Opcode_ae_addsp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5001; +} + +static void +Opcode_ae_subsp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8d001; +} + +static void +Opcode_ae_negsp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x26001; +} + +static void +Opcode_ae_abssp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa001; +} + +static void +Opcode_ae_andp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9001; +} + +static void +Opcode_ae_nandp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85001; +} + +static void +Opcode_ae_orp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x85081; +} + +static void +Opcode_ae_xorp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8d081; +} + +static void +Opcode_ae_ltp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x102081; +} + +static void +Opcode_ae_lep24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3001; +} + +static void +Opcode_ae_eqp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2081; +} + +static void +Opcode_ae_addq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40005; +} + +static void +Opcode_ae_subq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280605; +} + +static void +Opcode_ae_negq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600605; +} + +static void +Opcode_ae_absq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x480605; +} + +static void +Opcode_ae_maxq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100605; +} + +static void +Opcode_ae_minq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200605; +} + +static void +Opcode_ae_maxbq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5; +} + +static void +Opcode_ae_minbq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x205; +} + +static void +Opcode_ae_addsq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x605; +} + +static void +Opcode_ae_subsq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300605; +} + +static void +Opcode_ae_negsq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x484605; +} + +static void +Opcode_ae_abssq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500605; +} + +static void +Opcode_ae_andq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80605; +} + +static void +Opcode_ae_nandq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400605; +} + +static void +Opcode_ae_orq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180605; +} + +static void +Opcode_ae_xorq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380605; +} + +static void +Opcode_ae_sllip24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x101; +} + +static void +Opcode_ae_srlip24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x501; +} + +static void +Opcode_ae_sraip24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x301; +} + +static void +Opcode_ae_sllsp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86801; +} + +static void +Opcode_ae_srlsp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86c01; +} + +static void +Opcode_ae_srasp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86a01; +} + +static void +Opcode_ae_sllisp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x181; +} + +static void +Opcode_ae_sllssp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86601; +} + +static void +Opcode_ae_slliq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6d81; +} + +static void +Opcode_ae_slliq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40005c; +} + +static void +Opcode_ae_srliq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16d81; +} + +static void +Opcode_ae_srliq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40105c; +} + +static void +Opcode_ae_sraiq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xed81; +} + +static void +Opcode_ae_sraiq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40205c; +} + +static void +Opcode_ae_sllsq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16f01; +} + +static void +Opcode_ae_sllsq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41009c; +} + +static void +Opcode_ae_srlsq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80ef01; +} + +static void +Opcode_ae_srlsq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41109c; +} + +static void +Opcode_ae_srasq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4ef01; +} + +static void +Opcode_ae_srasq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41209c; +} + +static void +Opcode_ae_sllaq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1006f81; +} + +static void +Opcode_ae_sllaq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41001e; +} + +static void +Opcode_ae_srlaq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1806f81; +} + +static void +Opcode_ae_srlaq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41002e; +} + +static void +Opcode_ae_sraaq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4006f81; +} + +static void +Opcode_ae_sraaq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41003e; +} + +static void +Opcode_ae_sllisq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6f01; +} + +static void +Opcode_ae_sllisq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40305c; +} + +static void +Opcode_ae_sllssq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2ef01; +} + +static void +Opcode_ae_sllssq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41309c; +} + +static void +Opcode_ae_sllasq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2006f81; +} + +static void +Opcode_ae_sllasq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41004e; +} + +static void +Opcode_ae_ltq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10005; +} + +static void +Opcode_ae_leq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x805; +} + +static void +Opcode_ae_eqq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x405; +} + +static void +Opcode_ae_nsaq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x183d01; +} + +static void +Opcode_ae_nsaq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41047e; +} + +static void +Opcode_ae_mulsrfq32sp24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500901; +} + +static void +Opcode_ae_mulsrfq32sp24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300901; +} + +static void +Opcode_ae_mularfq32sp24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600901; +} + +static void +Opcode_ae_mularfq32sp24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100901; +} + +static void +Opcode_ae_mulrfq32sp24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400901; +} + +static void +Opcode_ae_mulrfq32sp24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200901; +} + +static void +Opcode_ae_mulsfq32sp24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680901; +} + +static void +Opcode_ae_mulsfq32sp24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180901; +} + +static void +Opcode_ae_mulafq32sp24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x480901; +} + +static void +Opcode_ae_mulafq32sp24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280901; +} + +static void +Opcode_ae_mulfq32sp24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700901; +} + +static void +Opcode_ae_mulfq32sp24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80901; +} + +static void +Opcode_ae_mulfs32p16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100086; +} + +static void +Opcode_ae_mulfp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x88186; +} + +static void +Opcode_ae_mulp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180006; +} + +static void +Opcode_ae_mulfs32p16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8c186; +} + +static void +Opcode_ae_mulfp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8c006; +} + +static void +Opcode_ae_mulp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x108006; +} + +static void +Opcode_ae_mulfs32p16s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8c106; +} + +static void +Opcode_ae_mulfp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x88106; +} + +static void +Opcode_ae_mulp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x104006; +} + +static void +Opcode_ae_mulfs32p16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8c086; +} + +static void +Opcode_ae_mulfp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x88086; +} + +static void +Opcode_ae_mulp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100106; +} + +static void +Opcode_ae_mulafs32p16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4106; +} + +static void +Opcode_ae_mulafp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200006; +} + +static void +Opcode_ae_mulap24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc186; +} + +static void +Opcode_ae_mulafs32p16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4086; +} + +static void +Opcode_ae_mulafp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100006; +} + +static void +Opcode_ae_mulap24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc106; +} + +static void +Opcode_ae_mulafs32p16s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x186; +} + +static void +Opcode_ae_mulafp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80006; +} + +static void +Opcode_ae_mulap24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc086; +} + +static void +Opcode_ae_mulafs32p16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400006; +} + +static void +Opcode_ae_mulafp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8006; +} + +static void +Opcode_ae_mulap24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8186; +} + +static void +Opcode_ae_mulsfs32p16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180086; +} + +static void +Opcode_ae_mulsfp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x108186; +} + +static void +Opcode_ae_mulsp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x188086; +} + +static void +Opcode_ae_mulsfs32p16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10c186; +} + +static void +Opcode_ae_mulsfp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10c006; +} + +static void +Opcode_ae_mulsp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x184186; +} + +static void +Opcode_ae_mulsfs32p16s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10c106; +} + +static void +Opcode_ae_mulsfp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x108106; +} + +static void +Opcode_ae_mulsp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x184106; +} + +static void +Opcode_ae_mulsfs32p16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10c086; +} + +static void +Opcode_ae_mulsfp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x108086; +} + +static void +Opcode_ae_mulsp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x184086; +} + +static void +Opcode_ae_mulafs56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc006; +} + +static void +Opcode_ae_mulas56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x88006; +} + +static void +Opcode_ae_mulafs56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8106; +} + +static void +Opcode_ae_mulas56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x84006; +} + +static void +Opcode_ae_mulafs56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8086; +} + +static void +Opcode_ae_mulas56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80106; +} + +static void +Opcode_ae_mulafs56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4186; +} + +static void +Opcode_ae_mulas56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80086; +} + +static void +Opcode_ae_mulsfs56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180186; +} + +static void +Opcode_ae_mulss56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18c086; +} + +static void +Opcode_ae_mulsfs56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x188006; +} + +static void +Opcode_ae_mulss56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x188186; +} + +static void +Opcode_ae_mulsfs56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x184006; +} + +static void +Opcode_ae_mulss56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18c006; +} + +static void +Opcode_ae_mulsfs56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180106; +} + +static void +Opcode_ae_mulss56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x188106; +} + +static void +Opcode_ae_mulfq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380381; +} + +static void +Opcode_ae_mulfq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300381; +} + +static void +Opcode_ae_mulfq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500381; +} + +static void +Opcode_ae_mulfq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x480381; +} + +static void +Opcode_ae_mulq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x580381; +} + +static void +Opcode_ae_mulq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600381; +} + +static void +Opcode_ae_mulq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700381; +} + +static void +Opcode_ae_mulq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680381; +} + +static void +Opcode_ae_mulafq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x381; +} + +static void +Opcode_ae_mulafq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x901; +} + +static void +Opcode_ae_mulafq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100381; +} + +static void +Opcode_ae_mulafq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80381; +} + +static void +Opcode_ae_mulaq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400381; +} + +static void +Opcode_ae_mulaq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200381; +} + +static void +Opcode_ae_mulaq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280381; +} + +static void +Opcode_ae_mulaq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180381; +} + +static void +Opcode_ae_mulsfq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x581; +} + +static void +Opcode_ae_mulsfq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x780381; +} + +static void +Opcode_ae_mulsfq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80581; +} + +static void +Opcode_ae_mulsfq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x701; +} + +static void +Opcode_ae_mulsq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200581; +} + +static void +Opcode_ae_mulsq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100581; +} + +static void +Opcode_ae_mulsq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180581; +} + +static void +Opcode_ae_mulsq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400581; +} + +static void +Opcode_ae_mulzaaq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380002; +} + +static void +Opcode_ae_mulzaafq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100002; +} + +static void +Opcode_ae_mulzaaq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600002; +} + +static void +Opcode_ae_mulzaafq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180002; +} + +static void +Opcode_ae_mulzaaq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280002; +} + +static void +Opcode_ae_mulzaafq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2; +} + +static void +Opcode_ae_mulzaaq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x480002; +} + +static void +Opcode_ae_mulzaafq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200002; +} + +static void +Opcode_ae_mulzaaq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300002; +} + +static void +Opcode_ae_mulzaafq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80002; +} + +static void +Opcode_ae_mulzaaq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500002; +} + +static void +Opcode_ae_mulzaafq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400002; +} + +static void +Opcode_ae_mulzasq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400003; +} + +static void +Opcode_ae_mulzasfq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700002; +} + +static void +Opcode_ae_mulzasq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300003; +} + +static void +Opcode_ae_mulzasfq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80003; +} + +static void +Opcode_ae_mulzasq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100003; +} + +static void +Opcode_ae_mulzasfq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x580002; +} + +static void +Opcode_ae_mulzasq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180003; +} + +static void +Opcode_ae_mulzasfq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x780002; +} + +static void +Opcode_ae_mulzasq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200003; +} + +static void +Opcode_ae_mulzasfq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680002; +} + +static void +Opcode_ae_mulzasq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280003; +} + +static void +Opcode_ae_mulzasfq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3; +} + +static void +Opcode_ae_mulzsaq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4; +} + +static void +Opcode_ae_mulzsafq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500003; +} + +static void +Opcode_ae_mulzsaq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200004; +} + +static void +Opcode_ae_mulzsafq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680003; +} + +static void +Opcode_ae_mulzsaq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700003; +} + +static void +Opcode_ae_mulzsafq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380003; +} + +static void +Opcode_ae_mulzsaq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80004; +} + +static void +Opcode_ae_mulzsafq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600003; +} + +static void +Opcode_ae_mulzsaq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x780003; +} + +static void +Opcode_ae_mulzsafq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x480003; +} + +static void +Opcode_ae_mulzsaq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100004; +} + +static void +Opcode_ae_mulzsafq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x580003; +} + +static void +Opcode_ae_mulzssq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x580004; +} + +static void +Opcode_ae_mulzssfq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280004; +} + +static void +Opcode_ae_mulzssq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x780004; +} + +static void +Opcode_ae_mulzssfq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x480004; +} + +static void +Opcode_ae_mulzssq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500004; +} + +static void +Opcode_ae_mulzssfq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400004; +} + +static void +Opcode_ae_mulzssq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x680004; +} + +static void +Opcode_ae_mulzssfq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300004; +} + +static void +Opcode_ae_mulzssq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600004; +} + +static void +Opcode_ae_mulzssfq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180004; +} + +static void +Opcode_ae_mulzssq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x700004; +} + +static void +Opcode_ae_mulzssfq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x380004; +} + +static void +Opcode_ae_mulzaafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x204006; +} + +static void +Opcode_ae_mulzaap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280006; +} + +static void +Opcode_ae_mulzaafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x208006; +} + +static void +Opcode_ae_mulzaap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300006; +} + +static void +Opcode_ae_mulzasfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200186; +} + +static void +Opcode_ae_mulzasp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x204106; +} + +static void +Opcode_ae_mulzasfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x204086; +} + +static void +Opcode_ae_mulzasp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x204186; +} + +static void +Opcode_ae_mulzsafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x208086; +} + +static void +Opcode_ae_mulzsap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20c006; +} + +static void +Opcode_ae_mulzsafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x208106; +} + +static void +Opcode_ae_mulzsap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x208186; +} + +static void +Opcode_ae_mulzssfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20c086; +} + +static void +Opcode_ae_mulzssp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20c186; +} + +static void +Opcode_ae_mulzssfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20c106; +} + +static void +Opcode_ae_mulzssp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x280086; +} + +static void +Opcode_ae_mulaafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6; +} + +static void +Opcode_ae_mulaap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x106; +} + +static void +Opcode_ae_mulaafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x86; +} + +static void +Opcode_ae_mulaap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4006; +} + +static void +Opcode_ae_mulasfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80186; +} + +static void +Opcode_ae_mulasp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x84106; +} + +static void +Opcode_ae_mulasfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x84086; +} + +static void +Opcode_ae_mulasp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x84186; +} + +static void +Opcode_ae_mulsafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100186; +} + +static void +Opcode_ae_mulsap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x104106; +} + +static void +Opcode_ae_mulsafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x104086; +} + +static void +Opcode_ae_mulsap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x104186; +} + +static void +Opcode_ae_mulssfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18c106; +} + +static void +Opcode_ae_mulssp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200086; +} + +static void +Opcode_ae_mulssfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18c186; +} + +static void +Opcode_ae_mulssp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200106; +} + +static void +Opcode_ae_sha32_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x41000e; +} + +static void +Opcode_ae_vldl32t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000a; +} + +static void +Opcode_ae_vldl16t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40001a; +} + +static void +Opcode_ae_vldl16c_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x410e7e; +} + +static void +Opcode_ae_vldsht_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4008ac; +} + +static void +Opcode_ae_lb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40006c; +} + +static void +Opcode_ae_lbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x42000e; +} + +static void +Opcode_ae_lbk_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40002a; +} + +static void +Opcode_ae_lbki_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000e; +} + +static void +Opcode_ae_db_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40010f; +} + +static void +Opcode_ae_dbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40020f; +} + +static void +Opcode_ae_vlel32t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40003a; +} + +static void +Opcode_ae_vlel16t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40004a; +} + +static void +Opcode_ae_sb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40011f; +} + +static void +Opcode_ae_sbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000f; +} + +static void +Opcode_ae_vles16c_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x410c7e; +} + +static void +Opcode_ae_sbf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x410d7e; +} + +static void +Opcode_ae_slaasq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6006f81; +} + +static void +Opcode_ae_addbrba32_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0; +} + +static void +Opcode_ae_minabssp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8e001; +} + +static void +Opcode_ae_maxabssp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe001; +} + +static void +Opcode_ae_minabssq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x240005; +} + +static void +Opcode_ae_maxabssq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x440005; +} + +static void +Opcode_rur_ae_cbegin0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6f03e; +} + +static void +Opcode_wur_ae_cbegin0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf63f; +} + +static void +Opcode_rur_ae_cend0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7f03e; +} + +static void +Opcode_wur_ae_cend0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf73f; +} + +static void +Opcode_ae_lp24x2_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7f01; +} + +static void +Opcode_ae_sp24x2s_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87781; +} + +static void +Opcode_ae_lp24x2f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87f01; +} + +static void +Opcode_ae_sp24x2f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7781; +} + +static void +Opcode_ae_lp16x2f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87d01; +} + +static void +Opcode_ae_sp16x2f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87581; +} + +static void +Opcode_ae_lp24_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7701; +} + +static void +Opcode_ae_sp24s_l_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87d81; +} + +static void +Opcode_ae_lp24f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87701; +} + +static void +Opcode_ae_sp24f_l_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7d81; +} + +static void +Opcode_ae_lp16f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7d01; +} + +static void +Opcode_ae_sp16f_l_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7581; +} + +static void +Opcode_ae_lq56_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x207501; +} + +static void +Opcode_ae_sq56s_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x87f81; +} + +static void +Opcode_ae_lq32f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x407501; +} + +static void +Opcode_ae_sq32f_c_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7f81; +} + +static void +Opcode_rur_expstate_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6e03e; +} + +static void +Opcode_wur_expstate_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe63f; +} + +static void +Opcode_read_impwire_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe0; +} + +static void +Opcode_setb_expstate_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e0; +} + +static void +Opcode_clrb_expstate_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x21e0; +} + +static void +Opcode_wrmsk_expstate_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2e0; +} + +static xtensa_opcode_encode_fn Opcode_excw_encode_fns[] = { + Opcode_excw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfe_encode_fns[] = { + Opcode_rfe_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfde_encode_fns[] = { + Opcode_rfde_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_syscall_encode_fns[] = { + Opcode_syscall_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_call12_encode_fns[] = { + Opcode_call12_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_call8_encode_fns[] = { + Opcode_call8_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_call4_encode_fns[] = { + Opcode_call4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx12_encode_fns[] = { + Opcode_callx12_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx8_encode_fns[] = { + Opcode_callx8_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx4_encode_fns[] = { + Opcode_callx4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_entry_encode_fns[] = { + Opcode_entry_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_movsp_encode_fns[] = { + Opcode_movsp_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rotw_encode_fns[] = { + Opcode_rotw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_retw_encode_fns[] = { + Opcode_retw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_retw_n_encode_fns[] = { + 0, 0, Opcode_retw_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfwo_encode_fns[] = { + Opcode_rfwo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfwu_encode_fns[] = { + Opcode_rfwu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_l32e_encode_fns[] = { + Opcode_l32e_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32e_encode_fns[] = { + Opcode_s32e_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_windowbase_encode_fns[] = { + Opcode_rsr_windowbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_windowbase_encode_fns[] = { + Opcode_wsr_windowbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_windowbase_encode_fns[] = { + Opcode_xsr_windowbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_windowstart_encode_fns[] = { + Opcode_rsr_windowstart_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_windowstart_encode_fns[] = { + Opcode_wsr_windowstart_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_windowstart_encode_fns[] = { + Opcode_xsr_windowstart_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_add_n_encode_fns[] = { + 0, Opcode_add_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_addi_n_encode_fns[] = { + 0, Opcode_addi_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_beqz_n_encode_fns[] = { + 0, 0, Opcode_beqz_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_bnez_n_encode_fns[] = { + 0, 0, Opcode_bnez_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ill_n_encode_fns[] = { + 0, 0, Opcode_ill_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_l32i_n_encode_fns[] = { + 0, Opcode_l32i_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mov_n_encode_fns[] = { + 0, 0, Opcode_mov_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_movi_n_encode_fns[] = { + 0, 0, Opcode_movi_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_nop_n_encode_fns[] = { + 0, 0, Opcode_nop_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ret_n_encode_fns[] = { + 0, 0, Opcode_ret_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32i_n_encode_fns[] = { + 0, Opcode_s32i_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_threadptr_encode_fns[] = { + Opcode_rur_threadptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_threadptr_encode_fns[] = { + Opcode_wur_threadptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_addi_encode_fns[] = { + Opcode_addi_Slot_inst_encode, 0, 0, 0, Opcode_addi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addmi_encode_fns[] = { + Opcode_addmi_Slot_inst_encode, 0, 0, 0, Opcode_addmi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_add_encode_fns[] = { + Opcode_add_Slot_inst_encode, 0, 0, 0, Opcode_add_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_sub_encode_fns[] = { + Opcode_sub_Slot_inst_encode, 0, 0, 0, Opcode_sub_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addx2_encode_fns[] = { + Opcode_addx2_Slot_inst_encode, 0, 0, 0, Opcode_addx2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addx4_encode_fns[] = { + Opcode_addx4_Slot_inst_encode, 0, 0, 0, Opcode_addx4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addx8_encode_fns[] = { + Opcode_addx8_Slot_inst_encode, 0, 0, 0, Opcode_addx8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_subx2_encode_fns[] = { + Opcode_subx2_Slot_inst_encode, 0, 0, 0, Opcode_subx2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_subx4_encode_fns[] = { + Opcode_subx4_Slot_inst_encode, 0, 0, 0, Opcode_subx4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_subx8_encode_fns[] = { + Opcode_subx8_Slot_inst_encode, 0, 0, 0, Opcode_subx8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_and_encode_fns[] = { + Opcode_and_Slot_inst_encode, 0, 0, 0, Opcode_and_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_or_encode_fns[] = { + Opcode_or_Slot_inst_encode, 0, 0, 0, Opcode_or_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_xor_encode_fns[] = { + Opcode_xor_Slot_inst_encode, 0, 0, 0, Opcode_xor_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_beqi_encode_fns[] = { + Opcode_beqi_Slot_inst_encode, 0, 0, 0, Opcode_beqi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnei_encode_fns[] = { + Opcode_bnei_Slot_inst_encode, 0, 0, 0, Opcode_bnei_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgei_encode_fns[] = { + Opcode_bgei_Slot_inst_encode, 0, 0, 0, Opcode_bgei_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_blti_encode_fns[] = { + Opcode_blti_Slot_inst_encode, 0, 0, 0, Opcode_blti_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbci_encode_fns[] = { + Opcode_bbci_Slot_inst_encode, 0, 0, 0, Opcode_bbci_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbsi_encode_fns[] = { + Opcode_bbsi_Slot_inst_encode, 0, 0, 0, Opcode_bbsi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgeui_encode_fns[] = { + Opcode_bgeui_Slot_inst_encode, 0, 0, 0, Opcode_bgeui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bltui_encode_fns[] = { + Opcode_bltui_Slot_inst_encode, 0, 0, 0, Opcode_bltui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_beq_encode_fns[] = { + Opcode_beq_Slot_inst_encode, 0, 0, 0, Opcode_beq_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bne_encode_fns[] = { + Opcode_bne_Slot_inst_encode, 0, 0, 0, Opcode_bne_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bge_encode_fns[] = { + Opcode_bge_Slot_inst_encode, 0, 0, 0, Opcode_bge_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_blt_encode_fns[] = { + Opcode_blt_Slot_inst_encode, 0, 0, 0, Opcode_blt_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgeu_encode_fns[] = { + Opcode_bgeu_Slot_inst_encode, 0, 0, 0, Opcode_bgeu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bltu_encode_fns[] = { + Opcode_bltu_Slot_inst_encode, 0, 0, 0, Opcode_bltu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bany_encode_fns[] = { + Opcode_bany_Slot_inst_encode, 0, 0, 0, Opcode_bany_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnone_encode_fns[] = { + Opcode_bnone_Slot_inst_encode, 0, 0, 0, Opcode_bnone_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ball_encode_fns[] = { + Opcode_ball_Slot_inst_encode, 0, 0, 0, Opcode_ball_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnall_encode_fns[] = { + Opcode_bnall_Slot_inst_encode, 0, 0, 0, Opcode_bnall_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbc_encode_fns[] = { + Opcode_bbc_Slot_inst_encode, 0, 0, 0, Opcode_bbc_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbs_encode_fns[] = { + Opcode_bbs_Slot_inst_encode, 0, 0, 0, Opcode_bbs_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_beqz_encode_fns[] = { + Opcode_beqz_Slot_inst_encode, 0, 0, 0, Opcode_beqz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnez_encode_fns[] = { + Opcode_bnez_Slot_inst_encode, 0, 0, 0, Opcode_bnez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgez_encode_fns[] = { + Opcode_bgez_Slot_inst_encode, 0, 0, 0, Opcode_bgez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bltz_encode_fns[] = { + Opcode_bltz_Slot_inst_encode, 0, 0, 0, Opcode_bltz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_call0_encode_fns[] = { + Opcode_call0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx0_encode_fns[] = { + Opcode_callx0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_extui_encode_fns[] = { + Opcode_extui_Slot_inst_encode, 0, 0, 0, Opcode_extui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ill_encode_fns[] = { + Opcode_ill_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_j_encode_fns[] = { + Opcode_j_Slot_inst_encode, 0, 0, 0, Opcode_j_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_jx_encode_fns[] = { + Opcode_jx_Slot_inst_encode, 0, 0, 0, Opcode_jx_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l16ui_encode_fns[] = { + Opcode_l16ui_Slot_inst_encode, 0, 0, 0, Opcode_l16ui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l16si_encode_fns[] = { + Opcode_l16si_Slot_inst_encode, 0, 0, 0, Opcode_l16si_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l32i_encode_fns[] = { + Opcode_l32i_Slot_inst_encode, 0, 0, 0, Opcode_l32i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l32r_encode_fns[] = { + Opcode_l32r_Slot_inst_encode, 0, 0, 0, Opcode_l32r_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l8ui_encode_fns[] = { + Opcode_l8ui_Slot_inst_encode, 0, 0, 0, Opcode_l8ui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_loop_encode_fns[] = { + Opcode_loop_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_loopnez_encode_fns[] = { + Opcode_loopnez_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_loopgtz_encode_fns[] = { + Opcode_loopgtz_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_movi_encode_fns[] = { + Opcode_movi_Slot_inst_encode, 0, 0, 0, Opcode_movi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_moveqz_encode_fns[] = { + Opcode_moveqz_Slot_inst_encode, 0, 0, 0, Opcode_moveqz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movnez_encode_fns[] = { + Opcode_movnez_Slot_inst_encode, 0, 0, 0, Opcode_movnez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movltz_encode_fns[] = { + Opcode_movltz_Slot_inst_encode, 0, 0, 0, Opcode_movltz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movgez_encode_fns[] = { + Opcode_movgez_Slot_inst_encode, 0, 0, 0, Opcode_movgez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_neg_encode_fns[] = { + Opcode_neg_Slot_inst_encode, 0, 0, 0, Opcode_neg_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_abs_encode_fns[] = { + Opcode_abs_Slot_inst_encode, 0, 0, 0, Opcode_abs_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_nop_encode_fns[] = { + Opcode_nop_Slot_inst_encode, 0, 0, Opcode_nop_Slot_ae_slot1_encode, Opcode_nop_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ret_encode_fns[] = { + Opcode_ret_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_simcall_encode_fns[] = { + Opcode_simcall_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s16i_encode_fns[] = { + Opcode_s16i_Slot_inst_encode, 0, 0, 0, Opcode_s16i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_s32i_encode_fns[] = { + Opcode_s32i_Slot_inst_encode, 0, 0, 0, Opcode_s32i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_s32nb_encode_fns[] = { + Opcode_s32nb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s8i_encode_fns[] = { + Opcode_s8i_Slot_inst_encode, 0, 0, 0, Opcode_s8i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssr_encode_fns[] = { + Opcode_ssr_Slot_inst_encode, 0, 0, 0, Opcode_ssr_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssl_encode_fns[] = { + Opcode_ssl_Slot_inst_encode, 0, 0, 0, Opcode_ssl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssa8l_encode_fns[] = { + Opcode_ssa8l_Slot_inst_encode, 0, 0, 0, Opcode_ssa8l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssa8b_encode_fns[] = { + Opcode_ssa8b_Slot_inst_encode, 0, 0, 0, Opcode_ssa8b_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssai_encode_fns[] = { + Opcode_ssai_Slot_inst_encode, 0, 0, 0, Opcode_ssai_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_sll_encode_fns[] = { + Opcode_sll_Slot_inst_encode, 0, 0, 0, Opcode_sll_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_src_encode_fns[] = { + Opcode_src_Slot_inst_encode, 0, 0, 0, Opcode_src_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_srl_encode_fns[] = { + Opcode_srl_Slot_inst_encode, 0, 0, 0, Opcode_srl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_sra_encode_fns[] = { + Opcode_sra_Slot_inst_encode, 0, 0, 0, Opcode_sra_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_slli_encode_fns[] = { + Opcode_slli_Slot_inst_encode, 0, 0, 0, Opcode_slli_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_srai_encode_fns[] = { + Opcode_srai_Slot_inst_encode, 0, 0, 0, Opcode_srai_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_srli_encode_fns[] = { + Opcode_srli_Slot_inst_encode, 0, 0, 0, Opcode_srli_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_memw_encode_fns[] = { + Opcode_memw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_extw_encode_fns[] = { + Opcode_extw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_isync_encode_fns[] = { + Opcode_isync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsync_encode_fns[] = { + Opcode_rsync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_esync_encode_fns[] = { + Opcode_esync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dsync_encode_fns[] = { + Opcode_dsync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsil_encode_fns[] = { + Opcode_rsil_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_lend_encode_fns[] = { + Opcode_rsr_lend_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_lend_encode_fns[] = { + Opcode_wsr_lend_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_lend_encode_fns[] = { + Opcode_xsr_lend_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_lcount_encode_fns[] = { + Opcode_rsr_lcount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_lcount_encode_fns[] = { + Opcode_wsr_lcount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_lcount_encode_fns[] = { + Opcode_xsr_lcount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_lbeg_encode_fns[] = { + Opcode_rsr_lbeg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_lbeg_encode_fns[] = { + Opcode_wsr_lbeg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_lbeg_encode_fns[] = { + Opcode_xsr_lbeg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_sar_encode_fns[] = { + Opcode_rsr_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_sar_encode_fns[] = { + Opcode_wsr_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_sar_encode_fns[] = { + Opcode_xsr_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_memctl_encode_fns[] = { + Opcode_rsr_memctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_memctl_encode_fns[] = { + Opcode_wsr_memctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_memctl_encode_fns[] = { + Opcode_xsr_memctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_litbase_encode_fns[] = { + Opcode_rsr_litbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_litbase_encode_fns[] = { + Opcode_wsr_litbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_litbase_encode_fns[] = { + Opcode_xsr_litbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_configid0_encode_fns[] = { + Opcode_rsr_configid0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_configid0_encode_fns[] = { + Opcode_wsr_configid0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_configid1_encode_fns[] = { + Opcode_rsr_configid1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_243_encode_fns[] = { + Opcode_rsr_243_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ps_encode_fns[] = { + Opcode_rsr_ps_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ps_encode_fns[] = { + Opcode_wsr_ps_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ps_encode_fns[] = { + Opcode_xsr_ps_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc1_encode_fns[] = { + Opcode_rsr_epc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc1_encode_fns[] = { + Opcode_wsr_epc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc1_encode_fns[] = { + Opcode_xsr_epc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave1_encode_fns[] = { + Opcode_rsr_excsave1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave1_encode_fns[] = { + Opcode_wsr_excsave1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave1_encode_fns[] = { + Opcode_xsr_excsave1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc2_encode_fns[] = { + Opcode_rsr_epc2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc2_encode_fns[] = { + Opcode_wsr_epc2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc2_encode_fns[] = { + Opcode_xsr_epc2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave2_encode_fns[] = { + Opcode_rsr_excsave2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave2_encode_fns[] = { + Opcode_wsr_excsave2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave2_encode_fns[] = { + Opcode_xsr_excsave2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc3_encode_fns[] = { + Opcode_rsr_epc3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc3_encode_fns[] = { + Opcode_wsr_epc3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc3_encode_fns[] = { + Opcode_xsr_epc3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave3_encode_fns[] = { + Opcode_rsr_excsave3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave3_encode_fns[] = { + Opcode_wsr_excsave3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave3_encode_fns[] = { + Opcode_xsr_excsave3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc4_encode_fns[] = { + Opcode_rsr_epc4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc4_encode_fns[] = { + Opcode_wsr_epc4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc4_encode_fns[] = { + Opcode_xsr_epc4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave4_encode_fns[] = { + Opcode_rsr_excsave4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave4_encode_fns[] = { + Opcode_wsr_excsave4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave4_encode_fns[] = { + Opcode_xsr_excsave4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc5_encode_fns[] = { + Opcode_rsr_epc5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc5_encode_fns[] = { + Opcode_wsr_epc5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc5_encode_fns[] = { + Opcode_xsr_epc5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave5_encode_fns[] = { + Opcode_rsr_excsave5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave5_encode_fns[] = { + Opcode_wsr_excsave5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave5_encode_fns[] = { + Opcode_xsr_excsave5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc6_encode_fns[] = { + Opcode_rsr_epc6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc6_encode_fns[] = { + Opcode_wsr_epc6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc6_encode_fns[] = { + Opcode_xsr_epc6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave6_encode_fns[] = { + Opcode_rsr_excsave6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave6_encode_fns[] = { + Opcode_wsr_excsave6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave6_encode_fns[] = { + Opcode_xsr_excsave6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc7_encode_fns[] = { + Opcode_rsr_epc7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc7_encode_fns[] = { + Opcode_wsr_epc7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc7_encode_fns[] = { + Opcode_xsr_epc7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave7_encode_fns[] = { + Opcode_rsr_excsave7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave7_encode_fns[] = { + Opcode_wsr_excsave7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave7_encode_fns[] = { + Opcode_xsr_excsave7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps2_encode_fns[] = { + Opcode_rsr_eps2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps2_encode_fns[] = { + Opcode_wsr_eps2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps2_encode_fns[] = { + Opcode_xsr_eps2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps3_encode_fns[] = { + Opcode_rsr_eps3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps3_encode_fns[] = { + Opcode_wsr_eps3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps3_encode_fns[] = { + Opcode_xsr_eps3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps4_encode_fns[] = { + Opcode_rsr_eps4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps4_encode_fns[] = { + Opcode_wsr_eps4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps4_encode_fns[] = { + Opcode_xsr_eps4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps5_encode_fns[] = { + Opcode_rsr_eps5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps5_encode_fns[] = { + Opcode_wsr_eps5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps5_encode_fns[] = { + Opcode_xsr_eps5_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps6_encode_fns[] = { + Opcode_rsr_eps6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps6_encode_fns[] = { + Opcode_wsr_eps6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps6_encode_fns[] = { + Opcode_xsr_eps6_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps7_encode_fns[] = { + Opcode_rsr_eps7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps7_encode_fns[] = { + Opcode_wsr_eps7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps7_encode_fns[] = { + Opcode_xsr_eps7_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excvaddr_encode_fns[] = { + Opcode_rsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excvaddr_encode_fns[] = { + Opcode_wsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excvaddr_encode_fns[] = { + Opcode_xsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_depc_encode_fns[] = { + Opcode_rsr_depc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_depc_encode_fns[] = { + Opcode_wsr_depc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_depc_encode_fns[] = { + Opcode_xsr_depc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_exccause_encode_fns[] = { + Opcode_rsr_exccause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_exccause_encode_fns[] = { + Opcode_wsr_exccause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_exccause_encode_fns[] = { + Opcode_xsr_exccause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_misc0_encode_fns[] = { + Opcode_rsr_misc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_misc0_encode_fns[] = { + Opcode_wsr_misc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_misc0_encode_fns[] = { + Opcode_xsr_misc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_misc1_encode_fns[] = { + Opcode_rsr_misc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_misc1_encode_fns[] = { + Opcode_wsr_misc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_misc1_encode_fns[] = { + Opcode_xsr_misc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_prid_encode_fns[] = { + Opcode_rsr_prid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_vecbase_encode_fns[] = { + Opcode_rsr_vecbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_vecbase_encode_fns[] = { + Opcode_wsr_vecbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_vecbase_encode_fns[] = { + Opcode_xsr_vecbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul16u_encode_fns[] = { + Opcode_mul16u_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul16s_encode_fns[] = { + Opcode_mul16s_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mull_encode_fns[] = { + Opcode_mull_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muluh_encode_fns[] = { + Opcode_muluh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mulsh_encode_fns[] = { + Opcode_mulsh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_aa_ll_encode_fns[] = { + Opcode_mul_aa_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_aa_hl_encode_fns[] = { + Opcode_mul_aa_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_aa_lh_encode_fns[] = { + Opcode_mul_aa_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_aa_hh_encode_fns[] = { + Opcode_mul_aa_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_umul_aa_ll_encode_fns[] = { + Opcode_umul_aa_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_umul_aa_hl_encode_fns[] = { + Opcode_umul_aa_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_umul_aa_lh_encode_fns[] = { + Opcode_umul_aa_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_umul_aa_hh_encode_fns[] = { + Opcode_umul_aa_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_ad_ll_encode_fns[] = { + Opcode_mul_ad_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_ad_hl_encode_fns[] = { + Opcode_mul_ad_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_ad_lh_encode_fns[] = { + Opcode_mul_ad_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_ad_hh_encode_fns[] = { + Opcode_mul_ad_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_da_ll_encode_fns[] = { + Opcode_mul_da_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_da_hl_encode_fns[] = { + Opcode_mul_da_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_da_lh_encode_fns[] = { + Opcode_mul_da_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_da_hh_encode_fns[] = { + Opcode_mul_da_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_dd_ll_encode_fns[] = { + Opcode_mul_dd_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_dd_hl_encode_fns[] = { + Opcode_mul_dd_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_dd_lh_encode_fns[] = { + Opcode_mul_dd_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul_dd_hh_encode_fns[] = { + Opcode_mul_dd_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_aa_ll_encode_fns[] = { + Opcode_mula_aa_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_aa_hl_encode_fns[] = { + Opcode_mula_aa_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_aa_lh_encode_fns[] = { + Opcode_mula_aa_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_aa_hh_encode_fns[] = { + Opcode_mula_aa_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_aa_ll_encode_fns[] = { + Opcode_muls_aa_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_aa_hl_encode_fns[] = { + Opcode_muls_aa_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_aa_lh_encode_fns[] = { + Opcode_muls_aa_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_aa_hh_encode_fns[] = { + Opcode_muls_aa_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_ad_ll_encode_fns[] = { + Opcode_mula_ad_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_ad_hl_encode_fns[] = { + Opcode_mula_ad_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_ad_lh_encode_fns[] = { + Opcode_mula_ad_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_ad_hh_encode_fns[] = { + Opcode_mula_ad_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_ad_ll_encode_fns[] = { + Opcode_muls_ad_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_ad_hl_encode_fns[] = { + Opcode_muls_ad_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_ad_lh_encode_fns[] = { + Opcode_muls_ad_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_ad_hh_encode_fns[] = { + Opcode_muls_ad_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_ll_encode_fns[] = { + Opcode_mula_da_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_hl_encode_fns[] = { + Opcode_mula_da_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_lh_encode_fns[] = { + Opcode_mula_da_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_hh_encode_fns[] = { + Opcode_mula_da_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_da_ll_encode_fns[] = { + Opcode_muls_da_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_da_hl_encode_fns[] = { + Opcode_muls_da_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_da_lh_encode_fns[] = { + Opcode_muls_da_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_da_hh_encode_fns[] = { + Opcode_muls_da_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_ll_encode_fns[] = { + Opcode_mula_dd_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_hl_encode_fns[] = { + Opcode_mula_dd_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_lh_encode_fns[] = { + Opcode_mula_dd_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_hh_encode_fns[] = { + Opcode_mula_dd_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_dd_ll_encode_fns[] = { + Opcode_muls_dd_ll_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_dd_hl_encode_fns[] = { + Opcode_muls_dd_hl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_dd_lh_encode_fns[] = { + Opcode_muls_dd_lh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_muls_dd_hh_encode_fns[] = { + Opcode_muls_dd_hh_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_ll_lddec_encode_fns[] = { + Opcode_mula_da_ll_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_ll_ldinc_encode_fns[] = { + Opcode_mula_da_ll_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_hl_lddec_encode_fns[] = { + Opcode_mula_da_hl_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_hl_ldinc_encode_fns[] = { + Opcode_mula_da_hl_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_lh_lddec_encode_fns[] = { + Opcode_mula_da_lh_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_lh_ldinc_encode_fns[] = { + Opcode_mula_da_lh_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_hh_lddec_encode_fns[] = { + Opcode_mula_da_hh_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_da_hh_ldinc_encode_fns[] = { + Opcode_mula_da_hh_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_ll_lddec_encode_fns[] = { + Opcode_mula_dd_ll_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_ll_ldinc_encode_fns[] = { + Opcode_mula_dd_ll_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_hl_lddec_encode_fns[] = { + Opcode_mula_dd_hl_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_hl_ldinc_encode_fns[] = { + Opcode_mula_dd_hl_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_lh_lddec_encode_fns[] = { + Opcode_mula_dd_lh_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_lh_ldinc_encode_fns[] = { + Opcode_mula_dd_lh_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_hh_lddec_encode_fns[] = { + Opcode_mula_dd_hh_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mula_dd_hh_ldinc_encode_fns[] = { + Opcode_mula_dd_hh_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_lddec_encode_fns[] = { + Opcode_lddec_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ldinc_encode_fns[] = { + Opcode_ldinc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_m0_encode_fns[] = { + Opcode_rsr_m0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_m0_encode_fns[] = { + Opcode_wsr_m0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_m0_encode_fns[] = { + Opcode_xsr_m0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_m1_encode_fns[] = { + Opcode_rsr_m1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_m1_encode_fns[] = { + Opcode_wsr_m1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_m1_encode_fns[] = { + Opcode_xsr_m1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_m2_encode_fns[] = { + Opcode_rsr_m2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_m2_encode_fns[] = { + Opcode_wsr_m2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_m2_encode_fns[] = { + Opcode_xsr_m2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_m3_encode_fns[] = { + Opcode_rsr_m3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_m3_encode_fns[] = { + Opcode_wsr_m3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_m3_encode_fns[] = { + Opcode_xsr_m3_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_acclo_encode_fns[] = { + Opcode_rsr_acclo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_acclo_encode_fns[] = { + Opcode_wsr_acclo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_acclo_encode_fns[] = { + Opcode_xsr_acclo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_acchi_encode_fns[] = { + Opcode_rsr_acchi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_acchi_encode_fns[] = { + Opcode_wsr_acchi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_acchi_encode_fns[] = { + Opcode_xsr_acchi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfi_encode_fns[] = { + Opcode_rfi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_waiti_encode_fns[] = { + Opcode_waiti_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_interrupt_encode_fns[] = { + Opcode_rsr_interrupt_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_intset_encode_fns[] = { + Opcode_wsr_intset_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_intclear_encode_fns[] = { + Opcode_wsr_intclear_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_intenable_encode_fns[] = { + Opcode_rsr_intenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_intenable_encode_fns[] = { + Opcode_wsr_intenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_intenable_encode_fns[] = { + Opcode_xsr_intenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_break_encode_fns[] = { + Opcode_break_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_break_n_encode_fns[] = { + 0, 0, Opcode_break_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_dbreaka0_encode_fns[] = { + Opcode_rsr_dbreaka0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_dbreaka0_encode_fns[] = { + Opcode_wsr_dbreaka0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_dbreaka0_encode_fns[] = { + Opcode_xsr_dbreaka0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_dbreakc0_encode_fns[] = { + Opcode_rsr_dbreakc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_dbreakc0_encode_fns[] = { + Opcode_wsr_dbreakc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_dbreakc0_encode_fns[] = { + Opcode_xsr_dbreakc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_dbreaka1_encode_fns[] = { + Opcode_rsr_dbreaka1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_dbreaka1_encode_fns[] = { + Opcode_wsr_dbreaka1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_dbreaka1_encode_fns[] = { + Opcode_xsr_dbreaka1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_dbreakc1_encode_fns[] = { + Opcode_rsr_dbreakc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_dbreakc1_encode_fns[] = { + Opcode_wsr_dbreakc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_dbreakc1_encode_fns[] = { + Opcode_xsr_dbreakc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ibreaka0_encode_fns[] = { + Opcode_rsr_ibreaka0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ibreaka0_encode_fns[] = { + Opcode_wsr_ibreaka0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ibreaka0_encode_fns[] = { + Opcode_xsr_ibreaka0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ibreaka1_encode_fns[] = { + Opcode_rsr_ibreaka1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ibreaka1_encode_fns[] = { + Opcode_wsr_ibreaka1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ibreaka1_encode_fns[] = { + Opcode_xsr_ibreaka1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ibreakenable_encode_fns[] = { + Opcode_rsr_ibreakenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ibreakenable_encode_fns[] = { + Opcode_wsr_ibreakenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ibreakenable_encode_fns[] = { + Opcode_xsr_ibreakenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_debugcause_encode_fns[] = { + Opcode_rsr_debugcause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_debugcause_encode_fns[] = { + Opcode_wsr_debugcause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_debugcause_encode_fns[] = { + Opcode_xsr_debugcause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_icount_encode_fns[] = { + Opcode_rsr_icount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_icount_encode_fns[] = { + Opcode_wsr_icount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_icount_encode_fns[] = { + Opcode_xsr_icount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_icountlevel_encode_fns[] = { + Opcode_rsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_icountlevel_encode_fns[] = { + Opcode_wsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_icountlevel_encode_fns[] = { + Opcode_xsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ddr_encode_fns[] = { + Opcode_rsr_ddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ddr_encode_fns[] = { + Opcode_wsr_ddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ddr_encode_fns[] = { + Opcode_xsr_ddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_lddr32_p_encode_fns[] = { + Opcode_lddr32_p_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sddr32_p_encode_fns[] = { + Opcode_sddr32_p_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfdo_encode_fns[] = { + Opcode_rfdo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfdd_encode_fns[] = { + Opcode_rfdd_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_mmid_encode_fns[] = { + Opcode_wsr_mmid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_andb_encode_fns[] = { + Opcode_andb_Slot_inst_encode, 0, 0, 0, Opcode_andb_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_andbc_encode_fns[] = { + Opcode_andbc_Slot_inst_encode, 0, 0, 0, Opcode_andbc_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_orb_encode_fns[] = { + Opcode_orb_Slot_inst_encode, 0, 0, 0, Opcode_orb_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_orbc_encode_fns[] = { + Opcode_orbc_Slot_inst_encode, 0, 0, 0, Opcode_orbc_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_xorb_encode_fns[] = { + Opcode_xorb_Slot_inst_encode, 0, 0, 0, Opcode_xorb_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_any4_encode_fns[] = { + Opcode_any4_Slot_inst_encode, 0, 0, 0, Opcode_any4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_all4_encode_fns[] = { + Opcode_all4_Slot_inst_encode, 0, 0, 0, Opcode_all4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_any8_encode_fns[] = { + Opcode_any8_Slot_inst_encode, 0, 0, 0, Opcode_any8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_all8_encode_fns[] = { + Opcode_all8_Slot_inst_encode, 0, 0, 0, Opcode_all8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bf_encode_fns[] = { + Opcode_bf_Slot_inst_encode, 0, 0, 0, Opcode_bf_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bt_encode_fns[] = { + Opcode_bt_Slot_inst_encode, 0, 0, 0, Opcode_bt_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movf_encode_fns[] = { + Opcode_movf_Slot_inst_encode, 0, 0, 0, Opcode_movf_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movt_encode_fns[] = { + Opcode_movt_Slot_inst_encode, 0, 0, 0, Opcode_movt_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_rsr_br_encode_fns[] = { + Opcode_rsr_br_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_br_encode_fns[] = { + Opcode_wsr_br_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_br_encode_fns[] = { + Opcode_xsr_br_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccount_encode_fns[] = { + Opcode_rsr_ccount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccount_encode_fns[] = { + Opcode_wsr_ccount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccount_encode_fns[] = { + Opcode_xsr_ccount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccompare0_encode_fns[] = { + Opcode_rsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccompare0_encode_fns[] = { + Opcode_wsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccompare0_encode_fns[] = { + Opcode_xsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccompare1_encode_fns[] = { + Opcode_rsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccompare1_encode_fns[] = { + Opcode_wsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccompare1_encode_fns[] = { + Opcode_xsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccompare2_encode_fns[] = { + Opcode_rsr_ccompare2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccompare2_encode_fns[] = { + Opcode_wsr_ccompare2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccompare2_encode_fns[] = { + Opcode_xsr_ccompare2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ipf_encode_fns[] = { + Opcode_ipf_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ihi_encode_fns[] = { + Opcode_ihi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ipfl_encode_fns[] = { + Opcode_ipfl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ihu_encode_fns[] = { + Opcode_ihu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_iiu_encode_fns[] = { + Opcode_iiu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_iii_encode_fns[] = { + Opcode_iii_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_lict_encode_fns[] = { + Opcode_lict_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_licw_encode_fns[] = { + Opcode_licw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sict_encode_fns[] = { + Opcode_sict_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sicw_encode_fns[] = { + Opcode_sicw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhwb_encode_fns[] = { + Opcode_dhwb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhwbi_encode_fns[] = { + Opcode_dhwbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_diwbui_p_encode_fns[] = { + Opcode_diwbui_p_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_diwb_encode_fns[] = { + Opcode_diwb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_diwbi_encode_fns[] = { + Opcode_diwbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhi_encode_fns[] = { + Opcode_dhi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dii_encode_fns[] = { + Opcode_dii_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfr_encode_fns[] = { + Opcode_dpfr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfw_encode_fns[] = { + Opcode_dpfw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfro_encode_fns[] = { + Opcode_dpfro_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfwo_encode_fns[] = { + Opcode_dpfwo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfl_encode_fns[] = { + Opcode_dpfl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhu_encode_fns[] = { + Opcode_dhu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_diu_encode_fns[] = { + Opcode_diu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sdct_encode_fns[] = { + Opcode_sdct_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ldct_encode_fns[] = { + Opcode_ldct_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_prefctl_encode_fns[] = { + Opcode_rsr_prefctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_prefctl_encode_fns[] = { + Opcode_wsr_prefctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_prefctl_encode_fns[] = { + Opcode_xsr_prefctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ptevaddr_encode_fns[] = { + Opcode_wsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ptevaddr_encode_fns[] = { + Opcode_rsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ptevaddr_encode_fns[] = { + Opcode_xsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_rasid_encode_fns[] = { + Opcode_rsr_rasid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_rasid_encode_fns[] = { + Opcode_wsr_rasid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_rasid_encode_fns[] = { + Opcode_xsr_rasid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_itlbcfg_encode_fns[] = { + Opcode_rsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_itlbcfg_encode_fns[] = { + Opcode_wsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_itlbcfg_encode_fns[] = { + Opcode_xsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_dtlbcfg_encode_fns[] = { + Opcode_rsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_dtlbcfg_encode_fns[] = { + Opcode_wsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_dtlbcfg_encode_fns[] = { + Opcode_xsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_idtlb_encode_fns[] = { + Opcode_idtlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_pdtlb_encode_fns[] = { + Opcode_pdtlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rdtlb0_encode_fns[] = { + Opcode_rdtlb0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rdtlb1_encode_fns[] = { + Opcode_rdtlb1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wdtlb_encode_fns[] = { + Opcode_wdtlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_iitlb_encode_fns[] = { + Opcode_iitlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_pitlb_encode_fns[] = { + Opcode_pitlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ritlb0_encode_fns[] = { + Opcode_ritlb0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ritlb1_encode_fns[] = { + Opcode_ritlb1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_witlb_encode_fns[] = { + Opcode_witlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ldpte_encode_fns[] = { + Opcode_ldpte_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_hwwitlba_encode_fns[] = { + Opcode_hwwitlba_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_hwwdtlba_encode_fns[] = { + Opcode_hwwdtlba_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_cpenable_encode_fns[] = { + Opcode_rsr_cpenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_cpenable_encode_fns[] = { + Opcode_wsr_cpenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_cpenable_encode_fns[] = { + Opcode_xsr_cpenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_clamps_encode_fns[] = { + Opcode_clamps_Slot_inst_encode, 0, 0, 0, Opcode_clamps_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_min_encode_fns[] = { + Opcode_min_Slot_inst_encode, 0, 0, 0, Opcode_min_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_max_encode_fns[] = { + Opcode_max_Slot_inst_encode, 0, 0, 0, Opcode_max_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_minu_encode_fns[] = { + Opcode_minu_Slot_inst_encode, 0, 0, 0, Opcode_minu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_maxu_encode_fns[] = { + Opcode_maxu_Slot_inst_encode, 0, 0, 0, Opcode_maxu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_nsa_encode_fns[] = { + Opcode_nsa_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_nsau_encode_fns[] = { + Opcode_nsau_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sext_encode_fns[] = { + Opcode_sext_Slot_inst_encode, 0, 0, 0, Opcode_sext_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l32ai_encode_fns[] = { + Opcode_l32ai_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32ri_encode_fns[] = { + Opcode_s32ri_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32c1i_encode_fns[] = { + Opcode_s32c1i_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_scompare1_encode_fns[] = { + Opcode_rsr_scompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_scompare1_encode_fns[] = { + Opcode_wsr_scompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_scompare1_encode_fns[] = { + Opcode_xsr_scompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_atomctl_encode_fns[] = { + Opcode_rsr_atomctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_atomctl_encode_fns[] = { + Opcode_wsr_atomctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_atomctl_encode_fns[] = { + Opcode_xsr_atomctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_quou_encode_fns[] = { + Opcode_quou_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_quos_encode_fns[] = { + Opcode_quos_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_remu_encode_fns[] = { + Opcode_remu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rems_encode_fns[] = { + Opcode_rems_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rer_encode_fns[] = { + Opcode_rer_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wer_encode_fns[] = { + Opcode_wer_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_ovf_sar_encode_fns[] = { + Opcode_rur_ae_ovf_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_ovf_sar_encode_fns[] = { + Opcode_wur_ae_ovf_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_bithead_encode_fns[] = { + Opcode_rur_ae_bithead_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_bithead_encode_fns[] = { + Opcode_wur_ae_bithead_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_ts_fts_bu_bp_encode_fns[] = { + Opcode_rur_ae_ts_fts_bu_bp_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_ts_fts_bu_bp_encode_fns[] = { + Opcode_wur_ae_ts_fts_bu_bp_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_sd_no_encode_fns[] = { + Opcode_rur_ae_sd_no_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_sd_no_encode_fns[] = { + Opcode_wur_ae_sd_no_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_overflow_encode_fns[] = { + Opcode_rur_ae_overflow_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_overflow_encode_fns[] = { + Opcode_wur_ae_overflow_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_sar_encode_fns[] = { + Opcode_rur_ae_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_sar_encode_fns[] = { + Opcode_wur_ae_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_bitptr_encode_fns[] = { + Opcode_rur_ae_bitptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_bitptr_encode_fns[] = { + Opcode_wur_ae_bitptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_bitsused_encode_fns[] = { + Opcode_rur_ae_bitsused_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_bitsused_encode_fns[] = { + Opcode_wur_ae_bitsused_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_tablesize_encode_fns[] = { + Opcode_rur_ae_tablesize_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_tablesize_encode_fns[] = { + Opcode_wur_ae_tablesize_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_first_ts_encode_fns[] = { + Opcode_rur_ae_first_ts_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_first_ts_encode_fns[] = { + Opcode_wur_ae_first_ts_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_nextoffset_encode_fns[] = { + Opcode_rur_ae_nextoffset_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_nextoffset_encode_fns[] = { + Opcode_wur_ae_nextoffset_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_searchdone_encode_fns[] = { + Opcode_rur_ae_searchdone_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_searchdone_encode_fns[] = { + Opcode_wur_ae_searchdone_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_i_encode_fns[] = { + Opcode_ae_lp16f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_iu_encode_fns[] = { + Opcode_ae_lp16f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_x_encode_fns[] = { + Opcode_ae_lp16f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_xu_encode_fns[] = { + Opcode_ae_lp16f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_i_encode_fns[] = { + Opcode_ae_lp24_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_iu_encode_fns[] = { + Opcode_ae_lp24_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_x_encode_fns[] = { + Opcode_ae_lp24_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_xu_encode_fns[] = { + Opcode_ae_lp24_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_i_encode_fns[] = { + Opcode_ae_lp24f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_iu_encode_fns[] = { + Opcode_ae_lp24f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_x_encode_fns[] = { + Opcode_ae_lp24f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_xu_encode_fns[] = { + Opcode_ae_lp24f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_i_encode_fns[] = { + Opcode_ae_lp16x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_iu_encode_fns[] = { + Opcode_ae_lp16x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_x_encode_fns[] = { + Opcode_ae_lp16x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_xu_encode_fns[] = { + Opcode_ae_lp16x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_i_encode_fns[] = { + Opcode_ae_lp24x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_iu_encode_fns[] = { + Opcode_ae_lp24x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_x_encode_fns[] = { + Opcode_ae_lp24x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_xu_encode_fns[] = { + Opcode_ae_lp24x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_i_encode_fns[] = { + Opcode_ae_lp24x2_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_iu_encode_fns[] = { + Opcode_ae_lp24x2_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_x_encode_fns[] = { + Opcode_ae_lp24x2_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_xu_encode_fns[] = { + Opcode_ae_lp24x2_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_i_encode_fns[] = { + Opcode_ae_sp16x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_iu_encode_fns[] = { + Opcode_ae_sp16x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_x_encode_fns[] = { + Opcode_ae_sp16x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_xu_encode_fns[] = { + Opcode_ae_sp16x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_i_encode_fns[] = { + Opcode_ae_sp24x2s_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_iu_encode_fns[] = { + Opcode_ae_sp24x2s_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_x_encode_fns[] = { + Opcode_ae_sp24x2s_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_xu_encode_fns[] = { + Opcode_ae_sp24x2s_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_i_encode_fns[] = { + Opcode_ae_sp24x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_iu_encode_fns[] = { + Opcode_ae_sp24x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_x_encode_fns[] = { + Opcode_ae_sp24x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_xu_encode_fns[] = { + Opcode_ae_sp24x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_i_encode_fns[] = { + Opcode_ae_sp16f_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_iu_encode_fns[] = { + Opcode_ae_sp16f_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_x_encode_fns[] = { + Opcode_ae_sp16f_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_xu_encode_fns[] = { + Opcode_ae_sp16f_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_i_encode_fns[] = { + Opcode_ae_sp24s_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_iu_encode_fns[] = { + Opcode_ae_sp24s_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_x_encode_fns[] = { + Opcode_ae_sp24s_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_xu_encode_fns[] = { + Opcode_ae_sp24s_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_i_encode_fns[] = { + Opcode_ae_sp24f_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_iu_encode_fns[] = { + Opcode_ae_sp24f_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_x_encode_fns[] = { + Opcode_ae_sp24f_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_xu_encode_fns[] = { + Opcode_ae_sp24f_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_i_encode_fns[] = { + Opcode_ae_lq56_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_iu_encode_fns[] = { + Opcode_ae_lq56_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_x_encode_fns[] = { + Opcode_ae_lq56_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_xu_encode_fns[] = { + Opcode_ae_lq56_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_i_encode_fns[] = { + Opcode_ae_lq32f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_iu_encode_fns[] = { + Opcode_ae_lq32f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_x_encode_fns[] = { + Opcode_ae_lq32f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_xu_encode_fns[] = { + Opcode_ae_lq32f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_i_encode_fns[] = { + Opcode_ae_sq56s_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_iu_encode_fns[] = { + Opcode_ae_sq56s_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_x_encode_fns[] = { + Opcode_ae_sq56s_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_xu_encode_fns[] = { + Opcode_ae_sq56s_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_i_encode_fns[] = { + Opcode_ae_sq32f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_iu_encode_fns[] = { + Opcode_ae_sq32f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_x_encode_fns[] = { + Opcode_ae_sq32f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_xu_encode_fns[] = { + Opcode_ae_sq32f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_zerop48_encode_fns[] = { + 0, 0, 0, Opcode_ae_zerop48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movp48_encode_fns[] = { + Opcode_ae_movp48_Slot_inst_encode, 0, 0, Opcode_ae_movp48_Slot_ae_slot1_encode, Opcode_ae_movp48_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_ll_Slot_ae_slot1_encode, Opcode_ae_selp24_ll_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_lh_Slot_ae_slot1_encode, Opcode_ae_selp24_lh_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_hl_Slot_ae_slot1_encode, Opcode_ae_selp24_hl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_hh_Slot_ae_slot1_encode, Opcode_ae_selp24_hh_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movtp24x2_encode_fns[] = { + 0, 0, 0, Opcode_ae_movtp24x2_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movfp24x2_encode_fns[] = { + 0, 0, 0, Opcode_ae_movfp24x2_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movtp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_movtp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movfp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_movfp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movpa24x2_encode_fns[] = { + Opcode_ae_movpa24x2_Slot_inst_encode, 0, 0, 0, Opcode_ae_movpa24x2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncp24a32x2_encode_fns[] = { + Opcode_ae_truncp24a32x2_Slot_inst_encode, 0, 0, 0, Opcode_ae_truncp24a32x2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvta32p24_l_encode_fns[] = { + Opcode_ae_cvta32p24_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvta32p24_l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvta32p24_h_encode_fns[] = { + Opcode_ae_cvta32p24_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvta32p24_h_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_ll_encode_fns[] = { + Opcode_ae_cvtp24a16x2_ll_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_ll_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_lh_encode_fns[] = { + Opcode_ae_cvtp24a16x2_lh_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_lh_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hl_encode_fns[] = { + Opcode_ae_cvtp24a16x2_hl_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_hl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hh_encode_fns[] = { + Opcode_ae_cvtp24a16x2_hh_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_hh_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncp24q48x2_encode_fns[] = { + 0, 0, 0, Opcode_ae_truncp24q48x2_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncp16_encode_fns[] = { + 0, 0, 0, Opcode_ae_truncp16_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp24q48sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp24q48sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp24q48asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp24q48asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16q48sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16q48sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16q48asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16q48asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_zeroq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_zeroq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movq56_encode_fns[] = { + Opcode_ae_movq56_Slot_inst_encode, 0, 0, Opcode_ae_movq56_Slot_ae_slot1_encode, Opcode_ae_movq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movtq56_encode_fns[] = { + Opcode_ae_movtq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_movtq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movfq56_encode_fns[] = { + Opcode_ae_movfq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_movfq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtq48a32s_encode_fns[] = { + Opcode_ae_cvtq48a32s_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtq48a32s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_cvtq48p24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_cvtq48p24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_satq48s_encode_fns[] = { + 0, 0, 0, Opcode_ae_satq48s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncq32_encode_fns[] = { + 0, 0, 0, Opcode_ae_truncq32_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsq32sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsq32sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsq32asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsq32asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_trunca32q48_encode_fns[] = { + Opcode_ae_trunca32q48_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca32q48_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movap24s_l_encode_fns[] = { + Opcode_ae_movap24s_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_movap24s_l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movap24s_h_encode_fns[] = { + Opcode_ae_movap24s_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_movap24s_h_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_l_encode_fns[] = { + Opcode_ae_trunca16p24s_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca16p24s_l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_h_encode_fns[] = { + Opcode_ae_trunca16p24s_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca16p24s_h_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_addp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_addp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_subp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_negp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_absp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_absp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxbp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxbp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minbp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minbp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_addsp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_addsp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subsp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_subsp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negsp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_negsp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_abssp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_abssp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_andp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_andp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_nandp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_nandp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_orp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_orp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_xorp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_xorp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_ltp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_ltp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lep24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_lep24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_eqp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_eqp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_addq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_addq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_subq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_negq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_absq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_absq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxbq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxbq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minbq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minbq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_addsq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_addsq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subsq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_subsq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negsq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_negsq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_abssq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_abssq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_andq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_andq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_nandq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_nandq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_orq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_orq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_xorq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_xorq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllip24_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllip24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlip24_encode_fns[] = { + 0, 0, 0, Opcode_ae_srlip24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sraip24_encode_fns[] = { + 0, 0, 0, Opcode_ae_sraip24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllsp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllsp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlsp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_srlsp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_srasp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_srasp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllisp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllisp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllssp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllssp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_slliq56_encode_fns[] = { + Opcode_ae_slliq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_slliq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srliq56_encode_fns[] = { + Opcode_ae_srliq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srliq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sraiq56_encode_fns[] = { + Opcode_ae_sraiq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sraiq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllsq56_encode_fns[] = { + Opcode_ae_sllsq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllsq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlsq56_encode_fns[] = { + Opcode_ae_srlsq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srlsq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srasq56_encode_fns[] = { + Opcode_ae_srasq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srasq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllaq56_encode_fns[] = { + Opcode_ae_sllaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllaq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlaq56_encode_fns[] = { + Opcode_ae_srlaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srlaq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sraaq56_encode_fns[] = { + Opcode_ae_sraaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sraaq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllisq56s_encode_fns[] = { + Opcode_ae_sllisq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllisq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllssq56s_encode_fns[] = { + Opcode_ae_sllssq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllssq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllasq56s_encode_fns[] = { + Opcode_ae_sllasq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllasq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_ltq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_ltq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_leq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_leq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_eqq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_eqq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_nsaq56s_encode_fns[] = { + Opcode_ae_nsaq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_nsaq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsrfq32sp24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsrfq32sp24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsrfq32sp24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsrfq32sp24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mularfq32sp24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mularfq32sp24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mularfq32sp24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mularfq32sp24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulrfq32sp24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulrfq32sp24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulrfq32sp24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulrfq32sp24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sha32_encode_fns[] = { + Opcode_ae_sha32_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldl32t_encode_fns[] = { + Opcode_ae_vldl32t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldl16t_encode_fns[] = { + Opcode_ae_vldl16t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldl16c_encode_fns[] = { + Opcode_ae_vldl16c_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldsht_encode_fns[] = { + Opcode_ae_vldsht_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lb_encode_fns[] = { + Opcode_ae_lb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lbi_encode_fns[] = { + Opcode_ae_lbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lbk_encode_fns[] = { + Opcode_ae_lbk_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lbki_encode_fns[] = { + Opcode_ae_lbki_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_db_encode_fns[] = { + Opcode_ae_db_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_dbi_encode_fns[] = { + Opcode_ae_dbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vlel32t_encode_fns[] = { + Opcode_ae_vlel32t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vlel16t_encode_fns[] = { + Opcode_ae_vlel16t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sb_encode_fns[] = { + Opcode_ae_sb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sbi_encode_fns[] = { + Opcode_ae_sbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vles16c_encode_fns[] = { + Opcode_ae_vles16c_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sbf_encode_fns[] = { + Opcode_ae_sbf_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_slaasq56s_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_slaasq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_addbrba32_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_addbrba32_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_minabssp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minabssp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxabssp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxabssp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minabssq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minabssq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxabssq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxabssq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_cbegin0_encode_fns[] = { + Opcode_rur_ae_cbegin0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_cbegin0_encode_fns[] = { + Opcode_wur_ae_cbegin0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_cend0_encode_fns[] = { + Opcode_rur_ae_cend0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_cend0_encode_fns[] = { + Opcode_wur_ae_cend0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lp24x2_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sp24x2s_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lp24x2f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sp24x2f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lp16x2f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sp16x2f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lp24_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sp24s_l_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lp24f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sp24f_l_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lp16f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sp16f_l_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lq56_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sq56s_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_lq32f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_c_encode_fns[] = { + 0, 0, 0, 0, Opcode_ae_sq32f_c_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_rur_expstate_encode_fns[] = { + Opcode_rur_expstate_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_expstate_encode_fns[] = { + Opcode_wur_expstate_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_read_impwire_encode_fns[] = { + Opcode_read_impwire_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_setb_expstate_encode_fns[] = { + Opcode_setb_expstate_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_clrb_expstate_encode_fns[] = { + Opcode_clrb_expstate_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wrmsk_expstate_encode_fns[] = { + Opcode_wrmsk_expstate_Slot_inst_encode, 0, 0, 0, 0 +}; + + + + + +/* Opcode table. */ + +static xtensa_funcUnit_use Opcode_ae_vldl32t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vldl16t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vldl16c_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_shift32x5, 3 }, + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vldsht_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_shift32x5, 3 }, + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_lb_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_lbi_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_lbk_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_lbki_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_db_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_dbi_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_vlel32t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vlel16t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_sb_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_sbi_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_vles16c_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_sbf_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_opcode_internal opcodes[] = { + { "excw", ICLASS_xt_iclass_excw, + 0, + Opcode_excw_encode_fns, 0, 0 }, + { "rfe", ICLASS_xt_iclass_rfe, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfe_encode_fns, 0, 0 }, + { "rfde", ICLASS_xt_iclass_rfde, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfde_encode_fns, 0, 0 }, + { "syscall", ICLASS_xt_iclass_syscall, + 0, + Opcode_syscall_encode_fns, 0, 0 }, + { "call12", ICLASS_xt_iclass_call12, + XTENSA_OPCODE_IS_CALL, + Opcode_call12_encode_fns, 0, 0 }, + { "call8", ICLASS_xt_iclass_call8, + XTENSA_OPCODE_IS_CALL, + Opcode_call8_encode_fns, 0, 0 }, + { "call4", ICLASS_xt_iclass_call4, + XTENSA_OPCODE_IS_CALL, + Opcode_call4_encode_fns, 0, 0 }, + { "callx12", ICLASS_xt_iclass_callx12, + XTENSA_OPCODE_IS_CALL, + Opcode_callx12_encode_fns, 0, 0 }, + { "callx8", ICLASS_xt_iclass_callx8, + XTENSA_OPCODE_IS_CALL, + Opcode_callx8_encode_fns, 0, 0 }, + { "callx4", ICLASS_xt_iclass_callx4, + XTENSA_OPCODE_IS_CALL, + Opcode_callx4_encode_fns, 0, 0 }, + { "entry", ICLASS_xt_iclass_entry, + 0, + Opcode_entry_encode_fns, 0, 0 }, + { "movsp", ICLASS_xt_iclass_movsp, + 0, + Opcode_movsp_encode_fns, 0, 0 }, + { "rotw", ICLASS_xt_iclass_rotw, + 0, + Opcode_rotw_encode_fns, 0, 0 }, + { "retw", ICLASS_xt_iclass_retw, + XTENSA_OPCODE_IS_JUMP, + Opcode_retw_encode_fns, 0, 0 }, + { "retw.n", ICLASS_xt_iclass_retw, + XTENSA_OPCODE_IS_JUMP, + Opcode_retw_n_encode_fns, 0, 0 }, + { "rfwo", ICLASS_xt_iclass_rfwou, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfwo_encode_fns, 0, 0 }, + { "rfwu", ICLASS_xt_iclass_rfwou, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfwu_encode_fns, 0, 0 }, + { "l32e", ICLASS_xt_iclass_l32e, + 0, + Opcode_l32e_encode_fns, 0, 0 }, + { "s32e", ICLASS_xt_iclass_s32e, + 0, + Opcode_s32e_encode_fns, 0, 0 }, + { "rsr.windowbase", ICLASS_xt_iclass_rsr_windowbase, + 0, + Opcode_rsr_windowbase_encode_fns, 0, 0 }, + { "wsr.windowbase", ICLASS_xt_iclass_wsr_windowbase, + 0, + Opcode_wsr_windowbase_encode_fns, 0, 0 }, + { "xsr.windowbase", ICLASS_xt_iclass_xsr_windowbase, + 0, + Opcode_xsr_windowbase_encode_fns, 0, 0 }, + { "rsr.windowstart", ICLASS_xt_iclass_rsr_windowstart, + 0, + Opcode_rsr_windowstart_encode_fns, 0, 0 }, + { "wsr.windowstart", ICLASS_xt_iclass_wsr_windowstart, + 0, + Opcode_wsr_windowstart_encode_fns, 0, 0 }, + { "xsr.windowstart", ICLASS_xt_iclass_xsr_windowstart, + 0, + Opcode_xsr_windowstart_encode_fns, 0, 0 }, + { "add.n", ICLASS_xt_iclass_add_n, + 0, + Opcode_add_n_encode_fns, 0, 0 }, + { "addi.n", ICLASS_xt_iclass_addi_n, + 0, + Opcode_addi_n_encode_fns, 0, 0 }, + { "beqz.n", ICLASS_xt_iclass_bz6, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beqz_n_encode_fns, 0, 0 }, + { "bnez.n", ICLASS_xt_iclass_bz6, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnez_n_encode_fns, 0, 0 }, + { "ill.n", ICLASS_xt_iclass_ill_n, + 0, + Opcode_ill_n_encode_fns, 0, 0 }, + { "l32i.n", ICLASS_xt_iclass_loadi4, + 0, + Opcode_l32i_n_encode_fns, 0, 0 }, + { "mov.n", ICLASS_xt_iclass_mov_n, + 0, + Opcode_mov_n_encode_fns, 0, 0 }, + { "movi.n", ICLASS_xt_iclass_movi_n, + 0, + Opcode_movi_n_encode_fns, 0, 0 }, + { "nop.n", ICLASS_xt_iclass_nopn, + 0, + Opcode_nop_n_encode_fns, 0, 0 }, + { "ret.n", ICLASS_xt_iclass_retn, + XTENSA_OPCODE_IS_JUMP, + Opcode_ret_n_encode_fns, 0, 0 }, + { "s32i.n", ICLASS_xt_iclass_storei4, + 0, + Opcode_s32i_n_encode_fns, 0, 0 }, + { "rur.threadptr", ICLASS_rur_threadptr, + 0, + Opcode_rur_threadptr_encode_fns, 0, 0 }, + { "wur.threadptr", ICLASS_wur_threadptr, + 0, + Opcode_wur_threadptr_encode_fns, 0, 0 }, + { "addi", ICLASS_xt_iclass_addi, + 0, + Opcode_addi_encode_fns, 0, 0 }, + { "addmi", ICLASS_xt_iclass_addmi, + 0, + Opcode_addmi_encode_fns, 0, 0 }, + { "add", ICLASS_xt_iclass_addsub, + 0, + Opcode_add_encode_fns, 0, 0 }, + { "sub", ICLASS_xt_iclass_addsub, + 0, + Opcode_sub_encode_fns, 0, 0 }, + { "addx2", ICLASS_xt_iclass_addsub, + 0, + Opcode_addx2_encode_fns, 0, 0 }, + { "addx4", ICLASS_xt_iclass_addsub, + 0, + Opcode_addx4_encode_fns, 0, 0 }, + { "addx8", ICLASS_xt_iclass_addsub, + 0, + Opcode_addx8_encode_fns, 0, 0 }, + { "subx2", ICLASS_xt_iclass_addsub, + 0, + Opcode_subx2_encode_fns, 0, 0 }, + { "subx4", ICLASS_xt_iclass_addsub, + 0, + Opcode_subx4_encode_fns, 0, 0 }, + { "subx8", ICLASS_xt_iclass_addsub, + 0, + Opcode_subx8_encode_fns, 0, 0 }, + { "and", ICLASS_xt_iclass_bit, + 0, + Opcode_and_encode_fns, 0, 0 }, + { "or", ICLASS_xt_iclass_bit, + 0, + Opcode_or_encode_fns, 0, 0 }, + { "xor", ICLASS_xt_iclass_bit, + 0, + Opcode_xor_encode_fns, 0, 0 }, + { "beqi", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beqi_encode_fns, 0, 0 }, + { "bnei", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnei_encode_fns, 0, 0 }, + { "bgei", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgei_encode_fns, 0, 0 }, + { "blti", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_blti_encode_fns, 0, 0 }, + { "bbci", ICLASS_xt_iclass_bsi8b, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbci_encode_fns, 0, 0 }, + { "bbsi", ICLASS_xt_iclass_bsi8b, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbsi_encode_fns, 0, 0 }, + { "bgeui", ICLASS_xt_iclass_bsi8u, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgeui_encode_fns, 0, 0 }, + { "bltui", ICLASS_xt_iclass_bsi8u, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bltui_encode_fns, 0, 0 }, + { "beq", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beq_encode_fns, 0, 0 }, + { "bne", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bne_encode_fns, 0, 0 }, + { "bge", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bge_encode_fns, 0, 0 }, + { "blt", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_blt_encode_fns, 0, 0 }, + { "bgeu", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgeu_encode_fns, 0, 0 }, + { "bltu", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bltu_encode_fns, 0, 0 }, + { "bany", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bany_encode_fns, 0, 0 }, + { "bnone", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnone_encode_fns, 0, 0 }, + { "ball", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_ball_encode_fns, 0, 0 }, + { "bnall", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnall_encode_fns, 0, 0 }, + { "bbc", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbc_encode_fns, 0, 0 }, + { "bbs", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbs_encode_fns, 0, 0 }, + { "beqz", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beqz_encode_fns, 0, 0 }, + { "bnez", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnez_encode_fns, 0, 0 }, + { "bgez", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgez_encode_fns, 0, 0 }, + { "bltz", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bltz_encode_fns, 0, 0 }, + { "call0", ICLASS_xt_iclass_call0, + XTENSA_OPCODE_IS_CALL, + Opcode_call0_encode_fns, 0, 0 }, + { "callx0", ICLASS_xt_iclass_callx0, + XTENSA_OPCODE_IS_CALL, + Opcode_callx0_encode_fns, 0, 0 }, + { "extui", ICLASS_xt_iclass_exti, + 0, + Opcode_extui_encode_fns, 0, 0 }, + { "ill", ICLASS_xt_iclass_ill, + 0, + Opcode_ill_encode_fns, 0, 0 }, + { "j", ICLASS_xt_iclass_jump, + XTENSA_OPCODE_IS_JUMP, + Opcode_j_encode_fns, 0, 0 }, + { "jx", ICLASS_xt_iclass_jumpx, + XTENSA_OPCODE_IS_JUMP, + Opcode_jx_encode_fns, 0, 0 }, + { "l16ui", ICLASS_xt_iclass_l16ui, + 0, + Opcode_l16ui_encode_fns, 0, 0 }, + { "l16si", ICLASS_xt_iclass_l16si, + 0, + Opcode_l16si_encode_fns, 0, 0 }, + { "l32i", ICLASS_xt_iclass_l32i, + 0, + Opcode_l32i_encode_fns, 0, 0 }, + { "l32r", ICLASS_xt_iclass_l32r, + 0, + Opcode_l32r_encode_fns, 0, 0 }, + { "l8ui", ICLASS_xt_iclass_l8i, + 0, + Opcode_l8ui_encode_fns, 0, 0 }, + { "loop", ICLASS_xt_iclass_loop, + XTENSA_OPCODE_IS_LOOP, + Opcode_loop_encode_fns, 0, 0 }, + { "loopnez", ICLASS_xt_iclass_loopz, + XTENSA_OPCODE_IS_LOOP, + Opcode_loopnez_encode_fns, 0, 0 }, + { "loopgtz", ICLASS_xt_iclass_loopz, + XTENSA_OPCODE_IS_LOOP, + Opcode_loopgtz_encode_fns, 0, 0 }, + { "movi", ICLASS_xt_iclass_movi, + 0, + Opcode_movi_encode_fns, 0, 0 }, + { "moveqz", ICLASS_xt_iclass_movz, + 0, + Opcode_moveqz_encode_fns, 0, 0 }, + { "movnez", ICLASS_xt_iclass_movz, + 0, + Opcode_movnez_encode_fns, 0, 0 }, + { "movltz", ICLASS_xt_iclass_movz, + 0, + Opcode_movltz_encode_fns, 0, 0 }, + { "movgez", ICLASS_xt_iclass_movz, + 0, + Opcode_movgez_encode_fns, 0, 0 }, + { "neg", ICLASS_xt_iclass_neg, + 0, + Opcode_neg_encode_fns, 0, 0 }, + { "abs", ICLASS_xt_iclass_neg, + 0, + Opcode_abs_encode_fns, 0, 0 }, + { "nop", ICLASS_xt_iclass_nop, + 0, + Opcode_nop_encode_fns, 0, 0 }, + { "ret", ICLASS_xt_iclass_return, + XTENSA_OPCODE_IS_JUMP, + Opcode_ret_encode_fns, 0, 0 }, + { "simcall", ICLASS_xt_iclass_simcall, + 0, + Opcode_simcall_encode_fns, 0, 0 }, + { "s16i", ICLASS_xt_iclass_s16i, + 0, + Opcode_s16i_encode_fns, 0, 0 }, + { "s32i", ICLASS_xt_iclass_s32i, + 0, + Opcode_s32i_encode_fns, 0, 0 }, + { "s32nb", ICLASS_xt_iclass_s32nb, + 0, + Opcode_s32nb_encode_fns, 0, 0 }, + { "s8i", ICLASS_xt_iclass_s8i, + 0, + Opcode_s8i_encode_fns, 0, 0 }, + { "ssr", ICLASS_xt_iclass_sar, + 0, + Opcode_ssr_encode_fns, 0, 0 }, + { "ssl", ICLASS_xt_iclass_sar, + 0, + Opcode_ssl_encode_fns, 0, 0 }, + { "ssa8l", ICLASS_xt_iclass_sar, + 0, + Opcode_ssa8l_encode_fns, 0, 0 }, + { "ssa8b", ICLASS_xt_iclass_sar, + 0, + Opcode_ssa8b_encode_fns, 0, 0 }, + { "ssai", ICLASS_xt_iclass_sari, + 0, + Opcode_ssai_encode_fns, 0, 0 }, + { "sll", ICLASS_xt_iclass_shifts, + 0, + Opcode_sll_encode_fns, 0, 0 }, + { "src", ICLASS_xt_iclass_shiftst, + 0, + Opcode_src_encode_fns, 0, 0 }, + { "srl", ICLASS_xt_iclass_shiftt, + 0, + Opcode_srl_encode_fns, 0, 0 }, + { "sra", ICLASS_xt_iclass_shiftt, + 0, + Opcode_sra_encode_fns, 0, 0 }, + { "slli", ICLASS_xt_iclass_slli, + 0, + Opcode_slli_encode_fns, 0, 0 }, + { "srai", ICLASS_xt_iclass_srai, + 0, + Opcode_srai_encode_fns, 0, 0 }, + { "srli", ICLASS_xt_iclass_srli, + 0, + Opcode_srli_encode_fns, 0, 0 }, + { "memw", ICLASS_xt_iclass_memw, + 0, + Opcode_memw_encode_fns, 0, 0 }, + { "extw", ICLASS_xt_iclass_extw, + 0, + Opcode_extw_encode_fns, 0, 0 }, + { "isync", ICLASS_xt_iclass_isync, + 0, + Opcode_isync_encode_fns, 0, 0 }, + { "rsync", ICLASS_xt_iclass_sync, + 0, + Opcode_rsync_encode_fns, 0, 0 }, + { "esync", ICLASS_xt_iclass_sync, + 0, + Opcode_esync_encode_fns, 0, 0 }, + { "dsync", ICLASS_xt_iclass_sync, + 0, + Opcode_dsync_encode_fns, 0, 0 }, + { "rsil", ICLASS_xt_iclass_rsil, + 0, + Opcode_rsil_encode_fns, 0, 0 }, + { "rsr.lend", ICLASS_xt_iclass_rsr_lend, + 0, + Opcode_rsr_lend_encode_fns, 0, 0 }, + { "wsr.lend", ICLASS_xt_iclass_wsr_lend, + 0, + Opcode_wsr_lend_encode_fns, 0, 0 }, + { "xsr.lend", ICLASS_xt_iclass_xsr_lend, + 0, + Opcode_xsr_lend_encode_fns, 0, 0 }, + { "rsr.lcount", ICLASS_xt_iclass_rsr_lcount, + 0, + Opcode_rsr_lcount_encode_fns, 0, 0 }, + { "wsr.lcount", ICLASS_xt_iclass_wsr_lcount, + 0, + Opcode_wsr_lcount_encode_fns, 0, 0 }, + { "xsr.lcount", ICLASS_xt_iclass_xsr_lcount, + 0, + Opcode_xsr_lcount_encode_fns, 0, 0 }, + { "rsr.lbeg", ICLASS_xt_iclass_rsr_lbeg, + 0, + Opcode_rsr_lbeg_encode_fns, 0, 0 }, + { "wsr.lbeg", ICLASS_xt_iclass_wsr_lbeg, + 0, + Opcode_wsr_lbeg_encode_fns, 0, 0 }, + { "xsr.lbeg", ICLASS_xt_iclass_xsr_lbeg, + 0, + Opcode_xsr_lbeg_encode_fns, 0, 0 }, + { "rsr.sar", ICLASS_xt_iclass_rsr_sar, + 0, + Opcode_rsr_sar_encode_fns, 0, 0 }, + { "wsr.sar", ICLASS_xt_iclass_wsr_sar, + 0, + Opcode_wsr_sar_encode_fns, 0, 0 }, + { "xsr.sar", ICLASS_xt_iclass_xsr_sar, + 0, + Opcode_xsr_sar_encode_fns, 0, 0 }, + { "rsr.memctl", ICLASS_xt_iclass_rsr_memctl, + 0, + Opcode_rsr_memctl_encode_fns, 0, 0 }, + { "wsr.memctl", ICLASS_xt_iclass_wsr_memctl, + 0, + Opcode_wsr_memctl_encode_fns, 0, 0 }, + { "xsr.memctl", ICLASS_xt_iclass_xsr_memctl, + 0, + Opcode_xsr_memctl_encode_fns, 0, 0 }, + { "rsr.litbase", ICLASS_xt_iclass_rsr_litbase, + 0, + Opcode_rsr_litbase_encode_fns, 0, 0 }, + { "wsr.litbase", ICLASS_xt_iclass_wsr_litbase, + 0, + Opcode_wsr_litbase_encode_fns, 0, 0 }, + { "xsr.litbase", ICLASS_xt_iclass_xsr_litbase, + 0, + Opcode_xsr_litbase_encode_fns, 0, 0 }, + { "rsr.configid0", ICLASS_xt_iclass_rsr_configid0, + 0, + Opcode_rsr_configid0_encode_fns, 0, 0 }, + { "wsr.configid0", ICLASS_xt_iclass_wsr_configid0, + 0, + Opcode_wsr_configid0_encode_fns, 0, 0 }, + { "rsr.configid1", ICLASS_xt_iclass_rsr_configid1, + 0, + Opcode_rsr_configid1_encode_fns, 0, 0 }, + { "rsr.243", ICLASS_xt_iclass_rsr_243, + 0, + Opcode_rsr_243_encode_fns, 0, 0 }, + { "rsr.ps", ICLASS_xt_iclass_rsr_ps, + 0, + Opcode_rsr_ps_encode_fns, 0, 0 }, + { "wsr.ps", ICLASS_xt_iclass_wsr_ps, + 0, + Opcode_wsr_ps_encode_fns, 0, 0 }, + { "xsr.ps", ICLASS_xt_iclass_xsr_ps, + 0, + Opcode_xsr_ps_encode_fns, 0, 0 }, + { "rsr.epc1", ICLASS_xt_iclass_rsr_epc1, + 0, + Opcode_rsr_epc1_encode_fns, 0, 0 }, + { "wsr.epc1", ICLASS_xt_iclass_wsr_epc1, + 0, + Opcode_wsr_epc1_encode_fns, 0, 0 }, + { "xsr.epc1", ICLASS_xt_iclass_xsr_epc1, + 0, + Opcode_xsr_epc1_encode_fns, 0, 0 }, + { "rsr.excsave1", ICLASS_xt_iclass_rsr_excsave1, + 0, + Opcode_rsr_excsave1_encode_fns, 0, 0 }, + { "wsr.excsave1", ICLASS_xt_iclass_wsr_excsave1, + 0, + Opcode_wsr_excsave1_encode_fns, 0, 0 }, + { "xsr.excsave1", ICLASS_xt_iclass_xsr_excsave1, + 0, + Opcode_xsr_excsave1_encode_fns, 0, 0 }, + { "rsr.epc2", ICLASS_xt_iclass_rsr_epc2, + 0, + Opcode_rsr_epc2_encode_fns, 0, 0 }, + { "wsr.epc2", ICLASS_xt_iclass_wsr_epc2, + 0, + Opcode_wsr_epc2_encode_fns, 0, 0 }, + { "xsr.epc2", ICLASS_xt_iclass_xsr_epc2, + 0, + Opcode_xsr_epc2_encode_fns, 0, 0 }, + { "rsr.excsave2", ICLASS_xt_iclass_rsr_excsave2, + 0, + Opcode_rsr_excsave2_encode_fns, 0, 0 }, + { "wsr.excsave2", ICLASS_xt_iclass_wsr_excsave2, + 0, + Opcode_wsr_excsave2_encode_fns, 0, 0 }, + { "xsr.excsave2", ICLASS_xt_iclass_xsr_excsave2, + 0, + Opcode_xsr_excsave2_encode_fns, 0, 0 }, + { "rsr.epc3", ICLASS_xt_iclass_rsr_epc3, + 0, + Opcode_rsr_epc3_encode_fns, 0, 0 }, + { "wsr.epc3", ICLASS_xt_iclass_wsr_epc3, + 0, + Opcode_wsr_epc3_encode_fns, 0, 0 }, + { "xsr.epc3", ICLASS_xt_iclass_xsr_epc3, + 0, + Opcode_xsr_epc3_encode_fns, 0, 0 }, + { "rsr.excsave3", ICLASS_xt_iclass_rsr_excsave3, + 0, + Opcode_rsr_excsave3_encode_fns, 0, 0 }, + { "wsr.excsave3", ICLASS_xt_iclass_wsr_excsave3, + 0, + Opcode_wsr_excsave3_encode_fns, 0, 0 }, + { "xsr.excsave3", ICLASS_xt_iclass_xsr_excsave3, + 0, + Opcode_xsr_excsave3_encode_fns, 0, 0 }, + { "rsr.epc4", ICLASS_xt_iclass_rsr_epc4, + 0, + Opcode_rsr_epc4_encode_fns, 0, 0 }, + { "wsr.epc4", ICLASS_xt_iclass_wsr_epc4, + 0, + Opcode_wsr_epc4_encode_fns, 0, 0 }, + { "xsr.epc4", ICLASS_xt_iclass_xsr_epc4, + 0, + Opcode_xsr_epc4_encode_fns, 0, 0 }, + { "rsr.excsave4", ICLASS_xt_iclass_rsr_excsave4, + 0, + Opcode_rsr_excsave4_encode_fns, 0, 0 }, + { "wsr.excsave4", ICLASS_xt_iclass_wsr_excsave4, + 0, + Opcode_wsr_excsave4_encode_fns, 0, 0 }, + { "xsr.excsave4", ICLASS_xt_iclass_xsr_excsave4, + 0, + Opcode_xsr_excsave4_encode_fns, 0, 0 }, + { "rsr.epc5", ICLASS_xt_iclass_rsr_epc5, + 0, + Opcode_rsr_epc5_encode_fns, 0, 0 }, + { "wsr.epc5", ICLASS_xt_iclass_wsr_epc5, + 0, + Opcode_wsr_epc5_encode_fns, 0, 0 }, + { "xsr.epc5", ICLASS_xt_iclass_xsr_epc5, + 0, + Opcode_xsr_epc5_encode_fns, 0, 0 }, + { "rsr.excsave5", ICLASS_xt_iclass_rsr_excsave5, + 0, + Opcode_rsr_excsave5_encode_fns, 0, 0 }, + { "wsr.excsave5", ICLASS_xt_iclass_wsr_excsave5, + 0, + Opcode_wsr_excsave5_encode_fns, 0, 0 }, + { "xsr.excsave5", ICLASS_xt_iclass_xsr_excsave5, + 0, + Opcode_xsr_excsave5_encode_fns, 0, 0 }, + { "rsr.epc6", ICLASS_xt_iclass_rsr_epc6, + 0, + Opcode_rsr_epc6_encode_fns, 0, 0 }, + { "wsr.epc6", ICLASS_xt_iclass_wsr_epc6, + 0, + Opcode_wsr_epc6_encode_fns, 0, 0 }, + { "xsr.epc6", ICLASS_xt_iclass_xsr_epc6, + 0, + Opcode_xsr_epc6_encode_fns, 0, 0 }, + { "rsr.excsave6", ICLASS_xt_iclass_rsr_excsave6, + 0, + Opcode_rsr_excsave6_encode_fns, 0, 0 }, + { "wsr.excsave6", ICLASS_xt_iclass_wsr_excsave6, + 0, + Opcode_wsr_excsave6_encode_fns, 0, 0 }, + { "xsr.excsave6", ICLASS_xt_iclass_xsr_excsave6, + 0, + Opcode_xsr_excsave6_encode_fns, 0, 0 }, + { "rsr.epc7", ICLASS_xt_iclass_rsr_epc7, + 0, + Opcode_rsr_epc7_encode_fns, 0, 0 }, + { "wsr.epc7", ICLASS_xt_iclass_wsr_epc7, + 0, + Opcode_wsr_epc7_encode_fns, 0, 0 }, + { "xsr.epc7", ICLASS_xt_iclass_xsr_epc7, + 0, + Opcode_xsr_epc7_encode_fns, 0, 0 }, + { "rsr.excsave7", ICLASS_xt_iclass_rsr_excsave7, + 0, + Opcode_rsr_excsave7_encode_fns, 0, 0 }, + { "wsr.excsave7", ICLASS_xt_iclass_wsr_excsave7, + 0, + Opcode_wsr_excsave7_encode_fns, 0, 0 }, + { "xsr.excsave7", ICLASS_xt_iclass_xsr_excsave7, + 0, + Opcode_xsr_excsave7_encode_fns, 0, 0 }, + { "rsr.eps2", ICLASS_xt_iclass_rsr_eps2, + 0, + Opcode_rsr_eps2_encode_fns, 0, 0 }, + { "wsr.eps2", ICLASS_xt_iclass_wsr_eps2, + 0, + Opcode_wsr_eps2_encode_fns, 0, 0 }, + { "xsr.eps2", ICLASS_xt_iclass_xsr_eps2, + 0, + Opcode_xsr_eps2_encode_fns, 0, 0 }, + { "rsr.eps3", ICLASS_xt_iclass_rsr_eps3, + 0, + Opcode_rsr_eps3_encode_fns, 0, 0 }, + { "wsr.eps3", ICLASS_xt_iclass_wsr_eps3, + 0, + Opcode_wsr_eps3_encode_fns, 0, 0 }, + { "xsr.eps3", ICLASS_xt_iclass_xsr_eps3, + 0, + Opcode_xsr_eps3_encode_fns, 0, 0 }, + { "rsr.eps4", ICLASS_xt_iclass_rsr_eps4, + 0, + Opcode_rsr_eps4_encode_fns, 0, 0 }, + { "wsr.eps4", ICLASS_xt_iclass_wsr_eps4, + 0, + Opcode_wsr_eps4_encode_fns, 0, 0 }, + { "xsr.eps4", ICLASS_xt_iclass_xsr_eps4, + 0, + Opcode_xsr_eps4_encode_fns, 0, 0 }, + { "rsr.eps5", ICLASS_xt_iclass_rsr_eps5, + 0, + Opcode_rsr_eps5_encode_fns, 0, 0 }, + { "wsr.eps5", ICLASS_xt_iclass_wsr_eps5, + 0, + Opcode_wsr_eps5_encode_fns, 0, 0 }, + { "xsr.eps5", ICLASS_xt_iclass_xsr_eps5, + 0, + Opcode_xsr_eps5_encode_fns, 0, 0 }, + { "rsr.eps6", ICLASS_xt_iclass_rsr_eps6, + 0, + Opcode_rsr_eps6_encode_fns, 0, 0 }, + { "wsr.eps6", ICLASS_xt_iclass_wsr_eps6, + 0, + Opcode_wsr_eps6_encode_fns, 0, 0 }, + { "xsr.eps6", ICLASS_xt_iclass_xsr_eps6, + 0, + Opcode_xsr_eps6_encode_fns, 0, 0 }, + { "rsr.eps7", ICLASS_xt_iclass_rsr_eps7, + 0, + Opcode_rsr_eps7_encode_fns, 0, 0 }, + { "wsr.eps7", ICLASS_xt_iclass_wsr_eps7, + 0, + Opcode_wsr_eps7_encode_fns, 0, 0 }, + { "xsr.eps7", ICLASS_xt_iclass_xsr_eps7, + 0, + Opcode_xsr_eps7_encode_fns, 0, 0 }, + { "rsr.excvaddr", ICLASS_xt_iclass_rsr_excvaddr, + 0, + Opcode_rsr_excvaddr_encode_fns, 0, 0 }, + { "wsr.excvaddr", ICLASS_xt_iclass_wsr_excvaddr, + 0, + Opcode_wsr_excvaddr_encode_fns, 0, 0 }, + { "xsr.excvaddr", ICLASS_xt_iclass_xsr_excvaddr, + 0, + Opcode_xsr_excvaddr_encode_fns, 0, 0 }, + { "rsr.depc", ICLASS_xt_iclass_rsr_depc, + 0, + Opcode_rsr_depc_encode_fns, 0, 0 }, + { "wsr.depc", ICLASS_xt_iclass_wsr_depc, + 0, + Opcode_wsr_depc_encode_fns, 0, 0 }, + { "xsr.depc", ICLASS_xt_iclass_xsr_depc, + 0, + Opcode_xsr_depc_encode_fns, 0, 0 }, + { "rsr.exccause", ICLASS_xt_iclass_rsr_exccause, + 0, + Opcode_rsr_exccause_encode_fns, 0, 0 }, + { "wsr.exccause", ICLASS_xt_iclass_wsr_exccause, + 0, + Opcode_wsr_exccause_encode_fns, 0, 0 }, + { "xsr.exccause", ICLASS_xt_iclass_xsr_exccause, + 0, + Opcode_xsr_exccause_encode_fns, 0, 0 }, + { "rsr.misc0", ICLASS_xt_iclass_rsr_misc0, + 0, + Opcode_rsr_misc0_encode_fns, 0, 0 }, + { "wsr.misc0", ICLASS_xt_iclass_wsr_misc0, + 0, + Opcode_wsr_misc0_encode_fns, 0, 0 }, + { "xsr.misc0", ICLASS_xt_iclass_xsr_misc0, + 0, + Opcode_xsr_misc0_encode_fns, 0, 0 }, + { "rsr.misc1", ICLASS_xt_iclass_rsr_misc1, + 0, + Opcode_rsr_misc1_encode_fns, 0, 0 }, + { "wsr.misc1", ICLASS_xt_iclass_wsr_misc1, + 0, + Opcode_wsr_misc1_encode_fns, 0, 0 }, + { "xsr.misc1", ICLASS_xt_iclass_xsr_misc1, + 0, + Opcode_xsr_misc1_encode_fns, 0, 0 }, + { "rsr.prid", ICLASS_xt_iclass_rsr_prid, + 0, + Opcode_rsr_prid_encode_fns, 0, 0 }, + { "rsr.vecbase", ICLASS_xt_iclass_rsr_vecbase, + 0, + Opcode_rsr_vecbase_encode_fns, 0, 0 }, + { "wsr.vecbase", ICLASS_xt_iclass_wsr_vecbase, + 0, + Opcode_wsr_vecbase_encode_fns, 0, 0 }, + { "xsr.vecbase", ICLASS_xt_iclass_xsr_vecbase, + 0, + Opcode_xsr_vecbase_encode_fns, 0, 0 }, + { "mul16u", ICLASS_xt_mul16, + 0, + Opcode_mul16u_encode_fns, 0, 0 }, + { "mul16s", ICLASS_xt_mul16, + 0, + Opcode_mul16s_encode_fns, 0, 0 }, + { "mull", ICLASS_xt_mul32, + 0, + Opcode_mull_encode_fns, 0, 0 }, + { "muluh", ICLASS_xt_mul32h, + 0, + Opcode_muluh_encode_fns, 0, 0 }, + { "mulsh", ICLASS_xt_mul32h, + 0, + Opcode_mulsh_encode_fns, 0, 0 }, + { "mul.aa.ll", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_mul_aa_ll_encode_fns, 0, 0 }, + { "mul.aa.hl", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_mul_aa_hl_encode_fns, 0, 0 }, + { "mul.aa.lh", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_mul_aa_lh_encode_fns, 0, 0 }, + { "mul.aa.hh", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_mul_aa_hh_encode_fns, 0, 0 }, + { "umul.aa.ll", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_umul_aa_ll_encode_fns, 0, 0 }, + { "umul.aa.hl", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_umul_aa_hl_encode_fns, 0, 0 }, + { "umul.aa.lh", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_umul_aa_lh_encode_fns, 0, 0 }, + { "umul.aa.hh", ICLASS_xt_iclass_mac16_aa, + 0, + Opcode_umul_aa_hh_encode_fns, 0, 0 }, + { "mul.ad.ll", ICLASS_xt_iclass_mac16_ad, + 0, + Opcode_mul_ad_ll_encode_fns, 0, 0 }, + { "mul.ad.hl", ICLASS_xt_iclass_mac16_ad, + 0, + Opcode_mul_ad_hl_encode_fns, 0, 0 }, + { "mul.ad.lh", ICLASS_xt_iclass_mac16_ad, + 0, + Opcode_mul_ad_lh_encode_fns, 0, 0 }, + { "mul.ad.hh", ICLASS_xt_iclass_mac16_ad, + 0, + Opcode_mul_ad_hh_encode_fns, 0, 0 }, + { "mul.da.ll", ICLASS_xt_iclass_mac16_da, + 0, + Opcode_mul_da_ll_encode_fns, 0, 0 }, + { "mul.da.hl", ICLASS_xt_iclass_mac16_da, + 0, + Opcode_mul_da_hl_encode_fns, 0, 0 }, + { "mul.da.lh", ICLASS_xt_iclass_mac16_da, + 0, + Opcode_mul_da_lh_encode_fns, 0, 0 }, + { "mul.da.hh", ICLASS_xt_iclass_mac16_da, + 0, + Opcode_mul_da_hh_encode_fns, 0, 0 }, + { "mul.dd.ll", ICLASS_xt_iclass_mac16_dd, + 0, + Opcode_mul_dd_ll_encode_fns, 0, 0 }, + { "mul.dd.hl", ICLASS_xt_iclass_mac16_dd, + 0, + Opcode_mul_dd_hl_encode_fns, 0, 0 }, + { "mul.dd.lh", ICLASS_xt_iclass_mac16_dd, + 0, + Opcode_mul_dd_lh_encode_fns, 0, 0 }, + { "mul.dd.hh", ICLASS_xt_iclass_mac16_dd, + 0, + Opcode_mul_dd_hh_encode_fns, 0, 0 }, + { "mula.aa.ll", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_mula_aa_ll_encode_fns, 0, 0 }, + { "mula.aa.hl", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_mula_aa_hl_encode_fns, 0, 0 }, + { "mula.aa.lh", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_mula_aa_lh_encode_fns, 0, 0 }, + { "mula.aa.hh", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_mula_aa_hh_encode_fns, 0, 0 }, + { "muls.aa.ll", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_muls_aa_ll_encode_fns, 0, 0 }, + { "muls.aa.hl", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_muls_aa_hl_encode_fns, 0, 0 }, + { "muls.aa.lh", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_muls_aa_lh_encode_fns, 0, 0 }, + { "muls.aa.hh", ICLASS_xt_iclass_mac16a_aa, + 0, + Opcode_muls_aa_hh_encode_fns, 0, 0 }, + { "mula.ad.ll", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_mula_ad_ll_encode_fns, 0, 0 }, + { "mula.ad.hl", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_mula_ad_hl_encode_fns, 0, 0 }, + { "mula.ad.lh", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_mula_ad_lh_encode_fns, 0, 0 }, + { "mula.ad.hh", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_mula_ad_hh_encode_fns, 0, 0 }, + { "muls.ad.ll", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_muls_ad_ll_encode_fns, 0, 0 }, + { "muls.ad.hl", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_muls_ad_hl_encode_fns, 0, 0 }, + { "muls.ad.lh", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_muls_ad_lh_encode_fns, 0, 0 }, + { "muls.ad.hh", ICLASS_xt_iclass_mac16a_ad, + 0, + Opcode_muls_ad_hh_encode_fns, 0, 0 }, + { "mula.da.ll", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_mula_da_ll_encode_fns, 0, 0 }, + { "mula.da.hl", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_mula_da_hl_encode_fns, 0, 0 }, + { "mula.da.lh", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_mula_da_lh_encode_fns, 0, 0 }, + { "mula.da.hh", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_mula_da_hh_encode_fns, 0, 0 }, + { "muls.da.ll", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_muls_da_ll_encode_fns, 0, 0 }, + { "muls.da.hl", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_muls_da_hl_encode_fns, 0, 0 }, + { "muls.da.lh", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_muls_da_lh_encode_fns, 0, 0 }, + { "muls.da.hh", ICLASS_xt_iclass_mac16a_da, + 0, + Opcode_muls_da_hh_encode_fns, 0, 0 }, + { "mula.dd.ll", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_mula_dd_ll_encode_fns, 0, 0 }, + { "mula.dd.hl", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_mula_dd_hl_encode_fns, 0, 0 }, + { "mula.dd.lh", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_mula_dd_lh_encode_fns, 0, 0 }, + { "mula.dd.hh", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_mula_dd_hh_encode_fns, 0, 0 }, + { "muls.dd.ll", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_muls_dd_ll_encode_fns, 0, 0 }, + { "muls.dd.hl", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_muls_dd_hl_encode_fns, 0, 0 }, + { "muls.dd.lh", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_muls_dd_lh_encode_fns, 0, 0 }, + { "muls.dd.hh", ICLASS_xt_iclass_mac16a_dd, + 0, + Opcode_muls_dd_hh_encode_fns, 0, 0 }, + { "mula.da.ll.lddec", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_ll_lddec_encode_fns, 0, 0 }, + { "mula.da.ll.ldinc", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_ll_ldinc_encode_fns, 0, 0 }, + { "mula.da.hl.lddec", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_hl_lddec_encode_fns, 0, 0 }, + { "mula.da.hl.ldinc", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_hl_ldinc_encode_fns, 0, 0 }, + { "mula.da.lh.lddec", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_lh_lddec_encode_fns, 0, 0 }, + { "mula.da.lh.ldinc", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_lh_ldinc_encode_fns, 0, 0 }, + { "mula.da.hh.lddec", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_hh_lddec_encode_fns, 0, 0 }, + { "mula.da.hh.ldinc", ICLASS_xt_iclass_mac16al_da, + 0, + Opcode_mula_da_hh_ldinc_encode_fns, 0, 0 }, + { "mula.dd.ll.lddec", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_ll_lddec_encode_fns, 0, 0 }, + { "mula.dd.ll.ldinc", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_ll_ldinc_encode_fns, 0, 0 }, + { "mula.dd.hl.lddec", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_hl_lddec_encode_fns, 0, 0 }, + { "mula.dd.hl.ldinc", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_hl_ldinc_encode_fns, 0, 0 }, + { "mula.dd.lh.lddec", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_lh_lddec_encode_fns, 0, 0 }, + { "mula.dd.lh.ldinc", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_lh_ldinc_encode_fns, 0, 0 }, + { "mula.dd.hh.lddec", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_hh_lddec_encode_fns, 0, 0 }, + { "mula.dd.hh.ldinc", ICLASS_xt_iclass_mac16al_dd, + 0, + Opcode_mula_dd_hh_ldinc_encode_fns, 0, 0 }, + { "lddec", ICLASS_xt_iclass_mac16_l, + 0, + Opcode_lddec_encode_fns, 0, 0 }, + { "ldinc", ICLASS_xt_iclass_mac16_l, + 0, + Opcode_ldinc_encode_fns, 0, 0 }, + { "rsr.m0", ICLASS_xt_iclass_rsr_m0, + 0, + Opcode_rsr_m0_encode_fns, 0, 0 }, + { "wsr.m0", ICLASS_xt_iclass_wsr_m0, + 0, + Opcode_wsr_m0_encode_fns, 0, 0 }, + { "xsr.m0", ICLASS_xt_iclass_xsr_m0, + 0, + Opcode_xsr_m0_encode_fns, 0, 0 }, + { "rsr.m1", ICLASS_xt_iclass_rsr_m1, + 0, + Opcode_rsr_m1_encode_fns, 0, 0 }, + { "wsr.m1", ICLASS_xt_iclass_wsr_m1, + 0, + Opcode_wsr_m1_encode_fns, 0, 0 }, + { "xsr.m1", ICLASS_xt_iclass_xsr_m1, + 0, + Opcode_xsr_m1_encode_fns, 0, 0 }, + { "rsr.m2", ICLASS_xt_iclass_rsr_m2, + 0, + Opcode_rsr_m2_encode_fns, 0, 0 }, + { "wsr.m2", ICLASS_xt_iclass_wsr_m2, + 0, + Opcode_wsr_m2_encode_fns, 0, 0 }, + { "xsr.m2", ICLASS_xt_iclass_xsr_m2, + 0, + Opcode_xsr_m2_encode_fns, 0, 0 }, + { "rsr.m3", ICLASS_xt_iclass_rsr_m3, + 0, + Opcode_rsr_m3_encode_fns, 0, 0 }, + { "wsr.m3", ICLASS_xt_iclass_wsr_m3, + 0, + Opcode_wsr_m3_encode_fns, 0, 0 }, + { "xsr.m3", ICLASS_xt_iclass_xsr_m3, + 0, + Opcode_xsr_m3_encode_fns, 0, 0 }, + { "rsr.acclo", ICLASS_xt_iclass_rsr_acclo, + 0, + Opcode_rsr_acclo_encode_fns, 0, 0 }, + { "wsr.acclo", ICLASS_xt_iclass_wsr_acclo, + 0, + Opcode_wsr_acclo_encode_fns, 0, 0 }, + { "xsr.acclo", ICLASS_xt_iclass_xsr_acclo, + 0, + Opcode_xsr_acclo_encode_fns, 0, 0 }, + { "rsr.acchi", ICLASS_xt_iclass_rsr_acchi, + 0, + Opcode_rsr_acchi_encode_fns, 0, 0 }, + { "wsr.acchi", ICLASS_xt_iclass_wsr_acchi, + 0, + Opcode_wsr_acchi_encode_fns, 0, 0 }, + { "xsr.acchi", ICLASS_xt_iclass_xsr_acchi, + 0, + Opcode_xsr_acchi_encode_fns, 0, 0 }, + { "rfi", ICLASS_xt_iclass_rfi, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfi_encode_fns, 0, 0 }, + { "waiti", ICLASS_xt_iclass_wait, + 0, + Opcode_waiti_encode_fns, 0, 0 }, + { "rsr.interrupt", ICLASS_xt_iclass_rsr_interrupt, + 0, + Opcode_rsr_interrupt_encode_fns, 0, 0 }, + { "wsr.intset", ICLASS_xt_iclass_wsr_intset, + 0, + Opcode_wsr_intset_encode_fns, 0, 0 }, + { "wsr.intclear", ICLASS_xt_iclass_wsr_intclear, + 0, + Opcode_wsr_intclear_encode_fns, 0, 0 }, + { "rsr.intenable", ICLASS_xt_iclass_rsr_intenable, + 0, + Opcode_rsr_intenable_encode_fns, 0, 0 }, + { "wsr.intenable", ICLASS_xt_iclass_wsr_intenable, + 0, + Opcode_wsr_intenable_encode_fns, 0, 0 }, + { "xsr.intenable", ICLASS_xt_iclass_xsr_intenable, + 0, + Opcode_xsr_intenable_encode_fns, 0, 0 }, + { "break", ICLASS_xt_iclass_break, + 0, + Opcode_break_encode_fns, 0, 0 }, + { "break.n", ICLASS_xt_iclass_break_n, + 0, + Opcode_break_n_encode_fns, 0, 0 }, + { "rsr.dbreaka0", ICLASS_xt_iclass_rsr_dbreaka0, + 0, + Opcode_rsr_dbreaka0_encode_fns, 0, 0 }, + { "wsr.dbreaka0", ICLASS_xt_iclass_wsr_dbreaka0, + 0, + Opcode_wsr_dbreaka0_encode_fns, 0, 0 }, + { "xsr.dbreaka0", ICLASS_xt_iclass_xsr_dbreaka0, + 0, + Opcode_xsr_dbreaka0_encode_fns, 0, 0 }, + { "rsr.dbreakc0", ICLASS_xt_iclass_rsr_dbreakc0, + 0, + Opcode_rsr_dbreakc0_encode_fns, 0, 0 }, + { "wsr.dbreakc0", ICLASS_xt_iclass_wsr_dbreakc0, + 0, + Opcode_wsr_dbreakc0_encode_fns, 0, 0 }, + { "xsr.dbreakc0", ICLASS_xt_iclass_xsr_dbreakc0, + 0, + Opcode_xsr_dbreakc0_encode_fns, 0, 0 }, + { "rsr.dbreaka1", ICLASS_xt_iclass_rsr_dbreaka1, + 0, + Opcode_rsr_dbreaka1_encode_fns, 0, 0 }, + { "wsr.dbreaka1", ICLASS_xt_iclass_wsr_dbreaka1, + 0, + Opcode_wsr_dbreaka1_encode_fns, 0, 0 }, + { "xsr.dbreaka1", ICLASS_xt_iclass_xsr_dbreaka1, + 0, + Opcode_xsr_dbreaka1_encode_fns, 0, 0 }, + { "rsr.dbreakc1", ICLASS_xt_iclass_rsr_dbreakc1, + 0, + Opcode_rsr_dbreakc1_encode_fns, 0, 0 }, + { "wsr.dbreakc1", ICLASS_xt_iclass_wsr_dbreakc1, + 0, + Opcode_wsr_dbreakc1_encode_fns, 0, 0 }, + { "xsr.dbreakc1", ICLASS_xt_iclass_xsr_dbreakc1, + 0, + Opcode_xsr_dbreakc1_encode_fns, 0, 0 }, + { "rsr.ibreaka0", ICLASS_xt_iclass_rsr_ibreaka0, + 0, + Opcode_rsr_ibreaka0_encode_fns, 0, 0 }, + { "wsr.ibreaka0", ICLASS_xt_iclass_wsr_ibreaka0, + 0, + Opcode_wsr_ibreaka0_encode_fns, 0, 0 }, + { "xsr.ibreaka0", ICLASS_xt_iclass_xsr_ibreaka0, + 0, + Opcode_xsr_ibreaka0_encode_fns, 0, 0 }, + { "rsr.ibreaka1", ICLASS_xt_iclass_rsr_ibreaka1, + 0, + Opcode_rsr_ibreaka1_encode_fns, 0, 0 }, + { "wsr.ibreaka1", ICLASS_xt_iclass_wsr_ibreaka1, + 0, + Opcode_wsr_ibreaka1_encode_fns, 0, 0 }, + { "xsr.ibreaka1", ICLASS_xt_iclass_xsr_ibreaka1, + 0, + Opcode_xsr_ibreaka1_encode_fns, 0, 0 }, + { "rsr.ibreakenable", ICLASS_xt_iclass_rsr_ibreakenable, + 0, + Opcode_rsr_ibreakenable_encode_fns, 0, 0 }, + { "wsr.ibreakenable", ICLASS_xt_iclass_wsr_ibreakenable, + 0, + Opcode_wsr_ibreakenable_encode_fns, 0, 0 }, + { "xsr.ibreakenable", ICLASS_xt_iclass_xsr_ibreakenable, + 0, + Opcode_xsr_ibreakenable_encode_fns, 0, 0 }, + { "rsr.debugcause", ICLASS_xt_iclass_rsr_debugcause, + 0, + Opcode_rsr_debugcause_encode_fns, 0, 0 }, + { "wsr.debugcause", ICLASS_xt_iclass_wsr_debugcause, + 0, + Opcode_wsr_debugcause_encode_fns, 0, 0 }, + { "xsr.debugcause", ICLASS_xt_iclass_xsr_debugcause, + 0, + Opcode_xsr_debugcause_encode_fns, 0, 0 }, + { "rsr.icount", ICLASS_xt_iclass_rsr_icount, + 0, + Opcode_rsr_icount_encode_fns, 0, 0 }, + { "wsr.icount", ICLASS_xt_iclass_wsr_icount, + 0, + Opcode_wsr_icount_encode_fns, 0, 0 }, + { "xsr.icount", ICLASS_xt_iclass_xsr_icount, + 0, + Opcode_xsr_icount_encode_fns, 0, 0 }, + { "rsr.icountlevel", ICLASS_xt_iclass_rsr_icountlevel, + 0, + Opcode_rsr_icountlevel_encode_fns, 0, 0 }, + { "wsr.icountlevel", ICLASS_xt_iclass_wsr_icountlevel, + 0, + Opcode_wsr_icountlevel_encode_fns, 0, 0 }, + { "xsr.icountlevel", ICLASS_xt_iclass_xsr_icountlevel, + 0, + Opcode_xsr_icountlevel_encode_fns, 0, 0 }, + { "rsr.ddr", ICLASS_xt_iclass_rsr_ddr, + 0, + Opcode_rsr_ddr_encode_fns, 0, 0 }, + { "wsr.ddr", ICLASS_xt_iclass_wsr_ddr, + 0, + Opcode_wsr_ddr_encode_fns, 0, 0 }, + { "xsr.ddr", ICLASS_xt_iclass_xsr_ddr, + 0, + Opcode_xsr_ddr_encode_fns, 0, 0 }, + { "lddr32.p", ICLASS_xt_iclass_lddr32_p, + 0, + Opcode_lddr32_p_encode_fns, 0, 0 }, + { "sddr32.p", ICLASS_xt_iclass_sddr32_p, + 0, + Opcode_sddr32_p_encode_fns, 0, 0 }, + { "rfdo", ICLASS_xt_iclass_rfdo, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfdo_encode_fns, 0, 0 }, + { "rfdd", ICLASS_xt_iclass_rfdd, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfdd_encode_fns, 0, 0 }, + { "wsr.mmid", ICLASS_xt_iclass_wsr_mmid, + 0, + Opcode_wsr_mmid_encode_fns, 0, 0 }, + { "andb", ICLASS_xt_iclass_bbool1, + 0, + Opcode_andb_encode_fns, 0, 0 }, + { "andbc", ICLASS_xt_iclass_bbool1, + 0, + Opcode_andbc_encode_fns, 0, 0 }, + { "orb", ICLASS_xt_iclass_bbool1, + 0, + Opcode_orb_encode_fns, 0, 0 }, + { "orbc", ICLASS_xt_iclass_bbool1, + 0, + Opcode_orbc_encode_fns, 0, 0 }, + { "xorb", ICLASS_xt_iclass_bbool1, + 0, + Opcode_xorb_encode_fns, 0, 0 }, + { "any4", ICLASS_xt_iclass_bbool4, + 0, + Opcode_any4_encode_fns, 0, 0 }, + { "all4", ICLASS_xt_iclass_bbool4, + 0, + Opcode_all4_encode_fns, 0, 0 }, + { "any8", ICLASS_xt_iclass_bbool8, + 0, + Opcode_any8_encode_fns, 0, 0 }, + { "all8", ICLASS_xt_iclass_bbool8, + 0, + Opcode_all8_encode_fns, 0, 0 }, + { "bf", ICLASS_xt_iclass_bbranch, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bf_encode_fns, 0, 0 }, + { "bt", ICLASS_xt_iclass_bbranch, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bt_encode_fns, 0, 0 }, + { "movf", ICLASS_xt_iclass_bmove, + 0, + Opcode_movf_encode_fns, 0, 0 }, + { "movt", ICLASS_xt_iclass_bmove, + 0, + Opcode_movt_encode_fns, 0, 0 }, + { "rsr.br", ICLASS_xt_iclass_RSR_BR, + 0, + Opcode_rsr_br_encode_fns, 0, 0 }, + { "wsr.br", ICLASS_xt_iclass_WSR_BR, + 0, + Opcode_wsr_br_encode_fns, 0, 0 }, + { "xsr.br", ICLASS_xt_iclass_XSR_BR, + 0, + Opcode_xsr_br_encode_fns, 0, 0 }, + { "rsr.ccount", ICLASS_xt_iclass_rsr_ccount, + 0, + Opcode_rsr_ccount_encode_fns, 0, 0 }, + { "wsr.ccount", ICLASS_xt_iclass_wsr_ccount, + 0, + Opcode_wsr_ccount_encode_fns, 0, 0 }, + { "xsr.ccount", ICLASS_xt_iclass_xsr_ccount, + 0, + Opcode_xsr_ccount_encode_fns, 0, 0 }, + { "rsr.ccompare0", ICLASS_xt_iclass_rsr_ccompare0, + 0, + Opcode_rsr_ccompare0_encode_fns, 0, 0 }, + { "wsr.ccompare0", ICLASS_xt_iclass_wsr_ccompare0, + 0, + Opcode_wsr_ccompare0_encode_fns, 0, 0 }, + { "xsr.ccompare0", ICLASS_xt_iclass_xsr_ccompare0, + 0, + Opcode_xsr_ccompare0_encode_fns, 0, 0 }, + { "rsr.ccompare1", ICLASS_xt_iclass_rsr_ccompare1, + 0, + Opcode_rsr_ccompare1_encode_fns, 0, 0 }, + { "wsr.ccompare1", ICLASS_xt_iclass_wsr_ccompare1, + 0, + Opcode_wsr_ccompare1_encode_fns, 0, 0 }, + { "xsr.ccompare1", ICLASS_xt_iclass_xsr_ccompare1, + 0, + Opcode_xsr_ccompare1_encode_fns, 0, 0 }, + { "rsr.ccompare2", ICLASS_xt_iclass_rsr_ccompare2, + 0, + Opcode_rsr_ccompare2_encode_fns, 0, 0 }, + { "wsr.ccompare2", ICLASS_xt_iclass_wsr_ccompare2, + 0, + Opcode_wsr_ccompare2_encode_fns, 0, 0 }, + { "xsr.ccompare2", ICLASS_xt_iclass_xsr_ccompare2, + 0, + Opcode_xsr_ccompare2_encode_fns, 0, 0 }, + { "ipf", ICLASS_xt_iclass_icache, + 0, + Opcode_ipf_encode_fns, 0, 0 }, + { "ihi", ICLASS_xt_iclass_icache, + 0, + Opcode_ihi_encode_fns, 0, 0 }, + { "ipfl", ICLASS_xt_iclass_icache_lock, + 0, + Opcode_ipfl_encode_fns, 0, 0 }, + { "ihu", ICLASS_xt_iclass_icache_lock, + 0, + Opcode_ihu_encode_fns, 0, 0 }, + { "iiu", ICLASS_xt_iclass_icache_lock, + 0, + Opcode_iiu_encode_fns, 0, 0 }, + { "iii", ICLASS_xt_iclass_icache_inv, + 0, + Opcode_iii_encode_fns, 0, 0 }, + { "lict", ICLASS_xt_iclass_licx, + 0, + Opcode_lict_encode_fns, 0, 0 }, + { "licw", ICLASS_xt_iclass_licx, + 0, + Opcode_licw_encode_fns, 0, 0 }, + { "sict", ICLASS_xt_iclass_sicx, + 0, + Opcode_sict_encode_fns, 0, 0 }, + { "sicw", ICLASS_xt_iclass_sicx, + 0, + Opcode_sicw_encode_fns, 0, 0 }, + { "dhwb", ICLASS_xt_iclass_dcache, + 0, + Opcode_dhwb_encode_fns, 0, 0 }, + { "dhwbi", ICLASS_xt_iclass_dcache, + 0, + Opcode_dhwbi_encode_fns, 0, 0 }, + { "diwbui.p", ICLASS_xt_iclass_dcache_dyn, + 0, + Opcode_diwbui_p_encode_fns, 0, 0 }, + { "diwb", ICLASS_xt_iclass_dcache_ind, + 0, + Opcode_diwb_encode_fns, 0, 0 }, + { "diwbi", ICLASS_xt_iclass_dcache_ind, + 0, + Opcode_diwbi_encode_fns, 0, 0 }, + { "dhi", ICLASS_xt_iclass_dcache_inv, + 0, + Opcode_dhi_encode_fns, 0, 0 }, + { "dii", ICLASS_xt_iclass_dcache_inv, + 0, + Opcode_dii_encode_fns, 0, 0 }, + { "dpfr", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfr_encode_fns, 0, 0 }, + { "dpfw", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfw_encode_fns, 0, 0 }, + { "dpfro", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfro_encode_fns, 0, 0 }, + { "dpfwo", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfwo_encode_fns, 0, 0 }, + { "dpfl", ICLASS_xt_iclass_dcache_lock, + 0, + Opcode_dpfl_encode_fns, 0, 0 }, + { "dhu", ICLASS_xt_iclass_dcache_lock, + 0, + Opcode_dhu_encode_fns, 0, 0 }, + { "diu", ICLASS_xt_iclass_dcache_lock, + 0, + Opcode_diu_encode_fns, 0, 0 }, + { "sdct", ICLASS_xt_iclass_sdct, + 0, + Opcode_sdct_encode_fns, 0, 0 }, + { "ldct", ICLASS_xt_iclass_ldct, + 0, + Opcode_ldct_encode_fns, 0, 0 }, + { "rsr.prefctl", ICLASS_xt_iclass_rsr_prefctl, + 0, + Opcode_rsr_prefctl_encode_fns, 0, 0 }, + { "wsr.prefctl", ICLASS_xt_iclass_wsr_prefctl, + 0, + Opcode_wsr_prefctl_encode_fns, 0, 0 }, + { "xsr.prefctl", ICLASS_xt_iclass_xsr_prefctl, + 0, + Opcode_xsr_prefctl_encode_fns, 0, 0 }, + { "wsr.ptevaddr", ICLASS_xt_iclass_wsr_ptevaddr, + 0, + Opcode_wsr_ptevaddr_encode_fns, 0, 0 }, + { "rsr.ptevaddr", ICLASS_xt_iclass_rsr_ptevaddr, + 0, + Opcode_rsr_ptevaddr_encode_fns, 0, 0 }, + { "xsr.ptevaddr", ICLASS_xt_iclass_xsr_ptevaddr, + 0, + Opcode_xsr_ptevaddr_encode_fns, 0, 0 }, + { "rsr.rasid", ICLASS_xt_iclass_rsr_rasid, + 0, + Opcode_rsr_rasid_encode_fns, 0, 0 }, + { "wsr.rasid", ICLASS_xt_iclass_wsr_rasid, + 0, + Opcode_wsr_rasid_encode_fns, 0, 0 }, + { "xsr.rasid", ICLASS_xt_iclass_xsr_rasid, + 0, + Opcode_xsr_rasid_encode_fns, 0, 0 }, + { "rsr.itlbcfg", ICLASS_xt_iclass_rsr_itlbcfg, + 0, + Opcode_rsr_itlbcfg_encode_fns, 0, 0 }, + { "wsr.itlbcfg", ICLASS_xt_iclass_wsr_itlbcfg, + 0, + Opcode_wsr_itlbcfg_encode_fns, 0, 0 }, + { "xsr.itlbcfg", ICLASS_xt_iclass_xsr_itlbcfg, + 0, + Opcode_xsr_itlbcfg_encode_fns, 0, 0 }, + { "rsr.dtlbcfg", ICLASS_xt_iclass_rsr_dtlbcfg, + 0, + Opcode_rsr_dtlbcfg_encode_fns, 0, 0 }, + { "wsr.dtlbcfg", ICLASS_xt_iclass_wsr_dtlbcfg, + 0, + Opcode_wsr_dtlbcfg_encode_fns, 0, 0 }, + { "xsr.dtlbcfg", ICLASS_xt_iclass_xsr_dtlbcfg, + 0, + Opcode_xsr_dtlbcfg_encode_fns, 0, 0 }, + { "idtlb", ICLASS_xt_iclass_idtlb, + 0, + Opcode_idtlb_encode_fns, 0, 0 }, + { "pdtlb", ICLASS_xt_iclass_rdtlb, + 0, + Opcode_pdtlb_encode_fns, 0, 0 }, + { "rdtlb0", ICLASS_xt_iclass_rdtlb, + 0, + Opcode_rdtlb0_encode_fns, 0, 0 }, + { "rdtlb1", ICLASS_xt_iclass_rdtlb, + 0, + Opcode_rdtlb1_encode_fns, 0, 0 }, + { "wdtlb", ICLASS_xt_iclass_wdtlb, + 0, + Opcode_wdtlb_encode_fns, 0, 0 }, + { "iitlb", ICLASS_xt_iclass_iitlb, + 0, + Opcode_iitlb_encode_fns, 0, 0 }, + { "pitlb", ICLASS_xt_iclass_ritlb, + 0, + Opcode_pitlb_encode_fns, 0, 0 }, + { "ritlb0", ICLASS_xt_iclass_ritlb, + 0, + Opcode_ritlb0_encode_fns, 0, 0 }, + { "ritlb1", ICLASS_xt_iclass_ritlb, + 0, + Opcode_ritlb1_encode_fns, 0, 0 }, + { "witlb", ICLASS_xt_iclass_witlb, + 0, + Opcode_witlb_encode_fns, 0, 0 }, + { "ldpte", ICLASS_xt_iclass_ldpte, + 0, + Opcode_ldpte_encode_fns, 0, 0 }, + { "hwwitlba", ICLASS_xt_iclass_hwwitlba, + XTENSA_OPCODE_IS_BRANCH, + Opcode_hwwitlba_encode_fns, 0, 0 }, + { "hwwdtlba", ICLASS_xt_iclass_hwwdtlba, + 0, + Opcode_hwwdtlba_encode_fns, 0, 0 }, + { "rsr.cpenable", ICLASS_xt_iclass_rsr_cpenable, + 0, + Opcode_rsr_cpenable_encode_fns, 0, 0 }, + { "wsr.cpenable", ICLASS_xt_iclass_wsr_cpenable, + 0, + Opcode_wsr_cpenable_encode_fns, 0, 0 }, + { "xsr.cpenable", ICLASS_xt_iclass_xsr_cpenable, + 0, + Opcode_xsr_cpenable_encode_fns, 0, 0 }, + { "clamps", ICLASS_xt_iclass_clamp, + 0, + Opcode_clamps_encode_fns, 0, 0 }, + { "min", ICLASS_xt_iclass_minmax, + 0, + Opcode_min_encode_fns, 0, 0 }, + { "max", ICLASS_xt_iclass_minmax, + 0, + Opcode_max_encode_fns, 0, 0 }, + { "minu", ICLASS_xt_iclass_minmax, + 0, + Opcode_minu_encode_fns, 0, 0 }, + { "maxu", ICLASS_xt_iclass_minmax, + 0, + Opcode_maxu_encode_fns, 0, 0 }, + { "nsa", ICLASS_xt_iclass_nsa, + 0, + Opcode_nsa_encode_fns, 0, 0 }, + { "nsau", ICLASS_xt_iclass_nsa, + 0, + Opcode_nsau_encode_fns, 0, 0 }, + { "sext", ICLASS_xt_iclass_sx, + 0, + Opcode_sext_encode_fns, 0, 0 }, + { "l32ai", ICLASS_xt_iclass_l32ai, + 0, + Opcode_l32ai_encode_fns, 0, 0 }, + { "s32ri", ICLASS_xt_iclass_s32ri, + 0, + Opcode_s32ri_encode_fns, 0, 0 }, + { "s32c1i", ICLASS_xt_iclass_s32c1i, + 0, + Opcode_s32c1i_encode_fns, 0, 0 }, + { "rsr.scompare1", ICLASS_xt_iclass_rsr_scompare1, + 0, + Opcode_rsr_scompare1_encode_fns, 0, 0 }, + { "wsr.scompare1", ICLASS_xt_iclass_wsr_scompare1, + 0, + Opcode_wsr_scompare1_encode_fns, 0, 0 }, + { "xsr.scompare1", ICLASS_xt_iclass_xsr_scompare1, + 0, + Opcode_xsr_scompare1_encode_fns, 0, 0 }, + { "rsr.atomctl", ICLASS_xt_iclass_rsr_atomctl, + 0, + Opcode_rsr_atomctl_encode_fns, 0, 0 }, + { "wsr.atomctl", ICLASS_xt_iclass_wsr_atomctl, + 0, + Opcode_wsr_atomctl_encode_fns, 0, 0 }, + { "xsr.atomctl", ICLASS_xt_iclass_xsr_atomctl, + 0, + Opcode_xsr_atomctl_encode_fns, 0, 0 }, + { "quou", ICLASS_xt_iclass_div, + 0, + Opcode_quou_encode_fns, 0, 0 }, + { "quos", ICLASS_xt_iclass_div, + 0, + Opcode_quos_encode_fns, 0, 0 }, + { "remu", ICLASS_xt_iclass_div, + 0, + Opcode_remu_encode_fns, 0, 0 }, + { "rems", ICLASS_xt_iclass_div, + 0, + Opcode_rems_encode_fns, 0, 0 }, + { "rer", ICLASS_xt_iclass_rer, + 0, + Opcode_rer_encode_fns, 0, 0 }, + { "wer", ICLASS_xt_iclass_wer, + 0, + Opcode_wer_encode_fns, 0, 0 }, + { "rur.ae_ovf_sar", ICLASS_rur_ae_ovf_sar, + 0, + Opcode_rur_ae_ovf_sar_encode_fns, 0, 0 }, + { "wur.ae_ovf_sar", ICLASS_wur_ae_ovf_sar, + 0, + Opcode_wur_ae_ovf_sar_encode_fns, 0, 0 }, + { "rur.ae_bithead", ICLASS_rur_ae_bithead, + 0, + Opcode_rur_ae_bithead_encode_fns, 0, 0 }, + { "wur.ae_bithead", ICLASS_wur_ae_bithead, + 0, + Opcode_wur_ae_bithead_encode_fns, 0, 0 }, + { "rur.ae_ts_fts_bu_bp", ICLASS_rur_ae_ts_fts_bu_bp, + 0, + Opcode_rur_ae_ts_fts_bu_bp_encode_fns, 0, 0 }, + { "wur.ae_ts_fts_bu_bp", ICLASS_wur_ae_ts_fts_bu_bp, + 0, + Opcode_wur_ae_ts_fts_bu_bp_encode_fns, 0, 0 }, + { "rur.ae_sd_no", ICLASS_rur_ae_sd_no, + 0, + Opcode_rur_ae_sd_no_encode_fns, 0, 0 }, + { "wur.ae_sd_no", ICLASS_wur_ae_sd_no, + 0, + Opcode_wur_ae_sd_no_encode_fns, 0, 0 }, + { "rur.ae_overflow", ICLASS_ae_iclass_rur_ae_overflow, + 0, + Opcode_rur_ae_overflow_encode_fns, 0, 0 }, + { "wur.ae_overflow", ICLASS_ae_iclass_wur_ae_overflow, + 0, + Opcode_wur_ae_overflow_encode_fns, 0, 0 }, + { "rur.ae_sar", ICLASS_ae_iclass_rur_ae_sar, + 0, + Opcode_rur_ae_sar_encode_fns, 0, 0 }, + { "wur.ae_sar", ICLASS_ae_iclass_wur_ae_sar, + 0, + Opcode_wur_ae_sar_encode_fns, 0, 0 }, + { "rur.ae_bitptr", ICLASS_ae_iclass_rur_ae_bitptr, + 0, + Opcode_rur_ae_bitptr_encode_fns, 0, 0 }, + { "wur.ae_bitptr", ICLASS_ae_iclass_wur_ae_bitptr, + 0, + Opcode_wur_ae_bitptr_encode_fns, 0, 0 }, + { "rur.ae_bitsused", ICLASS_ae_iclass_rur_ae_bitsused, + 0, + Opcode_rur_ae_bitsused_encode_fns, 0, 0 }, + { "wur.ae_bitsused", ICLASS_ae_iclass_wur_ae_bitsused, + 0, + Opcode_wur_ae_bitsused_encode_fns, 0, 0 }, + { "rur.ae_tablesize", ICLASS_ae_iclass_rur_ae_tablesize, + 0, + Opcode_rur_ae_tablesize_encode_fns, 0, 0 }, + { "wur.ae_tablesize", ICLASS_ae_iclass_wur_ae_tablesize, + 0, + Opcode_wur_ae_tablesize_encode_fns, 0, 0 }, + { "rur.ae_first_ts", ICLASS_ae_iclass_rur_ae_first_ts, + 0, + Opcode_rur_ae_first_ts_encode_fns, 0, 0 }, + { "wur.ae_first_ts", ICLASS_ae_iclass_wur_ae_first_ts, + 0, + Opcode_wur_ae_first_ts_encode_fns, 0, 0 }, + { "rur.ae_nextoffset", ICLASS_ae_iclass_rur_ae_nextoffset, + 0, + Opcode_rur_ae_nextoffset_encode_fns, 0, 0 }, + { "wur.ae_nextoffset", ICLASS_ae_iclass_wur_ae_nextoffset, + 0, + Opcode_wur_ae_nextoffset_encode_fns, 0, 0 }, + { "rur.ae_searchdone", ICLASS_ae_iclass_rur_ae_searchdone, + 0, + Opcode_rur_ae_searchdone_encode_fns, 0, 0 }, + { "wur.ae_searchdone", ICLASS_ae_iclass_wur_ae_searchdone, + 0, + Opcode_wur_ae_searchdone_encode_fns, 0, 0 }, + { "ae_lp16f.i", ICLASS_ae_iclass_lp16f_i, + 0, + Opcode_ae_lp16f_i_encode_fns, 0, 0 }, + { "ae_lp16f.iu", ICLASS_ae_iclass_lp16f_iu, + 0, + Opcode_ae_lp16f_iu_encode_fns, 0, 0 }, + { "ae_lp16f.x", ICLASS_ae_iclass_lp16f_x, + 0, + Opcode_ae_lp16f_x_encode_fns, 0, 0 }, + { "ae_lp16f.xu", ICLASS_ae_iclass_lp16f_xu, + 0, + Opcode_ae_lp16f_xu_encode_fns, 0, 0 }, + { "ae_lp24.i", ICLASS_ae_iclass_lp24_i, + 0, + Opcode_ae_lp24_i_encode_fns, 0, 0 }, + { "ae_lp24.iu", ICLASS_ae_iclass_lp24_iu, + 0, + Opcode_ae_lp24_iu_encode_fns, 0, 0 }, + { "ae_lp24.x", ICLASS_ae_iclass_lp24_x, + 0, + Opcode_ae_lp24_x_encode_fns, 0, 0 }, + { "ae_lp24.xu", ICLASS_ae_iclass_lp24_xu, + 0, + Opcode_ae_lp24_xu_encode_fns, 0, 0 }, + { "ae_lp24f.i", ICLASS_ae_iclass_lp24f_i, + 0, + Opcode_ae_lp24f_i_encode_fns, 0, 0 }, + { "ae_lp24f.iu", ICLASS_ae_iclass_lp24f_iu, + 0, + Opcode_ae_lp24f_iu_encode_fns, 0, 0 }, + { "ae_lp24f.x", ICLASS_ae_iclass_lp24f_x, + 0, + Opcode_ae_lp24f_x_encode_fns, 0, 0 }, + { "ae_lp24f.xu", ICLASS_ae_iclass_lp24f_xu, + 0, + Opcode_ae_lp24f_xu_encode_fns, 0, 0 }, + { "ae_lp16x2f.i", ICLASS_ae_iclass_lp16x2f_i, + 0, + Opcode_ae_lp16x2f_i_encode_fns, 0, 0 }, + { "ae_lp16x2f.iu", ICLASS_ae_iclass_lp16x2f_iu, + 0, + Opcode_ae_lp16x2f_iu_encode_fns, 0, 0 }, + { "ae_lp16x2f.x", ICLASS_ae_iclass_lp16x2f_x, + 0, + Opcode_ae_lp16x2f_x_encode_fns, 0, 0 }, + { "ae_lp16x2f.xu", ICLASS_ae_iclass_lp16x2f_xu, + 0, + Opcode_ae_lp16x2f_xu_encode_fns, 0, 0 }, + { "ae_lp24x2f.i", ICLASS_ae_iclass_lp24x2f_i, + 0, + Opcode_ae_lp24x2f_i_encode_fns, 0, 0 }, + { "ae_lp24x2f.iu", ICLASS_ae_iclass_lp24x2f_iu, + 0, + Opcode_ae_lp24x2f_iu_encode_fns, 0, 0 }, + { "ae_lp24x2f.x", ICLASS_ae_iclass_lp24x2f_x, + 0, + Opcode_ae_lp24x2f_x_encode_fns, 0, 0 }, + { "ae_lp24x2f.xu", ICLASS_ae_iclass_lp24x2f_xu, + 0, + Opcode_ae_lp24x2f_xu_encode_fns, 0, 0 }, + { "ae_lp24x2.i", ICLASS_ae_iclass_lp24x2_i, + 0, + Opcode_ae_lp24x2_i_encode_fns, 0, 0 }, + { "ae_lp24x2.iu", ICLASS_ae_iclass_lp24x2_iu, + 0, + Opcode_ae_lp24x2_iu_encode_fns, 0, 0 }, + { "ae_lp24x2.x", ICLASS_ae_iclass_lp24x2_x, + 0, + Opcode_ae_lp24x2_x_encode_fns, 0, 0 }, + { "ae_lp24x2.xu", ICLASS_ae_iclass_lp24x2_xu, + 0, + Opcode_ae_lp24x2_xu_encode_fns, 0, 0 }, + { "ae_sp16x2f.i", ICLASS_ae_iclass_sp16x2f_i, + 0, + Opcode_ae_sp16x2f_i_encode_fns, 0, 0 }, + { "ae_sp16x2f.iu", ICLASS_ae_iclass_sp16x2f_iu, + 0, + Opcode_ae_sp16x2f_iu_encode_fns, 0, 0 }, + { "ae_sp16x2f.x", ICLASS_ae_iclass_sp16x2f_x, + 0, + Opcode_ae_sp16x2f_x_encode_fns, 0, 0 }, + { "ae_sp16x2f.xu", ICLASS_ae_iclass_sp16x2f_xu, + 0, + Opcode_ae_sp16x2f_xu_encode_fns, 0, 0 }, + { "ae_sp24x2s.i", ICLASS_ae_iclass_sp24x2s_i, + 0, + Opcode_ae_sp24x2s_i_encode_fns, 0, 0 }, + { "ae_sp24x2s.iu", ICLASS_ae_iclass_sp24x2s_iu, + 0, + Opcode_ae_sp24x2s_iu_encode_fns, 0, 0 }, + { "ae_sp24x2s.x", ICLASS_ae_iclass_sp24x2s_x, + 0, + Opcode_ae_sp24x2s_x_encode_fns, 0, 0 }, + { "ae_sp24x2s.xu", ICLASS_ae_iclass_sp24x2s_xu, + 0, + Opcode_ae_sp24x2s_xu_encode_fns, 0, 0 }, + { "ae_sp24x2f.i", ICLASS_ae_iclass_sp24x2f_i, + 0, + Opcode_ae_sp24x2f_i_encode_fns, 0, 0 }, + { "ae_sp24x2f.iu", ICLASS_ae_iclass_sp24x2f_iu, + 0, + Opcode_ae_sp24x2f_iu_encode_fns, 0, 0 }, + { "ae_sp24x2f.x", ICLASS_ae_iclass_sp24x2f_x, + 0, + Opcode_ae_sp24x2f_x_encode_fns, 0, 0 }, + { "ae_sp24x2f.xu", ICLASS_ae_iclass_sp24x2f_xu, + 0, + Opcode_ae_sp24x2f_xu_encode_fns, 0, 0 }, + { "ae_sp16f.l.i", ICLASS_ae_iclass_sp16f_l_i, + 0, + Opcode_ae_sp16f_l_i_encode_fns, 0, 0 }, + { "ae_sp16f.l.iu", ICLASS_ae_iclass_sp16f_l_iu, + 0, + Opcode_ae_sp16f_l_iu_encode_fns, 0, 0 }, + { "ae_sp16f.l.x", ICLASS_ae_iclass_sp16f_l_x, + 0, + Opcode_ae_sp16f_l_x_encode_fns, 0, 0 }, + { "ae_sp16f.l.xu", ICLASS_ae_iclass_sp16f_l_xu, + 0, + Opcode_ae_sp16f_l_xu_encode_fns, 0, 0 }, + { "ae_sp24s.l.i", ICLASS_ae_iclass_sp24s_l_i, + 0, + Opcode_ae_sp24s_l_i_encode_fns, 0, 0 }, + { "ae_sp24s.l.iu", ICLASS_ae_iclass_sp24s_l_iu, + 0, + Opcode_ae_sp24s_l_iu_encode_fns, 0, 0 }, + { "ae_sp24s.l.x", ICLASS_ae_iclass_sp24s_l_x, + 0, + Opcode_ae_sp24s_l_x_encode_fns, 0, 0 }, + { "ae_sp24s.l.xu", ICLASS_ae_iclass_sp24s_l_xu, + 0, + Opcode_ae_sp24s_l_xu_encode_fns, 0, 0 }, + { "ae_sp24f.l.i", ICLASS_ae_iclass_sp24f_l_i, + 0, + Opcode_ae_sp24f_l_i_encode_fns, 0, 0 }, + { "ae_sp24f.l.iu", ICLASS_ae_iclass_sp24f_l_iu, + 0, + Opcode_ae_sp24f_l_iu_encode_fns, 0, 0 }, + { "ae_sp24f.l.x", ICLASS_ae_iclass_sp24f_l_x, + 0, + Opcode_ae_sp24f_l_x_encode_fns, 0, 0 }, + { "ae_sp24f.l.xu", ICLASS_ae_iclass_sp24f_l_xu, + 0, + Opcode_ae_sp24f_l_xu_encode_fns, 0, 0 }, + { "ae_lq56.i", ICLASS_ae_iclass_lq56_i, + 0, + Opcode_ae_lq56_i_encode_fns, 0, 0 }, + { "ae_lq56.iu", ICLASS_ae_iclass_lq56_iu, + 0, + Opcode_ae_lq56_iu_encode_fns, 0, 0 }, + { "ae_lq56.x", ICLASS_ae_iclass_lq56_x, + 0, + Opcode_ae_lq56_x_encode_fns, 0, 0 }, + { "ae_lq56.xu", ICLASS_ae_iclass_lq56_xu, + 0, + Opcode_ae_lq56_xu_encode_fns, 0, 0 }, + { "ae_lq32f.i", ICLASS_ae_iclass_lq32f_i, + 0, + Opcode_ae_lq32f_i_encode_fns, 0, 0 }, + { "ae_lq32f.iu", ICLASS_ae_iclass_lq32f_iu, + 0, + Opcode_ae_lq32f_iu_encode_fns, 0, 0 }, + { "ae_lq32f.x", ICLASS_ae_iclass_lq32f_x, + 0, + Opcode_ae_lq32f_x_encode_fns, 0, 0 }, + { "ae_lq32f.xu", ICLASS_ae_iclass_lq32f_xu, + 0, + Opcode_ae_lq32f_xu_encode_fns, 0, 0 }, + { "ae_sq56s.i", ICLASS_ae_iclass_sq56s_i, + 0, + Opcode_ae_sq56s_i_encode_fns, 0, 0 }, + { "ae_sq56s.iu", ICLASS_ae_iclass_sq56s_iu, + 0, + Opcode_ae_sq56s_iu_encode_fns, 0, 0 }, + { "ae_sq56s.x", ICLASS_ae_iclass_sq56s_x, + 0, + Opcode_ae_sq56s_x_encode_fns, 0, 0 }, + { "ae_sq56s.xu", ICLASS_ae_iclass_sq56s_xu, + 0, + Opcode_ae_sq56s_xu_encode_fns, 0, 0 }, + { "ae_sq32f.i", ICLASS_ae_iclass_sq32f_i, + 0, + Opcode_ae_sq32f_i_encode_fns, 0, 0 }, + { "ae_sq32f.iu", ICLASS_ae_iclass_sq32f_iu, + 0, + Opcode_ae_sq32f_iu_encode_fns, 0, 0 }, + { "ae_sq32f.x", ICLASS_ae_iclass_sq32f_x, + 0, + Opcode_ae_sq32f_x_encode_fns, 0, 0 }, + { "ae_sq32f.xu", ICLASS_ae_iclass_sq32f_xu, + 0, + Opcode_ae_sq32f_xu_encode_fns, 0, 0 }, + { "ae_zerop48", ICLASS_ae_iclass_zerop48, + 0, + Opcode_ae_zerop48_encode_fns, 0, 0 }, + { "ae_movp48", ICLASS_ae_iclass_movp48, + 0, + Opcode_ae_movp48_encode_fns, 0, 0 }, + { "ae_selp24.ll", ICLASS_ae_iclass_selp24_ll, + 0, + Opcode_ae_selp24_ll_encode_fns, 0, 0 }, + { "ae_selp24.lh", ICLASS_ae_iclass_selp24_lh, + 0, + Opcode_ae_selp24_lh_encode_fns, 0, 0 }, + { "ae_selp24.hl", ICLASS_ae_iclass_selp24_hl, + 0, + Opcode_ae_selp24_hl_encode_fns, 0, 0 }, + { "ae_selp24.hh", ICLASS_ae_iclass_selp24_hh, + 0, + Opcode_ae_selp24_hh_encode_fns, 0, 0 }, + { "ae_movtp24x2", ICLASS_ae_iclass_movtp24x2, + 0, + Opcode_ae_movtp24x2_encode_fns, 0, 0 }, + { "ae_movfp24x2", ICLASS_ae_iclass_movfp24x2, + 0, + Opcode_ae_movfp24x2_encode_fns, 0, 0 }, + { "ae_movtp48", ICLASS_ae_iclass_movtp48, + 0, + Opcode_ae_movtp48_encode_fns, 0, 0 }, + { "ae_movfp48", ICLASS_ae_iclass_movfp48, + 0, + Opcode_ae_movfp48_encode_fns, 0, 0 }, + { "ae_movpa24x2", ICLASS_ae_iclass_movpa24x2, + 0, + Opcode_ae_movpa24x2_encode_fns, 0, 0 }, + { "ae_truncp24a32x2", ICLASS_ae_iclass_truncp24a32x2, + 0, + Opcode_ae_truncp24a32x2_encode_fns, 0, 0 }, + { "ae_cvta32p24.l", ICLASS_ae_iclass_cvta32p24_l, + 0, + Opcode_ae_cvta32p24_l_encode_fns, 0, 0 }, + { "ae_cvta32p24.h", ICLASS_ae_iclass_cvta32p24_h, + 0, + Opcode_ae_cvta32p24_h_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.ll", ICLASS_ae_iclass_cvtp24a16x2_ll, + 0, + Opcode_ae_cvtp24a16x2_ll_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.lh", ICLASS_ae_iclass_cvtp24a16x2_lh, + 0, + Opcode_ae_cvtp24a16x2_lh_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.hl", ICLASS_ae_iclass_cvtp24a16x2_hl, + 0, + Opcode_ae_cvtp24a16x2_hl_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.hh", ICLASS_ae_iclass_cvtp24a16x2_hh, + 0, + Opcode_ae_cvtp24a16x2_hh_encode_fns, 0, 0 }, + { "ae_truncp24q48x2", ICLASS_ae_iclass_truncp24q48x2, + 0, + Opcode_ae_truncp24q48x2_encode_fns, 0, 0 }, + { "ae_truncp16", ICLASS_ae_iclass_truncp16, + 0, + Opcode_ae_truncp16_encode_fns, 0, 0 }, + { "ae_roundsp24q48sym", ICLASS_ae_iclass_roundsp24q48sym, + 0, + Opcode_ae_roundsp24q48sym_encode_fns, 0, 0 }, + { "ae_roundsp24q48asym", ICLASS_ae_iclass_roundsp24q48asym, + 0, + Opcode_ae_roundsp24q48asym_encode_fns, 0, 0 }, + { "ae_roundsp16q48sym", ICLASS_ae_iclass_roundsp16q48sym, + 0, + Opcode_ae_roundsp16q48sym_encode_fns, 0, 0 }, + { "ae_roundsp16q48asym", ICLASS_ae_iclass_roundsp16q48asym, + 0, + Opcode_ae_roundsp16q48asym_encode_fns, 0, 0 }, + { "ae_roundsp16sym", ICLASS_ae_iclass_roundsp16sym, + 0, + Opcode_ae_roundsp16sym_encode_fns, 0, 0 }, + { "ae_roundsp16asym", ICLASS_ae_iclass_roundsp16asym, + 0, + Opcode_ae_roundsp16asym_encode_fns, 0, 0 }, + { "ae_zeroq56", ICLASS_ae_iclass_zeroq56, + 0, + Opcode_ae_zeroq56_encode_fns, 0, 0 }, + { "ae_movq56", ICLASS_ae_iclass_movq56, + 0, + Opcode_ae_movq56_encode_fns, 0, 0 }, + { "ae_movtq56", ICLASS_ae_iclass_movtq56, + 0, + Opcode_ae_movtq56_encode_fns, 0, 0 }, + { "ae_movfq56", ICLASS_ae_iclass_movfq56, + 0, + Opcode_ae_movfq56_encode_fns, 0, 0 }, + { "ae_cvtq48a32s", ICLASS_ae_iclass_cvtq48a32s, + 0, + Opcode_ae_cvtq48a32s_encode_fns, 0, 0 }, + { "ae_cvtq48p24s.l", ICLASS_ae_iclass_cvtq48p24s_l, + 0, + Opcode_ae_cvtq48p24s_l_encode_fns, 0, 0 }, + { "ae_cvtq48p24s.h", ICLASS_ae_iclass_cvtq48p24s_h, + 0, + Opcode_ae_cvtq48p24s_h_encode_fns, 0, 0 }, + { "ae_satq48s", ICLASS_ae_iclass_satq48s, + 0, + Opcode_ae_satq48s_encode_fns, 0, 0 }, + { "ae_truncq32", ICLASS_ae_iclass_truncq32, + 0, + Opcode_ae_truncq32_encode_fns, 0, 0 }, + { "ae_roundsq32sym", ICLASS_ae_iclass_roundsq32sym, + 0, + Opcode_ae_roundsq32sym_encode_fns, 0, 0 }, + { "ae_roundsq32asym", ICLASS_ae_iclass_roundsq32asym, + 0, + Opcode_ae_roundsq32asym_encode_fns, 0, 0 }, + { "ae_trunca32q48", ICLASS_ae_iclass_trunca32q48, + 0, + Opcode_ae_trunca32q48_encode_fns, 0, 0 }, + { "ae_movap24s.l", ICLASS_ae_iclass_movap24s_l, + 0, + Opcode_ae_movap24s_l_encode_fns, 0, 0 }, + { "ae_movap24s.h", ICLASS_ae_iclass_movap24s_h, + 0, + Opcode_ae_movap24s_h_encode_fns, 0, 0 }, + { "ae_trunca16p24s.l", ICLASS_ae_iclass_trunca16p24s_l, + 0, + Opcode_ae_trunca16p24s_l_encode_fns, 0, 0 }, + { "ae_trunca16p24s.h", ICLASS_ae_iclass_trunca16p24s_h, + 0, + Opcode_ae_trunca16p24s_h_encode_fns, 0, 0 }, + { "ae_addp24", ICLASS_ae_iclass_addp24, + 0, + Opcode_ae_addp24_encode_fns, 0, 0 }, + { "ae_subp24", ICLASS_ae_iclass_subp24, + 0, + Opcode_ae_subp24_encode_fns, 0, 0 }, + { "ae_negp24", ICLASS_ae_iclass_negp24, + 0, + Opcode_ae_negp24_encode_fns, 0, 0 }, + { "ae_absp24", ICLASS_ae_iclass_absp24, + 0, + Opcode_ae_absp24_encode_fns, 0, 0 }, + { "ae_maxp24s", ICLASS_ae_iclass_maxp24s, + 0, + Opcode_ae_maxp24s_encode_fns, 0, 0 }, + { "ae_minp24s", ICLASS_ae_iclass_minp24s, + 0, + Opcode_ae_minp24s_encode_fns, 0, 0 }, + { "ae_maxbp24s", ICLASS_ae_iclass_maxbp24s, + 0, + Opcode_ae_maxbp24s_encode_fns, 0, 0 }, + { "ae_minbp24s", ICLASS_ae_iclass_minbp24s, + 0, + Opcode_ae_minbp24s_encode_fns, 0, 0 }, + { "ae_addsp24s", ICLASS_ae_iclass_addsp24s, + 0, + Opcode_ae_addsp24s_encode_fns, 0, 0 }, + { "ae_subsp24s", ICLASS_ae_iclass_subsp24s, + 0, + Opcode_ae_subsp24s_encode_fns, 0, 0 }, + { "ae_negsp24s", ICLASS_ae_iclass_negsp24s, + 0, + Opcode_ae_negsp24s_encode_fns, 0, 0 }, + { "ae_abssp24s", ICLASS_ae_iclass_abssp24s, + 0, + Opcode_ae_abssp24s_encode_fns, 0, 0 }, + { "ae_andp48", ICLASS_ae_iclass_andp48, + 0, + Opcode_ae_andp48_encode_fns, 0, 0 }, + { "ae_nandp48", ICLASS_ae_iclass_nandp48, + 0, + Opcode_ae_nandp48_encode_fns, 0, 0 }, + { "ae_orp48", ICLASS_ae_iclass_orp48, + 0, + Opcode_ae_orp48_encode_fns, 0, 0 }, + { "ae_xorp48", ICLASS_ae_iclass_xorp48, + 0, + Opcode_ae_xorp48_encode_fns, 0, 0 }, + { "ae_ltp24s", ICLASS_ae_iclass_ltp24s, + 0, + Opcode_ae_ltp24s_encode_fns, 0, 0 }, + { "ae_lep24s", ICLASS_ae_iclass_lep24s, + 0, + Opcode_ae_lep24s_encode_fns, 0, 0 }, + { "ae_eqp24", ICLASS_ae_iclass_eqp24, + 0, + Opcode_ae_eqp24_encode_fns, 0, 0 }, + { "ae_addq56", ICLASS_ae_iclass_addq56, + 0, + Opcode_ae_addq56_encode_fns, 0, 0 }, + { "ae_subq56", ICLASS_ae_iclass_subq56, + 0, + Opcode_ae_subq56_encode_fns, 0, 0 }, + { "ae_negq56", ICLASS_ae_iclass_negq56, + 0, + Opcode_ae_negq56_encode_fns, 0, 0 }, + { "ae_absq56", ICLASS_ae_iclass_absq56, + 0, + Opcode_ae_absq56_encode_fns, 0, 0 }, + { "ae_maxq56s", ICLASS_ae_iclass_maxq56s, + 0, + Opcode_ae_maxq56s_encode_fns, 0, 0 }, + { "ae_minq56s", ICLASS_ae_iclass_minq56s, + 0, + Opcode_ae_minq56s_encode_fns, 0, 0 }, + { "ae_maxbq56s", ICLASS_ae_iclass_maxbq56s, + 0, + Opcode_ae_maxbq56s_encode_fns, 0, 0 }, + { "ae_minbq56s", ICLASS_ae_iclass_minbq56s, + 0, + Opcode_ae_minbq56s_encode_fns, 0, 0 }, + { "ae_addsq56s", ICLASS_ae_iclass_addsq56s, + 0, + Opcode_ae_addsq56s_encode_fns, 0, 0 }, + { "ae_subsq56s", ICLASS_ae_iclass_subsq56s, + 0, + Opcode_ae_subsq56s_encode_fns, 0, 0 }, + { "ae_negsq56s", ICLASS_ae_iclass_negsq56s, + 0, + Opcode_ae_negsq56s_encode_fns, 0, 0 }, + { "ae_abssq56s", ICLASS_ae_iclass_abssq56s, + 0, + Opcode_ae_abssq56s_encode_fns, 0, 0 }, + { "ae_andq56", ICLASS_ae_iclass_andq56, + 0, + Opcode_ae_andq56_encode_fns, 0, 0 }, + { "ae_nandq56", ICLASS_ae_iclass_nandq56, + 0, + Opcode_ae_nandq56_encode_fns, 0, 0 }, + { "ae_orq56", ICLASS_ae_iclass_orq56, + 0, + Opcode_ae_orq56_encode_fns, 0, 0 }, + { "ae_xorq56", ICLASS_ae_iclass_xorq56, + 0, + Opcode_ae_xorq56_encode_fns, 0, 0 }, + { "ae_sllip24", ICLASS_ae_iclass_sllip24, + 0, + Opcode_ae_sllip24_encode_fns, 0, 0 }, + { "ae_srlip24", ICLASS_ae_iclass_srlip24, + 0, + Opcode_ae_srlip24_encode_fns, 0, 0 }, + { "ae_sraip24", ICLASS_ae_iclass_sraip24, + 0, + Opcode_ae_sraip24_encode_fns, 0, 0 }, + { "ae_sllsp24", ICLASS_ae_iclass_sllsp24, + 0, + Opcode_ae_sllsp24_encode_fns, 0, 0 }, + { "ae_srlsp24", ICLASS_ae_iclass_srlsp24, + 0, + Opcode_ae_srlsp24_encode_fns, 0, 0 }, + { "ae_srasp24", ICLASS_ae_iclass_srasp24, + 0, + Opcode_ae_srasp24_encode_fns, 0, 0 }, + { "ae_sllisp24s", ICLASS_ae_iclass_sllisp24s, + 0, + Opcode_ae_sllisp24s_encode_fns, 0, 0 }, + { "ae_sllssp24s", ICLASS_ae_iclass_sllssp24s, + 0, + Opcode_ae_sllssp24s_encode_fns, 0, 0 }, + { "ae_slliq56", ICLASS_ae_iclass_slliq56, + 0, + Opcode_ae_slliq56_encode_fns, 0, 0 }, + { "ae_srliq56", ICLASS_ae_iclass_srliq56, + 0, + Opcode_ae_srliq56_encode_fns, 0, 0 }, + { "ae_sraiq56", ICLASS_ae_iclass_sraiq56, + 0, + Opcode_ae_sraiq56_encode_fns, 0, 0 }, + { "ae_sllsq56", ICLASS_ae_iclass_sllsq56, + 0, + Opcode_ae_sllsq56_encode_fns, 0, 0 }, + { "ae_srlsq56", ICLASS_ae_iclass_srlsq56, + 0, + Opcode_ae_srlsq56_encode_fns, 0, 0 }, + { "ae_srasq56", ICLASS_ae_iclass_srasq56, + 0, + Opcode_ae_srasq56_encode_fns, 0, 0 }, + { "ae_sllaq56", ICLASS_ae_iclass_sllaq56, + 0, + Opcode_ae_sllaq56_encode_fns, 0, 0 }, + { "ae_srlaq56", ICLASS_ae_iclass_srlaq56, + 0, + Opcode_ae_srlaq56_encode_fns, 0, 0 }, + { "ae_sraaq56", ICLASS_ae_iclass_sraaq56, + 0, + Opcode_ae_sraaq56_encode_fns, 0, 0 }, + { "ae_sllisq56s", ICLASS_ae_iclass_sllisq56s, + 0, + Opcode_ae_sllisq56s_encode_fns, 0, 0 }, + { "ae_sllssq56s", ICLASS_ae_iclass_sllssq56s, + 0, + Opcode_ae_sllssq56s_encode_fns, 0, 0 }, + { "ae_sllasq56s", ICLASS_ae_iclass_sllasq56s, + 0, + Opcode_ae_sllasq56s_encode_fns, 0, 0 }, + { "ae_ltq56s", ICLASS_ae_iclass_ltq56s, + 0, + Opcode_ae_ltq56s_encode_fns, 0, 0 }, + { "ae_leq56s", ICLASS_ae_iclass_leq56s, + 0, + Opcode_ae_leq56s_encode_fns, 0, 0 }, + { "ae_eqq56", ICLASS_ae_iclass_eqq56, + 0, + Opcode_ae_eqq56_encode_fns, 0, 0 }, + { "ae_nsaq56s", ICLASS_ae_iclass_nsaq56s, + 0, + Opcode_ae_nsaq56s_encode_fns, 0, 0 }, + { "ae_mulsrfq32sp24s.h", ICLASS_ae_iclass_mulsrfq32sp24s_h, + 0, + Opcode_ae_mulsrfq32sp24s_h_encode_fns, 0, 0 }, + { "ae_mulsrfq32sp24s.l", ICLASS_ae_iclass_mulsrfq32sp24s_l, + 0, + Opcode_ae_mulsrfq32sp24s_l_encode_fns, 0, 0 }, + { "ae_mularfq32sp24s.h", ICLASS_ae_iclass_mularfq32sp24s_h, + 0, + Opcode_ae_mularfq32sp24s_h_encode_fns, 0, 0 }, + { "ae_mularfq32sp24s.l", ICLASS_ae_iclass_mularfq32sp24s_l, + 0, + Opcode_ae_mularfq32sp24s_l_encode_fns, 0, 0 }, + { "ae_mulrfq32sp24s.h", ICLASS_ae_iclass_mulrfq32sp24s_h, + 0, + Opcode_ae_mulrfq32sp24s_h_encode_fns, 0, 0 }, + { "ae_mulrfq32sp24s.l", ICLASS_ae_iclass_mulrfq32sp24s_l, + 0, + Opcode_ae_mulrfq32sp24s_l_encode_fns, 0, 0 }, + { "ae_mulsfq32sp24s.h", ICLASS_ae_iclass_mulsfq32sp24s_h, + 0, + Opcode_ae_mulsfq32sp24s_h_encode_fns, 0, 0 }, + { "ae_mulsfq32sp24s.l", ICLASS_ae_iclass_mulsfq32sp24s_l, + 0, + Opcode_ae_mulsfq32sp24s_l_encode_fns, 0, 0 }, + { "ae_mulafq32sp24s.h", ICLASS_ae_iclass_mulafq32sp24s_h, + 0, + Opcode_ae_mulafq32sp24s_h_encode_fns, 0, 0 }, + { "ae_mulafq32sp24s.l", ICLASS_ae_iclass_mulafq32sp24s_l, + 0, + Opcode_ae_mulafq32sp24s_l_encode_fns, 0, 0 }, + { "ae_mulfq32sp24s.h", ICLASS_ae_iclass_mulfq32sp24s_h, + 0, + Opcode_ae_mulfq32sp24s_h_encode_fns, 0, 0 }, + { "ae_mulfq32sp24s.l", ICLASS_ae_iclass_mulfq32sp24s_l, + 0, + Opcode_ae_mulfq32sp24s_l_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.ll", ICLASS_ae_iclass_mulfs32p16s_ll, + 0, + Opcode_ae_mulfs32p16s_ll_encode_fns, 0, 0 }, + { "ae_mulfp24s.ll", ICLASS_ae_iclass_mulfp24s_ll, + 0, + Opcode_ae_mulfp24s_ll_encode_fns, 0, 0 }, + { "ae_mulp24s.ll", ICLASS_ae_iclass_mulp24s_ll, + 0, + Opcode_ae_mulp24s_ll_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.lh", ICLASS_ae_iclass_mulfs32p16s_lh, + 0, + Opcode_ae_mulfs32p16s_lh_encode_fns, 0, 0 }, + { "ae_mulfp24s.lh", ICLASS_ae_iclass_mulfp24s_lh, + 0, + Opcode_ae_mulfp24s_lh_encode_fns, 0, 0 }, + { "ae_mulp24s.lh", ICLASS_ae_iclass_mulp24s_lh, + 0, + Opcode_ae_mulp24s_lh_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.hl", ICLASS_ae_iclass_mulfs32p16s_hl, + 0, + Opcode_ae_mulfs32p16s_hl_encode_fns, 0, 0 }, + { "ae_mulfp24s.hl", ICLASS_ae_iclass_mulfp24s_hl, + 0, + Opcode_ae_mulfp24s_hl_encode_fns, 0, 0 }, + { "ae_mulp24s.hl", ICLASS_ae_iclass_mulp24s_hl, + 0, + Opcode_ae_mulp24s_hl_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.hh", ICLASS_ae_iclass_mulfs32p16s_hh, + 0, + Opcode_ae_mulfs32p16s_hh_encode_fns, 0, 0 }, + { "ae_mulfp24s.hh", ICLASS_ae_iclass_mulfp24s_hh, + 0, + Opcode_ae_mulfp24s_hh_encode_fns, 0, 0 }, + { "ae_mulp24s.hh", ICLASS_ae_iclass_mulp24s_hh, + 0, + Opcode_ae_mulp24s_hh_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.ll", ICLASS_ae_iclass_mulafs32p16s_ll, + 0, + Opcode_ae_mulafs32p16s_ll_encode_fns, 0, 0 }, + { "ae_mulafp24s.ll", ICLASS_ae_iclass_mulafp24s_ll, + 0, + Opcode_ae_mulafp24s_ll_encode_fns, 0, 0 }, + { "ae_mulap24s.ll", ICLASS_ae_iclass_mulap24s_ll, + 0, + Opcode_ae_mulap24s_ll_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.lh", ICLASS_ae_iclass_mulafs32p16s_lh, + 0, + Opcode_ae_mulafs32p16s_lh_encode_fns, 0, 0 }, + { "ae_mulafp24s.lh", ICLASS_ae_iclass_mulafp24s_lh, + 0, + Opcode_ae_mulafp24s_lh_encode_fns, 0, 0 }, + { "ae_mulap24s.lh", ICLASS_ae_iclass_mulap24s_lh, + 0, + Opcode_ae_mulap24s_lh_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.hl", ICLASS_ae_iclass_mulafs32p16s_hl, + 0, + Opcode_ae_mulafs32p16s_hl_encode_fns, 0, 0 }, + { "ae_mulafp24s.hl", ICLASS_ae_iclass_mulafp24s_hl, + 0, + Opcode_ae_mulafp24s_hl_encode_fns, 0, 0 }, + { "ae_mulap24s.hl", ICLASS_ae_iclass_mulap24s_hl, + 0, + Opcode_ae_mulap24s_hl_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.hh", ICLASS_ae_iclass_mulafs32p16s_hh, + 0, + Opcode_ae_mulafs32p16s_hh_encode_fns, 0, 0 }, + { "ae_mulafp24s.hh", ICLASS_ae_iclass_mulafp24s_hh, + 0, + Opcode_ae_mulafp24s_hh_encode_fns, 0, 0 }, + { "ae_mulap24s.hh", ICLASS_ae_iclass_mulap24s_hh, + 0, + Opcode_ae_mulap24s_hh_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.ll", ICLASS_ae_iclass_mulsfs32p16s_ll, + 0, + Opcode_ae_mulsfs32p16s_ll_encode_fns, 0, 0 }, + { "ae_mulsfp24s.ll", ICLASS_ae_iclass_mulsfp24s_ll, + 0, + Opcode_ae_mulsfp24s_ll_encode_fns, 0, 0 }, + { "ae_mulsp24s.ll", ICLASS_ae_iclass_mulsp24s_ll, + 0, + Opcode_ae_mulsp24s_ll_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.lh", ICLASS_ae_iclass_mulsfs32p16s_lh, + 0, + Opcode_ae_mulsfs32p16s_lh_encode_fns, 0, 0 }, + { "ae_mulsfp24s.lh", ICLASS_ae_iclass_mulsfp24s_lh, + 0, + Opcode_ae_mulsfp24s_lh_encode_fns, 0, 0 }, + { "ae_mulsp24s.lh", ICLASS_ae_iclass_mulsp24s_lh, + 0, + Opcode_ae_mulsp24s_lh_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.hl", ICLASS_ae_iclass_mulsfs32p16s_hl, + 0, + Opcode_ae_mulsfs32p16s_hl_encode_fns, 0, 0 }, + { "ae_mulsfp24s.hl", ICLASS_ae_iclass_mulsfp24s_hl, + 0, + Opcode_ae_mulsfp24s_hl_encode_fns, 0, 0 }, + { "ae_mulsp24s.hl", ICLASS_ae_iclass_mulsp24s_hl, + 0, + Opcode_ae_mulsp24s_hl_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.hh", ICLASS_ae_iclass_mulsfs32p16s_hh, + 0, + Opcode_ae_mulsfs32p16s_hh_encode_fns, 0, 0 }, + { "ae_mulsfp24s.hh", ICLASS_ae_iclass_mulsfp24s_hh, + 0, + Opcode_ae_mulsfp24s_hh_encode_fns, 0, 0 }, + { "ae_mulsp24s.hh", ICLASS_ae_iclass_mulsp24s_hh, + 0, + Opcode_ae_mulsp24s_hh_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.ll", ICLASS_ae_iclass_mulafs56p24s_ll, + 0, + Opcode_ae_mulafs56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulas56p24s.ll", ICLASS_ae_iclass_mulas56p24s_ll, + 0, + Opcode_ae_mulas56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.lh", ICLASS_ae_iclass_mulafs56p24s_lh, + 0, + Opcode_ae_mulafs56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulas56p24s.lh", ICLASS_ae_iclass_mulas56p24s_lh, + 0, + Opcode_ae_mulas56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.hl", ICLASS_ae_iclass_mulafs56p24s_hl, + 0, + Opcode_ae_mulafs56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulas56p24s.hl", ICLASS_ae_iclass_mulas56p24s_hl, + 0, + Opcode_ae_mulas56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.hh", ICLASS_ae_iclass_mulafs56p24s_hh, + 0, + Opcode_ae_mulafs56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulas56p24s.hh", ICLASS_ae_iclass_mulas56p24s_hh, + 0, + Opcode_ae_mulas56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.ll", ICLASS_ae_iclass_mulsfs56p24s_ll, + 0, + Opcode_ae_mulsfs56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulss56p24s.ll", ICLASS_ae_iclass_mulss56p24s_ll, + 0, + Opcode_ae_mulss56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.lh", ICLASS_ae_iclass_mulsfs56p24s_lh, + 0, + Opcode_ae_mulsfs56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulss56p24s.lh", ICLASS_ae_iclass_mulss56p24s_lh, + 0, + Opcode_ae_mulss56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.hl", ICLASS_ae_iclass_mulsfs56p24s_hl, + 0, + Opcode_ae_mulsfs56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulss56p24s.hl", ICLASS_ae_iclass_mulss56p24s_hl, + 0, + Opcode_ae_mulss56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.hh", ICLASS_ae_iclass_mulsfs56p24s_hh, + 0, + Opcode_ae_mulsfs56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulss56p24s.hh", ICLASS_ae_iclass_mulss56p24s_hh, + 0, + Opcode_ae_mulss56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulfq32sp16s.l", ICLASS_ae_iclass_mulfq32sp16s_l, + 0, + Opcode_ae_mulfq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulfq32sp16s.h", ICLASS_ae_iclass_mulfq32sp16s_h, + 0, + Opcode_ae_mulfq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulfq32sp16u.l", ICLASS_ae_iclass_mulfq32sp16u_l, + 0, + Opcode_ae_mulfq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulfq32sp16u.h", ICLASS_ae_iclass_mulfq32sp16u_h, + 0, + Opcode_ae_mulfq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulq32sp16s.l", ICLASS_ae_iclass_mulq32sp16s_l, + 0, + Opcode_ae_mulq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulq32sp16s.h", ICLASS_ae_iclass_mulq32sp16s_h, + 0, + Opcode_ae_mulq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulq32sp16u.l", ICLASS_ae_iclass_mulq32sp16u_l, + 0, + Opcode_ae_mulq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulq32sp16u.h", ICLASS_ae_iclass_mulq32sp16u_h, + 0, + Opcode_ae_mulq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulafq32sp16s.l", ICLASS_ae_iclass_mulafq32sp16s_l, + 0, + Opcode_ae_mulafq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulafq32sp16s.h", ICLASS_ae_iclass_mulafq32sp16s_h, + 0, + Opcode_ae_mulafq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulafq32sp16u.l", ICLASS_ae_iclass_mulafq32sp16u_l, + 0, + Opcode_ae_mulafq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulafq32sp16u.h", ICLASS_ae_iclass_mulafq32sp16u_h, + 0, + Opcode_ae_mulafq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulaq32sp16s.l", ICLASS_ae_iclass_mulaq32sp16s_l, + 0, + Opcode_ae_mulaq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulaq32sp16s.h", ICLASS_ae_iclass_mulaq32sp16s_h, + 0, + Opcode_ae_mulaq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulaq32sp16u.l", ICLASS_ae_iclass_mulaq32sp16u_l, + 0, + Opcode_ae_mulaq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulaq32sp16u.h", ICLASS_ae_iclass_mulaq32sp16u_h, + 0, + Opcode_ae_mulaq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16s.l", ICLASS_ae_iclass_mulsfq32sp16s_l, + 0, + Opcode_ae_mulsfq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16s.h", ICLASS_ae_iclass_mulsfq32sp16s_h, + 0, + Opcode_ae_mulsfq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16u.l", ICLASS_ae_iclass_mulsfq32sp16u_l, + 0, + Opcode_ae_mulsfq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16u.h", ICLASS_ae_iclass_mulsfq32sp16u_h, + 0, + Opcode_ae_mulsfq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulsq32sp16s.l", ICLASS_ae_iclass_mulsq32sp16s_l, + 0, + Opcode_ae_mulsq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulsq32sp16s.h", ICLASS_ae_iclass_mulsq32sp16s_h, + 0, + Opcode_ae_mulsq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulsq32sp16u.l", ICLASS_ae_iclass_mulsq32sp16u_l, + 0, + Opcode_ae_mulsq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulsq32sp16u.h", ICLASS_ae_iclass_mulsq32sp16u_h, + 0, + Opcode_ae_mulsq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16s.ll", ICLASS_ae_iclass_mulzaaq32sp16s_ll, + 0, + Opcode_ae_mulzaaq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16s.ll", ICLASS_ae_iclass_mulzaafq32sp16s_ll, + 0, + Opcode_ae_mulzaafq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16u.ll", ICLASS_ae_iclass_mulzaaq32sp16u_ll, + 0, + Opcode_ae_mulzaaq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16u.ll", ICLASS_ae_iclass_mulzaafq32sp16u_ll, + 0, + Opcode_ae_mulzaafq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16s.hh", ICLASS_ae_iclass_mulzaaq32sp16s_hh, + 0, + Opcode_ae_mulzaaq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16s.hh", ICLASS_ae_iclass_mulzaafq32sp16s_hh, + 0, + Opcode_ae_mulzaafq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16u.hh", ICLASS_ae_iclass_mulzaaq32sp16u_hh, + 0, + Opcode_ae_mulzaaq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16u.hh", ICLASS_ae_iclass_mulzaafq32sp16u_hh, + 0, + Opcode_ae_mulzaafq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16s.lh", ICLASS_ae_iclass_mulzaaq32sp16s_lh, + 0, + Opcode_ae_mulzaaq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16s.lh", ICLASS_ae_iclass_mulzaafq32sp16s_lh, + 0, + Opcode_ae_mulzaafq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16u.lh", ICLASS_ae_iclass_mulzaaq32sp16u_lh, + 0, + Opcode_ae_mulzaaq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16u.lh", ICLASS_ae_iclass_mulzaafq32sp16u_lh, + 0, + Opcode_ae_mulzaafq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16s.ll", ICLASS_ae_iclass_mulzasq32sp16s_ll, + 0, + Opcode_ae_mulzasq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16s.ll", ICLASS_ae_iclass_mulzasfq32sp16s_ll, + 0, + Opcode_ae_mulzasfq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16u.ll", ICLASS_ae_iclass_mulzasq32sp16u_ll, + 0, + Opcode_ae_mulzasq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16u.ll", ICLASS_ae_iclass_mulzasfq32sp16u_ll, + 0, + Opcode_ae_mulzasfq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16s.hh", ICLASS_ae_iclass_mulzasq32sp16s_hh, + 0, + Opcode_ae_mulzasq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16s.hh", ICLASS_ae_iclass_mulzasfq32sp16s_hh, + 0, + Opcode_ae_mulzasfq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16u.hh", ICLASS_ae_iclass_mulzasq32sp16u_hh, + 0, + Opcode_ae_mulzasq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16u.hh", ICLASS_ae_iclass_mulzasfq32sp16u_hh, + 0, + Opcode_ae_mulzasfq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16s.lh", ICLASS_ae_iclass_mulzasq32sp16s_lh, + 0, + Opcode_ae_mulzasq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16s.lh", ICLASS_ae_iclass_mulzasfq32sp16s_lh, + 0, + Opcode_ae_mulzasfq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16u.lh", ICLASS_ae_iclass_mulzasq32sp16u_lh, + 0, + Opcode_ae_mulzasq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16u.lh", ICLASS_ae_iclass_mulzasfq32sp16u_lh, + 0, + Opcode_ae_mulzasfq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16s.ll", ICLASS_ae_iclass_mulzsaq32sp16s_ll, + 0, + Opcode_ae_mulzsaq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16s.ll", ICLASS_ae_iclass_mulzsafq32sp16s_ll, + 0, + Opcode_ae_mulzsafq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16u.ll", ICLASS_ae_iclass_mulzsaq32sp16u_ll, + 0, + Opcode_ae_mulzsaq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16u.ll", ICLASS_ae_iclass_mulzsafq32sp16u_ll, + 0, + Opcode_ae_mulzsafq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16s.hh", ICLASS_ae_iclass_mulzsaq32sp16s_hh, + 0, + Opcode_ae_mulzsaq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16s.hh", ICLASS_ae_iclass_mulzsafq32sp16s_hh, + 0, + Opcode_ae_mulzsafq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16u.hh", ICLASS_ae_iclass_mulzsaq32sp16u_hh, + 0, + Opcode_ae_mulzsaq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16u.hh", ICLASS_ae_iclass_mulzsafq32sp16u_hh, + 0, + Opcode_ae_mulzsafq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16s.lh", ICLASS_ae_iclass_mulzsaq32sp16s_lh, + 0, + Opcode_ae_mulzsaq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16s.lh", ICLASS_ae_iclass_mulzsafq32sp16s_lh, + 0, + Opcode_ae_mulzsafq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16u.lh", ICLASS_ae_iclass_mulzsaq32sp16u_lh, + 0, + Opcode_ae_mulzsaq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16u.lh", ICLASS_ae_iclass_mulzsafq32sp16u_lh, + 0, + Opcode_ae_mulzsafq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16s.ll", ICLASS_ae_iclass_mulzssq32sp16s_ll, + 0, + Opcode_ae_mulzssq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16s.ll", ICLASS_ae_iclass_mulzssfq32sp16s_ll, + 0, + Opcode_ae_mulzssfq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16u.ll", ICLASS_ae_iclass_mulzssq32sp16u_ll, + 0, + Opcode_ae_mulzssq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16u.ll", ICLASS_ae_iclass_mulzssfq32sp16u_ll, + 0, + Opcode_ae_mulzssfq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16s.hh", ICLASS_ae_iclass_mulzssq32sp16s_hh, + 0, + Opcode_ae_mulzssq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16s.hh", ICLASS_ae_iclass_mulzssfq32sp16s_hh, + 0, + Opcode_ae_mulzssfq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16u.hh", ICLASS_ae_iclass_mulzssq32sp16u_hh, + 0, + Opcode_ae_mulzssq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16u.hh", ICLASS_ae_iclass_mulzssfq32sp16u_hh, + 0, + Opcode_ae_mulzssfq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16s.lh", ICLASS_ae_iclass_mulzssq32sp16s_lh, + 0, + Opcode_ae_mulzssq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16s.lh", ICLASS_ae_iclass_mulzssfq32sp16s_lh, + 0, + Opcode_ae_mulzssfq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16u.lh", ICLASS_ae_iclass_mulzssq32sp16u_lh, + 0, + Opcode_ae_mulzssq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16u.lh", ICLASS_ae_iclass_mulzssfq32sp16u_lh, + 0, + Opcode_ae_mulzssfq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzaafp24s.hh.ll", ICLASS_ae_iclass_mulzaafp24s_hh_ll, + 0, + Opcode_ae_mulzaafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzaap24s.hh.ll", ICLASS_ae_iclass_mulzaap24s_hh_ll, + 0, + Opcode_ae_mulzaap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzaafp24s.hl.lh", ICLASS_ae_iclass_mulzaafp24s_hl_lh, + 0, + Opcode_ae_mulzaafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzaap24s.hl.lh", ICLASS_ae_iclass_mulzaap24s_hl_lh, + 0, + Opcode_ae_mulzaap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzasfp24s.hh.ll", ICLASS_ae_iclass_mulzasfp24s_hh_ll, + 0, + Opcode_ae_mulzasfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzasp24s.hh.ll", ICLASS_ae_iclass_mulzasp24s_hh_ll, + 0, + Opcode_ae_mulzasp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzasfp24s.hl.lh", ICLASS_ae_iclass_mulzasfp24s_hl_lh, + 0, + Opcode_ae_mulzasfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzasp24s.hl.lh", ICLASS_ae_iclass_mulzasp24s_hl_lh, + 0, + Opcode_ae_mulzasp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzsafp24s.hh.ll", ICLASS_ae_iclass_mulzsafp24s_hh_ll, + 0, + Opcode_ae_mulzsafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzsap24s.hh.ll", ICLASS_ae_iclass_mulzsap24s_hh_ll, + 0, + Opcode_ae_mulzsap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzsafp24s.hl.lh", ICLASS_ae_iclass_mulzsafp24s_hl_lh, + 0, + Opcode_ae_mulzsafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzsap24s.hl.lh", ICLASS_ae_iclass_mulzsap24s_hl_lh, + 0, + Opcode_ae_mulzsap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzssfp24s.hh.ll", ICLASS_ae_iclass_mulzssfp24s_hh_ll, + 0, + Opcode_ae_mulzssfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzssp24s.hh.ll", ICLASS_ae_iclass_mulzssp24s_hh_ll, + 0, + Opcode_ae_mulzssp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzssfp24s.hl.lh", ICLASS_ae_iclass_mulzssfp24s_hl_lh, + 0, + Opcode_ae_mulzssfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzssp24s.hl.lh", ICLASS_ae_iclass_mulzssp24s_hl_lh, + 0, + Opcode_ae_mulzssp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulaafp24s.hh.ll", ICLASS_ae_iclass_mulaafp24s_hh_ll, + 0, + Opcode_ae_mulaafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulaap24s.hh.ll", ICLASS_ae_iclass_mulaap24s_hh_ll, + 0, + Opcode_ae_mulaap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulaafp24s.hl.lh", ICLASS_ae_iclass_mulaafp24s_hl_lh, + 0, + Opcode_ae_mulaafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulaap24s.hl.lh", ICLASS_ae_iclass_mulaap24s_hl_lh, + 0, + Opcode_ae_mulaap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulasfp24s.hh.ll", ICLASS_ae_iclass_mulasfp24s_hh_ll, + 0, + Opcode_ae_mulasfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulasp24s.hh.ll", ICLASS_ae_iclass_mulasp24s_hh_ll, + 0, + Opcode_ae_mulasp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulasfp24s.hl.lh", ICLASS_ae_iclass_mulasfp24s_hl_lh, + 0, + Opcode_ae_mulasfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulasp24s.hl.lh", ICLASS_ae_iclass_mulasp24s_hl_lh, + 0, + Opcode_ae_mulasp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulsafp24s.hh.ll", ICLASS_ae_iclass_mulsafp24s_hh_ll, + 0, + Opcode_ae_mulsafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulsap24s.hh.ll", ICLASS_ae_iclass_mulsap24s_hh_ll, + 0, + Opcode_ae_mulsap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulsafp24s.hl.lh", ICLASS_ae_iclass_mulsafp24s_hl_lh, + 0, + Opcode_ae_mulsafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulsap24s.hl.lh", ICLASS_ae_iclass_mulsap24s_hl_lh, + 0, + Opcode_ae_mulsap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulssfp24s.hh.ll", ICLASS_ae_iclass_mulssfp24s_hh_ll, + 0, + Opcode_ae_mulssfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulssp24s.hh.ll", ICLASS_ae_iclass_mulssp24s_hh_ll, + 0, + Opcode_ae_mulssp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulssfp24s.hl.lh", ICLASS_ae_iclass_mulssfp24s_hl_lh, + 0, + Opcode_ae_mulssfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulssp24s.hl.lh", ICLASS_ae_iclass_mulssp24s_hl_lh, + 0, + Opcode_ae_mulssp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_sha32", ICLASS_ae_iclass_sha32, + 0, + Opcode_ae_sha32_encode_fns, 0, 0 }, + { "ae_vldl32t", ICLASS_ae_iclass_vldl32t, + 0, + Opcode_ae_vldl32t_encode_fns, 1, Opcode_ae_vldl32t_funcUnit_uses }, + { "ae_vldl16t", ICLASS_ae_iclass_vldl16t, + 0, + Opcode_ae_vldl16t_encode_fns, 1, Opcode_ae_vldl16t_funcUnit_uses }, + { "ae_vldl16c", ICLASS_ae_iclass_vldl16c, + 0, + Opcode_ae_vldl16c_encode_fns, 3, Opcode_ae_vldl16c_funcUnit_uses }, + { "ae_vldsht", ICLASS_ae_iclass_vldsht, + 0, + Opcode_ae_vldsht_encode_fns, 3, Opcode_ae_vldsht_funcUnit_uses }, + { "ae_lb", ICLASS_ae_iclass_lb, + 0, + Opcode_ae_lb_encode_fns, 1, Opcode_ae_lb_funcUnit_uses }, + { "ae_lbi", ICLASS_ae_iclass_lbi, + 0, + Opcode_ae_lbi_encode_fns, 1, Opcode_ae_lbi_funcUnit_uses }, + { "ae_lbk", ICLASS_ae_iclass_lbk, + 0, + Opcode_ae_lbk_encode_fns, 1, Opcode_ae_lbk_funcUnit_uses }, + { "ae_lbki", ICLASS_ae_iclass_lbki, + 0, + Opcode_ae_lbki_encode_fns, 1, Opcode_ae_lbki_funcUnit_uses }, + { "ae_db", ICLASS_ae_iclass_db, + 0, + Opcode_ae_db_encode_fns, 2, Opcode_ae_db_funcUnit_uses }, + { "ae_dbi", ICLASS_ae_iclass_dbi, + 0, + Opcode_ae_dbi_encode_fns, 2, Opcode_ae_dbi_funcUnit_uses }, + { "ae_vlel32t", ICLASS_ae_iclass_vlel32t, + 0, + Opcode_ae_vlel32t_encode_fns, 1, Opcode_ae_vlel32t_funcUnit_uses }, + { "ae_vlel16t", ICLASS_ae_iclass_vlel16t, + 0, + Opcode_ae_vlel16t_encode_fns, 1, Opcode_ae_vlel16t_funcUnit_uses }, + { "ae_sb", ICLASS_ae_iclass_sb, + 0, + Opcode_ae_sb_encode_fns, 2, Opcode_ae_sb_funcUnit_uses }, + { "ae_sbi", ICLASS_ae_iclass_sbi, + 0, + Opcode_ae_sbi_encode_fns, 2, Opcode_ae_sbi_funcUnit_uses }, + { "ae_vles16c", ICLASS_ae_iclass_vles16c, + 0, + Opcode_ae_vles16c_encode_fns, 2, Opcode_ae_vles16c_funcUnit_uses }, + { "ae_sbf", ICLASS_ae_iclass_sbf, + 0, + Opcode_ae_sbf_encode_fns, 2, Opcode_ae_sbf_funcUnit_uses }, + { "ae_slaasq56s", ICLASS_icls_AE_SLAASQ56S, + 0, + Opcode_ae_slaasq56s_encode_fns, 0, 0 }, + { "ae_addbrba32", ICLASS_icls_AE_ADDBRBA32, + 0, + Opcode_ae_addbrba32_encode_fns, 0, 0 }, + { "ae_minabssp24s", ICLASS_icls_AE_MINABSSP24S, + 0, + Opcode_ae_minabssp24s_encode_fns, 0, 0 }, + { "ae_maxabssp24s", ICLASS_icls_AE_MAXABSSP24S, + 0, + Opcode_ae_maxabssp24s_encode_fns, 0, 0 }, + { "ae_minabssq56s", ICLASS_icls_AE_MINABSSQ56S, + 0, + Opcode_ae_minabssq56s_encode_fns, 0, 0 }, + { "ae_maxabssq56s", ICLASS_icls_AE_MAXABSSQ56S, + 0, + Opcode_ae_maxabssq56s_encode_fns, 0, 0 }, + { "rur.ae_cbegin0", ICLASS_rur_ae_cbegin0, + 0, + Opcode_rur_ae_cbegin0_encode_fns, 0, 0 }, + { "wur.ae_cbegin0", ICLASS_wur_ae_cbegin0, + 0, + Opcode_wur_ae_cbegin0_encode_fns, 0, 0 }, + { "rur.ae_cend0", ICLASS_rur_ae_cend0, + 0, + Opcode_rur_ae_cend0_encode_fns, 0, 0 }, + { "wur.ae_cend0", ICLASS_wur_ae_cend0, + 0, + Opcode_wur_ae_cend0_encode_fns, 0, 0 }, + { "ae_lp24x2.c", ICLASS_icls_AE_LP24X2_C, + 0, + Opcode_ae_lp24x2_c_encode_fns, 0, 0 }, + { "ae_sp24x2s.c", ICLASS_icls_AE_SP24X2S_C, + 0, + Opcode_ae_sp24x2s_c_encode_fns, 0, 0 }, + { "ae_lp24x2f.c", ICLASS_icls_AE_LP24X2F_C, + 0, + Opcode_ae_lp24x2f_c_encode_fns, 0, 0 }, + { "ae_sp24x2f.c", ICLASS_icls_AE_SP24X2F_C, + 0, + Opcode_ae_sp24x2f_c_encode_fns, 0, 0 }, + { "ae_lp16x2f.c", ICLASS_icls_AE_LP16X2F_C, + 0, + Opcode_ae_lp16x2f_c_encode_fns, 0, 0 }, + { "ae_sp16x2f.c", ICLASS_icls_AE_SP16X2F_C, + 0, + Opcode_ae_sp16x2f_c_encode_fns, 0, 0 }, + { "ae_lp24.c", ICLASS_icls_AE_LP24_C, + 0, + Opcode_ae_lp24_c_encode_fns, 0, 0 }, + { "ae_sp24s.l.c", ICLASS_icls_AE_SP24S_L_C, + 0, + Opcode_ae_sp24s_l_c_encode_fns, 0, 0 }, + { "ae_lp24f.c", ICLASS_icls_AE_LP24F_C, + 0, + Opcode_ae_lp24f_c_encode_fns, 0, 0 }, + { "ae_sp24f.l.c", ICLASS_icls_AE_SP24F_L_C, + 0, + Opcode_ae_sp24f_l_c_encode_fns, 0, 0 }, + { "ae_lp16f.c", ICLASS_icls_AE_LP16F_C, + 0, + Opcode_ae_lp16f_c_encode_fns, 0, 0 }, + { "ae_sp16f.l.c", ICLASS_icls_AE_SP16F_L_C, + 0, + Opcode_ae_sp16f_l_c_encode_fns, 0, 0 }, + { "ae_lq56.c", ICLASS_icls_AE_LQ56_C, + 0, + Opcode_ae_lq56_c_encode_fns, 0, 0 }, + { "ae_sq56s.c", ICLASS_icls_AE_SQ56S_C, + 0, + Opcode_ae_sq56s_c_encode_fns, 0, 0 }, + { "ae_lq32f.c", ICLASS_icls_AE_LQ32F_C, + 0, + Opcode_ae_lq32f_c_encode_fns, 0, 0 }, + { "ae_sq32f.c", ICLASS_icls_AE_SQ32F_C, + 0, + Opcode_ae_sq32f_c_encode_fns, 0, 0 }, + { "rur.expstate", ICLASS_rur_expstate, + 0, + Opcode_rur_expstate_encode_fns, 0, 0 }, + { "wur.expstate", ICLASS_wur_expstate, + 0, + Opcode_wur_expstate_encode_fns, 0, 0 }, + { "read_impwire", ICLASS_iclass_READ_IMPWIRE, + 0, + Opcode_read_impwire_encode_fns, 0, 0 }, + { "setb_expstate", ICLASS_iclass_SETB_EXPSTATE, + 0, + Opcode_setb_expstate_encode_fns, 0, 0 }, + { "clrb_expstate", ICLASS_iclass_CLRB_EXPSTATE, + 0, + Opcode_clrb_expstate_encode_fns, 0, 0 }, + { "wrmsk_expstate", ICLASS_iclass_WRMSK_EXPSTATE, + 0, + Opcode_wrmsk_expstate_encode_fns, 0, 0 } +}; + +enum xtensa_opcode_id { + OPCODE_EXCW, + OPCODE_RFE, + OPCODE_RFDE, + OPCODE_SYSCALL, + OPCODE_CALL12, + OPCODE_CALL8, + OPCODE_CALL4, + OPCODE_CALLX12, + OPCODE_CALLX8, + OPCODE_CALLX4, + OPCODE_ENTRY, + OPCODE_MOVSP, + OPCODE_ROTW, + OPCODE_RETW, + OPCODE_RETW_N, + OPCODE_RFWO, + OPCODE_RFWU, + OPCODE_L32E, + OPCODE_S32E, + OPCODE_RSR_WINDOWBASE, + OPCODE_WSR_WINDOWBASE, + OPCODE_XSR_WINDOWBASE, + OPCODE_RSR_WINDOWSTART, + OPCODE_WSR_WINDOWSTART, + OPCODE_XSR_WINDOWSTART, + OPCODE_ADD_N, + OPCODE_ADDI_N, + OPCODE_BEQZ_N, + OPCODE_BNEZ_N, + OPCODE_ILL_N, + OPCODE_L32I_N, + OPCODE_MOV_N, + OPCODE_MOVI_N, + OPCODE_NOP_N, + OPCODE_RET_N, + OPCODE_S32I_N, + OPCODE_RUR_THREADPTR, + OPCODE_WUR_THREADPTR, + OPCODE_ADDI, + OPCODE_ADDMI, + OPCODE_ADD, + OPCODE_SUB, + OPCODE_ADDX2, + OPCODE_ADDX4, + OPCODE_ADDX8, + OPCODE_SUBX2, + OPCODE_SUBX4, + OPCODE_SUBX8, + OPCODE_AND, + OPCODE_OR, + OPCODE_XOR, + OPCODE_BEQI, + OPCODE_BNEI, + OPCODE_BGEI, + OPCODE_BLTI, + OPCODE_BBCI, + OPCODE_BBSI, + OPCODE_BGEUI, + OPCODE_BLTUI, + OPCODE_BEQ, + OPCODE_BNE, + OPCODE_BGE, + OPCODE_BLT, + OPCODE_BGEU, + OPCODE_BLTU, + OPCODE_BANY, + OPCODE_BNONE, + OPCODE_BALL, + OPCODE_BNALL, + OPCODE_BBC, + OPCODE_BBS, + OPCODE_BEQZ, + OPCODE_BNEZ, + OPCODE_BGEZ, + OPCODE_BLTZ, + OPCODE_CALL0, + OPCODE_CALLX0, + OPCODE_EXTUI, + OPCODE_ILL, + OPCODE_J, + OPCODE_JX, + OPCODE_L16UI, + OPCODE_L16SI, + OPCODE_L32I, + OPCODE_L32R, + OPCODE_L8UI, + OPCODE_LOOP, + OPCODE_LOOPNEZ, + OPCODE_LOOPGTZ, + OPCODE_MOVI, + OPCODE_MOVEQZ, + OPCODE_MOVNEZ, + OPCODE_MOVLTZ, + OPCODE_MOVGEZ, + OPCODE_NEG, + OPCODE_ABS, + OPCODE_NOP, + OPCODE_RET, + OPCODE_SIMCALL, + OPCODE_S16I, + OPCODE_S32I, + OPCODE_S32NB, + OPCODE_S8I, + OPCODE_SSR, + OPCODE_SSL, + OPCODE_SSA8L, + OPCODE_SSA8B, + OPCODE_SSAI, + OPCODE_SLL, + OPCODE_SRC, + OPCODE_SRL, + OPCODE_SRA, + OPCODE_SLLI, + OPCODE_SRAI, + OPCODE_SRLI, + OPCODE_MEMW, + OPCODE_EXTW, + OPCODE_ISYNC, + OPCODE_RSYNC, + OPCODE_ESYNC, + OPCODE_DSYNC, + OPCODE_RSIL, + OPCODE_RSR_LEND, + OPCODE_WSR_LEND, + OPCODE_XSR_LEND, + OPCODE_RSR_LCOUNT, + OPCODE_WSR_LCOUNT, + OPCODE_XSR_LCOUNT, + OPCODE_RSR_LBEG, + OPCODE_WSR_LBEG, + OPCODE_XSR_LBEG, + OPCODE_RSR_SAR, + OPCODE_WSR_SAR, + OPCODE_XSR_SAR, + OPCODE_RSR_MEMCTL, + OPCODE_WSR_MEMCTL, + OPCODE_XSR_MEMCTL, + OPCODE_RSR_LITBASE, + OPCODE_WSR_LITBASE, + OPCODE_XSR_LITBASE, + OPCODE_RSR_CONFIGID0, + OPCODE_WSR_CONFIGID0, + OPCODE_RSR_CONFIGID1, + OPCODE_RSR_243, + OPCODE_RSR_PS, + OPCODE_WSR_PS, + OPCODE_XSR_PS, + OPCODE_RSR_EPC1, + OPCODE_WSR_EPC1, + OPCODE_XSR_EPC1, + OPCODE_RSR_EXCSAVE1, + OPCODE_WSR_EXCSAVE1, + OPCODE_XSR_EXCSAVE1, + OPCODE_RSR_EPC2, + OPCODE_WSR_EPC2, + OPCODE_XSR_EPC2, + OPCODE_RSR_EXCSAVE2, + OPCODE_WSR_EXCSAVE2, + OPCODE_XSR_EXCSAVE2, + OPCODE_RSR_EPC3, + OPCODE_WSR_EPC3, + OPCODE_XSR_EPC3, + OPCODE_RSR_EXCSAVE3, + OPCODE_WSR_EXCSAVE3, + OPCODE_XSR_EXCSAVE3, + OPCODE_RSR_EPC4, + OPCODE_WSR_EPC4, + OPCODE_XSR_EPC4, + OPCODE_RSR_EXCSAVE4, + OPCODE_WSR_EXCSAVE4, + OPCODE_XSR_EXCSAVE4, + OPCODE_RSR_EPC5, + OPCODE_WSR_EPC5, + OPCODE_XSR_EPC5, + OPCODE_RSR_EXCSAVE5, + OPCODE_WSR_EXCSAVE5, + OPCODE_XSR_EXCSAVE5, + OPCODE_RSR_EPC6, + OPCODE_WSR_EPC6, + OPCODE_XSR_EPC6, + OPCODE_RSR_EXCSAVE6, + OPCODE_WSR_EXCSAVE6, + OPCODE_XSR_EXCSAVE6, + OPCODE_RSR_EPC7, + OPCODE_WSR_EPC7, + OPCODE_XSR_EPC7, + OPCODE_RSR_EXCSAVE7, + OPCODE_WSR_EXCSAVE7, + OPCODE_XSR_EXCSAVE7, + OPCODE_RSR_EPS2, + OPCODE_WSR_EPS2, + OPCODE_XSR_EPS2, + OPCODE_RSR_EPS3, + OPCODE_WSR_EPS3, + OPCODE_XSR_EPS3, + OPCODE_RSR_EPS4, + OPCODE_WSR_EPS4, + OPCODE_XSR_EPS4, + OPCODE_RSR_EPS5, + OPCODE_WSR_EPS5, + OPCODE_XSR_EPS5, + OPCODE_RSR_EPS6, + OPCODE_WSR_EPS6, + OPCODE_XSR_EPS6, + OPCODE_RSR_EPS7, + OPCODE_WSR_EPS7, + OPCODE_XSR_EPS7, + OPCODE_RSR_EXCVADDR, + OPCODE_WSR_EXCVADDR, + OPCODE_XSR_EXCVADDR, + OPCODE_RSR_DEPC, + OPCODE_WSR_DEPC, + OPCODE_XSR_DEPC, + OPCODE_RSR_EXCCAUSE, + OPCODE_WSR_EXCCAUSE, + OPCODE_XSR_EXCCAUSE, + OPCODE_RSR_MISC0, + OPCODE_WSR_MISC0, + OPCODE_XSR_MISC0, + OPCODE_RSR_MISC1, + OPCODE_WSR_MISC1, + OPCODE_XSR_MISC1, + OPCODE_RSR_PRID, + OPCODE_RSR_VECBASE, + OPCODE_WSR_VECBASE, + OPCODE_XSR_VECBASE, + OPCODE_MUL16U, + OPCODE_MUL16S, + OPCODE_MULL, + OPCODE_MULUH, + OPCODE_MULSH, + OPCODE_MUL_AA_LL, + OPCODE_MUL_AA_HL, + OPCODE_MUL_AA_LH, + OPCODE_MUL_AA_HH, + OPCODE_UMUL_AA_LL, + OPCODE_UMUL_AA_HL, + OPCODE_UMUL_AA_LH, + OPCODE_UMUL_AA_HH, + OPCODE_MUL_AD_LL, + OPCODE_MUL_AD_HL, + OPCODE_MUL_AD_LH, + OPCODE_MUL_AD_HH, + OPCODE_MUL_DA_LL, + OPCODE_MUL_DA_HL, + OPCODE_MUL_DA_LH, + OPCODE_MUL_DA_HH, + OPCODE_MUL_DD_LL, + OPCODE_MUL_DD_HL, + OPCODE_MUL_DD_LH, + OPCODE_MUL_DD_HH, + OPCODE_MULA_AA_LL, + OPCODE_MULA_AA_HL, + OPCODE_MULA_AA_LH, + OPCODE_MULA_AA_HH, + OPCODE_MULS_AA_LL, + OPCODE_MULS_AA_HL, + OPCODE_MULS_AA_LH, + OPCODE_MULS_AA_HH, + OPCODE_MULA_AD_LL, + OPCODE_MULA_AD_HL, + OPCODE_MULA_AD_LH, + OPCODE_MULA_AD_HH, + OPCODE_MULS_AD_LL, + OPCODE_MULS_AD_HL, + OPCODE_MULS_AD_LH, + OPCODE_MULS_AD_HH, + OPCODE_MULA_DA_LL, + OPCODE_MULA_DA_HL, + OPCODE_MULA_DA_LH, + OPCODE_MULA_DA_HH, + OPCODE_MULS_DA_LL, + OPCODE_MULS_DA_HL, + OPCODE_MULS_DA_LH, + OPCODE_MULS_DA_HH, + OPCODE_MULA_DD_LL, + OPCODE_MULA_DD_HL, + OPCODE_MULA_DD_LH, + OPCODE_MULA_DD_HH, + OPCODE_MULS_DD_LL, + OPCODE_MULS_DD_HL, + OPCODE_MULS_DD_LH, + OPCODE_MULS_DD_HH, + OPCODE_MULA_DA_LL_LDDEC, + OPCODE_MULA_DA_LL_LDINC, + OPCODE_MULA_DA_HL_LDDEC, + OPCODE_MULA_DA_HL_LDINC, + OPCODE_MULA_DA_LH_LDDEC, + OPCODE_MULA_DA_LH_LDINC, + OPCODE_MULA_DA_HH_LDDEC, + OPCODE_MULA_DA_HH_LDINC, + OPCODE_MULA_DD_LL_LDDEC, + OPCODE_MULA_DD_LL_LDINC, + OPCODE_MULA_DD_HL_LDDEC, + OPCODE_MULA_DD_HL_LDINC, + OPCODE_MULA_DD_LH_LDDEC, + OPCODE_MULA_DD_LH_LDINC, + OPCODE_MULA_DD_HH_LDDEC, + OPCODE_MULA_DD_HH_LDINC, + OPCODE_LDDEC, + OPCODE_LDINC, + OPCODE_RSR_M0, + OPCODE_WSR_M0, + OPCODE_XSR_M0, + OPCODE_RSR_M1, + OPCODE_WSR_M1, + OPCODE_XSR_M1, + OPCODE_RSR_M2, + OPCODE_WSR_M2, + OPCODE_XSR_M2, + OPCODE_RSR_M3, + OPCODE_WSR_M3, + OPCODE_XSR_M3, + OPCODE_RSR_ACCLO, + OPCODE_WSR_ACCLO, + OPCODE_XSR_ACCLO, + OPCODE_RSR_ACCHI, + OPCODE_WSR_ACCHI, + OPCODE_XSR_ACCHI, + OPCODE_RFI, + OPCODE_WAITI, + OPCODE_RSR_INTERRUPT, + OPCODE_WSR_INTSET, + OPCODE_WSR_INTCLEAR, + OPCODE_RSR_INTENABLE, + OPCODE_WSR_INTENABLE, + OPCODE_XSR_INTENABLE, + OPCODE_BREAK, + OPCODE_BREAK_N, + OPCODE_RSR_DBREAKA0, + OPCODE_WSR_DBREAKA0, + OPCODE_XSR_DBREAKA0, + OPCODE_RSR_DBREAKC0, + OPCODE_WSR_DBREAKC0, + OPCODE_XSR_DBREAKC0, + OPCODE_RSR_DBREAKA1, + OPCODE_WSR_DBREAKA1, + OPCODE_XSR_DBREAKA1, + OPCODE_RSR_DBREAKC1, + OPCODE_WSR_DBREAKC1, + OPCODE_XSR_DBREAKC1, + OPCODE_RSR_IBREAKA0, + OPCODE_WSR_IBREAKA0, + OPCODE_XSR_IBREAKA0, + OPCODE_RSR_IBREAKA1, + OPCODE_WSR_IBREAKA1, + OPCODE_XSR_IBREAKA1, + OPCODE_RSR_IBREAKENABLE, + OPCODE_WSR_IBREAKENABLE, + OPCODE_XSR_IBREAKENABLE, + OPCODE_RSR_DEBUGCAUSE, + OPCODE_WSR_DEBUGCAUSE, + OPCODE_XSR_DEBUGCAUSE, + OPCODE_RSR_ICOUNT, + OPCODE_WSR_ICOUNT, + OPCODE_XSR_ICOUNT, + OPCODE_RSR_ICOUNTLEVEL, + OPCODE_WSR_ICOUNTLEVEL, + OPCODE_XSR_ICOUNTLEVEL, + OPCODE_RSR_DDR, + OPCODE_WSR_DDR, + OPCODE_XSR_DDR, + OPCODE_LDDR32_P, + OPCODE_SDDR32_P, + OPCODE_RFDO, + OPCODE_RFDD, + OPCODE_WSR_MMID, + OPCODE_ANDB, + OPCODE_ANDBC, + OPCODE_ORB, + OPCODE_ORBC, + OPCODE_XORB, + OPCODE_ANY4, + OPCODE_ALL4, + OPCODE_ANY8, + OPCODE_ALL8, + OPCODE_BF, + OPCODE_BT, + OPCODE_MOVF, + OPCODE_MOVT, + OPCODE_RSR_BR, + OPCODE_WSR_BR, + OPCODE_XSR_BR, + OPCODE_RSR_CCOUNT, + OPCODE_WSR_CCOUNT, + OPCODE_XSR_CCOUNT, + OPCODE_RSR_CCOMPARE0, + OPCODE_WSR_CCOMPARE0, + OPCODE_XSR_CCOMPARE0, + OPCODE_RSR_CCOMPARE1, + OPCODE_WSR_CCOMPARE1, + OPCODE_XSR_CCOMPARE1, + OPCODE_RSR_CCOMPARE2, + OPCODE_WSR_CCOMPARE2, + OPCODE_XSR_CCOMPARE2, + OPCODE_IPF, + OPCODE_IHI, + OPCODE_IPFL, + OPCODE_IHU, + OPCODE_IIU, + OPCODE_III, + OPCODE_LICT, + OPCODE_LICW, + OPCODE_SICT, + OPCODE_SICW, + OPCODE_DHWB, + OPCODE_DHWBI, + OPCODE_DIWBUI_P, + OPCODE_DIWB, + OPCODE_DIWBI, + OPCODE_DHI, + OPCODE_DII, + OPCODE_DPFR, + OPCODE_DPFW, + OPCODE_DPFRO, + OPCODE_DPFWO, + OPCODE_DPFL, + OPCODE_DHU, + OPCODE_DIU, + OPCODE_SDCT, + OPCODE_LDCT, + OPCODE_RSR_PREFCTL, + OPCODE_WSR_PREFCTL, + OPCODE_XSR_PREFCTL, + OPCODE_WSR_PTEVADDR, + OPCODE_RSR_PTEVADDR, + OPCODE_XSR_PTEVADDR, + OPCODE_RSR_RASID, + OPCODE_WSR_RASID, + OPCODE_XSR_RASID, + OPCODE_RSR_ITLBCFG, + OPCODE_WSR_ITLBCFG, + OPCODE_XSR_ITLBCFG, + OPCODE_RSR_DTLBCFG, + OPCODE_WSR_DTLBCFG, + OPCODE_XSR_DTLBCFG, + OPCODE_IDTLB, + OPCODE_PDTLB, + OPCODE_RDTLB0, + OPCODE_RDTLB1, + OPCODE_WDTLB, + OPCODE_IITLB, + OPCODE_PITLB, + OPCODE_RITLB0, + OPCODE_RITLB1, + OPCODE_WITLB, + OPCODE_LDPTE, + OPCODE_HWWITLBA, + OPCODE_HWWDTLBA, + OPCODE_RSR_CPENABLE, + OPCODE_WSR_CPENABLE, + OPCODE_XSR_CPENABLE, + OPCODE_CLAMPS, + OPCODE_MIN, + OPCODE_MAX, + OPCODE_MINU, + OPCODE_MAXU, + OPCODE_NSA, + OPCODE_NSAU, + OPCODE_SEXT, + OPCODE_L32AI, + OPCODE_S32RI, + OPCODE_S32C1I, + OPCODE_RSR_SCOMPARE1, + OPCODE_WSR_SCOMPARE1, + OPCODE_XSR_SCOMPARE1, + OPCODE_RSR_ATOMCTL, + OPCODE_WSR_ATOMCTL, + OPCODE_XSR_ATOMCTL, + OPCODE_QUOU, + OPCODE_QUOS, + OPCODE_REMU, + OPCODE_REMS, + OPCODE_RER, + OPCODE_WER, + OPCODE_RUR_AE_OVF_SAR, + OPCODE_WUR_AE_OVF_SAR, + OPCODE_RUR_AE_BITHEAD, + OPCODE_WUR_AE_BITHEAD, + OPCODE_RUR_AE_TS_FTS_BU_BP, + OPCODE_WUR_AE_TS_FTS_BU_BP, + OPCODE_RUR_AE_SD_NO, + OPCODE_WUR_AE_SD_NO, + OPCODE_RUR_AE_OVERFLOW, + OPCODE_WUR_AE_OVERFLOW, + OPCODE_RUR_AE_SAR, + OPCODE_WUR_AE_SAR, + OPCODE_RUR_AE_BITPTR, + OPCODE_WUR_AE_BITPTR, + OPCODE_RUR_AE_BITSUSED, + OPCODE_WUR_AE_BITSUSED, + OPCODE_RUR_AE_TABLESIZE, + OPCODE_WUR_AE_TABLESIZE, + OPCODE_RUR_AE_FIRST_TS, + OPCODE_WUR_AE_FIRST_TS, + OPCODE_RUR_AE_NEXTOFFSET, + OPCODE_WUR_AE_NEXTOFFSET, + OPCODE_RUR_AE_SEARCHDONE, + OPCODE_WUR_AE_SEARCHDONE, + OPCODE_AE_LP16F_I, + OPCODE_AE_LP16F_IU, + OPCODE_AE_LP16F_X, + OPCODE_AE_LP16F_XU, + OPCODE_AE_LP24_I, + OPCODE_AE_LP24_IU, + OPCODE_AE_LP24_X, + OPCODE_AE_LP24_XU, + OPCODE_AE_LP24F_I, + OPCODE_AE_LP24F_IU, + OPCODE_AE_LP24F_X, + OPCODE_AE_LP24F_XU, + OPCODE_AE_LP16X2F_I, + OPCODE_AE_LP16X2F_IU, + OPCODE_AE_LP16X2F_X, + OPCODE_AE_LP16X2F_XU, + OPCODE_AE_LP24X2F_I, + OPCODE_AE_LP24X2F_IU, + OPCODE_AE_LP24X2F_X, + OPCODE_AE_LP24X2F_XU, + OPCODE_AE_LP24X2_I, + OPCODE_AE_LP24X2_IU, + OPCODE_AE_LP24X2_X, + OPCODE_AE_LP24X2_XU, + OPCODE_AE_SP16X2F_I, + OPCODE_AE_SP16X2F_IU, + OPCODE_AE_SP16X2F_X, + OPCODE_AE_SP16X2F_XU, + OPCODE_AE_SP24X2S_I, + OPCODE_AE_SP24X2S_IU, + OPCODE_AE_SP24X2S_X, + OPCODE_AE_SP24X2S_XU, + OPCODE_AE_SP24X2F_I, + OPCODE_AE_SP24X2F_IU, + OPCODE_AE_SP24X2F_X, + OPCODE_AE_SP24X2F_XU, + OPCODE_AE_SP16F_L_I, + OPCODE_AE_SP16F_L_IU, + OPCODE_AE_SP16F_L_X, + OPCODE_AE_SP16F_L_XU, + OPCODE_AE_SP24S_L_I, + OPCODE_AE_SP24S_L_IU, + OPCODE_AE_SP24S_L_X, + OPCODE_AE_SP24S_L_XU, + OPCODE_AE_SP24F_L_I, + OPCODE_AE_SP24F_L_IU, + OPCODE_AE_SP24F_L_X, + OPCODE_AE_SP24F_L_XU, + OPCODE_AE_LQ56_I, + OPCODE_AE_LQ56_IU, + OPCODE_AE_LQ56_X, + OPCODE_AE_LQ56_XU, + OPCODE_AE_LQ32F_I, + OPCODE_AE_LQ32F_IU, + OPCODE_AE_LQ32F_X, + OPCODE_AE_LQ32F_XU, + OPCODE_AE_SQ56S_I, + OPCODE_AE_SQ56S_IU, + OPCODE_AE_SQ56S_X, + OPCODE_AE_SQ56S_XU, + OPCODE_AE_SQ32F_I, + OPCODE_AE_SQ32F_IU, + OPCODE_AE_SQ32F_X, + OPCODE_AE_SQ32F_XU, + OPCODE_AE_ZEROP48, + OPCODE_AE_MOVP48, + OPCODE_AE_SELP24_LL, + OPCODE_AE_SELP24_LH, + OPCODE_AE_SELP24_HL, + OPCODE_AE_SELP24_HH, + OPCODE_AE_MOVTP24X2, + OPCODE_AE_MOVFP24X2, + OPCODE_AE_MOVTP48, + OPCODE_AE_MOVFP48, + OPCODE_AE_MOVPA24X2, + OPCODE_AE_TRUNCP24A32X2, + OPCODE_AE_CVTA32P24_L, + OPCODE_AE_CVTA32P24_H, + OPCODE_AE_CVTP24A16X2_LL, + OPCODE_AE_CVTP24A16X2_LH, + OPCODE_AE_CVTP24A16X2_HL, + OPCODE_AE_CVTP24A16X2_HH, + OPCODE_AE_TRUNCP24Q48X2, + OPCODE_AE_TRUNCP16, + OPCODE_AE_ROUNDSP24Q48SYM, + OPCODE_AE_ROUNDSP24Q48ASYM, + OPCODE_AE_ROUNDSP16Q48SYM, + OPCODE_AE_ROUNDSP16Q48ASYM, + OPCODE_AE_ROUNDSP16SYM, + OPCODE_AE_ROUNDSP16ASYM, + OPCODE_AE_ZEROQ56, + OPCODE_AE_MOVQ56, + OPCODE_AE_MOVTQ56, + OPCODE_AE_MOVFQ56, + OPCODE_AE_CVTQ48A32S, + OPCODE_AE_CVTQ48P24S_L, + OPCODE_AE_CVTQ48P24S_H, + OPCODE_AE_SATQ48S, + OPCODE_AE_TRUNCQ32, + OPCODE_AE_ROUNDSQ32SYM, + OPCODE_AE_ROUNDSQ32ASYM, + OPCODE_AE_TRUNCA32Q48, + OPCODE_AE_MOVAP24S_L, + OPCODE_AE_MOVAP24S_H, + OPCODE_AE_TRUNCA16P24S_L, + OPCODE_AE_TRUNCA16P24S_H, + OPCODE_AE_ADDP24, + OPCODE_AE_SUBP24, + OPCODE_AE_NEGP24, + OPCODE_AE_ABSP24, + OPCODE_AE_MAXP24S, + OPCODE_AE_MINP24S, + OPCODE_AE_MAXBP24S, + OPCODE_AE_MINBP24S, + OPCODE_AE_ADDSP24S, + OPCODE_AE_SUBSP24S, + OPCODE_AE_NEGSP24S, + OPCODE_AE_ABSSP24S, + OPCODE_AE_ANDP48, + OPCODE_AE_NANDP48, + OPCODE_AE_ORP48, + OPCODE_AE_XORP48, + OPCODE_AE_LTP24S, + OPCODE_AE_LEP24S, + OPCODE_AE_EQP24, + OPCODE_AE_ADDQ56, + OPCODE_AE_SUBQ56, + OPCODE_AE_NEGQ56, + OPCODE_AE_ABSQ56, + OPCODE_AE_MAXQ56S, + OPCODE_AE_MINQ56S, + OPCODE_AE_MAXBQ56S, + OPCODE_AE_MINBQ56S, + OPCODE_AE_ADDSQ56S, + OPCODE_AE_SUBSQ56S, + OPCODE_AE_NEGSQ56S, + OPCODE_AE_ABSSQ56S, + OPCODE_AE_ANDQ56, + OPCODE_AE_NANDQ56, + OPCODE_AE_ORQ56, + OPCODE_AE_XORQ56, + OPCODE_AE_SLLIP24, + OPCODE_AE_SRLIP24, + OPCODE_AE_SRAIP24, + OPCODE_AE_SLLSP24, + OPCODE_AE_SRLSP24, + OPCODE_AE_SRASP24, + OPCODE_AE_SLLISP24S, + OPCODE_AE_SLLSSP24S, + OPCODE_AE_SLLIQ56, + OPCODE_AE_SRLIQ56, + OPCODE_AE_SRAIQ56, + OPCODE_AE_SLLSQ56, + OPCODE_AE_SRLSQ56, + OPCODE_AE_SRASQ56, + OPCODE_AE_SLLAQ56, + OPCODE_AE_SRLAQ56, + OPCODE_AE_SRAAQ56, + OPCODE_AE_SLLISQ56S, + OPCODE_AE_SLLSSQ56S, + OPCODE_AE_SLLASQ56S, + OPCODE_AE_LTQ56S, + OPCODE_AE_LEQ56S, + OPCODE_AE_EQQ56, + OPCODE_AE_NSAQ56S, + OPCODE_AE_MULSRFQ32SP24S_H, + OPCODE_AE_MULSRFQ32SP24S_L, + OPCODE_AE_MULARFQ32SP24S_H, + OPCODE_AE_MULARFQ32SP24S_L, + OPCODE_AE_MULRFQ32SP24S_H, + OPCODE_AE_MULRFQ32SP24S_L, + OPCODE_AE_MULSFQ32SP24S_H, + OPCODE_AE_MULSFQ32SP24S_L, + OPCODE_AE_MULAFQ32SP24S_H, + OPCODE_AE_MULAFQ32SP24S_L, + OPCODE_AE_MULFQ32SP24S_H, + OPCODE_AE_MULFQ32SP24S_L, + OPCODE_AE_MULFS32P16S_LL, + OPCODE_AE_MULFP24S_LL, + OPCODE_AE_MULP24S_LL, + OPCODE_AE_MULFS32P16S_LH, + OPCODE_AE_MULFP24S_LH, + OPCODE_AE_MULP24S_LH, + OPCODE_AE_MULFS32P16S_HL, + OPCODE_AE_MULFP24S_HL, + OPCODE_AE_MULP24S_HL, + OPCODE_AE_MULFS32P16S_HH, + OPCODE_AE_MULFP24S_HH, + OPCODE_AE_MULP24S_HH, + OPCODE_AE_MULAFS32P16S_LL, + OPCODE_AE_MULAFP24S_LL, + OPCODE_AE_MULAP24S_LL, + OPCODE_AE_MULAFS32P16S_LH, + OPCODE_AE_MULAFP24S_LH, + OPCODE_AE_MULAP24S_LH, + OPCODE_AE_MULAFS32P16S_HL, + OPCODE_AE_MULAFP24S_HL, + OPCODE_AE_MULAP24S_HL, + OPCODE_AE_MULAFS32P16S_HH, + OPCODE_AE_MULAFP24S_HH, + OPCODE_AE_MULAP24S_HH, + OPCODE_AE_MULSFS32P16S_LL, + OPCODE_AE_MULSFP24S_LL, + OPCODE_AE_MULSP24S_LL, + OPCODE_AE_MULSFS32P16S_LH, + OPCODE_AE_MULSFP24S_LH, + OPCODE_AE_MULSP24S_LH, + OPCODE_AE_MULSFS32P16S_HL, + OPCODE_AE_MULSFP24S_HL, + OPCODE_AE_MULSP24S_HL, + OPCODE_AE_MULSFS32P16S_HH, + OPCODE_AE_MULSFP24S_HH, + OPCODE_AE_MULSP24S_HH, + OPCODE_AE_MULAFS56P24S_LL, + OPCODE_AE_MULAS56P24S_LL, + OPCODE_AE_MULAFS56P24S_LH, + OPCODE_AE_MULAS56P24S_LH, + OPCODE_AE_MULAFS56P24S_HL, + OPCODE_AE_MULAS56P24S_HL, + OPCODE_AE_MULAFS56P24S_HH, + OPCODE_AE_MULAS56P24S_HH, + OPCODE_AE_MULSFS56P24S_LL, + OPCODE_AE_MULSS56P24S_LL, + OPCODE_AE_MULSFS56P24S_LH, + OPCODE_AE_MULSS56P24S_LH, + OPCODE_AE_MULSFS56P24S_HL, + OPCODE_AE_MULSS56P24S_HL, + OPCODE_AE_MULSFS56P24S_HH, + OPCODE_AE_MULSS56P24S_HH, + OPCODE_AE_MULFQ32SP16S_L, + OPCODE_AE_MULFQ32SP16S_H, + OPCODE_AE_MULFQ32SP16U_L, + OPCODE_AE_MULFQ32SP16U_H, + OPCODE_AE_MULQ32SP16S_L, + OPCODE_AE_MULQ32SP16S_H, + OPCODE_AE_MULQ32SP16U_L, + OPCODE_AE_MULQ32SP16U_H, + OPCODE_AE_MULAFQ32SP16S_L, + OPCODE_AE_MULAFQ32SP16S_H, + OPCODE_AE_MULAFQ32SP16U_L, + OPCODE_AE_MULAFQ32SP16U_H, + OPCODE_AE_MULAQ32SP16S_L, + OPCODE_AE_MULAQ32SP16S_H, + OPCODE_AE_MULAQ32SP16U_L, + OPCODE_AE_MULAQ32SP16U_H, + OPCODE_AE_MULSFQ32SP16S_L, + OPCODE_AE_MULSFQ32SP16S_H, + OPCODE_AE_MULSFQ32SP16U_L, + OPCODE_AE_MULSFQ32SP16U_H, + OPCODE_AE_MULSQ32SP16S_L, + OPCODE_AE_MULSQ32SP16S_H, + OPCODE_AE_MULSQ32SP16U_L, + OPCODE_AE_MULSQ32SP16U_H, + OPCODE_AE_MULZAAQ32SP16S_LL, + OPCODE_AE_MULZAAFQ32SP16S_LL, + OPCODE_AE_MULZAAQ32SP16U_LL, + OPCODE_AE_MULZAAFQ32SP16U_LL, + OPCODE_AE_MULZAAQ32SP16S_HH, + OPCODE_AE_MULZAAFQ32SP16S_HH, + OPCODE_AE_MULZAAQ32SP16U_HH, + OPCODE_AE_MULZAAFQ32SP16U_HH, + OPCODE_AE_MULZAAQ32SP16S_LH, + OPCODE_AE_MULZAAFQ32SP16S_LH, + OPCODE_AE_MULZAAQ32SP16U_LH, + OPCODE_AE_MULZAAFQ32SP16U_LH, + OPCODE_AE_MULZASQ32SP16S_LL, + OPCODE_AE_MULZASFQ32SP16S_LL, + OPCODE_AE_MULZASQ32SP16U_LL, + OPCODE_AE_MULZASFQ32SP16U_LL, + OPCODE_AE_MULZASQ32SP16S_HH, + OPCODE_AE_MULZASFQ32SP16S_HH, + OPCODE_AE_MULZASQ32SP16U_HH, + OPCODE_AE_MULZASFQ32SP16U_HH, + OPCODE_AE_MULZASQ32SP16S_LH, + OPCODE_AE_MULZASFQ32SP16S_LH, + OPCODE_AE_MULZASQ32SP16U_LH, + OPCODE_AE_MULZASFQ32SP16U_LH, + OPCODE_AE_MULZSAQ32SP16S_LL, + OPCODE_AE_MULZSAFQ32SP16S_LL, + OPCODE_AE_MULZSAQ32SP16U_LL, + OPCODE_AE_MULZSAFQ32SP16U_LL, + OPCODE_AE_MULZSAQ32SP16S_HH, + OPCODE_AE_MULZSAFQ32SP16S_HH, + OPCODE_AE_MULZSAQ32SP16U_HH, + OPCODE_AE_MULZSAFQ32SP16U_HH, + OPCODE_AE_MULZSAQ32SP16S_LH, + OPCODE_AE_MULZSAFQ32SP16S_LH, + OPCODE_AE_MULZSAQ32SP16U_LH, + OPCODE_AE_MULZSAFQ32SP16U_LH, + OPCODE_AE_MULZSSQ32SP16S_LL, + OPCODE_AE_MULZSSFQ32SP16S_LL, + OPCODE_AE_MULZSSQ32SP16U_LL, + OPCODE_AE_MULZSSFQ32SP16U_LL, + OPCODE_AE_MULZSSQ32SP16S_HH, + OPCODE_AE_MULZSSFQ32SP16S_HH, + OPCODE_AE_MULZSSQ32SP16U_HH, + OPCODE_AE_MULZSSFQ32SP16U_HH, + OPCODE_AE_MULZSSQ32SP16S_LH, + OPCODE_AE_MULZSSFQ32SP16S_LH, + OPCODE_AE_MULZSSQ32SP16U_LH, + OPCODE_AE_MULZSSFQ32SP16U_LH, + OPCODE_AE_MULZAAFP24S_HH_LL, + OPCODE_AE_MULZAAP24S_HH_LL, + OPCODE_AE_MULZAAFP24S_HL_LH, + OPCODE_AE_MULZAAP24S_HL_LH, + OPCODE_AE_MULZASFP24S_HH_LL, + OPCODE_AE_MULZASP24S_HH_LL, + OPCODE_AE_MULZASFP24S_HL_LH, + OPCODE_AE_MULZASP24S_HL_LH, + OPCODE_AE_MULZSAFP24S_HH_LL, + OPCODE_AE_MULZSAP24S_HH_LL, + OPCODE_AE_MULZSAFP24S_HL_LH, + OPCODE_AE_MULZSAP24S_HL_LH, + OPCODE_AE_MULZSSFP24S_HH_LL, + OPCODE_AE_MULZSSP24S_HH_LL, + OPCODE_AE_MULZSSFP24S_HL_LH, + OPCODE_AE_MULZSSP24S_HL_LH, + OPCODE_AE_MULAAFP24S_HH_LL, + OPCODE_AE_MULAAP24S_HH_LL, + OPCODE_AE_MULAAFP24S_HL_LH, + OPCODE_AE_MULAAP24S_HL_LH, + OPCODE_AE_MULASFP24S_HH_LL, + OPCODE_AE_MULASP24S_HH_LL, + OPCODE_AE_MULASFP24S_HL_LH, + OPCODE_AE_MULASP24S_HL_LH, + OPCODE_AE_MULSAFP24S_HH_LL, + OPCODE_AE_MULSAP24S_HH_LL, + OPCODE_AE_MULSAFP24S_HL_LH, + OPCODE_AE_MULSAP24S_HL_LH, + OPCODE_AE_MULSSFP24S_HH_LL, + OPCODE_AE_MULSSP24S_HH_LL, + OPCODE_AE_MULSSFP24S_HL_LH, + OPCODE_AE_MULSSP24S_HL_LH, + OPCODE_AE_SHA32, + OPCODE_AE_VLDL32T, + OPCODE_AE_VLDL16T, + OPCODE_AE_VLDL16C, + OPCODE_AE_VLDSHT, + OPCODE_AE_LB, + OPCODE_AE_LBI, + OPCODE_AE_LBK, + OPCODE_AE_LBKI, + OPCODE_AE_DB, + OPCODE_AE_DBI, + OPCODE_AE_VLEL32T, + OPCODE_AE_VLEL16T, + OPCODE_AE_SB, + OPCODE_AE_SBI, + OPCODE_AE_VLES16C, + OPCODE_AE_SBF, + OPCODE_AE_SLAASQ56S, + OPCODE_AE_ADDBRBA32, + OPCODE_AE_MINABSSP24S, + OPCODE_AE_MAXABSSP24S, + OPCODE_AE_MINABSSQ56S, + OPCODE_AE_MAXABSSQ56S, + OPCODE_RUR_AE_CBEGIN0, + OPCODE_WUR_AE_CBEGIN0, + OPCODE_RUR_AE_CEND0, + OPCODE_WUR_AE_CEND0, + OPCODE_AE_LP24X2_C, + OPCODE_AE_SP24X2S_C, + OPCODE_AE_LP24X2F_C, + OPCODE_AE_SP24X2F_C, + OPCODE_AE_LP16X2F_C, + OPCODE_AE_SP16X2F_C, + OPCODE_AE_LP24_C, + OPCODE_AE_SP24S_L_C, + OPCODE_AE_LP24F_C, + OPCODE_AE_SP24F_L_C, + OPCODE_AE_LP16F_C, + OPCODE_AE_SP16F_L_C, + OPCODE_AE_LQ56_C, + OPCODE_AE_SQ56S_C, + OPCODE_AE_LQ32F_C, + OPCODE_AE_SQ32F_C, + OPCODE_RUR_EXPSTATE, + OPCODE_WUR_EXPSTATE, + OPCODE_READ_IMPWIRE, + OPCODE_SETB_EXPSTATE, + OPCODE_CLRB_EXPSTATE, + OPCODE_WRMSK_EXPSTATE +}; + + +/* Slot-specific opcode decode functions. */ + +static int +Slot_inst_decode (const xtensa_insnbuf insn) +{ + if (Field_op0_Slot_inst_get (insn) == 0) + { + if (Field_op1_Slot_inst_get (insn) == 0) + { + if (Field_op2_Slot_inst_get (insn) == 0) + { + if (Field_r_Slot_inst_get (insn) == 0) + { + if (Field_m_Slot_inst_get (insn) == 0 && + Field_s_Slot_inst_get (insn) == 0 && + Field_n_Slot_inst_get (insn) == 0) + return OPCODE_ILL; + if (Field_m_Slot_inst_get (insn) == 2) + { + if (Field_n_Slot_inst_get (insn) == 0) + return OPCODE_RET; + if (Field_n_Slot_inst_get (insn) == 1) + return OPCODE_RETW; + if (Field_n_Slot_inst_get (insn) == 2) + return OPCODE_JX; + } + if (Field_m_Slot_inst_get (insn) == 3) + { + if (Field_n_Slot_inst_get (insn) == 0) + return OPCODE_CALLX0; + if (Field_n_Slot_inst_get (insn) == 1) + return OPCODE_CALLX4; + if (Field_n_Slot_inst_get (insn) == 2) + return OPCODE_CALLX8; + if (Field_n_Slot_inst_get (insn) == 3) + return OPCODE_CALLX12; + } + } + if (Field_r_Slot_inst_get (insn) == 1) + return OPCODE_MOVSP; + if (Field_r_Slot_inst_get (insn) == 2) + { + if (Field_s_Slot_inst_get (insn) == 0) + { + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_ISYNC; + if (Field_t_Slot_inst_get (insn) == 1) + return OPCODE_RSYNC; + if (Field_t_Slot_inst_get (insn) == 2) + return OPCODE_ESYNC; + if (Field_t_Slot_inst_get (insn) == 3) + return OPCODE_DSYNC; + if (Field_t_Slot_inst_get (insn) == 8) + return OPCODE_EXCW; + if (Field_t_Slot_inst_get (insn) == 12) + return OPCODE_MEMW; + if (Field_t_Slot_inst_get (insn) == 13) + return OPCODE_EXTW; + if (Field_t_Slot_inst_get (insn) == 15) + return OPCODE_NOP; + } + } + if (Field_r_Slot_inst_get (insn) == 3) + { + if (Field_t_Slot_inst_get (insn) == 0) + { + if (Field_s_Slot_inst_get (insn) == 0) + return OPCODE_RFE; + if (Field_s_Slot_inst_get (insn) == 2) + return OPCODE_RFDE; + if (Field_s_Slot_inst_get (insn) == 4) + return OPCODE_RFWO; + if (Field_s_Slot_inst_get (insn) == 5) + return OPCODE_RFWU; + } + if (Field_t_Slot_inst_get (insn) == 1) + return OPCODE_RFI; + } + if (Field_r_Slot_inst_get (insn) == 4) + return OPCODE_BREAK; + if (Field_r_Slot_inst_get (insn) == 5) + { + if (Field_s_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SYSCALL; + if (Field_s_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SIMCALL; + } + if (Field_r_Slot_inst_get (insn) == 6) + return OPCODE_RSIL; + if (Field_r_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_WAITI; + if (Field_r_Slot_inst_get (insn) == 7) + { + if (Field_t_Slot_inst_get (insn) == 14) + return OPCODE_LDDR32_P; + if (Field_t_Slot_inst_get (insn) == 15) + return OPCODE_SDDR32_P; + } + if (Field_r_Slot_inst_get (insn) == 8) + return OPCODE_ANY4; + if (Field_r_Slot_inst_get (insn) == 9) + return OPCODE_ALL4; + if (Field_r_Slot_inst_get (insn) == 10) + return OPCODE_ANY8; + if (Field_r_Slot_inst_get (insn) == 11) + return OPCODE_ALL8; + } + if (Field_op2_Slot_inst_get (insn) == 1) + return OPCODE_AND; + if (Field_op2_Slot_inst_get (insn) == 2) + return OPCODE_OR; + if (Field_op2_Slot_inst_get (insn) == 3) + return OPCODE_XOR; + if (Field_op2_Slot_inst_get (insn) == 4) + { + if (Field_r_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSR; + if (Field_r_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSL; + if (Field_r_Slot_inst_get (insn) == 2 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSA8L; + if (Field_r_Slot_inst_get (insn) == 3 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSA8B; + if (Field_r_Slot_inst_get (insn) == 4 && + Field_thi3_Slot_inst_get (insn) == 0) + return OPCODE_SSAI; + if (Field_r_Slot_inst_get (insn) == 6) + return OPCODE_RER; + if (Field_r_Slot_inst_get (insn) == 7) + return OPCODE_WER; + if (Field_r_Slot_inst_get (insn) == 8 && + Field_s_Slot_inst_get (insn) == 0) + return OPCODE_ROTW; + if (Field_r_Slot_inst_get (insn) == 14) + return OPCODE_NSA; + if (Field_r_Slot_inst_get (insn) == 15) + return OPCODE_NSAU; + } + if (Field_op2_Slot_inst_get (insn) == 5) + { + if (Field_r_Slot_inst_get (insn) == 1) + return OPCODE_HWWITLBA; + if (Field_r_Slot_inst_get (insn) == 3) + return OPCODE_RITLB0; + if (Field_r_Slot_inst_get (insn) == 4 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_IITLB; + if (Field_r_Slot_inst_get (insn) == 5) + return OPCODE_PITLB; + if (Field_r_Slot_inst_get (insn) == 6) + return OPCODE_WITLB; + if (Field_r_Slot_inst_get (insn) == 7) + return OPCODE_RITLB1; + if (Field_r_Slot_inst_get (insn) == 9) + return OPCODE_HWWDTLBA; + if (Field_r_Slot_inst_get (insn) == 11) + return OPCODE_RDTLB0; + if (Field_r_Slot_inst_get (insn) == 12 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_IDTLB; + if (Field_r_Slot_inst_get (insn) == 13) + return OPCODE_PDTLB; + if (Field_r_Slot_inst_get (insn) == 14) + return OPCODE_WDTLB; + if (Field_r_Slot_inst_get (insn) == 15) + return OPCODE_RDTLB1; + } + if (Field_op2_Slot_inst_get (insn) == 6) + { + if (Field_s_Slot_inst_get (insn) == 0) + return OPCODE_NEG; + if (Field_s_Slot_inst_get (insn) == 1) + return OPCODE_ABS; + } + if (Field_op2_Slot_inst_get (insn) == 8) + return OPCODE_ADD; + if (Field_op2_Slot_inst_get (insn) == 9) + return OPCODE_ADDX2; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_ADDX4; + if (Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_ADDX8; + if (Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_SUB; + if (Field_op2_Slot_inst_get (insn) == 13) + return OPCODE_SUBX2; + if (Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_SUBX4; + if (Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_SUBX8; + } + if (Field_op1_Slot_inst_get (insn) == 1) + { + if ((Field_op2_Slot_inst_get (insn) == 0 || + Field_op2_Slot_inst_get (insn) == 1)) + return OPCODE_SLLI; + if ((Field_op2_Slot_inst_get (insn) == 2 || + Field_op2_Slot_inst_get (insn) == 3)) + return OPCODE_SRAI; + if (Field_op2_Slot_inst_get (insn) == 4) + return OPCODE_SRLI; + if (Field_op2_Slot_inst_get (insn) == 6) + { + if (Field_sr_Slot_inst_get (insn) == 0) + return OPCODE_XSR_LBEG; + if (Field_sr_Slot_inst_get (insn) == 1) + return OPCODE_XSR_LEND; + if (Field_sr_Slot_inst_get (insn) == 2) + return OPCODE_XSR_LCOUNT; + if (Field_sr_Slot_inst_get (insn) == 3) + return OPCODE_XSR_SAR; + if (Field_sr_Slot_inst_get (insn) == 4) + return OPCODE_XSR_BR; + if (Field_sr_Slot_inst_get (insn) == 5) + return OPCODE_XSR_LITBASE; + if (Field_sr_Slot_inst_get (insn) == 12) + return OPCODE_XSR_SCOMPARE1; + if (Field_sr_Slot_inst_get (insn) == 16) + return OPCODE_XSR_ACCLO; + if (Field_sr_Slot_inst_get (insn) == 17) + return OPCODE_XSR_ACCHI; + if (Field_sr_Slot_inst_get (insn) == 32) + return OPCODE_XSR_M0; + if (Field_sr_Slot_inst_get (insn) == 33) + return OPCODE_XSR_M1; + if (Field_sr_Slot_inst_get (insn) == 34) + return OPCODE_XSR_M2; + if (Field_sr_Slot_inst_get (insn) == 35) + return OPCODE_XSR_M3; + if (Field_sr_Slot_inst_get (insn) == 40) + return OPCODE_XSR_PREFCTL; + if (Field_sr_Slot_inst_get (insn) == 72) + return OPCODE_XSR_WINDOWBASE; + if (Field_sr_Slot_inst_get (insn) == 73) + return OPCODE_XSR_WINDOWSTART; + if (Field_sr_Slot_inst_get (insn) == 83) + return OPCODE_XSR_PTEVADDR; + if (Field_sr_Slot_inst_get (insn) == 90) + return OPCODE_XSR_RASID; + if (Field_sr_Slot_inst_get (insn) == 91) + return OPCODE_XSR_ITLBCFG; + if (Field_sr_Slot_inst_get (insn) == 92) + return OPCODE_XSR_DTLBCFG; + if (Field_sr_Slot_inst_get (insn) == 96) + return OPCODE_XSR_IBREAKENABLE; + if (Field_sr_Slot_inst_get (insn) == 97) + return OPCODE_XSR_MEMCTL; + if (Field_sr_Slot_inst_get (insn) == 99) + return OPCODE_XSR_ATOMCTL; + if (Field_sr_Slot_inst_get (insn) == 104) + return OPCODE_XSR_DDR; + if (Field_sr_Slot_inst_get (insn) == 128) + return OPCODE_XSR_IBREAKA0; + if (Field_sr_Slot_inst_get (insn) == 129) + return OPCODE_XSR_IBREAKA1; + if (Field_sr_Slot_inst_get (insn) == 144) + return OPCODE_XSR_DBREAKA0; + if (Field_sr_Slot_inst_get (insn) == 145) + return OPCODE_XSR_DBREAKA1; + if (Field_sr_Slot_inst_get (insn) == 160) + return OPCODE_XSR_DBREAKC0; + if (Field_sr_Slot_inst_get (insn) == 161) + return OPCODE_XSR_DBREAKC1; + if (Field_sr_Slot_inst_get (insn) == 177) + return OPCODE_XSR_EPC1; + if (Field_sr_Slot_inst_get (insn) == 178) + return OPCODE_XSR_EPC2; + if (Field_sr_Slot_inst_get (insn) == 179) + return OPCODE_XSR_EPC3; + if (Field_sr_Slot_inst_get (insn) == 180) + return OPCODE_XSR_EPC4; + if (Field_sr_Slot_inst_get (insn) == 181) + return OPCODE_XSR_EPC5; + if (Field_sr_Slot_inst_get (insn) == 182) + return OPCODE_XSR_EPC6; + if (Field_sr_Slot_inst_get (insn) == 183) + return OPCODE_XSR_EPC7; + if (Field_sr_Slot_inst_get (insn) == 192) + return OPCODE_XSR_DEPC; + if (Field_sr_Slot_inst_get (insn) == 194) + return OPCODE_XSR_EPS2; + if (Field_sr_Slot_inst_get (insn) == 195) + return OPCODE_XSR_EPS3; + if (Field_sr_Slot_inst_get (insn) == 196) + return OPCODE_XSR_EPS4; + if (Field_sr_Slot_inst_get (insn) == 197) + return OPCODE_XSR_EPS5; + if (Field_sr_Slot_inst_get (insn) == 198) + return OPCODE_XSR_EPS6; + if (Field_sr_Slot_inst_get (insn) == 199) + return OPCODE_XSR_EPS7; + if (Field_sr_Slot_inst_get (insn) == 209) + return OPCODE_XSR_EXCSAVE1; + if (Field_sr_Slot_inst_get (insn) == 210) + return OPCODE_XSR_EXCSAVE2; + if (Field_sr_Slot_inst_get (insn) == 211) + return OPCODE_XSR_EXCSAVE3; + if (Field_sr_Slot_inst_get (insn) == 212) + return OPCODE_XSR_EXCSAVE4; + if (Field_sr_Slot_inst_get (insn) == 213) + return OPCODE_XSR_EXCSAVE5; + if (Field_sr_Slot_inst_get (insn) == 214) + return OPCODE_XSR_EXCSAVE6; + if (Field_sr_Slot_inst_get (insn) == 215) + return OPCODE_XSR_EXCSAVE7; + if (Field_sr_Slot_inst_get (insn) == 224) + return OPCODE_XSR_CPENABLE; + if (Field_sr_Slot_inst_get (insn) == 228) + return OPCODE_XSR_INTENABLE; + if (Field_sr_Slot_inst_get (insn) == 230) + return OPCODE_XSR_PS; + if (Field_sr_Slot_inst_get (insn) == 231) + return OPCODE_XSR_VECBASE; + if (Field_sr_Slot_inst_get (insn) == 232) + return OPCODE_XSR_EXCCAUSE; + if (Field_sr_Slot_inst_get (insn) == 233) + return OPCODE_XSR_DEBUGCAUSE; + if (Field_sr_Slot_inst_get (insn) == 234) + return OPCODE_XSR_CCOUNT; + if (Field_sr_Slot_inst_get (insn) == 236) + return OPCODE_XSR_ICOUNT; + if (Field_sr_Slot_inst_get (insn) == 237) + return OPCODE_XSR_ICOUNTLEVEL; + if (Field_sr_Slot_inst_get (insn) == 238) + return OPCODE_XSR_EXCVADDR; + if (Field_sr_Slot_inst_get (insn) == 240) + return OPCODE_XSR_CCOMPARE0; + if (Field_sr_Slot_inst_get (insn) == 241) + return OPCODE_XSR_CCOMPARE1; + if (Field_sr_Slot_inst_get (insn) == 242) + return OPCODE_XSR_CCOMPARE2; + if (Field_sr_Slot_inst_get (insn) == 244) + return OPCODE_XSR_MISC0; + if (Field_sr_Slot_inst_get (insn) == 245) + return OPCODE_XSR_MISC1; + } + if (Field_op2_Slot_inst_get (insn) == 8) + return OPCODE_SRC; + if (Field_op2_Slot_inst_get (insn) == 9 && + Field_s_Slot_inst_get (insn) == 0) + return OPCODE_SRL; + if (Field_op2_Slot_inst_get (insn) == 10 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SLL; + if (Field_op2_Slot_inst_get (insn) == 11 && + Field_s_Slot_inst_get (insn) == 0) + return OPCODE_SRA; + if (Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_MUL16U; + if (Field_op2_Slot_inst_get (insn) == 13) + return OPCODE_MUL16S; + if (Field_op2_Slot_inst_get (insn) == 15) + { + if (Field_r_Slot_inst_get (insn) == 0) + return OPCODE_LICT; + if (Field_r_Slot_inst_get (insn) == 1) + return OPCODE_SICT; + if (Field_r_Slot_inst_get (insn) == 2) + return OPCODE_LICW; + if (Field_r_Slot_inst_get (insn) == 3) + return OPCODE_SICW; + if (Field_r_Slot_inst_get (insn) == 8) + return OPCODE_LDCT; + if (Field_r_Slot_inst_get (insn) == 9) + return OPCODE_SDCT; + if (Field_r_Slot_inst_get (insn) == 14 && + Field_t_Slot_inst_get (insn) == 0) + return OPCODE_RFDO; + if (Field_r_Slot_inst_get (insn) == 14 && + Field_t_Slot_inst_get (insn) == 1) + return OPCODE_RFDD; + if (Field_r_Slot_inst_get (insn) == 15) + return OPCODE_LDPTE; + } + } + if (Field_op1_Slot_inst_get (insn) == 2) + { + if (Field_op2_Slot_inst_get (insn) == 0) + return OPCODE_ANDB; + if (Field_op2_Slot_inst_get (insn) == 1) + return OPCODE_ANDBC; + if (Field_op2_Slot_inst_get (insn) == 2) + return OPCODE_ORB; + if (Field_op2_Slot_inst_get (insn) == 3) + return OPCODE_ORBC; + if (Field_op2_Slot_inst_get (insn) == 4) + return OPCODE_XORB; + if (Field_op2_Slot_inst_get (insn) == 8) + return OPCODE_MULL; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_MULUH; + if (Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_MULSH; + if (Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_QUOU; + if (Field_op2_Slot_inst_get (insn) == 13) + return OPCODE_QUOS; + if (Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_REMU; + if (Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_REMS; + } + if (Field_op1_Slot_inst_get (insn) == 3) + { + if (Field_op2_Slot_inst_get (insn) == 0) + { + if (Field_sr_Slot_inst_get (insn) == 0) + return OPCODE_RSR_LBEG; + if (Field_sr_Slot_inst_get (insn) == 1) + return OPCODE_RSR_LEND; + if (Field_sr_Slot_inst_get (insn) == 2) + return OPCODE_RSR_LCOUNT; + if (Field_sr_Slot_inst_get (insn) == 3) + return OPCODE_RSR_SAR; + if (Field_sr_Slot_inst_get (insn) == 4) + return OPCODE_RSR_BR; + if (Field_sr_Slot_inst_get (insn) == 5) + return OPCODE_RSR_LITBASE; + if (Field_sr_Slot_inst_get (insn) == 12) + return OPCODE_RSR_SCOMPARE1; + if (Field_sr_Slot_inst_get (insn) == 16) + return OPCODE_RSR_ACCLO; + if (Field_sr_Slot_inst_get (insn) == 17) + return OPCODE_RSR_ACCHI; + if (Field_sr_Slot_inst_get (insn) == 32) + return OPCODE_RSR_M0; + if (Field_sr_Slot_inst_get (insn) == 33) + return OPCODE_RSR_M1; + if (Field_sr_Slot_inst_get (insn) == 34) + return OPCODE_RSR_M2; + if (Field_sr_Slot_inst_get (insn) == 35) + return OPCODE_RSR_M3; + if (Field_sr_Slot_inst_get (insn) == 40) + return OPCODE_RSR_PREFCTL; + if (Field_sr_Slot_inst_get (insn) == 72) + return OPCODE_RSR_WINDOWBASE; + if (Field_sr_Slot_inst_get (insn) == 73) + return OPCODE_RSR_WINDOWSTART; + if (Field_sr_Slot_inst_get (insn) == 83) + return OPCODE_RSR_PTEVADDR; + if (Field_sr_Slot_inst_get (insn) == 90) + return OPCODE_RSR_RASID; + if (Field_sr_Slot_inst_get (insn) == 91) + return OPCODE_RSR_ITLBCFG; + if (Field_sr_Slot_inst_get (insn) == 92) + return OPCODE_RSR_DTLBCFG; + if (Field_sr_Slot_inst_get (insn) == 96) + return OPCODE_RSR_IBREAKENABLE; + if (Field_sr_Slot_inst_get (insn) == 97) + return OPCODE_RSR_MEMCTL; + if (Field_sr_Slot_inst_get (insn) == 99) + return OPCODE_RSR_ATOMCTL; + if (Field_sr_Slot_inst_get (insn) == 104) + return OPCODE_RSR_DDR; + if (Field_sr_Slot_inst_get (insn) == 128) + return OPCODE_RSR_IBREAKA0; + if (Field_sr_Slot_inst_get (insn) == 129) + return OPCODE_RSR_IBREAKA1; + if (Field_sr_Slot_inst_get (insn) == 144) + return OPCODE_RSR_DBREAKA0; + if (Field_sr_Slot_inst_get (insn) == 145) + return OPCODE_RSR_DBREAKA1; + if (Field_sr_Slot_inst_get (insn) == 160) + return OPCODE_RSR_DBREAKC0; + if (Field_sr_Slot_inst_get (insn) == 161) + return OPCODE_RSR_DBREAKC1; + if (Field_sr_Slot_inst_get (insn) == 176) + return OPCODE_RSR_CONFIGID0; + if (Field_sr_Slot_inst_get (insn) == 177) + return OPCODE_RSR_EPC1; + if (Field_sr_Slot_inst_get (insn) == 178) + return OPCODE_RSR_EPC2; + if (Field_sr_Slot_inst_get (insn) == 179) + return OPCODE_RSR_EPC3; + if (Field_sr_Slot_inst_get (insn) == 180) + return OPCODE_RSR_EPC4; + if (Field_sr_Slot_inst_get (insn) == 181) + return OPCODE_RSR_EPC5; + if (Field_sr_Slot_inst_get (insn) == 182) + return OPCODE_RSR_EPC6; + if (Field_sr_Slot_inst_get (insn) == 183) + return OPCODE_RSR_EPC7; + if (Field_sr_Slot_inst_get (insn) == 192) + return OPCODE_RSR_DEPC; + if (Field_sr_Slot_inst_get (insn) == 194) + return OPCODE_RSR_EPS2; + if (Field_sr_Slot_inst_get (insn) == 195) + return OPCODE_RSR_EPS3; + if (Field_sr_Slot_inst_get (insn) == 196) + return OPCODE_RSR_EPS4; + if (Field_sr_Slot_inst_get (insn) == 197) + return OPCODE_RSR_EPS5; + if (Field_sr_Slot_inst_get (insn) == 198) + return OPCODE_RSR_EPS6; + if (Field_sr_Slot_inst_get (insn) == 199) + return OPCODE_RSR_EPS7; + if (Field_sr_Slot_inst_get (insn) == 208) + return OPCODE_RSR_CONFIGID1; + if (Field_sr_Slot_inst_get (insn) == 209) + return OPCODE_RSR_EXCSAVE1; + if (Field_sr_Slot_inst_get (insn) == 210) + return OPCODE_RSR_EXCSAVE2; + if (Field_sr_Slot_inst_get (insn) == 211) + return OPCODE_RSR_EXCSAVE3; + if (Field_sr_Slot_inst_get (insn) == 212) + return OPCODE_RSR_EXCSAVE4; + if (Field_sr_Slot_inst_get (insn) == 213) + return OPCODE_RSR_EXCSAVE5; + if (Field_sr_Slot_inst_get (insn) == 214) + return OPCODE_RSR_EXCSAVE6; + if (Field_sr_Slot_inst_get (insn) == 215) + return OPCODE_RSR_EXCSAVE7; + if (Field_sr_Slot_inst_get (insn) == 224) + return OPCODE_RSR_CPENABLE; + if (Field_sr_Slot_inst_get (insn) == 226) + return OPCODE_RSR_INTERRUPT; + if (Field_sr_Slot_inst_get (insn) == 228) + return OPCODE_RSR_INTENABLE; + if (Field_sr_Slot_inst_get (insn) == 230) + return OPCODE_RSR_PS; + if (Field_sr_Slot_inst_get (insn) == 231) + return OPCODE_RSR_VECBASE; + if (Field_sr_Slot_inst_get (insn) == 232) + return OPCODE_RSR_EXCCAUSE; + if (Field_sr_Slot_inst_get (insn) == 233) + return OPCODE_RSR_DEBUGCAUSE; + if (Field_sr_Slot_inst_get (insn) == 234) + return OPCODE_RSR_CCOUNT; + if (Field_sr_Slot_inst_get (insn) == 235) + return OPCODE_RSR_PRID; + if (Field_sr_Slot_inst_get (insn) == 236) + return OPCODE_RSR_ICOUNT; + if (Field_sr_Slot_inst_get (insn) == 237) + return OPCODE_RSR_ICOUNTLEVEL; + if (Field_sr_Slot_inst_get (insn) == 238) + return OPCODE_RSR_EXCVADDR; + if (Field_sr_Slot_inst_get (insn) == 240) + return OPCODE_RSR_CCOMPARE0; + if (Field_sr_Slot_inst_get (insn) == 241) + return OPCODE_RSR_CCOMPARE1; + if (Field_sr_Slot_inst_get (insn) == 242) + return OPCODE_RSR_CCOMPARE2; + if (Field_sr_Slot_inst_get (insn) == 243) + return OPCODE_RSR_243; + if (Field_sr_Slot_inst_get (insn) == 244) + return OPCODE_RSR_MISC0; + if (Field_sr_Slot_inst_get (insn) == 245) + return OPCODE_RSR_MISC1; + } + if (Field_op2_Slot_inst_get (insn) == 1) + { + if (Field_sr_Slot_inst_get (insn) == 0) + return OPCODE_WSR_LBEG; + if (Field_sr_Slot_inst_get (insn) == 1) + return OPCODE_WSR_LEND; + if (Field_sr_Slot_inst_get (insn) == 2) + return OPCODE_WSR_LCOUNT; + if (Field_sr_Slot_inst_get (insn) == 3) + return OPCODE_WSR_SAR; + if (Field_sr_Slot_inst_get (insn) == 4) + return OPCODE_WSR_BR; + if (Field_sr_Slot_inst_get (insn) == 5) + return OPCODE_WSR_LITBASE; + if (Field_sr_Slot_inst_get (insn) == 12) + return OPCODE_WSR_SCOMPARE1; + if (Field_sr_Slot_inst_get (insn) == 16) + return OPCODE_WSR_ACCLO; + if (Field_sr_Slot_inst_get (insn) == 17) + return OPCODE_WSR_ACCHI; + if (Field_sr_Slot_inst_get (insn) == 32) + return OPCODE_WSR_M0; + if (Field_sr_Slot_inst_get (insn) == 33) + return OPCODE_WSR_M1; + if (Field_sr_Slot_inst_get (insn) == 34) + return OPCODE_WSR_M2; + if (Field_sr_Slot_inst_get (insn) == 35) + return OPCODE_WSR_M3; + if (Field_sr_Slot_inst_get (insn) == 40) + return OPCODE_WSR_PREFCTL; + if (Field_sr_Slot_inst_get (insn) == 72) + return OPCODE_WSR_WINDOWBASE; + if (Field_sr_Slot_inst_get (insn) == 73) + return OPCODE_WSR_WINDOWSTART; + if (Field_sr_Slot_inst_get (insn) == 83) + return OPCODE_WSR_PTEVADDR; + if (Field_sr_Slot_inst_get (insn) == 89) + return OPCODE_WSR_MMID; + if (Field_sr_Slot_inst_get (insn) == 90) + return OPCODE_WSR_RASID; + if (Field_sr_Slot_inst_get (insn) == 91) + return OPCODE_WSR_ITLBCFG; + if (Field_sr_Slot_inst_get (insn) == 92) + return OPCODE_WSR_DTLBCFG; + if (Field_sr_Slot_inst_get (insn) == 96) + return OPCODE_WSR_IBREAKENABLE; + if (Field_sr_Slot_inst_get (insn) == 97) + return OPCODE_WSR_MEMCTL; + if (Field_sr_Slot_inst_get (insn) == 99) + return OPCODE_WSR_ATOMCTL; + if (Field_sr_Slot_inst_get (insn) == 104) + return OPCODE_WSR_DDR; + if (Field_sr_Slot_inst_get (insn) == 128) + return OPCODE_WSR_IBREAKA0; + if (Field_sr_Slot_inst_get (insn) == 129) + return OPCODE_WSR_IBREAKA1; + if (Field_sr_Slot_inst_get (insn) == 144) + return OPCODE_WSR_DBREAKA0; + if (Field_sr_Slot_inst_get (insn) == 145) + return OPCODE_WSR_DBREAKA1; + if (Field_sr_Slot_inst_get (insn) == 160) + return OPCODE_WSR_DBREAKC0; + if (Field_sr_Slot_inst_get (insn) == 161) + return OPCODE_WSR_DBREAKC1; + if (Field_sr_Slot_inst_get (insn) == 176) + return OPCODE_WSR_CONFIGID0; + if (Field_sr_Slot_inst_get (insn) == 177) + return OPCODE_WSR_EPC1; + if (Field_sr_Slot_inst_get (insn) == 178) + return OPCODE_WSR_EPC2; + if (Field_sr_Slot_inst_get (insn) == 179) + return OPCODE_WSR_EPC3; + if (Field_sr_Slot_inst_get (insn) == 180) + return OPCODE_WSR_EPC4; + if (Field_sr_Slot_inst_get (insn) == 181) + return OPCODE_WSR_EPC5; + if (Field_sr_Slot_inst_get (insn) == 182) + return OPCODE_WSR_EPC6; + if (Field_sr_Slot_inst_get (insn) == 183) + return OPCODE_WSR_EPC7; + if (Field_sr_Slot_inst_get (insn) == 192) + return OPCODE_WSR_DEPC; + if (Field_sr_Slot_inst_get (insn) == 194) + return OPCODE_WSR_EPS2; + if (Field_sr_Slot_inst_get (insn) == 195) + return OPCODE_WSR_EPS3; + if (Field_sr_Slot_inst_get (insn) == 196) + return OPCODE_WSR_EPS4; + if (Field_sr_Slot_inst_get (insn) == 197) + return OPCODE_WSR_EPS5; + if (Field_sr_Slot_inst_get (insn) == 198) + return OPCODE_WSR_EPS6; + if (Field_sr_Slot_inst_get (insn) == 199) + return OPCODE_WSR_EPS7; + if (Field_sr_Slot_inst_get (insn) == 209) + return OPCODE_WSR_EXCSAVE1; + if (Field_sr_Slot_inst_get (insn) == 210) + return OPCODE_WSR_EXCSAVE2; + if (Field_sr_Slot_inst_get (insn) == 211) + return OPCODE_WSR_EXCSAVE3; + if (Field_sr_Slot_inst_get (insn) == 212) + return OPCODE_WSR_EXCSAVE4; + if (Field_sr_Slot_inst_get (insn) == 213) + return OPCODE_WSR_EXCSAVE5; + if (Field_sr_Slot_inst_get (insn) == 214) + return OPCODE_WSR_EXCSAVE6; + if (Field_sr_Slot_inst_get (insn) == 215) + return OPCODE_WSR_EXCSAVE7; + if (Field_sr_Slot_inst_get (insn) == 224) + return OPCODE_WSR_CPENABLE; + if (Field_sr_Slot_inst_get (insn) == 226) + return OPCODE_WSR_INTSET; + if (Field_sr_Slot_inst_get (insn) == 227) + return OPCODE_WSR_INTCLEAR; + if (Field_sr_Slot_inst_get (insn) == 228) + return OPCODE_WSR_INTENABLE; + if (Field_sr_Slot_inst_get (insn) == 230) + return OPCODE_WSR_PS; + if (Field_sr_Slot_inst_get (insn) == 231) + return OPCODE_WSR_VECBASE; + if (Field_sr_Slot_inst_get (insn) == 232) + return OPCODE_WSR_EXCCAUSE; + if (Field_sr_Slot_inst_get (insn) == 233) + return OPCODE_WSR_DEBUGCAUSE; + if (Field_sr_Slot_inst_get (insn) == 234) + return OPCODE_WSR_CCOUNT; + if (Field_sr_Slot_inst_get (insn) == 236) + return OPCODE_WSR_ICOUNT; + if (Field_sr_Slot_inst_get (insn) == 237) + return OPCODE_WSR_ICOUNTLEVEL; + if (Field_sr_Slot_inst_get (insn) == 238) + return OPCODE_WSR_EXCVADDR; + if (Field_sr_Slot_inst_get (insn) == 240) + return OPCODE_WSR_CCOMPARE0; + if (Field_sr_Slot_inst_get (insn) == 241) + return OPCODE_WSR_CCOMPARE1; + if (Field_sr_Slot_inst_get (insn) == 242) + return OPCODE_WSR_CCOMPARE2; + if (Field_sr_Slot_inst_get (insn) == 244) + return OPCODE_WSR_MISC0; + if (Field_sr_Slot_inst_get (insn) == 245) + return OPCODE_WSR_MISC1; + } + if (Field_op2_Slot_inst_get (insn) == 2) + return OPCODE_SEXT; + if (Field_op2_Slot_inst_get (insn) == 3) + return OPCODE_CLAMPS; + if (Field_op2_Slot_inst_get (insn) == 4) + return OPCODE_MIN; + if (Field_op2_Slot_inst_get (insn) == 5) + return OPCODE_MAX; + if (Field_op2_Slot_inst_get (insn) == 6) + return OPCODE_MINU; + if (Field_op2_Slot_inst_get (insn) == 7) + return OPCODE_MAXU; + if (Field_op2_Slot_inst_get (insn) == 8) + return OPCODE_MOVEQZ; + if (Field_op2_Slot_inst_get (insn) == 9) + return OPCODE_MOVNEZ; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_MOVLTZ; + if (Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_MOVGEZ; + if (Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_MOVF; + if (Field_op2_Slot_inst_get (insn) == 13) + return OPCODE_MOVT; + if (Field_op2_Slot_inst_get (insn) == 14) + { + if (Field_st_Slot_inst_get (insn) == 230) + return OPCODE_RUR_EXPSTATE; + if (Field_st_Slot_inst_get (insn) == 231) + return OPCODE_RUR_THREADPTR; + if (Field_st_Slot_inst_get (insn) == 240) + return OPCODE_RUR_AE_OVF_SAR; + if (Field_st_Slot_inst_get (insn) == 241) + return OPCODE_RUR_AE_BITHEAD; + if (Field_st_Slot_inst_get (insn) == 242) + return OPCODE_RUR_AE_TS_FTS_BU_BP; + if (Field_st_Slot_inst_get (insn) == 243) + return OPCODE_RUR_AE_SD_NO; + if (Field_st_Slot_inst_get (insn) == 246) + return OPCODE_RUR_AE_CBEGIN0; + if (Field_st_Slot_inst_get (insn) == 247) + return OPCODE_RUR_AE_CEND0; + } + if (Field_op2_Slot_inst_get (insn) == 15) + { + if (Field_sr_Slot_inst_get (insn) == 230) + return OPCODE_WUR_EXPSTATE; + if (Field_sr_Slot_inst_get (insn) == 231) + return OPCODE_WUR_THREADPTR; + if (Field_sr_Slot_inst_get (insn) == 240) + return OPCODE_WUR_AE_OVF_SAR; + if (Field_sr_Slot_inst_get (insn) == 241) + return OPCODE_WUR_AE_BITHEAD; + if (Field_sr_Slot_inst_get (insn) == 242) + return OPCODE_WUR_AE_TS_FTS_BU_BP; + if (Field_sr_Slot_inst_get (insn) == 243) + return OPCODE_WUR_AE_SD_NO; + if (Field_sr_Slot_inst_get (insn) == 246) + return OPCODE_WUR_AE_CBEGIN0; + if (Field_sr_Slot_inst_get (insn) == 247) + return OPCODE_WUR_AE_CEND0; + } + } + if ((Field_op1_Slot_inst_get (insn) == 4 || + Field_op1_Slot_inst_get (insn) == 5)) + return OPCODE_EXTUI; + if (Field_op1_Slot_inst_get (insn) == 9) + { + if (Field_op2_Slot_inst_get (insn) == 0) + return OPCODE_L32E; + if (Field_op2_Slot_inst_get (insn) == 4) + return OPCODE_S32E; + if (Field_op2_Slot_inst_get (insn) == 5) + return OPCODE_S32NB; + } + if (Field_r_Slot_inst_get (insn) == 0 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 14) + return OPCODE_READ_IMPWIRE; + if (Field_r_Slot_inst_get (insn) == 1 && + Field_s3to1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 14) + return OPCODE_SETB_EXPSTATE; + if (Field_r_Slot_inst_get (insn) == 1 && + Field_s3to1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 14) + return OPCODE_CLRB_EXPSTATE; + if (Field_r_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 14) + return OPCODE_WRMSK_EXPSTATE; + } + if (Field_op0_Slot_inst_get (insn) == 1) + return OPCODE_L32R; + if (Field_op0_Slot_inst_get (insn) == 2) + { + if (Field_r_Slot_inst_get (insn) == 0) + return OPCODE_L8UI; + if (Field_r_Slot_inst_get (insn) == 1) + return OPCODE_L16UI; + if (Field_r_Slot_inst_get (insn) == 2) + return OPCODE_L32I; + if (Field_r_Slot_inst_get (insn) == 4) + return OPCODE_S8I; + if (Field_r_Slot_inst_get (insn) == 5) + return OPCODE_S16I; + if (Field_r_Slot_inst_get (insn) == 6) + return OPCODE_S32I; + if (Field_r_Slot_inst_get (insn) == 7) + { + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_DPFR; + if (Field_t_Slot_inst_get (insn) == 1) + return OPCODE_DPFW; + if (Field_t_Slot_inst_get (insn) == 2) + return OPCODE_DPFRO; + if (Field_t_Slot_inst_get (insn) == 3) + return OPCODE_DPFWO; + if (Field_t_Slot_inst_get (insn) == 4) + return OPCODE_DHWB; + if (Field_t_Slot_inst_get (insn) == 5) + return OPCODE_DHWBI; + if (Field_t_Slot_inst_get (insn) == 6) + return OPCODE_DHI; + if (Field_t_Slot_inst_get (insn) == 7) + return OPCODE_DII; + if (Field_t_Slot_inst_get (insn) == 8) + { + if (Field_op1_Slot_inst_get (insn) == 0) + return OPCODE_DPFL; + if (Field_op1_Slot_inst_get (insn) == 2) + return OPCODE_DHU; + if (Field_op1_Slot_inst_get (insn) == 3) + return OPCODE_DIU; + if (Field_op1_Slot_inst_get (insn) == 4) + return OPCODE_DIWB; + if (Field_op1_Slot_inst_get (insn) == 5) + return OPCODE_DIWBI; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 0) + return OPCODE_DIWBUI_P; + } + if (Field_t_Slot_inst_get (insn) == 12) + return OPCODE_IPF; + if (Field_t_Slot_inst_get (insn) == 13) + { + if (Field_op1_Slot_inst_get (insn) == 0) + return OPCODE_IPFL; + if (Field_op1_Slot_inst_get (insn) == 2) + return OPCODE_IHU; + if (Field_op1_Slot_inst_get (insn) == 3) + return OPCODE_IIU; + } + if (Field_t_Slot_inst_get (insn) == 14) + return OPCODE_IHI; + if (Field_t_Slot_inst_get (insn) == 15) + return OPCODE_III; + } + if (Field_r_Slot_inst_get (insn) == 9) + return OPCODE_L16SI; + if (Field_r_Slot_inst_get (insn) == 10) + return OPCODE_MOVI; + if (Field_r_Slot_inst_get (insn) == 11) + return OPCODE_L32AI; + if (Field_r_Slot_inst_get (insn) == 12) + return OPCODE_ADDI; + if (Field_r_Slot_inst_get (insn) == 13) + return OPCODE_ADDMI; + if (Field_r_Slot_inst_get (insn) == 14) + return OPCODE_S32C1I; + if (Field_r_Slot_inst_get (insn) == 15) + return OPCODE_S32RI; + } + if (Field_op0_Slot_inst_get (insn) == 4) + { + if (Field_ae_r10_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_I; + if (Field_ae_r10_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_X; + if (Field_ae_r10_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_I; + if (Field_ae_r10_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_X; + if (Field_ae_r10_Slot_inst_get (insn) == 2 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_IU; + if (Field_ae_r10_Slot_inst_get (insn) == 2 && + Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_XU; + if (Field_ae_r10_Slot_inst_get (insn) == 2 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_CVTQ48A32S; + if (Field_ae_r10_Slot_inst_get (insn) == 3 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_IU; + if (Field_ae_r10_Slot_inst_get (insn) == 3 && + Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_I; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_X; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24F_I; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24F_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24F_X; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP24F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2F_I; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2F_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2F_X; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP24X2F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_I; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_X; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_I; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_X; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_I; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_X; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_ae_s3_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVP48; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVPA24X2; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_CVTA32P24_L; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_LL; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_HL; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVAP24S_L; + if (Field_ae_r3_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_TRUNCA16P24S_L; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_I; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_X; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16X2F_I; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16X2F_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16X2F_X; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP16X2F_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2_I; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2_X; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP24X2_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_I; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_X; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_I; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_X; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_I; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_IU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_X; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_XU; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_TRUNCP24A32X2; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_CVTA32P24_H; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_LH; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_HH; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVAP24S_H; + if (Field_ae_r3_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_TRUNCA16P24S_H; + if (Field_ae_r32_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_I; + if (Field_ae_r32_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_X; + if (Field_ae_r32_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_TRUNCA32Q48; + if (Field_ae_r32_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_I; + if (Field_ae_r32_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_X; + if (Field_ae_r32_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_NSAQ56S; + if (Field_ae_r32_Slot_inst_get (insn) == 2 && + Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_IU; + if (Field_ae_r32_Slot_inst_get (insn) == 2 && + Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_XU; + if (Field_ae_r32_Slot_inst_get (insn) == 3 && + Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_IU; + if (Field_ae_r32_Slot_inst_get (insn) == 3 && + Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_XU; + if (Field_ae_s_non_samt_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLIQ56; + if (Field_ae_s_non_samt_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRLIQ56; + if (Field_ae_s_non_samt_Slot_inst_get (insn) == 2 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRAIQ56; + if (Field_ae_s_non_samt_Slot_inst_get (insn) == 3 && + Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLISQ56S; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SHA32; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLDL32T; + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SLLAQ56; + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLDL16T; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SRLAQ56; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LBK; + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SRAAQ56; + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLEL32T; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SLLASQ56S; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLEL16T; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_MOVTQ56; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_MOVFQ56; + if (Field_op2_Slot_inst_get (insn) == 0) + { + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_LL_LDINC; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_HL_LDINC; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_LH_LDINC; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_HH_LDINC; + } + if (Field_op2_Slot_inst_get (insn) == 1) + { + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_LL_LDDEC; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_HL_LDDEC; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_LH_LDDEC; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_HH_LDDEC; + } + if (Field_op2_Slot_inst_get (insn) == 2) + { + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DD_LL; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DD_HL; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DD_LH; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DD_HH; + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_LL; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_HL; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_LH; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DD_HH; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DD_LL; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DD_HL; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DD_LH; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DD_HH; + } + if (Field_op2_Slot_inst_get (insn) == 3) + { + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AD_LL; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AD_HL; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AD_LH; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AD_HH; + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AD_LL; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AD_HL; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AD_LH; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AD_HH; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AD_LL; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AD_HL; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AD_LH; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_r_Slot_inst_get (insn) == 0 && + Field_t3_Slot_inst_get (insn) == 0 && + Field_tlo_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AD_HH; + } + if (Field_op2_Slot_inst_get (insn) == 4) + { + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_LL_LDINC; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_HL_LDINC; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_LH_LDINC; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_HH_LDINC; + } + if (Field_op2_Slot_inst_get (insn) == 5) + { + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_LL_LDDEC; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_HL_LDDEC; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_LH_LDDEC; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_HH_LDDEC; + } + if (Field_op2_Slot_inst_get (insn) == 6) + { + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DA_LL; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DA_HL; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DA_LH; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MUL_DA_HH; + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_LL; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_HL; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_LH; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULA_DA_HH; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DA_LL; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DA_HL; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DA_LH; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_s_Slot_inst_get (insn) == 0 && + Field_w_Slot_inst_get (insn) == 0 && + Field_r3_Slot_inst_get (insn) == 0) + return OPCODE_MULS_DA_HH; + } + if (Field_op2_Slot_inst_get (insn) == 7) + { + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_UMUL_AA_LL; + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_UMUL_AA_HL; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_UMUL_AA_LH; + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_UMUL_AA_HH; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AA_LL; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AA_HL; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AA_LH; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MUL_AA_HH; + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AA_LL; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AA_HL; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AA_LH; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULA_AA_HH; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AA_LL; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AA_HL; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AA_LH; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_r_Slot_inst_get (insn) == 0) + return OPCODE_MULS_AA_HH; + } + if (Field_op2_Slot_inst_get (insn) == 8) + { + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_rhi_Slot_inst_get (insn) == 0) + return OPCODE_LDINC; + } + if (Field_op2_Slot_inst_get (insn) == 9) + { + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_rhi_Slot_inst_get (insn) == 0) + return OPCODE_LDDEC; + } + if (Field_r_Slot_inst_get (insn) == 0 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_OVERFLOW; + if (Field_r_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_SBI; + if (Field_r_Slot_inst_get (insn) == 1 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_SAR; + if (Field_r_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_DB; + if (Field_r_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_SB; + if (Field_r_Slot_inst_get (insn) == 2 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_BITPTR; + if (Field_r_Slot_inst_get (insn) == 3 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_BITSUSED; + if (Field_r_Slot_inst_get (insn) == 4 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_TABLESIZE; + if (Field_r_Slot_inst_get (insn) == 5 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_FIRST_TS; + if (Field_r_Slot_inst_get (insn) == 6 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_NEXTOFFSET; + if (Field_r_Slot_inst_get (insn) == 7 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_SEARCHDONE; + if (Field_r_Slot_inst_get (insn) == 8 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_VLDSHT; + if (Field_r_Slot_inst_get (insn) == 12 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_VLES16C; + if (Field_r_Slot_inst_get (insn) == 13 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SBF; + if (Field_r_Slot_inst_get (insn) == 14 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_VLDL16C; + if (Field_s_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLSQ56; + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LB; + if (Field_s_Slot_inst_get (insn) == 1 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRLSQ56; + if (Field_s_Slot_inst_get (insn) == 2 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRASQ56; + if (Field_s_Slot_inst_get (insn) == 3 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLSSQ56S; + if (Field_s_Slot_inst_get (insn) == 4 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVQ56; + if (Field_s_Slot_inst_get (insn) == 8 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_OVERFLOW; + if (Field_s_Slot_inst_get (insn) == 9 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_SAR; + if (Field_s_Slot_inst_get (insn) == 10 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_BITPTR; + if (Field_s_Slot_inst_get (insn) == 11 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_BITSUSED; + if (Field_s_Slot_inst_get (insn) == 12 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_TABLESIZE; + if (Field_s_Slot_inst_get (insn) == 13 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_FIRST_TS; + if (Field_s_Slot_inst_get (insn) == 14 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_NEXTOFFSET; + if (Field_s_Slot_inst_get (insn) == 15 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_SEARCHDONE; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_LBKI; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_r_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_DBI; + if (Field_t_Slot_inst_get (insn) == 2 && + Field_s_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_LBI; + } + if (Field_op0_Slot_inst_get (insn) == 5) + { + if (Field_n_Slot_inst_get (insn) == 0) + return OPCODE_CALL0; + if (Field_n_Slot_inst_get (insn) == 1) + return OPCODE_CALL4; + if (Field_n_Slot_inst_get (insn) == 2) + return OPCODE_CALL8; + if (Field_n_Slot_inst_get (insn) == 3) + return OPCODE_CALL12; + } + if (Field_op0_Slot_inst_get (insn) == 6) + { + if (Field_n_Slot_inst_get (insn) == 0) + return OPCODE_J; + if (Field_n_Slot_inst_get (insn) == 1) + { + if (Field_m_Slot_inst_get (insn) == 0) + return OPCODE_BEQZ; + if (Field_m_Slot_inst_get (insn) == 1) + return OPCODE_BNEZ; + if (Field_m_Slot_inst_get (insn) == 2) + return OPCODE_BLTZ; + if (Field_m_Slot_inst_get (insn) == 3) + return OPCODE_BGEZ; + } + if (Field_n_Slot_inst_get (insn) == 2) + { + if (Field_m_Slot_inst_get (insn) == 0) + return OPCODE_BEQI; + if (Field_m_Slot_inst_get (insn) == 1) + return OPCODE_BNEI; + if (Field_m_Slot_inst_get (insn) == 2) + return OPCODE_BLTI; + if (Field_m_Slot_inst_get (insn) == 3) + return OPCODE_BGEI; + } + if (Field_n_Slot_inst_get (insn) == 3) + { + if (Field_m_Slot_inst_get (insn) == 0) + return OPCODE_ENTRY; + if (Field_m_Slot_inst_get (insn) == 1) + { + if (Field_r_Slot_inst_get (insn) == 0) + return OPCODE_BF; + if (Field_r_Slot_inst_get (insn) == 1) + return OPCODE_BT; + if (Field_r_Slot_inst_get (insn) == 8) + return OPCODE_LOOP; + if (Field_r_Slot_inst_get (insn) == 9) + return OPCODE_LOOPNEZ; + if (Field_r_Slot_inst_get (insn) == 10) + return OPCODE_LOOPGTZ; + } + if (Field_m_Slot_inst_get (insn) == 2) + return OPCODE_BLTUI; + if (Field_m_Slot_inst_get (insn) == 3) + return OPCODE_BGEUI; + } + } + if (Field_op0_Slot_inst_get (insn) == 7) + { + if (Field_r_Slot_inst_get (insn) == 0) + return OPCODE_BNONE; + if (Field_r_Slot_inst_get (insn) == 1) + return OPCODE_BEQ; + if (Field_r_Slot_inst_get (insn) == 2) + return OPCODE_BLT; + if (Field_r_Slot_inst_get (insn) == 3) + return OPCODE_BLTU; + if (Field_r_Slot_inst_get (insn) == 4) + return OPCODE_BALL; + if (Field_r_Slot_inst_get (insn) == 5) + return OPCODE_BBC; + if ((Field_r_Slot_inst_get (insn) == 6 || + Field_r_Slot_inst_get (insn) == 7)) + return OPCODE_BBCI; + if (Field_r_Slot_inst_get (insn) == 8) + return OPCODE_BANY; + if (Field_r_Slot_inst_get (insn) == 9) + return OPCODE_BNE; + if (Field_r_Slot_inst_get (insn) == 10) + return OPCODE_BGE; + if (Field_r_Slot_inst_get (insn) == 11) + return OPCODE_BGEU; + if (Field_r_Slot_inst_get (insn) == 12) + return OPCODE_BNALL; + if (Field_r_Slot_inst_get (insn) == 13) + return OPCODE_BBS; + if ((Field_r_Slot_inst_get (insn) == 14 || + Field_r_Slot_inst_get (insn) == 15)) + return OPCODE_BBSI; + } + return XTENSA_UNDEFINED; +} + +static int +Slot_inst16b_decode (const xtensa_insnbuf insn) +{ + if (Field_op0_Slot_inst16b_get (insn) == 12) + { + if (Field_i_Slot_inst16b_get (insn) == 0) + return OPCODE_MOVI_N; + if (Field_i_Slot_inst16b_get (insn) == 1) + { + if (Field_z_Slot_inst16b_get (insn) == 0) + return OPCODE_BEQZ_N; + if (Field_z_Slot_inst16b_get (insn) == 1) + return OPCODE_BNEZ_N; + } + } + if (Field_op0_Slot_inst16b_get (insn) == 13) + { + if (Field_r_Slot_inst16b_get (insn) == 0) + return OPCODE_MOV_N; + if (Field_r_Slot_inst16b_get (insn) == 15) + { + if (Field_t_Slot_inst16b_get (insn) == 0) + return OPCODE_RET_N; + if (Field_t_Slot_inst16b_get (insn) == 1) + return OPCODE_RETW_N; + if (Field_t_Slot_inst16b_get (insn) == 2) + return OPCODE_BREAK_N; + if (Field_t_Slot_inst16b_get (insn) == 3 && + Field_s_Slot_inst16b_get (insn) == 0) + return OPCODE_NOP_N; + if (Field_t_Slot_inst16b_get (insn) == 6 && + Field_s_Slot_inst16b_get (insn) == 0) + return OPCODE_ILL_N; + } + } + return XTENSA_UNDEFINED; +} + +static int +Slot_inst16a_decode (const xtensa_insnbuf insn) +{ + if (Field_op0_Slot_inst16a_get (insn) == 8) + return OPCODE_L32I_N; + if (Field_op0_Slot_inst16a_get (insn) == 9) + return OPCODE_S32I_N; + if (Field_op0_Slot_inst16a_get (insn) == 10) + return OPCODE_ADD_N; + if (Field_op0_Slot_inst16a_get (insn) == 11) + return OPCODE_ADDI_N; + return XTENSA_UNDEFINED; +} + +static int +Slot_ae_slot0_decode (const xtensa_insnbuf insn) +{ + if (Field_ae_s20_Slot_ae_slot0_get (insn) == 3 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3 && + Field_ftsf378ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_BT; + if (Field_combined1e9fefee_fld106ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined1e9fefee_fld96_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld98_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 0 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_s4_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SELP24_HH; + if (Field_combined1e9fefee_fld107ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld96_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld98_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 0 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_s4_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SELP24_HL; + if (Field_combined1e9fefee_fld108ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld96_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld98_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 0 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_s4_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld47_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SELP24_LH; + if (Field_combined2c0b5f72_fld123_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld121_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf363ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_ftsf315_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 2 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLAASQ56S; + if (Field_combined2c0b5f72_fld133ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_C; + if (Field_combined2c0b5f72_fld134ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_C; + if (Field_combined2c0b5f72_fld135ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_C; + if (Field_combined2c0b5f72_fld136ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_C; + if (Field_combined2c0b5f72_fld137ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_C; + if (Field_combined2c0b5f72_fld138ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_C; + if (Field_combined2c0b5f72_fld139ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_C; + if (Field_combined2c0b5f72_fld140ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_C; + if (Field_combined2c0b5f72_fld141ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_C; + if (Field_combined2c0b5f72_fld142ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_C; + if (Field_combined2c0b5f72_fld143ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_C; + if (Field_combined2c0b5f72_fld144ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_C; + if (Field_combined2c0b5f72_fld145ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld46_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SQ32F_C; + if (Field_combined2c0b5f72_fld146ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld46_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SQ56S_C; + if (Field_combined2c0b5f72_fld52_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld96_Slot_ae_slot0_get (insn) == 1 && + Field_combined1e9fefee_fld98_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 0 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_s4_Slot_ae_slot0_get (insn) == 0 && + Field_combined1e9fefee_fld109ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SELP24_LL; + if (Field_ftsf211ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_J; + if (Field_ftsf212ae_slot0_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_EXTUI; + if (Field_ftsf213ae_slot0_Slot_ae_slot0_get (insn) == 2 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BEQZ; + if (Field_ftsf213ae_slot0_Slot_ae_slot0_get (insn) == 3 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BNEZ; + if (Field_ftsf213ae_slot0_Slot_ae_slot0_get (insn) == 5 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BGEZ; + if (Field_ftsf213ae_slot0_Slot_ae_slot0_get (insn) == 6 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVI; + if (Field_ftsf213ae_slot0_Slot_ae_slot0_get (insn) == 13 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BLTZ; + if (Field_ftsf214ae_slot0_Slot_ae_slot0_get (insn) == 7 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRAI; + if (Field_ftsf214ae_slot0_Slot_ae_slot0_get (insn) == 10 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SLLI; + if (Field_ftsf215ae_slot0_Slot_ae_slot0_get (insn) == 43 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_HH; + if (Field_ftsf217ae_slot0_Slot_ae_slot0_get (insn) == 299 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_I; + if (Field_ftsf218ae_slot0_Slot_ae_slot0_get (insn) == 46 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_HL; + if (Field_ftsf219ae_slot0_Slot_ae_slot0_get (insn) == 47 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_IU; + if (Field_ftsf220ae_slot0_Slot_ae_slot0_get (insn) == 302 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_X; + if (Field_ftsf221ae_slot0_Slot_ae_slot0_get (insn) == 303 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_XU; + if (Field_ftsf222ae_slot0_Slot_ae_slot0_get (insn) == 58 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_LH; + if (Field_ftsf223ae_slot0_Slot_ae_slot0_get (insn) == 59 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_I; + if (Field_ftsf224ae_slot0_Slot_ae_slot0_get (insn) == 62 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_IU; + if (Field_ftsf225ae_slot0_Slot_ae_slot0_get (insn) == 63 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_XU; + if (Field_ftsf226ae_slot0_Slot_ae_slot0_get (insn) == 314 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_X; + if (Field_ftsf227ae_slot0_Slot_ae_slot0_get (insn) == 315 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_I; + if (Field_ftsf228ae_slot0_Slot_ae_slot0_get (insn) == 318 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_IU; + if (Field_ftsf229ae_slot0_Slot_ae_slot0_get (insn) == 319 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_X; + if (Field_ftsf230ae_slot0_Slot_ae_slot0_get (insn) == 170 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_LL; + if (Field_ftsf231ae_slot0_Slot_ae_slot0_get (insn) == 171 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_XU; + if (Field_ftsf232ae_slot0_Slot_ae_slot0_get (insn) == 174 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_I; + if (Field_ftsf233ae_slot0_Slot_ae_slot0_get (insn) == 175 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_XU; + if (Field_ftsf234ae_slot0_Slot_ae_slot0_get (insn) == 186 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_IU; + if (Field_ftsf235ae_slot0_Slot_ae_slot0_get (insn) == 187 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_I; + if (Field_ftsf236ae_slot0_Slot_ae_slot0_get (insn) == 190 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_IU; + if (Field_ftsf237ae_slot0_Slot_ae_slot0_get (insn) == 191 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_X; + if (Field_ftsf238ae_slot0_Slot_ae_slot0_get (insn) == 426 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_X; + if (Field_ftsf239ae_slot0_Slot_ae_slot0_get (insn) == 427 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_XU; + if (Field_ftsf240ae_slot0_Slot_ae_slot0_get (insn) == 430 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_I; + if (Field_ftsf241ae_slot0_Slot_ae_slot0_get (insn) == 431 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_X; + if (Field_ftsf242ae_slot0_Slot_ae_slot0_get (insn) == 442 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_IU; + if (Field_ftsf243ae_slot0_Slot_ae_slot0_get (insn) == 443 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_XU; + if (Field_ftsf244ae_slot0_Slot_ae_slot0_get (insn) == 446 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVPA24X2; + if (Field_ftsf245ae_slot0_Slot_ae_slot0_get (insn) == 447 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_I; + if (Field_ftsf246ae_slot0_Slot_ae_slot0_get (insn) == 75 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_IU; + if (Field_ftsf247ae_slot0_Slot_ae_slot0_get (insn) == 331 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_X; + if (Field_ftsf248ae_slot0_Slot_ae_slot0_get (insn) == 78 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_X; + if (Field_ftsf249ae_slot0_Slot_ae_slot0_get (insn) == 79 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_XU; + if (Field_ftsf250ae_slot0_Slot_ae_slot0_get (insn) == 334 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_I; + if (Field_ftsf251ae_slot0_Slot_ae_slot0_get (insn) == 335 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_IU; + if (Field_ftsf252ae_slot0_Slot_ae_slot0_get (insn) == 90 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_XU; + if (Field_ftsf253ae_slot0_Slot_ae_slot0_get (insn) == 91 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_X; + if (Field_ftsf254ae_slot0_Slot_ae_slot0_get (insn) == 94 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_XU; + if (Field_ftsf255ae_slot0_Slot_ae_slot0_get (insn) == 95 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_IU; + if (Field_ftsf256ae_slot0_Slot_ae_slot0_get (insn) == 346 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_I; + if (Field_ftsf257ae_slot0_Slot_ae_slot0_get (insn) == 347 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_X; + if (Field_ftsf258ae_slot0_Slot_ae_slot0_get (insn) == 350 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_XU; + if (Field_ftsf259ae_slot0_Slot_ae_slot0_get (insn) == 351 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_I; + if (Field_ftsf260ae_slot0_Slot_ae_slot0_get (insn) == 106 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_I; + if (Field_ftsf261ae_slot0_Slot_ae_slot0_get (insn) == 107 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_IU; + if (Field_ftsf262ae_slot0_Slot_ae_slot0_get (insn) == 110 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_X; + if (Field_ftsf263ae_slot0_Slot_ae_slot0_get (insn) == 111 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_IU; + if (Field_ftsf264ae_slot0_Slot_ae_slot0_get (insn) == 122 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_XU; + if (Field_ftsf265ae_slot0_Slot_ae_slot0_get (insn) == 123 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_X; + if (Field_ftsf266ae_slot0_Slot_ae_slot0_get (insn) == 126 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_XU; + if (Field_ftsf267ae_slot0_Slot_ae_slot0_get (insn) == 127 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_TRUNCP24A32X2; + if (Field_ftsf268ae_slot0_Slot_ae_slot0_get (insn) == 362 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_I; + if (Field_ftsf269ae_slot0_Slot_ae_slot0_get (insn) == 363 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ32F_I; + if (Field_ftsf270ae_slot0_Slot_ae_slot0_get (insn) == 875 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ32F_XU; + if (Field_ftsf271ae_slot0_Slot_ae_slot0_get (insn) == 366 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ32F_IU; + if (Field_ftsf272ae_slot0_Slot_ae_slot0_get (insn) == 367 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_I; + if (Field_ftsf273ae_slot0_Slot_ae_slot0_get (insn) == 878 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_IU; + if (Field_ftsf274ae_slot0_Slot_ae_slot0_get (insn) == 879 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_X; + if (Field_ftsf275ae_slot0_Slot_ae_slot0_get (insn) == 378 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ32F_X; + if (Field_ftsf276ae_slot0_Slot_ae_slot0_get (insn) == 379 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_XU; + if (Field_ftsf277ae_slot0_Slot_ae_slot0_get (insn) == 890 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_NSAQ56S; + if (Field_ftsf278ae_slot0_Slot_ae_slot0_get (insn) == 891 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_TRUNCA32Q48; + if (Field_ftsf279ae_slot0_Slot_ae_slot0_get (insn) == 2938 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s8_Slot_ae_slot0_get (insn) == 0) + return OPCODE_JX; + if (Field_ftsf280_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBCI; + if (Field_ftsf280_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBSI; + if (Field_ftsf281ae_slot0_Slot_ae_slot0_get (insn) == 2939 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s8_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSR; + if (Field_ftsf282ae_slot0_Slot_ae_slot0_get (insn) == 1981 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf361ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_NOP; + if (Field_ftsf284ae_slot0_Slot_ae_slot0_get (insn) == 957 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf364ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSA8B; + if (Field_ftsf285ae_slot0_Slot_ae_slot0_get (insn) == 957 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf366ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSA8L; + if (Field_ftsf287ae_slot0_Slot_ae_slot0_get (insn) == 957 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf368ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSL; + if (Field_ftsf288_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3 && + Field_ftsf360ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_BLTUI; + if (Field_ftsf289ae_slot0_Slot_ae_slot0_get (insn) == 382 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTA32P24_H; + if (Field_ftsf290ae_slot0_Slot_ae_slot0_get (insn) == 383 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTA32P24_L; + if (Field_ftsf291ae_slot0_Slot_ae_slot0_get (insn) == 447 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVAP24S_H; + if (Field_ftsf292ae_slot0_Slot_ae_slot0_get (insn) == 447 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf382ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVAP24S_L; + if (Field_ftsf293ae_slot0_Slot_ae_slot0_get (insn) == 447 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf384ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_TRUNCA16P24S_H; + if (Field_ftsf294ae_slot0_Slot_ae_slot0_get (insn) == 447 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf383ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_TRUNCA16P24S_L; + if (Field_ftsf295ae_slot0_Slot_ae_slot0_get (insn) == 202 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_IU; + if (Field_ftsf296ae_slot0_Slot_ae_slot0_get (insn) == 4298 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf315_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVP48; + if (Field_ftsf297ae_slot0_Slot_ae_slot0_get (insn) == 4554 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ALL4; + if (Field_ftsf297ae_slot0_Slot_ae_slot0_get (insn) == 12746 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf309_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSAI; + if (Field_ftsf298ae_slot0_Slot_ae_slot0_get (insn) == 2506 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf373ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ANY4; + if (Field_ftsf300ae_slot0_Slot_ae_slot0_get (insn) == 1482 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf370ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ALL8; + if (Field_ftsf302ae_slot0_Slot_ae_slot0_get (insn) == 970 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf376ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ANY8; + if (Field_ftsf304ae_slot0_Slot_ae_slot0_get (insn) == 203 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ32F_I; + if (Field_ftsf305ae_slot0_Slot_ae_slot0_get (insn) == 459 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ56_I; + if (Field_ftsf306ae_slot0_Slot_ae_slot0_get (insn) == 715 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ56_IU; + if (Field_ftsf307ae_slot0_Slot_ae_slot0_get (insn) == 971 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ56_X; + if (Field_ftsf308ae_slot0_Slot_ae_slot0_get (insn) == 206 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s8_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_LQ32F_IU; + if (Field_ftsf309_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s8_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_LQ56_C; + if (Field_ftsf310ae_slot0_Slot_ae_slot0_get (insn) == 207 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s8_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_LQ56_XU; + if (Field_ftsf311ae_slot0_Slot_ae_slot0_get (insn) == 231 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf386ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_CVTQ48A32S; + if (Field_ftsf312ae_slot0_Slot_ae_slot0_get (insn) == 219 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLIQ56; + if (Field_ftsf312ae_slot0_Slot_ae_slot0_get (insn) == 222 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLISQ56S; + if (Field_ftsf312ae_slot0_Slot_ae_slot0_get (insn) == 475 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SRAIQ56; + if (Field_ftsf312ae_slot0_Slot_ae_slot0_get (insn) == 731 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SRLIQ56; + if (Field_ftsf313ae_slot0_Slot_ae_slot0_get (insn) == 987 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ABS; + if (Field_ftsf313ae_slot0_Slot_ae_slot0_get (insn) == 2011 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_NEG; + if (Field_ftsf313ae_slot0_Slot_ae_slot0_get (insn) == 3035 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRA; + if (Field_ftsf313ae_slot0_Slot_ae_slot0_get (insn) == 4059 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRL; + if (Field_ftsf314ae_slot0_Slot_ae_slot0_get (insn) == 478 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVQ56; + if (Field_ftsf316ae_slot0_Slot_ae_slot0_get (insn) == 1502 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SLLSSQ56S; + if (Field_ftsf317ae_slot0_Slot_ae_slot0_get (insn) == 1502 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf389ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRASQ56; + if (Field_ftsf319ae_slot0_Slot_ae_slot0_get (insn) == 1502 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf388ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRLSQ56; + if (Field_ftsf320ae_slot0_Slot_ae_slot0_get (insn) == 478 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf387ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SLLSQ56; + if (Field_ftsf322ae_slot0_Slot_ae_slot0_get (insn) == 223 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVFQ56; + if (Field_ftsf323ae_slot0_Slot_ae_slot0_get (insn) == 479 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVTQ56; + if (Field_ftsf324ae_slot0_Slot_ae_slot0_get (insn) == 735 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLAQ56; + if (Field_ftsf325ae_slot0_Slot_ae_slot0_get (insn) == 991 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SRLAQ56; + if (Field_ftsf326ae_slot0_Slot_ae_slot0_get (insn) == 735 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf363ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SLLASQ56S; + if (Field_ftsf327ae_slot0_Slot_ae_slot0_get (insn) == 991 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf363ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SLL; + if (Field_ftsf328ae_slot0_Slot_ae_slot0_get (insn) == 479 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf360ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRAAQ56; + if (Field_ftsf329ae_slot0_Slot_ae_slot0_get (insn) == 31 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf379ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_LQ32F_XU; + if (Field_imm8_Slot_ae_slot0_get (insn) == 11 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVGEZ; + if (Field_imm8_Slot_ae_slot0_get (insn) == 14 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVLTZ; + if (Field_imm8_Slot_ae_slot0_get (insn) == 15 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ORBC; + if (Field_imm8_Slot_ae_slot0_get (insn) == 23 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADD; + if (Field_imm8_Slot_ae_slot0_get (insn) == 26 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVNEZ; + if (Field_imm8_Slot_ae_slot0_get (insn) == 27 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRLI; + if (Field_imm8_Slot_ae_slot0_get (insn) == 30 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUB; + if (Field_imm8_Slot_ae_slot0_get (insn) == 31 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUBX4; + if (Field_imm8_Slot_ae_slot0_get (insn) == 39 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADDX2; + if (Field_imm8_Slot_ae_slot0_get (insn) == 42 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVT; + if (Field_imm8_Slot_ae_slot0_get (insn) == 55 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AND; + if (Field_imm8_Slot_ae_slot0_get (insn) == 71 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADDX4; + if (Field_imm8_Slot_ae_slot0_get (insn) == 74 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_OR; + if (Field_imm8_Slot_ae_slot0_get (insn) == 87 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_CLAMPS; + if (Field_imm8_Slot_ae_slot0_get (insn) == 103 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MAX; + if (Field_imm8_Slot_ae_slot0_get (insn) == 119 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MIN; + if (Field_imm8_Slot_ae_slot0_get (insn) == 139 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ORB; + if (Field_imm8_Slot_ae_slot0_get (insn) == 142 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SEXT; + if (Field_imm8_Slot_ae_slot0_get (insn) == 143 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRC; + if (Field_imm8_Slot_ae_slot0_get (insn) == 151 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADDX8; + if (Field_imm8_Slot_ae_slot0_get (insn) == 154 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUBX2; + if (Field_imm8_Slot_ae_slot0_get (insn) == 155 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUBX8; + if (Field_imm8_Slot_ae_slot0_get (insn) == 158 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_XOR; + if (Field_imm8_Slot_ae_slot0_get (insn) == 159 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_XORB; + if (Field_imm8_Slot_ae_slot0_get (insn) == 167 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ANDB; + if (Field_imm8_Slot_ae_slot0_get (insn) == 183 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ANDBC; + if (Field_imm8_Slot_ae_slot0_get (insn) == 199 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MAXU; + if (Field_imm8_Slot_ae_slot0_get (insn) == 215 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MINU; + if (Field_imm8_Slot_ae_slot0_get (insn) == 218 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ae_r10_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_LQ32F_X; + if (Field_imm8_Slot_ae_slot0_get (insn) == 231 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVEQZ; + if (Field_imm8_Slot_ae_slot0_get (insn) == 247 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVF; + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 5) + return OPCODE_L32R; + if (Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld149ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_ADDBRBA32; + if (Field_r_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_BNE; + if (Field_r_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_BNONE; + if (Field_r_Slot_ae_slot0_get (insn) == 2 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_ADDI; + if (Field_r_Slot_ae_slot0_get (insn) == 2 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_L16SI; + if (Field_r_Slot_ae_slot0_get (insn) == 3 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BALL; + if (Field_r_Slot_ae_slot0_get (insn) == 3 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_L8UI; + if (Field_r_Slot_ae_slot0_get (insn) == 4 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_ADDMI; + if (Field_r_Slot_ae_slot0_get (insn) == 4 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_L16UI; + if (Field_r_Slot_ae_slot0_get (insn) == 5 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBS; + if (Field_r_Slot_ae_slot0_get (insn) == 5 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_S16I; + if (Field_r_Slot_ae_slot0_get (insn) == 6 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BEQ; + if (Field_r_Slot_ae_slot0_get (insn) == 6 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_S32I; + if (Field_r_Slot_ae_slot0_get (insn) == 7 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BGEU; + if (Field_r_Slot_ae_slot0_get (insn) == 7 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_S8I; + if (Field_r_Slot_ae_slot0_get (insn) == 10 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BANY; + if (Field_r_Slot_ae_slot0_get (insn) == 11 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBC; + if (Field_r_Slot_ae_slot0_get (insn) == 12 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BGE; + if (Field_r_Slot_ae_slot0_get (insn) == 13 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BLT; + if (Field_r_Slot_ae_slot0_get (insn) == 14 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BLTU; + if (Field_r_Slot_ae_slot0_get (insn) == 15 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BNALL; + if (Field_s8_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4 && + Field_ftsf280_Slot_ae_slot0_get (insn) == 0) + return OPCODE_L32I; + if (Field_s8_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf309_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get (insn) == 3 && + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get (insn) == 1 && + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get (insn) == 0 && + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ32F_C; + if (Field_t_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BEQI; + if (Field_t_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BGEI; + if (Field_t_Slot_ae_slot0_get (insn) == 2 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BGEUI; + if (Field_t_Slot_ae_slot0_get (insn) == 3 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BNEI; + if (Field_t_Slot_ae_slot0_get (insn) == 4 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BLTI; + if (Field_t_Slot_ae_slot0_get (insn) == 5 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3 && + Field_r_Slot_ae_slot0_get (insn) == 0) + return OPCODE_BF; + return XTENSA_UNDEFINED; +} + +static int +Slot_ae_slot1_decode (const xtensa_insnbuf insn) +{ + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 50 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP24S_L; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 82 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULARFQ32SP24S_L; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 114 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP24S_L; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 146 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULRFQ32SP24S_L; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 178 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP24S_L; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 210 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSRFQ32SP24S_L; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 274 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULRFQ32SP24S_H; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 306 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP24S_H; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 338 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSRFQ32SP24S_H; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 402 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULARFQ32SP24S_H; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 434 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP24S_H; + if (Field_ae_mul32x24fld_Slot_ae_slot1_get (insn) == 466 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP24S_H; + if (Field_ae_r20_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf343ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_LTQ56S; + if (Field_ae_s20_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf340ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULZAAP24S_HL_LH; + if (Field_combined2c0b5f72_fld131ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld69_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf91_Slot_ae_slot1_get (insn) == 1 && + Field_combined2c0b5f72_fld68_Slot_ae_slot1_get (insn) == 2 && + Field_combined2c0b5f72_fld19_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld22_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MAXABSSP24S; + if (Field_combined2c0b5f72_fld132ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_combined2c0b5f72_fld69_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf91_Slot_ae_slot1_get (insn) == 1 && + Field_combined2c0b5f72_fld68_Slot_ae_slot1_get (insn) == 2 && + Field_combined2c0b5f72_fld19_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld22_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MINABSSP24S; + if (Field_combined2c0b5f72_fld74_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf354ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld66_Slot_ae_slot1_get (insn) == 0 && + Field_ftsf127ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_combined2c0b5f72_fld91_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld90_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld88_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld65_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld24_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_s3_Slot_ae_slot1_get (insn) == 5 && + Field_combined2c0b5f72_fld147ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MINABSSQ56S; + if (Field_combined2c0b5f72_fld79_Slot_ae_slot1_get (insn) == 1 && + Field_combined2c0b5f72_fld74_Slot_ae_slot1_get (insn) == 0 && + Field_ftsf354ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld66_Slot_ae_slot1_get (insn) == 0 && + Field_ftsf127ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_combined2c0b5f72_fld91_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld90_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld88_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld65_Slot_ae_slot1_get (insn) == 0 && + Field_combined2c0b5f72_fld24_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MAXABSSQ56S; + if (Field_ftsf101ae_slot1_Slot_ae_slot1_get (insn) == 43 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf335_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULSQ32SP16U_H; + if (Field_ftsf102ae_slot1_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf336ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULSFQ32SP16U_H; + if (Field_ftsf103ae_slot1_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf337ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULAFQ32SP16S_H; + if (Field_ftsf106ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MAXBQ56S; + if (Field_ftsf107ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MINBQ56S; + if (Field_ftsf108ae_slot1_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ae_r32_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_EQQ56; + if (Field_ftsf109ae_slot1_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ADDSQ56S; + if (Field_ftsf110ae_slot1_Slot_ae_slot1_get (insn) == 67 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ANDQ56; + if (Field_ftsf111ae_slot1_Slot_ae_slot1_get (insn) == 131 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MAXQ56S; + if (Field_ftsf112ae_slot1_Slot_ae_slot1_get (insn) == 195 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ORQ56; + if (Field_ftsf113ae_slot1_Slot_ae_slot1_get (insn) == 259 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MINQ56S; + if (Field_ftsf114ae_slot1_Slot_ae_slot1_get (insn) == 323 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_SUBQ56; + if (Field_ftsf115ae_slot1_Slot_ae_slot1_get (insn) == 387 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_SUBSQ56S; + if (Field_ftsf116ae_slot1_Slot_ae_slot1_get (insn) == 451 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_XORQ56; + if (Field_ftsf117ae_slot1_Slot_ae_slot1_get (insn) == 515 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_NANDQ56; + if (Field_ftsf118ae_slot1_Slot_ae_slot1_get (insn) == 2307 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ABSQ56; + if (Field_ftsf120ae_slot1_Slot_ae_slot1_get (insn) == 2315 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_NEGSQ56S; + if (Field_ftsf121ae_slot1_Slot_ae_slot1_get (insn) == 1163 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf91_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_SATQ48S; + if (Field_ftsf123ae_slot1_Slot_ae_slot1_get (insn) == 323 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf349ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ABSSQ56S; + if (Field_ftsf124ae_slot1_Slot_ae_slot1_get (insn) == 195 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf352ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_NEGQ56; + if (Field_ftsf125ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf342ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_LEQ56S; + if (Field_ftsf126ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf357ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_TRUNCP24Q48X2; + if (Field_ftsf127ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf350ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ADDQ56; + if (Field_ftsf128ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAFP24S_HH_LL; + if (Field_ftsf129ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAFP24S_HL_LH; + if (Field_ftsf130ae_slot1_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAP24S_HH_LL; + if (Field_ftsf131ae_slot1_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS32P16S_HL; + if (Field_ftsf132ae_slot1_Slot_ae_slot1_get (insn) == 4 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAP24S_HL_LH; + if (Field_ftsf133ae_slot1_Slot_ae_slot1_get (insn) == 5 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS32P16S_LH; + if (Field_ftsf134ae_slot1_Slot_ae_slot1_get (insn) == 6 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS32P16S_LL; + if (Field_ftsf135ae_slot1_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_HH; + if (Field_ftsf136ae_slot1_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_HH; + if (Field_ftsf137ae_slot1_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_HL; + if (Field_ftsf138ae_slot1_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_LH; + if (Field_ftsf139ae_slot1_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_HH; + if (Field_ftsf140ae_slot1_Slot_ae_slot1_get (insn) == 12 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_LL; + if (Field_ftsf141ae_slot1_Slot_ae_slot1_get (insn) == 13 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_HL; + if (Field_ftsf142ae_slot1_Slot_ae_slot1_get (insn) == 14 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_LH; + if (Field_ftsf143ae_slot1_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_LL; + if (Field_ftsf144ae_slot1_Slot_ae_slot1_get (insn) == 16 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_HL; + if (Field_ftsf145ae_slot1_Slot_ae_slot1_get (insn) == 17 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_HH; + if (Field_ftsf146ae_slot1_Slot_ae_slot1_get (insn) == 18 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_HL; + if (Field_ftsf147ae_slot1_Slot_ae_slot1_get (insn) == 19 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASFP24S_HH_LL; + if (Field_ftsf148ae_slot1_Slot_ae_slot1_get (insn) == 20 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_LH; + if (Field_ftsf149ae_slot1_Slot_ae_slot1_get (insn) == 21 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASFP24S_HL_LH; + if (Field_ftsf150ae_slot1_Slot_ae_slot1_get (insn) == 22 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASP24S_HH_LL; + if (Field_ftsf151ae_slot1_Slot_ae_slot1_get (insn) == 23 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASP24S_HL_LH; + if (Field_ftsf152ae_slot1_Slot_ae_slot1_get (insn) == 24 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_LL; + if (Field_ftsf153ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_HH; + if (Field_ftsf154ae_slot1_Slot_ae_slot1_get (insn) == 26 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_HL; + if (Field_ftsf155ae_slot1_Slot_ae_slot1_get (insn) == 27 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_LL; + if (Field_ftsf156ae_slot1_Slot_ae_slot1_get (insn) == 28 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_LH; + if (Field_ftsf157ae_slot1_Slot_ae_slot1_get (insn) == 29 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_HH; + if (Field_ftsf158ae_slot1_Slot_ae_slot1_get (insn) == 30 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_HL; + if (Field_ftsf159ae_slot1_Slot_ae_slot1_get (insn) == 31 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_LH; + if (Field_ftsf160ae_slot1_Slot_ae_slot1_get (insn) == 32 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_LH; + if (Field_ftsf161ae_slot1_Slot_ae_slot1_get (insn) == 33 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_LL; + if (Field_ftsf162ae_slot1_Slot_ae_slot1_get (insn) == 34 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_HH; + if (Field_ftsf163ae_slot1_Slot_ae_slot1_get (insn) == 35 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAFP24S_HH_LL; + if (Field_ftsf164ae_slot1_Slot_ae_slot1_get (insn) == 36 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_HL; + if (Field_ftsf165ae_slot1_Slot_ae_slot1_get (insn) == 37 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAFP24S_HL_LH; + if (Field_ftsf166ae_slot1_Slot_ae_slot1_get (insn) == 38 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAP24S_HH_LL; + if (Field_ftsf167ae_slot1_Slot_ae_slot1_get (insn) == 39 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAP24S_HL_LH; + if (Field_ftsf168ae_slot1_Slot_ae_slot1_get (insn) == 40 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_LH; + if (Field_ftsf169ae_slot1_Slot_ae_slot1_get (insn) == 41 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_HH; + if (Field_ftsf170ae_slot1_Slot_ae_slot1_get (insn) == 42 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_HL; + if (Field_ftsf171ae_slot1_Slot_ae_slot1_get (insn) == 43 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_LL; + if (Field_ftsf172ae_slot1_Slot_ae_slot1_get (insn) == 44 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_LH; + if (Field_ftsf173ae_slot1_Slot_ae_slot1_get (insn) == 45 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_HH; + if (Field_ftsf174ae_slot1_Slot_ae_slot1_get (insn) == 46 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_HL; + if (Field_ftsf175ae_slot1_Slot_ae_slot1_get (insn) == 47 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_LH; + if (Field_ftsf176ae_slot1_Slot_ae_slot1_get (insn) == 48 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_LL; + if (Field_ftsf177ae_slot1_Slot_ae_slot1_get (insn) == 49 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_LL; + if (Field_ftsf178ae_slot1_Slot_ae_slot1_get (insn) == 50 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_HH; + if (Field_ftsf179ae_slot1_Slot_ae_slot1_get (insn) == 51 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_LL; + if (Field_ftsf180ae_slot1_Slot_ae_slot1_get (insn) == 52 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_HL; + if (Field_ftsf181ae_slot1_Slot_ae_slot1_get (insn) == 53 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_HH; + if (Field_ftsf182ae_slot1_Slot_ae_slot1_get (insn) == 54 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_HL; + if (Field_ftsf183ae_slot1_Slot_ae_slot1_get (insn) == 55 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_LH; + if (Field_ftsf184ae_slot1_Slot_ae_slot1_get (insn) == 56 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_LH; + if (Field_ftsf185ae_slot1_Slot_ae_slot1_get (insn) == 57 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_LL; + if (Field_ftsf186ae_slot1_Slot_ae_slot1_get (insn) == 58 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_HH; + if (Field_ftsf187ae_slot1_Slot_ae_slot1_get (insn) == 59 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_LH; + if (Field_ftsf188ae_slot1_Slot_ae_slot1_get (insn) == 60 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_HL; + if (Field_ftsf189ae_slot1_Slot_ae_slot1_get (insn) == 61 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_LL; + if (Field_ftsf190ae_slot1_Slot_ae_slot1_get (insn) == 62 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSFP24S_HH_LL; + if (Field_ftsf191ae_slot1_Slot_ae_slot1_get (insn) == 63 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSFP24S_HL_LH; + if (Field_ftsf192ae_slot1_Slot_ae_slot1_get (insn) == 64 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_LL; + if (Field_ftsf193ae_slot1_Slot_ae_slot1_get (insn) == 65 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSP24S_HH_LL; + if (Field_ftsf194ae_slot1_Slot_ae_slot1_get (insn) == 66 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSP24S_HL_LH; + if (Field_ftsf195ae_slot1_Slot_ae_slot1_get (insn) == 67 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASFP24S_HH_LL; + if (Field_ftsf196ae_slot1_Slot_ae_slot1_get (insn) == 68 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZAAFP24S_HH_LL; + if (Field_ftsf197ae_slot1_Slot_ae_slot1_get (insn) == 69 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASFP24S_HL_LH; + if (Field_ftsf198ae_slot1_Slot_ae_slot1_get (insn) == 70 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASP24S_HH_LL; + if (Field_ftsf199ae_slot1_Slot_ae_slot1_get (insn) == 71 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASP24S_HL_LH; + if (Field_ftsf200ae_slot1_Slot_ae_slot1_get (insn) == 72 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZAAFP24S_HL_LH; + if (Field_ftsf201ae_slot1_Slot_ae_slot1_get (insn) == 73 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAFP24S_HH_LL; + if (Field_ftsf202ae_slot1_Slot_ae_slot1_get (insn) == 74 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAFP24S_HL_LH; + if (Field_ftsf203ae_slot1_Slot_ae_slot1_get (insn) == 75 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAP24S_HL_LH; + if (Field_ftsf204ae_slot1_Slot_ae_slot1_get (insn) == 76 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAP24S_HH_LL; + if (Field_ftsf205ae_slot1_Slot_ae_slot1_get (insn) == 77 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSSFP24S_HH_LL; + if (Field_ftsf206ae_slot1_Slot_ae_slot1_get (insn) == 78 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSSFP24S_HL_LH; + if (Field_ftsf207ae_slot1_Slot_ae_slot1_get (insn) == 79 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSSP24S_HH_LL; + if (Field_ftsf208ae_slot1_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf341ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULZAAP24S_HH_LL; + if (Field_ftsf20ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MAXBP24S; + if (Field_ftsf210ae_slot1_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf339ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULZSSP24S_HL_LH; + if (Field_ftsf21ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MINBP24S; + if (Field_ftsf22ae_slot1_Slot_ae_slot1_get (insn) == 4 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVFP48; + if (Field_ftsf23ae_slot1_Slot_ae_slot1_get (insn) == 5 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ADDP24; + if (Field_ftsf24ae_slot1_Slot_ae_slot1_get (insn) == 69 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MAXP24S; + if (Field_ftsf25ae_slot1_Slot_ae_slot1_get (insn) == 20 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ADDSP24S; + if (Field_ftsf26ae_slot1_Slot_ae_slot1_get (insn) == 21 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MINP24S; + if (Field_ftsf27ae_slot1_Slot_ae_slot1_get (insn) == 84 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_NANDP48; + if (Field_ftsf28ae_slot1_Slot_ae_slot1_get (insn) == 85 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ORP48; + if (Field_ftsf29ae_slot1_Slot_ae_slot1_get (insn) == 36 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ANDP48; + if (Field_ftsf30ae_slot1_Slot_ae_slot1_get (insn) == 37 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_HH; + if (Field_ftsf31ae_slot1_Slot_ae_slot1_get (insn) == 52 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_HL; + if (Field_ftsf32ae_slot1_Slot_ae_slot1_get (insn) == 53 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_LL; + if (Field_ftsf33ae_slot1_Slot_ae_slot1_get (insn) == 100 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_LH; + if (Field_ftsf34ae_slot1_Slot_ae_slot1_get (insn) == 101 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SUBP24; + if (Field_ftsf35ae_slot1_Slot_ae_slot1_get (insn) == 116 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SUBSP24S; + if (Field_ftsf36ae_slot1_Slot_ae_slot1_get (insn) == 117 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_XORP48; + if (Field_ftsf37ae_slot1_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVTP48; + if (Field_ftsf38ae_slot1_Slot_ae_slot1_get (insn) == 24 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ABSP24; + if (Field_ftsf40ae_slot1_Slot_ae_slot1_get (insn) == 88 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_NEGP24; + if (Field_ftsf41ae_slot1_Slot_ae_slot1_get (insn) == 152 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_NEGSP24S; + if (Field_ftsf42ae_slot1_Slot_ae_slot1_get (insn) == 216 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf333ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_NOP; + if (Field_ftsf43ae_slot1_Slot_ae_slot1_get (insn) == 88 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf359ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ZEROP48; + if (Field_ftsf45ae_slot1_Slot_ae_slot1_get (insn) == 704 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVP48; + if (Field_ftsf47ae_slot1_Slot_ae_slot1_get (insn) == 708 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSP16ASYM; + if (Field_ftsf48ae_slot1_Slot_ae_slot1_get (insn) == 712 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSP16SYM; + if (Field_ftsf49ae_slot1_Slot_ae_slot1_get (insn) == 716 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SLLSSP24S; + if (Field_ftsf50ae_slot1_Slot_ae_slot1_get (insn) == 720 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SLLSP24; + if (Field_ftsf51ae_slot1_Slot_ae_slot1_get (insn) == 724 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRASP24; + if (Field_ftsf52ae_slot1_Slot_ae_slot1_get (insn) == 728 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRLSP24; + if (Field_ftsf53ae_slot1_Slot_ae_slot1_get (insn) == 732 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_TRUNCP16; + if (Field_ftsf54ae_slot1_Slot_ae_slot1_get (insn) == 24 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf356ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ABSSP24S; + if (Field_ftsf56ae_slot1_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_EQP24; + if (Field_ftsf57ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_LTP24S; + if (Field_ftsf58ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf354ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MOVFP24X2; + if (Field_ftsf60ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf355ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MOVTP24X2; + if (Field_ftsf61_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf334ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULAFS32P16S_HH; + if (Field_ftsf62ae_slot1_Slot_ae_slot1_get (insn) == 12 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_LEP24S; + if (Field_ftsf63ae_slot1_Slot_ae_slot1_get (insn) == 97 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf344ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSP16Q48ASYM; + if (Field_ftsf63ae_slot1_Slot_ae_slot1_get (insn) == 101 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf344ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSP16Q48SYM; + if (Field_ftsf64ae_slot1_Slot_ae_slot1_get (insn) == 53 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf345ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSP24Q48ASYM; + if (Field_ftsf66ae_slot1_Slot_ae_slot1_get (insn) == 29 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf347ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSP24Q48SYM; + if (Field_ftsf68ae_slot1_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SLLIP24; + if (Field_ftsf68ae_slot1_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SLLISP24S; + if (Field_ftsf68ae_slot1_Slot_ae_slot1_get (insn) == 6 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRAIP24; + if (Field_ftsf68ae_slot1_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRLIP24; + if (Field_ftsf69ae_slot1_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16S_L; + if (Field_ftsf70ae_slot1_Slot_ae_slot1_get (insn) == 39 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16U_H; + if (Field_ftsf71ae_slot1_Slot_ae_slot1_get (insn) == 71 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16U_L; + if (Field_ftsf72ae_slot1_Slot_ae_slot1_get (insn) == 103 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16U_H; + if (Field_ftsf73ae_slot1_Slot_ae_slot1_get (insn) == 135 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16S_H; + if (Field_ftsf74ae_slot1_Slot_ae_slot1_get (insn) == 167 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16U_L; + if (Field_ftsf75ae_slot1_Slot_ae_slot1_get (insn) == 199 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16S_H; + if (Field_ftsf76ae_slot1_Slot_ae_slot1_get (insn) == 231 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16S_L; + if (Field_ftsf77ae_slot1_Slot_ae_slot1_get (insn) == 263 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16S_L; + if (Field_ftsf78ae_slot1_Slot_ae_slot1_get (insn) == 295 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16U_H; + if (Field_ftsf79ae_slot1_Slot_ae_slot1_get (insn) == 327 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16U_L; + if (Field_ftsf80ae_slot1_Slot_ae_slot1_get (insn) == 359 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16S_L; + if (Field_ftsf81ae_slot1_Slot_ae_slot1_get (insn) == 391 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16S_H; + if (Field_ftsf82ae_slot1_Slot_ae_slot1_get (insn) == 423 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16U_H; + if (Field_ftsf83ae_slot1_Slot_ae_slot1_get (insn) == 455 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16U_L; + if (Field_ftsf84ae_slot1_Slot_ae_slot1_get (insn) == 487 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16S_H; + if (Field_ftsf85ae_slot1_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16S_L; + if (Field_ftsf86ae_slot1_Slot_ae_slot1_get (insn) == 43 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16U_L; + if (Field_ftsf87ae_slot1_Slot_ae_slot1_get (insn) == 75 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSQ32SP16S_H; + if (Field_ftsf88ae_slot1_Slot_ae_slot1_get (insn) == 107 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSQ32SP16U_L; + if (Field_ftsf89ae_slot1_Slot_ae_slot1_get (insn) == 139 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSQ32SP16S_L; + if (Field_ftsf90ae_slot1_Slot_ae_slot1_get (insn) == 331 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf55_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_CVTQ48P24S_H; + if (Field_ftsf92ae_slot1_Slot_ae_slot1_get (insn) == 363 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf358ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ZEROQ56; + if (Field_ftsf93ae_slot1_Slot_ae_slot1_get (insn) == 203 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r10_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_CVTQ48P24S_L; + if (Field_ftsf94ae_slot1_Slot_ae_slot1_get (insn) == 1803 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVQ56; + if (Field_ftsf96ae_slot1_Slot_ae_slot1_get (insn) == 1835 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSQ32ASYM; + if (Field_ftsf97ae_slot1_Slot_ae_slot1_get (insn) == 939 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf348ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSQ32SYM; + if (Field_ftsf99ae_slot1_Slot_ae_slot1_get (insn) == 491 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf351_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_TRUNCQ32; + if (Field_t_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASFQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASFQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 4 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 4 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 4 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 5 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 5 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 5 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 6 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 6 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 6 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 12 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 12 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 12 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 13 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 13 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16U_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 13 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 14 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16S_LL; + if (Field_t_Slot_ae_slot1_get (insn) == 14 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAQ32SP16S_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 14 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16U_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16U_HH; + if (Field_t_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAQ32SP16S_LH; + if (Field_t_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16U_LL; + return XTENSA_UNDEFINED; +} + + +/* Instruction slots. */ + +static void +Slot_x24_Format_inst_0_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[1] & 0xffffff00) >> 8); +} + +static void +Slot_x24_Format_inst_0_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[1] = (insn[1] & ~0xffffff00) | ((slotbuf[0] & 0xffffff) << 8); +} + +static void +Slot_x16a_Format_inst16a_0_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[1] & 0xffff0000) >> 16); +} + +static void +Slot_x16a_Format_inst16a_0_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[1] = (insn[1] & ~0xffff0000) | ((slotbuf[0] & 0xffff) << 16); +} + +static void +Slot_x16b_Format_inst16b_0_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[1] & 0xffff0000) >> 16); +} + +static void +Slot_x16b_Format_inst16b_0_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[1] = (insn[1] & ~0xffff0000) | ((slotbuf[0] & 0xffff) << 16); +} + +static void +Slot_ae_format_Format_ae_slot1_10_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[0] & 0xfffffc00) >> 10); + slotbuf[0] = (slotbuf[0] & ~0x400000) | ((insn[1] & 0x1) << 22); +} + +static void +Slot_ae_format_Format_ae_slot1_10_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[0] = (insn[0] & ~0xfffffc00) | ((slotbuf[0] & 0x3fffff) << 10); + insn[1] = (insn[1] & ~0x1) | ((slotbuf[0] & 0x400000) >> 22); +} + +static void +Slot_ae_format_Format_ae_slot0_33_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[1] & 0xffffffe) >> 1); +} + +static void +Slot_ae_format_Format_ae_slot0_33_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[1] = (insn[1] & ~0xffffffe) | ((slotbuf[0] & 0x7ffffff) << 1); +} + +static xtensa_get_field_fn +Slot_inst_get_field_fns[] = { + Field_t_Slot_inst_get, + Field_bbi4_Slot_inst_get, + Field_bbi_Slot_inst_get, + Field_imm12_Slot_inst_get, + Field_imm8_Slot_inst_get, + Field_s_Slot_inst_get, + Field_imm12b_Slot_inst_get, + Field_imm16_Slot_inst_get, + Field_m_Slot_inst_get, + Field_n_Slot_inst_get, + Field_offset_Slot_inst_get, + Field_op0_Slot_inst_get, + Field_op1_Slot_inst_get, + Field_op2_Slot_inst_get, + Field_r_Slot_inst_get, + Field_sa4_Slot_inst_get, + Field_sae4_Slot_inst_get, + Field_sae_Slot_inst_get, + Field_sal_Slot_inst_get, + Field_sargt_Slot_inst_get, + Field_sas4_Slot_inst_get, + Field_sas_Slot_inst_get, + Field_sr_Slot_inst_get, + Field_st_Slot_inst_get, + Field_thi3_Slot_inst_get, + Field_imm4_Slot_inst_get, + Field_mn_Slot_inst_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_r3_Slot_inst_get, + Field_rbit2_Slot_inst_get, + Field_rhi_Slot_inst_get, + Field_t3_Slot_inst_get, + Field_tbit2_Slot_inst_get, + Field_tlo_Slot_inst_get, + Field_w_Slot_inst_get, + Field_y_Slot_inst_get, + Field_x_Slot_inst_get, + Field_t2_Slot_inst_get, + Field_s2_Slot_inst_get, + Field_r2_Slot_inst_get, + Field_t4_Slot_inst_get, + Field_s4_Slot_inst_get, + Field_r4_Slot_inst_get, + Field_t8_Slot_inst_get, + Field_s8_Slot_inst_get, + Field_r8_Slot_inst_get, + Field_xt_wbr15_imm_Slot_inst_get, + Field_xt_wbr18_imm_Slot_inst_get, + Field_ae_r3_Slot_inst_get, + Field_ae_s_non_samt_Slot_inst_get, + Field_ae_s3_Slot_inst_get, + Field_ae_r32_Slot_inst_get, + Field_ae_samt_s_t_Slot_inst_get, + Field_ae_r20_Slot_inst_get, + Field_ae_r10_Slot_inst_get, + Field_ae_s20_Slot_inst_get, + Field_ae_fld_ohba_Slot_inst_get, + Field_ae_fld_ohba2_Slot_inst_get, + 0, + Field_ftsf11_Slot_inst_get, + Field_ftsf12_Slot_inst_get, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + Field_bitindex_Slot_inst_get, + Field_s3to1_Slot_inst_get, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_mr0_get, + Implicit_Field_mr1_get, + Implicit_Field_mr2_get, + Implicit_Field_mr3_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_inst_set_field_fns[] = { + Field_t_Slot_inst_set, + Field_bbi4_Slot_inst_set, + Field_bbi_Slot_inst_set, + Field_imm12_Slot_inst_set, + Field_imm8_Slot_inst_set, + Field_s_Slot_inst_set, + Field_imm12b_Slot_inst_set, + Field_imm16_Slot_inst_set, + Field_m_Slot_inst_set, + Field_n_Slot_inst_set, + Field_offset_Slot_inst_set, + Field_op0_Slot_inst_set, + Field_op1_Slot_inst_set, + Field_op2_Slot_inst_set, + Field_r_Slot_inst_set, + Field_sa4_Slot_inst_set, + Field_sae4_Slot_inst_set, + Field_sae_Slot_inst_set, + Field_sal_Slot_inst_set, + Field_sargt_Slot_inst_set, + Field_sas4_Slot_inst_set, + Field_sas_Slot_inst_set, + Field_sr_Slot_inst_set, + Field_st_Slot_inst_set, + Field_thi3_Slot_inst_set, + Field_imm4_Slot_inst_set, + Field_mn_Slot_inst_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_r3_Slot_inst_set, + Field_rbit2_Slot_inst_set, + Field_rhi_Slot_inst_set, + Field_t3_Slot_inst_set, + Field_tbit2_Slot_inst_set, + Field_tlo_Slot_inst_set, + Field_w_Slot_inst_set, + Field_y_Slot_inst_set, + Field_x_Slot_inst_set, + Field_t2_Slot_inst_set, + Field_s2_Slot_inst_set, + Field_r2_Slot_inst_set, + Field_t4_Slot_inst_set, + Field_s4_Slot_inst_set, + Field_r4_Slot_inst_set, + Field_t8_Slot_inst_set, + Field_s8_Slot_inst_set, + Field_r8_Slot_inst_set, + Field_xt_wbr15_imm_Slot_inst_set, + Field_xt_wbr18_imm_Slot_inst_set, + Field_ae_r3_Slot_inst_set, + Field_ae_s_non_samt_Slot_inst_set, + Field_ae_s3_Slot_inst_set, + Field_ae_r32_Slot_inst_set, + Field_ae_samt_s_t_Slot_inst_set, + Field_ae_r20_Slot_inst_set, + Field_ae_r10_Slot_inst_set, + Field_ae_s20_Slot_inst_set, + Field_ae_fld_ohba_Slot_inst_set, + Field_ae_fld_ohba2_Slot_inst_set, + 0, + Field_ftsf11_Slot_inst_set, + Field_ftsf12_Slot_inst_set, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + Field_bitindex_Slot_inst_set, + Field_s3to1_Slot_inst_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_inst16a_get_field_fns[] = { + Field_t_Slot_inst16a_get, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16a_get, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16a_get, + 0, + 0, + Field_r_Slot_inst16a_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16a_get, + Field_st_Slot_inst16a_get, + 0, + Field_imm4_Slot_inst16a_get, + 0, + Field_i_Slot_inst16a_get, + Field_imm6lo_Slot_inst16a_get, + Field_imm6hi_Slot_inst16a_get, + Field_imm7lo_Slot_inst16a_get, + Field_imm7hi_Slot_inst16a_get, + Field_z_Slot_inst16a_get, + Field_imm6_Slot_inst16a_get, + Field_imm7_Slot_inst16a_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_inst16a_get, + Field_s2_Slot_inst16a_get, + Field_r2_Slot_inst16a_get, + Field_t4_Slot_inst16a_get, + Field_s4_Slot_inst16a_get, + Field_r4_Slot_inst16a_get, + Field_t8_Slot_inst16a_get, + Field_s8_Slot_inst16a_get, + Field_r8_Slot_inst16a_get, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_bitindex_Slot_inst16a_get, + Field_s3to1_Slot_inst16a_get, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_mr0_get, + Implicit_Field_mr1_get, + Implicit_Field_mr2_get, + Implicit_Field_mr3_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_inst16a_set_field_fns[] = { + Field_t_Slot_inst16a_set, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16a_set, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16a_set, + 0, + 0, + Field_r_Slot_inst16a_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16a_set, + Field_st_Slot_inst16a_set, + 0, + Field_imm4_Slot_inst16a_set, + 0, + Field_i_Slot_inst16a_set, + Field_imm6lo_Slot_inst16a_set, + Field_imm6hi_Slot_inst16a_set, + Field_imm7lo_Slot_inst16a_set, + Field_imm7hi_Slot_inst16a_set, + Field_z_Slot_inst16a_set, + Field_imm6_Slot_inst16a_set, + Field_imm7_Slot_inst16a_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_inst16a_set, + Field_s2_Slot_inst16a_set, + Field_r2_Slot_inst16a_set, + Field_t4_Slot_inst16a_set, + Field_s4_Slot_inst16a_set, + Field_r4_Slot_inst16a_set, + Field_t8_Slot_inst16a_set, + Field_s8_Slot_inst16a_set, + Field_r8_Slot_inst16a_set, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_bitindex_Slot_inst16a_set, + Field_s3to1_Slot_inst16a_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_inst16b_get_field_fns[] = { + Field_t_Slot_inst16b_get, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16b_get, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16b_get, + 0, + 0, + Field_r_Slot_inst16b_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16b_get, + Field_st_Slot_inst16b_get, + 0, + Field_imm4_Slot_inst16b_get, + 0, + Field_i_Slot_inst16b_get, + Field_imm6lo_Slot_inst16b_get, + Field_imm6hi_Slot_inst16b_get, + Field_imm7lo_Slot_inst16b_get, + Field_imm7hi_Slot_inst16b_get, + Field_z_Slot_inst16b_get, + Field_imm6_Slot_inst16b_get, + Field_imm7_Slot_inst16b_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_inst16b_get, + Field_s2_Slot_inst16b_get, + Field_r2_Slot_inst16b_get, + Field_t4_Slot_inst16b_get, + Field_s4_Slot_inst16b_get, + Field_r4_Slot_inst16b_get, + Field_t8_Slot_inst16b_get, + Field_s8_Slot_inst16b_get, + Field_r8_Slot_inst16b_get, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_bitindex_Slot_inst16b_get, + Field_s3to1_Slot_inst16b_get, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_mr0_get, + Implicit_Field_mr1_get, + Implicit_Field_mr2_get, + Implicit_Field_mr3_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_inst16b_set_field_fns[] = { + Field_t_Slot_inst16b_set, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16b_set, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16b_set, + 0, + 0, + Field_r_Slot_inst16b_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16b_set, + Field_st_Slot_inst16b_set, + 0, + Field_imm4_Slot_inst16b_set, + 0, + Field_i_Slot_inst16b_set, + Field_imm6lo_Slot_inst16b_set, + Field_imm6hi_Slot_inst16b_set, + Field_imm7lo_Slot_inst16b_set, + Field_imm7hi_Slot_inst16b_set, + Field_z_Slot_inst16b_set, + Field_imm6_Slot_inst16b_set, + Field_imm7_Slot_inst16b_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_inst16b_set, + Field_s2_Slot_inst16b_set, + Field_r2_Slot_inst16b_set, + Field_t4_Slot_inst16b_set, + Field_s4_Slot_inst16b_set, + Field_r4_Slot_inst16b_set, + Field_t8_Slot_inst16b_set, + Field_s8_Slot_inst16b_set, + Field_r8_Slot_inst16b_set, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_bitindex_Slot_inst16b_set, + Field_s3to1_Slot_inst16b_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_ae_slot1_get_field_fns[] = { + Field_t_Slot_ae_slot1_get, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_ae_slot1_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot1_get, + 0, + Field_ae_r20_Slot_ae_slot1_get, + Field_ae_r10_Slot_ae_slot1_get, + Field_ae_s20_Slot_ae_slot1_get, + 0, + 0, + Field_op0_s3_Slot_ae_slot1_get, + Field_ftsf11_Slot_ae_slot1_get, + Field_ftsf12_Slot_ae_slot1_get, + Field_ftsf13_Slot_ae_slot1_get, + Field_ftsf20ae_slot1_Slot_ae_slot1_get, + Field_ftsf21ae_slot1_Slot_ae_slot1_get, + Field_ftsf22ae_slot1_Slot_ae_slot1_get, + Field_ftsf23ae_slot1_Slot_ae_slot1_get, + Field_ftsf24ae_slot1_Slot_ae_slot1_get, + Field_ftsf25ae_slot1_Slot_ae_slot1_get, + Field_ftsf26ae_slot1_Slot_ae_slot1_get, + Field_ftsf27ae_slot1_Slot_ae_slot1_get, + Field_ftsf28ae_slot1_Slot_ae_slot1_get, + Field_ftsf29ae_slot1_Slot_ae_slot1_get, + Field_ftsf30ae_slot1_Slot_ae_slot1_get, + Field_ftsf31ae_slot1_Slot_ae_slot1_get, + Field_ftsf32ae_slot1_Slot_ae_slot1_get, + Field_ftsf33ae_slot1_Slot_ae_slot1_get, + Field_ftsf34ae_slot1_Slot_ae_slot1_get, + Field_ftsf35ae_slot1_Slot_ae_slot1_get, + Field_ftsf36ae_slot1_Slot_ae_slot1_get, + Field_ftsf37ae_slot1_Slot_ae_slot1_get, + Field_ftsf38ae_slot1_Slot_ae_slot1_get, + Field_ftsf40ae_slot1_Slot_ae_slot1_get, + Field_ftsf41ae_slot1_Slot_ae_slot1_get, + Field_ftsf42ae_slot1_Slot_ae_slot1_get, + Field_ftsf43ae_slot1_Slot_ae_slot1_get, + Field_ftsf45ae_slot1_Slot_ae_slot1_get, + Field_ftsf47ae_slot1_Slot_ae_slot1_get, + Field_ftsf48ae_slot1_Slot_ae_slot1_get, + Field_ftsf49ae_slot1_Slot_ae_slot1_get, + Field_ftsf50ae_slot1_Slot_ae_slot1_get, + Field_ftsf51ae_slot1_Slot_ae_slot1_get, + Field_ftsf52ae_slot1_Slot_ae_slot1_get, + Field_ftsf53ae_slot1_Slot_ae_slot1_get, + Field_ftsf54ae_slot1_Slot_ae_slot1_get, + Field_ftsf55_Slot_ae_slot1_get, + Field_ftsf56ae_slot1_Slot_ae_slot1_get, + Field_ftsf57ae_slot1_Slot_ae_slot1_get, + Field_ftsf58ae_slot1_Slot_ae_slot1_get, + Field_ftsf60ae_slot1_Slot_ae_slot1_get, + Field_ftsf61_Slot_ae_slot1_get, + Field_ftsf62ae_slot1_Slot_ae_slot1_get, + Field_ftsf63ae_slot1_Slot_ae_slot1_get, + Field_ftsf64ae_slot1_Slot_ae_slot1_get, + Field_ftsf66ae_slot1_Slot_ae_slot1_get, + Field_ftsf68ae_slot1_Slot_ae_slot1_get, + Field_ftsf69ae_slot1_Slot_ae_slot1_get, + Field_ftsf70ae_slot1_Slot_ae_slot1_get, + Field_ftsf71ae_slot1_Slot_ae_slot1_get, + Field_ftsf72ae_slot1_Slot_ae_slot1_get, + Field_ftsf73ae_slot1_Slot_ae_slot1_get, + Field_ftsf74ae_slot1_Slot_ae_slot1_get, + Field_ftsf75ae_slot1_Slot_ae_slot1_get, + Field_ftsf76ae_slot1_Slot_ae_slot1_get, + Field_ftsf77ae_slot1_Slot_ae_slot1_get, + Field_ftsf78ae_slot1_Slot_ae_slot1_get, + Field_ftsf79ae_slot1_Slot_ae_slot1_get, + Field_ftsf80ae_slot1_Slot_ae_slot1_get, + Field_ftsf81ae_slot1_Slot_ae_slot1_get, + Field_ftsf82ae_slot1_Slot_ae_slot1_get, + Field_ftsf83ae_slot1_Slot_ae_slot1_get, + Field_ftsf84ae_slot1_Slot_ae_slot1_get, + Field_ftsf85ae_slot1_Slot_ae_slot1_get, + Field_ftsf86ae_slot1_Slot_ae_slot1_get, + Field_ftsf87ae_slot1_Slot_ae_slot1_get, + Field_ftsf88ae_slot1_Slot_ae_slot1_get, + Field_ftsf89ae_slot1_Slot_ae_slot1_get, + Field_ftsf90ae_slot1_Slot_ae_slot1_get, + Field_ftsf91_Slot_ae_slot1_get, + Field_ftsf92ae_slot1_Slot_ae_slot1_get, + Field_ftsf93ae_slot1_Slot_ae_slot1_get, + Field_ftsf94ae_slot1_Slot_ae_slot1_get, + Field_ftsf96ae_slot1_Slot_ae_slot1_get, + Field_ftsf97ae_slot1_Slot_ae_slot1_get, + Field_ftsf99ae_slot1_Slot_ae_slot1_get, + Field_ftsf101ae_slot1_Slot_ae_slot1_get, + Field_ftsf102ae_slot1_Slot_ae_slot1_get, + Field_ftsf103ae_slot1_Slot_ae_slot1_get, + Field_ftsf106ae_slot1_Slot_ae_slot1_get, + Field_ftsf107ae_slot1_Slot_ae_slot1_get, + Field_ftsf108ae_slot1_Slot_ae_slot1_get, + Field_ftsf109ae_slot1_Slot_ae_slot1_get, + Field_ftsf110ae_slot1_Slot_ae_slot1_get, + Field_ftsf111ae_slot1_Slot_ae_slot1_get, + Field_ftsf112ae_slot1_Slot_ae_slot1_get, + Field_ftsf113ae_slot1_Slot_ae_slot1_get, + Field_ftsf114ae_slot1_Slot_ae_slot1_get, + Field_ftsf115ae_slot1_Slot_ae_slot1_get, + Field_ftsf116ae_slot1_Slot_ae_slot1_get, + Field_ftsf117ae_slot1_Slot_ae_slot1_get, + Field_ftsf118ae_slot1_Slot_ae_slot1_get, + Field_ftsf120ae_slot1_Slot_ae_slot1_get, + Field_ftsf121ae_slot1_Slot_ae_slot1_get, + Field_ftsf123ae_slot1_Slot_ae_slot1_get, + Field_ftsf124ae_slot1_Slot_ae_slot1_get, + Field_ftsf125ae_slot1_Slot_ae_slot1_get, + Field_ftsf126ae_slot1_Slot_ae_slot1_get, + Field_ftsf127ae_slot1_Slot_ae_slot1_get, + Field_ftsf128ae_slot1_Slot_ae_slot1_get, + Field_ftsf129ae_slot1_Slot_ae_slot1_get, + Field_ftsf130ae_slot1_Slot_ae_slot1_get, + Field_ftsf131ae_slot1_Slot_ae_slot1_get, + Field_ftsf132ae_slot1_Slot_ae_slot1_get, + Field_ftsf133ae_slot1_Slot_ae_slot1_get, + Field_ftsf134ae_slot1_Slot_ae_slot1_get, + Field_ftsf135ae_slot1_Slot_ae_slot1_get, + Field_ftsf136ae_slot1_Slot_ae_slot1_get, + Field_ftsf137ae_slot1_Slot_ae_slot1_get, + Field_ftsf138ae_slot1_Slot_ae_slot1_get, + Field_ftsf139ae_slot1_Slot_ae_slot1_get, + Field_ftsf140ae_slot1_Slot_ae_slot1_get, + Field_ftsf141ae_slot1_Slot_ae_slot1_get, + Field_ftsf142ae_slot1_Slot_ae_slot1_get, + Field_ftsf143ae_slot1_Slot_ae_slot1_get, + Field_ftsf144ae_slot1_Slot_ae_slot1_get, + Field_ftsf145ae_slot1_Slot_ae_slot1_get, + Field_ftsf146ae_slot1_Slot_ae_slot1_get, + Field_ftsf147ae_slot1_Slot_ae_slot1_get, + Field_ftsf148ae_slot1_Slot_ae_slot1_get, + Field_ftsf149ae_slot1_Slot_ae_slot1_get, + Field_ftsf150ae_slot1_Slot_ae_slot1_get, + Field_ftsf151ae_slot1_Slot_ae_slot1_get, + Field_ftsf152ae_slot1_Slot_ae_slot1_get, + Field_ftsf153ae_slot1_Slot_ae_slot1_get, + Field_ftsf154ae_slot1_Slot_ae_slot1_get, + Field_ftsf155ae_slot1_Slot_ae_slot1_get, + Field_ftsf156ae_slot1_Slot_ae_slot1_get, + Field_ftsf157ae_slot1_Slot_ae_slot1_get, + Field_ftsf158ae_slot1_Slot_ae_slot1_get, + Field_ftsf159ae_slot1_Slot_ae_slot1_get, + Field_ftsf160ae_slot1_Slot_ae_slot1_get, + Field_ftsf161ae_slot1_Slot_ae_slot1_get, + Field_ftsf162ae_slot1_Slot_ae_slot1_get, + Field_ftsf163ae_slot1_Slot_ae_slot1_get, + Field_ftsf164ae_slot1_Slot_ae_slot1_get, + Field_ftsf165ae_slot1_Slot_ae_slot1_get, + Field_ftsf166ae_slot1_Slot_ae_slot1_get, + Field_ftsf167ae_slot1_Slot_ae_slot1_get, + Field_ftsf168ae_slot1_Slot_ae_slot1_get, + Field_ftsf169ae_slot1_Slot_ae_slot1_get, + Field_ftsf170ae_slot1_Slot_ae_slot1_get, + Field_ftsf171ae_slot1_Slot_ae_slot1_get, + Field_ftsf172ae_slot1_Slot_ae_slot1_get, + Field_ftsf173ae_slot1_Slot_ae_slot1_get, + Field_ftsf174ae_slot1_Slot_ae_slot1_get, + Field_ftsf175ae_slot1_Slot_ae_slot1_get, + Field_ftsf176ae_slot1_Slot_ae_slot1_get, + Field_ftsf177ae_slot1_Slot_ae_slot1_get, + Field_ftsf178ae_slot1_Slot_ae_slot1_get, + Field_ftsf179ae_slot1_Slot_ae_slot1_get, + Field_ftsf180ae_slot1_Slot_ae_slot1_get, + Field_ftsf181ae_slot1_Slot_ae_slot1_get, + Field_ftsf182ae_slot1_Slot_ae_slot1_get, + Field_ftsf183ae_slot1_Slot_ae_slot1_get, + Field_ftsf184ae_slot1_Slot_ae_slot1_get, + Field_ftsf185ae_slot1_Slot_ae_slot1_get, + Field_ftsf186ae_slot1_Slot_ae_slot1_get, + Field_ftsf187ae_slot1_Slot_ae_slot1_get, + Field_ftsf188ae_slot1_Slot_ae_slot1_get, + Field_ftsf189ae_slot1_Slot_ae_slot1_get, + Field_ftsf190ae_slot1_Slot_ae_slot1_get, + Field_ftsf191ae_slot1_Slot_ae_slot1_get, + Field_ftsf192ae_slot1_Slot_ae_slot1_get, + Field_ftsf193ae_slot1_Slot_ae_slot1_get, + Field_ftsf194ae_slot1_Slot_ae_slot1_get, + Field_ftsf195ae_slot1_Slot_ae_slot1_get, + Field_ftsf196ae_slot1_Slot_ae_slot1_get, + Field_ftsf197ae_slot1_Slot_ae_slot1_get, + Field_ftsf198ae_slot1_Slot_ae_slot1_get, + Field_ftsf199ae_slot1_Slot_ae_slot1_get, + Field_ftsf200ae_slot1_Slot_ae_slot1_get, + Field_ftsf201ae_slot1_Slot_ae_slot1_get, + Field_ftsf202ae_slot1_Slot_ae_slot1_get, + Field_ftsf203ae_slot1_Slot_ae_slot1_get, + Field_ftsf204ae_slot1_Slot_ae_slot1_get, + Field_ftsf205ae_slot1_Slot_ae_slot1_get, + Field_ftsf206ae_slot1_Slot_ae_slot1_get, + Field_ftsf207ae_slot1_Slot_ae_slot1_get, + Field_ftsf208ae_slot1_Slot_ae_slot1_get, + Field_ftsf210ae_slot1_Slot_ae_slot1_get, + Field_ftsf333ae_slot1_Slot_ae_slot1_get, + Field_ftsf334ae_slot1_Slot_ae_slot1_get, + Field_ftsf335_Slot_ae_slot1_get, + Field_ftsf336ae_slot1_Slot_ae_slot1_get, + Field_ftsf337ae_slot1_Slot_ae_slot1_get, + Field_ftsf339ae_slot1_Slot_ae_slot1_get, + Field_ftsf340ae_slot1_Slot_ae_slot1_get, + Field_ftsf341ae_slot1_Slot_ae_slot1_get, + Field_ftsf342ae_slot1_Slot_ae_slot1_get, + Field_ftsf343ae_slot1_Slot_ae_slot1_get, + Field_ftsf344ae_slot1_Slot_ae_slot1_get, + Field_ftsf345ae_slot1_Slot_ae_slot1_get, + Field_ftsf347ae_slot1_Slot_ae_slot1_get, + Field_ftsf348ae_slot1_Slot_ae_slot1_get, + Field_ftsf349ae_slot1_Slot_ae_slot1_get, + Field_ftsf350ae_slot1_Slot_ae_slot1_get, + Field_ftsf351_Slot_ae_slot1_get, + Field_ftsf352ae_slot1_Slot_ae_slot1_get, + Field_ftsf354ae_slot1_Slot_ae_slot1_get, + Field_ftsf355ae_slot1_Slot_ae_slot1_get, + Field_ftsf356ae_slot1_Slot_ae_slot1_get, + Field_ftsf357ae_slot1_Slot_ae_slot1_get, + Field_ftsf358ae_slot1_Slot_ae_slot1_get, + Field_ftsf359ae_slot1_Slot_ae_slot1_get, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + Field_ae_mul32x24fld_Slot_ae_slot1_get, + 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, + 0, + 0, + 0, + 0, + Field_op0_s3_s3_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld19_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld22_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld24_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld65_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld66_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld68_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld69_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld74_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld79_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld88_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld90_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld91_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld131ae_slot1_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld132ae_slot1_Slot_ae_slot1_get, + Field_combined2c0b5f72_fld147ae_slot1_Slot_ae_slot1_get, + 0, + 0, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_mr0_get, + Implicit_Field_mr1_get, + Implicit_Field_mr2_get, + Implicit_Field_mr3_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_ae_slot1_set_field_fns[] = { + Field_t_Slot_ae_slot1_set, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_ae_slot1_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot1_set, + 0, + Field_ae_r20_Slot_ae_slot1_set, + Field_ae_r10_Slot_ae_slot1_set, + Field_ae_s20_Slot_ae_slot1_set, + 0, + 0, + Field_op0_s3_Slot_ae_slot1_set, + Field_ftsf11_Slot_ae_slot1_set, + Field_ftsf12_Slot_ae_slot1_set, + Field_ftsf13_Slot_ae_slot1_set, + Field_ftsf20ae_slot1_Slot_ae_slot1_set, + Field_ftsf21ae_slot1_Slot_ae_slot1_set, + Field_ftsf22ae_slot1_Slot_ae_slot1_set, + Field_ftsf23ae_slot1_Slot_ae_slot1_set, + Field_ftsf24ae_slot1_Slot_ae_slot1_set, + Field_ftsf25ae_slot1_Slot_ae_slot1_set, + Field_ftsf26ae_slot1_Slot_ae_slot1_set, + Field_ftsf27ae_slot1_Slot_ae_slot1_set, + Field_ftsf28ae_slot1_Slot_ae_slot1_set, + Field_ftsf29ae_slot1_Slot_ae_slot1_set, + Field_ftsf30ae_slot1_Slot_ae_slot1_set, + Field_ftsf31ae_slot1_Slot_ae_slot1_set, + Field_ftsf32ae_slot1_Slot_ae_slot1_set, + Field_ftsf33ae_slot1_Slot_ae_slot1_set, + Field_ftsf34ae_slot1_Slot_ae_slot1_set, + Field_ftsf35ae_slot1_Slot_ae_slot1_set, + Field_ftsf36ae_slot1_Slot_ae_slot1_set, + Field_ftsf37ae_slot1_Slot_ae_slot1_set, + Field_ftsf38ae_slot1_Slot_ae_slot1_set, + Field_ftsf40ae_slot1_Slot_ae_slot1_set, + Field_ftsf41ae_slot1_Slot_ae_slot1_set, + Field_ftsf42ae_slot1_Slot_ae_slot1_set, + Field_ftsf43ae_slot1_Slot_ae_slot1_set, + Field_ftsf45ae_slot1_Slot_ae_slot1_set, + Field_ftsf47ae_slot1_Slot_ae_slot1_set, + Field_ftsf48ae_slot1_Slot_ae_slot1_set, + Field_ftsf49ae_slot1_Slot_ae_slot1_set, + Field_ftsf50ae_slot1_Slot_ae_slot1_set, + Field_ftsf51ae_slot1_Slot_ae_slot1_set, + Field_ftsf52ae_slot1_Slot_ae_slot1_set, + Field_ftsf53ae_slot1_Slot_ae_slot1_set, + Field_ftsf54ae_slot1_Slot_ae_slot1_set, + Field_ftsf55_Slot_ae_slot1_set, + Field_ftsf56ae_slot1_Slot_ae_slot1_set, + Field_ftsf57ae_slot1_Slot_ae_slot1_set, + Field_ftsf58ae_slot1_Slot_ae_slot1_set, + Field_ftsf60ae_slot1_Slot_ae_slot1_set, + Field_ftsf61_Slot_ae_slot1_set, + Field_ftsf62ae_slot1_Slot_ae_slot1_set, + Field_ftsf63ae_slot1_Slot_ae_slot1_set, + Field_ftsf64ae_slot1_Slot_ae_slot1_set, + Field_ftsf66ae_slot1_Slot_ae_slot1_set, + Field_ftsf68ae_slot1_Slot_ae_slot1_set, + Field_ftsf69ae_slot1_Slot_ae_slot1_set, + Field_ftsf70ae_slot1_Slot_ae_slot1_set, + Field_ftsf71ae_slot1_Slot_ae_slot1_set, + Field_ftsf72ae_slot1_Slot_ae_slot1_set, + Field_ftsf73ae_slot1_Slot_ae_slot1_set, + Field_ftsf74ae_slot1_Slot_ae_slot1_set, + Field_ftsf75ae_slot1_Slot_ae_slot1_set, + Field_ftsf76ae_slot1_Slot_ae_slot1_set, + Field_ftsf77ae_slot1_Slot_ae_slot1_set, + Field_ftsf78ae_slot1_Slot_ae_slot1_set, + Field_ftsf79ae_slot1_Slot_ae_slot1_set, + Field_ftsf80ae_slot1_Slot_ae_slot1_set, + Field_ftsf81ae_slot1_Slot_ae_slot1_set, + Field_ftsf82ae_slot1_Slot_ae_slot1_set, + Field_ftsf83ae_slot1_Slot_ae_slot1_set, + Field_ftsf84ae_slot1_Slot_ae_slot1_set, + Field_ftsf85ae_slot1_Slot_ae_slot1_set, + Field_ftsf86ae_slot1_Slot_ae_slot1_set, + Field_ftsf87ae_slot1_Slot_ae_slot1_set, + Field_ftsf88ae_slot1_Slot_ae_slot1_set, + Field_ftsf89ae_slot1_Slot_ae_slot1_set, + Field_ftsf90ae_slot1_Slot_ae_slot1_set, + Field_ftsf91_Slot_ae_slot1_set, + Field_ftsf92ae_slot1_Slot_ae_slot1_set, + Field_ftsf93ae_slot1_Slot_ae_slot1_set, + Field_ftsf94ae_slot1_Slot_ae_slot1_set, + Field_ftsf96ae_slot1_Slot_ae_slot1_set, + Field_ftsf97ae_slot1_Slot_ae_slot1_set, + Field_ftsf99ae_slot1_Slot_ae_slot1_set, + Field_ftsf101ae_slot1_Slot_ae_slot1_set, + Field_ftsf102ae_slot1_Slot_ae_slot1_set, + Field_ftsf103ae_slot1_Slot_ae_slot1_set, + Field_ftsf106ae_slot1_Slot_ae_slot1_set, + Field_ftsf107ae_slot1_Slot_ae_slot1_set, + Field_ftsf108ae_slot1_Slot_ae_slot1_set, + Field_ftsf109ae_slot1_Slot_ae_slot1_set, + Field_ftsf110ae_slot1_Slot_ae_slot1_set, + Field_ftsf111ae_slot1_Slot_ae_slot1_set, + Field_ftsf112ae_slot1_Slot_ae_slot1_set, + Field_ftsf113ae_slot1_Slot_ae_slot1_set, + Field_ftsf114ae_slot1_Slot_ae_slot1_set, + Field_ftsf115ae_slot1_Slot_ae_slot1_set, + Field_ftsf116ae_slot1_Slot_ae_slot1_set, + Field_ftsf117ae_slot1_Slot_ae_slot1_set, + Field_ftsf118ae_slot1_Slot_ae_slot1_set, + Field_ftsf120ae_slot1_Slot_ae_slot1_set, + Field_ftsf121ae_slot1_Slot_ae_slot1_set, + Field_ftsf123ae_slot1_Slot_ae_slot1_set, + Field_ftsf124ae_slot1_Slot_ae_slot1_set, + Field_ftsf125ae_slot1_Slot_ae_slot1_set, + Field_ftsf126ae_slot1_Slot_ae_slot1_set, + Field_ftsf127ae_slot1_Slot_ae_slot1_set, + Field_ftsf128ae_slot1_Slot_ae_slot1_set, + Field_ftsf129ae_slot1_Slot_ae_slot1_set, + Field_ftsf130ae_slot1_Slot_ae_slot1_set, + Field_ftsf131ae_slot1_Slot_ae_slot1_set, + Field_ftsf132ae_slot1_Slot_ae_slot1_set, + Field_ftsf133ae_slot1_Slot_ae_slot1_set, + Field_ftsf134ae_slot1_Slot_ae_slot1_set, + Field_ftsf135ae_slot1_Slot_ae_slot1_set, + Field_ftsf136ae_slot1_Slot_ae_slot1_set, + Field_ftsf137ae_slot1_Slot_ae_slot1_set, + Field_ftsf138ae_slot1_Slot_ae_slot1_set, + Field_ftsf139ae_slot1_Slot_ae_slot1_set, + Field_ftsf140ae_slot1_Slot_ae_slot1_set, + Field_ftsf141ae_slot1_Slot_ae_slot1_set, + Field_ftsf142ae_slot1_Slot_ae_slot1_set, + Field_ftsf143ae_slot1_Slot_ae_slot1_set, + Field_ftsf144ae_slot1_Slot_ae_slot1_set, + Field_ftsf145ae_slot1_Slot_ae_slot1_set, + Field_ftsf146ae_slot1_Slot_ae_slot1_set, + Field_ftsf147ae_slot1_Slot_ae_slot1_set, + Field_ftsf148ae_slot1_Slot_ae_slot1_set, + Field_ftsf149ae_slot1_Slot_ae_slot1_set, + Field_ftsf150ae_slot1_Slot_ae_slot1_set, + Field_ftsf151ae_slot1_Slot_ae_slot1_set, + Field_ftsf152ae_slot1_Slot_ae_slot1_set, + Field_ftsf153ae_slot1_Slot_ae_slot1_set, + Field_ftsf154ae_slot1_Slot_ae_slot1_set, + Field_ftsf155ae_slot1_Slot_ae_slot1_set, + Field_ftsf156ae_slot1_Slot_ae_slot1_set, + Field_ftsf157ae_slot1_Slot_ae_slot1_set, + Field_ftsf158ae_slot1_Slot_ae_slot1_set, + Field_ftsf159ae_slot1_Slot_ae_slot1_set, + Field_ftsf160ae_slot1_Slot_ae_slot1_set, + Field_ftsf161ae_slot1_Slot_ae_slot1_set, + Field_ftsf162ae_slot1_Slot_ae_slot1_set, + Field_ftsf163ae_slot1_Slot_ae_slot1_set, + Field_ftsf164ae_slot1_Slot_ae_slot1_set, + Field_ftsf165ae_slot1_Slot_ae_slot1_set, + Field_ftsf166ae_slot1_Slot_ae_slot1_set, + Field_ftsf167ae_slot1_Slot_ae_slot1_set, + Field_ftsf168ae_slot1_Slot_ae_slot1_set, + Field_ftsf169ae_slot1_Slot_ae_slot1_set, + Field_ftsf170ae_slot1_Slot_ae_slot1_set, + Field_ftsf171ae_slot1_Slot_ae_slot1_set, + Field_ftsf172ae_slot1_Slot_ae_slot1_set, + Field_ftsf173ae_slot1_Slot_ae_slot1_set, + Field_ftsf174ae_slot1_Slot_ae_slot1_set, + Field_ftsf175ae_slot1_Slot_ae_slot1_set, + Field_ftsf176ae_slot1_Slot_ae_slot1_set, + Field_ftsf177ae_slot1_Slot_ae_slot1_set, + Field_ftsf178ae_slot1_Slot_ae_slot1_set, + Field_ftsf179ae_slot1_Slot_ae_slot1_set, + Field_ftsf180ae_slot1_Slot_ae_slot1_set, + Field_ftsf181ae_slot1_Slot_ae_slot1_set, + Field_ftsf182ae_slot1_Slot_ae_slot1_set, + Field_ftsf183ae_slot1_Slot_ae_slot1_set, + Field_ftsf184ae_slot1_Slot_ae_slot1_set, + Field_ftsf185ae_slot1_Slot_ae_slot1_set, + Field_ftsf186ae_slot1_Slot_ae_slot1_set, + Field_ftsf187ae_slot1_Slot_ae_slot1_set, + Field_ftsf188ae_slot1_Slot_ae_slot1_set, + Field_ftsf189ae_slot1_Slot_ae_slot1_set, + Field_ftsf190ae_slot1_Slot_ae_slot1_set, + Field_ftsf191ae_slot1_Slot_ae_slot1_set, + Field_ftsf192ae_slot1_Slot_ae_slot1_set, + Field_ftsf193ae_slot1_Slot_ae_slot1_set, + Field_ftsf194ae_slot1_Slot_ae_slot1_set, + Field_ftsf195ae_slot1_Slot_ae_slot1_set, + Field_ftsf196ae_slot1_Slot_ae_slot1_set, + Field_ftsf197ae_slot1_Slot_ae_slot1_set, + Field_ftsf198ae_slot1_Slot_ae_slot1_set, + Field_ftsf199ae_slot1_Slot_ae_slot1_set, + Field_ftsf200ae_slot1_Slot_ae_slot1_set, + Field_ftsf201ae_slot1_Slot_ae_slot1_set, + Field_ftsf202ae_slot1_Slot_ae_slot1_set, + Field_ftsf203ae_slot1_Slot_ae_slot1_set, + Field_ftsf204ae_slot1_Slot_ae_slot1_set, + Field_ftsf205ae_slot1_Slot_ae_slot1_set, + Field_ftsf206ae_slot1_Slot_ae_slot1_set, + Field_ftsf207ae_slot1_Slot_ae_slot1_set, + Field_ftsf208ae_slot1_Slot_ae_slot1_set, + Field_ftsf210ae_slot1_Slot_ae_slot1_set, + Field_ftsf333ae_slot1_Slot_ae_slot1_set, + Field_ftsf334ae_slot1_Slot_ae_slot1_set, + Field_ftsf335_Slot_ae_slot1_set, + Field_ftsf336ae_slot1_Slot_ae_slot1_set, + Field_ftsf337ae_slot1_Slot_ae_slot1_set, + Field_ftsf339ae_slot1_Slot_ae_slot1_set, + Field_ftsf340ae_slot1_Slot_ae_slot1_set, + Field_ftsf341ae_slot1_Slot_ae_slot1_set, + Field_ftsf342ae_slot1_Slot_ae_slot1_set, + Field_ftsf343ae_slot1_Slot_ae_slot1_set, + Field_ftsf344ae_slot1_Slot_ae_slot1_set, + Field_ftsf345ae_slot1_Slot_ae_slot1_set, + Field_ftsf347ae_slot1_Slot_ae_slot1_set, + Field_ftsf348ae_slot1_Slot_ae_slot1_set, + Field_ftsf349ae_slot1_Slot_ae_slot1_set, + Field_ftsf350ae_slot1_Slot_ae_slot1_set, + Field_ftsf351_Slot_ae_slot1_set, + Field_ftsf352ae_slot1_Slot_ae_slot1_set, + Field_ftsf354ae_slot1_Slot_ae_slot1_set, + Field_ftsf355ae_slot1_Slot_ae_slot1_set, + Field_ftsf356ae_slot1_Slot_ae_slot1_set, + Field_ftsf357ae_slot1_Slot_ae_slot1_set, + Field_ftsf358ae_slot1_Slot_ae_slot1_set, + Field_ftsf359ae_slot1_Slot_ae_slot1_set, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + Field_ae_mul32x24fld_Slot_ae_slot1_set, + 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, + 0, + 0, + 0, + 0, + Field_op0_s3_s3_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld19_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld22_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld24_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld65_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld66_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld68_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld69_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld74_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld79_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld88_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld90_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld91_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld131ae_slot1_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld132ae_slot1_Slot_ae_slot1_set, + Field_combined2c0b5f72_fld147ae_slot1_Slot_ae_slot1_set, + 0, + 0, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_ae_slot0_get_field_fns[] = { + Field_t_Slot_ae_slot0_get, + 0, + Field_bbi_Slot_ae_slot0_get, + Field_imm12_Slot_ae_slot0_get, + Field_imm8_Slot_ae_slot0_get, + Field_s_Slot_ae_slot0_get, + Field_imm12b_Slot_ae_slot0_get, + Field_imm16_Slot_ae_slot0_get, + 0, + 0, + Field_offset_Slot_ae_slot0_get, + 0, + 0, + Field_op2_Slot_ae_slot0_get, + Field_r_Slot_ae_slot0_get, + 0, + 0, + Field_sae_Slot_ae_slot0_get, + Field_sal_Slot_ae_slot0_get, + Field_sargt_Slot_ae_slot0_get, + 0, + Field_sas_Slot_ae_slot0_get, + 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, + Field_s4_Slot_ae_slot0_get, + 0, + 0, + Field_s8_Slot_ae_slot0_get, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot0_get, + Field_ae_samt_s_t_Slot_ae_slot0_get, + Field_ae_r20_Slot_ae_slot0_get, + Field_ae_r10_Slot_ae_slot0_get, + Field_ae_s20_Slot_ae_slot0_get, + 0, + 0, + 0, + Field_ftsf11_Slot_ae_slot0_get, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_op0_s4_Slot_ae_slot0_get, + Field_ftsf211ae_slot0_Slot_ae_slot0_get, + Field_ftsf212ae_slot0_Slot_ae_slot0_get, + Field_ftsf213ae_slot0_Slot_ae_slot0_get, + Field_ftsf214ae_slot0_Slot_ae_slot0_get, + Field_ftsf215ae_slot0_Slot_ae_slot0_get, + Field_ftsf217ae_slot0_Slot_ae_slot0_get, + Field_ftsf218ae_slot0_Slot_ae_slot0_get, + Field_ftsf219ae_slot0_Slot_ae_slot0_get, + Field_ftsf220ae_slot0_Slot_ae_slot0_get, + Field_ftsf221ae_slot0_Slot_ae_slot0_get, + Field_ftsf222ae_slot0_Slot_ae_slot0_get, + Field_ftsf223ae_slot0_Slot_ae_slot0_get, + Field_ftsf224ae_slot0_Slot_ae_slot0_get, + Field_ftsf225ae_slot0_Slot_ae_slot0_get, + Field_ftsf226ae_slot0_Slot_ae_slot0_get, + Field_ftsf227ae_slot0_Slot_ae_slot0_get, + Field_ftsf228ae_slot0_Slot_ae_slot0_get, + Field_ftsf229ae_slot0_Slot_ae_slot0_get, + Field_ftsf230ae_slot0_Slot_ae_slot0_get, + Field_ftsf231ae_slot0_Slot_ae_slot0_get, + Field_ftsf232ae_slot0_Slot_ae_slot0_get, + Field_ftsf233ae_slot0_Slot_ae_slot0_get, + Field_ftsf234ae_slot0_Slot_ae_slot0_get, + Field_ftsf235ae_slot0_Slot_ae_slot0_get, + Field_ftsf236ae_slot0_Slot_ae_slot0_get, + Field_ftsf237ae_slot0_Slot_ae_slot0_get, + Field_ftsf238ae_slot0_Slot_ae_slot0_get, + Field_ftsf239ae_slot0_Slot_ae_slot0_get, + Field_ftsf240ae_slot0_Slot_ae_slot0_get, + Field_ftsf241ae_slot0_Slot_ae_slot0_get, + Field_ftsf242ae_slot0_Slot_ae_slot0_get, + Field_ftsf243ae_slot0_Slot_ae_slot0_get, + Field_ftsf244ae_slot0_Slot_ae_slot0_get, + Field_ftsf245ae_slot0_Slot_ae_slot0_get, + Field_ftsf246ae_slot0_Slot_ae_slot0_get, + Field_ftsf247ae_slot0_Slot_ae_slot0_get, + Field_ftsf248ae_slot0_Slot_ae_slot0_get, + Field_ftsf249ae_slot0_Slot_ae_slot0_get, + Field_ftsf250ae_slot0_Slot_ae_slot0_get, + Field_ftsf251ae_slot0_Slot_ae_slot0_get, + Field_ftsf252ae_slot0_Slot_ae_slot0_get, + Field_ftsf253ae_slot0_Slot_ae_slot0_get, + Field_ftsf254ae_slot0_Slot_ae_slot0_get, + Field_ftsf255ae_slot0_Slot_ae_slot0_get, + Field_ftsf256ae_slot0_Slot_ae_slot0_get, + Field_ftsf257ae_slot0_Slot_ae_slot0_get, + Field_ftsf258ae_slot0_Slot_ae_slot0_get, + Field_ftsf259ae_slot0_Slot_ae_slot0_get, + Field_ftsf260ae_slot0_Slot_ae_slot0_get, + Field_ftsf261ae_slot0_Slot_ae_slot0_get, + Field_ftsf262ae_slot0_Slot_ae_slot0_get, + Field_ftsf263ae_slot0_Slot_ae_slot0_get, + Field_ftsf264ae_slot0_Slot_ae_slot0_get, + Field_ftsf265ae_slot0_Slot_ae_slot0_get, + Field_ftsf266ae_slot0_Slot_ae_slot0_get, + Field_ftsf267ae_slot0_Slot_ae_slot0_get, + Field_ftsf268ae_slot0_Slot_ae_slot0_get, + Field_ftsf269ae_slot0_Slot_ae_slot0_get, + Field_ftsf270ae_slot0_Slot_ae_slot0_get, + Field_ftsf271ae_slot0_Slot_ae_slot0_get, + Field_ftsf272ae_slot0_Slot_ae_slot0_get, + Field_ftsf273ae_slot0_Slot_ae_slot0_get, + Field_ftsf274ae_slot0_Slot_ae_slot0_get, + Field_ftsf275ae_slot0_Slot_ae_slot0_get, + Field_ftsf276ae_slot0_Slot_ae_slot0_get, + Field_ftsf277ae_slot0_Slot_ae_slot0_get, + Field_ftsf278ae_slot0_Slot_ae_slot0_get, + Field_ftsf279ae_slot0_Slot_ae_slot0_get, + Field_ftsf280_Slot_ae_slot0_get, + Field_ftsf281ae_slot0_Slot_ae_slot0_get, + Field_ftsf282ae_slot0_Slot_ae_slot0_get, + Field_ftsf284ae_slot0_Slot_ae_slot0_get, + Field_ftsf285ae_slot0_Slot_ae_slot0_get, + Field_ftsf287ae_slot0_Slot_ae_slot0_get, + Field_ftsf288_Slot_ae_slot0_get, + Field_ftsf289ae_slot0_Slot_ae_slot0_get, + Field_ftsf290ae_slot0_Slot_ae_slot0_get, + Field_ftsf291ae_slot0_Slot_ae_slot0_get, + Field_ftsf292ae_slot0_Slot_ae_slot0_get, + Field_ftsf293ae_slot0_Slot_ae_slot0_get, + Field_ftsf294ae_slot0_Slot_ae_slot0_get, + Field_ftsf295ae_slot0_Slot_ae_slot0_get, + Field_ftsf296ae_slot0_Slot_ae_slot0_get, + Field_ftsf297ae_slot0_Slot_ae_slot0_get, + Field_ftsf298ae_slot0_Slot_ae_slot0_get, + Field_ftsf300ae_slot0_Slot_ae_slot0_get, + Field_ftsf302ae_slot0_Slot_ae_slot0_get, + Field_ftsf304ae_slot0_Slot_ae_slot0_get, + Field_ftsf305ae_slot0_Slot_ae_slot0_get, + Field_ftsf306ae_slot0_Slot_ae_slot0_get, + Field_ftsf307ae_slot0_Slot_ae_slot0_get, + Field_ftsf308ae_slot0_Slot_ae_slot0_get, + Field_ftsf309_Slot_ae_slot0_get, + Field_ftsf310ae_slot0_Slot_ae_slot0_get, + Field_ftsf311ae_slot0_Slot_ae_slot0_get, + Field_ftsf312ae_slot0_Slot_ae_slot0_get, + Field_ftsf313ae_slot0_Slot_ae_slot0_get, + Field_ftsf314ae_slot0_Slot_ae_slot0_get, + Field_ftsf315_Slot_ae_slot0_get, + Field_ftsf316ae_slot0_Slot_ae_slot0_get, + Field_ftsf317ae_slot0_Slot_ae_slot0_get, + Field_ftsf319ae_slot0_Slot_ae_slot0_get, + Field_ftsf320ae_slot0_Slot_ae_slot0_get, + Field_ftsf322ae_slot0_Slot_ae_slot0_get, + Field_ftsf323ae_slot0_Slot_ae_slot0_get, + Field_ftsf324ae_slot0_Slot_ae_slot0_get, + Field_ftsf325ae_slot0_Slot_ae_slot0_get, + Field_ftsf326ae_slot0_Slot_ae_slot0_get, + Field_ftsf327ae_slot0_Slot_ae_slot0_get, + Field_ftsf328ae_slot0_Slot_ae_slot0_get, + Field_ftsf329ae_slot0_Slot_ae_slot0_get, + Field_ftsf360ae_slot0_Slot_ae_slot0_get, + Field_ftsf361ae_slot0_Slot_ae_slot0_get, + Field_ftsf362_Slot_ae_slot0_get, + Field_ftsf363ae_slot0_Slot_ae_slot0_get, + Field_ftsf364ae_slot0_Slot_ae_slot0_get, + Field_ftsf366ae_slot0_Slot_ae_slot0_get, + Field_ftsf368ae_slot0_Slot_ae_slot0_get, + Field_ftsf370ae_slot0_Slot_ae_slot0_get, + Field_ftsf373ae_slot0_Slot_ae_slot0_get, + Field_ftsf376ae_slot0_Slot_ae_slot0_get, + Field_ftsf378ae_slot0_Slot_ae_slot0_get, + Field_ftsf379ae_slot0_Slot_ae_slot0_get, + Field_ftsf382ae_slot0_Slot_ae_slot0_get, + Field_ftsf383ae_slot0_Slot_ae_slot0_get, + Field_ftsf384ae_slot0_Slot_ae_slot0_get, + Field_ftsf386ae_slot0_Slot_ae_slot0_get, + Field_ftsf387ae_slot0_Slot_ae_slot0_get, + Field_ftsf388ae_slot0_Slot_ae_slot0_get, + Field_ftsf389ae_slot0_Slot_ae_slot0_get, + 0, + Field_op0_s4_s4_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld28_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld37_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld39_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld40_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld46_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld47_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld49_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld50_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld52_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld121_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld123_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld127_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld133ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld134ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld135ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld136ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld137ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld138ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld139ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld140ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld141ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld142ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld143ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld144ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld145ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld146ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld148ae_slot0_Slot_ae_slot0_get, + Field_combined2c0b5f72_fld149ae_slot0_Slot_ae_slot0_get, + Field_op0_s4_s4_s4_Slot_ae_slot0_get, + Field_combined1e9fefee_fld96_Slot_ae_slot0_get, + Field_combined1e9fefee_fld98_Slot_ae_slot0_get, + Field_combined1e9fefee_fld106ae_slot0_Slot_ae_slot0_get, + Field_combined1e9fefee_fld107ae_slot0_Slot_ae_slot0_get, + Field_combined1e9fefee_fld108ae_slot0_Slot_ae_slot0_get, + Field_combined1e9fefee_fld109ae_slot0_Slot_ae_slot0_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_bitindex_Slot_ae_slot0_get, + Field_s3to1_Slot_ae_slot0_get, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_mr0_get, + Implicit_Field_mr1_get, + Implicit_Field_mr2_get, + Implicit_Field_mr3_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_ae_slot0_set_field_fns[] = { + Field_t_Slot_ae_slot0_set, + 0, + Field_bbi_Slot_ae_slot0_set, + Field_imm12_Slot_ae_slot0_set, + Field_imm8_Slot_ae_slot0_set, + Field_s_Slot_ae_slot0_set, + Field_imm12b_Slot_ae_slot0_set, + Field_imm16_Slot_ae_slot0_set, + 0, + 0, + Field_offset_Slot_ae_slot0_set, + 0, + 0, + Field_op2_Slot_ae_slot0_set, + Field_r_Slot_ae_slot0_set, + 0, + 0, + Field_sae_Slot_ae_slot0_set, + Field_sal_Slot_ae_slot0_set, + Field_sargt_Slot_ae_slot0_set, + 0, + Field_sas_Slot_ae_slot0_set, + 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, + Field_s4_Slot_ae_slot0_set, + 0, + 0, + Field_s8_Slot_ae_slot0_set, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot0_set, + Field_ae_samt_s_t_Slot_ae_slot0_set, + Field_ae_r20_Slot_ae_slot0_set, + Field_ae_r10_Slot_ae_slot0_set, + Field_ae_s20_Slot_ae_slot0_set, + 0, + 0, + 0, + Field_ftsf11_Slot_ae_slot0_set, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_op0_s4_Slot_ae_slot0_set, + Field_ftsf211ae_slot0_Slot_ae_slot0_set, + Field_ftsf212ae_slot0_Slot_ae_slot0_set, + Field_ftsf213ae_slot0_Slot_ae_slot0_set, + Field_ftsf214ae_slot0_Slot_ae_slot0_set, + Field_ftsf215ae_slot0_Slot_ae_slot0_set, + Field_ftsf217ae_slot0_Slot_ae_slot0_set, + Field_ftsf218ae_slot0_Slot_ae_slot0_set, + Field_ftsf219ae_slot0_Slot_ae_slot0_set, + Field_ftsf220ae_slot0_Slot_ae_slot0_set, + Field_ftsf221ae_slot0_Slot_ae_slot0_set, + Field_ftsf222ae_slot0_Slot_ae_slot0_set, + Field_ftsf223ae_slot0_Slot_ae_slot0_set, + Field_ftsf224ae_slot0_Slot_ae_slot0_set, + Field_ftsf225ae_slot0_Slot_ae_slot0_set, + Field_ftsf226ae_slot0_Slot_ae_slot0_set, + Field_ftsf227ae_slot0_Slot_ae_slot0_set, + Field_ftsf228ae_slot0_Slot_ae_slot0_set, + Field_ftsf229ae_slot0_Slot_ae_slot0_set, + Field_ftsf230ae_slot0_Slot_ae_slot0_set, + Field_ftsf231ae_slot0_Slot_ae_slot0_set, + Field_ftsf232ae_slot0_Slot_ae_slot0_set, + Field_ftsf233ae_slot0_Slot_ae_slot0_set, + Field_ftsf234ae_slot0_Slot_ae_slot0_set, + Field_ftsf235ae_slot0_Slot_ae_slot0_set, + Field_ftsf236ae_slot0_Slot_ae_slot0_set, + Field_ftsf237ae_slot0_Slot_ae_slot0_set, + Field_ftsf238ae_slot0_Slot_ae_slot0_set, + Field_ftsf239ae_slot0_Slot_ae_slot0_set, + Field_ftsf240ae_slot0_Slot_ae_slot0_set, + Field_ftsf241ae_slot0_Slot_ae_slot0_set, + Field_ftsf242ae_slot0_Slot_ae_slot0_set, + Field_ftsf243ae_slot0_Slot_ae_slot0_set, + Field_ftsf244ae_slot0_Slot_ae_slot0_set, + Field_ftsf245ae_slot0_Slot_ae_slot0_set, + Field_ftsf246ae_slot0_Slot_ae_slot0_set, + Field_ftsf247ae_slot0_Slot_ae_slot0_set, + Field_ftsf248ae_slot0_Slot_ae_slot0_set, + Field_ftsf249ae_slot0_Slot_ae_slot0_set, + Field_ftsf250ae_slot0_Slot_ae_slot0_set, + Field_ftsf251ae_slot0_Slot_ae_slot0_set, + Field_ftsf252ae_slot0_Slot_ae_slot0_set, + Field_ftsf253ae_slot0_Slot_ae_slot0_set, + Field_ftsf254ae_slot0_Slot_ae_slot0_set, + Field_ftsf255ae_slot0_Slot_ae_slot0_set, + Field_ftsf256ae_slot0_Slot_ae_slot0_set, + Field_ftsf257ae_slot0_Slot_ae_slot0_set, + Field_ftsf258ae_slot0_Slot_ae_slot0_set, + Field_ftsf259ae_slot0_Slot_ae_slot0_set, + Field_ftsf260ae_slot0_Slot_ae_slot0_set, + Field_ftsf261ae_slot0_Slot_ae_slot0_set, + Field_ftsf262ae_slot0_Slot_ae_slot0_set, + Field_ftsf263ae_slot0_Slot_ae_slot0_set, + Field_ftsf264ae_slot0_Slot_ae_slot0_set, + Field_ftsf265ae_slot0_Slot_ae_slot0_set, + Field_ftsf266ae_slot0_Slot_ae_slot0_set, + Field_ftsf267ae_slot0_Slot_ae_slot0_set, + Field_ftsf268ae_slot0_Slot_ae_slot0_set, + Field_ftsf269ae_slot0_Slot_ae_slot0_set, + Field_ftsf270ae_slot0_Slot_ae_slot0_set, + Field_ftsf271ae_slot0_Slot_ae_slot0_set, + Field_ftsf272ae_slot0_Slot_ae_slot0_set, + Field_ftsf273ae_slot0_Slot_ae_slot0_set, + Field_ftsf274ae_slot0_Slot_ae_slot0_set, + Field_ftsf275ae_slot0_Slot_ae_slot0_set, + Field_ftsf276ae_slot0_Slot_ae_slot0_set, + Field_ftsf277ae_slot0_Slot_ae_slot0_set, + Field_ftsf278ae_slot0_Slot_ae_slot0_set, + Field_ftsf279ae_slot0_Slot_ae_slot0_set, + Field_ftsf280_Slot_ae_slot0_set, + Field_ftsf281ae_slot0_Slot_ae_slot0_set, + Field_ftsf282ae_slot0_Slot_ae_slot0_set, + Field_ftsf284ae_slot0_Slot_ae_slot0_set, + Field_ftsf285ae_slot0_Slot_ae_slot0_set, + Field_ftsf287ae_slot0_Slot_ae_slot0_set, + Field_ftsf288_Slot_ae_slot0_set, + Field_ftsf289ae_slot0_Slot_ae_slot0_set, + Field_ftsf290ae_slot0_Slot_ae_slot0_set, + Field_ftsf291ae_slot0_Slot_ae_slot0_set, + Field_ftsf292ae_slot0_Slot_ae_slot0_set, + Field_ftsf293ae_slot0_Slot_ae_slot0_set, + Field_ftsf294ae_slot0_Slot_ae_slot0_set, + Field_ftsf295ae_slot0_Slot_ae_slot0_set, + Field_ftsf296ae_slot0_Slot_ae_slot0_set, + Field_ftsf297ae_slot0_Slot_ae_slot0_set, + Field_ftsf298ae_slot0_Slot_ae_slot0_set, + Field_ftsf300ae_slot0_Slot_ae_slot0_set, + Field_ftsf302ae_slot0_Slot_ae_slot0_set, + Field_ftsf304ae_slot0_Slot_ae_slot0_set, + Field_ftsf305ae_slot0_Slot_ae_slot0_set, + Field_ftsf306ae_slot0_Slot_ae_slot0_set, + Field_ftsf307ae_slot0_Slot_ae_slot0_set, + Field_ftsf308ae_slot0_Slot_ae_slot0_set, + Field_ftsf309_Slot_ae_slot0_set, + Field_ftsf310ae_slot0_Slot_ae_slot0_set, + Field_ftsf311ae_slot0_Slot_ae_slot0_set, + Field_ftsf312ae_slot0_Slot_ae_slot0_set, + Field_ftsf313ae_slot0_Slot_ae_slot0_set, + Field_ftsf314ae_slot0_Slot_ae_slot0_set, + Field_ftsf315_Slot_ae_slot0_set, + Field_ftsf316ae_slot0_Slot_ae_slot0_set, + Field_ftsf317ae_slot0_Slot_ae_slot0_set, + Field_ftsf319ae_slot0_Slot_ae_slot0_set, + Field_ftsf320ae_slot0_Slot_ae_slot0_set, + Field_ftsf322ae_slot0_Slot_ae_slot0_set, + Field_ftsf323ae_slot0_Slot_ae_slot0_set, + Field_ftsf324ae_slot0_Slot_ae_slot0_set, + Field_ftsf325ae_slot0_Slot_ae_slot0_set, + Field_ftsf326ae_slot0_Slot_ae_slot0_set, + Field_ftsf327ae_slot0_Slot_ae_slot0_set, + Field_ftsf328ae_slot0_Slot_ae_slot0_set, + Field_ftsf329ae_slot0_Slot_ae_slot0_set, + Field_ftsf360ae_slot0_Slot_ae_slot0_set, + Field_ftsf361ae_slot0_Slot_ae_slot0_set, + Field_ftsf362_Slot_ae_slot0_set, + Field_ftsf363ae_slot0_Slot_ae_slot0_set, + Field_ftsf364ae_slot0_Slot_ae_slot0_set, + Field_ftsf366ae_slot0_Slot_ae_slot0_set, + Field_ftsf368ae_slot0_Slot_ae_slot0_set, + Field_ftsf370ae_slot0_Slot_ae_slot0_set, + Field_ftsf373ae_slot0_Slot_ae_slot0_set, + Field_ftsf376ae_slot0_Slot_ae_slot0_set, + Field_ftsf378ae_slot0_Slot_ae_slot0_set, + Field_ftsf379ae_slot0_Slot_ae_slot0_set, + Field_ftsf382ae_slot0_Slot_ae_slot0_set, + Field_ftsf383ae_slot0_Slot_ae_slot0_set, + Field_ftsf384ae_slot0_Slot_ae_slot0_set, + Field_ftsf386ae_slot0_Slot_ae_slot0_set, + Field_ftsf387ae_slot0_Slot_ae_slot0_set, + Field_ftsf388ae_slot0_Slot_ae_slot0_set, + Field_ftsf389ae_slot0_Slot_ae_slot0_set, + 0, + Field_op0_s4_s4_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld28_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld37_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld39_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld40_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld46_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld47_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld49_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld50_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld52_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld121_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld123_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld127_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld133ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld134ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld135ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld136ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld137ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld138ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld139ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld140ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld141ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld142ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld143ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld144ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld145ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld146ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld148ae_slot0_Slot_ae_slot0_set, + Field_combined2c0b5f72_fld149ae_slot0_Slot_ae_slot0_set, + Field_op0_s4_s4_s4_Slot_ae_slot0_set, + Field_combined1e9fefee_fld96_Slot_ae_slot0_set, + Field_combined1e9fefee_fld98_Slot_ae_slot0_set, + Field_combined1e9fefee_fld106ae_slot0_Slot_ae_slot0_set, + Field_combined1e9fefee_fld107ae_slot0_Slot_ae_slot0_set, + Field_combined1e9fefee_fld108ae_slot0_Slot_ae_slot0_set, + Field_combined1e9fefee_fld109ae_slot0_Slot_ae_slot0_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_bitindex_Slot_ae_slot0_set, + Field_s3to1_Slot_ae_slot0_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_slot_internal slots[] = { + { "Inst", "x24", 0, + Slot_x24_Format_inst_0_get, Slot_x24_Format_inst_0_set, + Slot_inst_get_field_fns, Slot_inst_set_field_fns, + Slot_inst_decode, "nop" }, + { "Inst16a", "x16a", 0, + Slot_x16a_Format_inst16a_0_get, Slot_x16a_Format_inst16a_0_set, + Slot_inst16a_get_field_fns, Slot_inst16a_set_field_fns, + Slot_inst16a_decode, "" }, + { "Inst16b", "x16b", 0, + Slot_x16b_Format_inst16b_0_get, Slot_x16b_Format_inst16b_0_set, + Slot_inst16b_get_field_fns, Slot_inst16b_set_field_fns, + Slot_inst16b_decode, "nop.n" }, + { "ae_slot1", "ae_format", 1, + Slot_ae_format_Format_ae_slot1_10_get, Slot_ae_format_Format_ae_slot1_10_set, + Slot_ae_slot1_get_field_fns, Slot_ae_slot1_set_field_fns, + Slot_ae_slot1_decode, "nop" }, + { "ae_slot0", "ae_format", 0, + Slot_ae_format_Format_ae_slot0_33_get, Slot_ae_format_Format_ae_slot0_33_set, + Slot_ae_slot0_get_field_fns, Slot_ae_slot0_set_field_fns, + Slot_ae_slot0_decode, "nop" } +}; + + +/* Instruction formats. */ + +static void +Format_x24_encode (xtensa_insnbuf insn) +{ + insn[0] = 0; + insn[1] = 0; +} + +static void +Format_x16a_encode (xtensa_insnbuf insn) +{ + insn[0] = 0; + insn[1] = 0x80000000; +} + +static void +Format_x16b_encode (xtensa_insnbuf insn) +{ + insn[0] = 0; + insn[1] = 0xc0000000; +} + +static void +Format_ae_format_encode (xtensa_insnbuf insn) +{ + insn[0] = 0; + insn[1] = 0xf0000000; +} + +static int Format_x24_slots[] = { 0 }; + +static int Format_x16a_slots[] = { 1 }; + +static int Format_x16b_slots[] = { 2 }; + +static int Format_ae_format_slots[] = { 3, 4 }; + +static xtensa_format_internal formats[] = { + { "x24", 3, Format_x24_encode, 1, Format_x24_slots }, + { "x16a", 2, Format_x16a_encode, 1, Format_x16a_slots }, + { "x16b", 2, Format_x16b_encode, 1, Format_x16b_slots }, + { "ae_format", 8, Format_ae_format_encode, 2, Format_ae_format_slots } +}; + + +static int +format_decoder (const xtensa_insnbuf insn) +{ + if ((insn[0] & 0) == 0 && (insn[1] & 0x80000000) == 0) + return 0; /* x24 */ + if ((insn[0] & 0) == 0 && (insn[1] & 0xc0000000) == 0x80000000) + return 1; /* x16a */ + if ((insn[0] & 0) == 0 && (insn[1] & 0xe0000000) == 0xc0000000) + return 2; /* x16b */ + if ((insn[0] & 0x3ff) == 0 && (insn[1] & 0xf0000000) == 0xf0000000) + return 3; /* ae_format */ + return -1; +} + +static int length_table[256] = { + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 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, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + -1, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8, + 8 +}; + +static int +length_decoder (const unsigned char *insn) +{ + int l = insn[0]; + return length_table[l]; +} + + +/* Top-level ISA structure. */ + +xtensa_isa_internal xtensa_modules = { + 1 /* big-endian */, + 8 /* insn_size */, 0, + 4, formats, format_decoder, length_decoder, + 5, slots, + 468 /* num_fields */, + 536, operands, + 746, iclasses, + 881, opcodes, 0, + 9, regfiles, + NUM_STATES, states, 0, + NUM_SYSREGS, sysregs, 0, + { MAX_SPECIAL_REG, MAX_USER_REG }, { 0, 0 }, + 6, interfaces, 0, + 4, funcUnits, 0 +}; diff --git a/target/xtensa/core-test_mmuhifi_c3.c b/target/xtensa/core-test_mmuhifi_c3.c new file mode 100644 index 0000000000..3a59fefa94 --- /dev/null +++ b/target/xtensa/core-test_mmuhifi_c3.c @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "cpu.h" +#include "exec/exec-all.h" +#include "exec/gdbstub.h" +#include "qemu/host-utils.h" + +#include "core-test_mmuhifi_c3/core-isa.h" +#include "overlay_tool.h" + +#define xtensa_modules xtensa_modules_test_mmuhifi_c3 +#include "core-test_mmuhifi_c3/xtensa-modules.inc.c" + +static XtensaConfig test_mmuhifi_c3 __attribute__((unused)) = { + .name = "test_mmuhifi_c3", + .options = XTENSA_OPTIONS, + .gdb_regmap = { + .reg = { +#include "core-test_mmuhifi_c3/gdb-config.inc.c" + } + }, + .isa_internal = &xtensa_modules, + .clock_freq_khz = 40000, + DEFAULT_SECTIONS +}; + +REGISTER_CORE(test_mmuhifi_c3) diff --git a/target/xtensa/core-test_mmuhifi_c3/core-isa.h b/target/xtensa/core-test_mmuhifi_c3/core-isa.h new file mode 100644 index 0000000000..309caa1a32 --- /dev/null +++ b/target/xtensa/core-test_mmuhifi_c3/core-isa.h @@ -0,0 +1,384 @@ +/* + * Xtensa processor core configuration information. + * + * This file is subject to the terms and conditions of version 2.1 of the GNU + * Lesser General Public License as published by the Free Software Foundation. + * + * Copyright (c) 1999-2009 Tensilica Inc. + */ + +#ifndef _XTENSA_CORE_CONFIGURATION_H +#define _XTENSA_CORE_CONFIGURATION_H + + +/**************************************************************************** + Parameters Useful for Any Code, USER or PRIVILEGED + ****************************************************************************/ + +/* + * Note: Macros of the form XCHAL_HAVE_*** have a value of 1 if the option is + * configured, and a value of 0 otherwise. These macros are always defined. + */ + + +/*---------------------------------------------------------------------- + ISA + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_BE 0 /* big-endian byte ordering */ +#define XCHAL_HAVE_WINDOWED 1 /* windowed registers option */ +#define XCHAL_NUM_AREGS 32 /* num of physical addr regs */ +#define XCHAL_NUM_AREGS_LOG2 5 /* log2(XCHAL_NUM_AREGS) */ +#define XCHAL_MAX_INSTRUCTION_SIZE 8 /* max instr bytes (3..8) */ +#define XCHAL_HAVE_DEBUG 1 /* debug option */ +#define XCHAL_HAVE_DENSITY 1 /* 16-bit instructions */ +#define XCHAL_HAVE_LOOPS 1 /* zero-overhead loops */ +#define XCHAL_HAVE_NSA 1 /* NSA/NSAU instructions */ +#define XCHAL_HAVE_MINMAX 1 /* MIN/MAX instructions */ +#define XCHAL_HAVE_SEXT 1 /* SEXT instruction */ +#define XCHAL_HAVE_CLAMPS 1 /* CLAMPS instruction */ +#define XCHAL_HAVE_MUL16 1 /* MUL16S/MUL16U instructions */ +#define XCHAL_HAVE_MUL32 1 /* MULL instruction */ +#define XCHAL_HAVE_MUL32_HIGH 0 /* MULUH/MULSH instructions */ +#define XCHAL_HAVE_DIV32 0 /* QUOS/QUOU/REMS/REMU instructions */ +#define XCHAL_HAVE_L32R 1 /* L32R instruction */ +#define XCHAL_HAVE_ABSOLUTE_LITERALS 1 /* non-PC-rel (extended) L32R */ +#define XCHAL_HAVE_CONST16 0 /* CONST16 instruction */ +#define XCHAL_HAVE_ADDX 1 /* ADDX#/SUBX# instructions */ +#define XCHAL_HAVE_WIDE_BRANCHES 0 /* B*.W18 or B*.W15 instr's */ +#define XCHAL_HAVE_PREDICTED_BRANCHES 0 /* B[EQ/EQZ/NE/NEZ]T instr's */ +#define XCHAL_HAVE_CALL4AND12 1 /* (obsolete option) */ +#define XCHAL_HAVE_ABS 1 /* ABS instruction */ +/*#define XCHAL_HAVE_POPC 0*/ /* POPC instruction */ +/*#define XCHAL_HAVE_CRC 0*/ /* CRC instruction */ +#define XCHAL_HAVE_RELEASE_SYNC 1 /* L32AI/S32RI instructions */ +#define XCHAL_HAVE_S32C1I 1 /* S32C1I instruction */ +#define XCHAL_HAVE_SPECULATION 0 /* speculation */ +#define XCHAL_HAVE_FULL_RESET 1 /* all regs/state reset */ +#define XCHAL_NUM_CONTEXTS 1 /* */ +#define XCHAL_NUM_MISC_REGS 2 /* num of scratch regs (0..4) */ +#define XCHAL_HAVE_TAP_MASTER 0 /* JTAG TAP control instr's */ +#define XCHAL_HAVE_PRID 1 /* processor ID register */ +#define XCHAL_HAVE_EXTERN_REGS 1 /* WER/RER instructions */ +#define XCHAL_HAVE_MP_INTERRUPTS 1 /* interrupt distributor port */ +#define XCHAL_HAVE_MP_RUNSTALL 1 /* core RunStall control port */ +#define XCHAL_HAVE_THREADPTR 1 /* THREADPTR register */ +#define XCHAL_HAVE_BOOLEANS 1 /* boolean registers */ +#define XCHAL_HAVE_CP 1 /* CPENABLE reg (coprocessor) */ +#define XCHAL_CP_MAXCFG 2 /* max allowed cp id plus one */ +#define XCHAL_HAVE_MAC16 0 /* MAC16 package */ +#define XCHAL_HAVE_VECTORFPU2005 0 /* vector floating-point pkg */ +#define XCHAL_HAVE_FP 0 /* floating point pkg */ +#define XCHAL_HAVE_DFP 0 /* double precision FP pkg */ +#define XCHAL_HAVE_DFP_accel 0 /* double precision FP acceleration pkg */ +#define XCHAL_HAVE_VECTRA1 0 /* Vectra I pkg */ +#define XCHAL_HAVE_VECTRALX 0 /* Vectra LX pkg */ +#define XCHAL_HAVE_HIFIPRO 0 /* HiFiPro Audio Engine pkg */ +#define XCHAL_HAVE_HIFI2 1 /* HiFi2 Audio Engine pkg */ +#define XCHAL_HAVE_CONNXD2 0 /* ConnX D2 pkg */ + + +/*---------------------------------------------------------------------- + MISC + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_WRITEBUFFER_ENTRIES 8 /* size of write buffer */ +#define XCHAL_INST_FETCH_WIDTH 8 /* instr-fetch width in bytes */ +#define XCHAL_DATA_WIDTH 8 /* data width in bytes */ +/* In T1050, applies to selected core load and store instructions (see ISA): */ +#define XCHAL_UNALIGNED_LOAD_EXCEPTION 1 /* unaligned loads cause exc. */ +#define XCHAL_UNALIGNED_STORE_EXCEPTION 1 /* unaligned stores cause exc.*/ +#define XCHAL_UNALIGNED_LOAD_HW 0 /* unaligned loads work in hw */ +#define XCHAL_UNALIGNED_STORE_HW 0 /* unaligned stores work in hw*/ + +#define XCHAL_SW_VERSION 800000 /* sw version of this header */ + +#define XCHAL_CORE_ID "test_mmuhifi_c3" /* alphanum core name + (CoreID) set in the Xtensa + Processor Generator */ + +#define XCHAL_CORE_DESCRIPTION "test_mmuhifi_c3" +#define XCHAL_BUILD_UNIQUE_ID 0x00005A6A /* 22-bit sw build ID */ + +/* + * These definitions describe the hardware targeted by this software. + */ +#define XCHAL_HW_CONFIGID0 0xC1B3CBFE /* ConfigID hi 32 bits*/ +#define XCHAL_HW_CONFIGID1 0x10405A6A /* ConfigID lo 32 bits*/ +#define XCHAL_HW_VERSION_NAME "LX3.0.0" /* full version name */ +#define XCHAL_HW_VERSION_MAJOR 2300 /* major ver# of targeted hw */ +#define XCHAL_HW_VERSION_MINOR 0 /* minor ver# of targeted hw */ +#define XCHAL_HW_VERSION 230000 /* major*100+minor */ +#define XCHAL_HW_REL_LX3 1 +#define XCHAL_HW_REL_LX3_0 1 +#define XCHAL_HW_REL_LX3_0_0 1 +#define XCHAL_HW_CONFIGID_RELIABLE 1 +/* If software targets a *range* of hardware versions, these are the bounds: */ +#define XCHAL_HW_MIN_VERSION_MAJOR 2300 /* major v of earliest tgt hw */ +#define XCHAL_HW_MIN_VERSION_MINOR 0 /* minor v of earliest tgt hw */ +#define XCHAL_HW_MIN_VERSION 230000 /* earliest targeted hw */ +#define XCHAL_HW_MAX_VERSION_MAJOR 2300 /* major v of latest tgt hw */ +#define XCHAL_HW_MAX_VERSION_MINOR 0 /* minor v of latest tgt hw */ +#define XCHAL_HW_MAX_VERSION 230000 /* latest targeted hw */ + + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +#define XCHAL_ICACHE_LINESIZE 32 /* I-cache line size in bytes */ +#define XCHAL_DCACHE_LINESIZE 32 /* D-cache line size in bytes */ +#define XCHAL_ICACHE_LINEWIDTH 5 /* log2(I line size in bytes) */ +#define XCHAL_DCACHE_LINEWIDTH 5 /* log2(D line size in bytes) */ + +#define XCHAL_ICACHE_SIZE 16384 /* I-cache size in bytes or 0 */ +#define XCHAL_DCACHE_SIZE 16384 /* D-cache size in bytes or 0 */ + +#define XCHAL_DCACHE_IS_WRITEBACK 1 /* writeback feature */ +#define XCHAL_DCACHE_IS_COHERENT 1 /* MP coherence feature */ + + + + +/**************************************************************************** + Parameters Useful for PRIVILEGED (Supervisory or Non-Virtualized) Code + ****************************************************************************/ + + +#ifndef XTENSA_HAL_NON_PRIVILEGED_ONLY + +/*---------------------------------------------------------------------- + CACHE + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_PIF 1 /* any outbound PIF present */ + +/* If present, cache size in bytes == (ways * 2^(linewidth + setwidth)). */ + +/* Number of cache sets in log2(lines per way): */ +#define XCHAL_ICACHE_SETWIDTH 8 +#define XCHAL_DCACHE_SETWIDTH 8 + +/* Cache set associativity (number of ways): */ +#define XCHAL_ICACHE_WAYS 2 +#define XCHAL_DCACHE_WAYS 2 + +/* Cache features: */ +#define XCHAL_ICACHE_LINE_LOCKABLE 0 +#define XCHAL_DCACHE_LINE_LOCKABLE 0 +#define XCHAL_ICACHE_ECC_PARITY 0 +#define XCHAL_DCACHE_ECC_PARITY 0 + +/* Cache access size in bytes (affects operation of SICW instruction): */ +#define XCHAL_ICACHE_ACCESS_SIZE 8 +#define XCHAL_DCACHE_ACCESS_SIZE 8 + +/* Number of encoded cache attr bits (see for decoded bits): */ +#define XCHAL_CA_BITS 4 + + +/*---------------------------------------------------------------------- + INTERNAL I/D RAM/ROMs and XLMI + ----------------------------------------------------------------------*/ + +#define XCHAL_NUM_INSTROM 0 /* number of core instr. ROMs */ +#define XCHAL_NUM_INSTRAM 0 /* number of core instr. RAMs */ +#define XCHAL_NUM_DATAROM 0 /* number of core data ROMs */ +#define XCHAL_NUM_DATARAM 0 /* number of core data RAMs */ +#define XCHAL_NUM_URAM 0 /* number of core unified RAMs*/ +#define XCHAL_NUM_XLMI 0 /* number of core XLMI ports */ + + +/*---------------------------------------------------------------------- + INTERRUPTS and TIMERS + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_INTERRUPTS 1 /* interrupt option */ +#define XCHAL_HAVE_HIGHPRI_INTERRUPTS 1 /* med/high-pri. interrupts */ +#define XCHAL_HAVE_NMI 0 /* non-maskable interrupt */ +#define XCHAL_HAVE_CCOUNT 1 /* CCOUNT reg. (timer option) */ +#define XCHAL_NUM_TIMERS 2 /* number of CCOMPAREn regs */ +#define XCHAL_NUM_INTERRUPTS 12 /* number of interrupts */ +#define XCHAL_NUM_INTERRUPTS_LOG2 4 /* ceil(log2(NUM_INTERRUPTS)) */ +#define XCHAL_NUM_EXTINTERRUPTS 9 /* num of external interrupts */ +#define XCHAL_NUM_INTLEVELS 2 /* number of interrupt levels + (not including level zero) */ +#define XCHAL_EXCM_LEVEL 1 /* level masked by PS.EXCM */ + /* (always 1 in XEA1; levels 2 .. EXCM_LEVEL are "medium priority") */ + +/* Masks of interrupts at each interrupt level: */ +#define XCHAL_INTLEVEL1_MASK 0x00000FFF +#define XCHAL_INTLEVEL2_MASK 0x00000000 +#define XCHAL_INTLEVEL3_MASK 0x00000000 +#define XCHAL_INTLEVEL4_MASK 0x00000000 +#define XCHAL_INTLEVEL5_MASK 0x00000000 +#define XCHAL_INTLEVEL6_MASK 0x00000000 +#define XCHAL_INTLEVEL7_MASK 0x00000000 + +/* Masks of interrupts at each range 1..n of interrupt levels: */ +#define XCHAL_INTLEVEL1_ANDBELOW_MASK 0x00000FFF +#define XCHAL_INTLEVEL2_ANDBELOW_MASK 0x00000FFF +#define XCHAL_INTLEVEL3_ANDBELOW_MASK 0x00000FFF +#define XCHAL_INTLEVEL4_ANDBELOW_MASK 0x00000FFF +#define XCHAL_INTLEVEL5_ANDBELOW_MASK 0x00000FFF +#define XCHAL_INTLEVEL6_ANDBELOW_MASK 0x00000FFF +#define XCHAL_INTLEVEL7_ANDBELOW_MASK 0x00000FFF + +/* Level of each interrupt: */ +#define XCHAL_INT0_LEVEL 1 +#define XCHAL_INT1_LEVEL 1 +#define XCHAL_INT2_LEVEL 1 +#define XCHAL_INT3_LEVEL 1 +#define XCHAL_INT4_LEVEL 1 +#define XCHAL_INT5_LEVEL 1 +#define XCHAL_INT6_LEVEL 1 +#define XCHAL_INT7_LEVEL 1 +#define XCHAL_INT8_LEVEL 1 +#define XCHAL_INT9_LEVEL 1 +#define XCHAL_INT10_LEVEL 1 +#define XCHAL_INT11_LEVEL 1 +#define XCHAL_DEBUGLEVEL 2 /* debug interrupt level */ +#define XCHAL_HAVE_DEBUG_EXTERN_INT 1 /* OCD external db interrupt */ + +/* Type of each interrupt: */ +#define XCHAL_INT0_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT1_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT2_TYPE XTHAL_INTTYPE_EXTERN_EDGE +#define XCHAL_INT3_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT4_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT5_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT6_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT7_TYPE XTHAL_INTTYPE_SOFTWARE +#define XCHAL_INT8_TYPE XTHAL_INTTYPE_TIMER +#define XCHAL_INT9_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT10_TYPE XTHAL_INTTYPE_EXTERN_LEVEL +#define XCHAL_INT11_TYPE XTHAL_INTTYPE_EXTERN_LEVEL + +/* Masks of interrupts for each type of interrupt: */ +#define XCHAL_INTTYPE_MASK_UNCONFIGURED 0xFFFFF000 +#define XCHAL_INTTYPE_MASK_SOFTWARE 0x00000080 +#define XCHAL_INTTYPE_MASK_EXTERN_EDGE 0x00000004 +#define XCHAL_INTTYPE_MASK_EXTERN_LEVEL 0x00000E3B +#define XCHAL_INTTYPE_MASK_TIMER 0x00000140 +#define XCHAL_INTTYPE_MASK_NMI 0x00000000 +#define XCHAL_INTTYPE_MASK_WRITE_ERROR 0x00000000 + +/* Interrupt numbers assigned to specific interrupt sources: */ +#define XCHAL_TIMER0_INTERRUPT 6 /* CCOMPARE0 */ +#define XCHAL_TIMER1_INTERRUPT 8 /* CCOMPARE1 */ +#define XCHAL_TIMER2_INTERRUPT XTHAL_TIMER_UNCONFIGURED +#define XCHAL_TIMER3_INTERRUPT XTHAL_TIMER_UNCONFIGURED + +/* Interrupt numbers for levels at which only one interrupt is configured: */ +/* (There are many interrupts each at level(s) 1.) */ + + +/* + * External interrupt vectors/levels. + * These macros describe how Xtensa processor interrupt numbers + * (as numbered internally, eg. in INTERRUPT and INTENABLE registers) + * map to external BInterrupt pins, for those interrupts + * configured as external (level-triggered, edge-triggered, or NMI). + * See the Xtensa processor databook for more details. + */ + +/* Core interrupt numbers mapped to each EXTERNAL interrupt number: */ +#define XCHAL_EXTINT0_NUM 0 /* (intlevel 1) */ +#define XCHAL_EXTINT1_NUM 1 /* (intlevel 1) */ +#define XCHAL_EXTINT2_NUM 2 /* (intlevel 1) */ +#define XCHAL_EXTINT3_NUM 3 /* (intlevel 1) */ +#define XCHAL_EXTINT4_NUM 4 /* (intlevel 1) */ +#define XCHAL_EXTINT5_NUM 5 /* (intlevel 1) */ +#define XCHAL_EXTINT6_NUM 9 /* (intlevel 1) */ +#define XCHAL_EXTINT7_NUM 10 /* (intlevel 1) */ +#define XCHAL_EXTINT8_NUM 11 /* (intlevel 1) */ + + +/*---------------------------------------------------------------------- + EXCEPTIONS and VECTORS + ----------------------------------------------------------------------*/ + +#define XCHAL_XEA_VERSION 2 /* Xtensa Exception Architecture + number: 1 == XEA1 (old) + 2 == XEA2 (new) + 0 == XEAX (extern) */ +#define XCHAL_HAVE_XEA1 0 /* Exception Architecture 1 */ +#define XCHAL_HAVE_XEA2 1 /* Exception Architecture 2 */ +#define XCHAL_HAVE_XEAX 0 /* External Exception Arch. */ +#define XCHAL_HAVE_EXCEPTIONS 1 /* exception option */ +#define XCHAL_HAVE_MEM_ECC_PARITY 0 /* local memory ECC/parity */ +#define XCHAL_HAVE_VECTOR_SELECT 1 /* relocatable vectors */ +#define XCHAL_HAVE_VECBASE 1 /* relocatable vectors */ +#define XCHAL_VECBASE_RESET_VADDR 0xD0000000 /* VECBASE reset value */ +#define XCHAL_VECBASE_RESET_PADDR 0x00000000 +#define XCHAL_RESET_VECBASE_OVERLAP 0 + +#define XCHAL_RESET_VECTOR0_VADDR 0xFE000000 +#define XCHAL_RESET_VECTOR0_PADDR 0xFE000000 +#define XCHAL_RESET_VECTOR1_VADDR 0xD8000500 +#define XCHAL_RESET_VECTOR1_PADDR 0x00000500 +#define XCHAL_RESET_VECTOR_VADDR 0xFE000000 +#define XCHAL_RESET_VECTOR_PADDR 0xFE000000 +#define XCHAL_USER_VECOFS 0x00000340 +#define XCHAL_USER_VECTOR_VADDR 0xD0000340 +#define XCHAL_USER_VECTOR_PADDR 0x00000340 +#define XCHAL_KERNEL_VECOFS 0x00000300 +#define XCHAL_KERNEL_VECTOR_VADDR 0xD0000300 +#define XCHAL_KERNEL_VECTOR_PADDR 0x00000300 +#define XCHAL_DOUBLEEXC_VECOFS 0x000003C0 +#define XCHAL_DOUBLEEXC_VECTOR_VADDR 0xD00003C0 +#define XCHAL_DOUBLEEXC_VECTOR_PADDR 0x000003C0 +#define XCHAL_WINDOW_OF4_VECOFS 0x00000000 +#define XCHAL_WINDOW_UF4_VECOFS 0x00000040 +#define XCHAL_WINDOW_OF8_VECOFS 0x00000080 +#define XCHAL_WINDOW_UF8_VECOFS 0x000000C0 +#define XCHAL_WINDOW_OF12_VECOFS 0x00000100 +#define XCHAL_WINDOW_UF12_VECOFS 0x00000140 +#define XCHAL_WINDOW_VECTORS_VADDR 0xD0000000 +#define XCHAL_WINDOW_VECTORS_PADDR 0x00000000 +#define XCHAL_INTLEVEL2_VECOFS 0x00000280 +#define XCHAL_INTLEVEL2_VECTOR_VADDR 0xD0000280 +#define XCHAL_INTLEVEL2_VECTOR_PADDR 0x00000280 +#define XCHAL_DEBUG_VECOFS XCHAL_INTLEVEL2_VECOFS +#define XCHAL_DEBUG_VECTOR_VADDR XCHAL_INTLEVEL2_VECTOR_VADDR +#define XCHAL_DEBUG_VECTOR_PADDR XCHAL_INTLEVEL2_VECTOR_PADDR + + +/*---------------------------------------------------------------------- + DEBUG + ----------------------------------------------------------------------*/ + +#define XCHAL_HAVE_OCD 1 /* OnChipDebug option */ +#define XCHAL_NUM_IBREAK 0 /* number of IBREAKn regs */ +#define XCHAL_NUM_DBREAK 0 /* number of DBREAKn regs */ +#define XCHAL_HAVE_OCD_DIR_ARRAY 0 /* faster OCD option */ + + +/*---------------------------------------------------------------------- + MMU + ----------------------------------------------------------------------*/ + +/* See core-matmap.h header file for more details. */ + +#define XCHAL_HAVE_TLBS 1 /* inverse of HAVE_CACHEATTR */ +#define XCHAL_HAVE_SPANNING_WAY 0 /* one way maps I+D 4GB vaddr */ +#define XCHAL_HAVE_IDENTITY_MAP 0 /* vaddr == paddr always */ +#define XCHAL_HAVE_CACHEATTR 0 /* CACHEATTR register present */ +#define XCHAL_HAVE_MIMIC_CACHEATTR 0 /* region protection */ +#define XCHAL_HAVE_XLT_CACHEATTR 0 /* region prot. w/translation */ +#define XCHAL_HAVE_PTP_MMU 1 /* full MMU (with page table + [autorefill] and protection) + usable for an MMU-based OS */ +/* If none of the above last 4 are set, it's a custom TLB configuration. */ +#define XCHAL_ITLB_ARF_ENTRIES_LOG2 2 /* log2(autorefill way size) */ +#define XCHAL_DTLB_ARF_ENTRIES_LOG2 2 /* log2(autorefill way size) */ + +#define XCHAL_MMU_ASID_BITS 8 /* number of bits in ASIDs */ +#define XCHAL_MMU_RINGS 4 /* number of rings (1..4) */ +#define XCHAL_MMU_RING_BITS 2 /* num of bits in RING field */ + +#endif /* !XTENSA_HAL_NON_PRIVILEGED_ONLY */ + + +#endif /* _XTENSA_CORE_CONFIGURATION_H */ + diff --git a/target/xtensa/core-test_mmuhifi_c3/gdb-config.inc.c b/target/xtensa/core-test_mmuhifi_c3/gdb-config.inc.c new file mode 100644 index 0000000000..618d30dffa --- /dev/null +++ b/target/xtensa/core-test_mmuhifi_c3/gdb-config.inc.c @@ -0,0 +1,140 @@ +/* Configuration for the Xtensa architecture for GDB, the GNU debugger. + + Copyright (C) 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. + + This file is part of GDB. + + 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 3 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 . */ + + /* idx ofs bi sz al targno flags cp typ group name */ + XTREG( 0, 0,32, 4, 4,0x0020,0x0006,-2, 9,0x0100,pc, 0,0,0,0,0,0) + XTREG( 1, 4,32, 4, 4,0x0100,0x0006,-2, 1,0x0002,ar0, 0,0,0,0,0,0) + XTREG( 2, 8,32, 4, 4,0x0101,0x0006,-2, 1,0x0002,ar1, 0,0,0,0,0,0) + XTREG( 3, 12,32, 4, 4,0x0102,0x0006,-2, 1,0x0002,ar2, 0,0,0,0,0,0) + XTREG( 4, 16,32, 4, 4,0x0103,0x0006,-2, 1,0x0002,ar3, 0,0,0,0,0,0) + XTREG( 5, 20,32, 4, 4,0x0104,0x0006,-2, 1,0x0002,ar4, 0,0,0,0,0,0) + XTREG( 6, 24,32, 4, 4,0x0105,0x0006,-2, 1,0x0002,ar5, 0,0,0,0,0,0) + XTREG( 7, 28,32, 4, 4,0x0106,0x0006,-2, 1,0x0002,ar6, 0,0,0,0,0,0) + XTREG( 8, 32,32, 4, 4,0x0107,0x0006,-2, 1,0x0002,ar7, 0,0,0,0,0,0) + XTREG( 9, 36,32, 4, 4,0x0108,0x0006,-2, 1,0x0002,ar8, 0,0,0,0,0,0) + XTREG( 10, 40,32, 4, 4,0x0109,0x0006,-2, 1,0x0002,ar9, 0,0,0,0,0,0) + XTREG( 11, 44,32, 4, 4,0x010a,0x0006,-2, 1,0x0002,ar10, 0,0,0,0,0,0) + XTREG( 12, 48,32, 4, 4,0x010b,0x0006,-2, 1,0x0002,ar11, 0,0,0,0,0,0) + XTREG( 13, 52,32, 4, 4,0x010c,0x0006,-2, 1,0x0002,ar12, 0,0,0,0,0,0) + XTREG( 14, 56,32, 4, 4,0x010d,0x0006,-2, 1,0x0002,ar13, 0,0,0,0,0,0) + XTREG( 15, 60,32, 4, 4,0x010e,0x0006,-2, 1,0x0002,ar14, 0,0,0,0,0,0) + XTREG( 16, 64,32, 4, 4,0x010f,0x0006,-2, 1,0x0002,ar15, 0,0,0,0,0,0) + XTREG( 17, 68,32, 4, 4,0x0110,0x0006,-2, 1,0x0002,ar16, 0,0,0,0,0,0) + XTREG( 18, 72,32, 4, 4,0x0111,0x0006,-2, 1,0x0002,ar17, 0,0,0,0,0,0) + XTREG( 19, 76,32, 4, 4,0x0112,0x0006,-2, 1,0x0002,ar18, 0,0,0,0,0,0) + XTREG( 20, 80,32, 4, 4,0x0113,0x0006,-2, 1,0x0002,ar19, 0,0,0,0,0,0) + XTREG( 21, 84,32, 4, 4,0x0114,0x0006,-2, 1,0x0002,ar20, 0,0,0,0,0,0) + XTREG( 22, 88,32, 4, 4,0x0115,0x0006,-2, 1,0x0002,ar21, 0,0,0,0,0,0) + XTREG( 23, 92,32, 4, 4,0x0116,0x0006,-2, 1,0x0002,ar22, 0,0,0,0,0,0) + XTREG( 24, 96,32, 4, 4,0x0117,0x0006,-2, 1,0x0002,ar23, 0,0,0,0,0,0) + XTREG( 25,100,32, 4, 4,0x0118,0x0006,-2, 1,0x0002,ar24, 0,0,0,0,0,0) + XTREG( 26,104,32, 4, 4,0x0119,0x0006,-2, 1,0x0002,ar25, 0,0,0,0,0,0) + XTREG( 27,108,32, 4, 4,0x011a,0x0006,-2, 1,0x0002,ar26, 0,0,0,0,0,0) + XTREG( 28,112,32, 4, 4,0x011b,0x0006,-2, 1,0x0002,ar27, 0,0,0,0,0,0) + XTREG( 29,116,32, 4, 4,0x011c,0x0006,-2, 1,0x0002,ar28, 0,0,0,0,0,0) + XTREG( 30,120,32, 4, 4,0x011d,0x0006,-2, 1,0x0002,ar29, 0,0,0,0,0,0) + XTREG( 31,124,32, 4, 4,0x011e,0x0006,-2, 1,0x0002,ar30, 0,0,0,0,0,0) + XTREG( 32,128,32, 4, 4,0x011f,0x0006,-2, 1,0x0002,ar31, 0,0,0,0,0,0) + XTREG( 33,132,32, 4, 4,0x0200,0x0006,-2, 2,0x1100,lbeg, 0,0,0,0,0,0) + XTREG( 34,136,32, 4, 4,0x0201,0x0006,-2, 2,0x1100,lend, 0,0,0,0,0,0) + XTREG( 35,140,32, 4, 4,0x0202,0x0006,-2, 2,0x1100,lcount, 0,0,0,0,0,0) + XTREG( 36,144, 6, 4, 4,0x0203,0x0006,-2, 2,0x1100,sar, 0,0,0,0,0,0) + XTREG( 37,148,32, 4, 4,0x0205,0x0006,-2, 2,0x1100,litbase, 0,0,0,0,0,0) + XTREG( 38,152, 3, 4, 4,0x0248,0x0006,-2, 2,0x1002,windowbase, 0,0,0,0,0,0) + XTREG( 39,156, 8, 4, 4,0x0249,0x0006,-2, 2,0x1002,windowstart, 0,0,0,0,0,0) + XTREG( 40,160,32, 4, 4,0x02b0,0x0002,-2, 2,0x1000,sr176, 0,0,0,0,0,0) + XTREG( 41,164,32, 4, 4,0x02d0,0x0002,-2, 2,0x1000,sr208, 0,0,0,0,0,0) + XTREG( 42,168,19, 4, 4,0x02e6,0x0006,-2, 2,0x1100,ps, 0,0,0,0,0,0) + XTREG( 43,172,32, 4, 4,0x03e7,0x0006,-2, 3,0x0110,threadptr, 0,0,0,0,0,0) + XTREG( 44,176,16, 4, 4,0x0204,0x0006,-1, 2,0x1100,br, 0,0,0,0,0,0) + XTREG( 45,180,32, 4, 4,0x020c,0x0006,-1, 2,0x1100,scompare1, 0,0,0,0,0,0) + XTREG( 46,184,48, 8, 8,0x0060,0x0006, 1, 4,0x0101,aep0, + "03:04:84:b2","03:04:84:a7",0,0,0,0) + XTREG( 47,192,48, 8, 8,0x0061,0x0006, 1, 4,0x0101,aep1, + "03:04:94:b2","03:04:94:a7",0,0,0,0) + XTREG( 48,200,48, 8, 8,0x0062,0x0006, 1, 4,0x0101,aep2, + "03:04:a4:b2","03:04:a4:a7",0,0,0,0) + XTREG( 49,208,48, 8, 8,0x0063,0x0006, 1, 4,0x0101,aep3, + "03:04:b4:b2","03:04:b4:a7",0,0,0,0) + XTREG( 50,216,48, 8, 8,0x0064,0x0006, 1, 4,0x0101,aep4, + "03:04:c4:b2","03:04:c4:a7",0,0,0,0) + XTREG( 51,224,48, 8, 8,0x0065,0x0006, 1, 4,0x0101,aep5, + "03:04:d4:b2","03:04:d4:a7",0,0,0,0) + XTREG( 52,232,48, 8, 8,0x0066,0x0006, 1, 4,0x0101,aep6, + "03:04:e4:b2","03:04:e4:a7",0,0,0,0) + XTREG( 53,240,48, 8, 8,0x0067,0x0006, 1, 4,0x0101,aep7, + "03:04:f4:b2","03:04:f4:a7",0,0,0,0) + XTREG( 54,248,56, 8, 8,0x0068,0x0006, 1, 4,0x0101,aeq0, + "03:04:04:c3","03:04:04:c1",0,0,0,0) + XTREG( 55,256,56, 8, 8,0x0069,0x0006, 1, 4,0x0101,aeq1, + "03:04:14:c3","03:04:44:c1",0,0,0,0) + XTREG( 56,264,56, 8, 8,0x006a,0x0006, 1, 4,0x0101,aeq2, + "03:04:24:c3","03:04:84:c1",0,0,0,0) + XTREG( 57,272,56, 8, 8,0x006b,0x0006, 1, 4,0x0101,aeq3, + "03:04:34:c3","03:04:c4:c1",0,0,0,0) + XTREG( 58,280, 7, 4, 4,0x03f0,0x0006, 1, 3,0x0100,ae_ovf_sar, 0,0,0,0,0,0) + XTREG( 59,284,32, 4, 4,0x03f1,0x0006, 1, 3,0x0110,ae_bithead, 0,0,0,0,0,0) + XTREG( 60,288,16, 4, 4,0x03f2,0x0006, 1, 3,0x0100,ae_ts_fts_bu_bp,0,0,0,0,0,0) + XTREG( 61,292,28, 4, 4,0x03f3,0x0006, 1, 3,0x0100,ae_sd_no, 0,0,0,0,0,0) + XTREG( 62,296,32, 4, 4,0x0253,0x0007,-2, 2,0x1000,ptevaddr, 0,0,0,0,0,0) + XTREG( 63,300,32, 4, 4,0x025a,0x0007,-2, 2,0x1000,rasid, 0,0,0,0,0,0) + XTREG( 64,304,18, 4, 4,0x025b,0x0007,-2, 2,0x1000,itlbcfg, 0,0,0,0,0,0) + XTREG( 65,308,18, 4, 4,0x025c,0x0007,-2, 2,0x1000,dtlbcfg, 0,0,0,0,0,0) + XTREG( 66,312, 6, 4, 4,0x0263,0x0007,-2, 2,0x1000,atomctl, 0,0,0,0,0,0) + XTREG( 67,316,32, 4, 4,0x0268,0x0007,-2, 2,0x1000,ddr, 0,0,0,0,0,0) + XTREG( 68,320,32, 4, 4,0x02b1,0x0007,-2, 2,0x1000,epc1, 0,0,0,0,0,0) + XTREG( 69,324,32, 4, 4,0x02b2,0x0007,-2, 2,0x1000,epc2, 0,0,0,0,0,0) + XTREG( 70,328,32, 4, 4,0x02c0,0x0007,-2, 2,0x1000,depc, 0,0,0,0,0,0) + XTREG( 71,332,19, 4, 4,0x02c2,0x0007,-2, 2,0x1000,eps2, 0,0,0,0,0,0) + XTREG( 72,336,32, 4, 4,0x02d1,0x0007,-2, 2,0x1000,excsave1, 0,0,0,0,0,0) + XTREG( 73,340,32, 4, 4,0x02d2,0x0007,-2, 2,0x1000,excsave2, 0,0,0,0,0,0) + XTREG( 74,344, 2, 4, 4,0x02e0,0x0007,-2, 2,0x1000,cpenable, 0,0,0,0,0,0) + XTREG( 75,348,12, 4, 4,0x02e2,0x000b,-2, 2,0x1000,interrupt, 0,0,0,0,0,0) + XTREG( 76,352,12, 4, 4,0x02e2,0x000d,-2, 2,0x1000,intset, 0,0,0,0,0,0) + XTREG( 77,356,12, 4, 4,0x02e3,0x000d,-2, 2,0x1000,intclear, 0,0,0,0,0,0) + XTREG( 78,360,12, 4, 4,0x02e4,0x0007,-2, 2,0x1000,intenable, 0,0,0,0,0,0) + XTREG( 79,364,32, 4, 4,0x02e7,0x0007,-2, 2,0x1000,vecbase, 0,0,0,0,0,0) + XTREG( 80,368, 6, 4, 4,0x02e8,0x0007,-2, 2,0x1000,exccause, 0,0,0,0,0,0) + XTREG( 81,372,12, 4, 4,0x02e9,0x0003,-2, 2,0x1000,debugcause, 0,0,0,0,0,0) + XTREG( 82,376,32, 4, 4,0x02ea,0x000f,-2, 2,0x1000,ccount, 0,0,0,0,0,0) + XTREG( 83,380,32, 4, 4,0x02eb,0x0003,-2, 2,0x1000,prid, 0,0,0,0,0,0) + XTREG( 84,384,32, 4, 4,0x02ec,0x000f,-2, 2,0x1000,icount, 0,0,0,0,0,0) + XTREG( 85,388, 4, 4, 4,0x02ed,0x0007,-2, 2,0x1000,icountlevel, 0,0,0,0,0,0) + XTREG( 86,392,32, 4, 4,0x02ee,0x0007,-2, 2,0x1000,excvaddr, 0,0,0,0,0,0) + XTREG( 87,396,32, 4, 4,0x02f0,0x000f,-2, 2,0x1000,ccompare0, 0,0,0,0,0,0) + XTREG( 88,400,32, 4, 4,0x02f1,0x000f,-2, 2,0x1000,ccompare1, 0,0,0,0,0,0) + XTREG( 89,404,32, 4, 4,0x02f4,0x0007,-2, 2,0x1000,misc0, 0,0,0,0,0,0) + XTREG( 90,408,32, 4, 4,0x02f5,0x0007,-2, 2,0x1000,misc1, 0,0,0,0,0,0) + XTREG( 91,412,32, 4, 4,0x0000,0x0006,-2, 8,0x0100,a0, 0,0,0,0,0,0) + XTREG( 92,416,32, 4, 4,0x0001,0x0006,-2, 8,0x0100,a1, 0,0,0,0,0,0) + XTREG( 93,420,32, 4, 4,0x0002,0x0006,-2, 8,0x0100,a2, 0,0,0,0,0,0) + XTREG( 94,424,32, 4, 4,0x0003,0x0006,-2, 8,0x0100,a3, 0,0,0,0,0,0) + XTREG( 95,428,32, 4, 4,0x0004,0x0006,-2, 8,0x0100,a4, 0,0,0,0,0,0) + XTREG( 96,432,32, 4, 4,0x0005,0x0006,-2, 8,0x0100,a5, 0,0,0,0,0,0) + XTREG( 97,436,32, 4, 4,0x0006,0x0006,-2, 8,0x0100,a6, 0,0,0,0,0,0) + XTREG( 98,440,32, 4, 4,0x0007,0x0006,-2, 8,0x0100,a7, 0,0,0,0,0,0) + XTREG( 99,444,32, 4, 4,0x0008,0x0006,-2, 8,0x0100,a8, 0,0,0,0,0,0) + XTREG(100,448,32, 4, 4,0x0009,0x0006,-2, 8,0x0100,a9, 0,0,0,0,0,0) + XTREG(101,452,32, 4, 4,0x000a,0x0006,-2, 8,0x0100,a10, 0,0,0,0,0,0) + XTREG(102,456,32, 4, 4,0x000b,0x0006,-2, 8,0x0100,a11, 0,0,0,0,0,0) + XTREG(103,460,32, 4, 4,0x000c,0x0006,-2, 8,0x0100,a12, 0,0,0,0,0,0) + XTREG(104,464,32, 4, 4,0x000d,0x0006,-2, 8,0x0100,a13, 0,0,0,0,0,0) + XTREG(105,468,32, 4, 4,0x000e,0x0006,-2, 8,0x0100,a14, 0,0,0,0,0,0) + XTREG(106,472,32, 4, 4,0x000f,0x0006,-2, 8,0x0100,a15, 0,0,0,0,0,0) + XTREG_END diff --git a/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c b/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c new file mode 100644 index 0000000000..687631b8fb --- /dev/null +++ b/target/xtensa/core-test_mmuhifi_c3/xtensa-modules.inc.c @@ -0,0 +1,36403 @@ +/* Xtensa configuration-specific ISA information. + Copyright 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc. + + This file is part of BFD, the Binary File Descriptor library. + + 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 3 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. */ + +#include "qemu/osdep.h" +#include "xtensa-isa.h" +#include "xtensa-isa-internal.h" + + +/* Sysregs. */ + +static xtensa_sysreg_internal sysregs[] = { + { "LBEG", 0, 0 }, + { "LEND", 1, 0 }, + { "LCOUNT", 2, 0 }, + { "BR", 4, 0 }, + { "PTEVADDR", 83, 0 }, + { "DDR", 104, 0 }, + { "176", 176, 0 }, + { "208", 208, 0 }, + { "INTERRUPT", 226, 0 }, + { "INTCLEAR", 227, 0 }, + { "CCOUNT", 234, 0 }, + { "PRID", 235, 0 }, + { "ICOUNT", 236, 0 }, + { "CCOMPARE0", 240, 0 }, + { "CCOMPARE1", 241, 0 }, + { "VECBASE", 231, 0 }, + { "EPC1", 177, 0 }, + { "EPC2", 178, 0 }, + { "EXCSAVE1", 209, 0 }, + { "EXCSAVE2", 210, 0 }, + { "EPS2", 194, 0 }, + { "EXCCAUSE", 232, 0 }, + { "DEPC", 192, 0 }, + { "EXCVADDR", 238, 0 }, + { "WINDOWBASE", 72, 0 }, + { "WINDOWSTART", 73, 0 }, + { "SAR", 3, 0 }, + { "LITBASE", 5, 0 }, + { "PS", 230, 0 }, + { "MISC0", 244, 0 }, + { "MISC1", 245, 0 }, + { "INTENABLE", 228, 0 }, + { "ICOUNTLEVEL", 237, 0 }, + { "DEBUGCAUSE", 233, 0 }, + { "RASID", 90, 0 }, + { "ITLBCFG", 91, 0 }, + { "DTLBCFG", 92, 0 }, + { "CPENABLE", 224, 0 }, + { "SCOMPARE1", 12, 0 }, + { "ATOMCTL", 99, 0 }, + { "THREADPTR", 231, 1 }, + { "AE_OVF_SAR", 240, 1 }, + { "AE_BITHEAD", 241, 1 }, + { "AE_TS_FTS_BU_BP", 242, 1 }, + { "AE_SD_NO", 243, 1 } +}; + +#define NUM_SYSREGS 45 +#define MAX_SPECIAL_REG 245 +#define MAX_USER_REG 243 + + +/* Processor states. */ + +static xtensa_state_internal states[] = { + { "LCOUNT", 32, 0 }, + { "PC", 32, 0 }, + { "ICOUNT", 32, 0 }, + { "DDR", 32, 0 }, + { "INTERRUPT", 12, 0 }, + { "CCOUNT", 32, 0 }, + { "XTSYNC", 1, 0 }, + { "VECBASE", 22, 0 }, + { "EPC1", 32, 0 }, + { "EPC2", 32, 0 }, + { "EXCSAVE1", 32, 0 }, + { "EXCSAVE2", 32, 0 }, + { "EPS2", 15, 0 }, + { "EXCCAUSE", 6, 0 }, + { "PSINTLEVEL", 4, 0 }, + { "PSUM", 1, 0 }, + { "PSWOE", 1, 0 }, + { "PSRING", 2, 0 }, + { "PSEXCM", 1, 0 }, + { "DEPC", 32, 0 }, + { "EXCVADDR", 32, 0 }, + { "WindowBase", 3, 0 }, + { "WindowStart", 8, 0 }, + { "PSCALLINC", 2, 0 }, + { "PSOWB", 4, 0 }, + { "LBEG", 32, 0 }, + { "LEND", 32, 0 }, + { "SAR", 6, 0 }, + { "THREADPTR", 32, 0 }, + { "LITBADDR", 20, 0 }, + { "LITBEN", 1, 0 }, + { "MISC0", 32, 0 }, + { "MISC1", 32, 0 }, + { "InOCDMode", 1, 0 }, + { "INTENABLE", 12, 0 }, + { "ICOUNTLEVEL", 4, 0 }, + { "DEBUGCAUSE", 6, 0 }, + { "DBNUM", 4, 0 }, + { "CCOMPARE0", 32, 0 }, + { "CCOMPARE1", 32, 0 }, + { "ASID3", 8, 0 }, + { "ASID2", 8, 0 }, + { "ASID1", 8, 0 }, + { "INSTPGSZID4", 2, 0 }, + { "DATAPGSZID4", 2, 0 }, + { "PTBASE", 10, 0 }, + { "CPENABLE", 2, 0 }, + { "SCOMPARE1", 32, 0 }, + { "ATOMCTL", 6, 0 }, + { "CCON", 1, XTENSA_STATE_IS_EXPORTED }, + { "MPSCORE", 16, XTENSA_STATE_IS_EXPORTED }, + { "WMPINT_ADDR", 12, XTENSA_STATE_IS_EXPORTED }, + { "WMPINT_DATA", 32, XTENSA_STATE_IS_EXPORTED }, + { "WMPINT_TOGGLEEN", 1, XTENSA_STATE_IS_EXPORTED }, + { "AE_OVERFLOW", 1, 0 }, + { "AE_SAR", 6, 0 }, + { "AE_BITHEAD", 32, 0 }, + { "AE_BITPTR", 4, 0 }, + { "AE_BITSUSED", 4, 0 }, + { "AE_TABLESIZE", 4, 0 }, + { "AE_FIRST_TS", 4, 0 }, + { "AE_NEXTOFFSET", 27, 0 }, + { "AE_SEARCHDONE", 1, 0 } +}; + +#define NUM_STATES 63 + +enum xtensa_state_id { + STATE_LCOUNT, + STATE_PC, + STATE_ICOUNT, + STATE_DDR, + STATE_INTERRUPT, + STATE_CCOUNT, + STATE_XTSYNC, + STATE_VECBASE, + STATE_EPC1, + STATE_EPC2, + STATE_EXCSAVE1, + STATE_EXCSAVE2, + STATE_EPS2, + STATE_EXCCAUSE, + STATE_PSINTLEVEL, + STATE_PSUM, + STATE_PSWOE, + STATE_PSRING, + STATE_PSEXCM, + STATE_DEPC, + STATE_EXCVADDR, + STATE_WindowBase, + STATE_WindowStart, + STATE_PSCALLINC, + STATE_PSOWB, + STATE_LBEG, + STATE_LEND, + STATE_SAR, + STATE_THREADPTR, + STATE_LITBADDR, + STATE_LITBEN, + STATE_MISC0, + STATE_MISC1, + STATE_InOCDMode, + STATE_INTENABLE, + STATE_ICOUNTLEVEL, + STATE_DEBUGCAUSE, + STATE_DBNUM, + STATE_CCOMPARE0, + STATE_CCOMPARE1, + STATE_ASID3, + STATE_ASID2, + STATE_ASID1, + STATE_INSTPGSZID4, + STATE_DATAPGSZID4, + STATE_PTBASE, + STATE_CPENABLE, + STATE_SCOMPARE1, + STATE_ATOMCTL, + STATE_CCON, + STATE_MPSCORE, + STATE_WMPINT_ADDR, + STATE_WMPINT_DATA, + STATE_WMPINT_TOGGLEEN, + STATE_AE_OVERFLOW, + STATE_AE_SAR, + STATE_AE_BITHEAD, + STATE_AE_BITPTR, + STATE_AE_BITSUSED, + STATE_AE_TABLESIZE, + STATE_AE_FIRST_TS, + STATE_AE_NEXTOFFSET, + STATE_AE_SEARCHDONE +}; + + +/* Field definitions. */ + +static unsigned +Field_t_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_t_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_s_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_s_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_r_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_r_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_op2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 8) >> 28); + return tie_t; +} + +static void +Field_op2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00000) | (tie_t << 20); +} + +static unsigned +Field_op1_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_op1_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_op0_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_op0_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_n_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_n_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_m_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_m_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_sr_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sr_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_st_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_st_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_thi3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_thi3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_ae_r3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_ae_r3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_ae_r10_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_ae_r10_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ae_r32_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_ae_r32_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ae_s3_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_ae_s3_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_ae_s_non_samt_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_ae_s_non_samt_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_op0_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_op0_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_t_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_t_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_r_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_r_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_op0_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_op0_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_z_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + return tie_t; +} + +static void +Field_z_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); +} + +static unsigned +Field_i_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_i_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_s_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_s_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_ftsf61ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf61ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 22) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_op0_s3_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 9) >> 25); + return tie_t; +} + +static void +Field_op0_s3_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf330ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_ftsf330ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); +} + +static unsigned +Field_ftsf81ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf81ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ae_r20_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ae_r20_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_ftsf73ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf73ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf35ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf35ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf34ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf34ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf32ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf32ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf33ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf33ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf96ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ftsf96ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ae_s20_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ae_s20_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); +} + +static unsigned +Field_ftsf94ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 29) >> 31); + return tie_t; +} + +static void +Field_ftsf94ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4) | (tie_t << 2); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf347_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 30) >> 30); + return tie_t; +} + +static void +Field_ftsf347_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3) | (tie_t << 0); +} + +static unsigned +Field_ftsf24ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + return tie_t; +} + +static void +Field_ftsf24ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf23ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + return tie_t; +} + +static void +Field_ftsf23ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf125ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 18) >> 30); + return tie_t; +} + +static void +Field_ftsf125ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3000) | (tie_t << 12); +} + +static unsigned +Field_ftsf350ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + tie_t = (tie_t << 4) | ((insn[0] << 25) >> 28); + return tie_t; +} + +static void +Field_ftsf350ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x78) | (tie_t << 3); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf80ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf80ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf88ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 16) >> 25); + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf88ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); + tie_t = (val << 23) >> 25; + insn[0] = (insn[0] & ~0xfe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf340_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf340_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_ftsf87ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 16) >> 25); + tie_t = (tie_t << 2) | ((insn[0] << 25) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf87ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x60) | (tie_t << 5); + tie_t = (val << 22) >> 25; + insn[0] = (insn[0] & ~0xfe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf342ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + return tie_t; +} + +static void +Field_ftsf342ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_ftsf86ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 16) >> 25); + tie_t = (tie_t << 4) | ((insn[0] << 25) >> 28); + return tie_t; +} + +static void +Field_ftsf86ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x78) | (tie_t << 3); + tie_t = (val << 21) >> 25; + insn[0] = (insn[0] & ~0xfe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf84ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 16) >> 25); + tie_t = (tie_t << 4) | ((insn[0] << 25) >> 28); + return tie_t; +} + +static void +Field_ftsf84ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x78) | (tie_t << 3); + tie_t = (val << 21) >> 25; + insn[0] = (insn[0] & ~0xfe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf76ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf76ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf75ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf75ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf60ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf60ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 21) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf64ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf64ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 20) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf63ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf63ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ae_r10_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + return tie_t; +} + +static void +Field_ae_r10_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); +} + +static unsigned +Field_ftsf59ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf59ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 21) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf119ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf119ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 24) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 21) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf338_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf338_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_ftsf69ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf69ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); + tie_t = (val << 22) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf67ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 2) | ((insn[0] << 25) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf67ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x60) | (tie_t << 5); + tie_t = (val << 21) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf66ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf66ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 20) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf25ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf25ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf36ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf36ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf103ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ftsf103ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf349ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 6) | ((insn[0] << 23) >> 26); + return tie_t; +} + +static void +Field_ftsf349ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 26) >> 26; + insn[0] = (insn[0] & ~0x1f8) | (tie_t << 3); +} + +static unsigned +Field_ftsf99ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf99ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf27ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf27ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf28ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf28ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf21ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ftsf21ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf22ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ftsf22ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf29ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf29ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf97ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf97ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf100ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf100ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf101ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + return tie_t; +} + +static void +Field_ftsf101ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf348ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 24) >> 27); + return tie_t; +} + +static void +Field_ftsf348ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf8) | (tie_t << 3); +} + +static unsigned +Field_ftsf26ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf26ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf30ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf30ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf31ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf31ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf98ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf98ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf92ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 29) >> 30); + return tie_t; +} + +static void +Field_ftsf92ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x6) | (tie_t << 1); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf208_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf208_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); +} + +static unsigned +Field_ftsf91ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ftsf91ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf90ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ftsf90ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); + tie_t = (val << 25) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf126ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 18) >> 31); + return tie_t; +} + +static void +Field_ftsf126ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x2000) | (tie_t << 13); +} + +static unsigned +Field_ftsf344ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 19) >> 30); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf344ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 23) >> 30; + insn[0] = (insn[0] & ~0x1800) | (tie_t << 11); +} + +static unsigned +Field_ftsf112ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf112ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf122ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 5) | ((insn[0] << 25) >> 27); + return tie_t; +} + +static void +Field_ftsf122ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x7c) | (tie_t << 2); + tie_t = (val << 24) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf346ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 30) >> 30); + return tie_t; +} + +static void +Field_ftsf346ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3) | (tie_t << 0); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); +} + +static unsigned +Field_ftsf116ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 9) | ((insn[0] << 23) >> 23); + return tie_t; +} + +static void +Field_ftsf116ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0x1ff) | (tie_t << 0); + tie_t = (val << 20) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf109ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf109ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf111ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf111ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf104ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ftsf104ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf105ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ftsf105ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf107ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf107ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf113ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf113ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf118ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 9) | ((insn[0] << 23) >> 23); + return tie_t; +} + +static void +Field_ftsf118ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0x1ff) | (tie_t << 0); + tie_t = (val << 20) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf120ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 6) | ((insn[0] << 25) >> 26); + return tie_t; +} + +static void +Field_ftsf120ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 26) >> 26; + insn[0] = (insn[0] & ~0x7e) | (tie_t << 1); + tie_t = (val << 23) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf343ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf343ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); +} + +static unsigned +Field_ftsf108ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf108ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf115ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf115ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf110ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf110ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf114ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 7) | ((insn[0] << 25) >> 25); + return tie_t; +} + +static void +Field_ftsf114ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f) | (tie_t << 0); + tie_t = (val << 22) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf37ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + return tie_t; +} + +static void +Field_ftsf37ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf78ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf78ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf79ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf79ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf77ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 16) >> 23); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf77ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 22) >> 23; + insn[0] = (insn[0] & ~0xff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf13_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_ftsf13_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf12_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + return tie_t; +} + +static void +Field_ftsf12_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf82ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 16) >> 25); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf82ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 24) >> 25; + insn[0] = (insn[0] & ~0xfe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf341ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ftsf341ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); +} + +static unsigned +Field_ftsf124ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + return tie_t; +} + +static void +Field_ftsf124ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); + tie_t = (val << 28) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ftsf339ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf339ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); +} + +static unsigned +Field_ftsf106ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 18) >> 29); + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ftsf106ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); + tie_t = (val << 26) >> 29; + insn[0] = (insn[0] & ~0x3800) | (tie_t << 11); +} + +static unsigned +Field_ae_r32_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 21) >> 30); + return tie_t; +} + +static void +Field_ae_r32_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x600) | (tie_t << 9); +} + +static unsigned +Field_ftsf160ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf160ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf154ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf154ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf175ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf175ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf158ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf158ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf155ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf155ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf167ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf167ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf157ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf157ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf153ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf153ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf163ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf163ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf156ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf156ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf152ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf152ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf161ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf161ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf133ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf133ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf191ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf191ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf142ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf142ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf132ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf132ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf159ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf159ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf141ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf141ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf130ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf130ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf143ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf143ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf140ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf140ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf211ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_ftsf211ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_ftsf332ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 17) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf332ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x4000) | (tie_t << 14); +} + +static unsigned +Field_ftsf135ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf135ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf138ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf138ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf176ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf176ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf170ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf170ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf184ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf184ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf174ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf174ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf171ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf171ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf182ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf182ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf173ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf173ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf169ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf169ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf181ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf181ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf172ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf172ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf168ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf168ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf180ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf180ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf139ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf139ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf151ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf151ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf137ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf137ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf147ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf147ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf136ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf136ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf145ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf145ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf134ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf134ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf144ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf144ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf178ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf178ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf188ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf188ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf183ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf183ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf186ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf186ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf179ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf179ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf187ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf187ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf177ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf177ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf185ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf185ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf45ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf45ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf44ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf44ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf48ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf48ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf47ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf47ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf49ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf49ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf50ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf50ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf52ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf52ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf51ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf51ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf38ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf38ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf54ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf54ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf40ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf40ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf39ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf39ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf46ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf46ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf42ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf42ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf43ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf43ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf41ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf41ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf55ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf55ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf53ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf53ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf58ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf58ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf56ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf56ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf72ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf72ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 26) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf71ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf71ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 26) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf57ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 16) >> 27); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf57ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 23) >> 27; + insn[0] = (insn[0] & ~0xf800) | (tie_t << 11); +} + +static unsigned +Field_ftsf89ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_ftsf89ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_ftsf334ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf334ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_t_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_t_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_ftsf195ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf195ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf207ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf207ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf336ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 28) >> 29); + return tie_t; +} + +static void +Field_ftsf336ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe) | (tie_t << 1); +} + +static unsigned +Field_ftsf199ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf199ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf210ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + return tie_t; +} + +static void +Field_ftsf210ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf337ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf337ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_ftsf194ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf194ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf197ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf197ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf196ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf196ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf198ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf198ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf200ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf200ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf203ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf203ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf201ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf201ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf202ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf202ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf204ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf204ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf206ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf206ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf205ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf205ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf209ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf209ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf127ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf127ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf129ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf129ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf128ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf128ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf131ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf131ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf146ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf146ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf149ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf149ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf148ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf148ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf150ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf150ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf162ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf162ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf165ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf165ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf164ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf164ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf166ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf166ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf189ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf189ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf192ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf192ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf190ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf190ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_ftsf193ae_slot1_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf193ae_slot1_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); + tie_t = (val << 24) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_r_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_r_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_op0_s4_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 5) >> 25); + return tie_t; +} + +static void +Field_op0_s4_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0x7f00000) | (tie_t << 20); +} + +static unsigned +Field_imm8_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + return tie_t; +} + +static void +Field_imm8_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_t_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_t_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); +} + +static unsigned +Field_ftsf293_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_ftsf293_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_ftsf321_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf321_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); +} + +static unsigned +Field_ae_s20_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ae_s20_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); +} + +static unsigned +Field_ftsf214ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 12) >> 28); + return tie_t; +} + +static void +Field_ftsf214ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0000) | (tie_t << 16); +} + +static unsigned +Field_ftsf213ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 12) >> 29); + return tie_t; +} + +static void +Field_ftsf213ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0000) | (tie_t << 17); +} + +static unsigned +Field_ftsf212ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 12) >> 30); + return tie_t; +} + +static void +Field_ftsf212ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0000) | (tie_t << 18); +} + +static unsigned +Field_ftsf281ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 8) | ((insn[0] << 24) >> 24); + return tie_t; +} + +static void +Field_ftsf281ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff) | (tie_t << 0); + tie_t = (val << 16) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf217_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf217_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_ae_r20_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ae_r20_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_ftsf300ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 12) >> 20); + return tie_t; +} + +static void +Field_ftsf300ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0xfff00) | (tie_t << 8); +} + +static unsigned +Field_ftsf283ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 6) | ((insn[0] << 26) >> 26); + return tie_t; +} + +static void +Field_ftsf283ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 26) >> 26; + insn[0] = (insn[0] & ~0x3f) | (tie_t << 0); + tie_t = (val << 25) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 17) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf352ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + return tie_t; +} + +static void +Field_ftsf352ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_ftsf282ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 8) | ((insn[0] << 24) >> 24); + return tie_t; +} + +static void +Field_ftsf282ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff) | (tie_t << 0); + tie_t = (val << 16) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf288ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 3) | ((insn[0] << 26) >> 29); + return tie_t; +} + +static void +Field_ftsf288ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x38) | (tie_t << 3); + tie_t = (val << 21) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf359ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ftsf359ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); + tie_t = (val << 27) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_ftsf286ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 4) | ((insn[0] << 26) >> 28); + return tie_t; +} + +static void +Field_ftsf286ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0x3c) | (tie_t << 2); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf356ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 30) >> 30); + return tie_t; +} + +static void +Field_ftsf356ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3) | (tie_t << 0); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_ftsf284ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 5) | ((insn[0] << 26) >> 27); + return tie_t; +} + +static void +Field_ftsf284ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x3e) | (tie_t << 1); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf354ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf354ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_ftsf295ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 26) >> 31); + return tie_t; +} + +static void +Field_ftsf295ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x20) | (tie_t << 5); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf358ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + return tie_t; +} + +static void +Field_ftsf358ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_ftsf325ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf325ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf215ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 7) | ((insn[0] << 12) >> 25); + return tie_t; +} + +static void +Field_ftsf215ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 25) >> 25; + insn[0] = (insn[0] & ~0xfe000) | (tie_t << 13); +} + +static unsigned +Field_ftsf301ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 13) | ((insn[0] << 12) >> 19); + return tie_t; +} + +static void +Field_ftsf301ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 19) >> 19; + insn[0] = (insn[0] & ~0xfff80) | (tie_t << 7); +} + +static unsigned +Field_ftsf353_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + return tie_t; +} + +static void +Field_ftsf353_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); +} + +static unsigned +Field_ftsf309ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 9) | ((insn[0] << 12) >> 23); + return tie_t; +} + +static void +Field_ftsf309ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 23) >> 23; + insn[0] = (insn[0] & ~0xff800) | (tie_t << 11); +} + +static unsigned +Field_ftsf360ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 21) >> 27); + return tie_t; +} + +static void +Field_ftsf360ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x7c0) | (tie_t << 6); +} + +static unsigned +Field_ftsf294ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_ftsf294ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); + tie_t = (val << 21) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_s_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_s_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_ftsf292ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_ftsf292ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); + tie_t = (val << 21) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf319_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 28) >> 29); + return tie_t; +} + +static void +Field_ftsf319_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe) | (tie_t << 1); +} + +static unsigned +Field_ftsf361ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf361ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_ftsf218ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf218ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf220ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf220ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf221ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf221ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf222ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf222ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf228ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf228ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf229ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf229ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf230ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf230ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf232ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf232ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf233ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf233ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf235ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf235ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf239ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf239ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf234ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf234ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf224ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf224ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf225ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf225ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf227ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf227ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf226ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf226ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf241ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf241ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf243ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf243ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf242ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf242ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf244ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf244ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf236ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf236ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf237ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf237ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf238ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf238ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf240ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf240ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf261ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf261ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf296ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf296ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf248ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf248ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf250ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf250ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf269ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf269ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf264ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf264ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf266ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf266ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf267ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf267ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf260ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf260ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf262ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf262ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf263ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf263ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf265ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf265ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf246ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf246ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf247ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf247ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf249ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf249ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf253ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf253ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf257ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf257ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf256ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf256ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf258ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf258ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf259ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf259ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf251ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf251ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf252ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf252ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf254ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf254ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf255ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf255ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf275ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf275ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf277ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf277ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf278ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf278ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf290ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 26) >> 31); + return tie_t; +} + +static void +Field_ftsf290ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x20) | (tie_t << 5); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_s8_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_ftsf272ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf272ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf276ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf276ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf273ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf273ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf274ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf274ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf297ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf297ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf298ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf298ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf310ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf310ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf311ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf311ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf270ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf270ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf271ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf271ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ae_r32_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ae_r32_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_ftsf329ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 12) >> 27); + return tie_t; +} + +static void +Field_ftsf329ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0xf8000) | (tie_t << 15); +} + +static unsigned +Field_ftsf362ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 17) >> 29); + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_ftsf362ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); + tie_t = (val << 27) >> 29; + insn[0] = (insn[0] & ~0x7000) | (tie_t << 12); +} + +static unsigned +Field_ftsf245ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf245ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf268ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf268ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf313ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf313ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf312ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf312ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf231ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf231ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf223ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf223ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf219ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf219ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf216ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_ftsf216ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf302ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 12) >> 20); + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ftsf302ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); + tie_t = (val << 17) >> 20; + insn[0] = (insn[0] & ~0xfff00) | (tie_t << 8); +} + +static unsigned +Field_ftsf364ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 19) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf364ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x1000) | (tie_t << 12); +} + +static unsigned +Field_ftsf322ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf322ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf279ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 6) | ((insn[0] << 26) >> 26); + return tie_t; +} + +static void +Field_ftsf279ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 26) >> 26; + insn[0] = (insn[0] & ~0x3f) | (tie_t << 0); + tie_t = (val << 18) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf318ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 3) | ((insn[0] << 28) >> 29); + return tie_t; +} + +static void +Field_ftsf318ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe) | (tie_t << 1); + tie_t = (val << 28) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf365ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf365ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); +} + +static unsigned +Field_ftsf316ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf316ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf314ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf314ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf315ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf315ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf320ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf320ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf299ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 10) | ((insn[0] << 12) >> 22); + return tie_t; +} + +static void +Field_ftsf299ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 22) >> 22; + insn[0] = (insn[0] & ~0xffc00) | (tie_t << 10); +} + +static unsigned +Field_ftsf308ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 11) | ((insn[0] << 12) >> 21); + return tie_t; +} + +static void +Field_ftsf308ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 21) >> 21; + insn[0] = (insn[0] & ~0xffe00) | (tie_t << 9); +} + +static unsigned +Field_ftsf366ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 23) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf366ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x100) | (tie_t << 8); +} + +static unsigned +Field_ftsf306ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 12) >> 20); + tie_t = (tie_t << 1) | ((insn[0] << 29) >> 31); + return tie_t; +} + +static void +Field_ftsf306ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x4) | (tie_t << 2); + tie_t = (val << 19) >> 20; + insn[0] = (insn[0] & ~0xfff00) | (tie_t << 8); +} + +static unsigned +Field_ftsf368ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + tie_t = (tie_t << 2) | ((insn[0] << 30) >> 30); + return tie_t; +} + +static void +Field_ftsf368ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3) | (tie_t << 0); + tie_t = (val << 29) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); +} + +static unsigned +Field_ftsf304ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 12) >> 20); + tie_t = (tie_t << 2) | ((insn[0] << 29) >> 30); + return tie_t; +} + +static void +Field_ftsf304ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x6) | (tie_t << 1); + tie_t = (val << 18) >> 20; + insn[0] = (insn[0] & ~0xfff00) | (tie_t << 8); +} + +static unsigned +Field_ftsf369ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + tie_t = (tie_t << 1) | ((insn[0] << 31) >> 31); + return tie_t; +} + +static void +Field_ftsf369ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1) | (tie_t << 0); + tie_t = (val << 30) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); +} + +static unsigned +Field_ftsf323ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf323ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf328ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf328ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 23) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf326ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 2) | ((insn[0] << 28) >> 30); + return tie_t; +} + +static void +Field_ftsf326ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc) | (tie_t << 2); + tie_t = (val << 22) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf357_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 30) >> 30); + return tie_t; +} + +static void +Field_ftsf357_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x3) | (tie_t << 0); +} + +static unsigned +Field_ftsf303ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 12) >> 20); + tie_t = (tie_t << 3) | ((insn[0] << 29) >> 29); + return tie_t; +} + +static void +Field_ftsf303ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7) | (tie_t << 0); + tie_t = (val << 17) >> 20; + insn[0] = (insn[0] & ~0xfff00) | (tie_t << 8); +} + +static unsigned +Field_ftsf324ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf324ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 20) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_ftsf317ae_slot0_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 12) >> 24); + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ftsf317ae_slot0_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); + tie_t = (val << 19) >> 24; + insn[0] = (insn[0] & ~0xff000) | (tie_t << 12); +} + +static unsigned +Field_t_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_t_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); +} + +static unsigned +Field_bbi4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 19) >> 31); + return tie_t; +} + +static void +Field_bbi4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x1000) | (tie_t << 12); +} + +static unsigned +Field_bbi_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 19) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_bbi_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x1000) | (tie_t << 12); +} + +static unsigned +Field_bbi_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 27) >> 27); + return tie_t; +} + +static void +Field_bbi_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x1f) | (tie_t << 0); +} + +static unsigned +Field_imm12_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 8) >> 20); + return tie_t; +} + +static void +Field_imm12_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0xfff000) | (tie_t << 12); +} + +static unsigned +Field_imm12_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 24) >> 24); + return tie_t; +} + +static void +Field_imm12_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff) | (tie_t << 0); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 8) | ((insn[0] << 8) >> 24); + return tie_t; +} + +static void +Field_imm8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff0000) | (tie_t << 16); +} + +static unsigned +Field_s_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_s_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_imm12b_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + tie_t = (tie_t << 8) | ((insn[0] << 8) >> 24); + return tie_t; +} + +static void +Field_imm12b_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 24) >> 24; + insn[0] = (insn[0] & ~0xff0000) | (tie_t << 16); + tie_t = (val << 20) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_imm12b_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 12) | ((insn[0] << 16) >> 20); + return tie_t; +} + +static void +Field_imm12b_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 20) >> 20; + insn[0] = (insn[0] & ~0xfff0) | (tie_t << 4); +} + +static unsigned +Field_imm16_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 16) | ((insn[0] << 8) >> 16); + return tie_t; +} + +static void +Field_imm16_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 16) >> 16; + insn[0] = (insn[0] & ~0xffff00) | (tie_t << 8); +} + +static unsigned +Field_imm16_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 16) | ((insn[0] << 12) >> 16); + return tie_t; +} + +static void +Field_imm16_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 16) >> 16; + insn[0] = (insn[0] & ~0xffff0) | (tie_t << 4); +} + +static unsigned +Field_offset_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 18) | ((insn[0] << 8) >> 14); + return tie_t; +} + +static void +Field_offset_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 14) >> 14; + insn[0] = (insn[0] & ~0xffffc0) | (tie_t << 6); +} + +static unsigned +Field_offset_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 18) | ((insn[0] << 14) >> 14); + return tie_t; +} + +static void +Field_offset_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 14) >> 14; + insn[0] = (insn[0] & ~0x3ffff) | (tie_t << 0); +} + +static unsigned +Field_op2_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_op2_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_r_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_r_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_sa4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 11) >> 31); + return tie_t; +} + +static void +Field_sa4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x100000) | (tie_t << 20); +} + +static unsigned +Field_sae4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + return tie_t; +} + +static void +Field_sae4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_sae_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 15) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sae_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x10000) | (tie_t << 16); +} + +static unsigned +Field_sae_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 15) >> 27); + return tie_t; +} + +static void +Field_sae_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x1f000) | (tie_t << 12); +} + +static unsigned +Field_sal_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 11) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_sal_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x100000) | (tie_t << 20); +} + +static unsigned +Field_sal_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 19) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_sal_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x1000) | (tie_t << 12); +} + +static unsigned +Field_sargt_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 11) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sargt_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x100000) | (tie_t << 20); +} + +static unsigned +Field_sargt_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 19) >> 27); + return tie_t; +} + +static void +Field_sargt_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x1f00) | (tie_t << 8); +} + +static unsigned +Field_sas4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + return tie_t; +} + +static void +Field_sas4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_sas_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 27) >> 31); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sas_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 27) >> 31; + insn[0] = (insn[0] & ~0x10) | (tie_t << 4); +} + +static unsigned +Field_sas_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 5) | ((insn[0] << 27) >> 27); + return tie_t; +} + +static void +Field_sas_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 27) >> 27; + insn[0] = (insn[0] & ~0x1f) | (tie_t << 0); +} + +static unsigned +Field_sr_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sr_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_sr_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + return tie_t; +} + +static void +Field_sr_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_st_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_st_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_st_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 20) >> 28); + tie_t = (tie_t << 4) | ((insn[0] << 24) >> 28); + return tie_t; +} + +static void +Field_st_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf0) | (tie_t << 4); + tie_t = (val << 24) >> 28; + insn[0] = (insn[0] & ~0xf00) | (tie_t << 8); +} + +static unsigned +Field_imm4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_mn_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_mn_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); + tie_t = (val << 28) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_i_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_i_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_imm6lo_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm6lo_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm6lo_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm6lo_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm6hi_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_imm6hi_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_imm6hi_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_imm6hi_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_imm7lo_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm7lo_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm7lo_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm7lo_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); +} + +static unsigned +Field_imm7hi_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_imm7hi_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_imm7hi_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_imm7hi_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_z_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 25) >> 31); + return tie_t; +} + +static void +Field_z_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x40) | (tie_t << 6); +} + +static unsigned +Field_imm6_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm6_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_imm6_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm6_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_imm7_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm7_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_imm7_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + tie_t = (tie_t << 4) | ((insn[0] << 16) >> 28); + return tie_t; +} + +static void +Field_imm7_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf000) | (tie_t << 12); + tie_t = (val << 25) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_t2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_t2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_t2_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_t2_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_t2_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 24) >> 29); + return tie_t; +} + +static void +Field_t2_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe0) | (tie_t << 5); +} + +static unsigned +Field_t2_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 23) >> 30); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_t2_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 29) >> 30; + insn[0] = (insn[0] & ~0x180) | (tie_t << 7); +} + +static unsigned +Field_s2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_s2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_s2_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_s2_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_s2_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 20) >> 29); + return tie_t; +} + +static void +Field_s2_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe00) | (tie_t << 9); +} + +static unsigned +Field_r2_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 16) >> 29); + return tie_t; +} + +static void +Field_r2_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe000) | (tie_t << 13); +} + +static unsigned +Field_r2_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 16) >> 29); + return tie_t; +} + +static void +Field_r2_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe000) | (tie_t << 13); +} + +static unsigned +Field_r2_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 16) >> 29); + return tie_t; +} + +static void +Field_r2_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0xe000) | (tie_t << 13); +} + +static unsigned +Field_t4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_t4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_t4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_t4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_t4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 24) >> 30); + return tie_t; +} + +static void +Field_t4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc0) | (tie_t << 6); +} + +static unsigned +Field_s4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_s4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_s4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 20) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc00) | (tie_t << 10); +} + +static unsigned +Field_s4_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_s4_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_r4_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_r4_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_r4_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_r4_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_r4_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 16) >> 30); + return tie_t; +} + +static void +Field_r4_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0xc000) | (tie_t << 14); +} + +static unsigned +Field_t8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_t8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_t8_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_t8_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_t8_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 24) >> 31); + return tie_t; +} + +static void +Field_t8_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x80) | (tie_t << 7); +} + +static unsigned +Field_s8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_s8_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_s8_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 20) >> 31); + return tie_t; +} + +static void +Field_s8_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x800) | (tie_t << 11); +} + +static unsigned +Field_r8_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_r8_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_r8_Slot_inst16a_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_r8_Slot_inst16a_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_r8_Slot_inst16b_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 1) | ((insn[0] << 16) >> 31); + return tie_t; +} + +static void +Field_r8_Slot_inst16b_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8000) | (tie_t << 15); +} + +static unsigned +Field_xt_wbr15_imm_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 15) | ((insn[0] << 8) >> 17); + return tie_t; +} + +static void +Field_xt_wbr15_imm_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 17) >> 17; + insn[0] = (insn[0] & ~0xfffe00) | (tie_t << 9); +} + +static unsigned +Field_xt_wbr18_imm_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 18) | ((insn[0] << 8) >> 14); + return tie_t; +} + +static void +Field_xt_wbr18_imm_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 14) >> 14; + insn[0] = (insn[0] & ~0xffffc0) | (tie_t << 6); +} + +static unsigned +Field_ae_samt_s_t_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 6) | ((insn[0] << 22) >> 26); + return tie_t; +} + +static void +Field_ae_samt_s_t_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 26) >> 26; + insn[0] = (insn[0] & ~0x3f0) | (tie_t << 4); +} + +static unsigned +Field_ae_samt_s_t_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 22) >> 30); + tie_t = (tie_t << 4) | ((insn[0] << 28) >> 28); + return tie_t; +} + +static void +Field_ae_samt_s_t_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 28) >> 28; + insn[0] = (insn[0] & ~0xf) | (tie_t << 0); + tie_t = (val << 26) >> 30; + insn[0] = (insn[0] & ~0x300) | (tie_t << 8); +} + +static unsigned +Field_ae_r20_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 17) >> 29); + return tie_t; +} + +static void +Field_ae_r20_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x7000) | (tie_t << 12); +} + +static unsigned +Field_ae_r10_Slot_ae_slot0_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ae_r10_Slot_ae_slot0_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_ae_s20_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 21) >> 29); + return tie_t; +} + +static void +Field_ae_s20_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x700) | (tie_t << 8); +} + +static unsigned +Field_ftsf12_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 3) | ((insn[0] << 25) >> 29); + return tie_t; +} + +static void +Field_ftsf12_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 29) >> 29; + insn[0] = (insn[0] & ~0x70) | (tie_t << 4); +} + +static unsigned +Field_ftsf13_Slot_inst_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 2) | ((insn[0] << 26) >> 30); + return tie_t; +} + +static void +Field_ftsf13_Slot_inst_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 30) >> 30; + insn[0] = (insn[0] & ~0x30) | (tie_t << 4); +} + +static unsigned +Field_ftsf14_Slot_ae_slot1_get (const xtensa_insnbuf insn) +{ + unsigned tie_t = 0; + tie_t = (tie_t << 4) | ((insn[0] << 21) >> 28); + tie_t = (tie_t << 1) | ((insn[0] << 28) >> 31); + return tie_t; +} + +static void +Field_ftsf14_Slot_ae_slot1_set (xtensa_insnbuf insn, uint32 val) +{ + uint32 tie_t; + tie_t = (val << 31) >> 31; + insn[0] = (insn[0] & ~0x8) | (tie_t << 3); + tie_t = (val << 27) >> 28; + insn[0] = (insn[0] & ~0x780) | (tie_t << 7); +} + +static void +Implicit_Field_set (xtensa_insnbuf insn ATTRIBUTE_UNUSED, + uint32 val ATTRIBUTE_UNUSED) +{ + /* Do nothing. */ +} + +static unsigned +Implicit_Field_ar0_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_ar4_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 4; +} + +static unsigned +Implicit_Field_ar8_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 8; +} + +static unsigned +Implicit_Field_ar12_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 12; +} + +static unsigned +Implicit_Field_bt16_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_bs16_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_br16_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +static unsigned +Implicit_Field_brall_get (const xtensa_insnbuf insn ATTRIBUTE_UNUSED) +{ + return 0; +} + +enum xtensa_field_id { + FIELD_t, + FIELD_bbi4, + FIELD_bbi, + FIELD_imm12, + FIELD_imm8, + FIELD_s, + FIELD_imm12b, + FIELD_imm16, + FIELD_m, + FIELD_n, + FIELD_offset, + FIELD_op0, + FIELD_op1, + FIELD_op2, + FIELD_r, + FIELD_sa4, + FIELD_sae4, + FIELD_sae, + FIELD_sal, + FIELD_sargt, + FIELD_sas4, + FIELD_sas, + FIELD_sr, + FIELD_st, + FIELD_thi3, + FIELD_imm4, + FIELD_mn, + FIELD_i, + FIELD_imm6lo, + FIELD_imm6hi, + FIELD_imm7lo, + FIELD_imm7hi, + FIELD_z, + FIELD_imm6, + FIELD_imm7, + FIELD_t2, + FIELD_s2, + FIELD_r2, + FIELD_t4, + FIELD_s4, + FIELD_r4, + FIELD_t8, + FIELD_s8, + FIELD_r8, + FIELD_xt_wbr15_imm, + FIELD_xt_wbr18_imm, + FIELD_ae_r3, + FIELD_ae_s_non_samt, + FIELD_ae_s3, + FIELD_ae_r32, + FIELD_ae_samt_s_t, + FIELD_ae_r20, + FIELD_ae_r10, + FIELD_ae_s20, + FIELD_op0_s3, + FIELD_ftsf12, + FIELD_ftsf13, + FIELD_ftsf14, + FIELD_ftsf21ae_slot1, + FIELD_ftsf22ae_slot1, + FIELD_ftsf23ae_slot1, + FIELD_ftsf24ae_slot1, + FIELD_ftsf25ae_slot1, + FIELD_ftsf26ae_slot1, + FIELD_ftsf27ae_slot1, + FIELD_ftsf28ae_slot1, + FIELD_ftsf29ae_slot1, + FIELD_ftsf30ae_slot1, + FIELD_ftsf31ae_slot1, + FIELD_ftsf32ae_slot1, + FIELD_ftsf33ae_slot1, + FIELD_ftsf34ae_slot1, + FIELD_ftsf35ae_slot1, + FIELD_ftsf36ae_slot1, + FIELD_ftsf37ae_slot1, + FIELD_ftsf38ae_slot1, + FIELD_ftsf39ae_slot1, + FIELD_ftsf40ae_slot1, + FIELD_ftsf41ae_slot1, + FIELD_ftsf42ae_slot1, + FIELD_ftsf43ae_slot1, + FIELD_ftsf44ae_slot1, + FIELD_ftsf45ae_slot1, + FIELD_ftsf46ae_slot1, + FIELD_ftsf47ae_slot1, + FIELD_ftsf48ae_slot1, + FIELD_ftsf49ae_slot1, + FIELD_ftsf50ae_slot1, + FIELD_ftsf51ae_slot1, + FIELD_ftsf52ae_slot1, + FIELD_ftsf53ae_slot1, + FIELD_ftsf54ae_slot1, + FIELD_ftsf55ae_slot1, + FIELD_ftsf56ae_slot1, + FIELD_ftsf57ae_slot1, + FIELD_ftsf58ae_slot1, + FIELD_ftsf59ae_slot1, + FIELD_ftsf60ae_slot1, + FIELD_ftsf61ae_slot1, + FIELD_ftsf63ae_slot1, + FIELD_ftsf64ae_slot1, + FIELD_ftsf66ae_slot1, + FIELD_ftsf67ae_slot1, + FIELD_ftsf69ae_slot1, + FIELD_ftsf71ae_slot1, + FIELD_ftsf72ae_slot1, + FIELD_ftsf73ae_slot1, + FIELD_ftsf75ae_slot1, + FIELD_ftsf76ae_slot1, + FIELD_ftsf77ae_slot1, + FIELD_ftsf78ae_slot1, + FIELD_ftsf79ae_slot1, + FIELD_ftsf80ae_slot1, + FIELD_ftsf81ae_slot1, + FIELD_ftsf82ae_slot1, + FIELD_ftsf84ae_slot1, + FIELD_ftsf86ae_slot1, + FIELD_ftsf87ae_slot1, + FIELD_ftsf88ae_slot1, + FIELD_ftsf89ae_slot1, + FIELD_ftsf90ae_slot1, + FIELD_ftsf91ae_slot1, + FIELD_ftsf92ae_slot1, + FIELD_ftsf94ae_slot1, + FIELD_ftsf96ae_slot1, + FIELD_ftsf97ae_slot1, + FIELD_ftsf98ae_slot1, + FIELD_ftsf99ae_slot1, + FIELD_ftsf100ae_slot1, + FIELD_ftsf101ae_slot1, + FIELD_ftsf103ae_slot1, + FIELD_ftsf104ae_slot1, + FIELD_ftsf105ae_slot1, + FIELD_ftsf106ae_slot1, + FIELD_ftsf107ae_slot1, + FIELD_ftsf108ae_slot1, + FIELD_ftsf109ae_slot1, + FIELD_ftsf110ae_slot1, + FIELD_ftsf111ae_slot1, + FIELD_ftsf112ae_slot1, + FIELD_ftsf113ae_slot1, + FIELD_ftsf114ae_slot1, + FIELD_ftsf115ae_slot1, + FIELD_ftsf116ae_slot1, + FIELD_ftsf118ae_slot1, + FIELD_ftsf119ae_slot1, + FIELD_ftsf120ae_slot1, + FIELD_ftsf122ae_slot1, + FIELD_ftsf124ae_slot1, + FIELD_ftsf125ae_slot1, + FIELD_ftsf126ae_slot1, + FIELD_ftsf127ae_slot1, + FIELD_ftsf128ae_slot1, + FIELD_ftsf129ae_slot1, + FIELD_ftsf130ae_slot1, + FIELD_ftsf131ae_slot1, + FIELD_ftsf132ae_slot1, + FIELD_ftsf133ae_slot1, + FIELD_ftsf134ae_slot1, + FIELD_ftsf135ae_slot1, + FIELD_ftsf136ae_slot1, + FIELD_ftsf137ae_slot1, + FIELD_ftsf138ae_slot1, + FIELD_ftsf139ae_slot1, + FIELD_ftsf140ae_slot1, + FIELD_ftsf141ae_slot1, + FIELD_ftsf142ae_slot1, + FIELD_ftsf143ae_slot1, + FIELD_ftsf144ae_slot1, + FIELD_ftsf145ae_slot1, + FIELD_ftsf146ae_slot1, + FIELD_ftsf147ae_slot1, + FIELD_ftsf148ae_slot1, + FIELD_ftsf149ae_slot1, + FIELD_ftsf150ae_slot1, + FIELD_ftsf151ae_slot1, + FIELD_ftsf152ae_slot1, + FIELD_ftsf153ae_slot1, + FIELD_ftsf154ae_slot1, + FIELD_ftsf155ae_slot1, + FIELD_ftsf156ae_slot1, + FIELD_ftsf157ae_slot1, + FIELD_ftsf158ae_slot1, + FIELD_ftsf159ae_slot1, + FIELD_ftsf160ae_slot1, + FIELD_ftsf161ae_slot1, + FIELD_ftsf162ae_slot1, + FIELD_ftsf163ae_slot1, + FIELD_ftsf164ae_slot1, + FIELD_ftsf165ae_slot1, + FIELD_ftsf166ae_slot1, + FIELD_ftsf167ae_slot1, + FIELD_ftsf168ae_slot1, + FIELD_ftsf169ae_slot1, + FIELD_ftsf170ae_slot1, + FIELD_ftsf171ae_slot1, + FIELD_ftsf172ae_slot1, + FIELD_ftsf173ae_slot1, + FIELD_ftsf174ae_slot1, + FIELD_ftsf175ae_slot1, + FIELD_ftsf176ae_slot1, + FIELD_ftsf177ae_slot1, + FIELD_ftsf178ae_slot1, + FIELD_ftsf179ae_slot1, + FIELD_ftsf180ae_slot1, + FIELD_ftsf181ae_slot1, + FIELD_ftsf182ae_slot1, + FIELD_ftsf183ae_slot1, + FIELD_ftsf184ae_slot1, + FIELD_ftsf185ae_slot1, + FIELD_ftsf186ae_slot1, + FIELD_ftsf187ae_slot1, + FIELD_ftsf188ae_slot1, + FIELD_ftsf189ae_slot1, + FIELD_ftsf190ae_slot1, + FIELD_ftsf191ae_slot1, + FIELD_ftsf192ae_slot1, + FIELD_ftsf193ae_slot1, + FIELD_ftsf194ae_slot1, + FIELD_ftsf195ae_slot1, + FIELD_ftsf196ae_slot1, + FIELD_ftsf197ae_slot1, + FIELD_ftsf198ae_slot1, + FIELD_ftsf199ae_slot1, + FIELD_ftsf200ae_slot1, + FIELD_ftsf201ae_slot1, + FIELD_ftsf202ae_slot1, + FIELD_ftsf203ae_slot1, + FIELD_ftsf204ae_slot1, + FIELD_ftsf205ae_slot1, + FIELD_ftsf206ae_slot1, + FIELD_ftsf207ae_slot1, + FIELD_ftsf208, + FIELD_ftsf209ae_slot1, + FIELD_ftsf210ae_slot1, + FIELD_ftsf211ae_slot1, + FIELD_ftsf330ae_slot1, + FIELD_ftsf332ae_slot1, + FIELD_ftsf334ae_slot1, + FIELD_ftsf336ae_slot1, + FIELD_ftsf337ae_slot1, + FIELD_ftsf338, + FIELD_ftsf339ae_slot1, + FIELD_ftsf340, + FIELD_ftsf341ae_slot1, + FIELD_ftsf342ae_slot1, + FIELD_ftsf343ae_slot1, + FIELD_ftsf344ae_slot1, + FIELD_ftsf346ae_slot1, + FIELD_ftsf347, + FIELD_ftsf348ae_slot1, + FIELD_ftsf349ae_slot1, + FIELD_ftsf350ae_slot1, + FIELD_op0_s4, + FIELD_ftsf212ae_slot0, + FIELD_ftsf213ae_slot0, + FIELD_ftsf214ae_slot0, + FIELD_ftsf215ae_slot0, + FIELD_ftsf216ae_slot0, + FIELD_ftsf217, + FIELD_ftsf218ae_slot0, + FIELD_ftsf219ae_slot0, + FIELD_ftsf220ae_slot0, + FIELD_ftsf221ae_slot0, + FIELD_ftsf222ae_slot0, + FIELD_ftsf223ae_slot0, + FIELD_ftsf224ae_slot0, + FIELD_ftsf225ae_slot0, + FIELD_ftsf226ae_slot0, + FIELD_ftsf227ae_slot0, + FIELD_ftsf228ae_slot0, + FIELD_ftsf229ae_slot0, + FIELD_ftsf230ae_slot0, + FIELD_ftsf231ae_slot0, + FIELD_ftsf232ae_slot0, + FIELD_ftsf233ae_slot0, + FIELD_ftsf234ae_slot0, + FIELD_ftsf235ae_slot0, + FIELD_ftsf236ae_slot0, + FIELD_ftsf237ae_slot0, + FIELD_ftsf238ae_slot0, + FIELD_ftsf239ae_slot0, + FIELD_ftsf240ae_slot0, + FIELD_ftsf241ae_slot0, + FIELD_ftsf242ae_slot0, + FIELD_ftsf243ae_slot0, + FIELD_ftsf244ae_slot0, + FIELD_ftsf245ae_slot0, + FIELD_ftsf246ae_slot0, + FIELD_ftsf247ae_slot0, + FIELD_ftsf248ae_slot0, + FIELD_ftsf249ae_slot0, + FIELD_ftsf250ae_slot0, + FIELD_ftsf251ae_slot0, + FIELD_ftsf252ae_slot0, + FIELD_ftsf253ae_slot0, + FIELD_ftsf254ae_slot0, + FIELD_ftsf255ae_slot0, + FIELD_ftsf256ae_slot0, + FIELD_ftsf257ae_slot0, + FIELD_ftsf258ae_slot0, + FIELD_ftsf259ae_slot0, + FIELD_ftsf260ae_slot0, + FIELD_ftsf261ae_slot0, + FIELD_ftsf262ae_slot0, + FIELD_ftsf263ae_slot0, + FIELD_ftsf264ae_slot0, + FIELD_ftsf265ae_slot0, + FIELD_ftsf266ae_slot0, + FIELD_ftsf267ae_slot0, + FIELD_ftsf268ae_slot0, + FIELD_ftsf269ae_slot0, + FIELD_ftsf270ae_slot0, + FIELD_ftsf271ae_slot0, + FIELD_ftsf272ae_slot0, + FIELD_ftsf273ae_slot0, + FIELD_ftsf274ae_slot0, + FIELD_ftsf275ae_slot0, + FIELD_ftsf276ae_slot0, + FIELD_ftsf277ae_slot0, + FIELD_ftsf278ae_slot0, + FIELD_ftsf279ae_slot0, + FIELD_ftsf281ae_slot0, + FIELD_ftsf282ae_slot0, + FIELD_ftsf283ae_slot0, + FIELD_ftsf284ae_slot0, + FIELD_ftsf286ae_slot0, + FIELD_ftsf288ae_slot0, + FIELD_ftsf290ae_slot0, + FIELD_ftsf292ae_slot0, + FIELD_ftsf293, + FIELD_ftsf294ae_slot0, + FIELD_ftsf295ae_slot0, + FIELD_ftsf296ae_slot0, + FIELD_ftsf297ae_slot0, + FIELD_ftsf298ae_slot0, + FIELD_ftsf299ae_slot0, + FIELD_ftsf300ae_slot0, + FIELD_ftsf301ae_slot0, + FIELD_ftsf302ae_slot0, + FIELD_ftsf303ae_slot0, + FIELD_ftsf304ae_slot0, + FIELD_ftsf306ae_slot0, + FIELD_ftsf308ae_slot0, + FIELD_ftsf309ae_slot0, + FIELD_ftsf310ae_slot0, + FIELD_ftsf311ae_slot0, + FIELD_ftsf312ae_slot0, + FIELD_ftsf313ae_slot0, + FIELD_ftsf314ae_slot0, + FIELD_ftsf315ae_slot0, + FIELD_ftsf316ae_slot0, + FIELD_ftsf317ae_slot0, + FIELD_ftsf318ae_slot0, + FIELD_ftsf319, + FIELD_ftsf320ae_slot0, + FIELD_ftsf321, + FIELD_ftsf322ae_slot0, + FIELD_ftsf323ae_slot0, + FIELD_ftsf324ae_slot0, + FIELD_ftsf325ae_slot0, + FIELD_ftsf326ae_slot0, + FIELD_ftsf328ae_slot0, + FIELD_ftsf329ae_slot0, + FIELD_ftsf352ae_slot0, + FIELD_ftsf353, + FIELD_ftsf354ae_slot0, + FIELD_ftsf356ae_slot0, + FIELD_ftsf357, + FIELD_ftsf358ae_slot0, + FIELD_ftsf359ae_slot0, + FIELD_ftsf360ae_slot0, + FIELD_ftsf361ae_slot0, + FIELD_ftsf362ae_slot0, + FIELD_ftsf364ae_slot0, + FIELD_ftsf365ae_slot0, + FIELD_ftsf366ae_slot0, + FIELD_ftsf368ae_slot0, + FIELD_ftsf369ae_slot0, + FIELD__ar0, + FIELD__ar4, + FIELD__ar8, + FIELD__ar12, + FIELD__bt16, + FIELD__bs16, + FIELD__br16, + FIELD__brall +}; + + +/* Functional units. */ + +static xtensa_funcUnit_internal funcUnits[] = { + { "ae_add32", 1 }, + { "ae_shift32x4", 1 }, + { "ae_shift32x5", 1 }, + { "ae_subshift", 1 } +}; + +enum xtensa_funcUnit_id { + FUNCUNIT_ae_add32, + FUNCUNIT_ae_shift32x4, + FUNCUNIT_ae_shift32x5, + FUNCUNIT_ae_subshift +}; + + +/* Register files. */ + +enum xtensa_regfile_id { + REGFILE_AR, + REGFILE_BR, + REGFILE_AE_PR, + REGFILE_AE_QR, + REGFILE_BR2, + REGFILE_BR4, + REGFILE_BR8, + REGFILE_BR16 +}; + +static xtensa_regfile_internal regfiles[] = { + { "AR", "a", REGFILE_AR, 32, 32 }, + { "BR", "b", REGFILE_BR, 1, 16 }, + { "AE_PR", "aep", REGFILE_AE_PR, 48, 8 }, + { "AE_QR", "aeq", REGFILE_AE_QR, 56, 4 }, + { "BR2", "b", REGFILE_BR, 2, 8 }, + { "BR4", "b", REGFILE_BR, 4, 4 }, + { "BR8", "b", REGFILE_BR, 8, 2 }, + { "BR16", "b", REGFILE_BR, 16, 1 } +}; + + +/* Interfaces. */ + +static xtensa_interface_internal interfaces[] = { + { "RMPINT_Out", 12, 0, 0, 'o' }, + { "RMPINT_In", 32, 0, 1, 'i' } +}; + +enum xtensa_interface_id { + INTERFACE_RMPINT_Out, + INTERFACE_RMPINT_In +}; + + +/* Constant tables. */ + +/* constant table ai4c */ +static const unsigned CONST_TBL_ai4c_0[] = { + 0xffffffff, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0x9, + 0xa, + 0xb, + 0xc, + 0xd, + 0xe, + 0xf, + 0 +}; + +/* constant table b4c */ +static const unsigned CONST_TBL_b4c_0[] = { + 0xffffffff, + 0x1, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0xa, + 0xc, + 0x10, + 0x20, + 0x40, + 0x80, + 0x100, + 0 +}; + +/* constant table b4cu */ +static const unsigned CONST_TBL_b4cu_0[] = { + 0x8000, + 0x10000, + 0x2, + 0x3, + 0x4, + 0x5, + 0x6, + 0x7, + 0x8, + 0xa, + 0xc, + 0x10, + 0x20, + 0x40, + 0x80, + 0x100, + 0 +}; + + +/* Instruction operands. */ + +static int +Operand_soffsetx4_decode (uint32 *valp) +{ + unsigned soffsetx4_0, offset_0; + offset_0 = *valp & 0x3ffff; + soffsetx4_0 = 0x4 + ((((int) offset_0 << 14) >> 14) << 2); + *valp = soffsetx4_0; + return 0; +} + +static int +Operand_soffsetx4_encode (uint32 *valp) +{ + unsigned offset_0, soffsetx4_0; + soffsetx4_0 = *valp; + offset_0 = ((soffsetx4_0 - 0x4) >> 2) & 0x3ffff; + *valp = offset_0; + return 0; +} + +static int +Operand_soffsetx4_ator (uint32 *valp, uint32 pc) +{ + *valp -= (pc & ~0x3); + return 0; +} + +static int +Operand_soffsetx4_rtoa (uint32 *valp, uint32 pc) +{ + *valp += (pc & ~0x3); + return 0; +} + +static int +Operand_uimm12x8_decode (uint32 *valp) +{ + unsigned uimm12x8_0, imm12_0; + imm12_0 = *valp & 0xfff; + uimm12x8_0 = imm12_0 << 3; + *valp = uimm12x8_0; + return 0; +} + +static int +Operand_uimm12x8_encode (uint32 *valp) +{ + unsigned imm12_0, uimm12x8_0; + uimm12x8_0 = *valp; + imm12_0 = ((uimm12x8_0 >> 3) & 0xfff); + *valp = imm12_0; + return 0; +} + +static int +Operand_simm4_decode (uint32 *valp) +{ + unsigned simm4_0, mn_0; + mn_0 = *valp & 0xf; + simm4_0 = ((int) mn_0 << 28) >> 28; + *valp = simm4_0; + return 0; +} + +static int +Operand_simm4_encode (uint32 *valp) +{ + unsigned mn_0, simm4_0; + simm4_0 = *valp; + mn_0 = (simm4_0 & 0xf); + *valp = mn_0; + return 0; +} + +static int +Operand_arr_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_arr_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0xf) != 0; + return error; +} + +static int +Operand_ars_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ars_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0xf) != 0; + return error; +} + +static int +Operand_art_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_art_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0xf) != 0; + return error; +} + +static int +Operand_ar0_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ar0_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x1f) != 0; + return error; +} + +static int +Operand_ar4_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ar4_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x1f) != 0; + return error; +} + +static int +Operand_ar8_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ar8_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x1f) != 0; + return error; +} + +static int +Operand_ar12_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ar12_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x1f) != 0; + return error; +} + +static int +Operand_ars_entry_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ars_entry_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x1f) != 0; + return error; +} + +static int +Operand_immrx4_decode (uint32 *valp) +{ + unsigned immrx4_0, r_0; + r_0 = *valp & 0xf; + immrx4_0 = (((0xfffffff) << 4) | r_0) << 2; + *valp = immrx4_0; + return 0; +} + +static int +Operand_immrx4_encode (uint32 *valp) +{ + unsigned r_0, immrx4_0; + immrx4_0 = *valp; + r_0 = ((immrx4_0 >> 2) & 0xf); + *valp = r_0; + return 0; +} + +static int +Operand_lsi4x4_decode (uint32 *valp) +{ + unsigned lsi4x4_0, r_0; + r_0 = *valp & 0xf; + lsi4x4_0 = r_0 << 2; + *valp = lsi4x4_0; + return 0; +} + +static int +Operand_lsi4x4_encode (uint32 *valp) +{ + unsigned r_0, lsi4x4_0; + lsi4x4_0 = *valp; + r_0 = ((lsi4x4_0 >> 2) & 0xf); + *valp = r_0; + return 0; +} + +static int +Operand_simm7_decode (uint32 *valp) +{ + unsigned simm7_0, imm7_0; + imm7_0 = *valp & 0x7f; + simm7_0 = ((((-((((imm7_0 >> 6) & 1)) & (((imm7_0 >> 5) & 1)))) & 0x1ffffff)) << 7) | imm7_0; + *valp = simm7_0; + return 0; +} + +static int +Operand_simm7_encode (uint32 *valp) +{ + unsigned imm7_0, simm7_0; + simm7_0 = *valp; + imm7_0 = (simm7_0 & 0x7f); + *valp = imm7_0; + return 0; +} + +static int +Operand_uimm6_decode (uint32 *valp) +{ + unsigned uimm6_0, imm6_0; + imm6_0 = *valp & 0x3f; + uimm6_0 = 0x4 + (((0) << 6) | imm6_0); + *valp = uimm6_0; + return 0; +} + +static int +Operand_uimm6_encode (uint32 *valp) +{ + unsigned imm6_0, uimm6_0; + uimm6_0 = *valp; + imm6_0 = (uimm6_0 - 0x4) & 0x3f; + *valp = imm6_0; + return 0; +} + +static int +Operand_uimm6_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_uimm6_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_ai4const_decode (uint32 *valp) +{ + unsigned ai4const_0, t_0; + t_0 = *valp & 0xf; + ai4const_0 = CONST_TBL_ai4c_0[t_0 & 0xf]; + *valp = ai4const_0; + return 0; +} + +static int +Operand_ai4const_encode (uint32 *valp) +{ + unsigned t_0, ai4const_0; + ai4const_0 = *valp; + switch (ai4const_0) + { + case 0xffffffff: t_0 = 0; break; + case 0x1: t_0 = 0x1; break; + case 0x2: t_0 = 0x2; break; + case 0x3: t_0 = 0x3; break; + case 0x4: t_0 = 0x4; break; + case 0x5: t_0 = 0x5; break; + case 0x6: t_0 = 0x6; break; + case 0x7: t_0 = 0x7; break; + case 0x8: t_0 = 0x8; break; + case 0x9: t_0 = 0x9; break; + case 0xa: t_0 = 0xa; break; + case 0xb: t_0 = 0xb; break; + case 0xc: t_0 = 0xc; break; + case 0xd: t_0 = 0xd; break; + case 0xe: t_0 = 0xe; break; + default: t_0 = 0xf; break; + } + *valp = t_0; + return 0; +} + +static int +Operand_b4const_decode (uint32 *valp) +{ + unsigned b4const_0, r_0; + r_0 = *valp & 0xf; + b4const_0 = CONST_TBL_b4c_0[r_0 & 0xf]; + *valp = b4const_0; + return 0; +} + +static int +Operand_b4const_encode (uint32 *valp) +{ + unsigned r_0, b4const_0; + b4const_0 = *valp; + switch (b4const_0) + { + case 0xffffffff: r_0 = 0; break; + case 0x1: r_0 = 0x1; break; + case 0x2: r_0 = 0x2; break; + case 0x3: r_0 = 0x3; break; + case 0x4: r_0 = 0x4; break; + case 0x5: r_0 = 0x5; break; + case 0x6: r_0 = 0x6; break; + case 0x7: r_0 = 0x7; break; + case 0x8: r_0 = 0x8; break; + case 0xa: r_0 = 0x9; break; + case 0xc: r_0 = 0xa; break; + case 0x10: r_0 = 0xb; break; + case 0x20: r_0 = 0xc; break; + case 0x40: r_0 = 0xd; break; + case 0x80: r_0 = 0xe; break; + default: r_0 = 0xf; break; + } + *valp = r_0; + return 0; +} + +static int +Operand_b4constu_decode (uint32 *valp) +{ + unsigned b4constu_0, r_0; + r_0 = *valp & 0xf; + b4constu_0 = CONST_TBL_b4cu_0[r_0 & 0xf]; + *valp = b4constu_0; + return 0; +} + +static int +Operand_b4constu_encode (uint32 *valp) +{ + unsigned r_0, b4constu_0; + b4constu_0 = *valp; + switch (b4constu_0) + { + case 0x8000: r_0 = 0; break; + case 0x10000: r_0 = 0x1; break; + case 0x2: r_0 = 0x2; break; + case 0x3: r_0 = 0x3; break; + case 0x4: r_0 = 0x4; break; + case 0x5: r_0 = 0x5; break; + case 0x6: r_0 = 0x6; break; + case 0x7: r_0 = 0x7; break; + case 0x8: r_0 = 0x8; break; + case 0xa: r_0 = 0x9; break; + case 0xc: r_0 = 0xa; break; + case 0x10: r_0 = 0xb; break; + case 0x20: r_0 = 0xc; break; + case 0x40: r_0 = 0xd; break; + case 0x80: r_0 = 0xe; break; + default: r_0 = 0xf; break; + } + *valp = r_0; + return 0; +} + +static int +Operand_uimm8_decode (uint32 *valp) +{ + unsigned uimm8_0, imm8_0; + imm8_0 = *valp & 0xff; + uimm8_0 = imm8_0; + *valp = uimm8_0; + return 0; +} + +static int +Operand_uimm8_encode (uint32 *valp) +{ + unsigned imm8_0, uimm8_0; + uimm8_0 = *valp; + imm8_0 = (uimm8_0 & 0xff); + *valp = imm8_0; + return 0; +} + +static int +Operand_uimm8x2_decode (uint32 *valp) +{ + unsigned uimm8x2_0, imm8_0; + imm8_0 = *valp & 0xff; + uimm8x2_0 = imm8_0 << 1; + *valp = uimm8x2_0; + return 0; +} + +static int +Operand_uimm8x2_encode (uint32 *valp) +{ + unsigned imm8_0, uimm8x2_0; + uimm8x2_0 = *valp; + imm8_0 = ((uimm8x2_0 >> 1) & 0xff); + *valp = imm8_0; + return 0; +} + +static int +Operand_uimm8x4_decode (uint32 *valp) +{ + unsigned uimm8x4_0, imm8_0; + imm8_0 = *valp & 0xff; + uimm8x4_0 = imm8_0 << 2; + *valp = uimm8x4_0; + return 0; +} + +static int +Operand_uimm8x4_encode (uint32 *valp) +{ + unsigned imm8_0, uimm8x4_0; + uimm8x4_0 = *valp; + imm8_0 = ((uimm8x4_0 >> 2) & 0xff); + *valp = imm8_0; + return 0; +} + +static int +Operand_uimm4x16_decode (uint32 *valp) +{ + unsigned uimm4x16_0, op2_0; + op2_0 = *valp & 0xf; + uimm4x16_0 = op2_0 << 4; + *valp = uimm4x16_0; + return 0; +} + +static int +Operand_uimm4x16_encode (uint32 *valp) +{ + unsigned op2_0, uimm4x16_0; + uimm4x16_0 = *valp; + op2_0 = ((uimm4x16_0 >> 4) & 0xf); + *valp = op2_0; + return 0; +} + +static int +Operand_simm8_decode (uint32 *valp) +{ + unsigned simm8_0, imm8_0; + imm8_0 = *valp & 0xff; + simm8_0 = ((int) imm8_0 << 24) >> 24; + *valp = simm8_0; + return 0; +} + +static int +Operand_simm8_encode (uint32 *valp) +{ + unsigned imm8_0, simm8_0; + simm8_0 = *valp; + imm8_0 = (simm8_0 & 0xff); + *valp = imm8_0; + return 0; +} + +static int +Operand_simm8x256_decode (uint32 *valp) +{ + unsigned simm8x256_0, imm8_0; + imm8_0 = *valp & 0xff; + simm8x256_0 = (((int) imm8_0 << 24) >> 24) << 8; + *valp = simm8x256_0; + return 0; +} + +static int +Operand_simm8x256_encode (uint32 *valp) +{ + unsigned imm8_0, simm8x256_0; + simm8x256_0 = *valp; + imm8_0 = ((simm8x256_0 >> 8) & 0xff); + *valp = imm8_0; + return 0; +} + +static int +Operand_simm12b_decode (uint32 *valp) +{ + unsigned simm12b_0, imm12b_0; + imm12b_0 = *valp & 0xfff; + simm12b_0 = ((int) imm12b_0 << 20) >> 20; + *valp = simm12b_0; + return 0; +} + +static int +Operand_simm12b_encode (uint32 *valp) +{ + unsigned imm12b_0, simm12b_0; + simm12b_0 = *valp; + imm12b_0 = (simm12b_0 & 0xfff); + *valp = imm12b_0; + return 0; +} + +static int +Operand_msalp32_decode (uint32 *valp) +{ + unsigned msalp32_0, sal_0; + sal_0 = *valp & 0x1f; + msalp32_0 = 0x20 - sal_0; + *valp = msalp32_0; + return 0; +} + +static int +Operand_msalp32_encode (uint32 *valp) +{ + unsigned sal_0, msalp32_0; + msalp32_0 = *valp; + sal_0 = (0x20 - msalp32_0) & 0x1f; + *valp = sal_0; + return 0; +} + +static int +Operand_op2p1_decode (uint32 *valp) +{ + unsigned op2p1_0, op2_0; + op2_0 = *valp & 0xf; + op2p1_0 = op2_0 + 0x1; + *valp = op2p1_0; + return 0; +} + +static int +Operand_op2p1_encode (uint32 *valp) +{ + unsigned op2_0, op2p1_0; + op2p1_0 = *valp; + op2_0 = (op2p1_0 - 0x1) & 0xf; + *valp = op2_0; + return 0; +} + +static int +Operand_label8_decode (uint32 *valp) +{ + unsigned label8_0, imm8_0; + imm8_0 = *valp & 0xff; + label8_0 = 0x4 + (((int) imm8_0 << 24) >> 24); + *valp = label8_0; + return 0; +} + +static int +Operand_label8_encode (uint32 *valp) +{ + unsigned imm8_0, label8_0; + label8_0 = *valp; + imm8_0 = (label8_0 - 0x4) & 0xff; + *valp = imm8_0; + return 0; +} + +static int +Operand_label8_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_label8_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_ulabel8_decode (uint32 *valp) +{ + unsigned ulabel8_0, imm8_0; + imm8_0 = *valp & 0xff; + ulabel8_0 = 0x4 + (((0) << 8) | imm8_0); + *valp = ulabel8_0; + return 0; +} + +static int +Operand_ulabel8_encode (uint32 *valp) +{ + unsigned imm8_0, ulabel8_0; + ulabel8_0 = *valp; + imm8_0 = (ulabel8_0 - 0x4) & 0xff; + *valp = imm8_0; + return 0; +} + +static int +Operand_ulabel8_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_ulabel8_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_label12_decode (uint32 *valp) +{ + unsigned label12_0, imm12_0; + imm12_0 = *valp & 0xfff; + label12_0 = 0x4 + (((int) imm12_0 << 20) >> 20); + *valp = label12_0; + return 0; +} + +static int +Operand_label12_encode (uint32 *valp) +{ + unsigned imm12_0, label12_0; + label12_0 = *valp; + imm12_0 = (label12_0 - 0x4) & 0xfff; + *valp = imm12_0; + return 0; +} + +static int +Operand_label12_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_label12_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_soffset_decode (uint32 *valp) +{ + unsigned soffset_0, offset_0; + offset_0 = *valp & 0x3ffff; + soffset_0 = 0x4 + (((int) offset_0 << 14) >> 14); + *valp = soffset_0; + return 0; +} + +static int +Operand_soffset_encode (uint32 *valp) +{ + unsigned offset_0, soffset_0; + soffset_0 = *valp; + offset_0 = (soffset_0 - 0x4) & 0x3ffff; + *valp = offset_0; + return 0; +} + +static int +Operand_soffset_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_soffset_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_uimm16x4_decode (uint32 *valp) +{ + unsigned uimm16x4_0, imm16_0; + imm16_0 = *valp & 0xffff; + uimm16x4_0 = (((0xffff) << 16) | imm16_0) << 2; + *valp = uimm16x4_0; + return 0; +} + +static int +Operand_uimm16x4_encode (uint32 *valp) +{ + unsigned imm16_0, uimm16x4_0; + uimm16x4_0 = *valp; + imm16_0 = (uimm16x4_0 >> 2) & 0xffff; + *valp = imm16_0; + return 0; +} + +static int +Operand_uimm16x4_ator (uint32 *valp, uint32 pc) +{ + *valp -= ((pc + 3) & ~0x3); + return 0; +} + +static int +Operand_uimm16x4_rtoa (uint32 *valp, uint32 pc) +{ + *valp += ((pc + 3) & ~0x3); + return 0; +} + +static int +Operand_immt_decode (uint32 *valp) +{ + unsigned immt_0, t_0; + t_0 = *valp & 0xf; + immt_0 = t_0; + *valp = immt_0; + return 0; +} + +static int +Operand_immt_encode (uint32 *valp) +{ + unsigned t_0, immt_0; + immt_0 = *valp; + t_0 = immt_0 & 0xf; + *valp = t_0; + return 0; +} + +static int +Operand_imms_decode (uint32 *valp) +{ + unsigned imms_0, s_0; + s_0 = *valp & 0xf; + imms_0 = s_0; + *valp = imms_0; + return 0; +} + +static int +Operand_imms_encode (uint32 *valp) +{ + unsigned s_0, imms_0; + imms_0 = *valp; + s_0 = imms_0 & 0xf; + *valp = s_0; + return 0; +} + +static int +Operand_bt_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_bt_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0xf) != 0; + return error; +} + +static int +Operand_bs_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_bs_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0xf) != 0; + return error; +} + +static int +Operand_br_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_br_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0xf) != 0; + return error; +} + +static int +Operand_bt2_decode (uint32 *valp) +{ + *valp = *valp << 1; + return 0; +} + +static int +Operand_bt2_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x7 << 1)) != 0; + *valp = *valp >> 1; + return error; +} + +static int +Operand_bs2_decode (uint32 *valp) +{ + *valp = *valp << 1; + return 0; +} + +static int +Operand_bs2_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x7 << 1)) != 0; + *valp = *valp >> 1; + return error; +} + +static int +Operand_br2_decode (uint32 *valp) +{ + *valp = *valp << 1; + return 0; +} + +static int +Operand_br2_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x7 << 1)) != 0; + *valp = *valp >> 1; + return error; +} + +static int +Operand_bt4_decode (uint32 *valp) +{ + *valp = *valp << 2; + return 0; +} + +static int +Operand_bt4_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x3 << 2)) != 0; + *valp = *valp >> 2; + return error; +} + +static int +Operand_bs4_decode (uint32 *valp) +{ + *valp = *valp << 2; + return 0; +} + +static int +Operand_bs4_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x3 << 2)) != 0; + *valp = *valp >> 2; + return error; +} + +static int +Operand_br4_decode (uint32 *valp) +{ + *valp = *valp << 2; + return 0; +} + +static int +Operand_br4_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x3 << 2)) != 0; + *valp = *valp >> 2; + return error; +} + +static int +Operand_bt8_decode (uint32 *valp) +{ + *valp = *valp << 3; + return 0; +} + +static int +Operand_bt8_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x1 << 3)) != 0; + *valp = *valp >> 3; + return error; +} + +static int +Operand_bs8_decode (uint32 *valp) +{ + *valp = *valp << 3; + return 0; +} + +static int +Operand_bs8_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x1 << 3)) != 0; + *valp = *valp >> 3; + return error; +} + +static int +Operand_br8_decode (uint32 *valp) +{ + *valp = *valp << 3; + return 0; +} + +static int +Operand_br8_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0x1 << 3)) != 0; + *valp = *valp >> 3; + return error; +} + +static int +Operand_bt16_decode (uint32 *valp) +{ + *valp = *valp << 4; + return 0; +} + +static int +Operand_bt16_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0 << 4)) != 0; + *valp = *valp >> 4; + return error; +} + +static int +Operand_bs16_decode (uint32 *valp) +{ + *valp = *valp << 4; + return 0; +} + +static int +Operand_bs16_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0 << 4)) != 0; + *valp = *valp >> 4; + return error; +} + +static int +Operand_br16_decode (uint32 *valp) +{ + *valp = *valp << 4; + return 0; +} + +static int +Operand_br16_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0 << 4)) != 0; + *valp = *valp >> 4; + return error; +} + +static int +Operand_brall_decode (uint32 *valp) +{ + *valp = *valp << 4; + return 0; +} + +static int +Operand_brall_encode (uint32 *valp) +{ + int error; + error = (*valp & ~(0 << 4)) != 0; + *valp = *valp >> 4; + return error; +} + +static int +Operand_tp7_decode (uint32 *valp) +{ + unsigned tp7_0, t_0; + t_0 = *valp & 0xf; + tp7_0 = t_0 + 0x7; + *valp = tp7_0; + return 0; +} + +static int +Operand_tp7_encode (uint32 *valp) +{ + unsigned t_0, tp7_0; + tp7_0 = *valp; + t_0 = (tp7_0 - 0x7) & 0xf; + *valp = t_0; + return 0; +} + +static int +Operand_xt_wbr15_label_decode (uint32 *valp) +{ + unsigned xt_wbr15_label_0, xt_wbr15_imm_0; + xt_wbr15_imm_0 = *valp & 0x7fff; + xt_wbr15_label_0 = 0x4 + (((int) xt_wbr15_imm_0 << 17) >> 17); + *valp = xt_wbr15_label_0; + return 0; +} + +static int +Operand_xt_wbr15_label_encode (uint32 *valp) +{ + unsigned xt_wbr15_imm_0, xt_wbr15_label_0; + xt_wbr15_label_0 = *valp; + xt_wbr15_imm_0 = (xt_wbr15_label_0 - 0x4) & 0x7fff; + *valp = xt_wbr15_imm_0; + return 0; +} + +static int +Operand_xt_wbr15_label_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_xt_wbr15_label_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_xt_wbr18_label_decode (uint32 *valp) +{ + unsigned xt_wbr18_label_0, xt_wbr18_imm_0; + xt_wbr18_imm_0 = *valp & 0x3ffff; + xt_wbr18_label_0 = 0x4 + (((int) xt_wbr18_imm_0 << 14) >> 14); + *valp = xt_wbr18_label_0; + return 0; +} + +static int +Operand_xt_wbr18_label_encode (uint32 *valp) +{ + unsigned xt_wbr18_imm_0, xt_wbr18_label_0; + xt_wbr18_label_0 = *valp; + xt_wbr18_imm_0 = (xt_wbr18_label_0 - 0x4) & 0x3ffff; + *valp = xt_wbr18_imm_0; + return 0; +} + +static int +Operand_xt_wbr18_label_ator (uint32 *valp, uint32 pc) +{ + *valp -= pc; + return 0; +} + +static int +Operand_xt_wbr18_label_rtoa (uint32 *valp, uint32 pc) +{ + *valp += pc; + return 0; +} + +static int +Operand_ae_samt32_decode (uint32 *valp) +{ + unsigned ae_samt32_0, ftsf14_0; + ftsf14_0 = *valp & 0x1f; + ae_samt32_0 = (0 << 5) | ftsf14_0; + *valp = ae_samt32_0; + return 0; +} + +static int +Operand_ae_samt32_encode (uint32 *valp) +{ + unsigned ftsf14_0, ae_samt32_0; + ae_samt32_0 = *valp; + ftsf14_0 = (ae_samt32_0 & 0x1f); + *valp = ftsf14_0; + return 0; +} + +static int +Operand_pr0_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_pr0_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x7) != 0; + return error; +} + +static int +Operand_qr0_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_qr0_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x3) != 0; + return error; +} + +static int +Operand_ae_lsimm16_decode (uint32 *valp) +{ + unsigned ae_lsimm16_0, t_0; + t_0 = *valp & 0xf; + ae_lsimm16_0 = (((int) t_0 << 28) >> 28) << 1; + *valp = ae_lsimm16_0; + return 0; +} + +static int +Operand_ae_lsimm16_encode (uint32 *valp) +{ + unsigned t_0, ae_lsimm16_0; + ae_lsimm16_0 = *valp; + t_0 = ((ae_lsimm16_0 >> 1) & 0xf); + *valp = t_0; + return 0; +} + +static int +Operand_ae_lsimm32_decode (uint32 *valp) +{ + unsigned ae_lsimm32_0, t_0; + t_0 = *valp & 0xf; + ae_lsimm32_0 = (((int) t_0 << 28) >> 28) << 2; + *valp = ae_lsimm32_0; + return 0; +} + +static int +Operand_ae_lsimm32_encode (uint32 *valp) +{ + unsigned t_0, ae_lsimm32_0; + ae_lsimm32_0 = *valp; + t_0 = ((ae_lsimm32_0 >> 2) & 0xf); + *valp = t_0; + return 0; +} + +static int +Operand_ae_lsimm64_decode (uint32 *valp) +{ + unsigned ae_lsimm64_0, t_0; + t_0 = *valp & 0xf; + ae_lsimm64_0 = (((int) t_0 << 28) >> 28) << 3; + *valp = ae_lsimm64_0; + return 0; +} + +static int +Operand_ae_lsimm64_encode (uint32 *valp) +{ + unsigned t_0, ae_lsimm64_0; + ae_lsimm64_0 = *valp; + t_0 = ((ae_lsimm64_0 >> 3) & 0xf); + *valp = t_0; + return 0; +} + +static int +Operand_ae_samt64_decode (uint32 *valp) +{ + unsigned ae_samt64_0, ae_samt_s_t_0; + ae_samt_s_t_0 = *valp & 0x3f; + ae_samt64_0 = (0 << 6) | ae_samt_s_t_0; + *valp = ae_samt64_0; + return 0; +} + +static int +Operand_ae_samt64_encode (uint32 *valp) +{ + unsigned ae_samt_s_t_0, ae_samt64_0; + ae_samt64_0 = *valp; + ae_samt_s_t_0 = (ae_samt64_0 & 0x3f); + *valp = ae_samt_s_t_0; + return 0; +} + +static int +Operand_ae_ohba_decode (uint32 *valp) +{ + unsigned ae_ohba_0, op1_0; + op1_0 = *valp & 0xf; + ae_ohba_0 = (0 << 5) | (((((op1_0 & 0xf))) == 0) << 4) | ((op1_0 & 0xf)); + *valp = ae_ohba_0; + return 0; +} + +static int +Operand_ae_ohba_encode (uint32 *valp) +{ + unsigned op1_0, ae_ohba_0; + ae_ohba_0 = *valp; + op1_0 = (ae_ohba_0 & 0xf); + *valp = op1_0; + return 0; +} + +static int +Operand_pr_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_pr_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x7) != 0; + return error; +} + +static int +Operand_qr0_rw_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_qr0_rw_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x3) != 0; + return error; +} + +static int +Operand_qr1_w_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_qr1_w_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x3) != 0; + return error; +} + +static int +Operand_ps_decode (uint32 *valp ATTRIBUTE_UNUSED) +{ + return 0; +} + +static int +Operand_ps_encode (uint32 *valp) +{ + int error; + error = (*valp & ~0x7) != 0; + return error; +} + +static xtensa_operand_internal operands[] = { + { "soffsetx4", FIELD_offset, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_soffsetx4_encode, Operand_soffsetx4_decode, + Operand_soffsetx4_ator, Operand_soffsetx4_rtoa }, + { "uimm12x8", FIELD_imm12, -1, 0, + 0, + Operand_uimm12x8_encode, Operand_uimm12x8_decode, + 0, 0 }, + { "simm4", FIELD_mn, -1, 0, + 0, + Operand_simm4_encode, Operand_simm4_decode, + 0, 0 }, + { "arr", FIELD_r, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_arr_encode, Operand_arr_decode, + 0, 0 }, + { "ars", FIELD_s, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_ars_encode, Operand_ars_decode, + 0, 0 }, + { "*ars_invisible", FIELD_s, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + Operand_ars_encode, Operand_ars_decode, + 0, 0 }, + { "art", FIELD_t, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_art_encode, Operand_art_decode, + 0, 0 }, + { "ar0", FIELD__ar0, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + Operand_ar0_encode, Operand_ar0_decode, + 0, 0 }, + { "ar4", FIELD__ar4, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + Operand_ar4_encode, Operand_ar4_decode, + 0, 0 }, + { "ar8", FIELD__ar8, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + Operand_ar8_encode, Operand_ar8_decode, + 0, 0 }, + { "ar12", FIELD__ar12, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + Operand_ar12_encode, Operand_ar12_decode, + 0, 0 }, + { "ars_entry", FIELD_s, REGFILE_AR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_ars_entry_encode, Operand_ars_entry_decode, + 0, 0 }, + { "immrx4", FIELD_r, -1, 0, + 0, + Operand_immrx4_encode, Operand_immrx4_decode, + 0, 0 }, + { "lsi4x4", FIELD_r, -1, 0, + 0, + Operand_lsi4x4_encode, Operand_lsi4x4_decode, + 0, 0 }, + { "simm7", FIELD_imm7, -1, 0, + 0, + Operand_simm7_encode, Operand_simm7_decode, + 0, 0 }, + { "uimm6", FIELD_imm6, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_uimm6_encode, Operand_uimm6_decode, + Operand_uimm6_ator, Operand_uimm6_rtoa }, + { "ai4const", FIELD_t, -1, 0, + 0, + Operand_ai4const_encode, Operand_ai4const_decode, + 0, 0 }, + { "b4const", FIELD_r, -1, 0, + 0, + Operand_b4const_encode, Operand_b4const_decode, + 0, 0 }, + { "b4constu", FIELD_r, -1, 0, + 0, + Operand_b4constu_encode, Operand_b4constu_decode, + 0, 0 }, + { "uimm8", FIELD_imm8, -1, 0, + 0, + Operand_uimm8_encode, Operand_uimm8_decode, + 0, 0 }, + { "uimm8x2", FIELD_imm8, -1, 0, + 0, + Operand_uimm8x2_encode, Operand_uimm8x2_decode, + 0, 0 }, + { "uimm8x4", FIELD_imm8, -1, 0, + 0, + Operand_uimm8x4_encode, Operand_uimm8x4_decode, + 0, 0 }, + { "uimm4x16", FIELD_op2, -1, 0, + 0, + Operand_uimm4x16_encode, Operand_uimm4x16_decode, + 0, 0 }, + { "simm8", FIELD_imm8, -1, 0, + 0, + Operand_simm8_encode, Operand_simm8_decode, + 0, 0 }, + { "simm8x256", FIELD_imm8, -1, 0, + 0, + Operand_simm8x256_encode, Operand_simm8x256_decode, + 0, 0 }, + { "simm12b", FIELD_imm12b, -1, 0, + 0, + Operand_simm12b_encode, Operand_simm12b_decode, + 0, 0 }, + { "msalp32", FIELD_sal, -1, 0, + 0, + Operand_msalp32_encode, Operand_msalp32_decode, + 0, 0 }, + { "op2p1", FIELD_op2, -1, 0, + 0, + Operand_op2p1_encode, Operand_op2p1_decode, + 0, 0 }, + { "label8", FIELD_imm8, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_label8_encode, Operand_label8_decode, + Operand_label8_ator, Operand_label8_rtoa }, + { "ulabel8", FIELD_imm8, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_ulabel8_encode, Operand_ulabel8_decode, + Operand_ulabel8_ator, Operand_ulabel8_rtoa }, + { "label12", FIELD_imm12, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_label12_encode, Operand_label12_decode, + Operand_label12_ator, Operand_label12_rtoa }, + { "soffset", FIELD_offset, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_soffset_encode, Operand_soffset_decode, + Operand_soffset_ator, Operand_soffset_rtoa }, + { "uimm16x4", FIELD_imm16, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_uimm16x4_encode, Operand_uimm16x4_decode, + Operand_uimm16x4_ator, Operand_uimm16x4_rtoa }, + { "immt", FIELD_t, -1, 0, + 0, + Operand_immt_encode, Operand_immt_decode, + 0, 0 }, + { "imms", FIELD_s, -1, 0, + 0, + Operand_imms_encode, Operand_imms_decode, + 0, 0 }, + { "bt", FIELD_t, REGFILE_BR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_bt_encode, Operand_bt_decode, + 0, 0 }, + { "bs", FIELD_s, REGFILE_BR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_bs_encode, Operand_bs_decode, + 0, 0 }, + { "br", FIELD_r, REGFILE_BR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_br_encode, Operand_br_decode, + 0, 0 }, + { "bt2", FIELD_t2, REGFILE_BR, 2, + XTENSA_OPERAND_IS_REGISTER, + Operand_bt2_encode, Operand_bt2_decode, + 0, 0 }, + { "bs2", FIELD_s2, REGFILE_BR, 2, + XTENSA_OPERAND_IS_REGISTER, + Operand_bs2_encode, Operand_bs2_decode, + 0, 0 }, + { "br2", FIELD_r2, REGFILE_BR, 2, + XTENSA_OPERAND_IS_REGISTER, + Operand_br2_encode, Operand_br2_decode, + 0, 0 }, + { "bt4", FIELD_t4, REGFILE_BR, 4, + XTENSA_OPERAND_IS_REGISTER, + Operand_bt4_encode, Operand_bt4_decode, + 0, 0 }, + { "bs4", FIELD_s4, REGFILE_BR, 4, + XTENSA_OPERAND_IS_REGISTER, + Operand_bs4_encode, Operand_bs4_decode, + 0, 0 }, + { "br4", FIELD_r4, REGFILE_BR, 4, + XTENSA_OPERAND_IS_REGISTER, + Operand_br4_encode, Operand_br4_decode, + 0, 0 }, + { "bt8", FIELD_t8, REGFILE_BR, 8, + XTENSA_OPERAND_IS_REGISTER, + Operand_bt8_encode, Operand_bt8_decode, + 0, 0 }, + { "bs8", FIELD_s8, REGFILE_BR, 8, + XTENSA_OPERAND_IS_REGISTER, + Operand_bs8_encode, Operand_bs8_decode, + 0, 0 }, + { "br8", FIELD_r8, REGFILE_BR, 8, + XTENSA_OPERAND_IS_REGISTER, + Operand_br8_encode, Operand_br8_decode, + 0, 0 }, + { "bt16", FIELD__bt16, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER, + Operand_bt16_encode, Operand_bt16_decode, + 0, 0 }, + { "bs16", FIELD__bs16, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER, + Operand_bs16_encode, Operand_bs16_decode, + 0, 0 }, + { "br16", FIELD__br16, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER, + Operand_br16_encode, Operand_br16_decode, + 0, 0 }, + { "brall", FIELD__brall, REGFILE_BR, 16, + XTENSA_OPERAND_IS_REGISTER | XTENSA_OPERAND_IS_INVISIBLE, + Operand_brall_encode, Operand_brall_decode, + 0, 0 }, + { "tp7", FIELD_t, -1, 0, + 0, + Operand_tp7_encode, Operand_tp7_decode, + 0, 0 }, + { "xt_wbr15_label", FIELD_xt_wbr15_imm, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_xt_wbr15_label_encode, Operand_xt_wbr15_label_decode, + Operand_xt_wbr15_label_ator, Operand_xt_wbr15_label_rtoa }, + { "xt_wbr18_label", FIELD_xt_wbr18_imm, -1, 0, + XTENSA_OPERAND_IS_PCRELATIVE, + Operand_xt_wbr18_label_encode, Operand_xt_wbr18_label_decode, + Operand_xt_wbr18_label_ator, Operand_xt_wbr18_label_rtoa }, + { "ae_samt32", FIELD_ftsf14, -1, 0, + 0, + Operand_ae_samt32_encode, Operand_ae_samt32_decode, + 0, 0 }, + { "pr0", FIELD_ftsf12, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_pr0_encode, Operand_pr0_decode, + 0, 0 }, + { "qr0", FIELD_ftsf13, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_qr0_encode, Operand_qr0_decode, + 0, 0 }, + { "ae_lsimm16", FIELD_t, -1, 0, + 0, + Operand_ae_lsimm16_encode, Operand_ae_lsimm16_decode, + 0, 0 }, + { "ae_lsimm32", FIELD_t, -1, 0, + 0, + Operand_ae_lsimm32_encode, Operand_ae_lsimm32_decode, + 0, 0 }, + { "ae_lsimm64", FIELD_t, -1, 0, + 0, + Operand_ae_lsimm64_encode, Operand_ae_lsimm64_decode, + 0, 0 }, + { "ae_samt64", FIELD_ae_samt_s_t, -1, 0, + 0, + Operand_ae_samt64_encode, Operand_ae_samt64_decode, + 0, 0 }, + { "ae_ohba", FIELD_op1, -1, 0, + 0, + Operand_ae_ohba_encode, Operand_ae_ohba_decode, + 0, 0 }, + { "pr", FIELD_ae_r20, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_pr_encode, Operand_pr_decode, + 0, 0 }, + { "qr0_rw", FIELD_ae_r10, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_qr0_rw_encode, Operand_qr0_rw_decode, + 0, 0 }, + { "qr1_w", FIELD_ae_r32, REGFILE_AE_QR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_qr1_w_encode, Operand_qr1_w_decode, + 0, 0 }, + { "ps", FIELD_ae_s20, REGFILE_AE_PR, 1, + XTENSA_OPERAND_IS_REGISTER, + Operand_ps_encode, Operand_ps_decode, + 0, 0 }, + { "t", FIELD_t, -1, 0, 0, 0, 0, 0, 0 }, + { "bbi4", FIELD_bbi4, -1, 0, 0, 0, 0, 0, 0 }, + { "bbi", FIELD_bbi, -1, 0, 0, 0, 0, 0, 0 }, + { "imm12", FIELD_imm12, -1, 0, 0, 0, 0, 0, 0 }, + { "imm8", FIELD_imm8, -1, 0, 0, 0, 0, 0, 0 }, + { "s", FIELD_s, -1, 0, 0, 0, 0, 0, 0 }, + { "imm12b", FIELD_imm12b, -1, 0, 0, 0, 0, 0, 0 }, + { "imm16", FIELD_imm16, -1, 0, 0, 0, 0, 0, 0 }, + { "m", FIELD_m, -1, 0, 0, 0, 0, 0, 0 }, + { "n", FIELD_n, -1, 0, 0, 0, 0, 0, 0 }, + { "offset", FIELD_offset, -1, 0, 0, 0, 0, 0, 0 }, + { "op0", FIELD_op0, -1, 0, 0, 0, 0, 0, 0 }, + { "op1", FIELD_op1, -1, 0, 0, 0, 0, 0, 0 }, + { "op2", FIELD_op2, -1, 0, 0, 0, 0, 0, 0 }, + { "r", FIELD_r, -1, 0, 0, 0, 0, 0, 0 }, + { "sa4", FIELD_sa4, -1, 0, 0, 0, 0, 0, 0 }, + { "sae4", FIELD_sae4, -1, 0, 0, 0, 0, 0, 0 }, + { "sae", FIELD_sae, -1, 0, 0, 0, 0, 0, 0 }, + { "sal", FIELD_sal, -1, 0, 0, 0, 0, 0, 0 }, + { "sargt", FIELD_sargt, -1, 0, 0, 0, 0, 0, 0 }, + { "sas4", FIELD_sas4, -1, 0, 0, 0, 0, 0, 0 }, + { "sas", FIELD_sas, -1, 0, 0, 0, 0, 0, 0 }, + { "sr", FIELD_sr, -1, 0, 0, 0, 0, 0, 0 }, + { "st", FIELD_st, -1, 0, 0, 0, 0, 0, 0 }, + { "thi3", FIELD_thi3, -1, 0, 0, 0, 0, 0, 0 }, + { "imm4", FIELD_imm4, -1, 0, 0, 0, 0, 0, 0 }, + { "mn", FIELD_mn, -1, 0, 0, 0, 0, 0, 0 }, + { "i", FIELD_i, -1, 0, 0, 0, 0, 0, 0 }, + { "imm6lo", FIELD_imm6lo, -1, 0, 0, 0, 0, 0, 0 }, + { "imm6hi", FIELD_imm6hi, -1, 0, 0, 0, 0, 0, 0 }, + { "imm7lo", FIELD_imm7lo, -1, 0, 0, 0, 0, 0, 0 }, + { "imm7hi", FIELD_imm7hi, -1, 0, 0, 0, 0, 0, 0 }, + { "z", FIELD_z, -1, 0, 0, 0, 0, 0, 0 }, + { "imm6", FIELD_imm6, -1, 0, 0, 0, 0, 0, 0 }, + { "imm7", FIELD_imm7, -1, 0, 0, 0, 0, 0, 0 }, + { "t2", FIELD_t2, -1, 0, 0, 0, 0, 0, 0 }, + { "s2", FIELD_s2, -1, 0, 0, 0, 0, 0, 0 }, + { "r2", FIELD_r2, -1, 0, 0, 0, 0, 0, 0 }, + { "t4", FIELD_t4, -1, 0, 0, 0, 0, 0, 0 }, + { "s4", FIELD_s4, -1, 0, 0, 0, 0, 0, 0 }, + { "r4", FIELD_r4, -1, 0, 0, 0, 0, 0, 0 }, + { "t8", FIELD_t8, -1, 0, 0, 0, 0, 0, 0 }, + { "s8", FIELD_s8, -1, 0, 0, 0, 0, 0, 0 }, + { "r8", FIELD_r8, -1, 0, 0, 0, 0, 0, 0 }, + { "xt_wbr15_imm", FIELD_xt_wbr15_imm, -1, 0, 0, 0, 0, 0, 0 }, + { "xt_wbr18_imm", FIELD_xt_wbr18_imm, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r3", FIELD_ae_r3, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_s_non_samt", FIELD_ae_s_non_samt, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_s3", FIELD_ae_s3, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r32", FIELD_ae_r32, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_samt_s_t", FIELD_ae_samt_s_t, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r20", FIELD_ae_r20, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_r10", FIELD_ae_r10, -1, 0, 0, 0, 0, 0, 0 }, + { "ae_s20", FIELD_ae_s20, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s3", FIELD_op0_s3, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf12", FIELD_ftsf12, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf13", FIELD_ftsf13, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf14", FIELD_ftsf14, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf21ae_slot1", FIELD_ftsf21ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf22ae_slot1", FIELD_ftsf22ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf23ae_slot1", FIELD_ftsf23ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf24ae_slot1", FIELD_ftsf24ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf25ae_slot1", FIELD_ftsf25ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf26ae_slot1", FIELD_ftsf26ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf27ae_slot1", FIELD_ftsf27ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf28ae_slot1", FIELD_ftsf28ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf29ae_slot1", FIELD_ftsf29ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf30ae_slot1", FIELD_ftsf30ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf31ae_slot1", FIELD_ftsf31ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf32ae_slot1", FIELD_ftsf32ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf33ae_slot1", FIELD_ftsf33ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf34ae_slot1", FIELD_ftsf34ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf35ae_slot1", FIELD_ftsf35ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf36ae_slot1", FIELD_ftsf36ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf37ae_slot1", FIELD_ftsf37ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf38ae_slot1", FIELD_ftsf38ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf39ae_slot1", FIELD_ftsf39ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf40ae_slot1", FIELD_ftsf40ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf41ae_slot1", FIELD_ftsf41ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf42ae_slot1", FIELD_ftsf42ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf43ae_slot1", FIELD_ftsf43ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf44ae_slot1", FIELD_ftsf44ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf45ae_slot1", FIELD_ftsf45ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf46ae_slot1", FIELD_ftsf46ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf47ae_slot1", FIELD_ftsf47ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf48ae_slot1", FIELD_ftsf48ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf49ae_slot1", FIELD_ftsf49ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf50ae_slot1", FIELD_ftsf50ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf51ae_slot1", FIELD_ftsf51ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf52ae_slot1", FIELD_ftsf52ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf53ae_slot1", FIELD_ftsf53ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf54ae_slot1", FIELD_ftsf54ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf55ae_slot1", FIELD_ftsf55ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf56ae_slot1", FIELD_ftsf56ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf57ae_slot1", FIELD_ftsf57ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf58ae_slot1", FIELD_ftsf58ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf59ae_slot1", FIELD_ftsf59ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf60ae_slot1", FIELD_ftsf60ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf61ae_slot1", FIELD_ftsf61ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf63ae_slot1", FIELD_ftsf63ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf64ae_slot1", FIELD_ftsf64ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf66ae_slot1", FIELD_ftsf66ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf67ae_slot1", FIELD_ftsf67ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf69ae_slot1", FIELD_ftsf69ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf71ae_slot1", FIELD_ftsf71ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf72ae_slot1", FIELD_ftsf72ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf73ae_slot1", FIELD_ftsf73ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf75ae_slot1", FIELD_ftsf75ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf76ae_slot1", FIELD_ftsf76ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf77ae_slot1", FIELD_ftsf77ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf78ae_slot1", FIELD_ftsf78ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf79ae_slot1", FIELD_ftsf79ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf80ae_slot1", FIELD_ftsf80ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf81ae_slot1", FIELD_ftsf81ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf82ae_slot1", FIELD_ftsf82ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf84ae_slot1", FIELD_ftsf84ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf86ae_slot1", FIELD_ftsf86ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf87ae_slot1", FIELD_ftsf87ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf88ae_slot1", FIELD_ftsf88ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf89ae_slot1", FIELD_ftsf89ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf90ae_slot1", FIELD_ftsf90ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf91ae_slot1", FIELD_ftsf91ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf92ae_slot1", FIELD_ftsf92ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf94ae_slot1", FIELD_ftsf94ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf96ae_slot1", FIELD_ftsf96ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf97ae_slot1", FIELD_ftsf97ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf98ae_slot1", FIELD_ftsf98ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf99ae_slot1", FIELD_ftsf99ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf100ae_slot1", FIELD_ftsf100ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf101ae_slot1", FIELD_ftsf101ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf103ae_slot1", FIELD_ftsf103ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf104ae_slot1", FIELD_ftsf104ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf105ae_slot1", FIELD_ftsf105ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf106ae_slot1", FIELD_ftsf106ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf107ae_slot1", FIELD_ftsf107ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf108ae_slot1", FIELD_ftsf108ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf109ae_slot1", FIELD_ftsf109ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf110ae_slot1", FIELD_ftsf110ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf111ae_slot1", FIELD_ftsf111ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf112ae_slot1", FIELD_ftsf112ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf113ae_slot1", FIELD_ftsf113ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf114ae_slot1", FIELD_ftsf114ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf115ae_slot1", FIELD_ftsf115ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf116ae_slot1", FIELD_ftsf116ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf118ae_slot1", FIELD_ftsf118ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf119ae_slot1", FIELD_ftsf119ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf120ae_slot1", FIELD_ftsf120ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf122ae_slot1", FIELD_ftsf122ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf124ae_slot1", FIELD_ftsf124ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf125ae_slot1", FIELD_ftsf125ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf126ae_slot1", FIELD_ftsf126ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf127ae_slot1", FIELD_ftsf127ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf128ae_slot1", FIELD_ftsf128ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf129ae_slot1", FIELD_ftsf129ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf130ae_slot1", FIELD_ftsf130ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf131ae_slot1", FIELD_ftsf131ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf132ae_slot1", FIELD_ftsf132ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf133ae_slot1", FIELD_ftsf133ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf134ae_slot1", FIELD_ftsf134ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf135ae_slot1", FIELD_ftsf135ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf136ae_slot1", FIELD_ftsf136ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf137ae_slot1", FIELD_ftsf137ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf138ae_slot1", FIELD_ftsf138ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf139ae_slot1", FIELD_ftsf139ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf140ae_slot1", FIELD_ftsf140ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf141ae_slot1", FIELD_ftsf141ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf142ae_slot1", FIELD_ftsf142ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf143ae_slot1", FIELD_ftsf143ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf144ae_slot1", FIELD_ftsf144ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf145ae_slot1", FIELD_ftsf145ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf146ae_slot1", FIELD_ftsf146ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf147ae_slot1", FIELD_ftsf147ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf148ae_slot1", FIELD_ftsf148ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf149ae_slot1", FIELD_ftsf149ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf150ae_slot1", FIELD_ftsf150ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf151ae_slot1", FIELD_ftsf151ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf152ae_slot1", FIELD_ftsf152ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf153ae_slot1", FIELD_ftsf153ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf154ae_slot1", FIELD_ftsf154ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf155ae_slot1", FIELD_ftsf155ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf156ae_slot1", FIELD_ftsf156ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf157ae_slot1", FIELD_ftsf157ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf158ae_slot1", FIELD_ftsf158ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf159ae_slot1", FIELD_ftsf159ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf160ae_slot1", FIELD_ftsf160ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf161ae_slot1", FIELD_ftsf161ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf162ae_slot1", FIELD_ftsf162ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf163ae_slot1", FIELD_ftsf163ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf164ae_slot1", FIELD_ftsf164ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf165ae_slot1", FIELD_ftsf165ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf166ae_slot1", FIELD_ftsf166ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf167ae_slot1", FIELD_ftsf167ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf168ae_slot1", FIELD_ftsf168ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf169ae_slot1", FIELD_ftsf169ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf170ae_slot1", FIELD_ftsf170ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf171ae_slot1", FIELD_ftsf171ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf172ae_slot1", FIELD_ftsf172ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf173ae_slot1", FIELD_ftsf173ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf174ae_slot1", FIELD_ftsf174ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf175ae_slot1", FIELD_ftsf175ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf176ae_slot1", FIELD_ftsf176ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf177ae_slot1", FIELD_ftsf177ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf178ae_slot1", FIELD_ftsf178ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf179ae_slot1", FIELD_ftsf179ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf180ae_slot1", FIELD_ftsf180ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf181ae_slot1", FIELD_ftsf181ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf182ae_slot1", FIELD_ftsf182ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf183ae_slot1", FIELD_ftsf183ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf184ae_slot1", FIELD_ftsf184ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf185ae_slot1", FIELD_ftsf185ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf186ae_slot1", FIELD_ftsf186ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf187ae_slot1", FIELD_ftsf187ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf188ae_slot1", FIELD_ftsf188ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf189ae_slot1", FIELD_ftsf189ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf190ae_slot1", FIELD_ftsf190ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf191ae_slot1", FIELD_ftsf191ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf192ae_slot1", FIELD_ftsf192ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf193ae_slot1", FIELD_ftsf193ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf194ae_slot1", FIELD_ftsf194ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf195ae_slot1", FIELD_ftsf195ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf196ae_slot1", FIELD_ftsf196ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf197ae_slot1", FIELD_ftsf197ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf198ae_slot1", FIELD_ftsf198ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf199ae_slot1", FIELD_ftsf199ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf200ae_slot1", FIELD_ftsf200ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf201ae_slot1", FIELD_ftsf201ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf202ae_slot1", FIELD_ftsf202ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf203ae_slot1", FIELD_ftsf203ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf204ae_slot1", FIELD_ftsf204ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf205ae_slot1", FIELD_ftsf205ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf206ae_slot1", FIELD_ftsf206ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf207ae_slot1", FIELD_ftsf207ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf208", FIELD_ftsf208, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf209ae_slot1", FIELD_ftsf209ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf210ae_slot1", FIELD_ftsf210ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf211ae_slot1", FIELD_ftsf211ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf330ae_slot1", FIELD_ftsf330ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf332ae_slot1", FIELD_ftsf332ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf334ae_slot1", FIELD_ftsf334ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf336ae_slot1", FIELD_ftsf336ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf337ae_slot1", FIELD_ftsf337ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf338", FIELD_ftsf338, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf339ae_slot1", FIELD_ftsf339ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf340", FIELD_ftsf340, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf341ae_slot1", FIELD_ftsf341ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf342ae_slot1", FIELD_ftsf342ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf343ae_slot1", FIELD_ftsf343ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf344ae_slot1", FIELD_ftsf344ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf346ae_slot1", FIELD_ftsf346ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf347", FIELD_ftsf347, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf348ae_slot1", FIELD_ftsf348ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf349ae_slot1", FIELD_ftsf349ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf350ae_slot1", FIELD_ftsf350ae_slot1, -1, 0, 0, 0, 0, 0, 0 }, + { "op0_s4", FIELD_op0_s4, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf212ae_slot0", FIELD_ftsf212ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf213ae_slot0", FIELD_ftsf213ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf214ae_slot0", FIELD_ftsf214ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf215ae_slot0", FIELD_ftsf215ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf216ae_slot0", FIELD_ftsf216ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf217", FIELD_ftsf217, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf218ae_slot0", FIELD_ftsf218ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf219ae_slot0", FIELD_ftsf219ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf220ae_slot0", FIELD_ftsf220ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf221ae_slot0", FIELD_ftsf221ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf222ae_slot0", FIELD_ftsf222ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf223ae_slot0", FIELD_ftsf223ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf224ae_slot0", FIELD_ftsf224ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf225ae_slot0", FIELD_ftsf225ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf226ae_slot0", FIELD_ftsf226ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf227ae_slot0", FIELD_ftsf227ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf228ae_slot0", FIELD_ftsf228ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf229ae_slot0", FIELD_ftsf229ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf230ae_slot0", FIELD_ftsf230ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf231ae_slot0", FIELD_ftsf231ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf232ae_slot0", FIELD_ftsf232ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf233ae_slot0", FIELD_ftsf233ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf234ae_slot0", FIELD_ftsf234ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf235ae_slot0", FIELD_ftsf235ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf236ae_slot0", FIELD_ftsf236ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf237ae_slot0", FIELD_ftsf237ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf238ae_slot0", FIELD_ftsf238ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf239ae_slot0", FIELD_ftsf239ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf240ae_slot0", FIELD_ftsf240ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf241ae_slot0", FIELD_ftsf241ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf242ae_slot0", FIELD_ftsf242ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf243ae_slot0", FIELD_ftsf243ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf244ae_slot0", FIELD_ftsf244ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf245ae_slot0", FIELD_ftsf245ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf246ae_slot0", FIELD_ftsf246ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf247ae_slot0", FIELD_ftsf247ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf248ae_slot0", FIELD_ftsf248ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf249ae_slot0", FIELD_ftsf249ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf250ae_slot0", FIELD_ftsf250ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf251ae_slot0", FIELD_ftsf251ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf252ae_slot0", FIELD_ftsf252ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf253ae_slot0", FIELD_ftsf253ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf254ae_slot0", FIELD_ftsf254ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf255ae_slot0", FIELD_ftsf255ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf256ae_slot0", FIELD_ftsf256ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf257ae_slot0", FIELD_ftsf257ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf258ae_slot0", FIELD_ftsf258ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf259ae_slot0", FIELD_ftsf259ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf260ae_slot0", FIELD_ftsf260ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf261ae_slot0", FIELD_ftsf261ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf262ae_slot0", FIELD_ftsf262ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf263ae_slot0", FIELD_ftsf263ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf264ae_slot0", FIELD_ftsf264ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf265ae_slot0", FIELD_ftsf265ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf266ae_slot0", FIELD_ftsf266ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf267ae_slot0", FIELD_ftsf267ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf268ae_slot0", FIELD_ftsf268ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf269ae_slot0", FIELD_ftsf269ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf270ae_slot0", FIELD_ftsf270ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf271ae_slot0", FIELD_ftsf271ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf272ae_slot0", FIELD_ftsf272ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf273ae_slot0", FIELD_ftsf273ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf274ae_slot0", FIELD_ftsf274ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf275ae_slot0", FIELD_ftsf275ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf276ae_slot0", FIELD_ftsf276ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf277ae_slot0", FIELD_ftsf277ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf278ae_slot0", FIELD_ftsf278ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf279ae_slot0", FIELD_ftsf279ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf281ae_slot0", FIELD_ftsf281ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf282ae_slot0", FIELD_ftsf282ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf283ae_slot0", FIELD_ftsf283ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf284ae_slot0", FIELD_ftsf284ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf286ae_slot0", FIELD_ftsf286ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf288ae_slot0", FIELD_ftsf288ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf290ae_slot0", FIELD_ftsf290ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf292ae_slot0", FIELD_ftsf292ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf293", FIELD_ftsf293, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf294ae_slot0", FIELD_ftsf294ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf295ae_slot0", FIELD_ftsf295ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf296ae_slot0", FIELD_ftsf296ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf297ae_slot0", FIELD_ftsf297ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf298ae_slot0", FIELD_ftsf298ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf299ae_slot0", FIELD_ftsf299ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf300ae_slot0", FIELD_ftsf300ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf301ae_slot0", FIELD_ftsf301ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf302ae_slot0", FIELD_ftsf302ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf303ae_slot0", FIELD_ftsf303ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf304ae_slot0", FIELD_ftsf304ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf306ae_slot0", FIELD_ftsf306ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf308ae_slot0", FIELD_ftsf308ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf309ae_slot0", FIELD_ftsf309ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf310ae_slot0", FIELD_ftsf310ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf311ae_slot0", FIELD_ftsf311ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf312ae_slot0", FIELD_ftsf312ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf313ae_slot0", FIELD_ftsf313ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf314ae_slot0", FIELD_ftsf314ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf315ae_slot0", FIELD_ftsf315ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf316ae_slot0", FIELD_ftsf316ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf317ae_slot0", FIELD_ftsf317ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf318ae_slot0", FIELD_ftsf318ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf319", FIELD_ftsf319, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf320ae_slot0", FIELD_ftsf320ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf321", FIELD_ftsf321, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf322ae_slot0", FIELD_ftsf322ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf323ae_slot0", FIELD_ftsf323ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf324ae_slot0", FIELD_ftsf324ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf325ae_slot0", FIELD_ftsf325ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf326ae_slot0", FIELD_ftsf326ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf328ae_slot0", FIELD_ftsf328ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf329ae_slot0", FIELD_ftsf329ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf352ae_slot0", FIELD_ftsf352ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf353", FIELD_ftsf353, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf354ae_slot0", FIELD_ftsf354ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf356ae_slot0", FIELD_ftsf356ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf357", FIELD_ftsf357, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf358ae_slot0", FIELD_ftsf358ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf359ae_slot0", FIELD_ftsf359ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf360ae_slot0", FIELD_ftsf360ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf361ae_slot0", FIELD_ftsf361ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf362ae_slot0", FIELD_ftsf362ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf364ae_slot0", FIELD_ftsf364ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf365ae_slot0", FIELD_ftsf365ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf366ae_slot0", FIELD_ftsf366ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf368ae_slot0", FIELD_ftsf368ae_slot0, -1, 0, 0, 0, 0, 0, 0 }, + { "ftsf369ae_slot0", FIELD_ftsf369ae_slot0, -1, 0, 0, 0, 0, 0, 0 } +}; + +enum xtensa_operand_id { + OPERAND_soffsetx4, + OPERAND_uimm12x8, + OPERAND_simm4, + OPERAND_arr, + OPERAND_ars, + OPERAND__ars_invisible, + OPERAND_art, + OPERAND_ar0, + OPERAND_ar4, + OPERAND_ar8, + OPERAND_ar12, + OPERAND_ars_entry, + OPERAND_immrx4, + OPERAND_lsi4x4, + OPERAND_simm7, + OPERAND_uimm6, + OPERAND_ai4const, + OPERAND_b4const, + OPERAND_b4constu, + OPERAND_uimm8, + OPERAND_uimm8x2, + OPERAND_uimm8x4, + OPERAND_uimm4x16, + OPERAND_simm8, + OPERAND_simm8x256, + OPERAND_simm12b, + OPERAND_msalp32, + OPERAND_op2p1, + OPERAND_label8, + OPERAND_ulabel8, + OPERAND_label12, + OPERAND_soffset, + OPERAND_uimm16x4, + OPERAND_immt, + OPERAND_imms, + OPERAND_bt, + OPERAND_bs, + OPERAND_br, + OPERAND_bt2, + OPERAND_bs2, + OPERAND_br2, + OPERAND_bt4, + OPERAND_bs4, + OPERAND_br4, + OPERAND_bt8, + OPERAND_bs8, + OPERAND_br8, + OPERAND_bt16, + OPERAND_bs16, + OPERAND_br16, + OPERAND_brall, + OPERAND_tp7, + OPERAND_xt_wbr15_label, + OPERAND_xt_wbr18_label, + OPERAND_ae_samt32, + OPERAND_pr0, + OPERAND_qr0, + OPERAND_ae_lsimm16, + OPERAND_ae_lsimm32, + OPERAND_ae_lsimm64, + OPERAND_ae_samt64, + OPERAND_ae_ohba, + OPERAND_pr, + OPERAND_qr0_rw, + OPERAND_qr1_w, + OPERAND_ps, + OPERAND_t, + OPERAND_bbi4, + OPERAND_bbi, + OPERAND_imm12, + OPERAND_imm8, + OPERAND_s, + OPERAND_imm12b, + OPERAND_imm16, + OPERAND_m, + OPERAND_n, + OPERAND_offset, + OPERAND_op0, + OPERAND_op1, + OPERAND_op2, + OPERAND_r, + OPERAND_sa4, + OPERAND_sae4, + OPERAND_sae, + OPERAND_sal, + OPERAND_sargt, + OPERAND_sas4, + OPERAND_sas, + OPERAND_sr, + OPERAND_st, + OPERAND_thi3, + OPERAND_imm4, + OPERAND_mn, + OPERAND_i, + OPERAND_imm6lo, + OPERAND_imm6hi, + OPERAND_imm7lo, + OPERAND_imm7hi, + OPERAND_z, + OPERAND_imm6, + OPERAND_imm7, + OPERAND_t2, + OPERAND_s2, + OPERAND_r2, + OPERAND_t4, + OPERAND_s4, + OPERAND_r4, + OPERAND_t8, + OPERAND_s8, + OPERAND_r8, + OPERAND_xt_wbr15_imm, + OPERAND_xt_wbr18_imm, + OPERAND_ae_r3, + OPERAND_ae_s_non_samt, + OPERAND_ae_s3, + OPERAND_ae_r32, + OPERAND_ae_samt_s_t, + OPERAND_ae_r20, + OPERAND_ae_r10, + OPERAND_ae_s20, + OPERAND_op0_s3, + OPERAND_ftsf12, + OPERAND_ftsf13, + OPERAND_ftsf14, + OPERAND_ftsf21ae_slot1, + OPERAND_ftsf22ae_slot1, + OPERAND_ftsf23ae_slot1, + OPERAND_ftsf24ae_slot1, + OPERAND_ftsf25ae_slot1, + OPERAND_ftsf26ae_slot1, + OPERAND_ftsf27ae_slot1, + OPERAND_ftsf28ae_slot1, + OPERAND_ftsf29ae_slot1, + OPERAND_ftsf30ae_slot1, + OPERAND_ftsf31ae_slot1, + OPERAND_ftsf32ae_slot1, + OPERAND_ftsf33ae_slot1, + OPERAND_ftsf34ae_slot1, + OPERAND_ftsf35ae_slot1, + OPERAND_ftsf36ae_slot1, + OPERAND_ftsf37ae_slot1, + OPERAND_ftsf38ae_slot1, + OPERAND_ftsf39ae_slot1, + OPERAND_ftsf40ae_slot1, + OPERAND_ftsf41ae_slot1, + OPERAND_ftsf42ae_slot1, + OPERAND_ftsf43ae_slot1, + OPERAND_ftsf44ae_slot1, + OPERAND_ftsf45ae_slot1, + OPERAND_ftsf46ae_slot1, + OPERAND_ftsf47ae_slot1, + OPERAND_ftsf48ae_slot1, + OPERAND_ftsf49ae_slot1, + OPERAND_ftsf50ae_slot1, + OPERAND_ftsf51ae_slot1, + OPERAND_ftsf52ae_slot1, + OPERAND_ftsf53ae_slot1, + OPERAND_ftsf54ae_slot1, + OPERAND_ftsf55ae_slot1, + OPERAND_ftsf56ae_slot1, + OPERAND_ftsf57ae_slot1, + OPERAND_ftsf58ae_slot1, + OPERAND_ftsf59ae_slot1, + OPERAND_ftsf60ae_slot1, + OPERAND_ftsf61ae_slot1, + OPERAND_ftsf63ae_slot1, + OPERAND_ftsf64ae_slot1, + OPERAND_ftsf66ae_slot1, + OPERAND_ftsf67ae_slot1, + OPERAND_ftsf69ae_slot1, + OPERAND_ftsf71ae_slot1, + OPERAND_ftsf72ae_slot1, + OPERAND_ftsf73ae_slot1, + OPERAND_ftsf75ae_slot1, + OPERAND_ftsf76ae_slot1, + OPERAND_ftsf77ae_slot1, + OPERAND_ftsf78ae_slot1, + OPERAND_ftsf79ae_slot1, + OPERAND_ftsf80ae_slot1, + OPERAND_ftsf81ae_slot1, + OPERAND_ftsf82ae_slot1, + OPERAND_ftsf84ae_slot1, + OPERAND_ftsf86ae_slot1, + OPERAND_ftsf87ae_slot1, + OPERAND_ftsf88ae_slot1, + OPERAND_ftsf89ae_slot1, + OPERAND_ftsf90ae_slot1, + OPERAND_ftsf91ae_slot1, + OPERAND_ftsf92ae_slot1, + OPERAND_ftsf94ae_slot1, + OPERAND_ftsf96ae_slot1, + OPERAND_ftsf97ae_slot1, + OPERAND_ftsf98ae_slot1, + OPERAND_ftsf99ae_slot1, + OPERAND_ftsf100ae_slot1, + OPERAND_ftsf101ae_slot1, + OPERAND_ftsf103ae_slot1, + OPERAND_ftsf104ae_slot1, + OPERAND_ftsf105ae_slot1, + OPERAND_ftsf106ae_slot1, + OPERAND_ftsf107ae_slot1, + OPERAND_ftsf108ae_slot1, + OPERAND_ftsf109ae_slot1, + OPERAND_ftsf110ae_slot1, + OPERAND_ftsf111ae_slot1, + OPERAND_ftsf112ae_slot1, + OPERAND_ftsf113ae_slot1, + OPERAND_ftsf114ae_slot1, + OPERAND_ftsf115ae_slot1, + OPERAND_ftsf116ae_slot1, + OPERAND_ftsf118ae_slot1, + OPERAND_ftsf119ae_slot1, + OPERAND_ftsf120ae_slot1, + OPERAND_ftsf122ae_slot1, + OPERAND_ftsf124ae_slot1, + OPERAND_ftsf125ae_slot1, + OPERAND_ftsf126ae_slot1, + OPERAND_ftsf127ae_slot1, + OPERAND_ftsf128ae_slot1, + OPERAND_ftsf129ae_slot1, + OPERAND_ftsf130ae_slot1, + OPERAND_ftsf131ae_slot1, + OPERAND_ftsf132ae_slot1, + OPERAND_ftsf133ae_slot1, + OPERAND_ftsf134ae_slot1, + OPERAND_ftsf135ae_slot1, + OPERAND_ftsf136ae_slot1, + OPERAND_ftsf137ae_slot1, + OPERAND_ftsf138ae_slot1, + OPERAND_ftsf139ae_slot1, + OPERAND_ftsf140ae_slot1, + OPERAND_ftsf141ae_slot1, + OPERAND_ftsf142ae_slot1, + OPERAND_ftsf143ae_slot1, + OPERAND_ftsf144ae_slot1, + OPERAND_ftsf145ae_slot1, + OPERAND_ftsf146ae_slot1, + OPERAND_ftsf147ae_slot1, + OPERAND_ftsf148ae_slot1, + OPERAND_ftsf149ae_slot1, + OPERAND_ftsf150ae_slot1, + OPERAND_ftsf151ae_slot1, + OPERAND_ftsf152ae_slot1, + OPERAND_ftsf153ae_slot1, + OPERAND_ftsf154ae_slot1, + OPERAND_ftsf155ae_slot1, + OPERAND_ftsf156ae_slot1, + OPERAND_ftsf157ae_slot1, + OPERAND_ftsf158ae_slot1, + OPERAND_ftsf159ae_slot1, + OPERAND_ftsf160ae_slot1, + OPERAND_ftsf161ae_slot1, + OPERAND_ftsf162ae_slot1, + OPERAND_ftsf163ae_slot1, + OPERAND_ftsf164ae_slot1, + OPERAND_ftsf165ae_slot1, + OPERAND_ftsf166ae_slot1, + OPERAND_ftsf167ae_slot1, + OPERAND_ftsf168ae_slot1, + OPERAND_ftsf169ae_slot1, + OPERAND_ftsf170ae_slot1, + OPERAND_ftsf171ae_slot1, + OPERAND_ftsf172ae_slot1, + OPERAND_ftsf173ae_slot1, + OPERAND_ftsf174ae_slot1, + OPERAND_ftsf175ae_slot1, + OPERAND_ftsf176ae_slot1, + OPERAND_ftsf177ae_slot1, + OPERAND_ftsf178ae_slot1, + OPERAND_ftsf179ae_slot1, + OPERAND_ftsf180ae_slot1, + OPERAND_ftsf181ae_slot1, + OPERAND_ftsf182ae_slot1, + OPERAND_ftsf183ae_slot1, + OPERAND_ftsf184ae_slot1, + OPERAND_ftsf185ae_slot1, + OPERAND_ftsf186ae_slot1, + OPERAND_ftsf187ae_slot1, + OPERAND_ftsf188ae_slot1, + OPERAND_ftsf189ae_slot1, + OPERAND_ftsf190ae_slot1, + OPERAND_ftsf191ae_slot1, + OPERAND_ftsf192ae_slot1, + OPERAND_ftsf193ae_slot1, + OPERAND_ftsf194ae_slot1, + OPERAND_ftsf195ae_slot1, + OPERAND_ftsf196ae_slot1, + OPERAND_ftsf197ae_slot1, + OPERAND_ftsf198ae_slot1, + OPERAND_ftsf199ae_slot1, + OPERAND_ftsf200ae_slot1, + OPERAND_ftsf201ae_slot1, + OPERAND_ftsf202ae_slot1, + OPERAND_ftsf203ae_slot1, + OPERAND_ftsf204ae_slot1, + OPERAND_ftsf205ae_slot1, + OPERAND_ftsf206ae_slot1, + OPERAND_ftsf207ae_slot1, + OPERAND_ftsf208, + OPERAND_ftsf209ae_slot1, + OPERAND_ftsf210ae_slot1, + OPERAND_ftsf211ae_slot1, + OPERAND_ftsf330ae_slot1, + OPERAND_ftsf332ae_slot1, + OPERAND_ftsf334ae_slot1, + OPERAND_ftsf336ae_slot1, + OPERAND_ftsf337ae_slot1, + OPERAND_ftsf338, + OPERAND_ftsf339ae_slot1, + OPERAND_ftsf340, + OPERAND_ftsf341ae_slot1, + OPERAND_ftsf342ae_slot1, + OPERAND_ftsf343ae_slot1, + OPERAND_ftsf344ae_slot1, + OPERAND_ftsf346ae_slot1, + OPERAND_ftsf347, + OPERAND_ftsf348ae_slot1, + OPERAND_ftsf349ae_slot1, + OPERAND_ftsf350ae_slot1, + OPERAND_op0_s4, + OPERAND_ftsf212ae_slot0, + OPERAND_ftsf213ae_slot0, + OPERAND_ftsf214ae_slot0, + OPERAND_ftsf215ae_slot0, + OPERAND_ftsf216ae_slot0, + OPERAND_ftsf217, + OPERAND_ftsf218ae_slot0, + OPERAND_ftsf219ae_slot0, + OPERAND_ftsf220ae_slot0, + OPERAND_ftsf221ae_slot0, + OPERAND_ftsf222ae_slot0, + OPERAND_ftsf223ae_slot0, + OPERAND_ftsf224ae_slot0, + OPERAND_ftsf225ae_slot0, + OPERAND_ftsf226ae_slot0, + OPERAND_ftsf227ae_slot0, + OPERAND_ftsf228ae_slot0, + OPERAND_ftsf229ae_slot0, + OPERAND_ftsf230ae_slot0, + OPERAND_ftsf231ae_slot0, + OPERAND_ftsf232ae_slot0, + OPERAND_ftsf233ae_slot0, + OPERAND_ftsf234ae_slot0, + OPERAND_ftsf235ae_slot0, + OPERAND_ftsf236ae_slot0, + OPERAND_ftsf237ae_slot0, + OPERAND_ftsf238ae_slot0, + OPERAND_ftsf239ae_slot0, + OPERAND_ftsf240ae_slot0, + OPERAND_ftsf241ae_slot0, + OPERAND_ftsf242ae_slot0, + OPERAND_ftsf243ae_slot0, + OPERAND_ftsf244ae_slot0, + OPERAND_ftsf245ae_slot0, + OPERAND_ftsf246ae_slot0, + OPERAND_ftsf247ae_slot0, + OPERAND_ftsf248ae_slot0, + OPERAND_ftsf249ae_slot0, + OPERAND_ftsf250ae_slot0, + OPERAND_ftsf251ae_slot0, + OPERAND_ftsf252ae_slot0, + OPERAND_ftsf253ae_slot0, + OPERAND_ftsf254ae_slot0, + OPERAND_ftsf255ae_slot0, + OPERAND_ftsf256ae_slot0, + OPERAND_ftsf257ae_slot0, + OPERAND_ftsf258ae_slot0, + OPERAND_ftsf259ae_slot0, + OPERAND_ftsf260ae_slot0, + OPERAND_ftsf261ae_slot0, + OPERAND_ftsf262ae_slot0, + OPERAND_ftsf263ae_slot0, + OPERAND_ftsf264ae_slot0, + OPERAND_ftsf265ae_slot0, + OPERAND_ftsf266ae_slot0, + OPERAND_ftsf267ae_slot0, + OPERAND_ftsf268ae_slot0, + OPERAND_ftsf269ae_slot0, + OPERAND_ftsf270ae_slot0, + OPERAND_ftsf271ae_slot0, + OPERAND_ftsf272ae_slot0, + OPERAND_ftsf273ae_slot0, + OPERAND_ftsf274ae_slot0, + OPERAND_ftsf275ae_slot0, + OPERAND_ftsf276ae_slot0, + OPERAND_ftsf277ae_slot0, + OPERAND_ftsf278ae_slot0, + OPERAND_ftsf279ae_slot0, + OPERAND_ftsf281ae_slot0, + OPERAND_ftsf282ae_slot0, + OPERAND_ftsf283ae_slot0, + OPERAND_ftsf284ae_slot0, + OPERAND_ftsf286ae_slot0, + OPERAND_ftsf288ae_slot0, + OPERAND_ftsf290ae_slot0, + OPERAND_ftsf292ae_slot0, + OPERAND_ftsf293, + OPERAND_ftsf294ae_slot0, + OPERAND_ftsf295ae_slot0, + OPERAND_ftsf296ae_slot0, + OPERAND_ftsf297ae_slot0, + OPERAND_ftsf298ae_slot0, + OPERAND_ftsf299ae_slot0, + OPERAND_ftsf300ae_slot0, + OPERAND_ftsf301ae_slot0, + OPERAND_ftsf302ae_slot0, + OPERAND_ftsf303ae_slot0, + OPERAND_ftsf304ae_slot0, + OPERAND_ftsf306ae_slot0, + OPERAND_ftsf308ae_slot0, + OPERAND_ftsf309ae_slot0, + OPERAND_ftsf310ae_slot0, + OPERAND_ftsf311ae_slot0, + OPERAND_ftsf312ae_slot0, + OPERAND_ftsf313ae_slot0, + OPERAND_ftsf314ae_slot0, + OPERAND_ftsf315ae_slot0, + OPERAND_ftsf316ae_slot0, + OPERAND_ftsf317ae_slot0, + OPERAND_ftsf318ae_slot0, + OPERAND_ftsf319, + OPERAND_ftsf320ae_slot0, + OPERAND_ftsf321, + OPERAND_ftsf322ae_slot0, + OPERAND_ftsf323ae_slot0, + OPERAND_ftsf324ae_slot0, + OPERAND_ftsf325ae_slot0, + OPERAND_ftsf326ae_slot0, + OPERAND_ftsf328ae_slot0, + OPERAND_ftsf329ae_slot0, + OPERAND_ftsf352ae_slot0, + OPERAND_ftsf353, + OPERAND_ftsf354ae_slot0, + OPERAND_ftsf356ae_slot0, + OPERAND_ftsf357, + OPERAND_ftsf358ae_slot0, + OPERAND_ftsf359ae_slot0, + OPERAND_ftsf360ae_slot0, + OPERAND_ftsf361ae_slot0, + OPERAND_ftsf362ae_slot0, + OPERAND_ftsf364ae_slot0, + OPERAND_ftsf365ae_slot0, + OPERAND_ftsf366ae_slot0, + OPERAND_ftsf368ae_slot0, + OPERAND_ftsf369ae_slot0 +}; + + +/* Iclass table. */ + +static xtensa_arg_internal Iclass_xt_iclass_rfe_stateArgs[] = { + { { STATE_PSRING }, 'i' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_EPC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfde_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call12_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar12 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call12_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call8_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar8 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call8_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call4_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call4_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx12_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar12 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx12_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx8_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar8 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx8_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx4_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx4_stateArgs[] = { + { { STATE_PSCALLINC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_entry_args[] = { + { { OPERAND_ars_entry }, 's' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm12x8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_entry_stateArgs[] = { + { { STATE_PSCALLINC }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSWOE }, 'i' }, + { { STATE_WindowBase }, 'm' }, + { { STATE_WindowStart }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movsp_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movsp_stateArgs[] = { + { { STATE_WindowBase }, 'i' }, + { { STATE_WindowStart }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rotw_args[] = { + { { OPERAND_simm4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rotw_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_retw_args[] = { + { { OPERAND__ars_invisible }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_retw_stateArgs[] = { + { { STATE_WindowBase }, 'm' }, + { { STATE_WindowStart }, 'm' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSWOE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfwou_stateArgs[] = { + { { STATE_EPC1 }, 'i' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'm' }, + { { STATE_WindowStart }, 'm' }, + { { STATE_PSOWB }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32e_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_immrx4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32e_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32e_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_immrx4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32e_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowbase_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowbase_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowbase_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowBase }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowstart_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_windowstart_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowStart }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowstart_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_windowstart_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowStart }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowstart_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_windowstart_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WindowStart }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_add_n_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addi_n_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ai4const }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bz6_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm6 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loadi4_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_lsi4x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_mov_n_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movi_n_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_simm7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_retn_args[] = { + { { OPERAND__ars_invisible }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_storei4_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_lsi4x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_threadptr_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_threadptr_stateArgs[] = { + { { STATE_THREADPTR }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_threadptr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_threadptr_stateArgs[] = { + { { STATE_THREADPTR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addi_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_simm8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addmi_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_simm8x256 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_addsub_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bit_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsi8_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_b4const }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsi8b_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_bbi }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsi8u_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_b4constu }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bst8_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bsz12_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_label12 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_call0_args[] = { + { { OPERAND_soffsetx4 }, 'i' }, + { { OPERAND_ar0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_callx0_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ar0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_exti_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_sae }, 'i' }, + { { OPERAND_op2p1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_jump_args[] = { + { { OPERAND_soffset }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_jumpx_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l16ui_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l16si_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32i_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32r_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_uimm16x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32r_stateArgs[] = { + { { STATE_LITBADDR }, 'i' }, + { { STATE_LITBEN }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l8i_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loop_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ulabel8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loop_stateArgs[] = { + { { STATE_LBEG }, 'o' }, + { { STATE_LEND }, 'o' }, + { { STATE_LCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loopz_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_ulabel8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_loopz_stateArgs[] = { + { { STATE_LBEG }, 'o' }, + { { STATE_LEND }, 'o' }, + { { STATE_LCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movi_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_simm12b }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_movz_args[] = { + { { OPERAND_arr }, 'm' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_neg_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_return_args[] = { + { { OPERAND__ars_invisible }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s16i_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32i_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s8i_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sar_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sar_stateArgs[] = { + { { STATE_SAR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sari_args[] = { + { { OPERAND_sas }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sari_stateArgs[] = { + { { STATE_SAR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shifts_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shifts_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftst_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftst_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftt_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_shiftt_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_slli_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_msalp32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_srai_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_sargt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_srli_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sync_stateArgs[] = { + { { STATE_XTSYNC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsil_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsil_stateArgs[] = { + { { STATE_PSWOE }, 'i' }, + { { STATE_PSCALLINC }, 'i' }, + { { STATE_PSOWB }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PSUM }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lend_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lend_stateArgs[] = { + { { STATE_LEND }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lend_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lend_stateArgs[] = { + { { STATE_LEND }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lend_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lend_stateArgs[] = { + { { STATE_LEND }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lcount_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lcount_stateArgs[] = { + { { STATE_LCOUNT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lcount_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lcount_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_LCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lcount_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lcount_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_LCOUNT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lbeg_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_lbeg_stateArgs[] = { + { { STATE_LBEG }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lbeg_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_lbeg_stateArgs[] = { + { { STATE_LBEG }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lbeg_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_lbeg_stateArgs[] = { + { { STATE_LBEG }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_sar_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_sar_stateArgs[] = { + { { STATE_SAR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_sar_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_sar_stateArgs[] = { + { { STATE_SAR }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_sar_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_sar_stateArgs[] = { + { { STATE_SAR }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_litbase_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_litbase_stateArgs[] = { + { { STATE_LITBADDR }, 'i' }, + { { STATE_LITBEN }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_litbase_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_litbase_stateArgs[] = { + { { STATE_LITBADDR }, 'o' }, + { { STATE_LITBEN }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_litbase_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_litbase_stateArgs[] = { + { { STATE_LITBADDR }, 'm' }, + { { STATE_LITBEN }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_176_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_176_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_176_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_176_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_208_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_208_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ps_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ps_stateArgs[] = { + { { STATE_PSWOE }, 'i' }, + { { STATE_PSCALLINC }, 'i' }, + { { STATE_PSOWB }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PSUM }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ps_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ps_stateArgs[] = { + { { STATE_PSWOE }, 'o' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSOWB }, 'o' }, + { { STATE_PSRING }, 'm' }, + { { STATE_PSUM }, 'o' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSINTLEVEL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ps_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ps_stateArgs[] = { + { { STATE_PSWOE }, 'm' }, + { { STATE_PSCALLINC }, 'm' }, + { { STATE_PSOWB }, 'm' }, + { { STATE_PSRING }, 'm' }, + { { STATE_PSUM }, 'm' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSINTLEVEL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_epc2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_epc2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_epc2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPC2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excsave2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excsave2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excsave2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCSAVE2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps2_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_eps2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps2_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_eps2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps2_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_eps2_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EPS2 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excvaddr_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_excvaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excvaddr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_excvaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCVADDR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excvaddr_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_excvaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCVADDR }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_depc_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_depc_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_depc_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_depc_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_depc_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_depc_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEPC }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_exccause_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_exccause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCCAUSE }, 'i' }, + { { STATE_XTSYNC }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_exccause_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_exccause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCCAUSE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_exccause_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_exccause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_EXCCAUSE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC0 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC0 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_misc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_misc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_misc1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MISC1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_prid_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_prid_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_vecbase_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_vecbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_VECBASE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_vecbase_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_vecbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_VECBASE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_vecbase_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_vecbase_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_VECBASE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_mul16_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_mul32_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfi_args[] = { + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfi_stateArgs[] = { + { { STATE_PSWOE }, 'o' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSOWB }, 'o' }, + { { STATE_PSRING }, 'm' }, + { { STATE_PSUM }, 'o' }, + { { STATE_PSEXCM }, 'm' }, + { { STATE_PSINTLEVEL }, 'o' }, + { { STATE_EPC1 }, 'i' }, + { { STATE_EPC2 }, 'i' }, + { { STATE_EPS2 }, 'i' }, + { { STATE_InOCDMode }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wait_args[] = { + { { OPERAND_s }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wait_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PSINTLEVEL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_interrupt_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_interrupt_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTERRUPT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intset_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intset_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intclear_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intclear_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_intenable_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_intenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intenable_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_intenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTENABLE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_intenable_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_intenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INTENABLE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_args[] = { + { { OPERAND_imms }, 'i' }, + { { OPERAND_immt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_n_args[] = { + { { OPERAND_imms }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_break_n_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSINTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_debugcause_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_debugcause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEBUGCAUSE }, 'i' }, + { { STATE_DBNUM }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_debugcause_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_debugcause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEBUGCAUSE }, 'o' }, + { { STATE_DBNUM }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_debugcause_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_debugcause_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DEBUGCAUSE }, 'm' }, + { { STATE_DBNUM }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icount_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icount_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_ICOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icount_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_ICOUNT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icountlevel_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_icountlevel_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNTLEVEL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icountlevel_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_icountlevel_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNTLEVEL }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icountlevel_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_icountlevel_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ICOUNTLEVEL }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ddr_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ddr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_DDR }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ddr_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_DDR }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfdo_args[] = { + { { OPERAND_imms }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfdo_stateArgs[] = { + { { STATE_InOCDMode }, 'm' }, + { { STATE_EPC2 }, 'i' }, + { { STATE_PSWOE }, 'o' }, + { { STATE_PSCALLINC }, 'o' }, + { { STATE_PSOWB }, 'o' }, + { { STATE_PSRING }, 'o' }, + { { STATE_PSUM }, 'o' }, + { { STATE_PSEXCM }, 'o' }, + { { STATE_PSINTLEVEL }, 'o' }, + { { STATE_EPS2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rfdd_stateArgs[] = { + { { STATE_InOCDMode }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbool1_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_bs }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbool4_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_bs4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbool8_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_bs8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bbranch_args[] = { + { { OPERAND_bs }, 'i' }, + { { OPERAND_label8 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_bmove_args[] = { + { { OPERAND_arr }, 'm' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_RSR_BR_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_brall }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_WSR_BR_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_brall }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_XSR_BR_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_brall }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccount_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOUNT }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccount_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_CCOUNT }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccount_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccount_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' }, + { { STATE_CCOUNT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare0_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare0_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE0 }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare0_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare0_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE0 }, 'm' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ccompare1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ccompare1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE1 }, 'o' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ccompare1_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CCOMPARE1 }, 'm' }, + { { STATE_INTERRUPT }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_inv_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_icache_inv_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_licx_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_licx_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sicx_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sicx_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_ind_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm4x16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_ind_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_inv_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dcache_inv_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_dpf_args[] = { + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sdct_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sdct_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ldct_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ldct_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ptevaddr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_ptevaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PTBASE }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ptevaddr_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_ptevaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PTBASE }, 'i' }, + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ptevaddr_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_ptevaddr_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_PTBASE }, 'm' }, + { { STATE_EXCVADDR }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_rasid_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_rasid_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ASID3 }, 'i' }, + { { STATE_ASID2 }, 'i' }, + { { STATE_ASID1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_rasid_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_rasid_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ASID3 }, 'o' }, + { { STATE_ASID2 }, 'o' }, + { { STATE_ASID1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_rasid_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_rasid_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ASID3 }, 'm' }, + { { STATE_ASID2 }, 'm' }, + { { STATE_ASID1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_itlbcfg_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_itlbcfg_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INSTPGSZID4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_itlbcfg_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_itlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INSTPGSZID4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_itlbcfg_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_itlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_INSTPGSZID4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dtlbcfg_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_dtlbcfg_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DATAPGSZID4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dtlbcfg_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_dtlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DATAPGSZID4 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dtlbcfg_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_dtlbcfg_stateArgs[] = { + { { STATE_XTSYNC }, 'o' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_DATAPGSZID4 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_idtlb_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_idtlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rdtlb_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rdtlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wdtlb_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wdtlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_iitlb_args[] = { + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_iitlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ritlb_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ritlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_witlb_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_witlb_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_ldpte_stateArgs[] = { + { { STATE_PTBASE }, 'i' }, + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_hwwitlba_stateArgs[] = { + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_hwwdtlba_stateArgs[] = { + { { STATE_EXCVADDR }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_cpenable_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_cpenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_cpenable_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_cpenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CPENABLE }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_cpenable_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_cpenable_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_CPENABLE }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_clamp_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_tp7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_minmax_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_nsa_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_sx_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_tp7 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_l32ai_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32ri_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32c1i_args[] = { + { { OPERAND_art }, 'm' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_uimm8x4 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_s32c1i_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'i' }, + { { STATE_XTSYNC }, 'i' }, + { { STATE_SCOMPARE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_scompare1_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_scompare1_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_scompare1_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_scompare1_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_scompare1_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_scompare1_stateArgs[] = { + { { STATE_SCOMPARE1 }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_atomctl_args[] = { + { { OPERAND_art }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rsr_atomctl_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ATOMCTL }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_atomctl_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wsr_atomctl_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ATOMCTL }, 'o' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_atomctl_args[] = { + { { OPERAND_art }, 'm' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_xsr_atomctl_stateArgs[] = { + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_ATOMCTL }, 'm' }, + { { STATE_XTSYNC }, 'o' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rer_args[] = { + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_rer_stateArgs[] = { + { { STATE_CCON }, 'i' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_MPSCORE }, 'i' } +}; + +static xtensa_interface Iclass_xt_iclass_rer_intfArgs[] = { + INTERFACE_RMPINT_Out, + INTERFACE_RMPINT_In +}; + +static xtensa_arg_internal Iclass_xt_iclass_wer_args[] = { + { { OPERAND_art }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_xt_iclass_wer_stateArgs[] = { + { { STATE_CCON }, 'm' }, + { { STATE_PSEXCM }, 'i' }, + { { STATE_PSRING }, 'i' }, + { { STATE_WMPINT_DATA }, 'o' }, + { { STATE_WMPINT_ADDR }, 'o' }, + { { STATE_MPSCORE }, 'm' }, + { { STATE_WMPINT_TOGGLEEN }, 'm' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ovf_sar_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ovf_sar_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'i' }, + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ovf_sar_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ovf_sar_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'o' }, + { { STATE_AE_SAR }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_bithead_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_bithead_stateArgs[] = { + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_bithead_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_bithead_stateArgs[] = { + { { STATE_AE_BITHEAD }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ts_fts_bu_bp_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_ts_fts_bu_bp_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_TABLESIZE }, 'i' }, + { { STATE_AE_FIRST_TS }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ts_fts_bu_bp_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_ts_fts_bu_bp_stateArgs[] = { + { { STATE_AE_BITPTR }, 'o' }, + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_TABLESIZE }, 'o' }, + { { STATE_AE_FIRST_TS }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_rur_ae_sd_no_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_rur_ae_sd_no_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'i' }, + { { STATE_AE_SEARCHDONE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_sd_no_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_wur_ae_sd_no_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_overflow_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_overflow_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_overflow_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_overflow_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_sar_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_sar_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_sar_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_sar_stateArgs[] = { + { { STATE_AE_SAR }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitptr_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitptr_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitptr_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitptr_stateArgs[] = { + { { STATE_AE_BITPTR }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitsused_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_bitsused_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitsused_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_bitsused_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_tablesize_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_tablesize_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_tablesize_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_tablesize_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_first_ts_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_first_ts_stateArgs[] = { + { { STATE_AE_FIRST_TS }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_first_ts_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_first_ts_stateArgs[] = { + { { STATE_AE_FIRST_TS }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_nextoffset_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_nextoffset_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_nextoffset_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_nextoffset_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_searchdone_args[] = { + { { OPERAND_arr }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_rur_ae_searchdone_stateArgs[] = { + { { STATE_AE_SEARCHDONE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_searchdone_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_wur_ae_searchdone_stateArgs[] = { + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp16x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_i_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_iu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_x_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_xu_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lp24x2_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2s_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24x2f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm16 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp16f_l_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24s_l_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_i_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_iu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_x_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_xu_args[] = { + { { OPERAND_pr }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sp24f_l_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_i_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_iu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_x_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_xu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq56_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_i_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_iu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_x_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_xu_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lq32f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_i_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_iu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_x_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_xu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq56s_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_i_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_i_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_iu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_lsimm32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_iu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_x_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_x_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_xu_args[] = { + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sq32f_xu_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zerop48_args[] = { + { { OPERAND_ps }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zerop48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_ll_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_lh_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hl_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hh_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_selp24_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp24x2_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp24x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp24x2_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp24x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp48_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp48_args[] = { + { { OPERAND_pr }, 'm' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movpa24x2_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movpa24x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24a32x2_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24a32x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_l_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_h_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvta32p24_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_ll_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_lh_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hl_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hh_args[] = { + { { OPERAND_pr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtp24a16x2_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24q48x2_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp24q48x2_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp16_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncp16_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48sym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48asym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp24q48asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48sym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48asym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16q48asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16sym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16asym_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsp16asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zeroq56_args[] = { + { { OPERAND_qr1_w }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_zeroq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtq56_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bs }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movtq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfq56_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bs }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movfq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48a32s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48a32s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_l_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_h_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_cvtq48p24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_satq48s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_satq48s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncq32_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_truncq32_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32sym_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32sym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32asym_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_roundsq32asym_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca32q48_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca32q48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_l_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_h_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_movap24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_l_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_h_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_trunca16p24s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' }, + { { OPERAND_bt2 }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorp48_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorp48_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltp24s_args[] = { + { { OPERAND_bt2 }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltp24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lep24s_args[] = { + { { OPERAND_bt2 }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lep24s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqp24_args[] = { + { { OPERAND_bt2 }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqp24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_absq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bt }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_maxbq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_bt }, 'o' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_minbq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_addsq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_subsq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_negsq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_abssq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_andq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nandq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_orq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_xorq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllip24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllip24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlip24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlip24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraip24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraip24_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsp24_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsp24_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasp24_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasp24_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_ae_samt32 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssp24s_args[] = { + { { OPERAND_ps }, 'o' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssp24s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_slliq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_slliq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srliq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srliq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraiq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraiq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllsq56_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlsq56_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srasq56_stateArgs[] = { + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllaq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllaq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlaq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_srlaq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraaq56_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sraaq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ae_samt64 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllisq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllssq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_AE_SAR }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllasq56s_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sllasq56s_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltq56s_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_ltq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_leq56s_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_leq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqq56_args[] = { + { { OPERAND_bt }, 'o' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_eqq56_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nsaq56s_args[] = { + { { OPERAND_ars }, 'o' }, + { { OPERAND_qr0_rw }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_nsaq56s_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hl_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hl_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hl_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfs32p16s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs32p16s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulap24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hl_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs32p16s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsp24s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafs56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulas56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_ll_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_lh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hl_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hl_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfs56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulss56p24s_hh_stateArgs[] = { + { { STATE_AE_OVERFLOW }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_l_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_h_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_l_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_h_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulfq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_l_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_h_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_l_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_h_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulafq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsfq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16s_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_l_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_l_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_h_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsq32sp16u_h_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaaq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsaq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_hh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_hh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16s_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_qr0_rw }, 'i' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_qr0 }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfq32sp16u_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzaap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzasp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzsap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'o' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulzssp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulaap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulasp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsafp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulsap24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hh_ll_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hh_ll_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssfp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hl_lh_args[] = { + { { OPERAND_qr1_w }, 'm' }, + { { OPERAND_pr }, 'i' }, + { { OPERAND_pr0 }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_mulssp24s_hl_lh_stateArgs[] = { + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sha32_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl32t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl32t_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'm' }, + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'm' }, + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'o' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16t_stateArgs[] = { + { { STATE_AE_TABLESIZE }, 'm' }, + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'm' }, + { { STATE_AE_SEARCHDONE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16c_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldl16c_stateArgs[] = { + { { STATE_AE_NEXTOFFSET }, 'm' }, + { { STATE_AE_TABLESIZE }, 'm' }, + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_AE_FIRST_TS }, 'i' }, + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_SEARCHDONE }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldsht_args[] = { + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vldsht_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_AE_FIRST_TS }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_AE_TABLESIZE }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lb_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lb_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbi_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ae_ohba }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbi_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbk_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbk_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbki_args[] = { + { { OPERAND_arr }, 'o' }, + { { OPERAND_ars }, 'i' }, + { { OPERAND_ae_ohba }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_lbki_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_db_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_db_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_dbi_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_ae_ohba }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_dbi_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel32t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'm' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel32t_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel16t_args[] = { + { { OPERAND_br }, 'o' }, + { { OPERAND_art }, 'm' }, + { { OPERAND_ars }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vlel16t_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'o' }, + { { STATE_AE_NEXTOFFSET }, 'o' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sb_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sb_stateArgs[] = { + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbi_args[] = { + { { OPERAND_ars }, 'm' }, + { { OPERAND_art }, 'i' }, + { { OPERAND_ae_ohba }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbi_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vles16c_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_vles16c_stateArgs[] = { + { { STATE_AE_BITPTR }, 'm' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_AE_BITSUSED }, 'i' }, + { { STATE_AE_NEXTOFFSET }, 'i' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbf_args[] = { + { { OPERAND_ars }, 'm' } +}; + +static xtensa_arg_internal Iclass_ae_iclass_sbf_stateArgs[] = { + { { STATE_AE_BITPTR }, 'i' }, + { { STATE_AE_BITHEAD }, 'm' }, + { { STATE_CPENABLE }, 'i' } +}; + +static xtensa_iclass_internal iclasses[] = { + { 0, 0 /* xt_iclass_excw */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_rfe */, + 3, Iclass_xt_iclass_rfe_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_rfde */, + 3, Iclass_xt_iclass_rfde_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_syscall */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_simcall */, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_call12_args, + 1, Iclass_xt_iclass_call12_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_call8_args, + 1, Iclass_xt_iclass_call8_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_call4_args, + 1, Iclass_xt_iclass_call4_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_callx12_args, + 1, Iclass_xt_iclass_callx12_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_callx8_args, + 1, Iclass_xt_iclass_callx8_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_callx4_args, + 1, Iclass_xt_iclass_callx4_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_entry_args, + 5, Iclass_xt_iclass_entry_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_movsp_args, + 2, Iclass_xt_iclass_movsp_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rotw_args, + 3, Iclass_xt_iclass_rotw_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_retw_args, + 4, Iclass_xt_iclass_retw_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_rfwou */, + 6, Iclass_xt_iclass_rfwou_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_l32e_args, + 2, Iclass_xt_iclass_l32e_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_s32e_args, + 2, Iclass_xt_iclass_s32e_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_windowbase_args, + 3, Iclass_xt_iclass_rsr_windowbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_windowbase_args, + 3, Iclass_xt_iclass_wsr_windowbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_windowbase_args, + 3, Iclass_xt_iclass_xsr_windowbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_windowstart_args, + 3, Iclass_xt_iclass_rsr_windowstart_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_windowstart_args, + 3, Iclass_xt_iclass_wsr_windowstart_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_windowstart_args, + 3, Iclass_xt_iclass_xsr_windowstart_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_add_n_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_addi_n_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bz6_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_ill_n */, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_loadi4_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_mov_n_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_movi_n_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_nopn */, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_retn_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_storei4_args, + 0, 0, 0, 0 }, + { 1, Iclass_rur_threadptr_args, + 1, Iclass_rur_threadptr_stateArgs, 0, 0 }, + { 1, Iclass_wur_threadptr_args, + 1, Iclass_wur_threadptr_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_addi_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_addmi_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_addsub_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bit_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bsi8_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bsi8b_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bsi8u_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bst8_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bsz12_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_call0_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_callx0_args, + 0, 0, 0, 0 }, + { 4, Iclass_xt_iclass_exti_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_ill */, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_jump_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_jumpx_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l16ui_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l16si_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l32i_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_l32r_args, + 2, Iclass_xt_iclass_l32r_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_l8i_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_loop_args, + 3, Iclass_xt_iclass_loop_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_loopz_args, + 3, Iclass_xt_iclass_loopz_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_movi_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_movz_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_neg_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_nop */, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_return_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s16i_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32i_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s8i_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_sar_args, + 1, Iclass_xt_iclass_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_sari_args, + 1, Iclass_xt_iclass_sari_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_shifts_args, + 1, Iclass_xt_iclass_shifts_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_shiftst_args, + 1, Iclass_xt_iclass_shiftst_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_shiftt_args, + 1, Iclass_xt_iclass_shiftt_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_slli_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_srai_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_srli_args, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_memw */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_extw */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_isync */, + 0, 0, 0, 0 }, + { 0, 0 /* xt_iclass_sync */, + 1, Iclass_xt_iclass_sync_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_rsil_args, + 7, Iclass_xt_iclass_rsil_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_lend_args, + 1, Iclass_xt_iclass_rsr_lend_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_lend_args, + 1, Iclass_xt_iclass_wsr_lend_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_lend_args, + 1, Iclass_xt_iclass_xsr_lend_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_lcount_args, + 1, Iclass_xt_iclass_rsr_lcount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_lcount_args, + 2, Iclass_xt_iclass_wsr_lcount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_lcount_args, + 2, Iclass_xt_iclass_xsr_lcount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_lbeg_args, + 1, Iclass_xt_iclass_rsr_lbeg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_lbeg_args, + 1, Iclass_xt_iclass_wsr_lbeg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_lbeg_args, + 1, Iclass_xt_iclass_xsr_lbeg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_sar_args, + 1, Iclass_xt_iclass_rsr_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_sar_args, + 2, Iclass_xt_iclass_wsr_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_sar_args, + 1, Iclass_xt_iclass_xsr_sar_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_litbase_args, + 2, Iclass_xt_iclass_rsr_litbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_litbase_args, + 2, Iclass_xt_iclass_wsr_litbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_litbase_args, + 2, Iclass_xt_iclass_xsr_litbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_176_args, + 2, Iclass_xt_iclass_rsr_176_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_176_args, + 2, Iclass_xt_iclass_wsr_176_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_208_args, + 2, Iclass_xt_iclass_rsr_208_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ps_args, + 7, Iclass_xt_iclass_rsr_ps_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ps_args, + 7, Iclass_xt_iclass_wsr_ps_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ps_args, + 7, Iclass_xt_iclass_xsr_ps_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc1_args, + 3, Iclass_xt_iclass_rsr_epc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc1_args, + 3, Iclass_xt_iclass_wsr_epc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc1_args, + 3, Iclass_xt_iclass_xsr_epc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave1_args, + 3, Iclass_xt_iclass_rsr_excsave1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave1_args, + 3, Iclass_xt_iclass_wsr_excsave1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave1_args, + 3, Iclass_xt_iclass_xsr_excsave1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_epc2_args, + 3, Iclass_xt_iclass_rsr_epc2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_epc2_args, + 3, Iclass_xt_iclass_wsr_epc2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_epc2_args, + 3, Iclass_xt_iclass_xsr_epc2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excsave2_args, + 3, Iclass_xt_iclass_rsr_excsave2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excsave2_args, + 3, Iclass_xt_iclass_wsr_excsave2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excsave2_args, + 3, Iclass_xt_iclass_xsr_excsave2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_eps2_args, + 3, Iclass_xt_iclass_rsr_eps2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_eps2_args, + 3, Iclass_xt_iclass_wsr_eps2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_eps2_args, + 3, Iclass_xt_iclass_xsr_eps2_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_excvaddr_args, + 3, Iclass_xt_iclass_rsr_excvaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_excvaddr_args, + 3, Iclass_xt_iclass_wsr_excvaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_excvaddr_args, + 3, Iclass_xt_iclass_xsr_excvaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_depc_args, + 3, Iclass_xt_iclass_rsr_depc_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_depc_args, + 3, Iclass_xt_iclass_wsr_depc_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_depc_args, + 3, Iclass_xt_iclass_xsr_depc_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_exccause_args, + 4, Iclass_xt_iclass_rsr_exccause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_exccause_args, + 3, Iclass_xt_iclass_wsr_exccause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_exccause_args, + 3, Iclass_xt_iclass_xsr_exccause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_misc0_args, + 3, Iclass_xt_iclass_rsr_misc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_misc0_args, + 3, Iclass_xt_iclass_wsr_misc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_misc0_args, + 3, Iclass_xt_iclass_xsr_misc0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_misc1_args, + 3, Iclass_xt_iclass_rsr_misc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_misc1_args, + 3, Iclass_xt_iclass_wsr_misc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_misc1_args, + 3, Iclass_xt_iclass_xsr_misc1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_prid_args, + 2, Iclass_xt_iclass_rsr_prid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_vecbase_args, + 3, Iclass_xt_iclass_rsr_vecbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_vecbase_args, + 3, Iclass_xt_iclass_wsr_vecbase_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_vecbase_args, + 3, Iclass_xt_iclass_xsr_vecbase_stateArgs, 0, 0 }, + { 3, Iclass_xt_mul16_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_mul32_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_rfi_args, + 11, Iclass_xt_iclass_rfi_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wait_args, + 3, Iclass_xt_iclass_wait_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_interrupt_args, + 3, Iclass_xt_iclass_rsr_interrupt_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_intset_args, + 4, Iclass_xt_iclass_wsr_intset_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_intclear_args, + 4, Iclass_xt_iclass_wsr_intclear_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_intenable_args, + 3, Iclass_xt_iclass_rsr_intenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_intenable_args, + 3, Iclass_xt_iclass_wsr_intenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_intenable_args, + 3, Iclass_xt_iclass_xsr_intenable_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_break_args, + 2, Iclass_xt_iclass_break_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_break_n_args, + 2, Iclass_xt_iclass_break_n_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_debugcause_args, + 4, Iclass_xt_iclass_rsr_debugcause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_debugcause_args, + 4, Iclass_xt_iclass_wsr_debugcause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_debugcause_args, + 4, Iclass_xt_iclass_xsr_debugcause_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_icount_args, + 3, Iclass_xt_iclass_rsr_icount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_icount_args, + 4, Iclass_xt_iclass_wsr_icount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_icount_args, + 4, Iclass_xt_iclass_xsr_icount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_icountlevel_args, + 3, Iclass_xt_iclass_rsr_icountlevel_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_icountlevel_args, + 3, Iclass_xt_iclass_wsr_icountlevel_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_icountlevel_args, + 3, Iclass_xt_iclass_xsr_icountlevel_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ddr_args, + 3, Iclass_xt_iclass_rsr_ddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ddr_args, + 4, Iclass_xt_iclass_wsr_ddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ddr_args, + 4, Iclass_xt_iclass_xsr_ddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rfdo_args, + 10, Iclass_xt_iclass_rfdo_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_rfdd */, + 1, Iclass_xt_iclass_rfdd_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_bbool1_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bbool4_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bbool8_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_bbranch_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_bmove_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_RSR_BR_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_WSR_BR_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_XSR_BR_args, + 0, 0, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccount_args, + 3, Iclass_xt_iclass_rsr_ccount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccount_args, + 4, Iclass_xt_iclass_wsr_ccount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccount_args, + 4, Iclass_xt_iclass_xsr_ccount_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccompare0_args, + 3, Iclass_xt_iclass_rsr_ccompare0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccompare0_args, + 4, Iclass_xt_iclass_wsr_ccompare0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccompare0_args, + 4, Iclass_xt_iclass_xsr_ccompare0_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ccompare1_args, + 3, Iclass_xt_iclass_rsr_ccompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ccompare1_args, + 4, Iclass_xt_iclass_wsr_ccompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ccompare1_args, + 4, Iclass_xt_iclass_xsr_ccompare1_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_icache_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_icache_inv_args, + 2, Iclass_xt_iclass_icache_inv_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_licx_args, + 2, Iclass_xt_iclass_licx_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_sicx_args, + 2, Iclass_xt_iclass_sicx_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_ind_args, + 2, Iclass_xt_iclass_dcache_ind_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dcache_inv_args, + 2, Iclass_xt_iclass_dcache_inv_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_dpf_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_sdct_args, + 2, Iclass_xt_iclass_sdct_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_ldct_args, + 2, Iclass_xt_iclass_ldct_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_ptevaddr_args, + 4, Iclass_xt_iclass_wsr_ptevaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_ptevaddr_args, + 4, Iclass_xt_iclass_rsr_ptevaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_ptevaddr_args, + 5, Iclass_xt_iclass_xsr_ptevaddr_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_rasid_args, + 5, Iclass_xt_iclass_rsr_rasid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_rasid_args, + 6, Iclass_xt_iclass_wsr_rasid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_rasid_args, + 6, Iclass_xt_iclass_xsr_rasid_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_itlbcfg_args, + 3, Iclass_xt_iclass_rsr_itlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_itlbcfg_args, + 4, Iclass_xt_iclass_wsr_itlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_itlbcfg_args, + 4, Iclass_xt_iclass_xsr_itlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_dtlbcfg_args, + 3, Iclass_xt_iclass_rsr_dtlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_dtlbcfg_args, + 4, Iclass_xt_iclass_wsr_dtlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_dtlbcfg_args, + 4, Iclass_xt_iclass_xsr_dtlbcfg_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_idtlb_args, + 3, Iclass_xt_iclass_idtlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_rdtlb_args, + 2, Iclass_xt_iclass_rdtlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_wdtlb_args, + 3, Iclass_xt_iclass_wdtlb_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_iitlb_args, + 2, Iclass_xt_iclass_iitlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_ritlb_args, + 2, Iclass_xt_iclass_ritlb_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_witlb_args, + 2, Iclass_xt_iclass_witlb_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_ldpte */, + 2, Iclass_xt_iclass_ldpte_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_hwwitlba */, + 1, Iclass_xt_iclass_hwwitlba_stateArgs, 0, 0 }, + { 0, 0 /* xt_iclass_hwwdtlba */, + 1, Iclass_xt_iclass_hwwdtlba_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_cpenable_args, + 3, Iclass_xt_iclass_rsr_cpenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_cpenable_args, + 3, Iclass_xt_iclass_wsr_cpenable_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_cpenable_args, + 3, Iclass_xt_iclass_xsr_cpenable_stateArgs, 0, 0 }, + { 3, Iclass_xt_iclass_clamp_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_minmax_args, + 0, 0, 0, 0 }, + { 2, Iclass_xt_iclass_nsa_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_sx_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_l32ai_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32ri_args, + 0, 0, 0, 0 }, + { 3, Iclass_xt_iclass_s32c1i_args, + 3, Iclass_xt_iclass_s32c1i_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_scompare1_args, + 1, Iclass_xt_iclass_rsr_scompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_scompare1_args, + 1, Iclass_xt_iclass_wsr_scompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_scompare1_args, + 1, Iclass_xt_iclass_xsr_scompare1_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_rsr_atomctl_args, + 3, Iclass_xt_iclass_rsr_atomctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_wsr_atomctl_args, + 4, Iclass_xt_iclass_wsr_atomctl_stateArgs, 0, 0 }, + { 1, Iclass_xt_iclass_xsr_atomctl_args, + 4, Iclass_xt_iclass_xsr_atomctl_stateArgs, 0, 0 }, + { 2, Iclass_xt_iclass_rer_args, + 4, Iclass_xt_iclass_rer_stateArgs, 2, Iclass_xt_iclass_rer_intfArgs }, + { 2, Iclass_xt_iclass_wer_args, + 7, Iclass_xt_iclass_wer_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_ovf_sar_args, + 3, Iclass_rur_ae_ovf_sar_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_ovf_sar_args, + 3, Iclass_wur_ae_ovf_sar_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_bithead_args, + 2, Iclass_rur_ae_bithead_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_bithead_args, + 2, Iclass_wur_ae_bithead_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_ts_fts_bu_bp_args, + 5, Iclass_rur_ae_ts_fts_bu_bp_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_ts_fts_bu_bp_args, + 5, Iclass_wur_ae_ts_fts_bu_bp_stateArgs, 0, 0 }, + { 1, Iclass_rur_ae_sd_no_args, + 3, Iclass_rur_ae_sd_no_stateArgs, 0, 0 }, + { 1, Iclass_wur_ae_sd_no_args, + 3, Iclass_wur_ae_sd_no_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_overflow_args, + 2, Iclass_ae_iclass_rur_ae_overflow_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_overflow_args, + 2, Iclass_ae_iclass_wur_ae_overflow_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_sar_args, + 2, Iclass_ae_iclass_rur_ae_sar_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_sar_args, + 2, Iclass_ae_iclass_wur_ae_sar_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_bitptr_args, + 2, Iclass_ae_iclass_rur_ae_bitptr_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_bitptr_args, + 2, Iclass_ae_iclass_wur_ae_bitptr_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_bitsused_args, + 2, Iclass_ae_iclass_rur_ae_bitsused_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_bitsused_args, + 2, Iclass_ae_iclass_wur_ae_bitsused_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_tablesize_args, + 2, Iclass_ae_iclass_rur_ae_tablesize_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_tablesize_args, + 2, Iclass_ae_iclass_wur_ae_tablesize_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_first_ts_args, + 2, Iclass_ae_iclass_rur_ae_first_ts_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_first_ts_args, + 2, Iclass_ae_iclass_wur_ae_first_ts_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_nextoffset_args, + 2, Iclass_ae_iclass_rur_ae_nextoffset_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_nextoffset_args, + 2, Iclass_ae_iclass_wur_ae_nextoffset_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_rur_ae_searchdone_args, + 2, Iclass_ae_iclass_rur_ae_searchdone_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_wur_ae_searchdone_args, + 2, Iclass_ae_iclass_wur_ae_searchdone_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_i_args, + 1, Iclass_ae_iclass_lp16f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_iu_args, + 1, Iclass_ae_iclass_lp16f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_x_args, + 1, Iclass_ae_iclass_lp16f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16f_xu_args, + 1, Iclass_ae_iclass_lp16f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_i_args, + 1, Iclass_ae_iclass_lp24_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_iu_args, + 1, Iclass_ae_iclass_lp24_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_x_args, + 1, Iclass_ae_iclass_lp24_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24_xu_args, + 1, Iclass_ae_iclass_lp24_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_i_args, + 1, Iclass_ae_iclass_lp24f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_iu_args, + 1, Iclass_ae_iclass_lp24f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_x_args, + 1, Iclass_ae_iclass_lp24f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24f_xu_args, + 1, Iclass_ae_iclass_lp24f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_i_args, + 1, Iclass_ae_iclass_lp16x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_iu_args, + 1, Iclass_ae_iclass_lp16x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_x_args, + 1, Iclass_ae_iclass_lp16x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp16x2f_xu_args, + 1, Iclass_ae_iclass_lp16x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_i_args, + 1, Iclass_ae_iclass_lp24x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_iu_args, + 1, Iclass_ae_iclass_lp24x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_x_args, + 1, Iclass_ae_iclass_lp24x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2f_xu_args, + 1, Iclass_ae_iclass_lp24x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_i_args, + 1, Iclass_ae_iclass_lp24x2_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_iu_args, + 1, Iclass_ae_iclass_lp24x2_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_x_args, + 1, Iclass_ae_iclass_lp24x2_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lp24x2_xu_args, + 1, Iclass_ae_iclass_lp24x2_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_i_args, + 1, Iclass_ae_iclass_sp16x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_iu_args, + 1, Iclass_ae_iclass_sp16x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_x_args, + 1, Iclass_ae_iclass_sp16x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16x2f_xu_args, + 1, Iclass_ae_iclass_sp16x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_i_args, + 1, Iclass_ae_iclass_sp24x2s_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_iu_args, + 1, Iclass_ae_iclass_sp24x2s_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_x_args, + 1, Iclass_ae_iclass_sp24x2s_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2s_xu_args, + 1, Iclass_ae_iclass_sp24x2s_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_i_args, + 1, Iclass_ae_iclass_sp24x2f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_iu_args, + 1, Iclass_ae_iclass_sp24x2f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_x_args, + 1, Iclass_ae_iclass_sp24x2f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24x2f_xu_args, + 1, Iclass_ae_iclass_sp24x2f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_i_args, + 1, Iclass_ae_iclass_sp16f_l_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_iu_args, + 1, Iclass_ae_iclass_sp16f_l_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_x_args, + 1, Iclass_ae_iclass_sp16f_l_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp16f_l_xu_args, + 1, Iclass_ae_iclass_sp16f_l_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_i_args, + 1, Iclass_ae_iclass_sp24s_l_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_iu_args, + 1, Iclass_ae_iclass_sp24s_l_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_x_args, + 1, Iclass_ae_iclass_sp24s_l_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24s_l_xu_args, + 1, Iclass_ae_iclass_sp24s_l_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_i_args, + 1, Iclass_ae_iclass_sp24f_l_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_iu_args, + 1, Iclass_ae_iclass_sp24f_l_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_x_args, + 1, Iclass_ae_iclass_sp24f_l_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sp24f_l_xu_args, + 1, Iclass_ae_iclass_sp24f_l_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_i_args, + 1, Iclass_ae_iclass_lq56_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_iu_args, + 1, Iclass_ae_iclass_lq56_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_x_args, + 1, Iclass_ae_iclass_lq56_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq56_xu_args, + 1, Iclass_ae_iclass_lq56_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_i_args, + 1, Iclass_ae_iclass_lq32f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_iu_args, + 1, Iclass_ae_iclass_lq32f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_x_args, + 1, Iclass_ae_iclass_lq32f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lq32f_xu_args, + 1, Iclass_ae_iclass_lq32f_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_i_args, + 1, Iclass_ae_iclass_sq56s_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_iu_args, + 1, Iclass_ae_iclass_sq56s_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_x_args, + 1, Iclass_ae_iclass_sq56s_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq56s_xu_args, + 1, Iclass_ae_iclass_sq56s_xu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_i_args, + 1, Iclass_ae_iclass_sq32f_i_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_iu_args, + 1, Iclass_ae_iclass_sq32f_iu_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_x_args, + 1, Iclass_ae_iclass_sq32f_x_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sq32f_xu_args, + 1, Iclass_ae_iclass_sq32f_xu_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_zerop48_args, + 1, Iclass_ae_iclass_zerop48_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movp48_args, + 1, Iclass_ae_iclass_movp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_ll_args, + 1, Iclass_ae_iclass_selp24_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_lh_args, + 1, Iclass_ae_iclass_selp24_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_hl_args, + 1, Iclass_ae_iclass_selp24_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_selp24_hh_args, + 1, Iclass_ae_iclass_selp24_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movtp24x2_args, + 1, Iclass_ae_iclass_movtp24x2_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movfp24x2_args, + 1, Iclass_ae_iclass_movfp24x2_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movtp48_args, + 1, Iclass_ae_iclass_movtp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movfp48_args, + 1, Iclass_ae_iclass_movfp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movpa24x2_args, + 1, Iclass_ae_iclass_movpa24x2_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_truncp24a32x2_args, + 1, Iclass_ae_iclass_truncp24a32x2_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvta32p24_l_args, + 1, Iclass_ae_iclass_cvta32p24_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvta32p24_h_args, + 1, Iclass_ae_iclass_cvta32p24_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_ll_args, + 1, Iclass_ae_iclass_cvtp24a16x2_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_lh_args, + 1, Iclass_ae_iclass_cvtp24a16x2_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_hl_args, + 1, Iclass_ae_iclass_cvtp24a16x2_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_cvtp24a16x2_hh_args, + 1, Iclass_ae_iclass_cvtp24a16x2_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_truncp24q48x2_args, + 1, Iclass_ae_iclass_truncp24q48x2_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_truncp16_args, + 1, Iclass_ae_iclass_truncp16_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp24q48sym_args, + 2, Iclass_ae_iclass_roundsp24q48sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp24q48asym_args, + 2, Iclass_ae_iclass_roundsp24q48asym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16q48sym_args, + 2, Iclass_ae_iclass_roundsp16q48sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16q48asym_args, + 2, Iclass_ae_iclass_roundsp16q48asym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16sym_args, + 2, Iclass_ae_iclass_roundsp16sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsp16asym_args, + 2, Iclass_ae_iclass_roundsp16asym_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_zeroq56_args, + 1, Iclass_ae_iclass_zeroq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movq56_args, + 1, Iclass_ae_iclass_movq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movtq56_args, + 1, Iclass_ae_iclass_movtq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_movfq56_args, + 1, Iclass_ae_iclass_movfq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvtq48a32s_args, + 1, Iclass_ae_iclass_cvtq48a32s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvtq48p24s_l_args, + 1, Iclass_ae_iclass_cvtq48p24s_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_cvtq48p24s_h_args, + 1, Iclass_ae_iclass_cvtq48p24s_h_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_satq48s_args, + 2, Iclass_ae_iclass_satq48s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_truncq32_args, + 1, Iclass_ae_iclass_truncq32_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsq32sym_args, + 2, Iclass_ae_iclass_roundsq32sym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_roundsq32asym_args, + 2, Iclass_ae_iclass_roundsq32asym_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_trunca32q48_args, + 1, Iclass_ae_iclass_trunca32q48_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movap24s_l_args, + 1, Iclass_ae_iclass_movap24s_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_movap24s_h_args, + 1, Iclass_ae_iclass_movap24s_h_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_trunca16p24s_l_args, + 1, Iclass_ae_iclass_trunca16p24s_l_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_trunca16p24s_h_args, + 1, Iclass_ae_iclass_trunca16p24s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addp24_args, + 1, Iclass_ae_iclass_addp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subp24_args, + 1, Iclass_ae_iclass_subp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negp24_args, + 1, Iclass_ae_iclass_negp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_absp24_args, + 1, Iclass_ae_iclass_absp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_maxp24s_args, + 1, Iclass_ae_iclass_maxp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_minp24s_args, + 1, Iclass_ae_iclass_minp24s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_maxbp24s_args, + 1, Iclass_ae_iclass_maxbp24s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_minbp24s_args, + 1, Iclass_ae_iclass_minbp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addsp24s_args, + 2, Iclass_ae_iclass_addsp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subsp24s_args, + 2, Iclass_ae_iclass_subsp24s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negsp24s_args, + 2, Iclass_ae_iclass_negsp24s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_abssp24s_args, + 2, Iclass_ae_iclass_abssp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_andp48_args, + 1, Iclass_ae_iclass_andp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_nandp48_args, + 1, Iclass_ae_iclass_nandp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_orp48_args, + 1, Iclass_ae_iclass_orp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_xorp48_args, + 1, Iclass_ae_iclass_xorp48_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_ltp24s_args, + 1, Iclass_ae_iclass_ltp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lep24s_args, + 1, Iclass_ae_iclass_lep24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_eqp24_args, + 1, Iclass_ae_iclass_eqp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addq56_args, + 1, Iclass_ae_iclass_addq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subq56_args, + 1, Iclass_ae_iclass_subq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negq56_args, + 1, Iclass_ae_iclass_negq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_absq56_args, + 1, Iclass_ae_iclass_absq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_maxq56s_args, + 1, Iclass_ae_iclass_maxq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_minq56s_args, + 1, Iclass_ae_iclass_minq56s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_maxbq56s_args, + 1, Iclass_ae_iclass_maxbq56s_stateArgs, 0, 0 }, + { 4, Iclass_ae_iclass_minbq56s_args, + 1, Iclass_ae_iclass_minbq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_addsq56s_args, + 2, Iclass_ae_iclass_addsq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_subsq56s_args, + 2, Iclass_ae_iclass_subsq56s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_negsq56s_args, + 2, Iclass_ae_iclass_negsq56s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_abssq56s_args, + 2, Iclass_ae_iclass_abssq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_andq56_args, + 1, Iclass_ae_iclass_andq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_nandq56_args, + 1, Iclass_ae_iclass_nandq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_orq56_args, + 1, Iclass_ae_iclass_orq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_xorq56_args, + 1, Iclass_ae_iclass_xorq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllip24_args, + 1, Iclass_ae_iclass_sllip24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_srlip24_args, + 1, Iclass_ae_iclass_srlip24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sraip24_args, + 1, Iclass_ae_iclass_sraip24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllsp24_args, + 2, Iclass_ae_iclass_sllsp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srlsp24_args, + 2, Iclass_ae_iclass_srlsp24_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srasp24_args, + 2, Iclass_ae_iclass_srasp24_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllisp24s_args, + 2, Iclass_ae_iclass_sllisp24s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllssp24s_args, + 3, Iclass_ae_iclass_sllssp24s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_slliq56_args, + 1, Iclass_ae_iclass_slliq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_srliq56_args, + 1, Iclass_ae_iclass_srliq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sraiq56_args, + 1, Iclass_ae_iclass_sraiq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllsq56_args, + 2, Iclass_ae_iclass_sllsq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srlsq56_args, + 2, Iclass_ae_iclass_srlsq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_srasq56_args, + 2, Iclass_ae_iclass_srasq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllaq56_args, + 1, Iclass_ae_iclass_sllaq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_srlaq56_args, + 1, Iclass_ae_iclass_srlaq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sraaq56_args, + 1, Iclass_ae_iclass_sraaq56_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllisq56s_args, + 2, Iclass_ae_iclass_sllisq56s_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sllssq56s_args, + 3, Iclass_ae_iclass_sllssq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sllasq56s_args, + 2, Iclass_ae_iclass_sllasq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_ltq56s_args, + 1, Iclass_ae_iclass_ltq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_leq56s_args, + 1, Iclass_ae_iclass_leq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_eqq56_args, + 1, Iclass_ae_iclass_eqq56_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_nsaq56s_args, + 1, Iclass_ae_iclass_nsaq56s_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_ll_args, + 2, Iclass_ae_iclass_mulfs32p16s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_ll_args, + 1, Iclass_ae_iclass_mulfp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_ll_args, + 1, Iclass_ae_iclass_mulp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_lh_args, + 2, Iclass_ae_iclass_mulfs32p16s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_lh_args, + 1, Iclass_ae_iclass_mulfp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_lh_args, + 1, Iclass_ae_iclass_mulp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_hl_args, + 2, Iclass_ae_iclass_mulfs32p16s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_hl_args, + 1, Iclass_ae_iclass_mulfp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_hl_args, + 1, Iclass_ae_iclass_mulp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfs32p16s_hh_args, + 2, Iclass_ae_iclass_mulfs32p16s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfp24s_hh_args, + 1, Iclass_ae_iclass_mulfp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulp24s_hh_args, + 1, Iclass_ae_iclass_mulp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_ll_args, + 2, Iclass_ae_iclass_mulafs32p16s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_ll_args, + 1, Iclass_ae_iclass_mulafp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_ll_args, + 1, Iclass_ae_iclass_mulap24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_lh_args, + 2, Iclass_ae_iclass_mulafs32p16s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_lh_args, + 1, Iclass_ae_iclass_mulafp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_lh_args, + 1, Iclass_ae_iclass_mulap24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_hl_args, + 2, Iclass_ae_iclass_mulafs32p16s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_hl_args, + 1, Iclass_ae_iclass_mulafp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_hl_args, + 1, Iclass_ae_iclass_mulap24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs32p16s_hh_args, + 2, Iclass_ae_iclass_mulafs32p16s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafp24s_hh_args, + 1, Iclass_ae_iclass_mulafp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulap24s_hh_args, + 1, Iclass_ae_iclass_mulap24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_ll_args, + 2, Iclass_ae_iclass_mulsfs32p16s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_ll_args, + 1, Iclass_ae_iclass_mulsfp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_ll_args, + 1, Iclass_ae_iclass_mulsp24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_lh_args, + 2, Iclass_ae_iclass_mulsfs32p16s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_lh_args, + 1, Iclass_ae_iclass_mulsfp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_lh_args, + 1, Iclass_ae_iclass_mulsp24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_hl_args, + 2, Iclass_ae_iclass_mulsfs32p16s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_hl_args, + 1, Iclass_ae_iclass_mulsfp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_hl_args, + 1, Iclass_ae_iclass_mulsp24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs32p16s_hh_args, + 2, Iclass_ae_iclass_mulsfs32p16s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfp24s_hh_args, + 1, Iclass_ae_iclass_mulsfp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsp24s_hh_args, + 1, Iclass_ae_iclass_mulsp24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_ll_args, + 2, Iclass_ae_iclass_mulafs56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_ll_args, + 2, Iclass_ae_iclass_mulas56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_lh_args, + 2, Iclass_ae_iclass_mulafs56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_lh_args, + 2, Iclass_ae_iclass_mulas56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_hl_args, + 2, Iclass_ae_iclass_mulafs56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_hl_args, + 2, Iclass_ae_iclass_mulas56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafs56p24s_hh_args, + 2, Iclass_ae_iclass_mulafs56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulas56p24s_hh_args, + 2, Iclass_ae_iclass_mulas56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_ll_args, + 2, Iclass_ae_iclass_mulsfs56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_ll_args, + 2, Iclass_ae_iclass_mulss56p24s_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_lh_args, + 2, Iclass_ae_iclass_mulsfs56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_lh_args, + 2, Iclass_ae_iclass_mulss56p24s_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_hl_args, + 2, Iclass_ae_iclass_mulsfs56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_hl_args, + 2, Iclass_ae_iclass_mulss56p24s_hl_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfs56p24s_hh_args, + 2, Iclass_ae_iclass_mulsfs56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulss56p24s_hh_args, + 2, Iclass_ae_iclass_mulss56p24s_hh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16s_l_args, + 1, Iclass_ae_iclass_mulfq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16s_h_args, + 1, Iclass_ae_iclass_mulfq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16u_l_args, + 1, Iclass_ae_iclass_mulfq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulfq32sp16u_h_args, + 1, Iclass_ae_iclass_mulfq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16s_l_args, + 1, Iclass_ae_iclass_mulq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16s_h_args, + 1, Iclass_ae_iclass_mulq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16u_l_args, + 1, Iclass_ae_iclass_mulq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulq32sp16u_h_args, + 1, Iclass_ae_iclass_mulq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16s_l_args, + 1, Iclass_ae_iclass_mulafq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16s_h_args, + 1, Iclass_ae_iclass_mulafq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16u_l_args, + 1, Iclass_ae_iclass_mulafq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulafq32sp16u_h_args, + 1, Iclass_ae_iclass_mulafq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16s_l_args, + 1, Iclass_ae_iclass_mulaq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16s_h_args, + 1, Iclass_ae_iclass_mulaq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16u_l_args, + 1, Iclass_ae_iclass_mulaq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaq32sp16u_h_args, + 1, Iclass_ae_iclass_mulaq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16s_l_args, + 1, Iclass_ae_iclass_mulsfq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16s_h_args, + 1, Iclass_ae_iclass_mulsfq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16u_l_args, + 1, Iclass_ae_iclass_mulsfq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsfq32sp16u_h_args, + 1, Iclass_ae_iclass_mulsfq32sp16u_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16s_l_args, + 1, Iclass_ae_iclass_mulsq32sp16s_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16s_h_args, + 1, Iclass_ae_iclass_mulsq32sp16s_h_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16u_l_args, + 1, Iclass_ae_iclass_mulsq32sp16u_l_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsq32sp16u_h_args, + 1, Iclass_ae_iclass_mulsq32sp16u_h_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzaaq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzaafq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzaaq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzaafq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaaq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzaaq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzaafq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzaafq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzasq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzasfq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzasq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzasfq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzasq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzasq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzasq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzasq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzasfq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzasfq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzsaq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzsafq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzsaq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzsafq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsaq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzsaq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzsafq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzsafq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzssq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16s_ll_args, + 1, Iclass_ae_iclass_mulzssfq32sp16s_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzssq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16u_ll_args, + 1, Iclass_ae_iclass_mulzssfq32sp16u_ll_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzssq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16s_hh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16s_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzssq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16u_hh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16u_hh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzssq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16s_lh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16s_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzssq32sp16u_lh_stateArgs, 0, 0 }, + { 5, Iclass_ae_iclass_mulzssfq32sp16u_lh_args, + 1, Iclass_ae_iclass_mulzssfq32sp16u_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzaafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzaap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzaafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzaap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzaap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzasfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzasp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzasfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzasp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzasp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzsafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzsap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzsafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzsap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzsap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzssfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulzssp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzssfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulzssp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulzssp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulaafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulaap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulaafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulaap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulaap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulasfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulasp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulasfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulasp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulasp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsafp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulsafp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsap24s_hh_ll_args, + 1, Iclass_ae_iclass_mulsap24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsafp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulsafp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulsap24s_hl_lh_args, + 1, Iclass_ae_iclass_mulsap24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssfp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulssfp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssp24s_hh_ll_args, + 1, Iclass_ae_iclass_mulssp24s_hh_ll_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssfp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulssfp24s_hl_lh_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_mulssp24s_hl_lh_args, + 1, Iclass_ae_iclass_mulssp24s_hl_lh_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sha32_args, + 0, 0, 0, 0 }, + { 3, Iclass_ae_iclass_vldl32t_args, + 5, Iclass_ae_iclass_vldl32t_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_vldl16t_args, + 5, Iclass_ae_iclass_vldl16t_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_vldl16c_args, + 8, Iclass_ae_iclass_vldl16c_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_vldsht_args, + 6, Iclass_ae_iclass_vldsht_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_lb_args, + 3, Iclass_ae_iclass_lb_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_lbi_args, + 3, Iclass_ae_iclass_lbi_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lbk_args, + 3, Iclass_ae_iclass_lbk_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_lbki_args, + 3, Iclass_ae_iclass_lbki_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_db_args, + 3, Iclass_ae_iclass_db_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_dbi_args, + 3, Iclass_ae_iclass_dbi_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_vlel32t_args, + 3, Iclass_ae_iclass_vlel32t_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_vlel16t_args, + 3, Iclass_ae_iclass_vlel16t_stateArgs, 0, 0 }, + { 2, Iclass_ae_iclass_sb_args, + 4, Iclass_ae_iclass_sb_stateArgs, 0, 0 }, + { 3, Iclass_ae_iclass_sbi_args, + 3, Iclass_ae_iclass_sbi_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_vles16c_args, + 5, Iclass_ae_iclass_vles16c_stateArgs, 0, 0 }, + { 1, Iclass_ae_iclass_sbf_args, + 3, Iclass_ae_iclass_sbf_stateArgs, 0, 0 } +}; + +enum xtensa_iclass_id { + ICLASS_xt_iclass_excw, + ICLASS_xt_iclass_rfe, + ICLASS_xt_iclass_rfde, + ICLASS_xt_iclass_syscall, + ICLASS_xt_iclass_simcall, + ICLASS_xt_iclass_call12, + ICLASS_xt_iclass_call8, + ICLASS_xt_iclass_call4, + ICLASS_xt_iclass_callx12, + ICLASS_xt_iclass_callx8, + ICLASS_xt_iclass_callx4, + ICLASS_xt_iclass_entry, + ICLASS_xt_iclass_movsp, + ICLASS_xt_iclass_rotw, + ICLASS_xt_iclass_retw, + ICLASS_xt_iclass_rfwou, + ICLASS_xt_iclass_l32e, + ICLASS_xt_iclass_s32e, + ICLASS_xt_iclass_rsr_windowbase, + ICLASS_xt_iclass_wsr_windowbase, + ICLASS_xt_iclass_xsr_windowbase, + ICLASS_xt_iclass_rsr_windowstart, + ICLASS_xt_iclass_wsr_windowstart, + ICLASS_xt_iclass_xsr_windowstart, + ICLASS_xt_iclass_add_n, + ICLASS_xt_iclass_addi_n, + ICLASS_xt_iclass_bz6, + ICLASS_xt_iclass_ill_n, + ICLASS_xt_iclass_loadi4, + ICLASS_xt_iclass_mov_n, + ICLASS_xt_iclass_movi_n, + ICLASS_xt_iclass_nopn, + ICLASS_xt_iclass_retn, + ICLASS_xt_iclass_storei4, + ICLASS_rur_threadptr, + ICLASS_wur_threadptr, + ICLASS_xt_iclass_addi, + ICLASS_xt_iclass_addmi, + ICLASS_xt_iclass_addsub, + ICLASS_xt_iclass_bit, + ICLASS_xt_iclass_bsi8, + ICLASS_xt_iclass_bsi8b, + ICLASS_xt_iclass_bsi8u, + ICLASS_xt_iclass_bst8, + ICLASS_xt_iclass_bsz12, + ICLASS_xt_iclass_call0, + ICLASS_xt_iclass_callx0, + ICLASS_xt_iclass_exti, + ICLASS_xt_iclass_ill, + ICLASS_xt_iclass_jump, + ICLASS_xt_iclass_jumpx, + ICLASS_xt_iclass_l16ui, + ICLASS_xt_iclass_l16si, + ICLASS_xt_iclass_l32i, + ICLASS_xt_iclass_l32r, + ICLASS_xt_iclass_l8i, + ICLASS_xt_iclass_loop, + ICLASS_xt_iclass_loopz, + ICLASS_xt_iclass_movi, + ICLASS_xt_iclass_movz, + ICLASS_xt_iclass_neg, + ICLASS_xt_iclass_nop, + ICLASS_xt_iclass_return, + ICLASS_xt_iclass_s16i, + ICLASS_xt_iclass_s32i, + ICLASS_xt_iclass_s8i, + ICLASS_xt_iclass_sar, + ICLASS_xt_iclass_sari, + ICLASS_xt_iclass_shifts, + ICLASS_xt_iclass_shiftst, + ICLASS_xt_iclass_shiftt, + ICLASS_xt_iclass_slli, + ICLASS_xt_iclass_srai, + ICLASS_xt_iclass_srli, + ICLASS_xt_iclass_memw, + ICLASS_xt_iclass_extw, + ICLASS_xt_iclass_isync, + ICLASS_xt_iclass_sync, + ICLASS_xt_iclass_rsil, + ICLASS_xt_iclass_rsr_lend, + ICLASS_xt_iclass_wsr_lend, + ICLASS_xt_iclass_xsr_lend, + ICLASS_xt_iclass_rsr_lcount, + ICLASS_xt_iclass_wsr_lcount, + ICLASS_xt_iclass_xsr_lcount, + ICLASS_xt_iclass_rsr_lbeg, + ICLASS_xt_iclass_wsr_lbeg, + ICLASS_xt_iclass_xsr_lbeg, + ICLASS_xt_iclass_rsr_sar, + ICLASS_xt_iclass_wsr_sar, + ICLASS_xt_iclass_xsr_sar, + ICLASS_xt_iclass_rsr_litbase, + ICLASS_xt_iclass_wsr_litbase, + ICLASS_xt_iclass_xsr_litbase, + ICLASS_xt_iclass_rsr_176, + ICLASS_xt_iclass_wsr_176, + ICLASS_xt_iclass_rsr_208, + ICLASS_xt_iclass_rsr_ps, + ICLASS_xt_iclass_wsr_ps, + ICLASS_xt_iclass_xsr_ps, + ICLASS_xt_iclass_rsr_epc1, + ICLASS_xt_iclass_wsr_epc1, + ICLASS_xt_iclass_xsr_epc1, + ICLASS_xt_iclass_rsr_excsave1, + ICLASS_xt_iclass_wsr_excsave1, + ICLASS_xt_iclass_xsr_excsave1, + ICLASS_xt_iclass_rsr_epc2, + ICLASS_xt_iclass_wsr_epc2, + ICLASS_xt_iclass_xsr_epc2, + ICLASS_xt_iclass_rsr_excsave2, + ICLASS_xt_iclass_wsr_excsave2, + ICLASS_xt_iclass_xsr_excsave2, + ICLASS_xt_iclass_rsr_eps2, + ICLASS_xt_iclass_wsr_eps2, + ICLASS_xt_iclass_xsr_eps2, + ICLASS_xt_iclass_rsr_excvaddr, + ICLASS_xt_iclass_wsr_excvaddr, + ICLASS_xt_iclass_xsr_excvaddr, + ICLASS_xt_iclass_rsr_depc, + ICLASS_xt_iclass_wsr_depc, + ICLASS_xt_iclass_xsr_depc, + ICLASS_xt_iclass_rsr_exccause, + ICLASS_xt_iclass_wsr_exccause, + ICLASS_xt_iclass_xsr_exccause, + ICLASS_xt_iclass_rsr_misc0, + ICLASS_xt_iclass_wsr_misc0, + ICLASS_xt_iclass_xsr_misc0, + ICLASS_xt_iclass_rsr_misc1, + ICLASS_xt_iclass_wsr_misc1, + ICLASS_xt_iclass_xsr_misc1, + ICLASS_xt_iclass_rsr_prid, + ICLASS_xt_iclass_rsr_vecbase, + ICLASS_xt_iclass_wsr_vecbase, + ICLASS_xt_iclass_xsr_vecbase, + ICLASS_xt_mul16, + ICLASS_xt_mul32, + ICLASS_xt_iclass_rfi, + ICLASS_xt_iclass_wait, + ICLASS_xt_iclass_rsr_interrupt, + ICLASS_xt_iclass_wsr_intset, + ICLASS_xt_iclass_wsr_intclear, + ICLASS_xt_iclass_rsr_intenable, + ICLASS_xt_iclass_wsr_intenable, + ICLASS_xt_iclass_xsr_intenable, + ICLASS_xt_iclass_break, + ICLASS_xt_iclass_break_n, + ICLASS_xt_iclass_rsr_debugcause, + ICLASS_xt_iclass_wsr_debugcause, + ICLASS_xt_iclass_xsr_debugcause, + ICLASS_xt_iclass_rsr_icount, + ICLASS_xt_iclass_wsr_icount, + ICLASS_xt_iclass_xsr_icount, + ICLASS_xt_iclass_rsr_icountlevel, + ICLASS_xt_iclass_wsr_icountlevel, + ICLASS_xt_iclass_xsr_icountlevel, + ICLASS_xt_iclass_rsr_ddr, + ICLASS_xt_iclass_wsr_ddr, + ICLASS_xt_iclass_xsr_ddr, + ICLASS_xt_iclass_rfdo, + ICLASS_xt_iclass_rfdd, + ICLASS_xt_iclass_bbool1, + ICLASS_xt_iclass_bbool4, + ICLASS_xt_iclass_bbool8, + ICLASS_xt_iclass_bbranch, + ICLASS_xt_iclass_bmove, + ICLASS_xt_iclass_RSR_BR, + ICLASS_xt_iclass_WSR_BR, + ICLASS_xt_iclass_XSR_BR, + ICLASS_xt_iclass_rsr_ccount, + ICLASS_xt_iclass_wsr_ccount, + ICLASS_xt_iclass_xsr_ccount, + ICLASS_xt_iclass_rsr_ccompare0, + ICLASS_xt_iclass_wsr_ccompare0, + ICLASS_xt_iclass_xsr_ccompare0, + ICLASS_xt_iclass_rsr_ccompare1, + ICLASS_xt_iclass_wsr_ccompare1, + ICLASS_xt_iclass_xsr_ccompare1, + ICLASS_xt_iclass_icache, + ICLASS_xt_iclass_icache_inv, + ICLASS_xt_iclass_licx, + ICLASS_xt_iclass_sicx, + ICLASS_xt_iclass_dcache, + ICLASS_xt_iclass_dcache_ind, + ICLASS_xt_iclass_dcache_inv, + ICLASS_xt_iclass_dpf, + ICLASS_xt_iclass_sdct, + ICLASS_xt_iclass_ldct, + ICLASS_xt_iclass_wsr_ptevaddr, + ICLASS_xt_iclass_rsr_ptevaddr, + ICLASS_xt_iclass_xsr_ptevaddr, + ICLASS_xt_iclass_rsr_rasid, + ICLASS_xt_iclass_wsr_rasid, + ICLASS_xt_iclass_xsr_rasid, + ICLASS_xt_iclass_rsr_itlbcfg, + ICLASS_xt_iclass_wsr_itlbcfg, + ICLASS_xt_iclass_xsr_itlbcfg, + ICLASS_xt_iclass_rsr_dtlbcfg, + ICLASS_xt_iclass_wsr_dtlbcfg, + ICLASS_xt_iclass_xsr_dtlbcfg, + ICLASS_xt_iclass_idtlb, + ICLASS_xt_iclass_rdtlb, + ICLASS_xt_iclass_wdtlb, + ICLASS_xt_iclass_iitlb, + ICLASS_xt_iclass_ritlb, + ICLASS_xt_iclass_witlb, + ICLASS_xt_iclass_ldpte, + ICLASS_xt_iclass_hwwitlba, + ICLASS_xt_iclass_hwwdtlba, + ICLASS_xt_iclass_rsr_cpenable, + ICLASS_xt_iclass_wsr_cpenable, + ICLASS_xt_iclass_xsr_cpenable, + ICLASS_xt_iclass_clamp, + ICLASS_xt_iclass_minmax, + ICLASS_xt_iclass_nsa, + ICLASS_xt_iclass_sx, + ICLASS_xt_iclass_l32ai, + ICLASS_xt_iclass_s32ri, + ICLASS_xt_iclass_s32c1i, + ICLASS_xt_iclass_rsr_scompare1, + ICLASS_xt_iclass_wsr_scompare1, + ICLASS_xt_iclass_xsr_scompare1, + ICLASS_xt_iclass_rsr_atomctl, + ICLASS_xt_iclass_wsr_atomctl, + ICLASS_xt_iclass_xsr_atomctl, + ICLASS_xt_iclass_rer, + ICLASS_xt_iclass_wer, + ICLASS_rur_ae_ovf_sar, + ICLASS_wur_ae_ovf_sar, + ICLASS_rur_ae_bithead, + ICLASS_wur_ae_bithead, + ICLASS_rur_ae_ts_fts_bu_bp, + ICLASS_wur_ae_ts_fts_bu_bp, + ICLASS_rur_ae_sd_no, + ICLASS_wur_ae_sd_no, + ICLASS_ae_iclass_rur_ae_overflow, + ICLASS_ae_iclass_wur_ae_overflow, + ICLASS_ae_iclass_rur_ae_sar, + ICLASS_ae_iclass_wur_ae_sar, + ICLASS_ae_iclass_rur_ae_bitptr, + ICLASS_ae_iclass_wur_ae_bitptr, + ICLASS_ae_iclass_rur_ae_bitsused, + ICLASS_ae_iclass_wur_ae_bitsused, + ICLASS_ae_iclass_rur_ae_tablesize, + ICLASS_ae_iclass_wur_ae_tablesize, + ICLASS_ae_iclass_rur_ae_first_ts, + ICLASS_ae_iclass_wur_ae_first_ts, + ICLASS_ae_iclass_rur_ae_nextoffset, + ICLASS_ae_iclass_wur_ae_nextoffset, + ICLASS_ae_iclass_rur_ae_searchdone, + ICLASS_ae_iclass_wur_ae_searchdone, + ICLASS_ae_iclass_lp16f_i, + ICLASS_ae_iclass_lp16f_iu, + ICLASS_ae_iclass_lp16f_x, + ICLASS_ae_iclass_lp16f_xu, + ICLASS_ae_iclass_lp24_i, + ICLASS_ae_iclass_lp24_iu, + ICLASS_ae_iclass_lp24_x, + ICLASS_ae_iclass_lp24_xu, + ICLASS_ae_iclass_lp24f_i, + ICLASS_ae_iclass_lp24f_iu, + ICLASS_ae_iclass_lp24f_x, + ICLASS_ae_iclass_lp24f_xu, + ICLASS_ae_iclass_lp16x2f_i, + ICLASS_ae_iclass_lp16x2f_iu, + ICLASS_ae_iclass_lp16x2f_x, + ICLASS_ae_iclass_lp16x2f_xu, + ICLASS_ae_iclass_lp24x2f_i, + ICLASS_ae_iclass_lp24x2f_iu, + ICLASS_ae_iclass_lp24x2f_x, + ICLASS_ae_iclass_lp24x2f_xu, + ICLASS_ae_iclass_lp24x2_i, + ICLASS_ae_iclass_lp24x2_iu, + ICLASS_ae_iclass_lp24x2_x, + ICLASS_ae_iclass_lp24x2_xu, + ICLASS_ae_iclass_sp16x2f_i, + ICLASS_ae_iclass_sp16x2f_iu, + ICLASS_ae_iclass_sp16x2f_x, + ICLASS_ae_iclass_sp16x2f_xu, + ICLASS_ae_iclass_sp24x2s_i, + ICLASS_ae_iclass_sp24x2s_iu, + ICLASS_ae_iclass_sp24x2s_x, + ICLASS_ae_iclass_sp24x2s_xu, + ICLASS_ae_iclass_sp24x2f_i, + ICLASS_ae_iclass_sp24x2f_iu, + ICLASS_ae_iclass_sp24x2f_x, + ICLASS_ae_iclass_sp24x2f_xu, + ICLASS_ae_iclass_sp16f_l_i, + ICLASS_ae_iclass_sp16f_l_iu, + ICLASS_ae_iclass_sp16f_l_x, + ICLASS_ae_iclass_sp16f_l_xu, + ICLASS_ae_iclass_sp24s_l_i, + ICLASS_ae_iclass_sp24s_l_iu, + ICLASS_ae_iclass_sp24s_l_x, + ICLASS_ae_iclass_sp24s_l_xu, + ICLASS_ae_iclass_sp24f_l_i, + ICLASS_ae_iclass_sp24f_l_iu, + ICLASS_ae_iclass_sp24f_l_x, + ICLASS_ae_iclass_sp24f_l_xu, + ICLASS_ae_iclass_lq56_i, + ICLASS_ae_iclass_lq56_iu, + ICLASS_ae_iclass_lq56_x, + ICLASS_ae_iclass_lq56_xu, + ICLASS_ae_iclass_lq32f_i, + ICLASS_ae_iclass_lq32f_iu, + ICLASS_ae_iclass_lq32f_x, + ICLASS_ae_iclass_lq32f_xu, + ICLASS_ae_iclass_sq56s_i, + ICLASS_ae_iclass_sq56s_iu, + ICLASS_ae_iclass_sq56s_x, + ICLASS_ae_iclass_sq56s_xu, + ICLASS_ae_iclass_sq32f_i, + ICLASS_ae_iclass_sq32f_iu, + ICLASS_ae_iclass_sq32f_x, + ICLASS_ae_iclass_sq32f_xu, + ICLASS_ae_iclass_zerop48, + ICLASS_ae_iclass_movp48, + ICLASS_ae_iclass_selp24_ll, + ICLASS_ae_iclass_selp24_lh, + ICLASS_ae_iclass_selp24_hl, + ICLASS_ae_iclass_selp24_hh, + ICLASS_ae_iclass_movtp24x2, + ICLASS_ae_iclass_movfp24x2, + ICLASS_ae_iclass_movtp48, + ICLASS_ae_iclass_movfp48, + ICLASS_ae_iclass_movpa24x2, + ICLASS_ae_iclass_truncp24a32x2, + ICLASS_ae_iclass_cvta32p24_l, + ICLASS_ae_iclass_cvta32p24_h, + ICLASS_ae_iclass_cvtp24a16x2_ll, + ICLASS_ae_iclass_cvtp24a16x2_lh, + ICLASS_ae_iclass_cvtp24a16x2_hl, + ICLASS_ae_iclass_cvtp24a16x2_hh, + ICLASS_ae_iclass_truncp24q48x2, + ICLASS_ae_iclass_truncp16, + ICLASS_ae_iclass_roundsp24q48sym, + ICLASS_ae_iclass_roundsp24q48asym, + ICLASS_ae_iclass_roundsp16q48sym, + ICLASS_ae_iclass_roundsp16q48asym, + ICLASS_ae_iclass_roundsp16sym, + ICLASS_ae_iclass_roundsp16asym, + ICLASS_ae_iclass_zeroq56, + ICLASS_ae_iclass_movq56, + ICLASS_ae_iclass_movtq56, + ICLASS_ae_iclass_movfq56, + ICLASS_ae_iclass_cvtq48a32s, + ICLASS_ae_iclass_cvtq48p24s_l, + ICLASS_ae_iclass_cvtq48p24s_h, + ICLASS_ae_iclass_satq48s, + ICLASS_ae_iclass_truncq32, + ICLASS_ae_iclass_roundsq32sym, + ICLASS_ae_iclass_roundsq32asym, + ICLASS_ae_iclass_trunca32q48, + ICLASS_ae_iclass_movap24s_l, + ICLASS_ae_iclass_movap24s_h, + ICLASS_ae_iclass_trunca16p24s_l, + ICLASS_ae_iclass_trunca16p24s_h, + ICLASS_ae_iclass_addp24, + ICLASS_ae_iclass_subp24, + ICLASS_ae_iclass_negp24, + ICLASS_ae_iclass_absp24, + ICLASS_ae_iclass_maxp24s, + ICLASS_ae_iclass_minp24s, + ICLASS_ae_iclass_maxbp24s, + ICLASS_ae_iclass_minbp24s, + ICLASS_ae_iclass_addsp24s, + ICLASS_ae_iclass_subsp24s, + ICLASS_ae_iclass_negsp24s, + ICLASS_ae_iclass_abssp24s, + ICLASS_ae_iclass_andp48, + ICLASS_ae_iclass_nandp48, + ICLASS_ae_iclass_orp48, + ICLASS_ae_iclass_xorp48, + ICLASS_ae_iclass_ltp24s, + ICLASS_ae_iclass_lep24s, + ICLASS_ae_iclass_eqp24, + ICLASS_ae_iclass_addq56, + ICLASS_ae_iclass_subq56, + ICLASS_ae_iclass_negq56, + ICLASS_ae_iclass_absq56, + ICLASS_ae_iclass_maxq56s, + ICLASS_ae_iclass_minq56s, + ICLASS_ae_iclass_maxbq56s, + ICLASS_ae_iclass_minbq56s, + ICLASS_ae_iclass_addsq56s, + ICLASS_ae_iclass_subsq56s, + ICLASS_ae_iclass_negsq56s, + ICLASS_ae_iclass_abssq56s, + ICLASS_ae_iclass_andq56, + ICLASS_ae_iclass_nandq56, + ICLASS_ae_iclass_orq56, + ICLASS_ae_iclass_xorq56, + ICLASS_ae_iclass_sllip24, + ICLASS_ae_iclass_srlip24, + ICLASS_ae_iclass_sraip24, + ICLASS_ae_iclass_sllsp24, + ICLASS_ae_iclass_srlsp24, + ICLASS_ae_iclass_srasp24, + ICLASS_ae_iclass_sllisp24s, + ICLASS_ae_iclass_sllssp24s, + ICLASS_ae_iclass_slliq56, + ICLASS_ae_iclass_srliq56, + ICLASS_ae_iclass_sraiq56, + ICLASS_ae_iclass_sllsq56, + ICLASS_ae_iclass_srlsq56, + ICLASS_ae_iclass_srasq56, + ICLASS_ae_iclass_sllaq56, + ICLASS_ae_iclass_srlaq56, + ICLASS_ae_iclass_sraaq56, + ICLASS_ae_iclass_sllisq56s, + ICLASS_ae_iclass_sllssq56s, + ICLASS_ae_iclass_sllasq56s, + ICLASS_ae_iclass_ltq56s, + ICLASS_ae_iclass_leq56s, + ICLASS_ae_iclass_eqq56, + ICLASS_ae_iclass_nsaq56s, + ICLASS_ae_iclass_mulfs32p16s_ll, + ICLASS_ae_iclass_mulfp24s_ll, + ICLASS_ae_iclass_mulp24s_ll, + ICLASS_ae_iclass_mulfs32p16s_lh, + ICLASS_ae_iclass_mulfp24s_lh, + ICLASS_ae_iclass_mulp24s_lh, + ICLASS_ae_iclass_mulfs32p16s_hl, + ICLASS_ae_iclass_mulfp24s_hl, + ICLASS_ae_iclass_mulp24s_hl, + ICLASS_ae_iclass_mulfs32p16s_hh, + ICLASS_ae_iclass_mulfp24s_hh, + ICLASS_ae_iclass_mulp24s_hh, + ICLASS_ae_iclass_mulafs32p16s_ll, + ICLASS_ae_iclass_mulafp24s_ll, + ICLASS_ae_iclass_mulap24s_ll, + ICLASS_ae_iclass_mulafs32p16s_lh, + ICLASS_ae_iclass_mulafp24s_lh, + ICLASS_ae_iclass_mulap24s_lh, + ICLASS_ae_iclass_mulafs32p16s_hl, + ICLASS_ae_iclass_mulafp24s_hl, + ICLASS_ae_iclass_mulap24s_hl, + ICLASS_ae_iclass_mulafs32p16s_hh, + ICLASS_ae_iclass_mulafp24s_hh, + ICLASS_ae_iclass_mulap24s_hh, + ICLASS_ae_iclass_mulsfs32p16s_ll, + ICLASS_ae_iclass_mulsfp24s_ll, + ICLASS_ae_iclass_mulsp24s_ll, + ICLASS_ae_iclass_mulsfs32p16s_lh, + ICLASS_ae_iclass_mulsfp24s_lh, + ICLASS_ae_iclass_mulsp24s_lh, + ICLASS_ae_iclass_mulsfs32p16s_hl, + ICLASS_ae_iclass_mulsfp24s_hl, + ICLASS_ae_iclass_mulsp24s_hl, + ICLASS_ae_iclass_mulsfs32p16s_hh, + ICLASS_ae_iclass_mulsfp24s_hh, + ICLASS_ae_iclass_mulsp24s_hh, + ICLASS_ae_iclass_mulafs56p24s_ll, + ICLASS_ae_iclass_mulas56p24s_ll, + ICLASS_ae_iclass_mulafs56p24s_lh, + ICLASS_ae_iclass_mulas56p24s_lh, + ICLASS_ae_iclass_mulafs56p24s_hl, + ICLASS_ae_iclass_mulas56p24s_hl, + ICLASS_ae_iclass_mulafs56p24s_hh, + ICLASS_ae_iclass_mulas56p24s_hh, + ICLASS_ae_iclass_mulsfs56p24s_ll, + ICLASS_ae_iclass_mulss56p24s_ll, + ICLASS_ae_iclass_mulsfs56p24s_lh, + ICLASS_ae_iclass_mulss56p24s_lh, + ICLASS_ae_iclass_mulsfs56p24s_hl, + ICLASS_ae_iclass_mulss56p24s_hl, + ICLASS_ae_iclass_mulsfs56p24s_hh, + ICLASS_ae_iclass_mulss56p24s_hh, + ICLASS_ae_iclass_mulfq32sp16s_l, + ICLASS_ae_iclass_mulfq32sp16s_h, + ICLASS_ae_iclass_mulfq32sp16u_l, + ICLASS_ae_iclass_mulfq32sp16u_h, + ICLASS_ae_iclass_mulq32sp16s_l, + ICLASS_ae_iclass_mulq32sp16s_h, + ICLASS_ae_iclass_mulq32sp16u_l, + ICLASS_ae_iclass_mulq32sp16u_h, + ICLASS_ae_iclass_mulafq32sp16s_l, + ICLASS_ae_iclass_mulafq32sp16s_h, + ICLASS_ae_iclass_mulafq32sp16u_l, + ICLASS_ae_iclass_mulafq32sp16u_h, + ICLASS_ae_iclass_mulaq32sp16s_l, + ICLASS_ae_iclass_mulaq32sp16s_h, + ICLASS_ae_iclass_mulaq32sp16u_l, + ICLASS_ae_iclass_mulaq32sp16u_h, + ICLASS_ae_iclass_mulsfq32sp16s_l, + ICLASS_ae_iclass_mulsfq32sp16s_h, + ICLASS_ae_iclass_mulsfq32sp16u_l, + ICLASS_ae_iclass_mulsfq32sp16u_h, + ICLASS_ae_iclass_mulsq32sp16s_l, + ICLASS_ae_iclass_mulsq32sp16s_h, + ICLASS_ae_iclass_mulsq32sp16u_l, + ICLASS_ae_iclass_mulsq32sp16u_h, + ICLASS_ae_iclass_mulzaaq32sp16s_ll, + ICLASS_ae_iclass_mulzaafq32sp16s_ll, + ICLASS_ae_iclass_mulzaaq32sp16u_ll, + ICLASS_ae_iclass_mulzaafq32sp16u_ll, + ICLASS_ae_iclass_mulzaaq32sp16s_hh, + ICLASS_ae_iclass_mulzaafq32sp16s_hh, + ICLASS_ae_iclass_mulzaaq32sp16u_hh, + ICLASS_ae_iclass_mulzaafq32sp16u_hh, + ICLASS_ae_iclass_mulzaaq32sp16s_lh, + ICLASS_ae_iclass_mulzaafq32sp16s_lh, + ICLASS_ae_iclass_mulzaaq32sp16u_lh, + ICLASS_ae_iclass_mulzaafq32sp16u_lh, + ICLASS_ae_iclass_mulzasq32sp16s_ll, + ICLASS_ae_iclass_mulzasfq32sp16s_ll, + ICLASS_ae_iclass_mulzasq32sp16u_ll, + ICLASS_ae_iclass_mulzasfq32sp16u_ll, + ICLASS_ae_iclass_mulzasq32sp16s_hh, + ICLASS_ae_iclass_mulzasfq32sp16s_hh, + ICLASS_ae_iclass_mulzasq32sp16u_hh, + ICLASS_ae_iclass_mulzasfq32sp16u_hh, + ICLASS_ae_iclass_mulzasq32sp16s_lh, + ICLASS_ae_iclass_mulzasfq32sp16s_lh, + ICLASS_ae_iclass_mulzasq32sp16u_lh, + ICLASS_ae_iclass_mulzasfq32sp16u_lh, + ICLASS_ae_iclass_mulzsaq32sp16s_ll, + ICLASS_ae_iclass_mulzsafq32sp16s_ll, + ICLASS_ae_iclass_mulzsaq32sp16u_ll, + ICLASS_ae_iclass_mulzsafq32sp16u_ll, + ICLASS_ae_iclass_mulzsaq32sp16s_hh, + ICLASS_ae_iclass_mulzsafq32sp16s_hh, + ICLASS_ae_iclass_mulzsaq32sp16u_hh, + ICLASS_ae_iclass_mulzsafq32sp16u_hh, + ICLASS_ae_iclass_mulzsaq32sp16s_lh, + ICLASS_ae_iclass_mulzsafq32sp16s_lh, + ICLASS_ae_iclass_mulzsaq32sp16u_lh, + ICLASS_ae_iclass_mulzsafq32sp16u_lh, + ICLASS_ae_iclass_mulzssq32sp16s_ll, + ICLASS_ae_iclass_mulzssfq32sp16s_ll, + ICLASS_ae_iclass_mulzssq32sp16u_ll, + ICLASS_ae_iclass_mulzssfq32sp16u_ll, + ICLASS_ae_iclass_mulzssq32sp16s_hh, + ICLASS_ae_iclass_mulzssfq32sp16s_hh, + ICLASS_ae_iclass_mulzssq32sp16u_hh, + ICLASS_ae_iclass_mulzssfq32sp16u_hh, + ICLASS_ae_iclass_mulzssq32sp16s_lh, + ICLASS_ae_iclass_mulzssfq32sp16s_lh, + ICLASS_ae_iclass_mulzssq32sp16u_lh, + ICLASS_ae_iclass_mulzssfq32sp16u_lh, + ICLASS_ae_iclass_mulzaafp24s_hh_ll, + ICLASS_ae_iclass_mulzaap24s_hh_ll, + ICLASS_ae_iclass_mulzaafp24s_hl_lh, + ICLASS_ae_iclass_mulzaap24s_hl_lh, + ICLASS_ae_iclass_mulzasfp24s_hh_ll, + ICLASS_ae_iclass_mulzasp24s_hh_ll, + ICLASS_ae_iclass_mulzasfp24s_hl_lh, + ICLASS_ae_iclass_mulzasp24s_hl_lh, + ICLASS_ae_iclass_mulzsafp24s_hh_ll, + ICLASS_ae_iclass_mulzsap24s_hh_ll, + ICLASS_ae_iclass_mulzsafp24s_hl_lh, + ICLASS_ae_iclass_mulzsap24s_hl_lh, + ICLASS_ae_iclass_mulzssfp24s_hh_ll, + ICLASS_ae_iclass_mulzssp24s_hh_ll, + ICLASS_ae_iclass_mulzssfp24s_hl_lh, + ICLASS_ae_iclass_mulzssp24s_hl_lh, + ICLASS_ae_iclass_mulaafp24s_hh_ll, + ICLASS_ae_iclass_mulaap24s_hh_ll, + ICLASS_ae_iclass_mulaafp24s_hl_lh, + ICLASS_ae_iclass_mulaap24s_hl_lh, + ICLASS_ae_iclass_mulasfp24s_hh_ll, + ICLASS_ae_iclass_mulasp24s_hh_ll, + ICLASS_ae_iclass_mulasfp24s_hl_lh, + ICLASS_ae_iclass_mulasp24s_hl_lh, + ICLASS_ae_iclass_mulsafp24s_hh_ll, + ICLASS_ae_iclass_mulsap24s_hh_ll, + ICLASS_ae_iclass_mulsafp24s_hl_lh, + ICLASS_ae_iclass_mulsap24s_hl_lh, + ICLASS_ae_iclass_mulssfp24s_hh_ll, + ICLASS_ae_iclass_mulssp24s_hh_ll, + ICLASS_ae_iclass_mulssfp24s_hl_lh, + ICLASS_ae_iclass_mulssp24s_hl_lh, + ICLASS_ae_iclass_sha32, + ICLASS_ae_iclass_vldl32t, + ICLASS_ae_iclass_vldl16t, + ICLASS_ae_iclass_vldl16c, + ICLASS_ae_iclass_vldsht, + ICLASS_ae_iclass_lb, + ICLASS_ae_iclass_lbi, + ICLASS_ae_iclass_lbk, + ICLASS_ae_iclass_lbki, + ICLASS_ae_iclass_db, + ICLASS_ae_iclass_dbi, + ICLASS_ae_iclass_vlel32t, + ICLASS_ae_iclass_vlel16t, + ICLASS_ae_iclass_sb, + ICLASS_ae_iclass_sbi, + ICLASS_ae_iclass_vles16c, + ICLASS_ae_iclass_sbf +}; + + +/* Opcode encodings. */ + +static void +Opcode_excw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2080; +} + +static void +Opcode_rfe_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000; +} + +static void +Opcode_rfde_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3200; +} + +static void +Opcode_syscall_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5000; +} + +static void +Opcode_simcall_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5100; +} + +static void +Opcode_call12_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x35; +} + +static void +Opcode_call8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x25; +} + +static void +Opcode_call4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15; +} + +static void +Opcode_callx12_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf0; +} + +static void +Opcode_callx8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe0; +} + +static void +Opcode_callx4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd0; +} + +static void +Opcode_entry_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x36; +} + +static void +Opcode_movsp_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1000; +} + +static void +Opcode_rotw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x408000; +} + +static void +Opcode_retw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x90; +} + +static void +Opcode_retw_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf01d; +} + +static void +Opcode_rfwo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3400; +} + +static void +Opcode_rfwu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3500; +} + +static void +Opcode_l32e_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x90000; +} + +static void +Opcode_s32e_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x490000; +} + +static void +Opcode_rsr_windowbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x34800; +} + +static void +Opcode_wsr_windowbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x134800; +} + +static void +Opcode_xsr_windowbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x614800; +} + +static void +Opcode_rsr_windowstart_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x34900; +} + +static void +Opcode_wsr_windowstart_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x134900; +} + +static void +Opcode_xsr_windowstart_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x614900; +} + +static void +Opcode_add_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa; +} + +static void +Opcode_addi_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb; +} + +static void +Opcode_beqz_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8c; +} + +static void +Opcode_bnez_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xcc; +} + +static void +Opcode_ill_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf06d; +} + +static void +Opcode_l32i_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8; +} + +static void +Opcode_mov_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd; +} + +static void +Opcode_movi_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc; +} + +static void +Opcode_nop_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf03d; +} + +static void +Opcode_ret_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf00d; +} + +static void +Opcode_s32i_n_Slot_inst16a_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9; +} + +static void +Opcode_rur_threadptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe30e70; +} + +static void +Opcode_wur_threadptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf3e700; +} + +static void +Opcode_addi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc002; +} + +static void +Opcode_addi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200040; +} + +static void +Opcode_addmi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd002; +} + +static void +Opcode_addmi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200080; +} + +static void +Opcode_add_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x800000; +} + +static void +Opcode_add_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b2000; +} + +static void +Opcode_sub_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc00000; +} + +static void +Opcode_sub_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ca000; +} + +static void +Opcode_addx2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x900000; +} + +static void +Opcode_addx2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b4000; +} + +static void +Opcode_addx4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa00000; +} + +static void +Opcode_addx4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b8000; +} + +static void +Opcode_addx8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb00000; +} + +static void +Opcode_addx8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b3000; +} + +static void +Opcode_subx2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd00000; +} + +static void +Opcode_subx2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1cc000; +} + +static void +Opcode_subx4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe00000; +} + +static void +Opcode_subx4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1cb000; +} + +static void +Opcode_subx8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf00000; +} + +static void +Opcode_subx8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1cd000; +} + +static void +Opcode_and_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100000; +} + +static void +Opcode_and_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b5000; +} + +static void +Opcode_or_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200000; +} + +static void +Opcode_or_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e0000; +} + +static void +Opcode_xor_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300000; +} + +static void +Opcode_xor_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ce000; +} + +static void +Opcode_beqi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x26; +} + +static void +Opcode_beqi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300000; +} + +static void +Opcode_bnei_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x66; +} + +static void +Opcode_bnei_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300003; +} + +static void +Opcode_bgei_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe6; +} + +static void +Opcode_bgei_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300001; +} + +static void +Opcode_blti_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa6; +} + +static void +Opcode_blti_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300004; +} + +static void +Opcode_bbci_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6007; +} + +static void +Opcode_bbci_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200000; +} + +static void +Opcode_bbsi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe007; +} + +static void +Opcode_bbsi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200020; +} + +static void +Opcode_bgeui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf6; +} + +static void +Opcode_bgeui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300002; +} + +static void +Opcode_bltui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb6; +} + +static void +Opcode_bltui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300008; +} + +static void +Opcode_beq_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1007; +} + +static void +Opcode_beq_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000a0; +} + +static void +Opcode_bne_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9007; +} + +static void +Opcode_bne_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400000; +} + +static void +Opcode_bge_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa007; +} + +static void +Opcode_bge_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000c0; +} + +static void +Opcode_blt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2007; +} + +static void +Opcode_blt_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000d0; +} + +static void +Opcode_bgeu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb007; +} + +static void +Opcode_bgeu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000b0; +} + +static void +Opcode_bltu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3007; +} + +static void +Opcode_bltu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000e0; +} + +static void +Opcode_bany_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8007; +} + +static void +Opcode_bany_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200060; +} + +static void +Opcode_bnone_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7; +} + +static void +Opcode_bnone_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400010; +} + +static void +Opcode_ball_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4007; +} + +static void +Opcode_ball_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200050; +} + +static void +Opcode_bnall_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc007; +} + +static void +Opcode_bnall_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000f0; +} + +static void +Opcode_bbc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5007; +} + +static void +Opcode_bbc_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200070; +} + +static void +Opcode_bbs_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd007; +} + +static void +Opcode_bbs_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x200090; +} + +static void +Opcode_beqz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16; +} + +static void +Opcode_beqz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x180000; +} + +static void +Opcode_bnez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x56; +} + +static void +Opcode_bnez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x190000; +} + +static void +Opcode_bgez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd6; +} + +static void +Opcode_bgez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x160000; +} + +static void +Opcode_bltz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x96; +} + +static void +Opcode_bltz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x170000; +} + +static void +Opcode_call0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5; +} + +static void +Opcode_callx0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc0; +} + +static void +Opcode_extui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000; +} + +static void +Opcode_extui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x140000; +} + +static void +Opcode_ill_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0; +} + +static void +Opcode_j_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6; +} + +static void +Opcode_j_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x100000; +} + +static void +Opcode_jx_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa0; +} + +static void +Opcode_jx_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee031; +} + +static void +Opcode_l16ui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1002; +} + +static void +Opcode_l16ui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400040; +} + +static void +Opcode_l16si_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9002; +} + +static void +Opcode_l16si_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400020; +} + +static void +Opcode_l32i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2002; +} + +static void +Opcode_l32i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400080; +} + +static void +Opcode_l32r_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1; +} + +static void +Opcode_l32r_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500000; +} + +static void +Opcode_l8ui_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2; +} + +static void +Opcode_l8ui_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400030; +} + +static void +Opcode_loop_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8076; +} + +static void +Opcode_loopnez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9076; +} + +static void +Opcode_loopgtz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa076; +} + +static void +Opcode_movi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa002; +} + +static void +Opcode_movi_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1a0000; +} + +static void +Opcode_moveqz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x830000; +} + +static void +Opcode_moveqz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1be000; +} + +static void +Opcode_movnez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x930000; +} + +static void +Opcode_movnez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c8000; +} + +static void +Opcode_movltz_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa30000; +} + +static void +Opcode_movltz_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c4000; +} + +static void +Opcode_movgez_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb30000; +} + +static void +Opcode_movgez_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c2000; +} + +static void +Opcode_neg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600000; +} + +static void +Opcode_neg_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1d00; +} + +static void +Opcode_abs_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x600100; +} + +static void +Opcode_abs_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1c00; +} + +static void +Opcode_nop_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20f0; +} + +static void +Opcode_nop_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16105; +} + +static void +Opcode_nop_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee0b1; +} + +static void +Opcode_ret_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x80; +} + +static void +Opcode_s16i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5002; +} + +static void +Opcode_s16i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400050; +} + +static void +Opcode_s32i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6002; +} + +static void +Opcode_s32i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400060; +} + +static void +Opcode_s8i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4002; +} + +static void +Opcode_s8i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400070; +} + +static void +Opcode_ssr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x400000; +} + +static void +Opcode_ssr_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee071; +} + +static void +Opcode_ssl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x401000; +} + +static void +Opcode_ssl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee038; +} + +static void +Opcode_ssa8l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x402000; +} + +static void +Opcode_ssa8l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee034; +} + +static void +Opcode_ssa8b_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x403000; +} + +static void +Opcode_ssa8b_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee032; +} + +static void +Opcode_ssai_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x404000; +} + +static void +Opcode_ssai_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ef0a0; +} + +static void +Opcode_sll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa10000; +} + +static void +Opcode_sll_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f5003; +} + +static void +Opcode_src_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x810000; +} + +static void +Opcode_src_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c7000; +} + +static void +Opcode_srl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x910000; +} + +static void +Opcode_srl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1f00; +} + +static void +Opcode_sra_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb10000; +} + +static void +Opcode_sra_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1e00; +} + +static void +Opcode_slli_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10000; +} + +static void +Opcode_slli_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c0000; +} + +static void +Opcode_srai_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x210000; +} + +static void +Opcode_srai_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b0000; +} + +static void +Opcode_srli_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x410000; +} + +static void +Opcode_srli_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c9000; +} + +static void +Opcode_memw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20c0; +} + +static void +Opcode_extw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20d0; +} + +static void +Opcode_isync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000; +} + +static void +Opcode_rsync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2010; +} + +static void +Opcode_esync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2020; +} + +static void +Opcode_dsync_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2030; +} + +static void +Opcode_rsil_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000; +} + +static void +Opcode_rsr_lend_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30100; +} + +static void +Opcode_wsr_lend_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130100; +} + +static void +Opcode_xsr_lend_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610100; +} + +static void +Opcode_rsr_lcount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30200; +} + +static void +Opcode_wsr_lcount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130200; +} + +static void +Opcode_xsr_lcount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610200; +} + +static void +Opcode_rsr_lbeg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30000; +} + +static void +Opcode_wsr_lbeg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130000; +} + +static void +Opcode_xsr_lbeg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610000; +} + +static void +Opcode_rsr_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30300; +} + +static void +Opcode_wsr_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130300; +} + +static void +Opcode_xsr_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610300; +} + +static void +Opcode_rsr_litbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30500; +} + +static void +Opcode_wsr_litbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130500; +} + +static void +Opcode_xsr_litbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610500; +} + +static void +Opcode_rsr_176_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3b000; +} + +static void +Opcode_wsr_176_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13b000; +} + +static void +Opcode_rsr_208_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3d000; +} + +static void +Opcode_rsr_ps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e600; +} + +static void +Opcode_wsr_ps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e600; +} + +static void +Opcode_xsr_ps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61e600; +} + +static void +Opcode_rsr_epc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3b100; +} + +static void +Opcode_wsr_epc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13b100; +} + +static void +Opcode_xsr_epc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61b100; +} + +static void +Opcode_rsr_excsave1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3d100; +} + +static void +Opcode_wsr_excsave1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13d100; +} + +static void +Opcode_xsr_excsave1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61d100; +} + +static void +Opcode_rsr_epc2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3b200; +} + +static void +Opcode_wsr_epc2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13b200; +} + +static void +Opcode_xsr_epc2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61b200; +} + +static void +Opcode_rsr_excsave2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3d200; +} + +static void +Opcode_wsr_excsave2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13d200; +} + +static void +Opcode_xsr_excsave2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61d200; +} + +static void +Opcode_rsr_eps2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3c200; +} + +static void +Opcode_wsr_eps2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13c200; +} + +static void +Opcode_xsr_eps2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61c200; +} + +static void +Opcode_rsr_excvaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3ee00; +} + +static void +Opcode_wsr_excvaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13ee00; +} + +static void +Opcode_xsr_excvaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61ee00; +} + +static void +Opcode_rsr_depc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3c000; +} + +static void +Opcode_wsr_depc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13c000; +} + +static void +Opcode_xsr_depc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61c000; +} + +static void +Opcode_rsr_exccause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e800; +} + +static void +Opcode_wsr_exccause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e800; +} + +static void +Opcode_xsr_exccause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61e800; +} + +static void +Opcode_rsr_misc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f400; +} + +static void +Opcode_wsr_misc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13f400; +} + +static void +Opcode_xsr_misc0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61f400; +} + +static void +Opcode_rsr_misc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f500; +} + +static void +Opcode_wsr_misc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13f500; +} + +static void +Opcode_xsr_misc1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61f500; +} + +static void +Opcode_rsr_prid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3eb00; +} + +static void +Opcode_rsr_vecbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e700; +} + +static void +Opcode_wsr_vecbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e700; +} + +static void +Opcode_xsr_vecbase_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61e700; +} + +static void +Opcode_mul16u_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc10000; +} + +static void +Opcode_mul16s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd10000; +} + +static void +Opcode_mull_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x820000; +} + +static void +Opcode_rfi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3010; +} + +static void +Opcode_waiti_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7000; +} + +static void +Opcode_rsr_interrupt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e200; +} + +static void +Opcode_wsr_intset_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e200; +} + +static void +Opcode_wsr_intclear_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e300; +} + +static void +Opcode_rsr_intenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e400; +} + +static void +Opcode_wsr_intenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e400; +} + +static void +Opcode_xsr_intenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61e400; +} + +static void +Opcode_break_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000; +} + +static void +Opcode_break_n_Slot_inst16b_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf02d; +} + +static void +Opcode_rsr_debugcause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e900; +} + +static void +Opcode_wsr_debugcause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e900; +} + +static void +Opcode_xsr_debugcause_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61e900; +} + +static void +Opcode_rsr_icount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3ec00; +} + +static void +Opcode_wsr_icount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13ec00; +} + +static void +Opcode_xsr_icount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61ec00; +} + +static void +Opcode_rsr_icountlevel_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3ed00; +} + +static void +Opcode_wsr_icountlevel_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13ed00; +} + +static void +Opcode_xsr_icountlevel_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61ed00; +} + +static void +Opcode_rsr_ddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x36800; +} + +static void +Opcode_wsr_ddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x136800; +} + +static void +Opcode_xsr_ddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x616800; +} + +static void +Opcode_rfdo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf1e000; +} + +static void +Opcode_rfdd_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf1e010; +} + +static void +Opcode_andb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20000; +} + +static void +Opcode_andb_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b6000; +} + +static void +Opcode_andbc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x120000; +} + +static void +Opcode_andbc_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b7000; +} + +static void +Opcode_orb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x220000; +} + +static void +Opcode_orb_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c3000; +} + +static void +Opcode_orbc_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x320000; +} + +static void +Opcode_orbc_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c5000; +} + +static void +Opcode_xorb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x420000; +} + +static void +Opcode_xorb_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1cf000; +} + +static void +Opcode_any4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x8000; +} + +static void +Opcode_any4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2480; +} + +static void +Opcode_all4_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x9000; +} + +static void +Opcode_all4_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2800; +} + +static void +Opcode_any8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa000; +} + +static void +Opcode_any8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ef060; +} + +static void +Opcode_all8_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb000; +} + +static void +Opcode_all8_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ef020; +} + +static void +Opcode_bf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x76; +} + +static void +Opcode_bf_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300005; +} + +static void +Opcode_bt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1076; +} + +static void +Opcode_bt_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x300006; +} + +static void +Opcode_movf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc30000; +} + +static void +Opcode_movf_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1bf000; +} + +static void +Opcode_movt_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xd30000; +} + +static void +Opcode_movt_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d0000; +} + +static void +Opcode_rsr_br_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30400; +} + +static void +Opcode_wsr_br_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130400; +} + +static void +Opcode_xsr_br_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610400; +} + +static void +Opcode_rsr_ccount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3ea00; +} + +static void +Opcode_wsr_ccount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13ea00; +} + +static void +Opcode_xsr_ccount_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61ea00; +} + +static void +Opcode_rsr_ccompare0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f000; +} + +static void +Opcode_wsr_ccompare0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13f000; +} + +static void +Opcode_xsr_ccompare0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61f000; +} + +static void +Opcode_rsr_ccompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3f100; +} + +static void +Opcode_wsr_ccompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13f100; +} + +static void +Opcode_xsr_ccompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61f100; +} + +static void +Opcode_ipf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x70c2; +} + +static void +Opcode_ihi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x70e2; +} + +static void +Opcode_iii_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x70f2; +} + +static void +Opcode_lict_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf10000; +} + +static void +Opcode_licw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf12000; +} + +static void +Opcode_sict_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf11000; +} + +static void +Opcode_sicw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf13000; +} + +static void +Opcode_dhwb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7042; +} + +static void +Opcode_dhwbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7052; +} + +static void +Opcode_diwb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x47082; +} + +static void +Opcode_diwbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x57082; +} + +static void +Opcode_dhi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7062; +} + +static void +Opcode_dii_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7072; +} + +static void +Opcode_dpfr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7002; +} + +static void +Opcode_dpfw_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7012; +} + +static void +Opcode_dpfro_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7022; +} + +static void +Opcode_dpfwo_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x7032; +} + +static void +Opcode_sdct_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf19000; +} + +static void +Opcode_ldct_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf18000; +} + +static void +Opcode_wsr_ptevaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x135300; +} + +static void +Opcode_rsr_ptevaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x35300; +} + +static void +Opcode_xsr_ptevaddr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x615300; +} + +static void +Opcode_rsr_rasid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x35a00; +} + +static void +Opcode_wsr_rasid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x135a00; +} + +static void +Opcode_xsr_rasid_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x615a00; +} + +static void +Opcode_rsr_itlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x35b00; +} + +static void +Opcode_wsr_itlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x135b00; +} + +static void +Opcode_xsr_itlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x615b00; +} + +static void +Opcode_rsr_dtlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x35c00; +} + +static void +Opcode_wsr_dtlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x135c00; +} + +static void +Opcode_xsr_dtlbcfg_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x615c00; +} + +static void +Opcode_idtlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50c000; +} + +static void +Opcode_pdtlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50d000; +} + +static void +Opcode_rdtlb0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50b000; +} + +static void +Opcode_rdtlb1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50f000; +} + +static void +Opcode_wdtlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50e000; +} + +static void +Opcode_iitlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x504000; +} + +static void +Opcode_pitlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x505000; +} + +static void +Opcode_ritlb0_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x503000; +} + +static void +Opcode_ritlb1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x507000; +} + +static void +Opcode_witlb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x506000; +} + +static void +Opcode_ldpte_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf1f000; +} + +static void +Opcode_hwwitlba_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x501000; +} + +static void +Opcode_hwwdtlba_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x509000; +} + +static void +Opcode_rsr_cpenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3e000; +} + +static void +Opcode_wsr_cpenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x13e000; +} + +static void +Opcode_xsr_cpenable_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x61e000; +} + +static void +Opcode_clamps_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x330000; +} + +static void +Opcode_clamps_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1b9000; +} + +static void +Opcode_min_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x430000; +} + +static void +Opcode_min_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1bb000; +} + +static void +Opcode_max_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x530000; +} + +static void +Opcode_max_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ba000; +} + +static void +Opcode_minu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x630000; +} + +static void +Opcode_minu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1bd000; +} + +static void +Opcode_maxu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x730000; +} + +static void +Opcode_maxu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1bc000; +} + +static void +Opcode_nsa_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40e000; +} + +static void +Opcode_nsau_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40f000; +} + +static void +Opcode_sext_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x230000; +} + +static void +Opcode_sext_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c6000; +} + +static void +Opcode_l32ai_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb002; +} + +static void +Opcode_s32ri_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf002; +} + +static void +Opcode_s32c1i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe002; +} + +static void +Opcode_rsr_scompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30c00; +} + +static void +Opcode_wsr_scompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x130c00; +} + +static void +Opcode_xsr_scompare1_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x610c00; +} + +static void +Opcode_rsr_atomctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x36300; +} + +static void +Opcode_wsr_atomctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x136300; +} + +static void +Opcode_xsr_atomctl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x616300; +} + +static void +Opcode_rer_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x406000; +} + +static void +Opcode_wer_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x407000; +} + +static void +Opcode_rur_ae_ovf_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe30f00; +} + +static void +Opcode_wur_ae_ovf_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf3f000; +} + +static void +Opcode_rur_ae_bithead_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe30f10; +} + +static void +Opcode_wur_ae_bithead_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf3f100; +} + +static void +Opcode_rur_ae_ts_fts_bu_bp_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe30f20; +} + +static void +Opcode_wur_ae_ts_fts_bu_bp_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf3f200; +} + +static void +Opcode_rur_ae_sd_no_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe30f30; +} + +static void +Opcode_wur_ae_sd_no_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf3f300; +} + +static void +Opcode_rur_ae_overflow_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90804; +} + +static void +Opcode_wur_ae_overflow_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca0004; +} + +static void +Opcode_rur_ae_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90904; +} + +static void +Opcode_wur_ae_sar_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca1004; +} + +static void +Opcode_rur_ae_bitptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90a04; +} + +static void +Opcode_wur_ae_bitptr_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca2004; +} + +static void +Opcode_rur_ae_bitsused_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90b04; +} + +static void +Opcode_wur_ae_bitsused_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca3004; +} + +static void +Opcode_rur_ae_tablesize_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90c04; +} + +static void +Opcode_wur_ae_tablesize_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca4004; +} + +static void +Opcode_rur_ae_first_ts_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90d04; +} + +static void +Opcode_wur_ae_first_ts_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca5004; +} + +static void +Opcode_rur_ae_nextoffset_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90e04; +} + +static void +Opcode_wur_ae_nextoffset_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca6004; +} + +static void +Opcode_rur_ae_searchdone_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90f04; +} + +static void +Opcode_wur_ae_searchdone_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca7004; +} + +static void +Opcode_ae_lp16f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d1080; +} + +static void +Opcode_ae_lp16f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa50004; +} + +static void +Opcode_ae_lp16f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d2080; +} + +static void +Opcode_ae_lp16f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa90004; +} + +static void +Opcode_ae_lp16f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d3000; +} + +static void +Opcode_ae_lp16f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xac0004; +} + +static void +Opcode_ae_lp16f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d3080; +} + +static void +Opcode_ae_lp16f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xaf0004; +} + +static void +Opcode_ae_lp24_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d6080; +} + +static void +Opcode_ae_lp24_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa58004; +} + +static void +Opcode_ae_lp24_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d7000; +} + +static void +Opcode_ae_lp24_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa98004; +} + +static void +Opcode_ae_lp24_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d7080; +} + +static void +Opcode_ae_lp24_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xac8004; +} + +static void +Opcode_ae_lp24_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d8080; +} + +static void +Opcode_ae_lp24_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xaf8004; +} + +static void +Opcode_ae_lp24f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d9000; +} + +static void +Opcode_ae_lp24f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa60004; +} + +static void +Opcode_ae_lp24f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1da000; +} + +static void +Opcode_ae_lp24f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xaa0004; +} + +static void +Opcode_ae_lp24f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1dc000; +} + +static void +Opcode_ae_lp24f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xad0004; +} + +static void +Opcode_ae_lp24f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d9080; +} + +static void +Opcode_ae_lp24f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb00004; +} + +static void +Opcode_ae_lp16x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d4080; +} + +static void +Opcode_ae_lp16x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa68004; +} + +static void +Opcode_ae_lp16x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d5000; +} + +static void +Opcode_ae_lp16x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xaa8004; +} + +static void +Opcode_ae_lp16x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d6000; +} + +static void +Opcode_ae_lp16x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xad8004; +} + +static void +Opcode_ae_lp16x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d5080; +} + +static void +Opcode_ae_lp16x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb08004; +} + +static void +Opcode_ae_lp24x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1dd000; +} + +static void +Opcode_ae_lp24x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa70004; +} + +static void +Opcode_ae_lp24x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1de000; +} + +static void +Opcode_ae_lp24x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xab0004; +} + +static void +Opcode_ae_lp24x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1dd080; +} + +static void +Opcode_ae_lp24x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xae0004; +} + +static void +Opcode_ae_lp24x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1de080; +} + +static void +Opcode_ae_lp24x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb10004; +} + +static void +Opcode_ae_lp24x2_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1da080; +} + +static void +Opcode_ae_lp24x2_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa78004; +} + +static void +Opcode_ae_lp24x2_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1db000; +} + +static void +Opcode_ae_lp24x2_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xab8004; +} + +static void +Opcode_ae_lp24x2_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1db080; +} + +static void +Opcode_ae_lp24x2_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xae8004; +} + +static void +Opcode_ae_lp24x2_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1dc080; +} + +static void +Opcode_ae_lp24x2_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb18004; +} + +static void +Opcode_ae_sp16x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e8000; +} + +static void +Opcode_ae_sp16x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb20004; +} + +static void +Opcode_ae_sp16x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f0000; +} + +static void +Opcode_ae_sp16x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb50004; +} + +static void +Opcode_ae_sp16x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e1080; +} + +static void +Opcode_ae_sp16x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb80004; +} + +static void +Opcode_ae_sp16x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e2080; +} + +static void +Opcode_ae_sp16x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbb0004; +} + +static void +Opcode_ae_sp24x2s_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ec000; +} + +static void +Opcode_ae_sp24x2s_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb28004; +} + +static void +Opcode_ae_sp24x2s_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e9080; +} + +static void +Opcode_ae_sp24x2s_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb58004; +} + +static void +Opcode_ae_sp24x2s_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ea080; +} + +static void +Opcode_ae_sp24x2s_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb88004; +} + +static void +Opcode_ae_sp24x2s_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1eb000; +} + +static void +Opcode_ae_sp24x2s_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbb8004; +} + +static void +Opcode_ae_sp24x2f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e7080; +} + +static void +Opcode_ae_sp24x2f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb30004; +} + +static void +Opcode_ae_sp24x2f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e8080; +} + +static void +Opcode_ae_sp24x2f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb60004; +} + +static void +Opcode_ae_sp24x2f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e9000; +} + +static void +Opcode_ae_sp24x2f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb90004; +} + +static void +Opcode_ae_sp24x2f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ea000; +} + +static void +Opcode_ae_sp24x2f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbc0004; +} + +static void +Opcode_ae_sp16f_l_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1df080; +} + +static void +Opcode_ae_sp16f_l_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb38004; +} + +static void +Opcode_ae_sp16f_l_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e1000; +} + +static void +Opcode_ae_sp16f_l_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb68004; +} + +static void +Opcode_ae_sp16f_l_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e2000; +} + +static void +Opcode_ae_sp16f_l_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb98004; +} + +static void +Opcode_ae_sp16f_l_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e4000; +} + +static void +Opcode_ae_sp16f_l_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbc8004; +} + +static void +Opcode_ae_sp24s_l_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e6000; +} + +static void +Opcode_ae_sp24s_l_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb40004; +} + +static void +Opcode_ae_sp24s_l_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e5080; +} + +static void +Opcode_ae_sp24s_l_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb70004; +} + +static void +Opcode_ae_sp24s_l_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e6080; +} + +static void +Opcode_ae_sp24s_l_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xba0004; +} + +static void +Opcode_ae_sp24s_l_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e7000; +} + +static void +Opcode_ae_sp24s_l_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbd0004; +} + +static void +Opcode_ae_sp24f_l_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e3000; +} + +static void +Opcode_ae_sp24f_l_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb48004; +} + +static void +Opcode_ae_sp24f_l_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e3080; +} + +static void +Opcode_ae_sp24f_l_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xb78004; +} + +static void +Opcode_ae_sp24f_l_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e4080; +} + +static void +Opcode_ae_sp24f_l_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xba8004; +} + +static void +Opcode_ae_sp24f_l_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1e5000; +} + +static void +Opcode_ae_sp24f_l_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbd8004; +} + +static void +Opcode_ae_lq56_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ed030; +} + +static void +Opcode_ae_lq56_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc10004; +} + +static void +Opcode_ae_lq56_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee010; +} + +static void +Opcode_ae_lq56_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc12004; +} + +static void +Opcode_ae_lq56_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee020; +} + +static void +Opcode_ae_lq56_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc20004; +} + +static void +Opcode_ae_lq56_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ef000; +} + +static void +Opcode_ae_lq56_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc22004; +} + +static void +Opcode_ae_lq32f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ed000; +} + +static void +Opcode_ae_lq32f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc11004; +} + +static void +Opcode_ae_lq32f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee000; +} + +static void +Opcode_ae_lq32f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc13004; +} + +static void +Opcode_ae_lq32f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ed010; +} + +static void +Opcode_ae_lq32f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc21004; +} + +static void +Opcode_ae_lq32f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ed020; +} + +static void +Opcode_ae_lq32f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc23004; +} + +static void +Opcode_ae_sq56s_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f0080; +} + +static void +Opcode_ae_sq56s_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc30004; +} + +static void +Opcode_ae_sq56s_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f00c0; +} + +static void +Opcode_ae_sq56s_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc38004; +} + +static void +Opcode_ae_sq56s_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3000; +} + +static void +Opcode_ae_sq56s_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc40004; +} + +static void +Opcode_ae_sq56s_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3040; +} + +static void +Opcode_ae_sq56s_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc48004; +} + +static void +Opcode_ae_sq32f_i_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ec080; +} + +static void +Opcode_ae_sq32f_i_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc34004; +} + +static void +Opcode_ae_sq32f_iu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ec0c0; +} + +static void +Opcode_ae_sq32f_iu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc3c004; +} + +static void +Opcode_ae_sq32f_x_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f4000; +} + +static void +Opcode_ae_sq32f_x_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc44004; +} + +static void +Opcode_ae_sq32f_xu_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f8000; +} + +static void +Opcode_ae_sq32f_xu_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc4c004; +} + +static void +Opcode_ae_zerop48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16b88; +} + +static void +Opcode_ae_movp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16808; +} + +static void +Opcode_ae_movp48_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2400; +} + +static void +Opcode_ae_movp48_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90004; +} + +static void +Opcode_ae_selp24_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10780; +} + +static void +Opcode_ae_selp24_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10708; +} + +static void +Opcode_ae_selp24_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10688; +} + +static void +Opcode_ae_selp24_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10700; +} + +static void +Opcode_ae_movtp24x2_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c200; +} + +static void +Opcode_ae_movfp24x2_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c004; +} + +static void +Opcode_ae_movtp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10480; +} + +static void +Opcode_ae_movfp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10400; +} + +static void +Opcode_ae_movpa24x2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1df000; +} + +static void +Opcode_ae_movpa24x2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc00004; +} + +static void +Opcode_ae_truncp24a32x2_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1eb080; +} + +static void +Opcode_ae_truncp24a32x2_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc08004; +} + +static void +Opcode_ae_cvta32p24_l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3081; +} + +static void +Opcode_ae_cvta32p24_l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xcb0004; +} + +static void +Opcode_ae_cvta32p24_h_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3080; +} + +static void +Opcode_ae_cvta32p24_h_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xcb8004; +} + +static void +Opcode_ae_cvtp24a16x2_ll_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d8000; +} + +static void +Opcode_ae_cvtp24a16x2_ll_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbe0004; +} + +static void +Opcode_ae_cvtp24a16x2_lh_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d4000; +} + +static void +Opcode_ae_cvtp24a16x2_lh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbe8004; +} + +static void +Opcode_ae_cvtp24a16x2_hl_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d2000; +} + +static void +Opcode_ae_cvtp24a16x2_hl_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbf0004; +} + +static void +Opcode_ae_cvtp24a16x2_hh_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1d1000; +} + +static void +Opcode_ae_cvtp24a16x2_hh_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xbf8004; +} + +static void +Opcode_ae_truncp24q48x2_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x51000; +} + +static void +Opcode_ae_truncp16_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16b08; +} + +static void +Opcode_ae_roundsp24q48sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16e48; +} + +static void +Opcode_ae_roundsp24q48asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16e28; +} + +static void +Opcode_ae_roundsp16q48sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16e18; +} + +static void +Opcode_ae_roundsp16q48asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16e08; +} + +static void +Opcode_ae_roundsp16sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16908; +} + +static void +Opcode_ae_roundsp16asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16888; +} + +static void +Opcode_ae_zeroq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16085; +} + +static void +Opcode_ae_movq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16007; +} + +static void +Opcode_ae_movq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2500; +} + +static void +Opcode_ae_movq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90414; +} + +static void +Opcode_ae_movtq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f6000; +} + +static void +Opcode_ae_movtq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe50014; +} + +static void +Opcode_ae_movfq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f5000; +} + +static void +Opcode_ae_movfq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe60014; +} + +static void +Opcode_ae_cvtq48a32s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1ee030; +} + +static void +Opcode_ae_cvtq48a32s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe72034; +} + +static void +Opcode_ae_cvtq48p24s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16006; +} + +static void +Opcode_ae_cvtq48p24s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16005; +} + +static void +Opcode_ae_satq48s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50139; +} + +static void +Opcode_ae_truncq32_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16047; +} + +static void +Opcode_ae_roundsq32sym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16027; +} + +static void +Opcode_ae_roundsq32asym_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16017; +} + +static void +Opcode_ae_trunca32q48_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3086; +} + +static void +Opcode_ae_trunca32q48_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe70014; +} + +static void +Opcode_ae_movap24s_l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3084; +} + +static void +Opcode_ae_movap24s_l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc70004; +} + +static void +Opcode_ae_movap24s_h_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3082; +} + +static void +Opcode_ae_movap24s_h_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc78004; +} + +static void +Opcode_ae_trunca16p24s_l_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3083; +} + +static void +Opcode_ae_trunca16p24s_l_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc80004; +} + +static void +Opcode_ae_trunca16p24s_h_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3088; +} + +static void +Opcode_ae_trunca16p24s_h_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc88004; +} + +static void +Opcode_ae_addp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10500; +} + +static void +Opcode_ae_subp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10788; +} + +static void +Opcode_ae_negp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c600; +} + +static void +Opcode_ae_absp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c480; +} + +static void +Opcode_ae_maxp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10580; +} + +static void +Opcode_ae_minp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10588; +} + +static void +Opcode_ae_maxbp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10000; +} + +static void +Opcode_ae_minbp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10200; +} + +static void +Opcode_ae_addsp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10600; +} + +static void +Opcode_ae_subsp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c400; +} + +static void +Opcode_ae_negsp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c488; +} + +static void +Opcode_ae_abssp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c500; +} + +static void +Opcode_ae_andp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10508; +} + +static void +Opcode_ae_nandp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10608; +} + +static void +Opcode_ae_orp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x10680; +} + +static void +Opcode_ae_xorp48_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c408; +} + +static void +Opcode_ae_ltp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c002; +} + +static void +Opcode_ae_lep24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c001; +} + +static void +Opcode_ae_eqp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1c000; +} + +static void +Opcode_ae_addq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x52000; +} + +static void +Opcode_ae_subq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50035; +} + +static void +Opcode_ae_negq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5003c; +} + +static void +Opcode_ae_absq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50039; +} + +static void +Opcode_ae_maxq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50032; +} + +static void +Opcode_ae_minq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50034; +} + +static void +Opcode_ae_maxbq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50000; +} + +static void +Opcode_ae_minbq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50010; +} + +static void +Opcode_ae_addsq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50030; +} + +static void +Opcode_ae_subsq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50036; +} + +static void +Opcode_ae_negsq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x500b9; +} + +static void +Opcode_ae_abssq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x5003a; +} + +static void +Opcode_ae_andq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50031; +} + +static void +Opcode_ae_nandq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50038; +} + +static void +Opcode_ae_orq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50033; +} + +static void +Opcode_ae_xorq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50037; +} + +static void +Opcode_ae_sllip24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x14000; +} + +static void +Opcode_ae_srlip24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15000; +} + +static void +Opcode_ae_sraip24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x14800; +} + +static void +Opcode_ae_sllsp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16a08; +} + +static void +Opcode_ae_srlsp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16a88; +} + +static void +Opcode_ae_srasp24_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16988; +} + +static void +Opcode_ae_sllisp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x18000; +} + +static void +Opcode_ae_sllssp24s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16c08; +} + +static void +Opcode_ae_slliq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1000; +} + +static void +Opcode_ae_slliq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc50004; +} + +static void +Opcode_ae_srliq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1800; +} + +static void +Opcode_ae_srliq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc50404; +} + +static void +Opcode_ae_sraiq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f1400; +} + +static void +Opcode_ae_sraiq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc50804; +} + +static void +Opcode_ae_sllsq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2600; +} + +static void +Opcode_ae_sllsq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90014; +} + +static void +Opcode_ae_srlsq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2504; +} + +static void +Opcode_ae_srlsq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90114; +} + +static void +Opcode_ae_srasq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2502; +} + +static void +Opcode_ae_srasq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90214; +} + +static void +Opcode_ae_sllaq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f5001; +} + +static void +Opcode_ae_sllaq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe10014; +} + +static void +Opcode_ae_srlaq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f5008; +} + +static void +Opcode_ae_srlaq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe20014; +} + +static void +Opcode_ae_sraaq56_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f5004; +} + +static void +Opcode_ae_sraaq56_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe30014; +} + +static void +Opcode_ae_sllisq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2000; +} + +static void +Opcode_ae_sllisq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc50c04; +} + +static void +Opcode_ae_sllssq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f2501; +} + +static void +Opcode_ae_sllssq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc90314; +} + +static void +Opcode_ae_sllasq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f5002; +} + +static void +Opcode_ae_sllasq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe40014; +} + +static void +Opcode_ae_ltq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50800; +} + +static void +Opcode_ae_leq56s_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50040; +} + +static void +Opcode_ae_eqq56_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x50020; +} + +static void +Opcode_ae_nsaq56s_Slot_ae_slot0_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1f3085; +} + +static void +Opcode_ae_nsaq56s_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe74014; +} + +static void +Opcode_ae_mulfs32p16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60101; +} + +static void +Opcode_ae_mulfp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6008b; +} + +static void +Opcode_ae_mulp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60180; +} + +static void +Opcode_ae_mulfs32p16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6008f; +} + +static void +Opcode_ae_mulfp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6008c; +} + +static void +Opcode_ae_mulp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60108; +} + +static void +Opcode_ae_mulfs32p16s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6008e; +} + +static void +Opcode_ae_mulfp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6008a; +} + +static void +Opcode_ae_mulp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60104; +} + +static void +Opcode_ae_mulfs32p16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6008d; +} + +static void +Opcode_ae_mulfp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60089; +} + +static void +Opcode_ae_mulp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60102; +} + +static void +Opcode_ae_mulafs32p16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60006; +} + +static void +Opcode_ae_mulafp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64000; +} + +static void +Opcode_ae_mulap24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000f; +} + +static void +Opcode_ae_mulafs32p16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60005; +} + +static void +Opcode_ae_mulafp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60100; +} + +static void +Opcode_ae_mulap24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000e; +} + +static void +Opcode_ae_mulafs32p16s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60003; +} + +static void +Opcode_ae_mulafp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60080; +} + +static void +Opcode_ae_mulap24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000d; +} + +static void +Opcode_ae_mulafs32p16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x68000; +} + +static void +Opcode_ae_mulafp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60008; +} + +static void +Opcode_ae_mulap24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000b; +} + +static void +Opcode_ae_mulsfs32p16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60181; +} + +static void +Opcode_ae_mulsfp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6010b; +} + +static void +Opcode_ae_mulsp24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60189; +} + +static void +Opcode_ae_mulsfs32p16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6010f; +} + +static void +Opcode_ae_mulsfp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6010c; +} + +static void +Opcode_ae_mulsp24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60187; +} + +static void +Opcode_ae_mulsfs32p16s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6010e; +} + +static void +Opcode_ae_mulsfp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6010a; +} + +static void +Opcode_ae_mulsp24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60186; +} + +static void +Opcode_ae_mulsfs32p16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6010d; +} + +static void +Opcode_ae_mulsfp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60109; +} + +static void +Opcode_ae_mulsp24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60185; +} + +static void +Opcode_ae_mulafs56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000c; +} + +static void +Opcode_ae_mulas56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60088; +} + +static void +Opcode_ae_mulafs56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6000a; +} + +static void +Opcode_ae_mulas56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60084; +} + +static void +Opcode_ae_mulafs56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60009; +} + +static void +Opcode_ae_mulas56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60082; +} + +static void +Opcode_ae_mulafs56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60007; +} + +static void +Opcode_ae_mulas56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60081; +} + +static void +Opcode_ae_mulsfs56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60183; +} + +static void +Opcode_ae_mulss56p24s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6018d; +} + +static void +Opcode_ae_mulsfs56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60188; +} + +static void +Opcode_ae_mulss56p24s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6018b; +} + +static void +Opcode_ae_mulsfs56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60184; +} + +static void +Opcode_ae_mulss56p24s_hl_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6018c; +} + +static void +Opcode_ae_mulsfs56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60182; +} + +static void +Opcode_ae_mulss56p24s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6018a; +} + +static void +Opcode_ae_mulfq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15807; +} + +static void +Opcode_ae_mulfq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15806; +} + +static void +Opcode_ae_mulfq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1580a; +} + +static void +Opcode_ae_mulfq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15809; +} + +static void +Opcode_ae_mulq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1580b; +} + +static void +Opcode_ae_mulq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1580c; +} + +static void +Opcode_ae_mulq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1580e; +} + +static void +Opcode_ae_mulq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1580d; +} + +static void +Opcode_ae_mulafq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15800; +} + +static void +Opcode_ae_mulafq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16000; +} + +static void +Opcode_ae_mulafq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15802; +} + +static void +Opcode_ae_mulafq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15801; +} + +static void +Opcode_ae_mulaq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15808; +} + +static void +Opcode_ae_mulaq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15804; +} + +static void +Opcode_ae_mulaq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15805; +} + +static void +Opcode_ae_mulaq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x15803; +} + +static void +Opcode_ae_mulsfq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16001; +} + +static void +Opcode_ae_mulsfq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x1580f; +} + +static void +Opcode_ae_mulsfq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16004; +} + +static void +Opcode_ae_mulsfq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16002; +} + +static void +Opcode_ae_mulsq32sp16s_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16800; +} + +static void +Opcode_ae_mulsq32sp16s_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16008; +} + +static void +Opcode_ae_mulsq32sp16u_l_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x16003; +} + +static void +Opcode_ae_mulsq32sp16u_h_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x17000; +} + +static void +Opcode_ae_mulzaaq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20007; +} + +static void +Opcode_ae_mulzaafq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20002; +} + +static void +Opcode_ae_mulzaaq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000c; +} + +static void +Opcode_ae_mulzaafq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20003; +} + +static void +Opcode_ae_mulzaaq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20005; +} + +static void +Opcode_ae_mulzaafq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20000; +} + +static void +Opcode_ae_mulzaaq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20009; +} + +static void +Opcode_ae_mulzaafq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20004; +} + +static void +Opcode_ae_mulzaaq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20006; +} + +static void +Opcode_ae_mulzaafq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20001; +} + +static void +Opcode_ae_mulzaaq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000a; +} + +static void +Opcode_ae_mulzaafq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x20008; +} + +static void +Opcode_ae_mulzasq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30008; +} + +static void +Opcode_ae_mulzasfq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000e; +} + +static void +Opcode_ae_mulzasq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30006; +} + +static void +Opcode_ae_mulzasfq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30001; +} + +static void +Opcode_ae_mulzasq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30002; +} + +static void +Opcode_ae_mulzasfq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000b; +} + +static void +Opcode_ae_mulzasq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30003; +} + +static void +Opcode_ae_mulzasfq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000f; +} + +static void +Opcode_ae_mulzasq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30004; +} + +static void +Opcode_ae_mulzasfq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x2000d; +} + +static void +Opcode_ae_mulzasq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30005; +} + +static void +Opcode_ae_mulzasfq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30000; +} + +static void +Opcode_ae_mulzsaq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40000; +} + +static void +Opcode_ae_mulzsafq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000a; +} + +static void +Opcode_ae_mulzsaq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40004; +} + +static void +Opcode_ae_mulzsafq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000d; +} + +static void +Opcode_ae_mulzsaq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000e; +} + +static void +Opcode_ae_mulzsafq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30007; +} + +static void +Opcode_ae_mulzsaq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40001; +} + +static void +Opcode_ae_mulzsafq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000c; +} + +static void +Opcode_ae_mulzsaq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000f; +} + +static void +Opcode_ae_mulzsafq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x30009; +} + +static void +Opcode_ae_mulzsaq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40002; +} + +static void +Opcode_ae_mulzsafq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x3000b; +} + +static void +Opcode_ae_mulzssq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000b; +} + +static void +Opcode_ae_mulzssfq32sp16s_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40005; +} + +static void +Opcode_ae_mulzssq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000f; +} + +static void +Opcode_ae_mulzssfq32sp16u_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40009; +} + +static void +Opcode_ae_mulzssq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000a; +} + +static void +Opcode_ae_mulzssfq32sp16s_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40008; +} + +static void +Opcode_ae_mulzssq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000d; +} + +static void +Opcode_ae_mulzssfq32sp16u_hh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40006; +} + +static void +Opcode_ae_mulzssq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000c; +} + +static void +Opcode_ae_mulzssfq32sp16s_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40003; +} + +static void +Opcode_ae_mulzssq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x4000e; +} + +static void +Opcode_ae_mulzssfq32sp16u_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x40007; +} + +static void +Opcode_ae_mulzaafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64004; +} + +static void +Opcode_ae_mulzaap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64080; +} + +static void +Opcode_ae_mulzaafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64008; +} + +static void +Opcode_ae_mulzaap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64100; +} + +static void +Opcode_ae_mulzasfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64003; +} + +static void +Opcode_ae_mulzasp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64006; +} + +static void +Opcode_ae_mulzasfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64005; +} + +static void +Opcode_ae_mulzasp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64007; +} + +static void +Opcode_ae_mulzsafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64009; +} + +static void +Opcode_ae_mulzsap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6400c; +} + +static void +Opcode_ae_mulzsafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6400a; +} + +static void +Opcode_ae_mulzsap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6400b; +} + +static void +Opcode_ae_mulzssfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6400d; +} + +static void +Opcode_ae_mulzssp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6400f; +} + +static void +Opcode_ae_mulzssfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6400e; +} + +static void +Opcode_ae_mulzssp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64081; +} + +static void +Opcode_ae_mulaafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60000; +} + +static void +Opcode_ae_mulaap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60002; +} + +static void +Opcode_ae_mulaafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60001; +} + +static void +Opcode_ae_mulaap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60004; +} + +static void +Opcode_ae_mulasfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60083; +} + +static void +Opcode_ae_mulasp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60086; +} + +static void +Opcode_ae_mulasfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60085; +} + +static void +Opcode_ae_mulasp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60087; +} + +static void +Opcode_ae_mulsafp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60103; +} + +static void +Opcode_ae_mulsap24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60106; +} + +static void +Opcode_ae_mulsafp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60105; +} + +static void +Opcode_ae_mulsap24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x60107; +} + +static void +Opcode_ae_mulssfp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6018e; +} + +static void +Opcode_ae_mulssp24s_hh_ll_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64001; +} + +static void +Opcode_ae_mulssfp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x6018f; +} + +static void +Opcode_ae_mulssp24s_hl_lh_Slot_ae_slot1_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0x64002; +} + +static void +Opcode_ae_sha32_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe00014; +} + +static void +Opcode_ae_vldl32t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa00004; +} + +static void +Opcode_ae_vldl16t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa10004; +} + +static void +Opcode_ae_vldl16c_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe7e014; +} + +static void +Opcode_ae_vldsht_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xca8004; +} + +static void +Opcode_ae_lb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xc60004; +} + +static void +Opcode_ae_lbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe00024; +} + +static void +Opcode_ae_lbk_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa20004; +} + +static void +Opcode_ae_lbki_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe00004; +} + +static void +Opcode_ae_db_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf01004; +} + +static void +Opcode_ae_dbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf02004; +} + +static void +Opcode_ae_vlel32t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa30004; +} + +static void +Opcode_ae_vlel16t_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xa40004; +} + +static void +Opcode_ae_sb_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf11004; +} + +static void +Opcode_ae_sbi_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xf00004; +} + +static void +Opcode_ae_vles16c_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe7c014; +} + +static void +Opcode_ae_sbf_Slot_inst_encode (xtensa_insnbuf slotbuf) +{ + slotbuf[0] = 0xe7d014; +} + +static xtensa_opcode_encode_fn Opcode_excw_encode_fns[] = { + Opcode_excw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfe_encode_fns[] = { + Opcode_rfe_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfde_encode_fns[] = { + Opcode_rfde_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_syscall_encode_fns[] = { + Opcode_syscall_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_simcall_encode_fns[] = { + Opcode_simcall_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_call12_encode_fns[] = { + Opcode_call12_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_call8_encode_fns[] = { + Opcode_call8_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_call4_encode_fns[] = { + Opcode_call4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx12_encode_fns[] = { + Opcode_callx12_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx8_encode_fns[] = { + Opcode_callx8_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx4_encode_fns[] = { + Opcode_callx4_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_entry_encode_fns[] = { + Opcode_entry_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_movsp_encode_fns[] = { + Opcode_movsp_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rotw_encode_fns[] = { + Opcode_rotw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_retw_encode_fns[] = { + Opcode_retw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_retw_n_encode_fns[] = { + 0, 0, Opcode_retw_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfwo_encode_fns[] = { + Opcode_rfwo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfwu_encode_fns[] = { + Opcode_rfwu_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_l32e_encode_fns[] = { + Opcode_l32e_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32e_encode_fns[] = { + Opcode_s32e_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_windowbase_encode_fns[] = { + Opcode_rsr_windowbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_windowbase_encode_fns[] = { + Opcode_wsr_windowbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_windowbase_encode_fns[] = { + Opcode_xsr_windowbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_windowstart_encode_fns[] = { + Opcode_rsr_windowstart_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_windowstart_encode_fns[] = { + Opcode_wsr_windowstart_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_windowstart_encode_fns[] = { + Opcode_xsr_windowstart_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_add_n_encode_fns[] = { + 0, Opcode_add_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_addi_n_encode_fns[] = { + 0, Opcode_addi_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_beqz_n_encode_fns[] = { + 0, 0, Opcode_beqz_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_bnez_n_encode_fns[] = { + 0, 0, Opcode_bnez_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ill_n_encode_fns[] = { + 0, 0, Opcode_ill_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_l32i_n_encode_fns[] = { + 0, Opcode_l32i_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mov_n_encode_fns[] = { + 0, 0, Opcode_mov_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_movi_n_encode_fns[] = { + 0, 0, Opcode_movi_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_nop_n_encode_fns[] = { + 0, 0, Opcode_nop_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ret_n_encode_fns[] = { + 0, 0, Opcode_ret_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32i_n_encode_fns[] = { + 0, Opcode_s32i_n_Slot_inst16a_encode, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_threadptr_encode_fns[] = { + Opcode_rur_threadptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_threadptr_encode_fns[] = { + Opcode_wur_threadptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_addi_encode_fns[] = { + Opcode_addi_Slot_inst_encode, 0, 0, 0, Opcode_addi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addmi_encode_fns[] = { + Opcode_addmi_Slot_inst_encode, 0, 0, 0, Opcode_addmi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_add_encode_fns[] = { + Opcode_add_Slot_inst_encode, 0, 0, 0, Opcode_add_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_sub_encode_fns[] = { + Opcode_sub_Slot_inst_encode, 0, 0, 0, Opcode_sub_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addx2_encode_fns[] = { + Opcode_addx2_Slot_inst_encode, 0, 0, 0, Opcode_addx2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addx4_encode_fns[] = { + Opcode_addx4_Slot_inst_encode, 0, 0, 0, Opcode_addx4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_addx8_encode_fns[] = { + Opcode_addx8_Slot_inst_encode, 0, 0, 0, Opcode_addx8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_subx2_encode_fns[] = { + Opcode_subx2_Slot_inst_encode, 0, 0, 0, Opcode_subx2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_subx4_encode_fns[] = { + Opcode_subx4_Slot_inst_encode, 0, 0, 0, Opcode_subx4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_subx8_encode_fns[] = { + Opcode_subx8_Slot_inst_encode, 0, 0, 0, Opcode_subx8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_and_encode_fns[] = { + Opcode_and_Slot_inst_encode, 0, 0, 0, Opcode_and_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_or_encode_fns[] = { + Opcode_or_Slot_inst_encode, 0, 0, 0, Opcode_or_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_xor_encode_fns[] = { + Opcode_xor_Slot_inst_encode, 0, 0, 0, Opcode_xor_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_beqi_encode_fns[] = { + Opcode_beqi_Slot_inst_encode, 0, 0, 0, Opcode_beqi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnei_encode_fns[] = { + Opcode_bnei_Slot_inst_encode, 0, 0, 0, Opcode_bnei_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgei_encode_fns[] = { + Opcode_bgei_Slot_inst_encode, 0, 0, 0, Opcode_bgei_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_blti_encode_fns[] = { + Opcode_blti_Slot_inst_encode, 0, 0, 0, Opcode_blti_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbci_encode_fns[] = { + Opcode_bbci_Slot_inst_encode, 0, 0, 0, Opcode_bbci_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbsi_encode_fns[] = { + Opcode_bbsi_Slot_inst_encode, 0, 0, 0, Opcode_bbsi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgeui_encode_fns[] = { + Opcode_bgeui_Slot_inst_encode, 0, 0, 0, Opcode_bgeui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bltui_encode_fns[] = { + Opcode_bltui_Slot_inst_encode, 0, 0, 0, Opcode_bltui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_beq_encode_fns[] = { + Opcode_beq_Slot_inst_encode, 0, 0, 0, Opcode_beq_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bne_encode_fns[] = { + Opcode_bne_Slot_inst_encode, 0, 0, 0, Opcode_bne_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bge_encode_fns[] = { + Opcode_bge_Slot_inst_encode, 0, 0, 0, Opcode_bge_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_blt_encode_fns[] = { + Opcode_blt_Slot_inst_encode, 0, 0, 0, Opcode_blt_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgeu_encode_fns[] = { + Opcode_bgeu_Slot_inst_encode, 0, 0, 0, Opcode_bgeu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bltu_encode_fns[] = { + Opcode_bltu_Slot_inst_encode, 0, 0, 0, Opcode_bltu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bany_encode_fns[] = { + Opcode_bany_Slot_inst_encode, 0, 0, 0, Opcode_bany_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnone_encode_fns[] = { + Opcode_bnone_Slot_inst_encode, 0, 0, 0, Opcode_bnone_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ball_encode_fns[] = { + Opcode_ball_Slot_inst_encode, 0, 0, 0, Opcode_ball_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnall_encode_fns[] = { + Opcode_bnall_Slot_inst_encode, 0, 0, 0, Opcode_bnall_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbc_encode_fns[] = { + Opcode_bbc_Slot_inst_encode, 0, 0, 0, Opcode_bbc_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bbs_encode_fns[] = { + Opcode_bbs_Slot_inst_encode, 0, 0, 0, Opcode_bbs_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_beqz_encode_fns[] = { + Opcode_beqz_Slot_inst_encode, 0, 0, 0, Opcode_beqz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bnez_encode_fns[] = { + Opcode_bnez_Slot_inst_encode, 0, 0, 0, Opcode_bnez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bgez_encode_fns[] = { + Opcode_bgez_Slot_inst_encode, 0, 0, 0, Opcode_bgez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bltz_encode_fns[] = { + Opcode_bltz_Slot_inst_encode, 0, 0, 0, Opcode_bltz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_call0_encode_fns[] = { + Opcode_call0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_callx0_encode_fns[] = { + Opcode_callx0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_extui_encode_fns[] = { + Opcode_extui_Slot_inst_encode, 0, 0, 0, Opcode_extui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ill_encode_fns[] = { + Opcode_ill_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_j_encode_fns[] = { + Opcode_j_Slot_inst_encode, 0, 0, 0, Opcode_j_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_jx_encode_fns[] = { + Opcode_jx_Slot_inst_encode, 0, 0, 0, Opcode_jx_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l16ui_encode_fns[] = { + Opcode_l16ui_Slot_inst_encode, 0, 0, 0, Opcode_l16ui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l16si_encode_fns[] = { + Opcode_l16si_Slot_inst_encode, 0, 0, 0, Opcode_l16si_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l32i_encode_fns[] = { + Opcode_l32i_Slot_inst_encode, 0, 0, 0, Opcode_l32i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l32r_encode_fns[] = { + Opcode_l32r_Slot_inst_encode, 0, 0, 0, Opcode_l32r_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l8ui_encode_fns[] = { + Opcode_l8ui_Slot_inst_encode, 0, 0, 0, Opcode_l8ui_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_loop_encode_fns[] = { + Opcode_loop_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_loopnez_encode_fns[] = { + Opcode_loopnez_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_loopgtz_encode_fns[] = { + Opcode_loopgtz_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_movi_encode_fns[] = { + Opcode_movi_Slot_inst_encode, 0, 0, 0, Opcode_movi_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_moveqz_encode_fns[] = { + Opcode_moveqz_Slot_inst_encode, 0, 0, 0, Opcode_moveqz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movnez_encode_fns[] = { + Opcode_movnez_Slot_inst_encode, 0, 0, 0, Opcode_movnez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movltz_encode_fns[] = { + Opcode_movltz_Slot_inst_encode, 0, 0, 0, Opcode_movltz_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movgez_encode_fns[] = { + Opcode_movgez_Slot_inst_encode, 0, 0, 0, Opcode_movgez_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_neg_encode_fns[] = { + Opcode_neg_Slot_inst_encode, 0, 0, 0, Opcode_neg_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_abs_encode_fns[] = { + Opcode_abs_Slot_inst_encode, 0, 0, 0, Opcode_abs_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_nop_encode_fns[] = { + Opcode_nop_Slot_inst_encode, 0, 0, Opcode_nop_Slot_ae_slot1_encode, Opcode_nop_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ret_encode_fns[] = { + Opcode_ret_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s16i_encode_fns[] = { + Opcode_s16i_Slot_inst_encode, 0, 0, 0, Opcode_s16i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_s32i_encode_fns[] = { + Opcode_s32i_Slot_inst_encode, 0, 0, 0, Opcode_s32i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_s8i_encode_fns[] = { + Opcode_s8i_Slot_inst_encode, 0, 0, 0, Opcode_s8i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssr_encode_fns[] = { + Opcode_ssr_Slot_inst_encode, 0, 0, 0, Opcode_ssr_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssl_encode_fns[] = { + Opcode_ssl_Slot_inst_encode, 0, 0, 0, Opcode_ssl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssa8l_encode_fns[] = { + Opcode_ssa8l_Slot_inst_encode, 0, 0, 0, Opcode_ssa8l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssa8b_encode_fns[] = { + Opcode_ssa8b_Slot_inst_encode, 0, 0, 0, Opcode_ssa8b_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ssai_encode_fns[] = { + Opcode_ssai_Slot_inst_encode, 0, 0, 0, Opcode_ssai_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_sll_encode_fns[] = { + Opcode_sll_Slot_inst_encode, 0, 0, 0, Opcode_sll_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_src_encode_fns[] = { + Opcode_src_Slot_inst_encode, 0, 0, 0, Opcode_src_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_srl_encode_fns[] = { + Opcode_srl_Slot_inst_encode, 0, 0, 0, Opcode_srl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_sra_encode_fns[] = { + Opcode_sra_Slot_inst_encode, 0, 0, 0, Opcode_sra_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_slli_encode_fns[] = { + Opcode_slli_Slot_inst_encode, 0, 0, 0, Opcode_slli_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_srai_encode_fns[] = { + Opcode_srai_Slot_inst_encode, 0, 0, 0, Opcode_srai_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_srli_encode_fns[] = { + Opcode_srli_Slot_inst_encode, 0, 0, 0, Opcode_srli_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_memw_encode_fns[] = { + Opcode_memw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_extw_encode_fns[] = { + Opcode_extw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_isync_encode_fns[] = { + Opcode_isync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsync_encode_fns[] = { + Opcode_rsync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_esync_encode_fns[] = { + Opcode_esync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dsync_encode_fns[] = { + Opcode_dsync_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsil_encode_fns[] = { + Opcode_rsil_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_lend_encode_fns[] = { + Opcode_rsr_lend_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_lend_encode_fns[] = { + Opcode_wsr_lend_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_lend_encode_fns[] = { + Opcode_xsr_lend_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_lcount_encode_fns[] = { + Opcode_rsr_lcount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_lcount_encode_fns[] = { + Opcode_wsr_lcount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_lcount_encode_fns[] = { + Opcode_xsr_lcount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_lbeg_encode_fns[] = { + Opcode_rsr_lbeg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_lbeg_encode_fns[] = { + Opcode_wsr_lbeg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_lbeg_encode_fns[] = { + Opcode_xsr_lbeg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_sar_encode_fns[] = { + Opcode_rsr_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_sar_encode_fns[] = { + Opcode_wsr_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_sar_encode_fns[] = { + Opcode_xsr_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_litbase_encode_fns[] = { + Opcode_rsr_litbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_litbase_encode_fns[] = { + Opcode_wsr_litbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_litbase_encode_fns[] = { + Opcode_xsr_litbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_176_encode_fns[] = { + Opcode_rsr_176_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_176_encode_fns[] = { + Opcode_wsr_176_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_208_encode_fns[] = { + Opcode_rsr_208_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ps_encode_fns[] = { + Opcode_rsr_ps_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ps_encode_fns[] = { + Opcode_wsr_ps_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ps_encode_fns[] = { + Opcode_xsr_ps_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc1_encode_fns[] = { + Opcode_rsr_epc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc1_encode_fns[] = { + Opcode_wsr_epc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc1_encode_fns[] = { + Opcode_xsr_epc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave1_encode_fns[] = { + Opcode_rsr_excsave1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave1_encode_fns[] = { + Opcode_wsr_excsave1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave1_encode_fns[] = { + Opcode_xsr_excsave1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_epc2_encode_fns[] = { + Opcode_rsr_epc2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_epc2_encode_fns[] = { + Opcode_wsr_epc2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_epc2_encode_fns[] = { + Opcode_xsr_epc2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excsave2_encode_fns[] = { + Opcode_rsr_excsave2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excsave2_encode_fns[] = { + Opcode_wsr_excsave2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excsave2_encode_fns[] = { + Opcode_xsr_excsave2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_eps2_encode_fns[] = { + Opcode_rsr_eps2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_eps2_encode_fns[] = { + Opcode_wsr_eps2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_eps2_encode_fns[] = { + Opcode_xsr_eps2_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_excvaddr_encode_fns[] = { + Opcode_rsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_excvaddr_encode_fns[] = { + Opcode_wsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_excvaddr_encode_fns[] = { + Opcode_xsr_excvaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_depc_encode_fns[] = { + Opcode_rsr_depc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_depc_encode_fns[] = { + Opcode_wsr_depc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_depc_encode_fns[] = { + Opcode_xsr_depc_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_exccause_encode_fns[] = { + Opcode_rsr_exccause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_exccause_encode_fns[] = { + Opcode_wsr_exccause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_exccause_encode_fns[] = { + Opcode_xsr_exccause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_misc0_encode_fns[] = { + Opcode_rsr_misc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_misc0_encode_fns[] = { + Opcode_wsr_misc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_misc0_encode_fns[] = { + Opcode_xsr_misc0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_misc1_encode_fns[] = { + Opcode_rsr_misc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_misc1_encode_fns[] = { + Opcode_wsr_misc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_misc1_encode_fns[] = { + Opcode_xsr_misc1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_prid_encode_fns[] = { + Opcode_rsr_prid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_vecbase_encode_fns[] = { + Opcode_rsr_vecbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_vecbase_encode_fns[] = { + Opcode_wsr_vecbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_vecbase_encode_fns[] = { + Opcode_xsr_vecbase_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul16u_encode_fns[] = { + Opcode_mul16u_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mul16s_encode_fns[] = { + Opcode_mul16s_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_mull_encode_fns[] = { + Opcode_mull_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfi_encode_fns[] = { + Opcode_rfi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_waiti_encode_fns[] = { + Opcode_waiti_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_interrupt_encode_fns[] = { + Opcode_rsr_interrupt_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_intset_encode_fns[] = { + Opcode_wsr_intset_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_intclear_encode_fns[] = { + Opcode_wsr_intclear_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_intenable_encode_fns[] = { + Opcode_rsr_intenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_intenable_encode_fns[] = { + Opcode_wsr_intenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_intenable_encode_fns[] = { + Opcode_xsr_intenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_break_encode_fns[] = { + Opcode_break_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_break_n_encode_fns[] = { + 0, 0, Opcode_break_n_Slot_inst16b_encode, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_debugcause_encode_fns[] = { + Opcode_rsr_debugcause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_debugcause_encode_fns[] = { + Opcode_wsr_debugcause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_debugcause_encode_fns[] = { + Opcode_xsr_debugcause_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_icount_encode_fns[] = { + Opcode_rsr_icount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_icount_encode_fns[] = { + Opcode_wsr_icount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_icount_encode_fns[] = { + Opcode_xsr_icount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_icountlevel_encode_fns[] = { + Opcode_rsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_icountlevel_encode_fns[] = { + Opcode_wsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_icountlevel_encode_fns[] = { + Opcode_xsr_icountlevel_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ddr_encode_fns[] = { + Opcode_rsr_ddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ddr_encode_fns[] = { + Opcode_wsr_ddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ddr_encode_fns[] = { + Opcode_xsr_ddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfdo_encode_fns[] = { + Opcode_rfdo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rfdd_encode_fns[] = { + Opcode_rfdd_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_andb_encode_fns[] = { + Opcode_andb_Slot_inst_encode, 0, 0, 0, Opcode_andb_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_andbc_encode_fns[] = { + Opcode_andbc_Slot_inst_encode, 0, 0, 0, Opcode_andbc_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_orb_encode_fns[] = { + Opcode_orb_Slot_inst_encode, 0, 0, 0, Opcode_orb_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_orbc_encode_fns[] = { + Opcode_orbc_Slot_inst_encode, 0, 0, 0, Opcode_orbc_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_xorb_encode_fns[] = { + Opcode_xorb_Slot_inst_encode, 0, 0, 0, Opcode_xorb_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_any4_encode_fns[] = { + Opcode_any4_Slot_inst_encode, 0, 0, 0, Opcode_any4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_all4_encode_fns[] = { + Opcode_all4_Slot_inst_encode, 0, 0, 0, Opcode_all4_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_any8_encode_fns[] = { + Opcode_any8_Slot_inst_encode, 0, 0, 0, Opcode_any8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_all8_encode_fns[] = { + Opcode_all8_Slot_inst_encode, 0, 0, 0, Opcode_all8_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bf_encode_fns[] = { + Opcode_bf_Slot_inst_encode, 0, 0, 0, Opcode_bf_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_bt_encode_fns[] = { + Opcode_bt_Slot_inst_encode, 0, 0, 0, Opcode_bt_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movf_encode_fns[] = { + Opcode_movf_Slot_inst_encode, 0, 0, 0, Opcode_movf_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_movt_encode_fns[] = { + Opcode_movt_Slot_inst_encode, 0, 0, 0, Opcode_movt_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_rsr_br_encode_fns[] = { + Opcode_rsr_br_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_br_encode_fns[] = { + Opcode_wsr_br_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_br_encode_fns[] = { + Opcode_xsr_br_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccount_encode_fns[] = { + Opcode_rsr_ccount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccount_encode_fns[] = { + Opcode_wsr_ccount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccount_encode_fns[] = { + Opcode_xsr_ccount_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccompare0_encode_fns[] = { + Opcode_rsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccompare0_encode_fns[] = { + Opcode_wsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccompare0_encode_fns[] = { + Opcode_xsr_ccompare0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ccompare1_encode_fns[] = { + Opcode_rsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ccompare1_encode_fns[] = { + Opcode_wsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ccompare1_encode_fns[] = { + Opcode_xsr_ccompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ipf_encode_fns[] = { + Opcode_ipf_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ihi_encode_fns[] = { + Opcode_ihi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_iii_encode_fns[] = { + Opcode_iii_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_lict_encode_fns[] = { + Opcode_lict_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_licw_encode_fns[] = { + Opcode_licw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sict_encode_fns[] = { + Opcode_sict_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sicw_encode_fns[] = { + Opcode_sicw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhwb_encode_fns[] = { + Opcode_dhwb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhwbi_encode_fns[] = { + Opcode_dhwbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_diwb_encode_fns[] = { + Opcode_diwb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_diwbi_encode_fns[] = { + Opcode_diwbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dhi_encode_fns[] = { + Opcode_dhi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dii_encode_fns[] = { + Opcode_dii_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfr_encode_fns[] = { + Opcode_dpfr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfw_encode_fns[] = { + Opcode_dpfw_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfro_encode_fns[] = { + Opcode_dpfro_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_dpfwo_encode_fns[] = { + Opcode_dpfwo_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sdct_encode_fns[] = { + Opcode_sdct_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ldct_encode_fns[] = { + Opcode_ldct_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_ptevaddr_encode_fns[] = { + Opcode_wsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_ptevaddr_encode_fns[] = { + Opcode_rsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_ptevaddr_encode_fns[] = { + Opcode_xsr_ptevaddr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_rasid_encode_fns[] = { + Opcode_rsr_rasid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_rasid_encode_fns[] = { + Opcode_wsr_rasid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_rasid_encode_fns[] = { + Opcode_xsr_rasid_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_itlbcfg_encode_fns[] = { + Opcode_rsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_itlbcfg_encode_fns[] = { + Opcode_wsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_itlbcfg_encode_fns[] = { + Opcode_xsr_itlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_dtlbcfg_encode_fns[] = { + Opcode_rsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_dtlbcfg_encode_fns[] = { + Opcode_wsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_dtlbcfg_encode_fns[] = { + Opcode_xsr_dtlbcfg_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_idtlb_encode_fns[] = { + Opcode_idtlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_pdtlb_encode_fns[] = { + Opcode_pdtlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rdtlb0_encode_fns[] = { + Opcode_rdtlb0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rdtlb1_encode_fns[] = { + Opcode_rdtlb1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wdtlb_encode_fns[] = { + Opcode_wdtlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_iitlb_encode_fns[] = { + Opcode_iitlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_pitlb_encode_fns[] = { + Opcode_pitlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ritlb0_encode_fns[] = { + Opcode_ritlb0_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ritlb1_encode_fns[] = { + Opcode_ritlb1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_witlb_encode_fns[] = { + Opcode_witlb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ldpte_encode_fns[] = { + Opcode_ldpte_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_hwwitlba_encode_fns[] = { + Opcode_hwwitlba_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_hwwdtlba_encode_fns[] = { + Opcode_hwwdtlba_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_cpenable_encode_fns[] = { + Opcode_rsr_cpenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_cpenable_encode_fns[] = { + Opcode_wsr_cpenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_cpenable_encode_fns[] = { + Opcode_xsr_cpenable_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_clamps_encode_fns[] = { + Opcode_clamps_Slot_inst_encode, 0, 0, 0, Opcode_clamps_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_min_encode_fns[] = { + Opcode_min_Slot_inst_encode, 0, 0, 0, Opcode_min_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_max_encode_fns[] = { + Opcode_max_Slot_inst_encode, 0, 0, 0, Opcode_max_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_minu_encode_fns[] = { + Opcode_minu_Slot_inst_encode, 0, 0, 0, Opcode_minu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_maxu_encode_fns[] = { + Opcode_maxu_Slot_inst_encode, 0, 0, 0, Opcode_maxu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_nsa_encode_fns[] = { + Opcode_nsa_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_nsau_encode_fns[] = { + Opcode_nsau_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_sext_encode_fns[] = { + Opcode_sext_Slot_inst_encode, 0, 0, 0, Opcode_sext_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_l32ai_encode_fns[] = { + Opcode_l32ai_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32ri_encode_fns[] = { + Opcode_s32ri_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_s32c1i_encode_fns[] = { + Opcode_s32c1i_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_scompare1_encode_fns[] = { + Opcode_rsr_scompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_scompare1_encode_fns[] = { + Opcode_wsr_scompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_scompare1_encode_fns[] = { + Opcode_xsr_scompare1_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rsr_atomctl_encode_fns[] = { + Opcode_rsr_atomctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wsr_atomctl_encode_fns[] = { + Opcode_wsr_atomctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_xsr_atomctl_encode_fns[] = { + Opcode_xsr_atomctl_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rer_encode_fns[] = { + Opcode_rer_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wer_encode_fns[] = { + Opcode_wer_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_ovf_sar_encode_fns[] = { + Opcode_rur_ae_ovf_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_ovf_sar_encode_fns[] = { + Opcode_wur_ae_ovf_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_bithead_encode_fns[] = { + Opcode_rur_ae_bithead_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_bithead_encode_fns[] = { + Opcode_wur_ae_bithead_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_ts_fts_bu_bp_encode_fns[] = { + Opcode_rur_ae_ts_fts_bu_bp_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_ts_fts_bu_bp_encode_fns[] = { + Opcode_wur_ae_ts_fts_bu_bp_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_sd_no_encode_fns[] = { + Opcode_rur_ae_sd_no_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_sd_no_encode_fns[] = { + Opcode_wur_ae_sd_no_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_overflow_encode_fns[] = { + Opcode_rur_ae_overflow_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_overflow_encode_fns[] = { + Opcode_wur_ae_overflow_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_sar_encode_fns[] = { + Opcode_rur_ae_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_sar_encode_fns[] = { + Opcode_wur_ae_sar_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_bitptr_encode_fns[] = { + Opcode_rur_ae_bitptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_bitptr_encode_fns[] = { + Opcode_wur_ae_bitptr_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_bitsused_encode_fns[] = { + Opcode_rur_ae_bitsused_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_bitsused_encode_fns[] = { + Opcode_wur_ae_bitsused_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_tablesize_encode_fns[] = { + Opcode_rur_ae_tablesize_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_tablesize_encode_fns[] = { + Opcode_wur_ae_tablesize_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_first_ts_encode_fns[] = { + Opcode_rur_ae_first_ts_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_first_ts_encode_fns[] = { + Opcode_wur_ae_first_ts_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_nextoffset_encode_fns[] = { + Opcode_rur_ae_nextoffset_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_nextoffset_encode_fns[] = { + Opcode_wur_ae_nextoffset_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_rur_ae_searchdone_encode_fns[] = { + Opcode_rur_ae_searchdone_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_wur_ae_searchdone_encode_fns[] = { + Opcode_wur_ae_searchdone_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_i_encode_fns[] = { + Opcode_ae_lp16f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_iu_encode_fns[] = { + Opcode_ae_lp16f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_x_encode_fns[] = { + Opcode_ae_lp16f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16f_xu_encode_fns[] = { + Opcode_ae_lp16f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_i_encode_fns[] = { + Opcode_ae_lp24_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_iu_encode_fns[] = { + Opcode_ae_lp24_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_x_encode_fns[] = { + Opcode_ae_lp24_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24_xu_encode_fns[] = { + Opcode_ae_lp24_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_i_encode_fns[] = { + Opcode_ae_lp24f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_iu_encode_fns[] = { + Opcode_ae_lp24f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_x_encode_fns[] = { + Opcode_ae_lp24f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24f_xu_encode_fns[] = { + Opcode_ae_lp24f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_i_encode_fns[] = { + Opcode_ae_lp16x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_iu_encode_fns[] = { + Opcode_ae_lp16x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_x_encode_fns[] = { + Opcode_ae_lp16x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp16x2f_xu_encode_fns[] = { + Opcode_ae_lp16x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp16x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_i_encode_fns[] = { + Opcode_ae_lp24x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_iu_encode_fns[] = { + Opcode_ae_lp24x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_x_encode_fns[] = { + Opcode_ae_lp24x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2f_xu_encode_fns[] = { + Opcode_ae_lp24x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_i_encode_fns[] = { + Opcode_ae_lp24x2_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_iu_encode_fns[] = { + Opcode_ae_lp24x2_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_x_encode_fns[] = { + Opcode_ae_lp24x2_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lp24x2_xu_encode_fns[] = { + Opcode_ae_lp24x2_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lp24x2_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_i_encode_fns[] = { + Opcode_ae_sp16x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_iu_encode_fns[] = { + Opcode_ae_sp16x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_x_encode_fns[] = { + Opcode_ae_sp16x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16x2f_xu_encode_fns[] = { + Opcode_ae_sp16x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_i_encode_fns[] = { + Opcode_ae_sp24x2s_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_iu_encode_fns[] = { + Opcode_ae_sp24x2s_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_x_encode_fns[] = { + Opcode_ae_sp24x2s_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2s_xu_encode_fns[] = { + Opcode_ae_sp24x2s_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2s_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_i_encode_fns[] = { + Opcode_ae_sp24x2f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_iu_encode_fns[] = { + Opcode_ae_sp24x2f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_x_encode_fns[] = { + Opcode_ae_sp24x2f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24x2f_xu_encode_fns[] = { + Opcode_ae_sp24x2f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24x2f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_i_encode_fns[] = { + Opcode_ae_sp16f_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_iu_encode_fns[] = { + Opcode_ae_sp16f_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_x_encode_fns[] = { + Opcode_ae_sp16f_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp16f_l_xu_encode_fns[] = { + Opcode_ae_sp16f_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp16f_l_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_i_encode_fns[] = { + Opcode_ae_sp24s_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_iu_encode_fns[] = { + Opcode_ae_sp24s_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_x_encode_fns[] = { + Opcode_ae_sp24s_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24s_l_xu_encode_fns[] = { + Opcode_ae_sp24s_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24s_l_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_i_encode_fns[] = { + Opcode_ae_sp24f_l_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_iu_encode_fns[] = { + Opcode_ae_sp24f_l_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_x_encode_fns[] = { + Opcode_ae_sp24f_l_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sp24f_l_xu_encode_fns[] = { + Opcode_ae_sp24f_l_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sp24f_l_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_i_encode_fns[] = { + Opcode_ae_lq56_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_iu_encode_fns[] = { + Opcode_ae_lq56_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_x_encode_fns[] = { + Opcode_ae_lq56_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq56_xu_encode_fns[] = { + Opcode_ae_lq56_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq56_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_i_encode_fns[] = { + Opcode_ae_lq32f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_iu_encode_fns[] = { + Opcode_ae_lq32f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_x_encode_fns[] = { + Opcode_ae_lq32f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_lq32f_xu_encode_fns[] = { + Opcode_ae_lq32f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_lq32f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_i_encode_fns[] = { + Opcode_ae_sq56s_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_iu_encode_fns[] = { + Opcode_ae_sq56s_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_x_encode_fns[] = { + Opcode_ae_sq56s_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq56s_xu_encode_fns[] = { + Opcode_ae_sq56s_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq56s_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_i_encode_fns[] = { + Opcode_ae_sq32f_i_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_i_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_iu_encode_fns[] = { + Opcode_ae_sq32f_iu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_iu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_x_encode_fns[] = { + Opcode_ae_sq32f_x_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_x_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sq32f_xu_encode_fns[] = { + Opcode_ae_sq32f_xu_Slot_inst_encode, 0, 0, 0, Opcode_ae_sq32f_xu_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_zerop48_encode_fns[] = { + 0, 0, 0, Opcode_ae_zerop48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movp48_encode_fns[] = { + Opcode_ae_movp48_Slot_inst_encode, 0, 0, Opcode_ae_movp48_Slot_ae_slot1_encode, Opcode_ae_movp48_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_selp24_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_selp24_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movtp24x2_encode_fns[] = { + 0, 0, 0, Opcode_ae_movtp24x2_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movfp24x2_encode_fns[] = { + 0, 0, 0, Opcode_ae_movfp24x2_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movtp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_movtp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movfp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_movfp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movpa24x2_encode_fns[] = { + Opcode_ae_movpa24x2_Slot_inst_encode, 0, 0, 0, Opcode_ae_movpa24x2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncp24a32x2_encode_fns[] = { + Opcode_ae_truncp24a32x2_Slot_inst_encode, 0, 0, 0, Opcode_ae_truncp24a32x2_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvta32p24_l_encode_fns[] = { + Opcode_ae_cvta32p24_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvta32p24_l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvta32p24_h_encode_fns[] = { + Opcode_ae_cvta32p24_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvta32p24_h_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_ll_encode_fns[] = { + Opcode_ae_cvtp24a16x2_ll_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_ll_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_lh_encode_fns[] = { + Opcode_ae_cvtp24a16x2_lh_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_lh_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hl_encode_fns[] = { + Opcode_ae_cvtp24a16x2_hl_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_hl_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtp24a16x2_hh_encode_fns[] = { + Opcode_ae_cvtp24a16x2_hh_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtp24a16x2_hh_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncp24q48x2_encode_fns[] = { + 0, 0, 0, Opcode_ae_truncp24q48x2_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncp16_encode_fns[] = { + 0, 0, 0, Opcode_ae_truncp16_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp24q48sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp24q48sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp24q48asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp24q48asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16q48sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16q48sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16q48asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16q48asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsp16asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsp16asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_zeroq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_zeroq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_movq56_encode_fns[] = { + Opcode_ae_movq56_Slot_inst_encode, 0, 0, Opcode_ae_movq56_Slot_ae_slot1_encode, Opcode_ae_movq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movtq56_encode_fns[] = { + Opcode_ae_movtq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_movtq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movfq56_encode_fns[] = { + Opcode_ae_movfq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_movfq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtq48a32s_encode_fns[] = { + Opcode_ae_cvtq48a32s_Slot_inst_encode, 0, 0, 0, Opcode_ae_cvtq48a32s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_cvtq48p24s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_cvtq48p24s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_cvtq48p24s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_satq48s_encode_fns[] = { + 0, 0, 0, Opcode_ae_satq48s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_truncq32_encode_fns[] = { + 0, 0, 0, Opcode_ae_truncq32_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsq32sym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsq32sym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_roundsq32asym_encode_fns[] = { + 0, 0, 0, Opcode_ae_roundsq32asym_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_trunca32q48_encode_fns[] = { + Opcode_ae_trunca32q48_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca32q48_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movap24s_l_encode_fns[] = { + Opcode_ae_movap24s_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_movap24s_l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_movap24s_h_encode_fns[] = { + Opcode_ae_movap24s_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_movap24s_h_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_l_encode_fns[] = { + Opcode_ae_trunca16p24s_l_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca16p24s_l_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_trunca16p24s_h_encode_fns[] = { + Opcode_ae_trunca16p24s_h_Slot_inst_encode, 0, 0, 0, Opcode_ae_trunca16p24s_h_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_addp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_addp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_subp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_negp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_absp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_absp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxbp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxbp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minbp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minbp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_addsp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_addsp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subsp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_subsp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negsp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_negsp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_abssp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_abssp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_andp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_andp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_nandp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_nandp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_orp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_orp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_xorp48_encode_fns[] = { + 0, 0, 0, Opcode_ae_xorp48_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_ltp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_ltp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lep24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_lep24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_eqp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_eqp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_addq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_addq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_subq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_negq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_absq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_absq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_maxbq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_maxbq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_minbq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_minbq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_addsq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_addsq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_subsq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_subsq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_negsq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_negsq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_abssq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_abssq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_andq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_andq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_nandq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_nandq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_orq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_orq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_xorq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_xorq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllip24_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllip24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlip24_encode_fns[] = { + 0, 0, 0, Opcode_ae_srlip24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sraip24_encode_fns[] = { + 0, 0, 0, Opcode_ae_sraip24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllsp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllsp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlsp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_srlsp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_srasp24_encode_fns[] = { + 0, 0, 0, Opcode_ae_srasp24_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllisp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllisp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllssp24s_encode_fns[] = { + 0, 0, 0, Opcode_ae_sllssp24s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_slliq56_encode_fns[] = { + Opcode_ae_slliq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_slliq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srliq56_encode_fns[] = { + Opcode_ae_srliq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srliq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sraiq56_encode_fns[] = { + Opcode_ae_sraiq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sraiq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllsq56_encode_fns[] = { + Opcode_ae_sllsq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllsq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlsq56_encode_fns[] = { + Opcode_ae_srlsq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srlsq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srasq56_encode_fns[] = { + Opcode_ae_srasq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srasq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllaq56_encode_fns[] = { + Opcode_ae_sllaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllaq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_srlaq56_encode_fns[] = { + Opcode_ae_srlaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_srlaq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sraaq56_encode_fns[] = { + Opcode_ae_sraaq56_Slot_inst_encode, 0, 0, 0, Opcode_ae_sraaq56_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllisq56s_encode_fns[] = { + Opcode_ae_sllisq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllisq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllssq56s_encode_fns[] = { + Opcode_ae_sllssq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllssq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_sllasq56s_encode_fns[] = { + Opcode_ae_sllasq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_sllasq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_ltq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_ltq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_leq56s_encode_fns[] = { + 0, 0, 0, Opcode_ae_leq56s_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_eqq56_encode_fns[] = { + 0, 0, 0, Opcode_ae_eqq56_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_nsaq56s_encode_fns[] = { + Opcode_ae_nsaq56s_Slot_inst_encode, 0, 0, 0, Opcode_ae_nsaq56s_Slot_ae_slot0_encode +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfs32p16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfs32p16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs32p16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs32p16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulap24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulap24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs32p16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs32p16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsp24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsp24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafs56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafs56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulas56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulas56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hl_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_hl_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfs56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfs56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulss56p24s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulss56p24s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulfq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulfq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulafq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulafq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsfq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsfq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16s_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16s_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16s_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_l_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16u_l_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsq32sp16u_h_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsq32sp16u_h_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaaq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaaq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsaq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsaq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16s_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16u_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16s_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_hh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16u_hh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16s_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16s_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfq32sp16u_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfq32sp16u_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzaap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzaap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzasp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzasp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzsap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzsap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulzssp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulzssp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulaap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulaap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulasp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulasp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsafp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsap24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsafp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsafp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulsap24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulsap24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssfp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hh_ll_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssp24s_hh_ll_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssfp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssfp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_mulssp24s_hl_lh_encode_fns[] = { + 0, 0, 0, Opcode_ae_mulssp24s_hl_lh_Slot_ae_slot1_encode, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sha32_encode_fns[] = { + Opcode_ae_sha32_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldl32t_encode_fns[] = { + Opcode_ae_vldl32t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldl16t_encode_fns[] = { + Opcode_ae_vldl16t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldl16c_encode_fns[] = { + Opcode_ae_vldl16c_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vldsht_encode_fns[] = { + Opcode_ae_vldsht_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lb_encode_fns[] = { + Opcode_ae_lb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lbi_encode_fns[] = { + Opcode_ae_lbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lbk_encode_fns[] = { + Opcode_ae_lbk_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_lbki_encode_fns[] = { + Opcode_ae_lbki_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_db_encode_fns[] = { + Opcode_ae_db_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_dbi_encode_fns[] = { + Opcode_ae_dbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vlel32t_encode_fns[] = { + Opcode_ae_vlel32t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vlel16t_encode_fns[] = { + Opcode_ae_vlel16t_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sb_encode_fns[] = { + Opcode_ae_sb_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sbi_encode_fns[] = { + Opcode_ae_sbi_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_vles16c_encode_fns[] = { + Opcode_ae_vles16c_Slot_inst_encode, 0, 0, 0, 0 +}; + +static xtensa_opcode_encode_fn Opcode_ae_sbf_encode_fns[] = { + Opcode_ae_sbf_Slot_inst_encode, 0, 0, 0, 0 +}; + + +/* Opcode table. */ + +static xtensa_funcUnit_use Opcode_ae_vldl32t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vldl16t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vldl16c_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_shift32x5, 3 }, + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vldsht_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_shift32x5, 3 }, + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_lb_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_lbi_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_lbk_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_lbki_funcUnit_uses[] = { + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_db_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_dbi_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_vlel32t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_vlel16t_funcUnit_uses[] = { + { FUNCUNIT_ae_add32, 3 } +}; + +static xtensa_funcUnit_use Opcode_ae_sb_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_sbi_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_vles16c_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_funcUnit_use Opcode_ae_sbf_funcUnit_uses[] = { + { FUNCUNIT_ae_shift32x4, 2 }, + { FUNCUNIT_ae_subshift, 2 } +}; + +static xtensa_opcode_internal opcodes[] = { + { "excw", ICLASS_xt_iclass_excw, + 0, + Opcode_excw_encode_fns, 0, 0 }, + { "rfe", ICLASS_xt_iclass_rfe, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfe_encode_fns, 0, 0 }, + { "rfde", ICLASS_xt_iclass_rfde, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfde_encode_fns, 0, 0 }, + { "syscall", ICLASS_xt_iclass_syscall, + 0, + Opcode_syscall_encode_fns, 0, 0 }, + { "simcall", ICLASS_xt_iclass_simcall, + 0, + Opcode_simcall_encode_fns, 0, 0 }, + { "call12", ICLASS_xt_iclass_call12, + XTENSA_OPCODE_IS_CALL, + Opcode_call12_encode_fns, 0, 0 }, + { "call8", ICLASS_xt_iclass_call8, + XTENSA_OPCODE_IS_CALL, + Opcode_call8_encode_fns, 0, 0 }, + { "call4", ICLASS_xt_iclass_call4, + XTENSA_OPCODE_IS_CALL, + Opcode_call4_encode_fns, 0, 0 }, + { "callx12", ICLASS_xt_iclass_callx12, + XTENSA_OPCODE_IS_CALL, + Opcode_callx12_encode_fns, 0, 0 }, + { "callx8", ICLASS_xt_iclass_callx8, + XTENSA_OPCODE_IS_CALL, + Opcode_callx8_encode_fns, 0, 0 }, + { "callx4", ICLASS_xt_iclass_callx4, + XTENSA_OPCODE_IS_CALL, + Opcode_callx4_encode_fns, 0, 0 }, + { "entry", ICLASS_xt_iclass_entry, + 0, + Opcode_entry_encode_fns, 0, 0 }, + { "movsp", ICLASS_xt_iclass_movsp, + 0, + Opcode_movsp_encode_fns, 0, 0 }, + { "rotw", ICLASS_xt_iclass_rotw, + 0, + Opcode_rotw_encode_fns, 0, 0 }, + { "retw", ICLASS_xt_iclass_retw, + XTENSA_OPCODE_IS_JUMP, + Opcode_retw_encode_fns, 0, 0 }, + { "retw.n", ICLASS_xt_iclass_retw, + XTENSA_OPCODE_IS_JUMP, + Opcode_retw_n_encode_fns, 0, 0 }, + { "rfwo", ICLASS_xt_iclass_rfwou, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfwo_encode_fns, 0, 0 }, + { "rfwu", ICLASS_xt_iclass_rfwou, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfwu_encode_fns, 0, 0 }, + { "l32e", ICLASS_xt_iclass_l32e, + 0, + Opcode_l32e_encode_fns, 0, 0 }, + { "s32e", ICLASS_xt_iclass_s32e, + 0, + Opcode_s32e_encode_fns, 0, 0 }, + { "rsr.windowbase", ICLASS_xt_iclass_rsr_windowbase, + 0, + Opcode_rsr_windowbase_encode_fns, 0, 0 }, + { "wsr.windowbase", ICLASS_xt_iclass_wsr_windowbase, + 0, + Opcode_wsr_windowbase_encode_fns, 0, 0 }, + { "xsr.windowbase", ICLASS_xt_iclass_xsr_windowbase, + 0, + Opcode_xsr_windowbase_encode_fns, 0, 0 }, + { "rsr.windowstart", ICLASS_xt_iclass_rsr_windowstart, + 0, + Opcode_rsr_windowstart_encode_fns, 0, 0 }, + { "wsr.windowstart", ICLASS_xt_iclass_wsr_windowstart, + 0, + Opcode_wsr_windowstart_encode_fns, 0, 0 }, + { "xsr.windowstart", ICLASS_xt_iclass_xsr_windowstart, + 0, + Opcode_xsr_windowstart_encode_fns, 0, 0 }, + { "add.n", ICLASS_xt_iclass_add_n, + 0, + Opcode_add_n_encode_fns, 0, 0 }, + { "addi.n", ICLASS_xt_iclass_addi_n, + 0, + Opcode_addi_n_encode_fns, 0, 0 }, + { "beqz.n", ICLASS_xt_iclass_bz6, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beqz_n_encode_fns, 0, 0 }, + { "bnez.n", ICLASS_xt_iclass_bz6, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnez_n_encode_fns, 0, 0 }, + { "ill.n", ICLASS_xt_iclass_ill_n, + 0, + Opcode_ill_n_encode_fns, 0, 0 }, + { "l32i.n", ICLASS_xt_iclass_loadi4, + 0, + Opcode_l32i_n_encode_fns, 0, 0 }, + { "mov.n", ICLASS_xt_iclass_mov_n, + 0, + Opcode_mov_n_encode_fns, 0, 0 }, + { "movi.n", ICLASS_xt_iclass_movi_n, + 0, + Opcode_movi_n_encode_fns, 0, 0 }, + { "nop.n", ICLASS_xt_iclass_nopn, + 0, + Opcode_nop_n_encode_fns, 0, 0 }, + { "ret.n", ICLASS_xt_iclass_retn, + XTENSA_OPCODE_IS_JUMP, + Opcode_ret_n_encode_fns, 0, 0 }, + { "s32i.n", ICLASS_xt_iclass_storei4, + 0, + Opcode_s32i_n_encode_fns, 0, 0 }, + { "rur.threadptr", ICLASS_rur_threadptr, + 0, + Opcode_rur_threadptr_encode_fns, 0, 0 }, + { "wur.threadptr", ICLASS_wur_threadptr, + 0, + Opcode_wur_threadptr_encode_fns, 0, 0 }, + { "addi", ICLASS_xt_iclass_addi, + 0, + Opcode_addi_encode_fns, 0, 0 }, + { "addmi", ICLASS_xt_iclass_addmi, + 0, + Opcode_addmi_encode_fns, 0, 0 }, + { "add", ICLASS_xt_iclass_addsub, + 0, + Opcode_add_encode_fns, 0, 0 }, + { "sub", ICLASS_xt_iclass_addsub, + 0, + Opcode_sub_encode_fns, 0, 0 }, + { "addx2", ICLASS_xt_iclass_addsub, + 0, + Opcode_addx2_encode_fns, 0, 0 }, + { "addx4", ICLASS_xt_iclass_addsub, + 0, + Opcode_addx4_encode_fns, 0, 0 }, + { "addx8", ICLASS_xt_iclass_addsub, + 0, + Opcode_addx8_encode_fns, 0, 0 }, + { "subx2", ICLASS_xt_iclass_addsub, + 0, + Opcode_subx2_encode_fns, 0, 0 }, + { "subx4", ICLASS_xt_iclass_addsub, + 0, + Opcode_subx4_encode_fns, 0, 0 }, + { "subx8", ICLASS_xt_iclass_addsub, + 0, + Opcode_subx8_encode_fns, 0, 0 }, + { "and", ICLASS_xt_iclass_bit, + 0, + Opcode_and_encode_fns, 0, 0 }, + { "or", ICLASS_xt_iclass_bit, + 0, + Opcode_or_encode_fns, 0, 0 }, + { "xor", ICLASS_xt_iclass_bit, + 0, + Opcode_xor_encode_fns, 0, 0 }, + { "beqi", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beqi_encode_fns, 0, 0 }, + { "bnei", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnei_encode_fns, 0, 0 }, + { "bgei", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgei_encode_fns, 0, 0 }, + { "blti", ICLASS_xt_iclass_bsi8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_blti_encode_fns, 0, 0 }, + { "bbci", ICLASS_xt_iclass_bsi8b, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbci_encode_fns, 0, 0 }, + { "bbsi", ICLASS_xt_iclass_bsi8b, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbsi_encode_fns, 0, 0 }, + { "bgeui", ICLASS_xt_iclass_bsi8u, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgeui_encode_fns, 0, 0 }, + { "bltui", ICLASS_xt_iclass_bsi8u, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bltui_encode_fns, 0, 0 }, + { "beq", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beq_encode_fns, 0, 0 }, + { "bne", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bne_encode_fns, 0, 0 }, + { "bge", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bge_encode_fns, 0, 0 }, + { "blt", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_blt_encode_fns, 0, 0 }, + { "bgeu", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgeu_encode_fns, 0, 0 }, + { "bltu", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bltu_encode_fns, 0, 0 }, + { "bany", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bany_encode_fns, 0, 0 }, + { "bnone", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnone_encode_fns, 0, 0 }, + { "ball", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_ball_encode_fns, 0, 0 }, + { "bnall", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnall_encode_fns, 0, 0 }, + { "bbc", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbc_encode_fns, 0, 0 }, + { "bbs", ICLASS_xt_iclass_bst8, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bbs_encode_fns, 0, 0 }, + { "beqz", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_beqz_encode_fns, 0, 0 }, + { "bnez", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bnez_encode_fns, 0, 0 }, + { "bgez", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bgez_encode_fns, 0, 0 }, + { "bltz", ICLASS_xt_iclass_bsz12, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bltz_encode_fns, 0, 0 }, + { "call0", ICLASS_xt_iclass_call0, + XTENSA_OPCODE_IS_CALL, + Opcode_call0_encode_fns, 0, 0 }, + { "callx0", ICLASS_xt_iclass_callx0, + XTENSA_OPCODE_IS_CALL, + Opcode_callx0_encode_fns, 0, 0 }, + { "extui", ICLASS_xt_iclass_exti, + 0, + Opcode_extui_encode_fns, 0, 0 }, + { "ill", ICLASS_xt_iclass_ill, + 0, + Opcode_ill_encode_fns, 0, 0 }, + { "j", ICLASS_xt_iclass_jump, + XTENSA_OPCODE_IS_JUMP, + Opcode_j_encode_fns, 0, 0 }, + { "jx", ICLASS_xt_iclass_jumpx, + XTENSA_OPCODE_IS_JUMP, + Opcode_jx_encode_fns, 0, 0 }, + { "l16ui", ICLASS_xt_iclass_l16ui, + 0, + Opcode_l16ui_encode_fns, 0, 0 }, + { "l16si", ICLASS_xt_iclass_l16si, + 0, + Opcode_l16si_encode_fns, 0, 0 }, + { "l32i", ICLASS_xt_iclass_l32i, + 0, + Opcode_l32i_encode_fns, 0, 0 }, + { "l32r", ICLASS_xt_iclass_l32r, + 0, + Opcode_l32r_encode_fns, 0, 0 }, + { "l8ui", ICLASS_xt_iclass_l8i, + 0, + Opcode_l8ui_encode_fns, 0, 0 }, + { "loop", ICLASS_xt_iclass_loop, + XTENSA_OPCODE_IS_LOOP, + Opcode_loop_encode_fns, 0, 0 }, + { "loopnez", ICLASS_xt_iclass_loopz, + XTENSA_OPCODE_IS_LOOP, + Opcode_loopnez_encode_fns, 0, 0 }, + { "loopgtz", ICLASS_xt_iclass_loopz, + XTENSA_OPCODE_IS_LOOP, + Opcode_loopgtz_encode_fns, 0, 0 }, + { "movi", ICLASS_xt_iclass_movi, + 0, + Opcode_movi_encode_fns, 0, 0 }, + { "moveqz", ICLASS_xt_iclass_movz, + 0, + Opcode_moveqz_encode_fns, 0, 0 }, + { "movnez", ICLASS_xt_iclass_movz, + 0, + Opcode_movnez_encode_fns, 0, 0 }, + { "movltz", ICLASS_xt_iclass_movz, + 0, + Opcode_movltz_encode_fns, 0, 0 }, + { "movgez", ICLASS_xt_iclass_movz, + 0, + Opcode_movgez_encode_fns, 0, 0 }, + { "neg", ICLASS_xt_iclass_neg, + 0, + Opcode_neg_encode_fns, 0, 0 }, + { "abs", ICLASS_xt_iclass_neg, + 0, + Opcode_abs_encode_fns, 0, 0 }, + { "nop", ICLASS_xt_iclass_nop, + 0, + Opcode_nop_encode_fns, 0, 0 }, + { "ret", ICLASS_xt_iclass_return, + XTENSA_OPCODE_IS_JUMP, + Opcode_ret_encode_fns, 0, 0 }, + { "s16i", ICLASS_xt_iclass_s16i, + 0, + Opcode_s16i_encode_fns, 0, 0 }, + { "s32i", ICLASS_xt_iclass_s32i, + 0, + Opcode_s32i_encode_fns, 0, 0 }, + { "s8i", ICLASS_xt_iclass_s8i, + 0, + Opcode_s8i_encode_fns, 0, 0 }, + { "ssr", ICLASS_xt_iclass_sar, + 0, + Opcode_ssr_encode_fns, 0, 0 }, + { "ssl", ICLASS_xt_iclass_sar, + 0, + Opcode_ssl_encode_fns, 0, 0 }, + { "ssa8l", ICLASS_xt_iclass_sar, + 0, + Opcode_ssa8l_encode_fns, 0, 0 }, + { "ssa8b", ICLASS_xt_iclass_sar, + 0, + Opcode_ssa8b_encode_fns, 0, 0 }, + { "ssai", ICLASS_xt_iclass_sari, + 0, + Opcode_ssai_encode_fns, 0, 0 }, + { "sll", ICLASS_xt_iclass_shifts, + 0, + Opcode_sll_encode_fns, 0, 0 }, + { "src", ICLASS_xt_iclass_shiftst, + 0, + Opcode_src_encode_fns, 0, 0 }, + { "srl", ICLASS_xt_iclass_shiftt, + 0, + Opcode_srl_encode_fns, 0, 0 }, + { "sra", ICLASS_xt_iclass_shiftt, + 0, + Opcode_sra_encode_fns, 0, 0 }, + { "slli", ICLASS_xt_iclass_slli, + 0, + Opcode_slli_encode_fns, 0, 0 }, + { "srai", ICLASS_xt_iclass_srai, + 0, + Opcode_srai_encode_fns, 0, 0 }, + { "srli", ICLASS_xt_iclass_srli, + 0, + Opcode_srli_encode_fns, 0, 0 }, + { "memw", ICLASS_xt_iclass_memw, + 0, + Opcode_memw_encode_fns, 0, 0 }, + { "extw", ICLASS_xt_iclass_extw, + 0, + Opcode_extw_encode_fns, 0, 0 }, + { "isync", ICLASS_xt_iclass_isync, + 0, + Opcode_isync_encode_fns, 0, 0 }, + { "rsync", ICLASS_xt_iclass_sync, + 0, + Opcode_rsync_encode_fns, 0, 0 }, + { "esync", ICLASS_xt_iclass_sync, + 0, + Opcode_esync_encode_fns, 0, 0 }, + { "dsync", ICLASS_xt_iclass_sync, + 0, + Opcode_dsync_encode_fns, 0, 0 }, + { "rsil", ICLASS_xt_iclass_rsil, + 0, + Opcode_rsil_encode_fns, 0, 0 }, + { "rsr.lend", ICLASS_xt_iclass_rsr_lend, + 0, + Opcode_rsr_lend_encode_fns, 0, 0 }, + { "wsr.lend", ICLASS_xt_iclass_wsr_lend, + 0, + Opcode_wsr_lend_encode_fns, 0, 0 }, + { "xsr.lend", ICLASS_xt_iclass_xsr_lend, + 0, + Opcode_xsr_lend_encode_fns, 0, 0 }, + { "rsr.lcount", ICLASS_xt_iclass_rsr_lcount, + 0, + Opcode_rsr_lcount_encode_fns, 0, 0 }, + { "wsr.lcount", ICLASS_xt_iclass_wsr_lcount, + 0, + Opcode_wsr_lcount_encode_fns, 0, 0 }, + { "xsr.lcount", ICLASS_xt_iclass_xsr_lcount, + 0, + Opcode_xsr_lcount_encode_fns, 0, 0 }, + { "rsr.lbeg", ICLASS_xt_iclass_rsr_lbeg, + 0, + Opcode_rsr_lbeg_encode_fns, 0, 0 }, + { "wsr.lbeg", ICLASS_xt_iclass_wsr_lbeg, + 0, + Opcode_wsr_lbeg_encode_fns, 0, 0 }, + { "xsr.lbeg", ICLASS_xt_iclass_xsr_lbeg, + 0, + Opcode_xsr_lbeg_encode_fns, 0, 0 }, + { "rsr.sar", ICLASS_xt_iclass_rsr_sar, + 0, + Opcode_rsr_sar_encode_fns, 0, 0 }, + { "wsr.sar", ICLASS_xt_iclass_wsr_sar, + 0, + Opcode_wsr_sar_encode_fns, 0, 0 }, + { "xsr.sar", ICLASS_xt_iclass_xsr_sar, + 0, + Opcode_xsr_sar_encode_fns, 0, 0 }, + { "rsr.litbase", ICLASS_xt_iclass_rsr_litbase, + 0, + Opcode_rsr_litbase_encode_fns, 0, 0 }, + { "wsr.litbase", ICLASS_xt_iclass_wsr_litbase, + 0, + Opcode_wsr_litbase_encode_fns, 0, 0 }, + { "xsr.litbase", ICLASS_xt_iclass_xsr_litbase, + 0, + Opcode_xsr_litbase_encode_fns, 0, 0 }, + { "rsr.176", ICLASS_xt_iclass_rsr_176, + 0, + Opcode_rsr_176_encode_fns, 0, 0 }, + { "wsr.176", ICLASS_xt_iclass_wsr_176, + 0, + Opcode_wsr_176_encode_fns, 0, 0 }, + { "rsr.208", ICLASS_xt_iclass_rsr_208, + 0, + Opcode_rsr_208_encode_fns, 0, 0 }, + { "rsr.ps", ICLASS_xt_iclass_rsr_ps, + 0, + Opcode_rsr_ps_encode_fns, 0, 0 }, + { "wsr.ps", ICLASS_xt_iclass_wsr_ps, + 0, + Opcode_wsr_ps_encode_fns, 0, 0 }, + { "xsr.ps", ICLASS_xt_iclass_xsr_ps, + 0, + Opcode_xsr_ps_encode_fns, 0, 0 }, + { "rsr.epc1", ICLASS_xt_iclass_rsr_epc1, + 0, + Opcode_rsr_epc1_encode_fns, 0, 0 }, + { "wsr.epc1", ICLASS_xt_iclass_wsr_epc1, + 0, + Opcode_wsr_epc1_encode_fns, 0, 0 }, + { "xsr.epc1", ICLASS_xt_iclass_xsr_epc1, + 0, + Opcode_xsr_epc1_encode_fns, 0, 0 }, + { "rsr.excsave1", ICLASS_xt_iclass_rsr_excsave1, + 0, + Opcode_rsr_excsave1_encode_fns, 0, 0 }, + { "wsr.excsave1", ICLASS_xt_iclass_wsr_excsave1, + 0, + Opcode_wsr_excsave1_encode_fns, 0, 0 }, + { "xsr.excsave1", ICLASS_xt_iclass_xsr_excsave1, + 0, + Opcode_xsr_excsave1_encode_fns, 0, 0 }, + { "rsr.epc2", ICLASS_xt_iclass_rsr_epc2, + 0, + Opcode_rsr_epc2_encode_fns, 0, 0 }, + { "wsr.epc2", ICLASS_xt_iclass_wsr_epc2, + 0, + Opcode_wsr_epc2_encode_fns, 0, 0 }, + { "xsr.epc2", ICLASS_xt_iclass_xsr_epc2, + 0, + Opcode_xsr_epc2_encode_fns, 0, 0 }, + { "rsr.excsave2", ICLASS_xt_iclass_rsr_excsave2, + 0, + Opcode_rsr_excsave2_encode_fns, 0, 0 }, + { "wsr.excsave2", ICLASS_xt_iclass_wsr_excsave2, + 0, + Opcode_wsr_excsave2_encode_fns, 0, 0 }, + { "xsr.excsave2", ICLASS_xt_iclass_xsr_excsave2, + 0, + Opcode_xsr_excsave2_encode_fns, 0, 0 }, + { "rsr.eps2", ICLASS_xt_iclass_rsr_eps2, + 0, + Opcode_rsr_eps2_encode_fns, 0, 0 }, + { "wsr.eps2", ICLASS_xt_iclass_wsr_eps2, + 0, + Opcode_wsr_eps2_encode_fns, 0, 0 }, + { "xsr.eps2", ICLASS_xt_iclass_xsr_eps2, + 0, + Opcode_xsr_eps2_encode_fns, 0, 0 }, + { "rsr.excvaddr", ICLASS_xt_iclass_rsr_excvaddr, + 0, + Opcode_rsr_excvaddr_encode_fns, 0, 0 }, + { "wsr.excvaddr", ICLASS_xt_iclass_wsr_excvaddr, + 0, + Opcode_wsr_excvaddr_encode_fns, 0, 0 }, + { "xsr.excvaddr", ICLASS_xt_iclass_xsr_excvaddr, + 0, + Opcode_xsr_excvaddr_encode_fns, 0, 0 }, + { "rsr.depc", ICLASS_xt_iclass_rsr_depc, + 0, + Opcode_rsr_depc_encode_fns, 0, 0 }, + { "wsr.depc", ICLASS_xt_iclass_wsr_depc, + 0, + Opcode_wsr_depc_encode_fns, 0, 0 }, + { "xsr.depc", ICLASS_xt_iclass_xsr_depc, + 0, + Opcode_xsr_depc_encode_fns, 0, 0 }, + { "rsr.exccause", ICLASS_xt_iclass_rsr_exccause, + 0, + Opcode_rsr_exccause_encode_fns, 0, 0 }, + { "wsr.exccause", ICLASS_xt_iclass_wsr_exccause, + 0, + Opcode_wsr_exccause_encode_fns, 0, 0 }, + { "xsr.exccause", ICLASS_xt_iclass_xsr_exccause, + 0, + Opcode_xsr_exccause_encode_fns, 0, 0 }, + { "rsr.misc0", ICLASS_xt_iclass_rsr_misc0, + 0, + Opcode_rsr_misc0_encode_fns, 0, 0 }, + { "wsr.misc0", ICLASS_xt_iclass_wsr_misc0, + 0, + Opcode_wsr_misc0_encode_fns, 0, 0 }, + { "xsr.misc0", ICLASS_xt_iclass_xsr_misc0, + 0, + Opcode_xsr_misc0_encode_fns, 0, 0 }, + { "rsr.misc1", ICLASS_xt_iclass_rsr_misc1, + 0, + Opcode_rsr_misc1_encode_fns, 0, 0 }, + { "wsr.misc1", ICLASS_xt_iclass_wsr_misc1, + 0, + Opcode_wsr_misc1_encode_fns, 0, 0 }, + { "xsr.misc1", ICLASS_xt_iclass_xsr_misc1, + 0, + Opcode_xsr_misc1_encode_fns, 0, 0 }, + { "rsr.prid", ICLASS_xt_iclass_rsr_prid, + 0, + Opcode_rsr_prid_encode_fns, 0, 0 }, + { "rsr.vecbase", ICLASS_xt_iclass_rsr_vecbase, + 0, + Opcode_rsr_vecbase_encode_fns, 0, 0 }, + { "wsr.vecbase", ICLASS_xt_iclass_wsr_vecbase, + 0, + Opcode_wsr_vecbase_encode_fns, 0, 0 }, + { "xsr.vecbase", ICLASS_xt_iclass_xsr_vecbase, + 0, + Opcode_xsr_vecbase_encode_fns, 0, 0 }, + { "mul16u", ICLASS_xt_mul16, + 0, + Opcode_mul16u_encode_fns, 0, 0 }, + { "mul16s", ICLASS_xt_mul16, + 0, + Opcode_mul16s_encode_fns, 0, 0 }, + { "mull", ICLASS_xt_mul32, + 0, + Opcode_mull_encode_fns, 0, 0 }, + { "rfi", ICLASS_xt_iclass_rfi, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfi_encode_fns, 0, 0 }, + { "waiti", ICLASS_xt_iclass_wait, + 0, + Opcode_waiti_encode_fns, 0, 0 }, + { "rsr.interrupt", ICLASS_xt_iclass_rsr_interrupt, + 0, + Opcode_rsr_interrupt_encode_fns, 0, 0 }, + { "wsr.intset", ICLASS_xt_iclass_wsr_intset, + 0, + Opcode_wsr_intset_encode_fns, 0, 0 }, + { "wsr.intclear", ICLASS_xt_iclass_wsr_intclear, + 0, + Opcode_wsr_intclear_encode_fns, 0, 0 }, + { "rsr.intenable", ICLASS_xt_iclass_rsr_intenable, + 0, + Opcode_rsr_intenable_encode_fns, 0, 0 }, + { "wsr.intenable", ICLASS_xt_iclass_wsr_intenable, + 0, + Opcode_wsr_intenable_encode_fns, 0, 0 }, + { "xsr.intenable", ICLASS_xt_iclass_xsr_intenable, + 0, + Opcode_xsr_intenable_encode_fns, 0, 0 }, + { "break", ICLASS_xt_iclass_break, + 0, + Opcode_break_encode_fns, 0, 0 }, + { "break.n", ICLASS_xt_iclass_break_n, + 0, + Opcode_break_n_encode_fns, 0, 0 }, + { "rsr.debugcause", ICLASS_xt_iclass_rsr_debugcause, + 0, + Opcode_rsr_debugcause_encode_fns, 0, 0 }, + { "wsr.debugcause", ICLASS_xt_iclass_wsr_debugcause, + 0, + Opcode_wsr_debugcause_encode_fns, 0, 0 }, + { "xsr.debugcause", ICLASS_xt_iclass_xsr_debugcause, + 0, + Opcode_xsr_debugcause_encode_fns, 0, 0 }, + { "rsr.icount", ICLASS_xt_iclass_rsr_icount, + 0, + Opcode_rsr_icount_encode_fns, 0, 0 }, + { "wsr.icount", ICLASS_xt_iclass_wsr_icount, + 0, + Opcode_wsr_icount_encode_fns, 0, 0 }, + { "xsr.icount", ICLASS_xt_iclass_xsr_icount, + 0, + Opcode_xsr_icount_encode_fns, 0, 0 }, + { "rsr.icountlevel", ICLASS_xt_iclass_rsr_icountlevel, + 0, + Opcode_rsr_icountlevel_encode_fns, 0, 0 }, + { "wsr.icountlevel", ICLASS_xt_iclass_wsr_icountlevel, + 0, + Opcode_wsr_icountlevel_encode_fns, 0, 0 }, + { "xsr.icountlevel", ICLASS_xt_iclass_xsr_icountlevel, + 0, + Opcode_xsr_icountlevel_encode_fns, 0, 0 }, + { "rsr.ddr", ICLASS_xt_iclass_rsr_ddr, + 0, + Opcode_rsr_ddr_encode_fns, 0, 0 }, + { "wsr.ddr", ICLASS_xt_iclass_wsr_ddr, + 0, + Opcode_wsr_ddr_encode_fns, 0, 0 }, + { "xsr.ddr", ICLASS_xt_iclass_xsr_ddr, + 0, + Opcode_xsr_ddr_encode_fns, 0, 0 }, + { "rfdo", ICLASS_xt_iclass_rfdo, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfdo_encode_fns, 0, 0 }, + { "rfdd", ICLASS_xt_iclass_rfdd, + XTENSA_OPCODE_IS_JUMP, + Opcode_rfdd_encode_fns, 0, 0 }, + { "andb", ICLASS_xt_iclass_bbool1, + 0, + Opcode_andb_encode_fns, 0, 0 }, + { "andbc", ICLASS_xt_iclass_bbool1, + 0, + Opcode_andbc_encode_fns, 0, 0 }, + { "orb", ICLASS_xt_iclass_bbool1, + 0, + Opcode_orb_encode_fns, 0, 0 }, + { "orbc", ICLASS_xt_iclass_bbool1, + 0, + Opcode_orbc_encode_fns, 0, 0 }, + { "xorb", ICLASS_xt_iclass_bbool1, + 0, + Opcode_xorb_encode_fns, 0, 0 }, + { "any4", ICLASS_xt_iclass_bbool4, + 0, + Opcode_any4_encode_fns, 0, 0 }, + { "all4", ICLASS_xt_iclass_bbool4, + 0, + Opcode_all4_encode_fns, 0, 0 }, + { "any8", ICLASS_xt_iclass_bbool8, + 0, + Opcode_any8_encode_fns, 0, 0 }, + { "all8", ICLASS_xt_iclass_bbool8, + 0, + Opcode_all8_encode_fns, 0, 0 }, + { "bf", ICLASS_xt_iclass_bbranch, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bf_encode_fns, 0, 0 }, + { "bt", ICLASS_xt_iclass_bbranch, + XTENSA_OPCODE_IS_BRANCH, + Opcode_bt_encode_fns, 0, 0 }, + { "movf", ICLASS_xt_iclass_bmove, + 0, + Opcode_movf_encode_fns, 0, 0 }, + { "movt", ICLASS_xt_iclass_bmove, + 0, + Opcode_movt_encode_fns, 0, 0 }, + { "rsr.br", ICLASS_xt_iclass_RSR_BR, + 0, + Opcode_rsr_br_encode_fns, 0, 0 }, + { "wsr.br", ICLASS_xt_iclass_WSR_BR, + 0, + Opcode_wsr_br_encode_fns, 0, 0 }, + { "xsr.br", ICLASS_xt_iclass_XSR_BR, + 0, + Opcode_xsr_br_encode_fns, 0, 0 }, + { "rsr.ccount", ICLASS_xt_iclass_rsr_ccount, + 0, + Opcode_rsr_ccount_encode_fns, 0, 0 }, + { "wsr.ccount", ICLASS_xt_iclass_wsr_ccount, + 0, + Opcode_wsr_ccount_encode_fns, 0, 0 }, + { "xsr.ccount", ICLASS_xt_iclass_xsr_ccount, + 0, + Opcode_xsr_ccount_encode_fns, 0, 0 }, + { "rsr.ccompare0", ICLASS_xt_iclass_rsr_ccompare0, + 0, + Opcode_rsr_ccompare0_encode_fns, 0, 0 }, + { "wsr.ccompare0", ICLASS_xt_iclass_wsr_ccompare0, + 0, + Opcode_wsr_ccompare0_encode_fns, 0, 0 }, + { "xsr.ccompare0", ICLASS_xt_iclass_xsr_ccompare0, + 0, + Opcode_xsr_ccompare0_encode_fns, 0, 0 }, + { "rsr.ccompare1", ICLASS_xt_iclass_rsr_ccompare1, + 0, + Opcode_rsr_ccompare1_encode_fns, 0, 0 }, + { "wsr.ccompare1", ICLASS_xt_iclass_wsr_ccompare1, + 0, + Opcode_wsr_ccompare1_encode_fns, 0, 0 }, + { "xsr.ccompare1", ICLASS_xt_iclass_xsr_ccompare1, + 0, + Opcode_xsr_ccompare1_encode_fns, 0, 0 }, + { "ipf", ICLASS_xt_iclass_icache, + 0, + Opcode_ipf_encode_fns, 0, 0 }, + { "ihi", ICLASS_xt_iclass_icache, + 0, + Opcode_ihi_encode_fns, 0, 0 }, + { "iii", ICLASS_xt_iclass_icache_inv, + 0, + Opcode_iii_encode_fns, 0, 0 }, + { "lict", ICLASS_xt_iclass_licx, + 0, + Opcode_lict_encode_fns, 0, 0 }, + { "licw", ICLASS_xt_iclass_licx, + 0, + Opcode_licw_encode_fns, 0, 0 }, + { "sict", ICLASS_xt_iclass_sicx, + 0, + Opcode_sict_encode_fns, 0, 0 }, + { "sicw", ICLASS_xt_iclass_sicx, + 0, + Opcode_sicw_encode_fns, 0, 0 }, + { "dhwb", ICLASS_xt_iclass_dcache, + 0, + Opcode_dhwb_encode_fns, 0, 0 }, + { "dhwbi", ICLASS_xt_iclass_dcache, + 0, + Opcode_dhwbi_encode_fns, 0, 0 }, + { "diwb", ICLASS_xt_iclass_dcache_ind, + 0, + Opcode_diwb_encode_fns, 0, 0 }, + { "diwbi", ICLASS_xt_iclass_dcache_ind, + 0, + Opcode_diwbi_encode_fns, 0, 0 }, + { "dhi", ICLASS_xt_iclass_dcache_inv, + 0, + Opcode_dhi_encode_fns, 0, 0 }, + { "dii", ICLASS_xt_iclass_dcache_inv, + 0, + Opcode_dii_encode_fns, 0, 0 }, + { "dpfr", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfr_encode_fns, 0, 0 }, + { "dpfw", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfw_encode_fns, 0, 0 }, + { "dpfro", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfro_encode_fns, 0, 0 }, + { "dpfwo", ICLASS_xt_iclass_dpf, + 0, + Opcode_dpfwo_encode_fns, 0, 0 }, + { "sdct", ICLASS_xt_iclass_sdct, + 0, + Opcode_sdct_encode_fns, 0, 0 }, + { "ldct", ICLASS_xt_iclass_ldct, + 0, + Opcode_ldct_encode_fns, 0, 0 }, + { "wsr.ptevaddr", ICLASS_xt_iclass_wsr_ptevaddr, + 0, + Opcode_wsr_ptevaddr_encode_fns, 0, 0 }, + { "rsr.ptevaddr", ICLASS_xt_iclass_rsr_ptevaddr, + 0, + Opcode_rsr_ptevaddr_encode_fns, 0, 0 }, + { "xsr.ptevaddr", ICLASS_xt_iclass_xsr_ptevaddr, + 0, + Opcode_xsr_ptevaddr_encode_fns, 0, 0 }, + { "rsr.rasid", ICLASS_xt_iclass_rsr_rasid, + 0, + Opcode_rsr_rasid_encode_fns, 0, 0 }, + { "wsr.rasid", ICLASS_xt_iclass_wsr_rasid, + 0, + Opcode_wsr_rasid_encode_fns, 0, 0 }, + { "xsr.rasid", ICLASS_xt_iclass_xsr_rasid, + 0, + Opcode_xsr_rasid_encode_fns, 0, 0 }, + { "rsr.itlbcfg", ICLASS_xt_iclass_rsr_itlbcfg, + 0, + Opcode_rsr_itlbcfg_encode_fns, 0, 0 }, + { "wsr.itlbcfg", ICLASS_xt_iclass_wsr_itlbcfg, + 0, + Opcode_wsr_itlbcfg_encode_fns, 0, 0 }, + { "xsr.itlbcfg", ICLASS_xt_iclass_xsr_itlbcfg, + 0, + Opcode_xsr_itlbcfg_encode_fns, 0, 0 }, + { "rsr.dtlbcfg", ICLASS_xt_iclass_rsr_dtlbcfg, + 0, + Opcode_rsr_dtlbcfg_encode_fns, 0, 0 }, + { "wsr.dtlbcfg", ICLASS_xt_iclass_wsr_dtlbcfg, + 0, + Opcode_wsr_dtlbcfg_encode_fns, 0, 0 }, + { "xsr.dtlbcfg", ICLASS_xt_iclass_xsr_dtlbcfg, + 0, + Opcode_xsr_dtlbcfg_encode_fns, 0, 0 }, + { "idtlb", ICLASS_xt_iclass_idtlb, + 0, + Opcode_idtlb_encode_fns, 0, 0 }, + { "pdtlb", ICLASS_xt_iclass_rdtlb, + 0, + Opcode_pdtlb_encode_fns, 0, 0 }, + { "rdtlb0", ICLASS_xt_iclass_rdtlb, + 0, + Opcode_rdtlb0_encode_fns, 0, 0 }, + { "rdtlb1", ICLASS_xt_iclass_rdtlb, + 0, + Opcode_rdtlb1_encode_fns, 0, 0 }, + { "wdtlb", ICLASS_xt_iclass_wdtlb, + 0, + Opcode_wdtlb_encode_fns, 0, 0 }, + { "iitlb", ICLASS_xt_iclass_iitlb, + 0, + Opcode_iitlb_encode_fns, 0, 0 }, + { "pitlb", ICLASS_xt_iclass_ritlb, + 0, + Opcode_pitlb_encode_fns, 0, 0 }, + { "ritlb0", ICLASS_xt_iclass_ritlb, + 0, + Opcode_ritlb0_encode_fns, 0, 0 }, + { "ritlb1", ICLASS_xt_iclass_ritlb, + 0, + Opcode_ritlb1_encode_fns, 0, 0 }, + { "witlb", ICLASS_xt_iclass_witlb, + 0, + Opcode_witlb_encode_fns, 0, 0 }, + { "ldpte", ICLASS_xt_iclass_ldpte, + 0, + Opcode_ldpte_encode_fns, 0, 0 }, + { "hwwitlba", ICLASS_xt_iclass_hwwitlba, + XTENSA_OPCODE_IS_BRANCH, + Opcode_hwwitlba_encode_fns, 0, 0 }, + { "hwwdtlba", ICLASS_xt_iclass_hwwdtlba, + 0, + Opcode_hwwdtlba_encode_fns, 0, 0 }, + { "rsr.cpenable", ICLASS_xt_iclass_rsr_cpenable, + 0, + Opcode_rsr_cpenable_encode_fns, 0, 0 }, + { "wsr.cpenable", ICLASS_xt_iclass_wsr_cpenable, + 0, + Opcode_wsr_cpenable_encode_fns, 0, 0 }, + { "xsr.cpenable", ICLASS_xt_iclass_xsr_cpenable, + 0, + Opcode_xsr_cpenable_encode_fns, 0, 0 }, + { "clamps", ICLASS_xt_iclass_clamp, + 0, + Opcode_clamps_encode_fns, 0, 0 }, + { "min", ICLASS_xt_iclass_minmax, + 0, + Opcode_min_encode_fns, 0, 0 }, + { "max", ICLASS_xt_iclass_minmax, + 0, + Opcode_max_encode_fns, 0, 0 }, + { "minu", ICLASS_xt_iclass_minmax, + 0, + Opcode_minu_encode_fns, 0, 0 }, + { "maxu", ICLASS_xt_iclass_minmax, + 0, + Opcode_maxu_encode_fns, 0, 0 }, + { "nsa", ICLASS_xt_iclass_nsa, + 0, + Opcode_nsa_encode_fns, 0, 0 }, + { "nsau", ICLASS_xt_iclass_nsa, + 0, + Opcode_nsau_encode_fns, 0, 0 }, + { "sext", ICLASS_xt_iclass_sx, + 0, + Opcode_sext_encode_fns, 0, 0 }, + { "l32ai", ICLASS_xt_iclass_l32ai, + 0, + Opcode_l32ai_encode_fns, 0, 0 }, + { "s32ri", ICLASS_xt_iclass_s32ri, + 0, + Opcode_s32ri_encode_fns, 0, 0 }, + { "s32c1i", ICLASS_xt_iclass_s32c1i, + 0, + Opcode_s32c1i_encode_fns, 0, 0 }, + { "rsr.scompare1", ICLASS_xt_iclass_rsr_scompare1, + 0, + Opcode_rsr_scompare1_encode_fns, 0, 0 }, + { "wsr.scompare1", ICLASS_xt_iclass_wsr_scompare1, + 0, + Opcode_wsr_scompare1_encode_fns, 0, 0 }, + { "xsr.scompare1", ICLASS_xt_iclass_xsr_scompare1, + 0, + Opcode_xsr_scompare1_encode_fns, 0, 0 }, + { "rsr.atomctl", ICLASS_xt_iclass_rsr_atomctl, + 0, + Opcode_rsr_atomctl_encode_fns, 0, 0 }, + { "wsr.atomctl", ICLASS_xt_iclass_wsr_atomctl, + 0, + Opcode_wsr_atomctl_encode_fns, 0, 0 }, + { "xsr.atomctl", ICLASS_xt_iclass_xsr_atomctl, + 0, + Opcode_xsr_atomctl_encode_fns, 0, 0 }, + { "rer", ICLASS_xt_iclass_rer, + 0, + Opcode_rer_encode_fns, 0, 0 }, + { "wer", ICLASS_xt_iclass_wer, + 0, + Opcode_wer_encode_fns, 0, 0 }, + { "rur.ae_ovf_sar", ICLASS_rur_ae_ovf_sar, + 0, + Opcode_rur_ae_ovf_sar_encode_fns, 0, 0 }, + { "wur.ae_ovf_sar", ICLASS_wur_ae_ovf_sar, + 0, + Opcode_wur_ae_ovf_sar_encode_fns, 0, 0 }, + { "rur.ae_bithead", ICLASS_rur_ae_bithead, + 0, + Opcode_rur_ae_bithead_encode_fns, 0, 0 }, + { "wur.ae_bithead", ICLASS_wur_ae_bithead, + 0, + Opcode_wur_ae_bithead_encode_fns, 0, 0 }, + { "rur.ae_ts_fts_bu_bp", ICLASS_rur_ae_ts_fts_bu_bp, + 0, + Opcode_rur_ae_ts_fts_bu_bp_encode_fns, 0, 0 }, + { "wur.ae_ts_fts_bu_bp", ICLASS_wur_ae_ts_fts_bu_bp, + 0, + Opcode_wur_ae_ts_fts_bu_bp_encode_fns, 0, 0 }, + { "rur.ae_sd_no", ICLASS_rur_ae_sd_no, + 0, + Opcode_rur_ae_sd_no_encode_fns, 0, 0 }, + { "wur.ae_sd_no", ICLASS_wur_ae_sd_no, + 0, + Opcode_wur_ae_sd_no_encode_fns, 0, 0 }, + { "rur.ae_overflow", ICLASS_ae_iclass_rur_ae_overflow, + 0, + Opcode_rur_ae_overflow_encode_fns, 0, 0 }, + { "wur.ae_overflow", ICLASS_ae_iclass_wur_ae_overflow, + 0, + Opcode_wur_ae_overflow_encode_fns, 0, 0 }, + { "rur.ae_sar", ICLASS_ae_iclass_rur_ae_sar, + 0, + Opcode_rur_ae_sar_encode_fns, 0, 0 }, + { "wur.ae_sar", ICLASS_ae_iclass_wur_ae_sar, + 0, + Opcode_wur_ae_sar_encode_fns, 0, 0 }, + { "rur.ae_bitptr", ICLASS_ae_iclass_rur_ae_bitptr, + 0, + Opcode_rur_ae_bitptr_encode_fns, 0, 0 }, + { "wur.ae_bitptr", ICLASS_ae_iclass_wur_ae_bitptr, + 0, + Opcode_wur_ae_bitptr_encode_fns, 0, 0 }, + { "rur.ae_bitsused", ICLASS_ae_iclass_rur_ae_bitsused, + 0, + Opcode_rur_ae_bitsused_encode_fns, 0, 0 }, + { "wur.ae_bitsused", ICLASS_ae_iclass_wur_ae_bitsused, + 0, + Opcode_wur_ae_bitsused_encode_fns, 0, 0 }, + { "rur.ae_tablesize", ICLASS_ae_iclass_rur_ae_tablesize, + 0, + Opcode_rur_ae_tablesize_encode_fns, 0, 0 }, + { "wur.ae_tablesize", ICLASS_ae_iclass_wur_ae_tablesize, + 0, + Opcode_wur_ae_tablesize_encode_fns, 0, 0 }, + { "rur.ae_first_ts", ICLASS_ae_iclass_rur_ae_first_ts, + 0, + Opcode_rur_ae_first_ts_encode_fns, 0, 0 }, + { "wur.ae_first_ts", ICLASS_ae_iclass_wur_ae_first_ts, + 0, + Opcode_wur_ae_first_ts_encode_fns, 0, 0 }, + { "rur.ae_nextoffset", ICLASS_ae_iclass_rur_ae_nextoffset, + 0, + Opcode_rur_ae_nextoffset_encode_fns, 0, 0 }, + { "wur.ae_nextoffset", ICLASS_ae_iclass_wur_ae_nextoffset, + 0, + Opcode_wur_ae_nextoffset_encode_fns, 0, 0 }, + { "rur.ae_searchdone", ICLASS_ae_iclass_rur_ae_searchdone, + 0, + Opcode_rur_ae_searchdone_encode_fns, 0, 0 }, + { "wur.ae_searchdone", ICLASS_ae_iclass_wur_ae_searchdone, + 0, + Opcode_wur_ae_searchdone_encode_fns, 0, 0 }, + { "ae_lp16f.i", ICLASS_ae_iclass_lp16f_i, + 0, + Opcode_ae_lp16f_i_encode_fns, 0, 0 }, + { "ae_lp16f.iu", ICLASS_ae_iclass_lp16f_iu, + 0, + Opcode_ae_lp16f_iu_encode_fns, 0, 0 }, + { "ae_lp16f.x", ICLASS_ae_iclass_lp16f_x, + 0, + Opcode_ae_lp16f_x_encode_fns, 0, 0 }, + { "ae_lp16f.xu", ICLASS_ae_iclass_lp16f_xu, + 0, + Opcode_ae_lp16f_xu_encode_fns, 0, 0 }, + { "ae_lp24.i", ICLASS_ae_iclass_lp24_i, + 0, + Opcode_ae_lp24_i_encode_fns, 0, 0 }, + { "ae_lp24.iu", ICLASS_ae_iclass_lp24_iu, + 0, + Opcode_ae_lp24_iu_encode_fns, 0, 0 }, + { "ae_lp24.x", ICLASS_ae_iclass_lp24_x, + 0, + Opcode_ae_lp24_x_encode_fns, 0, 0 }, + { "ae_lp24.xu", ICLASS_ae_iclass_lp24_xu, + 0, + Opcode_ae_lp24_xu_encode_fns, 0, 0 }, + { "ae_lp24f.i", ICLASS_ae_iclass_lp24f_i, + 0, + Opcode_ae_lp24f_i_encode_fns, 0, 0 }, + { "ae_lp24f.iu", ICLASS_ae_iclass_lp24f_iu, + 0, + Opcode_ae_lp24f_iu_encode_fns, 0, 0 }, + { "ae_lp24f.x", ICLASS_ae_iclass_lp24f_x, + 0, + Opcode_ae_lp24f_x_encode_fns, 0, 0 }, + { "ae_lp24f.xu", ICLASS_ae_iclass_lp24f_xu, + 0, + Opcode_ae_lp24f_xu_encode_fns, 0, 0 }, + { "ae_lp16x2f.i", ICLASS_ae_iclass_lp16x2f_i, + 0, + Opcode_ae_lp16x2f_i_encode_fns, 0, 0 }, + { "ae_lp16x2f.iu", ICLASS_ae_iclass_lp16x2f_iu, + 0, + Opcode_ae_lp16x2f_iu_encode_fns, 0, 0 }, + { "ae_lp16x2f.x", ICLASS_ae_iclass_lp16x2f_x, + 0, + Opcode_ae_lp16x2f_x_encode_fns, 0, 0 }, + { "ae_lp16x2f.xu", ICLASS_ae_iclass_lp16x2f_xu, + 0, + Opcode_ae_lp16x2f_xu_encode_fns, 0, 0 }, + { "ae_lp24x2f.i", ICLASS_ae_iclass_lp24x2f_i, + 0, + Opcode_ae_lp24x2f_i_encode_fns, 0, 0 }, + { "ae_lp24x2f.iu", ICLASS_ae_iclass_lp24x2f_iu, + 0, + Opcode_ae_lp24x2f_iu_encode_fns, 0, 0 }, + { "ae_lp24x2f.x", ICLASS_ae_iclass_lp24x2f_x, + 0, + Opcode_ae_lp24x2f_x_encode_fns, 0, 0 }, + { "ae_lp24x2f.xu", ICLASS_ae_iclass_lp24x2f_xu, + 0, + Opcode_ae_lp24x2f_xu_encode_fns, 0, 0 }, + { "ae_lp24x2.i", ICLASS_ae_iclass_lp24x2_i, + 0, + Opcode_ae_lp24x2_i_encode_fns, 0, 0 }, + { "ae_lp24x2.iu", ICLASS_ae_iclass_lp24x2_iu, + 0, + Opcode_ae_lp24x2_iu_encode_fns, 0, 0 }, + { "ae_lp24x2.x", ICLASS_ae_iclass_lp24x2_x, + 0, + Opcode_ae_lp24x2_x_encode_fns, 0, 0 }, + { "ae_lp24x2.xu", ICLASS_ae_iclass_lp24x2_xu, + 0, + Opcode_ae_lp24x2_xu_encode_fns, 0, 0 }, + { "ae_sp16x2f.i", ICLASS_ae_iclass_sp16x2f_i, + 0, + Opcode_ae_sp16x2f_i_encode_fns, 0, 0 }, + { "ae_sp16x2f.iu", ICLASS_ae_iclass_sp16x2f_iu, + 0, + Opcode_ae_sp16x2f_iu_encode_fns, 0, 0 }, + { "ae_sp16x2f.x", ICLASS_ae_iclass_sp16x2f_x, + 0, + Opcode_ae_sp16x2f_x_encode_fns, 0, 0 }, + { "ae_sp16x2f.xu", ICLASS_ae_iclass_sp16x2f_xu, + 0, + Opcode_ae_sp16x2f_xu_encode_fns, 0, 0 }, + { "ae_sp24x2s.i", ICLASS_ae_iclass_sp24x2s_i, + 0, + Opcode_ae_sp24x2s_i_encode_fns, 0, 0 }, + { "ae_sp24x2s.iu", ICLASS_ae_iclass_sp24x2s_iu, + 0, + Opcode_ae_sp24x2s_iu_encode_fns, 0, 0 }, + { "ae_sp24x2s.x", ICLASS_ae_iclass_sp24x2s_x, + 0, + Opcode_ae_sp24x2s_x_encode_fns, 0, 0 }, + { "ae_sp24x2s.xu", ICLASS_ae_iclass_sp24x2s_xu, + 0, + Opcode_ae_sp24x2s_xu_encode_fns, 0, 0 }, + { "ae_sp24x2f.i", ICLASS_ae_iclass_sp24x2f_i, + 0, + Opcode_ae_sp24x2f_i_encode_fns, 0, 0 }, + { "ae_sp24x2f.iu", ICLASS_ae_iclass_sp24x2f_iu, + 0, + Opcode_ae_sp24x2f_iu_encode_fns, 0, 0 }, + { "ae_sp24x2f.x", ICLASS_ae_iclass_sp24x2f_x, + 0, + Opcode_ae_sp24x2f_x_encode_fns, 0, 0 }, + { "ae_sp24x2f.xu", ICLASS_ae_iclass_sp24x2f_xu, + 0, + Opcode_ae_sp24x2f_xu_encode_fns, 0, 0 }, + { "ae_sp16f.l.i", ICLASS_ae_iclass_sp16f_l_i, + 0, + Opcode_ae_sp16f_l_i_encode_fns, 0, 0 }, + { "ae_sp16f.l.iu", ICLASS_ae_iclass_sp16f_l_iu, + 0, + Opcode_ae_sp16f_l_iu_encode_fns, 0, 0 }, + { "ae_sp16f.l.x", ICLASS_ae_iclass_sp16f_l_x, + 0, + Opcode_ae_sp16f_l_x_encode_fns, 0, 0 }, + { "ae_sp16f.l.xu", ICLASS_ae_iclass_sp16f_l_xu, + 0, + Opcode_ae_sp16f_l_xu_encode_fns, 0, 0 }, + { "ae_sp24s.l.i", ICLASS_ae_iclass_sp24s_l_i, + 0, + Opcode_ae_sp24s_l_i_encode_fns, 0, 0 }, + { "ae_sp24s.l.iu", ICLASS_ae_iclass_sp24s_l_iu, + 0, + Opcode_ae_sp24s_l_iu_encode_fns, 0, 0 }, + { "ae_sp24s.l.x", ICLASS_ae_iclass_sp24s_l_x, + 0, + Opcode_ae_sp24s_l_x_encode_fns, 0, 0 }, + { "ae_sp24s.l.xu", ICLASS_ae_iclass_sp24s_l_xu, + 0, + Opcode_ae_sp24s_l_xu_encode_fns, 0, 0 }, + { "ae_sp24f.l.i", ICLASS_ae_iclass_sp24f_l_i, + 0, + Opcode_ae_sp24f_l_i_encode_fns, 0, 0 }, + { "ae_sp24f.l.iu", ICLASS_ae_iclass_sp24f_l_iu, + 0, + Opcode_ae_sp24f_l_iu_encode_fns, 0, 0 }, + { "ae_sp24f.l.x", ICLASS_ae_iclass_sp24f_l_x, + 0, + Opcode_ae_sp24f_l_x_encode_fns, 0, 0 }, + { "ae_sp24f.l.xu", ICLASS_ae_iclass_sp24f_l_xu, + 0, + Opcode_ae_sp24f_l_xu_encode_fns, 0, 0 }, + { "ae_lq56.i", ICLASS_ae_iclass_lq56_i, + 0, + Opcode_ae_lq56_i_encode_fns, 0, 0 }, + { "ae_lq56.iu", ICLASS_ae_iclass_lq56_iu, + 0, + Opcode_ae_lq56_iu_encode_fns, 0, 0 }, + { "ae_lq56.x", ICLASS_ae_iclass_lq56_x, + 0, + Opcode_ae_lq56_x_encode_fns, 0, 0 }, + { "ae_lq56.xu", ICLASS_ae_iclass_lq56_xu, + 0, + Opcode_ae_lq56_xu_encode_fns, 0, 0 }, + { "ae_lq32f.i", ICLASS_ae_iclass_lq32f_i, + 0, + Opcode_ae_lq32f_i_encode_fns, 0, 0 }, + { "ae_lq32f.iu", ICLASS_ae_iclass_lq32f_iu, + 0, + Opcode_ae_lq32f_iu_encode_fns, 0, 0 }, + { "ae_lq32f.x", ICLASS_ae_iclass_lq32f_x, + 0, + Opcode_ae_lq32f_x_encode_fns, 0, 0 }, + { "ae_lq32f.xu", ICLASS_ae_iclass_lq32f_xu, + 0, + Opcode_ae_lq32f_xu_encode_fns, 0, 0 }, + { "ae_sq56s.i", ICLASS_ae_iclass_sq56s_i, + 0, + Opcode_ae_sq56s_i_encode_fns, 0, 0 }, + { "ae_sq56s.iu", ICLASS_ae_iclass_sq56s_iu, + 0, + Opcode_ae_sq56s_iu_encode_fns, 0, 0 }, + { "ae_sq56s.x", ICLASS_ae_iclass_sq56s_x, + 0, + Opcode_ae_sq56s_x_encode_fns, 0, 0 }, + { "ae_sq56s.xu", ICLASS_ae_iclass_sq56s_xu, + 0, + Opcode_ae_sq56s_xu_encode_fns, 0, 0 }, + { "ae_sq32f.i", ICLASS_ae_iclass_sq32f_i, + 0, + Opcode_ae_sq32f_i_encode_fns, 0, 0 }, + { "ae_sq32f.iu", ICLASS_ae_iclass_sq32f_iu, + 0, + Opcode_ae_sq32f_iu_encode_fns, 0, 0 }, + { "ae_sq32f.x", ICLASS_ae_iclass_sq32f_x, + 0, + Opcode_ae_sq32f_x_encode_fns, 0, 0 }, + { "ae_sq32f.xu", ICLASS_ae_iclass_sq32f_xu, + 0, + Opcode_ae_sq32f_xu_encode_fns, 0, 0 }, + { "ae_zerop48", ICLASS_ae_iclass_zerop48, + 0, + Opcode_ae_zerop48_encode_fns, 0, 0 }, + { "ae_movp48", ICLASS_ae_iclass_movp48, + 0, + Opcode_ae_movp48_encode_fns, 0, 0 }, + { "ae_selp24.ll", ICLASS_ae_iclass_selp24_ll, + 0, + Opcode_ae_selp24_ll_encode_fns, 0, 0 }, + { "ae_selp24.lh", ICLASS_ae_iclass_selp24_lh, + 0, + Opcode_ae_selp24_lh_encode_fns, 0, 0 }, + { "ae_selp24.hl", ICLASS_ae_iclass_selp24_hl, + 0, + Opcode_ae_selp24_hl_encode_fns, 0, 0 }, + { "ae_selp24.hh", ICLASS_ae_iclass_selp24_hh, + 0, + Opcode_ae_selp24_hh_encode_fns, 0, 0 }, + { "ae_movtp24x2", ICLASS_ae_iclass_movtp24x2, + 0, + Opcode_ae_movtp24x2_encode_fns, 0, 0 }, + { "ae_movfp24x2", ICLASS_ae_iclass_movfp24x2, + 0, + Opcode_ae_movfp24x2_encode_fns, 0, 0 }, + { "ae_movtp48", ICLASS_ae_iclass_movtp48, + 0, + Opcode_ae_movtp48_encode_fns, 0, 0 }, + { "ae_movfp48", ICLASS_ae_iclass_movfp48, + 0, + Opcode_ae_movfp48_encode_fns, 0, 0 }, + { "ae_movpa24x2", ICLASS_ae_iclass_movpa24x2, + 0, + Opcode_ae_movpa24x2_encode_fns, 0, 0 }, + { "ae_truncp24a32x2", ICLASS_ae_iclass_truncp24a32x2, + 0, + Opcode_ae_truncp24a32x2_encode_fns, 0, 0 }, + { "ae_cvta32p24.l", ICLASS_ae_iclass_cvta32p24_l, + 0, + Opcode_ae_cvta32p24_l_encode_fns, 0, 0 }, + { "ae_cvta32p24.h", ICLASS_ae_iclass_cvta32p24_h, + 0, + Opcode_ae_cvta32p24_h_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.ll", ICLASS_ae_iclass_cvtp24a16x2_ll, + 0, + Opcode_ae_cvtp24a16x2_ll_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.lh", ICLASS_ae_iclass_cvtp24a16x2_lh, + 0, + Opcode_ae_cvtp24a16x2_lh_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.hl", ICLASS_ae_iclass_cvtp24a16x2_hl, + 0, + Opcode_ae_cvtp24a16x2_hl_encode_fns, 0, 0 }, + { "ae_cvtp24a16x2.hh", ICLASS_ae_iclass_cvtp24a16x2_hh, + 0, + Opcode_ae_cvtp24a16x2_hh_encode_fns, 0, 0 }, + { "ae_truncp24q48x2", ICLASS_ae_iclass_truncp24q48x2, + 0, + Opcode_ae_truncp24q48x2_encode_fns, 0, 0 }, + { "ae_truncp16", ICLASS_ae_iclass_truncp16, + 0, + Opcode_ae_truncp16_encode_fns, 0, 0 }, + { "ae_roundsp24q48sym", ICLASS_ae_iclass_roundsp24q48sym, + 0, + Opcode_ae_roundsp24q48sym_encode_fns, 0, 0 }, + { "ae_roundsp24q48asym", ICLASS_ae_iclass_roundsp24q48asym, + 0, + Opcode_ae_roundsp24q48asym_encode_fns, 0, 0 }, + { "ae_roundsp16q48sym", ICLASS_ae_iclass_roundsp16q48sym, + 0, + Opcode_ae_roundsp16q48sym_encode_fns, 0, 0 }, + { "ae_roundsp16q48asym", ICLASS_ae_iclass_roundsp16q48asym, + 0, + Opcode_ae_roundsp16q48asym_encode_fns, 0, 0 }, + { "ae_roundsp16sym", ICLASS_ae_iclass_roundsp16sym, + 0, + Opcode_ae_roundsp16sym_encode_fns, 0, 0 }, + { "ae_roundsp16asym", ICLASS_ae_iclass_roundsp16asym, + 0, + Opcode_ae_roundsp16asym_encode_fns, 0, 0 }, + { "ae_zeroq56", ICLASS_ae_iclass_zeroq56, + 0, + Opcode_ae_zeroq56_encode_fns, 0, 0 }, + { "ae_movq56", ICLASS_ae_iclass_movq56, + 0, + Opcode_ae_movq56_encode_fns, 0, 0 }, + { "ae_movtq56", ICLASS_ae_iclass_movtq56, + 0, + Opcode_ae_movtq56_encode_fns, 0, 0 }, + { "ae_movfq56", ICLASS_ae_iclass_movfq56, + 0, + Opcode_ae_movfq56_encode_fns, 0, 0 }, + { "ae_cvtq48a32s", ICLASS_ae_iclass_cvtq48a32s, + 0, + Opcode_ae_cvtq48a32s_encode_fns, 0, 0 }, + { "ae_cvtq48p24s.l", ICLASS_ae_iclass_cvtq48p24s_l, + 0, + Opcode_ae_cvtq48p24s_l_encode_fns, 0, 0 }, + { "ae_cvtq48p24s.h", ICLASS_ae_iclass_cvtq48p24s_h, + 0, + Opcode_ae_cvtq48p24s_h_encode_fns, 0, 0 }, + { "ae_satq48s", ICLASS_ae_iclass_satq48s, + 0, + Opcode_ae_satq48s_encode_fns, 0, 0 }, + { "ae_truncq32", ICLASS_ae_iclass_truncq32, + 0, + Opcode_ae_truncq32_encode_fns, 0, 0 }, + { "ae_roundsq32sym", ICLASS_ae_iclass_roundsq32sym, + 0, + Opcode_ae_roundsq32sym_encode_fns, 0, 0 }, + { "ae_roundsq32asym", ICLASS_ae_iclass_roundsq32asym, + 0, + Opcode_ae_roundsq32asym_encode_fns, 0, 0 }, + { "ae_trunca32q48", ICLASS_ae_iclass_trunca32q48, + 0, + Opcode_ae_trunca32q48_encode_fns, 0, 0 }, + { "ae_movap24s.l", ICLASS_ae_iclass_movap24s_l, + 0, + Opcode_ae_movap24s_l_encode_fns, 0, 0 }, + { "ae_movap24s.h", ICLASS_ae_iclass_movap24s_h, + 0, + Opcode_ae_movap24s_h_encode_fns, 0, 0 }, + { "ae_trunca16p24s.l", ICLASS_ae_iclass_trunca16p24s_l, + 0, + Opcode_ae_trunca16p24s_l_encode_fns, 0, 0 }, + { "ae_trunca16p24s.h", ICLASS_ae_iclass_trunca16p24s_h, + 0, + Opcode_ae_trunca16p24s_h_encode_fns, 0, 0 }, + { "ae_addp24", ICLASS_ae_iclass_addp24, + 0, + Opcode_ae_addp24_encode_fns, 0, 0 }, + { "ae_subp24", ICLASS_ae_iclass_subp24, + 0, + Opcode_ae_subp24_encode_fns, 0, 0 }, + { "ae_negp24", ICLASS_ae_iclass_negp24, + 0, + Opcode_ae_negp24_encode_fns, 0, 0 }, + { "ae_absp24", ICLASS_ae_iclass_absp24, + 0, + Opcode_ae_absp24_encode_fns, 0, 0 }, + { "ae_maxp24s", ICLASS_ae_iclass_maxp24s, + 0, + Opcode_ae_maxp24s_encode_fns, 0, 0 }, + { "ae_minp24s", ICLASS_ae_iclass_minp24s, + 0, + Opcode_ae_minp24s_encode_fns, 0, 0 }, + { "ae_maxbp24s", ICLASS_ae_iclass_maxbp24s, + 0, + Opcode_ae_maxbp24s_encode_fns, 0, 0 }, + { "ae_minbp24s", ICLASS_ae_iclass_minbp24s, + 0, + Opcode_ae_minbp24s_encode_fns, 0, 0 }, + { "ae_addsp24s", ICLASS_ae_iclass_addsp24s, + 0, + Opcode_ae_addsp24s_encode_fns, 0, 0 }, + { "ae_subsp24s", ICLASS_ae_iclass_subsp24s, + 0, + Opcode_ae_subsp24s_encode_fns, 0, 0 }, + { "ae_negsp24s", ICLASS_ae_iclass_negsp24s, + 0, + Opcode_ae_negsp24s_encode_fns, 0, 0 }, + { "ae_abssp24s", ICLASS_ae_iclass_abssp24s, + 0, + Opcode_ae_abssp24s_encode_fns, 0, 0 }, + { "ae_andp48", ICLASS_ae_iclass_andp48, + 0, + Opcode_ae_andp48_encode_fns, 0, 0 }, + { "ae_nandp48", ICLASS_ae_iclass_nandp48, + 0, + Opcode_ae_nandp48_encode_fns, 0, 0 }, + { "ae_orp48", ICLASS_ae_iclass_orp48, + 0, + Opcode_ae_orp48_encode_fns, 0, 0 }, + { "ae_xorp48", ICLASS_ae_iclass_xorp48, + 0, + Opcode_ae_xorp48_encode_fns, 0, 0 }, + { "ae_ltp24s", ICLASS_ae_iclass_ltp24s, + 0, + Opcode_ae_ltp24s_encode_fns, 0, 0 }, + { "ae_lep24s", ICLASS_ae_iclass_lep24s, + 0, + Opcode_ae_lep24s_encode_fns, 0, 0 }, + { "ae_eqp24", ICLASS_ae_iclass_eqp24, + 0, + Opcode_ae_eqp24_encode_fns, 0, 0 }, + { "ae_addq56", ICLASS_ae_iclass_addq56, + 0, + Opcode_ae_addq56_encode_fns, 0, 0 }, + { "ae_subq56", ICLASS_ae_iclass_subq56, + 0, + Opcode_ae_subq56_encode_fns, 0, 0 }, + { "ae_negq56", ICLASS_ae_iclass_negq56, + 0, + Opcode_ae_negq56_encode_fns, 0, 0 }, + { "ae_absq56", ICLASS_ae_iclass_absq56, + 0, + Opcode_ae_absq56_encode_fns, 0, 0 }, + { "ae_maxq56s", ICLASS_ae_iclass_maxq56s, + 0, + Opcode_ae_maxq56s_encode_fns, 0, 0 }, + { "ae_minq56s", ICLASS_ae_iclass_minq56s, + 0, + Opcode_ae_minq56s_encode_fns, 0, 0 }, + { "ae_maxbq56s", ICLASS_ae_iclass_maxbq56s, + 0, + Opcode_ae_maxbq56s_encode_fns, 0, 0 }, + { "ae_minbq56s", ICLASS_ae_iclass_minbq56s, + 0, + Opcode_ae_minbq56s_encode_fns, 0, 0 }, + { "ae_addsq56s", ICLASS_ae_iclass_addsq56s, + 0, + Opcode_ae_addsq56s_encode_fns, 0, 0 }, + { "ae_subsq56s", ICLASS_ae_iclass_subsq56s, + 0, + Opcode_ae_subsq56s_encode_fns, 0, 0 }, + { "ae_negsq56s", ICLASS_ae_iclass_negsq56s, + 0, + Opcode_ae_negsq56s_encode_fns, 0, 0 }, + { "ae_abssq56s", ICLASS_ae_iclass_abssq56s, + 0, + Opcode_ae_abssq56s_encode_fns, 0, 0 }, + { "ae_andq56", ICLASS_ae_iclass_andq56, + 0, + Opcode_ae_andq56_encode_fns, 0, 0 }, + { "ae_nandq56", ICLASS_ae_iclass_nandq56, + 0, + Opcode_ae_nandq56_encode_fns, 0, 0 }, + { "ae_orq56", ICLASS_ae_iclass_orq56, + 0, + Opcode_ae_orq56_encode_fns, 0, 0 }, + { "ae_xorq56", ICLASS_ae_iclass_xorq56, + 0, + Opcode_ae_xorq56_encode_fns, 0, 0 }, + { "ae_sllip24", ICLASS_ae_iclass_sllip24, + 0, + Opcode_ae_sllip24_encode_fns, 0, 0 }, + { "ae_srlip24", ICLASS_ae_iclass_srlip24, + 0, + Opcode_ae_srlip24_encode_fns, 0, 0 }, + { "ae_sraip24", ICLASS_ae_iclass_sraip24, + 0, + Opcode_ae_sraip24_encode_fns, 0, 0 }, + { "ae_sllsp24", ICLASS_ae_iclass_sllsp24, + 0, + Opcode_ae_sllsp24_encode_fns, 0, 0 }, + { "ae_srlsp24", ICLASS_ae_iclass_srlsp24, + 0, + Opcode_ae_srlsp24_encode_fns, 0, 0 }, + { "ae_srasp24", ICLASS_ae_iclass_srasp24, + 0, + Opcode_ae_srasp24_encode_fns, 0, 0 }, + { "ae_sllisp24s", ICLASS_ae_iclass_sllisp24s, + 0, + Opcode_ae_sllisp24s_encode_fns, 0, 0 }, + { "ae_sllssp24s", ICLASS_ae_iclass_sllssp24s, + 0, + Opcode_ae_sllssp24s_encode_fns, 0, 0 }, + { "ae_slliq56", ICLASS_ae_iclass_slliq56, + 0, + Opcode_ae_slliq56_encode_fns, 0, 0 }, + { "ae_srliq56", ICLASS_ae_iclass_srliq56, + 0, + Opcode_ae_srliq56_encode_fns, 0, 0 }, + { "ae_sraiq56", ICLASS_ae_iclass_sraiq56, + 0, + Opcode_ae_sraiq56_encode_fns, 0, 0 }, + { "ae_sllsq56", ICLASS_ae_iclass_sllsq56, + 0, + Opcode_ae_sllsq56_encode_fns, 0, 0 }, + { "ae_srlsq56", ICLASS_ae_iclass_srlsq56, + 0, + Opcode_ae_srlsq56_encode_fns, 0, 0 }, + { "ae_srasq56", ICLASS_ae_iclass_srasq56, + 0, + Opcode_ae_srasq56_encode_fns, 0, 0 }, + { "ae_sllaq56", ICLASS_ae_iclass_sllaq56, + 0, + Opcode_ae_sllaq56_encode_fns, 0, 0 }, + { "ae_srlaq56", ICLASS_ae_iclass_srlaq56, + 0, + Opcode_ae_srlaq56_encode_fns, 0, 0 }, + { "ae_sraaq56", ICLASS_ae_iclass_sraaq56, + 0, + Opcode_ae_sraaq56_encode_fns, 0, 0 }, + { "ae_sllisq56s", ICLASS_ae_iclass_sllisq56s, + 0, + Opcode_ae_sllisq56s_encode_fns, 0, 0 }, + { "ae_sllssq56s", ICLASS_ae_iclass_sllssq56s, + 0, + Opcode_ae_sllssq56s_encode_fns, 0, 0 }, + { "ae_sllasq56s", ICLASS_ae_iclass_sllasq56s, + 0, + Opcode_ae_sllasq56s_encode_fns, 0, 0 }, + { "ae_ltq56s", ICLASS_ae_iclass_ltq56s, + 0, + Opcode_ae_ltq56s_encode_fns, 0, 0 }, + { "ae_leq56s", ICLASS_ae_iclass_leq56s, + 0, + Opcode_ae_leq56s_encode_fns, 0, 0 }, + { "ae_eqq56", ICLASS_ae_iclass_eqq56, + 0, + Opcode_ae_eqq56_encode_fns, 0, 0 }, + { "ae_nsaq56s", ICLASS_ae_iclass_nsaq56s, + 0, + Opcode_ae_nsaq56s_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.ll", ICLASS_ae_iclass_mulfs32p16s_ll, + 0, + Opcode_ae_mulfs32p16s_ll_encode_fns, 0, 0 }, + { "ae_mulfp24s.ll", ICLASS_ae_iclass_mulfp24s_ll, + 0, + Opcode_ae_mulfp24s_ll_encode_fns, 0, 0 }, + { "ae_mulp24s.ll", ICLASS_ae_iclass_mulp24s_ll, + 0, + Opcode_ae_mulp24s_ll_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.lh", ICLASS_ae_iclass_mulfs32p16s_lh, + 0, + Opcode_ae_mulfs32p16s_lh_encode_fns, 0, 0 }, + { "ae_mulfp24s.lh", ICLASS_ae_iclass_mulfp24s_lh, + 0, + Opcode_ae_mulfp24s_lh_encode_fns, 0, 0 }, + { "ae_mulp24s.lh", ICLASS_ae_iclass_mulp24s_lh, + 0, + Opcode_ae_mulp24s_lh_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.hl", ICLASS_ae_iclass_mulfs32p16s_hl, + 0, + Opcode_ae_mulfs32p16s_hl_encode_fns, 0, 0 }, + { "ae_mulfp24s.hl", ICLASS_ae_iclass_mulfp24s_hl, + 0, + Opcode_ae_mulfp24s_hl_encode_fns, 0, 0 }, + { "ae_mulp24s.hl", ICLASS_ae_iclass_mulp24s_hl, + 0, + Opcode_ae_mulp24s_hl_encode_fns, 0, 0 }, + { "ae_mulfs32p16s.hh", ICLASS_ae_iclass_mulfs32p16s_hh, + 0, + Opcode_ae_mulfs32p16s_hh_encode_fns, 0, 0 }, + { "ae_mulfp24s.hh", ICLASS_ae_iclass_mulfp24s_hh, + 0, + Opcode_ae_mulfp24s_hh_encode_fns, 0, 0 }, + { "ae_mulp24s.hh", ICLASS_ae_iclass_mulp24s_hh, + 0, + Opcode_ae_mulp24s_hh_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.ll", ICLASS_ae_iclass_mulafs32p16s_ll, + 0, + Opcode_ae_mulafs32p16s_ll_encode_fns, 0, 0 }, + { "ae_mulafp24s.ll", ICLASS_ae_iclass_mulafp24s_ll, + 0, + Opcode_ae_mulafp24s_ll_encode_fns, 0, 0 }, + { "ae_mulap24s.ll", ICLASS_ae_iclass_mulap24s_ll, + 0, + Opcode_ae_mulap24s_ll_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.lh", ICLASS_ae_iclass_mulafs32p16s_lh, + 0, + Opcode_ae_mulafs32p16s_lh_encode_fns, 0, 0 }, + { "ae_mulafp24s.lh", ICLASS_ae_iclass_mulafp24s_lh, + 0, + Opcode_ae_mulafp24s_lh_encode_fns, 0, 0 }, + { "ae_mulap24s.lh", ICLASS_ae_iclass_mulap24s_lh, + 0, + Opcode_ae_mulap24s_lh_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.hl", ICLASS_ae_iclass_mulafs32p16s_hl, + 0, + Opcode_ae_mulafs32p16s_hl_encode_fns, 0, 0 }, + { "ae_mulafp24s.hl", ICLASS_ae_iclass_mulafp24s_hl, + 0, + Opcode_ae_mulafp24s_hl_encode_fns, 0, 0 }, + { "ae_mulap24s.hl", ICLASS_ae_iclass_mulap24s_hl, + 0, + Opcode_ae_mulap24s_hl_encode_fns, 0, 0 }, + { "ae_mulafs32p16s.hh", ICLASS_ae_iclass_mulafs32p16s_hh, + 0, + Opcode_ae_mulafs32p16s_hh_encode_fns, 0, 0 }, + { "ae_mulafp24s.hh", ICLASS_ae_iclass_mulafp24s_hh, + 0, + Opcode_ae_mulafp24s_hh_encode_fns, 0, 0 }, + { "ae_mulap24s.hh", ICLASS_ae_iclass_mulap24s_hh, + 0, + Opcode_ae_mulap24s_hh_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.ll", ICLASS_ae_iclass_mulsfs32p16s_ll, + 0, + Opcode_ae_mulsfs32p16s_ll_encode_fns, 0, 0 }, + { "ae_mulsfp24s.ll", ICLASS_ae_iclass_mulsfp24s_ll, + 0, + Opcode_ae_mulsfp24s_ll_encode_fns, 0, 0 }, + { "ae_mulsp24s.ll", ICLASS_ae_iclass_mulsp24s_ll, + 0, + Opcode_ae_mulsp24s_ll_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.lh", ICLASS_ae_iclass_mulsfs32p16s_lh, + 0, + Opcode_ae_mulsfs32p16s_lh_encode_fns, 0, 0 }, + { "ae_mulsfp24s.lh", ICLASS_ae_iclass_mulsfp24s_lh, + 0, + Opcode_ae_mulsfp24s_lh_encode_fns, 0, 0 }, + { "ae_mulsp24s.lh", ICLASS_ae_iclass_mulsp24s_lh, + 0, + Opcode_ae_mulsp24s_lh_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.hl", ICLASS_ae_iclass_mulsfs32p16s_hl, + 0, + Opcode_ae_mulsfs32p16s_hl_encode_fns, 0, 0 }, + { "ae_mulsfp24s.hl", ICLASS_ae_iclass_mulsfp24s_hl, + 0, + Opcode_ae_mulsfp24s_hl_encode_fns, 0, 0 }, + { "ae_mulsp24s.hl", ICLASS_ae_iclass_mulsp24s_hl, + 0, + Opcode_ae_mulsp24s_hl_encode_fns, 0, 0 }, + { "ae_mulsfs32p16s.hh", ICLASS_ae_iclass_mulsfs32p16s_hh, + 0, + Opcode_ae_mulsfs32p16s_hh_encode_fns, 0, 0 }, + { "ae_mulsfp24s.hh", ICLASS_ae_iclass_mulsfp24s_hh, + 0, + Opcode_ae_mulsfp24s_hh_encode_fns, 0, 0 }, + { "ae_mulsp24s.hh", ICLASS_ae_iclass_mulsp24s_hh, + 0, + Opcode_ae_mulsp24s_hh_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.ll", ICLASS_ae_iclass_mulafs56p24s_ll, + 0, + Opcode_ae_mulafs56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulas56p24s.ll", ICLASS_ae_iclass_mulas56p24s_ll, + 0, + Opcode_ae_mulas56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.lh", ICLASS_ae_iclass_mulafs56p24s_lh, + 0, + Opcode_ae_mulafs56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulas56p24s.lh", ICLASS_ae_iclass_mulas56p24s_lh, + 0, + Opcode_ae_mulas56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.hl", ICLASS_ae_iclass_mulafs56p24s_hl, + 0, + Opcode_ae_mulafs56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulas56p24s.hl", ICLASS_ae_iclass_mulas56p24s_hl, + 0, + Opcode_ae_mulas56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulafs56p24s.hh", ICLASS_ae_iclass_mulafs56p24s_hh, + 0, + Opcode_ae_mulafs56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulas56p24s.hh", ICLASS_ae_iclass_mulas56p24s_hh, + 0, + Opcode_ae_mulas56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.ll", ICLASS_ae_iclass_mulsfs56p24s_ll, + 0, + Opcode_ae_mulsfs56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulss56p24s.ll", ICLASS_ae_iclass_mulss56p24s_ll, + 0, + Opcode_ae_mulss56p24s_ll_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.lh", ICLASS_ae_iclass_mulsfs56p24s_lh, + 0, + Opcode_ae_mulsfs56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulss56p24s.lh", ICLASS_ae_iclass_mulss56p24s_lh, + 0, + Opcode_ae_mulss56p24s_lh_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.hl", ICLASS_ae_iclass_mulsfs56p24s_hl, + 0, + Opcode_ae_mulsfs56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulss56p24s.hl", ICLASS_ae_iclass_mulss56p24s_hl, + 0, + Opcode_ae_mulss56p24s_hl_encode_fns, 0, 0 }, + { "ae_mulsfs56p24s.hh", ICLASS_ae_iclass_mulsfs56p24s_hh, + 0, + Opcode_ae_mulsfs56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulss56p24s.hh", ICLASS_ae_iclass_mulss56p24s_hh, + 0, + Opcode_ae_mulss56p24s_hh_encode_fns, 0, 0 }, + { "ae_mulfq32sp16s.l", ICLASS_ae_iclass_mulfq32sp16s_l, + 0, + Opcode_ae_mulfq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulfq32sp16s.h", ICLASS_ae_iclass_mulfq32sp16s_h, + 0, + Opcode_ae_mulfq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulfq32sp16u.l", ICLASS_ae_iclass_mulfq32sp16u_l, + 0, + Opcode_ae_mulfq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulfq32sp16u.h", ICLASS_ae_iclass_mulfq32sp16u_h, + 0, + Opcode_ae_mulfq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulq32sp16s.l", ICLASS_ae_iclass_mulq32sp16s_l, + 0, + Opcode_ae_mulq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulq32sp16s.h", ICLASS_ae_iclass_mulq32sp16s_h, + 0, + Opcode_ae_mulq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulq32sp16u.l", ICLASS_ae_iclass_mulq32sp16u_l, + 0, + Opcode_ae_mulq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulq32sp16u.h", ICLASS_ae_iclass_mulq32sp16u_h, + 0, + Opcode_ae_mulq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulafq32sp16s.l", ICLASS_ae_iclass_mulafq32sp16s_l, + 0, + Opcode_ae_mulafq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulafq32sp16s.h", ICLASS_ae_iclass_mulafq32sp16s_h, + 0, + Opcode_ae_mulafq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulafq32sp16u.l", ICLASS_ae_iclass_mulafq32sp16u_l, + 0, + Opcode_ae_mulafq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulafq32sp16u.h", ICLASS_ae_iclass_mulafq32sp16u_h, + 0, + Opcode_ae_mulafq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulaq32sp16s.l", ICLASS_ae_iclass_mulaq32sp16s_l, + 0, + Opcode_ae_mulaq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulaq32sp16s.h", ICLASS_ae_iclass_mulaq32sp16s_h, + 0, + Opcode_ae_mulaq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulaq32sp16u.l", ICLASS_ae_iclass_mulaq32sp16u_l, + 0, + Opcode_ae_mulaq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulaq32sp16u.h", ICLASS_ae_iclass_mulaq32sp16u_h, + 0, + Opcode_ae_mulaq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16s.l", ICLASS_ae_iclass_mulsfq32sp16s_l, + 0, + Opcode_ae_mulsfq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16s.h", ICLASS_ae_iclass_mulsfq32sp16s_h, + 0, + Opcode_ae_mulsfq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16u.l", ICLASS_ae_iclass_mulsfq32sp16u_l, + 0, + Opcode_ae_mulsfq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulsfq32sp16u.h", ICLASS_ae_iclass_mulsfq32sp16u_h, + 0, + Opcode_ae_mulsfq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulsq32sp16s.l", ICLASS_ae_iclass_mulsq32sp16s_l, + 0, + Opcode_ae_mulsq32sp16s_l_encode_fns, 0, 0 }, + { "ae_mulsq32sp16s.h", ICLASS_ae_iclass_mulsq32sp16s_h, + 0, + Opcode_ae_mulsq32sp16s_h_encode_fns, 0, 0 }, + { "ae_mulsq32sp16u.l", ICLASS_ae_iclass_mulsq32sp16u_l, + 0, + Opcode_ae_mulsq32sp16u_l_encode_fns, 0, 0 }, + { "ae_mulsq32sp16u.h", ICLASS_ae_iclass_mulsq32sp16u_h, + 0, + Opcode_ae_mulsq32sp16u_h_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16s.ll", ICLASS_ae_iclass_mulzaaq32sp16s_ll, + 0, + Opcode_ae_mulzaaq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16s.ll", ICLASS_ae_iclass_mulzaafq32sp16s_ll, + 0, + Opcode_ae_mulzaafq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16u.ll", ICLASS_ae_iclass_mulzaaq32sp16u_ll, + 0, + Opcode_ae_mulzaaq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16u.ll", ICLASS_ae_iclass_mulzaafq32sp16u_ll, + 0, + Opcode_ae_mulzaafq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16s.hh", ICLASS_ae_iclass_mulzaaq32sp16s_hh, + 0, + Opcode_ae_mulzaaq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16s.hh", ICLASS_ae_iclass_mulzaafq32sp16s_hh, + 0, + Opcode_ae_mulzaafq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16u.hh", ICLASS_ae_iclass_mulzaaq32sp16u_hh, + 0, + Opcode_ae_mulzaaq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16u.hh", ICLASS_ae_iclass_mulzaafq32sp16u_hh, + 0, + Opcode_ae_mulzaafq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16s.lh", ICLASS_ae_iclass_mulzaaq32sp16s_lh, + 0, + Opcode_ae_mulzaaq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16s.lh", ICLASS_ae_iclass_mulzaafq32sp16s_lh, + 0, + Opcode_ae_mulzaafq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzaaq32sp16u.lh", ICLASS_ae_iclass_mulzaaq32sp16u_lh, + 0, + Opcode_ae_mulzaaq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzaafq32sp16u.lh", ICLASS_ae_iclass_mulzaafq32sp16u_lh, + 0, + Opcode_ae_mulzaafq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16s.ll", ICLASS_ae_iclass_mulzasq32sp16s_ll, + 0, + Opcode_ae_mulzasq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16s.ll", ICLASS_ae_iclass_mulzasfq32sp16s_ll, + 0, + Opcode_ae_mulzasfq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16u.ll", ICLASS_ae_iclass_mulzasq32sp16u_ll, + 0, + Opcode_ae_mulzasq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16u.ll", ICLASS_ae_iclass_mulzasfq32sp16u_ll, + 0, + Opcode_ae_mulzasfq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16s.hh", ICLASS_ae_iclass_mulzasq32sp16s_hh, + 0, + Opcode_ae_mulzasq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16s.hh", ICLASS_ae_iclass_mulzasfq32sp16s_hh, + 0, + Opcode_ae_mulzasfq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16u.hh", ICLASS_ae_iclass_mulzasq32sp16u_hh, + 0, + Opcode_ae_mulzasq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16u.hh", ICLASS_ae_iclass_mulzasfq32sp16u_hh, + 0, + Opcode_ae_mulzasfq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16s.lh", ICLASS_ae_iclass_mulzasq32sp16s_lh, + 0, + Opcode_ae_mulzasq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16s.lh", ICLASS_ae_iclass_mulzasfq32sp16s_lh, + 0, + Opcode_ae_mulzasfq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzasq32sp16u.lh", ICLASS_ae_iclass_mulzasq32sp16u_lh, + 0, + Opcode_ae_mulzasq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzasfq32sp16u.lh", ICLASS_ae_iclass_mulzasfq32sp16u_lh, + 0, + Opcode_ae_mulzasfq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16s.ll", ICLASS_ae_iclass_mulzsaq32sp16s_ll, + 0, + Opcode_ae_mulzsaq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16s.ll", ICLASS_ae_iclass_mulzsafq32sp16s_ll, + 0, + Opcode_ae_mulzsafq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16u.ll", ICLASS_ae_iclass_mulzsaq32sp16u_ll, + 0, + Opcode_ae_mulzsaq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16u.ll", ICLASS_ae_iclass_mulzsafq32sp16u_ll, + 0, + Opcode_ae_mulzsafq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16s.hh", ICLASS_ae_iclass_mulzsaq32sp16s_hh, + 0, + Opcode_ae_mulzsaq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16s.hh", ICLASS_ae_iclass_mulzsafq32sp16s_hh, + 0, + Opcode_ae_mulzsafq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16u.hh", ICLASS_ae_iclass_mulzsaq32sp16u_hh, + 0, + Opcode_ae_mulzsaq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16u.hh", ICLASS_ae_iclass_mulzsafq32sp16u_hh, + 0, + Opcode_ae_mulzsafq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16s.lh", ICLASS_ae_iclass_mulzsaq32sp16s_lh, + 0, + Opcode_ae_mulzsaq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16s.lh", ICLASS_ae_iclass_mulzsafq32sp16s_lh, + 0, + Opcode_ae_mulzsafq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzsaq32sp16u.lh", ICLASS_ae_iclass_mulzsaq32sp16u_lh, + 0, + Opcode_ae_mulzsaq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzsafq32sp16u.lh", ICLASS_ae_iclass_mulzsafq32sp16u_lh, + 0, + Opcode_ae_mulzsafq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16s.ll", ICLASS_ae_iclass_mulzssq32sp16s_ll, + 0, + Opcode_ae_mulzssq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16s.ll", ICLASS_ae_iclass_mulzssfq32sp16s_ll, + 0, + Opcode_ae_mulzssfq32sp16s_ll_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16u.ll", ICLASS_ae_iclass_mulzssq32sp16u_ll, + 0, + Opcode_ae_mulzssq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16u.ll", ICLASS_ae_iclass_mulzssfq32sp16u_ll, + 0, + Opcode_ae_mulzssfq32sp16u_ll_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16s.hh", ICLASS_ae_iclass_mulzssq32sp16s_hh, + 0, + Opcode_ae_mulzssq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16s.hh", ICLASS_ae_iclass_mulzssfq32sp16s_hh, + 0, + Opcode_ae_mulzssfq32sp16s_hh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16u.hh", ICLASS_ae_iclass_mulzssq32sp16u_hh, + 0, + Opcode_ae_mulzssq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16u.hh", ICLASS_ae_iclass_mulzssfq32sp16u_hh, + 0, + Opcode_ae_mulzssfq32sp16u_hh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16s.lh", ICLASS_ae_iclass_mulzssq32sp16s_lh, + 0, + Opcode_ae_mulzssq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16s.lh", ICLASS_ae_iclass_mulzssfq32sp16s_lh, + 0, + Opcode_ae_mulzssfq32sp16s_lh_encode_fns, 0, 0 }, + { "ae_mulzssq32sp16u.lh", ICLASS_ae_iclass_mulzssq32sp16u_lh, + 0, + Opcode_ae_mulzssq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzssfq32sp16u.lh", ICLASS_ae_iclass_mulzssfq32sp16u_lh, + 0, + Opcode_ae_mulzssfq32sp16u_lh_encode_fns, 0, 0 }, + { "ae_mulzaafp24s.hh.ll", ICLASS_ae_iclass_mulzaafp24s_hh_ll, + 0, + Opcode_ae_mulzaafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzaap24s.hh.ll", ICLASS_ae_iclass_mulzaap24s_hh_ll, + 0, + Opcode_ae_mulzaap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzaafp24s.hl.lh", ICLASS_ae_iclass_mulzaafp24s_hl_lh, + 0, + Opcode_ae_mulzaafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzaap24s.hl.lh", ICLASS_ae_iclass_mulzaap24s_hl_lh, + 0, + Opcode_ae_mulzaap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzasfp24s.hh.ll", ICLASS_ae_iclass_mulzasfp24s_hh_ll, + 0, + Opcode_ae_mulzasfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzasp24s.hh.ll", ICLASS_ae_iclass_mulzasp24s_hh_ll, + 0, + Opcode_ae_mulzasp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzasfp24s.hl.lh", ICLASS_ae_iclass_mulzasfp24s_hl_lh, + 0, + Opcode_ae_mulzasfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzasp24s.hl.lh", ICLASS_ae_iclass_mulzasp24s_hl_lh, + 0, + Opcode_ae_mulzasp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzsafp24s.hh.ll", ICLASS_ae_iclass_mulzsafp24s_hh_ll, + 0, + Opcode_ae_mulzsafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzsap24s.hh.ll", ICLASS_ae_iclass_mulzsap24s_hh_ll, + 0, + Opcode_ae_mulzsap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzsafp24s.hl.lh", ICLASS_ae_iclass_mulzsafp24s_hl_lh, + 0, + Opcode_ae_mulzsafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzsap24s.hl.lh", ICLASS_ae_iclass_mulzsap24s_hl_lh, + 0, + Opcode_ae_mulzsap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzssfp24s.hh.ll", ICLASS_ae_iclass_mulzssfp24s_hh_ll, + 0, + Opcode_ae_mulzssfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzssp24s.hh.ll", ICLASS_ae_iclass_mulzssp24s_hh_ll, + 0, + Opcode_ae_mulzssp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulzssfp24s.hl.lh", ICLASS_ae_iclass_mulzssfp24s_hl_lh, + 0, + Opcode_ae_mulzssfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulzssp24s.hl.lh", ICLASS_ae_iclass_mulzssp24s_hl_lh, + 0, + Opcode_ae_mulzssp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulaafp24s.hh.ll", ICLASS_ae_iclass_mulaafp24s_hh_ll, + 0, + Opcode_ae_mulaafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulaap24s.hh.ll", ICLASS_ae_iclass_mulaap24s_hh_ll, + 0, + Opcode_ae_mulaap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulaafp24s.hl.lh", ICLASS_ae_iclass_mulaafp24s_hl_lh, + 0, + Opcode_ae_mulaafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulaap24s.hl.lh", ICLASS_ae_iclass_mulaap24s_hl_lh, + 0, + Opcode_ae_mulaap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulasfp24s.hh.ll", ICLASS_ae_iclass_mulasfp24s_hh_ll, + 0, + Opcode_ae_mulasfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulasp24s.hh.ll", ICLASS_ae_iclass_mulasp24s_hh_ll, + 0, + Opcode_ae_mulasp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulasfp24s.hl.lh", ICLASS_ae_iclass_mulasfp24s_hl_lh, + 0, + Opcode_ae_mulasfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulasp24s.hl.lh", ICLASS_ae_iclass_mulasp24s_hl_lh, + 0, + Opcode_ae_mulasp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulsafp24s.hh.ll", ICLASS_ae_iclass_mulsafp24s_hh_ll, + 0, + Opcode_ae_mulsafp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulsap24s.hh.ll", ICLASS_ae_iclass_mulsap24s_hh_ll, + 0, + Opcode_ae_mulsap24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulsafp24s.hl.lh", ICLASS_ae_iclass_mulsafp24s_hl_lh, + 0, + Opcode_ae_mulsafp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulsap24s.hl.lh", ICLASS_ae_iclass_mulsap24s_hl_lh, + 0, + Opcode_ae_mulsap24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulssfp24s.hh.ll", ICLASS_ae_iclass_mulssfp24s_hh_ll, + 0, + Opcode_ae_mulssfp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulssp24s.hh.ll", ICLASS_ae_iclass_mulssp24s_hh_ll, + 0, + Opcode_ae_mulssp24s_hh_ll_encode_fns, 0, 0 }, + { "ae_mulssfp24s.hl.lh", ICLASS_ae_iclass_mulssfp24s_hl_lh, + 0, + Opcode_ae_mulssfp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_mulssp24s.hl.lh", ICLASS_ae_iclass_mulssp24s_hl_lh, + 0, + Opcode_ae_mulssp24s_hl_lh_encode_fns, 0, 0 }, + { "ae_sha32", ICLASS_ae_iclass_sha32, + 0, + Opcode_ae_sha32_encode_fns, 0, 0 }, + { "ae_vldl32t", ICLASS_ae_iclass_vldl32t, + 0, + Opcode_ae_vldl32t_encode_fns, 1, Opcode_ae_vldl32t_funcUnit_uses }, + { "ae_vldl16t", ICLASS_ae_iclass_vldl16t, + 0, + Opcode_ae_vldl16t_encode_fns, 1, Opcode_ae_vldl16t_funcUnit_uses }, + { "ae_vldl16c", ICLASS_ae_iclass_vldl16c, + 0, + Opcode_ae_vldl16c_encode_fns, 3, Opcode_ae_vldl16c_funcUnit_uses }, + { "ae_vldsht", ICLASS_ae_iclass_vldsht, + 0, + Opcode_ae_vldsht_encode_fns, 3, Opcode_ae_vldsht_funcUnit_uses }, + { "ae_lb", ICLASS_ae_iclass_lb, + 0, + Opcode_ae_lb_encode_fns, 1, Opcode_ae_lb_funcUnit_uses }, + { "ae_lbi", ICLASS_ae_iclass_lbi, + 0, + Opcode_ae_lbi_encode_fns, 1, Opcode_ae_lbi_funcUnit_uses }, + { "ae_lbk", ICLASS_ae_iclass_lbk, + 0, + Opcode_ae_lbk_encode_fns, 1, Opcode_ae_lbk_funcUnit_uses }, + { "ae_lbki", ICLASS_ae_iclass_lbki, + 0, + Opcode_ae_lbki_encode_fns, 1, Opcode_ae_lbki_funcUnit_uses }, + { "ae_db", ICLASS_ae_iclass_db, + 0, + Opcode_ae_db_encode_fns, 2, Opcode_ae_db_funcUnit_uses }, + { "ae_dbi", ICLASS_ae_iclass_dbi, + 0, + Opcode_ae_dbi_encode_fns, 2, Opcode_ae_dbi_funcUnit_uses }, + { "ae_vlel32t", ICLASS_ae_iclass_vlel32t, + 0, + Opcode_ae_vlel32t_encode_fns, 1, Opcode_ae_vlel32t_funcUnit_uses }, + { "ae_vlel16t", ICLASS_ae_iclass_vlel16t, + 0, + Opcode_ae_vlel16t_encode_fns, 1, Opcode_ae_vlel16t_funcUnit_uses }, + { "ae_sb", ICLASS_ae_iclass_sb, + 0, + Opcode_ae_sb_encode_fns, 2, Opcode_ae_sb_funcUnit_uses }, + { "ae_sbi", ICLASS_ae_iclass_sbi, + 0, + Opcode_ae_sbi_encode_fns, 2, Opcode_ae_sbi_funcUnit_uses }, + { "ae_vles16c", ICLASS_ae_iclass_vles16c, + 0, + Opcode_ae_vles16c_encode_fns, 2, Opcode_ae_vles16c_funcUnit_uses }, + { "ae_sbf", ICLASS_ae_iclass_sbf, + 0, + Opcode_ae_sbf_encode_fns, 2, Opcode_ae_sbf_funcUnit_uses } +}; + +enum xtensa_opcode_id { + OPCODE_EXCW, + OPCODE_RFE, + OPCODE_RFDE, + OPCODE_SYSCALL, + OPCODE_SIMCALL, + OPCODE_CALL12, + OPCODE_CALL8, + OPCODE_CALL4, + OPCODE_CALLX12, + OPCODE_CALLX8, + OPCODE_CALLX4, + OPCODE_ENTRY, + OPCODE_MOVSP, + OPCODE_ROTW, + OPCODE_RETW, + OPCODE_RETW_N, + OPCODE_RFWO, + OPCODE_RFWU, + OPCODE_L32E, + OPCODE_S32E, + OPCODE_RSR_WINDOWBASE, + OPCODE_WSR_WINDOWBASE, + OPCODE_XSR_WINDOWBASE, + OPCODE_RSR_WINDOWSTART, + OPCODE_WSR_WINDOWSTART, + OPCODE_XSR_WINDOWSTART, + OPCODE_ADD_N, + OPCODE_ADDI_N, + OPCODE_BEQZ_N, + OPCODE_BNEZ_N, + OPCODE_ILL_N, + OPCODE_L32I_N, + OPCODE_MOV_N, + OPCODE_MOVI_N, + OPCODE_NOP_N, + OPCODE_RET_N, + OPCODE_S32I_N, + OPCODE_RUR_THREADPTR, + OPCODE_WUR_THREADPTR, + OPCODE_ADDI, + OPCODE_ADDMI, + OPCODE_ADD, + OPCODE_SUB, + OPCODE_ADDX2, + OPCODE_ADDX4, + OPCODE_ADDX8, + OPCODE_SUBX2, + OPCODE_SUBX4, + OPCODE_SUBX8, + OPCODE_AND, + OPCODE_OR, + OPCODE_XOR, + OPCODE_BEQI, + OPCODE_BNEI, + OPCODE_BGEI, + OPCODE_BLTI, + OPCODE_BBCI, + OPCODE_BBSI, + OPCODE_BGEUI, + OPCODE_BLTUI, + OPCODE_BEQ, + OPCODE_BNE, + OPCODE_BGE, + OPCODE_BLT, + OPCODE_BGEU, + OPCODE_BLTU, + OPCODE_BANY, + OPCODE_BNONE, + OPCODE_BALL, + OPCODE_BNALL, + OPCODE_BBC, + OPCODE_BBS, + OPCODE_BEQZ, + OPCODE_BNEZ, + OPCODE_BGEZ, + OPCODE_BLTZ, + OPCODE_CALL0, + OPCODE_CALLX0, + OPCODE_EXTUI, + OPCODE_ILL, + OPCODE_J, + OPCODE_JX, + OPCODE_L16UI, + OPCODE_L16SI, + OPCODE_L32I, + OPCODE_L32R, + OPCODE_L8UI, + OPCODE_LOOP, + OPCODE_LOOPNEZ, + OPCODE_LOOPGTZ, + OPCODE_MOVI, + OPCODE_MOVEQZ, + OPCODE_MOVNEZ, + OPCODE_MOVLTZ, + OPCODE_MOVGEZ, + OPCODE_NEG, + OPCODE_ABS, + OPCODE_NOP, + OPCODE_RET, + OPCODE_S16I, + OPCODE_S32I, + OPCODE_S8I, + OPCODE_SSR, + OPCODE_SSL, + OPCODE_SSA8L, + OPCODE_SSA8B, + OPCODE_SSAI, + OPCODE_SLL, + OPCODE_SRC, + OPCODE_SRL, + OPCODE_SRA, + OPCODE_SLLI, + OPCODE_SRAI, + OPCODE_SRLI, + OPCODE_MEMW, + OPCODE_EXTW, + OPCODE_ISYNC, + OPCODE_RSYNC, + OPCODE_ESYNC, + OPCODE_DSYNC, + OPCODE_RSIL, + OPCODE_RSR_LEND, + OPCODE_WSR_LEND, + OPCODE_XSR_LEND, + OPCODE_RSR_LCOUNT, + OPCODE_WSR_LCOUNT, + OPCODE_XSR_LCOUNT, + OPCODE_RSR_LBEG, + OPCODE_WSR_LBEG, + OPCODE_XSR_LBEG, + OPCODE_RSR_SAR, + OPCODE_WSR_SAR, + OPCODE_XSR_SAR, + OPCODE_RSR_LITBASE, + OPCODE_WSR_LITBASE, + OPCODE_XSR_LITBASE, + OPCODE_RSR_176, + OPCODE_WSR_176, + OPCODE_RSR_208, + OPCODE_RSR_PS, + OPCODE_WSR_PS, + OPCODE_XSR_PS, + OPCODE_RSR_EPC1, + OPCODE_WSR_EPC1, + OPCODE_XSR_EPC1, + OPCODE_RSR_EXCSAVE1, + OPCODE_WSR_EXCSAVE1, + OPCODE_XSR_EXCSAVE1, + OPCODE_RSR_EPC2, + OPCODE_WSR_EPC2, + OPCODE_XSR_EPC2, + OPCODE_RSR_EXCSAVE2, + OPCODE_WSR_EXCSAVE2, + OPCODE_XSR_EXCSAVE2, + OPCODE_RSR_EPS2, + OPCODE_WSR_EPS2, + OPCODE_XSR_EPS2, + OPCODE_RSR_EXCVADDR, + OPCODE_WSR_EXCVADDR, + OPCODE_XSR_EXCVADDR, + OPCODE_RSR_DEPC, + OPCODE_WSR_DEPC, + OPCODE_XSR_DEPC, + OPCODE_RSR_EXCCAUSE, + OPCODE_WSR_EXCCAUSE, + OPCODE_XSR_EXCCAUSE, + OPCODE_RSR_MISC0, + OPCODE_WSR_MISC0, + OPCODE_XSR_MISC0, + OPCODE_RSR_MISC1, + OPCODE_WSR_MISC1, + OPCODE_XSR_MISC1, + OPCODE_RSR_PRID, + OPCODE_RSR_VECBASE, + OPCODE_WSR_VECBASE, + OPCODE_XSR_VECBASE, + OPCODE_MUL16U, + OPCODE_MUL16S, + OPCODE_MULL, + OPCODE_RFI, + OPCODE_WAITI, + OPCODE_RSR_INTERRUPT, + OPCODE_WSR_INTSET, + OPCODE_WSR_INTCLEAR, + OPCODE_RSR_INTENABLE, + OPCODE_WSR_INTENABLE, + OPCODE_XSR_INTENABLE, + OPCODE_BREAK, + OPCODE_BREAK_N, + OPCODE_RSR_DEBUGCAUSE, + OPCODE_WSR_DEBUGCAUSE, + OPCODE_XSR_DEBUGCAUSE, + OPCODE_RSR_ICOUNT, + OPCODE_WSR_ICOUNT, + OPCODE_XSR_ICOUNT, + OPCODE_RSR_ICOUNTLEVEL, + OPCODE_WSR_ICOUNTLEVEL, + OPCODE_XSR_ICOUNTLEVEL, + OPCODE_RSR_DDR, + OPCODE_WSR_DDR, + OPCODE_XSR_DDR, + OPCODE_RFDO, + OPCODE_RFDD, + OPCODE_ANDB, + OPCODE_ANDBC, + OPCODE_ORB, + OPCODE_ORBC, + OPCODE_XORB, + OPCODE_ANY4, + OPCODE_ALL4, + OPCODE_ANY8, + OPCODE_ALL8, + OPCODE_BF, + OPCODE_BT, + OPCODE_MOVF, + OPCODE_MOVT, + OPCODE_RSR_BR, + OPCODE_WSR_BR, + OPCODE_XSR_BR, + OPCODE_RSR_CCOUNT, + OPCODE_WSR_CCOUNT, + OPCODE_XSR_CCOUNT, + OPCODE_RSR_CCOMPARE0, + OPCODE_WSR_CCOMPARE0, + OPCODE_XSR_CCOMPARE0, + OPCODE_RSR_CCOMPARE1, + OPCODE_WSR_CCOMPARE1, + OPCODE_XSR_CCOMPARE1, + OPCODE_IPF, + OPCODE_IHI, + OPCODE_III, + OPCODE_LICT, + OPCODE_LICW, + OPCODE_SICT, + OPCODE_SICW, + OPCODE_DHWB, + OPCODE_DHWBI, + OPCODE_DIWB, + OPCODE_DIWBI, + OPCODE_DHI, + OPCODE_DII, + OPCODE_DPFR, + OPCODE_DPFW, + OPCODE_DPFRO, + OPCODE_DPFWO, + OPCODE_SDCT, + OPCODE_LDCT, + OPCODE_WSR_PTEVADDR, + OPCODE_RSR_PTEVADDR, + OPCODE_XSR_PTEVADDR, + OPCODE_RSR_RASID, + OPCODE_WSR_RASID, + OPCODE_XSR_RASID, + OPCODE_RSR_ITLBCFG, + OPCODE_WSR_ITLBCFG, + OPCODE_XSR_ITLBCFG, + OPCODE_RSR_DTLBCFG, + OPCODE_WSR_DTLBCFG, + OPCODE_XSR_DTLBCFG, + OPCODE_IDTLB, + OPCODE_PDTLB, + OPCODE_RDTLB0, + OPCODE_RDTLB1, + OPCODE_WDTLB, + OPCODE_IITLB, + OPCODE_PITLB, + OPCODE_RITLB0, + OPCODE_RITLB1, + OPCODE_WITLB, + OPCODE_LDPTE, + OPCODE_HWWITLBA, + OPCODE_HWWDTLBA, + OPCODE_RSR_CPENABLE, + OPCODE_WSR_CPENABLE, + OPCODE_XSR_CPENABLE, + OPCODE_CLAMPS, + OPCODE_MIN, + OPCODE_MAX, + OPCODE_MINU, + OPCODE_MAXU, + OPCODE_NSA, + OPCODE_NSAU, + OPCODE_SEXT, + OPCODE_L32AI, + OPCODE_S32RI, + OPCODE_S32C1I, + OPCODE_RSR_SCOMPARE1, + OPCODE_WSR_SCOMPARE1, + OPCODE_XSR_SCOMPARE1, + OPCODE_RSR_ATOMCTL, + OPCODE_WSR_ATOMCTL, + OPCODE_XSR_ATOMCTL, + OPCODE_RER, + OPCODE_WER, + OPCODE_RUR_AE_OVF_SAR, + OPCODE_WUR_AE_OVF_SAR, + OPCODE_RUR_AE_BITHEAD, + OPCODE_WUR_AE_BITHEAD, + OPCODE_RUR_AE_TS_FTS_BU_BP, + OPCODE_WUR_AE_TS_FTS_BU_BP, + OPCODE_RUR_AE_SD_NO, + OPCODE_WUR_AE_SD_NO, + OPCODE_RUR_AE_OVERFLOW, + OPCODE_WUR_AE_OVERFLOW, + OPCODE_RUR_AE_SAR, + OPCODE_WUR_AE_SAR, + OPCODE_RUR_AE_BITPTR, + OPCODE_WUR_AE_BITPTR, + OPCODE_RUR_AE_BITSUSED, + OPCODE_WUR_AE_BITSUSED, + OPCODE_RUR_AE_TABLESIZE, + OPCODE_WUR_AE_TABLESIZE, + OPCODE_RUR_AE_FIRST_TS, + OPCODE_WUR_AE_FIRST_TS, + OPCODE_RUR_AE_NEXTOFFSET, + OPCODE_WUR_AE_NEXTOFFSET, + OPCODE_RUR_AE_SEARCHDONE, + OPCODE_WUR_AE_SEARCHDONE, + OPCODE_AE_LP16F_I, + OPCODE_AE_LP16F_IU, + OPCODE_AE_LP16F_X, + OPCODE_AE_LP16F_XU, + OPCODE_AE_LP24_I, + OPCODE_AE_LP24_IU, + OPCODE_AE_LP24_X, + OPCODE_AE_LP24_XU, + OPCODE_AE_LP24F_I, + OPCODE_AE_LP24F_IU, + OPCODE_AE_LP24F_X, + OPCODE_AE_LP24F_XU, + OPCODE_AE_LP16X2F_I, + OPCODE_AE_LP16X2F_IU, + OPCODE_AE_LP16X2F_X, + OPCODE_AE_LP16X2F_XU, + OPCODE_AE_LP24X2F_I, + OPCODE_AE_LP24X2F_IU, + OPCODE_AE_LP24X2F_X, + OPCODE_AE_LP24X2F_XU, + OPCODE_AE_LP24X2_I, + OPCODE_AE_LP24X2_IU, + OPCODE_AE_LP24X2_X, + OPCODE_AE_LP24X2_XU, + OPCODE_AE_SP16X2F_I, + OPCODE_AE_SP16X2F_IU, + OPCODE_AE_SP16X2F_X, + OPCODE_AE_SP16X2F_XU, + OPCODE_AE_SP24X2S_I, + OPCODE_AE_SP24X2S_IU, + OPCODE_AE_SP24X2S_X, + OPCODE_AE_SP24X2S_XU, + OPCODE_AE_SP24X2F_I, + OPCODE_AE_SP24X2F_IU, + OPCODE_AE_SP24X2F_X, + OPCODE_AE_SP24X2F_XU, + OPCODE_AE_SP16F_L_I, + OPCODE_AE_SP16F_L_IU, + OPCODE_AE_SP16F_L_X, + OPCODE_AE_SP16F_L_XU, + OPCODE_AE_SP24S_L_I, + OPCODE_AE_SP24S_L_IU, + OPCODE_AE_SP24S_L_X, + OPCODE_AE_SP24S_L_XU, + OPCODE_AE_SP24F_L_I, + OPCODE_AE_SP24F_L_IU, + OPCODE_AE_SP24F_L_X, + OPCODE_AE_SP24F_L_XU, + OPCODE_AE_LQ56_I, + OPCODE_AE_LQ56_IU, + OPCODE_AE_LQ56_X, + OPCODE_AE_LQ56_XU, + OPCODE_AE_LQ32F_I, + OPCODE_AE_LQ32F_IU, + OPCODE_AE_LQ32F_X, + OPCODE_AE_LQ32F_XU, + OPCODE_AE_SQ56S_I, + OPCODE_AE_SQ56S_IU, + OPCODE_AE_SQ56S_X, + OPCODE_AE_SQ56S_XU, + OPCODE_AE_SQ32F_I, + OPCODE_AE_SQ32F_IU, + OPCODE_AE_SQ32F_X, + OPCODE_AE_SQ32F_XU, + OPCODE_AE_ZEROP48, + OPCODE_AE_MOVP48, + OPCODE_AE_SELP24_LL, + OPCODE_AE_SELP24_LH, + OPCODE_AE_SELP24_HL, + OPCODE_AE_SELP24_HH, + OPCODE_AE_MOVTP24X2, + OPCODE_AE_MOVFP24X2, + OPCODE_AE_MOVTP48, + OPCODE_AE_MOVFP48, + OPCODE_AE_MOVPA24X2, + OPCODE_AE_TRUNCP24A32X2, + OPCODE_AE_CVTA32P24_L, + OPCODE_AE_CVTA32P24_H, + OPCODE_AE_CVTP24A16X2_LL, + OPCODE_AE_CVTP24A16X2_LH, + OPCODE_AE_CVTP24A16X2_HL, + OPCODE_AE_CVTP24A16X2_HH, + OPCODE_AE_TRUNCP24Q48X2, + OPCODE_AE_TRUNCP16, + OPCODE_AE_ROUNDSP24Q48SYM, + OPCODE_AE_ROUNDSP24Q48ASYM, + OPCODE_AE_ROUNDSP16Q48SYM, + OPCODE_AE_ROUNDSP16Q48ASYM, + OPCODE_AE_ROUNDSP16SYM, + OPCODE_AE_ROUNDSP16ASYM, + OPCODE_AE_ZEROQ56, + OPCODE_AE_MOVQ56, + OPCODE_AE_MOVTQ56, + OPCODE_AE_MOVFQ56, + OPCODE_AE_CVTQ48A32S, + OPCODE_AE_CVTQ48P24S_L, + OPCODE_AE_CVTQ48P24S_H, + OPCODE_AE_SATQ48S, + OPCODE_AE_TRUNCQ32, + OPCODE_AE_ROUNDSQ32SYM, + OPCODE_AE_ROUNDSQ32ASYM, + OPCODE_AE_TRUNCA32Q48, + OPCODE_AE_MOVAP24S_L, + OPCODE_AE_MOVAP24S_H, + OPCODE_AE_TRUNCA16P24S_L, + OPCODE_AE_TRUNCA16P24S_H, + OPCODE_AE_ADDP24, + OPCODE_AE_SUBP24, + OPCODE_AE_NEGP24, + OPCODE_AE_ABSP24, + OPCODE_AE_MAXP24S, + OPCODE_AE_MINP24S, + OPCODE_AE_MAXBP24S, + OPCODE_AE_MINBP24S, + OPCODE_AE_ADDSP24S, + OPCODE_AE_SUBSP24S, + OPCODE_AE_NEGSP24S, + OPCODE_AE_ABSSP24S, + OPCODE_AE_ANDP48, + OPCODE_AE_NANDP48, + OPCODE_AE_ORP48, + OPCODE_AE_XORP48, + OPCODE_AE_LTP24S, + OPCODE_AE_LEP24S, + OPCODE_AE_EQP24, + OPCODE_AE_ADDQ56, + OPCODE_AE_SUBQ56, + OPCODE_AE_NEGQ56, + OPCODE_AE_ABSQ56, + OPCODE_AE_MAXQ56S, + OPCODE_AE_MINQ56S, + OPCODE_AE_MAXBQ56S, + OPCODE_AE_MINBQ56S, + OPCODE_AE_ADDSQ56S, + OPCODE_AE_SUBSQ56S, + OPCODE_AE_NEGSQ56S, + OPCODE_AE_ABSSQ56S, + OPCODE_AE_ANDQ56, + OPCODE_AE_NANDQ56, + OPCODE_AE_ORQ56, + OPCODE_AE_XORQ56, + OPCODE_AE_SLLIP24, + OPCODE_AE_SRLIP24, + OPCODE_AE_SRAIP24, + OPCODE_AE_SLLSP24, + OPCODE_AE_SRLSP24, + OPCODE_AE_SRASP24, + OPCODE_AE_SLLISP24S, + OPCODE_AE_SLLSSP24S, + OPCODE_AE_SLLIQ56, + OPCODE_AE_SRLIQ56, + OPCODE_AE_SRAIQ56, + OPCODE_AE_SLLSQ56, + OPCODE_AE_SRLSQ56, + OPCODE_AE_SRASQ56, + OPCODE_AE_SLLAQ56, + OPCODE_AE_SRLAQ56, + OPCODE_AE_SRAAQ56, + OPCODE_AE_SLLISQ56S, + OPCODE_AE_SLLSSQ56S, + OPCODE_AE_SLLASQ56S, + OPCODE_AE_LTQ56S, + OPCODE_AE_LEQ56S, + OPCODE_AE_EQQ56, + OPCODE_AE_NSAQ56S, + OPCODE_AE_MULFS32P16S_LL, + OPCODE_AE_MULFP24S_LL, + OPCODE_AE_MULP24S_LL, + OPCODE_AE_MULFS32P16S_LH, + OPCODE_AE_MULFP24S_LH, + OPCODE_AE_MULP24S_LH, + OPCODE_AE_MULFS32P16S_HL, + OPCODE_AE_MULFP24S_HL, + OPCODE_AE_MULP24S_HL, + OPCODE_AE_MULFS32P16S_HH, + OPCODE_AE_MULFP24S_HH, + OPCODE_AE_MULP24S_HH, + OPCODE_AE_MULAFS32P16S_LL, + OPCODE_AE_MULAFP24S_LL, + OPCODE_AE_MULAP24S_LL, + OPCODE_AE_MULAFS32P16S_LH, + OPCODE_AE_MULAFP24S_LH, + OPCODE_AE_MULAP24S_LH, + OPCODE_AE_MULAFS32P16S_HL, + OPCODE_AE_MULAFP24S_HL, + OPCODE_AE_MULAP24S_HL, + OPCODE_AE_MULAFS32P16S_HH, + OPCODE_AE_MULAFP24S_HH, + OPCODE_AE_MULAP24S_HH, + OPCODE_AE_MULSFS32P16S_LL, + OPCODE_AE_MULSFP24S_LL, + OPCODE_AE_MULSP24S_LL, + OPCODE_AE_MULSFS32P16S_LH, + OPCODE_AE_MULSFP24S_LH, + OPCODE_AE_MULSP24S_LH, + OPCODE_AE_MULSFS32P16S_HL, + OPCODE_AE_MULSFP24S_HL, + OPCODE_AE_MULSP24S_HL, + OPCODE_AE_MULSFS32P16S_HH, + OPCODE_AE_MULSFP24S_HH, + OPCODE_AE_MULSP24S_HH, + OPCODE_AE_MULAFS56P24S_LL, + OPCODE_AE_MULAS56P24S_LL, + OPCODE_AE_MULAFS56P24S_LH, + OPCODE_AE_MULAS56P24S_LH, + OPCODE_AE_MULAFS56P24S_HL, + OPCODE_AE_MULAS56P24S_HL, + OPCODE_AE_MULAFS56P24S_HH, + OPCODE_AE_MULAS56P24S_HH, + OPCODE_AE_MULSFS56P24S_LL, + OPCODE_AE_MULSS56P24S_LL, + OPCODE_AE_MULSFS56P24S_LH, + OPCODE_AE_MULSS56P24S_LH, + OPCODE_AE_MULSFS56P24S_HL, + OPCODE_AE_MULSS56P24S_HL, + OPCODE_AE_MULSFS56P24S_HH, + OPCODE_AE_MULSS56P24S_HH, + OPCODE_AE_MULFQ32SP16S_L, + OPCODE_AE_MULFQ32SP16S_H, + OPCODE_AE_MULFQ32SP16U_L, + OPCODE_AE_MULFQ32SP16U_H, + OPCODE_AE_MULQ32SP16S_L, + OPCODE_AE_MULQ32SP16S_H, + OPCODE_AE_MULQ32SP16U_L, + OPCODE_AE_MULQ32SP16U_H, + OPCODE_AE_MULAFQ32SP16S_L, + OPCODE_AE_MULAFQ32SP16S_H, + OPCODE_AE_MULAFQ32SP16U_L, + OPCODE_AE_MULAFQ32SP16U_H, + OPCODE_AE_MULAQ32SP16S_L, + OPCODE_AE_MULAQ32SP16S_H, + OPCODE_AE_MULAQ32SP16U_L, + OPCODE_AE_MULAQ32SP16U_H, + OPCODE_AE_MULSFQ32SP16S_L, + OPCODE_AE_MULSFQ32SP16S_H, + OPCODE_AE_MULSFQ32SP16U_L, + OPCODE_AE_MULSFQ32SP16U_H, + OPCODE_AE_MULSQ32SP16S_L, + OPCODE_AE_MULSQ32SP16S_H, + OPCODE_AE_MULSQ32SP16U_L, + OPCODE_AE_MULSQ32SP16U_H, + OPCODE_AE_MULZAAQ32SP16S_LL, + OPCODE_AE_MULZAAFQ32SP16S_LL, + OPCODE_AE_MULZAAQ32SP16U_LL, + OPCODE_AE_MULZAAFQ32SP16U_LL, + OPCODE_AE_MULZAAQ32SP16S_HH, + OPCODE_AE_MULZAAFQ32SP16S_HH, + OPCODE_AE_MULZAAQ32SP16U_HH, + OPCODE_AE_MULZAAFQ32SP16U_HH, + OPCODE_AE_MULZAAQ32SP16S_LH, + OPCODE_AE_MULZAAFQ32SP16S_LH, + OPCODE_AE_MULZAAQ32SP16U_LH, + OPCODE_AE_MULZAAFQ32SP16U_LH, + OPCODE_AE_MULZASQ32SP16S_LL, + OPCODE_AE_MULZASFQ32SP16S_LL, + OPCODE_AE_MULZASQ32SP16U_LL, + OPCODE_AE_MULZASFQ32SP16U_LL, + OPCODE_AE_MULZASQ32SP16S_HH, + OPCODE_AE_MULZASFQ32SP16S_HH, + OPCODE_AE_MULZASQ32SP16U_HH, + OPCODE_AE_MULZASFQ32SP16U_HH, + OPCODE_AE_MULZASQ32SP16S_LH, + OPCODE_AE_MULZASFQ32SP16S_LH, + OPCODE_AE_MULZASQ32SP16U_LH, + OPCODE_AE_MULZASFQ32SP16U_LH, + OPCODE_AE_MULZSAQ32SP16S_LL, + OPCODE_AE_MULZSAFQ32SP16S_LL, + OPCODE_AE_MULZSAQ32SP16U_LL, + OPCODE_AE_MULZSAFQ32SP16U_LL, + OPCODE_AE_MULZSAQ32SP16S_HH, + OPCODE_AE_MULZSAFQ32SP16S_HH, + OPCODE_AE_MULZSAQ32SP16U_HH, + OPCODE_AE_MULZSAFQ32SP16U_HH, + OPCODE_AE_MULZSAQ32SP16S_LH, + OPCODE_AE_MULZSAFQ32SP16S_LH, + OPCODE_AE_MULZSAQ32SP16U_LH, + OPCODE_AE_MULZSAFQ32SP16U_LH, + OPCODE_AE_MULZSSQ32SP16S_LL, + OPCODE_AE_MULZSSFQ32SP16S_LL, + OPCODE_AE_MULZSSQ32SP16U_LL, + OPCODE_AE_MULZSSFQ32SP16U_LL, + OPCODE_AE_MULZSSQ32SP16S_HH, + OPCODE_AE_MULZSSFQ32SP16S_HH, + OPCODE_AE_MULZSSQ32SP16U_HH, + OPCODE_AE_MULZSSFQ32SP16U_HH, + OPCODE_AE_MULZSSQ32SP16S_LH, + OPCODE_AE_MULZSSFQ32SP16S_LH, + OPCODE_AE_MULZSSQ32SP16U_LH, + OPCODE_AE_MULZSSFQ32SP16U_LH, + OPCODE_AE_MULZAAFP24S_HH_LL, + OPCODE_AE_MULZAAP24S_HH_LL, + OPCODE_AE_MULZAAFP24S_HL_LH, + OPCODE_AE_MULZAAP24S_HL_LH, + OPCODE_AE_MULZASFP24S_HH_LL, + OPCODE_AE_MULZASP24S_HH_LL, + OPCODE_AE_MULZASFP24S_HL_LH, + OPCODE_AE_MULZASP24S_HL_LH, + OPCODE_AE_MULZSAFP24S_HH_LL, + OPCODE_AE_MULZSAP24S_HH_LL, + OPCODE_AE_MULZSAFP24S_HL_LH, + OPCODE_AE_MULZSAP24S_HL_LH, + OPCODE_AE_MULZSSFP24S_HH_LL, + OPCODE_AE_MULZSSP24S_HH_LL, + OPCODE_AE_MULZSSFP24S_HL_LH, + OPCODE_AE_MULZSSP24S_HL_LH, + OPCODE_AE_MULAAFP24S_HH_LL, + OPCODE_AE_MULAAP24S_HH_LL, + OPCODE_AE_MULAAFP24S_HL_LH, + OPCODE_AE_MULAAP24S_HL_LH, + OPCODE_AE_MULASFP24S_HH_LL, + OPCODE_AE_MULASP24S_HH_LL, + OPCODE_AE_MULASFP24S_HL_LH, + OPCODE_AE_MULASP24S_HL_LH, + OPCODE_AE_MULSAFP24S_HH_LL, + OPCODE_AE_MULSAP24S_HH_LL, + OPCODE_AE_MULSAFP24S_HL_LH, + OPCODE_AE_MULSAP24S_HL_LH, + OPCODE_AE_MULSSFP24S_HH_LL, + OPCODE_AE_MULSSP24S_HH_LL, + OPCODE_AE_MULSSFP24S_HL_LH, + OPCODE_AE_MULSSP24S_HL_LH, + OPCODE_AE_SHA32, + OPCODE_AE_VLDL32T, + OPCODE_AE_VLDL16T, + OPCODE_AE_VLDL16C, + OPCODE_AE_VLDSHT, + OPCODE_AE_LB, + OPCODE_AE_LBI, + OPCODE_AE_LBK, + OPCODE_AE_LBKI, + OPCODE_AE_DB, + OPCODE_AE_DBI, + OPCODE_AE_VLEL32T, + OPCODE_AE_VLEL16T, + OPCODE_AE_SB, + OPCODE_AE_SBI, + OPCODE_AE_VLES16C, + OPCODE_AE_SBF +}; + + +/* Slot-specific opcode decode functions. */ + +static int +Slot_inst_decode (const xtensa_insnbuf insn) +{ + switch (Field_op0_Slot_inst_get (insn)) + { + case 0: + switch (Field_op1_Slot_inst_get (insn)) + { + case 0: + switch (Field_op2_Slot_inst_get (insn)) + { + case 0: + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + switch (Field_m_Slot_inst_get (insn)) + { + case 0: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_n_Slot_inst_get (insn) == 0) + return OPCODE_ILL; + break; + case 2: + switch (Field_n_Slot_inst_get (insn)) + { + case 0: + return OPCODE_RET; + case 1: + return OPCODE_RETW; + case 2: + return OPCODE_JX; + } + break; + case 3: + switch (Field_n_Slot_inst_get (insn)) + { + case 0: + return OPCODE_CALLX0; + case 1: + return OPCODE_CALLX4; + case 2: + return OPCODE_CALLX8; + case 3: + return OPCODE_CALLX12; + } + break; + } + break; + case 1: + return OPCODE_MOVSP; + case 2: + if (Field_s_Slot_inst_get (insn) == 0) + { + switch (Field_t_Slot_inst_get (insn)) + { + case 0: + return OPCODE_ISYNC; + case 1: + return OPCODE_RSYNC; + case 2: + return OPCODE_ESYNC; + case 3: + return OPCODE_DSYNC; + case 8: + return OPCODE_EXCW; + case 12: + return OPCODE_MEMW; + case 13: + return OPCODE_EXTW; + case 15: + return OPCODE_NOP; + } + } + break; + case 3: + switch (Field_t_Slot_inst_get (insn)) + { + case 0: + switch (Field_s_Slot_inst_get (insn)) + { + case 0: + return OPCODE_RFE; + case 2: + return OPCODE_RFDE; + case 4: + return OPCODE_RFWO; + case 5: + return OPCODE_RFWU; + } + break; + case 1: + return OPCODE_RFI; + } + break; + case 4: + return OPCODE_BREAK; + case 5: + switch (Field_s_Slot_inst_get (insn)) + { + case 0: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SYSCALL; + break; + case 1: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SIMCALL; + break; + } + break; + case 6: + return OPCODE_RSIL; + case 7: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_WAITI; + break; + case 8: + return OPCODE_ANY4; + case 9: + return OPCODE_ALL4; + case 10: + return OPCODE_ANY8; + case 11: + return OPCODE_ALL8; + } + break; + case 1: + return OPCODE_AND; + case 2: + return OPCODE_OR; + case 3: + return OPCODE_XOR; + case 4: + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSR; + break; + case 1: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSL; + break; + case 2: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSA8L; + break; + case 3: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SSA8B; + break; + case 4: + if (Field_thi3_Slot_inst_get (insn) == 0) + return OPCODE_SSAI; + break; + case 6: + return OPCODE_RER; + case 7: + return OPCODE_WER; + case 8: + if (Field_s_Slot_inst_get (insn) == 0) + return OPCODE_ROTW; + break; + case 14: + return OPCODE_NSA; + case 15: + return OPCODE_NSAU; + } + break; + case 5: + switch (Field_r_Slot_inst_get (insn)) + { + case 1: + return OPCODE_HWWITLBA; + case 3: + return OPCODE_RITLB0; + case 4: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_IITLB; + break; + case 5: + return OPCODE_PITLB; + case 6: + return OPCODE_WITLB; + case 7: + return OPCODE_RITLB1; + case 9: + return OPCODE_HWWDTLBA; + case 11: + return OPCODE_RDTLB0; + case 12: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_IDTLB; + break; + case 13: + return OPCODE_PDTLB; + case 14: + return OPCODE_WDTLB; + case 15: + return OPCODE_RDTLB1; + } + break; + case 6: + switch (Field_s_Slot_inst_get (insn)) + { + case 0: + return OPCODE_NEG; + case 1: + return OPCODE_ABS; + } + break; + case 8: + return OPCODE_ADD; + case 9: + return OPCODE_ADDX2; + case 10: + return OPCODE_ADDX4; + case 11: + return OPCODE_ADDX8; + case 12: + return OPCODE_SUB; + case 13: + return OPCODE_SUBX2; + case 14: + return OPCODE_SUBX4; + case 15: + return OPCODE_SUBX8; + } + break; + case 1: + switch (Field_op2_Slot_inst_get (insn)) + { + case 0: + case 1: + return OPCODE_SLLI; + case 2: + case 3: + return OPCODE_SRAI; + case 4: + return OPCODE_SRLI; + case 6: + switch (Field_sr_Slot_inst_get (insn)) + { + case 0: + return OPCODE_XSR_LBEG; + case 1: + return OPCODE_XSR_LEND; + case 2: + return OPCODE_XSR_LCOUNT; + case 3: + return OPCODE_XSR_SAR; + case 4: + return OPCODE_XSR_BR; + case 5: + return OPCODE_XSR_LITBASE; + case 12: + return OPCODE_XSR_SCOMPARE1; + case 72: + return OPCODE_XSR_WINDOWBASE; + case 73: + return OPCODE_XSR_WINDOWSTART; + case 83: + return OPCODE_XSR_PTEVADDR; + case 90: + return OPCODE_XSR_RASID; + case 91: + return OPCODE_XSR_ITLBCFG; + case 92: + return OPCODE_XSR_DTLBCFG; + case 99: + return OPCODE_XSR_ATOMCTL; + case 104: + return OPCODE_XSR_DDR; + case 177: + return OPCODE_XSR_EPC1; + case 178: + return OPCODE_XSR_EPC2; + case 192: + return OPCODE_XSR_DEPC; + case 194: + return OPCODE_XSR_EPS2; + case 209: + return OPCODE_XSR_EXCSAVE1; + case 210: + return OPCODE_XSR_EXCSAVE2; + case 224: + return OPCODE_XSR_CPENABLE; + case 228: + return OPCODE_XSR_INTENABLE; + case 230: + return OPCODE_XSR_PS; + case 231: + return OPCODE_XSR_VECBASE; + case 232: + return OPCODE_XSR_EXCCAUSE; + case 233: + return OPCODE_XSR_DEBUGCAUSE; + case 234: + return OPCODE_XSR_CCOUNT; + case 236: + return OPCODE_XSR_ICOUNT; + case 237: + return OPCODE_XSR_ICOUNTLEVEL; + case 238: + return OPCODE_XSR_EXCVADDR; + case 240: + return OPCODE_XSR_CCOMPARE0; + case 241: + return OPCODE_XSR_CCOMPARE1; + case 244: + return OPCODE_XSR_MISC0; + case 245: + return OPCODE_XSR_MISC1; + } + break; + case 8: + return OPCODE_SRC; + case 9: + if (Field_s_Slot_inst_get (insn) == 0) + return OPCODE_SRL; + break; + case 10: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_SLL; + break; + case 11: + if (Field_s_Slot_inst_get (insn) == 0) + return OPCODE_SRA; + break; + case 12: + return OPCODE_MUL16U; + case 13: + return OPCODE_MUL16S; + case 15: + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + return OPCODE_LICT; + case 1: + return OPCODE_SICT; + case 2: + return OPCODE_LICW; + case 3: + return OPCODE_SICW; + case 8: + return OPCODE_LDCT; + case 9: + return OPCODE_SDCT; + case 14: + if (Field_t_Slot_inst_get (insn) == 0) + return OPCODE_RFDO; + if (Field_t_Slot_inst_get (insn) == 1) + return OPCODE_RFDD; + break; + case 15: + return OPCODE_LDPTE; + } + break; + } + break; + case 2: + switch (Field_op2_Slot_inst_get (insn)) + { + case 0: + return OPCODE_ANDB; + case 1: + return OPCODE_ANDBC; + case 2: + return OPCODE_ORB; + case 3: + return OPCODE_ORBC; + case 4: + return OPCODE_XORB; + case 8: + return OPCODE_MULL; + } + break; + case 3: + switch (Field_op2_Slot_inst_get (insn)) + { + case 0: + switch (Field_sr_Slot_inst_get (insn)) + { + case 0: + return OPCODE_RSR_LBEG; + case 1: + return OPCODE_RSR_LEND; + case 2: + return OPCODE_RSR_LCOUNT; + case 3: + return OPCODE_RSR_SAR; + case 4: + return OPCODE_RSR_BR; + case 5: + return OPCODE_RSR_LITBASE; + case 12: + return OPCODE_RSR_SCOMPARE1; + case 72: + return OPCODE_RSR_WINDOWBASE; + case 73: + return OPCODE_RSR_WINDOWSTART; + case 83: + return OPCODE_RSR_PTEVADDR; + case 90: + return OPCODE_RSR_RASID; + case 91: + return OPCODE_RSR_ITLBCFG; + case 92: + return OPCODE_RSR_DTLBCFG; + case 99: + return OPCODE_RSR_ATOMCTL; + case 104: + return OPCODE_RSR_DDR; + case 176: + return OPCODE_RSR_176; + case 177: + return OPCODE_RSR_EPC1; + case 178: + return OPCODE_RSR_EPC2; + case 192: + return OPCODE_RSR_DEPC; + case 194: + return OPCODE_RSR_EPS2; + case 208: + return OPCODE_RSR_208; + case 209: + return OPCODE_RSR_EXCSAVE1; + case 210: + return OPCODE_RSR_EXCSAVE2; + case 224: + return OPCODE_RSR_CPENABLE; + case 226: + return OPCODE_RSR_INTERRUPT; + case 228: + return OPCODE_RSR_INTENABLE; + case 230: + return OPCODE_RSR_PS; + case 231: + return OPCODE_RSR_VECBASE; + case 232: + return OPCODE_RSR_EXCCAUSE; + case 233: + return OPCODE_RSR_DEBUGCAUSE; + case 234: + return OPCODE_RSR_CCOUNT; + case 235: + return OPCODE_RSR_PRID; + case 236: + return OPCODE_RSR_ICOUNT; + case 237: + return OPCODE_RSR_ICOUNTLEVEL; + case 238: + return OPCODE_RSR_EXCVADDR; + case 240: + return OPCODE_RSR_CCOMPARE0; + case 241: + return OPCODE_RSR_CCOMPARE1; + case 244: + return OPCODE_RSR_MISC0; + case 245: + return OPCODE_RSR_MISC1; + } + break; + case 1: + switch (Field_sr_Slot_inst_get (insn)) + { + case 0: + return OPCODE_WSR_LBEG; + case 1: + return OPCODE_WSR_LEND; + case 2: + return OPCODE_WSR_LCOUNT; + case 3: + return OPCODE_WSR_SAR; + case 4: + return OPCODE_WSR_BR; + case 5: + return OPCODE_WSR_LITBASE; + case 12: + return OPCODE_WSR_SCOMPARE1; + case 72: + return OPCODE_WSR_WINDOWBASE; + case 73: + return OPCODE_WSR_WINDOWSTART; + case 83: + return OPCODE_WSR_PTEVADDR; + case 90: + return OPCODE_WSR_RASID; + case 91: + return OPCODE_WSR_ITLBCFG; + case 92: + return OPCODE_WSR_DTLBCFG; + case 99: + return OPCODE_WSR_ATOMCTL; + case 104: + return OPCODE_WSR_DDR; + case 176: + return OPCODE_WSR_176; + case 177: + return OPCODE_WSR_EPC1; + case 178: + return OPCODE_WSR_EPC2; + case 192: + return OPCODE_WSR_DEPC; + case 194: + return OPCODE_WSR_EPS2; + case 209: + return OPCODE_WSR_EXCSAVE1; + case 210: + return OPCODE_WSR_EXCSAVE2; + case 224: + return OPCODE_WSR_CPENABLE; + case 226: + return OPCODE_WSR_INTSET; + case 227: + return OPCODE_WSR_INTCLEAR; + case 228: + return OPCODE_WSR_INTENABLE; + case 230: + return OPCODE_WSR_PS; + case 231: + return OPCODE_WSR_VECBASE; + case 232: + return OPCODE_WSR_EXCCAUSE; + case 233: + return OPCODE_WSR_DEBUGCAUSE; + case 234: + return OPCODE_WSR_CCOUNT; + case 236: + return OPCODE_WSR_ICOUNT; + case 237: + return OPCODE_WSR_ICOUNTLEVEL; + case 238: + return OPCODE_WSR_EXCVADDR; + case 240: + return OPCODE_WSR_CCOMPARE0; + case 241: + return OPCODE_WSR_CCOMPARE1; + case 244: + return OPCODE_WSR_MISC0; + case 245: + return OPCODE_WSR_MISC1; + } + break; + case 2: + return OPCODE_SEXT; + case 3: + return OPCODE_CLAMPS; + case 4: + return OPCODE_MIN; + case 5: + return OPCODE_MAX; + case 6: + return OPCODE_MINU; + case 7: + return OPCODE_MAXU; + case 8: + return OPCODE_MOVEQZ; + case 9: + return OPCODE_MOVNEZ; + case 10: + return OPCODE_MOVLTZ; + case 11: + return OPCODE_MOVGEZ; + case 12: + return OPCODE_MOVF; + case 13: + return OPCODE_MOVT; + case 14: + switch (Field_st_Slot_inst_get (insn)) + { + case 231: + return OPCODE_RUR_THREADPTR; + case 240: + return OPCODE_RUR_AE_OVF_SAR; + case 241: + return OPCODE_RUR_AE_BITHEAD; + case 242: + return OPCODE_RUR_AE_TS_FTS_BU_BP; + case 243: + return OPCODE_RUR_AE_SD_NO; + } + break; + case 15: + switch (Field_sr_Slot_inst_get (insn)) + { + case 231: + return OPCODE_WUR_THREADPTR; + case 240: + return OPCODE_WUR_AE_OVF_SAR; + case 241: + return OPCODE_WUR_AE_BITHEAD; + case 242: + return OPCODE_WUR_AE_TS_FTS_BU_BP; + case 243: + return OPCODE_WUR_AE_SD_NO; + } + break; + } + break; + case 4: + case 5: + return OPCODE_EXTUI; + case 9: + switch (Field_op2_Slot_inst_get (insn)) + { + case 0: + return OPCODE_L32E; + case 4: + return OPCODE_S32E; + } + break; + } + break; + case 1: + return OPCODE_L32R; + case 2: + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + return OPCODE_L8UI; + case 1: + return OPCODE_L16UI; + case 2: + return OPCODE_L32I; + case 4: + return OPCODE_S8I; + case 5: + return OPCODE_S16I; + case 6: + return OPCODE_S32I; + case 7: + switch (Field_t_Slot_inst_get (insn)) + { + case 0: + return OPCODE_DPFR; + case 1: + return OPCODE_DPFW; + case 2: + return OPCODE_DPFRO; + case 3: + return OPCODE_DPFWO; + case 4: + return OPCODE_DHWB; + case 5: + return OPCODE_DHWBI; + case 6: + return OPCODE_DHI; + case 7: + return OPCODE_DII; + case 8: + switch (Field_op1_Slot_inst_get (insn)) + { + case 4: + return OPCODE_DIWB; + case 5: + return OPCODE_DIWBI; + } + break; + case 12: + return OPCODE_IPF; + case 14: + return OPCODE_IHI; + case 15: + return OPCODE_III; + } + break; + case 9: + return OPCODE_L16SI; + case 10: + return OPCODE_MOVI; + case 11: + return OPCODE_L32AI; + case 12: + return OPCODE_ADDI; + case 13: + return OPCODE_ADDMI; + case 14: + return OPCODE_S32C1I; + case 15: + return OPCODE_S32RI; + } + break; + case 4: + switch (Field_ae_r10_Slot_inst_get (insn)) + { + case 0: + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_I; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_X; + break; + case 1: + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_I; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_X; + break; + case 2: + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_IU; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ56_XU; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_CVTQ48A32S; + break; + case 3: + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_IU; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LQ32F_XU; + break; + } + switch (Field_ae_r3_Slot_inst_get (insn)) + { + case 0: + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_I; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_IU; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_X; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16F_XU; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24F_I; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24F_IU; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24F_X; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP24F_XU; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2F_I; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2F_IU; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2F_X; + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP24X2F_XU; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_I; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_IU; + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_X; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16X2F_XU; + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_I; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_IU; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_X; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2F_XU; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_I; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_IU; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_X; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24S_L_XU; + if (Field_ae_s3_Slot_inst_get (insn) == 0 && + Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVP48; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVPA24X2; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_CVTA32P24_L; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_LL; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_HL; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVAP24S_L; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_TRUNCA16P24S_L; + break; + case 1: + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_I; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_IU; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_X; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24_XU; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16X2F_I; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16X2F_IU; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP16X2F_X; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP16X2F_XU; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2_I; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2_IU; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LP24X2_X; + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_LP24X2_XU; + if (Field_op1_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_I; + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_IU; + if (Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_X; + if (Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24X2S_XU; + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_I; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_IU; + if (Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_X; + if (Field_op1_Slot_inst_get (insn) == 12 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP16F_L_XU; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_I; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_IU; + if (Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_X; + if (Field_op1_Slot_inst_get (insn) == 13 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_SP24F_L_XU; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_TRUNCP24A32X2; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 11 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_CVTA32P24_H; + if (Field_op1_Slot_inst_get (insn) == 14 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_LH; + if (Field_op1_Slot_inst_get (insn) == 15 && + Field_op2_Slot_inst_get (insn) == 11) + return OPCODE_AE_CVTP24A16X2_HH; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 7 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVAP24S_H; + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 8 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_TRUNCA16P24S_H; + break; + } + switch (Field_ae_r32_Slot_inst_get (insn)) + { + case 0: + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_I; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_X; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_TRUNCA32Q48; + break; + case 1: + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_I; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_X; + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_NSAQ56S; + break; + case 2: + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_IU; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ56S_XU; + break; + case 3: + if (Field_op1_Slot_inst_get (insn) == 3 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_IU; + if (Field_op1_Slot_inst_get (insn) == 4 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SQ32F_XU; + break; + } + switch (Field_ae_s_non_samt_Slot_inst_get (insn)) + { + case 0: + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLIQ56; + break; + case 1: + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRLIQ56; + break; + case 2: + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRAIQ56; + break; + case 3: + if (Field_op1_Slot_inst_get (insn) == 5 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLISQ56S; + break; + } + switch (Field_op1_Slot_inst_get (insn)) + { + case 0: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SHA32; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLDL32T; + break; + case 1: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SLLAQ56; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLDL16T; + break; + case 2: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SRLAQ56; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_LBK; + break; + case 3: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SRAAQ56; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLEL32T; + break; + case 4: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SLLASQ56S; + if (Field_op2_Slot_inst_get (insn) == 10) + return OPCODE_AE_VLEL16T; + break; + case 5: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_MOVTQ56; + break; + case 6: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_MOVFQ56; + break; + } + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_OVERFLOW; + if (Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_SBI; + break; + case 1: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_SAR; + if (Field_op1_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_DB; + if (Field_op1_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_SB; + break; + case 2: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_BITPTR; + break; + case 3: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_BITSUSED; + break; + case 4: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_TABLESIZE; + break; + case 5: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_FIRST_TS; + break; + case 6: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_NEXTOFFSET; + break; + case 7: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_WUR_AE_SEARCHDONE; + break; + case 8: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 10 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_VLDSHT; + break; + case 12: + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_VLES16C; + break; + case 13: + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_SBF; + break; + case 14: + if (Field_op1_Slot_inst_get (insn) == 7 && + Field_t_Slot_inst_get (insn) == 1 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_VLDL16C; + break; + } + switch (Field_s_Slot_inst_get (insn)) + { + case 0: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLSQ56; + if (Field_op1_Slot_inst_get (insn) == 6 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_LB; + break; + case 1: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRLSQ56; + break; + case 2: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SRASQ56; + break; + case 3: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_SLLSSQ56S; + break; + case 4: + if (Field_t_Slot_inst_get (insn) == 1 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_AE_MOVQ56; + break; + case 8: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_OVERFLOW; + break; + case 9: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_SAR; + break; + case 10: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_BITPTR; + break; + case 11: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_BITSUSED; + break; + case 12: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_TABLESIZE; + break; + case 13: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_FIRST_TS; + break; + case 14: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_NEXTOFFSET; + break; + case 15: + if (Field_t_Slot_inst_get (insn) == 0 && + Field_op1_Slot_inst_get (insn) == 9 && + Field_op2_Slot_inst_get (insn) == 12) + return OPCODE_RUR_AE_SEARCHDONE; + break; + } + switch (Field_t_Slot_inst_get (insn)) + { + case 0: + if (Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_LBKI; + if (Field_r_Slot_inst_get (insn) == 2 && + Field_op2_Slot_inst_get (insn) == 15) + return OPCODE_AE_DBI; + break; + case 2: + if (Field_s_Slot_inst_get (insn) == 0 && + Field_op2_Slot_inst_get (insn) == 14) + return OPCODE_AE_LBI; + break; + } + break; + case 5: + switch (Field_n_Slot_inst_get (insn)) + { + case 0: + return OPCODE_CALL0; + case 1: + return OPCODE_CALL4; + case 2: + return OPCODE_CALL8; + case 3: + return OPCODE_CALL12; + } + break; + case 6: + switch (Field_n_Slot_inst_get (insn)) + { + case 0: + return OPCODE_J; + case 1: + switch (Field_m_Slot_inst_get (insn)) + { + case 0: + return OPCODE_BEQZ; + case 1: + return OPCODE_BNEZ; + case 2: + return OPCODE_BLTZ; + case 3: + return OPCODE_BGEZ; + } + break; + case 2: + switch (Field_m_Slot_inst_get (insn)) + { + case 0: + return OPCODE_BEQI; + case 1: + return OPCODE_BNEI; + case 2: + return OPCODE_BLTI; + case 3: + return OPCODE_BGEI; + } + break; + case 3: + switch (Field_m_Slot_inst_get (insn)) + { + case 0: + return OPCODE_ENTRY; + case 1: + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + return OPCODE_BF; + case 1: + return OPCODE_BT; + case 8: + return OPCODE_LOOP; + case 9: + return OPCODE_LOOPNEZ; + case 10: + return OPCODE_LOOPGTZ; + } + break; + case 2: + return OPCODE_BLTUI; + case 3: + return OPCODE_BGEUI; + } + break; + } + break; + case 7: + switch (Field_r_Slot_inst_get (insn)) + { + case 0: + return OPCODE_BNONE; + case 1: + return OPCODE_BEQ; + case 2: + return OPCODE_BLT; + case 3: + return OPCODE_BLTU; + case 4: + return OPCODE_BALL; + case 5: + return OPCODE_BBC; + case 6: + case 7: + return OPCODE_BBCI; + case 8: + return OPCODE_BANY; + case 9: + return OPCODE_BNE; + case 10: + return OPCODE_BGE; + case 11: + return OPCODE_BGEU; + case 12: + return OPCODE_BNALL; + case 13: + return OPCODE_BBS; + case 14: + case 15: + return OPCODE_BBSI; + } + break; + } + return XTENSA_UNDEFINED; +} + +static int +Slot_inst16b_decode (const xtensa_insnbuf insn) +{ + switch (Field_op0_Slot_inst16b_get (insn)) + { + case 12: + switch (Field_i_Slot_inst16b_get (insn)) + { + case 0: + return OPCODE_MOVI_N; + case 1: + switch (Field_z_Slot_inst16b_get (insn)) + { + case 0: + return OPCODE_BEQZ_N; + case 1: + return OPCODE_BNEZ_N; + } + break; + } + break; + case 13: + switch (Field_r_Slot_inst16b_get (insn)) + { + case 0: + return OPCODE_MOV_N; + case 15: + switch (Field_t_Slot_inst16b_get (insn)) + { + case 0: + return OPCODE_RET_N; + case 1: + return OPCODE_RETW_N; + case 2: + return OPCODE_BREAK_N; + case 3: + if (Field_s_Slot_inst16b_get (insn) == 0) + return OPCODE_NOP_N; + break; + case 6: + if (Field_s_Slot_inst16b_get (insn) == 0) + return OPCODE_ILL_N; + break; + } + break; + } + break; + } + return XTENSA_UNDEFINED; +} + +static int +Slot_inst16a_decode (const xtensa_insnbuf insn) +{ + switch (Field_op0_Slot_inst16a_get (insn)) + { + case 8: + return OPCODE_L32I_N; + case 9: + return OPCODE_S32I_N; + case 10: + return OPCODE_ADD_N; + case 11: + return OPCODE_ADDI_N; + } + return XTENSA_UNDEFINED; +} + +static int +Slot_ae_slot0_decode (const xtensa_insnbuf insn) +{ + if (Field_ftsf212ae_slot0_Slot_ae_slot0_get (insn) == 0 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_J; + if (Field_ftsf213ae_slot0_Slot_ae_slot0_get (insn) == 2 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_EXTUI; + switch (Field_ftsf214ae_slot0_Slot_ae_slot0_get (insn)) + { + case 6: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BGEZ; + break; + case 7: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BLTZ; + break; + case 8: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BEQZ; + break; + case 9: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_BNEZ; + break; + case 10: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVI; + break; + } + switch (Field_ftsf215ae_slot0_Slot_ae_slot0_get (insn)) + { + case 88: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRAI; + break; + case 96: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SLLI; + break; + case 123: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf364ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVTQ56; + break; + } + if (Field_ftsf216ae_slot0_Slot_ae_slot0_get (insn) == 418 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_HH; + if (Field_ftsf217_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 4 && + Field_ae_r20_Slot_ae_slot0_get (insn) == 0) + return OPCODE_L32I; + if (Field_ftsf218ae_slot0_Slot_ae_slot0_get (insn) == 419 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_I; + if (Field_ftsf219ae_slot0_Slot_ae_slot0_get (insn) == 420 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_HL; + if (Field_ftsf220ae_slot0_Slot_ae_slot0_get (insn) == 421 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_IU; + if (Field_ftsf221ae_slot0_Slot_ae_slot0_get (insn) == 422 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_X; + if (Field_ftsf222ae_slot0_Slot_ae_slot0_get (insn) == 423 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16F_XU; + if (Field_ftsf223ae_slot0_Slot_ae_slot0_get (insn) == 424 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_LH; + if (Field_ftsf224ae_slot0_Slot_ae_slot0_get (insn) == 425 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_I; + if (Field_ftsf225ae_slot0_Slot_ae_slot0_get (insn) == 426 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_IU; + if (Field_ftsf226ae_slot0_Slot_ae_slot0_get (insn) == 427 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_XU; + if (Field_ftsf227ae_slot0_Slot_ae_slot0_get (insn) == 428 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP16X2F_X; + if (Field_ftsf228ae_slot0_Slot_ae_slot0_get (insn) == 429 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_I; + if (Field_ftsf229ae_slot0_Slot_ae_slot0_get (insn) == 430 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_IU; + if (Field_ftsf230ae_slot0_Slot_ae_slot0_get (insn) == 431 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_X; + if (Field_ftsf231ae_slot0_Slot_ae_slot0_get (insn) == 432 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTP24A16X2_LL; + if (Field_ftsf232ae_slot0_Slot_ae_slot0_get (insn) == 433 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24_XU; + if (Field_ftsf233ae_slot0_Slot_ae_slot0_get (insn) == 434 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_I; + if (Field_ftsf234ae_slot0_Slot_ae_slot0_get (insn) == 435 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_XU; + if (Field_ftsf235ae_slot0_Slot_ae_slot0_get (insn) == 436 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_IU; + if (Field_ftsf236ae_slot0_Slot_ae_slot0_get (insn) == 437 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_I; + if (Field_ftsf237ae_slot0_Slot_ae_slot0_get (insn) == 438 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_IU; + if (Field_ftsf238ae_slot0_Slot_ae_slot0_get (insn) == 439 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_X; + if (Field_ftsf239ae_slot0_Slot_ae_slot0_get (insn) == 440 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24F_X; + if (Field_ftsf240ae_slot0_Slot_ae_slot0_get (insn) == 441 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2_XU; + if (Field_ftsf241ae_slot0_Slot_ae_slot0_get (insn) == 442 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_I; + if (Field_ftsf242ae_slot0_Slot_ae_slot0_get (insn) == 443 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_X; + if (Field_ftsf243ae_slot0_Slot_ae_slot0_get (insn) == 444 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_IU; + if (Field_ftsf244ae_slot0_Slot_ae_slot0_get (insn) == 445 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LP24X2F_XU; + if (Field_ftsf245ae_slot0_Slot_ae_slot0_get (insn) == 446 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVPA24X2; + if (Field_ftsf246ae_slot0_Slot_ae_slot0_get (insn) == 447 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_I; + if (Field_ftsf247ae_slot0_Slot_ae_slot0_get (insn) == 450 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_IU; + if (Field_ftsf248ae_slot0_Slot_ae_slot0_get (insn) == 451 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_X; + if (Field_ftsf249ae_slot0_Slot_ae_slot0_get (insn) == 452 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_X; + if (Field_ftsf250ae_slot0_Slot_ae_slot0_get (insn) == 453 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_XU; + if (Field_ftsf251ae_slot0_Slot_ae_slot0_get (insn) == 454 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_I; + if (Field_ftsf252ae_slot0_Slot_ae_slot0_get (insn) == 455 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_IU; + if (Field_ftsf253ae_slot0_Slot_ae_slot0_get (insn) == 456 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16F_L_XU; + if (Field_ftsf254ae_slot0_Slot_ae_slot0_get (insn) == 457 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_X; + if (Field_ftsf255ae_slot0_Slot_ae_slot0_get (insn) == 458 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24F_L_XU; + if (Field_ftsf256ae_slot0_Slot_ae_slot0_get (insn) == 459 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_IU; + if (Field_ftsf257ae_slot0_Slot_ae_slot0_get (insn) == 460 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_I; + if (Field_ftsf258ae_slot0_Slot_ae_slot0_get (insn) == 461 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_X; + if (Field_ftsf259ae_slot0_Slot_ae_slot0_get (insn) == 462 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24S_L_XU; + if (Field_ftsf260ae_slot0_Slot_ae_slot0_get (insn) == 463 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_I; + if (Field_ftsf261ae_slot0_Slot_ae_slot0_get (insn) == 464 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_I; + if (Field_ftsf262ae_slot0_Slot_ae_slot0_get (insn) == 465 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_IU; + if (Field_ftsf263ae_slot0_Slot_ae_slot0_get (insn) == 466 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_X; + if (Field_ftsf264ae_slot0_Slot_ae_slot0_get (insn) == 467 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_IU; + if (Field_ftsf265ae_slot0_Slot_ae_slot0_get (insn) == 468 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2F_XU; + if (Field_ftsf266ae_slot0_Slot_ae_slot0_get (insn) == 469 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_X; + if (Field_ftsf267ae_slot0_Slot_ae_slot0_get (insn) == 470 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_XU; + if (Field_ftsf268ae_slot0_Slot_ae_slot0_get (insn) == 471 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_TRUNCP24A32X2; + if (Field_ftsf269ae_slot0_Slot_ae_slot0_get (insn) == 472 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP24X2S_I; + if (Field_ftsf270ae_slot0_Slot_ae_slot0_get (insn) == 946 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ32F_I; + if (Field_ftsf271ae_slot0_Slot_ae_slot0_get (insn) == 947 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ32F_IU; + if (Field_ftsf272ae_slot0_Slot_ae_slot0_get (insn) == 948 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ32F_I; + if (Field_ftsf273ae_slot0_Slot_ae_slot0_get (insn) == 949 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ32F_X; + if (Field_ftsf274ae_slot0_Slot_ae_slot0_get (insn) == 950 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ32F_XU; + if (Field_ftsf275ae_slot0_Slot_ae_slot0_get (insn) == 951 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ56_I; + if (Field_ftsf276ae_slot0_Slot_ae_slot0_get (insn) == 952 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ32F_IU; + if (Field_ftsf277ae_slot0_Slot_ae_slot0_get (insn) == 953 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ56_IU; + if (Field_ftsf278ae_slot0_Slot_ae_slot0_get (insn) == 954 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_LQ56_X; + if (Field_ftsf279ae_slot0_Slot_ae_slot0_get (insn) == 15280 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTQ48A32S; + if (Field_ftsf281ae_slot0_Slot_ae_slot0_get (insn) == 60977 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_JX; + if (Field_ftsf282ae_slot0_Slot_ae_slot0_get (insn) == 61041 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SSR; + if (Field_ftsf283ae_slot0_Slot_ae_slot0_get (insn) == 30577 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf352ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_NOP; + if (Field_ftsf284ae_slot0_Slot_ae_slot0_get (insn) == 7641 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf354ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSA8B; + if (Field_ftsf286ae_slot0_Slot_ae_slot0_get (insn) == 3821 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf356ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSA8L; + if (Field_ftsf288ae_slot0_Slot_ae_slot0_get (insn) == 1911 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf359ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSL; + if (Field_ftsf290ae_slot0_Slot_ae_slot0_get (insn) == 478 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s8_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_LQ56_XU; + if (Field_ftsf292ae_slot0_Slot_ae_slot0_get (insn) == 1913 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ALL8; + switch (Field_ftsf293_Slot_ae_slot0_get (insn)) + { + case 0: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBCI; + break; + case 1: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBSI; + break; + } + if (Field_ftsf294ae_slot0_Slot_ae_slot0_get (insn) == 1915 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_s_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ANY8; + if (Field_ftsf295ae_slot0_Slot_ae_slot0_get (insn) == 959 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf358ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_SSAI; + if (Field_ftsf296ae_slot0_Slot_ae_slot0_get (insn) == 480 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SP16X2F_IU; + if (Field_ftsf297ae_slot0_Slot_ae_slot0_get (insn) == 962 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_I; + if (Field_ftsf298ae_slot0_Slot_ae_slot0_get (insn) == 963 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_IU; + switch (Field_ftsf299ae_slot0_Slot_ae_slot0_get (insn)) + { + case 964: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLIQ56; + break; + case 965: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SRAIQ56; + break; + case 966: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SRLIQ56; + break; + case 968: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLISQ56S; + break; + } + switch (Field_ftsf300ae_slot0_Slot_ae_slot0_get (insn)) + { + case 3868: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ABS; + break; + case 3869: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_NEG; + break; + case 3870: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRA; + break; + case 3871: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRL; + break; + } + switch (Field_ftsf301ae_slot0_Slot_ae_slot0_get (insn)) + { + case 7752: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf321_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVP48; + break; + case 7753: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf353_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ANY4; + break; + } + if (Field_ftsf302ae_slot0_Slot_ae_slot0_get (insn) == 31016 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf321_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_MOVQ56; + if (Field_ftsf303ae_slot0_Slot_ae_slot0_get (insn) == 31017 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf321_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SLLSSQ56S; + if (Field_ftsf304ae_slot0_Slot_ae_slot0_get (insn) == 15509 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf369ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRASQ56; + if (Field_ftsf306ae_slot0_Slot_ae_slot0_get (insn) == 7755 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf368ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRLSQ56; + if (Field_ftsf308ae_slot0_Slot_ae_slot0_get (insn) == 1939 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf366ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SLLSQ56; + if (Field_ftsf309ae_slot0_Slot_ae_slot0_get (insn) == 485 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf360ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_ALL4; + if (Field_ftsf310ae_slot0_Slot_ae_slot0_get (insn) == 972 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_X; + if (Field_ftsf311ae_slot0_Slot_ae_slot0_get (insn) == 973 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SQ56S_XU; + if (Field_ftsf312ae_slot0_Slot_ae_slot0_get (insn) == 7792 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTA32P24_H; + if (Field_ftsf313ae_slot0_Slot_ae_slot0_get (insn) == 7793 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_CVTA32P24_L; + if (Field_ftsf314ae_slot0_Slot_ae_slot0_get (insn) == 7794 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVAP24S_H; + if (Field_ftsf315ae_slot0_Slot_ae_slot0_get (insn) == 7795 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_TRUNCA16P24S_L; + if (Field_ftsf316ae_slot0_Slot_ae_slot0_get (insn) == 7796 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVAP24S_L; + if (Field_ftsf317ae_slot0_Slot_ae_slot0_get (insn) == 7797 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf353_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_NSAQ56S; + if (Field_ftsf318ae_slot0_Slot_ae_slot0_get (insn) == 3899 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf365ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_TRUNCA32Q48; + if (Field_ftsf319_Slot_ae_slot0_get (insn) == 3 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3 && + Field_ftsf361ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_BT; + if (Field_ftsf320ae_slot0_Slot_ae_slot0_get (insn) == 975 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_TRUNCA16P24S_H; + if (Field_ftsf321_Slot_ae_slot0_get (insn) == 1 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 3 && + Field_ae_s20_Slot_ae_slot0_get (insn) == 0) + return OPCODE_BLTUI; + if (Field_ftsf322ae_slot0_Slot_ae_slot0_get (insn) == 3920 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_MOVFQ56; + if (Field_ftsf323ae_slot0_Slot_ae_slot0_get (insn) == 3921 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLAQ56; + if (Field_ftsf324ae_slot0_Slot_ae_slot0_get (insn) == 3922 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AE_SLLASQ56S; + if (Field_ftsf325ae_slot0_Slot_ae_slot0_get (insn) == 3923 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SLL; + if (Field_ftsf326ae_slot0_Slot_ae_slot0_get (insn) == 981 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf357_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRAAQ56; + if (Field_ftsf328ae_slot0_Slot_ae_slot0_get (insn) == 491 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SRLAQ56; + if (Field_ftsf329ae_slot0_Slot_ae_slot0_get (insn) == 31 && + Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ftsf362ae_slot0_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SQ32F_XU; + switch (Field_imm8_Slot_ae_slot0_get (insn)) + { + case 178: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADD; + break; + case 179: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADDX8; + break; + case 180: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADDX2; + break; + case 181: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_AND; + break; + case 182: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ANDB; + break; + case 183: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ANDBC; + break; + case 184: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ADDX4; + break; + case 185: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_CLAMPS; + break; + case 186: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MAX; + break; + case 187: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MIN; + break; + case 188: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MAXU; + break; + case 189: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MINU; + break; + case 190: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVEQZ; + break; + case 191: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVF; + break; + case 194: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVGEZ; + break; + case 195: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ORB; + break; + case 196: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVLTZ; + break; + case 197: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_ORBC; + break; + case 198: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SEXT; + break; + case 199: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRC; + break; + case 200: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVNEZ; + break; + case 201: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SRLI; + break; + case 202: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUB; + break; + case 203: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUBX4; + break; + case 204: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUBX2; + break; + case 205: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_SUBX8; + break; + case 206: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_XOR; + break; + case 207: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_XORB; + break; + case 208: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_MOVT; + break; + case 224: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1) + return OPCODE_OR; + break; + case 244: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 1 && + Field_ae_r32_Slot_ae_slot0_get (insn) == 0) + return OPCODE_AE_SQ32F_X; + break; + } + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 5) + return OPCODE_L32R; + switch (Field_r_Slot_ae_slot0_get (insn)) + { + case 0: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_BNE; + break; + case 1: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_BNONE; + break; + case 2: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_L16SI; + break; + case 3: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_L8UI; + break; + case 4: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_ADDI; + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_L16UI; + break; + case 5: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BALL; + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_S16I; + break; + case 6: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BANY; + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_S32I; + break; + case 7: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBC; + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 4) + return OPCODE_S8I; + break; + case 8: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_ADDMI; + break; + case 9: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BBS; + break; + case 10: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BEQ; + break; + case 11: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BGEU; + break; + case 12: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BGE; + break; + case 13: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BLT; + break; + case 14: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BLTU; + break; + case 15: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 2) + return OPCODE_BNALL; + break; + } + switch (Field_t_Slot_ae_slot0_get (insn)) + { + case 0: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BEQI; + break; + case 1: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BGEI; + break; + case 2: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BGEUI; + break; + case 3: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BNEI; + break; + case 4: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 3) + return OPCODE_BLTI; + break; + case 5: + if (Field_op0_s4_Slot_ae_slot0_get (insn) == 3 && + Field_r_Slot_ae_slot0_get (insn) == 0) + return OPCODE_BF; + break; + } + return XTENSA_UNDEFINED; +} + +static int +Slot_ae_slot1_decode (const xtensa_insnbuf insn) +{ + if (Field_ftsf100ae_slot1_Slot_ae_slot1_get (insn) == 115 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_NEGSP24S; + if (Field_ftsf101ae_slot1_Slot_ae_slot1_get (insn) == 29 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf348ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ABSSP24S; + if (Field_ftsf103ae_slot1_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf349ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_NEGP24; + if (Field_ftsf104ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MAXBQ56S; + if (Field_ftsf105ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MINBQ56S; + if (Field_ftsf106ae_slot1_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ae_r32_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_EQQ56; + if (Field_ftsf107ae_slot1_Slot_ae_slot1_get (insn) == 48 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ADDSQ56S; + if (Field_ftsf108ae_slot1_Slot_ae_slot1_get (insn) == 49 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ANDQ56; + if (Field_ftsf109ae_slot1_Slot_ae_slot1_get (insn) == 50 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MAXQ56S; + if (Field_ftsf110ae_slot1_Slot_ae_slot1_get (insn) == 51 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ORQ56; + if (Field_ftsf111ae_slot1_Slot_ae_slot1_get (insn) == 52 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_MINQ56S; + if (Field_ftsf112ae_slot1_Slot_ae_slot1_get (insn) == 53 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_SUBQ56; + if (Field_ftsf113ae_slot1_Slot_ae_slot1_get (insn) == 54 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_SUBSQ56S; + if (Field_ftsf114ae_slot1_Slot_ae_slot1_get (insn) == 55 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_XORQ56; + if (Field_ftsf115ae_slot1_Slot_ae_slot1_get (insn) == 56 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_NANDQ56; + if (Field_ftsf116ae_slot1_Slot_ae_slot1_get (insn) == 57 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_ABSQ56; + if (Field_ftsf118ae_slot1_Slot_ae_slot1_get (insn) == 185 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5) + return OPCODE_AE_NEGSQ56S; + if (Field_ftsf119ae_slot1_Slot_ae_slot1_get (insn) == 185 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf338_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_SATQ48S; + if (Field_ftsf12_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf341ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_LTQ56S; + if (Field_ftsf120ae_slot1_Slot_ae_slot1_get (insn) == 29 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf343ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ABSSQ56S; + if (Field_ftsf122ae_slot1_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf346ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_NEGQ56; + if (Field_ftsf124ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf339ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_LEQ56S; + if (Field_ftsf125ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf350ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_TRUNCP24Q48X2; + if (Field_ftsf126ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 5 && + Field_ftsf344ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ADDQ56; + if (Field_ftsf127ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAFP24S_HH_LL; + if (Field_ftsf128ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAFP24S_HL_LH; + if (Field_ftsf129ae_slot1_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAP24S_HH_LL; + if (Field_ftsf13_Slot_ae_slot1_get (insn) == 2 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf12_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_SLLISP24S; + if (Field_ftsf130ae_slot1_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS32P16S_HL; + if (Field_ftsf131ae_slot1_Slot_ae_slot1_get (insn) == 4 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAAP24S_HL_LH; + if (Field_ftsf132ae_slot1_Slot_ae_slot1_get (insn) == 5 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS32P16S_LH; + if (Field_ftsf133ae_slot1_Slot_ae_slot1_get (insn) == 6 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS32P16S_LL; + if (Field_ftsf134ae_slot1_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_HH; + if (Field_ftsf135ae_slot1_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_HH; + if (Field_ftsf136ae_slot1_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_HL; + if (Field_ftsf137ae_slot1_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_LH; + if (Field_ftsf138ae_slot1_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_HH; + if (Field_ftsf139ae_slot1_Slot_ae_slot1_get (insn) == 12 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFS56P24S_LL; + if (Field_ftsf140ae_slot1_Slot_ae_slot1_get (insn) == 13 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_HL; + if (Field_ftsf141ae_slot1_Slot_ae_slot1_get (insn) == 14 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_LH; + if (Field_ftsf142ae_slot1_Slot_ae_slot1_get (insn) == 15 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAP24S_LL; + if (Field_ftsf143ae_slot1_Slot_ae_slot1_get (insn) == 16 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_HL; + if (Field_ftsf144ae_slot1_Slot_ae_slot1_get (insn) == 17 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_HH; + if (Field_ftsf145ae_slot1_Slot_ae_slot1_get (insn) == 18 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_HL; + if (Field_ftsf146ae_slot1_Slot_ae_slot1_get (insn) == 19 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASFP24S_HH_LL; + if (Field_ftsf147ae_slot1_Slot_ae_slot1_get (insn) == 20 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_LH; + if (Field_ftsf148ae_slot1_Slot_ae_slot1_get (insn) == 21 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASFP24S_HL_LH; + if (Field_ftsf149ae_slot1_Slot_ae_slot1_get (insn) == 22 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASP24S_HH_LL; + if (Field_ftsf150ae_slot1_Slot_ae_slot1_get (insn) == 23 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULASP24S_HL_LH; + if (Field_ftsf151ae_slot1_Slot_ae_slot1_get (insn) == 24 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAS56P24S_LL; + if (Field_ftsf152ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_HH; + if (Field_ftsf153ae_slot1_Slot_ae_slot1_get (insn) == 26 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_HL; + if (Field_ftsf154ae_slot1_Slot_ae_slot1_get (insn) == 27 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_LL; + if (Field_ftsf155ae_slot1_Slot_ae_slot1_get (insn) == 28 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFP24S_LH; + if (Field_ftsf156ae_slot1_Slot_ae_slot1_get (insn) == 29 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_HH; + if (Field_ftsf157ae_slot1_Slot_ae_slot1_get (insn) == 30 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_HL; + if (Field_ftsf158ae_slot1_Slot_ae_slot1_get (insn) == 31 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_LH; + if (Field_ftsf159ae_slot1_Slot_ae_slot1_get (insn) == 32 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_LH; + if (Field_ftsf160ae_slot1_Slot_ae_slot1_get (insn) == 33 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULFS32P16S_LL; + if (Field_ftsf161ae_slot1_Slot_ae_slot1_get (insn) == 34 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_HH; + if (Field_ftsf162ae_slot1_Slot_ae_slot1_get (insn) == 35 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAFP24S_HH_LL; + if (Field_ftsf163ae_slot1_Slot_ae_slot1_get (insn) == 36 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_HL; + if (Field_ftsf164ae_slot1_Slot_ae_slot1_get (insn) == 37 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAFP24S_HL_LH; + if (Field_ftsf165ae_slot1_Slot_ae_slot1_get (insn) == 38 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAP24S_HH_LL; + if (Field_ftsf166ae_slot1_Slot_ae_slot1_get (insn) == 39 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSAP24S_HL_LH; + if (Field_ftsf167ae_slot1_Slot_ae_slot1_get (insn) == 40 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_LH; + if (Field_ftsf168ae_slot1_Slot_ae_slot1_get (insn) == 41 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_HH; + if (Field_ftsf169ae_slot1_Slot_ae_slot1_get (insn) == 42 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_HL; + if (Field_ftsf170ae_slot1_Slot_ae_slot1_get (insn) == 43 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_LL; + if (Field_ftsf171ae_slot1_Slot_ae_slot1_get (insn) == 44 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFP24S_LH; + if (Field_ftsf172ae_slot1_Slot_ae_slot1_get (insn) == 45 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_HH; + if (Field_ftsf173ae_slot1_Slot_ae_slot1_get (insn) == 46 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_HL; + if (Field_ftsf174ae_slot1_Slot_ae_slot1_get (insn) == 47 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_LH; + if (Field_ftsf175ae_slot1_Slot_ae_slot1_get (insn) == 48 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULP24S_LL; + if (Field_ftsf176ae_slot1_Slot_ae_slot1_get (insn) == 49 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS32P16S_LL; + if (Field_ftsf177ae_slot1_Slot_ae_slot1_get (insn) == 50 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_HH; + if (Field_ftsf178ae_slot1_Slot_ae_slot1_get (insn) == 51 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_LL; + if (Field_ftsf179ae_slot1_Slot_ae_slot1_get (insn) == 52 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_HL; + if (Field_ftsf180ae_slot1_Slot_ae_slot1_get (insn) == 53 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_HH; + if (Field_ftsf181ae_slot1_Slot_ae_slot1_get (insn) == 54 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_HL; + if (Field_ftsf182ae_slot1_Slot_ae_slot1_get (insn) == 55 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_LH; + if (Field_ftsf183ae_slot1_Slot_ae_slot1_get (insn) == 56 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSFS56P24S_LH; + if (Field_ftsf184ae_slot1_Slot_ae_slot1_get (insn) == 57 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSP24S_LL; + if (Field_ftsf185ae_slot1_Slot_ae_slot1_get (insn) == 58 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_HH; + if (Field_ftsf186ae_slot1_Slot_ae_slot1_get (insn) == 59 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_LH; + if (Field_ftsf187ae_slot1_Slot_ae_slot1_get (insn) == 60 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_HL; + if (Field_ftsf188ae_slot1_Slot_ae_slot1_get (insn) == 61 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSS56P24S_LL; + if (Field_ftsf189ae_slot1_Slot_ae_slot1_get (insn) == 62 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSFP24S_HH_LL; + if (Field_ftsf190ae_slot1_Slot_ae_slot1_get (insn) == 63 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSFP24S_HL_LH; + if (Field_ftsf191ae_slot1_Slot_ae_slot1_get (insn) == 64 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULAFP24S_LL; + if (Field_ftsf192ae_slot1_Slot_ae_slot1_get (insn) == 65 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSP24S_HH_LL; + if (Field_ftsf193ae_slot1_Slot_ae_slot1_get (insn) == 66 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULSSP24S_HL_LH; + if (Field_ftsf194ae_slot1_Slot_ae_slot1_get (insn) == 67 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASFP24S_HH_LL; + if (Field_ftsf195ae_slot1_Slot_ae_slot1_get (insn) == 68 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZAAFP24S_HH_LL; + if (Field_ftsf196ae_slot1_Slot_ae_slot1_get (insn) == 69 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASFP24S_HL_LH; + if (Field_ftsf197ae_slot1_Slot_ae_slot1_get (insn) == 70 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASP24S_HH_LL; + if (Field_ftsf198ae_slot1_Slot_ae_slot1_get (insn) == 71 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZASP24S_HL_LH; + if (Field_ftsf199ae_slot1_Slot_ae_slot1_get (insn) == 72 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZAAFP24S_HL_LH; + if (Field_ftsf200ae_slot1_Slot_ae_slot1_get (insn) == 73 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAFP24S_HH_LL; + if (Field_ftsf201ae_slot1_Slot_ae_slot1_get (insn) == 74 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAFP24S_HL_LH; + if (Field_ftsf202ae_slot1_Slot_ae_slot1_get (insn) == 75 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAP24S_HL_LH; + if (Field_ftsf203ae_slot1_Slot_ae_slot1_get (insn) == 76 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSAP24S_HH_LL; + if (Field_ftsf204ae_slot1_Slot_ae_slot1_get (insn) == 77 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSSFP24S_HH_LL; + if (Field_ftsf205ae_slot1_Slot_ae_slot1_get (insn) == 78 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSSFP24S_HL_LH; + if (Field_ftsf206ae_slot1_Slot_ae_slot1_get (insn) == 79 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6) + return OPCODE_AE_MULZSSP24S_HH_LL; + if (Field_ftsf207ae_slot1_Slot_ae_slot1_get (insn) == 10 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf336ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULZAAP24S_HH_LL; + if (Field_ftsf209ae_slot1_Slot_ae_slot1_get (insn) == 11 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf336ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULZSSP24S_HL_LH; + if (Field_ftsf210ae_slot1_Slot_ae_slot1_get (insn) == 3 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf337ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULZAAP24S_HL_LH; + if (Field_ftsf211ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 6 && + Field_ftsf332ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULAFS32P16S_HH; + if (Field_ftsf21ae_slot1_Slot_ae_slot1_get (insn) == 0 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MAXBP24S; + if (Field_ftsf22ae_slot1_Slot_ae_slot1_get (insn) == 1 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MINBP24S; + if (Field_ftsf23ae_slot1_Slot_ae_slot1_get (insn) == 8 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVFP48; + if (Field_ftsf24ae_slot1_Slot_ae_slot1_get (insn) == 9 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVTP48; + if (Field_ftsf25ae_slot1_Slot_ae_slot1_get (insn) == 20 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ADDP24; + if (Field_ftsf26ae_slot1_Slot_ae_slot1_get (insn) == 21 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ANDP48; + if (Field_ftsf27ae_slot1_Slot_ae_slot1_get (insn) == 22 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MAXP24S; + if (Field_ftsf28ae_slot1_Slot_ae_slot1_get (insn) == 23 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MINP24S; + if (Field_ftsf29ae_slot1_Slot_ae_slot1_get (insn) == 24 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ADDSP24S; + if (Field_ftsf30ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_NANDP48; + if (Field_ftsf31ae_slot1_Slot_ae_slot1_get (insn) == 26 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ORP48; + if (Field_ftsf32ae_slot1_Slot_ae_slot1_get (insn) == 27 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_HL; + if (Field_ftsf33ae_slot1_Slot_ae_slot1_get (insn) == 28 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_HH; + if (Field_ftsf34ae_slot1_Slot_ae_slot1_get (insn) == 29 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_LH; + if (Field_ftsf35ae_slot1_Slot_ae_slot1_get (insn) == 30 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SELP24_LL; + if (Field_ftsf36ae_slot1_Slot_ae_slot1_get (insn) == 31 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SUBP24; + switch (Field_ftsf37ae_slot1_Slot_ae_slot1_get (insn)) + { + case 8: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SLLIP24; + break; + case 9: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRAIP24; + break; + case 10: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRLIP24; + break; + } + if (Field_ftsf38ae_slot1_Slot_ae_slot1_get (insn) == 176 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16S_L; + if (Field_ftsf39ae_slot1_Slot_ae_slot1_get (insn) == 177 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16U_H; + if (Field_ftsf40ae_slot1_Slot_ae_slot1_get (insn) == 178 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16U_L; + if (Field_ftsf41ae_slot1_Slot_ae_slot1_get (insn) == 179 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16U_H; + if (Field_ftsf42ae_slot1_Slot_ae_slot1_get (insn) == 180 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16S_H; + if (Field_ftsf43ae_slot1_Slot_ae_slot1_get (insn) == 181 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16U_L; + if (Field_ftsf44ae_slot1_Slot_ae_slot1_get (insn) == 182 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16S_H; + if (Field_ftsf45ae_slot1_Slot_ae_slot1_get (insn) == 183 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16S_L; + if (Field_ftsf46ae_slot1_Slot_ae_slot1_get (insn) == 184 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAQ32SP16S_L; + if (Field_ftsf47ae_slot1_Slot_ae_slot1_get (insn) == 185 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16U_H; + if (Field_ftsf48ae_slot1_Slot_ae_slot1_get (insn) == 186 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULFQ32SP16U_L; + if (Field_ftsf49ae_slot1_Slot_ae_slot1_get (insn) == 187 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16S_L; + if (Field_ftsf50ae_slot1_Slot_ae_slot1_get (insn) == 188 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16S_H; + if (Field_ftsf51ae_slot1_Slot_ae_slot1_get (insn) == 189 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16U_H; + if (Field_ftsf52ae_slot1_Slot_ae_slot1_get (insn) == 190 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULQ32SP16U_L; + if (Field_ftsf53ae_slot1_Slot_ae_slot1_get (insn) == 191 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16S_H; + if (Field_ftsf54ae_slot1_Slot_ae_slot1_get (insn) == 192 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULAFQ32SP16S_H; + if (Field_ftsf55ae_slot1_Slot_ae_slot1_get (insn) == 193 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16S_L; + if (Field_ftsf56ae_slot1_Slot_ae_slot1_get (insn) == 194 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16U_H; + if (Field_ftsf57ae_slot1_Slot_ae_slot1_get (insn) == 195 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSQ32SP16U_L; + if (Field_ftsf58ae_slot1_Slot_ae_slot1_get (insn) == 196 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MULSFQ32SP16U_L; + if (Field_ftsf59ae_slot1_Slot_ae_slot1_get (insn) == 773 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_CVTQ48P24S_H; + if (Field_ftsf60ae_slot1_Slot_ae_slot1_get (insn) == 789 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ZEROQ56; + if (Field_ftsf61ae_slot1_Slot_ae_slot1_get (insn) == 405 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf330ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_NOP; + if (Field_ftsf63ae_slot1_Slot_ae_slot1_get (insn) == 198 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r10_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_CVTQ48P24S_L; + if (Field_ftsf64ae_slot1_Slot_ae_slot1_get (insn) == 1543 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVQ56; + if (Field_ftsf66ae_slot1_Slot_ae_slot1_get (insn) == 1559 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSQ32ASYM; + if (Field_ftsf67ae_slot1_Slot_ae_slot1_get (insn) == 791 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf342ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSQ32SYM; + if (Field_ftsf69ae_slot1_Slot_ae_slot1_get (insn) == 407 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf340_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_TRUNCQ32; + if (Field_ftsf71ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULSQ32SP16S_H; + if (Field_ftsf72ae_slot1_Slot_ae_slot1_get (insn) == 26 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULSQ32SP16S_L; + if (Field_ftsf73ae_slot1_Slot_ae_slot1_get (insn) == 417 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_MOVP48; + if (Field_ftsf75ae_slot1_Slot_ae_slot1_get (insn) == 419 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSP16ASYM; + if (Field_ftsf76ae_slot1_Slot_ae_slot1_get (insn) == 421 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSP16SYM; + if (Field_ftsf77ae_slot1_Slot_ae_slot1_get (insn) == 423 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRASP24; + if (Field_ftsf78ae_slot1_Slot_ae_slot1_get (insn) == 425 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SLLSP24; + if (Field_ftsf79ae_slot1_Slot_ae_slot1_get (insn) == 427 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SRLSP24; + if (Field_ftsf80ae_slot1_Slot_ae_slot1_get (insn) == 429 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_TRUNCP16; + if (Field_ftsf81ae_slot1_Slot_ae_slot1_get (insn) == 431 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ZEROP48; + if (Field_ftsf82ae_slot1_Slot_ae_slot1_get (insn) == 109 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r10_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_SLLSSP24S; + if (Field_ftsf84ae_slot1_Slot_ae_slot1_get (insn) == 881 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSP16Q48ASYM; + if (Field_ftsf86ae_slot1_Slot_ae_slot1_get (insn) == 883 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_ROUNDSP16Q48SYM; + if (Field_ftsf87ae_slot1_Slot_ae_slot1_get (insn) == 443 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf342ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSP24Q48ASYM; + if (Field_ftsf88ae_slot1_Slot_ae_slot1_get (insn) == 223 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf340_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ROUNDSP24Q48SYM; + if (Field_ftsf89ae_slot1_Slot_ae_slot1_get (insn) == 7 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf334ae_slot1_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MULSQ32SP16U_H; + if (Field_ftsf90ae_slot1_Slot_ae_slot1_get (insn) == 96 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_EQP24; + if (Field_ftsf91ae_slot1_Slot_ae_slot1_get (insn) == 97 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_LEP24S; + if (Field_ftsf92ae_slot1_Slot_ae_slot1_get (insn) == 49 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf208_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_LTP24S; + if (Field_ftsf94ae_slot1_Slot_ae_slot1_get (insn) == 25 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ftsf347_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MOVFP24X2; + if (Field_ftsf96ae_slot1_Slot_ae_slot1_get (insn) == 13 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_s20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_MOVTP24X2; + if (Field_ftsf97ae_slot1_Slot_ae_slot1_get (insn) == 112 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_SUBSP24S; + if (Field_ftsf98ae_slot1_Slot_ae_slot1_get (insn) == 113 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1) + return OPCODE_AE_XORP48; + if (Field_ftsf99ae_slot1_Slot_ae_slot1_get (insn) == 114 && + Field_op0_s3_Slot_ae_slot1_get (insn) == 1 && + Field_ae_r20_Slot_ae_slot1_get (insn) == 0) + return OPCODE_AE_ABSP24; + switch (Field_t_Slot_ae_slot1_get (insn)) + { + case 0: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16S_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASFQ32SP16U_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16S_LL; + break; + case 1: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16S_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASFQ32SP16U_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16U_HH; + break; + case 2: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16S_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16S_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16U_LH; + break; + case 3: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16U_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16U_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16S_LH; + break; + case 4: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16U_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16S_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSAQ32SP16U_LL; + break; + case 5: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16S_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16U_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16S_LL; + break; + case 6: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16S_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16U_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16U_HH; + break; + case 7: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16S_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16S_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16U_LH; + break; + case 8: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAFQ32SP16U_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZASQ32SP16S_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16S_HH; + break; + case 9: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16U_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16S_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSFQ32SP16U_LL; + break; + case 10: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16U_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16S_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16S_HH; + break; + case 11: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16S_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16U_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16S_LL; + break; + case 12: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZAAQ32SP16U_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16U_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16S_LH; + break; + case 13: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16S_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAFQ32SP16U_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16U_HH; + break; + case 14: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16S_LL; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAQ32SP16S_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16U_LH; + break; + case 15: + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 2) + return OPCODE_AE_MULZASFQ32SP16U_HH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 3) + return OPCODE_AE_MULZSAQ32SP16S_LH; + if (Field_op0_s3_Slot_ae_slot1_get (insn) == 4) + return OPCODE_AE_MULZSSQ32SP16U_LL; + break; + } + return XTENSA_UNDEFINED; +} + + +/* Instruction slots. */ + +static void +Slot_x24_Format_inst_0_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = (insn[0] & 0xffffff); +} + +static void +Slot_x24_Format_inst_0_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[0] = (insn[0] & ~0xffffff) | (slotbuf[0] & 0xffffff); +} + +static void +Slot_x16a_Format_inst16a_0_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = (insn[0] & 0xffff); +} + +static void +Slot_x16a_Format_inst16a_0_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[0] = (insn[0] & ~0xffff) | (slotbuf[0] & 0xffff); +} + +static void +Slot_x16b_Format_inst16b_0_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = (insn[0] & 0xffff); +} + +static void +Slot_x16b_Format_inst16b_0_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[0] = (insn[0] & ~0xffff) | (slotbuf[0] & 0xffff); +} + +static void +Slot_ae_format_Format_ae_slot1_31_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[0] & 0x80000000) >> 31); + slotbuf[0] = (slotbuf[0] & ~0x7ffffe) | ((insn[1] & 0x3fffff) << 1); +} + +static void +Slot_ae_format_Format_ae_slot1_31_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[0] = (insn[0] & ~0x80000000) | ((slotbuf[0] & 0x1) << 31); + insn[1] = (insn[1] & ~0x3fffff) | ((slotbuf[0] & 0x7ffffe) >> 1); +} + +static void +Slot_ae_format_Format_ae_slot0_4_get (const xtensa_insnbuf insn, + xtensa_insnbuf slotbuf) +{ + slotbuf[1] = 0; + slotbuf[0] = ((insn[0] & 0x7ffffff0) >> 4); +} + +static void +Slot_ae_format_Format_ae_slot0_4_set (xtensa_insnbuf insn, + const xtensa_insnbuf slotbuf) +{ + insn[0] = (insn[0] & ~0x7ffffff0) | ((slotbuf[0] & 0x7ffffff) << 4); +} + +static xtensa_get_field_fn +Slot_inst_get_field_fns[] = { + Field_t_Slot_inst_get, + Field_bbi4_Slot_inst_get, + Field_bbi_Slot_inst_get, + Field_imm12_Slot_inst_get, + Field_imm8_Slot_inst_get, + Field_s_Slot_inst_get, + Field_imm12b_Slot_inst_get, + Field_imm16_Slot_inst_get, + Field_m_Slot_inst_get, + Field_n_Slot_inst_get, + Field_offset_Slot_inst_get, + Field_op0_Slot_inst_get, + Field_op1_Slot_inst_get, + Field_op2_Slot_inst_get, + Field_r_Slot_inst_get, + Field_sa4_Slot_inst_get, + Field_sae4_Slot_inst_get, + Field_sae_Slot_inst_get, + Field_sal_Slot_inst_get, + Field_sargt_Slot_inst_get, + Field_sas4_Slot_inst_get, + Field_sas_Slot_inst_get, + Field_sr_Slot_inst_get, + Field_st_Slot_inst_get, + Field_thi3_Slot_inst_get, + Field_imm4_Slot_inst_get, + Field_mn_Slot_inst_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_inst_get, + Field_s2_Slot_inst_get, + Field_r2_Slot_inst_get, + Field_t4_Slot_inst_get, + Field_s4_Slot_inst_get, + Field_r4_Slot_inst_get, + Field_t8_Slot_inst_get, + Field_s8_Slot_inst_get, + Field_r8_Slot_inst_get, + Field_xt_wbr15_imm_Slot_inst_get, + Field_xt_wbr18_imm_Slot_inst_get, + Field_ae_r3_Slot_inst_get, + Field_ae_s_non_samt_Slot_inst_get, + Field_ae_s3_Slot_inst_get, + Field_ae_r32_Slot_inst_get, + Field_ae_samt_s_t_Slot_inst_get, + Field_ae_r20_Slot_inst_get, + Field_ae_r10_Slot_inst_get, + Field_ae_s20_Slot_inst_get, + 0, + Field_ftsf12_Slot_inst_get, + Field_ftsf13_Slot_inst_get, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_inst_set_field_fns[] = { + Field_t_Slot_inst_set, + Field_bbi4_Slot_inst_set, + Field_bbi_Slot_inst_set, + Field_imm12_Slot_inst_set, + Field_imm8_Slot_inst_set, + Field_s_Slot_inst_set, + Field_imm12b_Slot_inst_set, + Field_imm16_Slot_inst_set, + Field_m_Slot_inst_set, + Field_n_Slot_inst_set, + Field_offset_Slot_inst_set, + Field_op0_Slot_inst_set, + Field_op1_Slot_inst_set, + Field_op2_Slot_inst_set, + Field_r_Slot_inst_set, + Field_sa4_Slot_inst_set, + Field_sae4_Slot_inst_set, + Field_sae_Slot_inst_set, + Field_sal_Slot_inst_set, + Field_sargt_Slot_inst_set, + Field_sas4_Slot_inst_set, + Field_sas_Slot_inst_set, + Field_sr_Slot_inst_set, + Field_st_Slot_inst_set, + Field_thi3_Slot_inst_set, + Field_imm4_Slot_inst_set, + Field_mn_Slot_inst_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_t2_Slot_inst_set, + Field_s2_Slot_inst_set, + Field_r2_Slot_inst_set, + Field_t4_Slot_inst_set, + Field_s4_Slot_inst_set, + Field_r4_Slot_inst_set, + Field_t8_Slot_inst_set, + Field_s8_Slot_inst_set, + Field_r8_Slot_inst_set, + Field_xt_wbr15_imm_Slot_inst_set, + Field_xt_wbr18_imm_Slot_inst_set, + Field_ae_r3_Slot_inst_set, + Field_ae_s_non_samt_Slot_inst_set, + Field_ae_s3_Slot_inst_set, + Field_ae_r32_Slot_inst_set, + Field_ae_samt_s_t_Slot_inst_set, + Field_ae_r20_Slot_inst_set, + Field_ae_r10_Slot_inst_set, + Field_ae_s20_Slot_inst_set, + 0, + Field_ftsf12_Slot_inst_set, + Field_ftsf13_Slot_inst_set, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_inst16a_get_field_fns[] = { + Field_t_Slot_inst16a_get, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16a_get, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16a_get, + 0, + 0, + Field_r_Slot_inst16a_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16a_get, + Field_st_Slot_inst16a_get, + 0, + Field_imm4_Slot_inst16a_get, + 0, + Field_i_Slot_inst16a_get, + Field_imm6lo_Slot_inst16a_get, + Field_imm6hi_Slot_inst16a_get, + Field_imm7lo_Slot_inst16a_get, + Field_imm7hi_Slot_inst16a_get, + Field_z_Slot_inst16a_get, + Field_imm6_Slot_inst16a_get, + Field_imm7_Slot_inst16a_get, + Field_t2_Slot_inst16a_get, + Field_s2_Slot_inst16a_get, + Field_r2_Slot_inst16a_get, + Field_t4_Slot_inst16a_get, + Field_s4_Slot_inst16a_get, + Field_r4_Slot_inst16a_get, + Field_t8_Slot_inst16a_get, + Field_s8_Slot_inst16a_get, + Field_r8_Slot_inst16a_get, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_inst16a_set_field_fns[] = { + Field_t_Slot_inst16a_set, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16a_set, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16a_set, + 0, + 0, + Field_r_Slot_inst16a_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16a_set, + Field_st_Slot_inst16a_set, + 0, + Field_imm4_Slot_inst16a_set, + 0, + Field_i_Slot_inst16a_set, + Field_imm6lo_Slot_inst16a_set, + Field_imm6hi_Slot_inst16a_set, + Field_imm7lo_Slot_inst16a_set, + Field_imm7hi_Slot_inst16a_set, + Field_z_Slot_inst16a_set, + Field_imm6_Slot_inst16a_set, + Field_imm7_Slot_inst16a_set, + Field_t2_Slot_inst16a_set, + Field_s2_Slot_inst16a_set, + Field_r2_Slot_inst16a_set, + Field_t4_Slot_inst16a_set, + Field_s4_Slot_inst16a_set, + Field_r4_Slot_inst16a_set, + Field_t8_Slot_inst16a_set, + Field_s8_Slot_inst16a_set, + Field_r8_Slot_inst16a_set, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_inst16b_get_field_fns[] = { + Field_t_Slot_inst16b_get, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16b_get, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16b_get, + 0, + 0, + Field_r_Slot_inst16b_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16b_get, + Field_st_Slot_inst16b_get, + 0, + Field_imm4_Slot_inst16b_get, + 0, + Field_i_Slot_inst16b_get, + Field_imm6lo_Slot_inst16b_get, + Field_imm6hi_Slot_inst16b_get, + Field_imm7lo_Slot_inst16b_get, + Field_imm7hi_Slot_inst16b_get, + Field_z_Slot_inst16b_get, + Field_imm6_Slot_inst16b_get, + Field_imm7_Slot_inst16b_get, + Field_t2_Slot_inst16b_get, + Field_s2_Slot_inst16b_get, + Field_r2_Slot_inst16b_get, + Field_t4_Slot_inst16b_get, + Field_s4_Slot_inst16b_get, + Field_r4_Slot_inst16b_get, + Field_t8_Slot_inst16b_get, + Field_s8_Slot_inst16b_get, + Field_r8_Slot_inst16b_get, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_inst16b_set_field_fns[] = { + Field_t_Slot_inst16b_set, + 0, + 0, + 0, + 0, + Field_s_Slot_inst16b_set, + 0, + 0, + 0, + 0, + 0, + Field_op0_Slot_inst16b_set, + 0, + 0, + Field_r_Slot_inst16b_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_sr_Slot_inst16b_set, + Field_st_Slot_inst16b_set, + 0, + Field_imm4_Slot_inst16b_set, + 0, + Field_i_Slot_inst16b_set, + Field_imm6lo_Slot_inst16b_set, + Field_imm6hi_Slot_inst16b_set, + Field_imm7lo_Slot_inst16b_set, + Field_imm7hi_Slot_inst16b_set, + Field_z_Slot_inst16b_set, + Field_imm6_Slot_inst16b_set, + Field_imm7_Slot_inst16b_set, + Field_t2_Slot_inst16b_set, + Field_s2_Slot_inst16b_set, + Field_r2_Slot_inst16b_set, + Field_t4_Slot_inst16b_set, + Field_s4_Slot_inst16b_set, + Field_r4_Slot_inst16b_set, + Field_t8_Slot_inst16b_set, + Field_s8_Slot_inst16b_set, + Field_r8_Slot_inst16b_set, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_ae_slot1_get_field_fns[] = { + Field_t_Slot_ae_slot1_get, + 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, + 0, + 0, + Field_t2_Slot_ae_slot1_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot1_get, + 0, + Field_ae_r20_Slot_ae_slot1_get, + Field_ae_r10_Slot_ae_slot1_get, + Field_ae_s20_Slot_ae_slot1_get, + Field_op0_s3_Slot_ae_slot1_get, + Field_ftsf12_Slot_ae_slot1_get, + Field_ftsf13_Slot_ae_slot1_get, + Field_ftsf14_Slot_ae_slot1_get, + Field_ftsf21ae_slot1_Slot_ae_slot1_get, + Field_ftsf22ae_slot1_Slot_ae_slot1_get, + Field_ftsf23ae_slot1_Slot_ae_slot1_get, + Field_ftsf24ae_slot1_Slot_ae_slot1_get, + Field_ftsf25ae_slot1_Slot_ae_slot1_get, + Field_ftsf26ae_slot1_Slot_ae_slot1_get, + Field_ftsf27ae_slot1_Slot_ae_slot1_get, + Field_ftsf28ae_slot1_Slot_ae_slot1_get, + Field_ftsf29ae_slot1_Slot_ae_slot1_get, + Field_ftsf30ae_slot1_Slot_ae_slot1_get, + Field_ftsf31ae_slot1_Slot_ae_slot1_get, + Field_ftsf32ae_slot1_Slot_ae_slot1_get, + Field_ftsf33ae_slot1_Slot_ae_slot1_get, + Field_ftsf34ae_slot1_Slot_ae_slot1_get, + Field_ftsf35ae_slot1_Slot_ae_slot1_get, + Field_ftsf36ae_slot1_Slot_ae_slot1_get, + Field_ftsf37ae_slot1_Slot_ae_slot1_get, + Field_ftsf38ae_slot1_Slot_ae_slot1_get, + Field_ftsf39ae_slot1_Slot_ae_slot1_get, + Field_ftsf40ae_slot1_Slot_ae_slot1_get, + Field_ftsf41ae_slot1_Slot_ae_slot1_get, + Field_ftsf42ae_slot1_Slot_ae_slot1_get, + Field_ftsf43ae_slot1_Slot_ae_slot1_get, + Field_ftsf44ae_slot1_Slot_ae_slot1_get, + Field_ftsf45ae_slot1_Slot_ae_slot1_get, + Field_ftsf46ae_slot1_Slot_ae_slot1_get, + Field_ftsf47ae_slot1_Slot_ae_slot1_get, + Field_ftsf48ae_slot1_Slot_ae_slot1_get, + Field_ftsf49ae_slot1_Slot_ae_slot1_get, + Field_ftsf50ae_slot1_Slot_ae_slot1_get, + Field_ftsf51ae_slot1_Slot_ae_slot1_get, + Field_ftsf52ae_slot1_Slot_ae_slot1_get, + Field_ftsf53ae_slot1_Slot_ae_slot1_get, + Field_ftsf54ae_slot1_Slot_ae_slot1_get, + Field_ftsf55ae_slot1_Slot_ae_slot1_get, + Field_ftsf56ae_slot1_Slot_ae_slot1_get, + Field_ftsf57ae_slot1_Slot_ae_slot1_get, + Field_ftsf58ae_slot1_Slot_ae_slot1_get, + Field_ftsf59ae_slot1_Slot_ae_slot1_get, + Field_ftsf60ae_slot1_Slot_ae_slot1_get, + Field_ftsf61ae_slot1_Slot_ae_slot1_get, + Field_ftsf63ae_slot1_Slot_ae_slot1_get, + Field_ftsf64ae_slot1_Slot_ae_slot1_get, + Field_ftsf66ae_slot1_Slot_ae_slot1_get, + Field_ftsf67ae_slot1_Slot_ae_slot1_get, + Field_ftsf69ae_slot1_Slot_ae_slot1_get, + Field_ftsf71ae_slot1_Slot_ae_slot1_get, + Field_ftsf72ae_slot1_Slot_ae_slot1_get, + Field_ftsf73ae_slot1_Slot_ae_slot1_get, + Field_ftsf75ae_slot1_Slot_ae_slot1_get, + Field_ftsf76ae_slot1_Slot_ae_slot1_get, + Field_ftsf77ae_slot1_Slot_ae_slot1_get, + Field_ftsf78ae_slot1_Slot_ae_slot1_get, + Field_ftsf79ae_slot1_Slot_ae_slot1_get, + Field_ftsf80ae_slot1_Slot_ae_slot1_get, + Field_ftsf81ae_slot1_Slot_ae_slot1_get, + Field_ftsf82ae_slot1_Slot_ae_slot1_get, + Field_ftsf84ae_slot1_Slot_ae_slot1_get, + Field_ftsf86ae_slot1_Slot_ae_slot1_get, + Field_ftsf87ae_slot1_Slot_ae_slot1_get, + Field_ftsf88ae_slot1_Slot_ae_slot1_get, + Field_ftsf89ae_slot1_Slot_ae_slot1_get, + Field_ftsf90ae_slot1_Slot_ae_slot1_get, + Field_ftsf91ae_slot1_Slot_ae_slot1_get, + Field_ftsf92ae_slot1_Slot_ae_slot1_get, + Field_ftsf94ae_slot1_Slot_ae_slot1_get, + Field_ftsf96ae_slot1_Slot_ae_slot1_get, + Field_ftsf97ae_slot1_Slot_ae_slot1_get, + Field_ftsf98ae_slot1_Slot_ae_slot1_get, + Field_ftsf99ae_slot1_Slot_ae_slot1_get, + Field_ftsf100ae_slot1_Slot_ae_slot1_get, + Field_ftsf101ae_slot1_Slot_ae_slot1_get, + Field_ftsf103ae_slot1_Slot_ae_slot1_get, + Field_ftsf104ae_slot1_Slot_ae_slot1_get, + Field_ftsf105ae_slot1_Slot_ae_slot1_get, + Field_ftsf106ae_slot1_Slot_ae_slot1_get, + Field_ftsf107ae_slot1_Slot_ae_slot1_get, + Field_ftsf108ae_slot1_Slot_ae_slot1_get, + Field_ftsf109ae_slot1_Slot_ae_slot1_get, + Field_ftsf110ae_slot1_Slot_ae_slot1_get, + Field_ftsf111ae_slot1_Slot_ae_slot1_get, + Field_ftsf112ae_slot1_Slot_ae_slot1_get, + Field_ftsf113ae_slot1_Slot_ae_slot1_get, + Field_ftsf114ae_slot1_Slot_ae_slot1_get, + Field_ftsf115ae_slot1_Slot_ae_slot1_get, + Field_ftsf116ae_slot1_Slot_ae_slot1_get, + Field_ftsf118ae_slot1_Slot_ae_slot1_get, + Field_ftsf119ae_slot1_Slot_ae_slot1_get, + Field_ftsf120ae_slot1_Slot_ae_slot1_get, + Field_ftsf122ae_slot1_Slot_ae_slot1_get, + Field_ftsf124ae_slot1_Slot_ae_slot1_get, + Field_ftsf125ae_slot1_Slot_ae_slot1_get, + Field_ftsf126ae_slot1_Slot_ae_slot1_get, + Field_ftsf127ae_slot1_Slot_ae_slot1_get, + Field_ftsf128ae_slot1_Slot_ae_slot1_get, + Field_ftsf129ae_slot1_Slot_ae_slot1_get, + Field_ftsf130ae_slot1_Slot_ae_slot1_get, + Field_ftsf131ae_slot1_Slot_ae_slot1_get, + Field_ftsf132ae_slot1_Slot_ae_slot1_get, + Field_ftsf133ae_slot1_Slot_ae_slot1_get, + Field_ftsf134ae_slot1_Slot_ae_slot1_get, + Field_ftsf135ae_slot1_Slot_ae_slot1_get, + Field_ftsf136ae_slot1_Slot_ae_slot1_get, + Field_ftsf137ae_slot1_Slot_ae_slot1_get, + Field_ftsf138ae_slot1_Slot_ae_slot1_get, + Field_ftsf139ae_slot1_Slot_ae_slot1_get, + Field_ftsf140ae_slot1_Slot_ae_slot1_get, + Field_ftsf141ae_slot1_Slot_ae_slot1_get, + Field_ftsf142ae_slot1_Slot_ae_slot1_get, + Field_ftsf143ae_slot1_Slot_ae_slot1_get, + Field_ftsf144ae_slot1_Slot_ae_slot1_get, + Field_ftsf145ae_slot1_Slot_ae_slot1_get, + Field_ftsf146ae_slot1_Slot_ae_slot1_get, + Field_ftsf147ae_slot1_Slot_ae_slot1_get, + Field_ftsf148ae_slot1_Slot_ae_slot1_get, + Field_ftsf149ae_slot1_Slot_ae_slot1_get, + Field_ftsf150ae_slot1_Slot_ae_slot1_get, + Field_ftsf151ae_slot1_Slot_ae_slot1_get, + Field_ftsf152ae_slot1_Slot_ae_slot1_get, + Field_ftsf153ae_slot1_Slot_ae_slot1_get, + Field_ftsf154ae_slot1_Slot_ae_slot1_get, + Field_ftsf155ae_slot1_Slot_ae_slot1_get, + Field_ftsf156ae_slot1_Slot_ae_slot1_get, + Field_ftsf157ae_slot1_Slot_ae_slot1_get, + Field_ftsf158ae_slot1_Slot_ae_slot1_get, + Field_ftsf159ae_slot1_Slot_ae_slot1_get, + Field_ftsf160ae_slot1_Slot_ae_slot1_get, + Field_ftsf161ae_slot1_Slot_ae_slot1_get, + Field_ftsf162ae_slot1_Slot_ae_slot1_get, + Field_ftsf163ae_slot1_Slot_ae_slot1_get, + Field_ftsf164ae_slot1_Slot_ae_slot1_get, + Field_ftsf165ae_slot1_Slot_ae_slot1_get, + Field_ftsf166ae_slot1_Slot_ae_slot1_get, + Field_ftsf167ae_slot1_Slot_ae_slot1_get, + Field_ftsf168ae_slot1_Slot_ae_slot1_get, + Field_ftsf169ae_slot1_Slot_ae_slot1_get, + Field_ftsf170ae_slot1_Slot_ae_slot1_get, + Field_ftsf171ae_slot1_Slot_ae_slot1_get, + Field_ftsf172ae_slot1_Slot_ae_slot1_get, + Field_ftsf173ae_slot1_Slot_ae_slot1_get, + Field_ftsf174ae_slot1_Slot_ae_slot1_get, + Field_ftsf175ae_slot1_Slot_ae_slot1_get, + Field_ftsf176ae_slot1_Slot_ae_slot1_get, + Field_ftsf177ae_slot1_Slot_ae_slot1_get, + Field_ftsf178ae_slot1_Slot_ae_slot1_get, + Field_ftsf179ae_slot1_Slot_ae_slot1_get, + Field_ftsf180ae_slot1_Slot_ae_slot1_get, + Field_ftsf181ae_slot1_Slot_ae_slot1_get, + Field_ftsf182ae_slot1_Slot_ae_slot1_get, + Field_ftsf183ae_slot1_Slot_ae_slot1_get, + Field_ftsf184ae_slot1_Slot_ae_slot1_get, + Field_ftsf185ae_slot1_Slot_ae_slot1_get, + Field_ftsf186ae_slot1_Slot_ae_slot1_get, + Field_ftsf187ae_slot1_Slot_ae_slot1_get, + Field_ftsf188ae_slot1_Slot_ae_slot1_get, + Field_ftsf189ae_slot1_Slot_ae_slot1_get, + Field_ftsf190ae_slot1_Slot_ae_slot1_get, + Field_ftsf191ae_slot1_Slot_ae_slot1_get, + Field_ftsf192ae_slot1_Slot_ae_slot1_get, + Field_ftsf193ae_slot1_Slot_ae_slot1_get, + Field_ftsf194ae_slot1_Slot_ae_slot1_get, + Field_ftsf195ae_slot1_Slot_ae_slot1_get, + Field_ftsf196ae_slot1_Slot_ae_slot1_get, + Field_ftsf197ae_slot1_Slot_ae_slot1_get, + Field_ftsf198ae_slot1_Slot_ae_slot1_get, + Field_ftsf199ae_slot1_Slot_ae_slot1_get, + Field_ftsf200ae_slot1_Slot_ae_slot1_get, + Field_ftsf201ae_slot1_Slot_ae_slot1_get, + Field_ftsf202ae_slot1_Slot_ae_slot1_get, + Field_ftsf203ae_slot1_Slot_ae_slot1_get, + Field_ftsf204ae_slot1_Slot_ae_slot1_get, + Field_ftsf205ae_slot1_Slot_ae_slot1_get, + Field_ftsf206ae_slot1_Slot_ae_slot1_get, + Field_ftsf207ae_slot1_Slot_ae_slot1_get, + Field_ftsf208_Slot_ae_slot1_get, + Field_ftsf209ae_slot1_Slot_ae_slot1_get, + Field_ftsf210ae_slot1_Slot_ae_slot1_get, + Field_ftsf211ae_slot1_Slot_ae_slot1_get, + Field_ftsf330ae_slot1_Slot_ae_slot1_get, + Field_ftsf332ae_slot1_Slot_ae_slot1_get, + Field_ftsf334ae_slot1_Slot_ae_slot1_get, + Field_ftsf336ae_slot1_Slot_ae_slot1_get, + Field_ftsf337ae_slot1_Slot_ae_slot1_get, + Field_ftsf338_Slot_ae_slot1_get, + Field_ftsf339ae_slot1_Slot_ae_slot1_get, + Field_ftsf340_Slot_ae_slot1_get, + Field_ftsf341ae_slot1_Slot_ae_slot1_get, + Field_ftsf342ae_slot1_Slot_ae_slot1_get, + Field_ftsf343ae_slot1_Slot_ae_slot1_get, + Field_ftsf344ae_slot1_Slot_ae_slot1_get, + Field_ftsf346ae_slot1_Slot_ae_slot1_get, + Field_ftsf347_Slot_ae_slot1_get, + Field_ftsf348ae_slot1_Slot_ae_slot1_get, + Field_ftsf349ae_slot1_Slot_ae_slot1_get, + Field_ftsf350ae_slot1_Slot_ae_slot1_get, + 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, + 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, + 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, + 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, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_ae_slot1_set_field_fns[] = { + Field_t_Slot_ae_slot1_set, + 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, + 0, + 0, + Field_t2_Slot_ae_slot1_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot1_set, + 0, + Field_ae_r20_Slot_ae_slot1_set, + Field_ae_r10_Slot_ae_slot1_set, + Field_ae_s20_Slot_ae_slot1_set, + Field_op0_s3_Slot_ae_slot1_set, + Field_ftsf12_Slot_ae_slot1_set, + Field_ftsf13_Slot_ae_slot1_set, + Field_ftsf14_Slot_ae_slot1_set, + Field_ftsf21ae_slot1_Slot_ae_slot1_set, + Field_ftsf22ae_slot1_Slot_ae_slot1_set, + Field_ftsf23ae_slot1_Slot_ae_slot1_set, + Field_ftsf24ae_slot1_Slot_ae_slot1_set, + Field_ftsf25ae_slot1_Slot_ae_slot1_set, + Field_ftsf26ae_slot1_Slot_ae_slot1_set, + Field_ftsf27ae_slot1_Slot_ae_slot1_set, + Field_ftsf28ae_slot1_Slot_ae_slot1_set, + Field_ftsf29ae_slot1_Slot_ae_slot1_set, + Field_ftsf30ae_slot1_Slot_ae_slot1_set, + Field_ftsf31ae_slot1_Slot_ae_slot1_set, + Field_ftsf32ae_slot1_Slot_ae_slot1_set, + Field_ftsf33ae_slot1_Slot_ae_slot1_set, + Field_ftsf34ae_slot1_Slot_ae_slot1_set, + Field_ftsf35ae_slot1_Slot_ae_slot1_set, + Field_ftsf36ae_slot1_Slot_ae_slot1_set, + Field_ftsf37ae_slot1_Slot_ae_slot1_set, + Field_ftsf38ae_slot1_Slot_ae_slot1_set, + Field_ftsf39ae_slot1_Slot_ae_slot1_set, + Field_ftsf40ae_slot1_Slot_ae_slot1_set, + Field_ftsf41ae_slot1_Slot_ae_slot1_set, + Field_ftsf42ae_slot1_Slot_ae_slot1_set, + Field_ftsf43ae_slot1_Slot_ae_slot1_set, + Field_ftsf44ae_slot1_Slot_ae_slot1_set, + Field_ftsf45ae_slot1_Slot_ae_slot1_set, + Field_ftsf46ae_slot1_Slot_ae_slot1_set, + Field_ftsf47ae_slot1_Slot_ae_slot1_set, + Field_ftsf48ae_slot1_Slot_ae_slot1_set, + Field_ftsf49ae_slot1_Slot_ae_slot1_set, + Field_ftsf50ae_slot1_Slot_ae_slot1_set, + Field_ftsf51ae_slot1_Slot_ae_slot1_set, + Field_ftsf52ae_slot1_Slot_ae_slot1_set, + Field_ftsf53ae_slot1_Slot_ae_slot1_set, + Field_ftsf54ae_slot1_Slot_ae_slot1_set, + Field_ftsf55ae_slot1_Slot_ae_slot1_set, + Field_ftsf56ae_slot1_Slot_ae_slot1_set, + Field_ftsf57ae_slot1_Slot_ae_slot1_set, + Field_ftsf58ae_slot1_Slot_ae_slot1_set, + Field_ftsf59ae_slot1_Slot_ae_slot1_set, + Field_ftsf60ae_slot1_Slot_ae_slot1_set, + Field_ftsf61ae_slot1_Slot_ae_slot1_set, + Field_ftsf63ae_slot1_Slot_ae_slot1_set, + Field_ftsf64ae_slot1_Slot_ae_slot1_set, + Field_ftsf66ae_slot1_Slot_ae_slot1_set, + Field_ftsf67ae_slot1_Slot_ae_slot1_set, + Field_ftsf69ae_slot1_Slot_ae_slot1_set, + Field_ftsf71ae_slot1_Slot_ae_slot1_set, + Field_ftsf72ae_slot1_Slot_ae_slot1_set, + Field_ftsf73ae_slot1_Slot_ae_slot1_set, + Field_ftsf75ae_slot1_Slot_ae_slot1_set, + Field_ftsf76ae_slot1_Slot_ae_slot1_set, + Field_ftsf77ae_slot1_Slot_ae_slot1_set, + Field_ftsf78ae_slot1_Slot_ae_slot1_set, + Field_ftsf79ae_slot1_Slot_ae_slot1_set, + Field_ftsf80ae_slot1_Slot_ae_slot1_set, + Field_ftsf81ae_slot1_Slot_ae_slot1_set, + Field_ftsf82ae_slot1_Slot_ae_slot1_set, + Field_ftsf84ae_slot1_Slot_ae_slot1_set, + Field_ftsf86ae_slot1_Slot_ae_slot1_set, + Field_ftsf87ae_slot1_Slot_ae_slot1_set, + Field_ftsf88ae_slot1_Slot_ae_slot1_set, + Field_ftsf89ae_slot1_Slot_ae_slot1_set, + Field_ftsf90ae_slot1_Slot_ae_slot1_set, + Field_ftsf91ae_slot1_Slot_ae_slot1_set, + Field_ftsf92ae_slot1_Slot_ae_slot1_set, + Field_ftsf94ae_slot1_Slot_ae_slot1_set, + Field_ftsf96ae_slot1_Slot_ae_slot1_set, + Field_ftsf97ae_slot1_Slot_ae_slot1_set, + Field_ftsf98ae_slot1_Slot_ae_slot1_set, + Field_ftsf99ae_slot1_Slot_ae_slot1_set, + Field_ftsf100ae_slot1_Slot_ae_slot1_set, + Field_ftsf101ae_slot1_Slot_ae_slot1_set, + Field_ftsf103ae_slot1_Slot_ae_slot1_set, + Field_ftsf104ae_slot1_Slot_ae_slot1_set, + Field_ftsf105ae_slot1_Slot_ae_slot1_set, + Field_ftsf106ae_slot1_Slot_ae_slot1_set, + Field_ftsf107ae_slot1_Slot_ae_slot1_set, + Field_ftsf108ae_slot1_Slot_ae_slot1_set, + Field_ftsf109ae_slot1_Slot_ae_slot1_set, + Field_ftsf110ae_slot1_Slot_ae_slot1_set, + Field_ftsf111ae_slot1_Slot_ae_slot1_set, + Field_ftsf112ae_slot1_Slot_ae_slot1_set, + Field_ftsf113ae_slot1_Slot_ae_slot1_set, + Field_ftsf114ae_slot1_Slot_ae_slot1_set, + Field_ftsf115ae_slot1_Slot_ae_slot1_set, + Field_ftsf116ae_slot1_Slot_ae_slot1_set, + Field_ftsf118ae_slot1_Slot_ae_slot1_set, + Field_ftsf119ae_slot1_Slot_ae_slot1_set, + Field_ftsf120ae_slot1_Slot_ae_slot1_set, + Field_ftsf122ae_slot1_Slot_ae_slot1_set, + Field_ftsf124ae_slot1_Slot_ae_slot1_set, + Field_ftsf125ae_slot1_Slot_ae_slot1_set, + Field_ftsf126ae_slot1_Slot_ae_slot1_set, + Field_ftsf127ae_slot1_Slot_ae_slot1_set, + Field_ftsf128ae_slot1_Slot_ae_slot1_set, + Field_ftsf129ae_slot1_Slot_ae_slot1_set, + Field_ftsf130ae_slot1_Slot_ae_slot1_set, + Field_ftsf131ae_slot1_Slot_ae_slot1_set, + Field_ftsf132ae_slot1_Slot_ae_slot1_set, + Field_ftsf133ae_slot1_Slot_ae_slot1_set, + Field_ftsf134ae_slot1_Slot_ae_slot1_set, + Field_ftsf135ae_slot1_Slot_ae_slot1_set, + Field_ftsf136ae_slot1_Slot_ae_slot1_set, + Field_ftsf137ae_slot1_Slot_ae_slot1_set, + Field_ftsf138ae_slot1_Slot_ae_slot1_set, + Field_ftsf139ae_slot1_Slot_ae_slot1_set, + Field_ftsf140ae_slot1_Slot_ae_slot1_set, + Field_ftsf141ae_slot1_Slot_ae_slot1_set, + Field_ftsf142ae_slot1_Slot_ae_slot1_set, + Field_ftsf143ae_slot1_Slot_ae_slot1_set, + Field_ftsf144ae_slot1_Slot_ae_slot1_set, + Field_ftsf145ae_slot1_Slot_ae_slot1_set, + Field_ftsf146ae_slot1_Slot_ae_slot1_set, + Field_ftsf147ae_slot1_Slot_ae_slot1_set, + Field_ftsf148ae_slot1_Slot_ae_slot1_set, + Field_ftsf149ae_slot1_Slot_ae_slot1_set, + Field_ftsf150ae_slot1_Slot_ae_slot1_set, + Field_ftsf151ae_slot1_Slot_ae_slot1_set, + Field_ftsf152ae_slot1_Slot_ae_slot1_set, + Field_ftsf153ae_slot1_Slot_ae_slot1_set, + Field_ftsf154ae_slot1_Slot_ae_slot1_set, + Field_ftsf155ae_slot1_Slot_ae_slot1_set, + Field_ftsf156ae_slot1_Slot_ae_slot1_set, + Field_ftsf157ae_slot1_Slot_ae_slot1_set, + Field_ftsf158ae_slot1_Slot_ae_slot1_set, + Field_ftsf159ae_slot1_Slot_ae_slot1_set, + Field_ftsf160ae_slot1_Slot_ae_slot1_set, + Field_ftsf161ae_slot1_Slot_ae_slot1_set, + Field_ftsf162ae_slot1_Slot_ae_slot1_set, + Field_ftsf163ae_slot1_Slot_ae_slot1_set, + Field_ftsf164ae_slot1_Slot_ae_slot1_set, + Field_ftsf165ae_slot1_Slot_ae_slot1_set, + Field_ftsf166ae_slot1_Slot_ae_slot1_set, + Field_ftsf167ae_slot1_Slot_ae_slot1_set, + Field_ftsf168ae_slot1_Slot_ae_slot1_set, + Field_ftsf169ae_slot1_Slot_ae_slot1_set, + Field_ftsf170ae_slot1_Slot_ae_slot1_set, + Field_ftsf171ae_slot1_Slot_ae_slot1_set, + Field_ftsf172ae_slot1_Slot_ae_slot1_set, + Field_ftsf173ae_slot1_Slot_ae_slot1_set, + Field_ftsf174ae_slot1_Slot_ae_slot1_set, + Field_ftsf175ae_slot1_Slot_ae_slot1_set, + Field_ftsf176ae_slot1_Slot_ae_slot1_set, + Field_ftsf177ae_slot1_Slot_ae_slot1_set, + Field_ftsf178ae_slot1_Slot_ae_slot1_set, + Field_ftsf179ae_slot1_Slot_ae_slot1_set, + Field_ftsf180ae_slot1_Slot_ae_slot1_set, + Field_ftsf181ae_slot1_Slot_ae_slot1_set, + Field_ftsf182ae_slot1_Slot_ae_slot1_set, + Field_ftsf183ae_slot1_Slot_ae_slot1_set, + Field_ftsf184ae_slot1_Slot_ae_slot1_set, + Field_ftsf185ae_slot1_Slot_ae_slot1_set, + Field_ftsf186ae_slot1_Slot_ae_slot1_set, + Field_ftsf187ae_slot1_Slot_ae_slot1_set, + Field_ftsf188ae_slot1_Slot_ae_slot1_set, + Field_ftsf189ae_slot1_Slot_ae_slot1_set, + Field_ftsf190ae_slot1_Slot_ae_slot1_set, + Field_ftsf191ae_slot1_Slot_ae_slot1_set, + Field_ftsf192ae_slot1_Slot_ae_slot1_set, + Field_ftsf193ae_slot1_Slot_ae_slot1_set, + Field_ftsf194ae_slot1_Slot_ae_slot1_set, + Field_ftsf195ae_slot1_Slot_ae_slot1_set, + Field_ftsf196ae_slot1_Slot_ae_slot1_set, + Field_ftsf197ae_slot1_Slot_ae_slot1_set, + Field_ftsf198ae_slot1_Slot_ae_slot1_set, + Field_ftsf199ae_slot1_Slot_ae_slot1_set, + Field_ftsf200ae_slot1_Slot_ae_slot1_set, + Field_ftsf201ae_slot1_Slot_ae_slot1_set, + Field_ftsf202ae_slot1_Slot_ae_slot1_set, + Field_ftsf203ae_slot1_Slot_ae_slot1_set, + Field_ftsf204ae_slot1_Slot_ae_slot1_set, + Field_ftsf205ae_slot1_Slot_ae_slot1_set, + Field_ftsf206ae_slot1_Slot_ae_slot1_set, + Field_ftsf207ae_slot1_Slot_ae_slot1_set, + Field_ftsf208_Slot_ae_slot1_set, + Field_ftsf209ae_slot1_Slot_ae_slot1_set, + Field_ftsf210ae_slot1_Slot_ae_slot1_set, + Field_ftsf211ae_slot1_Slot_ae_slot1_set, + Field_ftsf330ae_slot1_Slot_ae_slot1_set, + Field_ftsf332ae_slot1_Slot_ae_slot1_set, + Field_ftsf334ae_slot1_Slot_ae_slot1_set, + Field_ftsf336ae_slot1_Slot_ae_slot1_set, + Field_ftsf337ae_slot1_Slot_ae_slot1_set, + Field_ftsf338_Slot_ae_slot1_set, + Field_ftsf339ae_slot1_Slot_ae_slot1_set, + Field_ftsf340_Slot_ae_slot1_set, + Field_ftsf341ae_slot1_Slot_ae_slot1_set, + Field_ftsf342ae_slot1_Slot_ae_slot1_set, + Field_ftsf343ae_slot1_Slot_ae_slot1_set, + Field_ftsf344ae_slot1_Slot_ae_slot1_set, + Field_ftsf346ae_slot1_Slot_ae_slot1_set, + Field_ftsf347_Slot_ae_slot1_set, + Field_ftsf348ae_slot1_Slot_ae_slot1_set, + Field_ftsf349ae_slot1_Slot_ae_slot1_set, + Field_ftsf350ae_slot1_Slot_ae_slot1_set, + 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, + 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, + 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, + 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, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_get_field_fn +Slot_ae_slot0_get_field_fns[] = { + Field_t_Slot_ae_slot0_get, + 0, + Field_bbi_Slot_ae_slot0_get, + Field_imm12_Slot_ae_slot0_get, + Field_imm8_Slot_ae_slot0_get, + Field_s_Slot_ae_slot0_get, + Field_imm12b_Slot_ae_slot0_get, + Field_imm16_Slot_ae_slot0_get, + 0, + 0, + Field_offset_Slot_ae_slot0_get, + 0, + 0, + Field_op2_Slot_ae_slot0_get, + Field_r_Slot_ae_slot0_get, + 0, + 0, + Field_sae_Slot_ae_slot0_get, + Field_sal_Slot_ae_slot0_get, + Field_sargt_Slot_ae_slot0_get, + 0, + Field_sas_Slot_ae_slot0_get, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_s4_Slot_ae_slot0_get, + 0, + 0, + Field_s8_Slot_ae_slot0_get, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot0_get, + Field_ae_samt_s_t_Slot_ae_slot0_get, + Field_ae_r20_Slot_ae_slot0_get, + Field_ae_r10_Slot_ae_slot0_get, + Field_ae_s20_Slot_ae_slot0_get, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_op0_s4_Slot_ae_slot0_get, + Field_ftsf212ae_slot0_Slot_ae_slot0_get, + Field_ftsf213ae_slot0_Slot_ae_slot0_get, + Field_ftsf214ae_slot0_Slot_ae_slot0_get, + Field_ftsf215ae_slot0_Slot_ae_slot0_get, + Field_ftsf216ae_slot0_Slot_ae_slot0_get, + Field_ftsf217_Slot_ae_slot0_get, + Field_ftsf218ae_slot0_Slot_ae_slot0_get, + Field_ftsf219ae_slot0_Slot_ae_slot0_get, + Field_ftsf220ae_slot0_Slot_ae_slot0_get, + Field_ftsf221ae_slot0_Slot_ae_slot0_get, + Field_ftsf222ae_slot0_Slot_ae_slot0_get, + Field_ftsf223ae_slot0_Slot_ae_slot0_get, + Field_ftsf224ae_slot0_Slot_ae_slot0_get, + Field_ftsf225ae_slot0_Slot_ae_slot0_get, + Field_ftsf226ae_slot0_Slot_ae_slot0_get, + Field_ftsf227ae_slot0_Slot_ae_slot0_get, + Field_ftsf228ae_slot0_Slot_ae_slot0_get, + Field_ftsf229ae_slot0_Slot_ae_slot0_get, + Field_ftsf230ae_slot0_Slot_ae_slot0_get, + Field_ftsf231ae_slot0_Slot_ae_slot0_get, + Field_ftsf232ae_slot0_Slot_ae_slot0_get, + Field_ftsf233ae_slot0_Slot_ae_slot0_get, + Field_ftsf234ae_slot0_Slot_ae_slot0_get, + Field_ftsf235ae_slot0_Slot_ae_slot0_get, + Field_ftsf236ae_slot0_Slot_ae_slot0_get, + Field_ftsf237ae_slot0_Slot_ae_slot0_get, + Field_ftsf238ae_slot0_Slot_ae_slot0_get, + Field_ftsf239ae_slot0_Slot_ae_slot0_get, + Field_ftsf240ae_slot0_Slot_ae_slot0_get, + Field_ftsf241ae_slot0_Slot_ae_slot0_get, + Field_ftsf242ae_slot0_Slot_ae_slot0_get, + Field_ftsf243ae_slot0_Slot_ae_slot0_get, + Field_ftsf244ae_slot0_Slot_ae_slot0_get, + Field_ftsf245ae_slot0_Slot_ae_slot0_get, + Field_ftsf246ae_slot0_Slot_ae_slot0_get, + Field_ftsf247ae_slot0_Slot_ae_slot0_get, + Field_ftsf248ae_slot0_Slot_ae_slot0_get, + Field_ftsf249ae_slot0_Slot_ae_slot0_get, + Field_ftsf250ae_slot0_Slot_ae_slot0_get, + Field_ftsf251ae_slot0_Slot_ae_slot0_get, + Field_ftsf252ae_slot0_Slot_ae_slot0_get, + Field_ftsf253ae_slot0_Slot_ae_slot0_get, + Field_ftsf254ae_slot0_Slot_ae_slot0_get, + Field_ftsf255ae_slot0_Slot_ae_slot0_get, + Field_ftsf256ae_slot0_Slot_ae_slot0_get, + Field_ftsf257ae_slot0_Slot_ae_slot0_get, + Field_ftsf258ae_slot0_Slot_ae_slot0_get, + Field_ftsf259ae_slot0_Slot_ae_slot0_get, + Field_ftsf260ae_slot0_Slot_ae_slot0_get, + Field_ftsf261ae_slot0_Slot_ae_slot0_get, + Field_ftsf262ae_slot0_Slot_ae_slot0_get, + Field_ftsf263ae_slot0_Slot_ae_slot0_get, + Field_ftsf264ae_slot0_Slot_ae_slot0_get, + Field_ftsf265ae_slot0_Slot_ae_slot0_get, + Field_ftsf266ae_slot0_Slot_ae_slot0_get, + Field_ftsf267ae_slot0_Slot_ae_slot0_get, + Field_ftsf268ae_slot0_Slot_ae_slot0_get, + Field_ftsf269ae_slot0_Slot_ae_slot0_get, + Field_ftsf270ae_slot0_Slot_ae_slot0_get, + Field_ftsf271ae_slot0_Slot_ae_slot0_get, + Field_ftsf272ae_slot0_Slot_ae_slot0_get, + Field_ftsf273ae_slot0_Slot_ae_slot0_get, + Field_ftsf274ae_slot0_Slot_ae_slot0_get, + Field_ftsf275ae_slot0_Slot_ae_slot0_get, + Field_ftsf276ae_slot0_Slot_ae_slot0_get, + Field_ftsf277ae_slot0_Slot_ae_slot0_get, + Field_ftsf278ae_slot0_Slot_ae_slot0_get, + Field_ftsf279ae_slot0_Slot_ae_slot0_get, + Field_ftsf281ae_slot0_Slot_ae_slot0_get, + Field_ftsf282ae_slot0_Slot_ae_slot0_get, + Field_ftsf283ae_slot0_Slot_ae_slot0_get, + Field_ftsf284ae_slot0_Slot_ae_slot0_get, + Field_ftsf286ae_slot0_Slot_ae_slot0_get, + Field_ftsf288ae_slot0_Slot_ae_slot0_get, + Field_ftsf290ae_slot0_Slot_ae_slot0_get, + Field_ftsf292ae_slot0_Slot_ae_slot0_get, + Field_ftsf293_Slot_ae_slot0_get, + Field_ftsf294ae_slot0_Slot_ae_slot0_get, + Field_ftsf295ae_slot0_Slot_ae_slot0_get, + Field_ftsf296ae_slot0_Slot_ae_slot0_get, + Field_ftsf297ae_slot0_Slot_ae_slot0_get, + Field_ftsf298ae_slot0_Slot_ae_slot0_get, + Field_ftsf299ae_slot0_Slot_ae_slot0_get, + Field_ftsf300ae_slot0_Slot_ae_slot0_get, + Field_ftsf301ae_slot0_Slot_ae_slot0_get, + Field_ftsf302ae_slot0_Slot_ae_slot0_get, + Field_ftsf303ae_slot0_Slot_ae_slot0_get, + Field_ftsf304ae_slot0_Slot_ae_slot0_get, + Field_ftsf306ae_slot0_Slot_ae_slot0_get, + Field_ftsf308ae_slot0_Slot_ae_slot0_get, + Field_ftsf309ae_slot0_Slot_ae_slot0_get, + Field_ftsf310ae_slot0_Slot_ae_slot0_get, + Field_ftsf311ae_slot0_Slot_ae_slot0_get, + Field_ftsf312ae_slot0_Slot_ae_slot0_get, + Field_ftsf313ae_slot0_Slot_ae_slot0_get, + Field_ftsf314ae_slot0_Slot_ae_slot0_get, + Field_ftsf315ae_slot0_Slot_ae_slot0_get, + Field_ftsf316ae_slot0_Slot_ae_slot0_get, + Field_ftsf317ae_slot0_Slot_ae_slot0_get, + Field_ftsf318ae_slot0_Slot_ae_slot0_get, + Field_ftsf319_Slot_ae_slot0_get, + Field_ftsf320ae_slot0_Slot_ae_slot0_get, + Field_ftsf321_Slot_ae_slot0_get, + Field_ftsf322ae_slot0_Slot_ae_slot0_get, + Field_ftsf323ae_slot0_Slot_ae_slot0_get, + Field_ftsf324ae_slot0_Slot_ae_slot0_get, + Field_ftsf325ae_slot0_Slot_ae_slot0_get, + Field_ftsf326ae_slot0_Slot_ae_slot0_get, + Field_ftsf328ae_slot0_Slot_ae_slot0_get, + Field_ftsf329ae_slot0_Slot_ae_slot0_get, + Field_ftsf352ae_slot0_Slot_ae_slot0_get, + Field_ftsf353_Slot_ae_slot0_get, + Field_ftsf354ae_slot0_Slot_ae_slot0_get, + Field_ftsf356ae_slot0_Slot_ae_slot0_get, + Field_ftsf357_Slot_ae_slot0_get, + Field_ftsf358ae_slot0_Slot_ae_slot0_get, + Field_ftsf359ae_slot0_Slot_ae_slot0_get, + Field_ftsf360ae_slot0_Slot_ae_slot0_get, + Field_ftsf361ae_slot0_Slot_ae_slot0_get, + Field_ftsf362ae_slot0_Slot_ae_slot0_get, + Field_ftsf364ae_slot0_Slot_ae_slot0_get, + Field_ftsf365ae_slot0_Slot_ae_slot0_get, + Field_ftsf366ae_slot0_Slot_ae_slot0_get, + Field_ftsf368ae_slot0_Slot_ae_slot0_get, + Field_ftsf369ae_slot0_Slot_ae_slot0_get, + Implicit_Field_ar0_get, + Implicit_Field_ar4_get, + Implicit_Field_ar8_get, + Implicit_Field_ar12_get, + Implicit_Field_bt16_get, + Implicit_Field_bs16_get, + Implicit_Field_br16_get, + Implicit_Field_brall_get +}; + +static xtensa_set_field_fn +Slot_ae_slot0_set_field_fns[] = { + Field_t_Slot_ae_slot0_set, + 0, + Field_bbi_Slot_ae_slot0_set, + Field_imm12_Slot_ae_slot0_set, + Field_imm8_Slot_ae_slot0_set, + Field_s_Slot_ae_slot0_set, + Field_imm12b_Slot_ae_slot0_set, + Field_imm16_Slot_ae_slot0_set, + 0, + 0, + Field_offset_Slot_ae_slot0_set, + 0, + 0, + Field_op2_Slot_ae_slot0_set, + Field_r_Slot_ae_slot0_set, + 0, + 0, + Field_sae_Slot_ae_slot0_set, + Field_sal_Slot_ae_slot0_set, + Field_sargt_Slot_ae_slot0_set, + 0, + Field_sas_Slot_ae_slot0_set, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_s4_Slot_ae_slot0_set, + 0, + 0, + Field_s8_Slot_ae_slot0_set, + 0, + 0, + 0, + 0, + 0, + 0, + Field_ae_r32_Slot_ae_slot0_set, + Field_ae_samt_s_t_Slot_ae_slot0_set, + Field_ae_r20_Slot_ae_slot0_set, + Field_ae_r10_Slot_ae_slot0_set, + Field_ae_s20_Slot_ae_slot0_set, + 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, + 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, + 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, + 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, + 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, + 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, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Field_op0_s4_Slot_ae_slot0_set, + Field_ftsf212ae_slot0_Slot_ae_slot0_set, + Field_ftsf213ae_slot0_Slot_ae_slot0_set, + Field_ftsf214ae_slot0_Slot_ae_slot0_set, + Field_ftsf215ae_slot0_Slot_ae_slot0_set, + Field_ftsf216ae_slot0_Slot_ae_slot0_set, + Field_ftsf217_Slot_ae_slot0_set, + Field_ftsf218ae_slot0_Slot_ae_slot0_set, + Field_ftsf219ae_slot0_Slot_ae_slot0_set, + Field_ftsf220ae_slot0_Slot_ae_slot0_set, + Field_ftsf221ae_slot0_Slot_ae_slot0_set, + Field_ftsf222ae_slot0_Slot_ae_slot0_set, + Field_ftsf223ae_slot0_Slot_ae_slot0_set, + Field_ftsf224ae_slot0_Slot_ae_slot0_set, + Field_ftsf225ae_slot0_Slot_ae_slot0_set, + Field_ftsf226ae_slot0_Slot_ae_slot0_set, + Field_ftsf227ae_slot0_Slot_ae_slot0_set, + Field_ftsf228ae_slot0_Slot_ae_slot0_set, + Field_ftsf229ae_slot0_Slot_ae_slot0_set, + Field_ftsf230ae_slot0_Slot_ae_slot0_set, + Field_ftsf231ae_slot0_Slot_ae_slot0_set, + Field_ftsf232ae_slot0_Slot_ae_slot0_set, + Field_ftsf233ae_slot0_Slot_ae_slot0_set, + Field_ftsf234ae_slot0_Slot_ae_slot0_set, + Field_ftsf235ae_slot0_Slot_ae_slot0_set, + Field_ftsf236ae_slot0_Slot_ae_slot0_set, + Field_ftsf237ae_slot0_Slot_ae_slot0_set, + Field_ftsf238ae_slot0_Slot_ae_slot0_set, + Field_ftsf239ae_slot0_Slot_ae_slot0_set, + Field_ftsf240ae_slot0_Slot_ae_slot0_set, + Field_ftsf241ae_slot0_Slot_ae_slot0_set, + Field_ftsf242ae_slot0_Slot_ae_slot0_set, + Field_ftsf243ae_slot0_Slot_ae_slot0_set, + Field_ftsf244ae_slot0_Slot_ae_slot0_set, + Field_ftsf245ae_slot0_Slot_ae_slot0_set, + Field_ftsf246ae_slot0_Slot_ae_slot0_set, + Field_ftsf247ae_slot0_Slot_ae_slot0_set, + Field_ftsf248ae_slot0_Slot_ae_slot0_set, + Field_ftsf249ae_slot0_Slot_ae_slot0_set, + Field_ftsf250ae_slot0_Slot_ae_slot0_set, + Field_ftsf251ae_slot0_Slot_ae_slot0_set, + Field_ftsf252ae_slot0_Slot_ae_slot0_set, + Field_ftsf253ae_slot0_Slot_ae_slot0_set, + Field_ftsf254ae_slot0_Slot_ae_slot0_set, + Field_ftsf255ae_slot0_Slot_ae_slot0_set, + Field_ftsf256ae_slot0_Slot_ae_slot0_set, + Field_ftsf257ae_slot0_Slot_ae_slot0_set, + Field_ftsf258ae_slot0_Slot_ae_slot0_set, + Field_ftsf259ae_slot0_Slot_ae_slot0_set, + Field_ftsf260ae_slot0_Slot_ae_slot0_set, + Field_ftsf261ae_slot0_Slot_ae_slot0_set, + Field_ftsf262ae_slot0_Slot_ae_slot0_set, + Field_ftsf263ae_slot0_Slot_ae_slot0_set, + Field_ftsf264ae_slot0_Slot_ae_slot0_set, + Field_ftsf265ae_slot0_Slot_ae_slot0_set, + Field_ftsf266ae_slot0_Slot_ae_slot0_set, + Field_ftsf267ae_slot0_Slot_ae_slot0_set, + Field_ftsf268ae_slot0_Slot_ae_slot0_set, + Field_ftsf269ae_slot0_Slot_ae_slot0_set, + Field_ftsf270ae_slot0_Slot_ae_slot0_set, + Field_ftsf271ae_slot0_Slot_ae_slot0_set, + Field_ftsf272ae_slot0_Slot_ae_slot0_set, + Field_ftsf273ae_slot0_Slot_ae_slot0_set, + Field_ftsf274ae_slot0_Slot_ae_slot0_set, + Field_ftsf275ae_slot0_Slot_ae_slot0_set, + Field_ftsf276ae_slot0_Slot_ae_slot0_set, + Field_ftsf277ae_slot0_Slot_ae_slot0_set, + Field_ftsf278ae_slot0_Slot_ae_slot0_set, + Field_ftsf279ae_slot0_Slot_ae_slot0_set, + Field_ftsf281ae_slot0_Slot_ae_slot0_set, + Field_ftsf282ae_slot0_Slot_ae_slot0_set, + Field_ftsf283ae_slot0_Slot_ae_slot0_set, + Field_ftsf284ae_slot0_Slot_ae_slot0_set, + Field_ftsf286ae_slot0_Slot_ae_slot0_set, + Field_ftsf288ae_slot0_Slot_ae_slot0_set, + Field_ftsf290ae_slot0_Slot_ae_slot0_set, + Field_ftsf292ae_slot0_Slot_ae_slot0_set, + Field_ftsf293_Slot_ae_slot0_set, + Field_ftsf294ae_slot0_Slot_ae_slot0_set, + Field_ftsf295ae_slot0_Slot_ae_slot0_set, + Field_ftsf296ae_slot0_Slot_ae_slot0_set, + Field_ftsf297ae_slot0_Slot_ae_slot0_set, + Field_ftsf298ae_slot0_Slot_ae_slot0_set, + Field_ftsf299ae_slot0_Slot_ae_slot0_set, + Field_ftsf300ae_slot0_Slot_ae_slot0_set, + Field_ftsf301ae_slot0_Slot_ae_slot0_set, + Field_ftsf302ae_slot0_Slot_ae_slot0_set, + Field_ftsf303ae_slot0_Slot_ae_slot0_set, + Field_ftsf304ae_slot0_Slot_ae_slot0_set, + Field_ftsf306ae_slot0_Slot_ae_slot0_set, + Field_ftsf308ae_slot0_Slot_ae_slot0_set, + Field_ftsf309ae_slot0_Slot_ae_slot0_set, + Field_ftsf310ae_slot0_Slot_ae_slot0_set, + Field_ftsf311ae_slot0_Slot_ae_slot0_set, + Field_ftsf312ae_slot0_Slot_ae_slot0_set, + Field_ftsf313ae_slot0_Slot_ae_slot0_set, + Field_ftsf314ae_slot0_Slot_ae_slot0_set, + Field_ftsf315ae_slot0_Slot_ae_slot0_set, + Field_ftsf316ae_slot0_Slot_ae_slot0_set, + Field_ftsf317ae_slot0_Slot_ae_slot0_set, + Field_ftsf318ae_slot0_Slot_ae_slot0_set, + Field_ftsf319_Slot_ae_slot0_set, + Field_ftsf320ae_slot0_Slot_ae_slot0_set, + Field_ftsf321_Slot_ae_slot0_set, + Field_ftsf322ae_slot0_Slot_ae_slot0_set, + Field_ftsf323ae_slot0_Slot_ae_slot0_set, + Field_ftsf324ae_slot0_Slot_ae_slot0_set, + Field_ftsf325ae_slot0_Slot_ae_slot0_set, + Field_ftsf326ae_slot0_Slot_ae_slot0_set, + Field_ftsf328ae_slot0_Slot_ae_slot0_set, + Field_ftsf329ae_slot0_Slot_ae_slot0_set, + Field_ftsf352ae_slot0_Slot_ae_slot0_set, + Field_ftsf353_Slot_ae_slot0_set, + Field_ftsf354ae_slot0_Slot_ae_slot0_set, + Field_ftsf356ae_slot0_Slot_ae_slot0_set, + Field_ftsf357_Slot_ae_slot0_set, + Field_ftsf358ae_slot0_Slot_ae_slot0_set, + Field_ftsf359ae_slot0_Slot_ae_slot0_set, + Field_ftsf360ae_slot0_Slot_ae_slot0_set, + Field_ftsf361ae_slot0_Slot_ae_slot0_set, + Field_ftsf362ae_slot0_Slot_ae_slot0_set, + Field_ftsf364ae_slot0_Slot_ae_slot0_set, + Field_ftsf365ae_slot0_Slot_ae_slot0_set, + Field_ftsf366ae_slot0_Slot_ae_slot0_set, + Field_ftsf368ae_slot0_Slot_ae_slot0_set, + Field_ftsf369ae_slot0_Slot_ae_slot0_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set, + Implicit_Field_set +}; + +static xtensa_slot_internal slots[] = { + { "Inst", "x24", 0, + Slot_x24_Format_inst_0_get, Slot_x24_Format_inst_0_set, + Slot_inst_get_field_fns, Slot_inst_set_field_fns, + Slot_inst_decode, "nop" }, + { "Inst16a", "x16a", 0, + Slot_x16a_Format_inst16a_0_get, Slot_x16a_Format_inst16a_0_set, + Slot_inst16a_get_field_fns, Slot_inst16a_set_field_fns, + Slot_inst16a_decode, "" }, + { "Inst16b", "x16b", 0, + Slot_x16b_Format_inst16b_0_get, Slot_x16b_Format_inst16b_0_set, + Slot_inst16b_get_field_fns, Slot_inst16b_set_field_fns, + Slot_inst16b_decode, "nop.n" }, + { "ae_slot1", "ae_format", 1, + Slot_ae_format_Format_ae_slot1_31_get, Slot_ae_format_Format_ae_slot1_31_set, + Slot_ae_slot1_get_field_fns, Slot_ae_slot1_set_field_fns, + Slot_ae_slot1_decode, "nop" }, + { "ae_slot0", "ae_format", 0, + Slot_ae_format_Format_ae_slot0_4_get, Slot_ae_format_Format_ae_slot0_4_set, + Slot_ae_slot0_get_field_fns, Slot_ae_slot0_set_field_fns, + Slot_ae_slot0_decode, "nop" } +}; + + +/* Instruction formats. */ + +static void +Format_x24_encode (xtensa_insnbuf insn) +{ + insn[0] = 0; + insn[1] = 0; +} + +static void +Format_x16a_encode (xtensa_insnbuf insn) +{ + insn[0] = 0x8; + insn[1] = 0; +} + +static void +Format_x16b_encode (xtensa_insnbuf insn) +{ + insn[0] = 0xc; + insn[1] = 0; +} + +static void +Format_ae_format_encode (xtensa_insnbuf insn) +{ + insn[0] = 0xf; + insn[1] = 0; +} + +static int Format_x24_slots[] = { 0 }; + +static int Format_x16a_slots[] = { 1 }; + +static int Format_x16b_slots[] = { 2 }; + +static int Format_ae_format_slots[] = { 4, 3 }; + +static xtensa_format_internal formats[] = { + { "x24", 3, Format_x24_encode, 1, Format_x24_slots }, + { "x16a", 2, Format_x16a_encode, 1, Format_x16a_slots }, + { "x16b", 2, Format_x16b_encode, 1, Format_x16b_slots }, + { "ae_format", 8, Format_ae_format_encode, 2, Format_ae_format_slots } +}; + + +static int +format_decoder (const xtensa_insnbuf insn) +{ + if ((insn[0] & 0x8) == 0 && (insn[1] & 0) == 0) + return 0; /* x24 */ + if ((insn[0] & 0xc) == 0x8 && (insn[1] & 0) == 0) + return 1; /* x16a */ + if ((insn[0] & 0xe) == 0xc && (insn[1] & 0) == 0) + return 2; /* x16b */ + if ((insn[0] & 0xf) == 0xf && (insn[1] & 0xffc00000) == 0) + return 3; /* ae_format */ + return -1; +} + +static int length_table[16] = { + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 2, + 2, + 2, + 2, + 2, + 2, + -1, + 8 +}; + +static int +length_decoder (const unsigned char *insn) +{ + int op0 = insn[0] & 0xf; + return length_table[op0]; +} + + +/* Top-level ISA structure. */ + +xtensa_isa_internal xtensa_modules = { + 0 /* little-endian */, + 8 /* insn_size */, 0, + 4, formats, format_decoder, length_decoder, + 5, slots, + 387 /* num_fields */, + 445, operands, + 588, iclasses, + 656, opcodes, 0, + 8, regfiles, + NUM_STATES, states, 0, + NUM_SYSREGS, sysregs, 0, + { MAX_SPECIAL_REG, MAX_USER_REG }, { 0, 0 }, + 2, interfaces, 0, + 4, funcUnits, 0 +}; diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 590813d4f7..a54dbe4260 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -186,7 +186,7 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) #else cc->do_unaligned_access = xtensa_cpu_do_unaligned_access; cc->get_phys_page_debug = xtensa_cpu_get_phys_page_debug; - cc->do_unassigned_access = xtensa_cpu_do_unassigned_access; + cc->do_transaction_failed = xtensa_cpu_do_transaction_failed; #endif cc->debug_excp_handler = xtensa_breakpoint_handler; cc->disas_set_info = xtensa_cpu_disas_set_info; diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 51b4551464..4d8152682f 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -131,6 +131,7 @@ enum { ACCLO = 16, ACCHI = 17, MR = 32, + PREFCTL = 40, WINDOW_BASE = 72, WINDOW_START = 73, PTEVADDR = 83, @@ -217,6 +218,7 @@ enum { #define MEMCTL_IL0EN 0x1 #define MAX_INSN_LENGTH 64 +#define MAX_INSN_SLOTS 32 #define MAX_OPCODE_ARGS 16 #define MAX_NAREG 64 #define MAX_NINTERRUPT 32 @@ -344,14 +346,65 @@ typedef struct XtensaMemory { } location[MAX_NMEMORY]; } XtensaMemory; +typedef struct opcode_arg { + uint32_t imm; + uint32_t raw_imm; + void *in; + void *out; +} OpcodeArg; + typedef struct DisasContext DisasContext; -typedef void (*XtensaOpcodeOp)(DisasContext *dc, const uint32_t arg[], +typedef void (*XtensaOpcodeOp)(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]); +typedef bool (*XtensaOpcodeBoolTest)(DisasContext *dc, + const OpcodeArg arg[], + const uint32_t par[]); +typedef uint32_t (*XtensaOpcodeUintTest)(DisasContext *dc, + const OpcodeArg arg[], + const uint32_t par[]); + +enum { + XTENSA_OP_ILL = 0x1, + XTENSA_OP_PRIVILEGED = 0x2, + XTENSA_OP_SYSCALL = 0x4, + XTENSA_OP_DEBUG_BREAK = 0x8, + + XTENSA_OP_OVERFLOW = 0x10, + XTENSA_OP_UNDERFLOW = 0x20, + XTENSA_OP_ALLOCA = 0x40, + XTENSA_OP_COPROCESSOR = 0x80, + + XTENSA_OP_DIVIDE_BY_ZERO = 0x100, + + /* Postprocessing flags */ + XTENSA_OP_CHECK_INTERRUPTS = 0x200, + XTENSA_OP_EXIT_TB_M1 = 0x400, + XTENSA_OP_EXIT_TB_0 = 0x800, + XTENSA_OP_SYNC_REGISTER_WINDOW = 0x1000, + + XTENSA_OP_POSTPROCESS = + XTENSA_OP_CHECK_INTERRUPTS | + XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_SYNC_REGISTER_WINDOW, + + XTENSA_OP_NAME_ARRAY = 0x8000, + + XTENSA_OP_CONTROL_FLOW = 0x10000, + XTENSA_OP_STORE = 0x20000, + XTENSA_OP_LOAD = 0x40000, + XTENSA_OP_LOAD_STORE = + XTENSA_OP_LOAD | XTENSA_OP_STORE, +}; typedef struct XtensaOpcodeOps { - const char *name; + const void *name; XtensaOpcodeOp translate; + XtensaOpcodeBoolTest test_ill; + XtensaOpcodeUintTest test_overflow; const uint32_t *par; + uint32_t op_flags; + uint32_t coprocessor; } XtensaOpcodeOps; typedef struct XtensaOpcodeTranslators { @@ -370,6 +423,7 @@ struct XtensaConfig { int excm_level; int ndepc; unsigned inst_fetch_width; + unsigned max_insn_size; uint32_t vecbase; uint32_t exception_vector[EXC_MAX]; unsigned ninterrupt; @@ -407,6 +461,8 @@ struct XtensaConfig { xtensa_isa isa; XtensaOpcodeOps **opcode_ops; const XtensaOpcodeTranslators **opcode_translators; + xtensa_regfile a_regfile; + void ***regfile; uint32_t clock_freq_khz; @@ -443,6 +499,7 @@ typedef struct CPUXtensaState { float64 f64; } fregs[16]; float_status fp_status; + uint32_t windowbase_next; #ifndef CONFIG_USER_ONLY xtensa_tlb_entry itlb[7][MAX_TLB_WAY_SIZE]; @@ -452,7 +509,9 @@ typedef struct CPUXtensaState { AddressSpace *address_space_er; MemoryRegion *system_er; int pending_irq_level; /* level of last raised IRQ */ - void **irq_inputs; + qemu_irq *irq_inputs; + qemu_irq ext_irq_inputs[MAX_NINTERRUPT]; + qemu_irq runstall_irq; XtensaCcompareTimer ccompare[MAX_NCCOMPARE]; uint64_t time_base; uint64_t ccount_time; @@ -497,12 +556,15 @@ int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int rw, int size, int mmu_idx); void xtensa_cpu_do_interrupt(CPUState *cpu); bool xtensa_cpu_exec_interrupt(CPUState *cpu, int interrupt_request); -void xtensa_cpu_do_unassigned_access(CPUState *cpu, hwaddr addr, - bool is_write, bool is_exec, int opaque, - unsigned size); +void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, + unsigned size, MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr); void xtensa_cpu_dump_state(CPUState *cpu, FILE *f, fprintf_function cpu_fprintf, int flags); hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); +void xtensa_count_regs(const XtensaConfig *config, + unsigned *n_regs, unsigned *n_core_regs); int xtensa_cpu_gdb_read_register(CPUState *cpu, uint8_t *buf, int reg); int xtensa_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, @@ -529,14 +591,14 @@ void xtensa_cpu_do_unaligned_access(CPUState *cpu, vaddr addr, XTENSA_CPU_TYPE_NAME(XTENSA_DEFAULT_CPU_NOMMU_MODEL) void xtensa_translate_init(void); +void **xtensa_get_regfile_by_name(const char *name); void xtensa_breakpoint_handler(CPUState *cs); -void xtensa_finalize_config(XtensaConfig *config); void xtensa_register_core(XtensaConfigList *node); void xtensa_sim_open_console(Chardev *chr); void check_interrupts(CPUXtensaState *s); void xtensa_irq_init(CPUXtensaState *env); -void *xtensa_get_extint(CPUXtensaState *env, unsigned extint); -void xtensa_timer_irq(CPUXtensaState *env, uint32_t id, uint32_t active); +qemu_irq *xtensa_get_extints(CPUXtensaState *env); +qemu_irq xtensa_get_runstall(CPUXtensaState *env); int cpu_xtensa_signal_handler(int host_signum, void *pinfo, void *puc); void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf); void xtensa_sync_window_from_phys(CPUXtensaState *env); @@ -552,8 +614,6 @@ static inline void xtensa_select_static_vectors(CPUXtensaState *env, env->static_vectors = n; } void xtensa_runstall(CPUXtensaState *env, bool runstall); -XtensaOpcodeOps *xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t, - const char *opcode); #define XTENSA_OPTION_BIT(opt) (((uint64_t)1) << (opt)) #define XTENSA_OPTION_ALL (~(uint64_t)0) @@ -658,6 +718,14 @@ static inline int cpu_mmu_index(CPUXtensaState *env, bool ifetch) #define XTENSA_TBFLAG_WINDOW_MASK 0x18000 #define XTENSA_TBFLAG_WINDOW_SHIFT 15 #define XTENSA_TBFLAG_YIELD 0x20000 +#define XTENSA_TBFLAG_CWOE 0x40000 +#define XTENSA_TBFLAG_CALLINC_MASK 0x180000 +#define XTENSA_TBFLAG_CALLINC_SHIFT 19 + +#define XTENSA_CSBASE_LEND_MASK 0x0000ffff +#define XTENSA_CSBASE_LEND_SHIFT 0 +#define XTENSA_CSBASE_LBEG_OFF_MASK 0x00ff0000 +#define XTENSA_CSBASE_LBEG_OFF_SHIFT 16 static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, target_ulong *cs_base, uint32_t *flags) @@ -670,6 +738,32 @@ static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, *flags |= xtensa_get_ring(env); if (env->sregs[PS] & PS_EXCM) { *flags |= XTENSA_TBFLAG_EXCM; + } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { + target_ulong lend_dist = + env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); + + /* + * 0 in the csbase_lend field means that there may not be a loopback + * for any instruction that starts inside this page. Any other value + * means that an instruction that ends at this offset from the page + * start may loop back and will need loopback code to be generated. + * + * lend_dist is 0 when LEND points to the start of the page, but + * no instruction that starts inside this page may end at offset 0, + * so it's still correct. + * + * When an instruction ends at a page boundary it may only start in + * the previous page. lend_dist will be encoded as TARGET_PAGE_SIZE + * for the TB that contains this instruction. + */ + if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { + target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; + + *cs_base = lend_dist; + if (lbeg_off < 256) { + *cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; + } + } } if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) && (env->sregs[LITBASE] & 1)) { @@ -695,7 +789,9 @@ static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, target_ulong *pc, (env->sregs[WINDOW_BASE] + 1); uint32_t w = ctz32(windowstart | 0x8); - *flags |= w << XTENSA_TBFLAG_WINDOW_SHIFT; + *flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; + *flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, + PS_CALLINC_LEN) << XTENSA_TBFLAG_CALLINC_SHIFT; } else { *flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; } diff --git a/target/xtensa/dbg_helper.c b/target/xtensa/dbg_helper.c new file mode 100644 index 0000000000..cd8fbd653a --- /dev/null +++ b/target/xtensa/dbg_helper.c @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "exec/address-spaces.h" + +static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) +{ + uint32_t paddr; + uint32_t page_size; + unsigned access; + int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, + &paddr, &page_size, &access); + if (ret == 0) { + tb_invalidate_phys_addr(&address_space_memory, paddr, + MEMTXATTRS_UNSPECIFIED); + } +} + +void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v) +{ + uint32_t change = v ^ env->sregs[IBREAKENABLE]; + unsigned i; + + for (i = 0; i < env->config->nibreak; ++i) { + if (change & (1 << i)) { + tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); + } + } + env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1); +} + +void HELPER(wsr_ibreaka)(CPUXtensaState *env, uint32_t i, uint32_t v) +{ + if (env->sregs[IBREAKENABLE] & (1 << i) && env->sregs[IBREAKA + i] != v) { + tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); + tb_invalidate_virtual_addr(env, v); + } + env->sregs[IBREAKA + i] = v; +} + +static void set_dbreak(CPUXtensaState *env, unsigned i, uint32_t dbreaka, + uint32_t dbreakc) +{ + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; + uint32_t mask = dbreakc | ~DBREAKC_MASK; + + if (env->cpu_watchpoint[i]) { + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[i]); + } + if (dbreakc & DBREAKC_SB) { + flags |= BP_MEM_WRITE; + } + if (dbreakc & DBREAKC_LB) { + flags |= BP_MEM_READ; + } + /* contiguous mask after inversion is one less than some power of 2 */ + if ((~mask + 1) & ~mask) { + 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_mask(LOG_GUEST_ERROR, + "Failed to set data breakpoint at 0x%08x/%d\n", + dbreaka & mask, ~mask + 1); + } +} + +void HELPER(wsr_dbreaka)(CPUXtensaState *env, uint32_t i, uint32_t v) +{ + uint32_t dbreakc = env->sregs[DBREAKC + i]; + + if ((dbreakc & DBREAKC_SB_LB) && + env->sregs[DBREAKA + i] != v) { + set_dbreak(env, i, v, dbreakc); + } + env->sregs[DBREAKA + i] = v; +} + +void HELPER(wsr_dbreakc)(CPUXtensaState *env, uint32_t i, uint32_t v) +{ + if ((env->sregs[DBREAKC + i] ^ v) & (DBREAKC_SB_LB | DBREAKC_MASK)) { + if (v & DBREAKC_SB_LB) { + set_dbreak(env, i, env->sregs[DBREAKA + i], v); + } else { + if (env->cpu_watchpoint[i]) { + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + + cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[i]); + env->cpu_watchpoint[i] = NULL; + } + } + } + env->sregs[DBREAKC + i] = v; +} diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c new file mode 100644 index 0000000000..4a1f7aef5d --- /dev/null +++ b/target/xtensa/exc_helper.c @@ -0,0 +1,271 @@ +/* + * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" + +void HELPER(exception)(CPUXtensaState *env, uint32_t excp) +{ + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + + cs->exception_index = excp; + if (excp == EXCP_YIELD) { + env->yield_needed = 0; + } + if (excp == EXCP_DEBUG) { + env->exception_taken = 0; + } + cpu_loop_exit(cs); +} + +void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause) +{ + uint32_t vector; + + env->pc = pc; + if (env->sregs[PS] & PS_EXCM) { + if (env->config->ndepc) { + env->sregs[DEPC] = pc; + } else { + env->sregs[EPC1] = pc; + } + vector = EXC_DOUBLE; + } else { + env->sregs[EPC1] = pc; + vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; + } + + env->sregs[EXCCAUSE] = cause; + env->sregs[PS] |= PS_EXCM; + + HELPER(exception)(env, vector); +} + +void HELPER(exception_cause_vaddr)(CPUXtensaState *env, + uint32_t pc, uint32_t cause, uint32_t vaddr) +{ + env->sregs[EXCVADDR] = vaddr; + HELPER(exception_cause)(env, pc, cause); +} + +void debug_exception_env(CPUXtensaState *env, uint32_t cause) +{ + if (xtensa_get_cintlevel(env) < env->config->debug_level) { + HELPER(debug_exception)(env, env->pc, cause); + } +} + +void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause) +{ + unsigned level = env->config->debug_level; + + env->pc = pc; + env->sregs[DEBUGCAUSE] = cause; + env->sregs[EPC1 + level - 1] = pc; + env->sregs[EPS2 + level - 2] = env->sregs[PS]; + env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | + (level << PS_INTLEVEL_SHIFT); + HELPER(exception)(env, EXC_DEBUG); +} + +#ifndef CONFIG_USER_ONLY + +void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) +{ + CPUState *cpu; + + env->pc = pc; + env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | + (intlevel << PS_INTLEVEL_SHIFT); + + qemu_mutex_lock_iothread(); + check_interrupts(env); + qemu_mutex_unlock_iothread(); + + if (env->pending_irq_level) { + cpu_loop_exit(CPU(xtensa_env_get_cpu(env))); + return; + } + + cpu = CPU(xtensa_env_get_cpu(env)); + cpu->halted = 1; + HELPER(exception)(env, EXCP_HLT); +} + +void HELPER(check_interrupts)(CPUXtensaState *env) +{ + qemu_mutex_lock_iothread(); + check_interrupts(env); + qemu_mutex_unlock_iothread(); +} + +void HELPER(intset)(CPUXtensaState *env, uint32_t v) +{ + atomic_or(&env->sregs[INTSET], + v & env->config->inttype_mask[INTTYPE_SOFTWARE]); +} + +void HELPER(intclear)(CPUXtensaState *env, uint32_t v) +{ + atomic_and(&env->sregs[INTSET], + ~(v & (env->config->inttype_mask[INTTYPE_SOFTWARE] | + env->config->inttype_mask[INTTYPE_EDGE]))); +} + +static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) +{ + if (xtensa_option_enabled(env->config, + XTENSA_OPTION_RELOCATABLE_VECTOR)) { + return vector - env->config->vecbase + env->sregs[VECBASE]; + } else { + return vector; + } +} + +/*! + * Handle penging IRQ. + * For the high priority interrupt jump to the corresponding interrupt vector. + * For the level-1 interrupt convert it to either user, kernel or double + * exception with the 'level-1 interrupt' exception cause. + */ +static void handle_interrupt(CPUXtensaState *env) +{ + int level = env->pending_irq_level; + + if (level > xtensa_get_cintlevel(env) && + level <= env->config->nlevel && + (env->config->level_mask[level] & + env->sregs[INTSET] & + env->sregs[INTENABLE])) { + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + + if (level > 1) { + env->sregs[EPC1 + level - 1] = env->pc; + env->sregs[EPS2 + level - 2] = env->sregs[PS]; + env->sregs[PS] = + (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; + env->pc = relocated_vector(env, + env->config->interrupt_vector[level]); + } else { + env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; + + if (env->sregs[PS] & PS_EXCM) { + if (env->config->ndepc) { + env->sregs[DEPC] = env->pc; + } else { + env->sregs[EPC1] = env->pc; + } + cs->exception_index = EXC_DOUBLE; + } else { + env->sregs[EPC1] = env->pc; + cs->exception_index = + (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; + } + env->sregs[PS] |= PS_EXCM; + } + env->exception_taken = 1; + } +} + +/* Called from cpu_handle_interrupt with BQL held */ +void xtensa_cpu_do_interrupt(CPUState *cs) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; + + if (cs->exception_index == EXC_IRQ) { + qemu_log_mask(CPU_LOG_INT, + "%s(EXC_IRQ) level = %d, cintlevel = %d, " + "pc = %08x, a0 = %08x, ps = %08x, " + "intset = %08x, intenable = %08x, " + "ccount = %08x\n", + __func__, env->pending_irq_level, + xtensa_get_cintlevel(env), + env->pc, env->regs[0], env->sregs[PS], + env->sregs[INTSET], env->sregs[INTENABLE], + env->sregs[CCOUNT]); + handle_interrupt(env); + } + + switch (cs->exception_index) { + case EXC_WINDOW_OVERFLOW4: + case EXC_WINDOW_UNDERFLOW4: + case EXC_WINDOW_OVERFLOW8: + case EXC_WINDOW_UNDERFLOW8: + case EXC_WINDOW_OVERFLOW12: + case EXC_WINDOW_UNDERFLOW12: + case EXC_KERNEL: + case EXC_USER: + case EXC_DOUBLE: + case EXC_DEBUG: + qemu_log_mask(CPU_LOG_INT, "%s(%d) " + "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", + __func__, cs->exception_index, + env->pc, env->regs[0], env->sregs[PS], + env->sregs[CCOUNT]); + if (env->config->exception_vector[cs->exception_index]) { + uint32_t vector; + + vector = env->config->exception_vector[cs->exception_index]; + env->pc = relocated_vector(env, vector); + env->exception_taken = 1; + } else { + qemu_log_mask(CPU_LOG_INT, + "%s(pc = %08x) bad exception_index: %d\n", + __func__, env->pc, cs->exception_index); + } + break; + + case EXC_IRQ: + break; + + default: + qemu_log("%s(pc = %08x) unknown exception_index: %d\n", + __func__, env->pc, cs->exception_index); + break; + } + check_interrupts(env); +} +#else +void xtensa_cpu_do_interrupt(CPUState *cs) +{ +} +#endif + +bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) +{ + if (interrupt_request & CPU_INTERRUPT_HARD) { + cs->exception_index = EXC_IRQ; + xtensa_cpu_do_interrupt(cs); + return true; + } + return false; +} diff --git a/target/xtensa/fpu_helper.c b/target/xtensa/fpu_helper.c new file mode 100644 index 0000000000..f8bbb6cdd8 --- /dev/null +++ b/target/xtensa/fpu_helper.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "fpu/softfloat.h" + +void HELPER(wur_fcr)(CPUXtensaState *env, uint32_t v) +{ + static const int rounding_mode[] = { + float_round_nearest_even, + float_round_to_zero, + float_round_up, + float_round_down, + }; + + env->uregs[FCR] = v & 0xfffff07f; + set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); +} + +float32 HELPER(abs_s)(float32 v) +{ + return float32_abs(v); +} + +float32 HELPER(neg_s)(float32 v) +{ + return float32_chs(v); +} + +float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) +{ + return float32_add(a, b, &env->fp_status); +} + +float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) +{ + return float32_sub(a, b, &env->fp_status); +} + +float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) +{ + return float32_mul(a, b, &env->fp_status); +} + +float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) +{ + return float32_muladd(b, c, a, 0, &env->fp_status); +} + +float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) +{ + return float32_muladd(b, c, a, float_muladd_negate_product, + &env->fp_status); +} + +uint32_t HELPER(ftoi)(float32 v, uint32_t rounding_mode, uint32_t scale) +{ + float_status fp_status = {0}; + + set_float_rounding_mode(rounding_mode, &fp_status); + return float32_to_int32(float32_scalbn(v, scale, &fp_status), &fp_status); +} + +uint32_t HELPER(ftoui)(float32 v, uint32_t rounding_mode, uint32_t scale) +{ + float_status fp_status = {0}; + float32 res; + + set_float_rounding_mode(rounding_mode, &fp_status); + + res = float32_scalbn(v, scale, &fp_status); + + if (float32_is_neg(v) && !float32_is_any_nan(v)) { + return float32_to_int32(res, &fp_status); + } else { + return float32_to_uint32(res, &fp_status); + } +} + +float32 HELPER(itof)(CPUXtensaState *env, uint32_t v, uint32_t scale) +{ + return float32_scalbn(int32_to_float32(v, &env->fp_status), + (int32_t)scale, &env->fp_status); +} + +float32 HELPER(uitof)(CPUXtensaState *env, uint32_t v, uint32_t scale) +{ + return float32_scalbn(uint32_to_float32(v, &env->fp_status), + (int32_t)scale, &env->fp_status); +} + +static inline void set_br(CPUXtensaState *env, bool v, uint32_t br) +{ + if (v) { + env->sregs[BR] |= br; + } else { + env->sregs[BR] &= ~br; + } +} + +void HELPER(un_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + set_br(env, float32_unordered_quiet(a, b, &env->fp_status), br); +} + +void HELPER(oeq_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + set_br(env, float32_eq_quiet(a, b, &env->fp_status), br); +} + +void HELPER(ueq_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + int v = float32_compare_quiet(a, b, &env->fp_status); + set_br(env, v == float_relation_equal || v == float_relation_unordered, br); +} + +void HELPER(olt_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + set_br(env, float32_lt_quiet(a, b, &env->fp_status), br); +} + +void HELPER(ult_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + int v = float32_compare_quiet(a, b, &env->fp_status); + set_br(env, v == float_relation_less || v == float_relation_unordered, br); +} + +void HELPER(ole_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + set_br(env, float32_le_quiet(a, b, &env->fp_status), br); +} + +void HELPER(ule_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) +{ + int v = float32_compare_quiet(a, b, &env->fp_status); + set_br(env, v != float_relation_greater, br); +} diff --git a/target/xtensa/gdbstub.c b/target/xtensa/gdbstub.c index a8ea98d03f..d43bb190c6 100644 --- a/target/xtensa/gdbstub.c +++ b/target/xtensa/gdbstub.c @@ -23,6 +23,47 @@ #include "exec/gdbstub.h" #include "qemu/log.h" +enum { + xtRegisterTypeArRegfile = 1, /* Register File ar0..arXX. */ + xtRegisterTypeSpecialReg, /* CPU states, such as PS, Booleans, (rsr). */ + xtRegisterTypeUserReg, /* User defined registers (rur). */ + xtRegisterTypeTieRegfile, /* User define register files. */ + xtRegisterTypeTieState, /* TIE States (mapped on user regs). */ + xtRegisterTypeMapped, /* Mapped on Special Registers. */ + xtRegisterTypeUnmapped, /* Special case of masked registers. */ + xtRegisterTypeWindow, /* Live window registers (a0..a15). */ + xtRegisterTypeVirtual, /* PC, FP. */ + xtRegisterTypeUnknown +}; + +#define XTENSA_REGISTER_FLAGS_PRIVILEGED 0x0001 +#define XTENSA_REGISTER_FLAGS_READABLE 0x0002 +#define XTENSA_REGISTER_FLAGS_WRITABLE 0x0004 +#define XTENSA_REGISTER_FLAGS_VOLATILE 0x0008 + +void xtensa_count_regs(const XtensaConfig *config, + unsigned *n_regs, unsigned *n_core_regs) +{ + unsigned i; + bool count_core_regs = true; + + for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) { + if (config->gdb_regmap.reg[i].type != xtRegisterTypeTieState && + config->gdb_regmap.reg[i].type != xtRegisterTypeMapped && + config->gdb_regmap.reg[i].type != xtRegisterTypeUnmapped) { + ++*n_regs; + if (count_core_regs) { + if ((config->gdb_regmap.reg[i].flags & + XTENSA_REGISTER_FLAGS_PRIVILEGED) == 0) { + ++*n_core_regs; + } else { + count_core_regs = false; + } + } + } + } +} + int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) { XtensaCPU *cpu = XTENSA_CPU(cs); @@ -40,21 +81,21 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) } switch (reg->type) { - case 9: /*pc*/ + case xtRegisterTypeVirtual: /*pc*/ return gdb_get_reg32(mem_buf, env->pc); - case 1: /*ar*/ + case xtRegisterTypeArRegfile: /*ar*/ xtensa_sync_phys_from_window(env); return gdb_get_reg32(mem_buf, env->phys_regs[(reg->targno & 0xff) % env->config->nareg]); - case 2: /*SR*/ + case xtRegisterTypeSpecialReg: /*SR*/ return gdb_get_reg32(mem_buf, env->sregs[reg->targno & 0xff]); - case 3: /*UR*/ + case xtRegisterTypeUserReg: /*UR*/ return gdb_get_reg32(mem_buf, env->uregs[reg->targno & 0xff]); - case 4: /*f*/ + case xtRegisterTypeTieRegfile: /*f*/ i = reg->targno & 0x0f; switch (reg->size) { case 4: @@ -69,7 +110,7 @@ int xtensa_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) return reg->size; } - case 8: /*a*/ + case xtRegisterTypeWindow: /*a*/ return gdb_get_reg32(mem_buf, env->regs[reg->targno & 0x0f]); default: @@ -99,24 +140,24 @@ int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) tmp = ldl_p(mem_buf); switch (reg->type) { - case 9: /*pc*/ + case xtRegisterTypeVirtual: /*pc*/ env->pc = tmp; break; - case 1: /*ar*/ + case xtRegisterTypeArRegfile: /*ar*/ env->phys_regs[(reg->targno & 0xff) % env->config->nareg] = tmp; xtensa_sync_window_from_phys(env); break; - case 2: /*SR*/ + case xtRegisterTypeSpecialReg: /*SR*/ env->sregs[reg->targno & 0xff] = tmp; break; - case 3: /*UR*/ + case xtRegisterTypeUserReg: /*UR*/ env->uregs[reg->targno & 0xff] = tmp; break; - case 4: /*f*/ + case xtRegisterTypeTieRegfile: /*f*/ switch (reg->size) { case 4: env->fregs[reg->targno & 0x0f].f32[FP_F32_LOW] = make_float32(tmp); @@ -130,7 +171,7 @@ int xtensa_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) return reg->size; } - case 8: /*a*/ + case xtRegisterTypeWindow: /*a*/ env->regs[reg->targno & 0x0f] = tmp; break; diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index c9a6132700..f4867a9b56 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -26,43 +26,84 @@ */ #include "qemu/osdep.h" -#include "qemu/units.h" #include "cpu.h" #include "exec/exec-all.h" #include "exec/gdbstub.h" +#include "exec/helper-proto.h" +#include "qemu/error-report.h" #include "qemu/host-utils.h" -#if !defined(CONFIG_USER_ONLY) -#include "hw/loader.h" -#endif static struct XtensaConfigList *xtensa_cores; -static void xtensa_core_class_init(ObjectClass *oc, void *data) +static void add_translator_to_hash(GHashTable *translator, + const char *name, + const XtensaOpcodeOps *opcode) { - CPUClass *cc = CPU_CLASS(oc); - XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc); - const XtensaConfig *config = data; + if (!g_hash_table_insert(translator, (void *)name, (void *)opcode)) { + error_report("Multiple definitions of '%s' opcode in a single table", + name); + } +} - xcc->config = config; +static GHashTable *hash_opcode_translators(const XtensaOpcodeTranslators *t) +{ + unsigned i, j; + GHashTable *translator = g_hash_table_new(g_str_hash, g_str_equal); - /* Use num_core_regs to see only non-privileged registers in an unmodified - * gdb. Use num_regs to see all registers. gdb modification is required - * for that: reset bit 0 in the 'flags' field of the registers definitions - * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay. - */ - cc->gdb_num_core_regs = config->gdb_regmap.num_regs; + for (i = 0; i < t->num_opcodes; ++i) { + if (t->opcode[i].op_flags & XTENSA_OP_NAME_ARRAY) { + const char * const *name = t->opcode[i].name; + + for (j = 0; name[j]; ++j) { + add_translator_to_hash(translator, + (void *)name[j], + (void *)(t->opcode + i)); + } + } else { + add_translator_to_hash(translator, + (void *)t->opcode[i].name, + (void *)(t->opcode + i)); + } + } + return translator; +} + +static XtensaOpcodeOps * +xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t, + const char *name) +{ + static GHashTable *translators; + GHashTable *translator; + + if (translators == NULL) { + translators = g_hash_table_new(g_direct_hash, g_direct_equal); + } + translator = g_hash_table_lookup(translators, t); + if (translator == NULL) { + translator = hash_opcode_translators(t); + g_hash_table_insert(translators, (void *)t, translator); + } + return g_hash_table_lookup(translator, name); } static void init_libisa(XtensaConfig *config) { unsigned i, j; unsigned opcodes; + unsigned formats; + unsigned regfiles; config->isa = xtensa_isa_init(config->isa_internal, NULL, NULL); assert(xtensa_isa_maxlength(config->isa) <= MAX_INSN_LENGTH); opcodes = xtensa_isa_num_opcodes(config->isa); + formats = xtensa_isa_num_formats(config->isa); + regfiles = xtensa_isa_num_regfiles(config->isa); config->opcode_ops = g_new(XtensaOpcodeOps *, opcodes); + for (i = 0; i < formats; ++i) { + assert(xtensa_format_num_slots(config->isa, i) <= MAX_INSN_SLOTS); + } + for (i = 0; i < opcodes; ++i) { const char *opc_name = xtensa_opcode_name(config->isa, i); XtensaOpcodeOps *ops = NULL; @@ -85,9 +126,23 @@ static void init_libisa(XtensaConfig *config) #endif config->opcode_ops[i] = ops; } + config->a_regfile = xtensa_regfile_lookup(config->isa, "AR"); + + config->regfile = g_new(void **, regfiles); + for (i = 0; i < regfiles; ++i) { + const char *name = xtensa_regfile_name(config->isa, i); + + config->regfile[i] = xtensa_get_regfile_by_name(name); +#ifdef DEBUG + if (config->regfile[i] == NULL) { + fprintf(stderr, "regfile '%s' not found for %s\n", + name, config->name); + } +#endif + } } -void xtensa_finalize_config(XtensaConfig *config) +static void xtensa_finalize_config(XtensaConfig *config) { if (config->isa_internal) { init_libisa(config); @@ -95,18 +150,10 @@ void xtensa_finalize_config(XtensaConfig *config) if (config->gdb_regmap.num_regs == 0 || config->gdb_regmap.num_core_regs == 0) { - unsigned i; unsigned n_regs = 0; unsigned n_core_regs = 0; - for (i = 0; config->gdb_regmap.reg[i].targno >= 0; ++i) { - if (config->gdb_regmap.reg[i].type != 6) { - ++n_regs; - if ((config->gdb_regmap.reg[i].flags & 0x1) == 0) { - ++n_core_regs; - } - } - } + xtensa_count_regs(config, &n_regs, &n_core_regs); if (config->gdb_regmap.num_regs == 0) { config->gdb_regmap.num_regs = n_regs; } @@ -116,6 +163,24 @@ void xtensa_finalize_config(XtensaConfig *config) } } +static void xtensa_core_class_init(ObjectClass *oc, void *data) +{ + CPUClass *cc = CPU_CLASS(oc); + XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc); + XtensaConfig *config = data; + + xtensa_finalize_config(config); + xcc->config = config; + + /* + * Use num_core_regs to see only non-privileged registers in an unmodified + * gdb. Use num_regs to see all registers. gdb modification is required + * for that: reset bit 0 in the 'flags' field of the registers definitions + * in the gdb/xtensa-config.c inside gdb source tree or inside gdb overlay. + */ + cc->gdb_num_core_regs = config->gdb_regmap.num_regs; +} + void xtensa_register_core(XtensaConfigList *node) { TypeInfo type = { @@ -172,155 +237,6 @@ void xtensa_cpu_list(FILE *f, fprintf_function cpu_fprintf) } } -hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) -{ -#ifndef CONFIG_USER_ONLY - XtensaCPU *cpu = XTENSA_CPU(cs); - uint32_t paddr; - uint32_t page_size; - unsigned access; - - if (xtensa_get_physical_addr(&cpu->env, false, addr, 0, 0, - &paddr, &page_size, &access) == 0) { - return paddr; - } - if (xtensa_get_physical_addr(&cpu->env, false, addr, 2, 0, - &paddr, &page_size, &access) == 0) { - return paddr; - } - return ~0; -#else - return addr; -#endif -} - -#ifndef CONFIG_USER_ONLY - -static uint32_t relocated_vector(CPUXtensaState *env, uint32_t vector) -{ - if (xtensa_option_enabled(env->config, - XTENSA_OPTION_RELOCATABLE_VECTOR)) { - return vector - env->config->vecbase + env->sregs[VECBASE]; - } else { - return vector; - } -} - -/*! - * Handle penging IRQ. - * For the high priority interrupt jump to the corresponding interrupt vector. - * For the level-1 interrupt convert it to either user, kernel or double - * exception with the 'level-1 interrupt' exception cause. - */ -static void handle_interrupt(CPUXtensaState *env) -{ - int level = env->pending_irq_level; - - if (level > xtensa_get_cintlevel(env) && - level <= env->config->nlevel && - (env->config->level_mask[level] & - env->sregs[INTSET] & - env->sregs[INTENABLE])) { - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - - if (level > 1) { - env->sregs[EPC1 + level - 1] = env->pc; - env->sregs[EPS2 + level - 2] = env->sregs[PS]; - env->sregs[PS] = - (env->sregs[PS] & ~PS_INTLEVEL) | level | PS_EXCM; - env->pc = relocated_vector(env, - env->config->interrupt_vector[level]); - } else { - env->sregs[EXCCAUSE] = LEVEL1_INTERRUPT_CAUSE; - - if (env->sregs[PS] & PS_EXCM) { - if (env->config->ndepc) { - env->sregs[DEPC] = env->pc; - } else { - env->sregs[EPC1] = env->pc; - } - cs->exception_index = EXC_DOUBLE; - } else { - env->sregs[EPC1] = env->pc; - cs->exception_index = - (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; - } - env->sregs[PS] |= PS_EXCM; - } - env->exception_taken = 1; - } -} - -/* Called from cpu_handle_interrupt with BQL held */ -void xtensa_cpu_do_interrupt(CPUState *cs) -{ - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; - - if (cs->exception_index == EXC_IRQ) { - qemu_log_mask(CPU_LOG_INT, - "%s(EXC_IRQ) level = %d, cintlevel = %d, " - "pc = %08x, a0 = %08x, ps = %08x, " - "intset = %08x, intenable = %08x, " - "ccount = %08x\n", - __func__, env->pending_irq_level, xtensa_get_cintlevel(env), - env->pc, env->regs[0], env->sregs[PS], - env->sregs[INTSET], env->sregs[INTENABLE], - env->sregs[CCOUNT]); - handle_interrupt(env); - } - - switch (cs->exception_index) { - case EXC_WINDOW_OVERFLOW4: - case EXC_WINDOW_UNDERFLOW4: - case EXC_WINDOW_OVERFLOW8: - case EXC_WINDOW_UNDERFLOW8: - case EXC_WINDOW_OVERFLOW12: - case EXC_WINDOW_UNDERFLOW12: - case EXC_KERNEL: - case EXC_USER: - case EXC_DOUBLE: - case EXC_DEBUG: - qemu_log_mask(CPU_LOG_INT, "%s(%d) " - "pc = %08x, a0 = %08x, ps = %08x, ccount = %08x\n", - __func__, cs->exception_index, - env->pc, env->regs[0], env->sregs[PS], env->sregs[CCOUNT]); - if (env->config->exception_vector[cs->exception_index]) { - env->pc = relocated_vector(env, - env->config->exception_vector[cs->exception_index]); - env->exception_taken = 1; - } else { - qemu_log_mask(CPU_LOG_INT, "%s(pc = %08x) bad exception_index: %d\n", - __func__, env->pc, cs->exception_index); - } - break; - - case EXC_IRQ: - break; - - default: - qemu_log("%s(pc = %08x) unknown exception_index: %d\n", - __func__, env->pc, cs->exception_index); - break; - } - check_interrupts(env); -} -#else -void xtensa_cpu_do_interrupt(CPUState *cs) -{ -} -#endif - -bool xtensa_cpu_exec_interrupt(CPUState *cs, int interrupt_request) -{ - if (interrupt_request & CPU_INTERRUPT_HARD) { - cs->exception_index = EXC_IRQ; - xtensa_cpu_do_interrupt(cs); - return true; - } - return false; -} - #ifdef CONFIG_USER_ONLY int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, @@ -340,452 +256,61 @@ int xtensa_cpu_handle_mmu_fault(CPUState *cs, vaddr address, int size, int rw, #else -static void reset_tlb_mmu_all_ways(CPUXtensaState *env, - const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) +void xtensa_cpu_do_unaligned_access(CPUState *cs, + vaddr addr, MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) { - unsigned wi, ei; + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; - for (wi = 0; wi < tlb->nways; ++wi) { - for (ei = 0; ei < tlb->way_size[wi]; ++ei) { - entry[wi][ei].asid = 0; - entry[wi][ei].variable = true; - } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) && + !xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) { + cpu_restore_state(CPU(cpu), retaddr, true); + HELPER(exception_cause_vaddr)(env, + env->pc, LOAD_STORE_ALIGNMENT_CAUSE, + addr); } } -static void reset_tlb_mmu_ways56(CPUXtensaState *env, - const xtensa_tlb *tlb, xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) +void tlb_fill(CPUState *cs, target_ulong vaddr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) { - if (!tlb->varway56) { - static const xtensa_tlb_entry way5[] = { - { - .vaddr = 0xd0000000, - .paddr = 0, - .asid = 1, - .attr = 7, - .variable = false, - }, { - .vaddr = 0xd8000000, - .paddr = 0, - .asid = 1, - .attr = 3, - .variable = false, - } - }; - static const xtensa_tlb_entry way6[] = { - { - .vaddr = 0xe0000000, - .paddr = 0xf0000000, - .asid = 1, - .attr = 7, - .variable = false, - }, { - .vaddr = 0xf0000000, - .paddr = 0xf0000000, - .asid = 1, - .attr = 3, - .variable = false, - } - }; - memcpy(entry[5], way5, sizeof(way5)); - memcpy(entry[6], way6, sizeof(way6)); - } else { - uint32_t ei; - for (ei = 0; ei < 8; ++ei) { - entry[6][ei].vaddr = ei << 29; - entry[6][ei].paddr = ei << 29; - entry[6][ei].asid = 1; - entry[6][ei].attr = 3; - } - } -} - -static void reset_tlb_region_way0(CPUXtensaState *env, - xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) -{ - unsigned ei; - - for (ei = 0; ei < 8; ++ei) { - entry[0][ei].vaddr = ei << 29; - entry[0][ei].paddr = ei << 29; - entry[0][ei].asid = 1; - entry[0][ei].attr = 2; - entry[0][ei].variable = true; - } -} - -void reset_mmu(CPUXtensaState *env) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - env->sregs[RASID] = 0x04030201; - env->sregs[ITLBCFG] = 0; - env->sregs[DTLBCFG] = 0; - env->autorefill_idx = 0; - reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb); - reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb); - reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb); - reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb); - } else { - reset_tlb_region_way0(env, env->itlb); - reset_tlb_region_way0(env, env->dtlb); - } -} - -static unsigned get_ring(const CPUXtensaState *env, uint8_t asid) -{ - unsigned i; - for (i = 0; i < 4; ++i) { - if (((env->sregs[RASID] >> i * 8) & 0xff) == asid) { - return i; - } - } - return 0xff; -} - -/*! - * Lookup xtensa TLB for the given virtual address. - * See ISA, 4.6.2.2 - * - * \param pwi: [out] way index - * \param pei: [out] entry index - * \param pring: [out] access ring - * \return 0 if ok, exception cause code otherwise - */ -int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb, - uint32_t *pwi, uint32_t *pei, uint8_t *pring) -{ - const xtensa_tlb *tlb = dtlb ? - &env->config->dtlb : &env->config->itlb; - const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ? - env->dtlb : env->itlb; - - int nhits = 0; - unsigned wi; - - for (wi = 0; wi < tlb->nways; ++wi) { - uint32_t vpn; - uint32_t ei; - split_tlb_entry_spec_way(env, addr, dtlb, &vpn, wi, &ei); - if (entry[wi][ei].vaddr == vpn && entry[wi][ei].asid) { - unsigned ring = get_ring(env, entry[wi][ei].asid); - if (ring < 4) { - if (++nhits > 1) { - return dtlb ? - LOAD_STORE_TLB_MULTI_HIT_CAUSE : - INST_TLB_MULTI_HIT_CAUSE; - } - *pwi = wi; - *pei = ei; - *pring = ring; - } - } - } - return nhits ? 0 : - (dtlb ? LOAD_STORE_TLB_MISS_CAUSE : INST_TLB_MISS_CAUSE); -} - -/*! - * Convert MMU ATTR to PAGE_{READ,WRITE,EXEC} mask. - * See ISA, 4.6.5.10 - */ -static unsigned mmu_attr_to_access(uint32_t attr) -{ - unsigned access = 0; - - if (attr < 12) { - access |= PAGE_READ; - if (attr & 0x1) { - access |= PAGE_EXEC; - } - if (attr & 0x2) { - access |= PAGE_WRITE; - } - - switch (attr & 0xc) { - case 0: - access |= PAGE_CACHE_BYPASS; - break; - - case 4: - access |= PAGE_CACHE_WB; - break; - - case 8: - access |= PAGE_CACHE_WT; - break; - } - } else if (attr == 13) { - access |= PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE; - } - return access; -} - -/*! - * Convert region protection ATTR to PAGE_{READ,WRITE,EXEC} mask. - * See ISA, 4.6.3.3 - */ -static unsigned region_attr_to_access(uint32_t attr) -{ - static const unsigned access[16] = { - [0] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_WT, - [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT, - [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS, - [3] = PAGE_EXEC | PAGE_CACHE_WB, - [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, - [5] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, - [14] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE, - }; - - return access[attr & 0xf]; -} - -/*! - * Convert cacheattr to PAGE_{READ,WRITE,EXEC} mask. - * See ISA, A.2.14 The Cache Attribute Register - */ -static unsigned cacheattr_attr_to_access(uint32_t attr) -{ - static const unsigned access[16] = { - [0] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_WT, - [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT, - [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS, - [3] = PAGE_EXEC | PAGE_CACHE_WB, - [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, - [14] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE, - }; - - return access[attr & 0xf]; -} - -static bool is_access_granted(unsigned access, int is_write) -{ - switch (is_write) { - case 0: - return access & PAGE_READ; - - case 1: - return access & PAGE_WRITE; - - case 2: - return access & PAGE_EXEC; - - default: - return 0; - } -} - -static int get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte); - -static int get_physical_addr_mmu(CPUXtensaState *env, bool update_tlb, - uint32_t vaddr, int is_write, int mmu_idx, - uint32_t *paddr, uint32_t *page_size, unsigned *access, - bool may_lookup_pt) -{ - bool dtlb = is_write != 2; - uint32_t wi; - uint32_t ei; - uint8_t ring; - uint32_t vpn; - uint32_t pte; - const xtensa_tlb_entry *entry = NULL; - xtensa_tlb_entry tmp_entry; - int ret = xtensa_tlb_lookup(env, vaddr, dtlb, &wi, &ei, &ring); - - if ((ret == INST_TLB_MISS_CAUSE || ret == LOAD_STORE_TLB_MISS_CAUSE) && - may_lookup_pt && get_pte(env, vaddr, &pte) == 0) { - ring = (pte >> 4) & 0x3; - wi = 0; - split_tlb_entry_spec_way(env, vaddr, dtlb, &vpn, wi, &ei); - - if (update_tlb) { - wi = ++env->autorefill_idx & 0x3; - xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, pte); - env->sregs[EXCVADDR] = vaddr; - 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; - } - ret = 0; - } - if (ret != 0) { - return ret; - } - - if (entry == NULL) { - entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); - } - - if (ring < mmu_idx) { - return dtlb ? - LOAD_STORE_PRIVILEGE_CAUSE : - INST_FETCH_PRIVILEGE_CAUSE; - } - - *access = mmu_attr_to_access(entry->attr) & - ~(dtlb ? PAGE_EXEC : PAGE_READ | PAGE_WRITE); - if (!is_access_granted(*access, is_write)) { - return dtlb ? - (is_write ? - STORE_PROHIBITED_CAUSE : - LOAD_PROHIBITED_CAUSE) : - INST_FETCH_PROHIBITED_CAUSE; - } - - *paddr = entry->paddr | (vaddr & ~xtensa_tlb_get_addr_mask(env, dtlb, wi)); - *page_size = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; - - return 0; -} - -static int get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; uint32_t paddr; uint32_t page_size; unsigned access; - uint32_t pt_vaddr = - (env->sregs[PTEVADDR] | (vaddr >> 10)) & 0xfffffffc; - int ret = get_physical_addr_mmu(env, false, pt_vaddr, 0, 0, - &paddr, &page_size, &access, false); + int ret = xtensa_get_physical_addr(env, true, vaddr, access_type, mmu_idx, + &paddr, &page_size, &access); - qemu_log_mask(CPU_LOG_MMU, "%s: trying autorefill(%08x) -> %08x\n", - __func__, vaddr, ret ? ~0 : paddr); + qemu_log_mask(CPU_LOG_MMU, "%s(%08x, %d, %d) -> %08x, ret = %d\n", + __func__, vaddr, access_type, mmu_idx, paddr, ret); if (ret == 0) { - *pte = ldl_phys(cs->as, paddr); - } - return ret; -} - -static int get_physical_addr_region(CPUXtensaState *env, - uint32_t vaddr, int is_write, int mmu_idx, - uint32_t *paddr, uint32_t *page_size, unsigned *access) -{ - bool dtlb = is_write != 2; - uint32_t wi = 0; - uint32_t ei = (vaddr >> 29) & 0x7; - const xtensa_tlb_entry *entry = - xtensa_tlb_get_entry(env, dtlb, wi, ei); - - *access = region_attr_to_access(entry->attr); - if (!is_access_granted(*access, is_write)) { - return dtlb ? - (is_write ? - STORE_PROHIBITED_CAUSE : - LOAD_PROHIBITED_CAUSE) : - INST_FETCH_PROHIBITED_CAUSE; - } - - *paddr = entry->paddr | (vaddr & ~REGION_PAGE_MASK); - *page_size = ~REGION_PAGE_MASK + 1; - - return 0; -} - -/*! - * Convert virtual address to physical addr. - * MMU may issue pagewalk and change xtensa autorefill TLB way entry. - * - * \return 0 if ok, exception cause code otherwise - */ -int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb, - uint32_t vaddr, int is_write, int mmu_idx, - uint32_t *paddr, uint32_t *page_size, unsigned *access) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - return get_physical_addr_mmu(env, update_tlb, - vaddr, is_write, mmu_idx, paddr, page_size, access, true); - } else if (xtensa_option_bits_enabled(env->config, - XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | - XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) { - return get_physical_addr_region(env, vaddr, is_write, mmu_idx, - paddr, page_size, access); + tlb_set_page(cs, + vaddr & TARGET_PAGE_MASK, + paddr & TARGET_PAGE_MASK, + access, mmu_idx, page_size); } else { - *paddr = vaddr; - *page_size = TARGET_PAGE_SIZE; - *access = cacheattr_attr_to_access( - env->sregs[CACHEATTR] >> ((vaddr & 0xe0000000) >> 27)); - return 0; + cpu_restore_state(cs, retaddr, true); + HELPER(exception_cause_vaddr)(env, env->pc, ret, vaddr); } } -static void dump_tlb(FILE *f, fprintf_function cpu_fprintf, - CPUXtensaState *env, bool dtlb) +void xtensa_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, + unsigned size, MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) { - unsigned wi, ei; - const xtensa_tlb *conf = - dtlb ? &env->config->dtlb : &env->config->itlb; - unsigned (*attr_to_access)(uint32_t) = - xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ? - mmu_attr_to_access : region_attr_to_access; + XtensaCPU *cpu = XTENSA_CPU(cs); + CPUXtensaState *env = &cpu->env; - for (wi = 0; wi < conf->nways; ++wi) { - uint32_t sz = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; - const char *sz_text; - bool print_header = true; - - if (sz >= 0x100000) { - sz /= MiB; - sz_text = "MB"; - } else { - sz /= KiB; - sz_text = "KB"; - } - - for (ei = 0; ei < conf->way_size[wi]; ++ei) { - const xtensa_tlb_entry *entry = - xtensa_tlb_get_entry(env, dtlb, wi, ei); - - if (entry->asid) { - static const char * const cache_text[8] = { - [PAGE_CACHE_BYPASS >> PAGE_CACHE_SHIFT] = "Bypass", - [PAGE_CACHE_WT >> PAGE_CACHE_SHIFT] = "WT", - [PAGE_CACHE_WB >> PAGE_CACHE_SHIFT] = "WB", - [PAGE_CACHE_ISOLATE >> PAGE_CACHE_SHIFT] = "Isolate", - }; - unsigned access = attr_to_access(entry->attr); - unsigned cache_idx = (access & PAGE_CACHE_MASK) >> - PAGE_CACHE_SHIFT; - - if (print_header) { - print_header = false; - cpu_fprintf(f, "Way %u (%d %s)\n", wi, sz, sz_text); - cpu_fprintf(f, - "\tVaddr Paddr ASID Attr RWX Cache\n" - "\t---------- ---------- ---- ---- --- -------\n"); - } - cpu_fprintf(f, - "\t0x%08x 0x%08x 0x%02x 0x%02x %c%c%c %-7s\n", - entry->vaddr, - entry->paddr, - entry->asid, - entry->attr, - (access & PAGE_READ) ? 'R' : '-', - (access & PAGE_WRITE) ? 'W' : '-', - (access & PAGE_EXEC) ? 'X' : '-', - cache_text[cache_idx] ? cache_text[cache_idx] : - "Invalid"); - } - } - } -} - -void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env) -{ - if (xtensa_option_bits_enabled(env->config, - XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | - XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) | - XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) { - - cpu_fprintf(f, "ITLB:\n"); - dump_tlb(f, cpu_fprintf, env, false); - cpu_fprintf(f, "\nDTLB:\n"); - dump_tlb(f, cpu_fprintf, env, true); - } else { - cpu_fprintf(f, "No TLB for this CPU core\n"); - } + cpu_restore_state(cs, retaddr, true); + HELPER(exception_cause_vaddr)(env, env->pc, + access_type == MMU_INST_FETCH ? + INSTR_PIF_ADDR_ERROR_CAUSE : + LOAD_STORE_PIF_ADDR_ERROR_CAUSE, + addr); } void xtensa_runstall(CPUXtensaState *env, bool runstall) @@ -797,7 +322,7 @@ void xtensa_runstall(CPUXtensaState *env, bool runstall) if (runstall) { cpu_interrupt(cpu, CPU_INTERRUPT_HALT); } else { - cpu_reset_interrupt(cpu, CPU_INTERRUPT_HALT); + qemu_cpu_kick(cpu); } } #endif diff --git a/target/xtensa/helper.h b/target/xtensa/helper.h index 73444ae02c..0b9ec670c8 100644 --- a/target/xtensa/helper.h +++ b/target/xtensa/helper.h @@ -3,19 +3,17 @@ DEF_HELPER_3(exception_cause, noreturn, env, i32, i32) DEF_HELPER_4(exception_cause_vaddr, noreturn, env, i32, i32, i32) DEF_HELPER_3(debug_exception, noreturn, env, i32, i32) -DEF_HELPER_2(wsr_windowbase, void, env, i32) +DEF_HELPER_1(sync_windowbase, void, env) DEF_HELPER_4(entry, void, env, i32, i32, i32) -DEF_HELPER_2(retw, i32, env, i32) -DEF_HELPER_2(rotw, void, env, i32) +DEF_HELPER_2(test_ill_retw, void, env, i32) +DEF_HELPER_2(test_underflow_retw, void, env, i32) +DEF_HELPER_2(retw, void, env, i32) DEF_HELPER_3(window_check, noreturn, env, i32, i32) DEF_HELPER_1(restore_owb, void, env) DEF_HELPER_2(movsp, void, env, i32) -DEF_HELPER_2(wsr_lbeg, void, env, i32) -DEF_HELPER_2(wsr_lend, void, env, i32) #ifndef CONFIG_USER_ONLY DEF_HELPER_1(simcall, void, env) #endif -DEF_HELPER_1(dump_state, void, env) #ifndef CONFIG_USER_ONLY DEF_HELPER_3(waiti, void, env, i32, i32) @@ -23,6 +21,8 @@ DEF_HELPER_1(update_ccount, void, env) DEF_HELPER_2(wsr_ccount, void, env, i32) DEF_HELPER_2(update_ccompare, void, env, i32) DEF_HELPER_1(check_interrupts, void, env) +DEF_HELPER_2(intset, void, env, i32) +DEF_HELPER_2(intclear, void, env, i32) DEF_HELPER_3(check_atomctl, void, env, i32, i32) DEF_HELPER_2(wsr_memctl, void, env, i32) diff --git a/target/xtensa/import_core.sh b/target/xtensa/import_core.sh index 039406bf28..e4a2e39f63 100755 --- a/target/xtensa/import_core.sh +++ b/target/xtensa/import_core.sh @@ -27,7 +27,7 @@ tar -xf "$OVERLAY" -O gdb/xtensa-config.c | \ # Fix up known issues in the xtensa-modules.c # tar -xf "$OVERLAY" -O binutils/xtensa-modules.c | \ - sed -e 's/\(xtensa_opcode_encode_fn.*\[\] =\)/static \1/' \ + sed -e 's/^\(xtensa_opcode_encode_fn.*\[\] =\)/static \1/' \ -e '/^int num_bypass_groups()/,/}/d' \ -e '/^int num_bypass_group_chunks()/,/}/d' \ -e '/^uint32 \*bypass_entry(int i)/,/}/d' \ diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c new file mode 100644 index 0000000000..2096fbbd9f --- /dev/null +++ b/target/xtensa/mmu_helper.c @@ -0,0 +1,818 @@ +/* + * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qemu/units.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" +#include "exec/cpu_ldst.h" + +void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) +{ + /* + * Attempt the memory load; we don't care about the result but + * only the side-effects (ie any MMU or other exception) + */ + cpu_ldub_code_ra(env, vaddr, GETPC()); +} + +void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v) +{ + XtensaCPU *cpu = xtensa_env_get_cpu(env); + + v = (v & 0xffffff00) | 0x1; + if (v != env->sregs[RASID]) { + env->sregs[RASID] = v; + tlb_flush(CPU(cpu)); + } +} + +static uint32_t get_page_size(const CPUXtensaState *env, + bool dtlb, uint32_t way) +{ + uint32_t tlbcfg = env->sregs[dtlb ? DTLBCFG : ITLBCFG]; + + switch (way) { + case 4: + return (tlbcfg >> 16) & 0x3; + + case 5: + return (tlbcfg >> 20) & 0x1; + + case 6: + return (tlbcfg >> 24) & 0x1; + + default: + return 0; + } +} + +/*! + * Get bit mask for the virtual address bits translated by the TLB way + */ +uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, + bool dtlb, uint32_t way) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + bool varway56 = dtlb ? + env->config->dtlb.varway56 : + env->config->itlb.varway56; + + switch (way) { + case 4: + return 0xfff00000 << get_page_size(env, dtlb, way) * 2; + + case 5: + if (varway56) { + return 0xf8000000 << get_page_size(env, dtlb, way); + } else { + return 0xf8000000; + } + + case 6: + if (varway56) { + return 0xf0000000 << (1 - get_page_size(env, dtlb, way)); + } else { + return 0xf0000000; + } + + default: + return 0xfffff000; + } + } else { + return REGION_PAGE_MASK; + } +} + +/*! + * Get bit mask for the 'VPN without index' field. + * See ISA, 4.6.5.6, data format for RxTLB0 + */ +static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way) +{ + if (way < 4) { + bool is32 = (dtlb ? + env->config->dtlb.nrefillentries : + env->config->itlb.nrefillentries) == 32; + return is32 ? 0xffff8000 : 0xffffc000; + } else if (way == 4) { + return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2; + } else if (way <= 6) { + uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way); + bool varway56 = dtlb ? + env->config->dtlb.varway56 : + env->config->itlb.varway56; + + if (varway56) { + return mask << (way == 5 ? 2 : 3); + } else { + return mask << 1; + } + } else { + return 0xfffff000; + } +} + +/*! + * Split virtual address into VPN (with index) and entry index + * for the given TLB way + */ +void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb, + uint32_t *vpn, uint32_t wi, uint32_t *ei) +{ + bool varway56 = dtlb ? + env->config->dtlb.varway56 : + env->config->itlb.varway56; + + if (!dtlb) { + wi &= 7; + } + + if (wi < 4) { + bool is32 = (dtlb ? + env->config->dtlb.nrefillentries : + env->config->itlb.nrefillentries) == 32; + *ei = (v >> 12) & (is32 ? 0x7 : 0x3); + } else { + switch (wi) { + case 4: + { + uint32_t eibase = 20 + get_page_size(env, dtlb, wi) * 2; + *ei = (v >> eibase) & 0x3; + } + break; + + case 5: + if (varway56) { + uint32_t eibase = 27 + get_page_size(env, dtlb, wi); + *ei = (v >> eibase) & 0x3; + } else { + *ei = (v >> 27) & 0x1; + } + break; + + case 6: + if (varway56) { + uint32_t eibase = 29 - get_page_size(env, dtlb, wi); + *ei = (v >> eibase) & 0x7; + } else { + *ei = (v >> 28) & 0x1; + } + break; + + default: + *ei = 0; + break; + } + } + *vpn = v & xtensa_tlb_get_addr_mask(env, dtlb, wi); +} + +/*! + * Split TLB address into TLB way, entry index and VPN (with index). + * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format + */ +static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, + uint32_t *vpn, uint32_t *wi, uint32_t *ei) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + *wi = v & (dtlb ? 0xf : 0x7); + split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); + } else { + *vpn = v & REGION_PAGE_MASK; + *wi = 0; + *ei = (v >> 29) & 0x7; + } +} + +static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env, + uint32_t v, bool dtlb, uint32_t *pwi) +{ + uint32_t vpn; + uint32_t wi; + uint32_t ei; + + split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); + if (pwi) { + *pwi = wi; + } + return xtensa_tlb_get_entry(env, dtlb, wi, ei); +} + +uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + uint32_t wi; + const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); + return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; + } else { + return v & REGION_PAGE_MASK; + } +} + +uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) +{ + const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL); + return entry->paddr | entry->attr; +} + +void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + uint32_t wi; + xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); + if (entry->variable && entry->asid) { + tlb_flush_page(CPU(xtensa_env_get_cpu(env)), entry->vaddr); + entry->asid = 0; + } + } +} + +uint32_t HELPER(ptlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + uint32_t wi; + uint32_t ei; + uint8_t ring; + int res = xtensa_tlb_lookup(env, v, dtlb, &wi, &ei, &ring); + + switch (res) { + case 0: + if (ring >= xtensa_get_ring(env)) { + return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8); + } + break; + + case INST_TLB_MULTI_HIT_CAUSE: + case LOAD_STORE_TLB_MULTI_HIT_CAUSE: + HELPER(exception_cause_vaddr)(env, env->pc, res, v); + break; + } + return 0; + } else { + return (v & REGION_PAGE_MASK) | 0x1; + } +} + +void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env, + xtensa_tlb_entry *entry, bool dtlb, + unsigned wi, unsigned ei, uint32_t vpn, + uint32_t pte) +{ + entry->vaddr = vpn; + entry->paddr = pte & xtensa_tlb_get_addr_mask(env, dtlb, wi); + entry->asid = (env->sregs[RASID] >> ((pte >> 1) & 0x18)) & 0xff; + entry->attr = pte & 0xf; +} + +void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb, + unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte) +{ + XtensaCPU *cpu = xtensa_env_get_cpu(env); + CPUState *cs = CPU(cpu); + xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); + + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + if (entry->variable) { + if (entry->asid) { + tlb_flush_page(cs, entry->vaddr); + } + xtensa_tlb_set_entry_mmu(env, entry, dtlb, wi, ei, vpn, pte); + tlb_flush_page(cs, entry->vaddr); + } else { + 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); + if (xtensa_option_enabled(env->config, + XTENSA_OPTION_REGION_TRANSLATION)) { + entry->paddr = pte & REGION_PAGE_MASK; + } + entry->attr = pte & 0xf; + } +} + +void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb) +{ + uint32_t vpn; + uint32_t wi; + uint32_t ei; + split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); + xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); +} + +hwaddr xtensa_cpu_get_phys_page_debug(CPUState *cs, vaddr addr) +{ + XtensaCPU *cpu = XTENSA_CPU(cs); + uint32_t paddr; + uint32_t page_size; + unsigned access; + + if (xtensa_get_physical_addr(&cpu->env, false, addr, 0, 0, + &paddr, &page_size, &access) == 0) { + return paddr; + } + if (xtensa_get_physical_addr(&cpu->env, false, addr, 2, 0, + &paddr, &page_size, &access) == 0) { + return paddr; + } + return ~0; +} + +static void reset_tlb_mmu_all_ways(CPUXtensaState *env, + const xtensa_tlb *tlb, + xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) +{ + unsigned wi, ei; + + for (wi = 0; wi < tlb->nways; ++wi) { + for (ei = 0; ei < tlb->way_size[wi]; ++ei) { + entry[wi][ei].asid = 0; + entry[wi][ei].variable = true; + } + } +} + +static void reset_tlb_mmu_ways56(CPUXtensaState *env, + const xtensa_tlb *tlb, + xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) +{ + if (!tlb->varway56) { + static const xtensa_tlb_entry way5[] = { + { + .vaddr = 0xd0000000, + .paddr = 0, + .asid = 1, + .attr = 7, + .variable = false, + }, { + .vaddr = 0xd8000000, + .paddr = 0, + .asid = 1, + .attr = 3, + .variable = false, + } + }; + static const xtensa_tlb_entry way6[] = { + { + .vaddr = 0xe0000000, + .paddr = 0xf0000000, + .asid = 1, + .attr = 7, + .variable = false, + }, { + .vaddr = 0xf0000000, + .paddr = 0xf0000000, + .asid = 1, + .attr = 3, + .variable = false, + } + }; + memcpy(entry[5], way5, sizeof(way5)); + memcpy(entry[6], way6, sizeof(way6)); + } else { + uint32_t ei; + for (ei = 0; ei < 8; ++ei) { + entry[6][ei].vaddr = ei << 29; + entry[6][ei].paddr = ei << 29; + entry[6][ei].asid = 1; + entry[6][ei].attr = 3; + } + } +} + +static void reset_tlb_region_way0(CPUXtensaState *env, + xtensa_tlb_entry entry[][MAX_TLB_WAY_SIZE]) +{ + unsigned ei; + + for (ei = 0; ei < 8; ++ei) { + entry[0][ei].vaddr = ei << 29; + entry[0][ei].paddr = ei << 29; + entry[0][ei].asid = 1; + entry[0][ei].attr = 2; + entry[0][ei].variable = true; + } +} + +void reset_mmu(CPUXtensaState *env) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + env->sregs[RASID] = 0x04030201; + env->sregs[ITLBCFG] = 0; + env->sregs[DTLBCFG] = 0; + env->autorefill_idx = 0; + reset_tlb_mmu_all_ways(env, &env->config->itlb, env->itlb); + reset_tlb_mmu_all_ways(env, &env->config->dtlb, env->dtlb); + reset_tlb_mmu_ways56(env, &env->config->itlb, env->itlb); + reset_tlb_mmu_ways56(env, &env->config->dtlb, env->dtlb); + } else { + reset_tlb_region_way0(env, env->itlb); + reset_tlb_region_way0(env, env->dtlb); + } +} + +static unsigned get_ring(const CPUXtensaState *env, uint8_t asid) +{ + unsigned i; + for (i = 0; i < 4; ++i) { + if (((env->sregs[RASID] >> i * 8) & 0xff) == asid) { + return i; + } + } + return 0xff; +} + +/*! + * Lookup xtensa TLB for the given virtual address. + * See ISA, 4.6.2.2 + * + * \param pwi: [out] way index + * \param pei: [out] entry index + * \param pring: [out] access ring + * \return 0 if ok, exception cause code otherwise + */ +int xtensa_tlb_lookup(const CPUXtensaState *env, uint32_t addr, bool dtlb, + uint32_t *pwi, uint32_t *pei, uint8_t *pring) +{ + const xtensa_tlb *tlb = dtlb ? + &env->config->dtlb : &env->config->itlb; + const xtensa_tlb_entry (*entry)[MAX_TLB_WAY_SIZE] = dtlb ? + env->dtlb : env->itlb; + + int nhits = 0; + unsigned wi; + + for (wi = 0; wi < tlb->nways; ++wi) { + uint32_t vpn; + uint32_t ei; + split_tlb_entry_spec_way(env, addr, dtlb, &vpn, wi, &ei); + if (entry[wi][ei].vaddr == vpn && entry[wi][ei].asid) { + unsigned ring = get_ring(env, entry[wi][ei].asid); + if (ring < 4) { + if (++nhits > 1) { + return dtlb ? + LOAD_STORE_TLB_MULTI_HIT_CAUSE : + INST_TLB_MULTI_HIT_CAUSE; + } + *pwi = wi; + *pei = ei; + *pring = ring; + } + } + } + return nhits ? 0 : + (dtlb ? LOAD_STORE_TLB_MISS_CAUSE : INST_TLB_MISS_CAUSE); +} + +/*! + * Convert MMU ATTR to PAGE_{READ,WRITE,EXEC} mask. + * See ISA, 4.6.5.10 + */ +static unsigned mmu_attr_to_access(uint32_t attr) +{ + unsigned access = 0; + + if (attr < 12) { + access |= PAGE_READ; + if (attr & 0x1) { + access |= PAGE_EXEC; + } + if (attr & 0x2) { + access |= PAGE_WRITE; + } + + switch (attr & 0xc) { + case 0: + access |= PAGE_CACHE_BYPASS; + break; + + case 4: + access |= PAGE_CACHE_WB; + break; + + case 8: + access |= PAGE_CACHE_WT; + break; + } + } else if (attr == 13) { + access |= PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE; + } + return access; +} + +/*! + * Convert region protection ATTR to PAGE_{READ,WRITE,EXEC} mask. + * See ISA, 4.6.3.3 + */ +static unsigned region_attr_to_access(uint32_t attr) +{ + static const unsigned access[16] = { + [0] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_WT, + [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT, + [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS, + [3] = PAGE_EXEC | PAGE_CACHE_WB, + [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, + [5] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, + [14] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE, + }; + + return access[attr & 0xf]; +} + +/*! + * Convert cacheattr to PAGE_{READ,WRITE,EXEC} mask. + * See ISA, A.2.14 The Cache Attribute Register + */ +static unsigned cacheattr_attr_to_access(uint32_t attr) +{ + static const unsigned access[16] = { + [0] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_WT, + [1] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WT, + [2] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_BYPASS, + [3] = PAGE_EXEC | PAGE_CACHE_WB, + [4] = PAGE_READ | PAGE_WRITE | PAGE_EXEC | PAGE_CACHE_WB, + [14] = PAGE_READ | PAGE_WRITE | PAGE_CACHE_ISOLATE, + }; + + return access[attr & 0xf]; +} + +static bool is_access_granted(unsigned access, int is_write) +{ + switch (is_write) { + case 0: + return access & PAGE_READ; + + case 1: + return access & PAGE_WRITE; + + case 2: + return access & PAGE_EXEC; + + default: + return 0; + } +} + +static bool get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte); + +static int get_physical_addr_mmu(CPUXtensaState *env, bool update_tlb, + uint32_t vaddr, int is_write, int mmu_idx, + uint32_t *paddr, uint32_t *page_size, + unsigned *access, bool may_lookup_pt) +{ + bool dtlb = is_write != 2; + uint32_t wi; + uint32_t ei; + uint8_t ring; + uint32_t vpn; + uint32_t pte; + const xtensa_tlb_entry *entry = NULL; + xtensa_tlb_entry tmp_entry; + int ret = xtensa_tlb_lookup(env, vaddr, dtlb, &wi, &ei, &ring); + + if ((ret == INST_TLB_MISS_CAUSE || ret == LOAD_STORE_TLB_MISS_CAUSE) && + may_lookup_pt && get_pte(env, vaddr, &pte)) { + ring = (pte >> 4) & 0x3; + wi = 0; + split_tlb_entry_spec_way(env, vaddr, dtlb, &vpn, wi, &ei); + + if (update_tlb) { + wi = ++env->autorefill_idx & 0x3; + xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, pte); + env->sregs[EXCVADDR] = vaddr; + 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; + } + ret = 0; + } + if (ret != 0) { + return ret; + } + + if (entry == NULL) { + entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); + } + + if (ring < mmu_idx) { + return dtlb ? + LOAD_STORE_PRIVILEGE_CAUSE : + INST_FETCH_PRIVILEGE_CAUSE; + } + + *access = mmu_attr_to_access(entry->attr) & + ~(dtlb ? PAGE_EXEC : PAGE_READ | PAGE_WRITE); + if (!is_access_granted(*access, is_write)) { + return dtlb ? + (is_write ? + STORE_PROHIBITED_CAUSE : + LOAD_PROHIBITED_CAUSE) : + INST_FETCH_PROHIBITED_CAUSE; + } + + *paddr = entry->paddr | (vaddr & ~xtensa_tlb_get_addr_mask(env, dtlb, wi)); + *page_size = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; + + return 0; +} + +static bool get_pte(CPUXtensaState *env, uint32_t vaddr, uint32_t *pte) +{ + CPUState *cs = CPU(xtensa_env_get_cpu(env)); + uint32_t paddr; + uint32_t page_size; + unsigned access; + uint32_t pt_vaddr = + (env->sregs[PTEVADDR] | (vaddr >> 10)) & 0xfffffffc; + int ret = get_physical_addr_mmu(env, false, pt_vaddr, 0, 0, + &paddr, &page_size, &access, false); + + if (ret == 0) { + qemu_log_mask(CPU_LOG_MMU, + "%s: autorefill(%08x): PTE va = %08x, pa = %08x\n", + __func__, vaddr, pt_vaddr, paddr); + } else { + qemu_log_mask(CPU_LOG_MMU, + "%s: autorefill(%08x): PTE va = %08x, failed (%d)\n", + __func__, vaddr, pt_vaddr, ret); + } + + if (ret == 0) { + MemTxResult result; + + *pte = address_space_ldl(cs->as, paddr, MEMTXATTRS_UNSPECIFIED, + &result); + if (result != MEMTX_OK) { + qemu_log_mask(CPU_LOG_MMU, + "%s: couldn't load PTE: transaction failed (%u)\n", + __func__, (unsigned)result); + ret = 1; + } + } + return ret == 0; +} + +static int get_physical_addr_region(CPUXtensaState *env, + uint32_t vaddr, int is_write, int mmu_idx, + uint32_t *paddr, uint32_t *page_size, + unsigned *access) +{ + bool dtlb = is_write != 2; + uint32_t wi = 0; + uint32_t ei = (vaddr >> 29) & 0x7; + const xtensa_tlb_entry *entry = + xtensa_tlb_get_entry(env, dtlb, wi, ei); + + *access = region_attr_to_access(entry->attr); + if (!is_access_granted(*access, is_write)) { + return dtlb ? + (is_write ? + STORE_PROHIBITED_CAUSE : + LOAD_PROHIBITED_CAUSE) : + INST_FETCH_PROHIBITED_CAUSE; + } + + *paddr = entry->paddr | (vaddr & ~REGION_PAGE_MASK); + *page_size = ~REGION_PAGE_MASK + 1; + + return 0; +} + +/*! + * Convert virtual address to physical addr. + * MMU may issue pagewalk and change xtensa autorefill TLB way entry. + * + * \return 0 if ok, exception cause code otherwise + */ +int xtensa_get_physical_addr(CPUXtensaState *env, bool update_tlb, + uint32_t vaddr, int is_write, int mmu_idx, + uint32_t *paddr, uint32_t *page_size, + unsigned *access) +{ + if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { + return get_physical_addr_mmu(env, update_tlb, + vaddr, is_write, mmu_idx, paddr, + page_size, access, true); + } else if (xtensa_option_bits_enabled(env->config, + XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | + XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION))) { + return get_physical_addr_region(env, vaddr, is_write, mmu_idx, + paddr, page_size, access); + } else { + *paddr = vaddr; + *page_size = TARGET_PAGE_SIZE; + *access = cacheattr_attr_to_access(env->sregs[CACHEATTR] >> + ((vaddr & 0xe0000000) >> 27)); + return 0; + } +} + +static void dump_tlb(FILE *f, fprintf_function cpu_fprintf, + CPUXtensaState *env, bool dtlb) +{ + unsigned wi, ei; + const xtensa_tlb *conf = + dtlb ? &env->config->dtlb : &env->config->itlb; + unsigned (*attr_to_access)(uint32_t) = + xtensa_option_enabled(env->config, XTENSA_OPTION_MMU) ? + mmu_attr_to_access : region_attr_to_access; + + for (wi = 0; wi < conf->nways; ++wi) { + uint32_t sz = ~xtensa_tlb_get_addr_mask(env, dtlb, wi) + 1; + const char *sz_text; + bool print_header = true; + + if (sz >= 0x100000) { + sz /= MiB; + sz_text = "MB"; + } else { + sz /= KiB; + sz_text = "KB"; + } + + for (ei = 0; ei < conf->way_size[wi]; ++ei) { + const xtensa_tlb_entry *entry = + xtensa_tlb_get_entry(env, dtlb, wi, ei); + + if (entry->asid) { + static const char * const cache_text[8] = { + [PAGE_CACHE_BYPASS >> PAGE_CACHE_SHIFT] = "Bypass", + [PAGE_CACHE_WT >> PAGE_CACHE_SHIFT] = "WT", + [PAGE_CACHE_WB >> PAGE_CACHE_SHIFT] = "WB", + [PAGE_CACHE_ISOLATE >> PAGE_CACHE_SHIFT] = "Isolate", + }; + unsigned access = attr_to_access(entry->attr); + unsigned cache_idx = (access & PAGE_CACHE_MASK) >> + PAGE_CACHE_SHIFT; + + if (print_header) { + print_header = false; + cpu_fprintf(f, "Way %u (%d %s)\n", wi, sz, sz_text); + cpu_fprintf(f, + "\tVaddr Paddr ASID Attr RWX Cache\n" + "\t---------- ---------- ---- ---- --- -------\n"); + } + cpu_fprintf(f, + "\t0x%08x 0x%08x 0x%02x 0x%02x %c%c%c %-7s\n", + entry->vaddr, + entry->paddr, + entry->asid, + entry->attr, + (access & PAGE_READ) ? 'R' : '-', + (access & PAGE_WRITE) ? 'W' : '-', + (access & PAGE_EXEC) ? 'X' : '-', + cache_text[cache_idx] ? + cache_text[cache_idx] : "Invalid"); + } + } + } +} + +void dump_mmu(FILE *f, fprintf_function cpu_fprintf, CPUXtensaState *env) +{ + if (xtensa_option_bits_enabled(env->config, + XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_PROTECTION) | + XTENSA_OPTION_BIT(XTENSA_OPTION_REGION_TRANSLATION) | + XTENSA_OPTION_BIT(XTENSA_OPTION_MMU))) { + + cpu_fprintf(f, "ITLB:\n"); + dump_tlb(f, cpu_fprintf, env, false); + cpu_fprintf(f, "\nDTLB:\n"); + dump_tlb(f, cpu_fprintf, env, true); + } else { + cpu_fprintf(f, "No TLB for this CPU core\n"); + } +} diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index d4c942d879..04971b044f 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -34,389 +34,9 @@ #include "exec/cpu_ldst.h" #include "exec/address-spaces.h" #include "qemu/timer.h" -#include "fpu/softfloat.h" #ifndef CONFIG_USER_ONLY -void xtensa_cpu_do_unaligned_access(CPUState *cs, - vaddr addr, MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; - - if (xtensa_option_enabled(env->config, XTENSA_OPTION_UNALIGNED_EXCEPTION) && - !xtensa_option_enabled(env->config, XTENSA_OPTION_HW_ALIGNMENT)) { - cpu_restore_state(CPU(cpu), retaddr, true); - HELPER(exception_cause_vaddr)(env, - env->pc, LOAD_STORE_ALIGNMENT_CAUSE, addr); - } -} - -void tlb_fill(CPUState *cs, target_ulong vaddr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr) -{ - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; - uint32_t paddr; - uint32_t page_size; - unsigned access; - int ret = xtensa_get_physical_addr(env, true, vaddr, access_type, mmu_idx, - &paddr, &page_size, &access); - - qemu_log_mask(CPU_LOG_MMU, "%s(%08x, %d, %d) -> %08x, ret = %d\n", - __func__, vaddr, access_type, mmu_idx, paddr, ret); - - if (ret == 0) { - tlb_set_page(cs, - vaddr & TARGET_PAGE_MASK, - paddr & TARGET_PAGE_MASK, - access, mmu_idx, page_size); - } else { - cpu_restore_state(cs, retaddr, true); - HELPER(exception_cause_vaddr)(env, env->pc, ret, vaddr); - } -} - -void xtensa_cpu_do_unassigned_access(CPUState *cs, hwaddr addr, - bool is_write, bool is_exec, int opaque, - unsigned size) -{ - XtensaCPU *cpu = XTENSA_CPU(cs); - CPUXtensaState *env = &cpu->env; - - HELPER(exception_cause_vaddr)(env, env->pc, - is_exec ? - INSTR_PIF_ADDR_ERROR_CAUSE : - LOAD_STORE_PIF_ADDR_ERROR_CAUSE, - is_exec ? addr : cs->mem_io_vaddr); -} - -static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) -{ - uint32_t paddr; - uint32_t page_size; - unsigned access; - int ret = xtensa_get_physical_addr(env, false, vaddr, 2, 0, - &paddr, &page_size, &access); - if (ret == 0) { - tb_invalidate_phys_addr(&address_space_memory, paddr, - MEMTXATTRS_UNSPECIFIED); - } -} - -#else - -static void tb_invalidate_virtual_addr(CPUXtensaState *env, uint32_t vaddr) -{ - tb_invalidate_phys_addr(vaddr); -} - -#endif - -void HELPER(exception)(CPUXtensaState *env, uint32_t excp) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - - cs->exception_index = excp; - if (excp == EXCP_YIELD) { - env->yield_needed = 0; - } - if (excp == EXCP_DEBUG) { - env->exception_taken = 0; - } - cpu_loop_exit(cs); -} - -void HELPER(exception_cause)(CPUXtensaState *env, uint32_t pc, uint32_t cause) -{ - uint32_t vector; - - env->pc = pc; - if (env->sregs[PS] & PS_EXCM) { - if (env->config->ndepc) { - env->sregs[DEPC] = pc; - } else { - env->sregs[EPC1] = pc; - } - vector = EXC_DOUBLE; - } else { - env->sregs[EPC1] = pc; - vector = (env->sregs[PS] & PS_UM) ? EXC_USER : EXC_KERNEL; - } - - env->sregs[EXCCAUSE] = cause; - env->sregs[PS] |= PS_EXCM; - - HELPER(exception)(env, vector); -} - -void HELPER(exception_cause_vaddr)(CPUXtensaState *env, - uint32_t pc, uint32_t cause, uint32_t vaddr) -{ - env->sregs[EXCVADDR] = vaddr; - HELPER(exception_cause)(env, pc, cause); -} - -void debug_exception_env(CPUXtensaState *env, uint32_t cause) -{ - if (xtensa_get_cintlevel(env) < env->config->debug_level) { - HELPER(debug_exception)(env, env->pc, cause); - } -} - -void HELPER(debug_exception)(CPUXtensaState *env, uint32_t pc, uint32_t cause) -{ - unsigned level = env->config->debug_level; - - env->pc = pc; - env->sregs[DEBUGCAUSE] = cause; - env->sregs[EPC1 + level - 1] = pc; - env->sregs[EPS2 + level - 2] = env->sregs[PS]; - env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | PS_EXCM | - (level << PS_INTLEVEL_SHIFT); - HELPER(exception)(env, EXC_DEBUG); -} - -static void copy_window_from_phys(CPUXtensaState *env, - uint32_t window, uint32_t phys, uint32_t n) -{ - assert(phys < env->config->nareg); - if (phys + n <= env->config->nareg) { - memcpy(env->regs + window, env->phys_regs + phys, - n * sizeof(uint32_t)); - } else { - uint32_t n1 = env->config->nareg - phys; - memcpy(env->regs + window, env->phys_regs + phys, - n1 * sizeof(uint32_t)); - memcpy(env->regs + window + n1, env->phys_regs, - (n - n1) * sizeof(uint32_t)); - } -} - -static void copy_phys_from_window(CPUXtensaState *env, - uint32_t phys, uint32_t window, uint32_t n) -{ - assert(phys < env->config->nareg); - if (phys + n <= env->config->nareg) { - memcpy(env->phys_regs + phys, env->regs + window, - n * sizeof(uint32_t)); - } else { - uint32_t n1 = env->config->nareg - phys; - memcpy(env->phys_regs + phys, env->regs + window, - n1 * sizeof(uint32_t)); - memcpy(env->phys_regs, env->regs + window + n1, - (n - n1) * sizeof(uint32_t)); - } -} - - -static inline unsigned windowbase_bound(unsigned a, const CPUXtensaState *env) -{ - return a & (env->config->nareg / 4 - 1); -} - -static inline unsigned windowstart_bit(unsigned a, const CPUXtensaState *env) -{ - return 1 << windowbase_bound(a, env); -} - -void xtensa_sync_window_from_phys(CPUXtensaState *env) -{ - copy_window_from_phys(env, 0, env->sregs[WINDOW_BASE] * 4, 16); -} - -void xtensa_sync_phys_from_window(CPUXtensaState *env) -{ - copy_phys_from_window(env, env->sregs[WINDOW_BASE] * 4, 0, 16); -} - -static void xtensa_rotate_window_abs(CPUXtensaState *env, uint32_t position) -{ - xtensa_sync_phys_from_window(env); - env->sregs[WINDOW_BASE] = windowbase_bound(position, env); - xtensa_sync_window_from_phys(env); -} - -void xtensa_rotate_window(CPUXtensaState *env, uint32_t delta) -{ - xtensa_rotate_window_abs(env, env->sregs[WINDOW_BASE] + delta); -} - -void HELPER(wsr_windowbase)(CPUXtensaState *env, uint32_t v) -{ - xtensa_rotate_window_abs(env, v); -} - -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_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) >> - (env->sregs[WINDOW_BASE] + 1); - - if (windowstart & ((1 << callinc) - 1)) { - HELPER(window_check)(env, pc, callinc); - } - env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - imm; - xtensa_rotate_window(env, callinc); - env->sregs[WINDOW_START] |= - windowstart_bit(env->sregs[WINDOW_BASE], env); - } -} - -void HELPER(window_check)(CPUXtensaState *env, uint32_t pc, uint32_t w) -{ - uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); - uint32_t windowstart = xtensa_replicate_windowstart(env) >> - (env->sregs[WINDOW_BASE] + 1); - uint32_t n = ctz32(windowstart) + 1; - - assert(n <= w); - - xtensa_rotate_window(env, n); - env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | - (windowbase << PS_OWB_SHIFT) | PS_EXCM; - env->sregs[EPC1] = env->pc = pc; - - switch (ctz32(windowstart >> n)) { - case 0: - HELPER(exception)(env, EXC_WINDOW_OVERFLOW4); - break; - case 1: - HELPER(exception)(env, EXC_WINDOW_OVERFLOW8); - break; - default: - HELPER(exception)(env, EXC_WINDOW_OVERFLOW12); - break; - } -} - -uint32_t HELPER(retw)(CPUXtensaState *env, uint32_t pc) -{ - int n = (env->regs[0] >> 30) & 0x3; - int m = 0; - uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); - uint32_t windowstart = env->sregs[WINDOW_START]; - uint32_t ret_pc = 0; - - if (windowstart & windowstart_bit(windowbase - 1, env)) { - m = 1; - } else if (windowstart & windowstart_bit(windowbase - 2, env)) { - m = 2; - } else if (windowstart & windowstart_bit(windowbase - 3, env)) { - m = 3; - } - - if (n == 0 || (m != 0 && m != n) || - ((env->sregs[PS] & (PS_WOE | PS_EXCM)) ^ PS_WOE) != 0) { - 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; - - ret_pc = (pc & 0xc0000000) | (env->regs[0] & 0x3fffffff); - - xtensa_rotate_window(env, -n); - if (windowstart & windowstart_bit(env->sregs[WINDOW_BASE], env)) { - env->sregs[WINDOW_START] &= ~windowstart_bit(owb, env); - } else { - /* window underflow */ - env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | - (windowbase << PS_OWB_SHIFT) | PS_EXCM; - env->sregs[EPC1] = env->pc = pc; - - if (n == 1) { - HELPER(exception)(env, EXC_WINDOW_UNDERFLOW4); - } else if (n == 2) { - HELPER(exception)(env, EXC_WINDOW_UNDERFLOW8); - } else if (n == 3) { - HELPER(exception)(env, EXC_WINDOW_UNDERFLOW12); - } - } - } - return ret_pc; -} - -void HELPER(rotw)(CPUXtensaState *env, uint32_t imm4) -{ - xtensa_rotate_window(env, imm4); -} - -void xtensa_restore_owb(CPUXtensaState *env) -{ - xtensa_rotate_window_abs(env, (env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT); -} - -void HELPER(restore_owb)(CPUXtensaState *env) -{ - xtensa_restore_owb(env); -} - -void HELPER(movsp)(CPUXtensaState *env, uint32_t pc) -{ - if ((env->sregs[WINDOW_START] & - (windowstart_bit(env->sregs[WINDOW_BASE] - 3, env) | - windowstart_bit(env->sregs[WINDOW_BASE] - 2, env) | - windowstart_bit(env->sregs[WINDOW_BASE] - 1, env))) == 0) { - HELPER(exception_cause)(env, pc, ALLOCA_CAUSE); - } -} - -void HELPER(wsr_lbeg)(CPUXtensaState *env, uint32_t v) -{ - if (env->sregs[LBEG] != v) { - tb_invalidate_virtual_addr(env, env->sregs[LEND] - 1); - env->sregs[LBEG] = v; - } -} - -void HELPER(wsr_lend)(CPUXtensaState *env, uint32_t v) -{ - if (env->sregs[LEND] != v) { - tb_invalidate_virtual_addr(env, env->sregs[LEND] - 1); - env->sregs[LEND] = v; - tb_invalidate_virtual_addr(env, env->sregs[LEND] - 1); - } -} - -void HELPER(dump_state)(CPUXtensaState *env) -{ - XtensaCPU *cpu = xtensa_env_get_cpu(env); - - cpu_dump_state(CPU(cpu), stderr, fprintf, 0); -} - -#ifndef CONFIG_USER_ONLY - -void HELPER(waiti)(CPUXtensaState *env, uint32_t pc, uint32_t intlevel) -{ - CPUState *cpu; - - env->pc = pc; - env->sregs[PS] = (env->sregs[PS] & ~PS_INTLEVEL) | - (intlevel << PS_INTLEVEL_SHIFT); - - qemu_mutex_lock_iothread(); - check_interrupts(env); - qemu_mutex_unlock_iothread(); - - if (env->pending_irq_level) { - cpu_loop_exit(CPU(xtensa_env_get_cpu(env))); - return; - } - - cpu = CPU(xtensa_env_get_cpu(env)); - cpu->halted = 1; - HELPER(exception)(env, EXCP_HLT); -} - void HELPER(update_ccount)(CPUXtensaState *env) { uint64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -442,6 +62,8 @@ void HELPER(update_ccompare)(CPUXtensaState *env, uint32_t i) { uint64_t dcc; + atomic_and(&env->sregs[INTSET], + ~(1u << env->config->timerint[i])); HELPER(update_ccount)(env); dcc = (uint64_t)(env->sregs[CCOMPARE + i] - env->sregs[CCOUNT] - 1) + 1; timer_mod(env->ccompare[i].timer, @@ -449,22 +71,6 @@ void HELPER(update_ccompare)(CPUXtensaState *env, uint32_t i) env->yield_needed = 1; } -void HELPER(check_interrupts)(CPUXtensaState *env) -{ - qemu_mutex_lock_iothread(); - check_interrupts(env); - qemu_mutex_unlock_iothread(); -} - -void HELPER(itlb_hit_test)(CPUXtensaState *env, uint32_t vaddr) -{ - /* - * Attempt the memory load; we don't care about the result but - * only the side-effects (ie any MMU or other exception) - */ - cpu_ldub_code_ra(env, vaddr, GETPC()); -} - /*! * Check vaddr accessibility/cache attributes and raise an exception if * specified by the ATOMCTL SR. @@ -548,505 +154,8 @@ void HELPER(wsr_memctl)(CPUXtensaState *env, uint32_t v) env->sregs[MEMCTL] = v & env->config->memctl_mask; } -void HELPER(wsr_rasid)(CPUXtensaState *env, uint32_t v) -{ - XtensaCPU *cpu = xtensa_env_get_cpu(env); - - v = (v & 0xffffff00) | 0x1; - if (v != env->sregs[RASID]) { - env->sregs[RASID] = v; - tlb_flush(CPU(cpu)); - } -} - -static uint32_t get_page_size(const CPUXtensaState *env, bool dtlb, uint32_t way) -{ - uint32_t tlbcfg = env->sregs[dtlb ? DTLBCFG : ITLBCFG]; - - switch (way) { - case 4: - return (tlbcfg >> 16) & 0x3; - - case 5: - return (tlbcfg >> 20) & 0x1; - - case 6: - return (tlbcfg >> 24) & 0x1; - - default: - return 0; - } -} - -/*! - * Get bit mask for the virtual address bits translated by the TLB way - */ -uint32_t xtensa_tlb_get_addr_mask(const CPUXtensaState *env, bool dtlb, uint32_t way) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - bool varway56 = dtlb ? - env->config->dtlb.varway56 : - env->config->itlb.varway56; - - switch (way) { - case 4: - return 0xfff00000 << get_page_size(env, dtlb, way) * 2; - - case 5: - if (varway56) { - return 0xf8000000 << get_page_size(env, dtlb, way); - } else { - return 0xf8000000; - } - - case 6: - if (varway56) { - return 0xf0000000 << (1 - get_page_size(env, dtlb, way)); - } else { - return 0xf0000000; - } - - default: - return 0xfffff000; - } - } else { - return REGION_PAGE_MASK; - } -} - -/*! - * Get bit mask for the 'VPN without index' field. - * See ISA, 4.6.5.6, data format for RxTLB0 - */ -static uint32_t get_vpn_mask(const CPUXtensaState *env, bool dtlb, uint32_t way) -{ - if (way < 4) { - bool is32 = (dtlb ? - env->config->dtlb.nrefillentries : - env->config->itlb.nrefillentries) == 32; - return is32 ? 0xffff8000 : 0xffffc000; - } else if (way == 4) { - return xtensa_tlb_get_addr_mask(env, dtlb, way) << 2; - } else if (way <= 6) { - uint32_t mask = xtensa_tlb_get_addr_mask(env, dtlb, way); - bool varway56 = dtlb ? - env->config->dtlb.varway56 : - env->config->itlb.varway56; - - if (varway56) { - return mask << (way == 5 ? 2 : 3); - } else { - return mask << 1; - } - } else { - return 0xfffff000; - } -} - -/*! - * Split virtual address into VPN (with index) and entry index - * for the given TLB way - */ -void split_tlb_entry_spec_way(const CPUXtensaState *env, uint32_t v, bool dtlb, - uint32_t *vpn, uint32_t wi, uint32_t *ei) -{ - bool varway56 = dtlb ? - env->config->dtlb.varway56 : - env->config->itlb.varway56; - - if (!dtlb) { - wi &= 7; - } - - if (wi < 4) { - bool is32 = (dtlb ? - env->config->dtlb.nrefillentries : - env->config->itlb.nrefillentries) == 32; - *ei = (v >> 12) & (is32 ? 0x7 : 0x3); - } else { - switch (wi) { - case 4: - { - uint32_t eibase = 20 + get_page_size(env, dtlb, wi) * 2; - *ei = (v >> eibase) & 0x3; - } - break; - - case 5: - if (varway56) { - uint32_t eibase = 27 + get_page_size(env, dtlb, wi); - *ei = (v >> eibase) & 0x3; - } else { - *ei = (v >> 27) & 0x1; - } - break; - - case 6: - if (varway56) { - uint32_t eibase = 29 - get_page_size(env, dtlb, wi); - *ei = (v >> eibase) & 0x7; - } else { - *ei = (v >> 28) & 0x1; - } - break; - - default: - *ei = 0; - break; - } - } - *vpn = v & xtensa_tlb_get_addr_mask(env, dtlb, wi); -} - -/*! - * Split TLB address into TLB way, entry index and VPN (with index). - * See ISA, 4.6.5.5 - 4.6.5.8 for the TLB addressing format - */ -static void split_tlb_entry_spec(CPUXtensaState *env, uint32_t v, bool dtlb, - uint32_t *vpn, uint32_t *wi, uint32_t *ei) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - *wi = v & (dtlb ? 0xf : 0x7); - split_tlb_entry_spec_way(env, v, dtlb, vpn, *wi, ei); - } else { - *vpn = v & REGION_PAGE_MASK; - *wi = 0; - *ei = (v >> 29) & 0x7; - } -} - -static xtensa_tlb_entry *get_tlb_entry(CPUXtensaState *env, - uint32_t v, bool dtlb, uint32_t *pwi) -{ - uint32_t vpn; - uint32_t wi; - uint32_t ei; - - split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); - if (pwi) { - *pwi = wi; - } - return xtensa_tlb_get_entry(env, dtlb, wi, ei); -} - -uint32_t HELPER(rtlb0)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - uint32_t wi; - const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); - return (entry->vaddr & get_vpn_mask(env, dtlb, wi)) | entry->asid; - } else { - return v & REGION_PAGE_MASK; - } -} - -uint32_t HELPER(rtlb1)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) -{ - const xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, NULL); - return entry->paddr | entry->attr; -} - -void HELPER(itlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - uint32_t wi; - xtensa_tlb_entry *entry = get_tlb_entry(env, v, dtlb, &wi); - if (entry->variable && entry->asid) { - tlb_flush_page(CPU(xtensa_env_get_cpu(env)), entry->vaddr); - entry->asid = 0; - } - } -} - -uint32_t HELPER(ptlb)(CPUXtensaState *env, uint32_t v, uint32_t dtlb) -{ - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - uint32_t wi; - uint32_t ei; - uint8_t ring; - int res = xtensa_tlb_lookup(env, v, dtlb, &wi, &ei, &ring); - - switch (res) { - case 0: - if (ring >= xtensa_get_ring(env)) { - return (v & 0xfffff000) | wi | (dtlb ? 0x10 : 0x8); - } - break; - - case INST_TLB_MULTI_HIT_CAUSE: - case LOAD_STORE_TLB_MULTI_HIT_CAUSE: - HELPER(exception_cause_vaddr)(env, env->pc, res, v); - break; - } - return 0; - } else { - return (v & REGION_PAGE_MASK) | 0x1; - } -} - -void xtensa_tlb_set_entry_mmu(const CPUXtensaState *env, - xtensa_tlb_entry *entry, bool dtlb, - unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte) -{ - entry->vaddr = vpn; - entry->paddr = pte & xtensa_tlb_get_addr_mask(env, dtlb, wi); - entry->asid = (env->sregs[RASID] >> ((pte >> 1) & 0x18)) & 0xff; - entry->attr = pte & 0xf; -} - -void xtensa_tlb_set_entry(CPUXtensaState *env, bool dtlb, - unsigned wi, unsigned ei, uint32_t vpn, uint32_t pte) -{ - XtensaCPU *cpu = xtensa_env_get_cpu(env); - CPUState *cs = CPU(cpu); - xtensa_tlb_entry *entry = xtensa_tlb_get_entry(env, dtlb, wi, ei); - - if (xtensa_option_enabled(env->config, XTENSA_OPTION_MMU)) { - if (entry->variable) { - if (entry->asid) { - tlb_flush_page(cs, entry->vaddr); - } - xtensa_tlb_set_entry_mmu(env, entry, dtlb, wi, ei, vpn, pte); - tlb_flush_page(cs, entry->vaddr); - } else { - 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); - if (xtensa_option_enabled(env->config, - XTENSA_OPTION_REGION_TRANSLATION)) { - entry->paddr = pte & REGION_PAGE_MASK; - } - entry->attr = pte & 0xf; - } -} - -void HELPER(wtlb)(CPUXtensaState *env, uint32_t p, uint32_t v, uint32_t dtlb) -{ - uint32_t vpn; - uint32_t wi; - uint32_t ei; - split_tlb_entry_spec(env, v, dtlb, &vpn, &wi, &ei); - xtensa_tlb_set_entry(env, dtlb, wi, ei, vpn, p); -} - - -void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v) -{ - uint32_t change = v ^ env->sregs[IBREAKENABLE]; - unsigned i; - - for (i = 0; i < env->config->nibreak; ++i) { - if (change & (1 << i)) { - tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); - } - } - env->sregs[IBREAKENABLE] = v & ((1 << env->config->nibreak) - 1); -} - -void HELPER(wsr_ibreaka)(CPUXtensaState *env, uint32_t i, uint32_t v) -{ - if (env->sregs[IBREAKENABLE] & (1 << i) && env->sregs[IBREAKA + i] != v) { - tb_invalidate_virtual_addr(env, env->sregs[IBREAKA + i]); - tb_invalidate_virtual_addr(env, v); - } - env->sregs[IBREAKA + i] = v; -} - -static void set_dbreak(CPUXtensaState *env, unsigned i, uint32_t dbreaka, - uint32_t dbreakc) -{ - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - int flags = BP_CPU | BP_STOP_BEFORE_ACCESS; - uint32_t mask = dbreakc | ~DBREAKC_MASK; - - if (env->cpu_watchpoint[i]) { - cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[i]); - } - if (dbreakc & DBREAKC_SB) { - flags |= BP_MEM_WRITE; - } - if (dbreakc & DBREAKC_LB) { - flags |= BP_MEM_READ; - } - /* contiguous mask after inversion is one less than some power of 2 */ - if ((~mask + 1) & ~mask) { - 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_mask(LOG_GUEST_ERROR, "Failed to set data breakpoint at 0x%08x/%d\n", - dbreaka & mask, ~mask + 1); - } -} - -void HELPER(wsr_dbreaka)(CPUXtensaState *env, uint32_t i, uint32_t v) -{ - uint32_t dbreakc = env->sregs[DBREAKC + i]; - - if ((dbreakc & DBREAKC_SB_LB) && - env->sregs[DBREAKA + i] != v) { - set_dbreak(env, i, v, dbreakc); - } - env->sregs[DBREAKA + i] = v; -} - -void HELPER(wsr_dbreakc)(CPUXtensaState *env, uint32_t i, uint32_t v) -{ - if ((env->sregs[DBREAKC + i] ^ v) & (DBREAKC_SB_LB | DBREAKC_MASK)) { - if (v & DBREAKC_SB_LB) { - set_dbreak(env, i, env->sregs[DBREAKA + i], v); - } else { - if (env->cpu_watchpoint[i]) { - CPUState *cs = CPU(xtensa_env_get_cpu(env)); - - cpu_watchpoint_remove_by_ref(cs, env->cpu_watchpoint[i]); - env->cpu_watchpoint[i] = NULL; - } - } - } - env->sregs[DBREAKC + i] = v; -} #endif -void HELPER(wur_fcr)(CPUXtensaState *env, uint32_t v) -{ - static const int rounding_mode[] = { - float_round_nearest_even, - float_round_to_zero, - float_round_up, - float_round_down, - }; - - env->uregs[FCR] = v & 0xfffff07f; - set_float_rounding_mode(rounding_mode[v & 3], &env->fp_status); -} - -float32 HELPER(abs_s)(float32 v) -{ - return float32_abs(v); -} - -float32 HELPER(neg_s)(float32 v) -{ - return float32_chs(v); -} - -float32 HELPER(add_s)(CPUXtensaState *env, float32 a, float32 b) -{ - return float32_add(a, b, &env->fp_status); -} - -float32 HELPER(sub_s)(CPUXtensaState *env, float32 a, float32 b) -{ - return float32_sub(a, b, &env->fp_status); -} - -float32 HELPER(mul_s)(CPUXtensaState *env, float32 a, float32 b) -{ - return float32_mul(a, b, &env->fp_status); -} - -float32 HELPER(madd_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) -{ - return float32_muladd(b, c, a, 0, - &env->fp_status); -} - -float32 HELPER(msub_s)(CPUXtensaState *env, float32 a, float32 b, float32 c) -{ - return float32_muladd(b, c, a, float_muladd_negate_product, - &env->fp_status); -} - -uint32_t HELPER(ftoi)(float32 v, uint32_t rounding_mode, uint32_t scale) -{ - float_status fp_status = {0}; - - set_float_rounding_mode(rounding_mode, &fp_status); - return float32_to_int32( - float32_scalbn(v, scale, &fp_status), &fp_status); -} - -uint32_t HELPER(ftoui)(float32 v, uint32_t rounding_mode, uint32_t scale) -{ - float_status fp_status = {0}; - float32 res; - - set_float_rounding_mode(rounding_mode, &fp_status); - - res = float32_scalbn(v, scale, &fp_status); - - if (float32_is_neg(v) && !float32_is_any_nan(v)) { - return float32_to_int32(res, &fp_status); - } else { - return float32_to_uint32(res, &fp_status); - } -} - -float32 HELPER(itof)(CPUXtensaState *env, uint32_t v, uint32_t scale) -{ - return float32_scalbn(int32_to_float32(v, &env->fp_status), - (int32_t)scale, &env->fp_status); -} - -float32 HELPER(uitof)(CPUXtensaState *env, uint32_t v, uint32_t scale) -{ - return float32_scalbn(uint32_to_float32(v, &env->fp_status), - (int32_t)scale, &env->fp_status); -} - -static inline void set_br(CPUXtensaState *env, bool v, uint32_t br) -{ - if (v) { - env->sregs[BR] |= br; - } else { - env->sregs[BR] &= ~br; - } -} - -void HELPER(un_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - set_br(env, float32_unordered_quiet(a, b, &env->fp_status), br); -} - -void HELPER(oeq_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - set_br(env, float32_eq_quiet(a, b, &env->fp_status), br); -} - -void HELPER(ueq_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - int v = float32_compare_quiet(a, b, &env->fp_status); - set_br(env, v == float_relation_equal || v == float_relation_unordered, br); -} - -void HELPER(olt_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - set_br(env, float32_lt_quiet(a, b, &env->fp_status), br); -} - -void HELPER(ult_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - int v = float32_compare_quiet(a, b, &env->fp_status); - set_br(env, v == float_relation_less || v == float_relation_unordered, br); -} - -void HELPER(ole_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - set_br(env, float32_le_quiet(a, b, &env->fp_status), br); -} - -void HELPER(ule_s)(CPUXtensaState *env, uint32_t br, float32 a, float32 b) -{ - int v = float32_compare_quiet(a, b, &env->fp_status); - set_br(env, v != float_relation_greater, br); -} - uint32_t HELPER(rer)(CPUXtensaState *env, uint32_t addr) { #ifndef CONFIG_USER_ONLY diff --git a/target/xtensa/overlay_tool.h b/target/xtensa/overlay_tool.h index ee37a04a17..ea07576bc9 100644 --- a/target/xtensa/overlay_tool.h +++ b/target/xtensa/overlay_tool.h @@ -377,7 +377,6 @@ static XtensaConfigList node = { \ .config = &core, \ }; \ - xtensa_finalize_config(&core); \ xtensa_register_core(&node); \ } #else @@ -457,6 +456,7 @@ .nareg = XCHAL_NUM_AREGS, \ .ndepc = (XCHAL_XEA_VERSION >= 2), \ .inst_fetch_width = XCHAL_INST_FETCH_WIDTH, \ + .max_insn_size = XCHAL_MAX_INSTRUCTION_SIZE, \ EXCEPTIONS_SECTION, \ INTERRUPTS_SECTION, \ TLB_SECTION, \ diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index d22cdcdb16..65561d2c49 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -53,7 +53,7 @@ struct DisasContext { uint32_t pc; int cring; int ring; - uint32_t lbeg; + uint32_t lbeg_off; uint32_t lend; bool sar_5bit; @@ -62,6 +62,8 @@ struct DisasContext { TCGv_i32 sar_m32; unsigned window; + unsigned callinc; + bool cwoe; bool debug; bool icount; @@ -69,7 +71,7 @@ struct DisasContext { unsigned cpenable; - uint32_t *raw_arg; + uint32_t op_flags; xtensa_insnbuf insnbuf; xtensa_insnbuf slotbuf; }; @@ -77,8 +79,15 @@ struct DisasContext { static TCGv_i32 cpu_pc; static TCGv_i32 cpu_R[16]; static TCGv_i32 cpu_FR[16]; +static TCGv_i32 cpu_MR[4]; +static TCGv_i32 cpu_BR[16]; +static TCGv_i32 cpu_BR4[4]; +static TCGv_i32 cpu_BR8[2]; static TCGv_i32 cpu_SR[256]; static TCGv_i32 cpu_UR[256]; +static TCGv_i32 cpu_windowbase_next; + +static GHashTable *xtensa_regfile_table; #include "exec/gen-icount.h" @@ -125,6 +134,7 @@ static const XtensaReg sregnames[256] = { [MR + 1] = XTENSA_REG("MR1", XTENSA_OPTION_MAC16), [MR + 2] = XTENSA_REG("MR2", XTENSA_OPTION_MAC16), [MR + 3] = XTENSA_REG("MR3", XTENSA_OPTION_MAC16), + [PREFCTL] = XTENSA_REG_BITS("PREFCTL", XTENSA_OPTION_ALL), [WINDOW_BASE] = XTENSA_REG("WINDOW_BASE", XTENSA_OPTION_WINDOWED_REGISTER), [WINDOW_START] = XTENSA_REG("WINDOW_START", XTENSA_OPTION_WINDOWED_REGISTER), @@ -218,6 +228,15 @@ void xtensa_translate_init(void) "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", }; + static const char * const mregnames[] = { + "m0", "m1", "m2", "m3", + }; + static const char * const bregnames[] = { + "b0", "b1", "b2", "b3", + "b4", "b5", "b6", "b7", + "b8", "b9", "b10", "b11", + "b12", "b13", "b14", "b15", + }; int i; cpu_pc = tcg_global_mem_new_i32(cpu_env, @@ -225,14 +244,41 @@ void xtensa_translate_init(void) for (i = 0; i < 16; i++) { cpu_R[i] = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUXtensaState, regs[i]), - regnames[i]); + offsetof(CPUXtensaState, regs[i]), + regnames[i]); } for (i = 0; i < 16; i++) { cpu_FR[i] = tcg_global_mem_new_i32(cpu_env, - offsetof(CPUXtensaState, fregs[i].f32[FP_F32_LOW]), - fregnames[i]); + offsetof(CPUXtensaState, + fregs[i].f32[FP_F32_LOW]), + fregnames[i]); + } + + for (i = 0; i < 4; i++) { + cpu_MR[i] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUXtensaState, + sregs[MR + i]), + mregnames[i]); + } + + for (i = 0; i < 16; i++) { + cpu_BR[i] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUXtensaState, + sregs[BR]), + bregnames[i]); + if (i % 4 == 0) { + cpu_BR4[i / 4] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUXtensaState, + sregs[BR]), + bregnames[i]); + } + if (i % 8 == 0) { + cpu_BR8[i / 8] = tcg_global_mem_new_i32(cpu_env, + offsetof(CPUXtensaState, + sregs[BR]), + bregnames[i]); + } } for (i = 0; i < 256; ++i) { @@ -250,6 +296,31 @@ void xtensa_translate_init(void) uregnames[i].name); } } + + cpu_windowbase_next = + tcg_global_mem_new_i32(cpu_env, + offsetof(CPUXtensaState, windowbase_next), + "windowbase_next"); +} + +void **xtensa_get_regfile_by_name(const char *name) +{ + if (xtensa_regfile_table == NULL) { + xtensa_regfile_table = g_hash_table_new(g_str_hash, g_str_equal); + g_hash_table_insert(xtensa_regfile_table, + (void *)"AR", (void *)cpu_R); + g_hash_table_insert(xtensa_regfile_table, + (void *)"MR", (void *)cpu_MR); + g_hash_table_insert(xtensa_regfile_table, + (void *)"FR", (void *)cpu_FR); + g_hash_table_insert(xtensa_regfile_table, + (void *)"BR", (void *)cpu_BR); + g_hash_table_insert(xtensa_regfile_table, + (void *)"BR4", (void *)cpu_BR4); + g_hash_table_insert(xtensa_regfile_table, + (void *)"BR8", (void *)cpu_BR8); + } + return (void **)g_hash_table_lookup(xtensa_regfile_table, (void *)name); } static inline bool option_enabled(DisasContext *dc, int opt) @@ -349,17 +420,20 @@ static bool gen_check_privilege(DisasContext *dc) return false; } -static bool gen_check_cpenable(DisasContext *dc, unsigned cp) +static bool gen_check_cpenable(DisasContext *dc, uint32_t cp_mask) { - if (option_enabled(dc, XTENSA_OPTION_COPROCESSOR) && - !(dc->cpenable & (1 << cp))) { - gen_exception_cause(dc, COPROCESSOR0_DISABLED + cp); + cp_mask &= ~dc->cpenable; + + if (option_enabled(dc, XTENSA_OPTION_COPROCESSOR) && cp_mask) { + gen_exception_cause(dc, COPROCESSOR0_DISABLED + ctz32(cp_mask)); dc->base.is_jmp = DISAS_NORETURN; return false; } return true; } +static int gen_postprocess(DisasContext *dc, int slot); + static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) { tcg_gen_mov_i32(cpu_pc, dest); @@ -369,6 +443,9 @@ static void gen_jump_slot(DisasContext *dc, TCGv dest, int slot) if (dc->base.singlestep_enabled) { gen_exception(dc, EXCP_DEBUG); } else { + if (dc->op_flags & XTENSA_OP_POSTPROCESS) { + slot = gen_postprocess(dc, slot); + } if (slot >= 0) { tcg_gen_goto_tb(slot); tcg_gen_exit_tb(dc->base.tb, slot); @@ -384,15 +461,19 @@ static void gen_jump(DisasContext *dc, TCGv dest) gen_jump_slot(dc, dest, -1); } +static int adjust_jump_slot(DisasContext *dc, uint32_t dest, int slot) +{ + if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { + return -1; + } else { + return slot; + } +} + static void gen_jumpi(DisasContext *dc, uint32_t dest, int slot) { TCGv_i32 tmp = tcg_const_i32(dest); -#ifndef CONFIG_USER_ONLY - if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { - slot = -1; - } -#endif - gen_jump_slot(dc, tmp, slot); + gen_jump_slot(dc, tmp, adjust_jump_slot(dc, dest, slot)); tcg_temp_free(tmp); } @@ -409,33 +490,18 @@ static void gen_callw_slot(DisasContext *dc, int callinc, TCGv_i32 dest, gen_jump_slot(dc, dest, slot); } -static void gen_callw(DisasContext *dc, int callinc, TCGv_i32 dest) -{ - gen_callw_slot(dc, callinc, dest, -1); -} - -static void gen_callwi(DisasContext *dc, int callinc, uint32_t dest, int slot) -{ - TCGv_i32 tmp = tcg_const_i32(dest); -#ifndef CONFIG_USER_ONLY - if (((dc->base.pc_first ^ dest) & TARGET_PAGE_MASK) != 0) { - slot = -1; - } -#endif - gen_callw_slot(dc, callinc, tmp, slot); - tcg_temp_free(tmp); -} - static bool gen_check_loop_end(DisasContext *dc, int slot) { - if (option_enabled(dc, XTENSA_OPTION_LOOP) && - !(dc->base.tb->flags & XTENSA_TBFLAG_EXCM) && - dc->base.pc_next == dc->lend) { + if (dc->base.pc_next == dc->lend) { TCGLabel *label = gen_new_label(); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_SR[LCOUNT], 0, label); tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_SR[LCOUNT], 1); - gen_jumpi(dc, dc->lbeg, slot); + if (dc->lbeg_off) { + gen_jumpi(dc, dc->base.pc_next - dc->lbeg_off, slot); + } else { + gen_jump(dc, cpu_SR[LBEG]); + } gen_set_label(label); gen_jumpi(dc, dc->base.pc_next, -1); return true; @@ -469,7 +535,7 @@ static void gen_brcondi(DisasContext *dc, TCGCond cond, tcg_temp_free(tmp); } -static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) +static bool check_sr(DisasContext *dc, uint32_t sr, unsigned access) { if (!xtensa_option_bits_enabled(dc->config, sregnames[sr].opt_bits)) { if (sregnames[sr].name) { @@ -477,7 +543,6 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) } else { qemu_log_mask(LOG_UNIMP, "SR %d is not implemented\n", sr); } - gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); return false; } else if (!(sregnames[sr].access & access)) { static const char * const access_text[] = { @@ -488,14 +553,13 @@ static bool gen_check_sr(DisasContext *dc, uint32_t sr, unsigned access) assert(access < ARRAY_SIZE(access_text) && 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; } return true; } #ifndef CONFIG_USER_ONLY -static bool gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr) +static void gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr) { if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); @@ -504,24 +568,21 @@ static bool gen_rsr_ccount(DisasContext *dc, TCGv_i32 d, uint32_t sr) tcg_gen_mov_i32(d, cpu_SR[sr]); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); - return true; } - return false; } -static bool gen_rsr_ptevaddr(DisasContext *dc, TCGv_i32 d, uint32_t sr) +static void gen_rsr_ptevaddr(DisasContext *dc, TCGv_i32 d, uint32_t sr) { tcg_gen_shri_i32(d, cpu_SR[EXCVADDR], 10); tcg_gen_or_i32(d, d, cpu_SR[sr]); tcg_gen_andi_i32(d, d, 0xfffffffc); - return false; } #endif -static bool gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr) +static void gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr) { - static bool (* const rsr_handler[256])(DisasContext *dc, - TCGv_i32 d, uint32_t sr) = { + static void (* const rsr_handler[256])(DisasContext *dc, + TCGv_i32 d, uint32_t sr) = { #ifndef CONFIG_USER_ONLY [CCOUNT] = gen_rsr_ccount, [INTSET] = gen_rsr_ccount, @@ -530,28 +591,13 @@ static bool gen_rsr(DisasContext *dc, TCGv_i32 d, uint32_t sr) }; if (rsr_handler[sr]) { - return rsr_handler[sr](dc, d, sr); + rsr_handler[sr](dc, d, sr); } else { tcg_gen_mov_i32(d, cpu_SR[sr]); - return false; } } -static bool gen_wsr_lbeg(DisasContext *dc, uint32_t sr, TCGv_i32 s) -{ - gen_helper_wsr_lbeg(cpu_env, s); - gen_jumpi_check_loop_end(dc, 0); - return false; -} - -static bool gen_wsr_lend(DisasContext *dc, uint32_t sr, TCGv_i32 s) -{ - gen_helper_wsr_lend(cpu_env, s); - gen_jumpi_check_loop_end(dc, 0); - return false; -} - -static bool gen_wsr_sar(DisasContext *dc, uint32_t sr, TCGv_i32 s) +static void gen_wsr_sar(DisasContext *dc, uint32_t sr, TCGv_i32 s) { tcg_gen_andi_i32(cpu_SR[sr], s, 0x3f); if (dc->sar_m32_5bit) { @@ -559,129 +605,97 @@ static bool gen_wsr_sar(DisasContext *dc, uint32_t sr, TCGv_i32 s) } dc->sar_5bit = false; dc->sar_m32_5bit = false; - return false; } -static bool gen_wsr_br(DisasContext *dc, uint32_t sr, TCGv_i32 s) +static void gen_wsr_br(DisasContext *dc, uint32_t sr, TCGv_i32 s) { tcg_gen_andi_i32(cpu_SR[sr], s, 0xffff); - return false; } -static bool gen_wsr_litbase(DisasContext *dc, uint32_t sr, TCGv_i32 s) +static void gen_wsr_litbase(DisasContext *dc, uint32_t sr, TCGv_i32 s) { tcg_gen_andi_i32(cpu_SR[sr], s, 0xfffff001); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; } -static bool gen_wsr_acchi(DisasContext *dc, uint32_t sr, TCGv_i32 s) +static void gen_wsr_acchi(DisasContext *dc, uint32_t sr, TCGv_i32 s) { tcg_gen_ext8s_i32(cpu_SR[sr], s); - return false; } #ifndef CONFIG_USER_ONLY -static bool gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_windowbase(DisasContext *dc, uint32_t sr, TCGv_i32 v) { - gen_helper_wsr_windowbase(cpu_env, v); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; + tcg_gen_mov_i32(cpu_windowbase_next, v); } -static bool gen_wsr_windowstart(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_windowstart(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, (1 << dc->config->nareg / 4) - 1); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; } -static bool gen_wsr_ptevaddr(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_ptevaddr(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, 0xffc00000); - return false; } -static bool gen_wsr_rasid(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_rasid(DisasContext *dc, uint32_t sr, TCGv_i32 v) { gen_helper_wsr_rasid(cpu_env, v); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; } -static bool gen_wsr_tlbcfg(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_tlbcfg(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, 0x01130000); - return false; } -static bool gen_wsr_ibreakenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_ibreakenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) { gen_helper_wsr_ibreakenable(cpu_env, v); - gen_jumpi_check_loop_end(dc, 0); - return true; } -static bool gen_wsr_memctl(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_memctl(DisasContext *dc, uint32_t sr, TCGv_i32 v) { gen_helper_wsr_memctl(cpu_env, v); - return false; } -static bool gen_wsr_atomctl(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_atomctl(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, 0x3f); - return false; } -static bool gen_wsr_ibreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_ibreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v) { unsigned id = sr - IBREAKA; + TCGv_i32 tmp = tcg_const_i32(id); - if (id < dc->config->nibreak) { - TCGv_i32 tmp = tcg_const_i32(id); - gen_helper_wsr_ibreaka(cpu_env, tmp, v); - tcg_temp_free(tmp); - gen_jumpi_check_loop_end(dc, 0); - return true; - } - return false; + assert(id < dc->config->nibreak); + gen_helper_wsr_ibreaka(cpu_env, tmp, v); + tcg_temp_free(tmp); } -static bool gen_wsr_dbreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_dbreaka(DisasContext *dc, uint32_t sr, TCGv_i32 v) { unsigned id = sr - DBREAKA; + TCGv_i32 tmp = tcg_const_i32(id); - if (id < dc->config->ndbreak) { - TCGv_i32 tmp = tcg_const_i32(id); - gen_helper_wsr_dbreaka(cpu_env, tmp, v); - tcg_temp_free(tmp); - } - return false; + assert(id < dc->config->ndbreak); + gen_helper_wsr_dbreaka(cpu_env, tmp, v); + tcg_temp_free(tmp); } -static bool gen_wsr_dbreakc(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_dbreakc(DisasContext *dc, uint32_t sr, TCGv_i32 v) { unsigned id = sr - DBREAKC; + TCGv_i32 tmp = tcg_const_i32(id); - if (id < dc->config->ndbreak) { - TCGv_i32 tmp = tcg_const_i32(id); - gen_helper_wsr_dbreakc(cpu_env, tmp, v); - tcg_temp_free(tmp); - } - return false; + assert(id < dc->config->ndbreak); + gen_helper_wsr_dbreakc(cpu_env, tmp, v); + tcg_temp_free(tmp); } -static bool gen_wsr_cpenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_cpenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, 0xff); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; } static void gen_check_interrupts(DisasContext *dc) @@ -695,39 +709,22 @@ static void gen_check_interrupts(DisasContext *dc) } } -static bool gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_intset(DisasContext *dc, uint32_t sr, TCGv_i32 v) { - tcg_gen_andi_i32(cpu_SR[sr], v, - dc->config->inttype_mask[INTTYPE_SOFTWARE]); - gen_check_interrupts(dc); - gen_jumpi_check_loop_end(dc, 0); - return true; + gen_helper_intset(cpu_env, v); } -static bool gen_wsr_intclear(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_intclear(DisasContext *dc, uint32_t sr, TCGv_i32 v) { - TCGv_i32 tmp = tcg_temp_new_i32(); - - tcg_gen_andi_i32(tmp, v, - dc->config->inttype_mask[INTTYPE_EDGE] | - dc->config->inttype_mask[INTTYPE_NMI] | - dc->config->inttype_mask[INTTYPE_SOFTWARE]); - tcg_gen_andc_i32(cpu_SR[INTSET], cpu_SR[INTSET], tmp); - tcg_temp_free(tmp); - gen_check_interrupts(dc); - gen_jumpi_check_loop_end(dc, 0); - return true; + gen_helper_intclear(cpu_env, v); } -static bool gen_wsr_intenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_intenable(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_mov_i32(cpu_SR[sr], v); - gen_check_interrupts(dc); - gen_jumpi_check_loop_end(dc, 0); - return true; } -static bool gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) { uint32_t mask = PS_WOE | PS_CALLINC | PS_OWB | PS_UM | PS_EXCM | PS_INTLEVEL; @@ -736,13 +733,9 @@ static bool gen_wsr_ps(DisasContext *dc, uint32_t sr, TCGv_i32 v) mask |= PS_RING; } tcg_gen_andi_i32(cpu_SR[sr], v, mask); - gen_check_interrupts(dc); - /* This can change mmu index and tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; } -static bool gen_wsr_ccount(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_ccount(DisasContext *dc, uint32_t sr, TCGv_i32 v) { if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_start(); @@ -750,53 +743,38 @@ static bool gen_wsr_ccount(DisasContext *dc, uint32_t sr, TCGv_i32 v) gen_helper_wsr_ccount(cpu_env, v); if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { gen_io_end(); - gen_jumpi_check_loop_end(dc, 0); - return true; } - return false; } -static bool gen_wsr_icount(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_icount(DisasContext *dc, uint32_t sr, TCGv_i32 v) { if (dc->icount) { tcg_gen_mov_i32(dc->next_icount, v); } else { tcg_gen_mov_i32(cpu_SR[sr], v); } - return false; } -static bool gen_wsr_icountlevel(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_icountlevel(DisasContext *dc, uint32_t sr, TCGv_i32 v) { tcg_gen_andi_i32(cpu_SR[sr], v, 0xf); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - return true; } -static bool gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) +static void gen_wsr_ccompare(DisasContext *dc, uint32_t sr, TCGv_i32 v) { uint32_t id = sr - CCOMPARE; - bool ret = false; + TCGv_i32 tmp = tcg_const_i32(id); - if (id < dc->config->nccompare) { - uint32_t int_bit = 1 << dc->config->timerint[id]; - TCGv_i32 tmp = tcg_const_i32(id); - - tcg_gen_mov_i32(cpu_SR[sr], v); - tcg_gen_andi_i32(cpu_SR[INTSET], cpu_SR[INTSET], ~int_bit); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_start(); - } - gen_helper_update_ccompare(cpu_env, tmp); - if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { - gen_io_end(); - gen_jumpi_check_loop_end(dc, 0); - ret = true; - } - tcg_temp_free(tmp); + assert(id < dc->config->nccompare); + tcg_gen_mov_i32(cpu_SR[sr], v); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_start(); + } + gen_helper_update_ccompare(cpu_env, tmp); + tcg_temp_free(tmp); + if (tb_cflags(dc->base.tb) & CF_USE_ICOUNT) { + gen_io_end(); } - return ret; } #else static void gen_check_interrupts(DisasContext *dc) @@ -804,12 +782,10 @@ static void gen_check_interrupts(DisasContext *dc) } #endif -static bool gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) +static void gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) { - static bool (* const wsr_handler[256])(DisasContext *dc, - uint32_t sr, TCGv_i32 v) = { - [LBEG] = gen_wsr_lbeg, - [LEND] = gen_wsr_lend, + static void (* const wsr_handler[256])(DisasContext *dc, + uint32_t sr, TCGv_i32 v) = { [SAR] = gen_wsr_sar, [BR] = gen_wsr_br, [LITBASE] = gen_wsr_litbase, @@ -845,10 +821,9 @@ static bool gen_wsr(DisasContext *dc, uint32_t sr, TCGv_i32 s) }; if (wsr_handler[sr]) { - return wsr_handler[sr](dc, sr, s); + wsr_handler[sr](dc, sr, s); } else { tcg_gen_mov_i32(cpu_SR[sr], s); - return false; } } @@ -901,15 +876,16 @@ static void gen_waiti(DisasContext *dc, uint32_t imm4) } tcg_temp_free(pc); tcg_temp_free(intlevel); - gen_jumpi_check_loop_end(dc, 0); } #endif -static bool gen_window_check1(DisasContext *dc, unsigned r1) +static bool gen_window_check(DisasContext *dc, uint32_t mask) { - if (r1 / 4 > dc->window) { + unsigned r = 31 - clz32(mask); + + if (r / 4 > dc->window) { TCGv_i32 pc = tcg_const_i32(dc->pc); - TCGv_i32 w = tcg_const_i32(r1 / 4); + TCGv_i32 w = tcg_const_i32(r / 4); gen_helper_window_check(cpu_env, pc, w); dc->base.is_jmp = DISAS_NORETURN; @@ -918,17 +894,6 @@ static bool gen_window_check1(DisasContext *dc, unsigned r1) return true; } -static bool gen_window_check2(DisasContext *dc, unsigned r1, unsigned r2) -{ - return gen_window_check1(dc, r1 > r2 ? r1 : r2); -} - -static bool gen_window_check3(DisasContext *dc, unsigned r1, unsigned r2, - unsigned r3) -{ - return gen_window_check2(dc, r1, r2 > r3 ? r2 : r3); -} - static TCGv_i32 gen_mac16_m(TCGv_i32 v, bool hi, bool is_unsigned) { TCGv_i32 m = tcg_temp_new_i32(); @@ -941,11 +906,283 @@ static TCGv_i32 gen_mac16_m(TCGv_i32 v, bool hi, bool is_unsigned) return m; } +static void gen_zero_check(DisasContext *dc, const OpcodeArg arg[]) +{ + TCGLabel *label = gen_new_label(); + + tcg_gen_brcondi_i32(TCG_COND_NE, arg[2].in, 0, label); + gen_exception_cause(dc, INTEGER_DIVIDE_BY_ZERO_CAUSE); + gen_set_label(label); +} + static inline unsigned xtensa_op0_insn_len(DisasContext *dc, uint8_t op0) { return xtensa_isa_length_from_chars(dc->config->isa, &op0); } +static int gen_postprocess(DisasContext *dc, int slot) +{ + uint32_t op_flags = dc->op_flags; + + if (op_flags & XTENSA_OP_CHECK_INTERRUPTS) { + gen_check_interrupts(dc); + } + if (op_flags & XTENSA_OP_SYNC_REGISTER_WINDOW) { + gen_helper_sync_windowbase(cpu_env); + } + if (op_flags & XTENSA_OP_EXIT_TB_M1) { + slot = -1; + } + return slot; +} + +struct opcode_arg_copy { + uint32_t resource; + void *temp; + OpcodeArg *arg; +}; + +struct opcode_arg_info { + uint32_t resource; + int index; +}; + +struct slot_prop { + XtensaOpcodeOps *ops; + OpcodeArg arg[MAX_OPCODE_ARGS]; + struct opcode_arg_info in[MAX_OPCODE_ARGS]; + struct opcode_arg_info out[MAX_OPCODE_ARGS]; + unsigned n_in; + unsigned n_out; + uint32_t op_flags; +}; + +enum resource_type { + RES_REGFILE, + RES_STATE, + RES_MAX, +}; + +static uint32_t encode_resource(enum resource_type r, unsigned g, unsigned n) +{ + assert(r < RES_MAX && g < 256 && n < 65536); + return (r << 24) | (g << 16) | n; +} + +static enum resource_type get_resource_type(uint32_t resource) +{ + return resource >> 24; +} + +/* + * a depends on b if b must be executed before a, + * because a's side effects will destroy b's inputs. + */ +static bool op_depends_on(const struct slot_prop *a, + const struct slot_prop *b) +{ + unsigned i = 0; + unsigned j = 0; + + if (a->op_flags & XTENSA_OP_CONTROL_FLOW) { + return true; + } + if ((a->op_flags & XTENSA_OP_LOAD_STORE) < + (b->op_flags & XTENSA_OP_LOAD_STORE)) { + return true; + } + while (i < a->n_out && j < b->n_in) { + if (a->out[i].resource < b->in[j].resource) { + ++i; + } else if (a->out[i].resource > b->in[j].resource) { + ++j; + } else { + return true; + } + } + return false; +} + +/* + * Try to break a dependency on b, append temporary register copy records + * to the end of copy and update n_copy in case of success. + * This is not always possible: e.g. control flow must always be the last, + * load/store must be first and state dependencies are not supported yet. + */ +static bool break_dependency(struct slot_prop *a, + struct slot_prop *b, + struct opcode_arg_copy *copy, + unsigned *n_copy) +{ + unsigned i = 0; + unsigned j = 0; + unsigned n = *n_copy; + bool rv = false; + + if (a->op_flags & XTENSA_OP_CONTROL_FLOW) { + return false; + } + if ((a->op_flags & XTENSA_OP_LOAD_STORE) < + (b->op_flags & XTENSA_OP_LOAD_STORE)) { + return false; + } + while (i < a->n_out && j < b->n_in) { + if (a->out[i].resource < b->in[j].resource) { + ++i; + } else if (a->out[i].resource > b->in[j].resource) { + ++j; + } else { + int index = b->in[j].index; + + if (get_resource_type(a->out[i].resource) != RES_REGFILE || + index < 0) { + return false; + } + copy[n].resource = b->in[j].resource; + copy[n].arg = b->arg + index; + ++n; + ++j; + rv = true; + } + } + *n_copy = n; + return rv; +} + +/* + * Calculate evaluation order for slot opcodes. + * Build opcode order graph and output its nodes in topological sort order. + * An edge a -> b in the graph means that opcode a must be followed by + * opcode b. + */ +static bool tsort(struct slot_prop *slot, + struct slot_prop *sorted[], + unsigned n, + struct opcode_arg_copy *copy, + unsigned *n_copy) +{ + struct tsnode { + unsigned n_in_edge; + unsigned n_out_edge; + unsigned out_edge[MAX_INSN_SLOTS]; + } node[MAX_INSN_SLOTS]; + + unsigned in[MAX_INSN_SLOTS]; + unsigned i, j; + unsigned n_in = 0; + unsigned n_out = 0; + unsigned n_edge = 0; + unsigned in_idx = 0; + unsigned node_idx = 0; + + for (i = 0; i < n; ++i) { + node[i].n_in_edge = 0; + node[i].n_out_edge = 0; + } + + for (i = 0; i < n; ++i) { + unsigned n_out_edge = 0; + + for (j = 0; j < n; ++j) { + if (i != j && op_depends_on(slot + j, slot + i)) { + node[i].out_edge[n_out_edge] = j; + ++node[j].n_in_edge; + ++n_out_edge; + ++n_edge; + } + } + node[i].n_out_edge = n_out_edge; + } + + for (i = 0; i < n; ++i) { + if (!node[i].n_in_edge) { + in[n_in] = i; + ++n_in; + } + } + +again: + for (; in_idx < n_in; ++in_idx) { + i = in[in_idx]; + sorted[n_out] = slot + i; + ++n_out; + for (j = 0; j < node[i].n_out_edge; ++j) { + --n_edge; + if (--node[node[i].out_edge[j]].n_in_edge == 0) { + in[n_in] = node[i].out_edge[j]; + ++n_in; + } + } + } + if (n_edge) { + for (; node_idx < n; ++node_idx) { + struct tsnode *cnode = node + node_idx; + + if (cnode->n_in_edge) { + for (j = 0; j < cnode->n_out_edge; ++j) { + unsigned k = cnode->out_edge[j]; + + if (break_dependency(slot + k, slot + node_idx, + copy, n_copy) && + --node[k].n_in_edge == 0) { + in[n_in] = k; + ++n_in; + --n_edge; + cnode->out_edge[j] = + cnode->out_edge[cnode->n_out_edge - 1]; + --cnode->n_out_edge; + goto again; + } + } + } + } + } + return n_edge == 0; +} + +static void opcode_add_resource(struct slot_prop *op, + uint32_t resource, char direction, + int index) +{ + switch (direction) { + case 'm': + case 'i': + assert(op->n_in < ARRAY_SIZE(op->in)); + op->in[op->n_in].resource = resource; + op->in[op->n_in].index = index; + ++op->n_in; + /* fall through */ + case 'o': + if (direction == 'm' || direction == 'o') { + assert(op->n_out < ARRAY_SIZE(op->out)); + op->out[op->n_out].resource = resource; + op->out[op->n_out].index = index; + ++op->n_out; + } + break; + default: + g_assert_not_reached(); + } +} + +static int resource_compare(const void *a, const void *b) +{ + const struct opcode_arg_info *pa = a; + const struct opcode_arg_info *pb = b; + + return pa->resource < pb->resource ? + -1 : (pa->resource > pb->resource ? 1 : 0); +} + +static int arg_copy_compare(const void *a, const void *b) +{ + const struct opcode_arg_copy *pa = a; + const struct opcode_arg_copy *pb = b; + + return pa->resource < pb->resource ? + -1 : (pa->resource > pb->resource ? 1 : 0); +} + static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) { xtensa_isa isa = dc->config->isa; @@ -954,6 +1191,14 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) xtensa_format fmt; int slot, slots; unsigned i; + uint32_t op_flags = 0; + struct slot_prop slot_prop[MAX_INSN_SLOTS]; + struct slot_prop *ordered[MAX_INSN_SLOTS]; + struct opcode_arg_copy arg_copy[MAX_INSN_SLOTS * MAX_OPCODE_ARGS]; + unsigned n_arg_copy = 0; + uint32_t debug_cause = 0; + uint32_t windowed_register = 0; + uint32_t coprocessor = 0; if (len == XTENSA_UNDEFINED) { qemu_log_mask(LOG_GUEST_ERROR, @@ -964,13 +1209,6 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) } dc->base.pc_next = dc->pc + len; - if (xtensa_option_enabled(dc->config, XTENSA_OPTION_LOOP) && - dc->lbeg == dc->pc && - ((dc->pc ^ (dc->base.pc_next - 1)) & -dc->config->inst_fetch_width)) { - qemu_log_mask(LOG_GUEST_ERROR, - "unaligned first instruction of a loop (pc = %08x)\n", - dc->pc); - } for (i = 1; i < len; ++i) { b[i] = cpu_ldub_code(env, dc->pc + i); } @@ -987,12 +1225,9 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) for (slot = 0; slot < slots; ++slot) { xtensa_opcode opc; int opnd, vopnd, opnds; - uint32_t raw_arg[MAX_OPCODE_ARGS]; - uint32_t arg[MAX_OPCODE_ARGS]; + OpcodeArg *arg = slot_prop[slot].arg; XtensaOpcodeOps *ops; - dc->raw_arg = raw_arg; - xtensa_format_get_slot(isa, fmt, slot, dc->insnbuf, dc->slotbuf); opc = xtensa_opcode_decode(isa, fmt, slot, dc->slotbuf); if (opc == XTENSA_UNDEFINED) { @@ -1005,33 +1240,219 @@ static void disas_xtensa_insn(CPUXtensaState *env, DisasContext *dc) opnds = xtensa_opcode_num_operands(isa, opc); for (opnd = vopnd = 0; opnd < opnds; ++opnd) { + void **register_file = NULL; + + if (xtensa_operand_is_register(isa, opc, opnd)) { + xtensa_regfile rf = xtensa_operand_regfile(isa, opc, opnd); + + register_file = dc->config->regfile[rf]; + + if (rf == dc->config->a_regfile) { + uint32_t v; + + xtensa_operand_get_field(isa, opc, opnd, fmt, slot, + dc->slotbuf, &v); + xtensa_operand_decode(isa, opc, opnd, &v); + windowed_register |= 1u << v; + } + } if (xtensa_operand_is_visible(isa, opc, opnd)) { uint32_t v; xtensa_operand_get_field(isa, opc, opnd, fmt, slot, dc->slotbuf, &v); xtensa_operand_decode(isa, opc, opnd, &v); - raw_arg[vopnd] = v; + arg[vopnd].raw_imm = v; if (xtensa_operand_is_PCrelative(isa, opc, opnd)) { xtensa_operand_undo_reloc(isa, opc, opnd, &v, dc->pc); } - arg[vopnd] = v; + arg[vopnd].imm = v; + if (register_file) { + arg[vopnd].in = register_file[v]; + arg[vopnd].out = register_file[v]; + } ++vopnd; } } ops = dc->config->opcode_ops[opc]; + slot_prop[slot].ops = ops; + if (ops) { - ops->translate(dc, arg, ops->par); + op_flags |= ops->op_flags; } else { - qemu_log_mask(LOG_GUEST_ERROR, + qemu_log_mask(LOG_UNIMP, "unimplemented opcode '%s' in slot %d (pc = %08x)\n", xtensa_opcode_name(isa, opc), slot, dc->pc); + op_flags |= XTENSA_OP_ILL; + } + if ((op_flags & XTENSA_OP_ILL) || + (ops && ops->test_ill && ops->test_ill(dc, arg, ops->par))) { gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); return; } + if (ops->op_flags & XTENSA_OP_DEBUG_BREAK) { + debug_cause |= ops->par[0]; + } + if (ops->test_overflow) { + windowed_register |= ops->test_overflow(dc, arg, ops->par); + } + coprocessor |= ops->coprocessor; + + if (slots > 1) { + slot_prop[slot].n_in = 0; + slot_prop[slot].n_out = 0; + slot_prop[slot].op_flags = ops->op_flags & XTENSA_OP_LOAD_STORE; + + opnds = xtensa_opcode_num_operands(isa, opc); + + for (opnd = vopnd = 0; opnd < opnds; ++opnd) { + bool visible = xtensa_operand_is_visible(isa, opc, opnd); + + if (xtensa_operand_is_register(isa, opc, opnd)) { + xtensa_regfile rf = xtensa_operand_regfile(isa, opc, opnd); + uint32_t v = 0; + + xtensa_operand_get_field(isa, opc, opnd, fmt, slot, + dc->slotbuf, &v); + xtensa_operand_decode(isa, opc, opnd, &v); + opcode_add_resource(slot_prop + slot, + encode_resource(RES_REGFILE, rf, v), + xtensa_operand_inout(isa, opc, opnd), + visible ? vopnd : -1); + } + if (visible) { + ++vopnd; + } + } + + opnds = xtensa_opcode_num_stateOperands(isa, opc); + + for (opnd = 0; opnd < opnds; ++opnd) { + xtensa_state state = xtensa_stateOperand_state(isa, opc, opnd); + + opcode_add_resource(slot_prop + slot, + encode_resource(RES_STATE, 0, state), + xtensa_stateOperand_inout(isa, opc, opnd), + -1); + } + if (xtensa_opcode_is_branch(isa, opc) || + xtensa_opcode_is_jump(isa, opc) || + xtensa_opcode_is_loop(isa, opc) || + xtensa_opcode_is_call(isa, opc)) { + slot_prop[slot].op_flags |= XTENSA_OP_CONTROL_FLOW; + } + + qsort(slot_prop[slot].in, slot_prop[slot].n_in, + sizeof(slot_prop[slot].in[0]), resource_compare); + qsort(slot_prop[slot].out, slot_prop[slot].n_out, + sizeof(slot_prop[slot].out[0]), resource_compare); + } } + + if (slots > 1) { + if (!tsort(slot_prop, ordered, slots, arg_copy, &n_arg_copy)) { + qemu_log_mask(LOG_UNIMP, + "Circular resource dependencies (pc = %08x)\n", + dc->pc); + gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); + return; + } + } else { + ordered[0] = slot_prop + 0; + } + + if ((op_flags & XTENSA_OP_PRIVILEGED) && + !gen_check_privilege(dc)) { + return; + } + + if (op_flags & XTENSA_OP_SYSCALL) { + gen_exception_cause(dc, SYSCALL_CAUSE); + return; + } + + if ((op_flags & XTENSA_OP_DEBUG_BREAK) && dc->debug) { + gen_debug_exception(dc, debug_cause); + return; + } + + if (windowed_register && !gen_window_check(dc, windowed_register)) { + return; + } + + if (op_flags & XTENSA_OP_UNDERFLOW) { + TCGv_i32 tmp = tcg_const_i32(dc->pc); + + gen_helper_test_underflow_retw(cpu_env, tmp); + tcg_temp_free(tmp); + } + + if (op_flags & XTENSA_OP_ALLOCA) { + TCGv_i32 tmp = tcg_const_i32(dc->pc); + + gen_helper_movsp(cpu_env, tmp); + tcg_temp_free(tmp); + } + + if (coprocessor && !gen_check_cpenable(dc, coprocessor)) { + return; + } + + if (n_arg_copy) { + uint32_t resource; + void *temp; + unsigned j; + + qsort(arg_copy, n_arg_copy, sizeof(*arg_copy), arg_copy_compare); + for (i = j = 0; i < n_arg_copy; ++i) { + if (i == 0 || arg_copy[i].resource != resource) { + resource = arg_copy[i].resource; + temp = tcg_temp_local_new(); + tcg_gen_mov_i32(temp, arg_copy[i].arg->in); + arg_copy[i].temp = temp; + + if (i != j) { + arg_copy[j] = arg_copy[i]; + } + ++j; + } + arg_copy[i].arg->in = temp; + } + n_arg_copy = j; + } + + if (op_flags & XTENSA_OP_DIVIDE_BY_ZERO) { + for (slot = 0; slot < slots; ++slot) { + if (slot_prop[slot].ops->op_flags & XTENSA_OP_DIVIDE_BY_ZERO) { + gen_zero_check(dc, slot_prop[slot].arg); + } + } + } + + dc->op_flags = op_flags; + + for (slot = 0; slot < slots; ++slot) { + struct slot_prop *pslot = ordered[slot]; + XtensaOpcodeOps *ops = pslot->ops; + + ops->translate(dc, pslot->arg, ops->par); + } + + for (i = 0; i < n_arg_copy; ++i) { + tcg_temp_free(arg_copy[i].temp); + } + if (dc->base.is_jmp == DISAS_NEXT) { - gen_check_loop_end(dc, 0); + gen_postprocess(dc, 0); + dc->op_flags = 0; + if (op_flags & XTENSA_OP_EXIT_TB_M1) { + /* Change in mmu index, memory mapping or tb->flags; exit tb */ + gen_jumpi_check_loop_end(dc, -1); + } else if (op_flags & XTENSA_OP_EXIT_TB_0) { + gen_jumpi_check_loop_end(dc, 0); + } else { + gen_check_loop_end(dc, 0); + } } dc->pc = dc->base.pc_next; } @@ -1066,14 +1487,19 @@ static void xtensa_tr_init_disas_context(DisasContextBase *dcbase, dc->pc = dc->base.pc_first; dc->ring = tb_flags & XTENSA_TBFLAG_RING_MASK; dc->cring = (tb_flags & XTENSA_TBFLAG_EXCM) ? 0 : dc->ring; - dc->lbeg = env->sregs[LBEG]; - dc->lend = env->sregs[LEND]; + dc->lbeg_off = (dc->base.tb->cs_base & XTENSA_CSBASE_LBEG_OFF_MASK) >> + XTENSA_CSBASE_LBEG_OFF_SHIFT; + dc->lend = (dc->base.tb->cs_base & XTENSA_CSBASE_LEND_MASK) + + (dc->base.pc_first & TARGET_PAGE_MASK); dc->debug = tb_flags & XTENSA_TBFLAG_DEBUG; dc->icount = tb_flags & XTENSA_TBFLAG_ICOUNT; dc->cpenable = (tb_flags & XTENSA_TBFLAG_CPENABLE_MASK) >> XTENSA_TBFLAG_CPENABLE_SHIFT; dc->window = ((tb_flags & XTENSA_TBFLAG_WINDOW_MASK) >> XTENSA_TBFLAG_WINDOW_SHIFT); + dc->cwoe = tb_flags & XTENSA_TBFLAG_CWOE; + dc->callinc = ((tb_flags & XTENSA_TBFLAG_CALLINC_MASK) >> + XTENSA_TBFLAG_CALLINC_SHIFT); if (dc->config->isa) { dc->insnbuf = xtensa_insnbuf_alloc(dc->config->isa); @@ -1278,172 +1704,134 @@ void restore_state_to_opc(CPUXtensaState *env, TranslationBlock *tb, env->pc = data[0]; } -static int compare_opcode_ops(const void *a, const void *b) -{ - return strcmp((const char *)a, - ((const XtensaOpcodeOps *)b)->name); -} - -XtensaOpcodeOps * -xtensa_find_opcode_ops(const XtensaOpcodeTranslators *t, - const char *name) -{ - return bsearch(name, t->opcode, t->num_opcodes, - sizeof(XtensaOpcodeOps), compare_opcode_ops); -} - -static void translate_abs(DisasContext *dc, const uint32_t arg[], +static void translate_abs(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 zero = tcg_const_i32(0); - TCGv_i32 neg = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 neg = tcg_temp_new_i32(); - tcg_gen_neg_i32(neg, cpu_R[arg[1]]); - tcg_gen_movcond_i32(TCG_COND_GE, cpu_R[arg[0]], - cpu_R[arg[1]], zero, cpu_R[arg[1]], neg); - tcg_temp_free(neg); - tcg_temp_free(zero); - } + tcg_gen_neg_i32(neg, arg[1].in); + tcg_gen_movcond_i32(TCG_COND_GE, arg[0].out, + arg[1].in, zero, arg[1].in, neg); + tcg_temp_free(neg); + tcg_temp_free(zero); } -static void translate_add(DisasContext *dc, const uint32_t arg[], +static void translate_add(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_add_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_add_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_addi(DisasContext *dc, const uint32_t arg[], +static void translate_addi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_addi_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2]); - } + tcg_gen_addi_i32(arg[0].out, arg[1].in, arg[2].imm); } -static void translate_addx(DisasContext *dc, const uint32_t arg[], +static void translate_addx(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, cpu_R[arg[1]], par[0]); - tcg_gen_add_i32(cpu_R[arg[0]], tmp, cpu_R[arg[2]]); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, arg[1].in, par[0]); + tcg_gen_add_i32(arg[0].out, tmp, arg[2].in); + tcg_temp_free(tmp); } -static void translate_all(DisasContext *dc, const uint32_t arg[], +static void translate_all(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { uint32_t shift = par[1]; - TCGv_i32 mask = tcg_const_i32(((1 << shift) - 1) << arg[1]); + TCGv_i32 mask = tcg_const_i32(((1 << shift) - 1) << arg[1].imm); TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_and_i32(tmp, cpu_SR[BR], mask); + tcg_gen_and_i32(tmp, arg[1].in, mask); if (par[0]) { - tcg_gen_addi_i32(tmp, tmp, 1 << arg[1]); + tcg_gen_addi_i32(tmp, tmp, 1 << arg[1].imm); } else { tcg_gen_add_i32(tmp, tmp, mask); } - tcg_gen_shri_i32(tmp, tmp, arg[1] + shift); - tcg_gen_deposit_i32(cpu_SR[BR], cpu_SR[BR], - tmp, arg[0], 1); + tcg_gen_shri_i32(tmp, tmp, arg[1].imm + shift); + tcg_gen_deposit_i32(arg[0].out, arg[0].out, + tmp, arg[0].imm, 1); tcg_temp_free(mask); tcg_temp_free(tmp); } -static void translate_and(DisasContext *dc, const uint32_t arg[], +static void translate_and(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_and_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_and_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_ball(DisasContext *dc, const uint32_t arg[], +static void translate_ball(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_and_i32(tmp, cpu_R[arg[0]], cpu_R[arg[1]]); - gen_brcond(dc, par[0], tmp, cpu_R[arg[1]], arg[2]); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_and_i32(tmp, arg[0].in, arg[1].in); + gen_brcond(dc, par[0], tmp, arg[1].in, arg[2].imm); + tcg_temp_free(tmp); } -static void translate_bany(DisasContext *dc, const uint32_t arg[], +static void translate_bany(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_and_i32(tmp, cpu_R[arg[0]], cpu_R[arg[1]]); - gen_brcondi(dc, par[0], tmp, 0, arg[2]); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_and_i32(tmp, arg[0].in, arg[1].in); + gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); + tcg_temp_free(tmp); } -static void translate_b(DisasContext *dc, const uint32_t arg[], +static void translate_b(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - gen_brcond(dc, par[0], cpu_R[arg[0]], cpu_R[arg[1]], arg[2]); - } + gen_brcond(dc, par[0], arg[0].in, arg[1].in, arg[2].imm); } -static void translate_bb(DisasContext *dc, const uint32_t arg[], +static void translate_bb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { #ifdef TARGET_WORDS_BIGENDIAN - TCGv_i32 bit = tcg_const_i32(0x80000000u); + TCGv_i32 bit = tcg_const_i32(0x80000000u); #else - TCGv_i32 bit = tcg_const_i32(0x00000001u); + TCGv_i32 bit = tcg_const_i32(0x00000001u); #endif - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_andi_i32(tmp, cpu_R[arg[1]], 0x1f); + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_andi_i32(tmp, arg[1].in, 0x1f); #ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_shr_i32(bit, bit, tmp); + tcg_gen_shr_i32(bit, bit, tmp); #else - tcg_gen_shl_i32(bit, bit, tmp); + tcg_gen_shl_i32(bit, bit, tmp); #endif - tcg_gen_and_i32(tmp, cpu_R[arg[0]], bit); - gen_brcondi(dc, par[0], tmp, 0, arg[2]); - tcg_temp_free(tmp); - tcg_temp_free(bit); - } + tcg_gen_and_i32(tmp, arg[0].in, bit); + gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); + tcg_temp_free(tmp); + tcg_temp_free(bit); } -static void translate_bbi(DisasContext *dc, const uint32_t arg[], +static void translate_bbi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); #ifdef TARGET_WORDS_BIGENDIAN - tcg_gen_andi_i32(tmp, cpu_R[arg[0]], 0x80000000u >> arg[1]); + tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm); #else - tcg_gen_andi_i32(tmp, cpu_R[arg[0]], 0x00000001u << arg[1]); + tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); #endif - gen_brcondi(dc, par[0], tmp, 0, arg[2]); - tcg_temp_free(tmp); - } + gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); + tcg_temp_free(tmp); } -static void translate_bi(DisasContext *dc, const uint32_t arg[], +static void translate_bi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - gen_brcondi(dc, par[0], cpu_R[arg[0]], arg[1], arg[2]); - } + gen_brcondi(dc, par[0], arg[0].in, arg[1].imm, arg[2].imm); } -static void translate_bz(DisasContext *dc, const uint32_t arg[], +static void translate_bz(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - gen_brcondi(dc, par[0], cpu_R[arg[0]], 0, arg[1]); - } + gen_brcondi(dc, par[0], arg[0].in, 0, arg[1].imm); } enum { @@ -1454,7 +1842,7 @@ enum { BOOLEAN_XOR, }; -static void translate_boolean(DisasContext *dc, const uint32_t arg[], +static void translate_boolean(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { static void (* const op[])(TCGv_i32, TCGv_i32, TCGv_i32) = { @@ -1468,285 +1856,249 @@ static void translate_boolean(DisasContext *dc, const uint32_t arg[], TCGv_i32 tmp1 = tcg_temp_new_i32(); TCGv_i32 tmp2 = tcg_temp_new_i32(); - tcg_gen_shri_i32(tmp1, cpu_SR[BR], arg[1]); - tcg_gen_shri_i32(tmp2, cpu_SR[BR], arg[2]); + tcg_gen_shri_i32(tmp1, arg[1].in, arg[1].imm); + tcg_gen_shri_i32(tmp2, arg[2].in, arg[2].imm); op[par[0]](tmp1, tmp1, tmp2); - tcg_gen_deposit_i32(cpu_SR[BR], cpu_SR[BR], tmp1, arg[0], 1); + tcg_gen_deposit_i32(arg[0].out, arg[0].out, tmp1, arg[0].imm, 1); tcg_temp_free(tmp1); tcg_temp_free(tmp2); } -static void translate_bp(DisasContext *dc, const uint32_t arg[], +static void translate_bp(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_andi_i32(tmp, cpu_SR[BR], 1 << arg[0]); - gen_brcondi(dc, par[0], tmp, 0, arg[1]); + tcg_gen_andi_i32(tmp, arg[0].in, 1 << arg[0].imm); + gen_brcondi(dc, par[0], tmp, 0, arg[1].imm); tcg_temp_free(tmp); } -static void translate_break(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (dc->debug) { - gen_debug_exception(dc, par[0]); - } -} - -static void translate_call0(DisasContext *dc, const uint32_t arg[], +static void translate_call0(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); - gen_jumpi(dc, arg[0], 0); + gen_jumpi(dc, arg[0].imm, 0); } -static void translate_callw(DisasContext *dc, const uint32_t arg[], +static void translate_callw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, par[0] << 2)) { - gen_callwi(dc, par[0], arg[0], 0); - } + TCGv_i32 tmp = tcg_const_i32(arg[0].imm); + gen_callw_slot(dc, par[0], tmp, adjust_jump_slot(dc, arg[0].imm, 0)); + tcg_temp_free(tmp); } -static void translate_callx0(DisasContext *dc, const uint32_t arg[], +static void translate_callx0(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp, cpu_R[arg[0]]); - tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); - gen_jump(dc, tmp); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_mov_i32(tmp, arg[0].in); + tcg_gen_movi_i32(cpu_R[0], dc->base.pc_next); + gen_jump(dc, tmp); + tcg_temp_free(tmp); } -static void translate_callxw(DisasContext *dc, const uint32_t arg[], +static void translate_callxw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], par[0] << 2)) { - TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp, cpu_R[arg[0]]); - gen_callw(dc, par[0], tmp); - tcg_temp_free(tmp); - } + tcg_gen_mov_i32(tmp, arg[0].in); + gen_callw_slot(dc, par[0], tmp, -1); + tcg_temp_free(tmp); } -static void translate_clamps(DisasContext *dc, const uint32_t arg[], +static void translate_clamps(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2]); - TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2]) - 1); + TCGv_i32 tmp1 = tcg_const_i32(-1u << arg[2].imm); + TCGv_i32 tmp2 = tcg_const_i32((1 << arg[2].imm) - 1); - tcg_gen_smax_i32(tmp1, tmp1, cpu_R[arg[1]]); - tcg_gen_smin_i32(cpu_R[arg[0]], tmp1, tmp2); - tcg_temp_free(tmp1); - tcg_temp_free(tmp2); - } + tcg_gen_smax_i32(tmp1, tmp1, arg[1].in); + tcg_gen_smin_i32(arg[0].out, tmp1, tmp2); + tcg_temp_free(tmp1); + tcg_temp_free(tmp2); } -static void translate_clrb_expstate(DisasContext *dc, const uint32_t arg[], +static void translate_clrb_expstate(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { /* TODO: GPIO32 may be a part of coprocessor */ - tcg_gen_andi_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], ~(1u << arg[0])); + tcg_gen_andi_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], ~(1u << arg[0].imm)); } -static void translate_const16(DisasContext *dc, const uint32_t arg[], +static void translate_const16(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - TCGv_i32 c = tcg_const_i32(arg[1]); + TCGv_i32 c = tcg_const_i32(arg[1].imm); - tcg_gen_deposit_i32(cpu_R[arg[0]], c, cpu_R[arg[0]], 16, 16); - tcg_temp_free(c); - } + tcg_gen_deposit_i32(arg[0].out, c, arg[0].in, 16, 16); + tcg_temp_free(c); } -/* par[0]: privileged, par[1]: check memory access */ -static void translate_dcache(DisasContext *dc, const uint32_t arg[], +static void translate_dcache(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if ((!par[0] || gen_check_privilege(dc)) && - gen_window_check1(dc, arg[0]) && par[1]) { - TCGv_i32 addr = tcg_temp_new_i32(); - TCGv_i32 res = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 res = tcg_temp_new_i32(); - tcg_gen_addi_i32(addr, cpu_R[arg[0]], arg[1]); - tcg_gen_qemu_ld8u(res, addr, dc->cring); - tcg_temp_free(addr); - tcg_temp_free(res); - } + tcg_gen_addi_i32(addr, arg[0].in, arg[1].imm); + tcg_gen_qemu_ld8u(res, addr, dc->cring); + tcg_temp_free(addr); + tcg_temp_free(res); } -static void translate_depbits(DisasContext *dc, const uint32_t arg[], +static void translate_depbits(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_deposit_i32(cpu_R[arg[1]], cpu_R[arg[1]], cpu_R[arg[0]], - arg[2], arg[3]); + tcg_gen_deposit_i32(arg[1].out, arg[1].in, arg[0].in, + arg[2].imm, arg[3].imm); +} + +static bool test_ill_entry(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + if (arg[0].imm > 3 || !dc->cwoe) { + qemu_log_mask(LOG_GUEST_ERROR, + "Illegal entry instruction(pc = %08x)\n", dc->pc); + return true; + } else { + return false; } } -static void translate_entry(DisasContext *dc, const uint32_t arg[], +static uint32_t test_overflow_entry(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + return 1 << (dc->callinc * 4); +} + +static void translate_entry(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { TCGv_i32 pc = tcg_const_i32(dc->pc); - TCGv_i32 s = tcg_const_i32(arg[0]); - TCGv_i32 imm = tcg_const_i32(arg[1]); + TCGv_i32 s = tcg_const_i32(arg[0].imm); + TCGv_i32 imm = tcg_const_i32(arg[1].imm); gen_helper_entry(cpu_env, pc, s, imm); tcg_temp_free(imm); tcg_temp_free(s); tcg_temp_free(pc); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); } -static void translate_extui(DisasContext *dc, const uint32_t arg[], +static void translate_extui(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - int maskimm = (1 << arg[3]) - 1; + int maskimm = (1 << arg[3].imm) - 1; - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shri_i32(tmp, cpu_R[arg[1]], arg[2]); - tcg_gen_andi_i32(cpu_R[arg[0]], tmp, maskimm); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shri_i32(tmp, arg[1].in, arg[2].imm); + tcg_gen_andi_i32(arg[0].out, tmp, maskimm); + tcg_temp_free(tmp); } -/* par[0]: privileged, par[1]: check memory access */ -static void translate_icache(DisasContext *dc, const uint32_t arg[], +static void translate_icache(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if ((!par[0] || gen_check_privilege(dc)) && - gen_window_check1(dc, arg[0]) && par[1]) { #ifndef CONFIG_USER_ONLY - TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); - tcg_gen_movi_i32(cpu_pc, dc->pc); - tcg_gen_addi_i32(addr, cpu_R[arg[0]], arg[1]); - gen_helper_itlb_hit_test(cpu_env, addr); - tcg_temp_free(addr); + tcg_gen_movi_i32(cpu_pc, dc->pc); + tcg_gen_addi_i32(addr, arg[0].in, arg[1].imm); + gen_helper_itlb_hit_test(cpu_env, addr); + tcg_temp_free(addr); #endif - } } -static void translate_itlb(DisasContext *dc, const uint32_t arg[], +static void translate_itlb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check1(dc, arg[0])) { #ifndef CONFIG_USER_ONLY - TCGv_i32 dtlb = tcg_const_i32(par[0]); + TCGv_i32 dtlb = tcg_const_i32(par[0]); - gen_helper_itlb(cpu_env, cpu_R[arg[0]], dtlb); - /* This could change memory mapping, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - tcg_temp_free(dtlb); + gen_helper_itlb(cpu_env, arg[0].in, dtlb); + tcg_temp_free(dtlb); #endif - } } -static void translate_ill(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); -} - -static void translate_j(DisasContext *dc, const uint32_t arg[], +static void translate_j(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - gen_jumpi(dc, arg[0], 0); + gen_jumpi(dc, arg[0].imm, 0); } -static void translate_jx(DisasContext *dc, const uint32_t arg[], +static void translate_jx(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - gen_jump(dc, cpu_R[arg[0]]); - } + gen_jump(dc, arg[0].in); } -static void translate_l32e(DisasContext *dc, const uint32_t arg[], +static void translate_l32e(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); - tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]); - gen_load_store_alignment(dc, 2, addr, false); - tcg_gen_qemu_ld_tl(cpu_R[arg[0]], addr, dc->ring, MO_TEUL); - tcg_temp_free(addr); - } + tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); + gen_load_store_alignment(dc, 2, addr, false); + tcg_gen_qemu_ld_tl(arg[0].out, addr, dc->ring, MO_TEUL); + tcg_temp_free(addr); } -static void translate_ldst(DisasContext *dc, const uint32_t arg[], +static void translate_ldst(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); - tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]); - if (par[0] & MO_SIZE) { - gen_load_store_alignment(dc, par[0] & MO_SIZE, addr, par[1]); - } - if (par[2]) { - if (par[1]) { - tcg_gen_mb(TCG_BAR_STRL | TCG_MO_ALL); - } - tcg_gen_qemu_st_tl(cpu_R[arg[0]], addr, dc->cring, par[0]); - } else { - tcg_gen_qemu_ld_tl(cpu_R[arg[0]], addr, dc->cring, par[0]); - if (par[1]) { - tcg_gen_mb(TCG_BAR_LDAQ | TCG_MO_ALL); - } - } - tcg_temp_free(addr); + tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); + if (par[0] & MO_SIZE) { + gen_load_store_alignment(dc, par[0] & MO_SIZE, addr, par[1]); } + if (par[2]) { + if (par[1]) { + tcg_gen_mb(TCG_BAR_STRL | TCG_MO_ALL); + } + tcg_gen_qemu_st_tl(arg[0].in, addr, dc->cring, par[0]); + } else { + tcg_gen_qemu_ld_tl(arg[0].out, addr, dc->cring, par[0]); + if (par[1]) { + tcg_gen_mb(TCG_BAR_LDAQ | TCG_MO_ALL); + } + } + tcg_temp_free(addr); } -static void translate_l32r(DisasContext *dc, const uint32_t arg[], +static void translate_l32r(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - TCGv_i32 tmp; + TCGv_i32 tmp; - if (dc->base.tb->flags & XTENSA_TBFLAG_LITBASE) { - tmp = tcg_const_i32(dc->raw_arg[1] - 1); - tcg_gen_add_i32(tmp, cpu_SR[LITBASE], tmp); - } else { - tmp = tcg_const_i32(arg[1]); - } - tcg_gen_qemu_ld32u(cpu_R[arg[0]], tmp, dc->cring); - tcg_temp_free(tmp); + if (dc->base.tb->flags & XTENSA_TBFLAG_LITBASE) { + tmp = tcg_const_i32(arg[1].raw_imm - 1); + tcg_gen_add_i32(tmp, cpu_SR[LITBASE], tmp); + } else { + tmp = tcg_const_i32(arg[1].imm); } + tcg_gen_qemu_ld32u(arg[0].out, tmp, dc->cring); + tcg_temp_free(tmp); } -static void translate_loop(DisasContext *dc, const uint32_t arg[], +static void translate_loop(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - uint32_t lend = arg[1]; - TCGv_i32 tmp = tcg_const_i32(lend); + uint32_t lend = arg[1].imm; - tcg_gen_subi_i32(cpu_SR[LCOUNT], cpu_R[arg[0]], 1); - tcg_gen_movi_i32(cpu_SR[LBEG], dc->base.pc_next); - gen_helper_wsr_lend(cpu_env, tmp); - tcg_temp_free(tmp); + tcg_gen_subi_i32(cpu_SR[LCOUNT], arg[0].in, 1); + tcg_gen_movi_i32(cpu_SR[LBEG], dc->base.pc_next); + tcg_gen_movi_i32(cpu_SR[LEND], lend); - if (par[0] != TCG_COND_NEVER) { - TCGLabel *label = gen_new_label(); - tcg_gen_brcondi_i32(par[0], cpu_R[arg[0]], 0, label); - gen_jumpi(dc, lend, 1); - gen_set_label(label); - } - - gen_jumpi(dc, dc->base.pc_next, 0); + if (par[0] != TCG_COND_NEVER) { + TCGLabel *label = gen_new_label(); + tcg_gen_brcondi_i32(par[0], arg[0].in, 0, label); + gen_jumpi(dc, lend, 1); + gen_set_label(label); } + + gen_jumpi(dc, dc->base.pc_next, 0); } enum { @@ -1767,499 +2119,400 @@ enum { MAC16_XH = 0x2, }; -enum { - MAC16_AA, - MAC16_AD, - MAC16_DA, - MAC16_DD, - - MAC16_XD = 0x1, - MAC16_DX = 0x2, -}; - -static void translate_mac16(DisasContext *dc, const uint32_t arg[], +static void translate_mac16(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { int op = par[0]; - bool is_m1_sr = par[1] & MAC16_DX; - bool is_m2_sr = par[1] & MAC16_XD; - unsigned half = par[2]; - uint32_t ld_offset = par[3]; + unsigned half = par[1]; + uint32_t ld_offset = par[2]; unsigned off = ld_offset ? 2 : 0; - uint32_t ar[3] = {0}; - unsigned n_ar = 0; - - if (op != MAC16_NONE) { - if (!is_m1_sr) { - ar[n_ar++] = arg[off]; - } - if (!is_m2_sr) { - ar[n_ar++] = arg[off + 1]; - } - } + TCGv_i32 vaddr = tcg_temp_new_i32(); + TCGv_i32 mem32 = tcg_temp_new_i32(); if (ld_offset) { - ar[n_ar++] = arg[1]; + tcg_gen_addi_i32(vaddr, arg[1].in, ld_offset); + gen_load_store_alignment(dc, 2, vaddr, false); + tcg_gen_qemu_ld32u(mem32, vaddr, dc->cring); } + if (op != MAC16_NONE) { + TCGv_i32 m1 = gen_mac16_m(arg[off].in, + half & MAC16_HX, op == MAC16_UMUL); + TCGv_i32 m2 = gen_mac16_m(arg[off + 1].in, + half & MAC16_XH, op == MAC16_UMUL); - if (gen_window_check3(dc, ar[0], ar[1], ar[2])) { - TCGv_i32 vaddr = tcg_temp_new_i32(); - TCGv_i32 mem32 = tcg_temp_new_i32(); - - if (ld_offset) { - tcg_gen_addi_i32(vaddr, cpu_R[arg[1]], ld_offset); - gen_load_store_alignment(dc, 2, vaddr, false); - tcg_gen_qemu_ld32u(mem32, vaddr, dc->cring); - } - if (op != MAC16_NONE) { - TCGv_i32 m1 = gen_mac16_m(is_m1_sr ? - cpu_SR[MR + arg[off]] : - cpu_R[arg[off]], - half & MAC16_HX, op == MAC16_UMUL); - TCGv_i32 m2 = gen_mac16_m(is_m2_sr ? - cpu_SR[MR + arg[off + 1]] : - cpu_R[arg[off + 1]], - half & MAC16_XH, op == MAC16_UMUL); - - if (op == MAC16_MUL || op == MAC16_UMUL) { - tcg_gen_mul_i32(cpu_SR[ACCLO], m1, m2); - if (op == MAC16_UMUL) { - tcg_gen_movi_i32(cpu_SR[ACCHI], 0); - } else { - tcg_gen_sari_i32(cpu_SR[ACCHI], cpu_SR[ACCLO], 31); - } + if (op == MAC16_MUL || op == MAC16_UMUL) { + tcg_gen_mul_i32(cpu_SR[ACCLO], m1, m2); + if (op == MAC16_UMUL) { + tcg_gen_movi_i32(cpu_SR[ACCHI], 0); } else { - TCGv_i32 lo = tcg_temp_new_i32(); - TCGv_i32 hi = tcg_temp_new_i32(); - - tcg_gen_mul_i32(lo, m1, m2); - tcg_gen_sari_i32(hi, lo, 31); - if (op == MAC16_MULA) { - tcg_gen_add2_i32(cpu_SR[ACCLO], cpu_SR[ACCHI], - cpu_SR[ACCLO], cpu_SR[ACCHI], - lo, hi); - } else { - tcg_gen_sub2_i32(cpu_SR[ACCLO], cpu_SR[ACCHI], - cpu_SR[ACCLO], cpu_SR[ACCHI], - lo, hi); - } - tcg_gen_ext8s_i32(cpu_SR[ACCHI], cpu_SR[ACCHI]); - - tcg_temp_free_i32(lo); - tcg_temp_free_i32(hi); + tcg_gen_sari_i32(cpu_SR[ACCHI], cpu_SR[ACCLO], 31); } - tcg_temp_free(m1); - tcg_temp_free(m2); + } else { + TCGv_i32 lo = tcg_temp_new_i32(); + TCGv_i32 hi = tcg_temp_new_i32(); + + tcg_gen_mul_i32(lo, m1, m2); + tcg_gen_sari_i32(hi, lo, 31); + if (op == MAC16_MULA) { + tcg_gen_add2_i32(cpu_SR[ACCLO], cpu_SR[ACCHI], + cpu_SR[ACCLO], cpu_SR[ACCHI], + lo, hi); + } else { + tcg_gen_sub2_i32(cpu_SR[ACCLO], cpu_SR[ACCHI], + cpu_SR[ACCLO], cpu_SR[ACCHI], + lo, hi); + } + tcg_gen_ext8s_i32(cpu_SR[ACCHI], cpu_SR[ACCHI]); + + tcg_temp_free_i32(lo); + tcg_temp_free_i32(hi); } - if (ld_offset) { - tcg_gen_mov_i32(cpu_R[arg[1]], vaddr); - tcg_gen_mov_i32(cpu_SR[MR + arg[0]], mem32); - } - tcg_temp_free(vaddr); - tcg_temp_free(mem32); + tcg_temp_free(m1); + tcg_temp_free(m2); } + if (ld_offset) { + tcg_gen_mov_i32(arg[1].out, vaddr); + tcg_gen_mov_i32(cpu_SR[MR + arg[0].imm], mem32); + } + tcg_temp_free(vaddr); + tcg_temp_free(mem32); } -static void translate_memw(DisasContext *dc, const uint32_t arg[], +static void translate_memw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { tcg_gen_mb(TCG_BAR_SC | TCG_MO_ALL); } -static void translate_smin(DisasContext *dc, const uint32_t arg[], +static void translate_smin(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_smin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_smin_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_umin(DisasContext *dc, const uint32_t arg[], +static void translate_umin(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_umin_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_umin_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_smax(DisasContext *dc, const uint32_t arg[], +static void translate_smax(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_smax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_smax_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_umax(DisasContext *dc, const uint32_t arg[], +static void translate_umax(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_umax_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_umax_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_mov(DisasContext *dc, const uint32_t arg[], +static void translate_mov(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_mov_i32(cpu_R[arg[0]], cpu_R[arg[1]]); - } + tcg_gen_mov_i32(arg[0].out, arg[1].in); } -static void translate_movcond(DisasContext *dc, const uint32_t arg[], +static void translate_movcond(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 zero = tcg_const_i32(0); - tcg_gen_movcond_i32(par[0], cpu_R[arg[0]], - cpu_R[arg[2]], zero, cpu_R[arg[1]], cpu_R[arg[0]]); - tcg_temp_free(zero); - } + tcg_gen_movcond_i32(par[0], arg[0].out, + arg[2].in, zero, arg[1].in, arg[0].in); + tcg_temp_free(zero); } -static void translate_movi(DisasContext *dc, const uint32_t arg[], +static void translate_movi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - tcg_gen_movi_i32(cpu_R[arg[0]], arg[1]); - } + tcg_gen_movi_i32(arg[0].out, arg[1].imm); } -static void translate_movp(DisasContext *dc, const uint32_t arg[], +static void translate_movp(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 zero = tcg_const_i32(0); - TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_andi_i32(tmp, cpu_SR[BR], 1 << arg[2]); - tcg_gen_movcond_i32(par[0], - cpu_R[arg[0]], tmp, zero, - cpu_R[arg[1]], cpu_R[arg[0]]); - tcg_temp_free(tmp); - tcg_temp_free(zero); - } + tcg_gen_andi_i32(tmp, arg[2].in, 1 << arg[2].imm); + tcg_gen_movcond_i32(par[0], + arg[0].out, tmp, zero, + arg[1].in, arg[0].in); + tcg_temp_free(tmp); + tcg_temp_free(zero); } -static void translate_movsp(DisasContext *dc, const uint32_t arg[], +static void translate_movsp(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 pc = tcg_const_i32(dc->pc); - gen_helper_movsp(cpu_env, pc); - tcg_gen_mov_i32(cpu_R[arg[0]], cpu_R[arg[1]]); - tcg_temp_free(pc); - } + tcg_gen_mov_i32(arg[0].out, arg[1].in); } -static void translate_mul16(DisasContext *dc, const uint32_t arg[], +static void translate_mul16(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - TCGv_i32 v1 = tcg_temp_new_i32(); - TCGv_i32 v2 = tcg_temp_new_i32(); + TCGv_i32 v1 = tcg_temp_new_i32(); + TCGv_i32 v2 = tcg_temp_new_i32(); - if (par[0]) { - tcg_gen_ext16s_i32(v1, cpu_R[arg[1]]); - tcg_gen_ext16s_i32(v2, cpu_R[arg[2]]); - } else { - tcg_gen_ext16u_i32(v1, cpu_R[arg[1]]); - tcg_gen_ext16u_i32(v2, cpu_R[arg[2]]); - } - tcg_gen_mul_i32(cpu_R[arg[0]], v1, v2); - tcg_temp_free(v2); - tcg_temp_free(v1); + if (par[0]) { + tcg_gen_ext16s_i32(v1, arg[1].in); + tcg_gen_ext16s_i32(v2, arg[2].in); + } else { + tcg_gen_ext16u_i32(v1, arg[1].in); + tcg_gen_ext16u_i32(v2, arg[2].in); } + tcg_gen_mul_i32(arg[0].out, v1, v2); + tcg_temp_free(v2); + tcg_temp_free(v1); } -static void translate_mull(DisasContext *dc, const uint32_t arg[], +static void translate_mull(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_mul_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_mul_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_mulh(DisasContext *dc, const uint32_t arg[], +static void translate_mulh(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - TCGv_i32 lo = tcg_temp_new(); + TCGv_i32 lo = tcg_temp_new(); - if (par[0]) { - tcg_gen_muls2_i32(lo, cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } else { - tcg_gen_mulu2_i32(lo, cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } - tcg_temp_free(lo); + if (par[0]) { + tcg_gen_muls2_i32(lo, arg[0].out, arg[1].in, arg[2].in); + } else { + tcg_gen_mulu2_i32(lo, arg[0].out, arg[1].in, arg[2].in); } + tcg_temp_free(lo); } -static void translate_neg(DisasContext *dc, const uint32_t arg[], +static void translate_neg(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_neg_i32(cpu_R[arg[0]], cpu_R[arg[1]]); - } + tcg_gen_neg_i32(arg[0].out, arg[1].in); } -static void translate_nop(DisasContext *dc, const uint32_t arg[], +static void translate_nop(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { } -static void translate_nsa(DisasContext *dc, const uint32_t arg[], +static void translate_nsa(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_clrsb_i32(cpu_R[arg[0]], cpu_R[arg[1]]); - } + tcg_gen_clrsb_i32(arg[0].out, arg[1].in); } -static void translate_nsau(DisasContext *dc, const uint32_t arg[], +static void translate_nsau(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_clzi_i32(cpu_R[arg[0]], cpu_R[arg[1]], 32); - } + tcg_gen_clzi_i32(arg[0].out, arg[1].in, 32); } -static void translate_or(DisasContext *dc, const uint32_t arg[], +static void translate_or(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_or_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } + tcg_gen_or_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_ptlb(DisasContext *dc, const uint32_t arg[], +static void translate_ptlb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { #ifndef CONFIG_USER_ONLY - TCGv_i32 dtlb = tcg_const_i32(par[0]); + TCGv_i32 dtlb = tcg_const_i32(par[0]); - tcg_gen_movi_i32(cpu_pc, dc->pc); - gen_helper_ptlb(cpu_R[arg[0]], cpu_env, cpu_R[arg[1]], dtlb); - tcg_temp_free(dtlb); + tcg_gen_movi_i32(cpu_pc, dc->pc); + gen_helper_ptlb(arg[0].out, cpu_env, arg[1].in, dtlb); + tcg_temp_free(dtlb); #endif - } } -static void gen_zero_check(DisasContext *dc, const uint32_t arg[]) -{ - TCGLabel *label = gen_new_label(); - - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[arg[2]], 0, label); - gen_exception_cause(dc, INTEGER_DIVIDE_BY_ZERO_CAUSE); - gen_set_label(label); -} - -static void translate_quos(DisasContext *dc, const uint32_t arg[], +static void translate_quos(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - TCGLabel *label1 = gen_new_label(); - TCGLabel *label2 = gen_new_label(); + TCGLabel *label1 = gen_new_label(); + TCGLabel *label2 = gen_new_label(); - gen_zero_check(dc, arg); - - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[arg[1]], 0x80000000, - label1); - tcg_gen_brcondi_i32(TCG_COND_NE, cpu_R[arg[2]], 0xffffffff, - label1); - tcg_gen_movi_i32(cpu_R[arg[0]], - par[0] ? 0x80000000 : 0); - tcg_gen_br(label2); - gen_set_label(label1); - if (par[0]) { - tcg_gen_div_i32(cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]]); - } else { - tcg_gen_rem_i32(cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]]); - } - gen_set_label(label2); + tcg_gen_brcondi_i32(TCG_COND_NE, arg[1].in, 0x80000000, + label1); + tcg_gen_brcondi_i32(TCG_COND_NE, arg[2].in, 0xffffffff, + label1); + tcg_gen_movi_i32(arg[0].out, + par[0] ? 0x80000000 : 0); + tcg_gen_br(label2); + gen_set_label(label1); + if (par[0]) { + tcg_gen_div_i32(arg[0].out, + arg[1].in, arg[2].in); + } else { + tcg_gen_rem_i32(arg[0].out, + arg[1].in, arg[2].in); } + gen_set_label(label2); } -static void translate_quou(DisasContext *dc, const uint32_t arg[], +static void translate_quou(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - gen_zero_check(dc, arg); - if (par[0]) { - tcg_gen_divu_i32(cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]]); - } else { - tcg_gen_remu_i32(cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]]); - } - } + tcg_gen_divu_i32(arg[0].out, + arg[1].in, arg[2].in); } -static void translate_read_impwire(DisasContext *dc, const uint32_t arg[], +static void translate_read_impwire(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - /* TODO: GPIO32 may be a part of coprocessor */ - tcg_gen_movi_i32(cpu_R[arg[0]], 0); - } + /* TODO: GPIO32 may be a part of coprocessor */ + tcg_gen_movi_i32(arg[0].out, 0); } -static void translate_rer(DisasContext *dc, const uint32_t arg[], +static void translate_remu(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + tcg_gen_remu_i32(arg[0].out, + arg[1].in, arg[2].in); +} + +static void translate_rer(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { - gen_helper_rer(cpu_R[arg[0]], cpu_env, cpu_R[arg[1]]); - } + gen_helper_rer(arg[0].out, cpu_env, arg[1].in); } -static void translate_ret(DisasContext *dc, const uint32_t arg[], +static void translate_ret(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { gen_jump(dc, cpu_R[0]); } -static void translate_retw(DisasContext *dc, const uint32_t arg[], +static bool test_ill_retw(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + if (!dc->cwoe) { + qemu_log_mask(LOG_GUEST_ERROR, + "Illegal retw instruction(pc = %08x)\n", dc->pc); + return true; + } else { + TCGv_i32 tmp = tcg_const_i32(dc->pc); + + gen_helper_test_ill_retw(cpu_env, tmp); + tcg_temp_free(tmp); + return false; + } +} + +static void translate_retw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - TCGv_i32 tmp = tcg_const_i32(dc->pc); - gen_helper_retw(tmp, cpu_env, tmp); + TCGv_i32 tmp = tcg_const_i32(1); + tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]); + tcg_gen_andc_i32(cpu_SR[WINDOW_START], + cpu_SR[WINDOW_START], tmp); + tcg_gen_movi_i32(tmp, dc->pc); + tcg_gen_deposit_i32(tmp, tmp, cpu_R[0], 0, 30); + gen_helper_retw(cpu_env, cpu_R[0]); gen_jump(dc, tmp); tcg_temp_free(tmp); } -static void translate_rfde(DisasContext *dc, const uint32_t arg[], +static void translate_rfde(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc)) { - gen_jump(dc, cpu_SR[dc->config->ndepc ? DEPC : EPC1]); - } + gen_jump(dc, cpu_SR[dc->config->ndepc ? DEPC : EPC1]); } -static void translate_rfe(DisasContext *dc, const uint32_t arg[], +static void translate_rfe(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc)) { - tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); - gen_check_interrupts(dc); - gen_jump(dc, cpu_SR[EPC1]); - } + tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); + gen_jump(dc, cpu_SR[EPC1]); } -static void translate_rfi(DisasContext *dc, const uint32_t arg[], +static void translate_rfi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc)) { - tcg_gen_mov_i32(cpu_SR[PS], cpu_SR[EPS2 + arg[0] - 2]); - gen_check_interrupts(dc); - gen_jump(dc, cpu_SR[EPC1 + arg[0] - 1]); - } + tcg_gen_mov_i32(cpu_SR[PS], cpu_SR[EPS2 + arg[0].imm - 2]); + gen_jump(dc, cpu_SR[EPC1 + arg[0].imm - 1]); } -static void translate_rfw(DisasContext *dc, const uint32_t arg[], +static void translate_rfw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc)) { - TCGv_i32 tmp = tcg_const_i32(1); + TCGv_i32 tmp = tcg_const_i32(1); - tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); - tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]); + tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_EXCM); + tcg_gen_shl_i32(tmp, tmp, cpu_SR[WINDOW_BASE]); - if (par[0]) { - tcg_gen_andc_i32(cpu_SR[WINDOW_START], - cpu_SR[WINDOW_START], tmp); - } else { - tcg_gen_or_i32(cpu_SR[WINDOW_START], - cpu_SR[WINDOW_START], tmp); - } - - gen_helper_restore_owb(cpu_env); - gen_check_interrupts(dc); - gen_jump(dc, cpu_SR[EPC1]); - - tcg_temp_free(tmp); + if (par[0]) { + tcg_gen_andc_i32(cpu_SR[WINDOW_START], + cpu_SR[WINDOW_START], tmp); + } else { + tcg_gen_or_i32(cpu_SR[WINDOW_START], + cpu_SR[WINDOW_START], tmp); } + + tcg_temp_free(tmp); + gen_helper_restore_owb(cpu_env); + gen_jump(dc, cpu_SR[EPC1]); } -static void translate_rotw(DisasContext *dc, const uint32_t arg[], +static void translate_rotw(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc)) { - TCGv_i32 tmp = tcg_const_i32(arg[0]); - gen_helper_rotw(cpu_env, tmp); - tcg_temp_free(tmp); - /* This can change tb->flags, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - } + tcg_gen_addi_i32(cpu_windowbase_next, cpu_SR[WINDOW_BASE], arg[0].imm); } -static void translate_rsil(DisasContext *dc, const uint32_t arg[], +static void translate_rsil(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check1(dc, arg[0])) { - tcg_gen_mov_i32(cpu_R[arg[0]], cpu_SR[PS]); - tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_INTLEVEL); - tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], arg[1]); - gen_check_interrupts(dc); - gen_jumpi_check_loop_end(dc, 0); - } + tcg_gen_mov_i32(arg[0].out, cpu_SR[PS]); + tcg_gen_andi_i32(cpu_SR[PS], cpu_SR[PS], ~PS_INTLEVEL); + tcg_gen_ori_i32(cpu_SR[PS], cpu_SR[PS], arg[1].imm); } -static void translate_rsr(DisasContext *dc, const uint32_t arg[], +static bool test_ill_rsr(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + return !check_sr(dc, par[0], SR_R); +} + +static void translate_rsr(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_sr(dc, par[0], SR_R) && - (par[0] < 64 || gen_check_privilege(dc)) && - gen_window_check1(dc, arg[0])) { - if (gen_rsr(dc, cpu_R[arg[0]], par[0])) { - gen_jumpi_check_loop_end(dc, 0); - } - } + gen_rsr(dc, arg[0].out, par[0]); } -static void translate_rtlb(DisasContext *dc, const uint32_t arg[], +static void translate_rtlb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { +#ifndef CONFIG_USER_ONLY static void (* const helper[])(TCGv_i32 r, TCGv_env env, TCGv_i32 a1, TCGv_i32 a2) = { -#ifndef CONFIG_USER_ONLY gen_helper_rtlb0, gen_helper_rtlb1, -#endif }; + TCGv_i32 dtlb = tcg_const_i32(par[0]); - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 dtlb = tcg_const_i32(par[0]); - - helper[par[1]](cpu_R[arg[0]], cpu_env, cpu_R[arg[1]], dtlb); - tcg_temp_free(dtlb); - } + helper[par[1]](arg[0].out, cpu_env, arg[1].in, dtlb); + tcg_temp_free(dtlb); +#endif } -static void translate_rur(DisasContext *dc, const uint32_t arg[], +static void translate_rur(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - if (uregnames[par[0]].name) { - tcg_gen_mov_i32(cpu_R[arg[0]], cpu_UR[par[0]]); - } else { - qemu_log_mask(LOG_UNIMP, "RUR %d not implemented\n", par[0]); - } + if (uregnames[par[0]].name) { + tcg_gen_mov_i32(arg[0].out, cpu_UR[par[0]]); + } else { + qemu_log_mask(LOG_UNIMP, "RUR %d not implemented\n", par[0]); } } -static void translate_setb_expstate(DisasContext *dc, const uint32_t arg[], +static void translate_setb_expstate(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { /* TODO: GPIO32 may be a part of coprocessor */ - tcg_gen_ori_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], 1u << arg[0]); + tcg_gen_ori_i32(cpu_UR[EXPSTATE], cpu_UR[EXPSTATE], 1u << arg[0].imm); } #ifdef CONFIG_USER_ONLY @@ -2276,81 +2529,78 @@ static void gen_check_atomctl(DisasContext *dc, TCGv_i32 addr) } #endif -static void translate_s32c1i(DisasContext *dc, const uint32_t arg[], +static void translate_s32c1i(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 tmp = tcg_temp_local_new_i32(); - TCGv_i32 addr = tcg_temp_local_new_i32(); + TCGv_i32 tmp = tcg_temp_local_new_i32(); + TCGv_i32 addr = tcg_temp_local_new_i32(); - tcg_gen_mov_i32(tmp, cpu_R[arg[0]]); - tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]); - gen_load_store_alignment(dc, 2, addr, true); - gen_check_atomctl(dc, addr); - tcg_gen_atomic_cmpxchg_i32(cpu_R[arg[0]], addr, cpu_SR[SCOMPARE1], - tmp, dc->cring, MO_32); - tcg_temp_free(addr); + tcg_gen_mov_i32(tmp, arg[0].in); + tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); + gen_load_store_alignment(dc, 2, addr, true); + gen_check_atomctl(dc, addr); + tcg_gen_atomic_cmpxchg_i32(arg[0].out, addr, cpu_SR[SCOMPARE1], + tmp, dc->cring, MO_TEUL); + tcg_temp_free(addr); + tcg_temp_free(tmp); +} + +static void translate_s32e(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + TCGv_i32 addr = tcg_temp_new_i32(); + + tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); + gen_load_store_alignment(dc, 2, addr, false); + tcg_gen_qemu_st_tl(arg[0].in, addr, dc->ring, MO_TEUL); + tcg_temp_free(addr); +} + +static void translate_salt(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + tcg_gen_setcond_i32(par[0], + arg[0].out, + arg[1].in, arg[2].in); +} + +static void translate_sext(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + int shift = 31 - arg[2].imm; + + if (shift == 24) { + tcg_gen_ext8s_i32(arg[0].out, arg[1].in); + } else if (shift == 16) { + tcg_gen_ext16s_i32(arg[0].out, arg[1].in); + } else { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, arg[1].in, shift); + tcg_gen_sari_i32(arg[0].out, tmp, shift); tcg_temp_free(tmp); } } -static void translate_s32e(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) +static bool test_ill_simcall(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { - TCGv_i32 addr = tcg_temp_new_i32(); - - tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]); - gen_load_store_alignment(dc, 2, addr, false); - tcg_gen_qemu_st_tl(cpu_R[arg[0]], addr, dc->ring, MO_TEUL); - tcg_temp_free(addr); +#ifdef CONFIG_USER_ONLY + bool ill = true; +#else + bool ill = !semihosting_enabled(); +#endif + if (ill) { + qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); } + return ill; } -static void translate_salt(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_setcond_i32(par[0], - cpu_R[arg[0]], - cpu_R[arg[1]], cpu_R[arg[2]]); - } -} - -static void translate_sext(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check2(dc, arg[0], arg[1])) { - int shift = 31 - arg[2]; - - if (shift == 24) { - tcg_gen_ext8s_i32(cpu_R[arg[0]], cpu_R[arg[1]]); - } else if (shift == 16) { - tcg_gen_ext16s_i32(cpu_R[arg[0]], cpu_R[arg[1]]); - } else { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, cpu_R[arg[1]], shift); - tcg_gen_sari_i32(cpu_R[arg[0]], tmp, shift); - tcg_temp_free(tmp); - } - } -} - -static void translate_simcall(DisasContext *dc, const uint32_t arg[], +static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { #ifndef CONFIG_USER_ONLY - if (semihosting_enabled()) { - if (gen_check_privilege(dc)) { - gen_helper_simcall(cpu_env); - } - } else + gen_helper_simcall(cpu_env); #endif - { - qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); - gen_exception_cause(dc, ILLEGAL_INSTRUCTION_CAUSE); - } } /* @@ -2361,262 +2611,214 @@ static void translate_simcall(DisasContext *dc, const uint32_t arg[], TCGv_i64 tmp = tcg_temp_new_i64(); \ tcg_gen_extu_i32_i64(tmp, reg); \ tcg_gen_##cmd##_i64(v, v, tmp); \ - tcg_gen_extrl_i64_i32(cpu_R[arg[0]], v); \ + tcg_gen_extrl_i64_i32(arg[0].out, v); \ tcg_temp_free_i64(v); \ tcg_temp_free_i64(tmp); \ } while (0) #define gen_shift(cmd) gen_shift_reg(cmd, cpu_SR[SAR]) -static void translate_sll(DisasContext *dc, const uint32_t arg[], +static void translate_sll(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - if (dc->sar_m32_5bit) { - tcg_gen_shl_i32(cpu_R[arg[0]], cpu_R[arg[1]], dc->sar_m32); - } else { - TCGv_i64 v = tcg_temp_new_i64(); - TCGv_i32 s = tcg_const_i32(32); - tcg_gen_sub_i32(s, s, cpu_SR[SAR]); - tcg_gen_andi_i32(s, s, 0x3f); - tcg_gen_extu_i32_i64(v, cpu_R[arg[1]]); - gen_shift_reg(shl, s); - tcg_temp_free(s); - } - } -} - -static void translate_slli(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check2(dc, arg[0], arg[1])) { - if (arg[2] == 32) { - qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined\n", - arg[0], arg[1]); - } - tcg_gen_shli_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2] & 0x1f); - } -} - -static void translate_sra(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check2(dc, arg[0], arg[1])) { - if (dc->sar_m32_5bit) { - tcg_gen_sar_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_SR[SAR]); - } else { - TCGv_i64 v = tcg_temp_new_i64(); - tcg_gen_ext_i32_i64(v, cpu_R[arg[1]]); - gen_shift(sar); - } - } -} - -static void translate_srai(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_sari_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2]); - } -} - -static void translate_src(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { + if (dc->sar_m32_5bit) { + tcg_gen_shl_i32(arg[0].out, arg[1].in, dc->sar_m32); + } else { TCGv_i64 v = tcg_temp_new_i64(); - tcg_gen_concat_i32_i64(v, cpu_R[arg[2]], cpu_R[arg[1]]); - gen_shift(shr); + TCGv_i32 s = tcg_const_i32(32); + tcg_gen_sub_i32(s, s, cpu_SR[SAR]); + tcg_gen_andi_i32(s, s, 0x3f); + tcg_gen_extu_i32_i64(v, arg[1].in); + gen_shift_reg(shl, s); + tcg_temp_free(s); } } -static void translate_srl(DisasContext *dc, const uint32_t arg[], +static void translate_slli(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + if (arg[2].imm == 32) { + qemu_log_mask(LOG_GUEST_ERROR, "slli a%d, a%d, 32 is undefined\n", + arg[0].imm, arg[1].imm); + } + tcg_gen_shli_i32(arg[0].out, arg[1].in, arg[2].imm & 0x1f); +} + +static void translate_sra(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - if (dc->sar_m32_5bit) { - tcg_gen_shr_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_SR[SAR]); - } else { - TCGv_i64 v = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(v, cpu_R[arg[1]]); - gen_shift(shr); - } + if (dc->sar_m32_5bit) { + tcg_gen_sar_i32(arg[0].out, arg[1].in, cpu_SR[SAR]); + } else { + TCGv_i64 v = tcg_temp_new_i64(); + tcg_gen_ext_i32_i64(v, arg[1].in); + gen_shift(sar); + } +} + +static void translate_srai(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + tcg_gen_sari_i32(arg[0].out, arg[1].in, arg[2].imm); +} + +static void translate_src(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + TCGv_i64 v = tcg_temp_new_i64(); + tcg_gen_concat_i32_i64(v, arg[2].in, arg[1].in); + gen_shift(shr); +} + +static void translate_srl(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + if (dc->sar_m32_5bit) { + tcg_gen_shr_i32(arg[0].out, arg[1].in, cpu_SR[SAR]); + } else { + TCGv_i64 v = tcg_temp_new_i64(); + tcg_gen_extu_i32_i64(v, arg[1].in); + gen_shift(shr); } } #undef gen_shift #undef gen_shift_reg -static void translate_srli(DisasContext *dc, const uint32_t arg[], +static void translate_srli(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - tcg_gen_shri_i32(cpu_R[arg[0]], cpu_R[arg[1]], arg[2]); - } + tcg_gen_shri_i32(arg[0].out, arg[1].in, arg[2].imm); } -static void translate_ssa8b(DisasContext *dc, const uint32_t arg[], +static void translate_ssa8b(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, cpu_R[arg[0]], 3); - gen_left_shift_sar(dc, tmp); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, arg[0].in, 3); + gen_left_shift_sar(dc, tmp); + tcg_temp_free(tmp); } -static void translate_ssa8l(DisasContext *dc, const uint32_t arg[], +static void translate_ssa8l(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, cpu_R[arg[0]], 3); - gen_right_shift_sar(dc, tmp); - tcg_temp_free(tmp); - } -} - -static void translate_ssai(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - TCGv_i32 tmp = tcg_const_i32(arg[0]); + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, arg[0].in, 3); gen_right_shift_sar(dc, tmp); tcg_temp_free(tmp); } -static void translate_ssl(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check1(dc, arg[0])) { - gen_left_shift_sar(dc, cpu_R[arg[0]]); - } -} - -static void translate_ssr(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check1(dc, arg[0])) { - gen_right_shift_sar(dc, cpu_R[arg[0]]); - } -} - -static void translate_sub(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_sub_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } -} - -static void translate_subx(DisasContext *dc, const uint32_t arg[], +static void translate_ssai(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_shli_i32(tmp, cpu_R[arg[1]], par[0]); - tcg_gen_sub_i32(cpu_R[arg[0]], tmp, cpu_R[arg[2]]); - tcg_temp_free(tmp); - } + TCGv_i32 tmp = tcg_const_i32(arg[0].imm); + gen_right_shift_sar(dc, tmp); + tcg_temp_free(tmp); } -static void translate_syscall(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) +static void translate_ssl(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) { - gen_exception_cause(dc, SYSCALL_CAUSE); + gen_left_shift_sar(dc, arg[0].in); } -static void translate_waiti(DisasContext *dc, const uint32_t arg[], +static void translate_ssr(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + gen_right_shift_sar(dc, arg[0].in); +} + +static void translate_sub(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + tcg_gen_sub_i32(arg[0].out, arg[1].in, arg[2].in); +} + +static void translate_subx(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_shli_i32(tmp, arg[1].in, par[0]); + tcg_gen_sub_i32(arg[0].out, tmp, arg[2].in); + tcg_temp_free(tmp); +} + +static void translate_waiti(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc)) { #ifndef CONFIG_USER_ONLY - gen_waiti(dc, arg[0]); + gen_waiti(dc, arg[0].imm); #endif - } } -static void translate_wtlb(DisasContext *dc, const uint32_t arg[], +static void translate_wtlb(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { #ifndef CONFIG_USER_ONLY - TCGv_i32 dtlb = tcg_const_i32(par[0]); + TCGv_i32 dtlb = tcg_const_i32(par[0]); - gen_helper_wtlb(cpu_env, cpu_R[arg[0]], cpu_R[arg[1]], dtlb); - /* This could change memory mapping, so exit tb */ - gen_jumpi_check_loop_end(dc, -1); - tcg_temp_free(dtlb); + gen_helper_wtlb(cpu_env, arg[0].in, arg[1].in, dtlb); + tcg_temp_free(dtlb); #endif - } } -static void translate_wer(DisasContext *dc, const uint32_t arg[], +static void translate_wer(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_privilege(dc) && - gen_window_check2(dc, arg[0], arg[1])) { - gen_helper_wer(cpu_env, cpu_R[arg[0]], cpu_R[arg[1]]); - } + gen_helper_wer(cpu_env, arg[0].in, arg[1].in); } -static void translate_wrmsk_expstate(DisasContext *dc, const uint32_t arg[], +static void translate_wrmsk_expstate(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[0], arg[1])) { - /* TODO: GPIO32 may be a part of coprocessor */ - tcg_gen_and_i32(cpu_UR[EXPSTATE], cpu_R[arg[0]], cpu_R[arg[1]]); + /* TODO: GPIO32 may be a part of coprocessor */ + tcg_gen_and_i32(cpu_UR[EXPSTATE], arg[0].in, arg[1].in); +} + +static bool test_ill_wsr(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + return !check_sr(dc, par[0], SR_W); +} + +static void translate_wsr(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + gen_wsr(dc, par[0], arg[0].in); +} + +static void translate_wur(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) +{ + if (uregnames[par[0]].name) { + gen_wur(par[0], arg[0].in); + } else { + qemu_log_mask(LOG_UNIMP, "WUR %d not implemented\n", par[0]); } } -static void translate_wsr(DisasContext *dc, const uint32_t arg[], +static void translate_xor(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_sr(dc, par[0], SR_W) && - (par[0] < 64 || gen_check_privilege(dc)) && - gen_window_check1(dc, arg[0])) { - gen_wsr(dc, par[0], cpu_R[arg[0]]); - } + tcg_gen_xor_i32(arg[0].out, arg[1].in, arg[2].in); } -static void translate_wur(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) +static bool test_ill_xsr(DisasContext *dc, const OpcodeArg arg[], + const uint32_t par[]) { - if (gen_window_check1(dc, arg[0])) { - if (uregnames[par[0]].name) { - gen_wur(par[0], cpu_R[arg[0]]); - } else { - qemu_log_mask(LOG_UNIMP, "WUR %d not implemented\n", par[0]); - } - } + return !check_sr(dc, par[0], SR_X); } -static void translate_xor(DisasContext *dc, const uint32_t arg[], +static void translate_xsr(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check3(dc, arg[0], arg[1], arg[2])) { - tcg_gen_xor_i32(cpu_R[arg[0]], cpu_R[arg[1]], cpu_R[arg[2]]); - } -} + TCGv_i32 tmp = tcg_temp_new_i32(); -static void translate_xsr(DisasContext *dc, const uint32_t arg[], - const uint32_t par[]) -{ - if (gen_check_sr(dc, par[0], SR_X) && - (par[0] < 64 || gen_check_privilege(dc)) && - gen_window_check1(dc, arg[0])) { - TCGv_i32 tmp = tcg_temp_new_i32(); - bool rsr_end, wsr_end; - - tcg_gen_mov_i32(tmp, cpu_R[arg[0]]); - rsr_end = gen_rsr(dc, cpu_R[arg[0]], par[0]); - wsr_end = gen_wsr(dc, par[0], tmp); - tcg_temp_free(tmp); - if (rsr_end && !wsr_end) { - gen_jumpi_check_loop_end(dc, 0); - } - } + tcg_gen_mov_i32(tmp, arg[0].in); + gen_rsr(dc, arg[0].out, par[0]); + gen_wsr(dc, par[0], tmp); + tcg_temp_free(tmp); } static const XtensaOpcodeOps core_ops[] = { @@ -2624,17 +2826,17 @@ static const XtensaOpcodeOps core_ops[] = { .name = "abs", .translate = translate_abs, }, { - .name = "add", + .name = (const char * const[]) { + "add", "add.n", NULL, + }, .translate = translate_add, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "add.n", - .translate = translate_add, - }, { - .name = "addi", - .translate = translate_addi, - }, { - .name = "addi.n", + .name = (const char * const[]) { + "addi", "addi.n", NULL, + }, .translate = translate_addi, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { .name = "addmi", .translate = translate_addi, @@ -2678,121 +2880,187 @@ static const XtensaOpcodeOps core_ops[] = { .translate = translate_all, .par = (const uint32_t[]){false, 8}, }, { - .name = "ball", + .name = (const char * const[]) { + "ball", "ball.w15", "ball.w18", NULL, + }, .translate = translate_ball, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bany", + .name = (const char * const[]) { + "bany", "bany.w15", "bany.w18", NULL, + }, .translate = translate_bany, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bbc", + .name = (const char * const[]) { + "bbc", "bbc.w15", "bbc.w18", NULL, + }, .translate = translate_bb, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bbci", + .name = (const char * const[]) { + "bbci", "bbci.w15", "bbci.w18", NULL, + }, .translate = translate_bbi, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bbs", + .name = (const char * const[]) { + "bbs", "bbs.w15", "bbs.w18", NULL, + }, .translate = translate_bb, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bbsi", + .name = (const char * const[]) { + "bbsi", "bbsi.w15", "bbsi.w18", NULL, + }, .translate = translate_bbi, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "beq", + .name = (const char * const[]) { + "beq", "beq.w15", "beq.w18", NULL, + }, .translate = translate_b, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "beqi", + .name = (const char * const[]) { + "beqi", "beqi.w15", "beqi.w18", NULL, + }, .translate = translate_bi, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "beqz", - .translate = translate_bz, - .par = (const uint32_t[]){TCG_COND_EQ}, - }, { - .name = "beqz.n", + .name = (const char * const[]) { + "beqz", "beqz.n", "beqz.w15", "beqz.w18", NULL, + }, .translate = translate_bz, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { .name = "bf", .translate = translate_bp, .par = (const uint32_t[]){TCG_COND_EQ}, }, { - .name = "bge", + .name = (const char * const[]) { + "bge", "bge.w15", "bge.w18", NULL, + }, .translate = translate_b, .par = (const uint32_t[]){TCG_COND_GE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bgei", + .name = (const char * const[]) { + "bgei", "bgei.w15", "bgei.w18", NULL, + }, .translate = translate_bi, .par = (const uint32_t[]){TCG_COND_GE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bgeu", + .name = (const char * const[]) { + "bgeu", "bgeu.w15", "bgeu.w18", NULL, + }, .translate = translate_b, .par = (const uint32_t[]){TCG_COND_GEU}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bgeui", + .name = (const char * const[]) { + "bgeui", "bgeui.w15", "bgeui.w18", NULL, + }, .translate = translate_bi, .par = (const uint32_t[]){TCG_COND_GEU}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bgez", + .name = (const char * const[]) { + "bgez", "bgez.w15", "bgez.w18", NULL, + }, .translate = translate_bz, .par = (const uint32_t[]){TCG_COND_GE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "blt", + .name = (const char * const[]) { + "blt", "blt.w15", "blt.w18", NULL, + }, .translate = translate_b, .par = (const uint32_t[]){TCG_COND_LT}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "blti", + .name = (const char * const[]) { + "blti", "blti.w15", "blti.w18", NULL, + }, .translate = translate_bi, .par = (const uint32_t[]){TCG_COND_LT}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bltu", + .name = (const char * const[]) { + "bltu", "bltu.w15", "bltu.w18", NULL, + }, .translate = translate_b, .par = (const uint32_t[]){TCG_COND_LTU}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bltui", + .name = (const char * const[]) { + "bltui", "bltui.w15", "bltui.w18", NULL, + }, .translate = translate_bi, .par = (const uint32_t[]){TCG_COND_LTU}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bltz", + .name = (const char * const[]) { + "bltz", "bltz.w15", "bltz.w18", NULL, + }, .translate = translate_bz, .par = (const uint32_t[]){TCG_COND_LT}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bnall", + .name = (const char * const[]) { + "bnall", "bnall.w15", "bnall.w18", NULL, + }, .translate = translate_ball, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bne", + .name = (const char * const[]) { + "bne", "bne.w15", "bne.w18", NULL, + }, .translate = translate_b, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bnei", + .name = (const char * const[]) { + "bnei", "bnei.w15", "bnei.w18", NULL, + }, .translate = translate_bi, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bnez", + .name = (const char * const[]) { + "bnez", "bnez.n", "bnez.w15", "bnez.w18", NULL, + }, .translate = translate_bz, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "bnez.n", - .translate = translate_bz, - .par = (const uint32_t[]){TCG_COND_NE}, - }, { - .name = "bnone", + .name = (const char * const[]) { + "bnone", "bnone.w15", "bnone.w18", NULL, + }, .translate = translate_bany, .par = (const uint32_t[]){TCG_COND_EQ}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { .name = "break", - .translate = translate_break, + .translate = translate_nop, .par = (const uint32_t[]){DEBUGCAUSE_BI}, + .op_flags = XTENSA_OP_DEBUG_BREAK, }, { .name = "break.n", - .translate = translate_break, + .translate = translate_nop, .par = (const uint32_t[]){DEBUGCAUSE_BN}, + .op_flags = XTENSA_OP_DEBUG_BREAK, }, { .name = "bt", .translate = translate_bp, @@ -2842,61 +3110,59 @@ static const XtensaOpcodeOps core_ops[] = { }, { .name = "dhi", .translate = translate_dcache, - .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "dhu", .translate = translate_dcache, - .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "dhwb", .translate = translate_dcache, - .par = (const uint32_t[]){false, true}, }, { .name = "dhwbi", .translate = translate_dcache, - .par = (const uint32_t[]){false, true}, }, { .name = "dii", - .translate = translate_dcache, - .par = (const uint32_t[]){true, false}, + .translate = translate_nop, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "diu", - .translate = translate_dcache, - .par = (const uint32_t[]){true, false}, + .translate = translate_nop, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "diwb", - .translate = translate_dcache, - .par = (const uint32_t[]){true, false}, + .translate = translate_nop, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "diwbi", - .translate = translate_dcache, - .par = (const uint32_t[]){true, false}, + .translate = translate_nop, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "dpfl", .translate = translate_dcache, - .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "dpfr", - .translate = translate_dcache, - .par = (const uint32_t[]){false, false}, + .translate = translate_nop, }, { .name = "dpfro", - .translate = translate_dcache, - .par = (const uint32_t[]){false, false}, + .translate = translate_nop, }, { .name = "dpfw", - .translate = translate_dcache, - .par = (const uint32_t[]){false, false}, + .translate = translate_nop, }, { .name = "dpfwo", - .translate = translate_dcache, - .par = (const uint32_t[]){false, false}, + .translate = translate_nop, }, { .name = "dsync", .translate = translate_nop, }, { .name = "entry", .translate = translate_entry, + .test_ill = test_ill_entry, + .test_overflow = test_overflow_entry, + .op_flags = XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_SYNC_REGISTER_WINDOW, }, { .name = "esync", .translate = translate_nop, @@ -2911,48 +3177,47 @@ static const XtensaOpcodeOps core_ops[] = { .translate = translate_memw, }, { .name = "hwwdtlba", - .translate = translate_ill, + .op_flags = XTENSA_OP_ILL, }, { .name = "hwwitlba", - .translate = translate_ill, + .op_flags = XTENSA_OP_ILL, }, { .name = "idtlb", .translate = translate_itlb, .par = (const uint32_t[]){true}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "ihi", .translate = translate_icache, - .par = (const uint32_t[]){false, true}, }, { .name = "ihu", .translate = translate_icache, - .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "iii", - .translate = translate_icache, - .par = (const uint32_t[]){true, false}, + .translate = translate_nop, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "iitlb", .translate = translate_itlb, .par = (const uint32_t[]){false}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "iiu", - .translate = translate_icache, - .par = (const uint32_t[]){true, false}, + .translate = translate_nop, + .op_flags = XTENSA_OP_PRIVILEGED, }, { - .name = "ill", - .translate = translate_ill, - }, { - .name = "ill.n", - .translate = translate_ill, + .name = (const char * const[]) { + "ill", "ill.n", NULL, + }, + .op_flags = XTENSA_OP_ILL | XTENSA_OP_NAME_ARRAY, }, { .name = "ipf", - .translate = translate_icache, - .par = (const uint32_t[]){false, false}, + .translate = translate_nop, }, { .name = "ipfl", .translate = translate_icache, - .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "isync", .translate = translate_nop, @@ -2966,55 +3231,71 @@ static const XtensaOpcodeOps core_ops[] = { .name = "l16si", .translate = translate_ldst, .par = (const uint32_t[]){MO_TESW, false, false}, + .op_flags = XTENSA_OP_LOAD, }, { .name = "l16ui", .translate = translate_ldst, .par = (const uint32_t[]){MO_TEUW, false, false}, + .op_flags = XTENSA_OP_LOAD, }, { .name = "l32ai", .translate = translate_ldst, .par = (const uint32_t[]){MO_TEUL, true, false}, + .op_flags = XTENSA_OP_LOAD, }, { .name = "l32e", .translate = translate_l32e, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_LOAD, }, { - .name = "l32i", - .translate = translate_ldst, - .par = (const uint32_t[]){MO_TEUL, false, false}, - }, { - .name = "l32i.n", + .name = (const char * const[]) { + "l32i", "l32i.n", NULL, + }, .translate = translate_ldst, .par = (const uint32_t[]){MO_TEUL, false, false}, + .op_flags = XTENSA_OP_NAME_ARRAY | XTENSA_OP_LOAD, }, { .name = "l32r", .translate = translate_l32r, + .op_flags = XTENSA_OP_LOAD, }, { .name = "l8ui", .translate = translate_ldst, .par = (const uint32_t[]){MO_UB, false, false}, + .op_flags = XTENSA_OP_LOAD, }, { .name = "lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_NONE, 0, 0, -4}, + .par = (const uint32_t[]){MAC16_NONE, 0, -4}, + .op_flags = XTENSA_OP_LOAD, }, { .name = "ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_NONE, 0, 0, 4}, + .par = (const uint32_t[]){MAC16_NONE, 0, 4}, + .op_flags = XTENSA_OP_LOAD, }, { .name = "ldpte", - .translate = translate_ill, + .op_flags = XTENSA_OP_ILL, }, { - .name = "loop", + .name = (const char * const[]) { + "loop", "loop.w15", NULL, + }, .translate = translate_loop, .par = (const uint32_t[]){TCG_COND_NEVER}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "loopgtz", + .name = (const char * const[]) { + "loopgtz", "loopgtz.w15", NULL, + }, .translate = translate_loop, .par = (const uint32_t[]){TCG_COND_GT}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "loopnez", + .name = (const char * const[]) { + "loopnez", "loopnez.w15", NULL, + }, .translate = translate_loop, .par = (const uint32_t[]){TCG_COND_NE}, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { .name = "max", .translate = translate_smax, @@ -3031,11 +3312,11 @@ static const XtensaOpcodeOps core_ops[] = { .name = "minu", .translate = translate_umin, }, { - .name = "mov", - .translate = translate_mov, - }, { - .name = "mov.n", + .name = (const char * const[]) { + "mov", "mov.n", NULL, + }, .translate = translate_mov, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { .name = "moveqz", .translate = translate_movcond, @@ -3065,6 +3346,7 @@ static const XtensaOpcodeOps core_ops[] = { }, { .name = "movsp", .translate = translate_movsp, + .op_flags = XTENSA_OP_ALLOCA, }, { .name = "movt", .translate = translate_movp, @@ -3072,67 +3354,67 @@ static const XtensaOpcodeOps core_ops[] = { }, { .name = "mul.aa.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0}, }, { .name = "mul.aa.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0}, }, { .name = "mul.aa.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0}, }, { .name = "mul.aa.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0}, }, { .name = "mul.ad.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0}, }, { .name = "mul.ad.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0}, }, { .name = "mul.ad.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0}, }, { .name = "mul.ad.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_AD, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0}, }, { .name = "mul.da.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0}, }, { .name = "mul.da.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0}, }, { .name = "mul.da.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0}, }, { .name = "mul.da.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0}, }, { .name = "mul.dd.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HH, 0}, }, { .name = "mul.dd.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_HL, 0}, }, { .name = "mul.dd.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LH, 0}, }, { .name = "mul.dd.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MUL, MAC16_DD, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MUL, MAC16_LL, 0}, }, { .name = "mul16s", .translate = translate_mul16, @@ -3144,198 +3426,198 @@ static const XtensaOpcodeOps core_ops[] = { }, { .name = "mula.aa.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0}, }, { .name = "mula.aa.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0}, }, { .name = "mula.aa.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0}, }, { .name = "mula.aa.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0}, }, { .name = "mula.ad.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0}, }, { .name = "mula.ad.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0}, }, { .name = "mula.ad.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0}, }, { .name = "mula.ad.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_AD, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0}, }, { .name = "mula.da.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0}, }, { .name = "mula.da.hh.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HH, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, -4}, }, { .name = "mula.da.hh.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HH, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 4}, }, { .name = "mula.da.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0}, }, { .name = "mula.da.hl.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HL, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, -4}, }, { .name = "mula.da.hl.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_HL, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 4}, }, { .name = "mula.da.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0}, }, { .name = "mula.da.lh.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LH, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, -4}, }, { .name = "mula.da.lh.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LH, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 4}, }, { .name = "mula.da.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0}, }, { .name = "mula.da.ll.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LL, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, -4}, }, { .name = "mula.da.ll.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DA, MAC16_LL, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 4}, }, { .name = "mula.dd.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 0}, }, { .name = "mula.dd.hh.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HH, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, -4}, }, { .name = "mula.dd.hh.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HH, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HH, 4}, }, { .name = "mula.dd.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 0}, }, { .name = "mula.dd.hl.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HL, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, -4}, }, { .name = "mula.dd.hl.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_HL, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_HL, 4}, }, { .name = "mula.dd.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 0}, }, { .name = "mula.dd.lh.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LH, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, -4}, }, { .name = "mula.dd.lh.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LH, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LH, 4}, }, { .name = "mula.dd.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 0}, }, { .name = "mula.dd.ll.lddec", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LL, -4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, -4}, }, { .name = "mula.dd.ll.ldinc", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULA, MAC16_DD, MAC16_LL, 4}, + .par = (const uint32_t[]){MAC16_MULA, MAC16_LL, 4}, }, { .name = "mull", .translate = translate_mull, }, { .name = "muls.aa.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0}, }, { .name = "muls.aa.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0}, }, { .name = "muls.aa.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0}, }, { .name = "muls.aa.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0}, }, { .name = "muls.ad.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0}, }, { .name = "muls.ad.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0}, }, { .name = "muls.ad.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0}, }, { .name = "muls.ad.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_AD, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0}, }, { .name = "muls.da.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0}, }, { .name = "muls.da.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0}, }, { .name = "muls.da.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0}, }, { .name = "muls.da.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0}, }, { .name = "muls.dd.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HH, 0}, }, { .name = "muls.dd.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_HL, 0}, }, { .name = "muls.dd.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LH, 0}, }, { .name = "muls.dd.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_MULS, MAC16_DD, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_MULS, MAC16_LL, 0}, }, { .name = "mulsh", .translate = translate_mulh, @@ -3348,11 +3630,11 @@ static const XtensaOpcodeOps core_ops[] = { .name = "neg", .translate = translate_neg, }, { - .name = "nop", - .translate = translate_nop, - }, { - .name = "nop.n", + .name = (const char * const[]) { + "nop", "nop.n", NULL, + }, .translate = translate_nop, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { .name = "nsa", .translate = translate_nsa, @@ -3374,26 +3656,31 @@ static const XtensaOpcodeOps core_ops[] = { .name = "pdtlb", .translate = translate_ptlb, .par = (const uint32_t[]){true}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "pitlb", .translate = translate_ptlb, .par = (const uint32_t[]){false}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "quos", .translate = translate_quos, .par = (const uint32_t[]){true}, + .op_flags = XTENSA_OP_DIVIDE_BY_ZERO, }, { .name = "quou", .translate = translate_quou, - .par = (const uint32_t[]){true}, + .op_flags = XTENSA_OP_DIVIDE_BY_ZERO, }, { .name = "rdtlb0", .translate = translate_rtlb, .par = (const uint32_t[]){true, 0}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rdtlb1", .translate = translate_rtlb, .par = (const uint32_t[]){true, 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "read_impwire", .translate = translate_read_impwire, @@ -3401,366 +3688,527 @@ static const XtensaOpcodeOps core_ops[] = { .name = "rems", .translate = translate_quos, .par = (const uint32_t[]){false}, + .op_flags = XTENSA_OP_DIVIDE_BY_ZERO, }, { .name = "remu", - .translate = translate_quou, - .par = (const uint32_t[]){false}, + .translate = translate_remu, + .op_flags = XTENSA_OP_DIVIDE_BY_ZERO, }, { .name = "rer", .translate = translate_rer, + .op_flags = XTENSA_OP_PRIVILEGED, }, { - .name = "ret", + .name = (const char * const[]) { + "ret", "ret.n", NULL, + }, .translate = translate_ret, + .op_flags = XTENSA_OP_NAME_ARRAY, }, { - .name = "ret.n", - .translate = translate_ret, - }, { - .name = "retw", - .translate = translate_retw, - }, { - .name = "retw.n", + .name = (const char * const[]) { + "retw", "retw.n", NULL, + }, .translate = translate_retw, + .test_ill = test_ill_retw, + .op_flags = XTENSA_OP_UNDERFLOW | XTENSA_OP_NAME_ARRAY, }, { .name = "rfdd", - .translate = translate_ill, + .op_flags = XTENSA_OP_ILL, }, { .name = "rfde", .translate = translate_rfde, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rfdo", - .translate = translate_ill, + .op_flags = XTENSA_OP_ILL, }, { .name = "rfe", .translate = translate_rfe, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "rfi", .translate = translate_rfi, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "rfwo", .translate = translate_rfw, .par = (const uint32_t[]){true}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "rfwu", .translate = translate_rfw, .par = (const uint32_t[]){false}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "ritlb0", .translate = translate_rtlb, .par = (const uint32_t[]){false, 0}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "ritlb1", .translate = translate_rtlb, .par = (const uint32_t[]){false, 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rotw", .translate = translate_rotw, + .op_flags = XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_SYNC_REGISTER_WINDOW, }, { .name = "rsil", .translate = translate_rsil, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "rsr.176", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){176}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.208", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){208}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.acchi", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){ACCHI}, }, { .name = "rsr.acclo", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){ACCLO}, }, { .name = "rsr.atomctl", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){ATOMCTL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.br", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){BR}, }, { .name = "rsr.cacheattr", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CACHEATTR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ccompare0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CCOMPARE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ccompare1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CCOMPARE + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ccompare2", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CCOMPARE + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ccount", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CCOUNT}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "rsr.configid0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CONFIGID0}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.configid1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CONFIGID1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.cpenable", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){CPENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.dbreaka0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DBREAKA}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.dbreaka1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DBREAKA + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.dbreakc0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DBREAKC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.dbreakc1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DBREAKC + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ddr", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.debugcause", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DEBUGCAUSE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.depc", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DEPC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.dtlbcfg", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){DTLBCFG}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc2", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc3", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc4", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc5", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc6", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.epc7", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPC1 + 6}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.eps2", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPS2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.eps3", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPS2 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.eps4", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPS2 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.eps5", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPS2 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.eps6", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPS2 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.eps7", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EPS2 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.exccause", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCCAUSE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave2", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave3", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave4", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave5", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave6", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excsave7", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCSAVE1 + 6}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.excvaddr", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){EXCVADDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ibreaka0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){IBREAKA}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ibreaka1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){IBREAKA + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ibreakenable", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){IBREAKENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.icount", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){ICOUNT}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.icountlevel", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){ICOUNTLEVEL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.intclear", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){INTCLEAR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.intenable", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){INTENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.interrupt", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){INTSET}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "rsr.intset", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){INTSET}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "rsr.itlbcfg", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){ITLBCFG}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.lbeg", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){LBEG}, }, { .name = "rsr.lcount", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){LCOUNT}, }, { .name = "rsr.lend", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){LEND}, }, { .name = "rsr.litbase", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){LITBASE}, }, { .name = "rsr.m0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MR}, }, { .name = "rsr.m1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MR + 1}, }, { .name = "rsr.m2", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MR + 2}, }, { .name = "rsr.m3", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MR + 3}, }, { .name = "rsr.memctl", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MEMCTL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.misc0", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MISC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.misc1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MISC + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.misc2", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MISC + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.misc3", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){MISC + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, + }, { + .name = "rsr.prefctl", + .translate = translate_rsr, + .test_ill = test_ill_rsr, + .par = (const uint32_t[]){PREFCTL}, }, { .name = "rsr.prid", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){PRID}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ps", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){PS}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.ptevaddr", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){PTEVADDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.rasid", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){RASID}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.sar", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){SAR}, }, { .name = "rsr.scompare1", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){SCOMPARE1}, }, { .name = "rsr.vecbase", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){VECBASE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.windowbase", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){WINDOW_BASE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsr.windowstart", .translate = translate_rsr, + .test_ill = test_ill_rsr, .par = (const uint32_t[]){WINDOW_START}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "rsync", .translate = translate_nop, @@ -3772,10 +4220,12 @@ static const XtensaOpcodeOps core_ops[] = { .name = "rur.fcr", .translate = translate_rur, .par = (const uint32_t[]){FCR}, + .coprocessor = 0x1, }, { .name = "rur.fsr", .translate = translate_rur, .par = (const uint32_t[]){FSR}, + .coprocessor = 0x1, }, { .name = "rur.threadptr", .translate = translate_rur, @@ -3784,32 +4234,32 @@ static const XtensaOpcodeOps core_ops[] = { .name = "s16i", .translate = translate_ldst, .par = (const uint32_t[]){MO_TEUW, false, true}, + .op_flags = XTENSA_OP_STORE, }, { .name = "s32c1i", .translate = translate_s32c1i, + .op_flags = XTENSA_OP_LOAD | XTENSA_OP_STORE, }, { .name = "s32e", .translate = translate_s32e, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_STORE, }, { - .name = "s32i", - .translate = translate_ldst, - .par = (const uint32_t[]){MO_TEUL, false, true}, - }, { - .name = "s32i.n", - .translate = translate_ldst, - .par = (const uint32_t[]){MO_TEUL, false, true}, - }, { - .name = "s32nb", + .name = (const char * const[]) { + "s32i", "s32i.n", "s32nb", NULL, + }, .translate = translate_ldst, .par = (const uint32_t[]){MO_TEUL, false, true}, + .op_flags = XTENSA_OP_NAME_ARRAY | XTENSA_OP_STORE, }, { .name = "s32ri", .translate = translate_ldst, .par = (const uint32_t[]){MO_TEUL, true, true}, + .op_flags = XTENSA_OP_STORE, }, { .name = "s8i", .translate = translate_ldst, .par = (const uint32_t[]){MO_UB, false, true}, + .op_flags = XTENSA_OP_STORE, }, { .name = "salt", .translate = translate_salt, @@ -3827,6 +4277,8 @@ static const XtensaOpcodeOps core_ops[] = { }, { .name = "simcall", .translate = translate_simcall, + .test_ill = test_ill_simcall, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "sll", .translate = translate_sll, @@ -3880,348 +4332,518 @@ static const XtensaOpcodeOps core_ops[] = { .par = (const uint32_t[]){3}, }, { .name = "syscall", - .translate = translate_syscall, + .op_flags = XTENSA_OP_SYSCALL, }, { .name = "umul.aa.hh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_HH, 0}, + .par = (const uint32_t[]){MAC16_UMUL, MAC16_HH, 0}, }, { .name = "umul.aa.hl", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_HL, 0}, + .par = (const uint32_t[]){MAC16_UMUL, MAC16_HL, 0}, }, { .name = "umul.aa.lh", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_LH, 0}, + .par = (const uint32_t[]){MAC16_UMUL, MAC16_LH, 0}, }, { .name = "umul.aa.ll", .translate = translate_mac16, - .par = (const uint32_t[]){MAC16_UMUL, MAC16_AA, MAC16_LL, 0}, + .par = (const uint32_t[]){MAC16_UMUL, MAC16_LL, 0}, }, { .name = "waiti", .translate = translate_waiti, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wdtlb", .translate = translate_wtlb, .par = (const uint32_t[]){true}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "wer", .translate = translate_wer, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "witlb", .translate = translate_wtlb, .par = (const uint32_t[]){false}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "wrmsk_expstate", .translate = translate_wrmsk_expstate, }, { .name = "wsr.176", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){176}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.208", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){208}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.acchi", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){ACCHI}, }, { .name = "wsr.acclo", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){ACCLO}, }, { .name = "wsr.atomctl", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){ATOMCTL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.br", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){BR}, }, { .name = "wsr.cacheattr", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CACHEATTR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.ccompare0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CCOMPARE}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.ccompare1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CCOMPARE + 1}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.ccompare2", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CCOMPARE + 2}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.ccount", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CCOUNT}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.configid0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CONFIGID0}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.configid1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CONFIGID1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.cpenable", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){CPENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "wsr.dbreaka0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DBREAKA}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.dbreaka1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DBREAKA + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.dbreakc0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DBREAKC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.dbreakc1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DBREAKC + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.ddr", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.debugcause", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DEBUGCAUSE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.depc", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DEPC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.dtlbcfg", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){DTLBCFG}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc2", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc3", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc4", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc5", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc6", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.epc7", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPC1 + 6}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.eps2", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPS2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.eps3", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPS2 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.eps4", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPS2 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.eps5", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPS2 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.eps6", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPS2 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.eps7", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EPS2 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.exccause", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCCAUSE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave2", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave3", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave4", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave5", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave6", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excsave7", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCSAVE1 + 6}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.excvaddr", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){EXCVADDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.ibreaka0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){IBREAKA}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.ibreaka1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){IBREAKA + 1}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.ibreakenable", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){IBREAKENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "wsr.icount", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){ICOUNT}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.icountlevel", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){ICOUNTLEVEL}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "wsr.intclear", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){INTCLEAR}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "wsr.intenable", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){INTENABLE}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "wsr.interrupt", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){INTSET}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "wsr.intset", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){INTSET}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "wsr.itlbcfg", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){ITLBCFG}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.lbeg", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){LBEG}, + .op_flags = XTENSA_OP_EXIT_TB_M1, }, { .name = "wsr.lcount", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){LCOUNT}, }, { .name = "wsr.lend", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){LEND}, + .op_flags = XTENSA_OP_EXIT_TB_M1, }, { .name = "wsr.litbase", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){LITBASE}, + .op_flags = XTENSA_OP_EXIT_TB_M1, }, { .name = "wsr.m0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MR}, }, { .name = "wsr.m1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MR + 1}, }, { .name = "wsr.m2", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MR + 2}, }, { .name = "wsr.m3", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MR + 3}, }, { .name = "wsr.memctl", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MEMCTL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.misc0", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MISC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.misc1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MISC + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.misc2", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MISC + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.misc3", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MISC + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.mmid", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){MMID}, + .op_flags = XTENSA_OP_PRIVILEGED, + }, { + .name = "wsr.prefctl", + .translate = translate_wsr, + .test_ill = test_ill_wsr, + .par = (const uint32_t[]){PREFCTL}, }, { .name = "wsr.prid", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){PRID}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.ps", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){PS}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "wsr.ptevaddr", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){PTEVADDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.rasid", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){RASID}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "wsr.sar", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){SAR}, }, { .name = "wsr.scompare1", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){SCOMPARE1}, }, { .name = "wsr.vecbase", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){VECBASE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "wsr.windowbase", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){WINDOW_BASE}, + .op_flags = XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_SYNC_REGISTER_WINDOW, }, { .name = "wsr.windowstart", .translate = translate_wsr, + .test_ill = test_ill_wsr, .par = (const uint32_t[]){WINDOW_START}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "wur.expstate", .translate = translate_wur, @@ -4230,10 +4852,12 @@ static const XtensaOpcodeOps core_ops[] = { .name = "wur.fcr", .translate = translate_wur, .par = (const uint32_t[]){FCR}, + .coprocessor = 0x1, }, { .name = "wur.fsr", .translate = translate_wur, .par = (const uint32_t[]){FSR}, + .coprocessor = 0x1, }, { .name = "wur.threadptr", .translate = translate_wur, @@ -4248,307 +4872,471 @@ static const XtensaOpcodeOps core_ops[] = { }, { .name = "xsr.176", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){176}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.208", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){208}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.acchi", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){ACCHI}, }, { .name = "xsr.acclo", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){ACCLO}, }, { .name = "xsr.atomctl", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){ATOMCTL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.br", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){BR}, }, { .name = "xsr.cacheattr", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CACHEATTR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.ccompare0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CCOMPARE}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.ccompare1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CCOMPARE + 1}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.ccompare2", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CCOMPARE + 2}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.ccount", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CCOUNT}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.configid0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CONFIGID0}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.configid1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CONFIGID1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.cpenable", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){CPENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "xsr.dbreaka0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DBREAKA}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.dbreaka1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DBREAKA + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.dbreakc0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DBREAKC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.dbreakc1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DBREAKC + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.ddr", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.debugcause", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DEBUGCAUSE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.depc", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DEPC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.dtlbcfg", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){DTLBCFG}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc2", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc3", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc4", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc5", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc6", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.epc7", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPC1 + 6}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.eps2", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPS2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.eps3", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPS2 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.eps4", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPS2 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.eps5", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPS2 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.eps6", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPS2 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.eps7", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EPS2 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.exccause", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCCAUSE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave2", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1 + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave3", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1 + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave4", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1 + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave5", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1 + 4}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave6", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1 + 5}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excsave7", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCSAVE1 + 6}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.excvaddr", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){EXCVADDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.ibreaka0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){IBREAKA}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.ibreaka1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){IBREAKA + 1}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.ibreakenable", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){IBREAKENABLE}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_0, }, { .name = "xsr.icount", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){ICOUNT}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.icountlevel", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){ICOUNTLEVEL}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "xsr.intclear", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){INTCLEAR}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "xsr.intenable", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){INTENABLE}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "xsr.interrupt", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){INTSET}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "xsr.intset", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){INTSET}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_0 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "xsr.itlbcfg", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){ITLBCFG}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.lbeg", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){LBEG}, + .op_flags = XTENSA_OP_EXIT_TB_M1, }, { .name = "xsr.lcount", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){LCOUNT}, }, { .name = "xsr.lend", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){LEND}, + .op_flags = XTENSA_OP_EXIT_TB_M1, }, { .name = "xsr.litbase", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){LITBASE}, + .op_flags = XTENSA_OP_EXIT_TB_M1, }, { .name = "xsr.m0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MR}, }, { .name = "xsr.m1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MR + 1}, }, { .name = "xsr.m2", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MR + 2}, }, { .name = "xsr.m3", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MR + 3}, }, { .name = "xsr.memctl", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MEMCTL}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.misc0", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MISC}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.misc1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MISC + 1}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.misc2", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MISC + 2}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.misc3", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){MISC + 3}, + .op_flags = XTENSA_OP_PRIVILEGED, + }, { + .name = "xsr.prefctl", + .translate = translate_xsr, + .test_ill = test_ill_xsr, + .par = (const uint32_t[]){PREFCTL}, }, { .name = "xsr.prid", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){PRID}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.ps", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){PS}, + .op_flags = + XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_CHECK_INTERRUPTS, }, { .name = "xsr.ptevaddr", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){PTEVADDR}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.rasid", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){RASID}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, { .name = "xsr.sar", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){SAR}, }, { .name = "xsr.scompare1", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){SCOMPARE1}, }, { .name = "xsr.vecbase", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){VECBASE}, + .op_flags = XTENSA_OP_PRIVILEGED, }, { .name = "xsr.windowbase", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){WINDOW_BASE}, + .op_flags = XTENSA_OP_PRIVILEGED | + XTENSA_OP_EXIT_TB_M1 | + XTENSA_OP_SYNC_REGISTER_WINDOW, }, { .name = "xsr.windowstart", .translate = translate_xsr, + .test_ill = test_ill_xsr, .par = (const uint32_t[]){WINDOW_START}, + .op_flags = XTENSA_OP_PRIVILEGED | XTENSA_OP_EXIT_TB_M1, }, }; @@ -4558,21 +5346,17 @@ const XtensaOpcodeTranslators xtensa_core_opcodes = { }; -static void translate_abs_s(DisasContext *dc, const uint32_t arg[], +static void translate_abs_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_abs_s(cpu_FR[arg[0]], cpu_FR[arg[1]]); - } + gen_helper_abs_s(arg[0].out, arg[1].in); } -static void translate_add_s(DisasContext *dc, const uint32_t arg[], +static void translate_add_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_add_s(cpu_FR[arg[0]], cpu_env, - cpu_FR[arg[1]], cpu_FR[arg[2]]); - } + gen_helper_add_s(arg[0].out, cpu_env, + arg[1].in, arg[2].in); } enum { @@ -4585,7 +5369,7 @@ enum { COMPARE_ULE, }; -static void translate_compare_s(DisasContext *dc, const uint32_t arg[], +static void translate_compare_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { static void (* const helper[])(TCGv_env env, TCGv_i32 bit, @@ -4598,331 +5382,344 @@ static void translate_compare_s(DisasContext *dc, const uint32_t arg[], [COMPARE_OLE] = gen_helper_ole_s, [COMPARE_ULE] = gen_helper_ule_s, }; + TCGv_i32 bit = tcg_const_i32(1 << arg[0].imm); - if (gen_check_cpenable(dc, 0)) { - TCGv_i32 bit = tcg_const_i32(1 << arg[0]); - - helper[par[0]](cpu_env, bit, cpu_FR[arg[1]], cpu_FR[arg[2]]); - tcg_temp_free(bit); - } + helper[par[0]](cpu_env, bit, arg[1].in, arg[2].in); + tcg_temp_free(bit); } -static void translate_float_s(DisasContext *dc, const uint32_t arg[], +static void translate_float_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[1]) && gen_check_cpenable(dc, 0)) { - TCGv_i32 scale = tcg_const_i32(-arg[2]); + TCGv_i32 scale = tcg_const_i32(-arg[2].imm); - if (par[0]) { - gen_helper_uitof(cpu_FR[arg[0]], cpu_env, cpu_R[arg[1]], scale); - } else { - gen_helper_itof(cpu_FR[arg[0]], cpu_env, cpu_R[arg[1]], scale); - } - tcg_temp_free(scale); + if (par[0]) { + gen_helper_uitof(arg[0].out, cpu_env, arg[1].in, scale); + } else { + gen_helper_itof(arg[0].out, cpu_env, arg[1].in, scale); } + tcg_temp_free(scale); } -static void translate_ftoi_s(DisasContext *dc, const uint32_t arg[], +static void translate_ftoi_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0]) && gen_check_cpenable(dc, 0)) { - TCGv_i32 rounding_mode = tcg_const_i32(par[0]); - TCGv_i32 scale = tcg_const_i32(arg[2]); + TCGv_i32 rounding_mode = tcg_const_i32(par[0]); + TCGv_i32 scale = tcg_const_i32(arg[2].imm); - if (par[1]) { - gen_helper_ftoui(cpu_R[arg[0]], cpu_FR[arg[1]], - rounding_mode, scale); - } else { - gen_helper_ftoi(cpu_R[arg[0]], cpu_FR[arg[1]], - rounding_mode, scale); - } - tcg_temp_free(rounding_mode); - tcg_temp_free(scale); + if (par[1]) { + gen_helper_ftoui(arg[0].out, arg[1].in, + rounding_mode, scale); + } else { + gen_helper_ftoi(arg[0].out, arg[1].in, + rounding_mode, scale); } + tcg_temp_free(rounding_mode); + tcg_temp_free(scale); } -static void translate_ldsti(DisasContext *dc, const uint32_t arg[], +static void translate_ldsti(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[1]) && gen_check_cpenable(dc, 0)) { - TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); - tcg_gen_addi_i32(addr, cpu_R[arg[1]], arg[2]); - gen_load_store_alignment(dc, 2, addr, false); - if (par[0]) { - tcg_gen_qemu_st32(cpu_FR[arg[0]], addr, dc->cring); - } else { - tcg_gen_qemu_ld32u(cpu_FR[arg[0]], addr, dc->cring); - } - if (par[1]) { - tcg_gen_mov_i32(cpu_R[arg[1]], addr); - } - tcg_temp_free(addr); + tcg_gen_addi_i32(addr, arg[1].in, arg[2].imm); + gen_load_store_alignment(dc, 2, addr, false); + if (par[0]) { + tcg_gen_qemu_st32(arg[0].in, addr, dc->cring); + } else { + tcg_gen_qemu_ld32u(arg[0].out, addr, dc->cring); } + if (par[1]) { + tcg_gen_mov_i32(arg[1].out, addr); + } + tcg_temp_free(addr); } -static void translate_ldstx(DisasContext *dc, const uint32_t arg[], +static void translate_ldstx(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check2(dc, arg[1], arg[2]) && gen_check_cpenable(dc, 0)) { - TCGv_i32 addr = tcg_temp_new_i32(); + TCGv_i32 addr = tcg_temp_new_i32(); - tcg_gen_add_i32(addr, cpu_R[arg[1]], cpu_R[arg[2]]); - gen_load_store_alignment(dc, 2, addr, false); - if (par[0]) { - tcg_gen_qemu_st32(cpu_FR[arg[0]], addr, dc->cring); - } else { - tcg_gen_qemu_ld32u(cpu_FR[arg[0]], addr, dc->cring); - } - if (par[1]) { - tcg_gen_mov_i32(cpu_R[arg[1]], addr); - } - tcg_temp_free(addr); + tcg_gen_add_i32(addr, arg[1].in, arg[2].in); + gen_load_store_alignment(dc, 2, addr, false); + if (par[0]) { + tcg_gen_qemu_st32(arg[0].in, addr, dc->cring); + } else { + tcg_gen_qemu_ld32u(arg[0].out, addr, dc->cring); } + if (par[1]) { + tcg_gen_mov_i32(arg[1].out, addr); + } + tcg_temp_free(addr); } -static void translate_madd_s(DisasContext *dc, const uint32_t arg[], +static void translate_madd_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_madd_s(cpu_FR[arg[0]], cpu_env, - cpu_FR[arg[0]], cpu_FR[arg[1]], cpu_FR[arg[2]]); - } + gen_helper_madd_s(arg[0].out, cpu_env, + arg[0].in, arg[1].in, arg[2].in); } -static void translate_mov_s(DisasContext *dc, const uint32_t arg[], +static void translate_mov_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - tcg_gen_mov_i32(cpu_FR[arg[0]], cpu_FR[arg[1]]); - } + tcg_gen_mov_i32(arg[0].out, arg[1].in); } -static void translate_movcond_s(DisasContext *dc, const uint32_t arg[], +static void translate_movcond_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[2]) && gen_check_cpenable(dc, 0)) { - TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 zero = tcg_const_i32(0); - tcg_gen_movcond_i32(par[0], cpu_FR[arg[0]], - cpu_R[arg[2]], zero, - cpu_FR[arg[1]], cpu_FR[arg[2]]); - tcg_temp_free(zero); - } + tcg_gen_movcond_i32(par[0], arg[0].out, + arg[2].in, zero, + arg[1].in, arg[0].in); + tcg_temp_free(zero); } -static void translate_movp_s(DisasContext *dc, const uint32_t arg[], +static void translate_movp_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - TCGv_i32 zero = tcg_const_i32(0); - TCGv_i32 tmp = tcg_temp_new_i32(); + TCGv_i32 zero = tcg_const_i32(0); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_andi_i32(tmp, cpu_SR[BR], 1 << arg[2]); - tcg_gen_movcond_i32(par[0], - cpu_FR[arg[0]], tmp, zero, - cpu_FR[arg[1]], cpu_FR[arg[0]]); - tcg_temp_free(tmp); - tcg_temp_free(zero); - } + tcg_gen_andi_i32(tmp, arg[2].in, 1 << arg[2].imm); + tcg_gen_movcond_i32(par[0], + arg[0].out, tmp, zero, + arg[1].in, arg[0].in); + tcg_temp_free(tmp); + tcg_temp_free(zero); } -static void translate_mul_s(DisasContext *dc, const uint32_t arg[], +static void translate_mul_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_mul_s(cpu_FR[arg[0]], cpu_env, - cpu_FR[arg[1]], cpu_FR[arg[2]]); - } + gen_helper_mul_s(arg[0].out, cpu_env, + arg[1].in, arg[2].in); } -static void translate_msub_s(DisasContext *dc, const uint32_t arg[], +static void translate_msub_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_msub_s(cpu_FR[arg[0]], cpu_env, - cpu_FR[arg[0]], cpu_FR[arg[1]], cpu_FR[arg[2]]); - } + gen_helper_msub_s(arg[0].out, cpu_env, + arg[0].in, arg[1].in, arg[2].in); } -static void translate_neg_s(DisasContext *dc, const uint32_t arg[], +static void translate_neg_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_neg_s(cpu_FR[arg[0]], cpu_FR[arg[1]]); - } + gen_helper_neg_s(arg[0].out, arg[1].in); } -static void translate_rfr_s(DisasContext *dc, const uint32_t arg[], +static void translate_rfr_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[0]) && - gen_check_cpenable(dc, 0)) { - tcg_gen_mov_i32(cpu_R[arg[0]], cpu_FR[arg[1]]); - } + tcg_gen_mov_i32(arg[0].out, arg[1].in); } -static void translate_sub_s(DisasContext *dc, const uint32_t arg[], +static void translate_sub_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_check_cpenable(dc, 0)) { - gen_helper_sub_s(cpu_FR[arg[0]], cpu_env, - cpu_FR[arg[1]], cpu_FR[arg[2]]); - } + gen_helper_sub_s(arg[0].out, cpu_env, + arg[1].in, arg[2].in); } -static void translate_wfr_s(DisasContext *dc, const uint32_t arg[], +static void translate_wfr_s(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - if (gen_window_check1(dc, arg[1]) && - gen_check_cpenable(dc, 0)) { - tcg_gen_mov_i32(cpu_FR[arg[0]], cpu_R[arg[1]]); - } + tcg_gen_mov_i32(arg[0].out, arg[1].in); } static const XtensaOpcodeOps fpu2000_ops[] = { { .name = "abs.s", .translate = translate_abs_s, + .coprocessor = 0x1, }, { .name = "add.s", .translate = translate_add_s, + .coprocessor = 0x1, }, { .name = "ceil.s", .translate = translate_ftoi_s, .par = (const uint32_t[]){float_round_up, false}, + .coprocessor = 0x1, }, { .name = "float.s", .translate = translate_float_s, .par = (const uint32_t[]){false}, + .coprocessor = 0x1, }, { .name = "floor.s", .translate = translate_ftoi_s, .par = (const uint32_t[]){float_round_down, false}, + .coprocessor = 0x1, }, { .name = "lsi", .translate = translate_ldsti, .par = (const uint32_t[]){false, false}, + .op_flags = XTENSA_OP_LOAD, + .coprocessor = 0x1, }, { .name = "lsiu", .translate = translate_ldsti, .par = (const uint32_t[]){false, true}, + .op_flags = XTENSA_OP_LOAD, + .coprocessor = 0x1, }, { .name = "lsx", .translate = translate_ldstx, .par = (const uint32_t[]){false, false}, + .op_flags = XTENSA_OP_LOAD, + .coprocessor = 0x1, }, { .name = "lsxu", .translate = translate_ldstx, .par = (const uint32_t[]){false, true}, + .op_flags = XTENSA_OP_LOAD, + .coprocessor = 0x1, }, { .name = "madd.s", .translate = translate_madd_s, + .coprocessor = 0x1, }, { .name = "mov.s", .translate = translate_mov_s, + .coprocessor = 0x1, }, { .name = "moveqz.s", .translate = translate_movcond_s, .par = (const uint32_t[]){TCG_COND_EQ}, + .coprocessor = 0x1, }, { .name = "movf.s", .translate = translate_movp_s, .par = (const uint32_t[]){TCG_COND_EQ}, + .coprocessor = 0x1, }, { .name = "movgez.s", .translate = translate_movcond_s, .par = (const uint32_t[]){TCG_COND_GE}, + .coprocessor = 0x1, }, { .name = "movltz.s", .translate = translate_movcond_s, .par = (const uint32_t[]){TCG_COND_LT}, + .coprocessor = 0x1, }, { .name = "movnez.s", .translate = translate_movcond_s, .par = (const uint32_t[]){TCG_COND_NE}, + .coprocessor = 0x1, }, { .name = "movt.s", .translate = translate_movp_s, .par = (const uint32_t[]){TCG_COND_NE}, + .coprocessor = 0x1, }, { .name = "msub.s", .translate = translate_msub_s, + .coprocessor = 0x1, }, { .name = "mul.s", .translate = translate_mul_s, + .coprocessor = 0x1, }, { .name = "neg.s", .translate = translate_neg_s, + .coprocessor = 0x1, }, { .name = "oeq.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_OEQ}, + .coprocessor = 0x1, }, { .name = "ole.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_OLE}, + .coprocessor = 0x1, }, { .name = "olt.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_OLT}, + .coprocessor = 0x1, }, { - .name = "rfr.s", + .name = "rfr", .translate = translate_rfr_s, + .coprocessor = 0x1, }, { .name = "round.s", .translate = translate_ftoi_s, .par = (const uint32_t[]){float_round_nearest_even, false}, + .coprocessor = 0x1, }, { .name = "ssi", .translate = translate_ldsti, .par = (const uint32_t[]){true, false}, + .op_flags = XTENSA_OP_STORE, + .coprocessor = 0x1, }, { .name = "ssiu", .translate = translate_ldsti, .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_STORE, + .coprocessor = 0x1, }, { .name = "ssx", .translate = translate_ldstx, .par = (const uint32_t[]){true, false}, + .op_flags = XTENSA_OP_STORE, + .coprocessor = 0x1, }, { .name = "ssxu", .translate = translate_ldstx, .par = (const uint32_t[]){true, true}, + .op_flags = XTENSA_OP_STORE, + .coprocessor = 0x1, }, { .name = "sub.s", .translate = translate_sub_s, + .coprocessor = 0x1, }, { .name = "trunc.s", .translate = translate_ftoi_s, .par = (const uint32_t[]){float_round_to_zero, false}, + .coprocessor = 0x1, }, { .name = "ueq.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_UEQ}, + .coprocessor = 0x1, }, { .name = "ufloat.s", .translate = translate_float_s, .par = (const uint32_t[]){true}, + .coprocessor = 0x1, }, { .name = "ule.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_ULE}, + .coprocessor = 0x1, }, { .name = "ult.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_ULT}, + .coprocessor = 0x1, }, { .name = "un.s", .translate = translate_compare_s, .par = (const uint32_t[]){COMPARE_UN}, + .coprocessor = 0x1, }, { .name = "utrunc.s", .translate = translate_ftoi_s, .par = (const uint32_t[]){float_round_to_zero, true}, + .coprocessor = 0x1, }, { - .name = "wfr.s", + .name = "wfr", .translate = translate_wfr_s, + .coprocessor = 0x1, }, }; diff --git a/target/xtensa/win_helper.c b/target/xtensa/win_helper.c new file mode 100644 index 0000000000..f6f96a64c3 --- /dev/null +++ b/target/xtensa/win_helper.c @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2011 - 2019, Max Filippov, Open Source and Linux Lab. + * 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 Open Source and Linux Lab 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 AUTHOR 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 "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "cpu.h" +#include "exec/helper-proto.h" +#include "qemu/host-utils.h" +#include "exec/exec-all.h" + +static void copy_window_from_phys(CPUXtensaState *env, + uint32_t window, uint32_t phys, uint32_t n) +{ + assert(phys < env->config->nareg); + if (phys + n <= env->config->nareg) { + memcpy(env->regs + window, env->phys_regs + phys, + n * sizeof(uint32_t)); + } else { + uint32_t n1 = env->config->nareg - phys; + memcpy(env->regs + window, env->phys_regs + phys, + n1 * sizeof(uint32_t)); + memcpy(env->regs + window + n1, env->phys_regs, + (n - n1) * sizeof(uint32_t)); + } +} + +static void copy_phys_from_window(CPUXtensaState *env, + uint32_t phys, uint32_t window, uint32_t n) +{ + assert(phys < env->config->nareg); + if (phys + n <= env->config->nareg) { + memcpy(env->phys_regs + phys, env->regs + window, + n * sizeof(uint32_t)); + } else { + uint32_t n1 = env->config->nareg - phys; + memcpy(env->phys_regs + phys, env->regs + window, + n1 * sizeof(uint32_t)); + memcpy(env->phys_regs, env->regs + window + n1, + (n - n1) * sizeof(uint32_t)); + } +} + +static inline unsigned windowbase_bound(unsigned a, const CPUXtensaState *env) +{ + return a & (env->config->nareg / 4 - 1); +} + +static inline unsigned windowstart_bit(unsigned a, const CPUXtensaState *env) +{ + return 1 << windowbase_bound(a, env); +} + +void xtensa_sync_window_from_phys(CPUXtensaState *env) +{ + copy_window_from_phys(env, 0, env->sregs[WINDOW_BASE] * 4, 16); +} + +void xtensa_sync_phys_from_window(CPUXtensaState *env) +{ + copy_phys_from_window(env, env->sregs[WINDOW_BASE] * 4, 0, 16); +} + +static void xtensa_rotate_window_abs(CPUXtensaState *env, uint32_t position) +{ + xtensa_sync_phys_from_window(env); + env->sregs[WINDOW_BASE] = windowbase_bound(position, env); + xtensa_sync_window_from_phys(env); +} + +void xtensa_rotate_window(CPUXtensaState *env, uint32_t delta) +{ + xtensa_rotate_window_abs(env, env->sregs[WINDOW_BASE] + delta); +} + +void HELPER(sync_windowbase)(CPUXtensaState *env) +{ + xtensa_rotate_window_abs(env, env->windowbase_next); +} + +void HELPER(entry)(CPUXtensaState *env, uint32_t pc, uint32_t s, uint32_t imm) +{ + int callinc = (env->sregs[PS] & PS_CALLINC) >> PS_CALLINC_SHIFT; + + env->regs[(callinc << 2) | (s & 3)] = env->regs[s] - imm; + env->windowbase_next = env->sregs[WINDOW_BASE] + callinc; + env->sregs[WINDOW_START] |= windowstart_bit(env->windowbase_next, env); +} + +void HELPER(window_check)(CPUXtensaState *env, uint32_t pc, uint32_t w) +{ + uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); + uint32_t windowstart = xtensa_replicate_windowstart(env) >> + (env->sregs[WINDOW_BASE] + 1); + uint32_t n = ctz32(windowstart) + 1; + + assert(n <= w); + + xtensa_rotate_window(env, n); + env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | + (windowbase << PS_OWB_SHIFT) | PS_EXCM; + env->sregs[EPC1] = env->pc = pc; + + switch (ctz32(windowstart >> n)) { + case 0: + HELPER(exception)(env, EXC_WINDOW_OVERFLOW4); + break; + case 1: + HELPER(exception)(env, EXC_WINDOW_OVERFLOW8); + break; + default: + HELPER(exception)(env, EXC_WINDOW_OVERFLOW12); + break; + } +} + +void HELPER(test_ill_retw)(CPUXtensaState *env, uint32_t pc) +{ + int n = (env->regs[0] >> 30) & 0x3; + int m = 0; + uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); + uint32_t windowstart = env->sregs[WINDOW_START]; + + if (windowstart & windowstart_bit(windowbase - 1, env)) { + m = 1; + } else if (windowstart & windowstart_bit(windowbase - 2, env)) { + m = 2; + } else if (windowstart & windowstart_bit(windowbase - 3, env)) { + m = 3; + } + + if (n == 0 || (m != 0 && 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); + } +} + +void HELPER(test_underflow_retw)(CPUXtensaState *env, uint32_t pc) +{ + int n = (env->regs[0] >> 30) & 0x3; + + if (!(env->sregs[WINDOW_START] & + windowstart_bit(env->sregs[WINDOW_BASE] - n, env))) { + uint32_t windowbase = windowbase_bound(env->sregs[WINDOW_BASE], env); + + xtensa_rotate_window(env, -n); + /* window underflow */ + env->sregs[PS] = (env->sregs[PS] & ~PS_OWB) | + (windowbase << PS_OWB_SHIFT) | PS_EXCM; + env->sregs[EPC1] = env->pc = pc; + + if (n == 1) { + HELPER(exception)(env, EXC_WINDOW_UNDERFLOW4); + } else if (n == 2) { + HELPER(exception)(env, EXC_WINDOW_UNDERFLOW8); + } else if (n == 3) { + HELPER(exception)(env, EXC_WINDOW_UNDERFLOW12); + } + } +} + +void HELPER(retw)(CPUXtensaState *env, uint32_t a0) +{ + int n = (a0 >> 30) & 0x3; + + xtensa_rotate_window(env, -n); +} + +void xtensa_restore_owb(CPUXtensaState *env) +{ + xtensa_rotate_window_abs(env, (env->sregs[PS] & PS_OWB) >> PS_OWB_SHIFT); +} + +void HELPER(restore_owb)(CPUXtensaState *env) +{ + xtensa_restore_owb(env); +} + +void HELPER(movsp)(CPUXtensaState *env, uint32_t pc) +{ + if ((env->sregs[WINDOW_START] & + (windowstart_bit(env->sregs[WINDOW_BASE] - 3, env) | + windowstart_bit(env->sregs[WINDOW_BASE] - 2, env) | + windowstart_bit(env->sregs[WINDOW_BASE] - 1, env))) == 0) { + HELPER(exception_cause)(env, pc, ALLOCA_CAUSE); + } +} diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 7aa1d1357b..5f5ce4f344 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -34,8 +34,6 @@ #include "qemu/log.h" #include "sysemu/sysemu.h" -static CharBackend *xtensa_sim_console; - enum { TARGET_SYS_exit = 1, TARGET_SYS_read = 3, @@ -153,13 +151,48 @@ static uint32_t errno_h2g(int host_errno) } } +typedef struct XtensaSimConsole { + CharBackend be; + struct { + char buffer[16]; + size_t offset; + } input; +} XtensaSimConsole; + +static XtensaSimConsole *sim_console; + +static IOCanReadHandler sim_console_can_read; +static int sim_console_can_read(void *opaque) +{ + XtensaSimConsole *p = opaque; + + return sizeof(p->input.buffer) - p->input.offset; +} + +static IOReadHandler sim_console_read; +static void sim_console_read(void *opaque, const uint8_t *buf, int size) +{ + XtensaSimConsole *p = opaque; + size_t copy = sizeof(p->input.buffer) - p->input.offset; + + if (size < copy) { + copy = size; + } + memcpy(p->input.buffer + p->input.offset, buf, copy); + p->input.offset += copy; +} + void xtensa_sim_open_console(Chardev *chr) { - static CharBackend console; + static XtensaSimConsole console; - qemu_chr_fe_init(&console, chr, &error_abort); - qemu_chr_fe_set_handlers(&console, NULL, NULL, NULL, NULL, NULL, NULL, true); - xtensa_sim_console = &console; + qemu_chr_fe_init(&console.be, chr, &error_abort); + qemu_chr_fe_set_handlers(&console.be, + sim_console_can_read, + sim_console_read, + NULL, NULL, &console, + NULL, true); + sim_console = &console; } void HELPER(simcall)(CPUXtensaState *env) @@ -169,7 +202,6 @@ void HELPER(simcall)(CPUXtensaState *env) switch (regs[2]) { case TARGET_SYS_exit: - qemu_log("exit(%d) simcall\n", regs[3]); exit(regs[3]); break; @@ -195,11 +227,27 @@ void HELPER(simcall)(CPUXtensaState *env) if (buf) { vaddr += io_sz; len -= io_sz; - if (fd < 3 && xtensa_sim_console) { + if (fd < 3 && sim_console) { if (is_write && (fd == 1 || fd == 2)) { - io_done = qemu_chr_fe_write_all(xtensa_sim_console, + io_done = qemu_chr_fe_write_all(&sim_console->be, buf, io_sz); regs[3] = errno_h2g(errno); + } else if (!is_write && fd == 0) { + if (sim_console->input.offset) { + io_done = sim_console->input.offset; + if (io_sz < io_done) { + io_done = io_sz; + } + memcpy(buf, sim_console->input.buffer, io_done); + memmove(sim_console->input.buffer, + sim_console->input.buffer + io_done, + sim_console->input.offset - io_done); + sim_console->input.offset -= io_done; + qemu_chr_fe_accept_input(&sim_console->be); + } else { + io_done = -1; + regs[3] = TARGET_EAGAIN; + } } else { qemu_log_mask(LOG_GUEST_ERROR, "%s fd %d is not supported with chardev console\n", @@ -292,9 +340,11 @@ void HELPER(simcall)(CPUXtensaState *env) tv.tv_sec = (int32_t)tswap32(target_tvv[0]); tv.tv_usec = (int32_t)tswap32(target_tvv[1]); } - if (fd < 3 && xtensa_sim_console) { + if (fd < 3 && sim_console) { if ((fd == 1 || fd == 2) && rq == SELECT_ONE_WRITE) { regs[2] = 1; + } else if (fd == 0 && rq == SELECT_ONE_READ) { + regs[2] = sim_console->input.offset > 0; } else { regs[2] = 0; } diff --git a/tcg/README b/tcg/README index d22ee084b8..603f4df659 100644 --- a/tcg/README +++ b/tcg/README @@ -554,6 +554,25 @@ E.g. VECL=1 -> 64 << 1 -> v128, and VECE=2 -> 1 << 2 -> i32. Similarly, v0 = -v1. +* smin_vec: +* umin_vec: + + Similarly, v0 = MIN(v1, v2), for signed and unsigned element types. + +* smax_vec: +* umax_vec: + + Similarly, v0 = MAX(v1, v2), for signed and unsigned element types. + +* ssadd_vec: +* sssub_vec: +* usadd_vec: +* ussub_vec: + + Signed and unsigned saturating addition and subtraction. If the true + result is not representable within the element type, the element is + set to the minimum or maximum value for the type. + * and_vec v0, v1, v2 * or_vec v0, v1, v2 * xor_vec v0, v1, v2 diff --git a/tcg/TODO b/tcg/TODO deleted file mode 100644 index 074784778e..0000000000 --- a/tcg/TODO +++ /dev/null @@ -1,14 +0,0 @@ -- Add new instructions such as: clz, ctz, popcnt. - -- See if it is worth exporting mul2, mulu2, div2, divu2. - -- Support of globals saved in fixed registers between TBs. - -Ideas: - -- Move the slow part of the qemu_ld/st ops after the end of the TB. - -- Change exception syntax to get closer to QOP system (exception - parameters given with a specific instruction). - -- Add float and vector support. diff --git a/tcg/aarch64/tcg-target.h b/tcg/aarch64/tcg-target.h index 9aea1d1771..2d93cf404e 100644 --- a/tcg/aarch64/tcg-target.h +++ b/tcg/aarch64/tcg-target.h @@ -135,8 +135,11 @@ typedef enum { #define TCG_TARGET_HAS_shv_vec 0 #define TCG_TARGET_HAS_cmp_vec 1 #define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/tcg/aarch64/tcg-target.inc.c b/tcg/aarch64/tcg-target.inc.c index 083592a4d7..d57f9e500f 100644 --- a/tcg/aarch64/tcg-target.inc.c +++ b/tcg/aarch64/tcg-target.inc.c @@ -78,48 +78,40 @@ static const int tcg_target_call_oarg_regs[1] = { #define TCG_REG_GUEST_BASE TCG_REG_X28 #endif -static inline void reloc_pc26(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +static inline bool 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); + if (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); + return true; + } + return false; } -static inline void reloc_pc26_atomic(tcg_insn_unit *code_ptr, - tcg_insn_unit *target) +static inline bool reloc_pc19(tcg_insn_unit *code_ptr, tcg_insn_unit *target) { ptrdiff_t offset = target - code_ptr; - tcg_insn_unit insn; - 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. */ - insn = atomic_read(code_ptr); - atomic_set(code_ptr, deposit32(insn, 0, 26, offset)); + if (offset == sextract64(offset, 0, 19)) { + *code_ptr = deposit32(*code_ptr, 5, 19, offset); + return true; + } + return false; } -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, +static inline bool 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; + return reloc_pc26(code_ptr, (tcg_insn_unit *)value); case R_AARCH64_CONDBR19: - reloc_pc19(code_ptr, (tcg_insn_unit *)value); - break; + return reloc_pc19(code_ptr, (tcg_insn_unit *)value); default: - tcg_abort(); + g_assert_not_reached(); } } @@ -506,6 +498,9 @@ typedef enum { I3510_EON = 0x4a200000, I3510_ANDS = 0x6a000000, + /* Logical shifted register instructions (with a shift). */ + I3502S_AND_LSR = I3510_AND | (1 << 22), + /* AdvSIMD copy */ I3605_DUP = 0x0e000400, I3605_INS = 0x4e001c00, @@ -536,6 +531,14 @@ typedef enum { I3616_CMHI = 0x2e203400, I3616_CMHS = 0x2e203c00, I3616_CMEQ = 0x2e208c00, + I3616_SMAX = 0x0e206400, + I3616_SMIN = 0x0e206c00, + I3616_SQADD = 0x0e200c00, + I3616_SQSUB = 0x0e202c00, + I3616_UMAX = 0x2e206400, + I3616_UMIN = 0x2e206c00, + I3616_UQADD = 0x2e200c00, + I3616_UQSUB = 0x2e202c00, /* AdvSIMD two-reg misc. */ I3617_CMGT0 = 0x0e208800, @@ -1141,23 +1144,6 @@ static inline void tcg_out_goto_long(TCGContext *s, tcg_insn_unit *target) } } -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); @@ -1204,7 +1190,7 @@ 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); + tcg_out_insn(s, 3206, B, 0); } else { tcg_out_goto(s, l->u.value_ptr); } @@ -1415,7 +1401,8 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOp opc = get_memop(oi); TCGMemOp size = opc & MO_SIZE; - reloc_pc19(lb->label_ptr[0], s->code_ptr); + bool ok = reloc_pc19(lb->label_ptr[0], s->code_ptr); + tcg_debug_assert(ok); 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); @@ -1437,7 +1424,8 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOp opc = get_memop(oi); TCGMemOp size = opc & MO_SIZE; - reloc_pc19(lb->label_ptr[0], s->code_ptr); + bool ok = reloc_pc19(lb->label_ptr[0], s->code_ptr); + tcg_debug_assert(ok); 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); @@ -1463,6 +1451,14 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, label->label_ptr[0] = label_ptr; } +/* We expect tlb_mask to be before tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) < + offsetof(CPUArchState, tlb_mask)); + +/* We expect to use a 24-bit unsigned offset from ENV. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1]) + > 0xffffff); + /* 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, @@ -1471,15 +1467,55 @@ 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 mask_ofs = offsetof(CPUArchState, tlb_mask[mem_index]); + int table_ofs = offsetof(CPUArchState, tlb_table[mem_index]); unsigned a_bits = get_alignment_bits(opc); unsigned s_bits = opc & MO_SIZE; unsigned a_mask = (1u << a_bits) - 1; unsigned s_mask = (1u << s_bits) - 1; - TCGReg base = TCG_AREG0, x3; - uint64_t tlb_mask; + TCGReg mask_base = TCG_AREG0, table_base = TCG_AREG0, x3; + TCGType mask_type; + uint64_t compare_mask; + + if (table_ofs > 0xfff) { + int table_hi = table_ofs & ~0xfff; + int mask_hi = mask_ofs & ~0xfff; + + table_base = TCG_REG_X1; + if (mask_hi == table_hi) { + mask_base = table_base; + } else if (mask_hi) { + mask_base = TCG_REG_X0; + tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, + mask_base, TCG_AREG0, mask_hi); + } + tcg_out_insn(s, 3401, ADDI, TCG_TYPE_I64, + table_base, TCG_AREG0, table_hi); + mask_ofs -= mask_hi; + table_ofs -= table_hi; + } + + mask_type = (TARGET_PAGE_BITS + CPU_TLB_DYN_MAX_BITS > 32 + ? TCG_TYPE_I64 : TCG_TYPE_I32); + + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, mask_type, TCG_REG_X0, mask_base, mask_ofs); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_X1, table_base, table_ofs); + + /* Extract the TLB index from the address into X0. */ + tcg_out_insn(s, 3502S, AND_LSR, mask_type == TCG_TYPE_I64, + TCG_REG_X0, TCG_REG_X0, addr_reg, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + /* Add the tlb_table pointer, creating the CPUTLBEntry address into X1. */ + tcg_out_insn(s, 3502, ADD, 1, TCG_REG_X1, TCG_REG_X1, TCG_REG_X0); + + /* Load the tlb comparator into X0, and the fast path addend into X1. */ + tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_X0, TCG_REG_X1, is_read + ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_X1, TCG_REG_X1, + offsetof(CPUTLBEntry, addend)); /* 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 @@ -1491,51 +1527,18 @@ static void tcg_out_tlb_read(TCGContext *s, TCGReg addr_reg, TCGMemOp opc, TCG_REG_X3, addr_reg, s_mask - a_mask); x3 = TCG_REG_X3; } - tlb_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; - - /* 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); + compare_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; /* 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, - TARGET_LONG_BITS == 32 ? 2 : 3); - - /* 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)), 3); + TCG_REG_X3, x3, compare_mask); /* Perform the address comparison. */ - tcg_out_cmp(s, (TARGET_LONG_BITS == 64), TCG_REG_X0, TCG_REG_X3, 0); + 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); + tcg_out_insn(s, 3202, B_C, TCG_COND_NE, 0); } #endif /* CONFIG_SOFTMMU */ @@ -2160,6 +2163,30 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_orc_vec: tcg_out_insn(s, 3616, ORN, is_q, 0, a0, a1, a2); break; + case INDEX_op_ssadd_vec: + tcg_out_insn(s, 3616, SQADD, is_q, vece, a0, a1, a2); + break; + case INDEX_op_sssub_vec: + tcg_out_insn(s, 3616, SQSUB, is_q, vece, a0, a1, a2); + break; + case INDEX_op_usadd_vec: + tcg_out_insn(s, 3616, UQADD, is_q, vece, a0, a1, a2); + break; + case INDEX_op_ussub_vec: + tcg_out_insn(s, 3616, UQSUB, is_q, vece, a0, a1, a2); + break; + case INDEX_op_smax_vec: + tcg_out_insn(s, 3616, SMAX, is_q, vece, a0, a1, a2); + break; + case INDEX_op_smin_vec: + tcg_out_insn(s, 3616, SMIN, is_q, vece, a0, a1, a2); + break; + case INDEX_op_umax_vec: + tcg_out_insn(s, 3616, UMAX, is_q, vece, a0, a1, a2); + break; + case INDEX_op_umin_vec: + tcg_out_insn(s, 3616, UMIN, is_q, vece, a0, a1, a2); + break; case INDEX_op_not_vec: tcg_out_insn(s, 3617, NOT, is_q, 0, a0, a1); break; @@ -2230,6 +2257,14 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) case INDEX_op_shli_vec: case INDEX_op_shri_vec: case INDEX_op_sari_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_usadd_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: return 1; case INDEX_op_mul_vec: return vece < MO_64; @@ -2409,6 +2444,14 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) case INDEX_op_xor_vec: case INDEX_op_andc_vec: case INDEX_op_orc_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_usadd_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smax_vec: + case INDEX_op_smin_vec: + case INDEX_op_umax_vec: + case INDEX_op_umin_vec: return &w_w_w; case INDEX_op_not_vec: case INDEX_op_neg_vec: diff --git a/tcg/arm/tcg-target.h b/tcg/arm/tcg-target.h index 94b3578c55..16172f73a3 100644 --- a/tcg/arm/tcg-target.h +++ b/tcg/arm/tcg-target.h @@ -131,6 +131,7 @@ enum { }; #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/tcg/arm/tcg-target.inc.c b/tcg/arm/tcg-target.inc.c index e1fbf465cb..2245a8aeb9 100644 --- a/tcg/arm/tcg-target.inc.c +++ b/tcg/arm/tcg-target.inc.c @@ -187,27 +187,23 @@ static const uint8_t tcg_cond_to_arm_cond[] = { [TCG_COND_GTU] = COND_HI, }; -static inline void reloc_pc24(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +static inline bool 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); + if (offset == sextract32(offset, 0, 24)) { + *code_ptr = (*code_ptr & ~0xffffff) | (offset & 0xffffff); + return true; + } + return false; } -static inline void reloc_pc24_atomic(tcg_insn_unit *code_ptr, tcg_insn_unit *target) -{ - ptrdiff_t offset = (tcg_ptr_byte_diff(target, code_ptr) - 8) >> 2; - tcg_insn_unit insn = atomic_read(code_ptr); - tcg_debug_assert(offset == sextract32(offset, 0, 24)); - atomic_set(code_ptr, deposit32(insn, 0, 24, offset)); -} - -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { tcg_debug_assert(addend == 0); if (type == R_ARM_PC24) { - reloc_pc24(code_ptr, (tcg_insn_unit *)value); + return reloc_pc24(code_ptr, (tcg_insn_unit *)value); } else if (type == R_ARM_PC13) { intptr_t diff = value - (uintptr_t)(code_ptr + 2); tcg_insn_unit insn = *code_ptr; @@ -221,7 +217,11 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, } else { int rd = extract32(insn, 12, 4); int rt = rd == TCG_REG_PC ? TCG_REG_TMP : rd; - assert(diff >= 0x1000 && diff < 0x100000); + + if (diff < 0x1000 || diff >= 0x100000) { + return false; + } + /* add rt, pc, #high */ *code_ptr++ = ((insn & 0xf0000000) | (1 << 25) | ARITH_ADD | (TCG_REG_PC << 16) | (rt << 12) @@ -237,6 +237,7 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, } else { g_assert_not_reached(); } + return true; } #define TCG_CT_CONST_ARM 0x100 @@ -374,22 +375,6 @@ static inline void tcg_out_b(TCGContext *s, int cond, int32_t offset) (((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 | @@ -515,6 +500,12 @@ static inline void tcg_out_ldrd_r(TCGContext *s, int cond, TCGReg rt, tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 0); } +static inline void tcg_out_ldrd_rwb(TCGContext *s, int cond, TCGReg rt, + TCGReg rn, TCGReg rm) +{ + tcg_out_memop_r(s, cond, INSN_LDRD_REG, rt, rn, rm, 1, 1, 1); +} + static inline void tcg_out_strd_8(TCGContext *s, int cond, TCGReg rt, TCGReg rn, int imm8) { @@ -1090,7 +1081,7 @@ static inline void tcg_out_goto_label(TCGContext *s, int cond, TCGLabel *l) 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); + tcg_out_b(s, cond, 0); } } @@ -1244,8 +1235,13 @@ static TCGReg tcg_out_arg_reg64(TCGContext *s, TCGReg 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 expect tlb_mask to be before tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) < + offsetof(CPUArchState, tlb_mask)); + +/* We expect to use a 20-bit unsigned offset from ENV. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1]) + > 0xfffff); /* 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. */ @@ -1253,84 +1249,72 @@ QEMU_BUILD_BUG_ON(CPU_TLB_BITS > 8); static TCGReg tcg_out_tlb_read(TCGContext *s, TCGReg addrlo, TCGReg addrhi, TCGMemOp opc, 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); - int mask_off; + int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + int mask_off = offsetof(CPUArchState, tlb_mask[mem_index]); + int table_off = offsetof(CPUArchState, tlb_table[mem_index]); + TCGReg mask_base = TCG_AREG0, table_base = TCG_AREG0; unsigned s_bits = opc & MO_SIZE; unsigned a_bits = get_alignment_bits(opc); - /* V7 generates the following: - * ubfx r0, addrlo, #TARGET_PAGE_BITS, #CPU_TLB_BITS - * add r2, env, #high - * add r2, r2, r0, lsl #CPU_TLB_ENTRY_BITS - * ldr r0, [r2, #cmp] - * ldr r2, [r2, #add] - * movw tmp, #page_align_mask - * bic tmp, addrlo, tmp - * cmp r0, tmp - * - * Otherwise we generate: - * shr tmp, addrlo, #TARGET_PAGE_BITS - * add r2, env, #high - * and r0, tmp, #(CPU_TLB_SIZE - 1) - * add r2, r2, r0, lsl #CPU_TLB_ENTRY_BITS - * ldr r0, [r2, #cmp] - * ldr r2, [r2, #add] - * tst addrlo, #s_mask - * cmpeq r0, tmp, lsl #TARGET_PAGE_BITS - */ - if (use_armv7_instructions) { - tcg_out_extract(s, COND_AL, TCG_REG_R0, addrlo, - TARGET_PAGE_BITS, CPU_TLB_BITS); - } else { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, - 0, addrlo, SHIFT_IMM_LSR(TARGET_PAGE_BITS)); - } + if (table_off > 0xfff) { + int mask_hi = mask_off & ~0xfff; + int table_hi = table_off & ~0xfff; + int rot; - /* Add portions of the offset until the memory access is in range. - * If we plan on using ldrd, reduce to an 8-bit offset; otherwise - * we can use a 12-bit offset. */ - if (use_armv6_instructions && TARGET_LONG_BITS == 64) { - mask_off = 0xff; - } else { - mask_off = 0xfff; - } - while (cmp_off > mask_off) { - int shift = ctz32(cmp_off & ~mask_off) & ~1; - int rot = ((32 - shift) << 7) & 0xf00; - int addend = cmp_off & (0xff << shift); - tcg_out_dat_imm(s, COND_AL, ARITH_ADD, TCG_REG_R2, base, - rot | ((cmp_off >> shift) & 0xff)); - base = TCG_REG_R2; - add_off -= addend; - cmp_off -= addend; - } - - if (!use_armv7_instructions) { - 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); + table_base = TCG_REG_R2; + if (mask_hi == table_hi) { + mask_base = table_base; + } else if (mask_hi) { + mask_base = TCG_REG_TMP; + rot = encode_imm(mask_hi); + assert(rot >= 0); + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, mask_base, TCG_AREG0, + rotl(mask_hi, rot) | (rot << 7)); } + rot = encode_imm(table_hi); + assert(rot >= 0); + tcg_out_dat_imm(s, COND_AL, ARITH_ADD, table_base, TCG_AREG0, + rotl(table_hi, rot) | (rot << 7)); + + mask_off -= mask_hi; + table_off -= table_hi; + } + + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP, mask_base, mask_off); + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R2, table_base, table_off); + + /* Extract the tlb index from the address into TMP. */ + tcg_out_dat_reg(s, COND_AL, ARITH_AND, TCG_REG_TMP, TCG_REG_TMP, addrlo, + SHIFT_IMM_LSR(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); + + /* + * Add the tlb_table pointer, creating the CPUTLBEntry address in R2. + * Load the tlb comparator into R0/R1 and the fast path addend into R2. + */ + if (cmp_off == 0) { + if (use_armv6_instructions && TARGET_LONG_BITS == 64) { + tcg_out_ldrd_rwb(s, COND_AL, TCG_REG_R0, TCG_REG_R2, TCG_REG_TMP); + } else { + tcg_out_ld32_rwb(s, COND_AL, TCG_REG_R0, TCG_REG_R2, TCG_REG_TMP); + } + } else { + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, + TCG_REG_R2, TCG_REG_R2, TCG_REG_TMP, 0); + 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 (!use_armv6_instructions && TARGET_LONG_BITS == 64) { + tcg_out_ld32_12(s, COND_AL, TCG_REG_R1, TCG_REG_R2, cmp_off + 4); } /* Load the tlb addend. */ - tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R2, add_off); + tcg_out_ld32_12(s, COND_AL, TCG_REG_R2, TCG_REG_R2, + offsetof(CPUTLBEntry, addend)); /* Check alignment. We don't support inline unaligned acceses, but we can easily support overalignment checks. */ @@ -1395,7 +1379,8 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOp opc = get_memop(oi); void *func; - reloc_pc24(lb->label_ptr[0], s->code_ptr); + bool ok = reloc_pc24(lb->label_ptr[0], s->code_ptr); + tcg_debug_assert(ok); argreg = tcg_out_arg_reg32(s, TCG_REG_R0, TCG_AREG0); if (TARGET_LONG_BITS == 64) { @@ -1455,7 +1440,8 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOpIdx oi = lb->oi; TCGMemOp opc = get_memop(oi); - reloc_pc24(lb->label_ptr[0], s->code_ptr); + bool ok = reloc_pc24(lb->label_ptr[0], s->code_ptr); + tcg_debug_assert(ok); argreg = TCG_REG_R0; argreg = tcg_out_arg_reg32(s, argreg, TCG_AREG0); @@ -1636,7 +1622,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) /* 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_bl(s, COND_NE, 0); tcg_out_qemu_ld_index(s, opc, datalo, datahi, addrlo, addend); @@ -1768,7 +1754,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) /* 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); + tcg_out_bl(s, COND_NE, 0); add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, s->code_ptr, label_ptr); diff --git a/tcg/i386/tcg-target.h b/tcg/i386/tcg-target.h index 9fdf37f23c..7995fe3eab 100644 --- a/tcg/i386/tcg-target.h +++ b/tcg/i386/tcg-target.h @@ -84,10 +84,12 @@ typedef enum { TCG_REG_RBP = TCG_REG_EBP, TCG_REG_RSI = TCG_REG_ESI, TCG_REG_RDI = TCG_REG_EDI, + + TCG_AREG0 = TCG_REG_EBP, + TCG_REG_CALL_STACK = TCG_REG_ESP } TCGReg; /* used for function call generation */ -#define TCG_REG_CALL_STACK TCG_REG_ESP #define TCG_TARGET_STACK_ALIGN 16 #if defined(_WIN64) #define TCG_TARGET_CALL_STACK_OFFSET 32 @@ -133,8 +135,9 @@ extern bool have_avx2; #define TCG_TARGET_HAS_direct_jump 1 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_extrl_i64_i32 0 -#define TCG_TARGET_HAS_extrh_i64_i32 0 +/* Keep target addresses zero-extended in a register. */ +#define TCG_TARGET_HAS_extrl_i64_i32 (TARGET_LONG_BITS == 32) +#define TCG_TARGET_HAS_extrh_i64_i32 (TARGET_LONG_BITS == 32) #define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_ext8s_i64 1 @@ -182,6 +185,8 @@ extern bool have_avx2; #define TCG_TARGET_HAS_shv_vec 0 #define TCG_TARGET_HAS_cmp_vec 1 #define TCG_TARGET_HAS_mul_vec 1 +#define TCG_TARGET_HAS_sat_vec 1 +#define TCG_TARGET_HAS_minmax_vec 1 #define TCG_TARGET_deposit_i32_valid(ofs, len) \ (((ofs) == 0 && (len) == 8) || ((ofs) == 8 && (len) == 8) || \ @@ -194,12 +199,6 @@ extern bool have_avx2; #define TCG_TARGET_extract_i64_valid(ofs, len) \ (((ofs) == 8 && (len) == 8) || ((ofs) + (len)) == 32) -#if TCG_TARGET_REG_BITS == 64 -# define TCG_AREG0 TCG_REG_R14 -#else -# define TCG_AREG0 TCG_REG_EBP -#endif - static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { } @@ -223,6 +222,8 @@ static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 + #ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS #endif diff --git a/tcg/i386/tcg-target.inc.c b/tcg/i386/tcg-target.inc.c index a91e4f1313..e0670e5098 100644 --- a/tcg/i386/tcg-target.inc.c +++ b/tcg/i386/tcg-target.inc.c @@ -167,7 +167,7 @@ static bool have_lzcnt; static tcg_insn_unit *tb_ret_addr; -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { value += addend; @@ -175,7 +175,7 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, case R_386_PC32: value -= (uintptr_t)code_ptr; if (value != (int32_t)value) { - tcg_abort(); + return false; } /* FALLTHRU */ case R_386_32: @@ -184,13 +184,14 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, case R_386_PC8: value -= (uintptr_t)code_ptr; if (value != (int8_t)value) { - tcg_abort(); + return false; } tcg_patch8(code_ptr, value); break; default: tcg_abort(); } + return true; } #if TCG_TARGET_REG_BITS == 64 @@ -302,23 +303,17 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type, 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 @@ -334,6 +329,7 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type, #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_AND_GvEv (OPC_ARITH_GvEv | (ARITH_AND << 3)) #define OPC_BLENDPS (0x0c | P_EXT3A | P_DATA16) #define OPC_BSF (0xbc | P_EXT) #define OPC_BSR (0xbd | P_EXT) @@ -382,6 +378,10 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type, #define OPC_PADDW (0xfd | P_EXT | P_DATA16) #define OPC_PADDD (0xfe | P_EXT | P_DATA16) #define OPC_PADDQ (0xd4 | P_EXT | P_DATA16) +#define OPC_PADDSB (0xec | P_EXT | P_DATA16) +#define OPC_PADDSW (0xed | P_EXT | P_DATA16) +#define OPC_PADDUB (0xdc | P_EXT | P_DATA16) +#define OPC_PADDUW (0xdd | P_EXT | P_DATA16) #define OPC_PAND (0xdb | P_EXT | P_DATA16) #define OPC_PANDN (0xdf | P_EXT | P_DATA16) #define OPC_PBLENDW (0x0e | P_EXT3A | P_DATA16) @@ -393,6 +393,18 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type, #define OPC_PCMPGTW (0x65 | P_EXT | P_DATA16) #define OPC_PCMPGTD (0x66 | P_EXT | P_DATA16) #define OPC_PCMPGTQ (0x37 | P_EXT38 | P_DATA16) +#define OPC_PMAXSB (0x3c | P_EXT38 | P_DATA16) +#define OPC_PMAXSW (0xee | P_EXT | P_DATA16) +#define OPC_PMAXSD (0x3d | P_EXT38 | P_DATA16) +#define OPC_PMAXUB (0xde | P_EXT | P_DATA16) +#define OPC_PMAXUW (0x3e | P_EXT38 | P_DATA16) +#define OPC_PMAXUD (0x3f | P_EXT38 | P_DATA16) +#define OPC_PMINSB (0x38 | P_EXT38 | P_DATA16) +#define OPC_PMINSW (0xea | P_EXT | P_DATA16) +#define OPC_PMINSD (0x39 | P_EXT38 | P_DATA16) +#define OPC_PMINUB (0xda | P_EXT | P_DATA16) +#define OPC_PMINUW (0x3a | P_EXT38 | P_DATA16) +#define OPC_PMINUD (0x3b | P_EXT38 | P_DATA16) #define OPC_PMOVSXBW (0x20 | P_EXT38 | P_DATA16) #define OPC_PMOVSXWD (0x23 | P_EXT38 | P_DATA16) #define OPC_PMOVSXDQ (0x25 | P_EXT38 | P_DATA16) @@ -413,6 +425,10 @@ static inline int tcg_target_const_match(tcg_target_long val, TCGType type, #define OPC_PSUBW (0xf9 | P_EXT | P_DATA16) #define OPC_PSUBD (0xfa | P_EXT | P_DATA16) #define OPC_PSUBQ (0xfb | P_EXT | P_DATA16) +#define OPC_PSUBSB (0xe8 | P_EXT | P_DATA16) +#define OPC_PSUBSW (0xe9 | P_EXT | P_DATA16) +#define OPC_PSUBUB (0xd8 | P_EXT | P_DATA16) +#define OPC_PSUBUW (0xd9 | P_EXT | P_DATA16) #define OPC_PUNPCKLBW (0x60 | P_EXT | P_DATA16) #define OPC_PUNPCKLWD (0x61 | P_EXT | P_DATA16) #define OPC_PUNPCKLDQ (0x62 | P_EXT | P_DATA16) @@ -531,9 +547,6 @@ static void tcg_out_opc(TCGContext *s, int opc, int r, int rm, int x) tcg_debug_assert((opc & P_REXW) == 0); tcg_out8(s, 0x66); } - if (opc & P_ADDR32) { - tcg_out8(s, 0x67); - } if (opc & P_SIMDF3) { tcg_out8(s, 0xf3); } else if (opc & P_SIMDF2) { @@ -1629,7 +1642,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, } if (TCG_TYPE_PTR == TCG_TYPE_I64) { hrexw = P_REXW; - if (TARGET_PAGE_BITS + CPU_TLB_BITS > 32) { + if (TARGET_PAGE_BITS + CPU_TLB_DYN_MAX_BITS > 32) { tlbtype = TCG_TYPE_I64; tlbrexw = P_REXW; } @@ -1637,6 +1650,15 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, } tcg_out_mov(s, tlbtype, r0, addrlo); + tcg_out_shifti(s, SHIFT_SHR + tlbrexw, r0, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + + tcg_out_modrm_offset(s, OPC_AND_GvEv + trexw, r0, TCG_AREG0, + offsetof(CPUArchState, tlb_mask[mem_index])); + + tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r0, TCG_AREG0, + offsetof(CPUArchState, tlb_table[mem_index])); + /* If the required alignment is at least as large as the access, simply copy the address and mask. For lesser alignments, check that we don't cross pages for the complete access. */ @@ -1646,27 +1668,13 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, tcg_out_modrm_offset(s, OPC_LEA + trexw, r1, addrlo, s_mask - a_mask); } tlb_mask = (target_ulong)TARGET_PAGE_MASK | a_mask; - - tcg_out_shifti(s, SHIFT_SHR + tlbrexw, r0, - TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); - tgen_arithi(s, ARITH_AND + trexw, r1, tlb_mask, 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); + tcg_out_modrm_offset(s, OPC_CMP_GvEv + trexw, r1, r0, which); /* 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. */ + path function argument setup. */ tcg_out_mov(s, ttype, r1, addrlo); /* jne slow_path */ @@ -1676,7 +1684,7 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, if (TARGET_LONG_BITS > TCG_TARGET_REG_BITS) { /* cmp 4(r0), addrhi */ - tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, r0, 4); + tcg_out_modrm_offset(s, OPC_CMP_GvEv, addrhi, r0, which + 4); /* jne slow_path */ tcg_out_opc(s, OPC_JCC_long + JCC_JNE, 0, 0, 0); @@ -1688,14 +1696,15 @@ static inline void tcg_out_tlb_load(TCGContext *s, TCGReg addrlo, TCGReg addrhi, /* add addend(r0), r1 */ tcg_out_modrm_offset(s, OPC_ADD_GvEv + hrexw, r1, r0, - offsetof(CPUTLBEntry, addend) - which); + offsetof(CPUTLBEntry, addend)); } /* * 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, +static void add_qemu_ldst_label(TCGContext *s, bool is_ld, bool is_64, + TCGMemOpIdx oi, TCGReg datalo, TCGReg datahi, TCGReg addrlo, TCGReg addrhi, tcg_insn_unit *raddr, @@ -1705,6 +1714,7 @@ static void add_qemu_ldst_label(TCGContext *s, bool is_ld, TCGMemOpIdx oi, label->is_ld = is_ld; label->oi = oi; + label->type = is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32; label->datalo_reg = datalo; label->datahi_reg = datahi; label->addrlo_reg = addrlo; @@ -1725,6 +1735,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) TCGMemOp opc = get_memop(oi); TCGReg data_reg; tcg_insn_unit **label_ptr = &l->label_ptr[0]; + int rexw = (l->type == TCG_TYPE_I64 ? P_REXW : 0); /* resolve label address */ tcg_patch32(label_ptr[0], s->code_ptr - label_ptr[0] - 4); @@ -1763,10 +1774,10 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) data_reg = l->datalo_reg; switch (opc & MO_SSIZE) { case MO_SB: - tcg_out_ext8s(s, data_reg, TCG_REG_EAX, P_REXW); + tcg_out_ext8s(s, data_reg, TCG_REG_EAX, rexw); break; case MO_SW: - tcg_out_ext16s(s, data_reg, TCG_REG_EAX, P_REXW); + tcg_out_ext16s(s, data_reg, TCG_REG_EAX, rexw); break; #if TCG_TARGET_REG_BITS == 64 case MO_SL: @@ -1866,30 +1877,49 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *l) 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 - +#elif TCG_TARGET_REG_BITS == 32 +# define x86_guest_base_seg 0 +# define x86_guest_base_index -1 +# define x86_guest_base_offset guest_base +#else +static int x86_guest_base_seg; +static int x86_guest_base_index = -1; +static int32_t x86_guest_base_offset; +# if 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) +static inline int setup_guest_base_seg(void) { if (arch_prctl(ARCH_SET_GS, guest_base) == 0) { - guest_base_flags = P_GS; + return P_GS; } + return 0; } -#else -# define guest_base_flags 0 -static inline void setup_guest_base_seg(void) { } +# elif defined (__FreeBSD__) || defined (__FreeBSD_kernel__) +# include +static inline int setup_guest_base_seg(void) +{ + if (sysarch(AMD64_SET_GSBASE, &guest_base) == 0) { + return P_GS; + } + return 0; +} +# else +static inline int setup_guest_base_seg(void) +{ + return 0; +} +# endif #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) + int seg, bool is64, TCGMemOp memop) { const TCGMemOp real_bswap = memop & MO_BSWAP; TCGMemOp bswap = real_bswap; + int rexw = is64 * P_REXW; int movop = OPC_MOVL_GvEv; if (have_movbe && real_bswap) { @@ -1903,7 +1933,7 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, base, index, 0, ofs); break; case MO_SB: - tcg_out_modrm_sib_offset(s, OPC_MOVSBL + P_REXW + seg, datalo, + tcg_out_modrm_sib_offset(s, OPC_MOVSBL + rexw + seg, datalo, base, index, 0, ofs); break; case MO_UW: @@ -1923,9 +1953,9 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, base, index, 0, ofs); tcg_out_rolw_8(s, datalo); } - tcg_out_modrm(s, OPC_MOVSWL + P_REXW, datalo, datalo); + tcg_out_modrm(s, OPC_MOVSWL + rexw, datalo, datalo); } else { - tcg_out_modrm_sib_offset(s, OPC_MOVSWL + P_REXW + seg, + tcg_out_modrm_sib_offset(s, OPC_MOVSWL + rexw + seg, datalo, base, index, 0, ofs); } break; @@ -2013,49 +2043,21 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is64) label_ptr, offsetof(CPUTLBEntry, addr_read)); /* TLB Hit. */ - tcg_out_qemu_ld_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, opc); + tcg_out_qemu_ld_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, is64, opc); /* Record the current context of a load into ldst label */ - add_qemu_ldst_label(s, true, oi, datalo, datahi, addrlo, addrhi, + add_qemu_ldst_label(s, true, is64, 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); - } + tcg_out_qemu_ld_direct(s, datalo, datahi, addrlo, x86_guest_base_index, + x86_guest_base_offset, x86_guest_base_seg, + is64, opc); #endif } static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg base, intptr_t ofs, int seg, - TCGMemOp memop) + TCGReg base, int index, 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 @@ -2079,8 +2081,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, 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); + tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + seg, + datalo, base, index, 0, ofs); break; case MO_16: if (bswap) { @@ -2088,7 +2090,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, tcg_out_rolw_8(s, scratch); datalo = scratch; } - tcg_out_modrm_offset(s, movop + P_DATA16 + seg, datalo, base, ofs); + tcg_out_modrm_sib_offset(s, movop + P_DATA16 + seg, datalo, + base, index, 0, ofs); break; case MO_32: if (bswap) { @@ -2096,7 +2099,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, tcg_out_bswap32(s, scratch); datalo = scratch; } - tcg_out_modrm_offset(s, movop + seg, datalo, base, ofs); + tcg_out_modrm_sib_offset(s, movop + seg, datalo, base, index, 0, ofs); break; case MO_64: if (TCG_TARGET_REG_BITS == 64) { @@ -2105,22 +2108,27 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, tcg_out_bswap64(s, scratch); datalo = scratch; } - tcg_out_modrm_offset(s, movop + P_REXW + seg, datalo, base, ofs); + tcg_out_modrm_sib_offset(s, movop + P_REXW + seg, datalo, + base, index, 0, 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_modrm_sib_offset(s, OPC_MOVL_EvGv + seg, scratch, + base, index, 0, 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); + tcg_out_modrm_sib_offset(s, OPC_MOVL_EvGv + seg, scratch, + base, index, 0, 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); + 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); } break; default: @@ -2153,44 +2161,14 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is64) label_ptr, offsetof(CPUTLBEntry, addr_write)); /* TLB Hit. */ - tcg_out_qemu_st_direct(s, datalo, datahi, TCG_REG_L1, 0, 0, opc); + tcg_out_qemu_st_direct(s, datalo, datahi, TCG_REG_L1, -1, 0, 0, opc); /* Record the current context of a store into ldst label */ - add_qemu_ldst_label(s, false, oi, datalo, datahi, addrlo, addrhi, + add_qemu_ldst_label(s, false, is64, 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); - } + tcg_out_qemu_st_direct(s, datalo, datahi, addrlo, x86_guest_base_index, + x86_guest_base_offset, x86_guest_base_seg, opc); #endif } @@ -2548,12 +2526,16 @@ static inline void tcg_out_op(TCGContext *s, TCGOpcode opc, break; case INDEX_op_extu_i32_i64: case INDEX_op_ext32u_i64: + case INDEX_op_extrl_i64_i32: tcg_out_ext32u(s, a0, a1); break; case INDEX_op_ext_i32_i64: case INDEX_op_ext32s_i64: tcg_out_ext32s(s, a0, a1); break; + case INDEX_op_extrh_i64_i32: + tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); + break; #endif OP_32_64(deposit): @@ -2629,9 +2611,21 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, static int const add_insn[4] = { OPC_PADDB, OPC_PADDW, OPC_PADDD, OPC_PADDQ }; + static int const ssadd_insn[4] = { + OPC_PADDSB, OPC_PADDSW, OPC_UD2, OPC_UD2 + }; + static int const usadd_insn[4] = { + OPC_PADDUB, OPC_PADDUW, OPC_UD2, OPC_UD2 + }; static int const sub_insn[4] = { OPC_PSUBB, OPC_PSUBW, OPC_PSUBD, OPC_PSUBQ }; + static int const sssub_insn[4] = { + OPC_PSUBSB, OPC_PSUBSW, OPC_UD2, OPC_UD2 + }; + static int const ussub_insn[4] = { + OPC_PSUBUB, OPC_PSUBUW, OPC_UD2, OPC_UD2 + }; static int const mul_insn[4] = { OPC_UD2, OPC_PMULLW, OPC_PMULLD, OPC_UD2 }; @@ -2656,6 +2650,18 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, static int const packus_insn[4] = { OPC_PACKUSWB, OPC_PACKUSDW, OPC_UD2, OPC_UD2 }; + static int const smin_insn[4] = { + OPC_PMINSB, OPC_PMINSW, OPC_PMINSD, OPC_UD2 + }; + static int const smax_insn[4] = { + OPC_PMAXSB, OPC_PMAXSW, OPC_PMAXSD, OPC_UD2 + }; + static int const umin_insn[4] = { + OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_UD2 + }; + static int const umax_insn[4] = { + OPC_PMAXUB, OPC_PMAXUW, OPC_PMAXUD, OPC_UD2 + }; TCGType type = vecl + TCG_TYPE_V64; int insn, sub; @@ -2669,9 +2675,21 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_add_vec: insn = add_insn[vece]; goto gen_simd; + case INDEX_op_ssadd_vec: + insn = ssadd_insn[vece]; + goto gen_simd; + case INDEX_op_usadd_vec: + insn = usadd_insn[vece]; + goto gen_simd; case INDEX_op_sub_vec: insn = sub_insn[vece]; goto gen_simd; + case INDEX_op_sssub_vec: + insn = sssub_insn[vece]; + goto gen_simd; + case INDEX_op_ussub_vec: + insn = ussub_insn[vece]; + goto gen_simd; case INDEX_op_mul_vec: insn = mul_insn[vece]; goto gen_simd; @@ -2684,6 +2702,18 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, case INDEX_op_xor_vec: insn = OPC_PXOR; goto gen_simd; + case INDEX_op_smin_vec: + insn = smin_insn[vece]; + goto gen_simd; + case INDEX_op_umin_vec: + insn = umin_insn[vece]; + goto gen_simd; + case INDEX_op_smax_vec: + insn = smax_insn[vece]; + goto gen_simd; + case INDEX_op_umax_vec: + insn = umax_insn[vece]; + goto gen_simd; case INDEX_op_x86_punpckl_vec: insn = punpckl_insn[vece]; goto gen_simd; @@ -2917,6 +2947,7 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: + case INDEX_op_extrh_i64_i32: return &r_0; case INDEX_op_ext8s_i32: @@ -2932,6 +2963,7 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: case INDEX_op_extract_i32: case INDEX_op_extract_i64: case INDEX_op_sextract_i32: @@ -3043,6 +3075,14 @@ static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) case INDEX_op_or_vec: case INDEX_op_xor_vec: case INDEX_op_andc_vec: + case INDEX_op_ssadd_vec: + case INDEX_op_usadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_ussub_vec: + case INDEX_op_smin_vec: + case INDEX_op_umin_vec: + case INDEX_op_smax_vec: + case INDEX_op_umax_vec: case INDEX_op_cmp_vec: case INDEX_op_x86_shufps_vec: case INDEX_op_x86_blend_vec: @@ -3110,258 +3150,310 @@ int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) } return 1; + case INDEX_op_ssadd_vec: + case INDEX_op_usadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_ussub_vec: + return vece <= MO_16; + case INDEX_op_smin_vec: + case INDEX_op_smax_vec: + case INDEX_op_umin_vec: + case INDEX_op_umax_vec: + return vece <= MO_32 ? 1 : -1; + default: return 0; } } +static void expand_vec_shi(TCGType type, unsigned vece, bool shr, + TCGv_vec v0, TCGv_vec v1, TCGArg imm) +{ + TCGv_vec t1, t2; + + tcg_debug_assert(vece == MO_8); + + t1 = tcg_temp_new_vec(type); + t2 = tcg_temp_new_vec(type); + + /* Unpack to W, shift, and repack. Tricky bits: + (1) Use punpck*bw x,x to produce DDCCBBAA, + i.e. duplicate in other half of the 16-bit lane. + (2) For right-shift, add 8 so that the high half of + the lane becomes zero. For left-shift, we must + shift up and down again. + (3) Step 2 leaves high half zero such that PACKUSWB + (pack with unsigned saturation) does not modify + the quantity. */ + vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, + tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); + vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, + tcgv_vec_arg(t2), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); + + if (shr) { + tcg_gen_shri_vec(MO_16, t1, t1, imm + 8); + tcg_gen_shri_vec(MO_16, t2, t2, imm + 8); + } else { + tcg_gen_shli_vec(MO_16, t1, t1, imm + 8); + tcg_gen_shli_vec(MO_16, t2, t2, imm + 8); + tcg_gen_shri_vec(MO_16, t1, t1, 8); + tcg_gen_shri_vec(MO_16, t2, t2, 8); + } + + vec_gen_3(INDEX_op_x86_packus_vec, type, MO_8, + tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t2)); + tcg_temp_free_vec(t1); + tcg_temp_free_vec(t2); +} + +static void expand_vec_sari(TCGType type, unsigned vece, + TCGv_vec v0, TCGv_vec v1, TCGArg imm) +{ + TCGv_vec t1, t2; + + switch (vece) { + case MO_8: + /* Unpack to W, shift, and repack, as in expand_vec_shi. */ + t1 = tcg_temp_new_vec(type); + t2 = tcg_temp_new_vec(type); + vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, + tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); + vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, + tcgv_vec_arg(t2), tcgv_vec_arg(v1), tcgv_vec_arg(v1)); + tcg_gen_sari_vec(MO_16, t1, t1, imm + 8); + tcg_gen_sari_vec(MO_16, t2, t2, imm + 8); + vec_gen_3(INDEX_op_x86_packss_vec, type, MO_8, + tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t2)); + tcg_temp_free_vec(t1); + tcg_temp_free_vec(t2); + break; + + case MO_64: + if (imm <= 32) { + /* We can emulate a small sign extend by performing an arithmetic + * 32-bit shift and overwriting the high half of a 64-bit logical + * shift (note that the ISA says shift of 32 is valid). + */ + t1 = tcg_temp_new_vec(type); + tcg_gen_sari_vec(MO_32, t1, v1, imm); + tcg_gen_shri_vec(MO_64, v0, v1, imm); + vec_gen_4(INDEX_op_x86_blend_vec, type, MO_32, + tcgv_vec_arg(v0), tcgv_vec_arg(v0), + tcgv_vec_arg(t1), 0xaa); + tcg_temp_free_vec(t1); + } else { + /* Otherwise we will need to use a compare vs 0 to produce + * the sign-extend, shift and merge. + */ + t1 = tcg_const_zeros_vec(type); + tcg_gen_cmp_vec(TCG_COND_GT, MO_64, t1, t1, v1); + tcg_gen_shri_vec(MO_64, v0, v1, imm); + tcg_gen_shli_vec(MO_64, t1, t1, 64 - imm); + tcg_gen_or_vec(MO_64, v0, v0, t1); + tcg_temp_free_vec(t1); + } + break; + + default: + g_assert_not_reached(); + } +} + +static void expand_vec_mul(TCGType type, unsigned vece, + TCGv_vec v0, TCGv_vec v1, TCGv_vec v2) +{ + TCGv_vec t1, t2, t3, t4; + + tcg_debug_assert(vece == MO_8); + + /* + * Unpack v1 bytes to words, 0 | x. + * Unpack v2 bytes to words, y | 0. + * This leaves the 8-bit result, x * y, with 8 bits of right padding. + * Shift logical right by 8 bits to clear the high 8 bytes before + * using an unsigned saturated pack. + * + * The difference between the V64, V128 and V256 cases is merely how + * we distribute the expansion between temporaries. + */ + switch (type) { + case TCG_TYPE_V64: + t1 = tcg_temp_new_vec(TCG_TYPE_V128); + t2 = tcg_temp_new_vec(TCG_TYPE_V128); + tcg_gen_dup16i_vec(t2, 0); + vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V128, MO_8, + tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(t2)); + vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V128, MO_8, + tcgv_vec_arg(t2), tcgv_vec_arg(t2), tcgv_vec_arg(v2)); + tcg_gen_mul_vec(MO_16, t1, t1, t2); + tcg_gen_shri_vec(MO_16, t1, t1, 8); + vec_gen_3(INDEX_op_x86_packus_vec, TCG_TYPE_V128, MO_8, + tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t1)); + tcg_temp_free_vec(t1); + tcg_temp_free_vec(t2); + break; + + case TCG_TYPE_V128: + case TCG_TYPE_V256: + t1 = tcg_temp_new_vec(type); + t2 = tcg_temp_new_vec(type); + t3 = tcg_temp_new_vec(type); + t4 = tcg_temp_new_vec(type); + tcg_gen_dup16i_vec(t4, 0); + vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, + tcgv_vec_arg(t1), tcgv_vec_arg(v1), tcgv_vec_arg(t4)); + vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, + tcgv_vec_arg(t2), tcgv_vec_arg(t4), tcgv_vec_arg(v2)); + vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, + tcgv_vec_arg(t3), tcgv_vec_arg(v1), tcgv_vec_arg(t4)); + vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, + tcgv_vec_arg(t4), tcgv_vec_arg(t4), tcgv_vec_arg(v2)); + tcg_gen_mul_vec(MO_16, t1, t1, t2); + tcg_gen_mul_vec(MO_16, t3, t3, t4); + tcg_gen_shri_vec(MO_16, t1, t1, 8); + tcg_gen_shri_vec(MO_16, t3, t3, 8); + vec_gen_3(INDEX_op_x86_packus_vec, type, MO_8, + tcgv_vec_arg(v0), tcgv_vec_arg(t1), tcgv_vec_arg(t3)); + tcg_temp_free_vec(t1); + tcg_temp_free_vec(t2); + tcg_temp_free_vec(t3); + tcg_temp_free_vec(t4); + break; + + default: + g_assert_not_reached(); + } +} + +static void expand_vec_cmp(TCGType type, unsigned vece, TCGv_vec v0, + TCGv_vec v1, TCGv_vec v2, TCGCond cond) +{ + enum { + NEED_SWAP = 1, + NEED_INV = 2, + NEED_BIAS = 4 + }; + static const uint8_t fixups[16] = { + [0 ... 15] = -1, + [TCG_COND_EQ] = 0, + [TCG_COND_NE] = NEED_INV, + [TCG_COND_GT] = 0, + [TCG_COND_LT] = NEED_SWAP, + [TCG_COND_LE] = NEED_INV, + [TCG_COND_GE] = NEED_SWAP | NEED_INV, + [TCG_COND_GTU] = NEED_BIAS, + [TCG_COND_LTU] = NEED_BIAS | NEED_SWAP, + [TCG_COND_LEU] = NEED_BIAS | NEED_INV, + [TCG_COND_GEU] = NEED_BIAS | NEED_SWAP | NEED_INV, + }; + TCGv_vec t1, t2; + uint8_t fixup; + + fixup = fixups[cond & 15]; + tcg_debug_assert(fixup != 0xff); + + if (fixup & NEED_INV) { + cond = tcg_invert_cond(cond); + } + if (fixup & NEED_SWAP) { + t1 = v1, v1 = v2, v2 = t1; + cond = tcg_swap_cond(cond); + } + + t1 = t2 = NULL; + if (fixup & NEED_BIAS) { + t1 = tcg_temp_new_vec(type); + t2 = tcg_temp_new_vec(type); + tcg_gen_dupi_vec(vece, t2, 1ull << ((8 << vece) - 1)); + tcg_gen_sub_vec(vece, t1, v1, t2); + tcg_gen_sub_vec(vece, t2, v2, t2); + v1 = t1; + v2 = t2; + cond = tcg_signed_cond(cond); + } + + tcg_debug_assert(cond == TCG_COND_EQ || cond == TCG_COND_GT); + /* Expand directly; do not recurse. */ + vec_gen_4(INDEX_op_cmp_vec, type, vece, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), tcgv_vec_arg(v2), cond); + + if (t1) { + tcg_temp_free_vec(t1); + if (t2) { + tcg_temp_free_vec(t2); + } + } + if (fixup & NEED_INV) { + tcg_gen_not_vec(vece, v0, v0); + } +} + +static void expand_vec_minmax(TCGType type, unsigned vece, + TCGCond cond, bool min, + TCGv_vec v0, TCGv_vec v1, TCGv_vec v2) +{ + TCGv_vec t1 = tcg_temp_new_vec(type); + + tcg_debug_assert(vece == MO_64); + + tcg_gen_cmp_vec(cond, vece, t1, v1, v2); + if (min) { + TCGv_vec t2; + t2 = v1, v1 = v2, v2 = t2; + } + vec_gen_4(INDEX_op_x86_vpblendvb_vec, type, vece, + tcgv_vec_arg(v0), tcgv_vec_arg(v1), + tcgv_vec_arg(v2), tcgv_vec_arg(t1)); + tcg_temp_free_vec(t1); +} + void tcg_expand_vec_op(TCGOpcode opc, TCGType type, unsigned vece, TCGArg a0, ...) { va_list va; - TCGArg a1, a2; - TCGv_vec v0, t1, t2, t3, t4; + TCGArg a2; + TCGv_vec v0, v1, v2; va_start(va, a0); v0 = temp_tcgv_vec(arg_temp(a0)); + v1 = temp_tcgv_vec(arg_temp(va_arg(va, TCGArg))); + a2 = va_arg(va, TCGArg); switch (opc) { case INDEX_op_shli_vec: case INDEX_op_shri_vec: - tcg_debug_assert(vece == MO_8); - a1 = va_arg(va, TCGArg); - a2 = va_arg(va, TCGArg); - /* Unpack to W, shift, and repack. Tricky bits: - (1) Use punpck*bw x,x to produce DDCCBBAA, - i.e. duplicate in other half of the 16-bit lane. - (2) For right-shift, add 8 so that the high half of - the lane becomes zero. For left-shift, we must - shift up and down again. - (3) Step 2 leaves high half zero such that PACKUSWB - (pack with unsigned saturation) does not modify - the quantity. */ - t1 = tcg_temp_new_vec(type); - t2 = tcg_temp_new_vec(type); - vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, - tcgv_vec_arg(t1), a1, a1); - vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, - tcgv_vec_arg(t2), a1, a1); - if (opc == INDEX_op_shri_vec) { - vec_gen_3(INDEX_op_shri_vec, type, MO_16, - tcgv_vec_arg(t1), tcgv_vec_arg(t1), a2 + 8); - vec_gen_3(INDEX_op_shri_vec, type, MO_16, - tcgv_vec_arg(t2), tcgv_vec_arg(t2), a2 + 8); - } else { - vec_gen_3(INDEX_op_shli_vec, type, MO_16, - tcgv_vec_arg(t1), tcgv_vec_arg(t1), a2 + 8); - vec_gen_3(INDEX_op_shli_vec, type, MO_16, - tcgv_vec_arg(t2), tcgv_vec_arg(t2), a2 + 8); - vec_gen_3(INDEX_op_shri_vec, type, MO_16, - tcgv_vec_arg(t1), tcgv_vec_arg(t1), 8); - vec_gen_3(INDEX_op_shri_vec, type, MO_16, - tcgv_vec_arg(t2), tcgv_vec_arg(t2), 8); - } - vec_gen_3(INDEX_op_x86_packus_vec, type, MO_8, - a0, tcgv_vec_arg(t1), tcgv_vec_arg(t2)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); + expand_vec_shi(type, vece, opc == INDEX_op_shri_vec, v0, v1, a2); break; case INDEX_op_sari_vec: - a1 = va_arg(va, TCGArg); - a2 = va_arg(va, TCGArg); - if (vece == MO_8) { - /* Unpack to W, shift, and repack, as above. */ - t1 = tcg_temp_new_vec(type); - t2 = tcg_temp_new_vec(type); - vec_gen_3(INDEX_op_x86_punpckl_vec, type, MO_8, - tcgv_vec_arg(t1), a1, a1); - vec_gen_3(INDEX_op_x86_punpckh_vec, type, MO_8, - tcgv_vec_arg(t2), a1, a1); - vec_gen_3(INDEX_op_sari_vec, type, MO_16, - tcgv_vec_arg(t1), tcgv_vec_arg(t1), a2 + 8); - vec_gen_3(INDEX_op_sari_vec, type, MO_16, - tcgv_vec_arg(t2), tcgv_vec_arg(t2), a2 + 8); - vec_gen_3(INDEX_op_x86_packss_vec, type, MO_8, - a0, tcgv_vec_arg(t1), tcgv_vec_arg(t2)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); - break; - } - tcg_debug_assert(vece == MO_64); - /* MO_64: If the shift is <= 32, we can emulate the sign extend by - performing an arithmetic 32-bit shift and overwriting the high - half of the result (note that the ISA says shift of 32 is valid). */ - if (a2 <= 32) { - t1 = tcg_temp_new_vec(type); - vec_gen_3(INDEX_op_sari_vec, type, MO_32, tcgv_vec_arg(t1), a1, a2); - vec_gen_3(INDEX_op_shri_vec, type, MO_64, a0, a1, a2); - vec_gen_4(INDEX_op_x86_blend_vec, type, MO_32, - a0, a0, tcgv_vec_arg(t1), 0xaa); - tcg_temp_free_vec(t1); - break; - } - /* Otherwise we will need to use a compare vs 0 to produce the - sign-extend, shift and merge. */ - t1 = tcg_temp_new_vec(type); - t2 = tcg_const_zeros_vec(type); - vec_gen_4(INDEX_op_cmp_vec, type, MO_64, - tcgv_vec_arg(t1), tcgv_vec_arg(t2), a1, TCG_COND_GT); - tcg_temp_free_vec(t2); - vec_gen_3(INDEX_op_shri_vec, type, MO_64, a0, a1, a2); - vec_gen_3(INDEX_op_shli_vec, type, MO_64, - tcgv_vec_arg(t1), tcgv_vec_arg(t1), 64 - a2); - vec_gen_3(INDEX_op_or_vec, type, MO_64, a0, a0, tcgv_vec_arg(t1)); - tcg_temp_free_vec(t1); + expand_vec_sari(type, vece, v0, v1, a2); break; case INDEX_op_mul_vec: - tcg_debug_assert(vece == MO_8); - a1 = va_arg(va, TCGArg); - a2 = va_arg(va, TCGArg); - switch (type) { - case TCG_TYPE_V64: - t1 = tcg_temp_new_vec(TCG_TYPE_V128); - t2 = tcg_temp_new_vec(TCG_TYPE_V128); - tcg_gen_dup16i_vec(t2, 0); - vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V128, MO_8, - tcgv_vec_arg(t1), a1, tcgv_vec_arg(t2)); - vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V128, MO_8, - tcgv_vec_arg(t2), tcgv_vec_arg(t2), a2); - tcg_gen_mul_vec(MO_16, t1, t1, t2); - tcg_gen_shri_vec(MO_16, t1, t1, 8); - vec_gen_3(INDEX_op_x86_packus_vec, TCG_TYPE_V128, MO_8, - a0, tcgv_vec_arg(t1), tcgv_vec_arg(t1)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); - break; - - case TCG_TYPE_V128: - t1 = tcg_temp_new_vec(TCG_TYPE_V128); - t2 = tcg_temp_new_vec(TCG_TYPE_V128); - t3 = tcg_temp_new_vec(TCG_TYPE_V128); - t4 = tcg_temp_new_vec(TCG_TYPE_V128); - tcg_gen_dup16i_vec(t4, 0); - vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V128, MO_8, - tcgv_vec_arg(t1), a1, tcgv_vec_arg(t4)); - vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V128, MO_8, - tcgv_vec_arg(t2), tcgv_vec_arg(t4), a2); - vec_gen_3(INDEX_op_x86_punpckh_vec, TCG_TYPE_V128, MO_8, - tcgv_vec_arg(t3), a1, tcgv_vec_arg(t4)); - vec_gen_3(INDEX_op_x86_punpckh_vec, TCG_TYPE_V128, MO_8, - tcgv_vec_arg(t4), tcgv_vec_arg(t4), a2); - tcg_gen_mul_vec(MO_16, t1, t1, t2); - tcg_gen_mul_vec(MO_16, t3, t3, t4); - tcg_gen_shri_vec(MO_16, t1, t1, 8); - tcg_gen_shri_vec(MO_16, t3, t3, 8); - vec_gen_3(INDEX_op_x86_packus_vec, TCG_TYPE_V128, MO_8, - a0, tcgv_vec_arg(t1), tcgv_vec_arg(t3)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t3); - tcg_temp_free_vec(t4); - break; - - case TCG_TYPE_V256: - t1 = tcg_temp_new_vec(TCG_TYPE_V256); - t2 = tcg_temp_new_vec(TCG_TYPE_V256); - t3 = tcg_temp_new_vec(TCG_TYPE_V256); - t4 = tcg_temp_new_vec(TCG_TYPE_V256); - tcg_gen_dup16i_vec(t4, 0); - /* a1: A[0-7] ... D[0-7]; a2: W[0-7] ... Z[0-7] - t1: extends of B[0-7], D[0-7] - t2: extends of X[0-7], Z[0-7] - t3: extends of A[0-7], C[0-7] - t4: extends of W[0-7], Y[0-7]. */ - vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V256, MO_8, - tcgv_vec_arg(t1), a1, tcgv_vec_arg(t4)); - vec_gen_3(INDEX_op_x86_punpckl_vec, TCG_TYPE_V256, MO_8, - tcgv_vec_arg(t2), tcgv_vec_arg(t4), a2); - vec_gen_3(INDEX_op_x86_punpckh_vec, TCG_TYPE_V256, MO_8, - tcgv_vec_arg(t3), a1, tcgv_vec_arg(t4)); - vec_gen_3(INDEX_op_x86_punpckh_vec, TCG_TYPE_V256, MO_8, - tcgv_vec_arg(t4), tcgv_vec_arg(t4), a2); - /* t1: BX DZ; t2: AW CY. */ - tcg_gen_mul_vec(MO_16, t1, t1, t2); - tcg_gen_mul_vec(MO_16, t3, t3, t4); - tcg_gen_shri_vec(MO_16, t1, t1, 8); - tcg_gen_shri_vec(MO_16, t3, t3, 8); - /* a0: AW BX CY DZ. */ - vec_gen_3(INDEX_op_x86_packus_vec, TCG_TYPE_V256, MO_8, - a0, tcgv_vec_arg(t1), tcgv_vec_arg(t3)); - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); - tcg_temp_free_vec(t3); - tcg_temp_free_vec(t4); - break; - - default: - g_assert_not_reached(); - } + v2 = temp_tcgv_vec(arg_temp(a2)); + expand_vec_mul(type, vece, v0, v1, v2); break; case INDEX_op_cmp_vec: - { - enum { - NEED_SWAP = 1, - NEED_INV = 2, - NEED_BIAS = 4 - }; - static const uint8_t fixups[16] = { - [0 ... 15] = -1, - [TCG_COND_EQ] = 0, - [TCG_COND_NE] = NEED_INV, - [TCG_COND_GT] = 0, - [TCG_COND_LT] = NEED_SWAP, - [TCG_COND_LE] = NEED_INV, - [TCG_COND_GE] = NEED_SWAP | NEED_INV, - [TCG_COND_GTU] = NEED_BIAS, - [TCG_COND_LTU] = NEED_BIAS | NEED_SWAP, - [TCG_COND_LEU] = NEED_BIAS | NEED_INV, - [TCG_COND_GEU] = NEED_BIAS | NEED_SWAP | NEED_INV, - }; + v2 = temp_tcgv_vec(arg_temp(a2)); + expand_vec_cmp(type, vece, v0, v1, v2, va_arg(va, TCGArg)); + break; - TCGCond cond; - uint8_t fixup; - - a1 = va_arg(va, TCGArg); - a2 = va_arg(va, TCGArg); - cond = va_arg(va, TCGArg); - fixup = fixups[cond & 15]; - tcg_debug_assert(fixup != 0xff); - - if (fixup & NEED_INV) { - cond = tcg_invert_cond(cond); - } - if (fixup & NEED_SWAP) { - TCGArg t; - t = a1, a1 = a2, a2 = t; - cond = tcg_swap_cond(cond); - } - - t1 = t2 = NULL; - if (fixup & NEED_BIAS) { - t1 = tcg_temp_new_vec(type); - t2 = tcg_temp_new_vec(type); - tcg_gen_dupi_vec(vece, t2, 1ull << ((8 << vece) - 1)); - tcg_gen_sub_vec(vece, t1, temp_tcgv_vec(arg_temp(a1)), t2); - tcg_gen_sub_vec(vece, t2, temp_tcgv_vec(arg_temp(a2)), t2); - a1 = tcgv_vec_arg(t1); - a2 = tcgv_vec_arg(t2); - cond = tcg_signed_cond(cond); - } - - tcg_debug_assert(cond == TCG_COND_EQ || cond == TCG_COND_GT); - vec_gen_4(INDEX_op_cmp_vec, type, vece, a0, a1, a2, cond); - - if (fixup & NEED_BIAS) { - tcg_temp_free_vec(t1); - tcg_temp_free_vec(t2); - } - if (fixup & NEED_INV) { - tcg_gen_not_vec(vece, v0, v0); - } - } + case INDEX_op_smin_vec: + v2 = temp_tcgv_vec(arg_temp(a2)); + expand_vec_minmax(type, vece, TCG_COND_GT, true, v0, v1, v2); + break; + case INDEX_op_smax_vec: + v2 = temp_tcgv_vec(arg_temp(a2)); + expand_vec_minmax(type, vece, TCG_COND_GT, false, v0, v1, v2); + break; + case INDEX_op_umin_vec: + v2 = temp_tcgv_vec(arg_temp(a2)); + expand_vec_minmax(type, vece, TCG_COND_GTU, true, v0, v1, v2); + break; + case INDEX_op_umax_vec: + v2 = temp_tcgv_vec(arg_temp(a2)); + expand_vec_minmax(type, vece, TCG_COND_GTU, false, v0, v1, v2); break; default: @@ -3428,9 +3520,24 @@ static void tcg_target_qemu_prologue(TCGContext *s) 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); + (ARRAY_SIZE(tcg_target_callee_save_regs) + 2) * 4 + + stack_addend); #else +# if !defined(CONFIG_SOFTMMU) && TCG_TARGET_REG_BITS == 64 + if (guest_base) { + int seg = setup_guest_base_seg(); + if (seg != 0) { + x86_guest_base_seg = seg; + } else if (guest_base == (int32_t)guest_base) { + x86_guest_base_offset = guest_base; + } else { + /* Choose R12 because, as a base, it requires a SIB byte. */ + x86_guest_base_index = TCG_REG_R12; + tcg_out_mov(s, TCG_TYPE_PTR, x86_guest_base_index, guest_base); + tcg_regset_set_reg(s->reserved_regs, x86_guest_base_index); + } + } +# endif 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. */ @@ -3456,13 +3563,6 @@ static void tcg_target_qemu_prologue(TCGContext *s) 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_out_nop_fill(tcg_insn_unit *p, int count) diff --git a/tcg/mips/tcg-target.h b/tcg/mips/tcg-target.h index a8222476f0..5cb8672470 100644 --- a/tcg/mips/tcg-target.h +++ b/tcg/mips/tcg-target.h @@ -203,6 +203,7 @@ extern bool use_mips32r2_instructions; #endif #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/tcg/mips/tcg-target.inc.c b/tcg/mips/tcg-target.inc.c index cff525373b..8a92e916dd 100644 --- a/tcg/mips/tcg-target.inc.c +++ b/tcg/mips/tcg-target.inc.c @@ -168,12 +168,13 @@ 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, +static bool 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); + return true; } #define TCG_CT_CONST_ZERO 0x100 @@ -483,12 +484,7 @@ static inline void tcg_out_opc_bf64(TCGContext *s, MIPSInsn opc, MIPSInsn opm, 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); + tcg_out_opc_imm(s, opc, rt, rs, 0); } /* @@ -796,7 +792,7 @@ static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, 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_sa(s, OPC_SRL, TCG_TMP0, al, TCG_TARGET_REG_BITS - 1); tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); } else { tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); @@ -1205,8 +1201,19 @@ static int tcg_out_call_iarg_reg2(TCGContext *s, int i, TCGReg al, TCGReg ah) return i; } -/* Perform the tlb comparison operation. The complete host address is - placed in BASE. Clobbers TMP0, TMP1, TMP2, A0. */ +/* We expect tlb_mask to be before tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) < + offsetof(CPUArchState, tlb_mask)); + +/* We expect tlb_mask to be "near" tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) - + offsetof(CPUArchState, tlb_mask) >= 0x8000); + +/* + * Perform the tlb comparison operation. + * The complete host address is placed in BASE. + * Clobbers TMP0, TMP1, TMP2, TMP3. + */ 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) @@ -1214,52 +1221,73 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, TCGMemOp opc = get_memop(oi); unsigned s_bits = opc & MO_SIZE; unsigned a_bits = get_alignment_bits(opc); - target_ulong mask; 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); + int mask_off = offsetof(CPUArchState, tlb_mask[mem_index]); + int table_off = offsetof(CPUArchState, tlb_table[mem_index]); + int add_off = offsetof(CPUTLBEntry, addend); + int cmp_off = (is_load ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + TCGReg mask_base = TCG_AREG0, table_base = TCG_AREG0; + target_ulong mask; - tcg_out_opc_sa(s, ALIAS_TSRL, 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, ALIAS_PADD, TCG_REG_A0, TCG_REG_A0, TCG_AREG0); + if (table_off > 0x7fff) { + int mask_hi = mask_off - (int16_t)mask_off; + int table_hi = table_off - (int16_t)table_off; - /* Compensate for very large offsets. */ - while (add_off >= 0x8000) { - /* Most target env are smaller than 32k, but a few are larger than 64k, - * so handle an arbitrarily large offset. - */ - tcg_out_opc_imm(s, ALIAS_PADDI, TCG_REG_A0, TCG_REG_A0, 0x7ff0); - cmp_off -= 0x7ff0; - add_off -= 0x7ff0; + table_base = TCG_TMP1; + if (likely(mask_hi == table_hi)) { + mask_base = table_base; + tcg_out_opc_imm(s, OPC_LUI, mask_base, TCG_REG_ZERO, mask_hi >> 16); + tcg_out_opc_reg(s, ALIAS_PADD, mask_base, mask_base, TCG_AREG0); + mask_off -= mask_hi; + table_off -= mask_hi; + } else { + if (mask_hi != 0) { + mask_base = TCG_TMP0; + tcg_out_opc_imm(s, OPC_LUI, + mask_base, TCG_REG_ZERO, mask_hi >> 16); + tcg_out_opc_reg(s, ALIAS_PADD, + mask_base, mask_base, TCG_AREG0); + } + table_off -= mask_off; + mask_off -= mask_hi; + tcg_out_opc_imm(s, ALIAS_PADDI, table_base, mask_base, mask_off); + } } + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP0, mask_base, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP1, table_base, table_off); + + /* Extract the TLB index from the address into TMP3. */ + tcg_out_opc_sa(s, ALIAS_TSRL, TCG_TMP3, addrl, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); + + /* Add the tlb_table pointer, creating the CPUTLBEntry address in TMP3. */ + tcg_out_opc_reg(s, ALIAS_PADD, TCG_TMP3, TCG_TMP3, TCG_TMP1); + /* We don't currently support unaligned accesses. We could do so with mips32r6. */ if (a_bits < s_bits) { a_bits = s_bits; } + /* Mask the page bits, keeping the alignment bits to compare against. */ mask = (target_ulong)TARGET_PAGE_MASK | ((1 << a_bits) - 1); - /* Load the (low half) tlb comparator. Mask the page bits, keeping the - alignment bits to compare against. */ + /* Load the (low-half) tlb comparator. */ if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { - tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_REG_A0, cmp_off + LO_OFF); + tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, cmp_off + LO_OFF); tcg_out_movi(s, TCG_TYPE_I32, TCG_TMP1, mask); } else { - tcg_out_ldst(s, - (TARGET_LONG_BITS == 64 ? OPC_LD - : TCG_TARGET_REG_BITS == 64 ? OPC_LWU : OPC_LW), - TCG_TMP0, TCG_REG_A0, cmp_off); + tcg_out_ldst(s, (TARGET_LONG_BITS == 64 ? OPC_LD + : TCG_TARGET_REG_BITS == 64 ? OPC_LWU : OPC_LW), + TCG_TMP0, TCG_TMP3, cmp_off); tcg_out_movi(s, TCG_TYPE_TL, TCG_TMP1, mask); /* No second compare is required here; load the tlb addend for the fast path. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_REG_A0, add_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); } tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, addrl); @@ -1275,10 +1303,10 @@ static void tcg_out_tlb_load(TCGContext *s, TCGReg base, TCGReg addrl, /* Load and test the high half tlb comparator. */ if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { /* delay slot */ - tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_REG_A0, cmp_off + HI_OFF); + tcg_out_ld(s, TCG_TYPE_I32, TCG_TMP0, TCG_TMP3, cmp_off + HI_OFF); /* Load the tlb addend for the fast path. */ - tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_REG_A0, add_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_TMP2, TCG_TMP3, add_off); label_ptr[1] = s->code_ptr; tcg_out_opc_br(s, OPC_BNE, addrh, TCG_TMP0); @@ -1347,8 +1375,9 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) } } - reloc_pc16(s->code_ptr, l->raddr); tcg_out_opc_br(s, OPC_BEQ, TCG_REG_ZERO, TCG_REG_ZERO); + reloc_pc16(s->code_ptr - 1, l->raddr); + /* delay slot */ if (TCG_TARGET_REG_BITS == 64 && l->type == TCG_TYPE_I32) { /* we always sign-extend 32-bit loads */ diff --git a/tcg/optimize.c b/tcg/optimize.c index 5dbe11c3c8..01e80c3e46 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -353,6 +353,15 @@ static TCGArg do_constant_folding_2(TCGOpcode op, TCGArg x, TCGArg y) CASE_OP_32_64(ext16u): return (uint16_t)x; + CASE_OP_32_64(bswap16): + return bswap16(x); + + CASE_OP_32_64(bswap32): + return bswap32(x); + + case INDEX_op_bswap64_i64: + return bswap64(x); + case INDEX_op_ext_i32_i64: case INDEX_op_ext32s_i64: return (int32_t)x; @@ -1105,6 +1114,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(ext16s): CASE_OP_32_64(ext16u): CASE_OP_32_64(ctpop): + CASE_OP_32_64(bswap16): + CASE_OP_32_64(bswap32): + case INDEX_op_bswap64_i64: case INDEX_op_ext32s_i64: case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: @@ -1249,7 +1261,7 @@ void tcg_optimize(TCGContext *s) uint64_t a = ((uint64_t)ah << 32) | al; uint64_t b = ((uint64_t)bh << 32) | bl; TCGArg rl, rh; - TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32, 2); + TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32); if (opc == INDEX_op_add2_i32) { a += b; @@ -1271,7 +1283,7 @@ void tcg_optimize(TCGContext *s) uint32_t b = arg_info(op->args[3])->val; uint64_t r = (uint64_t)a * b; TCGArg rl, rh; - TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32, 2); + TCGOp *op2 = tcg_op_insert_before(s, op, INDEX_op_movi_i32); rl = op->args[0]; rh = op->args[1]; diff --git a/tcg/ppc/tcg-target.h b/tcg/ppc/tcg-target.h index be52ad1d2e..52c1bb04b1 100644 --- a/tcg/ppc/tcg-target.h +++ b/tcg/ppc/tcg-target.h @@ -128,6 +128,7 @@ void flush_icache_range(uintptr_t start, uintptr_t stop); void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t); #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 #ifdef CONFIG_SOFTMMU #define TCG_TARGET_NEED_LDST_LABELS diff --git a/tcg/ppc/tcg-target.inc.c b/tcg/ppc/tcg-target.inc.c index c2f729ee8f..773690f1d9 100644 --- a/tcg/ppc/tcg-target.inc.c +++ b/tcg/ppc/tcg-target.inc.c @@ -193,9 +193,14 @@ static uint32_t reloc_pc24_val(tcg_insn_unit *pc, tcg_insn_unit *target) return disp & 0x3fffffc; } -static void reloc_pc24(tcg_insn_unit *pc, tcg_insn_unit *target) +static bool reloc_pc24(tcg_insn_unit *pc, tcg_insn_unit *target) { - *pc = (*pc & ~0x3fffffc) | reloc_pc24_val(pc, target); + ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); + if (in_range_b(disp)) { + *pc = (*pc & ~0x3fffffc) | (disp & 0x3fffffc); + return true; + } + return false; } static uint16_t reloc_pc14_val(tcg_insn_unit *pc, tcg_insn_unit *target) @@ -205,21 +210,14 @@ static uint16_t reloc_pc14_val(tcg_insn_unit *pc, tcg_insn_unit *target) return disp & 0xfffc; } -static void reloc_pc14(tcg_insn_unit *pc, tcg_insn_unit *target) +static bool 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); + ptrdiff_t disp = tcg_ptr_byte_diff(target, pc); + if (disp == (int16_t) disp) { + *pc = (*pc & ~0xfffc) | (disp & 0xfffc); + return true; + } + return false; } /* parse target specific constraints */ @@ -329,6 +327,7 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define LHZ OPCD( 40) #define LHA OPCD( 42) #define LWZ OPCD( 32) +#define LWZUX XO31( 55) #define STB OPCD( 38) #define STH OPCD( 44) #define STW OPCD( 36) @@ -340,6 +339,7 @@ static int tcg_target_const_match(tcg_target_long val, TCGType type, #define LD XO58( 0) #define LDX XO31( 21) #define LDU XO58( 1) +#define LDUX XO31( 53) #define LWA XO58( 2) #define LWAX XO31(341) @@ -525,7 +525,7 @@ static const uint32_t tcg_to_isel[] = { [TCG_COND_GTU] = ISEL | BC_(7, CR_GT), }; -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { tcg_insn_unit *target; @@ -536,11 +536,9 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, switch (type) { case R_PPC_REL14: - reloc_pc14(code_ptr, target); - break; + return reloc_pc14(code_ptr, target); case R_PPC_REL24: - reloc_pc24(code_ptr, target); - break; + return reloc_pc24(code_ptr, target); case R_PPC_ADDR16: /* We are abusing this relocation type. This points to a pair of insns, addis + load. If the displacement is small, we @@ -552,7 +550,9 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, } else { int16_t lo = value; int hi = value - lo; - assert(hi + lo == value); + if (hi + lo != value) { + return false; + } code_ptr[0] = deposit32(code_ptr[0], 0, 16, hi >> 16); code_ptr[1] = deposit32(code_ptr[1], 0, 16, lo); } @@ -560,6 +560,7 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, default: g_assert_not_reached(); } + return true; } static void tcg_out_mem_long(TCGContext *s, int opi, int opx, TCGReg rt, @@ -1179,11 +1180,11 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, 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)); + 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); } + tcg_out32(s, bc); } static void tcg_out_brcond(TCGContext *s, TCGCond cond, @@ -1504,6 +1505,10 @@ static void * const qemu_st_helpers[16] = { [MO_BEQ] = helper_be_stq_mmu, }; +/* We expect tlb_mask to be before tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) < + offsetof(CPUArchState, tlb_mask)); + /* 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. */ @@ -1514,61 +1519,63 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp opc, { 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; + ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + int mask_off = offsetof(CPUArchState, tlb_mask[mem_index]); + int table_off = offsetof(CPUArchState, tlb_table[mem_index]); + TCGReg mask_base = TCG_AREG0, table_base = TCG_AREG0; unsigned s_bits = opc & MO_SIZE; unsigned a_bits = get_alignment_bits(opc); - /* 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); + if (table_off > 0x7fff) { + int mask_hi = mask_off - (int16_t)mask_off; + int table_hi = table_off - (int16_t)table_off; + + table_base = TCG_REG_R4; + if (mask_hi == table_hi) { + mask_base = table_base; + } else if (mask_hi) { + mask_base = TCG_REG_R3; + tcg_out32(s, ADDIS | TAI(mask_base, TCG_AREG0, mask_hi >> 16)); } + tcg_out32(s, ADDIS | TAI(table_base, TCG_AREG0, table_hi >> 16)); + mask_off -= mask_hi; + table_off -= table_hi; } - /* Compensate for very large offsets. */ - if (add_off >= 0x8000) { - int low = (int16_t)cmp_off; - int high = cmp_off - low; - assert((high & 0xffff) == 0); - assert(cmp_off - high == (int16_t)(cmp_off - high)); - assert(add_off - high == (int16_t)(add_off - high)); - tcg_out32(s, ADDIS | TAI(TCG_REG_TMP1, base, high >> 16)); - base = TCG_REG_TMP1; - cmp_off -= high; - add_off -= high; - } + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, mask_base, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R4, table_base, table_off); - /* 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); + /* Extract the page index, shifted into place for tlb index. */ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_shri32(s, TCG_REG_TMP1, addrlo, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } else { - tcg_out_shli64(s, TCG_REG_R3, TCG_REG_R3, CPU_TLB_ENTRY_BITS); + tcg_out_shri64(s, TCG_REG_TMP1, addrlo, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } + tcg_out32(s, AND | SAB(TCG_REG_R3, TCG_REG_R3, TCG_REG_TMP1)); - 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); + /* Load the TLB comparator. */ + if (cmp_off == 0 && TCG_TARGET_REG_BITS >= TARGET_LONG_BITS) { + uint32_t lxu = (TCG_TARGET_REG_BITS == 32 || TARGET_LONG_BITS == 32 + ? LWZUX : LDUX); + tcg_out32(s, lxu | TAB(TCG_REG_TMP1, TCG_REG_R3, TCG_REG_R4)); } else { - tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP1, TCG_REG_R3, cmp_off); + tcg_out32(s, ADD | TAB(TCG_REG_R3, TCG_REG_R3, TCG_REG_R4)); + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_TMP1, TCG_REG_R3, cmp_off + 4); + tcg_out_ld(s, TCG_TYPE_I32, TCG_REG_R4, TCG_REG_R3, cmp_off); + } 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); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_R3, TCG_REG_R3, + offsetof(CPUTLBEntry, addend)); /* Clear the non-page, non-alignment bits from the address */ if (TCG_TARGET_REG_BITS == 32) { @@ -1601,6 +1608,9 @@ static TCGReg tcg_out_tlb_read(TCGContext *s, TCGMemOp opc, if (TARGET_LONG_BITS == 32) { tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); + /* Zero-extend the address for use in the final address. */ + tcg_out_ext32u(s, TCG_REG_R4, addrlo); + addrlo = TCG_REG_R4; } else if (a_bits == 0) { tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - TARGET_PAGE_BITS); } else { @@ -1649,7 +1659,7 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOp opc = get_memop(oi); TCGReg hi, lo, arg = TCG_REG_R3; - reloc_pc14(lb->label_ptr[0], s->code_ptr); + **lb->label_ptr |= reloc_pc14_val(*lb->label_ptr, s->code_ptr); tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); @@ -1694,7 +1704,7 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOp s_bits = opc & MO_SIZE; TCGReg hi, lo, arg = TCG_REG_R3; - reloc_pc14(lb->label_ptr[0], s->code_ptr); + **lb->label_ptr |= reloc_pc14_val(*lb->label_ptr, s->code_ptr); tcg_out_mov(s, TCG_TYPE_PTR, arg++, TCG_AREG0); @@ -1771,7 +1781,7 @@ static void tcg_out_qemu_ld(TCGContext *s, const TCGArg *args, bool is_64) /* 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); + tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ @@ -1846,7 +1856,7 @@ static void tcg_out_qemu_st(TCGContext *s, const TCGArg *args, bool is_64) /* 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); + tcg_out32(s, BC | BI(7, CR_EQ) | BO_COND_FALSE | LK); rbase = TCG_REG_R3; #else /* !CONFIG_SOFTMMU */ @@ -2044,13 +2054,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, const TCGArg *args, case INDEX_op_br: { TCGLabel *l = arg_label(args[0]); + uint32_t insn = B; if (l->has_value) { - tcg_out_b(s, 0, l->u.value_ptr); + insn |= reloc_pc24_val(s->code_ptr, l->u.value_ptr); } else { tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); - tcg_out_b_noaddr(s, B); } + tcg_out32(s, insn); } break; case INDEX_op_ld8u_i32: diff --git a/tcg/riscv/tcg-target.h b/tcg/riscv/tcg-target.h new file mode 100644 index 0000000000..60918cacb4 --- /dev/null +++ b/tcg/riscv/tcg-target.h @@ -0,0 +1,177 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2018 SiFive, 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 RISCV_TCG_TARGET_H +#define RISCV_TCG_TARGET_H + +#if __riscv_xlen == 32 +# define TCG_TARGET_REG_BITS 32 +#elif __riscv_xlen == 64 +# define TCG_TARGET_REG_BITS 64 +#endif + +#define TCG_TARGET_INSN_UNIT_SIZE 4 +#define TCG_TARGET_TLB_DISPLACEMENT_BITS 20 +#define TCG_TARGET_NB_REGS 32 + +typedef enum { + TCG_REG_ZERO, + TCG_REG_RA, + TCG_REG_SP, + TCG_REG_GP, + TCG_REG_TP, + TCG_REG_T0, + TCG_REG_T1, + TCG_REG_T2, + TCG_REG_S0, + TCG_REG_S1, + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_A4, + TCG_REG_A5, + TCG_REG_A6, + TCG_REG_A7, + TCG_REG_S2, + TCG_REG_S3, + TCG_REG_S4, + TCG_REG_S5, + TCG_REG_S6, + TCG_REG_S7, + TCG_REG_S8, + TCG_REG_S9, + TCG_REG_S10, + TCG_REG_S11, + TCG_REG_T3, + TCG_REG_T4, + TCG_REG_T5, + TCG_REG_T6, + + /* aliases */ + TCG_AREG0 = TCG_REG_S0, + TCG_GUEST_BASE_REG = TCG_REG_S1, + TCG_REG_TMP0 = TCG_REG_T6, + TCG_REG_TMP1 = TCG_REG_T5, + TCG_REG_TMP2 = TCG_REG_T4, +} TCGReg; + +/* used for function call generation */ +#define TCG_REG_CALL_STACK TCG_REG_SP +#define TCG_TARGET_STACK_ALIGN 16 +#define TCG_TARGET_CALL_ALIGN_ARGS 1 +#define TCG_TARGET_CALL_STACK_OFFSET 0 + +/* optional instructions */ +#define TCG_TARGET_HAS_goto_ptr 1 +#define TCG_TARGET_HAS_movcond_i32 0 +#define TCG_TARGET_HAS_div_i32 1 +#define TCG_TARGET_HAS_rem_i32 1 +#define TCG_TARGET_HAS_div2_i32 0 +#define TCG_TARGET_HAS_rot_i32 0 +#define TCG_TARGET_HAS_deposit_i32 0 +#define TCG_TARGET_HAS_extract_i32 0 +#define TCG_TARGET_HAS_sextract_i32 0 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_mulu2_i32 0 +#define TCG_TARGET_HAS_muls2_i32 0 +#define TCG_TARGET_HAS_muluh_i32 (TCG_TARGET_REG_BITS == 32) +#define TCG_TARGET_HAS_mulsh_i32 (TCG_TARGET_REG_BITS == 32) +#define TCG_TARGET_HAS_ext8s_i32 1 +#define TCG_TARGET_HAS_ext16s_i32 1 +#define TCG_TARGET_HAS_ext8u_i32 1 +#define TCG_TARGET_HAS_ext16u_i32 1 +#define TCG_TARGET_HAS_bswap16_i32 0 +#define TCG_TARGET_HAS_bswap32_i32 0 +#define TCG_TARGET_HAS_not_i32 1 +#define TCG_TARGET_HAS_neg_i32 1 +#define TCG_TARGET_HAS_andc_i32 0 +#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_nor_i32 0 +#define TCG_TARGET_HAS_clz_i32 0 +#define TCG_TARGET_HAS_ctz_i32 0 +#define TCG_TARGET_HAS_ctpop_i32 0 +#define TCG_TARGET_HAS_direct_jump 0 +#define TCG_TARGET_HAS_brcond2 1 +#define TCG_TARGET_HAS_setcond2 1 + +#if TCG_TARGET_REG_BITS == 64 +#define TCG_TARGET_HAS_movcond_i64 0 +#define TCG_TARGET_HAS_div_i64 1 +#define TCG_TARGET_HAS_rem_i64 1 +#define TCG_TARGET_HAS_div2_i64 0 +#define TCG_TARGET_HAS_rot_i64 0 +#define TCG_TARGET_HAS_deposit_i64 0 +#define TCG_TARGET_HAS_extract_i64 0 +#define TCG_TARGET_HAS_sextract_i64 0 +#define TCG_TARGET_HAS_extrl_i64_i32 1 +#define TCG_TARGET_HAS_extrh_i64_i32 1 +#define TCG_TARGET_HAS_ext8s_i64 1 +#define TCG_TARGET_HAS_ext16s_i64 1 +#define TCG_TARGET_HAS_ext32s_i64 1 +#define TCG_TARGET_HAS_ext8u_i64 1 +#define TCG_TARGET_HAS_ext16u_i64 1 +#define TCG_TARGET_HAS_ext32u_i64 1 +#define TCG_TARGET_HAS_bswap16_i64 0 +#define TCG_TARGET_HAS_bswap32_i64 0 +#define TCG_TARGET_HAS_bswap64_i64 0 +#define TCG_TARGET_HAS_not_i64 1 +#define TCG_TARGET_HAS_neg_i64 1 +#define TCG_TARGET_HAS_andc_i64 0 +#define TCG_TARGET_HAS_orc_i64 0 +#define TCG_TARGET_HAS_eqv_i64 0 +#define TCG_TARGET_HAS_nand_i64 0 +#define TCG_TARGET_HAS_nor_i64 0 +#define TCG_TARGET_HAS_clz_i64 0 +#define TCG_TARGET_HAS_ctz_i64 0 +#define TCG_TARGET_HAS_ctpop_i64 0 +#define TCG_TARGET_HAS_add2_i64 1 +#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_mulu2_i64 0 +#define TCG_TARGET_HAS_muls2_i64 0 +#define TCG_TARGET_HAS_muluh_i64 1 +#define TCG_TARGET_HAS_mulsh_i64 1 +#endif + +static inline void flush_icache_range(uintptr_t start, uintptr_t stop) +{ + __builtin___clear_cache((char *)start, (char *)stop); +} + +/* not defined -- call should be eliminated at compile time */ +void tb_target_set_jmp_target(uintptr_t, uintptr_t, uintptr_t); + +#define TCG_TARGET_DEFAULT_MO (0) + +#ifdef CONFIG_SOFTMMU +#define TCG_TARGET_NEED_LDST_LABELS +#endif +#define TCG_TARGET_NEED_POOL_LABELS + +#define TCG_TARGET_HAS_MEMORY_BSWAP 0 + +#endif diff --git a/tcg/riscv/tcg-target.inc.c b/tcg/riscv/tcg-target.inc.c new file mode 100644 index 0000000000..b785f4acb7 --- /dev/null +++ b/tcg/riscv/tcg-target.inc.c @@ -0,0 +1,1933 @@ +/* + * Tiny Code Generator for QEMU + * + * Copyright (c) 2018 SiFive, Inc + * Copyright (c) 2008-2009 Arnaud Patard + * Copyright (c) 2009 Aurelien Jarno + * Copyright (c) 2008 Fabrice Bellard + * + * Based on i386/tcg-target.c and mips/tcg-target.c + * + * 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-pool.inc.c" + +#ifdef CONFIG_DEBUG_TCG +static const char * const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { + "zero", + "ra", + "sp", + "gp", + "tp", + "t0", + "t1", + "t2", + "s0", + "s1", + "a0", + "a1", + "a2", + "a3", + "a4", + "a5", + "a6", + "a7", + "s2", + "s3", + "s4", + "s5", + "s6", + "s7", + "s8", + "s9", + "s10", + "s11", + "t3", + "t4", + "t5", + "t6" +}; +#endif + +static const int tcg_target_reg_alloc_order[] = { + /* Call saved registers */ + /* TCG_REG_S0 reservered for 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_S9, + TCG_REG_S10, + TCG_REG_S11, + + /* Call clobbered registers */ + TCG_REG_T0, + TCG_REG_T1, + TCG_REG_T2, + TCG_REG_T3, + TCG_REG_T4, + TCG_REG_T5, + TCG_REG_T6, + + /* Argument registers */ + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_A4, + TCG_REG_A5, + TCG_REG_A6, + TCG_REG_A7, +}; + +static const int tcg_target_call_iarg_regs[] = { + TCG_REG_A0, + TCG_REG_A1, + TCG_REG_A2, + TCG_REG_A3, + TCG_REG_A4, + TCG_REG_A5, + TCG_REG_A6, + TCG_REG_A7, +}; + +static const int tcg_target_call_oarg_regs[] = { + TCG_REG_A0, + TCG_REG_A1, +}; + +#define TCG_CT_CONST_ZERO 0x100 +#define TCG_CT_CONST_S12 0x200 +#define TCG_CT_CONST_N12 0x400 +#define TCG_CT_CONST_M12 0x800 + +static inline tcg_target_long sextreg(tcg_target_long val, int pos, int len) +{ + if (TCG_TARGET_REG_BITS == 32) { + return sextract32(val, pos, len); + } else { + return sextract64(val, pos, len); + } +} + +/* parse target specific constraints */ +static const char *target_parse_constraint(TCGArgConstraint *ct, + const char *ct_str, TCGType type) +{ + switch (*ct_str++) { + case 'r': + ct->ct |= TCG_CT_REG; + ct->u.regs = 0xffffffff; + break; + case 'L': + /* qemu_ld/qemu_st constraint */ + ct->ct |= TCG_CT_REG; + ct->u.regs = 0xffffffff; + /* qemu_ld/qemu_st uses TCG_REG_TMP0 */ +#if defined(CONFIG_SOFTMMU) + tcg_regset_reset_reg(ct->u.regs, tcg_target_call_iarg_regs[0]); + tcg_regset_reset_reg(ct->u.regs, tcg_target_call_iarg_regs[1]); + tcg_regset_reset_reg(ct->u.regs, tcg_target_call_iarg_regs[2]); + tcg_regset_reset_reg(ct->u.regs, tcg_target_call_iarg_regs[3]); + tcg_regset_reset_reg(ct->u.regs, tcg_target_call_iarg_regs[4]); +#endif + break; + case 'I': + ct->ct |= TCG_CT_CONST_S12; + break; + case 'N': + ct->ct |= TCG_CT_CONST_N12; + break; + case 'M': + ct->ct |= TCG_CT_CONST_M12; + break; + case 'Z': + /* we can use a zero immediate as a zero register argument. */ + ct->ct |= TCG_CT_CONST_ZERO; + break; + default: + return NULL; + } + return ct_str; +} + +/* 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 ((ct & TCG_CT_CONST_ZERO) && val == 0) { + return 1; + } + if ((ct & TCG_CT_CONST_S12) && val == sextreg(val, 0, 12)) { + return 1; + } + if ((ct & TCG_CT_CONST_N12) && -val == sextreg(-val, 0, 12)) { + return 1; + } + if ((ct & TCG_CT_CONST_M12) && val >= -0xfff && val <= 0xfff) { + return 1; + } + return 0; +} + +/* + * RISC-V Base ISA opcodes (IM) + */ + +typedef enum { + OPC_ADD = 0x33, + OPC_ADDI = 0x13, + OPC_AND = 0x7033, + OPC_ANDI = 0x7013, + OPC_AUIPC = 0x17, + OPC_BEQ = 0x63, + OPC_BGE = 0x5063, + OPC_BGEU = 0x7063, + OPC_BLT = 0x4063, + OPC_BLTU = 0x6063, + OPC_BNE = 0x1063, + OPC_DIV = 0x2004033, + OPC_DIVU = 0x2005033, + OPC_JAL = 0x6f, + OPC_JALR = 0x67, + OPC_LB = 0x3, + OPC_LBU = 0x4003, + OPC_LD = 0x3003, + OPC_LH = 0x1003, + OPC_LHU = 0x5003, + OPC_LUI = 0x37, + OPC_LW = 0x2003, + OPC_LWU = 0x6003, + OPC_MUL = 0x2000033, + OPC_MULH = 0x2001033, + OPC_MULHSU = 0x2002033, + OPC_MULHU = 0x2003033, + OPC_OR = 0x6033, + OPC_ORI = 0x6013, + OPC_REM = 0x2006033, + OPC_REMU = 0x2007033, + OPC_SB = 0x23, + OPC_SD = 0x3023, + OPC_SH = 0x1023, + OPC_SLL = 0x1033, + OPC_SLLI = 0x1013, + OPC_SLT = 0x2033, + OPC_SLTI = 0x2013, + OPC_SLTIU = 0x3013, + OPC_SLTU = 0x3033, + OPC_SRA = 0x40005033, + OPC_SRAI = 0x40005013, + OPC_SRL = 0x5033, + OPC_SRLI = 0x5013, + OPC_SUB = 0x40000033, + OPC_SW = 0x2023, + OPC_XOR = 0x4033, + OPC_XORI = 0x4013, + +#if TCG_TARGET_REG_BITS == 64 + OPC_ADDIW = 0x1b, + OPC_ADDW = 0x3b, + OPC_DIVUW = 0x200503b, + OPC_DIVW = 0x200403b, + OPC_MULW = 0x200003b, + OPC_REMUW = 0x200703b, + OPC_REMW = 0x200603b, + OPC_SLLIW = 0x101b, + OPC_SLLW = 0x103b, + OPC_SRAIW = 0x4000501b, + OPC_SRAW = 0x4000503b, + OPC_SRLIW = 0x501b, + OPC_SRLW = 0x503b, + OPC_SUBW = 0x4000003b, +#else + /* Simplify code throughout by defining aliases for RV32. */ + OPC_ADDIW = OPC_ADDI, + OPC_ADDW = OPC_ADD, + OPC_DIVUW = OPC_DIVU, + OPC_DIVW = OPC_DIV, + OPC_MULW = OPC_MUL, + OPC_REMUW = OPC_REMU, + OPC_REMW = OPC_REM, + OPC_SLLIW = OPC_SLLI, + OPC_SLLW = OPC_SLL, + OPC_SRAIW = OPC_SRAI, + OPC_SRAW = OPC_SRA, + OPC_SRLIW = OPC_SRLI, + OPC_SRLW = OPC_SRL, + OPC_SUBW = OPC_SUB, +#endif + + OPC_FENCE = 0x0000000f, +} RISCVInsn; + +/* + * RISC-V immediate and instruction encoders (excludes 16-bit RVC) + */ + +/* Type-R */ + +static int32_t encode_r(RISCVInsn opc, TCGReg rd, TCGReg rs1, TCGReg rs2) +{ + return opc | (rd & 0x1f) << 7 | (rs1 & 0x1f) << 15 | (rs2 & 0x1f) << 20; +} + +/* Type-I */ + +static int32_t encode_imm12(uint32_t imm) +{ + return (imm & 0xfff) << 20; +} + +static int32_t encode_i(RISCVInsn opc, TCGReg rd, TCGReg rs1, uint32_t imm) +{ + return opc | (rd & 0x1f) << 7 | (rs1 & 0x1f) << 15 | encode_imm12(imm); +} + +/* Type-S */ + +static int32_t encode_simm12(uint32_t imm) +{ + int32_t ret = 0; + + ret |= (imm & 0xFE0) << 20; + ret |= (imm & 0x1F) << 7; + + return ret; +} + +static int32_t encode_s(RISCVInsn opc, TCGReg rs1, TCGReg rs2, uint32_t imm) +{ + return opc | (rs1 & 0x1f) << 15 | (rs2 & 0x1f) << 20 | encode_simm12(imm); +} + +/* Type-SB */ + +static int32_t encode_sbimm12(uint32_t imm) +{ + int32_t ret = 0; + + ret |= (imm & 0x1000) << 19; + ret |= (imm & 0x7e0) << 20; + ret |= (imm & 0x1e) << 7; + ret |= (imm & 0x800) >> 4; + + return ret; +} + +static int32_t encode_sb(RISCVInsn opc, TCGReg rs1, TCGReg rs2, uint32_t imm) +{ + return opc | (rs1 & 0x1f) << 15 | (rs2 & 0x1f) << 20 | encode_sbimm12(imm); +} + +/* Type-U */ + +static int32_t encode_uimm20(uint32_t imm) +{ + return imm & 0xfffff000; +} + +static int32_t encode_u(RISCVInsn opc, TCGReg rd, uint32_t imm) +{ + return opc | (rd & 0x1f) << 7 | encode_uimm20(imm); +} + +/* Type-UJ */ + +static int32_t encode_ujimm20(uint32_t imm) +{ + int32_t ret = 0; + + ret |= (imm & 0x0007fe) << (21 - 1); + ret |= (imm & 0x000800) << (20 - 11); + ret |= (imm & 0x0ff000) << (12 - 12); + ret |= (imm & 0x100000) << (31 - 20); + + return ret; +} + +static int32_t encode_uj(RISCVInsn opc, TCGReg rd, uint32_t imm) +{ + return opc | (rd & 0x1f) << 7 | encode_ujimm20(imm); +} + +/* + * RISC-V instruction emitters + */ + +static void tcg_out_opc_reg(TCGContext *s, RISCVInsn opc, + TCGReg rd, TCGReg rs1, TCGReg rs2) +{ + tcg_out32(s, encode_r(opc, rd, rs1, rs2)); +} + +static void tcg_out_opc_imm(TCGContext *s, RISCVInsn opc, + TCGReg rd, TCGReg rs1, TCGArg imm) +{ + tcg_out32(s, encode_i(opc, rd, rs1, imm)); +} + +static void tcg_out_opc_store(TCGContext *s, RISCVInsn opc, + TCGReg rs1, TCGReg rs2, uint32_t imm) +{ + tcg_out32(s, encode_s(opc, rs1, rs2, imm)); +} + +static void tcg_out_opc_branch(TCGContext *s, RISCVInsn opc, + TCGReg rs1, TCGReg rs2, uint32_t imm) +{ + tcg_out32(s, encode_sb(opc, rs1, rs2, imm)); +} + +static void tcg_out_opc_upper(TCGContext *s, RISCVInsn opc, + TCGReg rd, uint32_t imm) +{ + tcg_out32(s, encode_u(opc, rd, imm)); +} + +static void tcg_out_opc_jump(TCGContext *s, RISCVInsn opc, + TCGReg rd, uint32_t imm) +{ + tcg_out32(s, encode_uj(opc, rd, imm)); +} + +static void tcg_out_nop_fill(tcg_insn_unit *p, int count) +{ + int i; + for (i = 0; i < count; ++i) { + p[i] = encode_i(OPC_ADDI, TCG_REG_ZERO, TCG_REG_ZERO, 0); + } +} + +/* + * Relocations + */ + +static bool reloc_sbimm12(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +{ + intptr_t offset = (intptr_t)target - (intptr_t)code_ptr; + + if (offset == sextreg(offset, 1, 12) << 1) { + code_ptr[0] |= encode_sbimm12(offset); + return true; + } + + return false; +} + +static bool reloc_jimm20(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +{ + intptr_t offset = (intptr_t)target - (intptr_t)code_ptr; + + if (offset == sextreg(offset, 1, 20) << 1) { + code_ptr[0] |= encode_ujimm20(offset); + return true; + } + + return false; +} + +static bool reloc_call(tcg_insn_unit *code_ptr, tcg_insn_unit *target) +{ + intptr_t offset = (intptr_t)target - (intptr_t)code_ptr; + int32_t lo = sextreg(offset, 0, 12); + int32_t hi = offset - lo; + + if (offset == hi + lo) { + code_ptr[0] |= encode_uimm20(hi); + code_ptr[1] |= encode_imm12(lo); + return true; + } + + return false; +} + +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, + intptr_t value, intptr_t addend) +{ + uint32_t insn = *code_ptr; + intptr_t diff; + bool short_jmp; + + tcg_debug_assert(addend == 0); + + switch (type) { + case R_RISCV_BRANCH: + diff = value - (uintptr_t)code_ptr; + short_jmp = diff == sextreg(diff, 0, 12); + if (short_jmp) { + return reloc_sbimm12(code_ptr, (tcg_insn_unit *)value); + } else { + /* Invert the condition */ + insn = insn ^ (1 << 12); + /* Clear the offset */ + insn &= 0x01fff07f; + /* Set the offset to the PC + 8 */ + insn |= encode_sbimm12(8); + + /* Move forward */ + code_ptr[0] = insn; + + /* Overwrite the NOP with jal x0,value */ + diff = value - (uintptr_t)(code_ptr + 1); + insn = encode_uj(OPC_JAL, TCG_REG_ZERO, diff); + code_ptr[1] = insn; + + return true; + } + break; + case R_RISCV_JAL: + return reloc_jimm20(code_ptr, (tcg_insn_unit *)value); + break; + case R_RISCV_CALL: + return reloc_call(code_ptr, (tcg_insn_unit *)value); + break; + default: + tcg_abort(); + } +} + +/* + * TCG intrinsics + */ + +static void tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + if (ret == arg) { + return; + } + switch (type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + tcg_out_opc_imm(s, OPC_ADDI, ret, arg, 0); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_movi(TCGContext *s, TCGType type, TCGReg rd, + tcg_target_long val) +{ + tcg_target_long lo, hi, tmp; + int shift, ret; + + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + val = (int32_t)val; + } + + lo = sextreg(val, 0, 12); + if (val == lo) { + tcg_out_opc_imm(s, OPC_ADDI, rd, TCG_REG_ZERO, lo); + return; + } + + hi = val - lo; + if (TCG_TARGET_REG_BITS == 32 || val == (int32_t)val) { + tcg_out_opc_upper(s, OPC_LUI, rd, hi); + if (lo != 0) { + tcg_out_opc_imm(s, OPC_ADDIW, rd, rd, lo); + } + return; + } + + /* We can only be here if TCG_TARGET_REG_BITS != 32 */ + tmp = tcg_pcrel_diff(s, (void *)val); + if (tmp == (int32_t)tmp) { + tcg_out_opc_upper(s, OPC_AUIPC, rd, 0); + tcg_out_opc_imm(s, OPC_ADDI, rd, rd, 0); + ret = reloc_call(s->code_ptr - 2, (tcg_insn_unit *)val); + tcg_debug_assert(ret == true); + return; + } + + /* Look for a single 20-bit section. */ + shift = ctz64(val); + tmp = val >> shift; + if (tmp == sextreg(tmp, 0, 20)) { + tcg_out_opc_upper(s, OPC_LUI, rd, tmp << 12); + if (shift > 12) { + tcg_out_opc_imm(s, OPC_SLLI, rd, rd, shift - 12); + } else { + tcg_out_opc_imm(s, OPC_SRAI, rd, rd, 12 - shift); + } + return; + } + + /* Look for a few high zero bits, with lots of bits set in the middle. */ + shift = clz64(val); + tmp = val << shift; + if (tmp == sextreg(tmp, 12, 20) << 12) { + tcg_out_opc_upper(s, OPC_LUI, rd, tmp); + tcg_out_opc_imm(s, OPC_SRLI, rd, rd, shift); + return; + } else if (tmp == sextreg(tmp, 0, 12)) { + tcg_out_opc_imm(s, OPC_ADDI, rd, TCG_REG_ZERO, tmp); + tcg_out_opc_imm(s, OPC_SRLI, rd, rd, shift); + return; + } + + /* Drop into the constant pool. */ + new_pool_label(s, val, R_RISCV_CALL, s->code_ptr, 0); + tcg_out_opc_upper(s, OPC_AUIPC, rd, 0); + tcg_out_opc_imm(s, OPC_LD, rd, rd, 0); +} + +static void tcg_out_ext8u(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_opc_imm(s, OPC_ANDI, ret, arg, 0xff); +} + +static void tcg_out_ext16u(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); + tcg_out_opc_imm(s, OPC_SRLIW, ret, ret, 16); +} + +static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_opc_imm(s, OPC_SLLI, ret, arg, 32); + tcg_out_opc_imm(s, OPC_SRLI, ret, ret, 32); +} + +static void tcg_out_ext8s(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 24); + tcg_out_opc_imm(s, OPC_SRAIW, ret, ret, 24); +} + +static void tcg_out_ext16s(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_opc_imm(s, OPC_SLLIW, ret, arg, 16); + tcg_out_opc_imm(s, OPC_SRAIW, ret, ret, 16); +} + +static void tcg_out_ext32s(TCGContext *s, TCGReg ret, TCGReg arg) +{ + tcg_out_opc_imm(s, OPC_ADDIW, ret, arg, 0); +} + +static void tcg_out_ldst(TCGContext *s, RISCVInsn opc, TCGReg data, + TCGReg addr, intptr_t offset) +{ + intptr_t imm12 = sextreg(offset, 0, 12); + + if (offset != imm12) { + intptr_t diff = offset - (uintptr_t)s->code_ptr; + + if (addr == TCG_REG_ZERO && diff == (int32_t)diff) { + imm12 = sextreg(diff, 0, 12); + tcg_out_opc_upper(s, OPC_AUIPC, TCG_REG_TMP2, diff - imm12); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP2, offset - imm12); + if (addr != TCG_REG_ZERO) { + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP2, TCG_REG_TMP2, addr); + } + } + addr = TCG_REG_TMP2; + } + + switch (opc) { + case OPC_SB: + case OPC_SH: + case OPC_SW: + case OPC_SD: + tcg_out_opc_store(s, opc, addr, data, imm12); + break; + case OPC_LB: + case OPC_LBU: + case OPC_LH: + case OPC_LHU: + case OPC_LW: + case OPC_LWU: + case OPC_LD: + tcg_out_opc_imm(s, opc, data, addr, imm12); + break; + default: + g_assert_not_reached(); + } +} + +static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + bool is32bit = (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32); + tcg_out_ldst(s, is32bit ? OPC_LW : OPC_LD, arg, arg1, arg2); +} + +static void tcg_out_st(TCGContext *s, TCGType type, TCGReg arg, + TCGReg arg1, intptr_t arg2) +{ + bool is32bit = (TCG_TARGET_REG_BITS == 32 || type == TCG_TYPE_I32); + tcg_out_ldst(s, is32bit ? OPC_SW : OPC_SD, arg, arg1, arg2); +} + +static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, + TCGReg base, intptr_t ofs) +{ + if (val == 0) { + tcg_out_st(s, type, TCG_REG_ZERO, base, ofs); + return true; + } + return false; +} + +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, bool is32bit) +{ + const RISCVInsn opc_add = is32bit ? OPC_ADDW : OPC_ADD; + const RISCVInsn opc_addi = is32bit ? OPC_ADDIW : OPC_ADDI; + const RISCVInsn opc_sub = is32bit ? OPC_SUBW : OPC_SUB; + TCGReg th = TCG_REG_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_sub : opc_add), th, ah, bh); + } else if (bh != 0 || ah == rl) { + tcg_out_opc_imm(s, opc_addi, 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_REG_TMP0, al, bl); + tcg_out_opc_imm(s, opc_addi, rl, al, -bl); + } else { + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, al, bl); + tcg_out_opc_reg(s, opc_sub, rl, al, bl); + } + tcg_out_opc_reg(s, opc_sub, rh, th, TCG_REG_TMP0); + } else { + if (cbl) { + tcg_out_opc_imm(s, opc_addi, rl, al, bl); + tcg_out_opc_imm(s, OPC_SLTIU, TCG_REG_TMP0, rl, bl); + } else if (rl == al && rl == bl) { + tcg_out_opc_imm(s, OPC_SLTI, TCG_REG_TMP0, al, 0); + tcg_out_opc_reg(s, opc_addi, rl, al, bl); + } else { + tcg_out_opc_reg(s, opc_add, rl, al, bl); + tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, + rl, (rl == bl ? al : bl)); + } + tcg_out_opc_reg(s, opc_add, rh, th, TCG_REG_TMP0); + } +} + +static const struct { + RISCVInsn op; + bool swap; +} tcg_brcond_to_riscv[] = { + [TCG_COND_EQ] = { OPC_BEQ, false }, + [TCG_COND_NE] = { OPC_BNE, false }, + [TCG_COND_LT] = { OPC_BLT, false }, + [TCG_COND_GE] = { OPC_BGE, false }, + [TCG_COND_LE] = { OPC_BGE, true }, + [TCG_COND_GT] = { OPC_BLT, true }, + [TCG_COND_LTU] = { OPC_BLTU, false }, + [TCG_COND_GEU] = { OPC_BGEU, false }, + [TCG_COND_LEU] = { OPC_BGEU, true }, + [TCG_COND_GTU] = { OPC_BLTU, true } +}; + +static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, + TCGReg arg2, TCGLabel *l) +{ + RISCVInsn op = tcg_brcond_to_riscv[cond].op; + + tcg_debug_assert(op != 0); + + if (tcg_brcond_to_riscv[cond].swap) { + TCGReg t = arg1; + arg1 = arg2; + arg2 = t; + } + + if (l->has_value) { + intptr_t diff = tcg_pcrel_diff(s, l->u.value_ptr); + if (diff == sextreg(diff, 0, 12)) { + tcg_out_opc_branch(s, op, arg1, arg2, diff); + } else { + /* Invert the conditional branch. */ + tcg_out_opc_branch(s, op ^ (1 << 12), arg1, arg2, 8); + tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, diff - 4); + } + } else { + tcg_out_reloc(s, s->code_ptr, R_RISCV_BRANCH, l, 0); + tcg_out_opc_branch(s, op, arg1, arg2, 0); + /* NOP to allow patching later */ + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_ZERO, TCG_REG_ZERO, 0); + } +} + +static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + switch (cond) { + case TCG_COND_EQ: + tcg_out_opc_reg(s, OPC_SUB, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_SLTIU, ret, ret, 1); + break; + case TCG_COND_NE: + tcg_out_opc_reg(s, OPC_SUB, ret, arg1, arg2); + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, ret); + break; + case TCG_COND_LT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + break; + case TCG_COND_GE: + tcg_out_opc_reg(s, OPC_SLT, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_LE: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_GT: + tcg_out_opc_reg(s, OPC_SLT, ret, arg2, arg1); + break; + case TCG_COND_LTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + break; + case TCG_COND_GEU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg1, arg2); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_LEU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + tcg_out_opc_imm(s, OPC_XORI, ret, ret, 1); + break; + case TCG_COND_GTU: + tcg_out_opc_reg(s, OPC_SLTU, ret, arg2, arg1); + break; + default: + g_assert_not_reached(); + break; + } +} + +static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGReg bl, TCGReg bh, TCGLabel *l) +{ + /* todo */ + g_assert_not_reached(); +} + +static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) +{ + /* todo */ + g_assert_not_reached(); +} + +static inline void tcg_out_goto(TCGContext *s, tcg_insn_unit *target) +{ + ptrdiff_t offset = tcg_pcrel_diff(s, target); + tcg_debug_assert(offset == sextreg(offset, 1, 20) << 1); + tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, offset); +} + +static void tcg_out_call_int(TCGContext *s, tcg_insn_unit *arg, bool tail) +{ + TCGReg link = tail ? TCG_REG_ZERO : TCG_REG_RA; + ptrdiff_t offset = tcg_pcrel_diff(s, arg); + int ret; + + if (offset == sextreg(offset, 1, 20) << 1) { + /* short jump: -2097150 to 2097152 */ + tcg_out_opc_jump(s, OPC_JAL, link, offset); + } else if (TCG_TARGET_REG_BITS == 32 || + offset == sextreg(offset, 1, 31) << 1) { + /* long jump: -2147483646 to 2147483648 */ + tcg_out_opc_upper(s, OPC_AUIPC, TCG_REG_TMP0, 0); + tcg_out_opc_imm(s, OPC_JALR, link, TCG_REG_TMP0, 0); + ret = reloc_call(s->code_ptr - 2, arg);\ + tcg_debug_assert(ret == true); + } else if (TCG_TARGET_REG_BITS == 64) { + /* far jump: 64-bit */ + tcg_target_long imm = sextreg((tcg_target_long)arg, 0, 12); + tcg_target_long base = (tcg_target_long)arg - imm; + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP0, base); + tcg_out_opc_imm(s, OPC_JALR, link, TCG_REG_TMP0, imm); + } else { + g_assert_not_reached(); + } +} + +static void tcg_out_call(TCGContext *s, tcg_insn_unit *arg) +{ + tcg_out_call_int(s, arg, false); +} + +static void tcg_out_mb(TCGContext *s, TCGArg a0) +{ + tcg_insn_unit insn = OPC_FENCE; + + if (a0 & TCG_MO_LD_LD) { + insn |= 0x02200000; + } + if (a0 & TCG_MO_ST_LD) { + insn |= 0x01200000; + } + if (a0 & TCG_MO_LD_ST) { + insn |= 0x02100000; + } + if (a0 & TCG_MO_ST_ST) { + insn |= 0x02200000; + } + tcg_out32(s, insn); +} + +/* + * Load/store and TLB + */ + +#if defined(CONFIG_SOFTMMU) +#include "tcg-ldst.inc.c" + +/* 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_SB] = helper_ret_ldsb_mmu, + [MO_LEUW] = helper_le_lduw_mmu, + [MO_LESW] = helper_le_ldsw_mmu, + [MO_LEUL] = helper_le_ldul_mmu, +#if TCG_TARGET_REG_BITS == 64 + [MO_LESL] = helper_le_ldsl_mmu, +#endif + [MO_LEQ] = helper_le_ldq_mmu, + [MO_BEUW] = helper_be_lduw_mmu, + [MO_BESW] = helper_be_ldsw_mmu, + [MO_BEUL] = helper_be_ldul_mmu, +#if TCG_TARGET_REG_BITS == 64 + [MO_BESL] = helper_be_ldsl_mmu, +#endif + [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, +}; + +/* We don't support oversize guests */ +QEMU_BUILD_BUG_ON(TCG_TARGET_REG_BITS < TARGET_LONG_BITS); + +/* We expect tlb_mask to be before tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) < + offsetof(CPUArchState, tlb_mask)); + +/* We expect tlb_mask to be "near" tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) - + offsetof(CPUArchState, tlb_mask) >= 0x800); + +static void tcg_out_tlb_load(TCGContext *s, TCGReg addrl, + TCGReg addrh, TCGMemOpIdx oi, + tcg_insn_unit **label_ptr, bool is_load) +{ + TCGMemOp opc = get_memop(oi); + unsigned s_bits = opc & MO_SIZE; + unsigned a_bits = get_alignment_bits(opc); + tcg_target_long compare_mask; + int mem_index = get_mmuidx(oi); + int mask_off, table_off; + TCGReg mask_base = TCG_AREG0, table_base = TCG_AREG0; + + mask_off = offsetof(CPUArchState, tlb_mask[mem_index]); + table_off = offsetof(CPUArchState, tlb_table[mem_index]); + if (table_off > 0x7ff) { + int mask_hi = mask_off - sextreg(mask_off, 0, 12); + int table_hi = table_off - sextreg(table_off, 0, 12); + + if (likely(mask_hi == table_hi)) { + mask_base = table_base = TCG_REG_TMP1; + tcg_out_opc_upper(s, OPC_LUI, mask_base, mask_hi); + tcg_out_opc_reg(s, OPC_ADD, mask_base, mask_base, TCG_AREG0); + mask_off -= mask_hi; + table_off -= mask_hi; + } else { + mask_base = TCG_REG_TMP0; + table_base = TCG_REG_TMP1; + tcg_out_opc_upper(s, OPC_LUI, mask_base, mask_hi); + tcg_out_opc_reg(s, OPC_ADD, mask_base, mask_base, TCG_AREG0); + table_off -= mask_off; + mask_off -= mask_hi; + tcg_out_opc_imm(s, OPC_ADDI, table_base, mask_base, mask_off); + } + } + + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, mask_base, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, table_base, table_off); + + tcg_out_opc_imm(s, OPC_SRLI, TCG_REG_TMP2, addrl, + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); + tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); + + /* Load the tlb comparator and the addend. */ + tcg_out_ld(s, TCG_TYPE_TL, TCG_REG_TMP0, TCG_REG_TMP2, + is_load ? offsetof(CPUTLBEntry, addr_read) + : offsetof(CPUTLBEntry, addr_write)); + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP2, TCG_REG_TMP2, + offsetof(CPUTLBEntry, addend)); + + /* We don't support unaligned accesses. */ + if (a_bits < s_bits) { + a_bits = s_bits; + } + /* Clear the non-page, non-alignment bits from the address. */ + compare_mask = (tcg_target_long)TARGET_PAGE_MASK | ((1 << a_bits) - 1); + if (compare_mask == sextreg(compare_mask, 0, 12)) { + tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addrl, compare_mask); + } else { + tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_TMP1, compare_mask); + tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP1, TCG_REG_TMP1, addrl); + } + + /* Compare masked address with the TLB entry. */ + label_ptr[0] = s->code_ptr; + tcg_out_opc_branch(s, OPC_BNE, TCG_REG_TMP0, TCG_REG_TMP1, 0); + /* NOP to allow patching later */ + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_ZERO, TCG_REG_ZERO, 0); + + /* TLB Hit - translate address using addend. */ + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, TCG_REG_TMP0, addrl); + addrl = TCG_REG_TMP0; + } + tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP0, TCG_REG_TMP2, addrl); +} + +static void add_qemu_ldst_label(TCGContext *s, int is_ld, TCGMemOpIdx oi, + TCGType ext, + TCGReg datalo, TCGReg datahi, + TCGReg addrlo, TCGReg addrhi, + void *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 = datalo; + label->datahi_reg = datahi; + label->addrlo_reg = addrlo; + label->addrhi_reg = addrhi; + label->raddr = raddr; + label->label_ptr[0] = label_ptr[0]; +} + +static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *l) +{ + TCGMemOpIdx oi = l->oi; + TCGMemOp opc = get_memop(oi); + TCGReg a0 = tcg_target_call_iarg_regs[0]; + TCGReg a1 = tcg_target_call_iarg_regs[1]; + TCGReg a2 = tcg_target_call_iarg_regs[2]; + TCGReg a3 = tcg_target_call_iarg_regs[3]; + + /* We don't support oversize guests */ + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + g_assert_not_reached(); + } + + /* resolve label address */ + patch_reloc(l->label_ptr[0], R_RISCV_BRANCH, (intptr_t) s->code_ptr, 0); + + /* call load helper */ + tcg_out_mov(s, TCG_TYPE_PTR, a0, TCG_AREG0); + tcg_out_mov(s, TCG_TYPE_PTR, a1, l->addrlo_reg); + tcg_out_movi(s, TCG_TYPE_PTR, a2, oi); + tcg_out_movi(s, TCG_TYPE_PTR, a3, (tcg_target_long)l->raddr); + + tcg_out_call(s, qemu_ld_helpers[opc & (MO_BSWAP | MO_SSIZE)]); + tcg_out_mov(s, (opc & MO_SIZE) == MO_64, l->datalo_reg, a0); + + tcg_out_goto(s, l->raddr); +} + +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; + TCGReg a0 = tcg_target_call_iarg_regs[0]; + TCGReg a1 = tcg_target_call_iarg_regs[1]; + TCGReg a2 = tcg_target_call_iarg_regs[2]; + TCGReg a3 = tcg_target_call_iarg_regs[3]; + TCGReg a4 = tcg_target_call_iarg_regs[4]; + + /* We don't support oversize guests */ + if (TCG_TARGET_REG_BITS < TARGET_LONG_BITS) { + g_assert_not_reached(); + } + + /* resolve label address */ + patch_reloc(l->label_ptr[0], R_RISCV_BRANCH, (intptr_t) s->code_ptr, 0); + + /* call store helper */ + tcg_out_mov(s, TCG_TYPE_PTR, a0, TCG_AREG0); + tcg_out_mov(s, TCG_TYPE_PTR, a1, l->addrlo_reg); + tcg_out_mov(s, TCG_TYPE_PTR, a2, l->datalo_reg); + switch (s_bits) { + case MO_8: + tcg_out_ext8u(s, a2, a2); + break; + case MO_16: + tcg_out_ext16u(s, a2, a2); + break; + default: + break; + } + tcg_out_movi(s, TCG_TYPE_PTR, a3, oi); + tcg_out_movi(s, TCG_TYPE_PTR, a4, (tcg_target_long)l->raddr); + + tcg_out_call(s, qemu_st_helpers[opc & (MO_BSWAP | MO_SSIZE)]); + + tcg_out_goto(s, l->raddr); +} +#endif /* CONFIG_SOFTMMU */ + +static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg lo, TCGReg hi, + TCGReg base, TCGMemOp opc, bool is_64) +{ + const TCGMemOp bswap = opc & MO_BSWAP; + + /* We don't yet handle byteswapping, assert */ + g_assert(!bswap); + + switch (opc & (MO_SSIZE)) { + case MO_UB: + tcg_out_opc_imm(s, OPC_LBU, lo, base, 0); + break; + case MO_SB: + tcg_out_opc_imm(s, OPC_LB, lo, base, 0); + break; + case MO_UW: + tcg_out_opc_imm(s, OPC_LHU, lo, base, 0); + break; + case MO_SW: + tcg_out_opc_imm(s, OPC_LH, lo, base, 0); + break; + case MO_UL: + if (TCG_TARGET_REG_BITS == 64 && is_64) { + tcg_out_opc_imm(s, OPC_LWU, lo, base, 0); + break; + } + /* FALLTHRU */ + case MO_SL: + tcg_out_opc_imm(s, OPC_LW, lo, base, 0); + break; + case MO_Q: + /* Prefer to load from offset 0 first, but allow for overlap. */ + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_opc_imm(s, OPC_LD, lo, base, 0); + } else if (lo != base) { + tcg_out_opc_imm(s, OPC_LW, lo, base, 0); + tcg_out_opc_imm(s, OPC_LW, hi, base, 4); + } else { + tcg_out_opc_imm(s, OPC_LW, hi, base, 4); + tcg_out_opc_imm(s, OPC_LW, lo, base, 0); + } + break; + default: + g_assert_not_reached(); + } +} + +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[1]; +#endif + TCGReg base = TCG_REG_TMP0; + + data_regl = *args++; + data_regh = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); + addr_regl = *args++; + addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#if defined(CONFIG_SOFTMMU) + tcg_out_tlb_load(s, addr_regl, addr_regh, oi, label_ptr, 1); + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); + add_qemu_ldst_label(s, 1, oi, + (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), + data_regl, data_regh, addr_regl, addr_regh, + s->code_ptr, label_ptr); +#else + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, base, addr_regl); + addr_regl = base; + } + + if (guest_base == 0) { + tcg_out_opc_reg(s, OPC_ADD, base, addr_regl, TCG_REG_ZERO); + } else { + tcg_out_opc_reg(s, OPC_ADD, base, TCG_GUEST_BASE_REG, addr_regl); + } + tcg_out_qemu_ld_direct(s, data_regl, data_regh, base, opc, is_64); +#endif +} + +static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, + TCGReg base, TCGMemOp opc) +{ + const TCGMemOp bswap = opc & MO_BSWAP; + + /* We don't yet handle byteswapping, assert */ + g_assert(!bswap); + + switch (opc & (MO_SSIZE)) { + case MO_8: + tcg_out_opc_store(s, OPC_SB, base, lo, 0); + break; + case MO_16: + tcg_out_opc_store(s, OPC_SH, base, lo, 0); + break; + case MO_32: + tcg_out_opc_store(s, OPC_SW, base, lo, 0); + break; + case MO_64: + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_opc_store(s, OPC_SD, base, lo, 0); + } else { + tcg_out_opc_store(s, OPC_SW, base, lo, 0); + tcg_out_opc_store(s, OPC_SW, base, hi, 4); + } + break; + default: + g_assert_not_reached(); + } +} + +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; + TCGMemOpIdx oi; + TCGMemOp opc; +#if defined(CONFIG_SOFTMMU) + tcg_insn_unit *label_ptr[1]; +#endif + TCGReg base = TCG_REG_TMP0; + + data_regl = *args++; + data_regh = (TCG_TARGET_REG_BITS == 32 && is_64 ? *args++ : 0); + addr_regl = *args++; + addr_regh = (TCG_TARGET_REG_BITS < TARGET_LONG_BITS ? *args++ : 0); + oi = *args++; + opc = get_memop(oi); + +#if defined(CONFIG_SOFTMMU) + tcg_out_tlb_load(s, 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, + (is_64 ? TCG_TYPE_I64 : TCG_TYPE_I32), + data_regl, data_regh, addr_regl, addr_regh, + s->code_ptr, label_ptr); +#else + if (TCG_TARGET_REG_BITS > TARGET_LONG_BITS) { + tcg_out_ext32u(s, base, addr_regl); + addr_regl = base; + } + + if (guest_base == 0) { + tcg_out_opc_reg(s, OPC_ADD, base, addr_regl, TCG_REG_ZERO); + } else { + tcg_out_opc_reg(s, OPC_ADD, base, TCG_GUEST_BASE_REG, addr_regl); + } + tcg_out_qemu_st_direct(s, data_regl, data_regh, base, opc); +#endif +} + +static tcg_insn_unit *tb_ret_addr; + +static void tcg_out_op(TCGContext *s, TCGOpcode opc, + const TCGArg *args, const int *const_args) +{ + TCGArg a0 = args[0]; + TCGArg a1 = args[1]; + TCGArg a2 = args[2]; + int c2 = const_args[2]; + + switch (opc) { + case INDEX_op_exit_tb: + /* Reuse the zeroing that exists for goto_ptr. */ + if (a0 == 0) { + tcg_out_call_int(s, s->code_gen_epilogue, true); + } else { + tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_A0, a0); + tcg_out_call_int(s, tb_ret_addr, true); + } + break; + + case INDEX_op_goto_tb: + assert(s->tb_jmp_insn_offset == 0); + /* indirect jump method */ + tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP0, TCG_REG_ZERO, + (uintptr_t)(s->tb_jmp_target_addr + a0)); + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, TCG_REG_TMP0, 0); + set_jmp_reset_offset(s, a0); + break; + + case INDEX_op_goto_ptr: + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); + break; + + case INDEX_op_br: + tcg_out_reloc(s, s->code_ptr, R_RISCV_JAL, arg_label(a0), 0); + tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, 0); + break; + + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + tcg_out_ldst(s, OPC_LBU, a0, a1, a2); + break; + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + tcg_out_ldst(s, OPC_LB, a0, a1, a2); + break; + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + tcg_out_ldst(s, OPC_LHU, a0, a1, a2); + break; + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + tcg_out_ldst(s, OPC_LH, a0, a1, a2); + break; + case INDEX_op_ld32u_i64: + tcg_out_ldst(s, OPC_LWU, a0, a1, a2); + break; + case INDEX_op_ld_i32: + case INDEX_op_ld32s_i64: + tcg_out_ldst(s, OPC_LW, a0, a1, a2); + break; + case INDEX_op_ld_i64: + tcg_out_ldst(s, OPC_LD, a0, a1, a2); + break; + + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + tcg_out_ldst(s, OPC_SB, a0, a1, a2); + break; + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + tcg_out_ldst(s, OPC_SH, a0, a1, a2); + break; + case INDEX_op_st_i32: + case INDEX_op_st32_i64: + tcg_out_ldst(s, OPC_SW, a0, a1, a2); + break; + case INDEX_op_st_i64: + tcg_out_ldst(s, OPC_SD, a0, a1, a2); + break; + + case INDEX_op_add_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_ADDW, a0, a1, a2); + } + break; + case INDEX_op_add_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_ADDI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_ADD, a0, a1, a2); + } + break; + + case INDEX_op_sub_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, -a2); + } else { + tcg_out_opc_reg(s, OPC_SUBW, a0, a1, a2); + } + break; + case INDEX_op_sub_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_ADDI, a0, a1, -a2); + } else { + tcg_out_opc_reg(s, OPC_SUB, a0, a1, a2); + } + break; + + case INDEX_op_and_i32: + case INDEX_op_and_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); + } + break; + + case INDEX_op_or_i32: + case INDEX_op_or_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); + } + break; + + case INDEX_op_xor_i32: + case INDEX_op_xor_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); + } + break; + + case INDEX_op_not_i32: + case INDEX_op_not_i64: + tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); + break; + + case INDEX_op_neg_i32: + tcg_out_opc_reg(s, OPC_SUBW, a0, TCG_REG_ZERO, a1); + break; + case INDEX_op_neg_i64: + tcg_out_opc_reg(s, OPC_SUB, a0, TCG_REG_ZERO, a1); + break; + + case INDEX_op_mul_i32: + tcg_out_opc_reg(s, OPC_MULW, a0, a1, a2); + break; + case INDEX_op_mul_i64: + tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); + break; + + case INDEX_op_div_i32: + tcg_out_opc_reg(s, OPC_DIVW, a0, a1, a2); + break; + case INDEX_op_div_i64: + tcg_out_opc_reg(s, OPC_DIV, a0, a1, a2); + break; + + case INDEX_op_divu_i32: + tcg_out_opc_reg(s, OPC_DIVUW, a0, a1, a2); + break; + case INDEX_op_divu_i64: + tcg_out_opc_reg(s, OPC_DIVU, a0, a1, a2); + break; + + case INDEX_op_rem_i32: + tcg_out_opc_reg(s, OPC_REMW, a0, a1, a2); + break; + case INDEX_op_rem_i64: + tcg_out_opc_reg(s, OPC_REM, a0, a1, a2); + break; + + case INDEX_op_remu_i32: + tcg_out_opc_reg(s, OPC_REMUW, a0, a1, a2); + break; + case INDEX_op_remu_i64: + tcg_out_opc_reg(s, OPC_REMU, a0, a1, a2); + break; + + case INDEX_op_shl_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_SLLIW, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_SLLW, a0, a1, a2); + } + break; + case INDEX_op_shl_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_SLLI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_SLL, a0, a1, a2); + } + break; + + case INDEX_op_shr_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_SRLW, a0, a1, a2); + } + break; + case INDEX_op_shr_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_SRL, a0, a1, a2); + } + break; + + case INDEX_op_sar_i32: + if (c2) { + tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_SRAW, a0, a1, a2); + } + break; + case INDEX_op_sar_i64: + if (c2) { + tcg_out_opc_imm(s, OPC_SRAI, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_SRA, a0, a1, a2); + } + 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, true); + break; + case INDEX_op_add2_i64: + tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], + const_args[4], const_args[5], false, 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, true); + break; + case INDEX_op_sub2_i64: + tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], + const_args[4], const_args[5], true, false); + break; + + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + 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_setcond_i32: + case INDEX_op_setcond_i64: + 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_ext8u_i32: + case INDEX_op_ext8u_i64: + tcg_out_ext8u(s, a0, a1); + break; + + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + tcg_out_ext16u(s, a0, a1); + break; + + case INDEX_op_ext32u_i64: + case INDEX_op_extu_i32_i64: + tcg_out_ext32u(s, a0, a1); + break; + + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + tcg_out_ext8s(s, a0, a1); + break; + + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + tcg_out_ext16s(s, a0, a1); + break; + + case INDEX_op_ext32s_i64: + case INDEX_op_extrl_i64_i32: + case INDEX_op_ext_i32_i64: + tcg_out_ext32s(s, a0, a1); + break; + + case INDEX_op_extrh_i64_i32: + tcg_out_opc_imm(s, OPC_SRAI, a0, a1, 32); + break; + + case INDEX_op_mulsh_i32: + case INDEX_op_mulsh_i64: + tcg_out_opc_reg(s, OPC_MULH, a0, a1, a2); + break; + + case INDEX_op_muluh_i32: + case INDEX_op_muluh_i64: + tcg_out_opc_reg(s, OPC_MULHU, a0, a1, a2); + break; + + case INDEX_op_mb: + tcg_out_mb(s, a0); + 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: + g_assert_not_reached(); + } +} + +static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode op) +{ + static const TCGTargetOpDef r + = { .args_ct_str = { "r" } }; + static const TCGTargetOpDef r_r + = { .args_ct_str = { "r", "r" } }; + static const TCGTargetOpDef rZ_r + = { .args_ct_str = { "rZ", "r" } }; + static const TCGTargetOpDef rZ_rZ + = { .args_ct_str = { "rZ", "rZ" } }; + static const TCGTargetOpDef rZ_rZ_rZ_rZ + = { .args_ct_str = { "rZ", "rZ", "rZ", "rZ" } }; + static const TCGTargetOpDef r_r_ri + = { .args_ct_str = { "r", "r", "ri" } }; + static const TCGTargetOpDef r_r_rI + = { .args_ct_str = { "r", "r", "rI" } }; + static const TCGTargetOpDef r_rZ_rN + = { .args_ct_str = { "r", "rZ", "rN" } }; + static const TCGTargetOpDef r_rZ_rZ + = { .args_ct_str = { "r", "rZ", "rZ" } }; + static const TCGTargetOpDef r_rZ_rZ_rZ_rZ + = { .args_ct_str = { "r", "rZ", "rZ", "rZ", "rZ" } }; + static const TCGTargetOpDef r_L + = { .args_ct_str = { "r", "L" } }; + static const TCGTargetOpDef r_r_L + = { .args_ct_str = { "r", "r", "L" } }; + static const TCGTargetOpDef r_L_L + = { .args_ct_str = { "r", "L", "L" } }; + static const TCGTargetOpDef r_r_L_L + = { .args_ct_str = { "r", "r", "L", "L" } }; + static const TCGTargetOpDef LZ_L + = { .args_ct_str = { "LZ", "L" } }; + static const TCGTargetOpDef LZ_L_L + = { .args_ct_str = { "LZ", "L", "L" } }; + static const TCGTargetOpDef LZ_LZ_L + = { .args_ct_str = { "LZ", "LZ", "L" } }; + static const TCGTargetOpDef LZ_LZ_L_L + = { .args_ct_str = { "LZ", "LZ", "L", "L" } }; + static const TCGTargetOpDef r_r_rZ_rZ_rM_rM + = { .args_ct_str = { "r", "r", "rZ", "rZ", "rM", "rM" } }; + + switch (op) { + case INDEX_op_goto_ptr: + return &r; + + 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_not_i32: + case INDEX_op_neg_i32: + case INDEX_op_ld8u_i64: + case INDEX_op_ld8s_i64: + case INDEX_op_ld16u_i64: + case INDEX_op_ld16s_i64: + case INDEX_op_ld32s_i64: + case INDEX_op_ld32u_i64: + case INDEX_op_ld_i64: + case INDEX_op_not_i64: + case INDEX_op_neg_i64: + case INDEX_op_ext8u_i32: + case INDEX_op_ext8u_i64: + case INDEX_op_ext16u_i32: + case INDEX_op_ext16u_i64: + case INDEX_op_ext32u_i64: + case INDEX_op_extu_i32_i64: + case INDEX_op_ext8s_i32: + case INDEX_op_ext8s_i64: + case INDEX_op_ext16s_i32: + case INDEX_op_ext16s_i64: + case INDEX_op_ext32s_i64: + case INDEX_op_extrl_i64_i32: + case INDEX_op_extrh_i64_i32: + case INDEX_op_ext_i32_i64: + return &r_r; + + case INDEX_op_st8_i32: + case INDEX_op_st16_i32: + case INDEX_op_st_i32: + case INDEX_op_st8_i64: + case INDEX_op_st16_i64: + case INDEX_op_st32_i64: + case INDEX_op_st_i64: + return &rZ_r; + + case INDEX_op_add_i32: + case INDEX_op_and_i32: + case INDEX_op_or_i32: + case INDEX_op_xor_i32: + case INDEX_op_add_i64: + case INDEX_op_and_i64: + case INDEX_op_or_i64: + case INDEX_op_xor_i64: + return &r_r_rI; + + case INDEX_op_sub_i32: + case INDEX_op_sub_i64: + return &r_rZ_rN; + + case INDEX_op_mul_i32: + case INDEX_op_mulsh_i32: + case INDEX_op_muluh_i32: + case INDEX_op_div_i32: + case INDEX_op_divu_i32: + case INDEX_op_rem_i32: + case INDEX_op_remu_i32: + case INDEX_op_setcond_i32: + case INDEX_op_mul_i64: + case INDEX_op_mulsh_i64: + case INDEX_op_muluh_i64: + case INDEX_op_div_i64: + case INDEX_op_divu_i64: + case INDEX_op_rem_i64: + case INDEX_op_remu_i64: + case INDEX_op_setcond_i64: + return &r_rZ_rZ; + + case INDEX_op_shl_i32: + case INDEX_op_shr_i32: + case INDEX_op_sar_i32: + case INDEX_op_shl_i64: + case INDEX_op_shr_i64: + case INDEX_op_sar_i64: + return &r_r_ri; + + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + return &rZ_rZ; + + case INDEX_op_add2_i32: + case INDEX_op_add2_i64: + case INDEX_op_sub2_i32: + case INDEX_op_sub2_i64: + return &r_r_rZ_rZ_rM_rM; + + case INDEX_op_brcond2_i32: + return &rZ_rZ_rZ_rZ; + + case INDEX_op_setcond2_i32: + return &r_rZ_rZ_rZ_rZ; + + case INDEX_op_qemu_ld_i32: + return TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? &r_L : &r_L_L; + case INDEX_op_qemu_st_i32: + return TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? &LZ_L : &LZ_L_L; + case INDEX_op_qemu_ld_i64: + return TCG_TARGET_REG_BITS == 64 ? &r_L + : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? &r_r_L + : &r_r_L_L; + case INDEX_op_qemu_st_i64: + return TCG_TARGET_REG_BITS == 64 ? &LZ_L + : TARGET_LONG_BITS <= TCG_TARGET_REG_BITS ? &LZ_LZ_L + : &LZ_LZ_L_L; + + default: + return NULL; + } +} + +static const 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_S9, + TCG_REG_S10, + TCG_REG_S11, + TCG_REG_RA, /* should be last for ABI compliance */ +}; + +/* Stack frame parameters. */ +#define REG_SIZE (TCG_TARGET_REG_BITS / 8) +#define SAVE_SIZE ((int)ARRAY_SIZE(tcg_target_callee_save_regs) * REG_SIZE) +#define TEMP_SIZE (CPU_TEMP_BUF_NLONGS * (int)sizeof(long)) +#define FRAME_SIZE ((TCG_STATIC_CALL_ARGS_SIZE + TEMP_SIZE + SAVE_SIZE \ + + TCG_TARGET_STACK_ALIGN - 1) \ + & -TCG_TARGET_STACK_ALIGN) +#define SAVE_OFS (TCG_STATIC_CALL_ARGS_SIZE + TEMP_SIZE) + +/* We're expecting to be able to use an immediate for frame allocation. */ +QEMU_BUILD_BUG_ON(FRAME_SIZE > 0x7ff); + +/* Generate global QEMU prologue and epilogue code */ +static void tcg_target_qemu_prologue(TCGContext *s) +{ + int i; + + tcg_set_frame(s, TCG_REG_SP, TCG_STATIC_CALL_ARGS_SIZE, TEMP_SIZE); + + /* TB prologue */ + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_SP, TCG_REG_SP, -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_SP, SAVE_OFS + i * REG_SIZE); + } + +#if !defined(CONFIG_SOFTMMU) + 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 + + /* Call generated code */ + tcg_out_mov(s, TCG_TYPE_PTR, TCG_AREG0, tcg_target_call_iarg_regs[0]); + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, tcg_target_call_iarg_regs[1], 0); + + /* Return path for goto_ptr. Set return value to 0 */ + s->code_gen_epilogue = s->code_ptr; + tcg_out_mov(s, TCG_TYPE_REG, TCG_REG_A0, TCG_REG_ZERO); + + /* TB epilogue */ + tb_ret_addr = s->code_ptr; + 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_SP, SAVE_OFS + i * REG_SIZE); + } + + tcg_out_opc_imm(s, OPC_ADDI, TCG_REG_SP, TCG_REG_SP, FRAME_SIZE); + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, TCG_REG_RA, 0); +} + +static void tcg_target_init(TCGContext *s) +{ + tcg_target_available_regs[TCG_TYPE_I32] = 0xffffffff; + if (TCG_TARGET_REG_BITS == 64) { + tcg_target_available_regs[TCG_TYPE_I64] = 0xffffffff; + } + + tcg_target_call_clobber_regs = -1u; + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S0); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S1); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S2); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S3); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S4); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S5); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S6); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S7); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S8); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S9); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S10); + tcg_regset_reset_reg(tcg_target_call_clobber_regs, TCG_REG_S11); + + s->reserved_regs = 0; + tcg_regset_set_reg(s->reserved_regs, TCG_REG_ZERO); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP0); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP1); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TMP2); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_SP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_GP); + tcg_regset_set_reg(s->reserved_regs, TCG_REG_TP); +} + +typedef struct { + DebugFrameHeader h; + uint8_t fde_def_cfa[4]; + uint8_t fde_reg_ofs[ARRAY_SIZE(tcg_target_callee_save_regs) * 2]; +} DebugFrame; + +#define ELF_HOST_MACHINE EM_RISCV + +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 = -(TCG_TARGET_REG_BITS / 8) & 0x7f, /* sleb128 */ + .h.cie.return_column = TCG_REG_RA, + + /* 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 + 9, 12, /* DW_CFA_offset, s1, -96 */ + 0x80 + 18, 11, /* DW_CFA_offset, s2, -88 */ + 0x80 + 19, 10, /* DW_CFA_offset, s3, -80 */ + 0x80 + 20, 9, /* DW_CFA_offset, s4, -72 */ + 0x80 + 21, 8, /* DW_CFA_offset, s5, -64 */ + 0x80 + 22, 7, /* DW_CFA_offset, s6, -56 */ + 0x80 + 23, 6, /* DW_CFA_offset, s7, -48 */ + 0x80 + 24, 5, /* DW_CFA_offset, s8, -40 */ + 0x80 + 25, 4, /* DW_CFA_offset, s9, -32 */ + 0x80 + 26, 3, /* DW_CFA_offset, s10, -24 */ + 0x80 + 27, 2, /* DW_CFA_offset, s11, -16 */ + 0x80 + 1 , 1, /* DW_CFA_offset, ra, -8 */ + } +}; + +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/tcg/s390/tcg-target.h b/tcg/s390/tcg-target.h index 6f2b06a7d1..853ed6e7aa 100644 --- a/tcg/s390/tcg-target.h +++ b/tcg/s390/tcg-target.h @@ -135,6 +135,7 @@ extern uint64_t s390_facilities; #define TCG_TARGET_CALL_STACK_OFFSET 160 #define TCG_TARGET_EXTEND_ARGS 1 +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 #define TCG_TARGET_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) diff --git a/tcg/s390/tcg-target.inc.c b/tcg/s390/tcg-target.inc.c index 17c435ade5..7db90b3bae 100644 --- a/tcg/s390/tcg-target.inc.c +++ b/tcg/s390/tcg-target.inc.c @@ -366,7 +366,7 @@ static void * const qemu_st_helpers[16] = { static tcg_insn_unit *tb_ret_addr; uint64_t s390_facilities; -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { intptr_t pcrel2; @@ -377,22 +377,29 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, switch (type) { case R_390_PC16DBL: - assert(pcrel2 == (int16_t)pcrel2); - tcg_patch16(code_ptr, pcrel2); + if (pcrel2 == (int16_t)pcrel2) { + tcg_patch16(code_ptr, pcrel2); + return true; + } break; case R_390_PC32DBL: - assert(pcrel2 == (int32_t)pcrel2); - tcg_patch32(code_ptr, pcrel2); + if (pcrel2 == (int32_t)pcrel2) { + tcg_patch32(code_ptr, pcrel2); + return true; + } break; case R_390_20: - assert(value == sextract64(value, 0, 20)); - old = *(uint32_t *)code_ptr & 0xf00000ff; - old |= ((value & 0xfff) << 16) | ((value & 0xff000) >> 4); - tcg_patch32(code_ptr, old); + if (value == sextract64(value, 0, 20)) { + old = *(uint32_t *)code_ptr & 0xf00000ff; + old |= ((value & 0xfff) << 16) | ((value & 0xff000) >> 4); + tcg_patch32(code_ptr, old); + return true; + } break; default: g_assert_not_reached(); } + return false; } /* parse target specific constraints */ @@ -1329,13 +1336,12 @@ static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, TCGReg r1, TCGReg r2, TCGLabel *l) { - intptr_t off; + intptr_t off = 0; if (l->has_value) { off = l->u.value_ptr - s->code_ptr; + tcg_debug_assert(off == (int16_t)off); } 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); } @@ -1347,13 +1353,12 @@ static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, static void tgen_compare_imm_branch(TCGContext *s, S390Opcode opc, int cc, TCGReg r1, int i2, TCGLabel *l) { - tcg_target_long off; + tcg_target_long off = 0; if (l->has_value) { off = l->u.value_ptr - s->code_ptr; + tcg_debug_assert(off == (int16_t)off); } 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); } @@ -1532,10 +1537,10 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGMemOp opc, TCGReg data, #if defined(CONFIG_SOFTMMU) #include "tcg-ldst.inc.c" -/* 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]) +/* We're expecting to use a 20-bit signed offset on the tlb memory ops. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_mask[NB_MMU_MODES - 1]) + > 0x7ffff); +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table[NB_MMU_MODES - 1]) > 0x7ffff); /* Load and compare a TLB entry, leaving the flags set. Loads the TLB @@ -1547,48 +1552,41 @@ static TCGReg tcg_out_tlb_read(TCGContext* s, TCGReg addr_reg, TCGMemOp opc, unsigned a_bits = get_alignment_bits(opc); unsigned s_mask = (1 << s_bits) - 1; unsigned a_mask = (1 << a_bits) - 1; + int mask_off = offsetof(CPUArchState, tlb_mask[mem_index]); + int table_off = offsetof(CPUArchState, tlb_table[mem_index]); int ofs, a_off; uint64_t tlb_mask; + 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, RXY, NG, TCG_REG_R2, TCG_AREG0, TCG_REG_NONE, mask_off); + tcg_out_insn(s, RXY, AG, TCG_REG_R2, TCG_AREG0, TCG_REG_NONE, table_off); + /* 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. */ a_off = (a_bits >= s_bits ? 0 : s_mask - a_mask); tlb_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; - - if (s390_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); - } + if ((s390_facilities & FACILITY_GEN_INST_EXT) && a_off == 0) { + 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); + ofs = offsetof(CPUTLBEntry, addr_read); } else { - ofs = offsetof(CPUArchState, tlb_table[mem_index][0].addr_write); + ofs = offsetof(CPUTLBEntry, addr_write); } if (TARGET_LONG_BITS == 32) { - tcg_out_mem(s, RX_C, RXY_CY, TCG_REG_R3, TCG_REG_R2, TCG_AREG0, ofs); + tcg_out_insn(s, RX, C, TCG_REG_R3, TCG_REG_R2, TCG_REG_NONE, ofs); } else { - tcg_out_mem(s, 0, RXY_CG, TCG_REG_R3, TCG_REG_R2, TCG_AREG0, ofs); + tcg_out_insn(s, RXY, CG, TCG_REG_R3, TCG_REG_R2, TCG_REG_NONE, 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); + tcg_out_insn(s, RXY, LG, TCG_REG_R2, TCG_REG_R2, TCG_REG_NONE, + offsetof(CPUTLBEntry, addend)); if (TARGET_LONG_BITS == 32) { tgen_ext32u(s, TCG_REG_R3, addr_reg); @@ -1618,7 +1616,9 @@ static void tcg_out_qemu_ld_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOpIdx oi = lb->oi; TCGMemOp opc = get_memop(oi); - patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)s->code_ptr, 2); + bool ok = patch_reloc(lb->label_ptr[0], R_390_PC16DBL, + (intptr_t)s->code_ptr, 2); + tcg_debug_assert(ok); tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); if (TARGET_LONG_BITS == 64) { @@ -1639,7 +1639,9 @@ static void tcg_out_qemu_st_slow_path(TCGContext *s, TCGLabelQemuLdst *lb) TCGMemOpIdx oi = lb->oi; TCGMemOp opc = get_memop(oi); - patch_reloc(lb->label_ptr[0], R_390_PC16DBL, (intptr_t)s->code_ptr, 2); + bool ok = patch_reloc(lb->label_ptr[0], R_390_PC16DBL, + (intptr_t)s->code_ptr, 2); + tcg_debug_assert(ok); tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_R2, TCG_AREG0); if (TARGET_LONG_BITS == 64) { @@ -1696,7 +1698,6 @@ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_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; @@ -1724,7 +1725,6 @@ static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_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; diff --git a/tcg/sparc/tcg-target.h b/tcg/sparc/tcg-target.h index d8339bf010..a0ed2a3342 100644 --- a/tcg/sparc/tcg-target.h +++ b/tcg/sparc/tcg-target.h @@ -164,6 +164,7 @@ extern bool use_vis3_instructions; #define TCG_AREG0 TCG_REG_I0 #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 static inline void flush_icache_range(uintptr_t start, uintptr_t stop) { diff --git a/tcg/sparc/tcg-target.inc.c b/tcg/sparc/tcg-target.inc.c index 04bdc3df5e..7a61839dc1 100644 --- a/tcg/sparc/tcg-target.inc.c +++ b/tcg/sparc/tcg-target.inc.c @@ -291,7 +291,7 @@ static inline int check_fit_i32(int32_t val, unsigned int bits) # define check_fit_ptr check_fit_i32 #endif -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { uint32_t insn = *code_ptr; @@ -311,29 +311,12 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, insn &= ~INSN_OFF19(-1); insn |= INSN_OFF19(pcrel); break; - case R_SPARC_13: - /* Note that we're abusing this reloc type for our own needs. */ - if (!check_fit_ptr(value, 13)) { - int adj = (value > 0 ? 0xff8 : -0x1000); - value -= adj; - assert(check_fit_ptr(value, 13)); - *code_ptr++ = (ARITH_ADD | INSN_RD(TCG_REG_T2) - | INSN_RS1(TCG_REG_TB) | INSN_IMM13(adj)); - insn ^= INSN_RS1(TCG_REG_TB) ^ INSN_RS1(TCG_REG_T2); - } - insn &= ~INSN_IMM13(-1); - insn |= INSN_IMM13(value); - break; - case R_SPARC_32: - /* Note that we're abusing this reloc type for our own needs. */ - code_ptr[0] = deposit32(code_ptr[0], 0, 22, value >> 10); - code_ptr[1] = deposit32(code_ptr[1], 0, 10, value); - return; default: g_assert_not_reached(); } *code_ptr = insn; + return true; } /* parse target specific constraints */ @@ -459,6 +442,15 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, return; } + /* A 13-bit constant relative to the TB. */ + if (!in_prologue && USE_REG_TB) { + test = arg - (uintptr_t)s->code_gen_ptr; + if (check_fit_ptr(test, 13)) { + tcg_out_arithi(s, ret, TCG_REG_TB, test, ARITH_ADD); + 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); @@ -488,26 +480,6 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, TCGReg ret, return; } - if (!in_prologue) { - if (USE_REG_TB) { - intptr_t diff = arg - (uintptr_t)s->code_gen_ptr; - if (check_fit_ptr(diff, 13)) { - tcg_out_arithi(s, ret, TCG_REG_TB, diff, ARITH_ADD); - } else { - new_pool_label(s, arg, R_SPARC_13, s->code_ptr, - -(intptr_t)s->code_gen_ptr); - tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(TCG_REG_TB)); - /* May be used to extend the 13-bit range in patch_reloc. */ - tcg_out32(s, NOP); - } - } else { - new_pool_label(s, arg, R_SPARC_32, s->code_ptr, 0); - tcg_out_sethi(s, ret, 0); - tcg_out32(s, LDX | INSN_RD(ret) | INSN_RS1(ret) | INSN_IMM13(0)); - } - return; - } - /* A 64-bit constant decomposed into 2 32-bit pieces. */ if (check_fit_i32(lo, 13)) { hi = (arg - lo) >> 32; @@ -639,13 +611,11 @@ static void tcg_out_bpcc0(TCGContext *s, int scond, int flags, int off19) static void tcg_out_bpcc(TCGContext *s, int scond, int flags, TCGLabel *l) { - int off19; + int off19 = 0; 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); @@ -685,13 +655,11 @@ static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, { /* For 64-bit signed comparisons vs zero, we can avoid the compare. */ if (arg2 == 0 && !is_unsigned_cond(cond)) { - int off16; + int off16 = 0; 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) @@ -1106,54 +1074,72 @@ static void tcg_out_nop_fill(tcg_insn_unit *p, int count) 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. */ +/* We expect tlb_mask to be before tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) < + offsetof(CPUArchState, tlb_mask)); + +/* We expect tlb_mask to be "near" tlb_table. */ +QEMU_BUILD_BUG_ON(offsetof(CPUArchState, tlb_table) - + offsetof(CPUArchState, tlb_mask) >= (1 << 13)); + static TCGReg tcg_out_tlb_load(TCGContext *s, TCGReg addr, int mem_index, TCGMemOp opc, int which) { + int mask_off = offsetof(CPUArchState, tlb_mask[mem_index]); + int table_off = offsetof(CPUArchState, tlb_table[mem_index]); + TCGReg base = TCG_AREG0; const TCGReg r0 = TCG_REG_O0; const TCGReg r1 = TCG_REG_O1; const TCGReg r2 = TCG_REG_O2; unsigned s_bits = opc & MO_SIZE; unsigned a_bits = get_alignment_bits(opc); - int tlb_ofs; + tcg_target_long compare_mask; - /* Shift the page number down. */ - tcg_out_arithi(s, r1, addr, TARGET_PAGE_BITS, SHIFT_SRL); + if (!check_fit_i32(table_off, 13)) { + int table_hi; + + base = r1; + if (table_off <= 2 * 0xfff) { + table_hi = 0xfff; + tcg_out_arithi(s, base, TCG_AREG0, table_hi, ARITH_ADD); + } else { + table_hi = table_off & ~0x3ff; + tcg_out_sethi(s, base, table_hi); + tcg_out_arith(s, base, TCG_AREG0, base, ARITH_ADD); + } + mask_off -= table_hi; + table_off -= table_hi; + tcg_debug_assert(check_fit_i32(mask_off, 13)); + } + + /* Load tlb_mask[mmu_idx] and tlb_table[mmu_idx]. */ + tcg_out_ld(s, TCG_TYPE_PTR, r0, base, mask_off); + tcg_out_ld(s, TCG_TYPE_PTR, r1, base, table_off); + + /* Extract the page index, shifted into place for tlb index. */ + tcg_out_arithi(s, r2, addr, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS, + SHIFT_SRL); + tcg_out_arith(s, r2, r2, r0, ARITH_AND); + + /* Add the tlb_table pointer, creating the CPUTLBEntry address into R2. */ + tcg_out_arith(s, r2, r2, r1, ARITH_ADD); + + /* Load the tlb comparator and the addend. */ + tcg_out_ld(s, TCG_TYPE_TL, r0, r2, which); + tcg_out_ld(s, TCG_TYPE_PTR, r1, r2, offsetof(CPUTLBEntry, addend)); /* Mask out the page offset, except for the required alignment. We don't support unaligned accesses. */ if (a_bits < s_bits) { a_bits = s_bits; } - tcg_out_movi(s, TCG_TYPE_TL, TCG_REG_T1, - TARGET_PAGE_MASK | ((1 << a_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; + compare_mask = (tcg_target_ulong)TARGET_PAGE_MASK | ((1 << a_bits) - 1); + if (check_fit_tl(compare_mask, 13)) { + tcg_out_arithi(s, r2, addr, compare_mask, ARITH_AND); + } else { + tcg_out_movi(s, TCG_TYPE_TL, r2, compare_mask); + tcg_out_arith(s, r2, addr, r2, ARITH_AND); } - - /* 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. */ diff --git a/tcg/tcg-gvec-desc.h b/tcg/tcg-gvec-desc.h index 3b4c2d9c69..2dda7d6ba1 100644 --- a/tcg/tcg-gvec-desc.h +++ b/tcg/tcg-gvec-desc.h @@ -6,7 +6,7 @@ * 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. + * 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 diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 61c25f5784..0996ef0812 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -665,7 +665,7 @@ static void expand_3_i32(uint32_t dofs, uint32_t aofs, /* Expand OPSZ bytes worth of three-operand operations using i32 elements. */ static void expand_4_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t oprsz, + uint32_t cofs, uint32_t oprsz, bool write_aofs, void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32)) { TCGv_i32 t0 = tcg_temp_new_i32(); @@ -680,6 +680,9 @@ static void expand_4_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_gen_ld_i32(t3, cpu_env, cofs + i); fni(t0, t1, t2, t3); tcg_gen_st_i32(t0, cpu_env, dofs + i); + if (write_aofs) { + tcg_gen_st_i32(t1, cpu_env, aofs + i); + } } tcg_temp_free_i32(t3); tcg_temp_free_i32(t2); @@ -769,7 +772,7 @@ static void expand_3_i64(uint32_t dofs, uint32_t aofs, /* Expand OPSZ bytes worth of three-operand operations using i64 elements. */ static void expand_4_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t cofs, uint32_t oprsz, + uint32_t cofs, uint32_t oprsz, bool write_aofs, void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64, TCGv_i64)) { TCGv_i64 t0 = tcg_temp_new_i64(); @@ -784,6 +787,9 @@ static void expand_4_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_gen_ld_i64(t3, cpu_env, cofs + i); fni(t0, t1, t2, t3); tcg_gen_st_i64(t0, cpu_env, dofs + i); + if (write_aofs) { + tcg_gen_st_i64(t1, cpu_env, aofs + i); + } } tcg_temp_free_i64(t3); tcg_temp_free_i64(t2); @@ -880,7 +886,7 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, /* Expand OPSZ bytes worth of four-operand operations using host vectors. */ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, - uint32_t tysz, TCGType type, + uint32_t tysz, TCGType type, bool write_aofs, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec, TCGv_vec)) { @@ -896,6 +902,9 @@ static void expand_4_vec(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_ld_vec(t3, cpu_env, cofs + i); fni(vece, t0, t1, t2, t3); tcg_gen_st_vec(t0, cpu_env, dofs + i); + if (write_aofs) { + tcg_gen_st_vec(t1, cpu_env, aofs + i); + } } tcg_temp_free_vec(t3); tcg_temp_free_vec(t2); @@ -1187,7 +1196,7 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, */ some = QEMU_ALIGN_DOWN(oprsz, 32); expand_4_vec(g->vece, dofs, aofs, bofs, cofs, some, - 32, TCG_TYPE_V256, g->fniv); + 32, TCG_TYPE_V256, g->write_aofs, g->fniv); if (some == oprsz) { break; } @@ -1200,18 +1209,20 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, /* fallthru */ case TCG_TYPE_V128: expand_4_vec(g->vece, dofs, aofs, bofs, cofs, oprsz, - 16, TCG_TYPE_V128, g->fniv); + 16, TCG_TYPE_V128, g->write_aofs, g->fniv); break; case TCG_TYPE_V64: expand_4_vec(g->vece, dofs, aofs, bofs, cofs, oprsz, - 8, TCG_TYPE_V64, g->fniv); + 8, TCG_TYPE_V64, g->write_aofs, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_4_i64(dofs, aofs, bofs, cofs, oprsz, g->fni8); + expand_4_i64(dofs, aofs, bofs, cofs, oprsz, + g->write_aofs, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_4_i32(dofs, aofs, bofs, cofs, oprsz, g->fni4); + expand_4_i32(dofs, aofs, bofs, cofs, oprsz, + g->write_aofs, g->fni4); } else { assert(g->fno != NULL); tcg_gen_gvec_4_ool(dofs, aofs, bofs, cofs, @@ -1667,10 +1678,22 @@ void tcg_gen_gvec_ssadd(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { - { .fno = gen_helper_gvec_ssadd8, .vece = MO_8 }, - { .fno = gen_helper_gvec_ssadd16, .vece = MO_16 }, - { .fno = gen_helper_gvec_ssadd32, .vece = MO_32 }, - { .fno = gen_helper_gvec_ssadd64, .vece = MO_64 } + { .fniv = tcg_gen_ssadd_vec, + .fno = gen_helper_gvec_ssadd8, + .opc = INDEX_op_ssadd_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_ssadd_vec, + .fno = gen_helper_gvec_ssadd16, + .opc = INDEX_op_ssadd_vec, + .vece = MO_16 }, + { .fniv = tcg_gen_ssadd_vec, + .fno = gen_helper_gvec_ssadd32, + .opc = INDEX_op_ssadd_vec, + .vece = MO_32 }, + { .fniv = tcg_gen_ssadd_vec, + .fno = gen_helper_gvec_ssadd64, + .opc = INDEX_op_ssadd_vec, + .vece = MO_64 }, }; tcg_debug_assert(vece <= MO_64); tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); @@ -1680,16 +1703,28 @@ void tcg_gen_gvec_sssub(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { - { .fno = gen_helper_gvec_sssub8, .vece = MO_8 }, - { .fno = gen_helper_gvec_sssub16, .vece = MO_16 }, - { .fno = gen_helper_gvec_sssub32, .vece = MO_32 }, - { .fno = gen_helper_gvec_sssub64, .vece = MO_64 } + { .fniv = tcg_gen_sssub_vec, + .fno = gen_helper_gvec_sssub8, + .opc = INDEX_op_sssub_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_sssub_vec, + .fno = gen_helper_gvec_sssub16, + .opc = INDEX_op_sssub_vec, + .vece = MO_16 }, + { .fniv = tcg_gen_sssub_vec, + .fno = gen_helper_gvec_sssub32, + .opc = INDEX_op_sssub_vec, + .vece = MO_32 }, + { .fniv = tcg_gen_sssub_vec, + .fno = gen_helper_gvec_sssub64, + .opc = INDEX_op_sssub_vec, + .vece = MO_64 }, }; tcg_debug_assert(vece <= MO_64); tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); } -static void tcg_gen_vec_usadd32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +static void tcg_gen_usadd_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { TCGv_i32 max = tcg_const_i32(-1); tcg_gen_add_i32(d, a, b); @@ -1697,7 +1732,7 @@ static void tcg_gen_vec_usadd32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_temp_free_i32(max); } -static void tcg_gen_vec_usadd32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +static void tcg_gen_usadd_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { TCGv_i64 max = tcg_const_i64(-1); tcg_gen_add_i64(d, a, b); @@ -1709,20 +1744,30 @@ void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { - { .fno = gen_helper_gvec_usadd8, .vece = MO_8 }, - { .fno = gen_helper_gvec_usadd16, .vece = MO_16 }, - { .fni4 = tcg_gen_vec_usadd32_i32, + { .fniv = tcg_gen_usadd_vec, + .fno = gen_helper_gvec_usadd8, + .opc = INDEX_op_usadd_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_usadd_vec, + .fno = gen_helper_gvec_usadd16, + .opc = INDEX_op_usadd_vec, + .vece = MO_16 }, + { .fni4 = tcg_gen_usadd_i32, + .fniv = tcg_gen_usadd_vec, .fno = gen_helper_gvec_usadd32, + .opc = INDEX_op_usadd_vec, .vece = MO_32 }, - { .fni8 = tcg_gen_vec_usadd32_i64, + { .fni8 = tcg_gen_usadd_i64, + .fniv = tcg_gen_usadd_vec, .fno = gen_helper_gvec_usadd64, + .opc = INDEX_op_usadd_vec, .vece = MO_64 } }; tcg_debug_assert(vece <= MO_64); tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); } -static void tcg_gen_vec_ussub32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) +static void tcg_gen_ussub_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) { TCGv_i32 min = tcg_const_i32(0); tcg_gen_sub_i32(d, a, b); @@ -1730,7 +1775,7 @@ static void tcg_gen_vec_ussub32_i32(TCGv_i32 d, TCGv_i32 a, TCGv_i32 b) tcg_temp_free_i32(min); } -static void tcg_gen_vec_ussub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) +static void tcg_gen_ussub_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) { TCGv_i64 min = tcg_const_i64(0); tcg_gen_sub_i64(d, a, b); @@ -1742,13 +1787,131 @@ void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { - { .fno = gen_helper_gvec_ussub8, .vece = MO_8 }, - { .fno = gen_helper_gvec_ussub16, .vece = MO_16 }, - { .fni4 = tcg_gen_vec_ussub32_i32, + { .fniv = tcg_gen_ussub_vec, + .fno = gen_helper_gvec_ussub8, + .opc = INDEX_op_ussub_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_ussub_vec, + .fno = gen_helper_gvec_ussub16, + .opc = INDEX_op_ussub_vec, + .vece = MO_16 }, + { .fni4 = tcg_gen_ussub_i32, + .fniv = tcg_gen_ussub_vec, .fno = gen_helper_gvec_ussub32, + .opc = INDEX_op_ussub_vec, .vece = MO_32 }, - { .fni8 = tcg_gen_vec_ussub32_i64, + { .fni8 = tcg_gen_ussub_i64, + .fniv = tcg_gen_ussub_vec, .fno = gen_helper_gvec_ussub64, + .opc = INDEX_op_ussub_vec, + .vece = MO_64 } + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_smin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g[4] = { + { .fniv = tcg_gen_smin_vec, + .fno = gen_helper_gvec_smin8, + .opc = INDEX_op_smin_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_smin_vec, + .fno = gen_helper_gvec_smin16, + .opc = INDEX_op_smin_vec, + .vece = MO_16 }, + { .fni4 = tcg_gen_smin_i32, + .fniv = tcg_gen_smin_vec, + .fno = gen_helper_gvec_smin32, + .opc = INDEX_op_smin_vec, + .vece = MO_32 }, + { .fni8 = tcg_gen_smin_i64, + .fniv = tcg_gen_smin_vec, + .fno = gen_helper_gvec_smin64, + .opc = INDEX_op_smin_vec, + .vece = MO_64 } + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_umin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g[4] = { + { .fniv = tcg_gen_umin_vec, + .fno = gen_helper_gvec_umin8, + .opc = INDEX_op_umin_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_umin_vec, + .fno = gen_helper_gvec_umin16, + .opc = INDEX_op_umin_vec, + .vece = MO_16 }, + { .fni4 = tcg_gen_umin_i32, + .fniv = tcg_gen_umin_vec, + .fno = gen_helper_gvec_umin32, + .opc = INDEX_op_umin_vec, + .vece = MO_32 }, + { .fni8 = tcg_gen_umin_i64, + .fniv = tcg_gen_umin_vec, + .fno = gen_helper_gvec_umin64, + .opc = INDEX_op_umin_vec, + .vece = MO_64 } + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_smax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g[4] = { + { .fniv = tcg_gen_smax_vec, + .fno = gen_helper_gvec_smax8, + .opc = INDEX_op_smax_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_smax_vec, + .fno = gen_helper_gvec_smax16, + .opc = INDEX_op_smax_vec, + .vece = MO_16 }, + { .fni4 = tcg_gen_smax_i32, + .fniv = tcg_gen_smax_vec, + .fno = gen_helper_gvec_smax32, + .opc = INDEX_op_smax_vec, + .vece = MO_32 }, + { .fni8 = tcg_gen_smax_i64, + .fniv = tcg_gen_smax_vec, + .fno = gen_helper_gvec_smax64, + .opc = INDEX_op_smax_vec, + .vece = MO_64 } + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g[4] = { + { .fniv = tcg_gen_umax_vec, + .fno = gen_helper_gvec_umax8, + .opc = INDEX_op_umax_vec, + .vece = MO_8 }, + { .fniv = tcg_gen_umax_vec, + .fno = gen_helper_gvec_umax16, + .opc = INDEX_op_umax_vec, + .vece = MO_16 }, + { .fni4 = tcg_gen_umax_i32, + .fniv = tcg_gen_umax_vec, + .fno = gen_helper_gvec_umax32, + .opc = INDEX_op_umax_vec, + .vece = MO_32 }, + { .fni8 = tcg_gen_umax_i64, + .fniv = tcg_gen_umax_vec, + .fno = gen_helper_gvec_umax64, + .opc = INDEX_op_umax_vec, .vece = MO_64 } }; tcg_debug_assert(vece <= MO_64); @@ -1840,7 +2003,12 @@ void tcg_gen_gvec_and(unsigned vece, uint32_t dofs, uint32_t aofs, .opc = INDEX_op_and_vec, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + + if (aofs == bofs) { + tcg_gen_gvec_mov(vece, dofs, aofs, oprsz, maxsz); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } } void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1853,7 +2021,12 @@ void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, .opc = INDEX_op_or_vec, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + + if (aofs == bofs) { + tcg_gen_gvec_mov(vece, dofs, aofs, oprsz, maxsz); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } } void tcg_gen_gvec_xor(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1866,7 +2039,12 @@ void tcg_gen_gvec_xor(unsigned vece, uint32_t dofs, uint32_t aofs, .opc = INDEX_op_xor_vec, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + + if (aofs == bofs) { + tcg_gen_gvec_dup8i(dofs, oprsz, maxsz, 0); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } } void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1879,7 +2057,12 @@ void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, .opc = INDEX_op_andc_vec, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + + if (aofs == bofs) { + tcg_gen_gvec_dup8i(dofs, oprsz, maxsz, 0); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } } void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1892,7 +2075,63 @@ void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, .opc = INDEX_op_orc_vec, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + + if (aofs == bofs) { + tcg_gen_gvec_dup8i(dofs, oprsz, maxsz, -1); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } +} + +void tcg_gen_gvec_nand(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g = { + .fni8 = tcg_gen_nand_i64, + .fniv = tcg_gen_nand_vec, + .fno = gen_helper_gvec_nand, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + + if (aofs == bofs) { + tcg_gen_gvec_not(vece, dofs, aofs, oprsz, maxsz); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } +} + +void tcg_gen_gvec_nor(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g = { + .fni8 = tcg_gen_nor_i64, + .fniv = tcg_gen_nor_vec, + .fno = gen_helper_gvec_nor, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + + if (aofs == bofs) { + tcg_gen_gvec_not(vece, dofs, aofs, oprsz, maxsz); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } +} + +void tcg_gen_gvec_eqv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + static const GVecGen3 g = { + .fni8 = tcg_gen_eqv_i64, + .fniv = tcg_gen_eqv_vec, + .fno = gen_helper_gvec_eqv, + .prefer_i64 = TCG_TARGET_REG_BITS == 64, + }; + + if (aofs == bofs) { + tcg_gen_gvec_dup8i(dofs, oprsz, maxsz, -1); + } else { + tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g); + } } static const GVecGen2s gop_ands = { diff --git a/tcg/tcg-op-gvec.h b/tcg/tcg-op-gvec.h index ff43a29a0b..850da32ded 100644 --- a/tcg/tcg-op-gvec.h +++ b/tcg/tcg-op-gvec.h @@ -6,7 +6,7 @@ * 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. + * 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 @@ -181,6 +181,8 @@ typedef struct { uint8_t vece; /* Prefer i64 to v64. */ bool prefer_i64; + /* Write aofs as a 2nd dest operand. */ + bool write_aofs; } GVecGen4; void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, @@ -232,6 +234,16 @@ void tcg_gen_gvec_usadd(unsigned vece, uint32_t dofs, uint32_t aofs, void tcg_gen_gvec_ussub(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +/* Min/max. */ +void tcg_gen_gvec_smin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_umin(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_smax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_umax(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); + void tcg_gen_gvec_and(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz); void tcg_gen_gvec_or(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -242,6 +254,12 @@ void tcg_gen_gvec_andc(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz); void tcg_gen_gvec_orc(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_nand(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_nor(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_eqv(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz); void tcg_gen_gvec_andi(unsigned vece, uint32_t dofs, uint32_t aofs, int64_t c, uint32_t oprsz, uint32_t maxsz); diff --git a/tcg/tcg-op-vec.c b/tcg/tcg-op-vec.c index cefba3d185..27f65600c3 100644 --- a/tcg/tcg-op-vec.c +++ b/tcg/tcg-op-vec.c @@ -6,7 +6,7 @@ * 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. + * 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 @@ -275,6 +275,27 @@ void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) } } +void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + /* TODO: Add TCG_TARGET_HAS_nand_vec when adding a backend supports it. */ + tcg_gen_and_vec(0, r, a, b); + tcg_gen_not_vec(0, r, r); +} + +void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + /* TODO: Add TCG_TARGET_HAS_nor_vec when adding a backend supports it. */ + tcg_gen_or_vec(0, r, a, b); + tcg_gen_not_vec(0, r, r); +} + +void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + /* TODO: Add TCG_TARGET_HAS_eqv_vec when adding a backend supports it. */ + tcg_gen_xor_vec(0, r, a, b); + tcg_gen_not_vec(0, r, r); +} + void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a) { if (TCG_TARGET_HAS_not_vec) { @@ -365,7 +386,8 @@ void tcg_gen_cmp_vec(TCGCond cond, unsigned vece, } } -void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +static void do_op3(unsigned vece, TCGv_vec r, TCGv_vec a, + TCGv_vec b, TCGOpcode opc) { TCGTemp *rt = tcgv_vec_temp(r); TCGTemp *at = tcgv_vec_temp(a); @@ -378,11 +400,56 @@ void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) tcg_debug_assert(at->base_type >= type); tcg_debug_assert(bt->base_type >= type); - can = tcg_can_emit_vec_op(INDEX_op_mul_vec, type, vece); + can = tcg_can_emit_vec_op(opc, type, vece); if (can > 0) { - vec_gen_3(INDEX_op_mul_vec, type, vece, ri, ai, bi); + vec_gen_3(opc, type, vece, ri, ai, bi); } else { tcg_debug_assert(can < 0); - tcg_expand_vec_op(INDEX_op_mul_vec, type, vece, ri, ai, bi); + tcg_expand_vec_op(opc, type, vece, ri, ai, bi); } } + +void tcg_gen_mul_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_mul_vec); +} + +void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_ssadd_vec); +} + +void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_usadd_vec); +} + +void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_sssub_vec); +} + +void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_ussub_vec); +} + +void tcg_gen_smin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_smin_vec); +} + +void tcg_gen_umin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_umin_vec); +} + +void tcg_gen_smax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_smax_vec); +} + +void tcg_gen_umax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b) +{ + do_op3(vece, r, a, b, INDEX_op_umax_vec); +} diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index daa416a143..1bd7ef24af 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -240,6 +240,7 @@ void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *l) if (cond == TCG_COND_ALWAYS) { tcg_gen_br(l); } else if (cond != TCG_COND_NEVER) { + l->refs++; tcg_gen_op4ii_i32(INDEX_op_brcond_i32, arg1, arg2, cond, label_arg(l)); } } @@ -1012,24 +1013,24 @@ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) if (TCG_TARGET_HAS_bswap32_i32) { tcg_gen_op2_i32(INDEX_op_bswap32_i32, ret, arg); } else { - TCGv_i32 t0, t1; - t0 = tcg_temp_new_i32(); - t1 = tcg_temp_new_i32(); + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + TCGv_i32 t2 = tcg_const_i32(0x00ff00ff); - tcg_gen_shli_i32(t0, arg, 24); + /* arg = abcd */ + tcg_gen_shri_i32(t0, arg, 8); /* t0 = .abc */ + tcg_gen_and_i32(t1, arg, t2); /* t1 = .b.d */ + tcg_gen_and_i32(t0, t0, t2); /* t0 = .a.c */ + tcg_gen_shli_i32(t1, t1, 8); /* t1 = b.d. */ + tcg_gen_or_i32(ret, t0, t1); /* ret = badc */ - tcg_gen_andi_i32(t1, arg, 0x0000ff00); - tcg_gen_shli_i32(t1, t1, 8); - tcg_gen_or_i32(t0, t0, t1); + tcg_gen_shri_i32(t0, ret, 16); /* t0 = ..ba */ + tcg_gen_shli_i32(t1, ret, 16); /* t1 = dc.. */ + tcg_gen_or_i32(ret, t0, t1); /* ret = dcba */ - tcg_gen_shri_i32(t1, arg, 8); - tcg_gen_andi_i32(t1, t1, 0x0000ff00); - tcg_gen_or_i32(t0, t0, t1); - - tcg_gen_shri_i32(t1, arg, 24); - tcg_gen_or_i32(ret, t0, t1); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); + tcg_temp_free_i32(t2); } } @@ -1405,6 +1406,7 @@ void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *l) if (cond == TCG_COND_ALWAYS) { tcg_gen_br(l); } else if (cond != TCG_COND_NEVER) { + l->refs++; if (TCG_TARGET_REG_BITS == 32) { tcg_gen_op6ii_i32(INDEX_op_brcond2_i32, TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), @@ -1638,25 +1640,25 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg) } else if (TCG_TARGET_HAS_bswap32_i64) { tcg_gen_op2_i64(INDEX_op_bswap32_i64, ret, arg); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_new_i64(); - t1 = tcg_temp_new_i64(); + TCGv_i64 t0 = tcg_temp_new_i64(); + TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_const_i64(0x00ff00ff); - tcg_gen_shli_i64(t0, arg, 24); - tcg_gen_ext32u_i64(t0, t0); + /* arg = ....abcd */ + tcg_gen_shri_i64(t0, arg, 8); /* t0 = .....abc */ + tcg_gen_and_i64(t1, arg, t2); /* t1 = .....b.d */ + tcg_gen_and_i64(t0, t0, t2); /* t0 = .....a.c */ + tcg_gen_shli_i64(t1, t1, 8); /* t1 = ....b.d. */ + tcg_gen_or_i64(ret, t0, t1); /* ret = ....badc */ - tcg_gen_andi_i64(t1, arg, 0x0000ff00); - tcg_gen_shli_i64(t1, t1, 8); - tcg_gen_or_i64(t0, t0, t1); + tcg_gen_shli_i64(t1, ret, 48); /* t1 = dc...... */ + tcg_gen_shri_i64(t0, ret, 16); /* t0 = ......ba */ + tcg_gen_shri_i64(t1, t1, 32); /* t1 = ....dc.. */ + tcg_gen_or_i64(ret, t0, t1); /* ret = ....dcba */ - tcg_gen_shri_i64(t1, arg, 8); - tcg_gen_andi_i64(t1, t1, 0x0000ff00); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 24); - tcg_gen_or_i64(ret, t0, t1); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); } } @@ -1678,37 +1680,30 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) } else { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); + TCGv_i64 t2 = tcg_temp_new_i64(); - tcg_gen_shli_i64(t0, arg, 56); + /* arg = abcdefgh */ + tcg_gen_movi_i64(t2, 0x00ff00ff00ff00ffull); + tcg_gen_shri_i64(t0, arg, 8); /* t0 = .abcdefg */ + tcg_gen_and_i64(t1, arg, t2); /* t1 = .b.d.f.h */ + tcg_gen_and_i64(t0, t0, t2); /* t0 = .a.c.e.g */ + tcg_gen_shli_i64(t1, t1, 8); /* t1 = b.d.f.h. */ + tcg_gen_or_i64(ret, t0, t1); /* ret = badcfehg */ - tcg_gen_andi_i64(t1, arg, 0x0000ff00); - tcg_gen_shli_i64(t1, t1, 40); - tcg_gen_or_i64(t0, t0, t1); + tcg_gen_movi_i64(t2, 0x0000ffff0000ffffull); + tcg_gen_shri_i64(t0, ret, 16); /* t0 = ..badcfe */ + tcg_gen_and_i64(t1, ret, t2); /* t1 = ..dc..hg */ + tcg_gen_and_i64(t0, t0, t2); /* t0 = ..ba..fe */ + tcg_gen_shli_i64(t1, t1, 16); /* t1 = dc..hg.. */ + tcg_gen_or_i64(ret, t0, t1); /* ret = dcbahgfe */ - tcg_gen_andi_i64(t1, arg, 0x00ff0000); - tcg_gen_shli_i64(t1, t1, 24); - tcg_gen_or_i64(t0, t0, t1); + tcg_gen_shri_i64(t0, ret, 32); /* t0 = ....dcba */ + tcg_gen_shli_i64(t1, ret, 32); /* t1 = hgfe.... */ + tcg_gen_or_i64(ret, t0, t1); /* ret = hgfedcba */ - tcg_gen_andi_i64(t1, arg, 0xff000000); - tcg_gen_shli_i64(t1, t1, 8); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 8); - tcg_gen_andi_i64(t1, t1, 0xff000000); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 24); - tcg_gen_andi_i64(t1, t1, 0x00ff0000); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 40); - tcg_gen_andi_i64(t1, t1, 0x0000ff00); - tcg_gen_or_i64(t0, t0, t1); - - tcg_gen_shri_i64(t1, arg, 56); - tcg_gen_or_i64(ret, t0, t1); tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); + tcg_temp_free_i64(t2); } } @@ -2586,6 +2581,10 @@ void tcg_gen_exit_tb(TranslationBlock *tb, unsigned idx) seen this numbered exit before, via tcg_gen_goto_tb. */ tcg_debug_assert(tcg_ctx->goto_tb_issue_mask & (1 << idx)); #endif + /* When not chaining, exit without indicating a link. */ + if (qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { + val = 0; + } } else { /* This is an exit via the exitreq label. */ tcg_debug_assert(idx == TB_EXIT_REQUESTED); @@ -2603,7 +2602,10 @@ void tcg_gen_goto_tb(unsigned idx) tcg_debug_assert((tcg_ctx->goto_tb_issue_mask & (1 << idx)) == 0); tcg_ctx->goto_tb_issue_mask |= 1 << idx; #endif - tcg_gen_op1i(INDEX_op_goto_tb, idx); + /* When not chaining, we simply fall through to the "fallback" exit. */ + if (!qemu_loglevel_mask(CPU_LOG_TB_NOCHAIN)) { + tcg_gen_op1i(INDEX_op_goto_tb, idx); + } } void tcg_gen_lookup_and_goto_ptr(void) @@ -2694,25 +2696,78 @@ static void tcg_gen_req_mo(TCGBar type) void tcg_gen_qemu_ld_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) { + TCGMemOp orig_memop; + tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 0, 0); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, trace_mem_get_info(memop, 0)); + + orig_memop = memop; + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + memop &= ~MO_BSWAP; + /* The bswap primitive requires zero-extended input. */ + if ((memop & MO_SSIZE) == MO_SW) { + memop &= ~MO_SIGN; + } + } + gen_ldst_i32(INDEX_op_qemu_ld_i32, val, addr, memop, idx); + + if ((orig_memop ^ memop) & MO_BSWAP) { + switch (orig_memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i32(val, val); + if (orig_memop & MO_SIGN) { + tcg_gen_ext16s_i32(val, val); + } + break; + case MO_32: + tcg_gen_bswap32_i32(val, val); + break; + default: + g_assert_not_reached(); + } + } } void tcg_gen_qemu_st_i32(TCGv_i32 val, TCGv addr, TCGArg idx, TCGMemOp memop) { + TCGv_i32 swap = NULL; + tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, trace_mem_get_info(memop, 1)); + + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + swap = tcg_temp_new_i32(); + switch (memop & MO_SIZE) { + case MO_16: + tcg_gen_ext16u_i32(swap, val); + tcg_gen_bswap16_i32(swap, swap); + break; + case MO_32: + tcg_gen_bswap32_i32(swap, val); + break; + default: + g_assert_not_reached(); + } + val = swap; + memop &= ~MO_BSWAP; + } + gen_ldst_i32(INDEX_op_qemu_st_i32, val, addr, memop, idx); + + if (swap) { + tcg_temp_free_i32(swap); + } } void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) { - tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + TCGMemOp orig_memop; + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_ld_i32(TCGV_LOW(val), addr, idx, memop); if (memop & MO_SIGN) { @@ -2723,24 +2778,85 @@ void tcg_gen_qemu_ld_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) return; } + tcg_gen_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); memop = tcg_canonicalize_memop(memop, 1, 0); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, trace_mem_get_info(memop, 0)); + + orig_memop = memop; + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + memop &= ~MO_BSWAP; + /* The bswap primitive requires zero-extended input. */ + if ((memop & MO_SIGN) && (memop & MO_SIZE) < MO_64) { + memop &= ~MO_SIGN; + } + } + gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, memop, idx); + + if ((orig_memop ^ memop) & MO_BSWAP) { + switch (orig_memop & MO_SIZE) { + case MO_16: + tcg_gen_bswap16_i64(val, val); + if (orig_memop & MO_SIGN) { + tcg_gen_ext16s_i64(val, val); + } + break; + case MO_32: + tcg_gen_bswap32_i64(val, val); + if (orig_memop & MO_SIGN) { + tcg_gen_ext32s_i64(val, val); + } + break; + case MO_64: + tcg_gen_bswap64_i64(val, val); + break; + default: + g_assert_not_reached(); + } + } } void tcg_gen_qemu_st_i64(TCGv_i64 val, TCGv addr, TCGArg idx, TCGMemOp memop) { - tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + TCGv_i64 swap = NULL; + if (TCG_TARGET_REG_BITS == 32 && (memop & MO_SIZE) < MO_64) { tcg_gen_qemu_st_i32(TCGV_LOW(val), addr, idx, memop); return; } + tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 1, 1); trace_guest_mem_before_tcg(tcg_ctx->cpu, cpu_env, addr, trace_mem_get_info(memop, 1)); + + if (!TCG_TARGET_HAS_MEMORY_BSWAP && (memop & MO_BSWAP)) { + swap = tcg_temp_new_i64(); + switch (memop & MO_SIZE) { + case MO_16: + tcg_gen_ext16u_i64(swap, val); + tcg_gen_bswap16_i64(swap, swap); + break; + case MO_32: + tcg_gen_ext32u_i64(swap, val); + tcg_gen_bswap32_i64(swap, swap); + break; + case MO_64: + tcg_gen_bswap64_i64(swap, val); + break; + default: + g_assert_not_reached(); + } + val = swap; + memop &= ~MO_BSWAP; + } + gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, memop, idx); + + if (swap) { + tcg_temp_free_i64(swap); + } } static void tcg_gen_ext_i32(TCGv_i32 ret, TCGv_i32 val, TCGMemOp opc) diff --git a/tcg/tcg-op.h b/tcg/tcg-op.h index 7513c1eb7c..d3e51b15af 100644 --- a/tcg/tcg-op.h +++ b/tcg/tcg-op.h @@ -22,6 +22,9 @@ * THE SOFTWARE. */ +#ifndef TCG_TCG_OP_H +#define TCG_TCG_OP_H + #include "tcg.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" @@ -252,11 +255,13 @@ static inline void tcg_gen_op6ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, static inline void gen_set_label(TCGLabel *l) { + l->present = 1; tcg_gen_op1(INDEX_op_set_label, label_arg(l)); } static inline void tcg_gen_br(TCGLabel *l) { + l->refs++; tcg_gen_op1(INDEX_op_br, label_arg(l)); } @@ -958,8 +963,19 @@ void tcg_gen_or_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); void tcg_gen_xor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); void tcg_gen_andc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); void tcg_gen_orc_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_nand_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_nor_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_eqv_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); void tcg_gen_not_vec(unsigned vece, TCGv_vec r, TCGv_vec a); void tcg_gen_neg_vec(unsigned vece, TCGv_vec r, TCGv_vec a); +void tcg_gen_ssadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_usadd_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_sssub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_ussub_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_smin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_umin_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_smax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); +void tcg_gen_umax_vec(unsigned vece, TCGv_vec r, TCGv_vec a, TCGv_vec b); void tcg_gen_shli_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); void tcg_gen_shri_vec(unsigned vece, TCGv_vec r, TCGv_vec a, int64_t i); @@ -1267,3 +1283,5 @@ static inline void tcg_gen_trunc_ptr_i32(TCGv_i32 r, TCGv_ptr a) #undef PTR #undef NAT + +#endif /* TCG_TCG_OP_H */ diff --git a/tcg/tcg-opc.h b/tcg/tcg-opc.h index e3a43aabb6..4e0238ad1a 100644 --- a/tcg/tcg-opc.h +++ b/tcg/tcg-opc.h @@ -191,9 +191,10 @@ DEF(mulsh_i64, 1, 2, 0, IMPL64 | IMPL(TCG_TARGET_HAS_mulsh_i64)) /* QEMU specific */ 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) -DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_END | IMPL(TCG_TARGET_HAS_goto_ptr)) +DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) +DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) +DEF(goto_ptr, 0, 1, 0, + TCG_OPF_BB_EXIT | TCG_OPF_BB_END | IMPL(TCG_TARGET_HAS_goto_ptr)) DEF(qemu_ld_i32, 1, TLADDR_ARGS, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) @@ -221,6 +222,14 @@ DEF(add_vec, 1, 2, 0, IMPLVEC) DEF(sub_vec, 1, 2, 0, IMPLVEC) DEF(mul_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_mul_vec)) DEF(neg_vec, 1, 1, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_neg_vec)) +DEF(ssadd_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) +DEF(usadd_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) +DEF(sssub_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) +DEF(ussub_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_sat_vec)) +DEF(smin_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) +DEF(umin_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) +DEF(smax_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) +DEF(umax_vec, 1, 2, 0, IMPLVEC | IMPL(TCG_TARGET_HAS_minmax_vec)) DEF(and_vec, 1, 2, 0, IMPLVEC) DEF(or_vec, 1, 2, 0, IMPLVEC) diff --git a/tcg/tcg.c b/tcg/tcg.c index f27b22bd3c..9b2bf7f439 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -30,6 +30,7 @@ /* Define to jump the ELF file used to communicate with GDB. */ #undef DEBUG_JIT +#include "qemu/error-report.h" #include "qemu/cutils.h" #include "qemu/host-utils.h" #include "qemu/timer.h" @@ -65,7 +66,7 @@ static void tcg_target_init(TCGContext *s); static const TCGTargetOpDef *tcg_target_op_def(TCGOpcode); static void tcg_target_qemu_prologue(TCGContext *s); -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend); /* The CIE and FDE header definitions will be common to all hosts. */ @@ -267,7 +268,8 @@ static void tcg_out_reloc(TCGContext *s, tcg_insn_unit *code_ptr, int type, /* FIXME: This may break relocations on RISC targets that modify instruction fields in place. The caller may not have written the initial value. */ - patch_reloc(code_ptr, type, l->u.value, addend); + bool ok = patch_reloc(code_ptr, type, l->u.value, addend); + tcg_debug_assert(ok); } else { /* add a new relocation entry */ r = tcg_malloc(sizeof(TCGRelocation)); @@ -287,7 +289,8 @@ static void tcg_out_label(TCGContext *s, TCGLabel *l, tcg_insn_unit *ptr) 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); + bool ok = patch_reloc(r->ptr, r->type, value, r->addend); + tcg_debug_assert(ok); } l->has_value = 1; @@ -302,6 +305,9 @@ TCGLabel *gen_new_label(void) *l = (TCGLabel){ .id = s->nb_labels++ }; +#ifdef CONFIG_DEBUG_TCG + QSIMPLEQ_INSERT_TAIL(&s->labels, l, next); +#endif return l; } @@ -1089,6 +1095,9 @@ void tcg_func_start(TCGContext *s) QTAILQ_INIT(&s->ops); QTAILQ_INIT(&s->free_ops); +#ifdef CONFIG_DEBUG_TCG + QSIMPLEQ_INIT(&s->labels); +#endif } static inline TCGTemp *tcg_temp_alloc(TCGContext *s) @@ -1604,6 +1613,16 @@ bool tcg_op_supported(TCGOpcode op) case INDEX_op_shrv_vec: case INDEX_op_sarv_vec: return have_vec && TCG_TARGET_HAS_shv_vec; + case INDEX_op_ssadd_vec: + case INDEX_op_usadd_vec: + case INDEX_op_sssub_vec: + case INDEX_op_ussub_vec: + return have_vec && TCG_TARGET_HAS_sat_vec; + case INDEX_op_smin_vec: + case INDEX_op_umin_vec: + case INDEX_op_smax_vec: + case INDEX_op_umax_vec: + return have_vec && TCG_TARGET_HAS_minmax_vec; default: tcg_debug_assert(op > INDEX_op_last_generic && op < NB_OPS); @@ -1884,7 +1903,21 @@ static const char * const alignment_name[(MO_AMASK >> MO_ASHIFT) + 1] = { [MO_ALIGN_64 >> MO_ASHIFT] = "al64+", }; -void tcg_dump_ops(TCGContext *s) +static inline bool tcg_regset_single(TCGRegSet d) +{ + return (d & (d - 1)) == 0; +} + +static inline TCGReg tcg_regset_first(TCGRegSet d) +{ + if (TCG_TARGET_NB_REGS <= 32) { + return ctz32(d); + } else { + return ctz64(d); + } +} + +static void tcg_dump_ops(TCGContext *s, bool have_prefs) { char buf[128]; TCGOp *op; @@ -1899,6 +1932,7 @@ void tcg_dump_ops(TCGContext *s) def = &tcg_op_defs[c]; if (c == INDEX_op_insn_start) { + nb_oargs = 0; col += qemu_log("\n ----"); for (i = 0; i < TARGET_INSN_START_WORDS; ++i) { @@ -2018,12 +2052,15 @@ void tcg_dump_ops(TCGContext *s) col += qemu_log("%s$0x%" TCG_PRIlx, k ? "," : "", op->args[k]); } } - if (op->life) { - unsigned life = op->life; - for (; col < 48; ++col) { + if (have_prefs || op->life) { + for (; col < 40; ++col) { putc(' ', qemu_logfile); } + } + + if (op->life) { + unsigned life = op->life; if (life & (SYNC_ARG * 3)) { qemu_log(" sync:"); @@ -2043,6 +2080,33 @@ void tcg_dump_ops(TCGContext *s) } } } + + if (have_prefs) { + for (i = 0; i < nb_oargs; ++i) { + TCGRegSet set = op->output_pref[i]; + + if (i == 0) { + qemu_log(" pref="); + } else { + qemu_log(","); + } + if (set == 0) { + qemu_log("none"); + } else if (set == MAKE_64BIT_MASK(0, TCG_TARGET_NB_REGS)) { + qemu_log("all"); +#ifdef CONFIG_DEBUG_TCG + } else if (tcg_regset_single(set)) { + TCGReg reg = tcg_regset_first(set); + qemu_log("%s", tcg_target_reg_names[reg]); +#endif + } else if (TCG_TARGET_NB_REGS <= 32) { + qemu_log("%#x", (uint32_t)set); + } else { + qemu_log("%#" PRIx64, (uint64_t)set); + } + } + } + qemu_log("\n"); } } @@ -2168,6 +2232,26 @@ static void process_op_defs(TCGContext *s) void tcg_op_remove(TCGContext *s, TCGOp *op) { + TCGLabel *label; + + switch (op->opc) { + case INDEX_op_br: + label = arg_label(op->args[0]); + label->refs--; + break; + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + label = arg_label(op->args[3]); + label->refs--; + break; + case INDEX_op_brcond2_i32: + label = arg_label(op->args[5]); + label->refs--; + break; + default: + break; + } + QTAILQ_REMOVE(&s->ops, op, link); QTAILQ_INSERT_TAIL(&s->free_ops, op, link); s->nb_ops--; @@ -2202,59 +2286,195 @@ TCGOp *tcg_emit_op(TCGOpcode opc) return op; } -TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, - TCGOpcode opc, int nargs) +TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, TCGOpcode opc) { TCGOp *new_op = tcg_op_alloc(opc); QTAILQ_INSERT_BEFORE(old_op, new_op, link); return new_op; } -TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, - TCGOpcode opc, int nargs) +TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, TCGOpcode opc) { TCGOp *new_op = tcg_op_alloc(opc); QTAILQ_INSERT_AFTER(&s->ops, old_op, new_op, link); return new_op; } +/* Reachable analysis : remove unreachable code. */ +static void reachable_code_pass(TCGContext *s) +{ + TCGOp *op, *op_next; + bool dead = false; + + QTAILQ_FOREACH_SAFE(op, &s->ops, link, op_next) { + bool remove = dead; + TCGLabel *label; + int call_flags; + + switch (op->opc) { + case INDEX_op_set_label: + label = arg_label(op->args[0]); + if (label->refs == 0) { + /* + * While there is an occasional backward branch, virtually + * all branches generated by the translators are forward. + * Which means that generally we will have already removed + * all references to the label that will be, and there is + * little to be gained by iterating. + */ + remove = true; + } else { + /* Once we see a label, insns become live again. */ + dead = false; + remove = false; + + /* + * Optimization can fold conditional branches to unconditional. + * If we find a label with one reference which is preceded by + * an unconditional branch to it, remove both. This needed to + * wait until the dead code in between them was removed. + */ + if (label->refs == 1) { + TCGOp *op_prev = QTAILQ_PREV(op, link); + if (op_prev->opc == INDEX_op_br && + label == arg_label(op_prev->args[0])) { + tcg_op_remove(s, op_prev); + remove = true; + } + } + } + break; + + case INDEX_op_br: + case INDEX_op_exit_tb: + case INDEX_op_goto_ptr: + /* Unconditional branches; everything following is dead. */ + dead = true; + break; + + case INDEX_op_call: + /* Notice noreturn helper calls, raising exceptions. */ + call_flags = op->args[TCGOP_CALLO(op) + TCGOP_CALLI(op) + 1]; + if (call_flags & TCG_CALL_NO_RETURN) { + dead = true; + } + break; + + case INDEX_op_insn_start: + /* Never remove -- we need to keep these for unwind. */ + remove = false; + break; + + default: + break; + } + + if (remove) { + tcg_op_remove(s, op); + } + } +} + #define TS_DEAD 1 #define TS_MEM 2 #define IS_DEAD_ARG(n) (arg_life & (DEAD_ARG << (n))) #define NEED_SYNC_ARG(n) (arg_life & (SYNC_ARG << (n))) +/* For liveness_pass_1, the register preferences for a given temp. */ +static inline TCGRegSet *la_temp_pref(TCGTemp *ts) +{ + return ts->state_ptr; +} + +/* For liveness_pass_1, reset the preferences for a given temp to the + * maximal regset for its type. + */ +static inline void la_reset_pref(TCGTemp *ts) +{ + *la_temp_pref(ts) + = (ts->state == TS_DEAD ? 0 : tcg_target_available_regs[ts->type]); +} + /* liveness analysis: end of function: all temps are dead, and globals should be in memory. */ -static void tcg_la_func_end(TCGContext *s) +static void la_func_end(TCGContext *s, int ng, int nt) { - int ng = s->nb_globals; - int nt = s->nb_temps; int i; for (i = 0; i < ng; ++i) { s->temps[i].state = TS_DEAD | TS_MEM; + la_reset_pref(&s->temps[i]); } for (i = ng; i < nt; ++i) { s->temps[i].state = TS_DEAD; + la_reset_pref(&s->temps[i]); } } /* liveness analysis: end of basic block: all temps are dead, globals and local temps should be in memory. */ -static void tcg_la_bb_end(TCGContext *s) +static void la_bb_end(TCGContext *s, int ng, int nt) { - int ng = s->nb_globals; - int nt = s->nb_temps; int i; for (i = 0; i < ng; ++i) { s->temps[i].state = TS_DEAD | TS_MEM; + la_reset_pref(&s->temps[i]); } for (i = ng; i < nt; ++i) { s->temps[i].state = (s->temps[i].temp_local ? TS_DEAD | TS_MEM : TS_DEAD); + la_reset_pref(&s->temps[i]); + } +} + +/* liveness analysis: sync globals back to memory. */ +static void la_global_sync(TCGContext *s, int ng) +{ + int i; + + for (i = 0; i < ng; ++i) { + int state = s->temps[i].state; + s->temps[i].state = state | TS_MEM; + if (state == TS_DEAD) { + /* If the global was previously dead, reset prefs. */ + la_reset_pref(&s->temps[i]); + } + } +} + +/* liveness analysis: sync globals back to memory and kill. */ +static void la_global_kill(TCGContext *s, int ng) +{ + int i; + + for (i = 0; i < ng; i++) { + s->temps[i].state = TS_DEAD | TS_MEM; + la_reset_pref(&s->temps[i]); + } +} + +/* liveness analysis: note live globals crossing calls. */ +static void la_cross_call(TCGContext *s, int nt) +{ + TCGRegSet mask = ~tcg_target_call_clobber_regs; + int i; + + for (i = 0; i < nt; i++) { + TCGTemp *ts = &s->temps[i]; + if (!(ts->state & TS_DEAD)) { + TCGRegSet *pset = la_temp_pref(ts); + TCGRegSet set = *pset; + + set &= mask; + /* If the combination is not possible, restart. */ + if (set == 0) { + set = tcg_target_available_regs[ts->type] & mask; + } + *pset = set; + } } } @@ -2264,16 +2484,25 @@ static void tcg_la_bb_end(TCGContext *s) static void liveness_pass_1(TCGContext *s) { int nb_globals = s->nb_globals; + int nb_temps = s->nb_temps; TCGOp *op, *op_prev; + TCGRegSet *prefs; + int i; - tcg_la_func_end(s); + prefs = tcg_malloc(sizeof(TCGRegSet) * nb_temps); + for (i = 0; i < nb_temps; ++i) { + s->temps[i].state_ptr = prefs + i; + } - QTAILQ_FOREACH_REVERSE_SAFE(op, &s->ops, TCGOpHead, link, op_prev) { - int i, nb_iargs, nb_oargs; + /* ??? Should be redundant with the exit_tb that ends the TB. */ + la_func_end(s, nb_globals, nb_temps); + + QTAILQ_FOREACH_REVERSE_SAFE(op, &s->ops, link, op_prev) { + int nb_iargs, nb_oargs; TCGOpcode opc_new, opc_new2; bool have_opc_new2; TCGLifeData arg_life = 0; - TCGTemp *arg_ts; + TCGTemp *ts; TCGOpcode opc = op->opc; const TCGOpDef *def = &tcg_op_defs[opc]; @@ -2281,6 +2510,7 @@ static void liveness_pass_1(TCGContext *s) case INDEX_op_call: { int call_flags; + int nb_call_regs; nb_oargs = TCGOP_CALLO(op); nb_iargs = TCGOP_CALLI(op); @@ -2289,53 +2519,74 @@ static void liveness_pass_1(TCGContext *s) /* pure functions can be removed if their result is unused */ if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) { for (i = 0; i < nb_oargs; i++) { - arg_ts = arg_temp(op->args[i]); - if (arg_ts->state != TS_DEAD) { + ts = arg_temp(op->args[i]); + if (ts->state != TS_DEAD) { goto do_not_remove_call; } } goto do_remove; - } else { - do_not_remove_call: + } + do_not_remove_call: - /* output args are dead */ - for (i = 0; i < nb_oargs; i++) { - arg_ts = arg_temp(op->args[i]); - if (arg_ts->state & TS_DEAD) { - arg_life |= DEAD_ARG << i; - } - if (arg_ts->state & TS_MEM) { - arg_life |= SYNC_ARG << i; - } - arg_ts->state = TS_DEAD; + /* Output args are dead. */ + for (i = 0; i < nb_oargs; i++) { + ts = arg_temp(op->args[i]); + if (ts->state & TS_DEAD) { + arg_life |= DEAD_ARG << i; } + if (ts->state & TS_MEM) { + arg_life |= SYNC_ARG << i; + } + ts->state = TS_DEAD; + la_reset_pref(ts); - if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS | - TCG_CALL_NO_READ_GLOBALS))) { - /* globals should go back to memory */ - for (i = 0; i < nb_globals; i++) { - s->temps[i].state = TS_DEAD | TS_MEM; - } - } else if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) { - /* globals should be synced to memory */ - for (i = 0; i < nb_globals; i++) { - s->temps[i].state |= TS_MEM; - } - } + /* Not used -- it will be tcg_target_call_oarg_regs[i]. */ + op->output_pref[i] = 0; + } - /* record arguments that die in this helper */ - for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { - arg_ts = arg_temp(op->args[i]); - if (arg_ts && arg_ts->state & TS_DEAD) { - arg_life |= DEAD_ARG << i; - } + if (!(call_flags & (TCG_CALL_NO_WRITE_GLOBALS | + TCG_CALL_NO_READ_GLOBALS))) { + la_global_kill(s, nb_globals); + } else if (!(call_flags & TCG_CALL_NO_READ_GLOBALS)) { + la_global_sync(s, nb_globals); + } + + /* Record arguments that die in this helper. */ + for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { + ts = arg_temp(op->args[i]); + if (ts && ts->state & TS_DEAD) { + arg_life |= DEAD_ARG << i; } - /* input arguments are live for preceding opcodes */ - for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { - arg_ts = arg_temp(op->args[i]); - if (arg_ts) { - arg_ts->state &= ~TS_DEAD; - } + } + + /* For all live registers, remove call-clobbered prefs. */ + la_cross_call(s, nb_temps); + + nb_call_regs = ARRAY_SIZE(tcg_target_call_iarg_regs); + + /* Input arguments are live for preceding opcodes. */ + for (i = 0; i < nb_iargs; i++) { + ts = arg_temp(op->args[i + nb_oargs]); + if (ts && ts->state & TS_DEAD) { + /* For those arguments that die, and will be allocated + * in registers, clear the register set for that arg, + * to be filled in below. For args that will be on + * the stack, reset to any available reg. + */ + *la_temp_pref(ts) + = (i < nb_call_regs ? 0 : + tcg_target_available_regs[ts->type]); + ts->state &= ~TS_DEAD; + } + } + + /* For each input argument, add its input register to prefs. + If a temp is used once, this produces a single set bit. */ + for (i = 0; i < MIN(nb_call_regs, nb_iargs); i++) { + ts = arg_temp(op->args[i + nb_oargs]); + if (ts) { + tcg_regset_set_reg(*la_temp_pref(ts), + tcg_target_call_iarg_regs[i]); } } } @@ -2344,7 +2595,9 @@ static void liveness_pass_1(TCGContext *s) break; case INDEX_op_discard: /* mark the temporary as dead */ - arg_temp(op->args[0])->state = TS_DEAD; + ts = arg_temp(op->args[0]); + ts->state = TS_DEAD; + la_reset_pref(ts); break; case INDEX_op_add2_i32: @@ -2439,43 +2692,96 @@ static void liveness_pass_1(TCGContext *s) goto do_not_remove; } } - do_remove: - tcg_op_remove(s, op); - } else { - do_not_remove: - /* output args are dead */ - for (i = 0; i < nb_oargs; i++) { - arg_ts = arg_temp(op->args[i]); - if (arg_ts->state & TS_DEAD) { - arg_life |= DEAD_ARG << i; - } - if (arg_ts->state & TS_MEM) { - arg_life |= SYNC_ARG << i; - } - arg_ts->state = TS_DEAD; - } + goto do_remove; + } + goto do_not_remove; - /* if end of basic block, update */ - if (def->flags & TCG_OPF_BB_END) { - tcg_la_bb_end(s); - } else if (def->flags & TCG_OPF_SIDE_EFFECTS) { - /* globals should be synced to memory */ - for (i = 0; i < nb_globals; i++) { - s->temps[i].state |= TS_MEM; - } - } + do_remove: + tcg_op_remove(s, op); + break; - /* record arguments that die in this opcode */ + do_not_remove: + for (i = 0; i < nb_oargs; i++) { + ts = arg_temp(op->args[i]); + + /* Remember the preference of the uses that followed. */ + op->output_pref[i] = *la_temp_pref(ts); + + /* Output args are dead. */ + if (ts->state & TS_DEAD) { + arg_life |= DEAD_ARG << i; + } + if (ts->state & TS_MEM) { + arg_life |= SYNC_ARG << i; + } + ts->state = TS_DEAD; + la_reset_pref(ts); + } + + /* If end of basic block, update. */ + if (def->flags & TCG_OPF_BB_EXIT) { + la_func_end(s, nb_globals, nb_temps); + } else if (def->flags & TCG_OPF_BB_END) { + la_bb_end(s, nb_globals, nb_temps); + } else if (def->flags & TCG_OPF_SIDE_EFFECTS) { + la_global_sync(s, nb_globals); + if (def->flags & TCG_OPF_CALL_CLOBBER) { + la_cross_call(s, nb_temps); + } + } + + /* Record arguments that die in this opcode. */ + for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + ts = arg_temp(op->args[i]); + if (ts->state & TS_DEAD) { + arg_life |= DEAD_ARG << i; + } + } + + /* Input arguments are live for preceding opcodes. */ + for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + ts = arg_temp(op->args[i]); + if (ts->state & TS_DEAD) { + /* For operands that were dead, initially allow + all regs for the type. */ + *la_temp_pref(ts) = tcg_target_available_regs[ts->type]; + ts->state &= ~TS_DEAD; + } + } + + /* Incorporate constraints for this operand. */ + switch (opc) { + case INDEX_op_mov_i32: + case INDEX_op_mov_i64: + /* Note that these are TCG_OPF_NOT_PRESENT and do not + have proper constraints. That said, special case + moves to propagate preferences backward. */ + if (IS_DEAD_ARG(1)) { + *la_temp_pref(arg_temp(op->args[0])) + = *la_temp_pref(arg_temp(op->args[1])); + } + break; + + default: for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - arg_ts = arg_temp(op->args[i]); - if (arg_ts->state & TS_DEAD) { - arg_life |= DEAD_ARG << i; + const TCGArgConstraint *ct = &def->args_ct[i]; + TCGRegSet set, *pset; + + ts = arg_temp(op->args[i]); + pset = la_temp_pref(ts); + set = *pset; + + set &= ct->u.regs; + if (ct->ct & TCG_CT_IALIAS) { + set &= op->output_pref[ct->alias_index]; } + /* If the combination is not possible, restart. */ + if (set == 0) { + set = ct->u.regs; + } + *pset = set; } - /* input arguments are live for preceding opcodes */ - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { - arg_temp(op->args[i])->state &= ~TS_DEAD; - } + break; } break; } @@ -2549,7 +2855,7 @@ static bool liveness_pass_2(TCGContext *s) TCGOpcode lopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_ld_i32 : INDEX_op_ld_i64); - TCGOp *lop = tcg_op_insert_before(s, op, lopc, 3); + TCGOp *lop = tcg_op_insert_before(s, op, lopc); lop->args[0] = temp_arg(dir_ts); lop->args[1] = temp_arg(arg_ts->mem_base); @@ -2618,7 +2924,7 @@ static bool liveness_pass_2(TCGContext *s) TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_st_i32 : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc, 3); + TCGOp *sop = tcg_op_insert_after(s, op, sopc); sop->args[0] = temp_arg(dir_ts); sop->args[1] = temp_arg(arg_ts->mem_base); @@ -2726,7 +3032,7 @@ static void temp_allocate_frame(TCGContext *s, TCGTemp *ts) s->current_frame_offset += sizeof(tcg_target_long); } -static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet); +static void temp_load(TCGContext *, TCGTemp *, TCGRegSet, TCGRegSet, TCGRegSet); /* Mark a temporary as free or dead. If 'free_or_dead' is negative, mark it free; otherwise mark it dead. */ @@ -2754,8 +3060,8 @@ static inline void temp_dead(TCGContext *s, TCGTemp *ts) registers needs to be allocated to store a constant. If 'free_or_dead' is non-zero, subsequently release the temporary; if it is positive, the temp is dead; if it is negative, the temp is free. */ -static void temp_sync(TCGContext *s, TCGTemp *ts, - TCGRegSet allocated_regs, int free_or_dead) +static void temp_sync(TCGContext *s, TCGTemp *ts, TCGRegSet allocated_regs, + TCGRegSet preferred_regs, int free_or_dead) { if (ts->fixed_reg) { return; @@ -2775,7 +3081,7 @@ static void temp_sync(TCGContext *s, TCGTemp *ts, break; } temp_load(s, ts, tcg_target_available_regs[ts->type], - allocated_regs); + allocated_regs, preferred_regs); /* fallthrough */ case TEMP_VAL_REG: @@ -2802,35 +3108,76 @@ static void tcg_reg_free(TCGContext *s, TCGReg reg, TCGRegSet allocated_regs) { TCGTemp *ts = s->reg_to_temp[reg]; if (ts != NULL) { - temp_sync(s, ts, allocated_regs, -1); + temp_sync(s, ts, allocated_regs, 0, -1); } } -/* Allocate a register belonging to reg1 & ~reg2 */ -static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet desired_regs, - TCGRegSet allocated_regs, bool rev) +/** + * tcg_reg_alloc: + * @required_regs: Set of registers in which we must allocate. + * @allocated_regs: Set of registers which must be avoided. + * @preferred_regs: Set of registers we should prefer. + * @rev: True if we search the registers in "indirect" order. + * + * The allocated register must be in @required_regs & ~@allocated_regs, + * but if we can put it in @preferred_regs we may save a move later. + */ +static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet required_regs, + TCGRegSet allocated_regs, + TCGRegSet preferred_regs, bool rev) { - int i, n = ARRAY_SIZE(tcg_target_reg_alloc_order); + int i, j, f, n = ARRAY_SIZE(tcg_target_reg_alloc_order); + TCGRegSet reg_ct[2]; const int *order; - TCGReg reg; - TCGRegSet reg_ct; - reg_ct = desired_regs & ~allocated_regs; + reg_ct[1] = required_regs & ~allocated_regs; + tcg_debug_assert(reg_ct[1] != 0); + reg_ct[0] = reg_ct[1] & preferred_regs; + + /* Skip the preferred_regs option if it cannot be satisfied, + or if the preference made no difference. */ + f = reg_ct[0] == 0 || reg_ct[0] == reg_ct[1]; + order = rev ? indirect_reg_alloc_order : tcg_target_reg_alloc_order; - /* first try free registers */ - 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; + /* Try free registers, preferences first. */ + for (j = f; j < 2; j++) { + TCGRegSet set = reg_ct[j]; + + if (tcg_regset_single(set)) { + /* One register in the set. */ + TCGReg reg = tcg_regset_first(set); + if (s->reg_to_temp[reg] == NULL) { + return reg; + } + } else { + for (i = 0; i < n; i++) { + TCGReg reg = order[i]; + if (s->reg_to_temp[reg] == NULL && + tcg_regset_test_reg(set, reg)) { + return reg; + } + } + } } - /* XXX: do better spill choice */ - for(i = 0; i < n; i++) { - reg = order[i]; - if (tcg_regset_test_reg(reg_ct, reg)) { + /* We must spill something. */ + for (j = f; j < 2; j++) { + TCGRegSet set = reg_ct[j]; + + if (tcg_regset_single(set)) { + /* One register in the set. */ + TCGReg reg = tcg_regset_first(set); tcg_reg_free(s, reg, allocated_regs); return reg; + } else { + for (i = 0; i < n; i++) { + TCGReg reg = order[i]; + if (tcg_regset_test_reg(set, reg)) { + tcg_reg_free(s, reg, allocated_regs); + return reg; + } + } } } @@ -2840,7 +3187,7 @@ static TCGReg tcg_reg_alloc(TCGContext *s, TCGRegSet desired_regs, /* 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) + TCGRegSet allocated_regs, TCGRegSet preferred_regs) { TCGReg reg; @@ -2848,12 +3195,14 @@ static void temp_load(TCGContext *s, TCGTemp *ts, TCGRegSet desired_regs, case TEMP_VAL_REG: return; case TEMP_VAL_CONST: - reg = tcg_reg_alloc(s, desired_regs, allocated_regs, ts->indirect_base); + reg = tcg_reg_alloc(s, desired_regs, allocated_regs, + preferred_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); + reg = tcg_reg_alloc(s, desired_regs, allocated_regs, + preferred_regs, ts->indirect_base); tcg_out_ld(s, ts->type, reg, ts->mem_base->reg, ts->mem_offset); ts->mem_coherent = 1; break; @@ -2923,7 +3272,8 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) } static void tcg_reg_alloc_do_movi(TCGContext *s, TCGTemp *ots, - tcg_target_ulong val, TCGLifeData arg_life) + tcg_target_ulong val, TCGLifeData arg_life, + TCGRegSet preferred_regs) { if (ots->fixed_reg) { /* For fixed registers, we do not do any constant propagation. */ @@ -2939,7 +3289,7 @@ static void tcg_reg_alloc_do_movi(TCGContext *s, TCGTemp *ots, ots->val = val; ots->mem_coherent = 0; if (NEED_SYNC_ARG(0)) { - temp_sync(s, ots, s->reserved_regs, IS_DEAD_ARG(0)); + temp_sync(s, ots, s->reserved_regs, preferred_regs, IS_DEAD_ARG(0)); } else if (IS_DEAD_ARG(0)) { temp_dead(s, ots); } @@ -2950,17 +3300,18 @@ static void tcg_reg_alloc_movi(TCGContext *s, const TCGOp *op) TCGTemp *ots = arg_temp(op->args[0]); tcg_target_ulong val = op->args[1]; - tcg_reg_alloc_do_movi(s, ots, val, op->life); + tcg_reg_alloc_do_movi(s, ots, val, op->life, op->output_pref[0]); } static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) { const TCGLifeData arg_life = op->life; - TCGRegSet allocated_regs; + TCGRegSet allocated_regs, preferred_regs; TCGTemp *ts, *ots; TCGType otype, itype; allocated_regs = s->reserved_regs; + preferred_regs = op->output_pref[0]; ots = arg_temp(op->args[0]); ts = arg_temp(op->args[1]); @@ -2974,7 +3325,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) if (IS_DEAD_ARG(1)) { temp_dead(s, ts); } - tcg_reg_alloc_do_movi(s, ots, val, arg_life); + tcg_reg_alloc_do_movi(s, ots, val, arg_life, preferred_regs); return; } @@ -2983,7 +3334,8 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) the SOURCE value into its own register first, that way we don't have to reload SOURCE the next time it is used. */ if (ts->val_type == TEMP_VAL_MEM) { - temp_load(s, ts, tcg_target_available_regs[itype], allocated_regs); + temp_load(s, ts, tcg_target_available_regs[itype], + allocated_regs, preferred_regs); } tcg_debug_assert(ts->val_type == TEMP_VAL_REG); @@ -3013,7 +3365,8 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) input one. */ tcg_regset_set_reg(allocated_regs, ts->reg); ots->reg = tcg_reg_alloc(s, tcg_target_available_regs[otype], - allocated_regs, ots->indirect_base); + allocated_regs, preferred_regs, + ots->indirect_base); } tcg_out_mov(s, otype, ots->reg, ts->reg); } @@ -3021,7 +3374,7 @@ static void tcg_reg_alloc_mov(TCGContext *s, const TCGOp *op) ots->mem_coherent = 0; s->reg_to_temp[ots->reg] = ots; if (NEED_SYNC_ARG(0)) { - temp_sync(s, ots, allocated_regs, 0); + temp_sync(s, ots, allocated_regs, 0, 0); } } } @@ -3053,6 +3406,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* satisfy input constraints */ for (k = 0; k < nb_iargs; k++) { + TCGRegSet i_preferred_regs, o_preferred_regs; + i = def->sorted_args[nb_oargs + k]; arg = op->args[i]; arg_ct = &def->args_ct[i]; @@ -3063,17 +3418,18 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* constant is OK for instruction */ const_args[i] = 1; new_args[i] = ts->val; - goto iarg_end; + continue; } - temp_load(s, ts, arg_ct->u.regs, i_allocated_regs); - + i_preferred_regs = o_preferred_regs = 0; if (arg_ct->ct & TCG_CT_IALIAS) { + o_preferred_regs = op->output_pref[arg_ct->alias_index]; if (ts->fixed_reg) { /* if fixed register, we must allocate a new register if the alias is not the same register */ - if (arg != op->args[arg_ct->alias_index]) + if (arg != op->args[arg_ct->alias_index]) { goto allocate_in_reg; + } } else { /* if the input is aliased to an output and if it is not dead after the instruction, we must allocate @@ -3081,33 +3437,42 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) if (!IS_DEAD_ARG(i)) { goto allocate_in_reg; } + /* check if the current register has already been allocated for another input aliased to an output */ - int k2, i2; - for (k2 = 0 ; k2 < k ; k2++) { - i2 = def->sorted_args[nb_oargs + k2]; - if ((def->args_ct[i2].ct & TCG_CT_IALIAS) && - (new_args[i2] == ts->reg)) { - goto allocate_in_reg; + if (ts->val_type == TEMP_VAL_REG) { + int k2, i2; + reg = ts->reg; + for (k2 = 0 ; k2 < k ; k2++) { + i2 = def->sorted_args[nb_oargs + k2]; + if ((def->args_ct[i2].ct & TCG_CT_IALIAS) && + reg == new_args[i2]) { + goto allocate_in_reg; + } } } + i_preferred_regs = o_preferred_regs; } } + + temp_load(s, ts, arg_ct->u.regs, i_allocated_regs, i_preferred_regs); reg = ts->reg; + if (tcg_regset_test_reg(arg_ct->u.regs, reg)) { /* nothing to do : the constraint is satisfied */ } else { allocate_in_reg: /* allocate a new register matching the constraint and move the temporary register into it */ + temp_load(s, ts, tcg_target_available_regs[ts->type], + i_allocated_regs, 0); reg = tcg_reg_alloc(s, arg_ct->u.regs, i_allocated_regs, - ts->indirect_base); + o_preferred_regs, ts->indirect_base); tcg_out_mov(s, ts->type, reg, ts->reg); } new_args[i] = reg; const_args[i] = 0; tcg_regset_set_reg(i_allocated_regs, reg); - iarg_end: ; } /* mark dead temporaries and free the associated registers */ @@ -3146,7 +3511,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } else if (arg_ct->ct & TCG_CT_NEWREG) { reg = tcg_reg_alloc(s, arg_ct->u.regs, i_allocated_regs | o_allocated_regs, - ts->indirect_base); + op->output_pref[k], ts->indirect_base); } else { /* if fixed register, we try to use it */ reg = ts->reg; @@ -3155,7 +3520,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) goto oarg_end; } reg = tcg_reg_alloc(s, arg_ct->u.regs, o_allocated_regs, - ts->indirect_base); + op->output_pref[k], ts->indirect_base); } tcg_regset_set_reg(o_allocated_regs, reg); /* if a fixed register is used, then a move will be done afterwards */ @@ -3191,7 +3556,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) tcg_out_mov(s, ts->type, ts->reg, reg); } if (NEED_SYNC_ARG(i)) { - temp_sync(s, ts, o_allocated_regs, IS_DEAD_ARG(i)); + temp_sync(s, ts, o_allocated_regs, 0, IS_DEAD_ARG(i)); } else if (IS_DEAD_ARG(i)) { temp_dead(s, ts); } @@ -3247,7 +3612,7 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op) if (arg != TCG_CALL_DUMMY_ARG) { ts = arg_temp(arg); temp_load(s, ts, tcg_target_available_regs[ts->type], - s->reserved_regs); + s->reserved_regs, 0); tcg_out_st(s, ts->type, ts->reg, TCG_REG_CALL_STACK, stack_offset); } #ifndef TCG_TARGET_STACK_GROWSUP @@ -3262,17 +3627,18 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op) if (arg != TCG_CALL_DUMMY_ARG) { ts = arg_temp(arg); reg = tcg_target_call_iarg_regs[i]; - tcg_reg_free(s, reg, allocated_regs); if (ts->val_type == TEMP_VAL_REG) { if (ts->reg != reg) { + tcg_reg_free(s, reg, allocated_regs); tcg_out_mov(s, ts->type, reg, ts->reg); } } else { TCGRegSet arg_set = 0; + tcg_reg_free(s, reg, allocated_regs); tcg_regset_set_reg(arg_set, reg); - temp_load(s, ts, arg_set, allocated_regs); + temp_load(s, ts, arg_set, allocated_regs, 0); } tcg_regset_set_reg(allocated_regs, reg); @@ -3325,7 +3691,7 @@ static void tcg_reg_alloc_call(TCGContext *s, TCGOp *op) ts->mem_coherent = 0; s->reg_to_temp[reg] = ts; if (NEED_SYNC_ARG(i)) { - temp_sync(s, ts, allocated_regs, IS_DEAD_ARG(i)); + temp_sync(s, ts, allocated_regs, 0, IS_DEAD_ARG(i)); } else if (IS_DEAD_ARG(i)) { temp_dead(s, ts); } @@ -3361,6 +3727,7 @@ void tcg_profile_snapshot(TCGProfile *prof, bool counters, bool table) const TCGProfile *orig = &s->prof; if (counters) { + PROF_ADD(prof, orig, cpu_exec_time); PROF_ADD(prof, orig, tb_count1); PROF_ADD(prof, orig, tb_count); PROF_ADD(prof, orig, op_count); @@ -3412,11 +3779,32 @@ void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf) prof.table_op_count[i]); } } + +int64_t tcg_cpu_exec_time(void) +{ + unsigned int n_ctxs = atomic_read(&n_tcg_ctxs); + unsigned int i; + int64_t ret = 0; + + for (i = 0; i < n_ctxs; i++) { + const TCGContext *s = atomic_read(&tcg_ctxs[i]); + const TCGProfile *prof = &s->prof; + + ret += atomic_read(&prof->cpu_exec_time); + } + return ret; +} #else void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf) { cpu_fprintf(f, "[TCG profiler not compiled]\n"); } + +int64_t tcg_cpu_exec_time(void) +{ + error_report("%s: TCG profiler not compiled", __func__); + exit(EXIT_FAILURE); +} #endif @@ -3430,7 +3818,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) #ifdef CONFIG_PROFILER { - int n; + int n = 0; QTAILQ_FOREACH(op, &s->ops, link) { n++; @@ -3453,12 +3841,29 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) && qemu_log_in_addr_range(tb->pc))) { qemu_log_lock(); qemu_log("OP:\n"); - tcg_dump_ops(s); + tcg_dump_ops(s, false); qemu_log("\n"); qemu_log_unlock(); } #endif +#ifdef CONFIG_DEBUG_TCG + /* Ensure all labels referenced have been emitted. */ + { + TCGLabel *l; + bool error = false; + + QSIMPLEQ_FOREACH(l, &s->labels, next) { + if (unlikely(!l->present) && l->refs) { + qemu_log_mask(CPU_LOG_TB_OP, + "$L%d referenced but not present.\n", l->id); + error = true; + } + } + assert(!error); + } +#endif + #ifdef CONFIG_PROFILER atomic_set(&prof->opt_time, prof->opt_time - profile_getclock()); #endif @@ -3472,6 +3877,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) atomic_set(&prof->la_time, prof->la_time - profile_getclock()); #endif + reachable_code_pass(s); liveness_pass_1(s); if (s->nb_indirects > 0) { @@ -3480,7 +3886,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) && qemu_log_in_addr_range(tb->pc))) { qemu_log_lock(); qemu_log("OP before indirect lowering:\n"); - tcg_dump_ops(s); + tcg_dump_ops(s, false); qemu_log("\n"); qemu_log_unlock(); } @@ -3501,7 +3907,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb) && qemu_log_in_addr_range(tb->pc))) { qemu_log_lock(); qemu_log("OP after optimization and liveness analysis:\n"); - tcg_dump_ops(s); + tcg_dump_ops(s, true); qemu_log("\n"); qemu_log_unlock(); } diff --git a/tcg/tcg.h b/tcg/tcg.h index f9f12378e9..32b7cf3489 100644 --- a/tcg/tcg.h +++ b/tcg/tcg.h @@ -32,6 +32,7 @@ #include "qemu/queue.h" #include "tcg-mo.h" #include "tcg-target.h" +#include "qemu/int128.h" /* XXX: make safe guess about sizes */ #define MAX_OP_PER_INSTR 266 @@ -182,6 +183,8 @@ typedef uint64_t TCGRegSet; #define TCG_TARGET_HAS_shs_vec 0 #define TCG_TARGET_HAS_shv_vec 0 #define TCG_TARGET_HAS_mul_vec 0 +#define TCG_TARGET_HAS_sat_vec 0 +#define TCG_TARGET_HAS_minmax_vec 0 #else #define TCG_TARGET_MAYBE_vec 1 #endif @@ -229,11 +232,9 @@ typedef uint64_t tcg_insn_unit; #if defined CONFIG_DEBUG_TCG || defined QEMU_STATIC_ANALYSIS # define tcg_debug_assert(X) do { assert(X); } while (0) -#elif QEMU_GNUC_PREREQ(4, 5) +#else # define tcg_debug_assert(X) \ do { if (!(X)) { __builtin_unreachable(); } } while (0) -#else -# define tcg_debug_assert(X) do { (void)(X); } while (0) #endif typedef struct TCGRelocation { @@ -243,15 +244,21 @@ typedef struct TCGRelocation { intptr_t addend; } TCGRelocation; -typedef struct TCGLabel { +typedef struct TCGLabel TCGLabel; +struct TCGLabel { + unsigned present : 1; unsigned has_value : 1; - unsigned id : 31; + unsigned id : 14; + unsigned refs : 16; union { uintptr_t value; tcg_insn_unit *value_ptr; TCGRelocation *first_reloc; } u; -} TCGLabel; +#ifdef CONFIG_DEBUG_TCG + QSIMPLEQ_ENTRY(TCGLabel) next; +#endif +}; typedef struct TCGPool { struct TCGPool *next; @@ -463,11 +470,13 @@ typedef TCGv_ptr TCGv_env; /* call flags */ /* Helper does not read globals (either directly or through an exception). It implies TCG_CALL_NO_WRITE_GLOBALS. */ -#define TCG_CALL_NO_READ_GLOBALS 0x0010 +#define TCG_CALL_NO_READ_GLOBALS 0x0001 /* Helper does not write globals */ -#define TCG_CALL_NO_WRITE_GLOBALS 0x0020 +#define TCG_CALL_NO_WRITE_GLOBALS 0x0002 /* Helper can be safely suppressed if the return value is not used. */ -#define TCG_CALL_NO_SIDE_EFFECTS 0x0040 +#define TCG_CALL_NO_SIDE_EFFECTS 0x0004 +/* Helper is QEMU_NORETURN. */ +#define TCG_CALL_NO_RETURN 0x0008 /* convenience version of most used call flags */ #define TCG_CALL_NO_RWG TCG_CALL_NO_READ_GLOBALS @@ -617,6 +626,9 @@ typedef struct TCGOp { /* Arguments for the opcode. */ TCGArg args[MAX_OPC_PARAM]; + + /* Register preferences for the output(s). */ + TCGRegSet output_pref[2]; } TCGOp; #define TCGOP_CALLI(X) (X)->param1 @@ -629,12 +641,13 @@ typedef struct TCGOp { QEMU_BUILD_BUG_ON(NB_OPS > (1 << 8)); typedef struct TCGProfile { + int64_t cpu_exec_time; int64_t tb_count1; int64_t tb_count; int64_t op_count; /* total insn count */ int op_count_max; /* max insn per TB */ - int64_t temp_count; int temp_count_max; + int64_t temp_count; int64_t del_op_count; int64_t code_in_len; int64_t code_out_len; @@ -677,6 +690,7 @@ struct TCGContext { #endif #ifdef CONFIG_DEBUG_TCG + QSIMPLEQ_HEAD(, TCGLabel) labels; int temps_in_use; int goto_tb_issue_mask; #endif @@ -702,7 +716,7 @@ struct TCGContext { /* These structures are private to tcg-target.inc.c. */ #ifdef TCG_TARGET_NEED_LDST_LABELS - QSIMPLEQ_HEAD(ldst_labels, TCGLabelQemuLdst) ldst_labels; + QSIMPLEQ_HEAD(, TCGLabelQemuLdst) ldst_labels; #endif #ifdef TCG_TARGET_NEED_POOL_LABELS struct TCGLabelPoolData *pool_labels; @@ -713,7 +727,7 @@ struct TCGContext { TCGTempSet free_temps[TCG_TYPE_COUNT * 2]; TCGTemp temps[TCG_MAX_TEMPS]; /* globals first, temps after */ - QTAILQ_HEAD(TCGOpHead, TCGOp) ops, free_ops; + QTAILQ_HEAD(, TCGOp) ops, free_ops; /* Tells which temporary holds a given register. It does not take into account fixed registers */ @@ -841,7 +855,7 @@ static inline void tcg_set_insn_start_param(TCGOp *op, int arg, target_ulong v) /* The last op that was emitted. */ static inline TCGOp *tcg_last_op(void) { - return QTAILQ_LAST(&tcg_ctx->ops, TCGOpHead); + return QTAILQ_LAST(&tcg_ctx->ops); } /* Test for whether to terminate the TB for using too many opcodes. */ @@ -1002,6 +1016,7 @@ int tcg_check_temp_count(void); #define tcg_check_temp_count() 0 #endif +int64_t tcg_cpu_exec_time(void); void tcg_dump_info(FILE *f, fprintf_function cpu_fprintf); void tcg_dump_op_count(FILE *f, fprintf_function cpu_fprintf); @@ -1023,20 +1038,22 @@ typedef struct TCGArgConstraint { /* Bits for TCGOpDef->flags, 8 bits available. */ enum { + /* Instruction exits the translation block. */ + TCG_OPF_BB_EXIT = 0x01, /* Instruction defines the end of a basic block. */ - TCG_OPF_BB_END = 0x01, + TCG_OPF_BB_END = 0x02, /* Instruction clobbers call registers and potentially update globals. */ - TCG_OPF_CALL_CLOBBER = 0x02, + TCG_OPF_CALL_CLOBBER = 0x04, /* Instruction has side effects: it cannot be removed if its outputs are not used, and might trigger exceptions. */ - TCG_OPF_SIDE_EFFECTS = 0x04, + TCG_OPF_SIDE_EFFECTS = 0x08, /* Instruction operands are 64-bits (otherwise 32-bits). */ - TCG_OPF_64BIT = 0x08, + TCG_OPF_64BIT = 0x10, /* Instruction is optional and not implemented by the host, or insn is generic and should not be implemened by the host. */ - TCG_OPF_NOT_PRESENT = 0x10, + TCG_OPF_NOT_PRESENT = 0x20, /* Instruction operands are vectors. */ - TCG_OPF_VECTOR = 0x20, + TCG_OPF_VECTOR = 0x40, }; typedef struct TCGOpDef { @@ -1070,14 +1087,11 @@ void tcg_gen_callN(void *func, TCGTemp *ret, int nargs, TCGTemp **args); TCGOp *tcg_emit_op(TCGOpcode opc); void tcg_op_remove(TCGContext *s, TCGOp *op); -TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg); -TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc, int narg); +TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, TCGOpcode opc); +TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, TCGOpcode opc); void tcg_optimize(TCGContext *s); -/* only used for debugging purposes */ -void tcg_dump_ops(TCGContext *s); - TCGv_i32 tcg_const_i32(int32_t val); TCGv_i64 tcg_const_i64(int64_t val); TCGv_i32 tcg_const_local_i32(int32_t val); @@ -1454,11 +1468,14 @@ GEN_ATOMIC_HELPER_ALL(xchg) #undef GEN_ATOMIC_HELPER #endif /* CONFIG_SOFTMMU */ -#ifdef CONFIG_ATOMIC128 -#include "qemu/int128.h" - -/* These aren't really a "proper" helpers because TCG cannot manage Int128. - However, use the same format as the others, for use by the backends. */ +/* + * These aren't really a "proper" helpers because TCG cannot manage Int128. + * However, use the same format as the others, for use by the backends. + * + * The cmpxchg functions are only defined if HAVE_CMPXCHG128; + * the ld/st functions are only defined if HAVE_ATOMIC128, + * as defined by . + */ Int128 helper_atomic_cmpxchgo_le_mmu(CPUArchState *env, target_ulong addr, Int128 cmpv, Int128 newv, TCGMemOpIdx oi, uintptr_t retaddr); @@ -1475,6 +1492,4 @@ void helper_atomic_sto_le_mmu(CPUArchState *env, target_ulong addr, Int128 val, void helper_atomic_sto_be_mmu(CPUArchState *env, target_ulong addr, Int128 val, TCGMemOpIdx oi, uintptr_t retaddr); -#endif /* CONFIG_ATOMIC128 */ - #endif /* TCG_H */ diff --git a/tcg/tci/tcg-target.h b/tcg/tci/tcg-target.h index 26140d78cb..086f34e69a 100644 --- a/tcg/tci/tcg-target.h +++ b/tcg/tci/tcg-target.h @@ -198,6 +198,8 @@ static inline void flush_icache_range(uintptr_t start, uintptr_t stop) We prefer consistency across hosts on this. */ #define TCG_TARGET_DEFAULT_MO (0) +#define TCG_TARGET_HAS_MEMORY_BSWAP 1 + static inline void tb_target_set_jmp_target(uintptr_t tc_ptr, uintptr_t jmp_addr, uintptr_t addr) { diff --git a/tcg/tci/tcg-target.inc.c b/tcg/tci/tcg-target.inc.c index 62ed097254..0015a98485 100644 --- a/tcg/tci/tcg-target.inc.c +++ b/tcg/tci/tcg-target.inc.c @@ -369,7 +369,7 @@ static const char *const tcg_target_reg_names[TCG_TARGET_NB_REGS] = { }; #endif -static void patch_reloc(tcg_insn_unit *code_ptr, int type, +static bool patch_reloc(tcg_insn_unit *code_ptr, int type, intptr_t value, intptr_t addend) { /* tcg_out_reloc always uses the same type, addend. */ @@ -381,6 +381,7 @@ static void patch_reloc(tcg_insn_unit *code_ptr, int type, } else { tcg_patch64(code_ptr, value); } + return true; } /* Parse target specific constraints. */ diff --git a/tests/.gitignore b/tests/.gitignore index 72c18aaab0..f9c0170881 100644 --- a/tests/.gitignore +++ b/tests/.gitignore @@ -5,15 +5,25 @@ benchmark-crypto-hmac check-* !check-*.c !check-*.sh +fp/*.out qht-bench rcutorture test-* !test-*.c !docker/test-* test-qapi-commands.[ch] +include/test-qapi-commands-sub-module.[ch] +test-qapi-commands-sub-sub-module.[ch] +test-qapi-emit-events.[ch] test-qapi-events.[ch] +include/test-qapi-events-sub-module.[ch] +test-qapi-events-sub-sub-module.[ch] test-qapi-types.[ch] +include/test-qapi-types-sub-module.[ch] +test-qapi-types-sub-sub-module.[ch] test-qapi-visit.[ch] +include/test-qapi-visit-sub-module.[ch] +test-qapi-visit-sub-sub-module.[ch] test-qapi-introspect.[ch] *-test qapi-schema/*.test.* diff --git a/tests/Makefile.include b/tests/Makefile.include index a49282704e..36fc73fef5 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -3,7 +3,8 @@ check-help: @echo "Regression testing targets:" @echo - @echo " $(MAKE) check Run all tests" + @echo " $(MAKE) check Run unit, qapi-schema, qtest and decodetree" + @echo @echo " $(MAKE) check-qtest-TARGET Run qtest tests for given target" @echo " $(MAKE) check-qtest Run qtest tests" @echo " $(MAKE) check-unit Run qobject tests" @@ -11,11 +12,15 @@ check-help: @echo " $(MAKE) check-qapi-schema Run QAPI schema tests" @echo " $(MAKE) check-block Run block tests" @echo " $(MAKE) check-tcg Run TCG tests" + @echo " $(MAKE) check-softfloat Run FPU emulation tests" + @echo " $(MAKE) check-acceptance Run all acceptance (functional) tests" + @echo @echo " $(MAKE) check-report.html Generates an HTML test report" - @echo " $(MAKE) check-clean Clean the tests" + @echo " $(MAKE) check-venv Creates a Python venv for tests" + @echo " $(MAKE) check-clean Clean the tests and related data" @echo @echo "Please note that HTML reports do not regenerate if the unit tests" - @echo "has not changed." + @echo "have not changed." @echo @echo "The variable SPEED can be set to control the gtester speed setting." @echo "Default options are -k and (for $(MAKE) V=1) --verbose; they can be" @@ -38,103 +43,65 @@ $(SRC_PATH)/scripts/qapi-gen.py SYSEMU_TARGET_LIST := $(subst -softmmu.mak,,$(notdir \ $(wildcard $(SRC_PATH)/default-configs/*-softmmu.mak))) -check-unit-y = tests/check-qdict$(EXESUF) -gcov-files-check-qdict-y = qobject/qdict.c -check-unit-y = tests/check-block-qdict$(EXESUF) -gcov-files-check-block-qdict-y = qobject/block-qdict.c +check-unit-y += tests/check-qdict$(EXESUF) +check-unit-y += tests/check-block-qdict$(EXESUF) check-unit-y += tests/test-char$(EXESUF) -gcov-files-check-qdict-y = chardev/char.c check-unit-y += tests/check-qnum$(EXESUF) -gcov-files-check-qnum-y = qobject/qnum.c check-unit-y += tests/check-qstring$(EXESUF) -gcov-files-check-qstring-y = qobject/qstring.c check-unit-y += tests/check-qlist$(EXESUF) -gcov-files-check-qlist-y = qobject/qlist.c check-unit-y += tests/check-qnull$(EXESUF) -gcov-files-check-qnull-y = qobject/qnull.c check-unit-y += tests/check-qobject$(EXESUF) check-unit-y += tests/check-qjson$(EXESUF) -gcov-files-check-qjson-y = qobject/qjson.c check-unit-y += tests/check-qlit$(EXESUF) -gcov-files-check-qlit-y = qobject/qlit.c check-unit-y += tests/test-qobject-output-visitor$(EXESUF) -gcov-files-test-qobject-output-visitor-y = qapi/qobject-output-visitor.c check-unit-y += tests/test-clone-visitor$(EXESUF) -gcov-files-test-clone-visitor-y = qapi/qapi-clone-visitor.c check-unit-y += tests/test-qobject-input-visitor$(EXESUF) -gcov-files-test-qobject-input-visitor-y = qapi/qobject-input-visitor.c check-unit-y += tests/test-qmp-cmds$(EXESUF) -gcov-files-test-qmp-cmds-y = qapi/qmp-dispatch.c check-unit-y += tests/test-string-input-visitor$(EXESUF) -gcov-files-test-string-input-visitor-y = qapi/string-input-visitor.c check-unit-y += tests/test-string-output-visitor$(EXESUF) -gcov-files-test-string-output-visitor-y = qapi/string-output-visitor.c check-unit-y += tests/test-qmp-event$(EXESUF) -gcov-files-test-qmp-event-y += qapi/qmp-event.c check-unit-y += tests/test-opts-visitor$(EXESUF) -gcov-files-test-opts-visitor-y = qapi/opts-visitor.c check-unit-y += tests/test-coroutine$(EXESUF) -gcov-files-test-coroutine-y = coroutine-$(CONFIG_COROUTINE_BACKEND).c check-unit-y += tests/test-visitor-serialization$(EXESUF) check-unit-y += tests/test-iov$(EXESUF) -gcov-files-test-iov-y = util/iov.c check-unit-y += tests/test-aio$(EXESUF) -gcov-files-test-aio-y = util/async.c util/qemu-timer.o -gcov-files-test-aio-$(CONFIG_WIN32) += util/aio-win32.c -gcov-files-test-aio-$(CONFIG_POSIX) += util/aio-posix.c check-unit-y += tests/test-aio-multithread$(EXESUF) -gcov-files-test-aio-multithread-y = $(gcov-files-test-aio-y) -gcov-files-test-aio-multithread-y += util/qemu-coroutine.c tests/iothread.c check-unit-y += tests/test-throttle$(EXESUF) 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-bdrv-drain$(EXESUF) +check-unit-y += tests/test-bdrv-graph-mod$(EXESUF) check-unit-y += tests/test-blockjob$(EXESUF) check-unit-y += tests/test-blockjob-txn$(EXESUF) check-unit-y += tests/test-block-backend$(EXESUF) +check-unit-y += tests/test-block-iothread$(EXESUF) +check-unit-y += tests/test-image-locking$(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 = ifeq ($(CONFIG_SOFTMMU),y) check-unit-y += tests/test-xbzrle$(EXESUF) -gcov-files-test-xbzrle-y = migration/xbzrle.c check-unit-$(CONFIG_POSIX) += tests/test-vmstate$(EXESUF) endif check-unit-y += tests/test-cutils$(EXESUF) -gcov-files-test-cutils-y += util/cutils.c check-unit-y += tests/test-shift128$(EXESUF) -gcov-files-test-shift128-y = util/host-utils.c check-unit-y += tests/test-mul64$(EXESUF) -gcov-files-test-mul64-y = util/host-utils.c check-unit-y += tests/test-int128$(EXESUF) # all code tested by test-int128 is inside int128.h -gcov-files-test-int128-y = check-unit-y += tests/rcutorture$(EXESUF) -gcov-files-rcutorture-y = util/rcu.c check-unit-y += tests/test-rcu-list$(EXESUF) -gcov-files-test-rcu-list-y = util/rcu.c +check-unit-y += tests/test-rcu-simpleq$(EXESUF) +check-unit-y += tests/test-rcu-tailq$(EXESUF) check-unit-y += tests/test-qdist$(EXESUF) -gcov-files-test-qdist-y = util/qdist.c check-unit-y += tests/test-qht$(EXESUF) -gcov-files-test-qht-y = util/qht.c check-unit-y += tests/test-qht-par$(EXESUF) -gcov-files-test-qht-par-y = util/qht.c check-unit-y += tests/test-bitops$(EXESUF) check-unit-y += tests/test-bitcnt$(EXESUF) -check-unit-$(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) += tests/test-qdev-global-props$(EXESUF) +check-unit-y += tests/test-qdev-global-props$(EXESUF) check-unit-y += tests/check-qom-interface$(EXESUF) -gcov-files-check-qom-interface-y = qom/object.c check-unit-y += tests/check-qom-proplist$(EXESUF) -gcov-files-check-qom-proplist-y = qom/object.c check-unit-y += tests/test-qemu-opts$(EXESUF) -gcov-files-test-qemu-opts-y = util/qemu-option.c check-unit-y += tests/test-keyval$(EXESUF) -gcov-files-test-keyval-y = util/keyval.c check-unit-y += tests/test-write-threshold$(EXESUF) -gcov-files-test-write-threshold-y = block/write-threshold.c check-unit-y += tests/test-crypto-hash$(EXESUF) check-speed-y += tests/benchmark-crypto-hash$(EXESUF) check-unit-y += tests/test-crypto-hmac$(EXESUF) @@ -145,10 +112,15 @@ 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) +check-unit-$(call land,$(CONFIG_LINUX),$(CONFIG_VIRTIO_SERIAL)) += tests/test-qga$(EXESUF) endif check-unit-y += tests/test-timed-average$(EXESUF) +check-unit-$(CONFIG_INOTIFY1) += tests/test-util-filemonitor$(EXESUF) check-unit-y += tests/test-util-sockets$(EXESUF) +check-unit-y += tests/test-authz-simple$(EXESUF) +check-unit-y += tests/test-authz-list$(EXESUF) +check-unit-y += tests/test-authz-listfile$(EXESUF) +check-unit-$(CONFIG_AUTH_PAM) += tests/test-authz-pam$(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) @@ -156,244 +128,149 @@ 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-$(if $(CONFIG_NETTLE),y,$(CONFIG_GCRYPT)) += 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) check-unit-y += tests/test-logging$(EXESUF) -gcov-files-test-logging-y = util/log.c check-unit-$(CONFIG_REPLICATION) += tests/test-replication$(EXESUF) check-unit-y += tests/test-bufferiszero$(EXESUF) -gcov-files-check-bufferiszero-y = util/bufferiszero.c check-unit-y += tests/test-uuid$(EXESUF) check-unit-y += tests/ptimer-test$(EXESUF) -gcov-files-ptimer-test-y = hw/core/ptimer.c check-unit-y += tests/test-qapi-util$(EXESUF) -gcov-files-test-qapi-util-y = qapi/qapi-util.c 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/qmp-test$(EXESUF) -gcov-files-generic-y = monitor.c qapi/qmp-dispatch.c +check-qtest-generic-y += tests/qmp-test$(EXESUF) +check-qtest-generic-y += tests/qmp-cmd-test$(EXESUF) + check-qtest-generic-y += tests/device-introspect-test$(EXESUF) -gcov-files-generic-y = qdev-monitor.c qmp.c check-qtest-generic-y += tests/cdrom-test$(EXESUF) -gcov-files-ipack-y += hw/ipack/ipack.c -check-qtest-ipack-y += tests/ipoctal232-test$(EXESUF) -gcov-files-ipack-y += hw/char/ipoctal232.c +check-qtest-pci-$(CONFIG_RTL8139_PCI) += tests/rtl8139-test$(EXESUF) +check-qtest-pci-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) +check-qtest-pci-$(CONFIG_HDA) += tests/intel-hda-test$(EXESUF) +check-qtest-pci-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) -check-qtest-virtioserial-y += tests/virtio-console-test$(EXESUF) -gcov-files-virtioserial-y += hw/char/virtio-console.c - -gcov-files-virtio-y += i386-softmmu/hw/virtio/virtio.c -check-qtest-virtio-y += tests/virtio-net-test$(EXESUF) -gcov-files-virtio-y += i386-softmmu/hw/net/virtio-net.c -check-qtest-virtio-y += tests/virtio-balloon-test$(EXESUF) -gcov-files-virtio-y += i386-softmmu/hw/virtio/virtio-balloon.c -check-qtest-virtio-y += tests/virtio-blk-test$(EXESUF) -gcov-files-virtio-y += i386-softmmu/hw/block/virtio-blk.c -check-qtest-virtio-y += tests/virtio-rng-test$(EXESUF) -gcov-files-virtio-y += hw/virtio/virtio-rng.c -check-qtest-virtio-y += tests/virtio-scsi-test$(EXESUF) -gcov-files-virtio-y += i386-softmmu/hw/scsi/virtio-scsi.c -ifeq ($(CONFIG_VIRTIO)$(CONFIG_VIRTFS)$(CONFIG_PCI),yyy) -check-qtest-virtio-y += tests/virtio-9p-test$(EXESUF) -gcov-files-virtio-y += hw/9pfs/virtio-9p.c -gcov-files-virtio-y += i386-softmmu/hw/9pfs/virtio-9p-device.c -endif -check-qtest-virtio-y += tests/virtio-serial-test$(EXESUF) -gcov-files-virtio-y += i386-softmmu/hw/char/virtio-serial-bus.c -check-qtest-virtio-y += $(check-qtest-virtioserial-y) -gcov-files-virtio-y += $(gcov-files-virtioserial-y) - -check-qtest-pci-y += tests/e1000-test$(EXESUF) -gcov-files-pci-y += hw/net/e1000.c -check-qtest-pci-y += tests/e1000e-test$(EXESUF) -gcov-files-pci-y += hw/net/e1000e.c hw/net/e1000e_core.c -check-qtest-pci-y += tests/rtl8139-test$(EXESUF) -gcov-files-pci-y += hw/net/rtl8139.c -check-qtest-pci-y += tests/pcnet-test$(EXESUF) -gcov-files-pci-y += hw/net/pcnet.c -gcov-files-pci-y += hw/net/pcnet-pci.c -check-qtest-pci-y += tests/eepro100-test$(EXESUF) -gcov-files-pci-y += hw/net/eepro100.c -check-qtest-pci-y += tests/ne2000-test$(EXESUF) -gcov-files-pci-y += hw/net/ne2000.c -check-qtest-pci-y += tests/nvme-test$(EXESUF) -gcov-files-pci-y += hw/block/nvme.c -check-qtest-pci-y += tests/ac97-test$(EXESUF) -gcov-files-pci-y += hw/audio/ac97.c -check-qtest-pci-y += tests/es1370-test$(EXESUF) -gcov-files-pci-y += hw/audio/es1370.c -check-qtest-pci-y += $(check-qtest-virtio-y) -gcov-files-pci-y += $(gcov-files-virtio-y) hw/virtio/virtio-pci.c -check-qtest-pci-y += tests/tpci200-test$(EXESUF) -gcov-files-pci-y += hw/ipack/tpci200.c -check-qtest-pci-y += $(check-qtest-ipack-y) -gcov-files-pci-y += $(gcov-files-ipack-y) -check-qtest-pci-y += tests/display-vga-test$(EXESUF) -gcov-files-pci-y += hw/display/vga.c -gcov-files-pci-y += hw/display/cirrus_vga.c -gcov-files-pci-y += hw/display/vga-pci.c -gcov-files-pci-y += hw/display/virtio-gpu.c -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_IVSHMEM) += tests/ivshmem-test$(EXESUF) -gcov-files-pci-y += hw/misc/ivshmem.c -check-qtest-pci-y += tests/megasas-test$(EXESUF) -gcov-files-pci-y += hw/scsi/megasas.c - -check-qtest-i386-y = tests/endianness-test$(EXESUF) +check-qtest-i386-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) check-qtest-i386-y += tests/fdc-test$(EXESUF) -gcov-files-i386-y = hw/block/fdc.c check-qtest-i386-y += tests/ide-test$(EXESUF) check-qtest-i386-y += tests/ahci-test$(EXESUF) 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/boot-serial-test$(EXESUF) +check-qtest-i386-$(CONFIG_SGA) += tests/boot-serial-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += 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-$(CONFIG_ISA_IPMI_KCS) += tests/ipmi-kcs-test$(EXESUF) +# Disabled temporarily as it fails intermittently especially under NetBSD VM +# check-qtest-i386-$(CONFIG_ISA_IPMI_BT) += 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/device-plug-test$(EXESUF) check-qtest-i386-y += tests/drive_del-test$(EXESUF) -check-qtest-i386-y += tests/wdt_ib700-test$(EXESUF) +check-qtest-i386-$(CONFIG_WDT_IB700) += tests/wdt_ib700-test$(EXESUF) check-qtest-i386-y += tests/tco-test$(EXESUF) -gcov-files-i386-y += hw/watchdog/watchdog.c hw/watchdog/wdt_ib700.c check-qtest-i386-y += $(check-qtest-pci-y) -gcov-files-i386-y += $(gcov-files-pci-y) -check-qtest-i386-y += tests/vmxnet3-test$(EXESUF) -gcov-files-i386-y += hw/net/vmxnet3.c -gcov-files-i386-y += hw/net/net_rx_pkt.c -gcov-files-i386-y += hw/net/net_tx_pkt.c -check-qtest-i386-y += tests/pvpanic-test$(EXESUF) -gcov-files-i386-y += i386-softmmu/hw/misc/pvpanic.c -check-qtest-i386-y += tests/i82801b11-test$(EXESUF) -gcov-files-i386-y += hw/pci-bridge/i82801b11.c -check-qtest-i386-y += tests/ioh3420-test$(EXESUF) -gcov-files-i386-y += hw/pci-bridge/ioh3420.c -check-qtest-i386-y += tests/usb-hcd-ohci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-ohci.c -check-qtest-i386-y += tests/usb-hcd-uhci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-uhci.c +check-qtest-i386-$(CONFIG_PVPANIC) += tests/pvpanic-test$(EXESUF) +check-qtest-i386-$(CONFIG_I82801B11) += tests/i82801b11-test$(EXESUF) +check-qtest-i386-$(CONFIG_IOH3420) += tests/ioh3420-test$(EXESUF) +check-qtest-i386-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) +ifeq ($(CONFIG_USB_ECHI)$(CONFIG_USB_UHCI),yy) check-qtest-i386-y += tests/usb-hcd-ehci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-ehci.c -gcov-files-i386-y += hw/usb/dev-hid.c -gcov-files-i386-y += hw/usb/dev-storage.c -check-qtest-i386-y += tests/usb-hcd-xhci-test$(EXESUF) -gcov-files-i386-y += hw/usb/hcd-xhci.c +endif +check-qtest-i386-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) check-qtest-i386-y += tests/cpu-plug-test$(EXESUF) check-qtest-i386-y += tests/q35-test$(EXESUF) check-qtest-i386-y += tests/vmgenid-test$(EXESUF) -gcov-files-i386-y += hw/pci-host/q35.c -check-qtest-i386-$(CONFIG_VHOST_USER_NET_TEST_i386) += tests/vhost-user-test$(EXESUF) -ifeq ($(CONFIG_VHOST_USER_NET_TEST_i386),) -check-qtest-x86_64-$(CONFIG_VHOST_USER_NET_TEST_x86_64) += tests/vhost-user-test$(EXESUF) -endif -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-swtpm-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-crb-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-swtpm-test$(EXESUF) -check-qtest-i386-$(CONFIG_TPM) += tests/tpm-tis-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-swtpm-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_CRB) += tests/tpm-crb-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-swtpm-test$(EXESUF) +check-qtest-i386-$(CONFIG_TPM_TIS) += tests/tpm-tis-test$(EXESUF) check-qtest-i386-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-i386-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) +check-qtest-i386-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) check-qtest-i386-y += tests/migration-test$(EXESUF) check-qtest-i386-y += tests/test-x86-cpuid-compat$(EXESUF) check-qtest-i386-y += tests/numa-test$(EXESUF) check-qtest-x86_64-y += $(check-qtest-i386-y) -check-qtest-x86_64-y += tests/sdhci-test$(EXESUF) -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)) -check-qtest-alpha-y = tests/boot-serial-test$(EXESUF) +check-qtest-alpha-y += tests/boot-serial-test$(EXESUF) +check-qtest-alpha-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) -check-qtest-hppa-y = tests/boot-serial-test$(EXESUF) +check-qtest-hppa-y += tests/boot-serial-test$(EXESUF) +check-qtest-hppa-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-m68k-y = tests/boot-serial-test$(EXESUF) -check-qtest-microblaze-y = tests/boot-serial-test$(EXESUF) +check-qtest-microblaze-y += tests/boot-serial-test$(EXESUF) -check-qtest-mips-y = tests/endianness-test$(EXESUF) +check-qtest-mips-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) +check-qtest-mips-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) -check-qtest-mips64-y = tests/endianness-test$(EXESUF) +check-qtest-mips64-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) +check-qtest-mips64-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) -check-qtest-mips64el-y = tests/endianness-test$(EXESUF) +check-qtest-mips64el-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) +check-qtest-mips64el-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) -check-qtest-moxie-y = tests/boot-serial-test$(EXESUF) +check-qtest-moxie-y += tests/boot-serial-test$(EXESUF) -check-qtest-ppc-y = tests/endianness-test$(EXESUF) +check-qtest-ppc-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) check-qtest-ppc-y += tests/boot-order-test$(EXESUF) check-qtest-ppc-y += tests/prom-env-test$(EXESUF) check-qtest-ppc-y += tests/drive_del-test$(EXESUF) check-qtest-ppc-y += tests/boot-serial-test$(EXESUF) -check-qtest-ppc-y += tests/m48t59-test$(EXESUF) -gcov-files-ppc-y += hw/timer/m48t59.c +check-qtest-ppc-$(CONFIG_M48T59) += tests/m48t59-test$(EXESUF) -check-qtest-ppc64-y = $(check-qtest-ppc-y) -gcov-files-ppc64-y = $(subst ppc-softmmu/,ppc64-softmmu/,$(gcov-files-ppc-y)) -check-qtest-ppc64-y += tests/spapr-phb-test$(EXESUF) -gcov-files-ppc64-y += ppc64-softmmu/hw/ppc/spapr_pci.c -check-qtest-ppc64-y += tests/pnv-xscom-test$(EXESUF) +check-qtest-ppc64-y += $(check-qtest-ppc-y) +check-qtest-ppc64-$(CONFIG_PSERIES) += tests/device-plug-test$(EXESUF) +check-qtest-ppc64-$(CONFIG_POWERNV) += tests/pnv-xscom-test$(EXESUF) check-qtest-ppc64-y += tests/migration-test$(EXESUF) -check-qtest-ppc64-y += tests/rtas-test$(EXESUF) +check-qtest-ppc64-$(CONFIG_PSERIES) += tests/rtas-test$(EXESUF) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) -check-qtest-ppc64-y += tests/usb-hcd-ohci-test$(EXESUF) -gcov-files-ppc64-y += hw/usb/hcd-ohci.c -check-qtest-ppc64-y += tests/usb-hcd-uhci-test$(EXESUF) -gcov-files-ppc64-y += hw/usb/hcd-uhci.c -check-qtest-ppc64-y += tests/usb-hcd-xhci-test$(EXESUF) -gcov-files-ppc64-y += hw/usb/hcd-xhci.c -check-qtest-ppc64-y += $(check-qtest-virtio-y) +check-qtest-ppc64-$(CONFIG_USB_UHCI) += tests/usb-hcd-uhci-test$(EXESUF) +check-qtest-ppc64-$(CONFIG_USB_XHCI_NEC) += tests/usb-hcd-xhci-test$(EXESUF) check-qtest-ppc64-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) -check-qtest-ppc64-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) -check-qtest-ppc64-y += tests/display-vga-test$(EXESUF) +check-qtest-ppc64-$(CONFIG_RTL8139_PCI) += tests/test-filter-redirector$(EXESUF) +check-qtest-ppc64-$(CONFIG_VGA) += tests/display-vga-test$(EXESUF) check-qtest-ppc64-y += tests/numa-test$(EXESUF) -check-qtest-ppc64-$(CONFIG_IVSHMEM) += tests/ivshmem-test$(EXESUF) +check-qtest-ppc64-$(CONFIG_IVSHMEM_DEVICE) += tests/ivshmem-test$(EXESUF) check-qtest-ppc64-y += tests/cpu-plug-test$(EXESUF) -check-qtest-sh4-y = tests/endianness-test$(EXESUF) +check-qtest-sh4-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-sh4eb-y = tests/endianness-test$(EXESUF) +check-qtest-sh4eb-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) -check-qtest-sparc-y = tests/prom-env-test$(EXESUF) +check-qtest-sparc-y += tests/prom-env-test$(EXESUF) check-qtest-sparc-y += tests/m48t59-test$(EXESUF) -gcov-files-sparc-y = hw/timer/m48t59.c check-qtest-sparc-y += tests/boot-serial-test$(EXESUF) -check-qtest-sparc64-y = tests/endianness-test$(EXESUF) +check-qtest-sparc64-$(CONFIG_ISA_TESTDEV) = tests/endianness-test$(EXESUF) check-qtest-sparc64-y += tests/prom-env-test$(EXESUF) check-qtest-sparc64-y += tests/boot-serial-test$(EXESUF) -check-qtest-arm-y = tests/tmp105-test$(EXESUF) +check-qtest-arm-y += tests/tmp105-test$(EXESUF) check-qtest-arm-y += tests/pca9552-test$(EXESUF) check-qtest-arm-y += tests/ds1338-test$(EXESUF) +check-qtest-arm-y += tests/microbit-test$(EXESUF) check-qtest-arm-y += tests/m25p80-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 check-qtest-arm-y += tests/test-arm-mptimer$(EXESUF) -gcov-files-arm-y += hw/timer/arm_mptimer.c check-qtest-arm-y += tests/boot-serial-test$(EXESUF) -check-qtest-arm-y += tests/sdhci-test$(EXESUF) +check-qtest-arm-y += tests/hexloader-test$(EXESUF) check-qtest-aarch64-y = tests/numa-test$(EXESUF) -check-qtest-aarch64-y += tests/sdhci-test$(EXESUF) check-qtest-aarch64-y += tests/boot-serial-test$(EXESUF) +check-qtest-aarch64-y += tests/migration-test$(EXESUF) -check-qtest-microblazeel-y = $(check-qtest-microblaze-y) +check-qtest-microblazeel-y += $(check-qtest-microblaze-y) -check-qtest-xtensaeb-y = $(check-qtest-xtensa-y) +check-qtest-xtensaeb-y += $(check-qtest-xtensa-y) check-qtest-s390x-y = tests/boot-serial-test$(EXESUF) check-qtest-s390x-$(CONFIG_SLIRP) += tests/pxe-test$(EXESUF) @@ -401,10 +278,10 @@ check-qtest-s390x-$(CONFIG_SLIRP) += tests/test-netfilter$(EXESUF) check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-mirror$(EXESUF) check-qtest-s390x-$(CONFIG_POSIX) += tests/test-filter-redirector$(EXESUF) check-qtest-s390x-y += tests/drive_del-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-balloon-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-console-test$(EXESUF) -check-qtest-s390x-y += tests/virtio-serial-test$(EXESUF) +check-qtest-s390x-y += tests/device-plug-test$(EXESUF) +check-qtest-s390x-y += tests/virtio-ccw-test$(EXESUF) check-qtest-s390x-y += tests/cpu-plug-test$(EXESUF) +check-qtest-s390x-y += tests/migration-test$(EXESUF) check-qtest-generic-y += tests/machine-none-test$(EXESUF) check-qtest-generic-y += tests/qom-test$(EXESUF) @@ -421,6 +298,7 @@ qapi-schema += alternate-conflict-string.json qapi-schema += alternate-conflict-bool-string.json qapi-schema += alternate-conflict-num-string.json qapi-schema += alternate-empty.json +qapi-schema += alternate-invalid-dict.json qapi-schema += alternate-nested.json qapi-schema += alternate-unknown.json qapi-schema += args-alternate.json @@ -482,10 +360,12 @@ qapi-schema += double-data.json qapi-schema += double-type.json qapi-schema += duplicate-key.json qapi-schema += empty.json +qapi-schema += enum-bad-member.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-dict-member-unknown.json +qapi-schema += enum-if-invalid.json qapi-schema += enum-int-member.json qapi-schema += enum-member-case.json qapi-schema += enum-missing-data.json @@ -495,6 +375,7 @@ qapi-schema += escape-too-big.json qapi-schema += escape-too-short.json qapi-schema += event-boxed-empty.json qapi-schema += event-case.json +qapi-schema += event-member-invalid-dict.json qapi-schema += event-nest-struct.json qapi-schema += flat-union-array-branch.json qapi-schema += flat-union-bad-base.json @@ -504,9 +385,11 @@ 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-inline-invalid-dict.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-invalid-if-discriminator.json qapi-schema += flat-union-no-base.json qapi-schema += flat-union-optional-discriminator.json qapi-schema += flat-union-string-discriminator.json @@ -519,7 +402,6 @@ 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 @@ -531,6 +413,7 @@ 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 += nested-struct-data-invalid-dict.json qapi-schema += non-objects.json qapi-schema += oob-test.json qapi-schema += allow-preconfig-test.json @@ -561,6 +444,7 @@ 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-dict.json qapi-schema += struct-member-invalid.json qapi-schema += trailing-comma-list.json qapi-schema += trailing-comma-object.json @@ -572,6 +456,7 @@ qapi-schema += unicode-str.json qapi-schema += union-base-empty.json qapi-schema += union-base-no-discriminator.json qapi-schema += union-branch-case.json +qapi-schema += union-branch-invalid-dict.json qapi-schema += union-clash-branches.json qapi-schema += union-empty.json qapi-schema += union-invalid-base.json @@ -583,8 +468,18 @@ qapi-schema += unknown-expr-key.json check-qapi-schema-y := $(addprefix tests/qapi-schema/, $(qapi-schema)) -GENERATED_FILES += tests/test-qapi-types.h tests/test-qapi-visit.h \ - tests/test-qapi-commands.h tests/test-qapi-events.h \ +GENERATED_FILES += tests/test-qapi-types.h \ + tests/include/test-qapi-types-sub-module.h \ + tests/test-qapi-types-sub-sub-module.h \ + tests/test-qapi-visit.h \ + tests/include/test-qapi-visit-sub-module.h \ + tests/test-qapi-visit-sub-sub-module.h \ + tests/test-qapi-commands.h \ + tests/include/test-qapi-commands-sub-module.h \ + tests/test-qapi-commands-sub-sub-module.h \ + tests/test-qapi-events.h \ + tests/include/test-qapi-events-sub-module.h \ + tests/test-qapi-events-sub-sub-module.h \ tests/test-qapi-introspect.h test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ @@ -599,9 +494,11 @@ test-obj-y = tests/check-qnum.o tests/check-qstring.o tests/check-qdict.o \ tests/test-x86-cpuid.o tests/test-mul64.o tests/test-int128.o \ tests/test-opts-visitor.o tests/test-qmp-event.o \ tests/rcutorture.o tests/test-rcu-list.o \ + tests/test-rcu-simpleq.o \ + tests/test-rcu-tailq.o \ tests/test-qdist.o tests/test-shift128.o \ tests/test-qht.o tests/qht-bench.o tests/test-qht-par.o \ - tests/atomic_add-bench.o + tests/atomic_add-bench.o tests/atomic64-bench.o $(test-obj-y): QEMU_INCLUDES += -Itests QEMU_CFLAGS += -I$(SRC_PATH)/tests @@ -610,12 +507,18 @@ QEMU_CFLAGS += -I$(SRC_PATH)/tests # Deps that are common to various different sets of tests below test-util-obj-y = libqemuutil.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-events.o tests/test-qapi-introspect.o \ +test-qapi-obj-y = tests/test-qapi-types.o \ + tests/include/test-qapi-types-sub-module.o \ + tests/test-qapi-types-sub-sub-module.o \ + tests/test-qapi-visit.o \ + tests/include/test-qapi-visit-sub-module.o \ + tests/test-qapi-visit-sub-sub-module.o \ + tests/test-qapi-introspect.o \ $(test-qom-obj-y) -benchmark-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) -test-crypto-obj-y = $(crypto-obj-y) $(test-qom-obj-y) +benchmark-crypto-obj-y = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y) +test-crypto-obj-y = $(authz-obj-y) $(crypto-obj-y) $(test-qom-obj-y) test-io-obj-y = $(io-obj-y) $(test-crypto-obj-y) +test-authz-obj-y = $(test-qom-obj-y) $(authz-obj-y) test-block-obj-y = $(block-obj-y) $(test-io-obj-y) tests/iothread.o tests/check-qnum$(EXESUF): tests/check-qnum.o $(test-util-obj-y) @@ -636,9 +539,12 @@ tests/test-aio$(EXESUF): tests/test-aio.o $(test-block-obj-y) tests/test-aio-multithread$(EXESUF): tests/test-aio-multithread.o $(test-block-obj-y) tests/test-throttle$(EXESUF): tests/test-throttle.o $(test-block-obj-y) tests/test-bdrv-drain$(EXESUF): tests/test-bdrv-drain.o $(test-block-obj-y) $(test-util-obj-y) +tests/test-bdrv-graph-mod$(EXESUF): tests/test-bdrv-graph-mod.o $(test-block-obj-y) $(test-util-obj-y) tests/test-blockjob$(EXESUF): tests/test-blockjob.o $(test-block-obj-y) $(test-util-obj-y) tests/test-blockjob-txn$(EXESUF): tests/test-blockjob-txn.o $(test-block-obj-y) $(test-util-obj-y) tests/test-block-backend$(EXESUF): tests/test-block-backend.o $(test-block-obj-y) $(test-util-obj-y) +tests/test-block-iothread$(EXESUF): tests/test-block-iothread.o $(test-block-obj-y) $(test-util-obj-y) +tests/test-image-locking$(EXESUF): tests/test-image-locking.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) $(test-crypto-obj-y) @@ -648,12 +554,18 @@ tests/test-cutils$(EXESUF): tests/test-cutils.o util/cutils.o $(test-util-obj-y) tests/test-int128$(EXESUF): tests/test-int128.o 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-rcu-simpleq$(EXESUF): tests/test-rcu-simpleq.o $(test-util-obj-y) +tests/test-rcu-tailq$(EXESUF): tests/test-rcu-tailq.o $(test-util-obj-y) tests/test-qdist$(EXESUF): tests/test-qdist.o $(test-util-obj-y) tests/test-qht$(EXESUF): tests/test-qht.o $(test-util-obj-y) tests/test-qht-par$(EXESUF): tests/test-qht-par.o tests/qht-bench$(EXESUF) $(test-util-obj-y) tests/qht-bench$(EXESUF): tests/qht-bench.o $(test-util-obj-y) tests/test-bufferiszero$(EXESUF): tests/test-bufferiszero.o $(test-util-obj-y) tests/atomic_add-bench$(EXESUF): tests/atomic_add-bench.o $(test-util-obj-y) +tests/atomic64-bench$(EXESUF): tests/atomic64-bench.o $(test-util-obj-y) + +tests/fp/%: + $(MAKE) -C $(dir $@) $(notdir $@) 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\ @@ -676,12 +588,32 @@ tests/test-replication$(EXESUF): tests/test-replication.o $(test-util-obj-y) \ $(test-block-obj-y) tests/test-qapi-types.c tests/test-qapi-types.h \ +tests/include/test-qapi-types-sub-module.c \ +tests/include/test-qapi-types-sub-module.h \ +tests/test-qapi-types-sub-sub-module.c \ +tests/test-qapi-types-sub-sub-module.h \ tests/test-qapi-visit.c tests/test-qapi-visit.h \ +tests/include/test-qapi-visit-sub-module.c \ +tests/include/test-qapi-visit-sub-module.h \ +tests/test-qapi-visit-sub-sub-module.c \ +tests/test-qapi-visit-sub-sub-module.h \ tests/test-qapi-commands.h tests/test-qapi-commands.c \ +tests/include/test-qapi-commands-sub-module.h \ +tests/include/test-qapi-commands-sub-module.c \ +tests/test-qapi-commands-sub-sub-module.h \ +tests/test-qapi-commands-sub-sub-module.c \ tests/test-qapi-events.c tests/test-qapi-events.h \ +tests/include/test-qapi-events-sub-module.c \ +tests/include/test-qapi-events-sub-module.h \ +tests/test-qapi-events-sub-sub-module.c \ +tests/test-qapi-events-sub-sub-module.h \ tests/test-qapi-introspect.c tests/test-qapi-introspect.h: \ tests/test-qapi-gen-timestamp ; -tests/test-qapi-gen-timestamp: $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json $(qapi-py) +tests/test-qapi-gen-timestamp: \ + $(SRC_PATH)/tests/qapi-schema/qapi-schema-test.json \ + $(SRC_PATH)/tests/qapi-schema/include/sub-module.json \ + $(SRC_PATH)/tests/qapi-schema/sub-sub-module.json \ + $(qapi-py) $(call quiet-command,$(PYTHON) $(SRC_PATH)/scripts/qapi-gen.py \ -o tests -p "test-" $<, \ "GEN","$(@:%-timestamp=%)") @@ -696,7 +628,7 @@ tests/qapi-schema/doc-good.test.texi: $(SRC_PATH)/tests/qapi-schema/doc-good.jso 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-event$(EXESUF): tests/test-qmp-event.o $(test-qapi-obj-y) tests/test-qapi-events.o tests/test-qobject-output-visitor$(EXESUF): tests/test-qobject-output-visitor.o $(test-qapi-obj-y) tests/test-clone-visitor$(EXESUF): tests/test-clone-visitor.o $(test-qapi-obj-y) tests/test-qobject-input-visitor$(EXESUF): tests/test-qobject-input-visitor.o $(test-qapi-obj-y) @@ -730,8 +662,14 @@ tests/test-crypto-tlssession$(EXESUF): tests/test-crypto-tlssession.o \ tests/crypto-tls-x509-helpers.o tests/pkix_asn1_tab.o \ tests/crypto-tls-psk-helpers.o \ $(test-crypto-obj-y) +tests/test-util-filemonitor$(EXESUF): tests/test-util-filemonitor.o \ + $(test-util-obj-y) tests/test-util-sockets$(EXESUF): tests/test-util-sockets.o \ tests/socket-helpers.o $(test-util-obj-y) +tests/test-authz-simple$(EXESUF): tests/test-authz-simple.o $(test-authz-obj-y) +tests/test-authz-list$(EXESUF): tests/test-authz-list.o $(test-authz-obj-y) +tests/test-authz-listfile$(EXESUF): tests/test-authz-listfile.o $(test-authz-obj-y) +tests/test-authz-pam$(EXESUF): tests/test-authz-pam.o $(test-authz-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 tests/socket-helpers.o $(test-io-obj-y) @@ -755,7 +693,10 @@ 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 +libqgraph-obj-y = tests/libqos/qgraph.o + +libqos-obj-y = $(libqgraph-obj-y) tests/libqos/pci.o tests/libqos/fw_cfg.o +libqos-obj-y += tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o libqos-spapr-obj-y = $(libqos-obj-y) tests/libqos/malloc-spapr.o libqos-spapr-obj-y += tests/libqos/libqos-spapr.o @@ -767,19 +708,77 @@ 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-spapr-obj-y) $(libqos-pc-obj-y) tests/libqos/usb.o -libqos-virtio-obj-y = $(libqos-spapr-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 + +# Devices +qos-test-obj-y = tests/qos-test.o $(libqgraph-obj-y) +qos-test-obj-y += $(libqos-pc-obj-y) $(libqos-spapr-obj-y) +qos-test-obj-y += tests/libqos/e1000e.o +qos-test-obj-y += tests/libqos/sdhci.o +qos-test-obj-y += tests/libqos/tpci200.o +qos-test-obj-y += tests/libqos/virtio.o +qos-test-obj-$(CONFIG_VIRTFS) += tests/libqos/virtio-9p.o +qos-test-obj-y += tests/libqos/virtio-balloon.o +qos-test-obj-y += tests/libqos/virtio-blk.o +qos-test-obj-y += tests/libqos/virtio-mmio.o +qos-test-obj-y += tests/libqos/virtio-net.o +qos-test-obj-y += tests/libqos/virtio-pci.o +qos-test-obj-y += tests/libqos/virtio-rng.o +qos-test-obj-y += tests/libqos/virtio-scsi.o +qos-test-obj-y += tests/libqos/virtio-serial.o + +# Machines +qos-test-obj-y += tests/libqos/aarch64-xlnx-zcu102-machine.o +qos-test-obj-y += tests/libqos/arm-raspi2-machine.o +qos-test-obj-y += tests/libqos/arm-sabrelite-machine.o +qos-test-obj-y += tests/libqos/arm-smdkc210-machine.o +qos-test-obj-y += tests/libqos/arm-virt-machine.o +qos-test-obj-y += tests/libqos/arm-xilinx-zynq-a9-machine.o +qos-test-obj-y += tests/libqos/ppc64_pseries-machine.o +qos-test-obj-y += tests/libqos/x86_64_pc-machine.o + +# Tests +qos-test-obj-y += tests/ac97-test.o +qos-test-obj-y += tests/e1000-test.o +qos-test-obj-y += tests/e1000e-test.o +qos-test-obj-y += tests/eepro100-test.o +qos-test-obj-y += tests/es1370-test.o +qos-test-obj-y += tests/ipoctal232-test.o +qos-test-obj-y += tests/megasas-test.o +qos-test-obj-y += tests/ne2000-test.o +qos-test-obj-y += tests/nvme-test.o +qos-test-obj-y += tests/pci-test.o +qos-test-obj-y += tests/pcnet-test.o +qos-test-obj-y += tests/sdhci-test.o +qos-test-obj-y += tests/spapr-phb-test.o +qos-test-obj-y += tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) +qos-test-obj-$(CONFIG_VHOST_NET_USER) += tests/vhost-user-test.o $(chardev-obj-y) $(test-io-obj-y) +qos-test-obj-y += tests/virtio-test.o +qos-test-obj-$(CONFIG_VIRTFS) += tests/virtio-9p-test.o +qos-test-obj-y += tests/virtio-blk-test.o +qos-test-obj-y += tests/virtio-net-test.o +qos-test-obj-y += tests/virtio-rng-test.o +qos-test-obj-y += tests/virtio-scsi-test.o +qos-test-obj-y += tests/virtio-serial-test.o +qos-test-obj-y += tests/vmxnet3-test.o + +check-unit-y += tests/test-qgraph$(EXESUF) +tests/test-qgraph$(EXESUF): tests/test-qgraph.o $(libqgraph-obj-y) + +check-qtest-generic-y += tests/qos-test$(EXESUF) +tests/qos-test$(EXESUF): $(qos-test-obj-y) tests/qmp-test$(EXESUF): tests/qmp-test.o +tests/qmp-cmd-test$(EXESUF): tests/qmp-cmd-test.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/hexloader-test$(EXESUF): tests/hexloader-test.o tests/endianness-test$(EXESUF): tests/endianness-test.o -tests/spapr-phb-test$(EXESUF): tests/spapr-phb-test.o $(libqos-obj-y) tests/prom-env-test$(EXESUF): tests/prom-env-test.o $(libqos-obj-y) tests/rtas-test$(EXESUF): tests/rtas-test.o $(libqos-spapr-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/ahci-test$(EXESUF): tests/ahci-test.o $(libqos-pc-obj-y) qemu-img$(EXESUF) 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 @@ -791,52 +790,31 @@ 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/pca9552-test$(EXESUF): tests/pca9552-test.o $(libqos-omap-obj-y) tests/ds1338-test$(EXESUF): tests/ds1338-test.o $(libqos-imx-obj-y) +tests/microbit-test$(EXESUF): tests/microbit-test.o tests/m25p80-test$(EXESUF): tests/m25p80-test.o tests/i440fx-test$(EXESUF): tests/i440fx-test.o $(libqos-pc-obj-y) tests/q35-test$(EXESUF): tests/q35-test.o $(libqos-pc-obj-y) tests/fw_cfg-test$(EXESUF): tests/fw_cfg-test.o $(libqos-pc-obj-y) -tests/e1000-test$(EXESUF): tests/e1000-test.o -tests/e1000e-test$(EXESUF): tests/e1000e-test.o $(libqos-pc-obj-y) tests/rtl8139-test$(EXESUF): tests/rtl8139-test.o $(libqos-pc-obj-y) -tests/pcnet-test$(EXESUF): tests/pcnet-test.o tests/pnv-xscom-test$(EXESUF): tests/pnv-xscom-test.o -tests/eepro100-test$(EXESUF): tests/eepro100-test.o -tests/vmxnet3-test$(EXESUF): tests/vmxnet3-test.o -tests/ne2000-test$(EXESUF): tests/ne2000-test.o 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 $(libqos-virtio-obj-y) -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) $(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 $(libqos-virtio-obj-y) -tests/virtio-serial-test$(EXESUF): tests/virtio-serial-test.o $(libqos-virtio-obj-y) -tests/virtio-console-test$(EXESUF): tests/virtio-console-test.o $(libqos-virtio-obj-y) -tests/tpci200-test$(EXESUF): tests/tpci200-test.o +tests/virtio-ccw-test$(EXESUF): tests/virtio-ccw-test.o tests/display-vga-test$(EXESUF): tests/display-vga-test.o -tests/ipoctal232-test$(EXESUF): tests/ipoctal232-test.o tests/qom-test$(EXESUF): tests/qom-test.o tests/test-hmp$(EXESUF): tests/test-hmp.o tests/machine-none-test$(EXESUF): tests/machine-none-test.o -tests/drive_del-test$(EXESUF): tests/drive_del-test.o $(libqos-virtio-obj-y) -tests/qdev-monitor-test$(EXESUF): tests/qdev-monitor-test.o $(libqos-pc-obj-y) -tests/nvme-test$(EXESUF): tests/nvme-test.o +tests/device-plug-test$(EXESUF): tests/device-plug-test.o +tests/drive_del-test$(EXESUF): tests/drive_del-test.o tests/pvpanic-test$(EXESUF): tests/pvpanic-test.o tests/i82801b11-test$(EXESUF): tests/i82801b11-test.o -tests/ac97-test$(EXESUF): tests/ac97-test.o -tests/es1370-test$(EXESUF): tests/es1370-test.o tests/intel-hda-test$(EXESUF): tests/intel-hda-test.o tests/ioh3420-test$(EXESUF): tests/ioh3420-test.o -tests/usb-hcd-ohci-test$(EXESUF): tests/usb-hcd-ohci-test.o $(libqos-usb-obj-y) 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/cpu-plug-test$(EXESUF): tests/cpu-plug-test.o tests/migration-test$(EXESUF): tests/migration-test.o -tests/vhost-user-test$(EXESUF): tests/vhost-user-test.o $(test-util-obj-y) \ - $(qtest-obj-y) $(test-io-obj-y) $(libqos-virtio-obj-y) $(libqos-pc-obj-y) \ - $(chardev-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 $(test-util-obj-y) tests/test-keyval$(EXESUF): tests/test-keyval.o $(test-util-obj-y) $(test-qapi-obj-y) @@ -846,14 +824,12 @@ 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/test-x86-cpuid-compat$(EXESUF): tests/test-x86-cpuid-compat.o $(qtest-obj-y) tests/ivshmem-test$(EXESUF): tests/ivshmem-test.o contrib/ivshmem-server/ivshmem-server.o $(libqos-pc-obj-y) $(libqos-spapr-obj-y) -tests/megasas-test$(EXESUF): tests/megasas-test.o $(libqos-spapr-obj-y) $(libqos-pc-obj-y) tests/vhost-user-bridge$(EXESUF): tests/vhost-user-bridge.o $(test-util-obj-y) libvhost-user.a tests/test-uuid$(EXESUF): tests/test-uuid.o $(test-util-obj-y) tests/test-arm-mptimer$(EXESUF): tests/test-arm-mptimer.o tests/test-qapi-util$(EXESUF): tests/test-qapi-util.o $(test-util-obj-y) tests/numa-test$(EXESUF): tests/numa-test.o tests/vmgenid-test$(EXESUF): tests/vmgenid-test.o tests/boot-sector.o tests/acpi-utils.o -tests/sdhci-test$(EXESUF): tests/sdhci-test.o $(libqos-pc-obj-y) tests/cdrom-test$(EXESUF): tests/cdrom-test.o tests/boot-sector.o $(libqos-obj-y) tests/migration/stress$(EXESUF): tests/migration/stress.o @@ -868,10 +844,6 @@ tests/migration/initrd-stress.img: tests/migration/stress$(EXESUF) rm $(INITRD_WORK_DIR)/init rmdir $(INITRD_WORK_DIR) -ifeq ($(CONFIG_POSIX),y) -LIBS += -lutil -endif - # QTest rules TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGET_DIRS))) @@ -890,48 +862,205 @@ tests/test-qga$(EXESUF): qemu-ga$(EXESUF) tests/test-qga$(EXESUF): tests/test-qga.o $(qtest-obj-y) SPEED = quick -GTESTER_OPTIONS = -k $(if $(V),--verbose,-q) -GCOV_OPTIONS = -n $(if $(V),-f,) # gtester tests, possibly with verbose output +# do_test_tap runs all tests, even if some of them fail, while do_test_human +# stops at the first failure unless -k is given on the command line + +define do_test_human_k + $(quiet-@)rc=0; $(foreach COMMAND, $1, \ + $(call quiet-command-run, \ + export MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2; \ + $(COMMAND) -m=$(SPEED) -k --tap < /dev/null \ + | ./scripts/tap-driver.pl --test-name="$(notdir $(COMMAND))" $(if $(V),, --show-failures-only) \ + || rc=$$?;, "TEST", "$@: $(COMMAND)")) exit $$rc +endef +define do_test_human_no_k + $(foreach COMMAND, $1, \ + $(call quiet-command, \ + MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2 \ + $(COMMAND) -m=$(SPEED) -k --tap < /dev/null \ + | ./scripts/tap-driver.pl --test-name="$(notdir $(COMMAND))" $(if $(V),, --show-failures-only), \ + "TEST", "$@: $(COMMAND)") +) +endef +do_test_human = \ + $(if $(findstring k, $(MAKEFLAGS)), $(do_test_human_k), $(do_test_human_no_k)) + +define do_test_tap + $(call quiet-command, \ + { export MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} $2; \ + $(foreach COMMAND, $1, \ + $(COMMAND) -m=$(SPEED) -k --tap < /dev/null \ + | sed "s/^[a-z][a-z]* [0-9]* /&$(notdir $(COMMAND)) /" || true; ) } \ + | ./scripts/tap-merge.pl | tee "$@" \ + | ./scripts/tap-driver.pl $(if $(V),, --show-failures-only), \ + "TAP","$@") +endef .PHONY: $(patsubst %, check-qtest-%, $(QTEST_TARGETS)) $(patsubst %, check-qtest-%, $(QTEST_TARGETS)): check-qtest-%: subdir-%-softmmu $(check-qtest-y) - $(call quiet-command,QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ - QTEST_QEMU_IMG=qemu-img$(EXESUF) \ - MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \ - gtester $(GTESTER_OPTIONS) -m=$(SPEED) $(check-qtest-$*-y) $(check-qtest-generic-y),"GTESTER","$@") + $(call do_test_human,$(check-qtest-$*-y) $(check-qtest-generic-y), \ + QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ + QTEST_QEMU_IMG=qemu-img$(EXESUF)) -.PHONY: $(patsubst %, check-%, $(check-unit-y) $(check-speed-y)) -$(patsubst %, check-%, $(check-unit-y) $(check-speed-y)): check-%: % - $(call quiet-command, \ - MALLOC_PERTURB_=$${MALLOC_PERTURB_:-$$(( $${RANDOM:-0} % 255 + 1))} \ - gtester $(GTESTER_OPTIONS) -m=$(SPEED) $*,"GTESTER","$*") +check-unit: $(check-unit-y) + $(call do_test_human, $^) -# gtester tests with XML output +check-speed: $(check-speed-y) + $(call do_test_human, $^) -$(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) $(check-qtest-generic-y),"GTESTER","$@") +# gtester tests with TAP output -check-report-unit.xml: $(check-unit-y) - $(call quiet-command,gtester -q $(GTESTER_OPTIONS) -o $@ -m=$(SPEED) $^,"GTESTER","$@") +$(patsubst %, check-report-qtest-%.tap, $(QTEST_TARGETS)): check-report-qtest-%.tap: $(check-qtest-y) + $(call do_test_tap, $(check-qtest-$*-y) $(check-qtest-generic-y), \ + QTEST_QEMU_BINARY=$*-softmmu/qemu-system-$* \ + QTEST_QEMU_IMG=qemu-img$(EXESUF)) + +check-report-unit.tap: $(check-unit-y) + $(call do_test_tap,$^) # Reports and overall runs -check-report.xml: $(patsubst %,check-report-qtest-%.xml, $(QTEST_TARGETS)) check-report-unit.xml - $(call quiet-command,$(SRC_PATH)/scripts/gtester-cat $^ > $@,"GEN","$@") +check-report.tap: $(patsubst %,check-report-qtest-%.tap, $(QTEST_TARGETS)) check-report-unit.tap + $(call quiet-command,./scripts/tap-merge.py $^ > $@,"GEN","$@") -check-report.html: check-report.xml - $(call quiet-command,gtester-report $< > $@,"GEN","$@") +# FPU Emulation tests (aka softfloat) +# +# As we still have some places that need fixing the rules are a little +# more complex than they need to be and have to override some of the +# generic Makefile expansions. Once we are cleanly passing all +# the tests we can simplify the make syntax. + +FP_TEST_BIN=$(BUILD_DIR)/tests/fp/fp-test + +# the build dir is created by configure +.PHONY: $(FP_TEST_BIN) +$(FP_TEST_BIN): + $(call quiet-command, \ + $(MAKE) $(SUBDIR_MAKEFLAGS) -C $(dir $@) V="$(V)" $(notdir $@), \ + "BUILD", "$(notdir $@)") + +# The full test suite can take a bit of time, default to a quick run +# "-l 2 -r all" can take more than a day for some operations and is best +# run manually +FP_TL=-l 1 -r all + +# $1 = tests, $2 = description, $3 = test flags +test-softfloat = $(call quiet-command, \ + cd $(BUILD_DIR)/tests/fp && \ + ./fp-test -s $(if $3,$3,$(FP_TL)) $1 > $2.out 2>&1 || \ + (cat $2.out && exit 1;), \ + "FLOAT TEST", $2) + +# Conversion Routines: +# FIXME: i32_to_extF80 (broken), i64_to_extF80 (broken) +# ui32_to_f128 (not implemented), extF80_roundToInt (broken) +# +check-softfloat-conv: $(FP_TEST_BIN) + $(call test-softfloat, \ + i32_to_f16 i64_to_f16 \ + i32_to_f32 i64_to_f32 \ + i32_to_f64 i64_to_f64 \ + i32_to_f128 i64_to_f128, int-to-float) + $(call test-softfloat, \ + ui32_to_f16 ui64_to_f16 \ + ui32_to_f32 ui64_to_f32 \ + ui32_to_f64 ui64_to_f64 \ + ui64_to_f128, uint-to-float) + $(call test-softfloat, \ + f16_to_i32 f16_to_i32_r_minMag \ + f32_to_i32 f32_to_i32_r_minMag \ + f64_to_i32 f64_to_i32_r_minMag \ + extF80_to_i32 extF80_to_i32_r_minMag \ + f128_to_i32 f128_to_i32_r_minMag \ + f16_to_i64 f16_to_i64_r_minMag \ + f32_to_i64 f32_to_i64_r_minMag \ + f64_to_i64 f64_to_i64_r_minMag \ + extF80_to_i64 extF80_to_i64_r_minMag \ + f128_to_i64 f128_to_i64_r_minMag, \ + float-to-int) + $(call test-softfloat, \ + f16_to_ui32 f16_to_ui32_r_minMag \ + f32_to_ui32 f32_to_ui32_r_minMag \ + f64_to_ui32 f64_to_ui32_r_minMag \ + f128_to_ui32 f128_to_ui32_r_minMag \ + f16_to_ui64 f16_to_ui64_r_minMag \ + f32_to_ui64 f32_to_ui64_r_minMag \ + f64_to_ui64 f64_to_ui64_r_minMag \ + f128_to_ui64 f128_to_ui64_r_minMag, \ + float-to-uint) + $(call test-softfloat, \ + f16_roundToInt f32_roundToInt \ + f64_roundToInt f128_roundToInt, \ + round-to-integer) + +# Generic rule for all float operations +# +# Some patterns are overidden due to broken or missing tests. +# Hopefully these can be removed over time. + +check-softfloat-%: $(FP_TEST_BIN) + $(call test-softfloat, f16_$* f32_$* f64_$* extF80_$* f128_$*, $*) + +# Float Compare routines +SF_COMPARE_OPS=eq eq_signaling le le_quiet lt_quiet +SF_COMPARE_RULES=$(patsubst %,check-softfloat-%, $(SF_COMPARE_OPS)) + +# FIXME: extF80_le_quiet (broken) +check-softfloat-le_quiet: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_le_quiet f32_le_quiet f64_le_quiet \ + f128_le_quiet, \ + le_quiet) + +# FIXME: extF80_lt_quiet (broken) +check-softfloat-lt_quiet: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_lt_quiet f32_lt_quiet f64_lt_quiet \ + f128_lt_quiet, \ + lt_quiet) + +.PHONY: check-softfloat-compare +check-softfloat-compare: $(SF_COMPARE_RULES) + +# Math Operations + +# FIXME: extF80_mulAdd (missing) +check-softfloat-mulAdd: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_mulAdd f32_mulAdd f64_mulAdd f128_mulAdd, \ + mulAdd,-l 1) + +# FIXME: extF80_rem (broken) +check-softfloat-rem: $(FP_TEST_BIN) + $(call test-softfloat, \ + f16_rem f32_rem f64_rem f128_rem, \ + rem) + +SF_MATH_OPS=add sub mul mulAdd div rem sqrt +SF_MATH_RULES=$(patsubst %,check-softfloat-%, $(SF_MATH_OPS)) + +.PHONY: check-softfloat-ops +check-softfloat-ops: $(SF_MATH_RULES) + +# Finally a generic rule to test all of softfoat. If TCG isnt't +# enabled we define a null operation which skips the tests. + +.PHONY: check-softfloat +ifeq ($(CONFIG_TCG),y) +check-softfloat: check-softfloat-conv check-softfloat-compare check-softfloat-ops +else +check-softfloat: + $(call quiet-command, /bin/true, "FLOAT TEST", \ + "SKIPPED for non-TCG builds") +endif # Per guest TCG tests -LINUX_USER_TARGETS=$(filter %-linux-user,$(TARGET_DIRS)) -BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(LINUX_USER_TARGETS)) -CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(LINUX_USER_TARGETS)) -RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(LINUX_USER_TARGETS)) +BUILD_TCG_TARGET_RULES=$(patsubst %,build-tcg-tests-%, $(TARGET_DIRS)) +CLEAN_TCG_TARGET_RULES=$(patsubst %,clean-tcg-tests-%, $(TARGET_DIRS)) +RUN_TCG_TARGET_RULES=$(patsubst %,run-tcg-tests-%, $(TARGET_DIRS)) ifeq ($(HAVE_USER_DOCKER),y) # Probe for the Docker Builds needed for each build @@ -968,7 +1097,7 @@ clean-tcg: $(CLEAN_TCG_TARGET_RULES) QEMU_IOTESTS_HELPERS-$(call land,$(CONFIG_SOFTMMU),$(CONFIG_LINUX)) = tests/qemu-iotests/socket_scm_helper$(EXESUF) .PHONY: check-tests/qemu-iotests-quick.sh -check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) +check-tests/qemu-iotests-quick.sh: tests/qemu-iotests-quick.sh qemu-img$(EXESUF) qemu-io$(EXESUF) qemu-nbd$(EXESUF) $(QEMU_IOTESTS_HELPERS-y) $< .PHONY: $(patsubst %, check-%, $(check-qapi-schema-y)) @@ -994,19 +1123,57 @@ check-decodetree: ./check.sh "$(PYTHON)" "$(SRC_PATH)/scripts/decodetree.py", \ TEST, decodetree.py) +# Python venv for running tests + +.PHONY: check-venv check-acceptance + +TESTS_VENV_DIR=$(BUILD_DIR)/tests/venv +TESTS_VENV_REQ=$(SRC_PATH)/tests/requirements.txt +TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results +# Controls the output generated by Avocado when running tests. +# Any number of command separated loggers are accepted. For more +# information please refer to "avocado --help". +AVOCADO_SHOW=none + +ifneq ($(findstring v2,"v$(PYTHON_VERSION)"),v2) +$(TESTS_VENV_DIR): $(TESTS_VENV_REQ) + $(call quiet-command, \ + $(PYTHON) -m venv --system-site-packages $@, \ + VENV, $@) + $(call quiet-command, \ + $(TESTS_VENV_DIR)/bin/python -m pip -q install -r $(TESTS_VENV_REQ), \ + PIP, $(TESTS_VENV_REQ)) + $(call quiet-command, touch $@) +else +$(TESTS_VENV_DIR): + $(error "venv directory for tests requires Python 3") +endif + +$(TESTS_RESULTS_DIR): + $(call quiet-command, mkdir -p $@, \ + MKDIR, $@) + +check-venv: $(TESTS_VENV_DIR) + +check-acceptance: check-venv $(TESTS_RESULTS_DIR) + $(call quiet-command, \ + $(TESTS_VENV_DIR)/bin/python -m avocado \ + --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ + --failfast=on $(SRC_PATH)/tests/acceptance, \ + "AVOCADO", "tests/acceptance") + # Consolidated targets -.PHONY: check-qapi-schema check-qtest check-unit check check-clean +.PHONY: check-block check-qapi-schema check-qtest check-unit check check-clean check-qapi-schema: $(patsubst %,check-%, $(check-qapi-schema-y)) check-tests/qapi-schema/doc-good.texi check-qtest: $(patsubst %,check-qtest-%, $(QTEST_TARGETS)) -check-unit: $(patsubst %,check-%, $(check-unit-y)) -check-speed: $(patsubst %,check-%, $(check-speed-y)) check-block: $(patsubst %,check-%, $(check-block-y)) -check: check-qapi-schema check-unit check-qtest check-decodetree +check: check-qapi-schema check-unit check-softfloat check-qtest check-decodetree check-clean: rm -rf $(check-unit-y) tests/*.o $(QEMU_IOTESTS_HELPERS-y) rm -rf $(sort $(foreach target,$(SYSEMU_TARGET_LIST), $(check-qtest-$(target)-y)) $(check-qtest-generic-y)) rm -f tests/test-qapi-gen-timestamp + rm -rf $(TESTS_VENV_DIR) $(TESTS_RESULTS_DIR) clean: check-clean diff --git a/tests/ac97-test.c b/tests/ac97-test.c index e0d177bd9c..532fb1cc98 100644 --- a/tests/ac97-test.c +++ b/tests/ac97-test.c @@ -9,23 +9,48 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QAC97 QAC97; + +struct QAC97 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *ac97_get_driver(void *obj, const char *interface) { + QAC97 *ac97 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ac97->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *ac97_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QAC97 *ac97 = g_new0(QAC97, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ac97/nop", nop); - - qtest_start("-device AC97"); - ret = g_test_run(); - - qtest_end(); - - return ret; + qpci_device_init(&ac97->dev, bus, addr); + ac97->obj.get_driver = ac97_get_driver; + return &ac97->obj; } + +static void ac97_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("AC97", ac97_create); + qos_node_produces("AC97", "pci-device"); + qos_node_consumes("AC97", "pci-bus", &opts); +} + +libqos_init(ac97_register_nodes); diff --git a/tests/acceptance/avocado_qemu/__init__.py b/tests/acceptance/avocado_qemu/__init__.py index 1e54fd5932..a66ec72daa 100644 --- a/tests/acceptance/avocado_qemu/__init__.py +++ b/tests/acceptance/avocado_qemu/__init__.py @@ -10,12 +10,12 @@ import os import sys +import uuid import avocado -SRC_ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__))) -SRC_ROOT_DIR = os.path.abspath(os.path.dirname(SRC_ROOT_DIR)) -sys.path.append(os.path.join(SRC_ROOT_DIR, 'scripts')) +SRC_ROOT_DIR = os.path.join(os.path.dirname(__file__), '..', '..', '..') +sys.path.append(os.path.join(SRC_ROOT_DIR, 'python')) from qemu import QEMUMachine @@ -42,13 +42,29 @@ def pick_default_qemu_bin(): class Test(avocado.Test): def setUp(self): - self.vm = None + self._vms = {} self.qemu_bin = self.params.get('qemu_bin', default=pick_default_qemu_bin()) if self.qemu_bin is None: self.cancel("No QEMU binary defined or found in the source tree") - self.vm = QEMUMachine(self.qemu_bin) + + def _new_vm(self, *args): + vm = QEMUMachine(self.qemu_bin) + if args: + vm.add_args(*args) + return vm + + @property + def vm(self): + return self.get_vm(name='default') + + def get_vm(self, *args, name=None): + if not name: + name = str(uuid.uuid4()) + if self._vms.get(name) is None: + self._vms[name] = self._new_vm(*args) + return self._vms[name] def tearDown(self): - if self.vm is not None: - self.vm.shutdown() + for vm in self._vms.values(): + vm.shutdown() diff --git a/tests/acceptance/boot_linux_console.py b/tests/acceptance/boot_linux_console.py index 98324f7591..beeb1e59e8 100644 --- a/tests/acceptance/boot_linux_console.py +++ b/tests/acceptance/boot_linux_console.py @@ -18,7 +18,6 @@ class BootLinuxConsole(Test): Boots a x86_64 Linux kernel and checks that the console is operational and the kernel command line is properly passed from QEMU to the kernel - :avocado: enable :avocado: tags=x86_64 """ diff --git a/tests/acceptance/cpu_queries.py b/tests/acceptance/cpu_queries.py new file mode 100644 index 0000000000..e71edec39f --- /dev/null +++ b/tests/acceptance/cpu_queries.py @@ -0,0 +1,33 @@ +# Sanity check of query-cpu-* results +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# 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 logging + +from avocado_qemu import Test + +class QueryCPUModelExpansion(Test): + """ + Run query-cpu-model-expansion for each CPU model, and validate results + """ + + def test(self): + self.vm.set_machine('none') + self.vm.add_args('-S') + self.vm.launch() + + cpus = self.vm.command('query-cpu-definitions') + for c in cpus: + print(repr(c)) + self.assertNotIn('', c['unavailable-features'], c['name']) + + for c in cpus: + model = {'name': c['name']} + e = self.vm.command('query-cpu-model-expansion', model=model, type='full') + self.assertEquals(e['model']['name'], c['name']) diff --git a/tests/acceptance/linux_initrd.py b/tests/acceptance/linux_initrd.py new file mode 100644 index 0000000000..fbdb48e43f --- /dev/null +++ b/tests/acceptance/linux_initrd.py @@ -0,0 +1,84 @@ +# Linux initrd acceptance test. +# +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Wainer dos Santos Moschetta +# +# 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 logging +import tempfile +from avocado.utils.process import run + +from avocado_qemu import Test + + +class LinuxInitrd(Test): + """ + Checks QEMU evaluates correctly the initrd file passed as -initrd option. + + :avocado: tags=x86_64 + """ + + timeout = 300 + + def test_with_2gib_file_should_exit_error_msg_with_linux_v3_6(self): + """ + Pretends to boot QEMU with an initrd file with size of 2GiB + and expect it exits with error message. + Fedora-18 shipped with linux-3.6 which have not supported xloadflags + cannot support more than 2GiB initrd. + """ + kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora/li' + 'nux/releases/18/Fedora/x86_64/os/images/pxeboot/vmlinuz') + kernel_hash = '41464f68efe42b9991250bed86c7081d2ccdbb21' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + max_size = 2 * (1024 ** 3) - 1 + + with tempfile.NamedTemporaryFile() as initrd: + initrd.seek(max_size) + initrd.write(b'\0') + initrd.flush() + cmd = "%s -kernel %s -initrd %s -m 4096" % ( + self.qemu_bin, kernel_path, initrd.name) + res = run(cmd, ignore_status=True) + self.assertEqual(res.exit_status, 1) + expected_msg = r'.*initrd is too large.*max: \d+, need %s.*' % ( + max_size + 1) + self.assertRegex(res.stderr_text, expected_msg) + + def test_with_2gib_file_should_work_with_linux_v4_16(self): + """ + QEMU has supported up to 4 GiB initrd for recent kernel + Expect guest can reach 'Unpacking initramfs...' + """ + kernel_url = ('https://mirrors.kernel.org/fedora/releases/28/' + 'Everything/x86_64/os/images/pxeboot/vmlinuz') + kernel_hash = '238e083e114c48200f80d889f7e32eeb2793e02a' + kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) + max_size = 2 * (1024 ** 3) + 1 + + with tempfile.NamedTemporaryFile() as initrd: + initrd.seek(max_size) + initrd.write(b'\0') + initrd.flush() + + self.vm.set_machine('pc') + self.vm.set_console() + kernel_command_line = 'console=ttyS0' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-initrd', initrd.name, + '-m', '5120') + self.vm.launch() + console = self.vm.console_socket.makefile() + console_logger = logging.getLogger('console') + while True: + msg = console.readline() + console_logger.debug(msg.strip()) + if 'Unpacking initramfs...' in msg: + break + if 'Kernel panic - not syncing' in msg: + self.fail("Kernel panic reached") diff --git a/tests/acceptance/migration.py b/tests/acceptance/migration.py new file mode 100644 index 0000000000..6115cf6c24 --- /dev/null +++ b/tests/acceptance/migration.py @@ -0,0 +1,53 @@ +# Migration test +# +# Copyright (c) 2019 Red Hat, Inc. +# +# Authors: +# Cleber Rosa +# Caio Carrara +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + + +from avocado_qemu import Test + +from avocado.utils import network +from avocado.utils import wait + + +class Migration(Test): + """ + :avocado: enable + """ + + timeout = 10 + + @staticmethod + def migration_finished(vm): + return vm.command('query-migrate')['status'] in ('completed', 'failed') + + def _get_free_port(self): + port = network.find_free_port() + if port is None: + self.cancel('Failed to find a free port') + return port + + + def test_migration_with_tcp_localhost(self): + source_vm = self.get_vm() + dest_uri = 'tcp:localhost:%u' % self._get_free_port() + dest_vm = self.get_vm('-incoming', dest_uri) + dest_vm.launch() + source_vm.launch() + source_vm.qmp('migrate', uri=dest_uri) + wait.wait_for( + self.migration_finished, + timeout=self.timeout, + step=0.1, + args=(source_vm,) + ) + self.assertEqual(dest_vm.command('query-migrate')['status'], 'completed') + self.assertEqual(source_vm.command('query-migrate')['status'], 'completed') + self.assertEqual(dest_vm.command('query-status')['status'], 'running') + self.assertEqual(source_vm.command('query-status')['status'], 'postmigrate') diff --git a/tests/acceptance/version.py b/tests/acceptance/version.py index 13b0a7440d..67c2192c93 100644 --- a/tests/acceptance/version.py +++ b/tests/acceptance/version.py @@ -14,7 +14,6 @@ from avocado_qemu import Test class Version(Test): """ - :avocado: enable :avocado: tags=quick """ def test_qmp_human_info_version(self): diff --git a/tests/acceptance/virtio_version.py b/tests/acceptance/virtio_version.py new file mode 100644 index 0000000000..37fc01ea18 --- /dev/null +++ b/tests/acceptance/virtio_version.py @@ -0,0 +1,175 @@ +""" +Check compatibility of virtio device types +""" +# Copyright (c) 2018 Red Hat, Inc. +# +# Author: +# Eduardo Habkost +# +# 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 sys +import os + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import QEMUMachine +from avocado_qemu import Test + +# Virtio Device IDs: +VIRTIO_NET = 1 +VIRTIO_BLOCK = 2 +VIRTIO_CONSOLE = 3 +VIRTIO_RNG = 4 +VIRTIO_BALLOON = 5 +VIRTIO_RPMSG = 7 +VIRTIO_SCSI = 8 +VIRTIO_9P = 9 +VIRTIO_RPROC_SERIAL = 11 +VIRTIO_CAIF = 12 +VIRTIO_GPU = 16 +VIRTIO_INPUT = 18 +VIRTIO_VSOCK = 19 +VIRTIO_CRYPTO = 20 + +PCI_VENDOR_ID_REDHAT_QUMRANET = 0x1af4 + +# Device IDs for legacy/transitional devices: +PCI_LEGACY_DEVICE_IDS = { + VIRTIO_NET: 0x1000, + VIRTIO_BLOCK: 0x1001, + VIRTIO_BALLOON: 0x1002, + VIRTIO_CONSOLE: 0x1003, + VIRTIO_SCSI: 0x1004, + VIRTIO_RNG: 0x1005, + VIRTIO_9P: 0x1009, + VIRTIO_VSOCK: 0x1012, +} + +def pci_modern_device_id(virtio_devid): + return virtio_devid + 0x1040 + +def devtype_implements(vm, devtype, implements): + return devtype in [d['name'] for d in vm.command('qom-list-types', implements=implements)] + +def get_pci_interfaces(vm, devtype): + interfaces = ('pci-express-device', 'conventional-pci-device') + return [i for i in interfaces if devtype_implements(vm, devtype, i)] + +class VirtioVersionCheck(Test): + """ + Check if virtio-version-specific device types result in the + same device tree created by `disable-modern` and + `disable-legacy`. + + :avocado: tags=x86_64 + """ + + # just in case there are failures, show larger diff: + maxDiff = 4096 + + def run_device(self, devtype, opts=None, machine='pc'): + """ + Run QEMU with `-device DEVTYPE`, return device info from `query-pci` + """ + with QEMUMachine(self.qemu_bin) as vm: + vm.set_machine(machine) + if opts: + devtype += ',' + opts + vm.add_args('-device', '%s,id=devfortest' % (devtype)) + vm.add_args('-S') + vm.launch() + + pcibuses = vm.command('query-pci') + alldevs = [dev for bus in pcibuses for dev in bus['devices']] + devfortest = [dev for dev in alldevs + if dev['qdev_id'] == 'devfortest'] + return devfortest[0], get_pci_interfaces(vm, devtype) + + + def assert_devids(self, dev, devid, non_transitional=False): + self.assertEqual(dev['id']['vendor'], PCI_VENDOR_ID_REDHAT_QUMRANET) + self.assertEqual(dev['id']['device'], devid) + if non_transitional: + self.assertTrue(0x1040 <= dev['id']['device'] <= 0x107f) + self.assertGreaterEqual(dev['id']['subsystem'], 0x40) + + def check_all_variants(self, qemu_devtype, virtio_devid): + """Check if a virtio device type and its variants behave as expected""" + # Force modern mode: + dev_modern, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=on') + self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), + non_transitional=True) + + # -non-transitional device types should be 100% equivalent to + # ,disable-modern=off,disable-legacy=on + dev_1_0, nt_ifaces = self.run_device('%s-non-transitional' % (qemu_devtype)) + self.assertEqual(dev_modern, dev_1_0) + + # Force transitional mode: + dev_trans, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=off') + self.assert_devids(dev_trans, PCI_LEGACY_DEVICE_IDS[virtio_devid]) + + # Force legacy mode: + dev_legacy, _ = self.run_device(qemu_devtype, + 'disable-modern=on,disable-legacy=off') + self.assert_devids(dev_legacy, PCI_LEGACY_DEVICE_IDS[virtio_devid]) + + # No options: default to transitional on PC machine-type: + no_opts_pc, generic_ifaces = self.run_device(qemu_devtype) + self.assertEqual(dev_trans, no_opts_pc) + + #TODO: check if plugging on a PCI Express bus will make the + # device non-transitional + #no_opts_q35 = self.run_device(qemu_devtype, machine='q35') + #self.assertEqual(dev_modern, no_opts_q35) + + # -transitional device types should be 100% equivalent to + # ,disable-modern=off,disable-legacy=off + dev_trans, trans_ifaces = self.run_device('%s-transitional' % (qemu_devtype)) + self.assertEqual(dev_trans, dev_trans) + + # ensure the interface information is correct: + self.assertIn('conventional-pci-device', generic_ifaces) + self.assertIn('pci-express-device', generic_ifaces) + + self.assertIn('conventional-pci-device', nt_ifaces) + self.assertIn('pci-express-device', nt_ifaces) + + self.assertIn('conventional-pci-device', trans_ifaces) + self.assertNotIn('pci-express-device', trans_ifaces) + + + def test_conventional_devs(self): + self.check_all_variants('virtio-net-pci', VIRTIO_NET) + # virtio-blk requires 'driver' parameter + #self.check_all_variants('virtio-blk-pci', VIRTIO_BLOCK) + self.check_all_variants('virtio-serial-pci', VIRTIO_CONSOLE) + self.check_all_variants('virtio-rng-pci', VIRTIO_RNG) + self.check_all_variants('virtio-balloon-pci', VIRTIO_BALLOON) + self.check_all_variants('virtio-scsi-pci', VIRTIO_SCSI) + # virtio-9p requires 'fsdev' parameter + #self.check_all_variants('virtio-9p-pci', VIRTIO_9P) + + def check_modern_only(self, qemu_devtype, virtio_devid): + """Check if a modern-only virtio device type behaves as expected""" + # Force modern mode: + dev_modern, _ = self.run_device(qemu_devtype, + 'disable-modern=off,disable-legacy=on') + self.assert_devids(dev_modern, pci_modern_device_id(virtio_devid), + non_transitional=True) + + # No options: should be modern anyway + dev_no_opts, ifaces = self.run_device(qemu_devtype) + self.assertEqual(dev_modern, dev_no_opts) + + self.assertIn('conventional-pci-device', ifaces) + self.assertIn('pci-express-device', ifaces) + + def test_modern_only_devs(self): + self.check_modern_only('virtio-vga', VIRTIO_GPU) + self.check_modern_only('virtio-gpu-pci', VIRTIO_GPU) + self.check_modern_only('virtio-mouse-pci', VIRTIO_INPUT) + self.check_modern_only('virtio-tablet-pci', VIRTIO_INPUT) + self.check_modern_only('virtio-keyboard-pci', VIRTIO_INPUT) diff --git a/tests/acceptance/vnc.py b/tests/acceptance/vnc.py index b1ef9d71b1..064ceabcc1 100644 --- a/tests/acceptance/vnc.py +++ b/tests/acceptance/vnc.py @@ -13,7 +13,6 @@ from avocado_qemu import Test class Vnc(Test): """ - :avocado: enable :avocado: tags=vnc,quick """ def test_no_vnc(self): diff --git a/tests/acpi-utils.c b/tests/acpi-utils.c index 41dc1ea9b4..cc33b460ab 100644 --- a/tests/acpi-utils.c +++ b/tests/acpi-utils.c @@ -15,7 +15,6 @@ #include "qemu/osdep.h" #include #include "qemu-common.h" -#include "hw/smbios/smbios.h" #include "qemu/bitmap.h" #include "acpi-utils.h" #include "boot-sector.h" @@ -32,7 +31,7 @@ uint8_t acpi_calc_checksum(const uint8_t *data, int len) return sum; } -uint32_t acpi_find_rsdp_address(void) +uint32_t acpi_find_rsdp_address(QTestState *qts) { uint32_t off; @@ -42,7 +41,7 @@ uint32_t acpi_find_rsdp_address(void) int i; for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = readb(off + i); + sig[i] = qtest_readb(qts, off + i); } if (!memcmp(sig, "RSD PTR ", sizeof sig)) { @@ -52,14 +51,63 @@ uint32_t acpi_find_rsdp_address(void) return off; } -void acpi_parse_rsdp_table(uint32_t addr, AcpiRsdpDescriptor *rsdp_table) +uint64_t acpi_get_xsdt_address(uint8_t *rsdp_table) { - ACPI_READ_FIELD(rsdp_table->signature, addr); - ACPI_ASSERT_CMP64(rsdp_table->signature, "RSD PTR "); + uint64_t xsdt_physical_address; + uint8_t revision = rsdp_table[15 /* Revision offset */]; - ACPI_READ_FIELD(rsdp_table->checksum, addr); - ACPI_READ_ARRAY(rsdp_table->oem_id, addr); - ACPI_READ_FIELD(rsdp_table->revision, addr); - ACPI_READ_FIELD(rsdp_table->rsdt_physical_address, addr); - ACPI_READ_FIELD(rsdp_table->length, addr); + /* We must have revision 2 if we're looking for an XSDT pointer */ + g_assert(revision == 2); + + memcpy(&xsdt_physical_address, &rsdp_table[24 /* XsdtAddress offset */], 8); + return le64_to_cpu(xsdt_physical_address); +} + +void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table) +{ + uint8_t revision; + + /* Read mandatory revision 0 table data (20 bytes) first */ + qtest_memread(qts, addr, rsdp_table, 20); + revision = rsdp_table[15 /* Revision offset */]; + + switch (revision) { + case 0: /* ACPI 1.0 RSDP */ + break; + case 2: /* ACPI 2.0+ RSDP */ + /* Read the rest of the RSDP table */ + qtest_memread(qts, addr + 20, rsdp_table + 20, 16); + break; + default: + g_assert_not_reached(); + } + + ACPI_ASSERT_CMP64(*((uint64_t *)(rsdp_table)), "RSD PTR "); +} + +/** acpi_fetch_table + * load ACPI table at @addr_ptr offset pointer into buffer and return it in + * @aml, its length in @aml_len and check that signature/checksum matches + * actual one. + */ +void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len, + const uint8_t *addr_ptr, const char *sig, + bool verify_checksum) +{ + uint32_t addr, len; + + memcpy(&addr, addr_ptr , sizeof(addr)); + addr = le32_to_cpu(addr); + qtest_memread(qts, addr + 4, &len, 4); /* Length of ACPI table */ + *aml_len = le32_to_cpu(len); + *aml = g_malloc0(*aml_len); + /* get whole table */ + qtest_memread(qts, addr, *aml, *aml_len); + + if (sig) { + ACPI_ASSERT_CMP(**aml, sig); + } + if (verify_checksum) { + g_assert(!acpi_calc_checksum(*aml, *aml_len)); + } } diff --git a/tests/acpi-utils.h b/tests/acpi-utils.h index ac52abd0dd..ef388bbf12 100644 --- a/tests/acpi-utils.h +++ b/tests/acpi-utils.h @@ -13,14 +13,12 @@ #ifndef TEST_ACPI_UTILS_H #define TEST_ACPI_UTILS_H -#include "hw/acpi/acpi-defs.h" #include "libqtest.h" /* DSDT and SSDTs format */ typedef struct { - AcpiTableHeader header; - gchar *aml; /* aml bytecode from guest */ - gsize aml_len; + uint8_t *aml; /* aml bytecode from guest */ + uint32_t aml_len; gchar *aml_file; gchar *asl; /* asl code generated from aml */ gsize asl_len; @@ -28,36 +26,6 @@ typedef struct { bool tmp_files_retain; /* do not delete the temp asl/aml */ } AcpiSdtTable; -#define ACPI_READ_FIELD(field, addr) \ - do { \ - memread(addr, &field, sizeof(field)); \ - addr += sizeof(field); \ - } while (0) - -#define ACPI_READ_ARRAY_PTR(arr, length, addr) \ - do { \ - int idx; \ - for (idx = 0; idx < length; ++idx) { \ - ACPI_READ_FIELD(arr[idx], addr); \ - } \ - } while (0) - -#define ACPI_READ_ARRAY(arr, addr) \ - ACPI_READ_ARRAY_PTR(arr, sizeof(arr) / sizeof(arr[0]), addr) - -#define ACPI_READ_TABLE_HEADER(table, addr) \ - do { \ - ACPI_READ_FIELD((table)->signature, addr); \ - ACPI_READ_FIELD((table)->length, addr); \ - ACPI_READ_FIELD((table)->revision, addr); \ - ACPI_READ_FIELD((table)->checksum, addr); \ - ACPI_READ_ARRAY((table)->oem_id, addr); \ - ACPI_READ_ARRAY((table)->oem_table_id, addr); \ - ACPI_READ_FIELD((table)->oem_revision, addr); \ - ACPI_READ_ARRAY((table)->asl_compiler_id, addr); \ - ACPI_READ_FIELD((table)->asl_compiler_revision, addr); \ - } while (0) - #define ACPI_ASSERT_CMP(actual, expected) do { \ char ACPI_ASSERT_CMP_str[5] = {}; \ memcpy(ACPI_ASSERT_CMP_str, &actual, 4); \ @@ -70,18 +38,18 @@ typedef struct { g_assert_cmpstr(ACPI_ASSERT_CMP_str, ==, expected); \ } while (0) -#define ACPI_READ_GENERIC_ADDRESS(field, addr) \ - do { \ - ACPI_READ_FIELD((field).space_id, addr); \ - ACPI_READ_FIELD((field).bit_width, addr); \ - ACPI_READ_FIELD((field).bit_offset, addr); \ - ACPI_READ_FIELD((field).access_width, addr); \ - ACPI_READ_FIELD((field).address, addr); \ - } while (0) +#define ACPI_FOREACH_RSDT_ENTRY(table, table_len, entry_ptr, entry_size) \ + for (entry_ptr = table + 36 /* 1st Entry */; \ + entry_ptr < table + table_len; \ + entry_ptr += entry_size) uint8_t acpi_calc_checksum(const uint8_t *data, int len); -uint32_t acpi_find_rsdp_address(void); -void acpi_parse_rsdp_table(uint32_t addr, AcpiRsdpDescriptor *rsdp_table); +uint32_t acpi_find_rsdp_address(QTestState *qts); +uint64_t acpi_get_xsdt_address(uint8_t *rsdp_table); +void acpi_parse_rsdp_table(QTestState *qts, uint32_t addr, uint8_t *rsdp_table); +void acpi_fetch_table(QTestState *qts, uint8_t **aml, uint32_t *aml_len, + const uint8_t *addr_ptr, const char *sig, + bool verify_checksum); #endif /* TEST_ACPI_UTILS_H */ diff --git a/tests/ahci-test.c b/tests/ahci-test.c index 1a7b761304..086811e602 100644 --- a/tests/ahci-test.c +++ b/tests/ahci-test.c @@ -37,6 +37,9 @@ #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(s, ...) qobject_unref(qtest_qmp(s, __VA_ARGS__)) + /* Test images sizes in MB */ #define TEST_IMAGE_SIZE_MB_LARGE (200 * 1024) #define TEST_IMAGE_SIZE_MB_SMALL 64 @@ -158,8 +161,7 @@ static AHCIQState *ahci_vboot(const char *cli, va_list ap) s = g_new0(AHCIQState, 1); s->parent = qtest_pc_vboot(cli, ap); - global_qtest = s->parent->qts; - alloc_set_flags(s->parent->alloc, ALLOC_LEAK_ASSERT); + alloc_set_flags(&s->parent->alloc, ALLOC_LEAK_ASSERT); /* Verify that we have an AHCI device present. */ s->dev = get_ahci_device(s->parent->qts, &s->fingerprint); @@ -180,12 +182,12 @@ static AHCIQState *ahci_boot(const char *cli, ...) s = ahci_vboot(cli, ap); va_end(ap); } else { - cli = "-drive if=none,id=drive0,file=%s,cache=writeback,serial=%s" - ",format=%s" + cli = "-drive if=none,id=drive0,file=%s,cache=writeback,format=%s" " -M q35 " "-device ide-hd,drive=drive0 " + "-global ide-hd.serial=%s " "-global ide-hd.ver=%s"; - s = ahci_boot(cli, tmp_path, "testdisk", imgfmt, "version"); + s = ahci_boot(cli, tmp_path, imgfmt, "testdisk", "version"); } return s; @@ -198,7 +200,7 @@ static void ahci_shutdown(AHCIQState *ahci) { QOSState *qs = ahci->parent; - set_context(qs); + assert(!global_qtest); ahci_clean_mem(ahci); free_ahci_device(ahci->dev); g_free(ahci); @@ -871,15 +873,15 @@ static void ahci_test_io_rw_simple(AHCIQState *ahci, unsigned bufsize, /* Write some indicative pattern to our buffer. */ generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); - bufwrite(ptr, tx, bufsize); + qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize); /* Write this buffer to disk, then read it back to the DMA buffer. */ ahci_guest_io(ahci, port, write_cmd, ptr, bufsize, sector); - qmemset(ptr, 0x00, bufsize); + qtest_memset(ahci->parent->qts, ptr, 0x00, bufsize); ahci_guest_io(ahci, port, read_cmd, ptr, bufsize, sector); /*** Read back the Data ***/ - bufread(ptr, rx, bufsize); + qtest_bufread(ahci->parent->qts, ptr, rx, bufsize); g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); ahci_free(ahci, ptr); @@ -920,7 +922,7 @@ static void ahci_test_max(AHCIQState *ahci) } port = ahci_test_nondata(ahci, cmd); - memread(ahci->port[port].fb + 0x40, d2h, 0x20); + qtest_memread(ahci->parent->qts, ahci->port[port].fb + 0x40, d2h, 0x20); nsect = (uint64_t)d2h->lba_hi[2] << 40 | (uint64_t)d2h->lba_hi[1] << 32 | (uint64_t)d2h->lba_hi[0] << 24 | @@ -1036,9 +1038,9 @@ static void test_dma_fragmented(void) generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); /* Create a DMA buffer in guest memory, and write our pattern to it. */ - ptr = guest_alloc(ahci->parent->alloc, bufsize); + ptr = guest_alloc(&ahci->parent->alloc, bufsize); g_assert(ptr); - bufwrite(ptr, tx, bufsize); + qtest_bufwrite(ahci->parent->qts, ptr, tx, bufsize); cmd = ahci_command_create(CMD_WRITE_DMA); ahci_command_adjust(cmd, 0, ptr, bufsize, 32); @@ -1055,8 +1057,8 @@ static void test_dma_fragmented(void) ahci_command_free(cmd); /* Read back the guest's receive buffer into local memory */ - bufread(ptr, rx, bufsize); - guest_free(ahci->parent->alloc, ptr); + qtest_bufread(ahci->parent->qts, ptr, rx, bufsize); + guest_free(&ahci->parent->alloc, ptr); g_assert_cmphex(memcmp(tx, rx, bufsize), ==, 0); @@ -1166,8 +1168,6 @@ static void ahci_migrate_simple(uint8_t cmd_read, uint8_t cmd_write) "-drive if=ide,format=%s,file=%s " "-incoming %s", imgfmt, tmp_path, uri); - set_context(src->parent); - /* initialize */ px = ahci_port_select(src); ahci_port_clear(src, px); @@ -1235,7 +1235,7 @@ static void ahci_halted_io_test(uint8_t cmd_read, uint8_t cmd_write) generate_pattern(tx, bufsize, AHCI_SECTOR_SIZE); ptr = ahci_alloc(ahci, bufsize); g_assert(ptr); - memwrite(ptr, tx, bufsize); + qtest_memwrite(ahci->parent->qts, ptr, tx, bufsize); /* Attempt to write (and fail) */ cmd = ahci_guest_io_halt(ahci, port, cmd_write, @@ -1301,8 +1301,6 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) "-incoming %s", tmp_path, imgfmt, uri); - set_context(src->parent); - /* Initialize and prepare */ port = ahci_port_select(src); ahci_port_clear(src, port); @@ -1311,7 +1309,7 @@ static void ahci_migrate_halted_io(uint8_t cmd_read, uint8_t cmd_write) /* create DMA source buffer and write pattern */ ptr = ahci_alloc(src, bufsize); g_assert(ptr); - memwrite(ptr, tx, bufsize); + qtest_memwrite(src->parent->qts, ptr, tx, bufsize); /* Write, trigger the VM to stop, migrate, then resume. */ cmd = ahci_guest_io_halt(src, port, cmd_write, @@ -1352,7 +1350,6 @@ static void test_flush_migrate(void) AHCIQState *src, *dst; AHCICommand *cmd; uint8_t px; - const char *s; char *uri = g_strdup_printf("unix:%s", mig_socket); prepare_blkdebug_script(debug_path, "flush_to_disk"); @@ -1370,8 +1367,6 @@ static void test_flush_migrate(void) "-device ide-hd,drive=drive0 " "-incoming %s", tmp_path, imgfmt, uri); - set_context(src->parent); - px = ahci_port_select(src); ahci_port_clear(src, px); @@ -1382,15 +1377,14 @@ static void test_flush_migrate(void) cmd = ahci_command_create(CMD_FLUSH_CACHE); ahci_command_commit(src, cmd, px); ahci_command_issue_async(src, cmd); - qmp_eventwait("STOP"); + qtest_qmp_eventwait(src->parent->qts, "STOP"); /* Migrate over */ ahci_migrate(src, dst, uri); /* Complete the command */ - s = "{'execute':'cont' }"; - qmp_async(s); - qmp_eventwait("RESUME"); + qtest_qmp_send(dst->parent->qts, "{'execute':'cont' }"); + qtest_qmp_eventwait(dst->parent->qts, "RESUME"); ahci_command_wait(dst, cmd); ahci_command_verify(dst, cmd); @@ -1482,7 +1476,7 @@ static int ahci_cb_cmp_buff(AHCIQState *ahci, AHCICommand *cmd, } rx = g_malloc0(opts->size); - bufread(opts->buffer, rx, opts->size); + qtest_bufread(ahci->parent->qts, opts->buffer, rx, opts->size); g_assert_cmphex(memcmp(tx, rx, opts->size), ==, 0); g_free(rx); @@ -1557,9 +1551,10 @@ static void test_atapi_bcl(void) } -static void atapi_wait_tray(bool open) +static void atapi_wait_tray(AHCIQState *ahci, bool open) { - QDict *rsp = qmp_eventwait_ref("DEVICE_TRAY_MOVED"); + QDict *rsp = qtest_qmp_eventwait_ref(ahci->parent->qts, + "DEVICE_TRAY_MOVED"); QDict *data = qdict_get_qdict(rsp, "data"); if (open) { g_assert(qdict_get_bool(data, "tray-open")); @@ -1586,43 +1581,46 @@ static void test_atapi_tray(void) port = ahci_port_select(ahci); ahci_atapi_eject(ahci, port); - atapi_wait_tray(true); + atapi_wait_tray(ahci, true); ahci_atapi_load(ahci, port); - atapi_wait_tray(false); + atapi_wait_tray(ahci, false); /* Remove media */ - qmp_async("{'execute': 'blockdev-open-tray', " - "'arguments': {'id': 'cd0'}}"); - atapi_wait_tray(true); - rsp = qmp_receive(); + qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-open-tray', " + "'arguments': {'id': 'cd0'}}"); + atapi_wait_tray(ahci, true); + rsp = qtest_qmp_receive(ahci->parent->qts); qobject_unref(rsp); - qmp_discard_response("{'execute': 'blockdev-remove-medium', " + qmp_discard_response(ahci->parent->qts, + "{'execute': 'blockdev-remove-medium', " "'arguments': {'id': 'cd0'}}"); /* Test the tray without a medium */ ahci_atapi_load(ahci, port); - atapi_wait_tray(false); + atapi_wait_tray(ahci, false); ahci_atapi_eject(ahci, port); - atapi_wait_tray(true); + atapi_wait_tray(ahci, true); /* Re-insert media */ - qmp_discard_response("{'execute': 'blockdev-add', " - "'arguments': {'node-name': 'node0', " + qmp_discard_response(ahci->parent->qts, + "{'execute': 'blockdev-add', " + "'arguments': {'node-name': 'node0', " "'driver': 'raw', " "'file': { 'driver': 'file', " "'filename': %s }}}", iso); - qmp_discard_response("{'execute': 'blockdev-insert-medium'," - "'arguments': { 'id': 'cd0', " + qmp_discard_response(ahci->parent->qts, + "{'execute': 'blockdev-insert-medium'," + "'arguments': { 'id': 'cd0', " "'node-name': 'node0' }}"); /* Again, the event shows up first */ - qmp_async("{'execute': 'blockdev-close-tray', " - "'arguments': {'id': 'cd0'}}"); - atapi_wait_tray(false); - rsp = qmp_receive(); + qtest_qmp_send(ahci->parent->qts, "{'execute': 'blockdev-close-tray', " + "'arguments': {'id': 'cd0'}}"); + atapi_wait_tray(ahci, false); + rsp = qtest_qmp_receive(ahci->parent->qts); qobject_unref(rsp); /* Now, to convince ATAPI we understand the media has changed... */ @@ -1642,10 +1640,10 @@ static void test_atapi_tray(void) /* Final tray test. */ ahci_atapi_eject(ahci, port); - atapi_wait_tray(true); + atapi_wait_tray(ahci, true); ahci_atapi_load(ahci, port); - atapi_wait_tray(false); + atapi_wait_tray(ahci, false); /* Cleanup */ g_free(tx); diff --git a/tests/atomic64-bench.c b/tests/atomic64-bench.c new file mode 100644 index 0000000000..121a8c14f4 --- /dev/null +++ b/tests/atomic64-bench.c @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/host-utils.h" +#include "qemu/processor.h" + +struct thread_info { + uint64_t r; + uint64_t accesses; +} QEMU_ALIGNED(64); + +struct count { + int64_t i64; +} QEMU_ALIGNED(64); + +static QemuThread *threads; +static struct thread_info *th_info; +static unsigned int n_threads = 1; +static unsigned int n_ready_threads; +static struct count *counts; +static unsigned int duration = 1; +static unsigned int range = 1024; +static bool test_start; +static bool test_stop; + +static const char commands_string[] = + " -d = duration in seconds\n" + " -n = number of threads\n" + " -r = range (will be rounded up to pow2)"; + +static void usage_complete(char *argv[]) +{ + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n%s\n", commands_string); +} + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void *thread_func(void *arg) +{ + struct thread_info *info = arg; + + atomic_inc(&n_ready_threads); + while (!atomic_read(&test_start)) { + cpu_relax(); + } + + while (!atomic_read(&test_stop)) { + unsigned int index; + + info->r = xorshift64star(info->r); + index = info->r & (range - 1); + atomic_read_i64(&counts[index].i64); + info->accesses++; + } + return NULL; +} + +static void run_test(void) +{ + unsigned int i; + + while (atomic_read(&n_ready_threads) != n_threads) { + cpu_relax(); + } + + atomic_set(&test_start, true); + g_usleep(duration * G_USEC_PER_SEC); + atomic_set(&test_stop, true); + + for (i = 0; i < n_threads; i++) { + qemu_thread_join(&threads[i]); + } +} + +static void create_threads(void) +{ + unsigned int i; + + threads = g_new(QemuThread, n_threads); + th_info = g_new(struct thread_info, n_threads); + counts = g_malloc0_n(range, sizeof(*counts)); + + for (i = 0; i < n_threads; i++) { + struct thread_info *info = &th_info[i]; + + info->r = (i + 1) ^ time(NULL); + info->accesses = 0; + qemu_thread_create(&threads[i], NULL, thread_func, info, + QEMU_THREAD_JOINABLE); + } +} + +static void pr_params(void) +{ + printf("Parameters:\n"); + printf(" # of threads: %u\n", n_threads); + printf(" duration: %u\n", duration); + printf(" ops' range: %u\n", range); +} + +static void pr_stats(void) +{ + unsigned long long val = 0; + double tx; + int i; + + for (i = 0; i < n_threads; i++) { + val += th_info[i].accesses; + } + tx = val / duration / 1e6; + + printf("Results:\n"); + printf("Duration: %u s\n", duration); + printf(" Throughput: %.2f Mops/s\n", tx); + printf(" Throughput/thread: %.2f Mops/s/thread\n", tx / n_threads); +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + + for (;;) { + c = getopt(argc, argv, "hd:n:r:"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argv); + exit(0); + case 'd': + duration = atoi(optarg); + break; + case 'n': + n_threads = atoi(optarg); + break; + case 'r': + range = pow2ceil(atoi(optarg)); + break; + } + } +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + pr_params(); + create_threads(); + run_test(); + pr_stats(); + return 0; +} diff --git a/tests/atomic_add-bench.c b/tests/atomic_add-bench.c index f96d448f77..5666f6bbff 100644 --- a/tests/atomic_add-bench.c +++ b/tests/atomic_add-bench.c @@ -26,6 +26,7 @@ static bool test_stop; static const char commands_string[] = " -n = number of threads\n" " -m = use mutexes instead of atomic increments\n" + " -p = enable sync profiler\n" " -d = duration in seconds\n" " -r = range (will be rounded up to pow2)"; @@ -75,16 +76,14 @@ static void *thread_func(void *arg) static void run_test(void) { - unsigned int remaining; unsigned int i; while (atomic_read(&n_ready_threads) != n_threads) { cpu_relax(); } + atomic_set(&test_start, true); - do { - remaining = sleep(duration); - } while (remaining); + g_usleep(duration * G_USEC_PER_SEC); atomic_set(&test_stop, true); for (i = 0; i < n_threads; i++) { @@ -143,7 +142,7 @@ static void parse_args(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "hd:n:mr:"); + c = getopt(argc, argv, "hd:n:mpr:"); if (c < 0) { break; } @@ -160,6 +159,9 @@ static void parse_args(int argc, char *argv[]) case 'm': use_mutex = true; break; + case 'p': + qsp_enable(); + break; case 'r': range = pow2ceil(atoi(optarg)); break; diff --git a/tests/benchmark-crypto-cipher.c b/tests/benchmark-crypto-cipher.c index f5a0d0bc32..67fdf8c31d 100644 --- a/tests/benchmark-crypto-cipher.c +++ b/tests/benchmark-crypto-cipher.c @@ -15,17 +15,27 @@ #include "crypto/init.h" #include "crypto/cipher.h" -static void test_cipher_speed(const void *opaque) +static void test_cipher_speed(size_t chunk_size, + QCryptoCipherMode mode, + QCryptoCipherAlgorithm alg) { QCryptoCipher *cipher; Error *err = NULL; double total = 0.0; - size_t chunk_size = (size_t)opaque; uint8_t *key = NULL, *iv = NULL; uint8_t *plaintext = NULL, *ciphertext = NULL; - size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALG_AES_128); - size_t niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128, - QCRYPTO_CIPHER_MODE_CBC); + size_t nkey; + size_t niv; + + if (!qcrypto_cipher_supports(alg, mode)) { + return; + } + + nkey = qcrypto_cipher_get_key_len(alg); + niv = qcrypto_cipher_get_iv_len(alg, mode); + if (mode == QCRYPTO_CIPHER_MODE_XTS) { + nkey *= 2; + } key = g_new0(uint8_t, nkey); memset(key, g_test_rand_int(), nkey); @@ -38,14 +48,14 @@ static void test_cipher_speed(const void *opaque) plaintext = g_new0(uint8_t, chunk_size); memset(plaintext, g_test_rand_int(), chunk_size); - cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128, - QCRYPTO_CIPHER_MODE_CBC, + cipher = qcrypto_cipher_new(alg, mode, key, nkey, &err); g_assert(cipher != NULL); - g_assert(qcrypto_cipher_setiv(cipher, - iv, niv, - &err) == 0); + if (mode != QCRYPTO_CIPHER_MODE_ECB) + g_assert(qcrypto_cipher_setiv(cipher, + iv, niv, + &err) == 0); g_test_timer_start(); do { @@ -55,13 +65,26 @@ static void test_cipher_speed(const void *opaque) chunk_size, &err) == 0); total += chunk_size; - } while (g_test_timer_elapsed() < 5.0); + } while (g_test_timer_elapsed() < 1.0); total /= MiB; - g_print("cbc(aes128): "); - g_print("Testing chunk_size %zu bytes ", chunk_size); - g_print("done: %.2f MB in %.2f secs: ", total, g_test_timer_last()); - g_print("%.2f MB/sec\n", total / g_test_timer_last()); + g_print("Enc chunk %zu bytes ", chunk_size); + g_print("%.2f MB/sec ", total / g_test_timer_last()); + + total = 0.0; + g_test_timer_start(); + do { + g_assert(qcrypto_cipher_decrypt(cipher, + plaintext, + ciphertext, + chunk_size, + &err) == 0); + total += chunk_size; + } while (g_test_timer_elapsed() < 1.0); + + total /= MiB; + g_print("Dec chunk %zu bytes ", chunk_size); + g_print("%.2f MB/sec ", total / g_test_timer_last()); qcrypto_cipher_free(cipher); g_free(plaintext); @@ -70,19 +93,99 @@ static void test_cipher_speed(const void *opaque) g_free(key); } + +static void test_cipher_speed_ecb_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_ECB, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_ecb_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_ECB, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_cbc_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CBC, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_cbc_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CBC, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_ctr_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CTR, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_ctr_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_CTR, + QCRYPTO_CIPHER_ALG_AES_256); +} + +static void test_cipher_speed_xts_aes_128(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_XTS, + QCRYPTO_CIPHER_ALG_AES_128); +} + +static void test_cipher_speed_xts_aes_256(const void *opaque) +{ + size_t chunk_size = (size_t)opaque; + test_cipher_speed(chunk_size, + QCRYPTO_CIPHER_MODE_XTS, + QCRYPTO_CIPHER_ALG_AES_256); +} + + int main(int argc, char **argv) { - size_t i; - char name[64]; - g_test_init(&argc, &argv, NULL); g_assert(qcrypto_init(NULL) == 0); - for (i = 512; i <= 64 * KiB; i *= 2) { - memset(name, 0 , sizeof(name)); - snprintf(name, sizeof(name), "/crypto/cipher/speed-%zu", i); - g_test_add_data_func(name, (void *)i, test_cipher_speed); - } +#define ADD_TEST(mode, cipher, keysize, chunk) \ + g_test_add_data_func( \ + "/crypto/cipher/" #mode "-" #cipher "-" #keysize "/chunk-" #chunk, \ + (void *)chunk, \ + test_cipher_speed_ ## mode ## _ ## cipher ## _ ## keysize) + +#define ADD_TESTS(chunk) \ + do { \ + ADD_TEST(ecb, aes, 128, chunk); \ + ADD_TEST(ecb, aes, 256, chunk); \ + ADD_TEST(cbc, aes, 128, chunk); \ + ADD_TEST(cbc, aes, 256, chunk); \ + ADD_TEST(ctr, aes, 128, chunk); \ + ADD_TEST(ctr, aes, 256, chunk); \ + ADD_TEST(xts, aes, 128, chunk); \ + ADD_TEST(xts, aes, 256, chunk); \ + } while (0) + + ADD_TESTS(512); + ADD_TESTS(4096); + ADD_TESTS(16384); + ADD_TESTS(65536); return g_test_run(); } diff --git a/tests/bios-tables-test.c b/tests/bios-tables-test.c index 4e24930c4b..a506dcbb29 100644 --- a/tests/bios-tables-test.c +++ b/tests/bios-tables-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include #include "qemu-common.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "qemu/bitmap.h" #include "acpi-utils.h" #include "boot-sector.h" @@ -27,50 +27,51 @@ typedef struct { const char *machine; const char *variant; uint32_t rsdp_addr; - AcpiRsdpDescriptor rsdp_table; - AcpiRsdtDescriptorRev1 rsdt_table; - uint32_t dsdt_addr; - uint32_t facs_addr; - AcpiFacsDescriptorRev1 facs_table; - uint32_t *rsdt_tables_addr; - int rsdt_tables_nr; + uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */]; GArray *tables; uint32_t smbios_ep_addr; struct smbios_21_entry_point smbios_ep_table; uint8_t *required_struct_types; int required_struct_types_len; + QTestState *qts; } test_data; static char disk[] = "tests/acpi-test-disk-XXXXXX"; -static const char *data_dir = "tests/acpi-test-data"; +static const char *data_dir = "tests/data/acpi"; #ifdef CONFIG_IASL static const char *iasl = stringify(CONFIG_IASL); #else static const char *iasl; #endif +static bool compare_signature(const AcpiSdtTable *sdt, const char *signature) +{ + return !memcmp(sdt->aml, signature, 4); +} + +static void cleanup_table_descriptor(AcpiSdtTable *table) +{ + g_free(table->aml); + if (table->aml_file && + !table->tmp_files_retain && + g_strstr_len(table->aml_file, -1, "aml-")) { + unlink(table->aml_file); + } + g_free(table->aml_file); + g_free(table->asl); + if (table->asl_file && + !table->tmp_files_retain) { + unlink(table->asl_file); + } + g_free(table->asl_file); +} + static void free_test_data(test_data *data) { - AcpiSdtTable *temp; int i; - g_free(data->rsdt_tables_addr); - for (i = 0; i < data->tables->len; ++i) { - temp = &g_array_index(data->tables, AcpiSdtTable, i); - g_free(temp->aml); - if (temp->aml_file && - !temp->tmp_files_retain && - g_strstr_len(temp->aml_file, -1, "aml-")) { - unlink(temp->aml_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); + cleanup_table_descriptor(&g_array_index(data->tables, AcpiSdtTable, i)); } g_array_free(data->tables, true); @@ -78,172 +79,83 @@ static void free_test_data(test_data *data) static void test_acpi_rsdp_address(test_data *data) { - uint32_t off = acpi_find_rsdp_address(); + uint32_t off = acpi_find_rsdp_address(data->qts); g_assert_cmphex(off, <, 0x100000); data->rsdp_addr = off; } static void test_acpi_rsdp_table(test_data *data) { - AcpiRsdpDescriptor *rsdp_table = &data->rsdp_table; + uint8_t *rsdp_table = data->rsdp_table, revision; uint32_t addr = data->rsdp_addr; - acpi_parse_rsdp_table(addr, rsdp_table); + acpi_parse_rsdp_table(data->qts, addr, rsdp_table); + revision = rsdp_table[15 /* Revision offset */]; - /* rsdp checksum is not for the whole table, but for the first 20 bytes */ - g_assert(!acpi_calc_checksum((uint8_t *)rsdp_table, 20)); + switch (revision) { + case 0: /* ACPI 1.0 RSDP */ + /* With rev 1, checksum is only for the first 20 bytes */ + g_assert(!acpi_calc_checksum(rsdp_table, 20)); + break; + case 2: /* ACPI 2.0+ RSDP */ + /* With revision 2, we have 2 checksums */ + g_assert(!acpi_calc_checksum(rsdp_table, 20)); + g_assert(!acpi_calc_checksum(rsdp_table, 36)); + break; + default: + g_assert_not_reached(); + } } static void test_acpi_rsdt_table(test_data *data) { - AcpiRsdtDescriptorRev1 *rsdt_table = &data->rsdt_table; - uint32_t addr = le32_to_cpu(data->rsdp_table.rsdt_physical_address); - uint32_t *tables; - int tables_nr; - uint8_t checksum; - uint32_t rsdt_table_length; + AcpiSdtTable rsdt = {}; + uint8_t *ent; - /* read the header */ - ACPI_READ_TABLE_HEADER(rsdt_table, addr); - ACPI_ASSERT_CMP(rsdt_table->signature, "RSDT"); + /* read RSDT table */ + acpi_fetch_table(data->qts, &rsdt.aml, &rsdt.aml_len, + &data->rsdp_table[16 /* RsdtAddress */], "RSDT", true); - rsdt_table_length = le32_to_cpu(rsdt_table->length); - - /* compute the table entries in rsdt */ - tables_nr = (rsdt_table_length - sizeof(AcpiRsdtDescriptorRev1)) / - sizeof(uint32_t); - g_assert(tables_nr > 0); - - /* get the addresses of the tables pointed by rsdt */ - tables = g_new0(uint32_t, tables_nr); - ACPI_READ_ARRAY_PTR(tables, tables_nr, addr); - - checksum = acpi_calc_checksum((uint8_t *)rsdt_table, rsdt_table_length) + - acpi_calc_checksum((uint8_t *)tables, - tables_nr * sizeof(uint32_t)); - g_assert(!checksum); - - /* SSDT tables after FADT */ - data->rsdt_tables_addr = tables; - data->rsdt_tables_nr = tables_nr; -} - -static void fadt_fetch_facs_and_dsdt_ptrs(test_data *data) -{ - uint32_t addr; - AcpiTableHeader hdr; - - /* FADT table comes first */ - addr = le32_to_cpu(data->rsdt_tables_addr[0]); - ACPI_READ_TABLE_HEADER(&hdr, addr); - ACPI_ASSERT_CMP(hdr.signature, "FACP"); - - ACPI_READ_FIELD(data->facs_addr, addr); - ACPI_READ_FIELD(data->dsdt_addr, addr); -} - -static void sanitize_fadt_ptrs(test_data *data) -{ - /* fixup pointers in FADT */ - int i; - - for (i = 0; i < data->tables->len; i++) { - AcpiSdtTable *sdt = &g_array_index(data->tables, AcpiSdtTable, i); - - if (memcmp(&sdt->header.signature, "FACP", 4)) { - continue; - } - - /* check original FADT checksum before sanitizing table */ - g_assert(!(uint8_t)( - acpi_calc_checksum((uint8_t *)sdt, sizeof(AcpiTableHeader)) + - acpi_calc_checksum((uint8_t *)sdt->aml, sdt->aml_len) - )); - - /* sdt->aml field offset := spec offset - header size */ - memset(sdt->aml + 0, 0, 4); /* sanitize FIRMWARE_CTRL(36) ptr */ - memset(sdt->aml + 4, 0, 4); /* sanitize DSDT(40) ptr */ - if (sdt->header.revision >= 3) { - memset(sdt->aml + 96, 0, 8); /* sanitize X_FIRMWARE_CTRL(132) ptr */ - memset(sdt->aml + 104, 0, 8); /* sanitize X_DSDT(140) ptr */ - } - - /* update checksum */ - sdt->header.checksum = 0; - sdt->header.checksum -= - acpi_calc_checksum((uint8_t *)sdt, sizeof(AcpiTableHeader)) + - acpi_calc_checksum((uint8_t *)sdt->aml, sdt->aml_len); - break; - } -} - -static void test_acpi_facs_table(test_data *data) -{ - AcpiFacsDescriptorRev1 *facs_table = &data->facs_table; - uint32_t addr = le32_to_cpu(data->facs_addr); - - ACPI_READ_FIELD(facs_table->signature, addr); - ACPI_READ_FIELD(facs_table->length, addr); - ACPI_READ_FIELD(facs_table->hardware_signature, addr); - ACPI_READ_FIELD(facs_table->firmware_waking_vector, addr); - ACPI_READ_FIELD(facs_table->global_lock, addr); - ACPI_READ_FIELD(facs_table->flags, addr); - ACPI_READ_ARRAY(facs_table->resverved3, addr); - - ACPI_ASSERT_CMP(facs_table->signature, "FACS"); -} - -/** fetch_table - * load ACPI table at @addr into table descriptor @sdt_table - * and check that header checksum matches actual one. - */ -static void fetch_table(AcpiSdtTable *sdt_table, uint32_t addr) -{ - uint8_t checksum; - - memset(sdt_table, 0, sizeof(*sdt_table)); - ACPI_READ_TABLE_HEADER(&sdt_table->header, addr); - - sdt_table->aml_len = le32_to_cpu(sdt_table->header.length) - - sizeof(AcpiTableHeader); - sdt_table->aml = g_malloc0(sdt_table->aml_len); - ACPI_READ_ARRAY_PTR(sdt_table->aml, sdt_table->aml_len, addr); - - checksum = acpi_calc_checksum((uint8_t *)sdt_table, - sizeof(AcpiTableHeader)) + - acpi_calc_checksum((uint8_t *)sdt_table->aml, - sdt_table->aml_len); - g_assert(!checksum); -} - -static void test_acpi_dsdt_table(test_data *data) -{ - AcpiSdtTable dsdt_table; - uint32_t addr = le32_to_cpu(data->dsdt_addr); - - fetch_table(&dsdt_table, addr); - ACPI_ASSERT_CMP(dsdt_table.header.signature, "DSDT"); - - /* Since DSDT isn't in RSDT, add DSDT to ASL test tables list manually */ - g_array_append_val(data->tables, dsdt_table); -} - -/* Load all tables and add to test list directly RSDT referenced tables */ -static void fetch_rsdt_referenced_tables(test_data *data) -{ - int tables_nr = data->rsdt_tables_nr; - int i; - - for (i = 0; i < tables_nr; i++) { - AcpiSdtTable ssdt_table; - uint32_t addr; - - addr = le32_to_cpu(data->rsdt_tables_addr[i]); - fetch_table(&ssdt_table, addr); + /* Load all tables and add to test list directly RSDT referenced tables */ + ACPI_FOREACH_RSDT_ENTRY(rsdt.aml, rsdt.aml_len, ent, 4 /* Entry size */) { + AcpiSdtTable ssdt_table = {}; + acpi_fetch_table(data->qts, &ssdt_table.aml, &ssdt_table.aml_len, ent, + NULL, true); /* Add table to ASL test tables list */ g_array_append_val(data->tables, ssdt_table); } + cleanup_table_descriptor(&rsdt); +} + +static void test_acpi_fadt_table(test_data *data) +{ + /* FADT table is 1st */ + AcpiSdtTable table = g_array_index(data->tables, typeof(table), 0); + uint8_t *fadt_aml = table.aml; + uint32_t fadt_len = table.aml_len; + + g_assert(compare_signature(&table, "FACP")); + + /* Since DSDT/FACS isn't in RSDT, add them to ASL test list manually */ + acpi_fetch_table(data->qts, &table.aml, &table.aml_len, + fadt_aml + 36 /* FIRMWARE_CTRL */, "FACS", false); + g_array_append_val(data->tables, table); + + acpi_fetch_table(data->qts, &table.aml, &table.aml_len, + fadt_aml + 40 /* DSDT */, "DSDT", true); + g_array_append_val(data->tables, table); + + memset(fadt_aml + 36, 0, 4); /* sanitize FIRMWARE_CTRL ptr */ + memset(fadt_aml + 40, 0, 4); /* sanitize DSDT ptr */ + if (fadt_aml[8 /* FADT Major Version */] >= 3) { + memset(fadt_aml + 132, 0, 8); /* sanitize X_FIRMWARE_CTRL ptr */ + memset(fadt_aml + 140, 0, 8); /* sanitize X_DSDT ptr */ + } + + /* update checksum */ + fadt_aml[9 /* Checksum */] = 0; + fadt_aml[9 /* Checksum */] -= acpi_calc_checksum(fadt_aml, fadt_len); } static void dump_aml_files(test_data *data, bool rebuild) @@ -262,7 +174,7 @@ static void dump_aml_files(test_data *data, bool rebuild) if (rebuild) { aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - (gchar *)&sdt->header.signature, ext); + sdt->aml, ext); fd = g_open(aml_file, O_WRONLY|O_TRUNC|O_CREAT, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH); } else { @@ -271,8 +183,6 @@ static void dump_aml_files(test_data *data, bool rebuild) } g_assert(fd >= 0); - ret = qemu_write_full(fd, sdt, sizeof(AcpiTableHeader)); - g_assert(ret == sizeof(AcpiTableHeader)); ret = qemu_write_full(fd, sdt->aml, sdt->aml_len); g_assert(ret == sdt->aml_len); @@ -282,11 +192,6 @@ static void dump_aml_files(test_data *data, bool rebuild) } } -static bool compare_signature(AcpiSdtTable *sdt, const char *signature) -{ - return !memcmp(&sdt->header.signature, signature, 4); -} - static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) { AcpiSdtTable *temp; @@ -319,7 +224,7 @@ static bool load_asl(GArray *sdts, AcpiSdtTable *sdt) ret = g_spawn_command_line_sync(command_line->str, &out, &out_err, NULL, &error); g_assert_no_error(error); if (ret) { - ret = g_file_get_contents(sdt->asl_file, (gchar **)&sdt->asl, + ret = g_file_get_contents(sdt->asl_file, &sdt->asl, &sdt->asl_len, &error); g_assert(ret); g_assert_no_error(error); @@ -369,8 +274,12 @@ static GArray *load_expected_aml(test_data *data) AcpiSdtTable *sdt; GError *error = NULL; gboolean ret; + gsize aml_len; GArray *exp_tables = g_array_new(false, true, sizeof(AcpiSdtTable)); + if (getenv("V")) { + fputc('\n', stderr); + } for (i = 0; i < data->tables->len; ++i) { AcpiSdtTable exp_sdt; gchar *aml_file = NULL; @@ -379,28 +288,28 @@ static GArray *load_expected_aml(test_data *data) sdt = &g_array_index(data->tables, AcpiSdtTable, i); memset(&exp_sdt, 0, sizeof(exp_sdt)); - exp_sdt.header.signature = sdt->header.signature; try_again: aml_file = g_strdup_printf("%s/%s/%.4s%s", data_dir, data->machine, - (gchar *)&sdt->header.signature, ext); + sdt->aml, ext); if (getenv("V")) { - fprintf(stderr, "\nLooking for expected file '%s'\n", aml_file); + fprintf(stderr, "Looking for expected file '%s'\n", aml_file); } if (g_file_test(aml_file, G_FILE_TEST_EXISTS)) { exp_sdt.aml_file = aml_file; } else if (*ext != '\0') { - /* try fallback to generic (extention less) expected file */ + /* try fallback to generic (extension less) expected file */ ext = ""; g_free(aml_file); goto try_again; } g_assert(exp_sdt.aml_file); if (getenv("V")) { - fprintf(stderr, "\nUsing expected file '%s'\n", aml_file); + fprintf(stderr, "Using expected file '%s'\n", aml_file); } - ret = g_file_get_contents(aml_file, &exp_sdt.aml, - &exp_sdt.aml_len, &error); + ret = g_file_get_contents(aml_file, (gchar **)&exp_sdt.aml, + &aml_len, &error); + exp_sdt.aml_len = aml_len; g_assert(ret); g_assert_no_error(error); g_assert(exp_sdt.aml); @@ -443,14 +352,12 @@ static void test_acpi_asl(test_data *data) fprintf(stderr, "Warning! iasl couldn't parse the expected aml\n"); } else { - uint32_t signature = cpu_to_le32(exp_sdt->header.signature); sdt->tmp_files_retain = true; exp_sdt->tmp_files_retain = true; fprintf(stderr, "acpi-test: Warning! %.4s mismatch. " "Actual [asl:%s, aml:%s], Expected [asl:%s, aml:%s].\n", - (gchar *)&signature, - sdt->asl_file, sdt->aml_file, + exp_sdt->aml, sdt->asl_file, sdt->aml_file, exp_sdt->asl_file, exp_sdt->aml_file); if (getenv("V")) { const char *diff_cmd = getenv("DIFF"); @@ -482,32 +389,19 @@ static bool smbios_ep_table_ok(test_data *data) 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); + qtest_memread(data->qts, addr, ep_table, sizeof(*ep_table)); if (memcmp(ep_table->anchor_string, "_SM_", 4)) { return false; } - ACPI_READ_FIELD(ep_table->checksum, addr); - ACPI_READ_FIELD(ep_table->length, addr); - ACPI_READ_FIELD(ep_table->smbios_major_version, addr); - ACPI_READ_FIELD(ep_table->smbios_minor_version, addr); - ACPI_READ_FIELD(ep_table->max_structure_size, addr); - ACPI_READ_FIELD(ep_table->entry_point_revision, addr); - ACPI_READ_ARRAY(ep_table->formatted_area, addr); - ACPI_READ_ARRAY(ep_table->intermediate_anchor_string, addr); if (memcmp(ep_table->intermediate_anchor_string, "_DMI_", 5)) { return false; } - ACPI_READ_FIELD(ep_table->intermediate_checksum, addr); - ACPI_READ_FIELD(ep_table->structure_table_length, addr); if (ep_table->structure_table_length == 0) { return false; } - ACPI_READ_FIELD(ep_table->structure_table_address, addr); - ACPI_READ_FIELD(ep_table->number_of_structures, addr); if (ep_table->number_of_structures == 0) { return false; } - ACPI_READ_FIELD(ep_table->smbios_bcd_revision, addr); if (acpi_calc_checksum((uint8_t *)ep_table, sizeof *ep_table) || acpi_calc_checksum((uint8_t *)ep_table + 0x10, sizeof *ep_table - 0x10)) { @@ -526,7 +420,7 @@ static void test_smbios_entry_point(test_data *data) int i; for (i = 0; i < sizeof sig - 1; ++i) { - sig[i] = readb(off + i); + sig[i] = qtest_readb(data->qts, off + i); } if (!memcmp(sig, "_SM_", sizeof sig)) { @@ -569,9 +463,9 @@ static void test_smbios_structs(test_data *data) for (i = 0; i < le16_to_cpu(ep_table->number_of_structures); i++) { /* grab type and formatted area length from struct header */ - type = readb(addr); + type = qtest_readb(data->qts, addr); g_assert_cmpuint(type, <=, SMBIOS_MAX_TYPE); - len = readb(addr + 1); + len = qtest_readb(data->qts, addr + 1); /* single-instance structs must not have been encountered before */ if (smbios_single_instance(type)) { @@ -583,7 +477,7 @@ static void test_smbios_structs(test_data *data) prv = crt = 1; while (prv || crt) { prv = crt; - crt = readb(addr + len); + crt = qtest_readb(data->qts, addr + len); len++; } @@ -620,20 +514,15 @@ static void test_acpi_one(const char *params, test_data *data) data->machine, "kvm:tcg", params ? params : "", disk); - qtest_start(args); + data->qts = qtest_init(args); - boot_sector_test(global_qtest); + boot_sector_test(data->qts); data->tables = g_array_new(false, true, sizeof(AcpiSdtTable)); test_acpi_rsdp_address(data); test_acpi_rsdp_table(data); test_acpi_rsdt_table(data); - fadt_fetch_facs_and_dsdt_ptrs(data); - test_acpi_facs_table(data); - test_acpi_dsdt_table(data); - fetch_rsdt_referenced_tables(data); - - sanitize_fadt_ptrs(data); + test_acpi_fadt_table(data); if (iasl) { if (getenv(ACPI_REBUILD_EXPECTED_AML)) { @@ -646,7 +535,8 @@ static void test_acpi_one(const char *params, test_data *data) test_smbios_entry_point(data); test_smbios_structs(data); - qtest_quit(global_qtest); + assert(!global_qtest); + qtest_quit(data->qts); g_free(args); } @@ -708,6 +598,21 @@ static void test_acpi_q35_tcg_bridge(void) free_test_data(&data); } +static void test_acpi_q35_tcg_mmio64(void) +{ + test_data data = { + .machine = MACHINE_Q35, + .variant = ".mmio64", + .required_struct_types = base_required_struct_types, + .required_struct_types_len = ARRAY_SIZE(base_required_struct_types) + }; + + test_acpi_one("-m 128M,slots=1,maxmem=2G " + "-device pci-testdev,membar=2G", + &data); + free_test_data(&data); +} + static void test_acpi_piix4_tcg_cphp(void) { test_data data; @@ -875,6 +780,7 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/piix4/bridge", test_acpi_piix4_tcg_bridge); qtest_add_func("acpi/q35", test_acpi_q35_tcg); qtest_add_func("acpi/q35/bridge", test_acpi_q35_tcg_bridge); + qtest_add_func("acpi/q35/mmio64", test_acpi_q35_tcg_mmio64); qtest_add_func("acpi/piix4/ipmi", test_acpi_piix4_tcg_ipmi); qtest_add_func("acpi/q35/ipmi", test_acpi_q35_tcg_ipmi); qtest_add_func("acpi/piix4/cpuhp", test_acpi_piix4_tcg_cphp); diff --git a/tests/boot-order-test.c b/tests/boot-order-test.c index e70f5dedba..a725bce729 100644 --- a/tests/boot-order-test.c +++ b/tests/boot-order-test.c @@ -13,8 +13,11 @@ #include "qemu/osdep.h" #include "libqos/fw_cfg.h" #include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "standard-headers/linux/qemu_fw_cfg.h" -#include "hw/nvram/fw_cfg_keys.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) typedef struct { const char *args; @@ -24,31 +27,30 @@ typedef struct { static void test_a_boot_order(const char *machine, const char *test_args, - uint64_t (*read_boot_order)(void), + uint64_t (*read_boot_order)(QTestState *), uint64_t expected_boot, uint64_t expected_reboot) { uint64_t actual; + QTestState *qts; - global_qtest = qtest_startf("-nodefaults%s%s %s", - machine ? " -M " : "", - machine ?: "", - test_args); - actual = read_boot_order(); + qts = qtest_initf("-nodefaults%s%s %s", machine ? " -M " : "", + machine ?: "", test_args); + actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_boot); - qmp_discard_response("{ 'execute': 'system_reset' }"); + qmp_discard_response(qts, "{ 'execute': 'system_reset' }"); /* * system_reset only requests reset. We get a RESET event after * the actual reset completes. Need to wait for that. */ - qmp_eventwait("RESET"); - actual = read_boot_order(); + qtest_qmp_eventwait(qts, "RESET"); + actual = read_boot_order(qts); g_assert_cmphex(actual, ==, expected_reboot); - qtest_quit(global_qtest); + qtest_quit(qts); } static void test_boot_orders(const char *machine, - uint64_t (*read_boot_order)(void), + uint64_t (*read_boot_order)(QTestState *), const boot_order_test *tests) { int i; @@ -61,16 +63,16 @@ static void test_boot_orders(const char *machine, } } -static uint8_t read_mc146818(uint16_t port, uint8_t reg) +static uint8_t read_mc146818(QTestState *qts, uint16_t port, uint8_t reg) { - outb(port, reg); - return inb(port + 1); + qtest_outb(qts, port, reg); + return qtest_inb(qts, port + 1); } -static uint64_t read_boot_order_pc(void) +static uint64_t read_boot_order_pc(QTestState *qts) { - uint8_t b1 = read_mc146818(0x70, 0x38); - uint8_t b2 = read_mc146818(0x70, 0x3d); + uint8_t b1 = read_mc146818(qts, 0x70, 0x38); + uint8_t b2 = read_mc146818(qts, 0x70, 0x3d); return b1 | (b2 << 8); } @@ -106,16 +108,16 @@ static void test_pc_boot_order(void) test_boot_orders(NULL, read_boot_order_pc, test_cases_pc); } -static uint8_t read_m48t59(uint64_t addr, uint16_t reg) +static uint8_t read_m48t59(QTestState *qts, uint64_t addr, uint16_t reg) { - writeb(addr, reg & 0xff); - writeb(addr + 1, reg >> 8); - return readb(addr + 3); + qtest_writeb(qts, addr, reg & 0xff); + qtest_writeb(qts, addr + 1, reg >> 8); + return qtest_readb(qts, addr + 3); } -static uint64_t read_boot_order_prep(void) +static uint64_t read_boot_order_prep(QTestState *qts) { - return read_m48t59(0x80000000 + 0x74, 0x34); + return read_m48t59(qts, 0x80000000 + 0x74, 0x34); } static const boot_order_test test_cases_prep[] = { @@ -130,9 +132,9 @@ static void test_prep_boot_order(void) test_boot_orders("prep", read_boot_order_prep, test_cases_prep); } -static uint64_t read_boot_order_pmac(void) +static uint64_t read_boot_order_pmac(QTestState *qts) { - QFWCFG *fw_cfg = mm_fw_cfg_init(global_qtest, 0xf0000510); + QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xf0000510); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -155,9 +157,9 @@ static void test_pmac_newworld_boot_order(void) test_boot_orders("mac99", read_boot_order_pmac, test_cases_fw_cfg); } -static uint64_t read_boot_order_sun4m(void) +static uint64_t read_boot_order_sun4m(QTestState *qts) { - QFWCFG *fw_cfg = mm_fw_cfg_init(global_qtest, 0xd00000510ULL); + QFWCFG *fw_cfg = mm_fw_cfg_init(qts, 0xd00000510ULL); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } @@ -167,9 +169,9 @@ static void test_sun4m_boot_order(void) test_boot_orders("SS-5", read_boot_order_sun4m, test_cases_fw_cfg); } -static uint64_t read_boot_order_sun4u(void) +static uint64_t read_boot_order_sun4u(QTestState *qts) { - QFWCFG *fw_cfg = io_fw_cfg_init(global_qtest, 0x510); + QFWCFG *fw_cfg = io_fw_cfg_init(qts, 0x510); return qfw_cfg_get_u16(fw_cfg, FW_CFG_BOOT_DEVICE); } diff --git a/tests/boot-serial-test.c b/tests/boot-serial-test.c index 952a2e7ead..c591748aaf 100644 --- a/tests/boot-serial-test.c +++ b/tests/boot-serial-test.c @@ -62,6 +62,24 @@ static const uint8_t kernel_aarch64[] = { 0xfd, 0xff, 0xff, 0x17, /* b -12 (loop) */ }; +static const uint8_t kernel_nrf51[] = { + 0x00, 0x00, 0x00, 0x00, /* Stack top address */ + 0x09, 0x00, 0x00, 0x00, /* Reset handler address */ + 0x04, 0x4a, /* ldr r2, [pc, #16] Get ENABLE */ + 0x04, 0x21, /* movs r1, #4 */ + 0x11, 0x60, /* str r1, [r2] */ + 0x04, 0x4a, /* ldr r2, [pc, #16] Get STARTTX */ + 0x01, 0x21, /* movs r1, #1 */ + 0x11, 0x60, /* str r1, [r2] */ + 0x03, 0x4a, /* ldr r2, [pc, #12] Get TXD */ + 0x54, 0x21, /* movs r1, 'T' */ + 0x11, 0x60, /* str r1, [r2] */ + 0xfe, 0xe7, /* b . */ + 0x00, 0x25, 0x00, 0x40, /* 0x40002500 = UART ENABLE */ + 0x08, 0x20, 0x00, 0x40, /* 0x40002008 = UART STARTTX */ + 0x1c, 0x25, 0x00, 0x40 /* 0x4000251c = UART TXD */ +}; + typedef struct testdef { const char *arch; /* Target architecture */ const char *machine; /* Name of the machine */ @@ -75,16 +93,16 @@ typedef struct testdef { static testdef_t tests[] = { { "alpha", "clipper", "", "PCI:" }, { "ppc", "ppce500", "", "U-Boot" }, - { "ppc", "prep", "-m 96", "Memory size: 96 MB" }, - { "ppc", "40p", "-boot d", "Booting from device d" }, + { "ppc", "40p", "-vga none -boot d", "Trying cd:," }, { "ppc", "g3beige", "", "PowerPC,750" }, { "ppc", "mac99", "", "PowerPC,G4" }, { "ppc", "sam460ex", "-m 256", "DRAM: 256 MiB" }, { "ppc64", "ppce500", "", "U-Boot" }, - { "ppc64", "prep", "-boot e", "Booting from device e" }, - { "ppc64", "40p", "-m 192", "Memory size: 192 MB" }, + { "ppc64", "40p", "-m 192", "Memory: 192M" }, { "ppc64", "mac99", "", "PowerPC,970FX" }, - { "ppc64", "pseries", "", "Open Firmware" }, + { "ppc64", "pseries", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken", + "Open Firmware" }, { "ppc64", "powernv", "-cpu POWER8", "OPAL" }, { "ppc64", "sam460ex", "-device e1000", "8086 100e" }, { "i386", "isapc", "-cpu qemu32 -device sga", "SGABIOS" }, @@ -107,17 +125,19 @@ static testdef_t tests[] = { { "hppa", "hppa", "", "SeaBIOS wants SYSTEM HALT" }, { "aarch64", "virt", "-cpu cortex-a57", "TT", sizeof(kernel_aarch64), kernel_aarch64 }, + { "arm", "microbit", "", "T", sizeof(kernel_nrf51), kernel_nrf51 }, { NULL } }; -static bool check_guest_output(const testdef_t *test, int fd) +static bool check_guest_output(QTestState *qts, const testdef_t *test, int fd) { - int i, nbr = 0, pos = 0, ccnt; + int nbr = 0, pos = 0, ccnt; + time_t now, start = time(NULL); char ch; - /* Poll serial output... Wait at most 60 seconds */ - for (i = 0; i < 6000; ++i) { + /* Poll serial output... */ + while (1) { ccnt = 0; while (ccnt++ < 512 && (nbr = read(fd, &ch, 1)) == 1) { if (ch == test->expect[pos]) { @@ -131,6 +151,15 @@ static bool check_guest_output(const testdef_t *test, int fd) } } g_assert(nbr >= 0); + /* Wait only if the child is still alive. */ + if (!qtest_probe_child(qts)) { + break; + } + /* Wait at most 360 seconds. */ + now = time(NULL); + if (now - start >= 360) { + break; + } g_usleep(10000); } @@ -144,6 +173,7 @@ static void test_machine(const void *data) char codetmp[] = "/tmp/qtest-boot-serial-cXXXXXX"; const char *codeparam = ""; const uint8_t *code = NULL; + QTestState *qts; int ser_fd; ser_fd = mkstemp(serialtmp); @@ -172,22 +202,22 @@ static void test_machine(const void *data) * Make sure that this test uses tcg if available: It is used as a * fast-enough smoketest for that. */ - global_qtest = qtest_startf("%s %s -M %s,accel=tcg:kvm " - "-chardev file,id=serial0,path=%s " - "-no-shutdown -serial chardev:serial0 %s", - codeparam, code ? codetmp : "", - test->machine, serialtmp, test->extra); + qts = qtest_initf("%s %s -M %s,accel=tcg:kvm -no-shutdown " + "-chardev file,id=serial0,path=%s " + "-serial chardev:serial0 %s", + codeparam, code ? codetmp : "", test->machine, + serialtmp, test->extra); if (code) { unlink(codetmp); } - if (!check_guest_output(test, ser_fd)) { + if (!check_guest_output(qts, test, ser_fd)) { g_error("Failed to find expected string. Please check '%s'", serialtmp); } unlink(serialtmp); - qtest_quit(global_qtest); + qtest_quit(qts); close(ser_fd); } diff --git a/tests/cdrom-test.c b/tests/cdrom-test.c index 7a1fce5dfb..05611da648 100644 --- a/tests/cdrom-test.c +++ b/tests/cdrom-test.c @@ -99,7 +99,7 @@ static void test_cdrom_param(gconstpointer data) QTestState *qts; char *resp; - qts = qtest_startf("-M %s -cdrom %s", (const char *)data, isoimage); + qts = qtest_initf("-M %s -cdrom %s", (const char *)data, isoimage); resp = qtest_hmp(qts, "info block"); g_assert(strstr(resp, isoimage) != 0); g_free(resp); @@ -120,8 +120,8 @@ static void test_cdboot(gconstpointer data) { QTestState *qts; - qts = qtest_startf("-accel kvm:tcg -no-shutdown %s%s", (const char *)data, - isoimage); + qts = qtest_initf("-accel kvm:tcg -no-shutdown %s%s", (const char *)data, + isoimage); boot_sector_test(qts); qtest_quit(qts); } @@ -132,8 +132,14 @@ static void add_x86_tests(void) qtest_add_data_func("cdrom/boot/virtio-scsi", "-device virtio-scsi -device scsi-cd,drive=cdr " "-blockdev file,node-name=cdr,filename=", test_cdboot); - qtest_add_data_func("cdrom/boot/isapc", "-M isapc " - "-drive if=ide,media=cdrom,file=", test_cdboot); + /* + * Unstable CI test under load + * See https://lists.gnu.org/archive/html/qemu-devel/2019-02/msg05509.html + */ + if (g_test_slow()) { + qtest_add_data_func("cdrom/boot/isapc", "-M isapc " + "-drive if=ide,media=cdrom,file=", test_cdboot); + } qtest_add_data_func("cdrom/boot/am53c974", "-device am53c974 -device scsi-cd,drive=cd1 " "-drive if=none,id=cd1,format=raw,file=", test_cdboot); @@ -169,7 +175,7 @@ int main(int argc, char **argv) if (exec_genisoimg(genisocheck)) { /* genisoimage not available - so can't run tests */ - return 0; + return g_test_run(); } ret = prepare_image(arch, isoimage); diff --git a/tests/check-block-qdict.c b/tests/check-block-qdict.c index 478807f839..73d3e9f574 100644 --- a/tests/check-block-qdict.c +++ b/tests/check-block-qdict.c @@ -491,6 +491,7 @@ static void qdict_crumple_test_recursive(void) empty_list_0 = qobject_to(QDict, qlist_pop(empty_list)); g_assert(empty_list_0); g_assert_cmpint(qdict_size(empty_list_0), ==, 0); + qobject_unref(empty_list_0); qobject_unref(src); qobject_unref(dst); diff --git a/tests/check-block.sh b/tests/check-block.sh index c3de3789c4..f3d12fd602 100755 --- a/tests/check-block.sh +++ b/tests/check-block.sh @@ -5,9 +5,9 @@ if [ "$#" -ne 0 ]; then FORMAT_LIST="$@" fi -export QEMU_PROG="$(pwd)/x86_64-softmmu/qemu-system-x86_64" -export QEMU_IMG_PROG="$(pwd)/qemu-img" -export QEMU_IO_PROG="$(pwd)/qemu-io" +export QEMU_PROG="$PWD/x86_64-softmmu/qemu-system-x86_64" +export QEMU_IMG_PROG="$PWD/qemu-img" +export QEMU_IO_PROG="$PWD/qemu-io" if [ ! -x $QEMU_PROG ]; then echo "'make check-block' requires qemu-system-x86_64" diff --git a/tests/check-qdict.c b/tests/check-qdict.c index 86e9fe7dc4..b5efa859b0 100644 --- a/tests/check-qdict.c +++ b/tests/check-qdict.c @@ -12,6 +12,8 @@ #include "qemu/osdep.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qnum.h" +#include "qapi/qmp/qstring.h" /* * Public Interface test-cases @@ -289,7 +291,7 @@ static void qdict_stress_test(void) FILE *test_file; QDict *qdict; QString *value; - const char *test_file_path = "qdict-test-data.txt"; + const char *test_file_path = "tests/data/qobject/qdict.txt"; test_file = fopen(test_file_path, "r"); g_assert(test_file != NULL); diff --git a/tests/check-qjson.c b/tests/check-qjson.c index da582df3e9..fa2afccb0a 100644 --- a/tests/check-qjson.c +++ b/tests/check-qjson.c @@ -20,113 +20,111 @@ #include "qapi/qmp/qnull.h" #include "qapi/qmp/qnum.h" #include "qapi/qmp/qstring.h" +#include "qemu/unicode.h" #include "qemu-common.h" +static QString *from_json_str(const char *jstr, bool single, Error **errp) +{ + char quote = single ? '\'' : '"'; + char *qjstr = g_strdup_printf("%c%s%c", quote, jstr, quote); + QString *ret = qobject_to(QString, qobject_from_json(qjstr, errp)); + + g_free(qjstr); + return ret; +} + +static char *to_json_str(QString *str) +{ + QString *json = qobject_to_json(QOBJECT(str)); + char *jstr; + + if (!json) { + return NULL; + } + /* peel off double quotes */ + jstr = g_strndup(qstring_get_str(json) + 1, + qstring_get_length(json) - 2); + qobject_unref(json); + return jstr; +} + static void escaped_string(void) { - int i; struct { - const char *encoded; - const char *decoded; + /* Content of JSON string to parse with qobject_from_json() */ + const char *json_in; + /* Expected parse output; to unparse with qobject_to_json() */ + const char *utf8_out; int skip; } test_cases[] = { - { "\"\\b\"", "\b" }, - { "\"\\f\"", "\f" }, - { "\"\\n\"", "\n" }, - { "\"\\r\"", "\r" }, - { "\"\\t\"", "\t" }, - { "\"/\"", "/" }, - { "\"\\/\"", "/", .skip = 1 }, - { "\"\\\\\"", "\\" }, - { "\"\\\"\"", "\"" }, - { "\"hello world \\\"embedded string\\\"\"", - "hello world \"embedded string\"" }, - { "\"hello world\\nwith new line\"", "hello world\nwith new line" }, - { "\"single byte utf-8 \\u0020\"", "single byte utf-8 ", .skip = 1 }, - { "\"double byte utf-8 \\u00A2\"", "double byte utf-8 \xc2\xa2" }, - { "\"triple byte utf-8 \\u20AC\"", "triple byte utf-8 \xe2\x82\xac" }, - { "'\\b'", "\b", .skip = 1 }, - { "'\\f'", "\f", .skip = 1 }, - { "'\\n'", "\n", .skip = 1 }, - { "'\\r'", "\r", .skip = 1 }, - { "'\\t'", "\t", .skip = 1 }, - { "'\\/'", "/", .skip = 1 }, - { "'\\\\'", "\\", .skip = 1 }, + { "\\b\\f\\n\\r\\t\\\\\\\"", "\b\f\n\r\t\\\"" }, + { "\\/\\'", "/'", .skip = 1 }, + { "single byte utf-8 \\u0020", "single byte utf-8 ", .skip = 1 }, + { "double byte utf-8 \\u00A2", "double byte utf-8 \xc2\xa2" }, + { "triple byte utf-8 \\u20AC", "triple byte utf-8 \xe2\x82\xac" }, + { "quadruple byte utf-8 \\uD834\\uDD1E", /* U+1D11E */ + "quadruple byte utf-8 \xF0\x9D\x84\x9E" }, + { "\\", NULL }, + { "\\z", NULL }, + { "\\ux", NULL }, + { "\\u1x", NULL }, + { "\\u12x", NULL }, + { "\\u123x", NULL }, + { "\\u12345", "\341\210\2645" }, + { "\\u0000x", "\xC0\x80x" }, + { "unpaired leading surrogate \\uD800", NULL }, + { "unpaired leading surrogate \\uD800\\uCAFE", NULL }, + { "unpaired leading surrogate \\uD800\\uD801\\uDC02", NULL }, + { "unpaired trailing surrogate \\uDC00", NULL }, + { "backward surrogate pair \\uDC00\\uD800", NULL }, + { "noncharacter U+FDD0 \\uFDD0", NULL }, + { "noncharacter U+FDEF \\uFDEF", NULL }, + { "noncharacter U+1FFFE \\uD87F\\uDFFE", NULL }, + { "noncharacter U+10FFFF \\uDC3F\\uDFFF", NULL }, {} }; + int i, j; + QString *cstr; + char *jstr; - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - QString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to(QString, obj); - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].decoded); - - if (test_cases[i].skip == 0) { - str = qobject_to_json(obj); - g_assert_cmpstr(qstring_get_str(str), ==, test_cases[i].encoded); - qobject_unref(obj); + for (i = 0; test_cases[i].json_in; i++) { + for (j = 0; j < 2; j++) { + if (test_cases[i].utf8_out) { + cstr = from_json_str(test_cases[i].json_in, j, &error_abort); + g_assert_cmpstr(qstring_get_try_str(cstr), + ==, test_cases[i].utf8_out); + if (!test_cases[i].skip) { + jstr = to_json_str(cstr); + g_assert_cmpstr(jstr, ==, test_cases[i].json_in); + g_free(jstr); + } + qobject_unref(cstr); + } else { + cstr = from_json_str(test_cases[i].json_in, j, NULL); + g_assert(!cstr); + } } - - qobject_unref(str); } } -static void simple_string(void) +static void string_with_quotes(void) { - int i; - struct { - const char *encoded; - const char *decoded; - } test_cases[] = { - { "\"hello world\"", "hello world" }, - { "\"the quick brown fox jumped over the fence\"", - "the quick brown fox jumped over the fence" }, - {} + const char *test_cases[] = { + "\"the bee's knees\"", + "'double quote \"'", + NULL }; - - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - QString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to(QString, obj); - g_assert(str); - g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - - str = qobject_to_json(obj); - g_assert(strcmp(qstring_get_str(str), test_cases[i].encoded) == 0); - - qobject_unref(obj); - - qobject_unref(str); - } -} - -static void single_quote_string(void) -{ int i; - struct { - const char *encoded; - const char *decoded; - } test_cases[] = { - { "'hello world'", "hello world" }, - { "'the quick brown fox \\' jumped over the fence'", - "the quick brown fox ' jumped over the fence" }, - {} - }; + QString *str; + char *cstr; - for (i = 0; test_cases[i].encoded; i++) { - QObject *obj; - QString *str; - - obj = qobject_from_json(test_cases[i].encoded, &error_abort); - str = qobject_to(QString, obj); + for (i = 0; test_cases[i]; i++) { + str = qobject_to(QString, + qobject_from_json(test_cases[i], &error_abort)); g_assert(str); - g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - + cstr = g_strndup(test_cases[i] + 1, strlen(test_cases[i]) - 2); + g_assert_cmpstr(qstring_get_str(str), ==, cstr); + g_free(cstr); qobject_unref(str); } } @@ -134,117 +132,109 @@ static void single_quote_string(void) static void utf8_string(void) { /* - * FIXME Current behavior for invalid UTF-8 sequences is - * incorrect. This test expects current, incorrect results. - * They're all marked "bug:" below, and are to be replaced by - * correct ones as the bugs get fixed. - * - * The JSON parser rejects some invalid sequences, but accepts - * others without correcting the problem. - * - * We should either reject all invalid sequences, or minimize - * overlong sequences and replace all other invalid sequences by a - * suitable replacement character. A common choice for - * replacement is U+FFFD. - * - * Problem: we can't easily deal with embedded U+0000. Parsing - * the JSON string "this \\u0000" is fun" yields "this \0 is fun", - * which gets misinterpreted as NUL-terminated "this ". We should - * consider using overlong encoding \xC0\x80 for U+0000 ("modified - * UTF-8"). - * * Most test cases are scraped from Markus Kuhn's UTF-8 decoder * capability and stress test at * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt */ static const struct { + /* Content of JSON string to parse with qobject_from_json() */ const char *json_in; + /* Expected parse output */ const char *utf8_out; - const char *json_out; /* defaults to @json_in */ - const char *utf8_in; /* defaults to @utf8_out */ + /* Expected unparse output, defaults to @json_in */ + const char *json_out; } test_cases[] = { - /* - * Bug markers used here: - * - bug: not corrected - * JSON parser fails to correct invalid sequence(s) - * - bug: rejected - * JSON parser rejects invalid sequence(s) - * We may choose to define this as feature - * - bug: want "..." - * JSON parser produces incorrect result, this is the - * correct one, assuming replacement character U+FFFF - * We may choose to reject instead of replace - */ - + /* 0 Control characters */ + { + /* + * Note: \x00 is impossible, other representations of + * U+0000 are covered under 4.3 + */ + "\x01\x02\x03\x04\x05\x06\x07" + "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17" + "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", + NULL, + "\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007" + "\\b\\t\\n\\u000B\\f\\r\\u000E\\u000F" + "\\u0010\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017" + "\\u0018\\u0019\\u001A\\u001B\\u001C\\u001D\\u001E\\u001F", + }, /* 1 Some correct UTF-8 text */ { /* a bit of German */ - "\"Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" - " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.\"", "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", - "\"Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" - " jeden gr\\u00F6\\u00DFeren Zwerg.\"", + "Falsches \xC3\x9C" "ben von Xylophonmusik qu\xC3\xA4lt" + " jeden gr\xC3\xB6\xC3\x9F" "eren Zwerg.", + "Falsches \\u00DCben von Xylophonmusik qu\\u00E4lt" + " jeden gr\\u00F6\\u00DFeren Zwerg.", }, { /* a bit of Greek */ - "\"\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5\"", "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", - "\"\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5\"", + "\xCE\xBA\xE1\xBD\xB9\xCF\x83\xCE\xBC\xCE\xB5", + "\\u03BA\\u1F79\\u03C3\\u03BC\\u03B5", + }, + /* '%' character when not interpolating */ + { + "100%", + "100%", }, /* 2 Boundary condition test cases */ /* 2.1 First possible sequence of a certain length */ - /* 2.1.1 1 byte U+0000 */ + /* + * 2.1.1 1 byte U+0020 + * Control characters are already covered by their own test + * case under 0. Test the first 1 byte non-control character + * here. + */ { - "\"\\u0000\"", - "", /* bug: want overlong "\xC0\x80" */ - "\"\\u0000\"", - "\xC0\x80", + " ", + " ", }, /* 2.1.2 2 bytes U+0080 */ { - "\"\xC2\x80\"", "\xC2\x80", - "\"\\u0080\"", + "\xC2\x80", + "\\u0080", }, /* 2.1.3 3 bytes U+0800 */ { - "\"\xE0\xA0\x80\"", "\xE0\xA0\x80", - "\"\\u0800\"", + "\xE0\xA0\x80", + "\\u0800", }, /* 2.1.4 4 bytes U+10000 */ { - "\"\xF0\x90\x80\x80\"", "\xF0\x90\x80\x80", - "\"\\uD800\\uDC00\"", + "\xF0\x90\x80\x80", + "\\uD800\\uDC00", }, /* 2.1.5 5 bytes U+200000 */ { - "\"\xF8\x88\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x88\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 2.1.6 6 bytes U+4000000 */ { - "\"\xFC\x84\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x84\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 2.2 Last possible sequence of a certain length */ /* 2.2.1 1 byte U+007F */ { - "\"\x7F\"", "\x7F", - "\"\\u007F\"", + "\x7F", + "\\u007F", }, /* 2.2.2 2 bytes U+07FF */ { - "\"\xDF\xBF\"", "\xDF\xBF", - "\"\\u07FF\"", + "\xDF\xBF", + "\\u07FF", }, /* * 2.2.3 3 bytes U+FFFC @@ -256,123 +246,111 @@ static void utf8_string(void) * U+FFFC here. */ { - "\"\xEF\xBF\xBC\"", "\xEF\xBF\xBC", - "\"\\uFFFC\"", + "\xEF\xBF\xBC", + "\\uFFFC", }, /* 2.2.4 4 bytes U+1FFFFF */ { - "\"\xF7\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF7\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 2.2.5 5 bytes U+3FFFFFF */ { - "\"\xFB\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFB\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 2.2.6 6 bytes U+7FFFFFFF */ { - "\"\xFD\xBF\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFD\xBF\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 2.3 Other boundary conditions */ { /* last one before surrogate range: U+D7FF */ - "\"\xED\x9F\xBF\"", "\xED\x9F\xBF", - "\"\\uD7FF\"", + "\xED\x9F\xBF", + "\\uD7FF", }, { /* first one after surrogate range: U+E000 */ - "\"\xEE\x80\x80\"", "\xEE\x80\x80", - "\"\\uE000\"", + "\xEE\x80\x80", + "\\uE000", }, { /* last one in BMP: U+FFFD */ - "\"\xEF\xBF\xBD\"", "\xEF\xBF\xBD", - "\"\\uFFFD\"", + "\xEF\xBF\xBD", + "\\uFFFD", }, { /* last one in last plane: U+10FFFD */ - "\"\xF4\x8F\xBF\xBD\"", "\xF4\x8F\xBF\xBD", - "\"\\uDBFF\\uDFFD\"" + "\xF4\x8F\xBF\xBD", + "\\uDBFF\\uDFFD" }, { /* first one beyond Unicode range: U+110000 */ - "\"\xF4\x90\x80\x80\"", "\xF4\x90\x80\x80", - "\"\\uFFFD\"", + NULL, + "\\uFFFD", }, /* 3 Malformed sequences */ /* 3.1 Unexpected continuation bytes */ /* 3.1.1 First continuation byte */ { - "\"\x80\"", - "\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\x80", + NULL, + "\\uFFFD", }, /* 3.1.2 Last continuation byte */ { - "\"\xBF\"", - "\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xBF", + NULL, + "\\uFFFD", }, /* 3.1.3 2 continuation bytes */ { - "\"\x80\xBF\"", - "\x80\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\x80\xBF", + NULL, + "\\uFFFD\\uFFFD", }, /* 3.1.4 3 continuation bytes */ { - "\"\x80\xBF\x80\"", - "\x80\xBF\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.5 4 continuation bytes */ { - "\"\x80\xBF\x80\xBF\"", - "\x80\xBF\x80\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.6 5 continuation bytes */ { - "\"\x80\xBF\x80\xBF\x80\"", - "\x80\xBF\x80\xBF\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.7 6 continuation bytes */ { - "\"\x80\xBF\x80\xBF\x80\xBF\"", - "\x80\xBF\x80\xBF\x80\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF\x80\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.8 7 continuation bytes */ { - "\"\x80\xBF\x80\xBF\x80\xBF\x80\"", - "\x80\xBF\x80\xBF\x80\xBF\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\x80\xBF\x80\xBF\x80\xBF\x80", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.1.9 Sequence of all 64 possible continuation bytes */ { - "\"\x80\x81\x82\x83\x84\x85\x86\x87" - "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" - "\x90\x91\x92\x93\x94\x95\x96\x97" - "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F" - "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7" - "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" - "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" - "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF\"", - /* bug: not corrected */ "\x80\x81\x82\x83\x84\x85\x86\x87" "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F" "\x90\x91\x92\x93\x94\x95\x96\x97" @@ -381,188 +359,166 @@ static void utf8_string(void) "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF" "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7" "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + NULL, "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.2 Lonely start characters */ /* 3.2.1 All 32 first bytes of 2-byte sequences, followed by space */ { - "\"\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " - "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " - "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " - "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", "\xC0 \xC1 \xC2 \xC3 \xC4 \xC5 \xC6 \xC7 " "\xC8 \xC9 \xCA \xCB \xCC \xCD \xCE \xCF " "\xD0 \xD1 \xD2 \xD3 \xD4 \xD5 \xD6 \xD7 " "\xD8 \xD9 \xDA \xDB \xDC \xDD \xDE \xDF ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.2 All 16 first bytes of 3-byte sequences, followed by space */ { - "\"\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " - "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF \"", - /* bug: not corrected */ "\xE0 \xE1 \xE2 \xE3 \xE4 \xE5 \xE6 \xE7 " "\xE8 \xE9 \xEA \xEB \xEC \xED \xEE \xEF ", - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " - "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD " + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.3 All 8 first bytes of 4-byte sequences, followed by space */ { - "\"\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", "\xF0 \xF1 \xF2 \xF3 \xF4 \xF5 \xF6 \xF7 ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.4 All 4 first bytes of 5-byte sequences, followed by space */ { - "\"\xF8 \xF9 \xFA \xFB \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \\uFFFD \\uFFFD \"", "\xF8 \xF9 \xFA \xFB ", + NULL, + "\\uFFFD \\uFFFD \\uFFFD \\uFFFD ", }, /* 3.2.5 All 2 first bytes of 6-byte sequences, followed by space */ { - "\"\xFC \xFD \"", - NULL, /* bug: rejected */ - "\"\\uFFFD \\uFFFD \"", "\xFC \xFD ", + NULL, + "\\uFFFD \\uFFFD ", }, /* 3.3 Sequences with last continuation byte missing */ /* 3.3.1 2-byte sequence with last byte missing (U+0000) */ { - "\"\xC0\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xC0", + NULL, + "\\uFFFD", }, /* 3.3.2 3-byte sequence with last byte missing (U+0000) */ { - "\"\xE0\x80\"", - "\xE0\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x80", + NULL, + "\\uFFFD", }, /* 3.3.3 4-byte sequence with last byte missing (U+0000) */ { - "\"\xF0\x80\x80\"", - "\xF0\x80\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x80\x80", + NULL, + "\\uFFFD", }, /* 3.3.4 5-byte sequence with last byte missing (U+0000) */ { - "\"\xF8\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 3.3.5 6-byte sequence with last byte missing (U+0000) */ { - "\"\xFC\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 3.3.6 2-byte sequence with last byte missing (U+07FF) */ { - "\"\xDF\"", - "\xDF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xDF", + NULL, + "\\uFFFD", }, /* 3.3.7 3-byte sequence with last byte missing (U+FFFF) */ { - "\"\xEF\xBF\"", - "\xEF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xBF", + NULL, + "\\uFFFD", }, /* 3.3.8 4-byte sequence with last byte missing (U+1FFFFF) */ { - "\"\xF7\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF7\xBF\xBF", + NULL, + "\\uFFFD", }, /* 3.3.9 5-byte sequence with last byte missing (U+3FFFFFF) */ { - "\"\xFB\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFB\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 3.3.10 6-byte sequence with last byte missing (U+7FFFFFFF) */ { - "\"\xFD\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFD\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 3.4 Concatenation of incomplete sequences */ { - "\"\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" - "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", "\xC0\xE0\x80\xF0\x80\x80\xF8\x80\x80\x80\xFC\x80\x80\x80\x80" "\xDF\xEF\xBF\xF7\xBF\xBF\xFB\xBF\xBF\xBF\xFD\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 3.5 Impossible bytes */ { - "\"\xFE\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFE", + NULL, + "\\uFFFD", }, { - "\"\xFF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFF", + NULL, + "\\uFFFD", }, { - "\"\xFE\xFE\xFF\xFF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", "\xFE\xFE\xFF\xFF", + NULL, + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, /* 4 Overlong sequences */ /* 4.1 Overlong '/' */ { - "\"\xC0\xAF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xC0\xAF", + NULL, + "\\uFFFD", }, { - "\"\xE0\x80\xAF\"", - "\xE0\x80\xAF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x80\xAF", + NULL, + "\\uFFFD", }, { - "\"\xF0\x80\x80\xAF\"", - "\xF0\x80\x80\xAF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x80\x80\xAF", + NULL, + "\\uFFFD", }, { - "\"\xF8\x80\x80\x80\xAF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x80\x80\x80\xAF", + NULL, + "\\uFFFD", }, { - "\"\xFC\x80\x80\x80\x80\xAF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x80\x80\x80\x80\xAF", + NULL, + "\\uFFFD", }, /* * 4.2 Maximum overlong sequences @@ -572,16 +528,15 @@ static void utf8_string(void) */ { /* \U+007F */ - "\"\xC1\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xC1\xBF", + NULL, + "\\uFFFD", }, { /* \U+07FF */ - "\"\xE0\x9F\xBF\"", - "\xE0\x9F\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x9F\xBF", + NULL, + "\\uFFFD", }, { /* @@ -590,197 +545,175 @@ static void utf8_string(void) * noncharacter. Testing U+FFFC seems more useful. See * also 2.2.3 */ - "\"\xF0\x8F\xBF\xBC\"", - "\xF0\x8F\xBF\xBC", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x8F\xBF\xBC", + NULL, + "\\uFFFD", }, { /* \U+1FFFFF */ - "\"\xF8\x87\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x87\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, { /* \U+3FFFFFF */ - "\"\xFC\x83\xBF\xBF\xBF\xBF\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x83\xBF\xBF\xBF\xBF", + NULL, + "\\uFFFD", }, /* 4.3 Overlong representation of the NUL character */ { /* \U+0000 */ - "\"\xC0\x80\"", - NULL, /* bug: rejected */ - "\"\\u0000\"", "\xC0\x80", + "\xC0\x80", + "\\u0000", }, { /* \U+0000 */ - "\"\xE0\x80\x80\"", - "\xE0\x80\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xE0\x80\x80", + NULL, + "\\uFFFD", }, { /* \U+0000 */ - "\"\xF0\x80\x80\x80\"", - "\xF0\x80\x80\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xF0\x80\x80\x80", + NULL, + "\\uFFFD", }, { /* \U+0000 */ - "\"\xF8\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xF8\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, { /* \U+0000 */ - "\"\xFC\x80\x80\x80\x80\x80\"", - NULL, /* bug: rejected */ - "\"\\uFFFD\"", "\xFC\x80\x80\x80\x80\x80", + NULL, + "\\uFFFD", }, /* 5 Illegal code positions */ /* 5.1 Single UTF-16 surrogates */ { /* \U+D800 */ - "\"\xED\xA0\x80\"", - "\xED\xA0\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xA0\x80", + NULL, + "\\uFFFD", }, { /* \U+DB7F */ - "\"\xED\xAD\xBF\"", - "\xED\xAD\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xAD\xBF", + NULL, + "\\uFFFD", }, { /* \U+DB80 */ - "\"\xED\xAE\x80\"", - "\xED\xAE\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xAE\x80", + NULL, + "\\uFFFD", }, { /* \U+DBFF */ - "\"\xED\xAF\xBF\"", - "\xED\xAF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xAF\xBF", + NULL, + "\\uFFFD", }, { /* \U+DC00 */ - "\"\xED\xB0\x80\"", - "\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xB0\x80", + NULL, + "\\uFFFD", }, { /* \U+DF80 */ - "\"\xED\xBE\x80\"", - "\xED\xBE\x80", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xBE\x80", + NULL, + "\\uFFFD", }, { /* \U+DFFF */ - "\"\xED\xBF\xBF\"", - "\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xED\xBF\xBF", + NULL, + "\\uFFFD", }, /* 5.2 Paired UTF-16 surrogates */ { /* \U+D800\U+DC00 */ - "\"\xED\xA0\x80\xED\xB0\x80\"", - "\xED\xA0\x80\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xA0\x80\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+D800\U+DFFF */ - "\"\xED\xA0\x80\xED\xBF\xBF\"", - "\xED\xA0\x80\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xA0\x80\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB7F\U+DC00 */ - "\"\xED\xAD\xBF\xED\xB0\x80\"", - "\xED\xAD\xBF\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAD\xBF\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB7F\U+DFFF */ - "\"\xED\xAD\xBF\xED\xBF\xBF\"", - "\xED\xAD\xBF\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAD\xBF\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB80\U+DC00 */ - "\"\xED\xAE\x80\xED\xB0\x80\"", - "\xED\xAE\x80\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAE\x80\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DB80\U+DFFF */ - "\"\xED\xAE\x80\xED\xBF\xBF\"", - "\xED\xAE\x80\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAE\x80\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DBFF\U+DC00 */ - "\"\xED\xAF\xBF\xED\xB0\x80\"", - "\xED\xAF\xBF\xED\xB0\x80", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAF\xBF\xED\xB0\x80", + NULL, + "\\uFFFD\\uFFFD", }, { /* \U+DBFF\U+DFFF */ - "\"\xED\xAF\xBF\xED\xBF\xBF\"", - "\xED\xAF\xBF\xED\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\\uFFFD\"", + "\xED\xAF\xBF\xED\xBF\xBF", + NULL, + "\\uFFFD\\uFFFD", }, /* 5.3 Other illegal code positions */ /* BMP noncharacters */ { /* \U+FFFE */ - "\"\xEF\xBF\xBE\"", - "\xEF\xBF\xBE", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xBF\xBE", + NULL, + "\\uFFFD", }, { /* \U+FFFF */ - "\"\xEF\xBF\xBF\"", - "\xEF\xBF\xBF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xBF\xBF", + NULL, + "\\uFFFD", }, { /* U+FDD0 */ - "\"\xEF\xB7\x90\"", - "\xEF\xB7\x90", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xB7\x90", + NULL, + "\\uFFFD", }, { /* U+FDEF */ - "\"\xEF\xB7\xAF\"", - "\xEF\xB7\xAF", /* bug: not corrected */ - "\"\\uFFFD\"", + "\xEF\xB7\xAF", + NULL, + "\\uFFFD", }, /* Plane 1 .. 16 noncharacters */ { /* U+1FFFE U+1FFFF U+2FFFE U+2FFFF ... U+10FFFE U+10FFFF */ - "\"\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF" - "\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF" - "\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF" - "\xF1\x8F\xBF\xBE\xF1\x8F\xBF\xBF" - "\xF1\x9F\xBF\xBE\xF1\x9F\xBF\xBF" - "\xF1\xAF\xBF\xBE\xF1\xAF\xBF\xBF" - "\xF1\xBF\xBF\xBE\xF1\xBF\xBF\xBF" - "\xF2\x8F\xBF\xBE\xF2\x8F\xBF\xBF" - "\xF2\x9F\xBF\xBE\xF2\x9F\xBF\xBF" - "\xF2\xAF\xBF\xBE\xF2\xAF\xBF\xBF" - "\xF2\xBF\xBF\xBE\xF2\xBF\xBF\xBF" - "\xF3\x8F\xBF\xBE\xF3\x8F\xBF\xBF" - "\xF3\x9F\xBF\xBE\xF3\x9F\xBF\xBF" - "\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF" - "\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF" - "\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF\"", - /* bug: not corrected */ "\xF0\x9F\xBF\xBE\xF0\x9F\xBF\xBF" "\xF0\xAF\xBF\xBE\xF0\xAF\xBF\xBF" "\xF0\xBF\xBF\xBE\xF0\xBF\xBF\xBF" @@ -797,79 +730,64 @@ static void utf8_string(void) "\xF3\xAF\xBF\xBE\xF3\xAF\xBF\xBF" "\xF3\xBF\xBF\xBE\xF3\xBF\xBF\xBF" "\xF4\x8F\xBF\xBE\xF4\x8F\xBF\xBF", - "\"\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + NULL, "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" - "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\"", + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD" + "\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD\\uFFFD", }, {} }; - int i; - QObject *obj; + int i, j; QString *str; - const char *json_in, *utf8_out, *utf8_in, *json_out; + const char *json_in, *utf8_out, *utf8_in, *json_out, *tail; + char *end, *in, *jstr; for (i = 0; test_cases[i].json_in; i++) { - json_in = test_cases[i].json_in; - utf8_out = test_cases[i].utf8_out; - utf8_in = test_cases[i].utf8_in ?: test_cases[i].utf8_out; - json_out = test_cases[i].json_out ?: test_cases[i].json_in; + for (j = 0; j < 2; j++) { + json_in = test_cases[i].json_in; + utf8_out = test_cases[i].utf8_out; + utf8_in = test_cases[i].utf8_out ?: test_cases[i].json_in; + json_out = test_cases[i].json_out ?: test_cases[i].json_in; - obj = qobject_from_json(json_in, utf8_out ? &error_abort : NULL); - if (utf8_out) { - str = qobject_to(QString, obj); - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); - } else { - g_assert(!obj); + /* Parse @json_in, expect @utf8_out */ + if (utf8_out) { + str = from_json_str(json_in, j, &error_abort); + g_assert_cmpstr(qstring_get_try_str(str), ==, utf8_out); + qobject_unref(str); + } else { + str = from_json_str(json_in, j, NULL); + g_assert(!str); + /* + * Failure may be due to any sequence, but *all* sequences + * are expected to fail. Test each one in isolation. + */ + for (tail = json_in; *tail; tail = end) { + mod_utf8_codepoint(tail, 6, &end); + if (*end == ' ') { + end++; + } + in = strndup(tail, end - tail); + str = from_json_str(in, j, NULL); + g_assert(!str); + g_free(in); + } + } + + /* Unparse @utf8_in, expect @json_out */ + str = qstring_from_str(utf8_in); + jstr = to_json_str(str); + g_assert_cmpstr(jstr, ==, json_out); + qobject_unref(str); + g_free(jstr); + + /* Parse @json_out right back, unless it has replacements */ + if (!strstr(json_out, "\\uFFFD")) { + str = from_json_str(json_out, j, &error_abort); + g_assert_cmpstr(qstring_get_try_str(str), ==, utf8_in); + qobject_unref(str); + } } - qobject_unref(obj); - - obj = QOBJECT(qstring_from_str(utf8_in)); - str = qobject_to_json(obj); - if (json_out) { - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, json_out); - } else { - g_assert(!str); - } - qobject_unref(str); - qobject_unref(obj); - - /* - * Disabled, because qobject_from_json() is buggy, and I can't - * be bothered to add the expected incorrect results. - * FIXME Enable once these bugs have been fixed. - */ - if (0 && json_out != json_in) { - obj = qobject_from_json(json_out, &error_abort); - str = qobject_to(QString, obj); - g_assert(str); - g_assert_cmpstr(qstring_get_str(str), ==, utf8_out); - } - } -} - -static void vararg_string(void) -{ - int i; - struct { - const char *decoded; - } test_cases[] = { - { "hello world" }, - { "the quick brown fox jumped over the fence" }, - {} - }; - - for (i = 0; test_cases[i].decoded; i++) { - QString *str; - - str = qobject_to(QString, - qobject_from_jsonf("%s", test_cases[i].decoded)); - g_assert(str); - g_assert(strcmp(qstring_get_str(str), test_cases[i].decoded) == 0); - - qobject_unref(str); } } @@ -990,29 +908,6 @@ static void float_number(void) } } -static void vararg_number(void) -{ - QNum *qnum; - int value = 0x2342; - long long value_ll = 0x2342342343LL; - double valuef = 2.323423423; - int64_t val; - - qnum = qobject_to(QNum, qobject_from_jsonf("%d", value)); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, value); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf("%lld", value_ll)); - g_assert(qnum_get_try_int(qnum, &val)); - g_assert_cmpint(val, ==, value_ll); - qobject_unref(qnum); - - qnum = qobject_to(QNum, qobject_from_jsonf("%f", valuef)); - g_assert(qnum_get_double(qnum) == valuef); - qobject_unref(qnum); -} - static void keyword_literal(void) { QObject *obj; @@ -1042,17 +937,6 @@ static void keyword_literal(void) qobject_unref(qbool); - qbool = qobject_to(QBool, qobject_from_jsonf("%i", false)); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == false); - qobject_unref(qbool); - - /* Test that non-zero values other than 1 get collapsed to true */ - qbool = qobject_to(QBool, qobject_from_jsonf("%i", 2)); - g_assert(qbool); - g_assert(qbool_get_bool(qbool) == true); - qobject_unref(qbool); - obj = qobject_from_json("null", &error_abort); g_assert(obj != NULL); g_assert(qobject_type(obj) == QTYPE_QNULL); @@ -1064,6 +948,110 @@ static void keyword_literal(void) qobject_unref(null); } +static void interpolation_valid(void) +{ + long long value_lld = 0x123456789abcdefLL; + int64_t value_d64 = value_lld; + long value_ld = (long)value_lld; + int value_d = (int)value_lld; + unsigned long long value_llu = 0xfedcba9876543210ULL; + uint64_t value_u64 = value_llu; + unsigned long value_lu = (unsigned long)value_llu; + unsigned value_u = (unsigned)value_llu; + double value_f = 2.323423423; + const char *value_s = "hello world"; + QObject *value_p = QOBJECT(qnull()); + QBool *qbool; + QNum *qnum; + QString *qstr; + QObject *qobj; + + /* bool */ + + qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", false)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == false); + qobject_unref(qbool); + + /* Test that non-zero values other than 1 get collapsed to true */ + qbool = qobject_to(QBool, qobject_from_jsonf_nofail("%i", 2)); + g_assert(qbool); + g_assert(qbool_get_bool(qbool) == true); + qobject_unref(qbool); + + /* number */ + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%d", value_d)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_d); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%ld", value_ld)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_ld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lld", value_lld)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRId64, value_d64)); + g_assert_cmpint(qnum_get_int(qnum), ==, value_lld); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%u", value_u)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_u); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%lu", value_lu)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_lu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%llu", value_llu)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%" PRIu64, value_u64)); + g_assert_cmpuint(qnum_get_uint(qnum), ==, value_llu); + qobject_unref(qnum); + + qnum = qobject_to(QNum, qobject_from_jsonf_nofail("%f", value_f)); + g_assert(qnum_get_double(qnum) == value_f); + qobject_unref(qnum); + + /* string */ + + qstr = qobject_to(QString, + qobject_from_jsonf_nofail("%s", value_s)); + g_assert_cmpstr(qstring_get_try_str(qstr), ==, value_s); + qobject_unref(qstr); + + /* object */ + + qobj = qobject_from_jsonf_nofail("%p", value_p); + g_assert(qobj == value_p); +} + +static void interpolation_unknown(void) +{ + if (g_test_subprocess()) { + qobject_from_jsonf_nofail("%x", 666); + } + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*" + "invalid interpolation '%x'*"); +} + +static void interpolation_string(void) +{ + if (g_test_subprocess()) { + qobject_from_jsonf_nofail("['%s', %s]", "eins", "zwei"); + } + g_test_trap_subprocess(NULL, 0, 0); + g_test_trap_assert_failed(); + g_test_trap_assert_stderr("*Unexpected error*" + "can't interpolate into string*"); +} + static void simple_dict(void) { int i; @@ -1235,7 +1223,7 @@ static void simple_whitespace(void) })), }, { - .encoded = " [ 43 , { 'h' : 'b' }, [ ], 42 ]", + .encoded = "\t[ 43 , { 'h' : 'b' },\r\n\t[ ], 42 ]\n", .decoded = QLIT_QLIST(((QLitObject[]){ QLIT_QNUM(43), QLIT_QDICT(((QLitDictEntry[]){ @@ -1282,13 +1270,13 @@ static void simple_whitespace(void) } } -static void simple_varargs(void) +static void simple_interpolation(void) { QObject *embedded_obj; QObject *obj; QLitObject decoded = QLIT_QLIST(((QLitObject[]){ QLIT_QNUM(1), - QLIT_QNUM(2), + QLIT_QSTR("100%"), QLIT_QLIST(((QLitObject[]){ QLIT_QNUM(32), QLIT_QNUM(42), @@ -1298,7 +1286,7 @@ static void simple_varargs(void) embedded_obj = qobject_from_json("[32, 42]", &error_abort); g_assert(embedded_obj != NULL); - obj = qobject_from_jsonf("[%d, 2, %p]", 1, embedded_obj); + obj = qobject_from_jsonf_nofail("[%d, '100%%', %p]", 1, embedded_obj); g_assert(qlit_equal_qobject(&decoded, obj)); qobject_unref(obj); @@ -1306,8 +1294,52 @@ static void simple_varargs(void) static void empty_input(void) { - const char *empty = ""; - QObject *obj = qobject_from_json(empty, &error_abort); + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void blank_input(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("\n ", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + +static void junk_input(void) +{ + /* Note: junk within strings is covered elsewhere */ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("@", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("{\x01", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("[0\xFF]", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("00", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("[1e", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("truer", &err); + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1315,7 +1347,7 @@ static void unterminated_string(void) { Error *err = NULL; QObject *obj = qobject_from_json("\"abc", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1323,7 +1355,7 @@ static void unterminated_sq_string(void) { Error *err = NULL; QObject *obj = qobject_from_json("'abc", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1331,7 +1363,7 @@ static void unterminated_escape(void) { Error *err = NULL; QObject *obj = qobject_from_json("\"abc\\\"", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1339,7 +1371,7 @@ static void unterminated_array(void) { Error *err = NULL; QObject *obj = qobject_from_json("[32", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1347,7 +1379,7 @@ static void unterminated_array_comma(void) { Error *err = NULL; QObject *obj = qobject_from_json("[32,", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1363,7 +1395,7 @@ static void unterminated_dict(void) { Error *err = NULL; QObject *obj = qobject_from_json("{'abc':32", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1371,7 +1403,7 @@ static void unterminated_dict_comma(void) { Error *err = NULL; QObject *obj = qobject_from_json("{'abc':32,", &err); - g_assert(!err); /* BUG */ + error_free_or_abort(&err); g_assert(obj == NULL); } @@ -1417,32 +1449,48 @@ static void limits_nesting(void) g_assert(obj == NULL); } +static void multiple_values(void) +{ + Error *err = NULL; + QObject *obj; + + obj = qobject_from_json("false true", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); + + obj = qobject_from_json("} true", &err); + error_free_or_abort(&err); + g_assert(obj == NULL); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - g_test_add_func("/literals/string/simple", simple_string); g_test_add_func("/literals/string/escaped", escaped_string); + g_test_add_func("/literals/string/quotes", string_with_quotes); g_test_add_func("/literals/string/utf8", utf8_string); - g_test_add_func("/literals/string/single_quote", single_quote_string); - g_test_add_func("/literals/string/vararg", vararg_string); g_test_add_func("/literals/number/simple", simple_number); g_test_add_func("/literals/number/large", large_number); g_test_add_func("/literals/number/float", float_number); - g_test_add_func("/literals/number/vararg", vararg_number); g_test_add_func("/literals/keyword", keyword_literal); + g_test_add_func("/literals/interpolation/valid", interpolation_valid); + g_test_add_func("/literals/interpolation/unkown", interpolation_unknown); + g_test_add_func("/literals/interpolation/string", interpolation_string); + g_test_add_func("/dicts/simple_dict", simple_dict); g_test_add_func("/dicts/large_dict", large_dict); g_test_add_func("/lists/simple_list", simple_list); - g_test_add_func("/whitespace/simple_whitespace", simple_whitespace); + g_test_add_func("/mixed/simple_whitespace", simple_whitespace); + g_test_add_func("/mixed/interpolation", simple_interpolation); - g_test_add_func("/varargs/simple_varargs", simple_varargs); - - g_test_add_func("/errors/empty_input", empty_input); + g_test_add_func("/errors/empty", empty_input); + g_test_add_func("/errors/blank", blank_input); + g_test_add_func("/errors/junk", junk_input); g_test_add_func("/errors/unterminated/string", unterminated_string); g_test_add_func("/errors/unterminated/escape", unterminated_escape); g_test_add_func("/errors/unterminated/sq_string", unterminated_sq_string); @@ -1454,6 +1502,7 @@ int main(int argc, char **argv) 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); + g_test_add_func("/errors/multiple_values", multiple_values); return g_test_run(); } diff --git a/tests/check-qom-interface.c b/tests/check-qom-interface.c index f87c9aaa8a..2177f0dce5 100644 --- a/tests/check-qom-interface.c +++ b/tests/check-qom-interface.c @@ -23,9 +23,7 @@ #define TEST_IF(obj) \ INTERFACE_CHECK(TestIf, (obj), TYPE_TEST_IF) -typedef struct TestIf { - Object parent_obj; -} TestIf; +typedef struct TestIf TestIf; typedef struct TestIfClass { InterfaceClass parent_class; diff --git a/tests/check-qom-proplist.c b/tests/check-qom-proplist.c index 92898e1520..a8b2958e6e 100644 --- a/tests/check-qom-proplist.c +++ b/tests/check-qom-proplist.c @@ -125,10 +125,13 @@ static char *dummy_get_sv(Object *obj, static void dummy_init(Object *obj) { + Error *err = NULL; + object_property_add_bool(obj, "bv", dummy_get_bv, dummy_set_bv, - NULL); + &err); + error_free_or_abort(&err); } @@ -517,6 +520,32 @@ static void test_dummy_getenum(void) } +static void test_dummy_prop_iterator(ObjectPropertyIterator *iter) +{ + bool seenbv = false, seensv = false, seenav = false, seentype = false; + ObjectProperty *prop; + + while ((prop = object_property_iter_next(iter))) { + if (!seenbv && g_str_equal(prop->name, "bv")) { + seenbv = true; + } else if (!seensv && g_str_equal(prop->name, "sv")) { + seensv = true; + } else if (!seenav && g_str_equal(prop->name, "av")) { + seenav = true; + } else if (!seentype && 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); +} + static void test_dummy_iterator(void) { Object *parent = object_get_objects_root(); @@ -529,35 +558,21 @@ static void test_dummy_iterator(void) "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); - + test_dummy_prop_iterator(&iter); object_unparent(OBJECT(dobj)); } +static void test_dummy_class_iterator(void) +{ + ObjectPropertyIterator iter; + ObjectClass *klass = object_class_by_name(TYPE_DUMMY); + + object_class_property_iter_init(&iter, klass); + test_dummy_prop_iterator(&iter); +} static void test_dummy_delchild(void) { @@ -629,6 +644,7 @@ int main(int argc, char **argv) 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/class_iterator", test_dummy_class_iterator); g_test_add_func("/qom/proplist/delchild", test_dummy_delchild); g_test_add_func("/qom/resolve/partial", test_qom_partial_path); diff --git a/tests/cpu-plug-test.c b/tests/cpu-plug-test.c index 5f39ba0df3..668f00144e 100644 --- a/tests/cpu-plug-test.c +++ b/tests/cpu-plug-test.c @@ -32,12 +32,12 @@ static void test_plug_with_cpu_add(gconstpointer data) unsigned int i; args = g_strdup_printf("-machine %s -cpu %s " - "-smp sockets=%u,cores=%u,threads=%u,maxcpus=%u", + "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", s->machine, s->cpu_model, s->sockets, s->cores, s->threads, s->maxcpus); qtest_start(args); - for (i = s->sockets * s->cores * s->threads; i < s->maxcpus; i++) { + for (i = 1; i < s->maxcpus; i++) { response = qmp("{ 'execute': 'cpu-add'," " 'arguments': { 'id': %d } }", i); g_assert(response); @@ -56,7 +56,7 @@ static void test_plug_without_cpu_add(gconstpointer data) QDict *response; args = g_strdup_printf("-machine %s -cpu %s " - "-smp sockets=%u,cores=%u,threads=%u,maxcpus=%u", + "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", s->machine, s->cpu_model, s->sockets, s->cores, s->threads, s->maxcpus); qtest_start(args); @@ -79,17 +79,18 @@ static void test_plug_with_device_add_x86(gconstpointer data) unsigned int s, c, t; args = g_strdup_printf("-machine %s -cpu %s " - "-smp sockets=%u,cores=%u,threads=%u,maxcpus=%u", + "-smp 1,sockets=%u,cores=%u,threads=%u,maxcpus=%u", td->machine, td->cpu_model, td->sockets, td->cores, td->threads, td->maxcpus); qtest_start(args); - for (s = td->sockets; s < td->maxcpus / td->cores / td->threads; s++) { + for (s = 1; s < td->sockets; s++) { for (c = 0; c < td->cores; c++) { for (t = 0; t < td->threads; t++) { char *id = g_strdup_printf("id-%i-%i-%i", s, c, t); - qtest_qmp_device_add(td->device_model, id, "'socket-id':'%i', " - "'core-id':'%i', 'thread-id':'%i'", + qtest_qmp_device_add(td->device_model, id, + "{'socket-id':%u, 'core-id':%u," + " 'thread-id':%u}", s, c, t); g_free(id); } @@ -112,9 +113,9 @@ static void test_plug_with_device_add_coreid(gconstpointer data) td->sockets, td->cores, td->threads, td->maxcpus); qtest_start(args); - for (c = td->cores; c < td->maxcpus / td->sockets / td->threads; c++) { + for (c = 1; c < td->cores; c++) { char *id = g_strdup_printf("id-%i", c); - qtest_qmp_device_add(td->device_model, id, "'core-id':'%i'", c); + qtest_qmp_device_add(td->device_model, id, "{'core-id':%u}", c); g_free(id); } @@ -147,7 +148,7 @@ static void add_pc_test_case(const char *mname) data->sockets = 1; data->cores = 3; data->threads = 2; - data->maxcpus = data->sockets * data->cores * data->threads * 2; + data->maxcpus = data->sockets * data->cores * data->threads; if (g_str_has_suffix(mname, "-1.4") || (strcmp(mname, "pc-1.3") == 0) || (strcmp(mname, "pc-1.2") == 0) || @@ -156,9 +157,7 @@ static void add_pc_test_case(const char *mname) (strcmp(mname, "pc-0.15") == 0) || (strcmp(mname, "pc-0.14") == 0) || (strcmp(mname, "pc-0.13") == 0) || - (strcmp(mname, "pc-0.12") == 0) || - (strcmp(mname, "pc-0.11") == 0) || - (strcmp(mname, "pc-0.10") == 0)) { + (strcmp(mname, "pc-0.12") == 0)) { path = g_strdup_printf("cpu-plug/%s/init/%ux%ux%u&maxcpus=%u", mname, data->sockets, data->cores, data->threads, data->maxcpus); @@ -202,7 +201,7 @@ static void add_pseries_test_case(const char *mname) data->sockets = 2; data->cores = 3; data->threads = 1; - data->maxcpus = data->sockets * data->cores * data->threads * 2; + data->maxcpus = data->sockets * data->cores * data->threads; path = g_strdup_printf("cpu-plug/%s/device-add/%ux%ux%u&maxcpus=%u", mname, data->sockets, data->cores, @@ -228,7 +227,7 @@ static void add_s390x_test_case(const char *mname) data->sockets = 1; data->cores = 3; data->threads = 1; - data->maxcpus = data->sockets * data->cores * data->threads * 2; + data->maxcpus = data->sockets * data->cores * data->threads; data2 = g_memdup(data, sizeof(PlugTestData)); data2->machine = g_strdup(data->machine); @@ -256,11 +255,11 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_cb_for_every_machine(add_pc_test_case); + qtest_cb_for_every_machine(add_pc_test_case, g_test_quick()); } else if (g_str_equal(arch, "ppc64")) { - qtest_cb_for_every_machine(add_pseries_test_case); + qtest_cb_for_every_machine(add_pseries_test_case, g_test_quick()); } else if (g_str_equal(arch, "s390x")) { - qtest_cb_for_every_machine(add_s390x_test_case); + qtest_cb_for_every_machine(add_s390x_test_case, g_test_quick()); } return g_test_run(); diff --git a/tests/crypto-tls-x509-helpers.h b/tests/crypto-tls-x509-helpers.h index 921341c649..88c30d7c94 100644 --- a/tests/crypto-tls-x509-helpers.h +++ b/tests/crypto-tls-x509-helpers.h @@ -22,8 +22,7 @@ #include #if !(defined WIN32) && \ - defined(CONFIG_TASN1) && \ - (LIBGNUTLS_VERSION_NUMBER >= 0x020600) + defined(CONFIG_TASN1) # define QCRYPTO_HAVE_TLS_TEST_SUPPORT #endif diff --git a/tests/acpi-test-data/pc/APIC b/tests/data/acpi/pc/APIC similarity index 100% rename from tests/acpi-test-data/pc/APIC rename to tests/data/acpi/pc/APIC diff --git a/tests/acpi-test-data/pc/APIC.cphp b/tests/data/acpi/pc/APIC.cphp similarity index 100% rename from tests/acpi-test-data/pc/APIC.cphp rename to tests/data/acpi/pc/APIC.cphp diff --git a/tests/acpi-test-data/pc/APIC.dimmpxm b/tests/data/acpi/pc/APIC.dimmpxm similarity index 100% rename from tests/acpi-test-data/pc/APIC.dimmpxm rename to tests/data/acpi/pc/APIC.dimmpxm diff --git a/tests/acpi-test-data/pc/DSDT b/tests/data/acpi/pc/DSDT similarity index 93% rename from tests/acpi-test-data/pc/DSDT rename to tests/data/acpi/pc/DSDT index 99f05a5027..c6adfe32d5 100644 Binary files a/tests/acpi-test-data/pc/DSDT and b/tests/data/acpi/pc/DSDT differ diff --git a/tests/acpi-test-data/pc/DSDT.bridge b/tests/data/acpi/pc/DSDT.bridge similarity index 94% rename from tests/acpi-test-data/pc/DSDT.bridge rename to tests/data/acpi/pc/DSDT.bridge index cf23343e64..f01fa3ad4e 100644 Binary files a/tests/acpi-test-data/pc/DSDT.bridge and b/tests/data/acpi/pc/DSDT.bridge differ diff --git a/tests/acpi-test-data/pc/DSDT.cphp b/tests/data/acpi/pc/DSDT.cphp similarity index 95% rename from tests/acpi-test-data/pc/DSDT.cphp rename to tests/data/acpi/pc/DSDT.cphp index c99c49f437..3295d81c7f 100644 Binary files a/tests/acpi-test-data/pc/DSDT.cphp and b/tests/data/acpi/pc/DSDT.cphp differ diff --git a/tests/acpi-test-data/pc/DSDT.dimmpxm b/tests/data/acpi/pc/DSDT.dimmpxm similarity index 89% rename from tests/acpi-test-data/pc/DSDT.dimmpxm rename to tests/data/acpi/pc/DSDT.dimmpxm index 38661cb13e..ad2800de67 100644 Binary files a/tests/acpi-test-data/pc/DSDT.dimmpxm and b/tests/data/acpi/pc/DSDT.dimmpxm differ diff --git a/tests/acpi-test-data/pc/DSDT.ipmikcs b/tests/data/acpi/pc/DSDT.ipmikcs similarity index 93% rename from tests/acpi-test-data/pc/DSDT.ipmikcs rename to tests/data/acpi/pc/DSDT.ipmikcs index 5e970fda72..2633a8cecf 100644 Binary files a/tests/acpi-test-data/pc/DSDT.ipmikcs and b/tests/data/acpi/pc/DSDT.ipmikcs differ diff --git a/tests/acpi-test-data/pc/DSDT.memhp b/tests/data/acpi/pc/DSDT.memhp similarity index 88% rename from tests/acpi-test-data/pc/DSDT.memhp rename to tests/data/acpi/pc/DSDT.memhp index 1fe6871aa2..9e75ac96e1 100644 Binary files a/tests/acpi-test-data/pc/DSDT.memhp and b/tests/data/acpi/pc/DSDT.memhp differ diff --git a/tests/acpi-test-data/pc/DSDT.numamem b/tests/data/acpi/pc/DSDT.numamem similarity index 93% rename from tests/acpi-test-data/pc/DSDT.numamem rename to tests/data/acpi/pc/DSDT.numamem index 224cfdd9e9..71a975b3e2 100644 Binary files a/tests/acpi-test-data/pc/DSDT.numamem and b/tests/data/acpi/pc/DSDT.numamem differ diff --git a/tests/acpi-test-data/pc/FACP b/tests/data/acpi/pc/FACP similarity index 100% rename from tests/acpi-test-data/pc/FACP rename to tests/data/acpi/pc/FACP diff --git a/tests/acpi-test-data/pc/FACS b/tests/data/acpi/pc/FACS similarity index 100% rename from tests/acpi-test-data/pc/FACS rename to tests/data/acpi/pc/FACS diff --git a/tests/acpi-test-data/pc/HPET b/tests/data/acpi/pc/HPET similarity index 100% rename from tests/acpi-test-data/pc/HPET rename to tests/data/acpi/pc/HPET diff --git a/tests/acpi-test-data/pc/NFIT.dimmpxm b/tests/data/acpi/pc/NFIT.dimmpxm similarity index 100% rename from tests/acpi-test-data/pc/NFIT.dimmpxm rename to tests/data/acpi/pc/NFIT.dimmpxm diff --git a/tests/acpi-test-data/pc/SLIT.cphp b/tests/data/acpi/pc/SLIT.cphp similarity index 100% rename from tests/acpi-test-data/pc/SLIT.cphp rename to tests/data/acpi/pc/SLIT.cphp diff --git a/tests/acpi-test-data/pc/SLIT.memhp b/tests/data/acpi/pc/SLIT.memhp similarity index 100% rename from tests/acpi-test-data/pc/SLIT.memhp rename to tests/data/acpi/pc/SLIT.memhp diff --git a/tests/acpi-test-data/pc/SRAT.cphp b/tests/data/acpi/pc/SRAT.cphp similarity index 100% rename from tests/acpi-test-data/pc/SRAT.cphp rename to tests/data/acpi/pc/SRAT.cphp diff --git a/tests/acpi-test-data/pc/SRAT.dimmpxm b/tests/data/acpi/pc/SRAT.dimmpxm similarity index 54% rename from tests/acpi-test-data/pc/SRAT.dimmpxm rename to tests/data/acpi/pc/SRAT.dimmpxm index 5aa6f693ef..f5c0267ea2 100644 Binary files a/tests/acpi-test-data/pc/SRAT.dimmpxm and b/tests/data/acpi/pc/SRAT.dimmpxm differ diff --git a/tests/acpi-test-data/pc/SRAT.memhp b/tests/data/acpi/pc/SRAT.memhp similarity index 55% rename from tests/acpi-test-data/pc/SRAT.memhp rename to tests/data/acpi/pc/SRAT.memhp index 5de8a100a4..e508b4ae3c 100644 Binary files a/tests/acpi-test-data/pc/SRAT.memhp and b/tests/data/acpi/pc/SRAT.memhp differ diff --git a/tests/acpi-test-data/pc/SRAT.numamem b/tests/data/acpi/pc/SRAT.numamem similarity index 100% rename from tests/acpi-test-data/pc/SRAT.numamem rename to tests/data/acpi/pc/SRAT.numamem diff --git a/tests/acpi-test-data/pc/SSDT.dimmpxm b/tests/data/acpi/pc/SSDT.dimmpxm similarity index 100% rename from tests/acpi-test-data/pc/SSDT.dimmpxm rename to tests/data/acpi/pc/SSDT.dimmpxm diff --git a/tests/acpi-test-data/q35/APIC b/tests/data/acpi/q35/APIC similarity index 100% rename from tests/acpi-test-data/q35/APIC rename to tests/data/acpi/q35/APIC diff --git a/tests/acpi-test-data/q35/APIC.cphp b/tests/data/acpi/q35/APIC.cphp similarity index 100% rename from tests/acpi-test-data/q35/APIC.cphp rename to tests/data/acpi/q35/APIC.cphp diff --git a/tests/acpi-test-data/q35/APIC.dimmpxm b/tests/data/acpi/q35/APIC.dimmpxm similarity index 100% rename from tests/acpi-test-data/q35/APIC.dimmpxm rename to tests/data/acpi/q35/APIC.dimmpxm diff --git a/tests/acpi-test-data/q35/DSDT b/tests/data/acpi/q35/DSDT similarity index 96% rename from tests/acpi-test-data/q35/DSDT rename to tests/data/acpi/q35/DSDT index aa402cca66..7576ffcd05 100644 Binary files a/tests/acpi-test-data/q35/DSDT and b/tests/data/acpi/q35/DSDT differ diff --git a/tests/acpi-test-data/q35/DSDT.bridge b/tests/data/acpi/q35/DSDT.bridge similarity index 95% rename from tests/acpi-test-data/q35/DSDT.bridge rename to tests/data/acpi/q35/DSDT.bridge index fc3e79c583..c623cc5d72 100644 Binary files a/tests/acpi-test-data/q35/DSDT.bridge and b/tests/data/acpi/q35/DSDT.bridge differ diff --git a/tests/acpi-test-data/q35/DSDT.cphp b/tests/data/acpi/q35/DSDT.cphp similarity index 96% rename from tests/acpi-test-data/q35/DSDT.cphp rename to tests/data/acpi/q35/DSDT.cphp index fd3cb34218..7ac526e466 100644 Binary files a/tests/acpi-test-data/q35/DSDT.cphp and b/tests/data/acpi/q35/DSDT.cphp differ diff --git a/tests/acpi-test-data/q35/DSDT.dimmpxm b/tests/data/acpi/q35/DSDT.dimmpxm similarity index 92% rename from tests/acpi-test-data/q35/DSDT.dimmpxm rename to tests/data/acpi/q35/DSDT.dimmpxm index 14904e8ea2..7177116a21 100644 Binary files a/tests/acpi-test-data/q35/DSDT.dimmpxm and b/tests/data/acpi/q35/DSDT.dimmpxm differ diff --git a/tests/acpi-test-data/q35/DSDT.ipmibt b/tests/data/acpi/q35/DSDT.ipmibt similarity index 96% rename from tests/acpi-test-data/q35/DSDT.ipmibt rename to tests/data/acpi/q35/DSDT.ipmibt index 332237529e..c7f431f058 100644 Binary files a/tests/acpi-test-data/q35/DSDT.ipmibt and b/tests/data/acpi/q35/DSDT.ipmibt differ diff --git a/tests/acpi-test-data/q35/DSDT.memhp b/tests/data/acpi/q35/DSDT.memhp similarity index 91% rename from tests/acpi-test-data/q35/DSDT.memhp rename to tests/data/acpi/q35/DSDT.memhp index f0a27e1a30..0235461391 100644 Binary files a/tests/acpi-test-data/q35/DSDT.memhp and b/tests/data/acpi/q35/DSDT.memhp differ diff --git a/tests/data/acpi/q35/DSDT.mmio64 b/tests/data/acpi/q35/DSDT.mmio64 new file mode 100644 index 0000000000..f60ee77fb4 Binary files /dev/null and b/tests/data/acpi/q35/DSDT.mmio64 differ diff --git a/tests/acpi-test-data/q35/DSDT.numamem b/tests/data/acpi/q35/DSDT.numamem similarity index 95% rename from tests/acpi-test-data/q35/DSDT.numamem rename to tests/data/acpi/q35/DSDT.numamem index 8c9fa445b0..6c0d4f2bcb 100644 Binary files a/tests/acpi-test-data/q35/DSDT.numamem and b/tests/data/acpi/q35/DSDT.numamem differ diff --git a/tests/acpi-test-data/q35/FACP b/tests/data/acpi/q35/FACP similarity index 100% rename from tests/acpi-test-data/q35/FACP rename to tests/data/acpi/q35/FACP diff --git a/tests/acpi-test-data/q35/FACS b/tests/data/acpi/q35/FACS similarity index 100% rename from tests/acpi-test-data/q35/FACS rename to tests/data/acpi/q35/FACS diff --git a/tests/acpi-test-data/q35/HPET b/tests/data/acpi/q35/HPET similarity index 100% rename from tests/acpi-test-data/q35/HPET rename to tests/data/acpi/q35/HPET diff --git a/tests/acpi-test-data/q35/MCFG b/tests/data/acpi/q35/MCFG similarity index 100% rename from tests/acpi-test-data/q35/MCFG rename to tests/data/acpi/q35/MCFG diff --git a/tests/acpi-test-data/q35/NFIT.dimmpxm b/tests/data/acpi/q35/NFIT.dimmpxm similarity index 100% rename from tests/acpi-test-data/q35/NFIT.dimmpxm rename to tests/data/acpi/q35/NFIT.dimmpxm diff --git a/tests/acpi-test-data/q35/SLIT.cphp b/tests/data/acpi/q35/SLIT.cphp similarity index 100% rename from tests/acpi-test-data/q35/SLIT.cphp rename to tests/data/acpi/q35/SLIT.cphp diff --git a/tests/acpi-test-data/q35/SLIT.memhp b/tests/data/acpi/q35/SLIT.memhp similarity index 100% rename from tests/acpi-test-data/q35/SLIT.memhp rename to tests/data/acpi/q35/SLIT.memhp diff --git a/tests/acpi-test-data/q35/SRAT.cphp b/tests/data/acpi/q35/SRAT.cphp similarity index 100% rename from tests/acpi-test-data/q35/SRAT.cphp rename to tests/data/acpi/q35/SRAT.cphp diff --git a/tests/acpi-test-data/q35/SRAT.dimmpxm b/tests/data/acpi/q35/SRAT.dimmpxm similarity index 54% rename from tests/acpi-test-data/q35/SRAT.dimmpxm rename to tests/data/acpi/q35/SRAT.dimmpxm index 5aa6f693ef..f5c0267ea2 100644 Binary files a/tests/acpi-test-data/q35/SRAT.dimmpxm and b/tests/data/acpi/q35/SRAT.dimmpxm differ diff --git a/tests/acpi-test-data/q35/SRAT.memhp b/tests/data/acpi/q35/SRAT.memhp similarity index 55% rename from tests/acpi-test-data/q35/SRAT.memhp rename to tests/data/acpi/q35/SRAT.memhp index 5de8a100a4..e508b4ae3c 100644 Binary files a/tests/acpi-test-data/q35/SRAT.memhp and b/tests/data/acpi/q35/SRAT.memhp differ diff --git a/tests/data/acpi/q35/SRAT.mmio64 b/tests/data/acpi/q35/SRAT.mmio64 new file mode 100644 index 0000000000..ac35f3dac4 Binary files /dev/null and b/tests/data/acpi/q35/SRAT.mmio64 differ diff --git a/tests/acpi-test-data/q35/SRAT.numamem b/tests/data/acpi/q35/SRAT.numamem similarity index 100% rename from tests/acpi-test-data/q35/SRAT.numamem rename to tests/data/acpi/q35/SRAT.numamem diff --git a/tests/acpi-test-data/q35/SSDT.dimmpxm b/tests/data/acpi/q35/SSDT.dimmpxm similarity index 100% rename from tests/acpi-test-data/q35/SSDT.dimmpxm rename to tests/data/acpi/q35/SSDT.dimmpxm diff --git a/tests/acpi-test-data/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh similarity index 85% rename from tests/acpi-test-data/rebuild-expected-aml.sh rename to tests/data/acpi/rebuild-expected-aml.sh index 11bf743914..abdff70a0d 100755 --- a/tests/acpi-test-data/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash # # Rebuild expected AML files for acpi unit-test @@ -32,5 +32,3 @@ fi TEST_ACPI_REBUILD_AML=y QTEST_QEMU_BINARY=$qemu tests/bios-tables-test echo "The files were rebuilt and can be added to git." -echo "However, if new files were created, please copy them manually" \ - "to tests/acpi-test-data/pc/ or tests/acpi-test-data/q35/ ." diff --git a/tests/data/hex-loader/test.hex b/tests/data/hex-loader/test.hex new file mode 100644 index 0000000000..008a90bd4d --- /dev/null +++ b/tests/data/hex-loader/test.hex @@ -0,0 +1,18 @@ +:020000040001F9 +:10000000000102030405060708090a0b0c0d0e0f78 +:10001000101112131415161718191a1b1c1d1e1f68 +:10002000202122232425262728292a2b2c2d2e2f58 +:10003000303132333435363738393a3b3c3d3e3f48 +:10004000404142434445464748494a4b4c4d4e4f38 +:10005000505152535455565758595a5b5c5d5e5f28 +:10006000606162636465666768696a6b6c6d6e6f18 +:10007000707172737475767778797a7b7c7d7e7f08 +:10008000808182838485868788898a8b8c8d8e8ff8 +:10009000909192939495969798999a9b9c9d9e9fe8 +:1000a000a0a1a2a3a4a5a6a7a8a9aaabacadaeafd8 +:1000b000b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc8 +:1000c000c0c1c2c3c4c5c6c7c8c9cacbcccdcecfb8 +:1000d000d0d1d2d3d4d5d6d7d8d9dadbdcdddedfa8 +:1000e000e0e1e2e3e4e5e6e7e8e9eaebecedeeef98 +:1000f000f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff88 +:00000001FF diff --git a/qdict-test-data.txt b/tests/data/qobject/qdict.txt similarity index 100% rename from qdict-test-data.txt rename to tests/data/qobject/qdict.txt diff --git a/tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2 new file mode 100644 index 0000000000..ac0b7b1b8f Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2 differ diff --git a/tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2 new file mode 100644 index 0000000000..d20fa7c819 Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.arm.iso.qcow2 differ diff --git a/tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2 new file mode 100644 index 0000000000..26c882baea Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.i386.iso.qcow2 differ diff --git a/tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2 new file mode 100644 index 0000000000..9ec3c1f20b Binary files /dev/null and b/tests/data/uefi-boot-images/bios-tables-test.x86_64.iso.qcow2 differ diff --git a/tests/decode/check.sh b/tests/decode/check.sh index 79a06c37cd..95445a0115 100755 --- a/tests/decode/check.sh +++ b/tests/decode/check.sh @@ -15,4 +15,10 @@ for i in err_*.decode; do fi done +for i in succ_*.decode; do + if ! $PYTHON $DECODETREE $i > /dev/null 2> /dev/null; then + echo FAIL:$i 1>&2 + fi +done + exit $E diff --git a/tests/decode/err_pattern_group_empty.decode b/tests/decode/err_pattern_group_empty.decode new file mode 100644 index 0000000000..abbff6b528 --- /dev/null +++ b/tests/decode/err_pattern_group_empty.decode @@ -0,0 +1,6 @@ +# 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. + +# empty groups are not allowed +{ +} diff --git a/tests/decode/err_pattern_group_ident1.decode b/tests/decode/err_pattern_group_ident1.decode new file mode 100644 index 0000000000..3e65fab2f9 --- /dev/null +++ b/tests/decode/err_pattern_group_ident1.decode @@ -0,0 +1,10 @@ +# 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. + +%sub1 0:8 + +# Make sure that indentation is enforced +{ + top 00000000 00000000 00000000 00000000 + sub1 00000000 00000000 00000000 ........ %sub1 +} diff --git a/tests/decode/err_pattern_group_ident2.decode b/tests/decode/err_pattern_group_ident2.decode new file mode 100644 index 0000000000..bc859233b1 --- /dev/null +++ b/tests/decode/err_pattern_group_ident2.decode @@ -0,0 +1,11 @@ +# 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. + +%sub1 0:8 + +# Make sure that indentation is enforced +{ + top 00000000 00000000 00000000 00000000 + sub1 00000000 00000000 00000000 ........ %sub1 +# comments are suposed to be indented +} diff --git a/tests/decode/err_pattern_group_nest1.decode b/tests/decode/err_pattern_group_nest1.decode new file mode 100644 index 0000000000..92e971c3c5 --- /dev/null +++ b/tests/decode/err_pattern_group_nest1.decode @@ -0,0 +1,13 @@ +# 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. + +%sub1 0:8 +%sub2 8:8 +%sub3 16:8 +%sub4 24:8 + +# Groups with no overlap are supposed to fail +{ + top 00000000 00000000 00000000 00000000 + sub4 ........ ........ ........ ........ %sub1 %sub2 %sub3 %sub4 +} diff --git a/tests/decode/err_pattern_group_overlap1.decode b/tests/decode/err_pattern_group_overlap1.decode new file mode 100644 index 0000000000..ebe3030d26 --- /dev/null +++ b/tests/decode/err_pattern_group_overlap1.decode @@ -0,0 +1,6 @@ +one 00000000000000000000000000000000 +{ + two 0000000000000000000000000000000 s:1 + three 000000000000000000000000000000 s:1 0 +} + diff --git a/tests/decode/err_width1.decode b/tests/decode/err_width1.decode new file mode 100644 index 0000000000..0c14f6d73b --- /dev/null +++ b/tests/decode/err_width1.decode @@ -0,0 +1,5 @@ +# 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. + +# Diagnose too many bits (33 of 32) +one 000000000000000000000000000000000 diff --git a/tests/decode/err_width2.decode b/tests/decode/err_width2.decode new file mode 100644 index 0000000000..47f0acf322 --- /dev/null +++ b/tests/decode/err_width2.decode @@ -0,0 +1,5 @@ +# 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. + +# Diagnose too few bits (31 of 32) +one 0000000000000000000000000000000 diff --git a/tests/decode/err_width3.decode b/tests/decode/err_width3.decode new file mode 100644 index 0000000000..c5fb6b3699 --- /dev/null +++ b/tests/decode/err_width3.decode @@ -0,0 +1,5 @@ +# 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. + +# Diagnose too many bits (33 of 32) +one 0 s:32 diff --git a/tests/decode/err_width4.decode b/tests/decode/err_width4.decode new file mode 100644 index 0000000000..1588a63698 --- /dev/null +++ b/tests/decode/err_width4.decode @@ -0,0 +1,5 @@ +# 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. + +# Diagnose too few bits (31 of 32) +one 0 s:30 diff --git a/tests/decode/succ_pattern_group_nest1.decode b/tests/decode/succ_pattern_group_nest1.decode new file mode 100644 index 0000000000..77b0f48b49 --- /dev/null +++ b/tests/decode/succ_pattern_group_nest1.decode @@ -0,0 +1,22 @@ +# 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. + +%sub1 0:8 +%sub2 8:8 +%sub3 16:8 +%sub4 24:7 + +# Make sure deep netsting works, as few targets will actually exercise it +{ + top 00000000 00000000 00000000 00000000 + { + sub1 00000000 00000000 00000000 ........ %sub1 + { + sub2 00000000 00000000 ........ ........ %sub1 %sub2 + { + sub3 00000000 ........ ........ ........ %sub1 %sub2 %sub3 + sub4 0....... ........ ........ ........ %sub1 %sub2 %sub3 %sub4 + } + } + } +} diff --git a/tests/device-introspect-test.c b/tests/device-introspect-test.c index 0b4f221c29..a25092dfaa 100644 --- a/tests/device-introspect-test.c +++ b/tests/device-introspect-test.c @@ -103,7 +103,14 @@ static QList *device_type_list(bool abstract) static void test_one_device(const char *type) { QDict *resp; - char *help, *qom_tree; + char *help; + char *qom_tree_start, *qom_tree_end; + char *qtree_start, *qtree_end; + + g_test_message("Testing device '%s'", type); + + qom_tree_start = hmp("info qom-tree"); + qtree_start = hmp("info qtree"); resp = qmp("{'execute': 'device-list-properties'," " 'arguments': {'typename': %s}}", @@ -115,10 +122,18 @@ static void test_one_device(const char *type) /* * Some devices leave dangling pointers in QOM behind. - * "info qom-tree" has a good chance at crashing then + * "info qom-tree" or "info qtree" have a good chance at crashing then. + * Also make sure that the tree did not change. */ - qom_tree = hmp("info qom-tree"); - g_free(qom_tree); + qom_tree_end = hmp("info qom-tree"); + g_assert_cmpstr(qom_tree_start, ==, qom_tree_end); + g_free(qom_tree_start); + g_free(qom_tree_end); + + qtree_end = hmp("info qtree"); + g_assert_cmpstr(qtree_start, ==, qtree_end); + g_free(qtree_start); + g_free(qtree_end); } static void test_device_intro_list(void) @@ -206,13 +221,13 @@ static void test_device_intro_abstract(void) qtest_end(); } -static void test_device_intro_concrete(void) +static void test_device_intro_concrete(const void *args) { QList *types; QListEntry *entry; const char *type; - qtest_start(common_args); + qtest_start(args); types = device_type_list(false); QLIST_FOREACH_ENTRY(types, entry) { @@ -224,6 +239,7 @@ static void test_device_intro_concrete(void) qobject_unref(types); qtest_end(); + g_free((void *)args); } static void test_abstract_interfaces(void) @@ -260,6 +276,26 @@ static void test_abstract_interfaces(void) qtest_end(); } +static void add_machine_test_case(const char *mname) +{ + char *path, *args; + + /* Ignore blacklisted machines */ + if (g_str_equal("xenfv", mname) || g_str_equal("xenpv", mname)) { + return; + } + + path = g_strdup_printf("device/introspect/concrete/defaults/%s", mname); + args = g_strdup_printf("-M %s", mname); + qtest_add_data_func(path, args, test_device_intro_concrete); + g_free(path); + + path = g_strdup_printf("device/introspect/concrete/nodefaults/%s", mname); + args = g_strdup_printf("-nodefaults -M %s", mname); + qtest_add_data_func(path, args, test_device_intro_concrete); + g_free(path); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -268,8 +304,13 @@ int main(int argc, char **argv) qtest_add_func("device/introspect/list-fields", test_qom_list_fields); 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); qtest_add_func("device/introspect/abstract-interfaces", test_abstract_interfaces); + if (g_test_quick()) { + qtest_add_data_func("device/introspect/concrete/defaults/none", + g_strdup(common_args), test_device_intro_concrete); + } else { + qtest_cb_for_every_machine(add_machine_test_case, true); + } return g_test_run(); } diff --git a/tests/device-plug-test.c b/tests/device-plug-test.c new file mode 100644 index 0000000000..318e422d51 --- /dev/null +++ b/tests/device-plug-test.c @@ -0,0 +1,178 @@ +/* + * QEMU device plug/unplug handling + * + * Copyright (C) 2019 Red Hat Inc. + * + * Authors: + * David Hildenbrand + * + * 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 "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qstring.h" + +static void device_del_start(QTestState *qtest, const char *id) +{ + qtest_qmp_send(qtest, + "{'execute': 'device_del', 'arguments': { 'id': %s } }", id); +} + +static void device_del_finish(QTestState *qtest) +{ + QDict *resp = qtest_qmp_receive(qtest); + + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static void device_del_request(QTestState *qtest, const char *id) +{ + device_del_start(qtest, id); + device_del_finish(qtest); +} + +static void system_reset(QTestState *qtest) +{ + QDict *resp; + + resp = qtest_qmp(qtest, "{'execute': 'system_reset'}"); + g_assert(qdict_haskey(resp, "return")); + qobject_unref(resp); +} + +static void wait_device_deleted_event(QTestState *qtest, const char *id) +{ + QDict *resp, *data; + QString *qstr; + + /* + * Other devices might get removed along with the removed device. Skip + * these. The device of interest will be the last one. + */ + for (;;) { + resp = qtest_qmp_eventwait_ref(qtest, "DEVICE_DELETED"); + data = qdict_get_qdict(resp, "data"); + if (!data || !qdict_get(data, "device")) { + qobject_unref(resp); + continue; + } + qstr = qobject_to(QString, qdict_get(data, "device")); + g_assert(qstr); + if (!strcmp(qstring_get_str(qstr), id)) { + qobject_unref(resp); + break; + } + qobject_unref(resp); + } +} + +static void test_pci_unplug_request(void) +{ + QTestState *qtest = qtest_initf("-device virtio-mouse-pci,id=dev0"); + + /* + * Request device removal. As the guest is not running, the request won't + * be processed. However during system reset, the removal will be + * handled, removing the device. + */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_ccw_unplug(void) +{ + QTestState *qtest = qtest_initf("-device virtio-balloon-ccw,id=dev0"); + + /* + * The DEVICE_DELETED events will be sent before the command + * completes. + */ + device_del_start(qtest, "dev0"); + wait_device_deleted_event(qtest, "dev0"); + device_del_finish(qtest); + + qtest_quit(qtest); +} + +static void test_spapr_cpu_unplug_request(void) +{ + QTestState *qtest; + + qtest = qtest_initf("-cpu power9_v2.0 -smp 1,maxcpus=2 " + "-device power9_v2.0-spapr-cpu-core,core-id=1,id=dev0"); + + /* similar to test_pci_unplug_request */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_spapr_memory_unplug_request(void) +{ + QTestState *qtest; + + qtest = qtest_initf("-m 256M,slots=1,maxmem=768M " + "-object memory-backend-ram,id=mem0,size=512M " + "-device pc-dimm,id=dev0,memdev=mem0"); + + /* similar to test_pci_unplug_request */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +static void test_spapr_phb_unplug_request(void) +{ + QTestState *qtest; + + qtest = qtest_initf("-device spapr-pci-host-bridge,index=1,id=dev0"); + + /* similar to test_pci_unplug_request */ + device_del_request(qtest, "dev0"); + system_reset(qtest); + wait_device_deleted_event(qtest, "dev0"); + + qtest_quit(qtest); +} + +int main(int argc, char **argv) +{ + const char *arch = qtest_get_arch(); + + g_test_init(&argc, &argv, NULL); + + /* + * We need a system that will process unplug requests during system resets + * and does not do PCI surprise removal. This holds for x86 ACPI, + * s390x and spapr. + */ + qtest_add_func("/device-plug/pci-unplug-request", + test_pci_unplug_request); + + if (!strcmp(arch, "s390x")) { + qtest_add_func("/device-plug/ccw-unplug", + test_ccw_unplug); + } + + if (!strcmp(arch, "ppc64")) { + qtest_add_func("/device-plug/spapr-cpu-unplug-request", + test_spapr_cpu_unplug_request); + qtest_add_func("/device-plug/spapr-memory-unplug-request", + test_spapr_memory_unplug_request); + qtest_add_func("/device-plug/spapr-phb-unplug-request", + test_spapr_phb_unplug_request); + } + + return g_test_run(); +} diff --git a/tests/display-vga-test.c b/tests/display-vga-test.c index 2d7d24eee0..bd176dcf3a 100644 --- a/tests/display-vga-test.c +++ b/tests/display-vga-test.c @@ -40,13 +40,11 @@ static void pci_virtio_gpu(void) qtest_end(); } -#ifdef CONFIG_VIRTIO_VGA static void pci_virtio_vga(void) { qtest_start("-vga none -device virtio-vga"); qtest_end(); } -#endif int main(int argc, char **argv) { @@ -62,8 +60,10 @@ int main(int argc, char **argv) qtest_add_func("/display/pci/secondary", pci_secondary); qtest_add_func("/display/pci/multihead", pci_multihead); qtest_add_func("/display/pci/virtio-gpu", pci_virtio_gpu); -#ifdef CONFIG_VIRTIO_VGA - qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga); -#endif + if (g_str_equal(arch, "i386") || g_str_equal(arch, "x86_64") || + g_str_equal(arch, "hppa") || g_str_equal(arch, "ppc64")) { + qtest_add_func("/display/pci/virtio-vga", pci_virtio_vga); + } + return g_test_run(); } diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index 1aaa795743..c0e1bf57a3 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -25,10 +25,14 @@ IMAGES ?= % CUR_TIME := $(shell date +%Y-%m-%d-%H.%M.%S.$$$$) DOCKER_SRC_COPY := $(BUILD_DIR)/docker-src.$(CUR_TIME) +.DELETE_ON_ERROR: $(DOCKER_SRC_COPY) $(DOCKER_SRC_COPY): @mkdir $@ - $(call quiet-command, cd $(SRC_PATH) && scripts/archive-source.sh $@/qemu.tar, \ - "GEN", "$@/qemu.tar") + $(if $(SRC_ARCHIVE), \ + $(call quiet-command, cp "$(SRC_ARCHIVE)" $@/qemu.tar, \ + "CP", "$@/qemu.tar"), \ + $(call quiet-command, cd $(SRC_PATH) && scripts/archive-source.sh $@/qemu.tar, \ + "GEN", "$@/qemu.tar")) $(call quiet-command, cp $(SRC_PATH)/tests/docker/run $@/run, \ "COPY","RUNNER") @@ -37,7 +41,7 @@ docker-qemu-src: $(DOCKER_SRC_COPY) docker-image: ${DOCKER_TARGETS} # General rule for building docker images. If we are a sub-make -# invoked with SKIP_DOCKER_BUILD we still check the image is upto date +# invoked with SKIP_DOCKER_BUILD we still check the image is up to date # though ifdef SKIP_DOCKER_BUILD docker-image-%: $(DOCKER_FILES_DIR)/%.docker @@ -86,6 +90,7 @@ docker-image-debian-amd64: docker-image-debian9 docker-image-debian-armel-cross: docker-image-debian9 docker-image-debian-armhf-cross: docker-image-debian9 docker-image-debian-arm64-cross: docker-image-debian9 +docker-image-debian-buster-arm64-cross: docker-image-debian10 docker-image-debian-mips-cross: docker-image-debian9 docker-image-debian-mipsel-cross: docker-image-debian9 docker-image-debian-mips64el-cross: docker-image-debian9 @@ -94,19 +99,6 @@ docker-image-debian-s390x-cross: docker-image-debian9 docker-image-debian-win32-cross: docker-image-debian8-mxe docker-image-debian-win64-cross: docker-image-debian8-mxe -# Debian SID images - we are tracking a rolling distro so we want to -# force a re-build of the base image if we ever need to build one of -# its children. -ifndef SKIP_DOCKER_BUILD -ifeq ($(HAVE_USER_DOCKER),y) -SID_AGE=$(shell $(DOCKER_SCRIPT) check --checktype=age --olderthan=180 --quiet qemu:debian-sid) -ifeq ($(SID_AGE),) -else -docker-image-debian-sid: NOCACHE=1 -endif -endif -endif - docker-image-debian-alpha-cross: docker-image-debian-sid docker-image-debian-hppa-cross: docker-image-debian-sid docker-image-debian-m68k-cross: docker-image-debian-sid @@ -124,6 +116,7 @@ docker-image-tricore-cross: docker-image-debian9 DOCKER_PARTIAL_IMAGES += debian-alpha-cross debian-hppa-cross debian-m68k-cross debian-sh4-cross DOCKER_PARTIAL_IMAGES += debian-sparc64-cross debian-mips64-cross debian-riscv64-cross DOCKER_PARTIAL_IMAGES += debian-tricore-cross debian-powerpc-cross fedora-i386-cross +DOCKER_PARTIAL_IMAGES += fedora-cris-cross # Rules for building linux-user powered images # @@ -158,15 +151,15 @@ docker: @echo @echo ' docker: Print this help.' @echo ' docker-all-tests: Run all image/test combinations.' - @echo ' docker-TEST: Run TEST on all image combinations.' + @echo ' docker-TEST: Run "TEST" on all image combinations.' @echo ' docker-clean: Kill and remove residual docker testing containers.' @echo ' docker-TEST@IMAGE: Run "TEST" in container "IMAGE".' @echo ' Note: "TEST" is one of the listed test name,' @echo ' or a script name under $$QEMU_SRC/tests/docker/;' - @echo ' "IMAGE" is one of the listed container name."' + @echo ' "IMAGE" is one of the listed container name.' @echo ' docker-image: Build all images.' @echo ' docker-image-IMAGE: Build image "IMAGE".' - @echo ' docker-run: For manually running a "TEST" with "IMAGE"' + @echo ' docker-run: For manually running a "TEST" with "IMAGE".' @echo @echo 'Available container images:' @echo ' $(DOCKER_IMAGES)' diff --git a/tests/docker/docker.py b/tests/docker/docker.py index d3006d4dae..53a8c9c801 100755 --- a/tests/docker/docker.py +++ b/tests/docker/docker.py @@ -14,14 +14,12 @@ from __future__ import print_function import os import sys -sys.path.append(os.path.join(os.path.dirname(__file__), - '..', '..', 'scripts')) -import argparse import subprocess import json import hashlib import atexit import uuid +import argparse import tempfile import re import signal @@ -32,7 +30,7 @@ except ImportError: from io import StringIO from shutil import copy, rmtree from pwd import getpwuid -from datetime import datetime,timedelta +from datetime import datetime, timedelta FILTERED_ENV_NAMES = ['ftp_proxy', 'http_proxy', 'https_proxy'] @@ -45,9 +43,11 @@ def _text_checksum(text): """Calculate a digest string unique to the text content""" return hashlib.sha1(text).hexdigest() + def _file_checksum(filename): return _text_checksum(open(filename, 'rb').read()) + def _guess_docker_command(): """ Guess a working docker command or raise exception if not found""" commands = [["docker"], ["sudo", "-n", "docker"]] @@ -61,9 +61,10 @@ def _guess_docker_command(): except OSError: pass commands_txt = "\n".join([" " + " ".join(x) for x in commands]) - raise Exception("Cannot find working docker command. Tried:\n%s" % \ + raise Exception("Cannot find working docker command. Tried:\n%s" % commands_txt) + def _copy_with_mkdir(src, root_dir, sub_path='.'): """Copy src into root_dir, creating sub_path as needed.""" dest_dir = os.path.normpath("%s/%s" % (root_dir, sub_path)) @@ -98,19 +99,29 @@ def _get_so_libs(executable): return libs -def _copy_binary_with_libs(src, dest_dir): - """Copy a binary executable and all its dependant libraries. + +def _copy_binary_with_libs(src, bin_dest, dest_dir): + """Maybe copy a binary and all its dependent libraries. + + If bin_dest isn't set we only copy the support libraries because + we don't need qemu in the docker path to run (due to persistent + mapping). Indeed users may get confused if we aren't running what + is in the image. This does rely on the host file-system being fairly multi-arch - aware so the file don't clash with the guests layout.""" + aware so the file don't clash with the guests layout. + """ - _copy_with_mkdir(src, dest_dir, "/usr/bin") + if bin_dest: + _copy_with_mkdir(src, dest_dir, os.path.dirname(bin_dest)) + else: + print("only copying support libraries for %s" % (src)) libs = _get_so_libs(src) if libs: for l in libs: so_path = os.path.dirname(l) - _copy_with_mkdir(l , dest_dir, so_path) + _copy_with_mkdir(l, dest_dir, so_path) def _check_binfmt_misc(executable): @@ -118,23 +129,34 @@ def _check_binfmt_misc(executable): The details of setting up binfmt_misc are outside the scope of this script but we should at least fail early with a useful - message if it won't work.""" + message if it won't work. + + Returns the configured binfmt path and a valid flag. For + persistent configurations we will still want to copy and dependent + libraries. + """ binary = os.path.basename(executable) binfmt_entry = "/proc/sys/fs/binfmt_misc/%s" % (binary) if not os.path.exists(binfmt_entry): print ("No binfmt_misc entry for %s" % (binary)) - return False + return None, False with open(binfmt_entry) as x: entry = x.read() - qpath = "/usr/bin/%s" % (binary) - if not re.search("interpreter %s\n" % (qpath), entry): - print ("binfmt_misc for %s does not point to %s" % (binary, qpath)) - return False + if re.search("flags:.*F.*\n", entry): + print("binfmt_misc for %s uses persistent(F) mapping to host binary" % + (binary)) + return None, True - return True + m = re.search("interpreter (\S+)\n", entry) + interp = m.group(1) + if interp and interp != executable: + print("binfmt_misc for %s does not point to %s, using %s" % + (binary, executable, interp)) + + return interp, True def _read_qemu_dockerfile(img_name): @@ -146,6 +168,7 @@ def _read_qemu_dockerfile(img_name): img_name + ".docker") return open(df, "r").read() + def _dockerfile_preprocess(df): out = "" for l in df.splitlines(): @@ -163,6 +186,7 @@ def _dockerfile_preprocess(df): out += l + "\n" return out + class Docker(object): """ Running Docker commands """ def __init__(self): @@ -230,7 +254,7 @@ class Docker(object): def build_image(self, tag, docker_dir, dockerfile, quiet=True, user=False, argv=None, extra_files_cksum=[]): - if argv == None: + if argv is None: argv = [] tmp_df = tempfile.NamedTemporaryFile(dir=docker_dir, suffix=".docker") @@ -251,7 +275,7 @@ class Docker(object): tmp_df.flush() - self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv + \ + self._do_check(["build", "-t", tag, "-f", tmp_df.name] + argv + [docker_dir], quiet=quiet) @@ -281,16 +305,19 @@ class Docker(object): def command(self, cmd, argv, quiet): return self._do([cmd] + argv, quiet=quiet) + class SubCommand(object): """A SubCommand template base class""" - name = None # Subcommand name + name = None # Subcommand name + def shared_args(self, parser): parser.add_argument("--quiet", action="store_true", - help="Run quietly unless an error occured") + help="Run quietly unless an error occurred") def args(self, parser): """Setup argument parser""" pass + def run(self, args, argv): """Run command. args: parsed argument by argument parser. @@ -298,18 +325,23 @@ class SubCommand(object): """ pass + class RunCommand(SubCommand): """Invoke docker run and take care of cleaning up""" name = "run" + def args(self, parser): parser.add_argument("--keep", action="store_true", help="Don't remove image when command completes") + def run(self, args, argv): return Docker().run(argv, args.keep, quiet=args.quiet) + class BuildCommand(SubCommand): - """ Build docker image out of a dockerfile. Arguments: """ + """ Build docker image out of a dockerfile. Arg: """ name = "build" + def args(self, parser): parser.add_argument("--include-executable", "-e", help="""Specify a binary that will be copied to the @@ -342,7 +374,8 @@ class BuildCommand(SubCommand): # Validate binfmt_misc will work if args.include_executable: - if not _check_binfmt_misc(args.include_executable): + qpath, enabled = _check_binfmt_misc(args.include_executable) + if not enabled: return 1 # Is there a .pre file to run in the build context? @@ -365,14 +398,16 @@ class BuildCommand(SubCommand): # FIXME: there is no checksum of this executable and the linked # libraries, once the image built any change of this executable # or any library won't trigger another build. - _copy_binary_with_libs(args.include_executable, docker_dir) + _copy_binary_with_libs(args.include_executable, + qpath, docker_dir) + for filename in args.extra_files or []: _copy_with_mkdir(filename, docker_dir) cksum += [(filename, _file_checksum(filename))] argv += ["--build-arg=" + k.lower() + "=" + v - for k, v in os.environ.iteritems() - if k.lower() in FILTERED_ENV_NAMES] + for k, v in os.environ.iteritems() + if k.lower() in FILTERED_ENV_NAMES] dkr.build_image(tag, docker_dir, dockerfile, quiet=args.quiet, user=args.user, argv=argv, extra_files_cksum=cksum) @@ -381,9 +416,11 @@ class BuildCommand(SubCommand): return 0 + class UpdateCommand(SubCommand): - """ Update a docker image with new executables. Arguments: """ + """ Update a docker image with new executables. Args: """ name = "update" + def args(self, parser): parser.add_argument("tag", help="Image Tag") @@ -396,10 +433,17 @@ class UpdateCommand(SubCommand): tmp = tempfile.NamedTemporaryFile(suffix="dckr.tar.gz") tmp_tar = TarFile(fileobj=tmp, mode='w') - # Add the executable to the tarball - bn = os.path.basename(args.executable) - ff = "/usr/bin/%s" % bn - tmp_tar.add(args.executable, arcname=ff) + # Add the executable to the tarball, using the current + # configured binfmt_misc path. If we don't get a path then we + # only need the support libraries copied + ff, enabled = _check_binfmt_misc(args.executable) + + if not enabled: + print("binfmt_misc not enabled, update disabled") + return 1 + + if ff: + tmp_tar.add(args.executable, arcname=ff) # Add any associated libraries libs = _get_so_libs(args.executable) @@ -429,16 +473,20 @@ class UpdateCommand(SubCommand): return 0 + class CleanCommand(SubCommand): """Clean up docker instances""" name = "clean" + def run(self, args, argv): Docker().clean() return 0 + class ImagesCommand(SubCommand): """Run "docker images" command""" name = "images" + def run(self, args, argv): return Docker().command("images", argv, args.quiet) @@ -511,7 +559,7 @@ class CheckCommand(SubCommand): try: dkr = Docker() - except: + except subprocess.CalledProcessError: print("Docker not set up") return 1 @@ -550,7 +598,8 @@ class CheckCommand(SubCommand): def main(): parser = argparse.ArgumentParser(description="A Docker helper", - usage="%s ..." % os.path.basename(sys.argv[0])) + usage="%s ..." % + os.path.basename(sys.argv[0])) subparsers = parser.add_subparsers(title="subcommands", help=None) for cls in SubCommand.__subclasses__(): cmd = cls() @@ -561,5 +610,6 @@ def main(): args, argv = parser.parse_known_args() return args.cmdobj.run(args, argv) + if __name__ == "__main__": sys.exit(main()) diff --git a/tests/docker/dockerfiles/centos7.docker b/tests/docker/dockerfiles/centos7.docker index 575de29a0a..e0b9d7dbe9 100644 --- a/tests/docker/dockerfiles/centos7.docker +++ b/tests/docker/dockerfiles/centos7.docker @@ -1,17 +1,20 @@ FROM centos:7 -RUN yum install -y epel-release centos-release-xen +RUN yum install -y epel-release centos-release-xen-48 + RUN yum -y update ENV PACKAGES \ bison \ + bzip2 \ bzip2-devel \ ccache \ csnappy-devel \ flex \ - g++ \ + gcc-c++ \ gcc \ gettext \ git \ glib2-devel \ + libaio-devel \ libepoxy-devel \ libfdt-devel \ librdmacm-devel \ @@ -19,6 +22,8 @@ ENV PACKAGES \ make \ mesa-libEGL-devel \ mesa-libgbm-devel \ + nettle-devel \ + perl-Test-Harness \ pixman-devel \ SDL-devel \ spice-glib-devel \ diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index eb13f06ed1..d770a11a52 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -24,7 +24,8 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ libegl1-mesa-dev \ libepoxy-dev \ libgbm-dev -RUN git clone git://anongit.freedesktop.org/virglrenderer /usr/src/virglrenderer +RUN git clone https://anongit.freedesktop.org/git/virglrenderer.git /usr/src/virglrenderer && \ + cd /usr/src/virglrenderer && git checkout virglrenderer-0.7.0 RUN cd /usr/src/virglrenderer && ./autogen.sh && ./configure --with-glx --disable-tests && make install # netmap @@ -32,8 +33,11 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ apt-get install -y --no-install-recommends \ linux-headers-amd64 RUN git clone https://github.com/luigirizzo/netmap.git /usr/src/netmap +RUN cd /usr/src/netmap && git checkout v11.3 RUN cd /usr/src/netmap/LINUX && ./configure --no-drivers --no-apps --kernel-dir=$(ls -d /usr/src/linux-headers-*-amd64) && make install ENV QEMU_CONFIGURE_OPTS --enable-netmap +RUN ldconfig + # gcrypt ENV QEMU_CONFIGURE_OPTS $QEMU_CONFIGURE_OPTS --enable-gcrypt diff --git a/tests/docker/dockerfiles/debian-bootstrap.pre b/tests/docker/dockerfiles/debian-bootstrap.pre index 3b0ef95374..c164778c30 100755 --- a/tests/docker/dockerfiles/debian-bootstrap.pre +++ b/tests/docker/dockerfiles/debian-bootstrap.pre @@ -2,7 +2,7 @@ # # Simple wrapper for debootstrap, run in the docker build context # -FAKEROOT=`which fakeroot 2> /dev/null` +FAKEROOT=$(which fakeroot 2> /dev/null) # debootstrap < 1.0.67 generates empty sources.list, see Debian#732255 MIN_DEBOOTSTRAP_VERSION=1.0.67 @@ -52,7 +52,7 @@ fi if [ -z $DEBOOTSTRAP_DIR ]; then NEED_DEBOOTSTRAP=false - DEBOOTSTRAP=`which debootstrap 2> /dev/null` + DEBOOTSTRAP=$(which debootstrap 2> /dev/null) if [ -z $DEBOOTSTRAP ]; then echo "No debootstrap installed, attempting to install from SCM" NEED_DEBOOTSTRAP=true diff --git a/tests/docker/dockerfiles/debian-buster-arm64-cross.docker b/tests/docker/dockerfiles/debian-buster-arm64-cross.docker new file mode 100644 index 0000000000..33ada13806 --- /dev/null +++ b/tests/docker/dockerfiles/debian-buster-arm64-cross.docker @@ -0,0 +1,15 @@ +# +# Docker arm64 cross-compiler target +# +# This docker target builds on the Debian's Buster base image. There +# are no QEMU pre-requistes so this image can only be used to build +# test cases. +# +FROM qemu:debian10 + +# Add the foreign architecture we want and install dependencies +RUN dpkg --add-architecture arm64 +RUN apt update +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + crossbuild-essential-arm64 diff --git a/tests/docker/dockerfiles/debian-sid.docker b/tests/docker/dockerfiles/debian-sid.docker index 9a3d168705..676941cb32 100644 --- a/tests/docker/dockerfiles/debian-sid.docker +++ b/tests/docker/dockerfiles/debian-sid.docker @@ -11,7 +11,16 @@ # updated and trigger a re-build. # -FROM debian:sid-slim +# This must be earlier than the snapshot date we are aiming for +FROM debian:sid-20181011-slim + +# Use a snapshot known to work (see http://snapshot.debian.org/#Usage) +ENV DEBIAN_SNAPSHOT_DATE "20181030" +RUN sed -i "s%^deb \(https\?://\)deb.debian.org/debian/\? \(.*\)%deb [check-valid-until=no] \1snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT_DATE} \2%" /etc/apt/sources.list + +# Use a snapshot known to work (see http://snapshot.debian.org/#Usage) +ENV DEBIAN_SNAPSHOT_DATE "20181030" +RUN sed -i "s%^deb \(https\?://\)deb.debian.org/debian/\? \(.*\)%deb [check-valid-until=no] \1snapshot.debian.org/archive/debian/${DEBIAN_SNAPSHOT_DATE} \2%" /etc/apt/sources.list # Duplicate deb line as deb-src RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list diff --git a/tests/docker/dockerfiles/debian-xtensa-cross.docker b/tests/docker/dockerfiles/debian-xtensa-cross.docker new file mode 100644 index 0000000000..afd2ab9163 --- /dev/null +++ b/tests/docker/dockerfiles/debian-xtensa-cross.docker @@ -0,0 +1,31 @@ +# +# Docker cross-compiler target +# +# This docker target builds on the debian stretch base image, +# using a prebuilt toolchains for Xtensa cores from: +# https://github.com/foss-xtensa/toolchain/releases +# +FROM debian:stretch-slim + +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt-get install -y --no-install-recommends \ + bison \ + build-essential \ + ca-certificates \ + curl \ + flex \ + gettext \ + git \ + python-minimal + +ENV CPU_LIST csp dc232b dc233c +ENV TOOLCHAIN_RELEASE 2018.02 + +RUN for cpu in $CPU_LIST; do \ + curl -#SL http://github.com/foss-xtensa/toolchain/releases/download/$TOOLCHAIN_RELEASE/x86_64-$TOOLCHAIN_RELEASE-xtensa-$cpu-elf.tar.gz \ + | tar -xzC /opt; \ + done + +ENV PATH $PATH:/opt/$TOOLCHAIN_RELEASE/xtensa-dc232b-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-dc233c-elf/bin:/opt/$TOOLCHAIN_RELEASE/xtensa-csp-elf/bin diff --git a/tests/docker/dockerfiles/debian.docker b/tests/docker/dockerfiles/debian.docker deleted file mode 100644 index fd32e71b79..0000000000 --- a/tests/docker/dockerfiles/debian.docker +++ /dev/null @@ -1,13 +0,0 @@ -# This template is deprecated and was previously based on Jessie on QEMU 2.9. -# Now than Stretch is out, please use qemu:debian8 as base for Jessie, -# and qemu:debian9 for Stretch. -# -FROM qemu:debian9 - -MAINTAINER Philippe Mathieu-Daudé - -RUN for n in $(seq 8); do echo; done && \ - echo "\n\t\tThis image is deprecated." && echo && \ - echo "\tUse 'FROM qemu:debian9' to use the stable Debian Stretch image" && \ - echo "\tor 'FROM qemu:debian8' to use old Debian Jessie." && \ - for n in $(seq 8); do echo; done diff --git a/tests/docker/dockerfiles/debian10.docker b/tests/docker/dockerfiles/debian10.docker new file mode 100644 index 0000000000..aeeb151b52 --- /dev/null +++ b/tests/docker/dockerfiles/debian10.docker @@ -0,0 +1,31 @@ +# +# Docker multiarch cross-compiler target +# +# This docker target is builds on Debian cross compiler targets to build distro +# with a selection of cross compilers for building test binaries. +# +# On its own you can't build much but the docker-foo-cross targets +# build on top of the base debian image. +# +FROM debian:buster-slim + +# Duplicate deb line as deb-src +RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list + +# Install common build utilities +RUN apt update +RUN DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + bison \ + build-essential \ + ca-certificates \ + clang \ + flex \ + gettext \ + git \ + pkg-config \ + psmisc \ + python \ + texinfo \ + $(apt-get -s build-dep qemu | egrep ^Inst | fgrep '[all]' | cut -d\ -f2) diff --git a/tests/docker/dockerfiles/debian9.docker b/tests/docker/dockerfiles/debian9.docker index 154ae2a455..5f23a35404 100644 --- a/tests/docker/dockerfiles/debian9.docker +++ b/tests/docker/dockerfiles/debian9.docker @@ -13,8 +13,8 @@ FROM debian:stretch-slim RUN cat /etc/apt/sources.list | sed "s/^deb\ /deb-src /" >> /etc/apt/sources.list # Install common build utilities -RUN apt update -RUN DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt install -yy eatmydata RUN DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ bison \ diff --git a/tests/docker/dockerfiles/fedora-cris-cross.docker b/tests/docker/dockerfiles/fedora-cris-cross.docker new file mode 100644 index 0000000000..b168ada615 --- /dev/null +++ b/tests/docker/dockerfiles/fedora-cris-cross.docker @@ -0,0 +1,8 @@ +# +# Cross compiler for cris system tests +# + +FROM fedora:latest +ENV PACKAGES gcc-cris-linux-gnu +RUN dnf install -y $PACKAGES +RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/fedora-i386-cross.docker b/tests/docker/dockerfiles/fedora-i386-cross.docker index 8fbef2fa53..eb8108d118 100644 --- a/tests/docker/dockerfiles/fedora-i386-cross.docker +++ b/tests/docker/dockerfiles/fedora-i386-cross.docker @@ -1,14 +1,13 @@ -FROM fedora:latest +FROM fedora:29 ENV PACKAGES \ gcc \ - glibc-static.i686 \ + glib2-devel.i686 \ glibc-devel.i686 \ - glib2-devel.i686 \ - zlib-devel.i686 \ - glib2-devel.i686 \ + glibc-static.i686 \ + gnutls-devel.i686 \ nettle-devel.i686 \ pixman-devel.i686 \ - gnutls-devel.i686 + zlib-devel.i686 RUN dnf install -y $PACKAGES RUN rpm -q $PACKAGES | sort > /packages.txt diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 7d1d008002..69d4a7f5d7 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -1,24 +1,91 @@ -FROM fedora:28 +FROM fedora:29 ENV PACKAGES \ - ccache gettext git tar PyYAML sparse flex bison python3 bzip2 hostname \ - gcc gcc-c++ llvm clang make perl which bc findutils glib2-devel \ - libaio-devel pixman-devel zlib-devel libfdt-devel libasan libubsan \ - bluez-libs-devel brlapi-devel bzip2-devel \ - device-mapper-multipath-devel glusterfs-api-devel gnutls-devel \ - gtk3-devel libattr-devel libcap-devel libcap-ng-devel libcurl-devel \ - libjpeg-devel libpng-devel librbd-devel libssh2-devel libusbx-devel \ - libxml2-devel lzo-devel ncurses-devel nettle-devel nss-devel \ - numactl-devel SDL2-devel snappy-devel spice-server-devel \ - systemtap-sdt-devel usbredir-devel virglrenderer-devel vte3-devel \ - xen-devel \ - mingw32-pixman mingw32-glib2 mingw32-gmp mingw32-SDL2 mingw32-pkg-config \ - mingw32-gtk3 mingw32-gnutls mingw32-nettle mingw32-libtasn1 \ - mingw32-libjpeg-turbo mingw32-libpng mingw32-curl mingw32-libssh2 \ + bc \ + bison \ + bluez-libs-devel \ + brlapi-devel \ + bzip2 \ + bzip2-devel \ + ccache \ + clang \ + device-mapper-multipath-devel \ + findutils \ + flex \ + gcc \ + gcc-c++ \ + gettext \ + git \ + glib2-devel \ + glusterfs-api-devel \ + gnutls-devel \ + gtk3-devel \ + hostname \ + libaio-devel \ + libasan \ + libattr-devel \ + libcap-devel \ + libcap-ng-devel \ + libcurl-devel \ + libfdt-devel \ + libjpeg-devel \ + libpng-devel \ + librbd-devel \ + libssh2-devel \ + libubsan \ + libusbx-devel \ + libxml2-devel \ + llvm \ + lzo-devel \ + make \ mingw32-bzip2 \ - mingw64-pixman mingw64-glib2 mingw64-gmp mingw64-SDL2 mingw64-pkg-config \ - mingw64-gtk3 mingw64-gnutls mingw64-nettle mingw64-libtasn1 \ - mingw64-libjpeg-turbo mingw64-libpng mingw64-curl mingw64-libssh2 \ - mingw64-bzip2 + mingw32-curl \ + mingw32-glib2 \ + mingw32-gmp \ + mingw32-gnutls \ + mingw32-gtk3 \ + mingw32-libjpeg-turbo \ + mingw32-libpng \ + mingw32-libssh2 \ + mingw32-libtasn1 \ + mingw32-nettle \ + mingw32-pixman \ + mingw32-pkg-config \ + mingw32-SDL2 \ + mingw64-bzip2 \ + mingw64-curl \ + mingw64-glib2 \ + mingw64-gmp \ + mingw64-gnutls \ + mingw64-gtk3 \ + mingw64-libjpeg-turbo \ + mingw64-libpng \ + mingw64-libssh2 \ + mingw64-libtasn1 \ + mingw64-nettle \ + mingw64-pixman \ + mingw64-pkg-config \ + mingw64-SDL2 \ + ncurses-devel \ + nettle-devel \ + nss-devel \ + numactl-devel \ + perl \ + perl-Test-Harness \ + pixman-devel \ + python3 \ + PyYAML \ + SDL2-devel \ + snappy-devel \ + sparse \ + spice-server-devel \ + systemtap-sdt-devel \ + tar \ + usbredir-devel \ + virglrenderer-devel \ + vte291-devel \ + which \ + xen-devel \ + zlib-devel ENV QEMU_CONFIGURE_OPTS --python=/usr/bin/python3 RUN dnf install -y $PACKAGES diff --git a/tests/docker/dockerfiles/travis.docker b/tests/docker/dockerfiles/travis.docker index 03ebfb0ef2..e72dc85ca7 100644 --- a/tests/docker/dockerfiles/travis.docker +++ b/tests/docker/dockerfiles/travis.docker @@ -1,8 +1,8 @@ -FROM travisci/ci-garnet:packer-1512502276-986baf0 +FROM travisci/ci-sardonyx:packer-1546978056-2c98a19 ENV DEBIAN_FRONTEND noninteractive ENV LANG en_US.UTF-8 ENV LC_ALL en_US.UTF-8 -RUN cat /etc/apt/sources.list | sed "s/# deb-src/deb-src/" >> /etc/apt/sources.list +RUN sed -i "s/# deb-src/deb-src/" /etc/apt/sources.list RUN apt-get update RUN apt-get -y build-dep qemu RUN apt-get -y install device-tree-compiler python2.7 python-yaml dh-autoreconf gdb strace lsof net-tools gcovr diff --git a/tests/docker/dockerfiles/ubuntu.docker b/tests/docker/dockerfiles/ubuntu.docker index 7d724e7f53..36e2b17de5 100644 --- a/tests/docker/dockerfiles/ubuntu.docker +++ b/tests/docker/dockerfiles/ubuntu.docker @@ -2,16 +2,59 @@ FROM ubuntu:16.04 RUN echo "deb http://archive.ubuntu.com/ubuntu/ trusty universe multiverse" >> \ /etc/apt/sources.list ENV PACKAGES flex bison \ - libusb-1.0-0-dev libiscsi-dev librados-dev libncurses5-dev libncursesw5-dev \ - libseccomp-dev libgnutls-dev libssh2-1-dev libspice-server-dev \ - libspice-protocol-dev libnss3-dev libfdt-dev \ - libgtk-3-dev libvte-2.91-dev libsdl2-dev libpng12-dev libpixman-1-dev \ - libvdeplug-dev liblzo2-dev libsnappy-dev libbz2-dev libxen-dev librdmacm-dev libibverbs-dev \ - libsasl2-dev libjpeg-turbo8-dev xfslibs-dev libcap-ng-dev libbrlapi-dev libcurl4-gnutls-dev \ - libbluetooth-dev librbd-dev libaio-dev glusterfs-common libnuma-dev libepoxy-dev libdrm-dev libgbm-dev \ - libjemalloc-dev libcacard-dev libusbredirhost-dev libnfs-dev libcap-dev libattr1-dev \ + ccache \ + clang \ + gcc \ + gettext \ + git \ + glusterfs-common \ + libaio-dev \ + libattr1-dev \ + libbluetooth-dev \ + libbrlapi-dev \ + libbz2-dev \ + libcacard-dev \ + libcap-dev \ + libcap-ng-dev \ + libcurl4-gnutls-dev \ + libdrm-dev \ + libepoxy-dev \ + libfdt-dev \ + libgbm-dev \ + libgnutls-dev \ + libgtk-3-dev \ + libibverbs-dev \ + libiscsi-dev \ + libjemalloc-dev \ + libjpeg-turbo8-dev \ + liblzo2-dev \ + libncurses5-dev \ + libncursesw5-dev \ + libnfs-dev \ + libnss3-dev \ + libnuma-dev \ + libpixman-1-dev \ + libpng12-dev \ + librados-dev \ + librbd-dev \ + librdmacm-dev \ + libsasl2-dev \ + libsdl2-dev \ + libseccomp-dev \ + libsnappy-dev \ + libspice-protocol-dev \ + libspice-server-dev \ + libssh2-1-dev \ + libusb-1.0-0-dev \ + libusbredirhost-dev \ + libvdeplug-dev \ + libvte-2.91-dev \ + libxen-dev \ + make \ + python-yaml \ + sparse \ texinfo \ - gettext git make ccache python-yaml gcc clang sparse + xfslibs-dev RUN apt-get update && \ apt-get -y install $PACKAGES RUN dpkg -l $PACKAGES | sort > /packages.txt diff --git a/tests/docker/test-mingw b/tests/docker/test-mingw index 7cca7e16a6..4b84cfe120 100755 --- a/tests/docker/test-mingw +++ b/tests/docker/test-mingw @@ -27,9 +27,7 @@ for prefix in x86_64-w64-mingw32- i686-w64-mingw32-; do --enable-curl \ --enable-vnc \ --enable-bzip2 \ - --enable-guest-agent \ - --with-sdlabi=2.0 \ - --with-gtkabi=3.0 + --enable-guest-agent install_qemu make clean diff --git a/tests/docker/travis b/tests/docker/travis index d345393ced..47c03677d6 100755 --- a/tests/docker/travis +++ b/tests/docker/travis @@ -18,4 +18,5 @@ cmdfile=/tmp/travis_cmd_list.sh $QEMU_SRC/tests/docker/travis.py $QEMU_SRC/.travis.yml > $cmdfile chmod +x $cmdfile cd "$QEMU_SRC" +unset BUILD_DIR SRC_DIR $cmdfile diff --git a/tests/docker/travis.py b/tests/docker/travis.py index ea1ef169e6..e1433012bd 100755 --- a/tests/docker/travis.py +++ b/tests/docker/travis.py @@ -17,18 +17,17 @@ import yaml import itertools def load_yaml(fname): - return yaml.load(open(fname, "r").read()) + return yaml.safe_load(open(fname, "r").read()) def conf_iter(conf): + # If "compiler" is omitted from the included env then Travis picks the + # first entry of the global compiler list. + default_compiler = conf["compiler"][0] def env_to_list(env): return env if isinstance(env, list) else [env] for entry in conf["matrix"]["include"]: yield {"env": env_to_list(entry["env"]), - "compiler": entry["compiler"]} - for entry in itertools.product(conf["compiler"], - conf["env"]["matrix"]): - yield {"env": env_to_list(entry[1]), - "compiler": entry[0]} + "compiler": entry.get("compiler", default_compiler)} def main(): if len(sys.argv) < 2: diff --git a/tests/drive_del-test.c b/tests/drive_del-test.c index 852fefc8f3..2f9474e03c 100644 --- a/tests/drive_del-test.c +++ b/tests/drive_del-test.c @@ -15,6 +15,9 @@ #include "libqos/virtio.h" #include "qapi/qmp/qdict.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) + static void drive_add(void) { char *resp = hmp("drive_add 0 if=none,id=drive0"); @@ -60,10 +63,31 @@ static void test_drive_without_dev(void) qtest_end(); } +/* + * qvirtio_get_dev_type: + * Returns: the preferred virtio bus/device type for the current architecture. + * TODO: delete this + */ +static const char *qvirtio_get_dev_type(void) +{ + const char *arch = qtest_get_arch(); + + if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { + return "device"; /* for virtio-mmio */ + } else if (g_str_equal(arch, "s390x")) { + return "ccw"; + } else { + return "pci"; + } +} + static void test_after_failed_device_add(void) { + char driver[32]; QDict *response; - QDict *error; + + snprintf(driver, sizeof(driver), "virtio-blk-%s", + qvirtio_get_dev_type()); qtest_start("-drive if=none,id=drive0"); @@ -72,13 +96,11 @@ static void test_after_failed_device_add(void) */ response = qmp("{'execute': 'device_add'," " 'arguments': {" - " 'driver': 'virtio-blk-%s'," + " 'driver': %s," " 'drive': 'drive0'" - "}}", qvirtio_get_dev_type()); + "}}", driver); g_assert(response); - error = qdict_get_qdict(response, "error"); - g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, "GenericError"); - qobject_unref(response); + qmp_assert_error_class(response, "GenericError"); /* Delete the drive */ drive_del(); @@ -115,16 +137,11 @@ static void test_drive_del_device_del(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); - g_test_init(&argc, &argv, NULL); qtest_add_func("/drive_del/without-dev", test_drive_without_dev); - /* TODO I guess any arch with a hot-pluggable virtio bus would do */ - if (!strcmp(arch, "i386") || !strcmp(arch, "x86_64") || - !strcmp(arch, "ppc") || !strcmp(arch, "ppc64") || - !strcmp(arch, "s390x")) { + if (qvirtio_get_dev_type() != NULL) { qtest_add_func("/drive_del/after_failed_device_add", test_after_failed_device_add); qtest_add_func("/blockdev/drive_del_device_del", diff --git a/tests/e1000-test.c b/tests/e1000-test.c index 0c5fcdcc44..9e67916169 100644 --- a/tests/e1000-test.c +++ b/tests/e1000-test.c @@ -9,22 +9,15 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void test_device(gconstpointer data) -{ - const char *model = data; - QTestState *s; - char *args; +typedef struct QE1000 QE1000; - args = g_strdup_printf("-device %s", model); - s = qtest_start(args); - - if (s) { - qtest_quit(s); - } - g_free(args); -} +struct QE1000 { + QOSGraphObject obj; + QPCIDevice dev; +}; static const char *models[] = { "e1000", @@ -33,19 +26,42 @@ static const char *models[] = { "e1000-82545em", }; -int main(int argc, char **argv) +static void *e1000_get_driver(void *obj, const char *interface) { - int i; + QE1000 *e1000 = obj; - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < ARRAY_SIZE(models); i++) { - char *path; - - path = g_strdup_printf("e1000/%s", models[i]); - qtest_add_data_func(path, models[i], test_device); - g_free(path); + if (!g_strcmp0(interface, "pci-device")) { + return &e1000->dev; } - return g_test_run(); + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); } + +static void *e1000_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QE1000 *e1000 = g_new0(QE1000, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&e1000->dev, bus, addr); + e1000->obj.get_driver = e1000_get_driver; + + return &e1000->obj; +} + +static void e1000_register_nodes(void) +{ + int i; + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + for (i = 0; i < ARRAY_SIZE(models); i++) { + qos_node_create_driver(models[i], e1000_create); + qos_node_consumes(models[i], "pci-bus", &opts); + qos_node_produces(models[i], "pci-device"); + } +} + +libqos_init(e1000_register_nodes); diff --git a/tests/e1000e-test.c b/tests/e1000e-test.c index 32aa738b72..77ba8095bb 100644 --- a/tests/e1000e-test.c +++ b/tests/e1000e-test.c @@ -32,210 +32,9 @@ #include "qemu/iov.h" #include "qemu/bitops.h" #include "libqos/malloc.h" -#include "libqos/malloc-pc.h" -#include "libqos/malloc-generic.h" +#include "libqos/e1000e.h" -#define E1000E_IMS (0x00d0) - -#define E1000E_STATUS (0x0008) -#define E1000E_STATUS_LU BIT(1) -#define E1000E_STATUS_ASDV1000 BIT(9) - -#define E1000E_CTRL (0x0000) -#define E1000E_CTRL_RESET BIT(26) - -#define E1000E_RCTL (0x0100) -#define E1000E_RCTL_EN BIT(1) -#define E1000E_RCTL_UPE BIT(3) -#define E1000E_RCTL_MPE BIT(4) - -#define E1000E_RFCTL (0x5008) -#define E1000E_RFCTL_EXTEN BIT(15) - -#define E1000E_TCTL (0x0400) -#define E1000E_TCTL_EN BIT(1) - -#define E1000E_CTRL_EXT (0x0018) -#define E1000E_CTRL_EXT_DRV_LOAD BIT(28) -#define E1000E_CTRL_EXT_TXLSFLOW BIT(22) - -#define E1000E_RX0_MSG_ID (0) -#define E1000E_TX0_MSG_ID (1) -#define E1000E_OTHER_MSG_ID (2) - -#define E1000E_IVAR (0x00E4) -#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \ - (E1000E_TX0_MSG_ID << 8) | BIT(11) | \ - (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \ - BIT(31)) - -#define E1000E_RING_LEN (0x1000) -#define E1000E_TXD_LEN (16) -#define E1000E_RXD_LEN (16) - -#define E1000E_TDBAL (0x3800) -#define E1000E_TDBAH (0x3804) -#define E1000E_TDLEN (0x3808) -#define E1000E_TDH (0x3810) -#define E1000E_TDT (0x3818) - -#define E1000E_RDBAL (0x2800) -#define E1000E_RDBAH (0x2804) -#define E1000E_RDLEN (0x2808) -#define E1000E_RDH (0x2810) -#define E1000E_RDT (0x2818) - -typedef struct e1000e_device { - QPCIDevice *pci_dev; - QPCIBar mac_regs; - - uint64_t tx_ring; - uint64_t rx_ring; -} e1000e_device; - -static int test_sockets[2]; -static QGuestAllocator *test_alloc; -static QPCIBus *test_bus; - -static void e1000e_pci_foreach_callback(QPCIDevice *dev, int devfn, void *data) -{ - QPCIDevice **res = data; - - g_assert_null(*res); - *res = dev; -} - -static QPCIDevice *e1000e_device_find(QPCIBus *bus) -{ - static const int e1000e_vendor_id = 0x8086; - static const int e1000e_dev_id = 0x10D3; - - QPCIDevice *e1000e_dev = NULL; - - qpci_device_foreach(bus, e1000e_vendor_id, e1000e_dev_id, - e1000e_pci_foreach_callback, &e1000e_dev); - - g_assert_nonnull(e1000e_dev); - - return e1000e_dev; -} - -static void e1000e_macreg_write(e1000e_device *d, uint32_t reg, uint32_t val) -{ - qpci_io_writel(d->pci_dev, d->mac_regs, reg, val); -} - -static uint32_t e1000e_macreg_read(e1000e_device *d, uint32_t reg) -{ - return qpci_io_readl(d->pci_dev, d->mac_regs, reg); -} - -static void e1000e_device_init(QPCIBus *bus, e1000e_device *d) -{ - uint32_t val; - - d->pci_dev = e1000e_device_find(bus); - - /* Enable the device */ - qpci_device_enable(d->pci_dev); - - /* Map BAR0 (mac registers) */ - d->mac_regs = qpci_iomap(d->pci_dev, 0, NULL); - - /* Reset the device */ - val = e1000e_macreg_read(d, E1000E_CTRL); - e1000e_macreg_write(d, E1000E_CTRL, val | E1000E_CTRL_RESET); - - /* Enable and configure MSI-X */ - qpci_msix_enable(d->pci_dev); - e1000e_macreg_write(d, E1000E_IVAR, E1000E_IVAR_TEST_CFG); - - /* Check the device status - link and speed */ - val = e1000e_macreg_read(d, E1000E_STATUS); - g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000), - ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000); - - /* Initialize TX/RX logic */ - e1000e_macreg_write(d, E1000E_RCTL, 0); - e1000e_macreg_write(d, E1000E_TCTL, 0); - - /* Notify the device that the driver is ready */ - val = e1000e_macreg_read(d, E1000E_CTRL_EXT); - e1000e_macreg_write(d, E1000E_CTRL_EXT, - val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW); - - /* Allocate and setup TX ring */ - d->tx_ring = guest_alloc(test_alloc, E1000E_RING_LEN); - g_assert(d->tx_ring != 0); - - e1000e_macreg_write(d, E1000E_TDBAL, (uint32_t) d->tx_ring); - e1000e_macreg_write(d, E1000E_TDBAH, (uint32_t) (d->tx_ring >> 32)); - e1000e_macreg_write(d, E1000E_TDLEN, E1000E_RING_LEN); - e1000e_macreg_write(d, E1000E_TDT, 0); - e1000e_macreg_write(d, E1000E_TDH, 0); - - /* Enable transmit */ - e1000e_macreg_write(d, E1000E_TCTL, E1000E_TCTL_EN); - - /* Allocate and setup RX ring */ - d->rx_ring = guest_alloc(test_alloc, E1000E_RING_LEN); - g_assert(d->rx_ring != 0); - - e1000e_macreg_write(d, E1000E_RDBAL, (uint32_t)d->rx_ring); - e1000e_macreg_write(d, E1000E_RDBAH, (uint32_t)(d->rx_ring >> 32)); - e1000e_macreg_write(d, E1000E_RDLEN, E1000E_RING_LEN); - e1000e_macreg_write(d, E1000E_RDT, 0); - e1000e_macreg_write(d, E1000E_RDH, 0); - - /* Enable receive */ - e1000e_macreg_write(d, E1000E_RFCTL, E1000E_RFCTL_EXTEN); - e1000e_macreg_write(d, E1000E_RCTL, E1000E_RCTL_EN | - E1000E_RCTL_UPE | - E1000E_RCTL_MPE); - - /* Enable all interrupts */ - e1000e_macreg_write(d, E1000E_IMS, 0xFFFFFFFF); -} - -static void e1000e_tx_ring_push(e1000e_device *d, void *descr) -{ - uint32_t tail = e1000e_macreg_read(d, E1000E_TDT); - uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN; - - memwrite(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); - e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len); - - /* Read WB data for the packet transmitted */ - memread(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); -} - -static void e1000e_rx_ring_push(e1000e_device *d, void *descr) -{ - uint32_t tail = e1000e_macreg_read(d, E1000E_RDT); - uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN; - - memwrite(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); - e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len); - - /* Read WB data for the packet received */ - memread(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); -} - -static void e1000e_wait_isr(e1000e_device *d, uint16_t msg_id) -{ - guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; - - do { - if (qpci_msix_pending(d->pci_dev, msg_id)) { - return; - } - clock_step(10000); - } while (g_get_monotonic_time() < end_time); - - g_error("Timeout expired"); -} - -static void e1000e_send_verify(e1000e_device *d) +static void e1000e_send_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { struct { uint64_t buffer_addr; @@ -268,7 +67,7 @@ static void e1000e_send_verify(e1000e_device *d) uint32_t recv_len; /* Prepare test data buffer */ - uint64_t data = guest_alloc(test_alloc, data_len); + uint64_t data = guest_alloc(alloc, data_len); memwrite(data, "TEST", 5); /* Prepare TX descriptor */ @@ -296,10 +95,10 @@ static void e1000e_send_verify(e1000e_device *d) g_assert_cmpstr(buffer, == , "TEST"); /* Free test data buffer */ - guest_free(test_alloc, data); + guest_free(alloc, data); } -static void e1000e_receive_verify(e1000e_device *d) +static void e1000e_receive_verify(QE1000E *d, int *test_sockets, QGuestAllocator *alloc) { union { struct { @@ -348,7 +147,7 @@ static void e1000e_receive_verify(e1000e_device *d) g_assert_cmpint(ret, == , sizeof(test) + sizeof(len)); /* Prepare test data buffer */ - uint64_t data = guest_alloc(test_alloc, data_len); + uint64_t data = guest_alloc(alloc, data_len); /* Prepare RX descriptor */ memset(&descr, 0, sizeof(descr)); @@ -369,113 +168,108 @@ static void e1000e_receive_verify(e1000e_device *d) g_assert_cmpstr(buffer, == , "TEST"); /* Free test data buffer */ - guest_free(test_alloc, data); + guest_free(alloc, data); } -static void e1000e_device_clear(QPCIBus *bus, e1000e_device *d) +static void test_e1000e_init(void *obj, void *data, QGuestAllocator * alloc) { - qpci_iounmap(d->pci_dev, d->mac_regs); - qpci_msix_disable(d->pci_dev); + /* init does nothing */ } -static void data_test_init(e1000e_device *d) +static void test_e1000e_tx(void *obj, void *data, QGuestAllocator * alloc) { - char *cmdline; + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); - g_assert_cmpint(ret, != , -1); + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } - cmdline = g_strdup_printf("-netdev socket,fd=%d,id=hs0 " - "-device e1000e,netdev=hs0", test_sockets[1]); - g_assert_nonnull(cmdline); - - qtest_start(cmdline); - g_free(cmdline); - - test_alloc = pc_alloc_init(global_qtest); - g_assert_nonnull(test_alloc); - - test_bus = qpci_init_pc(global_qtest, test_alloc); - g_assert_nonnull(test_bus); - - e1000e_device_init(test_bus, d); + e1000e_send_verify(d, data, alloc); } -static void data_test_clear(e1000e_device *d) +static void test_e1000e_rx(void *obj, void *data, QGuestAllocator * alloc) { - e1000e_device_clear(test_bus, d); - close(test_sockets[0]); - pc_alloc_uninit(test_alloc); - g_free(d->pci_dev); - qpci_free_pc(test_bus); - qtest_end(); + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); + + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + e1000e_receive_verify(d, data, alloc); } -static void test_e1000e_init(gconstpointer data) -{ - e1000e_device d; - - data_test_init(&d); - data_test_clear(&d); -} - -static void test_e1000e_tx(gconstpointer data) -{ - e1000e_device d; - - data_test_init(&d); - e1000e_send_verify(&d); - data_test_clear(&d); -} - -static void test_e1000e_rx(gconstpointer data) -{ - e1000e_device d; - - data_test_init(&d); - e1000e_receive_verify(&d); - data_test_clear(&d); -} - -static void test_e1000e_multiple_transfers(gconstpointer data) +static void test_e1000e_multiple_transfers(void *obj, void *data, + QGuestAllocator *alloc) { static const long iterations = 4 * 1024; long i; - e1000e_device d; + QE1000E_PCI *e1000e = obj; + QE1000E *d = &e1000e->e1000e; + QOSGraphObject *e_object = obj; + QPCIDevice *dev = e_object->get_driver(e_object, "pci-device"); - data_test_init(&d); - - for (i = 0; i < iterations; i++) { - e1000e_send_verify(&d); - e1000e_receive_verify(&d); + /* FIXME: add spapr support */ + if (qpci_check_buggy_msi(dev)) { + return; + } + + for (i = 0; i < iterations; i++) { + e1000e_send_verify(d, data, alloc); + e1000e_receive_verify(d, data, alloc); } - data_test_clear(&d); } -static void test_e1000e_hotplug(gconstpointer data) +static void test_e1000e_hotplug(void *obj, void *data, QGuestAllocator * alloc) { - static const uint8_t slot = 0x06; - - qtest_start("-device e1000e"); - - qpci_plug_device_test("e1000e", "e1000e_net", slot, NULL); - qpci_unplug_acpi_device_test("e1000e_net", slot); - - qtest_end(); + qtest_qmp_device_add("e1000e", "e1000e_net", "{'addr': '0x06'}"); + qpci_unplug_acpi_device_test("e1000e_net", 0x06); } -int main(int argc, char **argv) +static void data_test_clear(void *sockets) { - g_test_init(&argc, &argv, NULL); + int *test_sockets = sockets; - qtest_add_data_func("e1000e/init", NULL, test_e1000e_init); - qtest_add_data_func("e1000e/tx", NULL, test_e1000e_tx); - qtest_add_data_func("e1000e/rx", NULL, test_e1000e_rx); - qtest_add_data_func("e1000e/multiple_transfers", NULL, - test_e1000e_multiple_transfers); - qtest_add_data_func("e1000e/hotplug", NULL, test_e1000e_hotplug); - - return g_test_run(); + close(test_sockets[0]); + qos_invalidate_command_line(); + close(test_sockets[1]); + g_free(test_sockets); } + +static void *data_test_init(GString *cmd_line, void *arg) +{ + int *test_sockets = g_new(int, 2); + int ret = socketpair(PF_UNIX, SOCK_STREAM, 0, test_sockets); + g_assert_cmpint(ret, != , -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", + test_sockets[1]); + + g_test_queue_destroy(data_test_clear, test_sockets); + return test_sockets; +} + +static void register_e1000e_test(void) +{ + QOSGraphTestOptions opts = { + .before = data_test_init, + }; + + qos_add_test("init", "e1000e", test_e1000e_init, &opts); + qos_add_test("tx", "e1000e", test_e1000e_tx, &opts); + qos_add_test("rx", "e1000e", test_e1000e_rx, &opts); + qos_add_test("multiple_transfers", "e1000e", + test_e1000e_multiple_transfers, &opts); + qos_add_test("hotplug", "e1000e", test_e1000e_hotplug, &opts); +} + +libqos_init(register_e1000e_test); diff --git a/tests/eepro100-test.c b/tests/eepro100-test.c index bdc8a67d57..90b5c1afd9 100644 --- a/tests/eepro100-test.c +++ b/tests/eepro100-test.c @@ -9,23 +9,15 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -static void test_device(gconstpointer data) -{ - const char *model = data; - QTestState *s; - char *args; +typedef struct QEEPRO100 QEEPRO100; - args = g_strdup_printf("-device %s", model); - s = qtest_start(args); - - /* Tests only initialization so far. TODO: Implement functional tests */ - - if (s) { - qtest_quit(s); - } - g_free(args); -} +struct QEEPRO100 { + QOSGraphObject obj; + QPCIDevice dev; +}; static const char *models[] = { "i82550", @@ -43,19 +35,42 @@ static const char *models[] = { "i82801", }; -int main(int argc, char **argv) +static void *eepro100_get_driver(void *obj, const char *interface) { - int i; + QEEPRO100 *eepro100 = obj; - g_test_init(&argc, &argv, NULL); - - for (i = 0; i < ARRAY_SIZE(models); i++) { - char *path; - - path = g_strdup_printf("eepro100/%s", models[i]); - qtest_add_data_func(path, models[i], test_device); - g_free(path); + if (!g_strcmp0(interface, "pci-device")) { + return &eepro100->dev; } - return g_test_run(); + fprintf(stderr, "%s not present in eepro100\n", interface); + g_assert_not_reached(); } + +static void *eepro100_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QEEPRO100 *eepro100 = g_new0(QEEPRO100, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&eepro100->dev, bus, addr); + eepro100->obj.get_driver = eepro100_get_driver; + + return &eepro100->obj; +} + +static void eepro100_register_nodes(void) +{ + int i; + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + for (i = 0; i < ARRAY_SIZE(models); i++) { + qos_node_create_driver(models[i], eepro100_create); + qos_node_consumes(models[i], "pci-bus", &opts); + qos_node_produces(models[i], "pci-device"); + } +} + +libqos_init(eepro100_register_nodes); diff --git a/tests/endianness-test.c b/tests/endianness-test.c index 546e0969e4..58527952a5 100644 --- a/tests/endianness-test.c +++ b/tests/endianness-test.c @@ -48,65 +48,68 @@ static const TestCase test_cases[] = { {} }; -static uint8_t isa_inb(const TestCase *test, uint16_t addr) +static uint8_t isa_inb(QTestState *qts, const TestCase *test, uint16_t addr) { uint8_t value; if (test->isa_base == -1) { - value = inb(addr); + value = qtest_inb(qts, addr); } else { - value = readb(test->isa_base + addr); + value = qtest_readb(qts, test->isa_base + addr); } return value; } -static uint16_t isa_inw(const TestCase *test, uint16_t addr) +static uint16_t isa_inw(QTestState *qts, const TestCase *test, uint16_t addr) { uint16_t value; if (test->isa_base == -1) { - value = inw(addr); + value = qtest_inw(qts, addr); } else { - value = readw(test->isa_base + addr); + value = qtest_readw(qts, test->isa_base + addr); } return test->bswap ? bswap16(value) : value; } -static uint32_t isa_inl(const TestCase *test, uint16_t addr) +static uint32_t isa_inl(QTestState *qts, const TestCase *test, uint16_t addr) { uint32_t value; if (test->isa_base == -1) { - value = inl(addr); + value = qtest_inl(qts, addr); } else { - value = readl(test->isa_base + addr); + value = qtest_readl(qts, test->isa_base + addr); } return test->bswap ? bswap32(value) : value; } -static void isa_outb(const TestCase *test, uint16_t addr, uint8_t value) +static void isa_outb(QTestState *qts, const TestCase *test, uint16_t addr, + uint8_t value) { if (test->isa_base == -1) { - outb(addr, value); + qtest_outb(qts, addr, value); } else { - writeb(test->isa_base + addr, value); + qtest_writeb(qts, test->isa_base + addr, value); } } -static void isa_outw(const TestCase *test, uint16_t addr, uint16_t value) +static void isa_outw(QTestState *qts, const TestCase *test, uint16_t addr, + uint16_t value) { value = test->bswap ? bswap16(value) : value; if (test->isa_base == -1) { - outw(addr, value); + qtest_outw(qts, addr, value); } else { - writew(test->isa_base + addr, value); + qtest_writew(qts, test->isa_base + addr, value); } } -static void isa_outl(const TestCase *test, uint16_t addr, uint32_t value) +static void isa_outl(QTestState *qts, const TestCase *test, uint16_t addr, + uint32_t value) { value = test->bswap ? bswap32(value) : value; if (test->isa_base == -1) { - outl(addr, value); + qtest_outl(qts, addr, value); } else { - writel(test->isa_base + addr, value); + qtest_writel(qts, test->isa_base + addr, value); } } @@ -114,161 +117,161 @@ static void isa_outl(const TestCase *test, uint16_t addr, uint32_t value) static void test_endianness(gconstpointer data) { const TestCase *test = data; + QTestState *qts; - global_qtest = qtest_startf("-M %s%s%s -device pc-testdev", - test->machine, - test->superio ? " -device " : "", - test->superio ?: ""); - isa_outl(test, 0xe0, 0x87654321); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x21); + qts = qtest_initf("-M %s%s%s -device pc-testdev", test->machine, + test->superio ? " -device " : "", + test->superio ?: ""); + isa_outl(qts, test, 0xe0, 0x87654321); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x21); - isa_outw(test, 0xe2, 0x8866); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664321); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x88); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x66); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x21); + isa_outw(qts, test, 0xe2, 0x8866); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664321); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x88); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x66); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x21); - isa_outw(test, 0xe0, 0x4422); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664422); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x88); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x66); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x44); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22); + isa_outw(qts, test, 0xe0, 0x4422); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664422); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x88); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x66); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x44); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22); - isa_outb(test, 0xe3, 0x87); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87664422); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8766); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x66); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x44); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22); + isa_outb(qts, test, 0xe3, 0x87); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87664422); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8766); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x66); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x44); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22); - isa_outb(test, 0xe2, 0x65); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654422); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x44); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22); + isa_outb(qts, test, 0xe2, 0x65); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654422); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x44); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22); - isa_outb(test, 0xe1, 0x43); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654322); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4322); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x22); + isa_outb(qts, test, 0xe1, 0x43); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654322); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4322); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x22); - isa_outb(test, 0xe0, 0x21); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321); - g_assert_cmphex(isa_inb(test, 0xe3), ==, 0x87); - g_assert_cmphex(isa_inb(test, 0xe2), ==, 0x65); - g_assert_cmphex(isa_inb(test, 0xe1), ==, 0x43); - g_assert_cmphex(isa_inb(test, 0xe0), ==, 0x21); - qtest_quit(global_qtest); + isa_outb(qts, test, 0xe0, 0x21); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321); + g_assert_cmphex(isa_inb(qts, test, 0xe3), ==, 0x87); + g_assert_cmphex(isa_inb(qts, test, 0xe2), ==, 0x65); + g_assert_cmphex(isa_inb(qts, test, 0xe1), ==, 0x43); + g_assert_cmphex(isa_inb(qts, test, 0xe0), ==, 0x21); + qtest_quit(qts); } static void test_endianness_split(gconstpointer data) { const TestCase *test = data; + QTestState *qts; - global_qtest = qtest_startf("-M %s%s%s -device pc-testdev", - test->machine, - test->superio ? " -device " : "", - test->superio ?: ""); - isa_outl(test, 0xe8, 0x87654321); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321); + qts = qtest_initf("-M %s%s%s -device pc-testdev", test->machine, + test->superio ? " -device " : "", + test->superio ?: ""); + isa_outl(qts, test, 0xe8, 0x87654321); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321); - isa_outw(test, 0xea, 0x8866); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664321); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321); + isa_outw(qts, test, 0xea, 0x8866); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664321); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321); - isa_outw(test, 0xe8, 0x4422); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x88664422); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8866); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422); + isa_outw(qts, test, 0xe8, 0x4422); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x88664422); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8866); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422); - isa_outb(test, 0xeb, 0x87); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87664422); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8766); + isa_outb(qts, test, 0xeb, 0x87); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87664422); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8766); - isa_outb(test, 0xea, 0x65); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654422); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4422); + isa_outb(qts, test, 0xea, 0x65); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654422); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4422); - isa_outb(test, 0xe9, 0x43); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654322); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4322); + isa_outb(qts, test, 0xe9, 0x43); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654322); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4322); - isa_outb(test, 0xe8, 0x21); - g_assert_cmphex(isa_inl(test, 0xe0), ==, 0x87654321); - g_assert_cmphex(isa_inw(test, 0xe2), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe0), ==, 0x4321); - qtest_quit(global_qtest); + isa_outb(qts, test, 0xe8, 0x21); + g_assert_cmphex(isa_inl(qts, test, 0xe0), ==, 0x87654321); + g_assert_cmphex(isa_inw(qts, test, 0xe2), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe0), ==, 0x4321); + qtest_quit(qts); } static void test_endianness_combine(gconstpointer data) { const TestCase *test = data; + QTestState *qts; - global_qtest = qtest_startf("-M %s%s%s -device pc-testdev", - test->machine, - test->superio ? " -device " : "", - test->superio ?: ""); - isa_outl(test, 0xe0, 0x87654321); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654321); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4321); + qts = qtest_initf("-M %s%s%s -device pc-testdev", test->machine, + test->superio ? " -device " : "", + test->superio ?: ""); + isa_outl(qts, test, 0xe0, 0x87654321); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654321); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4321); - isa_outw(test, 0xe2, 0x8866); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x88664321); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8866); - g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4321); + isa_outw(qts, test, 0xe2, 0x8866); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x88664321); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8866); + g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4321); - isa_outw(test, 0xe0, 0x4422); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x88664422); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8866); - g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4422); + isa_outw(qts, test, 0xe0, 0x4422); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x88664422); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8866); + g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4422); - isa_outb(test, 0xe3, 0x87); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87664422); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8766); + isa_outb(qts, test, 0xe3, 0x87); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87664422); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8766); - isa_outb(test, 0xe2, 0x65); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654422); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4422); + isa_outb(qts, test, 0xe2, 0x65); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654422); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4422); - isa_outb(test, 0xe1, 0x43); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654322); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4322); + isa_outb(qts, test, 0xe1, 0x43); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654322); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4322); - isa_outb(test, 0xe0, 0x21); - g_assert_cmphex(isa_inl(test, 0xe8), ==, 0x87654321); - g_assert_cmphex(isa_inw(test, 0xea), ==, 0x8765); - g_assert_cmphex(isa_inw(test, 0xe8), ==, 0x4321); - qtest_quit(global_qtest); + isa_outb(qts, test, 0xe0, 0x21); + g_assert_cmphex(isa_inl(qts, test, 0xe8), ==, 0x87654321); + g_assert_cmphex(isa_inw(qts, test, 0xea), ==, 0x8765); + g_assert_cmphex(isa_inw(qts, test, 0xe8), ==, 0x4321); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/es1370-test.c b/tests/es1370-test.c index 199fe193ce..d845cd06f8 100644 --- a/tests/es1370-test.c +++ b/tests/es1370-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QES1370 QES1370; + +struct QES1370 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *es1370_get_driver(void *obj, const char *interface) { + QES1370 *es1370 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &es1370->dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *es1370_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QES1370 *es1370 = g_new0(QES1370, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/es1370/nop", nop); + qpci_device_init(&es1370->dev, bus, addr); + es1370->obj.get_driver = es1370_get_driver; - qtest_start("-device ES1370"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &es1370->obj; } + +static void es1370_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ES1370", es1370_create); + qos_node_consumes("ES1370", "pci-bus", &opts); + qos_node_produces("ES1370", "pci-device"); +} + +libqos_init(es1370_register_nodes); diff --git a/tests/fdc-test.c b/tests/fdc-test.c index 325712e0f2..31cd3295c1 100644 --- a/tests/fdc-test.c +++ b/tests/fdc-test.c @@ -26,8 +26,12 @@ #include "libqtest.h" +#include "qapi/qmp/qdict.h" #include "qemu-common.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) + #define TEST_IMAGE_SIZE 1440 * 1024 #define FLOPPY_BASE 0x3f0 @@ -544,16 +548,9 @@ static void fuzz_registers(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); int fd; int ret; - /* Check architecture */ - if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { - g_test_message("Skipping test for non-x86\n"); - return 0; - } - /* Create a temporary raw image */ fd = mkstemp(test_image); g_assert(fd >= 0); diff --git a/tests/fp/.gitignore b/tests/fp/.gitignore new file mode 100644 index 0000000000..704fd42992 --- /dev/null +++ b/tests/fp/.gitignore @@ -0,0 +1,2 @@ +fp-test +fp-bench diff --git a/tests/fp/Makefile b/tests/fp/Makefile new file mode 100644 index 0000000000..5a35e7c210 --- /dev/null +++ b/tests/fp/Makefile @@ -0,0 +1,602 @@ +BUILD_DIR := $(CURDIR)/../.. + +include $(BUILD_DIR)/config-host.mak +include $(SRC_PATH)/rules.mak + +SOFTFLOAT_DIR := $(SRC_PATH)/tests/fp/berkeley-softfloat-3 +TESTFLOAT_DIR := $(SRC_PATH)/tests/fp/berkeley-testfloat-3 + +SF_SOURCE_DIR := $(SOFTFLOAT_DIR)/source +SF_INCLUDE_DIR := $(SOFTFLOAT_DIR)/source/include +# we could use any specialize here, it doesn't matter +SF_SPECIALIZE := 8086-SSE +SF_SPECIALIZE_DIR := $(SF_SOURCE_DIR)/$(SF_SPECIALIZE) + +TF_SOURCE_DIR := $(TESTFLOAT_DIR)/source + +$(call set-vpath, $(SRC_PATH)/fpu $(SRC_PATH)/tests/fp) + +LIBQEMUUTIL := $(BUILD_DIR)/libqemuutil.a + +# Use this variable to be clear when we pull in our own implementation +# We build the object with a default rule thanks to the vpath above +QEMU_SOFTFLOAT_OBJ := softfloat.o + +QEMU_INCLUDES += -I$(SRC_PATH)/tests/fp +QEMU_INCLUDES += -I$(SF_INCLUDE_DIR) +QEMU_INCLUDES += -I$(SF_SPECIALIZE_DIR) +QEMU_INCLUDES += -I$(TF_SOURCE_DIR) + +# work around TARGET_* poisoning +QEMU_CFLAGS += -DHW_POISON_H +# define a target to match testfloat's implementation-defined choices, such as +# whether to raise the invalid flag when dealing with NaNs in muladd. +QEMU_CFLAGS += -DTARGET_ARM + +# capstone has a platform.h file that clashes with softfloat's +QEMU_CFLAGS := $(filter-out %capstone, $(QEMU_CFLAGS)) + +# softfloat defines +SF_OPTS := +SF_OPTS += -DSOFTFLOAT_ROUND_ODD +SF_OPTS += -DINLINE_LEVEL=5 +SF_OPTS += -DSOFTFLOAT_FAST_DIV32TO16 +SF_OPTS += -DSOFTFLOAT_FAST_DIV64TO32 +SF_OPTS += -DSOFTFLOAT_FAST_INT64 +QEMU_CFLAGS += $(SF_OPTS) + +# silence the build of softfloat objects +SF_CFLAGS += -Wno-missing-prototypes +SF_CFLAGS += -Wno-redundant-decls +SF_CFLAGS += -Wno-return-type +SF_CFLAGS += -Wno-error + +# testfloat defines +TF_OPTS := +TF_OPTS += -DFLOAT16 +TF_OPTS += -DFLOAT64 +TF_OPTS += -DEXTFLOAT80 +TF_OPTS += -DFLOAT128 +TF_OPTS += -DFLOAT_ROUND_ODD +TF_OPTS += -DLONG_DOUBLE_IS_EXTFLOAT80 +QEMU_CFLAGS += $(TF_OPTS) + +# silence the build of testfloat objects +TF_CFLAGS := +TF_CFLAGS += -Wno-strict-prototypes +TF_CFLAGS += -Wno-unknown-pragmas +TF_CFLAGS += -Wno-uninitialized +TF_CFLAGS += -Wno-missing-prototypes +TF_CFLAGS += -Wno-return-type +TF_CFLAGS += -Wno-unused-function +TF_CFLAGS += -Wno-error + +# softfloat objects +SF_OBJS_PRIMITIVES := +SF_OBJS_PRIMITIVES += s_eq128.o +SF_OBJS_PRIMITIVES += s_le128.o +SF_OBJS_PRIMITIVES += s_lt128.o +SF_OBJS_PRIMITIVES += s_shortShiftLeft128.o +SF_OBJS_PRIMITIVES += s_shortShiftRight128.o +SF_OBJS_PRIMITIVES += s_shortShiftRightJam64.o +SF_OBJS_PRIMITIVES += s_shortShiftRightJam64Extra.o +SF_OBJS_PRIMITIVES += s_shortShiftRightJam128.o +SF_OBJS_PRIMITIVES += s_shortShiftRightJam128Extra.o +SF_OBJS_PRIMITIVES += s_shiftRightJam32.o +SF_OBJS_PRIMITIVES += s_shiftRightJam64.o +SF_OBJS_PRIMITIVES += s_shiftRightJam64Extra.o +SF_OBJS_PRIMITIVES += s_shiftRightJam128.o +SF_OBJS_PRIMITIVES += s_shiftRightJam128Extra.o +SF_OBJS_PRIMITIVES += s_shiftRightJam256M.o +SF_OBJS_PRIMITIVES += s_countLeadingZeros8.o +SF_OBJS_PRIMITIVES += s_countLeadingZeros16.o +SF_OBJS_PRIMITIVES += s_countLeadingZeros32.o +SF_OBJS_PRIMITIVES += s_countLeadingZeros64.o +SF_OBJS_PRIMITIVES += s_add128.o +SF_OBJS_PRIMITIVES += s_add256M.o +SF_OBJS_PRIMITIVES += s_sub128.o +SF_OBJS_PRIMITIVES += s_sub256M.o +SF_OBJS_PRIMITIVES += s_mul64ByShifted32To128.o +SF_OBJS_PRIMITIVES += s_mul64To128.o +SF_OBJS_PRIMITIVES += s_mul128By32.o +SF_OBJS_PRIMITIVES += s_mul128To256M.o +SF_OBJS_PRIMITIVES += s_approxRecip_1Ks.o +SF_OBJS_PRIMITIVES += s_approxRecip32_1.o +SF_OBJS_PRIMITIVES += s_approxRecipSqrt_1Ks.o +SF_OBJS_PRIMITIVES += s_approxRecipSqrt32_1.o + +SF_OBJS_SPECIALIZE := +SF_OBJS_SPECIALIZE += softfloat_raiseFlags.o +SF_OBJS_SPECIALIZE += s_f16UIToCommonNaN.o +SF_OBJS_SPECIALIZE += s_commonNaNToF16UI.o +SF_OBJS_SPECIALIZE += s_propagateNaNF16UI.o +SF_OBJS_SPECIALIZE += s_f32UIToCommonNaN.o +SF_OBJS_SPECIALIZE += s_commonNaNToF32UI.o +SF_OBJS_SPECIALIZE += s_propagateNaNF32UI.o +SF_OBJS_SPECIALIZE += s_f64UIToCommonNaN.o +SF_OBJS_SPECIALIZE += s_commonNaNToF64UI.o +SF_OBJS_SPECIALIZE += s_propagateNaNF64UI.o +SF_OBJS_SPECIALIZE += extF80M_isSignalingNaN.o +SF_OBJS_SPECIALIZE += s_extF80UIToCommonNaN.o +SF_OBJS_SPECIALIZE += s_commonNaNToExtF80UI.o +SF_OBJS_SPECIALIZE += s_propagateNaNExtF80UI.o +SF_OBJS_SPECIALIZE += f128M_isSignalingNaN.o +SF_OBJS_SPECIALIZE += s_f128UIToCommonNaN.o +SF_OBJS_SPECIALIZE += s_commonNaNToF128UI.o +SF_OBJS_SPECIALIZE += s_propagateNaNF128UI.o + +SF_OBJS_OTHERS := +SF_OBJS_OTHERS += s_roundToUI32.o +SF_OBJS_OTHERS += s_roundToUI64.o +SF_OBJS_OTHERS += s_roundToI32.o +SF_OBJS_OTHERS += s_roundToI64.o +SF_OBJS_OTHERS += s_normSubnormalF16Sig.o +SF_OBJS_OTHERS += s_roundPackToF16.o +SF_OBJS_OTHERS += s_normRoundPackToF16.o +SF_OBJS_OTHERS += s_addMagsF16.o +SF_OBJS_OTHERS += s_subMagsF16.o +SF_OBJS_OTHERS += s_mulAddF16.o +SF_OBJS_OTHERS += s_normSubnormalF32Sig.o +SF_OBJS_OTHERS += s_roundPackToF32.o +SF_OBJS_OTHERS += s_normRoundPackToF32.o +SF_OBJS_OTHERS += s_addMagsF32.o +SF_OBJS_OTHERS += s_subMagsF32.o +SF_OBJS_OTHERS += s_mulAddF32.o +SF_OBJS_OTHERS += s_normSubnormalF64Sig.o +SF_OBJS_OTHERS += s_roundPackToF64.o +SF_OBJS_OTHERS += s_normRoundPackToF64.o +SF_OBJS_OTHERS += s_addMagsF64.o +SF_OBJS_OTHERS += s_subMagsF64.o +SF_OBJS_OTHERS += s_mulAddF64.o +SF_OBJS_OTHERS += s_normSubnormalExtF80Sig.o +SF_OBJS_OTHERS += s_roundPackToExtF80.o +SF_OBJS_OTHERS += s_normRoundPackToExtF80.o +SF_OBJS_OTHERS += s_addMagsExtF80.o +SF_OBJS_OTHERS += s_subMagsExtF80.o +SF_OBJS_OTHERS += s_normSubnormalF128Sig.o +SF_OBJS_OTHERS += s_roundPackToF128.o +SF_OBJS_OTHERS += s_normRoundPackToF128.o +SF_OBJS_OTHERS += s_addMagsF128.o +SF_OBJS_OTHERS += s_subMagsF128.o +SF_OBJS_OTHERS += s_mulAddF128.o +SF_OBJS_OTHERS += softfloat_state.o +SF_OBJS_OTHERS += ui32_to_f16.o +SF_OBJS_OTHERS += ui32_to_f32.o +SF_OBJS_OTHERS += ui32_to_f64.o +SF_OBJS_OTHERS += ui32_to_extF80.o +SF_OBJS_OTHERS += ui32_to_extF80M.o +SF_OBJS_OTHERS += ui32_to_f128.o +SF_OBJS_OTHERS += ui32_to_f128M.o +SF_OBJS_OTHERS += ui64_to_f16.o +SF_OBJS_OTHERS += ui64_to_f32.o +SF_OBJS_OTHERS += ui64_to_f64.o +SF_OBJS_OTHERS += ui64_to_extF80.o +SF_OBJS_OTHERS += ui64_to_extF80M.o +SF_OBJS_OTHERS += ui64_to_f128.o +SF_OBJS_OTHERS += ui64_to_f128M.o +SF_OBJS_OTHERS += i32_to_f16.o +SF_OBJS_OTHERS += i32_to_f32.o +SF_OBJS_OTHERS += i32_to_f64.o +SF_OBJS_OTHERS += i32_to_extF80.o +SF_OBJS_OTHERS += i32_to_extF80M.o +SF_OBJS_OTHERS += i32_to_f128.o +SF_OBJS_OTHERS += i32_to_f128M.o +SF_OBJS_OTHERS += i64_to_f16.o +SF_OBJS_OTHERS += i64_to_f32.o +SF_OBJS_OTHERS += i64_to_f64.o +SF_OBJS_OTHERS += i64_to_extF80.o +SF_OBJS_OTHERS += i64_to_extF80M.o +SF_OBJS_OTHERS += i64_to_f128.o +SF_OBJS_OTHERS += i64_to_f128M.o +SF_OBJS_OTHERS += f16_to_ui32.o +SF_OBJS_OTHERS += f16_to_ui64.o +SF_OBJS_OTHERS += f16_to_i32.o +SF_OBJS_OTHERS += f16_to_i64.o +SF_OBJS_OTHERS += f16_to_ui32_r_minMag.o +SF_OBJS_OTHERS += f16_to_ui64_r_minMag.o +SF_OBJS_OTHERS += f16_to_i32_r_minMag.o +SF_OBJS_OTHERS += f16_to_i64_r_minMag.o +SF_OBJS_OTHERS += f16_to_f32.o +SF_OBJS_OTHERS += f16_to_f64.o +SF_OBJS_OTHERS += f16_to_extF80.o +SF_OBJS_OTHERS += f16_to_extF80M.o +SF_OBJS_OTHERS += f16_to_f128.o +SF_OBJS_OTHERS += f16_to_f128M.o +SF_OBJS_OTHERS += f16_roundToInt.o +SF_OBJS_OTHERS += f16_add.o +SF_OBJS_OTHERS += f16_sub.o +SF_OBJS_OTHERS += f16_mul.o +SF_OBJS_OTHERS += f16_mulAdd.o +SF_OBJS_OTHERS += f16_div.o +SF_OBJS_OTHERS += f16_rem.o +SF_OBJS_OTHERS += f16_sqrt.o +SF_OBJS_OTHERS += f16_eq.o +SF_OBJS_OTHERS += f16_le.o +SF_OBJS_OTHERS += f16_lt.o +SF_OBJS_OTHERS += f16_eq_signaling.o +SF_OBJS_OTHERS += f16_le_quiet.o +SF_OBJS_OTHERS += f16_lt_quiet.o +SF_OBJS_OTHERS += f16_isSignalingNaN.o +SF_OBJS_OTHERS += f32_to_ui32.o +SF_OBJS_OTHERS += f32_to_ui64.o +SF_OBJS_OTHERS += f32_to_i32.o +SF_OBJS_OTHERS += f32_to_i64.o +SF_OBJS_OTHERS += f32_to_ui32_r_minMag.o +SF_OBJS_OTHERS += f32_to_ui64_r_minMag.o +SF_OBJS_OTHERS += f32_to_i32_r_minMag.o +SF_OBJS_OTHERS += f32_to_i64_r_minMag.o +SF_OBJS_OTHERS += f32_to_f16.o +SF_OBJS_OTHERS += f32_to_f64.o +SF_OBJS_OTHERS += f32_to_extF80.o +SF_OBJS_OTHERS += f32_to_extF80M.o +SF_OBJS_OTHERS += f32_to_f128.o +SF_OBJS_OTHERS += f32_to_f128M.o +SF_OBJS_OTHERS += f32_roundToInt.o +SF_OBJS_OTHERS += f32_add.o +SF_OBJS_OTHERS += f32_sub.o +SF_OBJS_OTHERS += f32_mul.o +SF_OBJS_OTHERS += f32_mulAdd.o +SF_OBJS_OTHERS += f32_div.o +SF_OBJS_OTHERS += f32_rem.o +SF_OBJS_OTHERS += f32_sqrt.o +SF_OBJS_OTHERS += f32_eq.o +SF_OBJS_OTHERS += f32_le.o +SF_OBJS_OTHERS += f32_lt.o +SF_OBJS_OTHERS += f32_eq_signaling.o +SF_OBJS_OTHERS += f32_le_quiet.o +SF_OBJS_OTHERS += f32_lt_quiet.o +SF_OBJS_OTHERS += f32_isSignalingNaN.o +SF_OBJS_OTHERS += f64_to_ui32.o +SF_OBJS_OTHERS += f64_to_ui64.o +SF_OBJS_OTHERS += f64_to_i32.o +SF_OBJS_OTHERS += f64_to_i64.o +SF_OBJS_OTHERS += f64_to_ui32_r_minMag.o +SF_OBJS_OTHERS += f64_to_ui64_r_minMag.o +SF_OBJS_OTHERS += f64_to_i32_r_minMag.o +SF_OBJS_OTHERS += f64_to_i64_r_minMag.o +SF_OBJS_OTHERS += f64_to_f16.o +SF_OBJS_OTHERS += f64_to_f32.o +SF_OBJS_OTHERS += f64_to_extF80.o +SF_OBJS_OTHERS += f64_to_extF80M.o +SF_OBJS_OTHERS += f64_to_f128.o +SF_OBJS_OTHERS += f64_to_f128M.o +SF_OBJS_OTHERS += f64_roundToInt.o +SF_OBJS_OTHERS += f64_add.o +SF_OBJS_OTHERS += f64_sub.o +SF_OBJS_OTHERS += f64_mul.o +SF_OBJS_OTHERS += f64_mulAdd.o +SF_OBJS_OTHERS += f64_div.o +SF_OBJS_OTHERS += f64_rem.o +SF_OBJS_OTHERS += f64_sqrt.o +SF_OBJS_OTHERS += f64_eq.o +SF_OBJS_OTHERS += f64_le.o +SF_OBJS_OTHERS += f64_lt.o +SF_OBJS_OTHERS += f64_eq_signaling.o +SF_OBJS_OTHERS += f64_le_quiet.o +SF_OBJS_OTHERS += f64_lt_quiet.o +SF_OBJS_OTHERS += f64_isSignalingNaN.o +SF_OBJS_OTHERS += extF80_to_ui32.o +SF_OBJS_OTHERS += extF80_to_ui64.o +SF_OBJS_OTHERS += extF80_to_i32.o +SF_OBJS_OTHERS += extF80_to_i64.o +SF_OBJS_OTHERS += extF80_to_ui32_r_minMag.o +SF_OBJS_OTHERS += extF80_to_ui64_r_minMag.o +SF_OBJS_OTHERS += extF80_to_i32_r_minMag.o +SF_OBJS_OTHERS += extF80_to_i64_r_minMag.o +SF_OBJS_OTHERS += extF80_to_f16.o +SF_OBJS_OTHERS += extF80_to_f32.o +SF_OBJS_OTHERS += extF80_to_f64.o +SF_OBJS_OTHERS += extF80_to_f128.o +SF_OBJS_OTHERS += extF80_roundToInt.o +SF_OBJS_OTHERS += extF80_add.o +SF_OBJS_OTHERS += extF80_sub.o +SF_OBJS_OTHERS += extF80_mul.o +SF_OBJS_OTHERS += extF80_div.o +SF_OBJS_OTHERS += extF80_rem.o +SF_OBJS_OTHERS += extF80_sqrt.o +SF_OBJS_OTHERS += extF80_eq.o +SF_OBJS_OTHERS += extF80_le.o +SF_OBJS_OTHERS += extF80_lt.o +SF_OBJS_OTHERS += extF80_eq_signaling.o +SF_OBJS_OTHERS += extF80_le_quiet.o +SF_OBJS_OTHERS += extF80_lt_quiet.o +SF_OBJS_OTHERS += extF80_isSignalingNaN.o +SF_OBJS_OTHERS += extF80M_to_ui32.o +SF_OBJS_OTHERS += extF80M_to_ui64.o +SF_OBJS_OTHERS += extF80M_to_i32.o +SF_OBJS_OTHERS += extF80M_to_i64.o +SF_OBJS_OTHERS += extF80M_to_ui32_r_minMag.o +SF_OBJS_OTHERS += extF80M_to_ui64_r_minMag.o +SF_OBJS_OTHERS += extF80M_to_i32_r_minMag.o +SF_OBJS_OTHERS += extF80M_to_i64_r_minMag.o +SF_OBJS_OTHERS += extF80M_to_f16.o +SF_OBJS_OTHERS += extF80M_to_f32.o +SF_OBJS_OTHERS += extF80M_to_f64.o +SF_OBJS_OTHERS += extF80M_to_f128M.o +SF_OBJS_OTHERS += extF80M_roundToInt.o +SF_OBJS_OTHERS += extF80M_add.o +SF_OBJS_OTHERS += extF80M_sub.o +SF_OBJS_OTHERS += extF80M_mul.o +SF_OBJS_OTHERS += extF80M_div.o +SF_OBJS_OTHERS += extF80M_rem.o +SF_OBJS_OTHERS += extF80M_sqrt.o +SF_OBJS_OTHERS += extF80M_eq.o +SF_OBJS_OTHERS += extF80M_le.o +SF_OBJS_OTHERS += extF80M_lt.o +SF_OBJS_OTHERS += extF80M_eq_signaling.o +SF_OBJS_OTHERS += extF80M_le_quiet.o +SF_OBJS_OTHERS += extF80M_lt_quiet.o +SF_OBJS_OTHERS += f128_to_ui32.o +SF_OBJS_OTHERS += f128_to_ui64.o +SF_OBJS_OTHERS += f128_to_i32.o +SF_OBJS_OTHERS += f128_to_i64.o +SF_OBJS_OTHERS += f128_to_ui32_r_minMag.o +SF_OBJS_OTHERS += f128_to_ui64_r_minMag.o +SF_OBJS_OTHERS += f128_to_i32_r_minMag.o +SF_OBJS_OTHERS += f128_to_i64_r_minMag.o +SF_OBJS_OTHERS += f128_to_f16.o +SF_OBJS_OTHERS += f128_to_f32.o +SF_OBJS_OTHERS += f128_to_extF80.o +SF_OBJS_OTHERS += f128_to_f64.o +SF_OBJS_OTHERS += f128_roundToInt.o +SF_OBJS_OTHERS += f128_add.o +SF_OBJS_OTHERS += f128_sub.o +SF_OBJS_OTHERS += f128_mul.o +SF_OBJS_OTHERS += f128_mulAdd.o +SF_OBJS_OTHERS += f128_div.o +SF_OBJS_OTHERS += f128_rem.o +SF_OBJS_OTHERS += f128_sqrt.o +SF_OBJS_OTHERS += f128_eq.o +SF_OBJS_OTHERS += f128_le.o +SF_OBJS_OTHERS += f128_lt.o +SF_OBJS_OTHERS += f128_eq_signaling.o +SF_OBJS_OTHERS += f128_le_quiet.o +SF_OBJS_OTHERS += f128_lt_quiet.o +SF_OBJS_OTHERS += f128_isSignalingNaN.o +SF_OBJS_OTHERS += f128M_to_ui32.o +SF_OBJS_OTHERS += f128M_to_ui64.o +SF_OBJS_OTHERS += f128M_to_i32.o +SF_OBJS_OTHERS += f128M_to_i64.o +SF_OBJS_OTHERS += f128M_to_ui32_r_minMag.o +SF_OBJS_OTHERS += f128M_to_ui64_r_minMag.o +SF_OBJS_OTHERS += f128M_to_i32_r_minMag.o +SF_OBJS_OTHERS += f128M_to_i64_r_minMag.o +SF_OBJS_OTHERS += f128M_to_f16.o +SF_OBJS_OTHERS += f128M_to_f32.o +SF_OBJS_OTHERS += f128M_to_extF80M.o +SF_OBJS_OTHERS += f128M_to_f64.o +SF_OBJS_OTHERS += f128M_roundToInt.o +SF_OBJS_OTHERS += f128M_add.o +SF_OBJS_OTHERS += f128M_sub.o +SF_OBJS_OTHERS += f128M_mul.o +SF_OBJS_OTHERS += f128M_mulAdd.o +SF_OBJS_OTHERS += f128M_div.o +SF_OBJS_OTHERS += f128M_rem.o +SF_OBJS_OTHERS += f128M_sqrt.o +SF_OBJS_OTHERS += f128M_eq.o +SF_OBJS_OTHERS += f128M_le.o +SF_OBJS_OTHERS += f128M_lt.o +SF_OBJS_OTHERS += f128M_eq_signaling.o +SF_OBJS_OTHERS += f128M_le_quiet.o +SF_OBJS_OTHERS += f128M_lt_quiet.o + +SF_OBJS_ALL_NOSPEC := +SF_OBJS_ALL_NOSPEC += $(SF_OBJS_PRIMITIVES) +SF_OBJS_ALL_NOSPEC += $(SF_OBJS_OTHERS) + +SF_OBJS_ALL := +SF_OBJS_ALL += $(SF_OBJS_ALL_NOSPEC) +SF_OBJS_ALL += $(SF_OBJS_SPECIALIZE) + +# testfloat objects +TF_OBJS_GENCASES := +TF_OBJS_GENCASES += genCases_ui32.o +TF_OBJS_GENCASES += genCases_ui64.o +TF_OBJS_GENCASES += genCases_i32.o +TF_OBJS_GENCASES += genCases_i64.o +TF_OBJS_GENCASES += genCases_f16.o +TF_OBJS_GENCASES += genCases_f32.o +TF_OBJS_GENCASES += genCases_f64.o +TF_OBJS_GENCASES += genCases_extF80.o +TF_OBJS_GENCASES += genCases_f128.o + +TF_OBJS_WRITECASE := +TF_OBJS_WRITECASE += writeCase_a_ui32.o +TF_OBJS_WRITECASE += writeCase_a_ui64.o +TF_OBJS_WRITECASE += writeCase_a_f16.o +TF_OBJS_WRITECASE += writeCase_ab_f16.o +TF_OBJS_WRITECASE += writeCase_abc_f16.o +TF_OBJS_WRITECASE += writeCase_a_f32.o +TF_OBJS_WRITECASE += writeCase_ab_f32.o +TF_OBJS_WRITECASE += writeCase_abc_f32.o +TF_OBJS_WRITECASE += writeCase_a_f64.o +TF_OBJS_WRITECASE += writeCase_ab_f64.o +TF_OBJS_WRITECASE += writeCase_abc_f64.o +TF_OBJS_WRITECASE += writeCase_a_extF80M.o +TF_OBJS_WRITECASE += writeCase_ab_extF80M.o +TF_OBJS_WRITECASE += writeCase_a_f128M.o +TF_OBJS_WRITECASE += writeCase_ab_f128M.o +TF_OBJS_WRITECASE += writeCase_abc_f128M.o +TF_OBJS_WRITECASE += writeCase_z_bool.o +TF_OBJS_WRITECASE += writeCase_z_ui32.o +TF_OBJS_WRITECASE += writeCase_z_ui64.o +TF_OBJS_WRITECASE += writeCase_z_f16.o +TF_OBJS_WRITECASE += writeCase_z_f32.o +TF_OBJS_WRITECASE += writeCase_z_f64.o +TF_OBJS_WRITECASE += writeCase_z_extF80M.o +TF_OBJS_WRITECASE += writeCase_z_f128M.o + +TF_OBJS_TEST := +TF_OBJS_TEST += test_a_ui32_z_f16.o +TF_OBJS_TEST += test_a_ui32_z_f32.o +TF_OBJS_TEST += test_a_ui32_z_f64.o +TF_OBJS_TEST += test_a_ui32_z_extF80.o +TF_OBJS_TEST += test_a_ui32_z_f128.o +TF_OBJS_TEST += test_a_ui64_z_f16.o +TF_OBJS_TEST += test_a_ui64_z_f32.o +TF_OBJS_TEST += test_a_ui64_z_f64.o +TF_OBJS_TEST += test_a_ui64_z_extF80.o +TF_OBJS_TEST += test_a_ui64_z_f128.o +TF_OBJS_TEST += test_a_i32_z_f16.o +TF_OBJS_TEST += test_a_i32_z_f32.o +TF_OBJS_TEST += test_a_i32_z_f64.o +TF_OBJS_TEST += test_a_i32_z_extF80.o +TF_OBJS_TEST += test_a_i32_z_f128.o +TF_OBJS_TEST += test_a_i64_z_f16.o +TF_OBJS_TEST += test_a_i64_z_f32.o +TF_OBJS_TEST += test_a_i64_z_f64.o +TF_OBJS_TEST += test_a_i64_z_extF80.o +TF_OBJS_TEST += test_a_i64_z_f128.o +TF_OBJS_TEST += test_a_f16_z_ui32_rx.o +TF_OBJS_TEST += test_a_f16_z_ui64_rx.o +TF_OBJS_TEST += test_a_f16_z_i32_rx.o +TF_OBJS_TEST += test_a_f16_z_i64_rx.o +TF_OBJS_TEST += test_a_f16_z_ui32_x.o +TF_OBJS_TEST += test_a_f16_z_ui64_x.o +TF_OBJS_TEST += test_a_f16_z_i32_x.o +TF_OBJS_TEST += test_a_f16_z_i64_x.o +TF_OBJS_TEST += test_a_f16_z_f32.o +TF_OBJS_TEST += test_a_f16_z_f64.o +TF_OBJS_TEST += test_a_f16_z_extF80.o +TF_OBJS_TEST += test_a_f16_z_f128.o +TF_OBJS_TEST += test_az_f16.o +TF_OBJS_TEST += test_az_f16_rx.o +TF_OBJS_TEST += test_abz_f16.o +TF_OBJS_TEST += test_abcz_f16.o +TF_OBJS_TEST += test_ab_f16_z_bool.o +TF_OBJS_TEST += test_a_f32_z_ui32_rx.o +TF_OBJS_TEST += test_a_f32_z_ui64_rx.o +TF_OBJS_TEST += test_a_f32_z_i32_rx.o +TF_OBJS_TEST += test_a_f32_z_i64_rx.o +TF_OBJS_TEST += test_a_f32_z_ui32_x.o +TF_OBJS_TEST += test_a_f32_z_ui64_x.o +TF_OBJS_TEST += test_a_f32_z_i32_x.o +TF_OBJS_TEST += test_a_f32_z_i64_x.o +TF_OBJS_TEST += test_a_f32_z_f16.o +TF_OBJS_TEST += test_a_f32_z_f64.o +TF_OBJS_TEST += test_a_f32_z_extF80.o +TF_OBJS_TEST += test_a_f32_z_f128.o +TF_OBJS_TEST += test_az_f32.o +TF_OBJS_TEST += test_az_f32_rx.o +TF_OBJS_TEST += test_abz_f32.o +TF_OBJS_TEST += test_abcz_f32.o +TF_OBJS_TEST += test_ab_f32_z_bool.o +TF_OBJS_TEST += test_a_f64_z_ui32_rx.o +TF_OBJS_TEST += test_a_f64_z_ui64_rx.o +TF_OBJS_TEST += test_a_f64_z_i32_rx.o +TF_OBJS_TEST += test_a_f64_z_i64_rx.o +TF_OBJS_TEST += test_a_f64_z_ui32_x.o +TF_OBJS_TEST += test_a_f64_z_ui64_x.o +TF_OBJS_TEST += test_a_f64_z_i32_x.o +TF_OBJS_TEST += test_a_f64_z_i64_x.o +TF_OBJS_TEST += test_a_f64_z_f16.o +TF_OBJS_TEST += test_a_f64_z_f32.o +TF_OBJS_TEST += test_a_f64_z_extF80.o +TF_OBJS_TEST += test_a_f64_z_f128.o +TF_OBJS_TEST += test_az_f64.o +TF_OBJS_TEST += test_az_f64_rx.o +TF_OBJS_TEST += test_abz_f64.o +TF_OBJS_TEST += test_abcz_f64.o +TF_OBJS_TEST += test_ab_f64_z_bool.o +TF_OBJS_TEST += test_a_extF80_z_ui32_rx.o +TF_OBJS_TEST += test_a_extF80_z_ui64_rx.o +TF_OBJS_TEST += test_a_extF80_z_i32_rx.o +TF_OBJS_TEST += test_a_extF80_z_i64_rx.o +TF_OBJS_TEST += test_a_extF80_z_ui32_x.o +TF_OBJS_TEST += test_a_extF80_z_ui64_x.o +TF_OBJS_TEST += test_a_extF80_z_i32_x.o +TF_OBJS_TEST += test_a_extF80_z_i64_x.o +TF_OBJS_TEST += test_a_extF80_z_f16.o +TF_OBJS_TEST += test_a_extF80_z_f32.o +TF_OBJS_TEST += test_a_extF80_z_f64.o +TF_OBJS_TEST += test_a_extF80_z_f128.o +TF_OBJS_TEST += test_az_extF80.o +TF_OBJS_TEST += test_az_extF80_rx.o +TF_OBJS_TEST += test_abz_extF80.o +TF_OBJS_TEST += test_ab_extF80_z_bool.o +TF_OBJS_TEST += test_a_f128_z_ui32_rx.o +TF_OBJS_TEST += test_a_f128_z_ui64_rx.o +TF_OBJS_TEST += test_a_f128_z_i32_rx.o +TF_OBJS_TEST += test_a_f128_z_i64_rx.o +TF_OBJS_TEST += test_a_f128_z_ui32_x.o +TF_OBJS_TEST += test_a_f128_z_ui64_x.o +TF_OBJS_TEST += test_a_f128_z_i32_x.o +TF_OBJS_TEST += test_a_f128_z_i64_x.o +TF_OBJS_TEST += test_a_f128_z_f16.o +TF_OBJS_TEST += test_a_f128_z_f32.o +TF_OBJS_TEST += test_a_f128_z_f64.o +TF_OBJS_TEST += test_a_f128_z_extF80.o +TF_OBJS_TEST += test_az_f128.o +TF_OBJS_TEST += test_az_f128_rx.o +TF_OBJS_TEST += test_abz_f128.o +TF_OBJS_TEST += test_abcz_f128.o +TF_OBJS_TEST += test_ab_f128_z_bool.o + +TF_OBJS_LIB := +TF_OBJS_LIB += uint128_inline.o +TF_OBJS_LIB += uint128.o +TF_OBJS_LIB += fail.o +TF_OBJS_LIB += functions_common.o +TF_OBJS_LIB += functionInfos.o +TF_OBJS_LIB += standardFunctionInfos.o +TF_OBJS_LIB += random.o +TF_OBJS_LIB += genCases_common.o +TF_OBJS_LIB += $(TF_OBJS_GENCASES) +TF_OBJS_LIB += genCases_writeTestsTotal.o +TF_OBJS_LIB += verCases_inline.o +TF_OBJS_LIB += verCases_common.o +TF_OBJS_LIB += verCases_writeFunctionName.o +TF_OBJS_LIB += readHex.o +TF_OBJS_LIB += writeHex.o +TF_OBJS_LIB += $(TF_OBJS_WRITECASE) +TF_OBJS_LIB += testLoops_common.o +TF_OBJS_LIB += $(TF_OBJS_TEST) + +BINARIES := fp-test$(EXESUF) fp-bench$(EXESUF) + +# everything depends on config-host.h because platform.h includes it +all: $(BUILD_DIR)/config-host.h + $(MAKE) $(BINARIES) + +$(LIBQEMUUTIL): + $(MAKE) -C $(BUILD_DIR) libqemuutil.a + +$(BUILD_DIR)/config-host.h: + $(MAKE) -C $(BUILD_DIR) config-host.h + +# libtestfloat.a depends on libsoftfloat.a, so specify it first +FP_TEST_LIBS := libtestfloat.a libsoftfloat.a $(LIBQEMUUTIL) + +fp-test$(EXESUF): fp-test.o slowfloat.o $(QEMU_SOFTFLOAT_OBJ) $(FP_TEST_LIBS) + +# Custom rule to build with SF_CFLAGS +SF_BUILD = $(call quiet-command,$(CC) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \ + $(QEMU_CFLAGS) $(SF_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ + $($@-cflags) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + +$(SF_OBJS_ALL_NOSPEC): %.o: $(SF_SOURCE_DIR)/%.c + $(SF_BUILD) +$(SF_OBJS_SPECIALIZE): %.o: $(SF_SPECIALIZE_DIR)/%.c + $(SF_BUILD) + +libsoftfloat.a: $(SF_OBJS_ALL) + +# Custom rule to build with TF_CFLAGS +$(TF_OBJS_LIB) slowfloat.o: %.o: $(TF_SOURCE_DIR)/%.c + $(call quiet-command,$(CC) $(QEMU_LOCAL_INCLUDES) $(QEMU_INCLUDES) \ + $(QEMU_CFLAGS) $(TF_CFLAGS) $(QEMU_DGFLAGS) $(CFLAGS) \ + $($@-cflags) -c -o $@ $<,"CC","$(TARGET_DIR)$@") + +libtestfloat.a: $(TF_OBJS_LIB) + +fp-bench$(EXESUF): fp-bench.o $(QEMU_SOFTFLOAT_OBJ) $(LIBQEMUUTIL) + +clean: + rm -f *.o *.d $(BINARIES) + rm -f *.gcno *.gcda *.gcov + rm -f fp-test$(EXESUF) + rm -f fp-bench$(EXESUF) + rm -f libsoftfloat.a + rm -f libtestfloat.a + +-include $(wildcard *.d) diff --git a/tests/fp/berkeley-softfloat-3 b/tests/fp/berkeley-softfloat-3 new file mode 160000 index 0000000000..b64af41c32 --- /dev/null +++ b/tests/fp/berkeley-softfloat-3 @@ -0,0 +1 @@ +Subproject commit b64af41c3276f97f0e181920400ee056b9c88037 diff --git a/tests/fp/berkeley-testfloat-3 b/tests/fp/berkeley-testfloat-3 new file mode 160000 index 0000000000..5a59dcec19 --- /dev/null +++ b/tests/fp/berkeley-testfloat-3 @@ -0,0 +1 @@ +Subproject commit 5a59dcec19327396a011a17fd924aed4fec416b3 diff --git a/tests/fp/fp-bench.c b/tests/fp/fp-bench.c new file mode 100644 index 0000000000..4ba5e1d2d4 --- /dev/null +++ b/tests/fp/fp-bench.c @@ -0,0 +1,631 @@ +/* + * fp-bench.c - A collection of simple floating point microbenchmarks. + * + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#ifndef HW_POISON_H +#error Must define HW_POISON_H to work around TARGET_* poisoning +#endif + +#include "qemu/osdep.h" +#include +#include +#include "qemu/timer.h" +#include "fpu/softfloat.h" + +/* amortize the computation of random inputs */ +#define OPS_PER_ITER 50000 + +#define MAX_OPERANDS 3 + +#define SEED_A 0xdeadfacedeadface +#define SEED_B 0xbadc0feebadc0fee +#define SEED_C 0xbeefdeadbeefdead + +enum op { + OP_ADD, + OP_SUB, + OP_MUL, + OP_DIV, + OP_FMA, + OP_SQRT, + OP_CMP, + OP_MAX_NR, +}; + +static const char * const op_names[] = { + [OP_ADD] = "add", + [OP_SUB] = "sub", + [OP_MUL] = "mul", + [OP_DIV] = "div", + [OP_FMA] = "mulAdd", + [OP_SQRT] = "sqrt", + [OP_CMP] = "cmp", + [OP_MAX_NR] = NULL, +}; + +enum precision { + PREC_SINGLE, + PREC_DOUBLE, + PREC_FLOAT32, + PREC_FLOAT64, + PREC_MAX_NR, +}; + +enum rounding { + ROUND_EVEN, + ROUND_ZERO, + ROUND_DOWN, + ROUND_UP, + ROUND_TIEAWAY, + N_ROUND_MODES, +}; + +static const char * const round_names[] = { + [ROUND_EVEN] = "even", + [ROUND_ZERO] = "zero", + [ROUND_DOWN] = "down", + [ROUND_UP] = "up", + [ROUND_TIEAWAY] = "tieaway", +}; + +enum tester { + TESTER_SOFT, + TESTER_HOST, + TESTER_MAX_NR, +}; + +static const char * const tester_names[] = { + [TESTER_SOFT] = "soft", + [TESTER_HOST] = "host", + [TESTER_MAX_NR] = NULL, +}; + +union fp { + float f; + double d; + float32 f32; + float64 f64; + uint64_t u64; +}; + +struct op_state; + +typedef float (*float_func_t)(const struct op_state *s); +typedef double (*double_func_t)(const struct op_state *s); + +union fp_func { + float_func_t float_func; + double_func_t double_func; +}; + +typedef void (*bench_func_t)(void); + +struct op_desc { + const char * const name; +}; + +#define DEFAULT_DURATION_SECS 1 + +static uint64_t random_ops[MAX_OPERANDS] = { + SEED_A, SEED_B, SEED_C, +}; +static float_status soft_status; +static enum precision precision; +static enum op operation; +static enum tester tester; +static uint64_t n_completed_ops; +static unsigned int duration = DEFAULT_DURATION_SECS; +static int64_t ns_elapsed; +/* disable optimizations with volatile */ +static volatile union fp res; + +/* + * From: https://en.wikipedia.org/wiki/Xorshift + * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only + * guaranteed to be >= INT_MAX). + */ +static uint64_t xorshift64star(uint64_t x) +{ + x ^= x >> 12; /* a */ + x ^= x << 25; /* b */ + x ^= x >> 27; /* c */ + return x * UINT64_C(2685821657736338717); +} + +static void update_random_ops(int n_ops, enum precision prec) +{ + int i; + + for (i = 0; i < n_ops; i++) { + uint64_t r = random_ops[i]; + + switch (prec) { + case PREC_SINGLE: + case PREC_FLOAT32: + do { + r = xorshift64star(r); + } while (!float32_is_normal(r)); + break; + case PREC_DOUBLE: + case PREC_FLOAT64: + do { + r = xorshift64star(r); + } while (!float64_is_normal(r)); + break; + default: + g_assert_not_reached(); + } + random_ops[i] = r; + } +} + +static void fill_random(union fp *ops, int n_ops, enum precision prec, + bool no_neg) +{ + int i; + + for (i = 0; i < n_ops; i++) { + switch (prec) { + case PREC_SINGLE: + case PREC_FLOAT32: + ops[i].f32 = make_float32(random_ops[i]); + if (no_neg && float32_is_neg(ops[i].f32)) { + ops[i].f32 = float32_chs(ops[i].f32); + } + break; + case PREC_DOUBLE: + case PREC_FLOAT64: + ops[i].f64 = make_float64(random_ops[i]); + if (no_neg && float64_is_neg(ops[i].f64)) { + ops[i].f64 = float64_chs(ops[i].f64); + } + break; + default: + g_assert_not_reached(); + } + } +} + +/* + * The main benchmark function. Instead of (ab)using macros, we rely + * on the compiler to unfold this at compile-time. + */ +static void bench(enum precision prec, enum op op, int n_ops, bool no_neg) +{ + int64_t tf = get_clock() + duration * 1000000000LL; + + while (get_clock() < tf) { + union fp ops[MAX_OPERANDS]; + int64_t t0; + int i; + + update_random_ops(n_ops, prec); + switch (prec) { + case PREC_SINGLE: + fill_random(ops, n_ops, prec, no_neg); + t0 = get_clock(); + for (i = 0; i < OPS_PER_ITER; i++) { + float a = ops[0].f; + float b = ops[1].f; + float c = ops[2].f; + + switch (op) { + case OP_ADD: + res.f = a + b; + break; + case OP_SUB: + res.f = a - b; + break; + case OP_MUL: + res.f = a * b; + break; + case OP_DIV: + res.f = a / b; + break; + case OP_FMA: + res.f = fmaf(a, b, c); + break; + case OP_SQRT: + res.f = sqrtf(a); + break; + case OP_CMP: + res.u64 = isgreater(a, b); + break; + default: + g_assert_not_reached(); + } + } + break; + case PREC_DOUBLE: + fill_random(ops, n_ops, prec, no_neg); + t0 = get_clock(); + for (i = 0; i < OPS_PER_ITER; i++) { + double a = ops[0].d; + double b = ops[1].d; + double c = ops[2].d; + + switch (op) { + case OP_ADD: + res.d = a + b; + break; + case OP_SUB: + res.d = a - b; + break; + case OP_MUL: + res.d = a * b; + break; + case OP_DIV: + res.d = a / b; + break; + case OP_FMA: + res.d = fma(a, b, c); + break; + case OP_SQRT: + res.d = sqrt(a); + break; + case OP_CMP: + res.u64 = isgreater(a, b); + break; + default: + g_assert_not_reached(); + } + } + break; + case PREC_FLOAT32: + fill_random(ops, n_ops, prec, no_neg); + t0 = get_clock(); + for (i = 0; i < OPS_PER_ITER; i++) { + float32 a = ops[0].f32; + float32 b = ops[1].f32; + float32 c = ops[2].f32; + + switch (op) { + case OP_ADD: + res.f32 = float32_add(a, b, &soft_status); + break; + case OP_SUB: + res.f32 = float32_sub(a, b, &soft_status); + break; + case OP_MUL: + res.f = float32_mul(a, b, &soft_status); + break; + case OP_DIV: + res.f32 = float32_div(a, b, &soft_status); + break; + case OP_FMA: + res.f32 = float32_muladd(a, b, c, 0, &soft_status); + break; + case OP_SQRT: + res.f32 = float32_sqrt(a, &soft_status); + break; + case OP_CMP: + res.u64 = float32_compare_quiet(a, b, &soft_status); + break; + default: + g_assert_not_reached(); + } + } + break; + case PREC_FLOAT64: + fill_random(ops, n_ops, prec, no_neg); + t0 = get_clock(); + for (i = 0; i < OPS_PER_ITER; i++) { + float64 a = ops[0].f64; + float64 b = ops[1].f64; + float64 c = ops[2].f64; + + switch (op) { + case OP_ADD: + res.f64 = float64_add(a, b, &soft_status); + break; + case OP_SUB: + res.f64 = float64_sub(a, b, &soft_status); + break; + case OP_MUL: + res.f = float64_mul(a, b, &soft_status); + break; + case OP_DIV: + res.f64 = float64_div(a, b, &soft_status); + break; + case OP_FMA: + res.f64 = float64_muladd(a, b, c, 0, &soft_status); + break; + case OP_SQRT: + res.f64 = float64_sqrt(a, &soft_status); + break; + case OP_CMP: + res.u64 = float64_compare_quiet(a, b, &soft_status); + break; + default: + g_assert_not_reached(); + } + } + break; + default: + g_assert_not_reached(); + } + ns_elapsed += get_clock() - t0; + n_completed_ops += OPS_PER_ITER; + } +} + +#define GEN_BENCH(name, type, prec, op, n_ops) \ + static void __attribute__((flatten)) name(void) \ + { \ + bench(prec, op, n_ops, false); \ + } + +#define GEN_BENCH_NO_NEG(name, type, prec, op, n_ops) \ + static void __attribute__((flatten)) name(void) \ + { \ + bench(prec, op, n_ops, true); \ + } + +#define GEN_BENCH_ALL_TYPES(opname, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _float, float, PREC_SINGLE, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _double, double, PREC_DOUBLE, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _float32, float32, PREC_FLOAT32, op, n_ops) \ + GEN_BENCH(bench_ ## opname ## _float64, float64, PREC_FLOAT64, op, n_ops) + +GEN_BENCH_ALL_TYPES(add, OP_ADD, 2) +GEN_BENCH_ALL_TYPES(sub, OP_SUB, 2) +GEN_BENCH_ALL_TYPES(mul, OP_MUL, 2) +GEN_BENCH_ALL_TYPES(div, OP_DIV, 2) +GEN_BENCH_ALL_TYPES(fma, OP_FMA, 3) +GEN_BENCH_ALL_TYPES(cmp, OP_CMP, 2) +#undef GEN_BENCH_ALL_TYPES + +#define GEN_BENCH_ALL_TYPES_NO_NEG(name, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float, float, PREC_SINGLE, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _double, double, PREC_DOUBLE, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float32, float32, PREC_FLOAT32, op, n) \ + GEN_BENCH_NO_NEG(bench_ ## name ## _float64, float64, PREC_FLOAT64, op, n) + +GEN_BENCH_ALL_TYPES_NO_NEG(sqrt, OP_SQRT, 1) +#undef GEN_BENCH_ALL_TYPES_NO_NEG + +#undef GEN_BENCH_NO_NEG +#undef GEN_BENCH + +#define GEN_BENCH_FUNCS(opname, op) \ + [op] = { \ + [PREC_SINGLE] = bench_ ## opname ## _float, \ + [PREC_DOUBLE] = bench_ ## opname ## _double, \ + [PREC_FLOAT32] = bench_ ## opname ## _float32, \ + [PREC_FLOAT64] = bench_ ## opname ## _float64, \ + } + +static const bench_func_t bench_funcs[OP_MAX_NR][PREC_MAX_NR] = { + GEN_BENCH_FUNCS(add, OP_ADD), + GEN_BENCH_FUNCS(sub, OP_SUB), + GEN_BENCH_FUNCS(mul, OP_MUL), + GEN_BENCH_FUNCS(div, OP_DIV), + GEN_BENCH_FUNCS(fma, OP_FMA), + GEN_BENCH_FUNCS(sqrt, OP_SQRT), + GEN_BENCH_FUNCS(cmp, OP_CMP), +}; + +#undef GEN_BENCH_FUNCS + +static void run_bench(void) +{ + bench_func_t f; + + f = bench_funcs[operation][precision]; + g_assert(f); + f(); +} + +/* @arr must be NULL-terminated */ +static int find_name(const char * const *arr, const char *name) +{ + int i; + + for (i = 0; arr[i] != NULL; i++) { + if (strcmp(name, arr[i]) == 0) { + return i; + } + } + return -1; +} + +static void usage_complete(int argc, char *argv[]) +{ + gchar *op_list = g_strjoinv(", ", (gchar **)op_names); + gchar *tester_list = g_strjoinv(", ", (gchar **)tester_names); + + fprintf(stderr, "Usage: %s [options]\n", argv[0]); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -d = duration, in seconds. Default: %d\n", + DEFAULT_DURATION_SECS); + fprintf(stderr, " -h = show this help message.\n"); + fprintf(stderr, " -o = floating point operation (%s). Default: %s\n", + op_list, op_names[0]); + fprintf(stderr, " -p = floating point precision (single, double). " + "Default: single\n"); + fprintf(stderr, " -r = rounding mode (even, zero, down, up, tieaway). " + "Default: even\n"); + fprintf(stderr, " -t = tester (%s). Default: %s\n", + tester_list, tester_names[0]); + fprintf(stderr, " -z = flush inputs to zero (soft tester only). " + "Default: disabled\n"); + fprintf(stderr, " -Z = flush output to zero (soft tester only). " + "Default: disabled\n"); + + g_free(tester_list); + g_free(op_list); +} + +static int round_name_to_mode(const char *name) +{ + int i; + + for (i = 0; i < N_ROUND_MODES; i++) { + if (!strcmp(round_names[i], name)) { + return i; + } + } + return -1; +} + +static void QEMU_NORETURN die_host_rounding(enum rounding rounding) +{ + fprintf(stderr, "fatal: '%s' rounding not supported on this host\n", + round_names[rounding]); + exit(EXIT_FAILURE); +} + +static void set_host_precision(enum rounding rounding) +{ + int rhost; + + switch (rounding) { + case ROUND_EVEN: + rhost = FE_TONEAREST; + break; + case ROUND_ZERO: + rhost = FE_TOWARDZERO; + break; + case ROUND_DOWN: + rhost = FE_DOWNWARD; + break; + case ROUND_UP: + rhost = FE_UPWARD; + break; + case ROUND_TIEAWAY: + die_host_rounding(rounding); + return; + default: + g_assert_not_reached(); + } + + if (fesetround(rhost)) { + die_host_rounding(rounding); + } +} + +static void set_soft_precision(enum rounding rounding) +{ + signed char mode; + + switch (rounding) { + case ROUND_EVEN: + mode = float_round_nearest_even; + break; + case ROUND_ZERO: + mode = float_round_to_zero; + break; + case ROUND_DOWN: + mode = float_round_down; + break; + case ROUND_UP: + mode = float_round_up; + break; + case ROUND_TIEAWAY: + mode = float_round_ties_away; + break; + default: + g_assert_not_reached(); + } + soft_status.float_rounding_mode = mode; +} + +static void parse_args(int argc, char *argv[]) +{ + int c; + int val; + int rounding = ROUND_EVEN; + + for (;;) { + c = getopt(argc, argv, "d:ho:p:r:t:zZ"); + if (c < 0) { + break; + } + switch (c) { + case 'd': + duration = atoi(optarg); + break; + case 'h': + usage_complete(argc, argv); + exit(EXIT_SUCCESS); + case 'o': + val = find_name(op_names, optarg); + if (val < 0) { + fprintf(stderr, "Unsupported op '%s'\n", optarg); + exit(EXIT_FAILURE); + } + operation = val; + break; + case 'p': + if (!strcmp(optarg, "single")) { + precision = PREC_SINGLE; + } else if (!strcmp(optarg, "double")) { + precision = PREC_DOUBLE; + } else { + fprintf(stderr, "Unsupported precision '%s'\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 'r': + rounding = round_name_to_mode(optarg); + if (rounding < 0) { + fprintf(stderr, "fatal: invalid rounding mode '%s'\n", optarg); + exit(EXIT_FAILURE); + } + break; + case 't': + val = find_name(tester_names, optarg); + if (val < 0) { + fprintf(stderr, "Unsupported tester '%s'\n", optarg); + exit(EXIT_FAILURE); + } + tester = val; + break; + case 'z': + soft_status.flush_inputs_to_zero = 1; + break; + case 'Z': + soft_status.flush_to_zero = 1; + break; + } + } + + /* set precision and rounding mode based on the tester */ + switch (tester) { + case TESTER_HOST: + set_host_precision(rounding); + break; + case TESTER_SOFT: + set_soft_precision(rounding); + switch (precision) { + case PREC_SINGLE: + precision = PREC_FLOAT32; + break; + case PREC_DOUBLE: + precision = PREC_FLOAT64; + break; + default: + g_assert_not_reached(); + } + break; + default: + g_assert_not_reached(); + } +} + +static void pr_stats(void) +{ + printf("%.2f MFlops\n", (double)n_completed_ops / ns_elapsed * 1e3); +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + run_bench(); + pr_stats(); + return 0; +} diff --git a/tests/fp/fp-test.c b/tests/fp/fp-test.c new file mode 100644 index 0000000000..7d0faf2b47 --- /dev/null +++ b/tests/fp/fp-test.c @@ -0,0 +1,1018 @@ +/* + * fp-test.c - test QEMU's softfloat implementation using Berkeley's Testfloat + * + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * This file is derived from testfloat/source/testsoftfloat.c. Its copyright + * info follows: + * + * Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 The Regents of the + * University of California. 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 the above copyright notice, + * this list of conditions, and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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_POISON_H +#error Must define HW_POISON_H to work around TARGET_* poisoning +#endif + +#include "qemu/osdep.h" +#include "qemu/cutils.h" +#include +#include "fpu/softfloat.h" +#include "platform.h" + +#include "fail.h" +#include "slowfloat.h" +#include "functions.h" +#include "genCases.h" +#include "verCases.h" +#include "writeCase.h" +#include "testLoops.h" + +typedef float16_t (*abz_f16)(float16_t, float16_t); +typedef bool (*ab_f16_z_bool)(float16_t, float16_t); +typedef float32_t (*abz_f32)(float32_t, float32_t); +typedef bool (*ab_f32_z_bool)(float32_t, float32_t); +typedef float64_t (*abz_f64)(float64_t, float64_t); +typedef bool (*ab_f64_z_bool)(float64_t, float64_t); +typedef void (*abz_extF80M)(const extFloat80_t *, const extFloat80_t *, + extFloat80_t *); +typedef bool (*ab_extF80M_z_bool)(const extFloat80_t *, const extFloat80_t *); +typedef void (*abz_f128M)(const float128_t *, const float128_t *, float128_t *); +typedef bool (*ab_f128M_z_bool)(const float128_t *, const float128_t *); + +static const char * const round_mode_names[] = { + [ROUND_NEAR_EVEN] = "even", + [ROUND_MINMAG] = "zero", + [ROUND_MIN] = "down", + [ROUND_MAX] = "up", + [ROUND_NEAR_MAXMAG] = "tieaway", + [ROUND_ODD] = "odd", +}; +static unsigned int *test_ops; +static unsigned int n_test_ops; +static unsigned int n_max_errors = 20; +static unsigned int test_round_mode = ROUND_NEAR_EVEN; +static unsigned int *round_modes; +static unsigned int n_round_modes; +static int test_level = 1; +static uint8_t slow_init_flags; +static uint8_t qemu_init_flags; + +/* qemu softfloat status */ +static float_status qsf; + +static const char commands_string[] = + "operations:\n" + " _to_ _add _eq\n" + " _to_ _sub _le\n" + " _to__r_minMag _mul _lt\n" + " _to_ _mulAdd _eq_signaling\n" + " _roundToInt _div _le_quiet\n" + " _rem _lt_quiet\n" + " _sqrt\n" + " Where : ui32, ui64, i32, i64\n" + " : f16, f32, f64, extF80, f128\n" + " If no operation is provided, all the above are tested\n" + "options:\n" + " -e = max error count per test. Default: 20. Set no limit with 0\n" + " -f = initial FP exception flags (vioux). Default: none\n" + " -l = thoroughness level (1 (default), 2)\n" + " -r = rounding mode (even (default), zero, down, up, tieaway, odd)\n" + " Set to 'all' to test all rounding modes, if applicable\n" + " -s = stop when a test fails"; + +static void usage_complete(int argc, char *argv[]) +{ + fprintf(stderr, "Usage: %s [options] [operation1 ...]\n", argv[0]); + fprintf(stderr, "%s\n", commands_string); + exit(EXIT_FAILURE); +} + +/* keep wrappers separate but do not bother defining headers for all of them */ +#include "wrap.inc.c" + +static void not_implemented(void) +{ + fprintf(stderr, "Not implemented.\n"); +} + +static bool blacklisted(unsigned op, int rmode) +{ + /* odd has not been implemented for any 80-bit ops */ + if (rmode == softfloat_round_odd) { + switch (op) { + case EXTF80_TO_UI32: + case EXTF80_TO_UI64: + case EXTF80_TO_I32: + case EXTF80_TO_I64: + case EXTF80_TO_UI32_R_MINMAG: + case EXTF80_TO_UI64_R_MINMAG: + case EXTF80_TO_I32_R_MINMAG: + case EXTF80_TO_I64_R_MINMAG: + case EXTF80_TO_F16: + case EXTF80_TO_F32: + case EXTF80_TO_F64: + case EXTF80_TO_F128: + case EXTF80_ROUNDTOINT: + case EXTF80_ADD: + case EXTF80_SUB: + case EXTF80_MUL: + case EXTF80_DIV: + case EXTF80_REM: + case EXTF80_SQRT: + case EXTF80_EQ: + case EXTF80_LE: + case EXTF80_LT: + case EXTF80_EQ_SIGNALING: + case EXTF80_LE_QUIET: + case EXTF80_LT_QUIET: + case UI32_TO_EXTF80: + case UI64_TO_EXTF80: + case I32_TO_EXTF80: + case I64_TO_EXTF80: + case F16_TO_EXTF80: + case F32_TO_EXTF80: + case F64_TO_EXTF80: + case F128_TO_EXTF80: + return true; + } + } + return false; +} + +static void do_testfloat(int op, int rmode, bool exact) +{ + abz_f16 true_abz_f16; + abz_f16 subj_abz_f16; + ab_f16_z_bool true_f16_z_bool; + ab_f16_z_bool subj_f16_z_bool; + abz_f32 true_abz_f32; + abz_f32 subj_abz_f32; + ab_f32_z_bool true_ab_f32_z_bool; + ab_f32_z_bool subj_ab_f32_z_bool; + abz_f64 true_abz_f64; + abz_f64 subj_abz_f64; + ab_f64_z_bool true_ab_f64_z_bool; + ab_f64_z_bool subj_ab_f64_z_bool; + abz_extF80M true_abz_extF80M; + abz_extF80M subj_abz_extF80M; + ab_extF80M_z_bool true_ab_extF80M_z_bool; + ab_extF80M_z_bool subj_ab_extF80M_z_bool; + abz_f128M true_abz_f128M; + abz_f128M subj_abz_f128M; + ab_f128M_z_bool true_ab_f128M_z_bool; + ab_f128M_z_bool subj_ab_f128M_z_bool; + + fputs(">> Testing ", stderr); + verCases_writeFunctionName(stderr); + fputs("\n", stderr); + + if (blacklisted(op, rmode)) { + not_implemented(); + return; + } + + switch (op) { + case UI32_TO_F16: + test_a_ui32_z_f16(slow_ui32_to_f16, qemu_ui32_to_f16); + break; + case UI32_TO_F32: + test_a_ui32_z_f32(slow_ui32_to_f32, qemu_ui32_to_f32); + break; + case UI32_TO_F64: + test_a_ui32_z_f64(slow_ui32_to_f64, qemu_ui32_to_f64); + break; + case UI32_TO_EXTF80: + not_implemented(); + break; + case UI32_TO_F128: + not_implemented(); + break; + case UI64_TO_F16: + test_a_ui64_z_f16(slow_ui64_to_f16, qemu_ui64_to_f16); + break; + case UI64_TO_F32: + test_a_ui64_z_f32(slow_ui64_to_f32, qemu_ui64_to_f32); + break; + case UI64_TO_F64: + test_a_ui64_z_f64(slow_ui64_to_f64, qemu_ui64_to_f64); + break; + case UI64_TO_EXTF80: + not_implemented(); + break; + case UI64_TO_F128: + test_a_ui64_z_f128(slow_ui64_to_f128M, qemu_ui64_to_f128M); + break; + case I32_TO_F16: + test_a_i32_z_f16(slow_i32_to_f16, qemu_i32_to_f16); + break; + case I32_TO_F32: + test_a_i32_z_f32(slow_i32_to_f32, qemu_i32_to_f32); + break; + case I32_TO_F64: + test_a_i32_z_f64(slow_i32_to_f64, qemu_i32_to_f64); + break; + case I32_TO_EXTF80: + test_a_i32_z_extF80(slow_i32_to_extF80M, qemu_i32_to_extF80M); + break; + case I32_TO_F128: + test_a_i32_z_f128(slow_i32_to_f128M, qemu_i32_to_f128M); + break; + case I64_TO_F16: + test_a_i64_z_f16(slow_i64_to_f16, qemu_i64_to_f16); + break; + case I64_TO_F32: + test_a_i64_z_f32(slow_i64_to_f32, qemu_i64_to_f32); + break; + case I64_TO_F64: + test_a_i64_z_f64(slow_i64_to_f64, qemu_i64_to_f64); + break; + case I64_TO_EXTF80: + test_a_i64_z_extF80(slow_i64_to_extF80M, qemu_i64_to_extF80M); + break; + case I64_TO_F128: + test_a_i64_z_f128(slow_i64_to_f128M, qemu_i64_to_f128M); + break; + case F16_TO_UI32: + test_a_f16_z_ui32_rx(slow_f16_to_ui32, qemu_f16_to_ui32, rmode, exact); + break; + case F16_TO_UI64: + test_a_f16_z_ui64_rx(slow_f16_to_ui64, qemu_f16_to_ui64, rmode, exact); + break; + case F16_TO_I32: + test_a_f16_z_i32_rx(slow_f16_to_i32, qemu_f16_to_i32, rmode, exact); + break; + case F16_TO_I64: + test_a_f16_z_i64_rx(slow_f16_to_i64, qemu_f16_to_i64, rmode, exact); + break; + case F16_TO_UI32_R_MINMAG: + test_a_f16_z_ui32_x(slow_f16_to_ui32_r_minMag, + qemu_f16_to_ui32_r_minMag, exact); + break; + case F16_TO_UI64_R_MINMAG: + test_a_f16_z_ui64_x(slow_f16_to_ui64_r_minMag, + qemu_f16_to_ui64_r_minMag, exact); + break; + case F16_TO_I32_R_MINMAG: + test_a_f16_z_i32_x(slow_f16_to_i32_r_minMag, qemu_f16_to_i32_r_minMag, + exact); + break; + case F16_TO_I64_R_MINMAG: + test_a_f16_z_i64_x(slow_f16_to_i64_r_minMag, qemu_f16_to_i64_r_minMag, + exact); + break; + case F16_TO_F32: + test_a_f16_z_f32(slow_f16_to_f32, qemu_f16_to_f32); + break; + case F16_TO_F64: + test_a_f16_z_f64(slow_f16_to_f64, qemu_f16_to_f64); + break; + case F16_TO_EXTF80: + not_implemented(); + break; + case F16_TO_F128: + not_implemented(); + break; + case F16_ROUNDTOINT: + test_az_f16_rx(slow_f16_roundToInt, qemu_f16_roundToInt, rmode, exact); + break; + case F16_ADD: + true_abz_f16 = slow_f16_add; + subj_abz_f16 = qemu_f16_add; + goto test_abz_f16; + case F16_SUB: + true_abz_f16 = slow_f16_sub; + subj_abz_f16 = qemu_f16_sub; + goto test_abz_f16; + case F16_MUL: + true_abz_f16 = slow_f16_mul; + subj_abz_f16 = qemu_f16_mul; + goto test_abz_f16; + case F16_DIV: + true_abz_f16 = slow_f16_div; + subj_abz_f16 = qemu_f16_div; + goto test_abz_f16; + case F16_REM: + not_implemented(); + break; + test_abz_f16: + test_abz_f16(true_abz_f16, subj_abz_f16); + break; + case F16_MULADD: + test_abcz_f16(slow_f16_mulAdd, qemu_f16_mulAdd); + break; + case F16_SQRT: + test_az_f16(slow_f16_sqrt, qemu_f16_sqrt); + break; + case F16_EQ: + true_f16_z_bool = slow_f16_eq; + subj_f16_z_bool = qemu_f16_eq; + goto test_ab_f16_z_bool; + case F16_LE: + true_f16_z_bool = slow_f16_le; + subj_f16_z_bool = qemu_f16_le; + goto test_ab_f16_z_bool; + case F16_LT: + true_f16_z_bool = slow_f16_lt; + subj_f16_z_bool = qemu_f16_lt; + goto test_ab_f16_z_bool; + case F16_EQ_SIGNALING: + true_f16_z_bool = slow_f16_eq_signaling; + subj_f16_z_bool = qemu_f16_eq_signaling; + goto test_ab_f16_z_bool; + case F16_LE_QUIET: + true_f16_z_bool = slow_f16_le_quiet; + subj_f16_z_bool = qemu_f16_le_quiet; + goto test_ab_f16_z_bool; + case F16_LT_QUIET: + true_f16_z_bool = slow_f16_lt_quiet; + subj_f16_z_bool = qemu_f16_lt_quiet; + test_ab_f16_z_bool: + test_ab_f16_z_bool(true_f16_z_bool, subj_f16_z_bool); + break; + case F32_TO_UI32: + test_a_f32_z_ui32_rx(slow_f32_to_ui32, qemu_f32_to_ui32, rmode, exact); + break; + case F32_TO_UI64: + test_a_f32_z_ui64_rx(slow_f32_to_ui64, qemu_f32_to_ui64, rmode, exact); + break; + case F32_TO_I32: + test_a_f32_z_i32_rx(slow_f32_to_i32, qemu_f32_to_i32, rmode, exact); + break; + case F32_TO_I64: + test_a_f32_z_i64_rx(slow_f32_to_i64, qemu_f32_to_i64, rmode, exact); + break; + case F32_TO_UI32_R_MINMAG: + test_a_f32_z_ui32_x(slow_f32_to_ui32_r_minMag, + qemu_f32_to_ui32_r_minMag, exact); + break; + case F32_TO_UI64_R_MINMAG: + test_a_f32_z_ui64_x(slow_f32_to_ui64_r_minMag, + qemu_f32_to_ui64_r_minMag, exact); + break; + case F32_TO_I32_R_MINMAG: + test_a_f32_z_i32_x(slow_f32_to_i32_r_minMag, qemu_f32_to_i32_r_minMag, + exact); + break; + case F32_TO_I64_R_MINMAG: + test_a_f32_z_i64_x(slow_f32_to_i64_r_minMag, qemu_f32_to_i64_r_minMag, + exact); + break; + case F32_TO_F16: + test_a_f32_z_f16(slow_f32_to_f16, qemu_f32_to_f16); + break; + case F32_TO_F64: + test_a_f32_z_f64(slow_f32_to_f64, qemu_f32_to_f64); + break; + case F32_TO_EXTF80: + test_a_f32_z_extF80(slow_f32_to_extF80M, qemu_f32_to_extF80M); + break; + case F32_TO_F128: + test_a_f32_z_f128(slow_f32_to_f128M, qemu_f32_to_f128M); + break; + case F32_ROUNDTOINT: + test_az_f32_rx(slow_f32_roundToInt, qemu_f32_roundToInt, rmode, exact); + break; + case F32_ADD: + true_abz_f32 = slow_f32_add; + subj_abz_f32 = qemu_f32_add; + goto test_abz_f32; + case F32_SUB: + true_abz_f32 = slow_f32_sub; + subj_abz_f32 = qemu_f32_sub; + goto test_abz_f32; + case F32_MUL: + true_abz_f32 = slow_f32_mul; + subj_abz_f32 = qemu_f32_mul; + goto test_abz_f32; + case F32_DIV: + true_abz_f32 = slow_f32_div; + subj_abz_f32 = qemu_f32_div; + goto test_abz_f32; + case F32_REM: + true_abz_f32 = slow_f32_rem; + subj_abz_f32 = qemu_f32_rem; + test_abz_f32: + test_abz_f32(true_abz_f32, subj_abz_f32); + break; + case F32_MULADD: + test_abcz_f32(slow_f32_mulAdd, qemu_f32_mulAdd); + break; + case F32_SQRT: + test_az_f32(slow_f32_sqrt, qemu_f32_sqrt); + break; + case F32_EQ: + true_ab_f32_z_bool = slow_f32_eq; + subj_ab_f32_z_bool = qemu_f32_eq; + goto test_ab_f32_z_bool; + case F32_LE: + true_ab_f32_z_bool = slow_f32_le; + subj_ab_f32_z_bool = qemu_f32_le; + goto test_ab_f32_z_bool; + case F32_LT: + true_ab_f32_z_bool = slow_f32_lt; + subj_ab_f32_z_bool = qemu_f32_lt; + goto test_ab_f32_z_bool; + case F32_EQ_SIGNALING: + true_ab_f32_z_bool = slow_f32_eq_signaling; + subj_ab_f32_z_bool = qemu_f32_eq_signaling; + goto test_ab_f32_z_bool; + case F32_LE_QUIET: + true_ab_f32_z_bool = slow_f32_le_quiet; + subj_ab_f32_z_bool = qemu_f32_le_quiet; + goto test_ab_f32_z_bool; + case F32_LT_QUIET: + true_ab_f32_z_bool = slow_f32_lt_quiet; + subj_ab_f32_z_bool = qemu_f32_lt_quiet; + test_ab_f32_z_bool: + test_ab_f32_z_bool(true_ab_f32_z_bool, subj_ab_f32_z_bool); + break; + case F64_TO_UI32: + test_a_f64_z_ui32_rx(slow_f64_to_ui32, qemu_f64_to_ui32, rmode, exact); + break; + case F64_TO_UI64: + test_a_f64_z_ui64_rx(slow_f64_to_ui64, qemu_f64_to_ui64, rmode, exact); + break; + case F64_TO_I32: + test_a_f64_z_i32_rx(slow_f64_to_i32, qemu_f64_to_i32, rmode, exact); + break; + case F64_TO_I64: + test_a_f64_z_i64_rx(slow_f64_to_i64, qemu_f64_to_i64, rmode, exact); + break; + case F64_TO_UI32_R_MINMAG: + test_a_f64_z_ui32_x(slow_f64_to_ui32_r_minMag, + qemu_f64_to_ui32_r_minMag, exact); + break; + case F64_TO_UI64_R_MINMAG: + test_a_f64_z_ui64_x(slow_f64_to_ui64_r_minMag, + qemu_f64_to_ui64_r_minMag, exact); + break; + case F64_TO_I32_R_MINMAG: + test_a_f64_z_i32_x(slow_f64_to_i32_r_minMag, qemu_f64_to_i32_r_minMag, + exact); + break; + case F64_TO_I64_R_MINMAG: + test_a_f64_z_i64_x(slow_f64_to_i64_r_minMag, qemu_f64_to_i64_r_minMag, + exact); + break; + case F64_TO_F16: + test_a_f64_z_f16(slow_f64_to_f16, qemu_f64_to_f16); + break; + case F64_TO_F32: + test_a_f64_z_f32(slow_f64_to_f32, qemu_f64_to_f32); + break; + case F64_TO_EXTF80: + test_a_f64_z_extF80(slow_f64_to_extF80M, qemu_f64_to_extF80M); + break; + case F64_TO_F128: + test_a_f64_z_f128(slow_f64_to_f128M, qemu_f64_to_f128M); + break; + case F64_ROUNDTOINT: + test_az_f64_rx(slow_f64_roundToInt, qemu_f64_roundToInt, rmode, exact); + break; + case F64_ADD: + true_abz_f64 = slow_f64_add; + subj_abz_f64 = qemu_f64_add; + goto test_abz_f64; + case F64_SUB: + true_abz_f64 = slow_f64_sub; + subj_abz_f64 = qemu_f64_sub; + goto test_abz_f64; + case F64_MUL: + true_abz_f64 = slow_f64_mul; + subj_abz_f64 = qemu_f64_mul; + goto test_abz_f64; + case F64_DIV: + true_abz_f64 = slow_f64_div; + subj_abz_f64 = qemu_f64_div; + goto test_abz_f64; + case F64_REM: + true_abz_f64 = slow_f64_rem; + subj_abz_f64 = qemu_f64_rem; + test_abz_f64: + test_abz_f64(true_abz_f64, subj_abz_f64); + break; + case F64_MULADD: + test_abcz_f64(slow_f64_mulAdd, qemu_f64_mulAdd); + break; + case F64_SQRT: + test_az_f64(slow_f64_sqrt, qemu_f64_sqrt); + break; + case F64_EQ: + true_ab_f64_z_bool = slow_f64_eq; + subj_ab_f64_z_bool = qemu_f64_eq; + goto test_ab_f64_z_bool; + case F64_LE: + true_ab_f64_z_bool = slow_f64_le; + subj_ab_f64_z_bool = qemu_f64_le; + goto test_ab_f64_z_bool; + case F64_LT: + true_ab_f64_z_bool = slow_f64_lt; + subj_ab_f64_z_bool = qemu_f64_lt; + goto test_ab_f64_z_bool; + case F64_EQ_SIGNALING: + true_ab_f64_z_bool = slow_f64_eq_signaling; + subj_ab_f64_z_bool = qemu_f64_eq_signaling; + goto test_ab_f64_z_bool; + case F64_LE_QUIET: + true_ab_f64_z_bool = slow_f64_le_quiet; + subj_ab_f64_z_bool = qemu_f64_le_quiet; + goto test_ab_f64_z_bool; + case F64_LT_QUIET: + true_ab_f64_z_bool = slow_f64_lt_quiet; + subj_ab_f64_z_bool = qemu_f64_lt_quiet; + test_ab_f64_z_bool: + test_ab_f64_z_bool(true_ab_f64_z_bool, subj_ab_f64_z_bool); + break; + case EXTF80_TO_UI32: + not_implemented(); + break; + case EXTF80_TO_UI64: + not_implemented(); + break; + case EXTF80_TO_I32: + test_a_extF80_z_i32_rx(slow_extF80M_to_i32, qemu_extF80M_to_i32, rmode, + exact); + break; + case EXTF80_TO_I64: + test_a_extF80_z_i64_rx(slow_extF80M_to_i64, qemu_extF80M_to_i64, rmode, + exact); + break; + case EXTF80_TO_UI32_R_MINMAG: + not_implemented(); + break; + case EXTF80_TO_UI64_R_MINMAG: + not_implemented(); + break; + case EXTF80_TO_I32_R_MINMAG: + test_a_extF80_z_i32_x(slow_extF80M_to_i32_r_minMag, + qemu_extF80M_to_i32_r_minMag, exact); + break; + case EXTF80_TO_I64_R_MINMAG: + test_a_extF80_z_i64_x(slow_extF80M_to_i64_r_minMag, + qemu_extF80M_to_i64_r_minMag, exact); + break; + case EXTF80_TO_F16: + not_implemented(); + break; + case EXTF80_TO_F32: + test_a_extF80_z_f32(slow_extF80M_to_f32, qemu_extF80M_to_f32); + break; + case EXTF80_TO_F64: + test_a_extF80_z_f64(slow_extF80M_to_f64, qemu_extF80M_to_f64); + break; + case EXTF80_TO_F128: + test_a_extF80_z_f128(slow_extF80M_to_f128M, qemu_extF80M_to_f128M); + break; + case EXTF80_ROUNDTOINT: + test_az_extF80_rx(slow_extF80M_roundToInt, qemu_extF80M_roundToInt, + rmode, exact); + break; + case EXTF80_ADD: + true_abz_extF80M = slow_extF80M_add; + subj_abz_extF80M = qemu_extF80M_add; + goto test_abz_extF80; + case EXTF80_SUB: + true_abz_extF80M = slow_extF80M_sub; + subj_abz_extF80M = qemu_extF80M_sub; + goto test_abz_extF80; + case EXTF80_MUL: + true_abz_extF80M = slow_extF80M_mul; + subj_abz_extF80M = qemu_extF80M_mul; + goto test_abz_extF80; + case EXTF80_DIV: + true_abz_extF80M = slow_extF80M_div; + subj_abz_extF80M = qemu_extF80M_div; + goto test_abz_extF80; + case EXTF80_REM: + true_abz_extF80M = slow_extF80M_rem; + subj_abz_extF80M = qemu_extF80M_rem; + test_abz_extF80: + test_abz_extF80(true_abz_extF80M, subj_abz_extF80M); + break; + case EXTF80_SQRT: + test_az_extF80(slow_extF80M_sqrt, qemu_extF80M_sqrt); + break; + case EXTF80_EQ: + true_ab_extF80M_z_bool = slow_extF80M_eq; + subj_ab_extF80M_z_bool = qemu_extF80M_eq; + goto test_ab_extF80_z_bool; + case EXTF80_LE: + true_ab_extF80M_z_bool = slow_extF80M_le; + subj_ab_extF80M_z_bool = qemu_extF80M_le; + goto test_ab_extF80_z_bool; + case EXTF80_LT: + true_ab_extF80M_z_bool = slow_extF80M_lt; + subj_ab_extF80M_z_bool = qemu_extF80M_lt; + goto test_ab_extF80_z_bool; + case EXTF80_EQ_SIGNALING: + true_ab_extF80M_z_bool = slow_extF80M_eq_signaling; + subj_ab_extF80M_z_bool = qemu_extF80M_eq_signaling; + goto test_ab_extF80_z_bool; + case EXTF80_LE_QUIET: + true_ab_extF80M_z_bool = slow_extF80M_le_quiet; + subj_ab_extF80M_z_bool = qemu_extF80M_le_quiet; + goto test_ab_extF80_z_bool; + case EXTF80_LT_QUIET: + true_ab_extF80M_z_bool = slow_extF80M_lt_quiet; + subj_ab_extF80M_z_bool = qemu_extF80M_lt_quiet; + test_ab_extF80_z_bool: + test_ab_extF80_z_bool(true_ab_extF80M_z_bool, subj_ab_extF80M_z_bool); + break; + case F128_TO_UI32: + test_a_f128_z_ui32_rx(slow_f128M_to_ui32, qemu_f128M_to_ui32, rmode, + exact); + break; + case F128_TO_UI64: + test_a_f128_z_ui64_rx(slow_f128M_to_ui64, qemu_f128M_to_ui64, rmode, + exact); + break; + case F128_TO_I32: + test_a_f128_z_i32_rx(slow_f128M_to_i32, qemu_f128M_to_i32, rmode, + exact); + break; + case F128_TO_I64: + test_a_f128_z_i64_rx(slow_f128M_to_i64, qemu_f128M_to_i64, rmode, + exact); + break; + case F128_TO_UI32_R_MINMAG: + test_a_f128_z_ui32_x(slow_f128M_to_ui32_r_minMag, + qemu_f128M_to_ui32_r_minMag, exact); + break; + case F128_TO_UI64_R_MINMAG: + test_a_f128_z_ui64_x(slow_f128M_to_ui64_r_minMag, + qemu_f128M_to_ui64_r_minMag, exact); + break; + case F128_TO_I32_R_MINMAG: + test_a_f128_z_i32_x(slow_f128M_to_i32_r_minMag, + qemu_f128M_to_i32_r_minMag, exact); + break; + case F128_TO_I64_R_MINMAG: + test_a_f128_z_i64_x(slow_f128M_to_i64_r_minMag, + qemu_f128M_to_i64_r_minMag, exact); + break; + case F128_TO_F16: + not_implemented(); + break; + case F128_TO_F32: + test_a_f128_z_f32(slow_f128M_to_f32, qemu_f128M_to_f32); + break; + case F128_TO_F64: + test_a_f128_z_f64(slow_f128M_to_f64, qemu_f128M_to_f64); + break; + case F128_TO_EXTF80: + test_a_f128_z_extF80(slow_f128M_to_extF80M, qemu_f128M_to_extF80M); + break; + case F128_ROUNDTOINT: + test_az_f128_rx(slow_f128M_roundToInt, qemu_f128M_roundToInt, rmode, + exact); + break; + case F128_ADD: + true_abz_f128M = slow_f128M_add; + subj_abz_f128M = qemu_f128M_add; + goto test_abz_f128; + case F128_SUB: + true_abz_f128M = slow_f128M_sub; + subj_abz_f128M = qemu_f128M_sub; + goto test_abz_f128; + case F128_MUL: + true_abz_f128M = slow_f128M_mul; + subj_abz_f128M = qemu_f128M_mul; + goto test_abz_f128; + case F128_DIV: + true_abz_f128M = slow_f128M_div; + subj_abz_f128M = qemu_f128M_div; + goto test_abz_f128; + case F128_REM: + true_abz_f128M = slow_f128M_rem; + subj_abz_f128M = qemu_f128M_rem; + test_abz_f128: + test_abz_f128(true_abz_f128M, subj_abz_f128M); + break; + case F128_MULADD: + not_implemented(); + break; + case F128_SQRT: + test_az_f128(slow_f128M_sqrt, qemu_f128M_sqrt); + break; + case F128_EQ: + true_ab_f128M_z_bool = slow_f128M_eq; + subj_ab_f128M_z_bool = qemu_f128M_eq; + goto test_ab_f128_z_bool; + case F128_LE: + true_ab_f128M_z_bool = slow_f128M_le; + subj_ab_f128M_z_bool = qemu_f128M_le; + goto test_ab_f128_z_bool; + case F128_LT: + true_ab_f128M_z_bool = slow_f128M_lt; + subj_ab_f128M_z_bool = qemu_f128M_lt; + goto test_ab_f128_z_bool; + case F128_EQ_SIGNALING: + true_ab_f128M_z_bool = slow_f128M_eq_signaling; + subj_ab_f128M_z_bool = qemu_f128M_eq_signaling; + goto test_ab_f128_z_bool; + case F128_LE_QUIET: + true_ab_f128M_z_bool = slow_f128M_le_quiet; + subj_ab_f128M_z_bool = qemu_f128M_le_quiet; + goto test_ab_f128_z_bool; + case F128_LT_QUIET: + true_ab_f128M_z_bool = slow_f128M_lt_quiet; + subj_ab_f128M_z_bool = qemu_f128M_lt_quiet; + test_ab_f128_z_bool: + test_ab_f128_z_bool(true_ab_f128M_z_bool, subj_ab_f128M_z_bool); + break; + } + if ((verCases_errorStop && verCases_anyErrors)) { + verCases_exitWithStatus(); + } +} + +static unsigned int test_name_to_op(const char *arg) +{ + unsigned int i; + + /* counting begins at 1 */ + for (i = 1; i < NUM_FUNCTIONS; i++) { + const char *name = functionInfos[i].namePtr; + + if (name && !strcmp(name, arg)) { + return i; + } + } + return 0; +} + +static unsigned int round_name_to_mode(const char *name) +{ + int i; + + /* counting begins at 1 */ + for (i = 1; i < NUM_ROUNDINGMODES; i++) { + if (!strcmp(round_mode_names[i], name)) { + return i; + } + } + return 0; +} + +static int set_init_flags(const char *flags) +{ + const char *p; + + for (p = flags; *p != '\0'; p++) { + switch (*p) { + case 'v': + slow_init_flags |= softfloat_flag_invalid; + qemu_init_flags |= float_flag_invalid; + break; + case 'i': + slow_init_flags |= softfloat_flag_infinite; + qemu_init_flags |= float_flag_divbyzero; + break; + case 'o': + slow_init_flags |= softfloat_flag_overflow; + qemu_init_flags |= float_flag_overflow; + break; + case 'u': + slow_init_flags |= softfloat_flag_underflow; + qemu_init_flags |= float_flag_underflow; + break; + case 'x': + slow_init_flags |= softfloat_flag_inexact; + qemu_init_flags |= float_flag_inexact; + break; + default: + return 1; + } + } + return 0; +} + +static uint_fast8_t slow_clear_flags(void) +{ + uint8_t prev = slowfloat_exceptionFlags; + + slowfloat_exceptionFlags = slow_init_flags; + return prev; +} + +static uint_fast8_t qemu_clear_flags(void) +{ + uint8_t prev = qemu_flags_to_sf(qsf.float_exception_flags); + + qsf.float_exception_flags = qemu_init_flags; + return prev; +} + +static void parse_args(int argc, char *argv[]) +{ + unsigned int i; + int c; + + for (;;) { + c = getopt(argc, argv, "he:f:l:r:s"); + if (c < 0) { + break; + } + switch (c) { + case 'h': + usage_complete(argc, argv); + exit(EXIT_SUCCESS); + case 'e': + if (qemu_strtoui(optarg, NULL, 0, &n_max_errors)) { + fprintf(stderr, "fatal: invalid max error count\n"); + exit(EXIT_FAILURE); + } + break; + case 'f': + if (set_init_flags(optarg)) { + fprintf(stderr, "fatal: flags must be a subset of 'vioux'\n"); + exit(EXIT_FAILURE); + } + break; + case 'l': + if (qemu_strtoi(optarg, NULL, 0, &test_level)) { + fprintf(stderr, "fatal: invalid test level\n"); + exit(EXIT_FAILURE); + } + break; + case 'r': + if (!strcmp(optarg, "all")) { + test_round_mode = 0; + } else { + test_round_mode = round_name_to_mode(optarg); + if (test_round_mode == 0) { + fprintf(stderr, "fatal: invalid rounding mode\n"); + exit(EXIT_FAILURE); + } + } + break; + case 's': + verCases_errorStop = true; + break; + case '?': + /* invalid option or missing argument; getopt prints error info */ + exit(EXIT_FAILURE); + } + } + + /* set rounding modes */ + if (test_round_mode == 0) { + /* test all rounding modes; note that counting begins at 1 */ + n_round_modes = NUM_ROUNDINGMODES - 1; + round_modes = g_malloc_n(n_round_modes, sizeof(*round_modes)); + for (i = 0; i < n_round_modes; i++) { + round_modes[i] = i + 1; + } + } else { + n_round_modes = 1; + round_modes = g_malloc(sizeof(*round_modes)); + round_modes[0] = test_round_mode; + } + + /* set test ops */ + if (optind == argc) { + /* test all ops; note that counting begins at 1 */ + n_test_ops = NUM_FUNCTIONS - 1; + test_ops = g_malloc_n(n_test_ops, sizeof(*test_ops)); + for (i = 0; i < n_test_ops; i++) { + test_ops[i] = i + 1; + } + } else { + n_test_ops = argc - optind; + test_ops = g_malloc_n(n_test_ops, sizeof(*test_ops)); + for (i = 0; i < n_test_ops; i++) { + const char *name = argv[i + optind]; + unsigned int op = test_name_to_op(name); + + if (op == 0) { + fprintf(stderr, "fatal: invalid op '%s'\n", name); + exit(EXIT_FAILURE); + } + test_ops[i] = op; + } + } +} + +static void QEMU_NORETURN run_test(void) +{ + unsigned int i; + + genCases_setLevel(test_level); + verCases_maxErrorCount = n_max_errors; + + testLoops_trueFlagsFunction = slow_clear_flags; + testLoops_subjFlagsFunction = qemu_clear_flags; + + for (i = 0; i < n_test_ops; i++) { + unsigned int op = test_ops[i]; + int j; + + if (functionInfos[op].namePtr == NULL) { + continue; + } + verCases_functionNamePtr = functionInfos[op].namePtr; + + for (j = 0; j < n_round_modes; j++) { + int attrs = functionInfos[op].attribs; + int round = round_modes[j]; + int rmode = roundingModes[round]; + int k; + + verCases_roundingCode = 0; + slowfloat_roundingMode = rmode; + qsf.float_rounding_mode = sf_rounding_to_qemu(rmode); + + if (attrs & (FUNC_ARG_ROUNDINGMODE | FUNC_EFF_ROUNDINGMODE)) { + /* print rounding mode if the op is affected by it */ + verCases_roundingCode = round; + } else if (j > 0) { + /* if the op is not sensitive to rounding, move on */ + break; + } + + /* QEMU doesn't have !exact */ + verCases_exact = true; + verCases_usesExact = !!(attrs & FUNC_ARG_EXACT); + + for (k = 0; k < 3; k++) { + int prec80 = 32; + int l; + + if (k == 1) { + prec80 = 64; + } else if (k == 2) { + prec80 = 80; + } + + verCases_roundingPrecision = 0; + slow_extF80_roundingPrecision = prec80; + qsf.floatx80_rounding_precision = prec80; + + if (attrs & FUNC_EFF_ROUNDINGPRECISION) { + verCases_roundingPrecision = prec80; + } else if (k > 0) { + /* if the op is not sensitive to prec80, move on */ + break; + } + + /* note: the count begins at 1 */ + for (l = 1; l < NUM_TININESSMODES; l++) { + int tmode = tininessModes[l]; + + verCases_tininessCode = 0; + slowfloat_detectTininess = tmode; + qsf.float_detect_tininess = sf_tininess_to_qemu(tmode); + + if (attrs & FUNC_EFF_TININESSMODE || + ((attrs & FUNC_EFF_TININESSMODE_REDUCEDPREC) && + prec80 && prec80 < 80)) { + verCases_tininessCode = l; + } else if (l > 1) { + /* if the op is not sensitive to tininess, move on */ + break; + } + + do_testfloat(op, rmode, true); + } + } + } + } + verCases_exitWithStatus(); + /* old compilers might miss that we exited */ + g_assert_not_reached(); +} + +int main(int argc, char *argv[]) +{ + parse_args(argc, argv); + fail_programName = argv[0]; + run_test(); /* does not return */ +} diff --git a/tests/fp/platform.h b/tests/fp/platform.h new file mode 100644 index 0000000000..c20ba70baa --- /dev/null +++ b/tests/fp/platform.h @@ -0,0 +1,41 @@ +#ifndef QEMU_TESTFLOAT_PLATFORM_H +#define QEMU_TESTFLOAT_PLATFORM_H +/* + * Copyright 2011, 2012, 2013, 2014, 2015, 2016 The Regents of the University of + * California. 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 the above copyright notice, + * this list of conditions, and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the University 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 REGENTS 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 REGENTS 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 "config-host.h" + +#ifndef HOST_WORDS_BIGENDIAN +#define LITTLEENDIAN 1 +/* otherwise do not define it */ +#endif + +#define INLINE static inline + +#endif /* QEMU_TESTFLOAT_PLATFORM_H */ diff --git a/tests/fp/wrap.inc.c b/tests/fp/wrap.inc.c new file mode 100644 index 0000000000..0cbd20013e --- /dev/null +++ b/tests/fp/wrap.inc.c @@ -0,0 +1,654 @@ +/* + * In this file we wrap QEMU FP functions to look like softfloat/testfloat's, + * so that we can use the testfloat infrastructure as-is. + * + * This file must be included directly from fp-test.c. We could compile it + * separately, but it would be tedious to add declarations for all the wrappers. + */ + +static signed char sf_tininess_to_qemu(uint_fast8_t mode) +{ + switch (mode) { + case softfloat_tininess_beforeRounding: + return float_tininess_before_rounding; + case softfloat_tininess_afterRounding: + return float_tininess_after_rounding; + default: + g_assert_not_reached(); + } +} + +static signed char sf_rounding_to_qemu(uint_fast8_t mode) +{ + switch (mode) { + case softfloat_round_near_even: + return float_round_nearest_even; + case softfloat_round_minMag: + return float_round_to_zero; + case softfloat_round_min: + return float_round_down; + case softfloat_round_max: + return float_round_up; + case softfloat_round_near_maxMag: + return float_round_ties_away; + case softfloat_round_odd: + return float_round_to_odd; + default: + g_assert_not_reached(); + } +} + +static uint_fast8_t qemu_flags_to_sf(uint8_t qflags) +{ + uint_fast8_t ret = 0; + + if (qflags & float_flag_invalid) { + ret |= softfloat_flag_invalid; + } + if (qflags & float_flag_divbyzero) { + ret |= softfloat_flag_infinite; + } + if (qflags & float_flag_overflow) { + ret |= softfloat_flag_overflow; + } + if (qflags & float_flag_underflow) { + ret |= softfloat_flag_underflow; + } + if (qflags & float_flag_inexact) { + ret |= softfloat_flag_inexact; + } + return ret; +} + +/* + * floatx80 and float128 cannot be cast between qemu and softfloat, because + * in softfloat the order of the fields depends on the host's endianness. + */ +static extFloat80_t qemu_to_soft80(floatx80 a) +{ + extFloat80_t ret; + + ret.signif = a.low; + ret.signExp = a.high; + return ret; +} + +static floatx80 soft_to_qemu80(extFloat80_t a) +{ + floatx80 ret; + + ret.low = a.signif; + ret.high = a.signExp; + return ret; +} + +static float128_t qemu_to_soft128(float128 a) +{ + float128_t ret; + struct uint128 *to = (struct uint128 *)&ret; + + to->v0 = a.low; + to->v64 = a.high; + return ret; +} + +static float128 soft_to_qemu128(float128_t a) +{ + struct uint128 *from = (struct uint128 *)&a; + float128 ret; + + ret.low = from->v0; + ret.high = from->v64; + return ret; +} + +/* conversions */ +#define WRAP_SF_TO_SF_IEEE(name, func, a_type, b_type) \ + static b_type##_t name(a_type##_t a) \ + { \ + a_type *ap = (a_type *)&a; \ + b_type ret; \ + \ + ret = func(*ap, true, &qsf); \ + return *(b_type##_t *)&ret; \ + } + +WRAP_SF_TO_SF_IEEE(qemu_f16_to_f32, float16_to_float32, float16, float32) +WRAP_SF_TO_SF_IEEE(qemu_f16_to_f64, float16_to_float64, float16, float64) + +WRAP_SF_TO_SF_IEEE(qemu_f32_to_f16, float32_to_float16, float32, float16) +WRAP_SF_TO_SF_IEEE(qemu_f64_to_f16, float64_to_float16, float64, float16) +#undef WRAP_SF_TO_SF_IEEE + +#define WRAP_SF_TO_SF(name, func, a_type, b_type) \ + static b_type##_t name(a_type##_t a) \ + { \ + a_type *ap = (a_type *)&a; \ + b_type ret; \ + \ + ret = func(*ap, &qsf); \ + return *(b_type##_t *)&ret; \ + } + +WRAP_SF_TO_SF(qemu_f32_to_f64, float32_to_float64, float32, float64) +WRAP_SF_TO_SF(qemu_f64_to_f32, float64_to_float32, float64, float32) +#undef WRAP_SF_TO_SF + +#define WRAP_SF_TO_80(name, func, type) \ + static void name(type##_t a, extFloat80_t *res) \ + { \ + floatx80 ret; \ + type *ap = (type *)&a; \ + \ + ret = func(*ap, &qsf); \ + *res = qemu_to_soft80(ret); \ + } + +WRAP_SF_TO_80(qemu_f32_to_extF80M, float32_to_floatx80, float32) +WRAP_SF_TO_80(qemu_f64_to_extF80M, float64_to_floatx80, float64) +#undef WRAP_SF_TO_80 + +#define WRAP_SF_TO_128(name, func, type) \ + static void name(type##_t a, float128_t *res) \ + { \ + float128 ret; \ + type *ap = (type *)&a; \ + \ + ret = func(*ap, &qsf); \ + *res = qemu_to_soft128(ret); \ + } + +WRAP_SF_TO_128(qemu_f32_to_f128M, float32_to_float128, float32) +WRAP_SF_TO_128(qemu_f64_to_f128M, float64_to_float128, float64) +#undef WRAP_SF_TO_128 + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_SF_TO_INT(name, func, type, fast_type) \ + static fast_type name(type##_t a, uint_fast8_t round, bool exact) \ + { \ + type *ap = (type *)&a; \ + \ + qsf.float_rounding_mode = sf_rounding_to_qemu(round); \ + return func(*ap, &qsf); \ + } + +WRAP_SF_TO_INT(qemu_f16_to_ui32, float16_to_uint32, float16, uint_fast32_t) +WRAP_SF_TO_INT(qemu_f16_to_ui64, float16_to_uint64, float16, uint_fast64_t) + +WRAP_SF_TO_INT(qemu_f32_to_ui32, float32_to_uint32, float32, uint_fast32_t) +WRAP_SF_TO_INT(qemu_f32_to_ui64, float32_to_uint64, float32, uint_fast64_t) + +WRAP_SF_TO_INT(qemu_f64_to_ui32, float64_to_uint32, float64, uint_fast32_t) +WRAP_SF_TO_INT(qemu_f64_to_ui64, float64_to_uint64, float64, uint_fast64_t) + +WRAP_SF_TO_INT(qemu_f16_to_i32, float16_to_int32, float16, int_fast32_t) +WRAP_SF_TO_INT(qemu_f16_to_i64, float16_to_int64, float16, int_fast64_t) + +WRAP_SF_TO_INT(qemu_f32_to_i32, float32_to_int32, float32, int_fast32_t) +WRAP_SF_TO_INT(qemu_f32_to_i64, float32_to_int64, float32, int_fast64_t) + +WRAP_SF_TO_INT(qemu_f64_to_i32, float64_to_int32, float64, int_fast32_t) +WRAP_SF_TO_INT(qemu_f64_to_i64, float64_to_int64, float64, int_fast64_t) +#undef WRAP_SF_TO_INT + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_SF_TO_INT_MINMAG(name, func, type, fast_type) \ + static fast_type name(type##_t a, bool exact) \ + { \ + type *ap = (type *)&a; \ + \ + return func(*ap, &qsf); \ + } + +WRAP_SF_TO_INT_MINMAG(qemu_f16_to_ui32_r_minMag, + float16_to_uint32_round_to_zero, float16, uint_fast32_t) +WRAP_SF_TO_INT_MINMAG(qemu_f16_to_ui64_r_minMag, + float16_to_uint64_round_to_zero, float16, uint_fast64_t) + +WRAP_SF_TO_INT_MINMAG(qemu_f16_to_i32_r_minMag, + float16_to_int32_round_to_zero, float16, int_fast32_t) +WRAP_SF_TO_INT_MINMAG(qemu_f16_to_i64_r_minMag, + float16_to_int64_round_to_zero, float16, int_fast64_t) + +WRAP_SF_TO_INT_MINMAG(qemu_f32_to_ui32_r_minMag, + float32_to_uint32_round_to_zero, float32, uint_fast32_t) +WRAP_SF_TO_INT_MINMAG(qemu_f32_to_ui64_r_minMag, + float32_to_uint64_round_to_zero, float32, uint_fast64_t) + +WRAP_SF_TO_INT_MINMAG(qemu_f32_to_i32_r_minMag, + float32_to_int32_round_to_zero, float32, int_fast32_t) +WRAP_SF_TO_INT_MINMAG(qemu_f32_to_i64_r_minMag, + float32_to_int64_round_to_zero, float32, int_fast64_t) + +WRAP_SF_TO_INT_MINMAG(qemu_f64_to_ui32_r_minMag, + float64_to_uint32_round_to_zero, float64, uint_fast32_t) +WRAP_SF_TO_INT_MINMAG(qemu_f64_to_ui64_r_minMag, + float64_to_uint64_round_to_zero, float64, uint_fast64_t) + +WRAP_SF_TO_INT_MINMAG(qemu_f64_to_i32_r_minMag, + float64_to_int32_round_to_zero, float64, int_fast32_t) +WRAP_SF_TO_INT_MINMAG(qemu_f64_to_i64_r_minMag, + float64_to_int64_round_to_zero, float64, int_fast64_t) +#undef WRAP_SF_TO_INT_MINMAG + +#define WRAP_80_TO_SF(name, func, type) \ + static type##_t name(const extFloat80_t *ap) \ + { \ + floatx80 a; \ + type ret; \ + \ + a = soft_to_qemu80(*ap); \ + ret = func(a, &qsf); \ + return *(type##_t *)&ret; \ + } + +WRAP_80_TO_SF(qemu_extF80M_to_f32, floatx80_to_float32, float32) +WRAP_80_TO_SF(qemu_extF80M_to_f64, floatx80_to_float64, float64) +#undef WRAP_80_TO_SF + +#define WRAP_128_TO_SF(name, func, type) \ + static type##_t name(const float128_t *ap) \ + { \ + float128 a; \ + type ret; \ + \ + a = soft_to_qemu128(*ap); \ + ret = func(a, &qsf); \ + return *(type##_t *)&ret; \ + } + +WRAP_128_TO_SF(qemu_f128M_to_f32, float128_to_float32, float32) +WRAP_128_TO_SF(qemu_f128M_to_f64, float128_to_float64, float64) +#undef WRAP_128_TO_SF + +static void qemu_extF80M_to_f128M(const extFloat80_t *from, float128_t *to) +{ + floatx80 qfrom; + float128 qto; + + qfrom = soft_to_qemu80(*from); + qto = floatx80_to_float128(qfrom, &qsf); + *to = qemu_to_soft128(qto); +} + +static void qemu_f128M_to_extF80M(const float128_t *from, extFloat80_t *to) +{ + float128 qfrom; + floatx80 qto; + + qfrom = soft_to_qemu128(*from); + qto = float128_to_floatx80(qfrom, &qsf); + *to = qemu_to_soft80(qto); +} + +#define WRAP_INT_TO_SF(name, func, int_type, type) \ + static type##_t name(int_type a) \ + { \ + type ret; \ + \ + ret = func(a, &qsf); \ + return *(type##_t *)&ret; \ + } + +WRAP_INT_TO_SF(qemu_ui32_to_f16, uint32_to_float16, uint32_t, float16) +WRAP_INT_TO_SF(qemu_ui32_to_f32, uint32_to_float32, uint32_t, float32) +WRAP_INT_TO_SF(qemu_ui32_to_f64, uint32_to_float64, uint32_t, float64) + +WRAP_INT_TO_SF(qemu_ui64_to_f16, uint64_to_float16, uint64_t, float16) +WRAP_INT_TO_SF(qemu_ui64_to_f32, uint64_to_float32, uint64_t, float32) +WRAP_INT_TO_SF(qemu_ui64_to_f64, uint64_to_float64, uint64_t, float64) + +WRAP_INT_TO_SF(qemu_i32_to_f16, int32_to_float16, int32_t, float16) +WRAP_INT_TO_SF(qemu_i32_to_f32, int32_to_float32, int32_t, float32) +WRAP_INT_TO_SF(qemu_i32_to_f64, int32_to_float64, int32_t, float64) + +WRAP_INT_TO_SF(qemu_i64_to_f16, int64_to_float16, int64_t, float16) +WRAP_INT_TO_SF(qemu_i64_to_f32, int64_to_float32, int64_t, float32) +WRAP_INT_TO_SF(qemu_i64_to_f64, int64_to_float64, int64_t, float64) +#undef WRAP_INT_TO_SF + +#define WRAP_INT_TO_80(name, func, int_type) \ + static void name(int_type a, extFloat80_t *res) \ + { \ + floatx80 ret; \ + \ + ret = func(a, &qsf); \ + *res = qemu_to_soft80(ret); \ + } + +WRAP_INT_TO_80(qemu_i32_to_extF80M, int32_to_floatx80, int32_t) +WRAP_INT_TO_80(qemu_i64_to_extF80M, int64_to_floatx80, int64_t) +#undef WRAP_INT_TO_80 + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_80_TO_INT(name, func, fast_type) \ + static fast_type name(const extFloat80_t *ap, uint_fast8_t round, \ + bool exact) \ + { \ + floatx80 a; \ + \ + a = soft_to_qemu80(*ap); \ + qsf.float_rounding_mode = sf_rounding_to_qemu(round); \ + return func(a, &qsf); \ + } + +WRAP_80_TO_INT(qemu_extF80M_to_i32, floatx80_to_int32, int_fast32_t) +WRAP_80_TO_INT(qemu_extF80M_to_i64, floatx80_to_int64, int_fast64_t) +#undef WRAP_80_TO_INT + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_80_TO_INT_MINMAG(name, func, fast_type) \ + static fast_type name(const extFloat80_t *ap, bool exact) \ + { \ + floatx80 a; \ + \ + a = soft_to_qemu80(*ap); \ + return func(a, &qsf); \ + } + +WRAP_80_TO_INT_MINMAG(qemu_extF80M_to_i32_r_minMag, + floatx80_to_int32_round_to_zero, int_fast32_t) +WRAP_80_TO_INT_MINMAG(qemu_extF80M_to_i64_r_minMag, + floatx80_to_int64_round_to_zero, int_fast64_t) +#undef WRAP_80_TO_INT_MINMAG + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_128_TO_INT(name, func, fast_type) \ + static fast_type name(const float128_t *ap, uint_fast8_t round, \ + bool exact) \ + { \ + float128 a; \ + \ + a = soft_to_qemu128(*ap); \ + qsf.float_rounding_mode = sf_rounding_to_qemu(round); \ + return func(a, &qsf); \ + } + +WRAP_128_TO_INT(qemu_f128M_to_i32, float128_to_int32, int_fast32_t) +WRAP_128_TO_INT(qemu_f128M_to_i64, float128_to_int64, int_fast64_t) + +WRAP_128_TO_INT(qemu_f128M_to_ui32, float128_to_uint32, uint_fast32_t) +WRAP_128_TO_INT(qemu_f128M_to_ui64, float128_to_uint64, uint_fast64_t) +#undef WRAP_128_TO_INT + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_128_TO_INT_MINMAG(name, func, fast_type) \ + static fast_type name(const float128_t *ap, bool exact) \ + { \ + float128 a; \ + \ + a = soft_to_qemu128(*ap); \ + return func(a, &qsf); \ + } + +WRAP_128_TO_INT_MINMAG(qemu_f128M_to_i32_r_minMag, + float128_to_int32_round_to_zero, int_fast32_t) +WRAP_128_TO_INT_MINMAG(qemu_f128M_to_i64_r_minMag, + float128_to_int64_round_to_zero, int_fast64_t) + +WRAP_128_TO_INT_MINMAG(qemu_f128M_to_ui32_r_minMag, + float128_to_uint32_round_to_zero, uint_fast32_t) +WRAP_128_TO_INT_MINMAG(qemu_f128M_to_ui64_r_minMag, + float128_to_uint64_round_to_zero, uint_fast64_t) +#undef WRAP_128_TO_INT_MINMAG + +#define WRAP_INT_TO_128(name, func, int_type) \ + static void name(int_type a, float128_t *res) \ + { \ + float128 ret; \ + \ + ret = func(a, &qsf); \ + *res = qemu_to_soft128(ret); \ + } + +WRAP_INT_TO_128(qemu_ui64_to_f128M, uint64_to_float128, uint64_t) + +WRAP_INT_TO_128(qemu_i32_to_f128M, int32_to_float128, int32_t) +WRAP_INT_TO_128(qemu_i64_to_f128M, int64_to_float128, int64_t) +#undef WRAP_INT_TO_128 + +/* Note: exact is ignored since qemu's softfloat assumes it is set */ +#define WRAP_ROUND_TO_INT(name, func, type) \ + static type##_t name(type##_t a, uint_fast8_t round, bool exact) \ + { \ + type *ap = (type *)&a; \ + type ret; \ + \ + qsf.float_rounding_mode = sf_rounding_to_qemu(round); \ + ret = func(*ap, &qsf); \ + return *(type##_t *)&ret; \ + } + +WRAP_ROUND_TO_INT(qemu_f16_roundToInt, float16_round_to_int, float16) +WRAP_ROUND_TO_INT(qemu_f32_roundToInt, float32_round_to_int, float32) +WRAP_ROUND_TO_INT(qemu_f64_roundToInt, float64_round_to_int, float64) +#undef WRAP_ROUND_TO_INT + +static void qemu_extF80M_roundToInt(const extFloat80_t *ap, uint_fast8_t round, + bool exact, extFloat80_t *res) +{ + floatx80 a; + floatx80 ret; + + a = soft_to_qemu80(*ap); + qsf.float_rounding_mode = sf_rounding_to_qemu(round); + ret = floatx80_round_to_int(a, &qsf); + *res = qemu_to_soft80(ret); +} + +static void qemu_f128M_roundToInt(const float128_t *ap, uint_fast8_t round, + bool exact, float128_t *res) +{ + float128 a; + float128 ret; + + a = soft_to_qemu128(*ap); + qsf.float_rounding_mode = sf_rounding_to_qemu(round); + ret = float128_round_to_int(a, &qsf); + *res = qemu_to_soft128(ret); +} + +/* operations */ +#define WRAP1(name, func, type) \ + static type##_t name(type##_t a) \ + { \ + type *ap = (type *)&a; \ + type ret; \ + \ + ret = func(*ap, &qsf); \ + return *(type##_t *)&ret; \ + } + +#define WRAP2(name, func, type) \ + static type##_t name(type##_t a, type##_t b) \ + { \ + type *ap = (type *)&a; \ + type *bp = (type *)&b; \ + type ret; \ + \ + ret = func(*ap, *bp, &qsf); \ + return *(type##_t *)&ret; \ + } + +#define WRAP_COMMON_OPS(b) \ + WRAP1(qemu_f##b##_sqrt, float##b##_sqrt, float##b) \ + WRAP2(qemu_f##b##_add, float##b##_add, float##b) \ + WRAP2(qemu_f##b##_sub, float##b##_sub, float##b) \ + WRAP2(qemu_f##b##_mul, float##b##_mul, float##b) \ + WRAP2(qemu_f##b##_div, float##b##_div, float##b) + +WRAP_COMMON_OPS(16) +WRAP_COMMON_OPS(32) +WRAP_COMMON_OPS(64) +#undef WRAP_COMMON + +WRAP2(qemu_f32_rem, float32_rem, float32) +WRAP2(qemu_f64_rem, float64_rem, float64) +#undef WRAP2 +#undef WRAP1 + +#define WRAP1_80(name, func) \ + static void name(const extFloat80_t *ap, extFloat80_t *res) \ + { \ + floatx80 a; \ + floatx80 ret; \ + \ + a = soft_to_qemu80(*ap); \ + ret = func(a, &qsf); \ + *res = qemu_to_soft80(ret); \ + } + +WRAP1_80(qemu_extF80M_sqrt, floatx80_sqrt) +#undef WRAP1_80 + +#define WRAP1_128(name, func) \ + static void name(const float128_t *ap, float128_t *res) \ + { \ + float128 a; \ + float128 ret; \ + \ + a = soft_to_qemu128(*ap); \ + ret = func(a, &qsf); \ + *res = qemu_to_soft128(ret); \ + } + +WRAP1_128(qemu_f128M_sqrt, float128_sqrt) +#undef WRAP1_128 + +#define WRAP2_80(name, func) \ + static void name(const extFloat80_t *ap, const extFloat80_t *bp, \ + extFloat80_t *res) \ + { \ + floatx80 a; \ + floatx80 b; \ + floatx80 ret; \ + \ + a = soft_to_qemu80(*ap); \ + b = soft_to_qemu80(*bp); \ + ret = func(a, b, &qsf); \ + *res = qemu_to_soft80(ret); \ + } + +WRAP2_80(qemu_extF80M_add, floatx80_add) +WRAP2_80(qemu_extF80M_sub, floatx80_sub) +WRAP2_80(qemu_extF80M_mul, floatx80_mul) +WRAP2_80(qemu_extF80M_div, floatx80_div) +WRAP2_80(qemu_extF80M_rem, floatx80_rem) +#undef WRAP2_80 + +#define WRAP2_128(name, func) \ + static void name(const float128_t *ap, const float128_t *bp, \ + float128_t *res) \ + { \ + float128 a; \ + float128 b; \ + float128 ret; \ + \ + a = soft_to_qemu128(*ap); \ + b = soft_to_qemu128(*bp); \ + ret = func(a, b, &qsf); \ + *res = qemu_to_soft128(ret); \ + } + +WRAP2_128(qemu_f128M_add, float128_add) +WRAP2_128(qemu_f128M_sub, float128_sub) +WRAP2_128(qemu_f128M_mul, float128_mul) +WRAP2_128(qemu_f128M_div, float128_div) +WRAP2_128(qemu_f128M_rem, float128_rem) +#undef WRAP2_128 + +#define WRAP_MULADD(name, func, type) \ + static type##_t name(type##_t a, type##_t b, type##_t c) \ + { \ + type *ap = (type *)&a; \ + type *bp = (type *)&b; \ + type *cp = (type *)&c; \ + type ret; \ + \ + ret = func(*ap, *bp, *cp, 0, &qsf); \ + return *(type##_t *)&ret; \ + } + +WRAP_MULADD(qemu_f16_mulAdd, float16_muladd, float16) +WRAP_MULADD(qemu_f32_mulAdd, float32_muladd, float32) +WRAP_MULADD(qemu_f64_mulAdd, float64_muladd, float64) +#undef WRAP_MULADD + +#define WRAP_CMP16(name, func, retcond) \ + static bool name(float16_t a, float16_t b) \ + { \ + float16 *ap = (float16 *)&a; \ + float16 *bp = (float16 *)&b; \ + int ret; \ + \ + ret = func(*ap, *bp, &qsf); \ + return retcond; \ + } + +WRAP_CMP16(qemu_f16_eq_signaling, float16_compare, ret == 0) +WRAP_CMP16(qemu_f16_eq, float16_compare_quiet, ret == 0) +WRAP_CMP16(qemu_f16_le, float16_compare, ret <= 0) +WRAP_CMP16(qemu_f16_lt, float16_compare, ret < 0) +WRAP_CMP16(qemu_f16_le_quiet, float16_compare_quiet, ret <= 0) +WRAP_CMP16(qemu_f16_lt_quiet, float16_compare_quiet, ret < 0) +#undef WRAP_CMP16 + +#define WRAP_CMP(name, func, type) \ + static bool name(type##_t a, type##_t b) \ + { \ + type *ap = (type *)&a; \ + type *bp = (type *)&b; \ + \ + return !!func(*ap, *bp, &qsf); \ + } + +#define GEN_WRAP_CMP(b) \ + WRAP_CMP(qemu_f##b##_eq_signaling, float##b##_eq, float##b) \ + WRAP_CMP(qemu_f##b##_eq, float##b##_eq_quiet, float##b) \ + WRAP_CMP(qemu_f##b##_le, float##b##_le, float##b) \ + WRAP_CMP(qemu_f##b##_lt, float##b##_lt, float##b) \ + WRAP_CMP(qemu_f##b##_le_quiet, float##b##_le_quiet, float##b) \ + WRAP_CMP(qemu_f##b##_lt_quiet, float##b##_lt_quiet, float##b) + +GEN_WRAP_CMP(32) +GEN_WRAP_CMP(64) +#undef GEN_WRAP_CMP +#undef WRAP_CMP + +#define WRAP_CMP80(name, func) \ + static bool name(const extFloat80_t *ap, const extFloat80_t *bp) \ + { \ + floatx80 a; \ + floatx80 b; \ + \ + a = soft_to_qemu80(*ap); \ + b = soft_to_qemu80(*bp); \ + return !!func(a, b, &qsf); \ + } + +WRAP_CMP80(qemu_extF80M_eq_signaling, floatx80_eq) +WRAP_CMP80(qemu_extF80M_eq, floatx80_eq_quiet) +WRAP_CMP80(qemu_extF80M_le, floatx80_le) +WRAP_CMP80(qemu_extF80M_lt, floatx80_lt) +WRAP_CMP80(qemu_extF80M_le_quiet, floatx80_le_quiet) +WRAP_CMP80(qemu_extF80M_lt_quiet, floatx80_le_quiet) +#undef WRAP_CMP80 + +#define WRAP_CMP128(name, func) \ + static bool name(const float128_t *ap, const float128_t *bp) \ + { \ + float128 a; \ + float128 b; \ + \ + a = soft_to_qemu128(*ap); \ + b = soft_to_qemu128(*bp); \ + return !!func(a, b, &qsf); \ + } + +WRAP_CMP128(qemu_f128M_eq_signaling, float128_eq) +WRAP_CMP128(qemu_f128M_eq, float128_eq_quiet) +WRAP_CMP128(qemu_f128M_le, float128_le) +WRAP_CMP128(qemu_f128M_lt, float128_lt) +WRAP_CMP128(qemu_f128M_le_quiet, float128_le_quiet) +WRAP_CMP128(qemu_f128M_lt_quiet, float128_lt_quiet) +#undef WRAP_CMP128 diff --git a/tests/fw_cfg-test.c b/tests/fw_cfg-test.c index 1548bf14b2..1c5103fe1c 100644 --- a/tests/fw_cfg-test.c +++ b/tests/fw_cfg-test.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #include "libqos/fw_cfg.h" static uint64_t ram_size = 128 << 20; diff --git a/tests/guest-debug/test-gdbstub.py b/tests/guest-debug/test-gdbstub.py index 474d2c5c65..c7e3986a24 100644 --- a/tests/guest-debug/test-gdbstub.py +++ b/tests/guest-debug/test-gdbstub.py @@ -16,6 +16,7 @@ def report(cond, msg): print ("PASS: %s" % (msg)) else: print ("FAIL: %s" % (msg)) + global failcount failcount += 1 @@ -122,7 +123,7 @@ class CatchBreakpoint(gdb.Breakpoint): def run_test(): - "Run throught the tests one by one" + "Run through the tests one by one" print ("Checking we can step the first few instructions") step_ok = 0 diff --git a/tests/hd-geo-test.c b/tests/hd-geo-test.c index 24870b38f4..ce665f1f83 100644 --- a/tests/hd-geo-test.c +++ b/tests/hd-geo-test.c @@ -201,7 +201,7 @@ static void setup_mbr(int img_idx, MBRcontents mbr) static int setup_ide(int argc, char *argv[], int argv_sz, int ide_idx, const char *dev, int img_idx, - MBRcontents mbr, const char *opts) + MBRcontents mbr) { char *s1, *s2, *s3; @@ -216,7 +216,7 @@ static int setup_ide(int argc, char *argv[], int argv_sz, s3 = g_strdup(",media=cdrom"); } argc = append_arg(argc, argv, argv_sz, - g_strdup_printf("%s%s%s%s", s1, s2, s3, opts)); + g_strdup_printf("%s%s%s", s1, s2, s3)); g_free(s1); g_free(s2); g_free(s3); @@ -260,7 +260,7 @@ static void test_ide_mbr(bool use_device, MBRcontents mbr) for (i = 0; i < backend_last; i++) { cur_ide[i] = &hd_chst[i][mbr]; dev = use_device ? (is_hd(cur_ide[i]) ? "ide-hd" : "ide-cd") : NULL; - argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr, ""); + argc = setup_ide(argc, argv, ARGV_SIZE, i, dev, i, mbr); } args = g_strjoinv(" ", argv); qtest_start(args); @@ -327,16 +327,12 @@ static void test_ide_drive_user(const char *dev, bool trans) const CHST expected_chst = { secs / (4 * 32) , 4, 32, trans }; argc = setup_common(argv, ARGV_SIZE); - opts = g_strdup_printf("%s,%s%scyls=%d,heads=%d,secs=%d", - dev ?: "", - trans && dev ? "bios-chs-" : "", - trans ? "trans=lba," : "", + opts = g_strdup_printf("%s,%scyls=%d,heads=%d,secs=%d", + dev, trans ? "bios-chs-trans=lba," : "", expected_chst.cyls, expected_chst.heads, expected_chst.secs); cur_ide[0] = &expected_chst; - argc = setup_ide(argc, argv, ARGV_SIZE, - 0, dev ? opts : NULL, backend_small, mbr_chs, - dev ? "" : opts); + argc = setup_ide(argc, argv, ARGV_SIZE, 0, opts, backend_small, mbr_chs); g_free(opts); args = g_strjoinv(" ", argv); qtest_start(args); @@ -346,22 +342,6 @@ static void test_ide_drive_user(const char *dev, bool trans) qtest_end(); } -/* - * Test case: IDE device (if=ide) with explicit CHS - */ -static void test_ide_drive_user_chs(void) -{ - test_ide_drive_user(NULL, false); -} - -/* - * Test case: IDE device (if=ide) with explicit CHS and translation - */ -static void test_ide_drive_user_chst(void) -{ - test_ide_drive_user(NULL, true); -} - /* * Test case: IDE device (if=none) with explicit CHS */ @@ -392,8 +372,7 @@ static void test_ide_drive_cd_0(void) for (i = 0; i <= backend_empty; i++) { ide_idx = backend_empty - i; cur_ide[ide_idx] = &hd_chst[i][mbr_blank]; - argc = setup_ide(argc, argv, ARGV_SIZE, - ide_idx, NULL, i, mbr_blank, ""); + argc = setup_ide(argc, argv, ARGV_SIZE, ide_idx, NULL, i, mbr_blank); } args = g_strjoinv(" ", argv); qtest_start(args); @@ -422,8 +401,6 @@ int main(int argc, char **argv) qtest_add_func("hd-geo/ide/drive/mbr/blank", test_ide_drive_mbr_blank); qtest_add_func("hd-geo/ide/drive/mbr/lba", test_ide_drive_mbr_lba); qtest_add_func("hd-geo/ide/drive/mbr/chs", test_ide_drive_mbr_chs); - qtest_add_func("hd-geo/ide/drive/user/chs", test_ide_drive_user_chs); - qtest_add_func("hd-geo/ide/drive/user/chst", test_ide_drive_user_chst); qtest_add_func("hd-geo/ide/drive/cd_0", test_ide_drive_cd_0); qtest_add_func("hd-geo/ide/device/mbr/blank", test_ide_device_mbr_blank); qtest_add_func("hd-geo/ide/device/mbr/lba", test_ide_device_mbr_lba); diff --git a/tests/hexloader-test.c b/tests/hexloader-test.c new file mode 100644 index 0000000000..8b7aa2d72d --- /dev/null +++ b/tests/hexloader-test.c @@ -0,0 +1,45 @@ +/* + * QTest testcase for the Intel Hexadecimal Object File Loader + * + * Authors: + * Su Hang 2018 + * + * 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 "libqtest.h" + +/* Load 'test.hex' and verify that the in-memory contents are as expected. + * 'test.hex' is a memory test pattern stored in Hexadecimal Object + * format. It loads at 0x10000 in RAM and contains values from 0 through + * 255. + */ +static void hex_loader_test(void) +{ + unsigned int i; + const unsigned int base_addr = 0x00010000; + + QTestState *s = qtest_initf( + "-M vexpress-a9 -device loader,file=tests/data/hex-loader/test.hex"); + + for (i = 0; i < 256; ++i) { + uint8_t val = qtest_readb(s, base_addr + i); + g_assert_cmpuint(i, ==, val); + } + qtest_quit(s); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/tmp/hex_loader", hex_loader_test); + ret = g_test_run(); + + return ret; +} diff --git a/tests/i440fx-test.c b/tests/i440fx-test.c index 4390e5591e..69205b58a8 100644 --- a/tests/i440fx-test.c +++ b/tests/i440fx-test.c @@ -38,7 +38,7 @@ static QPCIBus *test_start_get_bus(const TestData *s) cmdline = g_strdup_printf("-smp %d", s->num_cpus); qtest_start(cmdline); g_free(cmdline); - return qpci_init_pc(global_qtest, NULL); + return qpci_new_pc(global_qtest, NULL); } static void test_i440fx_defaults(gconstpointer opaque) diff --git a/tests/ide-test.c b/tests/ide-test.c index 2384c2c3e2..d863a99f7f 100644 --- a/tests/ide-test.c +++ b/tests/ide-test.c @@ -29,12 +29,15 @@ #include "libqos/libqos.h" #include "libqos/pci-pc.h" #include "libqos/malloc-pc.h" - +#include "qapi/qmp/qdict.h" #include "qemu-common.h" #include "qemu/bswap.h" #include "hw/pci/pci_ids.h" #include "hw/pci/pci_regs.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) + #define TEST_IMAGE_SIZE 64 * 1024 * 1024 #define IDE_PCI_DEV 1 @@ -117,7 +120,7 @@ enum { #define assert_bit_clear(data, mask) g_assert_cmphex((data) & (mask), ==, 0) static QPCIBus *pcibus = NULL; -static QGuestAllocator *guest_malloc; +static QGuestAllocator guest_malloc; static char tmp_path[] = "/tmp/qtest.XXXXXX"; static char debug_path[] = "/tmp/qtest-blkdebug.XXXXXX"; @@ -132,15 +135,18 @@ static void ide_test_start(const char *cmdline_fmt, ...) va_end(ap); qtest_start(cmdline); - guest_malloc = pc_alloc_init(global_qtest); + pc_alloc_init(&guest_malloc, global_qtest, 0); g_free(cmdline); } static void ide_test_quit(void) { - pc_alloc_uninit(guest_malloc); - guest_malloc = NULL; + if (pcibus) { + qpci_free_pc(pcibus); + pcibus = NULL; + } + alloc_destroy(&guest_malloc); qtest_end(); } @@ -150,7 +156,7 @@ static QPCIDevice *get_pci_device(QPCIBar *bmdma_bar, QPCIBar *ide_bar) uint16_t vendor_id, device_id; if (!pcibus) { - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); } /* Find PCI device and verify it's the right one */ @@ -239,7 +245,7 @@ static int send_dma_request(int cmd, uint64_t sector, int nb_sectors, /* Setup PRDT */ len = sizeof(*prdt) * prdt_entries; - guest_prdt = guest_alloc(guest_malloc, len); + guest_prdt = guest_alloc(&guest_malloc, len); memwrite(guest_prdt, prdt, len); qpci_io_writel(dev, bmdma_bar, bmreg_prdt, guest_prdt); @@ -304,7 +310,7 @@ static void test_bmdma_simple_rw(void) uint8_t *buf; uint8_t *cmpbuf; size_t len = 512; - uintptr_t guest_buf = guest_alloc(guest_malloc, len); + uintptr_t guest_buf = guest_alloc(&guest_malloc, len); PrdtEntry prdt[] = { { @@ -374,7 +380,7 @@ static void test_bmdma_trim(void) const uint64_t bad_range = trim_range_le(TEST_IMAGE_SIZE / 512 - 1, 2); size_t len = 512; uint8_t *buf; - uintptr_t guest_buf = guest_alloc(guest_malloc, len); + uintptr_t guest_buf = guest_alloc(&guest_malloc, len); PrdtEntry prdt[] = { { @@ -529,8 +535,8 @@ static void test_bmdma_no_busmaster(void) static void test_bmdma_setup(void) { ide_test_start( - "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " - "-global ide-hd.ver=%s", + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", tmp_path, "testdisk", "version"); qtest_irq_intercept_in(global_qtest, "ioapic"); } @@ -561,8 +567,8 @@ static void test_identify(void) int ret; ide_test_start( - "-drive file=%s,if=ide,serial=%s,cache=writeback,format=raw " - "-global ide-hd.ver=%s", + "-drive file=%s,if=ide,cache=writeback,format=raw " + "-global ide-hd.serial=%s -global ide-hd.ver=%s", tmp_path, "testdisk", "version"); dev = get_pci_device(&bmdma_bar, &ide_bar); @@ -618,7 +624,7 @@ static void make_dirty(uint8_t device) dev = get_pci_device(&bmdma_bar, &ide_bar); - guest_buf = guest_alloc(guest_malloc, len); + guest_buf = guest_alloc(&guest_malloc, len); buf = g_malloc(len); memset(buf, rand() % 255 + 1, len); g_assert(guest_buf); @@ -694,7 +700,6 @@ static void test_retry_flush(const char *machine) QPCIDevice *dev; QPCIBar bmdma_bar, ide_bar; uint8_t data; - const char *s; prepare_blkdebug_script(debug_path, "flush_to_disk"); @@ -722,8 +727,7 @@ static void test_retry_flush(const char *machine) qmp_eventwait("STOP"); /* Complete the command */ - s = "{'execute':'cont' }"; - qmp_discard_response(s); + qmp_discard_response("{'execute':'cont' }"); /* Check registers */ data = qpci_io_readb(dev, ide_bar, reg_device); @@ -981,7 +985,7 @@ static void test_cdrom_dma(void) "-device ide-cd,drive=sr0,bus=ide.0", tmp_path); qtest_irq_intercept_in(global_qtest, "ioapic"); - guest_buf = guest_alloc(guest_malloc, len); + guest_buf = guest_alloc(&guest_malloc, len); prdt[0].addr = cpu_to_le32(guest_buf); prdt[0].size = cpu_to_le32(len | PRDT_EOT); @@ -1004,16 +1008,9 @@ static void test_cdrom_dma(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); int fd; int ret; - /* Check architecture */ - if (strcmp(arch, "i386") && strcmp(arch, "x86_64")) { - g_test_message("Skipping test for non-x86\n"); - return 0; - } - /* Create temporary blkdebug instructions */ fd = mkstemp(debug_path); g_assert(fd >= 0); diff --git a/tests/ipmi-bt-test.c b/tests/ipmi-bt-test.c index 8be18e3f42..fc4c83b5db 100644 --- a/tests/ipmi-bt-test.c +++ b/tests/ipmi-bt-test.c @@ -400,21 +400,14 @@ static void open_socket(void) int main(int argc, char **argv) { - const char *arch = qtest_get_arch(); 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); - global_qtest = qtest_startf( + global_qtest = qtest_initf( " -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); diff --git a/tests/ipmi-kcs-test.c b/tests/ipmi-kcs-test.c index 178ffc1797..a2354c10c7 100644 --- a/tests/ipmi-kcs-test.c +++ b/tests/ipmi-kcs-test.c @@ -263,16 +263,9 @@ static void test_enable_irq(void) 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); diff --git a/tests/ipoctal232-test.c b/tests/ipoctal232-test.c index 684914164d..42d53718b8 100644 --- a/tests/ipoctal232-test.c +++ b/tests/ipoctal232-test.c @@ -9,23 +9,40 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" + +typedef struct QIpoctal232 QIpoctal232; + +struct QIpoctal232 { + QOSGraphObject obj; +}; /* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +static void nop(void *obj, void *data, QGuestAllocator *alloc) { } -int main(int argc, char **argv) +static void *ipoctal232_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) { - int ret; + QIpoctal232 *ipoctal232 = g_new0(QIpoctal232, 1); - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ipoctal232/tpci200/nop", nop); - - qtest_start("-device tpci200,id=ipack0 -device ipoctal232,bus=ipack0.0"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &ipoctal232->obj; } + +static void ipoctal232_register_nodes(void) +{ + qos_node_create_driver("ipoctal232", ipoctal232_create); + qos_node_consumes("ipoctal232", "ipack", &(QOSGraphEdgeOptions) { + .extra_device_opts = "bus=ipack0.0", + }); +} + +libqos_init(ipoctal232_register_nodes); + +static void register_ipoctal232_test(void) +{ + qos_add_test("nop", "ipoctal232", nop, NULL); +} + +libqos_init(register_ipoctal232_test); diff --git a/tests/ivshmem-test.c b/tests/ivshmem-test.c index 9b407a3e42..227561fbca 100644 --- a/tests/ivshmem-test.c +++ b/tests/ivshmem-test.c @@ -71,13 +71,10 @@ static const char* reg2str(enum Reg reg) { static inline unsigned in_reg(IVState *s, enum Reg reg) { const char *name = reg2str(reg); - QTestState *qtest = global_qtest; unsigned res; - global_qtest = s->qs->qts; res = qpci_io_readl(s->dev, s->reg_bar, reg); - g_test_message("*%s -> %x\n", name, res); - global_qtest = qtest; + g_test_message("*%s -> %x", name, res); return res; } @@ -85,35 +82,25 @@ static inline unsigned in_reg(IVState *s, enum Reg reg) static inline void out_reg(IVState *s, enum Reg reg, unsigned v) { const char *name = reg2str(reg); - QTestState *qtest = global_qtest; - global_qtest = s->qs->qts; - g_test_message("%x -> *%s\n", v, name); + g_test_message("%x -> *%s", v, name); qpci_io_writel(s->dev, s->reg_bar, reg, v); - global_qtest = qtest; } static inline void read_mem(IVState *s, uint64_t off, void *buf, size_t len) { - QTestState *qtest = global_qtest; - - global_qtest = s->qs->qts; qpci_memread(s->dev, s->mem_bar, off, buf, len); - global_qtest = qtest; } static inline void write_mem(IVState *s, uint64_t off, const void *buf, size_t len) { - QTestState *qtest = global_qtest; - - global_qtest = s->qs->qts; qpci_memwrite(s->dev, s->mem_bar, off, buf, len); - global_qtest = qtest; } static void cleanup_vm(IVState *s) { + assert(!global_qtest); g_free(s->dev); qtest_shutdown(s->qs); } @@ -131,7 +118,6 @@ static void setup_vm_cmd(IVState *s, const char *cmd, bool msix) g_printerr("ivshmem-test tests are only available on x86 or ppc64\n"); exit(EXIT_FAILURE); } - global_qtest = s->qs->qts; s->dev = get_device(s->qs->pcibus); s->reg_bar = qpci_iomap(s->dev, 0, &barsize); @@ -305,20 +291,20 @@ static void *server_thread(void *data) return NULL; } -static void setup_vm_with_server(IVState *s, int nvectors, bool msi) +static void setup_vm_with_server(IVState *s, int nvectors) { - 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); + char *cmd; - setup_vm_cmd(s, cmd, msi); + cmd = g_strdup_printf("-chardev socket,id=chr0,path=%s " + "-device ivshmem-doorbell,chardev=chr0,vectors=%d", + tmpserver, nvectors); + + setup_vm_cmd(s, cmd, true); g_free(cmd); } -static void test_ivshmem_server(bool msi) +static void test_ivshmem_server(void) { IVState state1, state2, *s1, *s2; ServerThread thread; @@ -341,9 +327,9 @@ static void test_ivshmem_server(bool msi) thread.thread = g_thread_new("ivshmem-server", server_thread, &thread); g_assert(thread.thread != NULL); - setup_vm_with_server(&state1, nvectors, msi); + setup_vm_with_server(&state1, nvectors); s1 = &state1; - setup_vm_with_server(&state2, nvectors, msi); + setup_vm_with_server(&state2, nvectors); s2 = &state2; /* check got different VM ids */ @@ -354,40 +340,28 @@ static void test_ivshmem_server(bool msi) g_assert_cmpint(vm1, !=, vm2); /* check number of MSI-X vectors */ - global_qtest = s1->qs->qts; - if (msi) { - ret = qpci_msix_table_size(s1->dev); - g_assert_cmpuint(ret, ==, nvectors); - } + 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); - } + ret = qpci_msix_pending(s1->dev, 0); + g_assert_cmpuint(ret, ==, 0); out_reg(s2, DOORBELL, vm1 << 16); do { g_usleep(10000); - ret = msi ? qpci_msix_pending(s1->dev, 0) : in_reg(s1, INTRSTATUS); + ret = qpci_msix_pending(s1->dev, 0); } while (ret == 0 && g_get_monotonic_time() < end_time); g_assert_cmpuint(ret, !=, 0); /* ping vm1 -> vm2 on vector 1 */ - global_qtest = s2->qs->qts; - if (msi) { - ret = qpci_msix_pending(s2->dev, 1); - g_assert_cmpuint(ret, ==, 0); - } else { - g_assert_cmpuint(in_reg(s2, INTRSTATUS), ==, 0); - } + ret = qpci_msix_pending(s2->dev, 1); + g_assert_cmpuint(ret, ==, 0); out_reg(s1, DOORBELL, vm2 << 16 | 1); do { g_usleep(10000); - ret = msi ? qpci_msix_pending(s2->dev, 1) : in_reg(s2, INTRSTATUS); + ret = qpci_msix_pending(s2->dev, 1); } while (ret == 0 && g_get_monotonic_time() < end_time); g_assert_cmpuint(ret, !=, 0); @@ -405,34 +379,22 @@ static void test_ivshmem_server(bool msi) 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) { const char *arch = qtest_get_arch(); - gchar *opts; - qtest_start(""); + qtest_start("-object memory-backend-ram,size=1M,id=mb1"); - opts = g_strdup_printf("'shm': '%s', 'size': '1M'", tmpshm); - - qpci_plug_device_test("ivshmem", "iv1", PCI_SLOT_HP, opts); + qtest_qmp_device_add("ivshmem-plain", "iv1", + "{'addr': %s, 'memdev': 'mb1'}", + stringify(PCI_SLOT_HP)); if (strcmp(arch, "ppc64") != 0) { qpci_unplug_acpi_device_test("iv1", PCI_SLOT_HP); } qtest_end(); - g_free(opts); } static void test_ivshmem_memdev(void) @@ -510,7 +472,7 @@ int main(int argc, char **argv) /* shm */ tmpshm = mktempshm(TMPSHMSIZE, &fd); if (!tmpshm) { - return 0; + goto out; } tmpshmem = mmap(0, TMPSHMSIZE, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); g_assert(tmpshmem != MAP_FAILED); @@ -527,14 +489,12 @@ int main(int argc, char **argv) if (g_test_slow()) { qtest_add_func("/ivshmem/pair", test_ivshmem_pair); if (strcmp(arch, "ppc64") != 0) { - qtest_add_func("/ivshmem/server-msi", test_ivshmem_server_msi); - qtest_add_func("/ivshmem/server-irq", test_ivshmem_server_irq); + qtest_add_func("/ivshmem/server", test_ivshmem_server); } } +out: ret = g_test_run(); - cleanup(); - return ret; } diff --git a/tests/libqos/aarch64-xlnx-zcu102-machine.c b/tests/libqos/aarch64-xlnx-zcu102-machine.c new file mode 100644 index 0000000000..6fff040b37 --- /dev/null +++ b/tests/libqos/aarch64-xlnx-zcu102-machine.c @@ -0,0 +1,94 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +typedef struct QXlnxZCU102Machine QXlnxZCU102Machine; + +struct QXlnxZCU102Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +#define ARM_PAGE_SIZE 4096 +#define XLNX_ZCU102_RAM_ADDR 0 +#define XLNX_ZCU102_RAM_SIZE 0x20000000 + +static void *xlnx_zcu102_get_driver(void *object, const char *interface) +{ + QXlnxZCU102Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *xlnx_zcu102_get_device(void *obj, const char *device) +{ + QXlnxZCU102Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in aarch64/xlnx-zcu102\n", device); + g_assert_not_reached(); +} + +static void xlnx_zcu102_destructor(QOSGraphObject *obj) +{ + QXlnxZCU102Machine *machine = (QXlnxZCU102Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_aarch64_xlnx_zcu102(QTestState *qts) +{ + QXlnxZCU102Machine *machine = g_new0(QXlnxZCU102Machine, 1); + + alloc_init(&machine->alloc, 0, + XLNX_ZCU102_RAM_ADDR + (1 << 20), + XLNX_ZCU102_RAM_ADDR + XLNX_ZCU102_RAM_SIZE, + ARM_PAGE_SIZE); + + machine->obj.get_device = xlnx_zcu102_get_device; + machine->obj.get_driver = xlnx_zcu102_get_driver; + machine->obj.destructor = xlnx_zcu102_destructor; + /* Datasheet: UG1085 (v1.7) */ + qos_init_sdhci_mm(&machine->sdhci, qts, 0xff160000, &(QSDHCIProperties) { + .version = 3, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x280737ec6481 + }); + return &machine->obj; +} + +static void xlnx_zcu102_register_nodes(void) +{ + qos_node_create_machine("aarch64/xlnx-zcu102", + qos_create_machine_aarch64_xlnx_zcu102); + qos_node_contains("aarch64/xlnx-zcu102", "generic-sdhci", NULL); +} + +libqos_init(xlnx_zcu102_register_nodes); diff --git a/tests/libqos/ahci.c b/tests/libqos/ahci.c index 42d3f76933..cc1b08eabe 100644 --- a/tests/libqos/ahci.c +++ b/tests/libqos/ahci.c @@ -130,7 +130,7 @@ QPCIDevice *get_ahci_device(QTestState *qts, uint32_t *fingerprint) uint32_t ahci_fingerprint; QPCIBus *pcibus; - pcibus = qpci_init_pc(qts, NULL); + pcibus = qpci_new_pc(qts, NULL); /* Find the AHCI PCI device and verify it's the right one. */ ahci = qpci_device_find(pcibus, QPCI_DEVFN(0x1F, 0x02)); @@ -674,7 +674,7 @@ void ahci_exec(AHCIQState *ahci, uint8_t port, g_assert_cmpint(rc, ==, 0); } if (opts->error) { - qtest_async_qmp(ahci->parent->qts, "{'execute':'cont' }"); + qtest_qmp_send(ahci->parent->qts, "{'execute':'cont' }"); qtest_qmp_eventwait(ahci->parent->qts, "RESUME"); } @@ -712,7 +712,7 @@ AHCICommand *ahci_guest_io_halt(AHCIQState *ahci, uint8_t port, void ahci_guest_io_resume(AHCIQState *ahci, AHCICommand *cmd) { /* Complete the command */ - qtest_async_qmp(ahci->parent->qts, "{'execute':'cont' }"); + qtest_qmp_send(ahci->parent->qts, "{'execute':'cont' }"); qtest_qmp_eventwait(ahci->parent->qts, "RESUME"); ahci_command_wait(ahci, cmd); ahci_command_verify(ahci, cmd); diff --git a/tests/libqos/arm-raspi2-machine.c b/tests/libqos/arm-raspi2-machine.c new file mode 100644 index 0000000000..3aff670f76 --- /dev/null +++ b/tests/libqos/arm-raspi2-machine.c @@ -0,0 +1,91 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +#define ARM_PAGE_SIZE 4096 +#define RASPI2_RAM_ADDR 0 +#define RASPI2_RAM_SIZE 0x20000000 + +typedef struct QRaspi2Machine QRaspi2Machine; + +struct QRaspi2Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +static void *raspi2_get_driver(void *object, const char *interface) +{ + QRaspi2Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/raspi2\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *raspi2_get_device(void *obj, const char *device) +{ + QRaspi2Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/raspi2\n", device); + g_assert_not_reached(); +} + +static void raspi2_destructor(QOSGraphObject *obj) +{ + QRaspi2Machine *machine = (QRaspi2Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_raspi2(QTestState *qts) +{ + QRaspi2Machine *machine = g_new0(QRaspi2Machine, 1); + + alloc_init(&machine->alloc, 0, + RASPI2_RAM_ADDR + (1 << 20), + RASPI2_RAM_ADDR + RASPI2_RAM_SIZE, + ARM_PAGE_SIZE); + machine->obj.get_device = raspi2_get_device; + machine->obj.get_driver = raspi2_get_driver; + machine->obj.destructor = raspi2_destructor; + qos_init_sdhci_mm(&machine->sdhci, qts, 0x3f300000, &(QSDHCIProperties) { + .version = 3, + .baseclock = 52, + .capab.sdma = false, + .capab.reg = 0x052134b4 + }); + return &machine->obj; +} + +static void raspi2_register_nodes(void) +{ + qos_node_create_machine("arm/raspi2", qos_create_machine_arm_raspi2); + qos_node_contains("arm/raspi2", "generic-sdhci", NULL); +} + +libqos_init(raspi2_register_nodes); diff --git a/tests/libqos/arm-sabrelite-machine.c b/tests/libqos/arm-sabrelite-machine.c new file mode 100644 index 0000000000..c4128d8686 --- /dev/null +++ b/tests/libqos/arm-sabrelite-machine.c @@ -0,0 +1,91 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +#define ARM_PAGE_SIZE 4096 +#define SABRELITE_RAM_START 0x10000000 +#define SABRELITE_RAM_END 0x30000000 + +typedef struct QSabreliteMachine QSabreliteMachine; + +struct QSabreliteMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +static void *sabrelite_get_driver(void *object, const char *interface) +{ + QSabreliteMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/sabrelite\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *sabrelite_get_device(void *obj, const char *device) +{ + QSabreliteMachine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/sabrelite\n", device); + g_assert_not_reached(); +} + +static void sabrelite_destructor(QOSGraphObject *obj) +{ + QSabreliteMachine *machine = (QSabreliteMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_sabrelite(QTestState *qts) +{ + QSabreliteMachine *machine = g_new0(QSabreliteMachine, 1); + + alloc_init(&machine->alloc, 0, + SABRELITE_RAM_START, + SABRELITE_RAM_END, + ARM_PAGE_SIZE); + machine->obj.get_device = sabrelite_get_device; + machine->obj.get_driver = sabrelite_get_driver; + machine->obj.destructor = sabrelite_destructor; + qos_init_sdhci_mm(&machine->sdhci, qts, 0x02190000, &(QSDHCIProperties) { + .version = 3, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x057834b4, + }); + return &machine->obj; +} + +static void sabrelite_register_nodes(void) +{ + qos_node_create_machine("arm/sabrelite", qos_create_machine_arm_sabrelite); + qos_node_contains("arm/sabrelite", "generic-sdhci", NULL); +} + +libqos_init(sabrelite_register_nodes); diff --git a/tests/libqos/arm-smdkc210-machine.c b/tests/libqos/arm-smdkc210-machine.c new file mode 100644 index 0000000000..1fb9dfc0cb --- /dev/null +++ b/tests/libqos/arm-smdkc210-machine.c @@ -0,0 +1,91 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +#define ARM_PAGE_SIZE 4096 +#define SMDKC210_RAM_ADDR 0x40000000ull +#define SMDKC210_RAM_SIZE 0x40000000ull + +typedef struct QSmdkc210Machine QSmdkc210Machine; + +struct QSmdkc210Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +static void *smdkc210_get_driver(void *object, const char *interface) +{ + QSmdkc210Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/smdkc210\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *smdkc210_get_device(void *obj, const char *device) +{ + QSmdkc210Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/smdkc210\n", device); + g_assert_not_reached(); +} + +static void smdkc210_destructor(QOSGraphObject *obj) +{ + QSmdkc210Machine *machine = (QSmdkc210Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_smdkc210(QTestState *qts) +{ + QSmdkc210Machine *machine = g_new0(QSmdkc210Machine, 1); + + alloc_init(&machine->alloc, 0, + SMDKC210_RAM_ADDR, + SMDKC210_RAM_ADDR + SMDKC210_RAM_SIZE, + ARM_PAGE_SIZE); + machine->obj.get_device = smdkc210_get_device; + machine->obj.get_driver = smdkc210_get_driver; + machine->obj.destructor = smdkc210_destructor; + qos_init_sdhci_mm(&machine->sdhci, qts, 0x12510000, &(QSDHCIProperties) { + .version = 2, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x5e80080, + }); + return &machine->obj; +} + +static void smdkc210_register_nodes(void) +{ + qos_node_create_machine("arm/smdkc210", qos_create_machine_arm_smdkc210); + qos_node_contains("arm/smdkc210", "generic-sdhci", NULL); +} + +libqos_init(smdkc210_register_nodes); diff --git a/tests/libqos/arm-virt-machine.c b/tests/libqos/arm-virt-machine.c new file mode 100644 index 0000000000..2abc431ecf --- /dev/null +++ b/tests/libqos/arm-virt-machine.c @@ -0,0 +1,90 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-mmio.h" + +#define ARM_PAGE_SIZE 4096 +#define VIRTIO_MMIO_BASE_ADDR 0x0A003E00 +#define ARM_VIRT_RAM_ADDR 0x40000000 +#define ARM_VIRT_RAM_SIZE 0x20000000 +#define VIRTIO_MMIO_SIZE 0x00000200 + +typedef struct QVirtMachine QVirtMachine; + +struct QVirtMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QVirtioMMIODevice virtio_mmio; +}; + +static void virt_destructor(QOSGraphObject *obj) +{ + QVirtMachine *machine = (QVirtMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *virt_get_driver(void *object, const char *interface) +{ + QVirtMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/virtio\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *virt_get_device(void *obj, const char *device) +{ + QVirtMachine *machine = obj; + if (!g_strcmp0(device, "virtio-mmio")) { + return &machine->virtio_mmio.obj; + } + + fprintf(stderr, "%s not present in arm/virtio\n", device); + g_assert_not_reached(); +} + +static void *qos_create_machine_arm_virt(QTestState *qts) +{ + QVirtMachine *machine = g_new0(QVirtMachine, 1); + + alloc_init(&machine->alloc, 0, + ARM_VIRT_RAM_ADDR, + ARM_VIRT_RAM_ADDR + ARM_VIRT_RAM_SIZE, + ARM_PAGE_SIZE); + qvirtio_mmio_init_device(&machine->virtio_mmio, qts, VIRTIO_MMIO_BASE_ADDR, + VIRTIO_MMIO_SIZE); + + machine->obj.get_device = virt_get_device; + machine->obj.get_driver = virt_get_driver; + machine->obj.destructor = virt_destructor; + return machine; +} + +static void virtio_mmio_register_nodes(void) +{ + qos_node_create_machine("arm/virt", qos_create_machine_arm_virt); + qos_node_contains("arm/virt", "virtio-mmio", NULL); +} + +libqos_init(virtio_mmio_register_nodes); diff --git a/tests/libqos/arm-xilinx-zynq-a9-machine.c b/tests/libqos/arm-xilinx-zynq-a9-machine.c new file mode 100644 index 0000000000..4e199fcd48 --- /dev/null +++ b/tests/libqos/arm-xilinx-zynq-a9-machine.c @@ -0,0 +1,94 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "sdhci.h" + +typedef struct QXilinxZynqA9Machine QXilinxZynqA9Machine; + +struct QXilinxZynqA9Machine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSDHCI_MemoryMapped sdhci; +}; + +#define ARM_PAGE_SIZE 4096 +#define XILINX_ZYNQ_A9_RAM_ADDR 0 +#define XILINX_ZYNQ_A9_RAM_SIZE 0x20000000 + +static void *xilinx_zynq_a9_get_driver(void *object, const char *interface) +{ + QXilinxZynqA9Machine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *xilinx_zynq_a9_get_device(void *obj, const char *device) +{ + QXilinxZynqA9Machine *machine = obj; + if (!g_strcmp0(device, "generic-sdhci")) { + return &machine->sdhci.obj; + } + + fprintf(stderr, "%s not present in arm/xilinx-zynq-a9\n", device); + g_assert_not_reached(); +} + +static void xilinx_zynq_a9_destructor(QOSGraphObject *obj) +{ + QXilinxZynqA9Machine *machine = (QXilinxZynqA9Machine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *qos_create_machine_arm_xilinx_zynq_a9(QTestState *qts) +{ + QXilinxZynqA9Machine *machine = g_new0(QXilinxZynqA9Machine, 1); + + alloc_init(&machine->alloc, 0, + XILINX_ZYNQ_A9_RAM_ADDR + (1 << 20), + XILINX_ZYNQ_A9_RAM_ADDR + XILINX_ZYNQ_A9_RAM_SIZE, + ARM_PAGE_SIZE); + + machine->obj.get_device = xilinx_zynq_a9_get_device; + machine->obj.get_driver = xilinx_zynq_a9_get_driver; + machine->obj.destructor = xilinx_zynq_a9_destructor; + /* Datasheet: UG585 (v1.12.1) */ + qos_init_sdhci_mm(&machine->sdhci, qts, 0xe0100000, &(QSDHCIProperties) { + .version = 2, + .baseclock = 0, + .capab.sdma = true, + .capab.reg = 0x69ec0080, + }); + return &machine->obj; +} + +static void xilinx_zynq_a9_register_nodes(void) +{ + qos_node_create_machine("arm/xilinx-zynq-a9", + qos_create_machine_arm_xilinx_zynq_a9); + qos_node_contains("arm/xilinx-zynq-a9", "generic-sdhci", NULL); +} + +libqos_init(xilinx_zynq_a9_register_nodes); diff --git a/tests/libqos/e1000e.c b/tests/libqos/e1000e.c new file mode 100644 index 0000000000..54d3898899 --- /dev/null +++ b/tests/libqos/e1000e.c @@ -0,0 +1,260 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu-common.h" +#include "libqos/pci-pc.h" +#include "qemu/sockets.h" +#include "qemu/iov.h" +#include "qemu/bitops.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "e1000e.h" + +#define E1000E_IMS (0x00d0) + +#define E1000E_STATUS (0x0008) +#define E1000E_STATUS_LU BIT(1) +#define E1000E_STATUS_ASDV1000 BIT(9) + +#define E1000E_CTRL (0x0000) +#define E1000E_CTRL_RESET BIT(26) + +#define E1000E_RCTL (0x0100) +#define E1000E_RCTL_EN BIT(1) +#define E1000E_RCTL_UPE BIT(3) +#define E1000E_RCTL_MPE BIT(4) + +#define E1000E_RFCTL (0x5008) +#define E1000E_RFCTL_EXTEN BIT(15) + +#define E1000E_TCTL (0x0400) +#define E1000E_TCTL_EN BIT(1) + +#define E1000E_CTRL_EXT (0x0018) +#define E1000E_CTRL_EXT_DRV_LOAD BIT(28) +#define E1000E_CTRL_EXT_TXLSFLOW BIT(22) + +#define E1000E_IVAR (0x00E4) +#define E1000E_IVAR_TEST_CFG ((E1000E_RX0_MSG_ID << 0) | BIT(3) | \ + (E1000E_TX0_MSG_ID << 8) | BIT(11) | \ + (E1000E_OTHER_MSG_ID << 16) | BIT(19) | \ + BIT(31)) + +#define E1000E_RING_LEN (0x1000) + +#define E1000E_TDBAL (0x3800) + +#define E1000E_TDBAH (0x3804) +#define E1000E_TDH (0x3810) + +#define E1000E_RDBAL (0x2800) +#define E1000E_RDBAH (0x2804) +#define E1000E_RDH (0x2810) + +#define E1000E_TXD_LEN (16) +#define E1000E_RXD_LEN (16) + +static void e1000e_macreg_write(QE1000E *d, uint32_t reg, uint32_t val) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + qpci_io_writel(&d_pci->pci_dev, d_pci->mac_regs, reg, val); +} + +static uint32_t e1000e_macreg_read(QE1000E *d, uint32_t reg) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + return qpci_io_readl(&d_pci->pci_dev, d_pci->mac_regs, reg); +} + +void e1000e_tx_ring_push(QE1000E *d, void *descr) +{ + uint32_t tail = e1000e_macreg_read(d, E1000E_TDT); + uint32_t len = e1000e_macreg_read(d, E1000E_TDLEN) / E1000E_TXD_LEN; + + memwrite(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); + e1000e_macreg_write(d, E1000E_TDT, (tail + 1) % len); + + /* Read WB data for the packet transmitted */ + memread(d->tx_ring + tail * E1000E_TXD_LEN, descr, E1000E_TXD_LEN); +} + +void e1000e_rx_ring_push(QE1000E *d, void *descr) +{ + uint32_t tail = e1000e_macreg_read(d, E1000E_RDT); + uint32_t len = e1000e_macreg_read(d, E1000E_RDLEN) / E1000E_RXD_LEN; + + memwrite(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); + e1000e_macreg_write(d, E1000E_RDT, (tail + 1) % len); + + /* Read WB data for the packet received */ + memread(d->rx_ring + tail * E1000E_RXD_LEN, descr, E1000E_RXD_LEN); +} + +static void e1000e_foreach_callback(QPCIDevice *dev, int devfn, void *data) +{ + QPCIDevice *res = data; + memcpy(res, dev, sizeof(QPCIDevice)); + g_free(dev); +} + +void e1000e_wait_isr(QE1000E *d, uint16_t msg_id) +{ + QE1000E_PCI *d_pci = container_of(d, QE1000E_PCI, e1000e); + guint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; + + do { + if (qpci_msix_pending(&d_pci->pci_dev, msg_id)) { + return; + } + clock_step(10000); + } while (g_get_monotonic_time() < end_time); + + g_error("Timeout expired"); +} + +static void e1000e_pci_destructor(QOSGraphObject *obj) +{ + QE1000E_PCI *epci = (QE1000E_PCI *) obj; + qpci_iounmap(&epci->pci_dev, epci->mac_regs); + qpci_msix_disable(&epci->pci_dev); +} + +static void e1000e_pci_start_hw(QOSGraphObject *obj) +{ + QE1000E_PCI *d = (QE1000E_PCI *) obj; + uint32_t val; + + /* Enable the device */ + qpci_device_enable(&d->pci_dev); + + /* Reset the device */ + val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL); + e1000e_macreg_write(&d->e1000e, E1000E_CTRL, val | E1000E_CTRL_RESET); + + /* Enable and configure MSI-X */ + qpci_msix_enable(&d->pci_dev); + e1000e_macreg_write(&d->e1000e, E1000E_IVAR, E1000E_IVAR_TEST_CFG); + + /* Check the device status - link and speed */ + val = e1000e_macreg_read(&d->e1000e, E1000E_STATUS); + g_assert_cmphex(val & (E1000E_STATUS_LU | E1000E_STATUS_ASDV1000), + ==, E1000E_STATUS_LU | E1000E_STATUS_ASDV1000); + + /* Initialize TX/RX logic */ + e1000e_macreg_write(&d->e1000e, E1000E_RCTL, 0); + e1000e_macreg_write(&d->e1000e, E1000E_TCTL, 0); + + /* Notify the device that the driver is ready */ + val = e1000e_macreg_read(&d->e1000e, E1000E_CTRL_EXT); + e1000e_macreg_write(&d->e1000e, E1000E_CTRL_EXT, + val | E1000E_CTRL_EXT_DRV_LOAD | E1000E_CTRL_EXT_TXLSFLOW); + + e1000e_macreg_write(&d->e1000e, E1000E_TDBAL, + (uint32_t) d->e1000e.tx_ring); + e1000e_macreg_write(&d->e1000e, E1000E_TDBAH, + (uint32_t) (d->e1000e.tx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000E_TDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000E_TDT, 0); + e1000e_macreg_write(&d->e1000e, E1000E_TDH, 0); + + /* Enable transmit */ + e1000e_macreg_write(&d->e1000e, E1000E_TCTL, E1000E_TCTL_EN); + e1000e_macreg_write(&d->e1000e, E1000E_RDBAL, + (uint32_t)d->e1000e.rx_ring); + e1000e_macreg_write(&d->e1000e, E1000E_RDBAH, + (uint32_t)(d->e1000e.rx_ring >> 32)); + e1000e_macreg_write(&d->e1000e, E1000E_RDLEN, E1000E_RING_LEN); + e1000e_macreg_write(&d->e1000e, E1000E_RDT, 0); + e1000e_macreg_write(&d->e1000e, E1000E_RDH, 0); + + /* Enable receive */ + e1000e_macreg_write(&d->e1000e, E1000E_RFCTL, E1000E_RFCTL_EXTEN); + e1000e_macreg_write(&d->e1000e, E1000E_RCTL, E1000E_RCTL_EN | + E1000E_RCTL_UPE | + E1000E_RCTL_MPE); + + /* Enable all interrupts */ + e1000e_macreg_write(&d->e1000e, E1000E_IMS, 0xFFFFFFFF); + +} + +static void *e1000e_pci_get_driver(void *obj, const char *interface) +{ + QE1000E_PCI *epci = obj; + if (!g_strcmp0(interface, "e1000e-if")) { + return &epci->e1000e; + } + + /* implicit contains */ + if (!g_strcmp0(interface, "pci-device")) { + return &epci->pci_dev; + } + + fprintf(stderr, "%s not present in e1000e\n", interface); + g_assert_not_reached(); +} + +static void *e1000e_pci_create(void *pci_bus, QGuestAllocator *alloc, + void *addr) +{ + QE1000E_PCI *d = g_new0(QE1000E_PCI, 1); + QPCIBus *bus = pci_bus; + QPCIAddress *address = addr; + + qpci_device_foreach(bus, address->vendor_id, address->device_id, + e1000e_foreach_callback, &d->pci_dev); + + /* Map BAR0 (mac registers) */ + d->mac_regs = qpci_iomap(&d->pci_dev, 0, NULL); + + /* Allocate and setup TX ring */ + d->e1000e.tx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.tx_ring != 0); + + /* Allocate and setup RX ring */ + d->e1000e.rx_ring = guest_alloc(alloc, E1000E_RING_LEN); + g_assert(d->e1000e.rx_ring != 0); + + d->obj.get_driver = e1000e_pci_get_driver; + d->obj.start_hw = e1000e_pci_start_hw; + d->obj.destructor = e1000e_pci_destructor; + + return &d->obj; +} + +static void e1000e_register_nodes(void) +{ + QPCIAddress addr = { + .vendor_id = 0x8086, + .device_id = 0x10D3, + }; + + /* FIXME: every test using this node needs to setup a -netdev socket,id=hs0 + * otherwise QEMU is not going to start */ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "netdev=hs0", + }; + add_qpci_address(&opts, &addr); + + qos_node_create_driver("e1000e", e1000e_pci_create); + qos_node_consumes("e1000e", "pci-bus", &opts); +} + +libqos_init(e1000e_register_nodes); diff --git a/tests/libqos/e1000e.h b/tests/libqos/e1000e.h new file mode 100644 index 0000000000..9d37094f43 --- /dev/null +++ b/tests/libqos/e1000e.h @@ -0,0 +1,53 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#ifndef QGRAPH_E1000E +#define QGRAPH_E1000E + +#include "libqos/qgraph.h" +#include "pci.h" + +#define E1000E_RX0_MSG_ID (0) +#define E1000E_TX0_MSG_ID (1) +#define E1000E_OTHER_MSG_ID (2) + +#define E1000E_TDLEN (0x3808) +#define E1000E_TDT (0x3818) +#define E1000E_RDLEN (0x2808) +#define E1000E_RDT (0x2818) + +typedef struct QE1000E QE1000E; +typedef struct QE1000E_PCI QE1000E_PCI; + +struct QE1000E { + uint64_t tx_ring; + uint64_t rx_ring; +}; + +struct QE1000E_PCI { + QOSGraphObject obj; + QPCIDevice pci_dev; + QPCIBar mac_regs; + QE1000E e1000e; +}; + +void e1000e_wait_isr(QE1000E *d, uint16_t msg_id); +void e1000e_tx_ring_push(QE1000E *d, void *descr); +void e1000e_rx_ring_push(QE1000E *d, void *descr); + +#endif diff --git a/tests/libqos/libqos-pc.c b/tests/libqos/libqos-pc.c index a9c1aceaa7..d04abc548b 100644 --- a/tests/libqos/libqos-pc.c +++ b/tests/libqos/libqos-pc.c @@ -4,9 +4,8 @@ #include "libqos/pci-pc.h" static QOSOps qos_ops = { - .init_allocator = pc_alloc_init_flags, - .uninit_allocator = pc_alloc_uninit, - .qpci_init = qpci_init_pc, + .alloc_init = pc_alloc_init, + .qpci_new = qpci_new_pc, .qpci_free = qpci_free_pc, .shutdown = qtest_pc_shutdown, }; diff --git a/tests/libqos/libqos-spapr.c b/tests/libqos/libqos-spapr.c index a37791e5d0..8766d543ce 100644 --- a/tests/libqos/libqos-spapr.c +++ b/tests/libqos/libqos-spapr.c @@ -4,9 +4,8 @@ #include "libqos/pci-spapr.h" static QOSOps qos_ops = { - .init_allocator = spapr_alloc_init_flags, - .uninit_allocator = spapr_alloc_uninit, - .qpci_init = qpci_init_spapr, + .alloc_init = spapr_alloc_init, + .qpci_new = qpci_new_spapr, .qpci_free = qpci_free_spapr, .shutdown = qtest_spapr_shutdown, }; diff --git a/tests/libqos/libqos.c b/tests/libqos/libqos.c index 013ca68581..d71557c5cb 100644 --- a/tests/libqos/libqos.c +++ b/tests/libqos/libqos.c @@ -24,8 +24,8 @@ QOSState *qtest_vboot(QOSOps *ops, const char *cmdline_fmt, va_list ap) qs->qts = qtest_init(cmdline); qs->ops = ops; if (ops) { - qs->alloc = ops->init_allocator(qs->qts, ALLOC_NO_FLAGS); - qs->pcibus = ops->qpci_init(qs->qts, qs->alloc); + ops->alloc_init(&qs->alloc, qs->qts, ALLOC_NO_FLAGS); + qs->pcibus = ops->qpci_new(qs->qts, &qs->alloc); } g_free(cmdline); @@ -58,11 +58,8 @@ void qtest_common_shutdown(QOSState *qs) qs->ops->qpci_free(qs->pcibus); qs->pcibus = NULL; } - if (qs->alloc && qs->ops->uninit_allocator) { - qs->ops->uninit_allocator(qs->alloc); - qs->alloc = NULL; - } } + alloc_destroy(&qs->alloc); qtest_quit(qs->qts); g_free(qs); } @@ -76,11 +73,6 @@ void qtest_shutdown(QOSState *qs) } } -void set_context(QOSState *s) -{ - global_qtest = s->qts; -} - static QDict *qmp_execute(QTestState *qts, const char *command) { return qtest_qmp(qts, "{ 'execute': %s }", command); @@ -92,8 +84,6 @@ void migrate(QOSState *from, QOSState *to, const char *uri) QDict *rsp, *sub; bool running; - set_context(from); - /* Is the machine currently running? */ rsp = qmp_execute(from->qts, "query-status"); g_assert(qdict_haskey(rsp, "return")); @@ -116,8 +106,7 @@ void migrate(QOSState *from, QOSState *to, const char *uri) /* If we were running, we can wait for an event. */ if (running) { - migrate_allocator(from->alloc, to->alloc); - set_context(to); + migrate_allocator(&from->alloc, &to->alloc); qtest_qmp_eventwait(to->qts, "RESUME"); return; } @@ -146,8 +135,7 @@ void migrate(QOSState *from, QOSState *to, const char *uri) g_assert_not_reached(); } - migrate_allocator(from->alloc, to->alloc); - set_context(to); + migrate_allocator(&from->alloc, &to->alloc); } bool have_qemu_img(void) @@ -185,22 +173,12 @@ void mkimg(const char *file, const char *fmt, unsigned size_mb) 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) { + if (err || !g_spawn_check_exit_status(rc, &err)) { fprintf(stderr, "%s\n", err->message); g_error_free(err); } g_assert(ret && !err); - /* In glib 2.34, we have g_spawn_check_exit_status. in 2.12, we don't. - * glib 2.43.91 implementation assumes that any non-zero is an error for - * windows, but uses extra precautions for Linux. However, - * 0 is only possible if the program exited normally, so that should be - * sufficient for our purposes on all platforms, here. */ - if (rc) { - fprintf(stderr, "qemu-img returned status code %d\n", rc); - } - g_assert(!rc); - g_free(out); g_free(out2); g_free(cli); diff --git a/tests/libqos/libqos.h b/tests/libqos/libqos.h index 07d4b93d1d..8e971c25a3 100644 --- a/tests/libqos/libqos.h +++ b/tests/libqos/libqos.h @@ -3,21 +3,20 @@ #include "libqtest.h" #include "libqos/pci.h" -#include "libqos/malloc-pc.h" +#include "libqos/malloc.h" typedef struct QOSState QOSState; typedef struct QOSOps { - QGuestAllocator *(*init_allocator)(QTestState *qts, QAllocOpts); - void (*uninit_allocator)(QGuestAllocator *); - QPCIBus *(*qpci_init)(QTestState *qts, QGuestAllocator *alloc); + void (*alloc_init)(QGuestAllocator *, QTestState *, QAllocOpts); + QPCIBus *(*qpci_new)(QTestState *qts, QGuestAllocator *alloc); void (*qpci_free)(QPCIBus *bus); void (*shutdown)(QOSState *); } QOSOps; struct QOSState { QTestState *qts; - QGuestAllocator *alloc; + QGuestAllocator alloc; QPCIBus *pcibus; QOSOps *ops; }; @@ -29,19 +28,18 @@ 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) { - return guest_alloc(q->alloc, bytes); + return guest_alloc(&q->alloc, bytes); } static inline void qfree(QOSState *q, uint64_t addr) { - guest_free(q->alloc, addr); + guest_free(&q->alloc, addr); } #endif diff --git a/tests/libqos/malloc-generic.c b/tests/libqos/malloc-generic.c deleted file mode 100644 index 33ce90b925..0000000000 --- a/tests/libqos/malloc-generic.c +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Basic libqos generic malloc support - * - * Copyright (c) 2014 Marc Marí - * - * 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/malloc-generic.h" -#include "libqos/malloc.h" - -/* - * Mostly for valgrind happiness, but it does offer - * a chokepoint for debugging guest memory leaks, too. - */ -void generic_alloc_uninit(QGuestAllocator *allocator) -{ - alloc_uninit(allocator); -} - -QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, - uint32_t page_size, QAllocOpts flags) -{ - QGuestAllocator *s; - uint64_t start = base_addr + (1 << 20); /* Start at 1MB */ - - s = alloc_init_flags(flags, start, start + size); - alloc_set_page_size(s, page_size); - - return s; -} - -inline QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, - uint32_t page_size) -{ - return generic_alloc_init_flags(base_addr, size, page_size, ALLOC_NO_FLAGS); -} diff --git a/tests/libqos/malloc-generic.h b/tests/libqos/malloc-generic.h deleted file mode 100644 index 90104ecec9..0000000000 --- a/tests/libqos/malloc-generic.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Basic libqos generic malloc support - * - * Copyright (c) 2014 Marc Marí - * - * 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 LIBQOS_MALLOC_GENERIC_H -#define LIBQOS_MALLOC_GENERIC_H - -#include "libqos/malloc.h" - -QGuestAllocator *generic_alloc_init(uint64_t base_addr, uint64_t size, - uint32_t page_size); -QGuestAllocator *generic_alloc_init_flags(uint64_t base_addr, uint64_t size, - uint32_t page_size, QAllocOpts flags); -void generic_alloc_uninit(QGuestAllocator *allocator); - -#endif diff --git a/tests/libqos/malloc-pc.c b/tests/libqos/malloc-pc.c index 634b9c288a..949a99361d 100644 --- a/tests/libqos/malloc-pc.c +++ b/tests/libqos/malloc-pc.c @@ -14,38 +14,20 @@ #include "libqos/malloc-pc.h" #include "libqos/fw_cfg.h" -#include "hw/nvram/fw_cfg_keys.h" +#include "standard-headers/linux/qemu_fw_cfg.h" #include "qemu-common.h" #define PAGE_SIZE (4096) -/* - * Mostly for valgrind happiness, but it does offer - * a chokepoint for debugging guest memory leaks, too. - */ -void pc_alloc_uninit(QGuestAllocator *allocator) +void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags) { - alloc_uninit(allocator); -} - -QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags) -{ - QGuestAllocator *s; uint64_t ram_size; QFWCFG *fw_cfg = pc_fw_cfg_init(qts); ram_size = qfw_cfg_get_u64(fw_cfg, FW_CFG_RAM_SIZE); - s = alloc_init_flags(flags, 1 << 20, MIN(ram_size, 0xE0000000)); - alloc_set_page_size(s, PAGE_SIZE); + alloc_init(s, flags, 1 << 20, MIN(ram_size, 0xE0000000), PAGE_SIZE); /* clean-up */ g_free(fw_cfg); - - return s; -} - -inline QGuestAllocator *pc_alloc_init(QTestState *qts) -{ - return pc_alloc_init_flags(qts, ALLOC_NO_FLAGS); } diff --git a/tests/libqos/malloc-pc.h b/tests/libqos/malloc-pc.h index 10f3da6cf2..21e75ae004 100644 --- a/tests/libqos/malloc-pc.h +++ b/tests/libqos/malloc-pc.h @@ -15,8 +15,6 @@ #include "libqos/malloc.h" -QGuestAllocator *pc_alloc_init(QTestState *qts); -QGuestAllocator *pc_alloc_init_flags(QTestState *qts, QAllocOpts flags); -void pc_alloc_uninit(QGuestAllocator *allocator); +void pc_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags); #endif diff --git a/tests/libqos/malloc-spapr.c b/tests/libqos/malloc-spapr.c index 1c359cea6c..2a6b7e3776 100644 --- a/tests/libqos/malloc-spapr.c +++ b/tests/libqos/malloc-spapr.c @@ -17,22 +17,7 @@ */ #define SPAPR_MIN_SIZE 0x10000000 -void spapr_alloc_uninit(QGuestAllocator *allocator) +void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags) { - alloc_uninit(allocator); -} - -QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags) -{ - QGuestAllocator *s; - - s = alloc_init_flags(flags, 1 << 20, SPAPR_MIN_SIZE); - alloc_set_page_size(s, PAGE_SIZE); - - return s; -} - -QGuestAllocator *spapr_alloc_init(void) -{ - return spapr_alloc_init_flags(NULL, ALLOC_NO_FLAGS); + alloc_init(s, flags, 1 << 20, SPAPR_MIN_SIZE, PAGE_SIZE); } diff --git a/tests/libqos/malloc-spapr.h b/tests/libqos/malloc-spapr.h index 52a9346a26..e5fe9bfc4b 100644 --- a/tests/libqos/malloc-spapr.h +++ b/tests/libqos/malloc-spapr.h @@ -10,8 +10,6 @@ #include "libqos/malloc.h" -QGuestAllocator *spapr_alloc_init(void); -QGuestAllocator *spapr_alloc_init_flags(QTestState *qts, QAllocOpts flags); -void spapr_alloc_uninit(QGuestAllocator *allocator); +void spapr_alloc_init(QGuestAllocator *s, QTestState *qts, QAllocOpts flags); #endif diff --git a/tests/libqos/malloc.c b/tests/libqos/malloc.c index ac05874b0a..615422a5c4 100644 --- a/tests/libqos/malloc.c +++ b/tests/libqos/malloc.c @@ -15,24 +15,12 @@ #include "qemu-common.h" #include "qemu/host-utils.h" -typedef QTAILQ_HEAD(MemList, MemBlock) MemList; - typedef struct MemBlock { QTAILQ_ENTRY(MemBlock) MLIST_ENTNAME; uint64_t size; uint64_t addr; } MemBlock; -struct QGuestAllocator { - QAllocOpts opts; - uint64_t start; - uint64_t end; - uint32_t page_size; - - MemList *used; - MemList *free; -}; - #define DEFAULT_PAGE_SIZE 4096 static void mlist_delete(MemList *list, MemBlock *node) @@ -104,7 +92,7 @@ static void mlist_coalesce(MemList *head, MemBlock *node) do { merge = 0; - left = QTAILQ_PREV(node, MemList, MLIST_ENTNAME); + left = QTAILQ_PREV(node, MLIST_ENTNAME); right = QTAILQ_NEXT(node, MLIST_ENTNAME); /* clowns to the left of me */ @@ -225,7 +213,7 @@ static void mlist_free(QGuestAllocator *s, uint64_t addr) * Mostly for valgrind happiness, but it does offer * a chokepoint for debugging guest memory leaks, too. */ -void alloc_uninit(QGuestAllocator *allocator) +void alloc_destroy(QGuestAllocator *allocator) { MemBlock *node; MemBlock *tmp; @@ -261,7 +249,6 @@ void alloc_uninit(QGuestAllocator *allocator) g_free(allocator->used); g_free(allocator->free); - g_free(allocator); } uint64_t guest_alloc(QGuestAllocator *allocator, size_t size) @@ -297,11 +284,13 @@ void guest_free(QGuestAllocator *allocator, uint64_t addr) } } -QGuestAllocator *alloc_init(uint64_t start, uint64_t end) +void alloc_init(QGuestAllocator *s, QAllocOpts opts, + uint64_t start, uint64_t end, + size_t page_size) { - QGuestAllocator *s = g_malloc0(sizeof(*s)); MemBlock *node; + s->opts = opts; s->start = start; s->end = end; @@ -313,26 +302,7 @@ QGuestAllocator *alloc_init(uint64_t start, uint64_t end) node = mlist_new(s->start, s->end - s->start); QTAILQ_INSERT_HEAD(s->free, node, MLIST_ENTNAME); - s->page_size = DEFAULT_PAGE_SIZE; - - return s; -} - -QGuestAllocator *alloc_init_flags(QAllocOpts opts, - uint64_t start, uint64_t end) -{ - QGuestAllocator *s = alloc_init(start, end); - s->opts = opts; - return s; -} - -void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size) -{ - /* Can't alter the page_size for an allocator in-use */ - g_assert(QTAILQ_EMPTY(allocator->used)); - - g_assert(is_power_of_2(page_size)); - allocator->page_size = page_size; + s->page_size = page_size; } void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts) diff --git a/tests/libqos/malloc.h b/tests/libqos/malloc.h index 828fddabdb..4d1a2e2bef 100644 --- a/tests/libqos/malloc.h +++ b/tests/libqos/malloc.h @@ -23,19 +23,28 @@ typedef enum { ALLOC_PARANOID = 0x04 } QAllocOpts; -typedef struct QGuestAllocator QGuestAllocator; +typedef QTAILQ_HEAD(MemList, MemBlock) MemList; -void alloc_uninit(QGuestAllocator *allocator); +typedef struct QGuestAllocator { + QAllocOpts opts; + uint64_t start; + uint64_t end; + uint32_t page_size; + + MemList *used; + MemList *free; +} QGuestAllocator; /* Always returns page aligned values */ uint64_t guest_alloc(QGuestAllocator *allocator, size_t size); void guest_free(QGuestAllocator *allocator, uint64_t addr); void migrate_allocator(QGuestAllocator *src, QGuestAllocator *dst); -QGuestAllocator *alloc_init(uint64_t start, uint64_t end); -QGuestAllocator *alloc_init_flags(QAllocOpts flags, - uint64_t start, uint64_t end); -void alloc_set_page_size(QGuestAllocator *allocator, size_t page_size); void alloc_set_flags(QGuestAllocator *allocator, QAllocOpts opts); +void alloc_init(QGuestAllocator *alloc, QAllocOpts flags, + uint64_t start, uint64_t end, + size_t page_size); +void alloc_destroy(QGuestAllocator *allocator); + #endif diff --git a/tests/libqos/pci-pc.c b/tests/libqos/pci-pc.c index a7803308b7..407d8aff78 100644 --- a/tests/libqos/pci-pc.c +++ b/tests/libqos/pci-pc.c @@ -18,141 +18,160 @@ #include "qemu-common.h" - #define ACPI_PCIHP_ADDR 0xae00 #define PCI_EJ_BASE 0x0008 -typedef struct QPCIBusPC -{ - QPCIBus bus; -} QPCIBusPC; - static uint8_t qpci_pc_pio_readb(QPCIBus *bus, uint32_t addr) { - return inb(addr); + return qtest_inb(bus->qts, addr); } static void qpci_pc_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { - outb(addr, val); + qtest_outb(bus->qts, addr, val); } static uint16_t qpci_pc_pio_readw(QPCIBus *bus, uint32_t addr) { - return inw(addr); + return qtest_inw(bus->qts, addr); } static void qpci_pc_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) { - outw(addr, val); + qtest_outw(bus->qts, addr, val); } static uint32_t qpci_pc_pio_readl(QPCIBus *bus, uint32_t addr) { - return inl(addr); + return qtest_inl(bus->qts, addr); } static void qpci_pc_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) { - outl(addr, val); + qtest_outl(bus->qts, addr, val); } static uint64_t qpci_pc_pio_readq(QPCIBus *bus, uint32_t addr) { - return (uint64_t)inl(addr) + ((uint64_t)inl(addr + 4) << 32); + return (uint64_t)qtest_inl(bus->qts, addr) + + ((uint64_t)qtest_inl(bus->qts, addr + 4) << 32); } static void qpci_pc_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) { - outl(addr, val & 0xffffffff); - outl(addr + 4, val >> 32); + qtest_outl(bus->qts, addr, val & 0xffffffff); + qtest_outl(bus->qts, addr + 4, val >> 32); } static void qpci_pc_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { - memread(addr, buf, len); + qtest_memread(bus->qts, addr, buf, len); } static void qpci_pc_memwrite(QPCIBus *bus, uint32_t addr, const void *buf, size_t len) { - memwrite(addr, buf, len); + qtest_memwrite(bus->qts, addr, buf, len); } static uint8_t qpci_pc_config_readb(QPCIBus *bus, int devfn, uint8_t offset) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - return inb(0xcfc); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + return qtest_inb(bus->qts, 0xcfc); } static uint16_t qpci_pc_config_readw(QPCIBus *bus, int devfn, uint8_t offset) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - return inw(0xcfc); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + return qtest_inw(bus->qts, 0xcfc); } static uint32_t qpci_pc_config_readl(QPCIBus *bus, int devfn, uint8_t offset) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - return inl(0xcfc); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + return qtest_inl(bus->qts, 0xcfc); } static void qpci_pc_config_writeb(QPCIBus *bus, int devfn, uint8_t offset, uint8_t value) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - outb(0xcfc, value); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + qtest_outb(bus->qts, 0xcfc, value); } static void qpci_pc_config_writew(QPCIBus *bus, int devfn, uint8_t offset, uint16_t value) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - outw(0xcfc, value); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + qtest_outw(bus->qts, 0xcfc, value); } static void qpci_pc_config_writel(QPCIBus *bus, int devfn, uint8_t offset, uint32_t value) { - outl(0xcf8, (1U << 31) | (devfn << 8) | offset); - outl(0xcfc, value); + qtest_outl(bus->qts, 0xcf8, (1U << 31) | (devfn << 8) | offset); + qtest_outl(bus->qts, 0xcfc, value); } -QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc) +static void *qpci_pc_get_driver(void *obj, const char *interface) { - QPCIBusPC *ret = g_new0(QPCIBusPC, 1); + QPCIBusPC *qpci = obj; + if (!g_strcmp0(interface, "pci-bus")) { + return &qpci->bus; + } + fprintf(stderr, "%s not present in pci-bus-pc\n", interface); + g_assert_not_reached(); +} +void qpci_init_pc(QPCIBusPC *qpci, QTestState *qts, QGuestAllocator *alloc) +{ assert(qts); - ret->bus.pio_readb = qpci_pc_pio_readb; - ret->bus.pio_readw = qpci_pc_pio_readw; - ret->bus.pio_readl = qpci_pc_pio_readl; - ret->bus.pio_readq = qpci_pc_pio_readq; + /* tests can use pci-bus */ + qpci->bus.has_buggy_msi = false; - ret->bus.pio_writeb = qpci_pc_pio_writeb; - ret->bus.pio_writew = qpci_pc_pio_writew; - ret->bus.pio_writel = qpci_pc_pio_writel; - ret->bus.pio_writeq = qpci_pc_pio_writeq; + qpci->bus.pio_readb = qpci_pc_pio_readb; + qpci->bus.pio_readw = qpci_pc_pio_readw; + qpci->bus.pio_readl = qpci_pc_pio_readl; + qpci->bus.pio_readq = qpci_pc_pio_readq; - ret->bus.memread = qpci_pc_memread; - ret->bus.memwrite = qpci_pc_memwrite; + qpci->bus.pio_writeb = qpci_pc_pio_writeb; + qpci->bus.pio_writew = qpci_pc_pio_writew; + qpci->bus.pio_writel = qpci_pc_pio_writel; + qpci->bus.pio_writeq = qpci_pc_pio_writeq; - ret->bus.config_readb = qpci_pc_config_readb; - ret->bus.config_readw = qpci_pc_config_readw; - ret->bus.config_readl = qpci_pc_config_readl; + qpci->bus.memread = qpci_pc_memread; + qpci->bus.memwrite = qpci_pc_memwrite; - ret->bus.config_writeb = qpci_pc_config_writeb; - ret->bus.config_writew = qpci_pc_config_writew; - ret->bus.config_writel = qpci_pc_config_writel; + qpci->bus.config_readb = qpci_pc_config_readb; + qpci->bus.config_readw = qpci_pc_config_readw; + qpci->bus.config_readl = qpci_pc_config_readl; - ret->bus.qts = qts; - ret->bus.pio_alloc_ptr = 0xc000; - ret->bus.mmio_alloc_ptr = 0xE0000000; - ret->bus.mmio_limit = 0x100000000ULL; + qpci->bus.config_writeb = qpci_pc_config_writeb; + qpci->bus.config_writew = qpci_pc_config_writew; + qpci->bus.config_writel = qpci_pc_config_writel; - return &ret->bus; + qpci->bus.qts = qts; + qpci->bus.pio_alloc_ptr = 0xc000; + qpci->bus.mmio_alloc_ptr = 0xE0000000; + qpci->bus.mmio_limit = 0x100000000ULL; + + qpci->obj.get_driver = qpci_pc_get_driver; +} + +QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc) +{ + QPCIBusPC *qpci = g_new0(QPCIBusPC, 1); + qpci_init_pc(qpci, qts, alloc); + + return &qpci->bus; } void qpci_free_pc(QPCIBus *bus) { - QPCIBusPC *s = container_of(bus, QPCIBusPC, bus); + QPCIBusPC *s; + + if (!bus) { + return; + } + s = container_of(bus, QPCIBusPC, bus); g_free(s); } @@ -160,14 +179,9 @@ void qpci_free_pc(QPCIBus *bus) void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) { QDict *response; - char *cmd; - cmd = g_strdup_printf("{'execute': 'device_del'," - " 'arguments': {" - " 'id': '%s'" - "}}", id); - response = qmp(cmd); - g_free(cmd); + response = qmp("{'execute': 'device_del', 'arguments': {'id': %s}}", + id); g_assert(response); g_assert(!qdict_haskey(response, "error")); qobject_unref(response); @@ -176,3 +190,11 @@ void qpci_unplug_acpi_device_test(const char *id, uint8_t slot) qmp_eventwait("DEVICE_DELETED"); } + +static void qpci_pc_register_nodes(void) +{ + qos_node_create_driver("pci-bus-pc", NULL); + qos_node_produces("pci-bus-pc", "pci-bus"); +} + +libqos_init(qpci_pc_register_nodes); diff --git a/tests/libqos/pci-pc.h b/tests/libqos/pci-pc.h index 491eeac756..4690005232 100644 --- a/tests/libqos/pci-pc.h +++ b/tests/libqos/pci-pc.h @@ -15,8 +15,35 @@ #include "libqos/pci.h" #include "libqos/malloc.h" +#include "libqos/qgraph.h" + +typedef struct QPCIBusPC { + QOSGraphObject obj; + QPCIBus bus; +} QPCIBusPC; + +/* qpci_init_pc(): + * @ret: A valid QPCIBusPC * pointer + * @qts: The %QTestState for this PC machine + * @alloc: A previously initialized @alloc providing memory for @qts + * + * This function initializes an already allocated + * QPCIBusPC object. + */ +void qpci_init_pc(QPCIBusPC *ret, QTestState *qts, QGuestAllocator *alloc); + +/* qpci_pc_new(): + * @qts: The %QTestState for this PC machine + * @alloc: A previously initialized @alloc providing memory for @qts + * + * This function creates a new QPCIBusPC object, + * and properly initialize its fields. + * + * Returns the QPCIBus *bus field of a newly + * allocated QPCIBusPC. + */ +QPCIBus *qpci_new_pc(QTestState *qts, QGuestAllocator *alloc); -QPCIBus *qpci_init_pc(QTestState *qts, QGuestAllocator *alloc); void qpci_free_pc(QPCIBus *bus); #endif diff --git a/tests/libqos/pci-spapr.c b/tests/libqos/pci-spapr.c index c0f7e6db9b..58ba27ae6d 100644 --- a/tests/libqos/pci-spapr.c +++ b/tests/libqos/pci-spapr.c @@ -9,33 +9,13 @@ #include "libqtest.h" #include "libqos/pci-spapr.h" #include "libqos/rtas.h" +#include "libqos/qgraph.h" #include "hw/pci/pci_regs.h" #include "qemu-common.h" #include "qemu/host-utils.h" - -/* From include/hw/pci-host/spapr.h */ - -typedef struct QPCIWindow { - uint64_t pci_base; /* window address in PCI space */ - uint64_t size; /* window size */ -} QPCIWindow; - -typedef struct QPCIBusSPAPR { - QPCIBus bus; - QGuestAllocator *alloc; - - uint64_t buid; - - uint64_t pio_cpu_base; - QPCIWindow pio; - - uint64_t mmio32_cpu_base; - QPCIWindow mmio32; -} QPCIBusSPAPR; - /* * PCI devices are always little-endian * SPAPR by default is big-endian @@ -45,63 +25,63 @@ typedef struct QPCIBusSPAPR { static uint8_t qpci_spapr_pio_readb(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return readb(s->pio_cpu_base + addr); + return qtest_readb(bus->qts, s->pio_cpu_base + addr); } static void qpci_spapr_pio_writeb(QPCIBus *bus, uint32_t addr, uint8_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writeb(s->pio_cpu_base + addr, val); + qtest_writeb(bus->qts, s->pio_cpu_base + addr, val); } static uint16_t qpci_spapr_pio_readw(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return bswap16(readw(s->pio_cpu_base + addr)); + return bswap16(qtest_readw(bus->qts, s->pio_cpu_base + addr)); } static void qpci_spapr_pio_writew(QPCIBus *bus, uint32_t addr, uint16_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writew(s->pio_cpu_base + addr, bswap16(val)); + qtest_writew(bus->qts, s->pio_cpu_base + addr, bswap16(val)); } static uint32_t qpci_spapr_pio_readl(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return bswap32(readl(s->pio_cpu_base + addr)); + return bswap32(qtest_readl(bus->qts, s->pio_cpu_base + addr)); } static void qpci_spapr_pio_writel(QPCIBus *bus, uint32_t addr, uint32_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writel(s->pio_cpu_base + addr, bswap32(val)); + qtest_writel(bus->qts, s->pio_cpu_base + addr, bswap32(val)); } static uint64_t qpci_spapr_pio_readq(QPCIBus *bus, uint32_t addr) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - return bswap64(readq(s->pio_cpu_base + addr)); + return bswap64(qtest_readq(bus->qts, s->pio_cpu_base + addr)); } static void qpci_spapr_pio_writeq(QPCIBus *bus, uint32_t addr, uint64_t val) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - writeq(s->pio_cpu_base + addr, bswap64(val)); + qtest_writeq(bus->qts, s->pio_cpu_base + addr, bswap64(val)); } static void qpci_spapr_memread(QPCIBus *bus, uint32_t addr, void *buf, size_t len) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - memread(s->mmio32_cpu_base + addr, buf, len); + qtest_memread(bus->qts, s->mmio32_cpu_base + addr, buf, len); } static void qpci_spapr_memwrite(QPCIBus *bus, uint32_t addr, const void *buf, size_t len) { QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); - memwrite(s->mmio32_cpu_base + addr, buf, len); + qtest_memwrite(bus->qts, s->mmio32_cpu_base + addr, buf, len); } static uint8_t qpci_spapr_config_readb(QPCIBus *bus, int devfn, uint8_t offset) @@ -160,60 +140,93 @@ static void qpci_spapr_config_writel(QPCIBus *bus, int devfn, uint8_t offset, #define SPAPR_PCI_MMIO32_WIN_SIZE 0x80000000 /* 2 GiB */ #define SPAPR_PCI_IO_WIN_SIZE 0x10000 -QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc) +static void *qpci_spapr_get_driver(void *obj, const char *interface) { - QPCIBusSPAPR *ret = g_new0(QPCIBusSPAPR, 1); + QPCIBusSPAPR *qpci = obj; + if (!g_strcmp0(interface, "pci-bus")) { + return &qpci->bus; + } + fprintf(stderr, "%s not present in pci-bus-spapr", interface); + g_assert_not_reached(); +} +void qpci_init_spapr(QPCIBusSPAPR *qpci, QTestState *qts, + QGuestAllocator *alloc) +{ assert(qts); - ret->alloc = alloc; + /* tests cannot use spapr, needs to be fixed first */ + qpci->bus.has_buggy_msi = true; - ret->bus.pio_readb = qpci_spapr_pio_readb; - ret->bus.pio_readw = qpci_spapr_pio_readw; - ret->bus.pio_readl = qpci_spapr_pio_readl; - ret->bus.pio_readq = qpci_spapr_pio_readq; + qpci->alloc = alloc; - ret->bus.pio_writeb = qpci_spapr_pio_writeb; - ret->bus.pio_writew = qpci_spapr_pio_writew; - ret->bus.pio_writel = qpci_spapr_pio_writel; - ret->bus.pio_writeq = qpci_spapr_pio_writeq; + qpci->bus.pio_readb = qpci_spapr_pio_readb; + qpci->bus.pio_readw = qpci_spapr_pio_readw; + qpci->bus.pio_readl = qpci_spapr_pio_readl; + qpci->bus.pio_readq = qpci_spapr_pio_readq; - ret->bus.memread = qpci_spapr_memread; - ret->bus.memwrite = qpci_spapr_memwrite; + qpci->bus.pio_writeb = qpci_spapr_pio_writeb; + qpci->bus.pio_writew = qpci_spapr_pio_writew; + qpci->bus.pio_writel = qpci_spapr_pio_writel; + qpci->bus.pio_writeq = qpci_spapr_pio_writeq; - ret->bus.config_readb = qpci_spapr_config_readb; - ret->bus.config_readw = qpci_spapr_config_readw; - ret->bus.config_readl = qpci_spapr_config_readl; + qpci->bus.memread = qpci_spapr_memread; + qpci->bus.memwrite = qpci_spapr_memwrite; - ret->bus.config_writeb = qpci_spapr_config_writeb; - ret->bus.config_writew = qpci_spapr_config_writew; - ret->bus.config_writel = qpci_spapr_config_writel; + qpci->bus.config_readb = qpci_spapr_config_readb; + qpci->bus.config_readw = qpci_spapr_config_readw; + qpci->bus.config_readl = qpci_spapr_config_readl; + + qpci->bus.config_writeb = qpci_spapr_config_writeb; + qpci->bus.config_writew = qpci_spapr_config_writew; + qpci->bus.config_writel = qpci_spapr_config_writel; /* FIXME: We assume the default location of the PHB for now. * Ideally we'd parse the device tree deposited in the guest to * get the window locations */ - ret->buid = 0x800000020000000ULL; + qpci->buid = 0x800000020000000ULL; - ret->pio_cpu_base = SPAPR_PCI_BASE; - ret->pio.pci_base = 0; - ret->pio.size = SPAPR_PCI_IO_WIN_SIZE; + qpci->pio_cpu_base = SPAPR_PCI_BASE; + qpci->pio.pci_base = 0; + qpci->pio.size = SPAPR_PCI_IO_WIN_SIZE; /* 32-bit portion of the MMIO window is at PCI address 2..4 GiB */ - ret->mmio32_cpu_base = SPAPR_PCI_BASE; - ret->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE; - ret->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; + qpci->mmio32_cpu_base = SPAPR_PCI_BASE; + qpci->mmio32.pci_base = SPAPR_PCI_MMIO32_WIN_SIZE; + qpci->mmio32.size = SPAPR_PCI_MMIO32_WIN_SIZE; - ret->bus.qts = qts; - ret->bus.pio_alloc_ptr = 0xc000; - ret->bus.mmio_alloc_ptr = ret->mmio32.pci_base; - ret->bus.mmio_limit = ret->mmio32.pci_base + ret->mmio32.size; + qpci->bus.qts = qts; + qpci->bus.pio_alloc_ptr = 0xc000; + qpci->bus.mmio_alloc_ptr = qpci->mmio32.pci_base; + qpci->bus.mmio_limit = qpci->mmio32.pci_base + qpci->mmio32.size; - return &ret->bus; + qpci->obj.get_driver = qpci_spapr_get_driver; +} + +QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc) +{ + QPCIBusSPAPR *qpci = g_new0(QPCIBusSPAPR, 1); + qpci_init_spapr(qpci, qts, alloc); + + return &qpci->bus; } void qpci_free_spapr(QPCIBus *bus) { - QPCIBusSPAPR *s = container_of(bus, QPCIBusSPAPR, bus); + QPCIBusSPAPR *s; + + if (!bus) { + return; + } + s = container_of(bus, QPCIBusSPAPR, bus); g_free(s); } + +static void qpci_spapr_register_nodes(void) +{ + qos_node_create_driver("pci-bus-spapr", NULL); + qos_node_produces("pci-bus-spapr", "pci-bus"); +} + +libqos_init(qpci_spapr_register_nodes); diff --git a/tests/libqos/pci-spapr.h b/tests/libqos/pci-spapr.h index 387686dfc8..d9e25631c6 100644 --- a/tests/libqos/pci-spapr.h +++ b/tests/libqos/pci-spapr.h @@ -10,8 +10,32 @@ #include "libqos/malloc.h" #include "libqos/pci.h" +#include "libqos/qgraph.h" -QPCIBus *qpci_init_spapr(QTestState *qts, QGuestAllocator *alloc); +/* From include/hw/pci-host/spapr.h */ + +typedef struct QPCIWindow { + uint64_t pci_base; /* window address in PCI space */ + uint64_t size; /* window size */ +} QPCIWindow; + +typedef struct QPCIBusSPAPR { + QOSGraphObject obj; + QPCIBus bus; + QGuestAllocator *alloc; + + uint64_t buid; + + uint64_t pio_cpu_base; + QPCIWindow pio; + + uint64_t mmio32_cpu_base; + QPCIWindow mmio32; +} QPCIBusSPAPR; + +void qpci_init_spapr(QPCIBusSPAPR *ret, QTestState *qts, + QGuestAllocator *alloc); +QPCIBus *qpci_new_spapr(QTestState *qts, QGuestAllocator *alloc); void qpci_free_spapr(QPCIBus *bus); #endif diff --git a/tests/libqos/pci.c b/tests/libqos/pci.c index 0b73cb23d0..662ee7a517 100644 --- a/tests/libqos/pci.c +++ b/tests/libqos/pci.c @@ -15,6 +15,7 @@ #include "hw/pci/pci_regs.h" #include "qemu/host-utils.h" +#include "libqos/qgraph.h" void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), @@ -50,13 +51,34 @@ void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, } } +bool qpci_has_buggy_msi(QPCIDevice *dev) +{ + return dev->bus->has_buggy_msi; +} + +bool qpci_check_buggy_msi(QPCIDevice *dev) +{ + if (qpci_has_buggy_msi(dev)) { + g_test_skip("Skipping due to incomplete support for MSI"); + return true; + } + return false; +} + +static void qpci_device_set(QPCIDevice *dev, QPCIBus *bus, int devfn) +{ + g_assert(dev); + + dev->bus = bus; + dev->devfn = devfn; +} + QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn) { QPCIDevice *dev; dev = g_malloc0(sizeof(*dev)); - dev->bus = bus; - dev->devfn = devfn; + qpci_device_set(dev, bus, devfn); if (qpci_config_readw(dev, PCI_VENDOR_ID) == 0xFFFF) { g_free(dev); @@ -66,6 +88,17 @@ QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn) return dev; } +void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr) +{ + uint16_t vendor_id, device_id; + + qpci_device_set(dev, bus, addr->devfn); + vendor_id = qpci_config_readw(dev, PCI_VENDOR_ID); + device_id = qpci_config_readw(dev, PCI_DEVICE_ID); + g_assert(!addr->vendor_id || vendor_id == addr->vendor_id); + g_assert(!addr->device_id || device_id == addr->device_id); +} + void qpci_device_enable(QPCIDevice *dev) { uint16_t cmd; @@ -396,9 +429,11 @@ QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr) return bar; } -void qpci_plug_device_test(const char *driver, const char *id, - uint8_t slot, const char *opts) +void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr) { - qtest_qmp_device_add(driver, id, "'addr': '%d'%s%s", slot, - opts ? ", " : "", opts ? opts : ""); + g_assert(addr); + g_assert(opts); + + opts->arg = addr; + opts->size_arg = sizeof(QPCIAddress); } diff --git a/tests/libqos/pci.h b/tests/libqos/pci.h index 429c382282..8e1d292a7d 100644 --- a/tests/libqos/pci.h +++ b/tests/libqos/pci.h @@ -14,6 +14,7 @@ #define LIBQOS_PCI_H #include "libqtest.h" +#include "libqos/qgraph.h" #define QPCI_PIO_LIMIT 0x10000 @@ -22,6 +23,7 @@ typedef struct QPCIDevice QPCIDevice; typedef struct QPCIBus QPCIBus; typedef struct QPCIBar QPCIBar; +typedef struct QPCIAddress QPCIAddress; struct QPCIBus { uint8_t (*pio_readb)(QPCIBus *bus, uint32_t addr); @@ -51,6 +53,8 @@ struct QPCIBus { QTestState *qts; uint16_t pio_alloc_ptr; uint64_t mmio_alloc_ptr, mmio_limit; + bool has_buggy_msi; /* TRUE for spapr, FALSE for pci */ + }; struct QPCIBar { @@ -66,10 +70,20 @@ struct QPCIDevice uint64_t msix_table_off, msix_pba_off; }; +struct QPCIAddress { + uint32_t devfn; + uint16_t vendor_id; + uint16_t device_id; +}; + void qpci_device_foreach(QPCIBus *bus, int vendor_id, int device_id, void (*func)(QPCIDevice *dev, int devfn, void *data), void *data); QPCIDevice *qpci_device_find(QPCIBus *bus, int devfn); +void qpci_device_init(QPCIDevice *dev, QPCIBus *bus, QPCIAddress *addr); + +bool qpci_has_buggy_msi(QPCIDevice *dev); +bool qpci_check_buggy_msi(QPCIDevice *dev); void qpci_device_enable(QPCIDevice *dev); uint8_t qpci_find_capability(QPCIDevice *dev, uint8_t id); @@ -109,7 +123,7 @@ QPCIBar qpci_iomap(QPCIDevice *dev, int barno, uint64_t *sizeptr); void qpci_iounmap(QPCIDevice *dev, QPCIBar addr); QPCIBar qpci_legacy_iomap(QPCIDevice *dev, uint16_t addr); -void qpci_plug_device_test(const char *driver, const char *id, - uint8_t slot, const char *opts); void qpci_unplug_acpi_device_test(const char *id, uint8_t slot); + +void add_qpci_address(QOSGraphEdgeOptions *opts, QPCIAddress *addr); #endif diff --git a/tests/libqos/ppc64_pseries-machine.c b/tests/libqos/ppc64_pseries-machine.c new file mode 100644 index 0000000000..2f3640010d --- /dev/null +++ b/tests/libqos/ppc64_pseries-machine.c @@ -0,0 +1,111 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + + #include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "pci-spapr.h" +#include "libqos/malloc-spapr.h" + +typedef struct QSPAPR_pci_host QSPAPR_pci_host; +typedef struct Qppc64_pseriesMachine Qppc64_pseriesMachine; + +struct QSPAPR_pci_host { + QOSGraphObject obj; + QPCIBusSPAPR pci; +}; + +struct Qppc64_pseriesMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + QSPAPR_pci_host bridge; +}; + +/* QSPAPR_pci_host */ + +static QOSGraphObject *QSPAPR_host_get_device(void *obj, const char *device) +{ + QSPAPR_pci_host *host = obj; + if (!g_strcmp0(device, "pci-bus-spapr")) { + return &host->pci.obj; + } + fprintf(stderr, "%s not present in QSPAPR_pci_host\n", device); + g_assert_not_reached(); +} + +static void qos_create_QSPAPR_host(QSPAPR_pci_host *host, + QTestState *qts, + QGuestAllocator *alloc) +{ + host->obj.get_device = QSPAPR_host_get_device; + qpci_init_spapr(&host->pci, qts, alloc); +} + +/* ppc64/pseries machine */ + +static void spapr_destructor(QOSGraphObject *obj) +{ + Qppc64_pseriesMachine *machine = (Qppc64_pseriesMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *spapr_get_driver(void *object, const char *interface) +{ + Qppc64_pseriesMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in ppc64/pseries\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *spapr_get_device(void *obj, const char *device) +{ + Qppc64_pseriesMachine *machine = obj; + if (!g_strcmp0(device, "spapr-pci-host-bridge")) { + return &machine->bridge.obj; + } + + fprintf(stderr, "%s not present in ppc64/pseries\n", device); + g_assert_not_reached(); +} + +static void *qos_create_machine_spapr(QTestState *qts) +{ + Qppc64_pseriesMachine *machine = g_new0(Qppc64_pseriesMachine, 1); + machine->obj.get_device = spapr_get_device; + machine->obj.get_driver = spapr_get_driver; + machine->obj.destructor = spapr_destructor; + spapr_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS); + + qos_create_QSPAPR_host(&machine->bridge, qts, &machine->alloc); + + return &machine->obj; +} + +static void spapr_machine_register_nodes(void) +{ + qos_node_create_machine("ppc64/pseries", qos_create_machine_spapr); + qos_node_create_driver("spapr-pci-host-bridge", NULL); + qos_node_contains("ppc64/pseries", "spapr-pci-host-bridge", NULL); + qos_node_contains("spapr-pci-host-bridge", "pci-bus-spapr", NULL); +} + +libqos_init(spapr_machine_register_nodes); + diff --git a/tests/libqos/qgraph.c b/tests/libqos/qgraph.c new file mode 100644 index 0000000000..b149caaaa9 --- /dev/null +++ b/tests/libqos/qgraph.c @@ -0,0 +1,755 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/queue.h" +#include "libqos/qgraph_internal.h" +#include "libqos/qgraph.h" + +#define QGRAPH_PRINT_DEBUG 0 +#define QOS_ROOT "" +typedef struct QOSStackElement QOSStackElement; + +/* Graph Edge.*/ +struct QOSGraphEdge { + QOSEdgeType type; + char *dest; + void *arg; /* just for QEDGE_CONTAINS + * and QEDGE_CONSUMED_BY */ + char *extra_device_opts; /* added to -device option, "," is + * automatically added + */ + char *before_cmd_line; /* added before node cmd_line */ + char *after_cmd_line; /* added after -device options */ + char *edge_name; /* used by QEDGE_CONTAINS */ + QSLIST_ENTRY(QOSGraphEdge) edge_list; +}; + +typedef QSLIST_HEAD(, QOSGraphEdge) QOSGraphEdgeList; + +/** + * Stack used to keep track of the discovered path when using + * the DFS algorithm + */ +struct QOSStackElement { + QOSGraphNode *node; + QOSStackElement *parent; + QOSGraphEdge *parent_edge; + int length; +}; + +/* Each enty in these hash table will consist of pair. */ +static GHashTable *edge_table; +static GHashTable *node_table; + +/* stack used by the DFS algorithm to store the path from machine to test */ +static QOSStackElement qos_node_stack[QOS_PATH_MAX_ELEMENT_SIZE]; +static int qos_node_tos; + +/** + * add_edge(): creates an edge of type @type + * from @source to @dest node, and inserts it in the + * edges hash table + * + * Nodes @source and @dest do not necessarily need to exist. + * Possibility to add also options (see #QOSGraphEdgeOptions) + * edge->edge_name is used as identifier for get_device relationships, + * so by default is equal to @dest. + */ +static void add_edge(const char *source, const char *dest, + QOSEdgeType type, QOSGraphEdgeOptions *opts) +{ + char *key; + QOSGraphEdgeList *list = g_hash_table_lookup(edge_table, source); + QOSGraphEdgeOptions def_opts = { }; + + if (!list) { + list = g_new0(QOSGraphEdgeList, 1); + key = g_strdup(source); + g_hash_table_insert(edge_table, key, list); + } + + if (!opts) { + opts = &def_opts; + } + + QOSGraphEdge *edge = g_new0(QOSGraphEdge, 1); + edge->type = type; + edge->dest = g_strdup(dest); + edge->edge_name = g_strdup(opts->edge_name ?: dest); + edge->arg = g_memdup(opts->arg, opts->size_arg); + + edge->before_cmd_line = + opts->before_cmd_line ? g_strconcat(" ", opts->before_cmd_line, NULL) : NULL; + edge->extra_device_opts = + opts->extra_device_opts ? g_strconcat(",", opts->extra_device_opts, NULL) : NULL; + edge->after_cmd_line = + opts->after_cmd_line ? g_strconcat(" ", opts->after_cmd_line, NULL) : NULL; + + QSLIST_INSERT_HEAD(list, edge, edge_list); +} + +/* destroy_edges(): frees all edges inside a given @list */ +static void destroy_edges(void *list) +{ + QOSGraphEdge *temp; + QOSGraphEdgeList *elist = list; + + while (!QSLIST_EMPTY(elist)) { + temp = QSLIST_FIRST(elist); + QSLIST_REMOVE_HEAD(elist, edge_list); + g_free(temp->dest); + g_free(temp->before_cmd_line); + g_free(temp->after_cmd_line); + g_free(temp->extra_device_opts); + g_free(temp->edge_name); + g_free(temp->arg); + g_free(temp); + } + g_free(elist); +} + +/** + * create_node(): creates a node @name of type @type + * and inserts it to the nodes hash table. + * By default, node is not available. + */ +static QOSGraphNode *create_node(const char *name, QOSNodeType type) +{ + if (g_hash_table_lookup(node_table, name)) { + g_printerr("Node %s already created\n", name); + abort(); + } + + QOSGraphNode *node = g_new0(QOSGraphNode, 1); + node->type = type; + node->available = false; + node->name = g_strdup(name); + g_hash_table_insert(node_table, node->name, node); + return node; +} + +/** + * destroy_node(): frees a node @val from the nodes hash table. + * Note that node->name is not free'd since it will represent the + * hash table key + */ +static void destroy_node(void *val) +{ + QOSGraphNode *node = val; + g_free(node->command_line); + g_free(node); +} + +/** + * destroy_string(): frees @key from the nodes hash table. + * Actually frees the node->name + */ +static void destroy_string(void *key) +{ + g_free(key); +} + +/** + * search_node(): search for a node @key in the nodes hash table + * Returns the QOSGraphNode if found, #NULL otherwise + */ +static QOSGraphNode *search_node(const char *key) +{ + return g_hash_table_lookup(node_table, key); +} + +/** + * get_edgelist(): returns the edge list (value) assigned to + * the @key in the edge hash table. + * This list will contain all edges with source equal to @key + * + * Returns: on success: the %QOSGraphEdgeList + * otherwise: abort() + */ +static QOSGraphEdgeList *get_edgelist(const char *key) +{ + return g_hash_table_lookup(edge_table, key); +} + +/** + * search_list_edges(): search for an edge with destination @dest + * in the given @edgelist. + * + * Returns: on success: the %QOSGraphEdge + * otherwise: #NULL + */ +static QOSGraphEdge *search_list_edges(QOSGraphEdgeList *edgelist, + const char *dest) +{ + QOSGraphEdge *tmp, *next; + if (!edgelist) { + return NULL; + } + QSLIST_FOREACH_SAFE(tmp, edgelist, edge_list, next) { + if (g_strcmp0(tmp->dest, dest) == 0) { + break; + } + } + return tmp; +} + +/** + * search_machine(): search for a machine @name in the node hash + * table. A machine is the child of the root node. + * This function forces the research in the childs of the root, + * to check the node is a proper machine + * + * Returns: on success: the %QOSGraphNode + * otherwise: #NULL + */ +static QOSGraphNode *search_machine(const char *name) +{ + QOSGraphNode *n; + QOSGraphEdgeList *root_list = get_edgelist(QOS_ROOT); + QOSGraphEdge *e = search_list_edges(root_list, name); + if (!e) { + return NULL; + } + n = search_node(e->dest); + if (n->type == QNODE_MACHINE) { + return n; + } + return NULL; +} + +/** + * create_interface(): checks if there is already + * a node @node in the node hash table, if not + * creates a node @node of type #QNODE_INTERFACE + * and inserts it. If there is one, check it's + * a #QNODE_INTERFACE and abort() if it's not. + */ +static void create_interface(const char *node) +{ + QOSGraphNode *interface; + interface = search_node(node); + if (!interface) { + create_node(node, QNODE_INTERFACE); + } else if (interface->type != QNODE_INTERFACE) { + fprintf(stderr, "Error: Node %s is not an interface\n", node); + abort(); + } +} + +/** + * build_machine_cmd_line(): builds the command line for the machine + * @node. The node name must be a valid qemu identifier, since it + * will be used to build the command line. + * + * It is also possible to pass an optional @args that will be + * concatenated to the command line. + * + * For machines, prepend -M to the machine name. ", @rgs" is added + * after the -M command. + */ +static void build_machine_cmd_line(QOSGraphNode *node, const char *args) +{ + char *machine = qos_get_machine_type(node->name); + if (args) { + node->command_line = g_strconcat("-M ", machine, ",", args, NULL); + } else { + node->command_line = g_strconcat("-M ", machine, " ", NULL); + } +} + +/** + * build_driver_cmd_line(): builds the command line for the driver + * @node. The node name must be a valid qemu identifier, since it + * will be used to build the command line. + * + * Driver do not need additional command line, since it will be + * provided by the edge options. + * + * For drivers, prepend -device to the node name. + */ +static void build_driver_cmd_line(QOSGraphNode *node) +{ + node->command_line = g_strconcat(" -device ", node->name, NULL); +} + +/* qos_print_cb(): callback prints all path found by the DFS algorithm. */ +static void qos_print_cb(QOSGraphNode *path, int length) +{ + #if QGRAPH_PRINT_DEBUG + printf("%d elements\n", length); + + if (!path) { + return; + } + + while (path->path_edge) { + printf("%s ", path->name); + switch (path->path_edge->type) { + case QEDGE_PRODUCES: + printf("--PRODUCES--> "); + break; + case QEDGE_CONSUMED_BY: + printf("--CONSUMED_BY--> "); + break; + case QEDGE_CONTAINS: + printf("--CONTAINS--> "); + break; + } + path = search_node(path->path_edge->dest); + } + + printf("%s\n\n", path->name); + #endif +} + +/* qos_push(): push a node @el and edge @e in the qos_node_stack */ +static void qos_push(QOSGraphNode *el, QOSStackElement *parent, + QOSGraphEdge *e) +{ + int len = 0; /* root is not counted */ + if (qos_node_tos == QOS_PATH_MAX_ELEMENT_SIZE) { + g_printerr("QOSStack: full stack, cannot push"); + abort(); + } + + if (parent) { + len = parent->length + 1; + } + qos_node_stack[qos_node_tos++] = (QOSStackElement) { + .node = el, + .parent = parent, + .parent_edge = e, + .length = len, + }; +} + +/* qos_tos(): returns the top of stack, without popping */ +static QOSStackElement *qos_tos(void) +{ + return &qos_node_stack[qos_node_tos - 1]; +} + +/* qos_pop(): pops an element from the tos, setting it unvisited*/ +static QOSStackElement *qos_pop(void) +{ + if (qos_node_tos == 0) { + g_printerr("QOSStack: empty stack, cannot pop"); + abort(); + } + QOSStackElement *e = qos_tos(); + e->node->visited = false; + qos_node_tos--; + return e; +} + +/** + * qos_reverse_path(): reverses the found path, going from + * test-to-machine to machine-to-test + */ +static QOSGraphNode *qos_reverse_path(QOSStackElement *el) +{ + if (!el) { + return NULL; + } + + el->node->path_edge = NULL; + + while (el->parent) { + el->parent->node->path_edge = el->parent_edge; + el = el->parent; + } + + return el->node; +} + +/** + * qos_traverse_graph(): graph-walking algorithm, using Depth First Search it + * starts from the root @machine and walks all possible path until it + * reaches a test node. + * At that point, it reverses the path found and invokes the @callback. + * + * Being Depth First Search, time complexity is O(|V| + |E|), while + * space is O(|V|). In this case, the maximum stack size is set by + * QOS_PATH_MAX_ELEMENT_SIZE. + */ +static void qos_traverse_graph(QOSGraphNode *root, QOSTestCallback callback) +{ + QOSGraphNode *v, *dest_node, *path; + QOSStackElement *s_el; + QOSGraphEdge *e, *next; + QOSGraphEdgeList *list; + + qos_push(root, NULL, NULL); + + while (qos_node_tos > 0) { + s_el = qos_tos(); + v = s_el->node; + if (v->visited) { + qos_pop(); + continue; + } + v->visited = true; + list = get_edgelist(v->name); + if (!list) { + qos_pop(); + if (v->type == QNODE_TEST) { + v->visited = false; + path = qos_reverse_path(s_el); + callback(path, s_el->length); + } + } else { + QSLIST_FOREACH_SAFE(e, list, edge_list, next) { + dest_node = search_node(e->dest); + + if (!dest_node) { + fprintf(stderr, "node %s in %s -> %s does not exist\n", + e->dest, v->name, e->dest); + abort(); + } + + if (!dest_node->visited && dest_node->available) { + qos_push(dest_node, s_el, e); + } + } + } + } +} + +/* QGRAPH API*/ + +QOSGraphNode *qos_graph_get_node(const char *key) +{ + return search_node(key); +} + +bool qos_graph_has_node(const char *node) +{ + QOSGraphNode *n = search_node(node); + return n != NULL; +} + +QOSNodeType qos_graph_get_node_type(const char *node) +{ + QOSGraphNode *n = search_node(node); + if (n) { + return n->type; + } + return -1; +} + +bool qos_graph_get_node_availability(const char *node) +{ + QOSGraphNode *n = search_node(node); + if (n) { + return n->available; + } + return false; +} + +QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest) +{ + QOSGraphEdgeList *list = get_edgelist(node); + return search_list_edges(list, dest); +} + +QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge) +{ + if (!edge) { + return -1; + } + return edge->type;; +} + +char *qos_graph_edge_get_dest(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->dest; +} + +void *qos_graph_edge_get_arg(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->arg; +} + +char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->after_cmd_line; +} + +char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->before_cmd_line; +} + +char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->extra_device_opts; +} + +char *qos_graph_edge_get_name(QOSGraphEdge *edge) +{ + if (!edge) { + return NULL; + } + return edge->edge_name; +} + +bool qos_graph_has_edge(const char *start, const char *dest) +{ + QOSGraphEdgeList *list = get_edgelist(start); + QOSGraphEdge *e = search_list_edges(list, dest); + return e != NULL; +} + +QOSGraphNode *qos_graph_get_machine(const char *node) +{ + return search_machine(node); +} + +bool qos_graph_has_machine(const char *node) +{ + QOSGraphNode *m = search_machine(node); + return m != NULL; +} + +void qos_print_graph(void) +{ + qos_graph_foreach_test_path(qos_print_cb); +} + +void qos_graph_init(void) +{ + if (!node_table) { + node_table = g_hash_table_new_full(g_str_hash, g_str_equal, + destroy_string, destroy_node); + create_node(QOS_ROOT, QNODE_DRIVER); + } + + if (!edge_table) { + edge_table = g_hash_table_new_full(g_str_hash, g_str_equal, + destroy_string, destroy_edges); + } +} + +void qos_graph_destroy(void) +{ + if (node_table) { + g_hash_table_destroy(node_table); + } + + if (edge_table) { + g_hash_table_destroy(edge_table); + } + + node_table = NULL; + edge_table = NULL; +} + +void qos_node_destroy(void *key) +{ + g_hash_table_remove(node_table, key); +} + +void qos_edge_destroy(void *key) +{ + g_hash_table_remove(edge_table, key); +} + +void qos_add_test(const char *name, const char *interface, + QOSTestFunc test_func, QOSGraphTestOptions *opts) +{ + QOSGraphNode *node; + char *test_name = g_strdup_printf("%s-tests/%s", interface, name);; + QOSGraphTestOptions def_opts = { }; + + if (!opts) { + opts = &def_opts; + } + node = create_node(test_name, QNODE_TEST); + node->u.test.function = test_func; + node->u.test.arg = opts->arg; + assert(!opts->edge.arg); + assert(!opts->edge.size_arg); + + node->u.test.before = opts->before; + node->u.test.subprocess = opts->subprocess; + node->available = true; + add_edge(interface, test_name, QEDGE_CONSUMED_BY, &opts->edge); + g_free(test_name); +} + +void qos_node_create_machine(const char *name, QOSCreateMachineFunc function) +{ + qos_node_create_machine_args(name, function, NULL); +} + +void qos_node_create_machine_args(const char *name, + QOSCreateMachineFunc function, + const char *opts) +{ + QOSGraphNode *node = create_node(name, QNODE_MACHINE); + build_machine_cmd_line(node, opts); + node->u.machine.constructor = function; + add_edge(QOS_ROOT, name, QEDGE_CONTAINS, NULL); +} + +void qos_node_create_driver(const char *name, QOSCreateDriverFunc function) +{ + QOSGraphNode *node = create_node(name, QNODE_DRIVER); + build_driver_cmd_line(node); + node->u.driver.constructor = function; +} + +void qos_node_contains(const char *container, const char *contained, + ...) +{ + va_list va; + va_start(va, contained); + QOSGraphEdgeOptions *opts; + + do { + opts = va_arg(va, QOSGraphEdgeOptions *); + add_edge(container, contained, QEDGE_CONTAINS, opts); + } while (opts != NULL); + + va_end(va); +} + +void qos_node_produces(const char *producer, const char *interface) +{ + create_interface(interface); + add_edge(producer, interface, QEDGE_PRODUCES, NULL); +} + +void qos_node_consumes(const char *consumer, const char *interface, + QOSGraphEdgeOptions *opts) +{ + create_interface(interface); + add_edge(interface, consumer, QEDGE_CONSUMED_BY, opts); +} + +void qos_graph_node_set_availability(const char *node, bool av) +{ + QOSGraphEdgeList *elist; + QOSGraphNode *n = search_node(node); + QOSGraphEdge *e, *next; + if (!n) { + return; + } + n->available = av; + elist = get_edgelist(node); + if (!elist) { + return; + } + QSLIST_FOREACH_SAFE(e, elist, edge_list, next) { + if (e->type == QEDGE_CONTAINS || e->type == QEDGE_PRODUCES) { + qos_graph_node_set_availability(e->dest, av); + } + } +} + +void qos_graph_foreach_test_path(QOSTestCallback fn) +{ + QOSGraphNode *root = qos_graph_get_node(QOS_ROOT); + qos_traverse_graph(root, fn); +} + +QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts) +{ + QOSGraphObject *obj; + + g_assert(node->type == QNODE_MACHINE); + obj = node->u.machine.constructor(qts); + obj->free = g_free; + return obj; +} + +QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, + QGuestAllocator *alloc, void *arg) +{ + QOSGraphObject *obj; + + g_assert(node->type == QNODE_DRIVER); + obj = node->u.driver.constructor(parent, alloc, arg); + obj->free = g_free; + return obj; +} + +void qos_object_destroy(QOSGraphObject *obj) +{ + if (!obj) { + return; + } + if (obj->destructor) { + obj->destructor(obj); + } + if (obj->free) { + obj->free(obj); + } +} + +void qos_object_queue_destroy(QOSGraphObject *obj) +{ + g_test_queue_destroy((GDestroyNotify) qos_object_destroy, obj); +} + +void qos_object_start_hw(QOSGraphObject *obj) +{ + if (obj->start_hw) { + obj->start_hw(obj); + } +} + +char *qos_get_machine_type(char *name) +{ + while (*name != '\0' && *name != '/') { + name++; + } + + if (!*name || !name[1]) { + fprintf(stderr, "Machine name has to be of the form /\n"); + abort(); + } + + return name + 1; +} + +void qos_delete_cmd_line(const char *name) +{ + QOSGraphNode *node = search_node(name); + if (node) { + g_free(node->command_line); + node->command_line = NULL; + } +} diff --git a/tests/libqos/qgraph.h b/tests/libqos/qgraph.h new file mode 100644 index 0000000000..ef0c73837a --- /dev/null +++ b/tests/libqos/qgraph.h @@ -0,0 +1,575 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#ifndef QGRAPH_H +#define QGRAPH_H + +#include +#include +#include +#include +#include +#include "qemu/module.h" +#include "malloc.h" + +/* maximum path length */ +#define QOS_PATH_MAX_ELEMENT_SIZE 50 + +typedef struct QOSGraphObject QOSGraphObject; +typedef struct QOSGraphNode QOSGraphNode; +typedef struct QOSGraphEdge QOSGraphEdge; +typedef struct QOSGraphNodeOptions QOSGraphNodeOptions; +typedef struct QOSGraphEdgeOptions QOSGraphEdgeOptions; +typedef struct QOSGraphTestOptions QOSGraphTestOptions; + +/* Constructor for drivers, machines and test */ +typedef void *(*QOSCreateDriverFunc) (void *parent, QGuestAllocator *alloc, + void *addr); +typedef void *(*QOSCreateMachineFunc) (QTestState *qts); +typedef void (*QOSTestFunc) (void *parent, void *arg, QGuestAllocator *alloc); + +/* QOSGraphObject functions */ +typedef void *(*QOSGetDriver) (void *object, const char *interface); +typedef QOSGraphObject *(*QOSGetDevice) (void *object, const char *name); +typedef void (*QOSDestructorFunc) (QOSGraphObject *object); +typedef void (*QOSStartFunct) (QOSGraphObject *object); + +/* Test options functions */ +typedef void *(*QOSBeforeTest) (GString *cmd_line, void *arg); + +/** + * SECTION: qgraph.h + * @title: Qtest Driver Framework + * @short_description: interfaces to organize drivers and tests + * as nodes in a graph + * + * This Qgraph API provides all basic functions to create a graph + * and instantiate nodes representing machines, drivers and tests + * representing their relations with CONSUMES, PRODUCES, and CONTAINS + * edges. + * + * The idea is to have a framework where each test asks for a specific + * driver, and the framework takes care of allocating the proper devices + * required and passing the correct command line arguments to QEMU. + * + * A node can be of four types: + * - QNODE_MACHINE: for example "arm/raspi2" + * - QNODE_DRIVER: for example "generic-sdhci" + * - QNODE_INTERFACE: for example "sdhci" (interface for all "-sdhci" drivers) + * an interface is not explicitly created, it will be auto- + * matically instantiated when a node consumes or produces + * it. + * - QNODE_TEST: for example "sdhci-test", consumes an interface and tests + * the functions provided + * + * Notes for the nodes: + * - QNODE_MACHINE: each machine struct must have a QGuestAllocator and + * implement get_driver to return the allocator passing + * "memory". The function can also return NULL if the + * allocator is not set. + * - QNODE_DRIVER: driver names must be unique, and machines and nodes + * planned to be "consumed" by other nodes must match QEMU + * drivers name, otherwise they won't be discovered + * + * An edge relation between two nodes (drivers or machines) X and Y can be: + * - X CONSUMES Y: Y can be plugged into X + * - X PRODUCES Y: X provides the interface Y + * - X CONTAINS Y: Y is part of X component + * + * Basic framework steps are the following: + * - All nodes and edges are created in their respective + * machine/driver/test files + * - The framework starts QEMU and asks for a list of available devices + * and machines (note that only machines and "consumed" nodes are mapped + * 1:1 with QEMU devices) + * - The framework walks the graph starting from the available machines and + * performs a Depth First Search for tests + * - Once a test is found, the path is walked again and all drivers are + * allocated accordingly and the final interface is passed to the test + * - The test is executed + * - Unused objects are cleaned and the path discovery is continued + * + * Depending on the QEMU binary used, only some drivers/machines will be + * available and only test that are reached by them will be executed. + * + * + * Creating new driver an its interface + * + #include "libqos/qgraph.h" + + struct My_driver { + QOSGraphObject obj; + Node_produced prod; + Node_contained cont; + } + + static void my_destructor(QOSGraphObject *obj) + { + g_free(obj); + } + + static void my_get_driver(void *object, const char *interface) { + My_driver *dev = object; + if (!g_strcmp0(interface, "my_interface")) { + return &dev->prod; + } + abort(); + } + + static void my_get_device(void *object, const char *device) { + My_driver *dev = object; + if (!g_strcmp0(device, "my_driver_contained")) { + return &dev->cont; + } + abort(); + } + + static void *my_driver_constructor(void *node_consumed, + QOSGraphObject *alloc) + { + My_driver dev = g_new(My_driver, 1); + // get the node pointed by the produce edge + dev->obj.get_driver = my_get_driver; + // get the node pointed by the contains + dev->obj.get_device = my_get_device; + // free the object + dev->obj.destructor = my_destructor; + do_something_with_node_consumed(node_consumed); + // set all fields of contained device + init_contained_device(&dev->cont); + return &dev->obj; + } + + static void register_my_driver(void) + { + qos_node_create_driver("my_driver", my_driver_constructor); + // contained drivers don't need a constructor, + // they will be init by the parent. + qos_node_create_driver("my_driver_contained", NULL); + + // For the sake of this example, assume machine x86_64/pc contains + // "other_node". + // This relation, along with the machine and "other_node" creation, + // should be defined in the x86_64_pc-machine.c file. + // "my_driver" will then consume "other_node" + qos_node_contains("my_driver", "my_driver_contained"); + qos_node_produces("my_driver", "my_interface"); + qos_node_consumes("my_driver", "other_node"); + } + * + * + * + * In the above example, all possible types of relations are created: + * node "my_driver" consumes, contains and produces other nodes. + * more specifically: + * x86_64/pc -->contains--> other_node <--consumes-- my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_interface <--produces--+ + * + * or inverting the consumes edge in consumed_by: + * + * x86_64/pc -->contains--> other_node --consumed_by--> my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_interface <--produces--+ + * + * + * Creating new test + * + * #include "libqos/qgraph.h" + * + * static void my_test_function(void *obj, void *data) + * { + * Node_produced *interface_to_test = obj; + * // test interface_to_test + * } + * + * static void register_my_test(void) + * { + * qos_add_test("my_interface", "my_test", my_test_function); + * } + * + * libqos_init(register_my_test); + * + * + * + * + * Here a new test is created, consuming "my_interface" node + * and creating a valid path from a machine to a test. + * Final graph will be like this: + * x86_64/pc -->contains--> other_node <--consumes-- my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_test --consumes--> my_interface <--produces--+ + * + * or inverting the consumes edge in consumed_by: + * + * x86_64/pc -->contains--> other_node --consumed_by--> my_driver + * | + * my_driver_contained <--contains--+ + * | + * my_test <--consumed_by-- my_interface <--produces--+ + * + * Assuming there the binary is + * QTEST_QEMU_BINARY=x86_64-softmmu/qemu-system-x86_64 + * a valid test path will be: + * "/x86_64/pc/other_node/my_driver/my_interface/my_test". + * + * Additional examples are also in libqos/test-qgraph.c + * + * Command line: + * Command line is built by using node names and optional arguments + * passed by the user when building the edges. + * + * There are three types of command line arguments: + * - in node : created from the node name. For example, machines will + * have "-M " to its command line, while devices + * "-device ". It is automatically done by the + * framework. + * - after node : added as additional argument to the node name. + * This argument is added optionally when creating edges, + * by setting the parameter @after_cmd_line and + * @extra_edge_opts in #QOSGraphEdgeOptions. + * The framework automatically adds + * a comma before @extra_edge_opts, + * because it is going to add attributes + * after the destination node pointed by + * the edge containing these options, and automatically + * adds a space before @after_cmd_line, because it + * adds an additional device, not an attribute. + * - before node : added as additional argument to the node name. + * This argument is added optionally when creating edges, + * by setting the parameter @before_cmd_line in + * #QOSGraphEdgeOptions. This attribute + * is going to add attributes before the destination node + * pointed by the edge containing these options. It is + * helpful to commands that are not node-representable, + * such as "-fdsev" or "-netdev". + * + * While adding command line in edges is always used, not all nodes names are + * used in every path walk: this is because the contained or produced ones + * are already added by QEMU, so only nodes that "consumes" will be used to + * build the command line. Also, nodes that will have { "abstract" : true } + * as QMP attribute will loose their command line, since they are not proper + * devices to be added in QEMU. + * + * Example: + * + QOSGraphEdgeOptions opts = { + .arg = NULL, + .size_arg = 0, + .after_cmd_line = "-device other", + .before_cmd_line = "-netdev something", + .extra_edge_opts = "addr=04.0", + }; + QOSGraphNode * node = qos_node_create_driver("my_node", constructor); + qos_node_consumes_args("my_node", "interface", &opts); + * + * Will produce the following command line: + * "-netdev something -device my_node,addr=04.0 -device other" + */ + +/** + * Edge options to be passed to the contains/consumes *_args function. + */ +struct QOSGraphEdgeOptions { + void *arg; /* + * optional arg that will be used by + * dest edge + */ + uint32_t size_arg; /* + * optional arg size that will be used by + * dest edge + */ + const char *extra_device_opts;/* + *optional additional command line for dest + * edge, used to add additional attributes + * *after* the node command line, the + * framework automatically prepends "," + * to this argument. + */ + const char *before_cmd_line; /* + * optional additional command line for dest + * edge, used to add additional attributes + * *before* the node command line, usually + * other non-node represented commands, + * like "-fdsev synt" + */ + const char *after_cmd_line; /* + * optional extra command line to be added + * after the device command. This option + * is used to add other devices + * command line that depend on current node. + * Automatically prepends " " to this + * argument + */ + const char *edge_name; /* + * optional edge to differentiate multiple + * devices with same node name + */ +}; + +/** + * Test options to be passed to the test functions. + */ +struct QOSGraphTestOptions { + QOSGraphEdgeOptions edge; /* edge arguments that will be used by test. + * Note that test *does not* use edge_name, + * and uses instead arg and size_arg as + * data arg for its test function. + */ + void *arg; /* passed to the .before function, or to the + * test function if there is no .before + * function + */ + QOSBeforeTest before; /* executed before the test. Can add + * additional parameters to the command line + * and modify the argument to the test function. + */ + bool subprocess; /* run the test in a subprocess */ +}; + +/** + * Each driver, test or machine of this framework will have a + * QOSGraphObject as first field. + * + * This set of functions offered by QOSGraphObject are executed + * in different stages of the framework: + * - get_driver / get_device : Once a machine-to-test path has been + * found, the framework traverses it again and allocates all the + * nodes, using the provided constructor. To satisfy their relations, + * i.e. for produces or contains, where a struct constructor needs + * an external parameter represented by the previous node, + * the framework will call get_device (for contains) or + * get_driver (for produces), depending on the edge type, passing + * them the name of the next node to be taken and getting from them + * the corresponding pointer to the actual structure of the next node to + * be used in the path. + * + * - start_hw: This function is executed after all the path objects + * have been allocated, but before the test is run. It starts the hw, setting + * the initial configurations (*_device_enable) and making it ready for the + * test. + * + * - destructor: Opposite to the node constructor, destroys the object. + * This function is called after the test has been executed, and performs + * a complete cleanup of each node allocated field. In case no constructor + * is provided, no destructor will be called. + * + */ +struct QOSGraphObject { + /* for produces edges, returns void * */ + QOSGetDriver get_driver; + /* for contains edges, returns a QOSGraphObject * */ + QOSGetDevice get_device; + /* start the hw, get ready for the test */ + QOSStartFunct start_hw; + /* destroy this QOSGraphObject */ + QOSDestructorFunc destructor; + /* free the memory associated to the QOSGraphObject and its contained + * children */ + GDestroyNotify free; +}; + +/** + * qos_graph_init(): initialize the framework, creates two hash + * tables: one for the nodes and another for the edges. + */ +void qos_graph_init(void); + +/** + * qos_graph_destroy(): deallocates all the hash tables, + * freeing all nodes and edges. + */ +void qos_graph_destroy(void); + +/** + * qos_node_destroy(): removes and frees a node from the, + * nodes hash table. + */ +void qos_node_destroy(void *key); + +/** + * qos_edge_destroy(): removes and frees an edge from the, + * edges hash table. + */ +void qos_edge_destroy(void *key); + +/** + * qos_add_test(): adds a test node @name to the nodes hash table. + * + * The test will consume a @interface node, and once the + * graph walking algorithm has found it, the @test_func will be + * executed. It also has the possibility to + * add an optional @opts (see %QOSGraphNodeOptions). + * + * For tests, opts->edge.arg and size_arg represent the arg to pass + * to @test_func + */ +void qos_add_test(const char *name, const char *interface, + QOSTestFunc test_func, + QOSGraphTestOptions *opts); + +/** + * qos_node_create_machine(): creates the machine @name and + * adds it to the node hash table. + * + * This node will be of type QNODE_MACHINE and have @function + * as constructor + */ +void qos_node_create_machine(const char *name, QOSCreateMachineFunc function); + +/** + * qos_node_create_machine_args(): same as qos_node_create_machine, + * but with the possibility to add an optional ", @opts" after -M machine + * command line. + */ +void qos_node_create_machine_args(const char *name, + QOSCreateMachineFunc function, + const char *opts); + +/** + * qos_node_create_driver(): creates the driver @name and + * adds it to the node hash table. + * + * This node will be of type QNODE_DRIVER and have @function + * as constructor + */ +void qos_node_create_driver(const char *name, QOSCreateDriverFunc function); + +/** + * qos_node_contains(): creates an edge of type QEDGE_CONTAINS and + * adds it to the edge list mapped to @container in the + * edge hash table. + * + * This edge will have @container as source and @contained as destination. + * + * It also has the possibility to add optional NULL-terminated + * @opts parameters (see %QOSGraphEdgeOptions) + * + * This function can be useful when there are multiple devices + * with the same node name contained in a machine/other node + * + * For example, if "arm/raspi2" contains 2 "generic-sdhci" + * devices, the right commands will be: + * qos_node_create_machine("arm/raspi2"); + * qos_node_create_driver("generic-sdhci", constructor); + * //assume rest of the fields are set NULL + * QOSGraphEdgeOptions op1 = { .edge_name = "emmc" }; + * QOSGraphEdgeOptions op2 = { .edge_name = "sdcard" }; + * qos_node_contains("arm/raspi2", "generic-sdhci", &op1, &op2, NULL); + * + * Of course this also requires that the @container's get_device function + * should implement a case for "emmc" and "sdcard". + * + * For contains, op1.arg and op1.size_arg represent the arg to pass + * to @contained constructor to properly initialize it. + */ +void qos_node_contains(const char *container, const char *contained, ...); + +/** + * qos_node_produces(): creates an edge of type QEDGE_PRODUCES and + * adds it to the edge list mapped to @producer in the + * edge hash table. + * + * This edge will have @producer as source and @interface as destination. + */ +void qos_node_produces(const char *producer, const char *interface); + +/** + * qos_node_consumes(): creates an edge of type QEDGE_CONSUMED_BY and + * adds it to the edge list mapped to @interface in the + * edge hash table. + * + * This edge will have @interface as source and @consumer as destination. + * It also has the possibility to add an optional @opts + * (see %QOSGraphEdgeOptions) + */ +void qos_node_consumes(const char *consumer, const char *interface, + QOSGraphEdgeOptions *opts); + +/** + * qos_invalidate_command_line(): invalidates current command line, so that + * qgraph framework cannot try to cache the current command line and + * forces QEMU to restart. + */ +void qos_invalidate_command_line(void); + +/** + * qos_get_current_command_line(): return the command line required by the + * machine and driver objects. This is the same string that was passed to + * the test's "before" callback, if any. + */ +const char *qos_get_current_command_line(void); + +/** + * qos_allocate_objects(): + * @qts: The #QTestState that will be referred to by the machine object. + * @alloc: Where to store the allocator for the machine object, or %NULL. + * + * Allocate driver objects for the current test + * path, but relative to the QTestState @qts. + * + * Returns a test object just like the one that was passed to + * the test function, but relative to @qts. + */ +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc); + +/** + * qos_object_destroy(): calls the destructor for @obj + */ +void qos_object_destroy(QOSGraphObject *obj); + +/** + * qos_object_queue_destroy(): queue the destructor for @obj so that it is + * called at the end of the test + */ +void qos_object_queue_destroy(QOSGraphObject *obj); + +/** + * qos_object_start_hw(): calls the start_hw function for @obj + */ +void qos_object_start_hw(QOSGraphObject *obj); + +/** + * qos_machine_new(): instantiate a new machine node + * @node: A machine node to be instantiated + * @qts: The #QTestState that will be referred to by the machine object. + * + * Returns a machine object. + */ +QOSGraphObject *qos_machine_new(QOSGraphNode *node, QTestState *qts); + +/** + * qos_machine_new(): instantiate a new driver node + * @node: A driver node to be instantiated + * @parent: A #QOSGraphObject to be consumed by the new driver node + * @alloc: An allocator to be used by the new driver node. + * @arg: The argument for the consumed-by edge to @node. + * + * Calls the constructor for the driver object. + */ +QOSGraphObject *qos_driver_new(QOSGraphNode *node, QOSGraphObject *parent, + QGuestAllocator *alloc, void *arg); + + +#endif diff --git a/tests/libqos/qgraph_internal.h b/tests/libqos/qgraph_internal.h new file mode 100644 index 0000000000..2ef748baf6 --- /dev/null +++ b/tests/libqos/qgraph_internal.h @@ -0,0 +1,257 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#ifndef QGRAPH_EXTRA_H +#define QGRAPH_EXTRA_H + +/* This header is declaring additional helper functions defined in + * libqos/qgraph.c + * It should not be included in tests + */ + +#include "libqos/qgraph.h" + +typedef struct QOSGraphMachine QOSGraphMachine; +typedef enum QOSEdgeType QOSEdgeType; +typedef enum QOSNodeType QOSNodeType; + +/* callback called when the walk path algorithm found a + * valid path + */ +typedef void (*QOSTestCallback) (QOSGraphNode *path, int len); + +/* edge types*/ +enum QOSEdgeType { + QEDGE_CONTAINS, + QEDGE_PRODUCES, + QEDGE_CONSUMED_BY +}; + +/* node types*/ +enum QOSNodeType { + QNODE_MACHINE, + QNODE_DRIVER, + QNODE_INTERFACE, + QNODE_TEST +}; + +/* Graph Node */ +struct QOSGraphNode { + QOSNodeType type; + bool available; /* set by QEMU via QMP, used during graph walk */ + bool visited; /* used during graph walk */ + char *name; /* used to identify the node */ + char *command_line; /* used to start QEMU at test execution */ + union { + struct { + QOSCreateDriverFunc constructor; + } driver; + struct { + QOSCreateMachineFunc constructor; + } machine; + struct { + QOSTestFunc function; + void *arg; + QOSBeforeTest before; + bool subprocess; + } test; + } u; + + /** + * only used when traversing the path, never rely on that except in the + * qos_traverse_graph callback function + */ + QOSGraphEdge *path_edge; +}; + +/** + * qos_graph_get_node(): returns the node mapped to that @key. + * It performs an hash map search O(1) + * + * Returns: on success: the %QOSGraphNode + * otherwise: #NULL + */ +QOSGraphNode *qos_graph_get_node(const char *key); + +/** + * qos_graph_has_node(): returns #TRUE if the node + * has map has a node mapped to that @key. + */ +bool qos_graph_has_node(const char *node); + +/** + * qos_graph_get_node_type(): returns the %QOSNodeType + * of the node @node. + * It performs an hash map search O(1) + * Returns: on success: the %QOSNodeType + * otherwise: #-1 + */ +QOSNodeType qos_graph_get_node_type(const char *node); + +/** + * qos_graph_get_node_availability(): returns the availability (boolean) + * of the node @node. + */ +bool qos_graph_get_node_availability(const char *node); + +/** + * qos_graph_get_edge(): returns the edge + * linking of the node @node with @dest. + * + * Returns: on success: the %QOSGraphEdge + * otherwise: #NULL + */ +QOSGraphEdge *qos_graph_get_edge(const char *node, const char *dest); + +/** + * qos_graph_edge_get_type(): returns the edge type + * of the edge @edge. + * + * Returns: on success: the %QOSEdgeType + * otherwise: #-1 + */ +QOSEdgeType qos_graph_edge_get_type(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_dest(): returns the name of the node + * pointed as destination of edge @edge. + * + * Returns: on success: the destination + * otherwise: #NULL + */ +char *qos_graph_edge_get_dest(QOSGraphEdge *edge); + +/** + * qos_graph_has_edge(): returns #TRUE if there + * exists an edge from @start to @dest. + */ +bool qos_graph_has_edge(const char *start, const char *dest); + +/** + * qos_graph_edge_get_arg(): returns the args assigned + * to that @edge. + * + * Returns: on success: the arg + * otherwise: #NULL + */ +void *qos_graph_edge_get_arg(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_after_cmd_line(): returns the edge + * command line that will be added after all the node arguments + * and all the before_cmd_line arguments. + * + * Returns: on success: the char* arg + * otherwise: #NULL + */ +char *qos_graph_edge_get_after_cmd_line(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_before_cmd_line(): returns the edge + * command line that will be added before the node command + * line argument. + * + * Returns: on success: the char* arg + * otherwise: #NULL + */ +char *qos_graph_edge_get_before_cmd_line(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_extra_device_opts(): returns the arg + * command line that will be added to the node command + * line argument. + * + * Returns: on success: the char* arg + * otherwise: #NULL + */ +char *qos_graph_edge_get_extra_device_opts(QOSGraphEdge *edge); + +/** + * qos_graph_edge_get_name(): returns the name + * assigned to the destination node (different only) + * if there are multiple devices with the same node name + * e.g. a node has two "generic-sdhci", "emmc" and "sdcard" + * there will be two edges with edge_name ="emmc" and "sdcard" + * + * Returns always the char* edge_name + */ +char *qos_graph_edge_get_name(QOSGraphEdge *edge); + +/** + * qos_graph_get_machine(): returns the machine assigned + * to that @node name. + * + * It performs a search only trough the list of machines + * (i.e. the QOS_ROOT child). + * + * Returns: on success: the %QOSGraphNode + * otherwise: #NULL + */ +QOSGraphNode *qos_graph_get_machine(const char *node); + +/** + * qos_graph_has_machine(): returns #TRUE if the node + * has map has a node mapped to that @node. + */ +bool qos_graph_has_machine(const char *node); + + +/** + * qos_print_graph(): walks the graph and prints + * all machine-to-test paths. + */ +void qos_print_graph(void); + +/** + * qos_graph_foreach_test_path(): executes the Depth First search + * algorithm and applies @fn to all discovered paths. + * + * See qos_traverse_graph() in qgraph.c for more info on + * how it works. + */ +void qos_graph_foreach_test_path(QOSTestCallback fn); + +/** + * qos_get_machine_type(): return QEMU machine type for a machine node. + * This function requires every machine @name to be in the form + * /, like "arm/raspi2" or "x86_64/pc". + * + * The function will validate the format and return a pointer to + * @machine to . For example, when passed "x86_64/pc" + * it will return "pc". + * + * Note that this function *does not* allocate any new string. + */ +char *qos_get_machine_type(char *name); + +/** + * qos_delete_cmd_line(): delete the + * command line present in node mapped with key @name. + * + * This function is called when the QMP query returns a node with + * { "abstract" : true } attribute. + */ +void qos_delete_cmd_line(const char *name); + +/** + * qos_graph_node_set_availability(): sets the node identified + * by @node with availability @av. + */ +void qos_graph_node_set_availability(const char *node, bool av); + +#endif diff --git a/tests/libqos/sdhci.c b/tests/libqos/sdhci.c new file mode 100644 index 0000000000..22c33de453 --- /dev/null +++ b/tests/libqos/sdhci.c @@ -0,0 +1,163 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "pci.h" +#include "sdhci.h" +#include "hw/pci/pci.h" + +static void set_qsdhci_fields(QSDHCI *s, uint8_t version, uint8_t baseclock, + bool sdma, uint64_t reg) +{ + s->props.version = version; + s->props.baseclock = baseclock; + s->props.capab.sdma = sdma; + s->props.capab.reg = reg; +} + +/* Memory mapped implementation of QSDHCI */ + +static uint16_t sdhci_mm_readw(QSDHCI *s, uint32_t reg) +{ + QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci); + return qtest_readw(smm->qts, smm->addr + reg); +} + +static uint64_t sdhci_mm_readq(QSDHCI *s, uint32_t reg) +{ + QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci); + return qtest_readq(smm->qts, smm->addr + reg); +} + +static void sdhci_mm_writeq(QSDHCI *s, uint32_t reg, uint64_t val) +{ + QSDHCI_MemoryMapped *smm = container_of(s, QSDHCI_MemoryMapped, sdhci); + qtest_writeq(smm->qts, smm->addr + reg, val); +} + +static void *sdhci_mm_get_driver(void *obj, const char *interface) +{ + QSDHCI_MemoryMapped *smm = obj; + if (!g_strcmp0(interface, "sdhci")) { + return &smm->sdhci; + } + fprintf(stderr, "%s not present in generic-sdhci\n", interface); + g_assert_not_reached(); +} + +void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts, + uint32_t addr, QSDHCIProperties *common) +{ + sdhci->obj.get_driver = sdhci_mm_get_driver; + sdhci->sdhci.readw = sdhci_mm_readw; + sdhci->sdhci.readq = sdhci_mm_readq; + sdhci->sdhci.writeq = sdhci_mm_writeq; + memcpy(&sdhci->sdhci.props, common, sizeof(QSDHCIProperties)); + sdhci->addr = addr; + sdhci->qts = qts; +} + +/* PCI implementation of QSDHCI */ + +static uint16_t sdhci_pci_readw(QSDHCI *s, uint32_t reg) +{ + QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci); + return qpci_io_readw(&spci->dev, spci->mem_bar, reg); +} + +static uint64_t sdhci_pci_readq(QSDHCI *s, uint32_t reg) +{ + QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci); + return qpci_io_readq(&spci->dev, spci->mem_bar, reg); +} + +static void sdhci_pci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) +{ + QSDHCI_PCI *spci = container_of(s, QSDHCI_PCI, sdhci); + return qpci_io_writeq(&spci->dev, spci->mem_bar, reg, val); +} + +static void *sdhci_pci_get_driver(void *object, const char *interface) +{ + QSDHCI_PCI *spci = object; + if (!g_strcmp0(interface, "sdhci")) { + return &spci->sdhci; + } + + fprintf(stderr, "%s not present in sdhci-pci\n", interface); + g_assert_not_reached(); +} + +static void sdhci_pci_start_hw(QOSGraphObject *obj) +{ + QSDHCI_PCI *spci = (QSDHCI_PCI *)obj; + qpci_device_enable(&spci->dev); +} + +static void sdhci_destructor(QOSGraphObject *obj) +{ + QSDHCI_PCI *spci = (QSDHCI_PCI *)obj; + qpci_iounmap(&spci->dev, spci->mem_bar); +} + +static void *sdhci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QSDHCI_PCI *spci = g_new0(QSDHCI_PCI, 1); + QPCIBus *bus = pci_bus; + uint64_t barsize; + + qpci_device_init(&spci->dev, bus, addr); + spci->mem_bar = qpci_iomap(&spci->dev, 0, &barsize); + spci->sdhci.readw = sdhci_pci_readw; + spci->sdhci.readq = sdhci_pci_readq; + spci->sdhci.writeq = sdhci_pci_writeq; + set_qsdhci_fields(&spci->sdhci, 2, 0, 1, 0x057834b4); + + spci->obj.get_driver = sdhci_pci_get_driver; + spci->obj.start_hw = sdhci_pci_start_hw; + spci->obj.destructor = sdhci_destructor; + return &spci->obj; +} + +static void qsdhci_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + .vendor_id = PCI_VENDOR_ID_REDHAT, + .device_id = PCI_DEVICE_ID_REDHAT_SDHCI, + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* generic-sdhci */ + qos_node_create_driver("generic-sdhci", NULL); + qos_node_produces("generic-sdhci", "sdhci"); + + /* sdhci-pci */ + add_qpci_address(&opts, &addr); + qos_node_create_driver("sdhci-pci", sdhci_pci_create); + qos_node_produces("sdhci-pci", "sdhci"); + qos_node_consumes("sdhci-pci", "pci-bus", &opts); + +} + +libqos_init(qsdhci_register_nodes); diff --git a/tests/libqos/sdhci.h b/tests/libqos/sdhci.h new file mode 100644 index 0000000000..032d815c38 --- /dev/null +++ b/tests/libqos/sdhci.h @@ -0,0 +1,70 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#ifndef QGRAPH_QSDHCI +#define QGRAPH_QSDHCI + +#include "libqos/qgraph.h" +#include "pci.h" + +typedef struct QSDHCI QSDHCI; +typedef struct QSDHCI_MemoryMapped QSDHCI_MemoryMapped; +typedef struct QSDHCI_PCI QSDHCI_PCI; +typedef struct QSDHCIProperties QSDHCIProperties; + +/* Properties common to all QSDHCI devices */ +struct QSDHCIProperties { + uint8_t version; + uint8_t baseclock; + struct { + bool sdma; + uint64_t reg; + } capab; +}; + +struct QSDHCI { + uint16_t (*readw)(QSDHCI *s, uint32_t reg); + uint64_t (*readq)(QSDHCI *s, uint32_t reg); + void (*writeq)(QSDHCI *s, uint32_t reg, uint64_t val); + QSDHCIProperties props; +}; + +/* Memory Mapped implementation of QSDHCI */ +struct QSDHCI_MemoryMapped { + QOSGraphObject obj; + QTestState *qts; + QSDHCI sdhci; + uint64_t addr; +}; + +/* PCI implementation of QSDHCI */ +struct QSDHCI_PCI { + QOSGraphObject obj; + QPCIDevice dev; + QSDHCI sdhci; + QPCIBar mem_bar; +}; + +/** + * qos_init_sdhci_mm(): external constructor used by all drivers/machines + * that "contain" a #QSDHCI_MemoryMapped driver + */ +void qos_init_sdhci_mm(QSDHCI_MemoryMapped *sdhci, QTestState *qts, + uint32_t addr, QSDHCIProperties *common); + +#endif diff --git a/tests/libqos/tpci200.c b/tests/libqos/tpci200.c new file mode 100644 index 0000000000..98dc532933 --- /dev/null +++ b/tests/libqos/tpci200.c @@ -0,0 +1,65 @@ +/* + * QTest testcase for tpci200 PCI-IndustryPack bridge + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * + * 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 "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +typedef struct QTpci200 QTpci200; +typedef struct QIpack QIpack; + +struct QIpack { + +}; +struct QTpci200 { + QOSGraphObject obj; + QPCIDevice dev; + QIpack ipack; +}; + +/* tpci200 */ +static void *tpci200_get_driver(void *obj, const char *interface) +{ + QTpci200 *tpci200 = obj; + if (!g_strcmp0(interface, "ipack")) { + return &tpci200->ipack; + } + if (!g_strcmp0(interface, "pci-device")) { + return &tpci200->dev; + } + + fprintf(stderr, "%s not present in tpci200\n", interface); + g_assert_not_reached(); +} + +static void *tpci200_create(void *pci_bus, QGuestAllocator *alloc, void *addr) +{ + QTpci200 *tpci200 = g_new0(QTpci200, 1); + QPCIBus *bus = pci_bus; + + qpci_device_init(&tpci200->dev, bus, addr); + tpci200->obj.get_driver = tpci200_get_driver; + return &tpci200->obj; +} + +static void tpci200_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=ipack0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("tpci200", tpci200_create); + qos_node_consumes("tpci200", "pci-bus", &opts); + qos_node_produces("tpci200", "ipack"); + qos_node_produces("tpci200", "pci-device"); +} + +libqos_init(tpci200_register_nodes); diff --git a/tests/libqos/usb.c b/tests/libqos/usb.c index 2a476049a8..49e2f4bc0a 100644 --- a/tests/libqos/usb.c +++ b/tests/libqos/usb.c @@ -37,13 +37,14 @@ void uhci_port_test(struct qhc *hc, int port, uint16_t expect) g_assert((value & mask) == (expect & mask)); } -void usb_test_hotplug(const char *hcd_id, const int port, +void usb_test_hotplug(const char *hcd_id, const char *port, void (*port_check)(void)) { - char *id = g_strdup_printf("usbdev%d", port); + char *id = g_strdup_printf("usbdev%s", port); + char *bus = g_strdup_printf("%s.0", hcd_id); - qtest_qmp_device_add("usb-tablet", id, "'port': '%d', 'bus': '%s.0'", - port, hcd_id); + qtest_qmp_device_add("usb-tablet", id, "{'port': %s, 'bus': %s}", + port, bus); if (port_check) { port_check(); @@ -51,5 +52,6 @@ void usb_test_hotplug(const char *hcd_id, const int port, qtest_qmp_device_del(id); + g_free(bus); g_free(id); } diff --git a/tests/libqos/usb.h b/tests/libqos/usb.h index 297cfc564d..c506418a13 100644 --- a/tests/libqos/usb.h +++ b/tests/libqos/usb.h @@ -13,6 +13,6 @@ void qusb_pci_init_one(QPCIBus *pcibus, struct qhc *hc, void uhci_port_test(struct qhc *hc, int port, uint16_t expect); void uhci_deinit(struct qhc *hc); -void usb_test_hotplug(const char *bus_name, const int port, +void usb_test_hotplug(const char *bus_name, const char *port, void (*port_check)(void)); #endif diff --git a/tests/libqos/virtio-9p.c b/tests/libqos/virtio-9p.c new file mode 100644 index 0000000000..a378b56fa9 --- /dev/null +++ b/tests/libqos/virtio-9p.c @@ -0,0 +1,173 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_ids.h" +#include "libqos/virtio-9p.h" +#include "libqos/qgraph.h" + +static QGuestAllocator *alloc; + +static void virtio_9p_cleanup(QVirtio9P *interface) +{ + qvirtqueue_cleanup(interface->vdev->bus, interface->vq, alloc); +} + +static void virtio_9p_setup(QVirtio9P *interface) +{ + interface->vq = qvirtqueue_setup(interface->vdev, alloc, 0); + qvirtio_set_driver_ok(interface->vdev); +} + +/* virtio-9p-device */ +static void virtio_9p_device_destructor(QOSGraphObject *obj) +{ + QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj; + QVirtio9P *v9p = &v_9p->v9p; + + virtio_9p_cleanup(v9p); +} + +static void virtio_9p_device_start_hw(QOSGraphObject *obj) +{ + QVirtio9PDevice *v_9p = (QVirtio9PDevice *) obj; + QVirtio9P *v9p = &v_9p->v9p; + + virtio_9p_setup(v9p); +} + +static void *virtio_9p_get_driver(QVirtio9P *v_9p, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-9p")) { + return v_9p; + } + if (!g_strcmp0(interface, "virtio")) { + return v_9p->vdev; + } + + fprintf(stderr, "%s not present in virtio-9p-device\n", interface); + g_assert_not_reached(); +} + +static void *virtio_9p_device_get_driver(void *object, const char *interface) +{ + QVirtio9PDevice *v_9p = object; + return virtio_9p_get_driver(&v_9p->v9p, interface); +} + +static void *virtio_9p_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtio9PDevice *virtio_device = g_new0(QVirtio9PDevice, 1); + QVirtio9P *interface = &virtio_device->v9p; + + interface->vdev = virtio_dev; + alloc = t_alloc; + + virtio_device->obj.destructor = virtio_9p_device_destructor; + virtio_device->obj.get_driver = virtio_9p_device_get_driver; + virtio_device->obj.start_hw = virtio_9p_device_start_hw; + + return &virtio_device->obj; +} + +/* virtio-9p-pci */ +static void virtio_9p_pci_destructor(QOSGraphObject *obj) +{ + QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj; + QVirtio9P *interface = &v9_pci->v9p; + QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj; + + virtio_9p_cleanup(interface); + qvirtio_pci_destructor(pci_vobj); +} + +static void virtio_9p_pci_start_hw(QOSGraphObject *obj) +{ + QVirtio9PPCI *v9_pci = (QVirtio9PPCI *) obj; + QVirtio9P *interface = &v9_pci->v9p; + QOSGraphObject *pci_vobj = &v9_pci->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_9p_setup(interface); +} + +static void *virtio_9p_pci_get_driver(void *object, const char *interface) +{ + QVirtio9PPCI *v_9p = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_9p->pci_vdev.pdev; + } + return virtio_9p_get_driver(&v_9p->v9p, interface); +} + +static void *virtio_9p_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtio9PPCI *v9_pci = g_new0(QVirtio9PPCI, 1); + QVirtio9P *interface = &v9_pci->v9p; + QOSGraphObject *obj = &v9_pci->pci_vdev.obj; + + virtio_pci_init(&v9_pci->pci_vdev, pci_bus, addr); + interface->vdev = &v9_pci->pci_vdev.vdev; + alloc = t_alloc; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_9P); + + obj->destructor = virtio_9p_pci_destructor; + obj->start_hw = virtio_9p_pci_start_hw; + obj->get_driver = virtio_9p_pci_get_driver; + + return obj; +} + +static void virtio_9p_register_nodes(void) +{ + const char *str_simple = "fsdev=fsdev0,mount_tag=" MOUNT_TAG; + const char *str_addr = "fsdev=fsdev0,addr=04.0,mount_tag=" MOUNT_TAG; + + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .before_cmd_line = "-fsdev synth,id=fsdev0", + }; + + /* virtio-9p-device */ + opts.extra_device_opts = str_simple, + qos_node_create_driver("virtio-9p-device", virtio_9p_device_create); + qos_node_consumes("virtio-9p-device", "virtio-bus", &opts); + qos_node_produces("virtio-9p-device", "virtio"); + qos_node_produces("virtio-9p-device", "virtio-9p"); + + /* virtio-9p-pci */ + opts.extra_device_opts = str_addr; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-9p-pci", virtio_9p_pci_create); + qos_node_consumes("virtio-9p-pci", "pci-bus", &opts); + qos_node_produces("virtio-9p-pci", "pci-device"); + qos_node_produces("virtio-9p-pci", "virtio"); + qos_node_produces("virtio-9p-pci", "virtio-9p"); + +} + +libqos_init(virtio_9p_register_nodes); diff --git a/tests/libqos/virtio-9p.h b/tests/libqos/virtio-9p.h new file mode 100644 index 0000000000..dba22772b5 --- /dev/null +++ b/tests/libqos/virtio-9p.h @@ -0,0 +1,42 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtio9P QVirtio9P; +typedef struct QVirtio9PPCI QVirtio9PPCI; +typedef struct QVirtio9PDevice QVirtio9PDevice; + +#define MOUNT_TAG "qtest" + +struct QVirtio9P { + QVirtioDevice *vdev; + QVirtQueue *vq; +}; + +struct QVirtio9PPCI { + QVirtioPCIDevice pci_vdev; + QVirtio9P v9p; +}; + +struct QVirtio9PDevice { + QOSGraphObject obj; + QVirtio9P v9p; +}; diff --git a/tests/libqos/virtio-balloon.c b/tests/libqos/virtio-balloon.c new file mode 100644 index 0000000000..7e6e9e9de5 --- /dev/null +++ b/tests/libqos/virtio-balloon.c @@ -0,0 +1,113 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-balloon.h" + +/* virtio-balloon-device */ +static void *qvirtio_balloon_get_driver(QVirtioBalloon *v_balloon, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-balloon")) { + return v_balloon; + } + if (!g_strcmp0(interface, "virtio")) { + return v_balloon->vdev; + } + + fprintf(stderr, "%s not present in virtio-balloon-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_balloon_device_get_driver(void *object, + const char *interface) +{ + QVirtioBalloonDevice *v_balloon = object; + return qvirtio_balloon_get_driver(&v_balloon->balloon, interface); +} + +static void *virtio_balloon_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBalloonDevice *virtio_bdevice = g_new0(QVirtioBalloonDevice, 1); + QVirtioBalloon *interface = &virtio_bdevice->balloon; + + interface->vdev = virtio_dev; + + virtio_bdevice->obj.get_driver = qvirtio_balloon_device_get_driver; + + return &virtio_bdevice->obj; +} + +/* virtio-balloon-pci */ +static void *qvirtio_balloon_pci_get_driver(void *object, + const char *interface) +{ + QVirtioBalloonPCI *v_balloon = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_balloon->pci_vdev.pdev; + } + return qvirtio_balloon_get_driver(&v_balloon->balloon, interface); +} + +static void *virtio_balloon_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBalloonPCI *virtio_bpci = g_new0(QVirtioBalloonPCI, 1); + QVirtioBalloon *interface = &virtio_bpci->balloon; + QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj; + + + virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_bpci->pci_vdev.vdev; + + obj->get_driver = qvirtio_balloon_pci_get_driver; + + return obj; +} + +static void virtio_balloon_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* virtio-balloon-device */ + qos_node_create_driver("virtio-balloon-device", + virtio_balloon_device_create); + qos_node_consumes("virtio-balloon-device", "virtio-bus", NULL); + qos_node_produces("virtio-balloon-device", "virtio"); + qos_node_produces("virtio-balloon-device", "virtio-balloon"); + + /* virtio-balloon-pci */ + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-balloon-pci", virtio_balloon_pci_create); + qos_node_consumes("virtio-balloon-pci", "pci-bus", &opts); + qos_node_produces("virtio-balloon-pci", "pci-device"); + qos_node_produces("virtio-balloon-pci", "virtio"); + qos_node_produces("virtio-balloon-pci", "virtio-balloon"); +} + +libqos_init(virtio_balloon_register_nodes); diff --git a/tests/libqos/virtio-balloon.h b/tests/libqos/virtio-balloon.h new file mode 100644 index 0000000000..e8066c42bb --- /dev/null +++ b/tests/libqos/virtio-balloon.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioBalloon QVirtioBalloon; +typedef struct QVirtioBalloonPCI QVirtioBalloonPCI; +typedef struct QVirtioBalloonDevice QVirtioBalloonDevice; + +struct QVirtioBalloon { + QVirtioDevice *vdev; +}; + +struct QVirtioBalloonPCI { + QVirtioPCIDevice pci_vdev; + QVirtioBalloon balloon; +}; + +struct QVirtioBalloonDevice { + QOSGraphObject obj; + QVirtioBalloon balloon; +}; diff --git a/tests/libqos/virtio-blk.c b/tests/libqos/virtio-blk.c new file mode 100644 index 0000000000..c17bdf4217 --- /dev/null +++ b/tests/libqos/virtio-blk.c @@ -0,0 +1,124 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_blk.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-blk.h" + +#define PCI_SLOT 0x04 +#define PCI_FN 0x00 + +/* virtio-blk-device */ +static void *qvirtio_blk_get_driver(QVirtioBlk *v_blk, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-blk")) { + return v_blk; + } + if (!g_strcmp0(interface, "virtio")) { + return v_blk->vdev; + } + + fprintf(stderr, "%s not present in virtio-blk-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_blk_device_get_driver(void *object, + const char *interface) +{ + QVirtioBlkDevice *v_blk = object; + return qvirtio_blk_get_driver(&v_blk->blk, interface); +} + +static void *virtio_blk_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBlkDevice *virtio_blk = g_new0(QVirtioBlkDevice, 1); + QVirtioBlk *interface = &virtio_blk->blk; + + interface->vdev = virtio_dev; + + virtio_blk->obj.get_driver = qvirtio_blk_device_get_driver; + + return &virtio_blk->obj; +} + +/* virtio-blk-pci */ +static void *qvirtio_blk_pci_get_driver(void *object, const char *interface) +{ + QVirtioBlkPCI *v_blk = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_blk->pci_vdev.pdev; + } + return qvirtio_blk_get_driver(&v_blk->blk, interface); +} + +static void *virtio_blk_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioBlkPCI *virtio_blk = g_new0(QVirtioBlkPCI, 1); + QVirtioBlk *interface = &virtio_blk->blk; + QOSGraphObject *obj = &virtio_blk->pci_vdev.obj; + + virtio_pci_init(&virtio_blk->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_blk->pci_vdev.vdev; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_BLOCK); + + obj->get_driver = qvirtio_blk_pci_get_driver; + + return obj; +} + +static void virtio_blk_register_nodes(void) +{ + /* FIXME: every test using these two nodes needs to setup a + * -drive,id=drive0 otherwise QEMU is not going to start. + * Therefore, we do not include "produces" edge for virtio + * and pci-device yet. + */ + + char *arg = g_strdup_printf("id=drv0,drive=drive0,addr=%x.%x", + PCI_SLOT, PCI_FN); + + QPCIAddress addr = { + .devfn = QPCI_DEVFN(PCI_SLOT, PCI_FN), + }; + + QOSGraphEdgeOptions opts = { }; + + /* virtio-blk-device */ + opts.extra_device_opts = "drive=drive0"; + qos_node_create_driver("virtio-blk-device", virtio_blk_device_create); + qos_node_consumes("virtio-blk-device", "virtio-bus", &opts); + qos_node_produces("virtio-blk-device", "virtio-blk"); + + /* virtio-blk-pci */ + opts.extra_device_opts = arg; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-blk-pci", virtio_blk_pci_create); + qos_node_consumes("virtio-blk-pci", "pci-bus", &opts); + qos_node_produces("virtio-blk-pci", "virtio-blk"); + + g_free(arg); +} + +libqos_init(virtio_blk_register_nodes); diff --git a/tests/libqos/virtio-blk.h b/tests/libqos/virtio-blk.h new file mode 100644 index 0000000000..dc258496ba --- /dev/null +++ b/tests/libqos/virtio-blk.h @@ -0,0 +1,40 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioBlk QVirtioBlk; +typedef struct QVirtioBlkPCI QVirtioBlkPCI; +typedef struct QVirtioBlkDevice QVirtioBlkDevice; + +/* virtqueue is created in each test */ +struct QVirtioBlk { + QVirtioDevice *vdev; +}; + +struct QVirtioBlkPCI { + QVirtioPCIDevice pci_vdev; + QVirtioBlk blk; +}; + +struct QVirtioBlkDevice { + QOSGraphObject obj; + QVirtioBlk blk; +}; diff --git a/tests/libqos/virtio-mmio.c b/tests/libqos/virtio-mmio.c index 7aa8383338..3678c07ef0 100644 --- a/tests/libqos/virtio-mmio.c +++ b/tests/libqos/virtio-mmio.c @@ -12,74 +12,74 @@ #include "libqos/virtio.h" #include "libqos/virtio-mmio.h" #include "libqos/malloc.h" -#include "libqos/malloc-generic.h" +#include "libqos/qgraph.h" #include "standard-headers/linux/virtio_ring.h" static uint8_t qvirtio_mmio_config_readb(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readb(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readb(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint16_t qvirtio_mmio_config_readw(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readw(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readw(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_config_readl(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readl(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint64_t qvirtio_mmio_config_readq(QVirtioDevice *d, uint64_t off) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return readq(dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return qtest_readq(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_SPECIFIC + off); } static uint32_t qvirtio_mmio_get_features(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); - return readl(dev->addr + QVIRTIO_MMIO_HOST_FEATURES); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES_SEL, 0); + return qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_HOST_FEATURES); } static void qvirtio_mmio_set_features(QVirtioDevice *d, uint32_t features) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); dev->features = features; - writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); - writel(dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES_SEL, 0); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_GUEST_FEATURES, features); } static uint32_t qvirtio_mmio_get_guest_features(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); return dev->features; } static uint8_t qvirtio_mmio_get_status(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return (uint8_t)readl(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return (uint8_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS); } static void qvirtio_mmio_set_status(QVirtioDevice *d, uint8_t status) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_DEVICE_STATUS, (uint32_t)status); } static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); uint32_t isr; - isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; + isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 1; if (isr != 0) { - writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 1); return true; } @@ -88,12 +88,12 @@ static bool qvirtio_mmio_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); uint32_t isr; - isr = readl(dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; + isr = qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_STATUS) & 2; if (isr != 0) { - writel(dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_INTERRUPT_ACK, 2); return true; } @@ -102,34 +102,34 @@ static bool qvirtio_mmio_get_config_isr_status(QVirtioDevice *d) static void qvirtio_mmio_queue_select(QVirtioDevice *d, uint16_t index) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_SEL, (uint32_t)index); - g_assert_cmphex(readl(dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); + g_assert_cmphex(qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN), ==, 0); } static uint16_t qvirtio_mmio_get_queue_size(QVirtioDevice *d) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - return (uint16_t)readl(dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + return (uint16_t)qtest_readl(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM_MAX); } static void qvirtio_mmio_set_queue_address(QVirtioDevice *d, uint32_t pfn) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_PFN, pfn); } static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, QGuestAllocator *alloc, uint16_t index) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); QVirtQueue *vq; uint64_t addr; vq = g_malloc0(sizeof(*vq)); qvirtio_mmio_queue_select(d, index); - writel(dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_ALIGN, dev->page_size); vq->index = index; vq->size = qvirtio_mmio_get_queue_size(d); @@ -139,7 +139,7 @@ static QVirtQueue *qvirtio_mmio_virtqueue_setup(QVirtioDevice *d, vq->indirect = (dev->features & (1u << VIRTIO_RING_F_INDIRECT_DESC)) != 0; vq->event = (dev->features & (1u << VIRTIO_RING_F_EVENT_IDX)) != 0; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NUM, vq->size); /* Check different than 0 */ g_assert_cmpint(vq->size, !=, 0); @@ -163,8 +163,8 @@ static void qvirtio_mmio_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_mmio_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioMMIODevice *dev = (QVirtioMMIODevice *)d; - writel(dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); + QVirtioMMIODevice *dev = container_of(d, QVirtioMMIODevice, vdev); + qtest_writel(dev->qts, dev->addr + QVIRTIO_MMIO_QUEUE_NOTIFY, vq->index); } const QVirtioBus qvirtio_mmio = { @@ -187,21 +187,45 @@ const QVirtioBus qvirtio_mmio = { .virtqueue_kick = qvirtio_mmio_virtqueue_kick, }; -QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size) +static void *qvirtio_mmio_get_driver(void *obj, const char *interface) { - QVirtioMMIODevice *dev; - uint32_t magic; - dev = g_malloc0(sizeof(*dev)); + QVirtioMMIODevice *virtio_mmio = obj; + if (!g_strcmp0(interface, "virtio-bus")) { + return &virtio_mmio->vdev; + } + fprintf(stderr, "%s not present in virtio-mmio\n", interface); + g_assert_not_reached(); +} - magic = readl(addr + QVIRTIO_MMIO_MAGIC_VALUE); +static void qvirtio_mmio_start_hw(QOSGraphObject *obj) +{ + QVirtioMMIODevice *dev = (QVirtioMMIODevice *) obj; + qvirtio_start_device(&dev->vdev); +} + +void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts, + uint64_t addr, uint32_t page_size) +{ + uint32_t magic; + magic = qtest_readl(qts, addr + QVIRTIO_MMIO_MAGIC_VALUE); g_assert(magic == ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)); + dev->qts = qts; dev->addr = addr; dev->page_size = page_size; - dev->vdev.device_type = readl(addr + QVIRTIO_MMIO_DEVICE_ID); + dev->vdev.device_type = qtest_readl(qts, addr + QVIRTIO_MMIO_DEVICE_ID); dev->vdev.bus = &qvirtio_mmio; - writel(addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); + qtest_writel(qts, addr + QVIRTIO_MMIO_GUEST_PAGE_SIZE, page_size); - return dev; + dev->obj.get_driver = qvirtio_mmio_get_driver; + dev->obj.start_hw = qvirtio_mmio_start_hw; } + +static void virtio_mmio_register_nodes(void) +{ + qos_node_create_driver("virtio-mmio", NULL); + qos_node_produces("virtio-mmio", "virtio-bus"); +} + +libqos_init(virtio_mmio_register_nodes); diff --git a/tests/libqos/virtio-mmio.h b/tests/libqos/virtio-mmio.h index e3e52b9ce1..17a17141c3 100644 --- a/tests/libqos/virtio-mmio.h +++ b/tests/libqos/virtio-mmio.h @@ -11,6 +11,7 @@ #define LIBQOS_VIRTIO_MMIO_H #include "libqos/virtio.h" +#include "libqos/qgraph.h" #define QVIRTIO_MMIO_MAGIC_VALUE 0x000 #define QVIRTIO_MMIO_VERSION 0x004 @@ -33,7 +34,9 @@ #define QVIRTIO_MMIO_DEVICE_SPECIFIC 0x100 typedef struct QVirtioMMIODevice { + QOSGraphObject obj; QVirtioDevice vdev; + QTestState *qts; uint64_t addr; uint32_t page_size; uint32_t features; /* As it cannot be read later, save it */ @@ -41,6 +44,7 @@ typedef struct QVirtioMMIODevice { extern const QVirtioBus qvirtio_mmio; -QVirtioMMIODevice *qvirtio_mmio_init_device(uint64_t addr, uint32_t page_size); +void qvirtio_mmio_init_device(QVirtioMMIODevice *dev, QTestState *qts, + uint64_t addr, uint32_t page_size); #endif diff --git a/tests/libqos/virtio-net.c b/tests/libqos/virtio-net.c new file mode 100644 index 0000000000..61c56170e9 --- /dev/null +++ b/tests/libqos/virtio-net.c @@ -0,0 +1,195 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-net.h" +#include "hw/virtio/virtio-net.h" + + +static QGuestAllocator *alloc; + +static void virtio_net_cleanup(QVirtioNet *interface) +{ + int i; + + for (i = 0; i < interface->n_queues; i++) { + qvirtqueue_cleanup(interface->vdev->bus, interface->queues[i], alloc); + } + g_free(interface->queues); +} + +static void virtio_net_setup(QVirtioNet *interface) +{ + QVirtioDevice *vdev = interface->vdev; + uint64_t features; + int i; + + features = qvirtio_get_features(vdev); + features &= ~(QVIRTIO_F_BAD_FEATURE | + (1u << VIRTIO_RING_F_INDIRECT_DESC) | + (1u << VIRTIO_RING_F_EVENT_IDX)); + qvirtio_set_features(vdev, features); + + if (features & (1u << VIRTIO_NET_F_MQ)) { + interface->n_queues = qvirtio_config_readw(vdev, 8) * 2; + } else { + interface->n_queues = 2; + } + + interface->queues = g_new(QVirtQueue *, interface->n_queues); + for (i = 0; i < interface->n_queues; i++) { + interface->queues[i] = qvirtqueue_setup(vdev, alloc, i); + } + qvirtio_set_driver_ok(vdev); +} + +/* virtio-net-device */ +static void qvirtio_net_device_destructor(QOSGraphObject *obj) +{ + QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj; + virtio_net_cleanup(&v_net->net); +} + +static void qvirtio_net_device_start_hw(QOSGraphObject *obj) +{ + QVirtioNetDevice *v_net = (QVirtioNetDevice *) obj; + QVirtioNet *interface = &v_net->net; + + virtio_net_setup(interface); +} + +static void *qvirtio_net_get_driver(QVirtioNet *v_net, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-net")) { + return v_net; + } + if (!g_strcmp0(interface, "virtio")) { + return v_net->vdev; + } + + fprintf(stderr, "%s not present in virtio-net-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_net_device_get_driver(void *object, + const char *interface) +{ + QVirtioNetDevice *v_net = object; + return qvirtio_net_get_driver(&v_net->net, interface); +} + +static void *virtio_net_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioNetDevice *virtio_ndevice = g_new0(QVirtioNetDevice, 1); + QVirtioNet *interface = &virtio_ndevice->net; + + interface->vdev = virtio_dev; + alloc = t_alloc; + + virtio_ndevice->obj.destructor = qvirtio_net_device_destructor; + virtio_ndevice->obj.get_driver = qvirtio_net_device_get_driver; + virtio_ndevice->obj.start_hw = qvirtio_net_device_start_hw; + + return &virtio_ndevice->obj; +} + +/* virtio-net-pci */ +static void qvirtio_net_pci_destructor(QOSGraphObject *obj) +{ + QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj; + QVirtioNet *interface = &v_net->net; + QOSGraphObject *pci_vobj = &v_net->pci_vdev.obj; + + virtio_net_cleanup(interface); + qvirtio_pci_destructor(pci_vobj); +} + +static void qvirtio_net_pci_start_hw(QOSGraphObject *obj) +{ + QVirtioNetPCI *v_net = (QVirtioNetPCI *) obj; + QVirtioNet *interface = &v_net->net; + QOSGraphObject *pci_vobj = &v_net->pci_vdev.obj; + + qvirtio_pci_start_hw(pci_vobj); + virtio_net_setup(interface); +} + +static void *qvirtio_net_pci_get_driver(void *object, + const char *interface) +{ + QVirtioNetPCI *v_net = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_net->pci_vdev.pdev; + } + return qvirtio_net_get_driver(&v_net->net, interface); +} + +static void *virtio_net_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioNetPCI *virtio_bpci = g_new0(QVirtioNetPCI, 1); + QVirtioNet *interface = &virtio_bpci->net; + QOSGraphObject *obj = &virtio_bpci->pci_vdev.obj; + + virtio_pci_init(&virtio_bpci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_bpci->pci_vdev.vdev; + alloc = t_alloc; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_NET); + + obj->destructor = qvirtio_net_pci_destructor; + obj->start_hw = qvirtio_net_pci_start_hw; + obj->get_driver = qvirtio_net_pci_get_driver; + + return obj; +} + +static void virtio_net_register_nodes(void) +{ + /* FIXME: every test using these nodes needs to setup a + * -netdev socket,id=hs0 otherwise QEMU is not going to start. + * Therefore, we do not include "produces" edge for virtio + * and pci-device yet. + */ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { }; + + /* virtio-net-device */ + opts.extra_device_opts = "netdev=hs0"; + qos_node_create_driver("virtio-net-device", + virtio_net_device_create); + qos_node_consumes("virtio-net-device", "virtio-bus", &opts); + qos_node_produces("virtio-net-device", "virtio-net"); + + /* virtio-net-pci */ + opts.extra_device_opts = "netdev=hs0,addr=04.0"; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-net-pci", virtio_net_pci_create); + qos_node_consumes("virtio-net-pci", "pci-bus", &opts); + qos_node_produces("virtio-net-pci", "virtio-net"); +} + +libqos_init(virtio_net_register_nodes); diff --git a/tests/libqos/virtio-net.h b/tests/libqos/virtio-net.h new file mode 100644 index 0000000000..28238a1b20 --- /dev/null +++ b/tests/libqos/virtio-net.h @@ -0,0 +1,41 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioNet QVirtioNet; +typedef struct QVirtioNetPCI QVirtioNetPCI; +typedef struct QVirtioNetDevice QVirtioNetDevice; + +struct QVirtioNet { + QVirtioDevice *vdev; + int n_queues; + QVirtQueue **queues; +}; + +struct QVirtioNetPCI { + QVirtioPCIDevice pci_vdev; + QVirtioNet net; +}; + +struct QVirtioNetDevice { + QOSGraphObject obj; + QVirtioNet net; +}; diff --git a/tests/libqos/virtio-pci.c b/tests/libqos/virtio-pci.c index 550dede0a2..993d347830 100644 --- a/tests/libqos/virtio-pci.c +++ b/tests/libqos/virtio-pci.c @@ -15,68 +15,39 @@ #include "libqos/pci-pc.h" #include "libqos/malloc.h" #include "libqos/malloc-pc.h" +#include "libqos/qgraph.h" #include "standard-headers/linux/virtio_ring.h" #include "standard-headers/linux/virtio_pci.h" #include "hw/pci/pci.h" #include "hw/pci/pci_regs.h" -typedef struct QVirtioPCIForeachData { - void (*func)(QVirtioDevice *d, void *data); - uint16_t device_type; - bool has_slot; - int slot; - void *user_data; -} QVirtioPCIForeachData; +/* virtio-pci is a superclass of all virtio-xxx-pci devices; + * the relation between virtio-pci and virtio-xxx-pci is implicit, + * and therefore virtio-pci does not produce virtio and is not + * reached by any edge, not even as a "contains" edge. + * In facts, every device is a QVirtioPCIDevice with + * additional fields, since every one has its own + * number of queues and various attributes. + * Virtio-pci provides default functions to start the + * hw and destroy the object, and nodes that want to + * override them should always remember to call the + * original qvirtio_pci_destructor and qvirtio_pci_start_hw. + */ -void qvirtio_pci_device_free(QVirtioPCIDevice *dev) +static inline bool qvirtio_pci_is_big_endian(QVirtioPCIDevice *dev) { - g_free(dev->pdev); - g_free(dev); -} + QPCIBus *bus = dev->pdev->bus; -static QVirtioPCIDevice *qpcidevice_to_qvirtiodevice(QPCIDevice *pdev) -{ - QVirtioPCIDevice *vpcidev; - vpcidev = g_malloc0(sizeof(*vpcidev)); - - if (pdev) { - vpcidev->pdev = pdev; - vpcidev->vdev.device_type = - qpci_config_readw(vpcidev->pdev, PCI_SUBSYSTEM_ID); - } - - vpcidev->config_msix_entry = -1; - - return vpcidev; -} - -static void qvirtio_pci_foreach_callback( - QPCIDevice *dev, int devfn, void *data) -{ - QVirtioPCIForeachData *d = data; - QVirtioPCIDevice *vpcidev = qpcidevice_to_qvirtiodevice(dev); - - if (vpcidev->vdev.device_type == d->device_type && - (!d->has_slot || vpcidev->pdev->devfn == d->slot << 3)) { - d->func(&vpcidev->vdev, d->user_data); - } else { - qvirtio_pci_device_free(vpcidev); - } -} - -static void qvirtio_pci_assign_device(QVirtioDevice *d, void *data) -{ - QVirtioPCIDevice **vpcidev = data; - assert(!*vpcidev); - *vpcidev = (QVirtioPCIDevice *)d; + /* FIXME: virtio 1.0 is always little-endian */ + return qtest_big_endian(bus->qts); } #define CONFIG_BASE(dev) (VIRTIO_PCI_CONFIG_OFF((dev)->pdev->msix_enabled)) static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readb(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); } @@ -85,12 +56,12 @@ static uint8_t qvirtio_pci_config_readb(QVirtioDevice *d, uint64_t off) * so with a big-endian guest the order has been reversed, * reverse it again * virtio-1.0 is always little-endian, like PCI, but this - * case will be managed inside qvirtio_is_big_endian() + * case will be managed inside qvirtio_pci_is_big_endian() */ static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint16_t value; value = qpci_io_readw(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); @@ -102,7 +73,7 @@ static uint16_t qvirtio_pci_config_readw(QVirtioDevice *d, uint64_t off) static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint32_t value; value = qpci_io_readl(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); @@ -114,7 +85,7 @@ static uint32_t qvirtio_pci_config_readl(QVirtioDevice *d, uint64_t off) static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint64_t val; val = qpci_io_readq(dev->pdev, dev->bar, CONFIG_BASE(dev) + off); @@ -127,37 +98,37 @@ static uint64_t qvirtio_pci_config_readq(QVirtioDevice *d, uint64_t off) static uint32_t qvirtio_pci_get_features(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_HOST_FEATURES); } static void qvirtio_pci_set_features(QVirtioDevice *d, uint32_t features) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES, features); } static uint32_t qvirtio_pci_get_guest_features(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readl(dev->pdev, dev->bar, VIRTIO_PCI_GUEST_FEATURES); } static uint8_t qvirtio_pci_get_status(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS); } static void qvirtio_pci_set_status(QVirtioDevice *d, uint8_t status) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_STATUS, status); } static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); QVirtQueuePCI *vqpci = (QVirtQueuePCI *)vq; uint32_t data; @@ -182,7 +153,7 @@ static bool qvirtio_pci_get_queue_isr_status(QVirtioDevice *d, QVirtQueue *vq) static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); uint32_t data; if (dev->pdev->msix_enabled) { @@ -206,19 +177,19 @@ static bool qvirtio_pci_get_config_isr_status(QVirtioDevice *d) static void qvirtio_pci_queue_select(QVirtioDevice *d, uint16_t index) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writeb(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_SEL, index); } static uint16_t qvirtio_pci_get_queue_size(QVirtioDevice *d) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); return qpci_io_readw(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NUM); } static void qvirtio_pci_set_queue_address(QVirtioDevice *d, uint32_t pfn) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writel(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_PFN, pfn); } @@ -270,7 +241,7 @@ static void qvirtio_pci_virtqueue_cleanup(QVirtQueue *vq, static void qvirtio_pci_virtqueue_kick(QVirtioDevice *d, QVirtQueue *vq) { - QVirtioPCIDevice *dev = (QVirtioPCIDevice *)d; + QVirtioPCIDevice *dev = container_of(d, QVirtioPCIDevice, vdev); qpci_io_writew(dev->pdev, dev->bar, VIRTIO_PCI_QUEUE_NOTIFY, vq->index); } @@ -294,47 +265,6 @@ const QVirtioBus qvirtio_pci = { .virtqueue_kick = qvirtio_pci_virtqueue_kick, }; -static void qvirtio_pci_foreach(QPCIBus *bus, uint16_t device_type, - bool has_slot, int slot, - void (*func)(QVirtioDevice *d, void *data), void *data) -{ - QVirtioPCIForeachData d = { .func = func, - .device_type = device_type, - .has_slot = has_slot, - .slot = slot, - .user_data = data }; - - qpci_device_foreach(bus, PCI_VENDOR_ID_REDHAT_QUMRANET, -1, - qvirtio_pci_foreach_callback, &d); -} - -QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type) -{ - QVirtioPCIDevice *dev = NULL; - - qvirtio_pci_foreach(bus, device_type, false, 0, - qvirtio_pci_assign_device, &dev); - - if (dev) { - dev->vdev.bus = &qvirtio_pci; - } - - return dev; -} - -QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus, - uint16_t device_type, int slot) -{ - QVirtioPCIDevice *dev = NULL; - - qvirtio_pci_foreach(bus, device_type, true, slot, - qvirtio_pci_assign_device, &dev); - - dev->vdev.bus = &qvirtio_pci; - - return dev; -} - void qvirtio_pci_device_enable(QVirtioPCIDevice *d) { qpci_device_enable(d->pdev); @@ -416,3 +346,54 @@ void qvirtio_pci_set_msix_configuration_vector(QVirtioPCIDevice *d, vector = qpci_io_readw(d->pdev, d->bar, VIRTIO_MSI_CONFIG_VECTOR); g_assert_cmphex(vector, !=, VIRTIO_MSI_NO_VECTOR); } + +void qvirtio_pci_destructor(QOSGraphObject *obj) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; + qvirtio_pci_device_disable(dev); + g_free(dev->pdev); +} + +void qvirtio_pci_start_hw(QOSGraphObject *obj) +{ + QVirtioPCIDevice *dev = (QVirtioPCIDevice *)obj; + qvirtio_pci_device_enable(dev); + qvirtio_start_device(&dev->vdev); +} + +static void qvirtio_pci_init_from_pcidev(QVirtioPCIDevice *dev, QPCIDevice *pci_dev) +{ + dev->pdev = pci_dev; + dev->vdev.device_type = qpci_config_readw(pci_dev, PCI_SUBSYSTEM_ID); + + dev->config_msix_entry = -1; + + dev->vdev.bus = &qvirtio_pci; + dev->vdev.big_endian = qvirtio_pci_is_big_endian(dev); + + /* each virtio-xxx-pci device should override at least this function */ + dev->obj.get_driver = NULL; + dev->obj.start_hw = qvirtio_pci_start_hw; + dev->obj.destructor = qvirtio_pci_destructor; +} + +void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr) +{ + QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); + g_assert_nonnull(pci_dev); + qvirtio_pci_init_from_pcidev(dev, pci_dev); +} + +QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr) +{ + QVirtioPCIDevice *dev; + QPCIDevice *pci_dev = qpci_device_find(bus, addr->devfn); + if (!pci_dev) { + return NULL; + } + + dev = g_new0(QVirtioPCIDevice, 1); + qvirtio_pci_init_from_pcidev(dev, pci_dev); + dev->obj.free = g_free; + return dev; +} diff --git a/tests/libqos/virtio-pci.h b/tests/libqos/virtio-pci.h index 6ef19094cb..728b4715f1 100644 --- a/tests/libqos/virtio-pci.h +++ b/tests/libqos/virtio-pci.h @@ -12,8 +12,10 @@ #include "libqos/virtio.h" #include "libqos/pci.h" +#include "libqos/qgraph.h" typedef struct QVirtioPCIDevice { + QOSGraphObject obj; QVirtioDevice vdev; QPCIDevice *pdev; QPCIBar bar; @@ -31,10 +33,18 @@ typedef struct QVirtQueuePCI { extern const QVirtioBus qvirtio_pci; -QVirtioPCIDevice *qvirtio_pci_device_find(QPCIBus *bus, uint16_t device_type); -QVirtioPCIDevice *qvirtio_pci_device_find_slot(QPCIBus *bus, - uint16_t device_type, int slot); -void qvirtio_pci_device_free(QVirtioPCIDevice *dev); +void virtio_pci_init(QVirtioPCIDevice *dev, QPCIBus *bus, QPCIAddress * addr); +QVirtioPCIDevice *virtio_pci_new(QPCIBus *bus, QPCIAddress * addr); + +/* virtio-pci object functions available for subclasses that + * override the original start_hw and destroy + * function. All virtio-xxx-pci subclass that override must + * take care of calling these two functions in the respective + * places + */ +void qvirtio_pci_destructor(QOSGraphObject *obj); +void qvirtio_pci_start_hw(QOSGraphObject *obj); + void qvirtio_pci_device_enable(QVirtioPCIDevice *d); void qvirtio_pci_device_disable(QVirtioPCIDevice *d); diff --git a/tests/libqos/virtio-rng.c b/tests/libqos/virtio-rng.c new file mode 100644 index 0000000000..a1d2c7671c --- /dev/null +++ b/tests/libqos/virtio-rng.c @@ -0,0 +1,110 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-rng.h" + +/* virtio-rng-device */ +static void *qvirtio_rng_get_driver(QVirtioRng *v_rng, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-rng")) { + return v_rng; + } + if (!g_strcmp0(interface, "virtio")) { + return v_rng->vdev; + } + + fprintf(stderr, "%s not present in virtio-rng-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_rng_device_get_driver(void *object, + const char *interface) +{ + QVirtioRngDevice *v_rng = object; + return qvirtio_rng_get_driver(&v_rng->rng, interface); +} + +static void *virtio_rng_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioRngDevice *virtio_rdevice = g_new0(QVirtioRngDevice, 1); + QVirtioRng *interface = &virtio_rdevice->rng; + + interface->vdev = virtio_dev; + + virtio_rdevice->obj.get_driver = qvirtio_rng_device_get_driver; + + return &virtio_rdevice->obj; +} + +/* virtio-rng-pci */ +static void *qvirtio_rng_pci_get_driver(void *object, const char *interface) +{ + QVirtioRngPCI *v_rng = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_rng->pci_vdev.pdev; + } + return qvirtio_rng_get_driver(&v_rng->rng, interface); +} + +static void *virtio_rng_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioRngPCI *virtio_rpci = g_new0(QVirtioRngPCI, 1); + QVirtioRng *interface = &virtio_rpci->rng; + QOSGraphObject *obj = &virtio_rpci->pci_vdev.obj; + + virtio_pci_init(&virtio_rpci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_rpci->pci_vdev.vdev; + + obj->get_driver = qvirtio_rng_pci_get_driver; + + return obj; +} + +static void virtio_rng_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + + /* virtio-rng-device */ + qos_node_create_driver("virtio-rng-device", virtio_rng_device_create); + qos_node_consumes("virtio-rng-device", "virtio-bus", NULL); + qos_node_produces("virtio-rng-device", "virtio"); + qos_node_produces("virtio-rng-device", "virtio-rng"); + + /* virtio-rng-pci */ + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-rng-pci", virtio_rng_pci_create); + qos_node_consumes("virtio-rng-pci", "pci-bus", &opts); + qos_node_produces("virtio-rng-pci", "pci-device"); + qos_node_produces("virtio-rng-pci", "virtio"); + qos_node_produces("virtio-rng-pci", "virtio-rng"); +} + +libqos_init(virtio_rng_register_nodes); diff --git a/tests/libqos/virtio-rng.h b/tests/libqos/virtio-rng.h new file mode 100644 index 0000000000..fbba988875 --- /dev/null +++ b/tests/libqos/virtio-rng.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioRng QVirtioRng; +typedef struct QVirtioRngPCI QVirtioRngPCI; +typedef struct QVirtioRngDevice QVirtioRngDevice; + +struct QVirtioRng { + QVirtioDevice *vdev; +}; + +struct QVirtioRngPCI { + QVirtioPCIDevice pci_vdev; + QVirtioRng rng; +}; + +struct QVirtioRngDevice { + QOSGraphObject obj; + QVirtioRng rng; +}; diff --git a/tests/libqos/virtio-scsi.c b/tests/libqos/virtio-scsi.c new file mode 100644 index 0000000000..482684d0bc --- /dev/null +++ b/tests/libqos/virtio-scsi.c @@ -0,0 +1,117 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "standard-headers/linux/virtio_ids.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-scsi.h" + +/* virtio-scsi-device */ +static void *qvirtio_scsi_get_driver(QVirtioSCSI *v_scsi, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-scsi")) { + return v_scsi; + } + if (!g_strcmp0(interface, "virtio")) { + return v_scsi->vdev; + } + + fprintf(stderr, "%s not present in virtio-scsi-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_scsi_device_get_driver(void *object, + const char *interface) +{ + QVirtioSCSIDevice *v_scsi = object; + return qvirtio_scsi_get_driver(&v_scsi->scsi, interface); +} + +static void *virtio_scsi_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSCSIDevice *virtio_bdevice = g_new0(QVirtioSCSIDevice, 1); + QVirtioSCSI *interface = &virtio_bdevice->scsi; + + interface->vdev = virtio_dev; + + virtio_bdevice->obj.get_driver = qvirtio_scsi_device_get_driver; + + return &virtio_bdevice->obj; +} + +/* virtio-scsi-pci */ +static void *qvirtio_scsi_pci_get_driver(void *object, + const char *interface) +{ + QVirtioSCSIPCI *v_scsi = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_scsi->pci_vdev.pdev; + } + return qvirtio_scsi_get_driver(&v_scsi->scsi, interface); +} + +static void *virtio_scsi_pci_create(void *pci_bus, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSCSIPCI *virtio_spci = g_new0(QVirtioSCSIPCI, 1); + QVirtioSCSI *interface = &virtio_spci->scsi; + QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; + + virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_spci->pci_vdev.vdev; + + g_assert_cmphex(interface->vdev->device_type, ==, VIRTIO_ID_SCSI); + + obj->get_driver = qvirtio_scsi_pci_get_driver; + + return obj; +} + +static void virtio_scsi_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions opts = { + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw", + .after_cmd_line = "-device scsi-hd,bus=vs0.0,drive=drv0", + }; + + /* virtio-scsi-device */ + opts.extra_device_opts = "id=vs0"; + qos_node_create_driver("virtio-scsi-device", + virtio_scsi_device_create); + qos_node_consumes("virtio-scsi-device", "virtio-bus", &opts); + qos_node_produces("virtio-scsi-device", "virtio-scsi"); + + /* virtio-scsi-pci */ + opts.extra_device_opts = "id=vs0,addr=04.0"; + add_qpci_address(&opts, &addr); + qos_node_create_driver("virtio-scsi-pci", virtio_scsi_pci_create); + qos_node_consumes("virtio-scsi-pci", "pci-bus", &opts); + qos_node_produces("virtio-scsi-pci", "pci-device"); + qos_node_produces("virtio-scsi-pci", "virtio-scsi"); +} + +libqos_init(virtio_scsi_register_nodes); diff --git a/tests/libqos/virtio-scsi.h b/tests/libqos/virtio-scsi.h new file mode 100644 index 0000000000..17a47beddc --- /dev/null +++ b/tests/libqos/virtio-scsi.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioSCSI QVirtioSCSI; +typedef struct QVirtioSCSIPCI QVirtioSCSIPCI; +typedef struct QVirtioSCSIDevice QVirtioSCSIDevice; + +struct QVirtioSCSI { + QVirtioDevice *vdev; +}; + +struct QVirtioSCSIPCI { + QVirtioPCIDevice pci_vdev; + QVirtioSCSI scsi; +}; + +struct QVirtioSCSIDevice { + QOSGraphObject obj; + QVirtioSCSI scsi; +}; diff --git a/tests/libqos/virtio-serial.c b/tests/libqos/virtio-serial.c new file mode 100644 index 0000000000..91cedefb8d --- /dev/null +++ b/tests/libqos/virtio-serial.c @@ -0,0 +1,110 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-serial.h" + +static void *qvirtio_serial_get_driver(QVirtioSerial *v_serial, + const char *interface) +{ + if (!g_strcmp0(interface, "virtio-serial")) { + return v_serial; + } + if (!g_strcmp0(interface, "virtio")) { + return v_serial->vdev; + } + + fprintf(stderr, "%s not present in virtio-serial-device\n", interface); + g_assert_not_reached(); +} + +static void *qvirtio_serial_device_get_driver(void *object, + const char *interface) +{ + QVirtioSerialDevice *v_serial = object; + return qvirtio_serial_get_driver(&v_serial->serial, interface); +} + +static void *virtio_serial_device_create(void *virtio_dev, + QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSerialDevice *virtio_device = g_new0(QVirtioSerialDevice, 1); + QVirtioSerial *interface = &virtio_device->serial; + + interface->vdev = virtio_dev; + + virtio_device->obj.get_driver = qvirtio_serial_device_get_driver; + + return &virtio_device->obj; +} + +/* virtio-serial-pci */ +static void *qvirtio_serial_pci_get_driver(void *object, const char *interface) +{ + QVirtioSerialPCI *v_serial = object; + if (!g_strcmp0(interface, "pci-device")) { + return v_serial->pci_vdev.pdev; + } + return qvirtio_serial_get_driver(&v_serial->serial, interface); +} + +static void *virtio_serial_pci_create(void *pci_bus, QGuestAllocator *t_alloc, + void *addr) +{ + QVirtioSerialPCI *virtio_spci = g_new0(QVirtioSerialPCI, 1); + QVirtioSerial *interface = &virtio_spci->serial; + QOSGraphObject *obj = &virtio_spci->pci_vdev.obj; + + virtio_pci_init(&virtio_spci->pci_vdev, pci_bus, addr); + interface->vdev = &virtio_spci->pci_vdev.vdev; + + obj->get_driver = qvirtio_serial_pci_get_driver; + + return obj; +} + +static void virtio_serial_register_nodes(void) +{ + QPCIAddress addr = { + .devfn = QPCI_DEVFN(4, 0), + }; + + QOSGraphEdgeOptions edge_opts = { }; + + /* virtio-serial-device */ + edge_opts.extra_device_opts = "id=vser0"; + qos_node_create_driver("virtio-serial-device", + virtio_serial_device_create); + qos_node_consumes("virtio-serial-device", "virtio-bus", &edge_opts); + qos_node_produces("virtio-serial-device", "virtio"); + qos_node_produces("virtio-serial-device", "virtio-serial"); + + /* virtio-serial-pci */ + edge_opts.extra_device_opts = "id=vser0,addr=04.0"; + add_qpci_address(&edge_opts, &addr); + qos_node_create_driver("virtio-serial-pci", virtio_serial_pci_create); + qos_node_consumes("virtio-serial-pci", "pci-bus", &edge_opts); + qos_node_produces("virtio-serial-pci", "pci-device"); + qos_node_produces("virtio-serial-pci", "virtio"); + qos_node_produces("virtio-serial-pci", "virtio-serial"); +} + +libqos_init(virtio_serial_register_nodes); diff --git a/tests/libqos/virtio-serial.h b/tests/libqos/virtio-serial.h new file mode 100644 index 0000000000..b7e2a5d178 --- /dev/null +++ b/tests/libqos/virtio-serial.h @@ -0,0 +1,39 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "libqos/qgraph.h" +#include "libqos/virtio.h" +#include "libqos/virtio-pci.h" + +typedef struct QVirtioSerial QVirtioSerial; +typedef struct QVirtioSerialPCI QVirtioSerialPCI; +typedef struct QVirtioSerialDevice QVirtioSerialDevice; + +struct QVirtioSerial { + QVirtioDevice *vdev; +}; + +struct QVirtioSerialPCI { + QVirtioPCIDevice pci_vdev; + QVirtioSerial serial; +}; + +struct QVirtioSerialDevice { + QOSGraphObject obj; + QVirtioSerial serial; +}; diff --git a/tests/libqos/virtio.c b/tests/libqos/virtio.c index 0dad5c19ac..5e8f39b4d3 100644 --- a/tests/libqos/virtio.c +++ b/tests/libqos/virtio.c @@ -40,6 +40,7 @@ uint32_t qvirtio_get_features(QVirtioDevice *d) void qvirtio_set_features(QVirtioDevice *d, uint32_t features) { + d->features = features; d->bus->set_features(d, features); } @@ -349,19 +350,14 @@ void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx) writew(vq->avail + 4 + (2 * vq->size), idx); } -/* - * qvirtio_get_dev_type: - * Returns: the preferred virtio bus/device type for the current architecture. - */ -const char *qvirtio_get_dev_type(void) +void qvirtio_start_device(QVirtioDevice *vdev) { - const char *arch = qtest_get_arch(); - - if (g_str_equal(arch, "arm") || g_str_equal(arch, "aarch64")) { - return "device"; /* for virtio-mmio */ - } else if (g_str_equal(arch, "s390x")) { - return "ccw"; - } else { - return "pci"; - } + qvirtio_reset(vdev); + qvirtio_set_acknowledge(vdev); + qvirtio_set_driver(vdev); +} + +bool qvirtio_is_big_endian(QVirtioDevice *d) +{ + return d->big_endian; } diff --git a/tests/libqos/virtio.h b/tests/libqos/virtio.h index 69b5b13840..51d2359ace 100644 --- a/tests/libqos/virtio.h +++ b/tests/libqos/virtio.h @@ -21,6 +21,8 @@ typedef struct QVirtioDevice { const QVirtioBus *bus; /* Device type */ uint16_t device_type; + uint64_t features; + bool big_endian; } QVirtioDevice; typedef struct QVirtQueue { @@ -90,12 +92,6 @@ struct QVirtioBus { void (*virtqueue_kick)(QVirtioDevice *d, QVirtQueue *vq); }; -static inline bool qvirtio_is_big_endian(QVirtioDevice *d) -{ - /* FIXME: virtio 1.0 is always little-endian */ - return qtest_big_endian(global_qtest); -} - static inline uint32_t qvring_size(uint32_t num, uint32_t align) { return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num) @@ -109,6 +105,7 @@ uint32_t qvirtio_config_readl(QVirtioDevice *d, uint64_t addr); uint64_t qvirtio_config_readq(QVirtioDevice *d, uint64_t addr); uint32_t qvirtio_get_features(QVirtioDevice *d); void qvirtio_set_features(QVirtioDevice *d, uint32_t features); +bool qvirtio_is_big_endian(QVirtioDevice *d); void qvirtio_reset(QVirtioDevice *d); void qvirtio_set_acknowledge(QVirtioDevice *d); @@ -145,6 +142,6 @@ bool qvirtqueue_get_buf(QVirtQueue *vq, uint32_t *desc_idx, uint32_t *len); void qvirtqueue_set_used_event(QVirtQueue *vq, uint16_t idx); -const char *qvirtio_get_dev_type(void); +void qvirtio_start_device(QVirtioDevice *vdev); #endif diff --git a/tests/libqos/x86_64_pc-machine.c b/tests/libqos/x86_64_pc-machine.c new file mode 100644 index 0000000000..8bd0360ba9 --- /dev/null +++ b/tests/libqos/x86_64_pc-machine.c @@ -0,0 +1,114 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "pci-pc.h" +#include "malloc-pc.h" + +typedef struct QX86PCMachine QX86PCMachine; +typedef struct i440FX_pcihost i440FX_pcihost; +typedef struct QSDHCI_PCI QSDHCI_PCI; + +struct i440FX_pcihost { + QOSGraphObject obj; + QPCIBusPC pci; +}; + +struct QX86PCMachine { + QOSGraphObject obj; + QGuestAllocator alloc; + i440FX_pcihost bridge; +}; + +/* i440FX_pcihost */ + +static QOSGraphObject *i440FX_host_get_device(void *obj, const char *device) +{ + i440FX_pcihost *host = obj; + if (!g_strcmp0(device, "pci-bus-pc")) { + return &host->pci.obj; + } + fprintf(stderr, "%s not present in i440FX-pcihost\n", device); + g_assert_not_reached(); +} + +static void qos_create_i440FX_host(i440FX_pcihost *host, + QTestState *qts, + QGuestAllocator *alloc) +{ + host->obj.get_device = i440FX_host_get_device; + qpci_init_pc(&host->pci, qts, alloc); +} + +/* x86_64/pc machine */ + +static void pc_destructor(QOSGraphObject *obj) +{ + QX86PCMachine *machine = (QX86PCMachine *) obj; + alloc_destroy(&machine->alloc); +} + +static void *pc_get_driver(void *object, const char *interface) +{ + QX86PCMachine *machine = object; + if (!g_strcmp0(interface, "memory")) { + return &machine->alloc; + } + + fprintf(stderr, "%s not present in x86_64/pc\n", interface); + g_assert_not_reached(); +} + +static QOSGraphObject *pc_get_device(void *obj, const char *device) +{ + QX86PCMachine *machine = obj; + if (!g_strcmp0(device, "i440FX-pcihost")) { + return &machine->bridge.obj; + } + + fprintf(stderr, "%s not present in x86_64/pc\n", device); + g_assert_not_reached(); +} + +static void *qos_create_machine_pc(QTestState *qts) +{ + QX86PCMachine *machine = g_new0(QX86PCMachine, 1); + machine->obj.get_device = pc_get_device; + machine->obj.get_driver = pc_get_driver; + machine->obj.destructor = pc_destructor; + pc_alloc_init(&machine->alloc, qts, ALLOC_NO_FLAGS); + qos_create_i440FX_host(&machine->bridge, qts, &machine->alloc); + + return &machine->obj; +} + +static void pc_machine_register_nodes(void) +{ + qos_node_create_machine("i386/pc", qos_create_machine_pc); + qos_node_contains("i386/pc", "i440FX-pcihost", NULL); + + qos_node_create_machine("x86_64/pc", qos_create_machine_pc); + qos_node_contains("x86_64/pc", "i440FX-pcihost", NULL); + + qos_node_create_driver("i440FX-pcihost", NULL); + qos_node_contains("i440FX-pcihost", "pci-bus-pc", NULL); +} + +libqos_init(pc_machine_register_nodes); diff --git a/tests/libqtest.c b/tests/libqtest.c index 098af6aec4..c49b85482d 100644 --- a/tests/libqtest.c +++ b/tests/libqtest.c @@ -21,10 +21,10 @@ #include #include "libqtest.h" +#include "qemu-common.h" #include "qemu/cutils.h" #include "qapi/error.h" #include "qapi/qmp/json-parser.h" -#include "qapi/qmp/json-streamer.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qjson.h" #include "qapi/qmp/qlist.h" @@ -39,19 +39,16 @@ struct QTestState { int fd; int qmp_fd; + pid_t qemu_pid; /* our child QEMU process */ + int wstatus; + bool big_endian; bool irq_level[MAX_IRQ]; GString *rx; - pid_t qemu_pid; /* our child QEMU process */ - bool big_endian; }; static GHookList abrt_hooks; static struct sigaction sigact_old; -#define g_assert_no_errno(ret) do { \ - g_assert_cmpint(ret, !=, -1); \ -} while (0) - static int qtest_query_target_endianness(QTestState *s); static int init_socket(const char *socket_path) @@ -61,7 +58,7 @@ static int init_socket(const char *socket_path) int ret; sock = socket(PF_UNIX, SOCK_STREAM, 0); - g_assert_no_errno(sock); + g_assert_cmpint(sock, !=, -1); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", socket_path); @@ -70,9 +67,9 @@ static int init_socket(const char *socket_path) do { ret = bind(sock, (struct sockaddr *)&addr, sizeof(addr)); } while (ret == -1 && errno == EINTR); - g_assert_no_errno(ret); + g_assert_cmpint(ret, !=, -1); ret = listen(sock, 1); - g_assert_no_errno(ret); + g_assert_cmpint(ret, !=, -1); return sock; } @@ -100,18 +97,52 @@ static int socket_accept(int sock) return ret; } +bool qtest_probe_child(QTestState *s) +{ + pid_t pid = s->qemu_pid; + + if (pid != -1) { + pid = waitpid(pid, &s->wstatus, WNOHANG); + if (pid == 0) { + return true; + } + s->qemu_pid = -1; + } + return false; +} + static void kill_qemu(QTestState *s) { - if (s->qemu_pid != -1) { - int wstatus = 0; - pid_t pid; + pid_t pid = s->qemu_pid; + int wstatus; - kill(s->qemu_pid, SIGTERM); - pid = waitpid(s->qemu_pid, &wstatus, 0); + /* Skip wait if qtest_probe_child already reaped. */ + if (pid != -1) { + kill(pid, SIGTERM); + TFR(pid = waitpid(s->qemu_pid, &s->wstatus, 0)); + assert(pid == s->qemu_pid); + } - if (pid == s->qemu_pid && WIFSIGNALED(wstatus)) { - assert(!WCOREDUMP(wstatus)); + /* + * We expect qemu to exit with status 0; anything else is + * fishy and should be logged with as much detail as possible. + */ + wstatus = s->wstatus; + if (wstatus) { + if (WIFEXITED(wstatus)) { + fprintf(stderr, "%s:%d: kill_qemu() tried to terminate QEMU " + "process but encountered exit status %d\n", + __FILE__, __LINE__, WEXITSTATUS(wstatus)); + } else if (WIFSIGNALED(wstatus)) { + int sig = WTERMSIG(wstatus); + const char *signame = strsignal(sig) ?: "unknown ???"; + const char *dump = WCOREDUMP(wstatus) ? " (core dumped)" : ""; + + fprintf(stderr, "%s:%d: kill_qemu() detected QEMU death " + "from signal %d (%s)%s\n", + __FILE__, __LINE__, sig, signame, dump); } + abort(); } } @@ -173,8 +204,7 @@ static const char *qtest_qemu_binary(void) return qemu_bin; } -QTestState *qtest_init_without_qmp_handshake(bool use_oob, - const char *extra_args) +QTestState *qtest_init_without_qmp_handshake(const char *extra_args) { QTestState *s; int sock, qmpsock, i; @@ -201,24 +231,29 @@ QTestState *qtest_init_without_qmp_handshake(bool use_oob, qtest_add_abrt_handler(kill_qemu_hook_func, s); + command = g_strdup_printf("exec %s " + "-qtest unix:%s " + "-qtest-log %s " + "-chardev socket,path=%s,id=char0 " + "-mon chardev=char0,mode=control " + "-machine accel=qtest " + "-display none " + "%s", qemu_binary, socket_path, + getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", + qmp_socket_path, + extra_args ?: ""); + + g_test_message("starting QEMU: %s", command); + + s->wstatus = 0; s->qemu_pid = fork(); if (s->qemu_pid == 0) { setenv("QEMU_AUDIO_DRV", "none", true); - command = g_strdup_printf("exec %s " - "-qtest unix:%s,nowait " - "-qtest-log %s " - "-chardev socket,path=%s,nowait,id=char0 " - "-mon chardev=char0,mode=control%s " - "-machine accel=qtest " - "-display none " - "%s", qemu_binary, socket_path, - getenv("QTEST_LOG") ? "/dev/fd/2" : "/dev/null", - qmp_socket_path, use_oob ? ",x-oob=on" : "", - extra_args ?: ""); execlp("/bin/sh", "sh", "-c", command, NULL); exit(1); } + g_free(command); s->fd = socket_accept(sock); if (s->fd >= 0) { s->qmp_fd = socket_accept(qmpsock); @@ -248,37 +283,63 @@ QTestState *qtest_init_without_qmp_handshake(bool use_oob, QTestState *qtest_init(const char *extra_args) { - QTestState *s = qtest_init_without_qmp_handshake(false, extra_args); + QTestState *s = qtest_init_without_qmp_handshake(extra_args); + QDict *greeting; /* Read the QMP greeting and then do the handshake */ - qtest_qmp_discard_response(s, ""); - qtest_qmp_discard_response(s, "{ 'execute': 'qmp_capabilities' }"); + greeting = qtest_qmp_receive(s); + qobject_unref(greeting); + qobject_unref(qtest_qmp(s, "{ 'execute': 'qmp_capabilities' }")); return s; } -QTestState *qtest_vstartf(const char *fmt, va_list ap) +QTestState *qtest_vinitf(const char *fmt, va_list ap) { char *args = g_strdup_vprintf(fmt, ap); QTestState *s; - s = qtest_start(args); + s = qtest_init(args); g_free(args); - global_qtest = NULL; return s; } -QTestState *qtest_startf(const char *fmt, ...) +QTestState *qtest_initf(const char *fmt, ...) { va_list ap; QTestState *s; va_start(ap, fmt); - s = qtest_vstartf(fmt, ap); + s = qtest_vinitf(fmt, ap); va_end(ap); return s; } +QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd) +{ + int sock_fd_init; + char *sock_path, sock_dir[] = "/tmp/qtest-serial-XXXXXX"; + QTestState *qts; + + g_assert_true(mkdtemp(sock_dir) != NULL); + sock_path = g_strdup_printf("%s/sock", sock_dir); + + sock_fd_init = init_socket(sock_path); + + qts = qtest_initf("-chardev socket,id=s0,path=%s -serial chardev:s0 %s", + sock_path, extra_args); + + *sock_fd = socket_accept(sock_fd_init); + + unlink(sock_path); + g_free(sock_path); + rmdir(sock_dir); + + g_assert_true(*sock_fd >= 0); + + return qts; +} + void qtest_quit(QTestState *s) { g_hook_destroy_link(&abrt_hooks, g_hook_find_data(&abrt_hooks, TRUE, s)); @@ -306,7 +367,6 @@ static void socket_send(int fd, const char *buf, size_t size) continue; } - g_assert_no_errno(len); g_assert_cmpint(len, >, 0); offset += len; @@ -348,7 +408,7 @@ static GString *qtest_recv_line(QTestState *s) if (len == -1 || len == 0) { fprintf(stderr, "Broken pipe\n"); - exit(1); + abort(); } g_string_append_len(s->rx, buffer, len); @@ -427,15 +487,16 @@ typedef struct { QDict *response; } QMPResponseParser; -static void qmp_response(JSONMessageParser *parser, GQueue *tokens) +static void qmp_response(void *opaque, QObject *obj, Error *err) { - QMPResponseParser *qmp = container_of(parser, QMPResponseParser, parser); - QObject *obj; + QMPResponseParser *qmp = opaque; - obj = json_parser_parse(tokens, NULL); - if (!obj) { - fprintf(stderr, "QMP JSON response parsing failed\n"); - exit(1); + assert(!obj != !err); + + if (err) { + error_prepend(&err, "QMP JSON response parsing failed: "); + error_report_err(err); + abort(); } g_assert(!qmp->response); @@ -449,7 +510,7 @@ QDict *qmp_fd_receive(int fd) bool log = getenv("QTEST_LOG") != NULL; qmp.response = NULL; - json_message_parser_init(&qmp.parser, qmp_response); + json_message_parser_init(&qmp.parser, qmp_response, &qmp, NULL); while (!qmp.response) { ssize_t len; char c; @@ -461,7 +522,7 @@ QDict *qmp_fd_receive(int fd) if (len == -1 || len == 0) { fprintf(stderr, "Broken pipe\n"); - exit(1); + abort(); } if (log) { @@ -484,26 +545,12 @@ QDict *qtest_qmp_receive(QTestState *s) * in the case that they choose to discard all replies up until * a particular EVENT is received. */ -void qmp_fd_sendv(int fd, const char *fmt, va_list ap) +void qmp_fd_vsend(int fd, const char *fmt, va_list ap) { - va_list ap_copy; QObject *qobj; - /* qobject_from_jsonv() silently eats leading 0xff as invalid - * JSON, but we want to test sending them over the wire to force - * resyncs */ - if (*fmt == '\377') { - socket_send(fd, fmt, 1); - fmt++; - } - - /* Going through qobject ensures we escape strings properly. - * This seemingly unnecessary copy is required in case va_list - * is an array type. - */ - va_copy(ap_copy, ap); - qobj = qobject_from_jsonv(fmt, &ap_copy, &error_abort); - va_end(ap_copy); + /* Going through qobject ensures we escape strings properly */ + qobj = qobject_from_vjsonf_nofail(fmt, ap); /* No need to send anything for an empty QObject. */ if (qobj) { @@ -529,21 +576,21 @@ void qmp_fd_sendv(int fd, const char *fmt, va_list ap) } } -void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap) +void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) { - qmp_fd_sendv(s->qmp_fd, fmt, ap); + qmp_fd_vsend(s->qmp_fd, fmt, ap); } QDict *qmp_fdv(int fd, const char *fmt, va_list ap) { - qmp_fd_sendv(fd, fmt, ap); + qmp_fd_vsend(fd, fmt, ap); return qmp_fd_receive(fd); } -QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap) +QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) { - qtest_async_qmpv(s, fmt, ap); + qtest_qmp_vsend(s, fmt, ap); /* Receive reply */ return qtest_qmp_receive(s); @@ -565,7 +612,7 @@ void qmp_fd_send(int fd, const char *fmt, ...) va_list ap; va_start(ap, fmt); - qmp_fd_sendv(fd, fmt, ap); + qmp_fd_vsend(fd, fmt, ap); va_end(ap); } @@ -575,35 +622,48 @@ QDict *qtest_qmp(QTestState *s, const char *fmt, ...) QDict *response; va_start(ap, fmt); - response = qtest_qmpv(s, fmt, ap); + response = qtest_vqmp(s, fmt, ap); va_end(ap); return response; } -void qtest_async_qmp(QTestState *s, const char *fmt, ...) +void qtest_qmp_send(QTestState *s, const char *fmt, ...) { va_list ap; va_start(ap, fmt); - qtest_async_qmpv(s, fmt, ap); + qtest_qmp_vsend(s, fmt, ap); va_end(ap); } -void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap) +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) { - QDict *response = qtest_qmpv(s, fmt, ap); - qobject_unref(response); + bool log = getenv("QTEST_LOG") != NULL; + char *str = g_strdup_vprintf(fmt, ap); + + if (log) { + fprintf(stderr, "%s", str); + } + socket_send(fd, str, strlen(str)); + g_free(str); } -void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...) +void qmp_fd_send_raw(int fd, const char *fmt, ...) { va_list ap; - QDict *response; va_start(ap, fmt); - response = qtest_qmpv(s, fmt, ap); + qmp_fd_vsend_raw(fd, fmt, ap); + va_end(ap); +} + +void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + qmp_fd_vsend_raw(s->qmp_fd, fmt, ap); va_end(ap); - qobject_unref(response); } QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event) @@ -628,7 +688,7 @@ void qtest_qmp_eventwait(QTestState *s, const char *event) qobject_unref(response); } -char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap) +char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) { char *cmd; QDict *resp; @@ -657,7 +717,7 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...) char *ret; va_start(ap, fmt); - ret = qtest_hmpv(s, fmt, ap); + ret = qtest_vhmp(s, fmt, ap); va_end(ap); return ret; } @@ -718,6 +778,16 @@ void qtest_irq_intercept_in(QTestState *s, const char *qom_path) qtest_rsp(s, 0); } +void qtest_set_irq_in(QTestState *s, const char *qom_path, const char *name, + int num, int level) +{ + if (!name) { + name = "unnamed-gpio-in"; + } + qtest_sendf(s, "set_irq_in %s %s %d %d\n", qom_path, name, num, level); + qtest_rsp(s, 0); +} + static void qtest_out(QTestState *s, const char *cmd, uint16_t addr, uint32_t value) { qtest_sendf(s, "%s 0x%x 0x%x\n", cmd, addr, value); @@ -963,35 +1033,27 @@ QDict *qmp(const char *fmt, ...) QDict *response; va_start(ap, fmt); - response = qtest_qmpv(global_qtest, fmt, ap); + response = qtest_vqmp(global_qtest, fmt, ap); va_end(ap); return response; } -void qmp_async(const char *fmt, ...) +void qmp_send(const char *fmt, ...) { va_list ap; va_start(ap, fmt); - qtest_async_qmpv(global_qtest, fmt, ap); + qtest_qmp_vsend(global_qtest, fmt, ap); va_end(ap); } -void qmp_discard_response(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, 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); + ret = qtest_vhmp(global_qtest, fmt, ap); va_end(ap); return ret; } @@ -1001,7 +1063,53 @@ bool qtest_big_endian(QTestState *s) return s->big_endian; } -void qtest_cb_for_every_machine(void (*cb)(const char *machine)) +static bool qtest_check_machine_version(const char *mname, const char *basename, + int major, int minor) +{ + char *newname; + bool is_equal; + + newname = g_strdup_printf("%s-%i.%i", basename, major, minor); + is_equal = g_str_equal(mname, newname); + g_free(newname); + + return is_equal; +} + +static bool qtest_is_old_versioned_machine(const char *mname) +{ + const char *dash = strrchr(mname, '-'); + const char *dot = strrchr(mname, '.'); + const char *chr; + char *bname; + const int major = QEMU_VERSION_MAJOR; + const int minor = QEMU_VERSION_MINOR; + bool res = false; + + if (dash && dot && dot > dash) { + for (chr = dash + 1; *chr; chr++) { + if (!qemu_isdigit(*chr) && *chr != '.') { + return false; + } + } + /* + * Now check if it is one of the latest versions. Check major + 1 + * and minor + 1 versions as well, since they might already exist + * in the development branch. + */ + bname = g_strdup(mname); + bname[dash - mname] = 0; + res = !qtest_check_machine_version(mname, bname, major + 1, 0) && + !qtest_check_machine_version(mname, bname, major, minor + 1) && + !qtest_check_machine_version(mname, bname, major, minor); + g_free(bname); + } + + return res; +} + +void qtest_cb_for_every_machine(void (*cb)(const char *machine), + bool skip_old_versioned) { QDict *response, *minfo; QList *list; @@ -1024,42 +1132,76 @@ void qtest_cb_for_every_machine(void (*cb)(const char *machine)) qstr = qobject_to(QString, qobj); g_assert(qstr); mname = qstring_get_str(qstr); - cb(mname); + if (!skip_old_versioned || !qtest_is_old_versioned_machine(mname)) { + cb(mname); + } } qtest_end(); qobject_unref(response); } +QDict *qtest_qmp_receive_success(QTestState *s, + void (*event_cb)(void *opaque, + const char *event, + QDict *data), + void *opaque) +{ + QDict *response, *ret, *data; + const char *event; + + for (;;) { + response = qtest_qmp_receive(s); + g_assert(!qdict_haskey(response, "error")); + ret = qdict_get_qdict(response, "return"); + if (ret) { + break; + } + event = qdict_get_str(response, "event"); + data = qdict_get_qdict(response, "data"); + if (event_cb) { + event_cb(opaque, event, data); + } + qobject_unref(response); + } + + qobject_ref(ret); + qobject_unref(response); + return ret; +} + /* * Generic hot-plugging test via the device_add QMP command. */ -void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, - ...) +void qtest_qmp_device_add(const char *driver, const char *id, + const char *fmt, ...) { - QDict *response; - char *cmd, *opts = NULL; - va_list va; + QDict *args, *response; + va_list ap; - if (fmt) { - va_start(va, fmt); - opts = g_strdup_vprintf(fmt, va); - va_end(va); - } + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); - cmd = g_strdup_printf("{'execute': 'device_add'," - " 'arguments': { 'driver': '%s', 'id': '%s'%s%s }}", - driver, id, opts ? ", " : "", opts ? opts : ""); - g_free(opts); + g_assert(!qdict_haskey(args, "driver") && !qdict_haskey(args, "id")); + qdict_put_str(args, "driver", driver); + qdict_put_str(args, "id", id); - response = qmp(cmd); - g_free(cmd); + response = qmp("{'execute': 'device_add', 'arguments': %p}", args); g_assert(response); g_assert(!qdict_haskey(response, "event")); /* We don't expect any events */ g_assert(!qdict_haskey(response, "error")); qobject_unref(response); } +static void device_deleted_cb(void *opaque, const char *name, QDict *data) +{ + bool *got_event = opaque; + + g_assert_cmpstr(name, ==, "DEVICE_DELETED"); + *got_event = true; +} + /* * Generic hot-unplugging test via the device_del QMP command. * Device deletion will get one response and one event. For example: @@ -1080,30 +1222,21 @@ void qtest_qmp_device_add(const char *driver, const char *id, const char *fmt, */ void qtest_qmp_device_del(const char *id) { - QDict *response1, *response2, *event = NULL; - char *cmd; + bool got_event = false; + QDict *rsp; - cmd = g_strdup_printf("{'execute': 'device_del'," - " 'arguments': { 'id': '%s' }}", id); - response1 = qmp(cmd); - g_free(cmd); - g_assert(response1); - g_assert(!qdict_haskey(response1, "error")); - - response2 = qmp(""); - g_assert(response2); - g_assert(!qdict_haskey(response2, "error")); - - if (qdict_haskey(response1, "event")) { - event = response1; - } else if (qdict_haskey(response2, "event")) { - event = response2; + qtest_qmp_send(global_qtest, + "{'execute': 'device_del', 'arguments': {'id': %s}}", + id); + rsp = qtest_qmp_receive_success(global_qtest, device_deleted_cb, + &got_event); + qobject_unref(rsp); + if (!got_event) { + rsp = qmp_receive(); + g_assert_cmpstr(qdict_get_try_str(rsp, "event"), + ==, "DEVICE_DELETED"); + qobject_unref(rsp); } - g_assert(event); - g_assert_cmpstr(qdict_get_str(event, "event"), ==, "DEVICE_DELETED"); - - qobject_unref(response1); - qobject_unref(response2); } bool qmp_rsp_is_err(QDict *rsp) @@ -1112,3 +1245,14 @@ bool qmp_rsp_is_err(QDict *rsp) qobject_unref(rsp); return !!error; } + +void qmp_assert_error_class(QDict *rsp, const char *class) +{ + QDict *error = qdict_get_qdict(rsp, "error"); + + g_assert_cmpstr(qdict_get_try_str(error, "class"), ==, class); + g_assert_nonnull(qdict_get_try_str(error, "desc")); + g_assert(!qdict_haskey(rsp, "return")); + + qobject_unref(rsp); +} diff --git a/tests/libqtest.h b/tests/libqtest.h index ac52872cbe..a16acd58a6 100644 --- a/tests/libqtest.h +++ b/tests/libqtest.h @@ -17,38 +17,40 @@ #ifndef LIBQTEST_H #define LIBQTEST_H +#include "qapi/qmp/qobject.h" +#include "qapi/qmp/qdict.h" + typedef struct QTestState QTestState; extern QTestState *global_qtest; /** - * qtest_startf: + * qtest_initf: * @fmt...: Format for creating other arguments to pass to QEMU, formatted * like sprintf(). * - * Start QEMU and return the resulting #QTestState (but unlike qtest_start(), - * #global_qtest is left at NULL). + * Convenience wrapper around qtest_start(). * * Returns: #QTestState instance. */ -QTestState *qtest_startf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); +QTestState *qtest_initf(const char *fmt, ...) GCC_FMT_ATTR(1, 2); /** - * qtest_vstartf: + * qtest_vinitf: * @fmt: Format for creating other arguments to pass to QEMU, formatted * like vsprintf(). * @ap: Format arguments. * - * Start QEMU and return the resulting #QTestState (but unlike qtest_start(), - * #global_qtest is left at NULL). + * Convenience wrapper around qtest_start(). * * Returns: #QTestState instance. */ -QTestState *qtest_vstartf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); +QTestState *qtest_vinitf(const char *fmt, va_list ap) GCC_FMT_ATTR(1, 0); /** * qtest_init: - * @extra_args: other arguments to pass to QEMU. + * @extra_args: other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. * * Returns: #QTestState instance. */ @@ -56,14 +58,23 @@ QTestState *qtest_init(const char *extra_args); /** * qtest_init_without_qmp_handshake: - * @use_oob: true to have the server advertise OOB support * @extra_args: other arguments to pass to QEMU. CAUTION: these * arguments are subject to word splitting and shell evaluation. * * Returns: #QTestState instance. */ -QTestState *qtest_init_without_qmp_handshake(bool use_oob, - const char *extra_args); +QTestState *qtest_init_without_qmp_handshake(const char *extra_args); + +/** + * qtest_init_with_serial: + * @extra_args: other arguments to pass to QEMU. CAUTION: these + * arguments are subject to word splitting and shell evaluation. + * @sock_fd: pointer to store the socket file descriptor for + * connection with serial. + * + * Returns: #QTestState instance. + */ +QTestState *qtest_init_with_serial(const char *extra_args, int *sock_fd); /** * qtest_quit: @@ -73,62 +84,66 @@ QTestState *qtest_init_without_qmp_handshake(bool use_oob, */ void qtest_quit(QTestState *s); -/** - * qtest_qmp_discard_response: - * @s: #QTestState instance to operate on. - * @fmt...: QMP message to send to qemu - * - * Sends a QMP message to QEMU and consumes the response. - */ -void qtest_qmp_discard_response(QTestState *s, const char *fmt, ...); - /** * qtest_qmp: * @s: #QTestState instance to operate on. - * @fmt...: QMP message to send to qemu + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * * Sends a QMP message to QEMU and returns the response. */ -QDict *qtest_qmp(QTestState *s, const char *fmt, ...); +QDict *qtest_qmp(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); /** - * qtest_async_qmp: + * qtest_qmp_send: * @s: #QTestState instance to operate on. - * @fmt...: QMP message to send to qemu + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * * Sends a QMP message to QEMU and leaves the response in the stream. */ -void qtest_async_qmp(QTestState *s, const char *fmt, ...); +void qtest_qmp_send(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); /** - * qtest_qmpv_discard_response: + * qtest_qmp_send_raw: * @s: #QTestState instance to operate on. - * @fmt: QMP message to send to QEMU - * @ap: QMP message arguments + * @fmt...: text to send, formatted like sprintf() * - * Sends a QMP message to QEMU and consumes the response. + * Sends text to the QMP monitor verbatim. Need not be valid JSON; + * this is useful for negative tests. */ -void qtest_qmpv_discard_response(QTestState *s, const char *fmt, va_list ap); +void qtest_qmp_send_raw(QTestState *s, const char *fmt, ...) + GCC_FMT_ATTR(2, 3); /** * qtest_qmpv: * @s: #QTestState instance to operate on. - * @fmt: QMP message to send to QEMU + * @fmt: QMP message to send to QEMU, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * @ap: QMP message arguments * * Sends a QMP message to QEMU and returns the response. */ -QDict *qtest_qmpv(QTestState *s, const char *fmt, va_list ap); +QDict *qtest_vqmp(QTestState *s, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); /** - * qtest_async_qmpv: + * qtest_qmp_vsend: * @s: #QTestState instance to operate on. - * @fmt: QMP message to send to QEMU + * @fmt: QMP message to send to QEMU, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * @ap: QMP message arguments * * Sends a QMP message to QEMU and leaves the response in the stream. */ -void qtest_async_qmpv(QTestState *s, const char *fmt, va_list ap); +void qtest_qmp_vsend(QTestState *s, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); /** * qtest_receive: @@ -157,6 +172,23 @@ void qtest_qmp_eventwait(QTestState *s, const char *event); */ QDict *qtest_qmp_eventwait_ref(QTestState *s, const char *event); +/** + * qtest_qmp_receive_success: + * @s: #QTestState instance to operate on + * @event_cb: Event callback + * @opaque: Argument for @event_cb + * + * Poll QMP messages until a command success response is received. + * If @event_cb, call it for each event received, passing @opaque, + * the event's name and data. + * Return the success response's "return" member. + */ +QDict *qtest_qmp_receive_success(QTestState *s, + void (*event_cb)(void *opaque, + const char *name, + QDict *data), + void *opaque); + /** * qtest_hmp: * @s: #QTestState instance to operate on. @@ -172,7 +204,7 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** * qtest_hmpv: * @s: #QTestState instance to operate on. - * @fmt: HMP command to send to QEMU + * @fmt: HMP command to send to QEMU, formats arguments like vsprintf(). * @ap: HMP command arguments * * Send HMP command to QEMU via QMP's human-monitor-command. @@ -180,7 +212,8 @@ char *qtest_hmp(QTestState *s, const char *fmt, ...) GCC_FMT_ATTR(2, 3); * * Returns: the command's output. The caller should g_free() it. */ -char *qtest_hmpv(QTestState *s, const char *fmt, va_list ap); +char *qtest_vhmp(QTestState *s, const char *fmt, va_list ap) + GCC_FMT_ATTR(2, 0); /** * qtest_get_irq: @@ -211,6 +244,19 @@ void qtest_irq_intercept_in(QTestState *s, const char *string); */ void qtest_irq_intercept_out(QTestState *s, const char *string); +/** + * qtest_set_irq_in: + * @s: QTestState instance to operate on. + * @string: QOM path of a device + * @name: IRQ name + * @irq: IRQ number + * @level: IRQ level + * + * Force given device/irq GPIO-in pin to the given level. + */ +void qtest_set_irq_in(QTestState *s, const char *string, const char *name, + int irq, int level); + /** * qtest_outb: * @s: #QTestState instance to operate on. @@ -555,33 +601,32 @@ static inline QTestState *qtest_start(const char *args) */ static inline void qtest_end(void) { + if (!global_qtest) { + return; + } qtest_quit(global_qtest); global_qtest = NULL; } /** * qmp: - * @fmt...: QMP message to send to qemu + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * * Sends a QMP message to QEMU and returns the response. */ -QDict *qmp(const char *fmt, ...); +QDict *qmp(const char *fmt, ...) GCC_FMT_ATTR(1, 2); /** - * qmp_async: - * @fmt...: QMP message to send to qemu + * qmp_send: + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * * Sends a QMP message to QEMU and leaves the response in the stream. */ -void qmp_async(const char *fmt, ...); - -/** - * qmp_discard_response: - * @fmt...: QMP message to send to qemu - * - * Sends a QMP message to QEMU and consumes the response. - */ -void qmp_discard_response(const char *fmt, ...); +void qmp_send(const char *fmt, ...) GCC_FMT_ATTR(1, 2); /** * qmp_receive: @@ -940,24 +985,30 @@ static inline int64_t clock_set(int64_t val) } 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, ...); +void qmp_fd_vsend(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +void qmp_fd_send(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void qmp_fd_send_raw(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); +void qmp_fd_vsend_raw(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +QDict *qmp_fdv(int fd, const char *fmt, va_list ap) GCC_FMT_ATTR(2, 0); +QDict *qmp_fd(int fd, const char *fmt, ...) GCC_FMT_ATTR(2, 3); /** * qtest_cb_for_every_machine: * @cb: Pointer to the callback function + * @skip_old_versioned: true if versioned old machine types should be skipped * * Call a callback function for every name of all available machines. */ -void qtest_cb_for_every_machine(void (*cb)(const char *machine)); +void qtest_cb_for_every_machine(void (*cb)(const char *machine), + bool skip_old_versioned); /** * qtest_qmp_device_add: * @driver: Name of the device that should be added * @id: Identification string - * @fmt: printf-like format string for further options to device_add + * @fmt...: QMP message to send to qemu, formatted like + * qobject_from_jsonf_nofail(). See parse_escape() for what's + * supported after '%'. * * Generic hot-plugging test via the device_add QMP command. */ @@ -981,4 +1032,21 @@ void qtest_qmp_device_del(const char *id); */ bool qmp_rsp_is_err(QDict *rsp); +/** + * qmp_assert_error_class: + * @rsp: QMP response to check for error + * @class: an error class + * + * Assert the response has the given error class and discard @rsp. + */ +void qmp_assert_error_class(QDict *rsp, const char *class); + +/** + * qtest_probe_child: + * @s: QTestState instance to operate on. + * + * Returns: true if the child is still alive. + */ +bool qtest_probe_child(QTestState *s); + #endif diff --git a/tests/m25p80-test.c b/tests/m25p80-test.c index c276e738e9..055f7246a8 100644 --- a/tests/m25p80-test.c +++ b/tests/m25p80-test.c @@ -363,9 +363,9 @@ int main(int argc, char **argv) g_assert(ret == 0); close(fd); - global_qtest = qtest_startf("-m 256 -machine palmetto-bmc " - "-drive file=%s,format=raw,if=mtd", - tmp_path); + global_qtest = qtest_initf("-m 256 -machine palmetto-bmc " + "-drive file=%s,format=raw,if=mtd", + tmp_path); qtest_add_func("/m25p80/read_jedec", test_read_jedec); qtest_add_func("/m25p80/erase_sector", test_erase_sector); diff --git a/tests/m48t59-test.c b/tests/m48t59-test.c index 5b695971c7..b94a1230f7 100644 --- a/tests/m48t59-test.c +++ b/tests/m48t59-test.c @@ -146,7 +146,7 @@ static void cmos_get_date_time(QTestState *s, struct tm *date) static QTestState *m48t59_qtest_start(void) { - return qtest_startf("-M %s -rtc clock=vm", base_machine); + return qtest_initf("-M %s -rtc clock=vm", base_machine); } static void bcd_check_time(void) @@ -199,9 +199,9 @@ static void bcd_check_time(void) t = (long)mktime(datep); s = (long)mktime(&start); if (t < s) { - g_test_message("RTC is %ld second(s) behind wall-clock\n", (s - t)); + g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); } else { - g_test_message("RTC is %ld second(s) ahead of wall-clock\n", (t - s)); + g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); } g_assert_cmpint(ABS(t - s), <=, wiggle); diff --git a/tests/machine-none-test.c b/tests/machine-none-test.c index f286557b3e..4c6d470798 100644 --- a/tests/machine-none-test.c +++ b/tests/machine-none-test.c @@ -44,7 +44,6 @@ static struct arch2cpu cpus_map[] = { { "or1k", "or1200" }, { "ppc", "604" }, { "ppc64", "power8e_v2.1" }, - { "ppcemb", "440epb" }, { "s390x", "qemu" }, { "sh4", "sh7750r" }, { "sh4eb", "sh7751r" }, @@ -76,6 +75,7 @@ static void test_machine_cpu_cli(void) QDict *response; const char *arch = qtest_get_arch(); const char *cpu_model = get_cpu_model_by_arch(arch); + QTestState *qts; if (!cpu_model) { if (!(!strcmp(arch, "microblaze") || !strcmp(arch, "microblazeel"))) { @@ -84,13 +84,13 @@ static void test_machine_cpu_cli(void) } return; /* TODO: die here to force all targets have a test */ } - global_qtest = qtest_startf("-machine none -cpu '%s'", cpu_model); + qts = qtest_initf("-machine none -cpu '%s'", cpu_model); - response = qmp("{ 'execute': 'quit' }"); + response = qtest_qmp(qts, "{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); qobject_unref(response); - qtest_quit(global_qtest); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/megasas-test.c b/tests/megasas-test.c index 81837e14af..33aa97042c 100644 --- a/tests/megasas-test.c +++ b/tests/megasas-test.c @@ -10,55 +10,49 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qemu/bswap.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -static QOSState *qmegasas_start(const char *extra_opts) +typedef struct QMegasas QMegasas; + +struct QMegasas { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *megasas_get_driver(void *obj, const char *interface) { - QOSState *qs; - const char *arch = qtest_get_arch(); - const char *cmd = "-drive id=hd0,if=none,file=null-co://,format=raw " - "-device megasas,id=scsi0,addr=04.0 " - "-device scsi-hd,bus=scsi0.0,drive=hd0 %s"; + QMegasas *megasas = obj; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, extra_opts ? : ""); - global_qtest = qs->qts; - return qs; + if (!g_strcmp0(interface, "pci-device")) { + return &megasas->dev; } - g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); + fprintf(stderr, "%s not present in megasas\n", interface); + g_assert_not_reached(); } -static void qmegasas_stop(QOSState *qs) +static void *megasas_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - qtest_shutdown(qs); -} + QMegasas *megasas = g_new0(QMegasas, 1); + QPCIBus *bus = pci_bus; -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) -{ - QOSState *qs; + qpci_device_init(&megasas->dev, bus, addr); + megasas->obj.get_driver = megasas_get_driver; - qs = qmegasas_start(NULL); - qmegasas_stop(qs); + return &megasas->obj; } /* This used to cause a NULL pointer dereference. */ -static void megasas_pd_get_info_fuzz(void) +static void megasas_pd_get_info_fuzz(void *obj, void *data, QGuestAllocator *alloc) { - QPCIDevice *dev; - QOSState *qs; + QMegasas *megasas = obj; + QPCIDevice *dev = &megasas->dev; QPCIBar bar; uint32_t context[256]; uint64_t context_pa; int i; - qs = qmegasas_start(NULL); - dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4,0)); - g_assert(dev != NULL); - qpci_device_enable(dev); bar = qpci_iomap(dev, 0, NULL); @@ -71,19 +65,25 @@ static void megasas_pd_get_info_fuzz(void) context[6] = cpu_to_le32(0x02020000); context[7] = cpu_to_le32(0); - context_pa = qmalloc(qs, sizeof(context)); + context_pa = guest_alloc(alloc, sizeof(context)); memwrite(context_pa, context, sizeof(context)); qpci_io_writel(dev, bar, 0x40, context_pa); - - g_free(dev); - qmegasas_stop(qs); } -int main(int argc, char **argv) +static void megasas_register_nodes(void) { - g_test_init(&argc, &argv, NULL); - qtest_add_func("/megasas/pci/nop", pci_nop); - qtest_add_func("/megasas/dcmd/pd-get-info/fuzz", megasas_pd_get_info_fuzz); + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=scsi0", + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw", + .after_cmd_line = "-device scsi-hd,bus=scsi0.0,drive=drv0", + }; - return g_test_run(); + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("megasas", megasas_create); + qos_node_consumes("megasas", "pci-bus", &opts); + qos_node_produces("megasas", "pci-device"); + + qos_add_test("dcmd/pd-get-info/fuzz", "megasas", megasas_pd_get_info_fuzz, NULL); } +libqos_init(megasas_register_nodes); diff --git a/tests/microbit-test.c b/tests/microbit-test.c new file mode 100644 index 0000000000..04e199ec33 --- /dev/null +++ b/tests/microbit-test.c @@ -0,0 +1,507 @@ +/* + * QTest testcase for Microbit board using the Nordic Semiconductor nRF51 SoC. + * + * nRF51: + * Reference Manual: http://infocenter.nordicsemi.com/pdf/nRF51_RM_v3.0.pdf + * Product Spec: http://infocenter.nordicsemi.com/pdf/nRF51822_PS_v3.1.pdf + * + * Microbit Board: http://microbit.org/ + * + * Copyright 2018 Steffen Görtz + * + * 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 "exec/hwaddr.h" +#include "libqtest.h" + +#include "hw/arm/nrf51.h" +#include "hw/char/nrf51_uart.h" +#include "hw/gpio/nrf51_gpio.h" +#include "hw/nvram/nrf51_nvm.h" +#include "hw/timer/nrf51_timer.h" +#include "hw/i2c/microbit_i2c.h" + +static bool uart_wait_for_event(QTestState *qts, uint32_t event_addr) +{ + time_t now, start = time(NULL); + + while (true) { + if (qtest_readl(qts, event_addr) == 1) { + qtest_writel(qts, event_addr, 0x00); + return true; + } + + /* Wait at most 10 minutes */ + now = time(NULL); + if (now - start > 600) { + break; + } + g_usleep(10000); + } + + return false; +} + +static void uart_rw_to_rxd(QTestState *qts, int sock_fd, const char *in, + char *out) +{ + int i, in_len = strlen(in); + + g_assert_true(write(sock_fd, in, in_len) == in_len); + for (i = 0; i < in_len; i++) { + g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + + A_UART_RXDRDY)); + out[i] = qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD); + } + out[i] = '\0'; +} + +static void uart_w_to_txd(QTestState *qts, const char *in) +{ + int i, in_len = strlen(in); + + for (i = 0; i < in_len; i++) { + qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, in[i]); + g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + + A_UART_TXDRDY)); + } +} + +static void test_nrf51_uart(void) +{ + int sock_fd; + char s[10]; + QTestState *qts = qtest_init_with_serial("-M microbit", &sock_fd); + + g_assert_true(write(sock_fd, "c", 1) == 1); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 0x00); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_ENABLE, 0x04); + qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTRX, 0x01); + + g_assert_true(uart_wait_for_event(qts, NRF51_UART_BASE + A_UART_RXDRDY)); + qtest_writel(qts, NRF51_UART_BASE + A_UART_RXDRDY, 0x00); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_RXD), ==, 'c'); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENSET, 0x04); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x04); + qtest_writel(qts, NRF51_UART_BASE + A_UART_INTENCLR, 0x04); + g_assert_cmphex(qtest_readl(qts, NRF51_UART_BASE + A_UART_INTEN), ==, 0x00); + + uart_rw_to_rxd(qts, sock_fd, "hello", s); + g_assert_true(memcmp(s, "hello", 5) == 0); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); + uart_w_to_txd(qts, "d"); + g_assert_true(read(sock_fd, s, 10) == 1); + g_assert_cmphex(s[0], ==, 'd'); + + qtest_writel(qts, NRF51_UART_BASE + A_UART_SUSPEND, 0x01); + qtest_writel(qts, NRF51_UART_BASE + A_UART_TXD, 'h'); + qtest_writel(qts, NRF51_UART_BASE + A_UART_STARTTX, 0x01); + uart_w_to_txd(qts, "world"); + g_assert_true(read(sock_fd, s, 10) == 5); + g_assert_true(memcmp(s, "world", 5) == 0); + + close(sock_fd); + + qtest_quit(qts); +} + +/* Read a byte from I2C device at @addr from register @reg */ +static uint32_t i2c_read_byte(QTestState *qts, uint32_t addr, uint32_t reg) +{ + uint32_t val; + + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ADDRESS, addr); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTTX, 1); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_TXD, reg); + val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_TXDSENT); + g_assert_cmpuint(val, ==, 1); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); + + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STARTRX, 1); + val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_EVENT_RXDREADY); + g_assert_cmpuint(val, ==, 1); + val = qtest_readl(qts, NRF51_TWI_BASE + NRF51_TWI_REG_RXD); + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_TASK_STOP, 1); + + return val; +} + +static void test_microbit_i2c(void) +{ + uint32_t val; + QTestState *qts = qtest_init("-M microbit"); + + /* We don't program pins/irqs but at least enable the device */ + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 5); + + /* MMA8653 magnetometer detection */ + val = i2c_read_byte(qts, 0x3A, 0x0D); + g_assert_cmpuint(val, ==, 0x5A); + + val = i2c_read_byte(qts, 0x3A, 0x0D); + g_assert_cmpuint(val, ==, 0x5A); + + /* LSM303 accelerometer detection */ + val = i2c_read_byte(qts, 0x3C, 0x4F); + g_assert_cmpuint(val, ==, 0x40); + + qtest_writel(qts, NRF51_TWI_BASE + NRF51_TWI_REG_ENABLE, 0); + + qtest_quit(qts); +} + +#define FLASH_SIZE (256 * NRF51_PAGE_SIZE) + +static void fill_and_erase(QTestState *qts, hwaddr base, hwaddr size, + uint32_t address_reg) +{ + hwaddr i; + + /* Erase Page */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + address_reg, base); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + /* Check memory */ + for (i = 0; i < size / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, 0xFFFFFFFF); + } + + /* Fill memory */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); + for (i = 0; i < size / 4; i++) { + qtest_writel(qts, base + i * 4, i); + g_assert_cmpuint(qtest_readl(qts, base + i * 4), ==, i); + } + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); +} + +static void test_nrf51_nvmc(void) +{ + uint32_t value; + hwaddr i; + QTestState *qts = qtest_init("-M microbit"); + + /* Test always ready */ + value = qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_READY); + g_assert_cmpuint(value & 0x01, ==, 0x01); + + /* Test write-read config register */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x03); + g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), + ==, 0x03); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + g_assert_cmpuint(qtest_readl(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG), + ==, 0x00); + + /* Test PCR0 */ + fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, + NRF51_NVMC_ERASEPCR0); + fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, + NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR0); + + /* Test PCR1 */ + fill_and_erase(qts, NRF51_FLASH_BASE, NRF51_PAGE_SIZE, + NRF51_NVMC_ERASEPCR1); + fill_and_erase(qts, NRF51_FLASH_BASE + NRF51_PAGE_SIZE, + NRF51_PAGE_SIZE, NRF51_NVMC_ERASEPCR1); + + /* Erase all */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); + for (i = 0; i < FLASH_SIZE / 4; i++) { + qtest_writel(qts, NRF51_FLASH_BASE + i * 4, i); + g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), ==, i); + } + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEALL, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + for (i = 0; i < FLASH_SIZE / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, NRF51_FLASH_BASE + i * 4), + ==, 0xFFFFFFFF); + } + + /* Erase UICR */ + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), + ==, 0xFFFFFFFF); + } + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x01); + for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { + qtest_writel(qts, NRF51_UICR_BASE + i * 4, i); + g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), ==, i); + } + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x02); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_ERASEUICR, 0x01); + qtest_writel(qts, NRF51_NVMC_BASE + NRF51_NVMC_CONFIG, 0x00); + + for (i = 0; i < NRF51_UICR_SIZE / 4; i++) { + g_assert_cmpuint(qtest_readl(qts, NRF51_UICR_BASE + i * 4), + ==, 0xFFFFFFFF); + } + + qtest_quit(qts); +} + +static void test_nrf51_gpio(void) +{ + size_t i; + uint32_t actual, expected; + + struct { + hwaddr addr; + uint32_t expected; + } const reset_state[] = { + {NRF51_GPIO_REG_OUT, 0x00000000}, {NRF51_GPIO_REG_OUTSET, 0x00000000}, + {NRF51_GPIO_REG_OUTCLR, 0x00000000}, {NRF51_GPIO_REG_IN, 0x00000000}, + {NRF51_GPIO_REG_DIR, 0x00000000}, {NRF51_GPIO_REG_DIRSET, 0x00000000}, + {NRF51_GPIO_REG_DIRCLR, 0x00000000} + }; + + QTestState *qts = qtest_init("-M microbit"); + + /* Check reset state */ + for (i = 0; i < ARRAY_SIZE(reset_state); i++) { + expected = reset_state[i].expected; + actual = qtest_readl(qts, NRF51_GPIO_BASE + reset_state[i].addr); + g_assert_cmpuint(actual, ==, expected); + } + + for (i = 0; i < NRF51_GPIO_PINS; i++) { + expected = 0x00000002; + actual = qtest_readl(qts, NRF51_GPIO_BASE + + NRF51_GPIO_REG_CNF_START + i * 4); + g_assert_cmpuint(actual, ==, expected); + } + + /* Check dir bit consistency between dir and cnf */ + /* Check set via DIRSET */ + expected = 0x80000001; + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRSET, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) + & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + /* Check clear via DIRCLR */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIRCLR, 0x80000001); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, 0x00000000); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) + & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + + /* Check set via DIR */ + expected = 0x80000001; + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR); + g_assert_cmpuint(actual, ==, expected); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START) + & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_END) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + /* Reset DIR */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_DIR, 0x00000000); + + /* Check Input propagates */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x00); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + + /* Check pull-up working */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b1110); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + + /* Check pull-down working */ + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 1); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0000); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0110); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0x02); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, -1); + + /* Check Output propagates */ + qtest_irq_intercept_out(qts, "/machine/nrf51"); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b0011); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + g_assert_true(qtest_get_irq(qts, 0)); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); + g_assert_false(qtest_get_irq(qts, 0)); + + /* Check self-stimulation */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x01); + + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTCLR, 0x01); + actual = qtest_readl(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_IN) & 0x01; + g_assert_cmpuint(actual, ==, 0x00); + + /* + * Check short-circuit - generates an guest_error which must be checked + * manually as long as qtest can not scan qemu_log messages + */ + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_CNF_START, 0b01); + qtest_writel(qts, NRF51_GPIO_BASE + NRF51_GPIO_REG_OUTSET, 0x01); + qtest_set_irq_in(qts, "/machine/nrf51", "unnamed-gpio-in", 0, 0); + + qtest_quit(qts); +} + +static void timer_task(QTestState *qts, hwaddr task) +{ + qtest_writel(qts, NRF51_TIMER_BASE + task, NRF51_TRIGGER_TASK); +} + +static void timer_clear_event(QTestState *qts, hwaddr event) +{ + qtest_writel(qts, NRF51_TIMER_BASE + event, NRF51_EVENT_CLEAR); +} + +static void timer_set_bitmode(QTestState *qts, uint8_t mode) +{ + qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_BITMODE, mode); +} + +static void timer_set_prescaler(QTestState *qts, uint8_t prescaler) +{ + qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_PRESCALER, prescaler); +} + +static void timer_set_cc(QTestState *qts, size_t idx, uint32_t value) +{ + qtest_writel(qts, NRF51_TIMER_BASE + NRF51_TIMER_REG_CC0 + idx * 4, value); +} + +static void timer_assert_events(QTestState *qts, uint32_t ev0, uint32_t ev1, + uint32_t ev2, uint32_t ev3) +{ + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_0) + == ev0); + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_1) + == ev1); + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_2) + == ev2); + g_assert(qtest_readl(qts, NRF51_TIMER_BASE + NRF51_TIMER_EVENT_COMPARE_3) + == ev3); +} + +static void test_nrf51_timer(void) +{ + uint32_t steps_to_overflow = 408; + QTestState *qts = qtest_init("-M microbit"); + + /* Compare Match */ + timer_task(qts, NRF51_TIMER_TASK_STOP); + timer_task(qts, NRF51_TIMER_TASK_CLEAR); + + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); + + timer_set_bitmode(qts, NRF51_TIMER_WIDTH_16); /* 16 MHz Timer */ + timer_set_prescaler(qts, 0); + /* Swept over in first step */ + timer_set_cc(qts, 0, 2); + /* Barely miss on first step */ + timer_set_cc(qts, 1, 162); + /* Spot on on third step */ + timer_set_cc(qts, 2, 480); + + timer_assert_events(qts, 0, 0, 0, 0); + + timer_task(qts, NRF51_TIMER_TASK_START); + qtest_clock_step(qts, 10000); + timer_assert_events(qts, 1, 0, 0, 0); + + /* Swept over on first overflow */ + timer_set_cc(qts, 3, 114); + + qtest_clock_step(qts, 10000); + timer_assert_events(qts, 1, 1, 0, 0); + + qtest_clock_step(qts, 10000); + timer_assert_events(qts, 1, 1, 1, 0); + + /* Wrap time until internal counter overflows */ + while (steps_to_overflow--) { + timer_assert_events(qts, 1, 1, 1, 0); + qtest_clock_step(qts, 10000); + } + + timer_assert_events(qts, 1, 1, 1, 1); + + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_0); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_1); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_2); + timer_clear_event(qts, NRF51_TIMER_EVENT_COMPARE_3); + timer_assert_events(qts, 0, 0, 0, 0); + + timer_task(qts, NRF51_TIMER_TASK_STOP); + + /* Test Proposal: Stop/Shutdown */ + /* Test Proposal: Shortcut Compare -> Clear */ + /* Test Proposal: Shortcut Compare -> Stop */ + /* Test Proposal: Counter Mode */ + + qtest_quit(qts); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/microbit/nrf51/uart", test_nrf51_uart); + qtest_add_func("/microbit/nrf51/gpio", test_nrf51_gpio); + qtest_add_func("/microbit/nrf51/nvmc", test_nrf51_nvmc); + qtest_add_func("/microbit/nrf51/timer", test_nrf51_timer); + qtest_add_func("/microbit/microbit/i2c", test_microbit_i2c); + + return g_test_run(); +} diff --git a/tests/migration-test.c b/tests/migration-test.c index e079e0bdb6..bd3f5c3125 100644 --- a/tests/migration-test.c +++ b/tests/migration-test.c @@ -14,14 +14,23 @@ #include "libqtest.h" #include "qapi/qmp/qdict.h" +#include "qapi/qmp/qjson.h" #include "qemu/option.h" #include "qemu/range.h" #include "qemu/sockets.h" #include "chardev/char.h" #include "sysemu/sysemu.h" +#include "qapi/qapi-visit-sockets.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qobject-output-visitor.h" -const unsigned start_address = 1024 * 1024; -const unsigned end_address = 100 * 1024 * 1024; +#include "migration/migration-test.h" + +/* TODO actually test the results and get rid of this */ +#define qtest_qmp_discard_response(...) qobject_unref(qtest_qmp(__VA_ARGS__)) + +unsigned start_address; +unsigned end_address; bool got_stop; static bool uffd_feature_thread_id; @@ -76,16 +85,28 @@ static bool ufd_version_check(void) static const char *tmpfs; -/* A simple PC boot sector that modifies memory (1-100MB) quickly - * outputting a 'B' every so often if it's still running. +/* The boot file modifies memory area in [start_address, end_address) + * repeatedly. It outputs a 'B' at a fixed rate while it's still running. */ -#include "tests/migration/x86-a-b-bootblock.h" +#include "tests/migration/i386/a-b-bootblock.h" +#include "tests/migration/aarch64/a-b-kernel.h" -static void init_bootfile_x86(const char *bootpath) +static void init_bootfile(const char *bootpath, void *content) { FILE *bootfile = fopen(bootpath, "wb"); - g_assert_cmpint(fwrite(x86_bootsect, 512, 1, bootfile), ==, 1); + g_assert_cmpint(fwrite(content, 512, 1, bootfile), ==, 1); + fclose(bootfile); +} + +#include "tests/migration/s390x/a-b-bios.h" + +static void init_bootfile_s390x(const char *bootpath) +{ + FILE *bootfile = fopen(bootpath, "wb"); + size_t len = sizeof(s390x_elf); + + g_assert_cmpint(fwrite(s390x_elf, len, 1, bootfile), ==, 1); fclose(bootfile); } @@ -146,26 +167,26 @@ static void wait_for_serial(const char *side) } while (true); } +static void stop_cb(void *opaque, const char *name, QDict *data) +{ + if (!strcmp(name, "STOP")) { + got_stop = true; + } +} + /* * Events can get in the way of responses we are actually waiting for. */ -static QDict *wait_command(QTestState *who, const char *command) +GCC_FMT_ATTR(2, 3) +static QDict *wait_command(QTestState *who, const char *command, ...) { - const char *event_string; - QDict *response; + va_list ap; - response = qtest_qmp(who, command); + va_start(ap, command); + qtest_qmp_vsend(who, command, ap); + va_end(ap); - while (qdict_haskey(response, "event")) { - /* OK, it was an event */ - event_string = qdict_get_str(response, "event"); - if (!strcmp(event_string, "STOP")) { - got_stop = true; - } - qobject_unref(response); - response = qtest_qmp_receive(who); - } - return response; + return qtest_qmp_receive_success(who, stop_cb, NULL); } /* @@ -174,15 +195,7 @@ static QDict *wait_command(QTestState *who, const char *command) */ static QDict *migrate_query(QTestState *who) { - QDict *rsp, *rsp_return; - - rsp = wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - g_assert(rsp_return); - qobject_ref(rsp_return); - qobject_unref(rsp); - - return rsp_return; + return wait_command(who, "{ 'execute': 'query-migrate' }"); } /* @@ -205,10 +218,10 @@ static gchar *migrate_query_status(QTestState *who) * events suddenly appearing confuse the qmp()/hmp() responses. */ -static uint64_t get_migration_pass(QTestState *who) +static int64_t read_ram_property_int(QTestState *who, const char *property) { QDict *rsp_return, *rsp_ram; - uint64_t result; + int64_t result; rsp_return = migrate_query(who); if (!qdict_haskey(rsp_return, "ram")) { @@ -216,12 +229,17 @@ static uint64_t get_migration_pass(QTestState *who) result = 0; } else { rsp_ram = qdict_get_qdict(rsp_return, "ram"); - result = qdict_get_try_int(rsp_ram, "dirty-sync-count", 0); + result = qdict_get_try_int(rsp_ram, property, 0); } qobject_unref(rsp_return); return result; } +static uint64_t get_migration_pass(QTestState *who) +{ + return read_ram_property_int(who, "dirty-sync-count"); +} + static void read_blocktime(QTestState *who) { QDict *rsp_return; @@ -274,11 +292,11 @@ static void wait_for_migration_pass(QTestState *who) static void check_guests_ram(QTestState *who) { /* Our ASM test will have been incrementing one byte from each page from - * 1MB to <100MB in order. - * This gives us a constraint that any page's byte should be equal or less - * than the previous pages byte (mod 256); and they should all be equal - * except for one transition at the point where we meet the incrementer. - * (We're running this with the guest stopped). + * start_address to < end_address in order. This gives us a constraint + * that any page's byte should be equal or less than the previous pages + * byte (mod 256); and they should all be equal except for one transition + * at the point where we meet the incrementer. (We're running this with + * the guest stopped). */ unsigned address; uint8_t first_byte; @@ -289,7 +307,8 @@ static void check_guests_ram(QTestState *who) qtest_memread(who, start_address, &first_byte, 1); last_byte = first_byte; - for (address = start_address + 4096; address < end_address; address += 4096) + for (address = start_address + TEST_MEM_PAGE_SIZE; address < end_address; + address += TEST_MEM_PAGE_SIZE) { uint8_t b; qtest_memread(who, address, &b, 1); @@ -321,32 +340,87 @@ static void cleanup(const char *filename) g_free(path); } -static void migrate_check_parameter(QTestState *who, const char *parameter, - const char *value) +static char *get_shmem_opts(const char *mem_size, const char *shmem_path) { - QDict *rsp, *rsp_return; + return g_strdup_printf("-object memory-backend-file,id=mem0,size=%s" + ",mem-path=%s,share=on -numa node,memdev=mem0", + mem_size, shmem_path); +} + +static char *SocketAddress_to_str(SocketAddress *addr) +{ + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("tcp:%s:%s", + addr->u.inet.host, + addr->u.inet.port); + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("unix:%s", + addr->u.q_unix.path); + case SOCKET_ADDRESS_TYPE_FD: + return g_strdup_printf("fd:%s", addr->u.fd.str); + case SOCKET_ADDRESS_TYPE_VSOCK: + return g_strdup_printf("tcp:%s:%s", + addr->u.vsock.cid, + addr->u.vsock.port); + default: + return g_strdup("unknown address type"); + } +} + +static char *migrate_get_socket_address(QTestState *who, const char *parameter) +{ + QDict *rsp; char *result; + Error *local_err = NULL; + SocketAddressList *addrs; + Visitor *iv = NULL; + QObject *object; + + rsp = migrate_query(who); + object = qdict_get(rsp, parameter); + + iv = qobject_input_visitor_new(object); + visit_type_SocketAddressList(iv, NULL, &addrs, &local_err); + visit_free(iv); + + /* we are only using a single address */ + result = SocketAddress_to_str(addrs->value); + + qapi_free_SocketAddressList(addrs); + qobject_unref(rsp); + return result; +} + +static long long migrate_get_parameter(QTestState *who, const char *parameter) +{ + QDict *rsp; + long long result; rsp = wait_command(who, "{ 'execute': 'query-migrate-parameters' }"); - rsp_return = qdict_get_qdict(rsp, "return"); - result = g_strdup_printf("%" PRId64, - qdict_get_try_int(rsp_return, parameter, -1)); - g_assert_cmpstr(result, ==, value); - g_free(result); + result = qdict_get_int(rsp, parameter); qobject_unref(rsp); + return result; +} + +static void migrate_check_parameter(QTestState *who, const char *parameter, + long long value) +{ + long long result; + + result = migrate_get_parameter(who, parameter); + g_assert_cmpint(result, ==, value); } static void migrate_set_parameter(QTestState *who, const char *parameter, - const char *value) + long long value) { QDict *rsp; - gchar *cmd; - cmd = g_strdup_printf("{ 'execute': 'migrate-set-parameters'," - "'arguments': { '%s': %s } }", - parameter, value); - rsp = qtest_qmp(who, cmd); - g_free(cmd); + rsp = qtest_qmp(who, + "{ 'execute': 'migrate-set-parameters'," + "'arguments': { %s: %lld } }", + parameter, value); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); migrate_check_parameter(who, parameter, value); @@ -357,51 +431,55 @@ static void migrate_pause(QTestState *who) QDict *rsp; rsp = wait_command(who, "{ 'execute': 'migrate-pause' }"); - g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); } static void migrate_recover(QTestState *who, const char *uri) { QDict *rsp; - gchar *cmd = g_strdup_printf( - "{ 'execute': 'migrate-recover', " - " 'id': 'recover-cmd', " - " 'arguments': { 'uri': '%s' } }", uri); - rsp = wait_command(who, cmd); - g_assert(qdict_haskey(rsp, "return")); - g_free(cmd); + rsp = wait_command(who, + "{ 'execute': 'migrate-recover', " + " 'id': 'recover-cmd', " + " 'arguments': { 'uri': %s } }", + uri); qobject_unref(rsp); } static void migrate_set_capability(QTestState *who, const char *capability, - const char *value) + bool value) { QDict *rsp; - gchar *cmd; - cmd = g_strdup_printf("{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': '%s', 'state': %s } ] } }", - capability, value); - rsp = qtest_qmp(who, cmd); - g_free(cmd); + rsp = qtest_qmp(who, + "{ 'execute': 'migrate-set-capabilities'," + "'arguments': { " + "'capabilities': [ { " + "'capability': %s, 'state': %i } ] } }", + capability, value); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); } -static void migrate(QTestState *who, const char *uri, const char *extra) +/* + * Send QMP command "migrate". + * Arguments are built from @fmt... (formatted like + * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. + */ +GCC_FMT_ATTR(3, 4) +static void migrate(QTestState *who, const char *uri, const char *fmt, ...) { - QDict *rsp; - gchar *cmd; + va_list ap; + QDict *args, *rsp; - cmd = g_strdup_printf("{ 'execute': 'migrate'," - " 'arguments': { 'uri': '%s' %s } }", - uri, extra ? extra : ""); - rsp = qtest_qmp(who, cmd); - g_free(cmd); + va_start(ap, fmt); + args = qdict_from_vjsonf_nofail(fmt, ap); + va_end(ap); + + g_assert(!qdict_haskey(args, "uri")); + qdict_put_str(args, "uri", uri); + + rsp = qmp("{ 'execute': 'migrate', 'arguments': %p}", args); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); } @@ -411,7 +489,6 @@ static void migrate_postcopy_start(QTestState *from, QTestState *to) QDict *rsp; rsp = wait_command(from, "{ 'execute': 'migrate-start-postcopy' }"); - g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); if (!got_stop) { @@ -422,56 +499,106 @@ static void migrate_postcopy_start(QTestState *from, QTestState *to) } static int test_migrate_start(QTestState **from, QTestState **to, - const char *uri, bool hide_stderr) + const char *uri, bool hide_stderr, + bool use_shmem) { gchar *cmd_src, *cmd_dst; - char *bootpath = g_strdup_printf("%s/bootsect", tmpfs); + char *bootpath = NULL; + char *extra_opts = NULL; + char *shmem_path = NULL; const char *arch = qtest_get_arch(); const char *accel = "kvm:tcg"; - got_stop = false; + if (use_shmem) { + if (!g_file_test("/dev/shm", G_FILE_TEST_IS_DIR)) { + g_test_skip("/dev/shm is not supported"); + return -1; + } + shmem_path = g_strdup_printf("/dev/shm/qemu-%d", getpid()); + } + got_stop = false; + bootpath = g_strdup_printf("%s/bootsect", tmpfs); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - init_bootfile_x86(bootpath); + init_bootfile(bootpath, x86_bootsect); + extra_opts = use_shmem ? get_shmem_opts("150M", shmem_path) : NULL; cmd_src = g_strdup_printf("-machine accel=%s -m 150M" " -name source,debug-threads=on" " -serial file:%s/src_serial" - " -drive file=%s,format=raw", - accel, tmpfs, bootpath); + " -drive file=%s,format=raw %s", + accel, tmpfs, bootpath, + extra_opts ? extra_opts : ""); cmd_dst = g_strdup_printf("-machine accel=%s -m 150M" " -name target,debug-threads=on" " -serial file:%s/dest_serial" " -drive file=%s,format=raw" - " -incoming %s", - accel, tmpfs, bootpath, uri); + " -incoming %s %s", + accel, tmpfs, bootpath, uri, + extra_opts ? extra_opts : ""); + start_address = X86_TEST_MEM_START; + end_address = X86_TEST_MEM_END; + } else if (g_str_equal(arch, "s390x")) { + init_bootfile_s390x(bootpath); + extra_opts = use_shmem ? get_shmem_opts("128M", shmem_path) : NULL; + cmd_src = g_strdup_printf("-machine accel=%s -m 128M" + " -name source,debug-threads=on" + " -serial file:%s/src_serial -bios %s %s", + accel, tmpfs, bootpath, + extra_opts ? extra_opts : ""); + cmd_dst = g_strdup_printf("-machine accel=%s -m 128M" + " -name target,debug-threads=on" + " -serial file:%s/dest_serial -bios %s" + " -incoming %s %s", + accel, tmpfs, bootpath, uri, + extra_opts ? extra_opts : ""); + start_address = S390_TEST_MEM_START; + end_address = S390_TEST_MEM_END; } else if (strcmp(arch, "ppc64") == 0) { - - /* On ppc64, the test only works with kvm-hv, but not with kvm-pr - * and TCG is touchy due to race conditions on dirty bits - * (especially on PPC for some reason) - */ - if (access("/sys/module/kvm_hv", F_OK)) { - g_print("Skipping test: kvm_hv not available "); - return -1; - } - cmd_src = g_strdup_printf("-machine accel=%s -m 256M" + extra_opts = use_shmem ? get_shmem_opts("256M", shmem_path) : NULL; + cmd_src = g_strdup_printf("-machine accel=%s -m 256M -nodefaults" " -name source,debug-threads=on" " -serial file:%s/src_serial" - " -prom-env '" - "boot-command=hex .\" _\" begin %x %x " + " -prom-env 'use-nvramrc?=true' -prom-env " + "'nvramrc=hex .\" _\" begin %x %x " "do i c@ 1 + i c! 1000 +loop .\" B\" 0 " - "until'", accel, tmpfs, end_address, - start_address); + "until' %s", accel, tmpfs, end_address, + start_address, extra_opts ? extra_opts : ""); cmd_dst = g_strdup_printf("-machine accel=%s -m 256M" " -name target,debug-threads=on" " -serial file:%s/dest_serial" - " -incoming %s", - accel, tmpfs, uri); + " -incoming %s %s", + accel, tmpfs, uri, + extra_opts ? extra_opts : ""); + + start_address = PPC_TEST_MEM_START; + end_address = PPC_TEST_MEM_END; + } else if (strcmp(arch, "aarch64") == 0) { + init_bootfile(bootpath, aarch64_kernel); + extra_opts = use_shmem ? get_shmem_opts("150M", shmem_path) : NULL; + cmd_src = g_strdup_printf("-machine virt,accel=%s,gic-version=max " + "-name vmsource,debug-threads=on -cpu max " + "-m 150M -serial file:%s/src_serial " + "-kernel %s %s", + accel, tmpfs, bootpath, + extra_opts ? extra_opts : ""); + cmd_dst = g_strdup_printf("-machine virt,accel=%s,gic-version=max " + "-name vmdest,debug-threads=on -cpu max " + "-m 150M -serial file:%s/dest_serial " + "-kernel %s " + "-incoming %s %s", + accel, tmpfs, bootpath, uri, + extra_opts ? extra_opts : ""); + + start_address = ARM_TEST_MEM_START; + end_address = ARM_TEST_MEM_END; + + g_assert(sizeof(aarch64_kernel) <= ARM_TEST_MAX_KERNEL_SIZE); } else { g_assert_not_reached(); } g_free(bootpath); + g_free(extra_opts); if (hide_stderr) { gchar *tmp; @@ -489,6 +616,16 @@ static int test_migrate_start(QTestState **from, QTestState **to, *to = qtest_init(cmd_dst); g_free(cmd_dst); + + /* + * Remove shmem file immediately to avoid memory leak in test failed case. + * It's valid becase QEMU has already opened this file + */ + if (use_shmem) { + unlink(shmem_path); + g_free(shmem_path); + } + return 0; } @@ -529,44 +666,46 @@ static void test_migrate_end(QTestState *from, QTestState *to, bool test_dest) static void deprecated_set_downtime(QTestState *who, const double value) { QDict *rsp; - gchar *cmd; - char *expected; - int64_t result_int; - cmd = g_strdup_printf("{ 'execute': 'migrate_set_downtime'," - "'arguments': { 'value': %g } }", value); - rsp = qtest_qmp(who, cmd); - g_free(cmd); + rsp = qtest_qmp(who, + "{ 'execute': 'migrate_set_downtime'," + " 'arguments': { 'value': %f } }", value); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); - result_int = value * 1000L; - expected = g_strdup_printf("%" PRId64, result_int); - migrate_check_parameter(who, "downtime-limit", expected); - g_free(expected); + migrate_check_parameter(who, "downtime-limit", value * 1000); } -static void deprecated_set_speed(QTestState *who, const char *value) +static void deprecated_set_speed(QTestState *who, long long value) { QDict *rsp; - gchar *cmd; - cmd = g_strdup_printf("{ 'execute': 'migrate_set_speed'," - "'arguments': { 'value': %s } }", value); - rsp = qtest_qmp(who, cmd); - g_free(cmd); + rsp = qtest_qmp(who, "{ 'execute': 'migrate_set_speed'," + "'arguments': { 'value': %lld } }", value); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); migrate_check_parameter(who, "max-bandwidth", value); } +static void deprecated_set_cache_size(QTestState *who, long long value) +{ + QDict *rsp; + + rsp = qtest_qmp(who, "{ 'execute': 'migrate-set-cache-size'," + "'arguments': { 'value': %lld } }", value); + g_assert(qdict_haskey(rsp, "return")); + qobject_unref(rsp); + migrate_check_parameter(who, "xbzrle-cache-size", value); +} + static void test_deprecated(void) { QTestState *from; - from = qtest_start(""); + from = qtest_start("-machine none"); deprecated_set_downtime(from, 0.12345); - deprecated_set_speed(from, "12345"); + deprecated_set_speed(from, 12345); + deprecated_set_cache_size(from, 4096); qtest_quit(from); } @@ -578,25 +717,25 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, hide_error)) { + if (test_migrate_start(&from, &to, uri, hide_error, false)) { return -1; } - migrate_set_capability(from, "postcopy-ram", "true"); - migrate_set_capability(to, "postcopy-ram", "true"); - migrate_set_capability(to, "postcopy-blocktime", "true"); + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-blocktime", true); /* We want to pick a speed slow enough that the test completes * quickly, but that it doesn't complete precopy even on a slow * machine, so also set the downtime. */ - migrate_set_parameter(from, "max-bandwidth", "100000000"); - migrate_set_parameter(from, "downtime-limit", "1"); + migrate_set_parameter(from, "max-bandwidth", 100000000); + migrate_set_parameter(from, "downtime-limit", 1); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, NULL); + migrate(from, uri, "{}"); g_free(uri); wait_for_migration_pass(from); @@ -642,7 +781,7 @@ static void test_postcopy_recovery(void) } /* Turn postcopy speed down, 4K/s is slow enough on any machines */ - migrate_set_parameter(from, "max-postcopy-bandwidth", "4096"); + migrate_set_parameter(from, "max-postcopy-bandwidth", 4096); /* Now we start the postcopy */ migrate_postcopy_start(from, to); @@ -679,11 +818,11 @@ static void test_postcopy_recovery(void) * the newly created channel */ wait_for_migration_status(from, "postcopy-paused"); - migrate(from, uri, ", 'resume': true"); + migrate(from, uri, "{'resume': true}"); g_free(uri); /* Restore the postcopy bandwidth to unlimited */ - migrate_set_parameter(from, "max-postcopy-bandwidth", "0"); + migrate_set_parameter(from, "max-postcopy-bandwidth", 0); migrate_postcopy_complete(from, to); } @@ -691,14 +830,14 @@ static void test_postcopy_recovery(void) static void test_baddest(void) { QTestState *from, *to; - QDict *rsp, *rsp_return; + QDict *rsp_return; char *status; bool failed; - if (test_migrate_start(&from, &to, "tcp:0:0", true)) { + if (test_migrate_start(&from, &to, "tcp:0:0", true, false)) { return; } - migrate(from, "tcp:0:0", NULL); + migrate(from, "tcp:0:0", "{}"); do { status = migrate_query_status(from); g_assert(!strcmp(status, "setup") || !(strcmp(status, "failed"))); @@ -707,12 +846,10 @@ static void test_baddest(void) } while (!failed); /* Is the machine currently running? */ - rsp = wait_command(from, "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp, "return")); - rsp_return = qdict_get_qdict(rsp, "return"); + rsp_return = wait_command(from, "{ 'execute': 'query-status' }"); g_assert(qdict_haskey(rsp_return, "running")); g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp); + qobject_unref(rsp_return); test_migrate_end(from, to, false); } @@ -722,7 +859,7 @@ static void test_precopy_unix(void) char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); QTestState *from, *to; - if (test_migrate_start(&from, &to, uri, false)) { + if (test_migrate_start(&from, &to, uri, false, false)) { return; } @@ -731,19 +868,19 @@ static void test_precopy_unix(void) * machine, so also set the downtime. */ /* 1 ms should make it not converge*/ - migrate_set_parameter(from, "downtime-limit", "1"); + migrate_set_parameter(from, "downtime-limit", 1); /* 1GB/s */ - migrate_set_parameter(from, "max-bandwidth", "1000000000"); + migrate_set_parameter(from, "max-bandwidth", 1000000000); /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); - migrate(from, uri, NULL); + migrate(from, uri, "{}"); wait_for_migration_pass(from); /* 300 ms should converge */ - migrate_set_parameter(from, "downtime-limit", "300"); + migrate_set_parameter(from, "downtime-limit", 300); if (!got_stop) { qtest_qmp_eventwait(from, "STOP"); @@ -758,6 +895,138 @@ static void test_precopy_unix(void) g_free(uri); } +#if 0 +/* Currently upset on aarch64 TCG */ +static void test_ignore_shared(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, false, true)) { + return; + } + + migrate_set_capability(from, "x-ignore-shared", true); + migrate_set_capability(to, "x-ignore-shared", true); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate(from, uri, "{}"); + + wait_for_migration_pass(from); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + /* Check whether shared RAM has been really skipped */ + g_assert_cmpint(read_ram_property_int(from, "transferred"), <, 1024 * 1024); + + test_migrate_end(from, to, true); + g_free(uri); +} +#endif + +static void test_xbzrle(const char *uri) +{ + QTestState *from, *to; + + if (test_migrate_start(&from, &to, uri, false, false)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter(from, "max-bandwidth", 1000000000); + + migrate_set_parameter(from, "xbzrle-cache-size", 33554432); + + migrate_set_capability(from, "xbzrle", "true"); + migrate_set_capability(to, "xbzrle", "true"); + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + migrate(from, uri, "{}"); + + wait_for_migration_pass(from); + + /* 300ms should converge */ + migrate_set_parameter(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); +} + +static void test_xbzrle_unix(void) +{ + char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); + + test_xbzrle(uri); + g_free(uri); +} + +static void test_precopy_tcp(void) +{ + char *uri; + QTestState *from, *to; + + if (test_migrate_start(&from, &to, "tcp:127.0.0.1:0", false, false)) { + return; + } + + /* + * We want to pick a speed slow enough that the test completes + * quickly, but that it doesn't complete precopy even on a slow + * machine, so also set the downtime. + */ + /* 1 ms should make it not converge*/ + migrate_set_parameter(from, "downtime-limit", 1); + /* 1GB/s */ + migrate_set_parameter(from, "max-bandwidth", 1000000000); + + /* Wait for the first serial output from the source */ + wait_for_serial("src_serial"); + + uri = migrate_get_socket_address(to, "socket-address"); + + migrate(from, uri, "{}"); + + wait_for_migration_pass(from); + + /* 300ms should converge */ + migrate_set_parameter(from, "downtime-limit", 300); + + if (!got_stop) { + qtest_qmp_eventwait(from, "STOP"); + } + qtest_qmp_eventwait(to, "RESUME"); + + wait_for_serial("dest_serial"); + wait_for_migration_complete(from); + + test_migrate_end(from, to, true); + g_free(uri); +} + int main(int argc, char **argv) { char template[] = "/tmp/migration-test-XXXXXX"; @@ -766,12 +1035,39 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); if (!ufd_version_check()) { - return 0; + return g_test_run(); + } + + /* + * On ppc64, the test only works with kvm-hv, but not with kvm-pr and TCG + * is touchy due to race conditions on dirty bits (especially on PPC for + * some reason) + */ + if (g_str_equal(qtest_get_arch(), "ppc64") && + access("/sys/module/kvm_hv", F_OK)) { + g_test_message("Skipping test: kvm_hv not available"); + return g_test_run(); + } + + /* + * Similar to ppc64, s390x seems to be touchy with TCG, so disable it + * there until the problems are resolved + */ + if (g_str_equal(qtest_get_arch(), "s390x")) { +#if defined(HOST_S390X) + if (access("/dev/kvm", R_OK | W_OK)) { + g_test_message("Skipping test: kvm not available"); + return g_test_run(); + } +#else + g_test_message("Skipping test: Need s390x host to work properly"); + return g_test_run(); +#endif } tmpfs = mkdtemp(template); if (!tmpfs) { - g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno)); + g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); } g_assert(tmpfs); @@ -782,6 +1078,9 @@ int main(int argc, char **argv) qtest_add_func("/migration/deprecated", test_deprecated); qtest_add_func("/migration/bad_dest", test_baddest); qtest_add_func("/migration/precopy/unix", test_precopy_unix); + qtest_add_func("/migration/precopy/tcp", test_precopy_tcp); + /* qtest_add_func("/migration/ignore_shared", test_ignore_shared); */ + qtest_add_func("/migration/xbzrle/unix", test_xbzrle_unix); ret = g_test_run(); @@ -789,7 +1088,7 @@ int main(int argc, char **argv) ret = rmdir(tmpfs); if (ret != 0) { - g_test_message("unable to rmdir: path (%s): %s\n", + g_test_message("unable to rmdir: path (%s): %s", tmpfs, strerror(errno)); } diff --git a/tests/migration/Makefile b/tests/migration/Makefile new file mode 100644 index 0000000000..13e99b1692 --- /dev/null +++ b/tests/migration/Makefile @@ -0,0 +1,40 @@ +# +# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# + +TARGET_LIST = i386 aarch64 s390x + +SRC_PATH = ../.. + +.PHONY: help $(TARGET_LIST) +help: + @echo "Create migration guest includes. We generate a binary." + @echo "And then convert that binary to an include file that can be" + @echo "run in a guest." + @echo "Possible operations are:" + @echo + @echo " $(MAKE) clean Remove all intermediate files" + @echo " $(MAKE) target Generate for that target" + @echo " $(MAKE) CROSS_PREFIX=... target" + @echo " Cross-compile than target" + @echo " Possible targets are: $(TARGET_LIST)" + +override define __note +/* This file is automatically generated from the assembly file in + * tests/migration/$@. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +endef +export __note + +$(TARGET_LIST): + $(MAKE) CROSS_PREFIX=$(CROSS_PREFIX) -C $@ + +clean: + for target in $(TARGET_LIST); do \ + $(MAKE) -C $$target clean; \ + done diff --git a/tests/migration/aarch64/Makefile b/tests/migration/aarch64/Makefile new file mode 100644 index 0000000000..9c4fa18e76 --- /dev/null +++ b/tests/migration/aarch64/Makefile @@ -0,0 +1,18 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=aarch64-linux-gnu- + +.PHONY: all clean +all: a-b-kernel.h + +a-b-kernel.h: aarch64.kernel + echo "$$__note" > $@ + xxd -i $< | sed -e 's/.*int.*//' >> $@ + +aarch64.kernel: aarch64.elf + $(CROSS_PREFIX)objcopy -O binary $< $@ + +aarch64.elf: a-b-kernel.S + $(CROSS_PREFIX)gcc -o $@ -nostdlib -Wl,--build-id=none $< + +clean: + $(RM) *.kernel *.elf diff --git a/tests/migration/aarch64/a-b-kernel.S b/tests/migration/aarch64/a-b-kernel.S new file mode 100644 index 0000000000..0225945348 --- /dev/null +++ b/tests/migration/aarch64/a-b-kernel.S @@ -0,0 +1,75 @@ +# +# Copyright (c) 2018 Red Hat, Inc. and/or its affiliates +# +# Author: +# Wei Huang +# +# This work is licensed under the terms of the GNU GPL, version 2 or later. +# See the COPYING file in the top-level directory. +# +# Note: Please make sure the compiler compiles the assembly code below with +# pc-relative address. Also the branch instructions should use relative +# addresses only. + +#include "../migration-test.h" + +.section .text + + .globl _start + +_start: + /* disable MMU to use phys mem address */ + mrs x0, sctlr_el1 + bic x0, x0, #(1<<0) + msr sctlr_el1, x0 + isb + + /* traverse test memory region */ + mov x0, #ARM_TEST_MEM_START + mov x1, #ARM_TEST_MEM_END + + /* output char 'A' to PL011 */ + mov w3, 'A' + mov x2, #ARM_MACH_VIRT_UART + strb w3, [x2] + + /* clean up memory */ + mov w3, #0 + mov x4, x0 +clean: + strb w3, [x4] + add x4, x4, #TEST_MEM_PAGE_SIZE + cmp x4, x1 + ble clean + + /* w5 keeps a counter so we can limit the output speed */ + mov w5, #0 + + /* main body */ +mainloop: + mov x4, x0 + +innerloop: + /* increment the first byte of each page by 1 */ + ldrb w3, [x4] + add w3, w3, #1 + and w3, w3, #0xff + strb w3, [x4] + + /* make sure QEMU user space can see consistent data as MMU is off */ + dc civac, x4 + + add x4, x4, #TEST_MEM_PAGE_SIZE + cmp x4, x1 + blt innerloop + + add w5, w5, #1 + and w5, w5, #0xff + cmp w5, #0 + bne mainloop + + /* output char 'B' to PL011 */ + mov w3, 'B' + strb w3, [x2] + + b mainloop diff --git a/tests/migration/aarch64/a-b-kernel.h b/tests/migration/aarch64/a-b-kernel.h new file mode 100644 index 0000000000..0a9b01137e --- /dev/null +++ b/tests/migration/aarch64/a-b-kernel.h @@ -0,0 +1,18 @@ +/* This file is automatically generated from the assembly file in + * tests/migration/aarch64. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char aarch64_kernel[] = { + 0x00, 0x10, 0x38, 0xd5, 0x00, 0xf8, 0x7f, 0x92, 0x00, 0x10, 0x18, 0xd5, + 0xdf, 0x3f, 0x03, 0xd5, 0x00, 0x02, 0xa8, 0xd2, 0x01, 0xc8, 0xa8, 0xd2, + 0x23, 0x08, 0x80, 0x52, 0x02, 0x20, 0xa1, 0xd2, 0x43, 0x00, 0x00, 0x39, + 0x03, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x00, 0x39, + 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, 0xad, 0xff, 0xff, 0x54, + 0x05, 0x00, 0x80, 0x52, 0xe4, 0x03, 0x00, 0xaa, 0x83, 0x00, 0x40, 0x39, + 0x63, 0x04, 0x00, 0x11, 0x63, 0x1c, 0x00, 0x12, 0x83, 0x00, 0x00, 0x39, + 0x24, 0x7e, 0x0b, 0xd5, 0x84, 0x04, 0x40, 0x91, 0x9f, 0x00, 0x01, 0xeb, + 0x2b, 0xff, 0xff, 0x54, 0xa5, 0x04, 0x00, 0x11, 0xa5, 0x1c, 0x00, 0x12, + 0xbf, 0x00, 0x00, 0x71, 0x81, 0xfe, 0xff, 0x54, 0x43, 0x08, 0x80, 0x52, + 0x43, 0x00, 0x00, 0x39, 0xf1, 0xff, 0xff, 0x17 +}; diff --git a/tests/migration/guestperf/engine.py b/tests/migration/guestperf/engine.py index 398e3f2706..0e304660b8 100644 --- a/tests/migration/guestperf/engine.py +++ b/tests/migration/guestperf/engine.py @@ -24,13 +24,14 @@ import re import sys import time -sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', '..', 'scripts')) -import qemu -import qmp.qmp from guestperf.progress import Progress, ProgressStats from guestperf.report import Report from guestperf.timings import TimingRecord, Timings +sys.path.append(os.path.join(os.path.dirname(__file__), + '..', '..', '..', 'python')) +import qemu + class Engine(object): diff --git a/tests/migration/guestperf/shell.py b/tests/migration/guestperf/shell.py index a6b8cec1e0..61d2abbaad 100644 --- a/tests/migration/guestperf/shell.py +++ b/tests/migration/guestperf/shell.py @@ -19,14 +19,12 @@ from __future__ import print_function # -import os -import os.path -import sys -sys.path.append(os.path.join(os.path.dirname(__file__), - '..', '..', '..', 'scripts')) import argparse import fnmatch +import os +import os.path import platform +import sys import logging from guestperf.hardware import Hardware diff --git a/tests/migration/i386/Makefile b/tests/migration/i386/Makefile new file mode 100644 index 0000000000..5c0324134a --- /dev/null +++ b/tests/migration/i386/Makefile @@ -0,0 +1,22 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=x86_64-linux-gnu- + +.PHONY: all clean +all: a-b-bootblock.h + +a-b-bootblock.h: x86.bootsect + echo "$$__note" > header.tmp + xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + mv header.tmp $@ + +x86.bootsect: x86.boot + dd if=$< of=$@ bs=256 count=2 skip=124 + +x86.boot: x86.o + $(CROSS_PREFIX)objcopy -O binary $< $@ + +x86.o: a-b-bootblock.S + $(CROSS_PREFIX)gcc -m32 -march=i486 -c $< -o $@ + +clean: + @rm -rf *.boot *.o *.bootsect diff --git a/tests/migration/x86-a-b-bootblock.s b/tests/migration/i386/a-b-bootblock.S similarity index 93% rename from tests/migration/x86-a-b-bootblock.s rename to tests/migration/i386/a-b-bootblock.S index b1642641a7..3f97f28023 100644 --- a/tests/migration/x86-a-b-bootblock.s +++ b/tests/migration/i386/a-b-bootblock.S @@ -3,10 +3,6 @@ # range. # Outputs an initial 'A' on serial followed by repeated 'B's # -# run tests/migration/rebuild-x86-bootblock.sh -# to regenerate the hex, and remember to include both the .h and .s -# in any patches. -# # Copyright (c) 2016 Red Hat, Inc. and/or its affiliates # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. diff --git a/tests/migration/x86-a-b-bootblock.h b/tests/migration/i386/a-b-bootblock.h similarity index 92% rename from tests/migration/x86-a-b-bootblock.h rename to tests/migration/i386/a-b-bootblock.h index 78a151fe2a..7d459d4fde 100644 --- a/tests/migration/x86-a-b-bootblock.h +++ b/tests/migration/i386/a-b-bootblock.h @@ -1,7 +1,7 @@ -/* This file is automatically generated from - * tests/migration/x86-a-b-bootblock.s, edit that and then run - * tests/migration/rebuild-x86-bootblock.sh to update, - * and then remember to send both in your patch submission. +/* This file is automatically generated from the assembly file in + * tests/migration/i386. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. */ unsigned char x86_bootsect[] = { 0xfa, 0x0f, 0x01, 0x16, 0x74, 0x7c, 0x66, 0xb8, 0x01, 0x00, 0x00, 0x00, diff --git a/tests/migration/migration-test.h b/tests/migration/migration-test.h new file mode 100644 index 0000000000..03c252368a --- /dev/null +++ b/tests/migration/migration-test.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018 Red Hat, Inc. and/or its affiliates + * + * 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_MIGRATION_H_ +#define _TEST_MIGRATION_H_ + +/* Common */ +#define TEST_MEM_PAGE_SIZE 4096 + +/* x86 */ +#define X86_TEST_MEM_START (1 * 1024 * 1024) +#define X86_TEST_MEM_END (100 * 1024 * 1024) + +/* S390 */ +#define S390_TEST_MEM_START (1 * 1024 * 1024) +#define S390_TEST_MEM_END (100 * 1024 * 1024) + +/* PPC */ +#define PPC_TEST_MEM_START (1 * 1024 * 1024) +#define PPC_TEST_MEM_END (100 * 1024 * 1024) + +/* ARM */ +#define ARM_TEST_MEM_START (0x40000000 + 1 * 1024 * 1024) +#define ARM_TEST_MEM_END (0x40000000 + 100 * 1024 * 1024) +#define ARM_MACH_VIRT_UART 0x09000000 +/* AArch64 kernel load address is 0x40080000, and the test memory starts at + * 0x40100000. So the maximum allowable kernel size is 512KB. + */ +#define ARM_TEST_MAX_KERNEL_SIZE (512 * 1024) + +#endif /* _TEST_MIGRATION_H_ */ diff --git a/tests/migration/rebuild-x86-bootblock.sh b/tests/migration/rebuild-x86-bootblock.sh deleted file mode 100755 index 86cec5d284..0000000000 --- a/tests/migration/rebuild-x86-bootblock.sh +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates -# This work is licensed under the terms of the GNU GPL, version 2 or later. -# See the COPYING file in the top-level directory. -# -# Author: dgilbert@redhat.com - -ASMFILE=$PWD/tests/migration/x86-a-b-bootblock.s -HEADER=$PWD/tests/migration/x86-a-b-bootblock.h - -if [ ! -e "$ASMFILE" ] -then - echo "Couldn't find $ASMFILE" >&2 - exit 1 -fi - -ASM_WORK_DIR=$(mktemp -d --tmpdir X86BB.XXXXXX) -cd "$ASM_WORK_DIR" && -as --32 -march=i486 "$ASMFILE" -o x86.o && -objcopy -O binary x86.o x86.boot && -dd if=x86.boot of=x86.bootsect bs=256 count=2 skip=124 && -xxd -i x86.bootsect | -sed -e 's/.*int.*//' > x86.hex && -cat - x86.hex < "$HEADER" -/* This file is automatically generated from - * tests/migration/x86-a-b-bootblock.s, edit that and then run - * tests/migration/rebuild-x86-bootblock.sh to update, - * and then remember to send both in your patch submission. - */ -HERE - -rm x86.hex x86.bootsect x86.boot x86.o -cd .. && rmdir "$ASM_WORK_DIR" diff --git a/tests/migration/s390x/Makefile b/tests/migration/s390x/Makefile new file mode 100644 index 0000000000..6393c3e5b9 --- /dev/null +++ b/tests/migration/s390x/Makefile @@ -0,0 +1,24 @@ +# To specify cross compiler prefix, use CROSS_PREFIX= +# $ make CROSS_PREFIX=s390x-linux-gnu- + +.PHONY: all clean +all: a-b-bios.h +fwdir=../../../pc-bios/s390-ccw + +CFLAGS+=-ffreestanding -fno-delete-null-pointer-checks -fPIE -Os \ + -msoft-float -march=z900 -fno-asynchronous-unwind-tables -Wl,-pie \ + -Wl,--build-id=none -nostdlib + +a-b-bios.h: s390x.elf + echo "$$__note" > header.tmp + xxd -i $< | sed -e 's/.*int.*//' >> header.tmp + mv header.tmp $@ + +# We use common-page-size=16 to avoid big padding in the ELF file +s390x.elf: a-b-bios.c + $(CROSS_PREFIX)gcc $(CFLAGS) -I$(fwdir) $(fwdir)/start.S \ + $(fwdir)/sclp.c -Wl,-zcommon-page-size=16 -o $@ $< + $(CROSS_PREFIX)strip $@ + +clean: + @rm -rf *.elf *.o diff --git a/tests/migration/s390x/a-b-bios.c b/tests/migration/s390x/a-b-bios.c new file mode 100644 index 0000000000..a0327cd153 --- /dev/null +++ b/tests/migration/s390x/a-b-bios.c @@ -0,0 +1,36 @@ +/* + * S390 guest code used in migration tests + * + * Copyright 2018 Thomas Huth, Red Hat Inc. + * + * This code 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. + */ + +#define LOADPARM_LEN 8 /* Needed for sclp.h */ + +#include +#include +#include + +char stack[0x8000] __attribute__((aligned(4096))); + +#define START_ADDRESS (1024 * 1024) +#define END_ADDRESS (100 * 1024 * 1024) + +void main(void) +{ + unsigned long addr; + + sclp_setup(); + sclp_print("A"); + + while (1) { + for (addr = START_ADDRESS; addr < END_ADDRESS; addr += 4096) { + *(volatile char *)addr += 1; /* Change pages */ + } + sclp_print("B"); + } +} diff --git a/tests/migration/s390x/a-b-bios.h b/tests/migration/s390x/a-b-bios.h new file mode 100644 index 0000000000..e722dc7c40 --- /dev/null +++ b/tests/migration/s390x/a-b-bios.h @@ -0,0 +1,253 @@ +/* This file is automatically generated from the a-b-bios.c file in + * tests/migration/s390x. Edit that file and then run "make all" + * inside tests/migration to update, and then remember to send both + * the header and the assembler differences in your patch submission. + */ +unsigned char s390x_elf[] = { + 0x7f, 0x45, 0x4c, 0x46, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x78, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x38, 0x00, 0x07, 0x00, 0x40, + 0x00, 0x0c, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x05, 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, 0x07, 0x0c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x98, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x64, 0x74, 0xe5, 0x51, + 0x00, 0x00, 0x00, 0x07, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x10, 0x64, 0x74, 0xe5, 0x52, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xd0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x2f, 0x6c, 0x69, 0x62, 0x2f, 0x6c, 0x64, 0x36, 0x34, 0x2e, 0x73, 0x6f, + 0x2e, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 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, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xeb, 0xef, 0xf0, 0x70, + 0x00, 0x24, 0xa7, 0xfb, 0xff, 0x60, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x1f, + 0xc0, 0x20, 0x00, 0x00, 0x02, 0x64, 0xc0, 0xe5, 0x00, 0x00, 0x01, 0x35, + 0xa5, 0x1e, 0x00, 0x10, 0xa7, 0x29, 0x63, 0x00, 0xe3, 0x30, 0x10, 0x00, + 0x00, 0x90, 0xa7, 0x3a, 0x00, 0x01, 0x42, 0x30, 0x10, 0x00, 0xa7, 0x1b, + 0x10, 0x00, 0xa7, 0x27, 0xff, 0xf7, 0xc0, 0x20, 0x00, 0x00, 0x02, 0x50, + 0xa7, 0xf4, 0xff, 0xeb, 0x07, 0x07, 0x07, 0x07, 0xc0, 0xf0, 0x00, 0x00, + 0x56, 0xc4, 0xc0, 0x20, 0x00, 0x00, 0x0a, 0xbd, 0xc0, 0x30, 0x00, 0x00, + 0x56, 0xbe, 0xb9, 0x0b, 0x00, 0x32, 0xb9, 0x02, 0x00, 0x33, 0xa7, 0x84, + 0x00, 0x19, 0xa7, 0x3b, 0xff, 0xff, 0xeb, 0x43, 0x00, 0x08, 0x00, 0x0c, + 0xb9, 0x02, 0x00, 0x44, 0xb9, 0x04, 0x00, 0x12, 0xa7, 0x84, 0x00, 0x09, + 0xd7, 0xff, 0x10, 0x00, 0x10, 0x00, 0x41, 0x10, 0x11, 0x00, 0xa7, 0x47, + 0xff, 0xfb, 0xc0, 0x20, 0x00, 0x00, 0x00, 0x07, 0x44, 0x30, 0x20, 0x00, + 0xa7, 0xf4, 0xff, 0xb6, 0xd7, 0x00, 0x10, 0x00, 0x10, 0x00, 0xc0, 0x10, + 0x00, 0x00, 0x00, 0x29, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, 0xf0, 0x00, + 0x00, 0x25, 0x96, 0x02, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, 0x00, 0x2f, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x11, 0xe3, 0x10, 0x01, 0xb8, 0x00, 0x24, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x26, 0xd2, 0x07, 0x01, 0xb0, 0x10, 0x00, + 0xc0, 0x10, 0x00, 0x00, 0x00, 0x18, 0xb2, 0xb2, 0x10, 0x00, 0xeb, 0x00, + 0xf0, 0x00, 0x00, 0x25, 0x94, 0xfd, 0xf0, 0x06, 0xeb, 0x00, 0xf0, 0x00, + 0x00, 0x2f, 0x07, 0xfe, 0x07, 0x07, 0x07, 0x07, 0x00, 0x02, 0x00, 0x01, + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x02, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x00, + 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0x10, 0x00, 0x00, 0x0e, 0x59, + 0xa7, 0xfb, 0xff, 0x60, 0xb2, 0x20, 0x00, 0x21, 0xb2, 0x22, 0x00, 0xb0, + 0x88, 0xb0, 0x00, 0x1c, 0xc0, 0xe5, 0xff, 0xff, 0xff, 0xba, 0xa7, 0xbe, + 0x00, 0x03, 0xa7, 0x84, 0x00, 0x13, 0xa7, 0xbe, 0x00, 0x02, 0xa7, 0x28, + 0x00, 0x00, 0xa7, 0x74, 0x00, 0x04, 0xa7, 0x28, 0xff, 0xfe, 0xe3, 0x40, + 0xf1, 0x10, 0x00, 0x04, 0xb9, 0x14, 0x00, 0x22, 0xeb, 0xbf, 0xf0, 0xf8, + 0x00, 0x04, 0x07, 0xf4, 0xa7, 0x28, 0xff, 0xff, 0xa7, 0xf4, 0xff, 0xf5, + 0x07, 0x07, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, 0x00, 0x24, 0xc0, 0xd0, + 0x00, 0x00, 0x01, 0x21, 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0xb9, 0x00, 0x00, + 0xa7, 0x19, 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0x0e, 0x24, 0xa7, 0x3b, + 0x00, 0x01, 0xa7, 0x37, 0x00, 0x23, 0xc0, 0x20, 0x00, 0x00, 0x0e, 0x1d, + 0x18, 0x31, 0xa7, 0x1a, 0x00, 0x06, 0x40, 0x10, 0x20, 0x08, 0xa7, 0x3a, + 0x00, 0x0e, 0xa7, 0x18, 0x1a, 0x00, 0x40, 0x30, 0x20, 0x00, 0x92, 0x00, + 0x20, 0x02, 0x40, 0x10, 0x20, 0x0a, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, + 0xc0, 0xe5, 0xff, 0xff, 0xff, 0xac, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, + 0xb9, 0x04, 0x00, 0x2b, 0xeb, 0xbf, 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, + 0xb9, 0x04, 0x00, 0x51, 0xa7, 0x5b, 0x00, 0x01, 0xa7, 0x09, 0x0f, 0xf7, + 0xb9, 0x21, 0x00, 0x50, 0xa7, 0x24, 0xff, 0xd7, 0x41, 0xeb, 0x20, 0x00, + 0x95, 0x0a, 0xe0, 0x00, 0xa7, 0x74, 0x00, 0x08, 0x41, 0x11, 0x40, 0x0e, + 0x92, 0x0d, 0x10, 0x00, 0xb9, 0x04, 0x00, 0x15, 0x43, 0x5b, 0x20, 0x00, + 0x42, 0x51, 0x40, 0x0e, 0xa7, 0xbb, 0x00, 0x01, 0x41, 0x10, 0x10, 0x01, + 0xa7, 0xf4, 0xff, 0xbf, 0xc0, 0x50, 0x00, 0x00, 0x00, 0xd4, 0xc0, 0x10, + 0x00, 0x00, 0x0d, 0xd9, 0xa7, 0x48, 0x00, 0x1c, 0x40, 0x40, 0x10, 0x00, + 0x50, 0x20, 0x10, 0x0c, 0xa7, 0x48, 0x00, 0x04, 0xe3, 0x20, 0x50, 0x00, + 0x00, 0x04, 0x40, 0x40, 0x10, 0x0a, 0x50, 0x30, 0x10, 0x10, 0xc0, 0xf4, + 0xff, 0xff, 0xff, 0x6b, 0xa7, 0x39, 0x00, 0x40, 0xa7, 0x29, 0x00, 0x00, + 0xc0, 0xf4, 0xff, 0xff, 0xff, 0xe4, 0x07, 0x07, 0xb9, 0x04, 0x00, 0x13, + 0xa7, 0x2a, 0xff, 0xff, 0xb9, 0x04, 0x00, 0x34, 0xa7, 0x48, 0x00, 0x01, + 0x15, 0x24, 0xa7, 0x24, 0x00, 0x07, 0xb9, 0x04, 0x00, 0x21, 0xc0, 0xf4, + 0xff, 0xff, 0xff, 0x7f, 0xa7, 0x29, 0xff, 0xff, 0x07, 0xfe, 0x07, 0x07, + 0xa7, 0x39, 0x00, 0x00, 0x41, 0x13, 0x20, 0x00, 0x95, 0x00, 0x10, 0x00, + 0xa7, 0x74, 0x00, 0x05, 0xc0, 0xf4, 0xff, 0xff, 0xff, 0x70, 0xa7, 0x3b, + 0x00, 0x01, 0xa7, 0xf4, 0xff, 0xf5, 0x07, 0x07, 0xeb, 0xbf, 0xf0, 0x58, + 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x91, 0xa7, 0xfb, 0xff, 0x60, + 0xb9, 0x04, 0x00, 0xb2, 0xa7, 0x19, 0x00, 0x20, 0xc0, 0x20, 0x00, 0x00, + 0x0d, 0x8c, 0x92, 0x00, 0x20, 0x00, 0xa7, 0x2b, 0x00, 0x01, 0xa7, 0x17, + 0xff, 0xfc, 0xc0, 0x10, 0x00, 0x00, 0x0d, 0x83, 0xa7, 0x28, 0x00, 0x20, + 0x40, 0x20, 0x10, 0x00, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, 0xc0, 0xe5, + 0xff, 0xff, 0xff, 0x1d, 0x12, 0x22, 0xa7, 0x74, 0x00, 0x17, 0xa7, 0x19, + 0x00, 0x00, 0xc0, 0x40, 0x00, 0x00, 0x00, 0x75, 0xc0, 0x50, 0x00, 0x00, + 0x0d, 0x7a, 0xa7, 0x29, 0x00, 0x08, 0xe3, 0x31, 0x50, 0x00, 0x00, 0x90, + 0x43, 0x33, 0x40, 0x00, 0x42, 0x31, 0xb0, 0x00, 0xa7, 0x1b, 0x00, 0x01, + 0xa7, 0x27, 0xff, 0xf7, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, 0xeb, 0xbf, + 0xf0, 0xf8, 0x00, 0x04, 0x07, 0xf4, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0xeb, 0xaf, 0xf0, 0x50, 0x00, 0x24, 0xc0, 0xd0, 0x00, 0x00, 0x00, 0x51, + 0xa7, 0xfb, 0xff, 0x60, 0xa7, 0x19, 0x0f, 0xf8, 0xb9, 0x21, 0x00, 0x31, + 0xb9, 0x04, 0x00, 0xa2, 0xa7, 0xc4, 0x00, 0x2d, 0xa7, 0xb9, 0x0f, 0xf8, + 0xc0, 0x10, 0x00, 0x00, 0x0d, 0x42, 0xa7, 0x28, 0x10, 0x00, 0x40, 0x20, + 0x10, 0x00, 0x92, 0x00, 0x10, 0x02, 0xe3, 0x20, 0xd0, 0x00, 0x00, 0x04, + 0xc0, 0xe5, 0xff, 0xff, 0xfe, 0xda, 0xa7, 0xbb, 0x00, 0x01, 0xa7, 0x19, + 0x00, 0x00, 0xc0, 0x20, 0x00, 0x00, 0x0d, 0x2f, 0xa7, 0xb7, 0x00, 0x17, + 0xc0, 0x10, 0x00, 0x00, 0x0d, 0x2a, 0xe3, 0x40, 0xf1, 0x10, 0x00, 0x04, + 0xe3, 0x20, 0x10, 0x08, 0x00, 0x91, 0xa7, 0x2a, 0xff, 0xf9, 0xb9, 0x14, + 0x00, 0x22, 0xeb, 0xaf, 0xf0, 0xf0, 0x00, 0x04, 0x07, 0xf4, 0xb9, 0x04, + 0x00, 0xb3, 0xa7, 0xf4, 0xff, 0xd5, 0x43, 0x31, 0x20, 0x0f, 0x42, 0x31, + 0xa0, 0x00, 0xa7, 0x1b, 0x00, 0x01, 0xa7, 0xf4, 0xff, 0xe3, 0x07, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x77, 0x00, 0x05, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x20, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x3c, 0x28, 0x2b, 0x7c, 0x26, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x21, 0x24, 0x2a, 0x29, 0x3b, 0x2e, 0x2d, 0x2f, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2c, 0x25, 0x5f, 0x3e, 0x3f, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x60, 0x3a, 0x23, + 0x40, 0x27, 0x3d, 0x22, 0x2e, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x6a, 0x6b, 0x6c, + 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x41, 0x42, 0x43, + 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x2e, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x2e, 0x2e, + 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, + 0x59, 0x5a, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x30, 0x31, 0x32, 0x33, + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, 0x2e, + 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0xff, 0xfe, 0xf5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6f, 0xff, 0xff, 0xfb, 0x00, 0x00, 0x00, 0x00, 0x08, 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, 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, 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, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x43, 0x43, 0x3a, 0x20, 0x28, 0x47, 0x4e, 0x55, 0x29, 0x20, 0x38, + 0x2e, 0x32, 0x2e, 0x31, 0x20, 0x32, 0x30, 0x31, 0x38, 0x30, 0x39, 0x30, + 0x35, 0x20, 0x28, 0x52, 0x65, 0x64, 0x20, 0x48, 0x61, 0x74, 0x20, 0x38, + 0x2e, 0x32, 0x2e, 0x31, 0x2d, 0x33, 0x29, 0x00, 0x00, 0x2e, 0x73, 0x68, + 0x73, 0x74, 0x72, 0x74, 0x61, 0x62, 0x00, 0x2e, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x70, 0x00, 0x2e, 0x67, 0x6e, 0x75, 0x2e, 0x68, 0x61, 0x73, 0x68, + 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x73, 0x79, 0x6d, 0x00, 0x2e, 0x64, 0x79, + 0x6e, 0x73, 0x74, 0x72, 0x00, 0x2e, 0x74, 0x65, 0x78, 0x74, 0x00, 0x2e, + 0x72, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x00, 0x2e, 0x64, 0x79, 0x6e, 0x61, + 0x6d, 0x69, 0x63, 0x00, 0x2e, 0x67, 0x6f, 0x74, 0x00, 0x2e, 0x62, 0x73, + 0x73, 0x00, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 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, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x13, 0x6f, 0xff, 0xff, 0xf6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xd8, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x01, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x03, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x05, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xe8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xe0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x49, 0x00, 0x00, 0x00, 0x08, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xf8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x07, 0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; diff --git a/tests/multiboot/run_test.sh b/tests/multiboot/run_test.sh index 6c33003e71..98df91e6af 100755 --- a/tests/multiboot/run_test.sh +++ b/tests/multiboot/run_test.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Copyright (c) 2013 Kevin Wolf # diff --git a/tests/ne2000-test.c b/tests/ne2000-test.c index b7cf3dd2f5..097c2eec6c 100644 --- a/tests/ne2000-test.c +++ b/tests/ne2000-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +typedef struct QNe2k_pci QNe2k_pci; + +struct QNe2k_pci { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *ne2k_pci_get_driver(void *obj, const char *interface) { + QNe2k_pci *ne2k_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ne2k_pci->dev; + } + + fprintf(stderr, "%s not present in ne2k_pci\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *ne2k_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QNe2k_pci *ne2k_pci = g_new0(QNe2k_pci, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/ne2000/pci/nop", pci_nop); + qpci_device_init(&ne2k_pci->dev, bus, addr); + ne2k_pci->obj.get_driver = ne2k_pci_get_driver; - qtest_start("-device ne2k_pci"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &ne2k_pci->obj; } + +static void ne2000_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("ne2k_pci", ne2k_pci_create); + qos_node_consumes("ne2k_pci", "pci-bus", &opts); + qos_node_produces("ne2k_pci", "pci-device"); +} + +libqos_init(ne2000_register_nodes); diff --git a/tests/numa-test.c b/tests/numa-test.c index 893f826acb..9824fdd587 100644 --- a/tests/numa-test.c +++ b/tests/numa-test.c @@ -267,8 +267,8 @@ static void pc_dynamic_cpu_cfg(const void *data) QList *cpus; QTestState *qs; - qs = qtest_startf("%s %s", data ? (char *)data : "", - "-nodefaults --preconfig -smp 2"); + qs = qtest_initf("%s -nodefaults --preconfig -smp 2", + data ? (char *)data : ""); /* create 2 numa nodes */ g_assert(!qmp_rsp_is_err(qtest_qmp(qs, "{ 'execute': 'set-numa-node'," diff --git a/tests/nvme-test.c b/tests/nvme-test.c index 7674a446e4..b48d3a24b9 100644 --- a/tests/nvme-test.c +++ b/tests/nvme-test.c @@ -8,25 +8,79 @@ */ #include "qemu/osdep.h" +#include "qemu/units.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QNvme QNvme; + +struct QNvme { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *nvme_get_driver(void *obj, const char *interface) { + QNvme *nvme = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &nvme->dev; + } + + fprintf(stderr, "%s not present in nvme\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *nvme_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QNvme *nvme = g_new0(QNvme, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/nvme/nop", nop); + qpci_device_init(&nvme->dev, bus, addr); + nvme->obj.get_driver = nvme_get_driver; - qtest_start("-drive id=drv0,if=none,file=null-co://,format=raw " - "-device nvme,drive=drv0,serial=foo"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &nvme->obj; } + +/* This used to cause a NULL pointer dereference. */ +static void nvmetest_oob_cmb_test(void *obj, void *data, QGuestAllocator *alloc) +{ + const int cmb_bar_size = 2 * MiB; + QNvme *nvme = obj; + QPCIDevice *pdev = &nvme->dev; + QPCIBar bar; + + qpci_device_enable(pdev); + bar = qpci_iomap(pdev, 2, NULL); + + qpci_io_writel(pdev, bar, 0, 0xccbbaa99); + g_assert_cmpint(qpci_io_readb(pdev, bar, 0), ==, 0x99); + g_assert_cmpint(qpci_io_readw(pdev, bar, 0), ==, 0xaa99); + + /* Test partially out-of-bounds accesses. */ + qpci_io_writel(pdev, bar, cmb_bar_size - 1, 0x44332211); + g_assert_cmpint(qpci_io_readb(pdev, bar, cmb_bar_size - 1), ==, 0x11); + g_assert_cmpint(qpci_io_readw(pdev, bar, cmb_bar_size - 1), !=, 0x2211); + g_assert_cmpint(qpci_io_readl(pdev, bar, cmb_bar_size - 1), !=, 0x44332211); +} + +static void nvme_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,drive=drv0,serial=foo", + .before_cmd_line = "-drive id=drv0,if=none,file=null-co://,format=raw", + }; + + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("nvme", nvme_create); + qos_node_consumes("nvme", "pci-bus", &opts); + qos_node_produces("nvme", "pci-device"); + + qos_add_test("oob-cmb-access", "nvme", nvmetest_oob_cmb_test, &(QOSGraphTestOptions) { + .edge.extra_device_opts = "cmb_size_mb=2" + }); +} + +libqos_init(nvme_register_nodes); diff --git a/tests/pci-test.c b/tests/pci-test.c new file mode 100644 index 0000000000..ff80985093 --- /dev/null +++ b/tests/pci-test.c @@ -0,0 +1,25 @@ +/* + * QTest testcase for PCI + * + * Copyright (c) 2018 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 "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_pci_test(void) +{ + qos_add_test("nop", "pci-device", nop, NULL); +} + +libqos_init(register_pci_test); diff --git a/tests/pcnet-test.c b/tests/pcnet-test.c index efb1ef44e9..484448cc64 100644 --- a/tests/pcnet-test.c +++ b/tests/pcnet-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +typedef struct QPCNet QPCNet; + +struct QPCNet { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *pcnet_get_driver(void *obj, const char *interface) { + QPCNet *pcnet = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &pcnet->dev; + } + + fprintf(stderr, "%s not present in pcnet\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *pcnet_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QPCNet *pcnet = g_new0(QPCNet, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/pcnet/pci/nop", pci_nop); + qpci_device_init(&pcnet->dev, bus, addr); + pcnet->obj.get_driver = pcnet_get_driver; - qtest_start("-device pcnet"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &pcnet->obj; } + +static void pcnet_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("pcnet", pcnet_create); + qos_node_consumes("pcnet", "pci-bus", &opts); + qos_node_produces("pcnet", "pci-device"); +} + +libqos_init(pcnet_register_nodes); diff --git a/tests/pnv-xscom-test.c b/tests/pnv-xscom-test.c index efb7c838b5..63d464048d 100644 --- a/tests/pnv-xscom-test.c +++ b/tests/pnv-xscom-test.c @@ -39,7 +39,6 @@ static const PnvChip pnv_chips[] = { .cfam_id = 0x120d304980000000ull, .first_core = 0x1, }, -#if 0 /* POWER9 support is not ready yet */ { .chip_type = PNV_CHIP_POWER9, .cpu_model = "POWER9", @@ -47,7 +46,6 @@ static const PnvChip pnv_chips[] = { .cfam_id = 0x220d104900008000ull, .first_core = 0x0, }, -#endif }; static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) @@ -63,14 +61,15 @@ static uint64_t pnv_xscom_addr(const PnvChip *chip, uint32_t pcba) return addr; } -static uint64_t pnv_xscom_read(const PnvChip *chip, uint32_t pcba) +static uint64_t pnv_xscom_read(QTestState *qts, const PnvChip *chip, + uint32_t pcba) { - return readq(pnv_xscom_addr(chip, pcba)); + return qtest_readq(qts, pnv_xscom_addr(chip, pcba)); } -static void test_xscom_cfam_id(const PnvChip *chip) +static void test_xscom_cfam_id(QTestState *qts, const PnvChip *chip) { - uint64_t f000f = pnv_xscom_read(chip, 0xf000f); + uint64_t f000f = pnv_xscom_read(qts, chip, 0xf000f); g_assert_cmphex(f000f, ==, chip->cfam_id); } @@ -78,11 +77,11 @@ static void test_xscom_cfam_id(const PnvChip *chip) static void test_cfam_id(const void *data) { const PnvChip *chip = data; + QTestState *qts; - global_qtest = qtest_startf("-M powernv,accel=tcg -cpu %s", - chip->cpu_model); - test_xscom_cfam_id(chip); - qtest_quit(global_qtest); + qts = qtest_initf("-M powernv,accel=tcg -cpu %s", chip->cpu_model); + test_xscom_cfam_id(qts, chip); + qtest_quit(qts); } @@ -94,7 +93,7 @@ static void test_cfam_id(const void *data) #define PNV_XSCOM_EX_DTS_RESULT0 0x50000 -static void test_xscom_core(const PnvChip *chip) +static void test_xscom_core(QTestState *qts, const PnvChip *chip) { uint32_t first_core_dts0 = PNV_XSCOM_EX_DTS_RESULT0; uint64_t dts0; @@ -105,7 +104,7 @@ static void test_xscom_core(const PnvChip *chip) first_core_dts0 |= PNV_XSCOM_P9_EC_BASE(chip->first_core); } - dts0 = pnv_xscom_read(chip, first_core_dts0); + dts0 = pnv_xscom_read(qts, chip, first_core_dts0); g_assert_cmphex(dts0, ==, 0x26f024f023f0000ull); } @@ -113,11 +112,11 @@ static void test_xscom_core(const PnvChip *chip) static void test_core(const void *data) { const PnvChip *chip = data; + QTestState *qts; - global_qtest = qtest_startf("-M powernv,accel=tcg -cpu %s", - chip->cpu_model); - test_xscom_core(chip); - qtest_quit(global_qtest); + qts = qtest_initf("-M powernv,accel=tcg -cpu %s", chip->cpu_model); + test_xscom_core(qts, chip); + qtest_quit(qts); } static void add_test(const char *name, void (*test)(const void *data)) diff --git a/tests/prom-env-test.c b/tests/prom-env-test.c index 8c867e631a..61bc1d1e7b 100644 --- a/tests/prom-env-test.c +++ b/tests/prom-env-test.c @@ -25,14 +25,14 @@ #define MAGIC 0xcafec0de #define ADDRESS 0x4000 -static void check_guest_memory(void) +static void check_guest_memory(QTestState *qts) { uint32_t signature; int i; /* Poll until code has run and modified memory. Wait at most 600 seconds */ for (i = 0; i < 60000; ++i) { - signature = readl(ADDRESS); + signature = qtest_readl(qts, ADDRESS); if (signature == MAGIC) { break; } @@ -44,18 +44,24 @@ static void check_guest_memory(void) static void test_machine(const void *machine) { - const char *extra_args; + const char *extra_args = ""; + QTestState *qts; - /* The pseries firmware boots much faster without the default devices */ - extra_args = strcmp(machine, "pseries") == 0 ? "-nodefaults" : ""; + /* + * The pseries firmware boots much faster without the default + * devices, it also needs Spectre/Meltdown workarounds disabled to + * avoid warnings with TCG + */ + if (strcmp(machine, "pseries") == 0) { + extra_args = "-nodefaults" + " -machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken"; + } - global_qtest = qtest_startf("-M %s,accel=tcg %s " - "-prom-env 'use-nvramrc?=true' " - "-prom-env 'nvramrc=%x %x l!' ", - (const char *)machine, extra_args, - MAGIC, ADDRESS); - check_guest_memory(); - qtest_quit(global_qtest); + qts = qtest_initf("-M %s,accel=tcg %s -prom-env 'use-nvramrc?=true' " + "-prom-env 'nvramrc=%x %x l!' ", (const char *)machine, + extra_args, MAGIC, ADDRESS); + check_guest_memory(qts); + qtest_quit(qts); } static void add_tests(const char *machines[]) diff --git a/tests/ptimer-test-stubs.c b/tests/ptimer-test-stubs.c index ca5cc3b13b..54b3fd26f6 100644 --- a/tests/ptimer-test-stubs.c +++ b/tests/ptimer-test-stubs.c @@ -34,14 +34,19 @@ int64_t ptimer_test_time_ns; int use_icount = 1; bool qtest_allowed; -void timer_init_tl(QEMUTimer *ts, - QEMUTimerList *timer_list, int scale, - QEMUTimerCB *cb, void *opaque) +void timer_init_full(QEMUTimer *ts, + QEMUTimerListGroup *timer_list_group, QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque) { - ts->timer_list = timer_list; + if (!timer_list_group) { + timer_list_group = &main_loop_tlg; + } + ts->timer_list = timer_list_group->tl[type]; ts->cb = cb; ts->opaque = opaque; ts->scale = scale; + ts->attributes = attributes; ts->expire_time = -1; } diff --git a/tests/pvpanic-test.c b/tests/pvpanic-test.c index 7461a7254f..ff9176adf3 100644 --- a/tests/pvpanic-test.c +++ b/tests/pvpanic-test.c @@ -15,13 +15,16 @@ static void test_panic(void) { uint8_t val; QDict *response, *data; + QTestState *qts; - val = inb(0x505); + qts = qtest_init("-device pvpanic"); + + val = qtest_inb(qts, 0x505); g_assert_cmpuint(val, ==, 1); - outb(0x505, 0x1); + qtest_outb(qts, 0x505, 0x1); - response = qmp_receive(); + response = qtest_qmp_receive(qts); g_assert(qdict_haskey(response, "event")); g_assert_cmpstr(qdict_get_str(response, "event"), ==, "GUEST_PANICKED"); g_assert(qdict_haskey(response, "data")); @@ -29,6 +32,8 @@ static void test_panic(void) g_assert(qdict_haskey(data, "action")); g_assert_cmpstr(qdict_get_str(data, "action"), ==, "pause"); qobject_unref(response); + + qtest_quit(qts); } int main(int argc, char **argv) @@ -38,10 +43,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); qtest_add_func("/pvpanic/panic", test_panic); - qtest_start("-device pvpanic"); ret = g_test_run(); - qtest_end(); - return ret; } diff --git a/tests/pxe-test.c b/tests/pxe-test.c index 6e3679672c..948b0fbdc7 100644 --- a/tests/pxe-test.c +++ b/tests/pxe-test.c @@ -25,6 +25,7 @@ static char disk[] = "tests/pxe-test-disk-XXXXXX"; typedef struct testdef { const char *machine; /* Machine type */ const char *model; /* NIC device model */ + const char *extra; /* Any additional parameters */ } testdef_t; static testdef_t x86_tests[] = { @@ -44,13 +45,16 @@ static testdef_t x86_tests_slow[] = { }; static testdef_t ppc64_tests[] = { - { "pseries", "spapr-vlan" }, - { "pseries", "virtio-net-pci", }, + { "pseries", "spapr-vlan", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken" }, + { "pseries", "virtio-net-pci", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken" }, { NULL }, }; static testdef_t ppc64_tests_slow[] = { - { "pseries", "e1000" }, + { "pseries", "e1000", + "-machine cap-cfpc=broken,cap-sbbc=broken,cap-ibs=broken" }, { NULL }, }; @@ -61,18 +65,24 @@ static testdef_t s390x_tests[] = { static void test_pxe_one(const testdef_t *test, bool ipv6) { + QTestState *qts; char *args; + const char *extra = test->extra; + + if (!extra) { + extra = ""; + } args = g_strdup_printf( "-machine %s,accel=kvm:tcg -nodefaults -boot order=n " "-netdev user,id=" NETNAME ",tftp=./,bootfile=%s,ipv4=%s,ipv6=%s " - "-device %s,bootindex=1,netdev=" NETNAME, + "-device %s,bootindex=1,netdev=" NETNAME " %s", test->machine, disk, ipv6 ? "off" : "on", ipv6 ? "on" : "off", - test->model); + test->model, extra); - qtest_start(args); - boot_sector_test(global_qtest); - qtest_quit(global_qtest); + qts = qtest_init(args); + boot_sector_test(qts); + qtest_quit(qts); g_free(args); } diff --git a/tests/q35-test.c b/tests/q35-test.c index 7ea7acc9d8..34b34bc2b9 100644 --- a/tests/q35-test.c +++ b/tests/q35-test.c @@ -87,7 +87,7 @@ static void test_smram_lock(void) qtest_start("-M q35"); - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); g_assert(pcibus != NULL); pcidev = qpci_device_find(pcibus, 0); @@ -146,7 +146,7 @@ static void test_tseg_size(const void *data) g_free(cmdline); /* locate the DRAM controller */ - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); g_assert(pcibus != NULL); pcidev = qpci_device_find(pcibus, 0); g_assert(pcidev != NULL); diff --git a/tests/qapi-schema/alternate-base.err b/tests/qapi-schema/alternate-base.err index 30d8a34373..ebe05bc898 100644 --- a/tests/qapi-schema/alternate-base.err +++ b/tests/qapi-schema/alternate-base.err @@ -1 +1,2 @@ tests/qapi-schema/alternate-base.json:4: Unknown key 'base' in alternate 'Alt' +Valid keys are 'alternate', 'data', 'if'. diff --git a/tests/qapi-schema/alternate-invalid-dict.err b/tests/qapi-schema/alternate-invalid-dict.err new file mode 100644 index 0000000000..631d46628e --- /dev/null +++ b/tests/qapi-schema/alternate-invalid-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/alternate-invalid-dict.json:2: Key 'type' is missing from member 'two' of alternate 'Alt' diff --git a/tests/qapi-schema/enum-dict-member.exit b/tests/qapi-schema/alternate-invalid-dict.exit similarity index 100% rename from tests/qapi-schema/enum-dict-member.exit rename to tests/qapi-schema/alternate-invalid-dict.exit diff --git a/tests/qapi-schema/alternate-invalid-dict.json b/tests/qapi-schema/alternate-invalid-dict.json new file mode 100644 index 0000000000..8e0b2ac287 --- /dev/null +++ b/tests/qapi-schema/alternate-invalid-dict.json @@ -0,0 +1,4 @@ +# exploded member form must have a 'type' +{ 'alternate': 'Alt', + 'data': { 'one': 'str', + 'two': { 'if': 'foo' } } } diff --git a/scripts/qmp/__init__.py b/tests/qapi-schema/alternate-invalid-dict.out similarity index 100% rename from scripts/qmp/__init__.py rename to tests/qapi-schema/alternate-invalid-dict.out diff --git a/tests/qapi-schema/comments.out b/tests/qapi-schema/comments.out index 8d2f1ce8a2..273f0f54e1 100644 --- a/tests/qapi-schema/comments.out +++ b/tests/qapi-schema/comments.out @@ -1,5 +1,16 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module comments.json -enum Status ['good', 'bad', 'ugly'] +enum Status + member good + member bad + member ugly diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out index cd28721568..367e2a1c3e 100644 --- a/tests/qapi-schema/doc-bad-section.out +++ b/tests/qapi-schema/doc-bad-section.out @@ -1,8 +1,18 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module doc-bad-section.json -enum Enum ['one', 'two'] +enum Enum + member one + member two doc symbol=Enum body= == Produces *invalid* texinfo diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 984cd8ed06..f7fb48af38 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -55,7 +55,9 @@ # # @two is undocumented ## -{ 'enum': 'Enum', 'data': [ 'one', 'two' ], 'if': 'defined(IFCOND)' } +{ 'enum': 'Enum', 'data': + [ { 'name': 'one', 'if': 'defined(IFONE)' }, 'two' ], + 'if': 'defined(IFCOND)' } ## # @Base: @@ -70,7 +72,8 @@ # # Another paragraph (but no @var: line) ## -{ 'struct': 'Variant1', 'data': { 'var1': 'str' } } +{ 'struct': 'Variant1', + 'data': { 'var1': { 'type': 'str', 'if': 'defined(IFSTR)' } } } ## # @Variant2: @@ -83,13 +86,13 @@ { 'union': 'Object', 'base': 'Base', 'discriminator': 'base1', - 'data': { 'one': 'Variant1', 'two': 'Variant2' } } + 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # @SugaredUnion: ## { 'union': 'SugaredUnion', - 'data': { 'one': 'Variant1', 'two': 'Variant2' } } + 'data': { 'one': 'Variant1', 'two': { 'type': 'Variant2', 'if': 'IFTWO' } } } ## # == Another subsection diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index 35f3f1164c..d3bca343eb 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -1,29 +1,46 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module doc-good.json -enum Enum ['one', 'two'] +enum Enum + member one + if ['defined(IFONE)'] + member two if ['defined(IFCOND)'] object Base member base1: Enum optional=False object Variant1 member var1: str optional=False + if ['defined(IFSTR)'] object Variant2 object Object base Base tag base1 case one: Variant1 case two: Variant2 + if ['IFTWO'] object q_obj_Variant1-wrapper member data: Variant1 optional=False object q_obj_Variant2-wrapper member data: Variant2 optional=False -enum SugaredUnionKind ['one', 'two'] +enum SugaredUnionKind + member one + member two + if ['IFTWO'] object SugaredUnion member type: SugaredUnionKind optional=False tag type case one: q_obj_Variant1-wrapper case two: q_obj_Variant2-wrapper + if ['IFTWO'] object q_obj_cmd-arg member arg1: int optional=False member arg2: str optional=True diff --git a/tests/qapi-schema/doc-good.texi b/tests/qapi-schema/doc-good.texi index e42eace474..2526abc6d9 100644 --- a/tests/qapi-schema/doc-good.texi +++ b/tests/qapi-schema/doc-good.texi @@ -84,12 +84,12 @@ Examples: @table @asis @item @code{one} The @emph{one} @{and only@} +@*@b{If:} @code{defined(IFONE)} @item @code{two} Not documented @end table @code{two} is undocumented - @b{If:} @code{defined(IFCOND)} @end deftp @@ -119,6 +119,7 @@ Another paragraph (but no @code{var}: line) @table @asis @item @code{var1: string} Not documented +@*@b{If:} @code{defined(IFSTR)} @end table @end deftp @@ -141,7 +142,7 @@ Not documented @table @asis @item The members of @code{Base} @item The members of @code{Variant1} when @code{base1} is @t{"one"} -@item The members of @code{Variant2} when @code{base1} is @t{"two"} +@item The members of @code{Variant2} when @code{base1} is @t{"two"} (@b{If:} @code{IFTWO}) @end table @end deftp @@ -157,7 +158,7 @@ Not documented @item @code{type} One of @t{"one"}, @t{"two"} @item @code{data: Variant1} when @code{type} is @t{"one"} -@item @code{data: Variant2} when @code{type} is @t{"two"} +@item @code{data: Variant2} when @code{type} is @t{"two"} (@b{If:} @code{IFTWO}) @end table @end deftp diff --git a/tests/qapi-schema/double-type.err b/tests/qapi-schema/double-type.err index f9613c6d6b..799193dba1 100644 --- a/tests/qapi-schema/double-type.err +++ b/tests/qapi-schema/double-type.err @@ -1 +1,2 @@ tests/qapi-schema/double-type.json:2: Unknown key 'command' in struct 'bar' +Valid keys are 'base', 'data', 'if', 'struct'. diff --git a/tests/qapi-schema/empty.out b/tests/qapi-schema/empty.out index 0ec234eec4..5b53d00702 100644 --- a/tests/qapi-schema/empty.out +++ b/tests/qapi-schema/empty.out @@ -1,3 +1,11 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool diff --git a/tests/qapi-schema/enum-bad-member.err b/tests/qapi-schema/enum-bad-member.err new file mode 100644 index 0000000000..211db9e6fc --- /dev/null +++ b/tests/qapi-schema/enum-bad-member.err @@ -0,0 +1 @@ +tests/qapi-schema/enum-bad-member.json:2: Member of enum 'MyEnum' requires a string name diff --git a/tests/qapi-schema/enum-bad-member.exit b/tests/qapi-schema/enum-bad-member.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/enum-bad-member.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/enum-bad-member.json b/tests/qapi-schema/enum-bad-member.json new file mode 100644 index 0000000000..98da6828b4 --- /dev/null +++ b/tests/qapi-schema/enum-bad-member.json @@ -0,0 +1,2 @@ +# we reject any enum member that is not a string +{ 'enum': 'MyEnum', 'data': [ [ ] ] } diff --git a/tests/qapi-schema/enum-dict-member.out b/tests/qapi-schema/enum-bad-member.out similarity index 100% rename from tests/qapi-schema/enum-dict-member.out rename to tests/qapi-schema/enum-bad-member.out diff --git a/tests/qapi-schema/enum-dict-member-unknown.err b/tests/qapi-schema/enum-dict-member-unknown.err new file mode 100644 index 0000000000..2aae618be0 --- /dev/null +++ b/tests/qapi-schema/enum-dict-member-unknown.err @@ -0,0 +1,2 @@ +tests/qapi-schema/enum-dict-member-unknown.json:2: Unknown key 'bad-key' in dictionary member of enum 'MyEnum' +Valid keys are 'if', 'name'. diff --git a/tests/qapi-schema/enum-dict-member-unknown.exit b/tests/qapi-schema/enum-dict-member-unknown.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/enum-dict-member-unknown.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/enum-dict-member-unknown.json b/tests/qapi-schema/enum-dict-member-unknown.json new file mode 100644 index 0000000000..6664c59201 --- /dev/null +++ b/tests/qapi-schema/enum-dict-member-unknown.json @@ -0,0 +1,2 @@ +# we reject any enum member that is not a string or a dict with 'name' +{ 'enum': 'MyEnum', 'data': [ { 'name': 'foo', 'bad-key': 'str' } ] } diff --git a/tests/qapi-schema/include-relpath.err b/tests/qapi-schema/enum-dict-member-unknown.out similarity index 100% rename from tests/qapi-schema/include-relpath.err rename to tests/qapi-schema/enum-dict-member-unknown.out diff --git a/tests/qapi-schema/enum-dict-member.err b/tests/qapi-schema/enum-dict-member.err deleted file mode 100644 index 8ca146ea59..0000000000 --- a/tests/qapi-schema/enum-dict-member.err +++ /dev/null @@ -1 +0,0 @@ -tests/qapi-schema/enum-dict-member.json:2: Member of enum 'MyEnum' requires a string name diff --git a/tests/qapi-schema/enum-dict-member.json b/tests/qapi-schema/enum-dict-member.json deleted file mode 100644 index 79672e0f09..0000000000 --- a/tests/qapi-schema/enum-dict-member.json +++ /dev/null @@ -1,2 +0,0 @@ -# we reject any enum member that is not a string -{ 'enum': 'MyEnum', 'data': [ { 'value': 'str' } ] } diff --git a/tests/qapi-schema/enum-if-invalid.err b/tests/qapi-schema/enum-if-invalid.err new file mode 100644 index 0000000000..54c3cf887b --- /dev/null +++ b/tests/qapi-schema/enum-if-invalid.err @@ -0,0 +1 @@ +tests/qapi-schema/enum-if-invalid.json:2: 'if' condition must be a string or a list of strings diff --git a/tests/qapi-schema/enum-if-invalid.exit b/tests/qapi-schema/enum-if-invalid.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/enum-if-invalid.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/enum-if-invalid.json b/tests/qapi-schema/enum-if-invalid.json new file mode 100644 index 0000000000..60bd0ef1d7 --- /dev/null +++ b/tests/qapi-schema/enum-if-invalid.json @@ -0,0 +1,3 @@ +# check invalid 'if' type +{ 'enum': 'TestIfEnum', 'data': + [ 'foo', { 'name' : 'bar', 'if': { 'val': 'foo' } } ] } diff --git a/tests/qapi-schema/enum-if-invalid.out b/tests/qapi-schema/enum-if-invalid.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/event-case.out b/tests/qapi-schema/event-case.out index 88c0964917..ec8a1406e4 100644 --- a/tests/qapi-schema/event-case.out +++ b/tests/qapi-schema/event-case.out @@ -1,6 +1,14 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module event-case.json event oops None boxed=False diff --git a/tests/qapi-schema/event-member-invalid-dict.err b/tests/qapi-schema/event-member-invalid-dict.err new file mode 100644 index 0000000000..1a57fa29b0 --- /dev/null +++ b/tests/qapi-schema/event-member-invalid-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/event-member-invalid-dict.json:1: Key 'type' is missing from member 'a' of 'data' for event 'EVENT_A' diff --git a/tests/qapi-schema/event-member-invalid-dict.exit b/tests/qapi-schema/event-member-invalid-dict.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/event-member-invalid-dict.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/event-member-invalid-dict.json b/tests/qapi-schema/event-member-invalid-dict.json new file mode 100644 index 0000000000..ee6f3ecb6f --- /dev/null +++ b/tests/qapi-schema/event-member-invalid-dict.json @@ -0,0 +1,2 @@ +{ 'event': 'EVENT_A', + 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/tests/qapi-schema/event-member-invalid-dict.out b/tests/qapi-schema/event-member-invalid-dict.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/event-nest-struct.json b/tests/qapi-schema/event-nest-struct.json index ee6f3ecb6f..355ddaeff1 100644 --- a/tests/qapi-schema/event-nest-struct.json +++ b/tests/qapi-schema/event-nest-struct.json @@ -1,2 +1,2 @@ { 'event': 'EVENT_A', - 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } + 'data': { 'a' : { 'type' : { 'integer': 'int' } }, 'b' : 'str' } } diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.err b/tests/qapi-schema/flat-union-inline-invalid-dict.err new file mode 100644 index 0000000000..9c4c45b7f0 --- /dev/null +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-inline-invalid-dict.json:7: Key 'type' is missing from member 'value1' of union 'TestUnion' diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.exit b/tests/qapi-schema/flat-union-inline-invalid-dict.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.json b/tests/qapi-schema/flat-union-inline-invalid-dict.json new file mode 100644 index 0000000000..62c7cda617 --- /dev/null +++ b/tests/qapi-schema/flat-union-inline-invalid-dict.json @@ -0,0 +1,11 @@ +# we require branches to be a struct name +# TODO: should we allow anonymous inline branch types? +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } +{ 'struct': 'Base', + 'data': { 'enum1': 'TestEnum', 'kind': 'str' } } +{ 'union': 'TestUnion', + 'base': 'Base', + 'discriminator': 'enum1', + 'data': { 'value1': { 'string': 'str' }, + 'value2': { 'integer': 'int' } } } diff --git a/tests/qapi-schema/flat-union-inline-invalid-dict.out b/tests/qapi-schema/flat-union-inline-invalid-dict.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/flat-union-inline.json b/tests/qapi-schema/flat-union-inline.json index 62c7cda617..a9b3ce3f0d 100644 --- a/tests/qapi-schema/flat-union-inline.json +++ b/tests/qapi-schema/flat-union-inline.json @@ -7,5 +7,5 @@ { 'union': 'TestUnion', 'base': 'Base', 'discriminator': 'enum1', - 'data': { 'value1': { 'string': 'str' }, + 'data': { 'value1': { 'type': {} }, 'value2': { 'integer': 'int' } } } diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.err b/tests/qapi-schema/flat-union-invalid-if-discriminator.err new file mode 100644 index 0000000000..0c94c9860d --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.err @@ -0,0 +1 @@ +tests/qapi-schema/flat-union-invalid-if-discriminator.json:13: The discriminator TestBase.enum1 for union TestUnion must not be conditional diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.exit b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.json b/tests/qapi-schema/flat-union-invalid-if-discriminator.json new file mode 100644 index 0000000000..618ec36396 --- /dev/null +++ b/tests/qapi-schema/flat-union-invalid-if-discriminator.json @@ -0,0 +1,17 @@ +{ 'enum': 'TestEnum', + 'data': [ 'value1', 'value2' ] } + +{ 'struct': 'TestBase', + 'data': { 'enum1': { 'type': 'TestEnum', 'if': 'FOO' } } } + +{ 'struct': 'TestTypeA', + 'data': { 'string': 'str' } } + +{ 'struct': 'TestTypeB', + 'data': { 'integer': 'int' } } + +{ 'union': 'TestUnion', + 'base': 'TestBase', + 'discriminator': 'enum1', + 'data': { 'value1': 'TestTypeA', + 'value2': 'TestTypeB' } } diff --git a/tests/qapi-schema/flat-union-invalid-if-discriminator.out b/tests/qapi-schema/flat-union-invalid-if-discriminator.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/ident-with-escape.out b/tests/qapi-schema/ident-with-escape.out index 24c976f473..39754eba8c 100644 --- a/tests/qapi-schema/ident-with-escape.out +++ b/tests/qapi-schema/ident-with-escape.out @@ -1,6 +1,14 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module ident-with-escape.json object q_obj_fooA-arg member bar1: str optional=False diff --git a/tests/qapi-schema/include-relpath-sub.json b/tests/qapi-schema/include-relpath-sub.json deleted file mode 100644 index 4bd4af4162..0000000000 --- a/tests/qapi-schema/include-relpath-sub.json +++ /dev/null @@ -1,2 +0,0 @@ -{ 'enum': 'Status', - 'data': [ 'good', 'bad', 'ugly' ] } diff --git a/tests/qapi-schema/include-relpath.exit b/tests/qapi-schema/include-relpath.exit deleted file mode 100644 index 573541ac97..0000000000 --- a/tests/qapi-schema/include-relpath.exit +++ /dev/null @@ -1 +0,0 @@ -0 diff --git a/tests/qapi-schema/include-relpath.json b/tests/qapi-schema/include-relpath.json deleted file mode 100644 index 05018f3908..0000000000 --- a/tests/qapi-schema/include-relpath.json +++ /dev/null @@ -1 +0,0 @@ -{ 'include': 'include/relpath.json' } diff --git a/tests/qapi-schema/include-relpath.out b/tests/qapi-schema/include-relpath.out deleted file mode 100644 index ebbabd7a18..0000000000 --- a/tests/qapi-schema/include-relpath.out +++ /dev/null @@ -1,9 +0,0 @@ -object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] - prefix QTYPE -module include-relpath.json -include include/relpath.json -module include/relpath.json -include include-relpath-sub.json -module include-relpath-sub.json -enum Status ['good', 'bad', 'ugly'] diff --git a/tests/qapi-schema/include-repetition.out b/tests/qapi-schema/include-repetition.out index 7235e055bc..5423983239 100644 --- a/tests/qapi-schema/include-repetition.out +++ b/tests/qapi-schema/include-repetition.out @@ -1,10 +1,21 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module include-repetition.json include comments.json module comments.json -enum Status ['good', 'bad', 'ugly'] +enum Status + member good + member bad + member ugly module include-repetition.json include include-repetition-sub.json module include-repetition-sub.json diff --git a/tests/qapi-schema/include-simple.out b/tests/qapi-schema/include-simple.out index 006f723eeb..061f81e509 100644 --- a/tests/qapi-schema/include-simple.out +++ b/tests/qapi-schema/include-simple.out @@ -1,7 +1,18 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module include-simple.json include include-simple-sub.json module include-simple-sub.json -enum Status ['good', 'bad', 'ugly'] +enum Status + member good + member bad + member ugly diff --git a/tests/qapi-schema/include/relpath.json b/tests/qapi-schema/include/relpath.json deleted file mode 100644 index 45dee24704..0000000000 --- a/tests/qapi-schema/include/relpath.json +++ /dev/null @@ -1 +0,0 @@ -{ 'include': '../include-relpath-sub.json' } diff --git a/tests/qapi-schema/include/sub-module.json b/tests/qapi-schema/include/sub-module.json new file mode 100644 index 0000000000..afdb267228 --- /dev/null +++ b/tests/qapi-schema/include/sub-module.json @@ -0,0 +1,7 @@ +# *-*- Mode: Python -*-* + +# Sub-module of ../qapi-schema-test.json + +{ 'include': '../sub-sub-module.json' } + +{ 'struct': 'SecondArrayRef', 'data': { 's': ['Status'] } } diff --git a/tests/qapi-schema/indented-expr.out b/tests/qapi-schema/indented-expr.out index bd8a48630e..bffdf6756d 100644 --- a/tests/qapi-schema/indented-expr.out +++ b/tests/qapi-schema/indented-expr.out @@ -1,6 +1,14 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module indented-expr.json command eins None -> None gen=True success_response=True boxed=False oob=False preconfig=False diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.err b/tests/qapi-schema/nested-struct-data-invalid-dict.err new file mode 100644 index 0000000000..5bd364e8d9 --- /dev/null +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/nested-struct-data-invalid-dict.json:2: Key 'type' is missing from member 'a' of 'data' for command 'foo' diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.exit b/tests/qapi-schema/nested-struct-data-invalid-dict.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.json b/tests/qapi-schema/nested-struct-data-invalid-dict.json new file mode 100644 index 0000000000..efbe773ded --- /dev/null +++ b/tests/qapi-schema/nested-struct-data-invalid-dict.json @@ -0,0 +1,3 @@ +# inline subtypes collide with our desired future use of defaults +{ 'command': 'foo', + 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } diff --git a/tests/qapi-schema/nested-struct-data-invalid-dict.out b/tests/qapi-schema/nested-struct-data-invalid-dict.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/nested-struct-data.json b/tests/qapi-schema/nested-struct-data.json index efbe773ded..5b8a40cca3 100644 --- a/tests/qapi-schema/nested-struct-data.json +++ b/tests/qapi-schema/nested-struct-data.json @@ -1,3 +1,3 @@ # inline subtypes collide with our desired future use of defaults { 'command': 'foo', - 'data': { 'a' : { 'string' : 'str', 'integer': 'int' }, 'b' : 'str' } } + 'data': { 'a' : { 'type': {} }, 'b' : 'str' } } diff --git a/tests/qapi-schema/qapi-schema-test.json b/tests/qapi-schema/qapi-schema-test.json index 11aa4c8f8d..0952c68734 100644 --- a/tests/qapi-schema/qapi-schema-test.json +++ b/tests/qapi-schema/qapi-schema-test.json @@ -11,7 +11,7 @@ 'guest-sync' ] } } { 'struct': 'TestStruct', - 'data': { 'integer': 'int', 'boolean': 'bool', 'string': 'str' } } + 'data': { 'integer': {'type': 'int'}, 'boolean': 'bool', 'string': 'str' } } # for testing enums { 'struct': 'NestedEnumsOne', @@ -77,7 +77,7 @@ { 'union': 'UserDefFlatUnion', 'base': 'UserDefUnionBase', # intentional forward reference 'discriminator': 'enum1', - 'data': { 'value1' : 'UserDefA', + 'data': { 'value1' : {'type': 'UserDefA'}, 'value2' : 'UserDefB', 'value3' : 'UserDefB' # 'value4' defaults to empty @@ -98,7 +98,7 @@ { 'struct': 'WrapAlternate', 'data': { 'alt': 'UserDefAlternate' } } { 'alternate': 'UserDefAlternate', - 'data': { 'udfu': 'UserDefFlatUnion', 'e': 'EnumOne', 'i': 'int', + 'data': { 'udfu': {'type': 'UserDefFlatUnion'}, 'e': 'EnumOne', 'i': 'int', 'n': 'null' } } { 'struct': 'UserDefC', @@ -113,8 +113,8 @@ # for testing use of 'str' within alternates { 'alternate': 'AltStrObj', 'data': { 's': 'str', 'o': 'TestStruct' } } -# for testing native lists -{ 'union': 'UserDefNativeListUnion', +# for testing lists +{ 'union': 'UserDefListUnion', 'data': { 'integer': ['int'], 's8': ['int8'], 's16': ['int16'], @@ -128,21 +128,27 @@ 'boolean': ['bool'], 'string': ['str'], 'sizes': ['size'], - 'any': ['any'] } } + 'any': ['any'], + 'user': ['Status'] } } # intentional forward ref. to sub-module + +# for testing sub-modules +{ 'include': 'include/sub-module.json' } # testing commands { 'command': 'user_def_cmd', 'data': {} } { 'command': 'user_def_cmd1', 'data': {'ud1a': 'UserDefOne'} } { 'command': 'user_def_cmd2', - 'data': {'ud1a': 'UserDefOne', '*ud1b': 'UserDefOne'}, + 'data': {'ud1a': {'type': 'UserDefOne'}, '*ud1b': 'UserDefOne'}, 'returns': 'UserDefTwo' } +{ 'command': 'cmd-success-response', 'data': {}, 'success-response': false } + # 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' } { 'command': 'boxed-struct', 'boxed': true, 'data': 'UserDefZero' } -{ 'command': 'boxed-union', 'data': 'UserDefNativeListUnion', 'boxed': true } +{ 'command': 'boxed-union', 'data': 'UserDefListUnion', 'boxed': true } # Smoke test on out-of-band and allow-preconfig-test { 'command': 'test-flags-command', 'allow-oob': true, 'allow-preconfig': true } @@ -164,7 +170,7 @@ # testing event { 'struct': 'EventStructOne', - 'data': { 'struct1': 'UserDefOne', 'string': 'str', '*enum2': 'EnumOne' } } + 'data': { 'struct1': {'type': 'UserDefOne'}, 'string': 'str', '*enum2': 'EnumOne' } } { 'event': 'EVENT_A' } { 'event': 'EVENT_B', @@ -199,23 +205,40 @@ # test 'if' condition handling -{ 'struct': 'TestIfStruct', 'data': { 'foo': 'int' }, +{ 'struct': 'TestIfStruct', 'data': + { 'foo': 'int', + 'bar': { 'type': 'int', 'if': 'defined(TEST_IF_STRUCT_BAR)'} }, 'if': 'defined(TEST_IF_STRUCT)' } -{ 'enum': 'TestIfEnum', 'data': [ 'foo', 'bar' ], +{ 'enum': 'TestIfEnum', 'data': + [ 'foo', { 'name' : 'bar', 'if': 'defined(TEST_IF_ENUM_BAR)' } ], 'if': 'defined(TEST_IF_ENUM)' } -{ 'union': 'TestIfUnion', 'data': { 'foo': 'TestStruct' }, +{ 'union': 'TestIfUnion', 'data': + { 'foo': 'TestStruct', + 'union_bar': { 'type': 'str', 'if': 'defined(TEST_IF_UNION_BAR)'} }, 'if': 'defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)' } -{ 'alternate': 'TestIfAlternate', 'data': { 'foo': 'int', 'bar': 'TestStruct' }, +{ 'command': 'TestIfUnionCmd', 'data': { 'union_cmd_arg': 'TestIfUnion' }, + 'if': 'defined(TEST_IF_UNION)' } + +{ 'alternate': 'TestIfAlternate', 'data': + { 'foo': 'int', + 'bar': { 'type': 'TestStruct', 'if': 'defined(TEST_IF_ALT_BAR)'} }, 'if': 'defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)' } -{ 'command': 'TestIfCmd', 'data': { 'foo': 'TestIfStruct' }, +{ 'command': 'TestIfAlternateCmd', 'data': { 'alt_cmd_arg': 'TestIfAlternate' }, + 'if': 'defined(TEST_IF_ALT)' } + +{ 'command': 'TestIfCmd', 'data': + { 'foo': 'TestIfStruct', + 'bar': { 'type': 'TestIfEnum', 'if': 'defined(TEST_IF_CMD_BAR)' } }, 'returns': 'UserDefThree', 'if': ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] } { 'command': 'TestCmdReturnDefThree', 'returns': 'UserDefThree' } -{ 'event': 'TestIfEvent', 'data': { 'foo': 'TestIfStruct' }, +{ 'event': 'TestIfEvent', 'data': + { 'foo': 'TestIfStruct', + 'bar': { 'type': ['TestIfEnum'], 'if': 'defined(TEST_IF_EVT_BAR)' } }, 'if': 'defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)' } diff --git a/tests/qapi-schema/qapi-schema-test.out b/tests/qapi-schema/qapi-schema-test.out index 0da92455da..77fb1e1aa9 100644 --- a/tests/qapi-schema/qapi-schema-test.out +++ b/tests/qapi-schema/qapi-schema-test.out @@ -1,6 +1,14 @@ +module None object q_empty -enum QType ['none', 'qnull', 'qnum', 'qstring', 'qdict', 'qlist', 'qbool'] +enum QType prefix QTYPE + member none + member qnull + member qnum + member qstring + member qdict + member qlist + member qbool module qapi-schema-test.json object TestStruct member integer: int optional=False @@ -11,19 +19,25 @@ object NestedEnumsOne member enum2: EnumOne optional=True member enum3: EnumOne optional=False member enum4: EnumOne optional=True -enum MyEnum [] +enum MyEnum object Empty1 object Empty2 base Empty1 command user_def_cmd0 Empty2 -> Empty2 gen=True success_response=True boxed=False oob=False preconfig=False -enum QEnumTwo ['value1', 'value2'] +enum QEnumTwo prefix QENUM_TWO + member value1 + member value2 object UserDefOne base UserDefZero member string: str optional=False member enum1: EnumOne optional=True -enum EnumOne ['value1', 'value2', 'value3', 'value4'] +enum EnumOne + member value1 + member value2 + member value3 + member value4 object UserDefZero member integer: int optional=False object UserDefTwoDictDict @@ -38,6 +52,9 @@ object UserDefTwo member dict1: UserDefTwoDict optional=False object UserDefThree member string0: str optional=False +array UserDefOneList UserDefOne +array UserDefTwoList UserDefTwo +array TestStructList TestStruct object ForceArrays member unused1: UserDefOneList optional=False member unused2: UserDefTwoList optional=False @@ -127,9 +144,29 @@ object q_obj_sizeList-wrapper member data: sizeList optional=False object q_obj_anyList-wrapper member data: anyList optional=False -enum UserDefNativeListUnionKind ['integer', 's8', 's16', 's32', 's64', 'u8', 'u16', 'u32', 'u64', 'number', 'boolean', 'string', 'sizes', 'any'] -object UserDefNativeListUnion - member type: UserDefNativeListUnionKind optional=False +module sub-sub-module.json +array StatusList Status +module qapi-schema-test.json +object q_obj_StatusList-wrapper + member data: StatusList optional=False +enum UserDefListUnionKind + member integer + member s8 + member s16 + member s32 + member s64 + member u8 + member u16 + member u32 + member u64 + member number + member boolean + member string + member sizes + member any + member user +object UserDefListUnion + member type: UserDefListUnionKind optional=False tag type case integer: q_obj_intList-wrapper case s8: q_obj_int8List-wrapper @@ -145,6 +182,19 @@ object UserDefNativeListUnion case string: q_obj_strList-wrapper case sizes: q_obj_sizeList-wrapper case any: q_obj_anyList-wrapper + case user: q_obj_StatusList-wrapper +include include/sub-module.json +module include/sub-module.json +include sub-sub-module.json +module sub-sub-module.json +enum Status + member good + member bad + member ugly +module include/sub-module.json +object SecondArrayRef + member s: StatusList optional=False +module qapi-schema-test.json command user_def_cmd None -> None gen=True success_response=True boxed=False oob=False preconfig=False object q_obj_user_def_cmd1-arg @@ -156,6 +206,8 @@ object q_obj_user_def_cmd2-arg member ud1b: UserDefOne optional=True command user_def_cmd2 q_obj_user_def_cmd2-arg -> UserDefTwo gen=True success_response=True boxed=False oob=False preconfig=False +command cmd-success-response None -> None + gen=True success_response=False boxed=False oob=False preconfig=False object q_obj_guest-get-time-arg member a: int optional=False member b: int optional=True @@ -167,7 +219,7 @@ command guest-sync q_obj_guest-sync-arg -> any gen=True success_response=True boxed=False oob=False preconfig=False command boxed-struct UserDefZero -> None gen=True success_response=True boxed=True oob=False preconfig=False -command boxed-union UserDefNativeListUnion -> None +command boxed-union UserDefListUnion -> None gen=True success_response=True boxed=True oob=False preconfig=False command test-flags-command None -> None gen=True success_response=True boxed=False oob=True preconfig=True @@ -202,7 +254,8 @@ event EVENT_E UserDefZero boxed=True event EVENT_F UserDefAlternate boxed=True -enum __org.qemu_x-Enum ['__org.qemu_x-value'] +enum __org.qemu_x-Enum + member __org.qemu_x-value object __org.qemu_x-Base member __org.qemu_x-member1: __org.qemu_x-Enum optional=False object __org.qemu_x-Struct @@ -211,11 +264,13 @@ object __org.qemu_x-Struct member wchar-t: int optional=True object q_obj_str-wrapper member data: str optional=False -enum __org.qemu_x-Union1Kind ['__org.qemu_x-branch'] +enum __org.qemu_x-Union1Kind + member __org.qemu_x-branch object __org.qemu_x-Union1 member type: __org.qemu_x-Union1Kind optional=False tag type case __org.qemu_x-branch: q_obj_str-wrapper +array __org.qemu_x-Union1List __org.qemu_x-Union1 object __org.qemu_x-Struct2 member array: __org.qemu_x-Union1List optional=False object __org.qemu_x-Union2 @@ -228,6 +283,8 @@ alternate __org.qemu_x-Alt case b: __org.qemu_x-Base event __ORG.QEMU_X-EVENT __org.qemu_x-Struct boxed=False +array __org.qemu_x-EnumList __org.qemu_x-Enum +array __org.qemu_x-StructList __org.qemu_x-Struct object q_obj___org.qemu_x-command-arg member a: __org.qemu_x-EnumList optional=False member b: __org.qemu_x-StructList optional=False @@ -237,33 +294,62 @@ command __org.qemu_x-command q_obj___org.qemu_x-command-arg -> __org.qemu_x-Unio gen=True success_response=True boxed=False oob=False preconfig=False object TestIfStruct member foo: int optional=False + member bar: int optional=False + if ['defined(TEST_IF_STRUCT_BAR)'] if ['defined(TEST_IF_STRUCT)'] -enum TestIfEnum ['foo', 'bar'] +enum TestIfEnum + member foo + member bar + if ['defined(TEST_IF_ENUM_BAR)'] if ['defined(TEST_IF_ENUM)'] object q_obj_TestStruct-wrapper member data: TestStruct optional=False -enum TestIfUnionKind ['foo'] +enum TestIfUnionKind + member foo + member union_bar + if ['defined(TEST_IF_UNION_BAR)'] if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] object TestIfUnion member type: TestIfUnionKind optional=False tag type case foo: q_obj_TestStruct-wrapper + case union_bar: q_obj_str-wrapper + if ['defined(TEST_IF_UNION_BAR)'] if ['defined(TEST_IF_UNION) && defined(TEST_IF_STRUCT)'] +object q_obj_TestIfUnionCmd-arg + member union_cmd_arg: TestIfUnion optional=False + if ['defined(TEST_IF_UNION)'] +command TestIfUnionCmd q_obj_TestIfUnionCmd-arg -> None + gen=True success_response=True boxed=False oob=False preconfig=False + if ['defined(TEST_IF_UNION)'] alternate TestIfAlternate tag type case foo: int case bar: TestStruct + if ['defined(TEST_IF_ALT_BAR)'] if ['defined(TEST_IF_ALT) && defined(TEST_IF_STRUCT)'] +object q_obj_TestIfAlternateCmd-arg + member alt_cmd_arg: TestIfAlternate optional=False + if ['defined(TEST_IF_ALT)'] +command TestIfAlternateCmd q_obj_TestIfAlternateCmd-arg -> None + gen=True success_response=True boxed=False oob=False preconfig=False + if ['defined(TEST_IF_ALT)'] object q_obj_TestIfCmd-arg member foo: TestIfStruct optional=False + member bar: TestIfEnum optional=False + if ['defined(TEST_IF_CMD_BAR)'] if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] command TestIfCmd q_obj_TestIfCmd-arg -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False if ['defined(TEST_IF_CMD)', 'defined(TEST_IF_STRUCT)'] command TestCmdReturnDefThree None -> UserDefThree gen=True success_response=True boxed=False oob=False preconfig=False +array TestIfEnumList TestIfEnum + if ['defined(TEST_IF_ENUM)'] object q_obj_TestIfEvent-arg member foo: TestIfStruct optional=False + member bar: TestIfEnumList optional=False + if ['defined(TEST_IF_EVT_BAR)'] if ['defined(TEST_IF_EVT) && defined(TEST_IF_STRUCT)'] event TestIfEvent q_obj_TestIfEvent-arg boxed=False diff --git a/tests/qapi-schema/struct-member-invalid-dict.err b/tests/qapi-schema/struct-member-invalid-dict.err new file mode 100644 index 0000000000..6a765bc668 --- /dev/null +++ b/tests/qapi-schema/struct-member-invalid-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/struct-member-invalid-dict.json:2: Key 'type' is missing from member '*a' of 'data' for struct 'foo' diff --git a/tests/qapi-schema/struct-member-invalid-dict.exit b/tests/qapi-schema/struct-member-invalid-dict.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/struct-member-invalid-dict.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/struct-member-invalid-dict.json b/tests/qapi-schema/struct-member-invalid-dict.json new file mode 100644 index 0000000000..9fe0d455a9 --- /dev/null +++ b/tests/qapi-schema/struct-member-invalid-dict.json @@ -0,0 +1,3 @@ +# Long form of member must have a value member 'type' +{ 'struct': 'foo', + 'data': { '*a': { 'case': 'foo' } } } diff --git a/tests/qapi-schema/struct-member-invalid-dict.out b/tests/qapi-schema/struct-member-invalid-dict.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/sub-sub-module.json b/tests/qapi-schema/sub-sub-module.json new file mode 100644 index 0000000000..524ef9b83f --- /dev/null +++ b/tests/qapi-schema/sub-sub-module.json @@ -0,0 +1,6 @@ +# *-*- Mode: Python -*-* + +# Sub-module of sub-module include/sub-module.json of qapi-schema-test.json + +{ 'enum': 'Status', + 'data': [ 'good', 'bad', 'ugly' ] } diff --git a/tests/qapi-schema/test-qapi.py b/tests/qapi-schema/test-qapi.py index f514fe71e4..d21fca01fc 100644 --- a/tests/qapi-schema/test-qapi.py +++ b/tests/qapi-schema/test-qapi.py @@ -23,10 +23,19 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_include(self, name, info): print('include %s' % name) - def visit_enum_type(self, name, info, ifcond, values, prefix): - print('enum %s %s' % (name, values)) + def visit_enum_type(self, name, info, ifcond, members, prefix): + print('enum %s' % name) if prefix: print(' prefix %s' % prefix) + for m in members: + print(' member %s' % m.name) + self._print_if(m.ifcond, indent=8) + self._print_if(ifcond) + + def visit_array_type(self, name, info, ifcond, element_type): + if not info: + return # suppress built-in arrays + print('array %s %s' % (name, element_type.name)) self._print_if(ifcond) def visit_object_type(self, name, info, ifcond, base, members, variants): @@ -34,8 +43,9 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): if base: print(' base %s' % base.name) for m in members: - print(' member %s: %s optional=%s' % \ - (m.name, m.type.name, m.optional)) + print(' member %s: %s optional=%s' + % (m.name, m.type.name, m.optional)) + self._print_if(m.ifcond, 8) self._print_variants(variants) self._print_if(ifcond) @@ -46,10 +56,11 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): def visit_command(self, name, info, ifcond, arg_type, ret_type, gen, success_response, boxed, allow_oob, allow_preconfig): - print('command %s %s -> %s' % \ - (name, arg_type and arg_type.name, ret_type and ret_type.name)) - print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' % \ - (gen, success_response, boxed, allow_oob, allow_preconfig)) + print('command %s %s -> %s' + % (name, arg_type and arg_type.name, + ret_type and ret_type.name)) + print(' gen=%s success_response=%s boxed=%s oob=%s preconfig=%s' + % (gen, success_response, boxed, allow_oob, allow_preconfig)) self._print_if(ifcond) def visit_event(self, name, info, ifcond, arg_type, boxed): @@ -63,6 +74,7 @@ class QAPISchemaTestVisitor(QAPISchemaVisitor): print(' tag %s' % variants.tag_member.name) for v in variants.variants: print(' case %s: %s' % (v.name, v.type.name)) + QAPISchemaTestVisitor._print_if(v.ifcond, indent=8) @staticmethod def _print_if(ifcond, indent=4): diff --git a/tests/qapi-schema/union-branch-invalid-dict.err b/tests/qapi-schema/union-branch-invalid-dict.err new file mode 100644 index 0000000000..89f9b36791 --- /dev/null +++ b/tests/qapi-schema/union-branch-invalid-dict.err @@ -0,0 +1 @@ +tests/qapi-schema/union-branch-invalid-dict.json:2: Key 'type' is missing from member 'integer' of union 'UnionInvalidBranch' diff --git a/tests/qapi-schema/union-branch-invalid-dict.exit b/tests/qapi-schema/union-branch-invalid-dict.exit new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/tests/qapi-schema/union-branch-invalid-dict.exit @@ -0,0 +1 @@ +1 diff --git a/tests/qapi-schema/union-branch-invalid-dict.json b/tests/qapi-schema/union-branch-invalid-dict.json new file mode 100644 index 0000000000..9778598dbd --- /dev/null +++ b/tests/qapi-schema/union-branch-invalid-dict.json @@ -0,0 +1,4 @@ +# Long form of member must have a value member 'type' +{ 'union': 'UnionInvalidBranch', + 'data': { 'integer': { 'if': 'foo'}, + 's8': 'int8' } } diff --git a/tests/qapi-schema/union-branch-invalid-dict.out b/tests/qapi-schema/union-branch-invalid-dict.out new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qapi-schema/unknown-expr-key.err b/tests/qapi-schema/unknown-expr-key.err index 12f5ed5b43..6ff8bb99c5 100644 --- a/tests/qapi-schema/unknown-expr-key.err +++ b/tests/qapi-schema/unknown-expr-key.err @@ -1 +1,2 @@ -tests/qapi-schema/unknown-expr-key.json:2: Unknown key 'bogus' in struct 'bar' +tests/qapi-schema/unknown-expr-key.json:2: Unknown keys 'bogus', 'phony' in struct 'bar' +Valid keys are 'base', 'data', 'if', 'struct'. diff --git a/tests/qapi-schema/unknown-expr-key.json b/tests/qapi-schema/unknown-expr-key.json index 3b2be00cc4..13292d75ed 100644 --- a/tests/qapi-schema/unknown-expr-key.json +++ b/tests/qapi-schema/unknown-expr-key.json @@ -1,2 +1,2 @@ # we reject an expression with unknown top-level keys -{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { } } +{ 'struct': 'bar', 'data': { 'string': 'str'}, 'bogus': { }, 'phony': { } } diff --git a/tests/qemu-iotests/001 b/tests/qemu-iotests/001 index ffd14e2ce9..5d266e170a 100755 --- a/tests/qemu-iotests/001 +++ b/tests/qemu-iotests/001 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test simple read/write using plain bdrv_read/bdrv_write # @@ -24,7 +24,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/002 b/tests/qemu-iotests/002 index d4f8e91b91..7fb85084a1 100755 --- a/tests/qemu-iotests/002 +++ b/tests/qemu-iotests/002 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test simple read/write using plain bdrv_pread/bdrv_pwrite # @@ -24,7 +24,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/003 b/tests/qemu-iotests/003 index 19889b9fcd..f008c57cdc 100755 --- a/tests/qemu-iotests/003 +++ b/tests/qemu-iotests/003 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test simple read/write using bdrv_aio_readv/bdrv_aio_writev # @@ -24,7 +24,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/004 b/tests/qemu-iotests/004 index 6f2aa3d9a2..64fab3e714 100755 --- a/tests/qemu-iotests/004 +++ b/tests/qemu-iotests/004 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Make sure we can't read and write outside of the image size. # @@ -24,7 +24,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/005 b/tests/qemu-iotests/005 index 444737751f..2fef63af88 100755 --- a/tests/qemu-iotests/005 +++ b/tests/qemu-iotests/005 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Make sure qemu-img can create 5TB images # @@ -27,7 +27,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/007 b/tests/qemu-iotests/007 index fa543eeb7d..3ab5490db3 100755 --- a/tests/qemu-iotests/007 +++ b/tests/qemu-iotests/007 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Check for one possible case of qcow2 refcount corruption. # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/008 b/tests/qemu-iotests/008 index 8e89d74fe9..75067e36ad 100755 --- a/tests/qemu-iotests/008 +++ b/tests/qemu-iotests/008 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test simple asynchronous read/write operations. # @@ -24,7 +24,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/009 b/tests/qemu-iotests/009 index 16e4475ca4..bc4b461122 100755 --- a/tests/qemu-iotests/009 +++ b/tests/qemu-iotests/009 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Nolan I qcow2 corruption - incorrectly reports free clusters # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/010 b/tests/qemu-iotests/010 index 151dac238d..6920408d28 100755 --- a/tests/qemu-iotests/010 +++ b/tests/qemu-iotests/010 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Nolan II qcow2 corruption - wrong used cluster # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/011 b/tests/qemu-iotests/011 index f8d044ec85..b4c7e8f799 100755 --- a/tests/qemu-iotests/011 +++ b/tests/qemu-iotests/011 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test for AIO allocation on the same cluster # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/012 b/tests/qemu-iotests/012 index 01a770d59c..2c3b42d9dd 100755 --- a/tests/qemu-iotests/012 +++ b/tests/qemu-iotests/012 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Make sure we can open read-only images # @@ -26,7 +26,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/013 b/tests/qemu-iotests/013 index d013f87da9..5cb9032f16 100755 --- a/tests/qemu-iotests/013 +++ b/tests/qemu-iotests/013 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 pattern test, empty and compressed image - 4k cluster patterns # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/014 b/tests/qemu-iotests/014 index 2ea79e8c8b..2f728a1956 100755 --- a/tests/qemu-iotests/014 +++ b/tests/qemu-iotests/014 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 pattern test, complex patterns including compression and snapshots # Using patterns for 4k cluster size. @@ -26,7 +26,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/015 b/tests/qemu-iotests/015 index aaf9c3f415..5a4063e4f5 100755 --- a/tests/qemu-iotests/015 +++ b/tests/qemu-iotests/015 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Combined test to grow the refcount table and test snapshots. # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/017 b/tests/qemu-iotests/017 index 4f9302db42..83744f29a3 100755 --- a/tests/qemu-iotests/017 +++ b/tests/qemu-iotests/017 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Simple backing file reads # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/018 b/tests/qemu-iotests/018 index 1d39d35c47..78169838ba 100755 --- a/tests/qemu-iotests/018 +++ b/tests/qemu-iotests/018 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Merge backing file into test image when converting the image # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/019 b/tests/qemu-iotests/019 index 24a789a25c..a56dd30bed 100755 --- a/tests/qemu-iotests/019 +++ b/tests/qemu-iotests/019 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # When using a backing file for the output image in qemu-img convert, # the backing file clusters must not copied. The data must still be @@ -26,7 +26,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/020 b/tests/qemu-iotests/020 index eac5080f83..71fa753b4e 100755 --- a/tests/qemu-iotests/020 +++ b/tests/qemu-iotests/020 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Commit changes to backing file # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/021 b/tests/qemu-iotests/021 index 11e8ed7187..f6555f3b74 100755 --- a/tests/qemu-iotests/021 +++ b/tests/qemu-iotests/021 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test handling of invalid patterns arguments to qemu-io # @@ -24,7 +24,6 @@ owner=hch@lst.de seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/022 b/tests/qemu-iotests/022 index 2452a9f86a..b68cd64b33 100755 --- a/tests/qemu-iotests/022 +++ b/tests/qemu-iotests/022 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test bdrv_load/save_vmstate using the usual patterns # @@ -26,7 +26,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/023 b/tests/qemu-iotests/023 index 497ae1ed17..02ed047820 100755 --- a/tests/qemu-iotests/023 +++ b/tests/qemu-iotests/023 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 pattern test with various cluster sizes # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/024 b/tests/qemu-iotests/024 index 4071ed6093..23298c6f59 100755 --- a/tests/qemu-iotests/024 +++ b/tests/qemu-iotests/024 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Rebasing COW images # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/025 b/tests/qemu-iotests/025 index 70dd5f10aa..d9a4ebc5e7 100755 --- a/tests/qemu-iotests/025 +++ b/tests/qemu-iotests/025 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Resizing images # @@ -24,7 +24,6 @@ owner=stefanha@linux.vnet.ibm.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/026 b/tests/qemu-iotests/026 index 582d254195..ca89ad7048 100755 --- a/tests/qemu-iotests/026 +++ b/tests/qemu-iotests/026 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 error path testing # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/027 b/tests/qemu-iotests/027 index 08593da775..b7df9701f7 100755 --- a/tests/qemu-iotests/027 +++ b/tests/qemu-iotests/027 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that sub-cluster allocating writes zero the rest of the cluster # @@ -24,7 +24,6 @@ owner=stefanha@linux.vnet.ibm.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/028 b/tests/qemu-iotests/028 index 97a8869251..01f495912f 100755 --- a/tests/qemu-iotests/028 +++ b/tests/qemu-iotests/028 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that backing files can be smaller than the image # @@ -27,7 +27,6 @@ owner=stefanha@linux.vnet.ibm.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/029 b/tests/qemu-iotests/029 index 5cff6875bf..5f42f76cc6 100755 --- a/tests/qemu-iotests/029 +++ b/tests/qemu-iotests/029 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 internal snapshots/VM state tests # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/030 b/tests/qemu-iotests/030 index 1dbc2ddc49..c6311d1825 100755 --- a/tests/qemu-iotests/030 +++ b/tests/qemu-iotests/030 @@ -314,6 +314,23 @@ class TestParallelOps(iotests.QMPTestCase): self.wait_until_completed(drive='commit-drive0') + # In this case the base node of the stream job is the same as the + # top node of commit job. Since block-commit removes the top node + # when it finishes, this is not allowed. + def test_overlapping_4(self): + self.assert_no_active_block_jobs() + + # Commit from node2 into node0 + result = self.vm.qmp('block-commit', device='drive0', top=self.imgs[2], base=self.imgs[0]) + self.assert_qmp(result, 'return', {}) + + # Stream from node2 into node4 + result = self.vm.qmp('block-stream', device='node4', base_node='node2', job_id='node4') + self.assert_qmp(result, 'error/class', 'GenericError') + + self.wait_until_completed() + self.assert_no_active_block_jobs() + # Test a block-stream and a block-commit job in parallel # Here the stream job is supposed to finish quickly in order to reproduce # the scenario that triggers the bug fixed in 3d5d319e1221 and 1a63a907507 @@ -521,7 +538,7 @@ new_state = "2" state = "2" event = "%s" new_state = "1" -''' % (event, errno, self.STREAM_BUFFER_SIZE / 512, event, event)) +''' % (event, errno, self.STREAM_BUFFER_SIZE // 512, event, event)) file.close() class TestEIO(TestErrors): diff --git a/tests/qemu-iotests/030.out b/tests/qemu-iotests/030.out index 42314e9c00..4fd1c2dcd2 100644 --- a/tests/qemu-iotests/030.out +++ b/tests/qemu-iotests/030.out @@ -1,5 +1,5 @@ -........................ +......................... ---------------------------------------------------------------------- -Ran 24 tests +Ran 25 tests OK diff --git a/tests/qemu-iotests/031 b/tests/qemu-iotests/031 index 1e08abc5ed..ef92d8eee3 100755 --- a/tests/qemu-iotests/031 +++ b/tests/qemu-iotests/031 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that all qcow2 header extensions survive a header rewrite # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out index 7f5050b816..68a74d03b9 100644 --- a/tests/qemu-iotests/031.out +++ b/tests/qemu-iotests/031.out @@ -117,7 +117,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data Header extension: @@ -150,7 +150,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data Header extension: @@ -164,7 +164,7 @@ No errors were found on the image. magic 0x514649fb version 3 -backing_file_offset 0x148 +backing_file_offset 0x178 backing_file_size 0x17 cluster_bits 16 size 67108864 @@ -188,7 +188,7 @@ data 'host_device' Header extension: magic 0x6803f857 -length 144 +length 192 data Header extension: diff --git a/tests/qemu-iotests/032 b/tests/qemu-iotests/032 index 24bcb52fc2..a1757bb15e 100755 --- a/tests/qemu-iotests/032 +++ b/tests/qemu-iotests/032 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that AIO requests are drained before an image is closed. This used # to segfault because the request coroutine kept running even after the @@ -26,7 +26,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/033 b/tests/qemu-iotests/033 index ee8a1338bb..cfdf1ec2ba 100755 --- a/tests/qemu-iotests/033 +++ b/tests/qemu-iotests/033 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test aligned and misaligned write zeroes operations. # @@ -24,7 +24,6 @@ owner=pbonzini@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/034 b/tests/qemu-iotests/034 index 1b28bdae63..324bed28c6 100755 --- a/tests/qemu-iotests/034 +++ b/tests/qemu-iotests/034 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test bdrv_pwrite_zeroes with backing files (see also 154) # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/035 b/tests/qemu-iotests/035 index efc38e4d49..46aa835936 100755 --- a/tests/qemu-iotests/035 +++ b/tests/qemu-iotests/035 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Let a few AIO requests run in parallel and have them access different L2 # tables so that the cache has a chance to get used up. @@ -25,7 +25,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -50,7 +49,7 @@ echo echo "creating image" _make_test_img $size -function generate_requests() { +generate_requests() { for i in $(seq 0 63); do echo "aio_write ${i}M 512" echo "aio_write ${i}M 512" diff --git a/tests/qemu-iotests/036 b/tests/qemu-iotests/036 index ce638d6076..1b56394129 100755 --- a/tests/qemu-iotests/036 +++ b/tests/qemu-iotests/036 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qcow2 feature bits # @@ -27,7 +27,6 @@ owner=stefanha@linux.vnet.ibm.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/036.out b/tests/qemu-iotests/036.out index 9b009b8c15..e489b44386 100644 --- a/tests/qemu-iotests/036.out +++ b/tests/qemu-iotests/036.out @@ -58,7 +58,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data @@ -86,7 +86,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data *** done diff --git a/tests/qemu-iotests/037 b/tests/qemu-iotests/037 index c476b823d2..0781bebefe 100755 --- a/tests/qemu-iotests/037 +++ b/tests/qemu-iotests/037 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test COW from backing files # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -55,7 +54,7 @@ TEST_IMG="$TEST_IMG.base" _make_test_img $size -function backing_io() +backing_io() { local offset=$1 local sectors=$2 diff --git a/tests/qemu-iotests/038 b/tests/qemu-iotests/038 index d99a1501d7..707e2d72e9 100755 --- a/tests/qemu-iotests/038 +++ b/tests/qemu-iotests/038 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test COW from backing files with AIO # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -52,7 +51,7 @@ TEST_IMG="$TEST_IMG.base" _make_test_img $size -function backing_io() +backing_io() { local offset=$1 local sectors=$2 @@ -77,7 +76,7 @@ _make_test_img -b "$TEST_IMG.base" 6G echo echo "== Some concurrent requests touching the same cluster ==" -function overlay_io() +overlay_io() { # Start with a request touching two clusters echo aio_write -P 0x80 2020k 80k @@ -103,7 +102,7 @@ overlay_io | $QEMU_IO "$TEST_IMG" | _filter_qemu_io |\ echo echo "== Verify image content ==" -function verify_io() +verify_io() { echo read -P 31 2016k 4k echo read -P 0x80 2020k 80k diff --git a/tests/qemu-iotests/039 b/tests/qemu-iotests/039 index 1f48339692..0d4e963bd4 100755 --- a/tests/qemu-iotests/039 +++ b/tests/qemu-iotests/039 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qcow2 lazy refcounts # @@ -27,7 +27,6 @@ owner=stefanha@linux.vnet.ibm.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/040 b/tests/qemu-iotests/040 index 1beb5e6dab..b81133a474 100755 --- a/tests/qemu-iotests/040 +++ b/tests/qemu-iotests/040 @@ -57,9 +57,12 @@ class ImageCommitTestCase(iotests.QMPTestCase): self.assert_no_active_block_jobs() self.vm.shutdown() - def run_commit_test(self, top, base, need_ready=False): + def run_commit_test(self, top, base, need_ready=False, node_names=False): self.assert_no_active_block_jobs() - result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) + if node_names: + result = self.vm.qmp('block-commit', device='drive0', top_node=top, base_node=base) + else: + result = self.vm.qmp('block-commit', device='drive0', top=top, base=base) self.assert_qmp(result, 'return', {}) self.wait_for_complete(need_ready) @@ -101,6 +104,11 @@ class TestSingleDrive(ImageCommitTestCase): self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + def test_commit_node(self): + self.run_commit_test("mid", "base", node_names=True) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) + self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xef 524288 524288', backing_img).find("verification failed")) + def test_device_not_found(self): result = self.vm.qmp('block-commit', device='nonexistent', top='%s' % mid_img) self.assert_qmp(result, 'error/class', 'DeviceNotFound') @@ -123,6 +131,30 @@ class TestSingleDrive(ImageCommitTestCase): self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/desc', 'Base \'badfile\' not found') + def test_top_node_invalid(self): + self.assert_no_active_block_jobs() + result = self.vm.qmp('block-commit', device='drive0', top_node='badfile', base_node='base') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile") + + def test_base_node_invalid(self): + self.assert_no_active_block_jobs() + result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='badfile') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Cannot find device= nor node_name=badfile") + + def test_top_path_and_node(self): + self.assert_no_active_block_jobs() + result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', top='%s' % mid_img) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "'top-node' and 'top' are mutually exclusive") + + def test_base_path_and_node(self): + self.assert_no_active_block_jobs() + result = self.vm.qmp('block-commit', device='drive0', top_node='mid', base_node='base', base='%s' % backing_img) + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "'base-node' and 'base' are mutually exclusive") + def test_top_is_active(self): self.run_commit_test(test_img, backing_img, need_ready=True) self.assertEqual(-1, qemu_io('-f', 'raw', '-c', 'read -P 0xab 0 524288', backing_img).find("verification failed")) @@ -139,6 +171,22 @@ class TestSingleDrive(ImageCommitTestCase): self.assert_qmp(result, 'error/class', 'GenericError') self.assert_qmp(result, 'error/desc', 'Base \'%s\' not found' % mid_img) + def test_top_and_base_node_reversed(self): + self.assert_no_active_block_jobs() + result = self.vm.qmp('block-commit', device='drive0', top_node='base', base_node='top') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "'top' is not in this backing file chain") + + def test_top_node_in_wrong_chain(self): + self.assert_no_active_block_jobs() + + result = self.vm.qmp('blockdev-add', driver='null-co', node_name='null') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('block-commit', device='drive0', top_node='null', base_node='base') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "'null' is not in this backing file chain") + # When the job is running on a BB that is automatically deleted on hot # unplug, the job is cancelled when the device disappears def test_hot_unplug(self): @@ -147,7 +195,7 @@ class TestSingleDrive(ImageCommitTestCase): self.assert_no_active_block_jobs() result = self.vm.qmp('block-commit', device='drive0', top=mid_img, - base=backing_img, speed=(self.image_len / 4)) + base=backing_img, speed=(self.image_len // 4)) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('device_del', id='scsi0') self.assert_qmp(result, 'return', {}) @@ -177,7 +225,7 @@ class TestSingleDrive(ImageCommitTestCase): self.assert_no_active_block_jobs() result = self.vm.qmp('block-commit', device='drive0', top=mid_img, - base=backing_img, speed=(self.image_len / 4)) + base=backing_img, speed=(self.image_len // 4)) self.assert_qmp(result, 'return', {}) result = self.vm.qmp('query-block') diff --git a/tests/qemu-iotests/040.out b/tests/qemu-iotests/040.out index e20a75ce4f..802ffaa0c0 100644 --- a/tests/qemu-iotests/040.out +++ b/tests/qemu-iotests/040.out @@ -1,5 +1,5 @@ -............................. +........................................... ---------------------------------------------------------------------- -Ran 29 tests +Ran 43 tests OK diff --git a/tests/qemu-iotests/041 b/tests/qemu-iotests/041 index c20ac7da87..26bf1701eb 100755 --- a/tests/qemu-iotests/041 +++ b/tests/qemu-iotests/041 @@ -234,6 +234,12 @@ class TestSingleBlockdev(TestSingleDrive): result = self.vm.qmp("blockdev-add", **args) self.assert_qmp(result, 'return', {}) + def test_mirror_to_self(self): + result = self.vm.qmp(self.qmp_cmd, job_id='job0', + device=self.qmp_target, sync='full', + target=self.qmp_target) + self.assert_qmp(result, 'error/class', 'GenericError') + test_large_cluster = None test_image_not_found = None test_small_buffer2 = None @@ -398,7 +404,7 @@ new_state = "2" state = "2" event = "%s" new_state = "1" -''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) +''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event)) file.close() def setUp(self): @@ -463,7 +469,7 @@ new_state = "1" self.assert_qmp(event, 'data/id', 'drive0') event = self.vm.get_qmp_event(wait=True) - self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') result = self.vm.qmp('query-block-jobs') @@ -488,7 +494,7 @@ new_state = "1" self.assert_qmp(event, 'data/id', 'drive0') event = self.vm.get_qmp_event(wait=True) - self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'read') result = self.vm.qmp('query-block-jobs') @@ -563,7 +569,7 @@ new_state = "2" state = "2" event = "%s" new_state = "1" -''' % (event, errno, self.MIRROR_GRANULARITY / 512, event, event)) +''' % (event, errno, self.MIRROR_GRANULARITY // 512, event, event)) file.close() def setUp(self): @@ -619,7 +625,7 @@ new_state = "1" self.assert_qmp(result, 'return', {}) event = self.vm.event_wait(name='BLOCK_JOB_ERROR') - self.assertEquals(event['event'], 'BLOCK_JOB_ERROR') + self.assertEqual(event['event'], 'BLOCK_JOB_ERROR') self.assert_qmp(event, 'data/device', 'drive0') self.assert_qmp(event, 'data/operation', 'write') result = self.vm.qmp('query-block-jobs') diff --git a/tests/qemu-iotests/041.out b/tests/qemu-iotests/041.out index c28b392b87..e071d0b261 100644 --- a/tests/qemu-iotests/041.out +++ b/tests/qemu-iotests/041.out @@ -1,5 +1,5 @@ -..................................................................................... +........................................................................................ ---------------------------------------------------------------------- -Ran 85 tests +Ran 88 tests OK diff --git a/tests/qemu-iotests/042 b/tests/qemu-iotests/042 index a53e7cb757..a9a7fc3041 100755 --- a/tests/qemu-iotests/042 +++ b/tests/qemu-iotests/042 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qemu-img operation on zero size images # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/043 b/tests/qemu-iotests/043 index 1c6c22d92a..9894b154ec 100755 --- a/tests/qemu-iotests/043 +++ b/tests/qemu-iotests/043 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that qemu-img info --backing-chain detects infinite loops # @@ -24,7 +24,6 @@ owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/044 b/tests/qemu-iotests/044 index 11ea0f4d35..9ec3dba734 100755 --- a/tests/qemu-iotests/044 +++ b/tests/qemu-iotests/044 @@ -26,6 +26,10 @@ import iotests from iotests import qemu_img, qemu_img_verbose, qemu_io import struct import subprocess +import sys + +if sys.version_info.major == 2: + range = xrange test_img = os.path.join(iotests.test_dir, 'test.img') @@ -52,23 +56,23 @@ class TestRefcountTableGrowth(iotests.QMPTestCase): # Write a refcount table fd.seek(off_reftable) - for i in xrange(0, h.refcount_table_clusters): - sector = ''.join(struct.pack('>Q', + for i in range(0, h.refcount_table_clusters): + sector = b''.join(struct.pack('>Q', off_refblock + i * 64 * 512 + j * 512) - for j in xrange(0, 64)) + for j in range(0, 64)) fd.write(sector) # Write the refcount blocks assert(fd.tell() == off_refblock) - sector = ''.join(struct.pack('>H', 1) for j in xrange(0, 64 * 256)) - for block in xrange(0, h.refcount_table_clusters): + sector = b''.join(struct.pack('>H', 1) for j in range(0, 64 * 256)) + for block in range(0, h.refcount_table_clusters): fd.write(sector) # Write the L1 table assert(fd.tell() == off_l1) assert(off_l2 + 512 * h.l1_size == off_data) - table = ''.join(struct.pack('>Q', (1 << 63) | off_l2 + 512 * j) - for j in xrange(0, h.l1_size)) + table = b''.join(struct.pack('>Q', (1 << 63) | off_l2 + 512 * j) + for j in range(0, h.l1_size)) fd.write(table) # Write the L2 tables @@ -79,14 +83,14 @@ class TestRefcountTableGrowth(iotests.QMPTestCase): off = off_data while remaining > 1024 * 512: pytable = list((1 << 63) | off + 512 * j - for j in xrange(0, 1024)) + for j in range(0, 1024)) table = struct.pack('>1024Q', *pytable) fd.write(table) remaining = remaining - 1024 * 512 off = off + 1024 * 512 - table = ''.join(struct.pack('>Q', (1 << 63) | off + 512 * j) - for j in xrange(0, remaining / 512)) + table = b''.join(struct.pack('>Q', (1 << 63) | off + 512 * j) + for j in range(0, remaining // 512)) fd.write(table) diff --git a/tests/qemu-iotests/045 b/tests/qemu-iotests/045 index 6be8fc4912..d5484a0ee1 100755 --- a/tests/qemu-iotests/045 +++ b/tests/qemu-iotests/045 @@ -132,7 +132,7 @@ class TestSCMFd(iotests.QMPTestCase): qemu_img('create', '-f', iotests.imgfmt, image0, '128K') # Add an unused monitor, to verify it works fine when two monitor # instances present - self.vm.add_monitor_telnet("0",4445) + self.vm.add_monitor_null() self.vm.launch() def tearDown(self): @@ -140,7 +140,7 @@ class TestSCMFd(iotests.QMPTestCase): os.remove(image0) def _send_fd_by_SCM(self): - ret = self.vm.send_fd_scm(image0) + ret = self.vm.send_fd_scm(file_path=image0) self.assertEqual(ret, 0, 'Failed to send fd with UNIX SCM') def test_add_fd(self): diff --git a/tests/qemu-iotests/046 b/tests/qemu-iotests/046 index f2ebecf24c..95160bea4c 100755 --- a/tests/qemu-iotests/046 +++ b/tests/qemu-iotests/046 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test concurrent cluster allocations # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -49,7 +48,7 @@ echo "== creating backing file for COW tests ==" _make_test_img $size -function backing_io() +backing_io() { local offset=$1 local sectors=$2 @@ -74,7 +73,7 @@ _make_test_img -b "$TEST_IMG.base" 6G echo echo "== Some concurrent requests touching the same cluster ==" -function overlay_io() +overlay_io() { # Allocate middle of cluster 1, then write to somewhere before and after it cat < /dev/null); then # For v2 images, discarded clusters are read from the backing file diff --git a/tests/qemu-iotests/047 b/tests/qemu-iotests/047 index 1b8f3d4a64..ce81fc6fa7 100755 --- a/tests/qemu-iotests/047 +++ b/tests/qemu-iotests/047 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Regression test for commit b7ab0fea (which was a corruption fix, # despite the commit message claiming otherwise) @@ -25,7 +25,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -46,7 +45,7 @@ size=128M _make_test_img $size -function qemu_io_cmds() +qemu_io_cmds() { cat <&1 | filter_test_dir diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 0871bff564..6b505408dd 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -95,35 +95,31 @@ 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: Value '-1024' is out of range for parameter 'size' -qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' +qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size' 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: Value '-1k' is out of range for parameter 'size' -qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' +qemu-img: TEST_DIR/t.qcow2: Value '-1k' is out of range for parameter 'size' 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 qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 -qemu-img: Parameter 'size' expects a non-negative number below 2^64 +qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- and exabytes, respectively. -qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar qemu-img: Invalid image size specified! You may use k, M, G, T, P or E suffixes for 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 non-negative number below 2^64 +qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- and exabytes, respectively. -qemu-img: TEST_DIR/t.qcow2: Invalid options for file format 'qcow2' == Check correct interpretation of suffixes for cluster size == diff --git a/tests/qemu-iotests/050 b/tests/qemu-iotests/050 index 03b4a5d620..dd7b2c72eb 100755 --- a/tests/qemu-iotests/050 +++ b/tests/qemu-iotests/050 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qemu-img rebase with zero clusters # @@ -24,7 +24,6 @@ owner=pbonzini@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/051 b/tests/qemu-iotests/051 index ee9c820d0f..02ac960da4 100755 --- a/tests/qemu-iotests/051 +++ b/tests/qemu-iotests/051 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test command line configuration of block devices and driver-specific options # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -44,7 +43,7 @@ _supported_os Linux # other than refcount_bits=16 _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" ( @@ -58,7 +57,7 @@ function do_run_qemu() echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_generated_node_ids | _filter_hmp @@ -354,6 +353,16 @@ printf %b "qemu-io $device_id \"write -P 0x33 0 4k\"\ncommit $device_id\n" | $QEMU_IO -c "read -P 0x33 0 4k" "$TEST_IMG" | _filter_qemu_io +# Using snapshot=on with a non-existent TMPDIR +TMPDIR=/nonexistent run_qemu -drive driver=null-co,snapshot=on + +# Using snapshot=on together with read-only=on +echo "info block" | + run_qemu -drive file="$TEST_IMG",snapshot=on,read-only=on,if=none,id=$device_id | + _filter_qemu_io | + sed -e 's#"[^"]*/vl\.[A-Za-z0-9]\{6\}"#SNAPSHOT_PATH#g' + + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/051.out b/tests/qemu-iotests/051.out index b7273505c7..9f1cf22608 100644 --- a/tests/qemu-iotests/051.out +++ b/tests/qemu-iotests/051.out @@ -82,7 +82,7 @@ QEMU X.Y.Z monitor - type 'help' for more information 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.orig"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) @@ -172,7 +172,7 @@ 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) @@ -192,7 +192,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only) 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2) Removable device: not locked, tray closed Cache mode: writethrough Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) @@ -212,7 +212,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only) 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "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) @@ -455,4 +455,16 @@ wrote 4096/4096 bytes at offset 0 read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: -drive driver=null-co,snapshot=on +QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory + +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) info block +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only) + Removable device: not locked, tray closed + Cache mode: writeback, ignore flushes + Backing file: TEST_DIR/t.qcow2 (chain depth: 1) +(qemu) quit + *** done diff --git a/tests/qemu-iotests/051.pc.out b/tests/qemu-iotests/051.pc.out index e9257fe318..c4743cc31c 100644 --- a/tests/qemu-iotests/051.pc.out +++ b/tests/qemu-iotests/051.pc.out @@ -82,7 +82,7 @@ QEMU X.Y.Z monitor - type 'help' for more information 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.orig"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.orig (chain depth: 1) @@ -244,7 +244,7 @@ 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2) Removable device: not locked, tray closed Cache mode: writeback Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) @@ -264,7 +264,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only) 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}} (qcow2) Removable device: not locked, tray closed Cache mode: writethrough Backing file: TEST_DIR/t.qcow2.base (chain depth: 1) @@ -284,7 +284,7 @@ backing-file: TEST_DIR/t.qcow2.base (file, read-only) 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) info block -drive0 (NODE_NAME): TEST_DIR/t.qcow2 (qcow2) +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2.base"}}, "driver": "qcow2", "file": {"driver": "file", "filename": "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) @@ -527,4 +527,16 @@ wrote 4096/4096 bytes at offset 0 read 4096/4096 bytes at offset 0 4 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Testing: -drive driver=null-co,snapshot=on +QEMU_PROG: -drive driver=null-co,snapshot=on: Could not get temporary filename: No such file or directory + +Testing: -drive file=TEST_DIR/t.qcow2,snapshot=on,read-only=on,if=none,id=drive0 +QEMU X.Y.Z monitor - type 'help' for more information +(qemu) info block +drive0 (NODE_NAME): json:{"backing": {"driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/t.qcow2"}}, "driver": "qcow2", "file": {"driver": "file", "filename": SNAPSHOT_PATH}} (qcow2, read-only) + Removable device: not locked, tray closed + Cache mode: writeback, ignore flushes + Backing file: TEST_DIR/t.qcow2 (chain depth: 1) +(qemu) quit + *** done diff --git a/tests/qemu-iotests/052 b/tests/qemu-iotests/052 index 842eaced3b..b3a2dc1143 100755 --- a/tests/qemu-iotests/052 +++ b/tests/qemu-iotests/052 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test bdrv_read/bdrv_write using BDRV_O_SNAPSHOT # @@ -24,7 +24,6 @@ owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/053 b/tests/qemu-iotests/053 index 2a04f5f551..50c62f0f56 100755 --- a/tests/qemu-iotests/053 +++ b/tests/qemu-iotests/053 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qemu-img convert when image length is not a multiple of cluster size # @@ -24,7 +24,6 @@ owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/054 b/tests/qemu-iotests/054 index bf47ef9fac..0d5e14f847 100755 --- a/tests/qemu-iotests/054 +++ b/tests/qemu-iotests/054 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test huge qcow2 images # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/056 b/tests/qemu-iotests/056 index 223292175a..3df323984d 100755 --- a/tests/qemu-iotests/056 +++ b/tests/qemu-iotests/056 @@ -32,7 +32,7 @@ target_img = os.path.join(iotests.test_dir, 'target.img') def img_create(img, fmt=iotests.imgfmt, size='64M', **kwargs): fullname = os.path.join(iotests.test_dir, '%s.%s' % (img, fmt)) optargs = [] - for k,v in kwargs.iteritems(): + for k,v in kwargs.items(): optargs = optargs + ['-o', '%s=%s' % (k,v)] args = ['create', '-f', fmt] + optargs + [fullname, size] iotests.qemu_img(*args) diff --git a/tests/qemu-iotests/058 b/tests/qemu-iotests/058 index 5eb8784669..8c3212a72f 100755 --- a/tests/qemu-iotests/058 +++ b/tests/qemu-iotests/058 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test export internal snapshot by qemu-nbd, convert it by qemu-img. # @@ -26,58 +26,21 @@ owner=xiawenc@linux.vnet.ibm.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` 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() -{ - 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" -} - -_wait_for_nbd() -{ - for ((i = 0; i < 300; i++)) - do - if [ -r "$nbd_unix_socket" ]; then - return - fi - sleep 0.1 - done - echo "Failed in check of unix socket created by qemu-nbd" - exit 1 -} - -converted_image=$TEST_IMG.converted - _export_nbd_snapshot() { - _cleanup_nbd - $QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l $1 & - _wait_for_nbd + nbd_server_start_unix_socket "$TEST_IMG" -l $1 } _export_nbd_snapshot1() { - _cleanup_nbd - $QEMU_NBD -v -t -k "$nbd_unix_socket" "$TEST_IMG" -l snapshot.name=$1 & - _wait_for_nbd + nbd_server_start_unix_socket "$TEST_IMG" -l snapshot.name=$1 } _cleanup() { - _cleanup_nbd + nbd_server_stop _cleanup_test_img rm -f "$converted_image" } @@ -87,6 +50,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter . ./common.pattern +. ./common.nbd _supported_fmt qcow2 _supported_proto file @@ -95,6 +59,10 @@ _require_command QEMU_NBD # Internal snapshots are (currently) impossible with refcount_bits=1 _unsupported_imgopts 'refcount_bits=1[^0-9]' +nbd_snapshot_img="nbd:unix:$nbd_unix_socket" + +converted_image=$TEST_IMG.converted + # Use -f raw instead of -f $IMGFMT for the NBD connection QEMU_IO_NBD="$QEMU_IO -f raw --cache=$CACHEMODE" diff --git a/tests/qemu-iotests/059 b/tests/qemu-iotests/059 index 530bbbe6ce..279aee6815 100755 --- a/tests/qemu-iotests/059 +++ b/tests/qemu-iotests/059 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for vmdk # @@ -24,7 +24,6 @@ owner=famz@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/060 b/tests/qemu-iotests/060 index 74ad371885..89e911400c 100755 --- a/tests/qemu-iotests/060 +++ b/tests/qemu-iotests/060 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for image corruption (overlapping data structures) in qcow2 # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/060.out b/tests/qemu-iotests/060.out index d67c6234a4..af623cfd86 100644 --- a/tests/qemu-iotests/060.out +++ b/tests/qemu-iotests/060.out @@ -419,7 +419,7 @@ QMP_VERSION write failed: Input/output error {"return": ""} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Testing incoming inactive corrupted image === @@ -430,7 +430,7 @@ qcow2: Image is corrupt: L2 table offset 0x2a2a2a00 unaligned (L1 index: 0); fur {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_IMAGE_CORRUPTED", "data": {"device": "", "msg": "L2 table offset 0x2a2a2a00 unaligned (L1 index: 0)", "node-name": "drive", "fatal": false}} {"return": ""} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} corrupt: false *** done diff --git a/tests/qemu-iotests/061 b/tests/qemu-iotests/061 index 911b6f2894..d7dbd7e2c7 100755 --- a/tests/qemu-iotests/061 +++ b/tests/qemu-iotests/061 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for image option amendment in qcow2. # @@ -24,12 +24,12 @@ 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 + _cleanup_test_img + rm -f $TEST_IMG.data } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -251,6 +251,48 @@ $QEMU_IMG snapshot -c foo "$TEST_IMG" $QEMU_IMG amend -p -o "compat=0.10" "$TEST_IMG" _check_test_img +echo +echo "=== Testing version downgrade with external data file ===" +echo +IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M +$QEMU_IMG amend -o "compat=0.10" "$TEST_IMG" +_img_info --format-specific +_check_test_img + +echo +echo "=== Try changing the external data file ===" +echo +IMGOPTS="compat=1.1" _make_test_img 64M +$QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" + +echo +IMGOPTS="compat=1.1,data_file=$TEST_IMG.data" _make_test_img 64M +$QEMU_IMG amend -o "data_file=foo" "$TEST_IMG" +_img_info --format-specific +TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts + +echo +$QEMU_IMG amend -o "data_file=" --image-opts "data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" +_img_info --format-specific +TEST_IMG="data-file.filename=$TEST_IMG.data,file.filename=$TEST_IMG" _img_info --format-specific --image-opts + +echo +echo "=== Clearing and setting data-file-raw ===" +echo +IMGOPTS="compat=1.1,data_file=$TEST_IMG.data,data_file_raw=on" _make_test_img 64M +$QEMU_IMG amend -o "data_file_raw=on" "$TEST_IMG" +_img_info --format-specific +_check_test_img + +$QEMU_IMG amend -o "data_file_raw=off" "$TEST_IMG" +_img_info --format-specific +_check_test_img + +$QEMU_IMG amend -o "data_file_raw=on" "$TEST_IMG" +_img_info --format-specific +_check_test_img + + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 183f7dd690..9fe1ec702f 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -26,7 +26,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data magic 0x514649fb @@ -84,7 +84,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data magic 0x514649fb @@ -144,7 +144,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data ERROR cluster 5 refcount=0 reference=1 @@ -199,7 +199,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data magic 0x514649fb @@ -268,7 +268,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data read 65536/65536 bytes at offset 44040192 @@ -306,7 +306,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data ERROR cluster 5 refcount=0 reference=1 @@ -335,7 +335,7 @@ header_length 104 Header extension: magic 0x6803f857 -length 144 +length 192 data read 131072/131072 bytes at offset 0 @@ -488,4 +488,93 @@ wrote 65536/65536 bytes at offset 3221225472 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) (0.00/100%) (6.25/100%) (12.50/100%) (18.75/100%) (25.00/100%) (31.25/100%) (37.50/100%) (43.75/100%) (50.00/100%) (56.25/100%) (62.50/100%) (68.75/100%) (75.00/100%) (81.25/100%) (87.50/100%) (93.75/100%) (100.00/100%) (100.00/100%) No errors were found on the image. + +=== Testing version downgrade with external data file === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data +qemu-img: Cannot downgrade an image with a data file +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + data file: TEST_DIR/t.IMGFMT.data + data file raw: false + corrupt: false +No errors were found on the image. + +=== Try changing the external data file === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +qemu-img: data-file can only be set for images that use an external data file + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': Could not open 'foo': No such file or directory +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + data file: foo + data file raw: false + corrupt: false + +qemu-img: Could not open 'TEST_DIR/t.IMGFMT': 'data-file' is required for this image +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + data file raw: false + corrupt: false + +=== Clearing and setting data-file-raw === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + data file: TEST_DIR/t.IMGFMT.data + data file raw: true + corrupt: false +No errors were found on the image. +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + data file: TEST_DIR/t.IMGFMT.data + data file raw: false + corrupt: false +No errors were found on the image. +qemu-img: data-file-raw cannot be set on existing images +image: TEST_DIR/t.IMGFMT +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + data file: TEST_DIR/t.IMGFMT.data + data file raw: false + corrupt: false +No errors were found on the image. *** done diff --git a/tests/qemu-iotests/062 b/tests/qemu-iotests/062 index 051fb9f410..ed7400fed2 100755 --- a/tests/qemu-iotests/062 +++ b/tests/qemu-iotests/062 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for snapshotting images with unallocated zero clusters in # qcow2 @@ -25,7 +25,6 @@ owner=mreitz@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/063 b/tests/qemu-iotests/063 index adc037c1f5..2d5c0ce9fb 100755 --- a/tests/qemu-iotests/063 +++ b/tests/qemu-iotests/063 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # test of qemu-img convert -n - convert without creation # @@ -25,7 +25,6 @@ owner=alex@alex.org.uk seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/064 b/tests/qemu-iotests/064 index 5792fbbc92..90673186ec 100755 --- a/tests/qemu-iotests/064 +++ b/tests/qemu-iotests/064 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test VHDX read/write from a sample image created with Hyper-V # @@ -24,7 +24,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/065 b/tests/qemu-iotests/065 index 72aa9707c7..8bac383ea7 100755 --- a/tests/qemu-iotests/065 +++ b/tests/qemu-iotests/065 @@ -59,7 +59,7 @@ class TestQemuImgInfo(TestImageInfoSpecific): :data.index('')] for field in data: self.assertTrue(re.match('^ {4}[^ ]', field) is not None) - data = map(lambda line: line.strip(), data) + data = [line.strip() for line in data] self.assertEqual(data, self.human_compare) class TestQMP(TestImageInfoSpecific): @@ -80,7 +80,7 @@ class TestQMP(TestImageInfoSpecific): def test_qmp(self): result = self.vm.qmp('query-block')['return'] - drive = filter(lambda drive: drive['device'] == 'drive0', result)[0] + drive = next(drive for drive in result if drive['device'] == 'drive0') data = drive['inserted']['image']['format-specific'] self.assertEqual(data['type'], iotests.imgfmt) self.assertEqual(data['data'], self.compare) diff --git a/tests/qemu-iotests/066 b/tests/qemu-iotests/066 index 8638217736..f480986e35 100755 --- a/tests/qemu-iotests/066 +++ b/tests/qemu-iotests/066 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for preallocated zero clusters in qcow2 # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/067 b/tests/qemu-iotests/067 index fe259f6165..fda16a6b0d 100755 --- a/tests/qemu-iotests/067 +++ b/tests/qemu-iotests/067 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test automatic deletion of BDSes created by -drive/drive_add # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! # get standard environment, filters and checks @@ -37,7 +36,7 @@ _supported_os Linux # Because anything other than 16 would change the output of query-block _unsupported_imgopts 'refcount_bits=\([^1]\|.\([^6]\|$\)\)' -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" $QEMU -nographic -qmp-pretty stdio -serial none "$@" @@ -53,7 +52,7 @@ _filter_qmp_events() | tr '\t' '\n' } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp | _filter_qemu \ | _filter_actual_image_size \ diff --git a/tests/qemu-iotests/067.out b/tests/qemu-iotests/067.out index 2e71cff3ce..b10c71db03 100644 --- a/tests/qemu-iotests/067.out +++ b/tests/qemu-iotests/067.out @@ -385,6 +385,7 @@ Testing: -device virtio-scsi -device scsi-cd,id=cd0 { "return": [ { + "io-status": "ok", "device": "", "locked": false, "removable": true, diff --git a/tests/qemu-iotests/068 b/tests/qemu-iotests/068 index e7fca6a494..881a022107 100755 --- a/tests/qemu-iotests/068 +++ b/tests/qemu-iotests/068 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for loading a saved VM state from a qcow2 image # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/069 b/tests/qemu-iotests/069 index 96e55ef216..6a8e4aa22e 100755 --- a/tests/qemu-iotests/069 +++ b/tests/qemu-iotests/069 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for deleting a backing file # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/070 b/tests/qemu-iotests/070 index 8d08d74ff9..cb0f927c16 100755 --- a/tests/qemu-iotests/070 +++ b/tests/qemu-iotests/070 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test VHDX log replay from an image with a journal that needs to be # replayed @@ -25,7 +25,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/071 b/tests/qemu-iotests/071 index 48b495513f..7f3e5abd57 100755 --- a/tests/qemu-iotests/071 +++ b/tests/qemu-iotests/071 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for the QMP blkdebug and blkverify interfaces # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() @@ -41,14 +40,14 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" | _filter_imgfmt $QEMU -nographic -qmp stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp | _filter_qemu_io } diff --git a/tests/qemu-iotests/071.out b/tests/qemu-iotests/071.out index 1d5e28d730..bca0c02f5c 100644 --- a/tests/qemu-iotests/071.out +++ b/tests/qemu-iotests/071.out @@ -46,7 +46,7 @@ QMP_VERSION read failed: Input/output error {"return": ""} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Testing blkverify on existing block device === @@ -85,7 +85,7 @@ wrote 512/512 bytes at offset 0 read failed: Input/output error {"return": ""} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} QEMU_PROG: Failed to flush the L2 table cache: Input/output error QEMU_PROG: Failed to flush the refcount block cache: Input/output error diff --git a/tests/qemu-iotests/072 b/tests/qemu-iotests/072 index aa027c7d29..6f9f247fa5 100755 --- a/tests/qemu-iotests/072 +++ b/tests/qemu-iotests/072 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for nested image formats # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/073 b/tests/qemu-iotests/073 index 40f85b18b9..990f90acbd 100755 --- a/tests/qemu-iotests/073 +++ b/tests/qemu-iotests/073 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test count_contiguous_clusters in qcow2 # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/074 b/tests/qemu-iotests/074 index b17866bd34..bb4ad1cc08 100755 --- a/tests/qemu-iotests/074 +++ b/tests/qemu-iotests/074 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash ## ## qemu-img compare test (qcow2 only ones) ## diff --git a/tests/qemu-iotests/075 b/tests/qemu-iotests/075 index caa30d4743..389d5675fa 100755 --- a/tests/qemu-iotests/075 +++ b/tests/qemu-iotests/075 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # cloop format input validation tests # @@ -24,7 +24,6 @@ owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/076 b/tests/qemu-iotests/076 index ef9e6a4ff3..0d405ef3f2 100755 --- a/tests/qemu-iotests/076 +++ b/tests/qemu-iotests/076 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # parallels format input validation tests # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/077 b/tests/qemu-iotests/077 index b3c6fb1370..c284952082 100755 --- a/tests/qemu-iotests/077 +++ b/tests/qemu-iotests/077 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test concurrent pread/pwrite # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -49,7 +48,7 @@ _make_test_img $size echo echo "== Some concurrent requests involving RMW ==" -function test_io() +test_io() { echo "open -o driver=$IMGFMT,file.align=4k blkdebug::$TEST_IMG" # A simple RMW request @@ -194,7 +193,7 @@ test_io | $QEMU_IO | _filter_qemu_io | \ echo echo "== Verify image content ==" -function verify_io() +verify_io() { # A simple RMW request echo read -P 0 0 0x200 diff --git a/tests/qemu-iotests/078 b/tests/qemu-iotests/078 index a106c26f6b..54fc654d8e 100755 --- a/tests/qemu-iotests/078 +++ b/tests/qemu-iotests/078 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # bochs format input validation tests # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/079 b/tests/qemu-iotests/079 index b2e3f7426a..1b6594ebef 100755 --- a/tests/qemu-iotests/079 +++ b/tests/qemu-iotests/079 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qcow2 preallocation with different cluster_sizes # @@ -24,7 +24,6 @@ owner=hutao@cn.fujitsu.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/080 b/tests/qemu-iotests/080 index f0eb42f390..4bcb5021e8 100755 --- a/tests/qemu-iotests/080 +++ b/tests/qemu-iotests/080 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 format input validation tests # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/081 b/tests/qemu-iotests/081 index da3fb0984b..c418bab093 100755 --- a/tests/qemu-iotests/081 +++ b/tests/qemu-iotests/081 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test Quorum block driver # @@ -24,7 +24,6 @@ owner=benoit@irqsave.net seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -43,14 +42,14 @@ _supported_fmt raw _supported_proto file _supported_os Linux -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" | _filter_imgfmt $QEMU -nographic -qmp stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ | _filter_qemu_io | _filter_generated_node_ids @@ -168,6 +167,122 @@ echo "== checking that quorum is broken ==" $QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io +echo +echo "== checking the blkverify mode with broken content ==" + +quorum="driver=raw,file.driver=quorum,file.vote-threshold=2,file.blkverify=on" +quorum="$quorum,file.children.0.file.filename=$TEST_DIR/1.raw" +quorum="$quorum,file.children.1.file.filename=$TEST_DIR/2.raw" +quorum="$quorum,file.children.0.driver=raw" +quorum="$quorum,file.children.1.driver=raw" + +$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io + +echo +echo "== writing the same data to both files ==" + +$QEMU_IO -c "write -P 0x32 0 $size" "$TEST_DIR/1.raw" | _filter_qemu_io +$QEMU_IO -c "write -P 0x32 0 $size" "$TEST_DIR/2.raw" | _filter_qemu_io + +echo +echo "== checking the blkverify mode with valid content ==" + +$QEMU_IO -c "open -o $quorum" -c "read -P 0x32 0 $size" | _filter_qemu_io + +echo +echo "== checking the blkverify mode with invalid settings ==" + +quorum="$quorum,file.children.2.file.filename=$TEST_DIR/3.raw" +quorum="$quorum,file.children.2.driver=raw" + +$QEMU_IO -c "open -o $quorum" | _filter_qemu_io + +echo +echo "== dynamically adding a child to a quorum ==" + +for verify in false true; do + run_qemu < - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o ? TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 128M Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -f qcow2 -u -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 128M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=134217728 backing_file=TEST_DIR/t.qcow2,,help cluster_size=65536 lazy_refcounts=off refcount_bits=16 @@ -227,27 +243,29 @@ qemu-img: Invalid option list: ,, Testing: create -f qcow2 -o help Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: create -o help Supported options: -size Virtual disk size + size= - Virtual disk size Testing: create -f bochs -o help qemu-img: Format driver 'bochs' does not support image creation @@ -300,171 +318,187 @@ cluster_size: 8192 Testing: convert -O qcow2 -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits -nocow Turn off copy-on-write (valid only on btrfs) + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + nocow= - Turn off copy-on-write (valid only on btrfs) + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -O qcow2 -o backing_file=TEST_DIR/t.qcow2,,help TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.base qemu-img: Could not open 'TEST_DIR/t.qcow2.base': Could not open backing file: Could not open 'TEST_DIR/t.qcow2,help': No such file or directory @@ -483,27 +517,29 @@ qemu-img: Invalid option list: ,, Testing: convert -O qcow2 -o help Supported options: -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Testing: convert -o help Supported options: -size Virtual disk size + size= - Virtual disk size Testing: convert -O bochs -o help qemu-img: Format driver 'bochs' does not support image creation @@ -564,177 +600,193 @@ cluster_size: 65536 Testing: amend -f qcow2 -o help TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o ? TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k,help TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k,? TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o help,cluster_size=4k TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o ?,cluster_size=4k TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k -o help TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: amend -f qcow2 -o cluster_size=4k -o ? TEST_DIR/t.qcow2 Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. @@ -757,29 +809,31 @@ qemu-img: Invalid option list: ,, Testing: amend -f qcow2 -o help Creation options for 'qcow2': -size Virtual disk size -compat Compatibility level (0.10 or 1.1) -backing_file File name of a base image -backing_fmt Image format of the base image -encryption Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) -encrypt.format Encrypt the image, format choices: 'aes', 'luks' -encrypt.key-secret ID of secret providing qcow AES key or LUKS passphrase -encrypt.cipher-alg Name of encryption cipher algorithm -encrypt.cipher-mode Name of encryption cipher mode -encrypt.ivgen-alg Name of IV generator algorithm -encrypt.ivgen-hash-alg Name of IV generator hash algorithm -encrypt.hash-alg Name of encryption hash algorithm -encrypt.iter-time Time to spend in PBKDF in milliseconds -cluster_size qcow2 cluster size -preallocation Preallocation mode (allowed values: off, metadata, falloc, full) -lazy_refcounts Postpone refcount updates -refcount_bits Width of a reference count entry in bits + backing_file= - File name of a base image + backing_fmt= - Image format of the base image + cluster_size= - qcow2 cluster size + compat= - Compatibility level (0.10 or 1.1) + data_file= - File name of an external data file + data_file_raw= - The external data file must stay valid as a raw image + encrypt.cipher-alg= - Name of encryption cipher algorithm + encrypt.cipher-mode= - Name of encryption cipher mode + encrypt.format= - Encrypt the image, format choices: 'aes', 'luks' + encrypt.hash-alg= - Name of encryption hash algorithm + encrypt.iter-time= - Time to spend in PBKDF in milliseconds + encrypt.ivgen-alg= - Name of IV generator algorithm + encrypt.ivgen-hash-alg= - Name of IV generator hash algorithm + encrypt.key-secret= - ID of secret providing qcow AES key or LUKS passphrase + encryption= - Encrypt the image with format 'aes'. (Deprecated in favor of encrypt.format=aes) + lazy_refcounts= - Postpone refcount updates + preallocation= - Preallocation mode (allowed values: off, metadata, falloc, full) + refcount_bits= - Width of a reference count entry in bits + size= - Virtual disk size Note that not all of these options may be amendable. Testing: convert -o help Supported options: -size Virtual disk size + size= - Virtual disk size Testing: amend -f bochs -o help qemu-img: Format driver 'bochs' does not support option amendment diff --git a/tests/qemu-iotests/083 b/tests/qemu-iotests/083 index 3c1adbf0fb..b270550d3e 100755 --- a/tests/qemu-iotests/083 +++ b/tests/qemu-iotests/083 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test NBD client unexpected disconnect # @@ -24,7 +24,6 @@ owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -39,7 +38,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter -_supported_fmt generic +_supported_fmt raw _supported_proto nbd _supported_os Linux diff --git a/tests/qemu-iotests/083.out b/tests/qemu-iotests/083.out index be6079d27e..7419722cd7 100644 --- a/tests/qemu-iotests/083.out +++ b/tests/qemu-iotests/083.out @@ -41,7 +41,6 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect after neg2 === -Connection closed read failed: Input/output error === Check disconnect 8 neg2 === @@ -54,39 +53,30 @@ can't open device nbd+tcp://127.0.0.1:PORT/foo === Check disconnect before request === -Connection closed read failed: Input/output error === Check disconnect after request === -Connection closed read failed: Input/output error === Check disconnect before reply === -Connection closed read failed: Input/output error === Check disconnect after reply === -Unexpected end-of-file before all bytes were read read failed: Input/output error === Check disconnect 4 reply === -Unexpected end-of-file before all bytes were read -Connection closed read failed: Input/output error === Check disconnect 8 reply === -Unexpected end-of-file before all bytes were read -Connection closed read failed: Input/output error === Check disconnect before data === -Unexpected end-of-file before all bytes were read read failed: Input/output error === Check disconnect after data === @@ -116,7 +106,6 @@ can't open device nbd+tcp://127.0.0.1:PORT/ === Check disconnect after neg-classic === -Connection closed read failed: Input/output error === Check disconnect before neg1 === @@ -177,34 +166,26 @@ read failed: Input/output error === Check disconnect after request === -Connection closed read failed: Input/output error === Check disconnect before reply === -Connection closed read failed: Input/output error === Check disconnect after reply === -Unexpected end-of-file before all bytes were read read failed: Input/output error === Check disconnect 4 reply === -Unexpected end-of-file before all bytes were read -Connection closed read failed: Input/output error === Check disconnect 8 reply === -Unexpected end-of-file before all bytes were read -Connection closed read failed: Input/output error === Check disconnect before data === -Unexpected end-of-file before all bytes were read read failed: Input/output error === Check disconnect after data === diff --git a/tests/qemu-iotests/084 b/tests/qemu-iotests/084 index 04f2aa9d7d..c29d7395e9 100755 --- a/tests/qemu-iotests/084 +++ b/tests/qemu-iotests/084 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for VDI header corruption; image too large, and too many blocks. # Also simple test for creating dynamic and static VDI images. @@ -25,7 +25,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/085 b/tests/qemu-iotests/085 index 5c7668cf9b..68cb665987 100755 --- a/tests/qemu-iotests/085 +++ b/tests/qemu-iotests/085 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Live snapshot tests # @@ -29,7 +29,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! snapshot_virt0="snapshot-v0.qcow2" @@ -61,7 +60,7 @@ _supported_os Linux # ${1}: unique identifier for the snapshot filename -function create_single_snapshot() +create_single_snapshot() { cmd="{ 'execute': 'blockdev-snapshot-sync', 'arguments': { 'device': 'virtio0', @@ -71,7 +70,7 @@ function create_single_snapshot() } # ${1}: unique identifier for the snapshot filename -function create_group_snapshot() +create_group_snapshot() { cmd="{ 'execute': 'transaction', 'arguments': {'actions': [ @@ -89,7 +88,7 @@ function create_group_snapshot() # ${1}: unique identifier for the snapshot filename # ${2}: extra_params to the blockdev-add command # ${3}: filename -function do_blockdev_add() +do_blockdev_add() { cmd="{ 'execute': 'blockdev-add', 'arguments': { 'driver': 'qcow2', 'node-name': 'snap_${1}', ${2} @@ -100,7 +99,7 @@ function do_blockdev_add() } # ${1}: unique identifier for the snapshot filename -function add_snapshot_image() +add_snapshot_image() { base_image="${TEST_DIR}/$((${1}-1))-${snapshot_virt0}" snapshot_file="${TEST_DIR}/${1}-${snapshot_virt0}" @@ -111,7 +110,7 @@ function add_snapshot_image() # ${1}: unique identifier for the snapshot filename # ${2}: expected response, defaults to 'return' -function blockdev_snapshot() +blockdev_snapshot() { cmd="{ 'execute': 'blockdev-snapshot', 'arguments': { 'node': 'virtio0', diff --git a/tests/qemu-iotests/086 b/tests/qemu-iotests/086 index 84e3835071..fea1a7bd8a 100755 --- a/tests/qemu-iotests/086 +++ b/tests/qemu-iotests/086 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qemu-img progress output # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -41,7 +40,7 @@ _supported_fmt qcow2 raw _supported_proto file _supported_os Linux -function run_qemu_img() +run_qemu_img() { echo echo Testing: "$@" | _filter_testdir diff --git a/tests/qemu-iotests/087 b/tests/qemu-iotests/087 index 2561a14456..d6c8613419 100755 --- a/tests/qemu-iotests/087 +++ b/tests/qemu-iotests/087 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test unsupported blockdev-add cases # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! # get standard environment, filters and checks @@ -35,14 +34,14 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" $QEMU -nographic -qmp stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ | _filter_qemu | _filter_imgfmt \ @@ -103,7 +102,7 @@ echo === aio=native without O_DIRECT === echo # Skip this test if AIO is not enabled in this build -function run_qemu_filter_aio() +run_qemu_filter_aio() { run_qemu "$@" | \ sed -e 's/is not supported in this build/it requires cache.direct=on, which was not specified/' diff --git a/tests/qemu-iotests/087.out b/tests/qemu-iotests/087.out index b1318c6ed6..2d92ea847b 100644 --- a/tests/qemu-iotests/087.out +++ b/tests/qemu-iotests/087.out @@ -8,7 +8,7 @@ QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "'node-name' must be specified for the root node"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Duplicate ID === @@ -19,7 +19,7 @@ QMP_VERSION {"error": {"class": "GenericError", "desc": "node-name=disk is conflicting with a device id"}} {"error": {"class": "GenericError", "desc": "Duplicate node name"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === aio=native without O_DIRECT === @@ -29,7 +29,7 @@ QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "aio=native was specified, but it requires cache.direct=on, which was not specified."}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Encrypted image QCow === @@ -41,7 +41,7 @@ QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "Use of AES-CBC encrypted IMGFMT images is no longer supported in system emulators"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Encrypted image LUKS === @@ -53,7 +53,7 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Missing driver === @@ -64,6 +64,6 @@ QMP_VERSION {"return": {}} {"error": {"class": "GenericError", "desc": "Parameter 'driver' is missing"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} *** done diff --git a/tests/qemu-iotests/088 b/tests/qemu-iotests/088 index b8076f216b..b44edd0cf9 100755 --- a/tests/qemu-iotests/088 +++ b/tests/qemu-iotests/088 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # vpc (VHD) format input validation tests # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/089 b/tests/qemu-iotests/089 index aa1ba4a98e..6609954908 100755 --- a/tests/qemu-iotests/089 +++ b/tests/qemu-iotests/089 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for support of JSON filenames # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/090 b/tests/qemu-iotests/090 index 7380503d57..193bae7d77 100755 --- a/tests/qemu-iotests/090 +++ b/tests/qemu-iotests/090 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test for discarding compressed clusters on qcow2 images # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/091 b/tests/qemu-iotests/091 index 10ac4a8d73..d62ef18a02 100755 --- a/tests/qemu-iotests/091 +++ b/tests/qemu-iotests/091 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Live migration test # @@ -26,7 +26,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! MIG_FIFO="${TEST_DIR}/migrate" diff --git a/tests/qemu-iotests/092 b/tests/qemu-iotests/092 index 5bbdd071d8..e2e0726de1 100755 --- a/tests/qemu-iotests/092 +++ b/tests/qemu-iotests/092 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow1 format input validation tests # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index 68e344f8c1..d88fbc182e 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -69,18 +69,18 @@ class ThrottleTestCase(iotests.QMPTestCase): # 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, - params['iops'] / 2, + rd_nr = max(params['bps'] // rq_size // 2, + params['bps_rd'] // rq_size, + params['iops'] // 2, params['iops_rd']) rd_nr *= seconds * 2 - rd_nr /= ndrives - wr_nr = max(params['bps'] / rq_size / 2, - params['bps_wr'] / rq_size, - params['iops'] / 2, + rd_nr //= ndrives + wr_nr = max(params['bps'] // rq_size // 2, + params['bps_wr'] // rq_size, + params['iops'] // 2, params['iops_wr']) wr_nr *= seconds * 2 - wr_nr /= ndrives + wr_nr //= ndrives # Send I/O requests to all drives for i in range(rd_nr): @@ -196,7 +196,7 @@ class ThrottleTestCase(iotests.QMPTestCase): 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 + 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 @@ -208,6 +208,61 @@ class ThrottleTestCase(iotests.QMPTestCase): limits[tk] = rate self.do_test_throttle(ndrives, 5, limits) + # Test that removing a drive from a throttle group should not + # affect the remaining members of the group. + # https://bugzilla.redhat.com/show_bug.cgi?id=1535914 + def test_remove_group_member(self): + # Create a throttle group with two drives + # and set a 4 KB/s read limit. + params = {"bps": 0, + "bps_rd": 4096, + "bps_wr": 0, + "iops": 0, + "iops_rd": 0, + "iops_wr": 0 } + self.configure_throttle(2, params) + + # Read 4KB from drive0. This is performed immediately. + self.vm.hmp_qemu_io("drive0", "aio_read 0 4096") + + # Read 2KB. The I/O limit has been exceeded so this + # request is throttled and a timer is set to wake it up. + self.vm.hmp_qemu_io("drive0", "aio_read 0 2048") + + # Read 2KB again. We're still over the I/O limit so this is + # request is also throttled, but no new timer is set since + # there's already one. + self.vm.hmp_qemu_io("drive0", "aio_read 0 2048") + + # Read from drive1. This request is also throttled, and no + # timer is set in drive1 because there's already one in + # drive0. + self.vm.hmp_qemu_io("drive1", "aio_read 0 4096") + + # At this point only the first 4KB have been read from drive0. + # The other requests are throttled. + self.assertEqual(self.blockstats('drive0')[0], 4096) + self.assertEqual(self.blockstats('drive1')[0], 0) + + # Remove drive0 from the throttle group and disable its I/O limits. + # drive1 remains in the group with a throttled request. + params['bps_rd'] = 0 + params['device'] = 'drive0' + result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) + self.assert_qmp(result, 'return', {}) + + # Removing the I/O limits from drive0 drains its two pending requests. + # The read request in drive1 is still throttled. + self.assertEqual(self.blockstats('drive0')[0], 8192) + self.assertEqual(self.blockstats('drive1')[0], 0) + + # Advance the clock 5 seconds. This completes the request in drive1 + self.vm.qtest("clock_step %d" % (5 * nsec_per_sec)) + + # Now all requests have been processed. + self.assertEqual(self.blockstats('drive0')[0], 8192) + self.assertEqual(self.blockstats('drive1')[0], 4096) + class ThrottleTestCoroutine(ThrottleTestCase): test_img = "null-co://" diff --git a/tests/qemu-iotests/093.out b/tests/qemu-iotests/093.out index 594c16f49f..36376bed87 100644 --- a/tests/qemu-iotests/093.out +++ b/tests/qemu-iotests/093.out @@ -1,5 +1,5 @@ -........ +.......... ---------------------------------------------------------------------- -Ran 8 tests +Ran 10 tests OK diff --git a/tests/qemu-iotests/094 b/tests/qemu-iotests/094 index 9aa01e3627..0bcca77261 100755 --- a/tests/qemu-iotests/094 +++ b/tests/qemu-iotests/094 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for drive-mirror to NBD # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/094.out b/tests/qemu-iotests/094.out index 665b630b08..f3b9ecf22b 100644 --- a/tests/qemu-iotests/094.out +++ b/tests/qemu-iotests/094.out @@ -14,5 +14,5 @@ Formatting 'TEST_DIR/source.IMGFMT', fmt=IMGFMT size=67108864 {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "src"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "src"}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} *** done diff --git a/tests/qemu-iotests/095 b/tests/qemu-iotests/095 index 72ecc22e1b..18505b7181 100755 --- a/tests/qemu-iotests/095 +++ b/tests/qemu-iotests/095 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test for commit of larger active layer # @@ -27,7 +27,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/097 b/tests/qemu-iotests/097 index e22670c8d0..690f3d3ce1 100755 --- a/tests/qemu-iotests/097 +++ b/tests/qemu-iotests/097 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Commit changes into backing chains and empty the top image if the # backing image is not explicitly specified @@ -25,7 +25,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/098 b/tests/qemu-iotests/098 index b002e969b3..461144c831 100755 --- a/tests/qemu-iotests/098 +++ b/tests/qemu-iotests/098 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qcow2's bdrv_make_empty for images without internal snapshots # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/099 b/tests/qemu-iotests/099 index caaf58eee5..ae02f27afe 100755 --- a/tests/qemu-iotests/099 +++ b/tests/qemu-iotests/099 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test valid filenames for blkdebug and blkverify representatively for # other protocols (such as NBD) when queried @@ -25,7 +25,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() @@ -46,12 +45,12 @@ _supported_os Linux _unsupported_imgopts "subformat=monolithicFlat" "subformat=twoGbMaxExtentFlat" \ "subformat=twoGbMaxExtentSparse" -function do_run_qemu() +do_run_qemu() { $QEMU -nographic -qmp stdio -serial none "$@" } -function run_qemu() +run_qemu() { # Get the "file": "foo" entry ($foo may only contain escaped double quotes, # which is how we can extract it) @@ -60,7 +59,7 @@ function run_qemu() | sed -e 's/^.*"file": "\(\(\\"\|[^"]\)*\)".*$/\1/' -e 's/\\"/"/g' } -function test_qemu() +test_qemu() { run_qemu -drive "if=none,id=drv0,$1" <&1 | _filter_testdir | _filter_qemu | _filter_hmp } @@ -89,7 +88,7 @@ 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() +check_cache_all() { # cache.direct is supposed to be inherited by both bs->file and # bs->backing @@ -232,7 +231,7 @@ drv_bk="if=none,file=json:{'driver':'$IMGFMT',,'file':'backing-file',,'node-name 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_all_separate() { # Check cache.direct diff --git a/tests/qemu-iotests/143 b/tests/qemu-iotests/143 index 5ff1944507..c223867cb3 100755 --- a/tests/qemu-iotests/143 +++ b/tests/qemu-iotests/143 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for connecting to a non-existing NBD export name # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/143.out b/tests/qemu-iotests/143.out index 1c7fb45543..3e55fcd0ac 100644 --- a/tests/qemu-iotests/143.out +++ b/tests/qemu-iotests/143.out @@ -4,5 +4,5 @@ QA output created by 143 can't open device nbd+unix:///no_such_export?socket=TEST_DIR/nbd: Requested export not available server reported: export 'no_such_export' not present {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} *** done diff --git a/tests/qemu-iotests/144 b/tests/qemu-iotests/144 index 4b915718cd..15157f33d7 100755 --- a/tests/qemu-iotests/144 +++ b/tests/qemu-iotests/144 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Check live snapshot, followed by active commit, and another snapshot. # # This test is to catch the error case of BZ #1300209: @@ -26,7 +26,6 @@ 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 diff --git a/tests/qemu-iotests/145 b/tests/qemu-iotests/145 index c371b3c46a..28878dc8a1 100755 --- a/tests/qemu-iotests/145 +++ b/tests/qemu-iotests/145 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test the combination of -incoming and snapshot=on # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/146 b/tests/qemu-iotests/146 index 043711be68..2e43abddfc 100755 --- a/tests/qemu-iotests/146 +++ b/tests/qemu-iotests/146 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test VHD image format creator detection and override # @@ -24,7 +24,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/147 b/tests/qemu-iotests/147 index d2081df84b..82513279b0 100755 --- a/tests/qemu-iotests/147 +++ b/tests/qemu-iotests/147 @@ -19,13 +19,17 @@ # import os +import random import socket import stat import time import iotests -from iotests import cachemode, imgfmt, qemu_img, qemu_nbd +from iotests import cachemode, imgfmt, qemu_img, qemu_nbd, qemu_nbd_pipe -NBD_PORT = 10811 +NBD_PORT_START = 32768 +NBD_PORT_END = NBD_PORT_START + 1024 +NBD_IPV6_PORT_START = NBD_PORT_END +NBD_IPV6_PORT_END = NBD_IPV6_PORT_START + 1024 test_img = os.path.join(iotests.test_dir, 'test.img') unix_socket = os.path.join(iotests.test_dir, 'nbd.socket') @@ -88,17 +92,29 @@ class QemuNBD(NBDBlockdevAddBase): except OSError: pass + def _try_server_up(self, *args): + status, msg = qemu_nbd_pipe('-f', imgfmt, test_img, *args) + if status == 0: + return True + if 'Address already in use' in msg: + return False + self.fail(msg) + def _server_up(self, *args): - self.assertEqual(qemu_nbd('-f', imgfmt, test_img, *args), 0) + self.assertTrue(self._try_server_up(*args)) def test_inet(self): - self._server_up('-p', str(NBD_PORT)) + while True: + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) + if self._try_server_up('-b', 'localhost', '-p', str(nbd_port)): + break + address = { 'type': 'inet', 'data': { 'host': 'localhost', - 'port': str(NBD_PORT) + 'port': str(nbd_port) } } - self.client_test('nbd://localhost:%i' % NBD_PORT, + self.client_test('nbd://localhost:%i' % nbd_port, flatten_sock_addr(address)) def test_unix(self): @@ -130,8 +146,13 @@ class BuiltinNBD(NBDBlockdevAddBase): except OSError: pass - def _server_up(self, address, export_name=None, export_name2=None): + # Returns False on EADDRINUSE; fails an assertion on other errors. + # Returns True on success. + def _try_server_up(self, address, export_name=None, export_name2=None): result = self.server.qmp('nbd-server-start', addr=address) + if 'error' in result and \ + 'Address already in use' in result['error']['desc']: + return False self.assert_qmp(result, 'return', {}) if export_name is None: @@ -146,20 +167,28 @@ class BuiltinNBD(NBDBlockdevAddBase): name=export_name2) self.assert_qmp(result, 'return', {}) + return True + + def _server_up(self, address, export_name=None, export_name2=None): + self.assertTrue(self._try_server_up(address, export_name, export_name2)) def _server_down(self): result = self.server.qmp('nbd-server-stop') self.assert_qmp(result, 'return', {}) def do_test_inet(self, export_name=None): - address = { 'type': 'inet', - 'data': { - 'host': 'localhost', - 'port': str(NBD_PORT) - } } - self._server_up(address, export_name) + while True: + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(nbd_port) + } } + if self._try_server_up(address, export_name): + break + export_name = export_name or 'nbd-export' - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, export_name), + self.client_test('nbd://localhost:%i/%s' % (nbd_port, export_name), flatten_sock_addr(address), export_name) self._server_down() @@ -173,15 +202,19 @@ class BuiltinNBD(NBDBlockdevAddBase): self.do_test_inet('shadow') def test_inet_two_exports(self): - address = { 'type': 'inet', - 'data': { - 'host': 'localhost', - 'port': str(NBD_PORT) - } } - self._server_up(address, 'exp1', 'exp2') - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp1'), + while True: + nbd_port = random.randrange(NBD_PORT_START, NBD_PORT_END) + address = { 'type': 'inet', + 'data': { + 'host': 'localhost', + 'port': str(nbd_port) + } } + if self._try_server_up(address, 'exp1', 'exp2'): + break + + self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp1'), flatten_sock_addr(address), 'exp1', 'node1', False) - self.client_test('nbd://localhost:%i/%s' % (NBD_PORT, 'exp2'), + self.client_test('nbd://localhost:%i/%s' % (nbd_port, 'exp2'), flatten_sock_addr(address), 'exp2', 'node2', False) result = self.vm.qmp('blockdev-del', node_name='node1') self.assert_qmp(result, 'return', {}) @@ -197,20 +230,25 @@ class BuiltinNBD(NBDBlockdevAddBase): except socket.gaierror: # IPv6 not available, skip return - address = { 'type': 'inet', - 'data': { - 'host': '::1', - 'port': str(NBD_PORT), - 'ipv4': False, - 'ipv6': True - } } + + while True: + nbd_port = random.randrange(NBD_IPV6_PORT_START, NBD_IPV6_PORT_END) + address = { 'type': 'inet', + 'data': { + 'host': '::1', + 'port': str(nbd_port), + 'ipv4': False, + 'ipv6': True + } } + if self._try_server_up(address): + break + filename = { 'driver': 'raw', 'file': { 'driver': 'nbd', 'export': 'nbd-export', 'server': flatten_sock_addr(address) } } - self._server_up(address) self.client_test(filename, flatten_sock_addr(address), 'nbd-export') self._server_down() @@ -229,7 +267,7 @@ class BuiltinNBD(NBDBlockdevAddBase): sockfd = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sockfd.connect(unix_socket) - result = self.vm.send_fd_scm(str(sockfd.fileno())) + result = self.vm.send_fd_scm(fd=sockfd.fileno()) self.assertEqual(result, 0, 'Failed to send socket FD') result = self.vm.qmp('getfd', fdname='nbd-fifo') diff --git a/tests/qemu-iotests/149 b/tests/qemu-iotests/149 index 9e0cad76f9..4f363f295f 100755 --- a/tests/qemu-iotests/149 +++ b/tests/qemu-iotests/149 @@ -79,7 +79,7 @@ class LUKSConfig(object): def first_password_base64(self): (pw, slot) = self.first_password() - return base64.b64encode(pw) + return base64.b64encode(pw.encode('ascii')).decode('ascii') def active_slots(self): slots = [] @@ -98,7 +98,8 @@ def verify_passwordless_sudo(): proc = subprocess.Popen(args, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + universal_newlines=True) msg = proc.communicate()[0] @@ -116,7 +117,8 @@ def cryptsetup(args, password=None): proc = subprocess.Popen(fullargs, stdin=subprocess.PIPE, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + stderr=subprocess.STDOUT, + universal_newlines=True) msg = proc.communicate(password)[0] @@ -312,13 +314,13 @@ def test_once(config, qemu_img=False): image_size = 4 * oneTB if qemu_img: iotests.log("# Create image") - qemu_img_create(config, image_size / oneMB) + qemu_img_create(config, image_size // oneMB) else: iotests.log("# Create image") - create_image(config, image_size / oneMB) + create_image(config, image_size // oneMB) lowOffsetMB = 100 - highOffsetMB = 3 * oneTB / oneMB + highOffsetMB = 3 * oneTB // oneMB try: if not qemu_img: diff --git a/tests/qemu-iotests/150 b/tests/qemu-iotests/150 index ee8f6375f6..3b1f32197a 100755 --- a/tests/qemu-iotests/150 +++ b/tests/qemu-iotests/150 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that qemu-img convert -S 0 fully allocates the target image # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/151 b/tests/qemu-iotests/151 index fe53b9f446..1bb74d67c4 100755 --- a/tests/qemu-iotests/151 +++ b/tests/qemu-iotests/151 @@ -67,9 +67,9 @@ class TestActiveMirror(iotests.QMPTestCase): 'write -P 1 0 %i' % self.image_len); # Start some background requests - for offset in range(1 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024): + for offset in range(1 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024): self.vm.hmp_qemu_io('source', 'aio_write -P 2 %i 1M' % offset) - for offset in range(2 * self.image_len / 8, 3 * self.image_len / 8, 1024 * 1024): + for offset in range(2 * self.image_len // 8, 3 * self.image_len // 8, 1024 * 1024): self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) # Start the block job @@ -83,9 +83,9 @@ class TestActiveMirror(iotests.QMPTestCase): self.assert_qmp(result, 'return', {}) # Start some more requests - for offset in range(3 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024): + for offset in range(3 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) - for offset in range(4 * self.image_len / 8, 5 * self.image_len / 8, 1024 * 1024): + for offset in range(4 * self.image_len // 8, 5 * self.image_len // 8, 1024 * 1024): self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) # Wait for the READY event @@ -95,9 +95,9 @@ class TestActiveMirror(iotests.QMPTestCase): # the source) should be settled using the active mechanism. # The mirror code itself asserts that the source BDS's dirty # bitmap will stay clean between READY and COMPLETED. - for offset in range(5 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024): + for offset in range(5 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): self.vm.hmp_qemu_io('source', 'aio_write -P 3 %i 1M' % offset) - for offset in range(6 * self.image_len / 8, 7 * self.image_len / 8, 1024 * 1024): + for offset in range(6 * self.image_len // 8, 7 * self.image_len // 8, 1024 * 1024): self.vm.hmp_qemu_io('source', 'aio_write -z %i 1M' % offset) if sync_source_and_target: diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index 0daeb1b005..08ad8a6730 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test image locking # @@ -24,7 +24,6 @@ owner=famz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" tmp=/tmp/$$ status=1 # failure is the default! @@ -71,7 +70,7 @@ _run_cmd() (echo "$@"; "$@" 2>&1 1>/dev/null) | _filter_testdir } -function _do_run_qemu() +_do_run_qemu() { ( if ! test -t 0; then @@ -83,7 +82,7 @@ function _do_run_qemu() ) | $QEMU -nographic -monitor stdio -serial none "$@" 1>/dev/null } -function _run_qemu_with_images() +_run_qemu_with_images() { _do_run_qemu \ $(for i in $@; do echo "-drive if=none,file=$i"; done) 2>&1 \ @@ -156,7 +155,7 @@ for opts1 in "" "read-only=on" "read-only=on,force-share=on"; do _img_info -U | grep 'file format' fi done - _send_qemu_cmd $h "{ 'execute': 'quit', }" "" + _send_qemu_cmd $h "{ 'execute': 'quit' }" '' echo echo "Round done" _cleanup_qemu @@ -220,7 +219,7 @@ echo "Adding drive" _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_add 0 if=none,id=d0,file=${TEST_IMG}' } }" \ - "" + 'return' _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' @@ -231,7 +230,7 @@ echo "== Closing an image should unlock it ==" _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_del d0' } }" \ - "" + 'return' _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' @@ -240,7 +239,7 @@ for d in d0 d1; do _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_add 0 if=none,id=$d,file=${TEST_IMG},readonly=on' } }" \ - "" + 'return' done _run_cmd $QEMU_IMG info "${TEST_IMG}" @@ -248,7 +247,7 @@ _run_cmd $QEMU_IMG info "${TEST_IMG}" _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_del d0' } }" \ - "" + 'return' _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' @@ -256,7 +255,7 @@ echo "Closing the other" _send_qemu_cmd $QEMU_HANDLE \ "{ 'execute': 'human-monitor-command', 'arguments': { 'command-line': 'drive_del d1' } }" \ - "" + 'return' _run_cmd $QEMU_IO "${TEST_IMG}" -c 'write 0 512' diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index 93eaf10486..9747ce3c41 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -12,11 +12,11 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t == Launching another QEMU, opts: '' == QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Launching another QEMU, opts: 'read-only=on' == QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,read-only=on: Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Launching another QEMU, opts: 'read-only=on,force-share=on' == @@ -24,77 +24,77 @@ Is another process using the image? _qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2 can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? no file open, try 'help open' _qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512 can't open device TEST_DIR/t.qcow2: Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? no file open, try 'help open' _qemu_img_wrapper info TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper check TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper compare TEST_DIR/t.qcow2 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper map TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper commit TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper resize TEST_DIR/t.qcow2 32M qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper convert TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.convert qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper dd if=TEST_DIR/t.qcow2 of=TEST_DIR/t.qcow2.convert bs=512 count=1 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? file format: IMGFMT == Running utility commands -U == @@ -132,7 +132,7 @@ Try 'qemu-img --help' for more information _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2 @@ -157,7 +157,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=33554432 backing_file=TEST_DIR/t == Launching another QEMU, opts: '' == QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Launching another QEMU, opts: 'read-only=on' == @@ -167,13 +167,13 @@ Is another process using the image? _qemu_io_wrapper -c read 0 512 TEST_DIR/t.qcow2 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_io_wrapper -r -c read 0 512 TEST_DIR/t.qcow2 _qemu_io_wrapper -c open TEST_DIR/t.qcow2 -c read 0 512 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? no file open, try 'help open' _qemu_io_wrapper -c open -r TEST_DIR/t.qcow2 -c read 0 512 @@ -188,19 +188,19 @@ _qemu_img_wrapper map TEST_DIR/t.qcow2 _qemu_img_wrapper amend -o TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper commit TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper resize TEST_DIR/t.qcow2 32M qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper rebase TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper snapshot -l TEST_DIR/t.qcow2 @@ -212,11 +212,11 @@ _qemu_img_wrapper bench -c 1 TEST_DIR/t.qcow2 _qemu_img_wrapper bench -w -c 1 TEST_DIR/t.qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper create -f qcow2 TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base qemu-img: TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? file format: IMGFMT == Running utility commands -U == @@ -254,7 +254,7 @@ Try 'qemu-img --help' for more information _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? _qemu_img_wrapper snapshot -l -U TEST_DIR/t.qcow2 @@ -372,17 +372,17 @@ Round done == Two devices with the same image (read-only=off - read-only=off) == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Two devices with the same image (read-only=off - read-only=on) == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=on: Failed to get shared "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Two devices with the same image (read-only=off - read-only=on,force-share=on) == == Two devices with the same image (read-only=on - read-only=off) == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2,read-only=off: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Two devices with the same image (read-only=on - read-only=on) == @@ -403,13 +403,13 @@ Formatting 'TEST_DIR/t.IMGFMT.c', fmt=IMGFMT size=33554432 backing_file=TEST_DIR == Backing image also as an active device == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Backing image also as an active device (ro) == == Symbolic link == QEMU_PROG: -drive if=none,file=TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? == Active commit to intermediate layer should work when base in use == {"return": {}} @@ -417,24 +417,30 @@ Is another process using the image? _qemu_img_wrapper commit -b TEST_DIR/t.qcow2.b TEST_DIR/t.qcow2.c {"return": {}} Adding drive +{"return": "OKrn"} _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? Creating overlay with qemu-img when the guest is running should be allowed _qemu_img_wrapper create -f qcow2 -b TEST_DIR/t.qcow2 TEST_DIR/t.qcow2.overlay == Closing an image should unlock it == +{"return": ""} _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 Adding two and closing one +{"return": "OKrn"} +{"return": "OKrn"} _qemu_img_wrapper info TEST_DIR/t.qcow2 +{"return": ""} _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 can't open device TEST_DIR/t.qcow2: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? Closing the other +{"return": ""} _qemu_io_wrapper TEST_DIR/t.qcow2 -c write 0 512 diff --git a/tests/qemu-iotests/154 b/tests/qemu-iotests/154 index fde03b0dc8..d68f66b9e0 100755 --- a/tests/qemu-iotests/154 +++ b/tests/qemu-iotests/154 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qcow2 specific bdrv_pwrite_zeroes tests with backing files (complements 034) # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/156 b/tests/qemu-iotests/156 index 0a9a09802e..8d134029c6 100755 --- a/tests/qemu-iotests/156 +++ b/tests/qemu-iotests/156 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Tests oVirt-like storage migration: # - Create snapshot @@ -32,7 +32,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/156.out b/tests/qemu-iotests/156.out index 34c057b626..4c391a7603 100644 --- a/tests/qemu-iotests/156.out +++ b/tests/qemu-iotests/156.out @@ -41,7 +41,7 @@ read 65536/65536 bytes at offset 196608 {"return": ""} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} read 65536/65536 bytes at offset 0 64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) diff --git a/tests/qemu-iotests/157 b/tests/qemu-iotests/157 index 2bf02be465..69b25cab30 100755 --- a/tests/qemu-iotests/157 +++ b/tests/qemu-iotests/157 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test command line configuration of block devices with qdev # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() @@ -41,7 +40,7 @@ _supported_fmt generic _supported_proto file _supported_os Linux -function do_run_qemu() +do_run_qemu() { ( if ! test -t 0; then @@ -54,7 +53,7 @@ function do_run_qemu() echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt \ | _filter_qemu | _filter_generated_node_ids diff --git a/tests/qemu-iotests/158 b/tests/qemu-iotests/158 index 24ac600a4a..8c0928a7f9 100755 --- a/tests/qemu-iotests/158 +++ b/tests/qemu-iotests/158 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test encrypted read/write using backing files # @@ -24,7 +24,6 @@ owner=berrange@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/159 b/tests/qemu-iotests/159 index 9b0e1ecc90..29066eebde 100755 --- a/tests/qemu-iotests/159 +++ b/tests/qemu-iotests/159 @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash # # qemu-img dd test with different block sizes # @@ -23,7 +23,6 @@ owner=fullmanet@gmail.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 _cleanup() diff --git a/tests/qemu-iotests/160 b/tests/qemu-iotests/160 index 5c910e5bfc..df89d3864b 100755 --- a/tests/qemu-iotests/160 +++ b/tests/qemu-iotests/160 @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash # # qemu-img dd test for the skip option # @@ -23,7 +23,6 @@ owner=fullmanet@gmail.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 _cleanup() diff --git a/tests/qemu-iotests/161 b/tests/qemu-iotests/161 new file mode 100755 index 0000000000..456a4bd8c4 --- /dev/null +++ b/tests/qemu-iotests/161 @@ -0,0 +1,137 @@ +#!/usr/bin/env bash +# +# Test reopening a backing image after block-stream and block-commit +# +# Copyright (C) 2018 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 . +# + +# creator +owner=berto@igalia.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.base" + rm -f "$TEST_IMG.int" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.qemu + +# Any format implementing BlockDriver.bdrv_change_backing_file +_supported_fmt qcow2 qed +_supported_proto file +_supported_os Linux + +IMG_SIZE=1M + +# Create the images +TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt +TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt +_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt + +# First test: reopen $TEST.IMG changing the detect-zeroes option on +# its backing file ($TEST_IMG.int). +echo +echo "*** Change an option on the backing file" +echo +_launch_qemu -drive if=none,file="${TEST_IMG}" +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'human-monitor-command', + 'arguments': { 'command-line': + 'qemu-io none0 \"reopen -o backing.detect-zeroes=on\"' } }" \ + "return" + +_cleanup_qemu + +# Second test: stream $TEST_IMG.base into $TEST_IMG.int and then +# reopen $TEST.IMG changing the detect-zeroes option on its new +# backing file ($TEST_IMG.base). +echo +echo "*** Stream and then change an option on the backing file" +echo +_launch_qemu -drive if=none,file="${TEST_IMG}" +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'block-stream', \ + 'arguments': { 'device': 'none0', + 'base': '${TEST_IMG}.base' } }" \ + 'return' + +# Wait for block-stream to finish +sleep 0.5 + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'human-monitor-command', + 'arguments': { 'command-line': + 'qemu-io none0 \"reopen -o backing.detect-zeroes=on\"' } }" \ + "return" + +_cleanup_qemu + +# Third test: commit $TEST_IMG.int into $TEST_IMG.base and then reopen +# $TEST.IMG changing the detect-zeroes option on its new backing file +# ($TEST_IMG.base). +echo +echo "*** Commit and then change an option on the backing file" +echo +# Create the images again +TEST_IMG="$TEST_IMG.base" _make_test_img $IMG_SIZE | _filter_imgfmt +TEST_IMG="$TEST_IMG.int" _make_test_img -b "$TEST_IMG.base" | _filter_imgfmt +_make_test_img -b "$TEST_IMG.int" | _filter_imgfmt + +_launch_qemu -drive if=none,file="${TEST_IMG}" +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'qmp_capabilities' }" \ + 'return' + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'block-commit', \ + 'arguments': { 'device': 'none0', + 'top': '${TEST_IMG}.int' } }" \ + 'return' + +# Wait for block-commit to finish +sleep 0.5 + +_send_qemu_cmd $QEMU_HANDLE \ + "{ 'execute': 'human-monitor-command', + 'arguments': { 'command-line': + 'qemu-io none0 \"reopen -o backing.detect-zeroes=on\"' } }" \ + "return" + +_cleanup_qemu + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/161.out b/tests/qemu-iotests/161.out new file mode 100644 index 0000000000..39951993ee --- /dev/null +++ b/tests/qemu-iotests/161.out @@ -0,0 +1,39 @@ +QA output created by 161 +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int + +*** Change an option on the backing file + +{"return": {}} +{"return": ""} + +*** Stream and then change an option on the backing file + +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "none0"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "none0", "len": 1048576, "offset": 1048576, "speed": 0, "type": "stream"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "none0"}} +{"return": ""} + +*** Commit and then change an option on the backing file + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.int', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.int +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "none0"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "none0", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "none0"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "none0"}} +{"return": ""} +*** done diff --git a/tests/qemu-iotests/162 b/tests/qemu-iotests/162 index 477a806360..2e9947fd9a 100755 --- a/tests/qemu-iotests/162 +++ b/tests/qemu-iotests/162 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for specifying runtime options of the wrong type to some # block drivers @@ -25,7 +25,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/163 b/tests/qemu-iotests/163 index 403842354e..158ba5d092 100755 --- a/tests/qemu-iotests/163 +++ b/tests/qemu-iotests/163 @@ -18,9 +18,12 @@ # along with this program. If not, see . # -import os, random, iotests, struct, qcow2 +import os, random, iotests, struct, qcow2, sys from iotests import qemu_img, qemu_io, image_size +if sys.version_info.major == 2: + range = xrange + test_img = os.path.join(iotests.test_dir, 'test.img') check_img = os.path.join(iotests.test_dir, 'check.img') @@ -38,10 +41,10 @@ class ShrinkBaseClass(iotests.QMPTestCase): entry_bits = 3 entry_size = 1 << entry_bits l1_mask = 0x00fffffffffffe00 - div_roundup = lambda n, d: (n + d - 1) / d + div_roundup = lambda n, d: (n + d - 1) // d def split_by_n(data, n): - for x in xrange(0, len(data), n): + for x in range(0, len(data), n): yield struct.unpack('>Q', data[x:x + n])[0] & l1_mask def check_l1_table(h, l1_data): @@ -135,8 +138,8 @@ class ShrinkBaseClass(iotests.QMPTestCase): self.image_verify() def test_random_write(self): - offs_list = range(0, size_to_int(self.image_len), - size_to_int(self.chunk_size)) + offs_list = list(range(0, size_to_int(self.image_len), + size_to_int(self.chunk_size))) random.shuffle(offs_list) for offs in offs_list: qemu_io('-c', 'write -P 0xff %d %s' % (offs, self.chunk_size), diff --git a/tests/qemu-iotests/169 b/tests/qemu-iotests/169 index f243db9955..527aebd0cb 100755 --- a/tests/qemu-iotests/169 +++ b/tests/qemu-iotests/169 @@ -23,7 +23,7 @@ import iotests import time import itertools import operator -import new +import re from iotests import qemu_img @@ -58,7 +58,6 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): 'granularity': granularity} if persistent: params['persistent'] = True - params['autoload'] = True result = vm.qmp('block-dirty-bitmap-add', **params) self.assert_qmp(result, 'return', {}); @@ -77,6 +76,58 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): self.assert_qmp(result, 'error/desc', "Dirty bitmap 'bitmap0' not found"); + def do_test_migration_resume_source(self, persistent, migrate_bitmaps): + granularity = 512 + + # regions = ((start, count), ...) + regions = ((0, 0x10000), + (0xf0000, 0x10000), + (0xa0201, 0x1000)) + + mig_caps = [{'capability': 'events', 'state': True}] + if migrate_bitmaps: + mig_caps.append({'capability': 'dirty-bitmaps', 'state': True}) + + result = self.vm_a.qmp('migrate-set-capabilities', + capabilities=mig_caps) + self.assert_qmp(result, 'return', {}) + + self.add_bitmap(self.vm_a, granularity, persistent) + for r in regions: + self.vm_a.hmp_qemu_io('drive0', 'write %d %d' % r) + sha256 = self.get_bitmap_hash(self.vm_a) + + result = self.vm_a.qmp('migrate', uri=mig_cmd) + while True: + event = self.vm_a.event_wait('MIGRATION') + if event['data']['status'] == 'completed': + break + + # test that bitmap is still here + removed = (not migrate_bitmaps) and persistent + self.check_bitmap(self.vm_a, False if removed else sha256) + + self.vm_a.qmp('cont') + + # test that bitmap is still here after invalidation + self.check_bitmap(self.vm_a, sha256) + + # shutdown and check that invalidation didn't fail + self.vm_a.shutdown() + + # catch 'Could not reopen qcow2 layer: Bitmap already exists' + # possible error + log = self.vm_a.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'^(wrote .* bytes at offset .*\n.*KiB.*ops.*sec.*\n){3}', + '', log) + log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + self.assertEqual(log, '') + + # test that bitmap is still persistent + self.vm_a.launch() + self.check_bitmap(self.vm_a, sha256 if persistent else False) + def do_test_migration(self, persistent, migrate_bitmaps, online, shared_storage): granularity = 512 @@ -134,6 +185,14 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): if should_migrate: self.vm_b.shutdown() + + # catch 'Could not reopen qcow2 layer: Bitmap already exists' + # possible error + log = self.vm_b.get_log() + log = re.sub(r'^\[I \d+\.\d+\] OPENED\n', '', log) + log = re.sub(r'\[I \+\d+\.\d+\] CLOSED\n?$', '', log) + self.assertEqual(log, '') + # recreate vm_b, as we don't want -incoming option (this will lead # to "cat" process left alive after test finish) self.vm_b = iotests.VM(path_suffix='b') @@ -144,7 +203,7 @@ class TestDirtyBitmapMigration(iotests.QMPTestCase): def inject_test_case(klass, name, method, *args, **kwargs): mc = operator.methodcaller(method, *args, **kwargs) - setattr(klass, 'test_' + name, new.instancemethod(mc, None, klass)) + setattr(klass, 'test_' + method + name, lambda self: mc(self)) for cmb in list(itertools.product((True, False), repeat=4)): name = ('_' if cmb[0] else '_not_') + 'persistent_' @@ -155,6 +214,12 @@ for cmb in list(itertools.product((True, False), repeat=4)): inject_test_case(TestDirtyBitmapMigration, name, 'do_test_migration', *list(cmb)) +for cmb in list(itertools.product((True, False), repeat=2)): + name = ('_' if cmb[0] else '_not_') + 'persistent_' + name += ('_' if cmb[1] else '_not_') + 'migbitmap' + + inject_test_case(TestDirtyBitmapMigration, name, + 'do_test_migration_resume_source', *list(cmb)) if __name__ == '__main__': iotests.main(supported_fmts=['qcow2']) diff --git a/tests/qemu-iotests/169.out b/tests/qemu-iotests/169.out index b6f257674e..3a89159833 100644 --- a/tests/qemu-iotests/169.out +++ b/tests/qemu-iotests/169.out @@ -1,5 +1,5 @@ -................ +.................... ---------------------------------------------------------------------- -Ran 16 tests +Ran 20 tests OK diff --git a/tests/qemu-iotests/170 b/tests/qemu-iotests/170 index b79359fc4e..7deb7563c9 100755 --- a/tests/qemu-iotests/170 +++ b/tests/qemu-iotests/170 @@ -1,4 +1,4 @@ -#! /bin/bash +#!/usr/bin/env bash # # qemu-img dd test # @@ -23,7 +23,6 @@ owner=fullmanet@gmail.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 _cleanup() diff --git a/tests/qemu-iotests/171 b/tests/qemu-iotests/171 index bcfaaf1be2..341064a1c6 100755 --- a/tests/qemu-iotests/171 +++ b/tests/qemu-iotests/171 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test 'offset' and 'size' options of the raw driver. Make sure we can't # (or can) read and write outside of the image size. @@ -25,7 +25,6 @@ owner=tgolembi@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/172 b/tests/qemu-iotests/172 index 02c5f79bab..ba7dad9057 100755 --- a/tests/qemu-iotests/172 +++ b/tests/qemu-iotests/172 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test floppy configuration # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -47,7 +46,7 @@ if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then _notrun "Requires a PC machine" fi -function do_run_qemu() +do_run_qemu() { ( if ! test -t 0; then @@ -60,7 +59,7 @@ function do_run_qemu() echo } -function check_floppy_qtree() +check_floppy_qtree() { echo echo Testing: "$@" | _filter_testdir @@ -76,7 +75,7 @@ function check_floppy_qtree() _filter_win32 | _filter_qemu } -function check_cache_mode() +check_cache_mode() { echo "info block none0" | QEMU_OPTIONS="" do_run_qemu -drive if=none,file="$TEST_IMG" "$@" | diff --git a/tests/qemu-iotests/173 b/tests/qemu-iotests/173 index bdaa092979..47036a5564 100755 --- a/tests/qemu-iotests/173 +++ b/tests/qemu-iotests/173 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test QAPI commands looking up protocol based images with relative # filename backing strings @@ -24,7 +24,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/174 b/tests/qemu-iotests/174 index 552879db32..0a952a73fd 100755 --- a/tests/qemu-iotests/174 +++ b/tests/qemu-iotests/174 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test that qemu-io fail with non-zero exit code # @@ -24,7 +24,6 @@ owner=nirsof@gmail.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 index ca56e827cd..d0ffc495c2 100755 --- a/tests/qemu-iotests/175 +++ b/tests/qemu-iotests/175 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test creating raw image preallocation mode # @@ -24,7 +24,6 @@ owner=nirsof@gmail.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/176 b/tests/qemu-iotests/176 index 32baa116dd..50df4c00fa 100755 --- a/tests/qemu-iotests/176 +++ b/tests/qemu-iotests/176 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Commit changes into backing chains and empty the top image if the # backing image is not explicitly specified. @@ -29,7 +29,6 @@ owner=mreitz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() @@ -51,7 +50,7 @@ _supported_os Linux # Persistent dirty bitmaps require compat=1.1 _unsupported_imgopts 'compat=0.10' -function run_qemu() +run_qemu() { $QEMU -nographic -qmp stdio -serial none "$@" 2>&1 \ | _filter_testdir | _filter_qmp | _filter_qemu \ diff --git a/tests/qemu-iotests/176.out b/tests/qemu-iotests/176.out index f03a2e776c..cc33def96b 100644 --- a/tests/qemu-iotests/176.out +++ b/tests/qemu-iotests/176.out @@ -170,7 +170,7 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -207,7 +207,7 @@ QMP_VERSION {"return": {}} {"return": {"sha256": HASH}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Test pass bitmap.1 === @@ -219,7 +219,7 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -257,7 +257,7 @@ QMP_VERSION {"return": {}} {"return": {"sha256": HASH}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Test pass bitmap.2 === @@ -269,7 +269,7 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -307,7 +307,7 @@ QMP_VERSION {"return": {}} {"return": {"sha256": HASH}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Test pass bitmap.3 === @@ -319,7 +319,7 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} wrote 196608/196608 bytes at offset 2147287040 192 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 131072/131072 bytes at offset 2147352576 @@ -354,5 +354,5 @@ QMP_VERSION {"return": {}} {"return": {"sha256": HASH}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} *** done diff --git a/tests/qemu-iotests/177 b/tests/qemu-iotests/177 index 396986da89..752d29f8ad 100755 --- a/tests/qemu-iotests/177 +++ b/tests/qemu-iotests/177 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test corner cases with unusual block geometries # @@ -24,7 +24,6 @@ owner=eblake@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -86,7 +85,7 @@ $QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \ echo echo "== verify image content ==" -function verify_io() +verify_io() { if ($QEMU_IMG info -f "$IMGFMT" "$TEST_IMG" | grep "compat: 0.10" > /dev/null); then diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 index 6af52c653a..21231cadd3 100755 --- a/tests/qemu-iotests/178 +++ b/tests/qemu-iotests/178 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qemu-img measure sub-command tests # @@ -24,7 +24,6 @@ owner=stefanha@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -143,6 +142,14 @@ for ofmt in human json; do # The backing file doesn't need to exist :) $QEMU_IMG measure --output=$ofmt -o backing_file=x \ -f "$fmt" -O "$IMGFMT" "$TEST_IMG" + + echo + echo "== $fmt input image and LUKS encryption ==" + echo + $QEMU_IMG measure --output=$ofmt \ + --object secret,id=sec0,data=base \ + -o encrypt.format=luks,encrypt.key-secret=sec0,encrypt.iter-time=10 \ + -f "$fmt" -O "$IMGFMT" "$TEST_IMG" fi echo diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index d42d4a4597..55a8dc926f 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -68,6 +68,11 @@ converted image file size in bytes: 458752 required size: 1074135040 fully allocated size: 1074135040 +== qcow2 input image and LUKS encryption == + +required size: 2686976 +fully allocated size: 1076232192 + == qcow2 input image and preallocation (human) == required size: 1074135040 @@ -114,6 +119,11 @@ converted image file size in bytes: 524288 required size: 1074135040 fully allocated size: 1074135040 +== raw input image and LUKS encryption == + +required size: 2686976 +fully allocated size: 1076232192 + == raw input image and preallocation (human) == required size: 1074135040 @@ -205,6 +215,13 @@ converted image file size in bytes: 458752 "fully-allocated": 1074135040 } +== qcow2 input image and LUKS encryption == + +{ + "required": 2686976, + "fully-allocated": 1076232192 +} + == qcow2 input image and preallocation (json) == { @@ -263,6 +280,13 @@ converted image file size in bytes: 524288 "fully-allocated": 1074135040 } +== raw input image and LUKS encryption == + +{ + "required": 2686976, + "fully-allocated": 1076232192 +} + == raw input image and preallocation (json) == { diff --git a/tests/qemu-iotests/179 b/tests/qemu-iotests/179 index 115944a753..9372dc30ef 100755 --- a/tests/qemu-iotests/179 +++ b/tests/qemu-iotests/179 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for write zeroes with unmap # @@ -24,7 +24,6 @@ owner=eblake@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/181 b/tests/qemu-iotests/181 index e02979378d..e317e63422 100755 --- a/tests/qemu-iotests/181 +++ b/tests/qemu-iotests/181 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test postcopy live migration with shared storage # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! MIG_SOCKET="${TEST_DIR}/migrate" diff --git a/tests/qemu-iotests/182 b/tests/qemu-iotests/182 index 4b31592fb8..ff3d7e7ec1 100755 --- a/tests/qemu-iotests/182 +++ b/tests/qemu-iotests/182 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test image locking for POSIX locks # @@ -24,13 +24,13 @@ owner=famz@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" tmp=/tmp/$$ status=1 # failure is the default! _cleanup() { _cleanup_test_img + rm -f "$TEST_IMG.overlay" } trap "_cleanup; exit \$status" 0 1 2 3 15 @@ -71,6 +71,76 @@ echo 'quit' | $QEMU -nographic -monitor stdio \ _cleanup_qemu +echo +echo '=== Testing reopen ===' +echo + +# This tests that reopening does not unshare any permissions it should +# not unshare +# (There was a bug where reopening shared exactly the opposite of the +# permissions it was supposed to share) + +_launch_qemu + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +# Open the image without any format layer (we are not going to access +# it, so that is fine) +# This should keep all permissions shared. +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'node-name': 'node0', + 'driver': 'file', + 'filename': '$TEST_IMG', + 'locking': 'on' + } }" \ + 'return' \ + 'error' + +# This snapshot will perform a reopen to drop R/W to RO. +# It should still keep all permissions shared. +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-snapshot-sync', + 'arguments': { + 'node-name': 'node0', + 'snapshot-file': '$TEST_IMG.overlay', + 'snapshot-node-name': 'node1' + } }" \ + 'return' \ + 'error' + +# Now open the same file again +# This does not require any permissions (and does not unshare any), so +# this will not conflict with node0. +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'blockdev-add', + 'arguments': { + 'node-name': 'node1', + 'driver': 'file', + 'filename': '$TEST_IMG', + 'locking': 'on' + } }" \ + 'return' \ + 'error' + +# Now we attach the image to a virtio-blk device. This device does +# require some permissions (at least WRITE and READ_CONSISTENT), so if +# reopening node0 unshared any (which it should not have), this will +# fail (but it should not). +success_or_failure=y _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'device_add', + 'arguments': { + 'driver': 'virtio-blk', + 'drive': 'node1' + } }" \ + 'return' \ + 'error' + +_cleanup_qemu + # success, all done echo "*** done" rm -f $seq.full diff --git a/tests/qemu-iotests/182.out b/tests/qemu-iotests/182.out index 23a4dbf809..af501ca3f3 100644 --- a/tests/qemu-iotests/182.out +++ b/tests/qemu-iotests/182.out @@ -4,5 +4,14 @@ Starting QEMU Starting a second QEMU using the same image should fail QEMU_PROG: -drive file=TEST_DIR/t.qcow2,if=none,id=drive0,file.locking=on: Failed to get "write" lock -Is another process using the image? +Is another process using the image [TEST_DIR/t.qcow2]? + +=== Testing reopen === + +{"return": {}} +{"return": {}} +Formatting 'TEST_DIR/t.qcow2.overlay', fmt=qcow2 size=197120 backing_file=TEST_DIR/t.qcow2 backing_fmt=file cluster_size=65536 lazy_refcounts=off refcount_bits=16 +{"return": {}} +{"return": {}} +{"return": {}} *** done diff --git a/tests/qemu-iotests/183 b/tests/qemu-iotests/183 index c49e1ad6ef..93b7bd798a 100755 --- a/tests/qemu-iotests/183 +++ b/tests/qemu-iotests/183 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test old-style block migration (migrate -b) # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! MIG_SOCKET="${TEST_DIR}/migrate" diff --git a/tests/qemu-iotests/183.out b/tests/qemu-iotests/183.out index 103fdc778b..112cf31f8b 100644 --- a/tests/qemu-iotests/183.out +++ b/tests/qemu-iotests/183.out @@ -36,8 +36,8 @@ wrote 65536/65536 bytes at offset 1048576 {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} No errors were found on the image. No errors were found on the image. wrote 65536/65536 bytes at offset 1048576 diff --git a/tests/qemu-iotests/184 b/tests/qemu-iotests/184 index 2b68284d58..cb0c181228 100755 --- a/tests/qemu-iotests/184 +++ b/tests/qemu-iotests/184 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test I/O throttle block filter driver interface # @@ -24,7 +24,6 @@ owner="Manos Pitsidianakis" seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! trap "exit \$status" 0 1 2 3 15 @@ -35,14 +34,14 @@ trap "exit \$status" 0 1 2 3 15 _supported_os Linux -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" | _filter_imgfmt $QEMU -nographic -qmp-pretty stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_qmp\ | _filter_qemu_io | _filter_generated_node_ids \ diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out index 672650cde8..3deb3cfb94 100644 --- a/tests/qemu-iotests/184.out +++ b/tests/qemu-iotests/184.out @@ -94,7 +94,8 @@ Testing: }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -173,7 +174,8 @@ Testing: }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -214,7 +216,8 @@ Testing: }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -249,7 +252,8 @@ Testing: }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } diff --git a/tests/qemu-iotests/185 b/tests/qemu-iotests/185 index 7dcfdeac60..454ff600cc 100755 --- a/tests/qemu-iotests/185 +++ b/tests/qemu-iotests/185 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test exiting qemu while jobs are still running # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/185.out b/tests/qemu-iotests/185.out index 4e0ca0dffa..ddfbf3c765 100644 --- a/tests/qemu-iotests/185.out +++ b/tests/qemu-iotests/185.out @@ -21,7 +21,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "commit"}} === Start active commit job and exit qemu === @@ -31,7 +31,7 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 backing_file=TEST_DIR/t.q {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "commit"}} === Start mirror job and exit qemu === @@ -42,7 +42,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 4194304, "offset": 4194304, "speed": 65536, "type": "mirror"}} === Start backup job and exit qemu === @@ -53,7 +53,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 65536, "speed": 65536, "type": "backup"}} === Start streaming job and exit qemu === @@ -63,7 +63,7 @@ Formatting 'TEST_DIR/t.qcow2.copy', fmt=qcow2 size=67108864 cluster_size=65536 l {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "disk"}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "disk", "len": 67108864, "offset": 524288, "speed": 65536, "type": "stream"}} No errors were found on the image. *** done diff --git a/tests/qemu-iotests/186 b/tests/qemu-iotests/186 index 0aa4395a57..5dd2177b89 100755 --- a/tests/qemu-iotests/186 +++ b/tests/qemu-iotests/186 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test 'info block' with all kinds of configurations # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -45,7 +44,7 @@ if [ "$QEMU_DEFAULT_MACHINE" != "pc" ]; then _notrun "Requires a PC machine" fi -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" @@ -60,7 +59,7 @@ function do_run_qemu() echo } -function check_info_block() +check_info_block() { echo "info block" | do_run_qemu "$@" | _filter_win32 | _filter_hmp | _filter_qemu | diff --git a/tests/qemu-iotests/187 b/tests/qemu-iotests/187 index 7bb783363c..a45addde09 100755 --- a/tests/qemu-iotests/187 +++ b/tests/qemu-iotests/187 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test switching between read-only and read-write # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188 index 83ed03e33e..be7278aa65 100755 --- a/tests/qemu-iotests/188 +++ b/tests/qemu-iotests/188 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test encrypted read/write using plain bdrv_read/bdrv_write # @@ -24,7 +24,6 @@ owner=berrange@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/189 b/tests/qemu-iotests/189 index e695475722..c9ce9d3bed 100755 --- a/tests/qemu-iotests/189 +++ b/tests/qemu-iotests/189 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test encrypted read/write using backing files # @@ -24,7 +24,6 @@ owner=berrange@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/190 b/tests/qemu-iotests/190 index 8f808fef5d..e1c1d407f0 100755 --- a/tests/qemu-iotests/190 +++ b/tests/qemu-iotests/190 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # qemu-img measure sub-command tests on huge qcow2 files # @@ -24,7 +24,6 @@ owner=eblake@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/191 b/tests/qemu-iotests/191 index d6860e72f7..1ea908ce3d 100755 --- a/tests/qemu-iotests/191 +++ b/tests/qemu-iotests/191 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test commit block job where top has two parents # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/191.out b/tests/qemu-iotests/191.out index 31a0c7d4c4..a513ccca0b 100644 --- a/tests/qemu-iotests/191.out +++ b/tests/qemu-iotests/191.out @@ -389,7 +389,8 @@ wrote 65536/65536 bytes at offset 1048576 }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } image: TEST_DIR/t.IMGFMT @@ -806,7 +807,8 @@ wrote 65536/65536 bytes at offset 1048576 }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } image: TEST_DIR/t.IMGFMT diff --git a/tests/qemu-iotests/192 b/tests/qemu-iotests/192 index 595f0d786a..158086f9d2 100755 --- a/tests/qemu-iotests/192 +++ b/tests/qemu-iotests/192 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test NBD export with -incoming (non-shared storage migration use case from # libvirt) @@ -25,7 +25,6 @@ owner=famz@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 50ac50da5e..71857853fb 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -1,18 +1,18 @@ Launching VMs... Launching NBD server on destination... -{u'return': {}} -{u'return': {}} +{"return": {}} +{"return": {}} Starting `drive-mirror` on source... -{u'return': {}} +{"return": {}} Waiting for `drive-mirror` to complete... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror-job0', u'type': u'mirror', u'speed': 0, u'len': 1073741824, u'offset': 1073741824}, u'event': u'BLOCK_JOB_READY'} +{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Starting migration... -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} +{"return": {}} +{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Gracefully ending the `drive-mirror` job on source... -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror-job0', u'type': u'mirror', u'speed': 0, u'len': 1073741824, u'offset': 1073741824}, u'event': u'BLOCK_JOB_COMPLETED'} +{"return": {}} +{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Stopping the NBD server on destination... -{u'return': {}} +{"return": {}} diff --git a/tests/qemu-iotests/195 b/tests/qemu-iotests/195 index e7a403ded2..bd1b71ae5e 100755 --- a/tests/qemu-iotests/195 +++ b/tests/qemu-iotests/195 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test change-backing-file command # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -42,14 +41,14 @@ _supported_fmt qcow2 _supported_proto file _supported_os Linux -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" $QEMU -nographic -qmp-pretty stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_imgfmt | _filter_qemu \ | _filter_qmp | _filter_qemu_io \ diff --git a/tests/qemu-iotests/195.out b/tests/qemu-iotests/195.out index 7613575c64..1e9330b1be 100644 --- a/tests/qemu-iotests/195.out +++ b/tests/qemu-iotests/195.out @@ -28,7 +28,8 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,backing.node-name=mid }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -65,7 +66,8 @@ Testing: -drive if=none,file=TEST_DIR/t.IMGFMT,node-name=top }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } diff --git a/tests/qemu-iotests/197 b/tests/qemu-iotests/197 index 0369aa5cff..2c664793f4 100755 --- a/tests/qemu-iotests/197 +++ b/tests/qemu-iotests/197 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for copy-on-read into qcow2 # @@ -24,7 +24,6 @@ owner=eblake@redhat.com seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! # get standard environment, filters and checks diff --git a/tests/qemu-iotests/198 b/tests/qemu-iotests/198 index 54eaaf5153..c8f824cfae 100755 --- a/tests/qemu-iotests/198 +++ b/tests/qemu-iotests/198 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test commit of encrypted qcow2 files # @@ -24,7 +24,6 @@ owner=berrange@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/200 b/tests/qemu-iotests/200 index ddbdedc476..1c0f8cafc2 100755 --- a/tests/qemu-iotests/200 +++ b/tests/qemu-iotests/200 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Block job co-routine race condition test. # @@ -26,7 +26,6 @@ owner=jcody@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -53,13 +52,21 @@ ${QEMU_IMG} create -f $IMGFMT -F $IMGFMT "${TEST_IMG}" -b "${BACKING_IMG}" 512M ${QEMU_IO} -c "write -P 0xa5 512 300M" "${BACKING_IMG}" | _filter_qemu_io +case "$QEMU_DEFAULT_MACHINE" in + s390-ccw-virtio) + virtio_scsi="-device virtio-scsi-ccw,id=scsi0,iothread=iothread0" + ;; + *) + virtio_scsi="-device pci-bridge,id=bridge1,chassis_nr=1,bus=pci.0 + -device virtio-scsi-pci,bus=bridge1,addr=0x1f,id=scsi0,iothread=iothread0" + ;; +esac + echo echo === Starting QEMU VM === echo qemu_comm_method="qmp" -_launch_qemu -device pci-bridge,id=bridge1,chassis_nr=1,bus=pci.0 \ - -object iothread,id=iothread0 \ - -device virtio-scsi-pci,bus=bridge1,addr=0x1f,id=scsi0,iothread=iothread0 \ +_launch_qemu -object iothread,id=iothread0 $virtio_scsi \ -drive file="${TEST_IMG}",media=disk,if=none,cache=$CACHEMODE,id=drive_sysdisk,format=$IMGFMT \ -device scsi-hd,drive=drive_sysdisk,bus=scsi0.0,id=sysdisk,bootindex=0 h1=$QEMU_HANDLE diff --git a/tests/qemu-iotests/201 b/tests/qemu-iotests/201 index c1a1e00077..7abf740fe4 100755 --- a/tests/qemu-iotests/201 +++ b/tests/qemu-iotests/201 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test savevm and loadvm after live migration with postcopy flag # diff --git a/tests/qemu-iotests/202.out b/tests/qemu-iotests/202.out index d5ea374e17..9a8619e796 100644 --- a/tests/qemu-iotests/202.out +++ b/tests/qemu-iotests/202.out @@ -1,11 +1,11 @@ Launching VM... Adding IOThread... -{u'return': {}} +{"return": {}} Adding blockdevs... -{u'return': {}} -{u'return': {}} +{"return": {}} +{"return": {}} Setting iothread... -{u'return': {}} -{u'return': {}} +{"return": {}} +{"return": {}} Creating external snapshots... -{u'return': {}} +{"return": {}} diff --git a/tests/qemu-iotests/203.out b/tests/qemu-iotests/203.out index 1a11f0975c..9d4abba8c5 100644 --- a/tests/qemu-iotests/203.out +++ b/tests/qemu-iotests/203.out @@ -1,11 +1,11 @@ Launching VM... Setting IOThreads... -{u'return': {}} -{u'return': {}} +{"return": {}} +{"return": {}} Enabling migration QMP events... -{u'return': {}} +{"return": {}} Starting migration... -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'setup'}, u'event': u'MIGRATION'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'active'}, u'event': u'MIGRATION'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'completed'}, u'event': u'MIGRATION'} +{"return": {}} +{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/204 b/tests/qemu-iotests/204 index feb69d2ada..abb73dc381 100755 --- a/tests/qemu-iotests/204 +++ b/tests/qemu-iotests/204 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test corner cases with unusual block geometries # @@ -24,7 +24,6 @@ owner=eblake@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() @@ -94,7 +93,7 @@ $QEMU_IO -c "open -o $options,$limits blkdebug::$TEST_IMG" \ echo echo "== verify image content ==" -function verify_io() +verify_io() { echo read -P 22 0 1000 echo read -P 33 1000 128k diff --git a/tests/qemu-iotests/206 b/tests/qemu-iotests/206 index 128c334c7c..5bb738bf23 100755 --- a/tests/qemu-iotests/206 +++ b/tests/qemu-iotests/206 @@ -26,7 +26,9 @@ from iotests import imgfmt iotests.verify_image_format(supported_fmts=['qcow2']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', + filters=[iotests.filter_qmp_testfiles], + job_id='job0', options=options) if 'return' in result: assert result['return'] == {} @@ -52,7 +54,9 @@ with iotests.FilePath('t.qcow2') as disk_path, \ 'filename': disk_path, 'size': 0 }) - vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + vm.qmp_log('blockdev-add', + filters=[iotests.filter_qmp_testfiles], + driver='file', filename=disk_path, node_name='imgfile') blockdev_create(vm, { 'driver': imgfmt, diff --git a/tests/qemu-iotests/206.out b/tests/qemu-iotests/206.out index 789eebe57b..0f1c23babb 100644 --- a/tests/qemu-iotests/206.out +++ b/tests/qemu-iotests/206.out @@ -1,16 +1,16 @@ === Successful image creation (defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}} -{u'return': {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'imgfile', 'size': 134217728}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "node-name": "imgfile"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "imgfile", "size": 134217728}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -24,15 +24,15 @@ Format specific information: === Successful image creation (inline blockdev-add, explicit defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': False, 'preallocation': 'off', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": false, "preallocation": "off", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'refcount-bits': 16, 'version': 'v3', 'preallocation': 'off', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': False, 'driver': 'qcow2', 'size': 67108864}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 65536, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": false, "preallocation": "off", "refcount-bits": 16, "size": 67108864, "version": "v3"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -46,15 +46,15 @@ Format specific information: === Successful image creation (v3 non-default options) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'nocow': True, 'preallocation': 'falloc', 'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "nocow": true, "preallocation": "falloc", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 2097152, 'refcount-bits': 1, 'version': 'v3', 'preallocation': 'metadata', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'lazy-refcounts': True, 'driver': 'qcow2', 'size': 33554432}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 2097152, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "lazy-refcounts": true, "preallocation": "metadata", "refcount-bits": 1, "size": 33554432, "version": "v3"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -68,15 +68,15 @@ Format specific information: === Successful image creation (v2 non-default options) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'backing-fmt': 'qcow2', 'driver': 'qcow2', 'version': 'v2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'backing-file': 'TEST_DIR/PID-t.qcow2.base', 'size': 33554432}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-file": "TEST_DIR/PID-t.qcow2.base", "backing-fmt": "qcow2", "cluster-size": 512, "driver": "qcow2", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432, "version": "v2"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -90,10 +90,10 @@ Format specific information: === Successful image creation (encrypted) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'encrypt': {'key-secret': 'keysec0', 'iter-time': 10, 'cipher-mode': 'ctr', 'ivgen-hash-alg': 'md5', 'cipher-alg': 'twofish-128', 'format': 'luks', 'ivgen-alg': 'plain64', 'hash-alg': 'sha1'}, 'driver': 'qcow2', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.qcow2'}, 'size': 33554432}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "encrypt": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "format": "luks", "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0"}, "file": {"driver": "file", "filename": "TEST_DIR/PID-t.qcow2"}, "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -144,113 +144,113 @@ Format specific information: === Invalid BlockdevRef === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': "this doesn't exist", 'size': 33554432}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "this doesn't exist", "size": 33554432}}} +{"return": {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid sizes === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 1234}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 1234}}} +{"return": {}} Job failed: Image size must be a multiple of 512 bytes -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 18446744073709551104L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 18446744073709551104}}} +{"return": {}} Job failed: Could not resize image: Image size cannot be negative -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775808L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775808}}} +{"return": {}} Job failed: Could not resize image: Image size cannot be negative -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'qcow2', 'file': 'node0', 'size': 9223372036854775296}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 9223372036854775296}}} +{"return": {}} Job failed: Could not resize image: Failed to grow the L1 table: File too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid version === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'version': 'v1', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter 'v1'"}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "size": 67108864, "version": "v1"}}} +{"error": {"class": "GenericError", "desc": "Invalid parameter 'v1'"}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'lazy-refcounts': True, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "lazy-refcounts": true, "size": 67108864, "version": "v2"}}} +{"return": {}} Job failed: Lazy refcounts only supported with compatibility level 1.1 and above (use version=v3 or greater) -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 8, 'version': 'v2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 8, "size": 67108864, "version": "v2"}}} +{"return": {}} Job failed: Different refcount widths than 16 bits require compatibility level 1.1 or above (use version=v3 or greater) -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid backing file options === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'full', 'driver': 'qcow2', 'backing-file': '/dev/null', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-file": "/dev/null", "driver": "qcow2", "file": "node0", "preallocation": "full", "size": 67108864}}} +{"return": {}} Job failed: Backing file and preallocation cannot be used at the same time -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'backing-fmt': 'qcow2', 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"backing-fmt": "qcow2", "driver": "qcow2", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Backing format cannot be used without backing file -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid cluster size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "qcow2", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size must be a power of two between 512 and 2048k -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 128, "driver": "qcow2", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size must be a power of two between 512 and 2048k -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4194304, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 4194304, "driver": "qcow2", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size must be a power of two between 512 and 2048k -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 0, "driver": "qcow2", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size must be a power of two between 512 and 2048k -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'qcow2', 'file': 'node0', 'size': 281474976710656}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 512, "driver": "qcow2", "file": "node0", "size": 281474976710656}}} +{"return": {}} Job failed: Could not resize image: Failed to grow the L1 table: File too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid refcount width === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 128, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 128, "size": 67108864}}} +{"return": {}} Job failed: Refcount width must be a power of two and may not exceed 64 bits -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 0, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 0, "size": 67108864}}} +{"return": {}} Job failed: Refcount width must be a power of two and may not exceed 64 bits -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'refcount-bits': 7, 'driver': 'qcow2', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "qcow2", "file": "node0", "refcount-bits": 7, "size": 67108864}}} +{"return": {}} Job failed: Refcount width must be a power of two and may not exceed 64 bits -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/207 b/tests/qemu-iotests/207 index 444ae233ae..dfd3c51bd1 100755 --- a/tests/qemu-iotests/207 +++ b/tests/qemu-iotests/207 @@ -27,12 +27,16 @@ import re iotests.verify_image_format(supported_fmts=['raw']) iotests.verify_protocol(supported=['ssh']) -def filter_hash(msg): - return re.sub("'hash': '[0-9a-f]+'", "'hash': HASH", msg) +def filter_hash(qmsg): + def _filter(key, value): + if key == 'hash' and re.match('[0-9a-f]+', value): + return 'HASH' + return value + return iotests.filter_qmp(qmsg, _filter) def blockdev_create(vm, options): result = vm.qmp_log('blockdev-create', job_id='job0', options=options, - filters=[iotests.filter_testfiles, filter_hash]) + filters=[iotests.filter_qmp_testfiles, filter_hash]) if 'return' in result: assert result['return'] == {} @@ -109,7 +113,7 @@ with iotests.FilePath('t.img') as disk_path, \ md5_key = subprocess.check_output( 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + 'cut -d" " -f3 | base64 -d | md5sum -b | cut -d" " -f1', - shell=True).rstrip() + shell=True).rstrip().decode('ascii') vm.launch() blockdev_create(vm, { 'driver': 'ssh', @@ -147,7 +151,7 @@ with iotests.FilePath('t.img') as disk_path, \ sha1_key = subprocess.check_output( 'ssh-keyscan -t rsa 127.0.0.1 2>/dev/null | grep -v "\\^#" | ' + 'cut -d" " -f3 | base64 -d | sha1sum -b | cut -d" " -f1', - shell=True).rstrip() + shell=True).rstrip().decode('ascii') vm.launch() blockdev_create(vm, { 'driver': 'ssh', diff --git a/tests/qemu-iotests/207.out b/tests/qemu-iotests/207.out index 078b7e63cb..568e8619d0 100644 --- a/tests/qemu-iotests/207.out +++ b/tests/qemu-iotests/207.out @@ -1,9 +1,9 @@ === Successful image creation (defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT @@ -16,49 +16,49 @@ virtual size: 4.0M (4194304 bytes) === Test host-key-check options === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 8.0M (8388608 bytes) -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'known_hosts'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "known_hosts"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 4.0M (4194304 bytes) -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} +{"return": {}} Job failed: remote host key does not match host_key_check 'wrong' -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'md5', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 8388608}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "md5"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 8388608}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT virtual size: 8.0M (8388608 bytes) -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': 'wrong', 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 2097152}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "wrong", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 2097152}}} +{"return": {}} Job failed: remote host key does not match host_key_check 'wrong' -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'hash': HASH, 'type': 'sha1', 'mode': 'hash'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"hash": "HASH", "mode": "hash", "type": "sha1"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"server.host": "127.0.0.1", "server.port": "22", "driver": "ssh", "path": "TEST_IMG"}} file format: IMGFMT @@ -66,15 +66,15 @@ virtual size: 4.0M (4194304 bytes) === Invalid path and user === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': '/this/is/not/an/existing/path', 'host-key-check': {'mode': 'none'}, 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "/this/is/not/an/existing/path", "server": {"host": "127.0.0.1", "port": "22"}}, "size": 4194304}}} +{"return": {}} Job failed: failed to open remote file '/this/is/not/an/existing/path': Failed opening remote file (libssh2 error code: -31) -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'ssh', 'location': {'path': 'TEST_DIR/PID-t.img', 'host-key-check': {'mode': 'none'}, 'user': 'invalid user', 'server': {'host': '127.0.0.1', 'port': '22'}}, 'size': 4194304}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "ssh", "location": {"host-key-check": {"mode": "none"}, "path": "TEST_DIR/PID-t.img", "server": {"host": "127.0.0.1", "port": "22"}, "user": "invalid user"}, "size": 4194304}}} +{"return": {}} Job failed: failed to authenticate using publickey authentication and the identities held by your ssh-agent -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/208.out b/tests/qemu-iotests/208.out index 3687e9d0dd..9ff2582a42 100644 --- a/tests/qemu-iotests/208.out +++ b/tests/qemu-iotests/208.out @@ -1,9 +1,9 @@ Launching VM... Starting NBD server... -{u'return': {}} +{"return": {}} Adding NBD export... -{u'return': {}} +{"return": {}} Creating external snapshot... -{u'return': {}} +{"return": {}} Stopping NBD server... -{u'return': {}} +{"return": {}} diff --git a/tests/qemu-iotests/209.out b/tests/qemu-iotests/209.out index 0d29724e84..214e27bfce 100644 --- a/tests/qemu-iotests/209.out +++ b/tests/qemu-iotests/209.out @@ -1,2 +1,2 @@ -[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true}, -{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false}] +[{ "start": 0, "length": 524288, "depth": 0, "zero": false, "data": true, "offset": 0}, +{ "start": 524288, "length": 524288, "depth": 0, "zero": true, "data": false, "offset": 524288}] diff --git a/tests/qemu-iotests/210 b/tests/qemu-iotests/210 index d142841e2b..565e3b7b9b 100755 --- a/tests/qemu-iotests/210 +++ b/tests/qemu-iotests/210 @@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['luks']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_qmp_testfiles]) if 'return' in result: assert result['return'] == {} @@ -53,7 +54,7 @@ with iotests.FilePath('t.luks') as disk_path, \ 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, - node_name='imgfile') + node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) blockdev_create(vm, { 'driver': imgfmt, 'file': 'imgfile', diff --git a/tests/qemu-iotests/210.out b/tests/qemu-iotests/210.out index 078ba544a1..a3692ce00d 100644 --- a/tests/qemu-iotests/210.out +++ b/tests/qemu-iotests/210.out @@ -1,16 +1,16 @@ === Successful image creation (defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}} -{u'return': {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'imgfile', 'size': 134217728}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "node-name": "imgfile"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "imgfile", "iter-time": 10, "key-secret": "keysec0", "size": 134217728}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT @@ -54,15 +54,15 @@ Format specific information: === Successful image creation (with non-default options) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.luks", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'hash-alg': 'sha1', 'cipher-mode': 'ctr', 'cipher-alg': 'twofish-128', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.luks'}, 'iter-time': 10, 'ivgen-alg': 'plain64', 'ivgen-hash-alg': 'md5', 'driver': 'luks', 'size': 67108864}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cipher-alg": "twofish-128", "cipher-mode": "ctr", "driver": "luks", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.luks"}, "hash-alg": "sha1", "iter-time": 10, "ivgen-alg": "plain64", "ivgen-hash-alg": "md5", "key-secret": "keysec0", "size": 67108864}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT @@ -106,18 +106,18 @@ Format specific information: === Invalid BlockdevRef === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'luks', 'file': "this doesn't exist", 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "this doesn't exist", "size": 67108864}}} +{"return": {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Zero size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'iter-time': 10, 'driver': 'luks', 'file': 'node0', 'size': 0}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "iter-time": 10, "key-secret": "keysec0", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT @@ -161,34 +161,34 @@ Format specific information: === Invalid sizes === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 18446744073709551104L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 18446744073709551104}}} +{"return": {}} Job failed: The requested file size is too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775808L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775808}}} +{"return": {}} Job failed: The requested file size is too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'key-secret': 'keysec0', 'driver': 'luks', 'file': 'node0', 'size': 9223372036854775296}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "luks", "file": "node0", "key-secret": "keysec0", "size": 9223372036854775296}}} +{"return": {}} Job failed: The requested file size is too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Resize image with invalid sizes === -{'execute': 'block_resize', 'arguments': {'size': 9223372036854775296, 'node_name': 'node1'}} -{u'error': {u'class': u'GenericError', u'desc': u'The requested file size is too large'}} -{'execute': 'block_resize', 'arguments': {'size': 9223372036854775808L, 'node_name': 'node1'}} -{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} -{'execute': 'block_resize', 'arguments': {'size': 18446744073709551104L, 'node_name': 'node1'}} -{u'error': {u'class': u'GenericError', u'desc': u"Invalid parameter type for 'size', expected: integer"}} -{'execute': 'block_resize', 'arguments': {'size': -9223372036854775808, 'node_name': 'node1'}} -{u'error': {u'class': u'GenericError', u'desc': u"Parameter 'size' expects a >0 size"}} +{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 9223372036854775296}} +{"error": {"class": "GenericError", "desc": "The requested file size is too large"}} +{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 9223372036854775808}} +{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} +{"execute": "block_resize", "arguments": {"node-name": "node1", "size": 18446744073709551104}} +{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'size', expected: integer"}} +{"execute": "block_resize", "arguments": {"node-name": "node1", "size": -9223372036854775808}} +{"error": {"class": "GenericError", "desc": "Parameter 'size' expects a >0 size"}} image: json:{"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_IMG"}, "key-secret": "keysec0"} file format: IMGFMT virtual size: 0 (0 bytes) diff --git a/tests/qemu-iotests/211 b/tests/qemu-iotests/211 index 7b7985db6c..6afc894f76 100755 --- a/tests/qemu-iotests/211 +++ b/tests/qemu-iotests/211 @@ -27,11 +27,14 @@ iotests.verify_image_format(supported_fmts=['vdi']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_qmp_testfiles]) if 'return' in result: assert result['return'] == {} - vm.run_job('job0') + error = vm.run_job('job0') + if error and 'Could not allocate bmap' in error: + iotests.notrun('Insufficient memory') iotests.log("") with iotests.FilePath('t.vdi') as disk_path, \ @@ -51,7 +54,7 @@ with iotests.FilePath('t.vdi') as disk_path, \ 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, - node_name='imgfile') + node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) blockdev_create(vm, { 'driver': imgfmt, 'file': 'imgfile', diff --git a/tests/qemu-iotests/211.out b/tests/qemu-iotests/211.out index 6feaea3978..682adc2a10 100644 --- a/tests/qemu-iotests/211.out +++ b/tests/qemu-iotests/211.out @@ -1,16 +1,16 @@ === Successful image creation (defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}} -{u'return': {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'imgfile', 'size': 134217728}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "node-name": "imgfile"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "imgfile", "size": 134217728}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -21,15 +21,15 @@ cluster_size: 1048576 === Successful image creation (explicit defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'off', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 67108864}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "off", "size": 67108864}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -40,15 +40,15 @@ cluster_size: 1048576 === Successful image creation (with non-default options) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'preallocation': 'metadata', 'driver': 'vdi', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vdi'}, 'size': 33554432}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vdi"}, "preallocation": "metadata", "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -60,18 +60,18 @@ cluster_size: 1048576 === Invalid BlockdevRef === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': "this doesn't exist", 'size': 33554432}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "this doesn't exist", "size": 33554432}}} +{"return": {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Zero size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 0}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -80,10 +80,10 @@ cluster_size: 1048576 === Maximum size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203584}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203584}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -92,21 +92,21 @@ cluster_size: 1048576 === Invalid sizes === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 18446744073709551104L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 18446744073709551104}}} +{"return": {}} Job failed: Unsupported VDI image size (size is 0xfffffffffffffe00, max supported is 0x1fffff8000000) -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 9223372036854775808L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 9223372036854775808}}} +{"return": {}} Job failed: Unsupported VDI image size (size is 0x8000000000000000, max supported is 0x1fffff8000000) -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vdi', 'file': 'node0', 'size': 562949819203585}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vdi", "file": "node0", "size": 562949819203585}}} +{"return": {}} Job failed: Unsupported VDI image size (size is 0x1fffff8000001, max supported is 0x1fffff8000000) -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/212 b/tests/qemu-iotests/212 index 95c8810d83..42b74f208b 100755 --- a/tests/qemu-iotests/212 +++ b/tests/qemu-iotests/212 @@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['parallels']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_qmp_testfiles]) if 'return' in result: assert result['return'] == {} @@ -51,7 +52,7 @@ with iotests.FilePath('t.parallels') as disk_path, \ 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, - node_name='imgfile') + node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) blockdev_create(vm, { 'driver': imgfmt, 'file': 'imgfile', diff --git a/tests/qemu-iotests/212.out b/tests/qemu-iotests/212.out index 9150da7a2c..22810720cf 100644 --- a/tests/qemu-iotests/212.out +++ b/tests/qemu-iotests/212.out @@ -1,16 +1,16 @@ === Successful image creation (defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}} -{u'return': {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'imgfile', 'size': 134217728}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "node-name": "imgfile"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "imgfile", "size": 134217728}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -18,15 +18,15 @@ virtual size: 128M (134217728 bytes) === Successful image creation (explicit defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1048576, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 67108864}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1048576, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 67108864}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -34,15 +34,15 @@ virtual size: 64M (67108864 bytes) === Successful image creation (with non-default options) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 65536, 'driver': 'parallels', 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.parallels'}, 'size': 33554432}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 65536, "driver": "parallels", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.parallels"}, "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -50,18 +50,18 @@ virtual size: 32M (33554432 bytes) === Invalid BlockdevRef === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': "this doesn't exist", 'size': 33554432}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "this doesn't exist", "size": 33554432}}} +{"return": {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Zero size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 0}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -69,10 +69,10 @@ virtual size: 0 (0 bytes) === Maximum size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627369984}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627369984}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -80,77 +80,77 @@ virtual size: 4096T (4503599627369984 bytes) === Invalid sizes === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 1234}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 1234}}} +{"return": {}} Job failed: Image size must be a multiple of 512 bytes -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 18446744073709551104L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 18446744073709551104}}} +{"return": {}} Job failed: Image size is too large for this cluster size -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775808L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775808}}} +{"return": {}} Job failed: Image size is too large for this cluster size -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 9223372036854775296}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 9223372036854775296}}} +{"return": {}} Job failed: Image size is too large for this cluster size -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'parallels', 'file': 'node0', 'size': 4503599627370497}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "parallels", "file": "node0", "size": 4503599627370497}}} +{"return": {}} Job failed: Image size is too large for this cluster size -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid cluster size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 1234, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 1234, "driver": "parallels", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size must be a multiple of 512 bytes -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 128, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 128, "driver": "parallels", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size must be a multiple of 512 bytes -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 4294967296, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 4294967296, "driver": "parallels", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size is too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 9223372036854775808L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 9223372036854775808, "driver": "parallels", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size is too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 18446744073709551104L, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 18446744073709551104, "driver": "parallels", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Cluster size is too large -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 0, 'driver': 'parallels', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 0, "driver": "parallels", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Image size is too large for this cluster size -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'cluster-size': 512, 'driver': 'parallels', 'file': 'node0', 'size': 281474976710656}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"cluster-size": 512, "driver": "parallels", "file": "node0", "size": 281474976710656}}} +{"return": {}} Job failed: Image size is too large for this cluster size -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/213 b/tests/qemu-iotests/213 index 4054439e3c..5604f3cebb 100755 --- a/tests/qemu-iotests/213 +++ b/tests/qemu-iotests/213 @@ -27,7 +27,8 @@ iotests.verify_image_format(supported_fmts=['vhdx']) iotests.verify_protocol(supported=['file']) def blockdev_create(vm, options): - result = vm.qmp_log('blockdev-create', job_id='job0', options=options) + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_qmp_testfiles]) if 'return' in result: assert result['return'] == {} @@ -51,7 +52,7 @@ with iotests.FilePath('t.vhdx') as disk_path, \ 'size': 0 }) vm.qmp_log('blockdev-add', driver='file', filename=disk_path, - node_name='imgfile') + node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) blockdev_create(vm, { 'driver': imgfmt, 'file': 'imgfile', diff --git a/tests/qemu-iotests/213.out b/tests/qemu-iotests/213.out index e1dcd47201..169083e08e 100644 --- a/tests/qemu-iotests/213.out +++ b/tests/qemu-iotests/213.out @@ -1,16 +1,16 @@ === Successful image creation (defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-add', 'arguments': {'node_name': 'imgfile', 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}} -{u'return': {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'imgfile', 'size': 134217728}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "node-name": "imgfile"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "imgfile", "size": 134217728}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -19,15 +19,15 @@ cluster_size: 8388608 === Successful image creation (explicit defaults) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 8388608, 'driver': 'vhdx', 'subformat': 'dynamic', 'log-size': 1048576, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': True, 'size': 67108864}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 8388608, "block-state-zero": true, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 1048576, "size": 67108864, "subformat": "dynamic"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -36,15 +36,15 @@ cluster_size: 8388608 === Successful image creation (with non-default options) === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'size': 0, 'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'block-size': 268435456, 'driver': 'vhdx', 'subformat': 'fixed', 'log-size': 8388608, 'file': {'driver': 'file', 'filename': 'TEST_DIR/PID-t.vhdx'}, 'block-state-zero': False, 'size': 33554432}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 268435456, "block-state-zero": false, "driver": "vhdx", "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vhdx"}, "log-size": 8388608, "size": 33554432, "subformat": "fixed"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -53,18 +53,18 @@ cluster_size: 268435456 === Invalid BlockdevRef === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': "this doesn't exist", 'size': 33554432}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "this doesn't exist", "size": 33554432}}} +{"return": {}} Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Zero size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 0}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -73,10 +73,10 @@ cluster_size: 8388608 === Maximum size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177664}}} -{u'return': {}} -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177664}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} image: TEST_IMG file format: IMGFMT @@ -85,85 +85,85 @@ cluster_size: 67108864 === Invalid sizes === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 18446744073709551104L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 18446744073709551104}}} +{"return": {}} Job failed: Image size too large; max of 64TB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775808L}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775808}}} +{"return": {}} Job failed: Image size too large; max of 64TB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 9223372036854775296}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 9223372036854775296}}} +{"return": {}} Job failed: Image size too large; max of 64TB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'file': 'node0', 'size': 70368744177665}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "size": 70368744177665}}} +{"return": {}} Job failed: Image size too large; max of 64TB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid block size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 1234567, 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 1234567, "driver": "vhdx", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Block size must be a multiple of 1 MB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 128, 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 128, "driver": "vhdx", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Block size must be a multiple of 1 MB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 3145728, 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 3145728, "driver": "vhdx", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Block size must be a power of two -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 536870912, 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 536870912, "driver": "vhdx", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Block size must not exceed 268435456 -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'driver': 'vhdx', 'block-size': 0, 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"block-size": 0, "driver": "vhdx", "file": "node0", "size": 67108864}}} +{"return": {}} Job failed: Block size must be a multiple of 1 MB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} === Invalid log size === -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 1234567, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 1234567, "size": 67108864}}} +{"return": {}} Job failed: Log size must be a multiple of 1 MB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 128, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 128, "size": 67108864}}} +{"return": {}} Job failed: Log size must be a multiple of 1 MB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 4294967296, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 4294967296, "size": 67108864}}} +{"return": {}} Job failed: Log size must be smaller than 4 GB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} -{'execute': 'blockdev-create', 'arguments': {'job_id': 'job0', 'options': {'log-size': 0, 'driver': 'vhdx', 'file': 'node0', 'size': 67108864}}} -{u'return': {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vhdx", "file": "node0", "log-size": 0, "size": 67108864}}} +{"return": {}} Job failed: Log size must be a multiple of 1 MB -{'execute': 'job-dismiss', 'arguments': {'id': 'job0'}} -{u'return': {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} diff --git a/tests/qemu-iotests/214 b/tests/qemu-iotests/214 index c46ca2a6dd..c1a452ff9a 100755 --- a/tests/qemu-iotests/214 +++ b/tests/qemu-iotests/214 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qcow2 image compression # @@ -22,7 +22,6 @@ seq=$(basename "$0") echo "QA output created by $seq" -here=$PWD status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/215 b/tests/qemu-iotests/215 index 2e616ed659..7b063d7cfa 100755 --- a/tests/qemu-iotests/215 +++ b/tests/qemu-iotests/215 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test case for copy-on-read into qcow2, using the COR filter driver # @@ -21,7 +21,6 @@ seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! # get standard environment, filters and checks diff --git a/tests/qemu-iotests/216.out b/tests/qemu-iotests/216.out index 45ea857ee1..a70aa5cdae 100644 --- a/tests/qemu-iotests/216.out +++ b/tests/qemu-iotests/216.out @@ -7,8 +7,8 @@ Done --- Doing COR --- -{u'return': {}} -{u'return': u''} +{"return": {}} +{"return": ""} --- Checking COR result --- diff --git a/tests/qemu-iotests/217 b/tests/qemu-iotests/217 index d3ab5d72be..f5482bb669 100755 --- a/tests/qemu-iotests/217 +++ b/tests/qemu-iotests/217 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # I/O errors when working with internal qcow2 snapshots, and repairing # the result diff --git a/tests/qemu-iotests/218 b/tests/qemu-iotests/218 old mode 100644 new mode 100755 diff --git a/tests/qemu-iotests/218.out b/tests/qemu-iotests/218.out index 7dbf78e682..825a657081 100644 --- a/tests/qemu-iotests/218.out +++ b/tests/qemu-iotests/218.out @@ -4,27 +4,27 @@ --- force=false --- Cancelling job -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} +{"return": {}} +{"data": {"device": "mirror", "len": 1048576, "offset": 65536, "speed": 65536, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} --- force=true --- Cancelling job -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 65536, u'len': 1048576, u'offset': 65536}, u'event': u'BLOCK_JOB_CANCELLED'} +{"return": {}} +{"data": {"device": "mirror", "len": 1048576, "offset": 65536, "speed": 65536, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} === Cancel mirror job after convergence === --- force=false --- -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} +{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Cancelling job -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_COMPLETED'} +{"return": {}} +{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} --- force=true --- -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_READY'} +{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Cancelling job -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'mirror', u'type': u'mirror', u'speed': 0, u'len': 1048576, u'offset': 1048576}, u'event': u'BLOCK_JOB_CANCELLED'} +{"return": {}} +{"data": {"device": "mirror", "len": 1048576, "offset": 1048576, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} diff --git a/tests/qemu-iotests/219.out b/tests/qemu-iotests/219.out index 6dc07bc41e..8ebd3fee60 100644 --- a/tests/qemu-iotests/219.out +++ b/tests/qemu-iotests/219.out @@ -2,326 +2,326 @@ Launching VM... Starting block job: drive-mirror (auto-finalize: True; auto-dismiss: True) -{u'return': {}} -{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'mirror'}]} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{"return": {}} +{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "mirror"}]} +{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Pause/resume in RUNNING === Testing block-job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} === Testing block-job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} === Testing job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} === Testing job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'return': {}} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "mirror"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"return": {}} Waiting for READY state... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} Pause/resume in READY === Testing block-job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"return": {}} +{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} === Testing block-job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"return": {}} +{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} === Testing job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} +{"return": {}} +{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} === Testing job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'standby', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'standby', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'ready', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'ready', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'mirror'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} -{u'return': {}} +{"return": {}} +{"data": {"id": "job0", "status": "standby"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "standby", "total-progress": 4194304, "type": "mirror"}]} +{"return": {}} +{"data": {"id": "job0", "status": "ready"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "ready", "total-progress": 4194304, "type": "mirror"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'ready' cannot accept command verb 'dismiss'"}} +{"return": {}} Waiting for PENDING state... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': []} +{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": []} Starting block job: drive-backup (auto-finalize: True; auto-dismiss: True) -{u'return': {}} -{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{"return": {}} +{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} +{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Pause/resume in RUNNING === Testing block-job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing block-job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'return': {}} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"return": {}} Waiting for PENDING state... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': []} +{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": []} Starting block job: drive-backup (auto-finalize: True; auto-dismiss: False) -{u'return': {}} -{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{"return": {}} +{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} +{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Pause/resume in RUNNING === Testing block-job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing block-job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'return': {}} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"return": {}} Waiting for PENDING state... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': []} +{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "concluded", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{"return": {}} +{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": []} Starting block job: drive-backup (auto-finalize: False; auto-dismiss: True) -{u'return': {}} -{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{"return": {}} +{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} +{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Pause/resume in RUNNING === Testing block-job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing block-job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'return': {}} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"return": {}} Waiting for PENDING state... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': []} +{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{"return": {}} +{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": []} Starting block job: drive-backup (auto-finalize: False; auto-dismiss: False) -{u'return': {}} -{u'return': [{u'status': u'running', u'current-progress': 'FILTERED', u'total-progress': 'FILTERED', u'id': u'job0', u'type': u'backup'}]} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'created', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} +{"return": {}} +{"return": [{"current-progress": "FILTERED", "id": "job0", "status": "running", "total-progress": "FILTERED", "type": "backup"}]} +{"data": {"id": "job0", "status": "created"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Pause/resume in RUNNING === Testing block-job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 65536, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 65536, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing block-job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 131072, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 131072, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/block-job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 196608, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 196608, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} === Testing job-pause/job-resume === -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'paused', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'paused', u'current-progress': 262144, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'running', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'running', u'current-progress': 327680, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} -{u'return': {}} +{"return": {}} +{"data": {"id": "job0", "status": "paused"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 262144, "id": "job0", "status": "paused", "total-progress": 4194304, "type": "backup"}]} +{"return": {}} +{"data": {"id": "job0", "status": "running"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 327680, "id": "job0", "status": "running", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'running' cannot accept command verb 'dismiss'"}} +{"return": {}} Waiting for PENDING state... -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'waiting', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'pending', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'pending', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'concluded', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': [{u'status': u'concluded', u'current-progress': 4194304, u'total-progress': 4194304, u'id': u'job0', u'type': u'backup'}]} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} -{u'error': {u'class': u'GenericError', u'desc': u"Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'status': u'null', u'id': u'job0'}, u'event': u'JOB_STATUS_CHANGE'} -{u'return': []} +{"data": {"id": "job0", "status": "waiting"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"id": "job0", "status": "pending"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "pending", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'pending' cannot accept command verb 'dismiss'"}} +{"return": {}} +{"data": {"id": "job0", "status": "concluded"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": [{"current-progress": 4194304, "id": "job0", "status": "concluded", "total-progress": 4194304, "type": "backup"}]} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'pause'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'complete'"}} +{"error": {"class": "GenericError", "desc": "Job 'job0' in state 'concluded' cannot accept command verb 'finalize'"}} +{"return": {}} +{"data": {"id": "job0", "status": "null"}, "event": "JOB_STATUS_CHANGE", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": []} diff --git a/tests/qemu-iotests/220 b/tests/qemu-iotests/220 new file mode 100755 index 0000000000..2d62c5dcac --- /dev/null +++ b/tests/qemu-iotests/220 @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# +# max limits on compression in huge qcow2 files +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq=$(basename $0) +echo "QA output created by $seq" + +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.pattern + +_supported_fmt qcow2 +_supported_proto file +_supported_os Linux + +echo "== Creating huge file ==" + +# Sanity check: We require a file system that permits the creation +# of a HUGE (but very sparse) file. tmpfs works, ext4 does not. +if ! truncate --size=513T "$TEST_IMG"; then + _notrun "file system on $TEST_DIR does not support large enough files" +fi +rm "$TEST_IMG" +IMGOPTS='cluster_size=2M,refcount_bits=1' _make_test_img 513T + +echo "== Populating refcounts ==" +# We want an image with 256M refcounts * 2M clusters = 512T referenced. +# Each 2M cluster holds 16M refcounts; the refcount table initially uses +# 1 refblock, so we need to add 15 more. The refcount table lives at 2M, +# first refblock at 4M, L2 at 6M, so our remaining additions start at 8M. +# Then, for each refblock, mark it as fully populated. +to_hex() { + printf %016x\\n $1 | sed 's/\(..\)/\\x\1/g' +} +truncate --size=38m "$TEST_IMG" +entry=$((0x200000)) +$QEMU_IO_PROG -f raw -c "w -P 0xff 4m 2m" "$TEST_IMG" | _filter_qemu_io +for i in {1..15}; do + offs=$((0x600000 + i*0x200000)) + poke_file "$TEST_IMG" $((i*8 + entry)) $(to_hex $offs) + $QEMU_IO_PROG -f raw -c "w -P 0xff $offs 2m" "$TEST_IMG" | _filter_qemu_io +done + +echo "== Checking file before ==" +# FIXME: 'qemu-img check' doesn't diagnose refcounts beyond the end of +# the file as leaked clusters +_check_test_img 2>&1 | sed '/^Leaked cluster/d' +stat -c 'image size %s' "$TEST_IMG" + +echo "== Trying to write compressed cluster ==" +# Given our file size, the next available cluster at 512T lies beyond the +# maximum offset that a compressed 2M cluster can reside in +$QEMU_IO_PROG -c 'w -c 0 2m' "$TEST_IMG" | _filter_qemu_io +# The attempt failed, but ended up allocating a new refblock +stat -c 'image size %s' "$TEST_IMG" + +echo "== Writing normal cluster ==" +# The failed write should not corrupt the image, so a normal write succeeds +$QEMU_IO_PROG -c 'w 0 2m' "$TEST_IMG" | _filter_qemu_io + +echo "== Checking file after ==" +# qemu-img now sees the millions of leaked clusters, thanks to the allocations +# at 512T. Undo many of our faked references to speed up the check. +$QEMU_IO_PROG -f raw -c "w -z 5m 1m" -c "w -z 8m 30m" "$TEST_IMG" | + _filter_qemu_io +_check_test_img 2>&1 | sed '/^Leaked cluster/d' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/220.out b/tests/qemu-iotests/220.out new file mode 100644 index 0000000000..33b994b8a1 --- /dev/null +++ b/tests/qemu-iotests/220.out @@ -0,0 +1,54 @@ +QA output created by 220 +== Creating huge file == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=564049465049088 +== Populating refcounts == +wrote 2097152/2097152 bytes at offset 4194304 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 8388608 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 10485760 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 12582912 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 14680064 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 16777216 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 18874368 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 20971520 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 23068672 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 25165824 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 27262976 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 29360128 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 31457280 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 33554432 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 35651584 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 2097152/2097152 bytes at offset 37748736 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== Checking file before == +No errors were found on the image. +image size 39845888 +== Trying to write compressed cluster == +write failed: File too large +image size 562949957615616 +== Writing normal cluster == +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +== Checking file after == +wrote 1048576/1048576 bytes at offset 5242880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 31457280/31457280 bytes at offset 8388608 +30 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +8388589 leaked clusters were found on the image. +This means waste of disk space, but no harm to data. +*** done diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 index 41c4e4bdf8..808cd9a289 100755 --- a/tests/qemu-iotests/221 +++ b/tests/qemu-iotests/221 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test qemu-img vs. unaligned images # @@ -21,7 +21,6 @@ seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/222.out b/tests/qemu-iotests/222.out index 48f336a02b..16643dde30 100644 --- a/tests/qemu-iotests/222.out +++ b/tests/qemu-iotests/222.out @@ -8,13 +8,13 @@ Done --- Setting up Fleecing Graph --- -{u'return': {}} -{u'return': {}} +{"return": {}} +{"return": {}} --- Setting up NBD Export --- -{u'return': {}} -{u'return': {}} +{"return": {}} +{"return": {}} --- Sanity Check --- @@ -29,13 +29,13 @@ read -P0 0x3fe0000 64k --- Testing COW --- write -P0xab 0 64k -{u'return': u''} +{"return": ""} write -P0xad 0x00f8000 64k -{u'return': u''} +{"return": ""} write -P0x1d 0x2008000 64k -{u'return': u''} +{"return": ""} write -P0xea 0x3fe0000 64k -{u'return': u''} +{"return": ""} --- Verifying Data --- @@ -49,10 +49,10 @@ read -P0 0x3fe0000 64k --- Cleanup --- -{u'return': {}} -{u'timestamp': {u'seconds': 'SECS', u'microseconds': 'USECS'}, u'data': {u'device': u'drive0', u'type': u'backup', u'speed': 0, u'len': 67108864, u'offset': 393216}, u'event': u'BLOCK_JOB_CANCELLED'} -{u'return': {}} -{u'return': {}} +{"return": {}} +{"data": {"device": "drive0", "len": 67108864, "offset": 393216, "speed": 0, "type": "backup"}, "event": "BLOCK_JOB_CANCELLED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"return": {}} +{"return": {}} --- Confirming writes --- diff --git a/tests/qemu-iotests/223 b/tests/qemu-iotests/223 index 8b1859c2dd..cc48e78ea7 100755 --- a/tests/qemu-iotests/223 +++ b/tests/qemu-iotests/223 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test reading dirty bitmap over NBD # @@ -21,11 +21,11 @@ seq="$(basename $0)" echo "QA output created by $seq" -here="$PWD" status=1 # failure is the default! _cleanup() { + nbd_server_stop _cleanup_test_img _cleanup_qemu rm -f "$TEST_DIR/nbd" @@ -36,6 +36,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 . ./common.rc . ./common.filter . ./common.qemu +. ./common.nbd _supported_fmt qcow2 _supported_proto file # uses NBD as well @@ -43,14 +44,14 @@ _supported_os Linux # Persistent dirty bitmaps require compat=1.1 _unsupported_imgopts 'compat=0.10' -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" $QEMU -nographic -qmp stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ | _filter_qemu | _filter_imgfmt \ @@ -58,10 +59,13 @@ function run_qemu() } echo -echo "=== Create partially sparse image, then add dirty bitmap ===" +echo "=== Create partially sparse image, then add dirty bitmaps ===" echo -_make_test_img 4M +# Two bitmaps, to contrast granularity issues +# Also note that b will be disabled, while b2 is left enabled, to +# check for read-only interactions +_make_test_img -o cluster_size=4k 4M $QEMU_IO -c 'w -P 0x11 1M 2M' "$TEST_IMG" | _filter_qemu_io run_qemu < >(_filter_nbd) +# Intentionally provoke some errors as well, to check error handling silent= _send_qemu_cmd $QEMU_HANDLE '{"execute":"qmp_capabilities"}' "return" _send_qemu_cmd $QEMU_HANDLE '{"execute":"blockdev-add", "arguments":{"driver":"qcow2", "node-name":"n", "file":{"driver":"file", "filename":"'"$TEST_IMG"'"}}}' "return" -_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-block-dirty-bitmap-disable", +_send_qemu_cmd $QEMU_HANDLE '{"execute":"block-dirty-bitmap-disable", "arguments":{"node":"n", "name":"b"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "error" # Attempt add without server _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-start", + "arguments":{"addr":{"type":"unix", + "data":{"path":"'"$TEST_DIR/nbd"1'"}}}}' "error" # Attempt second server +$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd" _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", - "arguments":{"device":"n"}}' "return" -_send_qemu_cmd $QEMU_HANDLE '{"execute":"x-nbd-server-add-bitmap", - "arguments":{"name":"n", "bitmap":"b"}}' "return" + "arguments":{"device":"n", "bitmap":"b"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"nosuch"}}' "error" # Attempt to export missing node +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n"}}' "error" # Attempt to export same name twice +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2", + "bitmap":"b2"}}' "error" # enabled vs. read-only +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2", + "bitmap":"b3"}}' "error" # Missing bitmap +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-add", + "arguments":{"device":"n", "name":"n2", "writable":true, + "bitmap":"b2"}}' "return" +$QEMU_NBD_PROG -L -k "$TEST_DIR/nbd" echo -echo "=== Contrast normal status with dirty-bitmap status ===" +echo "=== Contrast normal status to large granularity dirty-bitmap ===" echo QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT IMG="driver=nbd,export=n,server.type=unix,server.path=$TEST_DIR/nbd" -$QEMU_IO -r -c 'r -P 0 0 1m' -c 'r -P 0x11 1m 1m' \ - -c 'r -P 0x22 2m 2m' --image-opts "$IMG" | _filter_qemu_io +$QEMU_IO -r -c 'r -P 0x22 512 512' -c 'r -P 0 512k 512k' -c 'r -P 0x11 1m 1m' \ + -c 'r -P 0x33 2m 2m' --image-opts "$IMG" | _filter_qemu_io $QEMU_IMG map --output=json --image-opts \ "$IMG" | _filter_qemu_img_map $QEMU_IMG map --output=json --image-opts \ "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map echo -echo "=== End NBD server ===" +echo "=== Contrast to small granularity dirty-bitmap ===" +echo + +IMG="driver=nbd,export=n2,server.type=unix,server.path=$TEST_DIR/nbd" +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map + +echo +echo "=== End qemu NBD server ===" echo _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", "arguments":{"name":"n"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-remove", + "arguments":{"name":"n2"}}' "error" # Attempt duplicate clean _send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "return" +_send_qemu_cmd $QEMU_HANDLE '{"execute":"nbd-server-stop"}' "error" # Again _send_qemu_cmd $QEMU_HANDLE '{"execute":"quit"}' "return" +wait=yes _cleanup_qemu + +echo +echo "=== Use qemu-nbd as server ===" +echo + +nbd_server_start_unix_socket -r -f $IMGFMT -B b "$TEST_IMG" +IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket" +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b" | _filter_qemu_img_map + +nbd_server_start_unix_socket -f $IMGFMT -B b2 "$TEST_IMG" +IMG="driver=nbd,server.type=unix,server.path=$nbd_unix_socket" +$QEMU_IMG map --output=json --image-opts \ + "$IMG,x-dirty-bitmap=qemu:dirty-bitmap:b2" | _filter_qemu_img_map # success, all done echo '*** done' diff --git a/tests/qemu-iotests/223.out b/tests/qemu-iotests/223.out index 33021c8e6a..d5201b2356 100644 --- a/tests/qemu-iotests/223.out +++ b/tests/qemu-iotests/223.out @@ -1,6 +1,6 @@ QA output created by 223 -=== Create partially sparse image, then add dirty bitmap === +=== Create partially sparse image, then add dirty bitmaps === Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=4194304 wrote 2097152/2097152 bytes at offset 1048576 @@ -11,39 +11,93 @@ QMP_VERSION {"return": {}} {"return": {}} {"return": {}} -{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} === Write part of the file under active bitmap === +wrote 512/512 bytes at offset 512 +512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) wrote 2097152/2097152 bytes at offset 2097152 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -=== End dirty bitmap, and start serving image over NBD === +=== End dirty bitmaps, and start serving image over NBD === {"return": {}} {"return": {}} {"return": {}} +{"error": {"class": "GenericError", "desc": "NBD server not running"}} {"return": {}} +{"error": {"class": "GenericError", "desc": "NBD server already running"}} +exports available: 0 {"return": {}} +{"error": {"class": "GenericError", "desc": "Cannot find device=nosuch nor node_name=nosuch"}} +{"error": {"class": "GenericError", "desc": "NBD server already has export named 'n'"}} +{"error": {"class": "GenericError", "desc": "Enabled bitmap 'b2' incompatible with readonly export"}} +{"error": {"class": "GenericError", "desc": "Bitmap 'b3' is not found"}} {"return": {}} +exports available: 2 + export: 'n' + size: 4194304 + flags: 0x4ef ( readonly flush fua trim zeroes df cache ) + min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 2 + base:allocation + qemu:dirty-bitmap:b + export: 'n2' + size: 4194304 + flags: 0x4ed ( flush fua trim zeroes df cache ) + min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 2 + base:allocation + qemu:dirty-bitmap:b2 -=== Contrast normal status with dirty-bitmap status === +=== Contrast normal status to large granularity dirty-bitmap === -read 1048576/1048576 bytes at offset 0 -1 MiB, 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 524288/524288 bytes at offset 524288 +512 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 1048576/1048576 bytes at offset 1048576 1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) read 2097152/2097152 bytes at offset 2097152 2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, -{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true}] -[{ "start": 0, "length": 2097152, "depth": 0, "zero": false, "data": true}, +[{ "start": 0, "length": 4096, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 4096, "length": 1044480, "depth": 0, "zero": true, "data": false, "offset": OFFSET}, +{ "start": 1048576, "length": 3145728, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] +[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, +{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, { "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] -=== End NBD server === +=== Contrast to small granularity dirty-bitmap === + +[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, +{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] + +=== End qemu NBD server === {"return": {}} {"return": {}} +{"error": {"class": "GenericError", "desc": "Export 'n2' is not found"}} {"return": {}} +{"error": {"class": "GenericError", "desc": "NBD server not running"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN", "data": {"guest": false, "reason": "host-qmp-quit"}} + +=== Use qemu-nbd as server === + +[{ "start": 0, "length": 65536, "depth": 0, "zero": false, "data": false}, +{ "start": 65536, "length": 2031616, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] +[{ "start": 0, "length": 512, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 512, "length": 512, "depth": 0, "zero": false, "data": false}, +{ "start": 1024, "length": 2096128, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": false, "data": false}] *** done diff --git a/tests/qemu-iotests/224 b/tests/qemu-iotests/224 new file mode 100755 index 0000000000..b4dfaa639f --- /dev/null +++ b/tests/qemu-iotests/224 @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# Test json:{} filenames with qemu-internal BDSs +# (the one of commit, to be precise) +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Creator/Owner: Max Reitz + +import iotests +from iotests import log, qemu_img, qemu_io_silent, filter_qmp_testfiles, \ + filter_qmp_imgfmt +import json + +# Need backing file support (for arbitrary backing formats) +iotests.verify_image_format(supported_fmts=['qcow2', 'qcow', 'qed']) +iotests.verify_platform(['linux']) + + +# There are two variations of this test: +# (1) We do not set filter_node_name. In that case, the commit_top +# driver should not appear anywhere. +# (2) We do set filter_node_name. In that case, it should appear. +# +# This for loop executes both. +for filter_node_name in False, True: + log('') + log('--- filter_node_name: %s ---' % filter_node_name) + log('') + + with iotests.FilePath('base.img') as base_img_path, \ + iotests.FilePath('mid.img') as mid_img_path, \ + iotests.FilePath('top.img') as top_img_path, \ + iotests.VM() as vm: + + assert qemu_img('create', '-f', iotests.imgfmt, + base_img_path, '64M') == 0 + assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + mid_img_path) == 0 + assert qemu_img('create', '-f', iotests.imgfmt, '-b', mid_img_path, + top_img_path) == 0 + + # Something to commit + assert qemu_io_silent(mid_img_path, '-c', 'write -P 1 0 1M') == 0 + + vm.launch() + + # Change the bottom-most image's backing file (to null-co://) + # to enforce json:{} filenames + vm.qmp_log('blockdev-add', + node_name='top', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': top_img_path + }, + backing={ + 'node-name': 'mid', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': mid_img_path + }, + 'backing': { + 'node-name': 'base', + 'driver': iotests.imgfmt, + 'file': { + 'driver': 'file', + 'filename': base_img_path + }, + 'backing': { + 'driver': 'null-co' + } + } + }, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + # As long as block-commit does not accept node names, we have to + # get our mid/base filenames here + mid_name = vm.node_info('mid')['image']['filename'] + base_name = vm.node_info('base')['image']['filename'] + + assert mid_name[:5] == 'json:' + assert base_name[:5] == 'json:' + + # Start the block job + if filter_node_name: + vm.qmp_log('block-commit', + job_id='commit', + device='top', + filter_node_name='filter_node', + top=mid_name, + base=base_name, + speed=1, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + else: + vm.qmp_log('block-commit', + job_id='commit', + device='top', + top=mid_name, + base=base_name, + speed=1, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + vm.qmp_log('job-pause', id='commit') + + # Get and parse top's json:{} filename + top_name = vm.node_info('top')['image']['filename'] + + vm.shutdown() + + assert top_name[:5] == 'json:' + top_options = json.loads(top_name[5:]) + + if filter_node_name: + # This should be present and set + assert top_options['backing']['driver'] == 'commit_top' + # And the mid image is commit_top's backing image + mid_options = top_options['backing']['backing'] + else: + # The mid image should appear as the immediate backing BDS + # of top + mid_options = top_options['backing'] + + assert mid_options['driver'] == iotests.imgfmt + assert mid_options['file']['filename'] == mid_img_path diff --git a/tests/qemu-iotests/224.out b/tests/qemu-iotests/224.out new file mode 100644 index 0000000000..23374a1d29 --- /dev/null +++ b/tests/qemu-iotests/224.out @@ -0,0 +1,18 @@ + +--- filter_node_name: False --- + +{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "base"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-mid.img"}, "node-name": "mid"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "top"}} +{"return": {}} +{"execute": "block-commit", "arguments": {"base": "json:{\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}", "device": "top", "job-id": "commit", "speed": 1, "top": "json:{\"backing\": {\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-mid.img\"}}"}} +{"return": {}} +{"execute": "job-pause", "arguments": {"id": "commit"}} +{"return": {}} + +--- filter_node_name: True --- + +{"execute": "blockdev-add", "arguments": {"backing": {"backing": {"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "base"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-mid.img"}, "node-name": "mid"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "top"}} +{"return": {}} +{"execute": "block-commit", "arguments": {"base": "json:{\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}", "device": "top", "filter-node-name": "filter_node", "job-id": "commit", "speed": 1, "top": "json:{\"backing\": {\"backing\": {\"driver\": \"null-co\"}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-base.img\"}}, \"driver\": \"IMGFMT\", \"file\": {\"driver\": \"file\", \"filename\": \"TEST_DIR/PID-mid.img\"}}"}} +{"return": {}} +{"execute": "job-pause", "arguments": {"id": "commit"}} +{"return": {}} diff --git a/tests/qemu-iotests/225 b/tests/qemu-iotests/225 index f2ee715685..fbd7404791 100755 --- a/tests/qemu-iotests/225 +++ b/tests/qemu-iotests/225 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test vmdk backing file correlation # @@ -24,7 +24,6 @@ owner=mreitz@redhat.com seq=$(basename $0) echo "QA output created by $seq" -here=$PWD status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/226 b/tests/qemu-iotests/226 index 8ec3e612dd..c1e1fb2b1c 100755 --- a/tests/qemu-iotests/226 +++ b/tests/qemu-iotests/226 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # This test covers expected filetypes for the file, host_cdrom and # host_device drivers. @@ -25,7 +25,6 @@ owner=jsnow@redhat.com seq=`basename $0` echo "QA output created by $seq" -here=`pwd` status=1 # failure is the default! _cleanup() diff --git a/tests/qemu-iotests/227 b/tests/qemu-iotests/227 index 9a5f7f9f14..10cf144eb0 100755 --- a/tests/qemu-iotests/227 +++ b/tests/qemu-iotests/227 @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Test query-blockstats with different ways to create a BB # @@ -24,7 +24,6 @@ owner=kwolf@redhat.com seq=$(basename $0) echo "QA output created by $seq" -here=$PWD status=1 # failure is the default! _cleanup() @@ -41,14 +40,14 @@ _supported_fmt generic _supported_proto file _supported_os Linux -function do_run_qemu() +do_run_qemu() { echo Testing: "$@" $QEMU -nographic -qmp-pretty stdio -serial none "$@" echo } -function run_qemu() +run_qemu() { do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qmp \ | _filter_qemu | _filter_imgfmt \ diff --git a/tests/qemu-iotests/227.out b/tests/qemu-iotests/227.out index 736f2e3b11..e77efaf4cf 100644 --- a/tests/qemu-iotests/227.out +++ b/tests/qemu-iotests/227.out @@ -53,7 +53,8 @@ Testing: -drive driver=null-co,if=virtio }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -110,7 +111,8 @@ Testing: -drive driver=null-co,if=none }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -140,7 +142,8 @@ Testing: -blockdev driver=null-co,node-name=null }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } @@ -198,7 +201,8 @@ Testing: -blockdev driver=null-co,node-name=null -device virtio-blk,drive=null,i }, "event": "SHUTDOWN", "data": { - "guest": false + "guest": false, + "reason": "host-qmp-quit" } } diff --git a/tests/qemu-iotests/228 b/tests/qemu-iotests/228 new file mode 100755 index 0000000000..9a50afd205 --- /dev/null +++ b/tests/qemu-iotests/228 @@ -0,0 +1,239 @@ +#!/usr/bin/env python +# +# Test for when a backing file is considered overridden (thus, a +# json:{} filename is generated for the overlay) and when it is not +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Creator/Owner: Max Reitz + +import iotests +from iotests import log, qemu_img, filter_testfiles, filter_imgfmt, \ + filter_qmp_testfiles, filter_qmp_imgfmt + +# Need backing file and change-backing-file support +iotests.verify_image_format(supported_fmts=['qcow2', 'qed']) +iotests.verify_platform(['linux']) + + +def log_node_info(node): + log('') + + log('bs->filename: ' + node['image']['filename'], + filters=[filter_testfiles, filter_imgfmt]) + log('bs->backing_file: ' + node['backing_file'], + filters=[filter_testfiles, filter_imgfmt]) + + if 'backing-image' in node['image']: + log('bs->backing->bs->filename: ' + + node['image']['backing-image']['filename'], + filters=[filter_testfiles, filter_imgfmt]) + else: + log('bs->backing: (none)') + + log('') + + +with iotests.FilePath('base.img') as base_img_path, \ + iotests.FilePath('top.img') as top_img_path, \ + iotests.VM() as vm: + + assert qemu_img('create', '-f', iotests.imgfmt, base_img_path, '64M') == 0 + # Choose a funny way to describe the backing filename + assert qemu_img('create', '-f', iotests.imgfmt, '-b', + 'file:' + base_img_path, top_img_path) == 0 + + vm.launch() + + log('--- Implicit backing file ---') + log('') + + vm.qmp_log('blockdev-add', + node_name='node0', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': top_img_path + }, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + # Filename should be plain, and the backing filename should not + # contain the "file:" prefix + log_node_info(vm.node_info('node0')) + + vm.qmp_log('blockdev-del', node_name='node0') + + log('') + log('--- change-backing-file ---') + log('') + + vm.qmp_log('blockdev-add', + node_name='node0', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': top_img_path + }, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + # Changing the backing file to a qemu-reported filename should + # result in qemu accepting the corresponding BDS as the implicit + # backing BDS (and thus not generate a json:{} filename). + # So, first, query the backing filename. + + backing_filename = \ + vm.node_info('node0')['image']['backing-image']['filename'] + + # Next, change the backing file to something different + + vm.qmp_log('change-backing-file', + image_node_name='node0', + device='node0', + backing_file='null-co://', + filters=[filter_qmp_testfiles]) + + # Now, verify that we get a json:{} filename + # (Image header says "null-co://", actual backing file still is + # base_img_path) + + log_node_info(vm.node_info('node0')) + + # Change it back + # (To get header and backing file in sync) + + vm.qmp_log('change-backing-file', + image_node_name='node0', + device='node0', + backing_file=backing_filename, + filters=[filter_qmp_testfiles]) + + # And verify that we get our original results + + log_node_info(vm.node_info('node0')) + + # Finally, try a "file:" prefix. While this is actually what we + # originally had in the image header, qemu will not reopen the + # backing file here, so it cannot verify that this filename + # "resolves" to the actual backing BDS's filename and will thus + # consider both to be different. + # (This may be fixed in the future.) + + vm.qmp_log('change-backing-file', + image_node_name='node0', + device='node0', + backing_file=('file:' + backing_filename), + filters=[filter_qmp_testfiles]) + + # So now we should get a json:{} filename + + log_node_info(vm.node_info('node0')) + + # Remove and re-attach so we can see that (as in our first try), + # opening the image anew helps qemu resolve the header backing + # filename. + + vm.qmp_log('blockdev-del', node_name='node0') + + vm.qmp_log('blockdev-add', + node_name='node0', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': top_img_path + }, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + log_node_info(vm.node_info('node0')) + + vm.qmp_log('blockdev-del', node_name='node0') + + log('') + log('--- Override backing file ---') + log('') + + # For this test, we need the plain filename in the image header + # (because qemu cannot "canonicalize"/"resolve" the backing + # filename unless the backing file is opened implicitly with the + # overlay) + assert qemu_img('create', '-f', iotests.imgfmt, '-b', base_img_path, + top_img_path) == 0 + + # You can only reliably override backing options by using a node + # reference (or by specifying file.filename, but, well...) + vm.qmp_log('blockdev-add', node_name='null', driver='null-co') + + vm.qmp_log('blockdev-add', + node_name='node0', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': top_img_path + }, + backing='null', + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + # Should get a json:{} filename (and bs->backing_file is + # null-co://, because that field actually has not much to do + # with the header backing filename (except that it is changed by + # change-backing-file)) + + log_node_info(vm.node_info('node0')) + + # Detach the backing file by reopening the whole thing + + vm.qmp_log('blockdev-del', node_name='node0') + vm.qmp_log('blockdev-del', node_name='null') + + vm.qmp_log('blockdev-add', + node_name='node0', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': top_img_path + }, + backing=None, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + # Should get a json:{} filename (because we overrode the backing + # file to not be there) + + log_node_info(vm.node_info('node0')) + + # Open the original backing file + + vm.qmp_log('blockdev-add', + node_name='original-backing', + driver=iotests.imgfmt, + file={ + 'driver': 'file', + 'filename': base_img_path + }, + filters=[filter_qmp_testfiles, filter_qmp_imgfmt]) + + # Attach the original backing file to its overlay + + vm.qmp_log('blockdev-snapshot', + node='original-backing', + overlay='node0') + + # This should give us the original plain result + + log_node_info(vm.node_info('node0')) + + vm.qmp_log('blockdev-del', node_name='node0') + vm.qmp_log('blockdev-del', node_name='original-backing') + + vm.shutdown() diff --git a/tests/qemu-iotests/228.out b/tests/qemu-iotests/228.out new file mode 100644 index 0000000000..4217df24fe --- /dev/null +++ b/tests/qemu-iotests/228.out @@ -0,0 +1,84 @@ +--- Implicit backing file --- + +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}} +{"return": {}} + +bs->filename: TEST_DIR/PID-top.img +bs->backing_file: TEST_DIR/PID-base.img +bs->backing->bs->filename: TEST_DIR/PID-base.img + +{"execute": "blockdev-del", "arguments": {"node-name": "node0"}} +{"return": {}} + +--- change-backing-file --- + +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}} +{"return": {}} +{"execute": "change-backing-file", "arguments": {"backing-file": "null-co://", "device": "node0", "image-node-name": "node0"}} +{"return": {}} + +bs->filename: json:{"backing": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}} +bs->backing_file: null-co:// +bs->backing->bs->filename: TEST_DIR/PID-base.img + +{"execute": "change-backing-file", "arguments": {"backing-file": "TEST_DIR/PID-base.img", "device": "node0", "image-node-name": "node0"}} +{"return": {}} + +bs->filename: TEST_DIR/PID-top.img +bs->backing_file: TEST_DIR/PID-base.img +bs->backing->bs->filename: TEST_DIR/PID-base.img + +{"execute": "change-backing-file", "arguments": {"backing-file": "file:TEST_DIR/PID-base.img", "device": "node0", "image-node-name": "node0"}} +{"return": {}} + +bs->filename: json:{"backing": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}} +bs->backing_file: file:TEST_DIR/PID-base.img +bs->backing->bs->filename: TEST_DIR/PID-base.img + +{"execute": "blockdev-del", "arguments": {"node-name": "node0"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}} +{"return": {}} + +bs->filename: TEST_DIR/PID-top.img +bs->backing_file: TEST_DIR/PID-base.img +bs->backing->bs->filename: TEST_DIR/PID-base.img + +{"execute": "blockdev-del", "arguments": {"node-name": "node0"}} +{"return": {}} + +--- Override backing file --- + +{"execute": "blockdev-add", "arguments": {"driver": "null-co", "node-name": "null"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": {"backing": "null", "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}} +{"return": {}} + +bs->filename: json:{"backing": {"driver": "null-co"}, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}} +bs->backing_file: null-co:// +bs->backing->bs->filename: null-co:// + +{"execute": "blockdev-del", "arguments": {"node-name": "node0"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "null"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": {"backing": null, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}, "node-name": "node0"}} +{"return": {}} + +bs->filename: json:{"backing": null, "driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-top.img"}} +bs->backing_file: TEST_DIR/PID-base.img +bs->backing: (none) + +{"execute": "blockdev-add", "arguments": {"driver": "IMGFMT", "file": {"driver": "file", "filename": "TEST_DIR/PID-base.img"}, "node-name": "original-backing"}} +{"return": {}} +{"execute": "blockdev-snapshot", "arguments": {"node": "original-backing", "overlay": "node0"}} +{"return": {}} + +bs->filename: TEST_DIR/PID-top.img +bs->backing_file: TEST_DIR/PID-base.img +bs->backing->bs->filename: TEST_DIR/PID-base.img + +{"execute": "blockdev-del", "arguments": {"node-name": "node0"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": {"node-name": "original-backing"}} +{"return": {}} diff --git a/tests/qemu-iotests/229 b/tests/qemu-iotests/229 new file mode 100755 index 0000000000..e18a464fe0 --- /dev/null +++ b/tests/qemu-iotests/229 @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +# +# Test for force canceling a running blockjob that is paused in +# an error state. +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=jcody@redhat.com + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _cleanup_test_img + rm -f "$TEST_IMG" "$DEST_IMG" +} +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 + + +DEST_IMG="$TEST_DIR/d.$IMGFMT" +TEST_IMG="$TEST_DIR/b.$IMGFMT" + +_make_test_img 2M + +# destination for mirror will be too small, causing error +TEST_IMG=$DEST_IMG _make_test_img 1M + +$QEMU_IO -c 'write 0 2M' "$TEST_IMG" | _filter_qemu_io + +_launch_qemu -drive id=testdisk,file="$TEST_IMG",format="$IMGFMT" + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'qmp_capabilities'}" \ + 'return' + +echo +echo '=== Starting drive-mirror, causing error & stop ===' +echo + +_send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'drive-mirror', + 'arguments': {'device': 'testdisk', + 'format': '$IMGFMT', + 'target': '$DEST_IMG', + 'sync': 'full', + 'mode': 'existing', + 'on-source-error': 'stop', + 'on-target-error': 'stop' }}" \ + "JOB_STATUS_CHANGE.*pause" + +echo +echo '=== Force cancel job paused in error state ===' +echo + +# Filter out BLOCK_JOB_ERROR events because they may or may not occur. +# Cancelling the job means resuming it for a bit before it is actually +# aborted, and in that time it may or may not re-encounter the error. +success_or_failure="y" _send_qemu_cmd $QEMU_HANDLE \ + "{'execute': 'block-job-cancel', + 'arguments': { 'device': 'testdisk', + 'force': true}}" \ + "BLOCK_JOB_CANCELLED" "Assertion" \ + | grep -v '"BLOCK_JOB_ERROR"' + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/229.out b/tests/qemu-iotests/229.out new file mode 100644 index 0000000000..a3eb33788a --- /dev/null +++ b/tests/qemu-iotests/229.out @@ -0,0 +1,22 @@ +QA output created by 229 +Formatting 'TEST_DIR/b.IMGFMT', fmt=IMGFMT size=2097152 +Formatting 'TEST_DIR/d.IMGFMT', fmt=IMGFMT size=1048576 +wrote 2097152/2097152 bytes at offset 0 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"return": {}} + +=== Starting drive-mirror, causing error & stop === + +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "testdisk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_ERROR", "data": {"device": "testdisk", "operation": "write", "action": "stop"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "paused", "id": "testdisk"}} + +=== Force cancel job paused in error state === + +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "testdisk"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "aborting", "id": "testdisk"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_CANCELLED", "data": {"device": "testdisk", "len": 2097152, "offset": 1048576, "speed": 0, "type": "mirror"}} +*** done diff --git a/tests/qemu-iotests/231 b/tests/qemu-iotests/231 new file mode 100755 index 0000000000..5b2cbab9ac --- /dev/null +++ b/tests/qemu-iotests/231 @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# +# Test legacy and modern option parsing for rbd/ceph. This will not +# actually connect to a ceph server, but rather looks for the appropriate +# error message that indicates we parsed the options correctly. +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=jcody@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + rm "${BOGUS_CONF}" +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto rbd +_supported_os Linux + +BOGUS_CONF=${TEST_DIR}/ceph-$$.conf +touch "${BOGUS_CONF}" + +_filter_conf() +{ + sed -e "s#$BOGUS_CONF#BOGUS_CONF#g" +} + +# We expect this to fail, with no monitor ip provided and a null conf file. Just want it +# to fail in the right way. +$QEMU_IMG info "json:{'file.driver':'rbd','file.filename':'rbd:rbd/bogus:conf=${BOGUS_CONF}'}" 2>&1 | _filter_conf +$QEMU_IMG info "json:{'file.driver':'rbd','file.pool':'rbd','file.image':'bogus','file.conf':'${BOGUS_CONF}'}" 2>&1 | _filter_conf + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/231.out b/tests/qemu-iotests/231.out new file mode 100644 index 0000000000..579ba11c16 --- /dev/null +++ b/tests/qemu-iotests/231.out @@ -0,0 +1,9 @@ +QA output created by 231 +qemu-img: RBD options encoded in the filename as keyvalue pairs is deprecated. Future versions may cease to parse these options in the future. +unable to get monitor info from DNS SRV with service name: ceph-mon +no monitors specified to connect to. +qemu-img: Could not open 'json:{'file.driver':'rbd','file.filename':'rbd:rbd/bogus:conf=BOGUS_CONF'}': error connecting: No such file or directory +unable to get monitor info from DNS SRV with service name: ceph-mon +no monitors specified to connect to. +qemu-img: Could not open 'json:{'file.driver':'rbd','file.pool':'rbd','file.image':'bogus','file.conf':'BOGUS_CONF'}': error connecting: No such file or directory +*** done diff --git a/tests/qemu-iotests/232 b/tests/qemu-iotests/232 new file mode 100755 index 0000000000..2063f78876 --- /dev/null +++ b/tests/qemu-iotests/232 @@ -0,0 +1,150 @@ +#!/usr/bin/env bash +# +# Test for auto-read-only +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +# creator +owner=kwolf@redhat.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f $TEST_IMG.[01234] +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto file +_supported_os Linux + +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 +} + +run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_testdir | _filter_qemu | _filter_hmp | + _filter_generated_node_ids | _filter_imgfmt +} + +run_qemu_info_block() +{ + echo "info block -n" | run_qemu "$@" | grep -e "(file" -e "QEMU_PROG" +} + +size=128M + +_make_test_img $size + +if [ -n "$TEST_IMG_FILE" ]; then + TEST_IMG=$TEST_IMG_FILE +fi + +echo +echo "=== -drive with read-write image: read-only/auto-read-only combinations ===" +echo + +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=off +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=on +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on +echo +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=off +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=on +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off +echo +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=off +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=on +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none + +echo +echo "=== -drive with read-only image: read-only/auto-read-only combinations ===" +echo + +chmod a-w $TEST_IMG + +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=off +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on,auto-read-only=on +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=on +echo +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=off +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off,auto-read-only=on +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,read-only=off +echo +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=off +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none,auto-read-only=on +run_qemu_info_block -drive driver=file,file="$TEST_IMG",if=none + +echo +echo "=== -blockdev with read-write image: read-only/auto-read-only combinations ===" +echo + +chmod a+w $TEST_IMG + +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=off +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=on +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on +echo +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=off +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=on +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off +echo +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=off +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0 + +echo +echo "=== -blockdev with read-only image: read-only/auto-read-only combinations ===" +echo + +chmod a-w $TEST_IMG + +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=off +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on,auto-read-only=on +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=on +echo +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=off +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off,auto-read-only=on +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,read-only=off +echo +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=off +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0,auto-read-only=on +run_qemu_info_block -blockdev driver=file,filename="$TEST_IMG",node-name=node0 + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/232.out b/tests/qemu-iotests/232.out new file mode 100644 index 0000000000..3bd1a920af --- /dev/null +++ b/tests/qemu-iotests/232.out @@ -0,0 +1,59 @@ +QA output created by 232 +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 + +=== -drive with read-write image: read-only/auto-read-only combinations === + +NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) +NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) +NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) + +NODE_NAME: TEST_DIR/t.IMGFMT (file) +NODE_NAME: TEST_DIR/t.IMGFMT (file) +NODE_NAME: TEST_DIR/t.IMGFMT (file) + +NODE_NAME: TEST_DIR/t.IMGFMT (file) +NODE_NAME: TEST_DIR/t.IMGFMT (file) +NODE_NAME: TEST_DIR/t.IMGFMT (file) + +=== -drive with read-only image: read-only/auto-read-only combinations === + +NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) +NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) +NODE_NAME: TEST_DIR/t.IMGFMT (file, read-only) + +QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +NODE_NAME: TEST_DIR/t.IMGFMT (file) +NODE_NAME: TEST_DIR/t.IMGFMT (file) + +QEMU_PROG: -drive driver=file,file=TEST_DIR/t.IMGFMT,if=none,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +NODE_NAME: TEST_DIR/t.IMGFMT (file) +NODE_NAME: TEST_DIR/t.IMGFMT (file) + +=== -blockdev with read-write image: read-only/auto-read-only combinations === + +node0: TEST_DIR/t.IMGFMT (file, read-only) +node0: TEST_DIR/t.IMGFMT (file, read-only) +node0: TEST_DIR/t.IMGFMT (file, read-only) + +node0: TEST_DIR/t.IMGFMT (file) +node0: TEST_DIR/t.IMGFMT (file) +node0: TEST_DIR/t.IMGFMT (file) + +node0: TEST_DIR/t.IMGFMT (file) +node0: TEST_DIR/t.IMGFMT (file) +node0: TEST_DIR/t.IMGFMT (file) + +=== -blockdev with read-only image: read-only/auto-read-only combinations === + +node0: TEST_DIR/t.IMGFMT (file, read-only) +node0: TEST_DIR/t.IMGFMT (file, read-only) +node0: TEST_DIR/t.IMGFMT (file, read-only) + +QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +node0: TEST_DIR/t.IMGFMT (file) +QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied + +QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0,auto-read-only=off: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +node0: TEST_DIR/t.IMGFMT (file) +QEMU_PROG: -blockdev driver=file,filename=TEST_DIR/t.IMGFMT,node-name=node0: Could not open 'TEST_DIR/t.IMGFMT': Permission denied +*** done diff --git a/tests/qemu-iotests/233 b/tests/qemu-iotests/233 new file mode 100755 index 0000000000..b8b6c8cc4c --- /dev/null +++ b/tests/qemu-iotests/233 @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# +# Test NBD TLS certificate / authorization integration +# +# Copyright (C) 2018-2019 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=berrange@redhat.com + +seq=$(basename $0) +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + nbd_server_stop + _cleanup_test_img + # If we aborted early we want to see this log for diagnosis + test -f "$TEST_DIR/server.log" && cat "$TEST_DIR/server.log" + rm -f "$TEST_DIR/server.log" + tls_x509_cleanup +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.pattern +. ./common.tls +. ./common.nbd + +_supported_fmt raw qcow2 +_supported_proto file +# If porting to non-Linux, consider using socat instead of ss in common.nbd +_supported_os Linux +_require_command QEMU_NBD + +nbd_server_set_tcp_port +tls_x509_init + +echo +echo "== preparing TLS creds ==" + +tls_x509_create_root_ca "ca1" +tls_x509_create_root_ca "ca2" +tls_x509_create_server "ca1" "server1" +tls_x509_create_client "ca1" "client1" +tls_x509_create_client "ca2" "client2" +tls_x509_create_client "ca1" "client3" + +echo +echo "== preparing image ==" +_make_test_img 64M +$QEMU_IO -c 'w -P 0x11 1m 1m' "$TEST_IMG" | _filter_qemu_io + +echo +echo "== check TLS client to plain server fails ==" +nbd_server_start_tcp_socket -f $IMGFMT "$TEST_IMG" 2> "$TEST_DIR/server.log" + +obj=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" +$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ + --tls-creds=tls0 + +nbd_server_stop + +echo +echo "== check plain client to TLS server fails ==" + +nbd_server_start_tcp_socket \ + --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \ + --tls-creds tls0 \ + -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" + +$QEMU_IMG info nbd://localhost:$nbd_tcp_port 2>&1 | sed "s/$nbd_tcp_port/PORT/g" +$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port + +echo +echo "== check TLS works ==" +obj1=tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 +obj2=tls-creds-x509,dir=${tls_dir}/client3,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj1 \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" +$QEMU_IMG info --image-opts --object $obj2 \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" +$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj1 \ + --tls-creds=tls0 + +echo +echo "== check TLS with different CA fails ==" +obj=tls-creds-x509,dir=${tls_dir}/client2,endpoint=client,id=tls0 +$QEMU_IMG info --image-opts --object $obj \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | sed "s/$nbd_tcp_port/PORT/g" +$QEMU_NBD_PROG -L -b $nbd_tcp_addr -p $nbd_tcp_port --object $obj \ + --tls-creds=tls0 + +echo +echo "== perform I/O over TLS ==" +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT +$QEMU_IO -c 'r -P 0x11 1m 1m' -c 'w -P 0x22 1m 1m' --image-opts \ + --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 \ + 2>&1 | _filter_qemu_io + +$QEMU_IO -f $IMGFMT -r -U -c 'r -P 0x22 1m 1m' "$TEST_IMG" | _filter_qemu_io + +echo +echo "== check TLS with authorization ==" + +nbd_server_stop + +nbd_server_start_tcp_socket \ + --object tls-creds-x509,dir=${tls_dir}/server1,endpoint=server,id=tls0,verify-peer=yes \ + --object "authz-simple,id=authz0,identity=CN=localhost,, \ + O=Cthulu Dark Lord Enterprises client1,,L=R'lyeh,,C=South Pacific" \ + --tls-authz authz0 \ + --tls-creds tls0 \ + -f $IMGFMT "$TEST_IMG" 2>> "$TEST_DIR/server.log" + +$QEMU_IMG info --image-opts \ + --object tls-creds-x509,dir=${tls_dir}/client1,endpoint=client,id=tls0 \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 + +$QEMU_IMG info --image-opts \ + --object tls-creds-x509,dir=${tls_dir}/client3,endpoint=client,id=tls0 \ + driver=nbd,host=$nbd_tcp_addr,port=$nbd_tcp_port,tls-creds=tls0 + +echo +echo "== final server log ==" +cat "$TEST_DIR/server.log" +rm -f "$TEST_DIR/server.log" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/233.out b/tests/qemu-iotests/233.out new file mode 100644 index 0000000000..9511b6ea65 --- /dev/null +++ b/tests/qemu-iotests/233.out @@ -0,0 +1,68 @@ +QA output created by 233 + +== preparing TLS creds == +Generating a self signed certificate... +Generating a self signed certificate... +Generating a signed certificate... +Generating a signed certificate... +Generating a signed certificate... +Generating a signed certificate... + +== preparing image == +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== check TLS client to plain server fails == +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': Denied by server for option 5 (starttls) +server reported: TLS not configured +qemu-nbd: Denied by server for option 5 (starttls) +server reported: TLS not configured + +== check plain client to TLS server fails == +qemu-img: Could not open 'nbd://localhost:PORT': TLS negotiation required before option 8 (structured reply) +server reported: Option 0x8 not permitted before TLS +qemu-nbd: TLS negotiation required before option 8 (structured reply) +server reported: Option 0x8 not permitted before TLS + +== check TLS works == +image: nbd://127.0.0.1:PORT +file format: nbd +virtual size: 64M (67108864 bytes) +disk size: unavailable +image: nbd://127.0.0.1:PORT +file format: nbd +virtual size: 64M (67108864 bytes) +disk size: unavailable +exports available: 1 + export: '' + size: 67108864 + flags: 0x4ed ( flush fua trim zeroes df cache ) + min block: 1 + opt block: 4096 + max block: 33554432 + available meta contexts: 1 + base:allocation + +== check TLS with different CA fails == +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=PORT,tls-creds=tls0': The certificate hasn't got a known issuer +qemu-nbd: The certificate hasn't got a known issuer + +== perform I/O over TLS == +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +== check TLS with authorization == +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=10809,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort +qemu-img: Could not open 'driver=nbd,host=127.0.0.1,port=10809,tls-creds=tls0': Failed to read option reply: Cannot read from TLS channel: Software caused connection abort + +== final server log == +qemu-nbd: option negotiation failed: Verify failed: No certificate was found. +qemu-nbd: option negotiation failed: Verify failed: No certificate was found. +qemu-nbd: option negotiation failed: TLS x509 authz check for CN=localhost,O=Cthulhu Dark Lord Enterprises client1,L=R'lyeh,C=South Pacific is denied +qemu-nbd: option negotiation failed: TLS x509 authz check for CN=localhost,O=Cthulhu Dark Lord Enterprises client3,L=R'lyeh,C=South Pacific is denied +*** done diff --git a/tests/qemu-iotests/234 b/tests/qemu-iotests/234 new file mode 100755 index 0000000000..c4c26bc21e --- /dev/null +++ b/tests/qemu-iotests/234 @@ -0,0 +1,125 @@ +#!/usr/bin/env python +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Creator/Owner: Kevin Wolf +# +# Check that block node activation and inactivation works with a block graph +# that is built with individually created nodes + +import iotests +import os + +iotests.verify_image_format(supported_fmts=['qcow2']) +iotests.verify_platform(['linux']) + +def enable_migration_events(vm, name): + iotests.log('Enabling migration QMP events on %s...' % name) + iotests.log(vm.qmp('migrate-set-capabilities', capabilities=[ + { + 'capability': 'events', + 'state': True + } + ])) + +def wait_migration(vm): + while True: + event = vm.event_wait('MIGRATION') + iotests.log(event, filters=[iotests.filter_qmp_event]) + if event['data']['status'] == 'completed': + break + +with iotests.FilePath('img') as img_path, \ + iotests.FilePath('backing') as backing_path, \ + iotests.FilePath('mig_fifo_a') as fifo_a, \ + iotests.FilePath('mig_fifo_b') as fifo_b, \ + iotests.VM(path_suffix='a') as vm_a, \ + iotests.VM(path_suffix='b') as vm_b: + + iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, backing_path, '64M') + iotests.qemu_img_pipe('create', '-f', iotests.imgfmt, img_path, '64M') + + os.mkfifo(fifo_a) + os.mkfifo(fifo_b) + + iotests.log('Launching source VM...') + (vm_a.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path)) + .add_blockdev('%s,file=drive0-file,node-name=drive0' % (iotests.imgfmt)) + .add_blockdev('file,filename=%s,node-name=drive0-backing-file' % (backing_path)) + .add_blockdev('%s,file=drive0-backing-file,node-name=drive0-backing' % (iotests.imgfmt)) + .launch()) + + enable_migration_events(vm_a, 'A') + + iotests.log('Launching destination VM...') + (vm_b.add_blockdev('file,filename=%s,node-name=drive0-file' % (img_path)) + .add_blockdev('%s,file=drive0-file,node-name=drive0' % (iotests.imgfmt)) + .add_blockdev('file,filename=%s,node-name=drive0-backing-file' % (backing_path)) + .add_blockdev('%s,file=drive0-backing-file,node-name=drive0-backing' % (iotests.imgfmt)) + .add_incoming("exec: cat '%s'" % (fifo_a)) + .launch()) + + enable_migration_events(vm_b, 'B') + + # Add a child node that was created after the parent node. The reverse case + # is covered by the -blockdev options above. + iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing', + overlay='drive0')) + iotests.log(vm_b.qmp('blockdev-snapshot', node='drive0-backing', + overlay='drive0')) + + iotests.log('Starting migration to B...') + iotests.log(vm_a.qmp('migrate', uri='exec:cat >%s' % (fifo_a))) + with iotests.Timeout(3, 'Migration does not complete'): + # Wait for the source first (which includes setup=setup) + wait_migration(vm_a) + # Wait for the destination second (which does not) + wait_migration(vm_b) + + iotests.log(vm_a.qmp('query-migrate')['return']['status']) + iotests.log(vm_b.qmp('query-migrate')['return']['status']) + + iotests.log(vm_a.qmp('query-status')) + iotests.log(vm_b.qmp('query-status')) + + iotests.log('Add a second parent to drive0-file...') + iotests.log(vm_b.qmp('blockdev-add', driver='raw', file='drive0-file', + node_name='drive0-raw')) + + iotests.log('Restart A with -incoming and second parent...') + vm_a.shutdown() + (vm_a.add_blockdev('raw,file=drive0-file,node-name=drive0-raw') + .add_incoming("exec: cat '%s'" % (fifo_b)) + .launch()) + + enable_migration_events(vm_a, 'A') + + iotests.log(vm_a.qmp('blockdev-snapshot', node='drive0-backing', + overlay='drive0')) + + iotests.log('Starting migration back to A...') + iotests.log(vm_b.qmp('migrate', uri='exec:cat >%s' % (fifo_b))) + with iotests.Timeout(3, 'Migration does not complete'): + # Wait for the source first (which includes setup=setup) + wait_migration(vm_b) + # Wait for the destination second (which does not) + wait_migration(vm_a) + + iotests.log(vm_a.qmp('query-migrate')['return']['status']) + iotests.log(vm_b.qmp('query-migrate')['return']['status']) + + iotests.log(vm_a.qmp('query-status')) + iotests.log(vm_b.qmp('query-status')) diff --git a/tests/qemu-iotests/234.out b/tests/qemu-iotests/234.out new file mode 100644 index 0000000000..692976d1c6 --- /dev/null +++ b/tests/qemu-iotests/234.out @@ -0,0 +1,36 @@ +Launching source VM... +Enabling migration QMP events on A... +{"return": {}} +Launching destination VM... +Enabling migration QMP events on B... +{"return": {}} +{"return": {}} +{"return": {}} +Starting migration to B... +{"return": {}} +{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +completed +completed +{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} +{"return": {"running": true, "singlestep": false, "status": "running"}} +Add a second parent to drive0-file... +{"return": {}} +Restart A with -incoming and second parent... +Enabling migration QMP events on A... +{"return": {}} +{"return": {}} +Starting migration back to A... +{"return": {}} +{"data": {"status": "setup"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "active"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +completed +completed +{"return": {"running": true, "singlestep": false, "status": "running"}} +{"return": {"running": false, "singlestep": false, "status": "postmigrate"}} diff --git a/tests/qemu-iotests/235 b/tests/qemu-iotests/235 new file mode 100755 index 0000000000..2b6a8c13be --- /dev/null +++ b/tests/qemu-iotests/235 @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# Simple mirror test +# +# Copyright (c) 2018 Virtuozzo International GmbH. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import sys +import os +import iotests +from iotests import qemu_img_create, qemu_io, file_path, log + +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) + +from qemu import QEMUMachine + +# Note: +# This test was added to check that mirror dead-lock was fixed (see previous +# commit before this test addition). +# And it didn't reproduce if at least one of the following: +# 1. use small image size +# 2. use raw format (not qcow2) +# 3. drop kvm and use iotests.VM() (maybe, because of qtest) (however, it still +# reproduces, if just drop kvm, but gdb failed to produce full backtraces +# for me) +# 4. add iothread + +size = 1 * 1024 * 1024 * 1024 + +iotests.verify_image_format(supported_fmts=['qcow2']) + +disk = file_path('disk') + +# prepare source image +qemu_img_create('-f', iotests.imgfmt, '-o', 'preallocation=metadata', disk, + str(size)) + +vm = QEMUMachine(iotests.qemu_prog) +vm.add_args('-machine', 'accel=kvm:tcg') +if iotests.qemu_default_machine == 's390-ccw-virtio': + vm.add_args('-no-shutdown') +vm.add_args('-drive', 'id=src,file=' + disk) +vm.launch() + +log(vm.qmp('object-add', qom_type='throttle-group', id='tg0', + props={ 'x-bps-total': size })) + +log(vm.qmp('blockdev-add', + **{ 'node-name': 'target', + 'driver': 'throttle', + 'throttle-group': 'tg0', + 'file': { + 'driver': 'null-co', + 'size': size + } })) + +log(vm.qmp('blockdev-mirror', device='src', target='target', sync='full')) + +try: + vm.event_wait('BLOCK_JOB_READY', timeout=10.0) +except: + vm.shutdown() + raise + +vm.shutdown() diff --git a/tests/qemu-iotests/235.out b/tests/qemu-iotests/235.out new file mode 100644 index 0000000000..39db621e04 --- /dev/null +++ b/tests/qemu-iotests/235.out @@ -0,0 +1,3 @@ +{"return": {}} +{"return": {}} +{"return": {}} diff --git a/tests/qemu-iotests/236 b/tests/qemu-iotests/236 new file mode 100755 index 0000000000..79a6381f8e --- /dev/null +++ b/tests/qemu-iotests/236 @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# +# Test bitmap merges. +# +# Copyright (c) 2018 John Snow for 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 . +# +# owner=jsnow@redhat.com + +import iotests +from iotests import log + +iotests.verify_image_format(supported_fmts=['generic']) +size = 64 * 1024 * 1024 +granularity = 64 * 1024 + +patterns = [("0x5d", "0", "64k"), + ("0xd5", "1M", "64k"), + ("0xdc", "32M", "64k"), + ("0xcd", "0x3ff0000", "64k")] # 64M - 64K + +overwrite = [("0xab", "0", "64k"), # Full overwrite + ("0xad", "0x00f8000", "64k"), # Partial-left (1M-32K) + ("0x1d", "0x2008000", "64k"), # Partial-right (32M+32K) + ("0xea", "0x3fe0000", "64k")] # Adjacent-left (64M - 128K) + +def query_bitmaps(vm): + res = vm.qmp("query-block") + return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for + device in res['return'] } } + +with iotests.FilePath('img') as img_path, \ + iotests.VM() as vm: + + log('--- Preparing image & VM ---\n') + iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) + vm.add_drive(img_path) + vm.launch() + + log('\n--- Adding preliminary bitmaps A & B ---\n') + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="bitmapA", granularity=granularity) + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="bitmapB", granularity=granularity) + + # Dirties 4 clusters. count=262144 + log('\n--- Emulating writes ---\n') + for p in patterns: + cmd = "write -P%s %s %s" % p + log(cmd) + log(vm.hmp_qemu_io("drive0", cmd)) + + log(query_bitmaps(vm), indent=2) + + log('\n--- Submitting & Aborting Transaction ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapB" }}, + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapC", + "granularity": granularity }}, + { "type": "block-dirty-bitmap-clear", + "data": { "node": "drive0", "name": "bitmapA" }}, + { "type": "abort", "data": {}} + ]) + log(query_bitmaps(vm), indent=2) + + log('\n--- Disabling B & Adding C ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapB" }}, + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapC", + "granularity": granularity }}, + # Purely extraneous, but test that it works: + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapC" }}, + { "type": "block-dirty-bitmap-enable", + "data": { "node": "drive0", "name": "bitmapC" }}, + ]) + + log('\n--- Emulating further writes ---\n') + # Dirties 6 clusters, 3 of which are new in contrast to "A". + # A = 64 * 1024 * (4 + 3) = 458752 + # C = 64 * 1024 * 6 = 393216 + for p in overwrite: + cmd = "write -P%s %s %s" % p + log(cmd) + log(vm.hmp_qemu_io("drive0", cmd)) + + log('\n--- Disabling A & C ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapA" }}, + { "type": "block-dirty-bitmap-disable", + "data": { "node": "drive0", "name": "bitmapC" }} + ]) + + # A: 7 clusters + # B: 4 clusters + # C: 6 clusters + log(query_bitmaps(vm), indent=2) + + log('\n--- Submitting & Aborting Merge Transaction ---\n') + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapD", + "disabled": True, "granularity": granularity }}, + { "type": "block-dirty-bitmap-merge", + "data": { "node": "drive0", "target": "bitmapD", + "bitmaps": ["bitmapB", "bitmapC"] }}, + { "type": "abort", "data": {}} + ]) + log(query_bitmaps(vm), indent=2) + + log('\n--- Creating D as a merge of B & C ---\n') + # Good hygiene: create a disabled bitmap as a merge target. + vm.qmp_log("transaction", indent=2, actions=[ + { "type": "block-dirty-bitmap-add", + "data": { "node": "drive0", "name": "bitmapD", + "disabled": True, "granularity": granularity }}, + { "type": "block-dirty-bitmap-merge", + "data": { "node": "drive0", "target": "bitmapD", + "bitmaps": ["bitmapB", "bitmapC"] }} + ]) + + # A and D should now both have 7 clusters apiece. + # B and C remain unchanged with 4 and 6 respectively. + log(query_bitmaps(vm), indent=2) + + # A and D should be equivalent. + # Some formats round the size of the disk, so don't print the checksums. + check_a = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node="drive0", name="bitmapA")['return']['sha256'] + check_d = vm.qmp('x-debug-block-dirty-bitmap-sha256', + node="drive0", name="bitmapD")['return']['sha256'] + assert(check_a == check_d) + + log('\n--- Removing bitmaps A, B, C, and D ---\n') + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapA") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapB") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapC") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="bitmapD") + + log('\n--- Final Query ---\n') + log(query_bitmaps(vm), indent=2) + + log('\n--- Done ---\n') + vm.shutdown() diff --git a/tests/qemu-iotests/236.out b/tests/qemu-iotests/236.out new file mode 100644 index 0000000000..815cd053f0 --- /dev/null +++ b/tests/qemu-iotests/236.out @@ -0,0 +1,393 @@ +--- Preparing image & VM --- + + +--- Adding preliminary bitmaps A & B --- + +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapA", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 65536, "name": "bitmapB", "node": "drive0"}} +{"return": {}} + +--- Emulating writes --- + +write -P0x5d 0 64k +{"return": ""} +write -P0xd5 1M 64k +{"return": ""} +write -P0xdc 32M 64k +{"return": ""} +write -P0xcd 0x3ff0000 64k +{"return": ""} +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, + "recording": true, + "status": "active" + } + ] + } +} + +--- Submitting & Aborting Transaction --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "name": "bitmapB", + "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "granularity": 65536, + "name": "bitmapC", + "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "name": "bitmapA", + "node": "drive0" + }, + "type": "block-dirty-bitmap-clear" + }, + { + "data": {}, + "type": "abort" + } + ] + } +} +{ + "error": { + "class": "GenericError", + "desc": "Transaction aborted using Abort action" + } +} +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, + "recording": true, + "status": "active" + } + ] + } +} + +--- Disabling B & Adding C --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "name": "bitmapB", + "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "granularity": 65536, + "name": "bitmapC", + "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "name": "bitmapC", + "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "name": "bitmapC", + "node": "drive0" + }, + "type": "block-dirty-bitmap-enable" + } + ] + } +} +{ + "return": {} +} + +--- Emulating further writes --- + +write -P0xab 0 64k +{"return": ""} +write -P0xad 0x00f8000 64k +{"return": ""} +write -P0x1d 0x2008000 64k +{"return": ""} +write -P0xea 0x3fe0000 64k +{"return": ""} + +--- Disabling A & C --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "name": "bitmapA", + "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + }, + { + "data": { + "name": "bitmapC", + "node": "drive0" + }, + "type": "block-dirty-bitmap-disable" + } + ] + } +} +{ + "return": {} +} +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, + "recording": false, + "status": "disabled" + } + ] + } +} + +--- Submitting & Aborting Merge Transaction --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "disabled": true, + "granularity": 65536, + "name": "bitmapD", + "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "bitmaps": [ + "bitmapB", + "bitmapC" + ], + "node": "drive0", + "target": "bitmapD" + }, + "type": "block-dirty-bitmap-merge" + }, + { + "data": {}, + "type": "abort" + } + ] + } +} +{ + "error": { + "class": "GenericError", + "desc": "Transaction aborted using Abort action" + } +} +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, + "recording": false, + "status": "disabled" + } + ] + } +} + +--- Creating D as a merge of B & C --- + +{ + "execute": "transaction", + "arguments": { + "actions": [ + { + "data": { + "disabled": true, + "granularity": 65536, + "name": "bitmapD", + "node": "drive0" + }, + "type": "block-dirty-bitmap-add" + }, + { + "data": { + "bitmaps": [ + "bitmapB", + "bitmapC" + ], + "node": "drive0", + "target": "bitmapD" + }, + "type": "block-dirty-bitmap-merge" + } + ] + } +} +{ + "return": {} +} +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapD", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 393216, + "granularity": 65536, + "name": "bitmapC", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 262144, + "granularity": 65536, + "name": "bitmapB", + "persistent": false, + "recording": false, + "status": "disabled" + }, + { + "busy": false, + "count": 458752, + "granularity": 65536, + "name": "bitmapA", + "persistent": false, + "recording": false, + "status": "disabled" + } + ] + } +} + +--- Removing bitmaps A, B, C, and D --- + +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapA", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapB", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapC", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "bitmapD", "node": "drive0"}} +{"return": {}} + +--- Final Query --- + +{ + "bitmaps": { + "drive0": [] + } +} + +--- Done --- + diff --git a/tests/qemu-iotests/237 b/tests/qemu-iotests/237 new file mode 100755 index 0000000000..06897f8c87 --- /dev/null +++ b/tests/qemu-iotests/237 @@ -0,0 +1,238 @@ +#!/usr/bin/env python +# +# Test vmdk and file image creation +# +# Copyright (C) 2018 Red Hat, Inc. +# +# Creator/Owner: Kevin Wolf +# +# 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 math +import iotests +from iotests import imgfmt + +iotests.verify_image_format(supported_fmts=['vmdk']) + +def blockdev_create(vm, options): + result = vm.qmp_log('blockdev-create', job_id='job0', options=options, + filters=[iotests.filter_qmp_testfiles]) + + if 'return' in result: + assert result['return'] == {} + vm.run_job('job0') + iotests.log("") + +with iotests.FilePath('t.vmdk') as disk_path, \ + iotests.FilePath('t.vmdk.1') as extent1_path, \ + iotests.FilePath('t.vmdk.2') as extent2_path, \ + iotests.FilePath('t.vmdk.3') as extent3_path, \ + iotests.VM() as vm: + + # + # Successful image creation (defaults) + # + iotests.log("=== Successful image creation (defaults) ===") + iotests.log("") + + size = 5 * 1024 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + vm.qmp_log('blockdev-add', driver='file', filename=disk_path, + node_name='imgfile', filters=[iotests.filter_qmp_testfiles]) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'imgfile', + 'size': size }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (inline blockdev-add, explicit defaults) + # + iotests.log("=== Successful image creation (inline blockdev-add, explicit defaults) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 64 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'extents': [], + 'subformat': 'monolithicSparse', + 'adapter-type': 'ide', + 'hwversion': '4', + 'zeroed-grain': False }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Successful image creation (non-default options) + # + iotests.log("=== Successful image creation (with non-default options) ===") + iotests.log("") + + # Choose a different size to show that we got a new image + size = 32 * 1024 * 1024 + + vm.launch() + blockdev_create(vm, { 'driver': 'file', + 'filename': disk_path, + 'size': 0 }) + + blockdev_create(vm, { 'driver': imgfmt, + 'file': { + 'driver': 'file', + 'filename': disk_path, + }, + 'size': size, + 'extents': [], + 'subformat': 'monolithicSparse', + 'adapter-type': 'buslogic', + 'zeroed-grain': True }) + vm.shutdown() + + iotests.img_info_log(disk_path) + + # + # Invalid BlockdevRef + # + iotests.log("=== Invalid BlockdevRef ===") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': "this doesn't exist", + 'size': size }) + vm.shutdown() + + # + # Adapter types + # + + iotests.log("=== Adapter types ===") + iotests.log("") + + vm.add_blockdev('driver=file,filename=%s,node-name=node0' % (disk_path)) + + # Valid + iotests.log("== Valid adapter types ==") + iotests.log("") + + vm.launch() + for adapter_type in [ 'ide', 'buslogic', 'lsilogic', 'legacyESX' ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'adapter-type': adapter_type }) + vm.shutdown() + + # Invalid + iotests.log("== Invalid adapter types ==") + iotests.log("") + + vm.launch() + for adapter_type in [ 'foo', 'IDE', 'legacyesx', 1 ]: + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'adapter-type': adapter_type }) + vm.shutdown() + + # + # Other subformats + # + iotests.log("=== Other subformats ===") + iotests.log("") + + for path in [ extent1_path, extent2_path, extent3_path ]: + msg = iotests.qemu_img_pipe('create', '-f', imgfmt, path, '0') + iotests.log(msg, [iotests.filter_testfiles]) + + vm.add_blockdev('driver=file,filename=%s,node-name=ext1' % (extent1_path)) + vm.add_blockdev('driver=file,filename=%s,node-name=ext2' % (extent2_path)) + vm.add_blockdev('driver=file,filename=%s,node-name=ext3' % (extent3_path)) + + # Missing extent + iotests.log("== Missing extent ==") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'subformat': 'monolithicFlat' }) + vm.shutdown() + + # Correct extent + iotests.log("== Correct extent ==") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'subformat': 'monolithicFlat', + 'extents': ['ext1'] }) + vm.shutdown() + + # Extra extent + iotests.log("== Extra extent ==") + iotests.log("") + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': 512, + 'subformat': 'monolithicFlat', + 'extents': ['ext1', 'ext2', 'ext3'] }) + vm.shutdown() + + # Split formats + iotests.log("== Split formats ==") + iotests.log("") + + for size in [ 512, 1073741824, 2147483648, 5368709120 ]: + for subfmt in [ 'twoGbMaxExtentFlat', 'twoGbMaxExtentSparse' ]: + iotests.log("= %s %d =" % (subfmt, size)) + iotests.log("") + + num_extents = int(math.ceil(size / 2.0**31)) + extents = [ "ext%d" % (i) for i in range(1, num_extents + 1) ] + + vm.launch() + blockdev_create(vm, { 'driver': imgfmt, + 'file': 'node0', + 'size': size, + 'subformat': subfmt, + 'extents': extents }) + vm.shutdown() + + iotests.img_info_log(disk_path) diff --git a/tests/qemu-iotests/237.out b/tests/qemu-iotests/237.out new file mode 100644 index 0000000000..2aaa68f672 --- /dev/null +++ b/tests/qemu-iotests/237.out @@ -0,0 +1,348 @@ +=== Successful image creation (defaults) === + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-add", "arguments": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "node-name": "imgfile"}} +{"return": {}} +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "imgfile", "size": 5368709120}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 5.0G (5368709120 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: monolithicSparse + extents: + [0]: + virtual size: 5368709120 + filename: TEST_IMG + cluster size: 65536 + format: + +=== Successful image creation (inline blockdev-add, explicit defaults) === + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "hwversion": "4", "size": 67108864, "subformat": "monolithicSparse", "zeroed-grain": false}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 64M (67108864 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: monolithicSparse + extents: + [0]: + virtual size: 67108864 + filename: TEST_IMG + cluster size: 65536 + format: + +=== Successful image creation (with non-default options) === + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk", "size": 0}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "extents": [], "file": {"driver": "file", "filename": "TEST_DIR/PID-t.vmdk"}, "size": 33554432, "subformat": "monolithicSparse", "zeroed-grain": true}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 32M (33554432 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: monolithicSparse + extents: + [0]: + virtual size: 33554432 + filename: TEST_IMG + cluster size: 65536 + format: + +=== Invalid BlockdevRef === + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "this doesn't exist", "size": 33554432}}} +{"return": {}} +Job failed: Cannot find device=this doesn't exist nor node_name=this doesn't exist +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +=== Adapter types === + +== Valid adapter types == + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "ide", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "buslogic", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "lsilogic", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyESX", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +== Invalid adapter types == + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "foo", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"error": {"class": "GenericError", "desc": "Invalid parameter 'foo'"}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "IDE", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"error": {"class": "GenericError", "desc": "Invalid parameter 'IDE'"}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": "legacyesx", "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"error": {"class": "GenericError", "desc": "Invalid parameter 'legacyesx'"}} + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"adapter-type": 1, "driver": "vmdk", "file": "node0", "size": 33554432}}} +{"error": {"class": "GenericError", "desc": "Invalid parameter type for 'options.adapter-type', expected: string"}} + +=== Other subformats === + +Formatting 'TEST_DIR/PID-t.vmdk.1', fmt=vmdk size=0 compat6=off hwversion=undefined + +Formatting 'TEST_DIR/PID-t.vmdk.2', fmt=vmdk size=0 compat6=off hwversion=undefined + +Formatting 'TEST_DIR/PID-t.vmdk.3', fmt=vmdk size=0 compat6=off hwversion=undefined + +== Missing extent == + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}} +{"return": {}} +Job failed: Extent [0] not specified +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +== Correct extent == + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 33554432, "subformat": "monolithicFlat"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +== Extra extent == + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 512, "subformat": "monolithicFlat"}}} +{"return": {}} +Job failed: List of extents contains unused extents +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +== Split formats == + += twoGbMaxExtentFlat 512 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentFlat"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 512 (512 bytes) +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentFlat + extents: + [0]: + virtual size: 512 + filename: TEST_IMG.1 + format: FLAT + += twoGbMaxExtentSparse 512 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 512, "subformat": "twoGbMaxExtentSparse"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 512 (512 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentSparse + extents: + [0]: + virtual size: 512 + filename: TEST_IMG.1 + cluster size: 65536 + format: SPARSE + += twoGbMaxExtentFlat 1073741824 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentFlat"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0G (1073741824 bytes) +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentFlat + extents: + [0]: + virtual size: 1073741824 + filename: TEST_IMG.1 + format: FLAT + += twoGbMaxExtentSparse 1073741824 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 1073741824, "subformat": "twoGbMaxExtentSparse"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0G (1073741824 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentSparse + extents: + [0]: + virtual size: 1073741824 + filename: TEST_IMG.1 + cluster size: 65536 + format: SPARSE + += twoGbMaxExtentFlat 2147483648 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentFlat"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 2.0G (2147483648 bytes) +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentFlat + extents: + [0]: + virtual size: 2147483648 + filename: TEST_IMG.1 + format: FLAT + += twoGbMaxExtentSparse 2147483648 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1"], "file": "node0", "size": 2147483648, "subformat": "twoGbMaxExtentSparse"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 2.0G (2147483648 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentSparse + extents: + [0]: + virtual size: 2147483648 + filename: TEST_IMG.1 + cluster size: 65536 + format: SPARSE + += twoGbMaxExtentFlat 5368709120 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentFlat"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 5.0G (5368709120 bytes) +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentFlat + extents: + [0]: + virtual size: 2147483648 + filename: TEST_IMG.1 + format: FLAT + [1]: + virtual size: 2147483648 + filename: TEST_IMG.2 + format: FLAT + [2]: + virtual size: 1073741824 + filename: TEST_IMG.3 + format: FLAT + += twoGbMaxExtentSparse 5368709120 = + +{"execute": "blockdev-create", "arguments": {"job-id": "job0", "options": {"driver": "vmdk", "extents": ["ext1", "ext2", "ext3"], "file": "node0", "size": 5368709120, "subformat": "twoGbMaxExtentSparse"}}} +{"return": {}} +{"execute": "job-dismiss", "arguments": {"id": "job0"}} +{"return": {}} + +image: TEST_IMG +file format: IMGFMT +virtual size: 5.0G (5368709120 bytes) +cluster_size: 65536 +Format specific information: + cid: XXXXXXXXXX + parent cid: XXXXXXXXXX + create type: twoGbMaxExtentSparse + extents: + [0]: + virtual size: 2147483648 + filename: TEST_IMG.1 + cluster size: 65536 + format: SPARSE + [1]: + virtual size: 2147483648 + filename: TEST_IMG.2 + cluster size: 65536 + format: SPARSE + [2]: + virtual size: 1073741824 + filename: TEST_IMG.3 + cluster size: 65536 + format: SPARSE + diff --git a/tests/qemu-iotests/238 b/tests/qemu-iotests/238 new file mode 100755 index 0000000000..1c0a46fa90 --- /dev/null +++ b/tests/qemu-iotests/238 @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# +# Regression test for throttle group member unregister segfault with iothread +# +# Copyright (c) 2019 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 sys +import os +import iotests +from iotests import log + +if iotests.qemu_default_machine == 's390-ccw-virtio': + virtio_scsi_device = 'virtio-scsi-ccw' +else: + virtio_scsi_device = 'virtio-scsi-pci' + +vm = iotests.VM() +vm.launch() + +log(vm.qmp('blockdev-add', node_name='hd0', driver='null-co')) +log(vm.qmp('object-add', qom_type='iothread', id='iothread0')) +log(vm.qmp('device_add', id='scsi0', driver=virtio_scsi_device, iothread='iothread0')) +log(vm.qmp('device_add', id='scsi-hd0', driver='scsi-hd', drive='hd0')) +log(vm.qmp('block_set_io_throttle', id='scsi-hd0', bps=0, bps_rd=0, bps_wr=0, + iops=1000, iops_rd=0, iops_wr=0, conv_keys=False)) +log(vm.qmp('device_del', id='scsi-hd0')) + +vm.shutdown() diff --git a/tests/qemu-iotests/238.out b/tests/qemu-iotests/238.out new file mode 100644 index 0000000000..4de840ba8c --- /dev/null +++ b/tests/qemu-iotests/238.out @@ -0,0 +1,6 @@ +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} +{"return": {}} diff --git a/tests/qemu-iotests/239 b/tests/qemu-iotests/239 new file mode 100755 index 0000000000..6f085d573d --- /dev/null +++ b/tests/qemu-iotests/239 @@ -0,0 +1,53 @@ +#!/bin/bash +# +# Test case for dmg +# +# Copyright (C) 2019 yuchenlin +# +# 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=npes87184@gmail.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + rm -f "$TEST_IMG.raw" + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc + +_supported_fmt dmg +_supported_proto file +_supported_os Linux + +echo +echo "== Testing conversion to raw should success ==" +_use_sample_img simple-dmg.dmg.bz2 +if ! $QEMU_IMG convert -f $IMGFMT -O raw "$TEST_IMG" "$TEST_IMG.raw" ; then + exit 1 +fi + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/239.out b/tests/qemu-iotests/239.out new file mode 100644 index 0000000000..bedbad065b --- /dev/null +++ b/tests/qemu-iotests/239.out @@ -0,0 +1,4 @@ +QA output created by 239 + +== Testing conversion to raw should success == +*** done diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 new file mode 100755 index 0000000000..65cc3b39b1 --- /dev/null +++ b/tests/qemu-iotests/240 @@ -0,0 +1,129 @@ +#!/bin/bash +# +# Test hot plugging and unplugging with iothreads +# +# Copyright (C) 2019 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 . +# + +# creator +owner=berto@igalia.com + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +_supported_fmt generic +_supported_proto generic +_supported_os Linux + +do_run_qemu() +{ + echo Testing: "$@" + $QEMU -nographic -qmp stdio -serial none "$@" + 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' +} + +run_qemu() +{ + do_run_qemu "$@" 2>&1 | _filter_qmp | _filter_qmp_events +} + +case "$QEMU_DEFAULT_MACHINE" in + s390-ccw-virtio) + virtio_scsi=virtio-scsi-ccw + ;; + *) + virtio_scsi=virtio-scsi-pci + ;; +esac + +echo +echo === Unplug a SCSI disk and then plug it again === +echo + +run_qemu <. +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +nbd_unix_socket=$TEST_DIR/test_qemu_nbd_socket + +_cleanup() +{ + _cleanup_test_img + rm -f "$TEST_DIR/server.log" + nbd_server_stop +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter +. ./common.nbd + +_supported_fmt raw +_supported_proto nbd +_supported_os Linux +_require_command QEMU_NBD + +# can't use _make_test_img, because qemu-img rounds image size up, +# and because we want to use Unix socket rather than TCP port. Likewise, +# we have to redirect TEST_IMG to our server. +# This tests that we can deal with the hole at the end of an unaligned +# raw file (either because the server doesn't advertise alignment too +# large, or because the client ignores the server's noncompliance - even +# though we can't actually wire iotests into checking trace messages). +printf %01000d 0 > "$TEST_IMG_FILE" +TEST_IMG="nbd:unix:$nbd_unix_socket" + +echo +echo "=== Exporting unaligned raw image, natural alignment ===" +echo + +nbd_server_start_unix_socket -f $IMGFMT "$TEST_IMG_FILE" + +$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' +$QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map +$QEMU_IO -f raw -c map "$TEST_IMG" +nbd_server_stop + +echo +echo "=== Exporting unaligned raw image, forced server sector alignment ===" +echo + +# Intentionally omit '-f' to force image probing, which in turn forces +# sector alignment, here at the server. +nbd_server_start_unix_socket "$TEST_IMG_FILE" 2> "$TEST_DIR/server.log" + +$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' +$QEMU_IMG map -f raw --output=json "$TEST_IMG" | _filter_qemu_img_map +$QEMU_IO -f raw -c map "$TEST_IMG" +nbd_server_stop +cat "$TEST_DIR/server.log" | _filter_testdir + +echo +echo "=== Exporting unaligned raw image, forced client sector alignment ===" +echo + +# Now force sector alignment at the client. +nbd_server_start_unix_socket -f $IMGFMT "$TEST_IMG_FILE" + +$QEMU_NBD_PROG --list -k $nbd_unix_socket | grep '\(size\|min\)' +$QEMU_IMG map --output=json "$TEST_IMG" | _filter_qemu_img_map +$QEMU_IO -c map "$TEST_IMG" +nbd_server_stop + +# Not tested yet: we also want to ensure that qemu as NBD client does +# not access beyond the end of a server's advertised unaligned size: +# nbdkit -U - memory size=513 --run 'qemu-io -f raw -c "r 512 512" $nbd' +# However, since qemu as server always rounds up to a sector alignment, +# we would have to use nbdkit to provoke the current client failures. + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/241.out b/tests/qemu-iotests/241.out new file mode 100644 index 0000000000..75f9f465e5 --- /dev/null +++ b/tests/qemu-iotests/241.out @@ -0,0 +1,28 @@ +QA output created by 241 + +=== Exporting unaligned raw image, natural alignment === + + size: 1024 + min block: 1 +[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] +1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) + +=== Exporting unaligned raw image, forced server sector alignment === + + size: 1024 + min block: 512 +[{ "start": 0, "length": 1024, "depth": 0, "zero": false, "data": true, "offset": OFFSET}] +1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) +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. + +=== Exporting unaligned raw image, forced client sector alignment === + + size: 1024 + min block: 1 +[{ "start": 0, "length": 1000, "depth": 0, "zero": false, "data": true, "offset": OFFSET}, +{ "start": 1000, "length": 24, "depth": 0, "zero": true, "data": true, "offset": OFFSET}] +1 KiB (0x400) bytes allocated at offset 0 bytes (0x0) +*** done diff --git a/tests/qemu-iotests/242 b/tests/qemu-iotests/242 new file mode 100755 index 0000000000..c176e92da6 --- /dev/null +++ b/tests/qemu-iotests/242 @@ -0,0 +1,106 @@ +#!/usr/bin/env python +# +# Test for qcow2 bitmap printed information +# +# Copyright (c) 2019 Virtuozzo International GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import iotests +import json +import struct +from iotests import qemu_img_create, qemu_io, qemu_img_pipe, \ + file_path, img_info_log, log, filter_qemu_io + +iotests.verify_image_format(supported_fmts=['qcow2']) + +disk = file_path('disk') +chunk = 256 * 1024 +bitmap_flag_unknown = 1 << 2 +# flag_offset = 5*cluster_size + flag_offset_in_bitmap_directory_entry +flag_offset = 0x5000f + + +def print_bitmap(extra_args): + log('qemu-img info dump:\n') + img_info_log(disk, extra_args=extra_args) + result = json.loads(qemu_img_pipe('info', '--force-share', + '--output=json', disk)) + if 'bitmaps' in result['format-specific']['data']: + bitmaps = result['format-specific']['data']['bitmaps'] + log('The same bitmaps in JSON format:') + log(bitmaps, indent=2) + else: + log('No bitmap in JSON format output') + + +def add_bitmap(bitmap_number, persistent, disabled): + granularity = 1 << (13 + bitmap_number) + bitmap_name = 'bitmap-' + str(bitmap_number-1) + vm = iotests.VM().add_drive(disk) + vm.launch() + vm.qmp_log('block-dirty-bitmap-add', node='drive0', name=bitmap_name, + granularity=granularity, persistent=persistent, + disabled=disabled) + vm.shutdown() + + +def write_to_disk(offset, size): + write = 'write {} {}'.format(offset, size) + log(qemu_io('-c', write, disk), filters=[filter_qemu_io]) + + +def toggle_flag(offset): + with open(disk, "r+b") as f: + f.seek(offset, 0) + # Read one byte in a way compatible with Python 2 + flags = struct.unpack("B", f.read(1)) + toggled = flags[0] ^ bitmap_flag_unknown + f.seek(-1, 1) + f.write(struct.pack("B", toggled)) + + +qemu_img_create('-f', iotests.imgfmt, disk, '1M') + +for num in range(1, 4): + disabled = False + if num == 2: + disabled = True + log('Test {}'.format(num)) + add_bitmap(num, num > 1, disabled) + write_to_disk((num-1) * chunk, chunk) + print_bitmap([]) + log('') + +vm = iotests.VM().add_drive(disk) +vm.launch() +num += 1 +log('Test {}\nChecking "in-use" flag...'.format(num)) +print_bitmap(['--force-share']) +vm.shutdown() + +num += 1 +log('\nTest {}'.format(num)) +qemu_img_create('-f', iotests.imgfmt, disk, '1M') +add_bitmap(1, True, False) +log('Write an unknown bitmap flag \'{}\' into a new QCOW2 image at offset {}' + .format(hex(bitmap_flag_unknown), flag_offset)) +toggle_flag(flag_offset) +img_info_log(disk) +toggle_flag(flag_offset) +log('Unset the unknown bitmap flag \'{}\' in the bitmap directory entry:\n' + .format(hex(bitmap_flag_unknown))) +img_info_log(disk) +log('Test complete') diff --git a/tests/qemu-iotests/242.out b/tests/qemu-iotests/242.out new file mode 100644 index 0000000000..fbe05d71c0 --- /dev/null +++ b/tests/qemu-iotests/242.out @@ -0,0 +1,166 @@ +Test 1 +{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": false, "granularity": 16384, "name": "bitmap-0", "node": "drive0", "persistent": false}} +{"return": {}} +wrote 262144/262144 bytes at offset 0 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +qemu-img info dump: + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0M (1048576 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + refcount bits: 16 + corrupt: false + +No bitmap in JSON format output + +Test 2 +{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": true, "granularity": 32768, "name": "bitmap-1", "node": "drive0", "persistent": true}} +{"return": {}} +wrote 262144/262144 bytes at offset 262144 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +qemu-img info dump: + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0M (1048576 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + bitmaps: + [0]: + flags: + name: bitmap-1 + granularity: 32768 + refcount bits: 16 + corrupt: false + +The same bitmaps in JSON format: +[ + { + "flags": [], + "granularity": 32768, + "name": "bitmap-1" + } +] + +Test 3 +{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": false, "granularity": 65536, "name": "bitmap-2", "node": "drive0", "persistent": true}} +{"return": {}} +wrote 262144/262144 bytes at offset 524288 +256 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +qemu-img info dump: + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0M (1048576 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + bitmaps: + [0]: + flags: + name: bitmap-1 + granularity: 32768 + [1]: + flags: + [0]: auto + name: bitmap-2 + granularity: 65536 + refcount bits: 16 + corrupt: false + +The same bitmaps in JSON format: +[ + { + "flags": [], + "granularity": 32768, + "name": "bitmap-1" + }, + { + "flags": [ + "auto" + ], + "granularity": 65536, + "name": "bitmap-2" + } +] + +Test 4 +Checking "in-use" flag... +qemu-img info dump: + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0M (1048576 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + bitmaps: + [0]: + flags: + [0]: in-use + name: bitmap-1 + granularity: 32768 + [1]: + flags: + [0]: in-use + [1]: auto + name: bitmap-2 + granularity: 65536 + refcount bits: 16 + corrupt: false + +The same bitmaps in JSON format: +[ + { + "flags": [ + "in-use" + ], + "granularity": 32768, + "name": "bitmap-1" + }, + { + "flags": [ + "in-use", + "auto" + ], + "granularity": 65536, + "name": "bitmap-2" + } +] + +Test 5 +{"execute": "block-dirty-bitmap-add", "arguments": {"disabled": false, "granularity": 16384, "name": "bitmap-0", "node": "drive0", "persistent": true}} +{"return": {}} +Write an unknown bitmap flag '0x4' into a new QCOW2 image at offset 327695 +qemu-img: Could not open 'TEST_IMG': Bitmap 'bitmap-0' doesn't satisfy the constraints + +Unset the unknown bitmap flag '0x4' in the bitmap directory entry: + +image: TEST_IMG +file format: IMGFMT +virtual size: 1.0M (1048576 bytes) +cluster_size: 65536 +Format specific information: + compat: 1.1 + lazy refcounts: false + bitmaps: + [0]: + flags: + [0]: auto + name: bitmap-0 + granularity: 16384 + refcount bits: 16 + corrupt: false + +Test complete diff --git a/tests/qemu-iotests/243 b/tests/qemu-iotests/243 new file mode 100755 index 0000000000..5838c6e89c --- /dev/null +++ b/tests/qemu-iotests/243 @@ -0,0 +1,85 @@ +#!/bin/bash +# +# Test qcow2 preallocation +# +# Copyright (C) 2019 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" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f $TEST_IMG.data +} +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 + +for mode in off metadata falloc full; do + + echo + echo "=== preallocation=$mode ===" + echo + + IMGOPTS="preallocation=$mode" _make_test_img 64M + + printf "File size: " + du -b $TEST_IMG | cut -f1 + + # Can't use precise numbers here because they differ between filesystems + printf "Disk usage: " + [ $(du -B1 $TEST_IMG | cut -f1) -lt 1048576 ] && echo "low" || echo "high" + +done + +for mode in off metadata falloc full; do + + echo + echo "=== External data file: preallocation=$mode ===" + echo + + IMGOPTS="data_file=$TEST_IMG.data,preallocation=$mode" _make_test_img 64M + + echo -n "qcow2 file size: " + du -b $TEST_IMG | cut -f1 + echo -n "data file size: " + du -b $TEST_IMG.data | cut -f1 + + # Can't use precise numbers here because they differ between filesystems + echo -n "qcow2 disk usage: " + [ $(du -B1 $TEST_IMG | cut -f1) -lt 1048576 ] && echo "low" || echo "high" + echo -n "data disk usage: " + [ $(du -B1 $TEST_IMG.data | cut -f1) -lt 1048576 ] && echo "low" || echo "high" + +done + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/243.out b/tests/qemu-iotests/243.out new file mode 100644 index 0000000000..dcb33fac32 --- /dev/null +++ b/tests/qemu-iotests/243.out @@ -0,0 +1,58 @@ +QA output created by 243 + +=== preallocation=off === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 preallocation=off +File size: 196616 +Disk usage: low + +=== preallocation=metadata === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 preallocation=metadata +File size: 67436544 +Disk usage: low + +=== preallocation=falloc === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 preallocation=falloc +File size: 67436544 +Disk usage: high + +=== preallocation=full === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 preallocation=full +File size: 67436544 +Disk usage: high + +=== External data file: preallocation=off === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data preallocation=off +qcow2 file size: 196616 +data file size: 67108864 +qcow2 disk usage: low +data disk usage: low + +=== External data file: preallocation=metadata === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data preallocation=metadata +qcow2 file size: 327680 +data file size: 67108864 +qcow2 disk usage: low +data disk usage: low + +=== External data file: preallocation=falloc === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data preallocation=falloc +qcow2 file size: 327680 +data file size: 67108864 +qcow2 disk usage: low +data disk usage: high + +=== External data file: preallocation=full === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data preallocation=full +qcow2 file size: 327680 +data file size: 67108864 +qcow2 disk usage: low +data disk usage: high +*** done diff --git a/tests/qemu-iotests/244 b/tests/qemu-iotests/244 new file mode 100755 index 0000000000..d8e7122305 --- /dev/null +++ b/tests/qemu-iotests/244 @@ -0,0 +1,200 @@ +#!/bin/bash +# +# Test qcow2 with external data files +# +# Copyright (C) 2019 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" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f $TEST_IMG.data + rm -f $TEST_IMG.src +} +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 + +echo +echo "=== Create and open image with external data file ===" +echo + +echo "With data file name in the image:" +IMGOPTS="data_file=$TEST_IMG.data" _make_test_img 64M +_check_test_img + +$QEMU_IO -c "open $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "open -odata-file.filename=$TEST_IMG.data $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "open -odata-file.filename=inexistent $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "Data file required, but without data file name in the image:" +$QEMU_IMG amend -odata_file= $TEST_IMG + +$QEMU_IO -c "open $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "open -odata-file.filename=$TEST_IMG.data $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "open -odata-file.filename=inexistent $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "Setting data-file for an image with internal data:" +_make_test_img 64M + +$QEMU_IO -c "open -odata-file.filename=$TEST_IMG.data $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir +$QEMU_IO -c "open -odata-file.filename=inexistent $TEST_IMG" -c "read -P 0 0 64k" 2>&1 | _filter_qemu_io | _filter_testdir + +echo +echo "=== Conflicting features ===" +echo + +echo "Convert to compressed target with data file:" +TEST_IMG="$TEST_IMG.src" _make_test_img 64M + +$QEMU_IO -c 'write -P 0x11 0 1M' \ + -f $IMGFMT "$TEST_IMG.src" | + _filter_qemu_io + +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -c -odata_file="$TEST_IMG.data" \ + "$TEST_IMG.src" "$TEST_IMG" + +echo +echo "Convert uncompressed, then write compressed data manually:" +$QEMU_IMG convert -f $IMGFMT -O $IMGFMT -odata_file="$TEST_IMG.data" \ + "$TEST_IMG.src" "$TEST_IMG" +$QEMU_IMG compare "$TEST_IMG.src" "$TEST_IMG" + +$QEMU_IO -c 'write -c -P 0x22 0 1M' \ + -f $IMGFMT "$TEST_IMG" | + _filter_qemu_io +_check_test_img + +echo +echo "Take an internal snapshot:" + +$QEMU_IMG snapshot -c test "$TEST_IMG" +_check_test_img + +echo +echo "=== Standalone image with external data file (efficient) ===" +echo + +IMGOPTS="data_file=$TEST_IMG.data" _make_test_img 64M + +echo -n "qcow2 file size before I/O: " +du -b $TEST_IMG | cut -f1 + +# Create image with the following layout +# 0-1 MB: Unallocated +# 1-2 MB: Written (pattern 0x11) +# 2-3 MB: Discarded +# 3-4 MB: Zero write over discarded space +# 4-5 MB: Zero write over written space +# 5-6 MB: Zero write over unallocated space + +echo +$QEMU_IO -c 'write -P 0x11 1M 4M' \ + -c 'discard 2M 2M' \ + -c 'write -z 3M 3M' \ + -f $IMGFMT "$TEST_IMG" | + _filter_qemu_io +_check_test_img + +echo +$QEMU_IMG map --output=json "$TEST_IMG" + +echo +$QEMU_IO -c 'read -P 0 0 1M' \ + -c 'read -P 0x11 1M 1M' \ + -c 'read -P 0 2M 4M' \ + -f $IMGFMT "$TEST_IMG" | + _filter_qemu_io + +# Zero clusters are only marked as such in the qcow2 metadata, but contain +# stale data in the external data file +echo +$QEMU_IO -c 'read -P 0 0 1M' \ + -c 'read -P 0x11 1M 1M' \ + -c 'read -P 0 2M 2M' \ + -c 'read -P 0x11 4M 1M' \ + -c 'read -P 0 5M 1M' \ + -f raw "$TEST_IMG.data" | + _filter_qemu_io + + +echo -n "qcow2 file size after I/O: " +du -b $TEST_IMG | cut -f1 + +echo +echo "=== Standalone image with external data file (valid raw) ===" +echo + +IMGOPTS="data_file=$TEST_IMG.data,data_file_raw=on" _make_test_img 64M + +echo -n "qcow2 file size before I/O: " +du -b $TEST_IMG | cut -f1 + +echo +$QEMU_IO -c 'write -P 0x11 1M 4M' \ + -c 'discard 2M 2M' \ + -c 'write -z 3M 3M' \ + -f $IMGFMT "$TEST_IMG" | + _filter_qemu_io +_check_test_img + +echo +$QEMU_IMG map --output=json "$TEST_IMG" + +echo +$QEMU_IO -c 'read -P 0 0 1M' \ + -c 'read -P 0x11 1M 1M' \ + -c 'read -P 0 2M 4M' \ + -f $IMGFMT "$TEST_IMG" | + _filter_qemu_io + +echo +$QEMU_IMG compare "$TEST_IMG" "$TEST_IMG.data" + +echo -n "qcow2 file size after I/O: " +du -b $TEST_IMG | cut -f1 + +echo +echo "=== bdrv_co_block_status test for file and offset=0 ===" +echo + +IMGOPTS="data_file=$TEST_IMG.data" _make_test_img 64M + +$QEMU_IO -c 'write -P 0x11 0 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io +$QEMU_IO -c 'read -P 0x11 0 1M' -f $IMGFMT "$TEST_IMG" | _filter_qemu_io +$QEMU_IMG map --output=human "$TEST_IMG" | _filter_testdir +$QEMU_IMG map --output=json "$TEST_IMG" + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/244.out b/tests/qemu-iotests/244.out new file mode 100644 index 0000000000..98e5946976 --- /dev/null +++ b/tests/qemu-iotests/244.out @@ -0,0 +1,125 @@ +QA output created by 244 + +=== Create and open image with external data file === + +With data file name in the image: +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data +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) +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +can't open device TEST_DIR/t.qcow2: Could not open 'inexistent': No such file or directory +no file open, try 'help open' + +Data file required, but without data file name in the image: +can't open device TEST_DIR/t.qcow2: 'data-file' is required for this image +no file open, try 'help open' +read 65536/65536 bytes at offset 0 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +can't open device TEST_DIR/t.qcow2: Could not open 'inexistent': No such file or directory +no file open, try 'help open' + +Setting data-file for an image with internal data: +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 +can't open device TEST_DIR/t.qcow2: 'data-file' can only be set for images with an external data file +no file open, try 'help open' +can't open device TEST_DIR/t.qcow2: Could not open 'inexistent': No such file or directory +no file open, try 'help open' + +=== Conflicting features === + +Convert to compressed target with data file: +Formatting 'TEST_DIR/t.IMGFMT.src', fmt=IMGFMT size=67108864 +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qemu-img: error while writing sector 0: Operation not supported + +Convert uncompressed, then write compressed data manually: +Images are identical. +write failed: Operation not supported +No errors were found on the image. + +Take an internal snapshot: +qemu-img: Could not create snapshot 'test': -95 (Operation not supported) +No errors were found on the image. + +=== Standalone image with external data file (efficient) === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data +qcow2 file size before I/O: 196616 + +wrote 4194304/4194304 bytes at offset 1048576 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 3145728/3145728 bytes at offset 3145728 +3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, +{ "start": 1048576, "length": 1048576, "depth": 0, "zero": false, "data": true, "offset": 1048576}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": true, "data": false}, +{ "start": 4194304, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": 4194304}, +{ "start": 5242880, "length": 61865984, "depth": 0, "zero": true, "data": false}] + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4194304/4194304 bytes at offset 2097152 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 4194304 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 5242880 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +qcow2 file size after I/O: 327680 + +=== Standalone image with external data file (valid raw) === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data data_file_raw=on +qcow2 file size before I/O: 196616 + +wrote 4194304/4194304 bytes at offset 1048576 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 2097152/2097152 bytes at offset 2097152 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +wrote 3145728/3145728 bytes at offset 3145728 +3 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +No errors were found on the image. + +[{ "start": 0, "length": 1048576, "depth": 0, "zero": true, "data": false}, +{ "start": 1048576, "length": 1048576, "depth": 0, "zero": false, "data": true, "offset": 1048576}, +{ "start": 2097152, "length": 2097152, "depth": 0, "zero": true, "data": false}, +{ "start": 4194304, "length": 1048576, "depth": 0, "zero": true, "data": false, "offset": 4194304}, +{ "start": 5242880, "length": 61865984, "depth": 0, "zero": true, "data": false}] + +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 1048576 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 4194304/4194304 bytes at offset 2097152 +4 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +Images are identical. +qcow2 file size after I/O: 327680 + +=== bdrv_co_block_status test for file and offset=0 === + +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 data_file=TEST_DIR/t.IMGFMT.data +wrote 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +read 1048576/1048576 bytes at offset 0 +1 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +Offset Length Mapped to File +0 0x100000 0 TEST_DIR/t.qcow2.data +[{ "start": 0, "length": 1048576, "depth": 0, "zero": false, "data": true, "offset": 0}, +{ "start": 1048576, "length": 66060288, "depth": 0, "zero": true, "data": false}] +*** done diff --git a/tests/qemu-iotests/245 b/tests/qemu-iotests/245 new file mode 100644 index 0000000000..a04c6235c1 --- /dev/null +++ b/tests/qemu-iotests/245 @@ -0,0 +1,997 @@ +#!/usr/bin/env python +# +# Test cases for the QMP 'x-blockdev-reopen' command +# +# Copyright (C) 2018-2019 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 re +import iotests +import copy +import json +from iotests import qemu_img, qemu_io + +hd_path = [ + os.path.join(iotests.test_dir, 'hd0.img'), + os.path.join(iotests.test_dir, 'hd1.img'), + os.path.join(iotests.test_dir, 'hd2.img') +] + +def hd_opts(idx): + return {'driver': iotests.imgfmt, + 'node-name': 'hd%d' % idx, + 'file': {'driver': 'file', + 'node-name': 'hd%d-file' % idx, + 'filename': hd_path[idx] } } + +class TestBlockdevReopen(iotests.QMPTestCase): + total_io_cmds = 0 + + def setUp(self): + qemu_img('create', '-f', iotests.imgfmt, hd_path[0], '3M') + qemu_img('create', '-f', iotests.imgfmt, '-b', hd_path[0], hd_path[1]) + qemu_img('create', '-f', iotests.imgfmt, hd_path[2], '3M') + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa0 0 1M', hd_path[0]) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa1 1M 1M', hd_path[1]) + qemu_io('-f', iotests.imgfmt, '-c', 'write -P 0xa2 2M 1M', hd_path[2]) + self.vm = iotests.VM() + self.vm.launch() + + def tearDown(self): + self.vm.shutdown() + self.check_qemu_io_errors() + os.remove(hd_path[0]) + os.remove(hd_path[1]) + os.remove(hd_path[2]) + + # The output of qemu-io is not returned by vm.hmp_qemu_io() but + # it's stored in the log and can only be read when the VM has been + # shut down. This function runs qemu-io and keeps track of the + # number of times it's been called. + def run_qemu_io(self, img, cmd): + result = self.vm.hmp_qemu_io(img, cmd) + self.assert_qmp(result, 'return', '') + self.total_io_cmds += 1 + + # Once the VM is shut down we can parse the log and see if qemu-io + # ran without errors. + def check_qemu_io_errors(self): + self.assertFalse(self.vm.is_running()) + found = 0 + log = self.vm.get_log() + for line in log.split("\n"): + if line.startswith("Pattern verification failed"): + raise Exception("%s (command #%d)" % (line, found)) + if re.match("read .*/.* bytes at offset", line): + found += 1 + self.assertEqual(found, self.total_io_cmds, + "Expected output of %d qemu-io commands, found %d" % + (found, self.total_io_cmds)) + + # Run x-blockdev-reopen with 'opts' but applying 'newopts' + # on top of it. The original 'opts' dict is unmodified + def reopen(self, opts, newopts = {}, errmsg = None): + opts = copy.deepcopy(opts) + + # Apply the changes from 'newopts' on top of 'opts' + for key in newopts: + value = newopts[key] + # If key has the form "foo.bar" then we need to do + # opts["foo"]["bar"] = value, not opts["foo.bar"] = value + subdict = opts + while key.find('.') != -1: + [prefix, key] = key.split('.', 1) + subdict = opts[prefix] + subdict[key] = value + + result = self.vm.qmp('x-blockdev-reopen', conv_keys = False, **opts) + if errmsg: + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', errmsg) + else: + self.assert_qmp(result, 'return', {}) + + + # Run query-named-block-nodes and return the specified entry + def get_node(self, node_name): + result = self.vm.qmp('query-named-block-nodes') + for node in result['return']: + if node['node-name'] == node_name: + return node + return None + + # Run 'query-named-block-nodes' and compare its output with the + # value passed by the user in 'graph' + def check_node_graph(self, graph): + result = self.vm.qmp('query-named-block-nodes') + self.assertEqual(json.dumps(graph, sort_keys=True), + json.dumps(result, sort_keys=True)) + + # This test opens one single disk image (without backing files) + # and tries to reopen it with illegal / incorrect parameters. + def test_incorrect_parameters_single_file(self): + # Open 'hd0' only (no backing files) + opts = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + original_graph = self.vm.qmp('query-named-block-nodes') + + # We can reopen the image passing the same options + self.reopen(opts) + + # We can also reopen passing a child reference in 'file' + self.reopen(opts, {'file': 'hd0-file'}) + + # We cannot change any of these + self.reopen(opts, {'node-name': 'not-found'}, "Cannot find node named 'not-found'") + self.reopen(opts, {'node-name': ''}, "Cannot find node named ''") + self.reopen(opts, {'node-name': None}, "Invalid parameter type for 'node-name', expected: string") + self.reopen(opts, {'driver': 'raw'}, "Cannot change the option 'driver'") + self.reopen(opts, {'driver': ''}, "Invalid parameter ''") + self.reopen(opts, {'driver': None}, "Invalid parameter type for 'driver', expected: string") + self.reopen(opts, {'file': 'not-found'}, "Cannot change the option 'file'") + self.reopen(opts, {'file': ''}, "Cannot change the option 'file'") + self.reopen(opts, {'file': None}, "Invalid parameter type for 'file', expected: BlockdevRef") + self.reopen(opts, {'file.node-name': 'newname'}, "Cannot change the option 'node-name'") + self.reopen(opts, {'file.driver': 'host_device'}, "Cannot change the option 'driver'") + self.reopen(opts, {'file.filename': hd_path[1]}, "Cannot change the option 'filename'") + self.reopen(opts, {'file.aio': 'native'}, "Cannot change the option 'aio'") + self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") + self.reopen(opts, {'file.filename': None}, "Invalid parameter type for 'file.filename', expected: string") + + # node-name is optional in BlockdevOptions, but x-blockdev-reopen needs it + del opts['node-name'] + self.reopen(opts, {}, "Node name not specified") + + # Check that nothing has changed + self.check_node_graph(original_graph) + + # Remove the node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # This test opens an image with a backing file and tries to reopen + # it with illegal / incorrect parameters. + def test_incorrect_parameters_backing_file(self): + # Open hd1 omitting the backing options (hd0 will be opened + # with the default options) + opts = hd_opts(1) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + original_graph = self.vm.qmp('query-named-block-nodes') + + # We can't reopen the image passing the same options, 'backing' is mandatory + self.reopen(opts, {}, "backing is missing for 'hd1'") + + # Everything works if we pass 'backing' using the existing node name + for node in original_graph['return']: + if node['drv'] == iotests.imgfmt and node['file'] == hd_path[0]: + backing_node_name = node['node-name'] + self.reopen(opts, {'backing': backing_node_name}) + + # We can't use a non-existing or empty (non-NULL) node as the backing image + self.reopen(opts, {'backing': 'not-found'}, "Cannot find device= nor node_name=not-found") + self.reopen(opts, {'backing': ''}, "Cannot find device= nor node_name=") + + # We can reopen the image just fine if we specify the backing options + opts['backing'] = {'driver': iotests.imgfmt, + 'file': {'driver': 'file', + 'filename': hd_path[0]}} + self.reopen(opts) + + # We cannot change any of these options + self.reopen(opts, {'backing.node-name': 'newname'}, "Cannot change the option 'node-name'") + self.reopen(opts, {'backing.driver': 'raw'}, "Cannot change the option 'driver'") + self.reopen(opts, {'backing.file.node-name': 'newname'}, "Cannot change the option 'node-name'") + self.reopen(opts, {'backing.file.driver': 'host_device'}, "Cannot change the option 'driver'") + + # Check that nothing has changed since the beginning + self.check_node_graph(original_graph) + + # Remove the node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') + self.assert_qmp(result, 'return', {}) + + # Reopen an image several times changing some of its options + def test_reopen(self): + # Check whether the filesystem supports O_DIRECT + if 'O_DIRECT' in qemu_io('-f', 'raw', '-t', 'none', '-c', 'quit', hd_path[0]): + supports_direct = False + else: + supports_direct = True + + # Open the hd1 image passing all backing options + opts = hd_opts(1) + opts['backing'] = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + original_graph = self.vm.qmp('query-named-block-nodes') + + # We can reopen the image passing the same options + self.reopen(opts) + + # Reopen in read-only mode + self.assert_qmp(self.get_node('hd1'), 'ro', False) + + self.reopen(opts, {'read-only': True}) + self.assert_qmp(self.get_node('hd1'), 'ro', True) + self.reopen(opts) + self.assert_qmp(self.get_node('hd1'), 'ro', False) + + # Change the cache options + self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) + self.assert_qmp(self.get_node('hd1'), 'cache/direct', False) + self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False) + self.reopen(opts, {'cache': { 'direct': supports_direct, 'no-flush': True }}) + self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) + self.assert_qmp(self.get_node('hd1'), 'cache/direct', supports_direct) + self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', True) + + # Reopen again with the original options + self.reopen(opts) + self.assert_qmp(self.get_node('hd1'), 'cache/writeback', True) + self.assert_qmp(self.get_node('hd1'), 'cache/direct', False) + self.assert_qmp(self.get_node('hd1'), 'cache/no-flush', False) + + # Change 'detect-zeroes' and 'discard' + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off') + self.reopen(opts, {'detect-zeroes': 'on'}) + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') + self.reopen(opts, {'detect-zeroes': 'unmap'}, + "setting detect-zeroes to unmap is not allowed " + + "without setting discard operation to unmap") + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') + self.reopen(opts, {'detect-zeroes': 'unmap', 'discard': 'unmap'}) + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'unmap') + self.reopen(opts) + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'off') + + # Changing 'force-share' is currently not supported + self.reopen(opts, {'force-share': True}, "Cannot change the option 'force-share'") + + # Change some qcow2-specific options + # No way to test for success other than checking the return message + if iotests.imgfmt == 'qcow2': + self.reopen(opts, {'l2-cache-entry-size': 128 * 1024}, + "L2 cache entry size must be a power of two "+ + "between 512 and the cluster size (65536)") + self.reopen(opts, {'l2-cache-size': 1024 * 1024, + 'cache-size': 512 * 1024}, + "l2-cache-size may not exceed cache-size") + self.reopen(opts, {'l2-cache-size': 4 * 1024 * 1024, + 'refcount-cache-size': 4 * 1024 * 1024, + 'l2-cache-entry-size': 32 * 1024}) + self.reopen(opts, {'pass-discard-request': True}) + + # Check that nothing has changed since the beginning + # (from the parameters that we can check) + self.check_node_graph(original_graph) + + # Check that the node names (other than the top-level one) are optional + del opts['file']['node-name'] + del opts['backing']['node-name'] + del opts['backing']['file']['node-name'] + self.reopen(opts) + self.check_node_graph(original_graph) + + # Reopen setting backing = null, this removes the backing image from the chain + self.reopen(opts, {'backing': None}) + self.assert_qmp_absent(self.get_node('hd1'), 'image/backing-image') + + # Open the 'hd0' image + result = self.vm.qmp('blockdev-add', conv_keys = False, **hd_opts(0)) + self.assert_qmp(result, 'return', {}) + + # Reopen the hd1 image setting 'hd0' as its backing image + self.reopen(opts, {'backing': 'hd0'}) + self.assert_qmp(self.get_node('hd1'), 'image/backing-image/filename', hd_path[0]) + + # Check that nothing has changed since the beginning + self.reopen(hd_opts(0), {'read-only': True}) + self.check_node_graph(original_graph) + + # The backing file (hd0) is now a reference, we cannot change backing.* anymore + self.reopen(opts, {}, "Cannot change the option 'backing.driver'") + + # We can't remove 'hd0' while it's a backing image of 'hd1' + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Node 'hd0' is busy: node is used as backing hd of 'hd1'") + + # But we can remove both nodes if done in the proper order + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') + self.assert_qmp(result, 'return', {}) + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # Reopen a raw image and see the effect of changing the 'offset' option + def test_reopen_raw(self): + opts = {'driver': 'raw', 'node-name': 'hd0', + 'file': { 'driver': 'file', + 'filename': hd_path[0], + 'node-name': 'hd0-file' } } + + # First we create a 2MB raw file, and fill each half with a + # different value + qemu_img('create', '-f', 'raw', hd_path[0], '2M') + qemu_io('-f', 'raw', '-c', 'write -P 0xa0 0 1M', hd_path[0]) + qemu_io('-f', 'raw', '-c', 'write -P 0xa1 1M 1M', hd_path[0]) + + # Open the raw file with QEMU + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Read 1MB from offset 0 + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + + # Reopen the image with a 1MB offset. + # Now the results are different + self.reopen(opts, {'offset': 1024*1024}) + self.run_qemu_io("hd0", "read -P 0xa1 0 1M") + + # Reopen again with the original options. + # We get the original results again + self.reopen(opts) + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + + # Remove the block device + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # Omitting an option should reset it to the default value, but if + # an option cannot be changed it shouldn't be possible to reset it + # to its default value either + def test_reset_default_values(self): + opts = {'driver': 'qcow2', 'node-name': 'hd0', + 'file': { 'driver': 'file', + 'filename': hd_path[0], + 'x-check-cache-dropped': True, # This one can be changed + 'locking': 'off', # This one can NOT be changed + 'node-name': 'hd0-file' } } + + # Open the file with QEMU + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # file.x-check-cache-dropped can be changed... + self.reopen(opts, { 'file.x-check-cache-dropped': False }) + # ...and dropped completely (resetting to the default value) + del opts['file']['x-check-cache-dropped'] + self.reopen(opts) + + # file.locking cannot be changed nor reset to the default value + self.reopen(opts, { 'file.locking': 'on' }, "Cannot change the option 'locking'") + del opts['file']['locking'] + self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") + # But we can reopen it if we maintain its previous value + self.reopen(opts, { 'file.locking': 'off' }) + + # Remove the block device + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # This test modifies the node graph a few times by changing the + # 'backing' option on reopen and verifies that the guest data that + # is read afterwards is consistent with the graph changes. + def test_io_with_graph_changes(self): + opts = [] + + # Open hd0, hd1 and hd2 without any backing image + for i in range(3): + opts.append(hd_opts(i)) + opts[i]['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) + self.assert_qmp(result, 'return', {}) + + # hd0 + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0 1M 1M") + self.run_qemu_io("hd0", "read -P 0 2M 1M") + + # hd1 <- hd0 + self.reopen(opts[0], {'backing': 'hd1'}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd0", "read -P 0 2M 1M") + + # hd1 <- hd0 , hd1 <- hd2 + self.reopen(opts[2], {'backing': 'hd1'}) + + self.run_qemu_io("hd2", "read -P 0 0 1M") + self.run_qemu_io("hd2", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd2", "read -P 0xa2 2M 1M") + + # hd1 <- hd2 <- hd0 + self.reopen(opts[0], {'backing': 'hd2'}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") + + # hd2 <- hd0 + self.reopen(opts[2], {'backing': None}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0 1M 1M") + self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") + + # hd2 <- hd1 <- hd0 + self.reopen(opts[1], {'backing': 'hd2'}) + self.reopen(opts[0], {'backing': 'hd1'}) + + self.run_qemu_io("hd0", "read -P 0xa0 0 1M") + self.run_qemu_io("hd0", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd0", "read -P 0xa2 2M 1M") + + # Illegal operation: hd2 is a child of hd1 + self.reopen(opts[2], {'backing': 'hd1'}, + "Making 'hd1' a backing file of 'hd2' would create a cycle") + + # hd2 <- hd0, hd2 <- hd1 + self.reopen(opts[0], {'backing': 'hd2'}) + + self.run_qemu_io("hd1", "read -P 0 0 1M") + self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd1", "read -P 0xa2 2M 1M") + + # More illegal operations + self.reopen(opts[2], {'backing': 'hd1'}, + "Making 'hd1' a backing file of 'hd2' would create a cycle") + self.reopen(opts[2], {'file': 'hd0-file'}, "Cannot change the option 'file'") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'") + + # hd1 doesn't have a backing file now + self.reopen(opts[1], {'backing': None}) + + self.run_qemu_io("hd1", "read -P 0 0 1M") + self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd1", "read -P 0 2M 1M") + + # We can't remove the 'backing' option if the image has a + # default backing file + del opts[1]['backing'] + self.reopen(opts[1], {}, "backing is missing for 'hd1'") + + self.run_qemu_io("hd1", "read -P 0 0 1M") + self.run_qemu_io("hd1", "read -P 0xa1 1M 1M") + self.run_qemu_io("hd1", "read -P 0 2M 1M") + + # This test verifies that we can't change the children of a block + # device during a reopen operation in a way that would create + # cycles in the node graph + def test_graph_cycles(self): + opts = [] + + # Open all three images without backing file + for i in range(3): + opts.append(hd_opts(i)) + opts[i]['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts[i]) + self.assert_qmp(result, 'return', {}) + + # hd1 <- hd0, hd1 <- hd2 + self.reopen(opts[0], {'backing': 'hd1'}) + self.reopen(opts[2], {'backing': 'hd1'}) + + # Illegal: hd2 is backed by hd1 + self.reopen(opts[1], {'backing': 'hd2'}, + "Making 'hd2' a backing file of 'hd1' would create a cycle") + + # hd1 <- hd0 <- hd2 + self.reopen(opts[2], {'backing': 'hd0'}) + + # Illegal: hd2 is backed by hd0, which is backed by hd1 + self.reopen(opts[1], {'backing': 'hd2'}, + "Making 'hd2' a backing file of 'hd1' would create a cycle") + + # Illegal: hd1 cannot point to itself + self.reopen(opts[1], {'backing': 'hd1'}, + "Making 'hd1' a backing file of 'hd1' would create a cycle") + + # Remove all backing files + self.reopen(opts[0]) + self.reopen(opts[2]) + + ########################################## + # Add a blkverify node using hd0 and hd1 # + ########################################## + bvopts = {'driver': 'blkverify', + 'node-name': 'bv', + 'test': 'hd0', + 'raw': 'hd1'} + result = self.vm.qmp('blockdev-add', conv_keys = False, **bvopts) + self.assert_qmp(result, 'return', {}) + + # blkverify doesn't currently allow reopening. TODO: implement this + self.reopen(bvopts, {}, "Block format 'blkverify' used by node 'bv'" + + " does not support reopening files") + + # Illegal: hd0 is a child of the blkverify node + self.reopen(opts[0], {'backing': 'bv'}, + "Making 'bv' a backing file of 'hd0' would create a cycle") + + # Delete the blkverify node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bv') + self.assert_qmp(result, 'return', {}) + + # Misc reopen tests with different block drivers + def test_misc_drivers(self): + #################### + ###### quorum ###### + #################### + for i in range(3): + opts = hd_opts(i) + # Open all three images without backing file + opts['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + opts = {'driver': 'quorum', + 'node-name': 'quorum0', + 'children': ['hd0', 'hd1', 'hd2'], + 'vote-threshold': 2} + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Quorum doesn't currently allow reopening. TODO: implement this + self.reopen(opts, {}, "Block format 'quorum' used by node 'quorum0'" + + " does not support reopening files") + + # You can't make quorum0 a backing file of hd0: + # hd0 is already a child of quorum0. + self.reopen(hd_opts(0), {'backing': 'quorum0'}, + "Making 'quorum0' a backing file of 'hd0' would create a cycle") + + # Delete quorum0 + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'quorum0') + self.assert_qmp(result, 'return', {}) + + # Delete hd0, hd1 and hd2 + for i in range(3): + result = self.vm.qmp('blockdev-del', conv_keys = True, + node_name = 'hd%d' % i) + self.assert_qmp(result, 'return', {}) + + ###################### + ###### blkdebug ###### + ###################### + opts = {'driver': 'blkdebug', + 'node-name': 'bd', + 'config': '/dev/null', + 'image': hd_opts(0)} + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # blkdebug allows reopening if we keep the same options + self.reopen(opts) + + # but it currently does not allow changes + self.reopen(opts, {'image': 'hd1'}, "Cannot change the option 'image'") + self.reopen(opts, {'align': 33554432}, "Cannot change the option 'align'") + self.reopen(opts, {'config': '/non/existent'}, "Cannot change the option 'config'") + del opts['config'] + self.reopen(opts, {}, "Option 'config' cannot be reset to its default value") + + # Delete the blkdebug node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'bd') + self.assert_qmp(result, 'return', {}) + + ################## + ###### null ###### + ################## + opts = {'driver': 'null-aio', 'node-name': 'root', 'size': 1024} + + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # 1 << 30 is the default value, but we cannot change it explicitly + self.reopen(opts, {'size': (1 << 30)}, "Cannot change the option 'size'") + + # We cannot change 'size' back to its default value either + del opts['size'] + self.reopen(opts, {}, "Option 'size' cannot be reset to its default value") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'root') + self.assert_qmp(result, 'return', {}) + + ################## + ###### file ###### + ################## + opts = hd_opts(0) + opts['file']['locking'] = 'on' + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # 'locking' cannot be changed + del opts['file']['locking'] + self.reopen(opts, {'file.locking': 'on'}) + self.reopen(opts, {'file.locking': 'off'}, "Cannot change the option 'locking'") + self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") + + # Trying to reopen the 'file' node directly does not make a difference + opts = opts['file'] + self.reopen(opts, {'locking': 'on'}) + self.reopen(opts, {'locking': 'off'}, "Cannot change the option 'locking'") + self.reopen(opts, {}, "Option 'locking' cannot be reset to its default value") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + ###################### + ###### throttle ###### + ###################### + opts = { 'qom-type': 'throttle-group', 'id': 'group0', + 'props': { 'limits': { 'iops-total': 1000 } } } + result = self.vm.qmp('object-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + opts = { 'qom-type': 'throttle-group', 'id': 'group1', + 'props': { 'limits': { 'iops-total': 2000 } } } + result = self.vm.qmp('object-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Add a throttle filter with group = group0 + opts = { 'driver': 'throttle', 'node-name': 'throttle0', + 'throttle-group': 'group0', 'file': hd_opts(0) } + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # We can reopen it if we keep the same options + self.reopen(opts) + + # We can also reopen if 'file' is a reference to the child + self.reopen(opts, {'file': 'hd0'}) + + # This is illegal + self.reopen(opts, {'throttle-group': 'notfound'}, "Throttle group 'notfound' does not exist") + + # But it's possible to change the group to group1 + self.reopen(opts, {'throttle-group': 'group1'}) + + # Now group1 is in use, it cannot be deleted + result = self.vm.qmp('object-del', id = 'group1') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "object 'group1' is in use, can not be deleted") + + # Default options, this switches the group back to group0 + self.reopen(opts) + + # So now we cannot delete group0 + result = self.vm.qmp('object-del', id = 'group0') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "object 'group0' is in use, can not be deleted") + + # But group1 is free this time, and it can be deleted + result = self.vm.qmp('object-del', id = 'group1') + self.assert_qmp(result, 'return', {}) + + # Let's delete the filter node + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'throttle0') + self.assert_qmp(result, 'return', {}) + + # And we can finally get rid of group0 + result = self.vm.qmp('object-del', id = 'group0') + self.assert_qmp(result, 'return', {}) + + # If an image has a backing file then the 'backing' option must be + # passed on reopen. We don't allow leaving the option out in this + # case because it's unclear what the correct semantics would be. + def test_missing_backing_options_1(self): + # hd2 + opts = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # hd0 + opts = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # hd0 has no backing file: we can omit the 'backing' option + self.reopen(opts) + + # hd2 <- hd0 + self.reopen(opts, {'backing': 'hd2'}) + + # hd0 has a backing file: we must set the 'backing' option + self.reopen(opts, {}, "backing is missing for 'hd0'") + + # hd2 can't be removed because it's the backing file of hd0 + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') + self.assert_qmp(result, 'error/class', 'GenericError') + self.assert_qmp(result, 'error/desc', "Node 'hd2' is busy: node is used as backing hd of 'hd0'") + + # Detach hd2 from hd0. + self.reopen(opts, {'backing': None}) + self.reopen(opts, {}, "backing is missing for 'hd0'") + + # Remove both hd0 and hd2 + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd2') + self.assert_qmp(result, 'return', {}) + + # If an image has default backing file (as part of its metadata) + # then the 'backing' option must be passed on reopen. We don't + # allow leaving the option out in this case because it's unclear + # what the correct semantics would be. + def test_missing_backing_options_2(self): + # hd0 <- hd1 + # (hd0 is hd1's default backing file) + opts = hd_opts(1) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # hd1 has a backing file: we can't omit the 'backing' option + self.reopen(opts, {}, "backing is missing for 'hd1'") + + # Let's detach the backing file + self.reopen(opts, {'backing': None}) + + # No backing file attached to hd1 now, but we still can't omit the 'backing' option + self.reopen(opts, {}, "backing is missing for 'hd1'") + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd1') + self.assert_qmp(result, 'return', {}) + + # Test that making 'backing' a reference to an existing child + # keeps its current options + def test_backing_reference(self): + # hd2 <- hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = hd_opts(2) + # Enable 'detect-zeroes' on all three nodes + opts['detect-zeroes'] = 'on' + opts['backing']['detect-zeroes'] = 'on' + opts['backing']['backing']['detect-zeroes'] = 'on' + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Reopen the chain passing the minimum amount of required options. + # By making 'backing' a reference to hd1 (instead of a sub-dict) + # we tell QEMU to keep its current set of options. + opts = {'driver': iotests.imgfmt, + 'node-name': 'hd0', + 'file': 'hd0-file', + 'backing': 'hd1' } + self.reopen(opts) + + # This has reset 'detect-zeroes' on hd0, but not on hd1 and hd2. + self.assert_qmp(self.get_node('hd0'), 'detect_zeroes', 'off') + self.assert_qmp(self.get_node('hd1'), 'detect_zeroes', 'on') + self.assert_qmp(self.get_node('hd2'), 'detect_zeroes', 'on') + + # Test what happens if the graph changes due to other operations + # such as block-stream + def test_block_stream_1(self): + # hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = None + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Stream hd1 into hd0 and wait until it's done + result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', device = 'hd0') + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(drive = 'stream0') + + # Now we have only hd0 + self.assertEqual(self.get_node('hd1'), None) + + # We have backing.* options but there's no backing file anymore + self.reopen(opts, {}, "Cannot change the option 'backing.driver'") + + # If we remove the 'backing' option then we can reopen hd0 just fine + del opts['backing'] + self.reopen(opts) + + # We can also reopen hd0 if we set 'backing' to null + self.reopen(opts, {'backing': None}) + + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + + # Another block_stream test + def test_block_stream_2(self): + # hd2 <- hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # Stream hd1 into hd0 and wait until it's done + result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', + device = 'hd0', base_node = 'hd2') + self.assert_qmp(result, 'return', {}) + self.wait_until_completed(drive = 'stream0') + + # The chain is hd2 <- hd0 now. hd1 is missing + self.assertEqual(self.get_node('hd1'), None) + + # The backing options in the dict were meant for hd1, but we cannot + # use them with hd2 because hd1 had a backing file while hd2 does not. + self.reopen(opts, {}, "Cannot change the option 'backing.driver'") + + # If we remove hd1's options from the dict then things work fine + opts['backing'] = opts['backing']['backing'] + self.reopen(opts) + + # We can also reopen hd0 if we use a reference to the backing file + self.reopen(opts, {'backing': 'hd2'}) + + # But we cannot leave the option out + del opts['backing'] + self.reopen(opts, {}, "backing is missing for 'hd0'") + + # Now we can delete hd0 (and hd2) + result = self.vm.qmp('blockdev-del', conv_keys = True, node_name = 'hd0') + self.assert_qmp(result, 'return', {}) + self.assertEqual(self.get_node('hd2'), None) + + # Reopen the chain during a block-stream job (from hd1 to hd0) + def test_block_stream_3(self): + # hd2 <- hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # hd2 <- hd0 + result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', + device = 'hd0', base_node = 'hd2', speed = 512 * 1024) + self.assert_qmp(result, 'return', {}) + + # We can't remove hd2 while the stream job is ongoing + opts['backing']['backing'] = None + self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'") + + # We can't remove hd1 while the stream job is ongoing + opts['backing'] = None + self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'") + + self.wait_until_completed(drive = 'stream0') + + # Reopen the chain during a block-stream job (from hd2 to hd1) + def test_block_stream_4(self): + # hd2 <- hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + # hd1 <- hd0 + result = self.vm.qmp('block-stream', conv_keys = True, job_id = 'stream0', + device = 'hd1', speed = 512 * 1024) + self.assert_qmp(result, 'return', {}) + + # We can't reopen with the original options because that would + # make hd1 read-only and block-stream requires it to be read-write + self.reopen(opts, {}, "Can't set node 'hd1' to r/o with copy-on-read enabled") + + # We can't remove hd2 while the stream job is ongoing + opts['backing']['backing'] = None + self.reopen(opts, {'backing.read-only': False}, "Cannot change 'backing' link from 'hd1' to 'hd2'") + + # We can detach hd1 from hd0 because it doesn't affect the stream job + opts['backing'] = None + self.reopen(opts) + + self.wait_until_completed(drive = 'stream0') + + # Reopen the chain during a block-commit job (from hd0 to hd2) + def test_block_commit_1(self): + # hd2 <- hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0', + device = 'hd0', speed = 1024 * 1024) + self.assert_qmp(result, 'return', {}) + + # We can't remove hd2 while the commit job is ongoing + opts['backing']['backing'] = None + self.reopen(opts, {}, "Cannot change 'backing' link from 'hd1' to 'hd2'") + + # We can't remove hd1 while the commit job is ongoing + opts['backing'] = None + self.reopen(opts, {}, "Cannot change 'backing' link from 'hd0' to 'hd1'") + + event = self.vm.event_wait(name='BLOCK_JOB_READY') + self.assert_qmp(event, 'data/device', 'commit0') + self.assert_qmp(event, 'data/type', 'commit') + self.assert_qmp_absent(event, 'data/error') + + result = self.vm.qmp('block-job-complete', device='commit0') + self.assert_qmp(result, 'return', {}) + + self.wait_until_completed(drive = 'commit0') + + # Reopen the chain during a block-commit job (from hd1 to hd2) + def test_block_commit_2(self): + # hd2 <- hd1 <- hd0 + opts = hd_opts(0) + opts['backing'] = hd_opts(1) + opts['backing']['backing'] = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('block-commit', conv_keys = True, job_id = 'commit0', + device = 'hd0', top_node = 'hd1', speed = 1024 * 1024) + self.assert_qmp(result, 'return', {}) + + # We can't remove hd2 while the commit job is ongoing + opts['backing']['backing'] = None + self.reopen(opts, {}, "Cannot change the option 'backing.driver'") + + # We can't remove hd1 while the commit job is ongoing + opts['backing'] = None + self.reopen(opts, {}, "Cannot change backing link if 'hd0' has an implicit backing file") + + # hd2 <- hd0 + self.wait_until_completed(drive = 'commit0') + + self.assert_qmp(self.get_node('hd0'), 'ro', False) + self.assertEqual(self.get_node('hd1'), None) + self.assert_qmp(self.get_node('hd2'), 'ro', True) + + # We don't allow setting a backing file that uses a different AioContext + def test_iothreads(self): + opts = hd_opts(0) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts) + self.assert_qmp(result, 'return', {}) + + opts2 = hd_opts(2) + result = self.vm.qmp('blockdev-add', conv_keys = False, **opts2) + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('object-add', qom_type='iothread', id='iothread0') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('object-add', qom_type='iothread', id='iothread1') + self.assert_qmp(result, 'return', {}) + + result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd0', iothread='iothread0') + self.assert_qmp(result, 'return', {}) + + self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext") + + result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread1') + self.assert_qmp(result, 'return', {}) + + self.reopen(opts, {'backing': 'hd2'}, "Cannot use a new backing file with a different AioContext") + + result = self.vm.qmp('x-blockdev-set-iothread', node_name='hd2', iothread='iothread0') + self.assert_qmp(result, 'return', {}) + + self.reopen(opts, {'backing': 'hd2'}) + +if __name__ == '__main__': + iotests.main(supported_fmts=["qcow2"]) diff --git a/tests/qemu-iotests/245.out b/tests/qemu-iotests/245.out new file mode 100644 index 0000000000..71009c239f --- /dev/null +++ b/tests/qemu-iotests/245.out @@ -0,0 +1,5 @@ +.................. +---------------------------------------------------------------------- +Ran 18 tests + +OK diff --git a/tests/qemu-iotests/246 b/tests/qemu-iotests/246 new file mode 100755 index 0000000000..b0997a392f --- /dev/null +++ b/tests/qemu-iotests/246 @@ -0,0 +1,114 @@ +#!/usr/bin/env python +# +# Test persistent bitmap resizing. +# +# Copyright (c) 2019 John Snow for 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 . +# +# owner=jsnow@redhat.com + +import iotests +from iotests import log + +iotests.verify_image_format(supported_fmts=['qcow2']) +size = 64 * 1024 * 1024 * 1024 +gran_small = 32 * 1024 +gran_large = 128 * 1024 + +def query_bitmaps(vm): + res = vm.qmp("query-block") + return { "bitmaps": { device['device']: device.get('dirty-bitmaps', []) for + device in res['return'] } } + +with iotests.FilePath('img') as img_path, \ + iotests.VM() as vm: + + log('--- Preparing image & VM ---\n') + iotests.qemu_img_create('-f', iotests.imgfmt, img_path, str(size)) + vm.add_drive(img_path) + + + log('--- 1st Boot (Establish Baseline Image) ---\n') + vm.launch() + + log('\n--- Adding bitmaps Small, Medium, Large, and Transient ---\n') + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="Small", granularity=gran_small, persistent=True) + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="Medium", persistent=True) + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="Large", granularity=gran_large, persistent=True) + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="Transient", persistent=False) + + log('--- Forcing flush of bitmaps to disk ---\n') + log(query_bitmaps(vm), indent=2) + vm.shutdown() + + + log('--- 2nd Boot (Grow Image) ---\n') + vm.launch() + log(query_bitmaps(vm), indent=2) + + log('--- Adding new bitmap, growing image, and adding 2nd new bitmap ---') + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="New", persistent=True) + vm.qmp_log("human-monitor-command", + command_line="block_resize drive0 70G") + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="Newtwo", persistent=True) + log(query_bitmaps(vm), indent=2) + + log('--- Forcing flush of bitmaps to disk ---\n') + vm.shutdown() + + + log('--- 3rd Boot (Shrink Image) ---\n') + vm.launch() + log(query_bitmaps(vm), indent=2) + + log('--- Adding "NewB" bitmap, removing "New" bitmap ---') + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="NewB", persistent=True) + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", + name="New") + + log('--- Truncating image ---\n') + vm.qmp_log("human-monitor-command", + command_line="block_resize drive0 50G") + + log('--- Adding "NewC" bitmap, removing "NewTwo" bitmap ---') + vm.qmp_log("block-dirty-bitmap-add", node="drive0", + name="NewC", persistent=True) + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Newtwo") + + log('--- Forcing flush of bitmaps to disk ---\n') + vm.shutdown() + + + log('--- 4th Boot (Verification and Cleanup) ---\n') + vm.launch() + log(query_bitmaps(vm), indent=2) + + log('--- Removing all Bitmaps ---\n') + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Small") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Medium") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="Large") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="NewB") + vm.qmp_log("block-dirty-bitmap-remove", node="drive0", name="NewC") + log(query_bitmaps(vm), indent=2) + + log('\n--- Done ---') + vm.shutdown() diff --git a/tests/qemu-iotests/246.out b/tests/qemu-iotests/246.out new file mode 100644 index 0000000000..6671a11fdd --- /dev/null +++ b/tests/qemu-iotests/246.out @@ -0,0 +1,295 @@ +--- Preparing image & VM --- + +--- 1st Boot (Establish Baseline Image) --- + + +--- Adding bitmaps Small, Medium, Large, and Transient --- + +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 32768, "name": "Small", "node": "drive0", "persistent": true}} +{"return": {}} +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Medium", "node": "drive0", "persistent": true}} +{"return": {}} +{"execute": "block-dirty-bitmap-add", "arguments": {"granularity": 131072, "name": "Large", "node": "drive0", "persistent": true}} +{"return": {}} +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Transient", "node": "drive0", "persistent": false}} +{"return": {}} +--- Forcing flush of bitmaps to disk --- + +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Transient", + "persistent": false, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 131072, + "name": "Large", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Medium", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 32768, + "name": "Small", + "persistent": true, + "recording": true, + "status": "active" + } + ] + } +} +--- 2nd Boot (Grow Image) --- + +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 0, + "granularity": 32768, + "name": "Small", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Medium", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 131072, + "name": "Large", + "persistent": true, + "recording": true, + "status": "active" + } + ] + } +} +--- Adding new bitmap, growing image, and adding 2nd new bitmap --- +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "New", "node": "drive0", "persistent": true}} +{"return": {}} +{"execute": "human-monitor-command", "arguments": {"command-line": "block_resize drive0 70G"}} +{"return": ""} +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "Newtwo", "node": "drive0", "persistent": true}} +{"return": {}} +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Newtwo", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "New", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 32768, + "name": "Small", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Medium", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 131072, + "name": "Large", + "persistent": true, + "recording": true, + "status": "active" + } + ] + } +} +--- Forcing flush of bitmaps to disk --- + +--- 3rd Boot (Shrink Image) --- + +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "New", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Newtwo", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 32768, + "name": "Small", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Medium", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 131072, + "name": "Large", + "persistent": true, + "recording": true, + "status": "active" + } + ] + } +} +--- Adding "NewB" bitmap, removing "New" bitmap --- +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "NewB", "node": "drive0", "persistent": true}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "New", "node": "drive0"}} +{"return": {}} +--- Truncating image --- + +{"execute": "human-monitor-command", "arguments": {"command-line": "block_resize drive0 50G"}} +{"return": ""} +--- Adding "NewC" bitmap, removing "NewTwo" bitmap --- +{"execute": "block-dirty-bitmap-add", "arguments": {"name": "NewC", "node": "drive0", "persistent": true}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Newtwo", "node": "drive0"}} +{"return": {}} +--- Forcing flush of bitmaps to disk --- + +--- 4th Boot (Verification and Cleanup) --- + +{ + "bitmaps": { + "drive0": [ + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "NewB", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "NewC", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 32768, + "name": "Small", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 65536, + "name": "Medium", + "persistent": true, + "recording": true, + "status": "active" + }, + { + "busy": false, + "count": 0, + "granularity": 131072, + "name": "Large", + "persistent": true, + "recording": true, + "status": "active" + } + ] + } +} +--- Removing all Bitmaps --- + +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Small", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Medium", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "Large", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "NewB", "node": "drive0"}} +{"return": {}} +{"execute": "block-dirty-bitmap-remove", "arguments": {"name": "NewC", "node": "drive0"}} +{"return": {}} +{ + "bitmaps": { + "drive0": [] + } +} + +--- Done --- diff --git a/tests/qemu-iotests/247 b/tests/qemu-iotests/247 new file mode 100755 index 0000000000..fc50eb5dc1 --- /dev/null +++ b/tests/qemu-iotests/247 @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# +# Test for auto-read-only with commit block job +# +# Copyright (C) 2019 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" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + rm -f $TEST_IMG.[01234] +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +. ./common.rc +. ./common.filter + +# Requires backing files and .bdrv_change_backing_file support +_supported_fmt qcow2 qed +_supported_proto file +_supported_os Linux + +size=128M + +echo +echo "=== Try commit to backing file with auto-read-only ===" +echo +TEST_IMG="$TEST_IMG.0" _make_test_img $size +TEST_IMG="$TEST_IMG.1" _make_test_img $size +TEST_IMG="$TEST_IMG.2" _make_test_img $size +TEST_IMG="$TEST_IMG.3" _make_test_img $size +TEST_IMG="$TEST_IMG.4" _make_test_img $size + +(cat <. +# + +import iotests +from iotests import qemu_img_create, qemu_io, file_path, filter_qmp_testfiles + +iotests.verify_image_format(supported_fmts=['qcow2']) + +source, target = file_path('source', 'target') +size = 5 * 1024 * 1024 +limit = 2 * 1024 * 1024 + +qemu_img_create('-f', iotests.imgfmt, source, str(size)) +qemu_img_create('-f', iotests.imgfmt, target, str(size)) +qemu_io('-c', 'write 0 {}'.format(size), source) + +# raw format don't like empty files +qemu_io('-c', 'write 0 {}'.format(size), target) + +vm = iotests.VM().add_drive(source) +vm.launch() + +blockdev_opts = { + 'driver': iotests.imgfmt, + 'node-name': 'target', + 'file': { + 'driver': 'raw', + 'size': limit, + 'file': { + 'driver': 'file', + 'filename': target + } + } +} +vm.qmp_log('blockdev-add', filters=[filter_qmp_testfiles], **blockdev_opts) + +vm.qmp_log('blockdev-mirror', device='drive0', sync='full', target='target', + on_target_error='enospc') + +vm.event_wait('JOB_STATUS_CHANGE', timeout=3.0, + match={'data': {'status': 'paused'}}) + +# drop other cached events, to not interfere with further wait for 'running' +vm.get_qmp_events() + +del blockdev_opts['file']['size'] +vm.qmp_log('x-blockdev-reopen', filters=[filter_qmp_testfiles], + **blockdev_opts) + +vm.qmp_log('block-job-resume', device='drive0') +vm.event_wait('JOB_STATUS_CHANGE', timeout=1.0, + match={'data': {'status': 'running'}}) + +vm.shutdown() diff --git a/tests/qemu-iotests/248.out b/tests/qemu-iotests/248.out new file mode 100644 index 0000000000..369b25bf26 --- /dev/null +++ b/tests/qemu-iotests/248.out @@ -0,0 +1,8 @@ +{"execute": "blockdev-add", "arguments": {"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}, "size": 2097152}, "node-name": "target"}} +{"return": {}} +{"execute": "blockdev-mirror", "arguments": {"device": "drive0", "on-target-error": "enospc", "sync": "full", "target": "target"}} +{"return": {}} +{"execute": "x-blockdev-reopen", "arguments": {"driver": "qcow2", "file": {"driver": "raw", "file": {"driver": "file", "filename": "TEST_DIR/PID-target"}}, "node-name": "target"}} +{"return": {}} +{"execute": "block-job-resume", "arguments": {"device": "drive0"}} +{"return": {}} diff --git a/tests/qemu-iotests/COPYING b/tests/qemu-iotests/COPYING deleted file mode 100644 index 00ccfbb628..0000000000 --- a/tests/qemu-iotests/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - 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/tests/qemu-iotests/check b/tests/qemu-iotests/check index aa94c6c7ea..f9c24b6753 100755 --- a/tests/qemu-iotests/check +++ b/tests/qemu-iotests/check @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (C) 2009 Red Hat, Inc. # Copyright (c) 2000-2002,2006 Silicon Graphics, Inc. All Rights Reserved. @@ -25,6 +25,7 @@ try=0 n_bad=0 bad="" notrun="" +casenotrun="" interrupt=true # by default don't output timestamps @@ -80,17 +81,17 @@ _full_imgfmt_details() _full_platform_details() { - os=`uname -s` - host=`hostname -s` - kernel=`uname -r` - platform=`uname -m` + os=$(uname -s) + host=$(hostname -s) + kernel=$(uname -r) + platform=$(uname -m) echo "$os/$platform $host $kernel" } # $1 = prog to look for set_prog_path() { - p=`command -v $1 2> /dev/null` + p=$(command -v $1 2> /dev/null) if [ -n "$p" -a -x "$p" ]; then type -p "$p" else @@ -99,7 +100,7 @@ set_prog_path() } if [ -z "$TEST_DIR" ]; then - TEST_DIR=`pwd`/scratch + TEST_DIR=$PWD/scratch fi if [ ! -e "$TEST_DIR" ]; then @@ -147,9 +148,9 @@ do if $group then # arg after -g - group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ + group_list=$(sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ s/ .*//p -}'` +}') if [ -z "$group_list" ] then echo "Group \"$r\" is empty or not defined?" @@ -173,9 +174,9 @@ s/ .*//p # arg after -x # Populate $tmp.list with all tests awk '/^[0-9]{3,}/ {print $1}' "${source_iotests}/group" > $tmp.list 2>/dev/null - group_list=`sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ + group_list=$(sed -n <"$source_iotests/group" -e 's/$/ /' -e "/^[0-9][0-9][0-9].* $r /"'{ s/ .*//p -}'` +}') if [ -z "$group_list" ] then echo "Group \"$r\" is empty or not defined?" @@ -193,7 +194,7 @@ s/ .*//p rm -f $tmp.sed fi echo "/^$t\$/d" >>$tmp.sed - numsed=`expr $numsed + 1` + numsed=$(expr $numsed + 1) done sed -f $tmp.sed <$tmp.list >$tmp.tmp mv $tmp.tmp $tmp.list @@ -237,6 +238,7 @@ image format options -vhdx test vhdx -vmdk test vmdk -luks test luks + -dmg test dmg image protocol options -file test file (default) @@ -304,6 +306,12 @@ testlist options xpand=false ;; + -dmg) + IMGFMT=dmg + IMGFMT_GENERIC=false + xpand=false + ;; + -qed) IMGFMT=qed xpand=false @@ -433,12 +441,12 @@ testlist options ;; [0-9]*-[0-9]*) - eval `echo $r | sed -e 's/^/start=/' -e 's/-/ end=/'` + eval $(echo $r | sed -e 's/^/start=/' -e 's/-/ end=/') ;; [0-9]*-) - eval `echo $r | sed -e 's/^/start=/' -e 's/-//'` - end=`echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //'` + eval $(echo $r | sed -e 's/^/start=/' -e 's/-//') + end=$(echo [0-9][0-9][0-9] [0-9][0-9][0-9][0-9] | sed -e 's/\[0-9]//g' -e 's/ *$//' -e 's/.* //') if [ -z "$end" ] then echo "No tests in range \"$r\"?" @@ -455,8 +463,8 @@ testlist options esac # get rid of leading 0s as can be interpreted as octal - start=`echo $start | sed 's/^0*//'` - end=`echo $end | sed 's/^0*//'` + start=$(echo $start | sed 's/^0*//') + end=$(echo $end | sed 's/^0*//') if $xpand then @@ -531,7 +539,7 @@ fi # should be sort -n, but this did not work for Linux when this # was ported from IRIX # -list=`sort $tmp.list` +list=$(sort $tmp.list) rm -f $tmp.list $tmp.tmp $tmp.sed if [ -z "$QEMU_PROG" ] @@ -590,7 +598,7 @@ fi export QEMU_NBD_PROG="$(type -p "$QEMU_NBD_PROG")" if [ -z "$QEMU_VXHS_PROG" ]; then - export QEMU_VXHS_PROG="`set_prog_path qnio_server`" + export QEMU_VXHS_PROG="$(set_prog_path qnio_server)" fi if [ -x "$build_iotests/socket_scm_helper" ] @@ -616,7 +624,7 @@ _wallclock() _timestamp() { - now=`date "+%T"` + now=$(date "+%T") printf %s " [$now]" } @@ -642,9 +650,9 @@ END { if (NR > 0) { if [ -f $tmp.expunged ] then - notrun=`wc -l <$tmp.expunged | sed -e 's/ *//g'` - try=`expr $try - $notrun` - list=`echo "$list" | sed -f $tmp.expunged` + notrun=$(wc -l <$tmp.expunged | sed -e 's/ *//g') + try=$(expr $try - $notrun) + list=$(echo "$list" | sed -f $tmp.expunged) fi echo "" >>check.log @@ -657,6 +665,11 @@ END { if (NR > 0) { echo "Not run:$notrun" echo "Not run:$notrun" >>check.log fi + if [ ! -z "$casenotrun" ] + then + echo "Some cases not run in:$casenotrun" + echo "Some cases not run in:$casenotrun" >>check.log + fi if [ ! -z "$n_bad" -a $n_bad != 0 ] then echo "Failures:$bad" @@ -682,8 +695,8 @@ trap "_wrapup; exit \$status" 0 1 2 3 15 [ -f $TIMESTAMP_FILE ] || touch $TIMESTAMP_FILE -FULL_IMGFMT_DETAILS=`_full_imgfmt_details` -FULL_HOST_DETAILS=`_full_platform_details` +FULL_IMGFMT_DETAILS=$(_full_imgfmt_details) +FULL_HOST_DETAILS=$(_full_platform_details) cat <>$tmp.time + echo "$seq $(expr $stop - $start)" >>$tmp.time fi else echo " - output mismatch (see $seq.out.bad)" @@ -816,7 +830,11 @@ do fi fi fi - + if [ -f $seq.casenotrun ] + then + cat $seq.casenotrun + casenotrun="$casenotrun $seq" + fi fi # come here for each test, except when $showme is true @@ -824,14 +842,14 @@ do if $err then bad="$bad $seq" - n_bad=`expr $n_bad + 1` + n_bad=$(expr $n_bad + 1) quick=false fi - [ -f $seq.notrun ] || try=`expr $try + 1` + [ -f $seq.notrun ] || try=$(expr $try + 1) seq="after_$seq" done interrupt=false -status=`expr $n_bad` +status=$(expr $n_bad) exit diff --git a/tests/qemu-iotests/common.config b/tests/qemu-iotests/common.config index 102aa6878a..9bd1a5a6fc 100644 --- a/tests/qemu-iotests/common.config +++ b/tests/qemu-iotests/common.config @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (C) 2009 Red Hat, Inc. # Copyright (c) 2000-2003,2006 Silicon Graphics, Inc. All Rights Reserved. @@ -21,12 +21,10 @@ export LANG=C PATH=".:$PATH" -HOSTOS=`uname -s` -arch=`uname -m` +HOSTOS=$(uname -s) +arch=$(uname -m) [[ "$arch" =~ "ppc64" ]] && qemu_arch=ppc64 || qemu_arch="$arch" -export PWD=`pwd` - # make sure we have a standard umask umask 022 diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 2031e353a5..35fddc746f 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (C) 2009 Red Hat, Inc. # Copyright (c) 2000-2001 Silicon Graphics, Inc. All Rights Reserved. @@ -23,37 +23,37 @@ # _filter_date() { - sed \ + $SED \ -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/' + $SED -re 's/\#block[0-9]{3,}/NODE_NAME/' } _filter_qom_path() { - sed -e 's#\(Attached to: *\) /.*#\1 PATH#' + $SED -e 's#\(Attached to: *\) /.*#\1 PATH#' } # replace occurrences of the actual TEST_DIR value with TEST_DIR _filter_testdir() { - sed -e "s#$TEST_DIR/#TEST_DIR/#g" + $SED -e "s#$TEST_DIR/#TEST_DIR/#g" } # replace occurrences of the actual IMGFMT value with IMGFMT _filter_imgfmt() { - sed -e "s#$IMGFMT#IMGFMT#g" + $SED -e "s#$IMGFMT#IMGFMT#g" } # Replace error message when the format is not supported and delete # the output lines after the first one _filter_qemu_img_check() { - sed -e '/allocated.*fragmented.*compressed clusters/d' \ + $SED -e '/allocated.*fragmented.*compressed clusters/d' \ -e 's/qemu-img: This image format does not support checks/No errors were found on the image./' \ -e '/Image end offset: [0-9]\+/d' } @@ -61,13 +61,13 @@ _filter_qemu_img_check() # Removes \r from messages _filter_win32() { - sed -e 's/\r//g' + $SED -e 's/\r//g' } # sanitize qemu-io output _filter_qemu_io() { - _filter_win32 | sed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ + _filter_win32 | $SED -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\)/:\1/" \ -e "s/qemu-io> //g" } @@ -75,7 +75,7 @@ _filter_qemu_io() # replace occurrences of QEMU_PROG with "qemu" _filter_qemu() { - sed -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ + $SED -e "s#\\(^\\|(qemu) \\)$(basename $QEMU_PROG):#\1QEMU_PROG:#" \ -e 's#^QEMU [0-9]\+\.[0-9]\+\.[0-9]\+ monitor#QEMU X.Y.Z monitor#' \ -e $'s#\r##' # QEMU monitor uses \r\n line endings } @@ -84,7 +84,7 @@ _filter_qemu() _filter_qmp() { _filter_win32 | \ - sed -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \ + $SED -e 's#\("\(micro\)\?seconds": \)[0-9]\+#\1 TIMESTAMP#g' \ -e 's#^{"QMP":.*}$#QMP_VERSION#' \ -e '/^ "QMP": {\s*$/, /^ }\s*$/ c\' \ -e ' QMP_VERSION' @@ -93,32 +93,32 @@ _filter_qmp() # readline makes HMP command strings so long that git complains _filter_hmp() { - sed -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \ + $SED -e $'s/^\\((qemu) \\)\\?.*\e\\[D/\\1/g' \ -e $'s/\e\\[K//g' } # replace block job offset _filter_block_job_offset() { - sed -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/' + $SED -e 's/, "offset": [0-9]\+,/, "offset": OFFSET,/' } # replace block job len _filter_block_job_len() { - sed -e 's/, "len": [0-9]\+,/, "len": LEN,/g' + $SED -e 's/, "len": [0-9]\+,/, "len": LEN,/g' } # replace actual image size (depends on the host filesystem) _filter_actual_image_size() { - sed -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' + $SED -s 's/\("actual-size":\s*\)[0-9]\+/\1SIZE/g' } # replace driver-specific options in the "Formatting..." line _filter_img_create() { - sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ @@ -154,7 +154,7 @@ _filter_img_info() discard=0 regex_json_spec_start='^ *"format-specific": \{' - sed -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ + $SED -e "s#$REMOTE_TEST_DIR#TEST_DIR#g" \ -e "s#$IMGPROTO:$TEST_DIR#TEST_DIR#g" \ -e "s#$TEST_DIR#TEST_DIR#g" \ -e "s#$IMGFMT#IMGFMT#g" \ @@ -165,6 +165,7 @@ _filter_img_info() -e "/table_size: [0-9]\\+/d" \ -e "/compat: '[^']*'/d" \ -e "/compat6: \\(on\\|off\\)/d" \ + -e "s/cid: [0-9]\+/cid: XXXXXXXXXX/" \ -e "/static: \\(on\\|off\\)/d" \ -e "/zeroed_grain: \\(on\\|off\\)/d" \ -e "/subformat: '[^']*'/d" \ @@ -200,7 +201,7 @@ _filter_img_info() # human and json output _filter_qemu_img_map() { - sed -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \ + $SED -e 's/\([0-9a-fx]* *[0-9a-fx]* *\)[0-9a-fx]* */\1/g' \ -e 's/"offset": [0-9]\+/"offset": OFFSET/g' \ -e 's/Mapped to *//' | _filter_testdir | _filter_imgfmt } @@ -212,7 +213,7 @@ _filter_nbd() # receive callbacks sometimes, making them unreliable. # # Filter out the TCP port number since this changes between runs. - sed -e '/nbd\/.*\.c:/d' \ + $SED -e '/nbd\/.*\.c:/d' \ -e 's#127\.0\.0\.1:[0-9]*#127.0.0.1:PORT#g' \ -e "s#?socket=$TEST_DIR#?socket=TEST_DIR#g" \ -e 's#\(foo\|PORT/\?\|.sock\): Failed to .*$#\1#' diff --git a/tests/qemu-iotests/common.nbd b/tests/qemu-iotests/common.nbd new file mode 100644 index 0000000000..25fc9ffaa4 --- /dev/null +++ b/tests/qemu-iotests/common.nbd @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# -*- shell-script-mode -*- +# +# Helpers for NBD server related config +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +nbd_unix_socket="${TEST_DIR}/qemu-nbd.sock" +nbd_tcp_addr="127.0.0.1" +nbd_pid_file="${TEST_DIR}/qemu-nbd.pid" + +nbd_server_stop() +{ + local NBD_PID + if [ -f "$nbd_pid_file" ]; then + read NBD_PID < "$nbd_pid_file" + rm -f "$nbd_pid_file" + if [ -n "$NBD_PID" ]; then + kill "$NBD_PID" + fi + fi + rm -f "$nbd_unix_socket" +} + +nbd_server_wait_for_unix_socket() +{ + pid=$1 + + for ((i = 0; i < 300; i++)) + do + if [ -r "$nbd_unix_socket" ]; then + return + fi + kill -s 0 $pid 2>/dev/null + if test $? != 0 + then + echo "qemu-nbd unexpectedly quit" + exit 1 + fi + sleep 0.1 + done + echo "Failed in check of unix socket created by qemu-nbd" + exit 1 +} + +nbd_server_start_unix_socket() +{ + nbd_server_stop + $QEMU_NBD -v -t -k "$nbd_unix_socket" "$@" & + nbd_server_wait_for_unix_socket $! +} + +nbd_server_set_tcp_port() +{ + (ss --help) >/dev/null 2>&1 || _notrun "ss utility not found, skipping test" + + for ((port = 10809; port <= 10909; port++)) + do + if ! ss -tln | grep -sqE ":$port\b"; then + nbd_tcp_port=$port + return + fi + done + + echo "Cannot find free TCP port for nbd in range 10809-10909" + exit 1 +} + +nbd_server_wait_for_tcp_socket() +{ + pid=$1 + + for ((i = 0; i < 300; i++)) + do + if ss -tln | grep -sqE ":$nbd_tcp_port\b"; then + return + fi + kill -s 0 $pid 2>/dev/null + if test $? != 0 + then + echo "qemu-nbd unexpectedly quit" + exit 1 + fi + sleep 0.1 + done + echo "Failed in check of TCP socket created by qemu-nbd" + exit 1 +} + +nbd_server_start_tcp_socket() +{ + nbd_server_stop + $QEMU_NBD -v -t -b $nbd_tcp_addr -p $nbd_tcp_port "$@" & + nbd_server_wait_for_tcp_socket $! +} diff --git a/tests/qemu-iotests/common.pattern b/tests/qemu-iotests/common.pattern index 34f4a8dc9b..25aa0d01c1 100644 --- a/tests/qemu-iotests/common.pattern +++ b/tests/qemu-iotests/common.pattern @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (C) 2009 Red Hat, Inc. # @@ -16,7 +16,7 @@ # along with this program. If not, see . # -function do_is_allocated() { +do_is_allocated() { local start=$1 local size=$2 local step=$3 @@ -27,11 +27,11 @@ function do_is_allocated() { done } -function is_allocated() { +is_allocated() { do_is_allocated "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } -function do_io() { +do_io() { local op=$1 local start=$2 local size=$3 @@ -45,22 +45,22 @@ function do_io() { done } -function io_pattern() { +io_pattern() { do_io "$@" | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } -function io() { +io() { local start=$2 local pattern=$(( (start >> 9) % 256 )) do_io "$@" $pattern | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } -function io_zero() { +io_zero() { do_io "$@" 0 | $QEMU_IO "$TEST_IMG" | _filter_qemu_io } -function io_test() { +io_test() { local op=$1 local offset=$2 local cluster_size=$3 @@ -100,7 +100,7 @@ function io_test() { offset=$((offset + num_large * ( l2_size + half_cluster ))) } -function io_test2() { +io_test2() { local orig_offset=$1 local cluster_size=$2 local num=$3 diff --git a/tests/qemu-iotests/common.qemu b/tests/qemu-iotests/common.qemu index f285484951..8d2021a7eb 100644 --- a/tests/qemu-iotests/common.qemu +++ b/tests/qemu-iotests/common.qemu @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # This allows for launching of multiple QEMU instances, with independent # communication possible to each instance. @@ -60,7 +60,7 @@ _in_fd=4 # $3: A string to search for in the response; if found, this indicates # failure and the test is either aborted (if $qemu_error_no_exit # is not set) or ${QEMU_STATUS[$1]} is set to -1 (otherwise). -function _timed_wait_for() +_timed_wait_for() { local h=${1} shift @@ -131,7 +131,7 @@ function _timed_wait_for() # strings the response will be scanned for. The first of the two # indicates success, the latter indicates failure. Failure is handled # like a timeout. -function _send_qemu_cmd() +_send_qemu_cmd() { local h=${1} local count=1 @@ -186,7 +186,7 @@ function _send_qemu_cmd() # Returns: # $QEMU_HANDLE: set to a handle value to communicate with this QEMU instance. # -function _launch_qemu() +_launch_qemu() { local comm= local fifo_out= @@ -257,12 +257,12 @@ function _launch_qemu() } -# Silenty kills the QEMU process +# Silently kills the QEMU process # # If $wait is set to anything other than the empty string, the process will not # be killed but only waited for, and any output will be forwarded to stdout. If # $wait is empty, the process will be killed and all output will be suppressed. -function _cleanup_qemu() +_cleanup_qemu() { # QEMU_PID[], QEMU_IN[], QEMU_OUT[] all use same indices for i in "${!QEMU_OUT[@]}" diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 44bee16a5e..a543e546c2 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # # Copyright (C) 2009 Red Hat, Inc. # Copyright (c) 2000-2006 Silicon Graphics, Inc. All Rights Reserved. @@ -17,6 +17,19 @@ # along with this program. If not, see . # +SED= +for sed in sed gsed; do + ($sed --version | grep 'GNU sed') > /dev/null 2>&1 + if [ "$?" -eq 0 ]; then + SED=$sed + break + fi +done +if [ -z "$SED" ]; then + echo "$0: GNU sed not found" + exit 1 +fi + dd() { if [ "$HOSTOS" == "Linux" ] @@ -145,6 +158,7 @@ else TEST_IMG="nbd:127.0.0.1:10810" elif [ "$IMGPROTO" = "ssh" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT + REMOTE_TEST_DIR="ssh://127.0.0.1$TEST_DIR" TEST_IMG="ssh://127.0.0.1$TEST_IMG_FILE" elif [ "$IMGPROTO" = "nfs" ]; then TEST_IMG_FILE=$TEST_DIR/t.$IMGFMT @@ -160,7 +174,7 @@ fi ORIG_TEST_IMG="$TEST_IMG" if [ -z "$TEST_DIR" ]; then - TEST_DIR=`pwd`/scratch + TEST_DIR=$PWD/scratch fi QEMU_TEST_DIR="${TEST_DIR}" @@ -170,7 +184,7 @@ if [ ! -e "$TEST_DIR" ]; then fi if [ ! -d "$TEST_DIR" ]; then - echo "common.config: Error: \$TEST_DIR ($TEST_DIR) is not a directory" + echo "common.rc: Error: \$TEST_DIR ($TEST_DIR) is not a directory" exit 1 fi @@ -179,7 +193,7 @@ if [ -z "$REMOTE_TEST_DIR" ]; then fi if [ ! -d "$SAMPLE_IMG_DIR" ]; then - echo "common.config: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" + echo "common.rc: Error: \$SAMPLE_IMG_DIR ($SAMPLE_IMG_DIR) is not a directory" exit 1 fi diff --git a/tests/qemu-iotests/common.tls b/tests/qemu-iotests/common.tls new file mode 100644 index 0000000000..54c331d7a5 --- /dev/null +++ b/tests/qemu-iotests/common.tls @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +# +# Helpers for TLS related config +# +# Copyright (C) 2018 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +tls_dir="${TEST_DIR}/tls" + +tls_x509_cleanup() +{ + rm -f "${tls_dir}"/*.pem + rm -f "${tls_dir}"/*/*.pem + rmdir "${tls_dir}"/* + rmdir "${tls_dir}" +} + + +tls_certtool() +{ + certtool "$@" 1>"${tls_dir}"/certtool.log 2>&1 + if test "$?" = 0; then + head -1 "${tls_dir}"/certtool.log + else + cat "${tls_dir}"/certtool.log + fi + rm -f "${tls_dir}"/certtool.log +} + +tls_x509_init() +{ + (certtool --help) >/dev/null 2>&1 || \ + _notrun "certtool utility not found, skipping test" + + mkdir -p "${tls_dir}" + + # use a fixed key so we don't waste system entropy on + # each test run + cat > "${tls_dir}/key.pem" < "${tls_dir}/ca.info" < "${tls_dir}/cert.info" < "${tls_dir}/cert.info" < ' def close(self): @@ -178,6 +196,7 @@ class QemuIoInteractive: cmd = cmd.strip() assert cmd != 'q' and cmd != 'quit' self._p.stdin.write(cmd + '\n') + self._p.stdin.flush() return self._read_output() @@ -185,6 +204,20 @@ def qemu_nbd(*args): '''Run qemu-nbd in daemon mode and return the parent's exit code''' return subprocess.call(qemu_nbd_args + ['--fork'] + list(args)) +def qemu_nbd_pipe(*args): + '''Run qemu-nbd in daemon mode and return both the parent's exit code + and its output''' + subp = subprocess.Popen(qemu_nbd_args + ['--fork'] + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + exitcode = subp.wait() + if exitcode < 0: + sys.stderr.write('qemu-nbd received signal %i: %s\n' % + (-exitcode, + ' '.join(qemu_nbd_args + ['--fork'] + list(args)))) + return exitcode, subp.communicate()[0] + def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): '''Return True if two image files are identical''' return qemu_img('compare', '-f', fmt1, @@ -192,10 +225,10 @@ def compare_images(img1, img2, fmt1=imgfmt, fmt2=imgfmt): def create_image(name, size): '''Create a fully-allocated raw image with sector markers''' - file = open(name, 'w') + file = open(name, 'wb') i = 0 while i < size: - sector = struct.pack('>l504xl', i / 512, i / 512) + sector = struct.pack('>l504xl', i // 512, i // 512) file.write(sector) i = i + 512 file.close() @@ -205,6 +238,12 @@ def image_size(img): r = qemu_img_pipe('info', '--output=json', '-f', imgfmt, img) return json.loads(r)['virtual-size'] +def is_str(val): + if sys.version_info.major >= 3: + return isinstance(val, str) + else: + return isinstance(val, str) or isinstance(val, unicode) + test_dir_re = re.compile(r"%s" % test_dir) def filter_test_dir(msg): return test_dir_re.sub("TEST_DIR", msg) @@ -230,10 +269,36 @@ def filter_qmp_event(event): event['timestamp']['microseconds'] = 'USECS' return event +def filter_qmp(qmsg, filter_fn): + '''Given a string filter, filter a QMP object's values. + filter_fn takes a (key, value) pair.''' + # Iterate through either lists or dicts; + if isinstance(qmsg, list): + items = enumerate(qmsg) + else: + items = qmsg.items() + + for k, v in items: + if isinstance(v, list) or isinstance(v, dict): + qmsg[k] = filter_qmp(v, filter_fn) + else: + qmsg[k] = filter_fn(k, v) + return qmsg + def filter_testfiles(msg): prefix = os.path.join(test_dir, "%s-" % (os.getpid())) return msg.replace(prefix, 'TEST_DIR/PID-') +def filter_qmp_testfiles(qmsg): + def _filter(key, value): + if is_str(value): + return filter_testfiles(value) + return value + return filter_qmp(qmsg, _filter) + +def filter_generated_node_ids(msg): + return re.sub("#block[0-9]+", "NODE_NAME", msg) + def filter_img_info(output, filename): lines = [] for line in output.split('\n'): @@ -243,13 +308,34 @@ def filter_img_info(output, filename): .replace(imgfmt, 'IMGFMT') line = re.sub('iters: [0-9]+', 'iters: XXX', line) line = re.sub('uuid: [-a-f0-9]+', 'uuid: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', line) + line = re.sub('cid: [0-9]+', 'cid: XXXXXXXXXX', line) lines.append(line) return '\n'.join(lines) -def log(msg, filters=[]): +def filter_imgfmt(msg): + return msg.replace(imgfmt, 'IMGFMT') + +def filter_qmp_imgfmt(qmsg): + def _filter(key, value): + if is_str(value): + return filter_imgfmt(value) + return value + return filter_qmp(qmsg, _filter) + +def log(msg, filters=[], indent=None): + '''Logs either a string message or a JSON serializable message (like QMP). + If indent is provided, JSON serializable messages are pretty-printed.''' for flt in filters: msg = flt(msg) - print(msg) + if isinstance(msg, dict) or isinstance(msg, list): + # Python < 3.4 needs to know not to add whitespace when pretty-printing: + separators = (', ', ': ') if indent is None else (',', ': ') + # Don't sort if it's already sorted + do_sort = not isinstance(msg, OrderedDict) + print(json.dumps(msg, sort_keys=do_sort, + indent=indent, separators=separators)) + else: + print(msg) class Timeout: def __init__(self, seconds, errmsg = "Timeout"): @@ -436,14 +522,19 @@ class VM(qtest.QEMUQtestMachine): result.append(filter_qmp_event(ev)) return result - def qmp_log(self, cmd, filters=[filter_testfiles], **kwargs): - logmsg = "{'execute': '%s', 'arguments': %s}" % (cmd, kwargs) - log(logmsg, filters) + def qmp_log(self, cmd, filters=[], indent=None, **kwargs): + full_cmd = OrderedDict(( + ("execute", cmd), + ("arguments", ordered_qmp(kwargs)) + )) + log(full_cmd, filters, indent=indent) result = self.qmp(cmd, **kwargs) - log(str(result), filters) + log(result, filters, indent=indent) return result + # Returns None on success, and an error string on failure def run_job(self, job, auto_finalize=True, auto_dismiss=False): + error = None while True: for ev in self.get_qmp_events_filtered(wait=True): if ev['event'] == 'JOB_STATUS_CHANGE': @@ -452,16 +543,24 @@ class VM(qtest.QEMUQtestMachine): result = self.qmp('query-jobs') for j in result['return']: if j['id'] == job: + error = j['error'] log('Job failed: %s' % (j['error'])) elif status == 'pending' and not auto_finalize: self.qmp_log('job-finalize', id=job) elif status == 'concluded' and not auto_dismiss: self.qmp_log('job-dismiss', id=job) elif status == 'null': - return + return error else: iotests.log(ev) + def node_info(self, node_name): + nodes = self.qmp('query-named-block-nodes') + for x in nodes['return']: + if x['node-name'] == node_name: + return x + return None + index_re = re.compile(r'([^\[]+)\[([^\]]+)\]') @@ -572,7 +671,7 @@ class QMPTestCase(unittest.TestCase): def wait_ready_and_cancel(self, drive='drive0'): self.wait_ready(drive=drive) event = self.cancel_and_wait(drive=drive) - self.assertEquals(event['event'], 'BLOCK_JOB_COMPLETED') + self.assertEqual(event['event'], 'BLOCK_JOB_COMPLETED') self.assert_qmp(event, 'data/type', 'mirror') self.assert_qmp(event, 'data/offset', event['data']['len']) @@ -613,10 +712,18 @@ def notrun(reason): # Each test in qemu-iotests has a number ("seq") seq = os.path.basename(sys.argv[0]) - open('%s/%s.notrun' % (output_dir, seq), 'wb').write(reason + '\n') + open('%s/%s.notrun' % (output_dir, seq), 'w').write(reason + '\n') print('%s not run: %s' % (seq, reason)) sys.exit(0) +def case_notrun(reason): + '''Skip this test case''' + # Each test in qemu-iotests has a number ("seq") + seq = os.path.basename(sys.argv[0]) + + open('%s/%s.casenotrun' % (output_dir, seq), 'a').write( + ' [case not run] ' + reason + '\n') + def verify_image_format(supported_fmts=[], unsupported_fmts=[]): assert not (supported_fmts and unsupported_fmts) @@ -657,6 +764,41 @@ def verify_quorum(): if not supports_quorum(): notrun('quorum support missing') +def qemu_pipe(*args): + '''Run qemu with an option to print something and exit (e.g. a help option), + and return its output''' + args = [qemu_prog] + qemu_opts + list(args) + subp = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + universal_newlines=True) + exitcode = subp.wait() + if exitcode < 0: + sys.stderr.write('qemu received signal %i: %s\n' % (-exitcode, + ' '.join(args))) + return subp.communicate()[0] + +def supported_formats(read_only=False): + '''Set 'read_only' to True to check ro-whitelist + Otherwise, rw-whitelist is checked''' + format_message = qemu_pipe("-drive", "format=help") + line = 1 if read_only else 0 + return format_message.splitlines()[line].split(":")[1].split() + +def skip_if_unsupported(required_formats=[], read_only=False): + '''Skip Test Decorator + Runs the test if all the required formats are whitelisted''' + def skip_test_decorator(func): + def func_wrapper(*args, **kwargs): + usf_list = list(set(required_formats) - + set(supported_formats(read_only))) + if usf_list: + case_notrun('{}: formats {} are not whitelisted'.format( + args[0], usf_list)) + else: + return func(*args, **kwargs) + return func_wrapper + return skip_test_decorator + def main(supported_fmts=[], supported_oses=['linux'], supported_cache_modes=[], unsupported_fmts=[]): '''Run tests''' @@ -677,15 +819,19 @@ def main(supported_fmts=[], supported_oses=['linux'], supported_cache_modes=[], verify_platform(supported_oses) verify_cache_mode(supported_cache_modes) - # We need to filter out the time taken from the output so that qemu-iotest - # can reliably diff the results against master output. - import StringIO if debug: output = sys.stdout verbosity = 2 sys.argv.remove('-d') else: - output = StringIO.StringIO() + # We need to filter out the time taken from the output so that + # qemu-iotest can reliably diff the results against master output. + if sys.version_info.major >= 3: + output = io.StringIO() + else: + # io.StringIO is for unicode strings, which is not what + # 2.x's test runner emits. + output = io.BytesIO() logging.basicConfig(level=(logging.DEBUG if debug else logging.WARN)) diff --git a/tests/qemu-iotests/nbd-fault-injector.py b/tests/qemu-iotests/nbd-fault-injector.py index f9193c0fae..6b2d659dee 100755 --- a/tests/qemu-iotests/nbd-fault-injector.py +++ b/tests/qemu-iotests/nbd-fault-injector.py @@ -48,7 +48,10 @@ import sys import socket import struct import collections -import ConfigParser +if sys.version_info.major >= 3: + import configparser +else: + import ConfigParser as configparser FAKE_DISK_SIZE = 8 * 1024 * 1024 * 1024 # 8 GB @@ -86,7 +89,7 @@ def recvall(sock, bufsize): raise Exception('unexpected disconnect') chunks.append(chunk) received += len(chunk) - return ''.join(chunks) + return b''.join(chunks) class Rule(object): def __init__(self, name, event, io, when): @@ -112,6 +115,7 @@ class FaultInjectionSocket(object): if rule.match(event, io): if rule.when == 0 or bufsize is None: print('Closing connection on rule match %s' % rule.name) + self.sock.flush() sys.exit(0) if rule.when != -1: return rule.when @@ -176,7 +180,7 @@ def handle_connection(conn, use_export): req = read_request(conn) if req.type == NBD_CMD_READ: write_reply(conn, 0, req.handle) - conn.send('\0' * req.len, event='data') + conn.send(b'\0' * req.len, event='data') elif req.type == NBD_CMD_WRITE: _ = conn.recv(req.len, event='data') write_reply(conn, 0, req.handle) @@ -224,7 +228,7 @@ def parse_config(config): return rules def load_rules(filename): - config = ConfigParser.RawConfigParser() + config = configparser.RawConfigParser() with open(filename, 'rt') as f: config.readfp(f, filename) return parse_config(config) diff --git a/tests/qemu-iotests/qcow2.py b/tests/qemu-iotests/qcow2.py index b95a837759..b392972d1b 100755 --- a/tests/qemu-iotests/qcow2.py +++ b/tests/qemu-iotests/qcow2.py @@ -10,7 +10,7 @@ class QcowHeaderExtension: def __init__(self, magic, length, data): if length % 8 != 0: padding = 8 - (length % 8) - data += "\0" * padding + data += b"\0" * padding self.magic = magic self.length = length @@ -103,7 +103,7 @@ class QcowHeader: fd.seek(self.header_length) extensions = self.extensions - extensions.append(QcowHeaderExtension(0, 0, "")) + extensions.append(QcowHeaderExtension(0, 0, b"")) for ex in extensions: buf = struct.pack('>II', ex.magic, ex.length) fd.write(buf) @@ -137,8 +137,8 @@ class QcowHeader: for ex in self.extensions: data = ex.data[:ex.length] - if all(c in string.printable for c in data): - data = "'%s'" % data + if all(c in string.printable.encode('ascii') for c in data): + data = "'%s'" % data.decode('ascii') else: data = "" @@ -178,7 +178,7 @@ def cmd_add_header_ext(fd, magic, data): sys.exit(1) h = QcowHeader(fd) - h.extensions.append(QcowHeaderExtension.create(magic, data)) + h.extensions.append(QcowHeaderExtension.create(magic, data.encode('ascii'))) h.update(fd) def cmd_add_header_ext_stdio(fd, magic): diff --git a/tests/qemu-iotests/qed.py b/tests/qemu-iotests/qed.py index ea469b9c48..8adaaf46c4 100755 --- a/tests/qemu-iotests/qed.py +++ b/tests/qemu-iotests/qed.py @@ -80,7 +80,7 @@ class QED(object): def load_l1_table(self): self.l1_table = self.read_table(self.header['l1_table_offset']) - self.table_nelems = self.header['table_size'] * self.header['cluster_size'] / table_elem_size + self.table_nelems = self.header['table_size'] * self.header['cluster_size'] // table_elem_size def write_table(self, offset, table): s = ''.join(pack_table_elem(x) for x in table) @@ -167,14 +167,14 @@ def cmd_zero_cluster(qed, pos, *args): n = int(args[0]) for i in xrange(n): - l1_index = pos / qed.header['cluster_size'] / len(qed.l1_table) + l1_index = pos // qed.header['cluster_size'] // len(qed.l1_table) if qed.l1_table[l1_index] == 0: err('no l2 table allocated') l2_offset = qed.l1_table[l1_index] l2_table = qed.read_table(l2_offset) - l2_index = (pos / qed.header['cluster_size']) % len(qed.l1_table) + l2_index = (pos // qed.header['cluster_size']) % len(qed.l1_table) l2_table[l2_index] = 1 # zero the data cluster qed.write_table(l2_offset, l2_table) pos += qed.header['cluster_size'] diff --git a/tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2 b/tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2 new file mode 100644 index 0000000000..05e719d03d Binary files /dev/null and b/tests/qemu-iotests/sample_images/simple-dmg.dmg.bz2 differ diff --git a/tests/qht-bench.c b/tests/qht-bench.c index f492b3a20a..e3b512f26f 100644 --- a/tests/qht-bench.c +++ b/tests/qht-bench.c @@ -9,7 +9,7 @@ #include "qemu/atomic.h" #include "qemu/qht.h" #include "qemu/rcu.h" -#include "exec/tb-hash-xx.h" +#include "qemu/xxhash.h" struct thread_stats { size_t rd; @@ -53,6 +53,7 @@ static unsigned long resize_delay = 1000; static double resize_rate; /* 0.0 to 1.0 */ static unsigned int n_rz_threads = 1; static QemuThread *rz_threads; +static bool precompute_hash; static double update_rate; /* 0.0 to 1.0 */ static uint64_t update_threshold; @@ -71,6 +72,7 @@ static const char commands_string[] = " -n = number of threads\n" "\n" " -o = offset at which keys start\n" + " -p = precompute hashes\n" "\n" " -g = set -s,-k,-K,-l,-r to the same value\n" " -s = initial size hint\n" @@ -101,11 +103,18 @@ static bool is_equal(const void *ap, const void *bp) return *a == *b; } -static inline uint32_t h(unsigned long v) +static uint32_t h(unsigned long v) { - return tb_hash_func7(v, 0, 0, 0, 0); + return qemu_xxhash2(v); } +static uint32_t hval(unsigned long v) +{ + return v; +} + +static uint32_t (*hfunc)(unsigned long v) = h; + /* * From: https://en.wikipedia.org/wiki/Xorshift * This is faster than rand_r(), and gives us a wider range (RAND_MAX is only @@ -149,7 +158,7 @@ static void do_rw(struct thread_info *info) bool read; p = &keys[info->r & (lookup_range - 1)]; - hash = h(*p); + hash = hfunc(*p); read = qht_lookup(&ht, p, hash); if (read) { stats->rd++; @@ -158,7 +167,7 @@ static void do_rw(struct thread_info *info) } } else { p = &keys[info->r & (update_range - 1)]; - hash = h(*p); + hash = hfunc(*p); if (info->write_op) { bool written = false; @@ -289,7 +298,9 @@ static void htable_init(void) /* avoid allocating memory later by allocating all the keys now */ keys = g_malloc(sizeof(*keys) * n); for (i = 0; i < n; i++) { - keys[i] = populate_offset + i; + long val = populate_offset + i; + + keys[i] = precompute_hash ? h(val) : hval(val); } /* some sanity checks */ @@ -321,7 +332,7 @@ static void htable_init(void) r = xorshift64star(r); p = &keys[r & (init_range - 1)]; - hash = h(*p); + hash = hfunc(*p); if (qht_insert(&ht, p, hash, NULL)) { break; } @@ -387,16 +398,14 @@ static void pr_stats(void) static void run_test(void) { - unsigned int remaining; int i; while (atomic_read(&n_ready_threads) != n_rw_threads + n_rz_threads) { cpu_relax(); } + atomic_set(&test_start, true); - do { - remaining = sleep(duration); - } while (remaining); + g_usleep(duration * G_USEC_PER_SEC); atomic_set(&test_stop, true); for (i = 0; i < n_rw_threads; i++) { @@ -412,7 +421,7 @@ static void parse_args(int argc, char *argv[]) int c; for (;;) { - c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:r:Rs:S:u:"); + c = getopt(argc, argv, "d:D:g:k:K:l:hn:N:o:pr:Rs:S:u:"); if (c < 0) { break; } @@ -451,6 +460,10 @@ static void parse_args(int argc, char *argv[]) case 'o': populate_offset = atol(optarg); break; + case 'p': + precompute_hash = true; + hfunc = hval; + break; case 'r': update_range = pow2ceil(atol(optarg)); break; diff --git a/tests/qmp-cmd-test.c b/tests/qmp-cmd-test.c new file mode 100644 index 0000000000..d12cac539c --- /dev/null +++ b/tests/qmp-cmd-test.c @@ -0,0 +1,231 @@ +/* + * QMP command test cases + * + * Copyright (c) 2017 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. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-introspect.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qobject-input-visitor.h" + +const char common_args[] = "-nodefaults -machine none"; + +/* Query smoke tests */ + +static int query_error_class(const char *cmd) +{ + static struct { + const char *cmd; + int err_class; + } fails[] = { + /* Success depends on build configuration: */ +#ifndef CONFIG_SPICE + { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif +#ifndef CONFIG_VNC + { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, + { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, +#endif +#ifndef CONFIG_REPLICATION + { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, +#endif + /* Likewise, and require special QEMU command-line arguments: */ + { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, + { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, + { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, + { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, + { NULL, -1 } + }; + int i; + + for (i = 0; fails[i].cmd; i++) { + if (!strcmp(cmd, fails[i].cmd)) { + return fails[i].err_class; + } + } + return -1; +} + +static void test_query(const void *data) +{ + const char *cmd = data; + int expected_error_class = query_error_class(cmd); + QDict *resp, *error; + const char *error_class; + + qtest_start(common_args); + + resp = qmp("{ 'execute': %s }", cmd); + error = qdict_get_qdict(resp, "error"); + error_class = error ? qdict_get_str(error, "class") : NULL; + + if (expected_error_class < 0) { + g_assert(qdict_haskey(resp, "return")); + } else { + g_assert(error); + g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, + -1, &error_abort), + ==, expected_error_class); + } + qobject_unref(resp); + + qtest_end(); +} + +static bool query_is_blacklisted(const char *cmd) +{ + const char *blacklist[] = { + /* Not actually queries: */ + "add-fd", + /* Success depends on target arch: */ + "query-cpu-definitions", /* arm, i386, ppc, s390x */ + "query-gic-capabilities", /* arm */ + /* Success depends on target-specific build configuration: */ + "query-pci", /* CONFIG_PCI */ + /* Success depends on launching SEV guest */ + "query-sev-launch-measure", + /* Success depends on Host or Hypervisor SEV support */ + "query-sev", + "query-sev-capabilities", + NULL + }; + int i; + + for (i = 0; blacklist[i]; i++) { + if (!strcmp(cmd, blacklist[i])) { + return true; + } + } + return false; +} + +typedef struct { + SchemaInfoList *list; + GHashTable *hash; +} QmpSchema; + +static void qmp_schema_init(QmpSchema *schema) +{ + QDict *resp; + Visitor *qiv; + SchemaInfoList *tail; + + qtest_start(common_args); + resp = qmp("{ 'execute': 'query-qmp-schema' }"); + + qiv = qobject_input_visitor_new(qdict_get(resp, "return")); + visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); + visit_free(qiv); + + qobject_unref(resp); + qtest_end(); + + schema->hash = g_hash_table_new(g_str_hash, g_str_equal); + + /* Build @schema: hash table mapping entity name to SchemaInfo */ + for (tail = schema->list; tail; tail = tail->next) { + g_hash_table_insert(schema->hash, tail->value->name, tail->value); + } +} + +static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) +{ + return g_hash_table_lookup(schema->hash, name); +} + +static void qmp_schema_cleanup(QmpSchema *schema) +{ + qapi_free_SchemaInfoList(schema->list); + g_hash_table_destroy(schema->hash); +} + +static bool object_type_has_mandatory_members(SchemaInfo *type) +{ + SchemaInfoObjectMemberList *tail; + + g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); + + for (tail = type->u.object.members; tail; tail = tail->next) { + if (!tail->value->has_q_default) { + return true; + } + } + + return false; +} + +static void add_query_tests(QmpSchema *schema) +{ + SchemaInfoList *tail; + SchemaInfo *si, *arg_type, *ret_type; + char *test_name; + + /* Test the query-like commands */ + for (tail = schema->list; tail; tail = tail->next) { + si = tail->value; + if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { + continue; + } + + if (query_is_blacklisted(si->name)) { + continue; + } + + arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); + if (object_type_has_mandatory_members(arg_type)) { + continue; + } + + ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); + if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT + && !ret_type->u.object.members) { + continue; + } + + test_name = g_strdup_printf("qmp/%s", si->name); + qtest_add_data_func(test_name, si->name, test_query); + g_free(test_name); + } +} + +static void test_object_add_without_props(void) +{ + QTestState *qts; + QDict *resp; + + qts = qtest_init(common_args); + resp = qtest_qmp(qts, "{'execute': 'object-add', 'arguments':" + " {'qom-type': 'memory-backend-ram', 'id': 'ram1' } }"); + g_assert_nonnull(resp); + qmp_assert_error_class(resp, "GenericError"); + qtest_quit(qts); +} + +int main(int argc, char *argv[]) +{ + QmpSchema schema; + int ret; + + g_test_init(&argc, &argv, NULL); + + qmp_schema_init(&schema); + add_query_tests(&schema); + + qtest_add_func("qmp/object-add-without-props", + test_object_add_without_props); + /* TODO: add coverage of generic object-add failure modes */ + + ret = g_test_run(); + + qmp_schema_cleanup(&schema); + return ret; +} diff --git a/tests/qmp-test.c b/tests/qmp-test.c index b9774084f8..48a4fa791a 100644 --- a/tests/qmp-test.c +++ b/tests/qmp-test.c @@ -1,10 +1,10 @@ /* * QMP protocol test cases * - * Copyright (c) 2017 Red Hat Inc. + * Copyright (c) 2017-2018 Red Hat Inc. * * Authors: - * Markus Armbruster , + * 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. @@ -13,26 +13,14 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "qapi/error.h" -#include "qapi/qapi-visit-introspect.h" #include "qapi/qapi-visit-misc.h" #include "qapi/qmp/qdict.h" #include "qapi/qmp/qlist.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/util.h" -#include "qapi/visitor.h" #include "qapi/qmp/qstring.h" const char common_args[] = "-nodefaults -machine none"; -static const char *get_error_class(QDict *resp) -{ - QDict *error = qdict_get_qdict(resp, "error"); - const char *desc = qdict_get_try_str(error, "desc"); - - g_assert(desc); - return error ? qdict_get_try_str(error, "class") : NULL; -} - static void test_version(QObject *version) { Visitor *v; @@ -45,34 +33,73 @@ static void test_version(QObject *version) visit_free(v); } +static void assert_recovered(QTestState *qts) +{ + QDict *resp; + + resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd' }"); + qmp_assert_error_class(resp, "CommandNotFound"); +} + static void test_malformed(QTestState *qts) { QDict *resp; + /* syntax error */ + qtest_qmp_send_raw(qts, "{]\n"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: impossible byte outside string */ + qtest_qmp_send_raw(qts, "{\xFF"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: funny control character outside string */ + qtest_qmp_send_raw(qts, "{\x01"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: impossible byte in string */ + qtest_qmp_send_raw(qts, "{'bad \xFF"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: control character in string */ + qtest_qmp_send_raw(qts, "{'execute': 'nonexistent', 'id':'\n"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + + /* lexical error: interpolation */ + qtest_qmp_send_raw(qts, "%%p"); + resp = qtest_qmp_receive(qts); + qmp_assert_error_class(resp, "GenericError"); + assert_recovered(qts); + /* Not even a dictionary */ resp = qtest_qmp(qts, "null"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* No "execute" key */ resp = qtest_qmp(qts, "{}"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* "execute" isn't a string */ resp = qtest_qmp(qts, "{ 'execute': true }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* "arguments" isn't a dictionary */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'arguments': [] }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); /* extra key */ resp = qtest_qmp(qts, "{ 'execute': 'no-such-cmd', 'extra': true }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); } static void test_qmp_protocol(void) @@ -81,7 +108,7 @@ static void test_qmp_protocol(void) QList *capabilities; QTestState *qts; - qts = qtest_init_without_qmp_handshake(false, common_args); + qts = qtest_init_without_qmp_handshake(common_args); /* Test greeting */ resp = qtest_qmp_receive(qts); @@ -89,13 +116,12 @@ static void test_qmp_protocol(void) g_assert(q); test_version(qdict_get(q, "version")); capabilities = qdict_get_qlist(q, "capabilities"); - g_assert(capabilities && qlist_empty(capabilities)); + g_assert(capabilities); qobject_unref(resp); /* Test valid command before handshake */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); - g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - qobject_unref(resp); + qmp_assert_error_class(resp, "CommandNotFound"); /* Test malformed commands before handshake */ test_malformed(qts); @@ -108,8 +134,7 @@ static void test_qmp_protocol(void) /* Test repeated handshake */ resp = qtest_qmp(qts, "{ 'execute': 'qmp_capabilities' }"); - g_assert_cmpstr(get_error_class(resp), ==, "CommandNotFound"); - qobject_unref(resp); + qmp_assert_error_class(resp, "CommandNotFound"); /* Test valid command */ resp = qtest_qmp(qts, "{ 'execute': 'query-version' }"); @@ -128,9 +153,8 @@ static void test_qmp_protocol(void) /* Test command failure with 'id' */ resp = qtest_qmp(qts, "{ 'execute': 'human-monitor-command', 'id': 2 }"); - g_assert_cmpstr(get_error_class(resp), ==, "GenericError"); g_assert_cmpint(qdict_get_int(resp, "id"), ==, 2); - qobject_unref(resp); + qmp_assert_error_class(resp, "GenericError"); qtest_quit(qts); } @@ -159,12 +183,12 @@ static void cleanup_blocking_cmd(void) static void send_cmd_that_blocks(QTestState *s, const char *id) { - qtest_async_qmp(s, "{ 'execute': 'blockdev-add', 'id': %s," - " 'arguments': {" - " 'driver': 'blkdebug', 'node-name': %s," - " 'config': %s," - " 'image': { 'driver': 'null-co' } } }", - id, id, fifo_name); + qtest_qmp_send(s, "{ 'execute': 'blockdev-add', 'id': %s," + " 'arguments': {" + " 'driver': 'blkdebug', 'node-name': %s," + " 'config': %s," + " 'image': { 'driver': 'null-co' } } }", + id, id, fifo_name); } static void unblock_blocked_cmd(void) @@ -176,7 +200,7 @@ static void unblock_blocked_cmd(void) static void send_oob_cmd_that_fails(QTestState *s, const char *id) { - qtest_async_qmp(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); + qtest_qmp_send(s, "{ 'exec-oob': 'migrate-pause', 'id': %s }", id); } static void recv_cmd_id(QTestState *s, const char *id) @@ -195,7 +219,7 @@ static void test_qmp_oob(void) QList *capabilities; QString *qstr; - qts = qtest_init_without_qmp_handshake(true, common_args); + qts = qtest_init_without_qmp_handshake(common_args); /* Check the greeting message. */ resp = qtest_qmp_receive(qts); @@ -235,7 +259,7 @@ static void test_qmp_oob(void) /* OOB command overtakes slow in-band command */ setup_blocking_cmd(); send_cmd_that_blocks(qts, "ib-blocks-1"); - qtest_async_qmp(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); + qtest_qmp_send(qts, "{ 'execute': 'query-name', 'id': 'ib-quick-1' }"); send_oob_cmd_that_fails(qts, "oob-1"); recv_cmd_id(qts, "oob-1"); unblock_blocked_cmd(); @@ -244,7 +268,7 @@ static void test_qmp_oob(void) /* Even malformed in-band command fails in-band */ send_cmd_that_blocks(qts, "blocks-2"); - qtest_async_qmp(qts, "{ 'id': 'err-2' }"); + qtest_qmp_send(qts, "{ 'id': 'err-2' }"); unblock_blocked_cmd(); recv_cmd_id(qts, "blocks-2"); recv_cmd_id(qts, "err-2"); @@ -253,190 +277,12 @@ static void test_qmp_oob(void) qtest_quit(qts); } -/* Query smoke tests */ - -static int query_error_class(const char *cmd) -{ - static struct { - const char *cmd; - int err_class; - } fails[] = { - /* Success depends on build configuration: */ -#ifndef CONFIG_SPICE - { "query-spice", ERROR_CLASS_COMMAND_NOT_FOUND }, -#endif -#ifndef CONFIG_VNC - { "query-vnc", ERROR_CLASS_GENERIC_ERROR }, - { "query-vnc-servers", ERROR_CLASS_GENERIC_ERROR }, -#endif -#ifndef CONFIG_REPLICATION - { "query-xen-replication-status", ERROR_CLASS_COMMAND_NOT_FOUND }, -#endif - /* Likewise, and require special QEMU command-line arguments: */ - { "query-acpi-ospm-status", ERROR_CLASS_GENERIC_ERROR }, - { "query-balloon", ERROR_CLASS_DEVICE_NOT_ACTIVE }, - { "query-hotpluggable-cpus", ERROR_CLASS_GENERIC_ERROR }, - { "query-vm-generation-id", ERROR_CLASS_GENERIC_ERROR }, - { NULL, -1 } - }; - int i; - - for (i = 0; fails[i].cmd; i++) { - if (!strcmp(cmd, fails[i].cmd)) { - return fails[i].err_class; - } - } - return -1; -} - -static void test_query(const void *data) -{ - const char *cmd = data; - int expected_error_class = query_error_class(cmd); - QDict *resp, *error; - const char *error_class; - - qtest_start(common_args); - - resp = qmp("{ 'execute': %s }", cmd); - error = qdict_get_qdict(resp, "error"); - error_class = error ? qdict_get_str(error, "class") : NULL; - - if (expected_error_class < 0) { - g_assert(qdict_haskey(resp, "return")); - } else { - g_assert(error); - g_assert_cmpint(qapi_enum_parse(&QapiErrorClass_lookup, error_class, - -1, &error_abort), - ==, expected_error_class); - } - qobject_unref(resp); - - qtest_end(); -} - -static bool query_is_blacklisted(const char *cmd) -{ - const char *blacklist[] = { - /* Not actually queries: */ - "add-fd", - /* Success depends on target arch: */ - "query-cpu-definitions", /* arm, i386, ppc, s390x */ - "query-gic-capabilities", /* arm */ - /* Success depends on target-specific build configuration: */ - "query-pci", /* CONFIG_PCI */ - /* Success depends on launching SEV guest */ - "query-sev-launch-measure", - /* Success depends on Host or Hypervisor SEV support */ - "query-sev", - "query-sev-capabilities", - NULL - }; - int i; - - for (i = 0; blacklist[i]; i++) { - if (!strcmp(cmd, blacklist[i])) { - return true; - } - } - return false; -} - -typedef struct { - SchemaInfoList *list; - GHashTable *hash; -} QmpSchema; - -static void qmp_schema_init(QmpSchema *schema) -{ - QDict *resp; - Visitor *qiv; - SchemaInfoList *tail; - - qtest_start(common_args); - resp = qmp("{ 'execute': 'query-qmp-schema' }"); - - qiv = qobject_input_visitor_new(qdict_get(resp, "return")); - visit_type_SchemaInfoList(qiv, NULL, &schema->list, &error_abort); - visit_free(qiv); - - qobject_unref(resp); - qtest_end(); - - schema->hash = g_hash_table_new(g_str_hash, g_str_equal); - - /* Build @schema: hash table mapping entity name to SchemaInfo */ - for (tail = schema->list; tail; tail = tail->next) { - g_hash_table_insert(schema->hash, tail->value->name, tail->value); - } -} - -static SchemaInfo *qmp_schema_lookup(QmpSchema *schema, const char *name) -{ - return g_hash_table_lookup(schema->hash, name); -} - -static void qmp_schema_cleanup(QmpSchema *schema) -{ - qapi_free_SchemaInfoList(schema->list); - g_hash_table_destroy(schema->hash); -} - -static bool object_type_has_mandatory_members(SchemaInfo *type) -{ - SchemaInfoObjectMemberList *tail; - - g_assert(type->meta_type == SCHEMA_META_TYPE_OBJECT); - - for (tail = type->u.object.members; tail; tail = tail->next) { - if (!tail->value->has_q_default) { - return true; - } - } - - return false; -} - -static void add_query_tests(QmpSchema *schema) -{ - SchemaInfoList *tail; - SchemaInfo *si, *arg_type, *ret_type; - char *test_name; - - /* Test the query-like commands */ - for (tail = schema->list; tail; tail = tail->next) { - si = tail->value; - if (si->meta_type != SCHEMA_META_TYPE_COMMAND) { - continue; - } - - if (query_is_blacklisted(si->name)) { - continue; - } - - arg_type = qmp_schema_lookup(schema, si->u.command.arg_type); - if (object_type_has_mandatory_members(arg_type)) { - continue; - } - - ret_type = qmp_schema_lookup(schema, si->u.command.ret_type); - if (ret_type->meta_type == SCHEMA_META_TYPE_OBJECT - && !ret_type->u.object.members) { - continue; - } - - test_name = g_strdup_printf("qmp/%s", si->name); - qtest_add_data_func(test_name, si->name, test_query); - g_free(test_name); - } -} - /* Preconfig tests */ static void test_qmp_preconfig(void) { QDict *rsp, *ret; - QTestState *qs = qtest_startf("%s --preconfig", common_args); + QTestState *qs = qtest_initf("%s --preconfig", common_args); /* preconfig state */ /* enabled commands, no error expected */ @@ -472,21 +318,27 @@ static void test_qmp_preconfig(void) qtest_quit(qs); } +static void test_qmp_missing_any_arg(void) +{ + QTestState *qts; + QDict *resp; + + qts = qtest_init(common_args); + resp = qtest_qmp(qts, "{'execute': 'qom-set', 'arguments':" + " { 'path': '/machine', 'property': 'rtc-time' } }"); + g_assert_nonnull(resp); + qmp_assert_error_class(resp, "GenericError"); + qtest_quit(qts); +} + int main(int argc, char *argv[]) { - QmpSchema schema; - int ret; - g_test_init(&argc, &argv, NULL); qtest_add_func("qmp/protocol", test_qmp_protocol); qtest_add_func("qmp/oob", test_qmp_oob); - qmp_schema_init(&schema); - add_query_tests(&schema); qtest_add_func("qmp/preconfig", test_qmp_preconfig); + qtest_add_func("qmp/missing-any-arg", test_qmp_missing_any_arg); - ret = g_test_run(); - - qmp_schema_cleanup(&schema); - return ret; + return g_test_run(); } diff --git a/tests/qom-test.c b/tests/qom-test.c index e6f712cbd3..73c52af3bb 100644 --- a/tests/qom-test.c +++ b/tests/qom-test.c @@ -123,7 +123,7 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - qtest_cb_for_every_machine(add_machine_test_case); + qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); return g_test_run(); } diff --git a/tests/qos-test.c b/tests/qos-test.c new file mode 100644 index 0000000000..6b1145eccc --- /dev/null +++ b/tests/qos-test.c @@ -0,0 +1,445 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qapi/qmp/qdict.h" +#include "qapi/qmp/qbool.h" +#include "qapi/qmp/qstring.h" +#include "qapi/qmp/qlist.h" +#include "libqos/malloc.h" +#include "libqos/qgraph.h" +#include "libqos/qgraph_internal.h" + +static char *old_path; + +static void apply_to_node(const char *name, bool is_machine, bool is_abstract) +{ + char *machine_name = NULL; + if (is_machine) { + const char *arch = qtest_get_arch(); + machine_name = g_strconcat(arch, "/", name, NULL); + name = machine_name; + } + qos_graph_node_set_availability(name, true); + if (is_abstract) { + qos_delete_cmd_line(name); + } + g_free(machine_name); +} + +/** + * apply_to_qlist(): using QMP queries QEMU for a list of + * machines and devices available, and sets the respective node + * as true. If a node is found, also all its produced and contained + * child are marked available. + * + * See qos_graph_node_set_availability() for more info + */ +static void apply_to_qlist(QList *list, bool is_machine) +{ + const QListEntry *p; + const char *name; + bool abstract; + QDict *minfo; + QObject *qobj; + QString *qstr; + QBool *qbool; + + for (p = qlist_first(list); p; p = qlist_next(p)) { + minfo = qobject_to(QDict, qlist_entry_obj(p)); + qobj = qdict_get(minfo, "name"); + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + + qobj = qdict_get(minfo, "abstract"); + if (qobj) { + qbool = qobject_to(QBool, qobj); + abstract = qbool_get_bool(qbool); + } else { + abstract = false; + } + + apply_to_node(name, is_machine, abstract); + qobj = qdict_get(minfo, "alias"); + if (qobj) { + qstr = qobject_to(QString, qobj); + name = qstring_get_str(qstr); + apply_to_node(name, is_machine, abstract); + } + } +} + +/** + * qos_set_machines_devices_available(): sets availability of qgraph + * machines and devices. + * + * This function firstly starts QEMU with "-machine none" option, + * and then executes the QMP protocol asking for the list of devices + * and machines available. + * + * for each of these items, it looks up the corresponding qgraph node, + * setting it as available. The list currently returns all devices that + * are either machines or QEDGE_CONSUMED_BY other nodes. + * Therefore, in order to mark all other nodes, it recursively sets + * all its QEDGE_CONTAINS and QEDGE_PRODUCES child as available too. + */ +static void qos_set_machines_devices_available(void) +{ + QDict *response; + QDict *args = qdict_new(); + QList *list; + + qtest_start("-machine none"); + response = qmp("{ 'execute': 'query-machines' }"); + list = qdict_get_qlist(response, "return"); + + apply_to_qlist(list, true); + + qobject_unref(response); + + qdict_put_bool(args, "abstract", true); + qdict_put_str(args, "implements", "device"); + + response = qmp("{'execute': 'qom-list-types'," + " 'arguments': %p }", args); + g_assert(qdict_haskey(response, "return")); + list = qdict_get_qlist(response, "return"); + + apply_to_qlist(list, false); + + qtest_end(); + qobject_unref(response); +} + +static QGuestAllocator *get_machine_allocator(QOSGraphObject *obj) +{ + return obj->get_driver(obj, "memory"); +} + +static void restart_qemu_or_continue(char *path) +{ + /* compares the current command line with the + * one previously executed: if they are the same, + * don't restart QEMU, if they differ, stop previous + * QEMU subprocess (if active) and start over with + * the new command line + */ + if (g_strcmp0(old_path, path)) { + qtest_end(); + qos_invalidate_command_line(); + old_path = g_strdup(path); + qtest_start(path); + } else { /* if cmd line is the same, reset the guest */ + qobject_unref(qmp("{ 'execute': 'system_reset' }")); + qmp_eventwait("RESET"); + } +} + +void qos_invalidate_command_line(void) +{ + g_free(old_path); + old_path = NULL; +} + +/** + * allocate_objects(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, return the object it consumes. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. (The test's + * ->before callback is also welcome to use g_test_queue_destroy). + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + */ +static void *allocate_objects(QTestState *qts, char **path, QGuestAllocator **p_alloc) +{ + int current = 0; + QGuestAllocator *alloc; + QOSGraphObject *parent = NULL; + QOSGraphEdge *edge; + QOSGraphNode *node; + void *edge_arg; + void *obj; + + node = qos_graph_get_node(path[current]); + g_assert(node->type == QNODE_MACHINE); + + obj = qos_machine_new(node, qts); + qos_object_queue_destroy(obj); + + alloc = get_machine_allocator(obj); + if (p_alloc) { + *p_alloc = alloc; + } + + for (;;) { + if (node->type != QNODE_INTERFACE) { + qos_object_start_hw(obj); + parent = obj; + } + + /* follow edge and get object for next node constructor */ + current++; + edge = qos_graph_get_edge(path[current - 1], path[current]); + node = qos_graph_get_node(path[current]); + + if (node->type == QNODE_TEST) { + g_assert(qos_graph_edge_get_type(edge) == QEDGE_CONSUMED_BY); + return obj; + } + + switch (qos_graph_edge_get_type(edge)) { + case QEDGE_PRODUCES: + obj = parent->get_driver(parent, path[current]); + break; + + case QEDGE_CONSUMED_BY: + edge_arg = qos_graph_edge_get_arg(edge); + obj = qos_driver_new(node, obj, alloc, edge_arg); + qos_object_queue_destroy(obj); + break; + + case QEDGE_CONTAINS: + obj = parent->get_device(parent, path[current]); + break; + } + } +} + +/* The argument to run_one_test, which is the test function that is registered + * with GTest, is a vector of strings. The first item is the initial command + * line (before it is modified by the test's "before" function), the remaining + * items are node names forming the path to the test node. + */ +static char **current_path; + +const char *qos_get_current_command_line(void) +{ + return current_path[0]; +} + +void *qos_allocate_objects(QTestState *qts, QGuestAllocator **p_alloc) +{ + return allocate_objects(qts, current_path + 1, p_alloc); +} + +/** + * run_one_test(): given an array of nodes @arg, + * walks the path invoking all constructors and + * passing the corresponding parameter in order to + * continue the objects allocation. + * Once the test is reached, its function is executed. + * + * Since the machine and QEDGE_CONSUMED_BY nodes allocate + * memory in the constructor, g_test_queue_destroy is used so + * that after execution they can be safely free'd. The test's + * ->before callback is also welcome to use g_test_queue_destroy. + * + * Note: as specified in walk_path() too, @arg is an array of + * char *, where arg[0] is a pointer to the command line + * string that will be used to properly start QEMU when executing + * the test, and the remaining elements represent the actual objects + * that will be allocated. + * + * The order of execution is the following: + * 1) @before test function as defined in the given QOSGraphTestOptions + * 2) start QEMU + * 3) call all nodes constructor and get_driver/get_device depending on edge, + * start the hardware (*_device_enable functions) + * 4) start test + */ +static void run_one_test(const void *arg) +{ + QOSGraphNode *test_node; + QGuestAllocator *alloc = NULL; + void *obj; + char **path = (char **) arg; + GString *cmd_line = g_string_new(path[0]); + void *test_arg; + + /* Before test */ + current_path = path; + test_node = qos_graph_get_node(path[(g_strv_length(path) - 1)]); + test_arg = test_node->u.test.arg; + if (test_node->u.test.before) { + test_arg = test_node->u.test.before(cmd_line, test_arg); + } + + restart_qemu_or_continue(cmd_line->str); + g_string_free(cmd_line, true); + + obj = qos_allocate_objects(global_qtest, &alloc); + test_node->u.test.function(obj, test_arg, alloc); +} + +static void subprocess_run_one_test(const void *arg) +{ + const gchar *path = arg; + g_test_trap_subprocess(path, 0, 0); + g_test_trap_assert_passed(); +} + +/* + * in this function, 2 path will be built: + * path_str, a one-string path (ex "pc/i440FX-pcihost/...") + * path_vec, a string-array path (ex [0] = "pc", [1] = "i440FX-pcihost"). + * + * path_str will be only used to build the test name, and won't need the + * architecture name at beginning, since it will be added by qtest_add_func(). + * + * path_vec is used to allocate all constructors of the path nodes. + * Each name in this array except position 0 must correspond to a valid + * QOSGraphNode name. + * Position 0 is special, initially contains just the name of + * the node, (ex for "x86_64/pc" it will be "pc"), used to build the test + * path (see below). After it will contain the command line used to start + * qemu with all required devices. + * + * Note that the machine node name must be with format / + * (ex "x86_64/pc"), because it will identify the node "x86_64/pc" + * and start QEMU with "-M pc". For this reason, + * when building path_str, path_vec + * initially contains the at position 0 ("pc"), + * and the node name at position 1 (/) + * ("x86_64/pc"), followed by the rest of the nodes. + */ +static void walk_path(QOSGraphNode *orig_path, int len) +{ + QOSGraphNode *path; + QOSGraphEdge *edge; + + /* etype set to QEDGE_CONSUMED_BY so that machine can add to the command line */ + QOSEdgeType etype = QEDGE_CONSUMED_BY; + + /* twice QOS_PATH_MAX_ELEMENT_SIZE since each edge can have its arg */ + char **path_vec = g_new0(char *, (QOS_PATH_MAX_ELEMENT_SIZE * 2)); + int path_vec_size = 0; + + char *after_cmd = NULL, *before_cmd = NULL, *after_device = NULL; + char *node_name = orig_path->name, *path_str; + + GString *cmd_line = g_string_new(""); + GString *cmd_line2 = g_string_new(""); + + path = qos_graph_get_node(node_name); /* root */ + node_name = qos_graph_edge_get_dest(path->path_edge); /* machine name */ + + path_vec[path_vec_size++] = node_name; + path_vec[path_vec_size++] = qos_get_machine_type(node_name); + + for (;;) { + path = qos_graph_get_node(node_name); + if (!path->path_edge) { + break; + } + + node_name = qos_graph_edge_get_dest(path->path_edge); + + /* append node command line + previous edge command line */ + if (path->command_line && etype == QEDGE_CONSUMED_BY) { + g_string_append(cmd_line, path->command_line); + if (after_device) { + g_string_append(cmd_line, after_device); + } + } + + path_vec[path_vec_size++] = qos_graph_edge_get_name(path->path_edge); + /* detect if edge has command line args */ + after_cmd = qos_graph_edge_get_after_cmd_line(path->path_edge); + after_device = qos_graph_edge_get_extra_device_opts(path->path_edge); + before_cmd = qos_graph_edge_get_before_cmd_line(path->path_edge); + edge = qos_graph_get_edge(path->name, node_name); + etype = qos_graph_edge_get_type(edge); + + if (before_cmd) { + g_string_append(cmd_line, before_cmd); + } + if (after_cmd) { + g_string_append(cmd_line2, after_cmd); + } + } + + path_vec[path_vec_size++] = NULL; + if (after_device) { + g_string_append(cmd_line, after_device); + } + g_string_append(cmd_line, cmd_line2->str); + g_string_free(cmd_line2, true); + + /* here position 0 has /, position 1 has . + * The path must not have the , qtest_add_data_func adds it. + */ + path_str = g_strjoinv("/", path_vec + 1); + + /* put arch/machine in position 1 so run_one_test can do its work + * and add the command line at position 0. + */ + path_vec[1] = path_vec[0]; + path_vec[0] = g_string_free(cmd_line, false); + + if (path->u.test.subprocess) { + gchar *subprocess_path = g_strdup_printf("/%s/%s/subprocess", + qtest_get_arch(), path_str); + qtest_add_data_func(path_str, subprocess_path, subprocess_run_one_test); + g_test_add_data_func(subprocess_path, path_vec, run_one_test); + } else { + qtest_add_data_func(path_str, path_vec, run_one_test); + } + + g_free(path_str); +} + + + +/** + * main(): heart of the qgraph framework. + * + * - Initializes the glib test framework + * - Creates the graph by invoking the various _init constructors + * - Starts QEMU to mark the available devices + * - Walks the graph, and each path is added to + * the glib test framework (walk_path) + * - Runs the tests, calling allocate_object() and allocating the + * machine/drivers/test objects + * - Cleans up everything + */ +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + qos_graph_init(); + module_call_init(MODULE_INIT_QOM); + module_call_init(MODULE_INIT_LIBQOS); + qos_set_machines_devices_available(); + + qos_graph_foreach_test_path(walk_path); + g_test_run(); + qtest_end(); + qos_graph_destroy(); + g_free(old_path); + return 0; +} diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000000..002ded6a22 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,4 @@ +# Add Python module requirements, one per line, to be installed +# in the tests/venv Python virtual environment. For more info, +# refer to: https://pip.pypa.io/en/stable/user_guide/#id1 +avocado-framework==68.0 diff --git a/tests/rtas-test.c b/tests/rtas-test.c index 009bda6d23..ee888676ed 100644 --- a/tests/rtas-test.c +++ b/tests/rtas-test.c @@ -17,7 +17,7 @@ static void test_rtas_get_time_of_day(void) global_qtest = qs->qts; t1 = time(NULL); - ret = qrtas_get_time_of_day(qs->qts, qs->alloc, &tm, &ns); + ret = qrtas_get_time_of_day(qs->qts, &qs->alloc, &tm, &ns); g_assert_cmpint(ret, ==, 0); t2 = mktimegm(&tm); g_assert(t2 - t1 < 5); /* 5 sec max to run the test */ diff --git a/tests/rtc-test.c b/tests/rtc-test.c index d7a96cbd79..509be707e3 100644 --- a/tests/rtc-test.c +++ b/tests/rtc-test.c @@ -165,9 +165,9 @@ static void check_time(int wiggle) t = (long)mktime(datep); s = (long)mktime(&start); if (t < s) { - g_test_message("RTC is %ld second(s) behind wall-clock\n", (s - t)); + g_test_message("RTC is %ld second(s) behind wall-clock", (s - t)); } else { - g_test_message("RTC is %ld second(s) ahead of wall-clock\n", (t - s)); + g_test_message("RTC is %ld second(s) ahead of wall-clock", (t - s)); } g_assert_cmpint(ABS(t - s), <=, wiggle); diff --git a/tests/rtl8139-test.c b/tests/rtl8139-test.c index 68bfc42178..d6d0c24909 100644 --- a/tests/rtl8139-test.c +++ b/tests/rtl8139-test.c @@ -35,7 +35,7 @@ static QPCIDevice *get_device(void) { QPCIDevice *dev; - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); qpci_device_foreach(pcibus, 0x10ec, 0x8139, save_fn, &dev); g_assert(dev != NULL); @@ -46,12 +46,12 @@ static QPCIDevice *get_device(void) static unsigned __attribute__((unused)) in_##name(void) \ { \ unsigned res = qpci_io_read##len(dev, dev_bar, (val)); \ - g_test_message("*%s -> %x\n", #name, res); \ + g_test_message("*%s -> %x", #name, res); \ return res; \ } \ static void out_##name(unsigned v) \ { \ - g_test_message("%x -> *%s\n", v, #name); \ + g_test_message("%x -> *%s", v, #name); \ qpci_io_write##len(dev, dev_bar, (val), v); \ } @@ -176,7 +176,7 @@ static void test_timer(void) } } - g_test_message("Everythink is ok!\n"); + g_test_message("Everythink is ok!"); } diff --git a/tests/sdhci-test.c b/tests/sdhci-test.c index 1d825eb010..2f177e569f 100644 --- a/tests/sdhci-test.c +++ b/tests/sdhci-test.c @@ -12,6 +12,8 @@ #include "libqtest.h" #include "libqos/pci-pc.h" #include "hw/pci/pci.h" +#include "libqos/qgraph.h" +#include "libqos/sdhci.h" #define SDHC_CAPAB 0x40 FIELD(SDHC_CAPAB, BASECLKFREQ, 8, 8); /* since v2 */ @@ -20,99 +22,11 @@ FIELD(SDHC_CAPAB, SDR, 32, 3); /* since v3 */ FIELD(SDHC_CAPAB, DRIVER, 36, 3); /* since v3 */ #define SDHC_HCVER 0xFE -static const struct sdhci_t { - const char *arch, *machine; - struct { - uintptr_t addr; - uint8_t version; - uint8_t baseclock; - struct { - bool sdma; - uint64_t reg; - } capab; - } sdhci; - struct { - uint16_t vendor_id, device_id; - } pci; -} models[] = { - /* PC via PCI */ - { "x86_64", "pc", - {-1, 2, 0, {1, 0x057834b4} }, - .pci = { PCI_VENDOR_ID_REDHAT, PCI_DEVICE_ID_REDHAT_SDHCI } }, - - /* Exynos4210 */ - { "arm", "smdkc210", - {0x12510000, 2, 0, {1, 0x5e80080} } }, - - /* i.MX 6 */ - { "arm", "sabrelite", - {0x02190000, 3, 0, {1, 0x057834b4} } }, - - /* BCM2835 */ - { "arm", "raspi2", - {0x3f300000, 3, 52, {0, 0x052134b4} } }, - - /* Zynq-7000 */ - { "arm", "xilinx-zynq-a9", /* Datasheet: UG585 (v1.12.1) */ - {0xe0100000, 2, 0, {1, 0x69ec0080} } }, - - /* ZynqMP */ - { "aarch64", "xlnx-zcu102", /* Datasheet: UG1085 (v1.7) */ - {0xff160000, 3, 0, {1, 0x280737ec6481} } }, - -}; - -typedef struct QSDHCI { - struct { - QPCIBus *bus; - QPCIDevice *dev; - } pci; - union { - QPCIBar mem_bar; - uint64_t addr; - }; -} QSDHCI; - -static uint16_t sdhci_readw(QSDHCI *s, uint32_t reg) -{ - uint16_t val; - - if (s->pci.dev) { - val = qpci_io_readw(s->pci.dev, s->mem_bar, reg); - } else { - val = qtest_readw(global_qtest, s->addr + reg); - } - - return val; -} - -static uint64_t sdhci_readq(QSDHCI *s, uint32_t reg) -{ - uint64_t val; - - if (s->pci.dev) { - val = qpci_io_readq(s->pci.dev, s->mem_bar, reg); - } else { - val = qtest_readq(global_qtest, s->addr + reg); - } - - return val; -} - -static void sdhci_writeq(QSDHCI *s, uint32_t reg, uint64_t val) -{ - if (s->pci.dev) { - qpci_io_writeq(s->pci.dev, s->mem_bar, reg, val); - } else { - qtest_writeq(global_qtest, s->addr + reg, val); - } -} - static void check_specs_version(QSDHCI *s, uint8_t version) { uint32_t v; - v = sdhci_readw(s, SDHC_HCVER); + v = s->readw(s, SDHC_HCVER); v &= 0xff; v += 1; g_assert_cmpuint(v, ==, version); @@ -122,7 +36,7 @@ static void check_capab_capareg(QSDHCI *s, uint64_t expec_capab) { uint64_t capab; - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); g_assert_cmphex(capab, ==, expec_capab); } @@ -131,11 +45,11 @@ static void check_capab_readonly(QSDHCI *s) const uint64_t vrand = 0x123456789abcdef; uint64_t capab0, capab1; - capab0 = sdhci_readq(s, SDHC_CAPAB); + capab0 = s->readq(s, SDHC_CAPAB); g_assert_cmpuint(capab0, !=, vrand); - sdhci_writeq(s, SDHC_CAPAB, vrand); - capab1 = sdhci_readq(s, SDHC_CAPAB); + s->writeq(s, SDHC_CAPAB, vrand); + capab1 = s->readq(s, SDHC_CAPAB); g_assert_cmpuint(capab1, !=, vrand); g_assert_cmpuint(capab1, ==, capab0); } @@ -147,7 +61,7 @@ static void check_capab_baseclock(QSDHCI *s, uint8_t expec_freq) if (!expec_freq) { return; } - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); capab_freq = FIELD_EX64(capab, SDHC_CAPAB, BASECLKFREQ); g_assert_cmpuint(capab_freq, ==, expec_freq); } @@ -156,7 +70,7 @@ static void check_capab_sdma(QSDHCI *s, bool supported) { uint64_t capab, capab_sdma; - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); capab_sdma = FIELD_EX64(capab, SDHC_CAPAB, SDMA); g_assert_cmpuint(capab_sdma, ==, supported); } @@ -167,7 +81,7 @@ static void check_capab_v3(QSDHCI *s, uint8_t version) if (version < 3) { /* before v3 those fields are RESERVED */ - capab = sdhci_readq(s, SDHC_CAPAB); + capab = s->readq(s, SDHC_CAPAB); capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, SDR); g_assert_cmpuint(capab_v3, ==, 0); capab_v3 = FIELD_EX64(capab, SDHC_CAPAB, DRIVER); @@ -175,78 +89,21 @@ static void check_capab_v3(QSDHCI *s, uint8_t version) } } -static QSDHCI *machine_start(const struct sdhci_t *test) +static void test_registers(void *obj, void *data, QGuestAllocator *alloc) { - QSDHCI *s = g_new0(QSDHCI, 1); + QSDHCI *s = obj; - if (test->pci.vendor_id) { - /* PCI */ - uint16_t vendor_id, device_id; - uint64_t barsize; - - global_qtest = qtest_startf("-machine %s -device sdhci-pci", - test->machine); - - s->pci.bus = qpci_init_pc(global_qtest, NULL); - - /* Find PCI device and verify it's the right one */ - s->pci.dev = qpci_device_find(s->pci.bus, QPCI_DEVFN(4, 0)); - g_assert_nonnull(s->pci.dev); - vendor_id = qpci_config_readw(s->pci.dev, PCI_VENDOR_ID); - device_id = qpci_config_readw(s->pci.dev, PCI_DEVICE_ID); - g_assert(vendor_id == test->pci.vendor_id); - g_assert(device_id == test->pci.device_id); - s->mem_bar = qpci_iomap(s->pci.dev, 0, &barsize); - qpci_device_enable(s->pci.dev); - } else { - /* SysBus */ - global_qtest = qtest_startf("-machine %s", test->machine); - s->addr = test->sdhci.addr; - } - - return s; -} - -static void machine_stop(QSDHCI *s) -{ - qpci_free_pc(s->pci.bus); - g_free(s->pci.dev); - qtest_quit(global_qtest); - g_free(s); -} - -static void test_machine(const void *data) -{ - const struct sdhci_t *test = data; - QSDHCI *s; - - s = machine_start(test); - - check_specs_version(s, test->sdhci.version); - check_capab_capareg(s, test->sdhci.capab.reg); + check_specs_version(s, s->props.version); + check_capab_capareg(s, s->props.capab.reg); check_capab_readonly(s); - check_capab_v3(s, test->sdhci.version); - check_capab_sdma(s, test->sdhci.capab.sdma); - check_capab_baseclock(s, test->sdhci.baseclock); - - machine_stop(s); + check_capab_v3(s, s->props.version); + check_capab_sdma(s, s->props.capab.sdma); + check_capab_baseclock(s, s->props.baseclock); } -int main(int argc, char *argv[]) +static void register_sdhci_test(void) { - const char *arch = qtest_get_arch(); - char *name; - int i; - - g_test_init(&argc, &argv, NULL); - for (i = 0; i < ARRAY_SIZE(models); i++) { - if (strcmp(arch, models[i].arch)) { - continue; - } - name = g_strdup_printf("sdhci/%s", models[i].machine); - qtest_add_data_func(name, &models[i], test_machine); - g_free(name); - } - - return g_test_run(); + qos_add_test("registers", "sdhci", test_registers, NULL); } + +libqos_init(register_sdhci_test); diff --git a/tests/spapr-phb-test.c b/tests/spapr-phb-test.c index d3522ea093..39b5766710 100644 --- a/tests/spapr-phb-test.c +++ b/tests/spapr-phb-test.c @@ -7,29 +7,25 @@ * 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 "libqtest.h" +#include "libqos/qgraph.h" -#define TYPE_SPAPR_PCI_HOST_BRIDGE "spapr-pci-host-bridge" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void test_phb_device(void) +/* Tests only initialization so far. TODO: Replace with functional tests, + * for example by producing pci-bus. + */ +static void test_phb_device(void *obj, void *data, QGuestAllocator *alloc) { } -int main(int argc, char **argv) +static void register_phb_test(void) { - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/spapr-phb/device", test_phb_device); - - qtest_start("-device " TYPE_SPAPR_PCI_HOST_BRIDGE ",index=30"); - - ret = g_test_run(); - - qtest_end(); - - return ret; + qos_add_test("spapr-phb-test", "ppc64/pseries", + test_phb_device, &(QOSGraphTestOptions) { + .edge.before_cmd_line = "-device spapr-pci-host-bridge" + ",index=30", + }); } + +libqos_init(register_phb_test); diff --git a/tests/tcg/Makefile b/tests/tcg/Makefile index bf06415390..1cdd628e96 100644 --- a/tests/tcg/Makefile +++ b/tests/tcg/Makefile @@ -26,16 +26,23 @@ # # We also accept SPEED=slow to enable slower running tests # -# We also expect to be in the tests build dir for the FOO-linux-user. +# We also expect to be in the tests build dir for the FOO-(linux-user|softmmu). # -include ../../config-host.mak -include ../config-target.mak +# for including , in command strings +COMMA := , + quiet-command = $(if $(V),$1,$(if $(2),@printf " %-7s %s\n" $2 $3 && $1, @$1)) # $1 = test name, $2 = cmd, $3 = desc +ifdef CONFIG_USER_ONLY run-test = $(call quiet-command, timeout $(TIMEOUT) $2 > $1.out,"TEST",$3) +else +run-test = $(call quiet-command, timeout $(TIMEOUT) $2,"TEST",$3) +endif # $1 = test name, $2 = reference diff-out = $(call quiet-command, diff -u $1.out $2 | head -n 10,"DIFF","$1.out with $2") @@ -52,7 +59,13 @@ QEMU_CFLAGS= LDFLAGS= # The QEMU for this TARGET +ifdef CONFIG_USER_ONLY QEMU=../qemu-$(TARGET_NAME) +else +QEMU=../qemu-system-$(TARGET_NAME) +endif +QEMU_OPTS= + # If TCG debugging is enabled things are a lot slower ifeq ($(CONFIG_DEBUG_TCG),y) @@ -61,6 +74,7 @@ else TIMEOUT=15 endif +ifdef CONFIG_USER_ONLY # The order we include is important. We include multiarch, base arch # and finally arch if it's not the same as base arch. -include $(SRC_PATH)/tests/tcg/multiarch/Makefile.target @@ -77,6 +91,17 @@ endif %: %.c $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $< -o $@ $(LDFLAGS) +else +# For softmmu targets we include a different Makefile fragement as the +# build options for bare programs are usually pretty different. They +# are expected to provide their own build recipes. +-include $(SRC_PATH)/tests/tcg/minilib/Makefile.target +-include $(SRC_PATH)/tests/tcg/$(TARGET_BASE_ARCH)/Makefile.softmmu-target +ifneq ($(TARGET_BASE_ARCH),$(TARGET_NAME)) +-include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target +endif + +endif all: $(TESTS) @@ -92,8 +117,20 @@ all: $(TESTS) RUN_TESTS=$(patsubst %,run-%, $(TESTS)) RUN_TESTS+=$(EXTRA_RUNS) +ifdef CONFIG_USER_ONLY run-%: % - $(call run-test, $<, $(QEMU) $<, "$< on $(TARGET_NAME)") + $(call run-test, $<, $(QEMU) $(QEMU_OPTS) $<, "$< on $(TARGET_NAME)") +else +run-%: % + $(call run-test, $<, \ + $(QEMU) -monitor none -display none \ + -chardev file$(COMMA)path=$<.out$(COMMA)id=output \ + $(QEMU_OPTS) $<, \ + "$< on $(TARGET_NAME)") +endif + +gdb-%: % + gdb --args $(QEMU) $(QEMU_OPTS) $< .PHONY: run run: $(RUN_TESTS) diff --git a/tests/tcg/Makefile.include b/tests/tcg/Makefile.include index 57470b2a2c..73b5626fc5 100644 --- a/tests/tcg/Makefile.include +++ b/tests/tcg/Makefile.include @@ -2,7 +2,7 @@ # # TCG tests (per-target rules) # -# This Makefile fragement is included from the per-target +# This Makefile fragment is included from the per-target # Makefile.target so will be invoked for each linux-user program we # build. We have two options for compiling, either using a configured # guest compiler or calling one of our docker images to do it for us. @@ -67,7 +67,7 @@ endif ifneq ($(GUEST_BUILD),) guest-tests: $(GUEST_BUILD) -run-guest-tests: guest-tests qemu-$(TARGET_NAME) +run-guest-tests: guest-tests qemu-$(subst y,system-,$(CONFIG_SOFTMMU))$(TARGET_NAME) $(call quiet-command, \ (cd tests && $(MAKE) -f $(TCG_MAKE) SPEED=$(SPEED) run), \ "RUN", "tests for $(TARGET_NAME)") diff --git a/tests/tcg/Makefile.probe b/tests/tcg/Makefile.probe index 15c0412657..9dc654663d 100644 --- a/tests/tcg/Makefile.probe +++ b/tests/tcg/Makefile.probe @@ -2,7 +2,7 @@ # # TCG Compiler Probe # -# This Makefile fragement is included multiple times in the main make +# This Makefile fragment is included multiple times in the main make # script to probe for available compilers. This is used to build up a # selection of required docker targets before we invoke a sub-make for # each target. diff --git a/tests/tcg/README b/tests/tcg/README index a5643d33e7..2a58f9a058 100644 --- a/tests/tcg/README +++ b/tests/tcg/README @@ -10,6 +10,6 @@ with "make test-cris". LM32 ==== -The testsuite for LM32 is in tests/tcg/cris. You can run it +The testsuite for LM32 is in tests/tcg/lm32. You can run it with "make test-lm32". diff --git a/tests/tcg/aarch64/Makefile.include b/tests/tcg/aarch64/Makefile.include index de32c91235..5d4e4c6f99 100644 --- a/tests/tcg/aarch64/Makefile.include +++ b/tests/tcg/aarch64/Makefile.include @@ -3,6 +3,6 @@ # We don't have any bigendian build tools so we only use this for AArch64 ifeq ($(TARGET_NAME),aarch64) -DOCKER_IMAGE=debian-arm64-cross +DOCKER_IMAGE=debian-buster-arm64-cross DOCKER_CROSS_COMPILER=aarch64-linux-gnu-gcc endif diff --git a/tests/tcg/aarch64/Makefile.target b/tests/tcg/aarch64/Makefile.target index 08c45b8470..2bb914975b 100644 --- a/tests/tcg/aarch64/Makefile.target +++ b/tests/tcg/aarch64/Makefile.target @@ -8,10 +8,14 @@ VPATH += $(AARCH64_SRC) # we don't build any of the ARM tests AARCH64_TESTS=$(filter-out $(ARM_TESTS), $(TESTS)) AARCH64_TESTS+=fcvt -TESTS:=$(AARCH64_TESTS) fcvt: LDFLAGS+=-lm run-fcvt: fcvt $(call run-test,$<,$(QEMU) $<, "$< on $(TARGET_NAME)") $(call diff-out,$<,$(AARCH64_SRC)/fcvt.ref) + +AARCH64_TESTS += pauth-1 +run-pauth-%: QEMU += -cpu max + +TESTS:=$(AARCH64_TESTS) diff --git a/tests/tcg/aarch64/pauth-1.c b/tests/tcg/aarch64/pauth-1.c new file mode 100644 index 0000000000..a3c1443cd0 --- /dev/null +++ b/tests/tcg/aarch64/pauth-1.c @@ -0,0 +1,37 @@ +#include +#include +#include + +asm(".arch armv8.4-a"); + +#ifndef PR_PAC_RESET_KEYS +#define PR_PAC_RESET_KEYS 54 +#define PR_PAC_APDAKEY (1 << 2) +#endif + +#define TESTS 1000 + +int main() +{ + int x, i, count = 0; + void *p0 = &x, *p1, *p2; + float perc; + + for (i = 0; i < TESTS; i++) { + asm volatile("pacdza %0" : "=r"(p1) : "0"(p0)); + prctl(PR_PAC_RESET_KEYS, PR_PAC_APDAKEY, 0, 0, 0); + asm volatile("pacdza %0" : "=r"(p2) : "0"(p0)); + + if (p1 != p0) { + count++; + } + if (p1 != p2) { + count++; + } + } + + perc = (float) count / (float) (TESTS * 2); + printf("Ptr Check: %0.2f%%", perc * 100.0); + assert(perc > 0.95); + return 0; +} diff --git a/tests/tcg/alpha/test-cond.c b/tests/tcg/alpha/test-cond.c index e625313b3e..3e11c4c105 100644 --- a/tests/tcg/alpha/test-cond.c +++ b/tests/tcg/alpha/test-cond.c @@ -6,7 +6,7 @@ int test_##N (long a) \ { \ int res = 1; \ - \ + \ asm ("cmov"#N" %1,$31,%0" \ : "+r" (res) : "r" (a)); \ return !res; \ @@ -18,7 +18,7 @@ int test_##N (long a) \ int test_##N (long a) \ { \ int res = 1; \ - \ + \ asm ("b"#N" %1,1f\n\t" \ "addq $31,$31,%0\n\t" \ "1: unop\n" \ diff --git a/tests/tcg/arm/Makefile.softmmu-target b/tests/tcg/arm/Makefile.softmmu-target new file mode 100644 index 0000000000..49d48d8a1c --- /dev/null +++ b/tests/tcg/arm/Makefile.softmmu-target @@ -0,0 +1,29 @@ +# -*- Mode: makefile -*- +# +# ARM SoftMMU tests - included from tests/tcg/Makefile +# + +ifeq ($(TARGET_ABI_DIR),arm) + +ARM_SRC=$(SRC_PATH)/tests/tcg/arm + +# Set search path for all sources +VPATH += $(ARM_SRC) + +ARM_TESTS=test-armv6m-undef + +TESTS += $(ARM_TESTS) + +CFLAGS+=-Wl,--build-id=none -x assembler-with-cpp +LDFLAGS+=-nostdlib -N -static + +%: %.S %.ld + $(CC) $(CFLAGS) $(ASFLAGS) $< -o $@ $(LDFLAGS) -T $(ARM_SRC)/$@.ld + +# Specific Test Rules + +test-armv6m-undef: EXTRA_CFLAGS+=-mcpu=cortex-m0 + +run-test-armv6m-undef: QEMU_OPTS+=-semihosting -M microbit -kernel + +endif diff --git a/tests/tcg/arm/hello-arm.c b/tests/tcg/arm/hello-arm.c index e0daa7ad98..e33edf949f 100644 --- a/tests/tcg/arm/hello-arm.c +++ b/tests/tcg/arm/hello-arm.c @@ -11,7 +11,7 @@ #define __syscall_return(type, res) \ do { \ - return (type) (res); \ + return (type) (res); \ } while (0) #define _syscall0(type,name) \ @@ -33,7 +33,7 @@ type name(type1 arg1) { \ "mov %0,r0" \ : "=r" (__res) \ : "r" ((long)(arg1)) \ - : "r0","lr"); \ + : "r0","lr"); \ __syscall_return(type,__res); \ } @@ -47,7 +47,7 @@ type name(type1 arg1,type2 arg2) { \ "mov\t%0,r0" \ : "=r" (__res) \ : "r" ((long)(arg1)),"r" ((long)(arg2)) \ - : "r0","r1","lr"); \ + : "r0","r1","lr"); \ __syscall_return(type,__res); \ } @@ -78,9 +78,9 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ "mov\tr3,%4\n\t" \ __syscall(name) \ "mov\t%0,r0" \ - : "=r" (__res) \ - : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)) \ - : "r0","r1","r2","r3","lr"); \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)) \ + : "r0","r1","r2","r3","lr"); \ __syscall_return(type,__res); \ } @@ -96,10 +96,10 @@ type name(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) { \ "mov\tr4,%5\n\t" \ __syscall(name) \ "mov\t%0,r0" \ - : "=r" (__res) \ - : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)), \ - "r" ((long)(arg5)) \ - : "r0","r1","r2","r3","r4","lr"); \ + : "=r" (__res) \ + : "r" ((long)(arg1)),"r" ((long)(arg2)),"r" ((long)(arg3)),"r" ((long)(arg4)), \ + "r" ((long)(arg5)) \ + : "r0","r1","r2","r3","r4","lr"); \ __syscall_return(type,__res); \ } diff --git a/tests/tcg/arm/test-armv6m-undef.S b/tests/tcg/arm/test-armv6m-undef.S new file mode 100644 index 0000000000..d18ca56b4a --- /dev/null +++ b/tests/tcg/arm/test-armv6m-undef.S @@ -0,0 +1,154 @@ +/* + * Test ARMv6-M UNDEFINED 32-bit instructions + * + * Copyright 2018 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. + */ + +/* + * Test that UNDEFINED 32-bit instructions fault as expected. This is an + * interesting test because ARMv6-M shares code with its more fully-featured + * siblings and it's necessary to verify that its limited instruction set is + * emulated correctly. + * + * The emulator must be invoked with -semihosting so that the test case can + * terminate with exit code 0 on success or 1 on failure. + * + * Failures can be debugged with -d in_asm,int,exec,cpu and the + * gdbstub (-S -s). + */ + +.syntax unified +.cpu cortex-m0 +.thumb + +/* + * Memory map + */ +#define SRAM_BASE 0x20000000 +#define SRAM_SIZE (16 * 1024) + +/* + * Semihosting interface on ARM T32 + * See "Semihosting for AArch32 and AArch64 Version 2.0 Documentation" by ARM + */ +#define semihosting_call bkpt 0xab +#define SYS_EXIT 0x18 + +vector_table: + .word SRAM_BASE + SRAM_SIZE /* 0. SP_main */ + .word exc_reset_thumb /* 1. Reset */ + .word 0 /* 2. NMI */ + .word exc_hard_fault_thumb /* 3. HardFault */ + .rept 7 + .word 0 /* 4-10. Reserved */ + .endr + .word 0 /* 11. SVCall */ + .word 0 /* 12. Reserved */ + .word 0 /* 13. Reserved */ + .word 0 /* 14. PendSV */ + .word 0 /* 15. SysTick */ + .rept 32 + .word 0 /* 16-47. External Interrupts */ + .endr + +exc_reset: +.equ exc_reset_thumb, exc_reset + 1 +.global exc_reset_thumb + /* The following 32-bit UNDEFINED instructions are tested by executing + * them. The HardFault exception handler should execute and return to + * the next test case. If no exception is raised the test fails. + */ + + /* Table A5-9 32-bit Thumb encoding */ + .short 0b1110100000000000 + .short 0b0000000000000000 + b not_reached + .short 0b1110100000000000 + .short 0b1000000000000000 + b not_reached + .short 0b1111100000000000 + .short 0b0000000000000000 + b not_reached + .short 0b1111100000000000 + .short 0b1000000000000000 + b not_reached + .short 0b1111000000000000 + .short 0b0000000000000000 + b not_reached + + /* Table A5-10 Branch and miscellaneous control instructions */ + .short 0b1111011111110000 + .short 0b1010000000000000 + b not_reached + + /* The following are valid 32-bit instructions that must not raise a + * HardFault. + */ + + /* B4.2.3 Move to Special Register (moves to IPSR are ignored) */ + msr ipsr, r0 + b 1f + b not_reached +1: + /* B4.2.2 Move from Special Register */ + mrs r0, ipsr + b 1f + b not_reached +1: + /* A6.7.13 Branch with Link (immediate) */ + bl 1f +1: + b 1f + b not_reached +1: + /* A6.7.21 Data Memory Barrier */ + dmb + b 1f + b not_reached +1: + /* A6.7.22 Data Synchronization Barrier */ + dsb + b 1f + b not_reached +1: + /* A6.7.24 Instruction Memory Barrier */ + isb + b 1f + b not_reached +1: + + /* Success! */ + movs r0, 1 + b exit + +not_reached: /* Failure :( */ + movs r0, 0 + b exit + +/* When a HardFault occurs, return to pc+6 (test cases are 3 halfwords long) */ +exc_hard_fault: +.equ exc_hard_fault_thumb, exc_hard_fault + 1 +.global exc_hard_fault_thumb + ldr r0, [sp, 0x18] + adds r0, 6 + str r0, [sp, 0x18] + bx lr + +/* + * exit: Terminate emulator + * @r0: 0 - failure, 1 - success + */ +exit: + movs r1, 0 + cmp r0, 1 + bne 1f + ldr r1, ADP_Stopped_ApplicationExit +1: + movs r0, SYS_EXIT + semihosting_call +.align 2 +ADP_Stopped_ApplicationExit: + .word 0x20026 diff --git a/tests/tcg/arm/test-armv6m-undef.ld b/tests/tcg/arm/test-armv6m-undef.ld new file mode 100644 index 0000000000..43dbbf17d5 --- /dev/null +++ b/tests/tcg/arm/test-armv6m-undef.ld @@ -0,0 +1,21 @@ +ENTRY(exc_reset_thumb) + +SECTIONS +{ + . = 0x0; + .text : { + *(.text) + } + .data : { + *(.data) + } + .rodata : { + *(.rodata) + } + .bss : { + *(.bss) + } + /DISCARD/ : { + *(.ARM.attributes) + } +} diff --git a/tests/tcg/cris/Makefile b/tests/tcg/cris/Makefile deleted file mode 100644 index 664b30ce81..0000000000 --- a/tests/tcg/cris/Makefile +++ /dev/null @@ -1,168 +0,0 @@ --include ../../../config-host.mak - -CROSS=crisv32-axis-linux-gnu- -SIM=../../../cris-linux-user/qemu-cris -L ./ -SIMG=cris-axis-linux-gnu-run --sysroot=./ - -CC = $(CROSS)gcc -#AS = $(CROSS)as -AS = $(CC) -x assembler-with-cpp -SIZE = $(CROSS)size -LD = $(CC) -OBJCOPY = $(CROSS)objcopy - -# we rely on GCC inline:ing the stuff we tell it to in many places here. -CFLAGS = -Winline -Wall -g -O2 -static -NOSTDFLAGS = -nostartfiles -nostdlib -ASFLAGS += -g -Wa,-I,$(SRC_PATH)/tests/tcg/cris/ -LDLIBS = -NOSTDLIBS = -lgcc - -CRT = crt.o -SYS = sys.o -TESTCASES += check_abs.tst -TESTCASES += check_addc.tst -TESTCASES += check_addcm.tst -TESTCASES += check_addcv17.tst -TESTCASES += check_addo.tst -TESTCASES += check_addoq.tst -TESTCASES += check_addi.tst -TESTCASES += check_addiv32.tst -TESTCASES += check_addm.tst -TESTCASES += check_addr.tst -TESTCASES += check_addq.tst -TESTCASES += check_addxc.tst -TESTCASES += check_addxm.tst -TESTCASES += check_addxr.tst -TESTCASES += check_andc.tst -TESTCASES += check_andm.tst -TESTCASES += check_andr.tst -TESTCASES += check_andq.tst -TESTCASES += check_asr.tst -TESTCASES += check_ba.tst -TESTCASES += check_bas.tst -TESTCASES += check_bcc.tst -TESTCASES += check_bound.tst -TESTCASES += check_boundc.tst -TESTCASES += check_boundr.tst -TESTCASES += check_btst.tst -TESTCASES += check_clearfv32.tst -TESTCASES += check_cmpc.tst -TESTCASES += check_cmpr.tst -TESTCASES += check_cmpq.tst -TESTCASES += check_cmpm.tst -TESTCASES += check_cmpxc.tst -TESTCASES += check_cmpxm.tst -TESTCASES += check_cmp-2.tst -TESTCASES += check_clrjmp1.tst -TESTCASES += check_dstep.tst -TESTCASES += check_ftag.tst -TESTCASES += check_int64.tst -# check_jsr is broken. -#TESTCASES += check_jsr.tst -TESTCASES += check_mcp.tst -TESTCASES += check_movei.tst -TESTCASES += check_mover.tst -TESTCASES += check_moverm.tst -TESTCASES += check_moveq.tst -TESTCASES += check_movemr.tst -TESTCASES += check_movemrv32.tst -TESTCASES += check_movecr.tst -TESTCASES += check_movmp.tst -TESTCASES += check_movpr.tst -TESTCASES += check_movprv32.tst -TESTCASES += check_movdelsr1.tst -TESTCASES += check_movpmv32.tst -TESTCASES += check_movsr.tst -TESTCASES += check_movsm.tst -TESTCASES += check_movscr.tst -TESTCASES += check_movur.tst -TESTCASES += check_movum.tst -TESTCASES += check_movucr.tst -TESTCASES += check_mulx.tst -TESTCASES += check_mulv32.tst -TESTCASES += check_neg.tst -TESTCASES += check_not.tst -TESTCASES += check_lz.tst -TESTCASES += check_lapc.tst -TESTCASES += check_lsl.tst -TESTCASES += check_lsr.tst -TESTCASES += check_orc.tst -TESTCASES += check_orm.tst -TESTCASES += check_orr.tst -TESTCASES += check_orq.tst -TESTCASES += check_ret.tst -TESTCASES += check_swap.tst -TESTCASES += check_scc.tst -TESTCASES += check_subc.tst -TESTCASES += check_subq.tst -TESTCASES += check_subr.tst -TESTCASES += check_subm.tst -TESTCASES += check_glibc_kernelversion.tst -TESTCASES += check_xarith.tst - -TESTCASES += check_hello.ctst -TESTCASES += check_stat1.ctst -TESTCASES += check_stat2.ctst -TESTCASES += check_stat3.ctst -TESTCASES += check_stat4.ctst -TESTCASES += check_openpf1.ctst -TESTCASES += check_openpf2.ctst -TESTCASES += check_openpf3.ctst -TESTCASES += check_openpf5.ctst -TESTCASES += check_mapbrk.ctst -TESTCASES += check_mmap1.ctst -TESTCASES += check_mmap2.ctst -TESTCASES += check_mmap3.ctst -TESTCASES += check_sigalrm.ctst -TESTCASES += check_time2.ctst -TESTCASES += check_settls1.ctst - -TESTCASES += check_gcctorture_pr28634-1.ctst -#TESTCASES += check_gcctorture_pr28634.ctst - -all: build - -%.o: $(SRC_PATH)/tests/tcg/cris/%.c - $(CC) $(CFLAGS) -c $< -o $@ - -%.o: $(SRC_PATH)/tests/tcg/cris/%.s - $(AS) $(ASFLAGS) -c $< -o $@ - -%.tst: %.o - $(CC) $(CFLAGS) $(NOSTDFLAGS) $(LDLIBS) $(NOSTDLIBS) $(CRT) $< $(SYS) -o $@ - -%.ctst: %.o - $(CC) $(CFLAGS) $(LDLIBS) $< -o $@ - - -sysv10.o: sys.c - $(CC) $(CFLAGS) -mcpu=v10 -c $< -o $@ - -crtv10.o: crt.s - $(AS) $(ASFLAGS) -mcpu=v10 -c $< -o $@ - -check_addcv17.tst: ASFLAGS += -mcpu=v10 -check_addcv17.tst: CRT := crtv10.o -check_addcv17.tst: SYS := sysv10.o -check_addcv17.tst: crtv10.o sysv10.o - -build: $(CRT) $(SYS) $(TESTCASES) - -check: $(CRT) $(SYS) $(TESTCASES) - @printf "\nQEMU simulator.\n" - for case in $(TESTCASES); do \ - printf %s "$$case "; \ - SIMARGS=; \ - case $$case in *v17*) SIMARGS="-cpu crisv17";; esac; \ - $(SIM) $$SIMARGS ./$$case; \ - done -check-g: $(CRT) $(SYS) $(TESTCASES) - @printf "\nGDB simulator.\n" - @for case in $(TESTCASES); do \ - printf %s "$$case "; \ - $(SIMG) $$case; \ - done - -clean: - $(RM) -fr $(TESTCASES) *.o diff --git a/tests/tcg/cris/Makefile.include b/tests/tcg/cris/Makefile.include new file mode 100644 index 0000000000..1c037824bf --- /dev/null +++ b/tests/tcg/cris/Makefile.include @@ -0,0 +1,6 @@ +# +# Makefile.include for all CRIS targets +# + +DOCKER_IMAGE=fedora-cris-cross +DOCKER_CROSS_COMPILER=cris-linux-gnu-gcc diff --git a/tests/tcg/cris/Makefile.target b/tests/tcg/cris/Makefile.target new file mode 100644 index 0000000000..c1173ead42 --- /dev/null +++ b/tests/tcg/cris/Makefile.target @@ -0,0 +1,58 @@ +# -*- Mode: makefile -*- +# +# Cris tests +# +# Currently we can only build the "bare" tests with the docker +# supplied cross-compiler. +# + +CRIS_SRC = $(SRC_PATH)/tests/tcg/cris/bare +CRIS_ALL = $(wildcard $(CRIS_SRC)/*.s) +CRIS_TESTS = $(patsubst $(CRIS_SRC)/%.s, %, $(CRIS_ALL)) +# Filter out common blobs and broken tests +CRIS_BROKEN_TESTS = crt check_jsr +# upstream GCC doesn't support v32 +CRIS_BROKEN_TESTS += check_mcp check_mulv32 check_addiv32 check_movpmv32 +CRIS_BROKEN_TESTS += check_movprv32 check_clearfv32 check_movemrv32 check_bas +CRIS_BROKEN_TESTS += check_lapc check_movei +# no sure why +CRIS_BROKEN_TESTS += check_scc check_xarith + +CRIS_USABLE_TESTS = $(filter-out $(CRIS_BROKEN_TESTS), $(CRIS_TESTS)) +CRIS_RUNS = $(patsubst %, run-%, $(CRIS_USABLE_TESTS)) + +# override the list of tests, as we can't build the multiarch tests +TESTS = $(CRIS_USABLE_TESTS) +VPATH = $(CRIS_SRC) + +AS = $(CC) -x assembler-with-cpp +LD = $(CC) + +# we rely on GCC inline:ing the stuff we tell it to in many places here. +CFLAGS = -Winline -Wall -g -O2 -static +NOSTDFLAGS = -nostartfiles -nostdlib +ASFLAGS += -mcpu=v10 -g -Wa,-I,$(SRC_PATH)/tests/tcg/cris/bare +CRT_FILES = crt.o sys.o + +# stop make deleting crt files if build fails +.PRECIOUS: $(CRT_FILES) + +%.o: %.c + $(CC) -c $< -o $@ + +%.o: %.s + $(AS) $(ASFLAGS) -c $< -o $@ + +%: %.s $(CRT_FILES) + $(CC) $(ASFLAGS) $< -o $@ $(LDFLAGS) $(NOSTDFLAGS) $(CRT_FILES) + +# The default CPU breaks (possibly as it's max?) so force crisv17 +$(CRIS_RUNS): QEMU_OPTS=-cpu crisv17 + +# Additional runners to run under GNU SIM +CRIS_RUNS_ON_SIM=$(patsubst %, %-on-sim, $(CRIS_RUNS)) +SIMG:=cris-axis-linux-gnu-run + +# e.g.: make -f ../../tests/tcg/Makefile run-check_orm-on-sim +run-%-on-sim: + $(call run-test, $<, $(SIMG) $<, "$< on $(TARGET_NAME) with SIM") diff --git a/tests/tcg/cris/check_addcv17.s b/tests/tcg/cris/bare/check_addcv17.s similarity index 100% rename from tests/tcg/cris/check_addcv17.s rename to tests/tcg/cris/bare/check_addcv17.s diff --git a/tests/tcg/cris/check_addi.s b/tests/tcg/cris/bare/check_addi.s similarity index 100% rename from tests/tcg/cris/check_addi.s rename to tests/tcg/cris/bare/check_addi.s diff --git a/tests/tcg/cris/check_addiv32.s b/tests/tcg/cris/bare/check_addiv32.s similarity index 100% rename from tests/tcg/cris/check_addiv32.s rename to tests/tcg/cris/bare/check_addiv32.s diff --git a/tests/tcg/cris/check_addm.s b/tests/tcg/cris/bare/check_addm.s similarity index 100% rename from tests/tcg/cris/check_addm.s rename to tests/tcg/cris/bare/check_addm.s diff --git a/tests/tcg/cris/check_addq.s b/tests/tcg/cris/bare/check_addq.s similarity index 100% rename from tests/tcg/cris/check_addq.s rename to tests/tcg/cris/bare/check_addq.s diff --git a/tests/tcg/cris/check_addr.s b/tests/tcg/cris/bare/check_addr.s similarity index 100% rename from tests/tcg/cris/check_addr.s rename to tests/tcg/cris/bare/check_addr.s diff --git a/tests/tcg/cris/check_addxc.s b/tests/tcg/cris/bare/check_addxc.s similarity index 100% rename from tests/tcg/cris/check_addxc.s rename to tests/tcg/cris/bare/check_addxc.s diff --git a/tests/tcg/cris/check_addxm.s b/tests/tcg/cris/bare/check_addxm.s similarity index 100% rename from tests/tcg/cris/check_addxm.s rename to tests/tcg/cris/bare/check_addxm.s diff --git a/tests/tcg/cris/check_addxr.s b/tests/tcg/cris/bare/check_addxr.s similarity index 100% rename from tests/tcg/cris/check_addxr.s rename to tests/tcg/cris/bare/check_addxr.s diff --git a/tests/tcg/cris/check_andc.s b/tests/tcg/cris/bare/check_andc.s similarity index 100% rename from tests/tcg/cris/check_andc.s rename to tests/tcg/cris/bare/check_andc.s diff --git a/tests/tcg/cris/check_andm.s b/tests/tcg/cris/bare/check_andm.s similarity index 100% rename from tests/tcg/cris/check_andm.s rename to tests/tcg/cris/bare/check_andm.s diff --git a/tests/tcg/cris/check_andq.s b/tests/tcg/cris/bare/check_andq.s similarity index 100% rename from tests/tcg/cris/check_andq.s rename to tests/tcg/cris/bare/check_andq.s diff --git a/tests/tcg/cris/check_andr.s b/tests/tcg/cris/bare/check_andr.s similarity index 100% rename from tests/tcg/cris/check_andr.s rename to tests/tcg/cris/bare/check_andr.s diff --git a/tests/tcg/cris/check_asr.s b/tests/tcg/cris/bare/check_asr.s similarity index 100% rename from tests/tcg/cris/check_asr.s rename to tests/tcg/cris/bare/check_asr.s diff --git a/tests/tcg/cris/check_ba.s b/tests/tcg/cris/bare/check_ba.s similarity index 100% rename from tests/tcg/cris/check_ba.s rename to tests/tcg/cris/bare/check_ba.s diff --git a/tests/tcg/cris/check_bas.s b/tests/tcg/cris/bare/check_bas.s similarity index 100% rename from tests/tcg/cris/check_bas.s rename to tests/tcg/cris/bare/check_bas.s diff --git a/tests/tcg/cris/check_bcc.s b/tests/tcg/cris/bare/check_bcc.s similarity index 100% rename from tests/tcg/cris/check_bcc.s rename to tests/tcg/cris/bare/check_bcc.s diff --git a/tests/tcg/cris/check_boundc.s b/tests/tcg/cris/bare/check_boundc.s similarity index 100% rename from tests/tcg/cris/check_boundc.s rename to tests/tcg/cris/bare/check_boundc.s diff --git a/tests/tcg/cris/check_boundr.s b/tests/tcg/cris/bare/check_boundr.s similarity index 100% rename from tests/tcg/cris/check_boundr.s rename to tests/tcg/cris/bare/check_boundr.s diff --git a/tests/tcg/cris/check_btst.s b/tests/tcg/cris/bare/check_btst.s similarity index 89% rename from tests/tcg/cris/check_btst.s rename to tests/tcg/cris/bare/check_btst.s index e39fc8f4d6..485deb2006 100644 --- a/tests/tcg/cris/check_btst.s +++ b/tests/tcg/cris/bare/check_btst.s @@ -85,12 +85,12 @@ checkr3 1111 ; check that X gets cleared and that only the NZ flags are touched. - move.d 0xff, $r0 - move $r0, $ccs - btst r3,r3 - move $ccs, $r0 - and.d 0xff, $r0 - cmp.d 0xe3, $r0 - test_cc 0 1 0 0 + ;; move.d 0xff, $r0 + ;; move $r0, $ccs + ;; btst r3,r3 + ;; move $ccs, $r0 + ;; and.d 0xff, $r0 + ;; cmp.d 0xe3, $r0 + ;; test_cc 0 1 0 0 quit diff --git a/tests/tcg/cris/check_clearfv32.s b/tests/tcg/cris/bare/check_clearfv32.s similarity index 100% rename from tests/tcg/cris/check_clearfv32.s rename to tests/tcg/cris/bare/check_clearfv32.s diff --git a/tests/tcg/cris/check_clrjmp1.s b/tests/tcg/cris/bare/check_clrjmp1.s similarity index 100% rename from tests/tcg/cris/check_clrjmp1.s rename to tests/tcg/cris/bare/check_clrjmp1.s diff --git a/tests/tcg/cris/check_cmp-2.s b/tests/tcg/cris/bare/check_cmp-2.s similarity index 100% rename from tests/tcg/cris/check_cmp-2.s rename to tests/tcg/cris/bare/check_cmp-2.s diff --git a/tests/tcg/cris/check_cmpc.s b/tests/tcg/cris/bare/check_cmpc.s similarity index 100% rename from tests/tcg/cris/check_cmpc.s rename to tests/tcg/cris/bare/check_cmpc.s diff --git a/tests/tcg/cris/check_cmpm.s b/tests/tcg/cris/bare/check_cmpm.s similarity index 100% rename from tests/tcg/cris/check_cmpm.s rename to tests/tcg/cris/bare/check_cmpm.s diff --git a/tests/tcg/cris/check_cmpq.s b/tests/tcg/cris/bare/check_cmpq.s similarity index 100% rename from tests/tcg/cris/check_cmpq.s rename to tests/tcg/cris/bare/check_cmpq.s diff --git a/tests/tcg/cris/check_cmpr.s b/tests/tcg/cris/bare/check_cmpr.s similarity index 100% rename from tests/tcg/cris/check_cmpr.s rename to tests/tcg/cris/bare/check_cmpr.s diff --git a/tests/tcg/cris/check_cmpxc.s b/tests/tcg/cris/bare/check_cmpxc.s similarity index 100% rename from tests/tcg/cris/check_cmpxc.s rename to tests/tcg/cris/bare/check_cmpxc.s diff --git a/tests/tcg/cris/check_cmpxm.s b/tests/tcg/cris/bare/check_cmpxm.s similarity index 100% rename from tests/tcg/cris/check_cmpxm.s rename to tests/tcg/cris/bare/check_cmpxm.s diff --git a/tests/tcg/cris/check_dstep.s b/tests/tcg/cris/bare/check_dstep.s similarity index 100% rename from tests/tcg/cris/check_dstep.s rename to tests/tcg/cris/bare/check_dstep.s diff --git a/tests/tcg/cris/check_jsr.s b/tests/tcg/cris/bare/check_jsr.s similarity index 100% rename from tests/tcg/cris/check_jsr.s rename to tests/tcg/cris/bare/check_jsr.s diff --git a/tests/tcg/cris/check_lapc.s b/tests/tcg/cris/bare/check_lapc.s similarity index 100% rename from tests/tcg/cris/check_lapc.s rename to tests/tcg/cris/bare/check_lapc.s diff --git a/tests/tcg/cris/check_lsl.s b/tests/tcg/cris/bare/check_lsl.s similarity index 100% rename from tests/tcg/cris/check_lsl.s rename to tests/tcg/cris/bare/check_lsl.s diff --git a/tests/tcg/cris/check_lsr.s b/tests/tcg/cris/bare/check_lsr.s similarity index 100% rename from tests/tcg/cris/check_lsr.s rename to tests/tcg/cris/bare/check_lsr.s diff --git a/tests/tcg/cris/check_mcp.s b/tests/tcg/cris/bare/check_mcp.s similarity index 100% rename from tests/tcg/cris/check_mcp.s rename to tests/tcg/cris/bare/check_mcp.s diff --git a/tests/tcg/cris/check_movdelsr1.s b/tests/tcg/cris/bare/check_movdelsr1.s similarity index 100% rename from tests/tcg/cris/check_movdelsr1.s rename to tests/tcg/cris/bare/check_movdelsr1.s diff --git a/tests/tcg/cris/check_movecr.s b/tests/tcg/cris/bare/check_movecr.s similarity index 100% rename from tests/tcg/cris/check_movecr.s rename to tests/tcg/cris/bare/check_movecr.s diff --git a/tests/tcg/cris/check_movei.s b/tests/tcg/cris/bare/check_movei.s similarity index 100% rename from tests/tcg/cris/check_movei.s rename to tests/tcg/cris/bare/check_movei.s diff --git a/tests/tcg/cris/check_movemr.s b/tests/tcg/cris/bare/check_movemr.s similarity index 100% rename from tests/tcg/cris/check_movemr.s rename to tests/tcg/cris/bare/check_movemr.s diff --git a/tests/tcg/cris/check_movemrv32.s b/tests/tcg/cris/bare/check_movemrv32.s similarity index 100% rename from tests/tcg/cris/check_movemrv32.s rename to tests/tcg/cris/bare/check_movemrv32.s diff --git a/tests/tcg/cris/check_mover.s b/tests/tcg/cris/bare/check_mover.s similarity index 100% rename from tests/tcg/cris/check_mover.s rename to tests/tcg/cris/bare/check_mover.s diff --git a/tests/tcg/cris/check_moverm.s b/tests/tcg/cris/bare/check_moverm.s similarity index 100% rename from tests/tcg/cris/check_moverm.s rename to tests/tcg/cris/bare/check_moverm.s diff --git a/tests/tcg/cris/check_movmp.s b/tests/tcg/cris/bare/check_movmp.s similarity index 100% rename from tests/tcg/cris/check_movmp.s rename to tests/tcg/cris/bare/check_movmp.s diff --git a/tests/tcg/cris/check_movpmv32.s b/tests/tcg/cris/bare/check_movpmv32.s similarity index 100% rename from tests/tcg/cris/check_movpmv32.s rename to tests/tcg/cris/bare/check_movpmv32.s diff --git a/tests/tcg/cris/check_movpr.s b/tests/tcg/cris/bare/check_movpr.s similarity index 100% rename from tests/tcg/cris/check_movpr.s rename to tests/tcg/cris/bare/check_movpr.s diff --git a/tests/tcg/cris/check_movprv32.s b/tests/tcg/cris/bare/check_movprv32.s similarity index 100% rename from tests/tcg/cris/check_movprv32.s rename to tests/tcg/cris/bare/check_movprv32.s diff --git a/tests/tcg/cris/check_movscr.s b/tests/tcg/cris/bare/check_movscr.s similarity index 100% rename from tests/tcg/cris/check_movscr.s rename to tests/tcg/cris/bare/check_movscr.s diff --git a/tests/tcg/cris/check_movsm.s b/tests/tcg/cris/bare/check_movsm.s similarity index 100% rename from tests/tcg/cris/check_movsm.s rename to tests/tcg/cris/bare/check_movsm.s diff --git a/tests/tcg/cris/check_movsr.s b/tests/tcg/cris/bare/check_movsr.s similarity index 100% rename from tests/tcg/cris/check_movsr.s rename to tests/tcg/cris/bare/check_movsr.s diff --git a/tests/tcg/cris/check_movucr.s b/tests/tcg/cris/bare/check_movucr.s similarity index 100% rename from tests/tcg/cris/check_movucr.s rename to tests/tcg/cris/bare/check_movucr.s diff --git a/tests/tcg/cris/check_movum.s b/tests/tcg/cris/bare/check_movum.s similarity index 100% rename from tests/tcg/cris/check_movum.s rename to tests/tcg/cris/bare/check_movum.s diff --git a/tests/tcg/cris/check_movur.s b/tests/tcg/cris/bare/check_movur.s similarity index 100% rename from tests/tcg/cris/check_movur.s rename to tests/tcg/cris/bare/check_movur.s diff --git a/tests/tcg/cris/check_mulv32.s b/tests/tcg/cris/bare/check_mulv32.s similarity index 100% rename from tests/tcg/cris/check_mulv32.s rename to tests/tcg/cris/bare/check_mulv32.s diff --git a/tests/tcg/cris/check_mulx.s b/tests/tcg/cris/bare/check_mulx.s similarity index 98% rename from tests/tcg/cris/check_mulx.s rename to tests/tcg/cris/bare/check_mulx.s index d43241a6f5..a7a1f82a82 100644 --- a/tests/tcg/cris/check_mulx.s +++ b/tests/tcg/cris/bare/check_mulx.s @@ -3,6 +3,8 @@ .include "testutils.inc" start + + .align 4 moveq -1,r3 moveq 2,r4 muls.d r4,r3 @@ -11,6 +13,7 @@ move mof,r3 checkr3 ffffffff + .align 4 moveq -1,r3 moveq 2,r4 mulu.d r4,r3 @@ -19,6 +22,7 @@ move mof,r3 checkr3 1 + .align 4 moveq 2,r3 moveq -1,r4 muls.d r4,r3 @@ -27,6 +31,7 @@ move mof,r3 checkr3 ffffffff + .align 4 moveq 2,r3 moveq -1,r4 mulu.d r4,r3 @@ -98,6 +103,7 @@ checkr3 1fffe move mof,r3 checkr3 0 + nop moveq 2,r3 move.d 0xffff,r4 @@ -138,6 +144,7 @@ checkr3 fdbdade2 move mof,r3 checkr3 ffffffff + nop move.d 0x5432f789,r4 move.d 0x78134452,r3 @@ -146,6 +153,7 @@ checkr3 420fade2 move mof,r3 checkr3 0 + nop move.d 0xff,r3 moveq 2,r4 @@ -186,6 +194,7 @@ checkr3 1 move mof,r3 checkr3 0 + nop moveq -1,r4 move.d r4,r3 @@ -194,6 +203,7 @@ checkr3 fe01 move mof,r3 checkr3 0 + nop move.d 0xfeda49ff,r4 move.d r4,r3 @@ -202,6 +212,7 @@ checkr3 1 move mof,r3 checkr3 0 + nop move.d 0xfeda49ff,r4 move.d r4,r3 diff --git a/tests/tcg/cris/check_neg.s b/tests/tcg/cris/bare/check_neg.s similarity index 100% rename from tests/tcg/cris/check_neg.s rename to tests/tcg/cris/bare/check_neg.s diff --git a/tests/tcg/cris/check_not.s b/tests/tcg/cris/bare/check_not.s similarity index 100% rename from tests/tcg/cris/check_not.s rename to tests/tcg/cris/bare/check_not.s diff --git a/tests/tcg/cris/check_orc.s b/tests/tcg/cris/bare/check_orc.s similarity index 100% rename from tests/tcg/cris/check_orc.s rename to tests/tcg/cris/bare/check_orc.s diff --git a/tests/tcg/cris/check_orm.s b/tests/tcg/cris/bare/check_orm.s similarity index 100% rename from tests/tcg/cris/check_orm.s rename to tests/tcg/cris/bare/check_orm.s diff --git a/tests/tcg/cris/check_orq.s b/tests/tcg/cris/bare/check_orq.s similarity index 100% rename from tests/tcg/cris/check_orq.s rename to tests/tcg/cris/bare/check_orq.s diff --git a/tests/tcg/cris/check_orr.s b/tests/tcg/cris/bare/check_orr.s similarity index 100% rename from tests/tcg/cris/check_orr.s rename to tests/tcg/cris/bare/check_orr.s diff --git a/tests/tcg/cris/check_ret.s b/tests/tcg/cris/bare/check_ret.s similarity index 100% rename from tests/tcg/cris/check_ret.s rename to tests/tcg/cris/bare/check_ret.s diff --git a/tests/tcg/cris/check_scc.s b/tests/tcg/cris/bare/check_scc.s similarity index 100% rename from tests/tcg/cris/check_scc.s rename to tests/tcg/cris/bare/check_scc.s diff --git a/tests/tcg/cris/check_subc.s b/tests/tcg/cris/bare/check_subc.s similarity index 100% rename from tests/tcg/cris/check_subc.s rename to tests/tcg/cris/bare/check_subc.s diff --git a/tests/tcg/cris/check_subm.s b/tests/tcg/cris/bare/check_subm.s similarity index 100% rename from tests/tcg/cris/check_subm.s rename to tests/tcg/cris/bare/check_subm.s diff --git a/tests/tcg/cris/check_subq.s b/tests/tcg/cris/bare/check_subq.s similarity index 100% rename from tests/tcg/cris/check_subq.s rename to tests/tcg/cris/bare/check_subq.s diff --git a/tests/tcg/cris/check_subr.s b/tests/tcg/cris/bare/check_subr.s similarity index 100% rename from tests/tcg/cris/check_subr.s rename to tests/tcg/cris/bare/check_subr.s diff --git a/tests/tcg/cris/check_xarith.s b/tests/tcg/cris/bare/check_xarith.s similarity index 100% rename from tests/tcg/cris/check_xarith.s rename to tests/tcg/cris/bare/check_xarith.s diff --git a/tests/tcg/cris/crt.s b/tests/tcg/cris/bare/crt.s similarity index 100% rename from tests/tcg/cris/crt.s rename to tests/tcg/cris/bare/crt.s diff --git a/tests/tcg/cris/bare/sys.c b/tests/tcg/cris/bare/sys.c new file mode 100644 index 0000000000..1644eecc33 --- /dev/null +++ b/tests/tcg/cris/bare/sys.c @@ -0,0 +1,63 @@ +/* + * Helper functions for CRIS system tests + * + * There is no libc and only a limited set of headers. + */ + +#include + +void exit(int status) +{ + register unsigned int callno asm ("r9") = 1; /* NR_exit */ + + asm volatile ("break 13\n" + : /* no outputs */ + : "r" (callno) + : "memory"); + while (1) { + /* do nothing */ + }; +} + +size_t write(int fd, const void *buf, size_t count) +{ + register unsigned int callno asm ("r9") = 4; /* NR_write */ + register unsigned int r10 asm ("r10") = fd; + register const void *r11 asm ("r11") = buf; + register size_t r12 asm ("r12") = count; + register unsigned int r asm ("r10"); + + asm volatile ("break 13\n" + : "=r" (r) + : "r" (callno), "0" (r10), "r" (r11), "r" (r12) + : "memory"); + + return r; +} + +static inline int mystrlen(char *s) +{ + int i = 0; + while (s[i]) { + i++; + } + return i; +} + + +void pass(void) +{ + char s[] = "passed.\n"; + write(1, s, sizeof(s) - 1); + exit(0); +} + +void _fail(char *reason) +{ + char s[] = "\nfailed: "; + int len = mystrlen(reason); + write(1, s, sizeof(s) - 1); + write(1, reason, len); + write(1, "\n", 1); + exit(1); +} diff --git a/tests/tcg/cris/testutils.inc b/tests/tcg/cris/bare/testutils.inc similarity index 100% rename from tests/tcg/cris/testutils.inc rename to tests/tcg/cris/bare/testutils.inc diff --git a/tests/tcg/cris/check_abs.c b/tests/tcg/cris/libc/check_abs.c similarity index 100% rename from tests/tcg/cris/check_abs.c rename to tests/tcg/cris/libc/check_abs.c diff --git a/tests/tcg/cris/check_addc.c b/tests/tcg/cris/libc/check_addc.c similarity index 100% rename from tests/tcg/cris/check_addc.c rename to tests/tcg/cris/libc/check_addc.c diff --git a/tests/tcg/cris/check_addcm.c b/tests/tcg/cris/libc/check_addcm.c similarity index 100% rename from tests/tcg/cris/check_addcm.c rename to tests/tcg/cris/libc/check_addcm.c diff --git a/tests/tcg/cris/check_addo.c b/tests/tcg/cris/libc/check_addo.c similarity index 100% rename from tests/tcg/cris/check_addo.c rename to tests/tcg/cris/libc/check_addo.c diff --git a/tests/tcg/cris/check_addoq.c b/tests/tcg/cris/libc/check_addoq.c similarity index 100% rename from tests/tcg/cris/check_addoq.c rename to tests/tcg/cris/libc/check_addoq.c diff --git a/tests/tcg/cris/check_bound.c b/tests/tcg/cris/libc/check_bound.c similarity index 100% rename from tests/tcg/cris/check_bound.c rename to tests/tcg/cris/libc/check_bound.c diff --git a/tests/tcg/cris/check_ftag.c b/tests/tcg/cris/libc/check_ftag.c similarity index 100% rename from tests/tcg/cris/check_ftag.c rename to tests/tcg/cris/libc/check_ftag.c diff --git a/tests/tcg/cris/check_gcctorture_pr28634-1.c b/tests/tcg/cris/libc/check_gcctorture_pr28634-1.c similarity index 100% rename from tests/tcg/cris/check_gcctorture_pr28634-1.c rename to tests/tcg/cris/libc/check_gcctorture_pr28634-1.c diff --git a/tests/tcg/cris/check_gcctorture_pr28634.c b/tests/tcg/cris/libc/check_gcctorture_pr28634.c similarity index 100% rename from tests/tcg/cris/check_gcctorture_pr28634.c rename to tests/tcg/cris/libc/check_gcctorture_pr28634.c diff --git a/tests/tcg/cris/check_glibc_kernelversion.c b/tests/tcg/cris/libc/check_glibc_kernelversion.c similarity index 98% rename from tests/tcg/cris/check_glibc_kernelversion.c rename to tests/tcg/cris/libc/check_glibc_kernelversion.c index 07448722c0..7aada89911 100644 --- a/tests/tcg/cris/check_glibc_kernelversion.c +++ b/tests/tcg/cris/libc/check_glibc_kernelversion.c @@ -109,8 +109,8 @@ int main(void) if (parts < 3) version <<= 8 * (3 - parts); - if (version < __LINUX_KERNEL_VERSION) - err(); - pass(); - exit(0); + if (version < __LINUX_KERNEL_VERSION) + err(); + pass(); + exit(0); } diff --git a/tests/tcg/cris/check_hello.c b/tests/tcg/cris/libc/check_hello.c similarity index 100% rename from tests/tcg/cris/check_hello.c rename to tests/tcg/cris/libc/check_hello.c diff --git a/tests/tcg/cris/check_int64.c b/tests/tcg/cris/libc/check_int64.c similarity index 100% rename from tests/tcg/cris/check_int64.c rename to tests/tcg/cris/libc/check_int64.c diff --git a/tests/tcg/cris/check_lz.c b/tests/tcg/cris/libc/check_lz.c similarity index 100% rename from tests/tcg/cris/check_lz.c rename to tests/tcg/cris/libc/check_lz.c diff --git a/tests/tcg/cris/check_mapbrk.c b/tests/tcg/cris/libc/check_mapbrk.c similarity index 100% rename from tests/tcg/cris/check_mapbrk.c rename to tests/tcg/cris/libc/check_mapbrk.c diff --git a/tests/tcg/cris/check_mmap1.c b/tests/tcg/cris/libc/check_mmap1.c similarity index 100% rename from tests/tcg/cris/check_mmap1.c rename to tests/tcg/cris/libc/check_mmap1.c diff --git a/tests/tcg/cris/check_mmap2.c b/tests/tcg/cris/libc/check_mmap2.c similarity index 100% rename from tests/tcg/cris/check_mmap2.c rename to tests/tcg/cris/libc/check_mmap2.c diff --git a/tests/tcg/cris/check_mmap3.c b/tests/tcg/cris/libc/check_mmap3.c similarity index 92% rename from tests/tcg/cris/check_mmap3.c rename to tests/tcg/cris/libc/check_mmap3.c index 34401fa0c9..cb890ef120 100644 --- a/tests/tcg/cris/check_mmap3.c +++ b/tests/tcg/cris/libc/check_mmap3.c @@ -17,7 +17,7 @@ int main (int argc, char *argv[]) /* Check that we can map a non-multiple of a page and still get a full page. */ a = mmap (NULL, 0x4c, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (a == NULL || a == (unsigned char *) -1) abort (); diff --git a/tests/tcg/cris/check_moveq.c b/tests/tcg/cris/libc/check_moveq.c similarity index 100% rename from tests/tcg/cris/check_moveq.c rename to tests/tcg/cris/libc/check_moveq.c diff --git a/tests/tcg/cris/check_openpf1.c b/tests/tcg/cris/libc/check_openpf1.c similarity index 98% rename from tests/tcg/cris/check_openpf1.c rename to tests/tcg/cris/libc/check_openpf1.c index fdcf4c5c3f..251d26eec2 100644 --- a/tests/tcg/cris/check_openpf1.c +++ b/tests/tcg/cris/libc/check_openpf1.c @@ -19,7 +19,7 @@ int main (int argc, char *argv[]) { fnam = malloc (strlen (argv[0]) + 2); if (fnam == NULL) - abort (); + abort (); strcpy (fnam, "/"); strcat (fnam, argv[0]); } diff --git a/tests/tcg/cris/check_openpf2.c b/tests/tcg/cris/libc/check_openpf2.c similarity index 100% rename from tests/tcg/cris/check_openpf2.c rename to tests/tcg/cris/libc/check_openpf2.c diff --git a/tests/tcg/cris/check_openpf3.c b/tests/tcg/cris/libc/check_openpf3.c similarity index 100% rename from tests/tcg/cris/check_openpf3.c rename to tests/tcg/cris/libc/check_openpf3.c diff --git a/tests/tcg/cris/check_openpf5.c b/tests/tcg/cris/libc/check_openpf5.c similarity index 100% rename from tests/tcg/cris/check_openpf5.c rename to tests/tcg/cris/libc/check_openpf5.c diff --git a/tests/tcg/cris/check_settls1.c b/tests/tcg/cris/libc/check_settls1.c similarity index 96% rename from tests/tcg/cris/check_settls1.c rename to tests/tcg/cris/libc/check_settls1.c index 69d202652a..3abc3a9ea8 100644 --- a/tests/tcg/cris/check_settls1.c +++ b/tests/tcg/cris/libc/check_settls1.c @@ -35,7 +35,7 @@ int main (void) syscall (SYS_set_thread_area, old_tp); if (tp != 0xeddeed00) { - * (volatile int *) 0 = 0; + * (volatile int *) 0 = 0; perror ("tls2"); abort (); } diff --git a/tests/tcg/cris/check_sigalrm.c b/tests/tcg/cris/libc/check_sigalrm.c similarity index 100% rename from tests/tcg/cris/check_sigalrm.c rename to tests/tcg/cris/libc/check_sigalrm.c diff --git a/tests/tcg/cris/check_stat1.c b/tests/tcg/cris/libc/check_stat1.c similarity index 100% rename from tests/tcg/cris/check_stat1.c rename to tests/tcg/cris/libc/check_stat1.c diff --git a/tests/tcg/cris/check_stat2.c b/tests/tcg/cris/libc/check_stat2.c similarity index 100% rename from tests/tcg/cris/check_stat2.c rename to tests/tcg/cris/libc/check_stat2.c diff --git a/tests/tcg/cris/check_stat3.c b/tests/tcg/cris/libc/check_stat3.c similarity index 100% rename from tests/tcg/cris/check_stat3.c rename to tests/tcg/cris/libc/check_stat3.c diff --git a/tests/tcg/cris/check_stat4.c b/tests/tcg/cris/libc/check_stat4.c similarity index 100% rename from tests/tcg/cris/check_stat4.c rename to tests/tcg/cris/libc/check_stat4.c diff --git a/tests/tcg/cris/check_swap.c b/tests/tcg/cris/libc/check_swap.c similarity index 100% rename from tests/tcg/cris/check_swap.c rename to tests/tcg/cris/libc/check_swap.c diff --git a/tests/tcg/cris/check_time2.c b/tests/tcg/cris/libc/check_time2.c similarity index 100% rename from tests/tcg/cris/check_time2.c rename to tests/tcg/cris/libc/check_time2.c diff --git a/tests/tcg/cris/crisutils.h b/tests/tcg/cris/libc/crisutils.h similarity index 100% rename from tests/tcg/cris/crisutils.h rename to tests/tcg/cris/libc/crisutils.h diff --git a/tests/tcg/cris/sys.h b/tests/tcg/cris/libc/sys.h similarity index 100% rename from tests/tcg/cris/sys.h rename to tests/tcg/cris/libc/sys.h diff --git a/tests/tcg/cris/sys.c b/tests/tcg/cris/sys.c deleted file mode 100644 index 21f08c0747..0000000000 --- a/tests/tcg/cris/sys.c +++ /dev/null @@ -1,59 +0,0 @@ -#include -#include -#include - -static inline int mystrlen(char *s) { - int i = 0; - while (s[i]) - i++; - return i; -} - -void pass(void) { - char s[] = "passed.\n"; - write (1, s, sizeof (s) - 1); - exit (0); -} - -void _fail(char *reason) { - char s[] = "\nfailed: "; - int len = mystrlen(reason); - write (1, s, sizeof (s) - 1); - write (1, reason, len); - write (1, "\n", 1); -// exit (1); -} - -void *memset (void *s, int c, size_t n) { - char *p = s; - int i; - for (i = 0; i < n; i++) - p[i] = c; - return p; -} - -void exit (int status) { - register unsigned int callno asm ("r9") = 1; /* NR_exit */ - - asm volatile ("break 13\n" - : - : "r" (callno) - : "memory" ); - while(1) - ; -} - -ssize_t write (int fd, const void *buf, size_t count) { - register unsigned int callno asm ("r9") = 4; /* NR_write */ - register unsigned int r10 asm ("r10") = fd; - register const void *r11 asm ("r11") = buf; - register size_t r12 asm ("r12") = count; - register unsigned int r asm ("r10"); - - asm volatile ("break 13\n" - : "=r" (r) - : "r" (callno), "0" (r10), "r" (r11), "r" (r12) - : "memory"); - - return r; -} diff --git a/tests/tcg/i386/Makefile.softmmu-target b/tests/tcg/i386/Makefile.softmmu-target new file mode 100644 index 0000000000..53c9c5ece0 --- /dev/null +++ b/tests/tcg/i386/Makefile.softmmu-target @@ -0,0 +1,46 @@ +# +# x86 system tests +# +# This currently builds only for i386. The common C code is built +# with standard compiler flags however so we can support both by +# adding additional boot files for x86_64. +# + +I386_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/i386/system +X64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/x86_64/system +# Set search path for all sources +VPATH+=$(I386_SYSTEM_SRC) + +# These objects provide the basic boot code and helper functions for all tests +CRT_OBJS=boot.o + +X86_TEST_SRCS=$(wildcard $(I386_SYSTEM_SRC)/*.c) +X86_TESTS = $(patsubst $(I386_SYSTEM_SRC)/%.c, %, $(X86_TEST_SRCS)) + +ifeq ($(TARGET_X86_64), y) +CRT_PATH=$(X64_SYSTEM_SRC) +LINK_SCRIPT=$(X64_SYSTEM_SRC)/kernel.ld +LDFLAGS=-Wl,-T$(LINK_SCRIPT) -Wl,-melf_x86_64 +else +CRT_PATH=$(I386_SYSTEM_SRC) +CFLAGS+=-m32 +LINK_SCRIPT=$(I386_SYSTEM_SRC)/kernel.ld +LDFLAGS=-Wl,-T$(LINK_SCRIPT) -Wl,-melf_i386 +# FIXME: move to common once x86_64 is bootstrapped +TESTS+=$(X86_TESTS) +endif +CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) +LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc + +# building head blobs +.PRECIOUS: $(CRT_OBJS) + +%.o: $(CRT_PATH)/%.S + $(CC) $(CFLAGS) -c $< -o $@ + +# Build and link the tests +%: %.c $(LINK_SCRIPT) $(CRT_OBJS) $(MINILIB_OBJS) + $(CC) $(CFLAGS) $< -o $@ $(LDFLAGS) + +# Running +QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel diff --git a/tests/tcg/i386/hello-i386.c b/tests/tcg/i386/hello-i386.c index cfeb24b2f5..59196dd0b7 100644 --- a/tests/tcg/i386/hello-i386.c +++ b/tests/tcg/i386/hello-i386.c @@ -4,19 +4,19 @@ static inline void exit(int status) { int __res; __asm__ volatile ("movl %%ecx,%%ebx\n"\ - "int $0x80" \ - : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status))); + "int $0x80" \ + : "=a" (__res) : "0" (__NR_exit),"c" ((long)(status))); } static inline int write(int fd, const char * buf, int len) { int status; __asm__ volatile ("pushl %%ebx\n"\ - "movl %%esi,%%ebx\n"\ - "int $0x80\n" \ - "popl %%ebx\n"\ - : "=a" (status) \ - : "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len))); + "movl %%esi,%%ebx\n"\ + "int $0x80\n" \ + "popl %%ebx\n"\ + : "=a" (status) \ + : "0" (__NR_write),"S" ((long)(fd)),"c" ((long)(buf)),"d" ((long)(len))); return status; } diff --git a/tests/tcg/i386/system/boot.S b/tests/tcg/i386/system/boot.S new file mode 100644 index 0000000000..90aa174908 --- /dev/null +++ b/tests/tcg/i386/system/boot.S @@ -0,0 +1,172 @@ +/* + * i386 boot code, based on qemu-bmibug. + * + * Copyright 2019 Doug Gale + * Copyright 2019 Linaro + * + * This work is licensed under the terms of the GNU GPL, version 3 or later. + * See the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + + .section .head + + /* Multi-boot header */ +multiboot_st: + .int 0x1BADB002 + .int 0x10000 + .int -(0x10000+0x1BADB002) + // Load address + .int __load_st + .int __load_st + .int __load_en + .int __bss_en + .int _start + // mode + .int 0 + // width + .int 0 + // height + .int 0 + // depth + .int 0 + + .code32 + .section .text + + /* Kernel Entry Point */ +.global _start +_start: + // Setup stack ASAP + mov $stack_end,%esp + + // Load GDT ASAP + lgdt gdtr + ljmp $0x8,$.Lloadcs +.Lloadcs: + mov $0x10,%eax + mov %eax,%ds + mov %eax,%es + mov %eax,%fs + mov %eax,%gs + mov %eax,%ss + + // Fixup the IDT to the ridiculous i386 layout + xor %ebx,%ebx +.Lnextidt: + mov idt_00(,%ebx,8),%eax + shr $16,%eax + movw $0x8,idt_00+2(,%ebx,8) + movw $0x8E00,idt_00+4(,%ebx,8) + movw %ax,idt_00+6(,%ebx,8) + add $1,%ebx + cmp $32,%ebx + jl .Lnextidt + + // Load IDTR + push $idt_00 + push $((32 * 8 - 1) << 16) + lidt 2(%esp) + add $8,%esp + + /* + * Don't worry about stack frame, assume everthing + * is garbage when we return, we won't need it. + */ + call main + + /* output any non-zero result in eax to isa-debug-exit device */ + test %al, %al + jz 1f + out %ax, $0xf4 + +1: /* QEMU ACPI poweroff */ + mov $0x604,%edx + mov $0x2000,%eax + out %ax,%dx + hlt + jmp 1b + + /* + * Helper Functions + */ + + /* Output a single character to serial port */ + .global __sys_outc +__sys_outc: + pushl %ebp + movl %esp, %ebp + out %al,$0xE9 + movl %ebp, %esp + popl %ebp + ret + + + /* Interrupt Descriptor Table */ + + .section .data + .align 16 + +idt_00: .int 0, 0 +idt_01: .int 0, 0 +idt_02: .int 0, 0 +idt_03: .int 0, 0 +idt_04: .int 0, 0 +idt_05: .int 0, 0 +idt_06: .int 0, 0 /* intr_6_opcode, Invalid Opcode */ +idt_07: .int 0, 0 +idt_08: .int 0, 0 +idt_09: .int 0, 0 +idt_0A: .int 0, 0 +idt_0B: .int 0, 0 +idt_0C: .int 0, 0 +idt_0D: .int 0, 0 +idt_0E: .int 0, 0 +idt_0F: .int 0, 0 +idt_10: .int 0, 0 +idt_11: .int 0, 0 +idt_12: .int 0, 0 +idt_13: .int 0, 0 +idt_14: .int 0, 0 +idt_15: .int 0, 0 +idt_16: .int 0, 0 +idt_17: .int 0, 0 +idt_18: .int 0, 0 +idt_19: .int 0, 0 +idt_1A: .int 0, 0 +idt_1B: .int 0, 0 +idt_1C: .int 0, 0 +idt_1D: .int 0, 0 +idt_1E: .int 0, 0 +idt_1F: .int 0, 0 + +gdt: + .short 0 +gdtr: + .short gdt_en - gdt - 1 + .int gdt + + // Code + .short 0xFFFF + .short 0 + .byte 0 + .byte 0x9b + .byte 0xCF + .byte 0 + + // Data + .short 0xFFFF + .short 0 + .byte 0 + .byte 0x93 + .byte 0xCF + .byte 0 + +gdt_en: + + .section .bss + .align 16 + +stack: .space 65536 +stack_end: diff --git a/tests/tcg/i386/system/hello.c b/tests/tcg/i386/system/hello.c new file mode 100644 index 0000000000..821dc0ef09 --- /dev/null +++ b/tests/tcg/i386/system/hello.c @@ -0,0 +1,14 @@ +/* + * Hello World, system test version + * + * We don't have the benefit of libc, just builtin C primitives and + * whatever is in minilib. + */ + +#include + +int main(void) +{ + ml_printf("Hello World\n"); + return 0; +} diff --git a/tests/tcg/i386/system/kernel.ld b/tests/tcg/i386/system/kernel.ld new file mode 100644 index 0000000000..92de525e93 --- /dev/null +++ b/tests/tcg/i386/system/kernel.ld @@ -0,0 +1,23 @@ +SECTIONS { + . = 0x100000; + + .text : { + __load_st = .; + *(.head) + *(.text) + } + + .rodata : { + *(.rodata) + } + + .data : { + *(.data) + __load_en = .; + } + + .bss : { + *(.bss) + __bss_en = .; + } +} diff --git a/tests/tcg/i386/system/memory.c b/tests/tcg/i386/system/memory.c new file mode 100644 index 0000000000..a7a0a8e978 --- /dev/null +++ b/tests/tcg/i386/system/memory.c @@ -0,0 +1,243 @@ +/* + * Memory Test + * + * This is intended to test the softmmu code and ensure we properly + * behave across normal and unaligned accesses across several pages. + * We are not replicating memory tests for stuck bits and other + * hardware level failures but looking for issues with different size + * accesses when: + + * + */ + +#include +#include + +#define TEST_SIZE (4096 * 4) /* 4 pages */ + +static uint8_t test_data[TEST_SIZE]; + +static void pdot(int count) +{ + if (count % 128 == 0) { + ml_printf("."); + } +} + + +/* + * Fill the data with ascending value bytes. As x86 is a LE machine we + * write in ascending order and then read and high byte should either + * be zero or higher than the lower bytes. + */ + +static void init_test_data_u8(void) +{ + uint8_t count = 0, *ptr = &test_data[0]; + int i; + + ml_printf("Filling test area with u8:"); + for (i = 0; i < TEST_SIZE; i++) { + *ptr++ = count++; + pdot(i); + } + ml_printf("done\n"); +} + +static void init_test_data_u16(int offset) +{ + uint8_t count = 0; + uint16_t word, *ptr = (uint16_t *) &test_data[0]; + const int max = (TEST_SIZE - offset) / sizeof(word); + int i; + + ml_printf("Filling test area with u16 (offset %d):", offset); + + /* Leading zeros */ + for (i = 0; i < offset; i++) { + *ptr = 0; + } + + ptr = (uint16_t *) &test_data[offset]; + for (i = 0; i < max; i++) { + uint8_t high, low; + low = count++; + high = count++; + word = (high << 8) | low; + *ptr++ = word; + pdot(i); + } + ml_printf("done\n"); +} + +static void init_test_data_u32(int offset) +{ + uint8_t count = 0; + uint32_t word, *ptr = (uint32_t *) &test_data[0]; + const int max = (TEST_SIZE - offset) / sizeof(word); + int i; + + ml_printf("Filling test area with u32 (offset %d):", offset); + + /* Leading zeros */ + for (i = 0; i < offset; i++) { + *ptr = 0; + } + + ptr = (uint32_t *) &test_data[offset]; + for (i = 0; i < max; i++) { + uint8_t b1, b2, b3, b4; + b4 = count++; + b3 = count++; + b2 = count++; + b1 = count++; + word = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + *ptr++ = word; + pdot(i); + } + ml_printf("done\n"); +} + + +static int read_test_data_u16(int offset) +{ + uint16_t word, *ptr = (uint16_t *)&test_data[offset]; + int i; + const int max = (TEST_SIZE - offset) / sizeof(word); + + ml_printf("Reading u16 from %#lx (offset %d):", ptr, offset); + + for (i = 0; i < max; i++) { + uint8_t high, low; + word = *ptr++; + high = (word >> 8) & 0xff; + low = word & 0xff; + if (high < low && high != 0) { + ml_printf("Error %d < %d\n", high, low); + return 1; + } else { + pdot(i); + } + + } + ml_printf("done\n"); + return 0; +} + +static int read_test_data_u32(int offset) +{ + uint32_t word, *ptr = (uint32_t *)&test_data[offset]; + int i; + const int max = (TEST_SIZE - offset) / sizeof(word); + + ml_printf("Reading u32 from %#lx (offset %d):", ptr, offset); + + for (i = 0; i < max; i++) { + uint8_t b1, b2, b3, b4; + word = *ptr++; + + b1 = word >> 24 & 0xff; + b2 = word >> 16 & 0xff; + b3 = word >> 8 & 0xff; + b4 = word & 0xff; + + if ((b1 < b2 && b1 != 0) || + (b2 < b3 && b2 != 0) || + (b3 < b4 && b3 != 0)) { + ml_printf("Error %d, %d, %d, %d", b1, b2, b3, b4); + return 2; + } else { + pdot(i); + } + } + ml_printf("done\n"); + return 0; +} + +static int read_test_data_u64(int offset) +{ + uint64_t word, *ptr = (uint64_t *)&test_data[offset]; + int i; + const int max = (TEST_SIZE - offset) / sizeof(word); + + ml_printf("Reading u64 from %#lx (offset %d):", ptr, offset); + + for (i = 0; i < max; i++) { + uint8_t b1, b2, b3, b4, b5, b6, b7, b8; + word = *ptr++; + + b1 = ((uint64_t) (word >> 56)) & 0xff; + b2 = ((uint64_t) (word >> 48)) & 0xff; + b3 = ((uint64_t) (word >> 40)) & 0xff; + b4 = (word >> 32) & 0xff; + b5 = (word >> 24) & 0xff; + b6 = (word >> 16) & 0xff; + b7 = (word >> 8) & 0xff; + b8 = (word >> 0) & 0xff; + + if ((b1 < b2 && b1 != 0) || + (b2 < b3 && b2 != 0) || + (b3 < b4 && b3 != 0) || + (b4 < b5 && b4 != 0) || + (b5 < b6 && b5 != 0) || + (b6 < b7 && b6 != 0) || + (b7 < b8 && b7 != 0)) { + ml_printf("Error %d, %d, %d, %d, %d, %d, %d, %d", + b1, b2, b3, b4, b5, b6, b7, b8); + return 2; + } else { + pdot(i); + } + } + ml_printf("done\n"); + return 0; +} + +/* Read the test data and verify at various offsets */ +int do_reads(void) +{ + int r = 0; + int off = 0; + + while (r == 0 && off < 8) { + r = read_test_data_u16(off); + r |= read_test_data_u32(off); + r |= read_test_data_u64(off); + off++; + } + + return r; +} + +int main(void) +{ + int i, r = 0; + + + init_test_data_u8(); + r = do_reads(); + if (r) { + return r; + } + + for (i = 0; i < 8; i++) { + init_test_data_u16(i); + + r = do_reads(); + if (r) { + return r; + } + } + + for (i = 0; i < 8; i++) { + init_test_data_u32(i); + + r = do_reads(); + if (r) { + return r; + } + } + + ml_printf("Test complete: %s\n", r == 0 ? "PASSED" : "FAILED"); + return r; +} diff --git a/tests/tcg/i386/test-i386.c b/tests/tcg/i386/test-i386.c index a29b41e764..18d5609665 100644 --- a/tests/tcg/i386/test-i386.c +++ b/tests/tcg/i386/test-i386.c @@ -1137,7 +1137,7 @@ void test_xchg(void) TEST_XCHG(xchgb, "b", "+q"); #if defined(__x86_64__) - TEST_XCHG(xchgq, "", "=m"); + TEST_XCHG(xchgq, "", "+m"); #endif TEST_XCHG(xchgl, "k", "+m"); TEST_XCHG(xchgw, "w", "+m"); diff --git a/tests/tcg/minilib/Makefile.target b/tests/tcg/minilib/Makefile.target new file mode 100644 index 0000000000..3ed8077d0f --- /dev/null +++ b/tests/tcg/minilib/Makefile.target @@ -0,0 +1,21 @@ +# +# System test minilib objects +# +# The system tests are very constrained in terms of the library they +# support but we are not savages. We provide a few helpful routines +# that can be shared with the tests for basic I/O. +# +# They assume each arch has provided a putc function. +# + +SYSTEM_MINILIB_SRC=$(SRC_PATH)/tests/tcg/minilib +MINILIB_SRCS=$(wildcard $(SYSTEM_MINILIB_SRC)/*.c) +MINILIB_OBJS=$(patsubst $(SYSTEM_MINILIB_SRC)/%.c, %.o, $(MINILIB_SRCS)) + +MINILIB_CFLAGS+=-nostdlib -ggdb -O0 +MINILIB_INC=-isystem $(SYSTEM_MINILIB_SRC) + +.PRECIOUS: $(MINILIB_OBJS) + +%.o: $(SYSTEM_MINILIB_SRC)/%.c + $(CC) $(CFLAGS) -c $< -o $@ diff --git a/tests/tcg/minilib/minilib.h b/tests/tcg/minilib/minilib.h new file mode 100644 index 0000000000..e23361380a --- /dev/null +++ b/tests/tcg/minilib/minilib.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2015 Virtual Open Systems SAS + * Author: Alexander Spyridakis + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#ifndef _MINILIB_H_ +#define _MINILIB_H_ + +/* + * Provided by the individual arch + */ +extern void __sys_outc(char c); + +/* + * Provided by the common minilib + */ +void ml_printf(const char *fmt, ...); + +#endif /* _MINILIB_H_ */ diff --git a/tests/tcg/minilib/printf.c b/tests/tcg/minilib/printf.c new file mode 100644 index 0000000000..121620cb16 --- /dev/null +++ b/tests/tcg/minilib/printf.c @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2015 Virtual Open Systems SAS + * Author: Alexander Spyridakis + * + * printf based on implementation by Kevin Wolf + * + * 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. + * + * SPDX-License-Identifier: GPL-2.0-only + */ + +#include "minilib.h" + +typedef __builtin_va_list va_list; +#define va_start(ap, X) __builtin_va_start(ap, X) +#define va_arg(ap, type) __builtin_va_arg(ap, type) +#define va_end(ap) __builtin_va_end(ap) + +static void print_str(char *s) +{ + while (*s) { + __sys_outc(*s++); + } +} + +static void print_num(unsigned long long value, int base) +{ + char digits[] = "0123456789abcdef"; + char buf[32]; + int i = sizeof(buf) - 2, j; + + /* Set the buffer to 0. See problem of before. */ + for (j = 0; j < 32; j++) { + buf[j] = 0; + } + + do { + buf[i--] = digits[value % base]; + value /= base; + } while (value); + + print_str(&buf[i + 1]); +} + +void ml_printf(const char *fmt, ...) +{ + va_list ap; + char *str; + int base; + int has_long; + int alt_form; + unsigned long long val; + + va_start(ap, fmt); + + for (; *fmt; fmt++) { + if (*fmt != '%') { + __sys_outc(*fmt); + continue; + } + fmt++; + + if (*fmt == '#') { + fmt++; + alt_form = 1; + } else { + alt_form = 0; + } + + if (*fmt == 'l') { + fmt++; + if (*fmt == 'l') { + fmt++; + has_long = 2; + } else { + has_long = 1; + } + } else { + has_long = 0; + } + + switch (*fmt) { + case 'x': + case 'p': + base = 16; + goto convert_number; + case 'd': + case 'i': + case 'u': + base = 10; + goto convert_number; + case 'o': + base = 8; + goto convert_number; + + convert_number: + switch (has_long) { + case 0: + val = va_arg(ap, unsigned int); + break; + case 1: + val = va_arg(ap, unsigned long); + break; + case 2: + val = va_arg(ap, unsigned long long); + break; + } + + if (alt_form && base == 16) { + print_str("0x"); + } + + print_num(val, base); + break; + + case 's': + str = va_arg(ap, char*); + print_str(str); + break; + case '%': + __sys_outc(*fmt); + break; + default: + __sys_outc('%'); + __sys_outc(*fmt); + break; + } + } + + va_end(ap); +} diff --git a/tests/tcg/mips/Makefile.target b/tests/tcg/mips/Makefile.target index 086625f533..1a994d5525 100644 --- a/tests/tcg/mips/Makefile.target +++ b/tests/tcg/mips/Makefile.target @@ -8,15 +8,12 @@ MIPS_SRC=$(SRC_PATH)/tests/tcg/mips # Set search path for all sources VPATH += $(MIPS_SRC) +# hello-mips is 32 bit only +ifeq ($(findstring 64,$(TARGET_NAME)),) MIPS_TESTS=hello-mips TESTS += $(MIPS_TESTS) -hello-mips: CFLAGS+=-ffreestanding +hello-mips: CFLAGS+=-mno-abicalls -fno-PIC -mabi=32 hello-mips: LDFLAGS+=-nostdlib - -# For MIPS32 and 64 we have a bunch of extra tests in sub-directories -# however they are intended for system tests. - -run-hello-mips: hello-mips - $(call skip-test, $<, "BROKEN") +endif diff --git a/tests/tcg/mips/hello-mips.c b/tests/tcg/mips/hello-mips.c index f8256730dd..4e1cf501af 100644 --- a/tests/tcg/mips/hello-mips.c +++ b/tests/tcg/mips/hello-mips.c @@ -24,9 +24,9 @@ static inline void exit1(int status) " syscall \n" " .set pop " : - : "i" (__NR_exit), "r" (__a0) - : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", - "memory"); + : "i" (__NR_exit), "r" (__a0) + : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", + "memory"); } static inline int write(int fd, const char *buf, int len) @@ -46,8 +46,8 @@ static inline int write(int fd, const char *buf, int len) " .set pop " : "=r" (__v0), "=r" (__a3) : "i" (__NR_write), "r" (__a0), "r" (__a1), "r" (__a2) - : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", - "memory"); + : "$2", "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15", "$24", + "memory"); /* if (__a3 == 0) */ return (int) __v0; @@ -60,5 +60,5 @@ static inline int write(int fd, const char *buf, int len) void __start(void) { write (1, "Hello, World!\n", 14); - exit1 (42); + exit1(0); } diff --git a/tests/tcg/mips/include/test_inputs_128.h b/tests/tcg/mips/include/test_inputs_128.h new file mode 100644 index 0000000000..e4c22dde6e --- /dev/null +++ b/tests/tcg/mips/include/test_inputs_128.h @@ -0,0 +1,122 @@ +/* + * Header file for pattern and random test inputs + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 TEST_INPUTS_128_H +#define TEST_INPUTS_128_H + +#include + + +#define PATTERN_INPUTS_COUNT 64 +#define PATTERN_INPUTS_SHORT_COUNT 8 + +static const uint64_t b128_pattern[PATTERN_INPUTS_COUNT][2] = { + { 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xAAAAAAAAAAAAAAAAULL, 0xAAAAAAAAAAAAAAAAULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xCCCCCCCCCCCCCCCCULL, 0xCCCCCCCCCCCCCCCCULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xE38E38E38E38E38EULL, 0x38E38E38E38E38E3ULL, }, + { 0x1C71C71C71C71C71ULL, 0xC71C71C71C71C71CULL, }, + { 0xF0F0F0F0F0F0F0F0ULL, 0xF0F0F0F0F0F0F0F0ULL, }, /* 8 */ + { 0x0F0F0F0F0F0F0F0FULL, 0x0F0F0F0F0F0F0F0FULL, }, + { 0xF83E0F83E0F83E0FULL, 0x83E0F83E0F83E0F8ULL, }, + { 0x07C1F07C1F07C1F0ULL, 0x7C1F07C1F07C1F07ULL, }, + { 0xFC0FC0FC0FC0FC0FULL, 0xC0FC0FC0FC0FC0FCULL, }, + { 0x03F03F03F03F03F0ULL, 0x3F03F03F03F03F03ULL, }, + { 0xFE03F80FE03F80FEULL, 0x03F80FE03F80FE03ULL, }, + { 0x01FC07F01FC07F01ULL, 0xFC07F01FC07F01FCULL, }, + { 0xFF00FF00FF00FF00ULL, 0xFF00FF00FF00FF00ULL, }, /* 16 */ + { 0x00FF00FF00FF00FFULL, 0x00FF00FF00FF00FFULL, }, + { 0xFF803FE00FF803FEULL, 0x00FF803FE00FF803ULL, }, + { 0x007FC01FF007FC01ULL, 0xFF007FC01FF007FCULL, }, + { 0xFFC00FFC00FFC00FULL, 0xFC00FFC00FFC00FFULL, }, + { 0x003FF003FF003FF0ULL, 0x03FF003FF003FF00ULL, }, + { 0xFFE003FF800FFE00ULL, 0x3FF800FFE003FF80ULL, }, + { 0x001FFC007FF001FFULL, 0xC007FF001FFC007FULL, }, + { 0xFFF000FFF000FFF0ULL, 0x00FFF000FFF000FFULL, }, /* 24 */ + { 0x000FFF000FFF000FULL, 0xFF000FFF000FFF00ULL, }, + { 0xFFF8003FFE000FFFULL, 0x8003FFE000FFF800ULL, }, + { 0x0007FFC001FFF000ULL, 0x7FFC001FFF0007FFULL, }, + { 0xFFFC000FFFC000FFULL, 0xFC000FFFC000FFFCULL, }, + { 0x0003FFF0003FFF00ULL, 0x03FFF0003FFF0003ULL, }, + { 0xFFFE0003FFF8000FULL, 0xFFE0003FFF8000FFULL, }, + { 0x0001FFFC0007FFF0ULL, 0x001FFFC0007FFF00ULL, }, + { 0xFFFF0000FFFF0000ULL, 0xFFFF0000FFFF0000ULL, }, /* 32 */ + { 0x0000FFFF0000FFFFULL, 0x0000FFFF0000FFFFULL, }, + { 0xFFFF80003FFFE000ULL, 0x0FFFF80003FFFE00ULL, }, + { 0x00007FFFC0001FFFULL, 0xF00007FFFC0001FFULL, }, + { 0xFFFFC0000FFFFC00ULL, 0x00FFFFC0000FFFFCULL, }, + { 0x00003FFFF00003FFULL, 0xFF00003FFFF00003ULL, }, + { 0xFFFFE00003FFFF80ULL, 0x000FFFFE00003FFFULL, }, + { 0x00001FFFFC00007FULL, 0xFFF00001FFFFC000ULL, }, + { 0xFFFFF00000FFFFF0ULL, 0x0000FFFFF00000FFULL, }, /* 40 */ + { 0x00000FFFFF00000FULL, 0xFFFF00000FFFFF00ULL, }, + { 0xFFFFF800003FFFFEULL, 0x00000FFFFF800003ULL, }, + { 0x000007FFFFC00001ULL, 0xFFFFF000007FFFFCULL, }, + { 0xFFFFFC00000FFFFFULL, 0xC00000FFFFFC0000ULL, }, + { 0x000003FFFFF00000ULL, 0x3FFFFF000003FFFFULL, }, + { 0xFFFFFE000003FFFFULL, 0xF800000FFFFFE000ULL, }, + { 0x000001FFFFFC0000ULL, 0x07FFFFF000001FFFULL, }, + { 0xFFFFFF000000FFFFULL, 0xFF000000FFFFFF00ULL, }, /* 48 */ + { 0x000000FFFFFF0000ULL, 0x00FFFFFF000000FFULL, }, + { 0xFFFFFF8000003FFFULL, 0xFFE000000FFFFFF8ULL, }, + { 0x0000007FFFFFC000ULL, 0x001FFFFFF0000007ULL, }, + { 0xFFFFFFC000000FFFULL, 0xFFFC000000FFFFFFULL, }, + { 0x0000003FFFFFF000ULL, 0x0003FFFFFF000000ULL, }, + { 0xFFFFFFE0000003FFULL, 0xFFFF8000000FFFFFULL, }, + { 0x0000001FFFFFFC00ULL, 0x00007FFFFFF00000ULL, }, + { 0xFFFFFFF0000000FFULL, 0xFFFFF0000000FFFFULL, }, /* 56 */ + { 0x0000000FFFFFFF00ULL, 0x00000FFFFFFF0000ULL, }, + { 0xFFFFFFF80000003FULL, 0xFFFFFE0000000FFFULL, }, + { 0x00000007FFFFFFC0ULL, 0x000001FFFFFFF000ULL, }, + { 0xFFFFFFFC0000000FULL, 0xFFFFFFC0000000FFULL, }, + { 0x00000003FFFFFFF0ULL, 0x0000003FFFFFFF00ULL, }, + { 0xFFFFFFFE00000003ULL, 0xFFFFFFF80000000FULL, }, + { 0x00000001FFFFFFFCULL, 0x00000007FFFFFFF0ULL, }, +}; + + +#define RANDOM_INPUTS_COUNT 16 +#define RANDOM_INPUTS_SHORT_COUNT 4 + +static const uint64_t b128_random[RANDOM_INPUTS_COUNT][2] = { + { 0x886AE6CC28625540ULL, 0x4B670B5EFE7BB00CULL, }, /* 0 */ + { 0xFBBE00634D93C708ULL, 0x12F7BB1A153F52FCULL, }, + { 0xAC5AAEAAB9CF8B80ULL, 0x27D8C6FFAB2B2514ULL, }, + { 0x704F164D5E31E24EULL, 0x8DF188D8A942E2A0ULL, }, + { 0xB9926B7C7DAF4258ULL, 0xA1227CADDCCE65B6ULL, }, + { 0xD027BE89FF0A2EF9ULL, 0x170B5050FEA53078ULL, }, + { 0xB83B580665CABC4AULL, 0x91230822BFF0BA62ULL, }, + { 0xFC8F23F09AA6B782ULL, 0x93FD6637124275AEULL, }, + { 0x201E09CD56AEE649ULL, 0xEF5DE039A6A52758ULL, }, /* 8 */ + { 0xA57CD91365D9E5D7ULL, 0x9321BC9881ECBA5CULL, }, + { 0xA2E8F6F5C9CBC61BULL, 0xB2C471545E0D7A12ULL, }, + { 0xA89CF2F131A864AEULL, 0xD2A3E87A5DB986E7ULL, }, + { 0xE61438E9A652EA0AULL, 0xA85483D97879D41CULL, }, + { 0x944A35FD192361A8ULL, 0xF3912DA36A0B2D6BULL, }, + { 0x4630426322BEF79CULL, 0xEB5686F7CB19304EULL, }, + { 0x8B5AA7A2F259DEADULL, 0xD278CBCD696417E3ULL, }, +}; + + +#endif diff --git a/tests/tcg/mips/include/test_inputs_32.h b/tests/tcg/mips/include/test_inputs_32.h new file mode 100644 index 0000000000..a3b7e5464a --- /dev/null +++ b/tests/tcg/mips/include/test_inputs_32.h @@ -0,0 +1,122 @@ +/* + * Header file for pattern and random test inputs + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 TEST_INPUTS_32_H +#define TEST_INPUTS_32_H + +#include + + +#define PATTERN_INPUTS_32_COUNT 64 +#define PATTERN_INPUTS_32_SHORT_COUNT 8 + +static const uint32_t b32_pattern[PATTERN_INPUTS_32_COUNT] = { + 0xFFFFFFFF, /* 0 */ + 0x00000000, + 0xAAAAAAAA, + 0x55555555, + 0xCCCCCCCC, + 0x33333333, + 0xE38E38E3, + 0x1C71C71C, + 0xF0F0F0F0, /* 8 */ + 0x0F0F0F0F, + 0xF83E0F83, + 0x07C1F07C, + 0xFC0FC0FC, + 0x03F03F03, + 0xFE03F80F, + 0x01FC07F0, + 0xFF00FF00, /* 16 */ + 0x00FF00FF, + 0xFF803FE0, + 0x007FC01F, + 0xFFC00FFC, + 0x003FF003, + 0xFFE003FF, + 0x001FFC00, + 0xFFF000FF, /* 24 */ + 0x000FFF00, + 0xFFF8003F, + 0x0007FFC0, + 0xFFFC000F, + 0x0003FFF0, + 0xFFFE0003, + 0x0001FFFC, + 0xFFFF0000, /* 32 */ + 0x0000FFFF, + 0xFFFF8000, + 0x00007FFF, + 0xFFFFC000, + 0x00003FFF, + 0xFFFFE000, + 0x00001FFF, + 0xFFFFF000, /* 40 */ + 0x00000FFF, + 0xFFFFF800, + 0x000007FF, + 0xFFFFFC00, + 0x000003FF, + 0xFFFFFE00, + 0x000001FF, + 0xFFFFFF00, /* 48 */ + 0x000000FF, + 0xFFFFFF80, + 0x0000007F, + 0xFFFFFFC0, + 0x0000003F, + 0xFFFFFFE0, + 0x0000001F, + 0xFFFFFFF0, /* 56 */ + 0x0000000F, + 0xFFFFFFF8, + 0x00000007, + 0xFFFFFFFC, + 0x00000003, + 0xFFFFFFFE, + 0x00000001, +}; + + +#define RANDOM_INPUTS_32_COUNT 16 +#define RANDOM_INPUTS_32_SHORT_COUNT 4 + +static const uint32_t b32_random[RANDOM_INPUTS_32_COUNT] = { + 0x886AE6CC, /* 0 */ + 0xFBBE0063, + 0xAC5AAEAA, + 0x704F164D, + 0xB9926B7C, + 0xD027BE89, + 0xB83B5806, + 0xFC8F23F0, + 0x201E09CD, /* 8 */ + 0xA57CD913, + 0xA2E8F6F5, + 0xA89CF2F1, + 0xE61438E9, + 0x944A35FD, + 0x46304263, + 0x8B5AA7A2, +}; + + +#endif diff --git a/tests/tcg/mips/include/test_inputs_64.h b/tests/tcg/mips/include/test_inputs_64.h new file mode 100644 index 0000000000..6891a362a3 --- /dev/null +++ b/tests/tcg/mips/include/test_inputs_64.h @@ -0,0 +1,208 @@ +/* + * Header file for pattern and random test inputs + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 TEST_INPUTS_64_H +#define TEST_INPUTS_64_H + +#include + + +#define PATTERN_INPUTS_64_COUNT 64 +#define PATTERN_INPUTS_64_SHORT_COUNT 8 + +static const uint64_t b64_pattern[PATTERN_INPUTS_64_COUNT] = { + 0xFFFFFFFFFFFFFFFFULL, /* 0 */ + 0x0000000000000000ULL, + 0xAAAAAAAAAAAAAAAAULL, + 0x5555555555555555ULL, + 0xCCCCCCCCCCCCCCCCULL, + 0x3333333333333333ULL, + 0xE38E38E38E38E38EULL, + 0x1C71C71C71C71C71ULL, + 0xF0F0F0F0F0F0F0F0ULL, /* 8 */ + 0x0F0F0F0F0F0F0F0FULL, + 0xF83E0F83E0F83E0FULL, + 0x07C1F07C1F07C1F0ULL, + 0xFC0FC0FC0FC0FC0FULL, + 0x03F03F03F03F03F0ULL, + 0xFE03F80FE03F80FEULL, + 0x01FC07F01FC07F01ULL, + 0xFF00FF00FF00FF00ULL, /* 16 */ + 0x00FF00FF00FF00FFULL, + 0xFF803FE00FF803FEULL, + 0x007FC01FF007FC01ULL, + 0xFFC00FFC00FFC00FULL, + 0x003FF003FF003FF0ULL, + 0xFFE003FF800FFE00ULL, + 0x001FFC007FF001FFULL, + 0xFFF000FFF000FFF0ULL, /* 24 */ + 0x000FFF000FFF000FULL, + 0xFFF8003FFE000FFFULL, + 0x0007FFC001FFF000ULL, + 0xFFFC000FFFC000FFULL, + 0x0003FFF0003FFF00ULL, + 0xFFFE0003FFF8000FULL, + 0x0001FFFC0007FFF0ULL, + 0xFFFF0000FFFF0000ULL, /* 32 */ + 0x0000FFFF0000FFFFULL, + 0xFFFF80003FFFE000ULL, + 0x00007FFFC0001FFFULL, + 0xFFFFC0000FFFFC00ULL, + 0x00003FFFF00003FFULL, + 0xFFFFE00003FFFF80ULL, + 0x00001FFFFC00007FULL, + 0xFFFFF00000FFFFF0ULL, /* 40 */ + 0x00000FFFFF00000FULL, + 0xFFFFF800003FFFFEULL, + 0x000007FFFFC00001ULL, + 0xFFFFFC00000FFFFFULL, + 0x000003FFFFF00000ULL, + 0xFFFFFE000003FFFFULL, + 0x000001FFFFFC0000ULL, + 0xFFFFFF000000FFFFULL, /* 48 */ + 0x000000FFFFFF0000ULL, + 0xFFFFFF8000003FFFULL, + 0x0000007FFFFFC000ULL, + 0xFFFFFFC000000FFFULL, + 0x0000003FFFFFF000ULL, + 0xFFFFFFE0000003FFULL, + 0x0000001FFFFFFC00ULL, + 0xFFFFFFF0000000FFULL, /* 56 */ + 0x0000000FFFFFFF00ULL, + 0xFFFFFFF80000003FULL, + 0x00000007FFFFFFC0ULL, + 0xFFFFFFFC0000000FULL, + 0x00000003FFFFFFF0ULL, + 0xFFFFFFFE00000003ULL, + 0x00000001FFFFFFFCULL, +}; + +static const uint64_t b64_pattern_se[PATTERN_INPUTS_64_COUNT] = { + 0xFFFFFFFFFFFFFFFFULL, /* 0 */ + 0x0000000000000000ULL, + 0xFFFFFFFFAAAAAAAAULL, + 0x0000000055555555ULL, + 0xFFFFFFFFCCCCCCCCULL, + 0x0000000033333333ULL, + 0xFFFFFFFFE38E38E3ULL, + 0x000000001C71C71CULL, + 0xFFFFFFFFF0F0F0F0ULL, /* 8 */ + 0x000000000F0F0F0FULL, + 0xFFFFFFFFF83E0F83ULL, + 0x0000000007C1F07CULL, + 0xFFFFFFFFFC0FC0FCULL, + 0x0000000003F03F03ULL, + 0xFFFFFFFFFE03F80FULL, + 0x0000000001FC07F0ULL, + 0xFFFFFFFFFF00FF00ULL, /* 16 */ + 0x0000000000FF00FFULL, + 0xFFFFFFFFFF803FE0ULL, + 0x00000000007FC01FULL, + 0xFFFFFFFFFFC00FFCULL, + 0x00000000003FF003ULL, + 0xFFFFFFFFFFE003FFULL, + 0x00000000001FFC00ULL, + 0xFFFFFFFFFFF000FFULL, /* 24 */ + 0x00000000000FFF00ULL, + 0xFFFFFFFFFFF8003FULL, + 0x000000000007FFC0ULL, + 0xFFFFFFFFFFFC000FULL, + 0x000000000003FFF0ULL, + 0xFFFFFFFFFFFE0003ULL, + 0x000000000001FFFCULL, + 0xFFFFFFFFFFFF0000ULL, /* 32 */ + 0x000000000000FFFFULL, + 0xFFFFFFFFFFFF8000ULL, + 0x0000000000007FFFULL, + 0xFFFFFFFFFFFFC000ULL, + 0x0000000000003FFFULL, + 0xFFFFFFFFFFFFE000ULL, + 0x0000000000001FFFULL, + 0xFFFFFFFFFFFFF000ULL, /* 40 */ + 0x0000000000000FFFULL, + 0xFFFFFFFFFFFFF800ULL, + 0x00000000000007FFULL, + 0xFFFFFFFFFFFFFC00ULL, + 0x00000000000003FFULL, + 0xFFFFFFFFFFFFFE00ULL, + 0x00000000000001FFULL, + 0xFFFFFFFFFFFFFF00ULL, /* 48 */ + 0x00000000000000FFULL, + 0xFFFFFFFFFFFFFF80ULL, + 0x000000000000007FULL, + 0xFFFFFFFFFFFFFFC0ULL, + 0x000000000000003FULL, + 0xFFFFFFFFFFFFFFE0ULL, + 0x000000000000001FULL, + 0xFFFFFFFFFFFFFFF0ULL, /* 56 */ + 0x000000000000000FULL, + 0xFFFFFFFFFFFFFFF8ULL, + 0x0000000000000007ULL, + 0xFFFFFFFFFFFFFFFCULL, + 0x0000000000000003ULL, + 0xFFFFFFFFFFFFFFFEULL, + 0x0000000000000001ULL, +}; + + +#define RANDOM_INPUTS_64_COUNT 16 +#define RANDOM_INPUTS_64_SHORT_COUNT 4 + +static const uint64_t b64_random[RANDOM_INPUTS_64_COUNT] = { + 0x886AE6CC28625540ULL, /* 0 */ + 0xFBBE00634D93C708ULL, + 0xAC5AAEAAB9CF8B80ULL, + 0x704F164D5E31E24EULL, + 0xB9926B7C7DAF4258ULL, + 0xD027BE89FF0A2EF9ULL, + 0xB83B580665CABC4AULL, + 0xFC8F23F09AA6B782ULL, + 0x201E09CD56AEE649ULL, /* 8 */ + 0xA57CD91365D9E5D7ULL, + 0xA2E8F6F5C9CBC61BULL, + 0xA89CF2F131A864AEULL, + 0xE61438E9A652EA0AULL, + 0x944A35FD192361A8ULL, + 0x4630426322BEF79CULL, + 0x8B5AA7A2F259DEADULL, +}; + +static const uint64_t b64_random_se[RANDOM_INPUTS_64_COUNT] = { + 0xFFFFFFFF886AE6CCULL, /* 0 */ + 0xFFFFFFFFFBBE0063ULL, + 0xFFFFFFFFAC5AAEAAULL, + 0x00000000704F164DULL, + 0xFFFFFFFFB9926B7CULL, + 0xFFFFFFFFD027BE89ULL, + 0xFFFFFFFFB83B5806ULL, + 0xFFFFFFFFFC8F23F0ULL, + 0x00000000201E09CDULL, /* 8 */ + 0xFFFFFFFFA57CD913ULL, + 0xFFFFFFFFA2E8F6F5ULL, + 0xFFFFFFFFA89CF2F1ULL, + 0xFFFFFFFFE61438E9ULL, + 0xFFFFFFFF944A35FDULL, + 0x0000000046304263ULL, + 0xFFFFFFFF8B5AA7A2ULL, +}; + + +#endif diff --git a/tests/tcg/mips/include/test_utils_128.h b/tests/tcg/mips/include/test_utils_128.h new file mode 100644 index 0000000000..cfd7ad3188 --- /dev/null +++ b/tests/tcg/mips/include/test_utils_128.h @@ -0,0 +1,79 @@ +/* + * Header file for test utilities + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 TEST_UTILS_128_H +#define TEST_UTILS_128_H + +#include +#include +#include +#include + +#define PRINT_RESULTS 0 + + +static inline int32_t check_results(const char *instruction_name, + const uint32_t test_count, + const double elapsed_time, + const uint64_t *b128_result, + const uint64_t *b128_expect) +{ +#if PRINT_RESULTS + uint32_t ii; + printf("\n"); + for (ii = 0; ii < test_count; ii++) { + uint64_t a, b; + memcpy(&a, (b128_result + 2 * ii), 8); + memcpy(&b, (b128_result + 2 * ii + 1), 8); + if (ii % 8 != 0) { + printf(" { 0x%016llxULL, 0x%016llxULL, },\n", a, b); + } else { + printf(" { 0x%016llxULL, 0x%016llxULL, }, /* %3d */\n", + a, b, ii); + } + } + printf("\n"); +#endif + uint32_t i; + uint32_t pass_count = 0; + uint32_t fail_count = 0; + + printf("%s: ", instruction_name); + for (i = 0; i < test_count; i++) { + if ((b128_result[2 * i] == b128_expect[2 * i]) && + (b128_result[2 * i + 1] == b128_expect[2 * i + 1])) { + pass_count++; + } else { + fail_count++; + } + } + + printf("PASS: %3d FAIL: %3d elapsed time: %5.2f ms\n", + pass_count, fail_count, elapsed_time); + + if (fail_count > 0) { + return -1; + } else { + return 0; + } +} + +#endif diff --git a/tests/tcg/mips/include/test_utils_32.h b/tests/tcg/mips/include/test_utils_32.h new file mode 100644 index 0000000000..c33990c0c5 --- /dev/null +++ b/tests/tcg/mips/include/test_utils_32.h @@ -0,0 +1,78 @@ +/* + * Header file for test utilities + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 TEST_UTILS_32_H +#define TEST_UTILS_32_H + +#include +#include +#include +#include + +#define PRINT_RESULTS 0 + + +static inline int32_t check_results_32(const char *instruction_name, + const uint32_t test_count, + const double elapsed_time, + const uint32_t *b32_result, + const uint32_t *b32_expect) +{ +#if PRINT_RESULTS + uint32_t ii; + printf("\n"); + for (ii = 0; ii < test_count; ii++) { + uint64_t a; + memcpy(&a, (b32_result + ii), 8); + if (ii % 8 != 0) { + printf(" 0x%08lxULL,\n", a); + } else { + printf(" 0x%08lxULL, /* %3d */\n", + a, ii); + } + } + printf("\n"); +#endif + uint32_t i; + uint32_t pass_count = 0; + uint32_t fail_count = 0; + + printf("%s: ", instruction_name); + for (i = 0; i < test_count; i++) { + if (b32_result[i] == b32_expect[i]) { + pass_count++; + } else { + fail_count++; + } + } + + printf("PASS: %3d FAIL: %3d elapsed time: %5.2f ms\n", + pass_count, fail_count, elapsed_time); + + if (fail_count > 0) { + return -1; + } else { + return 0; + } +} + + +#endif diff --git a/tests/tcg/mips/include/test_utils_64.h b/tests/tcg/mips/include/test_utils_64.h new file mode 100644 index 0000000000..ee3828472c --- /dev/null +++ b/tests/tcg/mips/include/test_utils_64.h @@ -0,0 +1,78 @@ +/* + * Header file for test utilities + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 TEST_UTILS_64_H +#define TEST_UTILS_64_H + +#include +#include +#include +#include + +#define PRINT_RESULTS 0 + + +static inline int32_t check_results_64(const char *instruction_name, + const uint32_t test_count, + const double elapsed_time, + const uint64_t *b64_result, + const uint64_t *b64_expect) +{ +#if PRINT_RESULTS + uint32_t ii; + printf("\n"); + for (ii = 0; ii < test_count; ii++) { + uint64_t a; + memcpy(&a, (b64_result + ii), 8); + if (ii % 8 != 0) { + printf(" 0x%016llxULL,\n", a); + } else { + printf(" 0x%016llxULL, /* %3d */\n", + a, ii); + } + } + printf("\n"); +#endif + uint32_t i; + uint32_t pass_count = 0; + uint32_t fail_count = 0; + + printf("%s: ", instruction_name); + for (i = 0; i < test_count; i++) { + if (b64_result[i] == b64_expect[i]) { + pass_count++; + } else { + fail_count++; + } + } + + printf("PASS: %3d FAIL: %3d elapsed time: %5.2f ms\n", + pass_count, fail_count, elapsed_time); + + if (fail_count > 0) { + return -1; + } else { + return 0; + } +} + + +#endif diff --git a/tests/tcg/mips/include/wrappers_mips64r6.h b/tests/tcg/mips/include/wrappers_mips64r6.h new file mode 100644 index 0000000000..d1e5edb632 --- /dev/null +++ b/tests/tcg/mips/include/wrappers_mips64r6.h @@ -0,0 +1,83 @@ +/* + * Header file for wrappers around MIPS64R6 instructions assembler + * invocations + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 WRAPPERS_MIPS64R6_H +#define WRAPPERS_MIPS64R6_H + + +#define DO_MIPS64R6__RD__RS(suffix, mnemonic) \ +static inline void do_mips64r6_##suffix(const void *input, \ + void *output) \ +{ \ + __asm__ volatile ( \ + "ld $t1, 0(%0)\n\t" \ + #mnemonic " $t0, $t1\n\t" \ + "sd $t0, 0(%1)\n\t" \ + : \ + : "r" (input), "r" (output) \ + : "t0", "t1", "memory" \ + ); \ +} + +DO_MIPS64R6__RD__RS(CLO, clo) +DO_MIPS64R6__RD__RS(CLZ, clz) +DO_MIPS64R6__RD__RS(DCLO, dclo) +DO_MIPS64R6__RD__RS(DCLZ, dclz) + +DO_MIPS64R6__RD__RS(BITSWAP, bitswap) +DO_MIPS64R6__RD__RS(DBITSWAP, dbitswap) + + +#define DO_MIPS64R6__RD__RS_RT(suffix, mnemonic) \ +static inline void do_mips64r6_##suffix(const void *input1, \ + const void *input2, \ + void *output) \ +{ \ + __asm__ volatile ( \ + "ld $t1, 0(%0)\n\t" \ + "ld $t2, 0(%1)\n\t" \ + #mnemonic " $t0, $t1, $t2\n\t" \ + "sd $t0, 0(%2)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "t1", "memory" \ + ); \ +} + +DO_MIPS64R6__RD__RS_RT(SLLV, sllv) +DO_MIPS64R6__RD__RS_RT(SRLV, srlv) +DO_MIPS64R6__RD__RS_RT(SRAV, srav) +DO_MIPS64R6__RD__RS_RT(DSLLV, dsllv) +DO_MIPS64R6__RD__RS_RT(DSRLV, dsrlv) +DO_MIPS64R6__RD__RS_RT(DSRAV, dsrav) + +DO_MIPS64R6__RD__RS_RT(MUL, mul) +DO_MIPS64R6__RD__RS_RT(MUH, muh) +DO_MIPS64R6__RD__RS_RT(MULU, mulu) +DO_MIPS64R6__RD__RS_RT(MUHU, muhu) +DO_MIPS64R6__RD__RS_RT(DMUL, dmul) +DO_MIPS64R6__RD__RS_RT(DMUH, dmuh) +DO_MIPS64R6__RD__RS_RT(DMULU, dmulu) +DO_MIPS64R6__RD__RS_RT(DMUHU, dmuhu) + + +#endif diff --git a/tests/tcg/mips/include/wrappers_msa.h b/tests/tcg/mips/include/wrappers_msa.h new file mode 100644 index 0000000000..254e215b8a --- /dev/null +++ b/tests/tcg/mips/include/wrappers_msa.h @@ -0,0 +1,277 @@ +/* + * Header file for wrappers around MSA instructions assembler invocations + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 WRAPPERS_MSA_H +#define WRAPPERS_MSA_H + + +#define DO_MSA__WD__WS(suffix, mnemonic) \ +static inline void do_msa_##suffix(void *input, void *output) \ +{ \ + __asm__ volatile ( \ + "move $t0, %0\n\t" \ + "ld.d $w11, 0($t0)\n\t" \ + #mnemonic " $w10, $w11\n\t" \ + "move $t0, %1\n\t" \ + "st.d $w10, 0($t0)\n\t" \ + : \ + : "r" (input), "r" (output) \ + : "t0", "memory" \ + ); \ +} + +#define DO_MSA__WD__WD(suffix, mnemonic) \ +static inline void do_msa_##suffix(void *input, void *output) \ +{ \ + __asm__ volatile ( \ + "move $t0, %0\n\t" \ + "ld.d $w11, 0($t0)\n\t" \ + #mnemonic " $w10, $w10\n\t" \ + "move $t0, %1\n\t" \ + "st.d $w10, 0($t0)\n\t" \ + : \ + : "r" (input), "r" (output) \ + : "t0", "memory" \ + ); \ +} + +DO_MSA__WD__WS(NLOC_B, nloc.b) +DO_MSA__WD__WS(NLOC_H, nloc.h) +DO_MSA__WD__WS(NLOC_W, nloc.w) +DO_MSA__WD__WS(NLOC_D, nloc.d) + +DO_MSA__WD__WS(NLZC_B, nlzc.b) +DO_MSA__WD__WS(NLZC_H, nlzc.h) +DO_MSA__WD__WS(NLZC_W, nlzc.w) +DO_MSA__WD__WS(NLZC_D, nlzc.d) + +DO_MSA__WD__WS(PCNT_B, pcnt.b) +DO_MSA__WD__WS(PCNT_H, pcnt.h) +DO_MSA__WD__WS(PCNT_W, pcnt.w) +DO_MSA__WD__WS(PCNT_D, pcnt.d) + + +#define DO_MSA__WD__WS_WT(suffix, mnemonic) \ +static inline void do_msa_##suffix(void *input1, void *input2, \ + void *output) \ +{ \ + __asm__ volatile ( \ + "move $t0, %0\n\t" \ + "ld.d $w11, 0($t0)\n\t" \ + "move $t0, %1\n\t" \ + "ld.d $w12, 0($t0)\n\t" \ + #mnemonic " $w10, $w11, $w12\n\t" \ + "move $t0, %2\n\t" \ + "st.d $w10, 0($t0)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "memory" \ + ); \ +} + +#define DO_MSA__WD__WD_WT(suffix, mnemonic) \ +static inline void do_msa_##suffix(void *input1, void *input2, \ + void *output) \ +{ \ + __asm__ volatile ( \ + "move $t0, %0\n\t" \ + "ld.d $w11, 0($t0)\n\t" \ + "move $t0, %1\n\t" \ + "ld.d $w12, 0($t0)\n\t" \ + #mnemonic " $w10, $w10, $w12\n\t" \ + "move $t0, %2\n\t" \ + "st.d $w10, 0($t0)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "memory" \ + ); \ +} + +#define DO_MSA__WD__WS_WD(suffix, mnemonic) \ +static inline void do_msa_##suffix(void *input1, void *input2, \ + void *output) \ +{ \ + __asm__ volatile ( \ + "move $t0, %0\n\t" \ + "ld.d $w11, 0($t0)\n\t" \ + "move $t0, %1\n\t" \ + "ld.d $w12, 0($t0)\n\t" \ + #mnemonic " $w10, $w11, $w10\n\t" \ + "move $t0, %2\n\t" \ + "st.d $w10, 0($t0)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "memory" \ + ); \ +} + +DO_MSA__WD__WS_WT(ILVEV_B, ilvev.b) +DO_MSA__WD__WS_WT(ILVEV_H, ilvev.h) +DO_MSA__WD__WS_WT(ILVEV_W, ilvev.w) +DO_MSA__WD__WS_WT(ILVEV_D, ilvev.d) + +DO_MSA__WD__WS_WT(ILVOD_B, ilvod.b) +DO_MSA__WD__WS_WT(ILVOD_H, ilvod.h) +DO_MSA__WD__WS_WT(ILVOD_W, ilvod.w) +DO_MSA__WD__WS_WT(ILVOD_D, ilvod.d) + +DO_MSA__WD__WS_WT(ILVL_B, ilvl.b) +DO_MSA__WD__WS_WT(ILVL_H, ilvl.h) +DO_MSA__WD__WS_WT(ILVL_W, ilvl.w) +DO_MSA__WD__WS_WT(ILVL_D, ilvl.d) + +DO_MSA__WD__WS_WT(ILVR_B, ilvr.b) +DO_MSA__WD__WS_WT(ILVR_H, ilvr.h) +DO_MSA__WD__WS_WT(ILVR_W, ilvr.w) +DO_MSA__WD__WS_WT(ILVR_D, ilvr.d) + +DO_MSA__WD__WS_WT(AND_V, and.v) +DO_MSA__WD__WS_WT(NOR_V, nor.v) +DO_MSA__WD__WS_WT(OR_V, or.v) +DO_MSA__WD__WS_WT(XOR_V, xor.v) + +DO_MSA__WD__WS_WT(CEQ_B, ceq.b) +DO_MSA__WD__WS_WT(CEQ_H, ceq.h) +DO_MSA__WD__WS_WT(CEQ_W, ceq.w) +DO_MSA__WD__WS_WT(CEQ_D, ceq.d) + +DO_MSA__WD__WS_WT(CLE_S_B, cle_s.b) +DO_MSA__WD__WS_WT(CLE_S_H, cle_s.h) +DO_MSA__WD__WS_WT(CLE_S_W, cle_s.w) +DO_MSA__WD__WS_WT(CLE_S_D, cle_s.d) + +DO_MSA__WD__WS_WT(CLE_U_B, cle_u.b) +DO_MSA__WD__WS_WT(CLE_U_H, cle_u.h) +DO_MSA__WD__WS_WT(CLE_U_W, cle_u.w) +DO_MSA__WD__WS_WT(CLE_U_D, cle_u.d) + +DO_MSA__WD__WS_WT(CLT_S_B, clt_s.b) +DO_MSA__WD__WS_WT(CLT_S_H, clt_s.h) +DO_MSA__WD__WS_WT(CLT_S_W, clt_s.w) +DO_MSA__WD__WS_WT(CLT_S_D, clt_s.d) + +DO_MSA__WD__WS_WT(CLT_U_B, clt_u.b) +DO_MSA__WD__WS_WT(CLT_U_H, clt_u.h) +DO_MSA__WD__WS_WT(CLT_U_W, clt_u.w) +DO_MSA__WD__WS_WT(CLT_U_D, clt_u.d) + +DO_MSA__WD__WS_WT(MAX_A_B, max_a.b) +DO_MSA__WD__WS_WT(MAX_A_H, max_a.h) +DO_MSA__WD__WS_WT(MAX_A_W, max_a.w) +DO_MSA__WD__WS_WT(MAX_A_D, max_a.d) + +DO_MSA__WD__WS_WT(MIN_A_B, min_a.b) +DO_MSA__WD__WS_WT(MIN_A_H, min_a.h) +DO_MSA__WD__WS_WT(MIN_A_W, min_a.w) +DO_MSA__WD__WS_WT(MIN_A_D, min_a.d) + +DO_MSA__WD__WS_WT(MAX_S_B, max_s.b) +DO_MSA__WD__WS_WT(MAX_S_H, max_s.h) +DO_MSA__WD__WS_WT(MAX_S_W, max_s.w) +DO_MSA__WD__WS_WT(MAX_S_D, max_s.d) + +DO_MSA__WD__WS_WT(MIN_S_B, min_s.b) +DO_MSA__WD__WS_WT(MIN_S_H, min_s.h) +DO_MSA__WD__WS_WT(MIN_S_W, min_s.w) +DO_MSA__WD__WS_WT(MIN_S_D, min_s.d) + +DO_MSA__WD__WS_WT(MAX_U_B, max_u.b) +DO_MSA__WD__WS_WT(MAX_U_H, max_u.h) +DO_MSA__WD__WS_WT(MAX_U_W, max_u.w) +DO_MSA__WD__WS_WT(MAX_U_D, max_u.d) + +DO_MSA__WD__WS_WT(MIN_U_B, min_u.b) +DO_MSA__WD__WS_WT(MIN_U_H, min_u.h) +DO_MSA__WD__WS_WT(MIN_U_W, min_u.w) +DO_MSA__WD__WS_WT(MIN_U_D, min_u.d) + +DO_MSA__WD__WS_WT(BCLR_B, bclr.b) +DO_MSA__WD__WS_WT(BCLR_H, bclr.h) +DO_MSA__WD__WS_WT(BCLR_W, bclr.w) +DO_MSA__WD__WS_WT(BCLR_D, bclr.d) + +DO_MSA__WD__WS_WT(BSET_B, bset.b) +DO_MSA__WD__WS_WT(BSET_H, bset.h) +DO_MSA__WD__WS_WT(BSET_W, bset.w) +DO_MSA__WD__WS_WT(BSET_D, bset.d) + +DO_MSA__WD__WS_WT(BNEG_B, bneg.b) +DO_MSA__WD__WS_WT(BNEG_H, bneg.h) +DO_MSA__WD__WS_WT(BNEG_W, bneg.w) +DO_MSA__WD__WS_WT(BNEG_D, bneg.d) + +DO_MSA__WD__WS_WT(PCKEV_B, pckev.b) +DO_MSA__WD__WS_WT(PCKEV_H, pckev.h) +DO_MSA__WD__WS_WT(PCKEV_W, pckev.w) +DO_MSA__WD__WS_WT(PCKEV_D, pckev.d) + +DO_MSA__WD__WS_WT(PCKOD_B, pckod.b) +DO_MSA__WD__WS_WT(PCKOD_H, pckod.h) +DO_MSA__WD__WS_WT(PCKOD_W, pckod.w) +DO_MSA__WD__WS_WT(PCKOD_D, pckod.d) + +DO_MSA__WD__WS_WT(VSHF_B, vshf.b) +DO_MSA__WD__WS_WT(VSHF_H, vshf.h) +DO_MSA__WD__WS_WT(VSHF_W, vshf.w) +DO_MSA__WD__WS_WT(VSHF_D, vshf.d) + +DO_MSA__WD__WS_WT(SLL_B, sll.b) +DO_MSA__WD__WS_WT(SLL_H, sll.h) +DO_MSA__WD__WS_WT(SLL_W, sll.w) +DO_MSA__WD__WS_WT(SLL_D, sll.d) + +DO_MSA__WD__WS_WT(SRA_B, sra.b) +DO_MSA__WD__WS_WT(SRA_H, sra.h) +DO_MSA__WD__WS_WT(SRA_W, sra.w) +DO_MSA__WD__WS_WT(SRA_D, sra.d) + +DO_MSA__WD__WS_WT(SRAR_B, srar.b) +DO_MSA__WD__WS_WT(SRAR_H, srar.h) +DO_MSA__WD__WS_WT(SRAR_W, srar.w) +DO_MSA__WD__WS_WT(SRAR_D, srar.d) + +DO_MSA__WD__WS_WT(SRL_B, srl.b) +DO_MSA__WD__WS_WT(SRL_H, srl.h) +DO_MSA__WD__WS_WT(SRL_W, srl.w) +DO_MSA__WD__WS_WT(SRL_D, srl.d) + +DO_MSA__WD__WS_WT(SRLR_B, srlr.b) +DO_MSA__WD__WS_WT(SRLR_H, srlr.h) +DO_MSA__WD__WS_WT(SRLR_W, srlr.w) +DO_MSA__WD__WS_WT(SRLR_D, srlr.d) + +DO_MSA__WD__WS_WT(BMNZ_V, bmnz.v) +DO_MSA__WD__WS_WT(BMZ_V, bmz.v) + +DO_MSA__WD__WS_WT(FMAX_W, fmax.w) +DO_MSA__WD__WS_WT(FMAX_D, fmax.d) + +DO_MSA__WD__WS_WT(FMAX_A_W, fmax_a.w) +DO_MSA__WD__WS_WT(FMAX_A_D, fmax_a.d) + +DO_MSA__WD__WS_WT(FMIN_W, fmin.w) +DO_MSA__WD__WS_WT(FMIN_D, fmin.d) + +DO_MSA__WD__WS_WT(FMIN_A_W, fmin_a.w) +DO_MSA__WD__WS_WT(FMIN_A_D, fmin_a.d) + + +#endif diff --git a/tests/tcg/mips/mips32-dsp/Makefile b/tests/tcg/mips/mips32-dsp/Makefile deleted file mode 100644 index c3a0a00944..0000000000 --- a/tests/tcg/mips/mips32-dsp/Makefile +++ /dev/null @@ -1,136 +0,0 @@ --include ../../config-host.mak - -CROSS=mips64el-unknown-linux-gnu- - -SIM=qemu-mipsel -SIM_FLAGS=-cpu 74Kf - -CC = $(CROSS)gcc -CFLAGS = -mabi=32 -march=mips32r2 -mgp32 -mdsp -static - -TESTCASES = absq_s_ph.tst -TESTCASES += absq_s_w.tst -TESTCASES += addq_ph.tst -TESTCASES += addq_s_ph.tst -TESTCASES += addq_s_w.tst -TESTCASES += addsc.tst -TESTCASES += addu_qb.tst -TESTCASES += addu_s_qb.tst -TESTCASES += addwc.tst -TESTCASES += bitrev.tst -TESTCASES += bposge32.tst -TESTCASES += cmp_eq_ph.tst -TESTCASES += cmpgu_eq_qb.tst -TESTCASES += cmpgu_le_qb.tst -TESTCASES += cmpgu_lt_qb.tst -TESTCASES += cmp_le_ph.tst -TESTCASES += cmp_lt_ph.tst -TESTCASES += cmpu_eq_qb.tst -TESTCASES += cmpu_le_qb.tst -TESTCASES += cmpu_lt_qb.tst -TESTCASES += dpaq_sa_l_w.tst -TESTCASES += dpaq_s_w_ph.tst -TESTCASES += dpau_h_qbl.tst -TESTCASES += dpau_h_qbr.tst -TESTCASES += dpsq_sa_l_w.tst -TESTCASES += dpsq_s_w_ph.tst -TESTCASES += dpsu_h_qbl.tst -TESTCASES += dpsu_h_qbr.tst -TESTCASES += extp.tst -TESTCASES += extpdp.tst -TESTCASES += extpdpv.tst -TESTCASES += extpv.tst -TESTCASES += extr_rs_w.tst -TESTCASES += extr_r_w.tst -TESTCASES += extr_s_h.tst -TESTCASES += extrv_rs_w.tst -TESTCASES += extrv_r_w.tst -TESTCASES += extrv_s_h.tst -TESTCASES += extrv_w.tst -TESTCASES += extr_w.tst -TESTCASES += insv.tst -TESTCASES += lbux.tst -TESTCASES += lhx.tst -TESTCASES += lwx.tst -TESTCASES += madd.tst -TESTCASES += maddu.tst -TESTCASES += maq_sa_w_phl.tst -TESTCASES += maq_sa_w_phr.tst -TESTCASES += maq_s_w_phl.tst -TESTCASES += maq_s_w_phr.tst -TESTCASES += mfhi.tst -TESTCASES += mflo.tst -TESTCASES += modsub.tst -TESTCASES += msub.tst -TESTCASES += msubu.tst -TESTCASES += mthi.tst -TESTCASES += mthlip.tst -TESTCASES += mtlo.tst -TESTCASES += muleq_s_w_phl.tst -TESTCASES += muleq_s_w_phr.tst -TESTCASES += muleu_s_ph_qbl.tst -TESTCASES += muleu_s_ph_qbr.tst -TESTCASES += mulq_rs_ph.tst -TESTCASES += mult.tst -TESTCASES += multu.tst -TESTCASES += packrl_ph.tst -TESTCASES += pick_ph.tst -TESTCASES += pick_qb.tst -TESTCASES += precequ_ph_qbla.tst -TESTCASES += precequ_ph_qbl.tst -TESTCASES += precequ_ph_qbra.tst -TESTCASES += precequ_ph_qbr.tst -TESTCASES += preceq_w_phl.tst -TESTCASES += preceq_w_phr.tst -TESTCASES += preceu_ph_qbla.tst -TESTCASES += preceu_ph_qbl.tst -TESTCASES += preceu_ph_qbra.tst -TESTCASES += preceu_ph_qbr.tst -TESTCASES += precrq_ph_w.tst -TESTCASES += precrq_qb_ph.tst -TESTCASES += precrq_rs_ph_w.tst -TESTCASES += precrqu_s_qb_ph.tst -TESTCASES += raddu_w_qb.tst -TESTCASES += rddsp.tst -TESTCASES += repl_ph.tst -TESTCASES += repl_qb.tst -TESTCASES += replv_ph.tst -TESTCASES += replv_qb.tst -TESTCASES += shilo.tst -TESTCASES += shilov.tst -TESTCASES += shll_ph.tst -TESTCASES += shll_qb.tst -TESTCASES += shll_s_ph.tst -TESTCASES += shll_s_w.tst -TESTCASES += shllv_ph.tst -TESTCASES += shllv_qb.tst -TESTCASES += shllv_s_ph.tst -TESTCASES += shllv_s_w.tst -TESTCASES += shra_ph.tst -TESTCASES += shra_r_ph.tst -TESTCASES += shra_r_w.tst -TESTCASES += shrav_ph.tst -TESTCASES += shrav_r_ph.tst -TESTCASES += shrav_r_w.tst -TESTCASES += shrl_qb.tst -TESTCASES += shrlv_qb.tst -TESTCASES += subq_ph.tst -TESTCASES += subq_s_ph.tst -TESTCASES += subq_s_w.tst -TESTCASES += subu_qb.tst -TESTCASES += subu_s_qb.tst -TESTCASES += wrdsp.tst - -all: $(TESTCASES) - -%.tst: %.c - $(CC) $(CFLAGS) $< -o $@ - -check: $(TESTCASES) - @for case in $(TESTCASES); do \ - echo $(SIM) $(SIM_FLAGS) ./$$case;\ - $(SIM) $(SIM_FLAGS) ./$$case; \ - done - -clean: - $(RM) -rf $(TESTCASES) diff --git a/tests/tcg/mips/mips32-dspr2/Makefile b/tests/tcg/mips/mips32-dspr2/Makefile deleted file mode 100644 index ed19581c7e..0000000000 --- a/tests/tcg/mips/mips32-dspr2/Makefile +++ /dev/null @@ -1,71 +0,0 @@ --include ../../config-host.mak - -CROSS=mips64el-unknown-linux-gnu- - -SIM=qemu-mipsel -SIM_FLAGS=-cpu 74Kf - -CC = $(CROSS)gcc -CFLAGS = -mabi=32 -march=mips32r2 -mgp32 -mdspr2 -static - -TESTCASES = absq_s_qb.tst -TESTCASES += addqh_ph.tst -TESTCASES += addqh_r_ph.tst -TESTCASES += addqh_r_w.tst -TESTCASES += addqh_w.tst -TESTCASES += adduh_qb.tst -TESTCASES += adduh_r_qb.tst -TESTCASES += addu_ph.tst -TESTCASES += addu_s_ph.tst -TESTCASES += append.tst -TESTCASES += balign.tst -TESTCASES += cmpgdu_eq_qb.tst -TESTCASES += cmpgdu_le_qb.tst -TESTCASES += cmpgdu_lt_qb.tst -TESTCASES += dpaqx_sa_w_ph.tst -TESTCASES += dpa_w_ph.tst -TESTCASES += dpax_w_ph.tst -TESTCASES += dpaqx_s_w_ph.tst -TESTCASES += dpsqx_sa_w_ph.tst -TESTCASES += dpsqx_s_w_ph.tst -TESTCASES += dps_w_ph.tst -TESTCASES += dpsx_w_ph.tst -TESTCASES += mul_ph.tst -TESTCASES += mulq_rs_w.tst -TESTCASES += mulq_s_ph.tst -TESTCASES += mulq_s_w.tst -TESTCASES += mulsaq_s_w_ph.tst -TESTCASES += mulsa_w_ph.tst -TESTCASES += mul_s_ph.tst -TESTCASES += precr_qb_ph.tst -TESTCASES += precr_sra_ph_w.tst -TESTCASES += precr_sra_r_ph_w.tst -TESTCASES += prepend.tst -TESTCASES += shra_qb.tst -TESTCASES += shra_r_qb.tst -TESTCASES += shrav_qb.tst -TESTCASES += shrav_r_qb.tst -TESTCASES += shrl_ph.tst -TESTCASES += shrlv_ph.tst -TESTCASES += subqh_ph.tst -TESTCASES += subqh_r_ph.tst -TESTCASES += subqh_r_w.tst -TESTCASES += subqh_w.tst -TESTCASES += subuh_qb.tst -TESTCASES += subuh_r_qb.tst -TESTCASES += subu_ph.tst -TESTCASES += subu_s_ph.tst - -all: $(TESTCASES) - -%.tst: %.c - $(CC) $(CFLAGS) $< -o $@ - -check: $(TESTCASES) - @for case in $(TESTCASES); do \ - echo $(SIM) $(SIM_FLAGS) ./$$case;\ - $(SIM) $(SIM_FLAGS) ./$$case; \ - done - -clean: - $(RM) -rf $(TESTCASES) diff --git a/tests/tcg/mips/mips64-dsp/Makefile b/tests/tcg/mips/mips64-dsp/Makefile deleted file mode 100644 index b2ac6b3ffd..0000000000 --- a/tests/tcg/mips/mips64-dsp/Makefile +++ /dev/null @@ -1,306 +0,0 @@ - -CROSS_COMPILE ?= mips64el-unknown-linux-gnu- - -SIM = qemu-system-mips64el -SIMFLAGS = -nographic -cpu mips64dspr2 -kernel - -AS = $(CROSS_COMPILE)as -LD = $(CROSS_COMPILE)ld -CC = $(CROSS_COMPILE)gcc -AR = $(CROSS_COMPILE)ar -NM = $(CROSS_COMPILE)nm -STRIP = $(CROSS_COMPILE)strip -RANLIB = $(CROSS_COMPILE)ranlib -OBJCOPY = $(CROSS_COMPILE)objcopy -OBJDUMP = $(CROSS_COMPILE)objdump - -VECTORS_OBJ ?= ./head.o ./printf.o - -HEAD_FLAGS ?= -nostdinc -mabi=64 -G 0 -mno-abicalls -fno-pic -pipe \ - -msoft-float -march=mips64 -Wa,-mips64 -Wa,--trap \ - -msym32 -DKBUILD_64BIT_SYM32 -I./ - -CFLAGS ?= -nostdinc -mabi=64 -G 0 -mno-abicalls -fno-pic -fno-builtin \ - -pipe -march=mips64r2 -mgp64 -mdsp -static -Wa,--trap -msym32 \ - -DKBUILD_64BIT_SYM32 -I./ - -LDFLAGS = -T./mips_boot.lds -L./ -FLAGS = -nostdlib -mabi=64 -march=mips64r2 -mgp64 -mdsp - - -#TESTCASES = absq_s_ob.tst -TESTCASES = absq_s_ph.tst -TESTCASES += absq_s_pw.tst -TESTCASES += absq_s_qh.tst -TESTCASES += absq_s_w.tst -TESTCASES += addq_ph.tst -TESTCASES += addq_pw.tst -TESTCASES += addq_qh.tst -TESTCASES += addq_s_ph.tst -TESTCASES += addq_s_pw.tst -TESTCASES += addq_s_qh.tst -TESTCASES += addq_s_w.tst -TESTCASES += addsc.tst -TESTCASES += addu_ob.tst -TESTCASES += addu_qb.tst -TESTCASES += addu_s_ob.tst -TESTCASES += addu_s_qb.tst -TESTCASES += addwc.tst -TESTCASES += bitrev.tst -TESTCASES += bposge32.tst -TESTCASES += bposge64.tst -TESTCASES += cmp_eq_ph.tst -TESTCASES += cmp_eq_pw.tst -TESTCASES += cmp_eq_qh.tst -TESTCASES += cmpgu_eq_ob.tst -TESTCASES += cmpgu_eq_qb.tst -TESTCASES += cmpgu_le_ob.tst -TESTCASES += cmpgu_le_qb.tst -TESTCASES += cmpgu_lt_ob.tst -TESTCASES += cmpgu_lt_qb.tst -TESTCASES += cmp_le_ph.tst -TESTCASES += cmp_le_pw.tst -TESTCASES += cmp_le_qh.tst -TESTCASES += cmp_lt_ph.tst -TESTCASES += cmp_lt_pw.tst -TESTCASES += cmp_lt_qh.tst -TESTCASES += cmpu_eq_ob.tst -TESTCASES += cmpu_eq_qb.tst -TESTCASES += cmpu_le_ob.tst -TESTCASES += cmpu_le_qb.tst -TESTCASES += cmpu_lt_ob.tst -TESTCASES += cmpu_lt_qb.tst -#TESTCASES += dappend.tst -TESTCASES += dextp.tst -TESTCASES += dextpdp.tst -TESTCASES += dextpdpv.tst -TESTCASES += dextpv.tst -TESTCASES += dextr_l.tst -TESTCASES += dextr_r_l.tst -TESTCASES += dextr_rs_l.tst -TESTCASES += dextr_rs_w.tst -TESTCASES += dextr_r_w.tst -TESTCASES += dextr_s_h.tst -TESTCASES += dextrv_l.tst -TESTCASES += dextrv_r_l.tst -TESTCASES += dextrv_rs_l.tst -TESTCASES += dextrv_rs_w.tst -TESTCASES += dextrv_r_w.tst -TESTCASES += dextrv_s_h.tst -TESTCASES += dextrv_w.tst -TESTCASES += dextr_w.tst -TESTCASES += dinsv.tst -TESTCASES += dmadd.tst -TESTCASES += dmaddu.tst -TESTCASES += dmsub.tst -TESTCASES += dmsubu.tst -TESTCASES += dmthlip.tst -TESTCASES += dpaq_sa_l_pw.tst -TESTCASES += dpaq_sa_l_w.tst -TESTCASES += dpaq_s_w_ph.tst -TESTCASES += dpaq_s_w_qh.tst -TESTCASES += dpau_h_obl.tst -TESTCASES += dpau_h_obr.tst -TESTCASES += dpau_h_qbl.tst -TESTCASES += dpau_h_qbr.tst -TESTCASES += dpsq_sa_l_pw.tst -TESTCASES += dpsq_sa_l_w.tst -TESTCASES += dpsq_s_w_ph.tst -TESTCASES += dpsq_s_w_qh.tst -TESTCASES += dpsu_h_obl.tst -TESTCASES += dpsu_h_obr.tst -TESTCASES += dpsu_h_qbl.tst -TESTCASES += dpsu_h_qbr.tst -TESTCASES += dshilo.tst -TESTCASES += dshilov.tst -TESTCASES += extp.tst -TESTCASES += extpdp.tst -TESTCASES += extpdpv.tst -TESTCASES += extpv.tst -TESTCASES += extr_rs_w.tst -TESTCASES += extr_r_w.tst -TESTCASES += extr_s_h.tst -TESTCASES += extrv_rs_w.tst -TESTCASES += extrv_r_w.tst -TESTCASES += extrv_s_h.tst -TESTCASES += extrv_w.tst -TESTCASES += extr_w.tst -TESTCASES += insv.tst -TESTCASES += lbux.tst -TESTCASES += lhx.tst -TESTCASES += lwx.tst -TESTCASES += ldx.tst -TESTCASES += madd.tst -TESTCASES += maddu.tst -TESTCASES += maq_sa_w_phl.tst -TESTCASES += maq_sa_w_phr.tst -TESTCASES += maq_sa_w_qhll.tst -TESTCASES += maq_sa_w_qhlr.tst -TESTCASES += maq_sa_w_qhrl.tst -TESTCASES += maq_sa_w_qhrr.tst -TESTCASES += maq_s_l_pwl.tst -TESTCASES += maq_s_l_pwr.tst -TESTCASES += maq_s_w_phl.tst -TESTCASES += maq_s_w_phr.tst -TESTCASES += maq_s_w_qhll.tst -TESTCASES += maq_s_w_qhlr.tst -TESTCASES += maq_s_w_qhrl.tst -TESTCASES += maq_s_w_qhrr.tst -TESTCASES += mfhi.tst -TESTCASES += mflo.tst -TESTCASES += modsub.tst -TESTCASES += msub.tst -TESTCASES += msubu.tst -TESTCASES += mthi.tst -TESTCASES += mthlip.tst -TESTCASES += mtlo.tst -TESTCASES += muleq_s_pw_qhl.tst -TESTCASES += muleq_s_pw_qhr.tst -TESTCASES += muleq_s_w_phl.tst -TESTCASES += muleq_s_w_phr.tst -TESTCASES += muleu_s_ph_qbl.tst -TESTCASES += muleu_s_ph_qbr.tst -TESTCASES += muleu_s_qh_obl.tst -TESTCASES += muleu_s_qh_obr.tst -TESTCASES += mulq_rs_ph.tst -TESTCASES += mulq_rs_qh.tst -TESTCASES += mulsaq_s_l_pw.tst -TESTCASES += mulsaq_s_w_qh.tst -TESTCASES += mult.tst -TESTCASES += multu.tst -TESTCASES += packrl_ph.tst -TESTCASES += packrl_pw.tst -TESTCASES += pick_ob.tst -TESTCASES += pick_ph.tst -TESTCASES += pick_pw.tst -TESTCASES += pick_qb.tst -TESTCASES += pick_qh.tst -#TESTCASES += preceq_l_pwl.tst -#TESTCASES += preceq_l_pwr.tst -TESTCASES += preceq_pw_qhla.tst -TESTCASES += preceq_pw_qhl.tst -TESTCASES += preceq_pw_qhra.tst -TESTCASES += preceq_pw_qhr.tst -TESTCASES += precequ_ph_qbla.tst -TESTCASES += precequ_ph_qbl.tst -TESTCASES += precequ_ph_qbra.tst -TESTCASES += precequ_ph_qbr.tst -#TESTCASES += precequ_qh_obla.tst -#TESTCASES += precequ_qh_obl.tst -#TESTCASES += precequ_qh_obra.tst -#TESTCASES += precequ_qh_obr.tst -TESTCASES += preceq_w_phl.tst -TESTCASES += preceq_w_phr.tst -TESTCASES += preceu_ph_qbla.tst -TESTCASES += preceu_ph_qbl.tst -TESTCASES += preceu_ph_qbra.tst -TESTCASES += preceu_ph_qbr.tst -TESTCASES += preceu_qh_obla.tst -TESTCASES += preceu_qh_obl.tst -TESTCASES += preceu_qh_obra.tst -TESTCASES += preceu_qh_obr.tst -#TESTCASES += precr_ob_qh.tst -TESTCASES += precrq_ob_qh.tst -TESTCASES += precrq_ph_w.tst -TESTCASES += precrq_pw_l.tst -TESTCASES += precrq_qb_ph.tst -TESTCASES += precrq_qh_pw.tst -TESTCASES += precrq_rs_ph_w.tst -TESTCASES += precrq_rs_qh_pw.tst -TESTCASES += precrqu_s_ob_qh.tst -TESTCASES += precrqu_s_qb_ph.tst -#TESTCASES += precr_sra_qh_pw.tst -#TESTCASES += precr_sra_r_qh_pw.tst -#TESTCASES += prependd.tst -#TESTCASES += prependw.tst -#TESTCASES += raddu_l_ob.tst -TESTCASES += raddu_w_qb.tst -TESTCASES += rddsp.tst -TESTCASES += repl_ob.tst -TESTCASES += repl_ph.tst -TESTCASES += repl_pw.tst -TESTCASES += repl_qb.tst -TESTCASES += repl_qh.tst -TESTCASES += replv_ob.tst -TESTCASES += replv_ph.tst -TESTCASES += replv_pw.tst -TESTCASES += replv_qb.tst -TESTCASES += shilo.tst -TESTCASES += shilov.tst -TESTCASES += shll_ob.tst -TESTCASES += shll_ph.tst -TESTCASES += shll_pw.tst -TESTCASES += shll_qb.tst -TESTCASES += shll_qh.tst -TESTCASES += shll_s_ph.tst -TESTCASES += shll_s_pw.tst -TESTCASES += shll_s_qh.tst -TESTCASES += shll_s_w.tst -TESTCASES += shllv_ob.tst -TESTCASES += shllv_ph.tst -TESTCASES += shllv_pw.tst -TESTCASES += shllv_qb.tst -TESTCASES += shllv_qh.tst -TESTCASES += shllv_s_ph.tst -TESTCASES += shllv_s_pw.tst -TESTCASES += shllv_s_qh.tst -TESTCASES += shllv_s_w.tst -#TESTCASES += shra_ob.tst -TESTCASES += shra_ph.tst -TESTCASES += shra_pw.tst -TESTCASES += shra_qh.tst -#TESTCASES += shra_r_ob.tst -TESTCASES += shra_r_ph.tst -TESTCASES += shra_r_pw.tst -TESTCASES += shra_r_qh.tst -TESTCASES += shra_r_w.tst -TESTCASES += shrav_ph.tst -TESTCASES += shrav_pw.tst -TESTCASES += shrav_qh.tst -TESTCASES += shrav_r_ph.tst -TESTCASES += shrav_r_pw.tst -TESTCASES += shrav_r_qh.tst -TESTCASES += shrav_r_w.tst -TESTCASES += shrl_ob.tst -TESTCASES += shrl_qb.tst -#TESTCASES += shrl_qh.tst -TESTCASES += shrlv_ob.tst -TESTCASES += shrlv_qb.tst -#TESTCASES += shrlv_qh.tst -TESTCASES += subq_ph.tst -TESTCASES += subq_pw.tst -TESTCASES += subq_qh.tst -TESTCASES += subq_s_ph.tst -TESTCASES += subq_s_pw.tst -TESTCASES += subq_s_qh.tst -TESTCASES += subq_s_w.tst -TESTCASES += subu_ob.tst -TESTCASES += subu_qb.tst -TESTCASES += subu_s_ob.tst -TESTCASES += subu_s_qb.tst -TESTCASES += wrdsp.tst - -all: build - -head.o : head.S - $(Q)$(CC) $(HEAD_FLAGS) -D"STACK_TOP=0xffffffff80200000" -c $< -o $@ - -%.o : %.S - $(CC) $(CFLAGS) -c $< -o $@ - -%.o : %.c - $(CC) $(CFLAGS) -c $< -o $@ - -%.tst: %.o $(VECTORS_OBJ) - $(CC) $(VECTORS_OBJ) $(FLAGS) $(LDFLAGS) $< -o $@ - -build: $(VECTORS_OBJ) $(MIPSSOC_LIB) $(TESTCASES) - -check: $(VECTORS_OBJ) $(MIPSSOC_LIB) $(TESTCASES) - @for case in $(TESTCASES); do \ - echo $(SIM) $(SIMFLAGS) ./$$case; \ - $(SIM) $(SIMFLAGS) ./$$case & (sleep 1; killall $(SIM)); \ - done - -clean: - $(Q)rm -f *.o *.tst *.a diff --git a/tests/tcg/mips/mips64-dsp/absq_s_ob.c b/tests/tcg/mips/mips64-dsp/absq_s_ob.c deleted file mode 100644 index 6214031578..0000000000 --- a/tests/tcg/mips/mips64-dsp/absq_s_ob.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result, dspcontrol; - rt = 0x7F7F7F7F7F7F7F7F; - result = 0x7F7F7F7F7F7F7F7F; - - - __asm - (".set mips64\n\t" - "absq_s.ob %0 %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("absq_s.ob test 1 error\n"); - - return -1; - } - - __asm - ("rddsp %0\n\t" - : "=r"(rd) - ); - rd >> 20; - rd = rd & 0x1; - if (rd != 0) { - printf("absq_s.ob test 1 dspcontrol overflow flag error\n"); - - return -1; - } - - rt = 0x80FFFFFFFFFFFFFF; - result = 0x7F01010101010101; - - __asm - ("absq_s.ob %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("absq_s.ob test 2 error\n"); - - return -1; - } - - __asm - ("rddsp %0\n\t" - : "=r"(rd) - ); - rd = rd >> 20; - rd = rd & 0x1; - if (rd != 1) { - printf("absq_s.ob test 2 dspcontrol overflow flag error\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/absq_s_ph.c b/tests/tcg/mips/mips64-dsp/absq_s_ph.c deleted file mode 100644 index 238416d438..0000000000 --- a/tests/tcg/mips/mips64-dsp/absq_s_ph.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x10017EFD; - result = 0x10017EFD; - - __asm - ("absq_s.ph %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("absq_s.ph wrong\n"); - - return -1; - } - - rt = 0x8000A536; - result = 0x7FFF5ACA; - - __asm - ("absq_s.ph %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("absq_s.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/absq_s_pw.c b/tests/tcg/mips/mips64-dsp/absq_s_pw.c deleted file mode 100644 index 48fc763b4f..0000000000 --- a/tests/tcg/mips/mips64-dsp/absq_s_pw.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result, dspcontrol; - rd = 0; - rt = 0x7F7F7F7F7F7F7F7F; - result = 0x7F7F7F7F7F7F7F7F; - - - __asm - ("absq_s.pw %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("absq_s.pw test 1 error\n"); - - return -1; - } - - rd = 0; - __asm - ("rddsp %0\n\t" - : "=r"(rd) - ); - rd >> 20; - rd = rd & 0x1; - if (rd != 0) { - printf("absq_s.pw test 1 dspcontrol overflow flag error\n"); - - return -1; - } - - rd = 0; - rt = 0x80000000FFFFFFFF; - result = 0x7FFFFFFF00000001; - - __asm - ("absq_s.pw %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("absq_s.pw test 2 error\n"); - - return -1; - } - - rd = 0; - __asm - ("rddsp %0\n\t" - : "=r"(rd) - ); - rd = rd >> 20; - rd = rd & 0x1; - if (rd != 1) { - printf("absq_s.pw test 2 dspcontrol overflow flag error\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/absq_s_qh.c b/tests/tcg/mips/mips64-dsp/absq_s_qh.c deleted file mode 100644 index 9001a9e164..0000000000 --- a/tests/tcg/mips/mips64-dsp/absq_s_qh.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result, dspcontrol; - rd = 0; - rt = 0x7F7F7F7F7F7F7F7F; - result = 0x7F7F7F7F7F7F7F7F; - - - __asm - ("absq_s.qh %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("absq_s.qh test 1 error\n"); - - return -1; - } - - rd = 0; - rt = 0x8000FFFFFFFFFFFF; - result = 0x7FFF000100000001; - - __asm - ("absq_s.pw %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("absq_s.rw test 2 error\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/absq_s_w.c b/tests/tcg/mips/mips64-dsp/absq_s_w.c deleted file mode 100644 index 414c8bd3f6..0000000000 --- a/tests/tcg/mips/mips64-dsp/absq_s_w.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x80000000; - result = 0x7FFFFFFF; - __asm - ("absq_s.w %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("absq_s_w.ph wrong\n"); - - return -1; - } - - rt = 0x80030000; - result = 0x7FFD0000; - __asm - ("absq_s.w %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("absq_s_w.ph wrong\n"); - - return -1; - } - - rt = 0x31036080; - result = 0x31036080; - __asm - ("absq_s.w %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("absq_s_w.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_ph.c b/tests/tcg/mips/mips64-dsp/addq_ph.c deleted file mode 100644 index 22a36d9805..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_ph.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0xFFFFFFFF; - rt = 0x10101010; - result = 0x100F100F; - __asm - ("addq.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("1 addq.ph wrong\n"); - - return -1; - } - - rs = 0x3712847D; - rt = 0x0031AF2D; - result = 0x374333AA; - __asm - ("addq.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("2 addq.ph wrong\n"); - - return -1; - } - - rs = 0x7fff847D; - rt = 0x0031AF2D; - result = 0xffffffff803033AA; - __asm - ("addq.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - __asm("rddsp %0\n\t" - : "=r"(dsp) - ); - - if (rd != result || (((dsp >> 20) & 0x01) != 1)) { - printf("3 addq.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_pw.c b/tests/tcg/mips/mips64-dsp/addq_pw.c deleted file mode 100644 index 99a7668c0c..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_pw.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - - rs = 0x123456787FFFFFFF; - rt = 0x1111111100000101; - result = 0x2345678980000100; - dspresult = 0x1; - - __asm - ("addq.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addq.pw error\n"); - - return -1; - } - - rs = 0x1234567880FFFFFF; - rt = 0x1111111180000001; - result = 0x2345678901000000; - dspresult = 0x1; - - __asm - ("addq.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addq.pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_qh.c b/tests/tcg/mips/mips64-dsp/addq_qh.c deleted file mode 100644 index 4b874afb8a..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_qh.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - - rs = 0x123456787FFF8010; - rt = 0x1111111100018000; - result = 0x2345678980000010; - dspresult = 0x1; - - __asm - ("addq.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - - if ((rd != result) || (dspreg != dspresult)) { - printf("addq.qh error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_s_ph.c b/tests/tcg/mips/mips64-dsp/addq_s_ph.c deleted file mode 100644 index ad84cdcfe0..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_s_ph.c +++ /dev/null @@ -1,84 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0xFFFFFFFF; - rt = 0x10101010; - result = 0x100F100F; - __asm - ("addq_s.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("1 addq_s.ph wrong\n"); - - return -1; - } - - rs = 0x3712847D; - rt = 0x0031AF2D; - result = 0x37438000; - __asm - ("addq_s.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - __asm - ("rddsp %0\n\t" - : "=r"(dsp) - ); - - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("2 addq_s.ph wrong\n"); - - return -1; - } - - rs = 0x7fff847D; - rt = 0x0031AF2D; - result = 0x7fff8000; - __asm - ("addq_s.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - __asm - ("rddsp %0\n\t" - : "=r"(dsp) - ); - - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("3 addq_s.ph wrong\n"); - - return -1; - } - - rs = 0x8030847D; - rt = 0x8a00AF2D; - result = 0xffffffff80008000; - __asm - ("addq_s.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - __asm - ("rddsp %0\n\t" - : "=r"(dsp) - ); - - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("4 addq_s.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_s_pw.c b/tests/tcg/mips/mips64-dsp/addq_s_pw.c deleted file mode 100644 index 2e380bbfc5..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_s_pw.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rs = 0x123456787FFFFFFF; - rt = 0x1111111100000001; - result = 0x234567897FFFFFFF; - dspresult = 0x1; - - __asm - ("addq_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addq_s.pw error\n"); - - return -1; - } - - rs = 0x80FFFFFFE00000FF; - rt = 0x80000001200000DD; - result = 0x80000000000001DC; - dspresult = 0x01; - - __asm - ("addq_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addq_s.pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_s_qh.c b/tests/tcg/mips/mips64-dsp/addq_s_qh.c deleted file mode 100644 index b638a2b93a..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_s_qh.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rs = 0x123456787FFF8000; - rt = 0x1111111100028000; - result = 0x234567897FFF8000; - dspresult = 0x1; - - __asm - ("addq_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addq_s.qh error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addq_s_w.c b/tests/tcg/mips/mips64-dsp/addq_s_w.c deleted file mode 100644 index 3e08f5d482..0000000000 --- a/tests/tcg/mips/mips64-dsp/addq_s_w.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "io.h" - -int main() -{ - long long rd, rs, rt; - long long result; - - rt = 0x10017EFD; - rs = 0x11111111; - result = 0x2112900e; - - __asm - ("addq_s.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("addq_s.w error\n"); - } - - rt = 0x80017EFD; - rs = 0x81111111; - result = 0xffffffff80000000; - - __asm - ("addq_s.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("addq_s.w error\n"); - } - - rt = 0x7fffffff; - rs = 0x01111111; - result = 0x7fffffff; - - __asm - ("addq_s.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("addq_s.w error\n"); - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addsc.c b/tests/tcg/mips/mips64-dsp/addsc.c deleted file mode 100644 index 4b684b9b99..0000000000 --- a/tests/tcg/mips/mips64-dsp/addsc.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x0000000F; - rt = 0x00000001; - result = 0x00000010; - __asm - ("addsc %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("1 addsc wrong\n"); - - return -1; - } - - rs = 0xFFFF0FFF; - rt = 0x00010111; - result = 0x00001110; - __asm - ("addsc %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 13) & 0x01) != 1)) { - printf("2 addsc wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addu_ob.c b/tests/tcg/mips/mips64-dsp/addu_ob.c deleted file mode 100644 index 17f9c668c0..0000000000 --- a/tests/tcg/mips/mips64-dsp/addu_ob.c +++ /dev/null @@ -1,28 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x3456123498DEF390; - result = 0x468A68AC329AD180; - dspresult = 0x01; - - __asm - ("addu.ob %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - - if ((rd != result) || (dspreg != dspresult)) { - printf("addu.ob error\n\t"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addu_qb.c b/tests/tcg/mips/mips64-dsp/addu_qb.c deleted file mode 100644 index 3b9b5fc5bb..0000000000 --- a/tests/tcg/mips/mips64-dsp/addu_qb.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x00FF00FF; - rt = 0x00010001; - result = 0x00000000; - __asm - ("addu.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("1 addu.qb wrong\n"); - - return -1; - } - - rs = 0xFFFF1111; - rt = 0x00020001; - result = 0xFFFFFFFFFF011112; - __asm - ("addu.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("2 addu.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addu_s_ob.c b/tests/tcg/mips/mips64-dsp/addu_s_ob.c deleted file mode 100644 index e89a4638b7..0000000000 --- a/tests/tcg/mips/mips64-dsp/addu_s_ob.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rs = 0x123456789ABCDEF0; - rt = 0x3456123498DEF390; - result = 0x468A68ACFFFFFFFF; - dspresult = 0x01; - - __asm - ("addu_s.ob %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - - if ((rd != result) || (dspreg != dspresult)) { - printf("addu_s.ob error\n\t"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addu_s_qb.c b/tests/tcg/mips/mips64-dsp/addu_s_qb.c deleted file mode 100644 index cb84293ade..0000000000 --- a/tests/tcg/mips/mips64-dsp/addu_s_qb.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x10FF01FF; - rt = 0x10010001; - result = 0x20FF01FF; - __asm - ("addu_s.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 20) & 0x1) != 1)) { - printf("1 addu_s.qb error 1\n"); - - return -1; - } - - rs = 0xFFFFFFFFFFFF1111; - rt = 0x00020001; - result = 0xFFFFFFFFFFFF1112; - __asm - ("addu_s.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 20) & 0x1) != 1)) { - printf("2 addu_s.qb error 2\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/addwc.c b/tests/tcg/mips/mips64-dsp/addwc.c deleted file mode 100644 index 5929cd2f5c..0000000000 --- a/tests/tcg/mips/mips64-dsp/addwc.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dspi, dspo; - long long result; - - rs = 0x10FF01FF; - rt = 0x10010001; - dspi = 0x00002000; - result = 0x21000201; - __asm - ("wrdsp %3\n" - "addwc %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dspi) - ); - if (rd != result) { - printf("1 addwc wrong\n"); - - return -1; - } - - rs = 0xFFFF1111; - rt = 0x00020001; - dspi = 0x00; - result = 0x00011112; - __asm - ("wrdsp %3\n" - "addwc %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dspi) - ); - if (rd != result) { - printf("2 addwc wrong\n"); - - return -1; - } - - rs = 0x8FFF1111; - rt = 0x80020001; - dspi = 0x00; - result = 0x10011112; - __asm - ("wrdsp %4\n" - "addwc %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspo) - : "r"(rs), "r"(rt), "r"(dspi) - ); - if ((rd != result) || (((dspo >> 20) & 0x01) != 1)) { - printf("3 addwc wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/bitrev.c b/tests/tcg/mips/mips64-dsp/bitrev.c deleted file mode 100644 index ac24ef3f5c..0000000000 --- a/tests/tcg/mips/mips64-dsp/bitrev.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x12345678; - result = 0x00001E6A; - - __asm - ("bitrev %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("bitrev wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/bposge32.c b/tests/tcg/mips/mips64-dsp/bposge32.c deleted file mode 100644 index 97bce44602..0000000000 --- a/tests/tcg/mips/mips64-dsp/bposge32.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long dsp, sum; - long long result; - - dsp = 0x20; - sum = 0x01; - result = 0x02; - - __asm - ("wrdsp %1\n\t" - "bposge32 test1\n\t" - "nop\n\t" - "addi %0, 0xA2\n\t" - "nop\n\t" - "test1:\n\t" - "addi %0, 0x01\n\t" - : "+r"(sum) - : "r"(dsp) - ); - if (sum != result) { - printf("bposge32 wrong\n"); - - return -1; - } - - dsp = 0x10; - sum = 0x01; - result = 0xA4; - - __asm - ("wrdsp %1\n\t" - "bposge32 test2\n\t" - "nop\n\t" - "addi %0, 0xA2\n\t" - "nop\n\t" - "test2:\n\t" - "addi %0, 0x01\n\t" - : "+r"(sum) - : "r"(dsp) - ); - if (sum != result) { - printf("bposge32 wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/bposge64.c b/tests/tcg/mips/mips64-dsp/bposge64.c deleted file mode 100644 index 36161ad852..0000000000 --- a/tests/tcg/mips/mips64-dsp/bposge64.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long dsp, sum; - long long result; - - dsp = 0x40; - sum = 0x01; - result = 0x02; - - __asm - ("wrdsp %1\n\t" - "bposge64 test1\n\t" - "nop\n\t" - "addi %0, 0xA2\n\t" - "nop\n\t" - "test1:\n\t" - "addi %0, 0x01\n\t" - : "+r"(sum) - : "r"(dsp) - ); - if (sum != result) { - printf("bposge64 wrong\n"); - - return -1; - } - - dsp = 0x10; - sum = 0x01; - result = 0xA4; - - __asm - ("wrdsp %1\n\t" - "bposge64 test2\n\t" - "nop\n\t" - "addi %0, 0xA2\n\t" - "nop\n\t" - "test2:\n\t" - "addi %0, 0x01\n\t" - : "+r"(sum) - : "r"(dsp) - ); - if (sum != result) { - printf("bposge64 wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_eq_ph.c b/tests/tcg/mips/mips64-dsp/cmp_eq_ph.c deleted file mode 100644 index 63069d0dab..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_eq_ph.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x11777066; - rt = 0x55AA33FF; - result = 0x00; - __asm - ("cmp.eq.ph %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - rd = (rd >> 24) & 0x03; - if (rd != result) { - printf("cmp.eq.ph wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x03; - __asm - ("cmp.eq.ph %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - rd = (rd >> 24) & 0x03; - if (rd != result) { - printf("cmp.eq.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_eq_pw.c b/tests/tcg/mips/mips64-dsp/cmp_eq_pw.c deleted file mode 100644 index bae4c06ccb..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_eq_pw.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEFF; - rt = 0x123456789ABCDEFF; - dspresult = 0x03; - - __asm - ("cmp.eq.pw %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x03); - - if (dspreg != dspresult) { - printf("1 cmp.eq.pw error\n"); - - return -1; - } - - rs = 0x123456799ABCDEFe; - rt = 0x123456789ABCDEFF; - dspresult = 0x00; - - __asm - ("cmp.eq.pw %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x03); - - if (dspreg != dspresult) { - printf("2 cmp.eq.pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_eq_qh.c b/tests/tcg/mips/mips64-dsp/cmp_eq_qh.c deleted file mode 100644 index 49ea271003..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_eq_qh.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x0E; - - __asm - ("cmp.eq.qh %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x0F); - - if (dspreg != dspresult) { - printf("cmp.eq.qh error\n"); - - return -1; - } - - rs = 0x12355a789A4CD3F0; - rt = 0x123456789ABCDEFF; - dspresult = 0x00; - - __asm - ("cmp.eq.qh %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x0F); - - if (dspreg != dspresult) { - printf("cmp.eq.qh error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_le_ph.c b/tests/tcg/mips/mips64-dsp/cmp_le_ph.c deleted file mode 100644 index 12d24f1783..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_le_ph.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x11777066; - rt = 0x55AA33FF; - result = 0x02; - __asm - ("cmp.le.ph %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - rd = (rd >> 24) & 0x03; - if (rd != result) { - printf("cmp.le.ph wrong\n"); - - return -1; - } - rs = 0x11777066; - rt = 0x11777066; - result = 0x03; - __asm - ("cmp.le.ph %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - rd = (rd >> 24) & 0x03; - if (rd != result) { - printf("cmp.le.ph wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_le_pw.c b/tests/tcg/mips/mips64-dsp/cmp_le_pw.c deleted file mode 100644 index 6acc43cd5b..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_le_pw.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x03; - - __asm - ("cmp.le.pw %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x03); - - if (dspreg != dspresult) { - printf("1 cmp.le.pw error\n"); - - return -1; - } - - rs = 0x123456799ABCEEFF; - rt = 0x123456789ABCDEFF; - dspresult = 0x00; - - __asm - ("cmp.le.pw %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x03); - - if (dspreg != dspresult) { - printf("2 cmp.le.pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_le_qh.c b/tests/tcg/mips/mips64-dsp/cmp_le_qh.c deleted file mode 100644 index c9ce21667f..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_le_qh.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x0F; - - __asm - ("cmp.le.qh %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x0F); - - if (dspreg != dspresult) { - printf("cmp.le.qh error\n"); - - return -1; - } - - rs = 0x823456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x0f; - - __asm - ("cmp.le.qh %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x0F); - - if (dspreg != dspresult) { - printf("cmp.le.qh error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_lt_ph.c b/tests/tcg/mips/mips64-dsp/cmp_lt_ph.c deleted file mode 100644 index 1d91228c3a..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_lt_ph.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x11777066; - rt = 0x55AA33FF; - result = 0x02; - __asm - ("cmp.lt.ph %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - rd = (rd >> 24) & 0x03; - if (rd != result) { - printf("cmp.lt.ph wrong\n"); - - return -1; - } - rs = 0x11777066; - rt = 0x11777066; - result = 0x00; - __asm - ("cmp.lt.ph %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - rd = (rd >> 24) & 0x03; - if (rd != result) { - printf("cmp.lt.ph2 wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_lt_pw.c b/tests/tcg/mips/mips64-dsp/cmp_lt_pw.c deleted file mode 100644 index 87e74caf38..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_lt_pw.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x01; - - __asm - ("cmp.lt.pw %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x03); - - if (dspreg != dspresult) { - printf("cmp.lt.pw error\n"); - - return -1; - } - - rs = 0x123456779ABCDEFf; - rt = 0x123456789ABCDEFF; - dspresult = 0x02; - - __asm - ("cmp.lt.pw %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x03); - - if (dspreg != dspresult) { - printf("cmp.lt.pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmp_lt_qh.c b/tests/tcg/mips/mips64-dsp/cmp_lt_qh.c deleted file mode 100644 index 0a13a5eaae..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmp_lt_qh.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123558789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x01; - - __asm - ("cmp.lt.qh %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x0F); - - if (dspreg != dspresult) { - printf("cmp.lt.qh error\n"); - - return -1; - } - - rs = 0x123356779ABbDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x0f; - - __asm - ("cmp.lt.qh %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0x0F); - - if (dspreg != dspresult) { - printf("cmp.lt.qh error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpgu_eq_ob.c b/tests/tcg/mips/mips64-dsp/cmpgu_eq_ob.c deleted file mode 100644 index 697d73dd1a..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpgu_eq_ob.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0xFE; - - __asm - ("cmpgu.eq.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.eq.ob error\n"); - - return -1; - } - - rs = 0x133456789ABCDEF0; - rt = 0x123556789ABCDEFF; - result = 0x3E; - - __asm - ("cmpgu.eq.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.eq.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpgu_eq_qb.c b/tests/tcg/mips/mips64-dsp/cmpgu_eq_qb.c deleted file mode 100644 index b41c4430fd..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpgu_eq_qb.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x02; - __asm - ("cmpgu.eq.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.eq.ph wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x0F; - __asm - ("cmpgu.eq.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("cmpgu.eq.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpgu_le_ob.c b/tests/tcg/mips/mips64-dsp/cmpgu_le_ob.c deleted file mode 100644 index 8b65f18c00..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpgu_le_ob.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0xFF; - - __asm - ("cmpgu.le.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.le.ob error\n"); - - return -1; - } - - rs = 0x823556789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0x3F; - - __asm - ("cmpgu.le.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.le.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpgu_le_qb.c b/tests/tcg/mips/mips64-dsp/cmpgu_le_qb.c deleted file mode 100644 index dd2b091f61..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpgu_le_qb.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x0F; - __asm - ("cmpgu.le.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("cmpgu.le.qb wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11766066; - result = 0x09; - __asm - ("cmpgu.le.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("cmpgu.le.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpgu_lt_ob.c b/tests/tcg/mips/mips64-dsp/cmpgu_lt_ob.c deleted file mode 100644 index 3e5c9dd6da..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpgu_lt_ob.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0x01; - - __asm - ("cmpgu.lt.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.lt.ob error\n"); - - return -1; - } - - rs = 0x823455789ABCDEF0; - rt = 0x123356789ABCDEFF; - result = 0x21; - - __asm - ("cmpgu.lt.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.lt.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpgu_lt_qb.c b/tests/tcg/mips/mips64-dsp/cmpgu_lt_qb.c deleted file mode 100644 index a467cb78db..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpgu_lt_qb.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x0D; - __asm - ("cmpgu.lt.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("cmpgu.lt.qb wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11766066; - result = 0x00; - __asm - ("cmpgu.lt.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("cmpgu.lt.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpu_eq_ob.c b/tests/tcg/mips/mips64-dsp/cmpu_eq_ob.c deleted file mode 100644 index 4d1983e5ea..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpu_eq_ob.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0xFE; - - __asm - ("cmpu.eq.ob %1, %2\n\t" - "rddsp %0" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if (dspreg != dspresult) { - printf("cmpu.eq.ob error\n"); - - return -1; - } - - rs = 0x133516713A0CD1F0; - rt = 0x123456789ABCDEFF; - dspresult = 0x00; - - __asm - ("cmpu.eq.ob %1, %2\n\t" - "rddsp %0" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if (dspreg != dspresult) { - printf("cmpu.eq.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpu_eq_qb.c b/tests/tcg/mips/mips64-dsp/cmpu_eq_qb.c deleted file mode 100644 index 28f3bec252..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpu_eq_qb.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long dsp; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x02; - __asm - ("cmpu.eq.qb %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (dsp != result) { - printf("cmpu.eq.qb wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x0F; - __asm - ("cmpu.eq.qb %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (dsp != result) { - printf("cmpu.eq.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpu_le_ob.c b/tests/tcg/mips/mips64-dsp/cmpu_le_ob.c deleted file mode 100644 index 8acbd1c4ba..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpu_le_ob.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0xFF; - - __asm - ("cmpu.le.ob %1, %2\n\t" - "rddsp %0" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = dspreg >> 24; - if (dspreg != dspresult) { - printf("cmpu.le.ob error\n"); - - return -1; - } - - rs = 0x823656789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x3F; - - __asm - ("cmpu.le.ob %1, %2\n\t" - "rddsp %0" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = dspreg >> 24; - if (dspreg != dspresult) { - printf("cmpu.le.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpu_le_qb.c b/tests/tcg/mips/mips64-dsp/cmpu_le_qb.c deleted file mode 100644 index 8a17a08513..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpu_le_qb.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long dsp; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x0F; - __asm - ("cmpu.le.qb %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (dsp != result) { - printf("cmpu.le.qb wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x0F; - __asm - ("cmpu.le.qb %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (dsp != result) { - printf("cmpu.le.qb wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpu_lt_ob.c b/tests/tcg/mips/mips64-dsp/cmpu_lt_ob.c deleted file mode 100644 index 34e312d818..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpu_lt_ob.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x01; - - __asm - ("cmpu.lt.ob %1, %2\n\t" - "rddsp %0" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = dspreg >> 24; - if (dspreg != dspresult) { - printf("cmpu.lt.ob error\n"); - - return -1; - } - - rs = 0x823156789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x41; - - __asm - ("cmpu.lt.ob %1, %2\n\t" - "rddsp %0" - : "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = dspreg >> 24; - if (dspreg != dspresult) { - printf("cmpu.lt.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/cmpu_lt_qb.c b/tests/tcg/mips/mips64-dsp/cmpu_lt_qb.c deleted file mode 100644 index adb75eed52..0000000000 --- a/tests/tcg/mips/mips64-dsp/cmpu_lt_qb.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long dsp; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x0D; - __asm - ("cmpu.lt.qb %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (dsp != result) { - printf("cmpu.lt.qb wrong\n"); - - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x00; - __asm - ("cmpu.lt.qb %1, %2\n\t" - "rddsp %0\n\t" - : "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (dsp != result) { - printf("cmpu.lt.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dappend.c b/tests/tcg/mips/mips64-dsp/dappend.c deleted file mode 100644 index ba8e12182e..0000000000 --- a/tests/tcg/mips/mips64-dsp/dappend.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long res; - rt = 0x1234567887654321; - rs = 0xabcd1234abcd8765; - - res = 0x1234567887654321; - __asm - ("dappend %0, %1, 0x0\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("dappend error\n"); - return -1; - } - - rt = 0x1234567887654321; - rs = 0xabcd1234abcd8765; - - res = 0x2345678876543215; - __asm - ("dappend %0, %1, 0x4\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("dappend error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextp.c b/tests/tcg/mips/mips64-dsp/dextp.c deleted file mode 100644 index a469cc0366..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextp.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp; - int rs; - - rs = 0xabcd1234; - - achi = 0x12345678; - acli = 0x87654321; - res = 0xff; - resdsp = 0x0; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4\n\t" - "dextp %0, $ac1, 0x7\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 14) & 0x1; - if ((dsp != resdsp) || (rt != res)) { - printf("dextp error\n"); - return -1; - } - - rs = 0xabcd1200; - - achi = 0x12345678; - acli = 0x87654321; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4\n\t" - "dextp %0, $ac1, 0x7\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 14) & 0x1; - if (dsp != resdsp) { - printf("dextp error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextpdp.c b/tests/tcg/mips/mips64-dsp/dextpdp.c deleted file mode 100644 index a2361e2d42..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextpdp.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp, resdsppos; - int rs; - int tmp1, tmp2; - - rs = 0xabcd1234; - - achi = 0x12345678; - acli = 0x87654321; - res = 0xff; - resdsp = 0x0; - resdsppos = 0x2c; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4\n\t" - "dextpdp %0, $ac1, 0x7\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - tmp1 = (dsp >> 14) & 0x1; - tmp2 = dsp & 0x3f; - - if ((tmp1 != resdsp) || (rt != res) || (tmp2 != resdsppos)) { - printf("dextpdp error\n"); - return -1; - } - - rs = 0xabcd1200; - - achi = 0x12345678; - acli = 0x87654321; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4\n\t" - "dextpdp %0, $ac1, 0x7\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - tmp1 = (dsp >> 14) & 0x1; - - if (tmp1 != resdsp) { - printf("dextpdp error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextpdpv.c b/tests/tcg/mips/mips64-dsp/dextpdpv.c deleted file mode 100644 index 09c0b5b412..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextpdpv.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long res, resdsp, resdsppos; - int rsdsp; - int tmp1, tmp2; - - rsdsp = 0xabcd1234; - rs = 0x7; - achi = 0x12345678; - acli = 0x87654321; - res = 0xff; - resdsp = 0x0; - resdsppos = 0x2c; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4, 0x1\n\t" - "wrdsp %4\n\t" - "dextpdpv %0, $ac1, %5\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rsdsp), "r"(rs) - ); - - tmp1 = (dsp >> 14) & 0x1; - tmp2 = dsp & 0x3f; - - if ((tmp1 != resdsp) || (rt != res) || (tmp2 != resdsppos)) { - printf("dextpdpv error\n"); - return -1; - } - - rsdsp = 0xabcd1200; - rs = 0x7; - achi = 0x12345678; - acli = 0x87654321; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4, 0x1\n\t" - "wrdsp %4\n\t" - "dextpdpv %0, $ac1, %5\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rsdsp), "r"(rs) - ); - - tmp1 = (dsp >> 14) & 0x1; - - if (tmp1 != resdsp) { - printf("dextpdpv error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextpv.c b/tests/tcg/mips/mips64-dsp/dextpv.c deleted file mode 100644 index 2626f3d98c..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextpv.c +++ /dev/null @@ -1,58 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long res, resdsp; - int rsdsp; - - rsdsp = 0xabcd1234; - rs = 0x7; - - achi = 0x12345678; - acli = 0x87654321; - res = 0xff; - resdsp = 0x0; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4, 0x1\n\t" - "wrdsp %4\n\t" - "dextpv %0, $ac1, %5\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rsdsp), "r"(rs) - ); - dsp = (dsp >> 14) & 0x1; - if ((dsp != resdsp) || (rt != res)) { - printf("dextpv error\n"); - return -1; - } - - rsdsp = 0xabcd1200; - rs = 0x7; - - achi = 0x12345678; - acli = 0x87654321; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "wrdsp %4, 0x1\n\t" - "wrdsp %4\n\t" - "dextpv %0, $ac1, %5\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rsdsp), "r"(rs) - ); - dsp = (dsp >> 14) & 0x1; - if (dsp != resdsp) { - printf("dextpv error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_l.c b/tests/tcg/mips/mips64-dsp/dextr_l.c deleted file mode 100644 index 538846df18..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_l.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt; - long long achi, acli; - long long res; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x2100000000123456; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextr.l %0, $ac1, 0x8\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli) - ); - if (rt != res) { - printf("dextr.l error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x12345678; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextr.l %0, $ac1, 0x0\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli) - ); - if (rt != res) { - printf("dextr.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_r_l.c b/tests/tcg/mips/mips64-dsp/dextr_r_l.c deleted file mode 100644 index a10a9ab40e..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_r_l.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x2100000000123456; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_r.l %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_r.l error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x12345678; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_r.l %0, $ac1, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_r.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_r_w.c b/tests/tcg/mips/mips64-dsp/dextr_r_w.c deleted file mode 100644 index 2774e9bfcc..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_r_w.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x123456; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_r.w %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_r.w error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x12345678; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_r.w %0, $ac1, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_r.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_rs_l.c b/tests/tcg/mips/mips64-dsp/dextr_rs_l.c deleted file mode 100644 index 1a202fefa2..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_rs_l.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x8000000000000000; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_rs.l %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_rs.l error\n"); - return -1; - } - - achi = 0x00; - acli = 0x12345678; - - res = 0x12345678; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_rs.l %0, $ac1, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_rs.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_rs_w.c b/tests/tcg/mips/mips64-dsp/dextr_rs_w.c deleted file mode 100644 index ebe5f99db0..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_rs_w.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0xffffffff80000000; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_rs.w %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_rs.w error\n"); - return -1; - } - - achi = 0x00; - acli = 0x12345678; - - res = 0x123456; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_rs.w %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextr_rs.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_s_h.c b/tests/tcg/mips/mips64-dsp/dextr_s_h.c deleted file mode 100644 index 1adb5549a9..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_s_h.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0xffffffffffff8000; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_s.h %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("1 dextr_s.h error\n"); - return -1; - } - - achi = 0x77654321; - acli = 0x12345678; - - res = 0x7fff; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_s.h %0, $ac1, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("2 dextr_s.h error\n"); - return -1; - } - - achi = 0x00; - acli = 0x78; - - res = 0x7; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextr_s.h %0, $ac1, 0x4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("3 dextr_s.h error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextr_w.c b/tests/tcg/mips/mips64-dsp/dextr_w.c deleted file mode 100644 index 79bed5da35..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextr_w.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt; - long long achi, acli; - long long res; - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x123456; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextr.w %0, $ac1, 0x8\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli) - ); - if (rt != res) { - printf("dextr.w error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - - res = 0x12345678; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextr.w %0, $ac1, 0x0\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli) - ); - if (rt != res) { - printf("dextr.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_l.c b/tests/tcg/mips/mips64-dsp/dextrv_l.c deleted file mode 100644 index 2e6187f729..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_l.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long res; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0x2100000000123456; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextrv.l %0, $ac1, %3\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli), "r"(rs) - ); - if (rt != res) { - printf("dextrv.l error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x0; - - res = 0x12345678; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextrv.l %0, $ac1, %3\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli), "r"(rs) - ); - if (rt != res) { - printf("dextrv.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_r_l.c b/tests/tcg/mips/mips64-dsp/dextrv_r_l.c deleted file mode 100644 index b47a0177d4..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_r_l.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, dsp, rs; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0x2100000000123456; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_r.l %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_r.l error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x0; - - res = 0x12345678; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_r.l %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_r.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_r_w.c b/tests/tcg/mips/mips64-dsp/dextrv_r_w.c deleted file mode 100644 index cd201deb21..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_r_w.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0x123456; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_r.w %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_r.w error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x0; - - res = 0x12345678; - resdsp = 0x01; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_r.w %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_r.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_rs_l.c b/tests/tcg/mips/mips64-dsp/dextrv_rs_l.c deleted file mode 100644 index 6ce4185462..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_rs_l.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0x8000000000000000; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_rs.l %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_rs.l error\n"); - return -1; - } - - achi = 0x00; - acli = 0x12345678; - rs = 0x0; - - res = 0x12345678; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_rs.l %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_rs.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_rs_w.c b/tests/tcg/mips/mips64-dsp/dextrv_rs_w.c deleted file mode 100644 index a65183c030..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_rs_w.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0xffffffff80000000; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_rs.w %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_rs.w error\n"); - return -1; - } - - achi = 0x00; - acli = 0x12345678; - rs = 0x8; - - res = 0x123456; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_rs.w %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_rs.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_s_h.c b/tests/tcg/mips/mips64-dsp/dextrv_s_h.c deleted file mode 100644 index 87d3aeedce..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_s_h.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long res, resdsp; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0xffffffffffff8000; - resdsp = 0x1; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dextrv_s.h %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs) - ); - dsp = (dsp >> 23) & 0x1; - - if ((dsp != resdsp) || (rt != res)) { - printf("dextrv_s.h error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dextrv_w.c b/tests/tcg/mips/mips64-dsp/dextrv_w.c deleted file mode 100644 index 973765c1c0..0000000000 --- a/tests/tcg/mips/mips64-dsp/dextrv_w.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long res; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x8; - - res = 0x123456; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextrv.w %0, $ac1, %3\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli), "r"(rs) - ); - if (rt != res) { - printf("dextrv.w error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x0; - - res = 0x12345678; - - __asm - ("mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "dextrv.w %0, $ac1, %3\n\t" - : "=r"(rt) - : "r"(achi), "r"(acli), "r"(rs) - ); - if (rt != res) { - printf("dextrv.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dinsv.c b/tests/tcg/mips/mips64-dsp/dinsv.c deleted file mode 100644 index f6192188c0..0000000000 --- a/tests/tcg/mips/mips64-dsp/dinsv.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long res; - - rs = 0x1234567887654321; - rt = 0x1234567812345678; - dsp = 0x2222; - res = 0x1234567812345678; - __asm - ("wrdsp %1, 0x3\n\t" - "wrdsp %1\n\t" - "dinsv %0, %2\n\t" - : "+r"(rt) - : "r"(dsp), "r"(rs) - ); - - if (rt != res) { - printf("dinsv error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dmadd.c b/tests/tcg/mips/mips64-dsp/dmadd.c deleted file mode 100644 index fb22614725..0000000000 --- a/tests/tcg/mips/mips64-dsp/dmadd.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - achi = 0x1; - acli = 0x1; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - - resh = 0x1; - resl = 0x5; - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmadd $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("1 dmadd error\n"); - - return -1; - } - - achi = 0x1; - acli = 0x1; - - rs = 0xaaaabbbbccccdddd; - rt = 0xaaaabbbbccccdddd; - - resh = 0x0000000000000000; - resl = 0xffffffffca860b63; - - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmadd $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("2 dmadd error\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dmaddu.c b/tests/tcg/mips/mips64-dsp/dmaddu.c deleted file mode 100644 index 39ab0c10db..0000000000 --- a/tests/tcg/mips/mips64-dsp/dmaddu.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - achi = 0x1; - acli = 0x2; - - rs = 0x0000000200000002; - rt = 0x0000000200000002; - resh = 0x1; - resl = 0xa; - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmaddu $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("1 dmaddu error\n"); - - return -1; - } - - achi = 0x1; - acli = 0x1; - - rs = 0xaaaabbbbccccdddd; - rt = 0xaaaabbbbccccdddd; - - resh = 0x0000000000000002; - resl = 0xffffffffca860b63; - - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmaddu $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("2 dmaddu error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dmsub.c b/tests/tcg/mips/mips64-dsp/dmsub.c deleted file mode 100644 index 16be6170e4..0000000000 --- a/tests/tcg/mips/mips64-dsp/dmsub.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - achi = 0x1; - acli = 0x8; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - - resh = 0x1; - resl = 0x4; - - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmsub $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("1 dmsub error\n"); - - return -1; - } - - achi = 0xfffffffF; - acli = 0xfffffffF; - - rs = 0x8888999977776666; - rt = 0x9999888877776666; - - resh = 0xffffffffffffffff; - resl = 0x789aae13; - - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmsub $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("2 dmsub error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dmsubu.c b/tests/tcg/mips/mips64-dsp/dmsubu.c deleted file mode 100644 index cc4838ad5f..0000000000 --- a/tests/tcg/mips/mips64-dsp/dmsubu.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - achi = 0x1; - acli = 0x8; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - - resh = 0x1; - resl = 0x4; - - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmsubu $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("1 dmsubu error\n"); - - return -1; - } - - achi = 0xfffffffF; - acli = 0xfffffffF; - - rs = 0x8888999977776666; - rt = 0x9999888877776666; - - resh = 0xffffffffffffffff; - resl = 0x789aae13; - - __asm - ("mthi %2, $ac1 \t\n" - "mtlo %3, $ac1 \t\n" - "dmsubu $ac1, %4, %5\t\n" - "mfhi %0, $ac1 \t\n" - "mflo %1, $ac1 \t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("2 dmsubu error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dmthlip.c b/tests/tcg/mips/mips64-dsp/dmthlip.c deleted file mode 100644 index 027555fb53..0000000000 --- a/tests/tcg/mips/mips64-dsp/dmthlip.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, dsp; - long long achi, acli; - - long long rsdsp; - long long acho, aclo; - - long long res; - long long reshi, reslo; - - - rs = 0xaaaabbbbccccdddd; - achi = 0x87654321; - acli = 0x12345678; - dsp = 0x22; - - res = 0x62; - reshi = 0x12345678; - reslo = 0xffffffffccccdddd; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "wrdsp %5\n\t" - "dmthlip %6, $ac1\n\t" - "rddsp %0\n\t" - "mfhi %1, $ac1\n\t" - "mflo %2, $ac1\n\t" - : "=r"(rsdsp), "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(dsp), "r"(rs) - ); - if ((rsdsp != res) || (acho != reshi) || (aclo != reslo)) { - printf("dmthlip error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpaq_s_w_ph.c b/tests/tcg/mips/mips64-dsp/dpaq_s_w_ph.c deleted file mode 100644 index 1bca935008..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpaq_s_w_ph.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long ach = 0, acl = 0; - long long resulth, resultl, resultdsp; - - rs = 0x800000FF; - rt = 0x80000002; - resulth = 0x00; - resultl = 0xFFFFFFFF800003FB; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpaq_s.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = dsp >> 17 & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("dpaq_w.w.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpaq_s_w_qh.c b/tests/tcg/mips/mips64-dsp/dpaq_s_w_qh.c deleted file mode 100644 index 844a347429..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpaq_s_w_qh.c +++ /dev/null @@ -1,57 +0,0 @@ -#include"io.h" -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - achi = 0x1; - acli = 0x1; - rs = 0x0001000100010001; - rt = 0x0002000200020002; - resh = 0x1; - resl = 0x11; - - __asm - ("mthi %2, $ac1\t\n" - "mtlo %3, $ac1\t\n" - "dpaq_s.w.qh $ac1, %4, %5\t\n" - "mfhi %0, $ac1\t\n" - "mflo %1, $ac1\t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("1 dpaq_s.w.qh error\n"); - - return -1; - } - - achi = 0xffffffff; - acli = 0xaaaaaaaa; - - rs = 0x1111222233334444; - rt = 0xffffeeeeddddcccc; - - resh = 0x00; - resl = 0xffffffffd27ad82e; - - __asm - ("mthi %2, $ac1\t\n" - "mtlo %3, $ac1\t\n" - "dpaq_s.w.qh $ac1, %4, %5\t\n" - "mfhi %0, $ac1\t\n" - "mflo %1, $ac1\t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("2 dpaq_s.w.qh error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpaq_sa_l_pw.c b/tests/tcg/mips/mips64-dsp/dpaq_sa_l_pw.c deleted file mode 100644 index 1bb2ec2f26..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpaq_sa_l_pw.c +++ /dev/null @@ -1,88 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long achi, acli; - long long acho, aclo; - long long dsp; - long long resh, resl; - long long resdsp; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - achi = 0x1; - acli = 0x1; - resh = 0xffffffffffffffff; - resl = 0x0; - resdsp = 0x01; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "dpaq_sa.l.pw $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl) || ((dsp >> (16 + 1)) != resdsp)) { - printf("1 dpaq_sa_l_pw error\n"); - - return -1; - } - - rs = 0xaaaabbbbccccdddd; - rt = 0x3333444455556666; - achi = 0x88888888; - acli = 0x66666666; - - resh = 0xffffffff88888887; - resl = 0xffffffff9e2661da; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpaq_sa.l.pw $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("2 dpaq_sa_l_pw error\n"); - - return -1; - } - - rs = 0x8000000080000000; - rt = 0x8000000080000000; - achi = 0x88888888; - acli = 0x66666666; - - resh = 0xffffffffffffffff; - resl = 0x00; - resdsp = 0x01; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "dpaq_sa.l.pw $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl) || ((dsp >> (16 + 1)) != resdsp)) { - printf("2 dpaq_sa_l_pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpaq_sa_l_w.c b/tests/tcg/mips/mips64-dsp/dpaq_sa_l_w.c deleted file mode 100644 index f840cdd761..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpaq_sa_l_w.c +++ /dev/null @@ -1,82 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long ach = 0, acl = 0; - long long resulth, resultl, resultdsp; - - rs = 0x80000000; - rt = 0x80000000; - resulth = 0x7FFFFFFF; - resultl = 0xffffffffFFFFFFFF; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %0, $ac1\n\t" - "dpaq_sa.l.w $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("dpaq_sa.l.w error\n"); - - return -1; - } - - ach = 0x12; - acl = 0x48; - rs = 0x80000000; - rt = 0x80000000; - - resulth = 0x7FFFFFFF; - resultl = 0xffffffffFFFFFFFF; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %0, $ac1\n\t" - "dpaq_sa.l.w $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("dpaq_sa.l.w error\n"); - - return -1; - } - - ach = 0x741532A0; - acl = 0xfceabb08; - rs = 0x80000000; - rt = 0x80000000; - - resulth = 0x7fffffff; - resultl = 0xffffffffffffffff; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %0, $ac1\n\t" - "dpaq_sa.l.w $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("dpaq_sa.l.w error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpau_h_obl.c b/tests/tcg/mips/mips64-dsp/dpau_h_obl.c deleted file mode 100644 index 54905e8f93..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpau_h_obl.c +++ /dev/null @@ -1,59 +0,0 @@ - -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - achi = 0x1; - acli = 0x1; - resh = 0x1; - resl = 0x3; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpau.h.obl $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dpau.h.obl error\n"); - - return -1; - } - - rs = 0xaaaabbbbccccdddd; - rt = 0x3333444455556666; - achi = 0x88888888; - acli = 0x66666666; - - resh = 0xffffffff88888888; - resl = 0x66670d7a; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpau.h.obl $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dpau.h.obl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpau_h_obr.c b/tests/tcg/mips/mips64-dsp/dpau_h_obr.c deleted file mode 100644 index d7aa60b4b1..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpau_h_obr.c +++ /dev/null @@ -1,59 +0,0 @@ - -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - achi = 0x1; - acli = 0x1; - resh = 0x1; - resl = 0x3; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpau.h.obr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dpau.h.obr error\n"); - - return -1; - } - - rs = 0xccccddddaaaabbbb; - rt = 0x5555666633334444; - achi = 0x88888888; - acli = 0x66666666; - - resh = 0xffffffff88888888; - resl = 0x66670d7a; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpau.h.obr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dpau.h.obr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpau_h_qbl.c b/tests/tcg/mips/mips64-dsp/dpau_h_qbl.c deleted file mode 100644 index fcfd764310..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpau_h_qbl.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 3; - long long resulth, resultl; - - rs = 0x800000FF; - rt = 0x80000002; - resulth = 0x05; - resultl = 0x4003; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpau.h.qbl $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("dpau.h.qbl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpau_h_qbr.c b/tests/tcg/mips/mips64-dsp/dpau_h_qbr.c deleted file mode 100644 index 3282461a7d..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpau_h_qbr.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 3; - long long resulth, resultl; - - rs = 0x800000FF; - rt = 0x80000002; - resulth = 0x05; - resultl = 0x0201; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpau.h.qbr $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("dpau.h.qbr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsq_s_w_ph.c b/tests/tcg/mips/mips64-dsp/dpsq_s_w_ph.c deleted file mode 100644 index 7660f037da..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsq_s_w_ph.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0xBC0123AD; - rt = 0x01643721; - resulth = 0x04; - resultl = 0xFFFFFFFFEE9794A3; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsq_s.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("1 dpsq_s.w.ph wrong\n"); - - return -1; - } - - ach = 0x1424Ef1f; - acl = 0x1035219A; - rs = 0x800083AD; - rt = 0x80003721; - resulth = 0x1424ef1e; - resultl = 0x577ed901; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsq_s.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("2 dpsq_s.w.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsq_s_w_qh.c b/tests/tcg/mips/mips64-dsp/dpsq_s_w_qh.c deleted file mode 100644 index 2cc50c577e..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsq_s_w_qh.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - rs = 0xffffeeeeddddcccc; - rt = 0x9999888877776666; - achi = 0x67576; - acli = 0x98878; - - resh = 0x67576; - resl = 0x5b1682c4; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpsq_s.w.qh $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("1 dpsq_s.w.qh wrong\n"); - - return -1; - } - - rs = 0x8000800080008000; - rt = 0x8000800080008000; - achi = 0x67576; - acli = 0x98878; - - resh = 0x67575; - resl = 0x0009887c; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dpsq_s.w.qh $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("2 dpsq_s.w.qh wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsq_sa_l_pw.c b/tests/tcg/mips/mips64-dsp/dpsq_sa_l_pw.c deleted file mode 100644 index 7fc2503fc5..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsq_sa_l_pw.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long achi, acli; - long long resh, resl, resdsp; - - rs = 0x89789BC0123AD; - rt = 0x5467591643721; - - achi = 0x98765437; - acli = 0x65489709; - - resh = 0xffffffffffffffff; - resl = 0x00; - - resdsp = 0x01; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsq_sa.l.pw $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(achi), "+r"(acli), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x01; - if ((dsp != resdsp) || (achi != resh) || (acli != resl)) { - printf("1 dpsq_sa.l.pw wrong\n"); - - return -1; - } - - /* clear dspcontrol reg for next test use. */ - dsp = 0; - __asm - ("wrdsp %0" - : - : "r"(dsp) - ); - - rs = 0x8B78980000000; - rt = 0x5867580000000; - - achi = 0x98765437; - acli = 0x65489709; - - resh = 0xffffffff98765436; - resl = 0x11d367d0; - - resdsp = 0x01; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsq_sa.l.pw $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(achi), "+r"(acli), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x01; - if ((dsp != resdsp) || (achi != resh) || (acli != resl)) { - printf("2 dpsq_sa.l.pw wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsq_sa_l_w.c b/tests/tcg/mips/mips64-dsp/dpsq_sa_l_w.c deleted file mode 100644 index f55afc9095..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsq_sa_l_w.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long ach = 5, acl = 5; - long long resulth, resultl, resultdsp; - - rs = 0xBC0123AD; - rt = 0x01643721; - - resulth = 0xfffffffffdf4cbe0; - resultl = 0xFFFFFFFFd138776b; - resultdsp = 0x00; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsq_sa.l.w $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("1 dpsq_sa.l.w wrong\n"); - - return -1; - } - - ach = 0x54321123; - acl = 5; - rs = 0x80000000; - rt = 0x80000000; - - resulth = 0xffffffffd4321123; - resultl = 0x06; - resultdsp = 0x01; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsq_sa.l.w $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("2 dpsq_sa.l.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsu_h_obl.c b/tests/tcg/mips/mips64-dsp/dpsu_h_obl.c deleted file mode 100644 index c0a8f4d7aa..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsu_h_obl.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0x88886666BC0123AD; - rt = 0x9999888801643721; - - resulth = 0x04; - resultl = 0xFFFFFFFFFFFEF115; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsu.h.obl $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - - if ((ach != resulth) || (acl != resultl)) { - printf("dpsu.h.obl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsu_h_obr.c b/tests/tcg/mips/mips64-dsp/dpsu_h_obr.c deleted file mode 100644 index aa0d47a065..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsu_h_obr.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0x7878878888886666; - rt = 0x9865454399998888; - - resulth = 0x04; - resultl = 0xFFFFFFFFFFFeF115; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsu.h.obr $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - - if ((ach != resulth) || (acl != resultl)) { - printf("dpsu.h.qbr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsu_h_qbl.c b/tests/tcg/mips/mips64-dsp/dpsu_h_qbl.c deleted file mode 100644 index da6dbb6154..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsu_h_qbl.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0xBC0123AD; - rt = 0x01643721; - resulth = 0x04; - resultl = 0xFFFFFFFFFFFFFEE5; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsu.h.qbl $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("dpsu.h.qbl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dpsu_h_qbr.c b/tests/tcg/mips/mips64-dsp/dpsu_h_qbr.c deleted file mode 100644 index bf00b70aa7..0000000000 --- a/tests/tcg/mips/mips64-dsp/dpsu_h_qbr.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0xBC0123AD; - rt = 0x01643721; - resulth = 0x04; - resultl = 0xFFFFFFFFFFFFE233; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsu.h.qbr $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("dpsu.h.qbr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dshilo.c b/tests/tcg/mips/mips64-dsp/dshilo.c deleted file mode 100644 index f50584b9c4..0000000000 --- a/tests/tcg/mips/mips64-dsp/dshilo.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long achi, acli; - long long acho, aclo; - long long reshi, reslo; - - achi = 0x87654321; - acli = 0x12345678; - - reshi = 0xfffffffff8765432; - reslo = 0x1234567; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dshilo $ac1, 0x4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli) - ); - - if ((acho != reshi) || (aclo != reslo)) { - printf("1 dshilo error\n"); - return -1; - } - - achi = 0x87654321; - acli = 0x12345678; - - reshi = 0x1234567; - reslo = 0x00; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dshilo $ac1, -60\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli) - ); - - if ((acho != reshi) || (aclo != reslo)) { - printf("2 dshilo error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/dshilov.c b/tests/tcg/mips/mips64-dsp/dshilov.c deleted file mode 100644 index 792bd23730..0000000000 --- a/tests/tcg/mips/mips64-dsp/dshilov.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long achi, acli, rs; - long long acho, aclo; - long long reshi, reslo; - - achi = 0x87654321; - acli = 0x12345678; - rs = 0x4; - - reshi = 0xfffffffff8765432; - reslo = 0x1234567; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dshilov $ac1, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs) - ); - - if ((acho != reshi) || (aclo != reslo)) { - printf("dshilov error\n"); - return -1; - } - - rs = 0x44; - achi = 0x87654321; - acli = 0x12345678; - - reshi = 0x1234567; - reslo = 0x00; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "dshilov $ac1, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs) - ); - - if ((acho != reshi) || (aclo != reslo)) { - printf("dshilov error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extp.c b/tests/tcg/mips/mips64-dsp/extp.c deleted file mode 100644 index c72f54bace..0000000000 --- a/tests/tcg/mips/mips64-dsp/extp.c +++ /dev/null @@ -1,50 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - result = 0x000C; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extp %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 14) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extp wrong\n"); - - return -1; - } - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x01; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extp %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 14) & 0x01; - if (dsp != 1) { - printf("extp wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extpdp.c b/tests/tcg/mips/mips64-dsp/extpdp.c deleted file mode 100644 index f430193841..0000000000 --- a/tests/tcg/mips/mips64-dsp/extpdp.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ach, acl, dsp, pos, efi; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - result = 0x000C; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extpdp %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl) - ); - pos = dsp & 0x3F; - efi = (dsp >> 14) & 0x01; - if ((pos != 3) || (efi != 0) || (result != rt)) { - printf("extpdp wrong\n"); - - return -1; - } - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x01; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extpdp %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl) - ); - efi = (dsp >> 14) & 0x01; - if (efi != 1) { - printf("extpdp wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extpdpv.c b/tests/tcg/mips/mips64-dsp/extpdpv.c deleted file mode 100644 index ba57426d22..0000000000 --- a/tests/tcg/mips/mips64-dsp/extpdpv.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, ach, acl, dsp, pos, efi; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - rs = 0x03; - result = 0x000C; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extpdpv %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl), "r"(rs) - ); - pos = dsp & 0x3F; - efi = (dsp >> 14) & 0x01; - if ((pos != 3) || (efi != 0) || (result != rt)) { - printf("extpdpv wrong\n"); - - return -1; - } - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x01; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extpdpv %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl), "r"(rs) - ); - efi = (dsp >> 14) & 0x01; - if (efi != 1) { - printf("extpdpv wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extpv.c b/tests/tcg/mips/mips64-dsp/extpv.c deleted file mode 100644 index 158472bf93..0000000000 --- a/tests/tcg/mips/mips64-dsp/extpv.c +++ /dev/null @@ -1,51 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ac, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - ac = 0x03; - result = 0x000C; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extpv %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl), "r"(ac) - ); - dsp = (dsp >> 14) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extpv wrong\n"); - - return -1; - } - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x01; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extpv %0, $ac1, %4\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(ach), "r"(acl), "r"(ac) - ); - dsp = (dsp >> 14) & 0x01; - if (dsp != 1) { - printf("extpv wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extr_r_w.c b/tests/tcg/mips/mips64-dsp/extr_r_w.c deleted file mode 100644 index 94572ad154..0000000000 --- a/tests/tcg/mips/mips64-dsp/extr_r_w.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - result = 0xFFFFFFFFA0001699; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_r.w %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("1 extr_r.w wrong\n"); - - return -1; - } - - /* Clear dspcontrol */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - ach = 0x01; - acl = 0xB4CB; - result = 0x10000B4D; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_r.w %0, $ac1, 0x04\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("2 extr_r.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extr_rs_w.c b/tests/tcg/mips/mips64-dsp/extr_rs_w.c deleted file mode 100644 index 73551f96b3..0000000000 --- a/tests/tcg/mips/mips64-dsp/extr_rs_w.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - result = 0x7FFFFFFF; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_rs.w %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("1 extr_rs.w wrong\n"); - - return -1; - } - - /* Clear dspcontrol */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - ach = 0x01; - acl = 0xB4CB; - result = 0x10000B4D; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_rs.w %0, $ac1, 0x04\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("2 extr_rs.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extr_s_h.c b/tests/tcg/mips/mips64-dsp/extr_s_h.c deleted file mode 100644 index de10cb57a5..0000000000 --- a/tests/tcg/mips/mips64-dsp/extr_s_h.c +++ /dev/null @@ -1,71 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - result = 0x00007FFF; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_s.h %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extr_s.h wrong\n"); - - return -1; - } - - ach = 0xffffffff; - acl = 0x12344321; - result = 0xffffffffFFFF8000; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_s.h %0, $ac1, 0x08\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extr_s.h wrong\n"); - - return -1; - } - - /* Clear dsp */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - ach = 0x00; - acl = 0x4321; - result = 0x432; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr_s.h %0, $ac1, 0x04\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extr_s.h wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extr_w.c b/tests/tcg/mips/mips64-dsp/extr_w.c deleted file mode 100644 index bd69576687..0000000000 --- a/tests/tcg/mips/mips64-dsp/extr_w.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - result = 0xFFFFFFFFA0001699; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr.w %0, $ac1, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extr.w wrong\n"); - - return -1; - } - - /* Clear dspcontrol */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - ach = 0x01; - acl = 0xB4CB; - result = 0x10000B4C; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "extr.w %0, $ac1, 0x04\n\t" - "rddsp %1\n\t" - : "=r"(rt), "=r"(dsp) - : "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extr.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extrv_r_w.c b/tests/tcg/mips/mips64-dsp/extrv_r_w.c deleted file mode 100644 index 8379729787..0000000000 --- a/tests/tcg/mips/mips64-dsp/extrv_r_w.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - rs = 0x03; - result = 0xFFFFFFFFA0001699; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_r.w %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extrv_r.w wrong\n"); - - return -1; - } - - /* Clear dspcontrol */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - rs = 4; - ach = 0x01; - acl = 0xB4CB; - result = 0x10000B4D; - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_r.w %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extrv_r.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extrv_rs_w.c b/tests/tcg/mips/mips64-dsp/extrv_rs_w.c deleted file mode 100644 index 8707cd1174..0000000000 --- a/tests/tcg/mips/mips64-dsp/extrv_rs_w.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - rs = 0x03; - result = 0x7FFFFFFF; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_rs.w %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("1 extrv_rs.w wrong\n"); - - return -1; - } - - /* Clear dspcontrol */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - rs = 4; - ach = 0x01; - acl = 0xB4CB; - result = 0x10000B4D; - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_rs.w %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("2 extrv_rs.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extrv_s_h.c b/tests/tcg/mips/mips64-dsp/extrv_s_h.c deleted file mode 100644 index b6dcaebcbc..0000000000 --- a/tests/tcg/mips/mips64-dsp/extrv_s_h.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - rs = 0x03; - result = 0x00007FFF; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_s.h %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extrv_s.h wrong\n"); - - return -1; - } - - rs = 0x08; - ach = 0xffffffff; - acl = 0x12344321; - result = 0xffffffffFFFF8000; - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_s.h %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extrv_s.h wrong\n"); - - return -1; - } - - /* Clear dsp */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - rs = 0x04; - ach = 0x00; - acl = 0x4321; - result = 0x432; - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv_s.h %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extrv_s.h wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/extrv_w.c b/tests/tcg/mips/mips64-dsp/extrv_w.c deleted file mode 100644 index 8adffb3954..0000000000 --- a/tests/tcg/mips/mips64-dsp/extrv_w.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, ach, acl, dsp; - long long result; - - ach = 0x05; - acl = 0xB4CB; - dsp = 0x07; - rs = 0x03; - result = 0xFFFFFFFFA0001699; - - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv.w %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 1) || (result != rt)) { - printf("extrv.w wrong\n"); - - return -1; - } - - /* Clear dspcontrol */ - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - rs = 4; - ach = 0x01; - acl = 0xB4CB; - result = 0x10000B4C; - __asm - ("wrdsp %1, 0x01\n\t" - "mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "extrv.w %0, $ac1, %2\n\t" - "rddsp %1\n\t" - : "=r"(rt), "+r"(dsp) - : "r"(rs), "r"(ach), "r"(acl) - ); - dsp = (dsp >> 23) & 0x01; - if ((dsp != 0) || (result != rt)) { - printf("extrv.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/head.S b/tests/tcg/mips/mips64-dsp/head.S deleted file mode 100644 index 9a099ae42f..0000000000 --- a/tests/tcg/mips/mips64-dsp/head.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Startup Code for MIPS64 CPU-core - * - */ -.text -.globl _start -.align 4 -_start: - ori $2, $2, 0xffff - sll $2, $2, 16 - ori $2, $2, 0xffff - mtc0 $2, $12, 0 - jal main - -end: - b end diff --git a/tests/tcg/mips/mips64-dsp/insv.c b/tests/tcg/mips/mips64-dsp/insv.c deleted file mode 100644 index fc5696f4c4..0000000000 --- a/tests/tcg/mips/mips64-dsp/insv.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long result; - - /* msb = 10, lsb = 5 */ - dsp = 0x305; - rt = 0x12345678; - rs = 0xffffffff87654321; - result = 0x12345338; - __asm - ("wrdsp %2, 0x03\n\t" - "insv %0, %1\n\t" - : "+r"(rt) - : "r"(rs), "r"(dsp) - ); - if (rt != result) { - printf("insv wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/io.h b/tests/tcg/mips/mips64-dsp/io.h deleted file mode 100644 index b7db61d7c1..0000000000 --- a/tests/tcg/mips/mips64-dsp/io.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _ASM_IO_H -#define _ASM_IO_H -extern int printf(const char *fmt, ...); -extern unsigned long get_ticks(void); - -#define _read(source) \ -({ unsigned long __res; \ - __asm__ __volatile__( \ - "mfc0\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -#define __read(source) \ -({ unsigned long __res; \ - __asm__ __volatile__( \ - "move\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -#endif diff --git a/tests/tcg/mips/mips64-dsp/lbux.c b/tests/tcg/mips/mips64-dsp/lbux.c deleted file mode 100644 index dbdc87bffe..0000000000 --- a/tests/tcg/mips/mips64-dsp/lbux.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long value, rd; - long long *p; - unsigned long long addr, index; - long long result; - - value = 0xBCDEF389; - p = &value; - addr = (unsigned long long)p; - index = 0; - result = value & 0xFF; - __asm - ("lbux %0, %1(%2)\n\t" - : "=r"(rd) - : "r"(index), "r"(addr) - ); - if (rd != result) { - printf("lbux wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/ldx.c b/tests/tcg/mips/mips64-dsp/ldx.c deleted file mode 100644 index 787d9f00b4..0000000000 --- a/tests/tcg/mips/mips64-dsp/ldx.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long value, rd; - long long *p; - unsigned long long addr, index; - long long result; - - value = 0xBCDEF389; - p = &value; - addr = (unsigned long long)p; - index = 0; - result = 0xBCDEF389; - __asm - ("ldx %0, %1(%2)\n\t" - : "=r"(rd) - : "r"(index), "r"(addr) - ); - if (rd != result) { - printf("lwx wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/lhx.c b/tests/tcg/mips/mips64-dsp/lhx.c deleted file mode 100644 index 2020e56866..0000000000 --- a/tests/tcg/mips/mips64-dsp/lhx.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long value, rd; - long long *p; - unsigned long long addr, index; - long long result; - - value = 0xBCDEF389; - p = &value; - addr = (unsigned long long)p; - index = 0; - result = 0xFFFFFFFFFFFFF389; - __asm - ("lhx %0, %1(%2)\n\t" - : "=r"(rd) - : "r"(index), "r"(addr) - ); - if (rd != result) { - printf("lhx wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/lwx.c b/tests/tcg/mips/mips64-dsp/lwx.c deleted file mode 100644 index 6a81414d65..0000000000 --- a/tests/tcg/mips/mips64-dsp/lwx.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long value, rd; - long long *p; - unsigned long long addr, index; - long long result; - - value = 0xBCDEF389; - p = &value; - addr = (unsigned long long)p; - index = 0; - result = 0xFFFFFFFFBCDEF389; - __asm - ("lwx %0, %1(%2)\n\t" - : "=r"(rd) - : "r"(index), "r"(addr) - ); - if (rd != result) { - printf("lwx wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/madd.c b/tests/tcg/mips/mips64-dsp/madd.c deleted file mode 100644 index de6e44fbc5..0000000000 --- a/tests/tcg/mips/mips64-dsp/madd.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0xB4CB; - rs = 0x01; - rt = 0x01; - resulth = 0x05; - resultl = 0xB4CC; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "madd $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("madd wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maddu.c b/tests/tcg/mips/mips64-dsp/maddu.c deleted file mode 100644 index e9f426a374..0000000000 --- a/tests/tcg/mips/mips64-dsp/maddu.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0xB4CB; - rs = 0x01; - rt = 0x01; - resulth = 0x05; - resultl = 0xB4CC; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "madd $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("maddu wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_l_pwl.c b/tests/tcg/mips/mips64-dsp/maq_s_l_pwl.c deleted file mode 100644 index c196b43537..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_l_pwl.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0xB4CB; - rs = 0x98765432FF060000; - rt = 0xfdeca987CB000000; - resulth = 0x05; - resultl = 0x18278587; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.l.pwl $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("maq_s_l.w.pwl wrong 1\n"); - - return -1; - } - - achi = 0x05; - acli = 0xB4CB; - rs = 0x80000000FF060000; - rt = 0x80000000CB000000; - resulth = 0x05; - resultl = 0xb4ca; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.l.pwl $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("maq_s_l.w.pwl wrong 2\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_l_pwr.c b/tests/tcg/mips/mips64-dsp/maq_s_l_pwr.c deleted file mode 100644 index e2af69fe2c..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_l_pwr.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0xB4CB; - rs = 0x87898765432; - rt = 0x7878fdeca987; - resulth = 0x05; - resultl = 0x18278587; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.l.pwr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("maq_s.w.pwr wrong\n"); - - return -1; - } - - achi = 0x05; - acli = 0xB4CB; - rs = 0x89899980000000; - rt = 0x88780000000; - resulth = 0x05; - resultl = 0xb4ca; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.l.pwr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("maq_s.w.pwr wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_w_phl.c b/tests/tcg/mips/mips64-dsp/maq_s_w_phl.c deleted file mode 100644 index 7dba8746e5..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_w_phl.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long dsp; - long long acho, aclo; - long long resulth, resultl; - long long resdsp; - - achi = 0x05; - acli = 0xB4CB; - rs = 0xFF060000; - rt = 0xCB000000; - resulth = 0x04; - resultl = 0xffffffff947438CB; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.w.phl $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_s.w.phl error\n"); - - return -1; - } - - achi = 0x06; - acli = 0xB4CB; - rs = 0x80000000; - rt = 0x80000000; - resulth = 0x6; - resultl = 0xffffffff8000b4ca; - resdsp = 1; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.w.phl $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo) || - (((dsp >> 17) & 0x01) != resdsp)) { - printf("2 maq_s.w.phl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_w_phr.c b/tests/tcg/mips/mips64-dsp/maq_s_w_phr.c deleted file mode 100644 index 138ee2a691..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_w_phr.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long dsp; - long long acho, aclo; - long long resulth, resultl; - long long resdsp; - - achi = 0x05; - acli = 0xB4CB; - rs = 0xFF06; - rt = 0xCB00; - resulth = 0x04; - resultl = 0xffffffff947438CB; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.w.phr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_s.w.phr error\n"); - - return -1; - } - - achi = 0x06; - acli = 0xB4CB; - rs = 0x8000; - rt = 0x8000; - resulth = 0x6; - resultl = 0xffffffff8000b4ca; - resdsp = 1; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.w.phr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo) || - (((dsp >> 17) & 0x01) != resdsp)) { - printf("2 maq_s.w.phr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_w_qhll.c b/tests/tcg/mips/mips64-dsp/maq_s_w_qhll.c deleted file mode 100644 index 234a0af293..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_w_qhll.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234888899990000; - rt = 0x9876888899990000; - - resulth = 0x05; - resultl = 0x15ae87f5; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.w.qhll $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((resulth != acho) || (resultl != aclo)) { - printf("maq_s.w.qhll wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000888899990000; - rt = 0x8000888899990000; - - resulth = 0x04; - resultl = 0xffffffff80000005; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.w.qhll $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("maq_s.w.qhll wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_w_qhlr.c b/tests/tcg/mips/mips64-dsp/maq_s_w_qhlr.c deleted file mode 100644 index 8768cbaa3d..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_w_qhlr.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234123412340000; - rt = 0x9876987698760000; - - resulth = 0x05; - resultl = 0x15ae87f5; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.w.qhlr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_s.w.qhlr wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000800080000000; - rt = 0x8000800080000000; - - resulth = 0x04; - resultl = 0xffffffff80000005; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.w.qhlr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("2 maq_s.w.qhlr wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_w_qhrl.c b/tests/tcg/mips/mips64-dsp/maq_s_w_qhrl.c deleted file mode 100644 index 5006e2be34..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_w_qhrl.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234888812340000; - rt = 0x9876888898760000; - - resulth = 0x05; - resultl = 0x15ae87f5; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.w.qhrl $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_s.w.qhrl wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8888999980000000; - rt = 0x8888999980000000; - - resulth = 0x04; - resultl = 0xffffffff80000005; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.w.qhrl $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("2 maq_s.w.qhrl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_s_w_qhrr.c b/tests/tcg/mips/mips64-dsp/maq_s_w_qhrr.c deleted file mode 100644 index 1d213a51b5..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_s_w_qhrr.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234888812341234; - rt = 0x9876888898769876; - - resulth = 0x05; - resultl = 0x15ae87f5; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_s.w.qhrr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_s.w.qhrr wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000888899998000; - rt = 0x8000888899998000; - - resulth = 0x04; - resultl = 0xffffffff80000005; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_s.w.qhrr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("2 maq_s.w.qhrr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_sa_w_phl.c b/tests/tcg/mips/mips64-dsp/maq_sa_w_phl.c deleted file mode 100644 index 5530ffbe69..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_sa_w_phl.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long dsp; - long long acho, aclo; - long long resulth, resultl; - long long resdsp; - - achi = 0x05; - acli = 0xB4CB; - rs = 0xFF060000; - rt = 0xCB000000; - resulth = 0xffffffffffffffff; - resultl = 0xffffffff947438cb; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_sa.w.phl $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_sa.w.phl error\n"); - - return -1; - } - - achi = 0x06; - acli = 0xB4CB; - rs = 0x80000000; - rt = 0x80000000; - resulth = 0x00; - resultl = 0x7fffffff; - resdsp = 0x01; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.phl $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo) || - (((dsp >> 17) & 0x01) != 0x01)) { - printf("2 maq_sa.w.phl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_sa_w_phr.c b/tests/tcg/mips/mips64-dsp/maq_sa_w_phr.c deleted file mode 100644 index b611cfa91a..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_sa_w_phr.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long dsp; - long long acho, aclo; - long long resulth, resultl; - long long resdsp; - - achi = 0x05; - acli = 0xB4CB; - rs = 0xFF06; - rt = 0xCB00; - resulth = 0xffffffffffffffff; - resultl = 0xffffffff947438cb; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_sa.w.phr $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_sa.w.phr error\n"); - - return -1; - } - - achi = 0x06; - acli = 0xB4CB; - rs = 0x8000; - rt = 0x8000; - resulth = 0x00; - resultl = 0x7fffffff; - resdsp = 0x01; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.phr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((resulth != acho) || (resultl != aclo) || - (((dsp >> 17) & 0x01) != 0x01)) { - printf("2 maq_sa.w.phr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhll.c b/tests/tcg/mips/mips64-dsp/maq_sa_w_qhll.c deleted file mode 100644 index 136ff2d77b..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhll.c +++ /dev/null @@ -1,62 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234888899990000; - rt = 0x9876888899990000; - - resulth = 0x00; - resultl = 0x15ae87f5; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "maq_sa.w.qhll $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((resulth != acho) || (resultl != aclo)) { - printf("1 maq_sa.w.qhll wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000888899990000; - rt = 0x8000888899990000; - - resulth = 0x00; - resultl = 0x7fffffff; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhll $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("2 maq_sa.w.qhll wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhlr.c b/tests/tcg/mips/mips64-dsp/maq_sa_w_qhlr.c deleted file mode 100644 index dd0ae1cca2..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhlr.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234123412340000; - rt = 0x9876987699990000; - - resulth = 0x0; - resultl = 0x15ae87f5; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhlr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x0) || (resulth != acho) || (resultl != aclo)) { - printf("maq_sa.w.qhlr wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000800099990000; - rt = 0x8000800099990000; - - resulth = 0x00; - resultl = 0x7fffffff; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhlr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("maq_sa.w.qhlr wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhrl.c b/tests/tcg/mips/mips64-dsp/maq_sa_w_qhrl.c deleted file mode 100644 index a3de6f8e2e..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhrl.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234123412340000; - rt = 0x9876987698760000; - - resulth = 0x0; - resultl = 0x15ae87f5; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhrl $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x0) || (resulth != acho) || (resultl != aclo)) { - printf("1 maq_sa.w.qhrl wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000800080000000; - rt = 0x8000800080000000; - - resulth = 0x00; - resultl = 0x7fffffff; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhrl $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("2 maq_sa.w.qhrl wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhrr.c b/tests/tcg/mips/mips64-dsp/maq_sa_w_qhrr.c deleted file mode 100644 index f02173736a..0000000000 --- a/tests/tcg/mips/mips64-dsp/maq_sa_w_qhrr.c +++ /dev/null @@ -1,64 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs, dsp; - long long achi, acli; - long long acho, aclo; - long long resulth, resultl; - - achi = 0x05; - acli = 0x05; - - rs = 0x1234123412341234; - rt = 0x9876987698769876; - - resulth = 0x0; - resultl = 0x15ae87f5; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhrr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x0) || (resulth != acho) || (resultl != aclo)) { - printf("1 maq_sa.w.qhrr wrong\n"); - - return -1; - } - - - achi = 0x04; - acli = 0x06; - rs = 0x8000800080008000; - rt = 0x8000800080008000; - - resulth = 0x00; - resultl = 0x7fffffff; - - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "maq_sa.w.qhrr $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (resulth != acho) || (resultl != aclo)) { - printf("2 maq_sa.w.qhrr wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mfhi.c b/tests/tcg/mips/mips64-dsp/mfhi.c deleted file mode 100644 index ee915f796e..0000000000 --- a/tests/tcg/mips/mips64-dsp/mfhi.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long achi, acho; - long long result; - - achi = 0x004433; - result = 0x004433; - - __asm - ("mthi %1, $ac1\n\t" - "mfhi %0, $ac1\n\t" - : "=r"(acho) - : "r"(achi) - ); - if (result != acho) { - printf("mfhi wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mflo.c b/tests/tcg/mips/mips64-dsp/mflo.c deleted file mode 100644 index cdc646b5fc..0000000000 --- a/tests/tcg/mips/mips64-dsp/mflo.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long acli, aclo; - long long result; - - acli = 0x004433; - result = 0x004433; - - __asm - ("mtlo %1, $ac1\n\t" - "mflo %0, $ac1\n\t" - : "=r"(aclo) - : "r"(acli) - ); - if (result != aclo) { - printf("mflo wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mips_boot.lds b/tests/tcg/mips/mips64-dsp/mips_boot.lds deleted file mode 100644 index bd7c0c0f3f..0000000000 --- a/tests/tcg/mips/mips64-dsp/mips_boot.lds +++ /dev/null @@ -1,31 +0,0 @@ -OUTPUT_ARCH(mips) -SECTIONS -{ - . = 0xffffffff80100000; - . = ALIGN((1 << 13)); - .text : - { - *(.text) - *(.rodata) - *(.rodata.*) - } - - __init_begin = .; - . = ALIGN((1 << 12)); - .init.text : AT(ADDR(.init.text) - 0) - { - *(.init.text) - } - .init.data : AT(ADDR(.init.data) - 0) - { - *(.init.data) - } - . = ALIGN((1 << 12)); - __init_end = .; - - . = ALIGN((1 << 13)); - .data : - { - *(.data) - } -} diff --git a/tests/tcg/mips/mips64-dsp/modsub.c b/tests/tcg/mips/mips64-dsp/modsub.c deleted file mode 100644 index 2c91cb4c59..0000000000 --- a/tests/tcg/mips/mips64-dsp/modsub.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0xFFFFFFFF; - rt = 0x000000FF; - result = 0xFFFFFF00; - __asm - ("modsub %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("modsub wrong\n"); - - return -1; - } - - rs = 0x00000000; - rt = 0x00CD1FFF; - result = 0x0000CD1F; - __asm - ("modsub %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("modsub wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/msub.c b/tests/tcg/mips/mips64-dsp/msub.c deleted file mode 100644 index 75066b5916..0000000000 --- a/tests/tcg/mips/mips64-dsp/msub.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long achi, acli, rs, rt; - long long acho, aclo; - long long resulth, resultl; - - rs = 0x00BBAACC; - rt = 0x0B1C3D2F; - achi = 0x00004433; - acli = 0xFFCC0011; - resulth = 0xFFFFFFFFFFF81F29; - resultl = 0xFFFFFFFFB355089D; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "msub $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resulth) || (aclo != resultl)) { - printf("msub wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/msubu.c b/tests/tcg/mips/mips64-dsp/msubu.c deleted file mode 100644 index 55f8ae046f..0000000000 --- a/tests/tcg/mips/mips64-dsp/msubu.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long achi, acli, rs, rt; - long long acho, aclo; - long long resulth, resultl; - - rs = 0x00BBAACC; - rt = 0x0B1C3D2F; - achi = 0x00004433; - acli = 0xFFCC0011; - resulth = 0xFFFFFFFFFFF81F29; - resultl = 0xFFFFFFFFB355089D; - - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "msubu $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resulth) || (aclo != resultl)) { - printf("msubu wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mthi.c b/tests/tcg/mips/mips64-dsp/mthi.c deleted file mode 100644 index 857005139b..0000000000 --- a/tests/tcg/mips/mips64-dsp/mthi.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long achi, acho; - long long result; - - achi = 0x004433; - result = 0x004433; - - __asm - ("mthi %1, $ac1\n\t" - "mfhi %0, $ac1\n\t" - : "=r"(acho) - : "r"(achi) - ); - if (result != acho) { - printf("mthi wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mthlip.c b/tests/tcg/mips/mips64-dsp/mthlip.c deleted file mode 100644 index 957cd426f0..0000000000 --- a/tests/tcg/mips/mips64-dsp/mthlip.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, ach, acl, dsp; - long long result, resulth, resultl; - - dsp = 0x07; - ach = 0x05; - acl = 0xB4CB; - rs = 0x00FFBBAA; - resulth = 0xB4CB; - resultl = 0x00FFBBAA; - result = 0x27; - - __asm - ("wrdsp %0, 0x01\n\t" - "mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "mthlip %3, $ac1\n\t" - "mfhi %1, $ac1\n\t" - "mflo %2, $ac1\n\t" - "rddsp %0\n\t" - : "+r"(dsp), "+r"(ach), "+r"(acl) - : "r"(rs) - ); - dsp = dsp & 0x3F; - if ((dsp != result) || (ach != resulth) || (acl != resultl)) { - printf("mthlip wrong\n"); - - return -1; - } - - dsp = 0x3f; - ach = 0x05; - acl = 0xB4CB; - rs = 0x00FFBBAA; - resulth = 0xB4CB; - resultl = 0x00FFBBAA; - result = 0x3f; - - __asm - ("wrdsp %0, 0x01\n\t" - "mthi %1, $ac1\n\t" - "mtlo %2, $ac1\n\t" - "mthlip %3, $ac1\n\t" - "mfhi %1, $ac1\n\t" - "mflo %2, $ac1\n\t" - "rddsp %0\n\t" - : "+r"(dsp), "+r"(ach), "+r"(acl) - : "r"(rs) - ); - dsp = dsp & 0x3F; - if ((dsp != result) || (ach != resulth) || (acl != resultl)) { - printf("mthlip wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mtlo.c b/tests/tcg/mips/mips64-dsp/mtlo.c deleted file mode 100644 index 304fffbe7c..0000000000 --- a/tests/tcg/mips/mips64-dsp/mtlo.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long acli, aclo; - long long result; - - acli = 0x004433; - result = 0x004433; - - __asm - ("mthi %1, $ac1\n\t" - "mfhi %0, $ac1\n\t" - : "=r"(aclo) - : "r"(acli) - ); - if (result != aclo) { - printf("mtlo wrong\n"); - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleq_s_pw_qhl.c b/tests/tcg/mips/mips64-dsp/muleq_s_pw_qhl.c deleted file mode 100644 index 6c68d45afe..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleq_s_pw_qhl.c +++ /dev/null @@ -1,56 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - - rd = 0; - rs = 0x45BCFFFF12345678; - rt = 0x98529AD287654321; - result = 0x52fbec7035a2ca5c; - - __asm - ("muleq_s.pw.qhl %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (result != rd) { - printf("1 muleq_s.pw.qhl error\n"); - - return -1; - } - - rd = 0; - rs = 0x45BC800012345678; - rt = 0x9852800087654321; - result = 0x52fbec707FFFFFFF; - - __asm - ("muleq_s.pw.qhl %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (result != rd) { - printf("2 muleq_s.pw.qhl error\n"); - - return -1; - } - - rd = 0; - __asm - ("rddsp %0\n\t" - : "=r"(rd) - ); - rd = rd >> 21; - rd = rd & 0x1; - - if (rd != 1) { - printf("3 muleq_s.pw.qhl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleq_s_pw_qhr.c b/tests/tcg/mips/mips64-dsp/muleq_s_pw_qhr.c deleted file mode 100644 index fa8b41fd39..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleq_s_pw_qhr.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rd = 0; - rs = 0x1234567845BCFFFF; - rt = 0x8765432198529AD2; - result = 0x52fbec7035a2ca5c; - - __asm - ("muleq_s.pw.qhr %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (result != rd) { - printf("1 muleq_s.pw.qhr error\n"); - - return -1; - } - - rd = 0; - rs = 0x1234567845BC8000; - rt = 0x8765432198528000; - result = 0x52fbec707FFFFFFF; - - __asm - ("muleq_s.pw.qhr %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (result != rd) { - printf("2 muleq_s.pw.qhr error\n"); - - return -1; - } - - rd = 0; - __asm - ("rddsp %0\n\t" - : "=r"(rd) - ); - rd = rd >> 21; - rd = rd & 0x1; - - if (rd != 1) { - printf("3 muleq_s.pw.qhr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleq_s_w_phl.c b/tests/tcg/mips/mips64-dsp/muleq_s_w_phl.c deleted file mode 100644 index 997a9f64d9..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleq_s_w_phl.c +++ /dev/null @@ -1,46 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x80009988; - rt = 0x80009988; - result = 0x7FFFFFFF; - resultdsp = 1; - - __asm - ("muleq_s.w.phl %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("muleq_s.w.phl wrong\n"); - - return -1; - } - - rs = 0x12343322; - rt = 0x43213322; - result = 0x98be968; - resultdsp = 1; - - __asm - ("muleq_s.w.phl %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("muleq_s.w.phl wrong\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/muleq_s_w_phr.c b/tests/tcg/mips/mips64-dsp/muleq_s_w_phr.c deleted file mode 100644 index 0e594794dd..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleq_s_w_phr.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x8000; - rt = 0x8000; - result = 0x7FFFFFFF; - resultdsp = 1; - - __asm - ("muleq_s.w.phr %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("muleq_s.w.phr wrong\n"); - - return -1; - } - - rs = 0x1234; - rt = 0x4321; - result = 0x98be968; - resultdsp = 1; - - __asm - ("muleq_s.w.phr %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("muleq_s.w.phr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleu_s_ph_qbl.c b/tests/tcg/mips/mips64-dsp/muleu_s_ph_qbl.c deleted file mode 100644 index 2f444c9f85..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleu_s_ph_qbl.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x80001234; - rt = 0x80004321; - result = 0xFFFFFFFFFFFF0000; - resultdsp = 1; - - __asm - ("muleu_s.ph.qbl %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("muleu_s.ph.qbl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleu_s_ph_qbr.c b/tests/tcg/mips/mips64-dsp/muleu_s_ph_qbr.c deleted file mode 100644 index 8bd0e9942c..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleu_s_ph_qbr.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x8000; - rt = 0x80004321; - result = 0xFFFFFFFFFFFF0000; - resultdsp = 1; - - __asm - ("muleu_s.ph.qbr %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("muleu_s.ph.qbr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleu_s_qh_obl.c b/tests/tcg/mips/mips64-dsp/muleu_s_qh_obl.c deleted file mode 100644 index db0d386e88..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleu_s_qh_obl.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long resdsp, result; - - rd = 0; - rs = 0x1234567802020202; - rt = 0x0034432112344321; - result = 0x03A8FFFFFFFFFFFF; - resdsp = 0x01; - - __asm - ("muleu_s.qh.obl %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (resdsp != dsp)) { - printf("muleu_s.qh.obl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/muleu_s_qh_obr.c b/tests/tcg/mips/mips64-dsp/muleu_s_qh_obr.c deleted file mode 100644 index 52ed9c095a..0000000000 --- a/tests/tcg/mips/mips64-dsp/muleu_s_qh_obr.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long resdsp, result; - - rd = 0; - rs = 0x0202020212345678; - - rt = 0x0034432112344321; - result = 0x03A8FFFFFFFFFFFF; - resdsp = 0x01; - - __asm - ("muleu_s.qh.obr %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (resdsp != dsp)) { - printf("muleu_s.qh.obr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mulq_rs_ph.c b/tests/tcg/mips/mips64-dsp/mulq_rs_ph.c deleted file mode 100644 index fd6233d4df..0000000000 --- a/tests/tcg/mips/mips64-dsp/mulq_rs_ph.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x80001234; - rt = 0x80004321; - result = 0x7FFF098C; - resultdsp = 1; - - __asm - ("mulq_rs.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if ((rd != result) || (dsp != resultdsp)) { - printf("mulq_rs.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mulq_rs_qh.c b/tests/tcg/mips/mips64-dsp/mulq_rs_qh.c deleted file mode 100644 index 7863c05912..0000000000 --- a/tests/tcg/mips/mips64-dsp/mulq_rs_qh.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dsp, dspresult; - rt = 0x80003698CE8F9201; - rs = 0x800034634BCDE321; - result = 0x7fff16587a530313; - - dspresult = 0x01; - - __asm - ("mulq_rs.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - if (rd != result) { - printf("mulq_rs.qh error\n"); - - return -1; - } - - dsp = (dsp >> 21) & 0x01; - if (dsp != dspresult) { - printf("mulq_rs.qh DSPControl Reg ouflag error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mulsaq_s_l_pw.c b/tests/tcg/mips/mips64-dsp/mulsaq_s_l_pw.c deleted file mode 100644 index 02548f85cf..0000000000 --- a/tests/tcg/mips/mips64-dsp/mulsaq_s_l_pw.c +++ /dev/null @@ -1,59 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long achi, acli; - long long acho, aclo; - long long resl, resh; - - achi = 0x4; - acli = 0x4; - - rs = 0x1234567887654321; - rt = 0x8765432112345678; - - resh = 0x4; - resl = 0x4; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "mulsaq_s.l.pw $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 mulsaq_s.l.pw wrong\n"); - - return -1; - } - - achi = 0x4; - acli = 0x4; - - rs = 0x8000000087654321; - rt = 0x8000000012345678; - - resh = 0x4; - resl = 0x1e8ee513; - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "mulsaq_s.l.pw $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (acho != resh) || (aclo != resl)) { - printf("2 mulsaq_s.l.pw wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mulsaq_s_w_qh.c b/tests/tcg/mips/mips64-dsp/mulsaq_s_w_qh.c deleted file mode 100644 index 92d7a0b4f2..0000000000 --- a/tests/tcg/mips/mips64-dsp/mulsaq_s_w_qh.c +++ /dev/null @@ -1,57 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long achi, acli; - long long acho, aclo; - long long resl, resh; - - achi = 0x4; - acli = 0x4; - - rs = 0x5678123443218765; - rt = 0x4321876556781234; - - resh = 0x4; - resl = 0x342fcbd4; - __asm - ("mthi %2, $ac1\n\t" - "mtlo %3, $ac1\n\t" - "mulsaq_s.w.qh $ac1, %4, %5\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 mulsaq_s.w.qh wrong\n"); - return -1; - } - - achi = 0x4; - acli = 0x4; - - rs = 0x8000800087654321; - rt = 0x8000800012345678; - - resh = 0x3; - resl = 0xffffffffe5e81a1c; - __asm - ("mthi %3, $ac1\n\t" - "mtlo %4, $ac1\n\t" - "mulsaq_s.w.qh $ac1, %5, %6\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "=r"(acho), "=r"(aclo), "=r"(dsp) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x1; - if ((dsp != 0x1) || (acho != resh) || (aclo != resl)) { - printf("2 mulsaq_s.w.qh wrong\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/mult.c b/tests/tcg/mips/mips64-dsp/mult.c deleted file mode 100644 index 4a294d1a0c..0000000000 --- a/tests/tcg/mips/mips64-dsp/mult.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, ach, acl; - long long result, resulth, resultl; - - rs = 0x00FFBBAA; - rt = 0x4B231000; - resulth = 0x4b0f01; - resultl = 0x71f8a000; - __asm - ("mult $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(ach), "=r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("mult wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/multu.c b/tests/tcg/mips/mips64-dsp/multu.c deleted file mode 100644 index 21a8a7c77d..0000000000 --- a/tests/tcg/mips/mips64-dsp/multu.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt, ach, acl; - long long result, resulth, resultl; - - rs = 0x00FFBBAA; - rt = 0x4B231000; - resulth = 0x4b0f01; - resultl = 0x71f8a000; - __asm - ("multu $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "=r"(ach), "=r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("multu wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/packrl_ph.c b/tests/tcg/mips/mips64-dsp/packrl_ph.c deleted file mode 100644 index 3722b0ae6c..0000000000 --- a/tests/tcg/mips/mips64-dsp/packrl_ph.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x56788765; - - __asm - ("packrl.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("packrl.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/packrl_pw.c b/tests/tcg/mips/mips64-dsp/packrl_pw.c deleted file mode 100644 index 7807418834..0000000000 --- a/tests/tcg/mips/mips64-dsp/packrl_pw.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long res; - - rs = 0x1234567887654321; - rt = 0xabcdef9812345678; - - res = 0x87654321abcdef98; - - __asm - ("packrl.pw %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != res) { - printf("packrl.pw error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/pick_ob.c b/tests/tcg/mips/mips64-dsp/pick_ob.c deleted file mode 100644 index 160049ffd4..0000000000 --- a/tests/tcg/mips/mips64-dsp/pick_ob.c +++ /dev/null @@ -1,66 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long res; - - dsp = 0xff000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1234567812345678; - - __asm - ("wrdsp %1, 0x10\n\t" - "pick.ob %0, %2, %3\n\t" - : "=r"(rd) - : "r"(dsp), "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("1 pick.ob error\n"); - return -1; - } - - dsp = 0x00000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x8765432187654321; - - __asm - ("wrdsp %1, 0x10\n\t" - "pick.ob %0, %2, %3\n\t" - : "=r"(rd) - : "r"(dsp), "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("2 pick.ob error\n"); - return -1; - } - - dsp = 0x34000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x8765567887344321; - - __asm - ("wrdsp %1, 0x10\n\t" - "pick.ob %0, %2, %3\n\t" - : "=r"(rd) - : "r"(dsp), "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("3 pick.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/pick_ph.c b/tests/tcg/mips/mips64-dsp/pick_ph.c deleted file mode 100644 index 8800c14d1e..0000000000 --- a/tests/tcg/mips/mips64-dsp/pick_ph.c +++ /dev/null @@ -1,60 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - dsp = 0x0A000000; - result = 0x12344321; - - __asm - ("wrdsp %3, 0x10\n\t" - "pick.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dsp) - ); - if (rd != result) { - printf("1 pick.ph wrong\n"); - - return -1; - } - - rs = 0x12345678; - rt = 0x87654321; - dsp = 0x03000000; - result = 0x12345678; - - __asm - ("wrdsp %3, 0x10\n\t" - "pick.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dsp) - ); - if (rd != result) { - printf("2 pick.ph wrong\n"); - - return -1; - } - - rs = 0x12345678; - rt = 0x87654321; - dsp = 0x00000000; - result = 0xffffffff87654321; - - __asm - ("wrdsp %3, 0x10\n\t" - "pick.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dsp) - ); - if (rd != result) { - printf("3 pick.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/pick_pw.c b/tests/tcg/mips/mips64-dsp/pick_pw.c deleted file mode 100644 index 24d80f551a..0000000000 --- a/tests/tcg/mips/mips64-dsp/pick_pw.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long res; - dsp = 0xff000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1234567812345678; - - __asm - ("wrdsp %1, 0x10\n\t" - "wrdsp %1\n\t" - "pick.pw %0, %2, %3\n\t" - : "=r"(rd), "+r"(dsp) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("pick.pw error\n"); - return -1; - } - - dsp = 0x00000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x8765432187654321; - - __asm - ("wrdsp %1, 0x10\n\t" - "wrdsp %1\n\t" - "pick.pw %0, %2, %3\n\t" - : "=r"(rd), "+r"(dsp) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("pick.pw error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/pick_qb.c b/tests/tcg/mips/mips64-dsp/pick_qb.c deleted file mode 100644 index 0d5de9db9e..0000000000 --- a/tests/tcg/mips/mips64-dsp/pick_qb.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - dsp = 0x0f000000; - result = 0x12345678; - - __asm - ("wrdsp %3, 0x10\n\t" - "pick.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dsp) - ); - if (rd != result) { - printf("pick.qb wrong\n"); - - return -1; - } - - rs = 0x12345678; - rt = 0x87654321; - dsp = 0x00000000; - result = 0xffffffff87654321; - - __asm - ("wrdsp %3, 0x10\n\t" - "pick.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt), "r"(dsp) - ); - if (rd != result) { - printf("pick.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/pick_qh.c b/tests/tcg/mips/mips64-dsp/pick_qh.c deleted file mode 100644 index aa2e2938af..0000000000 --- a/tests/tcg/mips/mips64-dsp/pick_qh.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long res; - dsp = 0xff000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1234567812345678; - - __asm - ("wrdsp %1, 0x10\n\t" - "wrdsp %1\n\t" - "pick.qh %0, %2, %3\n\t" - : "=r"(rd), "+r"(dsp) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("pick.qh error\n"); - return -1; - } - - dsp = 0x00000000; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x8765432187654321; - - __asm - ("wrdsp %1, 0x10\n\t" - "wrdsp %1\n\t" - "pick.qh %0, %2, %3\n\t" - : "=r"(rd), "+r"(dsp) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("pick.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceq_l_pwl.c b/tests/tcg/mips/mips64-dsp/preceq_l_pwl.c deleted file mode 100644 index 64551007cf..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_l_pwl.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - rt = 0xFFFFFFFF11111111; - result = 0xFFFFFFFF00000000; - - __asm - ("preceq.l.pwl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceq.l.pwl wrong\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/preceq_l_pwr.c b/tests/tcg/mips/mips64-dsp/preceq_l_pwr.c deleted file mode 100644 index 1e05339d23..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_l_pwr.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - rt = 0xFFFFFFFF11111111; - result = 0x1111111100000000; - - __asm - ("preceq.l.pwl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceq.l.pwr wrong\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/preceq_pw_qhl.c b/tests/tcg/mips/mips64-dsp/preceq_pw_qhl.c deleted file mode 100644 index f44b940492..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_pw_qhl.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rt = 0x0123456789ABCDEF; - result = 0x0123000045670000; - - __asm - ("preceq.pw.qhl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceq.pw.qhl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceq_pw_qhla.c b/tests/tcg/mips/mips64-dsp/preceq_pw_qhla.c deleted file mode 100644 index f0f78f43c5..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_pw_qhla.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0x123456789ABCDEF0; - result = 0x123400009ABC0000; - - __asm - ("preceq.pw.qhla %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceq.pw.qhla error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceq_pw_qhr.c b/tests/tcg/mips/mips64-dsp/preceq_pw_qhr.c deleted file mode 100644 index 709d4f9004..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_pw_qhr.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rt = 0x0123456789ABCDEF; - result = 0x89AB0000CDEF0000; - - __asm - ("preceq.pw.qhr %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceq.pw.qhr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceq_pw_qhra.c b/tests/tcg/mips/mips64-dsp/preceq_pw_qhra.c deleted file mode 100644 index 4d071ec863..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_pw_qhra.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0x123456789ABCDEF0; - result = 0x56780000DEF00000; - - __asm - ("preceq.pw.qhra %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceq.pw.qhra error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceq_w_phl.c b/tests/tcg/mips/mips64-dsp/preceq_w_phl.c deleted file mode 100644 index 4ed3fc030c..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_w_phl.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0xFFFFFFFF87650000; - - __asm - ("preceq.w.phl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceq.w.phl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceq_w_phr.c b/tests/tcg/mips/mips64-dsp/preceq_w_phr.c deleted file mode 100644 index e2ea0933b2..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceq_w_phr.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x43210000; - - __asm - ("preceq.w.phr %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceq.w.phr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_ph_qbl.c b/tests/tcg/mips/mips64-dsp/precequ_ph_qbl.c deleted file mode 100644 index 17b73311dc..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_ph_qbl.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x43803280; - - __asm - ("precequ.ph.qbl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("precequ.ph.qbl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_ph_qbla.c b/tests/tcg/mips/mips64-dsp/precequ_ph_qbla.c deleted file mode 100644 index 15e94946b1..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_ph_qbla.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x43802180; - - __asm - ("precequ.ph.qbla %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("precequ.ph.qbla wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_ph_qbr.c b/tests/tcg/mips/mips64-dsp/precequ_ph_qbr.c deleted file mode 100644 index 495368ce0b..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_ph_qbr.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x21801080; - - __asm - ("precequ.ph.qbr %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("precequ.ph.qbr wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_ph_qbra.c b/tests/tcg/mips/mips64-dsp/precequ_ph_qbra.c deleted file mode 100644 index 7c6636975c..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_ph_qbra.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x32801080; - - __asm - ("precequ.ph.qbra %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("precequ.ph.qbra wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_qh_obl.c b/tests/tcg/mips/mips64-dsp/precequ_qh_obl.c deleted file mode 100644 index 176d2365a8..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_qh_obl.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rt = 0x123456789ABCDEF0; - result = 0x09001A002B003C00; - - __asm - ("precequ.qh.obla %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("precequ.qh.obla error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_qh_obla.c b/tests/tcg/mips/mips64-dsp/precequ_qh_obla.c deleted file mode 100644 index 93a36a4855..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_qh_obla.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rt = 0x123456789ABCDEF0; - result = 0x09002B004D006F00; - - __asm - ("precequ.qh.obla %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("precequ.qh.obla error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precequ_qh_obr.c b/tests/tcg/mips/mips64-dsp/precequ_qh_obr.c deleted file mode 100644 index 121473083b..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_qh_obr.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0x123456789ABCDEF0; - result = 0x4D005E006F007000; - - __asm - ("precequ.qh.obr %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("precequ.qh.obr error\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/precequ_qh_obra.c b/tests/tcg/mips/mips64-dsp/precequ_qh_obra.c deleted file mode 100644 index 3aa0e096ca..0000000000 --- a/tests/tcg/mips/mips64-dsp/precequ_qh_obra.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0x123456789ABCDEF0; - result = 0x1A003C005D007000; - - __asm - ("precequ.qh.obra %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("precequ.qh.obra error\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/preceu_ph_qbl.c b/tests/tcg/mips/mips64-dsp/preceu_ph_qbl.c deleted file mode 100644 index 81f7917c19..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_ph_qbl.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x00870065; - - __asm - ("preceu.ph.qbl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceu.ph.qbl wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_ph_qbla.c b/tests/tcg/mips/mips64-dsp/preceu_ph_qbla.c deleted file mode 100644 index 38cf6a62b9..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_ph_qbla.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x00870043; - - __asm - ("preceu.ph.qbla %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceu.ph.qbla wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_ph_qbr.c b/tests/tcg/mips/mips64-dsp/preceu_ph_qbr.c deleted file mode 100644 index 70c32b6716..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_ph_qbr.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x00430021; - - __asm - ("preceu.ph.qbr %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceu.ph.qbr wrong"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_ph_qbra.c b/tests/tcg/mips/mips64-dsp/preceu_ph_qbra.c deleted file mode 100644 index c6638aaafd..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_ph_qbra.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0x00650021; - - __asm - ("preceu.ph.qbra %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (result != rd) { - printf("preceu.ph.qbra wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_qh_obl.c b/tests/tcg/mips/mips64-dsp/preceu_qh_obl.c deleted file mode 100644 index 63f9373b08..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_qh_obl.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rt = 0x123456789ABCDEF0; - result = 0x0012003400560078; - - __asm - ("preceu.qh.obl %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceu.qh.obl error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_qh_obla.c b/tests/tcg/mips/mips64-dsp/preceu_qh_obla.c deleted file mode 100644 index 5fb65e4049..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_qh_obla.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rt = 0x123456789ABCDEF0; - result = 0x00120056009A00DE; - - __asm - ("preceu.qh.obla %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceu.qh.obla error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_qh_obr.c b/tests/tcg/mips/mips64-dsp/preceu_qh_obr.c deleted file mode 100644 index 9af3b6372e..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_qh_obr.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0x123456789ABCDEF0; - result = 0x009A00BC00DE00F0; - - __asm - ("preceu.qh.obr %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceu.qh.obr error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/preceu_qh_obra.c b/tests/tcg/mips/mips64-dsp/preceu_qh_obra.c deleted file mode 100644 index fd04083371..0000000000 --- a/tests/tcg/mips/mips64-dsp/preceu_qh_obra.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0x123456789ABCDEF0; - result = 0x0034007800BC00F0; - - __asm - ("preceu.qh.obra %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("preceu.qh.obra error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precr_ob_qh.c b/tests/tcg/mips/mips64-dsp/precr_ob_qh.c deleted file mode 100644 index ce2da79af8..0000000000 --- a/tests/tcg/mips/mips64-dsp/precr_ob_qh.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long res; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x3478347865216521; - - __asm - ("precr.ob.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("precr.ob.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precr_sra_qh_pw.c b/tests/tcg/mips/mips64-dsp/precr_sra_qh_pw.c deleted file mode 100644 index 8bb16de9af..0000000000 --- a/tests/tcg/mips/mips64-dsp/precr_sra_qh_pw.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long res; - - rt = 0x8765432187654321; - rs = 0x1234567812345678; - - res = 0x4321432156785678; - - __asm - ("precr_sra.qh.pw %0, %1, 0x0\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("precr_sra.qh.pw error\n"); - return -1; - } - - rt = 0x8765432187654321; - rs = 0x1234567812345678; - - res = 0x5432543245674567; - - __asm - ("precr_sra.qh.pw %0, %1, 0x4\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("precr_sra.qh.pw error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precr_sra_r_qh_pw.c b/tests/tcg/mips/mips64-dsp/precr_sra_r_qh_pw.c deleted file mode 100644 index 734ac322e7..0000000000 --- a/tests/tcg/mips/mips64-dsp/precr_sra_r_qh_pw.c +++ /dev/null @@ -1,40 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long res; - - rt = 0x8765432187654321; - rs = 0x1234567812345678; - - res = 0x4321432156785678; - - __asm - ("precr_sra_r.qh.pw %0, %1, 0x0\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("precr_sra_r.qh.pw error\n"); - return -1; - } - - rt = 0x8765432187654321; - rs = 0x1234567812345678; - - res = 0x5432543245684568; - - __asm - ("precr_sra_r.qh.pw %0, %1, 0x4\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("precr_sra_r.qh.pw error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_ob_qh.c b/tests/tcg/mips/mips64-dsp/precrq_ob_qh.c deleted file mode 100644 index 4f61b1709e..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_ob_qh.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long res; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1256125687438743; - - __asm - ("precrq.ob.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("precrq.ob.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_ph_w.c b/tests/tcg/mips/mips64-dsp/precrq_ph_w.c deleted file mode 100644 index f0946abdcb..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_ph_w.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x12348765; - - __asm - ("precrq.ph.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("precrq.ph.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_pw_l.c b/tests/tcg/mips/mips64-dsp/precrq_pw_l.c deleted file mode 100644 index da957c0743..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_pw_l.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long res; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1234567887654321; - - __asm - ("precrq.pw.l %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("precrq.pw.l error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_qb_ph.c b/tests/tcg/mips/mips64-dsp/precrq_qb_ph.c deleted file mode 100644 index f417c9f342..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_qb_ph.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x12568743; - - __asm - ("precrq.qb.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("precrq.qb.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_qh_pw.c b/tests/tcg/mips/mips64-dsp/precrq_qh_pw.c deleted file mode 100644 index 4a4ffef8ea..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_qh_pw.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long res; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1234123487658765; - - __asm - ("precrq.qh.pw %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("precrq.qh.pw error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_rs_ph_w.c b/tests/tcg/mips/mips64-dsp/precrq_rs_ph_w.c deleted file mode 100644 index 61da3331fb..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_rs_ph_w.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x12348765; - - __asm - ("precrq_rs.ph.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("1 precrq_rs.ph.w wrong\n"); - - return -1; - } - - rs = 0x7fffC678; - rt = 0x865432A0; - result = 0x7fff8654; - - __asm - ("precrq_rs.ph.w %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((result != rd) || (((dsp >> 22) & 0x01) != 1)) { - printf("2 precrq_rs.ph.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrq_rs_qh_pw.c b/tests/tcg/mips/mips64-dsp/precrq_rs_qh_pw.c deleted file mode 100644 index ac78728abf..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrq_rs_qh_pw.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long res; - - rs = 0x1234567812345678; - rt = 0x8765432187654321; - - res = 0x1234123487658765; - - __asm - ("precrq_rs.qh.pw %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("precrq_rs.qh.pw error\n"); - return -1; - } - - rs = 0x7fffC67812345678; - rt = 0x8765432187654321; - - res = 0x7fff123487658765; - - __asm - ("precrq_rs.qh.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - - if (rd != res) { - printf("precrq_rs.qh.pw error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrqu_s_ob_qh.c b/tests/tcg/mips/mips64-dsp/precrqu_s_ob_qh.c deleted file mode 100644 index e27c36b7fd..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrqu_s_ob_qh.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long res, resdsp; - - rs = 0x7fff567812345678; - rt = 0x8765432187654321; - - res = 0xffac24ac00860086; - resdsp = 0x1; - - __asm - ("precrqu_s.ob.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 22) & 0x1; - if ((rd != res) || (dsp != resdsp)) { - printf("precrq_s.ob.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/precrqu_s_qb_ph.c b/tests/tcg/mips/mips64-dsp/precrqu_s_qb_ph.c deleted file mode 100644 index cb1fee4508..0000000000 --- a/tests/tcg/mips/mips64-dsp/precrqu_s_qb_ph.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x12345678; - rt = 0x87657fff; - result = 0x24AC00FF; - - __asm - ("precrqu_s.qb.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((result != rd) || (((dsp >> 22) & 0x01) != 0x01)) { - printf("precrqu_s.qb.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/prependd.c b/tests/tcg/mips/mips64-dsp/prependd.c deleted file mode 100644 index b4208c2dab..0000000000 --- a/tests/tcg/mips/mips64-dsp/prependd.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long res; - rt = 0x1234567887654321; - rs = 0xabcd1234abcd8765; - - res = 0x1234567887654321; - __asm - ("prependd %0, %1, 0x0\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("prependd error\n"); - return -1; - } - - rt = 0x1234567887654321; - rs = 0xabcd1234abcd8765; - - res = 0xd876512345678876; - __asm - ("prependd %0, %1, 0x4\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("prependd error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/prependw.c b/tests/tcg/mips/mips64-dsp/prependw.c deleted file mode 100644 index d91bd2023c..0000000000 --- a/tests/tcg/mips/mips64-dsp/prependw.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long res; - rt = 0x1234567887654321; - rs = 0xabcd1234abcd8765; - - res = 0x1234567887654321; - __asm - ("prependw %0, %1, 0x0\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("prependw error\n"); - return -1; - } - - rt = 0x1234567887654321; - rs = 0xabcd1234abcd8765; - - res = 0x5123456788765432; - __asm - ("prependw %0, %1, 0x4\n\t" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("prependw error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/printf.c b/tests/tcg/mips/mips64-dsp/printf.c deleted file mode 100644 index cf8676d390..0000000000 --- a/tests/tcg/mips/mips64-dsp/printf.c +++ /dev/null @@ -1,266 +0,0 @@ - -typedef unsigned long va_list; - -#define ACC 4 -#define __read(source) \ -({ va_list __res; \ - __asm__ __volatile__( \ - "move\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -enum format_type { - FORMAT_TYPE_NONE, - FORMAT_TYPE_HEX, - FORMAT_TYPE_ULONG, - FORMAT_TYPE_FLOAT -}; - -struct printf_spec { - char type; -}; - -static int format_decode(char *fmt, struct printf_spec *spec) -{ - char *start = fmt; - - for (; *fmt ; ++fmt) { - if (*fmt == '%') { - break; - } - } - - switch (*++fmt) { - case 'x': - spec->type = FORMAT_TYPE_HEX; - break; - - case 'd': - spec->type = FORMAT_TYPE_ULONG; - break; - - case 'f': - spec->type = FORMAT_TYPE_FLOAT; - break; - - default: - spec->type = FORMAT_TYPE_NONE; - } - - return ++fmt - start; -} - -void *memcpy(void *dest, void *src, int n) -{ - int i; - char *s = src; - char *d = dest; - - for (i = 0; i < n; i++) { - d[i] = s[i]; - } - return dest; -} - -char *number(char *buf, va_list num) -{ - int i; - char *str = buf; - static char digits[16] = "0123456789abcdef"; - str = str + sizeof(num) * 2; - - for (i = 0; i < sizeof(num) * 2; i++) { - *--str = digits[num & 15]; - num >>= 4; - } - - return buf + sizeof(num) * 2; -} - -char *__number(char *buf, va_list num) -{ - int i; - va_list mm = num; - char *str = buf; - - if (!num) { - *str++ = '0'; - return str; - } - - for (i = 0; mm; mm = mm/10, i++) { - /* Do nothing. */ - } - - str = str + i; - - while (num) { - *--str = num % 10 + 48; - num = num / 10; - } - - return str + i; -} - -va_list modf(va_list args, va_list *integer, va_list *num) -{ - int i; - double dot_v = 0; - va_list E, DOT, DOT_V; - - if (!args) { - return 0; - } - - for (i = 0, args = args << 1 >> 1; i < 52; i++) { - if ((args >> i) & 0x1) { - break; - } - } - - *integer = 0; - - if ((args >> 56 != 0x3f) || (args >> 52 == 0x3ff)) { - E = (args >> 52) - 1023; - DOT = 52 - E - i; - DOT_V = args << (12 + E) >> (12 + E) >> i; - *integer = ((args << 12 >> 12) >> (i + DOT)) | (1 << E); - } else { - E = ~((args >> 52) - 1023) + 1; - DOT_V = args << 12 >> 12; - - dot_v += 1.0 / (1 << E); - - for (i = 1; i <= 16; i++) { - if ((DOT_V >> (52 - i)) & 0x1) { - dot_v += 1.0 / (1 << E + i); - } - } - - for (i = 1, E = 0; i <= ACC; i++) { - dot_v *= 10; - if (!(va_list)dot_v) { - E++; - } - } - - *num = E; - - return dot_v; - } - - if (args & 0xf) { - for (i = 1; i <= 16; i++) { - if ((DOT_V >> (DOT - i)) & 0x1) { - dot_v += 1.0 / (1 << i); - } - } - - for (i = 1, E = 0; i <= ACC; i++) { - dot_v *= 10; - if (!(va_list)dot_v) { - E++; - } - } - - *num = E; - - return dot_v; - } else if (DOT) { - for (i = 1; i <= DOT; i++) { - if ((DOT_V >> (DOT - i)) & 0x1) { - dot_v += 1.0 / (1 << i); - } - } - - for (i = 1; i <= ACC; i++) { - dot_v = dot_v * 10; - } - - return dot_v; - } - - return 0; -} - -int vsnprintf(char *buf, int size, char *fmt, va_list args) -{ - char *str, *mm; - struct printf_spec spec = {0}; - - str = mm = buf; - - while (*fmt) { - char *old_fmt = fmt; - int read = format_decode(fmt, &spec); - - fmt += read; - - switch (spec.type) { - case FORMAT_TYPE_NONE: { - memcpy(str, old_fmt, read); - str += read; - break; - } - case FORMAT_TYPE_HEX: { - memcpy(str, old_fmt, read); - str = number(str + read, args); - for (; *mm ; ++mm) { - if (*mm == '%') { - *mm = '0'; - break; - } - } - break; - } - case FORMAT_TYPE_ULONG: { - memcpy(str, old_fmt, read - 2); - str = __number(str + read - 2, args); - break; - } - case FORMAT_TYPE_FLOAT: { - va_list integer, dot_v, num; - dot_v = modf(args, &integer, &num); - memcpy(str, old_fmt, read - 2); - str += read - 2; - if ((args >> 63 & 0x1)) { - *str++ = '-'; - } - str = __number(str, integer); - if (dot_v) { - *str++ = '.'; - while (num--) { - *str++ = '0'; - } - str = __number(str, dot_v); - } - break; - } - } - } - *str = '\0'; - - return str - buf; -} - -static void serial_out(char *str) -{ - while (*str) { - *(char *)0xffffffffb80003f8 = *str++; - } -} - -int vprintf(char *fmt, va_list args) -{ - int printed_len = 0; - static char printf_buf[512]; - printed_len = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); - serial_out(printf_buf); - return printed_len; -} - -int printf(char *fmt, ...) -{ - return vprintf(fmt, __read($5)); -} diff --git a/tests/tcg/mips/mips64-dsp/raddu_l_ob.c b/tests/tcg/mips/mips64-dsp/raddu_l_ob.c deleted file mode 100644 index 76ddf25fb9..0000000000 --- a/tests/tcg/mips/mips64-dsp/raddu_l_ob.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, result; - rs = 0x12345678ABCDEF0; - result = 0x000000000001E258; - - __asm - ("raddu.l.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs) - ); - - if (rd != result) { - printf("raddu.l.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/raddu_w_qb.c b/tests/tcg/mips/mips64-dsp/raddu_w_qb.c deleted file mode 100644 index c9d6535bba..0000000000 --- a/tests/tcg/mips/mips64-dsp/raddu_w_qb.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs; - long long result; - - rs = 0x12345678; - result = 0x114; - - __asm - ("raddu.w.qb %0, %1\n\t" - : "=r"(rd) - : "r"(rs) - ); - if (rd != result) { - printf("raddu.w.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/rddsp.c b/tests/tcg/mips/mips64-dsp/rddsp.c deleted file mode 100644 index 7165572435..0000000000 --- a/tests/tcg/mips/mips64-dsp/rddsp.c +++ /dev/null @@ -1,53 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long dsp_i, dsp_o; - long long ccond_i, outflag_i, efi_i, c_i, scount_i, pos_i; - long long ccond_o, outflag_o, efi_o, c_o, scount_o, pos_o; - long long ccond_r, outflag_r, efi_r, c_r, scount_r, pos_r; - - ccond_i = 0x000000BC;/* 4 */ - outflag_i = 0x0000001B;/* 3 */ - efi_i = 0x00000001;/* 5 */ - c_i = 0x00000001;/* 2 */ - scount_i = 0x0000000F;/* 1 */ - pos_i = 0x0000000C;/* 0 */ - - dsp_i = (ccond_i << 24) | \ - (outflag_i << 16) | \ - (efi_i << 14) | \ - (c_i << 13) | \ - (scount_i << 7) | \ - pos_i; - - ccond_r = ccond_i; - outflag_r = outflag_i; - efi_r = efi_i; - c_r = c_i; - scount_r = scount_i; - pos_r = pos_i; - - __asm - ("wrdsp %1, 0x3F\n\t" - "rddsp %0, 0x3F\n\t" - : "=r"(dsp_o) - : "r"(dsp_i) - ); - - ccond_o = (dsp_o >> 24) & 0xFF; - outflag_o = (dsp_o >> 16) & 0xFF; - efi_o = (dsp_o >> 14) & 0x01; - c_o = (dsp_o >> 14) & 0x01; - scount_o = (dsp_o >> 7) & 0x3F; - pos_o = dsp_o & 0x1F; - - if ((ccond_o != ccond_r) || (outflag_o != outflag_r) || (efi_o != efi_r) \ - || (c_o != c_r) || (scount_o != scount_r) || (pos_o != pos_r)) { - printf("rddsp wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/repl_ob.c b/tests/tcg/mips/mips64-dsp/repl_ob.c deleted file mode 100644 index 20cb780136..0000000000 --- a/tests/tcg/mips/mips64-dsp/repl_ob.c +++ /dev/null @@ -1,21 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, result; - rd = 0; - result = 0xFFFFFFFFFFFFFFFF; - - __asm - ("repl.ob %0, 0xFF\n\t" - : "=r"(rd) - ); - - if (result != rd) { - printf("repl.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/repl_ph.c b/tests/tcg/mips/mips64-dsp/repl_ph.c deleted file mode 100644 index 11d29bdbc2..0000000000 --- a/tests/tcg/mips/mips64-dsp/repl_ph.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, result; - - result = 0x01BF01BF; - __asm - ("repl.ph %0, 0x1BF\n\t" - : "=r"(rd) - ); - if (rd != result) { - printf("repl.ph wrong\n"); - - return -1; - } - - result = 0x01FF01FF; - __asm - ("repl.ph %0, 0x01FF\n\t" - : "=r"(rd) - ); - if (rd != result) { - printf("repl.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/repl_pw.c b/tests/tcg/mips/mips64-dsp/repl_pw.c deleted file mode 100644 index d35376a2a3..0000000000 --- a/tests/tcg/mips/mips64-dsp/repl_pw.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, result; - rd = 0; - result = 0x000001FF000001FF; - - __asm - ("repl.pw %0, 0x1FF\n\t" - : "=r"(rd) - ); - - if (result != rd) { - printf("repl.pw error1\n"); - - return -1; - } - - rd = 0; - result = 0xFFFFFE00FFFFFE00; - __asm - ("repl.pw %0, 0xFFFFFFFFFFFFFE00\n\t" - : "=r"(rd) - ); - - if (result != rd) { - printf("repl.pw error2\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/repl_qb.c b/tests/tcg/mips/mips64-dsp/repl_qb.c deleted file mode 100644 index 592feaecb0..0000000000 --- a/tests/tcg/mips/mips64-dsp/repl_qb.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, result; - - result = 0xFFFFFFFFBFBFBFBF; - __asm - ("repl.qb %0, 0xBF\n\t" - : "=r"(rd) - ); - if (rd != result) { - printf("repl.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/repl_qh.c b/tests/tcg/mips/mips64-dsp/repl_qh.c deleted file mode 100644 index 82afc37167..0000000000 --- a/tests/tcg/mips/mips64-dsp/repl_qh.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, result; - rd = 0; - result = 0x01FF01FF01FF01FF; - - __asm - ("repl.qh %0, 0x1FF\n\t" - : "=r"(rd) - ); - - if (result != rd) { - printf("repl.qh error 1\n"); - - return -1; - } - - rd = 0; - result = 0xFE00FE00FE00FE00; - __asm - ("repl.qh %0, 0xFFFFFFFFFFFFFE00\n\t" - : "=r"(rd) - ); - - if (result != rd) { - printf("repl.qh error 2\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/replv_ob.c b/tests/tcg/mips/mips64-dsp/replv_ob.c deleted file mode 100644 index 31ff3186d3..0000000000 --- a/tests/tcg/mips/mips64-dsp/replv_ob.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - - rt = 0xFF; - result = 0xFFFFFFFFFFFFFFFF; - - __asm - ("replv.ob %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("replv.ob error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/replv_ph.c b/tests/tcg/mips/mips64-dsp/replv_ph.c deleted file mode 100644 index 0af7a36b40..0000000000 --- a/tests/tcg/mips/mips64-dsp/replv_ph.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x12345678; - result = 0x56785678; - __asm - ("replv.ph %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("replv.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/replv_pw.c b/tests/tcg/mips/mips64-dsp/replv_pw.c deleted file mode 100644 index e1789af4c8..0000000000 --- a/tests/tcg/mips/mips64-dsp/replv_pw.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, result; - rd = 0; - rt = 0xFFFFFFFF; - result = 0xFFFFFFFFFFFFFFFF; - - __asm - ("replv.pw %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (result != rd) { - printf("replv.pw error\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/replv_qb.c b/tests/tcg/mips/mips64-dsp/replv_qb.c deleted file mode 100644 index d99298c31e..0000000000 --- a/tests/tcg/mips/mips64-dsp/replv_qb.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x12345678; - result = 0x78787878; - __asm - ("replv.qb %0, %1\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("replv.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shilo.c b/tests/tcg/mips/mips64-dsp/shilo.c deleted file mode 100644 index 5f454f69e0..0000000000 --- a/tests/tcg/mips/mips64-dsp/shilo.c +++ /dev/null @@ -1,29 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long ach, acl; - long long resulth, resultl; - - ach = 0xBBAACCFF; - acl = 0x1C3B001D; - - resulth = 0x17755; - resultl = 0xFFFFFFFF99fe3876; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "shilo $ac1, 0x0F\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("shilo wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shilov.c b/tests/tcg/mips/mips64-dsp/shilov.c deleted file mode 100644 index e82615a8c9..0000000000 --- a/tests/tcg/mips/mips64-dsp/shilov.c +++ /dev/null @@ -1,31 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, ach, acl; - long long resulth, resultl; - - rs = 0x0F; - ach = 0xBBAACCFF; - acl = 0x1C3B001D; - - resulth = 0x17755; - resultl = 0xFFFFFFFF99fe3876; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "shilov $ac1, %2\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("shilov wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_ob.c b/tests/tcg/mips/mips64-dsp/shll_ob.c deleted file mode 100644 index 7dcb58ff46..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_ob.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long res, resdsp; - - rt = 0x9ba8765433456789; - res = 0x9ba8765433456789; - resdsp = 0x0; - __asm - ("shll.ob %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x1; - - if ((dsp != resdsp) || (rd != res)) { - printf("shll.ob error\n"); - return -1; - } - - rt = 0x9ba8765433456789; - res = 0xd840b0a098283848; - resdsp = 0x1; - __asm - ("shll.ob %0, %2, 0x3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x1; - - if ((dsp != resdsp) || (rd != res)) { - printf("shll.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_ph.c b/tests/tcg/mips/mips64-dsp/shll_ph.c deleted file mode 100644 index 42b462d20d..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_ph.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long result, resultdsp; - - rt = 0x12345678; - result = 0x12345678; - resultdsp = 0; - - __asm - ("shll.ph %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll.ph wrong\n"); - - return -1; - } - - rt = 0x12345678; - result = 0xFFFFFFFFA000C000; - resultdsp = 1; - - __asm - ("shll.ph %0, %2, 0x0B\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll.ph wrong1\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_pw.c b/tests/tcg/mips/mips64-dsp/shll_pw.c deleted file mode 100644 index d7878b2792..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_pw.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long result, resultdsp; - - rt = 0x8765432112345678; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shll.pw %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll.pw wrong\n"); - return -1; - } - - rt = 0x8765432112345678; - result = 0x6543210034567800; - resultdsp = 1; - - __asm - ("shll.pw %0, %2, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll.pw wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_qb.c b/tests/tcg/mips/mips64-dsp/shll_qb.c deleted file mode 100644 index c21ab6698a..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_qb.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long result, resultdsp; - - rt = 0x87654321; - result = 0x38281808; - resultdsp = 0x01; - - __asm - ("shll.qb %0, %2, 0x03\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x01; - if (rd != result) { - printf("shll.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_qh.c b/tests/tcg/mips/mips64-dsp/shll_qh.c deleted file mode 100644 index 1380825a32..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_qh.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long res, resdsp; - - rt = 0x9ba8765433456789; - res = 0x9ba8765433456789; - resdsp = 0x0; - __asm - ("shll.qh %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x1; - - if ((dsp != resdsp) || (rd != res)) { - printf("shll.qh error\n"); - return -1; - } - - rt = 0x9ba8765433456789; - res = 0xdd40b2a09a283c48; - resdsp = 0x1; - __asm - ("shll.qh %0, %2, 0x3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x1; - - if ((dsp != resdsp) || (rd != res)) { - printf("shll.qh error1\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_s_ph.c b/tests/tcg/mips/mips64-dsp/shll_s_ph.c deleted file mode 100644 index 1cf5d6da6e..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_s_ph.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long result, resultdsp; - - rt = 0x12345678; - result = 0x12345678; - resultdsp = 0x0; - - __asm - ("shll_s.ph %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll_s.ph wrong\n"); - - return -1; - } - - rt = 0x12345678; - result = 0x7FFF7FFF; - resultdsp = 0x01; - - __asm - ("shll_s.ph %0, %2, 0x0B\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll_s.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_s_pw.c b/tests/tcg/mips/mips64-dsp/shll_s_pw.c deleted file mode 100644 index e38f6860c7..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_s_pw.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long result, resultdsp; - - rt = 0x8765432112345678; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shll_s.pw %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll_s.pw wrong\n"); - return -1; - } - - rt = 0x8765432112345678; - result = 0x800000007fffffff; - resultdsp = 1; - - __asm - ("shll_s.pw %0, %2, 0x8\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll_s.pw wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_s_qh.c b/tests/tcg/mips/mips64-dsp/shll_s_qh.c deleted file mode 100644 index f2f57fa27a..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_s_qh.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long res, resdsp; - - rt = 0x9ba8765433456789; - res = 0x9ba8765433456789; - resdsp = 0x0; - __asm - ("shll_s.qh %0, %2, 0x0\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x1; - - if ((dsp != resdsp) || (rd != res)) { - printf("shll_s.qh error\n"); - return -1; - } - - rt = 0x9ba8765433456789; - res = 0x80007fff7fff7fff; - resdsp = 0x1; - __asm - ("shll_s.qh %0, %2, 0x3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - - dsp = (dsp >> 22) & 0x1; - - if ((dsp != resdsp) || (rd != res)) { - printf("shll_s.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shll_s_w.c b/tests/tcg/mips/mips64-dsp/shll_s_w.c deleted file mode 100644 index 57800615d2..0000000000 --- a/tests/tcg/mips/mips64-dsp/shll_s_w.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, dsp; - long long result, resultdsp; - - rt = 0x12345678; - result = 0x7FFFFFFF; - resultdsp = 0x01; - - __asm - ("shll_s.w %0, %2, 0x0B\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shll_s.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_ob.c b/tests/tcg/mips/mips64-dsp/shllv_ob.c deleted file mode 100644 index 96a2e6f55f..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_ob.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs, dsp; - long long result, resultdsp; - - rt = 0x8765432112345678; - rs = 0x0; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shllv.ob %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.ob wrong\n"); - return -1; - } - - rt = 0x8765432112345678; - rs = 0x4; - result = 0x7050301020406080; - resultdsp = 1; - - __asm - ("shllv.ob %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.ob wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_ph.c b/tests/tcg/mips/mips64-dsp/shllv_ph.c deleted file mode 100644 index 532291f3fb..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_ph.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x0B; - rt = 0x12345678; - result = 0xFFFFFFFFA000C000; - resultdsp = 1; - - __asm - ("shllv.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_pw.c b/tests/tcg/mips/mips64-dsp/shllv_pw.c deleted file mode 100644 index 8d4ec295bd..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_pw.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs, dsp; - long long result, resultdsp; - rt = 0x8765432112345678; - rs = 0x0; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shllv.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.pw wrong\n"); - return -1; - } - - - rt = 0x8765432112345678; - rs = 0x8; - result = 0x6543210034567800; - resultdsp = 1; - - __asm - ("shllv.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.pw wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_qb.c b/tests/tcg/mips/mips64-dsp/shllv_qb.c deleted file mode 100644 index e49356b8ec..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_qb.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x03; - rt = 0x87654321; - result = 0x38281808; - resultdsp = 0x01; - - __asm - ("shllv.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - dsp = (dsp >> 22) & 0x01; - if (rd != result) { - printf("shllv.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_qh.c b/tests/tcg/mips/mips64-dsp/shllv_qh.c deleted file mode 100644 index 0de4077e7d..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_qh.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs, dsp; - long long result, resultdsp; - - rt = 0x8765432112345678; - rs = 0x0; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shllv.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.qh wrong\n"); - return -1; - } - - rt = 0x8765432112345678; - rs = 0x4; - result = 0x7650321023406780; - resultdsp = 1; - - __asm - ("shllv.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv.qh wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_s_ph.c b/tests/tcg/mips/mips64-dsp/shllv_s_ph.c deleted file mode 100644 index 7e69f941f4..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_s_ph.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x0B; - rt = 0x12345678; - result = 0x7FFF7FFF; - resultdsp = 0x01; - - __asm - ("shllv_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv_s.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_s_pw.c b/tests/tcg/mips/mips64-dsp/shllv_s_pw.c deleted file mode 100644 index f8dc8d2964..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_s_pw.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs, dsp; - long long result, resultdsp; - - rt = 0x8765432112345678; - rs = 0x0; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shllv_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv_s.pw wrong\n"); - return -1; - } - - rt = 0x8765432112345678; - rs = 0x8; - result = 0x800000007fffffff; - resultdsp = 1; - - __asm - ("shllv_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv_s.pw wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_s_qh.c b/tests/tcg/mips/mips64-dsp/shllv_s_qh.c deleted file mode 100644 index db3832d091..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_s_qh.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs, dsp; - long long result, resultdsp; - - rt = 0x8765432112345678; - rs = 0x0; - result = 0x8765432112345678; - resultdsp = 0; - - __asm - ("shllv_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv_s.qh wrong\n"); - return -1; - } - - rt = 0x8765432112345678; - rs = 0x4; - result = 0x80007fff7fff7fff; - resultdsp = 1; - - __asm - ("shllv_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv_s.qh wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shllv_s_w.c b/tests/tcg/mips/mips64-dsp/shllv_s_w.c deleted file mode 100644 index 5f6af8b8c0..0000000000 --- a/tests/tcg/mips/mips64-dsp/shllv_s_w.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x0B; - rt = 0x12345678; - result = 0x7FFFFFFF; - resultdsp = 0x01; - - __asm - ("shllv_s.w %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rt), "r"(rs) - ); - dsp = (dsp >> 22) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("shllv_s.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_ob.c b/tests/tcg/mips/mips64-dsp/shra_ob.c deleted file mode 100644 index d7fcfa816b..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_ob.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main() -{ - long long rd, rt; - long long res; - - rt = 0xbc98756abc654389; - res = 0xfbf9f7f6fb0604f8; - - __asm - ("shra.ob %0, %1, 0x4\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_ph.c b/tests/tcg/mips/mips64-dsp/shra_ph.c deleted file mode 100644 index a2dc014742..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_ph.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0xFFFFFFFFF0EC0864; - - __asm - ("shra.ph %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_pw.c b/tests/tcg/mips/mips64-dsp/shra_pw.c deleted file mode 100644 index 33b1b8fe72..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_pw.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long res; - - rt = 0x1234567887654321; - res = 0x01234567f8765432; - - __asm - ("shra.pw %0, %1, 0x4" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra.pw error\n"); - return -1; - } - - rt = 0x1234567887654321; - res = 0x1234567887654321; - - __asm - ("shra.pw %0, %1, 0x0" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra.pw error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_qh.c b/tests/tcg/mips/mips64-dsp/shra_qh.c deleted file mode 100644 index 85dbfef3ea..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_qh.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long res; - - rt = 0x8512345654323454; - res = 0xf851034505430345; - - __asm - ("shra.qh %0, %1, 0x4\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra.qh error\n"); - return -1; - } - - rt = 0x8512345654323454; - res = 0x8512345654323454; - - __asm - ("shra.qh %0, %1, 0x0\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra.qh error1\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_r_ob.c b/tests/tcg/mips/mips64-dsp/shra_r_ob.c deleted file mode 100644 index 184709443e..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_r_ob.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main() -{ - long long rd, rt; - long long res; - - rt = 0xbc98756abc654389; - res = 0xfcfaf8f7fc0705f9; - - __asm - ("shra_r.ob %0, %1, 0x4\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra_r.ob error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_r_ph.c b/tests/tcg/mips/mips64-dsp/shra_r_ph.c deleted file mode 100644 index e0943ad474..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_r_ph.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0xFFFFFFFFF0ED0864; - - __asm - ("shra_r.ph %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra_r.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_r_pw.c b/tests/tcg/mips/mips64-dsp/shra_r_pw.c deleted file mode 100644 index 6a86e684b8..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_r_pw.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long res; - - rt = 0x1234567887654321; - res = 0x01234568f8765432; - - __asm - ("shra_r.pw %0, %1, 0x4" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra_r.pw error\n"); - return -1; - } - - rt = 0x1234567887654321; - res = 0x1234567887654321; - - __asm - ("shra_r.pw %0, %1, 0x0" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra_r.pw error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_r_qh.c b/tests/tcg/mips/mips64-dsp/shra_r_qh.c deleted file mode 100644 index d5c2110efe..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_r_qh.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long res; - - rt = 0x8512345654323454; - res = 0xf0a2068b0a86068b; - - __asm - ("shra_r.qh %0, %1, 0x3\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra_r.qh error\n"); - return -1; - } - - rt = 0x8512345654323454; - res = 0x8512345654323454; - - __asm - ("shra_r.qh %0, %1, 0x0\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shra_r.qh error1\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shra_r_w.c b/tests/tcg/mips/mips64-dsp/shra_r_w.c deleted file mode 100644 index 36d2c9c887..0000000000 --- a/tests/tcg/mips/mips64-dsp/shra_r_w.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x87654321; - result = 0xFFFFFFFFF0ECA864; - - __asm - ("shra_r.w %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra_r.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_ph.c b/tests/tcg/mips/mips64-dsp/shrav_ph.c deleted file mode 100644 index 1b4e983746..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_ph.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x03; - rt = 0x87654321; - result = 0xFFFFFFFFF0EC0864; - - __asm - ("shrav.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_pw.c b/tests/tcg/mips/mips64-dsp/shrav_pw.c deleted file mode 100644 index e19d515797..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_pw.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x1234567887654321; - rs = 0x4; - res = 0x01234567f8765432; - - __asm - ("shrav.pw %0, %1, %2" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav.pw error\n"); - return -1; - } - - rt = 0x1234567887654321; - rs = 0x0; - res = 0x1234567887654321; - - __asm - ("shrav.pw %0, %1, %2" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav.pw error1\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_qh.c b/tests/tcg/mips/mips64-dsp/shrav_qh.c deleted file mode 100644 index dc92e09d44..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_qh.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x8512345654323454; - rs = 0x4; - res = 0xf851034505430345; - - __asm - ("shrav.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav.qh error\n"); - return -1; - } - - rt = 0x8512345654323454; - rs = 0x0; - res = 0x8512345654323454; - - __asm - ("shrav.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_r_ph.c b/tests/tcg/mips/mips64-dsp/shrav_r_ph.c deleted file mode 100644 index 350d5294fc..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_r_ph.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x03; - rt = 0x87654321; - result = 0xFFFFFFFFF0ED0864; - - __asm - ("shrav_r.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav_r.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_r_pw.c b/tests/tcg/mips/mips64-dsp/shrav_r_pw.c deleted file mode 100644 index 25b0545931..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_r_pw.c +++ /dev/null @@ -1,37 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x1234567887654321; - rs = 0x4; - res = 0x01234568f8765432; - - __asm - ("shrav_r.pw %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav_r.pw error\n"); - return -1; - } - - rt = 0x1234567887654321; - rs = 0x0; - res = 0x1234567887654321; - - __asm - ("shrav_r.pw %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != res) { - printf("shrav_r.pw error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_r_qh.c b/tests/tcg/mips/mips64-dsp/shrav_r_qh.c deleted file mode 100644 index fd187a1e0b..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_r_qh.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x8512345654323454; - rs = 0x3; - res = 0xf0a2068b0a86068b; - - __asm - ("shrav_r.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav_r.qh error\n"); - return -1; - } - - rt = 0x400000000000000; - rs = 0x0; - res = 0x400000000000000; - - __asm - ("shrav_r.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrav_r.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrav_r_w.c b/tests/tcg/mips/mips64-dsp/shrav_r_w.c deleted file mode 100644 index 3766c7255c..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrav_r_w.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x03; - rt = 0x87654321; - result = 0xFFFFFFFFF0ECA864; - - __asm - ("shrav_r.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav_r.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrl_ob.c b/tests/tcg/mips/mips64-dsp/shrl_ob.c deleted file mode 100644 index a1145713a0..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrl_ob.c +++ /dev/null @@ -1,38 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long res; - - rt = 0xab76543212345678; - res = 0x150e0a0602060a0f; - - __asm - ("shrl.ob %0, %1, 0x3\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shrl.ob error\n"); - return -1; - } - - rt = 0xab76543212345678; - res = 0xab76543212345678; - - __asm - ("shrl.ob %0, %1, 0x0\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shrl.ob error\n"); - return -1; - } - - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrl_qb.c b/tests/tcg/mips/mips64-dsp/shrl_qb.c deleted file mode 100644 index c0e36dba10..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrl_qb.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x12345678; - result = 0x00010203; - - __asm - ("shrl.qb %0, %1, 0x05\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shrl.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrl_qh.c b/tests/tcg/mips/mips64-dsp/shrl_qh.c deleted file mode 100644 index c1562463e0..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrl_qh.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt; - long long res; - - rt = 0x8765679abc543786; - res = 0x087606790bc50378; - - __asm - ("shrl.qh %0, %1, 0x4\n\t" - : "=r"(rd) - : "r"(rt) - ); - - if (rd != res) { - printf("shrl.qh error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrlv_ob.c b/tests/tcg/mips/mips64-dsp/shrlv_ob.c deleted file mode 100644 index cb39c46716..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrlv_ob.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0xab76543212345678; - rs = 0x3; - res = 0x150e0a0602060a0f; - - __asm - ("shrlv.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrlv.ob error\n"); - return -1; - } - - rt = 0xab76543212345678; - rs = 0x0; - res = 0xab76543212345678; - - __asm - ("shrlv.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrlv.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrlv_qb.c b/tests/tcg/mips/mips64-dsp/shrlv_qb.c deleted file mode 100644 index 5616aa9c5b..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrlv_qb.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x05; - rt = 0x12345678; - result = 0x00010203; - - __asm - ("shrlv.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrlv.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/shrlv_qh.c b/tests/tcg/mips/mips64-dsp/shrlv_qh.c deleted file mode 100644 index 05de2fd6c8..0000000000 --- a/tests/tcg/mips/mips64-dsp/shrlv_qh.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x8765679abc543786; - rs = 0x4; - res = 0x087606790bc50378; - - __asm - ("shrlv.qh %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shrlv.qh error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/subq_ph.c b/tests/tcg/mips/mips64-dsp/subq_ph.c deleted file mode 100644 index 6a1b18610c..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_ph.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x12345678; - rt = 0x87654321; - result = 0xFFFFFFFF8ACF1357; - resultdsp = 0x01; - - __asm - ("subq.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("subq.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/subq_pw.c b/tests/tcg/mips/mips64-dsp/subq_pw.c deleted file mode 100644 index 32f96ba4bd..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_pw.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rt = 0x123456789ABCDEF0; - rs = 0x123456789ABCDEF0; - result = 0x0; - dspresult = 0x0; - - __asm - ("subq.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq.pw error1\n\t"); - - return -1; - } - - rt = 0x123456789ABCDEF1; - rs = 0x123456789ABCDEF2; - result = 0x0000000000000001; - dspresult = 0x0; - - __asm - ("subq.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq.pw error2\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/subq_qh.c b/tests/tcg/mips/mips64-dsp/subq_qh.c deleted file mode 100644 index 76d5f0a10a..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_qh.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rt = 0x123456789ABCDEF0; - rs = 0x123456789ABCDEF0; - result = 0x0; - dspresult = 0x0; - - __asm - ("subq.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq.qh error\n\t"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/subq_s_ph.c b/tests/tcg/mips/mips64-dsp/subq_s_ph.c deleted file mode 100644 index 0b162f07ea..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_s_ph.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x7FFF1357; - resultdsp = 0x01; - - __asm - ("subq_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("subq_s.ph wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/subq_s_pw.c b/tests/tcg/mips/mips64-dsp/subq_s_pw.c deleted file mode 100644 index e8e0b0567e..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_s_pw.c +++ /dev/null @@ -1,63 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rt = 0x9FFFFFFD9FFFFFFD; - rs = 0x4000000080000000; - result = 0x7fffffffe0000003; - dspresult = 0x1; - - __asm - ("subq_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq_s.pw error1\n"); - - return -1; - } - - rt = 0x123456789ABCDEF1; - rs = 0x123456789ABCDEF2; - result = 0x0000000000000001; - /* This time we do not set dspctrl, but it setted in pre-action. */ - dspresult = 0x1; - - __asm - ("subq_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq_s.pw error2\n"); - - return -1; - } - - rt = 0x8000000080000000; - rs = 0x7000000070000000; - dspresult = 0x1; - - __asm - ("subq_s.pw %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = (dspreg >> 20) & 0x1; - if ((dspreg != dspresult)) { - printf("subq_s.pw error3\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/subq_s_qh.c b/tests/tcg/mips/mips64-dsp/subq_s_qh.c deleted file mode 100644 index 4053b6b884..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_s_qh.c +++ /dev/null @@ -1,61 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEF0; - result = 0x0; - dspresult = 0x0; - - __asm - ("subq_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq_s.qh error1\n"); - - return -1; - } - - rs = 0x4000000080000000; - rt = 0x9FFD00009FFC0000; - result = 0x7FFF0000E0040000; - dspresult = 0x1; - - __asm - ("subq_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq_s.qh error2\n"); - - return -1; - } - - rs = 0x8000000000000000; - rt = 0x7000000000000000; - result = 0x8000000000000000; - dspresult = 0x1; - __asm - ("subq_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = (dspreg >> 20) & 0x1; - if ((rd != result) || (dspreg != dspresult)) { - printf("subq_s.qh error3\n"); - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/subq_s_w.c b/tests/tcg/mips/mips64-dsp/subq_s_w.c deleted file mode 100644 index 91d32da172..0000000000 --- a/tests/tcg/mips/mips64-dsp/subq_s_w.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x7FFFFFFF; - resultdsp = 0x01; - - __asm - ("subq_s.w %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("subq_s.w wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/subu_ob.c b/tests/tcg/mips/mips64-dsp/subu_ob.c deleted file mode 100644 index f670967113..0000000000 --- a/tests/tcg/mips/mips64-dsp/subu_ob.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - rs = 0x6F6F6F6F6F6F6F6F; - rt = 0x5E5E5E5E5E5E5E5E; - result = 0x1111111111111111; - dspresult = 0x0; - - __asm - ("subu.ob %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - if ((rd != result) || (dspreg != dspresult)) { - printf("subu.ob error\n"); - - return -1; - } - - return 0; -} - diff --git a/tests/tcg/mips/mips64-dsp/subu_qb.c b/tests/tcg/mips/mips64-dsp/subu_qb.c deleted file mode 100644 index 9eb80df379..0000000000 --- a/tests/tcg/mips/mips64-dsp/subu_qb.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x12345678; - rt = 0x87654321; - result = 0xFFFFFFFF8BCF1357; - resultdsp = 0x01; - - __asm - ("subu.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("subu.qb wrong\n"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/subu_s_ob.c b/tests/tcg/mips/mips64-dsp/subu_s_ob.c deleted file mode 100644 index 5df64e5ff0..0000000000 --- a/tests/tcg/mips/mips64-dsp/subu_s_ob.c +++ /dev/null @@ -1,26 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dspreg, result, dspresult; - rs = 0x12345678ABCDEF0; - rt = 0x12345678ABCDEF1; - result = 0x00000000000; - dspresult = 0x01; - - __asm - ("subu_s.ob %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("subu_s.ob error\n\t"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/subu_s_qb.c b/tests/tcg/mips/mips64-dsp/subu_s_qb.c deleted file mode 100644 index 9de76f4a1f..0000000000 --- a/tests/tcg/mips/mips64-dsp/subu_s_qb.c +++ /dev/null @@ -1,27 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x00001357; - resultdsp = 0x01; - - __asm - ("subu_s.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if ((dsp != resultdsp) || (rd != result)) { - printf("subu_s_qb wrong"); - - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dsp/wrdsp.c b/tests/tcg/mips/mips64-dsp/wrdsp.c deleted file mode 100644 index 3033fd88d1..0000000000 --- a/tests/tcg/mips/mips64-dsp/wrdsp.c +++ /dev/null @@ -1,48 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long dsp_i, dsp_o; - long long ccond_i, outflag_i, efi_i, c_i, scount_i, pos_i; - long long ccond_o, outflag_o, efi_o, c_o, scount_o, pos_o; - long long ccond_r, outflag_r, efi_r, c_r, scount_r, pos_r; - - ccond_i = 0x000000BC;/* 4 */ - outflag_i = 0x0000001B;/* 3 */ - efi_i = 0x00000001;/* 5 */ - c_i = 0x00000001;/* 2 */ - scount_i = 0x0000000F;/* 1 */ - pos_i = 0x0000000C;/* 0 */ - - dsp_i = (ccond_i << 24) | (outflag_i << 16) | (efi_i << 14) | (c_i << 13) - | (scount_i << 7) | pos_i; - - ccond_r = ccond_i; - outflag_r = outflag_i; - efi_r = efi_i; - c_r = c_i; - scount_r = scount_i; - pos_r = pos_i; - - __asm - ("wrdsp %1, 0x3F\n\t" - "rddsp %0, 0x3F\n\t" - : "=r"(dsp_o) - : "r"(dsp_i) - ); - - ccond_o = (dsp_o >> 24) & 0xFF; - outflag_o = (dsp_o >> 16) & 0xFF; - efi_o = (dsp_o >> 14) & 0x01; - c_o = (dsp_o >> 14) & 0x01; - scount_o = (dsp_o >> 7) & 0x3F; - pos_o = dsp_o & 0x1F; - - if ((ccond_o != ccond_r) || (outflag_o != outflag_r) || (efi_o != efi_r) \ - || (c_o != c_r) || (scount_o != scount_r) || (pos_o != pos_r)) { - printf("wrddsp wrong\n"); - - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/.directory b/tests/tcg/mips/mips64-dspr2/.directory deleted file mode 100644 index c75a91451c..0000000000 --- a/tests/tcg/mips/mips64-dspr2/.directory +++ /dev/null @@ -1,2 +0,0 @@ -[Dolphin] -Timestamp=2012,8,3,16,41,52 diff --git a/tests/tcg/mips/mips64-dspr2/Makefile b/tests/tcg/mips/mips64-dspr2/Makefile deleted file mode 100644 index ba44bb9c0e..0000000000 --- a/tests/tcg/mips/mips64-dspr2/Makefile +++ /dev/null @@ -1,116 +0,0 @@ -CROSS_COMPILE ?= mips64el-unknown-linux-gnu- - -SIM = qemu-system-mips64el -SIMFLAGS = -nographic -cpu mips64dspr2 -kernel - -AS = $(CROSS_COMPILE)as -LD = $(CROSS_COMPILE)ld -CC = $(CROSS_COMPILE)gcc -AR = $(CROSS_COMPILE)ar -NM = $(CROSS_COMPILE)nm -STRIP = $(CROSS_COMPILE)strip -RANLIB = $(CROSS_COMPILE)ranlib -OBJCOPY = $(CROSS_COMPILE)objcopy -OBJDUMP = $(CROSS_COMPILE)objdump - -VECTORS_OBJ ?= ./head.o ./printf.o - -HEAD_FLAGS ?= -nostdinc -mabi=64 -G 0 -mno-abicalls -fno-pic -pipe \ - -msoft-float -march=mips64 -Wa,-mips64 -Wa,--trap \ - -msym32 -DKBUILD_64BIT_SYM32 -I./ - -CFLAGS ?= -nostdinc -mabi=64 -G 0 -mno-abicalls -fno-pic -fno-builtin \ - -pipe -march=mips64r2 -mgp64 -mdspr2 -static -Wa,--trap -msym32 \ - -DKBUILD_64BIT_SYM32 -I./ - -LDFLAGS = -T./mips_boot.lds -L./ -FLAGS = -nostdlib -mabi=64 -march=mips64r2 -mgp64 -mdspr2 - -TESTCASES = absq_s_qb.tst -TESTCASES += addqh_ph.tst -TESTCASES += addqh_r_ph.tst -TESTCASES += addqh_r_w.tst -TESTCASES += addqh_w.tst -#TESTCASES += adduh_ob.tst -TESTCASES += adduh_qb.tst -#TESTCASES += adduh_r_ob.tst -TESTCASES += adduh_r_qb.tst -TESTCASES += addu_ph.tst -#TESTCASES += addu_qh.tst -TESTCASES += addu_s_ph.tst -#TESTCASES += addu_s_qh.tst -TESTCASES += append.tst -TESTCASES += balign.tst -#TESTCASES += cmpgdu_eq_ob.tst -TESTCASES += cmpgdu_eq_qb.tst -#TESTCASES += cmpgdu_le_ob.tst -TESTCASES += cmpgdu_le_qb.tst -#TESTCASES += cmpgdu_lt_ob.tst -TESTCASES += cmpgdu_lt_qb.tst -#TESTCASES += dbalign.tst -TESTCASES += dpaqx_sa_w_ph.tst -TESTCASES += dpaqx_s_w_ph.tst -TESTCASES += dpa_w_ph.tst -#TESTCASES += dpa_w_qh.tst -TESTCASES += dpax_w_ph.tst -TESTCASES += dpsqx_sa_w_ph.tst -TESTCASES += dpsqx_s_w_ph.tst -TESTCASES += dps_w_ph.tst -#TESTCASES += dps_w_qh.tst -TESTCASES += dpsx_w_ph.tst -TESTCASES += mul_ph.tst -TESTCASES += mulq_rs_w.tst -TESTCASES += mulq_s_ph.tst -TESTCASES += mulq_s_w.tst -TESTCASES += mulsaq_s_w_ph.tst -TESTCASES += mulsa_w_ph.tst -TESTCASES += mul_s_ph.tst -TESTCASES += precr_qb_ph.tst -TESTCASES += precr_sra_ph_w.tst -TESTCASES += precr_sra_r_ph_w.tst -TESTCASES += prepend.tst -TESTCASES += shra_qb.tst -TESTCASES += shra_r_qb.tst -#TESTCASES += shrav_ob.tst -TESTCASES += shrav_qb.tst -#TESTCASES += shrav_r_ob.tst -TESTCASES += shrav_r_qb.tst -TESTCASES += shrl_ph.tst -TESTCASES += shrlv_ph.tst -TESTCASES += subqh_ph.tst -TESTCASES += subqh_r_ph.tst -TESTCASES += subqh_r_w.tst -TESTCASES += subqh_w.tst -#TESTCASES += subuh_ob.tst -TESTCASES += subuh_qb.tst -#TESTCASES += subuh_r_ob.tst -TESTCASES += subuh_r_qb.tst -TESTCASES += subu_ph.tst -#TESTCASES += subu_qh.tst -TESTCASES += subu_s_ph.tst -#TESTCASES += subu_s_qh.tst - -all: build - -head.o : head.S - $(Q)$(CC) $(HEAD_FLAGS) -D"STACK_TOP=0xffffffff80200000" -c $< -o $@ - -%.o : %.S - $(CC) $(CFLAGS) -c $< -o $@ - -%.o : %.c - $(CC) $(CFLAGS) -c $< -o $@ - -%.tst: %.o $(VECTORS_OBJ) - $(CC) $(VECTORS_OBJ) $(FLAGS) $(LDFLAGS) $< -o $@ - -build: $(VECTORS_OBJ) $(MIPSSOC_LIB) $(TESTCASES) - -check: $(VECTORS_OBJ) $(MIPSSOC_LIB) $(TESTCASES) - @for case in $(TESTCASES); do \ - echo $(SIM) $(SIMFLAGS) ./$$case; \ - $(SIM) $(SIMFLAGS) ./$$case & (sleep 1; killall $(SIM)); \ - done - -clean: - $(Q)rm -f *.o *.tst *.a diff --git a/tests/tcg/mips/mips64-dspr2/absq_s_qb.c b/tests/tcg/mips/mips64-dspr2/absq_s_qb.c deleted file mode 100644 index f7aec3e568..0000000000 --- a/tests/tcg/mips/mips64-dspr2/absq_s_qb.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "io.h" -int main() -{ - long long input, result, dsp; - long long hope; - - input = 0x701BA35E; - hope = 0x701B5D5E; - - __asm - ("absq_s.qb %0, %1\n\t" - : "=r"(result) - : "r"(input) - ); - if (result != hope) { - printf("absq_s.qb error\n"); - return -1; - } - - input = 0x801BA35E; - hope = 0x7F1B5D5E; - - __asm - ("absq_s.qb %0, %2\n\t" - "rddsp %1\n\t" - : "=r"(result), "=r"(dsp) - : "r"(input) - ); - dsp = dsp >> 20; - dsp &= 0x01; - if (result != hope) { - printf("absq_s.qb error\n"); - return -1; - } - - if (dsp != 1) { - printf("absq_s.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addqh_ph.c b/tests/tcg/mips/mips64-dspr2/addqh_ph.c deleted file mode 100644 index 6b43cb8d06..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addqh_ph.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x706A13FE; - rt = 0x13065174; - result = 0x41B832B9; - __asm - ("addqh.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("addqh.ph error!\n"); - return -1; - } - - rs = 0x81000100; - rt = 0xc2000100; - result = 0xffffffffa1800100; - __asm - ("addqh.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("addqh.ph error!\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addqh_r_ph.c b/tests/tcg/mips/mips64-dspr2/addqh_r_ph.c deleted file mode 100644 index 890ec98d9b..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addqh_r_ph.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x706A13FE; - rt = 0x13065174; - result = 0x41B832B9; - __asm - ("addqh_r.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("addqh_r.ph error\n"); - return -1; - } - - rs = 0x81010100; - rt = 0xc2000100; - result = 0xffffffffa1810100; - __asm - ("addqh_r.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("addqh_r.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addqh_r_w.c b/tests/tcg/mips/mips64-dspr2/addqh_r_w.c deleted file mode 100644 index d324decbd3..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addqh_r_w.c +++ /dev/null @@ -1,38 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x00000010; - rt = 0x00000001; - result = 0x00000009; - - __asm - ("addqh_r.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("addqh_r.w error!\n"); - return -1; - } - rs = 0xFFFFFFFE; - rt = 0x00000001; - result = 0x00000000; - - __asm - ("addqh_r.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("addqh_r.w error!\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addqh_w.c b/tests/tcg/mips/mips64-dspr2/addqh_w.c deleted file mode 100644 index 78559e6784..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addqh_w.c +++ /dev/null @@ -1,39 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x00000010; - rt = 0x00000001; - result = 0x00000008; - - __asm - ("addqh.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("addqh.w wrong\n"); - return -1; - } - - rs = 0xFFFFFFFE; - rt = 0x00000001; - result = 0xFFFFFFFFFFFFFFFF; - - __asm - ("addqh.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("addqh.w wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addu_ph.c b/tests/tcg/mips/mips64-dspr2/addu_ph.c deleted file mode 100644 index d64c8cde8a..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addu_ph.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x00FF00FF; - rt = 0x00010001; - result = 0x01000100; - __asm - ("addu.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("1 addu.ph error\n"); - return -1; - } - - rs = 0xFFFF1111; - rt = 0x00020001; - result = 0x00011112; - __asm - ("addu.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("2 addu.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addu_qh.c b/tests/tcg/mips/mips64-dspr2/addu_qh.c deleted file mode 100644 index edcbf342ce..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addu_qh.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dspreg; - long long result, dspresult; - - rs = 0x123456787FFF0000; - rt = 0x1111111180000000; - result = 0x23456789FFFF0000; - dspresult = 0x0; - - __asm("addu.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addu.qh error\n"); - return -1; - } - - rs = 0x123456787FFF0000; - rt = 0x1111111180020000; - result = 0x23456789FFFF0000; - dspresult = 0x01; - - __asm("addu.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("addu.qh overflow error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addu_s_ph.c b/tests/tcg/mips/mips64-dspr2/addu_s_ph.c deleted file mode 100644 index 9250edb45c..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addu_s_ph.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x00FE00FE; - rt = 0x00020001; - result = 0x010000FF; - __asm - ("addu_s.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("addu_s.ph error\n"); - return -1; - } - - rs = 0xFFFF1111; - rt = 0x00020001; - result = 0xFFFFFFFFFFFF1112; - __asm - ("addu_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((rd != result) || (((dsp >> 20) & 0x01) != 1)) { - printf("addu_s.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/addu_s_qh.c b/tests/tcg/mips/mips64-dspr2/addu_s_qh.c deleted file mode 100644 index b0c1626251..0000000000 --- a/tests/tcg/mips/mips64-dspr2/addu_s_qh.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dspreg; - long long result, dspresult; - - rs = 0x123456787FFF0000; - rt = 0x1111111180000000; - result = 0x23456789FFFF0000; - dspresult = 0x0; - - __asm("addu_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("1 addu_s.qh error\n"); - return -1; - } - - rs = 0x12345678FFFF0000; - rt = 0x11111111000F0000; - result = 0x23456789FFFF0000; - dspresult = 0x01; - - __asm("addu_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("2 addu_s.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/adduh_ob.c b/tests/tcg/mips/mips64-dspr2/adduh_ob.c deleted file mode 100644 index 9b309f6f16..0000000000 --- a/tests/tcg/mips/mips64-dspr2/adduh_ob.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - rs = 0xFF987CDEBCEF2356; - rt = 0xFF987CDEBCEF2354; - result = 0xFF987CDEBCEF2355; - - __asm("adduh.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("adduh.ob error\n\t"); - return -1; - } - - rs = 0xac50691729945316; - rt = 0xb9234ca3f5573162; - result = 0xb2395a5d8f75423c; - - __asm("adduh.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("adduh.ob error\n\t"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/adduh_qb.c b/tests/tcg/mips/mips64-dspr2/adduh_qb.c deleted file mode 100644 index 796b409a86..0000000000 --- a/tests/tcg/mips/mips64-dspr2/adduh_qb.c +++ /dev/null @@ -1,35 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0xFF0055AA; - rt = 0x0113421B; - result = 0xffffffff80094B62; - __asm - ("adduh.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("adduh.qb error\n"); - return -1; - } - rs = 0xFFFF0FFF; - rt = 0x00010111; - result = 0x7F800888; - - __asm - ("adduh.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("adduh.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/adduh_r_ob.c b/tests/tcg/mips/mips64-dspr2/adduh_r_ob.c deleted file mode 100644 index 832de833ef..0000000000 --- a/tests/tcg/mips/mips64-dspr2/adduh_r_ob.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - rs = 0xFF987CDEBCEF2356; - rt = 0xFF987CDEBCEF2355; - result = 0xFF987CDEBCEF2356; - - __asm("adduh_r.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("1 adduh_r.ob error\n\t"); - return -1; - } - - rs = 0xac50691729945316; - rt = 0xb9234ca3f5573162; - result = 0xb33a5b5d8f76423c; - - __asm("adduh_r.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("2 adduh_r.ob error\n\t"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/adduh_r_qb.c b/tests/tcg/mips/mips64-dspr2/adduh_r_qb.c deleted file mode 100644 index ae65fa5e18..0000000000 --- a/tests/tcg/mips/mips64-dspr2/adduh_r_qb.c +++ /dev/null @@ -1,35 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0xFF0055AA; - rt = 0x01112211; - result = 0xffffffff80093C5E; - __asm - ("adduh_r.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("adduh_r.qb error\n"); - return -1; - } - - rs = 0xFFFF0FFF; - rt = 0x00010111; - result = 0xffffffff80800888; - __asm - ("adduh_r.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("adduh_r.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/append.c b/tests/tcg/mips/mips64-dspr2/append.c deleted file mode 100644 index 68a7cecc4b..0000000000 --- a/tests/tcg/mips/mips64-dspr2/append.c +++ /dev/null @@ -1,35 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long result; - - rs = 0xFF0055AA; - rt = 0x0113421B; - result = 0x02268436; - __asm - ("append %0, %1, 0x01\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (rt != result) { - printf("append error\n"); - return -1; - } - - rs = 0xFFFF0FFF; - rt = 0x00010111; - result = 0x0010111F; - __asm - ("append %0, %1, 0x04\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (rt != result) { - printf("append error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/balign.c b/tests/tcg/mips/mips64-dspr2/balign.c deleted file mode 100644 index 7fbe815782..0000000000 --- a/tests/tcg/mips/mips64-dspr2/balign.c +++ /dev/null @@ -1,35 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long result; - - rs = 0xFF0055AA; - rt = 0x0113421B; - result = 0x13421BFF; - __asm - ("balign %0, %1, 0x01\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (rt != result) { - printf("balign error\n"); - return -1; - } - - rs = 0xFFFF0FFF; - rt = 0x00010111; - result = 0x11FFFF0F; - __asm - ("balign %0, %1, 0x03\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (rt != result) { - printf("balign error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/cmpgdu_eq_ob.c b/tests/tcg/mips/mips64-dspr2/cmpgdu_eq_ob.c deleted file mode 100644 index 61217f38cf..0000000000 --- a/tests/tcg/mips/mips64-dspr2/cmpgdu_eq_ob.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0xFE; - dspresult = 0xFE; - - __asm("cmpgdu.eq.ob %0, %2, %3\n\t" - "rddsp %1" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if ((rd != result) || (dspreg != dspresult)) { - printf("1 cmpgdu.eq.ob error\n"); - return -1; - } - - rs = 0x133256789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0x3E; - dspresult = 0x3E; - - __asm("cmpgdu.eq.ob %0, %2, %3\n\t" - "rddsp %1" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if ((rd != result) || (dspreg != dspresult)) { - printf("2 cmpgdu.eq.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/cmpgdu_eq_qb.c b/tests/tcg/mips/mips64-dspr2/cmpgdu_eq_qb.c deleted file mode 100644 index c63f6480eb..0000000000 --- a/tests/tcg/mips/mips64-dspr2/cmpgdu_eq_qb.c +++ /dev/null @@ -1,41 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x02; - __asm - ("cmpgdu.eq.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if ((rd != result) || (dsp != result)) { - printf("cmpgdu.eq.qb error\n"); - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x0F; - __asm - ("cmpgdu.eq.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - - if ((rd != result) || (dsp != result)) { - printf("cmpgdu.eq.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/cmpgdu_le_ob.c b/tests/tcg/mips/mips64-dspr2/cmpgdu_le_ob.c deleted file mode 100644 index b3da098189..0000000000 --- a/tests/tcg/mips/mips64-dspr2/cmpgdu_le_ob.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - - rs = 0x123456789abcdef0; - rt = 0x123456789abcdeff; - dspresult = 0xff; - result = 0xff; - - __asm("cmpgdu.le.ob %0, %2, %3\n\t" - "rddsp %1" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xff); - - if ((rd != result) || (dspreg != dspresult)) { - printf("cmpgdu.le.ob error\n"); - return -1; - } - - rs = 0x113556789ABCDEF0; - rt = 0x123456789ABCDEFF; - result = 0xBE; - dspresult = 0xFE; - - __asm("cmpgdu.eq.ob %0, %2, %3\n\t" - "rddsp %1" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if ((rd != result) || (dspreg != dspresult)) { - printf("cmpgdu.eq.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/cmpgdu_le_qb.c b/tests/tcg/mips/mips64-dspr2/cmpgdu_le_qb.c deleted file mode 100644 index f0a60ea4e0..0000000000 --- a/tests/tcg/mips/mips64-dspr2/cmpgdu_le_qb.c +++ /dev/null @@ -1,48 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x0F; - __asm - ("cmpgdu.le.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (rd != result) { - printf("cmpgdu.le.qb error\n"); - return -1; - } - if (dsp != result) { - printf("cmpgdu.le.qb error\n"); - return -1; - } - - rs = 0x11777066; - rt = 0x11707066; - result = 0x0B; - __asm - ("cmpgdu.le.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (rd != result) { - printf("cmpgdu.le.qb error\n"); - return -1; - } - if (dsp != result) { - printf("cmpgdu.le.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/cmpgdu_lt_ob.c b/tests/tcg/mips/mips64-dspr2/cmpgdu_lt_ob.c deleted file mode 100644 index d80b4e6ab9..0000000000 --- a/tests/tcg/mips/mips64-dspr2/cmpgdu_lt_ob.c +++ /dev/null @@ -1,44 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result, dspreg, dspresult; - - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x01; - result = 0x01; - - __asm("cmpgdu.lt.ob %0, %2, %3\n\t" - "rddsp %1" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if ((rd != result) || (dspreg != dspresult)) { - printf("cmpgdu.lt.ob error\n"); - return -1; - } - - rs = 0x143356789ABCDEF0; - rt = 0x123456789ABCDEFF; - dspresult = 0x41; - result = 0x41; - - __asm("cmpgdu.lt.ob %0, %2, %3\n\t" - "rddsp %1" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 24) & 0xFF); - - if ((rd != result) || (dspreg != dspresult)) { - printf("cmpgdu.lt.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/cmpgdu_lt_qb.c b/tests/tcg/mips/mips64-dspr2/cmpgdu_lt_qb.c deleted file mode 100644 index a71e4e307f..0000000000 --- a/tests/tcg/mips/mips64-dspr2/cmpgdu_lt_qb.c +++ /dev/null @@ -1,48 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long dsp; - long long result; - - rs = 0x11777066; - rt = 0x55AA70FF; - result = 0x0D; - __asm - ("cmpgdu.lt.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (rd != result) { - printf("cmpgdu.lt.qb error\n"); - return -1; - } - if (dsp != result) { - printf("cmpgdu.lt.qb error\n"); - return -1; - } - - rs = 0x11777066; - rt = 0x11777066; - result = 0x00; - __asm - ("cmpgdu.lt.qb %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 24) & 0x0F; - if (rd != result) { - printf("cmpgdu.lt.qb error\n"); - return -1; - } - if (dsp != result) { - printf("cmpgdu.lt.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dbalign.c b/tests/tcg/mips/mips64-dspr2/dbalign.c deleted file mode 100644 index c7431b1857..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dbalign.c +++ /dev/null @@ -1,39 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rt, rs; - long long res; - - rt = 0x1234567887654321; - rs = 0xabcd1234abcd1234; - - res = 0x34567887654321ab; - - asm ("dbalign %0, %1, 0x1\n" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("dbalign error\n"); - return -1; - } - - rt = 0x1234567887654321; - rs = 0xabcd1234abcd1234; - - res = 0x7887654321abcd12; - - asm ("dbalign %0, %1, 0x3\n" - : "=r"(rt) - : "r"(rs) - ); - - if (rt != res) { - printf("dbalign error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpa_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpa_w_ph.c deleted file mode 100644 index 39dc99aa55..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpa_w_ph.c +++ /dev/null @@ -1,47 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0x00FF00FF; - rt = 0x00010002; - resulth = 0x05; - resultl = 0x0302; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpa.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("1 dpa.w.ph error\n"); - return -1; - } - - ach = 6, acl = 7; - rs = 0xFFFF00FF; - rt = 0xFFFF0002; - resulth = 0x05; - resultl = 0xfffffffffffe0206; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpa.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if ((ach != resulth) || (acl != resultl)) { - printf("2 dpa.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpa_w_qh.c b/tests/tcg/mips/mips64-dspr2/dpa_w_qh.c deleted file mode 100644 index 1411e44be3..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpa_w_qh.c +++ /dev/null @@ -1,56 +0,0 @@ -#include"io.h" -int main(void) -{ - long long rt, rs; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - achi = 0x1; - acli = 0x1; - - rs = 0x0001000100010001; - rt = 0x0002000200020002; - - resh = 0x1; - resl = 0x9; - - asm("mthi %2, $ac1\t\n" - "mtlo %3, $ac1\t\n" - "dpa.w.qh $ac1, %4, %5\t\n" - "mfhi %0, $ac1\t\n" - "mflo %1, $ac1\t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dpa.w.qh error\n"); - return -1; - } - - - achi = 0xffffffff; - acli = 0xaaaaaaaa; - - rs = 0xaaaabbbbccccdddd; - rt = 0x7777888899996666; - - resh = 0xffffffffffffffff; - resl = 0x320cdf02; - - asm("mthi %2, $ac1\t\n" - "mtlo %3, $ac1\t\n" - "dpa.w.qh $ac1, %4, %5\t\n" - "mfhi %0, $ac1\t\n" - "mflo %1, $ac1\t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - if ((acho != resh) || (aclo != resl)) { - printf("2 dpa.w.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpaqx_s_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpaqx_s_w_ph.c deleted file mode 100644 index 51252fb980..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpaqx_s_w_ph.c +++ /dev/null @@ -1,97 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long ach = 5, acl = 5; - long long resulth, resultl, resultdsp; - - rs = 0x800000FF; - rt = 0x00018000; - resulth = 0x05; - resultl = 0xFFFFFFFF80000202; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpaqx_s.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if (dsp != resultdsp) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - if (ach != resulth) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - if (acl != resultl) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - - ach = 5; - acl = 5; - rs = 0x00FF00FF; - rt = 0x00010002; - resulth = 0x05; - resultl = 0x05FF; - /*********************************************************** - * Because of we set outflag at last time, although this - * time we set nothing, but it is stay the last time value. - **********************************************************/ - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpaqx_s.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if (dsp != resultdsp) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - if (ach != resulth) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - if (acl != resultl) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - - ach = 5; - acl = 5; - rs = 0x800000FF; - rt = 0x00028000; - resulth = 0x05; - resultl = 0xffffffff80000400; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpaqx_s.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if ((dsp != resultdsp) || (ach != resulth) || (acl != resultl)) { - printf("dpaqx_s.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpaqx_sa_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpaqx_sa_w_ph.c deleted file mode 100644 index 18d6b3a98e..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpaqx_sa_w_ph.c +++ /dev/null @@ -1,54 +0,0 @@ -#include "io.h" - -int main() -{ - long long rs, rt, dsp; - long long ach = 5, acl = 5; - long long resulth, resultl, resultdsp; - - rs = 0x00FF00FF; - rt = 0x00010002; - resulth = 0x00; - resultl = 0x7FFFFFFF; - resultdsp = 0x01; - __asm - ("wrdsp %2\n\t" - "mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpaqx_sa.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "+r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((dsp >> (16 + 1) != resultdsp) || (ach != resulth) || - (acl != resultl)) { - printf("dpaqx_sa.w.ph errror\n"); - } - - ach = 9; - acl = 0xb; - rs = 0x800000FF; - rt = 0x00018000; - resulth = 0x00; - resultl = 0x7fffffff; - resultdsp = 0x01; - __asm - ("wrdsp %2\n\t" - "mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpaqx_sa.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "+r"(dsp) - : "r"(rs), "r"(rt) - ); - if ((dsp >> (16 + 1) != resultdsp) || (ach != resulth) || - (acl != resultl)) { - printf("dpaqx_sa.w.ph errror\n"); - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpax_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpax_w_ph.c deleted file mode 100644 index 9d595fc14a..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpax_w_ph.c +++ /dev/null @@ -1,32 +0,0 @@ -#include"io.h" - -int main(void) -{ - long rs, rt; - long ach = 5, acl = 5; - long resulth, resultl; - - rs = 0x00FF00FF; - rt = 0x00010002; - resulth = 0x05; - resultl = 0x0302; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpax.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if (ach != resulth) { - printf("dpax.w.ph error\n"); - return -1; - } - if (acl != resultl) { - printf("dpax.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dps_w_ph.c b/tests/tcg/mips/mips64-dspr2/dps_w_ph.c deleted file mode 100644 index 99f292ecb2..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dps_w_ph.c +++ /dev/null @@ -1,28 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0x00FF00FF; - rt = 0x00010002; - resulth = 0x04; - resultl = 0xFFFFFFFFFFFFFFD08; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dps.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if (ach != resulth || acl != resultl) { - printf("dps.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dps_w_qh.c b/tests/tcg/mips/mips64-dspr2/dps_w_qh.c deleted file mode 100644 index 61277eb30c..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dps_w_qh.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rs, rt; - long long achi, acli; - long long acho, aclo; - long long resh, resl; - - rs = 0x0000000100000001; - rt = 0x0000000200000002; - achi = 0x1; - acli = 0x8; - - resh = 0x1; - resl = 0x4; - - asm ("mthi %2, $ac1\t\n" - "mtlo %3, $ac1\t\n" - "dps.w.qh $ac1, %4, %5\t\n" - "mfhi %0, $ac1\t\n" - "mflo %1, $ac1\t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dps.w.qh error\n"); - return -1; - } - - rs = 0xaaaabbbbccccdddd; - rt = 0xaaaabbbbccccdddd; - - achi = 0x88888888; - achi = 0x55555555; - - resh = 0xfffffffff7777777; - resl = 0x0a38b181; - - asm ("mthi %2, $ac1\t\n" - "mtlo %3, $ac1\t\n" - "dps.w.qh $ac1, %4, %5\t\n" - "mfhi %0, $ac1\t\n" - "mflo %1, $ac1\t\n" - : "=r"(acho), "=r"(aclo) - : "r"(achi), "r"(acli), "r"(rs), "r"(rt) - ); - - if ((acho != resh) || (aclo != resl)) { - printf("1 dps.w.qh error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpsqx_s_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpsqx_s_w_ph.c deleted file mode 100644 index ba46a92698..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpsqx_s_w_ph.c +++ /dev/null @@ -1,55 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt, dsp; - long long ach = 5, acl = 5; - long long resulth, resultl, resultdsp; - - rs = 0xBC0123AD; - rt = 0x01643721; - resulth = 0x04; - resultl = 0xFFFFFFFFAEA3E09B; - resultdsp = 0x00; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsqx_s.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if (dsp != resultdsp || ach != resulth || acl != resultl) { - printf("dpsqx_s.w.ph error\n"); - return -1; - } - - ach = 0x99f13005; - acl = 0x51730062; - rs = 0x80008000; - rt = 0x80008000; - - resulth = 0xffffffff99f13004; - resultl = 0x51730064; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsqx_s.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if (dsp != resultdsp || ach != resulth || acl != resultl) { - printf("dpsqx_s.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpsqx_sa_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpsqx_sa_w_ph.c deleted file mode 100644 index 24c888134d..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpsqx_sa_w_ph.c +++ /dev/null @@ -1,53 +0,0 @@ -#include"io.h" -int main() -{ - long long rs, rt, dsp; - long long ach = 5, acl = 5; - long long resulth, resultl, resultdsp; - - rs = 0xBC0123AD; - rt = 0x01643721; - resulth = 0x00; - resultl = 0x7FFFFFFF; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsqx_sa.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if (dsp != resultdsp || ach != resulth || acl != resultl) { - printf("dpsqx_sa.w.ph error\n"); - return -1; - } - - ach = 0x8c0b354A; - acl = 0xbbc02249; - rs = 0x800023AD; - rt = 0x01648000; - resulth = 0xffffffffffffffff; - resultl = 0xffffffff80000000; - resultdsp = 0x01; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsqx_sa.w.ph $ac1, %3, %4\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - "rddsp %2\n\t" - : "+r"(ach), "+r"(acl), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 17) & 0x01; - if (dsp != resultdsp || ach != resulth || acl != resultl) { - printf("dpsqx_sa.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/dpsx_w_ph.c b/tests/tcg/mips/mips64-dspr2/dpsx_w_ph.c deleted file mode 100644 index b6291b5eb6..0000000000 --- a/tests/tcg/mips/mips64-dspr2/dpsx_w_ph.c +++ /dev/null @@ -1,28 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long ach = 5, acl = 5; - long long resulth, resultl; - - rs = 0xBC0123AD; - rt = 0x01643721; - resulth = 0x04; - resultl = 0xFFFFFFFFD751F050; - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "dpsx.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if (ach != resulth || acl != resultl) { - printf("dpsx.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/head.S b/tests/tcg/mips/mips64-dspr2/head.S deleted file mode 100644 index 9a099ae42f..0000000000 --- a/tests/tcg/mips/mips64-dspr2/head.S +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Startup Code for MIPS64 CPU-core - * - */ -.text -.globl _start -.align 4 -_start: - ori $2, $2, 0xffff - sll $2, $2, 16 - ori $2, $2, 0xffff - mtc0 $2, $12, 0 - jal main - -end: - b end diff --git a/tests/tcg/mips/mips64-dspr2/io.h b/tests/tcg/mips/mips64-dspr2/io.h deleted file mode 100644 index b7db61d7c1..0000000000 --- a/tests/tcg/mips/mips64-dspr2/io.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _ASM_IO_H -#define _ASM_IO_H -extern int printf(const char *fmt, ...); -extern unsigned long get_ticks(void); - -#define _read(source) \ -({ unsigned long __res; \ - __asm__ __volatile__( \ - "mfc0\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -#define __read(source) \ -({ unsigned long __res; \ - __asm__ __volatile__( \ - "move\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -#endif diff --git a/tests/tcg/mips/mips64-dspr2/mips_boot.lds b/tests/tcg/mips/mips64-dspr2/mips_boot.lds deleted file mode 100644 index bd7c0c0f3f..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mips_boot.lds +++ /dev/null @@ -1,31 +0,0 @@ -OUTPUT_ARCH(mips) -SECTIONS -{ - . = 0xffffffff80100000; - . = ALIGN((1 << 13)); - .text : - { - *(.text) - *(.rodata) - *(.rodata.*) - } - - __init_begin = .; - . = ALIGN((1 << 12)); - .init.text : AT(ADDR(.init.text) - 0) - { - *(.init.text) - } - .init.data : AT(ADDR(.init.data) - 0) - { - *(.init.data) - } - . = ALIGN((1 << 12)); - __init_end = .; - - . = ALIGN((1 << 13)); - .data : - { - *(.data) - } -} diff --git a/tests/tcg/mips/mips64-dspr2/mul_ph.c b/tests/tcg/mips/mips64-dspr2/mul_ph.c deleted file mode 100644 index 5a3d05cb29..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mul_ph.c +++ /dev/null @@ -1,50 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x03FB1234; - rt = 0x0BCC4321; - result = 0xFFFFFFFFF504F4B4; - resultdsp = 1; - - __asm - ("mul.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("mul.ph wrong\n"); - return -1; - } - - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - rs = 0x00210010; - rt = 0x00110005; - result = 0x2310050; - resultdsp = 0; - - __asm - ("mul.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("mul.ph wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/mul_s_ph.c b/tests/tcg/mips/mips64-dspr2/mul_s_ph.c deleted file mode 100644 index 7c8b2c718f..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mul_s_ph.c +++ /dev/null @@ -1,67 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x03FB1234; - rt = 0x0BCC4321; - result = 0x7fff7FFF; - resultdsp = 1; - - __asm - ("mul_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("1 mul_s.ph error\n"); - return -1; - } - - rs = 0x7fffff00; - rt = 0xff007fff; - result = 0xffffffff80008000; - resultdsp = 1; - - __asm - ("mul_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("2 mul_s.ph error\n"); - return -1; - } - - dsp = 0; - __asm - ("wrdsp %0\n\t" - : - : "r"(dsp) - ); - - rs = 0x00320001; - rt = 0x00210002; - result = 0x06720002; - resultdsp = 0; - - __asm - ("mul_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("3 mul_s.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/mulq_rs_w.c b/tests/tcg/mips/mips64-dspr2/mulq_rs_w.c deleted file mode 100644 index ffdc66d54a..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mulq_rs_w.c +++ /dev/null @@ -1,40 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x80001234; - rt = 0x80004321; - result = 0xFFFFFFFF80005555; - - __asm - ("mulq_rs.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("mulq_rs.w error!\n"); - return -1; - } - - rs = 0x80000000; - rt = 0x80000000; - result = 0x7FFFFFFF; - resultdsp = 1; - - __asm - ("mulq_rs.w %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("mulq_rs.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/mulq_s_ph.c b/tests/tcg/mips/mips64-dspr2/mulq_s_ph.c deleted file mode 100644 index b8c20c68cc..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mulq_s_ph.c +++ /dev/null @@ -1,26 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x80001234; - rt = 0x80004321; - result = 0x7FFF098B; - resultdsp = 1; - - __asm - ("mulq_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("mulq_s.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/mulq_s_w.c b/tests/tcg/mips/mips64-dspr2/mulq_s_w.c deleted file mode 100644 index db74b713f2..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mulq_s_w.c +++ /dev/null @@ -1,40 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x80001234; - rt = 0x80004321; - result = 0xFFFFFFFF80005555; - - __asm - ("mulq_s.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("mulq_s.w error\n"); - return -1; - } - - rs = 0x80000000; - rt = 0x80000000; - result = 0x7FFFFFFF; - resultdsp = 1; - - __asm - ("mulq_s.w %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 21) & 0x01; - if (rd != result || dsp != resultdsp) { - printf("mulq_s.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/mulsa_w_ph.c b/tests/tcg/mips/mips64-dspr2/mulsa_w_ph.c deleted file mode 100644 index 5b22a60a8d..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mulsa_w_ph.c +++ /dev/null @@ -1,30 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt, ach, acl; - long long resulth, resultl; - - ach = 0x05; - acl = 0x00BBDDCC; - rs = 0x80001234; - rt = 0x80004321; - resulth = 0x05; - resultl = 0x3BF5E918; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "mulsa.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if (ach != resulth || acl != resultl) { - printf("mulsa.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/mulsaq_s_w_ph.c b/tests/tcg/mips/mips64-dspr2/mulsaq_s_w_ph.c deleted file mode 100644 index 835a73d479..0000000000 --- a/tests/tcg/mips/mips64-dspr2/mulsaq_s_w_ph.c +++ /dev/null @@ -1,30 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt, ach, acl; - long long resulth, resultl; - - ach = 0x05; - acl = 0x00BBDDCC; - rs = 0x80001234; - rt = 0x80004321; - resulth = 0x05; - resultl = 0x772ff463; - - __asm - ("mthi %0, $ac1\n\t" - "mtlo %1, $ac1\n\t" - "mulsaq_s.w.ph $ac1, %2, %3\n\t" - "mfhi %0, $ac1\n\t" - "mflo %1, $ac1\n\t" - : "+r"(ach), "+r"(acl) - : "r"(rs), "r"(rt) - ); - if (ach != resulth || acl != resultl) { - printf("mulsaq_s.w.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/precr_qb_ph.c b/tests/tcg/mips/mips64-dspr2/precr_qb_ph.c deleted file mode 100644 index 80d5e8dce9..0000000000 --- a/tests/tcg/mips/mips64-dspr2/precr_qb_ph.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main() -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x34786521; - - __asm - ("precr.qb.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (result != rd) { - printf("precr.qb.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/precr_sra_ph_w.c b/tests/tcg/mips/mips64-dspr2/precr_sra_ph_w.c deleted file mode 100644 index b1d7bcdf8e..0000000000 --- a/tests/tcg/mips/mips64-dspr2/precr_sra_ph_w.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x43215678; - - __asm - ("precr_sra.ph.w %0, %1, 0x00\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (result != rt) { - printf("precr_sra.ph.w error\n"); - return -1; - } - - rs = 0x12345678; - rt = 0x87654321; - result = 0xFFFFFFFFFFFF0000; - - __asm - ("precr_sra.ph.w %0, %1, 0x1F\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (result != rt) { - printf("precr_sra.ph.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/precr_sra_r_ph_w.c b/tests/tcg/mips/mips64-dspr2/precr_sra_r_ph_w.c deleted file mode 100644 index 62d220dcae..0000000000 --- a/tests/tcg/mips/mips64-dspr2/precr_sra_r_ph_w.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x43215678; - - __asm - ("precr_sra_r.ph.w %0, %1, 0x00\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (result != rt) { - printf("precr_sra_r.ph.w error\n"); - return -1; - } - - rs = 0x12345678; - rt = 0x87654321; - result = 0xFFFFFFFFFFFF0000; - - __asm - ("precr_sra_r.ph.w %0, %1, 0x1F\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (result != rt) { - printf("precr_sra_r.ph.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/prepend.c b/tests/tcg/mips/mips64-dspr2/prepend.c deleted file mode 100644 index 4ab083e969..0000000000 --- a/tests/tcg/mips/mips64-dspr2/prepend.c +++ /dev/null @@ -1,35 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0xFFFFFFFF87654321; - __asm - ("prepend %0, %1, 0x00\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (rt != result) { - printf("prepend error\n"); - return -1; - } - - rs = 0x12345678; - rt = 0x87654321; - result = 0xFFFFFFFFACF10ECA; - __asm - ("prepend %0, %1, 0x0F\n\t" - : "+r"(rt) - : "r"(rs) - ); - if (rt != result) { - printf("prepend error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/printf.c b/tests/tcg/mips/mips64-dspr2/printf.c deleted file mode 100644 index cf8676d390..0000000000 --- a/tests/tcg/mips/mips64-dspr2/printf.c +++ /dev/null @@ -1,266 +0,0 @@ - -typedef unsigned long va_list; - -#define ACC 4 -#define __read(source) \ -({ va_list __res; \ - __asm__ __volatile__( \ - "move\t%0, " #source "\n\t" \ - : "=r" (__res)); \ - __res; \ -}) - -enum format_type { - FORMAT_TYPE_NONE, - FORMAT_TYPE_HEX, - FORMAT_TYPE_ULONG, - FORMAT_TYPE_FLOAT -}; - -struct printf_spec { - char type; -}; - -static int format_decode(char *fmt, struct printf_spec *spec) -{ - char *start = fmt; - - for (; *fmt ; ++fmt) { - if (*fmt == '%') { - break; - } - } - - switch (*++fmt) { - case 'x': - spec->type = FORMAT_TYPE_HEX; - break; - - case 'd': - spec->type = FORMAT_TYPE_ULONG; - break; - - case 'f': - spec->type = FORMAT_TYPE_FLOAT; - break; - - default: - spec->type = FORMAT_TYPE_NONE; - } - - return ++fmt - start; -} - -void *memcpy(void *dest, void *src, int n) -{ - int i; - char *s = src; - char *d = dest; - - for (i = 0; i < n; i++) { - d[i] = s[i]; - } - return dest; -} - -char *number(char *buf, va_list num) -{ - int i; - char *str = buf; - static char digits[16] = "0123456789abcdef"; - str = str + sizeof(num) * 2; - - for (i = 0; i < sizeof(num) * 2; i++) { - *--str = digits[num & 15]; - num >>= 4; - } - - return buf + sizeof(num) * 2; -} - -char *__number(char *buf, va_list num) -{ - int i; - va_list mm = num; - char *str = buf; - - if (!num) { - *str++ = '0'; - return str; - } - - for (i = 0; mm; mm = mm/10, i++) { - /* Do nothing. */ - } - - str = str + i; - - while (num) { - *--str = num % 10 + 48; - num = num / 10; - } - - return str + i; -} - -va_list modf(va_list args, va_list *integer, va_list *num) -{ - int i; - double dot_v = 0; - va_list E, DOT, DOT_V; - - if (!args) { - return 0; - } - - for (i = 0, args = args << 1 >> 1; i < 52; i++) { - if ((args >> i) & 0x1) { - break; - } - } - - *integer = 0; - - if ((args >> 56 != 0x3f) || (args >> 52 == 0x3ff)) { - E = (args >> 52) - 1023; - DOT = 52 - E - i; - DOT_V = args << (12 + E) >> (12 + E) >> i; - *integer = ((args << 12 >> 12) >> (i + DOT)) | (1 << E); - } else { - E = ~((args >> 52) - 1023) + 1; - DOT_V = args << 12 >> 12; - - dot_v += 1.0 / (1 << E); - - for (i = 1; i <= 16; i++) { - if ((DOT_V >> (52 - i)) & 0x1) { - dot_v += 1.0 / (1 << E + i); - } - } - - for (i = 1, E = 0; i <= ACC; i++) { - dot_v *= 10; - if (!(va_list)dot_v) { - E++; - } - } - - *num = E; - - return dot_v; - } - - if (args & 0xf) { - for (i = 1; i <= 16; i++) { - if ((DOT_V >> (DOT - i)) & 0x1) { - dot_v += 1.0 / (1 << i); - } - } - - for (i = 1, E = 0; i <= ACC; i++) { - dot_v *= 10; - if (!(va_list)dot_v) { - E++; - } - } - - *num = E; - - return dot_v; - } else if (DOT) { - for (i = 1; i <= DOT; i++) { - if ((DOT_V >> (DOT - i)) & 0x1) { - dot_v += 1.0 / (1 << i); - } - } - - for (i = 1; i <= ACC; i++) { - dot_v = dot_v * 10; - } - - return dot_v; - } - - return 0; -} - -int vsnprintf(char *buf, int size, char *fmt, va_list args) -{ - char *str, *mm; - struct printf_spec spec = {0}; - - str = mm = buf; - - while (*fmt) { - char *old_fmt = fmt; - int read = format_decode(fmt, &spec); - - fmt += read; - - switch (spec.type) { - case FORMAT_TYPE_NONE: { - memcpy(str, old_fmt, read); - str += read; - break; - } - case FORMAT_TYPE_HEX: { - memcpy(str, old_fmt, read); - str = number(str + read, args); - for (; *mm ; ++mm) { - if (*mm == '%') { - *mm = '0'; - break; - } - } - break; - } - case FORMAT_TYPE_ULONG: { - memcpy(str, old_fmt, read - 2); - str = __number(str + read - 2, args); - break; - } - case FORMAT_TYPE_FLOAT: { - va_list integer, dot_v, num; - dot_v = modf(args, &integer, &num); - memcpy(str, old_fmt, read - 2); - str += read - 2; - if ((args >> 63 & 0x1)) { - *str++ = '-'; - } - str = __number(str, integer); - if (dot_v) { - *str++ = '.'; - while (num--) { - *str++ = '0'; - } - str = __number(str, dot_v); - } - break; - } - } - } - *str = '\0'; - - return str - buf; -} - -static void serial_out(char *str) -{ - while (*str) { - *(char *)0xffffffffb80003f8 = *str++; - } -} - -int vprintf(char *fmt, va_list args) -{ - int printed_len = 0; - static char printf_buf[512]; - printed_len = vsnprintf(printf_buf, sizeof(printf_buf), fmt, args); - serial_out(printf_buf); - return printed_len; -} - -int printf(char *fmt, ...) -{ - return vprintf(fmt, __read($5)); -} diff --git a/tests/tcg/mips/mips64-dspr2/shra_qb.c b/tests/tcg/mips/mips64-dspr2/shra_qb.c deleted file mode 100644 index cac3102355..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shra_qb.c +++ /dev/null @@ -1,35 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x12345678; - result = 0x02060A0F; - - __asm - ("shra.qb %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra.qb error\n"); - return -1; - } - - rt = 0x87654321; - result = 0xFFFFFFFFF00C0804; - - __asm - ("shra.qb %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shra_r_qb.c b/tests/tcg/mips/mips64-dspr2/shra_r_qb.c deleted file mode 100644 index 9c64f75bd4..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shra_r_qb.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "io.h" - -int main() -{ - int rd, rt; - int result; - - rt = 0x12345678; - result = 0x02070B0F; - - __asm - ("shra_r.qb %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra_r.qb wrong\n"); - return -1; - } - - rt = 0x87654321; - result = 0xF10D0804; - - __asm - ("shra_r.qb %0, %1, 0x03\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shra_r.qb wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shrav_ob.c b/tests/tcg/mips/mips64-dspr2/shrav_ob.c deleted file mode 100644 index fbdfbab35a..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shrav_ob.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x1234567887654321; - rs = 0x4; - res = 0xf1f3f5f7f8060402; - - asm ("shrav.ob %0, %1, %2" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shra.ob error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shrav_qb.c b/tests/tcg/mips/mips64-dspr2/shrav_qb.c deleted file mode 100644 index a716203d80..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shrav_qb.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x03; - rt = 0x12345678; - result = 0x02060A0F; - - __asm - ("shrav.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav.qb error\n"); - return -1; - } - - rs = 0x03; - rt = 0x87654321; - result = 0xFFFFFFFFF00C0804; - - __asm - ("shrav.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shrav_r_ob.c b/tests/tcg/mips/mips64-dspr2/shrav_r_ob.c deleted file mode 100644 index b80100a7c2..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shrav_r_ob.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rt, rs; - long long res; - - rt = 0x1234567887654321; - rs = 0x4; - res = 0xe3e7ebf0f1ede9e5; - - asm ("shrav_r.ob %0, %1, %2" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - - if (rd != res) { - printf("shra_r.ob error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shrav_r_qb.c b/tests/tcg/mips/mips64-dspr2/shrav_r_qb.c deleted file mode 100644 index 009080b2a7..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shrav_r_qb.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x03; - rt = 0x12345678; - result = 0x02070B0F; - - __asm - ("shrav_r.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav_r.qb error\n"); - return -1; - } - - rs = 0x03; - rt = 0x87654321; - result = 0xFFFFFFFFF10D0804; - - __asm - ("shrav_r.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrav_r.qb error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shrl_ph.c b/tests/tcg/mips/mips64-dspr2/shrl_ph.c deleted file mode 100644 index e32d976625..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shrl_ph.c +++ /dev/null @@ -1,22 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rt; - long long result; - - rt = 0x12345678; - result = 0x009102B3; - - __asm - ("shrl.ph %0, %1, 0x05\n\t" - : "=r"(rd) - : "r"(rt) - ); - if (rd != result) { - printf("shrl.ph error!\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/shrlv_ph.c b/tests/tcg/mips/mips64-dspr2/shrlv_ph.c deleted file mode 100644 index 58c5488b58..0000000000 --- a/tests/tcg/mips/mips64-dspr2/shrlv_ph.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x05; - rt = 0x12345678; - result = 0x009102B3; - - __asm - ("shrlv.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rt), "r"(rs) - ); - if (rd != result) { - printf("shrlv.ph error!\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subqh_ph.c b/tests/tcg/mips/mips64-dspr2/subqh_ph.c deleted file mode 100644 index 90374019ae..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subqh_ph.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x456709AB; - - __asm - ("subqh.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("subqh.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subqh_r_ph.c b/tests/tcg/mips/mips64-dspr2/subqh_r_ph.c deleted file mode 100644 index b8f9d2fee6..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subqh_r_ph.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x456809AC; - - __asm - ("subqh_r.ph %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("subqh_r.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subqh_r_w.c b/tests/tcg/mips/mips64-dspr2/subqh_r_w.c deleted file mode 100644 index b025e40a35..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subqh_r_w.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main() -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x456789AC; - - __asm - ("subqh_r.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("subqh_r.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subqh_w.c b/tests/tcg/mips/mips64-dspr2/subqh_w.c deleted file mode 100644 index 65f17603d8..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subqh_w.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0x456789AB; - - __asm - ("subqh.w %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("subqh.w error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subu_ph.c b/tests/tcg/mips/mips64-dspr2/subu_ph.c deleted file mode 100644 index 60a6b1b7da..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subu_ph.c +++ /dev/null @@ -1,26 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x87654321; - rt = 0x12345678; - result = 0x7531ECA9; - resultdsp = 0x01; - - __asm - ("subu.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if (dsp != resultdsp || rd != result) { - printf("subu.ph error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subu_qh.c b/tests/tcg/mips/mips64-dspr2/subu_qh.c deleted file mode 100644 index 911cb349d4..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subu_qh.c +++ /dev/null @@ -1,24 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dspreg, result, dspresult; - rs = 0x123456789ABCDEF0; - rt = 0x123456789ABCDEF1; - result = 0x000000000000000F; - dspresult = 0x01; - - __asm("subu.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("subu.qh error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subu_s_ph.c b/tests/tcg/mips/mips64-dspr2/subu_s_ph.c deleted file mode 100644 index ae32cc06f5..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subu_s_ph.c +++ /dev/null @@ -1,25 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt, dsp; - long long result, resultdsp; - - rs = 0x87654321; - rt = 0x12345678; - result = 0x75310000; - resultdsp = 0x01; - - __asm - ("subu_s.ph %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dsp) - : "r"(rs), "r"(rt) - ); - dsp = (dsp >> 20) & 0x01; - if (dsp != resultdsp || rd != result) { - printf("subu_s.ph error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subu_s_qh.c b/tests/tcg/mips/mips64-dspr2/subu_s_qh.c deleted file mode 100644 index de7a29e775..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subu_s_qh.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, dspreg, result, dspresult; - rs = 0x1111111111111111; - rt = 0x2222222222222222; - result = 0x1111111111111111; - dspresult = 0x00; - - __asm("subu_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("subu_s.qh error\n\t"); - return -1; - } - - - rs = 0x8888888888888888; - rt = 0xa888a888a888a888; - result = 0x0000000000000000; - dspresult = 0x01; - - __asm("subu_s.qh %0, %2, %3\n\t" - "rddsp %1\n\t" - : "=r"(rd), "=r"(dspreg) - : "r"(rs), "r"(rt) - ); - - dspreg = ((dspreg >> 20) & 0x01); - if ((rd != result) || (dspreg != dspresult)) { - printf("subu_s.qh error\n\t"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subuh_ob.c b/tests/tcg/mips/mips64-dspr2/subuh_ob.c deleted file mode 100644 index 3fc452bf8e..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subuh_ob.c +++ /dev/null @@ -1,36 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - - rd = 0x0; - rs = 0x246856789ABCDEF0; - rt = 0x123456789ABCDEF0; - result = 0x091A000000000000; - - __asm("subuh.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("subuh.ob error\n"); - return -1; - } - - rs = 0x246856789ABCDEF0; - rt = 0x1131517191B1D1F1; - result = 0x1b4f2d2d51637577; - - __asm("subuh.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("subuh.ob error\n"); - return -1; - } - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subuh_qb.c b/tests/tcg/mips/mips64-dspr2/subuh_qb.c deleted file mode 100644 index aac7a834ee..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subuh_qb.c +++ /dev/null @@ -1,23 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0xC5E7092B; - - __asm - ("subuh.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("subuh.qb wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subuh_r_ob.c b/tests/tcg/mips/mips64-dspr2/subuh_r_ob.c deleted file mode 100644 index fc20ffd09e..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subuh_r_ob.c +++ /dev/null @@ -1,23 +0,0 @@ -#include "io.h" - -int main(void) -{ - long long rd, rs, rt, result; - - rd = 0x0; - rs = 0x246956789ABCDEF0; - rt = 0x123456789ABCDEF0; - result = 0x091B000000000000; - - __asm("subuh.ob %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - - if (rd != result) { - printf("subuh.ob error\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/mips64-dspr2/subuh_r_qb.c b/tests/tcg/mips/mips64-dspr2/subuh_r_qb.c deleted file mode 100644 index 66d4680440..0000000000 --- a/tests/tcg/mips/mips64-dspr2/subuh_r_qb.c +++ /dev/null @@ -1,37 +0,0 @@ -#include"io.h" - -int main(void) -{ - long long rd, rs, rt; - long long result; - - rs = 0x12345678; - rt = 0x87654321; - result = 0xC6E80A2C; - - __asm - ("subuh_r.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("1 subuh_r.qb wrong\n"); - return -1; - } - - rs = 0xBEFC292A; - rt = 0x9205C1B4; - result = 0x167cb4bb; - - __asm - ("subuh_r.qb %0, %1, %2\n\t" - : "=r"(rd) - : "r"(rs), "r"(rt) - ); - if (rd != result) { - printf("2 subuh_r.qb wrong\n"); - return -1; - } - - return 0; -} diff --git a/tests/tcg/mips/user/ase/dsp/Makefile b/tests/tcg/mips/user/ase/dsp/Makefile new file mode 100644 index 0000000000..5c6da96870 --- /dev/null +++ b/tests/tcg/mips/user/ase/dsp/Makefile @@ -0,0 +1,184 @@ +-include ../../../../config-host.mak + +CROSS=mips64el-unknown-linux-gnu- + +SIM=qemu-mipsel +SIM_FLAGS=-cpu 74Kf + +CC = $(CROSS)gcc +CFLAGS = -EL -mabi=32 -march=mips32r2 -mgp32 -mdsp -mdspr2 -static + +TESTCASES = test_dsp_r1_absq_s_ph.tst +TESTCASES += test_dsp_r1_absq_s_w.tst +TESTCASES += test_dsp_r1_addq_ph.tst +TESTCASES += test_dsp_r1_addq_s_ph.tst +TESTCASES += test_dsp_r1_addq_s_w.tst +TESTCASES += test_dsp_r1_addsc.tst +TESTCASES += test_dsp_r1_addu_qb.tst +TESTCASES += test_dsp_r1_addu_s_qb.tst +TESTCASES += test_dsp_r1_addwc.tst +TESTCASES += test_dsp_r1_bitrev.tst +TESTCASES += test_dsp_r1_bposge32.tst +TESTCASES += test_dsp_r1_cmp_eq_ph.tst +TESTCASES += test_dsp_r1_cmpgu_eq_qb.tst +TESTCASES += test_dsp_r1_cmpgu_le_qb.tst +TESTCASES += test_dsp_r1_cmpgu_lt_qb.tst +TESTCASES += test_dsp_r1_cmp_le_ph.tst +TESTCASES += test_dsp_r1_cmp_lt_ph.tst +TESTCASES += test_dsp_r1_cmpu_eq_qb.tst +TESTCASES += test_dsp_r1_cmpu_le_qb.tst +TESTCASES += test_dsp_r1_cmpu_lt_qb.tst +TESTCASES += test_dsp_r1_dpaq_sa_l_w.tst +TESTCASES += test_dsp_r1_dpaq_s_w_ph.tst +TESTCASES += test_dsp_r1_dpau_h_qbl.tst +TESTCASES += test_dsp_r1_dpau_h_qbr.tst +TESTCASES += test_dsp_r1_dpsq_sa_l_w.tst +TESTCASES += test_dsp_r1_dpsq_s_w_ph.tst +TESTCASES += test_dsp_r1_dpsu_h_qbl.tst +TESTCASES += test_dsp_r1_dpsu_h_qbr.tst +TESTCASES += test_dsp_r1_extp.tst +TESTCASES += test_dsp_r1_extpdp.tst +TESTCASES += test_dsp_r1_extpdpv.tst +TESTCASES += test_dsp_r1_extpv.tst +TESTCASES += test_dsp_r1_extr_rs_w.tst +TESTCASES += test_dsp_r1_extr_r_w.tst +TESTCASES += test_dsp_r1_extr_s_h.tst +TESTCASES += test_dsp_r1_extrv_rs_w.tst +TESTCASES += test_dsp_r1_extrv_r_w.tst +TESTCASES += test_dsp_r1_extrv_s_h.tst +TESTCASES += test_dsp_r1_extrv_w.tst +TESTCASES += test_dsp_r1_extr_w.tst +TESTCASES += test_dsp_r1_insv.tst +TESTCASES += test_dsp_r1_lbux.tst +TESTCASES += test_dsp_r1_lhx.tst +TESTCASES += test_dsp_r1_lwx.tst +TESTCASES += test_dsp_r1_madd.tst +TESTCASES += test_dsp_r1_maddu.tst +TESTCASES += test_dsp_r1_maq_sa_w_phl.tst +TESTCASES += test_dsp_r1_maq_sa_w_phr.tst +TESTCASES += test_dsp_r1_maq_s_w_phl.tst +TESTCASES += test_dsp_r1_maq_s_w_phr.tst +TESTCASES += test_dsp_r1_mfhi.tst +TESTCASES += test_dsp_r1_mflo.tst +TESTCASES += test_dsp_r1_modsub.tst +TESTCASES += test_dsp_r1_msub.tst +TESTCASES += test_dsp_r1_msubu.tst +TESTCASES += test_dsp_r1_mthi.tst +TESTCASES += test_dsp_r1_mthlip.tst +TESTCASES += test_dsp_r1_mtlo.tst +TESTCASES += test_dsp_r1_muleq_s_w_phl.tst +TESTCASES += test_dsp_r1_muleq_s_w_phr.tst +TESTCASES += test_dsp_r1_muleu_s_ph_qbl.tst +TESTCASES += test_dsp_r1_muleu_s_ph_qbr.tst +TESTCASES += test_dsp_r1_mulq_rs_ph.tst +TESTCASES += test_dsp_r1_mult.tst +TESTCASES += test_dsp_r1_multu.tst +TESTCASES += test_dsp_r1_packrl_ph.tst +TESTCASES += test_dsp_r1_pick_ph.tst +TESTCASES += test_dsp_r1_pick_qb.tst +TESTCASES += test_dsp_r1_precequ_ph_qbla.tst +TESTCASES += test_dsp_r1_precequ_ph_qbl.tst +TESTCASES += test_dsp_r1_precequ_ph_qbra.tst +TESTCASES += test_dsp_r1_precequ_ph_qbr.tst +TESTCASES += test_dsp_r1_preceq_w_phl.tst +TESTCASES += test_dsp_r1_preceq_w_phr.tst +TESTCASES += test_dsp_r1_preceu_ph_qbla.tst +TESTCASES += test_dsp_r1_preceu_ph_qbl.tst +TESTCASES += test_dsp_r1_preceu_ph_qbra.tst +TESTCASES += test_dsp_r1_preceu_ph_qbr.tst +TESTCASES += test_dsp_r1_precrq_ph_w.tst +TESTCASES += test_dsp_r1_precrq_qb_ph.tst +TESTCASES += test_dsp_r1_precrq_rs_ph_w.tst +TESTCASES += test_dsp_r1_precrqu_s_qb_ph.tst +TESTCASES += test_dsp_r1_raddu_w_qb.tst +TESTCASES += test_dsp_r1_rddsp.tst +TESTCASES += test_dsp_r1_repl_ph.tst +TESTCASES += test_dsp_r1_repl_qb.tst +TESTCASES += test_dsp_r1_replv_ph.tst +TESTCASES += test_dsp_r1_replv_qb.tst +TESTCASES += test_dsp_r1_shilo.tst +TESTCASES += test_dsp_r1_shilov.tst +TESTCASES += test_dsp_r1_shll_ph.tst +TESTCASES += test_dsp_r1_shll_qb.tst +TESTCASES += test_dsp_r1_shll_s_ph.tst +TESTCASES += test_dsp_r1_shll_s_w.tst +TESTCASES += test_dsp_r1_shllv_ph.tst +TESTCASES += test_dsp_r1_shllv_qb.tst +TESTCASES += test_dsp_r1_shllv_s_ph.tst +TESTCASES += test_dsp_r1_shllv_s_w.tst +TESTCASES += test_dsp_r1_shra_ph.tst +TESTCASES += test_dsp_r1_shra_r_ph.tst +TESTCASES += test_dsp_r1_shra_r_w.tst +TESTCASES += test_dsp_r1_shrav_ph.tst +TESTCASES += test_dsp_r1_shrav_r_ph.tst +TESTCASES += test_dsp_r1_shrav_r_w.tst +TESTCASES += test_dsp_r1_shrl_qb.tst +TESTCASES += test_dsp_r1_shrlv_qb.tst +TESTCASES += test_dsp_r1_subq_ph.tst +TESTCASES += test_dsp_r1_subq_s_ph.tst +TESTCASES += test_dsp_r1_subq_s_w.tst +TESTCASES += test_dsp_r1_subu_qb.tst +TESTCASES += test_dsp_r1_subu_s_qb.tst +TESTCASES += test_dsp_r1_wrdsp.tst +TESTCASES += test_dsp_r2_absq_s_qb.tst +TESTCASES += test_dsp_r2_addqh_ph.tst +TESTCASES += test_dsp_r2_addqh_r_ph.tst +TESTCASES += test_dsp_r2_addqh_r_w.tst +TESTCASES += test_dsp_r2_addqh_w.tst +TESTCASES += test_dsp_r2_adduh_qb.tst +TESTCASES += test_dsp_r2_adduh_r_qb.tst +TESTCASES += test_dsp_r2_addu_ph.tst +TESTCASES += test_dsp_r2_addu_s_ph.tst +TESTCASES += test_dsp_r2_append.tst +TESTCASES += test_dsp_r2_balign.tst +TESTCASES += test_dsp_r2_cmpgdu_eq_qb.tst +TESTCASES += test_dsp_r2_cmpgdu_le_qb.tst +TESTCASES += test_dsp_r2_cmpgdu_lt_qb.tst +TESTCASES += test_dsp_r2_dpaqx_sa_w_ph.tst +TESTCASES += test_dsp_r2_dpa_w_ph.tst +TESTCASES += test_dsp_r2_dpax_w_ph.tst +TESTCASES += test_dsp_r2_dpaqx_s_w_ph.tst +TESTCASES += test_dsp_r2_dpsqx_sa_w_ph.tst +TESTCASES += test_dsp_r2_dpsqx_s_w_ph.tst +TESTCASES += test_dsp_r2_dps_w_ph.tst +TESTCASES += test_dsp_r2_dpsx_w_ph.tst +TESTCASES += test_dsp_r2_mul_ph.tst +TESTCASES += test_dsp_r2_mulq_rs_w.tst +TESTCASES += test_dsp_r2_mulq_s_ph.tst +TESTCASES += test_dsp_r2_mulq_s_w.tst +TESTCASES += test_dsp_r2_mulsaq_s_w_ph.tst +TESTCASES += test_dsp_r2_mulsa_w_ph.tst +TESTCASES += test_dsp_r2_mul_s_ph.tst +TESTCASES += test_dsp_r2_precr_qb_ph.tst +TESTCASES += test_dsp_r2_precr_sra_ph_w.tst +TESTCASES += test_dsp_r2_precr_sra_r_ph_w.tst +TESTCASES += test_dsp_r2_prepend.tst +TESTCASES += test_dsp_r2_shra_qb.tst +TESTCASES += test_dsp_r2_shra_r_qb.tst +TESTCASES += test_dsp_r2_shrav_qb.tst +TESTCASES += test_dsp_r2_shrav_r_qb.tst +TESTCASES += test_dsp_r2_shrl_ph.tst +TESTCASES += test_dsp_r2_shrlv_ph.tst +TESTCASES += test_dsp_r2_subqh_ph.tst +TESTCASES += test_dsp_r2_subqh_r_ph.tst +TESTCASES += test_dsp_r2_subqh_r_w.tst +TESTCASES += test_dsp_r2_subqh_w.tst +TESTCASES += test_dsp_r2_subuh_qb.tst +TESTCASES += test_dsp_r2_subuh_r_qb.tst +TESTCASES += test_dsp_r2_subu_ph.tst +TESTCASES += test_dsp_r2_subu_s_ph.tst + + +all: $(TESTCASES) + +%.tst: %.c + $(CC) $(CFLAGS) $< -o $@ + +check: $(TESTCASES) + @for case in $(TESTCASES); do \ + echo $(SIM) $(SIM_FLAGS) ./$$case;\ + $(SIM) $(SIM_FLAGS) ./$$case; \ + done + +clean: + $(RM) -rf $(TESTCASES) diff --git a/tests/tcg/mips/mips32-dsp/absq_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_absq_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/absq_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_absq_s_ph.c diff --git a/tests/tcg/mips/mips32-dsp/absq_s_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_absq_s_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/absq_s_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_absq_s_w.c diff --git a/tests/tcg/mips/mips32-dsp/addq_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addq_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addq_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addq_ph.c diff --git a/tests/tcg/mips/mips32-dsp/addq_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addq_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addq_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addq_s_ph.c diff --git a/tests/tcg/mips/mips32-dsp/addq_s_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addq_s_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addq_s_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addq_s_w.c diff --git a/tests/tcg/mips/mips32-dsp/addsc.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addsc.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addsc.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addsc.c diff --git a/tests/tcg/mips/mips32-dsp/addu_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addu_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addu_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addu_qb.c diff --git a/tests/tcg/mips/mips32-dsp/addu_s_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addu_s_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addu_s_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addu_s_qb.c diff --git a/tests/tcg/mips/mips32-dsp/addwc.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_addwc.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/addwc.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_addwc.c diff --git a/tests/tcg/mips/mips32-dsp/bitrev.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_bitrev.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/bitrev.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_bitrev.c diff --git a/tests/tcg/mips/mips32-dsp/bposge32.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_bposge32.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/bposge32.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_bposge32.c diff --git a/tests/tcg/mips/mips32-dsp/cmp_eq_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmp_eq_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmp_eq_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmp_eq_ph.c diff --git a/tests/tcg/mips/mips32-dsp/cmp_le_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmp_le_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmp_le_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmp_le_ph.c diff --git a/tests/tcg/mips/mips32-dsp/cmp_lt_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmp_lt_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmp_lt_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmp_lt_ph.c diff --git a/tests/tcg/mips/mips32-dsp/cmpgu_eq_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpgu_eq_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmpgu_eq_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpgu_eq_qb.c diff --git a/tests/tcg/mips/mips32-dsp/cmpgu_le_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpgu_le_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmpgu_le_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpgu_le_qb.c diff --git a/tests/tcg/mips/mips32-dsp/cmpgu_lt_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpgu_lt_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmpgu_lt_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpgu_lt_qb.c diff --git a/tests/tcg/mips/mips32-dsp/cmpu_eq_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpu_eq_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmpu_eq_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpu_eq_qb.c diff --git a/tests/tcg/mips/mips32-dsp/cmpu_le_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpu_le_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmpu_le_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpu_le_qb.c diff --git a/tests/tcg/mips/mips32-dsp/cmpu_lt_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpu_lt_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/cmpu_lt_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_cmpu_lt_qb.c diff --git a/tests/tcg/mips/mips32-dsp/dpaq_s_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpaq_s_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpaq_s_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpaq_s_w_ph.c diff --git a/tests/tcg/mips/mips32-dsp/dpaq_sa_l_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpaq_sa_l_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpaq_sa_l_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpaq_sa_l_w.c diff --git a/tests/tcg/mips/mips32-dsp/dpau_h_qbl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpau_h_qbl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpau_h_qbl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpau_h_qbl.c diff --git a/tests/tcg/mips/mips32-dsp/dpau_h_qbr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpau_h_qbr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpau_h_qbr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpau_h_qbr.c diff --git a/tests/tcg/mips/mips32-dsp/dpsq_s_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsq_s_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpsq_s_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsq_s_w_ph.c diff --git a/tests/tcg/mips/mips32-dsp/dpsq_sa_l_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsq_sa_l_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpsq_sa_l_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsq_sa_l_w.c diff --git a/tests/tcg/mips/mips32-dsp/dpsu_h_qbl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsu_h_qbl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpsu_h_qbl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsu_h_qbl.c diff --git a/tests/tcg/mips/mips32-dsp/dpsu_h_qbr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsu_h_qbr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/dpsu_h_qbr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_dpsu_h_qbr.c diff --git a/tests/tcg/mips/mips32-dsp/extp.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extp.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extp.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extp.c diff --git a/tests/tcg/mips/mips32-dsp/extpdp.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extpdp.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extpdp.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extpdp.c diff --git a/tests/tcg/mips/mips32-dsp/extpdpv.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extpdpv.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extpdpv.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extpdpv.c diff --git a/tests/tcg/mips/mips32-dsp/extpv.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extpv.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extpv.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extpv.c diff --git a/tests/tcg/mips/mips32-dsp/extr_r_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_r_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extr_r_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_r_w.c diff --git a/tests/tcg/mips/mips32-dsp/extr_rs_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_rs_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extr_rs_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_rs_w.c diff --git a/tests/tcg/mips/mips32-dsp/extr_s_h.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_s_h.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extr_s_h.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_s_h.c diff --git a/tests/tcg/mips/mips32-dsp/extr_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extr_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extr_w.c diff --git a/tests/tcg/mips/mips32-dsp/extrv_r_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_r_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extrv_r_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_r_w.c diff --git a/tests/tcg/mips/mips32-dsp/extrv_rs_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_rs_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extrv_rs_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_rs_w.c diff --git a/tests/tcg/mips/mips32-dsp/extrv_s_h.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_s_h.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extrv_s_h.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_s_h.c diff --git a/tests/tcg/mips/mips32-dsp/extrv_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/extrv_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_extrv_w.c diff --git a/tests/tcg/mips/mips32-dsp/insv.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_insv.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/insv.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_insv.c diff --git a/tests/tcg/mips/mips32-dsp/lbux.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_lbux.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/lbux.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_lbux.c diff --git a/tests/tcg/mips/mips32-dsp/lhx.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_lhx.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/lhx.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_lhx.c diff --git a/tests/tcg/mips/mips32-dsp/lwx.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_lwx.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/lwx.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_lwx.c diff --git a/tests/tcg/mips/mips32-dsp/madd.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_madd.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/madd.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_madd.c diff --git a/tests/tcg/mips/mips32-dsp/maddu.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_maddu.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/maddu.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_maddu.c diff --git a/tests/tcg/mips/mips32-dsp/main.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_main.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/main.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_main.c diff --git a/tests/tcg/mips/mips32-dsp/maq_s_w_phl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_s_w_phl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/maq_s_w_phl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_s_w_phl.c diff --git a/tests/tcg/mips/mips32-dsp/maq_s_w_phr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_s_w_phr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/maq_s_w_phr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_s_w_phr.c diff --git a/tests/tcg/mips/mips32-dsp/maq_sa_w_phl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_sa_w_phl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/maq_sa_w_phl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_sa_w_phl.c diff --git a/tests/tcg/mips/mips32-dsp/maq_sa_w_phr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_sa_w_phr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/maq_sa_w_phr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_maq_sa_w_phr.c diff --git a/tests/tcg/mips/mips32-dsp/mfhi.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mfhi.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mfhi.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mfhi.c diff --git a/tests/tcg/mips/mips32-dsp/mflo.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mflo.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mflo.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mflo.c diff --git a/tests/tcg/mips/mips32-dsp/modsub.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_modsub.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/modsub.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_modsub.c diff --git a/tests/tcg/mips/mips32-dsp/msub.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_msub.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/msub.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_msub.c diff --git a/tests/tcg/mips/mips32-dsp/msubu.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_msubu.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/msubu.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_msubu.c diff --git a/tests/tcg/mips/mips32-dsp/mthi.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mthi.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mthi.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mthi.c diff --git a/tests/tcg/mips/mips32-dsp/mthlip.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mthlip.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mthlip.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mthlip.c diff --git a/tests/tcg/mips/mips32-dsp/mtlo.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mtlo.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mtlo.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mtlo.c diff --git a/tests/tcg/mips/mips32-dsp/muleq_s_w_phl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleq_s_w_phl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/muleq_s_w_phl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleq_s_w_phl.c diff --git a/tests/tcg/mips/mips32-dsp/muleq_s_w_phr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleq_s_w_phr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/muleq_s_w_phr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleq_s_w_phr.c diff --git a/tests/tcg/mips/mips32-dsp/muleu_s_ph_qbl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleu_s_ph_qbl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/muleu_s_ph_qbl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleu_s_ph_qbl.c diff --git a/tests/tcg/mips/mips32-dsp/muleu_s_ph_qbr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleu_s_ph_qbr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/muleu_s_ph_qbr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_muleu_s_ph_qbr.c diff --git a/tests/tcg/mips/mips32-dsp/mulq_rs_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mulq_rs_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mulq_rs_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mulq_rs_ph.c diff --git a/tests/tcg/mips/mips32-dsp/mult.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_mult.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/mult.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_mult.c diff --git a/tests/tcg/mips/mips32-dsp/multu.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_multu.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/multu.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_multu.c diff --git a/tests/tcg/mips/mips32-dsp/packrl_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_packrl_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/packrl_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_packrl_ph.c diff --git a/tests/tcg/mips/mips32-dsp/pick_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_pick_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/pick_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_pick_ph.c diff --git a/tests/tcg/mips/mips32-dsp/pick_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_pick_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/pick_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_pick_qb.c diff --git a/tests/tcg/mips/mips32-dsp/preceq_w_phl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceq_w_phl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/preceq_w_phl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceq_w_phl.c diff --git a/tests/tcg/mips/mips32-dsp/preceq_w_phr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceq_w_phr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/preceq_w_phr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceq_w_phr.c diff --git a/tests/tcg/mips/mips32-dsp/precequ_ph_qbl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precequ_ph_qbl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbl.c diff --git a/tests/tcg/mips/mips32-dsp/precequ_ph_qbla.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbla.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precequ_ph_qbla.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbla.c diff --git a/tests/tcg/mips/mips32-dsp/precequ_ph_qbr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precequ_ph_qbr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbr.c diff --git a/tests/tcg/mips/mips32-dsp/precequ_ph_qbra.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbra.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precequ_ph_qbra.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precequ_ph_qbra.c diff --git a/tests/tcg/mips/mips32-dsp/preceu_ph_qbl.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbl.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/preceu_ph_qbl.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbl.c diff --git a/tests/tcg/mips/mips32-dsp/preceu_ph_qbla.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbla.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/preceu_ph_qbla.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbla.c diff --git a/tests/tcg/mips/mips32-dsp/preceu_ph_qbr.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbr.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/preceu_ph_qbr.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbr.c diff --git a/tests/tcg/mips/mips32-dsp/preceu_ph_qbra.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbra.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/preceu_ph_qbra.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_preceu_ph_qbra.c diff --git a/tests/tcg/mips/mips32-dsp/precrq_ph_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrq_ph_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precrq_ph_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrq_ph_w.c diff --git a/tests/tcg/mips/mips32-dsp/precrq_qb_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrq_qb_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precrq_qb_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrq_qb_ph.c diff --git a/tests/tcg/mips/mips32-dsp/precrq_rs_ph_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrq_rs_ph_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precrq_rs_ph_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrq_rs_ph_w.c diff --git a/tests/tcg/mips/mips32-dsp/precrqu_s_qb_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrqu_s_qb_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/precrqu_s_qb_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_precrqu_s_qb_ph.c diff --git a/tests/tcg/mips/mips32-dsp/raddu_w_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_raddu_w_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/raddu_w_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_raddu_w_qb.c diff --git a/tests/tcg/mips/mips32-dsp/rddsp.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_rddsp.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/rddsp.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_rddsp.c diff --git a/tests/tcg/mips/mips32-dsp/repl_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_repl_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/repl_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_repl_ph.c diff --git a/tests/tcg/mips/mips32-dsp/repl_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_repl_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/repl_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_repl_qb.c diff --git a/tests/tcg/mips/mips32-dsp/replv_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_replv_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/replv_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_replv_ph.c diff --git a/tests/tcg/mips/mips32-dsp/replv_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_replv_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/replv_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_replv_qb.c diff --git a/tests/tcg/mips/mips32-dsp/shilo.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shilo.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shilo.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shilo.c diff --git a/tests/tcg/mips/mips32-dsp/shilov.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shilov.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shilov.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shilov.c diff --git a/tests/tcg/mips/mips32-dsp/shll_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shll_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shll_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shll_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_qb.c diff --git a/tests/tcg/mips/mips32-dsp/shll_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shll_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_s_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shll_s_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_s_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shll_s_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shll_s_w.c diff --git a/tests/tcg/mips/mips32-dsp/shllv_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shllv_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shllv_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shllv_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_qb.c diff --git a/tests/tcg/mips/mips32-dsp/shllv_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shllv_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_s_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shllv_s_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_s_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shllv_s_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shllv_s_w.c diff --git a/tests/tcg/mips/mips32-dsp/shra_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shra_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shra_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shra_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shra_r_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shra_r_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shra_r_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shra_r_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shra_r_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shra_r_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shra_r_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shra_r_w.c diff --git a/tests/tcg/mips/mips32-dsp/shrav_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrav_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shrav_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrav_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shrav_r_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrav_r_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shrav_r_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrav_r_ph.c diff --git a/tests/tcg/mips/mips32-dsp/shrav_r_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrav_r_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shrav_r_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrav_r_w.c diff --git a/tests/tcg/mips/mips32-dsp/shrl_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrl_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shrl_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrl_qb.c diff --git a/tests/tcg/mips/mips32-dsp/shrlv_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrlv_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/shrlv_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_shrlv_qb.c diff --git a/tests/tcg/mips/mips32-dsp/subq_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_subq_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/subq_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_subq_ph.c diff --git a/tests/tcg/mips/mips32-dsp/subq_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_subq_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/subq_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_subq_s_ph.c diff --git a/tests/tcg/mips/mips32-dsp/subq_s_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_subq_s_w.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/subq_s_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_subq_s_w.c diff --git a/tests/tcg/mips/mips32-dsp/subu_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_subu_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/subu_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_subu_qb.c diff --git a/tests/tcg/mips/mips32-dsp/subu_s_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_subu_s_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/subu_s_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_subu_s_qb.c diff --git a/tests/tcg/mips/mips32-dsp/wrdsp.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r1_wrdsp.c similarity index 100% rename from tests/tcg/mips/mips32-dsp/wrdsp.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r1_wrdsp.c diff --git a/tests/tcg/mips/mips32-dspr2/absq_s_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_absq_s_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/absq_s_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_absq_s_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/addqh_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/addqh_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/addqh_r_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_r_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/addqh_r_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_r_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/addqh_r_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_r_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/addqh_r_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_r_w.c diff --git a/tests/tcg/mips/mips32-dspr2/addqh_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/addqh_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_addqh_w.c diff --git a/tests/tcg/mips/mips32-dspr2/addu_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_addu_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/addu_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_addu_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/addu_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_addu_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/addu_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_addu_s_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/adduh_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_adduh_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/adduh_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_adduh_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/adduh_r_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_adduh_r_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/adduh_r_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_adduh_r_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/append.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_append.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/append.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_append.c diff --git a/tests/tcg/mips/mips32-dspr2/balign.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_balign.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/balign.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_balign.c diff --git a/tests/tcg/mips/mips32-dspr2/cmpgdu_eq_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_cmpgdu_eq_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/cmpgdu_eq_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_cmpgdu_eq_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/cmpgdu_le_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_cmpgdu_le_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/cmpgdu_le_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_cmpgdu_le_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/cmpgdu_lt_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_cmpgdu_lt_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/cmpgdu_lt_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_cmpgdu_lt_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/dpa_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpa_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpa_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpa_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dpaqx_s_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpaqx_s_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpaqx_s_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpaqx_s_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dpaqx_sa_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpaqx_sa_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpaqx_sa_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpaqx_sa_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dpax_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpax_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpax_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpax_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dps_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dps_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dps_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dps_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dpsqx_s_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpsqx_s_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpsqx_s_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpsqx_s_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dpsqx_sa_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpsqx_sa_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpsqx_sa_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpsqx_sa_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/dpsx_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpsx_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/dpsx_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_dpsx_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/mul_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mul_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mul_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mul_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/mul_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mul_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mul_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mul_s_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/mulq_rs_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulq_rs_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mulq_rs_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulq_rs_w.c diff --git a/tests/tcg/mips/mips32-dspr2/mulq_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulq_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mulq_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulq_s_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/mulq_s_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulq_s_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mulq_s_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulq_s_w.c diff --git a/tests/tcg/mips/mips32-dspr2/mulsa_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulsa_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mulsa_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulsa_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/mulsaq_s_w_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulsaq_s_w_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/mulsaq_s_w_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_mulsaq_s_w_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/precr_qb_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_precr_qb_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/precr_qb_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_precr_qb_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/precr_sra_ph_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_precr_sra_ph_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/precr_sra_ph_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_precr_sra_ph_w.c diff --git a/tests/tcg/mips/mips32-dspr2/precr_sra_r_ph_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_precr_sra_r_ph_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/precr_sra_r_ph_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_precr_sra_r_ph_w.c diff --git a/tests/tcg/mips/mips32-dspr2/prepend.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_prepend.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/prepend.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_prepend.c diff --git a/tests/tcg/mips/mips32-dspr2/shra_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_shra_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/shra_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_shra_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/shra_r_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_shra_r_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/shra_r_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_shra_r_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/shrav_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrav_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/shrav_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrav_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/shrav_r_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrav_r_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/shrav_r_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrav_r_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/shrl_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrl_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/shrl_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrl_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/shrlv_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrlv_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/shrlv_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_shrlv_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/subqh_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subqh_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/subqh_r_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_r_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subqh_r_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_r_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/subqh_r_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_r_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subqh_r_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_r_w.c diff --git a/tests/tcg/mips/mips32-dspr2/subqh_w.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_w.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subqh_w.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subqh_w.c diff --git a/tests/tcg/mips/mips32-dspr2/subu_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subu_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subu_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subu_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/subu_s_ph.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subu_s_ph.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subu_s_ph.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subu_s_ph.c diff --git a/tests/tcg/mips/mips32-dspr2/subuh_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subuh_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subuh_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subuh_qb.c diff --git a/tests/tcg/mips/mips32-dspr2/subuh_r_qb.c b/tests/tcg/mips/user/ase/dsp/test_dsp_r2_subuh_r_qb.c similarity index 100% rename from tests/tcg/mips/mips32-dspr2/subuh_r_qb.c rename to tests/tcg/mips/user/ase/dsp/test_dsp_r2_subuh_r_qb.c diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_b.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_b.c new file mode 100644 index 0000000000..c73ed2464e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_b.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLOC.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLOC.B"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0808080808080808ULL, 0x0808080808080808ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0202020202020202ULL, 0x0202020202020202ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0301000301000301ULL, 0x0003010003010003ULL, }, + { 0x0000020000020000ULL, 0x0200000200000200ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0500000103050000ULL, 0x0103050000010305ULL, }, + { 0x0002040000000204ULL, 0x0000000204000000ULL, }, + { 0x0600020600020600ULL, 0x0206000206000206ULL, }, + { 0x0004000004000004ULL, 0x0000040000040000ULL, }, + { 0x0700050003000107ULL, 0x0005000300010700ULL, }, + { 0x0006000400020000ULL, 0x0600040002000006ULL, }, + { 0x0800080008000800ULL, 0x0800080008000800ULL, }, /* 16 */ + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0801000300050007ULL, 0x0008010003000500ULL, }, + { 0x0000020004000600ULL, 0x0800000200040006ULL, }, + { 0x0802000600080200ULL, 0x0600080200060008ULL, }, + { 0x0000040008000004ULL, 0x0008000004000800ULL, }, + { 0x0803000801000700ULL, 0x0005000803000801ULL, }, + { 0x0000060000040008ULL, 0x0200080000060000ULL, }, + { 0x0804000804000804ULL, 0x0008040008040008ULL, }, /* 24 */ + { 0x0000080000080000ULL, 0x0800000800000800ULL, }, + { 0x0805000007000008ULL, 0x0100080300080500ULL, }, + { 0x0000080200080400ULL, 0x0006000008000008ULL, }, + { 0x0806000008020008ULL, 0x0600000802000806ULL, }, + { 0x0000080400000800ULL, 0x0008040000080000ULL, }, + { 0x0807000008050000ULL, 0x0803000008010008ULL, }, + { 0x0000080600000804ULL, 0x0000080200000800ULL, }, + { 0x0808000008080000ULL, 0x0808000008080000ULL, }, /* 32 */ + { 0x0000080800000808ULL, 0x0000080800000808ULL, }, + { 0x0808010000080300ULL, 0x0008050000080700ULL, }, + { 0x0000000802000008ULL, 0x0400000806000008ULL, }, + { 0x0808020000080600ULL, 0x0008080200000806ULL, }, + { 0x0000000804000008ULL, 0x0800000008040000ULL, }, + { 0x0808030000080801ULL, 0x0000080700000008ULL, }, + { 0x0000000806000000ULL, 0x0804000008080200ULL, }, + { 0x0808040000080804ULL, 0x0000080804000008ULL, }, /* 40 */ + { 0x0000000808000000ULL, 0x0808000000080800ULL, }, + { 0x0808050000000807ULL, 0x0000000808010000ULL, }, + { 0x0000000808020000ULL, 0x0808040000000806ULL, }, + { 0x0808060000000808ULL, 0x0200000808060000ULL, }, + { 0x0000000808040000ULL, 0x0008080000000808ULL, }, + { 0x0808070000000808ULL, 0x0500000008080300ULL, }, + { 0x0000000808060000ULL, 0x0008080400000008ULL, }, + { 0x0808080000000808ULL, 0x0800000008080800ULL, }, /* 48 */ + { 0x0000000808080000ULL, 0x0008080800000008ULL, }, + { 0x0808080100000008ULL, 0x0803000000080805ULL, }, + { 0x0000000008080200ULL, 0x0000080804000000ULL, }, + { 0x0808080200000008ULL, 0x0806000000080808ULL, }, + { 0x0000000008080400ULL, 0x0000080808000000ULL, }, + { 0x0808080300000008ULL, 0x0808010000000808ULL, }, + { 0x0000000008080600ULL, 0x0000000808040000ULL, }, + { 0x0808080400000008ULL, 0x0808040000000808ULL, }, /* 56 */ + { 0x0000000008080800ULL, 0x0000000808080000ULL, }, + { 0x0808080500000000ULL, 0x0808070000000008ULL, }, + { 0x0000000008080802ULL, 0x0000000808080400ULL, }, + { 0x0808080600000000ULL, 0x0808080200000008ULL, }, + { 0x0000000008080804ULL, 0x0000000008080800ULL, }, + { 0x0808080700000000ULL, 0x0808080500000000ULL, }, + { 0x0000000008080806ULL, 0x0000000008080804ULL, }, + { 0x0100030200000000ULL, 0x0000000007000100ULL, }, /* 64 */ + { 0x0501000000010200ULL, 0x0004010000000006ULL, }, + { 0x0100010101020101ULL, 0x0002020801000000ULL, }, + { 0x0000000000000300ULL, 0x0104010201000301ULL, }, + { 0x0101000000010000ULL, 0x0100000102020001ULL, }, + { 0x0200010108000005ULL, 0x0000000007010000ULL, }, + { 0x0100000000020100ULL, 0x0100000001040100ULL, }, + { 0x0601000401010101ULL, 0x0106000000000001ULL, }, + { 0x0000000200010300ULL, 0x0300030001010000ULL, }, /* 72 */ + { 0x0100020000020302ULL, 0x0100010101030100ULL, }, + { 0x0103040402020200ULL, 0x0102000000000000ULL, }, + { 0x0101040400010001ULL, 0x0201030000010103ULL, }, + { 0x0300000301000300ULL, 0x0100010200000200ULL, }, + { 0x0100000600000001ULL, 0x0401000100000000ULL, }, + { 0x0000000000010401ULL, 0x0300010402000000ULL, }, + { 0x0100010104000201ULL, 0x0200020200000003ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLOC_B(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLOC_B(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_d.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_d.c new file mode 100644 index 0000000000..b10fb23e88 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_d.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLOC.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLOC.D"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000040ULL, 0x0000000000000040ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000003ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x0000000000000004ULL, 0x0000000000000004ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000005ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000006ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000007ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000006ULL, }, + { 0x0000000000000008ULL, 0x0000000000000008ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000009ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x000000000000000aULL, 0x0000000000000006ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000bULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x000000000000000cULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x000000000000000dULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000eULL, 0x0000000000000006ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000fULL, 0x000000000000000bULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000010ULL, 0x0000000000000010ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000011ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000004ULL, }, + { 0x0000000000000012ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x0000000000000013ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x000000000000000cULL, }, + { 0x0000000000000014ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000010ULL, }, + { 0x0000000000000015ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000014ULL, }, + { 0x0000000000000016ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000017ULL, 0x0000000000000005ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000018ULL, 0x0000000000000008ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000019ULL, 0x000000000000000bULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001aULL, 0x000000000000000eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001bULL, 0x0000000000000011ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001cULL, 0x0000000000000014ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001dULL, 0x0000000000000017ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001eULL, 0x000000000000001aULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001fULL, 0x000000000000001dULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x0000000000000005ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000002ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000006ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, /* 72 */ + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000002ULL, }, + { 0x0000000000000003ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000004ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000001ULL, 0x0000000000000002ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLOC_D(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLOC_D(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_h.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_h.c new file mode 100644 index 0000000000..c1dc0754e6 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_h.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLOC.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLOC.H"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0010001000100010ULL, 0x0010001000100010ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0003000000010003ULL, 0x0000000100030000ULL, }, + { 0x0000000200000000ULL, 0x0002000000000002ULL, }, + { 0x0004000400040004ULL, 0x0004000400040004ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0005000000030000ULL, 0x0001000500000003ULL, }, + { 0x0000000400000002ULL, 0x0000000000040000ULL, }, + { 0x0006000200000006ULL, 0x0002000000060002ULL, }, + { 0x0000000000040000ULL, 0x0000000400000000ULL, }, + { 0x0007000500030001ULL, 0x0000000000000007ULL, }, + { 0x0000000000000000ULL, 0x0006000400020000ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0009000000000000ULL, 0x0000000100030005ULL, }, + { 0x0000000200040006ULL, 0x0008000000000000ULL, }, + { 0x000a000000000002ULL, 0x0006000a00000000ULL, }, + { 0x0000000400080000ULL, 0x0000000000040008ULL, }, + { 0x000b000000010007ULL, 0x0000000000030009ULL, }, + { 0x0000000600000000ULL, 0x0002000800000000ULL, }, + { 0x000c00000004000cULL, 0x00000004000c0000ULL, }, /* 24 */ + { 0x0000000800000000ULL, 0x0008000000000008ULL, }, + { 0x000d000000070000ULL, 0x0001000b00000005ULL, }, + { 0x0000000a00000004ULL, 0x0000000000080000ULL, }, + { 0x000e0000000a0000ULL, 0x000600000002000eULL, }, + { 0x0000000c00000008ULL, 0x0000000400000000ULL, }, + { 0x000f0000000d0000ULL, 0x000b000000090000ULL, }, + { 0x0000000e0000000cULL, 0x0000000a00000008ULL, }, + { 0x0010000000100000ULL, 0x0010000000100000ULL, }, /* 32 */ + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0010000100000003ULL, 0x0000000500000007ULL, }, + { 0x0000000000020000ULL, 0x0004000000060000ULL, }, + { 0x0010000200000006ULL, 0x0000000a0000000eULL, }, + { 0x0000000000040000ULL, 0x00080000000c0000ULL, }, + { 0x0010000300000009ULL, 0x0000000f00000000ULL, }, + { 0x0000000000060000ULL, 0x000c000000100002ULL, }, + { 0x001000040000000cULL, 0x0000001000040000ULL, }, /* 40 */ + { 0x0000000000080000ULL, 0x0010000000000008ULL, }, + { 0x001000050000000fULL, 0x0000000000090000ULL, }, + { 0x00000000000a0000ULL, 0x001000040000000eULL, }, + { 0x0010000600000010ULL, 0x00020000000e0000ULL, }, + { 0x00000000000c0000ULL, 0x0000000800000010ULL, }, + { 0x0010000700000010ULL, 0x0005000000100003ULL, }, + { 0x00000000000e0000ULL, 0x0000000c00000000ULL, }, + { 0x0010000800000010ULL, 0x0008000000100008ULL, }, /* 48 */ + { 0x0000000000100000ULL, 0x0000001000000000ULL, }, + { 0x0010000900000000ULL, 0x000b00000000000dULL, }, + { 0x0000000000100002ULL, 0x0000001000040000ULL, }, + { 0x0010000a00000000ULL, 0x000e000000000010ULL, }, + { 0x0000000000100004ULL, 0x0000001000080000ULL, }, + { 0x0010000b00000000ULL, 0x0010000100000010ULL, }, + { 0x0000000000100006ULL, 0x00000000000c0000ULL, }, + { 0x0010000c00000000ULL, 0x0010000400000010ULL, }, /* 56 */ + { 0x0000000000100008ULL, 0x0000000000100000ULL, }, + { 0x0010000d00000000ULL, 0x0010000700000000ULL, }, + { 0x000000000010000aULL, 0x0000000000100004ULL, }, + { 0x0010000e00000000ULL, 0x0010000a00000000ULL, }, + { 0x000000000010000cULL, 0x0000000000100008ULL, }, + { 0x0010000f00000000ULL, 0x0010000d00000000ULL, }, + { 0x000000000010000eULL, 0x000000000010000cULL, }, + { 0x0001000300000000ULL, 0x0000000000070001ULL, }, /* 64 */ + { 0x0005000000000002ULL, 0x0000000100000000ULL, }, + { 0x0001000100010001ULL, 0x0000000200010000ULL, }, + { 0x0000000000000003ULL, 0x0001000100010003ULL, }, + { 0x0001000000000000ULL, 0x0001000000020000ULL, }, + { 0x0002000100080000ULL, 0x0000000000070000ULL, }, + { 0x0001000000000001ULL, 0x0001000000010001ULL, }, + { 0x0006000000010001ULL, 0x0001000000000000ULL, }, + { 0x0000000000000003ULL, 0x0003000300010000ULL, }, /* 72 */ + { 0x0001000200000003ULL, 0x0001000100010001ULL, }, + { 0x0001000400020002ULL, 0x0001000000000000ULL, }, + { 0x0001000400000000ULL, 0x0002000300000001ULL, }, + { 0x0003000000010003ULL, 0x0001000100000002ULL, }, + { 0x0001000000000000ULL, 0x0004000000000000ULL, }, + { 0x0000000000000004ULL, 0x0003000100020000ULL, }, + { 0x0001000100040002ULL, 0x0002000200000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLOC_H(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLOC_H(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_w.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_w.c new file mode 100644 index 0000000000..4f7a556dec --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nloc_w.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLOC.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLOC.W"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000002000000020ULL, 0x0000002000000020ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000300000001ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000200000000ULL, }, + { 0x0000000400000004ULL, 0x0000000400000004ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000500000003ULL, 0x0000000100000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000004ULL, }, + { 0x0000000600000000ULL, 0x0000000200000006ULL, }, + { 0x0000000000000004ULL, 0x0000000000000000ULL, }, + { 0x0000000700000003ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000600000002ULL, }, + { 0x0000000800000008ULL, 0x0000000800000008ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000900000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000004ULL, 0x0000000800000000ULL, }, + { 0x0000000a00000000ULL, 0x0000000600000000ULL, }, + { 0x0000000000000008ULL, 0x0000000000000004ULL, }, + { 0x0000000b00000001ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000200000000ULL, }, + { 0x0000000c00000004ULL, 0x000000000000000cULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000800000000ULL, }, + { 0x0000000d00000007ULL, 0x0000000100000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x0000000e0000000aULL, 0x0000000600000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000f0000000dULL, 0x0000000b00000009ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000001100000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000400000006ULL, }, + { 0x0000001200000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000004ULL, 0x000000080000000cULL, }, + { 0x0000001300000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000006ULL, 0x0000000c00000012ULL, }, + { 0x0000001400000000ULL, 0x0000000000000004ULL, }, /* 40 */ + { 0x0000000000000008ULL, 0x0000001000000000ULL, }, + { 0x0000001500000000ULL, 0x0000000000000009ULL, }, + { 0x000000000000000aULL, 0x0000001400000000ULL, }, + { 0x0000001600000000ULL, 0x000000020000000eULL, }, + { 0x000000000000000cULL, 0x0000000000000000ULL, }, + { 0x0000001700000000ULL, 0x0000000500000013ULL, }, + { 0x000000000000000eULL, 0x0000000000000000ULL, }, + { 0x0000001800000000ULL, 0x0000000800000018ULL, }, /* 48 */ + { 0x0000000000000010ULL, 0x0000000000000000ULL, }, + { 0x0000001900000000ULL, 0x0000000b00000000ULL, }, + { 0x0000000000000012ULL, 0x0000000000000004ULL, }, + { 0x0000001a00000000ULL, 0x0000000e00000000ULL, }, + { 0x0000000000000014ULL, 0x0000000000000008ULL, }, + { 0x0000001b00000000ULL, 0x0000001100000000ULL, }, + { 0x0000000000000016ULL, 0x000000000000000cULL, }, + { 0x0000001c00000000ULL, 0x0000001400000000ULL, }, /* 56 */ + { 0x0000000000000018ULL, 0x0000000000000010ULL, }, + { 0x0000001d00000000ULL, 0x0000001700000000ULL, }, + { 0x000000000000001aULL, 0x0000000000000014ULL, }, + { 0x0000001e00000000ULL, 0x0000001a00000000ULL, }, + { 0x000000000000001cULL, 0x0000000000000018ULL, }, + { 0x0000001f00000000ULL, 0x0000001d00000000ULL, }, + { 0x000000000000001eULL, 0x000000000000001cULL, }, + { 0x0000000100000000ULL, 0x0000000000000007ULL, }, /* 64 */ + { 0x0000000500000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000100000001ULL, }, + { 0x0000000100000000ULL, 0x0000000100000002ULL, }, + { 0x0000000200000008ULL, 0x0000000000000007ULL, }, + { 0x0000000100000000ULL, 0x0000000100000001ULL, }, + { 0x0000000600000001ULL, 0x0000000100000000ULL, }, + { 0x0000000000000000ULL, 0x0000000300000001ULL, }, /* 72 */ + { 0x0000000100000000ULL, 0x0000000100000001ULL, }, + { 0x0000000100000002ULL, 0x0000000100000000ULL, }, + { 0x0000000100000000ULL, 0x0000000200000000ULL, }, + { 0x0000000300000001ULL, 0x0000000100000000ULL, }, + { 0x0000000100000000ULL, 0x0000000400000000ULL, }, + { 0x0000000000000000ULL, 0x0000000300000002ULL, }, + { 0x0000000100000004ULL, 0x0000000200000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLOC_W(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLOC_W(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_b.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_b.c new file mode 100644 index 0000000000..c202ba4856 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_b.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLZC.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLZC.B"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0808080808080808ULL, 0x0808080808080808ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0202020202020202ULL, 0x0202020202020202ULL, }, + { 0x0000020000020000ULL, 0x0200000200000200ULL, }, + { 0x0301000301000301ULL, 0x0003010003010003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0002040000000204ULL, 0x0000000204000000ULL, }, + { 0x0500000103050000ULL, 0x0103050000010305ULL, }, + { 0x0004000004000004ULL, 0x0000040000040000ULL, }, + { 0x0600020600020600ULL, 0x0206000206000206ULL, }, + { 0x0006000400020000ULL, 0x0600040002000006ULL, }, + { 0x0700050003000107ULL, 0x0005000300010700ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, /* 16 */ + { 0x0800080008000800ULL, 0x0800080008000800ULL, }, + { 0x0000020004000600ULL, 0x0800000200040006ULL, }, + { 0x0801000300050007ULL, 0x0008010003000500ULL, }, + { 0x0000040008000004ULL, 0x0008000004000800ULL, }, + { 0x0802000600080200ULL, 0x0600080200060008ULL, }, + { 0x0000060000040008ULL, 0x0200080000060000ULL, }, + { 0x0803000801000700ULL, 0x0005000803000801ULL, }, + { 0x0000080000080000ULL, 0x0800000800000800ULL, }, /* 24 */ + { 0x0804000804000804ULL, 0x0008040008040008ULL, }, + { 0x0000080200080400ULL, 0x0006000008000008ULL, }, + { 0x0805000007000008ULL, 0x0100080300080500ULL, }, + { 0x0000080400000800ULL, 0x0008040000080000ULL, }, + { 0x0806000008020008ULL, 0x0600000802000806ULL, }, + { 0x0000080600000804ULL, 0x0000080200000800ULL, }, + { 0x0807000008050000ULL, 0x0803000008010008ULL, }, + { 0x0000080800000808ULL, 0x0000080800000808ULL, }, /* 32 */ + { 0x0808000008080000ULL, 0x0808000008080000ULL, }, + { 0x0000000802000008ULL, 0x0400000806000008ULL, }, + { 0x0808010000080300ULL, 0x0008050000080700ULL, }, + { 0x0000000804000008ULL, 0x0800000008040000ULL, }, + { 0x0808020000080600ULL, 0x0008080200000806ULL, }, + { 0x0000000806000000ULL, 0x0804000008080200ULL, }, + { 0x0808030000080801ULL, 0x0000080700000008ULL, }, + { 0x0000000808000000ULL, 0x0808000000080800ULL, }, /* 40 */ + { 0x0808040000080804ULL, 0x0000080804000008ULL, }, + { 0x0000000808020000ULL, 0x0808040000000806ULL, }, + { 0x0808050000000807ULL, 0x0000000808010000ULL, }, + { 0x0000000808040000ULL, 0x0008080000000808ULL, }, + { 0x0808060000000808ULL, 0x0200000808060000ULL, }, + { 0x0000000808060000ULL, 0x0008080400000008ULL, }, + { 0x0808070000000808ULL, 0x0500000008080300ULL, }, + { 0x0000000808080000ULL, 0x0008080800000008ULL, }, /* 48 */ + { 0x0808080000000808ULL, 0x0800000008080800ULL, }, + { 0x0000000008080200ULL, 0x0000080804000000ULL, }, + { 0x0808080100000008ULL, 0x0803000000080805ULL, }, + { 0x0000000008080400ULL, 0x0000080808000000ULL, }, + { 0x0808080200000008ULL, 0x0806000000080808ULL, }, + { 0x0000000008080600ULL, 0x0000000808040000ULL, }, + { 0x0808080300000008ULL, 0x0808010000000808ULL, }, + { 0x0000000008080800ULL, 0x0000000808080000ULL, }, /* 56 */ + { 0x0808080400000008ULL, 0x0808040000000808ULL, }, + { 0x0000000008080802ULL, 0x0000000808080400ULL, }, + { 0x0808080500000000ULL, 0x0808070000000008ULL, }, + { 0x0000000008080804ULL, 0x0000000008080800ULL, }, + { 0x0808080600000000ULL, 0x0808080200000008ULL, }, + { 0x0000000008080806ULL, 0x0000000008080804ULL, }, + { 0x0808080700000000ULL, 0x0808080500000000ULL, }, + { 0x0001000002010101ULL, 0x0101040100010004ULL, }, /* 64 */ + { 0x0000080101000004ULL, 0x0300000303020100ULL, }, + { 0x0001000000000000ULL, 0x0200000000020203ULL, }, + { 0x0101030101020001ULL, 0x0000000000010000ULL, }, + { 0x0000010101000101ULL, 0x0002010000000100ULL, }, + { 0x0002000000040200ULL, 0x0304010100000201ULL, }, + { 0x0002010501000001ULL, 0x0002040200000001ULL, }, + { 0x0000020000000000ULL, 0x0000010203010100ULL, }, + { 0x0203040001000001ULL, 0x0001000200000201ULL, }, /* 72 */ + { 0x0001000301000000ULL, 0x0002000000000001ULL, }, + { 0x0000000000000003ULL, 0x0000010101040103ULL, }, + { 0x0000000002000100ULL, 0x0000000101000000ULL, }, + { 0x0003020000010004ULL, 0x0001000001010003ULL, }, + { 0x0001020003020100ULL, 0x0000020001040201ULL, }, + { 0x0102010102000000ULL, 0x0001000000030201ULL, }, + { 0x0001000000010000ULL, 0x0001000001010300ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLZC_B(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLZC_B(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_d.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_d.c new file mode 100644 index 0000000000..1edead2860 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_d.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLZC.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLZC.D"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000040ULL, 0x0000000000000040ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x0000000000000003ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000004ULL, 0x0000000000000004ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000005ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000006ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000006ULL, }, + { 0x0000000000000007ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000008ULL, 0x0000000000000008ULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x0000000000000009ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000aULL, 0x0000000000000006ULL, }, + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x000000000000000bULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, /* 24 */ + { 0x000000000000000cULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000dULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000eULL, 0x0000000000000006ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000000fULL, 0x000000000000000bULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000010ULL, 0x0000000000000010ULL, }, + { 0x0000000000000000ULL, 0x0000000000000004ULL, }, + { 0x0000000000000011ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x0000000000000012ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x000000000000000cULL, }, + { 0x0000000000000013ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000010ULL, }, /* 40 */ + { 0x0000000000000014ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000014ULL, }, + { 0x0000000000000015ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000016ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000017ULL, 0x0000000000000005ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000000000000018ULL, 0x0000000000000008ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000019ULL, 0x000000000000000bULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001aULL, 0x000000000000000eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001bULL, 0x0000000000000011ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x000000000000001cULL, 0x0000000000000014ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001dULL, 0x0000000000000017ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001eULL, 0x000000000000001aULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000001fULL, 0x000000000000001dULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLZC_D(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLZC_D(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_h.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_h.c new file mode 100644 index 0000000000..b2724c532e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_h.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLZC.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLZC.H"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0010001000100010ULL, 0x0010001000100010ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, + { 0x0000000200000000ULL, 0x0002000000000002ULL, }, + { 0x0003000000010003ULL, 0x0000000100030000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0004000400040004ULL, 0x0004000400040004ULL, }, + { 0x0000000400000002ULL, 0x0000000000040000ULL, }, + { 0x0005000000030000ULL, 0x0001000500000003ULL, }, + { 0x0000000000040000ULL, 0x0000000400000000ULL, }, + { 0x0006000200000006ULL, 0x0002000000060002ULL, }, + { 0x0000000000000000ULL, 0x0006000400020000ULL, }, + { 0x0007000500030001ULL, 0x0000000000000007ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0000000200040006ULL, 0x0008000000000000ULL, }, + { 0x0009000000000000ULL, 0x0000000100030005ULL, }, + { 0x0000000400080000ULL, 0x0000000000040008ULL, }, + { 0x000a000000000002ULL, 0x0006000a00000000ULL, }, + { 0x0000000600000000ULL, 0x0002000800000000ULL, }, + { 0x000b000000010007ULL, 0x0000000000030009ULL, }, + { 0x0000000800000000ULL, 0x0008000000000008ULL, }, /* 24 */ + { 0x000c00000004000cULL, 0x00000004000c0000ULL, }, + { 0x0000000a00000004ULL, 0x0000000000080000ULL, }, + { 0x000d000000070000ULL, 0x0001000b00000005ULL, }, + { 0x0000000c00000008ULL, 0x0000000400000000ULL, }, + { 0x000e0000000a0000ULL, 0x000600000002000eULL, }, + { 0x0000000e0000000cULL, 0x0000000a00000008ULL, }, + { 0x000f0000000d0000ULL, 0x000b000000090000ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, /* 32 */ + { 0x0010000000100000ULL, 0x0010000000100000ULL, }, + { 0x0000000000020000ULL, 0x0004000000060000ULL, }, + { 0x0010000100000003ULL, 0x0000000500000007ULL, }, + { 0x0000000000040000ULL, 0x00080000000c0000ULL, }, + { 0x0010000200000006ULL, 0x0000000a0000000eULL, }, + { 0x0000000000060000ULL, 0x000c000000100002ULL, }, + { 0x0010000300000009ULL, 0x0000000f00000000ULL, }, + { 0x0000000000080000ULL, 0x0010000000000008ULL, }, /* 40 */ + { 0x001000040000000cULL, 0x0000001000040000ULL, }, + { 0x00000000000a0000ULL, 0x001000040000000eULL, }, + { 0x001000050000000fULL, 0x0000000000090000ULL, }, + { 0x00000000000c0000ULL, 0x0000000800000010ULL, }, + { 0x0010000600000010ULL, 0x00020000000e0000ULL, }, + { 0x00000000000e0000ULL, 0x0000000c00000000ULL, }, + { 0x0010000700000010ULL, 0x0005000000100003ULL, }, + { 0x0000000000100000ULL, 0x0000001000000000ULL, }, /* 48 */ + { 0x0010000800000010ULL, 0x0008000000100008ULL, }, + { 0x0000000000100002ULL, 0x0000001000040000ULL, }, + { 0x0010000900000000ULL, 0x000b00000000000dULL, }, + { 0x0000000000100004ULL, 0x0000001000080000ULL, }, + { 0x0010000a00000000ULL, 0x000e000000000010ULL, }, + { 0x0000000000100006ULL, 0x00000000000c0000ULL, }, + { 0x0010000b00000000ULL, 0x0010000100000010ULL, }, + { 0x0000000000100008ULL, 0x0000000000100000ULL, }, /* 56 */ + { 0x0010000c00000000ULL, 0x0010000400000010ULL, }, + { 0x000000000010000aULL, 0x0000000000100004ULL, }, + { 0x0010000d00000000ULL, 0x0010000700000000ULL, }, + { 0x000000000010000cULL, 0x0000000000100008ULL, }, + { 0x0010000e00000000ULL, 0x0010000a00000000ULL, }, + { 0x000000000010000eULL, 0x000000000010000cULL, }, + { 0x0010000f00000000ULL, 0x0010000d00000000ULL, }, + { 0x0000000000020001ULL, 0x0001000400000000ULL, }, /* 64 */ + { 0x0000000900010000ULL, 0x0003000000030001ULL, }, + { 0x0000000000000000ULL, 0x0002000000000002ULL, }, + { 0x0001000300010000ULL, 0x0000000000000000ULL, }, + { 0x0000000100010001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000002ULL, 0x0003000100000002ULL, }, + { 0x0000000100010000ULL, 0x0000000400000000ULL, }, + { 0x0000000200000000ULL, 0x0000000100030001ULL, }, + { 0x0002000400010000ULL, 0x0000000000000002ULL, }, /* 72 */ + { 0x0000000000010000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000100010001ULL, }, + { 0x0000000000020001ULL, 0x0000000000010000ULL, }, + { 0x0000000200000000ULL, 0x0000000000010000ULL, }, + { 0x0000000200030001ULL, 0x0000000200010002ULL, }, + { 0x0001000100020000ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000010003ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLZC_H(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLZC_H(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_w.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_w.c new file mode 100644 index 0000000000..b547c73621 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_nlzc_w.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction NLZC.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NLZC.W"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000002000000020ULL, 0x0000002000000020ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, + { 0x0000000000000000ULL, 0x0000000200000000ULL, }, + { 0x0000000300000001ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000400000004ULL, 0x0000000400000004ULL, }, + { 0x0000000000000000ULL, 0x0000000000000004ULL, }, + { 0x0000000500000003ULL, 0x0000000100000000ULL, }, + { 0x0000000000000004ULL, 0x0000000000000000ULL, }, + { 0x0000000600000000ULL, 0x0000000200000006ULL, }, + { 0x0000000000000000ULL, 0x0000000600000002ULL, }, + { 0x0000000700000003ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000800000008ULL, 0x0000000800000008ULL, }, + { 0x0000000000000004ULL, 0x0000000800000000ULL, }, + { 0x0000000900000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000008ULL, 0x0000000000000004ULL, }, + { 0x0000000a00000000ULL, 0x0000000600000000ULL, }, + { 0x0000000000000000ULL, 0x0000000200000000ULL, }, + { 0x0000000b00000001ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000800000000ULL, }, /* 24 */ + { 0x0000000c00000004ULL, 0x000000000000000cULL, }, + { 0x0000000000000000ULL, 0x0000000000000008ULL, }, + { 0x0000000d00000007ULL, 0x0000000100000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000e0000000aULL, 0x0000000600000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000f0000000dULL, 0x0000000b00000009ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000000000000002ULL, 0x0000000400000006ULL, }, + { 0x0000001100000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000004ULL, 0x000000080000000cULL, }, + { 0x0000001200000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000006ULL, 0x0000000c00000012ULL, }, + { 0x0000001300000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000008ULL, 0x0000001000000000ULL, }, /* 40 */ + { 0x0000001400000000ULL, 0x0000000000000004ULL, }, + { 0x000000000000000aULL, 0x0000001400000000ULL, }, + { 0x0000001500000000ULL, 0x0000000000000009ULL, }, + { 0x000000000000000cULL, 0x0000000000000000ULL, }, + { 0x0000001600000000ULL, 0x000000020000000eULL, }, + { 0x000000000000000eULL, 0x0000000000000000ULL, }, + { 0x0000001700000000ULL, 0x0000000500000013ULL, }, + { 0x0000000000000010ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000001800000000ULL, 0x0000000800000018ULL, }, + { 0x0000000000000012ULL, 0x0000000000000004ULL, }, + { 0x0000001900000000ULL, 0x0000000b00000000ULL, }, + { 0x0000000000000014ULL, 0x0000000000000008ULL, }, + { 0x0000001a00000000ULL, 0x0000000e00000000ULL, }, + { 0x0000000000000016ULL, 0x000000000000000cULL, }, + { 0x0000001b00000000ULL, 0x0000001100000000ULL, }, + { 0x0000000000000018ULL, 0x0000000000000010ULL, }, /* 56 */ + { 0x0000001c00000000ULL, 0x0000001400000000ULL, }, + { 0x000000000000001aULL, 0x0000000000000014ULL, }, + { 0x0000001d00000000ULL, 0x0000001700000000ULL, }, + { 0x000000000000001cULL, 0x0000000000000018ULL, }, + { 0x0000001e00000000ULL, 0x0000001a00000000ULL, }, + { 0x000000000000001eULL, 0x000000000000001cULL, }, + { 0x0000001f00000000ULL, 0x0000001d00000000ULL, }, + { 0x0000000000000002ULL, 0x0000000100000000ULL, }, /* 64 */ + { 0x0000000000000001ULL, 0x0000000300000003ULL, }, + { 0x0000000000000000ULL, 0x0000000200000000ULL, }, + { 0x0000000100000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000300000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000200000001ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000002ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000003ULL, 0x0000000000000001ULL, }, + { 0x0000000100000002ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_NLZC_W(b128_pattern[i], b128_result[i]); + } else { + do_msa_NLZC_W(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_b.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_b.c new file mode 100644 index 0000000000..5918e7fcf3 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_b.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction PCNT.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "PCNT.B"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0808080808080808ULL, 0x0808080808080808ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0504030504030504ULL, 0x0305040305040305ULL, }, + { 0x0304050304050304ULL, 0x0503040503040503ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, /* 8 */ + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0505040303050504ULL, 0x0303050504030305ULL, }, + { 0x0303040505030304ULL, 0x0505030304050503ULL, }, + { 0x0604020604020604ULL, 0x0206040206040206ULL, }, + { 0x0204060204060204ULL, 0x0602040602040602ULL, }, + { 0x0702050403060107ULL, 0x0205040306010702ULL, }, + { 0x0106030405020701ULL, 0x0603040502070106ULL, }, + { 0x0800080008000800ULL, 0x0800080008000800ULL, }, /* 16 */ + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0801060304050207ULL, 0x0008010603040502ULL, }, + { 0x0007020504030601ULL, 0x0800070205040306ULL, }, + { 0x0802040600080204ULL, 0x0600080204060008ULL, }, + { 0x0006040208000604ULL, 0x0208000604020800ULL, }, + { 0x0803020801040700ULL, 0x0605000803020801ULL, }, + { 0x0005060007040108ULL, 0x0203080005060007ULL, }, + { 0x0804000804000804ULL, 0x0008040008040008ULL, }, /* 24 */ + { 0x0004080004080004ULL, 0x0800040800040800ULL, }, + { 0x0805000607000408ULL, 0x0102080300080500ULL, }, + { 0x0003080201080400ULL, 0x0706000508000308ULL, }, + { 0x0806000408020008ULL, 0x0600040802000806ULL, }, + { 0x0002080400060800ULL, 0x0208040006080002ULL, }, + { 0x0807000208050004ULL, 0x0803000608010008ULL, }, + { 0x0001080600030804ULL, 0x0005080200070800ULL, }, + { 0x0808000008080000ULL, 0x0808000008080000ULL, }, /* 32 */ + { 0x0000080800000808ULL, 0x0000080800000808ULL, }, + { 0x0808010006080300ULL, 0x0408050002080700ULL, }, + { 0x0000070802000508ULL, 0x0400030806000108ULL, }, + { 0x0808020004080600ULL, 0x0008080200040806ULL, }, + { 0x0000060804000208ULL, 0x0800000608040002ULL, }, + { 0x0808030002080801ULL, 0x0004080700000608ULL, }, + { 0x0000050806000007ULL, 0x0804000108080200ULL, }, + { 0x0808040000080804ULL, 0x0000080804000008ULL, }, /* 40 */ + { 0x0000040808000004ULL, 0x0808000004080800ULL, }, + { 0x0808050000060807ULL, 0x0000040808010002ULL, }, + { 0x0000030808020001ULL, 0x0808040000070806ULL, }, + { 0x0808060000040808ULL, 0x0200000808060000ULL, }, + { 0x0000020808040000ULL, 0x0608080000020808ULL, }, + { 0x0808070000020808ULL, 0x0500000408080300ULL, }, + { 0x0000010808060000ULL, 0x0308080400000508ULL, }, + { 0x0808080000000808ULL, 0x0800000008080800ULL, }, /* 48 */ + { 0x0000000808080000ULL, 0x0008080800000008ULL, }, + { 0x0808080100000608ULL, 0x0803000004080805ULL, }, + { 0x0000000708080200ULL, 0x0005080804000003ULL, }, + { 0x0808080200000408ULL, 0x0806000000080808ULL, }, + { 0x0000000608080400ULL, 0x0002080808000000ULL, }, + { 0x0808080300000208ULL, 0x0808010000040808ULL, }, + { 0x0000000508080600ULL, 0x0000070808040000ULL, }, + { 0x0808080400000008ULL, 0x0808040000000808ULL, }, /* 56 */ + { 0x0000000408080800ULL, 0x0000040808080000ULL, }, + { 0x0808080500000006ULL, 0x0808070000000408ULL, }, + { 0x0000000308080802ULL, 0x0000010808080400ULL, }, + { 0x0808080600000004ULL, 0x0808080200000008ULL, }, + { 0x0000000208080804ULL, 0x0000000608080800ULL, }, + { 0x0808080700000002ULL, 0x0808080500000004ULL, }, + { 0x0000000108080806ULL, 0x0000000308080804ULL, }, + { 0x0204050402030401ULL, 0x0405030507060302ULL, }, /* 64 */ + { 0x0706000404040501ULL, 0x0207060303060306ULL, }, + { 0x0404050405060401ULL, 0x0404040805040302ULL, }, + { 0x0305030405030404ULL, 0x0405020404020402ULL, }, + { 0x0503050506060203ULL, 0x0302050505050405ULL, }, + { 0x0304060308020406ULL, 0x0403020207040204ULL, }, + { 0x0405030204040503ULL, 0x0303010207040503ULL, }, + { 0x0605030404040602ULL, 0x0407040502020505ULL, }, + { 0x0104020504050503ULL, 0x0705030404040403ULL, }, /* 72 */ + { 0x0405050304050506ULL, 0x0402050302050504ULL, }, + { 0x0304060604050404ULL, 0x0403040305030502ULL, }, + { 0x0304050503030305ULL, 0x0404040505050306ULL, }, + { 0x0502030504030502ULL, 0x0303030504050403ULL, }, + { 0x0303040703030303ULL, 0x0603040404030405ULL, }, + { 0x0302020402060704ULL, 0x0604030705030204ULL, }, + { 0x0404050305040605ULL, 0x0404050504030405ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_PCNT_B(b128_pattern[i], b128_result[i]); + } else { + do_msa_PCNT_B(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_d.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_d.c new file mode 100644 index 0000000000..667ca3112a --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_d.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction PCNT.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "PCNT.D"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000040ULL, 0x0000000000000040ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000021ULL, 0x0000000000000020ULL, }, + { 0x000000000000001fULL, 0x0000000000000020ULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, /* 8 */ + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000022ULL, 0x000000000000001fULL, }, + { 0x000000000000001eULL, 0x0000000000000021ULL, }, + { 0x0000000000000022ULL, 0x0000000000000020ULL, }, + { 0x000000000000001eULL, 0x0000000000000020ULL, }, + { 0x0000000000000023ULL, 0x000000000000001eULL, }, + { 0x000000000000001dULL, 0x0000000000000022ULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, /* 16 */ + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000024ULL, 0x000000000000001dULL, }, + { 0x000000000000001cULL, 0x0000000000000023ULL, }, + { 0x0000000000000022ULL, 0x0000000000000022ULL, }, + { 0x000000000000001eULL, 0x000000000000001eULL, }, + { 0x0000000000000021ULL, 0x0000000000000021ULL, }, + { 0x000000000000001fULL, 0x000000000000001fULL, }, + { 0x0000000000000024ULL, 0x0000000000000020ULL, }, /* 24 */ + { 0x000000000000001cULL, 0x0000000000000020ULL, }, + { 0x0000000000000026ULL, 0x000000000000001bULL, }, + { 0x000000000000001aULL, 0x0000000000000025ULL, }, + { 0x0000000000000024ULL, 0x0000000000000022ULL, }, + { 0x000000000000001cULL, 0x000000000000001eULL, }, + { 0x0000000000000022ULL, 0x0000000000000022ULL, }, + { 0x000000000000001eULL, 0x000000000000001eULL, }, + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, /* 32 */ + { 0x0000000000000020ULL, 0x0000000000000020ULL, }, + { 0x0000000000000022ULL, 0x0000000000000022ULL, }, + { 0x000000000000001eULL, 0x000000000000001eULL, }, + { 0x0000000000000024ULL, 0x0000000000000024ULL, }, + { 0x000000000000001cULL, 0x000000000000001cULL, }, + { 0x0000000000000026ULL, 0x0000000000000021ULL, }, + { 0x000000000000001aULL, 0x000000000000001fULL, }, + { 0x0000000000000028ULL, 0x000000000000001cULL, }, /* 40 */ + { 0x0000000000000018ULL, 0x0000000000000024ULL, }, + { 0x000000000000002aULL, 0x0000000000000017ULL, }, + { 0x0000000000000016ULL, 0x0000000000000029ULL, }, + { 0x000000000000002aULL, 0x0000000000000018ULL, }, + { 0x0000000000000016ULL, 0x0000000000000028ULL, }, + { 0x0000000000000029ULL, 0x000000000000001cULL, }, + { 0x0000000000000017ULL, 0x0000000000000024ULL, }, + { 0x0000000000000028ULL, 0x0000000000000020ULL, }, /* 48 */ + { 0x0000000000000018ULL, 0x0000000000000020ULL, }, + { 0x0000000000000027ULL, 0x0000000000000024ULL, }, + { 0x0000000000000019ULL, 0x000000000000001cULL, }, + { 0x0000000000000026ULL, 0x0000000000000026ULL, }, + { 0x000000000000001aULL, 0x000000000000001aULL, }, + { 0x0000000000000025ULL, 0x0000000000000025ULL, }, + { 0x000000000000001bULL, 0x000000000000001bULL, }, + { 0x0000000000000024ULL, 0x0000000000000024ULL, }, /* 56 */ + { 0x000000000000001cULL, 0x000000000000001cULL, }, + { 0x0000000000000023ULL, 0x0000000000000023ULL, }, + { 0x000000000000001dULL, 0x000000000000001dULL, }, + { 0x0000000000000022ULL, 0x0000000000000022ULL, }, + { 0x000000000000001eULL, 0x000000000000001eULL, }, + { 0x0000000000000021ULL, 0x0000000000000021ULL, }, + { 0x000000000000001fULL, 0x000000000000001fULL, }, + { 0x0000000000000019ULL, 0x0000000000000023ULL, }, /* 64 */ + { 0x000000000000001fULL, 0x0000000000000024ULL, }, + { 0x0000000000000021ULL, 0x0000000000000022ULL, }, + { 0x000000000000001fULL, 0x000000000000001bULL, }, + { 0x0000000000000023ULL, 0x0000000000000022ULL, }, + { 0x0000000000000024ULL, 0x000000000000001cULL, }, + { 0x000000000000001eULL, 0x000000000000001cULL, }, + { 0x0000000000000022ULL, 0x0000000000000022ULL, }, + { 0x000000000000001dULL, 0x0000000000000022ULL, }, /* 72 */ + { 0x0000000000000025ULL, 0x000000000000001eULL, }, + { 0x0000000000000024ULL, 0x000000000000001dULL, }, + { 0x000000000000001fULL, 0x0000000000000024ULL, }, + { 0x000000000000001dULL, 0x000000000000001eULL, }, + { 0x000000000000001dULL, 0x0000000000000021ULL, }, + { 0x000000000000001eULL, 0x0000000000000022ULL, }, + { 0x0000000000000024ULL, 0x0000000000000022ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_PCNT_D(b128_pattern[i], b128_result[i]); + } else { + do_msa_PCNT_D(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_h.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_h.c new file mode 100644 index 0000000000..2951f86983 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_h.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction PCNT.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "PCNT.H"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0010001000100010ULL, 0x0010001000100010ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0009000800070009ULL, 0x0008000700090008ULL, }, + { 0x0007000800090007ULL, 0x0008000900070008ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, /* 8 */ + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x000a000700080009ULL, 0x0006000a00070008ULL, }, + { 0x0006000900080007ULL, 0x000a000600090008ULL, }, + { 0x000a00080006000aULL, 0x00080006000a0008ULL, }, + { 0x00060008000a0006ULL, 0x0008000a00060008ULL, }, + { 0x0009000900090008ULL, 0x0007000700070009ULL, }, + { 0x0007000700070008ULL, 0x0009000900090007ULL, }, + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, /* 16 */ + { 0x0008000800080008ULL, 0x0008000800080008ULL, }, + { 0x0009000900090009ULL, 0x0008000700070007ULL, }, + { 0x0007000700070007ULL, 0x0008000900090009ULL, }, + { 0x000a000a00080006ULL, 0x0006000a000a0008ULL, }, + { 0x000600060008000aULL, 0x000a000600060008ULL, }, + { 0x000b000a00050007ULL, 0x000b000800050009ULL, }, + { 0x00050006000b0009ULL, 0x00050008000b0007ULL, }, + { 0x000c00080004000cULL, 0x00080004000c0008ULL, }, /* 24 */ + { 0x00040008000c0004ULL, 0x0008000c00040008ULL, }, + { 0x000d00060007000cULL, 0x0003000b00080005ULL, }, + { 0x0003000a00090004ULL, 0x000d00050008000bULL, }, + { 0x000e0004000a0008ULL, 0x0006000c0002000eULL, }, + { 0x0002000c00060008ULL, 0x000a0004000e0002ULL, }, + { 0x000f0002000d0004ULL, 0x000b000600090008ULL, }, + { 0x0001000e0003000cULL, 0x0005000a00070008ULL, }, + { 0x0010000000100000ULL, 0x0010000000100000ULL, }, /* 32 */ + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x00100001000e0003ULL, 0x000c0005000a0007ULL, }, + { 0x0000000f0002000dULL, 0x0004000b00060009ULL, }, + { 0x00100002000c0006ULL, 0x0008000a0004000eULL, }, + { 0x0000000e0004000aULL, 0x00080006000c0002ULL, }, + { 0x00100003000a0009ULL, 0x0004000f0000000eULL, }, + { 0x0000000d00060007ULL, 0x000c000100100002ULL, }, + { 0x001000040008000cULL, 0x0000001000040008ULL, }, /* 40 */ + { 0x0000000c00080004ULL, 0x00100000000c0008ULL, }, + { 0x001000050006000fULL, 0x0000000c00090002ULL, }, + { 0x0000000b000a0001ULL, 0x001000040007000eULL, }, + { 0x0010000600040010ULL, 0x00020008000e0000ULL, }, + { 0x0000000a000c0000ULL, 0x000e000800020010ULL, }, + { 0x0010000700020010ULL, 0x0005000400100003ULL, }, + { 0x00000009000e0000ULL, 0x000b000c0000000dULL, }, + { 0x0010000800000010ULL, 0x0008000000100008ULL, }, /* 48 */ + { 0x0000000800100000ULL, 0x0008001000000008ULL, }, + { 0x001000090000000eULL, 0x000b0000000c000dULL, }, + { 0x0000000700100002ULL, 0x0005001000040003ULL, }, + { 0x0010000a0000000cULL, 0x000e000000080010ULL, }, + { 0x0000000600100004ULL, 0x0002001000080000ULL, }, + { 0x0010000b0000000aULL, 0x0010000100040010ULL, }, + { 0x0000000500100006ULL, 0x0000000f000c0000ULL, }, + { 0x0010000c00000008ULL, 0x0010000400000010ULL, }, /* 56 */ + { 0x0000000400100008ULL, 0x0000000c00100000ULL, }, + { 0x0010000d00000006ULL, 0x001000070000000cULL, }, + { 0x000000030010000aULL, 0x0000000900100004ULL, }, + { 0x0010000e00000004ULL, 0x0010000a00000008ULL, }, + { 0x000000020010000cULL, 0x0000000600100008ULL, }, + { 0x0010000f00000002ULL, 0x0010000d00000004ULL, }, + { 0x000000010010000eULL, 0x000000030010000cULL, }, + { 0x0006000900050005ULL, 0x00090008000d0005ULL, }, /* 64 */ + { 0x000d000400080006ULL, 0x0009000900090009ULL, }, + { 0x00080009000b0005ULL, 0x0008000c00090005ULL, }, + { 0x0008000700080008ULL, 0x0009000600060006ULL, }, + { 0x0008000a000c0005ULL, 0x0005000a000a0009ULL, }, + { 0x00070009000a000aULL, 0x00070004000b0006ULL, }, + { 0x0009000500080008ULL, 0x00060003000b0008ULL, }, + { 0x000b000700080008ULL, 0x000b00090004000aULL, }, + { 0x0005000700090008ULL, 0x000c000700080007ULL, }, /* 72 */ + { 0x000900080009000bULL, 0x0006000800070009ULL, }, + { 0x0007000c00090008ULL, 0x0007000700080007ULL, }, + { 0x0007000a00060008ULL, 0x00080009000a0009ULL, }, + { 0x0007000800070007ULL, 0x0006000800090007ULL, }, + { 0x0006000b00060006ULL, 0x0009000800070009ULL, }, + { 0x000500060008000bULL, 0x000a000a00080006ULL, }, + { 0x000800080009000bULL, 0x0008000a00070009ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_PCNT_H(b128_pattern[i], b128_result[i]); + } else { + do_msa_PCNT_H(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_w.c b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_w.c new file mode 100644 index 0000000000..ab43ea92cd --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/bit-count/test_msa_pcnt_w.c @@ -0,0 +1,144 @@ +/* + * Test program for MSA instruction PCNT.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_COUNT + RANDOM_INPUTS_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "PCNT.W"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000002000000020ULL, 0x0000002000000020ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001100000010ULL, 0x0000000f00000011ULL, }, + { 0x0000000f00000010ULL, 0x000000110000000fULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, /* 8 */ + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001100000011ULL, 0x000000100000000fULL, }, + { 0x0000000f0000000fULL, 0x0000001000000011ULL, }, + { 0x0000001200000010ULL, 0x0000000e00000012ULL, }, + { 0x0000000e00000010ULL, 0x000000120000000eULL, }, + { 0x0000001200000011ULL, 0x0000000e00000010ULL, }, + { 0x0000000e0000000fULL, 0x0000001200000010ULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, /* 16 */ + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001200000012ULL, 0x0000000f0000000eULL, }, + { 0x0000000e0000000eULL, 0x0000001100000012ULL, }, + { 0x000000140000000eULL, 0x0000001000000012ULL, }, + { 0x0000000c00000012ULL, 0x000000100000000eULL, }, + { 0x000000150000000cULL, 0x000000130000000eULL, }, + { 0x0000000b00000014ULL, 0x0000000d00000012ULL, }, + { 0x0000001400000010ULL, 0x0000000c00000014ULL, }, /* 24 */ + { 0x0000000c00000010ULL, 0x000000140000000cULL, }, + { 0x0000001300000013ULL, 0x0000000e0000000dULL, }, + { 0x0000000d0000000dULL, 0x0000001200000013ULL, }, + { 0x0000001200000012ULL, 0x0000001200000010ULL, }, + { 0x0000000e0000000eULL, 0x0000000e00000010ULL, }, + { 0x0000001100000011ULL, 0x0000001100000011ULL, }, + { 0x0000000f0000000fULL, 0x0000000f0000000fULL, }, + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, /* 32 */ + { 0x0000001000000010ULL, 0x0000001000000010ULL, }, + { 0x0000001100000011ULL, 0x0000001100000011ULL, }, + { 0x0000000f0000000fULL, 0x0000000f0000000fULL, }, + { 0x0000001200000012ULL, 0x0000001200000012ULL, }, + { 0x0000000e0000000eULL, 0x0000000e0000000eULL, }, + { 0x0000001300000013ULL, 0x000000130000000eULL, }, + { 0x0000000d0000000dULL, 0x0000000d00000012ULL, }, + { 0x0000001400000014ULL, 0x000000100000000cULL, }, /* 40 */ + { 0x0000000c0000000cULL, 0x0000001000000014ULL, }, + { 0x0000001500000015ULL, 0x0000000c0000000bULL, }, + { 0x0000000b0000000bULL, 0x0000001400000015ULL, }, + { 0x0000001600000014ULL, 0x0000000a0000000eULL, }, + { 0x0000000a0000000cULL, 0x0000001600000012ULL, }, + { 0x0000001700000012ULL, 0x0000000900000013ULL, }, + { 0x000000090000000eULL, 0x000000170000000dULL, }, + { 0x0000001800000010ULL, 0x0000000800000018ULL, }, /* 48 */ + { 0x0000000800000010ULL, 0x0000001800000008ULL, }, + { 0x000000190000000eULL, 0x0000000b00000019ULL, }, + { 0x0000000700000012ULL, 0x0000001500000007ULL, }, + { 0x0000001a0000000cULL, 0x0000000e00000018ULL, }, + { 0x0000000600000014ULL, 0x0000001200000008ULL, }, + { 0x0000001b0000000aULL, 0x0000001100000014ULL, }, + { 0x0000000500000016ULL, 0x0000000f0000000cULL, }, + { 0x0000001c00000008ULL, 0x0000001400000010ULL, }, /* 56 */ + { 0x0000000400000018ULL, 0x0000000c00000010ULL, }, + { 0x0000001d00000006ULL, 0x000000170000000cULL, }, + { 0x000000030000001aULL, 0x0000000900000014ULL, }, + { 0x0000001e00000004ULL, 0x0000001a00000008ULL, }, + { 0x000000020000001cULL, 0x0000000600000018ULL, }, + { 0x0000001f00000002ULL, 0x0000001d00000004ULL, }, + { 0x000000010000001eULL, 0x000000030000001cULL, }, + { 0x0000000f0000000aULL, 0x0000001100000012ULL, }, /* 64 */ + { 0x000000110000000eULL, 0x0000001200000012ULL, }, + { 0x0000001100000010ULL, 0x000000140000000eULL, }, + { 0x0000000f00000010ULL, 0x0000000f0000000cULL, }, + { 0x0000001200000011ULL, 0x0000000f00000013ULL, }, + { 0x0000001000000014ULL, 0x0000000b00000011ULL, }, + { 0x0000000e00000010ULL, 0x0000000900000013ULL, }, + { 0x0000001200000010ULL, 0x000000140000000eULL, }, + { 0x0000000c00000011ULL, 0x000000130000000fULL, }, /* 72 */ + { 0x0000001100000014ULL, 0x0000000e00000010ULL, }, + { 0x0000001300000011ULL, 0x0000000e0000000fULL, }, + { 0x000000110000000eULL, 0x0000001100000013ULL, }, + { 0x0000000f0000000eULL, 0x0000000e00000010ULL, }, + { 0x000000110000000cULL, 0x0000001100000010ULL, }, + { 0x0000000b00000013ULL, 0x000000140000000eULL, }, + { 0x0000001000000014ULL, 0x0000001200000010ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_COUNT) { + do_msa_PCNT_W(b128_pattern[i], b128_result[i]); + } else { + do_msa_PCNT_W(b128_random[i - PATTERN_INPUTS_COUNT], + b128_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_b.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_b.c new file mode 100644 index 0000000000..d2ea54f43d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADD_A.B + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADD_A.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0202020202020202ULL, 0x0202020202020202ULL, }, /* 0 */ + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x5757575757575757ULL, 0x5757575757575757ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0x3535353535353535ULL, 0x3535353535353535ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x1e73391e73391e73ULL, 0x391e73391e73391eULL, }, + { 0x1d723a1d723a1d72ULL, 0x3a1d723a1d723a1dULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1d72381d72381d72ULL, 0x381d72381d72381dULL, }, + { 0x1c71391c71391c71ULL, 0x391c71391c71391cULL, }, + { 0x5757575757575757ULL, 0x5757575757575757ULL, }, /* 16 */ + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0xacacacacacacacacULL, 0xacacacacacacacacULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x8a8a8a8a8a8a8a8aULL, 0x8a8a8a8a8a8a8a8aULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x73c88e73c88e73c8ULL, 0x8e73c88e73c88e73ULL, }, + { 0x72c78f72c78f72c7ULL, 0x8f72c78f72c78f72ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x72c78d72c78d72c7ULL, 0x8d72c78d72c78d72ULL, }, + { 0x71c68e71c68e71c6ULL, 0x8e71c68e71c68e71ULL, }, + { 0x3535353535353535ULL, 0x3535353535353535ULL, }, /* 32 */ + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x8a8a8a8a8a8a8a8aULL, 0x8a8a8a8a8a8a8a8aULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x6868686868686868ULL, 0x6868686868686868ULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x51a66c51a66c51a6ULL, 0x6c51a66c51a66c51ULL, }, + { 0x50a56d50a56d50a5ULL, 0x6d50a56d50a56d50ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x50a56b50a56b50a5ULL, 0x6b50a56b50a56b50ULL, }, + { 0x4fa46c4fa46c4fa4ULL, 0x6c4fa46c4fa46c4fULL, }, + { 0x1e73391e73391e73ULL, 0x391e73391e73391eULL, }, /* 48 */ + { 0x1d72381d72381d72ULL, 0x381d72381d72381dULL, }, + { 0x73c88e73c88e73c8ULL, 0x8e73c88e73c88e73ULL, }, + { 0x72c78d72c78d72c7ULL, 0x8d72c78d72c78d72ULL, }, + { 0x51a66c51a66c51a6ULL, 0x6c51a66c51a66c51ULL, }, + { 0x50a56b50a56b50a5ULL, 0x6b50a56b50a56b50ULL, }, + { 0x3ae4703ae4703ae4ULL, 0x703ae4703ae4703aULL, }, + { 0x39e37139e37139e3ULL, 0x7139e37139e37139ULL, }, + { 0x1d723a1d723a1d72ULL, 0x3a1d723a1d723a1dULL, }, /* 56 */ + { 0x1c71391c71391c71ULL, 0x391c71391c71391cULL, }, + { 0x72c78f72c78f72c7ULL, 0x8f72c78f72c78f72ULL, }, + { 0x71c68e71c68e71c6ULL, 0x8e71c68e71c68e71ULL, }, + { 0x50a56d50a56d50a5ULL, 0x6d50a56d50a56d50ULL, }, + { 0x4fa46c4fa46c4fa4ULL, 0x6c4fa46c4fa46c4fULL, }, + { 0x39e37139e37139e3ULL, 0x7139e37139e37139ULL, }, + { 0x38e27238e27238e2ULL, 0x7238e27238e27238ULL, }, + { 0xf0d4346850c4aa80ULL, 0x96ce16bc04f6a018ULL, }, /* 64 */ + { 0x7dac1a9775cf8e48ULL, 0x5d70507817baa210ULL, }, + { 0xccc46c8a6f93cac0ULL, 0x728f455f57a67520ULL, }, + { 0xe8b930818693738eULL, 0xbe76838659bd6e6cULL, }, + { 0x7dac1a9775cf8e48ULL, 0x5d70507817baa210ULL, }, + { 0x0a8400c69ada7210ULL, 0x24128a342a7ea408ULL, }, + { 0x599c52b9949eae88ULL, 0x39317f1b6a6a7718ULL, }, + { 0x759116b0ab9e5756ULL, 0x8518bd426c817064ULL, }, + { 0xccc46c8a6f93cac0ULL, 0x728f455f57a67520ULL, }, /* 72 */ + { 0x599c52b9949eae88ULL, 0x39317f1b6a6a7718ULL, }, + { 0xa8b4a4ac8e62ea00ULL, 0x4e507402aa564a28ULL, }, + { 0xc4a968a3a56293ceULL, 0x9a37b229ac6d4374ULL, }, + { 0xe8b930818693738eULL, 0xbe76838659bd6e6cULL, }, + { 0x759116b0ab9e5756ULL, 0x8518bd426c817064ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_d.c new file mode 100644 index 0000000000..56b81f9090 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADD_A.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADD_A.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, /* 0 */ + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x5555555555555557ULL, 0x5555555555555557ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0x3333333333333335ULL, 0x3333333333333335ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x1c71c71c71c71c73ULL, 0x38e38e38e38e38e4ULL, }, + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e5ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e4ULL, }, + { 0x5555555555555557ULL, 0x5555555555555557ULL, }, /* 16 */ + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0xaaaaaaaaaaaaaaacULL, 0xaaaaaaaaaaaaaaacULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x888888888888888aULL, 0x888888888888888aULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x71c71c71c71c71c8ULL, 0x8e38e38e38e38e39ULL, }, + { 0x71c71c71c71c71c7ULL, 0x8e38e38e38e38e3aULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x71c71c71c71c71c7ULL, 0x8e38e38e38e38e38ULL, }, + { 0x71c71c71c71c71c6ULL, 0x8e38e38e38e38e39ULL, }, + { 0x3333333333333335ULL, 0x3333333333333335ULL, }, /* 32 */ + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x888888888888888aULL, 0x888888888888888aULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x6666666666666668ULL, 0x6666666666666668ULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x4fa4fa4fa4fa4fa6ULL, 0x6c16c16c16c16c17ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c18ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0x6c16c16c16c16c17ULL, }, + { 0x1c71c71c71c71c73ULL, 0x38e38e38e38e38e4ULL, }, /* 48 */ + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e3ULL, }, + { 0x71c71c71c71c71c8ULL, 0x8e38e38e38e38e39ULL, }, + { 0x71c71c71c71c71c7ULL, 0x8e38e38e38e38e38ULL, }, + { 0x4fa4fa4fa4fa4fa6ULL, 0x6c16c16c16c16c17ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c16ULL, }, + { 0x38e38e38e38e38e4ULL, 0x71c71c71c71c71c6ULL, }, + { 0x38e38e38e38e38e3ULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e5ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e4ULL, }, + { 0x71c71c71c71c71c7ULL, 0x8e38e38e38e38e3aULL, }, + { 0x71c71c71c71c71c6ULL, 0x8e38e38e38e38e39ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c18ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0x6c16c16c16c16c17ULL, }, + { 0x38e38e38e38e38e3ULL, 0x71c71c71c71c71c7ULL, }, + { 0x38e38e38e38e38e2ULL, 0x71c71c71c71c71c8ULL, }, + { 0xef2a3267af3b5580ULL, 0x96ce16bdfcf76018ULL, }, /* 64 */ + { 0x7bd718d08a09e3b8ULL, 0x5e5ec67913bb0308ULL, }, + { 0xcb3a6a891dce1f40ULL, 0x733fd25ea9a6d520ULL, }, + { 0xe7e42f8135cf8d0eULL, 0xbd7582865538cd6cULL, }, + { 0x7bd718d08a09e3b8ULL, 0x5e5ec67913bb0308ULL, }, + { 0x0883ff3964d871f0ULL, 0x25ef76342a7ea5f8ULL, }, + { 0x57e750f1f89cad78ULL, 0x3ad08219c06a7810ULL, }, + { 0x749115ea109e1b46ULL, 0x850632416bfc705cULL, }, + { 0xcb3a6a891dce1f40ULL, 0x733fd25ea9a6d520ULL, }, /* 72 */ + { 0x57e750f1f89cad78ULL, 0x3ad08219c06a7810ULL, }, + { 0xa74aa2aa8c60e900ULL, 0x4fb18dff56564a28ULL, }, + { 0xc3f467a2a46256ceULL, 0x99e73e2701e84274ULL, }, + { 0xe7e42f8135cf8d0eULL, 0xbd7582865538cd6cULL, }, + { 0x749115ea109e1b46ULL, 0x850632416bfc705cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_h.c new file mode 100644 index 0000000000..fe3c664997 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADD_A.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADD_A.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, /* 0 */ + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x5557555755575557ULL, 0x5557555755575557ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0x3335333533353335ULL, 0x3335333533353335ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x1c7338e471c91c73ULL, 0x38e471c91c7338e4ULL, }, + { 0x1c7238e571c81c72ULL, 0x38e571c81c7238e5ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c7238e371c81c72ULL, 0x38e371c81c7238e3ULL, }, + { 0x1c7138e471c71c71ULL, 0x38e471c71c7138e4ULL, }, + { 0x5557555755575557ULL, 0x5557555755575557ULL, }, /* 16 */ + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0xaaacaaacaaacaaacULL, 0xaaacaaacaaacaaacULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x888a888a888a888aULL, 0x888a888a888a888aULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x71c88e39c71e71c8ULL, 0x8e39c71e71c88e39ULL, }, + { 0x71c78e3ac71d71c7ULL, 0x8e3ac71d71c78e3aULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x71c78e38c71d71c7ULL, 0x8e38c71d71c78e38ULL, }, + { 0x71c68e39c71c71c6ULL, 0x8e39c71c71c68e39ULL, }, + { 0x3335333533353335ULL, 0x3335333533353335ULL, }, /* 32 */ + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x888a888a888a888aULL, 0x888a888a888a888aULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x6668666866686668ULL, 0x6668666866686668ULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x4fa66c17a4fc4fa6ULL, 0x6c17a4fc4fa66c17ULL, }, + { 0x4fa56c18a4fb4fa5ULL, 0x6c18a4fb4fa56c18ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x4fa56c16a4fb4fa5ULL, 0x6c16a4fb4fa56c16ULL, }, + { 0x4fa46c17a4fa4fa4ULL, 0x6c17a4fa4fa46c17ULL, }, + { 0x1c7338e471c91c73ULL, 0x38e471c91c7338e4ULL, }, /* 48 */ + { 0x1c7238e371c81c72ULL, 0x38e371c81c7238e3ULL, }, + { 0x71c88e39c71e71c8ULL, 0x8e39c71e71c88e39ULL, }, + { 0x71c78e38c71d71c7ULL, 0x8e38c71d71c78e38ULL, }, + { 0x4fa66c17a4fc4fa6ULL, 0x6c17a4fc4fa66c17ULL, }, + { 0x4fa56c16a4fb4fa5ULL, 0x6c16a4fb4fa56c16ULL, }, + { 0x38e471c6e39038e4ULL, 0x71c6e39038e471c6ULL, }, + { 0x38e371c7e38f38e3ULL, 0x71c7e38f38e371c7ULL, }, + { 0x1c7238e571c81c72ULL, 0x38e571c81c7238e5ULL, }, /* 56 */ + { 0x1c7138e471c71c71ULL, 0x38e471c71c7138e4ULL, }, + { 0x71c78e3ac71d71c7ULL, 0x8e3ac71d71c78e3aULL, }, + { 0x71c68e39c71c71c6ULL, 0x8e39c71c71c68e39ULL, }, + { 0x4fa56c18a4fb4fa5ULL, 0x6c18a4fb4fa56c18ULL, }, + { 0x4fa46c17a4fa4fa4ULL, 0x6c17a4fa4fa46c17ULL, }, + { 0x38e371c7e38f38e3ULL, 0x71c7e38f38e371c7ULL, }, + { 0x38e271c8e38e38e2ULL, 0x71c8e38e38e271c8ULL, }, + { 0xef2c326850c4aa80ULL, 0x96ce16bc030a9fe8ULL, }, /* 64 */ + { 0x7bd8199775f58e38ULL, 0x5e5e504416c4a2f0ULL, }, + { 0xcb3c6a8a6e93c9c0ULL, 0x733f445f565a7508ULL, }, + { 0xe7e52f81869372f2ULL, 0xbd76828658436d54ULL, }, + { 0x7bd8199775f58e38ULL, 0x5e5e504416c4a2f0ULL, }, + { 0x088400c69b2671f0ULL, 0x25ee89cc2a7ea5f8ULL, }, + { 0x57e851b993c4ad78ULL, 0x3acf7de76a147810ULL, }, + { 0x749116b0abc456aaULL, 0x8506bc0e6bfd705cULL, }, + { 0xcb3c6a8a6e93c9c0ULL, 0x733f445f565a7508ULL, }, /* 72 */ + { 0x57e851b993c4ad78ULL, 0x3acf7de76a147810ULL, }, + { 0xa74ca2ac8c62e900ULL, 0x4fb07202a9aa4a28ULL, }, + { 0xc3f567a3a4629232ULL, 0x99e7b029ab934274ULL, }, + { 0xe7e52f81869372f2ULL, 0xbd76828658436d54ULL, }, + { 0x749116b0abc456aaULL, 0x8506bc0e6bfd705cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_w.c new file mode 100644 index 0000000000..205117ea95 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_add_a_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADD_A.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADD_A.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, /* 0 */ + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x5555555755555557ULL, 0x5555555755555557ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0x3333333533333335ULL, 0x3333333533333335ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x1c71c71e71c71c73ULL, 0x38e38e391c71c71eULL, }, + { 0x1c71c71d71c71c72ULL, 0x38e38e3a1c71c71dULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71d71c71c72ULL, 0x38e38e381c71c71dULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e391c71c71cULL, }, + { 0x5555555755555557ULL, 0x5555555755555557ULL, }, /* 16 */ + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0xaaaaaaacaaaaaaacULL, 0xaaaaaaacaaaaaaacULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x8888888a8888888aULL, 0x8888888a8888888aULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x71c71c73c71c71c8ULL, 0x8e38e38e71c71c73ULL, }, + { 0x71c71c72c71c71c7ULL, 0x8e38e38f71c71c72ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x71c71c72c71c71c7ULL, 0x8e38e38d71c71c72ULL, }, + { 0x71c71c71c71c71c6ULL, 0x8e38e38e71c71c71ULL, }, + { 0x3333333533333335ULL, 0x3333333533333335ULL, }, /* 32 */ + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x8888888a8888888aULL, 0x8888888a8888888aULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x6666666866666668ULL, 0x6666666866666668ULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x4fa4fa51a4fa4fa6ULL, 0x6c16c16c4fa4fa51ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0x6c16c16d4fa4fa50ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0x6c16c16b4fa4fa50ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0x6c16c16c4fa4fa4fULL, }, + { 0x1c71c71e71c71c73ULL, 0x38e38e391c71c71eULL, }, /* 48 */ + { 0x1c71c71d71c71c72ULL, 0x38e38e381c71c71dULL, }, + { 0x71c71c73c71c71c8ULL, 0x8e38e38e71c71c73ULL, }, + { 0x71c71c72c71c71c7ULL, 0x8e38e38d71c71c72ULL, }, + { 0x4fa4fa51a4fa4fa6ULL, 0x6c16c16c4fa4fa51ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0x6c16c16b4fa4fa50ULL, }, + { 0x38e38e3ae38e38e4ULL, 0x71c71c7038e38e3aULL, }, + { 0x38e38e39e38e38e3ULL, 0x71c71c7138e38e39ULL, }, + { 0x1c71c71d71c71c72ULL, 0x38e38e3a1c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x38e38e391c71c71cULL, }, + { 0x71c71c72c71c71c7ULL, 0x8e38e38f71c71c72ULL, }, + { 0x71c71c71c71c71c6ULL, 0x8e38e38e71c71c71ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0x6c16c16d4fa4fa50ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0x6c16c16c4fa4fa4fULL, }, + { 0x38e38e39e38e38e3ULL, 0x71c71c7138e38e39ULL, }, + { 0x38e38e38e38e38e2ULL, 0x71c71c7238e38e38ULL, }, + { 0xef2a326850c4aa80ULL, 0x96ce16bc03089fe8ULL, }, /* 64 */ + { 0x7bd718d175f61c48ULL, 0x5e5ec67816c3a2f0ULL, }, + { 0xcb3a6a8a6e92c9c0ULL, 0x733fd25d56592ae0ULL, }, + { 0xe7e42f818694378eULL, 0xbd75828658416d54ULL, }, + { 0x7bd718d175f61c48ULL, 0x5e5ec67816c3a2f0ULL, }, + { 0x0883ff3a9b278e10ULL, 0x25ef76342a7ea5f8ULL, }, + { 0x57e750f393c43b88ULL, 0x3ad082196a142de8ULL, }, + { 0x749115eaabc5a956ULL, 0x850632426bfc705cULL, }, + { 0xcb3a6a8a6e92c9c0ULL, 0x733fd25d56592ae0ULL, }, /* 72 */ + { 0x57e750f393c43b88ULL, 0x3ad082196a142de8ULL, }, + { 0xa74aa2ac8c60e900ULL, 0x4fb18dfea9a9b5d8ULL, }, + { 0xc3f467a3a46256ceULL, 0x99e73e27ab91f84cULL, }, + { 0xe7e42f818694378eULL, 0xbd75828658416d54ULL, }, + { 0x749115eaabc5a956ULL, 0x850632426bfc705cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADD_A_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_b.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_b.c new file mode 100644 index 0000000000..6939e91fe4 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_A.B + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_A.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0202020202020202ULL, 0x0202020202020202ULL, }, /* 0 */ + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x5757575757575757ULL, 0x5757575757575757ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0x3535353535353535ULL, 0x3535353535353535ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x1e73391e73391e73ULL, 0x391e73391e73391eULL, }, + { 0x1d723a1d723a1d72ULL, 0x3a1d723a1d723a1dULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1d72381d72381d72ULL, 0x381d72381d72381dULL, }, + { 0x1c71391c71391c71ULL, 0x391c71391c71391cULL, }, + { 0x5757575757575757ULL, 0x5757575757575757ULL, }, /* 16 */ + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x737f7f737f7f737fULL, 0x7f737f7f737f7f73ULL, }, + { 0x727f7f727f7f727fULL, 0x7f727f7f727f7f72ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x727f7f727f7f727fULL, 0x7f727f7f727f7f72ULL, }, + { 0x717f7f717f7f717fULL, 0x7f717f7f717f7f71ULL, }, + { 0x3535353535353535ULL, 0x3535353535353535ULL, }, /* 32 */ + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x6868686868686868ULL, 0x6868686868686868ULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x517f6c517f6c517fULL, 0x6c517f6c517f6c51ULL, }, + { 0x507f6d507f6d507fULL, 0x6d507f6d507f6d50ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x507f6b507f6b507fULL, 0x6b507f6b507f6b50ULL, }, + { 0x4f7f6c4f7f6c4f7fULL, 0x6c4f7f6c4f7f6c4fULL, }, + { 0x1e73391e73391e73ULL, 0x391e73391e73391eULL, }, /* 48 */ + { 0x1d72381d72381d72ULL, 0x381d72381d72381dULL, }, + { 0x737f7f737f7f737fULL, 0x7f737f7f737f7f73ULL, }, + { 0x727f7f727f7f727fULL, 0x7f727f7f727f7f72ULL, }, + { 0x517f6c517f6c517fULL, 0x6c517f6c517f6c51ULL, }, + { 0x507f6b507f6b507fULL, 0x6b507f6b507f6b50ULL, }, + { 0x3a7f703a7f703a7fULL, 0x703a7f703a7f703aULL, }, + { 0x397f71397f71397fULL, 0x71397f71397f7139ULL, }, + { 0x1d723a1d723a1d72ULL, 0x3a1d723a1d723a1dULL, }, /* 56 */ + { 0x1c71391c71391c71ULL, 0x391c71391c71391cULL, }, + { 0x727f7f727f7f727fULL, 0x7f727f7f727f7f72ULL, }, + { 0x717f7f717f7f717fULL, 0x7f717f7f717f7f71ULL, }, + { 0x507f6d507f6d507fULL, 0x6d507f6d507f6d50ULL, }, + { 0x4f7f6c4f7f6c4f7fULL, 0x6c4f7f6c4f7f6c4fULL, }, + { 0x397f71397f71397fULL, 0x71397f71397f7139ULL, }, + { 0x387f72387f72387fULL, 0x72387f72387f7238ULL, }, + { 0x7f7f3468507f7f7fULL, 0x7f7f167f047f7f18ULL, }, /* 64 */ + { 0x7d7f1a7f757f7f48ULL, 0x5d705078177f7f10ULL, }, + { 0x7f7f6c7f6f7f7f7fULL, 0x727f455f577f7520ULL, }, + { 0x7f7f307f7f7f737fULL, 0x7f767f7f597f6e6cULL, }, + { 0x7d7f1a7f757f7f48ULL, 0x5d705078177f7f10ULL, }, + { 0x0a7f007f7f7f7210ULL, 0x24127f342a7e7f08ULL, }, + { 0x597f527f7f7f7f7fULL, 0x39317f1b6a6a7718ULL, }, + { 0x757f167f7f7f5756ULL, 0x7f187f426c7f7064ULL, }, + { 0x7f7f6c7f6f7f7f7fULL, 0x727f455f577f7520ULL, }, /* 72 */ + { 0x597f527f7f7f7f7fULL, 0x39317f1b6a6a7718ULL, }, + { 0x7f7f7f7f7f627f7fULL, 0x4e5074027f564a28ULL, }, + { 0x7f7f687f7f627f7fULL, 0x7f377f297f6d4374ULL, }, + { 0x7f7f307f7f7f737fULL, 0x7f767f7f597f6e6cULL, }, + { 0x757f167f7f7f5756ULL, 0x7f187f426c7f7064ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_d.c new file mode 100644 index 0000000000..af0f3d3700 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_A.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_A.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, /* 0 */ + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x5555555555555557ULL, 0x5555555555555557ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0x3333333333333335ULL, 0x3333333333333335ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x1c71c71c71c71c73ULL, 0x38e38e38e38e38e4ULL, }, + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e5ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e4ULL, }, + { 0x5555555555555557ULL, 0x5555555555555557ULL, }, /* 16 */ + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c8ULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c7ULL, 0x7fffffffffffffffULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c7ULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c6ULL, 0x7fffffffffffffffULL, }, + { 0x3333333333333335ULL, 0x3333333333333335ULL, }, /* 32 */ + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x6666666666666668ULL, 0x6666666666666668ULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x4fa4fa4fa4fa4fa6ULL, 0x6c16c16c16c16c17ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c18ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0x6c16c16c16c16c17ULL, }, + { 0x1c71c71c71c71c73ULL, 0x38e38e38e38e38e4ULL, }, /* 48 */ + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e3ULL, }, + { 0x71c71c71c71c71c8ULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c7ULL, 0x7fffffffffffffffULL, }, + { 0x4fa4fa4fa4fa4fa6ULL, 0x6c16c16c16c16c17ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c16ULL, }, + { 0x38e38e38e38e38e4ULL, 0x71c71c71c71c71c6ULL, }, + { 0x38e38e38e38e38e3ULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71c71c71c72ULL, 0x38e38e38e38e38e5ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e4ULL, }, + { 0x71c71c71c71c71c7ULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c6ULL, 0x7fffffffffffffffULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x6c16c16c16c16c18ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0x6c16c16c16c16c17ULL, }, + { 0x38e38e38e38e38e3ULL, 0x71c71c71c71c71c7ULL, }, + { 0x38e38e38e38e38e2ULL, 0x71c71c71c71c71c8ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, /* 64 */ + { 0x7bd718d08a09e3b8ULL, 0x5e5ec67913bb0308ULL, }, + { 0x7fffffffffffffffULL, 0x733fd25ea9a6d520ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7bd718d08a09e3b8ULL, 0x5e5ec67913bb0308ULL, }, + { 0x0883ff3964d871f0ULL, 0x25ef76342a7ea5f8ULL, }, + { 0x57e750f1f89cad78ULL, 0x3ad08219c06a7810ULL, }, + { 0x749115ea109e1b46ULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x733fd25ea9a6d520ULL, }, /* 72 */ + { 0x57e750f1f89cad78ULL, 0x3ad08219c06a7810ULL, }, + { 0x7fffffffffffffffULL, 0x4fb18dff56564a28ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x749115ea109e1b46ULL, 0x7fffffffffffffffULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_h.c new file mode 100644 index 0000000000..4d3774fef2 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_A.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_A.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, /* 0 */ + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x5557555755575557ULL, 0x5557555755575557ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0x3335333533353335ULL, 0x3335333533353335ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x1c7338e471c91c73ULL, 0x38e471c91c7338e4ULL, }, + { 0x1c7238e571c81c72ULL, 0x38e571c81c7238e5ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c7238e371c81c72ULL, 0x38e371c81c7238e3ULL, }, + { 0x1c7138e471c71c71ULL, 0x38e471c71c7138e4ULL, }, + { 0x5557555755575557ULL, 0x5557555755575557ULL, }, /* 16 */ + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x71c87fff7fff71c8ULL, 0x7fff7fff71c87fffULL, }, + { 0x71c77fff7fff71c7ULL, 0x7fff7fff71c77fffULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x71c77fff7fff71c7ULL, 0x7fff7fff71c77fffULL, }, + { 0x71c67fff7fff71c6ULL, 0x7fff7fff71c67fffULL, }, + { 0x3335333533353335ULL, 0x3335333533353335ULL, }, /* 32 */ + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x6668666866686668ULL, 0x6668666866686668ULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x4fa66c177fff4fa6ULL, 0x6c177fff4fa66c17ULL, }, + { 0x4fa56c187fff4fa5ULL, 0x6c187fff4fa56c18ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x4fa56c167fff4fa5ULL, 0x6c167fff4fa56c16ULL, }, + { 0x4fa46c177fff4fa4ULL, 0x6c177fff4fa46c17ULL, }, + { 0x1c7338e471c91c73ULL, 0x38e471c91c7338e4ULL, }, /* 48 */ + { 0x1c7238e371c81c72ULL, 0x38e371c81c7238e3ULL, }, + { 0x71c87fff7fff71c8ULL, 0x7fff7fff71c87fffULL, }, + { 0x71c77fff7fff71c7ULL, 0x7fff7fff71c77fffULL, }, + { 0x4fa66c177fff4fa6ULL, 0x6c177fff4fa66c17ULL, }, + { 0x4fa56c167fff4fa5ULL, 0x6c167fff4fa56c16ULL, }, + { 0x38e471c67fff38e4ULL, 0x71c67fff38e471c6ULL, }, + { 0x38e371c77fff38e3ULL, 0x71c77fff38e371c7ULL, }, + { 0x1c7238e571c81c72ULL, 0x38e571c81c7238e5ULL, }, /* 56 */ + { 0x1c7138e471c71c71ULL, 0x38e471c71c7138e4ULL, }, + { 0x71c77fff7fff71c7ULL, 0x7fff7fff71c77fffULL, }, + { 0x71c67fff7fff71c6ULL, 0x7fff7fff71c67fffULL, }, + { 0x4fa56c187fff4fa5ULL, 0x6c187fff4fa56c18ULL, }, + { 0x4fa46c177fff4fa4ULL, 0x6c177fff4fa46c17ULL, }, + { 0x38e371c77fff38e3ULL, 0x71c77fff38e371c7ULL, }, + { 0x38e271c87fff38e2ULL, 0x71c87fff38e271c8ULL, }, + { 0x7fff326850c47fffULL, 0x7fff16bc030a7fffULL, }, /* 64 */ + { 0x7bd8199775f57fffULL, 0x5e5e504416c47fffULL, }, + { 0x7fff6a8a6e937fffULL, 0x733f445f565a7508ULL, }, + { 0x7fff2f817fff72f2ULL, 0x7fff7fff58436d54ULL, }, + { 0x7bd8199775f57fffULL, 0x5e5e504416c47fffULL, }, + { 0x088400c67fff71f0ULL, 0x25ee7fff2a7e7fffULL, }, + { 0x57e851b97fff7fffULL, 0x3acf7de76a147810ULL, }, + { 0x749116b07fff56aaULL, 0x7fff7fff6bfd705cULL, }, + { 0x7fff6a8a6e937fffULL, 0x733f445f565a7508ULL, }, /* 72 */ + { 0x57e851b97fff7fffULL, 0x3acf7de76a147810ULL, }, + { 0x7fff7fff7fff7fffULL, 0x4fb072027fff4a28ULL, }, + { 0x7fff67a37fff7fffULL, 0x7fff7fff7fff4274ULL, }, + { 0x7fff2f817fff72f2ULL, 0x7fff7fff58436d54ULL, }, + { 0x749116b07fff56aaULL, 0x7fff7fff6bfd705cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_w.c new file mode 100644 index 0000000000..6f06fdc7cd --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_a_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_A.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_A.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, /* 0 */ + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x5555555755555557ULL, 0x5555555755555557ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0x3333333533333335ULL, 0x3333333533333335ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x1c71c71e71c71c73ULL, 0x38e38e391c71c71eULL, }, + { 0x1c71c71d71c71c72ULL, 0x38e38e3a1c71c71dULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71d71c71c72ULL, 0x38e38e381c71c71dULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e391c71c71cULL, }, + { 0x5555555755555557ULL, 0x5555555755555557ULL, }, /* 16 */ + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x71c71c737fffffffULL, 0x7fffffff71c71c73ULL, }, + { 0x71c71c727fffffffULL, 0x7fffffff71c71c72ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x71c71c727fffffffULL, 0x7fffffff71c71c72ULL, }, + { 0x71c71c717fffffffULL, 0x7fffffff71c71c71ULL, }, + { 0x3333333533333335ULL, 0x3333333533333335ULL, }, /* 32 */ + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x6666666866666668ULL, 0x6666666866666668ULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x4fa4fa517fffffffULL, 0x6c16c16c4fa4fa51ULL, }, + { 0x4fa4fa507fffffffULL, 0x6c16c16d4fa4fa50ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x4fa4fa507fffffffULL, 0x6c16c16b4fa4fa50ULL, }, + { 0x4fa4fa4f7fffffffULL, 0x6c16c16c4fa4fa4fULL, }, + { 0x1c71c71e71c71c73ULL, 0x38e38e391c71c71eULL, }, /* 48 */ + { 0x1c71c71d71c71c72ULL, 0x38e38e381c71c71dULL, }, + { 0x71c71c737fffffffULL, 0x7fffffff71c71c73ULL, }, + { 0x71c71c727fffffffULL, 0x7fffffff71c71c72ULL, }, + { 0x4fa4fa517fffffffULL, 0x6c16c16c4fa4fa51ULL, }, + { 0x4fa4fa507fffffffULL, 0x6c16c16b4fa4fa50ULL, }, + { 0x38e38e3a7fffffffULL, 0x71c71c7038e38e3aULL, }, + { 0x38e38e397fffffffULL, 0x71c71c7138e38e39ULL, }, + { 0x1c71c71d71c71c72ULL, 0x38e38e3a1c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x38e38e391c71c71cULL, }, + { 0x71c71c727fffffffULL, 0x7fffffff71c71c72ULL, }, + { 0x71c71c717fffffffULL, 0x7fffffff71c71c71ULL, }, + { 0x4fa4fa507fffffffULL, 0x6c16c16d4fa4fa50ULL, }, + { 0x4fa4fa4f7fffffffULL, 0x6c16c16c4fa4fa4fULL, }, + { 0x38e38e397fffffffULL, 0x71c71c7138e38e39ULL, }, + { 0x38e38e387fffffffULL, 0x71c71c7238e38e38ULL, }, + { 0x7fffffff50c4aa80ULL, 0x7fffffff03089fe8ULL, }, /* 64 */ + { 0x7bd718d175f61c48ULL, 0x5e5ec67816c3a2f0ULL, }, + { 0x7fffffff6e92c9c0ULL, 0x733fd25d56592ae0ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff58416d54ULL, }, + { 0x7bd718d175f61c48ULL, 0x5e5ec67816c3a2f0ULL, }, + { 0x0883ff3a7fffffffULL, 0x25ef76342a7ea5f8ULL, }, + { 0x57e750f37fffffffULL, 0x3ad082196a142de8ULL, }, + { 0x749115ea7fffffffULL, 0x7fffffff6bfc705cULL, }, + { 0x7fffffff6e92c9c0ULL, 0x733fd25d56592ae0ULL, }, /* 72 */ + { 0x57e750f37fffffffULL, 0x3ad082196a142de8ULL, }, + { 0x7fffffff7fffffffULL, 0x4fb18dfe7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff58416d54ULL, }, + { 0x749115ea7fffffffULL, 0x7fffffff6bfc705cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_A_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_b.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_b.c new file mode 100644 index 0000000000..e6cb9871f5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_S.B + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfefefefefefefefeULL, 0xfefefefefefefefeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xa9a9a9a9a9a9a9a9ULL, 0xa9a9a9a9a9a9a9a9ULL, }, + { 0x5454545454545454ULL, 0x5454545454545454ULL, }, + { 0xcbcbcbcbcbcbcbcbULL, 0xcbcbcbcbcbcbcbcbULL, }, + { 0x3232323232323232ULL, 0x3232323232323232ULL, }, + { 0xe28d37e28d37e28dULL, 0x37e28d37e28d37e2ULL, }, + { 0x1b70c61b70c61b70ULL, 0xc61b70c61b70c61bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xa9a9a9a9a9a9a9a9ULL, 0xa9a9a9a9a9a9a9a9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8d80e28d80e28d80ULL, 0xe28d80e28d80e28dULL, }, + { 0xc61b80c61b80c61bULL, 0x80c61b80c61b80c6ULL, }, + { 0x5454545454545454ULL, 0x5454545454545454ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x2121212121212121ULL, 0x2121212121212121ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x38e37f38e37f38e3ULL, 0x7f38e37f38e37f38ULL, }, + { 0x717f1c717f1c717fULL, 0x1c717f1c717f1c71ULL, }, + { 0xcbcbcbcbcbcbcbcbULL, 0xcbcbcbcbcbcbcbcbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x2121212121212121ULL, 0x2121212121212121ULL, }, + { 0x9898989898989898ULL, 0x9898989898989898ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaf8004af8004af80ULL, 0x04af8004af8004afULL, }, + { 0xe83d93e83d93e83dULL, 0x93e83d93e83d93e8ULL, }, + { 0x3232323232323232ULL, 0x3232323232323232ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16b16c16b16c1ULL, 0x6b16c16b16c16b16ULL, }, + { 0x4f7ffa4f7ffa4f7fULL, 0xfa4f7ffa4f7ffa4fULL, }, + { 0xe28d37e28d37e28dULL, 0x37e28d37e28d37e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8d80e28d80e28d80ULL, 0xe28d80e28d80e28dULL, }, + { 0x38e37f38e37f38e3ULL, 0x7f38e37f38e37f38ULL, }, + { 0xaf8004af8004af80ULL, 0x04af8004af8004afULL, }, + { 0x16c16b16c16b16c1ULL, 0x6b16c16b16c16b16ULL, }, + { 0xc68070c68070c680ULL, 0x70c68070c68070c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1b70c61b70c61b70ULL, 0xc61b70c61b70c61bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc61b80c61b80c61bULL, 0x80c61b80c61b80c6ULL, }, + { 0x717f1c717f1c717fULL, 0x1c717f1c717f1c71ULL, }, + { 0xe83d93e83d93e83dULL, 0x93e83d93e83d93e8ULL, }, + { 0x4f7ffa4f7ffa4f7fULL, 0xfa4f7ffa4f7ffa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x387f8e387f8e387fULL, 0x8e387f8e387f8e38ULL, }, + { 0x807fcc98507f7f7fULL, 0x7f7f167ffc7f8018ULL, }, /* 64 */ + { 0x8328e62f75f51c48ULL, 0x5d5ec678137f0208ULL, }, + { 0x807f9480e131e0c0ULL, 0x723fd15da97fd520ULL, }, + { 0xf87ffc197f7f377fULL, 0xd8589336a77f92acULL, }, + { 0x8328e62f75f51c48ULL, 0x5d5ec678137f0208ULL, }, + { 0xf680007f7f808e10ULL, 0x24ee80342a7e7ff8ULL, }, + { 0xa718ae0d06808088ULL, 0x39cf8119c06a7710ULL, }, + { 0x6b0d167f7fc4a956ULL, 0x9fe880f2be7f349cULL, }, + { 0x807f9480e131e0c0ULL, 0x723fd15da97fd520ULL, }, /* 72 */ + { 0xa718ae0d06808088ULL, 0x39cf8119c06a7710ULL, }, + { 0x807f8080809e8080ULL, 0x4eb08cfe80564a28ULL, }, + { 0x1c7fc4f7170080ceULL, 0xb4c980d7806d07b4ULL, }, + { 0xf87ffc197f7f377fULL, 0xd8589336a77f92acULL, }, + { 0x6b0d167f7fc4a956ULL, 0x9fe880f2be7f349cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_d.c new file mode 100644 index 0000000000..2cda5d9661 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_S.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffffffffffeULL, 0xfffffffffffffffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaa9ULL, 0xaaaaaaaaaaaaaaa9ULL, }, + { 0x5555555555555554ULL, 0x5555555555555554ULL, }, + { 0xcccccccccccccccbULL, 0xcccccccccccccccbULL, }, + { 0x3333333333333332ULL, 0x3333333333333332ULL, }, + { 0xe38e38e38e38e38dULL, 0x38e38e38e38e38e2ULL, }, + { 0x1c71c71c71c71c70ULL, 0xc71c71c71c71c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaa9ULL, 0xaaaaaaaaaaaaaaa9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0xc71c71c71c71c71bULL, 0x8000000000000000ULL, }, + { 0x5555555555555554ULL, 0x5555555555555554ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x2222222222222221ULL, 0x2222222222222221ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x38e38e38e38e38e3ULL, 0x7fffffffffffffffULL, }, + { 0x71c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0xcccccccccccccccbULL, 0xcccccccccccccccbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x2222222222222221ULL, 0x2222222222222221ULL, }, + { 0x9999999999999998ULL, 0x9999999999999998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xb05b05b05b05b05aULL, 0x05b05b05b05b05afULL, }, + { 0xe93e93e93e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x3333333333333332ULL, 0x3333333333333332ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16c16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xe38e38e38e38e38dULL, 0x38e38e38e38e38e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0x38e38e38e38e38e3ULL, 0x7fffffffffffffffULL, }, + { 0xb05b05b05b05b05aULL, 0x05b05b05b05b05afULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16c16c16c16ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c70ULL, 0xc71c71c71c71c71bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71bULL, 0x8000000000000000ULL, }, + { 0x71c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe93e93e93e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e2ULL, 0x8e38e38e38e38e38ULL, }, + { 0x8000000000000000ULL, 0x7fffffffffffffffULL, }, /* 64 */ + { 0x8428e72f75f61c48ULL, 0x5e5ec67913bb0308ULL, }, + { 0x8000000000000000ULL, 0x733fd25ea9a6d520ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589437a7be92acULL, }, + { 0x8428e72f75f61c48ULL, 0x5e5ec67913bb0308ULL, }, + { 0xf77c00c69b278e10ULL, 0x25ef76342a7ea5f8ULL, }, + { 0xa818af0e07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x6c0d16b0abc5a956ULL, 0xa0e943f2be82359cULL, }, + { 0x8000000000000000ULL, 0x733fd25ea9a6d520ULL, }, /* 72 */ + { 0xa818af0e07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x8000000000000000ULL, 0x4fb18dff56564a28ULL, }, + { 0x1ca9c4f818016dceULL, 0xb5ca4fd8546e07b4ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589437a7be92acULL, }, + { 0x6c0d16b0abc5a956ULL, 0xa0e943f2be82359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_h.c new file mode 100644 index 0000000000..5539322423 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_S.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffefffefffefffeULL, 0xfffefffefffefffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaa9aaa9aaa9aaa9ULL, 0xaaa9aaa9aaa9aaa9ULL, }, + { 0x5554555455545554ULL, 0x5554555455545554ULL, }, + { 0xcccbcccbcccbcccbULL, 0xcccbcccbcccbcccbULL, }, + { 0x3332333233323332ULL, 0x3332333233323332ULL, }, + { 0xe38d38e28e37e38dULL, 0x38e28e37e38d38e2ULL, }, + { 0x1c70c71b71c61c70ULL, 0xc71b71c61c70c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaa9aaa9aaa9aaa9ULL, 0xaaa9aaa9aaa9aaa9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8e38e38d80008e38ULL, 0xe38d80008e38e38dULL, }, + { 0xc71b80001c71c71bULL, 0x80001c71c71b8000ULL, }, + { 0x5554555455545554ULL, 0x5554555455545554ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x2221222122212221ULL, 0x2221222122212221ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x38e37fffe38d38e3ULL, 0x7fffe38d38e37fffULL, }, + { 0x71c61c717fff71c6ULL, 0x1c717fff71c61c71ULL, }, + { 0xcccbcccbcccbcccbULL, 0xcccbcccbcccbcccbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x2221222122212221ULL, 0x2221222122212221ULL, }, + { 0x9998999899989998ULL, 0x9998999899989998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xb05a05af8000b05aULL, 0x05af8000b05a05afULL, }, + { 0xe93d93e83e93e93dULL, 0x93e83e93e93d93e8ULL, }, + { 0x3332333233323332ULL, 0x3332333233323332ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16c16c16b16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0x4fa4fa4f7fff4fa4ULL, 0xfa4f7fff4fa4fa4fULL, }, + { 0xe38d38e28e37e38dULL, 0x38e28e37e38d38e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8e38e38d80008e38ULL, 0xe38d80008e38e38dULL, }, + { 0x38e37fffe38d38e3ULL, 0x7fffe38d38e37fffULL, }, + { 0xb05a05af8000b05aULL, 0x05af8000b05a05afULL, }, + { 0x16c16c16c16b16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0xc71c71c68000c71cULL, 0x71c68000c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c70c71b71c61c70ULL, 0xc71b71c61c70c71bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71b80001c71c71bULL, 0x80001c71c71b8000ULL, }, + { 0x71c61c717fff71c6ULL, 0x1c717fff71c61c71ULL, }, + { 0xe93d93e83e93e93dULL, 0x93e83e93e93d93e8ULL, }, + { 0x4fa4fa4f7fff4fa4ULL, 0xfa4f7fff4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e28e387fff38e2ULL, 0x8e387fff38e28e38ULL, }, + { 0x8000cd9850c47fffULL, 0x7fff16bcfcf68000ULL, }, /* 64 */ + { 0x8428e72f75f51c48ULL, 0x5e5ec67813ba0308ULL, }, + { 0x80009576e231e0c0ULL, 0x733fd25da9a6d520ULL, }, + { 0xf8b9fd197fff378eULL, 0xd9589436a7bd92acULL, }, + { 0x8428e72f75f51c48ULL, 0x5e5ec67813ba0308ULL, }, + { 0xf77c00c67fff8e10ULL, 0x25ee80002a7e7fffULL, }, + { 0xa818af0d07628000ULL, 0x3acf8219c06a7810ULL, }, + { 0x6c0d16b07fffa956ULL, 0xa0e88000be81359cULL, }, + { 0x80009576e231e0c0ULL, 0x733fd25da9a6d520ULL, }, /* 72 */ + { 0xa818af0d07628000ULL, 0x3acf8219c06a7810ULL, }, + { 0x8000800080008000ULL, 0x4fb08dfe80004a28ULL, }, + { 0x1ca9c4f718008000ULL, 0xb5c98000800007b4ULL, }, + { 0xf8b9fd197fff378eULL, 0xd9589436a7bd92acULL, }, + { 0x6c0d16b07fffa956ULL, 0xa0e88000be81359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_w.c new file mode 100644 index 0000000000..4f2cc3862e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_S.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffefffffffeULL, 0xfffffffefffffffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaa9aaaaaaa9ULL, 0xaaaaaaa9aaaaaaa9ULL, }, + { 0x5555555455555554ULL, 0x5555555455555554ULL, }, + { 0xcccccccbcccccccbULL, 0xcccccccbcccccccbULL, }, + { 0x3333333233333332ULL, 0x3333333233333332ULL, }, + { 0xe38e38e28e38e38dULL, 0x38e38e37e38e38e2ULL, }, + { 0x1c71c71b71c71c70ULL, 0xc71c71c61c71c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaa9aaaaaaa9ULL, 0xaaaaaaa9aaaaaaa9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8e38e38d80000000ULL, 0xe38e38e28e38e38dULL, }, + { 0xc71c71c61c71c71bULL, 0x80000000c71c71c6ULL, }, + { 0x5555555455555554ULL, 0x5555555455555554ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x2222222122222221ULL, 0x2222222122222221ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x38e38e38e38e38e3ULL, 0x7fffffff38e38e38ULL, }, + { 0x71c71c717fffffffULL, 0x1c71c71c71c71c71ULL, }, + { 0xcccccccbcccccccbULL, 0xcccccccbcccccccbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x2222222122222221ULL, 0x2222222122222221ULL, }, + { 0x9999999899999998ULL, 0x9999999899999998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xb05b05af80000000ULL, 0x05b05b04b05b05afULL, }, + { 0xe93e93e83e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x3333333233333332ULL, 0x3333333233333332ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0x4fa4fa4f7fffffffULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xe38e38e28e38e38dULL, 0x38e38e37e38e38e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8e38e38d80000000ULL, 0xe38e38e28e38e38dULL, }, + { 0x38e38e38e38e38e3ULL, 0x7fffffff38e38e38ULL, }, + { 0xb05b05af80000000ULL, 0x05b05b04b05b05afULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0xc71c71c680000000ULL, 0x71c71c70c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71b71c71c70ULL, 0xc71c71c61c71c71bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c61c71c71bULL, 0x80000000c71c71c6ULL, }, + { 0x71c71c717fffffffULL, 0x1c71c71c71c71c71ULL, }, + { 0xe93e93e83e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x4fa4fa4f7fffffffULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e38e387fffffffULL, 0x8e38e38e38e38e38ULL, }, + { 0x8000000050c4aa80ULL, 0x7ffffffffcf76018ULL, }, /* 64 */ + { 0x8428e72f75f61c48ULL, 0x5e5ec67813bb0308ULL, }, + { 0x80000000e231e0c0ULL, 0x733fd25da9a6d520ULL, }, + { 0xf8b9fd197fffffffULL, 0xd9589436a7be92acULL, }, + { 0x8428e72f75f61c48ULL, 0x5e5ec67813bb0308ULL, }, + { 0xf77c00c67fffffffULL, 0x25ef76342a7ea5f8ULL, }, + { 0xa818af0d07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x6c0d16b07fffffffULL, 0xa0e943f2be82359cULL, }, + { 0x80000000e231e0c0ULL, 0x733fd25da9a6d520ULL, }, /* 72 */ + { 0xa818af0d07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x8000000080000000ULL, 0x4fb18dfe80000000ULL, }, + { 0x1ca9c4f718016dceULL, 0xb5ca4fd780000000ULL, }, + { 0xf8b9fd197fffffffULL, 0xd9589436a7be92acULL, }, + { 0x6c0d16b07fffffffULL, 0xa0e943f2be82359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_b.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_b.c new file mode 100644 index 0000000000..e2d9be38e7 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_U.B + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0xffffe2ffffe2ffffULL, 0xe2ffffe2ffffe2ffULL, }, + { 0xc6ffffc6ffffc6ffULL, 0xffc6ffffc6ffffc6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffe38dffe38dffe3ULL, 0x8dffe38dffe38dffULL, }, + { 0x71c6ff71c6ff71c6ULL, 0xff71c6ff71c6ff71ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe8ffffe8ffffe8ffULL, 0xffe8ffffe8ffffe8ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xffc16bffc16bffc1ULL, 0x6bffc16bffc16bffULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffe2ffffe2ffffULL, 0xe2ffffe2ffffe2ffULL, }, + { 0xffe38dffe38dffe3ULL, 0x8dffe38dffe38dffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffc16bffc16bffc1ULL, 0x6bffc16bffc16bffULL, }, + { 0xffff70ffff70ffffULL, 0x70ffff70ffff70ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc6ffffc6ffffc6ffULL, 0xffc6ffffc6ffffc6ULL, }, + { 0x71c6ff71c6ff71c6ULL, 0xff71c6ff71c6ff71ULL, }, + { 0xe8ffffe8ffffe8ffULL, 0xffe8ffffe8ffffe8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e2ff38e2ff38e2ULL, 0xff38e2ff38e2ff38ULL, }, + { 0xffd4ffff50c4aa80ULL, 0x96ce16bcfff6ff18ULL, }, /* 64 */ + { 0xffffe6ff75f5ff48ULL, 0x5dffc678ffbaffffULL, }, + { 0xffc4ffffe1ffe0c0ULL, 0x72ffd1ffffa6d520ULL, }, + { 0xf8b9fcff8693ff8eULL, 0xd8ff93ffffbdffacULL, }, + { 0xffffe6ff75f5ff48ULL, 0x5dffc678ffbaffffULL, }, + { 0xffff00c69affff10ULL, 0x24ffff342a7ea4ffULL, }, + { 0xffffaeffffffff88ULL, 0x39ffffffc06a77ffULL, }, + { 0xffff16b0abc4ff56ULL, 0x9ffffff2be81ffffULL, }, + { 0xffc4ffffe1ffe0c0ULL, 0x72ffd1ffffa6d520ULL, }, /* 72 */ + { 0xffffaeffffffff88ULL, 0x39ffffffc06a77ffULL, }, + { 0xffb4ffffffffffffULL, 0x4effffffff564a28ULL, }, + { 0xffa9c4f7ffffffceULL, 0xb4ffffffff6dffb4ULL, }, + { 0xf8b9fcff8693ff8eULL, 0xd8ff93ffffbdffacULL, }, + { 0xffff16b0abc4ff56ULL, 0x9ffffff2be81ffffULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_d.c new file mode 100644 index 0000000000..8418c636b6 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_U.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0xffffffffffffffffULL, 0xe38e38e38e38e38dULL, }, + { 0xc71c71c71c71c71bULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0x8e38e38e38e38e38ULL, }, + { 0x71c71c71c71c71c6ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe93e93e93e93e93dULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xffffffffffffffffULL, 0x6c16c16c16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xe38e38e38e38e38dULL, }, + { 0xffffffffffffffffULL, 0x8e38e38e38e38e38ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x6c16c16c16c16c16ULL, }, + { 0xffffffffffffffffULL, 0x71c71c71c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71bULL, 0xffffffffffffffffULL, }, + { 0x71c71c71c71c71c6ULL, 0xffffffffffffffffULL, }, + { 0xe93e93e93e93e93dULL, 0xffffffffffffffffULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e2ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x96ce16bdfcf76018ULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x5e5ec67913bb0308ULL, }, + { 0xffffffffffffffffULL, 0x733fd25ea9a6d520ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589437a7be92acULL, }, + { 0xffffffffffffffffULL, 0x5e5ec67913bb0308ULL, }, + { 0xffffffffffffffffULL, 0x25ef76342a7ea5f8ULL, }, + { 0xffffffffffffffffULL, 0x3ad08219c06a7810ULL, }, + { 0xffffffffffffffffULL, 0xa0e943f2be82359cULL, }, + { 0xffffffffffffffffULL, 0x733fd25ea9a6d520ULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x3ad08219c06a7810ULL, }, + { 0xffffffffffffffffULL, 0x4fb18dff56564a28ULL, }, + { 0xffffffffffffffffULL, 0xb5ca4fd8546e07b4ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589437a7be92acULL, }, + { 0xffffffffffffffffULL, 0xa0e943f2be82359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_h.c new file mode 100644 index 0000000000..8a3b5c5cf5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_U.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0xffffe38dffffffffULL, 0xe38dffffffffe38dULL, }, + { 0xc71bffffffffc71bULL, 0xffffffffc71bffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffff8e38e38dffffULL, 0x8e38e38dffff8e38ULL, }, + { 0x71c6ffffc71c71c6ULL, 0xffffc71c71c6ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe93dffffffffe93dULL, 0xffffffffe93dffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xffff6c16c16bffffULL, 0x6c16c16bffff6c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffe38dffffffffULL, 0xe38dffffffffe38dULL, }, + { 0xffff8e38e38dffffULL, 0x8e38e38dffff8e38ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffff6c16c16bffffULL, 0x6c16c16bffff6c16ULL, }, + { 0xffff71c6ffffffffULL, 0x71c6ffffffff71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71bffffffffc71bULL, 0xffffffffc71bffffULL, }, + { 0x71c6ffffc71c71c6ULL, 0xffffc71c71c6ffffULL, }, + { 0xe93dffffffffe93dULL, 0xffffffffe93dffffULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e2ffffe38e38e2ULL, 0xffffe38e38e2ffffULL, }, + { 0xffffffff50c4aa80ULL, 0x96ce16bcffffffffULL, }, /* 64 */ + { 0xffffe72f75f5ffffULL, 0x5e5ec678ffffffffULL, }, + { 0xffffffffe231e0c0ULL, 0x733fd25dffffd520ULL, }, + { 0xf8b9fd198693ffffULL, 0xd9589436ffffffffULL, }, + { 0xffffe72f75f5ffffULL, 0x5e5ec678ffffffffULL, }, + { 0xffff00c69b26ffffULL, 0x25eeffff2a7ea5f8ULL, }, + { 0xffffaf0dffffffffULL, 0x3acfffffc06a7810ULL, }, + { 0xffff16b0abc4ffffULL, 0xa0e8ffffbe81ffffULL, }, + { 0xffffffffe231e0c0ULL, 0x733fd25dffffd520ULL, }, /* 72 */ + { 0xffffaf0dffffffffULL, 0x3acfffffc06a7810ULL, }, + { 0xffffffffffffffffULL, 0x4fb0ffffffff4a28ULL, }, + { 0xffffc4f7ffffffffULL, 0xb5c9ffffffffffffULL, }, + { 0xf8b9fd198693ffffULL, 0xd9589436ffffffffULL, }, + { 0xffff16b0abc4ffffULL, 0xa0e8ffffbe81ffffULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_w.c new file mode 100644 index 0000000000..b18bdc3ea0 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_adds_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDS_U.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDS_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0xffffffffffffffffULL, 0xe38e38e2ffffffffULL, }, + { 0xc71c71c6ffffffffULL, 0xffffffffc71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffe38e38e3ULL, 0x8e38e38dffffffffULL, }, + { 0x71c71c71c71c71c6ULL, 0xffffffff71c71c71ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe93e93e8ffffffffULL, 0xffffffffe93e93e8ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xffffffffc16c16c1ULL, 0x6c16c16bffffffffULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xe38e38e2ffffffffULL, }, + { 0xffffffffe38e38e3ULL, 0x8e38e38dffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffc16c16c1ULL, 0x6c16c16bffffffffULL, }, + { 0xffffffffffffffffULL, 0x71c71c70ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c6ffffffffULL, 0xffffffffc71c71c6ULL, }, + { 0x71c71c71c71c71c6ULL, 0xffffffff71c71c71ULL, }, + { 0xe93e93e8ffffffffULL, 0xffffffffe93e93e8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e2ULL, 0xffffffff38e38e38ULL, }, + { 0xffffffff50c4aa80ULL, 0x96ce16bcffffffffULL, }, /* 64 */ + { 0xffffffff75f61c48ULL, 0x5e5ec678ffffffffULL, }, + { 0xffffffffe231e0c0ULL, 0x733fd25dffffffffULL, }, + { 0xf8b9fd198694378eULL, 0xd9589436ffffffffULL, }, + { 0xffffffff75f61c48ULL, 0x5e5ec678ffffffffULL, }, + { 0xffffffff9b278e10ULL, 0x25ef76342a7ea5f8ULL, }, + { 0xffffffffffffffffULL, 0x3ad08219c06a7810ULL, }, + { 0xffffffffabc5a956ULL, 0xa0e943f2be82359cULL, }, + { 0xffffffffe231e0c0ULL, 0x733fd25dffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x3ad08219c06a7810ULL, }, + { 0xffffffffffffffffULL, 0x4fb18dfeffffffffULL, }, + { 0xffffffffffffffffULL, 0xb5ca4fd7ffffffffULL, }, + { 0xf8b9fd198694378eULL, 0xd9589436ffffffffULL, }, + { 0xffffffffabc5a956ULL, 0xa0e943f2be82359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDS_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_b.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_b.c new file mode 100644 index 0000000000..c86c99291e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDV.B + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDV.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfefefefefefefefeULL, 0xfefefefefefefefeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xa9a9a9a9a9a9a9a9ULL, 0xa9a9a9a9a9a9a9a9ULL, }, + { 0x5454545454545454ULL, 0x5454545454545454ULL, }, + { 0xcbcbcbcbcbcbcbcbULL, 0xcbcbcbcbcbcbcbcbULL, }, + { 0x3232323232323232ULL, 0x3232323232323232ULL, }, + { 0xe28d37e28d37e28dULL, 0x37e28d37e28d37e2ULL, }, + { 0x1b70c61b70c61b70ULL, 0xc61b70c61b70c61bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xa9a9a9a9a9a9a9a9ULL, 0xa9a9a9a9a9a9a9a9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5454545454545454ULL, 0x5454545454545454ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7676767676767676ULL, 0x7676767676767676ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8d38e28d38e28d38ULL, 0xe28d38e28d38e28dULL, }, + { 0xc61b71c61b71c61bULL, 0x71c61b71c61b71c6ULL, }, + { 0x5454545454545454ULL, 0x5454545454545454ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x2121212121212121ULL, 0x2121212121212121ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x38e38d38e38d38e3ULL, 0x8d38e38d38e38d38ULL, }, + { 0x71c61c71c61c71c6ULL, 0x1c71c61c71c61c71ULL, }, + { 0xcbcbcbcbcbcbcbcbULL, 0xcbcbcbcbcbcbcbcbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7676767676767676ULL, 0x7676767676767676ULL, }, + { 0x2121212121212121ULL, 0x2121212121212121ULL, }, + { 0x9898989898989898ULL, 0x9898989898989898ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaf5a04af5a04af5aULL, 0x04af5a04af5a04afULL, }, + { 0xe83d93e83d93e83dULL, 0x93e83d93e83d93e8ULL, }, + { 0x3232323232323232ULL, 0x3232323232323232ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16b16c16b16c1ULL, 0x6b16c16b16c16b16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xe28d37e28d37e28dULL, 0x37e28d37e28d37e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8d38e28d38e28d38ULL, 0xe28d38e28d38e28dULL, }, + { 0x38e38d38e38d38e3ULL, 0x8d38e38d38e38d38ULL, }, + { 0xaf5a04af5a04af5aULL, 0x04af5a04af5a04afULL, }, + { 0x16c16b16c16b16c1ULL, 0x6b16c16b16c16b16ULL, }, + { 0xc61c70c61c70c61cULL, 0x70c61c70c61c70c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1b70c61b70c61b70ULL, 0xc61b70c61b70c61bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc61b71c61b71c61bULL, 0x71c61b71c61b71c6ULL, }, + { 0x71c61c71c61c71c6ULL, 0x1c71c61c71c61c71ULL, }, + { 0xe83d93e83d93e83dULL, 0x93e83d93e83d93e8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e28e38e28e38e2ULL, 0x8e38e28e38e28e38ULL, }, + { 0x10d4cc9850c4aa80ULL, 0x96ce16bcfcf66018ULL, }, /* 64 */ + { 0x8328e62f75f51c48ULL, 0x5d5ec67813ba0208ULL, }, + { 0x34c49476e131e0c0ULL, 0x723fd15da9a6d520ULL, }, + { 0xf8b9fc198693378eULL, 0xd8589336a7bd92acULL, }, + { 0x8328e62f75f51c48ULL, 0x5d5ec67813ba0208ULL, }, + { 0xf67c00c69a268e10ULL, 0x24ee76342a7ea4f8ULL, }, + { 0xa718ae0d06625288ULL, 0x39cf8119c06a7710ULL, }, + { 0x6b0d16b0abc4a956ULL, 0x9fe843f2be81349cULL, }, + { 0x34c49476e131e0c0ULL, 0x723fd15da9a6d520ULL, }, /* 72 */ + { 0xa718ae0d06625288ULL, 0x39cf8119c06a7710ULL, }, + { 0x58b45c54729e1600ULL, 0x4eb08cfe56564a28ULL, }, + { 0x1ca9c4f717006dceULL, 0xb4c94ed7546d07b4ULL, }, + { 0xf8b9fc198693378eULL, 0xd8589336a7bd92acULL, }, + { 0x6b0d16b0abc4a956ULL, 0x9fe843f2be81349cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_d.c new file mode 100644 index 0000000000..0f301515b3 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDV.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDV.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffffffffffeULL, 0xfffffffffffffffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaa9ULL, 0xaaaaaaaaaaaaaaa9ULL, }, + { 0x5555555555555554ULL, 0x5555555555555554ULL, }, + { 0xcccccccccccccccbULL, 0xcccccccccccccccbULL, }, + { 0x3333333333333332ULL, 0x3333333333333332ULL, }, + { 0xe38e38e38e38e38dULL, 0x38e38e38e38e38e2ULL, }, + { 0x1c71c71c71c71c70ULL, 0xc71c71c71c71c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaa9ULL, 0xaaaaaaaaaaaaaaa9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555554ULL, 0x5555555555555554ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7777777777777776ULL, 0x7777777777777776ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0xc71c71c71c71c71bULL, 0x71c71c71c71c71c6ULL, }, + { 0x5555555555555554ULL, 0x5555555555555554ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x2222222222222221ULL, 0x2222222222222221ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e38ULL, }, + { 0x71c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0xcccccccccccccccbULL, 0xcccccccccccccccbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7777777777777776ULL, 0x7777777777777776ULL, }, + { 0x2222222222222221ULL, 0x2222222222222221ULL, }, + { 0x9999999999999998ULL, 0x9999999999999998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xb05b05b05b05b05aULL, 0x05b05b05b05b05afULL, }, + { 0xe93e93e93e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x3333333333333332ULL, 0x3333333333333332ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16c16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xe38e38e38e38e38dULL, 0x38e38e38e38e38e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e38ULL, }, + { 0xb05b05b05b05b05aULL, 0x05b05b05b05b05afULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16c16c16c16ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c70ULL, 0xc71c71c71c71c71bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71bULL, 0x71c71c71c71c71c6ULL, }, + { 0x71c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe93e93e93e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e2ULL, 0x8e38e38e38e38e38ULL, }, + { 0x10d5cd9850c4aa80ULL, 0x96ce16bdfcf76018ULL, }, /* 64 */ + { 0x8428e72f75f61c48ULL, 0x5e5ec67913bb0308ULL, }, + { 0x34c59576e231e0c0ULL, 0x733fd25ea9a6d520ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589437a7be92acULL, }, + { 0x8428e72f75f61c48ULL, 0x5e5ec67913bb0308ULL, }, + { 0xf77c00c69b278e10ULL, 0x25ef76342a7ea5f8ULL, }, + { 0xa818af0e07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x6c0d16b0abc5a956ULL, 0xa0e943f2be82359cULL, }, + { 0x34c59576e231e0c0ULL, 0x733fd25ea9a6d520ULL, }, /* 72 */ + { 0xa818af0e07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x58b55d55739f1700ULL, 0x4fb18dff56564a28ULL, }, + { 0x1ca9c4f818016dceULL, 0xb5ca4fd8546e07b4ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589437a7be92acULL, }, + { 0x6c0d16b0abc5a956ULL, 0xa0e943f2be82359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_h.c new file mode 100644 index 0000000000..c6b4cf697b --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDV.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDV.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffefffefffefffeULL, 0xfffefffefffefffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaa9aaa9aaa9aaa9ULL, 0xaaa9aaa9aaa9aaa9ULL, }, + { 0x5554555455545554ULL, 0x5554555455545554ULL, }, + { 0xcccbcccbcccbcccbULL, 0xcccbcccbcccbcccbULL, }, + { 0x3332333233323332ULL, 0x3332333233323332ULL, }, + { 0xe38d38e28e37e38dULL, 0x38e28e37e38d38e2ULL, }, + { 0x1c70c71b71c61c70ULL, 0xc71b71c61c70c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaa9aaa9aaa9aaa9ULL, 0xaaa9aaa9aaa9aaa9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5554555455545554ULL, 0x5554555455545554ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7776777677767776ULL, 0x7776777677767776ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8e38e38d38e28e38ULL, 0xe38d38e28e38e38dULL, }, + { 0xc71b71c61c71c71bULL, 0x71c61c71c71b71c6ULL, }, + { 0x5554555455545554ULL, 0x5554555455545554ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x2221222122212221ULL, 0x2221222122212221ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x38e38e38e38d38e3ULL, 0x8e38e38d38e38e38ULL, }, + { 0x71c61c71c71c71c6ULL, 0x1c71c71c71c61c71ULL, }, + { 0xcccbcccbcccbcccbULL, 0xcccbcccbcccbcccbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7776777677767776ULL, 0x7776777677767776ULL, }, + { 0x2221222122212221ULL, 0x2221222122212221ULL, }, + { 0x9998999899989998ULL, 0x9998999899989998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xb05a05af5b04b05aULL, 0x05af5b04b05a05afULL, }, + { 0xe93d93e83e93e93dULL, 0x93e83e93e93d93e8ULL, }, + { 0x3332333233323332ULL, 0x3332333233323332ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16c16c16b16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xe38d38e28e37e38dULL, 0x38e28e37e38d38e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8e38e38d38e28e38ULL, 0xe38d38e28e38e38dULL, }, + { 0x38e38e38e38d38e3ULL, 0x8e38e38d38e38e38ULL, }, + { 0xb05a05af5b04b05aULL, 0x05af5b04b05a05afULL, }, + { 0x16c16c16c16b16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0xc71c71c61c70c71cULL, 0x71c61c70c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c70c71b71c61c70ULL, 0xc71b71c61c70c71bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71b71c61c71c71bULL, 0x71c61c71c71b71c6ULL, }, + { 0x71c61c71c71c71c6ULL, 0x1c71c71c71c61c71ULL, }, + { 0xe93d93e83e93e93dULL, 0x93e83e93e93d93e8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e28e38e38e38e2ULL, 0x8e38e38e38e28e38ULL, }, + { 0x10d4cd9850c4aa80ULL, 0x96ce16bcfcf66018ULL, }, /* 64 */ + { 0x8428e72f75f51c48ULL, 0x5e5ec67813ba0308ULL, }, + { 0x34c49576e231e0c0ULL, 0x733fd25da9a6d520ULL, }, + { 0xf8b9fd198693378eULL, 0xd9589436a7bd92acULL, }, + { 0x8428e72f75f51c48ULL, 0x5e5ec67813ba0308ULL, }, + { 0xf77c00c69b268e10ULL, 0x25ee76342a7ea5f8ULL, }, + { 0xa818af0d07625288ULL, 0x3acf8219c06a7810ULL, }, + { 0x6c0d16b0abc4a956ULL, 0xa0e843f2be81359cULL, }, + { 0x34c49576e231e0c0ULL, 0x733fd25da9a6d520ULL, }, /* 72 */ + { 0xa818af0d07625288ULL, 0x3acf8219c06a7810ULL, }, + { 0x58b45d54739e1700ULL, 0x4fb08dfe56564a28ULL, }, + { 0x1ca9c4f718006dceULL, 0xb5c94fd7546d07b4ULL, }, + { 0xf8b9fd198693378eULL, 0xd9589436a7bd92acULL, }, + { 0x6c0d16b0abc4a956ULL, 0xa0e843f2be81359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_w.c new file mode 100644 index 0000000000..2a565e8ed4 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_addv_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction ADDV.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ADDV.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffefffffffeULL, 0xfffffffefffffffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaa9aaaaaaa9ULL, 0xaaaaaaa9aaaaaaa9ULL, }, + { 0x5555555455555554ULL, 0x5555555455555554ULL, }, + { 0xcccccccbcccccccbULL, 0xcccccccbcccccccbULL, }, + { 0x3333333233333332ULL, 0x3333333233333332ULL, }, + { 0xe38e38e28e38e38dULL, 0x38e38e37e38e38e2ULL, }, + { 0x1c71c71b71c71c70ULL, 0xc71c71c61c71c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaa9aaaaaaa9ULL, 0xaaaaaaa9aaaaaaa9ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555455555554ULL, 0x5555555455555554ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7777777677777776ULL, 0x7777777677777776ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8e38e38d38e38e38ULL, 0xe38e38e28e38e38dULL, }, + { 0xc71c71c61c71c71bULL, 0x71c71c71c71c71c6ULL, }, + { 0x5555555455555554ULL, 0x5555555455555554ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x2222222122222221ULL, 0x2222222122222221ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38d38e38e38ULL, }, + { 0x71c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0xcccccccbcccccccbULL, 0xcccccccbcccccccbULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7777777677777776ULL, 0x7777777677777776ULL, }, + { 0x2222222122222221ULL, 0x2222222122222221ULL, }, + { 0x9999999899999998ULL, 0x9999999899999998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xb05b05af5b05b05aULL, 0x05b05b04b05b05afULL, }, + { 0xe93e93e83e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x3333333233333332ULL, 0x3333333233333332ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xe38e38e28e38e38dULL, 0x38e38e37e38e38e2ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8e38e38d38e38e38ULL, 0xe38e38e28e38e38dULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38d38e38e38ULL, }, + { 0xb05b05af5b05b05aULL, 0x05b05b04b05b05afULL, }, + { 0x16c16c16c16c16c1ULL, 0x6c16c16b16c16c16ULL, }, + { 0xc71c71c61c71c71cULL, 0x71c71c70c71c71c6ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71b71c71c70ULL, 0xc71c71c61c71c71bULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c61c71c71bULL, 0x71c71c71c71c71c6ULL, }, + { 0x71c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe93e93e83e93e93dULL, 0x93e93e93e93e93e8ULL, }, + { 0x4fa4fa4fa4fa4fa4ULL, 0xfa4fa4fa4fa4fa4fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e2ULL, 0x8e38e38e38e38e38ULL, }, + { 0x10d5cd9850c4aa80ULL, 0x96ce16bcfcf76018ULL, }, /* 64 */ + { 0x8428e72f75f61c48ULL, 0x5e5ec67813bb0308ULL, }, + { 0x34c59576e231e0c0ULL, 0x733fd25da9a6d520ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589436a7be92acULL, }, + { 0x8428e72f75f61c48ULL, 0x5e5ec67813bb0308ULL, }, + { 0xf77c00c69b278e10ULL, 0x25ef76342a7ea5f8ULL, }, + { 0xa818af0d07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x6c0d16b0abc5a956ULL, 0xa0e943f2be82359cULL, }, + { 0x34c59576e231e0c0ULL, 0x733fd25da9a6d520ULL, }, /* 72 */ + { 0xa818af0d07635288ULL, 0x3ad08219c06a7810ULL, }, + { 0x58b55d54739f1700ULL, 0x4fb18dfe56564a28ULL, }, + { 0x1ca9c4f718016dceULL, 0xb5ca4fd7546e07b4ULL, }, + { 0xf8b9fd198694378eULL, 0xd9589436a7be92acULL, }, + { 0x6c0d16b0abc5a956ULL, 0xa0e943f2be82359cULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ADDV_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_d.c new file mode 100644 index 0000000000..7845dc0218 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction HADD_S.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "HADD_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffffffffffeULL, 0xfffffffffffffffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffaaaaaaa9ULL, 0xffffffffaaaaaaa9ULL, }, + { 0x0000000055555554ULL, 0x0000000055555554ULL, }, + { 0xffffffffcccccccbULL, 0xffffffffcccccccbULL, }, + { 0x0000000033333332ULL, 0x0000000033333332ULL, }, + { 0xffffffff8e38e38dULL, 0xffffffffe38e38e2ULL, }, + { 0x0000000071c71c70ULL, 0x000000001c71c71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0xffffffffccccccccULL, 0xffffffffccccccccULL, }, + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0xffffffff8e38e38eULL, 0xffffffffe38e38e3ULL, }, + { 0x0000000071c71c71ULL, 0x000000001c71c71cULL, }, + { 0xffffffffaaaaaaa9ULL, 0xffffffffaaaaaaa9ULL, }, /* 16 */ + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0xffffffff55555554ULL, 0xffffffff55555554ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff77777776ULL, 0xffffffff77777776ULL, }, + { 0xffffffffddddddddULL, 0xffffffffddddddddULL, }, + { 0xffffffff38e38e38ULL, 0xffffffff8e38e38dULL, }, + { 0x000000001c71c71bULL, 0xffffffffc71c71c6ULL, }, + { 0x0000000055555554ULL, 0x0000000055555554ULL, }, /* 24 */ + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000022222221ULL, 0x0000000022222221ULL, }, + { 0x0000000088888888ULL, 0x0000000088888888ULL, }, + { 0xffffffffe38e38e3ULL, 0x0000000038e38e38ULL, }, + { 0x00000000c71c71c6ULL, 0x0000000071c71c71ULL, }, + { 0xffffffffcccccccbULL, 0xffffffffcccccccbULL, }, /* 32 */ + { 0xffffffffccccccccULL, 0xffffffffccccccccULL, }, + { 0xffffffff77777776ULL, 0xffffffff77777776ULL, }, + { 0x0000000022222221ULL, 0x0000000022222221ULL, }, + { 0xffffffff99999998ULL, 0xffffffff99999998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff5b05b05aULL, 0xffffffffb05b05afULL, }, + { 0x000000003e93e93dULL, 0xffffffffe93e93e8ULL, }, + { 0x0000000033333332ULL, 0x0000000033333332ULL, }, /* 40 */ + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0xffffffffddddddddULL, 0xffffffffddddddddULL, }, + { 0x0000000088888888ULL, 0x0000000088888888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000066666666ULL, 0x0000000066666666ULL, }, + { 0xffffffffc16c16c1ULL, 0x0000000016c16c16ULL, }, + { 0x00000000a4fa4fa4ULL, 0x000000004fa4fa4fULL, }, + { 0xffffffffe38e38e2ULL, 0x0000000038e38e37ULL, }, /* 48 */ + { 0xffffffffe38e38e3ULL, 0x0000000038e38e38ULL, }, + { 0xffffffff8e38e38dULL, 0xffffffffe38e38e2ULL, }, + { 0x0000000038e38e38ULL, 0x000000008e38e38dULL, }, + { 0xffffffffb05b05afULL, 0x0000000005b05b04ULL, }, + { 0x0000000016c16c16ULL, 0x000000006c16c16bULL, }, + { 0xffffffff71c71c71ULL, 0x000000001c71c71bULL, }, + { 0x0000000055555554ULL, 0x0000000055555554ULL, }, + { 0x000000001c71c71bULL, 0xffffffffc71c71c6ULL, }, /* 56 */ + { 0x000000001c71c71cULL, 0xffffffffc71c71c7ULL, }, + { 0xffffffffc71c71c6ULL, 0xffffffff71c71c71ULL, }, + { 0x0000000071c71c71ULL, 0x000000001c71c71cULL, }, + { 0xffffffffe93e93e8ULL, 0xffffffff93e93e93ULL, }, + { 0x000000004fa4fa4fULL, 0xfffffffffa4fa4faULL, }, + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0x000000008e38e38dULL, 0xffffffffe38e38e3ULL, }, + { 0xffffffffb0cd3c0cULL, 0x0000000049e2bb6aULL, }, /* 64 */ + { 0xffffffffd5feadd4ULL, 0x0000000060a65e5aULL, }, + { 0xffffffff423a724cULL, 0xfffffffff6923072ULL, }, + { 0xffffffffe69cc91aULL, 0xfffffffff4a9edfeULL, }, + { 0x00000000242055a3ULL, 0x0000000011736b26ULL, }, + { 0x000000004951c76bULL, 0x0000000028370e16ULL, }, + { 0xffffffffb58d8be3ULL, 0xffffffffbe22e02eULL, }, + { 0x0000000059efe2b1ULL, 0xffffffffbc3a9dbaULL, }, + { 0xffffffffd4bd03eaULL, 0x000000002654770bULL, }, /* 72 */ + { 0xfffffffff9ee75b2ULL, 0x000000003d1819fbULL, }, + { 0xffffffff662a3a2aULL, 0xffffffffd303ec13ULL, }, + { 0x000000000a8c90f8ULL, 0xffffffffd11ba99fULL, }, + { 0x0000000098b16b8dULL, 0xffffffff8c6d38e4ULL, }, + { 0x00000000bde2dd55ULL, 0xffffffffa330dbd4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_h.c new file mode 100644 index 0000000000..ddc2de3ff2 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction HADD_S.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "HADD_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffefffefffefffeULL, 0xfffefffefffefffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffa9ffa9ffa9ffa9ULL, 0xffa9ffa9ffa9ffa9ULL, }, + { 0x0054005400540054ULL, 0x0054005400540054ULL, }, + { 0xffcbffcbffcbffcbULL, 0xffcbffcbffcbffcbULL, }, + { 0x0032003200320032ULL, 0x0032003200320032ULL, }, + { 0xff8dffe20037ff8dULL, 0xffe20037ff8dffe2ULL, }, + { 0x0070001bffc60070ULL, 0x001bffc60070001bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffaaffaaffaaffaaULL, 0xffaaffaaffaaffaaULL, }, + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0xffccffccffccffccULL, 0xffccffccffccffccULL, }, + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0xff8effe30038ff8eULL, 0xffe30038ff8effe3ULL, }, + { 0x0071001cffc70071ULL, 0x001cffc70071001cULL, }, + { 0xffa9ffa9ffa9ffa9ULL, 0xffa9ffa9ffa9ffa9ULL, }, /* 16 */ + { 0xffaaffaaffaaffaaULL, 0xffaaffaaffaaffaaULL, }, + { 0xff54ff54ff54ff54ULL, 0xff54ff54ff54ff54ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff76ff76ff76ff76ULL, 0xff76ff76ff76ff76ULL, }, + { 0xffddffddffddffddULL, 0xffddffddffddffddULL, }, + { 0xff38ff8dffe2ff38ULL, 0xff8dffe2ff38ff8dULL, }, + { 0x001bffc6ff71001bULL, 0xffc6ff71001bffc6ULL, }, + { 0x0054005400540054ULL, 0x0054005400540054ULL, }, /* 24 */ + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0021002100210021ULL, 0x0021002100210021ULL, }, + { 0x0088008800880088ULL, 0x0088008800880088ULL, }, + { 0xffe30038008dffe3ULL, 0x0038008dffe30038ULL, }, + { 0x00c60071001c00c6ULL, 0x0071001c00c60071ULL, }, + { 0xffcbffcbffcbffcbULL, 0xffcbffcbffcbffcbULL, }, /* 32 */ + { 0xffccffccffccffccULL, 0xffccffccffccffccULL, }, + { 0xff76ff76ff76ff76ULL, 0xff76ff76ff76ff76ULL, }, + { 0x0021002100210021ULL, 0x0021002100210021ULL, }, + { 0xff98ff98ff98ff98ULL, 0xff98ff98ff98ff98ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff5affaf0004ff5aULL, 0xffaf0004ff5affafULL, }, + { 0x003dffe8ff93003dULL, 0xffe8ff93003dffe8ULL, }, + { 0x0032003200320032ULL, 0x0032003200320032ULL, }, /* 40 */ + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0xffddffddffddffddULL, 0xffddffddffddffddULL, }, + { 0x0088008800880088ULL, 0x0088008800880088ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0066006600660066ULL, 0x0066006600660066ULL, }, + { 0xffc10016006bffc1ULL, 0x0016006bffc10016ULL, }, + { 0x00a4004ffffa00a4ULL, 0x004ffffa00a4004fULL, }, + { 0xffe20037ff8dffe2ULL, 0x0037ff8dffe20037ULL, }, /* 48 */ + { 0xffe30038ff8effe3ULL, 0x0038ff8effe30038ULL, }, + { 0xff8dffe2ff38ff8dULL, 0xffe2ff38ff8dffe2ULL, }, + { 0x0038008dffe30038ULL, 0x008dffe30038008dULL, }, + { 0xffaf0004ff5affafULL, 0x0004ff5affaf0004ULL, }, + { 0x0016006bffc10016ULL, 0x006bffc10016006bULL, }, + { 0xff71001bffc6ff71ULL, 0x001bffc6ff71001bULL, }, + { 0x00540054ff550054ULL, 0x0054ff5500540054ULL, }, + { 0x001bffc60070001bULL, 0xffc60070001bffc6ULL, }, /* 56 */ + { 0x001cffc70071001cULL, 0xffc70071001cffc7ULL, }, + { 0xffc6ff71001bffc6ULL, 0xff71001bffc6ff71ULL, }, + { 0x0071001c00c60071ULL, 0x001c00c60071001cULL, }, + { 0xffe8ff93003dffe8ULL, 0xff93003dffe8ff93ULL, }, + { 0x004ffffa00a4004fULL, 0xfffa00a4004ffffaULL, }, + { 0xffaaffaa00a9ffaaULL, 0xffaa00a9ffaaffaaULL, }, + { 0x008dffe30038008dULL, 0xffe30038008dffe3ULL, }, + { 0xfff2ffb2008a0095ULL, 0x00b200690079ffbcULL, }, /* 64 */ + { 0xff460049ffbb005dULL, 0x00420025003dffacULL, }, + { 0xffe2ff90fff7ffd5ULL, 0x0023000a0029ffc4ULL, }, + { 0xffd70033005900a3ULL, 0x003cffe30040ff50ULL, }, + { 0x0065ffcc00af0007ULL, 0x007900190090005eULL, }, + { 0xffb90063ffe0ffcfULL, 0x0009ffd50054004eULL, }, + { 0x0055ffaa001cff47ULL, 0xffeaffba00400066ULL, }, + { 0x004a004d007e0015ULL, 0x0003ff930057fff2ULL, }, + { 0x0016ff7a001bffcbULL, 0x008e002400260031ULL, }, /* 72 */ + { 0xff6a0011ff4cff93ULL, 0x001effe0ffea0021ULL, }, + { 0x0006ff58ff88ff0bULL, 0xffffffc5ffd60039ULL, }, + { 0xfffbfffbffeaffd9ULL, 0x0018ff9effedffc5ULL, }, + { 0x00daffe200c00022ULL, 0xfff4ffe60024ffeeULL, }, + { 0x002e0079fff1ffeaULL, 0xff84ffa2ffe8ffdeULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_w.c new file mode 100644 index 0000000000..887cd1cd5c --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction HADD_S.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "HADD_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffefffffffeULL, 0xfffffffefffffffeULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffaaa9ffffaaa9ULL, 0xffffaaa9ffffaaa9ULL, }, + { 0x0000555400005554ULL, 0x0000555400005554ULL, }, + { 0xffffcccbffffcccbULL, 0xffffcccbffffcccbULL, }, + { 0x0000333200003332ULL, 0x0000333200003332ULL, }, + { 0x000038e2ffffe38dULL, 0xffff8e37000038e2ULL, }, + { 0xffffc71b00001c70ULL, 0x000071c6ffffc71bULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffaaaaffffaaaaULL, 0xffffaaaaffffaaaaULL, }, + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0xffffccccffffccccULL, 0xffffccccffffccccULL, }, + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x000038e3ffffe38eULL, 0xffff8e38000038e3ULL, }, + { 0xffffc71c00001c71ULL, 0x000071c7ffffc71cULL, }, + { 0xffffaaa9ffffaaa9ULL, 0xffffaaa9ffffaaa9ULL, }, /* 16 */ + { 0xffffaaaaffffaaaaULL, 0xffffaaaaffffaaaaULL, }, + { 0xffff5554ffff5554ULL, 0xffff5554ffff5554ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffff7776ffff7776ULL, 0xffff7776ffff7776ULL, }, + { 0xffffddddffffddddULL, 0xffffddddffffddddULL, }, + { 0xffffe38dffff8e38ULL, 0xffff38e2ffffe38dULL, }, + { 0xffff71c6ffffc71bULL, 0x00001c71ffff71c6ULL, }, + { 0x0000555400005554ULL, 0x0000555400005554ULL, }, /* 24 */ + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0000222100002221ULL, 0x0000222100002221ULL, }, + { 0x0000888800008888ULL, 0x0000888800008888ULL, }, + { 0x00008e38000038e3ULL, 0xffffe38d00008e38ULL, }, + { 0x00001c71000071c6ULL, 0x0000c71c00001c71ULL, }, + { 0xffffcccbffffcccbULL, 0xffffcccbffffcccbULL, }, /* 32 */ + { 0xffffccccffffccccULL, 0xffffccccffffccccULL, }, + { 0xffff7776ffff7776ULL, 0xffff7776ffff7776ULL, }, + { 0x0000222100002221ULL, 0x0000222100002221ULL, }, + { 0xffff9998ffff9998ULL, 0xffff9998ffff9998ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x000005afffffb05aULL, 0xffff5b04000005afULL, }, + { 0xffff93e8ffffe93dULL, 0x00003e93ffff93e8ULL, }, + { 0x0000333200003332ULL, 0x0000333200003332ULL, }, /* 40 */ + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0xffffddddffffddddULL, 0xffffddddffffddddULL, }, + { 0x0000888800008888ULL, 0x0000888800008888ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000666600006666ULL, 0x0000666600006666ULL, }, + { 0x00006c16000016c1ULL, 0xffffc16b00006c16ULL, }, + { 0xfffffa4f00004fa4ULL, 0x0000a4fafffffa4fULL, }, + { 0xffffe38dffff8e37ULL, 0x000038e2ffffe38dULL, }, /* 48 */ + { 0xffffe38effff8e38ULL, 0x000038e3ffffe38eULL, }, + { 0xffff8e38ffff38e2ULL, 0xffffe38dffff8e38ULL, }, + { 0x000038e3ffffe38dULL, 0x00008e38000038e3ULL, }, + { 0xffffb05affff5b04ULL, 0x000005afffffb05aULL, }, + { 0x000016c1ffffc16bULL, 0x00006c16000016c1ULL, }, + { 0x00001c71ffff71c6ULL, 0xffffc71b00001c71ULL, }, + { 0xffffaaaaffffaaa9ULL, 0x0000aaaaffffaaaaULL, }, + { 0x00001c70000071c6ULL, 0xffffc71b00001c70ULL, }, /* 56 */ + { 0x00001c71000071c7ULL, 0xffffc71c00001c71ULL, }, + { 0xffffc71b00001c71ULL, 0xffff71c6ffffc71bULL, }, + { 0x000071c60000c71cULL, 0x00001c71000071c6ULL, }, + { 0xffffe93d00003e93ULL, 0xffff93e8ffffe93dULL, }, + { 0x00004fa40000a4faULL, 0xfffffa4f00004fa4ULL, }, + { 0x0000555400005555ULL, 0xffff555400005554ULL, }, + { 0xffffe38d00008e38ULL, 0x000038e3ffffe38dULL, }, + { 0xffff6f3600007da2ULL, 0x000056c5ffffae87ULL, }, /* 64 */ + { 0xffff88cdffffef6aULL, 0x0000068100005177ULL, }, + { 0xffff3714ffffb3e2ULL, 0x000012660000238fULL, }, + { 0xffff9eb700000ab0ULL, 0xffffd43fffffe11bULL, }, + { 0xffffe28a0000a2d3ULL, 0x00001e55ffffc54bULL, }, + { 0xfffffc210000149bULL, 0xffffce110000683bULL, }, + { 0xffffaa68ffffd913ULL, 0xffffd9f600003a53ULL, }, + { 0x0000120b00002fe1ULL, 0xffff9bcffffff7dfULL, }, + { 0xffff932600000f0fULL, 0x00003336ffff5b37ULL, }, /* 72 */ + { 0xffffacbdffff80d7ULL, 0xffffe2f2fffffe27ULL, }, + { 0xffff5b04ffff454fULL, 0xffffeed7ffffd03fULL, }, + { 0xffffc2a7ffff9c1dULL, 0xffffb0b0ffff8dcbULL, }, + { 0x0000571b0000b371ULL, 0xffff994fffff594eULL, }, + { 0x000070b200002539ULL, 0xffff490bfffffc3eULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_d.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_d.c new file mode 100644 index 0000000000..f0710f15de --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction HADD_U.D + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "HADD_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x00000001fffffffeULL, 0x00000001fffffffeULL, }, /* 0 */ + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, + { 0x00000001aaaaaaa9ULL, 0x00000001aaaaaaa9ULL, }, + { 0x0000000155555554ULL, 0x0000000155555554ULL, }, + { 0x00000001cccccccbULL, 0x00000001cccccccbULL, }, + { 0x0000000133333332ULL, 0x0000000133333332ULL, }, + { 0x000000018e38e38dULL, 0x00000001e38e38e2ULL, }, + { 0x0000000171c71c70ULL, 0x000000011c71c71bULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0x00000000ccccccccULL, 0x00000000ccccccccULL, }, + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0x000000008e38e38eULL, 0x00000000e38e38e3ULL, }, + { 0x0000000071c71c71ULL, 0x000000001c71c71cULL, }, + { 0x00000001aaaaaaa9ULL, 0x00000001aaaaaaa9ULL, }, /* 16 */ + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000155555554ULL, 0x0000000155555554ULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000177777776ULL, 0x0000000177777776ULL, }, + { 0x00000000ddddddddULL, 0x00000000ddddddddULL, }, + { 0x0000000138e38e38ULL, 0x000000018e38e38dULL, }, + { 0x000000011c71c71bULL, 0x00000000c71c71c6ULL, }, + { 0x0000000155555554ULL, 0x0000000155555554ULL, }, /* 24 */ + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000122222221ULL, 0x0000000122222221ULL, }, + { 0x0000000088888888ULL, 0x0000000088888888ULL, }, + { 0x00000000e38e38e3ULL, 0x0000000138e38e38ULL, }, + { 0x00000000c71c71c6ULL, 0x0000000071c71c71ULL, }, + { 0x00000001cccccccbULL, 0x00000001cccccccbULL, }, /* 32 */ + { 0x00000000ccccccccULL, 0x00000000ccccccccULL, }, + { 0x0000000177777776ULL, 0x0000000177777776ULL, }, + { 0x0000000122222221ULL, 0x0000000122222221ULL, }, + { 0x0000000199999998ULL, 0x0000000199999998ULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, + { 0x000000015b05b05aULL, 0x00000001b05b05afULL, }, + { 0x000000013e93e93dULL, 0x00000000e93e93e8ULL, }, + { 0x0000000133333332ULL, 0x0000000133333332ULL, }, /* 40 */ + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0x00000000ddddddddULL, 0x00000000ddddddddULL, }, + { 0x0000000088888888ULL, 0x0000000088888888ULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000066666666ULL, 0x0000000066666666ULL, }, + { 0x00000000c16c16c1ULL, 0x0000000116c16c16ULL, }, + { 0x00000000a4fa4fa4ULL, 0x000000004fa4fa4fULL, }, + { 0x00000001e38e38e2ULL, 0x0000000138e38e37ULL, }, /* 48 */ + { 0x00000000e38e38e3ULL, 0x0000000038e38e38ULL, }, + { 0x000000018e38e38dULL, 0x00000000e38e38e2ULL, }, + { 0x0000000138e38e38ULL, 0x000000008e38e38dULL, }, + { 0x00000001b05b05afULL, 0x0000000105b05b04ULL, }, + { 0x0000000116c16c16ULL, 0x000000006c16c16bULL, }, + { 0x0000000171c71c71ULL, 0x000000011c71c71bULL, }, + { 0x0000000155555554ULL, 0x0000000055555554ULL, }, + { 0x000000011c71c71bULL, 0x00000001c71c71c6ULL, }, /* 56 */ + { 0x000000001c71c71cULL, 0x00000000c71c71c7ULL, }, + { 0x00000000c71c71c6ULL, 0x0000000171c71c71ULL, }, + { 0x0000000071c71c71ULL, 0x000000011c71c71cULL, }, + { 0x00000000e93e93e8ULL, 0x0000000193e93e93ULL, }, + { 0x000000004fa4fa4fULL, 0x00000000fa4fa4faULL, }, + { 0x00000000aaaaaaaaULL, 0x00000001aaaaaaaaULL, }, + { 0x000000008e38e38dULL, 0x00000000e38e38e3ULL, }, + { 0x00000000b0cd3c0cULL, 0x0000000149e2bb6aULL, }, /* 64 */ + { 0x00000000d5feadd4ULL, 0x0000000060a65e5aULL, }, + { 0x00000001423a724cULL, 0x00000000f6923072ULL, }, + { 0x00000000e69cc91aULL, 0x00000000f4a9edfeULL, }, + { 0x00000001242055a3ULL, 0x0000000111736b26ULL, }, + { 0x000000014951c76bULL, 0x0000000028370e16ULL, }, + { 0x00000001b58d8be3ULL, 0x00000000be22e02eULL, }, + { 0x0000000159efe2b1ULL, 0x00000000bc3a9dbaULL, }, + { 0x00000000d4bd03eaULL, 0x000000012654770bULL, }, /* 72 */ + { 0x00000000f9ee75b2ULL, 0x000000003d1819fbULL, }, + { 0x00000001662a3a2aULL, 0x00000000d303ec13ULL, }, + { 0x000000010a8c90f8ULL, 0x00000000d11ba99fULL, }, + { 0x0000000098b16b8dULL, 0x000000018c6d38e4ULL, }, + { 0x00000000bde2dd55ULL, 0x00000000a330dbd4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_h.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_h.c new file mode 100644 index 0000000000..fe55d3eaee --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction HADD_U.H + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "HADD_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x01fe01fe01fe01feULL, 0x01fe01fe01fe01feULL, }, /* 0 */ + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, + { 0x01a901a901a901a9ULL, 0x01a901a901a901a9ULL, }, + { 0x0154015401540154ULL, 0x0154015401540154ULL, }, + { 0x01cb01cb01cb01cbULL, 0x01cb01cb01cb01cbULL, }, + { 0x0132013201320132ULL, 0x0132013201320132ULL, }, + { 0x018d01e20137018dULL, 0x01e20137018d01e2ULL, }, + { 0x0170011b01c60170ULL, 0x011b01c60170011bULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0x00cc00cc00cc00ccULL, 0x00cc00cc00cc00ccULL, }, + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0x008e00e30038008eULL, 0x00e30038008e00e3ULL, }, + { 0x0071001c00c70071ULL, 0x001c00c70071001cULL, }, + { 0x01a901a901a901a9ULL, 0x01a901a901a901a9ULL, }, /* 16 */ + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0154015401540154ULL, 0x0154015401540154ULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, + { 0x0176017601760176ULL, 0x0176017601760176ULL, }, + { 0x00dd00dd00dd00ddULL, 0x00dd00dd00dd00ddULL, }, + { 0x0138018d00e20138ULL, 0x018d00e20138018dULL, }, + { 0x011b00c60171011bULL, 0x00c60171011b00c6ULL, }, + { 0x0154015401540154ULL, 0x0154015401540154ULL, }, /* 24 */ + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0121012101210121ULL, 0x0121012101210121ULL, }, + { 0x0088008800880088ULL, 0x0088008800880088ULL, }, + { 0x00e30138008d00e3ULL, 0x0138008d00e30138ULL, }, + { 0x00c60071011c00c6ULL, 0x0071011c00c60071ULL, }, + { 0x01cb01cb01cb01cbULL, 0x01cb01cb01cb01cbULL, }, /* 32 */ + { 0x00cc00cc00cc00ccULL, 0x00cc00cc00cc00ccULL, }, + { 0x0176017601760176ULL, 0x0176017601760176ULL, }, + { 0x0121012101210121ULL, 0x0121012101210121ULL, }, + { 0x0198019801980198ULL, 0x0198019801980198ULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, + { 0x015a01af0104015aULL, 0x01af0104015a01afULL, }, + { 0x013d00e80193013dULL, 0x00e80193013d00e8ULL, }, + { 0x0132013201320132ULL, 0x0132013201320132ULL, }, /* 40 */ + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0x00dd00dd00dd00ddULL, 0x00dd00dd00dd00ddULL, }, + { 0x0088008800880088ULL, 0x0088008800880088ULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, + { 0x0066006600660066ULL, 0x0066006600660066ULL, }, + { 0x00c10116006b00c1ULL, 0x0116006b00c10116ULL, }, + { 0x00a4004f00fa00a4ULL, 0x004f00fa00a4004fULL, }, + { 0x01e20137018d01e2ULL, 0x0137018d01e20137ULL, }, /* 48 */ + { 0x00e30038008e00e3ULL, 0x0038008e00e30038ULL, }, + { 0x018d00e20138018dULL, 0x00e20138018d00e2ULL, }, + { 0x0138008d00e30138ULL, 0x008d00e30138008dULL, }, + { 0x01af0104015a01afULL, 0x0104015a01af0104ULL, }, + { 0x0116006b00c10116ULL, 0x006b00c10116006bULL, }, + { 0x0171011b00c60171ULL, 0x011b00c60171011bULL, }, + { 0x0154005401550154ULL, 0x0054015501540054ULL, }, + { 0x011b01c60170011bULL, 0x01c60170011b01c6ULL, }, /* 56 */ + { 0x001c00c70071001cULL, 0x00c70071001c00c7ULL, }, + { 0x00c60171011b00c6ULL, 0x0171011b00c60171ULL, }, + { 0x0071011c00c60071ULL, 0x011c00c60071011cULL, }, + { 0x00e80193013d00e8ULL, 0x0193013d00e80193ULL, }, + { 0x004f00fa00a4004fULL, 0x00fa00a4004f00faULL, }, + { 0x00aa01aa00a900aaULL, 0x01aa00a900aa01aaULL, }, + { 0x008d00e30138008dULL, 0x00e30138008d00e3ULL, }, + { 0x00f201b2008a0095ULL, 0x00b20069017900bcULL, }, /* 64 */ + { 0x0146014900bb005dULL, 0x01420025013d01acULL, }, + { 0x00e2019000f700d5ULL, 0x0123010a012900c4ULL, }, + { 0x00d70133005900a3ULL, 0x013c00e301400150ULL, }, + { 0x016500cc00af0107ULL, 0x007901190090005eULL, }, + { 0x01b9006300e000cfULL, 0x010900d50054014eULL, }, + { 0x015500aa011c0147ULL, 0x00ea01ba00400066ULL, }, + { 0x014a004d007e0115ULL, 0x01030193005700f2ULL, }, + { 0x0116017a011b00cbULL, 0x008e012401260031ULL, }, /* 72 */ + { 0x016a0111014c0093ULL, 0x011e00e000ea0121ULL, }, + { 0x010601580188010bULL, 0x00ff01c500d60039ULL, }, + { 0x00fb00fb00ea00d9ULL, 0x0118019e00ed00c5ULL, }, + { 0x00da00e200c00122ULL, 0x00f400e6012400eeULL, }, + { 0x012e007900f100eaULL, 0x018400a200e801deULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_w.c b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_w.c new file mode 100644 index 0000000000..babe04d586 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-add/test_msa_hadd_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction HADD_U.W + * + * Copyright (C) 2019 RT-RK Computer Based Systems LLC + * Copyright (C) 2019 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "HADD_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0001fffe0001fffeULL, 0x0001fffe0001fffeULL, }, /* 0 */ + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, + { 0x0001aaa90001aaa9ULL, 0x0001aaa90001aaa9ULL, }, + { 0x0001555400015554ULL, 0x0001555400015554ULL, }, + { 0x0001cccb0001cccbULL, 0x0001cccb0001cccbULL, }, + { 0x0001333200013332ULL, 0x0001333200013332ULL, }, + { 0x000138e20001e38dULL, 0x00018e37000138e2ULL, }, + { 0x0001c71b00011c70ULL, 0x000171c60001c71bULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0x0000cccc0000ccccULL, 0x0000cccc0000ccccULL, }, + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x000038e30000e38eULL, 0x00008e38000038e3ULL, }, + { 0x0000c71c00001c71ULL, 0x000071c70000c71cULL, }, + { 0x0001aaa90001aaa9ULL, 0x0001aaa90001aaa9ULL, }, /* 16 */ + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0001555400015554ULL, 0x0001555400015554ULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, + { 0x0001777600017776ULL, 0x0001777600017776ULL, }, + { 0x0000dddd0000ddddULL, 0x0000dddd0000ddddULL, }, + { 0x0000e38d00018e38ULL, 0x000138e20000e38dULL, }, + { 0x000171c60000c71bULL, 0x00011c71000171c6ULL, }, + { 0x0001555400015554ULL, 0x0001555400015554ULL, }, /* 24 */ + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0001222100012221ULL, 0x0001222100012221ULL, }, + { 0x0000888800008888ULL, 0x0000888800008888ULL, }, + { 0x00008e38000138e3ULL, 0x0000e38d00008e38ULL, }, + { 0x00011c71000071c6ULL, 0x0000c71c00011c71ULL, }, + { 0x0001cccb0001cccbULL, 0x0001cccb0001cccbULL, }, /* 32 */ + { 0x0000cccc0000ccccULL, 0x0000cccc0000ccccULL, }, + { 0x0001777600017776ULL, 0x0001777600017776ULL, }, + { 0x0001222100012221ULL, 0x0001222100012221ULL, }, + { 0x0001999800019998ULL, 0x0001999800019998ULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, + { 0x000105af0001b05aULL, 0x00015b04000105afULL, }, + { 0x000193e80000e93dULL, 0x00013e93000193e8ULL, }, + { 0x0001333200013332ULL, 0x0001333200013332ULL, }, /* 40 */ + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x0000dddd0000ddddULL, 0x0000dddd0000ddddULL, }, + { 0x0000888800008888ULL, 0x0000888800008888ULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, + { 0x0000666600006666ULL, 0x0000666600006666ULL, }, + { 0x00006c16000116c1ULL, 0x0000c16b00006c16ULL, }, + { 0x0000fa4f00004fa4ULL, 0x0000a4fa0000fa4fULL, }, + { 0x0001e38d00018e37ULL, 0x000138e20001e38dULL, }, /* 48 */ + { 0x0000e38e00008e38ULL, 0x000038e30000e38eULL, }, + { 0x00018e38000138e2ULL, 0x0000e38d00018e38ULL, }, + { 0x000138e30000e38dULL, 0x00008e38000138e3ULL, }, + { 0x0001b05a00015b04ULL, 0x000105af0001b05aULL, }, + { 0x000116c10000c16bULL, 0x00006c16000116c1ULL, }, + { 0x00011c71000171c6ULL, 0x0000c71b00011c71ULL, }, + { 0x0001aaaa0000aaa9ULL, 0x0000aaaa0001aaaaULL, }, + { 0x00011c70000171c6ULL, 0x0001c71b00011c70ULL, }, /* 56 */ + { 0x00001c71000071c7ULL, 0x0000c71c00001c71ULL, }, + { 0x0000c71b00011c71ULL, 0x000171c60000c71bULL, }, + { 0x000071c60000c71cULL, 0x00011c71000071c6ULL, }, + { 0x0000e93d00013e93ULL, 0x000193e80000e93dULL, }, + { 0x00004fa40000a4faULL, 0x0000fa4f00004fa4ULL, }, + { 0x0000555400015555ULL, 0x0001555400005554ULL, }, + { 0x0000e38d00008e38ULL, 0x000138e30000e38dULL, }, + { 0x00016f3600007da2ULL, 0x000056c50001ae87ULL, }, /* 64 */ + { 0x000088cd0000ef6aULL, 0x0001068100015177ULL, }, + { 0x000137140000b3e2ULL, 0x000112660001238fULL, }, + { 0x00009eb700010ab0ULL, 0x0000d43f0001e11bULL, }, + { 0x0001e28a0000a2d3ULL, 0x00001e550000c54bULL, }, + { 0x0000fc210001149bULL, 0x0000ce110000683bULL, }, + { 0x0001aa680000d913ULL, 0x0000d9f600003a53ULL, }, + { 0x0001120b00012fe1ULL, 0x00009bcf0000f7dfULL, }, + { 0x0001932600010f0fULL, 0x0000333600015b37ULL, }, /* 72 */ + { 0x0000acbd000180d7ULL, 0x0000e2f20000fe27ULL, }, + { 0x00015b040001454fULL, 0x0000eed70000d03fULL, }, + { 0x0000c2a700019c1dULL, 0x0000b0b000018dcbULL, }, + { 0x0001571b0000b371ULL, 0x0000994f0001594eULL, }, + { 0x000070b200012539ULL, 0x0001490b0000fc3eULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_HADD_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_b.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_b.c new file mode 100644 index 0000000000..675fb90c72 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_S.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd4d4d4d4d4d4d4d4ULL, 0xd4d4d4d4d4d4d4d4ULL, }, + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, + { 0xe5e5e5e5e5e5e5e5ULL, 0xe5e5e5e5e5e5e5e5ULL, }, + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, + { 0xf1c61bf1c61bf1c6ULL, 0x1bf1c61bf1c61bf1ULL, }, + { 0x0d38e30d38e30d38ULL, 0xe30d38e30d38e30dULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, + { 0xf1c71cf1c71cf1c7ULL, 0x1cf1c71cf1c71cf1ULL, }, + { 0x0e38e30e38e30e38ULL, 0xe30e38e30e38e30eULL, }, + { 0xd4d4d4d4d4d4d4d4ULL, 0xd4d4d4d4d4d4d4d4ULL, }, /* 16 */ + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0xc69cf1c69cf1c69cULL, 0xf1c69cf1c69cf1c6ULL, }, + { 0xe30db8e30db8e30dULL, 0xb8e30db8e30db8e3ULL, }, + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, /* 24 */ + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1010101010101010ULL, 0x1010101010101010ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1cf1461cf1461cf1ULL, 0x461cf1461cf1461cULL, }, + { 0x38630e38630e3863ULL, 0x0e38630e38630e38ULL, }, + { 0xe5e5e5e5e5e5e5e5ULL, 0xe5e5e5e5e5e5e5e5ULL, }, /* 32 */ + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1010101010101010ULL, 0x1010101010101010ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd7ad02d7ad02d7adULL, 0x02d7ad02d7ad02d7ULL, }, + { 0xf41ec9f41ec9f41eULL, 0xc9f41ec9f41ec9f4ULL, }, + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, /* 40 */ + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0be0350be0350be0ULL, 0x350be0350be0350bULL, }, + { 0x2752fd2752fd2752ULL, 0xfd2752fd2752fd27ULL, }, + { 0xf1c61bf1c61bf1c6ULL, 0x1bf1c61bf1c61bf1ULL, }, /* 48 */ + { 0xf1c71cf1c71cf1c7ULL, 0x1cf1c71cf1c71cf1ULL, }, + { 0xc69cf1c69cf1c69cULL, 0xf1c69cf1c69cf1c6ULL, }, + { 0x1cf1461cf1461cf1ULL, 0x461cf1461cf1461cULL, }, + { 0xd7ad02d7ad02d7adULL, 0x02d7ad02d7ad02d7ULL, }, + { 0x0be0350be0350be0ULL, 0x350be0350be0350bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0d38e30d38e30d38ULL, 0xe30d38e30d38e30dULL, }, /* 56 */ + { 0x0e38e30e38e30e38ULL, 0xe30e38e30e38e30eULL, }, + { 0xe30db8e30db8e30dULL, 0xb8e30db8e30db8e3ULL, }, + { 0x38630e38630e3863ULL, 0x0e38630e38630e38ULL, }, + { 0xf41ec9f41ec9f41eULL, 0xc9f41ec9f41ec9f4ULL, }, + { 0x2752fd2752fd2752ULL, 0xfd2752fd2752fd27ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc114f3173afa0e24ULL, 0x2e2fe33c095d0104ULL, }, + { 0x9a62cabbf018f0e0ULL, 0x391fe82ed453ea10ULL, }, + { 0xfc5cfe0c43491b47ULL, 0xec2cc91bd35ec9d6ULL, }, + { 0xc114f3173afa0e24ULL, 0x2e2fe33c095d0104ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd30cd70603b1a9c4ULL, 0x1ce7c00ce0353b08ULL, }, + { 0x35060b5855e2d42bULL, 0xcff4a1f9df401aceULL, }, + { 0x9a62cabbf018f0e0ULL, 0x391fe82ed453ea10ULL, }, /* 72 */ + { 0xd30cd70603b1a9c4ULL, 0x1ce7c00ce0353b08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e54e2fb0b00b6e7ULL, 0xdae4a7ebaa3603daULL, }, + { 0xfc5cfe0c43491b47ULL, 0xec2cc91bd35ec9d6ULL, }, + { 0x35060b5855e2d42bULL, 0xcff4a1f9df401aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_d.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_d.c new file mode 100644 index 0000000000..e87d414b5f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_S.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd555555555555554ULL, 0xd555555555555554ULL, }, + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, + { 0xe666666666666665ULL, 0xe666666666666665ULL, }, + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e38e38e38eULL, }, + { 0xd555555555555554ULL, 0xd555555555555554ULL, }, /* 16 */ + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0xc71c71c71c71c71cULL, 0xf1c71c71c71c71c6ULL, }, + { 0xe38e38e38e38e38dULL, 0xb8e38e38e38e38e3ULL, }, + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1111111111111110ULL, 0x1111111111111110ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1c71c71c71c71c71ULL, 0x471c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0x0e38e38e38e38e38ULL, }, + { 0xe666666666666665ULL, 0xe666666666666665ULL, }, /* 32 */ + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1111111111111110ULL, 0x1111111111111110ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd82d82d82d82d82dULL, 0x02d82d82d82d82d7ULL, }, + { 0xf49f49f49f49f49eULL, 0xc9f49f49f49f49f4ULL, }, + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, /* 40 */ + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0b60b60b60b60b60ULL, 0x360b60b60b60b60bULL, }, + { 0x27d27d27d27d27d2ULL, 0xfd27d27d27d27d27ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x1c71c71c71c71c71ULL, }, /* 48 */ + { 0xf1c71c71c71c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0xc71c71c71c71c71cULL, 0xf1c71c71c71c71c6ULL, }, + { 0x1c71c71c71c71c71ULL, 0x471c71c71c71c71cULL, }, + { 0xd82d82d82d82d82dULL, 0x02d82d82d82d82d7ULL, }, + { 0x0b60b60b60b60b60ULL, 0x360b60b60b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, /* 56 */ + { 0x0e38e38e38e38e38ULL, 0xe38e38e38e38e38eULL, }, + { 0xe38e38e38e38e38dULL, 0xb8e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x0e38e38e38e38e38ULL, }, + { 0xf49f49f49f49f49eULL, 0xc9f49f49f49f49f4ULL, }, + { 0x27d27d27d27d27d2ULL, 0xfd27d27d27d27d27ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, + { 0xfc5cfe8cc34a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f95f411aceULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, /* 72 */ + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e54e27c0c00b6e7ULL, 0xdae527ec2a3703daULL, }, + { 0xfc5cfe8cc34a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f95f411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_h.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_h.c new file mode 100644 index 0000000000..c850543587 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_S.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd554d554d554d554ULL, 0xd554d554d554d554ULL, }, + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, + { 0xe665e665e665e665ULL, 0xe665e665e665e665ULL, }, + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, + { 0xf1c61c71c71bf1c6ULL, 0x1c71c71bf1c61c71ULL, }, + { 0x0e38e38d38e30e38ULL, 0xe38d38e30e38e38dULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, + { 0xf1c71c71c71cf1c7ULL, 0x1c71c71cf1c71c71ULL, }, + { 0x0e38e38e38e30e38ULL, 0xe38e38e30e38e38eULL, }, + { 0xd554d554d554d554ULL, 0xd554d554d554d554ULL, }, /* 16 */ + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0xc71cf1c69c71c71cULL, 0xf1c69c71c71cf1c6ULL, }, + { 0xe38db8e30e38e38dULL, 0xb8e30e38e38db8e3ULL, }, + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, /* 24 */ + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1110111011101110ULL, 0x1110111011101110ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1c71471cf1c61c71ULL, 0x471cf1c61c71471cULL, }, + { 0x38e30e38638e38e3ULL, 0x0e38638e38e30e38ULL, }, + { 0xe665e665e665e665ULL, 0xe665e665e665e665ULL, }, /* 32 */ + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1110111011101110ULL, 0x1110111011101110ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd82d02d7ad82d82dULL, 0x02d7ad82d82d02d7ULL, }, + { 0xf49ec9f41f49f49eULL, 0xc9f41f49f49ec9f4ULL, }, + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, /* 40 */ + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0b60360be0b50b60ULL, 0x360be0b50b60360bULL, }, + { 0x27d2fd27527d27d2ULL, 0xfd27527d27d2fd27ULL, }, + { 0xf1c61c71c71bf1c6ULL, 0x1c71c71bf1c61c71ULL, }, /* 48 */ + { 0xf1c71c71c71cf1c7ULL, 0x1c71c71cf1c71c71ULL, }, + { 0xc71cf1c69c71c71cULL, 0xf1c69c71c71cf1c6ULL, }, + { 0x1c71471cf1c61c71ULL, 0x471cf1c61c71471cULL, }, + { 0xd82d02d7ad82d82dULL, 0x02d7ad82d82d02d7ULL, }, + { 0x0b60360be0b50b60ULL, 0x360be0b50b60360bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0e38e38d38e30e38ULL, 0xe38d38e30e38e38dULL, }, /* 56 */ + { 0x0e38e38e38e30e38ULL, 0xe38e38e30e38e38eULL, }, + { 0xe38db8e30e38e38dULL, 0xb8e30e38e38db8e3ULL, }, + { 0x38e30e38638e38e3ULL, 0x0e38638e38e30e38ULL, }, + { 0xf49ec9f41f49f49eULL, 0xc9f41f49f49ec9f4ULL, }, + { 0x27d2fd27527d27d2ULL, 0xfd27527d27d2fd27ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc214f3973afa0e24ULL, 0x2f2fe33c09dd0184ULL, }, + { 0x9a62cabbf118f060ULL, 0x399fe92ed4d3ea90ULL, }, + { 0xfc5cfe8c43491bc7ULL, 0xecacca1bd3dec956ULL, }, + { 0xc214f3973afa0e24ULL, 0x2f2fe33c09dd0184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40cd78603b1a944ULL, 0x1d67c10ce0353c08ULL, }, + { 0x36060b5855e2d4abULL, 0xd074a1f9df401aceULL, }, + { 0x9a62cabbf118f060ULL, 0x399fe92ed4d3ea90ULL, }, /* 72 */ + { 0xd40cd78603b1a944ULL, 0x1d67c10ce0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e54e27b0c00b6e7ULL, 0xdae4a7ebaa3603daULL, }, + { 0xfc5cfe8c43491bc7ULL, 0xecacca1bd3dec956ULL, }, + { 0x36060b5855e2d4abULL, 0xd074a1f9df401aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_w.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_w.c new file mode 100644 index 0000000000..3220574ca0 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_S.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd5555554d5555554ULL, 0xd5555554d5555554ULL, }, + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, + { 0xe6666665e6666665ULL, 0xe6666665e6666665ULL, }, + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x1c71c71bf1c71c71ULL, }, + { 0x0e38e38d38e38e38ULL, 0xe38e38e30e38e38dULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71cf1c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e30e38e38eULL, }, + { 0xd5555554d5555554ULL, 0xd5555554d5555554ULL, }, /* 16 */ + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0xc71c71c69c71c71cULL, 0xf1c71c71c71c71c6ULL, }, + { 0xe38e38e30e38e38dULL, 0xb8e38e38e38e38e3ULL, }, + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, /* 24 */ + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1111111011111110ULL, 0x1111111011111110ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1c71c71cf1c71c71ULL, 0x471c71c61c71c71cULL, }, + { 0x38e38e38638e38e3ULL, 0x0e38e38e38e38e38ULL, }, + { 0xe6666665e6666665ULL, 0xe6666665e6666665ULL, }, /* 32 */ + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1111111011111110ULL, 0x1111111011111110ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xd82d82d7ad82d82dULL, 0x02d82d82d82d82d7ULL, }, + { 0xf49f49f41f49f49eULL, 0xc9f49f49f49f49f4ULL, }, + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, /* 40 */ + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0b60b60be0b60b60ULL, 0x360b60b50b60b60bULL, }, + { 0x27d27d27527d27d2ULL, 0xfd27d27d27d27d27ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x1c71c71bf1c71c71ULL, }, /* 48 */ + { 0xf1c71c71c71c71c7ULL, 0x1c71c71cf1c71c71ULL, }, + { 0xc71c71c69c71c71cULL, 0xf1c71c71c71c71c6ULL, }, + { 0x1c71c71cf1c71c71ULL, 0x471c71c61c71c71cULL, }, + { 0xd82d82d7ad82d82dULL, 0x02d82d82d82d82d7ULL, }, + { 0x0b60b60be0b60b60ULL, 0x360b60b50b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0e38e38d38e38e38ULL, 0xe38e38e30e38e38dULL, }, /* 56 */ + { 0x0e38e38e38e38e38ULL, 0xe38e38e30e38e38eULL, }, + { 0xe38e38e30e38e38dULL, 0xb8e38e38e38e38e3ULL, }, + { 0x38e38e38638e38e3ULL, 0x0e38e38e38e38e38ULL, }, + { 0xf49f49f41f49f49eULL, 0xc9f49f49f49f49f4ULL, }, + { 0x27d27d27527d27d2ULL, 0xfd27d27d27d27d27ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc21473973afb0e24ULL, 0x2f2f633c09dd8184ULL, }, + { 0x9a62cabbf118f060ULL, 0x399fe92ed4d36a90ULL, }, + { 0xfc5cfe8c434a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0xc21473973afb0e24ULL, 0x2f2f633c09dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578603b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f9df411aceULL, }, + { 0x9a62cabbf118f060ULL, 0x399fe92ed4d36a90ULL, }, /* 72 */ + { 0xd40c578603b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e54e27b0c00b6e7ULL, 0xdae527ebaa3703daULL, }, + { 0xfc5cfe8c434a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f9df411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_b.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_b.c new file mode 100644 index 0000000000..c3f96a6a5f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_U.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0xd4d4d4d4d4d4d4d4ULL, 0xd4d4d4d4d4d4d4d4ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe5e5e5e5e5e5e5e5ULL, 0xe5e5e5e5e5e5e5e5ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c69bf1c69bf1c6ULL, 0x9bf1c69bf1c69bf1ULL, }, + { 0x8db8e38db8e38db8ULL, 0xe38db8e38db8e38dULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, + { 0x71471c71471c7147ULL, 0x1c71471c71471c71ULL, }, + { 0x0e38630e38630e38ULL, 0x630e38630e38630eULL, }, + { 0xd4d4d4d4d4d4d4d4ULL, 0xd4d4d4d4d4d4d4d4ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6e6e6e6e6e6e6e6eULL, 0x6e6e6e6e6e6e6e6eULL, }, + { 0xc69c71c69c71c69cULL, 0x71c69c71c69c71c6ULL, }, + { 0x638db8638db8638dULL, 0xb8638db8638db863ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9090909090909090ULL, 0x9090909090909090ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c71469c71469c71ULL, 0x469c71469c71469cULL, }, + { 0x38638e38638e3863ULL, 0x8e38638e38638e38ULL, }, + { 0xe5e5e5e5e5e5e5e5ULL, 0xe5e5e5e5e5e5e5e5ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9090909090909090ULL, 0x9090909090909090ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0xd7ad82d7ad82d7adULL, 0x82d7ad82d7ad82d7ULL, }, + { 0x749ec9749ec9749eULL, 0xc9749ec9749ec974ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, + { 0x6e6e6e6e6e6e6e6eULL, 0x6e6e6e6e6e6e6e6eULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b60358b60358b60ULL, 0x358b60358b60358bULL, }, + { 0x27527d27527d2752ULL, 0x7d27527d27527d27ULL, }, + { 0xf1c69bf1c69bf1c6ULL, 0x9bf1c69bf1c69bf1ULL, }, /* 48 */ + { 0x71471c71471c7147ULL, 0x1c71471c71471c71ULL, }, + { 0xc69c71c69c71c69cULL, 0x71c69c71c69c71c6ULL, }, + { 0x9c71469c71469c71ULL, 0x469c71469c71469cULL, }, + { 0xd7ad82d7ad82d7adULL, 0x82d7ad82d7ad82d7ULL, }, + { 0x8b60358b60358b60ULL, 0x358b60358b60358bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x8db8e38db8e38db8ULL, 0xe38db8e38db8e38dULL, }, /* 56 */ + { 0x0e38630e38630e38ULL, 0x630e38630e38630eULL, }, + { 0x638db8638db8638dULL, 0xb8638db8638db863ULL, }, + { 0x38638e38638e3863ULL, 0x8e38638e38638e38ULL, }, + { 0x749ec9749ec9749eULL, 0xc9749ec9749ec974ULL, }, + { 0x27527d27527d2752ULL, 0x7d27527d27527d27ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc19473973a7a8e24ULL, 0x2eaf633c895d8184ULL, }, + { 0x9a62cabb70987060ULL, 0x399f68aed4536a10ULL, }, + { 0x7c5c7e8c43499b47ULL, 0x6cac499bd35ec956ULL, }, + { 0xc19473973a7a8e24ULL, 0x2eaf633c895d8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd38c578683b1a944ULL, 0x1ce7c08c60353b88ULL, }, + { 0xb5860b585562d42bULL, 0x4ff4a1795f409aceULL, }, + { 0x9a62cabb70987060ULL, 0x399f68aed4536a10ULL, }, /* 72 */ + { 0xd38c578683b1a944ULL, 0x1ce7c08c60353b88ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e54627b8b80b667ULL, 0x5ae4a7ebaa36835aULL, }, + { 0x7c5c7e8c43499b47ULL, 0x6cac499bd35ec956ULL, }, + { 0xb5860b585562d42bULL, 0x4ff4a1795f409aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_d.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_d.c new file mode 100644 index 0000000000..3a78629cd8 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_U.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0xd555555555555554ULL, 0xd555555555555554ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe666666666666665ULL, 0xe666666666666665ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x9c71c71c71c71c71ULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0x638e38e38e38e38eULL, }, + { 0xd555555555555554ULL, 0xd555555555555554ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6eeeeeeeeeeeeeeeULL, 0x6eeeeeeeeeeeeeeeULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c6ULL, }, + { 0x638e38e38e38e38dULL, 0xb8e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9111111111111110ULL, 0x9111111111111110ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c71c71c71c71c71ULL, 0x471c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e38ULL, }, + { 0xe666666666666665ULL, 0xe666666666666665ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9111111111111110ULL, 0x9111111111111110ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0xd82d82d82d82d82dULL, 0x82d82d82d82d82d7ULL, }, + { 0x749f49f49f49f49eULL, 0xc9f49f49f49f49f4ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, + { 0x6eeeeeeeeeeeeeeeULL, 0x6eeeeeeeeeeeeeeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b60b60b60b60b60ULL, 0x360b60b60b60b60bULL, }, + { 0x27d27d27d27d27d2ULL, 0x7d27d27d27d27d27ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x9c71c71c71c71c71ULL, }, /* 48 */ + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c6ULL, }, + { 0x9c71c71c71c71c71ULL, 0x471c71c71c71c71cULL, }, + { 0xd82d82d82d82d82dULL, 0x82d82d82d82d82d7ULL, }, + { 0x8b60b60b60b60b60ULL, 0x360b60b60b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38dULL, }, /* 56 */ + { 0x0e38e38e38e38e38ULL, 0x638e38e38e38e38eULL, }, + { 0x638e38e38e38e38dULL, 0xb8e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e38ULL, }, + { 0x749f49f49f49f49eULL, 0xc9f49f49f49f49f4ULL, }, + { 0x27d27d27d27d27d2ULL, 0x7d27d27d27d27d27ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, + { 0x7c5cfe8cc34a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, /* 72 */ + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e54e27c0c00b6e7ULL, 0x5ae527ec2a3703daULL, }, + { 0x7c5cfe8cc34a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_h.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_h.c new file mode 100644 index 0000000000..b7db518afb --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_U.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0xd554d554d554d554ULL, 0xd554d554d554d554ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe665e665e665e665ULL, 0xe665e665e665e665ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c69c71c71bf1c6ULL, 0x9c71c71bf1c69c71ULL, }, + { 0x8e38e38db8e38e38ULL, 0xe38db8e38e38e38dULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, + { 0x71c71c71471c71c7ULL, 0x1c71471c71c71c71ULL, }, + { 0x0e38638e38e30e38ULL, 0x638e38e30e38638eULL, }, + { 0xd554d554d554d554ULL, 0xd554d554d554d554ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6eee6eee6eee6eeeULL, 0x6eee6eee6eee6eeeULL, }, + { 0xc71c71c69c71c71cULL, 0x71c69c71c71c71c6ULL, }, + { 0x638db8e38e38638dULL, 0xb8e38e38638db8e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9110911091109110ULL, 0x9110911091109110ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c71471c71c69c71ULL, 0x471c71c69c71471cULL, }, + { 0x38e38e38638e38e3ULL, 0x8e38638e38e38e38ULL, }, + { 0xe665e665e665e665ULL, 0xe665e665e665e665ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9110911091109110ULL, 0x9110911091109110ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0xd82d82d7ad82d82dULL, 0x82d7ad82d82d82d7ULL, }, + { 0x749ec9f49f49749eULL, 0xc9f49f49749ec9f4ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, + { 0x6eee6eee6eee6eeeULL, 0x6eee6eee6eee6eeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b60360b60b58b60ULL, 0x360b60b58b60360bULL, }, + { 0x27d27d27527d27d2ULL, 0x7d27527d27d27d27ULL, }, + { 0xf1c69c71c71bf1c6ULL, 0x9c71c71bf1c69c71ULL, }, /* 48 */ + { 0x71c71c71471c71c7ULL, 0x1c71471c71c71c71ULL, }, + { 0xc71c71c69c71c71cULL, 0x71c69c71c71c71c6ULL, }, + { 0x9c71471c71c69c71ULL, 0x471c71c69c71471cULL, }, + { 0xd82d82d7ad82d82dULL, 0x82d7ad82d82d82d7ULL, }, + { 0x8b60360b60b58b60ULL, 0x360b60b58b60360bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x8e38e38db8e38e38ULL, 0xe38db8e38e38e38dULL, }, /* 56 */ + { 0x0e38638e38e30e38ULL, 0x638e38e30e38638eULL, }, + { 0x638db8e38e38638dULL, 0xb8e38e38638db8e3ULL, }, + { 0x38e38e38638e38e3ULL, 0x8e38638e38e38e38ULL, }, + { 0x749ec9f49f49749eULL, 0xc9f49f49749ec9f4ULL, }, + { 0x27d27d27527d27d2ULL, 0x7d27527d27d27d27ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc21473973afa8e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb71187060ULL, 0x399f692ed4d36a90ULL, }, + { 0x7c5c7e8c43499bc7ULL, 0x6cac4a1bd3dec956ULL, }, + { 0xc21473973afa8e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578683b1a944ULL, 0x1d67c10c60353c08ULL, }, + { 0xb6060b5855e2d4abULL, 0x5074a1f95f409aceULL, }, + { 0x9a62cabb71187060ULL, 0x399f692ed4d36a90ULL, }, /* 72 */ + { 0xd40c578683b1a944ULL, 0x1d67c10c60353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e54627b8c00b6e7ULL, 0x5ae4a7ebaa3683daULL, }, + { 0x7c5c7e8c43499bc7ULL, 0x6cac4a1bd3dec956ULL, }, + { 0xb6060b5855e2d4abULL, 0x5074a1f95f409aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_w.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_w.c new file mode 100644 index 0000000000..75e2409f1f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_ave_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVE_U.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVE_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0xd5555554d5555554ULL, 0xd5555554d5555554ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe6666665e6666665ULL, 0xe6666665e6666665ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x9c71c71bf1c71c71ULL, }, + { 0x8e38e38db8e38e38ULL, 0xe38e38e38e38e38dULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, + { 0x71c71c71471c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0x638e38e30e38e38eULL, }, + { 0xd5555554d5555554ULL, 0xd5555554d5555554ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6eeeeeee6eeeeeeeULL, 0x6eeeeeee6eeeeeeeULL, }, + { 0xc71c71c69c71c71cULL, 0x71c71c71c71c71c6ULL, }, + { 0x638e38e38e38e38dULL, 0xb8e38e38638e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9111111091111110ULL, 0x9111111091111110ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c71c71c71c71c71ULL, 0x471c71c69c71c71cULL, }, + { 0x38e38e38638e38e3ULL, 0x8e38e38e38e38e38ULL, }, + { 0xe6666665e6666665ULL, 0xe6666665e6666665ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9111111091111110ULL, 0x9111111091111110ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0xd82d82d7ad82d82dULL, 0x82d82d82d82d82d7ULL, }, + { 0x749f49f49f49f49eULL, 0xc9f49f49749f49f4ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, + { 0x6eeeeeee6eeeeeeeULL, 0x6eeeeeee6eeeeeeeULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b60b60b60b60b60ULL, 0x360b60b58b60b60bULL, }, + { 0x27d27d27527d27d2ULL, 0x7d27d27d27d27d27ULL, }, + { 0xf1c71c71c71c71c6ULL, 0x9c71c71bf1c71c71ULL, }, /* 48 */ + { 0x71c71c71471c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0xc71c71c69c71c71cULL, 0x71c71c71c71c71c6ULL, }, + { 0x9c71c71c71c71c71ULL, 0x471c71c69c71c71cULL, }, + { 0xd82d82d7ad82d82dULL, 0x82d82d82d82d82d7ULL, }, + { 0x8b60b60b60b60b60ULL, 0x360b60b58b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x8e38e38db8e38e38ULL, 0xe38e38e38e38e38dULL, }, /* 56 */ + { 0x0e38e38e38e38e38ULL, 0x638e38e30e38e38eULL, }, + { 0x638e38e38e38e38dULL, 0xb8e38e38638e38e3ULL, }, + { 0x38e38e38638e38e3ULL, 0x8e38e38e38e38e38ULL, }, + { 0x749f49f49f49f49eULL, 0xc9f49f49749f49f4ULL, }, + { 0x27d27d27527d27d2ULL, 0x7d27d27d27d27d27ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc21473973afb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92ed4d36a90ULL, }, + { 0x7c5cfe8c434a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xc21473973afb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578683b1a944ULL, 0x1d68410c60353c08ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92ed4d36a90ULL, }, /* 72 */ + { 0xd40c578683b1a944ULL, 0x1d68410c60353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e54e27b8c00b6e7ULL, 0x5ae527ebaa3703daULL, }, + { 0x7c5cfe8c434a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVE_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_b.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_b.c new file mode 100644 index 0000000000..59bba28d2e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_S.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, + { 0xf1c71cf1c71cf1c7ULL, 0x1cf1c71cf1c71cf1ULL, }, + { 0x0e38e30e38e30e38ULL, 0xe30e38e30e38e30eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, + { 0x2b2b2b2b2b2b2b2bULL, 0x2b2b2b2b2b2b2b2bULL, }, + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, + { 0x1a1a1a1a1a1a1a1aULL, 0x1a1a1a1a1a1a1a1aULL, }, + { 0xf2c71cf2c71cf2c7ULL, 0x1cf2c71cf2c71cf2ULL, }, + { 0x0e39e40e39e40e39ULL, 0xe40e39e40e39e40eULL, }, + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, /* 16 */ + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xefefefefefefefefULL, 0xefefefefefefefefULL, }, + { 0xc79cf1c79cf1c79cULL, 0xf1c79cf1c79cf1c7ULL, }, + { 0xe30eb9e30eb9e30eULL, 0xb9e30eb9e30eb9e3ULL, }, + { 0x2a2a2a2a2a2a2a2aULL, 0x2a2a2a2a2a2a2a2aULL, }, /* 24 */ + { 0x2b2b2b2b2b2b2b2bULL, 0x2b2b2b2b2b2b2b2bULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1cf2471cf2471cf2ULL, 0x471cf2471cf2471cULL, }, + { 0x39630e39630e3963ULL, 0x0e39630e39630e39ULL, }, + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, /* 32 */ + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd8ad02d8ad02d8adULL, 0x02d8ad02d8ad02d8ULL, }, + { 0xf41fcaf41fcaf41fULL, 0xcaf41fcaf41fcaf4ULL, }, + { 0x1919191919191919ULL, 0x1919191919191919ULL, }, /* 40 */ + { 0x1a1a1a1a1a1a1a1aULL, 0x1a1a1a1a1a1a1a1aULL, }, + { 0xefefefefefefefefULL, 0xefefefefefefefefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0be1360be1360be1ULL, 0x360be1360be1360bULL, }, + { 0x2852fd2852fd2852ULL, 0xfd2852fd2852fd28ULL, }, + { 0xf1c71cf1c71cf1c7ULL, 0x1cf1c71cf1c71cf1ULL, }, /* 48 */ + { 0xf2c71cf2c71cf2c7ULL, 0x1cf2c71cf2c71cf2ULL, }, + { 0xc79cf1c79cf1c79cULL, 0xf1c79cf1c79cf1c7ULL, }, + { 0x1cf2471cf2471cf2ULL, 0x471cf2471cf2471cULL, }, + { 0xd8ad02d8ad02d8adULL, 0x02d8ad02d8ad02d8ULL, }, + { 0x0be1360be1360be1ULL, 0x360be1360be1360bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0e38e30e38e30e38ULL, 0xe30e38e30e38e30eULL, }, /* 56 */ + { 0x0e39e40e39e40e39ULL, 0xe40e39e40e39e40eULL, }, + { 0xe30eb9e30eb9e30eULL, 0xb9e30eb9e30eb9e3ULL, }, + { 0x39630e39630e3963ULL, 0x0e39630e39630e39ULL, }, + { 0xf41fcaf41fcaf41fULL, 0xcaf41fcaf41fcaf4ULL, }, + { 0x2852fd2852fd2852ULL, 0xfd2852fd2852fd28ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc214f3183bfb0e24ULL, 0x2f2fe33c0a5d0104ULL, }, + { 0x9a62cabbf119f0e0ULL, 0x3920e92fd553eb10ULL, }, + { 0xfc5dfe0d434a1c47ULL, 0xec2cca1bd45fc9d6ULL, }, + { 0xc214f3183bfb0e24ULL, 0x2f2fe33c0a5d0104ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40cd70703b1a9c4ULL, 0x1de8c10de0353c08ULL, }, + { 0x36070b5856e2d52bULL, 0xd0f4a2f9df411aceULL, }, + { 0x9a62cabbf119f0e0ULL, 0x3920e92fd553eb10ULL, }, /* 72 */ + { 0xd40cd70703b1a9c4ULL, 0x1de8c10de0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e55e2fc0c00b7e7ULL, 0xdae5a7ecaa3704daULL, }, + { 0xfc5dfe0d434a1c47ULL, 0xec2cca1bd45fc9d6ULL, }, + { 0x36070b5856e2d52bULL, 0xd0f4a2f9df411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_d.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_d.c new file mode 100644 index 0000000000..435c09f9bf --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_S.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71c71c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, + { 0x2aaaaaaaaaaaaaabULL, 0x2aaaaaaaaaaaaaabULL, }, + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, + { 0x199999999999999aULL, 0x199999999999999aULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0x0e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, /* 16 */ + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeeeeeeeeeeeeeefULL, 0xeeeeeeeeeeeeeeefULL, }, + { 0xc71c71c71c71c71cULL, 0xf1c71c71c71c71c7ULL, }, + { 0xe38e38e38e38e38eULL, 0xb8e38e38e38e38e3ULL, }, + { 0x2aaaaaaaaaaaaaaaULL, 0x2aaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaaaaaaaaaaaaabULL, 0x2aaaaaaaaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1c71c71c71c71c72ULL, 0x471c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0x0e38e38e38e38e39ULL, }, + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, /* 32 */ + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd82d82d82d82d82dULL, 0x02d82d82d82d82d8ULL, }, + { 0xf49f49f49f49f49fULL, 0xc9f49f49f49f49f4ULL, }, + { 0x1999999999999999ULL, 0x1999999999999999ULL, }, /* 40 */ + { 0x199999999999999aULL, 0x199999999999999aULL, }, + { 0xeeeeeeeeeeeeeeefULL, 0xeeeeeeeeeeeeeeefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0b60b60b60b60b61ULL, 0x360b60b60b60b60bULL, }, + { 0x27d27d27d27d27d2ULL, 0xfd27d27d27d27d28ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71c71c71c71ULL, }, /* 48 */ + { 0xf1c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0xf1c71c71c71c71c7ULL, }, + { 0x1c71c71c71c71c72ULL, 0x471c71c71c71c71cULL, }, + { 0xd82d82d82d82d82dULL, 0x02d82d82d82d82d8ULL, }, + { 0x0b60b60b60b60b61ULL, 0x360b60b60b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e38e38e38eULL, }, /* 56 */ + { 0x0e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0xe38e38e38e38e38eULL, 0xb8e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x0e38e38e38e38e39ULL, }, + { 0xf49f49f49f49f49fULL, 0xc9f49f49f49f49f4ULL, }, + { 0x27d27d27d27d27d2ULL, 0xfd27d27d27d27d28ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, + { 0xfc5cfe8cc34a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f95f411aceULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, /* 72 */ + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e54e27c0c00b6e7ULL, 0xdae527ec2a3703daULL, }, + { 0xfc5cfe8cc34a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f95f411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_h.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_h.c new file mode 100644 index 0000000000..0902e508ec --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_S.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, + { 0xf1c71c71c71cf1c7ULL, 0x1c71c71cf1c71c71ULL, }, + { 0x0e38e38e38e30e38ULL, 0xe38e38e30e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, + { 0x2aab2aab2aab2aabULL, 0x2aab2aab2aab2aabULL, }, + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, + { 0x199a199a199a199aULL, 0x199a199a199a199aULL, }, + { 0xf1c71c72c71cf1c7ULL, 0x1c72c71cf1c71c72ULL, }, + { 0x0e39e38e38e40e39ULL, 0xe38e38e40e39e38eULL, }, + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, /* 16 */ + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeefeeefeeefeeefULL, 0xeeefeeefeeefeeefULL, }, + { 0xc71cf1c79c71c71cULL, 0xf1c79c71c71cf1c7ULL, }, + { 0xe38eb8e30e39e38eULL, 0xb8e30e39e38eb8e3ULL, }, + { 0x2aaa2aaa2aaa2aaaULL, 0x2aaa2aaa2aaa2aaaULL, }, /* 24 */ + { 0x2aab2aab2aab2aabULL, 0x2aab2aab2aab2aabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1c72471cf1c71c72ULL, 0x471cf1c71c72471cULL, }, + { 0x38e30e39638e38e3ULL, 0x0e39638e38e30e39ULL, }, + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, /* 32 */ + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd82d02d8ad82d82dULL, 0x02d8ad82d82d02d8ULL, }, + { 0xf49fc9f41f4af49fULL, 0xc9f41f4af49fc9f4ULL, }, + { 0x1999199919991999ULL, 0x1999199919991999ULL, }, /* 40 */ + { 0x199a199a199a199aULL, 0x199a199a199a199aULL, }, + { 0xeeefeeefeeefeeefULL, 0xeeefeeefeeefeeefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0b61360be0b60b61ULL, 0x360be0b60b61360bULL, }, + { 0x27d2fd28527d27d2ULL, 0xfd28527d27d2fd28ULL, }, + { 0xf1c71c71c71cf1c7ULL, 0x1c71c71cf1c71c71ULL, }, /* 48 */ + { 0xf1c71c72c71cf1c7ULL, 0x1c72c71cf1c71c72ULL, }, + { 0xc71cf1c79c71c71cULL, 0xf1c79c71c71cf1c7ULL, }, + { 0x1c72471cf1c71c72ULL, 0x471cf1c71c72471cULL, }, + { 0xd82d02d8ad82d82dULL, 0x02d8ad82d82d02d8ULL, }, + { 0x0b61360be0b60b61ULL, 0x360be0b60b61360bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0e38e38e38e30e38ULL, 0xe38e38e30e38e38eULL, }, /* 56 */ + { 0x0e39e38e38e40e39ULL, 0xe38e38e40e39e38eULL, }, + { 0xe38eb8e30e39e38eULL, 0xb8e30e39e38eb8e3ULL, }, + { 0x38e30e39638e38e3ULL, 0x0e39638e38e30e39ULL, }, + { 0xf49fc9f41f4af49fULL, 0xc9f41f4af49fc9f4ULL, }, + { 0x27d2fd28527d27d2ULL, 0xfd28527d27d2fd28ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc214f3983afb0e24ULL, 0x2f2fe33c09dd0184ULL, }, + { 0x9a62cabbf119f060ULL, 0x39a0e92fd4d3ea90ULL, }, + { 0xfc5dfe8d434a1bc7ULL, 0xecacca1bd3dfc956ULL, }, + { 0xc214f3983afb0e24ULL, 0x2f2fe33c09dd0184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40cd78703b1a944ULL, 0x1d68c10de0353c08ULL, }, + { 0x36070b5855e2d4abULL, 0xd074a1f9df411aceULL, }, + { 0x9a62cabbf119f060ULL, 0x39a0e92fd4d3ea90ULL, }, /* 72 */ + { 0xd40cd78703b1a944ULL, 0x1d68c10de0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e55e27c0c00b6e7ULL, 0xdae5a7ecaa3703daULL, }, + { 0xfc5dfe8d434a1bc7ULL, 0xecacca1bd3dfc956ULL, }, + { 0x36070b5855e2d4abULL, 0xd074a1f9df411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_w.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_w.c new file mode 100644 index 0000000000..31f4553916 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_S.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71cf1c71c71ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e30e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, + { 0x2aaaaaab2aaaaaabULL, 0x2aaaaaab2aaaaaabULL, }, + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, + { 0x1999999a1999999aULL, 0x1999999a1999999aULL, }, + { 0xf1c71c72c71c71c7ULL, 0x1c71c71cf1c71c72ULL, }, + { 0x0e38e38e38e38e39ULL, 0xe38e38e40e38e38eULL, }, + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, /* 16 */ + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xeeeeeeefeeeeeeefULL, 0xeeeeeeefeeeeeeefULL, }, + { 0xc71c71c79c71c71cULL, 0xf1c71c71c71c71c7ULL, }, + { 0xe38e38e30e38e38eULL, 0xb8e38e39e38e38e3ULL, }, + { 0x2aaaaaaa2aaaaaaaULL, 0x2aaaaaaa2aaaaaaaULL, }, /* 24 */ + { 0x2aaaaaab2aaaaaabULL, 0x2aaaaaab2aaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1c71c71cf1c71c72ULL, 0x471c71c71c71c71cULL, }, + { 0x38e38e39638e38e3ULL, 0x0e38e38e38e38e39ULL, }, + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, /* 32 */ + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd82d82d8ad82d82dULL, 0x02d82d82d82d82d8ULL, }, + { 0xf49f49f41f49f49fULL, 0xc9f49f4af49f49f4ULL, }, + { 0x1999999919999999ULL, 0x1999999919999999ULL, }, /* 40 */ + { 0x1999999a1999999aULL, 0x1999999a1999999aULL, }, + { 0xeeeeeeefeeeeeeefULL, 0xeeeeeeefeeeeeeefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0b60b60be0b60b61ULL, 0x360b60b60b60b60bULL, }, + { 0x27d27d28527d27d2ULL, 0xfd27d27d27d27d28ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x1c71c71cf1c71c71ULL, }, /* 48 */ + { 0xf1c71c72c71c71c7ULL, 0x1c71c71cf1c71c72ULL, }, + { 0xc71c71c79c71c71cULL, 0xf1c71c71c71c71c7ULL, }, + { 0x1c71c71cf1c71c72ULL, 0x471c71c71c71c71cULL, }, + { 0xd82d82d8ad82d82dULL, 0x02d82d82d82d82d8ULL, }, + { 0x0b60b60be0b60b61ULL, 0x360b60b60b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0e38e38e38e38e38ULL, 0xe38e38e30e38e38eULL, }, /* 56 */ + { 0x0e38e38e38e38e39ULL, 0xe38e38e40e38e38eULL, }, + { 0xe38e38e30e38e38eULL, 0xb8e38e39e38e38e3ULL, }, + { 0x38e38e39638e38e3ULL, 0x0e38e38e38e38e39ULL, }, + { 0xf49f49f41f49f49fULL, 0xc9f49f4af49f49f4ULL, }, + { 0x27d27d28527d27d2ULL, 0xfd27d27d27d27d28ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc21473983afb0e24ULL, 0x2f2f633c09dd8184ULL, }, + { 0x9a62cabbf118f060ULL, 0x399fe92fd4d36a90ULL, }, + { 0xfc5cfe8d434a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0xc21473983afb0e24ULL, 0x2f2f633c09dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578703b1a944ULL, 0x1d68410de0353c08ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f9df411aceULL, }, + { 0x9a62cabbf118f060ULL, 0x399fe92fd4d36a90ULL, }, /* 72 */ + { 0xd40c578703b1a944ULL, 0x1d68410de0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x0e54e27c0c00b6e7ULL, 0xdae527ecaa3703daULL, }, + { 0xfc5cfe8d434a1bc7ULL, 0xecac4a1bd3df4956ULL, }, + { 0x36068b5855e2d4abULL, 0xd074a1f9df411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_b.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_b.c new file mode 100644 index 0000000000..8aa7ec6a41 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_U.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c79cf1c79cf1c7ULL, 0x9cf1c79cf1c79cf1ULL, }, + { 0x8eb8e38eb8e38eb8ULL, 0xe38eb8e38eb8e38eULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2b2b2b2b2b2b2b2bULL, 0x2b2b2b2b2b2b2b2bULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x1a1a1a1a1a1a1a1aULL, 0x1a1a1a1a1a1a1a1aULL, }, + { 0x72471c72471c7247ULL, 0x1c72471c72471c72ULL, }, + { 0x0e39640e39640e39ULL, 0x640e39640e39640eULL, }, + { 0xd5d5d5d5d5d5d5d5ULL, 0xd5d5d5d5d5d5d5d5ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6f6f6f6f6f6f6f6fULL, 0x6f6f6f6f6f6f6f6fULL, }, + { 0xc79c71c79c71c79cULL, 0x71c79c71c79c71c7ULL, }, + { 0x638eb9638eb9638eULL, 0xb9638eb9638eb963ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2b2b2b2b2b2b2b2bULL, 0x2b2b2b2b2b2b2b2bULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9191919191919191ULL, 0x9191919191919191ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c72479c72479c72ULL, 0x479c72479c72479cULL, }, + { 0x39638e39638e3963ULL, 0x8e39638e39638e39ULL, }, + { 0xe6e6e6e6e6e6e6e6ULL, 0xe6e6e6e6e6e6e6e6ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9191919191919191ULL, 0x9191919191919191ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xd8ad82d8ad82d8adULL, 0x82d8ad82d8ad82d8ULL, }, + { 0x749fca749fca749fULL, 0xca749fca749fca74ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x1a1a1a1a1a1a1a1aULL, 0x1a1a1a1a1a1a1a1aULL, }, + { 0x6f6f6f6f6f6f6f6fULL, 0x6f6f6f6f6f6f6f6fULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b61368b61368b61ULL, 0x368b61368b61368bULL, }, + { 0x28527d28527d2852ULL, 0x7d28527d28527d28ULL, }, + { 0xf1c79cf1c79cf1c7ULL, 0x9cf1c79cf1c79cf1ULL, }, /* 48 */ + { 0x72471c72471c7247ULL, 0x1c72471c72471c72ULL, }, + { 0xc79c71c79c71c79cULL, 0x71c79c71c79c71c7ULL, }, + { 0x9c72479c72479c72ULL, 0x479c72479c72479cULL, }, + { 0xd8ad82d8ad82d8adULL, 0x82d8ad82d8ad82d8ULL, }, + { 0x8b61368b61368b61ULL, 0x368b61368b61368bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x8eb8e38eb8e38eb8ULL, 0xe38eb8e38eb8e38eULL, }, /* 56 */ + { 0x0e39640e39640e39ULL, 0x640e39640e39640eULL, }, + { 0x638eb9638eb9638eULL, 0xb9638eb9638eb963ULL, }, + { 0x39638e39638e3963ULL, 0x8e39638e39638e39ULL, }, + { 0x749fca749fca749fULL, 0xca749fca749fca74ULL, }, + { 0x28527d28527d2852ULL, 0x7d28527d28527d28ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc29473983b7b8e24ULL, 0x2faf633c8a5d8184ULL, }, + { 0x9a62cabb71997060ULL, 0x39a069afd5536b10ULL, }, + { 0x7c5d7e8d434a9c47ULL, 0x6cac4a9bd45fc956ULL, }, + { 0xc29473983b7b8e24ULL, 0x2faf633c8a5d8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd48c578783b1a944ULL, 0x1de8c18d60353c88ULL, }, + { 0xb6870b585662d52bULL, 0x50f4a2795f419aceULL, }, + { 0x9a62cabb71997060ULL, 0x39a069afd5536b10ULL, }, /* 72 */ + { 0xd48c578783b1a944ULL, 0x1de8c18d60353c88ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e55627c8c80b767ULL, 0x5ae5a7ecaa37845aULL, }, + { 0x7c5d7e8d434a9c47ULL, 0x6cac4a9bd45fc956ULL, }, + { 0xb6870b585662d52bULL, 0x50f4a2795f419aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_d.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_d.c new file mode 100644 index 0000000000..9b16e1250f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_U.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x9c71c71c71c71c71ULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38eULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2aaaaaaaaaaaaaabULL, 0x2aaaaaaaaaaaaaabULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x199999999999999aULL, 0x199999999999999aULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0x0e38e38e38e38e39ULL, 0x638e38e38e38e38eULL, }, + { 0xd555555555555555ULL, 0xd555555555555555ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6eeeeeeeeeeeeeefULL, 0x6eeeeeeeeeeeeeefULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x638e38e38e38e38eULL, 0xb8e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaaaaaaaaaaaaabULL, 0x2aaaaaaaaaaaaaabULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9111111111111111ULL, 0x9111111111111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c71c71c71c71c72ULL, 0x471c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0xe666666666666666ULL, 0xe666666666666666ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9111111111111111ULL, 0x9111111111111111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xd82d82d82d82d82dULL, 0x82d82d82d82d82d8ULL, }, + { 0x749f49f49f49f49fULL, 0xc9f49f49f49f49f4ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x199999999999999aULL, 0x199999999999999aULL, }, + { 0x6eeeeeeeeeeeeeefULL, 0x6eeeeeeeeeeeeeefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b60b60b60b60b61ULL, 0x360b60b60b60b60bULL, }, + { 0x27d27d27d27d27d2ULL, 0x7d27d27d27d27d28ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x9c71c71c71c71c71ULL, }, /* 48 */ + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x9c71c71c71c71c72ULL, 0x471c71c71c71c71cULL, }, + { 0xd82d82d82d82d82dULL, 0x82d82d82d82d82d8ULL, }, + { 0x8b60b60b60b60b61ULL, 0x360b60b60b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x8e38e38e38e38e38ULL, 0xe38e38e38e38e38eULL, }, /* 56 */ + { 0x0e38e38e38e38e39ULL, 0x638e38e38e38e38eULL, }, + { 0x638e38e38e38e38eULL, 0xb8e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0x749f49f49f49f49fULL, 0xc9f49f49f49f49f4ULL, }, + { 0x27d27d27d27d27d2ULL, 0x7d27d27d27d27d28ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, + { 0x7c5cfe8cc34a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xc2147397bafb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92f54d36a90ULL, }, /* 72 */ + { 0xd40c578703b1a944ULL, 0x1d68410ce0353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e54e27c0c00b6e7ULL, 0x5ae527ec2a3703daULL, }, + { 0x7c5cfe8cc34a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_h.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_h.c new file mode 100644 index 0000000000..191e4acbdc --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_U.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c79c71c71cf1c7ULL, 0x9c71c71cf1c79c71ULL, }, + { 0x8e38e38eb8e38e38ULL, 0xe38eb8e38e38e38eULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2aab2aab2aab2aabULL, 0x2aab2aab2aab2aabULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x199a199a199a199aULL, 0x199a199a199a199aULL, }, + { 0x71c71c72471c71c7ULL, 0x1c72471c71c71c72ULL, }, + { 0x0e39638e38e40e39ULL, 0x638e38e40e39638eULL, }, + { 0xd555d555d555d555ULL, 0xd555d555d555d555ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6eef6eef6eef6eefULL, 0x6eef6eef6eef6eefULL, }, + { 0xc71c71c79c71c71cULL, 0x71c79c71c71c71c7ULL, }, + { 0x638eb8e38e39638eULL, 0xb8e38e39638eb8e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aab2aab2aab2aabULL, 0x2aab2aab2aab2aabULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9111911191119111ULL, 0x9111911191119111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c72471c71c79c72ULL, 0x471c71c79c72471cULL, }, + { 0x38e38e39638e38e3ULL, 0x8e39638e38e38e39ULL, }, + { 0xe666e666e666e666ULL, 0xe666e666e666e666ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9111911191119111ULL, 0x9111911191119111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xd82d82d8ad82d82dULL, 0x82d8ad82d82d82d8ULL, }, + { 0x749fc9f49f4a749fULL, 0xc9f49f4a749fc9f4ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x199a199a199a199aULL, 0x199a199a199a199aULL, }, + { 0x6eef6eef6eef6eefULL, 0x6eef6eef6eef6eefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b61360b60b68b61ULL, 0x360b60b68b61360bULL, }, + { 0x27d27d28527d27d2ULL, 0x7d28527d27d27d28ULL, }, + { 0xf1c79c71c71cf1c7ULL, 0x9c71c71cf1c79c71ULL, }, /* 48 */ + { 0x71c71c72471c71c7ULL, 0x1c72471c71c71c72ULL, }, + { 0xc71c71c79c71c71cULL, 0x71c79c71c71c71c7ULL, }, + { 0x9c72471c71c79c72ULL, 0x471c71c79c72471cULL, }, + { 0xd82d82d8ad82d82dULL, 0x82d8ad82d82d82d8ULL, }, + { 0x8b61360b60b68b61ULL, 0x360b60b68b61360bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x8e38e38eb8e38e38ULL, 0xe38eb8e38e38e38eULL, }, /* 56 */ + { 0x0e39638e38e40e39ULL, 0x638e38e40e39638eULL, }, + { 0x638eb8e38e39638eULL, 0xb8e38e39638eb8e3ULL, }, + { 0x38e38e39638e38e3ULL, 0x8e39638e38e38e39ULL, }, + { 0x749fc9f49f4a749fULL, 0xc9f49f4a749fc9f4ULL, }, + { 0x27d27d28527d27d2ULL, 0x7d28527d27d27d28ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc21473983afb8e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb71197060ULL, 0x39a0692fd4d36a90ULL, }, + { 0x7c5d7e8d434a9bc7ULL, 0x6cac4a1bd3dfc956ULL, }, + { 0xc21473983afb8e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578783b1a944ULL, 0x1d68c10d60353c08ULL, }, + { 0xb6070b5855e2d4abULL, 0x5074a1f95f419aceULL, }, + { 0x9a62cabb71197060ULL, 0x39a0692fd4d36a90ULL, }, /* 72 */ + { 0xd40c578783b1a944ULL, 0x1d68c10d60353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e55627c8c00b6e7ULL, 0x5ae5a7ecaa3783daULL, }, + { 0x7c5d7e8d434a9bc7ULL, 0x6cac4a1bd3dfc956ULL, }, + { 0xb6070b5855e2d4abULL, 0x5074a1f95f419aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_w.c b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_w.c new file mode 100644 index 0000000000..e0d6b177c4 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-average/test_msa_aver_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction AVER_U.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AVER_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x9c71c71cf1c71c71ULL, }, + { 0x8e38e38eb8e38e38ULL, 0xe38e38e38e38e38eULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x2aaaaaab2aaaaaabULL, 0x2aaaaaab2aaaaaabULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x1999999a1999999aULL, 0x1999999a1999999aULL, }, + { 0x71c71c72471c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0x0e38e38e38e38e39ULL, 0x638e38e40e38e38eULL, }, + { 0xd5555555d5555555ULL, 0xd5555555d5555555ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x6eeeeeef6eeeeeefULL, 0x6eeeeeef6eeeeeefULL, }, + { 0xc71c71c79c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x638e38e38e38e38eULL, 0xb8e38e39638e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x2aaaaaab2aaaaaabULL, 0x2aaaaaab2aaaaaabULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x9111111191111111ULL, 0x9111111191111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x9c71c71c71c71c72ULL, 0x471c71c79c71c71cULL, }, + { 0x38e38e39638e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0xe6666666e6666666ULL, 0xe6666666e6666666ULL, }, /* 32 */ + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x9111111191111111ULL, 0x9111111191111111ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xd82d82d8ad82d82dULL, 0x82d82d82d82d82d8ULL, }, + { 0x749f49f49f49f49fULL, 0xc9f49f4a749f49f4ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, /* 40 */ + { 0x1999999a1999999aULL, 0x1999999a1999999aULL, }, + { 0x6eeeeeef6eeeeeefULL, 0x6eeeeeef6eeeeeefULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8b60b60b60b60b61ULL, 0x360b60b68b60b60bULL, }, + { 0x27d27d28527d27d2ULL, 0x7d27d27d27d27d28ULL, }, + { 0xf1c71c71c71c71c7ULL, 0x9c71c71cf1c71c71ULL, }, /* 48 */ + { 0x71c71c72471c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0xc71c71c79c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x9c71c71c71c71c72ULL, 0x471c71c79c71c71cULL, }, + { 0xd82d82d8ad82d82dULL, 0x82d82d82d82d82d8ULL, }, + { 0x8b60b60b60b60b61ULL, 0x360b60b68b60b60bULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x8e38e38eb8e38e38ULL, 0xe38e38e38e38e38eULL, }, /* 56 */ + { 0x0e38e38e38e38e39ULL, 0x638e38e40e38e38eULL, }, + { 0x638e38e38e38e38eULL, 0xb8e38e39638e38e3ULL, }, + { 0x38e38e39638e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0x749f49f49f49f49fULL, 0xc9f49f4a749f49f4ULL, }, + { 0x27d27d28527d27d2ULL, 0x7d27d27d27d27d28ULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xc21473983afb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92fd4d36a90ULL, }, + { 0x7c5cfe8d434a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xc21473983afb0e24ULL, 0x2f2f633c89dd8184ULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xd40c578783b1a944ULL, 0x1d68410d60353c08ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, + { 0x9a62cabb7118f060ULL, 0x399fe92fd4d36a90ULL, }, /* 72 */ + { 0xd40c578783b1a944ULL, 0x1d68410d60353c08ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8e54e27c8c00b6e7ULL, 0x5ae527ecaa3703daULL, }, + { 0x7c5cfe8d434a1bc7ULL, 0x6cac4a1bd3df4956ULL, }, + { 0xb6068b5855e2d4abULL, 0x5074a1f95f411aceULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AVER_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c new file mode 100644 index 0000000000..bb884ee752 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CEQ.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CEQ.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c new file mode 100644 index 0000000000..ef13f7d05d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CEQ.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CEQ.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c new file mode 100644 index 0000000000..1c43d40ee1 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CEQ.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CEQ.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c new file mode 100644 index 0000000000..1297d41f29 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_ceq_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CEQ.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CEQ.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CEQ_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c new file mode 100644 index 0000000000..afd5f635f0 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_S.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, /* 48 */ + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, /* 56 */ + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xff00ffffff000000ULL, 0x00000000ff00ff00ULL, }, + { 0xff00000000000000ULL, 0x000000000000ffffULL, }, + { 0xff00ffffff0000ffULL, 0x000000000000ff00ULL, }, + { 0x00ff000000ffffffULL, 0xffffffff00ff00ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00ff000000ff0000ULL, 0xff00ff00000000ffULL, }, + { 0xffffff00ffffffffULL, 0x0000000000ff0000ULL, }, + { 0x00ffffffffffffffULL, 0xffffffffffff0000ULL, }, /* 72 */ + { 0xff00ffffff00ffffULL, 0x00ff00ffffffff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff00ffffffffffffULL, 0x00ff000000ff0000ULL, }, + { 0x00ff000000ffff00ULL, 0xffffffffffff00ffULL, }, + { 0x000000ff00000000ULL, 0xffffffffff00ffffULL, }, + { 0x00ff000000000000ULL, 0xff00ffffff00ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c new file mode 100644 index 0000000000..04d58d103c --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_S.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c new file mode 100644 index 0000000000..ed1a1e21bd --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_S.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, /* 48 */ + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, /* 56 */ + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffffffffffff0000ULL, 0x00000000ffffffffULL, }, + { 0xffff000000000000ULL, 0x000000000000ffffULL, }, + { 0xffffffffffff0000ULL, 0x000000000000ffffULL, }, + { 0x000000000000ffffULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000ffffffffffffULL, 0xffffffffffff0000ULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x000000000000ffffULL, 0xffffffffffff0000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c new file mode 100644 index 0000000000..ea4dc1a30b --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_s_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_S.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c new file mode 100644 index 0000000000..6e4fdd83ec --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_U.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffff0000ffffff00ULL, 0x00ffff00000000ffULL, }, + { 0xff000000ffffffffULL, 0x00ffffff000000ffULL, }, + { 0x00000000ff00ffffULL, 0xffffffff0000ffffULL, }, + { 0x0000ffff000000ffULL, 0xff0000ffffffff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffffffff00ffULL, 0xff00ffffff000000ULL, }, + { 0x0000ff00ff00ffffULL, 0xff0000ffffffff00ULL, }, + { 0x00ffffff00000000ULL, 0xff000000ffffff00ULL, }, /* 72 */ + { 0xffff00000000ff00ULL, 0x00ff000000ffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x000000000000ff00ULL, 0xffff000000ffffffULL, }, + { 0xffffffff00ff0000ULL, 0x00000000ffff0000ULL, }, + { 0xffff00ff00ff0000ULL, 0x00ffff00000000ffULL, }, + { 0xffffffffffff00ffULL, 0x0000ffffff000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c new file mode 100644 index 0000000000..b2b2f557b8 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_U.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c new file mode 100644 index 0000000000..b2267752eb --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_U.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff0000ffffULL, }, + { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffffffff0000ULL, 0xffffffffffff0000ULL, }, + { 0x0000ffffffffffffULL, 0xffff0000ffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, }, /* 72 */ + { 0xffff00000000ffffULL, 0x000000000000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x000000000000ffffULL, 0xffff00000000ffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffff0000ULL, }, + { 0xffff000000000000ULL, 0x0000ffff00000000ULL, }, + { 0xffffffffffff0000ULL, 0x0000ffffffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c new file mode 100644 index 0000000000..00e930c0c7 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_cle_u_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLE_U.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLE_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLE_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c new file mode 100644 index 0000000000..4a52ebe491 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_S.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, /* 48 */ + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, /* 56 */ + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xff00ffff00ffff00ULL, 0xffff00ffff00ffffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xff00ffffff000000ULL, 0x00000000ff00ff00ULL, }, + { 0xff00000000000000ULL, 0x000000000000ffffULL, }, + { 0xff00ffffff0000ffULL, 0x000000000000ff00ULL, }, + { 0x00ff000000ffffffULL, 0xffffffff00ff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ff000000ff0000ULL, 0xff00ff00000000ffULL, }, + { 0xffffff00ffffffffULL, 0x0000000000ff0000ULL, }, + { 0x00ffffffffffffffULL, 0xffffffffffff0000ULL, }, /* 72 */ + { 0xff00ffffff00ffffULL, 0x00ff00ffffffff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff00ffffffffffffULL, 0x00ff000000ff0000ULL, }, + { 0x00ff000000ffff00ULL, 0xffffffffffff00ffULL, }, + { 0x000000ff00000000ULL, 0xffffffffff00ffffULL, }, + { 0x00ff000000000000ULL, 0xff00ffffff00ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c new file mode 100644 index 0000000000..cc945cdf8d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_S.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c new file mode 100644 index 0000000000..b228dfe7f5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_S.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, /* 48 */ + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, /* 56 */ + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffff0000ffffULL, 0xffff0000ffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffffffffffff0000ULL, 0x00000000ffffffffULL, }, + { 0xffff000000000000ULL, 0x000000000000ffffULL, }, + { 0xffffffffffff0000ULL, 0x000000000000ffffULL, }, + { 0x000000000000ffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000ffffffffffffULL, 0xffffffffffff0000ULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x000000000000ffffULL, 0xffffffffffff0000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c new file mode 100644 index 0000000000..6cb192a851 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_s_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_S.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffff00000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c new file mode 100644 index 0000000000..b6189d6b72 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_U.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x00ffff00ffff00ffULL, 0xff00ffff00ffff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffff0000ffffff00ULL, 0x00ffff00000000ffULL, }, + { 0xff000000ffffffffULL, 0x00ffffff000000ffULL, }, + { 0x00000000ff00ffffULL, 0xffffffff0000ffffULL, }, + { 0x0000ffff000000ffULL, 0xff0000ffffffff00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ffffffff00ffULL, 0xff00ffffff000000ULL, }, + { 0x0000ff00ff00ffffULL, 0xff0000ffffffff00ULL, }, + { 0x00ffffff00000000ULL, 0xff000000ffffff00ULL, }, /* 72 */ + { 0xffff00000000ff00ULL, 0x00ff000000ffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000ff00ULL, 0xffff000000ffffffULL, }, + { 0xffffffff00ff0000ULL, 0x00000000ffff0000ULL, }, + { 0xffff00ff00ff0000ULL, 0x00ffff00000000ffULL, }, + { 0xffffffffffff00ffULL, 0x0000ffffff000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c new file mode 100644 index 0000000000..4f547d8f0b --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_U.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c new file mode 100644 index 0000000000..9fcd81c653 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_U.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x0000ffffffff0000ULL, 0xffffffff0000ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffff00000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff0000ffffULL, }, + { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000ffffffff0000ULL, 0xffffffffffff0000ULL, }, + { 0x0000ffffffffffffULL, 0xffff0000ffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff0000ffffffffULL, }, /* 72 */ + { 0xffff00000000ffffULL, 0x000000000000ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000000000ffffULL, 0xffff00000000ffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffff0000ULL, }, + { 0xffff000000000000ULL, 0x0000ffff00000000ULL, }, + { 0xffffffffffff0000ULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c new file mode 100644 index 0000000000..8f648afa62 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-compare/test_msa_clt_u_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction CLT_U.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "CLT_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0xffffffffffffffffULL, }, + { 0x00000000ffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, /* 72 */ + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_CLT_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_b.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_b.c new file mode 100644 index 0000000000..38e3670422 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_S.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 0 */ + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 16 */ + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0200ff0200ff0200ULL, 0xff0200ff0200ff02ULL, }, + { 0xfd0001fd0001fd00ULL, 0x01fd0001fd0001fdULL, }, + { 0xababababababababULL, 0xababababababababULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xfe0001fe0001fe00ULL, 0x01fe0001fe0001feULL, }, + { 0x0300ff0300ff0300ULL, 0xff0300ff0300ff03ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 32 */ + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0100000100000100ULL, 0x0001000001000001ULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xff0000ff0000ff00ULL, 0x00ff0000ff0000ffULL, }, + { 0x0100000100000100ULL, 0x0001000001000001ULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, /* 48 */ + { 0x0101ff0101ff0101ULL, 0xff0101ff0101ff01ULL, }, + { 0x0001000001000001ULL, 0x0000010000010000ULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0x0002ff0002ff0002ULL, 0xff0002ff0002ff00ULL, }, + { 0x00fe0100fe0100feULL, 0x0100fe0100fe0100ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, /* 56 */ + { 0xffff01ffff01ffffULL, 0x01ffff01ffff01ffULL, }, + { 0x00ff0000ff0000ffULL, 0x0000ff0000ff0000ULL, }, + { 0x0001000001000001ULL, 0x0000010000010000ULL, }, + { 0x00fe0100fe0100feULL, 0x0100fe0100fe0100ULL, }, + { 0x0002ff0002ff0002ULL, 0xff0002ff0002ff00ULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 64 */ + { 0x18ff01000000ff08ULL, 0x04f50003000100fdULL, }, + { 0x0101000000fe0000ULL, 0x01fe00a20002fe00ULL, }, + { 0xff01ff000002fe00ULL, 0x00fa00fe00010200ULL, }, + { 0x000000ff01ff0000ULL, 0x0000fa00f600ff00ULL, }, + { 0x0101ff0101010101ULL, 0x0101010101010101ULL, }, + { 0x000000ffff020000ULL, 0x000001e600010200ULL, }, + { 0x0000000100fe0100ULL, 0x000000000000fe00ULL, }, + { 0x00000301ff00fffeULL, 0x0000fb002a000001ULL, }, /* 72 */ + { 0x10ff0100000002f0ULL, 0x02040000fc0000fbULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0001fdff00ff03ffULL, 0x000200000000ff00ULL, }, + { 0x000000ff02000001ULL, 0xff00f6002b0000f8ULL, }, + { 0xeaffff0001000009ULL, 0xfa0101fffc010018ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_d.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_d.c new file mode 100644 index 0000000000..d92b6953e8 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_S.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 0 */ + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 16 */ + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000003ULL, 0xffffffffffffffffULL, }, + { 0xfffffffffffffffdULL, 0x0000000000000001ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xfffffffffffffffeULL, 0x0000000000000001ULL, }, + { 0x0000000000000003ULL, 0xffffffffffffffffULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 32 */ + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, /* 48 */ + { 0x0000000000000001ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, /* 56 */ + { 0xffffffffffffffffULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 64 */ + { 0x000000000000001cULL, 0x0000000000000003ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000013ULL, 0x0000000000000002ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffe6ULL, 0xfffffffffffffffaULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_h.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_h.c new file mode 100644 index 0000000000..f191b985b1 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_S.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 0 */ + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 16 */ + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0003ffff00000003ULL, 0xffff00000003ffffULL, }, + { 0xfffd00010000fffdULL, 0x00010000fffd0001ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xfffe00010000fffeULL, 0x00010000fffe0001ULL, }, + { 0x0003ffff00000003ULL, 0xffff00000003ffffULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 32 */ + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000000000001ULL, 0x0000000000010000ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffff00000000ffffULL, 0x00000000ffff0000ULL, }, + { 0x0001000000000001ULL, 0x0000000000010000ULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, /* 48 */ + { 0x0001ffff00010001ULL, 0xffff00010001ffffULL, }, + { 0x0000000000010000ULL, 0x0000000100000000ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0x0000ffff00020000ULL, 0xffff00020000ffffULL, }, + { 0x00000001fffe0000ULL, 0x0001fffe00000001ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, /* 56 */ + { 0xffff0001ffffffffULL, 0x0001ffffffff0001ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0x0000000000010000ULL, 0x0000000100000000ULL, }, + { 0x00000001fffe0000ULL, 0x0001fffe00000001ULL, }, + { 0x0000ffff00020000ULL, 0xffff00020000ffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 64 */ + { 0x001cffbf0000ffffULL, 0x0003000000000000ULL, }, + { 0x0001000000000000ULL, 0x000100000000fffeULL, }, + { 0xffffffff0000fffeULL, 0x0000000000000002ULL, }, + { 0x0000000000010000ULL, 0x0000fffafff3ffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x00000000ffff0000ULL, 0x0000000100000002ULL, }, + { 0x0000000000000001ULL, 0x000000000000fffeULL, }, + { 0x00000003ffffffffULL, 0x0000fffb00370000ULL, }, /* 72 */ + { 0x0013ff2e00000002ULL, 0x00020000fffd0000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000fffd00000003ULL, 0x000000000000ffffULL, }, + { 0x0000000000020000ULL, 0xfffffff600390000ULL, }, + { 0xffe6003900010000ULL, 0xfffa0001fffc0000ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_w.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_w.c new file mode 100644 index 0000000000..0baaff10f9 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_S.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 0 */ + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 16 */ + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000200000000ULL, 0xffffffff00000002ULL, }, + { 0xfffffffd00000000ULL, 0x00000001fffffffdULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xfffffffe00000000ULL, 0x00000001fffffffeULL, }, + { 0x0000000300000000ULL, 0xffffffff00000003ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 32 */ + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000000ULL, 0x0000000000000001ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, + { 0x0000000100000000ULL, 0x0000000000000001ULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, /* 48 */ + { 0x0000000100000001ULL, 0xffffffff00000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0xffffffff00000000ULL, }, + { 0x00000000fffffffeULL, 0x0000000100000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, /* 56 */ + { 0xffffffffffffffffULL, 0x00000001ffffffffULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x00000000fffffffeULL, 0x0000000100000000ULL, }, + { 0x0000000000000002ULL, 0xffffffff00000000ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 64 */ + { 0x0000001c00000000ULL, 0x0000000300000000ULL, }, + { 0x0000000100000000ULL, 0x0000000100000000ULL, }, + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x00000000fffffff2ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000037ULL, }, /* 72 */ + { 0x0000001300000000ULL, 0x00000002fffffffdULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0xffffffff00000039ULL, }, + { 0xffffffe600000001ULL, 0xfffffffafffffffcULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_b.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_b.c new file mode 100644 index 0000000000..770544a2de --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_U.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0303030303030303ULL, 0x0303030303030303ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0505050505050505ULL, 0x0505050505050505ULL, }, + { 0x0101040101040101ULL, 0x0401010401010401ULL, }, + { 0x0902010902010902ULL, 0x0109020109020109ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0202020202020202ULL, 0x0202020202020202ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0303030303030303ULL, 0x0303030303030303ULL, }, + { 0x0001030001030001ULL, 0x0300010300010300ULL, }, + { 0x0601000601000601ULL, 0x0006010006010006ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000010000010000ULL, 0x0100000100000100ULL, }, + { 0x0300000300000300ULL, 0x0003000003000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0202020202020202ULL, 0x0202020202020202ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0404040404040404ULL, 0x0404040404040404ULL, }, + { 0x0001030001030001ULL, 0x0300010300010300ULL, }, + { 0x0701010701010701ULL, 0x0107010107010107ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0100000100000100ULL, 0x0001000001000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0100000100000100ULL, 0x0001000001000001ULL, }, + { 0x0201000201000201ULL, 0x0002010002010002ULL, }, + { 0x0100000100000100ULL, 0x0001000001000001ULL, }, + { 0x0402010402010402ULL, 0x0104020104020104ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0801000801000801ULL, 0x0008010008010008ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000010000010000ULL, 0x0100000100000100ULL, }, + { 0x0001020001020001ULL, 0x0200010200010200ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0002030002030002ULL, 0x0300020300020300ULL, }, + { 0x0000030000030000ULL, 0x0300000300000300ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 64 */ + { 0x0000ff0200000008ULL, 0x040000030c010200ULL, }, + { 0x0001010100000000ULL, 0x0100000001020400ULL, }, + { 0x01010a0200020000ULL, 0x0000000001010000ULL, }, + { 0x0101000001010200ULL, 0x0002110000000015ULL, }, + { 0x0101ff0101010101ULL, 0x0101010101010101ULL, }, + { 0x0102000000000100ULL, 0x000100000001020cULL, }, + { 0x0202000100030000ULL, 0x0001010000000001ULL, }, + { 0x0100000004020102ULL, 0x0002120200000001ULL, }, /* 72 */ + { 0x0000ff0102010010ULL, 0x0200010908000000ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, + { 0x0101070201040001ULL, 0x0000010101000000ULL, }, + { 0x0000000002000201ULL, 0x01020c020000010dULL, }, + { 0x0000ff0001000109ULL, 0x0700000808010200ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_d.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_d.c new file mode 100644 index 0000000000..9653e7db5c --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_U.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000003ULL, 0x0000000000000003ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000005ULL, 0x0000000000000005ULL, }, + { 0x0000000000000001ULL, 0x0000000000000004ULL, }, + { 0x0000000000000009ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000003ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000006ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000003ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000004ULL, 0x0000000000000004ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000007ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000004ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000008ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000002ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000007ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_h.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_h.c new file mode 100644 index 0000000000..3dcd30bee9 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_U.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0003000300030003ULL, 0x0003000300030003ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0005000500050005ULL, 0x0005000500050005ULL, }, + { 0x0001000400010001ULL, 0x0004000100010004ULL, }, + { 0x0009000100020009ULL, 0x0001000200090001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0003000300030003ULL, 0x0003000300030003ULL, }, + { 0x0000000300010000ULL, 0x0003000100000003ULL, }, + { 0x0006000000010006ULL, 0x0000000100060000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000100000000ULL, 0x0001000000000001ULL, }, + { 0x0003000000000003ULL, 0x0000000000030000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0004000400040004ULL, 0x0004000400040004ULL, }, + { 0x0000000300010000ULL, 0x0003000100000003ULL, }, + { 0x0007000100010007ULL, 0x0001000100070001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000000000001ULL, 0x0000000000010000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0001000000000001ULL, 0x0000000000010000ULL, }, + { 0x0002000000010002ULL, 0x0000000100020000ULL, }, + { 0x0001000000000001ULL, 0x0000000000010000ULL, }, + { 0x0004000100020004ULL, 0x0001000200040001ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0008000000010008ULL, 0x0000000100080000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000000ULL, 0x0001000000000001ULL, }, + { 0x0000000200010000ULL, 0x0002000100000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000300020000ULL, 0x0003000200000003ULL, }, + { 0x0000000300000000ULL, 0x0003000000000003ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 64 */ + { 0x0000025400000000ULL, 0x00030000000b0002ULL, }, + { 0x0000000100000000ULL, 0x0001000000010004ULL, }, + { 0x0001000a00000000ULL, 0x0000000000010000ULL, }, + { 0x0001000000010002ULL, 0x0000001000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0001000000000001ULL, 0x0000000000000002ULL, }, + { 0x0002000000000000ULL, 0x0000000100000000ULL, }, + { 0x0001000000040001ULL, 0x0000001100000000ULL, }, /* 72 */ + { 0x000001c300020000ULL, 0x0002000100080000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0x0001000700010000ULL, 0x0000000100010000ULL, }, + { 0x0000000000020002ULL, 0x0001000c00000001ULL, }, + { 0x0000003900010001ULL, 0x0007000000070002ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_w.c b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_w.c new file mode 100644 index 0000000000..fd395ef5e9 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-divide/test_msa_div_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DIV_U.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DIV_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000300000003ULL, 0x0000000300000003ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000500000005ULL, 0x0000000500000005ULL, }, + { 0x0000000100000001ULL, 0x0000000400000001ULL, }, + { 0x0000000900000002ULL, 0x0000000100000009ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000300000003ULL, 0x0000000300000003ULL, }, + { 0x0000000000000001ULL, 0x0000000300000000ULL, }, + { 0x0000000600000001ULL, 0x0000000000000006ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000100000000ULL, }, + { 0x0000000300000000ULL, 0x0000000000000003ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000400000004ULL, 0x0000000400000004ULL, }, + { 0x0000000000000001ULL, 0x0000000300000000ULL, }, + { 0x0000000700000001ULL, 0x0000000100000007ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000000ULL, 0x0000000000000001ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000100000000ULL, 0x0000000000000001ULL, }, + { 0x0000000200000001ULL, 0x0000000000000002ULL, }, + { 0x0000000100000000ULL, 0x0000000000000001ULL, }, + { 0x0000000400000002ULL, 0x0000000100000004ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000800000001ULL, 0x0000000000000008ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000100000000ULL, }, + { 0x0000000000000001ULL, 0x0000000200000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000002ULL, 0x0000000300000000ULL, }, + { 0x0000000000000000ULL, 0x0000000300000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x000000030000000bULL, }, + { 0x0000000000000000ULL, 0x0000000100000001ULL, }, + { 0x0000000100000000ULL, 0x0000000000000001ULL, }, + { 0x0000000100000001ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000100000000ULL, 0x0000000000000000ULL, }, + { 0x0000000200000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000004ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000002ULL, 0x0000000200000008ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0x0000000100000001ULL, 0x0000000000000001ULL, }, + { 0x0000000000000002ULL, 0x0000000100000000ULL, }, + { 0x0000000000000001ULL, 0x0000000700000007ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DIV_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_d.c b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_d.c new file mode 100644 index 0000000000..af8d609bea --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DOTP_S.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DOTP_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000002ULL, 0x0000000000000002ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaacULL, 0x00000000aaaaaaacULL, }, + { 0xffffffff55555556ULL, 0xffffffff55555556ULL, }, + { 0x0000000066666668ULL, 0x0000000066666668ULL, }, + { 0xffffffff9999999aULL, 0xffffffff9999999aULL, }, + { 0x000000008e38e38fULL, 0xffffffffe38e38e5ULL, }, + { 0xffffffff71c71c73ULL, 0x000000001c71c71dULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaacULL, 0x00000000aaaaaaacULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e38e39c71c71c8ULL, 0x38e38e39c71c71c8ULL, }, + { 0xc71c71c6e38e38e4ULL, 0xc71c71c6e38e38e4ULL, }, + { 0x22222222eeeeeef0ULL, 0x22222222eeeeeef0ULL, }, + { 0xddddddddbbbbbbbcULL, 0xddddddddbbbbbbbcULL, }, + { 0x2f684bdab425ed0aULL, 0xf684bda197b425eeULL, }, + { 0xd097b425f684bda2ULL, 0x097b425f12f684beULL, }, + { 0xffffffff55555556ULL, 0xffffffff55555556ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c6e38e38e4ULL, 0xc71c71c6e38e38e4ULL, }, + { 0x38e38e3871c71c72ULL, 0x38e38e3871c71c72ULL, }, + { 0xdddddddd77777778ULL, 0xdddddddd77777778ULL, }, + { 0x22222221dddddddeULL, 0x22222221dddddddeULL, }, + { 0xd097b425da12f685ULL, 0x097b425e4bda12f7ULL, }, + { 0x2f684bd97b425ed1ULL, 0xf684bda1097b425fULL, }, + { 0x0000000066666668ULL, 0x0000000066666668ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x22222222eeeeeef0ULL, 0x22222222eeeeeef0ULL, }, + { 0xdddddddd77777778ULL, 0xdddddddd77777778ULL, }, + { 0x147ae14851eb8520ULL, 0x147ae14851eb8520ULL, }, + { 0xeb851eb8147ae148ULL, 0xeb851eb8147ae148ULL, }, + { 0x1c71c71d0b60b60cULL, 0xfa4fa4fa82d82d84ULL, }, + { 0xe38e38e35b05b05cULL, 0x05b05b05e38e38e4ULL, }, + { 0xffffffff9999999aULL, 0xffffffff9999999aULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xddddddddbbbbbbbcULL, 0xddddddddbbbbbbbcULL, }, + { 0x22222221dddddddeULL, 0x22222221dddddddeULL, }, + { 0xeb851eb8147ae148ULL, 0xeb851eb8147ae148ULL, }, + { 0x147ae147851eb852ULL, 0x147ae147851eb852ULL, }, + { 0xe38e38e382d82d83ULL, 0x05b05b0560b60b61ULL, }, + { 0x1c71c71c16c16c17ULL, 0xfa4fa4fa38e38e39ULL, }, + { 0x000000008e38e38fULL, 0xffffffffe38e38e5ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2f684bdab425ed0aULL, 0xf684bda197b425eeULL, }, + { 0xd097b425da12f685ULL, 0x097b425e4bda12f7ULL, }, + { 0x1c71c71d0b60b60cULL, 0xfa4fa4fa82d82d84ULL, }, + { 0xe38e38e382d82d83ULL, 0x05b05b0560b60b61ULL, }, + { 0x35ba78199add3c0dULL, 0x0fcd6e9dc0ca4589ULL, }, + { 0xca4587e6f35ba782ULL, 0xf032916222c3f35cULL, }, + { 0xffffffff71c71c73ULL, 0x000000001c71c71dULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd097b425f684bda2ULL, 0x097b425f12f684beULL, }, + { 0x2f684bd97b425ed1ULL, 0xf684bda1097b425fULL, }, + { 0xe38e38e35b05b05cULL, 0x05b05b05e38e38e4ULL, }, + { 0x1c71c71c16c16c17ULL, 0xfa4fa4fa38e38e39ULL, }, + { 0xca4587e6f35ba782ULL, 0xf032916222c3f35cULL, }, + { 0x35ba78187e6b74f1ULL, 0x0fcd6e9df9add3c1ULL, }, + { 0x3e3ad4ae1266c290ULL, 0x1637d725aebdb714ULL, }, /* 64 */ + { 0x0e3a0c27f7d6aae4ULL, 0x0575fbb7f08ff55cULL, }, + { 0x1c00082337c84b78ULL, 0x0c3d39640fde8392ULL, }, + { 0xda65cd5e9f696cdcULL, 0xdeeb6bec644a26d0ULL, }, + { 0x0e3a0c27f7d6aae4ULL, 0x0575fbb7f08ff55cULL, }, + { 0x17945c09b2e19689ULL, 0x032b395187d966b4ULL, }, + { 0xec1f0e54b5aa67beULL, 0xfbe95b6e67ae6296ULL, }, + { 0x1aad30609bff5437ULL, 0xf059a43d01b40370ULL, }, + { 0x1c00082337c84b78ULL, 0x0c3d39640fde8392ULL, }, /* 72 */ + { 0xec1f0e54b5aa67beULL, 0xfbe95b6e67ae6296ULL, }, + { 0x2e9326619bb7c8e4ULL, 0x225024d84d163b91ULL, }, + { 0xc17a5d0372a2a622ULL, 0x0afd6368668933a8ULL, }, + { 0xda65cd5e9f696cdcULL, 0xdeeb6bec644a26d0ULL, }, + { 0x1aad30609bff5437ULL, 0xf059a43d01b40370ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_h.c b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_h.c new file mode 100644 index 0000000000..40de72ae97 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DOTP_S.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DOTP_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0002000200020002ULL, 0x0002000200020002ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ac00ac00ac00acULL, 0x00ac00ac00ac00acULL, }, + { 0xff56ff56ff56ff56ULL, 0xff56ff56ff56ff56ULL, }, + { 0x0068006800680068ULL, 0x0068006800680068ULL, }, + { 0xff9aff9aff9aff9aULL, 0xff9aff9aff9aff9aULL, }, + { 0x008fffe5003a008fULL, 0xffe5003a008fffe5ULL, }, + { 0xff73001dffc8ff73ULL, 0x001dffc8ff73001dULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00ac00ac00ac00acULL, 0x00ac00ac00ac00acULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x39c839c839c839c8ULL, 0x39c839c839c839c8ULL, }, + { 0xc6e4c6e4c6e4c6e4ULL, 0xc6e4c6e4c6e4c6e4ULL, }, + { 0x22f022f022f022f0ULL, 0x22f022f022f022f0ULL, }, + { 0xddbcddbcddbcddbcULL, 0xddbcddbcddbcddbcULL, }, + { 0x300af6ee137c300aULL, 0xf6ee137c300af6eeULL, }, + { 0xd0a209beed30d0a2ULL, 0x09beed30d0a209beULL, }, + { 0xff56ff56ff56ff56ULL, 0xff56ff56ff56ff56ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc6e4c6e4c6e4c6e4ULL, 0xc6e4c6e4c6e4c6e4ULL, }, + { 0x3872387238723872ULL, 0x3872387238723872ULL, }, + { 0xdd78dd78dd78dd78ULL, 0xdd78dd78dd78dd78ULL, }, + { 0x21de21de21de21deULL, 0x21de21de21de21deULL, }, + { 0xd08508f7ecbed085ULL, 0x08f7ecbed08508f7ULL, }, + { 0x2ed1f65f12982ed1ULL, 0xf65f12982ed1f65fULL, }, + { 0x0068006800680068ULL, 0x0068006800680068ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x22f022f022f022f0ULL, 0x22f022f022f022f0ULL, }, + { 0xdd78dd78dd78dd78ULL, 0xdd78dd78dd78dd78ULL, }, + { 0x1520152015201520ULL, 0x1520152015201520ULL, }, + { 0xeb48eb48eb48eb48ULL, 0xeb48eb48eb48eb48ULL, }, + { 0x1d0cfa840bc81d0cULL, 0xfa840bc81d0cfa84ULL, }, + { 0xe35c05e4f4a0e35cULL, 0x05e4f4a0e35c05e4ULL, }, + { 0xff9aff9aff9aff9aULL, 0xff9aff9aff9aff9aULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xddbcddbcddbcddbcULL, 0xddbcddbcddbcddbcULL, }, + { 0x21de21de21de21deULL, 0x21de21de21de21deULL, }, + { 0xeb48eb48eb48eb48ULL, 0xeb48eb48eb48eb48ULL, }, + { 0x1452145214521452ULL, 0x1452145214521452ULL, }, + { 0xe3830561f472e383ULL, 0x0561f472e3830561ULL, }, + { 0x1c17fa390b281c17ULL, 0xfa390b281c17fa39ULL, }, + { 0x008fffe5003a008fULL, 0xffe5003a008fffe5ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x300af6ee137c300aULL, 0xf6ee137c300af6eeULL, }, + { 0xd08508f7ecbed085ULL, 0x08f7ecbed08508f7ULL, }, + { 0x1d0cfa840bc81d0cULL, 0xfa840bc81d0cfa84ULL, }, + { 0xe3830561f472e383ULL, 0x0561f472e3830561ULL, }, + { 0x360d0f893f04360dULL, 0x0f893f04360d0f89ULL, }, + { 0xca82f05cc136ca82ULL, 0xf05cc136ca82f05cULL, }, + { 0xff73001dffc8ff73ULL, 0x001dffc8ff73001dULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd0a209beed30d0a2ULL, 0x09beed30d0a209beULL, }, + { 0x2ed1f65f12982ed1ULL, 0xf65f12982ed1f65fULL, }, + { 0xe35c05e4f4a0e35cULL, 0x05e4f4a0e35c05e4ULL, }, + { 0x1c17fa390b281c17ULL, 0xfa390b281c17fa39ULL, }, + { 0xca82f05cc136ca82ULL, 0xf05cc136ca82f05cULL, }, + { 0x34f10fc13e9234f1ULL, 0x0fc13e9234f10fc1ULL, }, + { 0x64240d342bc42c39ULL, 0x3f6a22fd3b1d1990ULL, }, /* 64 */ + { 0xe704ebe4e24eef13ULL, 0x01a706951e1be630ULL, }, + { 0x4ca419cce226b927ULL, 0xfb55fd241553f560ULL, }, + { 0xec36ee202172098aULL, 0xd846ec28206404e0ULL, }, + { 0xe704ebe4e24eef13ULL, 0x01a706951e1be630ULL, }, + { 0x111d264945920cf1ULL, 0x0195153d113a1a54ULL, }, + { 0xea70debeff82160dULL, 0x04260f88039c0b8aULL, }, + { 0xe9721dc70769091eULL, 0xf8711c48091bf7e4ULL, }, + { 0x4ca419cce226b927ULL, 0xfb55fd241553f560ULL, }, /* 72 */ + { 0xea70debeff82160dULL, 0x04260f88039c0b8aULL, }, + { 0x3b3437281d127579ULL, 0x0c310d25237206e9ULL, }, + { 0xf706df16dc8de6b6ULL, 0xf0d31b5827f9f42aULL, }, + { 0xec36ee202172098aULL, 0xd846ec28206404e0ULL, }, + { 0xe9721dc70769091eULL, 0xf8711c48091bf7e4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_w.c b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_w.c new file mode 100644 index 0000000000..2f1d23be6f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DOTP_S.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DOTP_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000200000002ULL, 0x0000000200000002ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaac0000aaacULL, 0x0000aaac0000aaacULL, }, + { 0xffff5556ffff5556ULL, 0xffff5556ffff5556ULL, }, + { 0x0000666800006668ULL, 0x0000666800006668ULL, }, + { 0xffff999affff999aULL, 0xffff999affff999aULL, }, + { 0xffffe38f00008e3aULL, 0x000038e5ffffe38fULL, }, + { 0x00001c73ffff71c8ULL, 0xffffc71d00001c73ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaac0000aaacULL, 0x0000aaac0000aaacULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e471c838e471c8ULL, 0x38e471c838e471c8ULL, }, + { 0xc71c38e4c71c38e4ULL, 0xc71c38e4c71c38e4ULL, }, + { 0x2222eef02222eef0ULL, 0x2222eef02222eef0ULL, }, + { 0xddddbbbcddddbbbcULL, 0xddddbbbcddddbbbcULL, }, + { 0xf684ed0a2f69097cULL, 0x12f725eef684ed0aULL, }, + { 0x097bbda2d097a130ULL, 0xed0984be097bbda2ULL, }, + { 0xffff5556ffff5556ULL, 0xffff5556ffff5556ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c38e4c71c38e4ULL, 0xc71c38e4c71c38e4ULL, }, + { 0x38e31c7238e31c72ULL, 0x38e31c7238e31c72ULL, }, + { 0xdddd7778dddd7778ULL, 0xdddd7778dddd7778ULL, }, + { 0x2221ddde2221dddeULL, 0x2221ddde2221dddeULL, }, + { 0x097af685d09784beULL, 0xed0912f7097af685ULL, }, + { 0xf6845ed12f67d098ULL, 0x12f6425ff6845ed1ULL, }, + { 0x0000666800006668ULL, 0x0000666800006668ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222eef02222eef0ULL, 0x2222eef02222eef0ULL, }, + { 0xdddd7778dddd7778ULL, 0xdddd7778dddd7778ULL, }, + { 0x147b8520147b8520ULL, 0x147b8520147b8520ULL, }, + { 0xeb84e148eb84e148ULL, 0xeb84e148eb84e148ULL, }, + { 0xfa4fb60c1c7271c8ULL, 0x0b612d84fa4fb60cULL, }, + { 0x05b0b05ce38df4a0ULL, 0xf49f38e405b0b05cULL, }, + { 0xffff999affff999aULL, 0xffff999affff999aULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xddddbbbcddddbbbcULL, 0xddddbbbcddddbbbcULL, }, + { 0x2221ddde2221dddeULL, 0x2221ddde2221dddeULL, }, + { 0xeb84e148eb84e148ULL, 0xeb84e148eb84e148ULL, }, + { 0x147ab852147ab852ULL, 0x147ab852147ab852ULL, }, + { 0x05b02d83e38e1c72ULL, 0xf49f0b6105b02d83ULL, }, + { 0xfa4f6c171c717d28ULL, 0x0b608e39fa4f6c17ULL, }, + { 0xffffe38f00008e3aULL, 0x000038e5ffffe38fULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xf684ed0a2f69097cULL, 0x12f725eef684ed0aULL, }, + { 0x097af685d09784beULL, 0xed0912f7097af685ULL, }, + { 0xfa4fb60c1c7271c8ULL, 0x0b612d84fa4fb60cULL, }, + { 0x05b02d83e38e1c72ULL, 0xf49f0b6105b02d83ULL, }, + { 0x0fcd3c0d35bb4f04ULL, 0x3f3645890fcd3c0dULL, }, + { 0xf032a782ca453f36ULL, 0xc0c9f35cf032a782ULL, }, + { 0x00001c73ffff71c8ULL, 0xffffc71d00001c73ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x097bbda2d097a130ULL, 0xed0984be097bbda2ULL, }, + { 0xf6845ed12f67d098ULL, 0x12f6425ff6845ed1ULL, }, + { 0x05b0b05ce38df4a0ULL, 0xf49f38e405b0b05cULL, }, + { 0xfa4f6c171c717d28ULL, 0x0b608e39fa4f6c17ULL, }, + { 0xf032a782ca453f36ULL, 0xc0c9f35cf032a782ULL, }, + { 0x0fcd74f135ba3292ULL, 0x3f35d3c10fcd74f1ULL, }, + { 0x3a57fe7422c25584ULL, 0x16b6b9f518facfa9ULL, }, /* 64 */ + { 0x01f36d90f9441446ULL, 0x0286cfede5f4db15ULL, }, + { 0x2f1518bcce21d93eULL, 0x0934568af4ec6499ULL, }, + { 0xc9576c1204f83042ULL, 0xd91d3e4709b06e36ULL, }, + { 0x01f36d90f9441446ULL, 0x0286cfede5f4db15ULL, }, + { 0x0012474d242f32a9ULL, 0x13f2a8f51ca9cd91ULL, }, + { 0x0144b48a04a7d0ddULL, 0x124b1c4e04fa8e45ULL, }, + { 0xfe2a6f6923268793ULL, 0x179e9377ef4766beULL, }, + { 0x2f1518bcce21d93eULL, 0x0934568af4ec6499ULL, }, /* 72 */ + { 0x0144b48a04a7d0ddULL, 0x124b1c4e04fa8e45ULL, }, + { 0x352c988848431561ULL, 0x12e4f841217b42c9ULL, }, + { 0xd437b4e8f3b0139fULL, 0x08c7d980187d5896ULL, }, + { 0xc9576c1204f83042ULL, 0xd91d3e4709b06e36ULL, }, + { 0xfe2a6f6923268793ULL, 0x179e9377ef4766beULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_d.c b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_d.c new file mode 100644 index 0000000000..e998e00410 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DOTP_U.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DOTP_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffffffc00000002ULL, 0xfffffffc00000002ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x55555552aaaaaaacULL, 0x55555552aaaaaaacULL, }, + { 0xaaaaaaa955555556ULL, 0xaaaaaaa955555556ULL, }, + { 0x9999999666666668ULL, 0x9999999666666668ULL, }, + { 0x666666659999999aULL, 0x666666659999999aULL, }, + { 0x71c71c6f8e38e38fULL, 0x1c71c719e38e38e5ULL, }, + { 0x8e38e38c71c71c73ULL, 0xe38e38e21c71c71dULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x55555552aaaaaaacULL, 0x55555552aaaaaaacULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e1c71c71c8ULL, 0xe38e38e1c71c71c8ULL, }, + { 0x71c71c70e38e38e4ULL, 0x71c71c70e38e38e4ULL, }, + { 0x1111110eeeeeeef0ULL, 0x1111110eeeeeeef0ULL, }, + { 0x44444443bbbbbbbcULL, 0x44444443bbbbbbbcULL, }, + { 0xf684bd9fb425ed0aULL, 0xbda12f6697b425eeULL, }, + { 0x5ed097b2f684bda2ULL, 0x97b425ec12f684beULL, }, + { 0xaaaaaaa955555556ULL, 0xaaaaaaa955555556ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x71c71c70e38e38e4ULL, 0x71c71c70e38e38e4ULL, }, + { 0x38e38e3871c71c72ULL, 0x38e38e3871c71c72ULL, }, + { 0x8888888777777778ULL, 0x8888888777777778ULL, }, + { 0x22222221dddddddeULL, 0x22222221dddddddeULL, }, + { 0x7b425ecfda12f685ULL, 0x5ed097b34bda12f7ULL, }, + { 0x2f684bd97b425ed1ULL, 0x4bda12f6097b425fULL, }, + { 0x9999999666666668ULL, 0x9999999666666668ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1111110eeeeeeef0ULL, 0x1111110eeeeeeef0ULL, }, + { 0x8888888777777778ULL, 0x8888888777777778ULL, }, + { 0x47ae147851eb8520ULL, 0x47ae147851eb8520ULL, }, + { 0x51eb851e147ae148ULL, 0x51eb851e147ae148ULL, }, + { 0x27d27d260b60b60cULL, 0xe38e38e182d82d84ULL, }, + { 0x71c71c705b05b05cULL, 0xb60b60b4e38e38e4ULL, }, + { 0x666666659999999aULL, 0x666666659999999aULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x44444443bbbbbbbcULL, 0x44444443bbbbbbbcULL, }, + { 0x22222221dddddddeULL, 0x22222221dddddddeULL, }, + { 0x51eb851e147ae148ULL, 0x51eb851e147ae148ULL, }, + { 0x147ae147851eb852ULL, 0x147ae147851eb852ULL, }, + { 0x49f49f4982d82d83ULL, 0x38e38e3860b60b61ULL, }, + { 0x1c71c71c16c16c17ULL, 0x2d82d82d38e38e39ULL, }, + { 0x71c71c6f8e38e38fULL, 0x1c71c719e38e38e5ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xf684bd9fb425ed0aULL, 0xbda12f6697b425eeULL, }, + { 0x7b425ecfda12f685ULL, 0x5ed097b34bda12f7ULL, }, + { 0x27d27d260b60b60cULL, 0xe38e38e182d82d84ULL, }, + { 0x49f49f4982d82d83ULL, 0x38e38e3860b60b61ULL, }, + { 0x1948b0fb9add3c0dULL, 0xd6e9e063c0ca4589ULL, }, + { 0x587e6b73f35ba782ULL, 0x4587e6b622c3f35cULL, }, + { 0x8e38e38c71c71c73ULL, 0xe38e38e21c71c71dULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5ed097b2f684bda2ULL, 0x97b425ec12f684beULL, }, + { 0x2f684bd97b425ed1ULL, 0x4bda12f6097b425fULL, }, + { 0x71c71c705b05b05cULL, 0xb60b60b4e38e38e4ULL, }, + { 0x1c71c71c16c16c17ULL, 0x2d82d82d38e38e39ULL, }, + { 0x587e6b73f35ba782ULL, 0x4587e6b622c3f35cULL, }, + { 0x35ba78187e6b74f1ULL, 0x9e06522bf9add3c1ULL, }, + { 0x4f10a2461266c290ULL, 0x132f373daebdb714ULL, }, /* 64 */ + { 0x9262f356f7d6aae4ULL, 0x1ab54eb3f08ff55cULL, }, + { 0x7927f2d937c84b78ULL, 0xb5e40e840fde8392ULL, }, + { 0x4ab4e3ab9f696cdcULL, 0xd21109f6644a26d0ULL, }, + { 0x9262f356f7d6aae4ULL, 0x1ab54eb3f08ff55cULL, }, + { 0x0f105ccfb2e19689ULL, 0x032b395187d966b4ULL, }, + { 0xe1cb8469b5aa67beULL, 0x1128ae6a67ae6296ULL, }, + { 0x8afc46ad9bff5437ULL, 0x1890b25301b40370ULL, }, + { 0x7927f2d937c84b78ULL, 0xb5e40e840fde8392ULL, }, /* 72 */ + { 0xe1cb8469b5aa67beULL, 0x1128ae6a67ae6296ULL, }, + { 0xfae79ab59bb7c8e4ULL, 0x78a66f004d163b91ULL, }, + { 0x8ffb559e72a2a622ULL, 0x8744321b668933a8ULL, }, + { 0x4ab4e3ab9f696cdcULL, 0xd21109f6644a26d0ULL, }, + { 0x8afc46ad9bff5437ULL, 0x1890b25301b40370ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_h.c b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_h.c new file mode 100644 index 0000000000..e8db601a74 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DOTP_U.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DOTP_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfc02fc02fc02fc02ULL, 0xfc02fc02fc02fc02ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x52ac52ac52ac52acULL, 0x52ac52ac52ac52acULL, }, + { 0xa956a956a956a956ULL, 0xa956a956a956a956ULL, }, + { 0x9668966896689668ULL, 0x9668966896689668ULL, }, + { 0x659a659a659a659aULL, 0x659a659a659a659aULL, }, + { 0x6f8f19e5c53a6f8fULL, 0x19e5c53a6f8f19e5ULL, }, + { 0x8c73e21d36c88c73ULL, 0xe21d36c88c73e21dULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x52ac52ac52ac52acULL, 0x52ac52ac52ac52acULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe1c8e1c8e1c8e1c8ULL, 0xe1c8e1c8e1c8e1c8ULL, }, + { 0x70e470e470e470e4ULL, 0x70e470e470e470e4ULL, }, + { 0x0ef00ef00ef00ef0ULL, 0x0ef00ef00ef00ef0ULL, }, + { 0x43bc43bc43bc43bcULL, 0x43bc43bc43bc43bcULL, }, + { 0xf50abbee837cf50aULL, 0xbbee837cf50abbeeULL, }, + { 0x5da296becf305da2ULL, 0x96becf305da296beULL, }, + { 0xa956a956a956a956ULL, 0xa956a956a956a956ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x70e470e470e470e4ULL, 0x70e470e470e470e4ULL, }, + { 0x3872387238723872ULL, 0x3872387238723872ULL, }, + { 0x8778877887788778ULL, 0x8778877887788778ULL, }, + { 0x21de21de21de21deULL, 0x21de21de21de21deULL, }, + { 0x7a855df741be7a85ULL, 0x5df741be7a855df7ULL, }, + { 0x2ed14b5f67982ed1ULL, 0x4b5f67982ed14b5fULL, }, + { 0x9668966896689668ULL, 0x9668966896689668ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0ef00ef00ef00ef0ULL, 0x0ef00ef00ef00ef0ULL, }, + { 0x8778877887788778ULL, 0x8778877887788778ULL, }, + { 0x4520452045204520ULL, 0x4520452045204520ULL, }, + { 0x5148514851485148ULL, 0x5148514851485148ULL, }, + { 0x260ce1849dc8260cULL, 0xe1849dc8260ce184ULL, }, + { 0x705cb4e4f8a0705cULL, 0xb4e4f8a0705cb4e4ULL, }, + { 0x659a659a659a659aULL, 0x659a659a659a659aULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x43bc43bc43bc43bcULL, 0x43bc43bc43bc43bcULL, }, + { 0x21de21de21de21deULL, 0x21de21de21de21deULL, }, + { 0x5148514851485148ULL, 0x5148514851485148ULL, }, + { 0x1452145214521452ULL, 0x1452145214521452ULL, }, + { 0x4983386127724983ULL, 0x3861277249833861ULL, }, + { 0x1c172d393e281c17ULL, 0x2d393e281c172d39ULL, }, + { 0x6f8f19e5c53a6f8fULL, 0x19e5c53a6f8f19e5ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xf50abbee837cf50aULL, 0xbbee837cf50abbeeULL, }, + { 0x7a855df741be7a85ULL, 0x5df741be7a855df7ULL, }, + { 0x260ce1849dc8260cULL, 0xe1849dc8260ce184ULL, }, + { 0x4983386127724983ULL, 0x3861277249833861ULL, }, + { 0x180dd5895b04180dULL, 0xd5895b04180dd589ULL, }, + { 0x5782445c6a365782ULL, 0x445c6a365782445cULL, }, + { 0x8c73e21d36c88c73ULL, 0xe21d36c88c73e21dULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5da296becf305da2ULL, 0x96becf305da296beULL, }, + { 0x2ed14b5f67982ed1ULL, 0x4b5f67982ed14b5fULL, }, + { 0x705cb4e4f8a0705cULL, 0xb4e4f8a0705cb4e4ULL, }, + { 0x1c172d393e281c17ULL, 0x2d393e281c172d39ULL, }, + { 0x5782445c6a365782ULL, 0x445c6a365782445cULL, }, + { 0x34f19dc1cc9234f1ULL, 0x9dc1cc9234f19dc1ULL, }, + { 0x742471342bc42c39ULL, 0x3f6a22fd371d7990ULL, }, /* 64 */ + { 0xd4044ee4444e4413ULL, 0x68a71195331b4430ULL, }, + { 0x80a423cc6c264e27ULL, 0x62556624be531a60ULL, }, + { 0x5c36512021725e8aULL, 0x8a465528c764a2e0ULL, }, + { 0xd4044ee4444e4413ULL, 0x68a71195331b4430ULL, }, + { 0x831d26496b929af1ULL, 0xef958b3d113a1254ULL, }, + { 0xeb7041beae82700dULL, 0xd326aa88189c1f8aULL, }, + { 0xa8721dc73869b21eULL, 0xf27179481e1be5e4ULL, }, + { 0x80a423cc6c264e27ULL, 0x62556624be531a60ULL, }, /* 72 */ + { 0xeb7041beae82700dULL, 0xd326aa88189c1f8aULL, }, + { 0x9334e7282d128b79ULL, 0xbc319725797206e9ULL, }, + { 0x670642166b8da1b6ULL, 0xe0d340587bf92d2aULL, }, + { 0x5c36512021725e8aULL, 0x8a465528c764a2e0ULL, }, + { 0xa8721dc73869b21eULL, 0xf27179481e1be5e4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_w.c b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_w.c new file mode 100644 index 0000000000..cf5bd13f48 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-dot-product/test_msa_dotp_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction DOTP_U.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "DOTP_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xfffc0002fffc0002ULL, 0xfffc0002fffc0002ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5552aaac5552aaacULL, 0x5552aaac5552aaacULL, }, + { 0xaaa95556aaa95556ULL, 0xaaa95556aaa95556ULL, }, + { 0x9996666899966668ULL, 0x9996666899966668ULL, }, + { 0x6665999a6665999aULL, 0x6665999a6665999aULL, }, + { 0x1c6fe38f71c48e3aULL, 0xc71a38e51c6fe38fULL, }, + { 0xe38c1c738e3771c8ULL, 0x38e1c71de38c1c73ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5552aaac5552aaacULL, 0x5552aaac5552aaacULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38c71c8e38c71c8ULL, 0xe38c71c8e38c71c8ULL, }, + { 0x71c638e471c638e4ULL, 0x71c638e471c638e4ULL, }, + { 0x110eeef0110eeef0ULL, 0x110eeef0110eeef0ULL, }, + { 0x4443bbbc4443bbbcULL, 0x4443bbbc4443bbbcULL, }, + { 0xbd9fed0af683097cULL, 0x84bc25eebd9fed0aULL, }, + { 0x97b2bda25ecfa130ULL, 0xd09684be97b2bda2ULL, }, + { 0xaaa95556aaa95556ULL, 0xaaa95556aaa95556ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x71c638e471c638e4ULL, 0x71c638e471c638e4ULL, }, + { 0x38e31c7238e31c72ULL, 0x38e31c7238e31c72ULL, }, + { 0x8887777888877778ULL, 0x8887777888877778ULL, }, + { 0x2221ddde2221dddeULL, 0x2221ddde2221dddeULL, }, + { 0x5ecff6857b4184beULL, 0x425e12f75ecff685ULL, }, + { 0x4bd95ed12f67d098ULL, 0x684b425f4bd95ed1ULL, }, + { 0x9996666899966668ULL, 0x9996666899966668ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x110eeef0110eeef0ULL, 0x110eeef0110eeef0ULL, }, + { 0x8887777888877778ULL, 0x8887777888877778ULL, }, + { 0x47ab852047ab8520ULL, 0x47ab852047ab8520ULL, }, + { 0x51eae14851eae148ULL, 0x51eae14851eae148ULL, }, + { 0xe38cb60c27d071c8ULL, 0x9f482d84e38cb60cULL, }, + { 0xb609b05c71c5f4a0ULL, 0xfa4e38e4b609b05cULL, }, + { 0x6665999a6665999aULL, 0x6665999a6665999aULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4443bbbc4443bbbcULL, 0x4443bbbc4443bbbcULL, }, + { 0x2221ddde2221dddeULL, 0x2221ddde2221dddeULL, }, + { 0x51eae14851eae148ULL, 0x51eae14851eae148ULL, }, + { 0x147ab852147ab852ULL, 0x147ab852147ab852ULL, }, + { 0x38e32d8349f41c72ULL, 0x27d20b6138e32d83ULL, }, + { 0x2d826c171c717d28ULL, 0x3e938e392d826c17ULL, }, + { 0x1c6fe38f71c48e3aULL, 0xc71a38e51c6fe38fULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xbd9fed0af683097cULL, 0x84bc25eebd9fed0aULL, }, + { 0x5ecff6857b4184beULL, 0x425e12f75ecff685ULL, }, + { 0xe38cb60c27d071c8ULL, 0x9f482d84e38cb60cULL, }, + { 0x38e32d8349f41c72ULL, 0x27d20b6138e32d83ULL, }, + { 0xd6e93c0d19474f04ULL, 0x5ba64589d6e93c0dULL, }, + { 0x4586a782587d3f36ULL, 0x6b73f35c4586a782ULL, }, + { 0xe38c1c738e3771c8ULL, 0x38e1c71de38c1c73ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x97b2bda25ecfa130ULL, 0xd09684be97b2bda2ULL, }, + { 0x4bd95ed12f67d098ULL, 0x684b425f4bd95ed1ULL, }, + { 0xb609b05c71c5f4a0ULL, 0xfa4e38e4b609b05cULL, }, + { 0x2d826c171c717d28ULL, 0x3e938e392d826c17ULL, }, + { 0x4586a782587d3f36ULL, 0x6b73f35c4586a782ULL, }, + { 0x9e0574f135ba3292ULL, 0xcd6dd3c19e0574f1ULL, }, + { 0x18c3fe7422c25584ULL, 0x16b6b9f57608cfa9ULL, }, /* 64 */ + { 0x867e6d904e841446ULL, 0x0de4cfed4e2fdb15ULL, }, + { 0xf94f18bc4bc3d93eULL, 0x1492568ac3a66499ULL, }, + { 0x4ff36c125a383042ULL, 0x2fe23e4744196e36ULL, }, + { 0x867e6d904e841446ULL, 0x0de4cfed4e2fdb15ULL, }, + { 0xf78e474db23f32a9ULL, 0x8a26a8f51ca9cd91ULL, }, + { 0xa9bfb48aa4c2d0ddULL, 0x94641c4e1a398e45ULL, }, + { 0x6e796f69cc7c8793ULL, 0x6e879377578266beULL, }, + { 0xf94f18bc4bc3d93eULL, 0x1492568ac3a66499ULL, }, /* 72 */ + { 0xa9bfb48aa4c2d0ddULL, 0x94641c4e1a398e45ULL, }, + { 0xeb349888d2e11561ULL, 0xa0e2f84177d142c9ULL, }, + { 0x5ad3b4e8bfaf139fULL, 0x8076d98091fe5896ULL, }, + { 0x4ff36c125a383042ULL, 0x2fe23e4744196e36ULL, }, + { 0x6e796f69cc7c8793ULL, 0x6e879377578266beULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_DOTP_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c new file mode 100644 index 0000000000..5fa2644c30 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_A.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_A.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, }, + { 0xaa71aaaa71aaaa71ULL, 0xaaaa71aaaa71aaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x558e55558e55558eULL, 0x55558e55558e5555ULL, }, + { 0x5571555571555571ULL, 0x5555715555715555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, }, + { 0xcc71c7cc71c7cc71ULL, 0xc7cc71c7cc71c7ccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x338e38338e38338eULL, 0x38338e38338e3833ULL, }, + { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, }, + { 0x558e55558e55558eULL, 0x55558e55558e5555ULL, }, + { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, }, + { 0x338e38338e38338eULL, 0x38338e38338e3833ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaa71aaaa71aaaa71ULL, 0xaaaa71aaaa71aaaaULL, }, + { 0x5571555571555571ULL, 0x5555715555715555ULL, }, + { 0xcc71c7cc71c7cc71ULL, 0xc7cc71c7cc71c7ccULL, }, + { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, }, + { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6634d935540ULL, 0x4b67bb5e157b520cULL, }, + { 0x886aaeaab9628b80ULL, 0x4b67c65eab7bb014ULL, }, + { 0x886ae64d5e62554eULL, 0x8d67885ea97bb0a0ULL, }, + { 0x886ae6634d935540ULL, 0x4b67bb5e157b520cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aae634d938b80ULL, 0x27d8bb1aab3f5214ULL, }, + { 0x704f16635e93c74eULL, 0x8df188d8a94252a0ULL, }, + { 0x886aaeaab9628b80ULL, 0x4b67c65eab7bb014ULL, }, /* 72 */ + { 0xac5aae634d938b80ULL, 0x27d8bb1aab3f5214ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x705aaeaa5e318b80ULL, 0x8dd888d8a94225a0ULL, }, + { 0x886ae64d5e62554eULL, 0x8d67885ea97bb0a0ULL, }, + { 0x704f16635e93c74eULL, 0x8df188d8a94252a0ULL, }, + { 0x705aaeaa5ecf8b80ULL, 0x8dd888d8a94225a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c new file mode 100644 index 0000000000..9d97982ab5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_A.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_A.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 72 */ + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c new file mode 100644 index 0000000000..3365f726a2 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_A.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_A.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, }, + { 0xaaaaaaaa71c7aaaaULL, 0xaaaa71c7aaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x555555558e385555ULL, 0x55558e3855555555ULL, }, + { 0x5555555571c75555ULL, 0x555571c755555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, }, + { 0xccccc71c71c7ccccULL, 0xc71c71c7ccccc71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x333338e38e383333ULL, 0x38e38e38333338e3ULL, }, + { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, }, + { 0x555555558e385555ULL, 0x55558e3855555555ULL, }, + { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, }, + { 0x333338e38e383333ULL, 0x38e38e38333338e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaa71c7aaaaULL, 0xaaaa71c7aaaaaaaaULL, }, + { 0x5555555571c75555ULL, 0x555571c755555555ULL, }, + { 0xccccc71c71c7ccccULL, 0xc71c71c7ccccc71cULL, }, + { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, }, + { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc4d935540ULL, 0x4b67bb1a153f52fcULL, }, + { 0x886aaeaab9cf8b80ULL, 0x4b67c6ffab2bb00cULL, }, + { 0x886ae6cc5e315540ULL, 0x8df188d8a942b00cULL, }, + { 0x886ae6cc4d935540ULL, 0x4b67bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaa4d938b80ULL, 0x27d8bb1aab2b52fcULL, }, + { 0x704f164d5e31c708ULL, 0x8df188d8a94252fcULL, }, + { 0x886aaeaab9cf8b80ULL, 0x4b67c6ffab2bb00cULL, }, /* 72 */ + { 0xac5aaeaa4d938b80ULL, 0x27d8bb1aab2b52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704faeaa5e318b80ULL, 0x8df188d8a9422514ULL, }, + { 0x886ae6cc5e315540ULL, 0x8df188d8a942b00cULL, }, + { 0x704f164d5e31c708ULL, 0x8df188d8a94252fcULL, }, + { 0x704faeaa5e318b80ULL, 0x8df188d8a9422514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c new file mode 100644 index 0000000000..b33f4b7d79 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_a_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_A.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_A.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x555555558e38e38eULL, 0x5555555555555555ULL, }, + { 0x5555555571c71c71ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, }, + { 0xcccccccc71c71c71ULL, 0xc71c71c7ccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x333333338e38e38eULL, 0x38e38e3833333333ULL, }, + { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x555555558e38e38eULL, 0x5555555555555555ULL, }, + { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, }, + { 0x333333338e38e38eULL, 0x38e38e3833333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaa71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555571c71c71ULL, 0x5555555555555555ULL, }, + { 0xcccccccc71c71c71ULL, 0xc71c71c7ccccccccULL, }, + { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc4d93c708ULL, 0x4b670b5e153f52fcULL, }, + { 0x886ae6ccb9cf8b80ULL, 0x4b670b5eab2b2514ULL, }, + { 0x886ae6cc5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc4d93c708ULL, 0x4b670b5e153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaa4d93c708ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6ccb9cf8b80ULL, 0x4b670b5eab2b2514ULL, }, /* 72 */ + { 0xac5aaeaa4d93c708ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_A_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c new file mode 100644 index 0000000000..71e571d0c4 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_S.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xffff38ffff38ffffULL, 0x38ffff38ffff38ffULL, }, + { 0x1c71ff1c71ff1c71ULL, 0xff1c71ff1c71ff1cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000380000380000ULL, 0x3800003800003800ULL, }, + { 0x1c71001c71001c71ULL, 0x001c71001c71001cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5571555571555571ULL, 0x5555715555715555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe3cc38e3cc38e3ccULL, 0x38e3cc38e3cc38e3ULL, }, + { 0x1c71cc1c71cc1c71ULL, 0xcc1c71cc1c71cc1cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333383333383333ULL, 0x3833333833333833ULL, }, + { 0x3371333371333371ULL, 0x3333713333713333ULL, }, + { 0xffff38ffff38ffffULL, 0x38ffff38ffff38ffULL, }, /* 48 */ + { 0x0000380000380000ULL, 0x3800003800003800ULL, }, + { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe3cc38e3cc38e3ccULL, 0x38e3cc38e3cc38e3ULL, }, + { 0x3333383333383333ULL, 0x3833333833333833ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, }, + { 0x1c71ff1c71ff1c71ULL, 0xff1c71ff1c71ff1cULL, }, /* 56 */ + { 0x1c71001c71001c71ULL, 0x001c71001c71001cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5571555571555571ULL, 0x5555715555715555ULL, }, + { 0x1c71cc1c71cc1c71ULL, 0xcc1c71cc1c71cc1cULL, }, + { 0x3371333371333371ULL, 0x3333713333713333ULL, }, + { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfb6a00634d625540ULL, 0x4b670b5e157b520cULL, }, + { 0xac6ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, }, + { 0x706a164d5e62554eULL, 0x4b670b5efe7be20cULL, }, + { 0xfb6a00634d625540ULL, 0x4b670b5e157b520cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfb5a00634dcfc708ULL, 0x27f7c61a153f5214ULL, }, + { 0x704f16635e31e24eULL, 0x12f7bb1a154252fcULL, }, + { 0xac6ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, }, /* 72 */ + { 0xfb5a00634dcfc708ULL, 0x27f7c61a153f5214ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x705a164d5e31e24eULL, 0x27f1c6ffab422514ULL, }, + { 0x706a164d5e62554eULL, 0x4b670b5efe7be20cULL, }, + { 0x704f16635e31e24eULL, 0x12f7bb1a154252fcULL, }, + { 0x705a164d5e31e24eULL, 0x27f1c6ffab422514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c new file mode 100644 index 0000000000..e088ab99e3 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_S.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c new file mode 100644 index 0000000000..6d1b81a119 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_S.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xffff38e3ffffffffULL, 0x38e3ffffffff38e3ULL, }, + { 0x1c71ffff71c71c71ULL, 0xffff71c71c71ffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x000038e300000000ULL, 0x38e30000000038e3ULL, }, + { 0x1c71000071c71c71ULL, 0x000071c71c710000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555571c75555ULL, 0x555571c755555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3cccce38eULL, 0x38e3cccce38e38e3ULL, }, + { 0x1c71cccc71c71c71ULL, 0xcccc71c71c71ccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x333338e333333333ULL, 0x38e33333333338e3ULL, }, + { 0x3333333371c73333ULL, 0x333371c733333333ULL, }, + { 0xffff38e3ffffffffULL, 0x38e3ffffffff38e3ULL, }, /* 48 */ + { 0x000038e300000000ULL, 0x38e30000000038e3ULL, }, + { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e38e3cccce38eULL, 0x38e3cccce38e38e3ULL, }, + { 0x333338e333333333ULL, 0x38e33333333338e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, }, + { 0x1c71ffff71c71c71ULL, 0xffff71c71c71ffffULL, }, /* 56 */ + { 0x1c71000071c71c71ULL, 0x000071c71c710000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555571c75555ULL, 0x555571c755555555ULL, }, + { 0x1c71cccc71c71c71ULL, 0xcccc71c71c71ccccULL, }, + { 0x3333333371c73333ULL, 0x333371c733333333ULL, }, + { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00634d935540ULL, 0x4b670b5e153f52fcULL, }, + { 0xac5ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, }, + { 0x704f164d5e315540ULL, 0x4b670b5efe7be2a0ULL, }, + { 0xfbbe00634d935540ULL, 0x4b670b5e153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5ae6cc28625540ULL, 0x4b670b5efe7b2514ULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e315540ULL, 0x4b670b5efe7be2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c new file mode 100644 index 0000000000..bd64294322 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_s_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_S.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38ffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffff1c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x38e38e3800000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0x000000001c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555571c71c71ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3ccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xcccccccc1c71c71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x38e38e3833333333ULL, }, + { 0x3333333371c71c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38ffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e38e3800000000ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e38e3ccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e3833333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffff1c71c71cULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x000000001c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555571c71c71ULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0xcccccccc1c71c71cULL, }, + { 0x3333333371c71c71ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x4b670b5e153f52fcULL, }, + { 0xac5aaeaa28625540ULL, 0x4b670b5efe7bb00cULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x4b670b5e153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaa28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0x27d8c6ff153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c new file mode 100644 index 0000000000..206d907a26 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_U.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe3aaaae3aaaae3aaULL, 0xaae3aaaae3aaaae3ULL, }, + { 0xaaaac7aaaac7aaaaULL, 0xc7aaaac7aaaac7aaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e55e38e55e38eULL, 0x55e38e55e38e55e3ULL, }, + { 0x5571c75571c75571ULL, 0xc75571c75571c755ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe3aaaae3aaaae3aaULL, 0xaae3aaaae3aaaae3ULL, }, + { 0xe38e55e38e55e38eULL, 0x55e38e55e38e55e3ULL, }, + { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaac7aaaac7aaaaULL, 0xc7aaaac7aaaac7aaULL, }, + { 0x5571c75571c75571ULL, 0xc75571c75571c755ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3371c73371c73371ULL, 0xc73371c73371c733ULL, }, + { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbee6cc4d93c740ULL, 0x4bf7bb5efe7bb0fcULL, }, + { 0xac6ae6ccb9cf8b80ULL, 0x4bd8c6fffe7bb014ULL, }, + { 0x886ae6cc5e62e24eULL, 0x8df188d8fe7be2a0ULL, }, + { 0xfbbee6cc4d93c740ULL, 0x4bf7bb5efe7bb0fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbeaeaab9cfc780ULL, 0x27f7c6ffab3f52fcULL, }, + { 0xfbbe16635e93e24eULL, 0x8df7bbd8a942e2fcULL, }, + { 0xac6ae6ccb9cf8b80ULL, 0x4bd8c6fffe7bb014ULL, }, /* 72 */ + { 0xfbbeaeaab9cfc780ULL, 0x27f7c6ffab3f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cfe280ULL, 0x8df1c6ffab42e2a0ULL, }, + { 0x886ae6cc5e62e24eULL, 0x8df188d8fe7be2a0ULL, }, + { 0xfbbe16635e93e24eULL, 0x8df7bbd8a942e2fcULL, }, + { 0xac5aaeaab9cfe280ULL, 0x8df1c6ffab42e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c new file mode 100644 index 0000000000..4dd247f54a --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_U.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c new file mode 100644 index 0000000000..0e6a7651eb --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_U.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38eaaaaaaaae38eULL, 0xaaaaaaaae38eaaaaULL, }, + { 0xaaaac71caaaaaaaaULL, 0xc71caaaaaaaac71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e55558e38e38eULL, 0x55558e38e38e5555ULL, }, + { 0x5555c71c71c75555ULL, 0xc71c71c75555c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38eaaaaaaaae38eULL, 0xaaaaaaaae38eaaaaULL, }, + { 0xe38e55558e38e38eULL, 0x55558e38e38e5555ULL, }, + { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaac71caaaaaaaaULL, 0xc71caaaaaaaac71cULL, }, + { 0x5555c71c71c75555ULL, 0xc71c71c75555c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333c71c71c73333ULL, 0xc71c71c73333c71cULL, }, + { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbee6cc4d93c708ULL, 0x4b67bb1afe7bb00cULL, }, + { 0xac5ae6ccb9cf8b80ULL, 0x4b67c6fffe7bb00cULL, }, + { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7be2a0ULL, }, + { 0xfbbee6cc4d93c708ULL, 0x4b67bb1afe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbeaeaab9cfc708ULL, 0x27d8c6ffab2b52fcULL, }, + { 0xfbbe164d5e31e24eULL, 0x8df1bb1aa942e2a0ULL, }, + { 0xac5ae6ccb9cf8b80ULL, 0x4b67c6fffe7bb00cULL, }, /* 72 */ + { 0xfbbeaeaab9cfc708ULL, 0x27d8c6ffab2b52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cfe24eULL, 0x8df1c6ffab2be2a0ULL, }, + { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7be2a0ULL, }, + { 0xfbbe164d5e31e24eULL, 0x8df1bb1aa942e2a0ULL, }, + { 0xac5aaeaab9cfe24eULL, 0x8df1c6ffab2be2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c new file mode 100644 index 0000000000..db61440551 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_max_u_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MAX_U.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MAX_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e3aaaaaaaaULL, 0xaaaaaaaae38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c7aaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x55555555e38e38e3ULL, }, + { 0x5555555571c71c71ULL, 0xc71c71c755555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0xaaaaaaaae38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x55555555e38e38e3ULL, }, + { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c7aaaaaaaaULL, }, + { 0x5555555571c71c71ULL, 0xc71c71c755555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333371c71c71ULL, 0xc71c71c733333333ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, }, + { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe0063b9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xfbbe00635e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x4b670b5efe7bb00cULL, }, /* 72 */ + { 0xfbbe0063b9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8ab2b2514ULL, }, + { 0x886ae6cc5e31e24eULL, 0x8df188d8fe7bb00cULL, }, + { 0xfbbe00635e31e24eULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8ab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MAX_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c new file mode 100644 index 0000000000..d2a93a2e44 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_A.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_A.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, }, + { 0x1caac71caac71caaULL, 0xc71caac71caac71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe35538e35538e355ULL, 0x38e35538e35538e3ULL, }, + { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, }, + { 0x1ccccc1ccccc1cccULL, 0xcc1ccccc1ccccc1cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe33333e33333e333ULL, 0x33e33333e33333e3ULL, }, + { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe3aa38e3aa38e3aaULL, 0x38e3aa38e3aa38e3ULL, }, + { 0xe35538e35538e355ULL, 0x38e35538e35538e3ULL, }, + { 0xe3cccce3cccce3ccULL, 0xcce3cccce3cccce3ULL, }, + { 0xe33333e33333e333ULL, 0x33e33333e33333e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1caac71caac71caaULL, 0xc71caac71caac71cULL, }, + { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, }, + { 0x1ccccc1ccccc1cccULL, 0xcc1ccccc1ccccc1cULL, }, + { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, }, + { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00cc2862c708ULL, 0x12f70b1afe3fb0fcULL, }, + { 0xac5ae6cc28cf5540ULL, 0x27d80bfffe2b250cULL, }, + { 0x704f16cc2831e240ULL, 0x4bf10bd8fe42e20cULL, }, + { 0xfbbe00cc2862c708ULL, 0x12f70b1afe3fb0fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00aab9cfc708ULL, 0x12f7c6ff152b25fcULL, }, + { 0xfbbe004d4d31e208ULL, 0x12f7bb1a153fe2fcULL, }, + { 0xac5ae6cc28cf5540ULL, 0x27d80bfffe2b250cULL, }, /* 72 */ + { 0xfbbe00aab9cfc708ULL, 0x12f7c6ff152b25fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac4f164db931e24eULL, 0x27f1c6ffab2be214ULL, }, + { 0x704f16cc2831e240ULL, 0x4bf10bd8fe42e20cULL, }, + { 0xfbbe004d4d31e208ULL, 0x12f7bb1a153fe2fcULL, }, + { 0xac4f164db9cfe24eULL, 0x27f1c6ffab2be214ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c new file mode 100644 index 0000000000..69fd3c7662 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_A.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_A.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c new file mode 100644 index 0000000000..9f45b55539 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_A.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_A.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, }, + { 0x1c71c71caaaa1c71ULL, 0xc71caaaa1c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e35555e38eULL, 0x38e35555e38e38e3ULL, }, + { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, }, + { 0x1c71cccccccc1c71ULL, 0xcccccccc1c71ccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e33333333e38eULL, 0x33333333e38e3333ULL, }, + { 0x1c71333333331c71ULL, 0x333333331c713333ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e3aaaae38eULL, 0x38e3aaaae38e38e3ULL, }, + { 0xe38e38e35555e38eULL, 0x38e35555e38e38e3ULL, }, + { 0xe38ecccccccce38eULL, 0xcccccccce38eccccULL, }, + { 0xe38e33333333e38eULL, 0x33333333e38e3333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71caaaa1c71ULL, 0xc71caaaa1c71c71cULL, }, + { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, }, + { 0x1c71cccccccc1c71ULL, 0xcccccccc1c71ccccULL, }, + { 0x1c71333333331c71ULL, 0x333333331c713333ULL, }, + { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe00632862c708ULL, 0x12f70b5efe7bb00cULL, }, + { 0xac5ae6cc28625540ULL, 0x27d80b5efe7b2514ULL, }, + { 0x704f164d2862e24eULL, 0x4b670b5efe7be2a0ULL, }, + { 0xfbbe00632862c708ULL, 0x12f70b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe0063b9cfc708ULL, 0x12f7c6ff153f2514ULL, }, + { 0xfbbe00634d93e24eULL, 0x12f7bb1a153fe2a0ULL, }, + { 0xac5ae6cc28625540ULL, 0x27d80b5efe7b2514ULL, }, /* 72 */ + { 0xfbbe0063b9cfc708ULL, 0x12f7c6ff153f2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5a164db9cfe24eULL, 0x27d8c6ffab2be2a0ULL, }, + { 0x704f164d2862e24eULL, 0x4b670b5efe7be2a0ULL, }, + { 0xfbbe00634d93e24eULL, 0x12f7bb1a153fe2a0ULL, }, + { 0xac5a164db9cfe24eULL, 0x27d8c6ffab2be2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c new file mode 100644 index 0000000000..b08231d65f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_a_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_A.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_A.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71caaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e355555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, }, + { 0x1c71c71cccccccccULL, 0xcccccccc1c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e333333333ULL, 0x33333333e38e38e3ULL, }, + { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e355555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e3ccccccccULL, 0xcccccccce38e38e3ULL, }, + { 0xe38e38e333333333ULL, 0x33333333e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71caaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71cccccccccULL, 0xcccccccc1c71c71cULL, }, + { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbbe006328625540ULL, 0x12f7bb1afe7bb00cULL, }, + { 0xac5aaeaa28625540ULL, 0x27d8c6fffe7bb00cULL, }, + { 0x704f164d28625540ULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe006328625540ULL, 0x12f7bb1afe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe0063b9cf8b80ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaa28625540ULL, 0x27d8c6fffe7bb00cULL, }, /* 72 */ + { 0xfbbe0063b9cf8b80ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d28625540ULL, 0x4b670b5efe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_A_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c new file mode 100644 index 0000000000..80b5201be1 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_S.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe38effe38effe38eULL, 0xffe38effe38effe3ULL, }, + { 0xffffc7ffffc7ffffULL, 0xc7ffffc7ffffc7ffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e00e38e00e38eULL, 0x00e38e00e38e00e3ULL, }, + { 0x0000c70000c70000ULL, 0xc70000c70000c700ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcc8ecccc8ecccc8eULL, 0xcccc8ecccc8eccccULL, }, + { 0xccccc7ccccc7ccccULL, 0xc7ccccc7ccccc7ccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e33e38e33e38eULL, 0x33e38e33e38e33e3ULL, }, + { 0x1c33c71c33c71c33ULL, 0xc71c33c71c33c71cULL, }, + { 0xe38effe38effe38eULL, 0xffe38effe38effe3ULL, }, /* 48 */ + { 0xe38e00e38e00e38eULL, 0x00e38e00e38e00e3ULL, }, + { 0xaa8eaaaa8eaaaa8eULL, 0xaaaa8eaaaa8eaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xcc8ecccc8ecccc8eULL, 0xcccc8ecccc8eccccULL, }, + { 0xe38e33e38e33e38eULL, 0x33e38e33e38e33e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, }, + { 0xffffc7ffffc7ffffULL, 0xc7ffffc7ffffc7ffULL, }, /* 56 */ + { 0x0000c70000c70000ULL, 0xc70000c70000c700ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c55c71c55c71c55ULL, 0xc71c55c71c55c71cULL, }, + { 0xccccc7ccccc7ccccULL, 0xc7ccccc7ccccc7ccULL, }, + { 0x1c33c71c33c71c33ULL, 0xc71c33c71c33c71cULL, }, + { 0xe38ec7e38ec7e38eULL, 0xc7e38ec7e38ec7e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x88bee6cc2893c708ULL, 0x12f7bb1afe3fb0fcULL, }, + { 0x885aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, }, + { 0x884fe6cc2831e240ULL, 0x8df188d8a942b0a0ULL, }, + { 0x88bee6cc2893c708ULL, 0x12f7bb1afe3fb0fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xacbeaeaab9938b80ULL, 0x12d8bbffab2b25fcULL, }, + { 0xfbbe004d4d93c708ULL, 0x8df188d8a93fe2a0ULL, }, + { 0x885aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, }, /* 72 */ + { 0xacbeaeaab9938b80ULL, 0x12d8bbffab2b25fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac4faeaab9cf8b80ULL, 0x8dd888d8a92be2a0ULL, }, + { 0x884fe6cc2831e240ULL, 0x8df188d8a942b0a0ULL, }, + { 0xfbbe004d4d93c708ULL, 0x8df188d8a93fe2a0ULL, }, + { 0xac4faeaab9cf8b80ULL, 0x8dd888d8a92be2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c new file mode 100644 index 0000000000..0ed319024c --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_S.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, }, + { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c new file mode 100644 index 0000000000..b049054d9f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_S.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe38effff8e38e38eULL, 0xffff8e38e38effffULL, }, + { 0xffffc71cffffffffULL, 0xc71cffffffffc71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e00008e38e38eULL, 0x00008e38e38e0000ULL, }, + { 0x0000c71c00000000ULL, 0xc71c00000000c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc8e38ccccULL, 0xcccc8e38ccccccccULL, }, + { 0xccccc71cccccccccULL, 0xc71cccccccccc71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e33338e38e38eULL, 0x33338e38e38e3333ULL, }, + { 0x1c71c71c33331c71ULL, 0xc71c33331c71c71cULL, }, + { 0xe38effff8e38e38eULL, 0xffff8e38e38effffULL, }, /* 48 */ + { 0xe38e00008e38e38eULL, 0x00008e38e38e0000ULL, }, + { 0xaaaaaaaa8e38aaaaULL, 0xaaaa8e38aaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xcccccccc8e38ccccULL, 0xcccc8e38ccccccccULL, }, + { 0xe38e33338e38e38eULL, 0x33338e38e38e3333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, }, + { 0xffffc71cffffffffULL, 0xc71cffffffffc71cULL, }, /* 56 */ + { 0x0000c71c00000000ULL, 0xc71c00000000c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c55551c71ULL, 0xc71c55551c71c71cULL, }, + { 0xccccc71cccccccccULL, 0xc71cccccccccc71cULL, }, + { 0x1c71c71c33331c71ULL, 0xc71c33331c71c71cULL, }, + { 0xe38ec71c8e38e38eULL, 0xc71c8e38e38ec71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc2862c708ULL, 0x12f7bb1afe7bb00cULL, }, + { 0x886aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, }, + { 0x886ae6cc2862e24eULL, 0x8df188d8a942b00cULL, }, + { 0x886ae6cc2862c708ULL, 0x12f7bb1afe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886aaeaab9cf8b80ULL, 0x27d8c6ffab2bb00cULL, }, /* 72 */ + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc2862e24eULL, 0x8df188d8a942b00cULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c new file mode 100644 index 0000000000..2bcd0a00ef --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_s_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_S.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffe38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xc71c71c7ffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x00000000e38e38e3ULL, }, + { 0x0000000000000000ULL, 0xc71c71c700000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc8e38e38eULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xc71c71c7ccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x33333333e38e38e3ULL, }, + { 0x1c71c71c33333333ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffe38e38e3ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x00000000e38e38e3ULL, }, + { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xcccccccc8e38e38eULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0x33333333e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xc71c71c7ffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c71c700000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c55555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xc71c71c7ccccccccULL, }, + { 0x1c71c71c33333333ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xc71c71c7e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc28625540ULL, 0x12f7bb1afe7bb00cULL, }, + { 0x886ae6ccb9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x12f7bb1afe7bb00cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6ccb9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1aab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x8df188d8a942e2a0ULL, }, + { 0xfbbe00634d93c708ULL, 0x8df188d8a942e2a0ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x8df188d8a942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c new file mode 100644 index 0000000000..2a06b43379 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_U.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaa8e38aa8e38aa8eULL, 0x38aa8e38aa8e38aaULL, }, + { 0x1c71aa1c71aa1c71ULL, 0xaa1c71aa1c71aa1cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555385555385555ULL, 0x3855553855553855ULL, }, + { 0x1c55551c55551c55ULL, 0x551c55551c55551cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaa8e38aa8e38aa8eULL, 0x38aa8e38aa8e38aaULL, }, + { 0x5555385555385555ULL, 0x3855553855553855ULL, }, + { 0xcc8e38cc8e38cc8eULL, 0x38cc8e38cc8e38ccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71aa1c71aa1c71ULL, 0xaa1c71aa1c71aa1cULL, }, + { 0x1c55551c55551c55ULL, 0x551c55551c55551cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c33331c33331c33ULL, 0x331c33331c33331cULL, }, + { 0x1c71381c71381c71ULL, 0x381c71381c71381cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886a006328625508ULL, 0x12670b1a153f520cULL, }, + { 0x885aaeaa28625540ULL, 0x27670b5eab2b250cULL, }, + { 0x704f164d28315540ULL, 0x4b670b5ea942b00cULL, }, + { 0x886a006328625508ULL, 0x12670b1a153f520cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5a00634d938b08ULL, 0x12d8bb1a152b2514ULL, }, + { 0x704f004d4d31c708ULL, 0x12f1881a153f52a0ULL, }, + { 0x885aaeaa28625540ULL, 0x27670b5eab2b250cULL, }, /* 72 */ + { 0xac5a00634d938b08ULL, 0x12d8bb1a152b2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e318b4eULL, 0x27d888d8a92b2514ULL, }, + { 0x704f164d28315540ULL, 0x4b670b5ea942b00cULL, }, + { 0x704f004d4d31c708ULL, 0x12f1881a153f52a0ULL, }, + { 0x704f164d5e318b4eULL, 0x27d888d8a92b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c new file mode 100644 index 0000000000..37924f3038 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_U.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, }, + { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0xac5aaeaab9cf8b80ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x4b670b5efe7bb00cULL, }, + { 0x704f164d5e31e24eULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c new file mode 100644 index 0000000000..1846995ce4 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_U.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaa38e38e38aaaaULL, 0x38e38e38aaaa38e3ULL, }, + { 0x1c71aaaa71c71c71ULL, 0xaaaa71c71c71aaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x555538e355555555ULL, 0x38e35555555538e3ULL, }, + { 0x1c71555555551c71ULL, 0x555555551c715555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71333333331c71ULL, 0x333333331c713333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaa38e38e38aaaaULL, 0x38e38e38aaaa38e3ULL, }, + { 0x555538e355555555ULL, 0x38e35555555538e3ULL, }, + { 0xcccc38e38e38ccccULL, 0x38e38e38cccc38e3ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71aaaa71c71c71ULL, 0xaaaa71c71c71aaaaULL, }, + { 0x1c71555555551c71ULL, 0x555555551c715555ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71333333331c71ULL, 0x333333331c713333ULL, }, + { 0x1c7138e371c71c71ULL, 0x38e371c71c7138e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886a006328625540ULL, 0x12f70b5e153f52fcULL, }, + { 0x886aaeaa28625540ULL, 0x27d80b5eab2b2514ULL, }, + { 0x704f164d28625540ULL, 0x4b670b5ea942b00cULL, }, + { 0x886a006328625540ULL, 0x12f70b5e153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5a00634d938b80ULL, 0x12f7bb1a153f2514ULL, }, + { 0x704f00634d93c708ULL, 0x12f788d8153f52fcULL, }, + { 0x886aaeaa28625540ULL, 0x27d80b5eab2b2514ULL, }, /* 72 */ + { 0xac5a00634d938b80ULL, 0x12f7bb1a153f2514ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e318b80ULL, 0x27d888d8a9422514ULL, }, + { 0x704f164d28625540ULL, 0x4b670b5ea942b00cULL, }, + { 0x704f00634d93c708ULL, 0x12f788d8153f52fcULL, }, + { 0x704f164d5e318b80ULL, 0x27d888d8a9422514ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c new file mode 100644 index 0000000000..8b20c05440 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-max-min/test_msa_min_u_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction MIN_U.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MIN_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaa8e38e38eULL, 0x38e38e38aaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaa1c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x38e38e3855555555ULL, }, + { 0x1c71c71c55555555ULL, 0x555555551c71c71cULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaa8e38e38eULL, 0x38e38e38aaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x38e38e3855555555ULL, }, + { 0xcccccccc8e38e38eULL, 0x38e38e38ccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaa1c71c71cULL, }, + { 0x1c71c71c55555555ULL, 0x555555551c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1c71c71c33333333ULL, 0x333333331c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0x38e38e381c71c71cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, }, + { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d28625540ULL, 0x4b670b5ea942e2a0ULL, }, + { 0x886ae6cc28625540ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaa4d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d4d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0x886ae6cc28625540ULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0xac5aaeaa4d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffa942e2a0ULL, }, + { 0x704f164d28625540ULL, 0x4b670b5ea942e2a0ULL, }, + { 0x704f164d4d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0x704f164d5e31e24eULL, 0x27d8c6ffa942e2a0ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MIN_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mul_q_h.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mul_q_h.c new file mode 100644 index 0000000000..f1526087fa --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mul_q_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MUL_Q.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MUL_Q.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e438e438e438e4ULL, 0x38e438e438e438e4ULL, }, + { 0xc71cc71cc71cc71cULL, 0xc71cc71cc71cc71cULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x12f6da134bdb12f6ULL, 0xda134bdb12f6da13ULL, }, + { 0xed0925edb425ed09ULL, 0x25edb425ed0925edULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71cc71cc71cc71cULL, 0xc71cc71cc71cc71cULL, }, + { 0x38e338e338e338e3ULL, 0x38e338e338e338e3ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x2221222122212221ULL, 0x2221222122212221ULL, }, + { 0xed0925ecb425ed09ULL, 0x25ecb425ed0925ecULL, }, + { 0x12f5da124bd912f5ULL, 0xda124bd912f5da12ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x147b147b147b147bULL, 0x147b147b147b147bULL, }, + { 0xeb84eb84eb84eb84ULL, 0xeb84eb84eb84eb84ULL, }, + { 0x0b60e93e2d830b60ULL, 0xe93e2d830b60e93eULL, }, + { 0xf49f16c1d27cf49fULL, 0x16c1d27cf49f16c1ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x2221222122212221ULL, 0x2221222122212221ULL, }, + { 0xeb84eb84eb84eb84ULL, 0xeb84eb84eb84eb84ULL, }, + { 0x147a147a147a147aULL, 0x147a147a147a147aULL, }, + { 0xf49f16c1d27cf49fULL, 0x16c1d27cf49f16c1ULL, }, + { 0x0b60e93e2d820b60ULL, 0xe93e2d820b60e93eULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x12f6da134bdb12f6ULL, 0xda134bdb12f6da13ULL, }, + { 0xed0925ecb425ed09ULL, 0x25ecb425ed0925ecULL, }, + { 0x0b60e93e2d830b60ULL, 0xe93e2d830b60e93eULL, }, + { 0xf49f16c1d27cf49fULL, 0x16c1d27cf49f16c1ULL, }, + { 0x0652194865240652ULL, 0x1948652406521948ULL, }, + { 0xf9ade6b79adcf9adULL, 0xe6b79adcf9ade6b7ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xed0925edb425ed09ULL, 0x25edb425ed0925edULL, }, + { 0x12f5da124bd912f5ULL, 0xda124bd912f5da12ULL, }, + { 0xf49f16c1d27cf49fULL, 0x16c1d27cf49f16c1ULL, }, + { 0x0b60e93e2d820b60ULL, 0xe93e2d820b60e93eULL, }, + { 0xf9ade6b79adcf9adULL, 0xe6b79adcf9ade6b7ULL, }, + { 0x0651194965220651ULL, 0x1949652206511949ULL, }, + { 0x6fb904f60cbd38c7ULL, 0x2c6b0102000431f1ULL, }, /* 64 */ + { 0x03faffec1879da0eULL, 0x0b2bf9e1ffbfcc2aULL, }, + { 0x4e261003e9dab268ULL, 0x1778faf00101e8d6ULL, }, + { 0x9712fb9b1db7ec38ULL, 0xbccff56b01071259ULL, }, + { 0x03faffec1879da0eULL, 0x0b2bf9e1ffbfcc2aULL, }, + { 0x002400002f03195aULL, 0x02cf2515038635ccULL, }, + { 0x02c8ffc1d57533d9ULL, 0x05e71eaef1eb1809ULL, }, + { 0xfc43001139150d37ULL, 0xef194023f19aecf4ULL, }, + { 0x4e261003e9dab268ULL, 0x1778faf00101e8d6ULL, }, /* 72 */ + { 0x02c8ffc1d57533d9ULL, 0x05e71eaef1eb1809ULL, }, + { 0x36aa33af267d6a08ULL, 0x0c67196238380abdULL, }, + { 0xb69bf1d4cc591b07ULL, 0xdc7e3510397df77dULL, }, + { 0x9712fb9b1db7ec38ULL, 0xbccff56b01071259ULL, }, + { 0xfc43001139150d37ULL, 0xef194023f19aecf4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MUL_Q_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MUL_Q_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mul_q_w.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mul_q_w.c new file mode 100644 index 0000000000..df815ee9da --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mul_q_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MUL_Q.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MUL_Q.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e38e3938e38e39ULL, 0x38e38e3938e38e39ULL, }, + { 0xc71c71c6c71c71c6ULL, 0xc71c71c6c71c71c6ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x12f684be4bda12f7ULL, 0xda12f68512f684beULL, }, + { 0xed097b42b425ed09ULL, 0x25ed097bed097b42ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c6c71c71c6ULL, 0xc71c71c6c71c71c6ULL, }, + { 0x38e38e3838e38e38ULL, 0x38e38e3838e38e38ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x2222222122222221ULL, 0x2222222122222221ULL, }, + { 0xed097b42b425ed09ULL, 0x25ed097aed097b42ULL, }, + { 0x12f684bd4bda12f5ULL, 0xda12f68412f684bdULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x147ae148147ae148ULL, 0x147ae148147ae148ULL, }, + { 0xeb851eb8eb851eb8ULL, 0xeb851eb8eb851eb8ULL, }, + { 0x0b60b60b2d82d82eULL, 0xe93e93e90b60b60bULL, }, + { 0xf49f49f4d27d27d2ULL, 0x16c16c17f49f49f4ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x2222222122222221ULL, 0x2222222122222221ULL, }, + { 0xeb851eb8eb851eb8ULL, 0xeb851eb8eb851eb8ULL, }, + { 0x147ae147147ae147ULL, 0x147ae147147ae147ULL, }, + { 0xf49f49f4d27d27d2ULL, 0x16c16c16f49f49f4ULL, }, + { 0x0b60b60b2d82d82dULL, 0xe93e93e90b60b60bULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x12f684be4bda12f7ULL, 0xda12f68512f684beULL, }, + { 0xed097b42b425ed09ULL, 0x25ed097aed097b42ULL, }, + { 0x0b60b60b2d82d82eULL, 0xe93e93e90b60b60bULL, }, + { 0xf49f49f4d27d27d2ULL, 0x16c16c16f49f49f4ULL, }, + { 0x06522c3f6522c3f3ULL, 0x1948b0fc06522c3fULL, }, + { 0xf9add3c09add3c0dULL, 0xe6b74f03f9add3c0ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xed097b42b425ed09ULL, 0x25ed097bed097b42ULL, }, + { 0x12f684bd4bda12f5ULL, 0xda12f68412f684bdULL, }, + { 0xf49f49f4d27d27d2ULL, 0x16c16c17f49f49f4ULL, }, + { 0x0b60b60b2d82d82dULL, 0xe93e93e90b60b60bULL, }, + { 0xf9add3c09add3c0dULL, 0xe6b74f03f9add3c0ULL, }, + { 0x06522c3f6522c3f1ULL, 0x1948b0fc06522c3fULL, }, + { 0x6fb7e8890cbdc0d2ULL, 0x2c6b144600049a04ULL, }, /* 64 */ + { 0x03fa514e1879c701ULL, 0x0b2c6ca9ffbf8ac6ULL, }, + { 0x4e252086e9daefbfULL, 0x1779189301015a34ULL, }, + { 0x9713a7171db7f3a5ULL, 0xbccfb4690107236fULL, }, + { 0x03fa514e1879c701ULL, 0x0b2c6ca9ffbf8ac6ULL, }, + { 0x002442012f047611ULL, 0x02cf8c140386e68eULL, }, + { 0x02c84b87d575d121ULL, 0x05e79a8af1eb1c52ULL, }, + { 0xfc439edc3916c1e4ULL, 0xef19389cf19a0fddULL, }, + { 0x4e252086e9daefbfULL, 0x1779189301015a34ULL, }, /* 72 */ + { 0x02c84b87d575d121ULL, 0x05e79a8af1eb1c52ULL, }, + { 0x36a93aff267d11c3ULL, 0x0c6788643838c14cULL, }, + { 0xb69baa39cc590fcdULL, 0xdc7e6df7397c58d9ULL, }, + { 0x9713a7171db7f3a5ULL, 0xbccfb4690107236fULL, }, + { 0xfc439edc3916c1e4ULL, 0xef19389cf19a0fddULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MUL_Q_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MUL_Q_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulr_q_h.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulr_q_h.c new file mode 100644 index 0000000000..fd0a5fa7a8 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulr_q_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MULR_Q.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MULR_Q.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000010000ULL, 0x0000000100000000ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e438e438e438e4ULL, 0x38e438e438e438e4ULL, }, + { 0xc71cc71cc71cc71cULL, 0xc71cc71cc71cc71cULL, }, + { 0x2223222322232223ULL, 0x2223222322232223ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x12f7da134bdb12f7ULL, 0xda134bdb12f7da13ULL, }, + { 0xed0a25eeb425ed0aULL, 0x25eeb425ed0a25eeULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71cc71cc71cc71cULL, 0xc71cc71cc71cc71cULL, }, + { 0x38e338e338e338e3ULL, 0x38e338e338e338e3ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xed0925edb426ed09ULL, 0x25edb426ed0925edULL, }, + { 0x12f6da134bda12f6ULL, 0xda134bda12f6da13ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2223222322232223ULL, 0x2223222322232223ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x147c147c147c147cULL, 0x147c147c147c147cULL, }, + { 0xeb85eb85eb85eb85ULL, 0xeb85eb85eb85eb85ULL, }, + { 0x0b61e93e2d840b61ULL, 0xe93e2d840b61e93eULL, }, + { 0xf49f16c2d27cf49fULL, 0x16c2d27cf49f16c2ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xeb85eb85eb85eb85ULL, 0xeb85eb85eb85eb85ULL, }, + { 0x147b147b147b147bULL, 0x147b147b147b147bULL, }, + { 0xf49f16c1d27df49fULL, 0x16c1d27df49f16c1ULL, }, + { 0x0b60e93e2d830b60ULL, 0xe93e2d830b60e93eULL, }, + { 0x0000000000010000ULL, 0x0000000100000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x12f7da134bdb12f7ULL, 0xda134bdb12f7da13ULL, }, + { 0xed0925edb426ed09ULL, 0x25edb426ed0925edULL, }, + { 0x0b61e93e2d840b61ULL, 0xe93e2d840b61e93eULL, }, + { 0xf49f16c1d27df49fULL, 0x16c1d27df49f16c1ULL, }, + { 0x0652194865240652ULL, 0x1948652406521948ULL, }, + { 0xf9aee6b79addf9aeULL, 0xe6b79addf9aee6b7ULL, }, + { 0x00000000ffff0000ULL, 0x0000ffff00000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xed0a25eeb425ed0aULL, 0x25eeb425ed0a25eeULL, }, + { 0x12f6da134bda12f6ULL, 0xda134bda12f6da13ULL, }, + { 0xf49f16c2d27cf49fULL, 0x16c2d27cf49f16c2ULL, }, + { 0x0b60e93e2d830b60ULL, 0xe93e2d830b60e93eULL, }, + { 0xf9aee6b79addf9aeULL, 0xe6b79addf9aee6b7ULL, }, + { 0x0652194965230652ULL, 0x1949652306521949ULL, }, + { 0x6fba04f60cbe38c7ULL, 0x2c6b0102000531f1ULL, }, /* 64 */ + { 0x03faffed1879da0fULL, 0x0b2cf9e2ffbfcc2aULL, }, + { 0x4e261004e9dbb269ULL, 0x1779faf00102e8d7ULL, }, + { 0x9713fb9c1db7ec39ULL, 0xbccff56b01081259ULL, }, + { 0x03faffed1879da0fULL, 0x0b2cf9e2ffbfcc2aULL, }, + { 0x002400002f04195bULL, 0x02cf2516038735cdULL, }, + { 0x02c8ffc1d57633daULL, 0x05e71eaff1eb180aULL, }, + { 0xfc44001139160d37ULL, 0xef1a4023f19aecf5ULL, }, + { 0x4e261004e9dbb269ULL, 0x1779faf00102e8d7ULL, }, /* 72 */ + { 0x02c8ffc1d57633daULL, 0x05e71eaff1eb180aULL, }, + { 0x36aa33af267e6a09ULL, 0x0c67196338390abeULL, }, + { 0xb69bf1d4cc591b07ULL, 0xdc7f3511397df77eULL, }, + { 0x9713fb9c1db7ec39ULL, 0xbccff56b01081259ULL, }, + { 0xfc44001139160d37ULL, 0xef1a4023f19aecf5ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MULR_Q_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MULR_Q_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulr_q_w.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulr_q_w.c new file mode 100644 index 0000000000..f28b0d0a20 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulr_q_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MULR_Q.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MULR_Q.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e38e3a38e38e3aULL, 0x38e38e3a38e38e3aULL, }, + { 0xc71c71c7c71c71c7ULL, 0xc71c71c7c71c71c7ULL, }, + { 0x2222222322222223ULL, 0x2222222322222223ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x12f684be4bda12f7ULL, 0xda12f68512f684beULL, }, + { 0xed097b43b425ed09ULL, 0x25ed097ced097b43ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c7c71c71c7ULL, 0xc71c71c7c71c71c7ULL, }, + { 0x38e38e3838e38e38ULL, 0x38e38e3838e38e38ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xed097b42b425ed0aULL, 0x25ed097bed097b42ULL, }, + { 0x12f684bd4bda12f6ULL, 0xda12f68512f684bdULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222322222223ULL, 0x2222222322222223ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x147ae148147ae148ULL, 0x147ae148147ae148ULL, }, + { 0xeb851eb8eb851eb8ULL, 0xeb851eb8eb851eb8ULL, }, + { 0x0b60b60c2d82d82eULL, 0xe93e93e90b60b60cULL, }, + { 0xf49f49f5d27d27d2ULL, 0x16c16c17f49f49f5ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xeb851eb8eb851eb8ULL, 0xeb851eb8eb851eb8ULL, }, + { 0x147ae148147ae148ULL, 0x147ae148147ae148ULL, }, + { 0xf49f49f4d27d27d3ULL, 0x16c16c16f49f49f4ULL, }, + { 0x0b60b60b2d82d82dULL, 0xe93e93e90b60b60bULL, }, + { 0x0000000000000001ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x12f684be4bda12f7ULL, 0xda12f68512f684beULL, }, + { 0xed097b42b425ed0aULL, 0x25ed097bed097b42ULL, }, + { 0x0b60b60c2d82d82eULL, 0xe93e93e90b60b60cULL, }, + { 0xf49f49f4d27d27d3ULL, 0x16c16c16f49f49f4ULL, }, + { 0x06522c3f6522c3f4ULL, 0x1948b0fc06522c3fULL, }, + { 0xf9add3c19add3c0dULL, 0xe6b74f04f9add3c1ULL, }, + { 0x00000000ffffffffULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xed097b43b425ed09ULL, 0x25ed097ced097b43ULL, }, + { 0x12f684bd4bda12f6ULL, 0xda12f68512f684bdULL, }, + { 0xf49f49f5d27d27d2ULL, 0x16c16c17f49f49f5ULL, }, + { 0x0b60b60b2d82d82dULL, 0xe93e93e90b60b60bULL, }, + { 0xf9add3c19add3c0dULL, 0xe6b74f04f9add3c1ULL, }, + { 0x06522c3f6522c3f2ULL, 0x1948b0fd06522c3fULL, }, + { 0x6fb7e8890cbdc0d3ULL, 0x2c6b144600049a05ULL, }, /* 64 */ + { 0x03fa514e1879c702ULL, 0x0b2c6ca9ffbf8ac7ULL, }, + { 0x4e252087e9daefc0ULL, 0x1779189301015a35ULL, }, + { 0x9713a7171db7f3a6ULL, 0xbccfb46a0107236fULL, }, + { 0x03fa514e1879c702ULL, 0x0b2c6ca9ffbf8ac7ULL, }, + { 0x002442012f047612ULL, 0x02cf8c140386e68fULL, }, + { 0x02c84b88d575d121ULL, 0x05e79a8bf1eb1c52ULL, }, + { 0xfc439edd3916c1e4ULL, 0xef19389cf19a0fdeULL, }, + { 0x4e252087e9daefc0ULL, 0x1779189301015a35ULL, }, /* 72 */ + { 0x02c84b88d575d121ULL, 0x05e79a8bf1eb1c52ULL, }, + { 0x36a93aff267d11c4ULL, 0x0c6788643838c14cULL, }, + { 0xb69baa3acc590fcdULL, 0xdc7e6df7397c58daULL, }, + { 0x9713a7171db7f3a6ULL, 0xbccfb46a0107236fULL, }, + { 0xfc439edd3916c1e4ULL, 0xef19389cf19a0fdeULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MULR_Q_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MULR_Q_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_b.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_b.c new file mode 100644 index 0000000000..6beeda906d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MULV.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MULV.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe4e4e4e4e4e4e4e4ULL, 0xe4e4e4e4e4e4e4e4ULL, }, + { 0x7272727272727272ULL, 0x7272727272727272ULL, }, + { 0x7878787878787878ULL, 0x7878787878787878ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0xbe4c30be4c30be4cULL, 0x30be4c30be4c30beULL, }, + { 0x980a26980a26980aULL, 0x26980a26980a2698ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7272727272727272ULL, 0x7272727272727272ULL, }, + { 0x3939393939393939ULL, 0x3939393939393939ULL, }, + { 0xbcbcbcbcbcbcbcbcULL, 0xbcbcbcbcbcbcbcbcULL, }, + { 0xefefefefefefefefULL, 0xefefefefefefefefULL, }, + { 0x5f26985f26985f26ULL, 0x985f26985f26985fULL, }, + { 0x4c85134c85134c85ULL, 0x134c85134c85134cULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7878787878787878ULL, 0x7878787878787878ULL, }, + { 0xbcbcbcbcbcbcbcbcULL, 0xbcbcbcbcbcbcbcbcULL, }, + { 0x9090909090909090ULL, 0x9090909090909090ULL, }, + { 0xa4a4a4a4a4a4a4a4ULL, 0xa4a4a4a4a4a4a4a4ULL, }, + { 0xe428a0e428a0e428ULL, 0xa0e428a0e428a0e4ULL, }, + { 0x500c94500c94500cULL, 0x94500c94500c9450ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0xefefefefefefefefULL, 0xefefefefefefefefULL, }, + { 0xa4a4a4a4a4a4a4a4ULL, 0xa4a4a4a4a4a4a4a4ULL, }, + { 0x2929292929292929ULL, 0x2929292929292929ULL, }, + { 0x394a28394a28394aULL, 0x28394a28394a2839ULL, }, + { 0x9483a59483a59483ULL, 0xa59483a59483a594ULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xbe4c30be4c30be4cULL, 0x30be4c30be4c30beULL, }, + { 0x5f26985f26985f26ULL, 0x985f26985f26985fULL, }, + { 0xe428a0e428a0e428ULL, 0xa0e428a0e428a0e4ULL, }, + { 0x394a28394a28394aULL, 0x28394a28394a2839ULL, }, + { 0x49c44049c44049c4ULL, 0x4049c44049c44049ULL, }, + { 0xd4ae88d4ae88d4aeULL, 0x88d4ae88d4ae88d4ULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x980a26980a26980aULL, 0x26980a26980a2698ULL, }, + { 0x4c85134c85134c85ULL, 0x134c85134c85134cULL, }, + { 0x500c94500c94500cULL, 0x94500c94500c9450ULL, }, + { 0x9483a59483a59483ULL, 0xa59483a59483a594ULL, }, + { 0xd4ae88d4ae88d4aeULL, 0x88d4ae88d4ae88d4ULL, }, + { 0x10e1b110e1b110e1ULL, 0xb110e1b110e1b110ULL, }, + { 0x40e4a49040843900ULL, 0xf971798404190090ULL, }, /* 64 */ + { 0x58ac00e408461300ULL, 0x4661098cd64560d0ULL, }, + { 0x60445478e83e2700ULL, 0x6de882a2aaa970f0ULL, }, + { 0x80b6c45cb0c20a80ULL, 0x4ff7d850aeb66080ULL, }, + { 0x58ac00e408461300ULL, 0x4661098cd64560d0ULL, }, + { 0x190400492969b140ULL, 0x445199a4b9814410ULL, }, + { 0xa4cc00bea5dd0d00ULL, 0xbe68a2e60795dab0ULL, }, + { 0xd0a200c74623ae70ULL, 0xea8758f0dd3e6480ULL, }, + { 0x60445478e83e2700ULL, 0x6de882a2aaa970f0ULL, }, /* 72 */ + { 0xa4cc00bea5dd0d00ULL, 0xbe68a2e60795dab0ULL, }, + { 0x90a444e4b1617900ULL, 0xf140240139395990ULL, }, + { 0x40c6f422ee9fb600ULL, 0x7b583028e316aa80ULL, }, + { 0x80b6c45cb0c20a80ULL, 0x4ff7d850aeb66080ULL, }, + { 0xd0a200c74623ae70ULL, 0xea8758f0dd3e6480ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_d.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_d.c new file mode 100644 index 0000000000..3205d4b378 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MULV.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MULV.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e38e38e38e38e4ULL, 0x38e38e38e38e38e4ULL, }, + { 0x1c71c71c71c71c72ULL, 0x1c71c71c71c71c72ULL, }, + { 0x7777777777777778ULL, 0x7777777777777778ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x12f684bda12f684cULL, 0x2f684bda12f684beULL, }, + { 0x425ed097b425ed0aULL, 0x25ed097b425ed098ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c72ULL, 0x1c71c71c71c71c72ULL, }, + { 0x8e38e38e38e38e39ULL, 0x8e38e38e38e38e39ULL, }, + { 0xbbbbbbbbbbbbbbbcULL, 0xbbbbbbbbbbbbbbbcULL, }, + { 0xeeeeeeeeeeeeeeefULL, 0xeeeeeeeeeeeeeeefULL, }, + { 0x097b425ed097b426ULL, 0x97b425ed097b425fULL, }, + { 0xa12f684bda12f685ULL, 0x12f684bda12f684cULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7777777777777778ULL, 0x7777777777777778ULL, }, + { 0xbbbbbbbbbbbbbbbcULL, 0xbbbbbbbbbbbbbbbcULL, }, + { 0xf5c28f5c28f5c290ULL, 0xf5c28f5c28f5c290ULL, }, + { 0x3d70a3d70a3d70a4ULL, 0x3d70a3d70a3d70a4ULL, }, + { 0x7d27d27d27d27d28ULL, 0x38e38e38e38e38e4ULL, }, + { 0xb60b60b60b60b60cULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0xeeeeeeeeeeeeeeefULL, 0xeeeeeeeeeeeeeeefULL, }, + { 0x3d70a3d70a3d70a4ULL, 0x3d70a3d70a3d70a4ULL, }, + { 0x8f5c28f5c28f5c29ULL, 0x8f5c28f5c28f5c29ULL, }, + { 0x9f49f49f49f49f4aULL, 0x8e38e38e38e38e39ULL, }, + { 0x2d82d82d82d82d83ULL, 0x3e93e93e93e93e94ULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x12f684bda12f684cULL, 0x2f684bda12f684beULL, }, + { 0x097b425ed097b426ULL, 0x97b425ed097b425fULL, }, + { 0x7d27d27d27d27d28ULL, 0x38e38e38e38e38e4ULL, }, + { 0x9f49f49f49f49f4aULL, 0x8e38e38e38e38e39ULL, }, + { 0xb0fcd6e9e06522c4ULL, 0x522c3f35ba781949ULL, }, + { 0x6b74f0329161f9aeULL, 0x74f0329161f9add4ULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x425ed097b425ed0aULL, 0x25ed097b425ed098ULL, }, + { 0xa12f684bda12f685ULL, 0x12f684bda12f684cULL, }, + { 0xb60b60b60b60b60cULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0x2d82d82d82d82d83ULL, 0x3e93e93e93e93e94ULL, }, + { 0x6b74f0329161f9aeULL, 0x74f0329161f9add4ULL, }, + { 0x781948b0fcd6e9e1ULL, 0xc3f35ba781948b10ULL, }, + { 0xad45be6961639000ULL, 0x3297fdea74988090ULL, }, /* 64 */ + { 0xefa7a5a0e7176a00ULL, 0xb8110a1f6f1923d0ULL, }, + { 0x08c6139fc4346000ULL, 0xab209f86581f7cf0ULL, }, + { 0xfbe1883aee787980ULL, 0x821d25438dd09f80ULL, }, + { 0xefa7a5a0e7176a00ULL, 0xb8110a1f6f1923d0ULL, }, + { 0x37ae2b38fded7040ULL, 0x682476774aee6810ULL, }, + { 0x6acb3d68be6cdc00ULL, 0xafdad2311444e7b0ULL, }, + { 0xedbf72842143b470ULL, 0x7f8223caefce5580ULL, }, + { 0x08c6139fc4346000ULL, 0xab209f86581f7cf0ULL, }, /* 72 */ + { 0x6acb3d68be6cdc00ULL, 0xafdad2311444e7b0ULL, }, + { 0x8624e5e1e5044000ULL, 0xd98178a63216c990ULL, }, + { 0x76a5ab8089e38100ULL, 0xa1019a60d4dad480ULL, }, + { 0xfbe1883aee787980ULL, 0x821d25438dd09f80ULL, }, + { 0xedbf72842143b470ULL, 0x7f8223caefce5580ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_h.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_h.c new file mode 100644 index 0000000000..e7bd985ae1 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MULV.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MULV.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x38e438e438e438e4ULL, 0x38e438e438e438e4ULL, }, + { 0x1c721c721c721c72ULL, 0x1c721c721c721c72ULL, }, + { 0x7778777877787778ULL, 0x7778777877787778ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x684c84bea130684cULL, 0x84bea130684c84beULL, }, + { 0xed0ad098b426ed0aULL, 0xd098b426ed0ad098ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c721c721c721c72ULL, 0x1c721c721c721c72ULL, }, + { 0x8e398e398e398e39ULL, 0x8e398e398e398e39ULL, }, + { 0xbbbcbbbcbbbcbbbcULL, 0xbbbcbbbcbbbcbbbcULL, }, + { 0xeeefeeefeeefeeefULL, 0xeeefeeefeeefeeefULL, }, + { 0xb426425fd098b426ULL, 0x425fd098b426425fULL, }, + { 0xf685684cda13f685ULL, 0x684cda13f685684cULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7778777877787778ULL, 0x7778777877787778ULL, }, + { 0xbbbcbbbcbbbcbbbcULL, 0xbbbcbbbcbbbcbbbcULL, }, + { 0xc290c290c290c290ULL, 0xc290c290c290c290ULL, }, + { 0x70a470a470a470a4ULL, 0x70a470a470a470a4ULL, }, + { 0x7d2838e4f4a07d28ULL, 0x38e4f4a07d2838e4ULL, }, + { 0xb60cfa503e94b60cULL, 0xfa503e94b60cfa50ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0xeeefeeefeeefeeefULL, 0xeeefeeefeeefeeefULL, }, + { 0x70a470a470a470a4ULL, 0x70a470a470a470a4ULL, }, + { 0x5c295c295c295c29ULL, 0x5c295c295c295c29ULL, }, + { 0x9f4a8e397d289f4aULL, 0x8e397d289f4a8e39ULL, }, + { 0x2d833e944fa52d83ULL, 0x3e944fa52d833e94ULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x684c84bea130684cULL, 0x84bea130684c84beULL, }, + { 0xb426425fd098b426ULL, 0x425fd098b426425fULL, }, + { 0x7d2838e4f4a07d28ULL, 0x38e4f4a07d2838e4ULL, }, + { 0x9f4a8e397d289f4aULL, 0x8e397d289f4a8e39ULL, }, + { 0x22c419492c4022c4ULL, 0x19492c4022c41949ULL, }, + { 0xf9aeadd44588f9aeULL, 0xadd44588f9aeadd4ULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xed0ad098b426ed0aULL, 0xd098b426ed0ad098ULL, }, + { 0xf685684cda13f685ULL, 0x684cda13f685684cULL, }, + { 0xb60cfa503e94b60cULL, 0xfa503e94b60cfa50ULL, }, + { 0x2d833e944fa52d83ULL, 0x3e944fa52d833e94ULL, }, + { 0xf9aeadd44588f9aeULL, 0xadd44588f9aeadd4ULL, }, + { 0xe9e18b1048b1e9e1ULL, 0x8b1048b1e9e18b10ULL, }, + { 0xcbe43290c5849000ULL, 0x837136844f198090ULL, }, /* 64 */ + { 0x2cac40e4aa466a00ULL, 0xfe61d18cb74523d0ULL, }, + { 0x2d44eb78793e6000ULL, 0x4fe806a2e7a97cf0ULL, }, + { 0x78b6f35cb6c27980ULL, 0xb6f78750ceb69f80ULL, }, + { 0x2cac40e4aa466a00ULL, 0xfe61d18cb74523d0ULL, }, + { 0x21042649c2697040ULL, 0xaa51fea465816810ULL, }, + { 0x28cc8bbef4dddc00ULL, 0xa1687ae6a695e7b0ULL, }, + { 0xcfa29fc7d323b470ULL, 0xe587adf0113e5580ULL, }, + { 0x2d44eb78793e6000ULL, 0x4fe806a2e7a97cf0ULL, }, /* 72 */ + { 0x28cc8bbef4dddc00ULL, 0xa1687ae6a695e7b0ULL, }, + { 0x0fa488e4d5614000ULL, 0x864072017939c990ULL, }, + { 0x8fc62522929f8100ULL, 0x7a585f288416d480ULL, }, + { 0x78b6f35cb6c27980ULL, 0xb6f78750ceb69f80ULL, }, + { 0xcfa29fc7d323b470ULL, 0xe587adf0113e5580ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_w.c b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_w.c new file mode 100644 index 0000000000..9c318b3fbb --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-multiply/test_msa_mulv_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction MULV.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "MULV.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e4e38e38e4ULL, 0xe38e38e4e38e38e4ULL, }, + { 0x71c71c7271c71c72ULL, 0x71c71c7271c71c72ULL, }, + { 0x7777777877777778ULL, 0x7777777877777778ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x12f684bea12f684cULL, 0x84bda13012f684beULL, }, + { 0x425ed098b425ed0aULL, 0xd097b426425ed098ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x71c71c7271c71c72ULL, 0x71c71c7271c71c72ULL, }, + { 0x38e38e3938e38e39ULL, 0x38e38e3938e38e39ULL, }, + { 0xbbbbbbbcbbbbbbbcULL, 0xbbbbbbbcbbbbbbbcULL, }, + { 0xeeeeeeefeeeeeeefULL, 0xeeeeeeefeeeeeeefULL, }, + { 0x097b425fd097b426ULL, 0x425ed098097b425fULL, }, + { 0xa12f684cda12f685ULL, 0x684bda13a12f684cULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7777777877777778ULL, 0x7777777877777778ULL, }, + { 0xbbbbbbbcbbbbbbbcULL, 0xbbbbbbbcbbbbbbbcULL, }, + { 0x28f5c29028f5c290ULL, 0x28f5c29028f5c290ULL, }, + { 0x0a3d70a40a3d70a4ULL, 0x0a3d70a40a3d70a4ULL, }, + { 0xe38e38e427d27d28ULL, 0x9f49f4a0e38e38e4ULL, }, + { 0x4fa4fa500b60b60cULL, 0x93e93e944fa4fa50ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0xeeeeeeefeeeeeeefULL, 0xeeeeeeefeeeeeeefULL, }, + { 0x0a3d70a40a3d70a4ULL, 0x0a3d70a40a3d70a4ULL, }, + { 0xc28f5c29c28f5c29ULL, 0xc28f5c29c28f5c29ULL, }, + { 0x38e38e3949f49f4aULL, 0x27d27d2838e38e39ULL, }, + { 0x93e93e9482d82d83ULL, 0xa4fa4fa593e93e94ULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x12f684bea12f684cULL, 0x84bda13012f684beULL, }, + { 0x097b425fd097b426ULL, 0x425ed098097b425fULL, }, + { 0xe38e38e427d27d28ULL, 0x9f49f4a0e38e38e4ULL, }, + { 0x38e38e3949f49f4aULL, 0x27d27d2838e38e39ULL, }, + { 0xba781949e06522c4ULL, 0x06522c40ba781949ULL, }, + { 0x61f9add49161f9aeULL, 0xc0ca458861f9add4ULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x425ed098b425ed0aULL, 0xd097b426425ed098ULL, }, + { 0xa12f684cda12f685ULL, 0x684bda13a12f684cULL, }, + { 0x4fa4fa500b60b60cULL, 0x93e93e944fa4fa50ULL, }, + { 0x93e93e9482d82d83ULL, 0xa4fa4fa593e93e94ULL, }, + { 0x61f9add49161f9aeULL, 0xc0ca458861f9add4ULL, }, + { 0x81948b10fcd6e9e1ULL, 0x781948b181948b10ULL, }, + { 0xb103329061639000ULL, 0x3a25368474988090ULL, }, /* 64 */ + { 0x10bf40e4e7176a00ULL, 0x8176d18c6f1923d0ULL, }, + { 0x7393eb78c4346000ULL, 0xb7bf06a2581f7cf0ULL, }, + { 0xb0f0f35cee787980ULL, 0xd67987508dd09f80ULL, }, + { 0x10bf40e4e7176a00ULL, 0x8176d18c6f1923d0ULL, }, + { 0xb4f42649fded7040ULL, 0x3ceafea44aee6810ULL, }, + { 0xf73d8bbebe6cdc00ULL, 0x53697ae61444e7b0ULL, }, + { 0x7abb9fc72143b470ULL, 0x11e5adf0efce5580ULL, }, + { 0x7393eb78c4346000ULL, 0xb7bf06a2581f7cf0ULL, }, /* 72 */ + { 0xf73d8bbebe6cdc00ULL, 0x53697ae61444e7b0ULL, }, + { 0xb6b388e4e5044000ULL, 0x1aff72013216c990ULL, }, + { 0xe8bf252289e38100ULL, 0x91ae5f28d4dad480ULL, }, + { 0xb0f0f35cee787980ULL, 0xd67987508dd09f80ULL, }, + { 0x7abb9fc72143b470ULL, 0x11e5adf0efce5580ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_MULV_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_b.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_b.c new file mode 100644 index 0000000000..04e6159fc7 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_S.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xc71c80c71c80c71cULL, 0x80c71c80c71c80c7ULL, }, + { 0x8e80e38e80e38e80ULL, 0xe38e80e38e80e38eULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x727f1d727f1d727fULL, 0x1d727f1d727f1d72ULL, }, + { 0x39e47f39e47f39e4ULL, 0x7f39e47f39e47f39ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e94e93e94e93eULL, 0x94e93e94e93e94e9ULL, }, + { 0xb08005b08005b080ULL, 0x05b08005b08005b0ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x507ffb507ffb507fULL, 0xfb507ffb507ffb50ULL, }, + { 0x17c26c17c26c17c2ULL, 0x6c17c26c17c26c17ULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x39e47f39e47f39e4ULL, 0x7f39e47f39e47f39ULL, }, + { 0x8e80e38e80e38e80ULL, 0xe38e80e38e80e38eULL, }, + { 0x17c26c17c26c17c2ULL, 0x6c17c26c17c26c17ULL, }, + { 0xb08005b08005b080ULL, 0x05b08005b08005b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc78071c78071c780ULL, 0x71c78071c78071c7ULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x727f1d727f1d727fULL, 0x1d727f1d727f1d72ULL, }, + { 0xc71c80c71c80c71cULL, 0x80c71c80c71c80c7ULL, }, + { 0x507ffb507ffb507fULL, 0xfb507ffb507ffb50ULL, }, + { 0xe93e94e93e94e93eULL, 0x94e93e94e93e94e9ULL, }, + { 0x397f8f397f8f397fULL, 0x8f397f8f397f8f39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8d7fe680db7f7f38ULL, 0x39705044e93c8010ULL, }, + { 0xdc1038226f7f7f7fULL, 0x247f455f53508bf8ULL, }, + { 0x801bd080ca3173f2ULL, 0x7f767f7f5539ce6cULL, }, + { 0x73801a7f258080c8ULL, 0xc790b0bc17c47ff0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f80527f7fc43c7fULL, 0xeb1ff51b6a142de8ULL, }, + { 0x8b80ea16ef80e5baULL, 0x7f0633426cfd705cULL, }, + { 0x24f0c8de91808080ULL, 0xdc80bba1adb07508ULL, }, /* 72 */ + { 0xb17fae80803cc480ULL, 0x15e10be596ecd318ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x800b9880809ea980ULL, 0x7fe73e2702e94374ULL, }, + { 0x7fe5307f36cf8d0eULL, 0x808a8080abc73294ULL, }, + { 0x757f16ea117f1b46ULL, 0x80facdbe940390a4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_d.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_d.c new file mode 100644 index 0000000000..195137f41f --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_S.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x8000000000000000ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0x38e38e38e38e38e4ULL, 0x7fffffffffffffffULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e93e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0x16c16c16c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e4ULL, 0x7fffffffffffffffULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x16c16c16c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71dULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0x8000000000000000ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e93e93e93e9ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace668dace8e38ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038216e92c9c0ULL, 0x238e445f53508af8ULL, }, + { 0x8000000000000000ULL, 0x7fffffffffffffffULL, }, + { 0x73531997253171c8ULL, 0xc790afbb16c3a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b893c43b88ULL, 0xeb1ef41a6a142de8ULL, }, + { 0x8b6eea15ef61e4baULL, 0x7fffffffffffffffULL, }, + { 0x23efc7de916d3640ULL, 0xdc71bba0acaf7508ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000000000000ULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x8000000000000000ULL, }, + { 0x749115ea109e1b46ULL, 0x8000000000000000ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_h.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_h.c new file mode 100644 index 0000000000..c57238d31a --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_S.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xc71c80001c72c71cULL, 0x80001c72c71c8000ULL, }, + { 0x8e39e38e80008e39ULL, 0xe38e80008e39e38eULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c727fff71c7ULL, 0x1c727fff71c71c72ULL, }, + { 0x38e47fffe38e38e4ULL, 0x7fffe38e38e47fffULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e94e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0xb05b05b08000b05bULL, 0x05b08000b05b05b0ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa5fa507fff4fa5ULL, 0xfa507fff4fa5fa50ULL, }, + { 0x16c26c17c16c16c2ULL, 0x6c17c16c16c26c17ULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e47fffe38e38e4ULL, 0x7fffe38e38e47fffULL, }, + { 0x8e39e38e80008e39ULL, 0xe38e80008e39e38eULL, }, + { 0x16c26c17c16c16c2ULL, 0x6c17c16c16c26c17ULL, }, + { 0xb05b05b08000b05bULL, 0x05b08000b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71d71c78000c71dULL, 0x71c78000c71d71c7ULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c727fff71c7ULL, 0x1c727fff71c71c72ULL, }, + { 0xc71c80001c72c71cULL, 0x80001c72c71c8000ULL, }, + { 0x4fa5fa507fff4fa5ULL, 0xfa507fff4fa5fa50ULL, }, + { 0xe93e93e93e94e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0x38e38e397fff38e3ULL, 0x8e397fff38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace669dacf7fffULL, 0x38705044e93c8000ULL, }, + { 0xdc1038226e937fffULL, 0x238f445f53508af8ULL, }, + { 0x8000d07fca3172f2ULL, 0x7fff7fff5539cd6cULL, }, + { 0x7354199725318000ULL, 0xc790afbc16c47fffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6451b97fff3b88ULL, 0xeb1ff41b6a142de8ULL, }, + { 0x8b6fea16ef62e4baULL, 0x7fff32426bfd705cULL, }, + { 0x23f0c7de916d8000ULL, 0xdc71bba1acb07508ULL, }, /* 72 */ + { 0xb09cae478000c478ULL, 0x14e10be595ecd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000985d8000a932ULL, 0x7fff3e2701e94274ULL, }, + { 0x7fff2f8135cf8d0eULL, 0x80008000aac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x8000cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_w.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_w.c new file mode 100644 index 0000000000..1cded65a7e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_S.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x80000000c71c71c7ULL, }, + { 0x8e38e38e80000000ULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c727fffffffULL, 0x1c71c71d71c71c72ULL, }, + { 0x38e38e39e38e38e4ULL, 0x7fffffff38e38e39ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0xb05b05b080000000ULL, 0x05b05b05b05b05b0ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa4fa507fffffffULL, 0xfa4fa4fb4fa4fa50ULL, }, + { 0x16c16c17c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e39e38e38e4ULL, 0x7fffffff38e38e39ULL, }, + { 0x8e38e38e80000000ULL, 0xe38e38e38e38e38eULL, }, + { 0x16c16c17c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xb05b05b080000000ULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c780000000ULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c727fffffffULL, 0x1c71c71d71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0x80000000c71c71c7ULL, }, + { 0x4fa4fa507fffffffULL, 0xfa4fa4fb4fa4fa50ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0x38e38e397fffffffULL, 0x8e38e38f38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace669dace8e38ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038226e92c9c0ULL, 0x238e445f53508af8ULL, }, + { 0x80000000ca3072f2ULL, 0x7fffffff5538cd6cULL, }, + { 0x73531997253171c8ULL, 0xc790afbc16c3a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b97fffffffULL, 0xeb1ef41b6a142de8ULL, }, + { 0x8b6eea16ef61e4baULL, 0x7fffffff6bfc705cULL, }, + { 0x23efc7de916d3640ULL, 0xdc71bba1acaf7508ULL, }, /* 72 */ + { 0xb09cae4780000000ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000080000000ULL, 0x7fffffff01e84274ULL, }, + { 0x7fffffff35cf8d0eULL, 0x80000000aac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x8000000094038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_b.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_b.c new file mode 100644 index 0000000000..cb38f033a6 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_U.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x001c72001c72001cULL, 0x72001c72001c7200ULL, }, + { 0x8e39008e39008e39ULL, 0x008e39008e39008eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x00001d00001d0000ULL, 0x1d00001d00001d00ULL, }, + { 0x3900003900003900ULL, 0x0039000039000039ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x003e94003e94003eULL, 0x94003e94003e9400ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1700001700001700ULL, 0x0017000017000017ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x3900003900003900ULL, 0x0039000039000039ULL, }, + { 0x8e39008e39008e39ULL, 0x008e39008e39008eULL, }, + { 0x1700001700001700ULL, 0x0017000017000017ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71d00c71d00c71dULL, 0x00c71d00c71d00c7ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x00001d00001d0000ULL, 0x1d00001d00001d00ULL, }, + { 0x001c72001c72001cULL, 0x72001c72001c7200ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x003e94003e94003eULL, 0x94003e94003e9400ULL, }, + { 0x00008f00008f0000ULL, 0x8f00008f00008f00ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x0000e66900000038ULL, 0x39000044e93c5e00ULL, }, + { 0x0010382200000000ULL, 0x2400000053508b00ULL, }, + { 0x181bd07f00310000ULL, 0x0000000055390000ULL, }, + { 0x7354000025317200ULL, 0x0090b000000000f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f64000000003c00ULL, 0x001f000000142de8ULL, }, + { 0x8b6f001600620000ULL, 0x000633000000005cULL, }, + { 0x24000000916d3640ULL, 0x0071bba100000008ULL, }, /* 72 */ + { 0x0000ae476c3c0078ULL, 0x15000be596000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9e0032ULL, 0x00003e2702000000ULL, }, + { 0x0000000036008d0eULL, 0x428a7d7a00003294ULL, }, + { 0x0000160011001b46ULL, 0x7b0000be94039000ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_d.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_d.c new file mode 100644 index 0000000000..2685b2fe7e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_U.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x71c71c71c71c71c7ULL, }, + { 0x8e38e38e38e38e39ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x0000000000000000ULL, 0x1c71c71c71c71c72ULL, }, + { 0x38e38e38e38e38e4ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x0000000000000000ULL, 0x93e93e93e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x16c16c16c16c16c2ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e4ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e38e38e39ULL, 0x0000000000000000ULL, }, + { 0x16c16c16c16c16c2ULL, 0x0000000000000000ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71dULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x1c71c71c71c71c72ULL, }, + { 0x0000000000000000ULL, 0x71c71c71c71c71c7ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x93e93e93e93e93e9ULL, }, + { 0x0000000000000000ULL, 0x8e38e38e38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x386f5044e93c5d10ULL, }, + { 0x0000000000000000ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07eca3072f2ULL, 0x0000000000000000ULL, }, + { 0x73531997253171c8ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b893c43b88ULL, 0x0000000000000000ULL, }, + { 0x8b6eea15ef61e4baULL, 0x0000000000000000ULL, }, + { 0x23efc7de916d3640ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x428a7d79aac73294ULL, }, + { 0x0000000000000000ULL, 0x7af9cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_h.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_h.c new file mode 100644 index 0000000000..ca6dd38b69 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_U.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x000071c71c720000ULL, 0x71c71c72000071c7ULL, }, + { 0x8e39000038e38e39ULL, 0x000038e38e390000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x00001c7200000000ULL, 0x1c72000000001c72ULL, }, + { 0x38e40000000038e4ULL, 0x0000000038e40000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x000093e93e940000ULL, 0x93e93e94000093e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x16c20000000016c2ULL, 0x0000000016c20000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e40000000038e4ULL, 0x0000000038e40000ULL, }, + { 0x8e39000038e38e39ULL, 0x000038e38e390000ULL, }, + { 0x16c20000000016c2ULL, 0x0000000016c20000ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71d00001c71c71dULL, 0x00001c71c71d0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x00001c7200000000ULL, 0x1c72000000001c72ULL, }, + { 0x000071c71c720000ULL, 0x71c71c72000071c7ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000093e93e940000ULL, 0x93e93e94000093e9ULL, }, + { 0x00008e3900000000ULL, 0x8e39000000008e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x0000e66900000000ULL, 0x38700000e93c5d10ULL, }, + { 0x0000382200000000ULL, 0x238f000053508af8ULL, }, + { 0x181bd07f00000000ULL, 0x0000000055390000ULL, }, + { 0x73540000253171c8ULL, 0x0000afbc00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f64000000003b88ULL, 0x0000000000002de8ULL, }, + { 0x8b6f000000000000ULL, 0x0000324200000000ULL, }, + { 0x23f00000916d3640ULL, 0x0000bba100000000ULL, }, /* 72 */ + { 0x0000ae476c3c0000ULL, 0x14e10be595ec0000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9e0000ULL, 0x00003e2701e90000ULL, }, + { 0x0000000035cf8d0eULL, 0x428a7d7a00003294ULL, }, + { 0x000015ea109e1b46ULL, 0x7afa000094038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_w.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_w.c new file mode 100644 index 0000000000..42ebddb408 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subs_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBS_U.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBS_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x000000001c71c71cULL, 0x71c71c7200000000ULL, }, + { 0x8e38e38e38e38e39ULL, 0x000000008e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x0000000000000000ULL, 0x1c71c71d00000000ULL, }, + { 0x38e38e3900000000ULL, 0x0000000038e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x000000003e93e93eULL, 0x93e93e9400000000ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x16c16c1700000000ULL, 0x0000000016c16c17ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e3900000000ULL, 0x0000000038e38e39ULL, }, + { 0x8e38e38e38e38e39ULL, 0x000000008e38e38eULL, }, + { 0x16c16c1700000000ULL, 0x0000000016c16c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71dULL, 0x00000000c71c71c7ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x1c71c71d00000000ULL, }, + { 0x000000001c71c71cULL, 0x71c71c7200000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x000000003e93e93eULL, 0x93e93e9400000000ULL, }, + { 0x0000000000000000ULL, 0x8e38e38f00000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x386f5044e93c5d10ULL, }, + { 0x0000000000000000ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07f00000000ULL, 0x000000005538cd6cULL, }, + { 0x73531997253171c8ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b900000000ULL, 0x0000000000000000ULL, }, + { 0x8b6eea1600000000ULL, 0x0000000000000000ULL, }, + { 0x23efc7de916d3640ULL, 0x0000000000000000ULL, }, /* 72 */ + { 0x000000006c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x0000000001e84274ULL, }, + { 0x0000000035cf8d0eULL, 0x428a7d7a00000000ULL, }, + { 0x00000000109e1b46ULL, 0x7af9cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBS_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_b.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_b.c new file mode 100644 index 0000000000..dac20cc769 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUS_U.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUS_U.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffc7ffffc7ffffULL, 0xc7ffffc7ffffc7ffULL, }, + { 0xe38effe38effe38eULL, 0xffe38effe38effe3ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1d72001d72001d72ULL, 0x001d72001d72001dULL, }, + { 0x0000390000390000ULL, 0x3900003900003900ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc7ff72c7ff72c7ffULL, 0x72c7ff72c7ff72c7ULL, }, + { 0x8e39e38e39e38e39ULL, 0xe38e39e38e39e38eULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x72c71d72c71d72c7ULL, 0x1d72c71d72c71d72ULL, }, + { 0x39008e39008e3900ULL, 0x8e39008e39008e39ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe9ff94e9ff94e9ffULL, 0x94e9ff94e9ff94e9ULL, }, + { 0xb05bffb05bffb05bULL, 0xffb05bffb05bffb0ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x50a50050a50050a5ULL, 0x0050a50050a50050ULL, }, + { 0x17006c17006c1700ULL, 0x6c17006c17006c17ULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffe48effe48effe4ULL, 0x8effe48effe48effULL, }, + { 0x8e39008e39008e39ULL, 0x008e39008e39008eULL, }, + { 0xffc26cffc26cffc2ULL, 0x6cffc26cffc26cffULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0xffff00ffff00ffffULL, 0x00ffff00ffff00ffULL, }, + { 0xc71d71c71d71c71dULL, 0x71c71d71c71d71c7ULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x72c7ff72c7ff72c7ULL, 0xff72c7ff72c7ff72ULL, }, + { 0x001c72001c72001cULL, 0x72001c72001c7200ULL, }, + { 0x50a5fb50a5fb50a5ULL, 0xfb50a5fb50a5fb50ULL, }, + { 0x003e94003e94003eULL, 0x94003e94003e9400ULL, }, + { 0x39e38f39e38f39e3ULL, 0x8f39e38f39e38f39ULL, }, + { 0x0000ff0000ff0000ULL, 0xff0000ff0000ff00ULL, }, + { 0xff00ffff00000000ULL, 0x00000000ff00ff00ULL, }, /* 64 */ + { 0x8dace66900cf8e38ULL, 0x39705044e93c5e10ULL, }, + { 0xdc10ffff6f93cac0ULL, 0x248f455fff508b00ULL, }, + { 0x181bd07f00317300ULL, 0xbe768386ff39ce6cULL, }, + { 0xff541a9725317200ULL, 0x0090b0001700a2f0ULL, }, + { 0xffff000000ffff00ULL, 0x00ffff00000000ffULL, }, + { 0xff6452b994c4ff88ULL, 0x00fff51b6a142de8ULL, }, + { 0x8b6f00160062e500ULL, 0x85ffff426c0070ffULL, }, + { 0xff00c8de916d3640ULL, 0x0071bba1ad007508ULL, }, /* 72 */ + { 0xb19cae476cffc478ULL, 0x15e1ffe596000018ULL, }, + { 0xff00ffffffffffffULL, 0x00ffffffff000000ULL, }, + { 0x3c0b985d5b9ea932ULL, 0x9ae7ffffff004374ULL, }, + { 0xe800308136008d0eULL, 0x428a7d7aab00ff94ULL, }, + { 0x75911600119eff46ULL, 0x7bfacdbe940390a4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_d.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_d.c new file mode 100644 index 0000000000..4485502c1c --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUS_U.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUS_U.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c72ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x38e38e38e38e38e4ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0x38e38e38e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e93e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0xffffffffffffffffULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0x0000000000000000ULL, }, + { 0x16c16c16c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0x8e38e38e38e38e39ULL, }, + { 0x8e38e38e38e38e39ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x6c16c16c16c16c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71dULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c71c71c71c7ULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x71c71c71c71c71c7ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0x0000000000000000ULL, 0x93e93e93e93e93e9ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace668dace8e38ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038216e92c9c0ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07eca3072f2ULL, 0xbd7582865538cd6cULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x8b6eea15ef61e4baULL, 0x850632416bfc705cULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x99e73e2701e84274ULL, }, + { 0xe7e42f8135cf8d0eULL, 0x428a7d79aac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x7af9cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_h.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_h.c new file mode 100644 index 0000000000..9e99aeefc5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUS_U.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUS_U.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffc71cffffffffULL, 0xc71cffffffffc71cULL, }, + { 0xe38effff8e38e38eULL, 0xffff8e38e38effffULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c72000071c81c72ULL, 0x000071c81c720000ULL, }, + { 0x000038e400000000ULL, 0x38e40000000038e4ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c7ffffc71cULL, 0x71c7ffffc71c71c7ULL, }, + { 0x8e39e38e38e38e39ULL, 0xe38e38e38e39e38eULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c72c71d71c7ULL, 0x1c72c71d71c71c72ULL, }, + { 0x38e48e39000038e4ULL, 0x8e39000038e48e39ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e9ffffe93eULL, 0x93e9ffffe93e93e9ULL, }, + { 0xb05bffff5b05b05bULL, 0xffff5b05b05bffffULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa50000a4fb4fa5ULL, 0x0000a4fb4fa50000ULL, }, + { 0x16c26c17000016c2ULL, 0x6c17000016c26c17ULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffff8e39e38effffULL, 0x8e39e38effff8e39ULL, }, + { 0x8e39000038e38e39ULL, 0x000038e38e390000ULL, }, + { 0xffff6c17c16cffffULL, 0x6c17c16cffff6c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0xffff0000ffffffffULL, 0x0000ffffffff0000ULL, }, + { 0xc71d71c71c71c71dULL, 0x71c71c71c71d71c7ULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c7ffffc71d71c7ULL, 0xffffc71d71c7ffffULL, }, + { 0x000071c71c720000ULL, 0x71c71c72000071c7ULL, }, + { 0x4fa5fa50a4fb4fa5ULL, 0xfa50a4fb4fa5fa50ULL, }, + { 0x000093e93e940000ULL, 0x93e93e94000093e9ULL, }, + { 0x38e38e39e38f38e3ULL, 0x8e39e38f38e38e39ULL, }, + { 0x0000ffff00000000ULL, 0xffff00000000ffffULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, /* 64 */ + { 0x8cace66900008e38ULL, 0x38705044e93c5d10ULL, }, + { 0xdc10ffff6e93c9c0ULL, 0x238f445fffff8af8ULL, }, + { 0x181bd07f000072f2ULL, 0xbd768286ffffcd6cULL, }, + { 0xffff1997253171c8ULL, 0x0000afbc16c4a2f0ULL, }, + { 0xffff00000000ffffULL, 0x0000ffff00000000ULL, }, + { 0xffff51b993c4ffffULL, 0x0000f41b6a142de8ULL, }, + { 0x8b6f00000000e4baULL, 0x8506ffff6bfd705cULL, }, + { 0xffffc7de916d3640ULL, 0x0000bba1acb07508ULL, }, /* 72 */ + { 0xb09cae476c3cc478ULL, 0x14e1ffff95ec0000ULL, }, + { 0xffffffffffffffffULL, 0x0000ffffffff0000ULL, }, + { 0x3c0b985d5b9ea932ULL, 0x99e7ffffffff4274ULL, }, + { 0xe7e52f8135cf8d0eULL, 0x428a7d7aaac7ffffULL, }, + { 0x749115ea109effffULL, 0x7afacdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_w.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_w.c new file mode 100644 index 0000000000..53a9acac1b --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsus_u_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUS_U.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUS_U.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xc71c71c7ffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffe38e38e3ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71d71c71c72ULL, 0x000000001c71c71dULL, }, + { 0x0000000000000000ULL, 0x38e38e3900000000ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c7ffffffffULL, 0x71c71c72c71c71c7ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c72c71c71c7ULL, 0x1c71c71d71c71c72ULL, }, + { 0x38e38e3900000000ULL, 0x8e38e38e38e38e39ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e9ffffffffULL, 0x93e93e94e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0xffffffffb05b05b0ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0x000000004fa4fa50ULL, }, + { 0x16c16c1700000000ULL, 0x6c16c16c16c16c17ULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffe38e38e4ULL, 0x8e38e38effffffffULL, }, + { 0x8e38e38e38e38e39ULL, 0x000000008e38e38eULL, }, + { 0xffffffffc16c16c2ULL, 0x6c16c16cffffffffULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0xc71c71c71c71c71dULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c72c71c71c7ULL, 0xffffffff71c71c72ULL, }, + { 0x000000001c71c71cULL, 0x71c71c7200000000ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0xfa4fa4fb4fa4fa50ULL, }, + { 0x000000003e93e93eULL, 0x93e93e9400000000ULL, }, + { 0x38e38e39e38e38e3ULL, 0x8e38e38f38e38e39ULL, }, + { 0x0000000000000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffff00000000ULL, 0x00000000ffffffffULL, }, /* 64 */ + { 0x8cace66900000000ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038226e92c9c0ULL, 0x238e445fffffffffULL, }, + { 0x181bd07f00000000ULL, 0xbd758286ffffffffULL, }, + { 0xffffffff253171c8ULL, 0x0000000016c3a2f0ULL, }, + { 0xffffffff00000000ULL, 0x0000000000000000ULL, }, + { 0xffffffff93c43b88ULL, 0x000000006a142de8ULL, }, + { 0x8b6eea1600000000ULL, 0x850632426bfc705cULL, }, + { 0xffffffff916d3640ULL, 0x00000000acaf7508ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0xffffffffffffffffULL, 0x00000000ffffffffULL, }, + { 0x3c0b985d5b9da932ULL, 0x99e73e27ffffffffULL, }, + { 0xe7e42f8135cf8d0eULL, 0x428a7d7aaac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x7af9cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUS_U_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_b.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_b.c new file mode 100644 index 0000000000..86fb4f3e26 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUU_S.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUU_S.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x1c717f1c717f1c71ULL, 0x7f1c717f1c717f1cULL, }, + { 0x7f7f387f7f387f7fULL, 0x387f7f387f7f387fULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, + { 0x8080c88080c88080ULL, 0xc88080c88080c880ULL, }, + { 0xe48f80e48f80e48fULL, 0x80e48f80e48f80e4ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, /* 16 */ + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c72c71c72c71cULL, 0x72c71c72c71c72c7ULL, }, + { 0x7f39e37f39e37f39ULL, 0xe37f39e37f39e37fULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x80c71d80c71d80c7ULL, 0x1d80c71d80c71d80ULL, }, + { 0x39e48e39e48e39e4ULL, 0x8e39e48e39e48e39ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, /* 32 */ + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7f7f7f7f7f7f7f7fULL, 0x7f7f7f7f7f7f7f7fULL, }, + { 0xe93e7fe93e7fe93eULL, 0x7fe93e7fe93e7fe9ULL, }, + { 0x7f5b057f5b057f5bULL, 0x057f5b057f5b057fULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x80a5fb80a5fb80a5ULL, 0xfb80a5fb80a5fb80ULL, }, + { 0x17c28017c28017c2ULL, 0x8017c28017c28017ULL, }, + { 0xe48f80e48f80e48fULL, 0x80e48f80e48f80e4ULL, }, /* 48 */ + { 0x7f7f387f7f387f7fULL, 0x387f7f387f7f387fULL, }, + { 0x39e48e39e48e39e4ULL, 0x8e39e48e39e48e39ULL, }, + { 0x7f39e37f39e37f39ULL, 0xe37f39e37f39e37fULL, }, + { 0x17c28017c28017c2ULL, 0x8017c28017c28017ULL, }, + { 0x7f5b057f5b057f5bULL, 0x057f5b057f5b057fULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7f1d807f1d807f1dULL, 0x807f1d807f1d807fULL, }, + { 0x8080c88080c88080ULL, 0xc88080c88080c880ULL, }, /* 56 */ + { 0x1c717f1c717f1c71ULL, 0x7f1c717f1c717f1cULL, }, + { 0x80c71d80c71d80c7ULL, 0x1d80c71d80c71d80ULL, }, + { 0xc71c72c71c72c71cULL, 0x72c71c72c71c72c7ULL, }, + { 0x80a5fb80a5fb80a5ULL, 0xfb80a5fb80a5fb80ULL, }, + { 0xe93e7fe93e7fe93eULL, 0x7fe93e7fe93e7fe9ULL, }, + { 0x80e37f80e37f80e3ULL, 0x7f80e37f80e37f80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8dac7f69dbcf8e38ULL, 0x398080447f3c5e80ULL, }, + { 0xdc1038228093cac0ULL, 0x248f808053507ff8ULL, }, + { 0x181b7f7fca3180f2ULL, 0xbe8083865539ce80ULL, }, + { 0x73548097253172c8ULL, 0xc77f7fbc80c4a27fULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6480b994c43c88ULL, 0xeb1ff58080142d7fULL, }, + { 0x7f6fea16ef62e5baULL, 0x8506338080fd805cULL, }, + { 0x24f0c8de7f6d3640ULL, 0xdc717f7fadb08008ULL, }, /* 72 */ + { 0xb19c7f476c3cc478ULL, 0x15e10b7f7fecd380ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b7f5d5b7fa932ULL, 0x9ae73e2702e98080ULL, }, + { 0xe8e5808136cf7f0eULL, 0x427f7d7aabc7327fULL, }, + { 0x809116ea119e1b46ULL, 0x7bfacd7f7f037fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_d.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_d.c new file mode 100644 index 0000000000..45a1eb3094 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUU_S.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUU_S.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, + { 0x8000000000000000ULL, 0xc71c71c71c71c71dULL, }, + { 0xe38e38e38e38e38fULL, 0x8000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, /* 16 */ + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x7fffffffffffffffULL, 0xe38e38e38e38e38eULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x8000000000000000ULL, 0x1c71c71c71c71c72ULL, }, + { 0x38e38e38e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, /* 32 */ + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fffffffffffffffULL, 0x7fffffffffffffffULL, }, + { 0xe93e93e93e93e93eULL, 0x7fffffffffffffffULL, }, + { 0x7fffffffffffffffULL, 0x05b05b05b05b05b0ULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x8000000000000000ULL, 0x8000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000000000000ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0x16c16c16c16c16c2ULL, 0x8000000000000000ULL, }, + { 0xe38e38e38e38e38fULL, 0x8000000000000000ULL, }, /* 48 */ + { 0x7fffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0x7fffffffffffffffULL, 0xe38e38e38e38e38eULL, }, + { 0x16c16c16c16c16c2ULL, 0x8000000000000000ULL, }, + { 0x7fffffffffffffffULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fffffffffffffffULL, 0x8000000000000000ULL, }, + { 0x8000000000000000ULL, 0xc71c71c71c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x7fffffffffffffffULL, }, + { 0x8000000000000000ULL, 0x1c71c71c71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x8000000000000000ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0xe93e93e93e93e93eULL, 0x7fffffffffffffffULL, }, + { 0x8000000000000000ULL, 0x7fffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace668dace8e38ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038216e92c9c0ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07eca3072f2ULL, 0xbd7582865538cd6cULL, }, + { 0x73531997253171c8ULL, 0xc790afbb16c3a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b893c43b88ULL, 0xeb1ef41a6a142de8ULL, }, + { 0x7fffffffffffffffULL, 0x850632416bfc705cULL, }, + { 0x23efc7de916d3640ULL, 0xdc71bba0acaf7508ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x99e73e2701e84274ULL, }, + { 0xe7e42f8135cf8d0eULL, 0x428a7d79aac73294ULL, }, + { 0x8000000000000000ULL, 0x7af9cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_h.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_h.c new file mode 100644 index 0000000000..14ac7def29 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUU_S.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUU_S.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x1c717fff71c71c71ULL, 0x7fff71c71c717fffULL, }, + { 0x7fff38e37fff7fffULL, 0x38e37fff7fff38e3ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, + { 0x8000c71d80008000ULL, 0xc71d80008000c71dULL, }, + { 0xe38f80008e39e38fULL, 0x80008e39e38f8000ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, /* 16 */ + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c72c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x7fffe38e38e37fffULL, 0xe38e38e37fffe38eULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x80001c72c71d8000ULL, 0x1c72c71d80001c72ULL, }, + { 0x38e48e39e38e38e4ULL, 0x8e39e38e38e48e39ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, /* 32 */ + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fff7fff7fff7fffULL, 0x7fff7fff7fff7fffULL, }, + { 0xe93e7fff3e94e93eULL, 0x7fff3e94e93e7fffULL, }, + { 0x7fff05b05b057fffULL, 0x05b05b057fff05b0ULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x8000800080008000ULL, 0x8000800080008000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000fa50a4fb8000ULL, 0xfa50a4fb8000fa50ULL, }, + { 0x16c28000c16c16c2ULL, 0x8000c16c16c28000ULL, }, + { 0xe38f80008e39e38fULL, 0x80008e39e38f8000ULL, }, /* 48 */ + { 0x7fff38e37fff7fffULL, 0x38e37fff7fff38e3ULL, }, + { 0x38e48e39e38e38e4ULL, 0x8e39e38e38e48e39ULL, }, + { 0x7fffe38e38e37fffULL, 0xe38e38e37fffe38eULL, }, + { 0x16c28000c16c16c2ULL, 0x8000c16c16c28000ULL, }, + { 0x7fff05b05b057fffULL, 0x05b05b057fff05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fff80001c717fffULL, 0x80001c717fff8000ULL, }, + { 0x8000c71d80008000ULL, 0xc71d80008000c71dULL, }, /* 56 */ + { 0x1c717fff71c71c71ULL, 0x7fff71c71c717fffULL, }, + { 0x80001c72c71d8000ULL, 0x1c72c71d80001c72ULL, }, + { 0xc71c71c71c72c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x8000fa50a4fb8000ULL, 0xfa50a4fb8000fa50ULL, }, + { 0xe93e7fff3e94e93eULL, 0x7fff3e94e93e7fffULL, }, + { 0x80007fffe38f8000ULL, 0x7fffe38f80007fffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cac7fffdacf8e38ULL, 0x387080007fff5d10ULL, }, + { 0xdc1038228000c9c0ULL, 0x238f800053507fffULL, }, + { 0x181b7fffca318000ULL, 0xbd7682865539cd6cULL, }, + { 0x73548000253171c8ULL, 0xc7907fff8000a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f64800093c43b88ULL, 0xeb1ff41b80002de8ULL, }, + { 0x7fffea16ef62e4baULL, 0x8506324280008000ULL, }, + { 0x23f0c7de7fff3640ULL, 0xdc717fffacb08000ULL, }, /* 72 */ + { 0xb09c7fff6c3cc478ULL, 0x14e10be57fffd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b7fff5b9ea932ULL, 0x99e73e2701e98000ULL, }, + { 0xe7e5800035cf7fffULL, 0x428a7d7aaac73294ULL, }, + { 0x800015ea109e1b46ULL, 0x7afacdbe7fff7fffULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_w.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_w.c new file mode 100644 index 0000000000..688f469cd0 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subsuu_s_w.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBSUU_S.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBSUU_S.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0x7fffffff1c71c71cULL, }, + { 0x7fffffff7fffffffULL, 0x38e38e387fffffffULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, + { 0x8000000080000000ULL, 0xc71c71c880000000ULL, }, + { 0xe38e38e48e38e38fULL, 0x80000000e38e38e4ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, /* 16 */ + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x7fffffff38e38e39ULL, 0xe38e38e37fffffffULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x80000000c71c71c7ULL, 0x1c71c71d80000000ULL, }, + { 0x38e38e39e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, /* 32 */ + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fffffff7fffffffULL, 0x7fffffff7fffffffULL, }, + { 0xe93e93e93e93e93eULL, 0x7fffffffe93e93e9ULL, }, + { 0x7fffffff5b05b05bULL, 0x05b05b057fffffffULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x8000000080000000ULL, 0x8000000080000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x80000000a4fa4fa5ULL, 0xfa4fa4fb80000000ULL, }, + { 0x16c16c17c16c16c2ULL, 0x8000000016c16c17ULL, }, + { 0xe38e38e48e38e38fULL, 0x80000000e38e38e4ULL, }, /* 48 */ + { 0x7fffffff7fffffffULL, 0x38e38e387fffffffULL, }, + { 0x38e38e39e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0x7fffffff38e38e39ULL, 0xe38e38e37fffffffULL, }, + { 0x16c16c17c16c16c2ULL, 0x8000000016c16c17ULL, }, + { 0x7fffffff5b05b05bULL, 0x05b05b057fffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x7fffffff1c71c71dULL, 0x800000007fffffffULL, }, + { 0x8000000080000000ULL, 0xc71c71c880000000ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0x7fffffff1c71c71cULL, }, + { 0x80000000c71c71c7ULL, 0x1c71c71d80000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x80000000a4fa4fa5ULL, 0xfa4fa4fb80000000ULL, }, + { 0xe93e93e93e93e93eULL, 0x7fffffffe93e93e9ULL, }, + { 0x80000000e38e38e3ULL, 0x7fffffff80000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace669dace8e38ULL, 0x386f50447fffffffULL, }, + { 0xdc10382280000000ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07fca3072f2ULL, 0xbd7582865538cd6cULL, }, + { 0x73531997253171c8ULL, 0xc790afbc80000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b993c43b88ULL, 0xeb1ef41b80000000ULL, }, + { 0x7fffffffef61e4baULL, 0x8506324280000000ULL, }, + { 0x23efc7de7fffffffULL, 0xdc71bba1acaf7508ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be57fffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x99e73e2701e84274ULL, }, + { 0xe7e42f8135cf8d0eULL, 0x428a7d7aaac73294ULL, }, + { 0x80000000109e1b46ULL, 0x7af9cdbe7fffffffULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBSUU_S_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_b.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_b.c new file mode 100644 index 0000000000..d0964dcd59 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_b.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBV.B + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBV.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0101010101010101ULL, 0x0101010101010101ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c72c71c72c71cULL, 0x72c71c72c71c72c7ULL, }, + { 0x8e39e38e39e38e39ULL, 0xe38e39e38e39e38eULL, }, + { 0x5656565656565656ULL, 0x5656565656565656ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xababababababababULL, 0xababababababababULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x72c71d72c71d72c7ULL, 0x1d72c71d72c71d72ULL, }, + { 0x39e48e39e48e39e4ULL, 0x8e39e48e39e48e39ULL, }, + { 0xcdcdcdcdcdcdcdcdULL, 0xcdcdcdcdcdcdcdcdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e94e93e94e93eULL, 0x94e93e94e93e94e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x3434343434343434ULL, 0x3434343434343434ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8989898989898989ULL, 0x8989898989898989ULL, }, + { 0xdedededededededeULL, 0xdedededededededeULL, }, + { 0x6767676767676767ULL, 0x6767676767676767ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x50a5fb50a5fb50a5ULL, 0xfb50a5fb50a5fb50ULL, }, + { 0x17c26c17c26c17c2ULL, 0x6c17c26c17c26c17ULL, }, + { 0xe48f39e48f39e48fULL, 0x39e48f39e48f39e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x39e48e39e48e39e4ULL, 0x8e39e48e39e48e39ULL, }, + { 0x8e39e38e39e38e39ULL, 0xe38e39e38e39e38eULL, }, + { 0x17c26c17c26c17c2ULL, 0x6c17c26c17c26c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71d71c71d71c71dULL, 0x71c71d71c71d71c7ULL, }, + { 0x1d72c81d72c81d72ULL, 0xc81d72c81d72c81dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x72c71d72c71d72c7ULL, 0x1d72c71d72c71d72ULL, }, + { 0xc71c72c71c72c71cULL, 0x72c71c72c71c72c7ULL, }, + { 0x50a5fb50a5fb50a5ULL, 0xfb50a5fb50a5fb50ULL, }, + { 0xe93e94e93e94e93eULL, 0x94e93e94e93e94e9ULL, }, + { 0x39e38f39e38f39e3ULL, 0x8f39e38f39e38f39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8dace669dbcf8e38ULL, 0x39705044e93c5e10ULL, }, + { 0xdc1038226f93cac0ULL, 0x248f455f53508bf8ULL, }, + { 0x181bd07fca3173f2ULL, 0xbe7683865539ce6cULL, }, + { 0x73541a97253172c8ULL, 0xc790b0bc17c4a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6452b994c43c88ULL, 0xeb1ff51b6a142de8ULL, }, + { 0x8b6fea16ef62e5baULL, 0x850633426cfd705cULL, }, + { 0x24f0c8de916d3640ULL, 0xdc71bba1adb07508ULL, }, /* 72 */ + { 0xb19cae476c3cc478ULL, 0x15e10be596ecd318ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9ea932ULL, 0x9ae73e2702e94374ULL, }, + { 0xe8e5308136cf8d0eULL, 0x428a7d7aabc73294ULL, }, + { 0x759116ea119e1b46ULL, 0x7bfacdbe940390a4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_d.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_d.c new file mode 100644 index 0000000000..ec26a8e0c6 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_d.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBV.D + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBV.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000001ULL, 0x0000000000000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555556ULL, 0x5555555555555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaabULL, 0xaaaaaaaaaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0x38e38e38e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0xcccccccccccccccdULL, 0xcccccccccccccccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e93e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x3333333333333334ULL, 0x3333333333333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888888888889ULL, 0x8888888888888889ULL, }, + { 0xdddddddddddddddeULL, 0xdddddddddddddddeULL, }, + { 0x6666666666666667ULL, 0x6666666666666667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0x16c16c16c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xe38e38e38e38e38fULL, 0x38e38e38e38e38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x16c16c16c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71dULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71c71c71c72ULL, 0xc71c71c71c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c71c71c71c7ULL, 0x1c71c71c71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c71c71c71c7ULL, }, + { 0x4fa4fa4fa4fa4fa5ULL, 0xfa4fa4fa4fa4fa50ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e93e93e93e9ULL, }, + { 0x38e38e38e38e38e3ULL, 0x8e38e38e38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace668dace8e38ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038216e92c9c0ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07eca3072f2ULL, 0xbd7582865538cd6cULL, }, + { 0x73531997253171c8ULL, 0xc790afbb16c3a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b893c43b88ULL, 0xeb1ef41a6a142de8ULL, }, + { 0x8b6eea15ef61e4baULL, 0x850632416bfc705cULL, }, + { 0x23efc7de916d3640ULL, 0xdc71bba0acaf7508ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x99e73e2701e84274ULL, }, + { 0xe7e42f8135cf8d0eULL, 0x428a7d79aac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x7af9cdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_h.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_h.c new file mode 100644 index 0000000000..420422ecf1 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_h.c @@ -0,0 +1,151 @@ +/* + * Test program for MSA instruction SUBV.H + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBV.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0001000100010001ULL, 0x0001000100010001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c72c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x8e39e38e38e38e39ULL, 0xe38e38e38e39e38eULL, }, + { 0x5556555655565556ULL, 0x5556555655565556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaabaaabaaabaaabULL, 0xaaabaaabaaabaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c72c71d71c7ULL, 0x1c72c71d71c71c72ULL, }, + { 0x38e48e39e38e38e4ULL, 0x8e39e38e38e48e39ULL, }, + { 0xcccdcccdcccdcccdULL, 0xcccdcccdcccdcccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e94e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x3334333433343334ULL, 0x3334333433343334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8889888988898889ULL, 0x8889888988898889ULL, }, + { 0xdddedddedddedddeULL, 0xdddedddedddedddeULL, }, + { 0x6667666766676667ULL, 0x6667666766676667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa5fa50a4fb4fa5ULL, 0xfa50a4fb4fa5fa50ULL, }, + { 0x16c26c17c16c16c2ULL, 0x6c17c16c16c26c17ULL, }, + { 0xe38f38e48e39e38fULL, 0x38e48e39e38f38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e48e39e38e38e4ULL, 0x8e39e38e38e48e39ULL, }, + { 0x8e39e38e38e38e39ULL, 0xe38e38e38e39e38eULL, }, + { 0x16c26c17c16c16c2ULL, 0x6c17c16c16c26c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71d71c71c71c71dULL, 0x71c71c71c71d71c7ULL, }, + { 0x1c72c71d71c81c72ULL, 0xc71d71c81c72c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c72c71d71c7ULL, 0x1c72c71d71c71c72ULL, }, + { 0xc71c71c71c72c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x4fa5fa50a4fb4fa5ULL, 0xfa50a4fb4fa5fa50ULL, }, + { 0xe93e93e93e94e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0x38e38e39e38f38e3ULL, 0x8e39e38f38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace669dacf8e38ULL, 0x38705044e93c5d10ULL, }, + { 0xdc1038226e93c9c0ULL, 0x238f445f53508af8ULL, }, + { 0x181bd07fca3172f2ULL, 0xbd7682865539cd6cULL, }, + { 0x73541997253171c8ULL, 0xc790afbc16c4a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6451b993c43b88ULL, 0xeb1ff41b6a142de8ULL, }, + { 0x8b6fea16ef62e4baULL, 0x850632426bfd705cULL, }, + { 0x23f0c7de916d3640ULL, 0xdc71bba1acb07508ULL, }, /* 72 */ + { 0xb09cae476c3cc478ULL, 0x14e10be595ecd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9ea932ULL, 0x99e73e2701e94274ULL, }, + { 0xe7e52f8135cf8d0eULL, 0x428a7d7aaac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x7afacdbe94038fa4ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_w.c b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_w.c new file mode 100644 index 0000000000..3e97005815 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/int-subtract/test_msa_subv_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction SUBV.W + * + * Copyright (C) 2018 Wave Computing, Inc. + * Copyright (C) 2018 Mateja Marjanovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs.h" +#include "../../../../include/test_utils.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "SUBV.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000100000001ULL, 0x0000000100000001ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555655555556ULL, 0x5555555655555556ULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaabaaaaaaabULL, 0xaaaaaaabaaaaaaabULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x71c71c72c71c71c7ULL, 0x1c71c71d71c71c72ULL, }, + { 0x38e38e39e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0xcccccccdcccccccdULL, 0xcccccccdcccccccdULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x3333333433333334ULL, 0x3333333433333334ULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8888888988888889ULL, 0x8888888988888889ULL, }, + { 0xdddddddedddddddeULL, 0xdddddddedddddddeULL, }, + { 0x6666666766666667ULL, 0x6666666766666667ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0xfa4fa4fb4fa4fa50ULL, }, + { 0x16c16c17c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xe38e38e48e38e38fULL, 0x38e38e39e38e38e4ULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e39e38e38e4ULL, 0x8e38e38e38e38e39ULL, }, + { 0x8e38e38e38e38e39ULL, 0xe38e38e38e38e38eULL, }, + { 0x16c16c17c16c16c2ULL, 0x6c16c16c16c16c17ULL, }, + { 0xb05b05b05b05b05bULL, 0x05b05b05b05b05b0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71dULL, 0x71c71c71c71c71c7ULL, }, + { 0x1c71c71d71c71c72ULL, 0xc71c71c81c71c71dULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x71c71c72c71c71c7ULL, 0x1c71c71d71c71c72ULL, }, + { 0xc71c71c71c71c71cULL, 0x71c71c72c71c71c7ULL, }, + { 0x4fa4fa50a4fa4fa5ULL, 0xfa4fa4fb4fa4fa50ULL, }, + { 0xe93e93e93e93e93eULL, 0x93e93e94e93e93e9ULL, }, + { 0x38e38e39e38e38e3ULL, 0x8e38e38f38e38e39ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x8cace669dace8e38ULL, 0x386f5044e93c5d10ULL, }, + { 0xdc1038226e92c9c0ULL, 0x238e445f53508af8ULL, }, + { 0x181bd07fca3072f2ULL, 0xbd7582865538cd6cULL, }, + { 0x73531997253171c8ULL, 0xc790afbc16c3a2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4f6351b993c43b88ULL, 0xeb1ef41b6a142de8ULL, }, + { 0x8b6eea16ef61e4baULL, 0x850632426bfc705cULL, }, + { 0x23efc7de916d3640ULL, 0xdc71bba1acaf7508ULL, }, /* 72 */ + { 0xb09cae476c3bc478ULL, 0x14e10be595ebd218ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3c0b985d5b9da932ULL, 0x99e73e2701e84274ULL, }, + { 0xe7e42f8135cf8d0eULL, 0x428a7d7aaac73294ULL, }, + { 0x749115ea109e1b46ULL, 0x7af9cdbe94038fa4ULL, }, + { 0xc3f467a3a46256ceULL, 0x6618c1d9fe17bd8cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, +}; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_SUBV_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_b.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_b.c new file mode 100644 index 0000000000..d720dc30a5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVEV.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "*ILVEV.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xff00ff00ff00ff00ULL, 0xff00ff00ff00ff00ULL, }, + { 0xffaaffaaffaaffaaULL, 0xffaaffaaffaaffaaULL, }, + { 0xff55ff55ff55ff55ULL, 0xff55ff55ff55ff55ULL, }, + { 0xffccffccffccffccULL, 0xffccffccffccffccULL, }, + { 0xff33ff33ff33ff33ULL, 0xff33ff33ff33ff33ULL, }, + { 0xff8effe3ff38ff8eULL, 0xffe3ff38ff8effe3ULL, }, + { 0xff71ff1cffc7ff71ULL, 0xff1cffc7ff71ff1cULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0x00cc00cc00cc00ccULL, 0x00cc00cc00cc00ccULL, }, + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0x008e00e30038008eULL, 0x00e30038008e00e3ULL, }, + { 0x0071001c00c70071ULL, 0x001c00c70071001cULL, }, + { 0xaaffaaffaaffaaffULL, 0xaaffaaffaaffaaffULL, }, /* 16 */ + { 0xaa00aa00aa00aa00ULL, 0xaa00aa00aa00aa00ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaa55aa55aa55aa55ULL, 0xaa55aa55aa55aa55ULL, }, + { 0xaaccaaccaaccaaccULL, 0xaaccaaccaaccaaccULL, }, + { 0xaa33aa33aa33aa33ULL, 0xaa33aa33aa33aa33ULL, }, + { 0xaa8eaae3aa38aa8eULL, 0xaae3aa38aa8eaae3ULL, }, + { 0xaa71aa1caac7aa71ULL, 0xaa1caac7aa71aa1cULL, }, + { 0x55ff55ff55ff55ffULL, 0x55ff55ff55ff55ffULL, }, /* 24 */ + { 0x5500550055005500ULL, 0x5500550055005500ULL, }, + { 0x55aa55aa55aa55aaULL, 0x55aa55aa55aa55aaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55cc55cc55cc55ccULL, 0x55cc55cc55cc55ccULL, }, + { 0x5533553355335533ULL, 0x5533553355335533ULL, }, + { 0x558e55e35538558eULL, 0x55e35538558e55e3ULL, }, + { 0x5571551c55c75571ULL, 0x551c55c75571551cULL, }, + { 0xccffccffccffccffULL, 0xccffccffccffccffULL, }, /* 32 */ + { 0xcc00cc00cc00cc00ULL, 0xcc00cc00cc00cc00ULL, }, + { 0xccaaccaaccaaccaaULL, 0xccaaccaaccaaccaaULL, }, + { 0xcc55cc55cc55cc55ULL, 0xcc55cc55cc55cc55ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcc33cc33cc33cc33ULL, 0xcc33cc33cc33cc33ULL, }, + { 0xcc8ecce3cc38cc8eULL, 0xcce3cc38cc8ecce3ULL, }, + { 0xcc71cc1cccc7cc71ULL, 0xcc1cccc7cc71cc1cULL, }, + { 0x33ff33ff33ff33ffULL, 0x33ff33ff33ff33ffULL, }, /* 40 */ + { 0x3300330033003300ULL, 0x3300330033003300ULL, }, + { 0x33aa33aa33aa33aaULL, 0x33aa33aa33aa33aaULL, }, + { 0x3355335533553355ULL, 0x3355335533553355ULL, }, + { 0x33cc33cc33cc33ccULL, 0x33cc33cc33cc33ccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x338e33e33338338eULL, 0x33e33338338e33e3ULL, }, + { 0x3371331c33c73371ULL, 0x331c33c73371331cULL, }, + { 0x8effe3ff38ff8effULL, 0xe3ff38ff8effe3ffULL, }, /* 48 */ + { 0x8e00e30038008e00ULL, 0xe30038008e00e300ULL, }, + { 0x8eaae3aa38aa8eaaULL, 0xe3aa38aa8eaae3aaULL, }, + { 0x8e55e35538558e55ULL, 0xe35538558e55e355ULL, }, + { 0x8ecce3cc38cc8eccULL, 0xe3cc38cc8ecce3ccULL, }, + { 0x8e33e33338338e33ULL, 0xe33338338e33e333ULL, }, + { 0x8e8ee3e338388e8eULL, 0xe3e338388e8ee3e3ULL, }, + { 0x8e71e31c38c78e71ULL, 0xe31c38c78e71e31cULL, }, + { 0x71ff1cffc7ff71ffULL, 0x1cffc7ff71ff1cffULL, }, /* 56 */ + { 0x71001c00c7007100ULL, 0x1c00c70071001c00ULL, }, + { 0x71aa1caac7aa71aaULL, 0x1caac7aa71aa1caaULL, }, + { 0x71551c55c7557155ULL, 0x1c55c75571551c55ULL, }, + { 0x71cc1cccc7cc71ccULL, 0x1cccc7cc71cc1cccULL, }, + { 0x71331c33c7337133ULL, 0x1c33c73371331c33ULL, }, + { 0x718e1ce3c738718eULL, 0x1ce3c738718e1ce3ULL, }, + { 0x71711c1cc7c77171ULL, 0x1c1cc7c771711c1cULL, }, + { 0x6a6acccc62624040ULL, 0x67675e5e7b7b0c0cULL, }, /* 64 */ + { 0x6abecc6362934008ULL, 0x67f75e1a7b3f0cfcULL, }, + { 0x6a5accaa62cf4080ULL, 0x67d85eff7b2b0c14ULL, }, + { 0x6a4fcc4d6231404eULL, 0x67f15ed87b420ca0ULL, }, + { 0xbe6a63cc93620840ULL, 0xf7671a5e3f7bfc0cULL, }, + { 0xbebe636393930808ULL, 0xf7f71a1a3f3ffcfcULL, }, + { 0xbe5a63aa93cf0880ULL, 0xf7d81aff3f2bfc14ULL, }, + { 0xbe4f634d9331084eULL, 0xf7f11ad83f42fca0ULL, }, + { 0x5a6aaacccf628040ULL, 0xd867ff5e2b7b140cULL, }, /* 72 */ + { 0x5abeaa63cf938008ULL, 0xd8f7ff1a2b3f14fcULL, }, + { 0x5a5aaaaacfcf8080ULL, 0xd8d8ffff2b2b1414ULL, }, + { 0x5a4faa4dcf31804eULL, 0xd8f1ffd82b4214a0ULL, }, + { 0x4f6a4dcc31624e40ULL, 0xf167d85e427ba00cULL, }, + { 0x4fbe4d6331934e08ULL, 0xf1f7d81a423fa0fcULL, }, + { 0x4f5a4daa31cf4e80ULL, 0xf1d8d8ff422ba014ULL, }, + { 0x4f4f4d4d31314e4eULL, 0xf1f1d8d84242a0a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_d.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_d.c new file mode 100644 index 0000000000..83239949af --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVEV.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "*ILVEV.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0xe38e38e38e38e38eULL, }, /* 48 */ + { 0x0000000000000000ULL, 0xe38e38e38e38e38eULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555555ULL, 0xe38e38e38e38e38eULL, }, + { 0xccccccccccccccccULL, 0xe38e38e38e38e38eULL, }, + { 0x3333333333333333ULL, 0xe38e38e38e38e38eULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x1c71c71c71c71c71ULL, 0xe38e38e38e38e38eULL, }, + { 0xffffffffffffffffULL, 0x1c71c71c71c71c71ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x1c71c71c71c71c71ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x1c71c71c71c71c71ULL, }, + { 0x5555555555555555ULL, 0x1c71c71c71c71c71ULL, }, + { 0xccccccccccccccccULL, 0x1c71c71c71c71c71ULL, }, + { 0x3333333333333333ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe38e38e38e38e38eULL, 0x1c71c71c71c71c71ULL, }, + { 0x1c71c71c71c71c71ULL, 0x1c71c71c71c71c71ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x886ae6cc28625540ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x886ae6cc28625540ULL, }, + { 0x704f164d5e31e24eULL, 0x886ae6cc28625540ULL, }, + { 0x886ae6cc28625540ULL, 0xfbbe00634d93c708ULL, }, + { 0xfbbe00634d93c708ULL, 0xfbbe00634d93c708ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xfbbe00634d93c708ULL, }, + { 0x704f164d5e31e24eULL, 0xfbbe00634d93c708ULL, }, + { 0x886ae6cc28625540ULL, 0xac5aaeaab9cf8b80ULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x704f164d5e31e24eULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x886ae6cc28625540ULL, 0x704f164d5e31e24eULL, }, + { 0xfbbe00634d93c708ULL, 0x704f164d5e31e24eULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x704f164d5e31e24eULL, }, + { 0x704f164d5e31e24eULL, 0x704f164d5e31e24eULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_h.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_h.c new file mode 100644 index 0000000000..3f6fc265d2 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVEV.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "*ILVEV.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffff0000ffff0000ULL, 0xffff0000ffff0000ULL, }, + { 0xffffaaaaffffaaaaULL, 0xffffaaaaffffaaaaULL, }, + { 0xffff5555ffff5555ULL, 0xffff5555ffff5555ULL, }, + { 0xffffccccffffccccULL, 0xffffccccffffccccULL, }, + { 0xffff3333ffff3333ULL, 0xffff3333ffff3333ULL, }, + { 0xffff38e3ffffe38eULL, 0xffff8e38ffff38e3ULL, }, + { 0xffffc71cffff1c71ULL, 0xffff71c7ffffc71cULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0x0000cccc0000ccccULL, 0x0000cccc0000ccccULL, }, + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x000038e30000e38eULL, 0x00008e38000038e3ULL, }, + { 0x0000c71c00001c71ULL, 0x000071c70000c71cULL, }, + { 0xaaaaffffaaaaffffULL, 0xaaaaffffaaaaffffULL, }, /* 16 */ + { 0xaaaa0000aaaa0000ULL, 0xaaaa0000aaaa0000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaa5555aaaa5555ULL, 0xaaaa5555aaaa5555ULL, }, + { 0xaaaaccccaaaaccccULL, 0xaaaaccccaaaaccccULL, }, + { 0xaaaa3333aaaa3333ULL, 0xaaaa3333aaaa3333ULL, }, + { 0xaaaa38e3aaaae38eULL, 0xaaaa8e38aaaa38e3ULL, }, + { 0xaaaac71caaaa1c71ULL, 0xaaaa71c7aaaac71cULL, }, + { 0x5555ffff5555ffffULL, 0x5555ffff5555ffffULL, }, /* 24 */ + { 0x5555000055550000ULL, 0x5555000055550000ULL, }, + { 0x5555aaaa5555aaaaULL, 0x5555aaaa5555aaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555cccc5555ccccULL, 0x5555cccc5555ccccULL, }, + { 0x5555333355553333ULL, 0x5555333355553333ULL, }, + { 0x555538e35555e38eULL, 0x55558e38555538e3ULL, }, + { 0x5555c71c55551c71ULL, 0x555571c75555c71cULL, }, + { 0xccccffffccccffffULL, 0xccccffffccccffffULL, }, /* 32 */ + { 0xcccc0000cccc0000ULL, 0xcccc0000cccc0000ULL, }, + { 0xccccaaaaccccaaaaULL, 0xccccaaaaccccaaaaULL, }, + { 0xcccc5555cccc5555ULL, 0xcccc5555cccc5555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccc3333cccc3333ULL, 0xcccc3333cccc3333ULL, }, + { 0xcccc38e3cccce38eULL, 0xcccc8e38cccc38e3ULL, }, + { 0xccccc71ccccc1c71ULL, 0xcccc71c7ccccc71cULL, }, + { 0x3333ffff3333ffffULL, 0x3333ffff3333ffffULL, }, /* 40 */ + { 0x3333000033330000ULL, 0x3333000033330000ULL, }, + { 0x3333aaaa3333aaaaULL, 0x3333aaaa3333aaaaULL, }, + { 0x3333555533335555ULL, 0x3333555533335555ULL, }, + { 0x3333cccc3333ccccULL, 0x3333cccc3333ccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x333338e33333e38eULL, 0x33338e38333338e3ULL, }, + { 0x3333c71c33331c71ULL, 0x333371c73333c71cULL, }, + { 0x38e3ffffe38effffULL, 0x8e38ffff38e3ffffULL, }, /* 48 */ + { 0x38e30000e38e0000ULL, 0x8e38000038e30000ULL, }, + { 0x38e3aaaae38eaaaaULL, 0x8e38aaaa38e3aaaaULL, }, + { 0x38e35555e38e5555ULL, 0x8e38555538e35555ULL, }, + { 0x38e3cccce38eccccULL, 0x8e38cccc38e3ccccULL, }, + { 0x38e33333e38e3333ULL, 0x8e38333338e33333ULL, }, + { 0x38e338e3e38ee38eULL, 0x8e388e3838e338e3ULL, }, + { 0x38e3c71ce38e1c71ULL, 0x8e3871c738e3c71cULL, }, + { 0xc71cffff1c71ffffULL, 0x71c7ffffc71cffffULL, }, /* 56 */ + { 0xc71c00001c710000ULL, 0x71c70000c71c0000ULL, }, + { 0xc71caaaa1c71aaaaULL, 0x71c7aaaac71caaaaULL, }, + { 0xc71c55551c715555ULL, 0x71c75555c71c5555ULL, }, + { 0xc71ccccc1c71ccccULL, 0x71c7ccccc71cccccULL, }, + { 0xc71c33331c713333ULL, 0x71c73333c71c3333ULL, }, + { 0xc71c38e31c71e38eULL, 0x71c78e38c71c38e3ULL, }, + { 0xc71cc71c1c711c71ULL, 0x71c771c7c71cc71cULL, }, + { 0xe6cce6cc55405540ULL, 0x0b5e0b5eb00cb00cULL, }, /* 64 */ + { 0xe6cc00635540c708ULL, 0x0b5ebb1ab00c52fcULL, }, + { 0xe6ccaeaa55408b80ULL, 0x0b5ec6ffb00c2514ULL, }, + { 0xe6cc164d5540e24eULL, 0x0b5e88d8b00ce2a0ULL, }, + { 0x0063e6ccc7085540ULL, 0xbb1a0b5e52fcb00cULL, }, + { 0x00630063c708c708ULL, 0xbb1abb1a52fc52fcULL, }, + { 0x0063aeaac7088b80ULL, 0xbb1ac6ff52fc2514ULL, }, + { 0x0063164dc708e24eULL, 0xbb1a88d852fce2a0ULL, }, + { 0xaeaae6cc8b805540ULL, 0xc6ff0b5e2514b00cULL, }, /* 72 */ + { 0xaeaa00638b80c708ULL, 0xc6ffbb1a251452fcULL, }, + { 0xaeaaaeaa8b808b80ULL, 0xc6ffc6ff25142514ULL, }, + { 0xaeaa164d8b80e24eULL, 0xc6ff88d82514e2a0ULL, }, + { 0x164de6cce24e5540ULL, 0x88d80b5ee2a0b00cULL, }, + { 0x164d0063e24ec708ULL, 0x88d8bb1ae2a052fcULL, }, + { 0x164daeaae24e8b80ULL, 0x88d8c6ffe2a02514ULL, }, + { 0x164d164de24ee24eULL, 0x88d888d8e2a0e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_w.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_w.c new file mode 100644 index 0000000000..30d2e3802d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvev_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVEV.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVEV.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffff00000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0xffffffff55555555ULL, 0xffffffff55555555ULL, }, + { 0xffffffffccccccccULL, 0xffffffffccccccccULL, }, + { 0xffffffff33333333ULL, 0xffffffff33333333ULL, }, + { 0xffffffff8e38e38eULL, 0xffffffffe38e38e3ULL, }, + { 0xffffffff71c71c71ULL, 0xffffffff1c71c71cULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0x00000000ccccccccULL, 0x00000000ccccccccULL, }, + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0x000000008e38e38eULL, 0x00000000e38e38e3ULL, }, + { 0x0000000071c71c71ULL, 0x000000001c71c71cULL, }, + { 0xaaaaaaaaffffffffULL, 0xaaaaaaaaffffffffULL, }, /* 16 */ + { 0xaaaaaaaa00000000ULL, 0xaaaaaaaa00000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa55555555ULL, 0xaaaaaaaa55555555ULL, }, + { 0xaaaaaaaaccccccccULL, 0xaaaaaaaaccccccccULL, }, + { 0xaaaaaaaa33333333ULL, 0xaaaaaaaa33333333ULL, }, + { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaae38e38e3ULL, }, + { 0xaaaaaaaa71c71c71ULL, 0xaaaaaaaa1c71c71cULL, }, + { 0x55555555ffffffffULL, 0x55555555ffffffffULL, }, /* 24 */ + { 0x5555555500000000ULL, 0x5555555500000000ULL, }, + { 0x55555555aaaaaaaaULL, 0x55555555aaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55555555ccccccccULL, 0x55555555ccccccccULL, }, + { 0x5555555533333333ULL, 0x5555555533333333ULL, }, + { 0x555555558e38e38eULL, 0x55555555e38e38e3ULL, }, + { 0x5555555571c71c71ULL, 0x555555551c71c71cULL, }, + { 0xccccccccffffffffULL, 0xccccccccffffffffULL, }, /* 32 */ + { 0xcccccccc00000000ULL, 0xcccccccc00000000ULL, }, + { 0xccccccccaaaaaaaaULL, 0xccccccccaaaaaaaaULL, }, + { 0xcccccccc55555555ULL, 0xcccccccc55555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc33333333ULL, 0xcccccccc33333333ULL, }, + { 0xcccccccc8e38e38eULL, 0xcccccccce38e38e3ULL, }, + { 0xcccccccc71c71c71ULL, 0xcccccccc1c71c71cULL, }, + { 0x33333333ffffffffULL, 0x33333333ffffffffULL, }, /* 40 */ + { 0x3333333300000000ULL, 0x3333333300000000ULL, }, + { 0x33333333aaaaaaaaULL, 0x33333333aaaaaaaaULL, }, + { 0x3333333355555555ULL, 0x3333333355555555ULL, }, + { 0x33333333ccccccccULL, 0x33333333ccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x333333338e38e38eULL, 0x33333333e38e38e3ULL, }, + { 0x3333333371c71c71ULL, 0x333333331c71c71cULL, }, + { 0x8e38e38effffffffULL, 0xe38e38e3ffffffffULL, }, /* 48 */ + { 0x8e38e38e00000000ULL, 0xe38e38e300000000ULL, }, + { 0x8e38e38eaaaaaaaaULL, 0xe38e38e3aaaaaaaaULL, }, + { 0x8e38e38e55555555ULL, 0xe38e38e355555555ULL, }, + { 0x8e38e38eccccccccULL, 0xe38e38e3ccccccccULL, }, + { 0x8e38e38e33333333ULL, 0xe38e38e333333333ULL, }, + { 0x8e38e38e8e38e38eULL, 0xe38e38e3e38e38e3ULL, }, + { 0x8e38e38e71c71c71ULL, 0xe38e38e31c71c71cULL, }, + { 0x71c71c71ffffffffULL, 0x1c71c71cffffffffULL, }, /* 56 */ + { 0x71c71c7100000000ULL, 0x1c71c71c00000000ULL, }, + { 0x71c71c71aaaaaaaaULL, 0x1c71c71caaaaaaaaULL, }, + { 0x71c71c7155555555ULL, 0x1c71c71c55555555ULL, }, + { 0x71c71c71ccccccccULL, 0x1c71c71cccccccccULL, }, + { 0x71c71c7133333333ULL, 0x1c71c71c33333333ULL, }, + { 0x71c71c718e38e38eULL, 0x1c71c71ce38e38e3ULL, }, + { 0x71c71c7171c71c71ULL, 0x1c71c71c1c71c71cULL, }, + { 0x2862554028625540ULL, 0xfe7bb00cfe7bb00cULL, }, /* 64 */ + { 0x286255404d93c708ULL, 0xfe7bb00c153f52fcULL, }, + { 0x28625540b9cf8b80ULL, 0xfe7bb00cab2b2514ULL, }, + { 0x286255405e31e24eULL, 0xfe7bb00ca942e2a0ULL, }, + { 0x4d93c70828625540ULL, 0x153f52fcfe7bb00cULL, }, + { 0x4d93c7084d93c708ULL, 0x153f52fc153f52fcULL, }, + { 0x4d93c708b9cf8b80ULL, 0x153f52fcab2b2514ULL, }, + { 0x4d93c7085e31e24eULL, 0x153f52fca942e2a0ULL, }, + { 0xb9cf8b8028625540ULL, 0xab2b2514fe7bb00cULL, }, /* 72 */ + { 0xb9cf8b804d93c708ULL, 0xab2b2514153f52fcULL, }, + { 0xb9cf8b80b9cf8b80ULL, 0xab2b2514ab2b2514ULL, }, + { 0xb9cf8b805e31e24eULL, 0xab2b2514a942e2a0ULL, }, + { 0x5e31e24e28625540ULL, 0xa942e2a0fe7bb00cULL, }, + { 0x5e31e24e4d93c708ULL, 0xa942e2a0153f52fcULL, }, + { 0x5e31e24eb9cf8b80ULL, 0xa942e2a0ab2b2514ULL, }, + { 0x5e31e24e5e31e24eULL, 0xa942e2a0a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVEV_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_b.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_b.c new file mode 100644 index 0000000000..c771287a71 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVL.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVL.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xff00ff00ff00ff00ULL, 0xff00ff00ff00ff00ULL, }, + { 0xffaaffaaffaaffaaULL, 0xffaaffaaffaaffaaULL, }, + { 0xff55ff55ff55ff55ULL, 0xff55ff55ff55ff55ULL, }, + { 0xffccffccffccffccULL, 0xffccffccffccffccULL, }, + { 0xff33ff33ff33ff33ULL, 0xff33ff33ff33ff33ULL, }, + { 0xffe3ff8eff38ffe3ULL, 0xff38ffe3ff8eff38ULL, }, + { 0xff1cff71ffc7ff1cULL, 0xffc7ff1cff71ffc7ULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0x00cc00cc00cc00ccULL, 0x00cc00cc00cc00ccULL, }, + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0x00e3008e003800e3ULL, 0x003800e3008e0038ULL, }, + { 0x001c007100c7001cULL, 0x00c7001c007100c7ULL, }, + { 0xaaffaaffaaffaaffULL, 0xaaffaaffaaffaaffULL, }, /* 16 */ + { 0xaa00aa00aa00aa00ULL, 0xaa00aa00aa00aa00ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaa55aa55aa55aa55ULL, 0xaa55aa55aa55aa55ULL, }, + { 0xaaccaaccaaccaaccULL, 0xaaccaaccaaccaaccULL, }, + { 0xaa33aa33aa33aa33ULL, 0xaa33aa33aa33aa33ULL, }, + { 0xaae3aa8eaa38aae3ULL, 0xaa38aae3aa8eaa38ULL, }, + { 0xaa1caa71aac7aa1cULL, 0xaac7aa1caa71aac7ULL, }, + { 0x55ff55ff55ff55ffULL, 0x55ff55ff55ff55ffULL, }, /* 24 */ + { 0x5500550055005500ULL, 0x5500550055005500ULL, }, + { 0x55aa55aa55aa55aaULL, 0x55aa55aa55aa55aaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55cc55cc55cc55ccULL, 0x55cc55cc55cc55ccULL, }, + { 0x5533553355335533ULL, 0x5533553355335533ULL, }, + { 0x55e3558e553855e3ULL, 0x553855e3558e5538ULL, }, + { 0x551c557155c7551cULL, 0x55c7551c557155c7ULL, }, + { 0xccffccffccffccffULL, 0xccffccffccffccffULL, }, /* 32 */ + { 0xcc00cc00cc00cc00ULL, 0xcc00cc00cc00cc00ULL, }, + { 0xccaaccaaccaaccaaULL, 0xccaaccaaccaaccaaULL, }, + { 0xcc55cc55cc55cc55ULL, 0xcc55cc55cc55cc55ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcc33cc33cc33cc33ULL, 0xcc33cc33cc33cc33ULL, }, + { 0xcce3cc8ecc38cce3ULL, 0xcc38cce3cc8ecc38ULL, }, + { 0xcc1ccc71ccc7cc1cULL, 0xccc7cc1ccc71ccc7ULL, }, + { 0x33ff33ff33ff33ffULL, 0x33ff33ff33ff33ffULL, }, /* 40 */ + { 0x3300330033003300ULL, 0x3300330033003300ULL, }, + { 0x33aa33aa33aa33aaULL, 0x33aa33aa33aa33aaULL, }, + { 0x3355335533553355ULL, 0x3355335533553355ULL, }, + { 0x33cc33cc33cc33ccULL, 0x33cc33cc33cc33ccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x33e3338e333833e3ULL, 0x333833e3338e3338ULL, }, + { 0x331c337133c7331cULL, 0x33c7331c337133c7ULL, }, + { 0xe3ff8eff38ffe3ffULL, 0x38ffe3ff8eff38ffULL, }, /* 48 */ + { 0xe3008e003800e300ULL, 0x3800e3008e003800ULL, }, + { 0xe3aa8eaa38aae3aaULL, 0x38aae3aa8eaa38aaULL, }, + { 0xe3558e553855e355ULL, 0x3855e3558e553855ULL, }, + { 0xe3cc8ecc38cce3ccULL, 0x38cce3cc8ecc38ccULL, }, + { 0xe3338e333833e333ULL, 0x3833e3338e333833ULL, }, + { 0xe3e38e8e3838e3e3ULL, 0x3838e3e38e8e3838ULL, }, + { 0xe31c8e7138c7e31cULL, 0x38c7e31c8e7138c7ULL, }, + { 0x1cff71ffc7ff1cffULL, 0xc7ff1cff71ffc7ffULL, }, /* 56 */ + { 0x1c007100c7001c00ULL, 0xc7001c007100c700ULL, }, + { 0x1caa71aac7aa1caaULL, 0xc7aa1caa71aac7aaULL, }, + { 0x1c557155c7551c55ULL, 0xc7551c557155c755ULL, }, + { 0x1ccc71ccc7cc1cccULL, 0xc7cc1ccc71ccc7ccULL, }, + { 0x1c337133c7331c33ULL, 0xc7331c337133c733ULL, }, + { 0x1ce3718ec7381ce3ULL, 0xc7381ce3718ec738ULL, }, + { 0x1c1c7171c7c71c1cULL, 0xc7c71c1c7171c7c7ULL, }, + { 0xfefe7b7bb0b00c0cULL, 0x4b4b67670b0b5e5eULL, }, /* 64 */ + { 0xfe157b3fb0520cfcULL, 0x4b1267f70bbb5e1aULL, }, + { 0xfeab7b2bb0250c14ULL, 0x4b2767d80bc65effULL, }, + { 0xfea97b42b0e20ca0ULL, 0x4b8d67f10b885ed8ULL, }, + { 0x15fe3f7b52b0fc0cULL, 0x124bf767bb0b1a5eULL, }, + { 0x15153f3f5252fcfcULL, 0x1212f7f7bbbb1a1aULL, }, + { 0x15ab3f2b5225fc14ULL, 0x1227f7d8bbc61affULL, }, + { 0x15a93f4252e2fca0ULL, 0x128df7f1bb881ad8ULL, }, + { 0xabfe2b7b25b0140cULL, 0x274bd867c60bff5eULL, }, /* 72 */ + { 0xab152b3f255214fcULL, 0x2712d8f7c6bbff1aULL, }, + { 0xabab2b2b25251414ULL, 0x2727d8d8c6c6ffffULL, }, + { 0xaba92b4225e214a0ULL, 0x278dd8f1c688ffd8ULL, }, + { 0xa9fe427be2b0a00cULL, 0x8d4bf167880bd85eULL, }, + { 0xa915423fe252a0fcULL, 0x8d12f1f788bbd81aULL, }, + { 0xa9ab422be225a014ULL, 0x8d27f1d888c6d8ffULL, }, + { 0xa9a94242e2e2a0a0ULL, 0x8d8df1f18888d8d8ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_d.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_d.c new file mode 100644 index 0000000000..b7d5fcdc18 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVL.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVL.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e3ULL, 0xffffffffffffffffULL, }, + { 0xc71c71c71c71c71cULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x38e38e38e38e38e3ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x38e38e38e38e38e3ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xc71c71c71c71c71cULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x38e38e38e38e38e3ULL, 0x5555555555555555ULL, }, + { 0xc71c71c71c71c71cULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x38e38e38e38e38e3ULL, 0xccccccccccccccccULL, }, + { 0xc71c71c71c71c71cULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x38e38e38e38e38e3ULL, 0x3333333333333333ULL, }, + { 0xc71c71c71c71c71cULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x38e38e38e38e38e3ULL, }, + { 0xc71c71c71c71c71cULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71cULL, 0xc71c71c71c71c71cULL, }, + { 0x4b670b5efe7bb00cULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x12f7bb1a153f52fcULL, 0x4b670b5efe7bb00cULL, }, + { 0x27d8c6ffab2b2514ULL, 0x4b670b5efe7bb00cULL, }, + { 0x8df188d8a942e2a0ULL, 0x4b670b5efe7bb00cULL, }, + { 0x4b670b5efe7bb00cULL, 0x12f7bb1a153f52fcULL, }, + { 0x12f7bb1a153f52fcULL, 0x12f7bb1a153f52fcULL, }, + { 0x27d8c6ffab2b2514ULL, 0x12f7bb1a153f52fcULL, }, + { 0x8df188d8a942e2a0ULL, 0x12f7bb1a153f52fcULL, }, + { 0x4b670b5efe7bb00cULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0x12f7bb1a153f52fcULL, 0x27d8c6ffab2b2514ULL, }, + { 0x27d8c6ffab2b2514ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8df188d8a942e2a0ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x4b670b5efe7bb00cULL, 0x8df188d8a942e2a0ULL, }, + { 0x12f7bb1a153f52fcULL, 0x8df188d8a942e2a0ULL, }, + { 0x27d8c6ffab2b2514ULL, 0x8df188d8a942e2a0ULL, }, + { 0x8df188d8a942e2a0ULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_h.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_h.c new file mode 100644 index 0000000000..af72876236 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVL.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVL.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffff0000ffff0000ULL, 0xffff0000ffff0000ULL, }, + { 0xffffaaaaffffaaaaULL, 0xffffaaaaffffaaaaULL, }, + { 0xffff5555ffff5555ULL, 0xffff5555ffff5555ULL, }, + { 0xffffccccffffccccULL, 0xffffccccffffccccULL, }, + { 0xffff3333ffff3333ULL, 0xffff3333ffff3333ULL, }, + { 0xffffe38effff38e3ULL, 0xffff38e3ffff8e38ULL, }, + { 0xffff1c71ffffc71cULL, 0xffffc71cffff71c7ULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0x0000cccc0000ccccULL, 0x0000cccc0000ccccULL, }, + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x0000e38e000038e3ULL, 0x000038e300008e38ULL, }, + { 0x00001c710000c71cULL, 0x0000c71c000071c7ULL, }, + { 0xaaaaffffaaaaffffULL, 0xaaaaffffaaaaffffULL, }, /* 16 */ + { 0xaaaa0000aaaa0000ULL, 0xaaaa0000aaaa0000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaa5555aaaa5555ULL, 0xaaaa5555aaaa5555ULL, }, + { 0xaaaaccccaaaaccccULL, 0xaaaaccccaaaaccccULL, }, + { 0xaaaa3333aaaa3333ULL, 0xaaaa3333aaaa3333ULL, }, + { 0xaaaae38eaaaa38e3ULL, 0xaaaa38e3aaaa8e38ULL, }, + { 0xaaaa1c71aaaac71cULL, 0xaaaac71caaaa71c7ULL, }, + { 0x5555ffff5555ffffULL, 0x5555ffff5555ffffULL, }, /* 24 */ + { 0x5555000055550000ULL, 0x5555000055550000ULL, }, + { 0x5555aaaa5555aaaaULL, 0x5555aaaa5555aaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555cccc5555ccccULL, 0x5555cccc5555ccccULL, }, + { 0x5555333355553333ULL, 0x5555333355553333ULL, }, + { 0x5555e38e555538e3ULL, 0x555538e355558e38ULL, }, + { 0x55551c715555c71cULL, 0x5555c71c555571c7ULL, }, + { 0xccccffffccccffffULL, 0xccccffffccccffffULL, }, /* 32 */ + { 0xcccc0000cccc0000ULL, 0xcccc0000cccc0000ULL, }, + { 0xccccaaaaccccaaaaULL, 0xccccaaaaccccaaaaULL, }, + { 0xcccc5555cccc5555ULL, 0xcccc5555cccc5555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccc3333cccc3333ULL, 0xcccc3333cccc3333ULL, }, + { 0xcccce38ecccc38e3ULL, 0xcccc38e3cccc8e38ULL, }, + { 0xcccc1c71ccccc71cULL, 0xccccc71ccccc71c7ULL, }, + { 0x3333ffff3333ffffULL, 0x3333ffff3333ffffULL, }, /* 40 */ + { 0x3333000033330000ULL, 0x3333000033330000ULL, }, + { 0x3333aaaa3333aaaaULL, 0x3333aaaa3333aaaaULL, }, + { 0x3333555533335555ULL, 0x3333555533335555ULL, }, + { 0x3333cccc3333ccccULL, 0x3333cccc3333ccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333e38e333338e3ULL, 0x333338e333338e38ULL, }, + { 0x33331c713333c71cULL, 0x3333c71c333371c7ULL, }, + { 0xe38effff38e3ffffULL, 0x38e3ffff8e38ffffULL, }, /* 48 */ + { 0xe38e000038e30000ULL, 0x38e300008e380000ULL, }, + { 0xe38eaaaa38e3aaaaULL, 0x38e3aaaa8e38aaaaULL, }, + { 0xe38e555538e35555ULL, 0x38e355558e385555ULL, }, + { 0xe38ecccc38e3ccccULL, 0x38e3cccc8e38ccccULL, }, + { 0xe38e333338e33333ULL, 0x38e333338e383333ULL, }, + { 0xe38ee38e38e338e3ULL, 0x38e338e38e388e38ULL, }, + { 0xe38e1c7138e3c71cULL, 0x38e3c71c8e3871c7ULL, }, + { 0x1c71ffffc71cffffULL, 0xc71cffff71c7ffffULL, }, /* 56 */ + { 0x1c710000c71c0000ULL, 0xc71c000071c70000ULL, }, + { 0x1c71aaaac71caaaaULL, 0xc71caaaa71c7aaaaULL, }, + { 0x1c715555c71c5555ULL, 0xc71c555571c75555ULL, }, + { 0x1c71ccccc71cccccULL, 0xc71ccccc71c7ccccULL, }, + { 0x1c713333c71c3333ULL, 0xc71c333371c73333ULL, }, + { 0x1c71e38ec71c38e3ULL, 0xc71c38e371c78e38ULL, }, + { 0x1c711c71c71cc71cULL, 0xc71cc71c71c771c7ULL, }, + { 0xfe7bfe7bb00cb00cULL, 0x4b674b670b5e0b5eULL, }, /* 64 */ + { 0xfe7b153fb00c52fcULL, 0x4b6712f70b5ebb1aULL, }, + { 0xfe7bab2bb00c2514ULL, 0x4b6727d80b5ec6ffULL, }, + { 0xfe7ba942b00ce2a0ULL, 0x4b678df10b5e88d8ULL, }, + { 0x153ffe7b52fcb00cULL, 0x12f74b67bb1a0b5eULL, }, + { 0x153f153f52fc52fcULL, 0x12f712f7bb1abb1aULL, }, + { 0x153fab2b52fc2514ULL, 0x12f727d8bb1ac6ffULL, }, + { 0x153fa94252fce2a0ULL, 0x12f78df1bb1a88d8ULL, }, + { 0xab2bfe7b2514b00cULL, 0x27d84b67c6ff0b5eULL, }, /* 72 */ + { 0xab2b153f251452fcULL, 0x27d812f7c6ffbb1aULL, }, + { 0xab2bab2b25142514ULL, 0x27d827d8c6ffc6ffULL, }, + { 0xab2ba9422514e2a0ULL, 0x27d88df1c6ff88d8ULL, }, + { 0xa942fe7be2a0b00cULL, 0x8df14b6788d80b5eULL, }, + { 0xa942153fe2a052fcULL, 0x8df112f788d8bb1aULL, }, + { 0xa942ab2be2a02514ULL, 0x8df127d888d8c6ffULL, }, + { 0xa942a942e2a0e2a0ULL, 0x8df18df188d888d8ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_w.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_w.c new file mode 100644 index 0000000000..e06c9d94ca --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvl_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVL.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVL.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffff00000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0xffffffff55555555ULL, 0xffffffff55555555ULL, }, + { 0xffffffffccccccccULL, 0xffffffffccccccccULL, }, + { 0xffffffff33333333ULL, 0xffffffff33333333ULL, }, + { 0xffffffffe38e38e3ULL, 0xffffffff38e38e38ULL, }, + { 0xffffffff1c71c71cULL, 0xffffffffc71c71c7ULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0x00000000ccccccccULL, 0x00000000ccccccccULL, }, + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0x00000000e38e38e3ULL, 0x0000000038e38e38ULL, }, + { 0x000000001c71c71cULL, 0x00000000c71c71c7ULL, }, + { 0xaaaaaaaaffffffffULL, 0xaaaaaaaaffffffffULL, }, /* 16 */ + { 0xaaaaaaaa00000000ULL, 0xaaaaaaaa00000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa55555555ULL, 0xaaaaaaaa55555555ULL, }, + { 0xaaaaaaaaccccccccULL, 0xaaaaaaaaccccccccULL, }, + { 0xaaaaaaaa33333333ULL, 0xaaaaaaaa33333333ULL, }, + { 0xaaaaaaaae38e38e3ULL, 0xaaaaaaaa38e38e38ULL, }, + { 0xaaaaaaaa1c71c71cULL, 0xaaaaaaaac71c71c7ULL, }, + { 0x55555555ffffffffULL, 0x55555555ffffffffULL, }, /* 24 */ + { 0x5555555500000000ULL, 0x5555555500000000ULL, }, + { 0x55555555aaaaaaaaULL, 0x55555555aaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55555555ccccccccULL, 0x55555555ccccccccULL, }, + { 0x5555555533333333ULL, 0x5555555533333333ULL, }, + { 0x55555555e38e38e3ULL, 0x5555555538e38e38ULL, }, + { 0x555555551c71c71cULL, 0x55555555c71c71c7ULL, }, + { 0xccccccccffffffffULL, 0xccccccccffffffffULL, }, /* 32 */ + { 0xcccccccc00000000ULL, 0xcccccccc00000000ULL, }, + { 0xccccccccaaaaaaaaULL, 0xccccccccaaaaaaaaULL, }, + { 0xcccccccc55555555ULL, 0xcccccccc55555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc33333333ULL, 0xcccccccc33333333ULL, }, + { 0xcccccccce38e38e3ULL, 0xcccccccc38e38e38ULL, }, + { 0xcccccccc1c71c71cULL, 0xccccccccc71c71c7ULL, }, + { 0x33333333ffffffffULL, 0x33333333ffffffffULL, }, /* 40 */ + { 0x3333333300000000ULL, 0x3333333300000000ULL, }, + { 0x33333333aaaaaaaaULL, 0x33333333aaaaaaaaULL, }, + { 0x3333333355555555ULL, 0x3333333355555555ULL, }, + { 0x33333333ccccccccULL, 0x33333333ccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x33333333e38e38e3ULL, 0x3333333338e38e38ULL, }, + { 0x333333331c71c71cULL, 0x33333333c71c71c7ULL, }, + { 0xe38e38e3ffffffffULL, 0x38e38e38ffffffffULL, }, /* 48 */ + { 0xe38e38e300000000ULL, 0x38e38e3800000000ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0x38e38e38aaaaaaaaULL, }, + { 0xe38e38e355555555ULL, 0x38e38e3855555555ULL, }, + { 0xe38e38e3ccccccccULL, 0x38e38e38ccccccccULL, }, + { 0xe38e38e333333333ULL, 0x38e38e3833333333ULL, }, + { 0xe38e38e3e38e38e3ULL, 0x38e38e3838e38e38ULL, }, + { 0xe38e38e31c71c71cULL, 0x38e38e38c71c71c7ULL, }, + { 0x1c71c71cffffffffULL, 0xc71c71c7ffffffffULL, }, /* 56 */ + { 0x1c71c71c00000000ULL, 0xc71c71c700000000ULL, }, + { 0x1c71c71caaaaaaaaULL, 0xc71c71c7aaaaaaaaULL, }, + { 0x1c71c71c55555555ULL, 0xc71c71c755555555ULL, }, + { 0x1c71c71cccccccccULL, 0xc71c71c7ccccccccULL, }, + { 0x1c71c71c33333333ULL, 0xc71c71c733333333ULL, }, + { 0x1c71c71ce38e38e3ULL, 0xc71c71c738e38e38ULL, }, + { 0x1c71c71c1c71c71cULL, 0xc71c71c7c71c71c7ULL, }, + { 0xfe7bb00cfe7bb00cULL, 0x4b670b5e4b670b5eULL, }, /* 64 */ + { 0xfe7bb00c153f52fcULL, 0x4b670b5e12f7bb1aULL, }, + { 0xfe7bb00cab2b2514ULL, 0x4b670b5e27d8c6ffULL, }, + { 0xfe7bb00ca942e2a0ULL, 0x4b670b5e8df188d8ULL, }, + { 0x153f52fcfe7bb00cULL, 0x12f7bb1a4b670b5eULL, }, + { 0x153f52fc153f52fcULL, 0x12f7bb1a12f7bb1aULL, }, + { 0x153f52fcab2b2514ULL, 0x12f7bb1a27d8c6ffULL, }, + { 0x153f52fca942e2a0ULL, 0x12f7bb1a8df188d8ULL, }, + { 0xab2b2514fe7bb00cULL, 0x27d8c6ff4b670b5eULL, }, /* 72 */ + { 0xab2b2514153f52fcULL, 0x27d8c6ff12f7bb1aULL, }, + { 0xab2b2514ab2b2514ULL, 0x27d8c6ff27d8c6ffULL, }, + { 0xab2b2514a942e2a0ULL, 0x27d8c6ff8df188d8ULL, }, + { 0xa942e2a0fe7bb00cULL, 0x8df188d84b670b5eULL, }, + { 0xa942e2a0153f52fcULL, 0x8df188d812f7bb1aULL, }, + { 0xa942e2a0ab2b2514ULL, 0x8df188d827d8c6ffULL, }, + { 0xa942e2a0a942e2a0ULL, 0x8df188d88df188d8ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVL_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_b.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_b.c new file mode 100644 index 0000000000..8e7f1c4706 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVOD.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVOD.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xff00ff00ff00ff00ULL, 0xff00ff00ff00ff00ULL, }, + { 0xffaaffaaffaaffaaULL, 0xffaaffaaffaaffaaULL, }, + { 0xff55ff55ff55ff55ULL, 0xff55ff55ff55ff55ULL, }, + { 0xffccffccffccffccULL, 0xffccffccffccffccULL, }, + { 0xff33ff33ff33ff33ULL, 0xff33ff33ff33ff33ULL, }, + { 0xffe3ff38ff8effe3ULL, 0xff38ff8effe3ff38ULL, }, + { 0xff1cffc7ff71ff1cULL, 0xffc7ff71ff1cffc7ULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0x00cc00cc00cc00ccULL, 0x00cc00cc00cc00ccULL, }, + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0x00e30038008e00e3ULL, 0x0038008e00e30038ULL, }, + { 0x001c00c70071001cULL, 0x00c70071001c00c7ULL, }, + { 0xaaffaaffaaffaaffULL, 0xaaffaaffaaffaaffULL, }, /* 16 */ + { 0xaa00aa00aa00aa00ULL, 0xaa00aa00aa00aa00ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaa55aa55aa55aa55ULL, 0xaa55aa55aa55aa55ULL, }, + { 0xaaccaaccaaccaaccULL, 0xaaccaaccaaccaaccULL, }, + { 0xaa33aa33aa33aa33ULL, 0xaa33aa33aa33aa33ULL, }, + { 0xaae3aa38aa8eaae3ULL, 0xaa38aa8eaae3aa38ULL, }, + { 0xaa1caac7aa71aa1cULL, 0xaac7aa71aa1caac7ULL, }, + { 0x55ff55ff55ff55ffULL, 0x55ff55ff55ff55ffULL, }, /* 24 */ + { 0x5500550055005500ULL, 0x5500550055005500ULL, }, + { 0x55aa55aa55aa55aaULL, 0x55aa55aa55aa55aaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55cc55cc55cc55ccULL, 0x55cc55cc55cc55ccULL, }, + { 0x5533553355335533ULL, 0x5533553355335533ULL, }, + { 0x55e35538558e55e3ULL, 0x5538558e55e35538ULL, }, + { 0x551c55c75571551cULL, 0x55c75571551c55c7ULL, }, + { 0xccffccffccffccffULL, 0xccffccffccffccffULL, }, /* 32 */ + { 0xcc00cc00cc00cc00ULL, 0xcc00cc00cc00cc00ULL, }, + { 0xccaaccaaccaaccaaULL, 0xccaaccaaccaaccaaULL, }, + { 0xcc55cc55cc55cc55ULL, 0xcc55cc55cc55cc55ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcc33cc33cc33cc33ULL, 0xcc33cc33cc33cc33ULL, }, + { 0xcce3cc38cc8ecce3ULL, 0xcc38cc8ecce3cc38ULL, }, + { 0xcc1cccc7cc71cc1cULL, 0xccc7cc71cc1cccc7ULL, }, + { 0x33ff33ff33ff33ffULL, 0x33ff33ff33ff33ffULL, }, /* 40 */ + { 0x3300330033003300ULL, 0x3300330033003300ULL, }, + { 0x33aa33aa33aa33aaULL, 0x33aa33aa33aa33aaULL, }, + { 0x3355335533553355ULL, 0x3355335533553355ULL, }, + { 0x33cc33cc33cc33ccULL, 0x33cc33cc33cc33ccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x33e33338338e33e3ULL, 0x3338338e33e33338ULL, }, + { 0x331c33c73371331cULL, 0x33c73371331c33c7ULL, }, + { 0xe3ff38ff8effe3ffULL, 0x38ff8effe3ff38ffULL, }, /* 48 */ + { 0xe30038008e00e300ULL, 0x38008e00e3003800ULL, }, + { 0xe3aa38aa8eaae3aaULL, 0x38aa8eaae3aa38aaULL, }, + { 0xe35538558e55e355ULL, 0x38558e55e3553855ULL, }, + { 0xe3cc38cc8ecce3ccULL, 0x38cc8ecce3cc38ccULL, }, + { 0xe33338338e33e333ULL, 0x38338e33e3333833ULL, }, + { 0xe3e338388e8ee3e3ULL, 0x38388e8ee3e33838ULL, }, + { 0xe31c38c78e71e31cULL, 0x38c78e71e31c38c7ULL, }, + { 0x1cffc7ff71ff1cffULL, 0xc7ff71ff1cffc7ffULL, }, /* 56 */ + { 0x1c00c70071001c00ULL, 0xc70071001c00c700ULL, }, + { 0x1caac7aa71aa1caaULL, 0xc7aa71aa1caac7aaULL, }, + { 0x1c55c75571551c55ULL, 0xc75571551c55c755ULL, }, + { 0x1cccc7cc71cc1cccULL, 0xc7cc71cc1cccc7ccULL, }, + { 0x1c33c73371331c33ULL, 0xc73371331c33c733ULL, }, + { 0x1ce3c738718e1ce3ULL, 0xc738718e1ce3c738ULL, }, + { 0x1c1cc7c771711c1cULL, 0xc7c771711c1cc7c7ULL, }, + { 0x8888e6e628285555ULL, 0x4b4b0b0bfefeb0b0ULL, }, /* 64 */ + { 0x88fbe600284d55c7ULL, 0x4b120bbbfe15b052ULL, }, + { 0x88ace6ae28b9558bULL, 0x4b270bc6feabb025ULL, }, + { 0x8870e616285e55e2ULL, 0x4b8d0b88fea9b0e2ULL, }, + { 0xfb8800e64d28c755ULL, 0x124bbb0b15fe52b0ULL, }, + { 0xfbfb00004d4dc7c7ULL, 0x1212bbbb15155252ULL, }, + { 0xfbac00ae4db9c78bULL, 0x1227bbc615ab5225ULL, }, + { 0xfb7000164d5ec7e2ULL, 0x128dbb8815a952e2ULL, }, + { 0xac88aee6b9288b55ULL, 0x274bc60babfe25b0ULL, }, /* 72 */ + { 0xacfbae00b94d8bc7ULL, 0x2712c6bbab152552ULL, }, + { 0xacacaeaeb9b98b8bULL, 0x2727c6c6abab2525ULL, }, + { 0xac70ae16b95e8be2ULL, 0x278dc688aba925e2ULL, }, + { 0x708816e65e28e255ULL, 0x8d4b880ba9fee2b0ULL, }, + { 0x70fb16005e4de2c7ULL, 0x8d1288bba915e252ULL, }, + { 0x70ac16ae5eb9e28bULL, 0x8d2788c6a9abe225ULL, }, + { 0x707016165e5ee2e2ULL, 0x8d8d8888a9a9e2e2ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_d.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_d.c new file mode 100644 index 0000000000..acbd94a68d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVOD.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVOD.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e3ULL, 0xffffffffffffffffULL, }, + { 0xc71c71c71c71c71cULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x38e38e38e38e38e3ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x38e38e38e38e38e3ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xc71c71c71c71c71cULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x38e38e38e38e38e3ULL, 0x5555555555555555ULL, }, + { 0xc71c71c71c71c71cULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x38e38e38e38e38e3ULL, 0xccccccccccccccccULL, }, + { 0xc71c71c71c71c71cULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x38e38e38e38e38e3ULL, 0x3333333333333333ULL, }, + { 0xc71c71c71c71c71cULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x38e38e38e38e38e3ULL, }, + { 0xc71c71c71c71c71cULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71cULL, 0xc71c71c71c71c71cULL, }, + { 0x4b670b5efe7bb00cULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x12f7bb1a153f52fcULL, 0x4b670b5efe7bb00cULL, }, + { 0x27d8c6ffab2b2514ULL, 0x4b670b5efe7bb00cULL, }, + { 0x8df188d8a942e2a0ULL, 0x4b670b5efe7bb00cULL, }, + { 0x4b670b5efe7bb00cULL, 0x12f7bb1a153f52fcULL, }, + { 0x12f7bb1a153f52fcULL, 0x12f7bb1a153f52fcULL, }, + { 0x27d8c6ffab2b2514ULL, 0x12f7bb1a153f52fcULL, }, + { 0x8df188d8a942e2a0ULL, 0x12f7bb1a153f52fcULL, }, + { 0x4b670b5efe7bb00cULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0x12f7bb1a153f52fcULL, 0x27d8c6ffab2b2514ULL, }, + { 0x27d8c6ffab2b2514ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8df188d8a942e2a0ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x4b670b5efe7bb00cULL, 0x8df188d8a942e2a0ULL, }, + { 0x12f7bb1a153f52fcULL, 0x8df188d8a942e2a0ULL, }, + { 0x27d8c6ffab2b2514ULL, 0x8df188d8a942e2a0ULL, }, + { 0x8df188d8a942e2a0ULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_h.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_h.c new file mode 100644 index 0000000000..8a82def407 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVOD.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVOD.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffff0000ffff0000ULL, 0xffff0000ffff0000ULL, }, + { 0xffffaaaaffffaaaaULL, 0xffffaaaaffffaaaaULL, }, + { 0xffff5555ffff5555ULL, 0xffff5555ffff5555ULL, }, + { 0xffffccccffffccccULL, 0xffffccccffffccccULL, }, + { 0xffff3333ffff3333ULL, 0xffff3333ffff3333ULL, }, + { 0xffffe38effff8e38ULL, 0xffff38e3ffffe38eULL, }, + { 0xffff1c71ffff71c7ULL, 0xffffc71cffff1c71ULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0x0000cccc0000ccccULL, 0x0000cccc0000ccccULL, }, + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x0000e38e00008e38ULL, 0x000038e30000e38eULL, }, + { 0x00001c71000071c7ULL, 0x0000c71c00001c71ULL, }, + { 0xaaaaffffaaaaffffULL, 0xaaaaffffaaaaffffULL, }, /* 16 */ + { 0xaaaa0000aaaa0000ULL, 0xaaaa0000aaaa0000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaa5555aaaa5555ULL, 0xaaaa5555aaaa5555ULL, }, + { 0xaaaaccccaaaaccccULL, 0xaaaaccccaaaaccccULL, }, + { 0xaaaa3333aaaa3333ULL, 0xaaaa3333aaaa3333ULL, }, + { 0xaaaae38eaaaa8e38ULL, 0xaaaa38e3aaaae38eULL, }, + { 0xaaaa1c71aaaa71c7ULL, 0xaaaac71caaaa1c71ULL, }, + { 0x5555ffff5555ffffULL, 0x5555ffff5555ffffULL, }, /* 24 */ + { 0x5555000055550000ULL, 0x5555000055550000ULL, }, + { 0x5555aaaa5555aaaaULL, 0x5555aaaa5555aaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555cccc5555ccccULL, 0x5555cccc5555ccccULL, }, + { 0x5555333355553333ULL, 0x5555333355553333ULL, }, + { 0x5555e38e55558e38ULL, 0x555538e35555e38eULL, }, + { 0x55551c71555571c7ULL, 0x5555c71c55551c71ULL, }, + { 0xccccffffccccffffULL, 0xccccffffccccffffULL, }, /* 32 */ + { 0xcccc0000cccc0000ULL, 0xcccc0000cccc0000ULL, }, + { 0xccccaaaaccccaaaaULL, 0xccccaaaaccccaaaaULL, }, + { 0xcccc5555cccc5555ULL, 0xcccc5555cccc5555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccc3333cccc3333ULL, 0xcccc3333cccc3333ULL, }, + { 0xcccce38ecccc8e38ULL, 0xcccc38e3cccce38eULL, }, + { 0xcccc1c71cccc71c7ULL, 0xccccc71ccccc1c71ULL, }, + { 0x3333ffff3333ffffULL, 0x3333ffff3333ffffULL, }, /* 40 */ + { 0x3333000033330000ULL, 0x3333000033330000ULL, }, + { 0x3333aaaa3333aaaaULL, 0x3333aaaa3333aaaaULL, }, + { 0x3333555533335555ULL, 0x3333555533335555ULL, }, + { 0x3333cccc3333ccccULL, 0x3333cccc3333ccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x3333e38e33338e38ULL, 0x333338e33333e38eULL, }, + { 0x33331c71333371c7ULL, 0x3333c71c33331c71ULL, }, + { 0xe38effff8e38ffffULL, 0x38e3ffffe38effffULL, }, /* 48 */ + { 0xe38e00008e380000ULL, 0x38e30000e38e0000ULL, }, + { 0xe38eaaaa8e38aaaaULL, 0x38e3aaaae38eaaaaULL, }, + { 0xe38e55558e385555ULL, 0x38e35555e38e5555ULL, }, + { 0xe38ecccc8e38ccccULL, 0x38e3cccce38eccccULL, }, + { 0xe38e33338e383333ULL, 0x38e33333e38e3333ULL, }, + { 0xe38ee38e8e388e38ULL, 0x38e338e3e38ee38eULL, }, + { 0xe38e1c718e3871c7ULL, 0x38e3c71ce38e1c71ULL, }, + { 0x1c71ffff71c7ffffULL, 0xc71cffff1c71ffffULL, }, /* 56 */ + { 0x1c71000071c70000ULL, 0xc71c00001c710000ULL, }, + { 0x1c71aaaa71c7aaaaULL, 0xc71caaaa1c71aaaaULL, }, + { 0x1c71555571c75555ULL, 0xc71c55551c715555ULL, }, + { 0x1c71cccc71c7ccccULL, 0xc71ccccc1c71ccccULL, }, + { 0x1c71333371c73333ULL, 0xc71c33331c713333ULL, }, + { 0x1c71e38e71c78e38ULL, 0xc71c38e31c71e38eULL, }, + { 0x1c711c7171c771c7ULL, 0xc71cc71c1c711c71ULL, }, + { 0x886a886a28622862ULL, 0x4b674b67fe7bfe7bULL, }, /* 64 */ + { 0x886afbbe28624d93ULL, 0x4b6712f7fe7b153fULL, }, + { 0x886aac5a2862b9cfULL, 0x4b6727d8fe7bab2bULL, }, + { 0x886a704f28625e31ULL, 0x4b678df1fe7ba942ULL, }, + { 0xfbbe886a4d932862ULL, 0x12f74b67153ffe7bULL, }, + { 0xfbbefbbe4d934d93ULL, 0x12f712f7153f153fULL, }, + { 0xfbbeac5a4d93b9cfULL, 0x12f727d8153fab2bULL, }, + { 0xfbbe704f4d935e31ULL, 0x12f78df1153fa942ULL, }, + { 0xac5a886ab9cf2862ULL, 0x27d84b67ab2bfe7bULL, }, /* 72 */ + { 0xac5afbbeb9cf4d93ULL, 0x27d812f7ab2b153fULL, }, + { 0xac5aac5ab9cfb9cfULL, 0x27d827d8ab2bab2bULL, }, + { 0xac5a704fb9cf5e31ULL, 0x27d88df1ab2ba942ULL, }, + { 0x704f886a5e312862ULL, 0x8df14b67a942fe7bULL, }, + { 0x704ffbbe5e314d93ULL, 0x8df112f7a942153fULL, }, + { 0x704fac5a5e31b9cfULL, 0x8df127d8a942ab2bULL, }, + { 0x704f704f5e315e31ULL, 0x8df18df1a942a942ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_w.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_w.c new file mode 100644 index 0000000000..e19170c364 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvod_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVOD.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVOD.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffff00000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0xffffffff55555555ULL, 0xffffffff55555555ULL, }, + { 0xffffffffccccccccULL, 0xffffffffccccccccULL, }, + { 0xffffffff33333333ULL, 0xffffffff33333333ULL, }, + { 0xffffffffe38e38e3ULL, 0xffffffff38e38e38ULL, }, + { 0xffffffff1c71c71cULL, 0xffffffffc71c71c7ULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0x00000000ccccccccULL, 0x00000000ccccccccULL, }, + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0x00000000e38e38e3ULL, 0x0000000038e38e38ULL, }, + { 0x000000001c71c71cULL, 0x00000000c71c71c7ULL, }, + { 0xaaaaaaaaffffffffULL, 0xaaaaaaaaffffffffULL, }, /* 16 */ + { 0xaaaaaaaa00000000ULL, 0xaaaaaaaa00000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa55555555ULL, 0xaaaaaaaa55555555ULL, }, + { 0xaaaaaaaaccccccccULL, 0xaaaaaaaaccccccccULL, }, + { 0xaaaaaaaa33333333ULL, 0xaaaaaaaa33333333ULL, }, + { 0xaaaaaaaae38e38e3ULL, 0xaaaaaaaa38e38e38ULL, }, + { 0xaaaaaaaa1c71c71cULL, 0xaaaaaaaac71c71c7ULL, }, + { 0x55555555ffffffffULL, 0x55555555ffffffffULL, }, /* 24 */ + { 0x5555555500000000ULL, 0x5555555500000000ULL, }, + { 0x55555555aaaaaaaaULL, 0x55555555aaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55555555ccccccccULL, 0x55555555ccccccccULL, }, + { 0x5555555533333333ULL, 0x5555555533333333ULL, }, + { 0x55555555e38e38e3ULL, 0x5555555538e38e38ULL, }, + { 0x555555551c71c71cULL, 0x55555555c71c71c7ULL, }, + { 0xccccccccffffffffULL, 0xccccccccffffffffULL, }, /* 32 */ + { 0xcccccccc00000000ULL, 0xcccccccc00000000ULL, }, + { 0xccccccccaaaaaaaaULL, 0xccccccccaaaaaaaaULL, }, + { 0xcccccccc55555555ULL, 0xcccccccc55555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc33333333ULL, 0xcccccccc33333333ULL, }, + { 0xcccccccce38e38e3ULL, 0xcccccccc38e38e38ULL, }, + { 0xcccccccc1c71c71cULL, 0xccccccccc71c71c7ULL, }, + { 0x33333333ffffffffULL, 0x33333333ffffffffULL, }, /* 40 */ + { 0x3333333300000000ULL, 0x3333333300000000ULL, }, + { 0x33333333aaaaaaaaULL, 0x33333333aaaaaaaaULL, }, + { 0x3333333355555555ULL, 0x3333333355555555ULL, }, + { 0x33333333ccccccccULL, 0x33333333ccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x33333333e38e38e3ULL, 0x3333333338e38e38ULL, }, + { 0x333333331c71c71cULL, 0x33333333c71c71c7ULL, }, + { 0xe38e38e3ffffffffULL, 0x38e38e38ffffffffULL, }, /* 48 */ + { 0xe38e38e300000000ULL, 0x38e38e3800000000ULL, }, + { 0xe38e38e3aaaaaaaaULL, 0x38e38e38aaaaaaaaULL, }, + { 0xe38e38e355555555ULL, 0x38e38e3855555555ULL, }, + { 0xe38e38e3ccccccccULL, 0x38e38e38ccccccccULL, }, + { 0xe38e38e333333333ULL, 0x38e38e3833333333ULL, }, + { 0xe38e38e3e38e38e3ULL, 0x38e38e3838e38e38ULL, }, + { 0xe38e38e31c71c71cULL, 0x38e38e38c71c71c7ULL, }, + { 0x1c71c71cffffffffULL, 0xc71c71c7ffffffffULL, }, /* 56 */ + { 0x1c71c71c00000000ULL, 0xc71c71c700000000ULL, }, + { 0x1c71c71caaaaaaaaULL, 0xc71c71c7aaaaaaaaULL, }, + { 0x1c71c71c55555555ULL, 0xc71c71c755555555ULL, }, + { 0x1c71c71cccccccccULL, 0xc71c71c7ccccccccULL, }, + { 0x1c71c71c33333333ULL, 0xc71c71c733333333ULL, }, + { 0x1c71c71ce38e38e3ULL, 0xc71c71c738e38e38ULL, }, + { 0x1c71c71c1c71c71cULL, 0xc71c71c7c71c71c7ULL, }, + { 0x886ae6cc886ae6ccULL, 0x4b670b5e4b670b5eULL, }, /* 64 */ + { 0x886ae6ccfbbe0063ULL, 0x4b670b5e12f7bb1aULL, }, + { 0x886ae6ccac5aaeaaULL, 0x4b670b5e27d8c6ffULL, }, + { 0x886ae6cc704f164dULL, 0x4b670b5e8df188d8ULL, }, + { 0xfbbe0063886ae6ccULL, 0x12f7bb1a4b670b5eULL, }, + { 0xfbbe0063fbbe0063ULL, 0x12f7bb1a12f7bb1aULL, }, + { 0xfbbe0063ac5aaeaaULL, 0x12f7bb1a27d8c6ffULL, }, + { 0xfbbe0063704f164dULL, 0x12f7bb1a8df188d8ULL, }, + { 0xac5aaeaa886ae6ccULL, 0x27d8c6ff4b670b5eULL, }, /* 72 */ + { 0xac5aaeaafbbe0063ULL, 0x27d8c6ff12f7bb1aULL, }, + { 0xac5aaeaaac5aaeaaULL, 0x27d8c6ff27d8c6ffULL, }, + { 0xac5aaeaa704f164dULL, 0x27d8c6ff8df188d8ULL, }, + { 0x704f164d886ae6ccULL, 0x8df188d84b670b5eULL, }, + { 0x704f164dfbbe0063ULL, 0x8df188d812f7bb1aULL, }, + { 0x704f164dac5aaeaaULL, 0x8df188d827d8c6ffULL, }, + { 0x704f164d704f164dULL, 0x8df188d88df188d8ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVOD_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_b.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_b.c new file mode 100644 index 0000000000..1e519e6e9e --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVR.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVR.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xff00ff00ff00ff00ULL, 0xff00ff00ff00ff00ULL, }, + { 0xffaaffaaffaaffaaULL, 0xffaaffaaffaaffaaULL, }, + { 0xff55ff55ff55ff55ULL, 0xff55ff55ff55ff55ULL, }, + { 0xffccffccffccffccULL, 0xffccffccffccffccULL, }, + { 0xff33ff33ff33ff33ULL, 0xff33ff33ff33ff33ULL, }, + { 0xff8eff38ffe3ff8eULL, 0xffe3ff8eff38ffe3ULL, }, + { 0xff71ffc7ff1cff71ULL, 0xff1cff71ffc7ff1cULL, }, + { 0x00ff00ff00ff00ffULL, 0x00ff00ff00ff00ffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00aa00aa00aa00aaULL, 0x00aa00aa00aa00aaULL, }, + { 0x0055005500550055ULL, 0x0055005500550055ULL, }, + { 0x00cc00cc00cc00ccULL, 0x00cc00cc00cc00ccULL, }, + { 0x0033003300330033ULL, 0x0033003300330033ULL, }, + { 0x008e003800e3008eULL, 0x00e3008e003800e3ULL, }, + { 0x007100c7001c0071ULL, 0x001c007100c7001cULL, }, + { 0xaaffaaffaaffaaffULL, 0xaaffaaffaaffaaffULL, }, /* 16 */ + { 0xaa00aa00aa00aa00ULL, 0xaa00aa00aa00aa00ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaa55aa55aa55aa55ULL, 0xaa55aa55aa55aa55ULL, }, + { 0xaaccaaccaaccaaccULL, 0xaaccaaccaaccaaccULL, }, + { 0xaa33aa33aa33aa33ULL, 0xaa33aa33aa33aa33ULL, }, + { 0xaa8eaa38aae3aa8eULL, 0xaae3aa8eaa38aae3ULL, }, + { 0xaa71aac7aa1caa71ULL, 0xaa1caa71aac7aa1cULL, }, + { 0x55ff55ff55ff55ffULL, 0x55ff55ff55ff55ffULL, }, /* 24 */ + { 0x5500550055005500ULL, 0x5500550055005500ULL, }, + { 0x55aa55aa55aa55aaULL, 0x55aa55aa55aa55aaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55cc55cc55cc55ccULL, 0x55cc55cc55cc55ccULL, }, + { 0x5533553355335533ULL, 0x5533553355335533ULL, }, + { 0x558e553855e3558eULL, 0x55e3558e553855e3ULL, }, + { 0x557155c7551c5571ULL, 0x551c557155c7551cULL, }, + { 0xccffccffccffccffULL, 0xccffccffccffccffULL, }, /* 32 */ + { 0xcc00cc00cc00cc00ULL, 0xcc00cc00cc00cc00ULL, }, + { 0xccaaccaaccaaccaaULL, 0xccaaccaaccaaccaaULL, }, + { 0xcc55cc55cc55cc55ULL, 0xcc55cc55cc55cc55ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcc33cc33cc33cc33ULL, 0xcc33cc33cc33cc33ULL, }, + { 0xcc8ecc38cce3cc8eULL, 0xcce3cc8ecc38cce3ULL, }, + { 0xcc71ccc7cc1ccc71ULL, 0xcc1ccc71ccc7cc1cULL, }, + { 0x33ff33ff33ff33ffULL, 0x33ff33ff33ff33ffULL, }, /* 40 */ + { 0x3300330033003300ULL, 0x3300330033003300ULL, }, + { 0x33aa33aa33aa33aaULL, 0x33aa33aa33aa33aaULL, }, + { 0x3355335533553355ULL, 0x3355335533553355ULL, }, + { 0x33cc33cc33cc33ccULL, 0x33cc33cc33cc33ccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x338e333833e3338eULL, 0x33e3338e333833e3ULL, }, + { 0x337133c7331c3371ULL, 0x331c337133c7331cULL, }, + { 0x8eff38ffe3ff8effULL, 0xe3ff8eff38ffe3ffULL, }, /* 48 */ + { 0x8e003800e3008e00ULL, 0xe3008e003800e300ULL, }, + { 0x8eaa38aae3aa8eaaULL, 0xe3aa8eaa38aae3aaULL, }, + { 0x8e553855e3558e55ULL, 0xe3558e553855e355ULL, }, + { 0x8ecc38cce3cc8eccULL, 0xe3cc8ecc38cce3ccULL, }, + { 0x8e333833e3338e33ULL, 0xe3338e333833e333ULL, }, + { 0x8e8e3838e3e38e8eULL, 0xe3e38e8e3838e3e3ULL, }, + { 0x8e7138c7e31c8e71ULL, 0xe31c8e7138c7e31cULL, }, + { 0x71ffc7ff1cff71ffULL, 0x1cff71ffc7ff1cffULL, }, /* 56 */ + { 0x7100c7001c007100ULL, 0x1c007100c7001c00ULL, }, + { 0x71aac7aa1caa71aaULL, 0x1caa71aac7aa1caaULL, }, + { 0x7155c7551c557155ULL, 0x1c557155c7551c55ULL, }, + { 0x71ccc7cc1ccc71ccULL, 0x1ccc71ccc7cc1cccULL, }, + { 0x7133c7331c337133ULL, 0x1c337133c7331c33ULL, }, + { 0x718ec7381ce3718eULL, 0x1ce3718ec7381ce3ULL, }, + { 0x7171c7c71c1c7171ULL, 0x1c1c7171c7c71c1cULL, }, + { 0x2828626255554040ULL, 0x88886a6ae6e6ccccULL, }, /* 64 */ + { 0x284d629355c74008ULL, 0x88fb6abee600cc63ULL, }, + { 0x28b962cf558b4080ULL, 0x88ac6a5ae6aeccaaULL, }, + { 0x285e623155e2404eULL, 0x88706a4fe616cc4dULL, }, + { 0x4d289362c7550840ULL, 0xfb88be6a00e663ccULL, }, + { 0x4d4d9393c7c70808ULL, 0xfbfbbebe00006363ULL, }, + { 0x4db993cfc78b0880ULL, 0xfbacbe5a00ae63aaULL, }, + { 0x4d5e9331c7e2084eULL, 0xfb70be4f0016634dULL, }, + { 0xb928cf628b558040ULL, 0xac885a6aaee6aaccULL, }, /* 72 */ + { 0xb94dcf938bc78008ULL, 0xacfb5abeae00aa63ULL, }, + { 0xb9b9cfcf8b8b8080ULL, 0xacac5a5aaeaeaaaaULL, }, + { 0xb95ecf318be2804eULL, 0xac705a4fae16aa4dULL, }, + { 0x5e283162e2554e40ULL, 0x70884f6a16e64dccULL, }, + { 0x5e4d3193e2c74e08ULL, 0x70fb4fbe16004d63ULL, }, + { 0x5eb931cfe28b4e80ULL, 0x70ac4f5a16ae4daaULL, }, + { 0x5e5e3131e2e24e4eULL, 0x70704f4f16164d4dULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_d.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_d.c new file mode 100644 index 0000000000..be760430c7 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVR.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVR.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0xe38e38e38e38e38eULL, }, /* 48 */ + { 0x0000000000000000ULL, 0xe38e38e38e38e38eULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555555ULL, 0xe38e38e38e38e38eULL, }, + { 0xccccccccccccccccULL, 0xe38e38e38e38e38eULL, }, + { 0x3333333333333333ULL, 0xe38e38e38e38e38eULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x1c71c71c71c71c71ULL, 0xe38e38e38e38e38eULL, }, + { 0xffffffffffffffffULL, 0x1c71c71c71c71c71ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x1c71c71c71c71c71ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x1c71c71c71c71c71ULL, }, + { 0x5555555555555555ULL, 0x1c71c71c71c71c71ULL, }, + { 0xccccccccccccccccULL, 0x1c71c71c71c71c71ULL, }, + { 0x3333333333333333ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe38e38e38e38e38eULL, 0x1c71c71c71c71c71ULL, }, + { 0x1c71c71c71c71c71ULL, 0x1c71c71c71c71c71ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x886ae6cc28625540ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x886ae6cc28625540ULL, }, + { 0x704f164d5e31e24eULL, 0x886ae6cc28625540ULL, }, + { 0x886ae6cc28625540ULL, 0xfbbe00634d93c708ULL, }, + { 0xfbbe00634d93c708ULL, 0xfbbe00634d93c708ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xfbbe00634d93c708ULL, }, + { 0x704f164d5e31e24eULL, 0xfbbe00634d93c708ULL, }, + { 0x886ae6cc28625540ULL, 0xac5aaeaab9cf8b80ULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x704f164d5e31e24eULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x886ae6cc28625540ULL, 0x704f164d5e31e24eULL, }, + { 0xfbbe00634d93c708ULL, 0x704f164d5e31e24eULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x704f164d5e31e24eULL, }, + { 0x704f164d5e31e24eULL, 0x704f164d5e31e24eULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_h.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_h.c new file mode 100644 index 0000000000..cbd4685eca --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVR.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVR.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffff0000ffff0000ULL, 0xffff0000ffff0000ULL, }, + { 0xffffaaaaffffaaaaULL, 0xffffaaaaffffaaaaULL, }, + { 0xffff5555ffff5555ULL, 0xffff5555ffff5555ULL, }, + { 0xffffccccffffccccULL, 0xffffccccffffccccULL, }, + { 0xffff3333ffff3333ULL, 0xffff3333ffff3333ULL, }, + { 0xffff8e38ffffe38eULL, 0xffffe38effff38e3ULL, }, + { 0xffff71c7ffff1c71ULL, 0xffff1c71ffffc71cULL, }, + { 0x0000ffff0000ffffULL, 0x0000ffff0000ffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000aaaa0000aaaaULL, 0x0000aaaa0000aaaaULL, }, + { 0x0000555500005555ULL, 0x0000555500005555ULL, }, + { 0x0000cccc0000ccccULL, 0x0000cccc0000ccccULL, }, + { 0x0000333300003333ULL, 0x0000333300003333ULL, }, + { 0x00008e380000e38eULL, 0x0000e38e000038e3ULL, }, + { 0x000071c700001c71ULL, 0x00001c710000c71cULL, }, + { 0xaaaaffffaaaaffffULL, 0xaaaaffffaaaaffffULL, }, /* 16 */ + { 0xaaaa0000aaaa0000ULL, 0xaaaa0000aaaa0000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaa5555aaaa5555ULL, 0xaaaa5555aaaa5555ULL, }, + { 0xaaaaccccaaaaccccULL, 0xaaaaccccaaaaccccULL, }, + { 0xaaaa3333aaaa3333ULL, 0xaaaa3333aaaa3333ULL, }, + { 0xaaaa8e38aaaae38eULL, 0xaaaae38eaaaa38e3ULL, }, + { 0xaaaa71c7aaaa1c71ULL, 0xaaaa1c71aaaac71cULL, }, + { 0x5555ffff5555ffffULL, 0x5555ffff5555ffffULL, }, /* 24 */ + { 0x5555000055550000ULL, 0x5555000055550000ULL, }, + { 0x5555aaaa5555aaaaULL, 0x5555aaaa5555aaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555cccc5555ccccULL, 0x5555cccc5555ccccULL, }, + { 0x5555333355553333ULL, 0x5555333355553333ULL, }, + { 0x55558e385555e38eULL, 0x5555e38e555538e3ULL, }, + { 0x555571c755551c71ULL, 0x55551c715555c71cULL, }, + { 0xccccffffccccffffULL, 0xccccffffccccffffULL, }, /* 32 */ + { 0xcccc0000cccc0000ULL, 0xcccc0000cccc0000ULL, }, + { 0xccccaaaaccccaaaaULL, 0xccccaaaaccccaaaaULL, }, + { 0xcccc5555cccc5555ULL, 0xcccc5555cccc5555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccc3333cccc3333ULL, 0xcccc3333cccc3333ULL, }, + { 0xcccc8e38cccce38eULL, 0xcccce38ecccc38e3ULL, }, + { 0xcccc71c7cccc1c71ULL, 0xcccc1c71ccccc71cULL, }, + { 0x3333ffff3333ffffULL, 0x3333ffff3333ffffULL, }, /* 40 */ + { 0x3333000033330000ULL, 0x3333000033330000ULL, }, + { 0x3333aaaa3333aaaaULL, 0x3333aaaa3333aaaaULL, }, + { 0x3333555533335555ULL, 0x3333555533335555ULL, }, + { 0x3333cccc3333ccccULL, 0x3333cccc3333ccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x33338e383333e38eULL, 0x3333e38e333338e3ULL, }, + { 0x333371c733331c71ULL, 0x33331c713333c71cULL, }, + { 0x8e38ffffe38effffULL, 0xe38effff38e3ffffULL, }, /* 48 */ + { 0x8e380000e38e0000ULL, 0xe38e000038e30000ULL, }, + { 0x8e38aaaae38eaaaaULL, 0xe38eaaaa38e3aaaaULL, }, + { 0x8e385555e38e5555ULL, 0xe38e555538e35555ULL, }, + { 0x8e38cccce38eccccULL, 0xe38ecccc38e3ccccULL, }, + { 0x8e383333e38e3333ULL, 0xe38e333338e33333ULL, }, + { 0x8e388e38e38ee38eULL, 0xe38ee38e38e338e3ULL, }, + { 0x8e3871c7e38e1c71ULL, 0xe38e1c7138e3c71cULL, }, + { 0x71c7ffff1c71ffffULL, 0x1c71ffffc71cffffULL, }, /* 56 */ + { 0x71c700001c710000ULL, 0x1c710000c71c0000ULL, }, + { 0x71c7aaaa1c71aaaaULL, 0x1c71aaaac71caaaaULL, }, + { 0x71c755551c715555ULL, 0x1c715555c71c5555ULL, }, + { 0x71c7cccc1c71ccccULL, 0x1c71ccccc71cccccULL, }, + { 0x71c733331c713333ULL, 0x1c713333c71c3333ULL, }, + { 0x71c78e381c71e38eULL, 0x1c71e38ec71c38e3ULL, }, + { 0x71c771c71c711c71ULL, 0x1c711c71c71cc71cULL, }, + { 0x2862286255405540ULL, 0x886a886ae6cce6ccULL, }, /* 64 */ + { 0x28624d935540c708ULL, 0x886afbbee6cc0063ULL, }, + { 0x2862b9cf55408b80ULL, 0x886aac5ae6ccaeaaULL, }, + { 0x28625e315540e24eULL, 0x886a704fe6cc164dULL, }, + { 0x4d932862c7085540ULL, 0xfbbe886a0063e6ccULL, }, + { 0x4d934d93c708c708ULL, 0xfbbefbbe00630063ULL, }, + { 0x4d93b9cfc7088b80ULL, 0xfbbeac5a0063aeaaULL, }, + { 0x4d935e31c708e24eULL, 0xfbbe704f0063164dULL, }, + { 0xb9cf28628b805540ULL, 0xac5a886aaeaae6ccULL, }, /* 72 */ + { 0xb9cf4d938b80c708ULL, 0xac5afbbeaeaa0063ULL, }, + { 0xb9cfb9cf8b808b80ULL, 0xac5aac5aaeaaaeaaULL, }, + { 0xb9cf5e318b80e24eULL, 0xac5a704faeaa164dULL, }, + { 0x5e312862e24e5540ULL, 0x704f886a164de6ccULL, }, + { 0x5e314d93e24ec708ULL, 0x704ffbbe164d0063ULL, }, + { 0x5e31b9cfe24e8b80ULL, 0x704fac5a164daeaaULL, }, + { 0x5e315e31e24ee24eULL, 0x704f704f164d164dULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_w.c b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_w.c new file mode 100644 index 0000000000..5f4cfd0377 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/interleave/test_msa_ilvr_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction ILVR.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "ILVR.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffff00000000ULL, 0xffffffff00000000ULL, }, + { 0xffffffffaaaaaaaaULL, 0xffffffffaaaaaaaaULL, }, + { 0xffffffff55555555ULL, 0xffffffff55555555ULL, }, + { 0xffffffffccccccccULL, 0xffffffffccccccccULL, }, + { 0xffffffff33333333ULL, 0xffffffff33333333ULL, }, + { 0xffffffff8e38e38eULL, 0xffffffffe38e38e3ULL, }, + { 0xffffffff71c71c71ULL, 0xffffffff1c71c71cULL, }, + { 0x00000000ffffffffULL, 0x00000000ffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x00000000aaaaaaaaULL, 0x00000000aaaaaaaaULL, }, + { 0x0000000055555555ULL, 0x0000000055555555ULL, }, + { 0x00000000ccccccccULL, 0x00000000ccccccccULL, }, + { 0x0000000033333333ULL, 0x0000000033333333ULL, }, + { 0x000000008e38e38eULL, 0x00000000e38e38e3ULL, }, + { 0x0000000071c71c71ULL, 0x000000001c71c71cULL, }, + { 0xaaaaaaaaffffffffULL, 0xaaaaaaaaffffffffULL, }, /* 16 */ + { 0xaaaaaaaa00000000ULL, 0xaaaaaaaa00000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaa55555555ULL, 0xaaaaaaaa55555555ULL, }, + { 0xaaaaaaaaccccccccULL, 0xaaaaaaaaccccccccULL, }, + { 0xaaaaaaaa33333333ULL, 0xaaaaaaaa33333333ULL, }, + { 0xaaaaaaaa8e38e38eULL, 0xaaaaaaaae38e38e3ULL, }, + { 0xaaaaaaaa71c71c71ULL, 0xaaaaaaaa1c71c71cULL, }, + { 0x55555555ffffffffULL, 0x55555555ffffffffULL, }, /* 24 */ + { 0x5555555500000000ULL, 0x5555555500000000ULL, }, + { 0x55555555aaaaaaaaULL, 0x55555555aaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x55555555ccccccccULL, 0x55555555ccccccccULL, }, + { 0x5555555533333333ULL, 0x5555555533333333ULL, }, + { 0x555555558e38e38eULL, 0x55555555e38e38e3ULL, }, + { 0x5555555571c71c71ULL, 0x555555551c71c71cULL, }, + { 0xccccccccffffffffULL, 0xccccccccffffffffULL, }, /* 32 */ + { 0xcccccccc00000000ULL, 0xcccccccc00000000ULL, }, + { 0xccccccccaaaaaaaaULL, 0xccccccccaaaaaaaaULL, }, + { 0xcccccccc55555555ULL, 0xcccccccc55555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xcccccccc33333333ULL, 0xcccccccc33333333ULL, }, + { 0xcccccccc8e38e38eULL, 0xcccccccce38e38e3ULL, }, + { 0xcccccccc71c71c71ULL, 0xcccccccc1c71c71cULL, }, + { 0x33333333ffffffffULL, 0x33333333ffffffffULL, }, /* 40 */ + { 0x3333333300000000ULL, 0x3333333300000000ULL, }, + { 0x33333333aaaaaaaaULL, 0x33333333aaaaaaaaULL, }, + { 0x3333333355555555ULL, 0x3333333355555555ULL, }, + { 0x33333333ccccccccULL, 0x33333333ccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x333333338e38e38eULL, 0x33333333e38e38e3ULL, }, + { 0x3333333371c71c71ULL, 0x333333331c71c71cULL, }, + { 0x8e38e38effffffffULL, 0xe38e38e3ffffffffULL, }, /* 48 */ + { 0x8e38e38e00000000ULL, 0xe38e38e300000000ULL, }, + { 0x8e38e38eaaaaaaaaULL, 0xe38e38e3aaaaaaaaULL, }, + { 0x8e38e38e55555555ULL, 0xe38e38e355555555ULL, }, + { 0x8e38e38eccccccccULL, 0xe38e38e3ccccccccULL, }, + { 0x8e38e38e33333333ULL, 0xe38e38e333333333ULL, }, + { 0x8e38e38e8e38e38eULL, 0xe38e38e3e38e38e3ULL, }, + { 0x8e38e38e71c71c71ULL, 0xe38e38e31c71c71cULL, }, + { 0x71c71c71ffffffffULL, 0x1c71c71cffffffffULL, }, /* 56 */ + { 0x71c71c7100000000ULL, 0x1c71c71c00000000ULL, }, + { 0x71c71c71aaaaaaaaULL, 0x1c71c71caaaaaaaaULL, }, + { 0x71c71c7155555555ULL, 0x1c71c71c55555555ULL, }, + { 0x71c71c71ccccccccULL, 0x1c71c71cccccccccULL, }, + { 0x71c71c7133333333ULL, 0x1c71c71c33333333ULL, }, + { 0x71c71c718e38e38eULL, 0x1c71c71ce38e38e3ULL, }, + { 0x71c71c7171c71c71ULL, 0x1c71c71c1c71c71cULL, }, + { 0x2862554028625540ULL, 0x886ae6cc886ae6ccULL, }, /* 64 */ + { 0x286255404d93c708ULL, 0x886ae6ccfbbe0063ULL, }, + { 0x28625540b9cf8b80ULL, 0x886ae6ccac5aaeaaULL, }, + { 0x286255405e31e24eULL, 0x886ae6cc704f164dULL, }, + { 0x4d93c70828625540ULL, 0xfbbe0063886ae6ccULL, }, + { 0x4d93c7084d93c708ULL, 0xfbbe0063fbbe0063ULL, }, + { 0x4d93c708b9cf8b80ULL, 0xfbbe0063ac5aaeaaULL, }, + { 0x4d93c7085e31e24eULL, 0xfbbe0063704f164dULL, }, + { 0xb9cf8b8028625540ULL, 0xac5aaeaa886ae6ccULL, }, /* 72 */ + { 0xb9cf8b804d93c708ULL, 0xac5aaeaafbbe0063ULL, }, + { 0xb9cf8b80b9cf8b80ULL, 0xac5aaeaaac5aaeaaULL, }, + { 0xb9cf8b805e31e24eULL, 0xac5aaeaa704f164dULL, }, + { 0x5e31e24e28625540ULL, 0x704f164d886ae6ccULL, }, + { 0x5e31e24e4d93c708ULL, 0x704f164dfbbe0063ULL, }, + { 0x5e31e24eb9cf8b80ULL, 0x704f164dac5aaeaaULL, }, + { 0x5e31e24e5e31e24eULL, 0x704f164d704f164dULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_ILVR_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/logic/test_msa_and_v.c b/tests/tcg/mips/user/ase/msa/logic/test_msa_and_v.c new file mode 100644 index 0000000000..534c4201a8 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/logic/test_msa_and_v.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction AND.V + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "AND.V"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0xa28a28a28a28a28aULL, 0x28a28a28a28a28a2ULL, }, + { 0x0820820820820820ULL, 0x8208208208208208ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x4104104104104104ULL, 0x1041041041041041ULL, }, + { 0x1451451451451451ULL, 0x4514514514514514ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xc08c08c08c08c08cULL, 0x08c08c08c08c08c0ULL, }, + { 0x0c40c40c40c40c40ULL, 0xc40c40c40c40c40cULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x2302302302302302ULL, 0x3023023023023023ULL, }, + { 0x1031031031031031ULL, 0x0310310310310310ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xa28a28a28a28a28aULL, 0x28a28a28a28a28a2ULL, }, + { 0x4104104104104104ULL, 0x1041041041041041ULL, }, + { 0xc08c08c08c08c08cULL, 0x08c08c08c08c08c0ULL, }, + { 0x2302302302302302ULL, 0x3023023023023023ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0820820820820820ULL, 0x8208208208208208ULL, }, + { 0x1451451451451451ULL, 0x4514514514514514ULL, }, + { 0x0c40c40c40c40c40ULL, 0xc40c40c40c40c40cULL, }, + { 0x1031031031031031ULL, 0x0310310310310310ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x882a004008024500ULL, 0x02670b1a143b100cULL, }, + { 0x884aa68828420100ULL, 0x0340025eaa2b2004ULL, }, + { 0x004a064c08204040ULL, 0x09610858a842a000ULL, }, + { 0x882a004008024500ULL, 0x02670b1a143b100cULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xa81a002209838300ULL, 0x02d0821a012b0014ULL, }, + { 0x700e00414c11c208ULL, 0x00f18818010242a0ULL, }, + { 0x884aa68828420100ULL, 0x0340025eaa2b2004ULL, }, /* 72 */ + { 0xa81a002209838300ULL, 0x02d0821a012b0014ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x204a060818018200ULL, 0x05d080d8a9022000ULL, }, + { 0x004a064c08204040ULL, 0x09610858a842a000ULL, }, + { 0x700e00414c11c208ULL, 0x00f18818010242a0ULL, }, + { 0x204a060818018200ULL, 0x05d080d8a9022000ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_AND_V(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_AND_V(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/logic/test_msa_nor_v.c b/tests/tcg/mips/user/ase/msa/logic/test_msa_nor_v.c new file mode 100644 index 0000000000..f781a8bb9d --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/logic/test_msa_nor_v.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction NOR.V + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "NOR.V"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 8 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 16 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x1451451451451451ULL, 0x4514514514514514ULL, }, + { 0x4104104104104104ULL, 0x1041041041041041ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 24 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x0820820820820820ULL, 0x8208208208208208ULL, }, + { 0xa28a28a28a28a28aULL, 0x28a28a28a28a28a2ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 32 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x1111111111111111ULL, 0x1111111111111111ULL, }, + { 0x2222222222222222ULL, 0x2222222222222222ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x1031031031031031ULL, 0x0310310310310310ULL, }, + { 0x2302302302302302ULL, 0x3023023023023023ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 40 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x4444444444444444ULL, 0x4444444444444444ULL, }, + { 0x8888888888888888ULL, 0x8888888888888888ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0c40c40c40c40c40ULL, 0xc40c40c40c40c40cULL, }, + { 0xc08c08c08c08c08cULL, 0x08c08c08c08c08c0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 48 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x1451451451451451ULL, 0x4514514514514514ULL, }, + { 0x0820820820820820ULL, 0x8208208208208208ULL, }, + { 0x1031031031031031ULL, 0x0310310310310310ULL, }, + { 0x0c40c40c40c40c40ULL, 0xc40c40c40c40c40cULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 56 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x4104104104104104ULL, 0x1041041041041041ULL, }, + { 0xa28a28a28a28a28aULL, 0x28a28a28a28a28a2ULL, }, + { 0x2302302302302302ULL, 0x3023023023023023ULL, }, + { 0xc08c08c08c08c08cULL, 0x08c08c08c08c08c0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x77951933d79daabfULL, 0xb498f4a101844ff3ULL, }, /* 64 */ + { 0x04011910920c28b7ULL, 0xa40844a100800d03ULL, }, + { 0x538511114610203fULL, 0x9000300000844ae3ULL, }, + { 0x07900932818c08b1ULL, 0x3008742100840d53ULL, }, + { 0x04011910920c28b7ULL, 0xa40844a100800d03ULL, }, + { 0x0441ff9cb26c38f7ULL, 0xed0844e5eac0ad03ULL, }, + { 0x0001511402203077ULL, 0xc800000040c08803ULL, }, + { 0x0400e990a04c18b1ULL, 0x6008442542800d03ULL, }, + { 0x538511114610203fULL, 0x9000300000844ae3ULL, }, /* 72 */ + { 0x0001511402203077ULL, 0xc800000040c08803ULL, }, + { 0x53a551554630747fULL, 0xd827390054d4daebULL, }, + { 0x03a0411000001431ULL, 0x500631005494184bULL, }, + { 0x07900932818c08b1ULL, 0x3008742100840d53ULL, }, + { 0x0400e990a04c18b1ULL, 0x6008442542800d03ULL, }, + { 0x03a0411000001431ULL, 0x500631005494184bULL, }, + { 0x8fb0e9b2a1ce1db1ULL, 0x720e772756bd1d5fULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_NOR_V(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_NOR_V(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/logic/test_msa_or_v.c b/tests/tcg/mips/user/ase/msa/logic/test_msa_or_v.c new file mode 100644 index 0000000000..924f216e41 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/logic/test_msa_or_v.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction OR.V + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "OR.V"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0xebaebaebaebaebaeULL, 0xbaebaebaebaebaebULL, }, + { 0xbefbefbefbefbefbULL, 0xefbefbefbefbefbeULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xf7df7df7df7df7dfULL, 0x7df7df7df7df7df7ULL, }, + { 0x5d75d75d75d75d75ULL, 0xd75d75d75d75d75dULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xeeeeeeeeeeeeeeeeULL, 0xeeeeeeeeeeeeeeeeULL, }, + { 0xddddddddddddddddULL, 0xddddddddddddddddULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xefcefcefcefcefceULL, 0xfcefcefcefcefcefULL, }, + { 0xdcfdcfdcfdcfdcfdULL, 0xcfdcfdcfdcfdcfdcULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xbbbbbbbbbbbbbbbbULL, 0xbbbbbbbbbbbbbbbbULL, }, + { 0x7777777777777777ULL, 0x7777777777777777ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xf3bf3bf3bf3bf3bfULL, 0x3bf3bf3bf3bf3bf3ULL, }, + { 0x3f73f73f73f73f73ULL, 0xf73f73f73f73f73fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xebaebaebaebaebaeULL, 0xbaebaebaebaebaebULL, }, + { 0xf7df7df7df7df7dfULL, 0x7df7df7df7df7df7ULL, }, + { 0xefcefcefcefcefceULL, 0xfcefcefcefcefcefULL, }, + { 0xf3bf3bf3bf3bf3bfULL, 0x3bf3bf3bf3bf3bf3ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xbefbefbefbefbefbULL, 0xefbefbefbefbefbeULL, }, + { 0x5d75d75d75d75d75ULL, 0xd75d75d75d75d75dULL, }, + { 0xdcfdcfdcfdcfdcfdULL, 0xcfdcfdcfdcfdcfdcULL, }, + { 0x3f73f73f73f73f73ULL, 0xf73f73f73f73f73fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x886ae6cc28625540ULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0xfbfee6ef6df3d748ULL, 0x5bf7bb5eff7ff2fcULL, }, + { 0xac7aeeeeb9efdfc0ULL, 0x6fffcfffff7bb51cULL, }, + { 0xf86ff6cd7e73f74eULL, 0xcff78bdeff7bf2acULL, }, + { 0xfbfee6ef6df3d748ULL, 0x5bf7bb5eff7ff2fcULL, }, + { 0xfbbe00634d93c708ULL, 0x12f7bb1a153f52fcULL, }, + { 0xfffeaeebfddfcf88ULL, 0x37ffffffbf3f77fcULL, }, + { 0xfbff166f5fb3e74eULL, 0x9ff7bbdabd7ff2fcULL, }, + { 0xac7aeeeeb9efdfc0ULL, 0x6fffcfffff7bb51cULL, }, /* 72 */ + { 0xfffeaeebfddfcf88ULL, 0x37ffffffbf3f77fcULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x27d8c6ffab2b2514ULL, }, + { 0xfc5fbeefffffebceULL, 0xaff9ceffab6be7b4ULL, }, + { 0xf86ff6cd7e73f74eULL, 0xcff78bdeff7bf2acULL, }, + { 0xfbff166f5fb3e74eULL, 0x9ff7bbdabd7ff2fcULL, }, + { 0xfc5fbeefffffebceULL, 0xaff9ceffab6be7b4ULL, }, + { 0x704f164d5e31e24eULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_OR_V(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_OR_V(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/logic/test_msa_xor_v.c b/tests/tcg/mips/user/ase/msa/logic/test_msa_xor_v.c new file mode 100644 index 0000000000..f0442e6577 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/logic/test_msa_xor_v.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction XOR.V + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "XOR.V"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, /* 16 */ + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x4924924924924924ULL, 0x9249249249249249ULL, }, + { 0xb6db6db6db6db6dbULL, 0x6db6db6db6db6db6ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 24 */ + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xb6db6db6db6db6dbULL, 0x6db6db6db6db6db6ULL, }, + { 0x4924924924924924ULL, 0x9249249249249249ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, /* 32 */ + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x2f42f42f42f42f42ULL, 0xf42f42f42f42f42fULL, }, + { 0xd0bd0bd0bd0bd0bdULL, 0x0bd0bd0bd0bd0bd0ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, /* 40 */ + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x9999999999999999ULL, 0x9999999999999999ULL, }, + { 0x6666666666666666ULL, 0x6666666666666666ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xd0bd0bd0bd0bd0bdULL, 0x0bd0bd0bd0bd0bd0ULL, }, + { 0x2f42f42f42f42f42ULL, 0xf42f42f42f42f42fULL, }, + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, /* 48 */ + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, + { 0x4924924924924924ULL, 0x9249249249249249ULL, }, + { 0xb6db6db6db6db6dbULL, 0x6db6db6db6db6db6ULL, }, + { 0x2f42f42f42f42f42ULL, 0xf42f42f42f42f42fULL, }, + { 0xd0bd0bd0bd0bd0bdULL, 0x0bd0bd0bd0bd0bd0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0x38e38e38e38e38e3ULL, }, /* 56 */ + { 0x1c71c71c71c71c71ULL, 0xc71c71c71c71c71cULL, }, + { 0xb6db6db6db6db6dbULL, 0x6db6db6db6db6db6ULL, }, + { 0x4924924924924924ULL, 0x9249249249249249ULL, }, + { 0xd0bd0bd0bd0bd0bdULL, 0x0bd0bd0bd0bd0bd0ULL, }, + { 0x2f42f42f42f42f42ULL, 0xf42f42f42f42f42fULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 64 */ + { 0x73d4e6af65f19248ULL, 0x5990b044eb44e2f0ULL, }, + { 0x2430486691addec0ULL, 0x6cbfcda155509518ULL, }, + { 0xf825f0817653b70eULL, 0xc6968386573952acULL, }, + { 0x73d4e6af65f19248ULL, 0x5990b044eb44e2f0ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x57e4aec9f45c4c88ULL, 0x352f7de5be1477e8ULL, }, + { 0x8bf1162e13a22546ULL, 0x9f0633c2bc7db05cULL, }, + { 0x2430486691addec0ULL, 0x6cbfcda155509518ULL, }, /* 72 */ + { 0x57e4aec9f45c4c88ULL, 0x352f7de5be1477e8ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xdc15b8e7e7fe69ceULL, 0xaa294e270269c7b4ULL, }, + { 0xf825f0817653b70eULL, 0xc6968386573952acULL, }, + { 0x8bf1162e13a22546ULL, 0x9f0633c2bc7db05cULL, }, + { 0xdc15b8e7e7fe69ceULL, 0xaa294e270269c7b4ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_XOR_V(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_XOR_V(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_b.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_b.c new file mode 100644 index 0000000000..409773d7f2 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKEV.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKEV.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0xe3388ee38ee3388eULL, 0xffffffffffffffffULL, }, + { 0x1cc7711c711cc771ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0xe3388ee38ee3388eULL, 0x0000000000000000ULL, }, + { 0x1cc7711c711cc771ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe3388ee38ee3388eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1cc7711c711cc771ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0xe3388ee38ee3388eULL, 0x5555555555555555ULL, }, + { 0x1cc7711c711cc771ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0xe3388ee38ee3388eULL, 0xccccccccccccccccULL, }, + { 0x1cc7711c711cc771ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe3388ee38ee3388eULL, 0x3333333333333333ULL, }, + { 0x1cc7711c711cc771ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0xe3388ee38ee3388eULL, }, /* 48 */ + { 0x0000000000000000ULL, 0xe3388ee38ee3388eULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xe3388ee38ee3388eULL, }, + { 0x5555555555555555ULL, 0xe3388ee38ee3388eULL, }, + { 0xccccccccccccccccULL, 0xe3388ee38ee3388eULL, }, + { 0x3333333333333333ULL, 0xe3388ee38ee3388eULL, }, + { 0xe3388ee38ee3388eULL, 0xe3388ee38ee3388eULL, }, + { 0x1cc7711c711cc771ULL, 0xe3388ee38ee3388eULL, }, + { 0xffffffffffffffffULL, 0x1cc7711c711cc771ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x1cc7711c711cc771ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x1cc7711c711cc771ULL, }, + { 0x5555555555555555ULL, 0x1cc7711c711cc771ULL, }, + { 0xccccccccccccccccULL, 0x1cc7711c711cc771ULL, }, + { 0x3333333333333333ULL, 0x1cc7711c711cc771ULL, }, + { 0xe3388ee38ee3388eULL, 0x1cc7711c711cc771ULL, }, + { 0x1cc7711c711cc771ULL, 0x1cc7711c711cc771ULL, }, + { 0x675e7b0c6acc6240ULL, 0x675e7b0c6acc6240ULL, }, /* 64 */ + { 0xf71a3ffcbe639308ULL, 0x675e7b0c6acc6240ULL, }, + { 0xd8ff2b145aaacf80ULL, 0x675e7b0c6acc6240ULL, }, + { 0xf1d842a04f4d314eULL, 0x675e7b0c6acc6240ULL, }, + { 0x675e7b0c6acc6240ULL, 0xf71a3ffcbe639308ULL, }, + { 0xf71a3ffcbe639308ULL, 0xf71a3ffcbe639308ULL, }, + { 0xd8ff2b145aaacf80ULL, 0xf71a3ffcbe639308ULL, }, + { 0xf1d842a04f4d314eULL, 0xf71a3ffcbe639308ULL, }, + { 0x675e7b0c6acc6240ULL, 0xd8ff2b145aaacf80ULL, }, /* 72 */ + { 0xf71a3ffcbe639308ULL, 0xd8ff2b145aaacf80ULL, }, + { 0xd8ff2b145aaacf80ULL, 0xd8ff2b145aaacf80ULL, }, + { 0xf1d842a04f4d314eULL, 0xd8ff2b145aaacf80ULL, }, + { 0x675e7b0c6acc6240ULL, 0xf1d842a04f4d314eULL, }, + { 0xf71a3ffcbe639308ULL, 0xf1d842a04f4d314eULL, }, + { 0xd8ff2b145aaacf80ULL, 0xf1d842a04f4d314eULL, }, + { 0xf1d842a04f4d314eULL, 0xf1d842a04f4d314eULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_d.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_d.c new file mode 100644 index 0000000000..8e89716416 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKEV.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKEV.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0xe38e38e38e38e38eULL, }, /* 48 */ + { 0x0000000000000000ULL, 0xe38e38e38e38e38eULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555555ULL, 0xe38e38e38e38e38eULL, }, + { 0xccccccccccccccccULL, 0xe38e38e38e38e38eULL, }, + { 0x3333333333333333ULL, 0xe38e38e38e38e38eULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x1c71c71c71c71c71ULL, 0xe38e38e38e38e38eULL, }, + { 0xffffffffffffffffULL, 0x1c71c71c71c71c71ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x1c71c71c71c71c71ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x1c71c71c71c71c71ULL, }, + { 0x5555555555555555ULL, 0x1c71c71c71c71c71ULL, }, + { 0xccccccccccccccccULL, 0x1c71c71c71c71c71ULL, }, + { 0x3333333333333333ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe38e38e38e38e38eULL, 0x1c71c71c71c71c71ULL, }, + { 0x1c71c71c71c71c71ULL, 0x1c71c71c71c71c71ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, /* 64 */ + { 0xfbbe00634d93c708ULL, 0x886ae6cc28625540ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x886ae6cc28625540ULL, }, + { 0x704f164d5e31e24eULL, 0x886ae6cc28625540ULL, }, + { 0x886ae6cc28625540ULL, 0xfbbe00634d93c708ULL, }, + { 0xfbbe00634d93c708ULL, 0xfbbe00634d93c708ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xfbbe00634d93c708ULL, }, + { 0x704f164d5e31e24eULL, 0xfbbe00634d93c708ULL, }, + { 0x886ae6cc28625540ULL, 0xac5aaeaab9cf8b80ULL, }, /* 72 */ + { 0xfbbe00634d93c708ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x704f164d5e31e24eULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x886ae6cc28625540ULL, 0x704f164d5e31e24eULL, }, + { 0xfbbe00634d93c708ULL, 0x704f164d5e31e24eULL, }, + { 0xac5aaeaab9cf8b80ULL, 0x704f164d5e31e24eULL, }, + { 0x704f164d5e31e24eULL, 0x704f164d5e31e24eULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_h.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_h.c new file mode 100644 index 0000000000..b389587dfe --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKEV.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKEV.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x8e3838e338e3e38eULL, 0xffffffffffffffffULL, }, + { 0x71c7c71cc71c1c71ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x8e3838e338e3e38eULL, 0x0000000000000000ULL, }, + { 0x71c7c71cc71c1c71ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x8e3838e338e3e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x71c7c71cc71c1c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x8e3838e338e3e38eULL, 0x5555555555555555ULL, }, + { 0x71c7c71cc71c1c71ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x8e3838e338e3e38eULL, 0xccccccccccccccccULL, }, + { 0x71c7c71cc71c1c71ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x8e3838e338e3e38eULL, 0x3333333333333333ULL, }, + { 0x71c7c71cc71c1c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x8e3838e338e3e38eULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x8e3838e338e3e38eULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x8e3838e338e3e38eULL, }, + { 0x5555555555555555ULL, 0x8e3838e338e3e38eULL, }, + { 0xccccccccccccccccULL, 0x8e3838e338e3e38eULL, }, + { 0x3333333333333333ULL, 0x8e3838e338e3e38eULL, }, + { 0x8e3838e338e3e38eULL, 0x8e3838e338e3e38eULL, }, + { 0x71c7c71cc71c1c71ULL, 0x8e3838e338e3e38eULL, }, + { 0xffffffffffffffffULL, 0x71c7c71cc71c1c71ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x71c7c71cc71c1c71ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x71c7c71cc71c1c71ULL, }, + { 0x5555555555555555ULL, 0x71c7c71cc71c1c71ULL, }, + { 0xccccccccccccccccULL, 0x71c7c71cc71c1c71ULL, }, + { 0x3333333333333333ULL, 0x71c7c71cc71c1c71ULL, }, + { 0x8e3838e338e3e38eULL, 0x71c7c71cc71c1c71ULL, }, + { 0x71c7c71cc71c1c71ULL, 0x71c7c71cc71c1c71ULL, }, + { 0x0b5eb00ce6cc5540ULL, 0x0b5eb00ce6cc5540ULL, }, /* 64 */ + { 0xbb1a52fc0063c708ULL, 0x0b5eb00ce6cc5540ULL, }, + { 0xc6ff2514aeaa8b80ULL, 0x0b5eb00ce6cc5540ULL, }, + { 0x88d8e2a0164de24eULL, 0x0b5eb00ce6cc5540ULL, }, + { 0x0b5eb00ce6cc5540ULL, 0xbb1a52fc0063c708ULL, }, + { 0xbb1a52fc0063c708ULL, 0xbb1a52fc0063c708ULL, }, + { 0xc6ff2514aeaa8b80ULL, 0xbb1a52fc0063c708ULL, }, + { 0x88d8e2a0164de24eULL, 0xbb1a52fc0063c708ULL, }, + { 0x0b5eb00ce6cc5540ULL, 0xc6ff2514aeaa8b80ULL, }, /* 72 */ + { 0xbb1a52fc0063c708ULL, 0xc6ff2514aeaa8b80ULL, }, + { 0xc6ff2514aeaa8b80ULL, 0xc6ff2514aeaa8b80ULL, }, + { 0x88d8e2a0164de24eULL, 0xc6ff2514aeaa8b80ULL, }, + { 0x0b5eb00ce6cc5540ULL, 0x88d8e2a0164de24eULL, }, + { 0xbb1a52fc0063c708ULL, 0x88d8e2a0164de24eULL, }, + { 0xc6ff2514aeaa8b80ULL, 0x88d8e2a0164de24eULL, }, + { 0x88d8e2a0164de24eULL, 0x88d8e2a0164de24eULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_w.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_w.c new file mode 100644 index 0000000000..d393ad5066 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckev_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKEV.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKEV.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0xe38e38e38e38e38eULL, 0xffffffffffffffffULL, }, + { 0x1c71c71c71c71c71ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0x0000000000000000ULL, }, + { 0x1c71c71c71c71c71ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xe38e38e38e38e38eULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x1c71c71c71c71c71ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0xe38e38e38e38e38eULL, 0x5555555555555555ULL, }, + { 0x1c71c71c71c71c71ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0xe38e38e38e38e38eULL, 0xccccccccccccccccULL, }, + { 0x1c71c71c71c71c71ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0xe38e38e38e38e38eULL, 0x3333333333333333ULL, }, + { 0x1c71c71c71c71c71ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0xe38e38e38e38e38eULL, }, /* 48 */ + { 0x0000000000000000ULL, 0xe38e38e38e38e38eULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xe38e38e38e38e38eULL, }, + { 0x5555555555555555ULL, 0xe38e38e38e38e38eULL, }, + { 0xccccccccccccccccULL, 0xe38e38e38e38e38eULL, }, + { 0x3333333333333333ULL, 0xe38e38e38e38e38eULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x1c71c71c71c71c71ULL, 0xe38e38e38e38e38eULL, }, + { 0xffffffffffffffffULL, 0x1c71c71c71c71c71ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x1c71c71c71c71c71ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x1c71c71c71c71c71ULL, }, + { 0x5555555555555555ULL, 0x1c71c71c71c71c71ULL, }, + { 0xccccccccccccccccULL, 0x1c71c71c71c71c71ULL, }, + { 0x3333333333333333ULL, 0x1c71c71c71c71c71ULL, }, + { 0xe38e38e38e38e38eULL, 0x1c71c71c71c71c71ULL, }, + { 0x1c71c71c71c71c71ULL, 0x1c71c71c71c71c71ULL, }, + { 0xfe7bb00c28625540ULL, 0xfe7bb00c28625540ULL, }, /* 64 */ + { 0x153f52fc4d93c708ULL, 0xfe7bb00c28625540ULL, }, + { 0xab2b2514b9cf8b80ULL, 0xfe7bb00c28625540ULL, }, + { 0xa942e2a05e31e24eULL, 0xfe7bb00c28625540ULL, }, + { 0xfe7bb00c28625540ULL, 0x153f52fc4d93c708ULL, }, + { 0x153f52fc4d93c708ULL, 0x153f52fc4d93c708ULL, }, + { 0xab2b2514b9cf8b80ULL, 0x153f52fc4d93c708ULL, }, + { 0xa942e2a05e31e24eULL, 0x153f52fc4d93c708ULL, }, + { 0xfe7bb00c28625540ULL, 0xab2b2514b9cf8b80ULL, }, /* 72 */ + { 0x153f52fc4d93c708ULL, 0xab2b2514b9cf8b80ULL, }, + { 0xab2b2514b9cf8b80ULL, 0xab2b2514b9cf8b80ULL, }, + { 0xa942e2a05e31e24eULL, 0xab2b2514b9cf8b80ULL, }, + { 0xfe7bb00c28625540ULL, 0xa942e2a05e31e24eULL, }, + { 0x153f52fc4d93c708ULL, 0xa942e2a05e31e24eULL, }, + { 0xab2b2514b9cf8b80ULL, 0xa942e2a05e31e24eULL, }, + { 0xa942e2a05e31e24eULL, 0xa942e2a05e31e24eULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKEV_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_b.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_b.c new file mode 100644 index 0000000000..ab363a0cdc --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKOD.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKOD.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x388ee338e3388ee3ULL, 0xffffffffffffffffULL, }, + { 0xc7711cc71cc7711cULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x388ee338e3388ee3ULL, 0x0000000000000000ULL, }, + { 0xc7711cc71cc7711cULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x388ee338e3388ee3ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xc7711cc71cc7711cULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x388ee338e3388ee3ULL, 0x5555555555555555ULL, }, + { 0xc7711cc71cc7711cULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x388ee338e3388ee3ULL, 0xccccccccccccccccULL, }, + { 0xc7711cc71cc7711cULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x388ee338e3388ee3ULL, 0x3333333333333333ULL, }, + { 0xc7711cc71cc7711cULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x388ee338e3388ee3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x388ee338e3388ee3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x388ee338e3388ee3ULL, }, + { 0x5555555555555555ULL, 0x388ee338e3388ee3ULL, }, + { 0xccccccccccccccccULL, 0x388ee338e3388ee3ULL, }, + { 0x3333333333333333ULL, 0x388ee338e3388ee3ULL, }, + { 0x388ee338e3388ee3ULL, 0x388ee338e3388ee3ULL, }, + { 0xc7711cc71cc7711cULL, 0x388ee338e3388ee3ULL, }, + { 0xffffffffffffffffULL, 0xc7711cc71cc7711cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc7711cc71cc7711cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc7711cc71cc7711cULL, }, + { 0x5555555555555555ULL, 0xc7711cc71cc7711cULL, }, + { 0xccccccccccccccccULL, 0xc7711cc71cc7711cULL, }, + { 0x3333333333333333ULL, 0xc7711cc71cc7711cULL, }, + { 0x388ee338e3388ee3ULL, 0xc7711cc71cc7711cULL, }, + { 0xc7711cc71cc7711cULL, 0xc7711cc71cc7711cULL, }, + { 0x4b0bfeb088e62855ULL, 0x4b0bfeb088e62855ULL, }, /* 64 */ + { 0x12bb1552fb004dc7ULL, 0x4b0bfeb088e62855ULL, }, + { 0x27c6ab25acaeb98bULL, 0x4b0bfeb088e62855ULL, }, + { 0x8d88a9e270165ee2ULL, 0x4b0bfeb088e62855ULL, }, + { 0x4b0bfeb088e62855ULL, 0x12bb1552fb004dc7ULL, }, + { 0x12bb1552fb004dc7ULL, 0x12bb1552fb004dc7ULL, }, + { 0x27c6ab25acaeb98bULL, 0x12bb1552fb004dc7ULL, }, + { 0x8d88a9e270165ee2ULL, 0x12bb1552fb004dc7ULL, }, + { 0x4b0bfeb088e62855ULL, 0x27c6ab25acaeb98bULL, }, /* 72 */ + { 0x12bb1552fb004dc7ULL, 0x27c6ab25acaeb98bULL, }, + { 0x27c6ab25acaeb98bULL, 0x27c6ab25acaeb98bULL, }, + { 0x8d88a9e270165ee2ULL, 0x27c6ab25acaeb98bULL, }, + { 0x4b0bfeb088e62855ULL, 0x8d88a9e270165ee2ULL, }, + { 0x12bb1552fb004dc7ULL, 0x8d88a9e270165ee2ULL, }, + { 0x27c6ab25acaeb98bULL, 0x8d88a9e270165ee2ULL, }, + { 0x8d88a9e270165ee2ULL, 0x8d88a9e270165ee2ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_d.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_d.c new file mode 100644 index 0000000000..09a61408bc --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKOD.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKOD.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e3ULL, 0xffffffffffffffffULL, }, + { 0xc71c71c71c71c71cULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x38e38e38e38e38e3ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x38e38e38e38e38e3ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xc71c71c71c71c71cULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x38e38e38e38e38e3ULL, 0x5555555555555555ULL, }, + { 0xc71c71c71c71c71cULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x38e38e38e38e38e3ULL, 0xccccccccccccccccULL, }, + { 0xc71c71c71c71c71cULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x38e38e38e38e38e3ULL, 0x3333333333333333ULL, }, + { 0xc71c71c71c71c71cULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x38e38e38e38e38e3ULL, }, + { 0xc71c71c71c71c71cULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71cULL, 0xc71c71c71c71c71cULL, }, + { 0x4b670b5efe7bb00cULL, 0x4b670b5efe7bb00cULL, }, /* 64 */ + { 0x12f7bb1a153f52fcULL, 0x4b670b5efe7bb00cULL, }, + { 0x27d8c6ffab2b2514ULL, 0x4b670b5efe7bb00cULL, }, + { 0x8df188d8a942e2a0ULL, 0x4b670b5efe7bb00cULL, }, + { 0x4b670b5efe7bb00cULL, 0x12f7bb1a153f52fcULL, }, + { 0x12f7bb1a153f52fcULL, 0x12f7bb1a153f52fcULL, }, + { 0x27d8c6ffab2b2514ULL, 0x12f7bb1a153f52fcULL, }, + { 0x8df188d8a942e2a0ULL, 0x12f7bb1a153f52fcULL, }, + { 0x4b670b5efe7bb00cULL, 0x27d8c6ffab2b2514ULL, }, /* 72 */ + { 0x12f7bb1a153f52fcULL, 0x27d8c6ffab2b2514ULL, }, + { 0x27d8c6ffab2b2514ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x8df188d8a942e2a0ULL, 0x27d8c6ffab2b2514ULL, }, + { 0x4b670b5efe7bb00cULL, 0x8df188d8a942e2a0ULL, }, + { 0x12f7bb1a153f52fcULL, 0x8df188d8a942e2a0ULL, }, + { 0x27d8c6ffab2b2514ULL, 0x8df188d8a942e2a0ULL, }, + { 0x8df188d8a942e2a0ULL, 0x8df188d8a942e2a0ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_h.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_h.c new file mode 100644 index 0000000000..d7a8c5b5af --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKOD.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKOD.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x38e3e38ee38e8e38ULL, 0xffffffffffffffffULL, }, + { 0xc71c1c711c7171c7ULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x38e3e38ee38e8e38ULL, 0x0000000000000000ULL, }, + { 0xc71c1c711c7171c7ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x38e3e38ee38e8e38ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xc71c1c711c7171c7ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x38e3e38ee38e8e38ULL, 0x5555555555555555ULL, }, + { 0xc71c1c711c7171c7ULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x38e3e38ee38e8e38ULL, 0xccccccccccccccccULL, }, + { 0xc71c1c711c7171c7ULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x38e3e38ee38e8e38ULL, 0x3333333333333333ULL, }, + { 0xc71c1c711c7171c7ULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e3e38ee38e8e38ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e3e38ee38e8e38ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e3e38ee38e8e38ULL, }, + { 0x5555555555555555ULL, 0x38e3e38ee38e8e38ULL, }, + { 0xccccccccccccccccULL, 0x38e3e38ee38e8e38ULL, }, + { 0x3333333333333333ULL, 0x38e3e38ee38e8e38ULL, }, + { 0x38e3e38ee38e8e38ULL, 0x38e3e38ee38e8e38ULL, }, + { 0xc71c1c711c7171c7ULL, 0x38e3e38ee38e8e38ULL, }, + { 0xffffffffffffffffULL, 0xc71c1c711c7171c7ULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c1c711c7171c7ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c1c711c7171c7ULL, }, + { 0x5555555555555555ULL, 0xc71c1c711c7171c7ULL, }, + { 0xccccccccccccccccULL, 0xc71c1c711c7171c7ULL, }, + { 0x3333333333333333ULL, 0xc71c1c711c7171c7ULL, }, + { 0x38e3e38ee38e8e38ULL, 0xc71c1c711c7171c7ULL, }, + { 0xc71c1c711c7171c7ULL, 0xc71c1c711c7171c7ULL, }, + { 0x4b67fe7b886a2862ULL, 0x4b67fe7b886a2862ULL, }, /* 64 */ + { 0x12f7153ffbbe4d93ULL, 0x4b67fe7b886a2862ULL, }, + { 0x27d8ab2bac5ab9cfULL, 0x4b67fe7b886a2862ULL, }, + { 0x8df1a942704f5e31ULL, 0x4b67fe7b886a2862ULL, }, + { 0x4b67fe7b886a2862ULL, 0x12f7153ffbbe4d93ULL, }, + { 0x12f7153ffbbe4d93ULL, 0x12f7153ffbbe4d93ULL, }, + { 0x27d8ab2bac5ab9cfULL, 0x12f7153ffbbe4d93ULL, }, + { 0x8df1a942704f5e31ULL, 0x12f7153ffbbe4d93ULL, }, + { 0x4b67fe7b886a2862ULL, 0x27d8ab2bac5ab9cfULL, }, /* 72 */ + { 0x12f7153ffbbe4d93ULL, 0x27d8ab2bac5ab9cfULL, }, + { 0x27d8ab2bac5ab9cfULL, 0x27d8ab2bac5ab9cfULL, }, + { 0x8df1a942704f5e31ULL, 0x27d8ab2bac5ab9cfULL, }, + { 0x4b67fe7b886a2862ULL, 0x8df1a942704f5e31ULL, }, + { 0x12f7153ffbbe4d93ULL, 0x8df1a942704f5e31ULL, }, + { 0x27d8ab2bac5ab9cfULL, 0x8df1a942704f5e31ULL, }, + { 0x8df1a942704f5e31ULL, 0x8df1a942704f5e31ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_w.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_w.c new file mode 100644 index 0000000000..4b732d0359 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_pckod_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction PCKOD.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "PCKOD.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 0 */ + { 0x0000000000000000ULL, 0xffffffffffffffffULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xffffffffffffffffULL, }, + { 0x5555555555555555ULL, 0xffffffffffffffffULL, }, + { 0xccccccccccccccccULL, 0xffffffffffffffffULL, }, + { 0x3333333333333333ULL, 0xffffffffffffffffULL, }, + { 0x38e38e38e38e38e3ULL, 0xffffffffffffffffULL, }, + { 0xc71c71c71c71c71cULL, 0xffffffffffffffffULL, }, + { 0xffffffffffffffffULL, 0x0000000000000000ULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x0000000000000000ULL, }, + { 0x5555555555555555ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0x0000000000000000ULL, }, + { 0x3333333333333333ULL, 0x0000000000000000ULL, }, + { 0x38e38e38e38e38e3ULL, 0x0000000000000000ULL, }, + { 0xc71c71c71c71c71cULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xaaaaaaaaaaaaaaaaULL, }, /* 16 */ + { 0x0000000000000000ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x5555555555555555ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xccccccccccccccccULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x3333333333333333ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x38e38e38e38e38e3ULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xc71c71c71c71c71cULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0xffffffffffffffffULL, 0x5555555555555555ULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x5555555555555555ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x5555555555555555ULL, }, + { 0x5555555555555555ULL, 0x5555555555555555ULL, }, + { 0xccccccccccccccccULL, 0x5555555555555555ULL, }, + { 0x3333333333333333ULL, 0x5555555555555555ULL, }, + { 0x38e38e38e38e38e3ULL, 0x5555555555555555ULL, }, + { 0xc71c71c71c71c71cULL, 0x5555555555555555ULL, }, + { 0xffffffffffffffffULL, 0xccccccccccccccccULL, }, /* 32 */ + { 0x0000000000000000ULL, 0xccccccccccccccccULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xccccccccccccccccULL, }, + { 0x5555555555555555ULL, 0xccccccccccccccccULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x3333333333333333ULL, 0xccccccccccccccccULL, }, + { 0x38e38e38e38e38e3ULL, 0xccccccccccccccccULL, }, + { 0xc71c71c71c71c71cULL, 0xccccccccccccccccULL, }, + { 0xffffffffffffffffULL, 0x3333333333333333ULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x3333333333333333ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x3333333333333333ULL, }, + { 0x5555555555555555ULL, 0x3333333333333333ULL, }, + { 0xccccccccccccccccULL, 0x3333333333333333ULL, }, + { 0x3333333333333333ULL, 0x3333333333333333ULL, }, + { 0x38e38e38e38e38e3ULL, 0x3333333333333333ULL, }, + { 0xc71c71c71c71c71cULL, 0x3333333333333333ULL, }, + { 0xffffffffffffffffULL, 0x38e38e38e38e38e3ULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x38e38e38e38e38e3ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0x38e38e38e38e38e3ULL, }, + { 0x5555555555555555ULL, 0x38e38e38e38e38e3ULL, }, + { 0xccccccccccccccccULL, 0x38e38e38e38e38e3ULL, }, + { 0x3333333333333333ULL, 0x38e38e38e38e38e3ULL, }, + { 0x38e38e38e38e38e3ULL, 0x38e38e38e38e38e3ULL, }, + { 0xc71c71c71c71c71cULL, 0x38e38e38e38e38e3ULL, }, + { 0xffffffffffffffffULL, 0xc71c71c71c71c71cULL, }, /* 56 */ + { 0x0000000000000000ULL, 0xc71c71c71c71c71cULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xc71c71c71c71c71cULL, }, + { 0x5555555555555555ULL, 0xc71c71c71c71c71cULL, }, + { 0xccccccccccccccccULL, 0xc71c71c71c71c71cULL, }, + { 0x3333333333333333ULL, 0xc71c71c71c71c71cULL, }, + { 0x38e38e38e38e38e3ULL, 0xc71c71c71c71c71cULL, }, + { 0xc71c71c71c71c71cULL, 0xc71c71c71c71c71cULL, }, + { 0x4b670b5e886ae6ccULL, 0x4b670b5e886ae6ccULL, }, /* 64 */ + { 0x12f7bb1afbbe0063ULL, 0x4b670b5e886ae6ccULL, }, + { 0x27d8c6ffac5aaeaaULL, 0x4b670b5e886ae6ccULL, }, + { 0x8df188d8704f164dULL, 0x4b670b5e886ae6ccULL, }, + { 0x4b670b5e886ae6ccULL, 0x12f7bb1afbbe0063ULL, }, + { 0x12f7bb1afbbe0063ULL, 0x12f7bb1afbbe0063ULL, }, + { 0x27d8c6ffac5aaeaaULL, 0x12f7bb1afbbe0063ULL, }, + { 0x8df188d8704f164dULL, 0x12f7bb1afbbe0063ULL, }, + { 0x4b670b5e886ae6ccULL, 0x27d8c6ffac5aaeaaULL, }, /* 72 */ + { 0x12f7bb1afbbe0063ULL, 0x27d8c6ffac5aaeaaULL, }, + { 0x27d8c6ffac5aaeaaULL, 0x27d8c6ffac5aaeaaULL, }, + { 0x8df188d8704f164dULL, 0x27d8c6ffac5aaeaaULL, }, + { 0x4b670b5e886ae6ccULL, 0x8df188d8704f164dULL, }, + { 0x12f7bb1afbbe0063ULL, 0x8df188d8704f164dULL, }, + { 0x27d8c6ffac5aaeaaULL, 0x8df188d8704f164dULL, }, + { 0x8df188d8704f164dULL, 0x8df188d8704f164dULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_PCKOD_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_b.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_b.c new file mode 100644 index 0000000000..d9ccf575fa --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_b.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction VSHF.B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "VSHF.B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e8e8e8e8e8e8e8eULL, 0x8e8e8e8e8e8e8e8eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4040404040404040ULL, 0x4040404040404040ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4040404040404040ULL, 0x4040404040404040ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4040404040404040ULL, 0x4040404040404040ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x4040404040404040ULL, 0x4040404040404040ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8080808080808080ULL, 0x8080808080808080ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_B(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_B(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_d.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_d.c new file mode 100644 index 0000000000..6c555fbb23 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_d.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction VSHF.D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "VSHF.D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38e38e38e38e38eULL, 0xe38e38e38e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x886ae6cc28625540ULL, 0x886ae6cc28625540ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xac5aaeaab9cf8b80ULL, 0xac5aaeaab9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_D(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_D(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_h.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_h.c new file mode 100644 index 0000000000..9dfcb51fe5 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_h.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction VSHF.H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "VSHF.H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xe38ee38ee38ee38eULL, 0xe38ee38ee38ee38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5540554055405540ULL, 0x5540554055405540ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8b808b808b808b80ULL, 0x8b808b808b808b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5540554055405540ULL, 0x5540554055405540ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8b808b808b808b80ULL, 0x8b808b808b808b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5540554055405540ULL, 0x5540554055405540ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8b808b808b808b80ULL, 0x8b808b808b808b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x5540554055405540ULL, 0x5540554055405540ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8b808b808b808b80ULL, 0x8b808b808b808b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_H(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_H(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_w.c b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_w.c new file mode 100644 index 0000000000..97074c0924 --- /dev/null +++ b/tests/tcg/mips/user/ase/msa/pack/test_msa_vshf_w.c @@ -0,0 +1,153 @@ +/* + * Test program for MSA instruction VSHF.W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_msa.h" +#include "../../../../include/test_inputs_128.h" +#include "../../../../include/test_utils_128.h" + +#define TEST_COUNT_TOTAL ( \ + (PATTERN_INPUTS_SHORT_COUNT) * (PATTERN_INPUTS_SHORT_COUNT) + \ + (RANDOM_INPUTS_SHORT_COUNT) * (RANDOM_INPUTS_SHORT_COUNT)) + + +int32_t main(void) +{ + char *instruction_name = "VSHF.W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b128_result[TEST_COUNT_TOTAL][2]; + uint64_t b128_expect[TEST_COUNT_TOTAL][2] = { + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, /* 0 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 8 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 16 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 24 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 32 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 40 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 48 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xffffffffffffffffULL, 0xffffffffffffffffULL, }, /* 56 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xaaaaaaaaaaaaaaaaULL, 0xaaaaaaaaaaaaaaaaULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xccccccccccccccccULL, 0xccccccccccccccccULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x8e38e38e8e38e38eULL, 0x8e38e38e8e38e38eULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2862554028625540ULL, 0x2862554028625540ULL, }, /* 64 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xb9cf8b80b9cf8b80ULL, 0xb9cf8b80b9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2862554028625540ULL, 0x2862554028625540ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xb9cf8b80b9cf8b80ULL, 0xb9cf8b80b9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2862554028625540ULL, 0x2862554028625540ULL, }, /* 72 */ + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xb9cf8b80b9cf8b80ULL, 0xb9cf8b80b9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0x2862554028625540ULL, 0x2862554028625540ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + { 0xb9cf8b80b9cf8b80ULL, 0xb9cf8b80b9cf8b80ULL, }, + { 0x0000000000000000ULL, 0x0000000000000000ULL, }, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_W(b128_pattern[i], b128_pattern[j], + b128_result[PATTERN_INPUTS_SHORT_COUNT * i + j]); + } + } + + for (i = 0; i < RANDOM_INPUTS_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_SHORT_COUNT; j++) { + do_msa_VSHF_W(b128_random[i], b128_random[j], + b128_result[((PATTERN_INPUTS_SHORT_COUNT) * + (PATTERN_INPUTS_SHORT_COUNT)) + + RANDOM_INPUTS_SHORT_COUNT * i + j]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + &b128_result[0][0], &b128_expect[0][0]); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_clo.c b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_clo.c new file mode 100644 index 0000000000..ca9a739c50 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_clo.c @@ -0,0 +1,144 @@ +/* + * Test program for MIPS64R6 instruction CLO + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "CLO"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000020ULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000002ULL, + 0x0000000000000000ULL, + 0x0000000000000003ULL, + 0x0000000000000000ULL, + 0x0000000000000004ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000005ULL, + 0x0000000000000000ULL, + 0x0000000000000006ULL, + 0x0000000000000000ULL, + 0x0000000000000007ULL, + 0x0000000000000000ULL, + 0x0000000000000008ULL, /* 16 */ + 0x0000000000000000ULL, + 0x0000000000000009ULL, + 0x0000000000000000ULL, + 0x000000000000000aULL, + 0x0000000000000000ULL, + 0x000000000000000bULL, + 0x0000000000000000ULL, + 0x000000000000000cULL, /* 24 */ + 0x0000000000000000ULL, + 0x000000000000000dULL, + 0x0000000000000000ULL, + 0x000000000000000eULL, + 0x0000000000000000ULL, + 0x000000000000000fULL, + 0x0000000000000000ULL, + 0x0000000000000010ULL, /* 32 */ + 0x0000000000000000ULL, + 0x0000000000000011ULL, + 0x0000000000000000ULL, + 0x0000000000000012ULL, + 0x0000000000000000ULL, + 0x0000000000000013ULL, + 0x0000000000000000ULL, + 0x0000000000000014ULL, /* 40 */ + 0x0000000000000000ULL, + 0x0000000000000015ULL, + 0x0000000000000000ULL, + 0x0000000000000016ULL, + 0x0000000000000000ULL, + 0x0000000000000017ULL, + 0x0000000000000000ULL, + 0x0000000000000018ULL, /* 48 */ + 0x0000000000000000ULL, + 0x0000000000000019ULL, + 0x0000000000000000ULL, + 0x000000000000001aULL, + 0x0000000000000000ULL, + 0x000000000000001bULL, + 0x0000000000000000ULL, + 0x000000000000001cULL, /* 56 */ + 0x0000000000000000ULL, + 0x000000000000001dULL, + 0x0000000000000000ULL, + 0x000000000000001eULL, + 0x0000000000000000ULL, + 0x000000000000001fULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, /* 64 */ + 0x0000000000000005ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000002ULL, + 0x0000000000000001ULL, + 0x0000000000000006ULL, + 0x0000000000000000ULL, /* 72 */ + 0x0000000000000001ULL, + 0x0000000000000001ULL, + 0x0000000000000001ULL, + 0x0000000000000003ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_64_COUNT) { + do_mips64r6_CLO(b64_pattern_se + i, b64_result + i); + } else { + do_mips64r6_CLO(b64_random_se + (i - PATTERN_INPUTS_64_COUNT), + b64_result + i); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_clz.c b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_clz.c new file mode 100644 index 0000000000..0a7d21dcbd --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_clz.c @@ -0,0 +1,144 @@ +/* + * Test program for MIPS64R6 instruction CLZ + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "CLZ"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0x0000000000000020ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000002ULL, + 0x0000000000000000ULL, + 0x0000000000000003ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000004ULL, + 0x0000000000000000ULL, + 0x0000000000000005ULL, + 0x0000000000000000ULL, + 0x0000000000000006ULL, + 0x0000000000000000ULL, + 0x0000000000000007ULL, + 0x0000000000000000ULL, /* 16 */ + 0x0000000000000008ULL, + 0x0000000000000000ULL, + 0x0000000000000009ULL, + 0x0000000000000000ULL, + 0x000000000000000aULL, + 0x0000000000000000ULL, + 0x000000000000000bULL, + 0x0000000000000000ULL, /* 24 */ + 0x000000000000000cULL, + 0x0000000000000000ULL, + 0x000000000000000dULL, + 0x0000000000000000ULL, + 0x000000000000000eULL, + 0x0000000000000000ULL, + 0x000000000000000fULL, + 0x0000000000000000ULL, /* 32 */ + 0x0000000000000010ULL, + 0x0000000000000000ULL, + 0x0000000000000011ULL, + 0x0000000000000000ULL, + 0x0000000000000012ULL, + 0x0000000000000000ULL, + 0x0000000000000013ULL, + 0x0000000000000000ULL, /* 40 */ + 0x0000000000000014ULL, + 0x0000000000000000ULL, + 0x0000000000000015ULL, + 0x0000000000000000ULL, + 0x0000000000000016ULL, + 0x0000000000000000ULL, + 0x0000000000000017ULL, + 0x0000000000000000ULL, /* 48 */ + 0x0000000000000018ULL, + 0x0000000000000000ULL, + 0x0000000000000019ULL, + 0x0000000000000000ULL, + 0x000000000000001aULL, + 0x0000000000000000ULL, + 0x000000000000001bULL, + 0x0000000000000000ULL, /* 56 */ + 0x000000000000001cULL, + 0x0000000000000000ULL, + 0x000000000000001dULL, + 0x0000000000000000ULL, + 0x000000000000001eULL, + 0x0000000000000000ULL, + 0x000000000000001fULL, + 0x0000000000000000ULL, /* 64 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000002ULL, /* 72 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_64_COUNT) { + do_mips64r6_CLZ(b64_pattern_se + i, b64_result + i); + } else { + do_mips64r6_CLZ(b64_random_se + (i - PATTERN_INPUTS_64_COUNT), + b64_result + i); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_dclo.c b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_dclo.c new file mode 100644 index 0000000000..72bb473f66 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_dclo.c @@ -0,0 +1,144 @@ +/* + * Test program for MIPS64R6 instruction DCLO + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DCLO"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000040ULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000002ULL, + 0x0000000000000000ULL, + 0x0000000000000003ULL, + 0x0000000000000000ULL, + 0x0000000000000004ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000005ULL, + 0x0000000000000000ULL, + 0x0000000000000006ULL, + 0x0000000000000000ULL, + 0x0000000000000007ULL, + 0x0000000000000000ULL, + 0x0000000000000008ULL, /* 16 */ + 0x0000000000000000ULL, + 0x0000000000000009ULL, + 0x0000000000000000ULL, + 0x000000000000000aULL, + 0x0000000000000000ULL, + 0x000000000000000bULL, + 0x0000000000000000ULL, + 0x000000000000000cULL, /* 24 */ + 0x0000000000000000ULL, + 0x000000000000000dULL, + 0x0000000000000000ULL, + 0x000000000000000eULL, + 0x0000000000000000ULL, + 0x000000000000000fULL, + 0x0000000000000000ULL, + 0x0000000000000010ULL, /* 32 */ + 0x0000000000000000ULL, + 0x0000000000000011ULL, + 0x0000000000000000ULL, + 0x0000000000000012ULL, + 0x0000000000000000ULL, + 0x0000000000000013ULL, + 0x0000000000000000ULL, + 0x0000000000000014ULL, /* 40 */ + 0x0000000000000000ULL, + 0x0000000000000015ULL, + 0x0000000000000000ULL, + 0x0000000000000016ULL, + 0x0000000000000000ULL, + 0x0000000000000017ULL, + 0x0000000000000000ULL, + 0x0000000000000018ULL, /* 48 */ + 0x0000000000000000ULL, + 0x0000000000000019ULL, + 0x0000000000000000ULL, + 0x000000000000001aULL, + 0x0000000000000000ULL, + 0x000000000000001bULL, + 0x0000000000000000ULL, + 0x000000000000001cULL, /* 56 */ + 0x0000000000000000ULL, + 0x000000000000001dULL, + 0x0000000000000000ULL, + 0x000000000000001eULL, + 0x0000000000000000ULL, + 0x000000000000001fULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, /* 64 */ + 0x0000000000000005ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000002ULL, + 0x0000000000000001ULL, + 0x0000000000000006ULL, + 0x0000000000000000ULL, /* 72 */ + 0x0000000000000001ULL, + 0x0000000000000001ULL, + 0x0000000000000001ULL, + 0x0000000000000003ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_64_COUNT) { + do_mips64r6_DCLO((void *)&b64_pattern[i], (void *)&b64_result[i]); + } else { + do_mips64r6_DCLO((void *)&b64_random[i - PATTERN_INPUTS_64_COUNT], + (void *)&b64_result[i]); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_dclz.c b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_dclz.c new file mode 100644 index 0000000000..87df12b36c --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/bit-count/test_mips64r6_dclz.c @@ -0,0 +1,144 @@ +/* + * Test program for MIPS64R6 instruction DCLZ + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DCLZ"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0x0000000000000040ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000002ULL, + 0x0000000000000000ULL, + 0x0000000000000003ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000004ULL, + 0x0000000000000000ULL, + 0x0000000000000005ULL, + 0x0000000000000000ULL, + 0x0000000000000006ULL, + 0x0000000000000000ULL, + 0x0000000000000007ULL, + 0x0000000000000000ULL, /* 16 */ + 0x0000000000000008ULL, + 0x0000000000000000ULL, + 0x0000000000000009ULL, + 0x0000000000000000ULL, + 0x000000000000000aULL, + 0x0000000000000000ULL, + 0x000000000000000bULL, + 0x0000000000000000ULL, /* 24 */ + 0x000000000000000cULL, + 0x0000000000000000ULL, + 0x000000000000000dULL, + 0x0000000000000000ULL, + 0x000000000000000eULL, + 0x0000000000000000ULL, + 0x000000000000000fULL, + 0x0000000000000000ULL, /* 32 */ + 0x0000000000000010ULL, + 0x0000000000000000ULL, + 0x0000000000000011ULL, + 0x0000000000000000ULL, + 0x0000000000000012ULL, + 0x0000000000000000ULL, + 0x0000000000000013ULL, + 0x0000000000000000ULL, /* 40 */ + 0x0000000000000014ULL, + 0x0000000000000000ULL, + 0x0000000000000015ULL, + 0x0000000000000000ULL, + 0x0000000000000016ULL, + 0x0000000000000000ULL, + 0x0000000000000017ULL, + 0x0000000000000000ULL, /* 48 */ + 0x0000000000000018ULL, + 0x0000000000000000ULL, + 0x0000000000000019ULL, + 0x0000000000000000ULL, + 0x000000000000001aULL, + 0x0000000000000000ULL, + 0x000000000000001bULL, + 0x0000000000000000ULL, /* 56 */ + 0x000000000000001cULL, + 0x0000000000000000ULL, + 0x000000000000001dULL, + 0x0000000000000000ULL, + 0x000000000000001eULL, + 0x0000000000000000ULL, + 0x000000000000001fULL, + 0x0000000000000000ULL, /* 64 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000002ULL, /* 72 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_64_COUNT) { + do_mips64r6_DCLZ(b64_pattern + i, b64_result + i); + } else { + do_mips64r6_DCLZ(b64_random + (i - PATTERN_INPUTS_64_COUNT), + b64_result + i); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/bit-swap/test_mips64r6_bitswap.c b/tests/tcg/mips/user/isa/mips64r6/bit-swap/test_mips64r6_bitswap.c new file mode 100644 index 0000000000..639850df6a --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/bit-swap/test_mips64r6_bitswap.c @@ -0,0 +1,144 @@ +/* + * Test program for MIPS64R6 instruction BITSWAP + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "BITSWAP"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffffffffffULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000055555555ULL, + 0xffffffffaaaaaaaaULL, + 0x0000000033333333ULL, + 0xffffffffccccccccULL, + 0x00000000711cc771ULL, + 0xffffffff8ee3388eULL, + 0x000000000f0f0f0fULL, /* 8 */ + 0xfffffffff0f0f0f0ULL, + 0x00000000071f7cf0ULL, + 0xfffffffff8e0830fULL, + 0xfffffffff0033ff0ULL, + 0x000000000ffcc00fULL, + 0x0000000007fc017fULL, + 0xfffffffff803fe80ULL, + 0xffffffffff00ff00ULL, /* 16 */ + 0x0000000000ff00ffULL, + 0xfffffffff01fc07fULL, + 0x000000000fe03f80ULL, + 0x0000000000ff03f0ULL, + 0xffffffffff00fc0fULL, + 0x0000000001f07f00ULL, + 0xfffffffffe0f80ffULL, + 0x000000000f00ff0fULL, /* 24 */ + 0xfffffffff0ff00f0ULL, + 0x000000007f00f0ffULL, + 0xffffffff80ff0f00ULL, + 0xffffffffff0300ffULL, + 0x0000000000fcff00ULL, + 0xffffffffff1f00f0ULL, + 0x0000000000e0ff0fULL, + 0xffffffffffff0000ULL, /* 32 */ + 0x000000000000ffffULL, + 0xfffffffffcff0700ULL, + 0x000000000300f8ffULL, + 0xfffffffff0ff3f00ULL, + 0x000000000f00c0ffULL, + 0xffffffffc0ffff01ULL, + 0x000000003f0000feULL, + 0x0000000000ffff0fULL, /* 40 */ + 0xffffffffff0000f0ULL, + 0x0000000000fcff7fULL, + 0xffffffffff030080ULL, + 0x0000000000f0ffffULL, + 0xffffffffff0f0000ULL, + 0x0000000000c0ffffULL, + 0xffffffffff3f0000ULL, + 0x000000000000ffffULL, /* 48 */ + 0xffffffffffff0000ULL, + 0x000000000000fcffULL, + 0xffffffffffff0300ULL, + 0x000000000000f0ffULL, + 0xffffffffffff0f00ULL, + 0x000000000000c0ffULL, + 0xffffffffffff3f00ULL, + 0x00000000000000ffULL, /* 56 */ + 0xffffffffffffff00ULL, + 0x00000000000000fcULL, + 0xffffffffffffff03ULL, + 0x00000000000000f0ULL, + 0xffffffffffffff0fULL, + 0x00000000000000c0ULL, + 0xffffffffffffff3fULL, + 0x000000001446aa02ULL, /* 64 */ + 0xffffffffb2c9e310ULL, + 0xffffffff9df3d101ULL, + 0x000000007a8c4772ULL, + 0xffffffffbef5421aULL, + 0xffffffffff50749fULL, + 0xffffffffa6533d52ULL, + 0x000000005965ed41ULL, + 0x000000006a756792ULL, /* 72 */ + 0xffffffffa69ba7ebULL, + 0xffffffff93d363d8ULL, + 0xffffffff8c152675ULL, + 0x00000000654a5750ULL, + 0xffffffff98c48615ULL, + 0x00000000447def39ULL, + 0x000000004f9a7bb5ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_64_COUNT) { + do_mips64r6_BITSWAP(b64_pattern + i, b64_result + i); + } else { + do_mips64r6_BITSWAP(b64_random + (i - PATTERN_INPUTS_64_COUNT), + b64_result + i); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/bit-swap/test_mips64r6_dbitswap.c b/tests/tcg/mips/user/isa/mips64r6/bit-swap/test_mips64r6_dbitswap.c new file mode 100644 index 0000000000..366fe61a95 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/bit-swap/test_mips64r6_dbitswap.c @@ -0,0 +1,144 @@ +/* + * Test program for MIPS64R6 instruction DBITSWAP + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DBITSWAP"; + int32_t ret; + uint32_t i; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffffffffffULL, /* 0 */ + 0x0000000000000000ULL, + 0x5555555555555555ULL, + 0xaaaaaaaaaaaaaaaaULL, + 0x3333333333333333ULL, + 0xccccccccccccccccULL, + 0xc7711cc7711cc771ULL, + 0x388ee3388ee3388eULL, + 0x0f0f0f0f0f0f0f0fULL, /* 8 */ + 0xf0f0f0f0f0f0f0f0ULL, + 0x1f7cf0c1071f7cf0ULL, + 0xe0830f3ef8e0830fULL, + 0x3ff0033ff0033ff0ULL, + 0xc00ffcc00ffcc00fULL, + 0x7fc01ff007fc017fULL, + 0x803fe00ff803fe80ULL, + 0xff00ff00ff00ff00ULL, /* 16 */ + 0x00ff00ff00ff00ffULL, + 0xff01fc07f01fc07fULL, + 0x00fe03f80fe03f80ULL, + 0xff03f03f00ff03f0ULL, + 0x00fc0fc0ff00fc0fULL, + 0xff07c0ff01f07f00ULL, + 0x00f83f00fe0f80ffULL, + 0xff0f00ff0f00ff0fULL, /* 24 */ + 0x00f0ff00f0ff00f0ULL, + 0xff1f00fc7f00f0ffULL, + 0x00e0ff0380ff0f00ULL, + 0xff3f00f0ff0300ffULL, + 0x00c0ff0f00fcff00ULL, + 0xff7f00c0ff1f00f0ULL, + 0x0080ff3f00e0ff0fULL, + 0xffff0000ffff0000ULL, /* 32 */ + 0x0000ffff0000ffffULL, + 0xffff0100fcff0700ULL, + 0x0000feff0300f8ffULL, + 0xffff0300f0ff3f00ULL, + 0x0000fcff0f00c0ffULL, + 0xffff0700c0ffff01ULL, + 0x0000f8ff3f0000feULL, + 0xffff0f0000ffff0fULL, /* 40 */ + 0x0000f0ffff0000f0ULL, + 0xffff1f0000fcff7fULL, + 0x0000e0ffff030080ULL, + 0xffff3f0000f0ffffULL, + 0x0000c0ffff0f0000ULL, + 0xffff7f0000c0ffffULL, + 0x000080ffff3f0000ULL, + 0xffffff000000ffffULL, /* 48 */ + 0x000000ffffff0000ULL, + 0xffffff010000fcffULL, + 0x000000feffff0300ULL, + 0xffffff030000f0ffULL, + 0x000000fcffff0f00ULL, + 0xffffff070000c0ffULL, + 0x000000f8ffff3f00ULL, + 0xffffff0f000000ffULL, /* 56 */ + 0x000000f0ffffff00ULL, + 0xffffff1f000000fcULL, + 0x000000e0ffffff03ULL, + 0xffffff3f000000f0ULL, + 0x000000c0ffffff0fULL, + 0xffffff7f000000c0ULL, + 0x00000080ffffff3fULL, + 0x115667331446aa02ULL, /* 64 */ + 0xdf7d00c6b2c9e310ULL, + 0x355a75559df3d101ULL, + 0x0ef268b27a8c4772ULL, + 0x9d49d63ebef5421aULL, + 0x0be47d91ff50749fULL, + 0x1ddc1a60a6533d52ULL, + 0x3ff1c40f5965ed41ULL, + 0x047890b36a756792ULL, /* 72 */ + 0xa53e9bc8a69ba7ebULL, + 0x45176faf93d363d8ULL, + 0x15394f8f8c152675ULL, + 0x67281c97654a5750ULL, + 0x2952acbf98c48615ULL, + 0x620c42c6447def39ULL, + 0xd15ae5454f9a7bb5ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < TEST_COUNT_TOTAL; i++) { + if (i < PATTERN_INPUTS_64_COUNT) { + do_mips64r6_DBITSWAP(b64_pattern + i, b64_result + i); + } else { + do_mips64r6_DBITSWAP(b64_random + (i - PATTERN_INPUTS_64_COUNT), + b64_result + i); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmuh.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmuh.c new file mode 100644 index 0000000000..c049378573 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmuh.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DMUH + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DMUH"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0x0000000000000000ULL, + 0x1c71c71c71c71c72ULL, + 0xe38e38e38e38e38eULL, + 0x1111111111111111ULL, + 0xeeeeeeeeeeeeeeeeULL, + 0x097b425ed097b426ULL, + 0xf684bda12f684bdaULL, + 0xffffffffffffffffULL, /* 24 */ + 0x0000000000000000ULL, + 0xe38e38e38e38e38eULL, + 0x1c71c71c71c71c71ULL, + 0xeeeeeeeeeeeeeeeeULL, + 0x1111111111111110ULL, + 0xf684bda12f684bdaULL, + 0x097b425ed097b425ULL, + 0x0000000000000000ULL, /* 32 */ + 0x0000000000000000ULL, + 0x1111111111111111ULL, + 0xeeeeeeeeeeeeeeeeULL, + 0x0a3d70a3d70a3d70ULL, + 0xf5c28f5c28f5c28fULL, + 0x05b05b05b05b05b0ULL, + 0xfa4fa4fa4fa4fa4fULL, + 0xffffffffffffffffULL, /* 40 */ + 0x0000000000000000ULL, + 0xeeeeeeeeeeeeeeeeULL, + 0x1111111111111110ULL, + 0xf5c28f5c28f5c28fULL, + 0x0a3d70a3d70a3d70ULL, + 0xfa4fa4fa4fa4fa4fULL, + 0x05b05b05b05b05b0ULL, + 0x0000000000000000ULL, /* 48 */ + 0x0000000000000000ULL, + 0x097b425ed097b426ULL, + 0xf684bda12f684bdaULL, + 0x05b05b05b05b05b0ULL, + 0xfa4fa4fa4fa4fa4fULL, + 0x0329161f9add3c0cULL, + 0xfcd6e9e06522c3f3ULL, + 0xffffffffffffffffULL, /* 56 */ + 0x0000000000000000ULL, + 0xf684bda12f684bdaULL, + 0x097b425ed097b425ULL, + 0xfa4fa4fa4fa4fa4fULL, + 0x05b05b05b05b05b0ULL, + 0xfcd6e9e06522c3f3ULL, + 0x0329161f9add3c0cULL, + 0x37dbf4448b48bce3ULL, /* 64 */ + 0x01fd28a6ebd66e19ULL, + 0x271290430f9643afULL, + 0xcb89d38b96a86603ULL, + 0x01fd28a6ebd66e19ULL, + 0x00122100b25f881aULL, + 0x016425c3dacd63e9ULL, + 0xfe21cf6e9b332df5ULL, + 0x271290430f9643afULL, /* 72 */ + 0x016425c3dacd63e9ULL, + 0x1b549d7f3d46f8d3ULL, + 0xdb4dd51d1b7c58f2ULL, + 0xcb89d38b96a86603ULL, + 0xfe21cf6e9b332df5ULL, + 0xdb4dd51d1b7c58f2ULL, + 0x31454bf2781d2c60ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMUH(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMUH(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmuhu.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmuhu.c new file mode 100644 index 0000000000..dfb41951b1 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmuhu.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DMUHU + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DMUHU"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xfffffffffffffffeULL, /* 0 */ + 0x0000000000000000ULL, + 0xaaaaaaaaaaaaaaa9ULL, + 0x5555555555555554ULL, + 0xcccccccccccccccbULL, + 0x3333333333333332ULL, + 0xe38e38e38e38e38dULL, + 0x1c71c71c71c71c70ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0xaaaaaaaaaaaaaaa9ULL, /* 16 */ + 0x0000000000000000ULL, + 0x71c71c71c71c71c6ULL, + 0x38e38e38e38e38e3ULL, + 0x8888888888888887ULL, + 0x2222222222222221ULL, + 0x97b425ed097b425eULL, + 0x12f684bda12f684bULL, + 0x5555555555555554ULL, /* 24 */ + 0x0000000000000000ULL, + 0x38e38e38e38e38e3ULL, + 0x1c71c71c71c71c71ULL, + 0x4444444444444443ULL, + 0x1111111111111110ULL, + 0x4bda12f684bda12fULL, + 0x097b425ed097b425ULL, + 0xcccccccccccccccbULL, /* 32 */ + 0x0000000000000000ULL, + 0x8888888888888887ULL, + 0x4444444444444443ULL, + 0xa3d70a3d70a3d708ULL, + 0x28f5c28f5c28f5c2ULL, + 0xb60b60b60b60b60aULL, + 0x16c16c16c16c16c0ULL, + 0x3333333333333332ULL, /* 40 */ + 0x0000000000000000ULL, + 0x2222222222222221ULL, + 0x1111111111111110ULL, + 0x28f5c28f5c28f5c2ULL, + 0x0a3d70a3d70a3d70ULL, + 0x2d82d82d82d82d82ULL, + 0x05b05b05b05b05b0ULL, + 0xe38e38e38e38e38dULL, /* 48 */ + 0x0000000000000000ULL, + 0x97b425ed097b425eULL, + 0x4bda12f684bda12fULL, + 0xb60b60b60b60b60aULL, + 0x2d82d82d82d82d82ULL, + 0xca4587e6b74f0328ULL, + 0x1948b0fcd6e9e064ULL, + 0x1c71c71c71c71c70ULL, /* 56 */ + 0x0000000000000000ULL, + 0x12f684bda12f684bULL, + 0x097b425ed097b425ULL, + 0x16c16c16c16c16c0ULL, + 0x05b05b05b05b05b0ULL, + 0x1948b0fcd6e9e064ULL, + 0x0329161f9add3c0cULL, + 0x48b1c1dcdc0d6763ULL, /* 64 */ + 0x86260fd661cc8a61ULL, + 0x5bd825b9f1c8246fULL, + 0x3bd8e9d8f4da4851ULL, + 0x86260fd661cc8a61ULL, + 0xf78e21c74d87162aULL, + 0xa97cd4d1e230b671ULL, + 0x6e70e5bbf9651043ULL, + 0x5bd825b9f1c8246fULL, /* 72 */ + 0xa97cd4d1e230b671ULL, + 0x7409fad4b0e60fd3ULL, + 0x4b9ceb6a79ae3b40ULL, + 0x3bd8e9d8f4da4851ULL, + 0x6e70e5bbf9651043ULL, + 0x4b9ceb6a79ae3b40ULL, + 0x31454bf2781d2c60ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMUHU(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMUHU(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmul.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmul.c new file mode 100644 index 0000000000..3c337dd648 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmul.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DMUL + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DMUL"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000001ULL, /* 0 */ + 0x0000000000000000ULL, + 0x5555555555555556ULL, + 0xaaaaaaaaaaaaaaabULL, + 0x3333333333333334ULL, + 0xcccccccccccccccdULL, + 0x1c71c71c71c71c72ULL, + 0xe38e38e38e38e38fULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x5555555555555556ULL, /* 16 */ + 0x0000000000000000ULL, + 0x38e38e38e38e38e4ULL, + 0x1c71c71c71c71c72ULL, + 0x7777777777777778ULL, + 0xdddddddddddddddeULL, + 0x12f684bda12f684cULL, + 0x425ed097b425ed0aULL, + 0xaaaaaaaaaaaaaaabULL, /* 24 */ + 0x0000000000000000ULL, + 0x1c71c71c71c71c72ULL, + 0x8e38e38e38e38e39ULL, + 0xbbbbbbbbbbbbbbbcULL, + 0xeeeeeeeeeeeeeeefULL, + 0x097b425ed097b426ULL, + 0xa12f684bda12f685ULL, + 0x3333333333333334ULL, /* 32 */ + 0x0000000000000000ULL, + 0x7777777777777778ULL, + 0xbbbbbbbbbbbbbbbcULL, + 0xf5c28f5c28f5c290ULL, + 0x3d70a3d70a3d70a4ULL, + 0x7d27d27d27d27d28ULL, + 0xb60b60b60b60b60cULL, + 0xcccccccccccccccdULL, /* 40 */ + 0x0000000000000000ULL, + 0xdddddddddddddddeULL, + 0xeeeeeeeeeeeeeeefULL, + 0x3d70a3d70a3d70a4ULL, + 0x8f5c28f5c28f5c29ULL, + 0x9f49f49f49f49f4aULL, + 0x2d82d82d82d82d83ULL, + 0x1c71c71c71c71c72ULL, /* 48 */ + 0x0000000000000000ULL, + 0x12f684bda12f684cULL, + 0x097b425ed097b426ULL, + 0x7d27d27d27d27d28ULL, + 0x9f49f49f49f49f4aULL, + 0xb0fcd6e9e06522c4ULL, + 0x6b74f0329161f9aeULL, + 0xe38e38e38e38e38fULL, /* 56 */ + 0x0000000000000000ULL, + 0x425ed097b425ed0aULL, + 0xa12f684bda12f685ULL, + 0xb60b60b60b60b60cULL, + 0x2d82d82d82d82d83ULL, + 0x6b74f0329161f9aeULL, + 0x781948b0fcd6e9e1ULL, + 0xad45be6961639000ULL, /* 64 */ + 0xefa7a5a0e7176a00ULL, + 0x08c6139fc4346000ULL, + 0xfbe1883aee787980ULL, + 0xefa7a5a0e7176a00ULL, + 0x37ae2b38fded7040ULL, + 0x6acb3d68be6cdc00ULL, + 0xedbf72842143b470ULL, + 0x08c6139fc4346000ULL, /* 72 */ + 0x6acb3d68be6cdc00ULL, + 0x8624e5e1e5044000ULL, + 0x76a5ab8089e38100ULL, + 0xfbe1883aee787980ULL, + 0xedbf72842143b470ULL, + 0x76a5ab8089e38100ULL, + 0x4bb436d5b1e9cfc4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMUL(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMUL(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmulu.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmulu.c new file mode 100644 index 0000000000..b20a1bafc1 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_dmulu.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DMULU + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DMULU"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000001ULL, /* 0 */ + 0x0000000000000000ULL, + 0x5555555555555556ULL, + 0xaaaaaaaaaaaaaaabULL, + 0x3333333333333334ULL, + 0xcccccccccccccccdULL, + 0x1c71c71c71c71c72ULL, + 0xe38e38e38e38e38fULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x5555555555555556ULL, /* 16 */ + 0x0000000000000000ULL, + 0x38e38e38e38e38e4ULL, + 0x1c71c71c71c71c72ULL, + 0x7777777777777778ULL, + 0xdddddddddddddddeULL, + 0x12f684bda12f684cULL, + 0x425ed097b425ed0aULL, + 0xaaaaaaaaaaaaaaabULL, /* 24 */ + 0x0000000000000000ULL, + 0x1c71c71c71c71c72ULL, + 0x8e38e38e38e38e39ULL, + 0xbbbbbbbbbbbbbbbcULL, + 0xeeeeeeeeeeeeeeefULL, + 0x097b425ed097b426ULL, + 0xa12f684bda12f685ULL, + 0x3333333333333334ULL, /* 32 */ + 0x0000000000000000ULL, + 0x7777777777777778ULL, + 0xbbbbbbbbbbbbbbbcULL, + 0xf5c28f5c28f5c290ULL, + 0x3d70a3d70a3d70a4ULL, + 0x7d27d27d27d27d28ULL, + 0xb60b60b60b60b60cULL, + 0xcccccccccccccccdULL, /* 40 */ + 0x0000000000000000ULL, + 0xdddddddddddddddeULL, + 0xeeeeeeeeeeeeeeefULL, + 0x3d70a3d70a3d70a4ULL, + 0x8f5c28f5c28f5c29ULL, + 0x9f49f49f49f49f4aULL, + 0x2d82d82d82d82d83ULL, + 0x1c71c71c71c71c72ULL, /* 48 */ + 0x0000000000000000ULL, + 0x12f684bda12f684cULL, + 0x097b425ed097b426ULL, + 0x7d27d27d27d27d28ULL, + 0x9f49f49f49f49f4aULL, + 0xb0fcd6e9e06522c4ULL, + 0x6b74f0329161f9aeULL, + 0xe38e38e38e38e38fULL, /* 56 */ + 0x0000000000000000ULL, + 0x425ed097b425ed0aULL, + 0xa12f684bda12f685ULL, + 0xb60b60b60b60b60cULL, + 0x2d82d82d82d82d83ULL, + 0x6b74f0329161f9aeULL, + 0x781948b0fcd6e9e1ULL, + 0xad45be6961639000ULL, /* 64 */ + 0xefa7a5a0e7176a00ULL, + 0x08c6139fc4346000ULL, + 0xfbe1883aee787980ULL, + 0xefa7a5a0e7176a00ULL, + 0x37ae2b38fded7040ULL, + 0x6acb3d68be6cdc00ULL, + 0xedbf72842143b470ULL, + 0x08c6139fc4346000ULL, /* 72 */ + 0x6acb3d68be6cdc00ULL, + 0x8624e5e1e5044000ULL, + 0x76a5ab8089e38100ULL, + 0xfbe1883aee787980ULL, + 0xedbf72842143b470ULL, + 0x76a5ab8089e38100ULL, + 0x4bb436d5b1e9cfc4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMULU(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DMULU(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_muh.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_muh.c new file mode 100644 index 0000000000..91340749b2 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_muh.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction MUH + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "MUH"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0x0000000000000000ULL, + 0x000000001c71c71cULL, + 0xffffffffe38e38e3ULL, + 0x0000000011111111ULL, + 0xffffffffeeeeeeeeULL, + 0x00000000097b425fULL, + 0xfffffffff684bda1ULL, + 0xffffffffffffffffULL, /* 24 */ + 0x0000000000000000ULL, + 0xffffffffe38e38e3ULL, + 0x000000001c71c71cULL, + 0xffffffffeeeeeeeeULL, + 0x0000000011111110ULL, + 0xfffffffff684bda1ULL, + 0x00000000097b425eULL, + 0x0000000000000000ULL, /* 32 */ + 0x0000000000000000ULL, + 0x0000000011111111ULL, + 0xffffffffeeeeeeeeULL, + 0x000000000a3d70a4ULL, + 0xfffffffff5c28f5cULL, + 0x0000000005b05b05ULL, + 0xfffffffffa4fa4faULL, + 0xffffffffffffffffULL, /* 40 */ + 0x0000000000000000ULL, + 0xffffffffeeeeeeeeULL, + 0x0000000011111110ULL, + 0xfffffffff5c28f5cULL, + 0x000000000a3d70a3ULL, + 0xfffffffffa4fa4faULL, + 0x0000000005b05b05ULL, + 0x0000000000000000ULL, /* 48 */ + 0x0000000000000000ULL, + 0x00000000097b425fULL, + 0xfffffffff684bda1ULL, + 0x0000000005b05b05ULL, + 0xfffffffffa4fa4faULL, + 0x000000000329161fULL, + 0xfffffffffcd6e9e0ULL, + 0xffffffffffffffffULL, /* 56 */ + 0x0000000000000000ULL, + 0xfffffffff684bda1ULL, + 0x00000000097b425eULL, + 0xfffffffffa4fa4faULL, + 0x0000000005b05b05ULL, + 0xfffffffffcd6e9e0ULL, + 0x000000000329161fULL, + 0x0000000037dbf444ULL, /* 64 */ + 0x0000000001fd28a7ULL, + 0x0000000027129043ULL, + 0xffffffffcb89d38bULL, + 0x0000000001fd28a7ULL, + 0x0000000000122100ULL, + 0x00000000016425c3ULL, + 0xfffffffffe21cf6eULL, + 0x0000000027129043ULL, /* 72 */ + 0x00000000016425c3ULL, + 0x000000001b549d7fULL, + 0xffffffffdb4dd51cULL, + 0xffffffffcb89d38bULL, + 0xfffffffffe21cf6eULL, + 0xffffffffdb4dd51cULL, + 0x0000000031454bf2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MUH(b64_pattern_se + i, b64_pattern_se + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MUH(b64_random_se + i, b64_random_se + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_muhu.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_muhu.c new file mode 100644 index 0000000000..24850609c9 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_muhu.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction MUHU + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "MUHU"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xfffffffffffffffeULL, /* 0 */ + 0x0000000000000000ULL, + 0xffffffffaaaaaaa9ULL, + 0x0000000055555554ULL, + 0xffffffffcccccccbULL, + 0x0000000033333332ULL, + 0xffffffffe38e38e2ULL, + 0x000000001c71c71bULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0xffffffffaaaaaaa9ULL, /* 16 */ + 0x0000000000000000ULL, + 0x0000000071c71c70ULL, + 0x0000000038e38e38ULL, + 0xffffffff88888887ULL, + 0x0000000022222221ULL, + 0xffffffff97b425ecULL, + 0x0000000012f684bdULL, + 0x0000000055555554ULL, /* 24 */ + 0x0000000000000000ULL, + 0x0000000038e38e38ULL, + 0x000000001c71c71cULL, + 0x0000000044444443ULL, + 0x0000000011111110ULL, + 0x000000004bda12f6ULL, + 0x00000000097b425eULL, + 0xffffffffcccccccbULL, /* 32 */ + 0x0000000000000000ULL, + 0xffffffff88888887ULL, + 0x0000000044444443ULL, + 0xffffffffa3d70a3cULL, + 0x0000000028f5c28fULL, + 0xffffffffb60b60b4ULL, + 0x0000000016c16c16ULL, + 0x0000000033333332ULL, /* 40 */ + 0x0000000000000000ULL, + 0x0000000022222221ULL, + 0x0000000011111110ULL, + 0x0000000028f5c28fULL, + 0x000000000a3d70a3ULL, + 0x000000002d82d82dULL, + 0x0000000005b05b05ULL, + 0xffffffffe38e38e2ULL, /* 48 */ + 0x0000000000000000ULL, + 0xffffffff97b425ecULL, + 0x000000004bda12f6ULL, + 0xffffffffb60b60b4ULL, + 0x000000002d82d82dULL, + 0xffffffffca4587e5ULL, + 0x000000001948b0fcULL, + 0x000000001c71c71bULL, /* 56 */ + 0x0000000000000000ULL, + 0x0000000012f684bdULL, + 0x00000000097b425eULL, + 0x0000000016c16c16ULL, + 0x0000000005b05b05ULL, + 0x000000001948b0fcULL, + 0x000000000329161fULL, + 0x0000000048b1c1dcULL, /* 64 */ + 0xffffffff86260fd6ULL, + 0x000000005bd825b9ULL, + 0x000000003bd8e9d8ULL, + 0xffffffff86260fd6ULL, + 0xfffffffff78e21c6ULL, + 0xffffffffa97cd4d0ULL, + 0x000000006e70e5bbULL, + 0x000000005bd825b9ULL, /* 72 */ + 0xffffffffa97cd4d0ULL, + 0x000000007409fad3ULL, + 0x000000004b9ceb69ULL, + 0x000000003bd8e9d8ULL, + 0x000000006e70e5bbULL, + 0x000000004b9ceb69ULL, + 0x0000000031454bf2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MUHU(b64_pattern_se + i, b64_pattern_se + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MUHU(b64_random_se + i, b64_random_se + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_mul.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_mul.c new file mode 100644 index 0000000000..acf779c6d9 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_mul.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction MUL + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "MUL"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000001ULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000055555556ULL, + 0xffffffffaaaaaaabULL, + 0x0000000033333334ULL, + 0xffffffffcccccccdULL, + 0x0000000071c71c72ULL, + 0xffffffff8e38e38fULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000055555556ULL, /* 16 */ + 0x0000000000000000ULL, + 0xffffffffe38e38e4ULL, + 0x0000000071c71c72ULL, + 0x0000000077777778ULL, + 0xffffffffdddddddeULL, + 0xffffffffa12f684cULL, + 0xffffffffb425ed0aULL, + 0xffffffffaaaaaaabULL, /* 24 */ + 0x0000000000000000ULL, + 0x0000000071c71c72ULL, + 0x0000000038e38e39ULL, + 0xffffffffbbbbbbbcULL, + 0xffffffffeeeeeeefULL, + 0xffffffffd097b426ULL, + 0xffffffffda12f685ULL, + 0x0000000033333334ULL, /* 32 */ + 0x0000000000000000ULL, + 0x0000000077777778ULL, + 0xffffffffbbbbbbbcULL, + 0x0000000028f5c290ULL, + 0x000000000a3d70a4ULL, + 0x0000000027d27d28ULL, + 0x000000000b60b60cULL, + 0xffffffffcccccccdULL, /* 40 */ + 0x0000000000000000ULL, + 0xffffffffdddddddeULL, + 0xffffffffeeeeeeefULL, + 0x000000000a3d70a4ULL, + 0xffffffffc28f5c29ULL, + 0x0000000049f49f4aULL, + 0xffffffff82d82d83ULL, + 0x0000000071c71c72ULL, /* 48 */ + 0x0000000000000000ULL, + 0xffffffffa12f684cULL, + 0xffffffffd097b426ULL, + 0x0000000027d27d28ULL, + 0x0000000049f49f4aULL, + 0xffffffffe06522c4ULL, + 0xffffffff9161f9aeULL, + 0xffffffff8e38e38fULL, /* 56 */ + 0x0000000000000000ULL, + 0xffffffffb425ed0aULL, + 0xffffffffda12f685ULL, + 0x000000000b60b60cULL, + 0xffffffff82d82d83ULL, + 0xffffffff9161f9aeULL, + 0xfffffffffcd6e9e1ULL, + 0x0000000061639000ULL, /* 64 */ + 0xffffffffe7176a00ULL, + 0xffffffffc4346000ULL, + 0xffffffffee787980ULL, + 0xffffffffe7176a00ULL, + 0xfffffffffded7040ULL, + 0xffffffffbe6cdc00ULL, + 0x000000002143b470ULL, + 0xffffffffc4346000ULL, /* 72 */ + 0xffffffffbe6cdc00ULL, + 0xffffffffe5044000ULL, + 0xffffffff89e38100ULL, + 0xffffffffee787980ULL, + 0x000000002143b470ULL, + 0xffffffff89e38100ULL, + 0xffffffffb1e9cfc4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MUL(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MUL(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_mulu.c b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_mulu.c new file mode 100644 index 0000000000..65d5803eef --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/int-multiply/test_mips64r6_mulu.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction MULU + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "MULU"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000001ULL, /* 0 */ + 0x0000000000000000ULL, + 0x0000000055555556ULL, + 0xffffffffaaaaaaabULL, + 0x0000000033333334ULL, + 0xffffffffcccccccdULL, + 0x0000000071c71c72ULL, + 0xffffffff8e38e38fULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000055555556ULL, /* 16 */ + 0x0000000000000000ULL, + 0xffffffffe38e38e4ULL, + 0x0000000071c71c72ULL, + 0x0000000077777778ULL, + 0xffffffffdddddddeULL, + 0xffffffffa12f684cULL, + 0xffffffffb425ed0aULL, + 0xffffffffaaaaaaabULL, /* 24 */ + 0x0000000000000000ULL, + 0x0000000071c71c72ULL, + 0x0000000038e38e39ULL, + 0xffffffffbbbbbbbcULL, + 0xffffffffeeeeeeefULL, + 0xffffffffd097b426ULL, + 0xffffffffda12f685ULL, + 0x0000000033333334ULL, /* 32 */ + 0x0000000000000000ULL, + 0x0000000077777778ULL, + 0xffffffffbbbbbbbcULL, + 0x0000000028f5c290ULL, + 0x000000000a3d70a4ULL, + 0x0000000027d27d28ULL, + 0x000000000b60b60cULL, + 0xffffffffcccccccdULL, /* 40 */ + 0x0000000000000000ULL, + 0xffffffffdddddddeULL, + 0xffffffffeeeeeeefULL, + 0x000000000a3d70a4ULL, + 0xffffffffc28f5c29ULL, + 0x0000000049f49f4aULL, + 0xffffffff82d82d83ULL, + 0x0000000071c71c72ULL, /* 48 */ + 0x0000000000000000ULL, + 0xffffffffa12f684cULL, + 0xffffffffd097b426ULL, + 0x0000000027d27d28ULL, + 0x0000000049f49f4aULL, + 0xffffffffe06522c4ULL, + 0xffffffff9161f9aeULL, + 0xffffffff8e38e38fULL, /* 56 */ + 0x0000000000000000ULL, + 0xffffffffb425ed0aULL, + 0xffffffffda12f685ULL, + 0x000000000b60b60cULL, + 0xffffffff82d82d83ULL, + 0xffffffff9161f9aeULL, + 0xfffffffffcd6e9e1ULL, + 0x0000000061639000ULL, /* 64 */ + 0xffffffffe7176a00ULL, + 0xffffffffc4346000ULL, + 0xffffffffee787980ULL, + 0xffffffffe7176a00ULL, + 0xfffffffffded7040ULL, + 0xffffffffbe6cdc00ULL, + 0x000000002143b470ULL, + 0xffffffffc4346000ULL, /* 72 */ + 0xffffffffbe6cdc00ULL, + 0xffffffffe5044000ULL, + 0xffffffff89e38100ULL, + 0xffffffffee787980ULL, + 0x000000002143b470ULL, + 0xffffffff89e38100ULL, + 0xffffffffb1e9cfc4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MULU(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_MULU(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_and.c b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_and.c new file mode 100644 index 0000000000..2039dd9899 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_and.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction AND + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "AND"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x8000000000000000ULL, /* 0 */ + 0xffffffffffffffffULL, + 0xfffffc0000000000ULL, + 0xffffffffffe00000ULL, + 0xfffffffffffff000ULL, + 0xfff8000000000000ULL, + 0xffffffffffffc000ULL, + 0xfffe000000000000ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0xaaaaa80000000000ULL, + 0x5555555555400000ULL, + 0xaaaaaaaaaaaaa000ULL, + 0x5550000000000000ULL, + 0xaaaaaaaaaaaa8000ULL, + 0x5554000000000000ULL, + 0x8000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x5555540000000000ULL, + 0xaaaaaaaaaaa00000ULL, + 0x5555555555555000ULL, + 0xaaa8000000000000ULL, + 0x5555555555554000ULL, + 0xaaaa000000000000ULL, + 0x0000000000000000ULL, /* 32 */ + 0xccccccccccccccccULL, + 0x3333300000000000ULL, + 0x9999999999800000ULL, + 0xccccccccccccc000ULL, + 0x6660000000000000ULL, + 0x3333333333330000ULL, + 0x9998000000000000ULL, + 0x8000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0xcccccc0000000000ULL, + 0x6666666666600000ULL, + 0x3333333333333000ULL, + 0x9998000000000000ULL, + 0xccccccccccccc000ULL, + 0x6666000000000000ULL, + 0x0000000000000000ULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0xe38e380000000000ULL, + 0x1c71c71c71c00000ULL, + 0xe38e38e38e38e000ULL, + 0x1c70000000000000ULL, + 0x8e38e38e38e38000ULL, + 0xc71c000000000000ULL, + 0x8000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x1c71c40000000000ULL, + 0xe38e38e38e200000ULL, + 0x1c71c71c71c71000ULL, + 0xe388000000000000ULL, + 0x71c71c71c71c4000ULL, + 0x38e2000000000000ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0x6ae6cc2862554000ULL, + 0x886ae6cc28625540ULL, + 0xb9b30a1895500000ULL, + 0xfbbe00634d93c708ULL, + 0xbe00634d93c70800ULL, + 0xfbbe00634d93c708ULL, + 0x8018d364f1c20000ULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0x5aaeaab9cf8b8000ULL, + 0xac5aaeaab9cf8b80ULL, + 0xabaaae73e2e00000ULL, + 0x704f164d5e31e24eULL, + 0x4f164d5e31e24e00ULL, + 0x704f164d5e31e24eULL, + 0xc593578c78938000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_AND(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_AND(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_nor.c b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_nor.c new file mode 100644 index 0000000000..f22393b6c7 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_nor.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction NOR + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "NOR"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x8000000000000000ULL, /* 0 */ + 0xffffffffffffffffULL, + 0xfffffc0000000000ULL, + 0xffffffffffe00000ULL, + 0xfffffffffffff000ULL, + 0xfff8000000000000ULL, + 0xffffffffffffc000ULL, + 0xfffe000000000000ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0xaaaaa80000000000ULL, + 0x5555555555400000ULL, + 0xaaaaaaaaaaaaa000ULL, + 0x5550000000000000ULL, + 0xaaaaaaaaaaaa8000ULL, + 0x5554000000000000ULL, + 0x8000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x5555540000000000ULL, + 0xaaaaaaaaaaa00000ULL, + 0x5555555555555000ULL, + 0xaaa8000000000000ULL, + 0x5555555555554000ULL, + 0xaaaa000000000000ULL, + 0x0000000000000000ULL, /* 32 */ + 0xccccccccccccccccULL, + 0x3333300000000000ULL, + 0x9999999999800000ULL, + 0xccccccccccccc000ULL, + 0x6660000000000000ULL, + 0x3333333333330000ULL, + 0x9998000000000000ULL, + 0x8000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0xcccccc0000000000ULL, + 0x6666666666600000ULL, + 0x3333333333333000ULL, + 0x9998000000000000ULL, + 0xccccccccccccc000ULL, + 0x6666000000000000ULL, + 0x0000000000000000ULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0xe38e380000000000ULL, + 0x1c71c71c71c00000ULL, + 0xe38e38e38e38e000ULL, + 0x1c70000000000000ULL, + 0x8e38e38e38e38000ULL, + 0xc71c000000000000ULL, + 0x8000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x1c71c40000000000ULL, + 0xe38e38e38e200000ULL, + 0x1c71c71c71c71000ULL, + 0xe388000000000000ULL, + 0x71c71c71c71c4000ULL, + 0x38e2000000000000ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0x6ae6cc2862554000ULL, + 0x886ae6cc28625540ULL, + 0xb9b30a1895500000ULL, + 0xfbbe00634d93c708ULL, + 0xbe00634d93c70800ULL, + 0xfbbe00634d93c708ULL, + 0x8018d364f1c20000ULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0x5aaeaab9cf8b8000ULL, + 0xac5aaeaab9cf8b80ULL, + 0xabaaae73e2e00000ULL, + 0x704f164d5e31e24eULL, + 0x4f164d5e31e24e00ULL, + 0x704f164d5e31e24eULL, + 0xc593578c78938000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_NOR(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_NOR(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_or.c b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_or.c new file mode 100644 index 0000000000..ed8bac1c9c --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_or.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction OR + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "OR"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x8000000000000000ULL, /* 0 */ + 0xffffffffffffffffULL, + 0xfffffc0000000000ULL, + 0xffffffffffe00000ULL, + 0xfffffffffffff000ULL, + 0xfff8000000000000ULL, + 0xffffffffffffc000ULL, + 0xfffe000000000000ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0xaaaaa80000000000ULL, + 0x5555555555400000ULL, + 0xaaaaaaaaaaaaa000ULL, + 0x5550000000000000ULL, + 0xaaaaaaaaaaaa8000ULL, + 0x5554000000000000ULL, + 0x8000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x5555540000000000ULL, + 0xaaaaaaaaaaa00000ULL, + 0x5555555555555000ULL, + 0xaaa8000000000000ULL, + 0x5555555555554000ULL, + 0xaaaa000000000000ULL, + 0x0000000000000000ULL, /* 32 */ + 0xccccccccccccccccULL, + 0x3333300000000000ULL, + 0x9999999999800000ULL, + 0xccccccccccccc000ULL, + 0x6660000000000000ULL, + 0x3333333333330000ULL, + 0x9998000000000000ULL, + 0x8000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0xcccccc0000000000ULL, + 0x6666666666600000ULL, + 0x3333333333333000ULL, + 0x9998000000000000ULL, + 0xccccccccccccc000ULL, + 0x6666000000000000ULL, + 0x0000000000000000ULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0xe38e380000000000ULL, + 0x1c71c71c71c00000ULL, + 0xe38e38e38e38e000ULL, + 0x1c70000000000000ULL, + 0x8e38e38e38e38000ULL, + 0xc71c000000000000ULL, + 0x8000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x1c71c40000000000ULL, + 0xe38e38e38e200000ULL, + 0x1c71c71c71c71000ULL, + 0xe388000000000000ULL, + 0x71c71c71c71c4000ULL, + 0x38e2000000000000ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0x6ae6cc2862554000ULL, + 0x886ae6cc28625540ULL, + 0xb9b30a1895500000ULL, + 0xfbbe00634d93c708ULL, + 0xbe00634d93c70800ULL, + 0xfbbe00634d93c708ULL, + 0x8018d364f1c20000ULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0x5aaeaab9cf8b8000ULL, + 0xac5aaeaab9cf8b80ULL, + 0xabaaae73e2e00000ULL, + 0x704f164d5e31e24eULL, + 0x4f164d5e31e24e00ULL, + 0x704f164d5e31e24eULL, + 0xc593578c78938000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_OR(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_OR(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_xor.c b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_xor.c new file mode 100644 index 0000000000..a8ba2b4ed1 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/logic/test_mips64r6_xor.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction XOR + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "XOR"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x8000000000000000ULL, /* 0 */ + 0xffffffffffffffffULL, + 0xfffffc0000000000ULL, + 0xffffffffffe00000ULL, + 0xfffffffffffff000ULL, + 0xfff8000000000000ULL, + 0xffffffffffffc000ULL, + 0xfffe000000000000ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0xaaaaa80000000000ULL, + 0x5555555555400000ULL, + 0xaaaaaaaaaaaaa000ULL, + 0x5550000000000000ULL, + 0xaaaaaaaaaaaa8000ULL, + 0x5554000000000000ULL, + 0x8000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x5555540000000000ULL, + 0xaaaaaaaaaaa00000ULL, + 0x5555555555555000ULL, + 0xaaa8000000000000ULL, + 0x5555555555554000ULL, + 0xaaaa000000000000ULL, + 0x0000000000000000ULL, /* 32 */ + 0xccccccccccccccccULL, + 0x3333300000000000ULL, + 0x9999999999800000ULL, + 0xccccccccccccc000ULL, + 0x6660000000000000ULL, + 0x3333333333330000ULL, + 0x9998000000000000ULL, + 0x8000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0xcccccc0000000000ULL, + 0x6666666666600000ULL, + 0x3333333333333000ULL, + 0x9998000000000000ULL, + 0xccccccccccccc000ULL, + 0x6666000000000000ULL, + 0x0000000000000000ULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0xe38e380000000000ULL, + 0x1c71c71c71c00000ULL, + 0xe38e38e38e38e000ULL, + 0x1c70000000000000ULL, + 0x8e38e38e38e38000ULL, + 0xc71c000000000000ULL, + 0x8000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x1c71c40000000000ULL, + 0xe38e38e38e200000ULL, + 0x1c71c71c71c71000ULL, + 0xe388000000000000ULL, + 0x71c71c71c71c4000ULL, + 0x38e2000000000000ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0x6ae6cc2862554000ULL, + 0x886ae6cc28625540ULL, + 0xb9b30a1895500000ULL, + 0xfbbe00634d93c708ULL, + 0xbe00634d93c70800ULL, + 0xfbbe00634d93c708ULL, + 0x8018d364f1c20000ULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0x5aaeaab9cf8b8000ULL, + 0xac5aaeaab9cf8b80ULL, + 0xabaaae73e2e00000ULL, + 0x704f164d5e31e24eULL, + 0x4f164d5e31e24e00ULL, + 0x704f164d5e31e24eULL, + 0xc593578c78938000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_XOR(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_XOR(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsllv.c b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsllv.c new file mode 100644 index 0000000000..b3eb86edb7 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsllv.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DSLLV + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DSLLV"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x8000000000000000ULL, /* 0 */ + 0xffffffffffffffffULL, + 0xfffffc0000000000ULL, + 0xffffffffffe00000ULL, + 0xfffffffffffff000ULL, + 0xfff8000000000000ULL, + 0xffffffffffffc000ULL, + 0xfffe000000000000ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0xaaaaa80000000000ULL, + 0x5555555555400000ULL, + 0xaaaaaaaaaaaaa000ULL, + 0x5550000000000000ULL, + 0xaaaaaaaaaaaa8000ULL, + 0x5554000000000000ULL, + 0x8000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x5555540000000000ULL, + 0xaaaaaaaaaaa00000ULL, + 0x5555555555555000ULL, + 0xaaa8000000000000ULL, + 0x5555555555554000ULL, + 0xaaaa000000000000ULL, + 0x0000000000000000ULL, /* 32 */ + 0xccccccccccccccccULL, + 0x3333300000000000ULL, + 0x9999999999800000ULL, + 0xccccccccccccc000ULL, + 0x6660000000000000ULL, + 0x3333333333330000ULL, + 0x9998000000000000ULL, + 0x8000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0xcccccc0000000000ULL, + 0x6666666666600000ULL, + 0x3333333333333000ULL, + 0x9998000000000000ULL, + 0xccccccccccccc000ULL, + 0x6666000000000000ULL, + 0x0000000000000000ULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0xe38e380000000000ULL, + 0x1c71c71c71c00000ULL, + 0xe38e38e38e38e000ULL, + 0x1c70000000000000ULL, + 0x8e38e38e38e38000ULL, + 0xc71c000000000000ULL, + 0x8000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x1c71c40000000000ULL, + 0xe38e38e38e200000ULL, + 0x1c71c71c71c71000ULL, + 0xe388000000000000ULL, + 0x71c71c71c71c4000ULL, + 0x38e2000000000000ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0x6ae6cc2862554000ULL, + 0x886ae6cc28625540ULL, + 0xb9b30a1895500000ULL, + 0xfbbe00634d93c708ULL, + 0xbe00634d93c70800ULL, + 0xfbbe00634d93c708ULL, + 0x8018d364f1c20000ULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0x5aaeaab9cf8b8000ULL, + 0xac5aaeaab9cf8b80ULL, + 0xabaaae73e2e00000ULL, + 0x704f164d5e31e24eULL, + 0x4f164d5e31e24e00ULL, + 0x704f164d5e31e24eULL, + 0xc593578c78938000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DSLLV(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DSLLV(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsrav.c b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsrav.c new file mode 100644 index 0000000000..025e8f8163 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsrav.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DSRAV + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DSRAV"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffffffffffULL, /* 0 */ + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0xffffffffffeaaaaaULL, + 0xfffffd5555555555ULL, + 0xfffaaaaaaaaaaaaaULL, + 0xfffffffffffff555ULL, + 0xfffeaaaaaaaaaaaaULL, + 0xffffffffffffd555ULL, + 0x0000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x0000000000155555ULL, + 0x000002aaaaaaaaaaULL, + 0x0005555555555555ULL, + 0x0000000000000aaaULL, + 0x0001555555555555ULL, + 0x0000000000002aaaULL, + 0xffffffffffffffffULL, /* 32 */ + 0xccccccccccccccccULL, + 0xfffffffffff33333ULL, + 0xfffffe6666666666ULL, + 0xfffcccccccccccccULL, + 0xfffffffffffff999ULL, + 0xffff333333333333ULL, + 0xffffffffffffe666ULL, + 0x0000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0x00000000000cccccULL, + 0x0000019999999999ULL, + 0x0003333333333333ULL, + 0x0000000000000666ULL, + 0x0000ccccccccccccULL, + 0x0000000000001999ULL, + 0xffffffffffffffffULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0xfffffffffff8e38eULL, + 0xffffff1c71c71c71ULL, + 0xfffe38e38e38e38eULL, + 0xfffffffffffffc71ULL, + 0xffff8e38e38e38e3ULL, + 0xfffffffffffff1c7ULL, + 0x0000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x0000000000071c71ULL, + 0x000000e38e38e38eULL, + 0x0001c71c71c71c71ULL, + 0x000000000000038eULL, + 0x000071c71c71c71cULL, + 0x0000000000000e38ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0xff886ae6cc286255ULL, + 0x886ae6cc28625540ULL, + 0xfffe21ab9b30a189ULL, + 0xfbbe00634d93c708ULL, + 0xfffbbe00634d93c7ULL, + 0xfbbe00634d93c708ULL, + 0xffffeef8018d364fULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0xffac5aaeaab9cf8bULL, + 0xac5aaeaab9cf8b80ULL, + 0xfffeb16abaaae73eULL, + 0x704f164d5e31e24eULL, + 0x00704f164d5e31e2ULL, + 0x704f164d5e31e24eULL, + 0x0001c13c593578c7ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DSRAV(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DSRAV(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsrlv.c b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsrlv.c new file mode 100644 index 0000000000..680f8a1845 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_dsrlv.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction DSRLV + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "DSRLV"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000001ULL, /* 0 */ + 0xffffffffffffffffULL, + 0x00000000003fffffULL, + 0x000007ffffffffffULL, + 0x000fffffffffffffULL, + 0x0000000000001fffULL, + 0x0003ffffffffffffULL, + 0x0000000000007fffULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, /* 16 */ + 0xaaaaaaaaaaaaaaaaULL, + 0x00000000002aaaaaULL, + 0x0000055555555555ULL, + 0x000aaaaaaaaaaaaaULL, + 0x0000000000001555ULL, + 0x0002aaaaaaaaaaaaULL, + 0x0000000000005555ULL, + 0x0000000000000000ULL, /* 24 */ + 0x5555555555555555ULL, + 0x0000000000155555ULL, + 0x000002aaaaaaaaaaULL, + 0x0005555555555555ULL, + 0x0000000000000aaaULL, + 0x0001555555555555ULL, + 0x0000000000002aaaULL, + 0x0000000000000001ULL, /* 32 */ + 0xccccccccccccccccULL, + 0x0000000000333333ULL, + 0x0000066666666666ULL, + 0x000cccccccccccccULL, + 0x0000000000001999ULL, + 0x0003333333333333ULL, + 0x0000000000006666ULL, + 0x0000000000000000ULL, /* 40 */ + 0x3333333333333333ULL, + 0x00000000000cccccULL, + 0x0000019999999999ULL, + 0x0003333333333333ULL, + 0x0000000000000666ULL, + 0x0000ccccccccccccULL, + 0x0000000000001999ULL, + 0x0000000000000001ULL, /* 48 */ + 0xe38e38e38e38e38eULL, + 0x000000000038e38eULL, + 0x0000071c71c71c71ULL, + 0x000e38e38e38e38eULL, + 0x0000000000001c71ULL, + 0x00038e38e38e38e3ULL, + 0x00000000000071c7ULL, + 0x0000000000000000ULL, /* 56 */ + 0x1c71c71c71c71c71ULL, + 0x0000000000071c71ULL, + 0x000000e38e38e38eULL, + 0x0001c71c71c71c71ULL, + 0x000000000000038eULL, + 0x000071c71c71c71cULL, + 0x0000000000000e38ULL, + 0x886ae6cc28625540ULL, /* 64 */ + 0x00886ae6cc286255ULL, + 0x886ae6cc28625540ULL, + 0x000221ab9b30a189ULL, + 0xfbbe00634d93c708ULL, + 0x00fbbe00634d93c7ULL, + 0xfbbe00634d93c708ULL, + 0x0003eef8018d364fULL, + 0xac5aaeaab9cf8b80ULL, /* 72 */ + 0x00ac5aaeaab9cf8bULL, + 0xac5aaeaab9cf8b80ULL, + 0x0002b16abaaae73eULL, + 0x704f164d5e31e24eULL, + 0x00704f164d5e31e2ULL, + 0x704f164d5e31e24eULL, + 0x0001c13c593578c7ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DSRLV(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_DSRLV(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_sllv.c b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_sllv.c new file mode 100644 index 0000000000..288d087773 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_sllv.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction SLLV + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "SLLV"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffff80000000ULL, /* 0 */ + 0xffffffffffffffffULL, + 0xfffffffffffffc00ULL, + 0xffffffffffe00000ULL, + 0xfffffffffffff000ULL, + 0xfffffffffff80000ULL, + 0xffffffffffffc000ULL, + 0xfffffffffffe0000ULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 16 */ + 0xffffffffaaaaaaaaULL, + 0xffffffffaaaaa800ULL, + 0x0000000055400000ULL, + 0xffffffffaaaaa000ULL, + 0x0000000055500000ULL, + 0xffffffffaaaa8000ULL, + 0x0000000055540000ULL, + 0xffffffff80000000ULL, /* 24 */ + 0x0000000055555555ULL, + 0x0000000055555400ULL, + 0xffffffffaaa00000ULL, + 0x0000000055555000ULL, + 0xffffffffaaa80000ULL, + 0x0000000055554000ULL, + 0xffffffffaaaa0000ULL, + 0x0000000000000000ULL, /* 32 */ + 0xffffffffccccccccULL, + 0x0000000033333000ULL, + 0xffffffff99800000ULL, + 0xffffffffccccc000ULL, + 0x0000000066600000ULL, + 0x0000000033330000ULL, + 0xffffffff99980000ULL, + 0xffffffff80000000ULL, /* 40 */ + 0x0000000033333333ULL, + 0xffffffffcccccc00ULL, + 0x0000000066600000ULL, + 0x0000000033333000ULL, + 0xffffffff99980000ULL, + 0xffffffffccccc000ULL, + 0x0000000066660000ULL, + 0x0000000000000000ULL, /* 48 */ + 0xffffffff8e38e38eULL, + 0xffffffffe38e3800ULL, + 0x0000000071c00000ULL, + 0xffffffff8e38e000ULL, + 0x000000001c700000ULL, + 0x0000000038e38000ULL, + 0xffffffffc71c0000ULL, + 0xffffffff80000000ULL, /* 56 */ + 0x0000000071c71c71ULL, + 0x000000001c71c400ULL, + 0xffffffff8e200000ULL, + 0x0000000071c71000ULL, + 0xffffffffe3880000ULL, + 0xffffffffc71c4000ULL, + 0x0000000038e20000ULL, + 0x0000000028625540ULL, /* 64 */ + 0x0000000062554000ULL, + 0x0000000028625540ULL, + 0xffffffff95500000ULL, + 0x000000004d93c708ULL, + 0xffffffff93c70800ULL, + 0x000000004d93c708ULL, + 0xfffffffff1c20000ULL, + 0xffffffffb9cf8b80ULL, /* 72 */ + 0xffffffffcf8b8000ULL, + 0xffffffffb9cf8b80ULL, + 0xffffffffe2e00000ULL, + 0x000000005e31e24eULL, + 0x0000000031e24e00ULL, + 0x000000005e31e24eULL, + 0x0000000078938000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_SLLV(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_SLLV(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_srav.c b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_srav.c new file mode 100644 index 0000000000..f547d9e408 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_srav.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction SRAV + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "SRAV"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffffffffffULL, /* 0 */ + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0xffffffffffffffffULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0xffffffffffffffffULL, /* 16 */ + 0xffffffffaaaaaaaaULL, + 0xffffffffffeaaaaaULL, + 0xfffffffffffffd55ULL, + 0xfffffffffffaaaaaULL, + 0xfffffffffffff555ULL, + 0xfffffffffffeaaaaULL, + 0xffffffffffffd555ULL, + 0x0000000000000000ULL, /* 24 */ + 0x0000000055555555ULL, + 0x0000000000155555ULL, + 0x00000000000002aaULL, + 0x0000000000055555ULL, + 0x0000000000000aaaULL, + 0x0000000000015555ULL, + 0x0000000000002aaaULL, + 0xffffffffffffffffULL, /* 32 */ + 0xffffffffccccccccULL, + 0xfffffffffff33333ULL, + 0xfffffffffffffe66ULL, + 0xfffffffffffcccccULL, + 0xfffffffffffff999ULL, + 0xffffffffffff3333ULL, + 0xffffffffffffe666ULL, + 0x0000000000000000ULL, /* 40 */ + 0x0000000033333333ULL, + 0x00000000000cccccULL, + 0x0000000000000199ULL, + 0x0000000000033333ULL, + 0x0000000000000666ULL, + 0x000000000000ccccULL, + 0x0000000000001999ULL, + 0xffffffffffffffffULL, /* 48 */ + 0xffffffff8e38e38eULL, + 0xffffffffffe38e38ULL, + 0xfffffffffffffc71ULL, + 0xfffffffffff8e38eULL, + 0xfffffffffffff1c7ULL, + 0xfffffffffffe38e3ULL, + 0xffffffffffffc71cULL, + 0x0000000000000000ULL, /* 56 */ + 0x0000000071c71c71ULL, + 0x00000000001c71c7ULL, + 0x000000000000038eULL, + 0x0000000000071c71ULL, + 0x0000000000000e38ULL, + 0x000000000001c71cULL, + 0x00000000000038e3ULL, + 0x0000000028625540ULL, /* 64 */ + 0x0000000000286255ULL, + 0x0000000028625540ULL, + 0x000000000000a189ULL, + 0x000000004d93c708ULL, + 0x00000000004d93c7ULL, + 0x000000004d93c708ULL, + 0x000000000001364fULL, + 0xffffffffb9cf8b80ULL, /* 72 */ + 0xffffffffffb9cf8bULL, + 0xffffffffb9cf8b80ULL, + 0xfffffffffffee73eULL, + 0x000000005e31e24eULL, + 0x00000000005e31e2ULL, + 0x000000005e31e24eULL, + 0x00000000000178c7ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_SRAV(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_SRAV(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_srlv.c b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_srlv.c new file mode 100644 index 0000000000..8b8075ca40 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/shift/test_mips64r6_srlv.c @@ -0,0 +1,151 @@ +/* + * Test program for MIPS64R6 instruction SRLV + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * + * 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 +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + + +int32_t main(void) +{ + char *instruction_name = "SRLV"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000001ULL, /* 0 */ + 0xffffffffffffffffULL, + 0x00000000003fffffULL, + 0x00000000000007ffULL, + 0x00000000000fffffULL, + 0x0000000000001fffULL, + 0x000000000003ffffULL, + 0x0000000000007fffULL, + 0x0000000000000000ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, + 0x0000000000000001ULL, /* 16 */ + 0xffffffffaaaaaaaaULL, + 0x00000000002aaaaaULL, + 0x0000000000000555ULL, + 0x00000000000aaaaaULL, + 0x0000000000001555ULL, + 0x000000000002aaaaULL, + 0x0000000000005555ULL, + 0x0000000000000000ULL, /* 24 */ + 0x0000000055555555ULL, + 0x0000000000155555ULL, + 0x00000000000002aaULL, + 0x0000000000055555ULL, + 0x0000000000000aaaULL, + 0x0000000000015555ULL, + 0x0000000000002aaaULL, + 0x0000000000000001ULL, /* 32 */ + 0xffffffffccccccccULL, + 0x0000000000333333ULL, + 0x0000000000000666ULL, + 0x00000000000cccccULL, + 0x0000000000001999ULL, + 0x0000000000033333ULL, + 0x0000000000006666ULL, + 0x0000000000000000ULL, /* 40 */ + 0x0000000033333333ULL, + 0x00000000000cccccULL, + 0x0000000000000199ULL, + 0x0000000000033333ULL, + 0x0000000000000666ULL, + 0x000000000000ccccULL, + 0x0000000000001999ULL, + 0x0000000000000001ULL, /* 48 */ + 0xffffffff8e38e38eULL, + 0x0000000000238e38ULL, + 0x0000000000000471ULL, + 0x000000000008e38eULL, + 0x00000000000011c7ULL, + 0x00000000000238e3ULL, + 0x000000000000471cULL, + 0x0000000000000000ULL, /* 56 */ + 0x0000000071c71c71ULL, + 0x00000000001c71c7ULL, + 0x000000000000038eULL, + 0x0000000000071c71ULL, + 0x0000000000000e38ULL, + 0x000000000001c71cULL, + 0x00000000000038e3ULL, + 0x0000000028625540ULL, /* 64 */ + 0x0000000000286255ULL, + 0x0000000028625540ULL, + 0x000000000000a189ULL, + 0x000000004d93c708ULL, + 0x00000000004d93c7ULL, + 0x000000004d93c708ULL, + 0x000000000001364fULL, + 0xffffffffb9cf8b80ULL, /* 72 */ + 0x0000000000b9cf8bULL, + 0xffffffffb9cf8b80ULL, + 0x000000000002e73eULL, + 0x000000005e31e24eULL, + 0x00000000005e31e2ULL, + 0x000000005e31e24eULL, + 0x00000000000178c7ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_SRLV(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_SRLV(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(instruction_name, TEST_COUNT_TOTAL, elapsed_time, + b64_result, b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/r5900/Makefile b/tests/tcg/mips/user/isa/r5900/Makefile new file mode 100644 index 0000000000..bff360df6c --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/Makefile @@ -0,0 +1,32 @@ +-include ../../../../config-host.mak + +CROSS=mipsr5900el-unknown-linux-gnu- + +SIM=qemu-mipsel +SIM_FLAGS=-cpu R5900 + +CC = $(CROSS)gcc +CFLAGS = -Wall -mabi=32 -march=r5900 -static + +TESTCASES = test_r5900_div1.tst +TESTCASES += test_r5900_divu1.tst +TESTCASES += test_r5900_madd.tst +TESTCASES += test_r5900_maddu.tst +TESTCASES += test_r5900_mflohi1.tst +TESTCASES += test_r5900_mtlohi1.tst +TESTCASES += test_r5900_mult.tst +TESTCASES += test_r5900_multu.tst + +all: $(TESTCASES) + +%.tst: %.c + $(CC) $(CFLAGS) $< -o $@ + +check: $(TESTCASES) + @for case in $(TESTCASES); do \ + echo $(SIM) $(SIM_FLAGS) ./$$case;\ + $(SIM) $(SIM_FLAGS) ./$$case; \ + done + +clean: + $(RM) -rf $(TESTCASES) diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_div1.c b/tests/tcg/mips/user/isa/r5900/test_r5900_div1.c new file mode 100644 index 0000000000..83dafa018b --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_div1.c @@ -0,0 +1,73 @@ +/* + * Test R5900-specific DIV1. + */ + +#include +#include +#include + +struct quotient_remainder { int32_t quotient, remainder; }; + +static struct quotient_remainder div1(int32_t rs, int32_t rt) +{ + int32_t lo, hi; + + __asm__ __volatile__ ( + " div1 $0, %2, %3\n" + " mflo1 %0\n" + " mfhi1 %1\n" + : "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt)); + + assert(rs / rt == lo); + assert(rs % rt == hi); + + return (struct quotient_remainder) { .quotient = lo, .remainder = hi }; +} + +static void verify_div1(int32_t rs, int32_t rt, + int32_t expected_quotient, + int32_t expected_remainder) +{ + struct quotient_remainder qr = div1(rs, rt); + + assert(qr.quotient == expected_quotient); + assert(qr.remainder == expected_remainder); +} + +static void verify_div1_negations(int32_t rs, int32_t rt, + int32_t expected_quotient, + int32_t expected_remainder) +{ + verify_div1(rs, rt, expected_quotient, expected_remainder); + verify_div1(rs, -rt, -expected_quotient, expected_remainder); + verify_div1(-rs, rt, -expected_quotient, -expected_remainder); + verify_div1(-rs, -rt, expected_quotient, -expected_remainder); +} + +int main() +{ + verify_div1_negations(0, 1, 0, 0); + verify_div1_negations(1, 1, 1, 0); + verify_div1_negations(1, 2, 0, 1); + verify_div1_negations(17, 19, 0, 17); + verify_div1_negations(19, 17, 1, 2); + verify_div1_negations(77773, 101, 770, 3); + + verify_div1(-0x80000000, 1, -0x80000000, 0); + + /* + * Supplementary explanation from the Toshiba TX System RISC TX79 Core + * Architecture manual, A-38 and B-7, https://wiki.qemu.org/File:C790.pdf + * + * Normally, when 0x80000000 (-2147483648) the signed minimum value is + * divided by 0xFFFFFFFF (-1), the operation will result in an overflow. + * However, in this instruction an overflow exception doesn't occur and + * the result will be as follows: + * + * Quotient is 0x80000000 (-2147483648), and remainder is 0x00000000 (0). + */ + verify_div1(-0x80000000, -1, -0x80000000, 0); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_divu1.c b/tests/tcg/mips/user/isa/r5900/test_r5900_divu1.c new file mode 100644 index 0000000000..72aeed31de --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_divu1.c @@ -0,0 +1,48 @@ +/* + * Test R5900-specific DIVU1. + */ + +#include +#include +#include + +struct quotient_remainder { uint32_t quotient, remainder; }; + +static struct quotient_remainder divu1(uint32_t rs, uint32_t rt) +{ + uint32_t lo, hi; + + __asm__ __volatile__ ( + " divu1 $0, %2, %3\n" + " mflo1 %0\n" + " mfhi1 %1\n" + : "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt)); + + assert(rs / rt == lo); + assert(rs % rt == hi); + + return (struct quotient_remainder) { .quotient = lo, .remainder = hi }; +} + +static void verify_divu1(uint32_t rs, uint32_t rt, + uint32_t expected_quotient, + uint32_t expected_remainder) +{ + struct quotient_remainder qr = divu1(rs, rt); + + assert(qr.quotient == expected_quotient); + assert(qr.remainder == expected_remainder); +} + +int main() +{ + verify_divu1(0, 1, 0, 0); + verify_divu1(1, 1, 1, 0); + verify_divu1(1, 2, 0, 1); + verify_divu1(17, 19, 0, 17); + verify_divu1(19, 17, 1, 2); + verify_divu1(77773, 101, 770, 3); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_madd.c b/tests/tcg/mips/user/isa/r5900/test_r5900_madd.c new file mode 100644 index 0000000000..f6f215e1c3 --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_madd.c @@ -0,0 +1,78 @@ +/* + * Test R5900-specific three-operand MADD and MADD1. + */ + +#include +#include +#include + +int64_t madd(int64_t a, int32_t rs, int32_t rt) +{ + int32_t lo = a; + int32_t hi = a >> 32; + int32_t rd; + int64_t r; + + __asm__ __volatile__ ( + " mtlo %5\n" + " mthi %6\n" + " madd %0, %3, %4\n" + " mflo %1\n" + " mfhi %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt), "r" (lo), "r" (hi)); + r = ((int64_t)hi << 32) | (uint32_t)lo; + + assert(a + (int64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +int64_t madd1(int64_t a, int32_t rs, int32_t rt) +{ + int32_t lo = a; + int32_t hi = a >> 32; + int32_t rd; + int64_t r; + + __asm__ __volatile__ ( + " mtlo1 %5\n" + " mthi1 %6\n" + " madd1 %0, %3, %4\n" + " mflo1 %1\n" + " mfhi1 %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt), "r" (lo), "r" (hi)); + r = ((int64_t)hi << 32) | (uint32_t)lo; + + assert(a + (int64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +static int64_t madd_variants(int64_t a, int32_t rs, int32_t rt) +{ + int64_t rd = madd(a, rs, rt); + int64_t rd1 = madd1(a, rs, rt); + + assert(rd == rd1); + + return rd; +} + +static void verify_madd(int64_t a, int32_t rs, int32_t rt, int64_t expected) +{ + assert(madd_variants(a, rs, rt) == expected); + assert(madd_variants(a, -rs, rt) == a + a - expected); + assert(madd_variants(a, rs, -rt) == a + a - expected); + assert(madd_variants(a, -rs, -rt) == expected); +} + +int main() +{ + verify_madd(13, 17, 19, 336); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_maddu.c b/tests/tcg/mips/user/isa/r5900/test_r5900_maddu.c new file mode 100644 index 0000000000..30936fb2b4 --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_maddu.c @@ -0,0 +1,70 @@ +/* + * Test R5900-specific three-operand MADDU and MADDU1. + */ + +#include +#include +#include + +uint64_t maddu(uint64_t a, uint32_t rs, uint32_t rt) +{ + uint32_t lo = a; + uint32_t hi = a >> 32; + uint32_t rd; + uint64_t r; + + __asm__ __volatile__ ( + " mtlo %5\n" + " mthi %6\n" + " maddu %0, %3, %4\n" + " mflo %1\n" + " mfhi %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt), "r" (lo), "r" (hi)); + r = ((uint64_t)hi << 32) | (uint32_t)lo; + + assert(a + (uint64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +uint64_t maddu1(uint64_t a, uint32_t rs, uint32_t rt) +{ + uint32_t lo = a; + uint32_t hi = a >> 32; + uint32_t rd; + uint64_t r; + + __asm__ __volatile__ ( + " mtlo1 %5\n" + " mthi1 %6\n" + " maddu1 %0, %3, %4\n" + " mflo1 %1\n" + " mfhi1 %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt), "r" (lo), "r" (hi)); + r = ((uint64_t)hi << 32) | (uint32_t)lo; + + assert(a + (uint64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +static int64_t maddu_variants(int64_t a, int32_t rs, int32_t rt) +{ + int64_t rd = maddu(a, rs, rt); + int64_t rd1 = maddu1(a, rs, rt); + + assert(rd == rd1); + + return rd; +} + +int main() +{ + assert(maddu_variants(13, 17, 19) == 336); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_mflohi1.c b/tests/tcg/mips/user/isa/r5900/test_r5900_mflohi1.c new file mode 100644 index 0000000000..eed3683dc5 --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_mflohi1.c @@ -0,0 +1,35 @@ +/* + * Test R5900-specific MFLO1 and MFHI1. + */ + +#include +#include +#include + +int main() +{ + int32_t rs = 12207031, rt = 305175781; + int32_t rs1 = 32452867, rt1 = 49979687; + int64_t lo, hi, lo1, hi1; + int64_t r, r1; + + /* Test both LO/HI and LO1/HI1 to verify separation. */ + __asm__ __volatile__ ( + " mult $0, %4, %5\n" + " mult1 $0, %6, %7\n" + " mflo %0\n" + " mfhi %1\n" + " mflo1 %2\n" + " mfhi1 %3\n" + : "=r" (lo), "=r" (hi), + "=r" (lo1), "=r" (hi1) + : "r" (rs), "r" (rt), + "r" (rs1), "r" (rt1)); + r = ((int64_t)hi << 32) | (uint32_t)lo; + r1 = ((int64_t)hi1 << 32) | (uint32_t)lo1; + + assert(r == 3725290219116211); + assert(r1 == 1621984134912629); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_mtlohi1.c b/tests/tcg/mips/user/isa/r5900/test_r5900_mtlohi1.c new file mode 100644 index 0000000000..7f3e72835a --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_mtlohi1.c @@ -0,0 +1,40 @@ +/* + * Test R5900-specific MTLO1 and MTHI1. + */ + +#include +#include +#include + +int main() +{ + int32_t tlo = 12207031, thi = 305175781; + int32_t tlo1 = 32452867, thi1 = 49979687; + int32_t flo, fhi, flo1, fhi1; + + /* Test both LO/HI and LO1/HI1 to verify separation. */ + __asm__ __volatile__ ( + " mtlo %4\n" + " mthi %5\n" + " mtlo1 %6\n" + " mthi1 %7\n" + " move %0, $0\n" + " move %1, $0\n" + " move %2, $0\n" + " move %3, $0\n" + " mflo %0\n" + " mfhi %1\n" + " mflo1 %2\n" + " mfhi1 %3\n" + : "=r" (flo), "=r" (fhi), + "=r" (flo1), "=r" (fhi1) + : "r" (tlo), "r" (thi), + "r" (tlo1), "r" (thi1)); + + assert(flo == 12207031); + assert(fhi == 305175781); + assert(flo1 == 32452867); + assert(fhi1 == 49979687); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_mult.c b/tests/tcg/mips/user/isa/r5900/test_r5900_mult.c new file mode 100644 index 0000000000..5710b395e6 --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_mult.c @@ -0,0 +1,76 @@ +/* + * Test R5900-specific three-operand MULT and MULT1. + */ + +#include +#include +#include + +static int64_t mult(int32_t rs, int32_t rt) +{ + int32_t rd, lo, hi; + int64_t r; + + __asm__ __volatile__ ( + " mult %0, %3, %4\n" + " mflo %1\n" + " mfhi %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt)); + r = ((int64_t)hi << 32) | (uint32_t)lo; + + assert((int64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +static int64_t mult1(int32_t rs, int32_t rt) +{ + int32_t rd, lo, hi; + int64_t r; + + __asm__ __volatile__ ( + " mult1 %0, %3, %4\n" + " mflo1 %1\n" + " mfhi1 %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt)); + r = ((int64_t)hi << 32) | (uint32_t)lo; + + assert((int64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +static int64_t mult_variants(int32_t rs, int32_t rt) +{ + int64_t rd = mult(rs, rt); + int64_t rd1 = mult1(rs, rt); + + assert(rd == rd1); + + return rd; +} + +static void verify_mult_negations(int32_t rs, int32_t rt, int64_t expected) +{ + assert(mult_variants(rs, rt) == expected); + assert(mult_variants(-rs, rt) == -expected); + assert(mult_variants(rs, -rt) == -expected); + assert(mult_variants(-rs, -rt) == expected); +} + +int main() +{ + verify_mult_negations(17, 19, 323); + verify_mult_negations(77773, 99991, 7776600043); + verify_mult_negations(12207031, 305175781, 3725290219116211); + + assert(mult_variants(-0x80000000, 0x7FFFFFFF) == -0x3FFFFFFF80000000); + assert(mult_variants(-0x80000000, -0x7FFFFFFF) == 0x3FFFFFFF80000000); + assert(mult_variants(-0x80000000, -0x80000000) == 0x4000000000000000); + + return 0; +} diff --git a/tests/tcg/mips/user/isa/r5900/test_r5900_multu.c b/tests/tcg/mips/user/isa/r5900/test_r5900_multu.c new file mode 100644 index 0000000000..f043904d69 --- /dev/null +++ b/tests/tcg/mips/user/isa/r5900/test_r5900_multu.c @@ -0,0 +1,68 @@ +/* + * Test R5900-specific three-operand MULTU and MULTU1. + */ + +#include +#include +#include + +static uint64_t multu(uint32_t rs, uint32_t rt) +{ + uint32_t rd, lo, hi; + uint64_t r; + + __asm__ __volatile__ ( + " multu %0, %3, %4\n" + " mflo %1\n" + " mfhi %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt)); + r = ((uint64_t)hi << 32) | (uint32_t)lo; + + assert((uint64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +static uint64_t multu1(uint32_t rs, uint32_t rt) +{ + uint32_t rd, lo, hi; + uint64_t r; + + __asm__ __volatile__ ( + " multu1 %0, %3, %4\n" + " mflo1 %1\n" + " mfhi1 %2\n" + : "=r" (rd), "=r" (lo), "=r" (hi) + : "r" (rs), "r" (rt)); + r = ((uint64_t)hi << 32) | (uint32_t)lo; + + assert((uint64_t)rs * rt == r); + assert(rd == lo); + + return r; +} + +static uint64_t multu_variants(uint32_t rs, uint32_t rt) +{ + uint64_t rd = multu(rs, rt); + uint64_t rd1 = multu1(rs, rt); + + assert(rd == rd1); + + return rd; +} + +int main() +{ + assert(multu_variants(17, 19) == 323); + assert(multu_variants(77773, 99991) == 7776600043); + assert(multu_variants(12207031, 305175781) == 3725290219116211); + + assert(multu_variants(0x80000000U, 0x7FFFFFFF) == 0x3FFFFFFF80000000); + assert(multu_variants(0x80000000U, 0x80000000U) == 0x4000000000000000); + assert(multu_variants(0xFFFFFFFFU, 0xFFFFFFFFU) == 0xFFFFFFFE00000001U); + + return 0; +} diff --git a/tests/tcg/multiarch/linux-test.c b/tests/tcg/multiarch/linux-test.c index e80eccc0ce..fa4243fc04 100644 --- a/tests/tcg/multiarch/linux-test.c +++ b/tests/tcg/multiarch/linux-test.c @@ -83,7 +83,7 @@ static void test_file(void) struct utimbuf tbuf; struct iovec vecs[2]; DIR *dir; - struct dirent *de; + struct dirent64 *de; /* TODO: make common tempdir creation for tcg tests */ char template[] = "/tmp/linux-test-XXXXXX"; char *tmpdir = mkdtemp(template); @@ -186,7 +186,7 @@ static void test_file(void) error("opendir"); len = 0; for(;;) { - de = readdir(dir); + de = readdir64(dir); if (!de) break; if (strcmp(de->d_name, ".") != 0 && diff --git a/tests/tcg/multiarch/sha1.c b/tests/tcg/multiarch/sha1.c index 93b7c8e808..87bfbcdf52 100644 --- a/tests/tcg/multiarch/sha1.c +++ b/tests/tcg/multiarch/sha1.c @@ -152,7 +152,7 @@ uint32_t j; j = context->count[0]; if ((context->count[0] += len << 3) < j) - context->count[1]++; + context->count[1]++; context->count[1] += (len>>29); j = (j >> 3) & 63; if ((j + len) > 63) { @@ -186,11 +186,11 @@ unsigned char c; for (i = 0; i < 2; i++) { - uint32_t t = context->count[i]; - int j; + uint32_t t = context->count[i]; + int j; - for (j = 0; j < 4; t >>= 8, j++) - *--fcp = (unsigned char) t; + for (j = 0; j < 4; t >>= 8, j++) + *--fcp = (unsigned char) t; } #else for (i = 0; i < 8; i++) { @@ -201,7 +201,7 @@ unsigned char c; c = 0200; SHA1Update(context, &c, 1); while ((context->count[0] & 504) != 448) { - c = 0000; + c = 0000; SHA1Update(context, &c, 1); } SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ diff --git a/tests/tcg/s390x/Makefile.target b/tests/tcg/s390x/Makefile.target new file mode 100644 index 0000000000..151dc075aa --- /dev/null +++ b/tests/tcg/s390x/Makefile.target @@ -0,0 +1,8 @@ +VPATH+=$(SRC_PATH)/tests/tcg/s390x +CFLAGS+=-march=zEC12 -m64 +TESTS+=hello-s390x +TESTS+=csst +TESTS+=ipm +TESTS+=exrl-trt +TESTS+=exrl-trtr +TESTS+=pack diff --git a/tests/tcg/s390x/csst.c b/tests/tcg/s390x/csst.c new file mode 100644 index 0000000000..1dae9071fb --- /dev/null +++ b/tests/tcg/s390x/csst.c @@ -0,0 +1,43 @@ +#include +#include + +int main(void) +{ + uint64_t parmlist[] = { + 0xfedcba9876543210ull, + 0, + 0x7777777777777777ull, + 0, + }; + uint64_t op1 = 0x0123456789abcdefull; + uint64_t op2 = 0; + uint64_t op3 = op1; + uint64_t cc; + + asm volatile( + " lghi %%r0,%[flags]\n" + " la %%r1,%[parmlist]\n" + " csst %[op1],%[op2],%[op3]\n" + " ipm %[cc]\n" + : [op1] "+m" (op1), + [op2] "+m" (op2), + [op3] "+r" (op3), + [cc] "=r" (cc) + : [flags] "K" (0x0301), + [parmlist] "m" (parmlist) + : "r0", "r1", "cc", "memory"); + cc = (cc >> 28) & 3; + if (cc) { + write(1, "bad cc\n", 7); + return 1; + } + if (op1 != parmlist[0]) { + write(1, "bad op1\n", 8); + return 1; + } + if (op2 != parmlist[2]) { + write(1, "bad op2\n", 8); + return 1; + } + return 0; +} diff --git a/tests/tcg/s390x/exrl-trt.c b/tests/tcg/s390x/exrl-trt.c new file mode 100644 index 0000000000..3c5323aecb --- /dev/null +++ b/tests/tcg/s390x/exrl-trt.c @@ -0,0 +1,48 @@ +#include +#include + +int main(void) +{ + char op1[] = "hello"; + char op2[256]; + uint64_t r1 = 0xffffffffffffffffull; + uint64_t r2 = 0xffffffffffffffffull; + uint64_t cc; + int i; + + for (i = 0; i < 256; i++) { + if (i == 0) { + op2[i] = 0xaa; + } else { + op2[i] = 0; + } + } + asm volatile( + " j 2f\n" + "1: trt 0(1,%[op1]),0(%[op2])\n" + "2: exrl %[op1_len],1b\n" + " lgr %[r1],%%r1\n" + " lgr %[r2],%%r2\n" + " ipm %[cc]\n" + : [r1] "+r" (r1), + [r2] "+r" (r2), + [cc] "=r" (cc) + : [op1] "r" (&op1), + [op1_len] "r" (5), + [op2] "r" (&op2) + : "r1", "r2", "cc"); + cc = (cc >> 28) & 3; + if (cc != 2) { + write(1, "bad cc\n", 7); + return 1; + } + if ((char *)r1 != &op1[5]) { + write(1, "bad r1\n", 7); + return 1; + } + if (r2 != 0xffffffffffffffaaull) { + write(1, "bad r2\n", 7); + return 1; + } + return 0; +} diff --git a/tests/tcg/s390x/exrl-trtr.c b/tests/tcg/s390x/exrl-trtr.c new file mode 100644 index 0000000000..c33153ad7e --- /dev/null +++ b/tests/tcg/s390x/exrl-trtr.c @@ -0,0 +1,48 @@ +#include +#include + +int main(void) +{ + char op1[] = {0, 1, 2, 3}; + char op2[256]; + uint64_t r1 = 0xffffffffffffffffull; + uint64_t r2 = 0xffffffffffffffffull; + uint64_t cc; + int i; + + for (i = 0; i < 256; i++) { + if (i == 1) { + op2[i] = 0xbb; + } else { + op2[i] = 0; + } + } + asm volatile( + " j 2f\n" + "1: trtr 3(1,%[op1]),0(%[op2])\n" + "2: exrl %[op1_len],1b\n" + " lgr %[r1],%%r1\n" + " lgr %[r2],%%r2\n" + " ipm %[cc]\n" + : [r1] "+r" (r1), + [r2] "+r" (r2), + [cc] "=r" (cc) + : [op1] "r" (&op1), + [op1_len] "r" (3), + [op2] "r" (&op2) + : "r1", "r2", "cc"); + cc = (cc >> 28) & 3; + if (cc != 1) { + write(1, "bad cc\n", 7); + return 1; + } + if ((char *)r1 != &op1[1]) { + write(1, "bad r1\n", 7); + return 1; + } + if (r2 != 0xffffffffffffffbbull) { + write(1, "bad r2\n", 7); + return 1; + } + return 0; +} diff --git a/tests/tcg/s390x/hello-s390x.c b/tests/tcg/s390x/hello-s390x.c new file mode 100644 index 0000000000..3dc0a05f2b --- /dev/null +++ b/tests/tcg/s390x/hello-s390x.c @@ -0,0 +1,7 @@ +#include + +int main(void) +{ + write(1, "hello\n", 6); + return 0; +} diff --git a/tests/tcg/s390x/ipm.c b/tests/tcg/s390x/ipm.c new file mode 100644 index 0000000000..742f3a18c5 --- /dev/null +++ b/tests/tcg/s390x/ipm.c @@ -0,0 +1,22 @@ +#include +#include + +int main(void) +{ + uint32_t op1 = 0x55555555; + uint32_t op2 = 0x44444444; + uint64_t cc = 0xffffffffffffffffull; + + asm volatile( + " clc 0(4,%[op1]),0(%[op2])\n" + " ipm %[cc]\n" + : [cc] "+r" (cc) + : [op1] "r" (&op1), + [op2] "r" (&op2) + : "cc"); + if (cc != 0xffffffff20ffffffull) { + write(1, "bad cc\n", 7); + return 1; + } + return 0; +} diff --git a/tests/tcg/s390x/pack.c b/tests/tcg/s390x/pack.c new file mode 100644 index 0000000000..4be36f29a7 --- /dev/null +++ b/tests/tcg/s390x/pack.c @@ -0,0 +1,21 @@ +#include + +int main(void) +{ + char data[] = {0xaa, 0xaa, 0xf1, 0xf2, 0xf3, 0xc4, 0xaa, 0xaa}; + char exp[] = {0xaa, 0xaa, 0x00, 0x01, 0x23, 0x4c, 0xaa, 0xaa}; + int i; + + asm volatile( + " pack 2(4,%[data]),2(4,%[data])\n" + : + : [data] "r" (&data[0]) + : "memory"); + for (i = 0; i < 8; i++) { + if (data[i] != exp[i]) { + write(1, "bad data\n", 9); + return 1; + } + } + return 0; +} diff --git a/tests/tcg/xtensa/Makefile b/tests/tcg/xtensa/Makefile deleted file mode 100644 index 091518c055..0000000000 --- a/tests/tcg/xtensa/Makefile +++ /dev/null @@ -1,92 +0,0 @@ --include ../../../config-host.mak - -CORE=dc232b -CROSS=xtensa-$(CORE)-elf- - -ifndef XT -SIM = ../../../xtensa-softmmu/qemu-system-xtensa -SIMFLAGS = -M sim -cpu $(CORE) -nographic -semihosting -icount 6 $(EXTFLAGS) -kernel -SIMDEBUG = -s -S -else -SIM = xt-run -SIMFLAGS = --xtensa-core=DC_B_232L --exit_with_target_code $(EXTFLAGS) -SIMDEBUG = --gdbserve=0 -endif - -HOST_CC = gcc -CC = $(CROSS)gcc -AS = $(CROSS)gcc -x assembler-with-cpp -LD = $(CROSS)ld - -XTENSA_SRC_PATH = $(SRC_PATH)/tests/tcg/xtensa -INCLUDE_DIRS = $(XTENSA_SRC_PATH) $(SRC_PATH)/target/xtensa/core-$(CORE) -XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS)) - -LDFLAGS = -Tlinker.ld - -CRT = crt.o vectors.o - -TESTCASES += test_b.tst -TESTCASES += test_bi.tst -#TESTCASES += test_boolean.tst -TESTCASES += test_break.tst -TESTCASES += test_bz.tst -TESTCASES += test_cache.tst -TESTCASES += test_clamps.tst -TESTCASES += test_extui.tst -TESTCASES += test_fail.tst -TESTCASES += test_interrupt.tst -TESTCASES += test_loop.tst -TESTCASES += test_mac16.tst -TESTCASES += test_max.tst -TESTCASES += test_min.tst -TESTCASES += test_mmu.tst -TESTCASES += test_mul16.tst -TESTCASES += test_mul32.tst -TESTCASES += test_nsa.tst -ifdef XT -TESTCASES += test_pipeline.tst -endif -TESTCASES += test_quo.tst -TESTCASES += test_rem.tst -TESTCASES += test_rst0.tst -TESTCASES += test_s32c1i.tst -TESTCASES += test_sar.tst -TESTCASES += test_sext.tst -TESTCASES += test_shift.tst -TESTCASES += test_sr.tst -TESTCASES += test_timer.tst -TESTCASES += test_windowed.tst - -all: build - -linker.ld: $(XTENSA_SRC_PATH)/linker.ld.S - $(HOST_CC) $(XTENSA_INC) -E -P $< -o $@ - -%.o: $(XTENSA_SRC_PATH)/%.c - $(CC) $(XTENSA_INC) $(CFLAGS) -c $< -o $@ - -%.o: $(XTENSA_SRC_PATH)/%.S - $(CC) $(XTENSA_INC) $(ASFLAGS) -c $< -o $@ - -%.tst: %.o linker.ld $(XTENSA_SRC_PATH)/macros.inc $(CRT) Makefile - $(LD) $(LDFLAGS) $(NOSTDFLAGS) $(CRT) $< -o $@ - -build: $(TESTCASES) - -check: $(addprefix run-, $(TESTCASES)) - -run-%.tst: %.tst - $(SIM) $(SIMFLAGS) ./$< - -run-test_fail.tst: test_fail.tst - ! $(SIM) $(SIMFLAGS) ./$< - -debug-%.tst: %.tst - $(SIM) $(SIMDEBUG) $(SIMFLAGS) ./$< - -host-debug-%.tst: %.tst - gdb --args $(SIM) $(SIMFLAGS) ./$< - -clean: - $(RM) -fr $(TESTCASES) $(CRT) linker.ld diff --git a/tests/tcg/xtensa/Makefile.include b/tests/tcg/xtensa/Makefile.include new file mode 100644 index 0000000000..423c00a5d3 --- /dev/null +++ b/tests/tcg/xtensa/Makefile.include @@ -0,0 +1,11 @@ +# Makefile.include for xtensa targets +# +# The compilers can only be used for building system tests + +ifeq ($(CONFIG_SOFTMMU),y) +DOCKER_IMAGE=debian-xtensa-cross + +# default to the dc232b cpu +DOCKER_CROSS_COMPILER=/opt/2018.02/xtensa-dc232b-elf/bin/xtensa-dc232b-elf-gcc +DOCKER_CROSS_LINKER=/opt/2018.02/xtensa-dc232b-elf/bin/xtensa-dc232b-elf-ld +endif diff --git a/tests/tcg/xtensa/Makefile.softmmu-target b/tests/tcg/xtensa/Makefile.softmmu-target new file mode 100644 index 0000000000..8212d96b81 --- /dev/null +++ b/tests/tcg/xtensa/Makefile.softmmu-target @@ -0,0 +1,42 @@ +# +# Xtensa softmmu tests +# + +ifneq ($(TARGET_WORDS_BIGENDIAN),y) + +XTENSA_SRC = $(SRC_PATH)/tests/tcg/xtensa +XTENSA_ALL = $(filter-out $(XTENSA_SRC)/linker.ld.S,$(wildcard $(XTENSA_SRC)/*.S)) +XTENSA_TESTS = $(patsubst $(XTENSA_SRC)/%.S, %, $(XTENSA_ALL)) +# Filter out common blobs and broken tests +XTENSA_BROKEN_TESTS = crt vectors +XTENSA_USABLE_TESTS = $(filter-out $(XTENSA_BROKEN_TESTS), $(XTENSA_TESTS)) + +# add to the list of tests +TESTS += $(XTENSA_USABLE_TESTS) +VPATH += $(XTENSA_SRC) + +CORE=dc232b +QEMU_OPTS+=-M sim -cpu $(CORE) -nographic -semihosting -icount 6 $(EXTFLAGS) -kernel + +INCLUDE_DIRS = $(SRC_PATH)/target/xtensa/core-$(CORE) +XTENSA_INC = $(addprefix -I,$(INCLUDE_DIRS)) + +vectors_ASFLAGS = -mtext-section-literals +ASFLAGS = -Wa,--no-absolute-literals +LDFLAGS = -Tlinker.ld -nostartfiles -nostdlib + +CRT = crt.o vectors.o + +linker.ld: linker.ld.S + $(CC) $(XTENSA_INC) -E -P $< -o $@ + +$(XTENSA_USABLE_TESTS): linker.ld macros.inc $(CRT) Makefile.softmmu-target + +# special rule for common blobs +%.o: %.S + $(CC) $(XTENSA_INC) $($*_ASFLAGS) $(ASFLAGS) -c $< -o $@ + +%: %.S + $(CC) $(XTENSA_INC) $(ASFLAGS) $< -o $@ $(LDFLAGS) $(NOSTDFLAGS) $(CRT) + +endif diff --git a/tests/tcg/xtensa/linker.ld.S b/tests/tcg/xtensa/linker.ld.S index 5902302cf8..ac89b0054e 100644 --- a/tests/tcg/xtensa/linker.ld.S +++ b/tests/tcg/xtensa/linker.ld.S @@ -1,17 +1,29 @@ #include "core-isa.h" -#if XTENSA_HAVE_BE +#ifndef XCHAL_VECBASE_RESET_VADDR +#define XCHAL_VECBASE_RESET_VADDR XCHAL_WINDOW_VECTORS_VADDR +#define XCHAL_WINDOW_OF4_VECOFS 0x00000000 +#define XCHAL_WINDOW_UF4_VECOFS 0x00000040 +#define XCHAL_WINDOW_OF8_VECOFS 0x00000080 +#define XCHAL_WINDOW_UF8_VECOFS 0x000000C0 +#define XCHAL_WINDOW_OF12_VECOFS 0x00000100 +#define XCHAL_WINDOW_UF12_VECOFS 0x00000140 +#endif + +#define RAM_SIZE 0x08000000 /* 128M */ +#define ROM_SIZE 0x00001000 /* 4k */ +#define VECTORS_RESERVED_SIZE 0x1000 + +#if XCHAL_HAVE_BE OUTPUT_FORMAT("elf32-xtensa-be") #else OUTPUT_FORMAT("elf32-xtensa-le") #endif ENTRY(_start) -__DYNAMIC = 0; - MEMORY { - ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = 0x08000000 /* 128M */ - rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = 0x00001000 /* 4k */ + ram : ORIGIN = XCHAL_VECBASE_RESET_VADDR, LENGTH = RAM_SIZE + rom : ORIGIN = XCHAL_RESET_VECTOR_VADDR, LENGTH = ROM_SIZE } SECTIONS @@ -22,66 +34,90 @@ SECTIONS *(.init.*) } > rom - .vector : +#if XCHAL_HAVE_WINDOWED + .vector.window XCHAL_WINDOW_VECTORS_VADDR : { . = XCHAL_WINDOW_OF4_VECOFS; *(.vector.window_overflow_4) - *(.vector.window_overflow_4.*) . = XCHAL_WINDOW_UF4_VECOFS; *(.vector.window_underflow_4) - *(.vector.window_underflow_4.*) . = XCHAL_WINDOW_OF8_VECOFS; *(.vector.window_overflow_8) - *(.vector.window_overflow_8.*) . = XCHAL_WINDOW_UF8_VECOFS; *(.vector.window_underflow_8) - *(.vector.window_underflow_8.*) . = XCHAL_WINDOW_OF12_VECOFS; *(.vector.window_overflow_12) - *(.vector.window_overflow_12.*) . = XCHAL_WINDOW_UF12_VECOFS; *(.vector.window_underflow_12) - *(.vector.window_underflow_12.*) - + } +#endif #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 2 - . = XCHAL_INTLEVEL2_VECOFS; + .vector.level2 XCHAL_INTLEVEL2_VECTOR_VADDR : + { *(.vector.level2) - *(.vector.level2.*) + } #endif #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 3 - . = XCHAL_INTLEVEL3_VECOFS; + .vector.level3 XCHAL_INTLEVEL3_VECTOR_VADDR : + { *(.vector.level3) - *(.vector.level3.*) + } #endif #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 4 - . = XCHAL_INTLEVEL4_VECOFS; + .vector.level4 XCHAL_INTLEVEL4_VECTOR_VADDR : + { *(.vector.level4) - *(.vector.level4.*) + } #endif #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 5 - . = XCHAL_INTLEVEL5_VECOFS; + .vector.level5 XCHAL_INTLEVEL5_VECTOR_VADDR : + { *(.vector.level5) - *(.vector.level5.*) + } #endif #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 6 - . = XCHAL_INTLEVEL6_VECOFS; + .vector.level6 XCHAL_INTLEVEL6_VECTOR_VADDR : + { *(.vector.level6) - *(.vector.level6.*) + } #endif #if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 7 - . = XCHAL_INTLEVEL7_VECOFS; + .vector.level7 XCHAL_INTLEVEL7_VECTOR_VADDR : + { *(.vector.level7) - *(.vector.level7.*) + } #endif - - . = XCHAL_KERNEL_VECOFS; + .vector.kernel XCHAL_KERNEL_VECTOR_VADDR : + { *(.vector.kernel) - *(.vector.kernel.*) - . = XCHAL_USER_VECOFS; + } + .vector.user XCHAL_USER_VECTOR_VADDR : + { *(.vector.user) - *(.vector.user.*) - . = XCHAL_DOUBLEEXC_VECOFS; + } + .vector.double XCHAL_DOUBLEEXC_VECTOR_VADDR : + { *(.vector.double) + } + + .vector.text XCHAL_VECBASE_RESET_VADDR + VECTORS_RESERVED_SIZE : + { + *(.vector.window_overflow_4.*) + *(.vector.window_underflow_4.*) + *(.vector.window_overflow_8.*) + *(.vector.window_underflow_8.*) + *(.vector.window_overflow_12.*) + *(.vector.window_underflow_12.*) + + *(.vector.level2.*) + *(.vector.level3.*) + *(.vector.level4.*) + *(.vector.level5.*) + *(.vector.level6.*) + *(.vector.level7.*) + + *(.vector.kernel.*) + *(.vector.user.*) *(.vector.double.*) } > ram diff --git a/tests/tcg/xtensa/macros.inc b/tests/tcg/xtensa/macros.inc index 4ebd30ab86..aa8f95bce8 100644 --- a/tests/tcg/xtensa/macros.inc +++ b/tests/tcg/xtensa/macros.inc @@ -23,11 +23,14 @@ main: movi a0, result sub a2, a2, a0 movi a3, 0 - loopnez a2, 1f - l8ui a2, a0, 0 - or a3, a3, a2 - addi a0, a0, 1 + beqz a2, 2f 1: + l8ui a1, a0, 0 + or a3, a3, a1 + addi a0, a0, 1 + addi a2, a2, -1 + bnez a2, 1b +2: exit .endm @@ -49,7 +52,9 @@ main: .endm .macro test name - //print test_\name +#ifdef DEBUG + print test_\name +#endif test_init test_\name: .global test_\name @@ -74,6 +79,9 @@ test_\name: l32i a2, a2, 0 movi a3, 1 s8i a3, a2, 0 +#ifdef DEBUG + print failed +#endif j 99f .endm @@ -89,3 +97,26 @@ test_\name: movi a3, \addr s32i a3, a2, 0 .endm + +.macro dump r +#ifdef DEBUG +.data +.align 4 +1: .word 0 +.text + movi a4, 1b + s32i a2, a4, 0 + movi a2, 4 + movi a3, 1 + movi a5, 4 + simcall + movi a4, 1b + l32i a2, a4, 0 +#endif +.endm + +#define glue(a, b) _glue(a, b) +#define _glue(a, b) a ## b + +#define glue3(a, b, c) _glue3(a, b, c) +#define _glue3(a, b, c) a ## b ## c diff --git a/tests/tcg/xtensa/test_b.S b/tests/tcg/xtensa/test_b.S index 8e81f956df..713a454c53 100644 --- a/tests/tcg/xtensa/test_b.S +++ b/tests/tcg/xtensa/test_b.S @@ -84,12 +84,24 @@ test_end test bbc movi a2, 0xfffffffd - movi a3, 0xffffff01 +#undef BIT +#if XCHAL_HAVE_BE +#define BIT 0xfffffffe +#else +#define BIT 0xffffff01 +#endif + movi a3, BIT bbc a2, a3, 1f test_fail 1: movi a2, 8 - movi a3, 0xffffff03 +#undef BIT +#if XCHAL_HAVE_BE +#define BIT 0xfffffffc +#else +#define BIT 0xffffff03 +#endif + movi a3, BIT bbc a2, a3, 1f j 2f 1: @@ -99,11 +111,11 @@ test_end test bbci movi a2, 0xfffdffff - bbci a2, 17, 1f + bbci.l a2, 17, 1f test_fail 1: movi a2, 0x00020000 - bbci a2, 17, 1f + bbci.l a2, 17, 1f j 2f 1: test_fail @@ -192,12 +204,24 @@ test_end test bbs movi a2, 8 - movi a3, 0xffffff03 +#undef BIT +#if XCHAL_HAVE_BE +#define BIT 0xfffffffc +#else +#define BIT 0xffffff03 +#endif + movi a3, BIT bbs a2, a3, 1f test_fail 1: movi a2, 0xfffffffd - movi a3, 0xffffff01 +#undef BIT +#if XCHAL_HAVE_BE +#define BIT 0xfffffffe +#else +#define BIT 0xffffff01 +#endif + movi a3, BIT bbs a2, a3, 1f j 2f 1: @@ -207,11 +231,11 @@ test_end test bbsi movi a2, 0x00020000 - bbsi a2, 17, 1f + bbsi.l a2, 17, 1f test_fail 1: movi a2, 0xfffdffff - bbsi a2, 17, 1f + bbsi.l a2, 17, 1f j 2f 1: test_fail diff --git a/tests/tcg/xtensa/test_boolean.S b/tests/tcg/xtensa/test_boolean.S index eac40e0973..5a850bfe7e 100644 --- a/tests/tcg/xtensa/test_boolean.S +++ b/tests/tcg/xtensa/test_boolean.S @@ -2,6 +2,8 @@ test_suite boolean +#if XCHAL_HAVE_BOOLEANS + test all4 movi a2, 0xfec0 wsr a2, br @@ -20,4 +22,6 @@ test all4 assert eq, a2, a3 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_break.S b/tests/tcg/xtensa/test_break.S index 775cd7c260..3379a3f9f0 100644 --- a/tests/tcg/xtensa/test_break.S +++ b/tests/tcg/xtensa/test_break.S @@ -1,10 +1,13 @@ #include "macros.inc" -#define debug_level 6 -#define debug_vector level6 - test_suite break +#if XCHAL_HAVE_DEBUG + +#define debug_level XCHAL_DEBUGLEVEL +#define debug_vector glue(level, XCHAL_DEBUGLEVEL) +#define EPC_DEBUG glue(epc, XCHAL_DEBUGLEVEL) + test break set_vector debug_vector, 0 rsil a2, debug_level @@ -21,7 +24,7 @@ test break and a2, a2, a3 movi a3, 0x10 | debug_level assert eq, a2, a3 - rsr a2, epc6 + rsr a2, EPC_DEBUG movi a3, 1b assert eq, a2, a3 rsr a2, debugcause @@ -45,7 +48,7 @@ test breakn and a2, a2, a3 movi a3, 0x10 | debug_level assert eq, a2, a3 - rsr a2, epc6 + rsr a2, EPC_DEBUG movi a3, 1b assert eq, a2, a3 rsr a2, debugcause @@ -53,6 +56,7 @@ test breakn assert eq, a2, a3 test_end +#if XCHAL_NUM_IBREAK test ibreak set_vector debug_vector, 0 rsil a2, debug_level @@ -83,7 +87,7 @@ test ibreak and a2, a2, a3 movi a3, 0x10 | debug_level assert eq, a2, a3 - rsr a2, epc6 + rsr a2, EPC_DEBUG movi a3, 1b assert eq, a2, a3 rsr a2, debugcause @@ -110,7 +114,7 @@ test ibreak_remove and a2, a2, a3 movi a3, 0x10 | debug_level assert eq, a2, a3 - rsr a2, epc6 + rsr a2, EPC_DEBUG movi a3, 2b assert eq, a2, a3 rsr a2, debugcause @@ -141,6 +145,7 @@ test ibreak_priority movi a3, 0x2 assert eq, a2, a3 test_end +#endif test icount set_vector debug_vector, 2f @@ -158,7 +163,7 @@ test icount 2: movi a2, 0 wsr a2, icountlevel - rsr a2, epc6 + rsr a2, EPC_DEBUG movi a3, 1b assert eq, a2, a3 rsr a2, debugcause @@ -167,7 +172,7 @@ test icount test_end .macro check_dbreak dr - rsr a2, epc6 + rsr a2, EPC_DEBUG movi a3, 1b assert eq, a2, a3 rsr a2, debugcause @@ -194,6 +199,7 @@ test_end reset_ps .endm +#if XCHAL_NUM_DBREAK test dbreak_exact dbreak_test 0, 0x4000003f, 0xd000007f, 0xd000007f, l8ui dbreak_test 1, 0x4000003e, 0xd000007e, 0xd000007e, l16ui @@ -253,5 +259,8 @@ test dbreak_invalid dbreak_test 0, 0x40000030, 0xd0000071, 0xd0000070, l16ui dbreak_test 1, 0x40000035, 0xd0000072, 0xd0000070, l32i test_end +#endif + +#endif test_suite_end diff --git a/tests/tcg/xtensa/test_cache.S b/tests/tcg/xtensa/test_cache.S index 6b2df9734b..7e6ba4c18a 100644 --- a/tests/tcg/xtensa/test_cache.S +++ b/tests/tcg/xtensa/test_cache.S @@ -7,6 +7,8 @@ test_suite cache +#if XCHAL_HAVE_PTP_MMU + .macro pf_op op \op a2, 0 \op a3, 0 @@ -18,14 +20,23 @@ test prefetch movi a3, 0xd8000000 /* non-cacheable */ movi a4, 0x00001235 /* unmapped */ +#if XCHAL_DCACHE_SIZE pf_op dpfr pf_op dpfro pf_op dpfw pf_op dpfwo +#endif +#ifdef XCHAL_ICACHE_SIZE pf_op ipf - +#endif +#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY +#if XCHAL_DCACHE_LINE_LOCKABLE dpfl a2, 0 +#endif +#if XCHAL_ICACHE_LINE_LOCKABLE ipfl a2, 0 +#endif +#endif test_end .macro cache_fault op, addr, exc_code @@ -46,10 +57,16 @@ test_end assert eq, a2, a3 .endm +#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY + +#if XCHAL_DCACHE_LINE_LOCKABLE test dpfl_tlb_miss cache_fault dpfl, 0x00002345, 24 test_end +#endif +#if XCHAL_DCACHE_SIZE +#if XCHAL_DCACHE_IS_WRITEBACK test dhwb_tlb_miss cache_fault dhwb, 0x00002345, 24 test_end @@ -57,16 +74,21 @@ test_end test dhwbi_tlb_miss cache_fault dhwbi, 0x00002345, 24 test_end +#endif test dhi_tlb_miss cache_fault dhi, 0x00002345, 24 test_end +#if XCHAL_DCACHE_LINE_LOCKABLE test dhu_tlb_miss cache_fault dhu, 0x00002345, 24 test_end +#endif +#endif - +#if XCHAL_ICACHE_SIZE +#if XCHAL_ICACHE_LINE_LOCKABLE test ipfl_tlb_miss cache_fault ipfl, 0x00002345, 16 test_end @@ -74,24 +96,40 @@ test_end test ihu_tlb_miss cache_fault ihu, 0x00002345, 16 test_end +#endif test ihi_tlb_miss cache_fault ihi, 0x00002345, 16 test_end +#endif + +#endif + +#endif test_suite_end -.macro cache_all op1, op2, size, linesize - movi a2, 0 - movi a3, \size -1: - \op1 a2, 0 - \op2 a2, 0 - addi a2, a2, \linesize - bltu a2, a3, 1b -.endm - cache_unlock_invalidate: - cache_all diu, dii, XCHAL_DCACHE_SIZE, XCHAL_DCACHE_LINESIZE - cache_all iiu, iii, XCHAL_ICACHE_SIZE, XCHAL_ICACHE_LINESIZE +#if XCHAL_DCACHE_SIZE + movi a2, 0 + movi a3, XCHAL_DCACHE_SIZE +1: +#if XCHAL_DCACHE_LINE_LOCKABLE + diu a2, 0 +#endif + dii a2, 0 + addi a2, a2, XCHAL_DCACHE_LINESIZE + bltu a2, a3, 1b +#endif +#if XCHAL_ICACHE_SIZE + movi a2, 0 + movi a3, XCHAL_ICACHE_SIZE +1: +#if XCHAL_ICACHE_LINE_LOCKABLE + iiu a2, 0 +#endif + iii a2, 0 + addi a2, a2, XCHAL_ICACHE_LINESIZE + bltu a2, a3, 1b +#endif ret diff --git a/tests/tcg/xtensa/test_clamps.S b/tests/tcg/xtensa/test_clamps.S index 3efabfd9d3..d9b2c38ac1 100644 --- a/tests/tcg/xtensa/test_clamps.S +++ b/tests/tcg/xtensa/test_clamps.S @@ -2,6 +2,8 @@ test_suite clamps +#if XCHAL_HAVE_CLAMPS + test clamps movi a2, 0 movi a3, 0 @@ -39,4 +41,6 @@ test clamps assert eq, a3, a2 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_fail.S b/tests/tcg/xtensa/test_fail.S deleted file mode 100644 index 1c26d50790..0000000000 --- a/tests/tcg/xtensa/test_fail.S +++ /dev/null @@ -1,9 +0,0 @@ -#include "macros.inc" - -test_suite fail - -test fail - test_fail -test_end - -test_suite_end diff --git a/tests/tcg/xtensa/test_flix.S b/tests/tcg/xtensa/test_flix.S new file mode 100644 index 0000000000..7af06b2b88 --- /dev/null +++ b/tests/tcg/xtensa/test_flix.S @@ -0,0 +1,77 @@ +#include "macros.inc" + +test_suite flix + +#if XCHAL_HAVE_FLIX3 + +test misc + { + mov a3, a4 + mov a2, a3 + nop + } + { + nop + bne.w18 a2, a3, 1f + } + movi a2, 1f + { + mov a2, a3 + mov a3, a2 + nop + } + { + l32i a2, a3, 0 + add a4, a4, a2 + nop + } + { + mov a3, a4 + jx a3 + nop + } +1: +test_end + +test sum + + movi a2, 0 + movi a3, 2f + movi a4, 0 + movi a5, 4 + + loop a5, 1f + { + l32i a2, a3, 0 + addi a3, a3, 4 + add a4, a4, a2 + } +1: + add a4, a4, a2 + assert eqi, a4, 10 + .data +2: + .word 1, 2, 3, 4 + .previous +test_end + +test rep_dependency + + { + movi a2, 1 + movi a3, 2 + nop + } + { + or a2, a3, a3 + or a3, a2, a2 + nop + } + assert eqi, a2, 2 + assert eqi, a3, 1 + +test_end + +#endif + +test_suite_end diff --git a/tests/tcg/xtensa/test_fp0_arith.S b/tests/tcg/xtensa/test_fp0_arith.S new file mode 100644 index 0000000000..253d033a33 --- /dev/null +++ b/tests/tcg/xtensa/test_fp0_arith.S @@ -0,0 +1,173 @@ +#include "macros.inc" + +test_suite fp0_arith + +#if XCHAL_HAVE_FP + +.macro movfp fr, v + movi a2, \v + wfr \fr, a2 +.endm + +.macro check_res fr, r + rfr a2, \fr + dump a2 + movi a3, \r + assert eq, a2, a3 + rur a2, fsr + assert eqi, a2, 0 +.endm + +.macro test_op2_rm op, fr0, fr1, fr2, v0, v1, r + movi a2, 0 + wur a2, fsr + movfp \fr0, \v0 + movfp \fr1, \v1 + \op \fr2, \fr0, \fr1 + check_res \fr2, \r +.endm + +.macro test_op3_rm op, fr0, fr1, fr2, fr3, v0, v1, v2, r + movi a2, 0 + wur a2, fsr + movfp \fr0, \v0 + movfp \fr1, \v1 + movfp \fr2, \v2 + \op \fr0, \fr1, \fr2 + check_res \fr3, \r +.endm + +.macro test_op2_ex op, fr0, fr1, fr2, v0, v1, rm, r + movi a2, \rm + wur a2, fcr + test_op2_rm \op, \fr0, \fr1, \fr2, \v0, \v1, \r + movi a2, (\rm) | 0x7c + wur a2, fcr + test_op2_rm \op, \fr0, \fr1, \fr2, \v0, \v1, \r +.endm + +.macro test_op3_ex op, fr0, fr1, fr2, fr3, v0, v1, v2, rm, r + movi a2, \rm + wur a2, fcr + test_op3_rm \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, \r + movi a2, (\rm) | 0x7c + wur a2, fcr + test_op3_rm \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, \r +.endm + +.macro test_op2 op, fr0, fr1, fr2, v0, v1, r0, r1, r2, r3 + test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 0, \r0 + test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 1, \r1 + test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 2, \r2 + test_op2_ex \op, \fr0, \fr1, \fr2, \v0, \v1, 3, \r3 +.endm + +.macro test_op3 op, fr0, fr1, fr2, fr3, v0, v1, v2, r0, r1, r2, r3 + test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 0, \r0 + test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 1, \r1 + test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 2, \r2 + test_op3_ex \op, \fr0, \fr1, \fr2, \fr3, \v0, \v1, \v2, 3, \r3 +.endm + +.macro test_op2_cpe op + set_vector kernel, 2f + movi a2, 0 + wsr a2, cpenable +1: + \op f2, f0, f1 + test_fail +2: + rsr a2, excvaddr + movi a3, 1b + assert eq, a2, a3 + rsr a2, exccause + movi a3, 32 + assert eq, a2, a3 + + set_vector kernel, 0 + movi a2, 1 + wsr a2, cpenable +.endm + +test add_s + movi a2, 1 + wsr a2, cpenable + + test_op2 add.s, f0, f1, f2, 0x3fc00000, 0x34400000, \ + 0x3fc00002, 0x3fc00001, 0x3fc00002, 0x3fc00001 + test_op2 add.s, f3, f4, f5, 0x3fc00000, 0x34a00000, \ + 0x3fc00002, 0x3fc00002, 0x3fc00003, 0x3fc00002 + + /* MAX_FLOAT + MAX_FLOAT = +inf/MAX_FLOAT */ + test_op2 add.s, f6, f7, f8, 0x7f7fffff, 0x7f7fffff, \ + 0x7f800000, 0x7f7fffff, 0x7f800000, 0x7f7fffff +test_end + +test add_s_inf + /* 1 + +inf = +inf */ + test_op2 add.s, f6, f7, f8, 0x3fc00000, 0x7f800000, \ + 0x7f800000, 0x7f800000, 0x7f800000, 0x7f800000 + + /* +inf + -inf = default NaN */ + test_op2 add.s, f0, f1, f2, 0x7f800000, 0xff800000, \ + 0x7fc00000, 0x7fc00000, 0x7fc00000, 0x7fc00000 +test_end + +test add_s_nan + /* 1 + NaN = NaN */ + test_op2 add.s, f9, f10, f11, 0x3fc00000, 0x7fc00001, \ + 0x7fc00001, 0x7fc00001, 0x7fc00001, 0x7fc00001 + test_op2 add.s, f12, f13, f14, 0x3fc00000, 0x7f800001, \ + 0x7f800001, 0x7f800001, 0x7f800001, 0x7f800001 + + /* NaN1 + NaN2 = NaN1 */ + test_op2 add.s, f15, f0, f1, 0x7f800001, 0x7fbfffff, \ + 0x7f800001, 0x7f800001, 0x7f800001, 0x7f800001 + test_op2 add.s, f2, f3, f4, 0x7fbfffff, 0x7f800001, \ + 0x7fbfffff, 0x7fbfffff, 0x7fbfffff, 0x7fbfffff + test_op2 add.s, f5, f6, f7, 0x7fc00001, 0x7fbfffff, \ + 0x7fc00001, 0x7fc00001, 0x7fc00001, 0x7fc00001 + test_op2 add.s, f8, f9, f10, 0x7fbfffff, 0x7fc00001, \ + 0x7fbfffff, 0x7fbfffff, 0x7fbfffff, 0x7fbfffff +test_end + +test sub_s + test_op2 sub.s, f0, f1, f0, 0x3f800001, 0x33800000, \ + 0x3f800000, 0x3f800000, 0x3f800001, 0x3f800000 + test_op2 sub.s, f0, f1, f1, 0x3f800002, 0x33800000, \ + 0x3f800002, 0x3f800001, 0x3f800002, 0x3f800001 + + /* norm - norm = denorm */ + test_op2 sub.s, f6, f7, f8, 0x00800001, 0x00800000, \ + 0x00000001, 0x00000001, 0x00000001, 0x00000001 +test_end + +test mul_s + test_op2 mul.s, f0, f1, f2, 0x3f800001, 0x3f800001, \ + 0x3f800002, 0x3f800002, 0x3f800003, 0x3f800002 + + /* MAX_FLOAT/2 * MAX_FLOAT/2 = +inf/MAX_FLOAT */ + test_op2 mul.s, f6, f7, f8, 0x7f000000, 0x7f000000, \ + 0x7f800000, 0x7f7fffff, 0x7f800000, 0x7f7fffff + /* min norm * min norm = 0/denorm */ + test_op2 mul.s, f6, f7, f8, 0x00800001, 0x00800000, \ + 0x00000000, 0x00000000, 0x00000001, 0x00000000 + + /* inf * 0 = default NaN */ + test_op2 mul.s, f6, f7, f8, 0x7f800000, 0x00000000, \ + 0x7fc00000, 0x7fc00000, 0x7fc00000, 0x7fc00000 +test_end + +test madd_s + test_op3 madd.s, f0, f1, f2, f0, 0, 0x3f800001, 0x3f800001, \ + 0x3f800002, 0x3f800002, 0x3f800003, 0x3f800002 +test_end + +test msub_s + test_op3 msub.s, f0, f1, f2, f0, 0x3f800000, 0x3f800001, 0x3f800001, \ + 0xb4800000, 0xb4800000, 0xb4800000, 0xb4800001 +test_end + +#endif + +test_suite_end diff --git a/tests/tcg/xtensa/test_fp0_conv.S b/tests/tcg/xtensa/test_fp0_conv.S new file mode 100644 index 0000000000..147e3d5062 --- /dev/null +++ b/tests/tcg/xtensa/test_fp0_conv.S @@ -0,0 +1,304 @@ +#include "macros.inc" + +test_suite fp0_conv + +#if XCHAL_HAVE_FP + +.macro movfp fr, v + movi a2, \v + wfr \fr, a2 +.endm + +.macro test_ftoi_ex op, r0, fr0, v, c, r + movi a2, 0 + wur a2, fsr + movfp \fr0, \v + \op \r0, \fr0, \c + dump \r0 + movi a3, \r + assert eq, \r0, a3 + rur a2, fsr + assert eqi, a2, 0 +.endm + +.macro test_ftoi op, r0, fr0, v, c, r + movi a2, 0 + wur a2, fcr + test_ftoi_ex \op, \r0, \fr0, \v, \c, \r + movi a2, 0x7c + wur a2, fcr + test_ftoi_ex \op, \r0, \fr0, \v, \c, \r +.endm + + +.macro test_itof_ex op, fr0, ar0, v, c, r + movi a2, 0 + wur a2, fsr + movi \ar0, \v + \op \fr0, \ar0, \c + + rfr a2, \fr0 + dump a2 + movi a3, \r + assert eq, a2, a3 + rur a2, fsr + assert eqi, a2, 0 +.endm + +.macro test_itof_rm op, fr0, ar0, v, c, rm, r + movi a2, \rm + wur a2, fcr + test_itof_ex \op, \fr0, \ar0, \v, \c, \r + movi a2, (\rm) | 0x7c + wur a2, fcr + test_itof_ex \op, \fr0, \ar0, \v, \c, \r +.endm + +.macro test_itof op, fr0, ar0, v, c, r0, r1, r2, r3 + test_itof_rm \op, \fr0, \ar0, \v, \c, 0, \r0 + test_itof_rm \op, \fr0, \ar0, \v, \c, 1, \r1 + test_itof_rm \op, \fr0, \ar0, \v, \c, 2, \r2 + test_itof_rm \op, \fr0, \ar0, \v, \c, 3, \r3 +.endm + +test round_s + movi a2, 1 + wsr a2, cpenable + + /* NaN */ + test_ftoi round.s, a2, f0, 0xffc00001, 0, 0x7fffffff + test_ftoi round.s, a2, f0, 0xff800001, 0, 0x7fffffff + + /* -inf */ + test_ftoi round.s, a2, f0, 0xff800000, 0, 0x80000000 + + /* negative overflow */ + test_ftoi round.s, a2, f0, 0xceffffff, 1, 0x80000000 + test_ftoi round.s, a2, f0, 0xcf000000, 0, 0x80000000 + test_ftoi round.s, a2, f0, 0xceffffff, 0, 0x80000080 + + /* negative */ + test_ftoi round.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */ + test_ftoi round.s, a2, f0, 0xbfc00000, 0, -2 /* -1.5 */ + test_ftoi round.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */ + test_ftoi round.s, a2, f0, 0xbf800000, 0, -1 /* -1 */ + test_ftoi round.s, a2, f0, 0xbf400000, 0, -1 /* -0.75 */ + test_ftoi round.s, a2, f0, 0xbf000000, 0, 0 /* -0.5 */ + + /* positive */ + test_ftoi round.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */ + test_ftoi round.s, a2, f0, 0x3f400000, 0, 1 /* 0.75 */ + test_ftoi round.s, a2, f0, 0x3f800000, 0, 1 /* 1 */ + test_ftoi round.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */ + test_ftoi round.s, a2, f0, 0x3fc00000, 0, 2 /* 1.5 */ + test_ftoi round.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */ + + /* positive overflow */ + test_ftoi round.s, a2, f0, 0x4effffff, 0, 0x7fffff80 + test_ftoi round.s, a2, f0, 0x4f000000, 0, 0x7fffffff + test_ftoi round.s, a2, f0, 0x4effffff, 1, 0x7fffffff + + /* +inf */ + test_ftoi round.s, a2, f0, 0x7f800000, 0, 0x7fffffff + + /* NaN */ + test_ftoi round.s, a2, f0, 0x7f800001, 0, 0x7fffffff + test_ftoi round.s, a2, f0, 0x7fc00000, 0, 0x7fffffff +test_end + +test trunc_s + /* NaN */ + test_ftoi trunc.s, a2, f0, 0xffc00001, 0, 0x7fffffff + test_ftoi trunc.s, a2, f0, 0xff800001, 0, 0x7fffffff + + /* -inf */ + test_ftoi trunc.s, a2, f0, 0xff800000, 0, 0x80000000 + + /* negative overflow */ + test_ftoi trunc.s, a2, f0, 0xceffffff, 1, 0x80000000 + test_ftoi trunc.s, a2, f0, 0xcf000000, 0, 0x80000000 + test_ftoi trunc.s, a2, f0, 0xceffffff, 0, 0x80000080 + + /* negative */ + test_ftoi trunc.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */ + test_ftoi trunc.s, a2, f0, 0xbfc00000, 0, -1 /* -1.5 */ + test_ftoi trunc.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */ + test_ftoi trunc.s, a2, f0, 0xbf800000, 0, -1 /* -1 */ + test_ftoi trunc.s, a2, f0, 0xbf400000, 0, 0 /* -0.75 */ + test_ftoi trunc.s, a2, f0, 0xbf000000, 0, 0 /* -0.5 */ + + /* positive */ + test_ftoi trunc.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */ + test_ftoi trunc.s, a2, f0, 0x3f400000, 0, 0 /* 0.75 */ + test_ftoi trunc.s, a2, f0, 0x3f800000, 0, 1 /* 1 */ + test_ftoi trunc.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */ + test_ftoi trunc.s, a2, f0, 0x3fc00000, 0, 1 /* 1.5 */ + test_ftoi trunc.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */ + + /* positive overflow */ + test_ftoi trunc.s, a2, f0, 0x4effffff, 0, 0x7fffff80 + test_ftoi trunc.s, a2, f0, 0x4f000000, 0, 0x7fffffff + test_ftoi trunc.s, a2, f0, 0x4effffff, 1, 0x7fffffff + + /* +inf */ + test_ftoi trunc.s, a2, f0, 0x7f800000, 0, 0x7fffffff + + /* NaN */ + test_ftoi trunc.s, a2, f0, 0x7f800001, 0, 0x7fffffff + test_ftoi trunc.s, a2, f0, 0x7fc00000, 0, 0x7fffffff +test_end + +test floor_s + /* NaN */ + test_ftoi floor.s, a2, f0, 0xffc00001, 0, 0x7fffffff + test_ftoi floor.s, a2, f0, 0xff800001, 0, 0x7fffffff + + /* -inf */ + test_ftoi floor.s, a2, f0, 0xff800000, 0, 0x80000000 + + /* negative overflow */ + test_ftoi floor.s, a2, f0, 0xceffffff, 1, 0x80000000 + test_ftoi floor.s, a2, f0, 0xcf000000, 0, 0x80000000 + test_ftoi floor.s, a2, f0, 0xceffffff, 0, 0x80000080 + + /* negative */ + test_ftoi floor.s, a2, f0, 0xbfa00000, 1, -3 /* -1.25 * 2 */ + test_ftoi floor.s, a2, f0, 0xbfc00000, 0, -2 /* -1.5 */ + test_ftoi floor.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */ + test_ftoi floor.s, a2, f0, 0xbf800000, 0, -1 /* -1 */ + test_ftoi floor.s, a2, f0, 0xbf400000, 0, -1 /* -0.75 */ + test_ftoi floor.s, a2, f0, 0xbf000000, 0, -1 /* -0.5 */ + + /* positive */ + test_ftoi floor.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */ + test_ftoi floor.s, a2, f0, 0x3f400000, 0, 0 /* 0.75 */ + test_ftoi floor.s, a2, f0, 0x3f800000, 0, 1 /* 1 */ + test_ftoi floor.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */ + test_ftoi floor.s, a2, f0, 0x3fc00000, 0, 1 /* 1.5 */ + test_ftoi floor.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */ + + /* positive overflow */ + test_ftoi floor.s, a2, f0, 0x4effffff, 0, 0x7fffff80 + test_ftoi floor.s, a2, f0, 0x4f000000, 0, 0x7fffffff + test_ftoi floor.s, a2, f0, 0x4effffff, 1, 0x7fffffff + + /* +inf */ + test_ftoi floor.s, a2, f0, 0x7f800000, 0, 0x7fffffff + + /* NaN */ + test_ftoi floor.s, a2, f0, 0x7f800001, 0, 0x7fffffff + test_ftoi floor.s, a2, f0, 0x7fc00000, 0, 0x7fffffff +test_end + +test ceil_s + /* NaN */ + test_ftoi ceil.s, a2, f0, 0xffc00001, 0, 0x7fffffff + test_ftoi ceil.s, a2, f0, 0xff800001, 0, 0x7fffffff + + /* -inf */ + test_ftoi ceil.s, a2, f0, 0xff800000, 0, 0x80000000 + + /* negative overflow */ + test_ftoi ceil.s, a2, f0, 0xceffffff, 1, 0x80000000 + test_ftoi ceil.s, a2, f0, 0xcf000000, 0, 0x80000000 + test_ftoi ceil.s, a2, f0, 0xceffffff, 0, 0x80000080 + + /* negative */ + test_ftoi ceil.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */ + test_ftoi ceil.s, a2, f0, 0xbfc00000, 0, -1 /* -1.5 */ + test_ftoi ceil.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */ + test_ftoi ceil.s, a2, f0, 0xbf800000, 0, -1 /* -1 */ + test_ftoi ceil.s, a2, f0, 0xbf400000, 0, 0 /* -0.75 */ + test_ftoi ceil.s, a2, f0, 0xbf000000, 0, 0 /* -0.5 */ + + /* positive */ + test_ftoi ceil.s, a2, f0, 0x3f000000, 0, 1 /* 0.5 */ + test_ftoi ceil.s, a2, f0, 0x3f400000, 0, 1 /* 0.75 */ + test_ftoi ceil.s, a2, f0, 0x3f800000, 0, 1 /* 1 */ + test_ftoi ceil.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */ + test_ftoi ceil.s, a2, f0, 0x3fc00000, 0, 2 /* 1.5 */ + test_ftoi ceil.s, a2, f0, 0x3fa00000, 1, 3 /* 1.25 * 2 */ + + /* positive overflow */ + test_ftoi ceil.s, a2, f0, 0x4effffff, 0, 0x7fffff80 + test_ftoi ceil.s, a2, f0, 0x4f000000, 0, 0x7fffffff + test_ftoi ceil.s, a2, f0, 0x4effffff, 1, 0x7fffffff + + /* +inf */ + test_ftoi ceil.s, a2, f0, 0x7f800000, 0, 0x7fffffff + + /* NaN */ + test_ftoi ceil.s, a2, f0, 0x7f800001, 0, 0x7fffffff + test_ftoi ceil.s, a2, f0, 0x7fc00000, 0, 0x7fffffff +test_end + +test utrunc_s + /* NaN */ + test_ftoi utrunc.s, a2, f0, 0xffc00001, 0, 0xffffffff + test_ftoi utrunc.s, a2, f0, 0xff800001, 0, 0xffffffff + + /* -inf */ + test_ftoi utrunc.s, a2, f0, 0xff800000, 0, 0x80000000 + + /* negative overflow */ + test_ftoi utrunc.s, a2, f0, 0xceffffff, 1, 0x80000000 + test_ftoi utrunc.s, a2, f0, 0xcf000000, 0, 0x80000000 + test_ftoi utrunc.s, a2, f0, 0xceffffff, 0, 0x80000080 + + /* negative */ + test_ftoi utrunc.s, a2, f0, 0xbfa00000, 1, -2 /* -1.25 * 2 */ + test_ftoi utrunc.s, a2, f0, 0xbfc00000, 0, -1 /* -1.5 */ + test_ftoi utrunc.s, a2, f0, 0xbf800000, 1, -2 /* -1 * 2 */ + test_ftoi utrunc.s, a2, f0, 0xbf800000, 0, -1 /* -1 */ + test_ftoi utrunc.s, a2, f0, 0xbf400000, 0, 0 /* -0.75 */ + test_ftoi utrunc.s, a2, f0, 0xbf000000, 0, 0 /* -0.5 */ + + /* positive */ + test_ftoi utrunc.s, a2, f0, 0x3f000000, 0, 0 /* 0.5 */ + test_ftoi utrunc.s, a2, f0, 0x3f400000, 0, 0 /* 0.75 */ + test_ftoi utrunc.s, a2, f0, 0x3f800000, 0, 1 /* 1 */ + test_ftoi utrunc.s, a2, f0, 0x3f800000, 1, 2 /* 1 * 2 */ + test_ftoi utrunc.s, a2, f0, 0x3fc00000, 0, 1 /* 1.5 */ + test_ftoi utrunc.s, a2, f0, 0x3fa00000, 1, 2 /* 1.25 * 2 */ + + /* positive overflow */ + test_ftoi utrunc.s, a2, f0, 0x4effffff, 0, 0x7fffff80 + test_ftoi utrunc.s, a2, f0, 0x4f000000, 0, 0x80000000 + test_ftoi utrunc.s, a2, f0, 0x4effffff, 1, 0xffffff00 + test_ftoi utrunc.s, a2, f0, 0x4f800000, 1, 0xffffffff + + /* +inf */ + test_ftoi utrunc.s, a2, f0, 0x7f800000, 0, 0xffffffff + + /* NaN */ + test_ftoi utrunc.s, a2, f0, 0x7f800001, 0, 0xffffffff + test_ftoi utrunc.s, a2, f0, 0x7fc00000, 0, 0xffffffff +test_end + +test float_s + test_itof float.s, f0, a2, -1, 0, \ + 0xbf800000, 0xbf800000, 0xbf800000, 0xbf800000 + test_itof float.s, f0, a2, 0, 0, 0, 0, 0, 0 + test_itof float.s, f0, a2, 1, 1, \ + 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000 + test_itof float.s, f0, a2, 1, 0, \ + 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 + test_itof float.s, f0, a2, 0x7fffffff, 0, \ + 0x4f000000, 0x4effffff, 0x4f000000, 0x4effffff +test_end + +test ufloat_s + test_itof ufloat.s, f0, a2, 0, 0, 0, 0, 0, 0 + test_itof ufloat.s, f0, a2, 1, 1, \ + 0x3f000000, 0x3f000000, 0x3f000000, 0x3f000000 + test_itof ufloat.s, f0, a2, 1, 0, \ + 0x3f800000, 0x3f800000, 0x3f800000, 0x3f800000 + test_itof ufloat.s, f0, a2, 0x7fffffff, 0, \ + 0x4f000000, 0x4effffff, 0x4f000000, 0x4effffff + test_itof ufloat.s, f0, a2, 0xffffffff, 0, \ + 0x4f800000, 0x4f7fffff, 0x4f800000, 0x4f7fffff +test_end + +#endif + +test_suite_end diff --git a/tests/tcg/xtensa/test_fp1.S b/tests/tcg/xtensa/test_fp1.S new file mode 100644 index 0000000000..6e182e5964 --- /dev/null +++ b/tests/tcg/xtensa/test_fp1.S @@ -0,0 +1,141 @@ +#include "macros.inc" + +test_suite fp1 + +#if XCHAL_HAVE_FP + +.macro movfp fr, v + movi a2, \v + wfr \fr, a2 +.endm + +.macro test_ord_ex op, br, fr0, fr1, v0, v1, r + movi a2, 0 + wur a2, fsr + movfp \fr0, \v0 + movfp \fr1, \v1 + \op \br, \fr0, \fr1 + movi a2, 0 + movi a3, 1 + movt a2, a3, \br + assert eqi, a2, \r + rur a2, fsr + assert eqi, a2, 0 +.endm + +.macro test_ord op, br, fr0, fr1, v0, v1, r + movi a2, 0 + wur a2, fcr + test_ord_ex \op, \br, \fr0, \fr1, \v0, \v1, \r + movi a2, 0x7c + wur a2, fcr + test_ord_ex \op, \br, \fr0, \fr1, \v0, \v1, \r +.endm + +.macro test_ord_all op, aa, ab, ba, aPI, PIa, aN, Na, II, IN, NI + test_ord \op b0, f0, f1, 0x3f800000, 0x3f800000, \aa + test_ord \op b1, f2, f3, 0x3f800000, 0x3fc00000, \ab + test_ord \op b2, f4, f5, 0x3fc00000, 0x3f800000, \ba + test_ord \op b3, f6, f7, 0x3f800000, 0x7f800000, \aPI + test_ord \op b4, f8, f9, 0x7f800000, 0x3f800000, \PIa + test_ord \op b5, f10, f11, 0x3f800000, 0xffc00001, \aN + test_ord \op b6, f12, f13, 0x3f800000, 0xff800001, \aN + test_ord \op b7, f14, f15, 0x3f800000, 0x7f800001, \aN + test_ord \op b8, f0, f1, 0x3f800000, 0x7fc00000, \aN + test_ord \op b9, f2, f3, 0xffc00001, 0x3f800000, \Na + test_ord \op b10, f4, f5, 0xff800001, 0x3f800000, \Na + test_ord \op b11, f6, f7, 0x7f800001, 0x3f800000, \Na + test_ord \op b12, f8, f9, 0x7fc00000, 0x3f800000, \Na + test_ord \op b13, f10, f11, 0x7f800000, 0x7f800000, \II + test_ord \op b14, f12, f13, 0x7f800000, 0x7fc00000, \IN + test_ord \op b15, f14, f15, 0x7fc00000, 0x7f800000, \NI +.endm + +test un_s + movi a2, 1 + wsr a2, cpenable + test_ord_all un.s, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1 +test_end + +test oeq_s + test_ord_all oeq.s, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0 +test_end + +test ueq_s + test_ord_all ueq.s, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1 +test_end + +test olt_s + test_ord_all olt.s, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0 +test_end + +test ult_s + test_ord_all ult.s, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1 +test_end + +test ole_s + test_ord_all ole.s, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0 +test_end + +test ule_s + test_ord_all ule.s, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1 +test_end + +.macro test_cond op, fr0, fr1, cr, v0, v1, r + movfp \fr0, \v0 + movfp \fr1, \v1 + \op \fr0, \fr1, \cr + rfr a2, \fr0 + movi a3, \r + assert eq, a2, a3 +.endm + +test moveqz_s + movi a3, 0 + test_cond moveqz.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000 + movi a3, 1 + test_cond moveqz.s, f0, f1, a3, 0, 0x3f800000, 0 +test_end + +test movnez_s + movi a3, 0 + test_cond movnez.s, f0, f1, a3, 0, 0x3f800000, 0 + movi a3, 1 + test_cond movnez.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000 +test_end + +test movltz_s + movi a3, -1 + test_cond movltz.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000 + movi a3, 0 + test_cond movltz.s, f0, f1, a3, 0, 0x3f800000, 0 + movi a3, 1 + test_cond movltz.s, f0, f1, a3, 0, 0x3f800000, 0 +test_end + +test movgez_s + movi a3, -1 + test_cond movgez.s, f0, f1, a3, 0, 0x3f800000, 0 + movi a3, 0 + test_cond movgez.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000 + movi a3, 1 + test_cond movgez.s, f0, f1, a3, 0, 0x3f800000, 0x3f800000 +test_end + +test movf_s + olt.s b0, f0, f0 + test_cond movf.s, f0, f1, b0, 0, 0x3f800000, 0x3f800000 + ueq.s b0, f0, f0 + test_cond movf.s, f0, f1, b0, 0, 0x3f800000, 0 +test_end + +test movt_s + ueq.s b0, f0, f0 + test_cond movt.s, f0, f1, b0, 0, 0x3f800000, 0x3f800000 + olt.s b0, f0, f0 + test_cond movt.s, f0, f1, b0, 0, 0x3f800000, 0 +test_end + +#endif + +test_suite_end diff --git a/tests/tcg/xtensa/test_fp_cpenable.S b/tests/tcg/xtensa/test_fp_cpenable.S new file mode 100644 index 0000000000..882bb2f3ce --- /dev/null +++ b/tests/tcg/xtensa/test_fp_cpenable.S @@ -0,0 +1,27 @@ +#include "macros.inc" + +test_suite fp_cpenable + +#if XCHAL_HAVE_FP + +test rur + set_vector kernel, 2f + movi a2, 0 + wsr a2, cpenable + isync +1: + rur a2, fsr + //wfr f0, a2 + test_fail +2: + movi a2, 1b + rsr a3, epc1 + assert eq, a2, a3 + movi a2, 32 + rsr a3, exccause + assert eq, a2, a3 +test_end + +#endif + +test_suite_end diff --git a/tests/tcg/xtensa/test_interrupt.S b/tests/tcg/xtensa/test_interrupt.S index 876683518e..efedc43f60 100644 --- a/tests/tcg/xtensa/test_interrupt.S +++ b/tests/tcg/xtensa/test_interrupt.S @@ -1,15 +1,59 @@ #include "macros.inc" -#define LSBIT(v) ((v) ^ ((v) & ((v) - 1))) +#define LSBIT(v) ((v) & -(v)) + +#define LEVEL_MASK(x) glue3(XCHAL_INTLEVEL, x, _MASK) +#define LEVEL_SOFT_MASK(x) (LEVEL_MASK(x) & XCHAL_INTTYPE_MASK_SOFTWARE) + +#define L1_SOFT_MASK LEVEL_SOFT_MASK(1) +#define L1_SOFT LSBIT(L1_SOFT_MASK) + +#if LEVEL_SOFT_MASK(2) +#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(2) +#elif LEVEL_SOFT_MASK(3) +#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(3) +#elif LEVEL_SOFT_MASK(4) +#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(4) +#elif LEVEL_SOFT_MASK(5) +#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(5) +#elif LEVEL_SOFT_MASK(6) +#define HIGH_LEVEL_SOFT_MASK LEVEL_SOFT_MASK(6) +#else +#define HIGH_LEVEL_SOFT_MASK 0 +#endif + +#define HIGH_LEVEL_SOFT LSBIT(HIGH_LEVEL_SOFT_MASK) + +#if LEVEL_SOFT_MASK(2) +#define HIGH_LEVEL_SOFT_LEVEL 2 +#elif LEVEL_SOFT_MASK(3) +#define HIGH_LEVEL_SOFT_LEVEL 3 +#elif LEVEL_SOFT_MASK(4) +#define HIGH_LEVEL_SOFT_LEVEL 4 +#elif LEVEL_SOFT_MASK(5) +#define HIGH_LEVEL_SOFT_LEVEL 5 +#elif LEVEL_SOFT_MASK(6) +#define HIGH_LEVEL_SOFT_LEVEL 6 +#else +#define HIGH_LEVEL_SOFT_LEVEL 0 +#endif test_suite interrupt +#if XCHAL_HAVE_INTERRUPTS + .macro clear_interrupts movi a2, 0 wsr a2, intenable +#if XCHAL_NUM_TIMERS wsr a2, ccompare0 +#endif +#if XCHAL_NUM_TIMERS > 1 wsr a2, ccompare1 +#endif +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif esync rsr a2, interrupt wsr a2, intclear @@ -44,11 +88,12 @@ test rsil assert eqi, a2, 0 test_end +#if L1_SOFT test soft_disabled set_vector kernel, 1f clear_interrupts - movi a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE) + movi a2, L1_SOFT wsr a2, intset esync rsr a3, interrupt @@ -70,7 +115,7 @@ test soft_intenable set_vector kernel, 1f clear_interrupts - movi a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE) + movi a2, L1_SOFT wsr a2, intset esync rsr a3, interrupt @@ -89,7 +134,7 @@ test soft_rsil set_vector kernel, 1f clear_interrupts - movi a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE) + movi a2, L1_SOFT wsr a2, intset esync rsr a3, interrupt @@ -108,7 +153,7 @@ test soft_waiti set_vector kernel, 1f clear_interrupts - movi a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE) + movi a2, L1_SOFT wsr a2, intset esync rsr a3, interrupt @@ -127,7 +172,7 @@ test soft_user set_vector user, 2f clear_interrupts - movi a2, LSBIT(XCHAL_INTTYPE_MASK_SOFTWARE) + movi a2, L1_SOFT wsr a2, intset esync rsr a3, interrupt @@ -147,12 +192,13 @@ test soft_user check_l1 test_end +#if HIGH_LEVEL_SOFT test soft_priority set_vector kernel, 1f - set_vector level3, 2f + set_vector glue(level, HIGH_LEVEL_SOFT_LEVEL), 2f clear_interrupts - movi a2, XCHAL_INTTYPE_MASK_SOFTWARE + movi a2, L1_SOFT | HIGH_LEVEL_SOFT wsr a2, intenable rsil a3, 0 esync @@ -164,17 +210,20 @@ test soft_priority rsr a2, ps movi a3, 0x1f /* EXCM | INTMASK */ and a2, a2, a3 - movi a3, 0x13 + movi a3, 0x10 | HIGH_LEVEL_SOFT_LEVEL assert eq, a2, a3 /* EXCM and INTMASK are set for high-priority interrupt */ test_end +#endif +#endif +#if HIGH_LEVEL_SOFT test eps_epc_rfi - set_vector level3, 3f + set_vector glue(level, HIGH_LEVEL_SOFT_LEVEL), 3f clear_interrupts reset_ps - movi a2, XCHAL_INTTYPE_MASK_SOFTWARE + movi a2, L1_SOFT_MASK | HIGH_LEVEL_SOFT_MASK wsr a2, intenable rsil a3, 0 rsr a3, ps @@ -185,23 +234,26 @@ test eps_epc_rfi 2: test_fail 3: - rsr a2, eps3 + rsr a2, glue(eps, HIGH_LEVEL_SOFT_LEVEL) assert eq, a2, a3 - rsr a2, epc3 + rsr a2, glue(epc, HIGH_LEVEL_SOFT_LEVEL) movi a3, 1b assert ge, a2, a3 movi a3, 2b assert ge, a3, a2 movi a2, 4f - wsr a2, epc3 - movi a2, 0x40003 - wsr a2, eps3 - rfi 3 + wsr a2, glue(epc, HIGH_LEVEL_SOFT_LEVEL) + movi a2, 0x40000 | HIGH_LEVEL_SOFT_LEVEL + wsr a2, glue(eps, HIGH_LEVEL_SOFT_LEVEL) + rfi HIGH_LEVEL_SOFT_LEVEL test_fail 4: rsr a2, ps - movi a3, 0x40003 + movi a3, 0x40000 | HIGH_LEVEL_SOFT_LEVEL assert eq, a2, a3 test_end +#endif + +#endif test_suite_end diff --git a/tests/tcg/xtensa/test_loop.S b/tests/tcg/xtensa/test_loop.S index 5755578d01..0cfd8661ea 100644 --- a/tests/tcg/xtensa/test_loop.S +++ b/tests/tcg/xtensa/test_loop.S @@ -2,6 +2,8 @@ test_suite loop +#if XCHAL_HAVE_LOOPS + test loop movi a2, 0 movi a3, 5 @@ -160,4 +162,6 @@ test loopgtz 1: test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_lsc.S b/tests/tcg/xtensa/test_lsc.S new file mode 100644 index 0000000000..0578bf19e7 --- /dev/null +++ b/tests/tcg/xtensa/test_lsc.S @@ -0,0 +1,122 @@ +#include "macros.inc" + +test_suite lsc + +#if XCHAL_HAVE_FP + +test lsi + movi a2, 1 + wsr a2, cpenable + + movi a2, 1f + lsi f0, a2, 0 + lsi f1, a2, 4 + lsiu f2, a2, 8 + movi a3, 1f + 8 + assert eq, a2, a3 + rfr a2, f0 + movi a3, 0x3f800000 + assert eq, a2, a3 + rfr a2, f1 + movi a3, 0x40000000 + assert eq, a2, a3 + rfr a2, f2 + movi a3, 0x40400000 + assert eq, a2, a3 +.data + .align 4 +1: +.float 1, 2, 3 +.text +test_end + +test ssi + movi a2, 1f + movi a3, 0x40800000 + wfr f3, a3 + ssi f3, a2, 0 + movi a3, 0x40a00000 + wfr f4, a3 + ssi f4, a2, 4 + movi a3, 0x40c00000 + wfr f5, a3 + ssiu f5, a2, 8 + movi a3, 1f + 8 + assert eq, a2, a3 + l32i a4, a2, -8 + movi a3, 0x40800000 + assert eq, a4, a3 + l32i a4, a2, -4 + movi a3, 0x40a00000 + assert eq, a4, a3 + l32i a4, a2, 0 + movi a3, 0x40c00000 + assert eq, a4, a3 +.data + .align 4 +1: +.float 0, 0, 0 +.text +test_end + +test lsx + movi a2, 1f + movi a3, 0 + lsx f6, a2, a3 + movi a3, 4 + lsx f7, a2, a3 + movi a3, 8 + lsxu f8, a2, a3 + movi a3, 1f + 8 + assert eq, a2, a3 + rfr a2, f6 + movi a3, 0x40e00000 + assert eq, a2, a3 + rfr a2, f7 + movi a3, 0x41000000 + assert eq, a2, a3 + rfr a2, f8 + movi a3, 0x41100000 + assert eq, a2, a3 +.data + .align 4 +1: +.float 7, 8, 9 +.text +test_end + +test ssx + movi a2, 1f + movi a3, 0 + movi a4, 0x41200000 + wfr f9, a4 + ssx f9, a2, a3 + movi a3, 4 + movi a4, 0x41300000 + wfr f10, a4 + ssx f10, a2, a3 + movi a3, 8 + movi a4, 0x41400000 + wfr f11, a4 + ssxu f11, a2, a3 + movi a3, 1f + 8 + assert eq, a2, a3 + l32i a4, a2, -8 + movi a3, 0x41200000 + assert eq, a4, a3 + l32i a4, a2, -4 + movi a3, 0x41300000 + assert eq, a4, a3 + l32i a4, a2, 0 + movi a3, 0x41400000 + assert eq, a4, a3 +.data + .align 4 +1: +.float 0, 0, 0 +.text +test_end + +#endif + +test_suite_end diff --git a/tests/tcg/xtensa/test_mac16.S b/tests/tcg/xtensa/test_mac16.S index 512025d842..ee0cedd2ae 100644 --- a/tests/tcg/xtensa/test_mac16.S +++ b/tests/tcg/xtensa/test_mac16.S @@ -2,6 +2,8 @@ test_suite mac16 +#if XCHAL_HAVE_MAC16 + #define ext16(v) (((v) & 0xffff) | (((v) & 0x8000) * 0x1ffffffe)) #define mul16(a, b) ((ext16(a) * ext16(b))) @@ -240,4 +242,6 @@ test mula_dd_lddec .text test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_max.S b/tests/tcg/xtensa/test_max.S index 3caa207ea5..f349d578e3 100644 --- a/tests/tcg/xtensa/test_max.S +++ b/tests/tcg/xtensa/test_max.S @@ -2,6 +2,8 @@ test_suite max +#if XCHAL_HAVE_MINMAX + test max movi a2, 0xffffffff movi a3, 1 @@ -78,4 +80,6 @@ test maxu assert eq, a3, a4 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_min.S b/tests/tcg/xtensa/test_min.S index 551cf591e5..89ee10334f 100644 --- a/tests/tcg/xtensa/test_min.S +++ b/tests/tcg/xtensa/test_min.S @@ -2,6 +2,8 @@ test_suite min +#if XCHAL_HAVE_MINMAX + test min movi a2, 0xffffffff movi a3, 1 @@ -78,4 +80,6 @@ test minu assert eq, a3, a4 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_mmu.S b/tests/tcg/xtensa/test_mmu.S index a15316ffb3..4cbd6ef4f9 100644 --- a/tests/tcg/xtensa/test_mmu.S +++ b/tests/tcg/xtensa/test_mmu.S @@ -2,6 +2,8 @@ test_suite mmu +#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY + .purgem test_init .macro clean_tlb_way way, page_size, n_entries @@ -740,4 +742,6 @@ test cross_page_tb assert eq, a2, a3 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_mul16.S b/tests/tcg/xtensa/test_mul16.S index 98fa7042b5..32507f7f1e 100644 --- a/tests/tcg/xtensa/test_mul16.S +++ b/tests/tcg/xtensa/test_mul16.S @@ -2,6 +2,8 @@ test_suite mul16 +#if XCHAL_HAVE_MUL16 + test mul16u_pp movi a2, 0x137f5a5a mov a3, a2 @@ -80,4 +82,6 @@ test mul16s_nn assert eq, a3, a6 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_mul32.S b/tests/tcg/xtensa/test_mul32.S index b288ead9f6..862d45abce 100644 --- a/tests/tcg/xtensa/test_mul32.S +++ b/tests/tcg/xtensa/test_mul32.S @@ -2,6 +2,8 @@ test_suite mul32 +#if XCHAL_HAVE_MUL32 + test mull movi a2, 0x137f5a5a mov a3, a2 @@ -15,6 +17,8 @@ test mull assert eq, a3, a6 test_end +#endif + /* unfortunately dc232b doesn't have muluh/mulsh*/ test_suite_end diff --git a/tests/tcg/xtensa/test_nsa.S b/tests/tcg/xtensa/test_nsa.S index 479b2e2429..0af7d1f50d 100644 --- a/tests/tcg/xtensa/test_nsa.S +++ b/tests/tcg/xtensa/test_nsa.S @@ -2,6 +2,8 @@ test_suite nsa +#if XCHAL_HAVE_NSA + test nsa movi a2, 0 movi a3, 31 @@ -56,4 +58,6 @@ test nsau assert eq, a3, a2 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_phys_mem.S b/tests/tcg/xtensa/test_phys_mem.S new file mode 100644 index 0000000000..9bb3ee3866 --- /dev/null +++ b/tests/tcg/xtensa/test_phys_mem.S @@ -0,0 +1,128 @@ +#include "macros.inc" + +test_suite phys_mem + +#if XCHAL_HAVE_PTP_MMU && !XCHAL_HAVE_SPANNING_WAY + +.purgem test_init + +.macro test_init + movi a2, 0xc0000003 /* PPN */ + movi a3, 0xc0000004 /* VPN */ + wdtlb a2, a3 + witlb a2, a3 + movi a2, 0xc0000000 + wsr a2, ptevaddr +.endm + +test inst_fetch_get_pte_no_phys + set_vector kernel, 2f + + movi a2, 0x20000000 + jx a2 +2: + movi a2, 0x20000000 + rsr a3, excvaddr + assert eq, a2, a3 + rsr a3, epc1 + assert eq, a2, a3 + rsr a3, exccause + movi a2, 16 + assert eq, a2, a3 +test_end + +test read_get_pte_no_phys + set_vector kernel, 2f + + movi a2, 0x20000000 +1: + l32i a3, a2, 0 + test_fail +2: + movi a2, 0x20000000 + rsr a3, excvaddr + assert eq, a2, a3 + movi a2, 1b + rsr a3, epc1 + assert eq, a2, a3 + rsr a3, exccause + movi a2, 24 + assert eq, a2, a3 +test_end + +test write_get_pte_no_phys + set_vector kernel, 2f + + movi a2, 0x20000000 +1: + s32i a3, a2, 0 + test_fail +2: + movi a2, 0x20000000 + rsr a3, excvaddr + assert eq, a2, a3 + movi a2, 1b + rsr a3, epc1 + assert eq, a2, a3 + rsr a3, exccause + movi a2, 24 + assert eq, a2, a3 +test_end + +#endif + +test inst_fetch_no_phys + set_vector kernel, 2f + + movi a2, 0xc0000000 + jx a2 +2: + movi a2, 0xc0000000 + rsr a3, excvaddr + assert eq, a2, a3 + rsr a3, epc1 + assert eq, a2, a3 + rsr a3, exccause + movi a2, 14 + assert eq, a2, a3 +test_end + +test read_no_phys + set_vector kernel, 2f + + movi a2, 0xc0000000 +1: + l32i a3, a2, 0 + test_fail +2: + movi a2, 0xc0000000 + rsr a3, excvaddr + assert eq, a2, a3 + movi a2, 1b + rsr a3, epc1 + assert eq, a2, a3 + rsr a3, exccause + movi a2, 15 + assert eq, a2, a3 +test_end + +test write_no_phys + set_vector kernel, 2f + + movi a2, 0xc0000000 +1: + s32i a3, a2, 0 + test_fail +2: + movi a2, 0xc0000000 + rsr a3, excvaddr + assert eq, a2, a3 + movi a2, 1b + rsr a3, epc1 + assert eq, a2, a3 + rsr a3, exccause + movi a2, 15 + assert eq, a2, a3 +test_end + +test_suite_end diff --git a/tests/tcg/xtensa/test_pipeline.S b/tests/tcg/xtensa/test_pipeline.S deleted file mode 100644 index f418c11974..0000000000 --- a/tests/tcg/xtensa/test_pipeline.S +++ /dev/null @@ -1,157 +0,0 @@ -#include "macros.inc" - -.purgem test -.macro test name - movi a2, 1f - movi a3, 99f -0: - ipf a2, 0 - ipf a2, 4 - ipf a2, 8 - ipf a2, 12 - addi a2, a2, 16 - blt a2, a3, 0b - j 1f - .align 4 -1: -.endm - -test_suite pipeline - -test register_no_stall - rsr a3, ccount - add a5, a6, a6 - add a6, a5, a5 - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 3 -test_end - -test register_stall - l32i a5, a1, 0 /* data cache preload */ - nop - rsr a3, ccount - l32i a5, a1, 0 - add a6, a5, a5 /* M-to-E interlock */ - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 4 -test_end - -test j0_stall - rsr a3, ccount - j 1f /* E + 2-cycle penalty */ -1: - rsr a4, ccount /* E */ - sub a3, a4, a3 - assert eqi, a3, 4 -test_end - -test j1_stall - rsr a3, ccount - j 1f - nop -1: - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 4 -test_end - -test j5_stall - rsr a3, ccount - j 1f - nop - nop - nop - nop - nop -1: - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 4 -test_end - -test b_no_stall - movi a5, 1 - rsr a3, ccount - beqi a5, 2, 1f - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 2 -1: -test_end - -test b1_stall - movi a5, 1 - rsr a3, ccount - beqi a5, 1, 1f - nop -1: - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 4 -test_end - -test b5_stall - movi a5, 1 - rsr a3, ccount - beqi a5, 1, 1f - nop - nop - nop - nop - nop -1: - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 4 -test_end - -/* PS *SYNC */ - -test ps_dsync - rsr a5, ps - isync - rsr a3, ccount - wsr a5, ps - dsync - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 5 -test_end - -test ps_esync - rsr a5, ps - isync - rsr a3, ccount - wsr a5, ps - esync - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 5 -test_end - -test ps_rsync - rsr a5, ps - isync - rsr a3, ccount - wsr a5, ps - rsync - rsr a4, ccount - sub a3, a4, a3 - assert eqi, a3, 5 -test_end - -test ps_isync - rsr a5, ps - isync - rsr a3, ccount - wsr a5, ps - isync - rsr a4, ccount - sub a3, a4, a3 - movi a4, 9 - assert eq, a3, a4 -test_end - -test_suite_end diff --git a/tests/tcg/xtensa/test_quo.S b/tests/tcg/xtensa/test_quo.S index 5b3ae383d0..32886b913b 100644 --- a/tests/tcg/xtensa/test_quo.S +++ b/tests/tcg/xtensa/test_quo.S @@ -2,6 +2,8 @@ test_suite quo +#if XCHAL_HAVE_DIV32 + test quou_pp movi a2, 0x5a5a137f mov a3, a2 @@ -144,4 +146,6 @@ test quos_exc assert eq, a2, a3 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_rem.S b/tests/tcg/xtensa/test_rem.S index 6357e520d9..0b96bb3390 100644 --- a/tests/tcg/xtensa/test_rem.S +++ b/tests/tcg/xtensa/test_rem.S @@ -2,6 +2,8 @@ test_suite rem +#if XCHAL_HAVE_DIV32 + test remu_pp movi a2, 0x5a5a137f mov a3, a2 @@ -144,4 +146,6 @@ test rems_exc assert eq, a2, a3 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_rst0.S b/tests/tcg/xtensa/test_rst0.S index a73366b120..143e90b401 100644 --- a/tests/tcg/xtensa/test_rst0.S +++ b/tests/tcg/xtensa/test_rst0.S @@ -54,6 +54,8 @@ test add assert eq, a4, a6 test_end +#if XCHAL_HAVE_ADDX + test addx2 movi a2, 0x137fa5a5 mov a3, a2 @@ -93,6 +95,8 @@ test addx8 assert eq, a4, a6 test_end +#endif + test sub movi a2, 0x137fa5a5 mov a3, a2 @@ -106,6 +110,8 @@ test sub assert eq, a4, a6 test_end +#if XCHAL_HAVE_ADDX + test subx2 movi a2, 0x137fa5a5 mov a3, a2 @@ -145,4 +151,6 @@ test subx8 assert eq, a4, a6 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_s32c1i.S b/tests/tcg/xtensa/test_s32c1i.S index 93b575db95..2885d9d003 100644 --- a/tests/tcg/xtensa/test_s32c1i.S +++ b/tests/tcg/xtensa/test_s32c1i.S @@ -2,7 +2,13 @@ test_suite s32c1i +#if XCHAL_HAVE_S32C1I + test s32c1i_nowrite +#if XCHAL_HW_VERSION >= 230000 + movi a2, 0x29 + wsr a2, atomctl +#endif movi a2, 1f movi a3, 1 wsr a3, scompare1 @@ -20,6 +26,10 @@ test s32c1i_nowrite test_end test s32c1i_write +#if XCHAL_HW_VERSION >= 230000 + movi a2, 0x29 + wsr a2, atomctl +#endif movi a2, 1f movi a3, 3 wsr a3, scompare1 @@ -36,4 +46,6 @@ test s32c1i_write .text test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_sext.S b/tests/tcg/xtensa/test_sext.S index 087a6333a4..483d2176e4 100644 --- a/tests/tcg/xtensa/test_sext.S +++ b/tests/tcg/xtensa/test_sext.S @@ -2,6 +2,8 @@ test_suite sext +#if XCHAL_HAVE_SEXT + test sext movi a2, 0xffffff5a movi a3, 0x0000005a @@ -66,4 +68,6 @@ test sext_same_rs assert eq, a3, a2 test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_sr.S b/tests/tcg/xtensa/test_sr.S index 052f1e04a7..b1a91a0637 100644 --- a/tests/tcg/xtensa/test_sr.S +++ b/tests/tcg/xtensa/test_sr.S @@ -2,11 +2,23 @@ test_suite sr +#if XCHAL_HAVE_BE +#define LOW__SR 0x04 +#define HI_RSR 0x30 +#define HI_WSR 0x31 +#define HI_XSR 0x16 +#else +#define LOW__SR 0x40 +#define HI_RSR 0x03 +#define HI_WSR 0x13 +#define HI_XSR 0x61 +#endif + .macro sr_op sym, op_sym, op_byte, sr .if \sym \op_sym a4, \sr .else - .byte 0x40, \sr, \op_byte + .byte LOW__SR, \sr, \op_byte .endif .endm @@ -32,9 +44,9 @@ test_suite sr .macro test_sr_mask sr, sym, mask test \sr - test_sr_op \sym, \mask & 1, rsr, 0x03, \sr - test_sr_op \sym, \mask & 2, wsr, 0x13, \sr - test_sr_op \sym, \mask & 4, xsr, 0x61, \sr + test_sr_op \sym, \mask & 1, rsr, HI_RSR, \sr + test_sr_op \sym, \mask & 2, wsr, HI_WSR, \sr + test_sr_op \sym, \mask & 4, xsr, HI_XSR, \sr test_end .endm @@ -42,50 +54,183 @@ test_end test_sr_mask \sr, \conf, 7 .endm +#if XCHAL_HAVE_MAC16 test_sr acchi, 1 test_sr acclo, 1 +#else +test_sr_mask /*acchi*/17, 0, 0 +test_sr_mask /*acclo*/16, 0, 0 +#endif + +#if XCHAL_HAVE_S32C1I && XCHAL_HW_VERSION >= 230000 +test_sr atomctl, 1 +#else test_sr_mask /*atomctl*/99, 0, 0 +#endif + +#if XCHAL_HAVE_BOOLEANS +test_sr br, 1 +#else test_sr_mask /*br*/4, 0, 0 +#endif + test_sr_mask /*cacheattr*/98, 0, 0 + +#if XCHAL_HAVE_CCOUNT test_sr ccompare0, 1 test_sr ccount, 1 +#else +test_sr_mask /*ccompare0*/240, 0, 0 +test_sr_mask /*ccount*/234, 0, 0 +#endif + +#if XCHAL_HAVE_CP test_sr cpenable, 1 +#else +test_sr_mask /*cpenable*/224, 0, 0 +#endif + +#if XCHAL_HAVE_DEBUG +#if XCHAL_NUM_DBREAK test_sr dbreaka0, 1 test_sr dbreakc0, 1 +#endif test_sr_mask debugcause, 1, 1 +#else +test_sr_mask /*dbreaka0*/144, 0, 0 +test_sr_mask /*dbreakc0*/160, 0, 0 +test_sr_mask /*debugcause*/233, 0, 0 +#endif + test_sr depc, 1 + +#if XCHAL_HAVE_PTP_MMU test_sr dtlbcfg, 1 +#else +test_sr_mask /*dtlbcfg*/92, 0, 0 +#endif + test_sr epc1, 1 + +#if XCHAL_NUM_INTLEVELS > 1 test_sr epc2, 1 test_sr eps2, 1 +#else +test_sr_mask /*epc2*/178, 0, 0 +test_sr_mask /*eps2*/194, 0, 0 +#endif + test_sr exccause, 1 test_sr excsave1, 1 + +#if XCHAL_NUM_INTLEVELS > 1 test_sr excsave2, 1 +#else +test_sr_mask /*excsave2*/210, 0, 0 +#endif + test_sr excvaddr, 1 + +#if XCHAL_HAVE_DEBUG +#if XCHAL_NUM_IBREAK test_sr ibreaka0, 1 test_sr ibreakenable, 1 +#endif test_sr icount, 1 test_sr icountlevel, 1 +#else +test_sr_mask /*ibreaka0*/128, 0, 0 +test_sr_mask /*ibreakenable*/96, 0, 0 +test_sr_mask /*icount*/236, 0, 0 +test_sr_mask /*icountlevel*/237, 0, 0 +#endif + test_sr_mask /*intclear*/227, 0, 2 test_sr_mask /*interrupt*/226, 0, 3 test_sr intenable, 1 + +#if XCHAL_HAVE_PTP_MMU test_sr itlbcfg, 1 +#else +test_sr_mask /*itlbcfg*/91, 0, 0 +#endif + +#if XCHAL_HAVE_LOOPS test_sr lbeg, 1 test_sr lcount, 1 test_sr lend, 1 +#else +test_sr_mask /*lbeg*/0, 0, 0 +test_sr_mask /*lcount*/2, 0, 0 +test_sr_mask /*lend*/1, 0, 0 +#endif + +#if XCHAL_HAVE_ABSOLUTE_LITERALS test_sr litbase, 1 +#else +test_sr_mask /*litbase*/5, 0, 0 +#endif + +#if XCHAL_HAVE_MAC16 test_sr m0, 1 +#else +test_sr_mask /*m0*/32, 0, 0 +#endif + +#if XCHAL_HW_VERSION >= 250000 +test_sr_mask /*memctl*/97, 0, 7 +#else test_sr_mask /*memctl*/97, 0, 0 +#endif + +#if XCHAL_NUM_MISC_REGS test_sr misc0, 1 +#else +test_sr_mask /*misc0*/244, 0, 0 +#endif + +#if XCHAL_HAVE_PREFETCH +test_sr prefctl, 1 +#else test_sr_mask /*prefctl*/40, 0, 0 +#endif + +#if XCHAL_HAVE_PRID test_sr_mask /*prid*/235, 0, 1 +#else +test_sr_mask /*prid*/235, 0, 0 +#endif + test_sr ps, 1 + +#if XCHAL_HAVE_PTP_MMU test_sr ptevaddr, 1 test_sr rasid, 1 +#else +test_sr_mask /*ptevaddr*/83, 0, 0 +test_sr_mask /*rasid*/90, 0, 0 +#endif + test_sr sar, 1 + +#if XCHAL_HAVE_S32C1I test_sr scompare1, 1 +#else +test_sr_mask /*scompare1*/12, 0, 0 +#endif + +#if XCHAL_HAVE_VECBASE test_sr vecbase, 1 +#else +test_sr_mask /*vecbase*/231, 0, 0 +#endif + +#if XCHAL_HAVE_WINDOWED test_sr windowbase, 1 test_sr windowstart, 1 +#else +test_sr_mask /*windowbase*/72, 0, 0 +test_sr_mask /*windowstart*/73, 0, 0 +#endif test_suite_end diff --git a/tests/tcg/xtensa/test_timer.S b/tests/tcg/xtensa/test_timer.S index 6cda71adbb..1ec8e20883 100644 --- a/tests/tcg/xtensa/test_timer.S +++ b/tests/tcg/xtensa/test_timer.S @@ -2,6 +2,8 @@ #define CCOUNT_SHIFT 4 #define WAIT_LOOPS 20 +#define level1 kernel +#define INTERRUPT_LEVEL(n) glue3(XCHAL_INT, n, _LEVEL) .macro make_ccount_delta target, delta rsr \delta, ccount @@ -13,6 +15,8 @@ test_suite timer +#if XCHAL_HAVE_CCOUNT + test ccount rsr a3, ccount rsr a4, ccount @@ -32,14 +36,20 @@ test ccount_write assert ltu, a3, a4 test_end +#if XCHAL_NUM_TIMERS + test ccount_update_deadline movi a2, 0 wsr a2, intenable rsr a2, interrupt wsr a2, intclear movi a2, 0 +#if XCHAL_NUM_TIMERS > 1 wsr a2, ccompare1 +#endif +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif movi a2, 0x12345678 wsr a2, ccompare0 rsr a3, interrupt @@ -59,8 +69,12 @@ test ccompare rsr a2, interrupt wsr a2, intclear movi a2, 0 +#if XCHAL_NUM_TIMERS > 1 wsr a2, ccompare1 +#endif +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif make_ccount_delta a2, a15 wsr a2, ccompare0 @@ -76,6 +90,7 @@ test ccompare assert nei, a5, 0 test_end +#if INTERRUPT_LEVEL(XCHAL_TIMER0_INTERRUPT) == 1 test ccompare0_interrupt set_vector kernel, 2f movi a2, 0 @@ -83,8 +98,12 @@ test ccompare0_interrupt rsr a2, interrupt wsr a2, intclear movi a2, 0 +#if XCHAL_NUM_TIMERS > 1 wsr a2, ccompare1 +#endif +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif movi a3, WAIT_LOOPS make_ccount_delta a2, a15 @@ -104,16 +123,21 @@ test ccompare0_interrupt rsr a2, exccause assert eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */ test_end +#endif + +#if XCHAL_NUM_TIMERS > 1 test ccompare1_interrupt - set_vector level3, 2f + set_vector glue(level, INTERRUPT_LEVEL(XCHAL_TIMER1_INTERRUPT)), 2f movi a2, 0 wsr a2, intenable rsr a2, interrupt wsr a2, intclear movi a2, 0 wsr a2, ccompare0 +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif movi a3, WAIT_LOOPS make_ccount_delta a2, a15 @@ -123,7 +147,7 @@ test ccompare1_interrupt assert eqi, a2, 0 movi a2, 1 << XCHAL_TIMER1_INTERRUPT wsr a2, intenable - rsil a2, 2 + rsil a2, INTERRUPT_LEVEL(XCHAL_TIMER1_INTERRUPT) - 1 loop a3, 1f nop 1: @@ -131,8 +155,11 @@ test ccompare1_interrupt 2: test_end +#endif +#if XCHAL_NUM_TIMERS > 2 + test ccompare2_interrupt - set_vector level5, 2f + set_vector glue(level, INTERRUPT_LEVEL(XCHAL_TIMER2_INTERRUPT)), 2f movi a2, 0 wsr a2, intenable rsr a2, interrupt @@ -149,7 +176,7 @@ test ccompare2_interrupt assert eqi, a2, 0 movi a2, 1 << XCHAL_TIMER2_INTERRUPT wsr a2, intenable - rsil a2, 4 + rsil a2, INTERRUPT_LEVEL(XCHAL_TIMER2_INTERRUPT) - 1 loop a3, 1f nop 1: @@ -157,6 +184,8 @@ test ccompare2_interrupt 2: test_end +#endif + test ccompare_interrupt_masked set_vector kernel, 2f movi a2, 0 @@ -164,11 +193,15 @@ test ccompare_interrupt_masked rsr a2, interrupt wsr a2, intclear movi a2, 0 +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif movi a3, 2 * WAIT_LOOPS make_ccount_delta a2, a15 +#if XCHAL_NUM_TIMERS > 1 wsr a2, ccompare1 +#endif add a2, a2, a15 wsr a2, ccompare0 rsync @@ -194,11 +227,15 @@ test ccompare_interrupt_masked_waiti rsr a2, interrupt wsr a2, intclear movi a2, 0 +#if XCHAL_NUM_TIMERS > 2 wsr a2, ccompare2 +#endif movi a3, 2 * WAIT_LOOPS make_ccount_delta a2, a15 +#if XCHAL_NUM_TIMERS > 1 wsr a2, ccompare1 +#endif add a2, a2, a15 wsr a2, ccompare0 rsync @@ -214,4 +251,7 @@ test ccompare_interrupt_masked_waiti assert eqi, a2, 4 /* LEVEL1_INTERRUPT_CAUSE */ test_end +#endif +#endif + test_suite_end diff --git a/tests/tcg/xtensa/test_windowed.S b/tests/tcg/xtensa/test_windowed.S index d851e8f43c..5ead90a790 100644 --- a/tests/tcg/xtensa/test_windowed.S +++ b/tests/tcg/xtensa/test_windowed.S @@ -2,10 +2,12 @@ test_suite windowed +#if XCHAL_HAVE_WINDOWED + .altmacro .macro reset_window start - movi a2, 0xff + movi a2, 0xffff wsr a2, windowstart rsync movi a2, 0 @@ -105,7 +107,8 @@ test_end movi a3, 0x4001f assert eq, a2, a3 rsr a2, windowbase - assert eqi, a2, 8 - ((\window) / 4) + movi a3, (XCHAL_NUM_AREGS - (\window)) / 4 + assert eq, a2, a3 rsr a2, windowstart assert eqi, a2, 1 rfwu @@ -116,8 +119,8 @@ test_end rsr a2, windowbase assert eqi, a2, 0 rsr a2, windowstart - assert bsi, a2, 0 - assert bsi, a2, 8 - ((\window) / 4) + assert bsi.l, a2, 0 + assert bsi.l, a2, (XCHAL_NUM_AREGS - (\window)) / 4 .endm test underflow @@ -132,7 +135,7 @@ test_end .macro retw_test window - reset_window %(1 | (1 << (8 - (\window) / 4))) + reset_window %(1 | (1 << ((XCHAL_NUM_AREGS - \window) / 4))) reset_ps ssai 2 @@ -147,10 +150,11 @@ test_end movi a3, 0x4000f assert eq, a2, a3 rsr a2, windowbase - assert eqi, a2, 8 - ((\window) / 4) + movi a3, (XCHAL_NUM_AREGS - (\window)) / 4 + assert eq, a2, a3 rsr a2, windowstart - assert bci, a2, 0 - assert bsi, a2, 8 - ((\window) / 4) + assert bci.l, a2, 0 + assert bsi.l, a2, (XCHAL_NUM_AREGS - (\window)) / 4 .endm test retw @@ -180,7 +184,7 @@ test movsp set_vector kernel, 0 - reset_window 0x81 + reset_window %(0x1 | (1 << ((XCHAL_NUM_AREGS / 4) - 1))) reset_ps movsp a2, a3 @@ -211,8 +215,16 @@ test rotw movi a3, 0x16 movi a7, 0x17 +#if XCHAL_NUM_AREGS == 32 movi a2, 0x44 wsr a2, windowstart +#elif XCHAL_NUM_AREGS == 64 + movi a2, 0x4004 + wsr a2, windowstart + rotw -8 +#else +#error XCHAL_NUM_AREGS unsupported +#endif rsync movi a2, 0x10 @@ -350,4 +362,6 @@ test entry_overflow all_entry_overflow_tests test_end +#endif + test_suite_end diff --git a/tests/tcg/xtensa/vectors.S b/tests/tcg/xtensa/vectors.S index 265a181239..cd48cfb656 100644 --- a/tests/tcg/xtensa/vectors.S +++ b/tests/tcg/xtensa/vectors.S @@ -1,9 +1,21 @@ +#include "core-isa.h" + .macro vector name -.section .vector.\name +.section .vector.\name, "ax" +.global vector_\name +vector_\name\(): j 1f -.section .vector.\name\().text + .literal_position 1: + wsr a0, excsave1 + movi a0, 1f + ret.n + +.section .vector.\name\().text, "ax" + .literal_position +1: + rsr a0, excsave1 wsr a2, excsave1 movi a2, handler_\name l32i a2, a2, 0 @@ -20,19 +32,33 @@ handler_\name\(): .word 0 .endm +#if XCHAL_HAVE_WINDOWED vector window_overflow_4 vector window_overflow_8 vector window_overflow_12 vector window_underflow_4 vector window_underflow_8 vector window_underflow_12 +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 2 vector level2 +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 3 vector level3 +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 4 vector level4 +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 5 vector level5 +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 6 vector level6 +#endif +#if XCHAL_NUM_INTLEVELS + XCHAL_HAVE_NMI >= 7 vector level7 +#endif vector kernel vector user diff --git a/tests/tco-test.c b/tests/tco-test.c index 9945fb8469..f89a42cdcc 100644 --- a/tests/tco-test.c +++ b/tests/tco-test.c @@ -58,13 +58,13 @@ static void test_init(TestData *d) { QTestState *qs; - qs = qtest_startf("-machine q35 %s %s", - d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", - !d->args ? "" : d->args); + qs = qtest_initf("-machine q35 %s %s", + d->noreboot ? "" : "-global ICH9-LPC.noreboot=false", + !d->args ? "" : d->args); global_qtest = qs; qtest_irq_intercept_in(qs, "ioapic"); - d->bus = qpci_init_pc(qs, NULL); + d->bus = qpci_new_pc(qs, NULL); d->dev = qpci_device_find(d->bus, QPCI_DEVFN(0x1f, 0x00)); g_assert(d->dev != NULL); diff --git a/tests/test-aio-multithread.c b/tests/test-aio-multithread.c index 6440d54ac3..d3144be7e0 100644 --- a/tests/test-aio-multithread.c +++ b/tests/test-aio-multithread.c @@ -178,7 +178,7 @@ static void test_multi_co_schedule(int seconds) } join_aio_contexts(); - g_test_message("scheduled %d, queued %d, retry %d, total %d\n", + g_test_message("scheduled %d, queued %d, retry %d, total %d", count_other, count_here, count_retry, count_here + count_other + count_retry); } @@ -242,7 +242,7 @@ static void test_multi_co_mutex(int threads, int seconds) } join_aio_contexts(); - g_test_message("%d iterations/second\n", counter / seconds); + g_test_message("%d iterations/second", counter / seconds); g_assert_cmpint(counter, ==, atomic_counter); } @@ -361,7 +361,7 @@ static void test_multi_fair_mutex(int threads, int seconds) } join_aio_contexts(); - g_test_message("%d iterations/second\n", counter / seconds); + g_test_message("%d iterations/second", counter / seconds); g_assert_cmpint(counter, ==, atomic_counter); } @@ -417,7 +417,7 @@ static void test_multi_mutex(int threads, int seconds) } join_aio_contexts(); - g_test_message("%d iterations/second\n", counter / seconds); + g_test_message("%d iterations/second", counter / seconds); g_assert_cmpint(counter, ==, atomic_counter); } diff --git a/tests/test-arm-mptimer.c b/tests/test-arm-mptimer.c index cb8f2df914..156a39f50d 100644 --- a/tests/test-arm-mptimer.c +++ b/tests/test-arm-mptimer.c @@ -991,10 +991,25 @@ static void test_timer_zero_load_nonscaled_periodic_to_prescaled_oneshot(void) g_assert_cmpuint(timer_get_and_clr_int_sts(), ==, 0); } +/* + * Add a qtest test that comes in two versions: one with + * a timer scaler setting, and one with the timer nonscaled. + */ +static void add_scaler_test(const char *str, bool scale, + void (*fn)(const void *)) +{ + char *name; + int *scaler = scale ? &scaled : &nonscaled; + + name = g_strdup_printf("%s=%d", str, *scaler); + qtest_add_data_func(name, scaler, fn); + g_free(name); +} + int main(int argc, char **argv) { - int *scaler = &nonscaled; int ret; + int scale; g_test_init(&argc, &argv, NULL); @@ -1012,89 +1027,59 @@ int main(int argc, char **argv) qtest_add_func("mptimer/prescaler", test_timer_prescaler); qtest_add_func("mptimer/prescaler_on_the_fly", test_timer_prescaler_on_the_fly); -tests_with_prescaler_arg: - qtest_add_data_func( - g_strdup_printf("mptimer/oneshot scaler=%d", *scaler), - scaler, test_timer_oneshot); - qtest_add_data_func( - g_strdup_printf("mptimer/pause scaler=%d", *scaler), - scaler, test_timer_pause); - qtest_add_data_func( - g_strdup_printf("mptimer/reload scaler=%d", *scaler), - scaler, test_timer_reload); - qtest_add_data_func( - g_strdup_printf("mptimer/periodic scaler=%d", *scaler), - scaler, test_timer_periodic); - qtest_add_data_func( - g_strdup_printf("mptimer/oneshot_to_periodic scaler=%d", *scaler), - scaler, test_timer_oneshot_to_periodic); - qtest_add_data_func( - g_strdup_printf("mptimer/periodic_to_oneshot scaler=%d", *scaler), - scaler, test_timer_periodic_to_oneshot); - qtest_add_data_func( - g_strdup_printf("mptimer/set_oneshot_counter_to_0 scaler=%d", *scaler), - scaler, test_timer_set_oneshot_counter_to_0); - qtest_add_data_func( - g_strdup_printf("mptimer/set_periodic_counter_to_0 scaler=%d", *scaler), - scaler, test_timer_set_periodic_counter_to_0); - qtest_add_data_func( - g_strdup_printf("mptimer/noload_oneshot scaler=%d", *scaler), - scaler, test_timer_noload_oneshot); - qtest_add_data_func( - g_strdup_printf("mptimer/noload_periodic scaler=%d", *scaler), - scaler, test_timer_noload_periodic); - qtest_add_data_func( - g_strdup_printf("mptimer/zero_load_oneshot scaler=%d", *scaler), - scaler, test_timer_zero_load_oneshot); - qtest_add_data_func( - g_strdup_printf("mptimer/zero_load_periodic scaler=%d", *scaler), - scaler, test_timer_zero_load_periodic); - qtest_add_data_func( - g_strdup_printf("mptimer/zero_load_oneshot_to_nonzero scaler=%d", *scaler), - scaler, test_timer_zero_load_oneshot_to_nonzero); - qtest_add_data_func( - g_strdup_printf("mptimer/zero_load_periodic_to_nonzero scaler=%d", *scaler), - scaler, test_timer_zero_load_periodic_to_nonzero); - qtest_add_data_func( - g_strdup_printf("mptimer/nonzero_load_oneshot_to_zero scaler=%d", *scaler), - scaler, test_timer_nonzero_load_oneshot_to_zero); - qtest_add_data_func( - g_strdup_printf("mptimer/nonzero_load_periodic_to_zero scaler=%d", *scaler), - scaler, test_timer_nonzero_load_periodic_to_zero); - qtest_add_data_func( - g_strdup_printf("mptimer/set_periodic_counter_on_the_fly scaler=%d", *scaler), - scaler, test_timer_set_periodic_counter_on_the_fly); - qtest_add_data_func( - g_strdup_printf("mptimer/enable_and_set_counter scaler=%d", *scaler), - scaler, test_timer_enable_and_set_counter); - qtest_add_data_func( - g_strdup_printf("mptimer/set_counter_and_enable scaler=%d", *scaler), - scaler, test_timer_set_counter_and_enable); - qtest_add_data_func( - g_strdup_printf("mptimer/oneshot_with_counter_0_on_start scaler=%d", *scaler), - scaler, test_timer_oneshot_with_counter_0_on_start); - qtest_add_data_func( - g_strdup_printf("mptimer/periodic_with_counter_0_on_start scaler=%d", *scaler), - scaler, test_timer_periodic_with_counter_0_on_start); - qtest_add_data_func( - g_strdup_printf("mptimer/periodic_counter scaler=%d", *scaler), - scaler, test_periodic_counter); - qtest_add_data_func( - g_strdup_printf("mptimer/set_counter_periodic_with_zero_load scaler=%d", *scaler), - scaler, test_timer_set_counter_periodic_with_zero_load); - qtest_add_data_func( - g_strdup_printf("mptimer/set_oneshot_load_to_0 scaler=%d", *scaler), - scaler, test_timer_set_oneshot_load_to_0); - qtest_add_data_func( - g_strdup_printf("mptimer/set_periodic_load_to_0 scaler=%d", *scaler), - scaler, test_timer_set_periodic_load_to_0); - qtest_add_data_func( - g_strdup_printf("mptimer/zero_load_mode_switch scaler=%d", *scaler), - scaler, test_timer_zero_load_mode_switch); - - if (scaler == &nonscaled) { - scaler = &scaled; - goto tests_with_prescaler_arg; + for (scale = 0; scale < 2; scale++) { + add_scaler_test("mptimer/oneshot scaler", + scale, test_timer_oneshot); + add_scaler_test("mptimer/pause scaler", + scale, test_timer_pause); + add_scaler_test("mptimer/reload scaler", + scale, test_timer_reload); + add_scaler_test("mptimer/periodic scaler", + scale, test_timer_periodic); + add_scaler_test("mptimer/oneshot_to_periodic scaler", + scale, test_timer_oneshot_to_periodic); + add_scaler_test("mptimer/periodic_to_oneshot scaler", + scale, test_timer_periodic_to_oneshot); + add_scaler_test("mptimer/set_oneshot_counter_to_0 scaler", + scale, test_timer_set_oneshot_counter_to_0); + add_scaler_test("mptimer/set_periodic_counter_to_0 scaler", + scale, test_timer_set_periodic_counter_to_0); + add_scaler_test("mptimer/noload_oneshot scaler", + scale, test_timer_noload_oneshot); + add_scaler_test("mptimer/noload_periodic scaler", + scale, test_timer_noload_periodic); + add_scaler_test("mptimer/zero_load_oneshot scaler", + scale, test_timer_zero_load_oneshot); + add_scaler_test("mptimer/zero_load_periodic scaler", + scale, test_timer_zero_load_periodic); + add_scaler_test("mptimer/zero_load_oneshot_to_nonzero scaler", + scale, test_timer_zero_load_oneshot_to_nonzero); + add_scaler_test("mptimer/zero_load_periodic_to_nonzero scaler", + scale, test_timer_zero_load_periodic_to_nonzero); + add_scaler_test("mptimer/nonzero_load_oneshot_to_zero scaler", + scale, test_timer_nonzero_load_oneshot_to_zero); + add_scaler_test("mptimer/nonzero_load_periodic_to_zero scaler", + scale, test_timer_nonzero_load_periodic_to_zero); + add_scaler_test("mptimer/set_periodic_counter_on_the_fly scaler", + scale, test_timer_set_periodic_counter_on_the_fly); + add_scaler_test("mptimer/enable_and_set_counter scaler", + scale, test_timer_enable_and_set_counter); + add_scaler_test("mptimer/set_counter_and_enable scaler", + scale, test_timer_set_counter_and_enable); + add_scaler_test("mptimer/oneshot_with_counter_0_on_start scaler", + scale, test_timer_oneshot_with_counter_0_on_start); + add_scaler_test("mptimer/periodic_with_counter_0_on_start scaler", + scale, test_timer_periodic_with_counter_0_on_start); + add_scaler_test("mptimer/periodic_counter scaler", + scale, test_periodic_counter); + add_scaler_test("mptimer/set_counter_periodic_with_zero_load scaler", + scale, test_timer_set_counter_periodic_with_zero_load); + add_scaler_test("mptimer/set_oneshot_load_to_0 scaler", + scale, test_timer_set_oneshot_load_to_0); + add_scaler_test("mptimer/set_periodic_load_to_0 scaler", + scale, test_timer_set_periodic_load_to_0); + add_scaler_test("mptimer/zero_load_mode_switch scaler", + scale, test_timer_zero_load_mode_switch); } qtest_start("-machine vexpress-a9"); diff --git a/tests/test-authz-list.c b/tests/test-authz-list.c new file mode 100644 index 0000000000..24347a6ac3 --- /dev/null +++ b/tests/test-authz-list.c @@ -0,0 +1,159 @@ +/* + * QEMU list file authorization object tests + * + * Copyright (c) 2018 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 "authz/list.h" + +static void test_authz_default_deny(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_default_allow(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_deny(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_allow(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +static void test_authz_complex(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); + + qauthz_list_append_rule(auth, "fred", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "bob", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "dan", QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, &error_abort); + qauthz_list_append_rule(auth, "dan*", QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, &error_abort); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_add_remove(void) +{ + QAuthZList *auth = qauthz_list_new("auth0", + QAUTHZ_LIST_POLICY_ALLOW, + &error_abort); + + g_assert_cmpint(qauthz_list_append_rule(auth, "fred", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 0); + g_assert_cmpint(qauthz_list_append_rule(auth, "bob", + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 1); + g_assert_cmpint(qauthz_list_append_rule(auth, "dan", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 2); + g_assert_cmpint(qauthz_list_append_rule(auth, "frank", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + &error_abort), + ==, 3); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + g_assert_cmpint(qauthz_list_delete_rule(auth, "dan"), + ==, 2); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + g_assert_cmpint(qauthz_list_insert_rule(auth, "dan", + QAUTHZ_LIST_POLICY_DENY, + QAUTHZ_LIST_FORMAT_EXACT, + 2, + &error_abort), + ==, 2); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/auth/list/default/deny", test_authz_default_deny); + g_test_add_func("/auth/list/default/allow", test_authz_default_allow); + g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); + g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow); + g_test_add_func("/auth/list/complex", test_authz_complex); + g_test_add_func("/auth/list/add-remove", test_authz_add_remove); + + return g_test_run(); +} diff --git a/tests/test-authz-listfile.c b/tests/test-authz-listfile.c new file mode 100644 index 0000000000..1e452fef6d --- /dev/null +++ b/tests/test-authz-listfile.c @@ -0,0 +1,195 @@ +/* + * QEMU list authorization object tests + * + * Copyright (c) 2018 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/main-loop.h" +#include "authz/listfile.h" + +static char *workdir; + +static gchar *qemu_authz_listfile_test_save(const gchar *name, + const gchar *cfg) +{ + gchar *path = g_strdup_printf("%s/default-deny.cfg", workdir); + GError *gerr = NULL; + + if (!g_file_set_contents(path, cfg, -1, &gerr)) { + g_printerr("Unable to save config %s: %s\n", + path, gerr->message); + g_error_free(gerr); + g_free(path); + rmdir(workdir); + abort(); + } + + return path; +} + +static void test_authz_default_deny(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "default-deny.cfg", + "{ \"policy\": \"deny\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_default_allow(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "default-allow.cfg", + "{ \"policy\": \"allow\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_deny(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "explicit-deny.cfg", + "{ \"rules\": [ " + " { \"match\": \"fred\"," + " \"policy\": \"deny\"," + " \"format\": \"exact\" } ]," + " \"policy\": \"allow\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + +static void test_authz_explicit_allow(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "explicit-allow.cfg", + "{ \"rules\": [ " + " { \"match\": \"fred\"," + " \"policy\": \"allow\"," + " \"format\": \"exact\" } ]," + " \"policy\": \"deny\" }"); + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +static void test_authz_complex(void) +{ + gchar *file = qemu_authz_listfile_test_save( + "complex.cfg", + "{ \"rules\": [ " + " { \"match\": \"fred\"," + " \"policy\": \"allow\"," + " \"format\": \"exact\" }," + " { \"match\": \"bob\"," + " \"policy\": \"allow\"," + " \"format\": \"exact\" }," + " { \"match\": \"dan\"," + " \"policy\": \"deny\"," + " \"format\": \"exact\" }," + " { \"match\": \"dan*\"," + " \"policy\": \"allow\"," + " \"format\": \"glob\" } ]," + " \"policy\": \"deny\" }"); + + Error *local_err = NULL; + + QAuthZListFile *auth = qauthz_list_file_new("auth0", + file, false, + &local_err); + unlink(file); + g_free(file); + g_assert(local_err == NULL); + + g_assert(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "bob", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(auth), "dan", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(auth), "danb", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +int main(int argc, char **argv) +{ + int ret; + GError *gerr = NULL; + + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + + workdir = g_dir_make_tmp("qemu-test-authz-listfile-XXXXXX", + &gerr); + if (!workdir) { + g_printerr("Unable to create temporary dir: %s\n", + gerr->message); + g_error_free(gerr); + abort(); + } + + g_test_add_func("/auth/list/default/deny", test_authz_default_deny); + g_test_add_func("/auth/list/default/allow", test_authz_default_allow); + g_test_add_func("/auth/list/explicit/deny", test_authz_explicit_deny); + g_test_add_func("/auth/list/explicit/allow", test_authz_explicit_allow); + g_test_add_func("/auth/list/complex", test_authz_complex); + + ret = g_test_run(); + + rmdir(workdir); + g_free(workdir); + + return ret; +} diff --git a/tests/test-authz-pam.c b/tests/test-authz-pam.c new file mode 100644 index 0000000000..93d5ac8bbf --- /dev/null +++ b/tests/test-authz-pam.c @@ -0,0 +1,124 @@ +/* + * QEMU PAM authorization object tests + * + * Copyright (c) 2018 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 "authz/pamacct.h" + +#include + +static bool failauth; + +/* + * These two functions are exported by libpam.so. + * + * By defining them again here, our impls are resolved + * by the linker instead of those in libpam.so + * + * The test suite is thus isolated from the host system + * PAM setup, so we can do predictable test scenarios + */ +int +pam_start(const char *service_name, const char *user, + const struct pam_conv *pam_conversation, + pam_handle_t **pamh) +{ + failauth = true; + if (!g_str_equal(service_name, "qemu-vnc")) { + return PAM_AUTH_ERR; + } + + if (g_str_equal(user, "fred")) { + failauth = false; + } + + return PAM_SUCCESS; +} + + +int +pam_acct_mgmt(pam_handle_t *pamh, int flags) +{ + if (failauth) { + return PAM_AUTH_ERR; + } + + return PAM_SUCCESS; +} + + +static void test_authz_unknown_service(void) +{ + Error *local_err = NULL; + QAuthZPAM *auth = qauthz_pam_new("auth0", + "qemu-does-not-exist", + &error_abort); + + g_assert_nonnull(auth); + + g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "fred", &local_err)); + + error_free_or_abort(&local_err); + object_unparent(OBJECT(auth)); +} + + +static void test_authz_good_user(void) +{ + QAuthZPAM *auth = qauthz_pam_new("auth0", + "qemu-vnc", + &error_abort); + + g_assert_nonnull(auth); + + g_assert_true(qauthz_is_allowed(QAUTHZ(auth), "fred", &error_abort)); + + object_unparent(OBJECT(auth)); +} + + +static void test_authz_bad_user(void) +{ + Error *local_err = NULL; + QAuthZPAM *auth = qauthz_pam_new("auth0", + "qemu-vnc", + &error_abort); + + g_assert_nonnull(auth); + + g_assert_false(qauthz_is_allowed(QAUTHZ(auth), "bob", &local_err)); + + error_free_or_abort(&local_err); + object_unparent(OBJECT(auth)); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/auth/pam/unknown-service", test_authz_unknown_service); + g_test_add_func("/auth/pam/good-user", test_authz_good_user); + g_test_add_func("/auth/pam/bad-user", test_authz_bad_user); + + return g_test_run(); +} diff --git a/tests/test-authz-simple.c b/tests/test-authz-simple.c new file mode 100644 index 0000000000..2cf14fb87e --- /dev/null +++ b/tests/test-authz-simple.c @@ -0,0 +1,50 @@ +/* + * QEMU simple authorization object testing + * + * Copyright (c) 2018 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 "authz/simple.h" + + +static void test_authz_simple(void) +{ + QAuthZSimple *authz = qauthz_simple_new("authz0", + "cthulu", + &error_abort); + + g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthul", &error_abort)); + g_assert(qauthz_is_allowed(QAUTHZ(authz), "cthulu", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(authz), "cthuluu", &error_abort)); + g_assert(!qauthz_is_allowed(QAUTHZ(authz), "fred", &error_abort)); + + object_unparent(OBJECT(authz)); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + module_call_init(MODULE_INIT_QOM); + + g_test_add_func("/authz/simple", test_authz_simple); + + return g_test_run(); +} diff --git a/tests/test-bdrv-drain.c b/tests/test-bdrv-drain.c index 17bb8508ae..eda90750eb 100644 --- a/tests/test-bdrv-drain.c +++ b/tests/test-bdrv-drain.c @@ -174,6 +174,28 @@ static void do_drain_end(enum drain_type drain_type, BlockDriverState *bs) } } +static void do_drain_begin_unlocked(enum drain_type drain_type, BlockDriverState *bs) +{ + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(bdrv_get_aio_context(bs)); + } + do_drain_begin(drain_type, bs); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(bdrv_get_aio_context(bs)); + } +} + +static void do_drain_end_unlocked(enum drain_type drain_type, BlockDriverState *bs) +{ + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_acquire(bdrv_get_aio_context(bs)); + } + do_drain_end(drain_type, bs); + if (drain_type != BDRV_DRAIN_ALL) { + aio_context_release(bdrv_get_aio_context(bs)); + } +} + static void test_drv_cb_common(enum drain_type drain_type, bool recursive) { BlockBackend *blk; @@ -182,12 +204,7 @@ static void test_drv_cb_common(enum drain_type drain_type, bool recursive) BlockAIOCB *acb; int aio_ret; - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = NULL, - .iov_len = 0, - }; - qemu_iovec_init_external(&qiov, &iov, 1); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, @@ -614,6 +631,17 @@ static void test_iothread_aio_cb(void *opaque, int ret) qemu_event_set(&done_event); } +static void test_iothread_main_thread_bh(void *opaque) +{ + struct test_iothread_data *data = opaque; + + /* Test that the AioContext is not yet locked in a random BH that is + * executed during drain, otherwise this would deadlock. */ + aio_context_acquire(bdrv_get_aio_context(data->bs)); + bdrv_flush(data->bs); + aio_context_release(bdrv_get_aio_context(data->bs)); +} + /* * Starts an AIO request on a BDS that runs in the AioContext of iothread 1. * The request involves a BH on iothread 2 before it can complete. @@ -637,12 +665,7 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) AioContext *ctx_a = iothread_get_aio_context(a); AioContext *ctx_b = iothread_get_aio_context(b); - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = NULL, - .iov_len = 0, - }; - qemu_iovec_init_external(&qiov, &iov, 1); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); /* bdrv_drain_all() may only be called from the main loop thread */ if (drain_type == BDRV_DRAIN_ALL && drain_thread != 0) { @@ -661,6 +684,8 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) s->bh_indirection_ctx = ctx_b; aio_ret = -EINPROGRESS; + qemu_event_reset(&done_event); + if (drain_thread == 0) { acb = blk_aio_preadv(blk, 0, &qiov, 0, test_iothread_aio_cb, &aio_ret); } else { @@ -683,12 +708,13 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) aio_context_acquire(ctx_a); } + aio_bh_schedule_oneshot(ctx_a, test_iothread_main_thread_bh, &data); + /* The request is running on the IOThread a. Draining its block device * will make sure that it has completed as far as the BDS is concerned, * but the drain in this thread can continue immediately after * bdrv_dec_in_flight() and aio_ret might be assigned only slightly * later. */ - qemu_event_reset(&done_event); do_drain_begin(drain_type, bs); g_assert_cmpint(bs->in_flight, ==, 0); @@ -708,7 +734,6 @@ static void test_iothread_common(enum drain_type drain_type, int drain_thread) } break; case 1: - qemu_event_reset(&done_event); aio_bh_schedule_oneshot(ctx_a, test_iothread_drain_entry, &data); qemu_event_wait(&done_event); break; @@ -749,28 +774,56 @@ static void test_iothread_drain_subtree(void) typedef struct TestBlockJob { BlockJob common; + int run_ret; + int prepare_ret; + bool running; bool should_complete; } TestBlockJob; -static void test_job_completed(Job *job, void *opaque) +static int test_job_prepare(Job *job) { - job_completed(job, 0, NULL); + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); + return s->prepare_ret; } -static void coroutine_fn test_job_start(void *opaque) +static void test_job_commit(Job *job) { - TestBlockJob *s = opaque; + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); +} + +static void test_job_abort(Job *job) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* Provoke an AIO_WAIT_WHILE() call to verify there is no deadlock */ + blk_flush(s->common.blk); +} + +static int coroutine_fn test_job_run(Job *job, Error **errp) +{ + TestBlockJob *s = container_of(job, TestBlockJob, common.job); + + /* We are running the actual job code past the pause point in + * job_co_entry(). */ + s->running = true; job_transition_to_ready(&s->common.job); while (!s->should_complete) { - /* Avoid block_job_sleep_ns() because it marks the job as !busy. We - * want to emulate some actual activity (probably some I/O) here so - * that drain has to wait for this acitivity to stop. */ - qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 100000); + /* Avoid job_sleep_ns() because it marks the job as !busy. We want to + * emulate some actual activity (probably some I/O) here so that drain + * has to wait for this activity to stop. */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + job_pause_point(&s->common.job); } - job_defer_to_main_loop(&s->common.job, test_job_completed, NULL); + return s->run_ret; } static void test_job_complete(Job *job, Error **errp) @@ -785,38 +838,117 @@ BlockJobDriver test_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = test_job_start, + .run = test_job_run, .complete = test_job_complete, + .prepare = test_job_prepare, + .commit = test_job_commit, + .abort = test_job_abort, }, }; -static void test_blockjob_common(enum drain_type drain_type) +enum test_job_result { + TEST_JOB_SUCCESS, + TEST_JOB_FAIL_RUN, + TEST_JOB_FAIL_PREPARE, +}; + +enum test_job_drain_node { + TEST_JOB_DRAIN_SRC, + TEST_JOB_DRAIN_SRC_CHILD, + TEST_JOB_DRAIN_SRC_PARENT, +}; + +static void test_blockjob_common_drain_node(enum drain_type drain_type, + bool use_iothread, + enum test_job_result result, + enum test_job_drain_node drain_node) { BlockBackend *blk_src, *blk_target; - BlockDriverState *src, *target; + BlockDriverState *src, *src_backing, *src_overlay, *target, *drain_bs; BlockJob *job; + TestBlockJob *tjob; + IOThread *iothread = NULL; + AioContext *ctx; int ret; src = bdrv_new_open_driver(&bdrv_test, "source", BDRV_O_RDWR, &error_abort); + src_backing = bdrv_new_open_driver(&bdrv_test, "source-backing", + BDRV_O_RDWR, &error_abort); + src_overlay = bdrv_new_open_driver(&bdrv_test, "source-overlay", + BDRV_O_RDWR, &error_abort); + + bdrv_set_backing_hd(src_overlay, src, &error_abort); + bdrv_unref(src); + bdrv_set_backing_hd(src, src_backing, &error_abort); + bdrv_unref(src_backing); + blk_src = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); - blk_insert_bs(blk_src, src, &error_abort); + blk_insert_bs(blk_src, src_overlay, &error_abort); + + switch (drain_node) { + case TEST_JOB_DRAIN_SRC: + drain_bs = src; + break; + case TEST_JOB_DRAIN_SRC_CHILD: + drain_bs = src_backing; + break; + case TEST_JOB_DRAIN_SRC_PARENT: + drain_bs = src_overlay; + break; + default: + g_assert_not_reached(); + } + + if (use_iothread) { + iothread = iothread_new(); + ctx = iothread_get_aio_context(iothread); + blk_set_aio_context(blk_src, ctx); + } else { + ctx = qemu_get_aio_context(); + } target = bdrv_new_open_driver(&bdrv_test, "target", BDRV_O_RDWR, &error_abort); blk_target = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk_target, target, &error_abort); - job = block_job_create("job0", &test_job_driver, NULL, src, 0, BLK_PERM_ALL, - 0, 0, NULL, NULL, &error_abort); + aio_context_acquire(ctx); + tjob = block_job_create("job0", &test_job_driver, NULL, src, + 0, BLK_PERM_ALL, + 0, 0, NULL, NULL, &error_abort); + job = &tjob->common; block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); + + switch (result) { + case TEST_JOB_SUCCESS: + break; + case TEST_JOB_FAIL_RUN: + tjob->run_ret = -EIO; + break; + case TEST_JOB_FAIL_PREPARE: + tjob->prepare_ret = -EIO; + break; + } + job_start(&job->job); + aio_context_release(ctx); + + if (use_iothread) { + /* job_co_entry() is run in the I/O thread, wait for the actual job + * code to start (we don't want to catch the job in the pause point in + * job_co_entry(). */ + while (!tjob->running) { + aio_poll(qemu_get_aio_context(), false); + } + } g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ + g_assert_true(tjob->running); + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ - do_drain_begin(drain_type, src); + do_drain_begin_unlocked(drain_type, drain_bs); if (drain_type == BDRV_DRAIN_ALL) { /* bdrv_drain_all() drains both src and target */ @@ -827,7 +959,14 @@ static void test_blockjob_common(enum drain_type drain_type) g_assert_true(job->job.paused); g_assert_false(job->job.busy); /* The job is paused */ - do_drain_end(drain_type, src); + do_drain_end_unlocked(drain_type, drain_bs); + + if (use_iothread) { + /* paused is reset in the I/O thread, wait for it */ + while (job->job.paused) { + aio_poll(qemu_get_aio_context(), false); + } + } g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); @@ -846,32 +985,113 @@ static void test_blockjob_common(enum drain_type drain_type) do_drain_end(drain_type, target); + if (use_iothread) { + /* paused is reset in the I/O thread, wait for it */ + while (job->job.paused) { + aio_poll(qemu_get_aio_context(), false); + } + } + g_assert_cmpint(job->job.pause_count, ==, 0); g_assert_false(job->job.paused); - g_assert_true(job->job.busy); /* We're in job_sleep_ns() */ + g_assert_true(job->job.busy); /* We're in qemu_co_sleep_ns() */ + aio_context_acquire(ctx); ret = job_complete_sync(&job->job, &error_abort); - g_assert_cmpint(ret, ==, 0); + g_assert_cmpint(ret, ==, (result == TEST_JOB_SUCCESS ? 0 : -EIO)); + + if (use_iothread) { + blk_set_aio_context(blk_src, qemu_get_aio_context()); + } + aio_context_release(ctx); blk_unref(blk_src); blk_unref(blk_target); - bdrv_unref(src); + bdrv_unref(src_overlay); bdrv_unref(target); + + if (iothread) { + iothread_join(iothread); + } +} + +static void test_blockjob_common(enum drain_type drain_type, bool use_iothread, + enum test_job_result result) +{ + test_blockjob_common_drain_node(drain_type, use_iothread, result, + TEST_JOB_DRAIN_SRC); + test_blockjob_common_drain_node(drain_type, use_iothread, result, + TEST_JOB_DRAIN_SRC_CHILD); + if (drain_type == BDRV_SUBTREE_DRAIN) { + test_blockjob_common_drain_node(drain_type, use_iothread, result, + TEST_JOB_DRAIN_SRC_PARENT); + } } static void test_blockjob_drain_all(void) { - test_blockjob_common(BDRV_DRAIN_ALL); + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_SUCCESS); } static void test_blockjob_drain(void) { - test_blockjob_common(BDRV_DRAIN); + test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_SUCCESS); } static void test_blockjob_drain_subtree(void) { - test_blockjob_common(BDRV_SUBTREE_DRAIN); + test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_SUCCESS); +} + +static void test_blockjob_error_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN_ALL, false, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_error_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN, false, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_error_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_SUBTREE_DRAIN, false, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_iothread_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_SUCCESS); +} + +static void test_blockjob_iothread_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_SUCCESS); +} + +static void test_blockjob_iothread_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_SUCCESS); +} + +static void test_blockjob_iothread_error_drain_all(void) +{ + test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN_ALL, true, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_iothread_error_drain(void) +{ + test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_DRAIN, true, TEST_JOB_FAIL_PREPARE); +} + +static void test_blockjob_iothread_error_drain_subtree(void) +{ + test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_RUN); + test_blockjob_common(BDRV_SUBTREE_DRAIN, true, TEST_JOB_FAIL_PREPARE); } @@ -918,13 +1138,7 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) BlockDriverState *bs = blk_bs(blk); BDRVTestTopState *tts = bs->opaque; void *buffer = g_malloc(65536); - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = buffer, - .iov_len = 65536, - }; - - qemu_iovec_init_external(&qiov, &iov, 1); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, buffer, 65536); /* Pretend some internal write operation from parent to child. * Important: We have to read from the child, not from the parent! @@ -948,6 +1162,7 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) } dbdd->done = true; + g_free(buffer); } /** @@ -1134,12 +1349,7 @@ static void test_detach_indirect(bool by_parent_cb) BdrvChild *child_a, *child_b; BlockAIOCB *acb; - QEMUIOVector qiov; - struct iovec iov = { - .iov_base = NULL, - .iov_len = 0, - }; - qemu_iovec_init_external(&qiov, &iov, 1); + QEMUIOVector qiov = QEMU_IOVEC_INIT_BUF(qiov, NULL, 0); if (!by_parent_cb) { detach_by_driver_cb_role = child_file; @@ -1291,6 +1501,36 @@ static void test_append_to_drained(void) blk_unref(blk); } +static void test_set_aio_context(void) +{ + BlockDriverState *bs; + IOThread *a = iothread_new(); + IOThread *b = iothread_new(); + AioContext *ctx_a = iothread_get_aio_context(a); + AioContext *ctx_b = iothread_get_aio_context(b); + + bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, + &error_abort); + + bdrv_drained_begin(bs); + bdrv_set_aio_context(bs, ctx_a); + + aio_context_acquire(ctx_a); + bdrv_drained_end(bs); + + bdrv_drained_begin(bs); + bdrv_set_aio_context(bs, ctx_b); + aio_context_release(ctx_a); + aio_context_acquire(ctx_b); + bdrv_set_aio_context(bs, qemu_get_aio_context()); + aio_context_release(ctx_b); + bdrv_drained_end(bs); + + bdrv_unref(bs); + iothread_join(a); + iothread_join(b); +} + int main(int argc, char **argv) { int ret; @@ -1342,6 +1582,27 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/blockjob/drain_subtree", test_blockjob_drain_subtree); + g_test_add_func("/bdrv-drain/blockjob/error/drain_all", + test_blockjob_error_drain_all); + g_test_add_func("/bdrv-drain/blockjob/error/drain", + test_blockjob_error_drain); + g_test_add_func("/bdrv-drain/blockjob/error/drain_subtree", + test_blockjob_error_drain_subtree); + + g_test_add_func("/bdrv-drain/blockjob/iothread/drain_all", + test_blockjob_iothread_drain_all); + g_test_add_func("/bdrv-drain/blockjob/iothread/drain", + test_blockjob_iothread_drain); + g_test_add_func("/bdrv-drain/blockjob/iothread/drain_subtree", + test_blockjob_iothread_drain_subtree); + + g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_all", + test_blockjob_iothread_error_drain_all); + g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain", + test_blockjob_iothread_error_drain); + g_test_add_func("/bdrv-drain/blockjob/iothread/error/drain_subtree", + test_blockjob_iothread_error_drain_subtree); + g_test_add_func("/bdrv-drain/deletion/drain", test_delete_by_drain); g_test_add_func("/bdrv-drain/detach/drain_all", test_detach_by_drain_all); g_test_add_func("/bdrv-drain/detach/drain", test_detach_by_drain); @@ -1351,6 +1612,8 @@ int main(int argc, char **argv) g_test_add_func("/bdrv-drain/attach/drain", test_append_to_drained); + g_test_add_func("/bdrv-drain/set_aio_context", test_set_aio_context); + ret = g_test_run(); qemu_event_destroy(&done_event); return ret; diff --git a/tests/test-bdrv-graph-mod.c b/tests/test-bdrv-graph-mod.c new file mode 100644 index 0000000000..283dc84869 --- /dev/null +++ b/tests/test-bdrv-graph-mod.c @@ -0,0 +1,199 @@ +/* + * Block node graph modifications tests + * + * Copyright (c) 2019 Virtuozzo International GmbH. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "block/block_int.h" +#include "sysemu/block-backend.h" + +static BlockDriver bdrv_pass_through = { + .format_name = "pass-through", + .bdrv_child_perm = bdrv_filter_default_perms, +}; + +static void no_perm_default_perms(BlockDriverState *bs, BdrvChild *c, + const BdrvChildRole *role, + BlockReopenQueue *reopen_queue, + uint64_t perm, uint64_t shared, + uint64_t *nperm, uint64_t *nshared) +{ + *nperm = 0; + *nshared = BLK_PERM_ALL; +} + +static BlockDriver bdrv_no_perm = { + .format_name = "no-perm", + .bdrv_child_perm = no_perm_default_perms, +}; + +static BlockDriverState *no_perm_node(const char *name) +{ + return bdrv_new_open_driver(&bdrv_no_perm, name, BDRV_O_RDWR, &error_abort); +} + +static BlockDriverState *pass_through_node(const char *name) +{ + return bdrv_new_open_driver(&bdrv_pass_through, name, + BDRV_O_RDWR, &error_abort); +} + +/* + * test_update_perm_tree + * + * When checking node for a possibility to update permissions, it's subtree + * should be correctly checked too. New permissions for each node should be + * calculated and checked in context of permissions of other nodes. If we + * check new permissions of the node only in context of old permissions of + * its neighbors, we can finish up with wrong permission graph. + * + * This test firstly create the following graph: + * +--------+ + * | root | + * +--------+ + * | + * | perm: write, read + * | shared: except write + * v + * +-------------------+ +----------------+ + * | passtrough filter |---------->| null-co node | + * +-------------------+ +----------------+ + * + * + * and then, tries to append filter under node. Expected behavior: fail. + * Otherwise we'll get the following picture, with two BdrvChild'ren, having + * write permission to one node, without actually sharing it. + * + * +--------+ + * | root | + * +--------+ + * | + * | perm: write, read + * | shared: except write + * v + * +-------------------+ + * | passtrough filter | + * +-------------------+ + * | | + * perm: write, read | | perm: write, read + * shared: except write | | shared: except write + * v v + * +----------------+ + * | null co node | + * +----------------+ + */ +static void test_update_perm_tree(void) +{ + Error *local_err = NULL; + + BlockBackend *root = blk_new(BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, + BLK_PERM_ALL & ~BLK_PERM_WRITE); + BlockDriverState *bs = no_perm_node("node"); + BlockDriverState *filter = pass_through_node("filter"); + + blk_insert_bs(root, bs, &error_abort); + + bdrv_attach_child(filter, bs, "child", &child_file, &error_abort); + + bdrv_append(filter, bs, &local_err); + + g_assert_nonnull(local_err); + error_free(local_err); + + bdrv_unref(bs); + blk_unref(root); +} + +/* + * test_should_update_child + * + * Test that bdrv_replace_node, and concretely should_update_child + * do the right thing, i.e. not creating loops on the graph. + * + * The test does the following: + * 1. initial graph: + * + * +------+ +--------+ + * | root | | filter | + * +------+ +--------+ + * | | + * root| target| + * v v + * +------+ +--------+ + * | node |<---------| target | + * +------+ backing +--------+ + * + * 2. Append @filter above @node. If should_update_child works correctly, + * it understands, that backing child of @target should not be updated, + * as it will create a loop on node graph. Resulting picture should + * be the left one, not the right: + * + * +------+ +------+ + * | root | | root | + * +------+ +------+ + * | | + * root| root| + * v v + * +--------+ target +--------+ target + * | filter |--------------+ | filter |--------------+ + * +--------+ | +--------+ | + * | | | ^ v + * backing| | backing| | +--------+ + * v v | +-----------| target | + * +------+ +--------+ v backing +--------+ + * | node |<---------| target | +------+ + * +------+ backing +--------+ | node | + * +------+ + * + * (good picture) (bad picture) + * + */ +static void test_should_update_child(void) +{ + BlockBackend *root = blk_new(0, BLK_PERM_ALL); + BlockDriverState *bs = no_perm_node("node"); + BlockDriverState *filter = no_perm_node("filter"); + BlockDriverState *target = no_perm_node("target"); + + blk_insert_bs(root, bs, &error_abort); + + bdrv_set_backing_hd(target, bs, &error_abort); + + g_assert(target->backing->bs == bs); + bdrv_attach_child(filter, target, "target", &child_file, &error_abort); + bdrv_append(filter, bs, &error_abort); + g_assert(target->backing->bs == bs); + + bdrv_unref(bs); + blk_unref(root); +} + +int main(int argc, char *argv[]) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/bdrv-graph-mod/update-perm-tree", test_update_perm_tree); + g_test_add_func("/bdrv-graph-mod/should-update-child", + test_should_update_child); + + return g_test_run(); +} diff --git a/tests/test-block-iothread.c b/tests/test-block-iothread.c new file mode 100644 index 0000000000..97ac0b159d --- /dev/null +++ b/tests/test-block-iothread.c @@ -0,0 +1,372 @@ +/* + * Block tests for iothreads + * + * Copyright (c) 2018 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 "block/block.h" +#include "block/blockjob_int.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "iothread.h" + +static int coroutine_fn bdrv_test_co_prwv(BlockDriverState *bs, + uint64_t offset, uint64_t bytes, + QEMUIOVector *qiov, int flags) +{ + return 0; +} + +static int coroutine_fn bdrv_test_co_pdiscard(BlockDriverState *bs, + int64_t offset, int bytes) +{ + return 0; +} + +static int coroutine_fn +bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, + PreallocMode prealloc, Error **errp) +{ + return 0; +} + +static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs, + bool want_zero, + int64_t offset, int64_t count, + int64_t *pnum, int64_t *map, + BlockDriverState **file) +{ + *pnum = count; + return 0; +} + +static BlockDriver bdrv_test = { + .format_name = "test", + .instance_size = 1, + + .bdrv_co_preadv = bdrv_test_co_prwv, + .bdrv_co_pwritev = bdrv_test_co_prwv, + .bdrv_co_pdiscard = bdrv_test_co_pdiscard, + .bdrv_co_truncate = bdrv_test_co_truncate, + .bdrv_co_block_status = bdrv_test_co_block_status, +}; + +static void test_sync_op_pread(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = bdrv_pread(c, 0, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = bdrv_pread(c, -2, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_pwrite(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = bdrv_pwrite(c, 0, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = bdrv_pwrite(c, -2, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pread(BlockBackend *blk) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = blk_pread(blk, 0, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = blk_pread(blk, -2, buf, sizeof(buf)); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pwrite(BlockBackend *blk) +{ + uint8_t buf[512]; + int ret; + + /* Success */ + ret = blk_pwrite(blk, 0, buf, sizeof(buf), 0); + g_assert_cmpint(ret, ==, 512); + + /* Early error: Negative offset */ + ret = blk_pwrite(blk, -2, buf, sizeof(buf), 0); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_load_vmstate(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Error: Driver does not support snapshots */ + ret = bdrv_load_vmstate(c->bs, buf, 0, sizeof(buf)); + g_assert_cmpint(ret, ==, -ENOTSUP); +} + +static void test_sync_op_save_vmstate(BdrvChild *c) +{ + uint8_t buf[512]; + int ret; + + /* Error: Driver does not support snapshots */ + ret = bdrv_save_vmstate(c->bs, buf, 0, sizeof(buf)); + g_assert_cmpint(ret, ==, -ENOTSUP); +} + +static void test_sync_op_pdiscard(BdrvChild *c) +{ + int ret; + + /* Normal success path */ + c->bs->open_flags |= BDRV_O_UNMAP; + ret = bdrv_pdiscard(c, 0, 512); + g_assert_cmpint(ret, ==, 0); + + /* Early success: UNMAP not supported */ + c->bs->open_flags &= ~BDRV_O_UNMAP; + ret = bdrv_pdiscard(c, 0, 512); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = bdrv_pdiscard(c, -2, 512); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_blk_pdiscard(BlockBackend *blk) +{ + int ret; + + /* Early success: UNMAP not supported */ + ret = blk_pdiscard(blk, 0, 512); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = blk_pdiscard(blk, -2, 512); + g_assert_cmpint(ret, ==, -EIO); +} + +static void test_sync_op_truncate(BdrvChild *c) +{ + int ret; + + /* Normal success path */ + ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL); + g_assert_cmpint(ret, ==, 0); + + /* Early error: Negative offset */ + ret = bdrv_truncate(c, -2, PREALLOC_MODE_OFF, NULL); + g_assert_cmpint(ret, ==, -EINVAL); + + /* Error: Read-only image */ + c->bs->read_only = true; + c->bs->open_flags &= ~BDRV_O_RDWR; + + ret = bdrv_truncate(c, 65536, PREALLOC_MODE_OFF, NULL); + g_assert_cmpint(ret, ==, -EACCES); + + c->bs->read_only = false; + c->bs->open_flags |= BDRV_O_RDWR; +} + +static void test_sync_op_block_status(BdrvChild *c) +{ + int ret; + int64_t n; + + /* Normal success path */ + ret = bdrv_is_allocated(c->bs, 0, 65536, &n); + g_assert_cmpint(ret, ==, 0); + + /* Early success: No driver support */ + bdrv_test.bdrv_co_block_status = NULL; + ret = bdrv_is_allocated(c->bs, 0, 65536, &n); + g_assert_cmpint(ret, ==, 1); + + /* Early success: bytes = 0 */ + ret = bdrv_is_allocated(c->bs, 0, 0, &n); + g_assert_cmpint(ret, ==, 0); + + /* Early success: Offset > image size*/ + ret = bdrv_is_allocated(c->bs, 0x1000000, 0x1000000, &n); + g_assert_cmpint(ret, ==, 0); +} + +static void test_sync_op_flush(BdrvChild *c) +{ + int ret; + + /* Normal success path */ + ret = bdrv_flush(c->bs); + g_assert_cmpint(ret, ==, 0); + + /* Early success: Read-only image */ + c->bs->read_only = true; + c->bs->open_flags &= ~BDRV_O_RDWR; + + ret = bdrv_flush(c->bs); + g_assert_cmpint(ret, ==, 0); + + c->bs->read_only = false; + c->bs->open_flags |= BDRV_O_RDWR; +} + +static void test_sync_op_blk_flush(BlockBackend *blk) +{ + BlockDriverState *bs = blk_bs(blk); + int ret; + + /* Normal success path */ + ret = blk_flush(blk); + g_assert_cmpint(ret, ==, 0); + + /* Early success: Read-only image */ + bs->read_only = true; + bs->open_flags &= ~BDRV_O_RDWR; + + ret = blk_flush(blk); + g_assert_cmpint(ret, ==, 0); + + bs->read_only = false; + bs->open_flags |= BDRV_O_RDWR; +} + +static void test_sync_op_check(BdrvChild *c) +{ + BdrvCheckResult result; + int ret; + + /* Error: Driver does not implement check */ + ret = bdrv_check(c->bs, &result, 0); + g_assert_cmpint(ret, ==, -ENOTSUP); +} + +static void test_sync_op_invalidate_cache(BdrvChild *c) +{ + /* Early success: Image is not inactive */ + bdrv_invalidate_cache(c->bs, NULL); +} + + +typedef struct SyncOpTest { + const char *name; + void (*fn)(BdrvChild *c); + void (*blkfn)(BlockBackend *blk); +} SyncOpTest; + +const SyncOpTest sync_op_tests[] = { + { + .name = "/sync-op/pread", + .fn = test_sync_op_pread, + .blkfn = test_sync_op_blk_pread, + }, { + .name = "/sync-op/pwrite", + .fn = test_sync_op_pwrite, + .blkfn = test_sync_op_blk_pwrite, + }, { + .name = "/sync-op/load_vmstate", + .fn = test_sync_op_load_vmstate, + }, { + .name = "/sync-op/save_vmstate", + .fn = test_sync_op_save_vmstate, + }, { + .name = "/sync-op/pdiscard", + .fn = test_sync_op_pdiscard, + .blkfn = test_sync_op_blk_pdiscard, + }, { + .name = "/sync-op/truncate", + .fn = test_sync_op_truncate, + }, { + .name = "/sync-op/block_status", + .fn = test_sync_op_block_status, + }, { + .name = "/sync-op/flush", + .fn = test_sync_op_flush, + .blkfn = test_sync_op_blk_flush, + }, { + .name = "/sync-op/check", + .fn = test_sync_op_check, + }, { + .name = "/sync-op/invalidate_cache", + .fn = test_sync_op_invalidate_cache, + }, +}; + +/* Test synchronous operations that run in a different iothread, so we have to + * poll for the coroutine there to return. */ +static void test_sync_op(const void *opaque) +{ + const SyncOpTest *t = opaque; + IOThread *iothread = iothread_new(); + AioContext *ctx = iothread_get_aio_context(iothread); + BlockBackend *blk; + BlockDriverState *bs; + BdrvChild *c; + + blk = blk_new(BLK_PERM_ALL, BLK_PERM_ALL); + bs = bdrv_new_open_driver(&bdrv_test, "base", BDRV_O_RDWR, &error_abort); + bs->total_sectors = 65536 / BDRV_SECTOR_SIZE; + blk_insert_bs(blk, bs, &error_abort); + c = QLIST_FIRST(&bs->parents); + + blk_set_aio_context(blk, ctx); + aio_context_acquire(ctx); + t->fn(c); + if (t->blkfn) { + t->blkfn(blk); + } + aio_context_release(ctx); + blk_set_aio_context(blk, qemu_get_aio_context()); + + bdrv_unref(bs); + blk_unref(blk); +} + +int main(int argc, char **argv) +{ + int i; + + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + for (i = 0; i < ARRAY_SIZE(sync_op_tests); i++) { + const SyncOpTest *t = &sync_op_tests[i]; + g_test_add_data_func(t->name, t, test_sync_op); + } + + return g_test_run(); +} diff --git a/tests/test-blockjob-txn.c b/tests/test-blockjob-txn.c index 58d9b87fb2..86606f92b3 100644 --- a/tests/test-blockjob-txn.c +++ b/tests/test-blockjob-txn.c @@ -24,39 +24,31 @@ typedef struct { int *result; } TestBlockJob; -static void test_block_job_complete(Job *job, void *opaque) +static void test_block_job_clean(Job *job) { BlockJob *bjob = container_of(job, BlockJob, job); BlockDriverState *bs = blk_bs(bjob->blk); - int rc = (intptr_t)opaque; - if (job_is_cancelled(job)) { - rc = -ECANCELED; - } - - job_completed(job, rc, NULL); bdrv_unref(bs); } -static void coroutine_fn test_block_job_run(void *opaque) +static int coroutine_fn test_block_job_run(Job *job, Error **errp) { - TestBlockJob *s = opaque; - BlockJob *job = &s->common; + TestBlockJob *s = container_of(job, TestBlockJob, common.job); while (s->iterations--) { if (s->use_timer) { - job_sleep_ns(&job->job, 0); + job_sleep_ns(job, 0); } else { - job_yield(&job->job); + job_yield(job); } - if (job_is_cancelled(&job->job)) { + if (job_is_cancelled(job)) { break; } } - job_defer_to_main_loop(&job->job, test_block_job_complete, - (void *)(intptr_t)s->rc); + return s->rc; } typedef struct { @@ -80,7 +72,8 @@ static const BlockJobDriver test_block_job_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = test_block_job_run, + .run = test_block_job_run, + .clean = test_block_job_clean, }, }; diff --git a/tests/test-blockjob.c b/tests/test-blockjob.c index cb42f06e61..652d1e8359 100644 --- a/tests/test-blockjob.c +++ b/tests/test-blockjob.c @@ -160,29 +160,21 @@ typedef struct CancelJob { BlockBackend *blk; bool should_converge; bool should_complete; - bool completed; } CancelJob; -static void cancel_job_completed(Job *job, void *opaque) -{ - CancelJob *s = opaque; - s->completed = true; - job_completed(job, 0, NULL); -} - static void cancel_job_complete(Job *job, Error **errp) { CancelJob *s = container_of(job, CancelJob, common.job); s->should_complete = true; } -static void coroutine_fn cancel_job_start(void *opaque) +static int coroutine_fn cancel_job_run(Job *job, Error **errp) { - CancelJob *s = opaque; + CancelJob *s = container_of(job, CancelJob, common.job); while (!s->should_complete) { if (job_is_cancelled(&s->common.job)) { - goto defer; + return 0; } if (!job_is_ready(&s->common.job) && s->should_converge) { @@ -192,8 +184,7 @@ static void coroutine_fn cancel_job_start(void *opaque) job_sleep_ns(&s->common.job, 100000); } - defer: - job_defer_to_main_loop(&s->common.job, cancel_job_completed, s); + return 0; } static const BlockJobDriver test_cancel_driver = { @@ -202,23 +193,25 @@ static const BlockJobDriver test_cancel_driver = { .free = block_job_free, .user_resume = block_job_user_resume, .drain = block_job_drain, - .start = cancel_job_start, + .run = cancel_job_run, .complete = cancel_job_complete, }, }; -static CancelJob *create_common(BlockJob **pjob) +static CancelJob *create_common(Job **pjob) { BlockBackend *blk; - BlockJob *job; + Job *job; + BlockJob *bjob; CancelJob *s; blk = create_blk(NULL); - job = mk_job(blk, "Steve", &test_cancel_driver, true, - JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); - job_ref(&job->job); - assert(job->job.status == JOB_STATUS_CREATED); - s = container_of(job, CancelJob, common); + bjob = mk_job(blk, "Steve", &test_cancel_driver, true, + JOB_MANUAL_FINALIZE | JOB_MANUAL_DISMISS); + job = &bjob->job; + job_ref(job); + assert(job->status == JOB_STATUS_CREATED); + s = container_of(bjob, CancelJob, common); s->blk = blk; *pjob = job; @@ -230,6 +223,10 @@ static void cancel_common(CancelJob *s) BlockJob *job = &s->common; BlockBackend *blk = s->blk; JobStatus sts = job->job.status; + AioContext *ctx; + + ctx = job->job.aio_context; + aio_context_acquire(ctx); job_cancel_sync(&job->job); if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) { @@ -239,11 +236,13 @@ static void cancel_common(CancelJob *s) assert(job->job.status == JOB_STATUS_NULL); job_unref(&job->job); destroy_blk(blk); + + aio_context_release(ctx); } static void test_cancel_created(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); @@ -252,119 +251,123 @@ static void test_cancel_created(void) static void test_cancel_running(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); - job_start(&job->job); - assert(job->job.status == JOB_STATUS_RUNNING); + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); cancel_common(s); } static void test_cancel_paused(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); - job_start(&job->job); - assert(job->job.status == JOB_STATUS_RUNNING); + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); - job_user_pause(&job->job, &error_abort); - job_enter(&job->job); - assert(job->job.status == JOB_STATUS_PAUSED); + job_user_pause(job, &error_abort); + job_enter(job); + assert(job->status == JOB_STATUS_PAUSED); cancel_common(s); } static void test_cancel_ready(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); - job_start(&job->job); - assert(job->job.status == JOB_STATUS_RUNNING); + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); s->should_converge = true; - job_enter(&job->job); - assert(job->job.status == JOB_STATUS_READY); + job_enter(job); + assert(job->status == JOB_STATUS_READY); cancel_common(s); } static void test_cancel_standby(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); - job_start(&job->job); - assert(job->job.status == JOB_STATUS_RUNNING); + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); s->should_converge = true; - job_enter(&job->job); - assert(job->job.status == JOB_STATUS_READY); + job_enter(job); + assert(job->status == JOB_STATUS_READY); - job_user_pause(&job->job, &error_abort); - job_enter(&job->job); - assert(job->job.status == JOB_STATUS_STANDBY); + job_user_pause(job, &error_abort); + job_enter(job); + assert(job->status == JOB_STATUS_STANDBY); cancel_common(s); } static void test_cancel_pending(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); - job_start(&job->job); - assert(job->job.status == JOB_STATUS_RUNNING); + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); s->should_converge = true; - job_enter(&job->job); - assert(job->job.status == JOB_STATUS_READY); + job_enter(job); + assert(job->status == JOB_STATUS_READY); - job_complete(&job->job, &error_abort); - job_enter(&job->job); - while (!s->completed) { + job_complete(job, &error_abort); + job_enter(job); + while (!job->deferred_to_main_loop) { aio_poll(qemu_get_aio_context(), true); } - assert(job->job.status == JOB_STATUS_PENDING); + assert(job->status == JOB_STATUS_READY); + aio_poll(qemu_get_aio_context(), true); + assert(job->status == JOB_STATUS_PENDING); cancel_common(s); } static void test_cancel_concluded(void) { - BlockJob *job; + Job *job; CancelJob *s; s = create_common(&job); - job_start(&job->job); - assert(job->job.status == JOB_STATUS_RUNNING); + job_start(job); + assert(job->status == JOB_STATUS_RUNNING); s->should_converge = true; - job_enter(&job->job); - assert(job->job.status == JOB_STATUS_READY); + job_enter(job); + assert(job->status == JOB_STATUS_READY); - job_complete(&job->job, &error_abort); - job_enter(&job->job); - while (!s->completed) { + job_complete(job, &error_abort); + job_enter(job); + while (!job->deferred_to_main_loop) { aio_poll(qemu_get_aio_context(), true); } - assert(job->job.status == JOB_STATUS_PENDING); + assert(job->status == JOB_STATUS_READY); + aio_poll(qemu_get_aio_context(), true); + assert(job->status == JOB_STATUS_PENDING); - job_finalize(&job->job, &error_abort); - assert(job->job.status == JOB_STATUS_CONCLUDED); + job_finalize(job, &error_abort); + assert(job->status == JOB_STATUS_CONCLUDED); cancel_common(s); } diff --git a/tests/test-char.c b/tests/test-char.c index 5905d31441..95ccfd3cdb 100644 --- a/tests/test-char.c +++ b/tests/test-char.c @@ -11,11 +11,17 @@ #include "qapi/qapi-commands-char.h" #include "qapi/qmp/qdict.h" #include "qom/qom-qobject.h" +#include "io/channel-socket.h" +#include "qapi/qobject-input-visitor.h" +#include "qapi/qapi-visit-sockets.h" static bool quit; typedef struct FeHandler { int read_count; + bool is_open; + int openclose_count; + bool openclose_mismatch; int last_event; char read_buf[128]; } FeHandler; @@ -49,14 +55,27 @@ static void fe_read(void *opaque, const uint8_t *buf, int size) static void fe_event(void *opaque, int event) { FeHandler *h = opaque; + bool new_open_state; h->last_event = event; - if (event != CHR_EVENT_BREAK) { + switch (event) { + case CHR_EVENT_BREAK: + break; + case CHR_EVENT_OPENED: + case CHR_EVENT_CLOSED: + h->openclose_count++; + new_open_state = (event == CHR_EVENT_OPENED); + if (h->is_open == new_open_state) { + h->openclose_mismatch = true; + } + h->is_open = new_open_state; + /* no break */ + default: quit = true; + break; } } -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS #ifdef _WIN32 static void char_console_test_subprocess(void) { @@ -67,7 +86,7 @@ static void char_console_test_subprocess(void) 1, &error_abort); qemu_opt_set(opts, "backend", "console", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); qemu_chr_write_all(chr, (const uint8_t *)"CONSOLE", 7); @@ -89,7 +108,7 @@ static void char_stdio_test_subprocess(void) CharBackend be; int ret; - chr = qemu_chr_new("label", "stdio"); + chr = qemu_chr_new("label", "stdio", NULL); g_assert_nonnull(chr); qemu_chr_fe_init(&be, chr, &error_abort); @@ -106,7 +125,6 @@ static void char_stdio_test(void) g_test_trap_assert_passed(); g_test_trap_assert_stdout("buf"); } -#endif static void char_ringbuf_test(void) { @@ -121,7 +139,7 @@ static void char_ringbuf_test(void) qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "5", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_null(chr); qemu_opts_del(opts); @@ -129,7 +147,7 @@ static void char_ringbuf_test(void) 1, &error_abort); qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "2", &error_abort); - chr = qemu_chr_new_from_opts(opts, &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); g_assert_nonnull(chr); qemu_opts_del(opts); @@ -152,7 +170,7 @@ static void char_ringbuf_test(void) 1, &error_abort); qemu_opt_set(opts, "backend", "memory", &error_abort); qemu_opt_set(opts, "size", "2", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); object_unparent(OBJECT(chr)); qemu_opts_del(opts); @@ -163,7 +181,7 @@ static void char_mux_test(void) QemuOpts *opts; Chardev *chr, *base; char *data; - FeHandler h1 = { 0, }, h2 = { 0, }; + FeHandler h1 = { 0, false, 0, false, }, h2 = { 0, false, 0, false, }; CharBackend chr_be1, chr_be2; opts = qemu_opts_create(qemu_find_opts("chardev"), "mux-label", @@ -171,7 +189,7 @@ static void char_mux_test(void) qemu_opt_set(opts, "backend", "ringbuf", &error_abort); qemu_opt_set(opts, "size", "128", &error_abort); qemu_opt_set(opts, "mux", "on", &error_abort); - chr = qemu_chr_new_from_opts(opts, &error_abort); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); g_assert_nonnull(chr); qemu_opts_del(opts); @@ -235,6 +253,65 @@ static void char_mux_test(void) g_assert_cmpint(h1.last_event, ==, CHR_EVENT_BREAK); g_assert_cmpint(h2.last_event, ==, CHR_EVENT_MUX_OUT); + /* open/close state and corresponding events */ + g_assert_true(qemu_chr_fe_backend_open(&chr_be1)); + g_assert_true(qemu_chr_fe_backend_open(&chr_be2)); + g_assert_true(h1.is_open); + g_assert_false(h1.openclose_mismatch); + g_assert_true(h2.is_open); + g_assert_false(h2.openclose_mismatch); + + h1.openclose_count = h2.openclose_count = 0; + + qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, + NULL, NULL, false); + qemu_chr_fe_set_handlers(&chr_be2, NULL, NULL, NULL, NULL, + NULL, NULL, false); + g_assert_cmpint(h1.openclose_count, ==, 0); + g_assert_cmpint(h2.openclose_count, ==, 0); + + h1.is_open = h2.is_open = false; + qemu_chr_fe_set_handlers(&chr_be1, + NULL, + NULL, + fe_event, + NULL, + &h1, + NULL, false); + qemu_chr_fe_set_handlers(&chr_be2, + NULL, + NULL, + fe_event, + NULL, + &h2, + NULL, false); + g_assert_cmpint(h1.openclose_count, ==, 1); + g_assert_false(h1.openclose_mismatch); + g_assert_cmpint(h2.openclose_count, ==, 1); + g_assert_false(h2.openclose_mismatch); + + qemu_chr_be_event(base, CHR_EVENT_CLOSED); + qemu_chr_be_event(base, CHR_EVENT_OPENED); + g_assert_cmpint(h1.openclose_count, ==, 3); + g_assert_false(h1.openclose_mismatch); + g_assert_cmpint(h2.openclose_count, ==, 3); + g_assert_false(h2.openclose_mismatch); + + qemu_chr_fe_set_handlers(&chr_be2, + fe_can_read, + fe_read, + fe_event, + NULL, + &h2, + NULL, false); + qemu_chr_fe_set_handlers(&chr_be1, + fe_can_read, + fe_read, + fe_event, + NULL, + &h1, + NULL, false); + /* remove first handler */ qemu_chr_fe_set_handlers(&chr_be1, NULL, NULL, NULL, NULL, NULL, NULL, true); @@ -259,153 +336,131 @@ static void char_mux_test(void) qemu_chr_fe_deinit(&chr_be2, true); } -typedef struct SocketIdleData { - GMainLoop *loop; - Chardev *chr; - bool conn_expected; - CharBackend *be; - CharBackend *client_be; -} SocketIdleData; -static gboolean char_socket_test_idle(gpointer user_data) -{ - SocketIdleData *data = user_data; - - if (object_property_get_bool(OBJECT(data->chr), "connected", NULL) - == data->conn_expected) { - quit = true; - return FALSE; - } - - return TRUE; -} - -static void socket_read(void *opaque, const uint8_t *buf, int size) -{ - SocketIdleData *data = opaque; - - g_assert_cmpint(size, ==, 1); - g_assert_cmpint(*buf, ==, 'Z'); - - size = qemu_chr_fe_write(data->be, (const uint8_t *)"hello", 5); - g_assert_cmpint(size, ==, 5); -} - -static int socket_can_read(void *opaque) -{ - return 10; -} - -static void socket_read_hello(void *opaque, const uint8_t *buf, int size) +static void websock_server_read(void *opaque, const uint8_t *buf, int size) { g_assert_cmpint(size, ==, 5); - g_assert(strncmp((char *)buf, "hello", 5) == 0); - + g_assert(memcmp(buf, "world", size) == 0); quit = true; } -static int socket_can_read_hello(void *opaque) + +static int websock_server_can_read(void *opaque) { return 10; } -static void char_socket_test_common(Chardev *chr) + +static bool websock_check_http_headers(char *buf, int size) +{ + int i; + const char *ans[] = { "HTTP/1.1 101 Switching Protocols\r\n", + "Server: QEMU VNC\r\n", + "Upgrade: websocket\r\n", + "Connection: Upgrade\r\n", + "Sec-WebSocket-Accept:", + "Sec-WebSocket-Protocol: binary\r\n" }; + + for (i = 0; i < 6; i++) { + if (g_strstr_len(buf, size, ans[i]) == NULL) { + return false; + } + } + + return true; +} + + +static void websock_client_read(void *opaque, const uint8_t *buf, int size) +{ + const uint8_t ping[] = { 0x89, 0x85, /* Ping header */ + 0x07, 0x77, 0x9e, 0xf9, /* Masking key */ + 0x6f, 0x12, 0xf2, 0x95, 0x68 /* "hello" */ }; + + const uint8_t binary[] = { 0x82, 0x85, /* Binary header */ + 0x74, 0x90, 0xb9, 0xdf, /* Masking key */ + 0x03, 0xff, 0xcb, 0xb3, 0x10 /* "world" */ }; + Chardev *chr_client = opaque; + + if (websock_check_http_headers((char *) buf, size)) { + qemu_chr_fe_write(chr_client->be, ping, sizeof(ping)); + } else if (buf[0] == 0x8a && buf[1] == 0x05) { + g_assert(strncmp((char *) buf + 2, "hello", 5) == 0); + qemu_chr_fe_write(chr_client->be, binary, sizeof(binary)); + } else { + g_assert(buf[0] == 0x88 && buf[1] == 0x16); + g_assert(strncmp((char *) buf + 4, "peer requested close", 10) == 0); + quit = true; + } +} + + +static int websock_client_can_read(void *opaque) +{ + return 4096; +} + + +static void char_websock_test(void) { - Chardev *chr_client; QObject *addr; QDict *qdict; const char *port; - SocketIdleData d = { .chr = chr }; + char *tmp; + char *handshake_port; CharBackend be; CharBackend client_be; - char *tmp; - - d.be = &be; - d.client_be = &be; - - g_assert_nonnull(chr); - g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + Chardev *chr_client; + Chardev *chr = qemu_chr_new("server", + "websocket:127.0.0.1:0,server,nowait", NULL); + const char handshake[] = "GET / HTTP/1.1\r\n" + "Upgrade: websocket\r\n" + "Connection: Upgrade\r\n" + "Host: localhost:%s\r\n" + "Origin: http://localhost:%s\r\n" + "Sec-WebSocket-Key: o9JHNiS3/0/0zYE1wa3yIw==\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Sec-WebSocket-Protocol: binary\r\n\r\n"; + const uint8_t close[] = { 0x88, 0x82, /* Close header */ + 0xef, 0xaa, 0xc5, 0x97, /* Masking key */ + 0xec, 0x42 /* Status code */ }; addr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); qdict = qobject_to(QDict, addr); port = qdict_get_str(qdict, "port"); tmp = g_strdup_printf("tcp:127.0.0.1:%s", port); + handshake_port = g_strdup_printf(handshake, port, port); qobject_unref(qdict); qemu_chr_fe_init(&be, chr, &error_abort); - qemu_chr_fe_set_handlers(&be, socket_can_read, socket_read, - NULL, NULL, &d, NULL, true); + qemu_chr_fe_set_handlers(&be, websock_server_can_read, websock_server_read, + NULL, NULL, chr, NULL, true); - chr_client = qemu_chr_new("client", tmp); + chr_client = qemu_chr_new("client", tmp, NULL); qemu_chr_fe_init(&client_be, chr_client, &error_abort); - qemu_chr_fe_set_handlers(&client_be, socket_can_read_hello, - socket_read_hello, - NULL, NULL, &d, NULL, true); + qemu_chr_fe_set_handlers(&client_be, websock_client_can_read, + websock_client_read, + NULL, NULL, chr_client, NULL, true); g_free(tmp); - d.conn_expected = true; - guint id = g_idle_add(char_socket_test_idle, &d); - g_source_set_name_by_id(id, "test-idle"); - g_assert_cmpint(id, >, 0); + qemu_chr_write_all(chr_client, + (uint8_t *) handshake_port, + strlen(handshake_port)); + g_free(handshake_port); main_loop(); g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); g_assert(object_property_get_bool(OBJECT(chr_client), "connected", &error_abort)); - qemu_chr_write_all(chr_client, (const uint8_t *)"Z", 1); + qemu_chr_write_all(chr_client, close, sizeof(close)); main_loop(); object_unparent(OBJECT(chr_client)); - - d.conn_expected = false; - g_idle_add(char_socket_test_idle, &d); - main_loop(); - object_unparent(OBJECT(chr)); } -static void char_socket_basic_test(void) -{ - Chardev *chr = qemu_chr_new("server", "tcp:127.0.0.1:0,server,nowait"); - - char_socket_test_common(chr); -} - - -static void char_socket_fdpass_test(void) -{ - Chardev *chr; - char *optstr; - QemuOpts *opts; - int fd; - SocketAddress *addr = g_new0(SocketAddress, 1); - - addr->type = SOCKET_ADDRESS_TYPE_INET; - addr->u.inet.host = g_strdup("127.0.0.1"); - addr->u.inet.port = g_strdup("0"); - - fd = socket_listen(addr, &error_abort); - g_assert(fd >= 0); - - qapi_free_SocketAddress(addr); - - optstr = g_strdup_printf("socket,id=cdev,fd=%d,server,nowait", fd); - - opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), - optstr, true); - g_free(optstr); - g_assert_nonnull(opts); - - chr = qemu_chr_new_from_opts(opts, &error_abort); - - qemu_opts_del(opts); - - char_socket_test_common(chr); -} - - #ifndef _WIN32 static void char_pipe_test(void) { @@ -427,7 +482,7 @@ static void char_pipe_test(void) } tmp = g_strdup_printf("pipe:%s", pipe); - chr = qemu_chr_new("pipe", tmp); + chr = qemu_chr_new("pipe", tmp, NULL); g_assert_nonnull(chr); g_free(tmp); @@ -472,6 +527,28 @@ static void char_pipe_test(void) } #endif +typedef struct SocketIdleData { + GMainLoop *loop; + Chardev *chr; + bool conn_expected; + CharBackend *be; + CharBackend *client_be; +} SocketIdleData; + + +static void socket_read_hello(void *opaque, const uint8_t *buf, int size) +{ + g_assert_cmpint(size, ==, 5); + g_assert(strncmp((char *)buf, "hello", 5) == 0); + + quit = true; +} + +static int socket_can_read_hello(void *opaque) +{ + return 10; +} + static int make_udp_socket(int *port) { struct sockaddr_in addr = { 0, }; @@ -509,7 +586,7 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) int port; sock = make_udp_socket(&port); tmp = g_strdup_printf("udp:127.0.0.1:%d", port); - chr = qemu_chr_new("client", tmp); + chr = qemu_chr_new("client", tmp, NULL); g_assert_nonnull(chr); be = g_alloca(sizeof(CharBackend)); @@ -542,6 +619,488 @@ static void char_udp_test(void) char_udp_test_internal(NULL, 0); } + +typedef struct { + int event; + bool got_pong; +} CharSocketTestData; + + +#define SOCKET_PING "Hello" +#define SOCKET_PONG "World" + + +static void +char_socket_event(void *opaque, int event) +{ + CharSocketTestData *data = opaque; + data->event = event; +} + + +static void +char_socket_read(void *opaque, const uint8_t *buf, int size) +{ + CharSocketTestData *data = opaque; + g_assert_cmpint(size, ==, sizeof(SOCKET_PONG)); + g_assert(memcmp(buf, SOCKET_PONG, size) == 0); + data->got_pong = true; +} + + +static int +char_socket_can_read(void *opaque) +{ + return sizeof(SOCKET_PONG); +} + + +static char * +char_socket_addr_to_opt_str(SocketAddress *addr, bool fd_pass, + const char *reconnect, bool is_listen) +{ + if (fd_pass) { + QIOChannelSocket *ioc = qio_channel_socket_new(); + int fd; + char *optstr; + g_assert(!reconnect); + if (is_listen) { + qio_channel_socket_listen_sync(ioc, addr, &error_abort); + } else { + qio_channel_socket_connect_sync(ioc, addr, &error_abort); + } + fd = ioc->fd; + ioc->fd = -1; + optstr = g_strdup_printf("socket,id=cdev0,fd=%d%s", + fd, is_listen ? ",server,nowait" : ""); + object_unref(OBJECT(ioc)); + return optstr; + } else { + switch (addr->type) { + case SOCKET_ADDRESS_TYPE_INET: + return g_strdup_printf("socket,id=cdev0,host=%s,port=%s%s%s", + addr->u.inet.host, + addr->u.inet.port, + reconnect ? reconnect : "", + is_listen ? ",server,nowait" : ""); + + case SOCKET_ADDRESS_TYPE_UNIX: + return g_strdup_printf("socket,id=cdev0,path=%s%s%s", + addr->u.q_unix.path, + reconnect ? reconnect : "", + is_listen ? ",server,nowait" : ""); + + default: + g_assert_not_reached(); + } + } +} + + +static void +char_socket_ping_pong(QIOChannel *ioc) +{ + char greeting[sizeof(SOCKET_PING)]; + const char *response = SOCKET_PONG; + + qio_channel_read_all(ioc, greeting, sizeof(greeting), &error_abort); + + g_assert(memcmp(greeting, SOCKET_PING, sizeof(greeting)) == 0); + + qio_channel_write_all(ioc, response, sizeof(SOCKET_PONG), &error_abort); + + object_unref(OBJECT(ioc)); +} + + +static gpointer +char_socket_server_client_thread(gpointer data) +{ + SocketAddress *addr = data; + QIOChannelSocket *ioc = qio_channel_socket_new(); + + qio_channel_socket_connect_sync(ioc, addr, &error_abort); + + char_socket_ping_pong(QIO_CHANNEL(ioc)); + + return NULL; +} + + +typedef struct { + SocketAddress *addr; + bool wait_connected; + bool fd_pass; +} CharSocketServerTestConfig; + + +static void char_socket_server_test(gconstpointer opaque) +{ + const CharSocketServerTestConfig *config = opaque; + Chardev *chr; + CharBackend be = {0}; + CharSocketTestData data = {0}; + QObject *qaddr; + SocketAddress *addr; + Visitor *v; + QemuThread thread; + int ret; + bool reconnected = false; + char *optstr; + QemuOpts *opts; + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + /* + * We rely on config->addr containing "nowait", otherwise + * qemu_chr_new() will block until a client connects. We + * can't spawn our client thread though, because until + * qemu_chr_new() returns we don't know what TCP port was + * allocated by the OS + */ + optstr = char_socket_addr_to_opt_str(config->addr, + config->fd_pass, + NULL, + true); + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + g_assert_nonnull(qaddr); + + v = qobject_input_visitor_new(qaddr); + visit_type_SocketAddress(v, "addr", &addr, &error_abort); + visit_free(v); + qobject_unref(qaddr); + + qemu_chr_fe_init(&be, chr, &error_abort); + + reconnect: + data.event = -1; + qemu_chr_fe_set_handlers(&be, NULL, NULL, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == -1); + + /* + * Kick off a thread to act as the "remote" client + * which just plays ping-pong with us + */ + qemu_thread_create(&thread, "client", + char_socket_server_client_thread, + addr, QEMU_THREAD_JOINABLE); + g_assert(data.event == -1); + + if (config->wait_connected) { + /* Synchronously accept a connection */ + qemu_chr_wait_connected(chr, &error_abort); + } else { + /* + * Asynchronously accept a connection when the evnt + * loop reports the listener socket as readable + */ + while (data.event == -1) { + main_loop_wait(false); + } + } + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Send a greeting to the client */ + ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, + sizeof(SOCKET_PING)); + g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); + g_assert(data.event == -1); + + /* Setup a callback to receive the reply to our greeting */ + qemu_chr_fe_set_handlers(&be, char_socket_can_read, + char_socket_read, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Wait for the client to go away */ + while (data.event == -1) { + main_loop_wait(false); + } + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.event == CHR_EVENT_CLOSED); + g_assert(data.got_pong); + + qemu_thread_join(&thread); + + if (!reconnected) { + reconnected = true; + goto reconnect; + } + + qapi_free_SocketAddress(addr); + object_unparent(OBJECT(chr)); + g_free(optstr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + + +static gpointer +char_socket_client_server_thread(gpointer data) +{ + QIOChannelSocket *ioc = data; + QIOChannelSocket *cioc; + + cioc = qio_channel_socket_accept(ioc, &error_abort); + g_assert_nonnull(cioc); + + char_socket_ping_pong(QIO_CHANNEL(cioc)); + + return NULL; +} + + +typedef struct { + SocketAddress *addr; + const char *reconnect; + bool wait_connected; + bool fd_pass; +} CharSocketClientTestConfig; + + +static void char_socket_client_test(gconstpointer opaque) +{ + const CharSocketClientTestConfig *config = opaque; + QIOChannelSocket *ioc; + char *optstr; + Chardev *chr; + CharBackend be = {0}; + CharSocketTestData data = {0}; + SocketAddress *addr; + QemuThread thread; + int ret; + bool reconnected = false; + QemuOpts *opts; + + /* + * Setup a listener socket and determine get its address + * so we know the TCP port for the client later + */ + ioc = qio_channel_socket_new(); + g_assert_nonnull(ioc); + qio_channel_socket_listen_sync(ioc, config->addr, &error_abort); + addr = qio_channel_socket_get_local_address(ioc, &error_abort); + g_assert_nonnull(addr); + + /* + * Kick off a thread to act as the "remote" client + * which just plays ping-pong with us + */ + qemu_thread_create(&thread, "client", + char_socket_client_server_thread, + ioc, QEMU_THREAD_JOINABLE); + + /* + * Populate the chardev address based on what the server + * is actually listening on + */ + optstr = char_socket_addr_to_opt_str(addr, + config->fd_pass, + config->reconnect, + false); + + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + + if (config->reconnect) { + /* + * If reconnect is set, the connection will be + * established in a background thread and we won't + * see the "connected" status updated until we + * run the main event loop, or call qemu_chr_wait_connected + */ + g_assert(!object_property_get_bool(OBJECT(chr), "connected", + &error_abort)); + } else { + g_assert(object_property_get_bool(OBJECT(chr), "connected", + &error_abort)); + } + + qemu_chr_fe_init(&be, chr, &error_abort); + + reconnect: + data.event = -1; + qemu_chr_fe_set_handlers(&be, NULL, NULL, + char_socket_event, NULL, + &data, NULL, true); + if (config->reconnect) { + g_assert(data.event == -1); + } else { + g_assert(data.event == CHR_EVENT_OPENED); + } + + if (config->wait_connected) { + /* + * Synchronously wait for the connection to complete + * This should be a no-op if reconnect is not set. + */ + qemu_chr_wait_connected(chr, &error_abort); + } else { + /* + * Asynchronously wait for the connection to be reported + * as complete when the background thread reports its + * status. + * The loop will short-circuit if reconnect was set + */ + while (data.event == -1) { + main_loop_wait(false); + } + } + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + g_assert(object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + /* Send a greeting to the server */ + ret = qemu_chr_fe_write_all(&be, (const uint8_t *)SOCKET_PING, + sizeof(SOCKET_PING)); + g_assert_cmpint(ret, ==, sizeof(SOCKET_PING)); + g_assert(data.event == -1); + + /* Setup a callback to receive the reply to our greeting */ + qemu_chr_fe_set_handlers(&be, char_socket_can_read, + char_socket_read, + char_socket_event, NULL, + &data, NULL, true); + g_assert(data.event == CHR_EVENT_OPENED); + data.event = -1; + + /* Wait for the server to go away */ + while (data.event == -1) { + main_loop_wait(false); + } + g_assert(data.event == CHR_EVENT_CLOSED); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + g_assert(data.got_pong); + qemu_thread_join(&thread); + + if (config->reconnect && !reconnected) { + reconnected = true; + qemu_thread_create(&thread, "client", + char_socket_client_server_thread, + ioc, QEMU_THREAD_JOINABLE); + goto reconnect; + } + + object_unref(OBJECT(ioc)); + object_unparent(OBJECT(chr)); + qapi_free_SocketAddress(addr); + g_free(optstr); +} + +static void +count_closed_event(void *opaque, int event) +{ + int *count = opaque; + if (event == CHR_EVENT_CLOSED) { + (*count)++; + } +} + +static void +char_socket_discard_read(void *opaque, const uint8_t *buf, int size) +{ +} + +static void char_socket_server_two_clients_test(gconstpointer opaque) +{ + SocketAddress *incoming_addr = (gpointer) opaque; + Chardev *chr; + CharBackend be = {0}; + QObject *qaddr; + SocketAddress *addr; + Visitor *v; + char *optstr; + QemuOpts *opts; + QIOChannelSocket *ioc1, *ioc2; + int closed = 0; + + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + /* + * We rely on addr containing "nowait", otherwise + * qemu_chr_new() will block until a client connects. We + * can't spawn our client thread though, because until + * qemu_chr_new() returns we don't know what TCP port was + * allocated by the OS + */ + optstr = char_socket_addr_to_opt_str(incoming_addr, + false, + NULL, + true); + opts = qemu_opts_parse_noisily(qemu_find_opts("chardev"), + optstr, true); + g_assert_nonnull(opts); + chr = qemu_chr_new_from_opts(opts, NULL, &error_abort); + qemu_opts_del(opts); + g_assert_nonnull(chr); + g_assert(!object_property_get_bool(OBJECT(chr), "connected", &error_abort)); + + qaddr = object_property_get_qobject(OBJECT(chr), "addr", &error_abort); + g_assert_nonnull(qaddr); + + v = qobject_input_visitor_new(qaddr); + visit_type_SocketAddress(v, "addr", &addr, &error_abort); + visit_free(v); + qobject_unref(qaddr); + + qemu_chr_fe_init(&be, chr, &error_abort); + + qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, + count_closed_event, NULL, + &closed, NULL, true); + + ioc1 = qio_channel_socket_new(); + qio_channel_socket_connect_sync(ioc1, addr, &error_abort); + qemu_chr_wait_connected(chr, &error_abort); + + /* switch the chardev to another context */ + GMainContext *ctx = g_main_context_new(); + qemu_chr_fe_set_handlers(&be, char_socket_can_read, char_socket_discard_read, + count_closed_event, NULL, + &closed, ctx, true); + + /* Start a second connection while the first is still connected. + * It will be placed in the listen() backlog, and connect() will + * succeed immediately. + */ + ioc2 = qio_channel_socket_new(); + qio_channel_socket_connect_sync(ioc2, addr, &error_abort); + + object_unref(OBJECT(ioc1)); + /* The two connections should now be processed serially. */ + while (g_main_context_iteration(ctx, TRUE)) { + if (closed == 1 && ioc2) { + object_unref(OBJECT(ioc2)); + ioc2 = NULL; + } + if (closed == 2) { + break; + } + } + + qapi_free_SocketAddress(addr); + object_unparent(OBJECT(chr)); + g_main_context_unref(ctx); + g_free(optstr); + g_unsetenv("QTEST_SILENT_ERRORS"); +} + + #ifdef HAVE_CHARDEV_SERIAL static void char_serial_test(void) { @@ -553,14 +1112,14 @@ static void char_serial_test(void) qemu_opt_set(opts, "backend", "serial", &error_abort); qemu_opt_set(opts, "path", "/dev/null", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); /* TODO: add more tests with a pty */ object_unparent(OBJECT(chr)); /* test tty alias */ qemu_opt_set(opts, "backend", "tty", &error_abort); - chr = qemu_chr_new_from_opts(opts, NULL); + chr = qemu_chr_new_from_opts(opts, NULL, NULL); g_assert_nonnull(chr); object_unparent(OBJECT(chr)); @@ -593,7 +1152,7 @@ static void char_file_fifo_test(void) g_assert_cmpint(ret, ==, 8); chr = qemu_chardev_new("label-file", TYPE_CHARDEV_FILE, &backend, - &error_abort); + NULL, &error_abort); qemu_chr_fe_init(&be, chr, &error_abort); qemu_chr_fe_set_handlers(&be, @@ -647,7 +1206,7 @@ static void char_file_test_internal(Chardev *ext_chr, const char *filepath) out = g_build_filename(tmp_path, "out", NULL); file.out = out; chr = qemu_chardev_new(NULL, TYPE_CHARDEV_FILE, &backend, - &error_abort); + NULL, &error_abort); } ret = qemu_chr_write_all(chr, (uint8_t *)"hello!", 6); g_assert_cmpint(ret, ==, 6); @@ -682,7 +1241,7 @@ static void char_null_test(void) chr = qemu_chr_find("label-null"); g_assert_null(chr); - chr = qemu_chr_new("label-null", "null"); + chr = qemu_chr_new("label-null", "null", NULL); chr = qemu_chr_find("label-null"); g_assert_nonnull(chr); @@ -718,9 +1277,10 @@ static void char_null_test(void) static void char_invalid_test(void) { Chardev *chr; - - chr = qemu_chr_new("label-invalid", "invalid"); + g_setenv("QTEST_SILENT_ERRORS", "1", 1); + chr = qemu_chr_new("label-invalid", "invalid", NULL); g_assert_null(chr); + g_unsetenv("QTEST_SILENT_ERRORS"); } static int chardev_change(void *opaque) @@ -752,7 +1312,7 @@ static void char_hotswap_test(void) chr_args = g_strdup_printf("udp:127.0.0.1:%d", port); - chr = qemu_chr_new("chardev", chr_args); + chr = qemu_chr_new("chardev", chr_args, NULL); qemu_chr_fe_init(&be, chr, &error_abort); /* check that chardev operates correctly */ @@ -807,14 +1367,12 @@ int main(int argc, char **argv) g_test_add_func("/char/invalid", char_invalid_test); g_test_add_func("/char/ringbuf", char_ringbuf_test); g_test_add_func("/char/mux", char_mux_test); -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS #ifdef _WIN32 g_test_add_func("/char/console/subprocess", char_console_test_subprocess); g_test_add_func("/char/console", char_console_test); #endif g_test_add_func("/char/stdio/subprocess", char_stdio_test_subprocess); g_test_add_func("/char/stdio", char_stdio_test); -#endif #ifndef _WIN32 g_test_add_func("/char/pipe", char_pipe_test); #endif @@ -822,13 +1380,80 @@ int main(int argc, char **argv) #ifndef _WIN32 g_test_add_func("/char/file-fifo", char_file_fifo_test); #endif - g_test_add_func("/char/socket/basic", char_socket_basic_test); - g_test_add_func("/char/socket/fdpass", char_socket_fdpass_test); + + SocketAddress tcpaddr = { + .type = SOCKET_ADDRESS_TYPE_INET, + .u.inet.host = (char *)"127.0.0.1", + .u.inet.port = (char *)"0", + }; +#ifndef WIN32 + SocketAddress unixaddr = { + .type = SOCKET_ADDRESS_TYPE_UNIX, + .u.q_unix.path = (char *)"test-char.sock", + }; +#endif + +#define SOCKET_SERVER_TEST(name, addr) \ + CharSocketServerTestConfig server1 ## name = \ + { addr, false, false }; \ + CharSocketServerTestConfig server2 ## name = \ + { addr, true, false }; \ + CharSocketServerTestConfig server3 ## name = \ + { addr, false, true }; \ + CharSocketServerTestConfig server4 ## name = \ + { addr, true, true }; \ + g_test_add_data_func("/char/socket/server/mainloop/" # name, \ + &server1 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/wait-conn/" # name, \ + &server2 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/mainloop-fdpass/" # name, \ + &server3 ##name, char_socket_server_test); \ + g_test_add_data_func("/char/socket/server/wait-conn-fdpass/" # name, \ + &server4 ##name, char_socket_server_test) + +#define SOCKET_CLIENT_TEST(name, addr) \ + CharSocketClientTestConfig client1 ## name = \ + { addr, NULL, false, false }; \ + CharSocketClientTestConfig client2 ## name = \ + { addr, NULL, true, false }; \ + CharSocketClientTestConfig client3 ## name = \ + { addr, ",reconnect=1", false }; \ + CharSocketClientTestConfig client4 ## name = \ + { addr, ",reconnect=1", true }; \ + CharSocketClientTestConfig client5 ## name = \ + { addr, NULL, false, true }; \ + CharSocketClientTestConfig client6 ## name = \ + { addr, NULL, true, true }; \ + g_test_add_data_func("/char/socket/client/mainloop/" # name, \ + &client1 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn/" # name, \ + &client2 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/mainloop-reconnect/" # name, \ + &client3 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn-reconnect/" # name, \ + &client4 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/mainloop-fdpass/" # name, \ + &client5 ##name, char_socket_client_test); \ + g_test_add_data_func("/char/socket/client/wait-conn-fdpass/" # name, \ + &client6 ##name, char_socket_client_test) + + SOCKET_SERVER_TEST(tcp, &tcpaddr); + SOCKET_CLIENT_TEST(tcp, &tcpaddr); + g_test_add_data_func("/char/socket/server/two-clients/tcp", &tcpaddr, + char_socket_server_two_clients_test); +#ifndef WIN32 + SOCKET_SERVER_TEST(unix, &unixaddr); + SOCKET_CLIENT_TEST(unix, &unixaddr); + g_test_add_data_func("/char/socket/server/two-clients/unix", &unixaddr, + char_socket_server_two_clients_test); +#endif + g_test_add_func("/char/udp", char_udp_test); #ifdef HAVE_CHARDEV_SERIAL g_test_add_func("/char/serial", char_serial_test); #endif g_test_add_func("/char/hotswap", char_hotswap_test); + g_test_add_func("/char/websocket", char_websock_test); return g_test_run(); } diff --git a/tests/test-clone-visitor.c b/tests/test-clone-visitor.c index 9aeaf86a07..5e1e8b2f5e 100644 --- a/tests/test-clone-visitor.c +++ b/tests/test-clone-visitor.c @@ -63,7 +63,7 @@ static void test_clone_alternate(void) qapi_free_AltEnumBool(s_dst); } -static void test_clone_native_list(void) +static void test_clone_list_union(void) { uint8List *src, *dst; uint8List *tmp = NULL; @@ -102,18 +102,18 @@ static void test_clone_empty(void) static void test_clone_complex1(void) { - UserDefNativeListUnion *src, *dst; + UserDefListUnion *src, *dst; - src = g_new0(UserDefNativeListUnion, 1); - src->type = USER_DEF_NATIVE_LIST_UNION_KIND_STRING; + src = g_new0(UserDefListUnion, 1); + src->type = USER_DEF_LIST_UNION_KIND_STRING; - dst = QAPI_CLONE(UserDefNativeListUnion, src); + dst = QAPI_CLONE(UserDefListUnion, src); g_assert(dst); g_assert_cmpint(dst->type, ==, src->type); g_assert(!dst->u.string.data); - qapi_free_UserDefNativeListUnion(src); - qapi_free_UserDefNativeListUnion(dst); + qapi_free_UserDefListUnion(src); + qapi_free_UserDefListUnion(dst); } static void test_clone_complex2(void) @@ -192,7 +192,7 @@ int main(int argc, char **argv) g_test_add_func("/visitor/clone/struct", test_clone_struct); g_test_add_func("/visitor/clone/alternate", test_clone_alternate); - g_test_add_func("/visitor/clone/native_list", test_clone_native_list); + g_test_add_func("/visitor/clone/list_union", test_clone_list_union); g_test_add_func("/visitor/clone/empty", test_clone_empty); g_test_add_func("/visitor/clone/complex1", test_clone_complex1); g_test_add_func("/visitor/clone/complex2", test_clone_complex2); diff --git a/tests/test-coroutine.c b/tests/test-coroutine.c index 28e79b3210..e946d93a65 100644 --- a/tests/test-coroutine.c +++ b/tests/test-coroutine.c @@ -369,7 +369,7 @@ static void perf_lifecycle(void) } duration = g_test_timer_elapsed(); - g_test_message("Lifecycle %u iterations: %f s\n", max, duration); + g_test_message("Lifecycle %u iterations: %f s", max, duration); } static void perf_nesting(void) @@ -393,7 +393,7 @@ static void perf_nesting(void) } duration = g_test_timer_elapsed(); - g_test_message("Nesting %u iterations of %u depth each: %f s\n", + g_test_message("Nesting %u iterations of %u depth each: %f s", maxcycles, maxnesting, duration); } @@ -426,8 +426,7 @@ static void perf_yield(void) } duration = g_test_timer_elapsed(); - g_test_message("Yield %u iterations: %f s\n", - maxcycles, duration); + g_test_message("Yield %u iterations: %f s", maxcycles, duration); } static __attribute__((noinline)) void dummy(unsigned *i) @@ -449,8 +448,7 @@ static void perf_baseline(void) } duration = g_test_timer_elapsed(); - g_test_message("Function call %u iterations: %f s\n", - maxcycles, duration); + g_test_message("Function call %u iterations: %f s", maxcycles, duration); } static __attribute__((noinline)) void perf_cost_func(void *opaque) diff --git a/tests/test-crypto-block.c b/tests/test-crypto-block.c index fd29a045d2..d309d044ef 100644 --- a/tests/test-crypto-block.c +++ b/tests/test-crypto-block.c @@ -29,7 +29,7 @@ #endif #if (defined(_WIN32) || defined RUSAGE_THREAD) && \ - (defined(CONFIG_NETTLE_KDF) || defined(CONFIG_GCRYPT_KDF)) + (defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)) #define TEST_LUKS #else #undef TEST_LUKS @@ -305,6 +305,7 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, + 1, NULL); g_assert(blk == NULL); @@ -313,6 +314,7 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, QCRYPTO_BLOCK_OPEN_NO_IO, + 1, &error_abort); g_assert(qcrypto_block_get_cipher(blk) == NULL); @@ -327,6 +329,7 @@ static void test_block(gconstpointer opaque) test_block_read_func, &header, 0, + 1, &error_abort); g_assert(blk); diff --git a/tests/test-crypto-pbkdf.c b/tests/test-crypto-pbkdf.c index d937aff6b2..85ed1f9b33 100644 --- a/tests/test-crypto-pbkdf.c +++ b/tests/test-crypto-pbkdf.c @@ -440,6 +440,7 @@ int main(int argc, char **argv) #else int main(int argc, char **argv) { - return 0; + g_test_init(&argc, &argv, NULL); + return g_test_run(); } #endif diff --git a/tests/test-crypto-tlscredsx509.c b/tests/test-crypto-tlscredsx509.c index 30f9ac4bbf..940a026c6e 100644 --- a/tests/test-crypto-tlscredsx509.c +++ b/tests/test-crypto-tlscredsx509.c @@ -283,14 +283,8 @@ int main(int argc, char **argv) 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); + true); TLS_TEST_REG(badca2, true, cacert5req.filename, servercert5req.filename, true); TLS_TEST_REG(badca3, true, diff --git a/tests/test-crypto-tlssession.c b/tests/test-crypto-tlssession.c index 6fa9950afb..15212ec276 100644 --- a/tests/test-crypto-tlssession.c +++ b/tests/test-crypto-tlssession.c @@ -28,7 +28,7 @@ #include "qom/object_interfaces.h" #include "qapi/error.h" #include "qemu/sockets.h" -#include "qemu/acl.h" +#include "authz/list.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -229,7 +229,7 @@ static void test_crypto_tls_session_x509(const void *opaque) QCryptoTLSCreds *serverCreds; QCryptoTLSSession *clientSess = NULL; QCryptoTLSSession *serverSess = NULL; - qemu_acl *acl; + QAuthZList *auth; const char * const *wildcards; int channel[2]; bool clientShake = false; @@ -285,11 +285,15 @@ static void test_crypto_tls_session_x509(const void *opaque) SERVER_CERT_DIR); g_assert(serverCreds != NULL); - acl = qemu_acl_init("tlssessionacl"); - qemu_acl_reset(acl); + auth = qauthz_list_new("tlssessionacl", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); wildcards = data->wildcards; while (wildcards && *wildcards) { - qemu_acl_append(acl, 0, *wildcards); + qauthz_list_append_rule(auth, *wildcards, + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &error_abort); wildcards++; } @@ -377,6 +381,7 @@ static void test_crypto_tls_session_x509(const void *opaque) object_unparent(OBJECT(serverCreds)); object_unparent(OBJECT(clientCreds)); + object_unparent(OBJECT(auth)); qcrypto_tls_session_free(serverSess); qcrypto_tls_session_free(clientSess); diff --git a/tests/test-crypto-xts.c b/tests/test-crypto-xts.c index 1f1412c45a..6fb61cf635 100644 --- a/tests/test-crypto-xts.c +++ b/tests/test-crypto-xts.c @@ -1,7 +1,7 @@ /* * QEMU Crypto XTS cipher mode * - * Copyright (c) 2015-2016 Red Hat, Inc. + * Copyright (c) 2015-2018 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 @@ -340,70 +340,161 @@ static void test_xts_aes_decrypt(const void *ctx, static void test_xts(const void *opaque) { const QCryptoXTSTestData *data = opaque; - unsigned char out[512], Torg[16], T[16]; + uint8_t 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); - 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); - seq = data->seqnum; - STORE64L(seq, Torg); - memset(Torg + 8, 0, 8); + memcpy(T, Torg, sizeof(T)); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, data->PTX); - 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); - g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + memcpy(T, Torg, sizeof(T)); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, data->CTX); - 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); +} - g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); - } + +static void test_xts_split(const void *opaque) +{ + const QCryptoXTSTestData *data = opaque; + uint8_t out[512], Torg[16], T[16]; + uint64_t seq; + unsigned long len = data->PTLEN / 2; + struct TestAES aesdata; + struct TestAES aestweak; + + 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)); + 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)); + 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); +} + + +static void test_xts_unaligned(const void *opaque) +{ +#define BAD_ALIGN 3 + const QCryptoXTSTestData *data = opaque; + uint8_t in[512 + BAD_ALIGN], out[512 + BAD_ALIGN]; + uint8_t Torg[16], T[16 + BAD_ALIGN]; + uint64_t seq; + struct TestAES aesdata; + struct TestAES aestweak; + + 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); + + /* IV not aligned */ + memcpy(T + BAD_ALIGN, Torg, 16); + memcpy(in, data->PTX, data->PTLEN); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T + BAD_ALIGN, data->PTLEN, out, in); + + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + + /* plain text not aligned */ + memcpy(T, Torg, 16); + memcpy(in + BAD_ALIGN, data->PTX, data->PTLEN); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, in + BAD_ALIGN); + + g_assert(memcmp(out, data->CTX, data->PTLEN) == 0); + + /* cipher text not aligned */ + memcpy(T, Torg, 16); + memcpy(in, data->PTX, data->PTLEN); + xts_encrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out + BAD_ALIGN, in); + + g_assert(memcmp(out + BAD_ALIGN, data->CTX, data->PTLEN) == 0); + + + /* IV not aligned */ + memcpy(T + BAD_ALIGN, Torg, 16); + memcpy(in, data->CTX, data->PTLEN); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T + BAD_ALIGN, data->PTLEN, out, in); + + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); + + /* cipher text not aligned */ + memcpy(T, Torg, 16); + memcpy(in + BAD_ALIGN, data->CTX, data->PTLEN); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out, in + BAD_ALIGN); + + g_assert(memcmp(out, data->PTX, data->PTLEN) == 0); + + /* plain text not aligned */ + memcpy(T, Torg, 16); + memcpy(in, data->CTX, data->PTLEN); + xts_decrypt(&aesdata, &aestweak, + test_xts_aes_encrypt, + test_xts_aes_decrypt, + T, data->PTLEN, out + BAD_ALIGN, in); + + g_assert(memcmp(out + BAD_ALIGN, data->PTX, data->PTLEN) == 0); } @@ -416,7 +507,22 @@ 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_xts); + gchar *path = g_strdup_printf("%s/basic", test_data[i].path); + g_test_add_data_func(path, &test_data[i], test_xts); + g_free(path); + + /* skip the cases where the length is smaller than 2*blocklen + * or the length is not a multiple of 32 + */ + if ((test_data[i].PTLEN >= 32) && !(test_data[i].PTLEN % 32)) { + path = g_strdup_printf("%s/split", test_data[i].path); + g_test_add_data_func(path, &test_data[i], test_xts_split); + g_free(path); + } + + path = g_strdup_printf("%s/unaligned", test_data[i].path); + g_test_add_data_func(path, &test_data[i], test_xts_unaligned); + g_free(path); } return g_test_run(); diff --git a/tests/test-cutils.c b/tests/test-cutils.c index d85c3e0f6d..1aa8351520 100644 --- a/tests/test-cutils.c +++ b/tests/test-cutils.c @@ -1950,7 +1950,7 @@ static void test_qemu_strtou64_full_max(void) static void test_qemu_strtosz_simple(void) { const char *str; - char *endptr = NULL; + const char *endptr; int err; uint64_t res = 0xbaadf00d; @@ -2017,7 +2017,7 @@ static void test_qemu_strtosz_units(void) const char *p = "1P"; const char *e = "1E"; int err; - char *endptr = NULL; + const char *endptr; uint64_t res = 0xbaadf00d; /* default is M */ @@ -2066,7 +2066,7 @@ static void test_qemu_strtosz_float(void) { const char *str = "12.345M"; int err; - char *endptr = NULL; + const char *endptr; uint64_t res = 0xbaadf00d; err = qemu_strtosz(str, &endptr, &res); @@ -2078,7 +2078,7 @@ static void test_qemu_strtosz_float(void) static void test_qemu_strtosz_invalid(void) { const char *str; - char *endptr = NULL; + const char *endptr; int err; uint64_t res = 0xbaadf00d; @@ -2096,12 +2096,22 @@ static void test_qemu_strtosz_invalid(void) err = qemu_strtosz(str, &endptr, &res); g_assert_cmpint(err, ==, -EINVAL); g_assert(endptr == str); + + str = "inf"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); + + str = "NaN"; + err = qemu_strtosz(str, &endptr, &res); + g_assert_cmpint(err, ==, -EINVAL); + g_assert(endptr == str); } static void test_qemu_strtosz_trailing(void) { const char *str; - char *endptr = NULL; + const char *endptr; int err; uint64_t res = 0xbaadf00d; @@ -2126,7 +2136,7 @@ static void test_qemu_strtosz_trailing(void) static void test_qemu_strtosz_erange(void) { const char *str; - char *endptr = NULL; + const char *endptr; int err; uint64_t res = 0xbaadf00d; @@ -2160,7 +2170,7 @@ static void test_qemu_strtosz_metric(void) { const char *str = "12345k"; int err; - char *endptr = NULL; + const char *endptr; uint64_t res = 0xbaadf00d; err = qemu_strtosz_metric(str, &endptr, &res); diff --git a/tests/test-filter-mirror.c b/tests/test-filter-mirror.c index 6c6f710dc6..d942c74a3f 100644 --- a/tests/test-filter-mirror.c +++ b/tests/test-filter-mirror.c @@ -10,21 +10,25 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "qapi/qmp/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) + static void test_mirror(void) { - int send_sock[2], recv_sock; + int send_sock[2], recv_sock[2]; 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); const char *devstr = "e1000"; + QTestState *qts; if (g_str_equal(qtest_get_arch(), "s390x")) { devstr = "virtio-net-ccw"; @@ -33,18 +37,15 @@ static void test_mirror(void) ret = socketpair(PF_UNIX, SOCK_STREAM, 0, send_sock); g_assert_cmpint(ret, !=, -1); - ret = mkstemp(sock_path); + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, recv_sock); g_assert_cmpint(ret, !=, -1); - global_qtest = qtest_startf( + qts = qtest_initf( "-netdev socket,id=qtest-bn0,fd=%d " "-device %s,netdev=qtest-bn0,id=qtest-e0 " - "-chardev socket,id=mirror0,path=%s,server,nowait " + "-chardev socket,id=mirror0,fd=%d " "-object filter-mirror,id=qtest-f0,netdev=qtest-bn0,queue=tx,outdev=mirror0 " - , send_sock[1], devstr, sock_path); - - recv_sock = unix_connect(sock_path, NULL); - g_assert_cmpint(recv_sock, !=, -1); + , send_sock[1], devstr, recv_sock[1]); struct iovec iov[] = { { @@ -57,23 +58,26 @@ static void test_mirror(void) }; /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response("{ 'execute' : 'query-status'}"); + qmp_discard_response(qts, "{ '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); + ret = qemu_recv(recv_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(recv_sock, recv_buf, len, 0); + ret = qemu_recv(recv_sock[0], recv_buf, len, 0); g_assert_cmpstr(recv_buf, ==, send_buf); g_free(recv_buf); - close(recv_sock); - unlink(sock_path); + close(send_sock[0]); + close(send_sock[1]); + close(recv_sock[0]); + close(recv_sock[1]); + qtest_quit(qts); } int main(int argc, char **argv) @@ -84,7 +88,6 @@ int main(int argc, char **argv) qtest_add_func("/netfilter/mirror", test_mirror); ret = g_test_run(); - qtest_end(); return ret; } diff --git a/tests/test-filter-redirector.c b/tests/test-filter-redirector.c index fbaf19bbd8..6dc21dd4fb 100644 --- a/tests/test-filter-redirector.c +++ b/tests/test-filter-redirector.c @@ -52,11 +52,15 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "qapi/qmp/qdict.h" #include "qemu/iov.h" #include "qemu/sockets.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(qs, ...) qobject_unref(qtest_qmp(qs, __VA_ARGS__)) + static const char *get_devstr(void) { if (g_str_equal(qtest_get_arch(), "s390x")) { @@ -77,6 +81,7 @@ static void test_redirector_tx(void) char *recv_buf; uint32_t size = sizeof(send_buf); size = htonl(size); + QTestState *qts; ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); g_assert_cmpint(ret, !=, -1); @@ -86,12 +91,12 @@ static void test_redirector_tx(void) ret = mkstemp(sock_path1); g_assert_cmpint(ret, !=, -1); - global_qtest = qtest_startf( + qts = qtest_initf( "-netdev socket,id=qtest-bn0,fd=%d " "-device %s,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 " + "-chardev socket,id=redirector2,path=%s " "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," "queue=tx,outdev=redirector0 " "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," @@ -104,7 +109,7 @@ static void test_redirector_tx(void) g_assert_cmpint(recv_sock, !=, -1); /* send a qmp command to guarantee that 'connected' is setting to true. */ - qmp_discard_response("{ 'execute' : 'query-status'}"); + qmp_discard_response(qts, "{ 'execute' : 'query-status'}"); struct iovec iov[] = { { @@ -133,7 +138,7 @@ static void test_redirector_tx(void) close(recv_sock); unlink(sock_path0); unlink(sock_path1); - qtest_end(); + qtest_quit(qts); } static void test_redirector_rx(void) @@ -146,6 +151,7 @@ static void test_redirector_rx(void) char *recv_buf; uint32_t size = sizeof(send_buf); size = htonl(size); + QTestState *qts; ret = socketpair(PF_UNIX, SOCK_STREAM, 0, backend_sock); g_assert_cmpint(ret, !=, -1); @@ -155,12 +161,12 @@ static void test_redirector_rx(void) ret = mkstemp(sock_path1); g_assert_cmpint(ret, !=, -1); - global_qtest = qtest_startf( + qts = qtest_initf( "-netdev socket,id=qtest-bn0,fd=%d " "-device %s,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 " + "-chardev socket,id=redirector2,path=%s " "-object filter-redirector,id=qtest-f0,netdev=qtest-bn0," "queue=rx,indev=redirector0 " "-object filter-redirector,id=qtest-f1,netdev=qtest-bn0," @@ -182,7 +188,7 @@ static void test_redirector_rx(void) 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_discard_response("{ 'execute' : 'query-status'}"); + qmp_discard_response(qts, "{ '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)); @@ -200,7 +206,7 @@ static void test_redirector_rx(void) g_free(recv_buf); unlink(sock_path0); unlink(sock_path1); - qtest_end(); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/test-hbitmap.c b/tests/test-hbitmap.c index 5e67ac1d3a..592d8219db 100644 --- a/tests/test-hbitmap.c +++ b/tests/test-hbitmap.c @@ -30,18 +30,6 @@ typedef struct TestHBitmapData { } TestHBitmapData; -static int64_t check_hbitmap_iter_next(HBitmapIter *hbi) -{ - int next0, next1; - - next0 = hbitmap_iter_next(hbi, false); - next1 = hbitmap_iter_next(hbi, true); - - g_assert_cmpint(next0, ==, next1); - - return next0; -} - /* Check that the HBitmap and the shadow bitmap contain the same data, * ignoring the same "first" bits. */ @@ -58,7 +46,7 @@ static void hbitmap_test_check(TestHBitmapData *data, i = first; for (;;) { - next = check_hbitmap_iter_next(&hbi); + next = hbitmap_iter_next(&hbi); if (next < 0) { next = data->size; } @@ -447,25 +435,25 @@ static void test_hbitmap_iter_granularity(TestHBitmapData *data, /* Note that hbitmap_test_check has to be invoked manually in this test. */ hbitmap_test_init(data, 131072 << 7, 7); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); hbitmap_test_set(data, ((L2 + L1 + 1) << 7) + 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(hbitmap_iter_next(&hbi, true), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); hbitmap_test_set(data, (131072 << 7) - 8, 8); hbitmap_iter_init(&hbi, data->hb, 0); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, (L2 + L1 + 1) << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); hbitmap_iter_init(&hbi, data->hb, (L2 + L1 + 2) << 7); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), ==, 131071 << 7); - g_assert_cmpint(check_hbitmap_iter_next(&hbi), <, 0); + g_assert_cmpint(hbitmap_iter_next(&hbi), ==, 131071 << 7); + g_assert_cmpint(hbitmap_iter_next(&hbi), <, 0); } static void hbitmap_test_set_boundary_bits(TestHBitmapData *data, ssize_t diff) @@ -905,7 +893,7 @@ static void test_hbitmap_serialize_zeroes(TestHBitmapData *data, for (i = 0; i < num_positions; i++) { hbitmap_deserialize_zeroes(data->hb, positions[i], min_l1, true); hbitmap_iter_init(&iter, data->hb, 0); - next = check_hbitmap_iter_next(&iter); + next = hbitmap_iter_next(&iter); if (i == num_positions - 1) { g_assert_cmpint(next, ==, -1); } else { @@ -931,37 +919,55 @@ static void test_hbitmap_iter_and_reset(TestHBitmapData *data, hbitmap_iter_init(&hbi, data->hb, BITS_PER_LONG - 1); - check_hbitmap_iter_next(&hbi); + hbitmap_iter_next(&hbi); hbitmap_reset_all(data->hb); - check_hbitmap_iter_next(&hbi); + hbitmap_iter_next(&hbi); } -static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) +static void test_hbitmap_next_zero_check_range(TestHBitmapData *data, + uint64_t start, + uint64_t count) { - int64_t ret1 = hbitmap_next_zero(data->hb, start); + int64_t ret1 = hbitmap_next_zero(data->hb, start, count); int64_t ret2 = start; - for ( ; ret2 < data->size && hbitmap_get(data->hb, ret2); ret2++) { + int64_t end = start >= data->size || data->size - start < count ? + data->size : start + count; + + for ( ; ret2 < end && hbitmap_get(data->hb, ret2); ret2++) { ; } - if (ret2 == data->size) { + if (ret2 == end) { ret2 = -1; } g_assert_cmpint(ret1, ==, ret2); } +static void test_hbitmap_next_zero_check(TestHBitmapData *data, int64_t start) +{ + test_hbitmap_next_zero_check_range(data, start, UINT64_MAX); +} + static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) { hbitmap_test_init(data, L3, granularity); test_hbitmap_next_zero_check(data, 0); test_hbitmap_next_zero_check(data, L3 - 1); + test_hbitmap_next_zero_check_range(data, 0, 1); + test_hbitmap_next_zero_check_range(data, L3 - 1, 1); hbitmap_set(data->hb, L2, 1); test_hbitmap_next_zero_check(data, 0); test_hbitmap_next_zero_check(data, L2 - 1); test_hbitmap_next_zero_check(data, L2); test_hbitmap_next_zero_check(data, L2 + 1); + test_hbitmap_next_zero_check_range(data, 0, 1); + test_hbitmap_next_zero_check_range(data, 0, L2); + test_hbitmap_next_zero_check_range(data, L2 - 1, 1); + test_hbitmap_next_zero_check_range(data, L2 - 1, 2); + test_hbitmap_next_zero_check_range(data, L2, 1); + test_hbitmap_next_zero_check_range(data, L2 + 1, 1); hbitmap_set(data->hb, L2 + 5, L1); test_hbitmap_next_zero_check(data, 0); @@ -970,6 +976,10 @@ static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) test_hbitmap_next_zero_check(data, L2 + 5); test_hbitmap_next_zero_check(data, L2 + L1 - 1); test_hbitmap_next_zero_check(data, L2 + L1); + test_hbitmap_next_zero_check_range(data, L2, 6); + test_hbitmap_next_zero_check_range(data, L2 + 1, 3); + test_hbitmap_next_zero_check_range(data, L2 + 4, L1); + test_hbitmap_next_zero_check_range(data, L2 + 5, L1); hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); test_hbitmap_next_zero_check(data, L2 * 2 - L1); @@ -977,6 +987,8 @@ static void test_hbitmap_next_zero_do(TestHBitmapData *data, int granularity) test_hbitmap_next_zero_check(data, L2 * 2 - 1); test_hbitmap_next_zero_check(data, L2 * 2); test_hbitmap_next_zero_check(data, L3 - 1); + test_hbitmap_next_zero_check_range(data, L2 * 2 - L1, L1 + 1); + test_hbitmap_next_zero_check_range(data, L2 * 2, L2); hbitmap_set(data->hb, 0, L3); test_hbitmap_next_zero_check(data, 0); @@ -992,6 +1004,106 @@ static void test_hbitmap_next_zero_4(TestHBitmapData *data, const void *unused) test_hbitmap_next_zero_do(data, 4); } +static void test_hbitmap_next_dirty_area_check(TestHBitmapData *data, + uint64_t offset, + uint64_t count) +{ + uint64_t off1, off2; + uint64_t len1 = 0, len2; + bool ret1, ret2; + int64_t end; + + off1 = offset; + len1 = count; + ret1 = hbitmap_next_dirty_area(data->hb, &off1, &len1); + + end = offset > data->size || data->size - offset < count ? data->size : + offset + count; + + for (off2 = offset; off2 < end && !hbitmap_get(data->hb, off2); off2++) { + ; + } + + for (len2 = 1; off2 + len2 < end && hbitmap_get(data->hb, off2 + len2); + len2++) { + ; + } + + ret2 = off2 < end; + if (!ret2) { + /* leave unchanged */ + off2 = offset; + len2 = count; + } + + g_assert_cmpint(ret1, ==, ret2); + g_assert_cmpint(off1, ==, off2); + g_assert_cmpint(len1, ==, len2); +} + +static void test_hbitmap_next_dirty_area_do(TestHBitmapData *data, + int granularity) +{ + hbitmap_test_init(data, L3, granularity); + test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, 0, 1); + test_hbitmap_next_dirty_area_check(data, L3 - 1, 1); + + hbitmap_set(data->hb, L2, 1); + test_hbitmap_next_dirty_area_check(data, 0, 1); + test_hbitmap_next_dirty_area_check(data, 0, L2); + test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 1, 1); + test_hbitmap_next_dirty_area_check(data, L2 - 1, 2); + test_hbitmap_next_dirty_area_check(data, L2 - 1, 3); + test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, 1); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 1); + + hbitmap_set(data->hb, L2 + 5, L1); + test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 - 2, 8); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 5); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 3); + test_hbitmap_next_dirty_area_check(data, L2 + 4, L1); + test_hbitmap_next_dirty_area_check(data, L2 + 5, L1); + test_hbitmap_next_dirty_area_check(data, L2 + 7, L1); + test_hbitmap_next_dirty_area_check(data, L2 + L1, L1); + test_hbitmap_next_dirty_area_check(data, L2, 0); + test_hbitmap_next_dirty_area_check(data, L2 + 1, 0); + + hbitmap_set(data->hb, L2 * 2, L3 - L2 * 2); + test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1 - 1, UINT64_MAX); + test_hbitmap_next_dirty_area_check(data, L2 + 5 + L1, 5); + test_hbitmap_next_dirty_area_check(data, L2 * 2 - L1, L1 + 1); + test_hbitmap_next_dirty_area_check(data, L2 * 2, L2); + + hbitmap_set(data->hb, 0, L3); + test_hbitmap_next_dirty_area_check(data, 0, UINT64_MAX); +} + +static void test_hbitmap_next_dirty_area_0(TestHBitmapData *data, + const void *unused) +{ + test_hbitmap_next_dirty_area_do(data, 0); +} + +static void test_hbitmap_next_dirty_area_1(TestHBitmapData *data, + const void *unused) +{ + test_hbitmap_next_dirty_area_do(data, 1); +} + +static void test_hbitmap_next_dirty_area_4(TestHBitmapData *data, + const void *unused) +{ + test_hbitmap_next_dirty_area_do(data, 4); +} + int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); @@ -1058,6 +1170,13 @@ int main(int argc, char **argv) hbitmap_test_add("/hbitmap/next_zero/next_zero_4", test_hbitmap_next_zero_4); + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_0", + test_hbitmap_next_dirty_area_0); + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_1", + test_hbitmap_next_dirty_area_1); + hbitmap_test_add("/hbitmap/next_dirty_area/next_dirty_area_4", + test_hbitmap_next_dirty_area_4); + g_test_run(); return 0; diff --git a/tests/test-hmp.c b/tests/test-hmp.c index 5352c9c088..54a01824dc 100644 --- a/tests/test-hmp.c +++ b/tests/test-hmp.c @@ -20,6 +20,7 @@ static int verbose; static const char *hmp_cmds[] = { + "announce_self", "boot_set ndc", "chardev-add null,id=testchardev1", "chardev-send-break testchardev1", @@ -30,6 +31,7 @@ static const char *hmp_cmds[] = { "cpu 0", "device_add ?", "device_add usb-mouse,id=mouse1", + "drive_add ignored format=help", "mouse_button 7", "mouse_move 10 10", "mouse_button 0", @@ -158,7 +160,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); - qtest_cb_for_every_machine(add_machine_test_case); + qtest_cb_for_every_machine(add_machine_test_case, g_test_quick()); /* as none machine has no memory by default, add a test case with memory */ qtest_add_data_func("hmp/none+2MB", g_strdup("none -m 2"), test_machine); diff --git a/tests/test-image-locking.c b/tests/test-image-locking.c new file mode 100644 index 0000000000..7614cbf90c --- /dev/null +++ b/tests/test-image-locking.c @@ -0,0 +1,157 @@ +/* + * Image locking tests + * + * Copyright (c) 2018 Red Hat Inc. + * + * Author: Fam Zheng + * + * 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 "block/block.h" +#include "sysemu/block-backend.h" +#include "qapi/error.h" +#include "qapi/qmp/qdict.h" + +static BlockBackend *open_image(const char *path, + uint64_t perm, uint64_t shared_perm, + Error **errp) +{ + Error *local_err = NULL; + BlockBackend *blk; + QDict *options = qdict_new(); + + qdict_put_str(options, "driver", "raw"); + blk = blk_new_open(path, NULL, options, BDRV_O_RDWR, &local_err); + if (blk) { + g_assert_null(local_err); + if (blk_set_perm(blk, perm, shared_perm, errp)) { + blk_unref(blk); + blk = NULL; + } + } else { + error_propagate(errp, local_err); + } + return blk; +} + +static void check_locked_bytes(int fd, uint64_t perm_locks, + uint64_t shared_perm_locks) +{ + int i; + + if (!perm_locks && !shared_perm_locks) { + g_assert(!qemu_lock_fd_test(fd, 0, 0, true)); + return; + } + for (i = 0; (1ULL << i) <= BLK_PERM_ALL; i++) { + uint64_t bit = (1ULL << i); + bool perm_expected = !!(bit & perm_locks); + bool shared_perm_expected = !!(bit & shared_perm_locks); + g_assert_cmpint(perm_expected, ==, + !!qemu_lock_fd_test(fd, 100 + i, 1, true)); + g_assert_cmpint(shared_perm_expected, ==, + !!qemu_lock_fd_test(fd, 200 + i, 1, true)); + } +} + +static void test_image_locking_basic(void) +{ + BlockBackend *blk1, *blk2, *blk3; + char img_path[] = "/tmp/qtest.XXXXXX"; + uint64_t perm, shared_perm; + + int fd = mkstemp(img_path); + assert(fd >= 0); + + perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; + shared_perm = BLK_PERM_ALL; + blk1 = open_image(img_path, perm, shared_perm, &error_abort); + g_assert(blk1); + + check_locked_bytes(fd, perm, ~shared_perm); + + /* compatible perm between blk1 and blk2 */ + blk2 = open_image(img_path, perm | BLK_PERM_RESIZE, shared_perm, NULL); + g_assert(blk2); + check_locked_bytes(fd, perm | BLK_PERM_RESIZE, ~shared_perm); + + /* incompatible perm with already open blk1 and blk2 */ + blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, NULL); + g_assert_null(blk3); + + blk_unref(blk2); + + /* Check that extra bytes in blk2 are correctly unlocked */ + check_locked_bytes(fd, perm, ~shared_perm); + + blk_unref(blk1); + + /* Image is unused, no lock there */ + check_locked_bytes(fd, 0, 0); + blk3 = open_image(img_path, perm, BLK_PERM_WRITE_UNCHANGED, &error_abort); + g_assert(blk3); + blk_unref(blk3); + close(fd); + unlink(img_path); +} + +static void test_set_perm_abort(void) +{ + BlockBackend *blk1, *blk2; + char img_path[] = "/tmp/qtest.XXXXXX"; + uint64_t perm, shared_perm; + int r; + int fd = mkstemp(img_path); + assert(fd >= 0); + + perm = BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ; + shared_perm = BLK_PERM_ALL; + blk1 = open_image(img_path, perm, shared_perm, &error_abort); + g_assert(blk1); + + blk2 = open_image(img_path, perm, shared_perm, &error_abort); + g_assert(blk2); + + check_locked_bytes(fd, perm, ~shared_perm); + + /* A failed blk_set_perm mustn't change perm status (locked bytes) */ + r = blk_set_perm(blk2, perm | BLK_PERM_RESIZE, BLK_PERM_WRITE_UNCHANGED, + NULL); + g_assert_cmpint(r, !=, 0); + check_locked_bytes(fd, perm, ~shared_perm); + blk_unref(blk1); + blk_unref(blk2); +} + +int main(int argc, char **argv) +{ + bdrv_init(); + qemu_init_main_loop(&error_abort); + + g_test_init(&argc, &argv, NULL); + + if (qemu_has_ofd_lock()) { + g_test_add_func("/image-locking/basic", test_image_locking_basic); + g_test_add_func("/image-locking/set-perm-abort", test_set_perm_abort); + } + + return g_test_run(); +} diff --git a/tests/test-io-channel-socket.c b/tests/test-io-channel-socket.c index 0597213f93..c253ae30f5 100644 --- a/tests/test-io-channel-socket.c +++ b/tests/test-io-channel-socket.c @@ -49,6 +49,7 @@ static void test_io_channel_set_socket_bufs(QIOChannel *src, static void test_io_channel_setup_sync(SocketAddress *listen_addr, SocketAddress *connect_addr, + QIOChannel **srv, QIOChannel **src, QIOChannel **dst) { @@ -78,7 +79,7 @@ static void test_io_channel_setup_sync(SocketAddress *listen_addr, test_io_channel_set_socket_bufs(*src, *dst); - object_unref(OBJECT(lioc)); + *srv = QIO_CHANNEL(lioc); } @@ -99,6 +100,7 @@ static void test_io_channel_complete(QIOTask *task, static void test_io_channel_setup_async(SocketAddress *listen_addr, SocketAddress *connect_addr, + QIOChannel **srv, QIOChannel **src, QIOChannel **dst) { @@ -146,21 +148,34 @@ static void test_io_channel_setup_async(SocketAddress *listen_addr, qio_channel_set_delay(*src, false); test_io_channel_set_socket_bufs(*src, *dst); - object_unref(OBJECT(lioc)); + *srv = QIO_CHANNEL(lioc); g_main_loop_unref(data.loop); } +static void test_io_channel_socket_path_exists(SocketAddress *addr, + bool expectExists) +{ + if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) { + return; + } + + g_assert(g_file_test(addr->u.q_unix.path, + G_FILE_TEST_EXISTS) == expectExists); +} + + static void test_io_channel(bool async, SocketAddress *listen_addr, SocketAddress *connect_addr, bool passFD) { - QIOChannel *src, *dst; + QIOChannel *src, *dst, *srv; QIOChannelTest *test; if (async) { - test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst); + test_io_channel_setup_async(listen_addr, connect_addr, + &srv, &src, &dst); g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); @@ -169,14 +184,25 @@ static void test_io_channel(bool async, g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); + test_io_channel_socket_path_exists(listen_addr, true); + test = qio_channel_test_new(); qio_channel_test_run_threads(test, true, src, dst); qio_channel_test_validate(test); + test_io_channel_socket_path_exists(listen_addr, true); + + /* unref without close, to ensure finalize() cleans up */ + object_unref(OBJECT(src)); object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); - test_io_channel_setup_async(listen_addr, connect_addr, &src, &dst); + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); + + test_io_channel_setup_async(listen_addr, connect_addr, + &srv, &src, &dst); g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); @@ -189,10 +215,24 @@ static void test_io_channel(bool async, qio_channel_test_run_threads(test, false, src, dst); qio_channel_test_validate(test); + /* close before unref, to ensure finalize copes with already closed */ + + qio_channel_close(src, &error_abort); + qio_channel_close(dst, &error_abort); + test_io_channel_socket_path_exists(listen_addr, true); + object_unref(OBJECT(src)); object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); + + qio_channel_close(srv, &error_abort); + test_io_channel_socket_path_exists(listen_addr, false); + + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); } else { - test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + test_io_channel_setup_sync(listen_addr, connect_addr, + &srv, &src, &dst); g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); @@ -201,14 +241,25 @@ static void test_io_channel(bool async, g_assert(qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_SHUTDOWN)); g_assert(qio_channel_has_feature(dst, QIO_CHANNEL_FEATURE_SHUTDOWN)); + test_io_channel_socket_path_exists(listen_addr, true); + test = qio_channel_test_new(); qio_channel_test_run_threads(test, true, src, dst); qio_channel_test_validate(test); + test_io_channel_socket_path_exists(listen_addr, true); + + /* unref without close, to ensure finalize() cleans up */ + object_unref(OBJECT(src)); object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); - test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); + + test_io_channel_setup_sync(listen_addr, connect_addr, + &srv, &src, &dst); g_assert(!passFD || qio_channel_has_feature(src, QIO_CHANNEL_FEATURE_FD_PASS)); @@ -221,8 +272,23 @@ static void test_io_channel(bool async, qio_channel_test_run_threads(test, false, src, dst); qio_channel_test_validate(test); + test_io_channel_socket_path_exists(listen_addr, true); + + /* close before unref, to ensure finalize copes with already closed */ + + qio_channel_close(src, &error_abort); + qio_channel_close(dst, &error_abort); + test_io_channel_socket_path_exists(listen_addr, true); + object_unref(OBJECT(src)); object_unref(OBJECT(dst)); + test_io_channel_socket_path_exists(listen_addr, true); + + qio_channel_close(srv, &error_abort); + test_io_channel_socket_path_exists(listen_addr, false); + + object_unref(OBJECT(srv)); + test_io_channel_socket_path_exists(listen_addr, false); } } @@ -316,7 +382,6 @@ static void test_io_channel_unix(bool async) qapi_free_SocketAddress(listen_addr); qapi_free_SocketAddress(connect_addr); - g_assert(g_file_test(TEST_SOCKET, G_FILE_TEST_EXISTS) == FALSE); } @@ -335,7 +400,7 @@ 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; + QIOChannel *src, *dst, *srv; int testfd; int fdsend[3]; int *fdrecv = NULL; @@ -359,7 +424,7 @@ static void test_io_channel_unix_fd_pass(void) connect_addr->type = SOCKET_ADDRESS_TYPE_UNIX; connect_addr->u.q_unix.path = g_strdup(TEST_SOCKET); - test_io_channel_setup_sync(listen_addr, connect_addr, &src, &dst); + test_io_channel_setup_sync(listen_addr, connect_addr, &srv, &src, &dst); memcpy(bufsend, "Hello World", G_N_ELEMENTS(bufsend)); @@ -412,6 +477,7 @@ static void test_io_channel_unix_fd_pass(void) object_unref(OBJECT(src)); object_unref(OBJECT(dst)); + object_unref(OBJECT(srv)); qapi_free_SocketAddress(listen_addr); qapi_free_SocketAddress(connect_addr); unlink(TEST_SOCKET); diff --git a/tests/test-io-channel-tls.c b/tests/test-io-channel-tls.c index 4900c6d433..43b707eba7 100644 --- a/tests/test-io-channel-tls.c +++ b/tests/test-io-channel-tls.c @@ -29,8 +29,8 @@ #include "io-channel-helpers.h" #include "crypto/init.h" #include "crypto/tlscredsx509.h" -#include "qemu/acl.h" #include "qapi/error.h" +#include "authz/list.h" #include "qom/object_interfaces.h" #ifdef QCRYPTO_HAVE_TLS_TEST_SUPPORT @@ -113,7 +113,7 @@ static void test_io_channel_tls(const void *opaque) QIOChannelTLS *serverChanTLS; QIOChannelSocket *clientChanSock; QIOChannelSocket *serverChanSock; - qemu_acl *acl; + QAuthZList *auth; const char * const *wildcards; int channel[2]; struct QIOChannelTLSHandshakeData clientHandshake = { false, false }; @@ -161,11 +161,15 @@ static void test_io_channel_tls(const void *opaque) SERVER_CERT_DIR); g_assert(serverCreds != NULL); - acl = qemu_acl_init("channeltlsacl"); - qemu_acl_reset(acl); + auth = qauthz_list_new("channeltlsacl", + QAUTHZ_LIST_POLICY_DENY, + &error_abort); wildcards = data->wildcards; while (wildcards && *wildcards) { - qemu_acl_append(acl, 0, *wildcards); + qauthz_list_append_rule(auth, *wildcards, + QAUTHZ_LIST_POLICY_ALLOW, + QAUTHZ_LIST_FORMAT_GLOB, + &error_abort); wildcards++; } @@ -253,6 +257,8 @@ static void test_io_channel_tls(const void *opaque) object_unref(OBJECT(serverChanSock)); object_unref(OBJECT(clientChanSock)); + object_unparent(OBJECT(auth)); + close(channel[0]); close(channel[1]); } diff --git a/tests/test-qdev-global-props.c b/tests/test-qdev-global-props.c index d81b0862d5..60231b1372 100644 --- a/tests/test-qdev-global-props.c +++ b/tests/test-qdev-global-props.c @@ -89,6 +89,16 @@ static void test_static_prop(void) g_test_trap_assert_stdout(""); } +static void register_global_properties(GlobalProperty *props) +{ + int i; + + for (i = 0; props[i].driver != NULL; i++) { + qdev_prop_register_global(props + i); + } +} + + /* Test setting of static property using global properties */ static void test_static_globalprop_subprocess(void) { @@ -98,7 +108,7 @@ static void test_static_globalprop_subprocess(void) {} }; - qdev_prop_register_global_list(props); + register_global_properties(props); mt = STATIC_TYPE(object_new(TYPE_STATIC_PROPS)); qdev_init_nofail(DEVICE(mt)); @@ -206,25 +216,25 @@ static void test_dynamic_globalprop_subprocess(void) { MyType *mt; static GlobalProperty props[] = { - { TYPE_DYNAMIC_PROPS, "prop1", "101", true }, - { TYPE_DYNAMIC_PROPS, "prop2", "102", true }, - { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", true }, - { TYPE_UNUSED_HOTPLUG, "prop4", "104", true }, - { TYPE_UNUSED_NOHOTPLUG, "prop5", "105", true }, - { TYPE_NONDEVICE, "prop6", "106", true }, + { TYPE_DYNAMIC_PROPS, "prop1", "101", }, + { TYPE_DYNAMIC_PROPS, "prop2", "102", }, + { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103", }, + { TYPE_UNUSED_HOTPLUG, "prop4", "104", }, + { TYPE_UNUSED_NOHOTPLUG, "prop5", "105", }, + { TYPE_NONDEVICE, "prop6", "106", }, {} }; - int all_used; + int global_error; - qdev_prop_register_global_list(props); + register_global_properties(props); mt = DYNAMIC_TYPE(object_new(TYPE_DYNAMIC_PROPS)); qdev_init_nofail(DEVICE(mt)); g_assert_cmpuint(mt->prop1, ==, 101); g_assert_cmpuint(mt->prop2, ==, 102); - all_used = qdev_prop_check_globals(); - g_assert_cmpuint(all_used, ==, 1); + global_error = qdev_prop_check_globals(); + g_assert_cmpuint(global_error, ==, 1); g_assert(props[0].used); g_assert(props[1].used); g_assert(!props[2].used); @@ -246,46 +256,6 @@ static void test_dynamic_globalprop(void) g_test_trap_assert_stdout(""); } -/* Test setting of dynamic properties using user_provided=false properties */ -static void test_dynamic_globalprop_nouser_subprocess(void) -{ - MyType *mt; - static GlobalProperty props[] = { - { TYPE_DYNAMIC_PROPS, "prop1", "101" }, - { TYPE_DYNAMIC_PROPS, "prop2", "102" }, - { TYPE_DYNAMIC_PROPS"-bad", "prop3", "103" }, - { TYPE_UNUSED_HOTPLUG, "prop4", "104" }, - { TYPE_UNUSED_NOHOTPLUG, "prop5", "105" }, - { TYPE_NONDEVICE, "prop6", "106" }, - {} - }; - int all_used; - - qdev_prop_register_global_list(props); - - mt = DYNAMIC_TYPE(object_new(TYPE_DYNAMIC_PROPS)); - qdev_init_nofail(DEVICE(mt)); - - g_assert_cmpuint(mt->prop1, ==, 101); - g_assert_cmpuint(mt->prop2, ==, 102); - all_used = qdev_prop_check_globals(); - g_assert_cmpuint(all_used, ==, 0); - g_assert(props[0].used); - g_assert(props[1].used); - g_assert(!props[2].used); - g_assert(!props[3].used); - g_assert(!props[4].used); - g_assert(!props[5].used); -} - -static void test_dynamic_globalprop_nouser(void) -{ - g_test_trap_subprocess("/qdev/properties/dynamic/global/nouser/subprocess", 0, 0); - g_test_trap_assert_passed(); - g_test_trap_assert_stderr(""); - g_test_trap_assert_stdout(""); -} - /* Test if global props affecting subclasses are applied in the right order */ static void test_subclass_global_props(void) { @@ -299,7 +269,7 @@ static void test_subclass_global_props(void) {} }; - qdev_prop_register_global_list(props); + register_global_properties(props); mt = STATIC_TYPE(object_new(TYPE_SUBCLASS)); qdev_init_nofail(DEVICE(mt)); @@ -335,11 +305,6 @@ int main(int argc, char **argv) g_test_add_func("/qdev/properties/dynamic/global", test_dynamic_globalprop); - g_test_add_func("/qdev/properties/dynamic/global/nouser/subprocess", - test_dynamic_globalprop_nouser_subprocess); - g_test_add_func("/qdev/properties/dynamic/global/nouser", - test_dynamic_globalprop_nouser); - g_test_add_func("/qdev/properties/global/subclass", test_subclass_global_props); diff --git a/tests/test-qga.c b/tests/test-qga.c index d638b1571a..891aa3d322 100644 --- a/tests/test-qga.c +++ b/tests/test-qga.c @@ -146,12 +146,12 @@ static void test_qga_sync_delimited(gconstpointer fix) guint32 v, r = g_random_int(); unsigned char c; QDict *ret; - gchar *cmd; - cmd = g_strdup_printf("\xff{'execute': 'guest-sync-delimited'," - " 'arguments': {'id': %u } }", r); - qmp_fd_send(fixture->fd, cmd); - g_free(cmd); + qmp_fd_send_raw(fixture->fd, "\xff"); + qmp_fd_send(fixture->fd, + "{'execute': 'guest-sync-delimited'," + " 'arguments': {'id': %u } }", + r); /* * Read and ignore garbage until resynchronized. @@ -188,7 +188,6 @@ static void test_qga_sync(gconstpointer fix) const TestFixture *fixture = fix; guint32 v, r = g_random_int(); QDict *ret; - gchar *cmd; /* * TODO guest-sync is inherently limited: we cannot distinguish @@ -201,10 +200,9 @@ static void test_qga_sync(gconstpointer fix) * invalid JSON. Testing of '\xff' handling is done in * guest-sync-delimited instead. */ - cmd = g_strdup_printf("{'execute': 'guest-sync'," - " 'arguments': {'id': %u } }", r); - ret = qmp_fd(fixture->fd, cmd); - g_free(cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-sync', 'arguments': {'id': %u } }", + r); g_assert_nonnull(ret); qmp_assert_no_error(ret); @@ -227,18 +225,15 @@ static void test_qga_ping(gconstpointer fix) qobject_unref(ret); } -static void test_qga_invalid_id(gconstpointer fix) +static void test_qga_id(gconstpointer fix) { const TestFixture *fixture = fix; - QDict *ret, *error; - const char *class; + QDict *ret; ret = qmp_fd(fixture->fd, "{'execute': 'guest-ping', 'id': 1}"); g_assert_nonnull(ret); - - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - g_assert_cmpstr(class, ==, "GenericError"); + qmp_assert_no_error(ret); + g_assert_cmpint(qdict_get_int(ret, "id"), ==, 1); qobject_unref(ret); } @@ -246,17 +241,12 @@ static void test_qga_invalid_id(gconstpointer fix) static void test_qga_invalid_oob(gconstpointer fix) { const TestFixture *fixture = fix; - QDict *ret, *error; - const char *class; + QDict *ret; ret = qmp_fd(fixture->fd, "{'exec-oob': 'guest-ping'}"); g_assert_nonnull(ret); - error = qdict_get_qdict(ret, "error"); - class = qdict_get_try_str(error, "class"); - g_assert_cmpstr(class, ==, "GenericError"); - - qobject_unref(ret); + qmp_assert_error_class(ret, "GenericError"); } static void test_qga_invalid_args(gconstpointer fix) @@ -428,7 +418,7 @@ 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; + gchar *path, *enc; unsigned char *dec; QDict *ret, *val; int64_t id, eof; @@ -446,10 +436,10 @@ static void test_qga_file_ops(gconstpointer fix) 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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" PRId64 ", 'buf-b64': %s } }", + id, enc); g_assert_nonnull(ret); qmp_assert_no_error(ret); @@ -459,23 +449,20 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert_cmpint(eof, ==, 0); qobject_unref(ret); - g_free(cmd); /* flush */ - cmd = g_strdup_printf("{'execute': 'guest-file-flush'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-flush'," + " 'arguments': {'handle': %" PRId64 "} }", + id); qobject_unref(ret); - g_free(cmd); /* close */ - cmd = g_strdup_printf("{'execute': 'guest-file-close'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); qobject_unref(ret); - g_free(cmd); /* check content */ path = g_build_filename(fixture->test_dir, "foo", NULL); @@ -497,10 +484,10 @@ static void test_qga_file_ops(gconstpointer fix) qobject_unref(ret); /* read */ - cmd = g_strdup_printf("{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "count"); eof = qdict_get_bool(val, "eof"); @@ -510,14 +497,13 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpstr(b64, ==, enc); qobject_unref(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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "count"); eof = qdict_get_bool(val, "eof"); @@ -526,14 +512,13 @@ static void test_qga_file_ops(gconstpointer fix) g_assert(eof); g_assert_cmpstr(b64, ==, ""); qobject_unref(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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" PRId64 ", " + " 'offset': %d, 'whence': %s } }", + id, 6, "set"); qmp_assert_no_error(ret); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "position"); @@ -541,13 +526,12 @@ static void test_qga_file_ops(gconstpointer fix) g_assert_cmpint(count, ==, 6); g_assert(!eof); qobject_unref(ret); - g_free(cmd); /* partial read */ - cmd = g_strdup_printf("{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "count"); eof = qdict_get_bool(val, "eof"); @@ -560,15 +544,13 @@ static void test_qga_file_ops(gconstpointer fix) g_free(dec); qobject_unref(ret); - g_free(cmd); /* close */ - cmd = g_strdup_printf("{'execute': 'guest-file-close'," - " 'arguments': {'handle': %" PRId64 "} }", - id); - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); qobject_unref(ret); - g_free(cmd); } static void test_qga_file_write_read(gconstpointer fix) @@ -576,7 +558,7 @@ 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; + gchar *enc; QDict *ret, *val; int64_t id, eof; gsize count; @@ -591,10 +573,10 @@ static void test_qga_file_write_read(gconstpointer fix) 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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-write'," + " 'arguments': { 'handle': %" PRId64 "," + " 'buf-b64': %s } }", id, enc); g_assert_nonnull(ret); qmp_assert_no_error(ret); @@ -604,13 +586,12 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_cmpint(count, ==, sizeof(helloworld)); g_assert_cmpint(eof, ==, 0); qobject_unref(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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "count"); eof = qdict_get_bool(val, "eof"); @@ -619,14 +600,13 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert(eof); g_assert_cmpstr(b64, ==, ""); qobject_unref(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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-seek'," + " 'arguments': { 'handle': %" PRId64 ", " + " 'offset': %d, 'whence': %s } }", + id, 0, "set"); qmp_assert_no_error(ret); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "position"); @@ -634,13 +614,12 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert_cmpint(count, ==, 0); g_assert(!eof); qobject_unref(ret); - g_free(cmd); /* read */ - cmd = g_strdup_printf("{'execute': 'guest-file-read'," - " 'arguments': { 'handle': %" PRId64 "} }", - id); - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-read'," + " 'arguments': { 'handle': %" PRId64 "} }", + id); val = qdict_get_qdict(ret, "return"); count = qdict_get_int(val, "count"); eof = qdict_get_bool(val, "eof"); @@ -649,16 +628,14 @@ static void test_qga_file_write_read(gconstpointer fix) g_assert(eof); g_assert_cmpstr(b64, ==, enc); qobject_unref(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); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-file-close'," + " 'arguments': {'handle': %" PRId64 "} }", + id); qobject_unref(ret); - g_free(cmd); } static void test_qga_get_time(gconstpointer fix) @@ -814,7 +791,6 @@ static void test_qga_guest_exec(gconstpointer fix) int64_t pid, now, exitcode; gsize len; bool exited; - char *cmd; /* exec 'echo foo bar' */ ret = qmp_fd(fixture->fd, "{'execute': 'guest-exec', 'arguments': {" @@ -829,10 +805,10 @@ static void test_qga_guest_exec(gconstpointer fix) /* wait for completion */ now = g_get_monotonic_time(); - cmd = g_strdup_printf("{'execute': 'guest-exec-status'," - " 'arguments': { 'pid': %" PRId64 " } }", pid); do { - ret = qmp_fd(fixture->fd, cmd); + ret = qmp_fd(fixture->fd, + "{'execute': 'guest-exec-status'," + " 'arguments': { 'pid': %" PRId64 " } }", pid); g_assert_nonnull(ret); val = qdict_get_qdict(ret, "return"); exited = qdict_get_bool(val, "exited"); @@ -842,7 +818,6 @@ static void test_qga_guest_exec(gconstpointer fix) } while (!exited && g_get_monotonic_time() < now + 5 * G_TIME_SPAN_SECOND); g_assert(exited); - g_free(cmd); /* check stdout */ exitcode = qdict_get_int(val, "exitcode"); @@ -1014,7 +989,7 @@ int main(int argc, char **argv) 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-id", &fix, test_qga_invalid_id); + g_test_add_data_func("/qga/id", &fix, test_qga_id); g_test_add_data_func("/qga/invalid-oob", &fix, test_qga_invalid_oob); g_test_add_data_func("/qga/invalid-cmd", &fix, test_qga_invalid_cmd); g_test_add_data_func("/qga/invalid-args", &fix, test_qga_invalid_args); diff --git a/tests/test-qgraph.c b/tests/test-qgraph.c new file mode 100644 index 0000000000..5c7e457075 --- /dev/null +++ b/tests/test-qgraph.c @@ -0,0 +1,435 @@ +/* + * libqos driver framework + * + * Copyright (c) 2018 Emanuele Giuseppe Esposito + * + * 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, see + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/qgraph_internal.h" + +#define MACHINE_PC "x86_64/pc" +#define MACHINE_RASPI2 "arm/raspi2" +#define I440FX "i440FX-pcihost" +#define PCIBUS_PC "pcibus-pc" +#define SDHCI "sdhci" +#define PCIBUS "pci-bus" +#define SDHCI_PCI "sdhci-pci" +#define SDHCI_MM "generic-sdhci" +#define REGISTER_TEST "register-test" + +int npath; + +static void *machinefunct(QTestState *qts) +{ + return NULL; +} + +static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) +{ + return NULL; +} + +static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) +{ + return; +} + +static void check_interface(const char *interface) +{ + g_assert_cmpint(qos_graph_has_machine(interface), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(interface)); + g_assert_cmpint(qos_graph_has_node(interface), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(interface), ==, QNODE_INTERFACE); + qos_graph_node_set_availability(interface, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(interface), ==, TRUE); +} + +static void check_machine(const char *machine) +{ + qos_node_create_machine(machine, machinefunct); + g_assert_nonnull(qos_graph_get_machine(machine)); + g_assert_cmpint(qos_graph_has_machine(machine), ==, TRUE); + g_assert_nonnull(qos_graph_get_node(machine)); + g_assert_cmpint(qos_graph_get_node_availability(machine), ==, FALSE); + qos_graph_node_set_availability(machine, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(machine), ==, TRUE); + g_assert_cmpint(qos_graph_has_node(machine), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(machine), ==, QNODE_MACHINE); +} + +static void check_contains(const char *machine, const char *driver) +{ + QOSGraphEdge *edge; + qos_node_contains(machine, driver, NULL); + + edge = qos_graph_get_edge(machine, driver); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONTAINS); + g_assert_cmpint(qos_graph_has_edge(machine, driver), ==, TRUE); +} + +static void check_produces(const char *machine, const char *interface) +{ + QOSGraphEdge *edge; + + qos_node_produces(machine, interface); + check_interface(interface); + edge = qos_graph_get_edge(machine, interface); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, + QEDGE_PRODUCES); + g_assert_cmpint(qos_graph_has_edge(machine, interface), ==, TRUE); +} + +static void check_consumes(const char *driver, const char *interface) +{ + QOSGraphEdge *edge; + + qos_node_consumes(driver, interface, NULL); + check_interface(interface); + edge = qos_graph_get_edge(interface, driver); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, QEDGE_CONSUMED_BY); + g_assert_cmpint(qos_graph_has_edge(interface, driver), ==, TRUE); +} + +static void check_driver(const char *driver) +{ + qos_node_create_driver(driver, driverfunct); + g_assert_cmpint(qos_graph_has_machine(driver), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(driver)); + g_assert_cmpint(qos_graph_has_node(driver), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(driver), ==, QNODE_DRIVER); + g_assert_cmpint(qos_graph_get_node_availability(driver), ==, FALSE); + qos_graph_node_set_availability(driver, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(driver), ==, TRUE); +} + +static void check_test(const char *test, const char *interface) +{ + QOSGraphEdge *edge; + char *full_name = g_strdup_printf("%s-tests/%s", interface, test); + + qos_add_test(test, interface, testfunct, NULL); + g_assert_cmpint(qos_graph_has_machine(test), ==, FALSE); + g_assert_cmpint(qos_graph_has_machine(full_name), ==, FALSE); + g_assert_nonnull(qos_graph_get_node(full_name)); + g_assert_cmpint(qos_graph_has_node(full_name), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_type(full_name), ==, QNODE_TEST); + edge = qos_graph_get_edge(interface, full_name); + g_assert_nonnull(edge); + g_assert_cmpint(qos_graph_edge_get_type(edge), ==, + QEDGE_CONSUMED_BY); + g_assert_cmpint(qos_graph_has_edge(interface, full_name), ==, TRUE); + g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, TRUE); + qos_graph_node_set_availability(full_name, FALSE); + g_assert_cmpint(qos_graph_get_node_availability(full_name), ==, FALSE); + g_free(full_name); +} + +static void count_each_test(QOSGraphNode *path, int len) +{ + npath++; +} + +static void check_leaf_discovered(int n) +{ + npath = 0; + qos_graph_foreach_test_path(count_each_test); + g_assert_cmpint(n, ==, npath); +} + +/* G_Test functions */ + +static void init_nop(void) +{ + qos_graph_init(); + qos_graph_destroy(); +} + +static void test_machine(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + qos_graph_destroy(); +} + +static void test_contains(void) +{ + qos_graph_init(); + check_contains(MACHINE_PC, I440FX); + g_assert_null(qos_graph_get_machine(MACHINE_PC)); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_node(MACHINE_PC)); + g_assert_null(qos_graph_get_node(I440FX)); + qos_graph_destroy(); +} + +static void test_multiple_contains(void) +{ + qos_graph_init(); + check_contains(MACHINE_PC, I440FX); + check_contains(MACHINE_PC, PCIBUS_PC); + qos_graph_destroy(); +} + +static void test_produces(void) +{ + qos_graph_init(); + check_produces(MACHINE_PC, I440FX); + g_assert_null(qos_graph_get_machine(MACHINE_PC)); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_node(MACHINE_PC)); + g_assert_nonnull(qos_graph_get_node(I440FX)); + qos_graph_destroy(); +} + +static void test_multiple_produces(void) +{ + qos_graph_init(); + check_produces(MACHINE_PC, I440FX); + check_produces(MACHINE_PC, PCIBUS_PC); + qos_graph_destroy(); +} + +static void test_consumes(void) +{ + qos_graph_init(); + check_consumes(I440FX, SDHCI); + g_assert_null(qos_graph_get_machine(I440FX)); + g_assert_null(qos_graph_get_machine(SDHCI)); + g_assert_null(qos_graph_get_node(I440FX)); + g_assert_nonnull(qos_graph_get_node(SDHCI)); + qos_graph_destroy(); +} + +static void test_multiple_consumes(void) +{ + qos_graph_init(); + check_consumes(I440FX, SDHCI); + check_consumes(PCIBUS_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver(void) +{ + qos_graph_init(); + check_driver(I440FX); + qos_graph_destroy(); +} + +static void test_test(void) +{ + qos_graph_init(); + check_test(REGISTER_TEST, SDHCI); + qos_graph_destroy(); +} + +static void test_machine_contains_driver(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_driver(I440FX); + check_contains(MACHINE_PC, I440FX); + qos_graph_destroy(); +} + +static void test_driver_contains_driver(void) +{ + qos_graph_init(); + check_driver(PCIBUS_PC); + check_driver(I440FX); + check_contains(PCIBUS_PC, I440FX); + qos_graph_destroy(); +} + +static void test_machine_produces_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_produces(MACHINE_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver_produces_interface(void) +{ + qos_graph_init(); + check_driver(I440FX); + check_produces(I440FX, SDHCI); + qos_graph_destroy(); +} + +static void test_machine_consumes_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_consumes(MACHINE_PC, SDHCI); + qos_graph_destroy(); +} + +static void test_driver_consumes_interface(void) +{ + qos_graph_init(); + check_driver(I440FX); + check_consumes(I440FX, SDHCI); + qos_graph_destroy(); +} + +static void test_test_consumes_interface(void) +{ + qos_graph_init(); + check_test(REGISTER_TEST, SDHCI); + qos_graph_destroy(); +} + +static void test_full_sample(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_contains(MACHINE_PC, I440FX); + check_driver(I440FX); + check_driver(PCIBUS_PC); + check_contains(I440FX, PCIBUS_PC); + check_produces(PCIBUS_PC, PCIBUS); + check_driver(SDHCI_PCI); + qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); + check_produces(SDHCI_PCI, SDHCI); + check_driver(SDHCI_MM); + check_produces(SDHCI_MM, SDHCI); + qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); + check_leaf_discovered(1); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_full_sample_raspi(void) +{ + qos_graph_init(); + check_machine(MACHINE_PC); + check_contains(MACHINE_PC, I440FX); + check_driver(I440FX); + check_driver(PCIBUS_PC); + check_contains(I440FX, PCIBUS_PC); + check_produces(PCIBUS_PC, PCIBUS); + check_driver(SDHCI_PCI); + qos_node_consumes(SDHCI_PCI, PCIBUS, NULL); + check_produces(SDHCI_PCI, SDHCI); + check_machine(MACHINE_RASPI2); + check_contains(MACHINE_RASPI2, SDHCI_MM); + check_driver(SDHCI_MM); + check_produces(SDHCI_MM, SDHCI); + qos_add_test(REGISTER_TEST, SDHCI, testfunct, NULL); + qos_print_graph(); + check_leaf_discovered(2); + qos_graph_destroy(); +} + +static void test_cycle(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_driver("B"); + check_driver("C"); + check_driver("D"); + check_contains(MACHINE_RASPI2, "B"); + check_contains("B", "C"); + check_contains("C", "D"); + check_contains("D", MACHINE_RASPI2); + check_leaf_discovered(0); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_two_test_same_interface(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces(MACHINE_RASPI2, "B"); + qos_add_test("C", "B", testfunct, NULL); + qos_add_test("D", "B", testfunct, NULL); + check_contains(MACHINE_RASPI2, "B"); + check_leaf_discovered(4); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_test_in_path(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces(MACHINE_RASPI2, "B"); + qos_add_test("C", "B", testfunct, NULL); + check_driver("D"); + check_consumes("D", "B"); + check_produces("D", "E"); + qos_add_test("F", "E", testfunct, NULL); + check_leaf_discovered(2); + qos_print_graph(); + qos_graph_destroy(); +} + +static void test_double_edge(void) +{ + qos_graph_init(); + check_machine(MACHINE_RASPI2); + check_produces("B", "C"); + qos_node_consumes("C", "B", NULL); + qos_add_test("D", "C", testfunct, NULL); + check_contains(MACHINE_RASPI2, "B"); + qos_print_graph(); + qos_graph_destroy(); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/qgraph/init_nop", init_nop); + g_test_add_func("/qgraph/test_machine", test_machine); + g_test_add_func("/qgraph/test_contains", test_contains); + g_test_add_func("/qgraph/test_multiple_contains", test_multiple_contains); + g_test_add_func("/qgraph/test_produces", test_produces); + g_test_add_func("/qgraph/test_multiple_produces", test_multiple_produces); + g_test_add_func("/qgraph/test_consumes", test_consumes); + g_test_add_func("/qgraph/test_multiple_consumes", + test_multiple_consumes); + g_test_add_func("/qgraph/test_driver", test_driver); + g_test_add_func("/qgraph/test_test", test_test); + g_test_add_func("/qgraph/test_machine_contains_driver", + test_machine_contains_driver); + g_test_add_func("/qgraph/test_driver_contains_driver", + test_driver_contains_driver); + g_test_add_func("/qgraph/test_machine_produces_interface", + test_machine_produces_interface); + g_test_add_func("/qgraph/test_driver_produces_interface", + test_driver_produces_interface); + g_test_add_func("/qgraph/test_machine_consumes_interface", + test_machine_consumes_interface); + g_test_add_func("/qgraph/test_driver_consumes_interface", + test_driver_consumes_interface); + g_test_add_func("/qgraph/test_test_consumes_interface", + test_test_consumes_interface); + g_test_add_func("/qgraph/test_full_sample", test_full_sample); + g_test_add_func("/qgraph/test_full_sample_raspi", test_full_sample_raspi); + g_test_add_func("/qgraph/test_cycle", test_cycle); + g_test_add_func("/qgraph/test_two_test_same_interface", + test_two_test_same_interface); + g_test_add_func("/qgraph/test_test_in_path", test_test_in_path); + g_test_add_func("/qgraph/test_double_edge", test_double_edge); + + g_test_run(); + return 0; +} diff --git a/tests/test-qht.c b/tests/test-qht.c index dda6a067be..4d23cefab6 100644 --- a/tests/test-qht.c +++ b/tests/test-qht.c @@ -41,7 +41,7 @@ static void insert(int a, int b) } } -static void rm(int init, int end) +static void do_rm(int init, int end, bool exist) { int i; @@ -49,10 +49,24 @@ static void rm(int init, int end) uint32_t hash; hash = arr[i]; - g_assert_true(qht_remove(&ht, &arr[i], hash)); + if (exist) { + g_assert_true(qht_remove(&ht, &arr[i], hash)); + } else { + g_assert_false(qht_remove(&ht, &arr[i], hash)); + } } } +static void rm(int init, int end) +{ + do_rm(init, end, true); +} + +static void rm_nonexist(int init, int end) +{ + do_rm(init, end, false); +} + static void check(int a, int b, bool expected) { struct qht_stats stats; @@ -84,7 +98,7 @@ static void check(int a, int b, bool expected) qht_statistics_destroy(&stats); } -static void count_func(struct qht *ht, void *p, uint32_t hash, void *userp) +static void count_func(void *p, uint32_t hash, void *userp) { unsigned int *curr = userp; @@ -108,14 +122,79 @@ static void iter_check(unsigned int count) g_assert_cmpuint(curr, ==, count); } +static void sum_func(void *p, uint32_t hash, void *userp) +{ + uint32_t *sum = userp; + uint32_t a = *(uint32_t *)p; + + *sum += a; +} + +static void iter_sum_check(unsigned int expected) +{ + unsigned int sum = 0; + + qht_iter(&ht, sum_func, &sum); + g_assert_cmpuint(sum, ==, expected); +} + +static bool rm_mod_func(void *p, uint32_t hash, void *userp) +{ + uint32_t a = *(uint32_t *)p; + unsigned int mod = *(unsigned int *)userp; + + return a % mod == 0; +} + +static void iter_rm_mod(unsigned int mod) +{ + qht_iter_remove(&ht, rm_mod_func, &mod); +} + +static void iter_rm_mod_check(unsigned int mod) +{ + unsigned int expected = 0; + unsigned int i; + + for (i = 0; i < N; i++) { + if (i % mod == 0) { + continue; + } + expected += i; + } + iter_sum_check(expected); +} + static void qht_do_test(unsigned int mode, size_t init_entries) { /* under KVM we might fetch stats from an uninitialized qht */ check_n(0); qht_init(&ht, is_equal, 0, mode); + rm_nonexist(0, 4); + /* + * Test that we successfully delete the last element in a bucket. + * This is a hard-to-reach code path when resizing is on, but without + * resizing we can easily hit it if init_entries <= 1. + * Given that the number of elements per bucket can be 4 or 6 depending on + * the host's pointer size, test the removal of the 4th and 6th elements. + */ + insert(0, 4); + rm_nonexist(5, 6); + rm(3, 4); + check_n(3); + insert(3, 6); + rm(5, 6); + check_n(5); + rm_nonexist(7, 8); + iter_rm_mod(1); + + if (!(mode & QHT_MODE_AUTO_RESIZE)) { + qht_resize(&ht, init_entries * 4 + 4); + } check_n(0); + rm_nonexist(0, 10); insert(0, N); check(0, N, true); check_n(N); @@ -138,8 +217,12 @@ static void qht_do_test(unsigned int mode, size_t init_entries) insert(10, 150); check_n(N); - rm(1, 2); - check_n(N - 1); + qht_reset(&ht); + insert(0, N); + rm_nonexist(N, N + 32); + iter_rm_mod(10); + iter_rm_mod_check(10); + check_n(N * 9 / 10); qht_reset_size(&ht, 0); check_n(0); check(0, N, false); diff --git a/tests/test-qmp-cmds.c b/tests/test-qmp-cmds.c index ba41a6161e..630b1b9bac 100644 --- a/tests/test-qmp-cmds.c +++ b/tests/test-qmp-cmds.c @@ -32,6 +32,10 @@ void qmp_test_flags_command(Error **errp) { } +void qmp_cmd_success_response(Error **errp) +{ +} + Empty2 *qmp_user_def_cmd0(Error **errp) { return g_new0(Empty2, 1); @@ -83,7 +87,7 @@ void qmp_boxed_struct(UserDefZero *arg, Error **errp) { } -void qmp_boxed_union(UserDefNativeListUnion *arg, Error **errp) +void qmp_boxed_union(UserDefListUnion *arg, Error **errp) { } @@ -122,6 +126,21 @@ static void test_dispatch_cmd(void) qobject_unref(req); } +static void test_dispatch_cmd_oob(void) +{ + QDict *req = qdict_new(); + QDict *resp; + + qdict_put_str(req, "exec-oob", "test-flags-command"); + + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), true); + assert(resp != NULL); + assert(!qdict_haskey(resp, "error")); + + qobject_unref(resp); + qobject_unref(req); +} + /* test commands that return an error due to invalid parameters */ static void test_dispatch_cmd_failure(void) { @@ -153,6 +172,17 @@ static void test_dispatch_cmd_failure(void) qobject_unref(req); } +static void test_dispatch_cmd_success_response(void) +{ + QDict *req = qdict_new(); + QDict *resp; + + qdict_put_str(req, "execute", "cmd-success-response"); + resp = qmp_dispatch(&qmp_commands, QOBJECT(req), false); + g_assert_null(resp); + qobject_unref(req); +} + static QObject *test_qmp_dispatch(QDict *req) { QDict *resp; @@ -286,11 +316,14 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); - g_test_add_func("/0.15/dispatch_cmd", test_dispatch_cmd); - g_test_add_func("/0.15/dispatch_cmd_failure", test_dispatch_cmd_failure); - g_test_add_func("/0.15/dispatch_cmd_io", test_dispatch_cmd_io); - g_test_add_func("/0.15/dealloc_types", test_dealloc_types); - g_test_add_func("/0.15/dealloc_partial", test_dealloc_partial); + g_test_add_func("/qmp/dispatch_cmd", test_dispatch_cmd); + g_test_add_func("/qmp/dispatch_cmd_oob", test_dispatch_cmd_oob); + g_test_add_func("/qmp/dispatch_cmd_failure", test_dispatch_cmd_failure); + g_test_add_func("/qmp/dispatch_cmd_io", test_dispatch_cmd_io); + g_test_add_func("/qmp/dispatch_cmd_success_response", + test_dispatch_cmd_success_response); + g_test_add_func("/qmp/dealloc_types", test_dealloc_types); + g_test_add_func("/qmp/dealloc_partial", test_dealloc_partial); test_qmp_init_marshal(&qmp_commands); g_test_run(); diff --git a/tests/test-qmp-event.c b/tests/test-qmp-event.c index 8677094ad1..eee7e08ab6 100644 --- a/tests/test-qmp-event.c +++ b/tests/test-qmp-event.c @@ -21,6 +21,7 @@ #include "qapi/qmp/qstring.h" #include "qapi/qmp-event.h" #include "test-qapi-events.h" +#include "test-qapi-emit-events.h" typedef struct TestEventData { QDict *expect; @@ -93,9 +94,7 @@ static bool qdict_cmp_simple(QDict *a, QDict *b) return d.result; } -/* 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) +void test_qapi_event_emit(test_QAPIEvent event, QDict *d) { QDict *t; int64_t s, ms; @@ -156,7 +155,7 @@ static void test_event_a(TestEventData *data, QDict *d; d = data->expect; qdict_put_str(d, "event", "EVENT_A"); - qapi_event_send_event_a(&error_abort); + qapi_event_send_event_a(); } static void test_event_b(TestEventData *data, @@ -165,7 +164,7 @@ static void test_event_b(TestEventData *data, QDict *d; d = data->expect; qdict_put_str(d, "event", "EVENT_B"); - qapi_event_send_event_b(&error_abort); + qapi_event_send_event_b(); } static void test_event_c(TestEventData *data, @@ -191,7 +190,7 @@ static void test_event_c(TestEventData *data, qdict_put_str(d, "event", "EVENT_C"); qdict_put(d, "data", d_data); - qapi_event_send_event_c(true, 1, true, &b, "test2", &error_abort); + qapi_event_send_event_c(true, 1, true, &b, "test2"); g_free(b.string); } @@ -233,8 +232,7 @@ static void test_event_d(TestEventData *data, qdict_put_str(d, "event", "EVENT_D"); qdict_put(d, "data", d_data); - qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3, - &error_abort); + qapi_event_send_event_d(&a, "test3", false, NULL, true, ENUM_ONE_VALUE3); g_free(struct1.string); g_free(a.string); @@ -242,8 +240,6 @@ static void test_event_d(TestEventData *data, int main(int argc, char **argv) { - qmp_event_set_func_emit(event_test_emit); - g_test_init(&argc, &argv, NULL); event_test_add("/event/event_a", test_event_a); diff --git a/tests/test-qobject-input-visitor.c b/tests/test-qobject-input-visitor.c index 0f4d234c3f..6bacabf063 100644 --- a/tests/test-qobject-input-visitor.c +++ b/tests/test-qobject-input-visitor.c @@ -47,15 +47,13 @@ static void visitor_input_teardown(TestInputVisitorData *data, /* 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, - bool keyval, - const char *json_string, - va_list *ap) + +static Visitor *test_init_internal(TestInputVisitorData *data, bool keyval, + QObject *obj) { visitor_input_teardown(data, NULL); - data->obj = qobject_from_jsonv(json_string, ap, &error_abort); - g_assert(data->obj); + data->obj = obj; if (keyval) { data->qiv = qobject_input_visitor_new_keyval(data->obj); @@ -75,7 +73,8 @@ Visitor *visitor_input_test_init_full(TestInputVisitorData *data, va_list ap; va_start(ap, json_string); - v = visitor_input_test_init_internal(data, keyval, json_string, &ap); + v = test_init_internal(data, keyval, + qobject_from_vjsonf_nofail(json_string, ap)); va_end(ap); return v; } @@ -88,7 +87,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data, va_list ap; va_start(ap, json_string); - v = visitor_input_test_init_internal(data, false, json_string, &ap); + v = test_init_internal(data, false, + qobject_from_vjsonf_nofail(json_string, ap)); va_end(ap); return v; } @@ -103,7 +103,8 @@ Visitor *visitor_input_test_init(TestInputVisitorData *data, static Visitor *visitor_input_test_init_raw(TestInputVisitorData *data, const char *json_string) { - return visitor_input_test_init_internal(data, false, json_string, NULL); + return test_init_internal(data, false, + qobject_from_json(json_string, &error_abort)); } static void test_visitor_in_int(TestInputVisitorData *data, @@ -685,11 +686,11 @@ static void test_visitor_in_alternate_number(TestInputVisitorData *data, qapi_free_AltEnumInt(asi); } -static void test_native_list_integer_helper(TestInputVisitorData *data, - const void *unused, - UserDefNativeListUnionKind kind) +static void test_list_union_integer_helper(TestInputVisitorData *data, + const void *unused, + UserDefListUnionKind kind) { - UserDefNativeListUnion *cvalue = NULL; + UserDefListUnion *cvalue = NULL; Visitor *v; GString *gstr_list = g_string_new(""); GString *gstr_union = g_string_new(""); @@ -702,16 +703,16 @@ static void test_native_list_integer_helper(TestInputVisitorData *data, } } g_string_append_printf(gstr_union, "{ 'type': '%s', 'data': [ %s ] }", - UserDefNativeListUnionKind_str(kind), + UserDefListUnionKind_str(kind), gstr_list->str); v = visitor_input_test_init_raw(data, gstr_union->str); - visit_type_UserDefNativeListUnion(v, NULL, &cvalue, &error_abort); + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); g_assert_cmpint(cvalue->type, ==, kind); switch (kind) { - case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: { + case USER_DEF_LIST_UNION_KIND_INTEGER: { intList *elem = NULL; for (i = 0, elem = cvalue->u.integer.data; elem; elem = elem->next, i++) { @@ -719,56 +720,56 @@ static void test_native_list_integer_helper(TestInputVisitorData *data, } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_S8: { + case USER_DEF_LIST_UNION_KIND_S8: { int8List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_S16: { int16List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_S32: { int32List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_S64: { int64List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_U8: { uint8List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_U16: { uint16List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_U32: { uint32List *elem = NULL; 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: { + case USER_DEF_LIST_UNION_KIND_U64: { uint64List *elem = NULL; for (i = 0, elem = cvalue->u.u64.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, i); @@ -781,76 +782,76 @@ static void test_native_list_integer_helper(TestInputVisitorData *data, g_string_free(gstr_union, true); g_string_free(gstr_list, true); - qapi_free_UserDefNativeListUnion(cvalue); + qapi_free_UserDefListUnion(cvalue); } -static void test_visitor_in_native_list_int(TestInputVisitorData *data, +static void test_visitor_in_list_union_int(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_INTEGER); +} + +static void test_visitor_in_list_union_int8(TestInputVisitorData *data, const void *unused) { - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER); + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S8); } -static void test_visitor_in_native_list_int8(TestInputVisitorData *data, +static void test_visitor_in_list_union_int16(TestInputVisitorData *data, const void *unused) { - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_S8); + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S16); } -static void test_visitor_in_native_list_int16(TestInputVisitorData *data, - const void *unused) -{ - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_S16); -} - -static void test_visitor_in_native_list_int32(TestInputVisitorData *data, - const void *unused) -{ - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_S32); -} - -static void test_visitor_in_native_list_int64(TestInputVisitorData *data, - const void *unused) -{ - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_S64); -} - -static void test_visitor_in_native_list_uint8(TestInputVisitorData *data, +static void test_visitor_in_list_union_int32(TestInputVisitorData *data, const void *unused) { - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_U8); + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S32); } -static void test_visitor_in_native_list_uint16(TestInputVisitorData *data, - const void *unused) +static void test_visitor_in_list_union_int64(TestInputVisitorData *data, + const void *unused) { - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_U16); + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_S64); } -static void test_visitor_in_native_list_uint32(TestInputVisitorData *data, - const void *unused) +static void test_visitor_in_list_union_uint8(TestInputVisitorData *data, + const void *unused) { - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_U32); + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U8); } -static void test_visitor_in_native_list_uint64(TestInputVisitorData *data, - const void *unused) +static void test_visitor_in_list_union_uint16(TestInputVisitorData *data, + const void *unused) { - test_native_list_integer_helper(data, unused, - USER_DEF_NATIVE_LIST_UNION_KIND_U64); + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U16); } -static void test_visitor_in_native_list_bool(TestInputVisitorData *data, +static void test_visitor_in_list_union_uint32(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U32); +} + +static void test_visitor_in_list_union_uint64(TestInputVisitorData *data, + const void *unused) +{ + test_list_union_integer_helper(data, unused, + USER_DEF_LIST_UNION_KIND_U64); +} + +static void test_visitor_in_list_union_bool(TestInputVisitorData *data, const void *unused) { - UserDefNativeListUnion *cvalue = NULL; + UserDefListUnion *cvalue = NULL; boolList *elem = NULL; Visitor *v; GString *gstr_list = g_string_new(""); @@ -868,9 +869,9 @@ 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, NULL, &cvalue, &error_abort); + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN); + g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_BOOLEAN); for (i = 0, elem = cvalue->u.boolean.data; elem; elem = elem->next, i++) { g_assert_cmpint(elem->value, ==, (i % 3 == 0) ? 1 : 0); @@ -878,13 +879,13 @@ static void test_visitor_in_native_list_bool(TestInputVisitorData *data, g_string_free(gstr_union, true); g_string_free(gstr_list, true); - qapi_free_UserDefNativeListUnion(cvalue); + qapi_free_UserDefListUnion(cvalue); } -static void test_visitor_in_native_list_string(TestInputVisitorData *data, - const void *unused) +static void test_visitor_in_list_union_string(TestInputVisitorData *data, + const void *unused) { - UserDefNativeListUnion *cvalue = NULL; + UserDefListUnion *cvalue = NULL; strList *elem = NULL; Visitor *v; GString *gstr_list = g_string_new(""); @@ -901,9 +902,9 @@ 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, NULL, &cvalue, &error_abort); + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_STRING); + g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_STRING); for (i = 0, elem = cvalue->u.string.data; elem; elem = elem->next, i++) { gchar str[8]; @@ -913,15 +914,15 @@ static void test_visitor_in_native_list_string(TestInputVisitorData *data, g_string_free(gstr_union, true); g_string_free(gstr_list, true); - qapi_free_UserDefNativeListUnion(cvalue); + qapi_free_UserDefListUnion(cvalue); } #define DOUBLE_STR_MAX 16 -static void test_visitor_in_native_list_number(TestInputVisitorData *data, - const void *unused) +static void test_visitor_in_list_union_number(TestInputVisitorData *data, + const void *unused) { - UserDefNativeListUnion *cvalue = NULL; + UserDefListUnion *cvalue = NULL; numberList *elem = NULL; Visitor *v; GString *gstr_list = g_string_new(""); @@ -938,9 +939,9 @@ 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, NULL, &cvalue, &error_abort); + visit_type_UserDefListUnion(v, NULL, &cvalue, &error_abort); g_assert(cvalue != NULL); - g_assert_cmpint(cvalue->type, ==, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER); + g_assert_cmpint(cvalue->type, ==, USER_DEF_LIST_UNION_KIND_NUMBER); for (i = 0, elem = cvalue->u.number.data; elem; elem = elem->next, i++) { GString *double_expected = g_string_new(""); @@ -956,7 +957,7 @@ static void test_visitor_in_native_list_number(TestInputVisitorData *data, g_string_free(gstr_union, true); g_string_free(gstr_list, true); - qapi_free_UserDefNativeListUnion(cvalue); + qapi_free_UserDefListUnion(cvalue); } static void input_visitor_test_add(const char *testpath, @@ -1191,17 +1192,17 @@ static void test_visitor_in_fail_list_nested(TestInputVisitorData *data, visit_end_list(v, NULL); } -static void test_visitor_in_fail_union_native_list(TestInputVisitorData *data, - const void *unused) +static void test_visitor_in_fail_union_list(TestInputVisitorData *data, + const void *unused) { - UserDefNativeListUnion *tmp = NULL; + UserDefListUnion *tmp = NULL; Error *err = NULL; Visitor *v; v = visitor_input_test_init(data, "{ 'type': 'integer', 'data' : [ 'string' ] }"); - visit_type_UserDefNativeListUnion(v, NULL, &tmp, &err); + visit_type_UserDefListUnion(v, NULL, &tmp, &err); error_free_or_abort(&err); g_assert(!tmp); } @@ -1270,7 +1271,6 @@ static void test_visitor_in_qmp_introspect(TestInputVisitorData *data, const void *unused) { do_test_visitor_in_qmp_introspect(data, &test_qmp_schema_qlit); - do_test_visitor_in_qmp_introspect(data, &qmp_schema_qlit); } int main(int argc, char **argv) @@ -1335,30 +1335,30 @@ int main(int argc, char **argv) NULL, test_visitor_in_wrong_type); input_visitor_test_add("/visitor/input/alternate-number", NULL, test_visitor_in_alternate_number); - input_visitor_test_add("/visitor/input/native_list/int", - NULL, test_visitor_in_native_list_int); - input_visitor_test_add("/visitor/input/native_list/int8", - NULL, test_visitor_in_native_list_int8); - input_visitor_test_add("/visitor/input/native_list/int16", - NULL, test_visitor_in_native_list_int16); - input_visitor_test_add("/visitor/input/native_list/int32", - NULL, test_visitor_in_native_list_int32); - input_visitor_test_add("/visitor/input/native_list/int64", - NULL, test_visitor_in_native_list_int64); - input_visitor_test_add("/visitor/input/native_list/uint8", - NULL, test_visitor_in_native_list_uint8); - input_visitor_test_add("/visitor/input/native_list/uint16", - NULL, test_visitor_in_native_list_uint16); - input_visitor_test_add("/visitor/input/native_list/uint32", - NULL, test_visitor_in_native_list_uint32); - input_visitor_test_add("/visitor/input/native_list/uint64", - NULL, test_visitor_in_native_list_uint64); - input_visitor_test_add("/visitor/input/native_list/bool", - NULL, test_visitor_in_native_list_bool); - input_visitor_test_add("/visitor/input/native_list/str", - NULL, test_visitor_in_native_list_string); - input_visitor_test_add("/visitor/input/native_list/number", - NULL, test_visitor_in_native_list_number); + input_visitor_test_add("/visitor/input/list_union/int", + NULL, test_visitor_in_list_union_int); + input_visitor_test_add("/visitor/input/list_union/int8", + NULL, test_visitor_in_list_union_int8); + input_visitor_test_add("/visitor/input/list_union/int16", + NULL, test_visitor_in_list_union_int16); + input_visitor_test_add("/visitor/input/list_union/int32", + NULL, test_visitor_in_list_union_int32); + input_visitor_test_add("/visitor/input/list_union/int64", + NULL, test_visitor_in_list_union_int64); + input_visitor_test_add("/visitor/input/list_union/uint8", + NULL, test_visitor_in_list_union_uint8); + input_visitor_test_add("/visitor/input/list_union/uint16", + NULL, test_visitor_in_list_union_uint16); + input_visitor_test_add("/visitor/input/list_union/uint32", + NULL, test_visitor_in_list_union_uint32); + input_visitor_test_add("/visitor/input/list_union/uint64", + NULL, test_visitor_in_list_union_uint64); + input_visitor_test_add("/visitor/input/list_union/bool", + NULL, test_visitor_in_list_union_bool); + input_visitor_test_add("/visitor/input/list_union/str", + NULL, test_visitor_in_list_union_string); + input_visitor_test_add("/visitor/input/list_union/number", + NULL, test_visitor_in_list_union_number); input_visitor_test_add("/visitor/input/fail/struct", NULL, test_visitor_in_fail_struct); input_visitor_test_add("/visitor/input/fail/struct-nested", @@ -1377,8 +1377,8 @@ int main(int argc, char **argv) NULL, test_visitor_in_fail_union_flat_no_discrim); input_visitor_test_add("/visitor/input/fail/alternate", NULL, test_visitor_in_fail_alternate); - input_visitor_test_add("/visitor/input/fail/union-native-list", - NULL, test_visitor_in_fail_union_native_list); + input_visitor_test_add("/visitor/input/fail/union-list", + NULL, test_visitor_in_fail_union_list); input_visitor_test_add("/visitor/input/qapi-introspect", NULL, test_visitor_in_qmp_introspect); diff --git a/tests/test-qobject-output-visitor.c b/tests/test-qobject-output-visitor.c index be635854b4..3e993e5ba8 100644 --- a/tests/test-qobject-output-visitor.c +++ b/tests/test-qobject-output-visitor.c @@ -474,11 +474,11 @@ static void test_visitor_out_null(TestOutputVisitorData *data, g_assert(qobject_type(nil) == QTYPE_QNULL); } -static void init_native_list(UserDefNativeListUnion *cvalue) +static void init_list_union(UserDefListUnion *cvalue) { int i; switch (cvalue->type) { - case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: { + case USER_DEF_LIST_UNION_KIND_INTEGER: { intList **list = &cvalue->u.integer.data; for (i = 0; i < 32; i++) { *list = g_new0(intList, 1); @@ -488,7 +488,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_S8: { + case USER_DEF_LIST_UNION_KIND_S8: { int8List **list = &cvalue->u.s8.data; for (i = 0; i < 32; i++) { *list = g_new0(int8List, 1); @@ -498,7 +498,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_S16: { + case USER_DEF_LIST_UNION_KIND_S16: { int16List **list = &cvalue->u.s16.data; for (i = 0; i < 32; i++) { *list = g_new0(int16List, 1); @@ -508,7 +508,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_S32: { + case USER_DEF_LIST_UNION_KIND_S32: { int32List **list = &cvalue->u.s32.data; for (i = 0; i < 32; i++) { *list = g_new0(int32List, 1); @@ -518,7 +518,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_S64: { + case USER_DEF_LIST_UNION_KIND_S64: { int64List **list = &cvalue->u.s64.data; for (i = 0; i < 32; i++) { *list = g_new0(int64List, 1); @@ -528,7 +528,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_U8: { + case USER_DEF_LIST_UNION_KIND_U8: { uint8List **list = &cvalue->u.u8.data; for (i = 0; i < 32; i++) { *list = g_new0(uint8List, 1); @@ -538,7 +538,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_U16: { + case USER_DEF_LIST_UNION_KIND_U16: { uint16List **list = &cvalue->u.u16.data; for (i = 0; i < 32; i++) { *list = g_new0(uint16List, 1); @@ -548,7 +548,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_U32: { + case USER_DEF_LIST_UNION_KIND_U32: { uint32List **list = &cvalue->u.u32.data; for (i = 0; i < 32; i++) { *list = g_new0(uint32List, 1); @@ -558,7 +558,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_U64: { + case USER_DEF_LIST_UNION_KIND_U64: { uint64List **list = &cvalue->u.u64.data; for (i = 0; i < 32; i++) { *list = g_new0(uint64List, 1); @@ -568,7 +568,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: { + case USER_DEF_LIST_UNION_KIND_BOOLEAN: { boolList **list = &cvalue->u.boolean.data; for (i = 0; i < 32; i++) { *list = g_new0(boolList, 1); @@ -578,7 +578,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: { + case USER_DEF_LIST_UNION_KIND_STRING: { strList **list = &cvalue->u.string.data; for (i = 0; i < 32; i++) { *list = g_new0(strList, 1); @@ -588,7 +588,7 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } break; } - case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: { + case USER_DEF_LIST_UNION_KIND_NUMBER: { numberList **list = &cvalue->u.number.data; for (i = 0; i < 32; i++) { *list = g_new0(numberList, 1); @@ -603,8 +603,8 @@ static void init_native_list(UserDefNativeListUnion *cvalue) } } -static void check_native_list(QObject *qobj, - UserDefNativeListUnionKind kind) +static void check_list_union(QObject *qobj, + UserDefListUnionKind kind) { QDict *qdict; QList *qlist; @@ -616,10 +616,10 @@ static void check_native_list(QObject *qobj, qlist = qlist_copy(qobject_to(QList, qdict_get(qdict, "data"))); switch (kind) { - case USER_DEF_NATIVE_LIST_UNION_KIND_U8: - case USER_DEF_NATIVE_LIST_UNION_KIND_U16: - case USER_DEF_NATIVE_LIST_UNION_KIND_U32: - case USER_DEF_NATIVE_LIST_UNION_KIND_U64: + case USER_DEF_LIST_UNION_KIND_U8: + case USER_DEF_LIST_UNION_KIND_U16: + case USER_DEF_LIST_UNION_KIND_U32: + case USER_DEF_LIST_UNION_KIND_U64: for (i = 0; i < 32; i++) { QObject *tmp; QNum *qvalue; @@ -634,16 +634,16 @@ static void check_native_list(QObject *qobj, } break; - case USER_DEF_NATIVE_LIST_UNION_KIND_S8: - case USER_DEF_NATIVE_LIST_UNION_KIND_S16: - case USER_DEF_NATIVE_LIST_UNION_KIND_S32: - case USER_DEF_NATIVE_LIST_UNION_KIND_S64: + case USER_DEF_LIST_UNION_KIND_S8: + case USER_DEF_LIST_UNION_KIND_S16: + case USER_DEF_LIST_UNION_KIND_S32: + case USER_DEF_LIST_UNION_KIND_S64: /* * All integer elements in JSON arrays get stored into QNums * when we convert to QObjects, so we can check them all in * the same fashion, so simply fall through here. */ - case USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER: + case USER_DEF_LIST_UNION_KIND_INTEGER: for (i = 0; i < 32; i++) { QObject *tmp; QNum *qvalue; @@ -657,7 +657,7 @@ static void check_native_list(QObject *qobj, qobject_unref(qlist_pop(qlist)); } break; - case USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN: + case USER_DEF_LIST_UNION_KIND_BOOLEAN: for (i = 0; i < 32; i++) { QObject *tmp; QBool *qvalue; @@ -668,7 +668,7 @@ static void check_native_list(QObject *qobj, qobject_unref(qlist_pop(qlist)); } break; - case USER_DEF_NATIVE_LIST_UNION_KIND_STRING: + case USER_DEF_LIST_UNION_KIND_STRING: for (i = 0; i < 32; i++) { QObject *tmp; QString *qvalue; @@ -681,7 +681,7 @@ static void check_native_list(QObject *qobj, qobject_unref(qlist_pop(qlist)); } break; - case USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER: + case USER_DEF_LIST_UNION_KIND_NUMBER: for (i = 0; i < 32; i++) { QObject *tmp; QNum *qvalue; @@ -706,93 +706,93 @@ static void check_native_list(QObject *qobj, qobject_unref(qlist); } -static void test_native_list(TestOutputVisitorData *data, - const void *unused, - UserDefNativeListUnionKind kind) +static void test_list_union(TestOutputVisitorData *data, + const void *unused, + UserDefListUnionKind kind) { - UserDefNativeListUnion *cvalue = g_new0(UserDefNativeListUnion, 1); + UserDefListUnion *cvalue = g_new0(UserDefListUnion, 1); QObject *obj; cvalue->type = kind; - init_native_list(cvalue); + init_list_union(cvalue); - visit_type_UserDefNativeListUnion(data->ov, NULL, &cvalue, &error_abort); + visit_type_UserDefListUnion(data->ov, NULL, &cvalue, &error_abort); obj = visitor_get(data); - check_native_list(obj, cvalue->type); - qapi_free_UserDefNativeListUnion(cvalue); + check_list_union(obj, cvalue->type); + qapi_free_UserDefListUnion(cvalue); } -static void test_visitor_out_native_list_int(TestOutputVisitorData *data, +static void test_visitor_out_list_union_int(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_INTEGER); +} + +static void test_visitor_out_list_union_int8(TestOutputVisitorData *data, const void *unused) { - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_INTEGER); + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S8); } -static void test_visitor_out_native_list_int8(TestOutputVisitorData *data, +static void test_visitor_out_list_union_int16(TestOutputVisitorData *data, const void *unused) { - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_S8); + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S16); } -static void test_visitor_out_native_list_int16(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_S16); -} - -static void test_visitor_out_native_list_int32(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_S32); -} - -static void test_visitor_out_native_list_int64(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_S64); -} - -static void test_visitor_out_native_list_uint8(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_U8); -} - -static void test_visitor_out_native_list_uint16(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_U16); -} - -static void test_visitor_out_native_list_uint32(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_U32); -} - -static void test_visitor_out_native_list_uint64(TestOutputVisitorData *data, - const void *unused) -{ - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_U64); -} - -static void test_visitor_out_native_list_bool(TestOutputVisitorData *data, +static void test_visitor_out_list_union_int32(TestOutputVisitorData *data, const void *unused) { - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_BOOLEAN); + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S32); } -static void test_visitor_out_native_list_str(TestOutputVisitorData *data, +static void test_visitor_out_list_union_int64(TestOutputVisitorData *data, const void *unused) { - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_STRING); + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_S64); } -static void test_visitor_out_native_list_number(TestOutputVisitorData *data, - const void *unused) +static void test_visitor_out_list_union_uint8(TestOutputVisitorData *data, + const void *unused) { - test_native_list(data, unused, USER_DEF_NATIVE_LIST_UNION_KIND_NUMBER); + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U8); +} + +static void test_visitor_out_list_union_uint16(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U16); +} + +static void test_visitor_out_list_union_uint32(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U32); +} + +static void test_visitor_out_list_union_uint64(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_U64); +} + +static void test_visitor_out_list_union_bool(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_BOOLEAN); +} + +static void test_visitor_out_list_union_str(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_STRING); +} + +static void test_visitor_out_list_union_number(TestOutputVisitorData *data, + const void *unused) +{ + test_list_union(data, unused, USER_DEF_LIST_UNION_KIND_NUMBER); } static void output_visitor_test_add(const char *testpath, @@ -841,42 +841,42 @@ int main(int argc, char **argv) &out_visitor_data, test_visitor_out_alternate); output_visitor_test_add("/visitor/output/null", &out_visitor_data, test_visitor_out_null); - output_visitor_test_add("/visitor/output/native_list/int", + output_visitor_test_add("/visitor/output/list_union/int", &out_visitor_data, - test_visitor_out_native_list_int); - output_visitor_test_add("/visitor/output/native_list/int8", + test_visitor_out_list_union_int); + output_visitor_test_add("/visitor/output/list_union/int8", &out_visitor_data, - test_visitor_out_native_list_int8); - output_visitor_test_add("/visitor/output/native_list/int16", + test_visitor_out_list_union_int8); + output_visitor_test_add("/visitor/output/list_union/int16", &out_visitor_data, - test_visitor_out_native_list_int16); - output_visitor_test_add("/visitor/output/native_list/int32", + test_visitor_out_list_union_int16); + output_visitor_test_add("/visitor/output/list_union/int32", &out_visitor_data, - test_visitor_out_native_list_int32); - output_visitor_test_add("/visitor/output/native_list/int64", + test_visitor_out_list_union_int32); + output_visitor_test_add("/visitor/output/list_union/int64", &out_visitor_data, - test_visitor_out_native_list_int64); - output_visitor_test_add("/visitor/output/native_list/uint8", + test_visitor_out_list_union_int64); + output_visitor_test_add("/visitor/output/list_union/uint8", &out_visitor_data, - test_visitor_out_native_list_uint8); - output_visitor_test_add("/visitor/output/native_list/uint16", + test_visitor_out_list_union_uint8); + output_visitor_test_add("/visitor/output/list_union/uint16", &out_visitor_data, - test_visitor_out_native_list_uint16); - output_visitor_test_add("/visitor/output/native_list/uint32", + test_visitor_out_list_union_uint16); + output_visitor_test_add("/visitor/output/list_union/uint32", &out_visitor_data, - test_visitor_out_native_list_uint32); - output_visitor_test_add("/visitor/output/native_list/uint64", + test_visitor_out_list_union_uint32); + output_visitor_test_add("/visitor/output/list_union/uint64", &out_visitor_data, - test_visitor_out_native_list_uint64); - output_visitor_test_add("/visitor/output/native_list/bool", + test_visitor_out_list_union_uint64); + output_visitor_test_add("/visitor/output/list_union/bool", &out_visitor_data, - test_visitor_out_native_list_bool); - output_visitor_test_add("/visitor/output/native_list/string", + test_visitor_out_list_union_bool); + output_visitor_test_add("/visitor/output/list_union/string", &out_visitor_data, - test_visitor_out_native_list_str); - output_visitor_test_add("/visitor/output/native_list/number", + test_visitor_out_list_union_str); + output_visitor_test_add("/visitor/output/list_union/number", &out_visitor_data, - test_visitor_out_native_list_number); + test_visitor_out_list_union_number); g_test_run(); diff --git a/tests/test-rcu-list.c b/tests/test-rcu-list.c index 1514d7ec97..6f076473e0 100644 --- a/tests/test-rcu-list.c +++ b/tests/test-rcu-list.c @@ -33,8 +33,8 @@ static QemuMutex counts_mutex; static long long n_reads = 0LL; static long long n_updates = 0LL; -static long long n_reclaims = 0LL; -static long long n_nodes_removed = 0LL; +static int64_t n_reclaims; +static int64_t n_nodes_removed; static long long n_nodes = 0LL; static int g_test_in_charge = 0; @@ -44,7 +44,7 @@ static int nthreadsrunning; #define GOFLAG_RUN 1 #define GOFLAG_STOP 2 -static volatile int goflag = GOFLAG_INIT; +static int goflag = GOFLAG_INIT; #define RCU_READ_RUN 1000 #define RCU_UPDATE_RUN 10 @@ -82,9 +82,20 @@ static void wait_all_threads(void) n_threads = 0; } +#ifndef TEST_LIST_TYPE +#define TEST_LIST_TYPE 1 +#endif struct list_element { +#if TEST_LIST_TYPE == 1 QLIST_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 2 + QSIMPLEQ_ENTRY(list_element) entry; +#elif TEST_LIST_TYPE == 3 + QTAILQ_ENTRY(list_element) entry; +#else +#error Invalid TEST_LIST_TYPE +#endif struct rcu_head rcu; }; @@ -93,10 +104,49 @@ static void reclaim_list_el(struct rcu_head *prcu) struct list_element *el = container_of(prcu, struct list_element, rcu); g_free(el); /* Accessed only from call_rcu thread. */ - n_reclaims++; + atomic_set_i64(&n_reclaims, n_reclaims + 1); } -static QLIST_HEAD(q_list_head, list_element) Q_list_head; +#if TEST_LIST_TYPE == 1 +static QLIST_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qlist" +#define TEST_LIST_REMOVE_RCU QLIST_REMOVE_RCU +#define TEST_LIST_INSERT_AFTER_RCU QLIST_INSERT_AFTER_RCU +#define TEST_LIST_INSERT_HEAD_RCU QLIST_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QLIST_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QLIST_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 2 +static QSIMPLEQ_HEAD(, list_element) Q_list_head = + QSIMPLEQ_HEAD_INITIALIZER(Q_list_head); + +#define TEST_NAME "qsimpleq" +#define TEST_LIST_REMOVE_RCU(el, f) \ + QSIMPLEQ_REMOVE_RCU(&Q_list_head, el, list_element, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QSIMPLEQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QSIMPLEQ_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QSIMPLEQ_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QSIMPLEQ_FOREACH_SAFE_RCU + +#elif TEST_LIST_TYPE == 3 +static QTAILQ_HEAD(, list_element) Q_list_head; + +#define TEST_NAME "qtailq" +#define TEST_LIST_REMOVE_RCU(el, f) QTAILQ_REMOVE_RCU(&Q_list_head, el, f) + +#define TEST_LIST_INSERT_AFTER_RCU(list_el, el, f) \ + QTAILQ_INSERT_AFTER_RCU(&Q_list_head, list_el, el, f) + +#define TEST_LIST_INSERT_HEAD_RCU QTAILQ_INSERT_HEAD_RCU +#define TEST_LIST_FOREACH_RCU QTAILQ_FOREACH_RCU +#define TEST_LIST_FOREACH_SAFE_RCU QTAILQ_FOREACH_SAFE_RCU +#else +#error Invalid TEST_LIST_TYPE +#endif static void *rcu_q_reader(void *arg) { @@ -107,15 +157,15 @@ static void *rcu_q_reader(void *arg) *(struct rcu_reader_data **)arg = &rcu_reader; atomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) { + while (atomic_read(&goflag) == GOFLAG_INIT) { g_usleep(1000); } - while (goflag == GOFLAG_RUN) { + while (atomic_read(&goflag) == GOFLAG_RUN) { rcu_read_lock(); - QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { n_reads_local++; - if (goflag == GOFLAG_STOP) { + if (atomic_read(&goflag) == GOFLAG_STOP) { break; } } @@ -142,35 +192,35 @@ static void *rcu_q_updater(void *arg) *(struct rcu_reader_data **)arg = &rcu_reader; atomic_inc(&nthreadsrunning); - while (goflag == GOFLAG_INIT) { + while (atomic_read(&goflag) == GOFLAG_INIT) { g_usleep(1000); } - while (goflag == GOFLAG_RUN) { + while (atomic_read(&goflag) == GOFLAG_RUN) { target_el = select_random_el(RCU_Q_LEN); j = 0; /* FOREACH_RCU could work here but let's use both macros */ - QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { j++; if (target_el == j) { - QLIST_REMOVE_RCU(prev_el, entry); + TEST_LIST_REMOVE_RCU(prev_el, entry); /* may be more than one updater in the future */ call_rcu1(&prev_el->rcu, reclaim_list_el); n_removed_local++; break; } } - if (goflag == GOFLAG_STOP) { + if (atomic_read(&goflag) == GOFLAG_STOP) { break; } target_el = select_random_el(RCU_Q_LEN); j = 0; - QLIST_FOREACH_RCU(el, &Q_list_head, entry) { + TEST_LIST_FOREACH_RCU(el, &Q_list_head, entry) { j++; if (target_el == j) { - prev_el = g_new(struct list_element, 1); + struct list_element *new_el = g_new(struct list_element, 1); n_nodes += n_nodes_local; - QLIST_INSERT_BEFORE_RCU(el, prev_el, entry); + TEST_LIST_INSERT_AFTER_RCU(el, new_el, entry); break; } } @@ -182,7 +232,7 @@ static void *rcu_q_updater(void *arg) qemu_mutex_lock(&counts_mutex); n_nodes += n_nodes_local; n_updates += n_updates_local; - n_nodes_removed += n_removed_local; + atomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local); qemu_mutex_unlock(&counts_mutex); return NULL; } @@ -195,7 +245,7 @@ static void rcu_qtest_init(void) srand(time(0)); for (i = 0; i < RCU_Q_LEN; i++) { new_el = g_new(struct list_element, 1); - QLIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); + TEST_LIST_INSERT_HEAD_RCU(&Q_list_head, new_el, entry); } qemu_mutex_lock(&counts_mutex); n_nodes += RCU_Q_LEN; @@ -209,9 +259,9 @@ static void rcu_qtest_run(int duration, int nreaders) g_usleep(1000); } - goflag = GOFLAG_RUN; + atomic_set(&goflag, GOFLAG_RUN); sleep(duration); - goflag = GOFLAG_STOP; + atomic_set(&goflag, GOFLAG_STOP); wait_all_threads(); } @@ -230,25 +280,27 @@ static void rcu_qtest(const char *test, int duration, int nreaders) create_thread(rcu_q_updater); rcu_qtest_run(duration, nreaders); - QLIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { - QLIST_REMOVE_RCU(prev_el, entry); + TEST_LIST_FOREACH_SAFE_RCU(prev_el, &Q_list_head, entry, el) { + TEST_LIST_REMOVE_RCU(prev_el, entry); call_rcu1(&prev_el->rcu, reclaim_list_el); n_removed_local++; } qemu_mutex_lock(&counts_mutex); - n_nodes_removed += n_removed_local; + atomic_set_i64(&n_nodes_removed, n_nodes_removed + n_removed_local); qemu_mutex_unlock(&counts_mutex); synchronize_rcu(); - while (n_nodes_removed > n_reclaims) { + while (atomic_read_i64(&n_nodes_removed) > atomic_read_i64(&n_reclaims)) { g_usleep(100); synchronize_rcu(); } if (g_test_in_charge) { - g_assert_cmpint(n_nodes_removed, ==, n_reclaims); + g_assert_cmpint(atomic_read_i64(&n_nodes_removed), ==, + atomic_read_i64(&n_reclaims)); } else { printf("%s: %d readers; 1 updater; nodes read: " \ - "%lld, nodes removed: %lld; nodes reclaimed: %lld\n", - test, nthreadsrunning - 1, n_reads, n_nodes_removed, n_reclaims); + "%lld, nodes removed: %"PRIi64"; nodes reclaimed: %"PRIi64"\n", + test, nthreadsrunning - 1, n_reads, + atomic_read_i64(&n_nodes_removed), atomic_read_i64(&n_reclaims)); exit(0); } } @@ -290,9 +342,9 @@ int main(int argc, char *argv[]) } else { gtest_seconds = 20; } - g_test_add_func("/rcu/qlist/single-threaded", gtest_rcuq_one); - g_test_add_func("/rcu/qlist/short-few", gtest_rcuq_few); - g_test_add_func("/rcu/qlist/long-many", gtest_rcuq_many); + g_test_add_func("/rcu/"TEST_NAME"/single-threaded", gtest_rcuq_one); + g_test_add_func("/rcu/"TEST_NAME"/short-few", gtest_rcuq_few); + g_test_add_func("/rcu/"TEST_NAME"/long-many", gtest_rcuq_many); g_test_in_charge = 1; return g_test_run(); } diff --git a/tests/test-rcu-simpleq.c b/tests/test-rcu-simpleq.c new file mode 100644 index 0000000000..057f7d33f7 --- /dev/null +++ b/tests/test-rcu-simpleq.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 2 +#include "test-rcu-list.c" diff --git a/tests/test-rcu-tailq.c b/tests/test-rcu-tailq.c new file mode 100644 index 0000000000..8d487e0ee0 --- /dev/null +++ b/tests/test-rcu-tailq.c @@ -0,0 +1,2 @@ +#define TEST_LIST_TYPE 3 +#include "test-rcu-list.c" diff --git a/tests/test-replication.c b/tests/test-replication.c index c8165ae954..f085d1993a 100644 --- a/tests/test-replication.c +++ b/tests/test-replication.c @@ -207,13 +207,17 @@ static BlockBackend *start_primary(void) static void teardown_primary(void) { BlockBackend *blk; + AioContext *ctx; /* remove P_ID */ blk = blk_by_name(P_ID); assert(blk); + ctx = blk_get_aio_context(blk); + aio_context_acquire(ctx); monitor_remove_blk(blk); blk_unref(blk); + aio_context_release(ctx); } static void test_primary_read(void) @@ -365,20 +369,27 @@ static void teardown_secondary(void) { /* only need to destroy two BBs */ BlockBackend *blk; + AioContext *ctx; /* remove S_LOCAL_DISK_ID */ blk = blk_by_name(S_LOCAL_DISK_ID); assert(blk); + ctx = blk_get_aio_context(blk); + aio_context_acquire(ctx); monitor_remove_blk(blk); blk_unref(blk); + aio_context_release(ctx); /* remove S_ID */ blk = blk_by_name(S_ID); assert(blk); + ctx = blk_get_aio_context(blk); + aio_context_acquire(ctx); monitor_remove_blk(blk); blk_unref(blk); + aio_context_release(ctx); } static void test_secondary_read(void) diff --git a/tests/test-string-input-visitor.c b/tests/test-string-input-visitor.c index 88e0e1aa9a..34b54dfc89 100644 --- a/tests/test-string-input-visitor.c +++ b/tests/test-string-input-visitor.c @@ -92,16 +92,6 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n) uint64List *tail; int i; - /* BUG: unsigned numbers above INT64_MAX don't work */ - for (i = 0; i < n; i++) { - if (expected[i] > INT64_MAX) { - Error *err = NULL; - visit_type_uint64List(v, NULL, &res, &err); - error_free_or_abort(&err); - return; - } - } - visit_type_uint64List(v, NULL, &res, &error_abort); tail = res; for (i = 0; i < n; i++) { @@ -117,14 +107,14 @@ static void check_ulist(Visitor *v, uint64_t *expected, size_t n) static void test_visitor_in_intList(TestInputVisitorData *data, const void *unused) { - /* Note: the visitor *sorts* ranges *unsigned* */ - int64_t expect1[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 20 }; + int64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, + 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; int64_t expect2[] = { 32767, -32768, -32767 }; - int64_t expect3[] = { INT64_MAX, INT64_MIN }; - uint64_t expect4[] = { UINT64_MAX }; + int64_t expect3[] = { INT64_MIN, INT64_MAX }; + int64_t expect4[] = { 1 }; + int64_t expect5[] = { INT64_MAX - 2, INT64_MAX - 1, INT64_MAX }; Error *err = NULL; int64List *res = NULL; - int64List *tail; Visitor *v; int64_t val; @@ -140,8 +130,45 @@ static void test_visitor_in_intList(TestInputVisitorData *data, "-9223372036854775808,9223372036854775807"); check_ilist(v, expect3, ARRAY_SIZE(expect3)); - v = visitor_input_test_init(data, "18446744073709551615"); - check_ulist(v, expect4, ARRAY_SIZE(expect4)); + v = visitor_input_test_init(data, "1-1"); + check_ilist(v, expect4, ARRAY_SIZE(expect4)); + + v = visitor_input_test_init(data, + "9223372036854775805-9223372036854775807"); + check_ilist(v, expect5, ARRAY_SIZE(expect5)); + + /* Value too large */ + + v = visitor_input_test_init(data, "9223372036854775808"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Value too small */ + + v = visitor_input_test_init(data, "-9223372036854775809"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range not ascending */ + + v = visitor_input_test_init(data, "3-1"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + v = visitor_input_test_init(data, "9223372036854775807-0"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range too big (65536 is the limit against DOS attacks) */ + + v = visitor_input_test_init(data, "0-65536"); + visit_type_int64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); /* Empty list */ @@ -161,39 +188,140 @@ static void test_visitor_in_intList(TestInputVisitorData *data, v = visitor_input_test_init(data, "0,2-3"); - /* Would be simpler if the visitor genuinely supported virtual walks */ - visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res), - &error_abort); - tail = res; - visit_type_int64(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 0); - tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res)); - g_assert(tail); - visit_type_int64(v, NULL, &tail->value, &error_abort); - g_assert_cmpint(tail->value, ==, 2); - tail = (int64List *)visit_next_list(v, (GenericList *)tail, sizeof(*res)); - g_assert(tail); + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_int64(v, NULL, &val, &error_abort); + g_assert_cmpint(val, ==, 0); + visit_type_int64(v, NULL, &val, &error_abort); + g_assert_cmpint(val, ==, 2); visit_check_list(v, &err); error_free_or_abort(&err); - visit_end_list(v, (void **)&res); - - qapi_free_int64List(res); + visit_end_list(v, NULL); /* Visit beyond end of list */ + v = visitor_input_test_init(data, "0"); - visit_start_list(v, NULL, (GenericList **)&res, sizeof(*res), - &error_abort); - tail = res; - visit_type_int64(v, NULL, &tail->value, &err); - g_assert_cmpint(tail->value, ==, 0); + visit_start_list(v, NULL, NULL, 0, &error_abort); visit_type_int64(v, NULL, &val, &err); - g_assert_cmpint(val, ==, 1); /* BUG */ - visit_check_list(v, &error_abort); - visit_end_list(v, (void **)&res); + g_assert_cmpint(val, ==, 0); + visit_type_int64(v, NULL, &val, &err); + error_free_or_abort(&err); - qapi_free_int64List(res); + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); +} + +static void test_visitor_in_uintList(TestInputVisitorData *data, + const void *unused) +{ + uint64_t expect1[] = { 1, 2, 0, 2, 3, 4, 20, 5, 6, 7, + 8, 9, 1, 2, 3, 4, 5, 6, 7, 8 }; + uint64_t expect2[] = { 32767, -32768, -32767 }; + uint64_t expect3[] = { INT64_MIN, INT64_MAX }; + uint64_t expect4[] = { 1 }; + uint64_t expect5[] = { UINT64_MAX }; + uint64_t expect6[] = { UINT64_MAX - 2, UINT64_MAX - 1, UINT64_MAX }; + Error *err = NULL; + uint64List *res = NULL; + Visitor *v; + uint64_t val; + + /* Valid lists */ + + v = visitor_input_test_init(data, "1,2,0,2-4,20,5-9,1-8"); + check_ulist(v, expect1, ARRAY_SIZE(expect1)); + + v = visitor_input_test_init(data, "32767,-32768--32767"); + check_ulist(v, expect2, ARRAY_SIZE(expect2)); + + v = visitor_input_test_init(data, + "-9223372036854775808,9223372036854775807"); + check_ulist(v, expect3, ARRAY_SIZE(expect3)); + + v = visitor_input_test_init(data, "1-1"); + check_ulist(v, expect4, ARRAY_SIZE(expect4)); + + v = visitor_input_test_init(data, "18446744073709551615"); + check_ulist(v, expect5, ARRAY_SIZE(expect5)); + + v = visitor_input_test_init(data, + "18446744073709551613-18446744073709551615"); + check_ulist(v, expect6, ARRAY_SIZE(expect6)); + + /* Value too large */ + + v = visitor_input_test_init(data, "18446744073709551616"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Value too small */ + + v = visitor_input_test_init(data, "-18446744073709551616"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range not ascending */ + + v = visitor_input_test_init(data, "3-1"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + v = visitor_input_test_init(data, "18446744073709551615-0"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Range too big (65536 is the limit against DOS attacks) */ + + v = visitor_input_test_init(data, "0-65536"); + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Empty list */ + + v = visitor_input_test_init(data, ""); + visit_type_uint64List(v, NULL, &res, &error_abort); + g_assert(!res); + + /* Not a list */ + + v = visitor_input_test_init(data, "not an uint list"); + + visit_type_uint64List(v, NULL, &res, &err); + error_free_or_abort(&err); + g_assert(!res); + + /* Unvisited list tail */ + + v = visitor_input_test_init(data, "0,2-3"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, NULL, &val, &error_abort); + g_assert_cmpuint(val, ==, 0); + visit_type_uint64(v, NULL, &val, &error_abort); + g_assert_cmpuint(val, ==, 2); + + visit_check_list(v, &err); + error_free_or_abort(&err); + visit_end_list(v, NULL); + + /* Visit beyond end of list */ + + v = visitor_input_test_init(data, "0"); + + visit_start_list(v, NULL, NULL, 0, &error_abort); + visit_type_uint64(v, NULL, &val, &err); + g_assert_cmpuint(val, ==, 0); + visit_type_uint64(v, NULL, &val, &err); + error_free_or_abort(&err); + + visit_check_list(v, &error_abort); + visit_end_list(v, NULL); } static void test_visitor_in_bool(TestInputVisitorData *data, @@ -252,6 +380,19 @@ static void test_visitor_in_number(TestInputVisitorData *data, visit_type_number(v, NULL, &res, &err); g_assert(!err); g_assert_cmpfloat(res, ==, value); + + /* NaN and infinity has to be rejected */ + + v = visitor_input_test_init(data, "NaN"); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); + + v = visitor_input_test_init(data, "inf"); + + visit_type_number(v, NULL, &res, &err); + error_free_or_abort(&err); + } static void test_visitor_in_string(TestInputVisitorData *data, @@ -356,6 +497,8 @@ int main(int argc, char **argv) &in_visitor_data, test_visitor_in_int); input_visitor_test_add("/string-visitor/input/intList", &in_visitor_data, test_visitor_in_intList); + input_visitor_test_add("/string-visitor/input/uintList", + &in_visitor_data, test_visitor_in_uintList); input_visitor_test_add("/string-visitor/input/bool", &in_visitor_data, test_visitor_in_bool); input_visitor_test_add("/string-visitor/input/number", diff --git a/tests/test-util-filemonitor.c b/tests/test-util-filemonitor.c new file mode 100644 index 0000000000..46e781c022 --- /dev/null +++ b/tests/test-util-filemonitor.c @@ -0,0 +1,681 @@ +/* + * Tests for util/filemonitor-*.c + * + * Copyright 2018 Red Hat, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this library; if not, see . + * + */ + +#include "qemu/osdep.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "qemu/filemonitor.h" + +#include + +enum { + QFILE_MONITOR_TEST_OP_ADD_WATCH, + QFILE_MONITOR_TEST_OP_DEL_WATCH, + QFILE_MONITOR_TEST_OP_EVENT, + QFILE_MONITOR_TEST_OP_CREATE, + QFILE_MONITOR_TEST_OP_APPEND, + QFILE_MONITOR_TEST_OP_TRUNC, + QFILE_MONITOR_TEST_OP_RENAME, + QFILE_MONITOR_TEST_OP_TOUCH, + QFILE_MONITOR_TEST_OP_UNLINK, + QFILE_MONITOR_TEST_OP_MKDIR, + QFILE_MONITOR_TEST_OP_RMDIR, +}; + +typedef struct { + int type; + const char *filesrc; + const char *filedst; + int64_t *watchid; + int eventid; +} QFileMonitorTestOp; + +typedef struct { + int64_t id; + QFileMonitorEvent event; + char *filename; +} QFileMonitorTestRecord; + + +typedef struct { + QemuMutex lock; + GList *records; +} QFileMonitorTestData; + +static QemuMutex evlock; +static bool evstopping; +static bool evrunning; +static bool debug; + +/* + * Main function for a background thread that is + * running the event loop during the test + */ +static void * +qemu_file_monitor_test_event_loop(void *opaque G_GNUC_UNUSED) +{ + qemu_mutex_lock(&evlock); + + while (!evstopping) { + qemu_mutex_unlock(&evlock); + main_loop_wait(true); + qemu_mutex_lock(&evlock); + } + + evrunning = false; + qemu_mutex_unlock(&evlock); + return NULL; +} + + +/* + * File monitor event handler which simply maintains + * an ordered list of all events that it receives + */ +static void +qemu_file_monitor_test_handler(int64_t id, + QFileMonitorEvent event, + const char *filename, + void *opaque) +{ + QFileMonitorTestData *data = opaque; + QFileMonitorTestRecord *rec = g_new0(QFileMonitorTestRecord, 1); + + rec->id = id; + rec->event = event; + rec->filename = g_strdup(filename); + + qemu_mutex_lock(&data->lock); + data->records = g_list_append(data->records, rec); + qemu_mutex_unlock(&data->lock); +} + + +static void +qemu_file_monitor_test_record_free(QFileMonitorTestRecord *rec) +{ + g_free(rec->filename); + g_free(rec); +} + + +/* + * Get the next event record that has been received by + * the file monitor event handler. Since events are + * emitted in the background thread running the event + * loop, we can't assume there is a record available + * immediately. Thus we will sleep for upto 5 seconds + * to wait for the event to be queued for us. + */ +static QFileMonitorTestRecord * +qemu_file_monitor_test_next_record(QFileMonitorTestData *data) +{ + GTimer *timer = g_timer_new(); + QFileMonitorTestRecord *record = NULL; + GList *tmp; + + qemu_mutex_lock(&data->lock); + while (!data->records && g_timer_elapsed(timer, NULL) < 5) { + qemu_mutex_unlock(&data->lock); + usleep(10 * 1000); + qemu_mutex_lock(&data->lock); + } + if (data->records) { + record = data->records->data; + tmp = data->records; + data->records = g_list_remove_link(data->records, tmp); + g_list_free(tmp); + } + qemu_mutex_unlock(&data->lock); + + g_timer_destroy(timer); + return record; +} + + +/* + * Check whether the event record we retrieved matches + * data we were expecting to see for the event + */ +static bool +qemu_file_monitor_test_expect(QFileMonitorTestData *data, + int64_t id, + QFileMonitorEvent event, + const char *filename) +{ + QFileMonitorTestRecord *rec; + bool ret = false; + + rec = qemu_file_monitor_test_next_record(data); + + if (!rec) { + g_printerr("Missing event watch id %" PRIx64 " event %d file %s\n", + id, event, filename); + return false; + } + + if (id != rec->id) { + g_printerr("Expected watch id %" PRIx64 " but got %" PRIx64 "\n", + id, rec->id); + goto cleanup; + } + + if (event != rec->event) { + g_printerr("Expected event %d but got %d\n", event, rec->event); + goto cleanup; + } + + if (!g_str_equal(filename, rec->filename)) { + g_printerr("Expected filename %s but got %s\n", + filename, rec->filename); + goto cleanup; + } + + ret = true; + + cleanup: + qemu_file_monitor_test_record_free(rec); + return ret; +} + + +static void +test_file_monitor_events(void) +{ + int64_t watch0 = 0; + int64_t watch1 = 0; + int64_t watch2 = 0; + int64_t watch3 = 0; + int64_t watch4 = 0; + int64_t watch5 = 0; + QFileMonitorTestOp ops[] = { + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = NULL, .watchid = &watch0 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = &watch1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "two.txt", .watchid = &watch2 }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch1, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "three.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "three.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "three.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "three.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_RENAME, + .filesrc = "one.txt", .filedst = "two.txt" }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch1, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_APPEND, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_MODIFIED }, + + + { .type = QFILE_MONITOR_TEST_OP_TOUCH, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_ATTRIBUTES }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = &watch1 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "one.txt", .watchid = &watch3 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch3, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "one.txt", .watchid = &watch3 }, + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_MKDIR, + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "fish/", .watchid = &watch4 }, + { .type = QFILE_MONITOR_TEST_OP_ADD_WATCH, + .filesrc = "fish/one.txt", .watchid = &watch5 }, + { .type = QFILE_MONITOR_TEST_OP_CREATE, + .filesrc = "fish/one.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch4, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch5, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "fish/one.txt", .watchid = &watch5 }, + { .type = QFILE_MONITOR_TEST_OP_RENAME, + .filesrc = "fish/one.txt", .filedst = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "one.txt", .watchid = &watch4, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_CREATED }, + + + { .type = QFILE_MONITOR_TEST_OP_RMDIR, + .filesrc = "fish", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "", .watchid = &watch4, + .eventid = QFILE_MONITOR_EVENT_IGNORED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "fish", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "fish", .watchid = &watch4 }, + + + { .type = QFILE_MONITOR_TEST_OP_UNLINK, + .filesrc = "two.txt", }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch0, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + { .type = QFILE_MONITOR_TEST_OP_EVENT, + .filesrc = "two.txt", .watchid = &watch2, + .eventid = QFILE_MONITOR_EVENT_DELETED }, + + + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = "two.txt", .watchid = &watch2 }, + { .type = QFILE_MONITOR_TEST_OP_DEL_WATCH, + .filesrc = NULL, .watchid = &watch0 }, + }; + Error *local_err = NULL; + GError *gerr = NULL; + QFileMonitor *mon = qemu_file_monitor_new(&local_err); + QemuThread th; + GTimer *timer; + gchar *dir = NULL; + int err = -1; + gsize i; + char *pathsrc = NULL; + char *pathdst = NULL; + QFileMonitorTestData data; + GHashTable *ids = g_hash_table_new(g_int64_hash, g_int64_equal); + + qemu_mutex_init(&data.lock); + data.records = NULL; + + /* + * The file monitor needs the main loop running in + * order to receive events from inotify. We must + * thus spawn a background thread to run an event + * loop impl, while this thread triggers the + * actual file operations we're testing + */ + evrunning = 1; + evstopping = 0; + qemu_thread_create(&th, "event-loop", + qemu_file_monitor_test_event_loop, NULL, + QEMU_THREAD_JOINABLE); + + if (local_err) { + g_printerr("File monitoring not available: %s", + error_get_pretty(local_err)); + error_free(local_err); + return; + } + + dir = g_dir_make_tmp("test-util-filemonitor-XXXXXX", + &gerr); + if (!dir) { + g_printerr("Unable to create tmp dir %s", + gerr->message); + g_error_free(gerr); + abort(); + } + + /* + * Run through the operation sequence validating events + * as we go + */ + for (i = 0; i < G_N_ELEMENTS(ops); i++) { + const QFileMonitorTestOp *op = &(ops[i]); + int fd; + struct utimbuf ubuf; + char *watchdir; + const char *watchfile; + + pathsrc = g_strdup_printf("%s/%s", dir, op->filesrc); + if (op->filedst) { + pathdst = g_strdup_printf("%s/%s", dir, op->filedst); + } + + switch (op->type) { + case QFILE_MONITOR_TEST_OP_ADD_WATCH: + if (debug) { + g_printerr("Add watch %s %s\n", + dir, op->filesrc); + } + if (op->filesrc && strchr(op->filesrc, '/')) { + watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); + watchfile = strrchr(watchdir, '/'); + *(char *)watchfile = '\0'; + watchfile++; + if (*watchfile == '\0') { + watchfile = NULL; + } + } else { + watchdir = g_strdup(dir); + watchfile = op->filesrc; + } + *op->watchid = + qemu_file_monitor_add_watch(mon, + watchdir, + watchfile, + qemu_file_monitor_test_handler, + &data, + &local_err); + g_free(watchdir); + if (*op->watchid < 0) { + g_printerr("Unable to add watch %s", + error_get_pretty(local_err)); + goto cleanup; + } + if (debug) { + g_printerr("Watch ID %" PRIx64 "\n", *op->watchid); + } + if (g_hash_table_contains(ids, op->watchid)) { + g_printerr("Watch ID %" PRIx64 "already exists", *op->watchid); + goto cleanup; + } + g_hash_table_add(ids, op->watchid); + break; + case QFILE_MONITOR_TEST_OP_DEL_WATCH: + if (debug) { + g_printerr("Del watch %s %" PRIx64 "\n", dir, *op->watchid); + } + if (op->filesrc && strchr(op->filesrc, '/')) { + watchdir = g_strdup_printf("%s/%s", dir, op->filesrc); + watchfile = strrchr(watchdir, '/'); + *(char *)watchfile = '\0'; + } else { + watchdir = g_strdup(dir); + } + g_hash_table_remove(ids, op->watchid); + qemu_file_monitor_remove_watch(mon, + watchdir, + *op->watchid); + g_free(watchdir); + break; + case QFILE_MONITOR_TEST_OP_EVENT: + if (debug) { + g_printerr("Event id=%" PRIx64 " event=%d file=%s\n", + *op->watchid, op->eventid, op->filesrc); + } + if (!qemu_file_monitor_test_expect( + &data, *op->watchid, op->eventid, op->filesrc)) + goto cleanup; + break; + case QFILE_MONITOR_TEST_OP_CREATE: + if (debug) { + g_printerr("Create %s\n", pathsrc); + } + fd = open(pathsrc, O_WRONLY | O_CREAT, 0700); + if (fd < 0) { + g_printerr("Unable to create %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + close(fd); + break; + + case QFILE_MONITOR_TEST_OP_APPEND: + if (debug) { + g_printerr("Append %s\n", pathsrc); + } + fd = open(pathsrc, O_WRONLY | O_APPEND, 0700); + if (fd < 0) { + g_printerr("Unable to open %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + + if (write(fd, "Hello World", 10) != 10) { + g_printerr("Unable to write %s: %s", + pathsrc, strerror(errno)); + close(fd); + goto cleanup; + } + close(fd); + break; + + case QFILE_MONITOR_TEST_OP_TRUNC: + if (debug) { + g_printerr("Truncate %s\n", pathsrc); + } + if (truncate(pathsrc, 4) < 0) { + g_printerr("Unable to truncate %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_RENAME: + if (debug) { + g_printerr("Rename %s -> %s\n", pathsrc, pathdst); + } + if (rename(pathsrc, pathdst) < 0) { + g_printerr("Unable to rename %s to %s: %s", + pathsrc, pathdst, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_UNLINK: + if (debug) { + g_printerr("Unlink %s\n", pathsrc); + } + if (unlink(pathsrc) < 0) { + g_printerr("Unable to unlink %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_TOUCH: + if (debug) { + g_printerr("Touch %s\n", pathsrc); + } + ubuf.actime = 1024; + ubuf.modtime = 1025; + if (utime(pathsrc, &ubuf) < 0) { + g_printerr("Unable to touch %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_MKDIR: + if (debug) { + g_printerr("Mkdir %s\n", pathsrc); + } + if (mkdir(pathsrc, 0700) < 0) { + g_printerr("Unable to mkdir %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + case QFILE_MONITOR_TEST_OP_RMDIR: + if (debug) { + g_printerr("Rmdir %s\n", pathsrc); + } + if (rmdir(pathsrc) < 0) { + g_printerr("Unable to rmdir %s: %s", + pathsrc, strerror(errno)); + goto cleanup; + } + break; + + default: + g_assert_not_reached(); + } + + g_free(pathsrc); + g_free(pathdst); + pathsrc = pathdst = NULL; + } + + g_assert_cmpint(g_hash_table_size(ids), ==, 0); + + err = 0; + + cleanup: + g_free(pathsrc); + g_free(pathdst); + + qemu_mutex_lock(&evlock); + evstopping = 1; + timer = g_timer_new(); + while (evrunning && g_timer_elapsed(timer, NULL) < 5) { + qemu_mutex_unlock(&evlock); + usleep(10 * 1000); + qemu_mutex_lock(&evlock); + } + qemu_mutex_unlock(&evlock); + + if (g_timer_elapsed(timer, NULL) >= 5) { + g_printerr("Event loop failed to quit after 5 seconds\n"); + } + g_timer_destroy(timer); + + qemu_file_monitor_free(mon); + g_list_foreach(data.records, + (GFunc)qemu_file_monitor_test_record_free, NULL); + g_list_free(data.records); + qemu_mutex_destroy(&data.lock); + if (dir) { + for (i = 0; i < G_N_ELEMENTS(ops); i++) { + const QFileMonitorTestOp *op = &(ops[i]); + char *path = g_strdup_printf("%s/%s", + dir, op->filesrc); + if (op->type == QFILE_MONITOR_TEST_OP_MKDIR) { + rmdir(path); + g_free(path); + } else { + unlink(path); + g_free(path); + if (op->filedst) { + path = g_strdup_printf("%s/%s", + dir, op->filedst); + unlink(path); + g_free(path); + } + } + } + if (rmdir(dir) < 0) { + g_printerr("Failed to remove %s: %s\n", + dir, strerror(errno)); + abort(); + } + } + g_hash_table_unref(ids); + g_free(dir); + g_assert(err == 0); +} + + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qemu_init_main_loop(&error_abort); + + qemu_mutex_init(&evlock); + + debug = getenv("FILEMONITOR_DEBUG") != NULL; + g_test_add_func("/util/filemonitor", test_file_monitor_events); + + return g_test_run(); +} diff --git a/tests/test-vmstate.c b/tests/test-vmstate.c index 37a7a93784..fc8ce62471 100644 --- a/tests/test-vmstate.c +++ b/tests/test-vmstate.c @@ -284,6 +284,55 @@ static void test_simple_primitive(void) FIELD_EQUAL(i64_2); } +typedef struct TestSimpleArray { + uint16_t u16_1[3]; +} TestSimpleArray; + +/* Object instantiation, we are going to use it in more than one test */ + +TestSimpleArray obj_simple_arr = { + .u16_1 = { 0x42, 0x43, 0x44 }, +}; + +/* Description of the values. If you add a primitive type + you are expected to add a test here */ + +static const VMStateDescription vmstate_simple_arr = { + .name = "simple/array", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT16_ARRAY(u16_1, TestSimpleArray, 3), + VMSTATE_END_OF_LIST() + } +}; + +uint8_t wire_simple_arr[] = { + /* u16_1 */ 0x00, 0x42, + /* u16_1 */ 0x00, 0x43, + /* u16_1 */ 0x00, 0x44, + QEMU_VM_EOF, /* just to ensure we won't get EOF reported prematurely */ +}; + +static void obj_simple_arr_copy(void *target, void *source) +{ + memcpy(target, source, sizeof(TestSimpleArray)); +} + +static void test_simple_array(void) +{ + TestSimpleArray obj, obj_clone; + + memset(&obj, 0, sizeof(obj)); + save_vmstate(&vmstate_simple_arr, &obj_simple_arr); + + compare_vmstate(wire_simple_arr, sizeof(wire_simple_arr)); + + SUCCESS(load_vmstate(&vmstate_simple_arr, &obj, &obj_clone, + obj_simple_arr_copy, 1, wire_simple_arr, + sizeof(wire_simple_arr))); +} + typedef struct TestStruct { uint32_t a, b, c, e; uint64_t d, f; @@ -630,7 +679,7 @@ struct TestQtailqElement { typedef struct TestQtailq { int16_t i16; - QTAILQ_HEAD(TestQtailqHead, TestQtailqElement) q; + QTAILQ_HEAD(, TestQtailqElement) q; int32_t i32; } TestQtailq; @@ -735,9 +784,9 @@ static void test_load_q(void) g_assert_cmpint(eof, ==, QEMU_VM_EOF); TestQtailqElement *qele_from = QTAILQ_FIRST(&obj_q.q); - TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q, TestQtailqHead); + TestQtailqElement *qlast_from = QTAILQ_LAST(&obj_q.q); TestQtailqElement *qele_to = QTAILQ_FIRST(&tgt.q); - TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q, TestQtailqHead); + TestQtailqElement *qlast_to = QTAILQ_LAST(&tgt.q); while (1) { g_assert_cmpint(qele_to->b, ==, qele_from->b); @@ -755,7 +804,7 @@ static void test_load_q(void) /* clean up */ TestQtailqElement *qele; while (!QTAILQ_EMPTY(&tgt.q)) { - qele = QTAILQ_LAST(&tgt.q, TestQtailqHead); + qele = QTAILQ_LAST(&tgt.q); QTAILQ_REMOVE(&tgt.q, qele, next); free(qele); qele = NULL; @@ -863,6 +912,7 @@ int main(int argc, char **argv) g_test_init(&argc, &argv, NULL); g_test_add_func("/vmstate/simple/primitive", test_simple_primitive); + g_test_add_func("/vmstate/simple/array", test_simple_array); g_test_add_func("/vmstate/versioned/load/v1", test_load_v1); g_test_add_func("/vmstate/versioned/load/v2", test_load_v2); g_test_add_func("/vmstate/field_exists/load/noskip", test_load_noskip); diff --git a/tests/test-x86-cpuid-compat.c b/tests/test-x86-cpuid-compat.c index 84ce9c71ae..e75b959950 100644 --- a/tests/test-x86-cpuid-compat.c +++ b/tests/test-x86-cpuid-compat.c @@ -35,7 +35,6 @@ static QObject *qom_get(const char *path, const char *prop) return ret; } -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS static bool qom_get_bool(const char *path, const char *prop) { QBool *value = qobject_to(QBool, qom_get(path, prop)); @@ -44,7 +43,6 @@ static bool qom_get_bool(const char *path, const char *prop) qobject_unref(value); return b; } -#endif typedef struct CpuidTestArgs { const char *cmdline; @@ -168,7 +166,6 @@ static FeatureTestArgs *add_feature_test(const char *name, const char *cmdline, return args; } -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS static void test_plus_minus_subprocess(void) { char *path; @@ -210,17 +207,14 @@ static void test_plus_minus(void) "Don't mix both \"+cx8\" and \"cx8=off\"*"); g_test_trap_assert_stdout(""); } -#endif int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); -#ifdef CONFIG_HAS_GLIB_SUBPROCESS_TESTS g_test_add_func("/x86/cpuid/parsing-plus-minus/subprocess", test_plus_minus_subprocess); g_test_add_func("/x86/cpuid/parsing-plus-minus", test_plus_minus); -#endif /* Original level values for CPU models: */ add_cpuid_test("x86/cpuid/phenom/level", diff --git a/tests/tpci200-test.c b/tests/tpci200-test.c deleted file mode 100644 index 0321ec27ec..0000000000 --- a/tests/tpci200-test.c +++ /dev/null @@ -1,31 +0,0 @@ -/* - * QTest testcase for tpci200 PCI-IndustryPack bridge - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * 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 "libqtest.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) -{ -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/tpci200/nop", nop); - - qtest_start("-device tpci200"); - ret = g_test_run(); - - qtest_end(); - - return ret; -} diff --git a/tests/tpm-crb-test.c b/tests/tpm-crb-test.c index d8f9569203..6fde579bab 100644 --- a/tests/tpm-crb-test.c +++ b/tests/tpm-crb-test.c @@ -151,6 +151,7 @@ int main(int argc, char **argv) test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); g_mutex_init(&test.data_mutex); g_cond_init(&test.data_cond); + test.data_cond_signal = false; thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); tpm_emu_test_wait_cond(&test); diff --git a/tests/tpm-emu.c b/tests/tpm-emu.c index 8c2bd53cad..125e697181 100644 --- a/tests/tpm-emu.c +++ b/tests/tpm-emu.c @@ -23,9 +23,14 @@ void tpm_emu_test_wait_cond(TestState *s) gint64 end_time = g_get_monotonic_time() + 5 * G_TIME_SPAN_SECOND; g_mutex_lock(&s->data_mutex); - if (!g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { + + if (!s->data_cond_signal && + !g_cond_wait_until(&s->data_cond, &s->data_mutex, end_time)) { g_assert_not_reached(); } + + s->data_cond_signal = false; + g_mutex_unlock(&s->data_mutex); } @@ -72,6 +77,10 @@ void *tpm_emu_ctrl_thread(void *data) QIOChannel *ioc; qio_channel_socket_listen_sync(lioc, s->addr, &error_abort); + + g_mutex_lock(&s->data_mutex); + s->data_cond_signal = true; + g_mutex_unlock(&s->data_mutex); g_cond_signal(&s->data_cond); qio_channel_wait(QIO_CHANNEL(lioc), G_IO_IN); diff --git a/tests/tpm-emu.h b/tests/tpm-emu.h index 08f902485e..8eb802a79e 100644 --- a/tests/tpm-emu.h +++ b/tests/tpm-emu.h @@ -26,6 +26,7 @@ struct tpm_hdr { typedef struct TestState { GMutex data_mutex; GCond data_cond; + bool data_cond_signal; SocketAddress *addr; QIOChannel *tpm_ioc; GThread *emu_tpm_thread; diff --git a/tests/tpm-tests.c b/tests/tpm-tests.c index 10c6592aac..582ec0cfd4 100644 --- a/tests/tpm-tests.c +++ b/tests/tpm-tests.c @@ -18,6 +18,17 @@ #include "libqtest.h" #include "tpm-tests.h" +static bool +tpm_test_swtpm_skip(void) +{ + if (!tpm_util_swtpm_has_tpm2()) { + g_test_message("swtpm not in PATH or missing --tpm2 support"); + return true; + } + + return false; +} + void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, const char *ifmodel) { @@ -28,12 +39,13 @@ void tpm_test_swtpm_test(const char *src_tpm_path, tx_func *tx, GPid swtpm_pid; GError *error = NULL; - succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error); - /* succ may be false if swtpm is not available */ - if (!succ) { + if (tpm_test_swtpm_skip()) { return; } + succ = tpm_util_swtpm_start(src_tpm_path, &swtpm_pid, &addr, &error); + g_assert_true(succ); + args = g_strdup_printf( "-chardev socket,id=chr,path=%s " "-tpmdev emulator,id=dev,chardev=chr " @@ -74,19 +86,17 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path, GError *error = NULL; QTestState *src_qemu, *dst_qemu; - succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid, - &src_tpm_addr, &error); - /* succ may be false if swtpm is not available */ - if (!succ) { + if (tpm_test_swtpm_skip()) { return; } + succ = tpm_util_swtpm_start(src_tpm_path, &src_tpm_pid, + &src_tpm_addr, &error); + g_assert_true(succ); + succ = tpm_util_swtpm_start(dst_tpm_path, &dst_tpm_pid, &dst_tpm_addr, &error); - /* succ may be false if swtpm is not available */ - if (!succ) { - goto err_src_tpm_kill; - } + g_assert_true(succ); tpm_util_migration_start_qemu(&src_qemu, &dst_qemu, src_tpm_addr, dst_tpm_addr, uri, @@ -118,7 +128,6 @@ void tpm_test_swtpm_migration_test(const char *src_tpm_path, qapi_free_SocketAddress(dst_tpm_addr); } -err_src_tpm_kill: tpm_util_swtpm_kill(src_tpm_pid); if (src_tpm_addr) { g_unlink(src_tpm_addr->u.q_unix.path); diff --git a/tests/tpm-tis-test.c b/tests/tpm-tis-test.c index 14754d9706..c8ec14888f 100644 --- a/tests/tpm-tis-test.c +++ b/tests/tpm-tis-test.c @@ -446,6 +446,7 @@ int main(int argc, char **argv) test.addr->u.q_unix.path = g_build_filename(tmp_path, "sock", NULL); g_mutex_init(&test.data_mutex); g_cond_init(&test.data_cond); + test.data_cond_signal = false; thread = g_thread_new(NULL, tpm_emu_ctrl_thread, &test); tpm_emu_test_wait_cond(&test); diff --git a/tests/tpm-util.c b/tests/tpm-util.c index 672cedf905..e08b137651 100644 --- a/tests/tpm-util.c +++ b/tests/tpm-util.c @@ -22,8 +22,6 @@ #define TIS_REG(LOCTY, REG) \ (TPM_TIS_ADDR_BASE + ((LOCTY) << 12) + REG) -static bool got_stop; - void tpm_util_crb_transfer(QTestState *s, const unsigned char *req, size_t req_size, unsigned char *rsp, size_t rsp_size) @@ -147,39 +145,33 @@ void tpm_util_pcrread(QTestState *s, tx_func *tx, g_assert_cmpmem(buffer, exp_resp_size, exp_resp, exp_resp_size); } -static gboolean tpm_util_swtpm_has_tpm2(void) +bool tpm_util_swtpm_has_tpm2(void) { - gint mystdout; - gboolean succ; - unsigned i; - char buffer[10240]; - ssize_t n; - gchar *swtpm_argv[] = { - g_strdup("swtpm"), g_strdup("socket"), g_strdup("--help"), NULL + bool has_tpm2 = false; + char *out = NULL; + static const char *argv[] = { + "swtpm", "socket", "--help", NULL }; - succ = g_spawn_async_with_pipes(NULL, swtpm_argv, NULL, - G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, - NULL, &mystdout, NULL, NULL); - if (!succ) { - goto cleanup; + if (!g_spawn_sync(NULL /* working_dir */, + (char **)argv, + NULL /* envp */, + G_SPAWN_SEARCH_PATH, + NULL /* child_setup */, + NULL /* user_data */, + &out, + NULL /* err */, + NULL /* exit_status */, + NULL)) { + return false; } - n = read(mystdout, buffer, sizeof(buffer) - 1); - if (n < 0) { - goto cleanup; - } - buffer[n] = 0; - if (!strstr(buffer, "--tpm2")) { - succ = false; + if (strstr(out, "--tpm2")) { + has_tpm2 = true; } - cleanup: - for (i = 0; swtpm_argv[i]; i++) { - g_free(swtpm_argv[i]); - } - - return succ; + g_free(out); + return has_tpm2; } gboolean tpm_util_swtpm_start(const char *path, GPid *pid, @@ -198,11 +190,6 @@ gboolean tpm_util_swtpm_start(const char *path, GPid *pid, gboolean succ; unsigned i; - succ = tpm_util_swtpm_has_tpm2(); - if (!succ) { - goto cleanup; - } - *addr = g_new0(SocketAddress, 1); (*addr)->type = SOCKET_ADDRESS_TYPE_UNIX; (*addr)->u.q_unix.path = g_build_filename(path, "sock", NULL); @@ -210,7 +197,6 @@ gboolean tpm_util_swtpm_start(const char *path, GPid *pid, succ = g_spawn_async(NULL, swtpm_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, pid, error); -cleanup: for (i = 0; swtpm_argv[i]; i++) { g_free(swtpm_argv[i]); } @@ -239,52 +225,27 @@ void tpm_util_swtpm_kill(GPid pid) void tpm_util_migrate(QTestState *who, const char *uri) { QDict *rsp; - gchar *cmd; - cmd = g_strdup_printf("{ 'execute': 'migrate'," - "'arguments': { 'uri': '%s' } }", - uri); - rsp = qtest_qmp(who, cmd); - g_free(cmd); + rsp = qtest_qmp(who, + "{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", + uri); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); } -/* - * Events can get in the way of responses we are actually waiting for. - */ -static QDict *tpm_util_wait_command(QTestState *who, const char *command) -{ - const char *event_string; - QDict *response; - - response = qtest_qmp(who, command); - - while (qdict_haskey(response, "event")) { - /* OK, it was an event */ - event_string = qdict_get_str(response, "event"); - if (!strcmp(event_string, "STOP")) { - got_stop = true; - } - qobject_unref(response); - response = qtest_qmp_receive(who); - } - return response; -} - void tpm_util_wait_for_migration_complete(QTestState *who) { while (true) { - QDict *rsp, *rsp_return; + QDict *rsp_return; bool completed; const char *status; - rsp = tpm_util_wait_command(who, "{ 'execute': 'query-migrate' }"); - rsp_return = qdict_get_qdict(rsp, "return"); + qtest_qmp_send(who, "{ 'execute': 'query-migrate' }"); + rsp_return = qtest_qmp_receive_success(who, NULL, NULL); status = qdict_get_str(rsp_return, "status"); completed = strcmp(status, "completed") == 0; g_assert_cmpstr(status, !=, "failed"); - qobject_unref(rsp); + qobject_unref(rsp_return); if (completed) { return; } diff --git a/tests/tpm-util.h b/tests/tpm-util.h index 330b9657fe..5755698ad2 100644 --- a/tests/tpm-util.h +++ b/tests/tpm-util.h @@ -13,7 +13,6 @@ #ifndef TESTS_TPM_UTIL_H #define TESTS_TPM_UTIL_H -#include "qemu/osdep.h" #include "io/channel-socket.h" typedef void (tx_func)(QTestState *s, @@ -32,6 +31,8 @@ void tpm_util_pcrextend(QTestState *s, tx_func *tx); void tpm_util_pcrread(QTestState *s, tx_func *tx, const unsigned char *exp_resp, size_t exp_resp_size); +bool tpm_util_swtpm_has_tpm2(void); + gboolean tpm_util_swtpm_start(const char *path, GPid *pid, SocketAddress **addr, GError **error); void tpm_util_swtpm_kill(GPid pid); diff --git a/tests/uefi-test-tools/.gitignore b/tests/uefi-test-tools/.gitignore new file mode 100644 index 0000000000..9f246701de --- /dev/null +++ b/tests/uefi-test-tools/.gitignore @@ -0,0 +1,3 @@ +Build +Conf +log diff --git a/tests/uefi-test-tools/LICENSE b/tests/uefi-test-tools/LICENSE new file mode 100644 index 0000000000..38b78aecdb --- /dev/null +++ b/tests/uefi-test-tools/LICENSE @@ -0,0 +1,25 @@ +All the files in this directory and subdirectories are released under the +2-Clause BSD License (see header in each file). + +Copyright (C) 2019, Red Hat, Inc. + +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 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 HOLDER 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. diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile new file mode 100644 index 0000000000..1d78bc14d5 --- /dev/null +++ b/tests/uefi-test-tools/Makefile @@ -0,0 +1,106 @@ +# Makefile for the test helper UEFI applications that run in guests. +# +# Copyright (C) 2019, Red Hat, Inc. +# +# 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 +# . +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +edk2_dir := ../../roms/edk2 +images_dir := ../data/uefi-boot-images +emulation_targets := arm aarch64 i386 x86_64 +uefi_binaries := bios-tables-test +intermediate_suffixes := .efi .fat .iso.raw + +images: $(foreach binary,$(uefi_binaries), \ + $(foreach target,$(emulation_targets), \ + $(images_dir)/$(binary).$(target).iso.qcow2)) + +# Preserve all intermediate targets if the build succeeds. +# - Intermediate targets help with development & debugging. +# - Preserving intermediate targets also keeps spurious changes out of the +# final build products, in case the user re-runs "make" without any changes +# to the UEFI source code. Normally, the intermediate files would have been +# removed by the last "make" invocation, hence the re-run would rebuild them +# from the unchanged UEFI sources. Unfortunately, the "mkdosfs" and +# "genisoimage" utilities embed timestamp-based information in their outputs, +# which causes git to report differences for the tracked qcow2 ISO images. +.SECONDARY: $(foreach binary,$(uefi_binaries), \ + $(foreach target,$(emulation_targets), \ + $(foreach suffix,$(intermediate_suffixes), \ + Build/$(binary).$(target)$(suffix)))) + +# In the pattern rules below, the stem (%, $*) stands for +# "$(binary).$(target)". + +# Convert the raw ISO image to a qcow2 one, enabling compression, and using a +# small cluster size. This allows for small binary files under git control, +# hence for small binary patches. +$(images_dir)/%.iso.qcow2: Build/%.iso.raw + mkdir -p -- $(images_dir) + $${QTEST_QEMU_IMG:-qemu-img} convert -f raw -O qcow2 -c \ + -o cluster_size=512 -- $< $@ + +# Embed the "UEFI system partition" into an ISO9660 file system as an ElTorito +# boot image. +Build/%.iso.raw: Build/%.fat + genisoimage -input-charset ASCII -efi-boot $(notdir $<) -no-emul-boot \ + -quiet -o $@ -- $< + +# Define chained macros in order to map QEMU system emulation targets to +# *short* UEFI architecture identifiers. Periods are allowed in, and ultimately +# stripped from, the argument. +map_arm_to_uefi = $(subst arm,ARM,$(1)) +map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1))) +map_i386_to_uefi = $(subst i386,IA32,$(call map_aarch64_to_uefi,$(1))) +map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1))) +map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1))) + +# Format a "UEFI system partition", using the UEFI binary as the default boot +# loader. Add 10% size for filesystem metadata, round up to the next KB, and +# make sure the size is large enough for a FAT filesystem. Name the filesystem +# after the UEFI binary. (Excess characters are automatically dropped from the +# filesystem label.) +Build/%.fat: Build/%.efi + rm -f -- $@ + uefi_bin_b=$$(stat --format=%s -- $<) && \ + uefi_fat_kb=$$(( (uefi_bin_b * 11 / 10 + 1023) / 1024 )) && \ + uefi_fat_kb=$$(( uefi_fat_kb >= 64 ? uefi_fat_kb : 64 )) && \ + mkdosfs -C $@ -n $(basename $(@F)) -- $$uefi_fat_kb + MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI + MTOOLS_SKIP_CHECK=1 mmd -i $@ ::EFI/BOOT + MTOOLS_SKIP_CHECK=1 mcopy -i $@ -- $< \ + ::EFI/BOOT/BOOT$(call map_to_uefi,$(suffix $*)).EFI + +# In the pattern rules below, the stem (%, $*) stands for "$(target)" only. The +# association between the UEFI binary (such as "bios-tables-test") and the +# component name from the edk2 platform DSC file (such as "BiosTablesTest") is +# explicit in each rule. + +# "build.sh" invokes the "build" utility of edk2 BaseTools. In any given edk2 +# workspace, at most one "build" instance may be operating at a time. Therefore +# we must serialize the rebuilding of targets in this Makefile. +.NOTPARALLEL: + +# In turn, the "build" utility of edk2 BaseTools invokes another "make". +# Although the outer "make" process advertizes its job server to all child +# processes via MAKEFLAGS in the environment, the outer "make" closes the job +# server file descriptors (exposed in MAKEFLAGS) before executing a recipe -- +# unless the recipe is recognized as a recursive "make" recipe. Recipes that +# call $(MAKE) are classified automatically as recursive; for "build.sh" below, +# we must mark the recipe manually as recursive, by using the "+" indicator. +# This way, when the inner "make" starts a parallel build of the target edk2 +# module, it can communicate with the outer "make"'s job server. +Build/bios-tables-test.%.efi: build-edk2-tools + +./build.sh $(edk2_dir) BiosTablesTest $* $@ + +build-edk2-tools: + $(MAKE) -C $(edk2_dir)/BaseTools + +clean: + rm -rf Build Conf log + $(MAKE) -C $(edk2_dir)/BaseTools clean diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c new file mode 100644 index 0000000000..b208e17fb0 --- /dev/null +++ b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.c @@ -0,0 +1,130 @@ +/** @file + Populate the BIOS_TABLES_TEST structure. + + Copyright (C) 2019, Red Hat, Inc. + + 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 + . + + THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT + WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +**/ + +#include +#include +#include +#include +#include +#include +#include + +/** + Wait for a keypress with a message that the application is about to exit. +**/ +STATIC +VOID +WaitForExitKeyPress ( + VOID + ) +{ + EFI_STATUS Status; + UINTN Idx; + EFI_INPUT_KEY Key; + + if (gST->ConIn == NULL) { + return; + } + AsciiPrint ("%a: press any key to exit\n", gEfiCallerBaseName); + Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Idx); + if (EFI_ERROR (Status)) { + return; + } + gST->ConIn->ReadKeyStroke (gST->ConIn, &Key); +} + +EFI_STATUS +EFIAPI +BiosTablesTestMain ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + VOID *Pages; + volatile BIOS_TABLES_TEST *BiosTablesTest; + CONST VOID *Rsdp10; + CONST VOID *Rsdp20; + CONST EFI_CONFIGURATION_TABLE *ConfigTable; + CONST EFI_CONFIGURATION_TABLE *ConfigTablesEnd; + volatile EFI_GUID *InverseSignature; + UINTN Idx; + + Pages = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof *BiosTablesTest), + SIZE_1MB); + if (Pages == NULL) { + AsciiErrorPrint ("%a: AllocateAlignedPages() failed\n", + gEfiCallerBaseName); + // + // Assuming the application was launched by the boot manager as a boot + // loader, exiting with error will cause the boot manager to proceed with + // the remaining boot options. If there are no other boot options, the boot + // manager menu will be pulled up. Give the user a chance to read the error + // message. + // + WaitForExitKeyPress (); + return EFI_OUT_OF_RESOURCES; + } + + // + // Locate both gEfiAcpi10TableGuid and gEfiAcpi20TableGuid config tables in + // one go. + // + Rsdp10 = NULL; + Rsdp20 = NULL; + ConfigTable = gST->ConfigurationTable; + ConfigTablesEnd = gST->ConfigurationTable + gST->NumberOfTableEntries; + while ((Rsdp10 == NULL || Rsdp20 == NULL) && ConfigTable < ConfigTablesEnd) { + if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi10TableGuid)) { + Rsdp10 = ConfigTable->VendorTable; + } else if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi20TableGuid)) { + Rsdp20 = ConfigTable->VendorTable; + } + ++ConfigTable; + } + + AsciiPrint ("%a: BiosTablesTest=%p Rsdp10=%p Rsdp20=%p\n", + gEfiCallerBaseName, Pages, Rsdp10, Rsdp20); + + // + // Store the RSD PTR address(es) first, then the signature second. + // + BiosTablesTest = Pages; + BiosTablesTest->Rsdp10 = (UINTN)Rsdp10; + BiosTablesTest->Rsdp20 = (UINTN)Rsdp20; + + MemoryFence(); + + InverseSignature = &BiosTablesTest->InverseSignatureGuid; + InverseSignature->Data1 = gBiosTablesTestGuid.Data1; + InverseSignature->Data1 ^= MAX_UINT32; + InverseSignature->Data2 = gBiosTablesTestGuid.Data2; + InverseSignature->Data2 ^= MAX_UINT16; + InverseSignature->Data3 = gBiosTablesTestGuid.Data3; + InverseSignature->Data3 ^= MAX_UINT16; + for (Idx = 0; Idx < sizeof InverseSignature->Data4; ++Idx) { + InverseSignature->Data4[Idx] = gBiosTablesTestGuid.Data4[Idx]; + InverseSignature->Data4[Idx] ^= MAX_UINT8; + } + + // + // The wait below has dual purpose. First, it blocks the application without + // wasting VCPU cycles while the hypervisor is scanning guest RAM. Second, + // assuming the application was launched by the boot manager as a boot + // loader, exiting the app with success causes the boot manager to pull up + // the boot manager menu at once (regardless of other boot options); the wait + // gives the user a chance to read the info printed above. + // + WaitForExitKeyPress (); + return EFI_SUCCESS; +} diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf new file mode 100644 index 0000000000..924d8a80d0 --- /dev/null +++ b/tests/uefi-test-tools/UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf @@ -0,0 +1,41 @@ +## @file +# Populate the BIOS_TABLES_TEST structure. +# +# Copyright (C) 2019, Red Hat, Inc. +# +# 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 +# . +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +## + +[Defines] + INF_VERSION = 1.27 + BASE_NAME = BiosTablesTest + UEFI_SPECIFICATION_VERSION = 2.31 + FILE_GUID = 87f00433-3b7c-45c3-ae78-a56495bd4e62 + MODULE_TYPE = UEFI_APPLICATION + ENTRY_POINT = BiosTablesTestMain + +[Sources] + BiosTablesTest.c + +[LibraryClasses] + BaseLib + BaseMemoryLib + MemoryAllocationLib + UefiApplicationEntryPoint + UefiBootServicesTableLib + UefiLib + +[Guids] + gBiosTablesTestGuid + gEfiAcpi10TableGuid + gEfiAcpi20TableGuid + +[Packages] + MdePkg/MdePkg.dec + UefiTestToolsPkg/UefiTestToolsPkg.dec diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h b/tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h new file mode 100644 index 0000000000..0b72c61254 --- /dev/null +++ b/tests/uefi-test-tools/UefiTestToolsPkg/Include/Guid/BiosTablesTest.h @@ -0,0 +1,67 @@ +/** @file + Expose the address(es) of the ACPI RSD PTR table(s) in a MB-aligned structure + to the hypervisor. + + The hypervisor locates the MB-aligned structure based on the signature GUID + that is at offset 0 in the structure. Once the RSD PTR address(es) are + retrieved, the hypervisor may perform various ACPI checks. + + This feature is a development aid, for supporting ACPI table unit tests in + hypervisors. Do not enable in production builds. + + Copyright (C) 2019, Red Hat, Inc. + + 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 + . + + 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 __BIOS_TABLES_TEST_H__ +#define __BIOS_TABLES_TEST_H__ + +#include + +#define BIOS_TABLES_TEST_GUID \ + { \ + 0x5478594e, \ + 0xdfcb, \ + 0x425f, \ + { 0x8e, 0x42, 0xc8, 0xaf, 0xf8, 0x8a, 0x88, 0x7a } \ + } + +extern EFI_GUID gBiosTablesTestGuid; + +// +// The following structure must be allocated in Boot Services Data type memory, +// aligned at a 1MB boundary. +// +#pragma pack (1) +typedef struct { + // + // The signature GUID is written to the MB-aligned structure from + // gBiosTablesTestGuid, but with all bits inverted. That's the actual GUID + // value that the hypervisor should look for at each MB boundary, looping + // over all guest RAM pages with that alignment, until a match is found. The + // bit-flipping occurs in order not to store the actual GUID in any UEFI + // executable, which might confuse guest memory analysis. Note that EFI_GUID + // has little endian representation. + // + EFI_GUID InverseSignatureGuid; + // + // The Rsdp10 and Rsdp20 fields may be read when the signature GUID matches. + // Rsdp10 is the guest-physical address of the ACPI 1.0 specification RSD PTR + // table, in 8-byte little endian representation. Rsdp20 is the same, for the + // ACPI 2.0 or later specification RSD PTR table. Each of these fields may be + // zero (independently of the other) if the UEFI System Table does not + // provide the corresponding UEFI Configuration Table. + // + EFI_PHYSICAL_ADDRESS Rsdp10; + EFI_PHYSICAL_ADDRESS Rsdp20; +} BIOS_TABLES_TEST; +#pragma pack () + +#endif // __BIOS_TABLES_TEST_H__ diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec new file mode 100644 index 0000000000..ed3a2fe110 --- /dev/null +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dec @@ -0,0 +1,27 @@ +## @file +# edk2 package declaration for the test helper UEFI applications that run in +# guests. +# +# Copyright (C) 2019, Red Hat, Inc. +# +# 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 +# . +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +## + +[Defines] + DEC_SPECIFICATION = 1.27 + PACKAGE_NAME = UefiTestToolsPkg + PACKAGE_GUID = 7b3f1794-0c85-4b27-a536-44dbf0b0669c + PACKAGE_VERSION = 0.1 + +[Includes] + Include + +[Guids] + gBiosTablesTestGuid = {0x5478594e, 0xdfcb, 0x425f, {0x8e, 0x42, 0xc8, 0xaf, 0xf8, 0x8a, 0x88, 0x7a}} + diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc new file mode 100644 index 0000000000..c8511cd732 --- /dev/null +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc @@ -0,0 +1,69 @@ +## @file +# edk2 platform description for the test helper UEFI applications that run in +# guests. +# +# Copyright (C) 2019, Red Hat, Inc. +# +# 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 +# . +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. +## + +[Defines] + DSC_SPECIFICATION = 1.28 + PLATFORM_GUID = 6750ccc1-8365-49f0-8437-948e516a9f55 + PLATFORM_VERSION = 0.1 + PLATFORM_NAME = UefiTestTools + SKUID_IDENTIFIER = DEFAULT + SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64 + BUILD_TARGETS = DEBUG + +[BuildOptions.IA32] + GCC:*_*_IA32_CC_FLAGS = -mno-mmx -mno-sse + +[BuildOptions.X64] + GCC:*_*_X64_CC_FLAGS = -mno-mmx -mno-sse + +[BuildOptions.ARM.EDKII.UEFI_APPLICATION] + GCC:*_*_ARM_DLINK_FLAGS = -z common-page-size=0x1000 + +[BuildOptions.AARCH64.EDKII.UEFI_APPLICATION] + GCC:*_*_AARCH64_DLINK_FLAGS = -z common-page-size=0x1000 + +[BuildOptions] + GCC:*_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES + +[SkuIds] + 0|DEFAULT + +[LibraryClasses] + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLibDevicePathProtocol/UefiDevicePathLibDevicePathProtocol.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + +[LibraryClasses.ARM, LibraryClasses.AARCH64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLibOptDxe/BaseMemoryLibOptDxe.inf + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf + +[LibraryClasses.IA32, LibraryClasses.X64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLibRepStr/BaseMemoryLibRepStr.inf + +[PcdsFixedAtBuild] + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F + +[Components] + UefiTestToolsPkg/BiosTablesTest/BiosTablesTest.inf diff --git a/tests/uefi-test-tools/build.sh b/tests/uefi-test-tools/build.sh new file mode 100755 index 0000000000..155cb75c4d --- /dev/null +++ b/tests/uefi-test-tools/build.sh @@ -0,0 +1,145 @@ +#!/bin/bash + +# Build script that determines the edk2 toolchain to use, invokes the edk2 +# "build" utility, and copies the built UEFI binary to the requested location. +# +# Copyright (C) 2019, Red Hat, Inc. +# +# 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 +# . +# +# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT +# WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +set -e -u -C + +# Save the command line arguments. We need to reset $# to 0 before sourcing +# "edksetup.sh", as it will inherit $@. +program_name=$(basename -- "$0") +edk2_dir=$1 +dsc_component=$2 +emulation_target=$3 +uefi_binary=$4 +shift 4 + +# Set up the environment for edk2 building. +export PACKAGES_PATH=$(realpath -- "$edk2_dir") +export WORKSPACE=$PWD +mkdir -p Conf + +# Source "edksetup.sh" carefully. +set +e +u +C +source "$PACKAGES_PATH/edksetup.sh" +ret=$? +set -e -u -C +if [ $ret -ne 0 ]; then + exit $ret +fi + +# Map the QEMU system emulation target to the following types of architecture +# identifiers: +# - edk2, +# - gcc cross-compilation. +# Cover only those targets that are supported by the UEFI spec and edk2. +case "$emulation_target" in + (arm) + edk2_arch=ARM + gcc_arch=arm + ;; + (aarch64) + edk2_arch=AARCH64 + gcc_arch=aarch64 + ;; + (i386) + edk2_arch=IA32 + gcc_arch=i686 + ;; + (x86_64) + edk2_arch=X64 + gcc_arch=x86_64 + ;; + (*) + printf '%s: unknown/unsupported QEMU system emulation target "%s"\n' \ + "$program_name" "$emulation_target" >&2 + exit 1 + ;; +esac + +# Check if cross-compilation is needed. +host_arch=$(uname -m) +if [ "$gcc_arch" == "$host_arch" ] || + ( [ "$gcc_arch" == i686 ] && [ "$host_arch" == x86_64 ] ); then + cross_prefix= +else + cross_prefix=${gcc_arch}-linux-gnu- +fi + +# Expose cross_prefix (which is possibly empty) to the edk2 tools. While at it, +# determine the suitable edk2 toolchain as well. +# - For ARM and AARCH64, edk2 only offers the GCC5 toolchain tag, which covers +# the gcc-5+ releases. +# - For IA32 and X64, edk2 offers the GCC44 through GCC49 toolchain tags, in +# addition to GCC5. Unfortunately, the mapping between the toolchain tags and +# the actual gcc releases isn't entirely trivial. Run "git-blame" on +# "OvmfPkg/build.sh" in edk2 for more information. +# And, because the above is too simple, we have to assign cross_prefix to an +# edk2 build variable that is specific to both the toolchain tag and the target +# architecture. +case "$edk2_arch" in + (ARM) + edk2_toolchain=GCC5 + export GCC5_ARM_PREFIX=$cross_prefix + ;; + (AARCH64) + edk2_toolchain=GCC5 + export GCC5_AARCH64_PREFIX=$cross_prefix + ;; + (IA32|X64) + gcc_version=$("${cross_prefix}gcc" -v 2>&1 | tail -1 | awk '{print $3}') + case "$gcc_version" in + ([1-3].*|4.[0-3].*) + printf '%s: unsupported gcc version "%s"\n' \ + "$program_name" "$gcc_version" >&2 + exit 1 + ;; + (4.4.*) + edk2_toolchain=GCC44 + ;; + (4.5.*) + edk2_toolchain=GCC45 + ;; + (4.6.*) + edk2_toolchain=GCC46 + ;; + (4.7.*) + edk2_toolchain=GCC47 + ;; + (4.8.*) + edk2_toolchain=GCC48 + ;; + (4.9.*|6.[0-2].*) + edk2_toolchain=GCC49 + ;; + (*) + edk2_toolchain=GCC5 + ;; + esac + eval "export ${edk2_toolchain}_BIN=\$cross_prefix" + ;; +esac + +# Build the UEFI binary +mkdir -p log +build \ + --arch="$edk2_arch" \ + --buildtarget=DEBUG \ + --platform=UefiTestToolsPkg/UefiTestToolsPkg.dsc \ + --tagname="$edk2_toolchain" \ + --module="UefiTestToolsPkg/$dsc_component/$dsc_component.inf" \ + --log="log/$dsc_component.$edk2_arch.log" \ + --report-file="log/$dsc_component.$edk2_arch.report" +cp -a -- \ + "Build/UefiTestTools/DEBUG_${edk2_toolchain}/$edk2_arch/$dsc_component.efi" \ + "$uefi_binary" diff --git a/tests/usb-hcd-ehci-test.c b/tests/usb-hcd-ehci-test.c index 55d4743a2a..8bc3e44189 100644 --- a/tests/usb-hcd-ehci-test.c +++ b/tests/usb-hcd-ehci-test.c @@ -52,7 +52,7 @@ static void ehci_port_test(struct qhc *hc, int port, uint32_t expect) static void test_init(void) { - pcibus = qpci_init_pc(global_qtest, NULL); + pcibus = qpci_new_pc(global_qtest, NULL); g_assert(pcibus != NULL); qusb_pci_init_one(pcibus, &uhci1, QPCI_DEVFN(0x1d, 0), 4); @@ -139,7 +139,7 @@ static void pci_ehci_port_3_hotplug(void) static void pci_ehci_port_hotplug(void) { - usb_test_hotplug("ich9-ehci-1", 3, pci_ehci_port_3_hotplug); + usb_test_hotplug("ich9-ehci-1", "3", pci_ehci_port_3_hotplug); } diff --git a/tests/usb-hcd-ohci-test.c b/tests/usb-hcd-ohci-test.c index 4758813d78..98af02e898 100644 --- a/tests/usb-hcd-ohci-test.c +++ b/tests/usb-hcd-ohci-test.c @@ -10,30 +10,58 @@ #include "qemu/osdep.h" #include "libqtest.h" #include "libqos/usb.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" +typedef struct QOHCI_PCI QOHCI_PCI; -static void test_ohci_init(void) +struct QOHCI_PCI { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void test_ohci_hotplug(void *obj, void *data, QGuestAllocator *alloc) { - + usb_test_hotplug("ohci", "1", NULL); } -static void test_ohci_hotplug(void) +static void *ohci_pci_get_driver(void *obj, const char *interface) { - usb_test_hotplug("ohci", 1, NULL); + QOHCI_PCI *ohci_pci = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &ohci_pci->dev; + } + + fprintf(stderr, "%s not present in pci-ohci\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *ohci_pci_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QOHCI_PCI *ohci_pci = g_new0(QOHCI_PCI, 1); + ohci_pci->obj.get_driver = ohci_pci_get_driver; - g_test_init(&argc, &argv, NULL); - - qtest_add_func("/ohci/pci/init", test_ohci_init); - qtest_add_func("/ohci/pci/hotplug", test_ohci_hotplug); - - qtest_start("-device pci-ohci,id=ohci"); - ret = g_test_run(); - qtest_end(); - - return ret; + return &ohci_pci->obj; } + +static void ohci_pci_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0,id=ohci", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("pci-ohci", ohci_pci_create); + qos_node_consumes("pci-ohci", "pci-bus", &opts); + qos_node_produces("pci-ohci", "pci-device"); +} + +libqos_init(ohci_pci_register_nodes); + +static void register_ohci_pci_test(void) +{ + qos_add_test("ohci_pci-test-hotplug", "pci-ohci", test_ohci_hotplug, NULL); +} + +libqos_init(register_ohci_pci_test); diff --git a/tests/usb-hcd-uhci-test.c b/tests/usb-hcd-uhci-test.c index 6a7e5a2fed..a119d6d5c8 100644 --- a/tests/usb-hcd-uhci-test.c +++ b/tests/usb-hcd-uhci-test.c @@ -43,12 +43,12 @@ static void test_port_2(void) static void test_uhci_hotplug(void) { - usb_test_hotplug("uhci", 2, test_port_2); + usb_test_hotplug("uhci", "2", test_port_2); } static void test_usb_storage_hotplug(void) { - qtest_qmp_device_add("usb-storage", "usbdev0", "'drive': 'drive0'"); + qtest_qmp_device_add("usb-storage", "usbdev0", "{'drive': 'drive0'}"); qtest_qmp_device_del("usbdev0"); } diff --git a/tests/usb-hcd-xhci-test.c b/tests/usb-hcd-xhci-test.c index 5b1b681bf2..9eb24b00e4 100644 --- a/tests/usb-hcd-xhci-test.c +++ b/tests/usb-hcd-xhci-test.c @@ -18,13 +18,13 @@ static void test_xhci_init(void) static void test_xhci_hotplug(void) { - usb_test_hotplug("xhci", 1, NULL); + usb_test_hotplug("xhci", "1", NULL); } static void test_usb_uas_hotplug(void) { - qtest_qmp_device_add("usb-uas", "uas", NULL); - qtest_qmp_device_add("scsi-hd", "scsihd", "'drive': 'drive0'"); + qtest_qmp_device_add("usb-uas", "uas", "{}"); + qtest_qmp_device_add("scsi-hd", "scsihd", "{'drive': 'drive0'}"); /* TODO: UAS HBA driver in libqos, to check that @@ -37,10 +37,10 @@ static void test_usb_uas_hotplug(void) static void test_usb_ccid_hotplug(void) { - qtest_qmp_device_add("usb-ccid", "ccid", NULL); + qtest_qmp_device_add("usb-ccid", "ccid", "{}"); qtest_qmp_device_del("ccid"); /* check the device can be added again */ - qtest_qmp_device_add("usb-ccid", "ccid", NULL); + qtest_qmp_device_add("usb-ccid", "ccid", "{}"); qtest_qmp_device_del("ccid"); } diff --git a/tests/vhost-user-bridge.c b/tests/vhost-user-bridge.c index 0884294141..0033b61f2e 100644 --- a/tests/vhost-user-bridge.c +++ b/tests/vhost-user-bridge.c @@ -29,8 +29,8 @@ #define _FILE_OFFSET_BITS 64 -#include "qemu/atomic.h" #include "qemu/osdep.h" +#include "qemu/atomic.h" #include "qemu/iov.h" #include "standard-headers/linux/virtio_net.h" #include "contrib/libvhost-user/libvhost-user.h" @@ -323,7 +323,7 @@ vubr_backend_recv_cb(int sock, void *ctx) .msg_name = (struct sockaddr *) &vubr->backend_udp_dest, .msg_namelen = sizeof(struct sockaddr_in), .msg_iov = sg, - .msg_iovlen = elem->in_num, + .msg_iovlen = num, .msg_flags = MSG_DONTWAIT, }; do { diff --git a/tests/vhost-user-test.c b/tests/vhost-user-test.c index fecc832d99..3817966010 100644 --- a/tests/vhost-user-test.c +++ b/tests/vhost-user-test.c @@ -27,10 +27,13 @@ #include "libqos/malloc-pc.h" #include "hw/virtio/virtio-net.h" -#include -#include -#include +#include "standard-headers/linux/vhost_types.h" +#include "standard-headers/linux/virtio_ids.h" +#include "standard-headers/linux/virtio_net.h" + +#ifdef CONFIG_LINUX #include +#endif #define QEMU_CMD_MEM " -m %d -object memory-backend-file,id=mem,size=%dM," \ @@ -38,8 +41,7 @@ #define QEMU_CMD_MEMFD " -m %d -object memory-backend-memfd,id=mem,size=%dM," \ " -numa node,memdev=mem" #define QEMU_CMD_CHR " -chardev socket,id=%s,path=%s%s" -#define QEMU_CMD_NETDEV " -netdev vhost-user,id=net0,chardev=%s,vhostforce" -#define QEMU_CMD_NET " -device virtio-net-pci,netdev=net0" +#define QEMU_CMD_NETDEV " -netdev vhost-user,id=hs0,chardev=%s,vhostforce" #define HUGETLBFS_MAGIC 0x958458f6 @@ -51,6 +53,7 @@ #define VHOST_USER_F_PROTOCOL_FEATURES 30 #define VHOST_USER_PROTOCOL_F_MQ 0 #define VHOST_USER_PROTOCOL_F_LOG_SHMFD 1 +#define VHOST_USER_PROTOCOL_F_CROSS_ENDIAN 6 #define VHOST_LOG_PAGE 0x1000 @@ -132,16 +135,17 @@ enum { }; typedef struct TestServer { - QPCIBus *bus; - QVirtioPCIDevice *dev; - QVirtQueue *vq[VHOST_MAX_VIRTQUEUES]; gchar *socket_path; gchar *mig_path; gchar *chr_name; + gchar *tmpfs; CharBackend chr; int fds_num; int fds[VHOST_MEMORY_MAX_NREGIONS]; VhostUserMemory memory; + GMainContext *context; + GMainLoop *loop; + GThread *thread; GMutex data_mutex; GCond data_cond; int log_fd; @@ -149,87 +153,49 @@ typedef struct TestServer { bool test_fail; int test_flags; int queues; - QGuestAllocator *alloc; } TestServer; +static const char *init_hugepagefs(void); static TestServer *test_server_new(const gchar *name); static void test_server_free(TestServer *server); static void test_server_listen(TestServer *server); -static const char *tmpfs; -static const char *root; - enum test_memfd { TEST_MEMFD_AUTO, TEST_MEMFD_YES, TEST_MEMFD_NO, }; -static char *get_qemu_cmd(TestServer *s, - int mem, enum test_memfd memfd, const char *mem_path, - const char *chr_opts, const char *extra) +static void append_vhost_opts(TestServer *s, GString *cmd_line, + const char *chr_opts) { - if (memfd == TEST_MEMFD_AUTO && qemu_memfd_check()) { - memfd = TEST_MEMFD_YES; + g_string_append_printf(cmd_line, QEMU_CMD_CHR QEMU_CMD_NETDEV, + s->chr_name, s->socket_path, + chr_opts, s->chr_name); +} + +static void append_mem_opts(TestServer *server, GString *cmd_line, + int size, enum test_memfd memfd) +{ + if (memfd == TEST_MEMFD_AUTO) { + memfd = qemu_memfd_check(MFD_ALLOW_SEALING) ? TEST_MEMFD_YES + : TEST_MEMFD_NO; } if (memfd == TEST_MEMFD_YES) { - return g_strdup_printf(QEMU_CMD_MEMFD QEMU_CMD_CHR - QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem, - s->chr_name, s->socket_path, - chr_opts, s->chr_name, extra); + g_string_append_printf(cmd_line, QEMU_CMD_MEMFD, size, size); } else { - return g_strdup_printf(QEMU_CMD_MEM QEMU_CMD_CHR - QEMU_CMD_NETDEV QEMU_CMD_NET "%s", mem, mem, - mem_path, s->chr_name, s->socket_path, - chr_opts, s->chr_name, extra); + const char *root = init_hugepagefs() ? : server->tmpfs; + + g_string_append_printf(cmd_line, QEMU_CMD_MEM, size, size, root); } } -static void init_virtio_dev(TestServer *s, uint32_t features_mask) -{ - uint32_t features; - int i; - - s->bus = qpci_init_pc(global_qtest, NULL); - g_assert_nonnull(s->bus); - - s->dev = qvirtio_pci_device_find(s->bus, VIRTIO_ID_NET); - g_assert_nonnull(s->dev); - - qvirtio_pci_device_enable(s->dev); - qvirtio_reset(&s->dev->vdev); - qvirtio_set_acknowledge(&s->dev->vdev); - qvirtio_set_driver(&s->dev->vdev); - - s->alloc = pc_alloc_init(global_qtest); - - for (i = 0; i < s->queues * 2; i++) { - s->vq[i] = qvirtqueue_setup(&s->dev->vdev, s->alloc, i); - } - - features = qvirtio_get_features(&s->dev->vdev); - features = features & features_mask; - qvirtio_set_features(&s->dev->vdev, features); - - qvirtio_set_driver_ok(&s->dev->vdev); -} - -static void uninit_virtio_dev(TestServer *s) -{ - int i; - - for (i = 0; i < s->queues * 2; i++) { - qvirtqueue_cleanup(s->dev->vdev.bus, s->vq[i], s->alloc); - } - pc_alloc_uninit(s->alloc); - - qvirtio_pci_device_free(s->dev); -} - -static void wait_for_fds(TestServer *s) +static bool wait_for_fds(TestServer *s) { gint64 end_time; + bool got_region; + int i; g_mutex_lock(&s->data_mutex); @@ -247,16 +213,27 @@ static void wait_for_fds(TestServer *s) g_assert_cmpint(s->fds_num, ==, s->memory.nregions); g_mutex_unlock(&s->data_mutex); + + got_region = false; + for (i = 0; i < s->memory.nregions; ++i) { + VhostUserMemoryRegion *reg = &s->memory.regions[i]; + if (reg->guest_phys_addr == 0) { + got_region = true; + break; + } + } + if (!got_region) { + g_test_skip("No memory at address 0x0"); + } + return got_region; } -static void read_guest_mem_server(TestServer *s) +static void read_guest_mem_server(QTestState *qts, TestServer *s) { - uint32_t *guest_mem; + uint8_t *guest_mem; int i, j; size_t size; - wait_for_fds(s); - g_mutex_lock(&s->data_mutex); /* iterate all regions */ @@ -278,8 +255,8 @@ static void read_guest_mem_server(TestServer *s) g_assert(guest_mem != MAP_FAILED); guest_mem += (s->memory.regions[i].mmap_offset / sizeof(*guest_mem)); - for (j = 0; j < 256; j++) { - uint32_t a = readl(s->memory.regions[i].guest_phys_addr + j*4); + for (j = 0; j < 1024; j++) { + uint32_t a = qtest_readb(qts, s->memory.regions[i].guest_phys_addr + j); uint32_t b = guest_mem[j]; g_assert_cmpint(a, ==, b); @@ -309,7 +286,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) CharBackend *chr = &s->chr; VhostUserMsg msg; uint8_t *p = (uint8_t *) &msg; - int fd; + int fd = -1; if (s->test_fail) { qemu_chr_fe_disconnect(chr); @@ -318,7 +295,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) } if (size != VHOST_USER_HDR_SIZE) { - g_test_message("Wrong message size received %d\n", size); + g_test_message("Wrong message size received %d", size); return; } @@ -329,7 +306,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) p += VHOST_USER_HDR_SIZE; size = qemu_chr_fe_read_all(chr, p, msg.size); if (size != msg.size) { - g_test_message("Wrong message size received %d != %d\n", + g_test_message("Wrong message size received %d != %d", size, msg.size); return; } @@ -354,8 +331,8 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) break; case VHOST_USER_SET_FEATURES: - g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), - !=, 0ULL); + g_assert_cmpint(msg.payload.u64 & (0x1ULL << VHOST_USER_F_PROTOCOL_FEATURES), + !=, 0ULL); if (s->test_flags == TEST_FLAGS_DISCONNECT) { qemu_chr_fe_disconnect(chr); s->test_flags = TEST_FLAGS_BAD; @@ -367,6 +344,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) msg.flags |= VHOST_USER_REPLY_MASK; msg.size = sizeof(m.payload.u64); msg.payload.u64 = 1 << VHOST_USER_PROTOCOL_F_LOG_SHMFD; + msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_CROSS_ENDIAN; if (s->queues > 1) { msg.payload.u64 |= 1 << VHOST_USER_PROTOCOL_F_MQ; } @@ -384,6 +362,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) assert(msg.payload.state.index < s->queues * 2); s->rings &= ~(0x1ULL << msg.payload.state.index); + g_cond_broadcast(&s->data_cond); break; case VHOST_USER_SET_MEM_TABLE: @@ -393,7 +372,7 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) G_N_ELEMENTS(s->fds)); /* signal the test that it can continue */ - g_cond_signal(&s->data_cond); + g_cond_broadcast(&s->data_cond); break; case VHOST_USER_SET_VRING_KICK: @@ -419,12 +398,13 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) p = (uint8_t *) &msg; qemu_chr_fe_write_all(chr, p, VHOST_USER_HDR_SIZE); - g_cond_signal(&s->data_cond); + g_cond_broadcast(&s->data_cond); break; case VHOST_USER_SET_VRING_BASE: assert(msg.payload.state.index < s->queues * 2); s->rings |= 0x1ULL << msg.payload.state.index; + g_cond_broadcast(&s->data_cond); break; case VHOST_USER_GET_QUEUE_NUM: @@ -442,13 +422,24 @@ static void chr_read(void *opaque, const uint8_t *buf, int size) g_mutex_unlock(&s->data_mutex); } -static const char *init_hugepagefs(const char *path) +static const char *init_hugepagefs(void) { +#ifdef CONFIG_LINUX + static const char *hugepagefs; + const char *path = getenv("QTEST_HUGETLBFS_PATH"); struct statfs fs; int ret; + if (hugepagefs) { + return hugepagefs; + } + if (!path) { + return NULL; + } + if (access(path, R_OK | W_OK | X_OK)) { - g_test_message("access on path (%s): %s\n", path, strerror(errno)); + g_test_message("access on path (%s): %s", path, strerror(errno)); + g_test_fail(); return NULL; } @@ -457,22 +448,43 @@ static const char *init_hugepagefs(const char *path) } while (ret != 0 && errno == EINTR); if (ret != 0) { - g_test_message("statfs on path (%s): %s\n", path, strerror(errno)); + g_test_message("statfs on path (%s): %s", path, strerror(errno)); + g_test_fail(); return NULL; } if (fs.f_type != HUGETLBFS_MAGIC) { - g_test_message("Warning: path not on HugeTLBFS: %s\n", path); + g_test_message("Warning: path not on HugeTLBFS: %s", path); + g_test_fail(); return NULL; } - return path; + hugepagefs = path; + return hugepagefs; +#else + return NULL; +#endif } static TestServer *test_server_new(const gchar *name) { TestServer *server = g_new0(TestServer, 1); + char template[] = "/tmp/vhost-test-XXXXXX"; + const char *tmpfs; + server->context = g_main_context_new(); + server->loop = g_main_loop_new(server->context, FALSE); + + /* run the main loop thread so the chardev may operate */ + server->thread = g_thread_new(NULL, thread_function, server->loop); + + tmpfs = mkdtemp(template); + if (!tmpfs) { + g_test_message("mkdtemp on path (%s): %s", template, strerror(errno)); + } + g_assert(tmpfs); + + server->tmpfs = g_strdup(tmpfs); server->socket_path = g_strdup_printf("%s/%s.sock", tmpfs, name); server->mig_path = g_strdup_printf("%s/%s.mig", tmpfs, name); server->chr_name = g_strdup_printf("chr-%s", name); @@ -502,13 +514,13 @@ static void test_server_create_chr(TestServer *server, const gchar *opt) Chardev *chr; chr_path = g_strdup_printf("unix:%s%s", server->socket_path, opt); - chr = qemu_chr_new(server->chr_name, chr_path); + chr = qemu_chr_new(server->chr_name, chr_path, server->context); g_free(chr_path); g_assert_nonnull(chr); qemu_chr_fe_init(&server->chr, chr, &error_abort); qemu_chr_fe_set_handlers(&server->chr, chr_can_read, chr_read, - chr_event, NULL, server, NULL, true); + chr_event, NULL, server, server->context, true); } static void test_server_listen(TestServer *server) @@ -516,9 +528,29 @@ static void test_server_listen(TestServer *server) test_server_create_chr(server, ",server,nowait"); } -static gboolean _test_server_free(TestServer *server) +static void test_server_free(TestServer *server) { - int i; + int i, ret; + + /* finish the helper thread and dispatch pending sources */ + g_main_loop_quit(server->loop); + g_thread_join(server->thread); + while (g_main_context_pending(NULL)) { + g_main_context_iteration(NULL, TRUE); + } + + unlink(server->socket_path); + g_free(server->socket_path); + + unlink(server->mig_path); + g_free(server->mig_path); + + ret = rmdir(server->tmpfs); + if (ret != 0) { + g_test_message("unable to rmdir: path (%s): %s", + server->tmpfs, strerror(errno)); + } + g_free(server->tmpfs); qemu_chr_fe_deinit(&server->chr, true); @@ -530,24 +562,13 @@ static gboolean _test_server_free(TestServer *server) 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_assert(server->bus); - qpci_free_pc(server->bus); + g_main_loop_unref(server->loop); + g_main_context_unref(server->context); + g_cond_clear(&server->data_cond); + g_mutex_clear(&server->data_mutex); 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) @@ -573,8 +594,6 @@ static void write_guest_mem(TestServer *s, uint32_t seed) int i, j; size_t size; - wait_for_fds(s); - /* iterate all regions */ for (i = 0; i < s->fds_num; i++) { @@ -639,68 +658,85 @@ GSourceFuncs test_migrate_source_funcs = { .check = test_migrate_source_check, }; -static void test_read_guest_mem(const void *arg) +static void vhost_user_test_cleanup(void *s) { - enum test_memfd memfd = GPOINTER_TO_INT(arg); - TestServer *server = NULL; - char *qemu_cmd = NULL; - QTestState *s = NULL; + TestServer *server = s; - server = test_server_new(memfd == TEST_MEMFD_YES ? - "read-guest-memfd" : "read-guest-mem"); - test_server_listen(server); - - qemu_cmd = get_qemu_cmd(server, 512, memfd, root, "", ""); - - s = qtest_start(qemu_cmd); - g_free(qemu_cmd); - - init_virtio_dev(server, 1u << VIRTIO_NET_F_MAC); - - read_guest_mem_server(server); - - uninit_virtio_dev(server); - - qtest_quit(s); + qos_invalidate_command_line(); test_server_free(server); } -static void test_migrate(void) +static void *vhost_user_test_setup(GString *cmd_line, void *arg) { - TestServer *s = test_server_new("src"); + TestServer *server = test_server_new("vhost-user-test"); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + +static void *vhost_user_test_setup_memfd(GString *cmd_line, void *arg) +{ + TestServer *server = test_server_new("vhost-user-test"); + test_server_listen(server); + + append_mem_opts(server, cmd_line, 256, TEST_MEMFD_YES); + append_vhost_opts(server, cmd_line, ""); + + g_test_queue_destroy(vhost_user_test_cleanup, server); + + return server; +} + +static void test_read_guest_mem(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *server = arg; + + if (!wait_for_fds(server)) { + return; + } + + read_guest_mem_server(global_qtest, server); +} + +static void test_migrate(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; TestServer *dest = test_server_new("dest"); + GString *dest_cmdline = g_string_new(qos_get_current_command_line()); char *uri = g_strdup_printf("%s%s", "unix:", dest->mig_path); - QTestState *global = global_qtest, *from, *to; + QTestState *to; GSource *source; - gchar *cmd, *tmp; QDict *rsp; guint8 *log; guint64 size; - test_server_listen(s); - test_server_listen(dest); + if (!wait_for_fds(s)) { + return; + } - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, "", ""); - from = qtest_start(cmd); - g_free(cmd); - - init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC); - init_virtio_dev(dest, 1u << VIRTIO_NET_F_MAC); - wait_for_fds(s); size = get_log_size(s); - g_assert_cmpint(size, ==, (2 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); + g_assert_cmpint(size, ==, (256 * 1024 * 1024) / (VHOST_LOG_PAGE * 8)); - tmp = g_strdup_printf(" -incoming %s", uri); - cmd = get_qemu_cmd(dest, 2, TEST_MEMFD_AUTO, root, "", tmp); - g_free(tmp); - to = qtest_init(cmd); - g_free(cmd); + test_server_listen(dest); + g_string_append_printf(dest_cmdline, " -incoming %s", uri); + append_mem_opts(dest, dest_cmdline, 256, TEST_MEMFD_AUTO); + append_vhost_opts(dest, dest_cmdline, ""); + to = qtest_init(dest_cmdline->str); + + /* This would be where you call qos_allocate_objects(to, NULL), if you want + * to talk to the QVirtioNet object on the destination. + */ source = g_source_new(&test_migrate_source_funcs, sizeof(TestMigrateSource)); ((TestMigrateSource *)source)->src = s; ((TestMigrateSource *)source)->dest = dest; - g_source_attach(source, NULL); + g_source_attach(source, s->context); /* slow down migration to have time to fiddle with log */ /* TODO: qtest could learn to break on some places */ @@ -709,11 +745,7 @@ static void test_migrate(void) g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); - cmd = g_strdup_printf("{ 'execute': 'migrate'," - "'arguments': { 'uri': '%s' } }", - uri); - rsp = qmp(cmd); - g_free(cmd); + rsp = qmp("{ 'execute': 'migrate', 'arguments': { 'uri': %s } }", uri); g_assert(qdict_haskey(rsp, "return")); qobject_unref(rsp); @@ -734,25 +766,17 @@ static void test_migrate(void) qobject_unref(rsp); qmp_eventwait("STOP"); + qtest_qmp_eventwait(to, "RESUME"); - global_qtest = to; - qmp_eventwait("RESUME"); - - read_guest_mem_server(dest); - - uninit_virtio_dev(s); - uninit_virtio_dev(dest); + g_assert(wait_for_fds(dest)); + read_guest_mem_server(to, 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; } static void wait_for_rings_started(TestServer *s, size_t count) @@ -772,7 +796,6 @@ static void wait_for_rings_started(TestServer *s, size_t count) g_mutex_unlock(&s->data_mutex); } -#if defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) static inline void test_server_connect(TestServer *server) { test_server_create_chr(server, ",reconnect=1"); @@ -800,218 +823,144 @@ connect_thread(gpointer data) return NULL; } -static void test_reconnect_subprocess(void) +static void *vhost_user_test_setup_reconnect(GString *cmd_line, void *arg) { TestServer *s = test_server_new("reconnect"); - char *cmd; g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", ""); - qtest_start(cmd); - g_free(cmd); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); + + g_test_queue_destroy(vhost_user_test_cleanup, s); + + return s; +} + +static void test_reconnect(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; + GSource *src; + + if (!wait_for_fds(s)) { + return; + } - init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC); - wait_for_fds(s); wait_for_rings_started(s, 2); /* reconnect */ s->fds_num = 0; s->rings = 0; - g_idle_add(reconnect_cb, s); - wait_for_fds(s); + src = g_idle_source_new(); + g_source_set_callback(src, reconnect_cb, s, NULL); + g_source_attach(src, s->context); + g_source_unref(src); + g_assert(wait_for_fds(s)); wait_for_rings_started(s, 2); - - uninit_virtio_dev(s); - - qtest_end(); - test_server_free(s); - return; } -static void test_reconnect(void) -{ - gchar *path = g_strdup_printf("/%s/vhost-user/reconnect/subprocess", - qtest_get_arch()); - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); - g_free(path); -} - -static void test_connect_fail_subprocess(void) +static void *vhost_user_test_setup_connect_fail(GString *cmd_line, void *arg) { TestServer *s = test_server_new("connect-fail"); - char *cmd; s->test_fail = true; + g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", ""); - qtest_start(cmd); - g_free(cmd); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); - init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC); - wait_for_fds(s); - wait_for_rings_started(s, 2); + g_test_queue_destroy(vhost_user_test_cleanup, s); - uninit_virtio_dev(s); - - qtest_end(); - test_server_free(s); + return s; } -static void test_connect_fail(void) -{ - gchar *path = g_strdup_printf("/%s/vhost-user/connect-fail/subprocess", - qtest_get_arch()); - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); - g_free(path); -} - -static void test_flags_mismatch_subprocess(void) +static void *vhost_user_test_setup_flags_mismatch(GString *cmd_line, void *arg) { TestServer *s = test_server_new("flags-mismatch"); - char *cmd; s->test_flags = TEST_FLAGS_DISCONNECT; + g_thread_new("connect", connect_thread, s); - cmd = get_qemu_cmd(s, 2, TEST_MEMFD_AUTO, root, ",server", ""); - qtest_start(cmd); - g_free(cmd); + append_mem_opts(s, cmd_line, 256, TEST_MEMFD_AUTO); + append_vhost_opts(s, cmd_line, ",server"); - init_virtio_dev(s, 1u << VIRTIO_NET_F_MAC); - wait_for_fds(s); - wait_for_rings_started(s, 2); + g_test_queue_destroy(vhost_user_test_cleanup, s); - uninit_virtio_dev(s); - - qtest_end(); - test_server_free(s); + return s; } -static void test_flags_mismatch(void) +static void test_vhost_user_started(void *obj, void *arg, QGuestAllocator *alloc) { - gchar *path = g_strdup_printf("/%s/vhost-user/flags-mismatch/subprocess", - qtest_get_arch()); - g_test_trap_subprocess(path, 0, 0); - g_test_trap_assert_passed(); - g_free(path); -} + TestServer *s = arg; -#endif - -static void test_multiqueue(void) -{ - TestServer *s = test_server_new("mq"); - char *cmd; - uint32_t features_mask = ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX)); - s->queues = 2; - test_server_listen(s); - - if (qemu_memfd_check()) { - cmd = g_strdup_printf( - QEMU_CMD_MEMFD QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " - "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 512, 512, s->chr_name, - s->socket_path, "", s->chr_name, - s->queues, s->queues * 2 + 2); - } else { - cmd = g_strdup_printf( - QEMU_CMD_MEM QEMU_CMD_CHR QEMU_CMD_NETDEV ",queues=%d " - "-device virtio-net-pci,netdev=net0,mq=on,vectors=%d", - 512, 512, root, s->chr_name, - s->socket_path, "", s->chr_name, - s->queues, s->queues * 2 + 2); + if (!wait_for_fds(s)) { + return; } - qtest_start(cmd); - g_free(cmd); + wait_for_rings_started(s, 2); +} - init_virtio_dev(s, features_mask); +static void *vhost_user_test_setup_multiqueue(GString *cmd_line, void *arg) +{ + TestServer *s = vhost_user_test_setup(cmd_line, arg); + + s->queues = 2; + g_string_append_printf(cmd_line, + " -set netdev.hs0.queues=%d" + " -global virtio-net-pci.vectors=%d", + s->queues, s->queues * 2 + 2); + + return s; +} + +static void test_multiqueue(void *obj, void *arg, QGuestAllocator *alloc) +{ + TestServer *s = arg; wait_for_rings_started(s, s->queues * 2); - - uninit_virtio_dev(s); - - qtest_end(); - - test_server_free(s); } -int main(int argc, char **argv) +static void register_vhost_user_test(void) { - const char *hugefs; - int ret; - char template[] = "/tmp/vhost-test-XXXXXX"; - GMainLoop *loop; - GThread *thread; + QOSGraphTestOptions opts = { + .before = vhost_user_test_setup, + .subprocess = true, + }; - g_test_init(&argc, &argv, NULL); - - module_call_init(MODULE_INIT_QOM); qemu_add_opts(&qemu_chardev_opts); - tmpfs = mkdtemp(template); - if (!tmpfs) { - g_test_message("mkdtemp on path (%s): %s\n", template, strerror(errno)); - } - g_assert(tmpfs); + qos_add_test("vhost-user/read-guest-mem/memfile", + "virtio-net", + test_read_guest_mem, &opts); - hugefs = getenv("QTEST_HUGETLBFS_PATH"); - if (hugefs) { - root = init_hugepagefs(hugefs); - g_assert(root); - } else { - root = tmpfs; + if (qemu_memfd_check(MFD_ALLOW_SEALING)) { + opts.before = vhost_user_test_setup_memfd; + qos_add_test("vhost-user/read-guest-mem/memfd", + "virtio-net", + test_read_guest_mem, &opts); } - loop = g_main_loop_new(NULL, FALSE); - /* run the main loop thread so the chardev may operate */ - thread = g_thread_new(NULL, thread_function, loop); + qos_add_test("vhost-user/migrate", + "virtio-net", + test_migrate, &opts); - if (qemu_memfd_check()) { - qtest_add_data_func("/vhost-user/read-guest-mem/memfd", - GINT_TO_POINTER(TEST_MEMFD_YES), - test_read_guest_mem); - } - qtest_add_data_func("/vhost-user/read-guest-mem/memfile", - GINT_TO_POINTER(TEST_MEMFD_NO), test_read_guest_mem); - qtest_add_func("/vhost-user/migrate", test_migrate); - qtest_add_func("/vhost-user/multiqueue", test_multiqueue); - -#if defined(CONFIG_HAS_GLIB_SUBPROCESS_TESTS) /* keeps failing on build-system since Aug 15 2017 */ if (getenv("QTEST_VHOST_USER_FIXME")) { - qtest_add_func("/vhost-user/reconnect/subprocess", - test_reconnect_subprocess); - qtest_add_func("/vhost-user/reconnect", test_reconnect); - qtest_add_func("/vhost-user/connect-fail/subprocess", - test_connect_fail_subprocess); - qtest_add_func("/vhost-user/connect-fail", test_connect_fail); - qtest_add_func("/vhost-user/flags-mismatch/subprocess", - test_flags_mismatch_subprocess); - qtest_add_func("/vhost-user/flags-mismatch", test_flags_mismatch); + opts.before = vhost_user_test_setup_reconnect; + qos_add_test("vhost-user/reconnect", "virtio-net", + test_reconnect, &opts); + + opts.before = vhost_user_test_setup_connect_fail; + qos_add_test("vhost-user/connect-fail", "virtio-net", + test_vhost_user_started, &opts); + + opts.before = vhost_user_test_setup_flags_mismatch; + qos_add_test("vhost-user/flags-mismatch", "virtio-net", + test_vhost_user_started, &opts); } -#endif - ret = g_test_run(); - - /* cleanup */ - - /* 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; + opts.before = vhost_user_test_setup_multiqueue; + opts.edge.extra_device_opts = "mq=on"; + qos_add_test("vhost-user/multiqueue", + "virtio-net", + test_multiqueue, &opts); } +libqos_init(register_vhost_user_test); diff --git a/tests/virtio-9p-test.c b/tests/virtio-9p-test.c index a2b31085f6..16107ad280 100644 --- a/tests/virtio-9p-test.c +++ b/tests/virtio-9p-test.c @@ -9,101 +9,36 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qemu-common.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_pci.h" #include "hw/9pfs/9p.h" #include "hw/9pfs/9p-synth.h" +#include "libqos/virtio-9p.h" +#include "libqos/qgraph.h" #define QVIRTIO_9P_TIMEOUT_US (10 * 1000 * 1000) +static QGuestAllocator *alloc; -static const char mount_tag[] = "qtest"; - -typedef struct { - QVirtioDevice *dev; - QOSState *qs; - QVirtQueue *vq; -} QVirtIO9P; - -static QVirtIO9P *qvirtio_9p_start(const char *driver) +static void pci_config(void *obj, void *data, QGuestAllocator *t_alloc) { - const char *arch = qtest_get_arch(); - const char *cmd = "-fsdev synth,id=fsdev0 " - "-device %s,fsdev=fsdev0,mount_tag=%s"; - QVirtIO9P *v9p = g_new0(QVirtIO9P, 1); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - v9p->qs = qtest_pc_boot(cmd, driver, mount_tag); - } else if (strcmp(arch, "ppc64") == 0) { - v9p->qs = qtest_spapr_boot(cmd, driver, mount_tag); - } else { - g_printerr("virtio-9p tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = v9p->qs->qts; - - return v9p; -} - -static void qvirtio_9p_stop(QVirtIO9P *v9p) -{ - qtest_shutdown(v9p->qs); - g_free(v9p); -} - -static QVirtIO9P *qvirtio_9p_pci_start(void) -{ - QVirtIO9P *v9p = qvirtio_9p_start("virtio-9p-pci"); - QVirtioPCIDevice *dev = qvirtio_pci_device_find(v9p->qs->pcibus, - VIRTIO_ID_9P); - g_assert_nonnull(dev); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_9P); - v9p->dev = (QVirtioDevice *) dev; - - qvirtio_pci_device_enable(dev); - qvirtio_reset(v9p->dev); - qvirtio_set_acknowledge(v9p->dev); - qvirtio_set_driver(v9p->dev); - - v9p->vq = qvirtqueue_setup(v9p->dev, v9p->qs->alloc, 0); - - qvirtio_set_driver_ok(v9p->dev); - - return v9p; -} - -static void qvirtio_9p_pci_stop(QVirtIO9P *v9p) -{ - qvirtqueue_cleanup(v9p->dev->bus, v9p->vq, v9p->qs->alloc); - qvirtio_pci_device_disable(container_of(v9p->dev, QVirtioPCIDevice, vdev)); - qvirtio_pci_device_free((QVirtioPCIDevice *)v9p->dev); - qvirtio_9p_stop(v9p); -} - -static void pci_config(QVirtIO9P *v9p) -{ - size_t tag_len = qvirtio_config_readw(v9p->dev, 0); + QVirtio9P *v9p = obj; + alloc = t_alloc; + size_t tag_len = qvirtio_config_readw(v9p->vdev, 0); char *tag; int i; - g_assert_cmpint(tag_len, ==, strlen(mount_tag)); + g_assert_cmpint(tag_len, ==, strlen(MOUNT_TAG)); tag = g_malloc(tag_len); for (i = 0; i < tag_len; i++) { - tag[i] = qvirtio_config_readb(v9p->dev, i + 2); + tag[i] = qvirtio_config_readb(v9p->vdev, i + 2); } - g_assert_cmpmem(tag, tag_len, mount_tag, tag_len); + g_assert_cmpmem(tag, tag_len, MOUNT_TAG, tag_len); g_free(tag); } #define P9_MAX_SIZE 4096 /* Max size of a T-message or R-message */ typedef struct { - QVirtIO9P *v9p; + QVirtio9P *v9p; uint16_t tag; uint64_t t_msg; uint32_t t_size; @@ -206,7 +141,7 @@ static void v9fs_string_read(P9Req *req, uint16_t *len, char **string) uint16_t tag; } QEMU_PACKED P9Hdr; -static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, +static P9Req *v9fs_req_init(QVirtio9P *v9p, uint32_t size, uint8_t id, uint16_t tag) { P9Req *req = g_new0(P9Req, 1); @@ -224,7 +159,7 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, req->v9p = v9p; req->t_size = total_size; - req->t_msg = guest_alloc(v9p->qs->alloc, req->t_size); + req->t_msg = guest_alloc(alloc, req->t_size); v9fs_memwrite(req, &hdr, 7); req->tag = tag; return req; @@ -232,13 +167,13 @@ static P9Req *v9fs_req_init(QVirtIO9P *v9p, uint32_t size, uint8_t id, static void v9fs_req_send(P9Req *req) { - QVirtIO9P *v9p = req->v9p; + QVirtio9P *v9p = req->v9p; - req->r_msg = guest_alloc(v9p->qs->alloc, P9_MAX_SIZE); + req->r_msg = guest_alloc(alloc, P9_MAX_SIZE); req->free_head = qvirtqueue_add(v9p->vq, req->t_msg, req->t_size, false, true); qvirtqueue_add(v9p->vq, req->r_msg, P9_MAX_SIZE, true, false); - qvirtqueue_kick(v9p->dev, v9p->vq, req->free_head); + qvirtqueue_kick(v9p->vdev, v9p->vq, req->free_head); req->t_off = 0; } @@ -257,9 +192,9 @@ static const char *rmessage_name(uint8_t id) static void v9fs_req_wait_for_reply(P9Req *req, uint32_t *len) { - QVirtIO9P *v9p = req->v9p; + QVirtio9P *v9p = req->v9p; - qvirtio_wait_used_elem(v9p->dev, v9p->vq, req->free_head, len, + qvirtio_wait_used_elem(v9p->vdev, v9p->vq, req->free_head, len, QVIRTIO_9P_TIMEOUT_US); } @@ -290,10 +225,8 @@ static void v9fs_req_recv(P9Req *req, uint8_t id) static void v9fs_req_free(P9Req *req) { - QVirtIO9P *v9p = req->v9p; - - guest_free(v9p->qs->alloc, req->t_msg); - guest_free(v9p->qs->alloc, req->r_msg); + guest_free(alloc, req->t_msg); + guest_free(alloc, req->r_msg); g_free(req); } @@ -306,7 +239,7 @@ static void v9fs_rlerror(P9Req *req, uint32_t *err) } /* size[4] Tversion tag[2] msize[4] version[s] */ -static P9Req *v9fs_tversion(QVirtIO9P *v9p, uint32_t msize, const char *version, +static P9Req *v9fs_tversion(QVirtio9P *v9p, uint32_t msize, const char *version, uint16_t tag) { P9Req *req; @@ -341,7 +274,7 @@ static void v9fs_rversion(P9Req *req, uint16_t *len, char **version) } /* size[4] Tattach tag[2] fid[4] afid[4] uname[s] aname[s] n_uname[4] */ -static P9Req *v9fs_tattach(QVirtIO9P *v9p, uint32_t fid, uint32_t n_uname, +static P9Req *v9fs_tattach(QVirtio9P *v9p, uint32_t fid, uint32_t n_uname, uint16_t tag) { const char *uname = ""; /* ignored by QEMU */ @@ -370,7 +303,7 @@ static void v9fs_rattach(P9Req *req, v9fs_qid *qid) } /* size[4] Twalk tag[2] fid[4] newfid[4] nwname[2] nwname*(wname[s]) */ -static P9Req *v9fs_twalk(QVirtIO9P *v9p, uint32_t fid, uint32_t newfid, +static P9Req *v9fs_twalk(QVirtio9P *v9p, uint32_t fid, uint32_t newfid, uint16_t nwname, char *const wnames[], uint16_t tag) { P9Req *req; @@ -412,7 +345,7 @@ static void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid) } /* size[4] Tlopen tag[2] fid[4] flags[4] */ -static P9Req *v9fs_tlopen(QVirtIO9P *v9p, uint32_t fid, uint32_t flags, +static P9Req *v9fs_tlopen(QVirtio9P *v9p, uint32_t fid, uint32_t flags, uint16_t tag) { P9Req *req; @@ -440,7 +373,7 @@ static void v9fs_rlopen(P9Req *req, v9fs_qid *qid, uint32_t *iounit) } /* size[4] Twrite tag[2] fid[4] offset[8] count[4] data[count] */ -static P9Req *v9fs_twrite(QVirtIO9P *v9p, uint32_t fid, uint64_t offset, +static P9Req *v9fs_twrite(QVirtio9P *v9p, uint32_t fid, uint64_t offset, uint32_t count, const void *data, uint16_t tag) { P9Req *req; @@ -468,7 +401,7 @@ static void v9fs_rwrite(P9Req *req, uint32_t *count) } /* size[4] Tflush tag[2] oldtag[2] */ -static P9Req *v9fs_tflush(QVirtIO9P *v9p, uint16_t oldtag, uint16_t tag) +static P9Req *v9fs_tflush(QVirtio9P *v9p, uint16_t oldtag, uint16_t tag) { P9Req *req; @@ -485,8 +418,10 @@ static void v9fs_rflush(P9Req *req) v9fs_req_free(req); } -static void fs_version(QVirtIO9P *v9p) +static void fs_version(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; const char *version = "9P2000.L"; uint16_t server_len; char *server_version; @@ -501,18 +436,22 @@ static void fs_version(QVirtIO9P *v9p) g_free(server_version); } -static void fs_attach(QVirtIO9P *v9p) +static void fs_attach(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; P9Req *req; - fs_version(v9p); + fs_version(v9p, NULL, t_alloc); req = v9fs_tattach(v9p, 0, getuid(), 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rattach(req, NULL); } -static void fs_walk(QVirtIO9P *v9p) +static void fs_walk(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *wnames[P9_MAXWELEM]; uint16_t nwqid; v9fs_qid *wqid; @@ -523,7 +462,7 @@ static void fs_walk(QVirtIO9P *v9p) wnames[i] = g_strdup_printf(QTEST_V9FS_SYNTH_WALK_FILE, i); } - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, P9_MAXWELEM, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, &nwqid, &wqid); @@ -537,13 +476,15 @@ static void fs_walk(QVirtIO9P *v9p) g_free(wqid); } -static void fs_walk_no_slash(QVirtIO9P *v9p) +static void fs_walk_no_slash(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(" /") }; P9Req *req; uint32_t err; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rlerror(req, &err); @@ -553,13 +494,15 @@ static void fs_walk_no_slash(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_walk_dotdot(QVirtIO9P *v9p) +static void fs_walk_dotdot(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup("..") }; v9fs_qid root_qid, *wqid; P9Req *req; - fs_version(v9p); + fs_version(v9p, NULL, t_alloc); req = v9fs_tattach(v9p, 0, getuid(), 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rattach(req, &root_qid); @@ -574,12 +517,14 @@ static void fs_walk_dotdot(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_lopen(QVirtIO9P *v9p) +static void fs_lopen(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_LOPEN_FILE) }; P9Req *req; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -591,15 +536,17 @@ static void fs_lopen(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_write(QVirtIO9P *v9p) +static void fs_write(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; static const uint32_t write_count = P9_MAX_SIZE / 2; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_WRITE_FILE) }; char *buf = g_malloc0(write_count); uint32_t count; P9Req *req; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -617,14 +564,16 @@ static void fs_write(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_flush_success(QVirtIO9P *v9p) +static void fs_flush_success(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; P9Req *req, *flush_req; uint32_t reply_len; uint8_t should_block; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -652,14 +601,16 @@ static void fs_flush_success(QVirtIO9P *v9p) g_free(wnames[0]); } -static void fs_flush_ignored(QVirtIO9P *v9p) +static void fs_flush_ignored(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtio9P *v9p = obj; + alloc = t_alloc; char *const wnames[] = { g_strdup(QTEST_V9FS_SYNTH_FLUSH_FILE) }; P9Req *req, *flush_req; uint32_t count; uint8_t should_block; - fs_attach(v9p); + fs_attach(v9p, NULL, t_alloc); req = v9fs_twalk(v9p, 0, 1, 1, wnames, 0); v9fs_req_wait_for_reply(req, NULL); v9fs_rwalk(req, NULL, NULL); @@ -687,39 +638,22 @@ static void fs_flush_ignored(QVirtIO9P *v9p) g_free(wnames[0]); } -typedef void (*v9fs_test_fn)(QVirtIO9P *v9p); - -static void v9fs_run_pci_test(gconstpointer data) +static void register_virtio_9p_test(void) { - v9fs_test_fn fn = data; - QVirtIO9P *v9p = qvirtio_9p_pci_start(); - - if (fn) { - fn(v9p); - } - qvirtio_9p_pci_stop(v9p); + qos_add_test("config", "virtio-9p", pci_config, NULL); + qos_add_test("fs/version/basic", "virtio-9p", fs_version, NULL); + qos_add_test("fs/attach/basic", "virtio-9p", fs_attach, NULL); + qos_add_test("fs/walk/basic", "virtio-9p", fs_walk, NULL); + qos_add_test("fs/walk/no_slash", "virtio-9p", fs_walk_no_slash, + NULL); + qos_add_test("fs/walk/dotdot_from_root", "virtio-9p", + fs_walk_dotdot, NULL); + qos_add_test("fs/lopen/basic", "virtio-9p", fs_lopen, NULL); + qos_add_test("fs/write/basic", "virtio-9p", fs_write, NULL); + qos_add_test("fs/flush/success", "virtio-9p", fs_flush_success, + NULL); + qos_add_test("fs/flush/ignored", "virtio-9p", fs_flush_ignored, + NULL); } -static void v9fs_qtest_pci_add(const char *path, v9fs_test_fn fn) -{ - qtest_add_data_func(path, fn, v9fs_run_pci_test); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - v9fs_qtest_pci_add("/virtio/9p/pci/nop", NULL); - v9fs_qtest_pci_add("/virtio/9p/pci/config", pci_config); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/version/basic", fs_version); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/attach/basic", fs_attach); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/basic", fs_walk); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/no_slash", fs_walk_no_slash); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/walk/dotdot_from_root", - fs_walk_dotdot); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/lopen/basic", fs_lopen); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/write/basic", fs_write); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/flush/success", fs_flush_success); - v9fs_qtest_pci_add("/virtio/9p/pci/fs/flush/ignored", fs_flush_ignored); - - return g_test_run(); -} +libqos_init(register_virtio_9p_test); diff --git a/tests/virtio-balloon-test.c b/tests/virtio-balloon-test.c deleted file mode 100644 index 0a07e036bb..0000000000 --- a/tests/virtio-balloon-test.c +++ /dev/null @@ -1,33 +0,0 @@ -/* - * QTest testcase for VirtIO Balloon - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * 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 "libqtest.h" -#include "libqos/virtio.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void balloon_nop(void) -{ -} - -int main(int argc, char **argv) -{ - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/balloon/nop", balloon_nop); - - global_qtest = qtest_startf("-device virtio-balloon-%s", - qvirtio_get_dev_type()); - ret = g_test_run(); - - qtest_end(); - - return ret; -} diff --git a/tests/virtio-blk-test.c b/tests/virtio-blk-test.c index 9be9ffb378..b65365934b 100644 --- a/tests/virtio-blk-test.c +++ b/tests/virtio-blk-test.c @@ -10,29 +10,18 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" -#include "libqos/virtio-mmio.h" -#include "libqos/malloc-generic.h" #include "qemu/bswap.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_config.h" -#include "standard-headers/linux/virtio_ring.h" #include "standard-headers/linux/virtio_blk.h" #include "standard-headers/linux/virtio_pci.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-blk.h" + +/* TODO actually test the results and get rid of this */ +#define qmp_discard_response(...) qobject_unref(qmp(__VA_ARGS__)) #define TEST_IMAGE_SIZE (64 * 1024 * 1024) #define QVIRTIO_BLK_TIMEOUT_US (30 * 1000 * 1000) #define PCI_SLOT_HP 0x06 -#define PCI_SLOT 0x04 -#define PCI_FN 0x00 - -#define MMIO_PAGE_SIZE 4096 -#define MMIO_DEV_BASE_ADDR 0x0A003E00 -#define MMIO_RAM_ADDR 0x40000000 -#define MMIO_RAM_SIZE 0x20000000 typedef struct QVirtioBlkReq { uint32_t type; @@ -42,91 +31,38 @@ typedef struct QVirtioBlkReq { uint8_t status; } QVirtioBlkReq; + +#ifdef HOST_WORDS_BIGENDIAN +const bool host_is_big_endian = true; +#else +const bool host_is_big_endian; /* false */ +#endif + +static void drive_destroy(void *path) +{ + unlink(path); + g_free(path); + qos_invalidate_command_line(); +} + static char *drive_create(void) { int fd, ret; - char *tmp_path = g_strdup("/tmp/qtest.XXXXXX"); + char *t_path = g_strdup("/tmp/qtest.XXXXXX"); /* Create a temporary raw image */ - fd = mkstemp(tmp_path); + fd = mkstemp(t_path); g_assert_cmpint(fd, >=, 0); ret = ftruncate(fd, TEST_IMAGE_SIZE); g_assert_cmpint(ret, ==, 0); close(fd); - return tmp_path; -} - -static QOSState *pci_test_start(void) -{ - QOSState *qs; - const char *arch = qtest_get_arch(); - char *tmp_path; - const char *cmd = "-drive if=none,id=drive0,file=%s,format=raw " - "-drive if=none,id=drive1,file=null-co://,format=raw " - "-device virtio-blk-pci,id=drv0,drive=drive0," - "addr=%x.%x"; - - tmp_path = drive_create(); - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); - } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd, tmp_path, PCI_SLOT, PCI_FN); - } else { - g_printerr("virtio-blk tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - unlink(tmp_path); - g_free(tmp_path); - return qs; -} - -static void arm_test_start(void) -{ - char *tmp_path; - - tmp_path = drive_create(); - - global_qtest = qtest_startf("-machine virt " - "-drive if=none,id=drive0,file=%s,format=raw " - "-device virtio-blk-device,drive=drive0", - tmp_path); - unlink(tmp_path); - g_free(tmp_path); -} - -static void test_end(void) -{ - qtest_end(); -} - -static QVirtioPCIDevice *virtio_blk_pci_init(QPCIBus *bus, int slot) -{ - QVirtioPCIDevice *dev; - - dev = qvirtio_pci_device_find_slot(bus, VIRTIO_ID_BLOCK, slot); - g_assert(dev != NULL); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - g_assert_cmphex(dev->pdev->devfn, ==, ((slot << 3) | PCI_FN)); - - qvirtio_pci_device_enable(dev); - qvirtio_reset(&dev->vdev); - qvirtio_set_acknowledge(&dev->vdev); - qvirtio_set_driver(&dev->vdev); - - return dev; + g_test_queue_destroy(drive_destroy, t_path); + return t_path; } static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) { -#ifdef HOST_WORDS_BIGENDIAN - const bool host_is_big_endian = true; -#else - const bool host_is_big_endian = false; -#endif - if (qvirtio_is_big_endian(d) != host_is_big_endian) { req->type = bswap32(req->type); req->ioprio = bswap32(req->ioprio); @@ -134,13 +70,37 @@ static inline void virtio_blk_fix_request(QVirtioDevice *d, QVirtioBlkReq *req) } } + +static inline void virtio_blk_fix_dwz_hdr(QVirtioDevice *d, + struct virtio_blk_discard_write_zeroes *dwz_hdr) +{ + if (qvirtio_is_big_endian(d) != host_is_big_endian) { + dwz_hdr->sector = bswap64(dwz_hdr->sector); + dwz_hdr->num_sectors = bswap32(dwz_hdr->num_sectors); + dwz_hdr->flags = bswap32(dwz_hdr->flags); + } +} + static uint64_t virtio_blk_request(QGuestAllocator *alloc, QVirtioDevice *d, QVirtioBlkReq *req, uint64_t data_size) { uint64_t addr; uint8_t status = 0xFF; - g_assert_cmpuint(data_size % 512, ==, 0); + switch (req->type) { + case VIRTIO_BLK_T_IN: + case VIRTIO_BLK_T_OUT: + g_assert_cmpuint(data_size % 512, ==, 0); + break; + case VIRTIO_BLK_T_DISCARD: + case VIRTIO_BLK_T_WRITE_ZEROES: + g_assert_cmpuint(data_size % + sizeof(struct virtio_blk_discard_write_zeroes), ==, 0); + break; + default: + g_assert_cmpuint(data_size, ==, 0); + } + addr = guest_alloc(alloc, sizeof(*req) + data_size); virtio_blk_fix_request(d, req); @@ -227,6 +187,95 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, guest_free(alloc, req_addr); + if (features & (1u << VIRTIO_BLK_F_WRITE_ZEROES)) { + struct virtio_blk_discard_write_zeroes dwz_hdr; + void *expected; + + /* + * WRITE_ZEROES request on the same sector of previous test where + * we wrote "TEST". + */ + req.type = VIRTIO_BLK_T_WRITE_ZEROES; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_used_elem(dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + + /* Read request to check if the sector contains all zeroes */ + req.type = VIRTIO_BLK_T_IN; + req.ioprio = 1; + req.sector = 0; + req.data = g_malloc0(512); + + req_addr = virtio_blk_request(alloc, dev, &req, 512); + + g_free(req.data); + + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_used_elem(dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 528); + g_assert_cmpint(status, ==, 0); + + data = g_malloc(512); + expected = g_malloc0(512); + memread(req_addr + 16, data, 512); + g_assert_cmpmem(data, 512, expected, 512); + g_free(expected); + g_free(data); + + guest_free(alloc, req_addr); + } + + if (features & (1u << VIRTIO_BLK_F_DISCARD)) { + struct virtio_blk_discard_write_zeroes dwz_hdr; + + req.type = VIRTIO_BLK_T_DISCARD; + req.data = (char *) &dwz_hdr; + dwz_hdr.sector = 0; + dwz_hdr.num_sectors = 1; + dwz_hdr.flags = 0; + + virtio_blk_fix_dwz_hdr(dev, &dwz_hdr); + + req_addr = virtio_blk_request(alloc, dev, &req, sizeof(dwz_hdr)); + + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, sizeof(dwz_hdr), false, true); + qvirtqueue_add(vq, req_addr + 16 + sizeof(dwz_hdr), 1, true, false); + + qvirtqueue_kick(dev, vq, free_head); + + qvirtio_wait_used_elem(dev, vq, free_head, NULL, + QVIRTIO_BLK_TIMEOUT_US); + status = readb(req_addr + 16 + sizeof(dwz_hdr)); + g_assert_cmpint(status, ==, 0); + + guest_free(alloc, req_addr); + } + if (features & (1u << VIRTIO_F_ANY_LAYOUT)) { /* Write and read with 2 descriptor layout */ /* Write request */ @@ -280,31 +329,21 @@ static void test_basic(QVirtioDevice *dev, QGuestAllocator *alloc, } } -static void pci_basic(void) +static void basic(void *obj, void *data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *vqpci; + QVirtioBlk *blk_if = obj; + QVirtQueue *vq; + vq = qvirtqueue_setup(blk_if->vdev, t_alloc, 0); + test_basic(blk_if->vdev, t_alloc, vq); + qvirtqueue_cleanup(blk_if->vdev->bus, vq, t_alloc); - qs = pci_test_start(); - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - - test_basic(&dev->vdev, qs->alloc, &vqpci->vq); - - /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - qtest_shutdown(qs); } -static void pci_indirect(void) +static void indirect(void *obj, void *u_data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QVirtQueuePCI *vqpci; - QOSState *qs; + QVirtQueue *vq; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; QVirtioBlkReq req; QVRingIndirectDesc *indirect; uint64_t req_addr; @@ -314,22 +353,18 @@ static void pci_indirect(void) uint8_t status; char *data; - qs = pci_test_start(); - - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&dev->vdev); + features = qvirtio_get_features(dev); g_assert_cmphex(features & (1u << VIRTIO_RING_F_INDIRECT_DESC), !=, 0); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&dev->vdev, features); + qvirtio_set_features(dev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - qvirtio_set_driver_ok(&dev->vdev); + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtio_set_driver_ok(dev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -338,23 +373,23 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); + indirect = qvring_indirect_desc_setup(dev, t_alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 528, false); qvring_indirect_desc_add(indirect, req_addr + 528, 1, true); - free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add_indirect(vq, indirect); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); g_free(indirect); - guest_free(qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -363,17 +398,17 @@ static void pci_indirect(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - indirect = qvring_indirect_desc_setup(&dev->vdev, qs->alloc, 2); + indirect = qvring_indirect_desc_setup(dev, t_alloc, 2); qvring_indirect_desc_add(indirect, req_addr, 16, false); qvring_indirect_desc_add(indirect, req_addr + 16, 513, true); - free_head = qvirtqueue_add_indirect(&vqpci->vq, indirect); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add_indirect(vq, indirect); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); @@ -384,50 +419,37 @@ static void pci_indirect(void) g_free(data); g_free(indirect); - guest_free(qs->alloc, req_addr); - - /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - qtest_shutdown(qs); + guest_free(t_alloc, req_addr); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); } -static void pci_config(void) +static void config(void *obj, void *data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; int n_size = TEST_IMAGE_SIZE / 2; uint64_t capacity; - qs = pci_test_start(); - - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - qvirtio_set_driver_ok(&dev->vdev); + qvirtio_set_driver_ok(dev); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, n_size / 512); - - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - - qtest_shutdown(qs); } -static void pci_msix(void) +static void msix(void *obj, void *u_data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *vqpci; + QVirtQueue *vq; + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; QVirtioBlkReq req; int n_size = TEST_IMAGE_SIZE / 2; uint64_t req_addr; @@ -436,36 +458,38 @@ static void pci_msix(void) uint32_t free_head; uint8_t status; char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); - qs = pci_test_start(); + if (qpci_check_buggy_msi(pci_dev)) { + return; + } - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - qpci_msix_enable(dev->pdev); + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&dev->vdev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_RING_F_EVENT_IDX) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&dev->vdev, features); + qvirtio_set_features(dev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); - qvirtio_set_driver_ok(&dev->vdev); + qvirtio_set_driver_ok(dev); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_config_isr(&dev->vdev, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_config_isr(dev, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, n_size / 512); /* Write request */ @@ -475,22 +499,22 @@ static void pci_msix(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); g_assert_cmpint(status, ==, 0); - guest_free(qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -498,18 +522,18 @@ static void pci_msix(void) req.sector = 0; req.data = g_malloc0(512); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); status = readb(req_addr + 528); @@ -520,21 +544,19 @@ static void pci_msix(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); - qpci_msix_disable(dev->pdev); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - qtest_shutdown(qs); + qpci_msix_disable(pdev->pdev); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); } -static void pci_idx(void) +static void idx(void *obj, void *u_data, QGuestAllocator *t_alloc) { - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *vqpci; + QVirtQueue *vq; + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; + QVirtioDevice *dev = &pdev->vdev; QVirtioBlkReq req; uint64_t req_addr; uint64_t capacity; @@ -544,28 +566,30 @@ static void pci_idx(void) uint32_t desc_idx; uint8_t status; char *data; + QOSGraphObject *blk_object = obj; + QPCIDevice *pci_dev = blk_object->get_driver(blk_object, "pci-device"); - qs = pci_test_start(); + if (qpci_check_buggy_msi(pci_dev)) { + return; + } - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT); - qpci_msix_enable(dev->pdev); + qpci_msix_enable(pdev->pdev); + qvirtio_pci_set_msix_configuration_vector(pdev, t_alloc, 0); - qvirtio_pci_set_msix_configuration_vector(dev, qs->alloc, 0); - - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, TEST_IMAGE_SIZE / 512); - features = qvirtio_get_features(&dev->vdev); + features = qvirtio_get_features(dev); features = features & ~(QVIRTIO_F_BAD_FEATURE | (1u << VIRTIO_RING_F_INDIRECT_DESC) | (1u << VIRTIO_F_NOTIFY_ON_EMPTY) | (1u << VIRTIO_BLK_F_SCSI)); - qvirtio_set_features(&dev->vdev, features); + qvirtio_set_features(dev, features); - vqpci = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - qvirtqueue_pci_msix_setup(dev, vqpci, qs->alloc, 1); + vq = qvirtqueue_setup(dev, t_alloc, 0); + qvirtqueue_pci_msix_setup(pdev, (QVirtQueuePCI *)vq, t_alloc, 1); - qvirtio_set_driver_ok(&dev->vdev); + qvirtio_set_driver_ok(dev); /* Write request */ req.type = VIRTIO_BLK_T_OUT; @@ -574,16 +598,16 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(dev, vq, free_head); - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, free_head, NULL, + qvirtio_wait_used_elem(dev, vq, free_head, NULL, QVIRTIO_BLK_TIMEOUT_US); /* Write request */ @@ -593,25 +617,25 @@ static void pci_idx(void) req.data = g_malloc0(512); strcpy(req.data, "TEST"); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); /* Notify after processing the third request */ - qvirtqueue_set_used_event(&vqpci->vq, 2); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + qvirtqueue_set_used_event(vq, 2); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, false, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); + qvirtqueue_kick(dev, vq, free_head); write_head = free_head; /* No notification expected */ - status = qvirtio_wait_status_byte_no_isr(&dev->vdev, - &vqpci->vq, req_addr + 528, + status = qvirtio_wait_status_byte_no_isr(dev, + vq, req_addr + 528, QVIRTIO_BLK_TIMEOUT_US); g_assert_cmpint(status, ==, 0); - guest_free(qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* Read request */ req.type = VIRTIO_BLK_T_IN; @@ -619,20 +643,20 @@ static void pci_idx(void) req.sector = 1; req.data = g_malloc0(512); - req_addr = virtio_blk_request(qs->alloc, &dev->vdev, &req, 512); + req_addr = virtio_blk_request(t_alloc, dev, &req, 512); g_free(req.data); - free_head = qvirtqueue_add(&vqpci->vq, req_addr, 16, false, true); - qvirtqueue_add(&vqpci->vq, req_addr + 16, 512, true, true); - qvirtqueue_add(&vqpci->vq, req_addr + 528, 1, true, false); + free_head = qvirtqueue_add(vq, req_addr, 16, false, true); + qvirtqueue_add(vq, req_addr + 16, 512, true, true); + qvirtqueue_add(vq, req_addr + 528, 1, true, false); - qvirtqueue_kick(&dev->vdev, &vqpci->vq, free_head); + qvirtqueue_kick(dev, vq, free_head); /* We get just one notification for both requests */ - qvirtio_wait_used_elem(&dev->vdev, &vqpci->vq, write_head, NULL, + qvirtio_wait_used_elem(dev, vq, write_head, NULL, QVIRTIO_BLK_TIMEOUT_US); - g_assert(qvirtqueue_get_buf(&vqpci->vq, &desc_idx, NULL)); + g_assert(qvirtqueue_get_buf(vq, &desc_idx, NULL)); g_assert_cmpint(desc_idx, ==, free_head); status = readb(req_addr + 528); @@ -643,123 +667,114 @@ static void pci_idx(void) g_assert_cmpstr(data, ==, "TEST"); g_free(data); - guest_free(qs->alloc, req_addr); + guest_free(t_alloc, req_addr); /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, &vqpci->vq, qs->alloc); - qpci_msix_disable(dev->pdev); - qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); - qtest_shutdown(qs); + qpci_msix_disable(pdev->pdev); + + qvirtqueue_cleanup(dev->bus, vq, t_alloc); } -static void pci_hotplug(void) +static void pci_hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { + QVirtioPCIDevice *dev1 = obj; QVirtioPCIDevice *dev; - QOSState *qs; - const char *arch = qtest_get_arch(); - - qs = pci_test_start(); /* plug secondary disk */ - qpci_plug_device_test("virtio-blk-pci", "drv1", PCI_SLOT_HP, - "'drive': 'drive1'"); + qtest_qmp_device_add("virtio-blk-pci", "drv1", + "{'addr': %s, 'drive': 'drive1'}", + stringify(PCI_SLOT_HP) ".0"); - dev = virtio_blk_pci_init(qs->pcibus, PCI_SLOT_HP); - g_assert(dev); + dev = virtio_pci_new(dev1->pdev->bus, + &(QPCIAddress) { .devfn = QPCI_DEVFN(PCI_SLOT_HP, 0) }); + g_assert_nonnull(dev); + g_assert_cmpint(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); qvirtio_pci_device_disable(dev); - qvirtio_pci_device_free(dev); + qos_object_destroy((QOSGraphObject *)dev); /* unplug secondary disk */ - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); - } - qtest_shutdown(qs); + qpci_unplug_acpi_device_test("drv1", PCI_SLOT_HP); } /* * Check that setting the vring addr on a non-existent virtqueue does * not crash. */ -static void test_nonexistent_virtqueue(void) +static void test_nonexistent_virtqueue(void *obj, void *data, + QGuestAllocator *t_alloc) { + QVirtioBlkPCI *blk = obj; + QVirtioPCIDevice *pdev = &blk->pci_vdev; QPCIBar bar0; - QOSState *qs; QPCIDevice *dev; - qs = pci_test_start(); - dev = qpci_device_find(qs->pcibus, QPCI_DEVFN(4, 0)); + dev = qpci_device_find(pdev->pdev->bus, QPCI_DEVFN(4, 0)); g_assert(dev != NULL); - qpci_device_enable(dev); + bar0 = qpci_iomap(dev, 0, NULL); qpci_io_writeb(dev, bar0, VIRTIO_PCI_QUEUE_SEL, 2); qpci_io_writel(dev, bar0, VIRTIO_PCI_QUEUE_PFN, 1); + g_free(dev); - qtest_shutdown(qs); } -static void mmio_basic(void) +static void resize(void *obj, void *data, QGuestAllocator *t_alloc) { - QVirtioMMIODevice *dev; - QVirtQueue *vq; - QGuestAllocator *alloc; + QVirtioBlk *blk_if = obj; + QVirtioDevice *dev = blk_if->vdev; int n_size = TEST_IMAGE_SIZE / 2; uint64_t capacity; + QVirtQueue *vq; - arm_test_start(); + vq = qvirtqueue_setup(dev, t_alloc, 0); - dev = qvirtio_mmio_init_device(MMIO_DEV_BASE_ADDR, MMIO_PAGE_SIZE); - g_assert(dev != NULL); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_BLOCK); - - qvirtio_reset(&dev->vdev); - qvirtio_set_acknowledge(&dev->vdev); - qvirtio_set_driver(&dev->vdev); - - alloc = generic_alloc_init(MMIO_RAM_ADDR, MMIO_RAM_SIZE, MMIO_PAGE_SIZE); - vq = qvirtqueue_setup(&dev->vdev, alloc, 0); - - test_basic(&dev->vdev, alloc, vq); + test_basic(dev, t_alloc, vq); qmp_discard_response("{ 'execute': 'block_resize', " " 'arguments': { 'device': 'drive0', " " 'size': %d } }", n_size); - qvirtio_wait_queue_isr(&dev->vdev, vq, QVIRTIO_BLK_TIMEOUT_US); + qvirtio_wait_queue_isr(dev, vq, QVIRTIO_BLK_TIMEOUT_US); - capacity = qvirtio_config_readq(&dev->vdev, 0); + capacity = qvirtio_config_readq(dev, 0); g_assert_cmpint(capacity, ==, n_size / 512); - /* End test */ - qvirtqueue_cleanup(dev->vdev.bus, vq, alloc); - g_free(dev); - generic_alloc_uninit(alloc); - test_end(); + qvirtqueue_cleanup(dev->bus, vq, t_alloc); + } -int main(int argc, char **argv) +static void *virtio_blk_test_setup(GString *cmd_line, void *arg) { - const char *arch = qtest_get_arch(); + char *tmp_path = drive_create(); - g_test_init(&argc, &argv, NULL); + g_string_append_printf(cmd_line, + " -drive if=none,id=drive0,file=%s,format=raw,auto-read-only=off " + "-drive if=none,id=drive1,file=null-co://,format=raw ", + tmp_path); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0 || - strcmp(arch, "ppc64") == 0) { - qtest_add_func("/virtio/blk/pci/basic", pci_basic); - qtest_add_func("/virtio/blk/pci/indirect", pci_indirect); - qtest_add_func("/virtio/blk/pci/config", pci_config); - qtest_add_func("/virtio/blk/pci/nxvirtq", test_nonexistent_virtqueue); - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qtest_add_func("/virtio/blk/pci/msix", pci_msix); - qtest_add_func("/virtio/blk/pci/idx", pci_idx); - } - qtest_add_func("/virtio/blk/pci/hotplug", pci_hotplug); - } else if (strcmp(arch, "arm") == 0) { - qtest_add_func("/virtio/blk/mmio/basic", mmio_basic); - } - - return g_test_run(); + return arg; } + +static void register_virtio_blk_test(void) +{ + QOSGraphTestOptions opts = { + .before = virtio_blk_test_setup, + }; + + qos_add_test("indirect", "virtio-blk", indirect, &opts); + qos_add_test("config", "virtio-blk", config, &opts); + qos_add_test("basic", "virtio-blk", basic, &opts); + qos_add_test("resize", "virtio-blk", resize, &opts); + + /* tests just for virtio-blk-pci */ + qos_add_test("msix", "virtio-blk-pci", msix, &opts); + qos_add_test("idx", "virtio-blk-pci", idx, &opts); + qos_add_test("nxvirtq", "virtio-blk-pci", + test_nonexistent_virtqueue, &opts); + qos_add_test("hotplug", "virtio-blk-pci", pci_hotplug, &opts); +} + +libqos_init(register_virtio_blk_test); diff --git a/tests/virtio-ccw-test.c b/tests/virtio-ccw-test.c new file mode 100644 index 0000000000..48c714d84c --- /dev/null +++ b/tests/virtio-ccw-test.c @@ -0,0 +1,110 @@ +/* + * QTest testcase for VirtIO CCW + * + * Copyright (c) 2014 SUSE LINUX Products GmbH + * Copyright (c) 2018 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. + */ + +/* Until we have a full libqos implementation of virtio-ccw (which requires + * also to add support for I/O channels to qtest), we can only do simple + * tests that initialize the devices. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "libqos/virtio.h" + +static void virtio_balloon_nop(void) +{ + global_qtest = qtest_initf("-device virtio-balloon-ccw"); + qtest_end(); +} + +static void virtconsole_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " + "-device virtconsole,bus=vser0.0"); + qtest_end(); +} + +static void virtserialport_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw,id=vser0 " + "-device virtserialport,bus=vser0.0"); + qtest_end(); +} + +static void virtio_serial_nop(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw"); + qtest_end(); +} + +static void virtio_serial_hotplug(void) +{ + global_qtest = qtest_initf("-device virtio-serial-ccw"); + qtest_qmp_device_add("virtserialport", "hp-port", "{}"); + qtest_qmp_device_del("hp-port"); + qtest_end(); +} + +static void virtio_blk_nop(void) +{ + global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://,format=raw " + "-device virtio-blk-ccw,drive=drv0"); + qtest_end(); +} + +static void virtio_net_nop(void) +{ + global_qtest = qtest_initf("-device virtio-net-ccw"); + qtest_end(); +} + +static void virtio_rng_nop(void) +{ + global_qtest = qtest_initf("-device virtio-rng-ccw"); + qtest_end(); +} + +static void virtio_scsi_nop(void) +{ + global_qtest = qtest_initf("-device virtio-scsi-ccw"); + qtest_end(); +} + +static void virtio_scsi_hotplug(void) +{ + global_qtest = qtest_initf("-drive if=none,id=drv0,file=null-co://,format=raw " + "-drive if=none,id=drv1,file=null-co://,format=raw " + "-device virtio-scsi-ccw " + "-device scsi-hd,drive=drv0"); + qtest_qmp_device_add("scsi-hd", "scsihd", "{'drive': 'drv1'}"); + qtest_qmp_device_del("scsihd"); + + qtest_end(); +} + +int main(int argc, char **argv) +{ + int ret; + + g_test_init(&argc, &argv, NULL); + qtest_add_func("/virtio/balloon/nop", virtio_balloon_nop); + qtest_add_func("/virtio/console/nop", virtconsole_nop); + qtest_add_func("/virtio/serialport/nop", virtserialport_nop); + qtest_add_func("/virtio/serial/nop", virtio_serial_nop); + qtest_add_func("/virtio/serial/hotplug", virtio_serial_hotplug); + qtest_add_func("/virtio/block/nop", virtio_blk_nop); + qtest_add_func("/virtio/net/nop", virtio_net_nop); + qtest_add_func("/virtio/rng/nop", virtio_rng_nop); + qtest_add_func("/virtio/scsi/nop", virtio_scsi_nop); + qtest_add_func("/virtio/scsi/hotplug", virtio_scsi_hotplug); + + ret = g_test_run(); + + return ret; +} diff --git a/tests/virtio-console-test.c b/tests/virtio-console-test.c deleted file mode 100644 index 945bae5a15..0000000000 --- a/tests/virtio-console-test.c +++ /dev/null @@ -1,38 +0,0 @@ -/* - * QTest testcase for VirtIO Console - * - * Copyright (c) 2014 SUSE LINUX Products GmbH - * - * 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 "libqtest.h" -#include "libqos/virtio.h" - -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void console_nop(void) -{ - global_qtest = qtest_startf("-device virtio-serial-%s,id=vser0 " - "-device virtconsole,bus=vser0.0", - qvirtio_get_dev_type()); - qtest_end(); -} - -static void serialport_nop(void) -{ - global_qtest = qtest_startf("-device virtio-serial-%s,id=vser0 " - "-device virtserialport,bus=vser0.0", - qvirtio_get_dev_type()); - qtest_end(); -} - -int main(int argc, char **argv) -{ - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/console/nop", console_nop); - qtest_add_func("/virtio/serialport/nop", serialport_nop); - - return g_test_run(); -} diff --git a/tests/virtio-net-test.c b/tests/virtio-net-test.c index b285a262e9..0d956f36fe 100644 --- a/tests/virtio-net-test.c +++ b/tests/virtio-net-test.c @@ -9,81 +9,24 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "qemu-common.h" -#include "qemu/sockets.h" #include "qemu/iov.h" -#include "libqos/libqos-pc.h" -#include "libqos/libqos-spapr.h" -#include "libqos/virtio.h" -#include "libqos/virtio-pci.h" #include "qapi/qmp/qdict.h" -#include "qemu/bswap.h" #include "hw/virtio/virtio-net.h" -#include "standard-headers/linux/virtio_ids.h" -#include "standard-headers/linux/virtio_ring.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-net.h" + +#ifndef ETH_P_RARP +#define ETH_P_RARP 0x8035 +#endif #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, VIRTIO_ID_NET); - g_assert(dev != NULL); - g_assert_cmphex(dev->vdev.device_type, ==, VIRTIO_ID_NET); - - qvirtio_pci_device_enable(dev); - qvirtio_reset(&dev->vdev); - qvirtio_set_acknowledge(&dev->vdev); - qvirtio_set_driver(&dev->vdev); - - return dev; -} - -static QOSState *pci_test_start(int socket) -{ - QOSState *qs; - const char *arch = qtest_get_arch(); - const char *cmd = "-netdev socket,fd=%d,id=hs0 -device " - "virtio-net-pci,netdev=hs0"; - - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, socket); - } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd, socket); - } else { - g_printerr("virtio-net tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - return qs; -} - -static void driver_init(QVirtioDevice *dev) -{ - uint32_t features; - - features = qvirtio_get_features(dev); - features = features & ~(QVIRTIO_F_BAD_FEATURE | - (1u << VIRTIO_RING_F_INDIRECT_DESC) | - (1u << VIRTIO_RING_F_EVENT_IDX)); - qvirtio_set_features(dev, features); - - qvirtio_set_driver_ok(dev); -} - static void rx_test(QVirtioDevice *dev, QGuestAllocator *alloc, QVirtQueue *vq, int socket) @@ -193,80 +136,140 @@ static void rx_stop_cont_test(QVirtioDevice *dev, guest_free(alloc, req_addr); } -static void send_recv_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *rvq, - QVirtQueue *tvq, int socket) +static void send_recv_test(void *obj, void *data, QGuestAllocator *t_alloc) { - rx_test(dev, alloc, rvq, socket); - tx_test(dev, alloc, tvq, socket); + QVirtioNet *net_if = obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *rx = net_if->queues[0]; + QVirtQueue *tx = net_if->queues[1]; + int *sv = data; + + rx_test(dev, t_alloc, rx, sv[0]); + tx_test(dev, t_alloc, tx, sv[0]); } -static void stop_cont_test(QVirtioDevice *dev, - QGuestAllocator *alloc, QVirtQueue *rvq, - QVirtQueue *tvq, int socket) +static void stop_cont_test(void *obj, void *data, QGuestAllocator *t_alloc) { - rx_stop_cont_test(dev, alloc, rvq, socket); + QVirtioNet *net_if = obj; + QVirtioDevice *dev = net_if->vdev; + QVirtQueue *rx = net_if->queues[0]; + int *sv = data; + + rx_stop_cont_test(dev, t_alloc, rx, sv[0]); } -static void pci_basic(gconstpointer data) -{ - QVirtioPCIDevice *dev; - QOSState *qs; - QVirtQueuePCI *tx, *rx; - void (*func) (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); - - qs = pci_test_start(sv[1]); - dev = virtio_net_pci_init(qs->pcibus, PCI_SLOT); - - rx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 0); - tx = (QVirtQueuePCI *)qvirtqueue_setup(&dev->vdev, qs->alloc, 1); - - driver_init(&dev->vdev); - func(&dev->vdev, qs->alloc, &rx->vq, &tx->vq, sv[0]); - - /* End test */ - close(sv[0]); - qvirtqueue_cleanup(dev->vdev.bus, &tx->vq, qs->alloc); - qvirtqueue_cleanup(dev->vdev.bus, &rx->vq, qs->alloc); - qvirtio_pci_device_disable(dev); - g_free(dev->pdev); - g_free(dev); - qtest_shutdown(qs); -} #endif -static void hotplug(void) +static void hotplug(void *obj, void *data, QGuestAllocator *t_alloc) { const char *arch = qtest_get_arch(); - qtest_start("-device virtio-net-pci"); - - qpci_plug_device_test("virtio-net-pci", "net1", PCI_SLOT_HP, NULL); + qtest_qmp_device_add("virtio-net-pci", "net1", + "{'addr': %s}", stringify(PCI_SLOT_HP)); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { qpci_unplug_acpi_device_test("net1", PCI_SLOT_HP); } - - test_end(); } -int main(int argc, char **argv) +static void announce_self(void *obj, void *data, QGuestAllocator *t_alloc) { - g_test_init(&argc, &argv, NULL); -#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); + int *sv = data; + char buffer[60]; + int len; + QDict *rsp; + int ret; + uint16_t *proto = (uint16_t *)&buffer[12]; - return g_test_run(); + rsp = qmp("{ 'execute' : 'announce-self', " + " 'arguments': {" + " 'initial': 50, 'max': 550," + " 'rounds': 10, 'step': 50 } }"); + assert(!qdict_haskey(rsp, "error")); + qobject_unref(rsp); + + /* Catch the packet and make sure it's a RARP */ + ret = qemu_recv(sv[0], &len, sizeof(len), 0); + g_assert_cmpint(ret, ==, sizeof(len)); + len = ntohl(len); + + ret = qemu_recv(sv[0], buffer, len, 0); + g_assert_cmpint(*proto, ==, htons(ETH_P_RARP)); } + +static void virtio_net_test_cleanup(void *sockets) +{ + int *sv = sockets; + + close(sv[0]); + qos_invalidate_command_line(); + close(sv[1]); + g_free(sv); +} + +static void *virtio_net_test_setup(GString *cmd_line, void *arg) +{ + int ret; + int *sv = g_new(int, 2); + + ret = socketpair(PF_UNIX, SOCK_STREAM, 0, sv); + g_assert_cmpint(ret, !=, -1); + + g_string_append_printf(cmd_line, " -netdev socket,fd=%d,id=hs0 ", sv[1]); + + g_test_queue_destroy(virtio_net_test_cleanup, sv); + return sv; +} + +static void large_tx(void *obj, void *data, QGuestAllocator *t_alloc) +{ + QVirtioNet *dev = obj; + QVirtQueue *vq = dev->queues[1]; + uint64_t req_addr; + uint32_t free_head; + size_t alloc_size = (size_t)data / 64; + int i; + + /* Bypass the limitation by pointing several descriptors to a single + * smaller area */ + req_addr = guest_alloc(t_alloc, alloc_size); + free_head = qvirtqueue_add(vq, req_addr, alloc_size, false, true); + + for (i = 0; i < 64; i++) { + qvirtqueue_add(vq, req_addr, alloc_size, false, i != 63); + } + qvirtqueue_kick(dev->vdev, vq, free_head); + + qvirtio_wait_used_elem(dev->vdev, vq, free_head, NULL, + QVIRTIO_NET_TIMEOUT_US); + guest_free(t_alloc, req_addr); +} + +static void *virtio_net_test_setup_nosocket(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, " -netdev hubport,hubid=0,id=hs0 "); + return arg; +} + +static void register_virtio_net_test(void) +{ + QOSGraphTestOptions opts = { + .before = virtio_net_test_setup, + }; + + qos_add_test("hotplug", "virtio-pci", hotplug, &opts); +#ifndef _WIN32 + qos_add_test("basic", "virtio-net", send_recv_test, &opts); + qos_add_test("rx_stop_cont", "virtio-net", stop_cont_test, &opts); +#endif + qos_add_test("announce-self", "virtio-net", announce_self, &opts); + + /* These tests do not need a loopback backend. */ + opts.before = virtio_net_test_setup_nosocket; + opts.arg = (gpointer)UINT_MAX; + qos_add_test("large_tx/uint_max", "virtio-net", large_tx, &opts); + opts.arg = (gpointer)NET_BUFSIZE; + qos_add_test("large_tx/net_bufsize", "virtio-net", large_tx, &opts); +} + +libqos_init(register_virtio_net_test); diff --git a/tests/virtio-rng-test.c b/tests/virtio-rng-test.c index dcecf77463..5309c7c8ab 100644 --- a/tests/virtio-rng-test.c +++ b/tests/virtio-rng-test.c @@ -9,38 +9,26 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/pci.h" +#include "libqos/qgraph.h" +#include "libqos/virtio-rng.h" #define PCI_SLOT_HP 0x06 -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) -{ -} - -static void hotplug(void) +static void rng_hotplug(void *obj, void *data, QGuestAllocator *alloc) { const char *arch = qtest_get_arch(); - qpci_plug_device_test("virtio-rng-pci", "rng1", PCI_SLOT_HP, NULL); + qtest_qmp_device_add("virtio-rng-pci", "rng1", + "{'addr': %s}", stringify(PCI_SLOT_HP)); if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { qpci_unplug_acpi_device_test("rng1", PCI_SLOT_HP); } } -int main(int argc, char **argv) +static void register_virtio_rng_test(void) { - int ret; - - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/rng/pci/nop", pci_nop); - qtest_add_func("/virtio/rng/pci/hotplug", hotplug); - - qtest_start("-device virtio-rng-pci"); - ret = g_test_run(); - - qtest_end(); - - return ret; + qos_add_test("hotplug", "virtio-rng-pci", rng_hotplug, NULL); } + +libqos_init(register_virtio_rng_test); diff --git a/tests/virtio-scsi-test.c b/tests/virtio-scsi-test.c index 037872bb98..162b31c88d 100644 --- a/tests/virtio-scsi-test.c +++ b/tests/virtio-scsi-test.c @@ -18,6 +18,8 @@ #include "standard-headers/linux/virtio_ids.h" #include "standard-headers/linux/virtio_pci.h" #include "standard-headers/linux/virtio_scsi.h" +#include "libqos/virtio-scsi.h" +#include "libqos/qgraph.h" #define PCI_SLOT 0x02 #define PCI_FN 0x00 @@ -27,55 +29,28 @@ typedef struct { QVirtioDevice *dev; - QOSState *qs; int num_queues; QVirtQueue *vq[MAX_NUM_QUEUES + 2]; -} QVirtIOSCSI; +} QVirtioSCSIQueues; -static QOSState *qvirtio_scsi_start(const char *extra_opts) -{ - QOSState *qs; - const char *arch = qtest_get_arch(); - const char *cmd = "-drive id=drv0,if=none,file=null-co://,format=raw " - "-device virtio-scsi-pci,id=vs0 " - "-device scsi-hd,bus=vs0.0,drive=drv0 %s"; +static QGuestAllocator *alloc; - if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { - qs = qtest_pc_boot(cmd, extra_opts ? : ""); - } else if (strcmp(arch, "ppc64") == 0) { - qs = qtest_spapr_boot(cmd, extra_opts ? : ""); - } else { - g_printerr("virtio-scsi tests are only available on x86 or ppc64\n"); - exit(EXIT_FAILURE); - } - global_qtest = qs->qts; - return qs; -} - -static void qvirtio_scsi_stop(QOSState *qs) -{ - qtest_shutdown(qs); -} - -static void qvirtio_scsi_pci_free(QVirtIOSCSI *vs) +static void qvirtio_scsi_pci_free(QVirtioSCSIQueues *vs) { int i; for (i = 0; i < vs->num_queues + 2; i++) { - qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], vs->qs->alloc); + qvirtqueue_cleanup(vs->dev->bus, vs->vq[i], alloc); } - qvirtio_pci_device_disable(container_of(vs->dev, QVirtioPCIDevice, vdev)); - qvirtio_pci_device_free((QVirtioPCIDevice *)vs->dev); - qvirtio_scsi_stop(vs->qs); g_free(vs); } -static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, +static uint64_t qvirtio_scsi_alloc(QVirtioSCSIQueues *vs, size_t alloc_size, const void *data) { uint64_t addr; - addr = guest_alloc(vs->qs->alloc, alloc_size); + addr = guest_alloc(alloc, alloc_size); if (data) { memwrite(addr, data, alloc_size); } @@ -83,7 +58,8 @@ static uint64_t qvirtio_scsi_alloc(QVirtIOSCSI *vs, size_t alloc_size, return addr; } -static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, +static uint8_t virtio_scsi_do_command(QVirtioSCSIQueues *vs, + const uint8_t *cdb, const uint8_t *data_in, size_t data_in_len, uint8_t *data_out, size_t data_out_len, @@ -133,42 +109,28 @@ static uint8_t virtio_scsi_do_command(QVirtIOSCSI *vs, const uint8_t *cdb, memread(resp_addr, resp_out, sizeof(*resp_out)); } - guest_free(vs->qs->alloc, req_addr); - guest_free(vs->qs->alloc, resp_addr); - guest_free(vs->qs->alloc, data_in_addr); - guest_free(vs->qs->alloc, data_out_addr); + guest_free(alloc, req_addr); + guest_free(alloc, resp_addr); + guest_free(alloc, data_in_addr); + guest_free(alloc, data_out_addr); return response; } -static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) +static QVirtioSCSIQueues *qvirtio_scsi_init(QVirtioDevice *dev) { + QVirtioSCSIQueues *vs; const uint8_t test_unit_ready_cdb[VIRTIO_SCSI_CDB_SIZE] = {}; - QVirtIOSCSI *vs; - QVirtioPCIDevice *dev; struct virtio_scsi_cmd_resp resp; int i; - vs = g_new0(QVirtIOSCSI, 1); - - vs->qs = qvirtio_scsi_start("-drive file=blkdebug::null-co://," - "if=none,id=dr1,format=raw,file.align=4k " - "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); - dev = qvirtio_pci_device_find(vs->qs->pcibus, VIRTIO_ID_SCSI); - vs->dev = (QVirtioDevice *)dev; - g_assert(dev != NULL); - g_assert_cmphex(vs->dev->device_type, ==, VIRTIO_ID_SCSI); - - qvirtio_pci_device_enable(dev); - qvirtio_reset(vs->dev); - qvirtio_set_acknowledge(vs->dev); - qvirtio_set_driver(vs->dev); - - vs->num_queues = qvirtio_config_readl(vs->dev, 0); + vs = g_new0(QVirtioSCSIQueues, 1); + vs->dev = dev; + vs->num_queues = qvirtio_config_readl(dev, 0); g_assert_cmpint(vs->num_queues, <, MAX_NUM_QUEUES); for (i = 0; i < vs->num_queues + 2; i++) { - vs->vq[i] = qvirtqueue_setup(vs->dev, vs->qs->alloc, i); + vs->vq[i] = qvirtqueue_setup(dev, alloc, i); } /* Clear the POWER ON OCCURRED unit attention */ @@ -184,30 +146,18 @@ static QVirtIOSCSI *qvirtio_scsi_pci_init(int slot) return vs; } -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void pci_nop(void) +static void hotplug(void *obj, void *data, QGuestAllocator *alloc) { - QOSState *qs; - - qs = qvirtio_scsi_start(NULL); - qvirtio_scsi_stop(qs); -} - -static void hotplug(void) -{ - QOSState *qs; - - qs = qvirtio_scsi_start( - "-drive id=drv1,if=none,file=null-co://,format=raw"); - qtest_qmp_device_add("scsi-hd", "scsihd", "'drive': 'drv1'"); + qtest_qmp_device_add("scsi-hd", "scsihd", "{'drive': 'drv1'}"); qtest_qmp_device_del("scsihd"); - qvirtio_scsi_stop(qs); } /* Test WRITE SAME with the lba not aligned */ -static void test_unaligned_write_same(void) +static void test_unaligned_write_same(void *obj, void *data, + QGuestAllocator *t_alloc) { - QVirtIOSCSI *vs; + QVirtioSCSI *scsi = obj; + QVirtioSCSIQueues *vs; uint8_t buf1[512] = { 0 }; uint8_t buf2[512] = { 1 }; const uint8_t write_same_cdb_1[VIRTIO_SCSI_CDB_SIZE] = { @@ -220,27 +170,50 @@ static void test_unaligned_write_same(void) 0x41, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0x00, 0x00 }; - vs = qvirtio_scsi_pci_init(PCI_SLOT); + alloc = t_alloc; + vs = qvirtio_scsi_init(scsi->vdev); g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_1, NULL, 0, buf1, 512, NULL)); + 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)); + virtio_scsi_do_command(vs, write_same_cdb_2, NULL, 0, buf2, 512, + NULL)); g_assert_cmphex(0, ==, - virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, NULL)); + virtio_scsi_do_command(vs, write_same_cdb_ndob, NULL, 0, NULL, 0, + NULL)); qvirtio_scsi_pci_free(vs); } -int main(int argc, char **argv) +static void *virtio_scsi_hotplug_setup(GString *cmd_line, void *arg) { - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/scsi/pci/nop", pci_nop); - qtest_add_func("/virtio/scsi/pci/hotplug", hotplug); - qtest_add_func("/virtio/scsi/pci/scsi-disk/unaligned-write-same", - test_unaligned_write_same); - - return g_test_run(); + g_string_append(cmd_line, + " -drive id=drv1,if=none,file=null-co://,format=raw"); + return arg; } + +static void *virtio_scsi_setup(GString *cmd_line, void *arg) +{ + g_string_append(cmd_line, + " -drive file=blkdebug::null-co://," + "if=none,id=dr1,format=raw,file.align=4k " + "-device scsi-hd,drive=dr1,lun=0,scsi-id=1"); + return arg; +} + +static void register_virtio_scsi_test(void) +{ + QOSGraphTestOptions opts = { }; + + opts.before = virtio_scsi_hotplug_setup; + qos_add_test("hotplug", "virtio-scsi", hotplug, &opts); + + opts.before = virtio_scsi_setup; + qos_add_test("unaligned-write-same", "virtio-scsi", + test_unaligned_write_same, &opts); +} + +libqos_init(register_virtio_scsi_test); diff --git a/tests/virtio-serial-test.c b/tests/virtio-serial-test.c index 7cc7060264..85f35e09b7 100644 --- a/tests/virtio-serial-test.c +++ b/tests/virtio-serial-test.c @@ -9,33 +9,30 @@ #include "qemu/osdep.h" #include "libqtest.h" -#include "libqos/virtio.h" +#include "libqos/virtio-serial.h" /* Tests only initialization so far. TODO: Replace with functional tests */ -static void virtio_serial_nop(void) +static void virtio_serial_nop(void *obj, void *data, QGuestAllocator *alloc) { + /* no operation */ } -static void hotplug(void) +static void serial_hotplug(void *obj, void *data, QGuestAllocator *alloc) { - qtest_qmp_device_add("virtserialport", "hp-port", NULL); - + qtest_qmp_device_add("virtserialport", "hp-port", "{}"); qtest_qmp_device_del("hp-port"); } -int main(int argc, char **argv) +static void register_virtio_serial_test(void) { - int ret; + QOSGraphTestOptions opts = { }; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/virtio/serial/nop", virtio_serial_nop); - qtest_add_func("/virtio/serial/hotplug", hotplug); + opts.edge.before_cmd_line = "-device virtconsole,bus=vser0.0"; + qos_add_test("console-nop", "virtio-serial", virtio_serial_nop, &opts); - global_qtest = qtest_startf("-device virtio-serial-%s", - qvirtio_get_dev_type()); - ret = g_test_run(); + opts.edge.before_cmd_line = "-device virtserialport,bus=vser0.0"; + qos_add_test("serialport-nop", "virtio-serial", virtio_serial_nop, &opts); - qtest_end(); - - return ret; + qos_add_test("hotplug", "virtio-serial", serial_hotplug, NULL); } +libqos_init(register_virtio_serial_test); diff --git a/tests/virtio-test.c b/tests/virtio-test.c new file mode 100644 index 0000000000..804e5371dc --- /dev/null +++ b/tests/virtio-test.c @@ -0,0 +1,25 @@ +/* + * QTest testcase for virtio + * + * Copyright (c) 2018 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 "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" + +/* Tests only initialization so far. TODO: Replace with functional tests */ +static void nop(void *obj, void *data, QGuestAllocator *alloc) +{ +} + +static void register_virtio_test(void) +{ + qos_add_test("nop", "virtio", nop, NULL); +} + +libqos_init(register_virtio_test); diff --git a/tests/vm/Makefile.include b/tests/vm/Makefile.include index 5daa2a3b73..992d823f6b 100644 --- a/tests/vm/Makefile.include +++ b/tests/vm/Makefile.include @@ -1,9 +1,10 @@ # Makefile for VM tests -.PHONY: vm-build-all +.PHONY: vm-build-all vm-clean-all -IMAGES := ubuntu.i386 freebsd netbsd openbsd -IMAGE_FILES := $(patsubst %, tests/vm/%.img, $(IMAGES)) +IMAGES := ubuntu.i386 freebsd netbsd openbsd centos +IMAGES_DIR := $(HOME)/.cache/qemu-vm/images +IMAGE_FILES := $(patsubst %, $(IMAGES_DIR)/%.img, $(IMAGES)) .PRECIOUS: $(IMAGE_FILES) @@ -14,12 +15,25 @@ vm-test: @echo " vm-build-freebsd - Build QEMU in FreeBSD VM" @echo " vm-build-netbsd - Build QEMU in NetBSD VM" @echo " vm-build-openbsd - Build QEMU in OpenBSD VM" + @echo " vm-build-centos - Build QEMU in CentOS VM, with Docker" + @echo "" + @echo " vm-build-all - Build QEMU in all VMs" + @echo " vm-clean-all - Clean up VM images" + @echo + @echo "Special variables:" + @echo " BUILD_TARGET=foo - override the build target" + @echo " TARGET_LIST=a,b,c - Override target list in builds." + @echo ' EXTRA_CONFIGURE_OPTS="..."' vm-build-all: $(addprefix vm-build-, $(IMAGES)) -tests/vm/%.img: $(SRC_PATH)/tests/vm/% \ - $(SRC_PATH)/tests/vm/basevm.py \ - $(SRC_PATH)/tests/vm/Makefile.include +vm-clean-all: + rm -f $(IMAGE_FILES) + +$(IMAGES_DIR)/%.img: $(SRC_PATH)/tests/vm/% \ + $(SRC_PATH)/tests/vm/basevm.py \ + $(SRC_PATH)/tests/vm/Makefile.include + @mkdir -p $(IMAGES_DIR) $(call quiet-command, \ $< \ $(if $(V)$(DEBUG), --debug) \ @@ -30,13 +44,17 @@ tests/vm/%.img: $(SRC_PATH)/tests/vm/% \ # Build in VM $(IMAGE) -vm-build-%: tests/vm/%.img +vm-build-%: $(IMAGES_DIR)/%.img $(call quiet-command, \ $(SRC_PATH)/tests/vm/$* \ $(if $(V)$(DEBUG), --debug) \ $(if $(DEBUG), --interactive) \ $(if $(J),--jobs $(J)) \ + $(if $(V),--verbose) \ --image "$<" \ - --build-qemu $(SRC_PATH), \ + $(if $(BUILD_TARGET),--build-target $(BUILD_TARGET)) \ + --build-qemu $(SRC_PATH) -- \ + $(if $(TARGET_LIST),--target-list=$(TARGET_LIST)) \ + $(if $(EXTRA_CONFIGURE_OPTS),$(EXTRA_CONFIGURE_OPTS)), \ " VM-BUILD $*") diff --git a/tests/vm/basevm.py b/tests/vm/basevm.py index 3643117816..0556bdcf9e 100755 --- a/tests/vm/basevm.py +++ b/tests/vm/basevm.py @@ -17,8 +17,8 @@ import sys import logging import time import datetime -sys.path.append(os.path.join(os.path.dirname(__file__), "..", "..", "scripts")) -from qemu import QEMUMachine +sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..', 'python')) +from qemu import QEMUMachine, kvm_available import subprocess import hashlib import optparse @@ -42,6 +42,8 @@ class BaseVM(object): BUILD_SCRIPT = "" # The guest name, to be overridden by subclasses name = "#base" + # The guest architecture, to be overridden by subclasses + arch = "#arch" def __init__(self, debug=False, vcpus=None): self._guest = None self._tmpdir = os.path.realpath(tempfile.mkdtemp(prefix="vm-test-", @@ -64,15 +66,15 @@ class BaseVM(object): else: self._stdout = self._devnull self._args = [ \ - "-nodefaults", "-m", "2G", - "-cpu", "host", + "-nodefaults", "-m", "4G", + "-cpu", "max", "-netdev", "user,id=vnet,hostfwd=:127.0.0.1:0-:22", "-device", "virtio-net-pci,netdev=vnet", "-vnc", "127.0.0.1:0,to=20", "-serial", "file:%s" % os.path.join(self._tmpdir, "serial.out")] - if vcpus: + if vcpus and vcpus > 1: self._args += ["-smp", str(vcpus)] - if os.access("/dev/kvm", os.R_OK | os.W_OK): + if kvm_available(self.arch): self._args += ["-enable-kvm"] else: logging.info("KVM not available, not using -enable-kvm") @@ -151,7 +153,7 @@ class BaseVM(object): "-device", "virtio-blk,drive=drive0,bootindex=0"] args += self._data_args + extra_args logging.debug("QEMU args: %s", " ".join(args)) - qemu_bin = os.environ.get("QEMU", "qemu-system-x86_64") + qemu_bin = os.environ.get("QEMU", "qemu-system-" + self.arch) guest = QEMUMachine(binary=qemu_bin, args=args) try: guest.launch() @@ -175,13 +177,16 @@ class BaseVM(object): raise Exception("Cannot find ssh port from 'info usernet':\n%s" % \ usernet_info) - def wait_ssh(self, seconds=120): + def wait_ssh(self, seconds=300): starttime = datetime.datetime.now() + endtime = starttime + datetime.timedelta(seconds=seconds) guest_up = False - while (datetime.datetime.now() - starttime).total_seconds() < seconds: + while datetime.datetime.now() < endtime: if self.ssh("exit 0") == 0: guest_up = True break + seconds = (endtime - datetime.datetime.now()).total_seconds() + logging.debug("%ds before timeout", seconds) time.sleep(1) if not guest_up: raise Exception("Timeout while waiting for guest ssh") @@ -195,7 +200,14 @@ class BaseVM(object): def qmp(self, *args, **kwargs): return self._guest.qmp(*args, **kwargs) -def parse_args(vm_name): +def parse_args(vmcls): + + def get_default_jobs(): + if kvm_available(vmcls.arch): + return multiprocessing.cpu_count() / 2 + else: + return 1 + parser = optparse.OptionParser( description="VM test utility. Exit codes: " "0 = success, " @@ -204,24 +216,30 @@ def parse_args(vm_name): "3 = test command failed") parser.add_option("--debug", "-D", action="store_true", help="enable debug output") - parser.add_option("--image", "-i", default="%s.img" % vm_name, + parser.add_option("--image", "-i", default="%s.img" % vmcls.name, help="image file name") parser.add_option("--force", "-f", action="store_true", help="force build image even if image exists") - parser.add_option("--jobs", type=int, default=multiprocessing.cpu_count() / 2, + parser.add_option("--jobs", type=int, default=get_default_jobs(), help="number of virtual CPUs") + parser.add_option("--verbose", "-V", action="store_true", + help="Pass V=1 to builds within the guest") parser.add_option("--build-image", "-b", action="store_true", help="build image") parser.add_option("--build-qemu", help="build QEMU from source in guest") + parser.add_option("--build-target", + help="QEMU build target", default="check") parser.add_option("--interactive", "-I", action="store_true", help="Interactively run command") + parser.add_option("--snapshot", "-s", action="store_true", + help="run tests with a snapshot") parser.disable_interspersed_args() return parser.parse_args() def main(vmcls): try: - args, argv = parse_args(vmcls.name) + args, argv = parse_args(vmcls) if not argv and not args.build_qemu and not args.build_image: print("Nothing to do?") return 1 @@ -238,10 +256,15 @@ def main(vmcls): vm.add_source_dir(args.build_qemu) cmd = [vm.BUILD_SCRIPT.format( configure_opts = " ".join(argv), - jobs=args.jobs)] + jobs=args.jobs, + target=args.build_target, + verbose = "V=1" if args.verbose else "")] else: cmd = argv - vm.boot(args.image + ",snapshot=on") + img = args.image + if args.snapshot: + img += ",snapshot=on" + vm.boot(img) vm.wait_ssh() except Exception as e: if isinstance(e, SystemExit) and e.code == 0: diff --git a/tests/vm/centos b/tests/vm/centos new file mode 100755 index 0000000000..ba133ea429 --- /dev/null +++ b/tests/vm/centos @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# CentOS image +# +# Copyright 2018 Red Hat Inc. +# +# Authors: +# Fam Zheng +# +# This code is licensed under the GPL version 2 or later. See +# the COPYING file in the top-level directory. +# + +import os +import sys +import subprocess +import basevm +import time + +class CentosVM(basevm.BaseVM): + name = "centos" + arch = "x86_64" + BUILD_SCRIPT = """ + set -e; + cd $(mktemp -d); + export SRC_ARCHIVE=/dev/vdb; + sudo chmod a+r $SRC_ARCHIVE; + tar -xf $SRC_ARCHIVE; + make docker-test-block@centos7 V={verbose} J={jobs}; + make docker-test-quick@centos7 V={verbose} J={jobs}; + make docker-test-mingw@fedora V={verbose} J={jobs}; + """ + + def _gen_cloud_init_iso(self): + cidir = self._tmpdir + mdata = open(os.path.join(cidir, "meta-data"), "w") + mdata.writelines(["instance-id: centos-vm-0\n", + "local-hostname: centos-guest\n"]) + mdata.close() + udata = open(os.path.join(cidir, "user-data"), "w") + udata.writelines(["#cloud-config\n", + "chpasswd:\n", + " list: |\n", + " root:%s\n" % self.ROOT_PASS, + " %s:%s\n" % (self.GUEST_USER, self.GUEST_PASS), + " expire: False\n", + "users:\n", + " - name: %s\n" % self.GUEST_USER, + " sudo: ALL=(ALL) NOPASSWD:ALL\n", + " ssh-authorized-keys:\n", + " - %s\n" % basevm.SSH_PUB_KEY, + " - name: root\n", + " ssh-authorized-keys:\n", + " - %s\n" % basevm.SSH_PUB_KEY, + "locale: en_US.UTF-8\n"]) + udata.close() + subprocess.check_call(["genisoimage", "-output", "cloud-init.iso", + "-volid", "cidata", "-joliet", "-rock", + "user-data", "meta-data"], + cwd=cidir, + stdin=self._devnull, stdout=self._stdout, + stderr=self._stdout) + return os.path.join(cidir, "cloud-init.iso") + + def build_image(self, img): + cimg = self._download_with_cache("https://cloud.centos.org/centos/7/images/CentOS-7-x86_64-GenericCloud-1802.qcow2.xz") + img_tmp = img + ".tmp" + sys.stderr.write("Extracting the image...\n") + subprocess.check_call(["cp", "-f", cimg, img_tmp + ".xz"]) + subprocess.check_call(["xz", "-dvf", img_tmp + ".xz"]) + subprocess.check_call(["qemu-img", "resize", img_tmp, "50G"]) + self.boot(img_tmp, extra_args = ["-cdrom", self._gen_cloud_init_iso()]) + self.wait_ssh() + self.ssh_root_check("touch /etc/cloud/cloud-init.disabled") + self.ssh_root_check("yum update -y") + self.ssh_root_check("yum install -y docker make git") + self.ssh_root_check("systemctl enable docker") + self.ssh_root("poweroff") + self.wait() + if os.path.exists(img): + os.remove(img) + os.rename(img_tmp, img) + return 0 + +if __name__ == "__main__": + sys.exit(basevm.main(CentosVM)) diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 039dad8f69..b0066017a6 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -18,13 +18,14 @@ import basevm class FreeBSDVM(basevm.BaseVM): name = "freebsd" + arch = "x86_64" BUILD_SCRIPT = """ set -e; + rm -rf /var/tmp/qemu-test.* cd $(mktemp -d /var/tmp/qemu-test.XXXXXX); tar -xf /dev/vtbd1; ./configure {configure_opts}; - gmake -j{jobs}; - gmake check; + gmake --output-sync -j{jobs} {target} {verbose}; """ def build_image(self, img): @@ -32,8 +33,9 @@ class FreeBSDVM(basevm.BaseVM): sha256sum='adcb771549b37bc63826c501f05121a206ed3d9f55f49145908f7e1432d65891') img_tmp_xz = img + ".tmp.xz" img_tmp = img + ".tmp" + sys.stderr.write("Extracting the image...\n") subprocess.check_call(["cp", "-f", cimg, img_tmp_xz]) - subprocess.check_call(["xz", "-df", img_tmp_xz]) + subprocess.check_call(["xz", "-dvf", img_tmp_xz]) if os.path.exists(img): os.remove(img) os.rename(img_tmp, img) diff --git a/tests/vm/netbsd b/tests/vm/netbsd index 3972d8b45c..4c6624ea5e 100755 --- a/tests/vm/netbsd +++ b/tests/vm/netbsd @@ -18,13 +18,14 @@ import basevm class NetBSDVM(basevm.BaseVM): name = "netbsd" + arch = "x86_64" BUILD_SCRIPT = """ set -e; + rm -rf /var/tmp/qemu-test.* cd $(mktemp -d /var/tmp/qemu-test.XXXXXX); tar -xf /dev/rld1a; ./configure --python=python2.7 {configure_opts}; - gmake -j{jobs}; - gmake check; + gmake --output-sync -j{jobs} {target} {verbose}; """ def build_image(self, img): @@ -32,8 +33,9 @@ class NetBSDVM(basevm.BaseVM): sha256sum='b633d565b0eac3d02015cd0c81440bd8a7a8df8512615ac1ee05d318be015732') img_tmp_xz = img + ".tmp.xz" img_tmp = img + ".tmp" + sys.stderr.write("Extracting the image...\n") subprocess.check_call(["cp", "-f", cimg, img_tmp_xz]) - subprocess.check_call(["xz", "-df", img_tmp_xz]) + subprocess.check_call(["xz", "-dvf", img_tmp_xz]) if os.path.exists(img): os.remove(img) os.rename(img_tmp, img) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 6ae16d97fd..2105c01a26 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -18,14 +18,16 @@ import basevm class OpenBSDVM(basevm.BaseVM): name = "openbsd" + arch = "x86_64" BUILD_SCRIPT = """ set -e; + rm -rf /var/tmp/qemu-test.* cd $(mktemp -d /var/tmp/qemu-test.XXXXXX); tar -xf /dev/rsd1c; ./configure --cc=x86_64-unknown-openbsd6.1-gcc-4.9.4 --python=python2.7 {configure_opts}; - gmake -j{jobs}; + gmake --output-sync -j{jobs} {verbose}; # XXX: "gmake check" seems to always hang or fail - #gmake check; + #gmake --output-sync -j{jobs} check {verbose}; """ def build_image(self, img): @@ -33,8 +35,9 @@ class OpenBSDVM(basevm.BaseVM): sha256sum='8c6cedc483e602cfee5e04f0406c64eb99138495e8ca580bc0293bcf0640c1bf') img_tmp_xz = img + ".tmp.xz" img_tmp = img + ".tmp" + sys.stderr.write("Extracting the image...\n") subprocess.check_call(["cp", "-f", cimg, img_tmp_xz]) - subprocess.check_call(["xz", "-df", img_tmp_xz]) + subprocess.check_call(["xz", "-dvf", img_tmp_xz]) if os.path.exists(img): os.remove(img) os.rename(img_tmp, img) diff --git a/tests/vm/ubuntu.i386 b/tests/vm/ubuntu.i386 index fc319e0e6e..a22d137e76 100755 --- a/tests/vm/ubuntu.i386 +++ b/tests/vm/ubuntu.i386 @@ -19,14 +19,14 @@ import time class UbuntuX86VM(basevm.BaseVM): name = "ubuntu.i386" + arch = "i386" BUILD_SCRIPT = """ set -e; cd $(mktemp -d); sudo chmod a+r /dev/vdb; tar -xf /dev/vdb; ./configure {configure_opts}; - make -j{jobs}; - make check; + make --output-sync {target} -j{jobs} {verbose}; """ def _gen_cloud_init_iso(self): @@ -77,7 +77,7 @@ class UbuntuX86VM(basevm.BaseVM): # The previous update sometimes doesn't survive a reboot, so do it again self.ssh_root_check("apt-get update") self.ssh_root_check("apt-get build-dep -y qemu") - self.ssh_root_check("apt-get install -y libfdt-dev") + self.ssh_root_check("apt-get install -y libfdt-dev flex bison") self.ssh_root("poweroff") self.wait() if os.path.exists(img): diff --git a/tests/vmgenid-test.c b/tests/vmgenid-test.c index 8d915c610c..ae38ee5ac0 100644 --- a/tests/vmgenid-test.c +++ b/tests/vmgenid-test.c @@ -23,102 +23,80 @@ */ #define RSDP_ADDR_INVALID 0x100000 /* RSDP must be below this address */ -typedef struct { - AcpiTableHeader header; - gchar name_op; - gchar vgia[4]; - gchar val_op; - uint32_t vgia_val; -} QEMU_PACKED VgidTable; - -static uint32_t acpi_find_vgia(void) +static uint32_t acpi_find_vgia(QTestState *qts) { uint32_t rsdp_offset; uint32_t guid_offset = 0; - AcpiRsdpDescriptor rsdp_table; - uint32_t rsdt, rsdt_table_length; - AcpiRsdtDescriptorRev1 rsdt_table; - size_t tables_nr; - uint32_t *tables; - AcpiTableHeader ssdt_table; - VgidTable vgid_table; - int i; + uint8_t rsdp_table[36 /* ACPI 2.0+ RSDP size */]; + uint32_t rsdt_len, table_length; + uint8_t *rsdt, *ent; /* Wait for guest firmware to finish and start the payload. */ - boot_sector_test(global_qtest); + boot_sector_test(qts); /* Tables should be initialized now. */ - rsdp_offset = acpi_find_rsdp_address(); + rsdp_offset = acpi_find_rsdp_address(qts); g_assert_cmphex(rsdp_offset, <, RSDP_ADDR_INVALID); - acpi_parse_rsdp_table(rsdp_offset, &rsdp_table); - rsdt = le32_to_cpu(rsdp_table.rsdt_physical_address); - /* read the header */ - ACPI_READ_TABLE_HEADER(&rsdt_table, rsdt); - ACPI_ASSERT_CMP(rsdt_table.signature, "RSDT"); - rsdt_table_length = le32_to_cpu(rsdt_table.length); + acpi_parse_rsdp_table(qts, rsdp_offset, rsdp_table); + acpi_fetch_table(qts, &rsdt, &rsdt_len, &rsdp_table[16 /* RsdtAddress */], + "RSDT", true); - /* compute the table entries in rsdt */ - g_assert_cmpint(rsdt_table_length, >, sizeof(AcpiRsdtDescriptorRev1)); - tables_nr = (rsdt_table_length - sizeof(AcpiRsdtDescriptorRev1)) / - sizeof(uint32_t); + ACPI_FOREACH_RSDT_ENTRY(rsdt, rsdt_len, ent, 4 /* Entry size */) { + uint8_t *table_aml; - /* get the addresses of the tables pointed by rsdt */ - tables = g_new0(uint32_t, tables_nr); - ACPI_READ_ARRAY_PTR(tables, tables_nr, rsdt); - - for (i = 0; i < tables_nr; i++) { - uint32_t addr = le32_to_cpu(tables[i]); - ACPI_READ_TABLE_HEADER(&ssdt_table, addr); - if (!strncmp((char *)ssdt_table.oem_table_id, "VMGENID", 7)) { + acpi_fetch_table(qts, &table_aml, &table_length, ent, NULL, true); + if (!memcmp(table_aml + 16 /* OEM Table ID */, "VMGENID", 7)) { + uint32_t vgia_val; + uint8_t *aml = &table_aml[36 /* AML byte-code start */]; /* the first entry in the table should be VGIA * That's all we need */ - ACPI_READ_FIELD(vgid_table.name_op, addr); - g_assert(vgid_table.name_op == 0x08); /* name */ - ACPI_READ_ARRAY(vgid_table.vgia, addr); - g_assert(memcmp(vgid_table.vgia, "VGIA", 4) == 0); - ACPI_READ_FIELD(vgid_table.val_op, addr); - g_assert(vgid_table.val_op == 0x0C); /* dword */ - ACPI_READ_FIELD(vgid_table.vgia_val, addr); + g_assert(aml[0 /* name_op*/] == 0x08); + g_assert(memcmp(&aml[1 /* name */], "VGIA", 4) == 0); + g_assert(aml[5 /* value op */] == 0x0C /* dword */); + memcpy(&vgia_val, &aml[6 /* value */], 4); + /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" * see docs/specs/vmgenid.txt for more details */ - guid_offset = le32_to_cpu(vgid_table.vgia_val) + VMGENID_GUID_OFFSET; + guid_offset = le32_to_cpu(vgia_val) + VMGENID_GUID_OFFSET; + g_free(table_aml); break; } + g_free(table_aml); } - g_free(tables); + g_free(rsdt); return guid_offset; } -static void read_guid_from_memory(QemuUUID *guid) +static void read_guid_from_memory(QTestState *qts, QemuUUID *guid) { uint32_t vmgenid_addr; int i; - vmgenid_addr = acpi_find_vgia(); + vmgenid_addr = acpi_find_vgia(qts); g_assert(vmgenid_addr); /* Read the GUID directly from guest memory */ for (i = 0; i < 16; i++) { - guid->data[i] = readb(vmgenid_addr + i); + guid->data[i] = qtest_readb(qts, vmgenid_addr + i); } /* The GUID is in little-endian format in the guest, while QEMU * uses big-endian. Swap after reading. */ - qemu_uuid_bswap(guid); + *guid = qemu_uuid_bswap(*guid); } -static void read_guid_from_monitor(QemuUUID *guid) +static void read_guid_from_monitor(QTestState *qts, QemuUUID *guid) { QDict *rsp, *rsp_ret; const char *guid_str; - rsp = qmp("{ 'execute': 'query-vm-generation-id' }"); + rsp = qtest_qmp(qts, "{ 'execute': 'query-vm-generation-id' }"); if (qdict_haskey(rsp, "return")) { rsp_ret = qdict_get_qdict(rsp, "return"); g_assert(qdict_haskey(rsp_ret, "guid")); @@ -139,45 +117,48 @@ static char disk[] = "tests/vmgenid-test-disk-XXXXXX"; static void vmgenid_set_guid_test(void) { QemuUUID expected, measured; + QTestState *qts; g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); - global_qtest = qtest_startf(GUID_CMD(VGID_GUID)); + qts = qtest_initf(GUID_CMD(VGID_GUID)); /* Read the GUID from accessing guest memory */ - read_guid_from_memory(&measured); + read_guid_from_memory(qts, &measured); g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); - qtest_quit(global_qtest); + qtest_quit(qts); } static void vmgenid_set_guid_auto_test(void) { QemuUUID measured; + QTestState *qts; - global_qtest = qtest_startf(GUID_CMD("auto")); + qts = qtest_initf(GUID_CMD("auto")); - read_guid_from_memory(&measured); + read_guid_from_memory(qts, &measured); /* Just check that the GUID is non-null */ g_assert(!qemu_uuid_is_null(&measured)); - qtest_quit(global_qtest); + qtest_quit(qts); } static void vmgenid_query_monitor_test(void) { QemuUUID expected, measured; + QTestState *qts; g_assert(qemu_uuid_parse(VGID_GUID, &expected) == 0); - global_qtest = qtest_startf(GUID_CMD(VGID_GUID)); + qts = qtest_initf(GUID_CMD(VGID_GUID)); /* Read the GUID via the monitor */ - read_guid_from_monitor(&measured); + read_guid_from_monitor(qts, &measured); g_assert(memcmp(measured.data, expected.data, sizeof(measured.data)) == 0); - qtest_quit(global_qtest); + qtest_quit(qts); } int main(int argc, char **argv) diff --git a/tests/vmxnet3-test.c b/tests/vmxnet3-test.c index 159c0ad728..35cdea939b 100644 --- a/tests/vmxnet3-test.c +++ b/tests/vmxnet3-test.c @@ -9,23 +9,49 @@ #include "qemu/osdep.h" #include "libqtest.h" +#include "libqos/qgraph.h" +#include "libqos/pci.h" -/* Tests only initialization so far. TODO: Replace with functional tests */ -static void nop(void) +typedef struct QVmxnet3 QVmxnet3; + +struct QVmxnet3 { + QOSGraphObject obj; + QPCIDevice dev; +}; + +static void *vmxnet3_get_driver(void *obj, const char *interface) { + QVmxnet3 *vmxnet3 = obj; + + if (!g_strcmp0(interface, "pci-device")) { + return &vmxnet3->dev; + } + + fprintf(stderr, "%s not present in vmxnet3\n", interface); + g_assert_not_reached(); } -int main(int argc, char **argv) +static void *vmxnet3_create(void *pci_bus, QGuestAllocator *alloc, void *addr) { - int ret; + QVmxnet3 *vmxnet3 = g_new0(QVmxnet3, 1); + QPCIBus *bus = pci_bus; - g_test_init(&argc, &argv, NULL); - qtest_add_func("/vmxnet3/nop", nop); + qpci_device_init(&vmxnet3->dev, bus, addr); + vmxnet3->obj.get_driver = vmxnet3_get_driver; - qtest_start("-device vmxnet3"); - ret = g_test_run(); - - qtest_end(); - - return ret; + return &vmxnet3->obj; } + +static void vmxnet3_register_nodes(void) +{ + QOSGraphEdgeOptions opts = { + .extra_device_opts = "addr=04.0", + }; + add_qpci_address(&opts, &(QPCIAddress) { .devfn = QPCI_DEVFN(4, 0) }); + + qos_node_create_driver("vmxnet3", vmxnet3_create); + qos_node_consumes("vmxnet3", "pci-bus", &opts); + qos_node_produces("vmxnet3", "pci-device"); +} + +libqos_init(vmxnet3_register_nodes); diff --git a/thunk.c b/thunk.c index d5d8645cd4..7f31cffe09 100644 --- a/thunk.c +++ b/thunk.c @@ -86,10 +86,10 @@ void thunk_register_struct(int id, const char *name, const argtype *types) #endif /* now we can alloc the data */ - for(i = 0;i < 2; i++) { + for (i = 0; i < ARRAY_SIZE(se->field_offsets); i++) { offset = 0; max_align = 1; - se->field_offsets[i] = malloc(nb_fields * sizeof(int)); + se->field_offsets[i] = g_new(int, nb_fields); type_ptr = se->field_types; for(j = 0;j < nb_fields; j++) { size = thunk_type_size(type_ptr, i); diff --git a/tpm.c b/tpm.c index 93031723ad..9c9e20bbb7 100644 --- a/tpm.c +++ b/tpm.c @@ -89,19 +89,19 @@ static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp) int i; if (!QLIST_EMPTY(&tpm_backends)) { - error_report("Only one TPM is allowed."); + error_setg(errp, "Only one TPM is allowed."); return 1; } id = qemu_opts_id(opts); if (id == NULL) { - error_report(QERR_MISSING_PARAMETER, "id"); + error_setg(errp, QERR_MISSING_PARAMETER, "id"); return 1; } value = qemu_opt_get(opts, "type"); if (!value) { - error_report(QERR_MISSING_PARAMETER, "type"); + error_setg(errp, QERR_MISSING_PARAMETER, "type"); tpm_display_backend_drivers(); return 1; } @@ -109,8 +109,8 @@ static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp) i = qapi_enum_parse(&TpmType_lookup, value, -1, NULL); be = i >= 0 ? tpm_be_find_by_type(i) : NULL; if (be == NULL) { - error_report(QERR_INVALID_PARAMETER_VALUE, - "type", "a TPM backend type"); + error_setg(errp, QERR_INVALID_PARAMETER_VALUE, "type", + "a TPM backend type"); tpm_display_backend_drivers(); return 1; } @@ -118,7 +118,7 @@ static int tpm_init_tpmdev(void *dummy, QemuOpts *opts, Error **errp) /* validate backend specific opts */ qemu_opts_validate(opts, be->opts, &local_err); if (local_err) { - error_report_err(local_err); + error_propagate(errp, local_err); return 1; } @@ -151,14 +151,10 @@ void tpm_cleanup(void) * Initialize the TPM. Process the tpmdev command line options describing the * TPM backend. */ -int tpm_init(void) +void tpm_init(void) { - if (qemu_opts_foreach(qemu_find_opts("tpmdev"), - tpm_init_tpmdev, NULL, NULL)) { - return -1; - } - - return 0; + qemu_opts_foreach(qemu_find_opts("tpmdev"), + tpm_init_tpmdev, NULL, &error_fatal); } /* diff --git a/trace-events b/trace-events index c445f54773..844ee58dd9 100644 --- a/trace-events +++ b/trace-events @@ -1,4 +1,4 @@ -# Trace events for debugging and performance instrumentation +# See docs/devel/tracing.txt for syntax documentation. # # This file is processed by the tracetool script during the build. # @@ -34,9 +34,9 @@ cpu_out(unsigned int addr, char size, unsigned int val) "addr 0x%x(%c) value %u" balloon_event(void *opaque, unsigned long addr) "opaque %p addr %lu" # vl.c -vm_state_notify(int running, int reason) "running %d reason %d" +vm_state_notify(int running, int reason, const char *reason_str) "running %d reason %d (%s)" load_file(const char *name, const char *path) "name %s location %s" -runstate_set(int new_state) "new state %d" +runstate_set(int current_state, const char *current_state_str, int new_state, const char *new_state_str) "current_run_state %d (%s) new_state %d (%s)" system_wakeup_request(int reason) "reason=%d" qemu_system_shutdown_request(int reason) "reason=%d" qemu_system_powerdown_request(void) "" @@ -107,7 +107,7 @@ gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packe # job.c job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)" job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)" -job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d" +job_completed(void *job, int ret) "job %p ret %d" # job-qmp.c qmp_job_cancel(void *job) "job %p" @@ -123,24 +123,32 @@ qmp_job_dismiss(void *job) "job %p" ## vCPU +# trace/control-target.c + # Hot-plug a new virtual (guest) CPU # # Mode: user, softmmu # Targets: all vcpu guest_cpu_enter(void) +# trace/control.c + # Hot-unplug a virtual (guest) CPU # # Mode: user, softmmu # Targets: all vcpu guest_cpu_exit(void) +# qom/cpu.c + # Reset the state of a virtual (guest) CPU # # Mode: user, softmmu # Targets: all vcpu guest_cpu_reset(void) +# tcg/tcg-op.c + # @vaddr: Access' virtual address. # @info : Access' information (see below). # @@ -161,6 +169,9 @@ vcpu guest_cpu_reset(void) # Targets: TCG(all) vcpu tcg guest_mem_before(TCGv vaddr, uint8_t info) "info=%d", "vaddr=0x%016"PRIx64" info=%d" +# linux-user/syscall.c +# bsd-user/syscall.c + # @num: System call number. # @arg*: System call argument value. # diff --git a/trace/Makefile.objs b/trace/Makefile.objs index afd571c3ec..c544509adf 100644 --- a/trace/Makefile.objs +++ b/trace/Makefile.objs @@ -36,7 +36,7 @@ $(obj)/generated-helpers.c-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/conf $(obj)/generated-helpers.o: $(obj)/generated-helpers.c -target-obj-y += generated-helpers.o +obj-y += generated-helpers.o $(obj)/generated-tcg-tracers.h: $(obj)/generated-tcg-tracers.h-timestamp @@ -55,5 +55,5 @@ $(obj)/generated-tcg-tracers.h-timestamp: $(SRC_PATH)/trace-events $(BUILD_DIR)/ util-obj-$(CONFIG_TRACE_SIMPLE) += simple.o util-obj-$(CONFIG_TRACE_FTRACE) += ftrace.o util-obj-y += control.o -target-obj-y += control-target.o +obj-y += control-target.o util-obj-y += qmp.o diff --git a/trace/ftrace.c b/trace/ftrace.c index 61692a8682..9749543d9b 100644 --- a/trace/ftrace.c +++ b/trace/ftrace.c @@ -53,7 +53,11 @@ bool ftrace_init(void) } if (tracefs_found) { - snprintf(path, PATH_MAX, "%s%s/tracing_on", mount_point, subdir); + if (snprintf(path, PATH_MAX, "%s%s/tracing_on", mount_point, subdir) + >= sizeof(path)) { + fprintf(stderr, "Using tracefs mountpoint would exceed PATH_MAX\n"); + return false; + } trace_fd = open(path, O_WRONLY); if (trace_fd < 0) { if (errno == EACCES) { @@ -72,7 +76,11 @@ bool ftrace_init(void) } close(trace_fd); } - snprintf(path, PATH_MAX, "%s%s/trace_marker", mount_point, subdir); + if (snprintf(path, PATH_MAX, "%s%s/trace_marker", mount_point, subdir) + >= sizeof(path)) { + fprintf(stderr, "Using tracefs mountpoint would exceed PATH_MAX\n"); + return false; + } trace_marker_fd = open(path, O_WRONLY); if (trace_marker_fd < 0) { perror("Could not open ftrace 'trace_marker' file"); diff --git a/trace/simple.c b/trace/simple.c index 701dec639c..ac904eca91 100644 --- a/trace/simple.c +++ b/trace/simple.c @@ -169,9 +169,9 @@ static gpointer writeout_thread(gpointer opaque) wait_for_trace_records_available(); if (g_atomic_int_get(&dropped_events)) { - dropped.rec.event = DROPPED_EVENT_ID, + dropped.rec.event = DROPPED_EVENT_ID; dropped.rec.timestamp_ns = get_clock(); - dropped.rec.length = sizeof(TraceRecord) + sizeof(uint64_t), + dropped.rec.length = sizeof(TraceRecord) + sizeof(uint64_t); dropped.rec.pid = trace_pid; do { dropped_count = g_atomic_int_get(&dropped_events); diff --git a/ui/Makefile.objs b/ui/Makefile.objs index 00f6976c30..cc2bf5b180 100644 --- a/ui/Makefile.objs +++ b/ui/Makefile.objs @@ -8,7 +8,7 @@ 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-y += input.o input-keymap.o input-legacy.o kbd-state.o common-obj-$(CONFIG_LINUX) += input-linux.o common-obj-$(CONFIG_SPICE) += spice-core.o spice-input.o spice-display.o common-obj-$(CONFIG_COCOA) += cocoa.o @@ -17,15 +17,10 @@ common-obj-$(call lnot,$(CONFIG_VNC)) += vnc-stubs.o # ui-sdl module common-obj-$(CONFIG_SDL) += sdl.mo -ifeq ($(CONFIG_SDLABI),1.2) -sdl.mo-objs := sdl.o sdl_zoom.o -endif -ifeq ($(CONFIG_SDLABI),2.0) sdl.mo-objs := sdl2.o sdl2-input.o sdl2-2d.o ifeq ($(CONFIG_OPENGL),y) sdl.mo-objs += sdl2-gl.o endif -endif sdl.mo-cflags := $(SDL_CFLAGS) sdl.mo-libs := $(SDL_LIBS) @@ -51,8 +46,13 @@ endif common-obj-$(CONFIG_CURSES) += curses.mo curses.mo-objs := curses.o -curses.mo-cflags := $(CURSES_CFLAGS) -curses.mo-libs := $(CURSES_LIBS) +curses.mo-cflags := $(CURSES_CFLAGS) $(ICONV_CFLAGS) +curses.mo-libs := $(CURSES_LIBS) $(ICONV_LIBS) + +common-obj-$(call land,$(CONFIG_SPICE),$(CONFIG_GIO)) += spice-app.mo +spice-app.mo-objs := spice-app.o +spice-app.mo-cflags := $(GIO_CFLAGS) +spice-app.mo-libs := $(GIO_LIBS) common-obj-$(CONFIG_OPENGL) += shader.o common-obj-$(CONFIG_OPENGL) += console-gl.o diff --git a/ui/cocoa.m b/ui/cocoa.m index ecf12bfc2e..420b2411c1 100644 --- a/ui/cocoa.m +++ b/ui/cocoa.m @@ -32,7 +32,8 @@ #include "ui/input.h" #include "sysemu/sysemu.h" #include "qapi/error.h" -#include "qapi/qapi-commands.h" +#include "qapi/qapi-commands-block.h" +#include "qapi/qapi-commands-misc.h" #include "sysemu/blockdev.h" #include "qemu-version.h" #include @@ -53,6 +54,9 @@ #ifndef MAC_OS_X_VERSION_10_12 #define MAC_OS_X_VERSION_10_12 101200 #endif +#ifndef MAC_OS_X_VERSION_10_13 +#define MAC_OS_X_VERSION_10_13 101300 +#endif /* macOS 10.12 deprecated many constants, #define the new names for older SDKs */ #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12 @@ -89,6 +93,14 @@ #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 #define NSModalResponseOK NSFileHandlingPanelOKButton #endif +/* 10.14 deprecates NSOnState and NSOffState in favor of + * NSControlStateValueOn/Off, which were introduced in 10.13. + * Define for older versions + */ +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_13 +#define NSControlStateValueOn NSOnState +#define NSControlStateValueOff NSOffState +#endif //#define DEBUG @@ -117,6 +129,40 @@ bool stretch_video; NSTextField *pauseLabel; NSArray * supportedImageFileTypes; +static QemuSemaphore display_init_sem; +static QemuSemaphore app_started_sem; + +// Utility functions to run specified code block with iothread lock held +typedef void (^CodeBlock)(void); +typedef bool (^BoolCodeBlock)(void); + +static void with_iothread_lock(CodeBlock block) +{ + bool locked = qemu_mutex_iothread_locked(); + if (!locked) { + qemu_mutex_lock_iothread(); + } + block(); + if (!locked) { + qemu_mutex_unlock_iothread(); + } +} + +static bool bool_with_iothread_lock(BoolCodeBlock block) +{ + bool locked = qemu_mutex_iothread_locked(); + bool val; + + if (!locked) { + qemu_mutex_lock_iothread(); + } + val = block(); + if (!locked) { + qemu_mutex_unlock_iothread(); + } + return val; +} + // Mac to QKeyCode conversion const int mac_to_qkeycode_map[] = { [kVK_ANSI_A] = Q_KEY_CODE_A, @@ -282,18 +328,20 @@ static void handleAnyDeviceErrors(Error * err) NSWindow *fullScreenWindow; float cx,cy,cw,ch,cdx,cdy; CGDataProviderRef dataProviderRef; + pixman_image_t *pixman_image; BOOL modifiers_state[256]; BOOL isMouseGrabbed; BOOL isFullscreen; BOOL isAbsoluteEnabled; BOOL isMouseDeassociated; } -- (void) switchSurface:(DisplaySurface *)surface; +- (void) switchSurface:(pixman_image_t *)image; - (void) grabMouse; - (void) ungrabMouse; - (void) toggleFullScreen:(id)sender; - (void) handleMonitorInput:(NSEvent *)event; -- (void) handleEvent:(NSEvent *)event; +- (bool) handleEvent:(NSEvent *)event; +- (bool) handleEventLocked:(NSEvent *)event; - (void) setAbsoluteEnabled:(BOOL)tIsAbsoluteEnabled; /* The state surrounding mouse grabbing is potentially confusing. * isAbsoluteEnabled tracks qemu_input_is_absolute() [ie "is the emulated @@ -339,8 +387,10 @@ QemuCocoaView *cocoaView; { COCOA_DEBUG("QemuCocoaView: dealloc\n"); - if (dataProviderRef) + if (dataProviderRef) { CGDataProviderRelease(dataProviderRef); + pixman_image_unref(pixman_image); + } [super dealloc]; } @@ -376,7 +426,12 @@ QemuCocoaView *cocoaView; COCOA_DEBUG("QemuCocoaView: drawRect\n"); // get CoreGraphic context +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_10 CGContextRef viewContextRef = [[NSGraphicsContext currentContext] graphicsPort]; +#else + CGContextRef viewContextRef = [[NSGraphicsContext currentContext] CGContext]; +#endif + CGContextSetInterpolationQuality (viewContextRef, kCGInterpolationNone); CGContextSetShouldAntialias (viewContextRef, NO); @@ -462,12 +517,13 @@ QemuCocoaView *cocoaView; } } -- (void) switchSurface:(DisplaySurface *)surface +- (void) switchSurface:(pixman_image_t *)image { COCOA_DEBUG("QemuCocoaView: switchSurface\n"); - int w = surface_width(surface); - int h = surface_height(surface); + int w = pixman_image_get_width(image); + int h = pixman_image_get_height(image); + pixman_format_code_t image_format = pixman_image_get_format(image); /* cdx == 0 means this is our very first surface, in which case we need * to recalculate the content dimensions even if it happens to be the size * of the initial empty window. @@ -485,14 +541,17 @@ QemuCocoaView *cocoaView; } // update screenBuffer - if (dataProviderRef) + if (dataProviderRef) { CGDataProviderRelease(dataProviderRef); + pixman_image_unref(pixman_image); + } //sync host window color space with guests - screen.bitsPerPixel = surface_bits_per_pixel(surface); - screen.bitsPerComponent = surface_bytes_per_pixel(surface) * 2; + screen.bitsPerPixel = PIXMAN_FORMAT_BPP(image_format); + screen.bitsPerComponent = DIV_ROUND_UP(screen.bitsPerPixel, 8) * 2; - dataProviderRef = CGDataProviderCreateWithData(NULL, surface_data(surface), w * 4 * h, NULL); + pixman_image = image; + dataProviderRef = CGDataProviderCreateWithData(NULL, pixman_image_get_data(image), w * 4 * h, NULL); // update windows if (isFullscreen) { @@ -630,10 +689,17 @@ QemuCocoaView *cocoaView; } } -- (void) handleEvent:(NSEvent *)event +- (bool) handleEvent:(NSEvent *)event { - COCOA_DEBUG("QemuCocoaView: handleEvent\n"); + return bool_with_iothread_lock(^{ + return [self handleEventLocked:event]; + }); +} +- (bool) handleEventLocked:(NSEvent *)event +{ + /* Return true if we handled the event, false if it should be given to OSX */ + COCOA_DEBUG("QemuCocoaView: handleEvent\n"); int buttons = 0; int keycode = 0; bool mouse_event = false; @@ -703,8 +769,7 @@ QemuCocoaView *cocoaView; if (keycode == Q_KEY_CODE_F) { switched_to_fullscreen = true; } - [NSApp sendEvent:event]; - return; + return false; } // default @@ -719,12 +784,12 @@ QemuCocoaView *cocoaView; // enable graphic console case '1' ... '9': console_select(key - '0' - 1); /* ascii math */ - return; + return true; // release the mouse grab case 'g': [self ungrabMouse]; - return; + return true; } } } @@ -741,7 +806,7 @@ QemuCocoaView *cocoaView; // don't pass the guest a spurious key-up if we treated this // command-key combo as a host UI action if (!isMouseGrabbed && ([event modifierFlags] & NSEventModifierFlagCommand)) { - return; + return true; } if (qemu_console_is_graphic(NULL)) { @@ -835,7 +900,7 @@ QemuCocoaView *cocoaView; mouse_event = false; break; default: - [NSApp sendEvent:event]; + return false; } if (mouse_event) { @@ -871,10 +936,11 @@ QemuCocoaView *cocoaView; qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, (int)[event deltaY]); } } else { - [NSApp sendEvent:event]; + return false; } qemu_input_event_sync(); } + return true; } - (void) grabMouse @@ -928,15 +994,18 @@ QemuCocoaView *cocoaView; */ - (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); - } - } + with_iothread_lock(^{ + int index; + + 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 @@ -953,7 +1022,6 @@ QemuCocoaView *cocoaView; #endif { } -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv; - (void)doToggleFullScreen:(id)sender; - (void)toggleFullScreen:(id)sender; - (void)showQEMUDoc:(id)sender; @@ -1041,8 +1109,8 @@ QemuCocoaView *cocoaView; - (void)applicationDidFinishLaunching: (NSNotification *) note { COCOA_DEBUG("QemuCocoaAppController: applicationDidFinishLaunching\n"); - // launch QEMU, with the global args - [self startEmulationWithArgc:gArgc argv:(char **)gArgv]; + /* Tell cocoa_display_init to proceed */ + qemu_sem_post(&app_started_sem); } - (void)applicationWillTerminate:(NSNotification *)aNotification @@ -1085,15 +1153,6 @@ QemuCocoaView *cocoaView; [cocoaView raiseAllKeys]; } -- (void)startEmulationWithArgc:(int)argc argv:(char**)argv -{ - COCOA_DEBUG("QemuCocoaAppController: startEmulationWithArgc\n"); - - int status; - status = qemu_main(argc, argv, *_NSGetEnviron()); - exit(status); -} - /* We abstract the method called by the Enter Fullscreen menu item * because Mac OS 10.7 and higher disables it. This is because of the * menu item's old selector's name toggleFullScreen: @@ -1146,9 +1205,9 @@ QemuCocoaView *cocoaView; { stretch_video = !stretch_video; if (stretch_video == true) { - [sender setState: NSOnState]; + [sender setState: NSControlStateValueOn]; } else { - [sender setState: NSOffState]; + [sender setState: NSControlStateValueOff]; } } @@ -1161,7 +1220,9 @@ QemuCocoaView *cocoaView; /* Pause the guest */ - (void)pauseQEMU:(id)sender { - qmp_stop(NULL); + with_iothread_lock(^{ + qmp_stop(NULL); + }); [sender setEnabled: NO]; [[[sender menu] itemWithTitle: @"Resume"] setEnabled: YES]; [self displayPause]; @@ -1170,7 +1231,9 @@ QemuCocoaView *cocoaView; /* Resume running the guest operating system */ - (void)resumeQEMU:(id) sender { - qmp_cont(NULL); + with_iothread_lock(^{ + qmp_cont(NULL); + }); [sender setEnabled: NO]; [[[sender menu] itemWithTitle: @"Pause"] setEnabled: YES]; [self removePause]; @@ -1198,13 +1261,17 @@ QemuCocoaView *cocoaView; /* Restarts QEMU */ - (void)restartQEMU:(id)sender { - qmp_system_reset(NULL); + with_iothread_lock(^{ + qmp_system_reset(NULL); + }); } /* Powers down QEMU */ - (void)powerDownQEMU:(id)sender { - qmp_system_powerdown(NULL); + with_iothread_lock(^{ + qmp_system_powerdown(NULL); + }); } /* Ejects the media. @@ -1220,9 +1287,11 @@ QemuCocoaView *cocoaView; return; } - Error *err = NULL; - qmp_eject(true, [drive cStringUsingEncoding: NSASCIIStringEncoding], - false, NULL, false, false, &err); + __block Error *err = NULL; + with_iothread_lock(^{ + qmp_eject(true, [drive cStringUsingEncoding: NSASCIIStringEncoding], + false, NULL, false, false, &err); + }); handleAnyDeviceErrors(err); } @@ -1254,16 +1323,18 @@ QemuCocoaView *cocoaView; return; } - Error *err = NULL; - qmp_blockdev_change_medium(true, - [drive cStringUsingEncoding: - NSASCIIStringEncoding], - false, NULL, - [file cStringUsingEncoding: - NSASCIIStringEncoding], - true, "raw", - false, 0, - &err); + __block Error *err = NULL; + with_iothread_lock(^{ + qmp_blockdev_change_medium(true, + [drive cStringUsingEncoding: + NSASCIIStringEncoding], + false, NULL, + [file cStringUsingEncoding: + NSASCIIStringEncoding], + true, "raw", + false, 0, + &err); + }); handleAnyDeviceErrors(err); } } @@ -1389,62 +1460,42 @@ QemuCocoaView *cocoaView; { /* Unselect the currently selected item */ for (NSMenuItem *item in [menu itemArray]) { - if (item.state == NSOnState) { - [item setState: NSOffState]; + if (item.state == NSControlStateValueOn) { + [item setState: NSControlStateValueOff]; break; } } } // check the menu item - [sender setState: NSOnState]; + [sender setState: NSControlStateValueOn]; // get the throttle percentage throttle_pct = [sender tag]; - cpu_throttle_set(throttle_pct); + with_iothread_lock(^{ + cpu_throttle_set(throttle_pct); + }); COCOA_DEBUG("cpu throttling at %d%c\n", cpu_throttle_get_percentage(), '%'); } @end +@interface QemuApplication : NSApplication +@end -int main (int argc, const char * argv[]) { - - gArgc = argc; - gArgv = (char **)argv; - int i; - - /* In case we don't need to display a window, let's not do that */ - for (i = 1; i < argc; i++) { - const char *opt = argv[i]; - - if (opt[0] == '-') { - /* Treat --foo the same as -foo. */ - if (opt[1] == '-') { - opt++; - } - if (!strcmp(opt, "-h") || !strcmp(opt, "-help") || - !strcmp(opt, "-vnc") || - !strcmp(opt, "-nographic") || - !strcmp(opt, "-version") || - !strcmp(opt, "-curses") || - !strcmp(opt, "-display") || - !strcmp(opt, "-qtest")) { - return qemu_main(gArgc, gArgv, *_NSGetEnviron()); - } - } +@implementation QemuApplication +- (void)sendEvent:(NSEvent *)event +{ + COCOA_DEBUG("QemuApplication: sendEvent\n"); + if (![cocoaView handleEvent:event]) { + [super sendEvent: event]; } +} +@end - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - // Pull this console process up to being a fully-fledged graphical - // app with a menubar and Dock icon - ProcessSerialNumber psn = { 0, kCurrentProcess }; - TransformProcessType(&psn, kProcessTransformToForegroundApplication); - - [NSApplication sharedApplication]; - +static void create_initial_menus(void) +{ // Add menus NSMenu *menu; NSMenuItem *menuItem; @@ -1501,7 +1552,7 @@ int main (int argc, const char * argv[]) { initWithTitle: [NSString stringWithFormat: @"%d%%", percentage] action:@selector(adjustSpeed:) keyEquivalent:@""] autorelease]; if (percentage == 100) { - [menuItem setState: NSOnState]; + [menuItem setState: NSControlStateValueOn]; } /* Calculate the throttle percentage */ @@ -1528,97 +1579,8 @@ int main (int argc, const char * argv[]) { menuItem = [[[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""] autorelease]; [menuItem setSubmenu:menu]; [[NSApp mainMenu] addItem:menuItem]; - - // Create an Application controller - QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; - [NSApp setDelegate:appController]; - - // Start the main event loop - [NSApp run]; - - [appController release]; - [pool release]; - - return 0; } - - -#pragma mark qemu -static void cocoa_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) -{ - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); - - NSRect rect; - if ([cocoaView cdx] == 1.0) { - rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); - } else { - rect = NSMakeRect( - x * [cocoaView cdx], - ([cocoaView gscreen].height - y - h) * [cocoaView cdy], - w * [cocoaView cdx], - h * [cocoaView cdy]); - } - [cocoaView setNeedsDisplayInRect:rect]; - - [pool release]; -} - -static void cocoa_switch(DisplayChangeListener *dcl, - DisplaySurface *surface) -{ - NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; - - COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); - [cocoaView switchSurface:surface]; - [pool release]; -} - -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]) { - if ([cocoaView isMouseGrabbed]) { - [cocoaView ungrabMouse]; - } - } - [cocoaView setAbsoluteEnabled:YES]; - } - - NSDate *distantPast; - NSEvent *event; - distantPast = [NSDate distantPast]; - do { - event = [NSApp nextEventMatchingMask:NSEventMaskAny untilDate:distantPast - inMode: NSDefaultRunLoopMode dequeue:YES]; - if (event != nil) { - [cocoaView handleEvent:event]; - } - } while(event != nil); - [pool release]; -} - -static void cocoa_cleanup(void) -{ - COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); - g_free(dcl); -} - -static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "cocoa", - .dpy_gfx_update = cocoa_update, - .dpy_gfx_switch = cocoa_switch, - .dpy_refresh = cocoa_refresh, -}; - /* Returns a name for a given console */ static NSString * getConsoleName(QemuConsole * console) { @@ -1711,14 +1673,191 @@ static void addRemovableDevicesMenuItems(void) qapi_free_BlockInfoList(pointerToFree); } +/* + * The startup process for the OSX/Cocoa UI is complicated, because + * OSX insists that the UI runs on the initial main thread, and so we + * need to start a second thread which runs the vl.c qemu_main(): + * + * Initial thread: 2nd thread: + * in main(): + * create qemu-main thread + * wait on display_init semaphore + * call qemu_main() + * ... + * in cocoa_display_init(): + * post the display_init semaphore + * wait on app_started semaphore + * create application, menus, etc + * enter OSX run loop + * in applicationDidFinishLaunching: + * post app_started semaphore + * tell main thread to fullscreen if needed + * [...] + * run qemu main-loop + * + * We do this in two stages so that we don't do the creation of the + * GUI application menus and so on for command line options like --help + * where we want to just print text to stdout and exit immediately. + */ + +static void *call_qemu_main(void *opaque) +{ + int status; + + COCOA_DEBUG("Second thread: calling qemu_main()\n"); + status = qemu_main(gArgc, gArgv, *_NSGetEnviron()); + COCOA_DEBUG("Second thread: qemu_main() returned, exiting\n"); + exit(status); +} + +int main (int argc, const char * argv[]) { + QemuThread thread; + + COCOA_DEBUG("Entered main()\n"); + gArgc = argc; + gArgv = (char **)argv; + + qemu_sem_init(&display_init_sem, 0); + qemu_sem_init(&app_started_sem, 0); + + qemu_thread_create(&thread, "qemu_main", call_qemu_main, + NULL, QEMU_THREAD_DETACHED); + + COCOA_DEBUG("Main thread: waiting for display_init_sem\n"); + qemu_sem_wait(&display_init_sem); + COCOA_DEBUG("Main thread: initializing app\n"); + + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + // Pull this console process up to being a fully-fledged graphical + // app with a menubar and Dock icon + ProcessSerialNumber psn = { 0, kCurrentProcess }; + TransformProcessType(&psn, kProcessTransformToForegroundApplication); + + [QemuApplication sharedApplication]; + + create_initial_menus(); + + /* + * Create the menu entries which depend on QEMU state (for consoles + * and removeable devices). These make calls back into QEMU functions, + * which is OK because at this point we know that the second thread + * holds the iothread lock and is synchronously waiting for us to + * finish. + */ + add_console_menu_entries(); + addRemovableDevicesMenuItems(); + + // Create an Application controller + QemuCocoaAppController *appController = [[QemuCocoaAppController alloc] init]; + [NSApp setDelegate:appController]; + + // Start the main event loop + COCOA_DEBUG("Main thread: entering OSX run loop\n"); + [NSApp run]; + COCOA_DEBUG("Main thread: left OSX run loop, exiting\n"); + + [appController release]; + [pool release]; + + return 0; +} + + + +#pragma mark qemu +static void cocoa_update(DisplayChangeListener *dcl, + int x, int y, int w, int h) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + + COCOA_DEBUG("qemu_cocoa: cocoa_update\n"); + + dispatch_async(dispatch_get_main_queue(), ^{ + NSRect rect; + if ([cocoaView cdx] == 1.0) { + rect = NSMakeRect(x, [cocoaView gscreen].height - y - h, w, h); + } else { + rect = NSMakeRect( + x * [cocoaView cdx], + ([cocoaView gscreen].height - y - h) * [cocoaView cdy], + w * [cocoaView cdx], + h * [cocoaView cdy]); + } + [cocoaView setNeedsDisplayInRect:rect]; + }); + + [pool release]; +} + +static void cocoa_switch(DisplayChangeListener *dcl, + DisplaySurface *surface) +{ + NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init]; + pixman_image_t *image = surface->image; + + COCOA_DEBUG("qemu_cocoa: cocoa_switch\n"); + + // The DisplaySurface will be freed as soon as this callback returns. + // We take a reference to the underlying pixman image here so it does + // not disappear from under our feet; the switchSurface method will + // deref the old image when it is done with it. + pixman_image_ref(image); + + dispatch_async(dispatch_get_main_queue(), ^{ + [cocoaView switchSurface:image]; + }); + [pool release]; +} + +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()) { + dispatch_async(dispatch_get_main_queue(), ^{ + if (![cocoaView isAbsoluteEnabled]) { + if ([cocoaView isMouseGrabbed]) { + [cocoaView ungrabMouse]; + } + } + [cocoaView setAbsoluteEnabled:YES]; + }); + } + [pool release]; +} + +static void cocoa_cleanup(void) +{ + COCOA_DEBUG("qemu_cocoa: cocoa_cleanup\n"); + g_free(dcl); +} + +static const DisplayChangeListenerOps dcl_ops = { + .dpy_name = "cocoa", + .dpy_gfx_update = cocoa_update, + .dpy_gfx_switch = cocoa_switch, + .dpy_refresh = cocoa_refresh, +}; + static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) { COCOA_DEBUG("qemu_cocoa: cocoa_display_init\n"); + /* Tell main thread to go ahead and create the app and enter the run loop */ + qemu_sem_post(&display_init_sem); + qemu_sem_wait(&app_started_sem); + COCOA_DEBUG("cocoa_display_init: app start completed\n"); + /* if fullscreen mode is to be used */ if (opts->has_full_screen && opts->full_screen) { - [NSApp activateIgnoringOtherApps: YES]; - [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; + dispatch_async(dispatch_get_main_queue(), ^{ + [NSApp activateIgnoringOtherApps: YES]; + [(QemuCocoaAppController *)[[NSApplication sharedApplication] delegate] toggleFullScreen: nil]; + }); } dcl = g_malloc0(sizeof(DisplayChangeListener)); @@ -1729,17 +1868,6 @@ static void cocoa_display_init(DisplayState *ds, DisplayOptions *opts) // register cleanup function atexit(cocoa_cleanup); - - /* At this point QEMU has created all the consoles, so we can add View - * menu entries for them. - */ - add_console_menu_entries(); - - /* Give all removable devices a menu item. - * Has to be called after QEMU has started to - * find out what removable devices it has. - */ - addRemovableDevicesMenuItems(); } static QemuDisplay qemu_display_cocoa = { diff --git a/ui/console.c b/ui/console.c index bc58458ee8..6d2282d3e9 100644 --- a/ui/console.c +++ b/ui/console.c @@ -182,7 +182,7 @@ struct DisplayState { static DisplayState *display_state; static QemuConsole *active_console; -static QTAILQ_HEAD(consoles_head, QemuConsole) consoles = +static QTAILQ_HEAD(, QemuConsole) consoles = QTAILQ_HEAD_INITIALIZER(consoles); static bool cursor_visible_phase; static QEMUTimer *cursor_timer; @@ -1303,7 +1303,7 @@ static QemuConsole *new_console(DisplayState *ds, console_type_t console_type, s->index = 0; QTAILQ_INSERT_TAIL(&consoles, s, next); } else if (console_type != GRAPHIC_CONSOLE || qdev_hotplug) { - QemuConsole *last = QTAILQ_LAST(&consoles, consoles_head); + QemuConsole *last = QTAILQ_LAST(&consoles); s->index = last->index + 1; QTAILQ_INSERT_TAIL(&consoles, s, next); } else { @@ -1385,42 +1385,6 @@ DisplaySurface *qemu_create_displaysurface_pixman(pixman_image_t *image) return surface; } -static void qemu_unmap_displaysurface_guestmem(pixman_image_t *image, - void *unused) -{ - void *data = pixman_image_get_data(image); - uint32_t size = pixman_image_get_stride(image) * - pixman_image_get_height(image); - cpu_physical_memory_unmap(data, size, 0, 0); -} - -DisplaySurface *qemu_create_displaysurface_guestmem(int width, int height, - pixman_format_code_t format, - int linesize, uint64_t addr) -{ - DisplaySurface *surface; - hwaddr size; - void *data; - - if (linesize == 0) { - linesize = width * PIXMAN_FORMAT_BPP(format) / 8; - } - - size = (hwaddr)linesize * height; - data = cpu_physical_memory_map(addr, &size, 0); - if (size != (hwaddr)linesize * height) { - cpu_physical_memory_unmap(data, size, 0, 0); - return NULL; - } - - surface = qemu_create_displaysurface_from - (width, height, format, linesize, data); - pixman_image_set_destroy_function - (surface->image, qemu_unmap_displaysurface_guestmem, NULL); - - return surface; -} - DisplaySurface *qemu_create_message_surface(int w, int h, const char *msg) { @@ -2319,7 +2283,7 @@ bool qemu_display_find_default(DisplayOptions *opts) for (i = 0; i < ARRAY_SIZE(prio); i++) { if (dpys[prio[i]] == NULL) { - ui_module_load_one(DisplayType_lookup.array[prio[i]]); + ui_module_load_one(DisplayType_str(prio[i])); } if (dpys[prio[i]] == NULL) { continue; @@ -2337,11 +2301,11 @@ void qemu_display_early_init(DisplayOptions *opts) return; } if (dpys[opts->type] == NULL) { - ui_module_load_one(DisplayType_lookup.array[opts->type]); + ui_module_load_one(DisplayType_str(opts->type)); } if (dpys[opts->type] == NULL) { error_report("Display '%s' is not available.", - DisplayType_lookup.array[opts->type]); + DisplayType_str(opts->type)); exit(1); } if (dpys[opts->type]->early_init) { diff --git a/ui/curses.c b/ui/curses.c index 59d819fd4d..fb63945188 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -27,7 +27,12 @@ #include #include #endif +#include +#include +#include +#include +#include "qapi/error.h" #include "qemu-common.h" #include "ui/console.h" #include "ui/input.h" @@ -41,31 +46,42 @@ #define FONT_HEIGHT 16 #define FONT_WIDTH 8 +enum maybe_keycode { + CURSES_KEYCODE, + CURSES_CHAR, + CURSES_CHAR_OR_KEYCODE, +}; + static DisplayChangeListener *dcl; static console_ch_t screen[160 * 100]; static WINDOW *screenpad = NULL; static int width, height, gwidth, gheight, invalidate; static int px, py, sminx, sminy, smaxx, smaxy; -static chtype vga_to_curses[256]; +static const char *font_charset = "CP437"; +static cchar_t vga_to_curses[256]; static void curses_update(DisplayChangeListener *dcl, int x, int y, int w, int h) { console_ch_t *line; - chtype curses_line[width]; + cchar_t curses_line[width]; line = screen + y * width; for (h += y; y < h; y ++, line += width) { for (x = 0; x < width; x++) { chtype ch = line[x] & 0xff; chtype at = line[x] & ~0xff; - if (vga_to_curses[ch]) { - ch = vga_to_curses[ch]; + if (vga_to_curses[ch].chars[0]) { + curses_line[x] = vga_to_curses[ch]; + } else { + curses_line[x] = (cchar_t) { + .chars[0] = ch, + }; } - curses_line[x] = ch | at; + curses_line[x].attr |= at; } - mvwaddchnstr(screenpad, y, 0, curses_line, width); + mvwadd_wchnstr(screenpad, y, 0, curses_line, width); } pnoutrefresh(screenpad, py, px, sminy, sminx, smaxy - 1, smaxx - 1); @@ -193,9 +209,54 @@ static void curses_cursor_position(DisplayChangeListener *dcl, static kbd_layout_t *kbd_layout = NULL; +static wint_t console_getch(enum maybe_keycode *maybe_keycode) +{ + wint_t ret; + switch (get_wch(&ret)) { + case KEY_CODE_YES: + *maybe_keycode = CURSES_KEYCODE; + break; + case OK: + *maybe_keycode = CURSES_CHAR; + break; + case ERR: + ret = -1; + break; + } + return ret; +} + +static int curses2foo(const int _curses2foo[], const int _curseskey2foo[], + int chr, enum maybe_keycode maybe_keycode) +{ + int ret = -1; + if (maybe_keycode == CURSES_CHAR) { + if (chr < CURSES_CHARS) { + ret = _curses2foo[chr]; + } + } else { + if (chr < CURSES_KEYS) { + ret = _curseskey2foo[chr]; + } + if (ret == -1 && maybe_keycode == CURSES_CHAR_OR_KEYCODE && + chr < CURSES_CHARS) { + ret = _curses2foo[chr]; + } + } + return ret; +} + +#define curses2keycode(chr, maybe_keycode) \ + curses2foo(_curses2keycode, _curseskey2keycode, chr, maybe_keycode) +#define curses2keysym(chr, maybe_keycode) \ + curses2foo(_curses2keysym, _curseskey2keysym, chr, maybe_keycode) +#define curses2qemu(chr, maybe_keycode) \ + curses2foo(_curses2qemu, _curseskey2qemu, chr, maybe_keycode) + static void curses_refresh(DisplayChangeListener *dcl) { int chr, keysym, keycode, keycode_alt; + enum maybe_keycode maybe_keycode; curses_winch_check(); @@ -211,14 +272,14 @@ static void curses_refresh(DisplayChangeListener *dcl) while (1) { /* while there are any pending key strokes to process */ - chr = getch(); + chr = console_getch(&maybe_keycode); - if (chr == ERR) + if (chr == -1) break; #ifdef KEY_RESIZE /* this shouldn't occur when we use a custom SIGWINCH handler */ - if (chr == KEY_RESIZE) { + if (maybe_keycode != CURSES_CHAR && chr == KEY_RESIZE) { clear(); refresh(); curses_calc_pad(); @@ -227,17 +288,19 @@ static void curses_refresh(DisplayChangeListener *dcl) } #endif - keycode = curses2keycode[chr]; + keycode = curses2keycode(chr, maybe_keycode); keycode_alt = 0; - /* alt key */ + /* alt or esc key */ if (keycode == 1) { - int nextchr = getch(); + enum maybe_keycode next_maybe_keycode; + int nextchr = console_getch(&next_maybe_keycode); - if (nextchr != ERR) { + if (nextchr != -1) { chr = nextchr; + maybe_keycode = next_maybe_keycode; keycode_alt = ALT; - keycode = curses2keycode[chr]; + keycode = curses2keycode(chr, maybe_keycode); if (keycode != -1) { keycode |= ALT; @@ -257,9 +320,7 @@ static void curses_refresh(DisplayChangeListener *dcl) } if (kbd_layout) { - keysym = -1; - if (chr < CURSES_KEYS) - keysym = curses2keysym[chr]; + keysym = curses2keysym(chr, maybe_keycode); if (keysym == -1) { if (chr < ' ') { @@ -272,7 +333,7 @@ static void curses_refresh(DisplayChangeListener *dcl) } keycode = keysym2scancode(kbd_layout, keysym & KEYSYM_MASK, - false, false, false); + NULL, false); if (keycode == 0) continue; @@ -325,10 +386,7 @@ static void curses_refresh(DisplayChangeListener *dcl) qemu_input_event_send_key_delay(0); } } else { - keysym = -1; - if (chr < CURSES_KEYS) { - keysym = curses2qemu[chr]; - } + keysym = curses2qemu(chr, maybe_keycode); if (keysym == -1) keysym = chr; @@ -342,6 +400,260 @@ static void curses_atexit(void) endwin(); } +/* Setup wchar glyph for one UCS-2 char */ +static void convert_ucs(int glyph, uint16_t ch, iconv_t conv) +{ + wchar_t wch; + char *pch, *pwch; + size_t sch, swch; + + pch = (char *) &ch; + pwch = (char *) &wch; + sch = sizeof(ch); + swch = sizeof(wch); + + if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) { + fprintf(stderr, "Could not convert 0x%04x from UCS-2 to WCHAR_T: %s\n", + ch, strerror(errno)); + } else { + vga_to_curses[glyph].chars[0] = wch; + } +} + +/* Setup wchar glyph for one font character */ +static void convert_font(unsigned char ch, iconv_t conv) +{ + wchar_t wch; + char *pch, *pwch; + size_t sch, swch; + + pch = (char *) &ch; + pwch = (char *) &wch; + sch = sizeof(ch); + swch = sizeof(wch); + + if (iconv(conv, &pch, &sch, &pwch, &swch) == (size_t) -1) { + fprintf(stderr, "Could not convert 0x%02x from %s to WCHAR_T: %s\n", + ch, font_charset, strerror(errno)); + } else { + vga_to_curses[ch].chars[0] = wch; + } +} + +/* Convert one wchar to UCS-2 */ +static uint16_t get_ucs(wchar_t wch, iconv_t conv) +{ + uint16_t ch; + char *pch, *pwch; + size_t sch, swch; + + pch = (char *) &ch; + pwch = (char *) &wch; + sch = sizeof(ch); + swch = sizeof(wch); + + if (iconv(conv, &pwch, &swch, &pch, &sch) == (size_t) -1) { + fprintf(stderr, "Could not convert 0x%02lx from WCHAR_T to UCS-2: %s\n", + (unsigned long)wch, strerror(errno)); + return 0xFFFD; + } + + return ch; +} + +/* + * Setup mapping for vga to curses line graphics. + */ +static void font_setup(void) +{ + /* + * Control characters are normally non-printable, but VGA does have + * well-known glyphs for them. + */ + static uint16_t control_characters[0x20] = { + 0x0020, + 0x263a, + 0x263b, + 0x2665, + 0x2666, + 0x2663, + 0x2660, + 0x2022, + 0x25d8, + 0x25cb, + 0x25d9, + 0x2642, + 0x2640, + 0x266a, + 0x266b, + 0x263c, + 0x25ba, + 0x25c4, + 0x2195, + 0x203c, + 0x00b6, + 0x00a7, + 0x25ac, + 0x21a8, + 0x2191, + 0x2193, + 0x2192, + 0x2190, + 0x221f, + 0x2194, + 0x25b2, + 0x25bc + }; + + iconv_t ucs_to_wchar_conv; + iconv_t wchar_to_ucs_conv; + iconv_t font_conv; + int i; + + ucs_to_wchar_conv = iconv_open("WCHAR_T", "UCS-2"); + if (ucs_to_wchar_conv == (iconv_t) -1) { + fprintf(stderr, "Could not convert font glyphs from UCS-2: '%s'\n", + strerror(errno)); + exit(1); + } + + wchar_to_ucs_conv = iconv_open("UCS-2", "WCHAR_T"); + if (wchar_to_ucs_conv == (iconv_t) -1) { + iconv_close(ucs_to_wchar_conv); + fprintf(stderr, "Could not convert font glyphs to UCS-2: '%s'\n", + strerror(errno)); + exit(1); + } + + font_conv = iconv_open("WCHAR_T", font_charset); + if (font_conv == (iconv_t) -1) { + iconv_close(ucs_to_wchar_conv); + iconv_close(wchar_to_ucs_conv); + fprintf(stderr, "Could not convert font glyphs from %s: '%s'\n", + font_charset, strerror(errno)); + exit(1); + } + + /* Control characters */ + for (i = 0; i <= 0x1F; i++) { + convert_ucs(i, control_characters[i], ucs_to_wchar_conv); + } + + for (i = 0x20; i <= 0xFF; i++) { + convert_font(i, font_conv); + } + + /* DEL */ + convert_ucs(0x7F, 0x2302, ucs_to_wchar_conv); + + if (strcmp(nl_langinfo(CODESET), "UTF-8")) { + /* Non-Unicode capable, use termcap equivalents for those available */ + for (i = 0; i <= 0xFF; i++) { + switch (get_ucs(vga_to_curses[i].chars[0], wchar_to_ucs_conv)) { + case 0x00a3: + vga_to_curses[i] = *WACS_STERLING; + break; + case 0x2591: + vga_to_curses[i] = *WACS_BOARD; + break; + case 0x2592: + vga_to_curses[i] = *WACS_CKBOARD; + break; + case 0x2502: + vga_to_curses[i] = *WACS_VLINE; + break; + case 0x2524: + vga_to_curses[i] = *WACS_RTEE; + break; + case 0x2510: + vga_to_curses[i] = *WACS_URCORNER; + break; + case 0x2514: + vga_to_curses[i] = *WACS_LLCORNER; + break; + case 0x2534: + vga_to_curses[i] = *WACS_BTEE; + break; + case 0x252c: + vga_to_curses[i] = *WACS_TTEE; + break; + case 0x251c: + vga_to_curses[i] = *WACS_LTEE; + break; + case 0x2500: + vga_to_curses[i] = *WACS_HLINE; + break; + case 0x253c: + vga_to_curses[i] = *WACS_PLUS; + break; + case 0x256c: + vga_to_curses[i] = *WACS_LANTERN; + break; + case 0x256a: + vga_to_curses[i] = *WACS_NEQUAL; + break; + case 0x2518: + vga_to_curses[i] = *WACS_LRCORNER; + break; + case 0x250c: + vga_to_curses[i] = *WACS_ULCORNER; + break; + case 0x2588: + vga_to_curses[i] = *WACS_BLOCK; + break; + case 0x03c0: + vga_to_curses[i] = *WACS_PI; + break; + case 0x00b1: + vga_to_curses[i] = *WACS_PLMINUS; + break; + case 0x2265: + vga_to_curses[i] = *WACS_GEQUAL; + break; + case 0x2264: + vga_to_curses[i] = *WACS_LEQUAL; + break; + case 0x00b0: + vga_to_curses[i] = *WACS_DEGREE; + break; + case 0x25a0: + vga_to_curses[i] = *WACS_BULLET; + break; + case 0x2666: + vga_to_curses[i] = *WACS_DIAMOND; + break; + case 0x2192: + vga_to_curses[i] = *WACS_RARROW; + break; + case 0x2190: + vga_to_curses[i] = *WACS_LARROW; + break; + case 0x2191: + vga_to_curses[i] = *WACS_UARROW; + break; + case 0x2193: + vga_to_curses[i] = *WACS_DARROW; + break; + case 0x23ba: + vga_to_curses[i] = *WACS_S1; + break; + case 0x23bb: + vga_to_curses[i] = *WACS_S3; + break; + case 0x23bc: + vga_to_curses[i] = *WACS_S7; + break; + case 0x23bd: + vga_to_curses[i] = *WACS_S9; + break; + } + } + } + iconv_close(ucs_to_wchar_conv); + iconv_close(wchar_to_ucs_conv); + iconv_close(font_conv); +} + static void curses_setup(void) { int i, colour_default[8] = { @@ -360,6 +672,7 @@ static void curses_setup(void) initscr(); noecho(); intrflush(stdscr, FALSE); nodelay(stdscr, TRUE); nonl(); keypad(stdscr, TRUE); start_color(); raw(); scrollok(stdscr, FALSE); + set_escdelay(25); /* Make color pair to match color format (3bits bg:3bits fg) */ for (i = 0; i < 64; i++) { @@ -370,47 +683,7 @@ static void curses_setup(void) 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[0x18] = ACS_UARROW; - vga_to_curses[0x19] = ACS_DARROW; - vga_to_curses[0x1a] = ACS_RARROW; - vga_to_curses[0x1b] = ACS_LARROW; - 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; + font_setup(); } static void curses_keyboard_setup(void) @@ -421,9 +694,8 @@ static void curses_keyboard_setup(void) keyboard_layout = "en-us"; #endif if(keyboard_layout) { - kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); - if (!kbd_layout) - exit(1); + kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout, + &error_fatal); } } @@ -444,6 +716,10 @@ static void curses_display_init(DisplayState *ds, DisplayOptions *opts) } #endif + setlocale(LC_CTYPE, ""); + if (opts->u.curses.charset) { + font_charset = opts->u.curses.charset; + } curses_setup(); curses_keyboard_setup(); atexit(curses_atexit); diff --git a/ui/curses_keys.h b/ui/curses_keys.h index e9195a1671..71e04acdc7 100644 --- a/ui/curses_keys.h +++ b/ui/curses_keys.h @@ -49,22 +49,28 @@ /* curses won't detect a Control + Alt + 1, so use Alt + 1 */ #define QEMU_KEY_CONSOLE0 (2 | ALT) /* (curses2keycode['1'] | ALT) */ +#define CURSES_CHARS 0x100 /* Support latin1 only */ #define CURSES_KEYS KEY_MAX /* KEY_MAX defined in */ -static const int curses2keysym[CURSES_KEYS] = { - [0 ... (CURSES_KEYS - 1)] = -1, +static const int _curses2keysym[CURSES_CHARS] = { + [0 ... (CURSES_CHARS - 1)] = -1, [0x7f] = KEY_BACKSPACE, ['\r'] = KEY_ENTER, ['\n'] = KEY_ENTER, [27] = 27, +}; + +static const int _curseskey2keysym[CURSES_KEYS] = { + [0 ... (CURSES_KEYS - 1)] = -1, + [KEY_BTAB] = '\t' | KEYSYM_SHIFT, [KEY_SPREVIOUS] = KEY_PPAGE | KEYSYM_SHIFT, [KEY_SNEXT] = KEY_NPAGE | KEYSYM_SHIFT, }; -static const int curses2keycode[CURSES_KEYS] = { - [0 ... (CURSES_KEYS - 1)] = -1, +static const int _curses2keycode[CURSES_CHARS] = { + [0 ... (CURSES_CHARS - 1)] = -1, [0x01b] = 1, /* Escape */ ['1'] = 2, @@ -80,7 +86,6 @@ static const int curses2keycode[CURSES_KEYS] = { ['-'] = 12, ['='] = 13, [0x07f] = 14, /* Backspace */ - [KEY_BACKSPACE] = 14, /* Backspace */ ['\t'] = 15, /* Tab */ ['q'] = 16, @@ -97,7 +102,6 @@ static const int curses2keycode[CURSES_KEYS] = { [']'] = 27, ['\n'] = 28, /* Return */ ['\r'] = 28, /* Return */ - [KEY_ENTER] = 28, /* Return */ ['a'] = 30, ['s'] = 31, @@ -126,33 +130,6 @@ static const int curses2keycode[CURSES_KEYS] = { [' '] = 57, - [KEY_F(1)] = 59, /* Function Key 1 */ - [KEY_F(2)] = 60, /* Function Key 2 */ - [KEY_F(3)] = 61, /* Function Key 3 */ - [KEY_F(4)] = 62, /* Function Key 4 */ - [KEY_F(5)] = 63, /* Function Key 5 */ - [KEY_F(6)] = 64, /* Function Key 6 */ - [KEY_F(7)] = 65, /* Function Key 7 */ - [KEY_F(8)] = 66, /* Function Key 8 */ - [KEY_F(9)] = 67, /* Function Key 9 */ - [KEY_F(10)] = 68, /* Function Key 10 */ - [KEY_F(11)] = 87, /* Function Key 11 */ - [KEY_F(12)] = 88, /* Function Key 12 */ - - [KEY_HOME] = 71 | GREY, /* Home */ - [KEY_UP] = 72 | GREY, /* Up Arrow */ - [KEY_PPAGE] = 73 | GREY, /* Page Up */ - [KEY_LEFT] = 75 | GREY, /* Left Arrow */ - [KEY_RIGHT] = 77 | GREY, /* Right Arrow */ - [KEY_END] = 79 | GREY, /* End */ - [KEY_DOWN] = 80 | GREY, /* Down Arrow */ - [KEY_NPAGE] = 81 | GREY, /* Page Down */ - [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, @@ -166,7 +143,6 @@ static const int curses2keycode[CURSES_KEYS] = { ['_'] = 12 | SHIFT, ['+'] = 13 | SHIFT, - [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */ ['Q'] = 16 | SHIFT, ['W'] = 17 | SHIFT, ['E'] = 18 | SHIFT, @@ -205,19 +181,6 @@ static const int curses2keycode[CURSES_KEYS] = { ['>'] = 52 | SHIFT, ['?'] = 53 | SHIFT, - [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */ - [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */ - [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */ - [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */ - [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */ - [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */ - [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */ - [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */ - [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */ - [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */ - [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */ - [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */ - ['Q' - '@'] = 16 | CNTRL, /* Control + q */ ['W' - '@'] = 17 | CNTRL, /* Control + w */ ['E' - '@'] = 18 | CNTRL, /* Control + e */ @@ -249,13 +212,67 @@ static const int curses2keycode[CURSES_KEYS] = { }; -static const int curses2qemu[CURSES_KEYS] = { +static const int _curseskey2keycode[CURSES_KEYS] = { [0 ... (CURSES_KEYS - 1)] = -1, + [KEY_BACKSPACE] = 14, /* Backspace */ + + [KEY_ENTER] = 28, /* Return */ + + [KEY_F(1)] = 59, /* Function Key 1 */ + [KEY_F(2)] = 60, /* Function Key 2 */ + [KEY_F(3)] = 61, /* Function Key 3 */ + [KEY_F(4)] = 62, /* Function Key 4 */ + [KEY_F(5)] = 63, /* Function Key 5 */ + [KEY_F(6)] = 64, /* Function Key 6 */ + [KEY_F(7)] = 65, /* Function Key 7 */ + [KEY_F(8)] = 66, /* Function Key 8 */ + [KEY_F(9)] = 67, /* Function Key 9 */ + [KEY_F(10)] = 68, /* Function Key 10 */ + [KEY_F(11)] = 87, /* Function Key 11 */ + [KEY_F(12)] = 88, /* Function Key 12 */ + + [KEY_HOME] = 71 | GREY, /* Home */ + [KEY_UP] = 72 | GREY, /* Up Arrow */ + [KEY_PPAGE] = 73 | GREY, /* Page Up */ + [KEY_LEFT] = 75 | GREY, /* Left Arrow */ + [KEY_RIGHT] = 77 | GREY, /* Right Arrow */ + [KEY_END] = 79 | GREY, /* End */ + [KEY_DOWN] = 80 | GREY, /* Down Arrow */ + [KEY_NPAGE] = 81 | GREY, /* Page Down */ + [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 */ + + [KEY_BTAB] = 15 | SHIFT, /* Shift + Tab */ + + [KEY_F(13)] = 59 | SHIFT, /* Shift + Function Key 1 */ + [KEY_F(14)] = 60 | SHIFT, /* Shift + Function Key 2 */ + [KEY_F(15)] = 61 | SHIFT, /* Shift + Function Key 3 */ + [KEY_F(16)] = 62 | SHIFT, /* Shift + Function Key 4 */ + [KEY_F(17)] = 63 | SHIFT, /* Shift + Function Key 5 */ + [KEY_F(18)] = 64 | SHIFT, /* Shift + Function Key 6 */ + [KEY_F(19)] = 65 | SHIFT, /* Shift + Function Key 7 */ + [KEY_F(20)] = 66 | SHIFT, /* Shift + Function Key 8 */ + [KEY_F(21)] = 67 | SHIFT, /* Shift + Function Key 9 */ + [KEY_F(22)] = 68 | SHIFT, /* Shift + Function Key 10 */ + [KEY_F(23)] = 69 | SHIFT, /* Shift + Function Key 11 */ + [KEY_F(24)] = 70 | SHIFT, /* Shift + Function Key 12 */ +}; + +static const int _curses2qemu[CURSES_CHARS] = { + [0 ... (CURSES_CHARS - 1)] = -1, + ['\n'] = '\n', ['\r'] = '\n', [0x07f] = QEMU_KEY_BACKSPACE, +}; + +static const int _curseskey2qemu[CURSES_KEYS] = { + [0 ... (CURSES_KEYS - 1)] = -1, [KEY_DOWN] = QEMU_KEY_DOWN, [KEY_UP] = QEMU_KEY_UP, diff --git a/ui/cursor.c b/ui/cursor.c index f3da0cee79..26ce69fe5e 100644 --- a/ui/cursor.c +++ b/ui/cursor.c @@ -128,13 +128,25 @@ void cursor_set_mono(QEMUCursor *c, uint32_t *data = c->data; uint8_t bit; int x,y,bpl; + bool expand_bitmap_only = image == mask; + bool has_inverted_colors = false; + const uint32_t inverted = 0x80000000; + /* + * Converts a monochrome bitmap with XOR mask 'image' and AND mask 'mask': + * https://docs.microsoft.com/en-us/windows-hardware/drivers/display/drawing-monochrome-pointers + */ bpl = cursor_get_mono_bpl(c); for (y = 0; y < c->height; y++) { bit = 0x80; for (x = 0; x < c->width; x++, data++) { if (transparent && mask[x/8] & bit) { - *data = 0x00000000; + if (!expand_bitmap_only && image[x / 8] & bit) { + *data = inverted; + has_inverted_colors = true; + } else { + *data = 0x00000000; + } } else if (!transparent && !(mask[x/8] & bit)) { *data = 0x00000000; } else if (image[x/8] & bit) { @@ -150,6 +162,32 @@ void cursor_set_mono(QEMUCursor *c, mask += bpl; image += bpl; } + + /* + * If there are any pixels with inverted colors, create an outline (fill + * transparent neighbors with the background color) and use the foreground + * color as "inverted" color. + */ + if (has_inverted_colors) { + data = c->data; + for (y = 0; y < c->height; y++) { + for (x = 0; x < c->width; x++, data++) { + if (*data == 0 /* transparent */ && + ((x > 0 && data[-1] == inverted) || + (x + 1 < c->width && data[1] == inverted) || + (y > 0 && data[-c->width] == inverted) || + (y + 1 < c->height && data[c->width] == inverted))) { + *data = 0xff000000 | background; + } + } + } + data = c->data; + for (x = 0; x < c->width * c->height; x++, data++) { + if (*data == inverted) { + *data = 0xff000000 | foreground; + } + } + } } void cursor_get_mono_image(QEMUCursor *c, int foreground, uint8_t *image) diff --git a/ui/egl-headless.c b/ui/egl-headless.c index 42a41310b0..e67b47aeff 100644 --- a/ui/egl-headless.c +++ b/ui/egl-headless.c @@ -38,6 +38,14 @@ static void egl_gfx_switch(DisplayChangeListener *dcl, edpy->ds = new_surface; } +static QEMUGLContext egl_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 egl_scanout_disable(DisplayChangeListener *dcl) { egl_dpy *edpy = container_of(dcl, egl_dpy, dcl); @@ -134,7 +142,8 @@ static void egl_scanout_flush(DisplayChangeListener *dcl, egl_texture_blit(edpy->gls, &edpy->blit_fb, &edpy->guest_fb, !edpy->y_0_top); egl_texture_blend(edpy->gls, &edpy->blit_fb, &edpy->cursor_fb, - !edpy->y_0_top, edpy->pos_x, edpy->pos_y); + !edpy->y_0_top, edpy->pos_x, edpy->pos_y, + 1.0, 1.0); } else { /* no cursor -> use simple framebuffer blit */ egl_fb_blit(&edpy->blit_fb, &edpy->guest_fb, edpy->y_0_top); @@ -150,7 +159,7 @@ static const DisplayChangeListenerOps egl_ops = { .dpy_gfx_update = egl_gfx_update, .dpy_gfx_switch = egl_gfx_switch, - .dpy_gl_ctx_create = qemu_egl_create_context, + .dpy_gl_ctx_create = egl_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, @@ -176,7 +185,7 @@ static void egl_headless_init(DisplayState *ds, DisplayOptions *opts) egl_dpy *edpy; int idx; - if (egl_rendernode_init(NULL, mode) < 0) { + if (egl_rendernode_init(opts->u.egl_headless.rendernode, mode) < 0) { error_report("egl: render node init failed"); exit(1); } diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 71b6a97bd1..e90eef8c9c 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -15,9 +15,7 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" -#include -#include - +#include "qemu/drm.h" #include "qemu/error-report.h" #include "ui/console.h" #include "ui/egl-helpers.h" @@ -122,14 +120,15 @@ void egl_texture_blit(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip) } void egl_texture_blend(QemuGLShader *gls, egl_fb *dst, egl_fb *src, bool flip, - int x, int y) + int x, int y, double scale_x, double scale_y) { glBindFramebuffer(GL_FRAMEBUFFER_EXT, dst->framebuffer); + int w = scale_x * src->width; + int h = scale_y * src->height; if (flip) { - glViewport(x, y, src->width, src->height); + glViewport(x, y, w, h); } else { - glViewport(x, dst->height - src->height - y, - src->width, src->height); + glViewport(x, dst->height - h - y, w, h); } glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, src->texture); @@ -147,57 +146,12 @@ int qemu_egl_rn_fd; struct gbm_device *qemu_egl_rn_gbm_dev; EGLContext qemu_egl_rn_ctx; -static int qemu_egl_rendernode_open(const char *rendernode) -{ - DIR *dir; - struct dirent *e; - int r, fd; - char *p; - - if (rendernode) { - return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); - } - - 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; - } - - p = g_strdup_printf("/dev/dri/%s", e->d_name); - - r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); - if (r < 0) { - g_free(p); - continue; - } - fd = r; - g_free(p); - break; - } - - closedir(dir); - if (fd < 0) { - return -1; - } - return fd; -} - int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) { qemu_egl_rn_fd = -1; int rc; - qemu_egl_rn_fd = qemu_egl_rendernode_open(rendernode); + qemu_egl_rn_fd = qemu_drm_rendernode_open(rendernode); if (qemu_egl_rn_fd == -1) { error_report("egl: no drm render node available"); goto err; @@ -320,14 +274,14 @@ void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf) /* ---------------------------------------------------------------------- */ -EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, Window win) +EGLSurface qemu_egl_init_surface_x11(EGLContext ectx, EGLNativeWindowType win) { EGLSurface esurface; EGLBoolean b; esurface = eglCreateWindowSurface(qemu_egl_display, qemu_egl_config, - (EGLNativeWindowType)win, NULL); + win, NULL); if (esurface == EGL_NO_SURFACE) { error_report("egl: eglCreateWindowSurface failed"); return NULL; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index fb00ad12ec..42801b688b 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -48,17 +48,14 @@ void gd_egl_init(VirtualConsole *vc) return; } -#if GTK_CHECK_VERSION(3, 0, 0) Window x11_window = gdk_x11_window_get_xid(gdk_window); -#else - Window x11_window = gdk_x11_drawable_get_xid(gdk_window); -#endif if (!x11_window) { return; } vc->gfx.ectx = qemu_egl_init_ctx(); - vc->gfx.esurface = qemu_egl_init_surface_x11(vc->gfx.ectx, x11_window); + vc->gfx.esurface = qemu_egl_init_surface_x11 + (vc->gfx.ectx, (EGLNativeWindowType)x11_window); assert(vc->gfx.esurface); } @@ -72,8 +69,15 @@ void gd_egl_draw(VirtualConsole *vc) return; } + window = gtk_widget_get_window(vc->gfx.drawing_area); + ww = gdk_window_get_width(window); + wh = gdk_window_get_height(window); + if (vc->gfx.scanout_mode) { gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); + + vc->gfx.scale_x = (double)ww / vc->gfx.w; + vc->gfx.scale_y = (double)wh / vc->gfx.h; } else { if (!vc->gfx.ds) { return; @@ -81,12 +85,13 @@ void gd_egl_draw(VirtualConsole *vc) 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); eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); + + vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); + vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); } } @@ -235,8 +240,8 @@ void gd_egl_cursor_position(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - vc->gfx.cursor_x = pos_x; - vc->gfx.cursor_y = pos_y; + vc->gfx.cursor_x = pos_x * vc->gfx.scale_x; + vc->gfx.cursor_y = pos_y * vc->gfx.scale_y; } void gd_egl_release_dmabuf(DisplayChangeListener *dcl, @@ -265,14 +270,16 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); window = gtk_widget_get_window(vc->gfx.drawing_area); - gdk_drawable_get_size(window, &ww, &wh); + ww = gdk_window_get_width(window); + wh = gdk_window_get_height(window); egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); if (vc->gfx.cursor_fb.texture) { egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, vc->gfx.y0_top); egl_texture_blend(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.cursor_fb, vc->gfx.y0_top, - vc->gfx.cursor_x, vc->gfx.cursor_y); + vc->gfx.cursor_x, vc->gfx.cursor_y, + vc->gfx.scale_x, vc->gfx.scale_y); } else { egl_fb_blit(&vc->gfx.win_fb, &vc->gfx.guest_fb, !vc->gfx.y0_top); } diff --git a/ui/gtk.c b/ui/gtk.c index 5cce6ed42d..e96e15435a 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -6,29 +6,25 @@ * Authors: * Anthony Liguori * - * 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 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. * - * Portions from gtk-vnc: + * 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 . + * + * Portions from gtk-vnc (originally licensed under the LGPL v2+): * * GTK VNC Widget * * Copyright (C) 2006 Anthony Liguori * Copyright (C) 2009-2010 Daniel P. Berrange - * - * 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, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define GETTEXT_PACKAGE "qemu" @@ -111,49 +107,6 @@ # define VTE_CHECK_VERSION(a, b, c) 0 #endif -#if defined(CONFIG_VTE) && !GTK_CHECK_VERSION(3, 0, 0) -/* - * The gtk2 vte terminal widget seriously messes up the window resize - * for some reason. You basically can't make the qemu window smaller - * any more because the toplevel window geoemtry hints are overridden. - * - * Workaround that by hiding all vte widgets, except the one in the - * current tab. - * - * Luckily everything works smooth in gtk3. - */ -# define VTE_RESIZE_HACK 1 -#endif - -#if !GTK_CHECK_VERSION(2, 20, 0) -#define gtk_widget_get_realized(widget) GTK_WIDGET_REALIZED(widget) -#endif - -#ifndef GDK_IS_X11_DISPLAY -#define GDK_IS_X11_DISPLAY(dpy) (dpy == dpy) -#endif -#ifndef GDK_IS_WAYLAND_DISPLAY -#define GDK_IS_WAYLAND_DISPLAY(dpy) (dpy == dpy) -#endif -#ifndef GDK_IS_WIN32_DISPLAY -#define GDK_IS_WIN32_DISPLAY(dpy) (dpy == dpy) -#endif - -#if !GTK_CHECK_VERSION(2, 22, 0) -#define GDK_KEY_0 GDK_0 -#define GDK_KEY_1 GDK_1 -#define GDK_KEY_2 GDK_2 -#define GDK_KEY_f GDK_f -#define GDK_KEY_g GDK_g -#define GDK_KEY_m GDK_m -#define GDK_KEY_q GDK_q -#define GDK_KEY_plus GDK_plus -#define GDK_KEY_equal GDK_equal -#define GDK_KEY_minus GDK_minus -#define GDK_KEY_Pause GDK_Pause -#define GDK_KEY_Delete GDK_Delete -#endif - /* Some older mingw versions lack this constant or have * it conditionally defined */ #ifdef _WIN32 @@ -165,17 +118,6 @@ #define HOTKEY_MODIFIERS (GDK_CONTROL_MASK | GDK_MOD1_MASK) -static const int modifier_keycode[] = { - Q_KEY_CODE_SHIFT, - Q_KEY_CODE_SHIFT_R, - Q_KEY_CODE_CTRL, - Q_KEY_CODE_CTRL_R, - Q_KEY_CODE_ALT, - Q_KEY_CODE_ALT_R, - Q_KEY_CODE_META_L, - Q_KEY_CODE_META_R, -}; - static const guint16 *keycode_map; static size_t keycode_maplen; @@ -230,7 +172,6 @@ struct GtkDisplayState { bool external_pause_update; - bool modifier_pressed[ARRAY_SIZE(modifier_keycode)]; bool ignore_keys; DisplayOptions *opts; @@ -455,7 +396,8 @@ 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); + ww = gdk_window_get_width(gtk_widget_get_window(area)); + wh = gdk_window_get_height(gtk_widget_get_window(area)); #if defined(CONFIG_GTK_GL) if (vc->gfx.gls && gtk_use_gl_area) { gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); @@ -468,20 +410,12 @@ static void gd_update_full_redraw(VirtualConsole *vc) static void gtk_release_modifiers(GtkDisplayState *s) { VirtualConsole *vc = gd_vc_find_current(s); - int i, qcode; if (vc->type != GD_VC_GFX || !qemu_console_is_graphic(vc->gfx.dcl.con)) { return; } - for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { - qcode = modifier_keycode[i]; - if (!s->modifier_pressed[i]) { - continue; - } - qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, false); - s->modifier_pressed[i] = false; - } + qkbd_state_lift_all_keys(vc->gfx.kbd); } static void gd_widget_reparent(GtkWidget *from, GtkWidget *to, @@ -530,7 +464,8 @@ static void gd_update(DisplayChangeListener *dcl, if (!win) { return; } - gdk_drawable_get_size(win, &ww, &wh); + ww = gdk_window_get_width(win); + wh = gdk_window_get_height(win); mx = my = 0; if (ww > fbw) { @@ -549,7 +484,6 @@ static void gd_refresh(DisplayChangeListener *dcl) graphic_hw_update(dcl->con); } -#if GTK_CHECK_VERSION(3, 0, 0) static GdkDevice *gd_get_pointer(GdkDisplay *dpy) { #if GTK_CHECK_VERSION(3, 20, 0) @@ -580,24 +514,6 @@ static void gd_mouse_set(DisplayChangeListener *dcl, vc->s->last_x = x; vc->s->last_y = y; } -#else -static void gd_mouse_set(DisplayChangeListener *dcl, - int x, int y, int visible) -{ - VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); - gint x_root, y_root; - - if (qemu_input_is_absolute()) { - return; - } - - gdk_window_get_root_coords(gtk_widget_get_window(vc->gfx.drawing_area), - x, y, &x_root, &y_root); - gdk_display_warp_pointer(gtk_widget_get_display(vc->gfx.drawing_area), - gtk_widget_get_screen(vc->gfx.drawing_area), - x_root, y_root); -} -#endif static void gd_cursor_define(DisplayChangeListener *dcl, QEMUCursor *c) @@ -619,11 +535,7 @@ static void gd_cursor_define(DisplayChangeListener *dcl, pixbuf, c->hot_x, c->hot_y); gdk_window_set_cursor(gtk_widget_get_window(vc->gfx.drawing_area), cursor); g_object_unref(pixbuf); -#if !GTK_CHECK_VERSION(3, 0, 0) - gdk_cursor_unref(cursor); -#else g_object_unref(cursor); -#endif } static void gd_switch(DisplayChangeListener *dcl, @@ -863,7 +775,8 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) fbw = surface_width(vc->gfx.ds); fbh = surface_height(vc->gfx.ds); - gdk_drawable_get_size(gtk_widget_get_window(widget), &ww, &wh); + ww = gdk_window_get_width(gtk_widget_get_window(widget)); + wh = gdk_window_get_height(gtk_widget_get_window(widget)); if (s->full_screen) { vc->gfx.scale_x = (double)ww / fbw; @@ -907,29 +820,6 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) return TRUE; } -#if !GTK_CHECK_VERSION(3, 0, 0) -static gboolean gd_expose_event(GtkWidget *widget, GdkEventExpose *expose, - void *opaque) -{ - cairo_t *cr; - gboolean ret; - - cr = gdk_cairo_create(gtk_widget_get_window(widget)); - cairo_rectangle(cr, - expose->area.x, - expose->area.y, - expose->area.width, - expose->area.height); - cairo_clip(cr); - - ret = gd_draw_event(widget, cr, opaque); - - cairo_destroy(cr); - - return ret; -} -#endif - static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, void *opaque) { @@ -947,8 +837,8 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - gdk_drawable_get_size(gtk_widget_get_window(vc->gfx.drawing_area), - &ww, &wh); + ww = gdk_window_get_width(gtk_widget_get_window(vc->gfx.drawing_area)); + wh = gdk_window_get_height(gtk_widget_get_window(vc->gfx.drawing_area)); mx = my = 0; if (ww > fbw) { @@ -1026,13 +916,8 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, } if (x != (int)motion->x_root || y != (int)motion->y_root) { -#if GTK_CHECK_VERSION(3, 0, 0) GdkDevice *dev = gdk_event_get_device((GdkEvent *)motion); gdk_device_warp(dev, screen, x, y); -#else - GdkDisplay *display = gtk_widget_get_display(widget); - gdk_display_warp_pointer(display, screen, x, y); -#endif s->last_set = FALSE; return FALSE; } @@ -1089,19 +974,19 @@ static gboolean gd_scroll_event(GtkWidget *widget, GdkEventScroll *scroll, btn = INPUT_BUTTON_WHEEL_UP; } else if (scroll->direction == GDK_SCROLL_DOWN) { btn = INPUT_BUTTON_WHEEL_DOWN; -#if GTK_CHECK_VERSION(3, 4, 0) } else if (scroll->direction == GDK_SCROLL_SMOOTH) { gdouble delta_x, delta_y; if (!gdk_event_get_scroll_deltas((GdkEvent *)scroll, &delta_x, &delta_y)) { return TRUE; } - if (delta_y > 0) { + if (delta_y == 0) { + return TRUE; + } else if (delta_y > 0) { btn = INPUT_BUTTON_WHEEL_DOWN; } else { btn = INPUT_BUTTON_WHEEL_UP; } -#endif } else { return TRUE; } @@ -1206,7 +1091,6 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; int qcode; - int i; if (s->ignore_keys) { s->ignore_keys = (key->type == GDK_KEY_PRESS); @@ -1227,8 +1111,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) || key->hardware_keycode == VK_PAUSE #endif ) { - qemu_input_event_send_key_qcode(vc->gfx.dcl.con, Q_KEY_CODE_PAUSE, - key->type == GDK_KEY_PRESS); + qkbd_state_key_event(vc->gfx.kbd, Q_KEY_CODE_PAUSE, + key->type == GDK_KEY_PRESS); return TRUE; } @@ -1237,14 +1121,8 @@ static gboolean gd_key_event(GtkWidget *widget, GdkEventKey *key, void *opaque) trace_gd_key_event(vc->label, key->hardware_keycode, qcode, (key->type == GDK_KEY_PRESS) ? "down" : "up"); - for (i = 0; i < ARRAY_SIZE(modifier_keycode); i++) { - if (qcode == modifier_keycode[i]) { - s->modifier_pressed[i] = (key->type == GDK_KEY_PRESS); - } - } - - qemu_input_event_send_key_qcode(vc->gfx.dcl.con, qcode, - key->type == GDK_KEY_PRESS); + qkbd_state_key_event(vc->gfx.kbd, qcode, + key->type == GDK_KEY_PRESS); return TRUE; } @@ -1309,10 +1187,6 @@ static void gd_accel_switch_vc(void *opaque) VirtualConsole *vc = opaque; gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); -#if !GTK_CHECK_VERSION(3, 0, 0) - /* GTK2 sends the accel key to the target console - ignore this until */ - vc->s->ignore_keys = true; -#endif } static void gd_menu_show_tabs(GtkMenuItem *item, void *opaque) @@ -1539,7 +1413,7 @@ static void gd_grab_update(VirtualConsole *vc, bool kbd, bool ptr) gdk_seat_ungrab(seat); } } -#elif GTK_CHECK_VERSION(3, 0, 0) +#else static void gd_grab_devices(VirtualConsole *vc, bool grab, GdkInputSource source, GdkEventMask mask, GdkCursor *cursor) @@ -1578,14 +1452,10 @@ static void gd_grab_keyboard(VirtualConsole *vc, const char *reason) #if GTK_CHECK_VERSION(3, 20, 0) gd_grab_update(vc, true, vc->s->ptr_owner == vc); -#elif GTK_CHECK_VERSION(3, 0, 0) +#else gd_grab_devices(vc, true, GDK_SOURCE_KEYBOARD, GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK, NULL); -#else - gdk_keyboard_grab(gtk_widget_get_window(vc->gfx.drawing_area), - FALSE, - GDK_CURRENT_TIME); #endif vc->s->kbd_owner = vc; gd_update_caption(vc->s); @@ -1603,10 +1473,8 @@ static void gd_ungrab_keyboard(GtkDisplayState *s) #if GTK_CHECK_VERSION(3, 20, 0) gd_grab_update(vc, false, vc->s->ptr_owner == vc); -#elif GTK_CHECK_VERSION(3, 0, 0) - gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL); #else - gdk_keyboard_ungrab(GDK_CURRENT_TIME); + gd_grab_devices(vc, false, GDK_SOURCE_KEYBOARD, 0, NULL); #endif gd_update_caption(s); trace_gd_ungrab(vc->label, "kbd"); @@ -1628,7 +1496,7 @@ static void gd_grab_pointer(VirtualConsole *vc, const char *reason) gd_grab_update(vc, vc->s->kbd_owner == vc, true); gdk_device_get_position(gd_get_pointer(display), NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); -#elif GTK_CHECK_VERSION(3, 0, 0) +#else gd_grab_devices(vc, true, GDK_SOURCE_MOUSE, GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | @@ -1638,19 +1506,6 @@ static void gd_grab_pointer(VirtualConsole *vc, const char *reason) vc->s->null_cursor); gdk_device_get_position(gd_get_pointer(display), NULL, &vc->s->grab_x_root, &vc->s->grab_y_root); -#else - gdk_pointer_grab(gtk_widget_get_window(vc->gfx.drawing_area), - FALSE, /* All events to come to our window directly */ - GDK_POINTER_MOTION_MASK | - GDK_BUTTON_PRESS_MASK | - GDK_BUTTON_RELEASE_MASK | - GDK_BUTTON_MOTION_MASK | - GDK_SCROLL_MASK, - NULL, /* Allow cursor to move over entire desktop */ - vc->s->null_cursor, - GDK_CURRENT_TIME); - gdk_display_get_pointer(display, NULL, - &vc->s->grab_x_root, &vc->s->grab_y_root, NULL); #endif vc->s->ptr_owner = vc; gd_update_caption(vc->s); @@ -1673,16 +1528,11 @@ static void gd_ungrab_pointer(GtkDisplayState *s) gdk_device_warp(gd_get_pointer(display), gtk_widget_get_screen(vc->gfx.drawing_area), vc->s->grab_x_root, vc->s->grab_y_root); -#elif GTK_CHECK_VERSION(3, 0, 0) +#else gd_grab_devices(vc, false, GDK_SOURCE_MOUSE, 0, NULL); gdk_device_warp(gd_get_pointer(display), gtk_widget_get_screen(vc->gfx.drawing_area), vc->s->grab_x_root, vc->s->grab_y_root); -#else - gdk_pointer_ungrab(GDK_CURRENT_TIME); - gdk_display_warp_pointer(display, - gtk_widget_get_screen(vc->gfx.drawing_area), - vc->s->grab_x_root, vc->s->grab_y_root); #endif gd_update_caption(s); trace_gd_ungrab(vc->label, "ptr"); @@ -1715,21 +1565,10 @@ static void gd_change_page(GtkNotebook *nb, gpointer arg1, guint arg2, return; } -#ifdef VTE_RESIZE_HACK - vc = gd_vc_find_current(s); - if (vc && vc->type == GD_VC_VTE) { - gtk_widget_hide(vc->vte.terminal); - } -#endif vc = gd_vc_find_by_page(s, arg2); if (!vc) { return; } -#ifdef VTE_RESIZE_HACK - if (vc->type == GD_VC_VTE) { - gtk_widget_show(vc->vte.terminal); - } -#endif gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(vc->menu_item), TRUE); on_vga = (vc->type == GD_VC_GFX && @@ -1802,11 +1641,9 @@ static GSList *gd_vc_menu_init(GtkDisplayState *s, VirtualConsole *vc, gtk_accel_group_connect(s->accel_group, GDK_KEY_1 + idx, HOTKEY_MODIFIERS, 0, g_cclosure_new_swap(G_CALLBACK(gd_accel_switch_vc), vc, NULL)); -#if GTK_CHECK_VERSION(3, 8, 0) gtk_accel_label_set_accel( GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(vc->menu_item))), GDK_KEY_1 + idx, HOTKEY_MODIFIERS); -#endif g_signal_connect(vc->menu_item, "activate", G_CALLBACK(gd_menu_switch_vc), s); @@ -1951,32 +1788,29 @@ static GSList *gd_vc_vte_init(GtkDisplayState *s, VirtualConsole *vc, 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. - */ + * 7-bit ASCII at least in VTE 0.38. The function is deprecated since + * VTE 0.54 (only UTF-8 is supported now). */ +#if !VTE_CHECK_VERSION(0, 54, 0) #if VTE_CHECK_VERSION(0, 38, 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 #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); -#if VTE_CHECK_VERSION(0, 28, 0) && GTK_CHECK_VERSION(3, 0, 0) +#if VTE_CHECK_VERSION(0, 28, 0) vadjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE(vc->vte.terminal)); #else vadjustment = vte_terminal_get_adjustment(VTE_TERMINAL(vc->vte.terminal)); #endif -#if GTK_CHECK_VERSION(3, 0, 0) box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); scrollbar = gtk_scrollbar_new(GTK_ORIENTATION_VERTICAL, vadjustment); -#else - box = gtk_hbox_new(false, 2); - scrollbar = gtk_vscrollbar_new(vadjustment); -#endif gtk_box_pack_end(GTK_BOX(box), scrollbar, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(box), vc->vte.terminal, TRUE, TRUE, 0); @@ -2015,7 +1849,6 @@ static void gd_vcs_init(GtkDisplayState *s, GSList *group, 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) @@ -2026,10 +1859,6 @@ static void gd_connect_vc_gfx_signals(VirtualConsole *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); #endif if (qemu_console_is_graphic(vc->gfx.dcl.con)) { g_signal_connect(vc->gfx.drawing_area, "event", @@ -2136,6 +1965,8 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) { + bool zoom_to_fit = false; + vc->label = qemu_console_get_label(con); vc->s = s; vc->gfx.scale_x = 1.0; @@ -2183,6 +2014,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_SCROLL_MASK | + GDK_SMOOTH_SCROLL_MASK | GDK_KEY_PRESS_MASK); gtk_widget_set_can_focus(vc->gfx.drawing_area, TRUE); @@ -2192,6 +2024,7 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, gtk_notebook_append_page(GTK_NOTEBOOK(s->notebook), vc->tab_item, gtk_label_new(vc->label)); + vc->gfx.kbd = qkbd_state_init(con); vc->gfx.dcl.con = con; register_displaychangelistener(&vc->gfx.dcl); @@ -2199,6 +2032,12 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, group = gd_vc_menu_init(s, vc, idx, group, view_menu); if (dpy_ui_info_supported(vc->gfx.dcl.con)) { + zoom_to_fit = true; + } + if (s->opts->u.gtk.has_zoom_to_fit) { + zoom_to_fit = s->opts->u.gtk.zoom_to_fit; + } + if (zoom_to_fit) { gtk_menu_item_activate(GTK_MENU_ITEM(s->zoom_fit_item)); s->free_scale = true; } @@ -2226,11 +2065,9 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) gtk_accel_group_connect(s->accel_group, GDK_KEY_f, HOTKEY_MODIFIERS, 0, g_cclosure_new_swap(G_CALLBACK(gd_accel_full_screen), s, NULL)); -#if GTK_CHECK_VERSION(3, 8, 0) gtk_accel_label_set_accel( GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->full_screen_item))), GDK_KEY_f, HOTKEY_MODIFIERS); -#endif gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->full_screen_item); separator = gtk_separator_menu_item_new(); @@ -2309,11 +2146,9 @@ static GtkWidget *gd_create_menu_view(GtkDisplayState *s) TRUE); gtk_accel_group_connect(s->accel_group, GDK_KEY_m, HOTKEY_MODIFIERS, 0, g_cclosure_new_swap(G_CALLBACK(gd_accel_show_menubar), s, NULL)); -#if GTK_CHECK_VERSION(3, 8, 0) gtk_accel_label_set_accel( GTK_ACCEL_LABEL(gtk_bin_get_child(GTK_BIN(s->show_menubar_item))), GDK_KEY_m, HOTKEY_MODIFIERS); -#endif gtk_menu_shell_append(GTK_MENU_SHELL(view_menu), s->show_menubar_item); return view_menu; @@ -2352,8 +2187,8 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) VirtualConsole *vc; GtkDisplayState *s = g_malloc0(sizeof(*s)); - char *filename; GdkDisplay *window_display; + GtkIconTheme *theme; if (!gtkinit) { fprintf(stderr, "gtk initialization failed\n"); @@ -2362,17 +2197,12 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) assert(opts->type == DISPLAY_TYPE_GTK); s->opts = opts; -#if !GTK_CHECK_VERSION(3, 0, 0) - g_printerr("Running QEMU with GTK 2.x is deprecated, and will be removed\n" - "in a future release. Please switch to GTK 3.x instead\n"); -#endif + theme = gtk_icon_theme_get_default(); + gtk_icon_theme_prepend_search_path(theme, CONFIG_QEMU_ICONDIR); + g_set_prgname("qemu"); s->window = gtk_window_new(GTK_WINDOW_TOPLEVEL); -#if GTK_CHECK_VERSION(3, 2, 0) s->vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); -#else - s->vbox = gtk_vbox_new(FALSE, 0); -#endif s->notebook = gtk_notebook_new(); s->menu_bar = gtk_menu_bar_new(); @@ -2395,17 +2225,7 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) qemu_add_mouse_mode_change_notifier(&s->mouse_mode_notifier); qemu_add_vm_change_state_handler(gd_change_runstate, s); - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu_logo_no_text.svg"); - if (filename) { - GError *error = NULL; - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(filename, &error); - if (pixbuf) { - gtk_window_set_icon(GTK_WINDOW(s->window), pixbuf); - } else { - g_error_free(error); - } - g_free(filename); - } + gtk_window_set_icon_name(GTK_WINDOW(s->window), "qemu"); gd_create_menus(s); @@ -2423,23 +2243,6 @@ static void gtk_display_init(DisplayState *ds, DisplayOptions *opts) gtk_widget_show_all(s->window); -#ifdef VTE_RESIZE_HACK - { - VirtualConsole *cur = gd_vc_find_current(s); - if (cur) { - int i; - - for (i = 0; i < s->nb_vcs; i++) { - VirtualConsole *vc = &s->vc[i]; - if (vc && vc->type == GD_VC_VTE && vc != cur) { - gtk_widget_hide(vc->vte.terminal); - } - } - gd_update_windowsize(cur); - } - } -#endif - vc = gd_vc_find_current(s); gtk_widget_set_sensitive(s->view_menu, vc != NULL); #ifdef CONFIG_VTE diff --git a/ui/icons/Makefile b/ui/icons/Makefile new file mode 100644 index 0000000000..20bd64ccce --- /dev/null +++ b/ui/icons/Makefile @@ -0,0 +1,13 @@ + +# Regenerate bitmaps from the SVG using inkscape CLI export +# and ImageMagick. Don't use ImageMagick for the initial +# SVG conversion, since it merely calls inkscape, but uses +# 96 DPI res resulting in poor quality output. + +regenerate: + for s in 16 24 32 48 64 128 256 512; \ + do \ + inkscape --without-gui --export-png=qemu_$${s}x$${s}.png \ + --export-width=$$s --export-height=$$s qemu.svg ; \ + done + convert qemu_32x32.png qemu_32x32.bmp diff --git a/pc-bios/qemu_logo_no_text.svg b/ui/icons/qemu.svg similarity index 100% rename from pc-bios/qemu_logo_no_text.svg rename to ui/icons/qemu.svg diff --git a/ui/icons/qemu_128x128.png b/ui/icons/qemu_128x128.png new file mode 100644 index 0000000000..96831807ba Binary files /dev/null and b/ui/icons/qemu_128x128.png differ diff --git a/ui/icons/qemu_16x16.png b/ui/icons/qemu_16x16.png new file mode 100644 index 0000000000..ff4f046024 Binary files /dev/null and b/ui/icons/qemu_16x16.png differ diff --git a/ui/icons/qemu_24x24.png b/ui/icons/qemu_24x24.png new file mode 100644 index 0000000000..f039c6e25d Binary files /dev/null and b/ui/icons/qemu_24x24.png differ diff --git a/ui/icons/qemu_256x256.png b/ui/icons/qemu_256x256.png new file mode 100644 index 0000000000..a39c0e307e Binary files /dev/null and b/ui/icons/qemu_256x256.png differ diff --git a/ui/icons/qemu_32x32.bmp b/ui/icons/qemu_32x32.bmp new file mode 100644 index 0000000000..c0daa54abe Binary files /dev/null and b/ui/icons/qemu_32x32.bmp differ diff --git a/ui/icons/qemu_32x32.png b/ui/icons/qemu_32x32.png new file mode 100644 index 0000000000..b746096cf8 Binary files /dev/null and b/ui/icons/qemu_32x32.png differ diff --git a/ui/icons/qemu_48x48.png b/ui/icons/qemu_48x48.png new file mode 100644 index 0000000000..067281225d Binary files /dev/null and b/ui/icons/qemu_48x48.png differ diff --git a/ui/icons/qemu_512x512.png b/ui/icons/qemu_512x512.png new file mode 100644 index 0000000000..86aaa6395f Binary files /dev/null and b/ui/icons/qemu_512x512.png differ diff --git a/ui/icons/qemu_64x64.png b/ui/icons/qemu_64x64.png new file mode 100644 index 0000000000..e00c8b4c9b Binary files /dev/null and b/ui/icons/qemu_64x64.png differ diff --git a/ui/input-linux.c b/ui/input-linux.c index 9720333b2c..ba550dd274 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -12,6 +12,8 @@ #include "sysemu/sysemu.h" #include "ui/input.h" #include "qom/object_interfaces.h" +#include "sysemu/iothread.h" +#include "block/aio.h" #include #include "standard-headers/linux/input.h" @@ -63,6 +65,8 @@ struct InputLinux { struct input_event event; int read_offset; + enum GrabToggleKeys grab_toggle; + QTAILQ_ENTRY(InputLinux) next; }; @@ -98,6 +102,44 @@ static void input_linux_toggle_grab(InputLinux *il) } } +static bool input_linux_check_toggle(InputLinux *il) +{ + switch (il->grab_toggle) { + case GRAB_TOGGLE_KEYS_CTRL_CTRL: + return il->keydown[KEY_LEFTCTRL] && + il->keydown[KEY_RIGHTCTRL]; + + case GRAB_TOGGLE_KEYS_ALT_ALT: + return il->keydown[KEY_LEFTALT] && + il->keydown[KEY_RIGHTALT]; + + case GRAB_TOGGLE_KEYS_META_META: + return il->keydown[KEY_LEFTMETA] && + il->keydown[KEY_RIGHTMETA]; + + case GRAB_TOGGLE_KEYS_SCROLLLOCK: + return il->keydown[KEY_SCROLLLOCK]; + + case GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK: + return (il->keydown[KEY_LEFTCTRL] || + il->keydown[KEY_RIGHTCTRL]) && + il->keydown[KEY_SCROLLLOCK]; + + case GRAB_TOGGLE_KEYS__MAX: + /* avoid gcc error */ + break; + } + return false; +} + +static bool input_linux_should_skip(InputLinux *il, + struct input_event *event) +{ + return (il->grab_toggle == GRAB_TOGGLE_KEYS_SCROLLLOCK || + il->grab_toggle == GRAB_TOGGLE_KEYS_CTRL_SCROLLLOCK) && + event->code == KEY_SCROLLLOCK; +} + static void input_linux_handle_keyboard(InputLinux *il, struct input_event *event) { @@ -128,14 +170,13 @@ static void input_linux_handle_keyboard(InputLinux *il, } /* send event to guest when grab is active */ - if (il->grab_active) { + if (il->grab_active && !input_linux_should_skip(il, event)) { 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]) { + if (input_linux_check_toggle(il)) { il->grab_request = true; } @@ -410,6 +451,21 @@ static void input_linux_set_repeat(Object *obj, bool value, il->repeat = value; } +static int input_linux_get_grab_toggle(Object *obj, Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + return il->grab_toggle; +} + +static void input_linux_set_grab_toggle(Object *obj, int value, + Error **errp) +{ + InputLinux *il = INPUT_LINUX(obj); + + il->grab_toggle = value; +} + static void input_linux_instance_init(Object *obj) { object_property_add_str(obj, "evdev", @@ -421,6 +477,10 @@ static void input_linux_instance_init(Object *obj) object_property_add_bool(obj, "repeat", input_linux_get_repeat, input_linux_set_repeat, NULL); + object_property_add_enum(obj, "grab-toggle", "GrabToggleKeys", + &GrabToggleKeys_lookup, + input_linux_get_grab_toggle, + input_linux_set_grab_toggle, NULL); } static void input_linux_class_init(ObjectClass *oc, void *data) diff --git a/ui/input.c b/ui/input.c index 51b1019252..9494688295 100644 --- a/ui/input.c +++ b/ui/input.c @@ -19,6 +19,9 @@ struct QemuInputHandlerState { }; typedef struct QemuInputEventQueue QemuInputEventQueue; +typedef QTAILQ_HEAD(QemuInputEventQueueHead, QemuInputEventQueue) + QemuInputEventQueueHead; + struct QemuInputEventQueue { enum { QEMU_INPUT_QUEUE_DELAY = 1, @@ -37,8 +40,7 @@ static QTAILQ_HEAD(, QemuInputHandlerState) handlers = static NotifierList mouse_mode_notifiers = NOTIFIER_LIST_INITIALIZER(mouse_mode_notifiers); -static QTAILQ_HEAD(QemuInputEventQueueHead, QemuInputEventQueue) kbd_queue = - QTAILQ_HEAD_INITIALIZER(kbd_queue); +static QemuInputEventQueueHead kbd_queue = QTAILQ_HEAD_INITIALIZER(kbd_queue); static QEMUTimer *kbd_timer; static uint32_t kbd_default_delay_ms = 10; static uint32_t queue_count; @@ -257,7 +259,7 @@ static void qemu_input_event_trace(QemuConsole *src, InputEvent *evt) static void qemu_input_queue_process(void *opaque) { - struct QemuInputEventQueueHead *queue = opaque; + QemuInputEventQueueHead *queue = opaque; QemuInputEventQueue *item; g_assert(!QTAILQ_EMPTY(queue)); @@ -288,7 +290,7 @@ static void qemu_input_queue_process(void *opaque) } } -static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue, +static void qemu_input_queue_delay(QemuInputEventQueueHead *queue, QEMUTimer *timer, uint32_t delay_ms) { QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); @@ -306,7 +308,7 @@ static void qemu_input_queue_delay(struct QemuInputEventQueueHead *queue, } } -static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue, +static void qemu_input_queue_event(QemuInputEventQueueHead *queue, QemuConsole *src, InputEvent *evt) { QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); @@ -318,7 +320,7 @@ static void qemu_input_queue_event(struct QemuInputEventQueueHead *queue, queue_count++; } -static void qemu_input_queue_sync(struct QemuInputEventQueueHead *queue) +static void qemu_input_queue_sync(QemuInputEventQueueHead *queue) { QemuInputEventQueue *item = g_new0(QemuInputEventQueue, 1); @@ -448,8 +450,9 @@ void qemu_input_event_send_key_delay(uint32_t delay_ms) } if (!kbd_timer) { - kbd_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, qemu_input_queue_process, - &kbd_queue); + kbd_timer = timer_new_full(NULL, QEMU_CLOCK_VIRTUAL, + SCALE_MS, QEMU_TIMER_ATTR_EXTERNAL, + qemu_input_queue_process, &kbd_queue); } if (queue_count < queue_limit) { qemu_input_queue_delay(&kbd_queue, kbd_timer, @@ -457,22 +460,18 @@ 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->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; -} - void qemu_input_queue_btn(QemuConsole *src, InputButton btn, bool down) { - InputEvent *evt; - evt = qemu_input_event_new_btn(btn, down); - qemu_input_event_send(src, evt); - qapi_free_InputEvent(evt); + InputBtnEvent bevt = { + .button = btn, + .down = down, + }; + InputEvent evt = { + .type = INPUT_EVENT_KIND_BTN, + .u.btn.data = &bevt, + }; + + qemu_input_event_send(src, &evt); } void qemu_input_update_buttons(QemuConsole *src, uint32_t *button_map, @@ -512,37 +511,35 @@ int qemu_input_scale_axis(int value, return ((int64_t)value - min_in) * range_out / range_in + min_out; } -InputEvent *qemu_input_event_new_move(InputEventKind kind, - InputAxis axis, int value) -{ - InputEvent *evt = g_new0(InputEvent, 1); - InputMoveEvent *move = g_new0(InputMoveEvent, 1); - - 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; -} - void qemu_input_queue_rel(QemuConsole *src, InputAxis axis, int value) { - InputEvent *evt; - evt = qemu_input_event_new_move(INPUT_EVENT_KIND_REL, axis, value); - qemu_input_event_send(src, evt); - qapi_free_InputEvent(evt); + InputMoveEvent move = { + .axis = axis, + .value = value, + }; + InputEvent evt = { + .type = INPUT_EVENT_KIND_REL, + .u.rel.data = &move, + }; + + qemu_input_event_send(src, &evt); } void qemu_input_queue_abs(QemuConsole *src, InputAxis axis, int value, int min_in, int max_in) { - InputEvent *evt; - int scaled = qemu_input_scale_axis(value, min_in, max_in, + InputMoveEvent move = { + .axis = axis, + .value = qemu_input_scale_axis(value, min_in, max_in, INPUT_EVENT_ABS_MIN, - INPUT_EVENT_ABS_MAX); - evt = qemu_input_event_new_move(INPUT_EVENT_KIND_ABS, axis, scaled); - qemu_input_event_send(src, evt); - qapi_free_InputEvent(evt); + INPUT_EVENT_ABS_MAX), + }; + InputEvent evt = { + .type = INPUT_EVENT_KIND_ABS, + .u.abs.data = &move, + }; + + qemu_input_event_send(src, &evt); } void qemu_input_check_mode_change(void) diff --git a/ui/kbd-state.c b/ui/kbd-state.c new file mode 100644 index 0000000000..f3ab2d7a66 --- /dev/null +++ b/ui/kbd-state.c @@ -0,0 +1,134 @@ +/* + * 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/bitmap.h" +#include "qemu/queue.h" +#include "ui/console.h" +#include "ui/input.h" +#include "ui/kbd-state.h" + +struct QKbdState { + QemuConsole *con; + int key_delay_ms; + DECLARE_BITMAP(keys, Q_KEY_CODE__MAX); + DECLARE_BITMAP(mods, QKBD_MOD__MAX); +}; + +static void qkbd_state_modifier_update(QKbdState *kbd, + QKeyCode qcode1, QKeyCode qcode2, + QKbdModifier mod) +{ + if (test_bit(qcode1, kbd->keys) || test_bit(qcode2, kbd->keys)) { + set_bit(mod, kbd->mods); + } else { + clear_bit(mod, kbd->mods); + } +} + +bool qkbd_state_modifier_get(QKbdState *kbd, QKbdModifier mod) +{ + return test_bit(mod, kbd->mods); +} + +bool qkbd_state_key_get(QKbdState *kbd, QKeyCode qcode) +{ + return test_bit(qcode, kbd->keys); +} + +void qkbd_state_key_event(QKbdState *kbd, QKeyCode qcode, bool down) +{ + bool state = test_bit(qcode, kbd->keys); + + if (down == false /* got key-up event */ && + state == false /* key is not pressed */) { + /* + * Filter out suspicious key-up events. + * + * This allows simply sending along all key-up events, and + * this function will filter out everything where the + * corresponding key-down event wasn't sent to the guest, for + * example due to being a host hotkey. + * + * Note that key-down events on already pressed keys are *not* + * suspicious, those are keyboard autorepeat events. + */ + return; + } + + /* update key and modifier state */ + change_bit(qcode, kbd->keys); + switch (qcode) { + case Q_KEY_CODE_SHIFT: + case Q_KEY_CODE_SHIFT_R: + qkbd_state_modifier_update(kbd, Q_KEY_CODE_SHIFT, Q_KEY_CODE_SHIFT_R, + QKBD_MOD_SHIFT); + break; + case Q_KEY_CODE_CTRL: + case Q_KEY_CODE_CTRL_R: + qkbd_state_modifier_update(kbd, Q_KEY_CODE_CTRL, Q_KEY_CODE_CTRL_R, + QKBD_MOD_CTRL); + break; + case Q_KEY_CODE_ALT: + qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT, Q_KEY_CODE_ALT, + QKBD_MOD_ALT); + break; + case Q_KEY_CODE_ALT_R: + qkbd_state_modifier_update(kbd, Q_KEY_CODE_ALT_R, Q_KEY_CODE_ALT_R, + QKBD_MOD_ALTGR); + break; + case Q_KEY_CODE_CAPS_LOCK: + if (down) { + change_bit(QKBD_MOD_CAPSLOCK, kbd->mods); + } + break; + case Q_KEY_CODE_NUM_LOCK: + if (down) { + change_bit(QKBD_MOD_NUMLOCK, kbd->mods); + } + break; + default: + /* keep gcc happy */ + break; + } + + /* send to guest */ + if (qemu_console_is_graphic(kbd->con)) { + qemu_input_event_send_key_qcode(kbd->con, qcode, down); + if (kbd->key_delay_ms) { + qemu_input_event_send_key_delay(kbd->key_delay_ms); + } + } +} + +void qkbd_state_lift_all_keys(QKbdState *kbd) +{ + int qcode; + + for (qcode = 0; qcode < Q_KEY_CODE__MAX; qcode++) { + if (test_bit(qcode, kbd->keys)) { + qkbd_state_key_event(kbd, qcode, false); + } + } +} + +void qkbd_state_set_delay(QKbdState *kbd, int delay_ms) +{ + kbd->key_delay_ms = delay_ms; +} + +void qkbd_state_free(QKbdState *kbd) +{ + g_free(kbd); +} + +QKbdState *qkbd_state_init(QemuConsole *con) +{ + QKbdState *kbd = g_new0(QKbdState, 1); + + kbd->con = con; + + return kbd; +} diff --git a/ui/keymaps.c b/ui/keymaps.c index 43fe604724..544b55c27b 100644 --- a/ui/keymaps.c +++ b/ui/keymaps.c @@ -27,6 +27,8 @@ #include "sysemu/sysemu.h" #include "trace.h" #include "qemu/error-report.h" +#include "qapi/error.h" +#include "ui/input.h" struct keysym2code { uint32_t count; @@ -79,10 +81,11 @@ static void add_keysym(char *line, int keysym, int keycode, kbd_layout_t *k) trace_keymap_add(keysym, keycode, line); } -static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, - const char *language, - kbd_layout_t *k) +static int parse_keyboard_layout(kbd_layout_t *k, + const name2keysym_t *table, + const char *language, Error **errp) { + int ret; FILE *f; char * filename; char line[1024]; @@ -94,13 +97,8 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, f = filename ? fopen(filename, "r") : NULL; g_free(filename); if (!f) { - fprintf(stderr, "Could not read keymap file: '%s'\n", language); - return NULL; - } - - if (!k) { - k = g_new0(kbd_layout_t, 1); - k->hash = g_hash_table_new(NULL, NULL); + error_setg(errp, "could not read keymap file: '%s'", language); + return -1; } for(;;) { @@ -118,7 +116,9 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, continue; } if (!strncmp(line, "include ", 8)) { - parse_keyboard_layout(table, line + 8, k); + error_setg(errp, "keymap include files are not supported any more"); + ret = -1; + goto out; } else { int offset = 0; while (line[offset] != 0 && @@ -164,20 +164,32 @@ static kbd_layout_t *parse_keyboard_layout(const name2keysym_t *table, } } } + + ret = 0; +out: fclose(f); - return k; + return ret; } kbd_layout_t *init_keyboard_layout(const name2keysym_t *table, - const char *language) + const char *language, Error **errp) { - return parse_keyboard_layout(table, language, NULL); + kbd_layout_t *k; + + k = g_new0(kbd_layout_t, 1); + k->hash = g_hash_table_new(NULL, NULL); + if (parse_keyboard_layout(k, table, language, errp) < 0) { + g_hash_table_unref(k->hash); + g_free(k); + return NULL; + } + return k; } int keysym2scancode(kbd_layout_t *k, int keysym, - bool shift, bool altgr, bool ctrl) + QKbdState *kbd, bool down) { static const uint32_t mask = SCANCODE_SHIFT | SCANCODE_ALTGR | SCANCODE_CTRL; @@ -201,27 +213,39 @@ int keysym2scancode(kbd_layout_t *k, int keysym, return keysym2code->keycodes[0]; } - /* - * We have multiple keysym -> keycode mappings. - * - * Check whenever we find one mapping where the modifier state of - * the mapping matches the current user interface modifier state. - * If so, prefer that one. - */ - mods = 0; - if (shift) { - mods |= SCANCODE_SHIFT; - } - if (altgr) { - mods |= SCANCODE_ALTGR; - } - if (ctrl) { - mods |= SCANCODE_CTRL; - } + /* We have multiple keysym -> keycode mappings. */ + if (down) { + /* + * On keydown: Check whenever we find one mapping where the + * modifier state of the mapping matches the current user + * interface modifier state. If so, prefer that one. + */ + mods = 0; + if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_SHIFT)) { + mods |= SCANCODE_SHIFT; + } + if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_ALTGR)) { + mods |= SCANCODE_ALTGR; + } + if (kbd && qkbd_state_modifier_get(kbd, QKBD_MOD_CTRL)) { + mods |= SCANCODE_CTRL; + } - for (i = 0; i < keysym2code->count; i++) { - if ((keysym2code->keycodes[i] & mask) == mods) { - return keysym2code->keycodes[i]; + for (i = 0; i < keysym2code->count; i++) { + if ((keysym2code->keycodes[i] & mask) == mods) { + return keysym2code->keycodes[i]; + } + } + } else { + /* + * On keyup: Try find a key which is actually down. + */ + for (i = 0; i < keysym2code->count; i++) { + QKeyCode qcode = qemu_input_key_number_to_qcode + (keysym2code->keycodes[i]); + if (kbd && qkbd_state_key_get(kbd, qcode)) { + return keysym2code->keycodes[i]; + } } } return keysym2code->keycodes[0]; diff --git a/ui/keymaps.h b/ui/keymaps.h index 0693588225..b6d48aac40 100644 --- a/ui/keymaps.h +++ b/ui/keymaps.h @@ -26,10 +26,11 @@ #define QEMU_KEYMAPS_H #include "qemu-common.h" +#include "ui/kbd-state.h" typedef struct { - const char* name; - int keysym; + const char* name; + int keysym; } name2keysym_t; /* scancode without modifiers */ @@ -53,9 +54,9 @@ typedef struct { typedef struct kbd_layout_t kbd_layout_t; kbd_layout_t *init_keyboard_layout(const name2keysym_t *table, - const char *language); + const char *language, Error **errp); int keysym2scancode(kbd_layout_t *k, int keysym, - bool shift, bool altgr, bool ctrl); + QKbdState *kbd, bool down); int keycode_is_keypad(kbd_layout_t *k, int keycode); int keysym_is_numlock(kbd_layout_t *k, int keysym); diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 3e52abd92d..1429cf08d5 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -36,7 +36,7 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format) pf.rshift = 0; break; case PIXMAN_TYPE_BGRA: - pf.bshift = bpp - pf.bbits; + pf.bshift = bpp - pf.bbits; pf.gshift = bpp - (pf.bbits + pf.gbits); pf.rshift = bpp - (pf.bbits + pf.gbits + pf.rbits); pf.ashift = 0; diff --git a/ui/qemu.desktop b/ui/qemu.desktop new file mode 100644 index 0000000000..20f09f56be --- /dev/null +++ b/ui/qemu.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Version=1.0 +Name=QEMU +Icon=qemu +Type=Application +Terminal=false +Keywords=Emulators;Virtualization;KVM; +NoDisplay=true diff --git a/ui/sdl.c b/ui/sdl.c deleted file mode 100644 index a5fd503c25..0000000000 --- a/ui/sdl.c +++ /dev/null @@ -1,1027 +0,0 @@ -/* - * QEMU SDL display driver - * - * Copyright (c) 2003 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. - */ - -/* 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" -#ifndef WIN32 -#include "x_keymap.h" -#endif -#include "sdl_zoom.h" - -static DisplayChangeListener *dcl; -static DisplaySurface *surface; -static DisplayOptions *opts; -static SDL_Surface *real_screen; -static SDL_Surface *guest_screen = NULL; -static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ -static int last_vm_running; -static bool gui_saved_scaling; -static int gui_saved_width; -static int gui_saved_height; -static int gui_saved_grab; -static int gui_fullscreen; -static int gui_key_modifier_pressed; -static int gui_keysym; -static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; -static uint8_t modifiers_state[256]; -static SDL_Cursor *sdl_cursor_normal; -static SDL_Cursor *sdl_cursor_hidden; -static int absolute_enabled = 0; -static int guest_cursor = 0; -static int guest_x, guest_y; -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; -static const guint16 *keycode_map; -static size_t keycode_maplen; - -#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 -#endif - -static void sdl_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) -{ - SDL_Rect rec; - rec.x = x; - rec.y = y; - rec.w = w; - rec.h = h; - -#ifdef DEBUG_SDL - printf("SDL: Updating x=%d y=%d w=%d h=%d (scaling: %d)\n", - x, y, w, h, scaling_active); -#endif - - if (guest_screen) { - if (!scaling_active) { - SDL_BlitSurface(guest_screen, &rec, real_screen, &rec); - } else { - if (sdl_zoom_blit(guest_screen, real_screen, SMOOTHING_ON, &rec) < 0) { - fprintf(stderr, "Zoom blit failed\n"); - exit(1); - } - } - } - SDL_UpdateRect(real_screen, rec.x, rec.y, rec.w, rec.h); -} - -static void do_sdl_resize(int width, int height, int bpp) -{ - int flags; - SDL_Surface *tmp_screen; - -#ifdef DEBUG_SDL - printf("SDL: Resizing to %dx%d bpp %d\n", width, height, bpp); -#endif - - flags = SDL_HWSURFACE | SDL_ASYNCBLIT | SDL_HWACCEL; - if (gui_fullscreen) { - flags |= SDL_FULLSCREEN; - } else { - flags |= SDL_RESIZABLE; - } - if (no_frame) { - flags |= SDL_NOFRAME; - } - - tmp_screen = SDL_SetVideoMode(width, height, bpp, flags); - if (!real_screen) { - if (!tmp_screen) { - fprintf(stderr, "Could not open SDL display (%dx%dx%d): %s\n", - width, height, bpp, SDL_GetError()); - exit(1); - } - } else { - /* - * Revert to the previous video mode if the change of resizing or - * resolution failed. - */ - if (!tmp_screen) { - fprintf(stderr, "Failed to set SDL display (%dx%dx%d): %s\n", - width, height, bpp, SDL_GetError()); - return; - } - } - - real_screen = tmp_screen; -} - -static void sdl_switch(DisplayChangeListener *dcl, - DisplaySurface *new_surface) -{ - PixelFormat pf; - - /* temporary hack: allows to call sdl_switch to handle scaling changes */ - if (new_surface) { - surface = new_surface; - } - pf = qemu_pixelformat_from_pixman(surface->format); - - if (!scaling_active) { - do_sdl_resize(surface_width(surface), surface_height(surface), 0); - } else if (real_screen->format->BitsPerPixel != - surface_bits_per_pixel(surface)) { - do_sdl_resize(real_screen->w, real_screen->h, - surface_bits_per_pixel(surface)); - } - - if (guest_screen != NULL) { - SDL_FreeSurface(guest_screen); - } - -#ifdef DEBUG_SDL - printf("SDL: Creating surface with masks: %08x %08x %08x %08x\n", - pf.rmask, pf.gmask, pf.bmask, pf.amask); -#endif - - guest_screen = SDL_CreateRGBSurfaceFrom - (surface_data(surface), - surface_width(surface), surface_height(surface), - surface_bits_per_pixel(surface), surface_stride(surface), - pf.rmask, pf.gmask, - pf.bmask, pf.amask); -} - -static bool sdl_check_format(DisplayChangeListener *dcl, - pixman_format_code_t format) -{ - /* - * We let SDL convert for us a few more formats than, - * the native ones. Thes are the ones I have tested. - */ - return (format == PIXMAN_x8r8g8b8 || - format == PIXMAN_b8g8r8x8 || - format == PIXMAN_x1r5g5b5 || - format == PIXMAN_r5g6b5); -} - -/* generic keyboard conversion */ - -#include "sdl_keysym.h" - -static kbd_layout_t *kbd_layout = NULL; - -static uint8_t sdl_keyevent_to_keycode_generic(const SDL_KeyboardEvent *ev) -{ - bool shift = modifiers_state[0x2a] || modifiers_state[0x36]; - bool altgr = modifiers_state[0xb8]; - bool ctrl = modifiers_state[0x1d] || modifiers_state[0x9d]; - int keysym; - /* workaround for X11+SDL bug with AltGR */ - keysym = ev->keysym.sym; - if (keysym == 0 && ev->keysym.scancode == 113) - keysym = SDLK_MODE; - /* For Japanese key '\' and '|' */ - if (keysym == 92 && ev->keysym.scancode == 133) { - keysym = 0xa5; - } - return keysym2scancode(kbd_layout, keysym, - shift, altgr, ctrl) & SCANCODE_KEYMASK; -} - - -static const guint16 *sdl_get_keymap(size_t *maplen) -{ -#if defined(WIN32) - *maplen = qemu_input_map_atset1_to_qcode_len; - return qemu_input_map_atset1_to_qcode; -#else -#if defined(SDL_VIDEO_DRIVER_X11) - SDL_SysWMinfo info; - - SDL_VERSION(&info.version); - if (SDL_GetWMInfo(&info) > 0) { - return qemu_xkeymap_mapping_table( - info.info.x11.display, maplen); - } -#endif - g_warning("Unsupported SDL video driver / platform.\n" - "Assuming Linux KBD scancodes, but probably wrong.\n" - "Please report to qemu-devel@nongnu.org\n" - "including the following information:\n" - "\n" - " - Operating system\n" - " - SDL video driver\n"); - *maplen = qemu_input_map_xorgkbd_to_qcode_len; - return qemu_input_map_xorgkbd_to_qcode; -#endif -} - -static uint8_t sdl_keyevent_to_keycode(const SDL_KeyboardEvent *ev) -{ - int qcode; - if (!keycode_map) { - return 0; - } - if (ev->keysym.scancode > keycode_maplen) { - return 0; - } - - qcode = keycode_map[ev->keysym.scancode]; - - if (qcode > qemu_input_map_qcode_to_qnum_len) { - return 0; - } - - return qemu_input_map_qcode_to_qnum[qcode]; -} - -static void reset_keys(void) -{ - int i; - for(i = 0; i < 256; i++) { - if (modifiers_state[i]) { - qemu_input_event_send_key_number(dcl->con, i, false); - modifiers_state[i] = 0; - } - } -} - -static void sdl_process_key(SDL_KeyboardEvent *ev) -{ - int keycode; - - if (ev->keysym.sym == SDLK_PAUSE) { - /* specific case */ - qemu_input_event_send_key_qcode(dcl->con, Q_KEY_CODE_PAUSE, - ev->type == SDL_KEYDOWN); - return; - } - - if (kbd_layout) { - keycode = sdl_keyevent_to_keycode_generic(ev); - } else { - keycode = sdl_keyevent_to_keycode(ev); - } - - switch(keycode) { - case 0x00: - /* sent when leaving window: reset the modifiers state */ - reset_keys(); - return; - case 0x2a: /* Left Shift */ - case 0x36: /* Right Shift */ - case 0x1d: /* Left CTRL */ - case 0x9d: /* Right CTRL */ - case 0x38: /* Left ALT */ - case 0xb8: /* Right ALT */ - if (ev->type == SDL_KEYUP) - modifiers_state[keycode] = 0; - else - modifiers_state[keycode] = 1; - break; -#define QEMU_SDL_VERSION ((SDL_MAJOR_VERSION << 8) + SDL_MINOR_VERSION) -#if QEMU_SDL_VERSION < 0x102 || QEMU_SDL_VERSION == 0x102 && SDL_PATCHLEVEL < 14 - /* SDL versions before 1.2.14 don't support key up for caps/num lock. */ - case 0x45: /* num lock */ - case 0x3a: /* caps lock */ - /* SDL does not send the key up event, so we generate it */ - qemu_input_event_send_key_number(dcl->con, keycode, true); - qemu_input_event_send_key_number(dcl->con, keycode, false); - return; -#endif - } - - /* now send the key code */ - qemu_input_event_send_key_number(dcl->con, keycode, - ev->type == SDL_KEYDOWN); -} - -static void sdl_update_caption(void) -{ - char win_title[1024]; - char icon_title[1024]; - const char *status = ""; - - if (!runstate_is_running()) - status = " [Stopped]"; - else if (gui_grab) { - if (alt_grab) - status = " - Press Ctrl-Alt-Shift-G to exit mouse grab"; - else if (ctrl_grab) - status = " - Press Right-Ctrl-G to exit mouse grab"; - else - status = " - Press Ctrl-Alt-G to exit mouse grab"; - } - - if (qemu_name) { - snprintf(win_title, sizeof(win_title), "QEMU (%s)%s", qemu_name, status); - snprintf(icon_title, sizeof(icon_title), "QEMU (%s)", qemu_name); - } else { - snprintf(win_title, sizeof(win_title), "QEMU%s", status); - snprintf(icon_title, sizeof(icon_title), "QEMU"); - } - - SDL_WM_SetCaption(win_title, icon_title); -} - -static void sdl_hide_cursor(void) -{ - if (!cursor_hide) - return; - - if (qemu_input_is_absolute()) { - SDL_ShowCursor(1); - SDL_SetCursor(sdl_cursor_hidden); - } else { - SDL_ShowCursor(0); - } -} - -static void sdl_show_cursor(void) -{ - if (!cursor_hide) - return; - - if (!qemu_input_is_absolute() || !qemu_console_is_graphic(NULL)) { - SDL_ShowCursor(1); - if (guest_cursor && - (gui_grab || qemu_input_is_absolute() || absolute_enabled)) - SDL_SetCursor(guest_sprite); - else - SDL_SetCursor(sdl_cursor_normal); - } -} - -static void sdl_grab_start(void) -{ - /* - * If the application is not active, do not try to enter grab state. This - * prevents 'SDL_WM_GrabInput(SDL_GRAB_ON)' from blocking all the - * application (SDL bug). - */ - if (!(SDL_GetAppState() & SDL_APPINPUTFOCUS)) { - return; - } - if (guest_cursor) { - SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { - SDL_WarpMouse(guest_x, guest_y); - } - } else - sdl_hide_cursor(); - SDL_WM_GrabInput(SDL_GRAB_ON); - gui_grab = 1; - sdl_update_caption(); -} - -static void sdl_grab_end(void) -{ - SDL_WM_GrabInput(SDL_GRAB_OFF); - gui_grab = 0; - sdl_show_cursor(); - sdl_update_caption(); -} - -static void absolute_mouse_grab(void) -{ - int mouse_x, mouse_y; - - SDL_GetMouseState(&mouse_x, &mouse_y); - if (mouse_x > 0 && mouse_x < real_screen->w - 1 && - mouse_y > 0 && mouse_y < real_screen->h - 1) { - sdl_grab_start(); - } -} - -static void sdl_mouse_mode_change(Notifier *notify, void *data) -{ - if (qemu_input_is_absolute()) { - if (!absolute_enabled) { - absolute_enabled = 1; - if (qemu_console_is_graphic(NULL)) { - absolute_mouse_grab(); - } - } - } else if (absolute_enabled) { - if (!gui_fullscreen) { - sdl_grab_end(); - } - absolute_enabled = 0; - } -} - -static void sdl_send_mouse_event(int dx, int dy, int x, int y, int state) -{ - 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), - [INPUT_BUTTON_WHEEL_UP] = SDL_BUTTON(SDL_BUTTON_WHEELUP), - [INPUT_BUTTON_WHEEL_DOWN] = SDL_BUTTON(SDL_BUTTON_WHEELDOWN), - }; - static uint32_t prev_state; - - if (prev_state != state) { - qemu_input_update_buttons(dcl->con, bmap, prev_state, state); - prev_state = state; - } - - if (qemu_input_is_absolute()) { - qemu_input_queue_abs(dcl->con, INPUT_AXIS_X, x, - 0, real_screen->w); - qemu_input_queue_abs(dcl->con, INPUT_AXIS_Y, y, - 0, real_screen->h); - } else { - if (guest_cursor) { - x -= guest_x; - y -= guest_y; - guest_x += x; - guest_y += y; - dx = x; - dy = y; - } - qemu_input_queue_rel(dcl->con, INPUT_AXIS_X, dx); - qemu_input_queue_rel(dcl->con, INPUT_AXIS_Y, dy); - } - qemu_input_event_sync(); -} - -static void sdl_scale(int width, int height) -{ - int bpp = real_screen->format->BitsPerPixel; - -#ifdef DEBUG_SDL - printf("SDL: Scaling to %dx%d bpp %d\n", width, height, bpp); -#endif - - if (bpp != 16 && bpp != 32) { - bpp = 32; - } - do_sdl_resize(width, height, bpp); - scaling_active = 1; -} - -static void toggle_full_screen(void) -{ - int width = surface_width(surface); - int height = surface_height(surface); - int bpp = surface_bits_per_pixel(surface); - - gui_fullscreen = !gui_fullscreen; - if (gui_fullscreen) { - gui_saved_width = real_screen->w; - gui_saved_height = real_screen->h; - gui_saved_scaling = scaling_active; - - do_sdl_resize(width, height, bpp); - scaling_active = 0; - - gui_saved_grab = gui_grab; - sdl_grab_start(); - } else { - if (gui_saved_scaling) { - sdl_scale(gui_saved_width, gui_saved_height); - } else { - do_sdl_resize(width, height, 0); - } - if (!gui_saved_grab || !qemu_console_is_graphic(NULL)) { - sdl_grab_end(); - } - } - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); -} - -static void handle_keydown(SDL_Event *ev) -{ - int mod_state; - int keycode; - - if (alt_grab) { - mod_state = (SDL_GetModState() & (gui_grab_code | KMOD_LSHIFT)) == - (gui_grab_code | KMOD_LSHIFT); - } else if (ctrl_grab) { - mod_state = (SDL_GetModState() & KMOD_RCTRL) == KMOD_RCTRL; - } else { - mod_state = (SDL_GetModState() & gui_grab_code) == gui_grab_code; - } - gui_key_modifier_pressed = mod_state; - - if (gui_key_modifier_pressed) { - keycode = sdl_keyevent_to_keycode(&ev->key); - switch (keycode) { - case 0x21: /* 'f' key on US keyboard */ - toggle_full_screen(); - gui_keysym = 1; - break; - case 0x22: /* 'g' key */ - if (!gui_grab) { - if (qemu_console_is_graphic(NULL)) { - sdl_grab_start(); - } - } else if (!gui_fullscreen) { - sdl_grab_end(); - } - gui_keysym = 1; - break; - case 0x16: /* 'u' key on US keyboard */ - if (scaling_active) { - scaling_active = 0; - sdl_switch(dcl, NULL); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); - } - gui_keysym = 1; - break; - case 0x02 ... 0x0a: /* '1' to '9' keys */ - /* Reset the modifiers sent to the current console */ - reset_keys(); - console_select(keycode - 0x02); - gui_keysym = 1; - if (gui_fullscreen) { - break; - } - if (!qemu_console_is_graphic(NULL)) { - /* release grab if going to a text console */ - if (gui_grab) { - sdl_grab_end(); - } else if (absolute_enabled) { - sdl_show_cursor(); - } - } else if (absolute_enabled) { - sdl_hide_cursor(); - absolute_mouse_grab(); - } - break; - case 0x1b: /* '+' */ - case 0x35: /* '-' */ - if (!gui_fullscreen) { - int width = MAX(real_screen->w + (keycode == 0x1b ? 50 : -50), - 160); - int height = (surface_height(surface) * width) / - surface_width(surface); - - sdl_scale(width, height); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); - gui_keysym = 1; - } - default: - break; - } - } else if (!qemu_console_is_graphic(NULL)) { - int keysym = 0; - - if (ev->key.keysym.mod & (KMOD_LCTRL | KMOD_RCTRL)) { - switch (ev->key.keysym.sym) { - case SDLK_UP: - keysym = QEMU_KEY_CTRL_UP; - break; - case SDLK_DOWN: - keysym = QEMU_KEY_CTRL_DOWN; - break; - case SDLK_LEFT: - keysym = QEMU_KEY_CTRL_LEFT; - break; - case SDLK_RIGHT: - keysym = QEMU_KEY_CTRL_RIGHT; - break; - case SDLK_HOME: - keysym = QEMU_KEY_CTRL_HOME; - break; - case SDLK_END: - keysym = QEMU_KEY_CTRL_END; - break; - case SDLK_PAGEUP: - keysym = QEMU_KEY_CTRL_PAGEUP; - break; - case SDLK_PAGEDOWN: - keysym = QEMU_KEY_CTRL_PAGEDOWN; - break; - default: - break; - } - } else { - switch (ev->key.keysym.sym) { - case SDLK_UP: - keysym = QEMU_KEY_UP; - break; - case SDLK_DOWN: - keysym = QEMU_KEY_DOWN; - break; - case SDLK_LEFT: - keysym = QEMU_KEY_LEFT; - break; - case SDLK_RIGHT: - keysym = QEMU_KEY_RIGHT; - break; - case SDLK_HOME: - keysym = QEMU_KEY_HOME; - break; - case SDLK_END: - keysym = QEMU_KEY_END; - break; - case SDLK_PAGEUP: - keysym = QEMU_KEY_PAGEUP; - break; - case SDLK_PAGEDOWN: - keysym = QEMU_KEY_PAGEDOWN; - break; - case SDLK_BACKSPACE: - keysym = QEMU_KEY_BACKSPACE; - break; - case SDLK_DELETE: - keysym = QEMU_KEY_DELETE; - break; - default: - break; - } - } - if (keysym) { - kbd_put_keysym(keysym); - } else if (ev->key.keysym.unicode != 0) { - kbd_put_keysym(ev->key.keysym.unicode); - } - } - if (qemu_console_is_graphic(NULL) && !gui_keysym) { - sdl_process_key(&ev->key); - } -} - -static void handle_keyup(SDL_Event *ev) -{ - int mod_state; - - if (!alt_grab) { - mod_state = (ev->key.keysym.mod & gui_grab_code); - } else { - mod_state = (ev->key.keysym.mod & (gui_grab_code | KMOD_LSHIFT)); - } - if (!mod_state && gui_key_modifier_pressed) { - gui_key_modifier_pressed = 0; - gui_keysym = 0; - } - if (qemu_console_is_graphic(NULL) && !gui_keysym) { - sdl_process_key(&ev->key); - } -} - -static void handle_mousemotion(SDL_Event *ev) -{ - int max_x, max_y; - - if (qemu_console_is_graphic(NULL) && - (qemu_input_is_absolute() || absolute_enabled)) { - max_x = real_screen->w - 1; - max_y = real_screen->h - 1; - if (gui_grab && (ev->motion.x == 0 || ev->motion.y == 0 || - ev->motion.x == max_x || ev->motion.y == max_y)) { - sdl_grab_end(); - } - if (!gui_grab && - (ev->motion.x > 0 && ev->motion.x < max_x && - ev->motion.y > 0 && ev->motion.y < max_y)) { - sdl_grab_start(); - } - } - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { - sdl_send_mouse_event(ev->motion.xrel, ev->motion.yrel, - ev->motion.x, ev->motion.y, ev->motion.state); - } -} - -static void handle_mousebutton(SDL_Event *ev) -{ - int buttonstate = SDL_GetMouseState(NULL, NULL); - SDL_MouseButtonEvent *bev; - - if (!qemu_console_is_graphic(NULL)) { - return; - } - - bev = &ev->button; - if (!gui_grab && !qemu_input_is_absolute()) { - if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { - /* start grabbing all events */ - sdl_grab_start(); - } - } else { - if (ev->type == SDL_MOUSEBUTTONDOWN) { - buttonstate |= SDL_BUTTON(bev->button); - } else { - buttonstate &= ~SDL_BUTTON(bev->button); - } - sdl_send_mouse_event(0, 0, bev->x, bev->y, buttonstate); - } -} - -static void handle_activation(SDL_Event *ev) -{ -#ifdef _WIN32 - /* Disable grab if the window no longer has the focus - * (Windows-only workaround) */ - if (gui_grab && ev->active.state == SDL_APPINPUTFOCUS && - !ev->active.gain && !gui_fullscreen) { - sdl_grab_end(); - } -#endif - if (!gui_grab && ev->active.gain && qemu_console_is_graphic(NULL) && - (qemu_input_is_absolute() || absolute_enabled)) { - absolute_mouse_grab(); - } - if (ev->active.state & SDL_APPACTIVE) { - if (ev->active.gain) { - /* Back to default interval */ - update_displaychangelistener(dcl, GUI_REFRESH_INTERVAL_DEFAULT); - } else { - /* Sleeping interval. Not using the long default here as - * sdl_refresh does not only update the guest screen, but - * also checks for gui events. */ - update_displaychangelistener(dcl, 500); - } - } -} - -static void sdl_refresh(DisplayChangeListener *dcl) -{ - SDL_Event ev1, *ev = &ev1; - bool allow_close = true; - int idle = 1; - - if (last_vm_running != runstate_is_running()) { - last_vm_running = runstate_is_running(); - sdl_update_caption(); - } - - graphic_hw_update(NULL); - SDL_EnableUNICODE(!qemu_console_is_graphic(NULL)); - - while (SDL_PollEvent(ev)) { - switch (ev->type) { - case SDL_VIDEOEXPOSE: - 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: - if (opts->has_window_close && !opts->window_close) { - allow_close = false; - } - if (allow_close) { - no_shutdown = 0; - qemu_system_shutdown_request(SHUTDOWN_CAUSE_HOST_UI); - } - 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: - handle_activation(ev); - break; - case SDL_VIDEORESIZE: - sdl_scale(ev->resize.w, ev->resize.h); - graphic_hw_invalidate(NULL); - graphic_hw_update(NULL); - break; - default: - 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, - int x, int y, int on) -{ - if (on) { - if (!guest_cursor) - sdl_show_cursor(); - if (gui_grab || qemu_input_is_absolute() || absolute_enabled) { - SDL_SetCursor(guest_sprite); - if (!qemu_input_is_absolute() && !absolute_enabled) { - SDL_WarpMouse(x, y); - } - } - } else if (gui_grab) - sdl_hide_cursor(); - guest_cursor = on; - guest_x = x, guest_y = y; -} - -static void sdl_mouse_define(DisplayChangeListener *dcl, - QEMUCursor *c) -{ - uint8_t *image, *mask; - int bpl; - - if (guest_sprite) - SDL_FreeCursor(guest_sprite); - - bpl = cursor_get_mono_bpl(c); - image = g_malloc0(bpl * c->height); - mask = g_malloc0(bpl * c->height); - cursor_get_mono_image(c, 0x000000, image); - cursor_get_mono_mask(c, 0, mask); - guest_sprite = SDL_CreateCursor(image, mask, c->width, c->height, - c->hot_x, c->hot_y); - g_free(image); - g_free(mask); - - if (guest_cursor && - (gui_grab || qemu_input_is_absolute() || absolute_enabled)) - SDL_SetCursor(guest_sprite); -} - -static void sdl_cleanup(void) -{ - if (guest_sprite) - SDL_FreeCursor(guest_sprite); - SDL_QuitSubSystem(SDL_INIT_VIDEO); -} - -static const DisplayChangeListenerOps dcl_ops = { - .dpy_name = "sdl", - .dpy_gfx_update = sdl_update, - .dpy_gfx_switch = sdl_switch, - .dpy_gfx_check_format = sdl_check_format, - .dpy_refresh = sdl_refresh, - .dpy_mouse_set = sdl_mouse_warp, - .dpy_cursor_define = sdl_mouse_define, -}; - -static void sdl1_display_init(DisplayState *ds, DisplayOptions *o) -{ - int flags; - uint8_t data = 0; - const SDL_VideoInfo *vi; - SDL_SysWMinfo info; - char *filename; - - assert(o->type == DISPLAY_TYPE_SDL); - opts = o; -#if defined(__APPLE__) - /* always use generic keymaps */ - if (!keyboard_layout) - keyboard_layout = "en-us"; -#endif - if(keyboard_layout) { - kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); - if (!kbd_layout) - exit(1); - } - - g_printerr("Running QEMU with SDL 1.2 is deprecated, and will be removed\n" - "in a future release. Please switch to SDL 2.0 instead\n"); - - if (opts->has_full_screen && opts->full_screen) { - setenv("SDL_VIDEO_ALLOW_SCREENSAVER", "1", 0); - } -#ifdef __linux__ - /* on Linux, SDL may use fbcon|directfb|svgalib when run without - * accessible $DISPLAY to open X11 window. This is often the case - * when qemu is run using sudo. But in this case, and when actually - * run in X11 environment, SDL fights with X11 for the video card, - * making current display unavailable, often until reboot. - * So make x11 the default SDL video driver if this variable is unset. - * This is a bit hackish but saves us from bigger problem. - * Maybe it's a good idea to fix this in SDL instead. - */ - setenv("SDL_VIDEODRIVER", "x11", 0); -#endif - - /* Enable normal up/down events for Caps-Lock and Num-Lock keys. - * This requires SDL >= 1.2.14. */ - setenv("SDL_DISABLE_LOCK_KEYS", "1", 1); - - flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; - if (SDL_Init (flags)) { - fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", - SDL_GetError()); - exit(1); - } - vi = SDL_GetVideoInfo(); - host_format = *(vi->vfmt); - - keycode_map = sdl_get_keymap(&keycode_maplen); - - /* Load a 32x32x4 image. White pixels are transparent. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); - if (filename) { - SDL_Surface *image = SDL_LoadBMP(filename); - if (image) { - uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); - SDL_SetColorKey(image, SDL_SRCCOLORKEY, colorkey); - SDL_WM_SetIcon(image, NULL); - } - g_free(filename); - } - - if (opts->has_full_screen && opts->full_screen) { - gui_fullscreen = 1; - sdl_grab_start(); - } - - dcl = g_new0(DisplayChangeListener, 1); - dcl->ops = &dcl_ops; - register_displaychangelistener(dcl); - - mouse_mode_notifier.notify = sdl_mouse_mode_change; - qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); - - sdl_update_caption(); - SDL_EnableKeyRepeat(250, 50); - gui_grab = 0; - - sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); - sdl_cursor_normal = SDL_GetCursor(); - - memset(&info, 0, sizeof(info)); - SDL_VERSION(&info.version); - if (SDL_GetWMInfo(&info)) { - int i; - for (i = 0; ; i++) { - /* All consoles share the same window */ - QemuConsole *con = qemu_console_lookup_by_index(i); - if (con) { -#if defined(SDL_VIDEO_DRIVER_X11) - qemu_console_set_window_id(con, info.info.x11.wmwindow); -#elif defined(SDL_VIDEO_DRIVER_NANOX) || \ - defined(SDL_VIDEO_DRIVER_WINDIB) || defined(SDL_VIDEO_DRIVER_DDRAW) || \ - defined(SDL_VIDEO_DRIVER_GAPI) || \ - defined(SDL_VIDEO_DRIVER_RISCOS) - qemu_console_set_window_id(con, (int) (uintptr_t) info.window); -#else - qemu_console_set_window_id(con, info.data); -#endif - } else { - break; - } - } - } - - atexit(sdl_cleanup); -} - -static QemuDisplay qemu_display_sdl1 = { - .type = DISPLAY_TYPE_SDL, - .init = sdl1_display_init, -}; - -static void register_sdl1(void) -{ - qemu_display_register(&qemu_display_sdl1); -} - -type_init(register_sdl1); diff --git a/ui/sdl2-2d.c b/ui/sdl2-2d.c index 85484407be..091ecfcc7f 100644 --- a/ui/sdl2-2d.c +++ b/ui/sdl2-2d.c @@ -101,15 +101,24 @@ void sdl2_2d_switch(DisplayChangeListener *dcl, case PIXMAN_r5g6b5: format = SDL_PIXELFORMAT_RGB565; break; + case PIXMAN_a8r8g8b8: case PIXMAN_x8r8g8b8: format = SDL_PIXELFORMAT_ARGB8888; break; + case PIXMAN_a8b8g8r8: + case PIXMAN_x8b8g8r8: + format = SDL_PIXELFORMAT_ABGR8888; + break; + case PIXMAN_r8g8b8a8: case PIXMAN_r8g8b8x8: format = SDL_PIXELFORMAT_RGBA8888; break; case PIXMAN_b8g8r8x8: format = SDL_PIXELFORMAT_BGRX8888; break; + case PIXMAN_b8g8r8a8: + format = SDL_PIXELFORMAT_BGRA8888; + break; default: g_assert_not_reached(); } @@ -149,7 +158,13 @@ bool sdl2_2d_check_format(DisplayChangeListener *dcl, * the native ones. Thes are the ones I have tested. */ return (format == PIXMAN_x8r8g8b8 || + format == PIXMAN_a8r8g8b8 || + format == PIXMAN_a8b8g8r8 || + format == PIXMAN_x8b8g8r8 || format == PIXMAN_b8g8r8x8 || + format == PIXMAN_b8g8r8a8 || + format == PIXMAN_r8g8b8x8 || + format == PIXMAN_r8g8b8a8 || format == PIXMAN_x1r5g5b5 || format == PIXMAN_r5g6b5); } diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index 83b71853d1..1bf4542d8d 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -124,6 +124,11 @@ void sdl2_gl_redraw(struct sdl2_console *scon) { assert(scon->opengl); + if (scon->scanout_mode) { + /* sdl2_gl_scanout_flush actually only care about + * the first argument. */ + return sdl2_gl_scanout_flush(&scon->dcl, 0, 0, 0, 0); + } if (scon->surface) { sdl2_gl_render_surface(scon); } diff --git a/ui/sdl2-input.c b/ui/sdl2-input.c index 1378b63dd9..fb345f45fb 100644 --- a/ui/sdl2-input.c +++ b/ui/sdl2-input.c @@ -30,63 +30,23 @@ #include "ui/sdl2.h" #include "sysemu/sysemu.h" -static uint8_t modifiers_state[SDL_NUM_SCANCODES]; - -void sdl2_reset_keys(struct sdl2_console *scon) -{ - QemuConsole *con = scon ? scon->dcl.con : NULL; - int i; - - for (i = 0 ; - i < SDL_NUM_SCANCODES && i < qemu_input_map_usb_to_qcode_len ; - i++) { - if (modifiers_state[i]) { - int qcode = qemu_input_map_usb_to_qcode[i]; - qemu_input_event_send_key_qcode(con, qcode, false); - modifiers_state[i] = 0; - } - } -} - void sdl2_process_key(struct sdl2_console *scon, SDL_KeyboardEvent *ev) { int qcode; - QemuConsole *con = scon ? scon->dcl.con : NULL; + QemuConsole *con = scon->dcl.con; if (ev->keysym.scancode >= qemu_input_map_usb_to_qcode_len) { return; } - qcode = qemu_input_map_usb_to_qcode[ev->keysym.scancode]; - - /* modifier state tracking */ - switch (ev->keysym.scancode) { - case SDL_SCANCODE_LCTRL: - case SDL_SCANCODE_LSHIFT: - case SDL_SCANCODE_LALT: - case SDL_SCANCODE_LGUI: - case SDL_SCANCODE_RCTRL: - case SDL_SCANCODE_RSHIFT: - case SDL_SCANCODE_RALT: - case SDL_SCANCODE_RGUI: - if (ev->type == SDL_KEYUP) { - modifiers_state[ev->keysym.scancode] = 0; - } else { - modifiers_state[ev->keysym.scancode] = 1; - } - break; - default: - /* nothing */ - break; - } + qkbd_state_key_event(scon->kbd, qcode, ev->type == SDL_KEYDOWN); if (!qemu_console_is_graphic(con)) { - bool ctrl = (modifiers_state[SDL_SCANCODE_LCTRL] || - modifiers_state[SDL_SCANCODE_RCTRL]); + bool ctrl = qkbd_state_modifier_get(scon->kbd, QKBD_MOD_CTRL); if (ev->type == SDL_KEYDOWN) { - switch (ev->keysym.scancode) { - case SDL_SCANCODE_RETURN: + switch (qcode) { + case Q_KEY_CODE_RET: kbd_put_keysym_console(con, '\n'); break; default: @@ -94,8 +54,5 @@ void sdl2_process_key(struct sdl2_console *scon, break; } } - } else { - qemu_input_event_send_key_qcode(con, qcode, - ev->type == SDL_KEYDOWN); } } diff --git a/ui/sdl2.c b/ui/sdl2.c index b7eed9e997..d95d2ce794 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -39,7 +39,6 @@ static int gui_grab; /* if true, all keyboard/mouse events are grabbed */ static int gui_saved_grab; static int gui_fullscreen; -static int gui_keysym; static int gui_grab_code = KMOD_LALT | KMOD_LCTRL; static SDL_Cursor *sdl_cursor_normal; static SDL_Cursor *sdl_cursor_hidden; @@ -337,6 +336,7 @@ static void handle_keydown(SDL_Event *ev) int win; struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); int gui_key_modifier_pressed = get_mod_state(); + int gui_keysym = 0; if (!scon->ignore_hotkeys && gui_key_modifier_pressed && !ev->key.repeat) { switch (ev->key.keysym.scancode) { @@ -417,16 +417,9 @@ static void handle_keydown(SDL_Event *ev) static void handle_keyup(SDL_Event *ev) { struct sdl2_console *scon = get_scon_from_window(ev->key.windowID); - int gui_key_modifier_pressed = get_mod_state(); scon->ignore_hotkeys = false; - - if (!gui_key_modifier_pressed) { - gui_keysym = 0; - } - if (!gui_keysym) { - sdl2_process_key(scon, &ev->key); - } + sdl2_process_key(scon, &ev->key); } static void handle_textinput(SDL_Event *ev) @@ -768,11 +761,10 @@ static void sdl2_display_early_init(DisplayOptions *o) static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) { - int flags; uint8_t data = 0; - char *filename; int i; SDL_SysWMinfo info; + SDL_Surface *icon = NULL; assert(o->type == DISPLAY_TYPE_SDL); @@ -789,16 +781,20 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) setenv("SDL_VIDEODRIVER", "x11", 0); #endif - flags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE; - if (SDL_Init(flags)) { + if (SDL_Init(SDL_INIT_VIDEO)) { fprintf(stderr, "Could not initialize SDL(%s) - exiting\n", SDL_GetError()); exit(1); } +#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* only available since SDL 2.0.8 */ + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); +#endif SDL_SetHint(SDL_HINT_GRAB_KEYBOARD, "1"); memset(&info, 0, sizeof(info)); SDL_VERSION(&info.version); + gui_fullscreen = o->has_full_screen && o->full_screen; + for (i = 0;; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); if (!con) { @@ -813,7 +809,8 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) for (i = 0; i < sdl2_num_outputs; i++) { QemuConsole *con = qemu_console_lookup_by_index(i); assert(con != NULL); - if (!qemu_console_is_graphic(con)) { + if (!qemu_console_is_graphic(con) && + qemu_console_get_index(con) != 0) { sdl2_console[i].hidden = true; } sdl2_console[i].idx = i; @@ -826,6 +823,7 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) sdl2_console[i].dcl.ops = &dcl_2d_ops; #endif sdl2_console[i].dcl.con = con; + sdl2_console[i].kbd = qkbd_state_init(con); register_displaychangelistener(&sdl2_console[i].dcl); #if defined(SDL_VIDEO_DRIVER_WINDOWS) || defined(SDL_VIDEO_DRIVER_X11) @@ -839,29 +837,28 @@ static void sdl2_display_init(DisplayState *ds, DisplayOptions *o) #endif } +#ifdef CONFIG_SDL_IMAGE + icon = IMG_Load(CONFIG_QEMU_ICONDIR "/hicolor/128x128/apps/qemu.png"); +#else /* Load a 32x32x4 image. White pixels are transparent. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, "qemu-icon.bmp"); - if (filename) { - SDL_Surface *image = SDL_LoadBMP(filename); - if (image) { - uint32_t colorkey = SDL_MapRGB(image->format, 255, 255, 255); - SDL_SetColorKey(image, SDL_TRUE, colorkey); - SDL_SetWindowIcon(sdl2_console[0].real_window, image); - } - g_free(filename); + icon = SDL_LoadBMP(CONFIG_QEMU_ICONDIR "/hicolor/32x32/apps/qemu.bmp"); + if (icon) { + uint32_t colorkey = SDL_MapRGB(icon->format, 255, 255, 255); + SDL_SetColorKey(icon, SDL_TRUE, colorkey); + } +#endif + if (icon) { + SDL_SetWindowIcon(sdl2_console[0].real_window, icon); } - if (sdl2_console->opts->has_full_screen && - sdl2_console->opts->full_screen) { - gui_fullscreen = 1; + gui_grab = 0; + if (gui_fullscreen) { sdl_grab_start(0); } mouse_mode_notifier.notify = sdl_mouse_mode_change; qemu_add_mouse_mode_change_notifier(&mouse_mode_notifier); - gui_grab = 0; - sdl_cursor_hidden = SDL_CreateCursor(&data, &data, 8, 1, 0, 0); sdl_cursor_normal = SDL_GetCursor(); diff --git a/ui/sdl_keysym.h b/ui/sdl_keysym.h deleted file mode 100644 index 599d9fc64d..0000000000 --- a/ui/sdl_keysym.h +++ /dev/null @@ -1,278 +0,0 @@ - -#include "keymaps.h" - -static const name2keysym_t name2keysym[]={ -/* ascii */ - { "space", 0x020}, - { "exclam", 0x021}, - { "quotedbl", 0x022}, - { "numbersign", 0x023}, - { "dollar", 0x024}, - { "percent", 0x025}, - { "ampersand", 0x026}, - { "apostrophe", 0x027}, - { "parenleft", 0x028}, - { "parenright", 0x029}, - { "asterisk", 0x02a}, - { "plus", 0x02b}, - { "comma", 0x02c}, - { "minus", 0x02d}, - { "period", 0x02e}, - { "slash", 0x02f}, - { "0", 0x030}, - { "1", 0x031}, - { "2", 0x032}, - { "3", 0x033}, - { "4", 0x034}, - { "5", 0x035}, - { "6", 0x036}, - { "7", 0x037}, - { "8", 0x038}, - { "9", 0x039}, - { "colon", 0x03a}, - { "semicolon", 0x03b}, - { "less", 0x03c}, - { "equal", 0x03d}, - { "greater", 0x03e}, - { "question", 0x03f}, - { "at", 0x040}, - { "A", 0x041}, - { "B", 0x042}, - { "C", 0x043}, - { "D", 0x044}, - { "E", 0x045}, - { "F", 0x046}, - { "G", 0x047}, - { "H", 0x048}, - { "I", 0x049}, - { "J", 0x04a}, - { "K", 0x04b}, - { "L", 0x04c}, - { "M", 0x04d}, - { "N", 0x04e}, - { "O", 0x04f}, - { "P", 0x050}, - { "Q", 0x051}, - { "R", 0x052}, - { "S", 0x053}, - { "T", 0x054}, - { "U", 0x055}, - { "V", 0x056}, - { "W", 0x057}, - { "X", 0x058}, - { "Y", 0x059}, - { "Z", 0x05a}, - { "bracketleft", 0x05b}, - { "backslash", 0x05c}, - { "bracketright", 0x05d}, - { "asciicircum", 0x05e}, - { "underscore", 0x05f}, - { "grave", 0x060}, - { "a", 0x061}, - { "b", 0x062}, - { "c", 0x063}, - { "d", 0x064}, - { "e", 0x065}, - { "f", 0x066}, - { "g", 0x067}, - { "h", 0x068}, - { "i", 0x069}, - { "j", 0x06a}, - { "k", 0x06b}, - { "l", 0x06c}, - { "m", 0x06d}, - { "n", 0x06e}, - { "o", 0x06f}, - { "p", 0x070}, - { "q", 0x071}, - { "r", 0x072}, - { "s", 0x073}, - { "t", 0x074}, - { "u", 0x075}, - { "v", 0x076}, - { "w", 0x077}, - { "x", 0x078}, - { "y", 0x079}, - { "z", 0x07a}, - { "braceleft", 0x07b}, - { "bar", 0x07c}, - { "braceright", 0x07d}, - { "asciitilde", 0x07e}, - -/* latin 1 extensions */ -{ "nobreakspace", 0x0a0}, -{ "exclamdown", 0x0a1}, -{ "cent", 0x0a2}, -{ "sterling", 0x0a3}, -{ "currency", 0x0a4}, -{ "yen", 0x0a5}, -{ "brokenbar", 0x0a6}, -{ "section", 0x0a7}, -{ "diaeresis", 0x0a8}, -{ "copyright", 0x0a9}, -{ "ordfeminine", 0x0aa}, -{ "guillemotleft", 0x0ab}, -{ "notsign", 0x0ac}, -{ "hyphen", 0x0ad}, -{ "registered", 0x0ae}, -{ "macron", 0x0af}, -{ "degree", 0x0b0}, -{ "plusminus", 0x0b1}, -{ "twosuperior", 0x0b2}, -{ "threesuperior", 0x0b3}, -{ "acute", 0x0b4}, -{ "mu", 0x0b5}, -{ "paragraph", 0x0b6}, -{ "periodcentered", 0x0b7}, -{ "cedilla", 0x0b8}, -{ "onesuperior", 0x0b9}, -{ "masculine", 0x0ba}, -{ "guillemotright", 0x0bb}, -{ "onequarter", 0x0bc}, -{ "onehalf", 0x0bd}, -{ "threequarters", 0x0be}, -{ "questiondown", 0x0bf}, -{ "Agrave", 0x0c0}, -{ "Aacute", 0x0c1}, -{ "Acircumflex", 0x0c2}, -{ "Atilde", 0x0c3}, -{ "Adiaeresis", 0x0c4}, -{ "Aring", 0x0c5}, -{ "AE", 0x0c6}, -{ "Ccedilla", 0x0c7}, -{ "Egrave", 0x0c8}, -{ "Eacute", 0x0c9}, -{ "Ecircumflex", 0x0ca}, -{ "Ediaeresis", 0x0cb}, -{ "Igrave", 0x0cc}, -{ "Iacute", 0x0cd}, -{ "Icircumflex", 0x0ce}, -{ "Idiaeresis", 0x0cf}, -{ "ETH", 0x0d0}, -{ "Eth", 0x0d0}, -{ "Ntilde", 0x0d1}, -{ "Ograve", 0x0d2}, -{ "Oacute", 0x0d3}, -{ "Ocircumflex", 0x0d4}, -{ "Otilde", 0x0d5}, -{ "Odiaeresis", 0x0d6}, -{ "multiply", 0x0d7}, -{ "Ooblique", 0x0d8}, -{ "Oslash", 0x0d8}, -{ "Ugrave", 0x0d9}, -{ "Uacute", 0x0da}, -{ "Ucircumflex", 0x0db}, -{ "Udiaeresis", 0x0dc}, -{ "Yacute", 0x0dd}, -{ "THORN", 0x0de}, -{ "Thorn", 0x0de}, -{ "ssharp", 0x0df}, -{ "agrave", 0x0e0}, -{ "aacute", 0x0e1}, -{ "acircumflex", 0x0e2}, -{ "atilde", 0x0e3}, -{ "adiaeresis", 0x0e4}, -{ "aring", 0x0e5}, -{ "ae", 0x0e6}, -{ "ccedilla", 0x0e7}, -{ "egrave", 0x0e8}, -{ "eacute", 0x0e9}, -{ "ecircumflex", 0x0ea}, -{ "ediaeresis", 0x0eb}, -{ "igrave", 0x0ec}, -{ "iacute", 0x0ed}, -{ "icircumflex", 0x0ee}, -{ "idiaeresis", 0x0ef}, -{ "eth", 0x0f0}, -{ "ntilde", 0x0f1}, -{ "ograve", 0x0f2}, -{ "oacute", 0x0f3}, -{ "ocircumflex", 0x0f4}, -{ "otilde", 0x0f5}, -{ "odiaeresis", 0x0f6}, -{ "division", 0x0f7}, -{ "oslash", 0x0f8}, -{ "ooblique", 0x0f8}, -{ "ugrave", 0x0f9}, -{ "uacute", 0x0fa}, -{ "ucircumflex", 0x0fb}, -{ "udiaeresis", 0x0fc}, -{ "yacute", 0x0fd}, -{ "thorn", 0x0fe}, -{ "ydiaeresis", 0x0ff}, -#if SDL_MAJOR_VERSION == 1 -{"EuroSign", SDLK_EURO}, - - /* modifiers */ -{"Control_L", SDLK_LCTRL}, -{"Control_R", SDLK_RCTRL}, -{"Alt_L", SDLK_LALT}, -{"Alt_R", SDLK_RALT}, -{"Caps_Lock", SDLK_CAPSLOCK}, -{"Meta_L", SDLK_LMETA}, -{"Meta_R", SDLK_RMETA}, -{"Shift_L", SDLK_LSHIFT}, -{"Shift_R", SDLK_RSHIFT}, -{"Super_L", SDLK_LSUPER}, -{"Super_R", SDLK_RSUPER}, - - /* special keys */ -{"BackSpace", SDLK_BACKSPACE}, -{"Tab", SDLK_TAB}, -{"Return", SDLK_RETURN}, -{"Right", SDLK_RIGHT}, -{"Left", SDLK_LEFT}, -{"Up", SDLK_UP}, -{"Down", SDLK_DOWN}, -{"Page_Down", SDLK_PAGEDOWN}, -{"Page_Up", SDLK_PAGEUP}, -{"Insert", SDLK_INSERT}, -{"Delete", SDLK_DELETE}, -{"Home", SDLK_HOME}, -{"End", SDLK_END}, -{"Scroll_Lock", SDLK_SCROLLOCK}, -{"F1", SDLK_F1}, -{"F2", SDLK_F2}, -{"F3", SDLK_F3}, -{"F4", SDLK_F4}, -{"F5", SDLK_F5}, -{"F6", SDLK_F6}, -{"F7", SDLK_F7}, -{"F8", SDLK_F8}, -{"F9", SDLK_F9}, -{"F10", SDLK_F10}, -{"F11", SDLK_F11}, -{"F12", SDLK_F12}, -{"F13", SDLK_F13}, -{"F14", SDLK_F14}, -{"F15", SDLK_F15}, -{"Sys_Req", SDLK_SYSREQ}, -{"KP_0", SDLK_KP0}, -{"KP_1", SDLK_KP1}, -{"KP_2", SDLK_KP2}, -{"KP_3", SDLK_KP3}, -{"KP_4", SDLK_KP4}, -{"KP_5", SDLK_KP5}, -{"KP_6", SDLK_KP6}, -{"KP_7", SDLK_KP7}, -{"KP_8", SDLK_KP8}, -{"KP_9", SDLK_KP9}, -{"KP_Add", SDLK_KP_PLUS}, -{"KP_Decimal", SDLK_KP_PERIOD}, -{"KP_Divide", SDLK_KP_DIVIDE}, -{"KP_Enter", SDLK_KP_ENTER}, -{"KP_Equal", SDLK_KP_EQUALS}, -{"KP_Multiply", SDLK_KP_MULTIPLY}, -{"KP_Subtract", SDLK_KP_MINUS}, -{"help", SDLK_HELP}, -{"Menu", SDLK_MENU}, -{"Power", SDLK_POWER}, -{"Print", SDLK_PRINT}, -{"Mode_switch", SDLK_MODE}, -{"Multi_Key", SDLK_COMPOSE}, -{"Num_Lock", SDLK_NUMLOCK}, -{"Pause", SDLK_PAUSE}, -{"Escape", SDLK_ESCAPE}, -#endif -{NULL, 0}, -}; diff --git a/ui/sdl_zoom.c b/ui/sdl_zoom.c deleted file mode 100644 index b96196bac5..0000000000 --- a/ui/sdl_zoom.c +++ /dev/null @@ -1,93 +0,0 @@ -/* - * SDL_zoom - surface scaling - * - * Copyright (c) 2009 Citrix Systems, Inc. - * - * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. - * Modifications by Stefano Stabellini. - * - * 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 "sdl_zoom.h" - -static void sdl_zoom_rgb16(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect); -static void sdl_zoom_rgb32(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect); - -#define BPP 32 -#include "sdl_zoom_template.h" -#undef BPP -#define BPP 16 -#include "sdl_zoom_template.h" -#undef BPP - -int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, int smooth, - SDL_Rect *in_rect) -{ - SDL_Rect zoom, src_rect; - int extra; - - /* Grow the size of the modified rectangle to avoid edge artefacts */ - src_rect.x = (in_rect->x > 0) ? (in_rect->x - 1) : 0; - src_rect.y = (in_rect->y > 0) ? (in_rect->y - 1) : 0; - - src_rect.w = in_rect->w + 1; - if (src_rect.x + src_rect.w > src_sfc->w) - src_rect.w = src_sfc->w - src_rect.x; - - src_rect.h = in_rect->h + 1; - if (src_rect.y + src_rect.h > src_sfc->h) - src_rect.h = src_sfc->h - src_rect.y; - - /* (x,y) : round down */ - zoom.x = (int)(((float)(src_rect.x * dst_sfc->w)) / (float)(src_sfc->w)); - zoom.y = (int)(((float)(src_rect.y * dst_sfc->h)) / (float)(src_sfc->h)); - - /* (w,h) : round up */ - zoom.w = (int)( ((double)((src_rect.w * dst_sfc->w) + (src_sfc->w - 1))) / - (double)(src_sfc->w)); - - zoom.h = (int)( ((double)((src_rect.h * dst_sfc->h) + (src_sfc->h - 1))) / - (double)(src_sfc->h)); - - /* Account for any (x,y) rounding by adding one-source-pixel's worth - * of destination pixels and then edge checking. - */ - - extra = ((dst_sfc->w-1) / src_sfc->w) + 1; - - if ((zoom.x + zoom.w) < (dst_sfc->w - extra)) - zoom.w += extra; - else - zoom.w = dst_sfc->w - zoom.x; - - extra = ((dst_sfc->h-1) / src_sfc->h) + 1; - - if ((zoom.y + zoom.h) < (dst_sfc->h - extra)) - zoom.h += extra; - else - zoom.h = dst_sfc->h - zoom.y; - - /* The rectangle (zoom.x, zoom.y, zoom.w, zoom.h) is the area on the - * destination surface that needs to be updated. - */ - if (src_sfc->format->BitsPerPixel == 32) - sdl_zoom_rgb32(src_sfc, dst_sfc, smooth, &zoom); - else if (src_sfc->format->BitsPerPixel == 16) - sdl_zoom_rgb16(src_sfc, dst_sfc, smooth, &zoom); - else { - fprintf(stderr, "pixel format not supported\n"); - return -1; - } - - /* Return the rectangle of the update to the caller */ - *in_rect = zoom; - - return 0; -} - diff --git a/ui/sdl_zoom.h b/ui/sdl_zoom.h deleted file mode 100644 index 39696ddb08..0000000000 --- a/ui/sdl_zoom.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * SDL_zoom - surface scaling - * - * Copyright (c) 2009 Citrix Systems, Inc. - * - * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. - * Modifications by Stefano Stabellini. - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef SDL_ZOOM_H -#define SDL_ZOOM_H - -#include - -#define SMOOTHING_OFF 0 -#define SMOOTHING_ON 1 - -int sdl_zoom_blit(SDL_Surface *src_sfc, SDL_Surface *dst_sfc, - int smooth, SDL_Rect *src_rect); - -#endif /* SDL_ZOOM_H */ diff --git a/ui/sdl_zoom_template.h b/ui/sdl_zoom_template.h deleted file mode 100644 index 6a424adfb4..0000000000 --- a/ui/sdl_zoom_template.h +++ /dev/null @@ -1,219 +0,0 @@ -/* - * SDL_zoom_template - surface scaling - * - * Copyright (c) 2009 Citrix Systems, Inc. - * - * Derived from: SDL_rotozoom, LGPL (c) A. Schiffler from the SDL_gfx library. - * Modifications by Stefano Stabellini. - * - * This work is licensed under the terms of the GNU GPL version 2. - * See the COPYING file in the top-level directory. - * - */ - -#if BPP == 16 -#define SDL_TYPE Uint16 -#elif BPP == 32 -#define SDL_TYPE Uint32 -#else -#error unsupport depth -#endif - -/* - * Simple helper functions to make the code looks nicer - * - * Assume spf = source SDL_PixelFormat - * dpf = dest SDL_PixelFormat - * - */ -#define getRed(color) (((color) & spf->Rmask) >> spf->Rshift) -#define getGreen(color) (((color) & spf->Gmask) >> spf->Gshift) -#define getBlue(color) (((color) & spf->Bmask) >> spf->Bshift) -#define getAlpha(color) (((color) & spf->Amask) >> spf->Ashift) - -#define setRed(r, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Rmask))) + \ - (((r) & (dpf->Rmask >> dpf->Rshift)) << dpf->Rshift); \ -} while (0) - -#define setGreen(g, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Gmask))) + \ - (((g) & (dpf->Gmask >> dpf->Gshift)) << dpf->Gshift); \ -} while (0) - -#define setBlue(b, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Bmask))) + \ - (((b) & (dpf->Bmask >> dpf->Bshift)) << dpf->Bshift); \ -} while (0) - -#define setAlpha(a, pcolor) do { \ - *pcolor = ((*pcolor) & (~(dpf->Amask))) + \ - (((a) & (dpf->Amask >> dpf->Ashift)) << dpf->Ashift); \ -} while (0) - -static void glue(sdl_zoom_rgb, BPP)(SDL_Surface *src, SDL_Surface *dst, int smooth, - SDL_Rect *dst_rect) -{ - int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, sstep_jump; - SDL_TYPE *c00, *c01, *c10, *c11, *sp, *csp, *dp; - int d_gap; - SDL_PixelFormat *spf = src->format; - SDL_PixelFormat *dpf = dst->format; - - if (smooth) { - /* For interpolation: assume source dimension is one pixel. - * Smaller here to avoid overflow on right and bottom edge. - */ - sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); - sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); - } else { - sx = (int) (65536.0 * (float) src->w / (float) dst->w); - sy = (int) (65536.0 * (float) src->h / (float) dst->h); - } - - sax = g_new(int, dst->w + 1); - say = g_new(int, dst->h + 1); - - sp = csp = (SDL_TYPE *) src->pixels; - dp = (SDL_TYPE *) (dst->pixels + dst_rect->y * dst->pitch + - dst_rect->x * dst->format->BytesPerPixel); - - csx = 0; - csax = sax; - for (x = 0; x <= dst->w; x++) { - *csax = csx; - csax++; - csx &= 0xffff; - csx += sx; - } - csy = 0; - csay = say; - for (y = 0; y <= dst->h; y++) { - *csay = csy; - csay++; - csy &= 0xffff; - csy += sy; - } - - d_gap = dst->pitch - dst_rect->w * dst->format->BytesPerPixel; - - if (smooth) { - csay = say; - for (y = 0; y < dst_rect->y; y++) { - csay++; - sstep = (*csay >> 16) * src->pitch; - csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); - } - - /* Calculate sstep_jump */ - csax = sax; - sstep_jump = 0; - for (x = 0; x < dst_rect->x; x++) { - csax++; - sstep = (*csax >> 16); - sstep_jump += sstep; - } - - for (y = 0; y < dst_rect->h ; y++) { - /* Setup colour source pointers */ - c00 = csp + sstep_jump; - c01 = c00 + 1; - c10 = (SDL_TYPE *) ((Uint8 *) csp + src->pitch) + sstep_jump; - c11 = c10 + 1; - csax = sax + dst_rect->x; - - for (x = 0; x < dst_rect->w; x++) { - - /* Interpolate colours */ - ex = (*csax & 0xffff); - ey = (*csay & 0xffff); - t1 = ((((getRed(*c01) - getRed(*c00)) * ex) >> 16) + - getRed(*c00)) & (dpf->Rmask >> dpf->Rshift); - t2 = ((((getRed(*c11) - getRed(*c10)) * ex) >> 16) + - getRed(*c10)) & (dpf->Rmask >> dpf->Rshift); - setRed((((t2 - t1) * ey) >> 16) + t1, dp); - t1 = ((((getGreen(*c01) - getGreen(*c00)) * ex) >> 16) + - getGreen(*c00)) & (dpf->Gmask >> dpf->Gshift); - t2 = ((((getGreen(*c11) - getGreen(*c10)) * ex) >> 16) + - getGreen(*c10)) & (dpf->Gmask >> dpf->Gshift); - setGreen((((t2 - t1) * ey) >> 16) + t1, dp); - t1 = ((((getBlue(*c01) - getBlue(*c00)) * ex) >> 16) + - getBlue(*c00)) & (dpf->Bmask >> dpf->Bshift); - t2 = ((((getBlue(*c11) - getBlue(*c10)) * ex) >> 16) + - getBlue(*c10)) & (dpf->Bmask >> dpf->Bshift); - setBlue((((t2 - t1) * ey) >> 16) + t1, dp); - t1 = ((((getAlpha(*c01) - getAlpha(*c00)) * ex) >> 16) + - getAlpha(*c00)) & (dpf->Amask >> dpf->Ashift); - t2 = ((((getAlpha(*c11) - getAlpha(*c10)) * ex) >> 16) + - getAlpha(*c10)) & (dpf->Amask >> dpf->Ashift); - setAlpha((((t2 - t1) * ey) >> 16) + t1, dp); - - /* Advance source pointers */ - csax++; - sstep = (*csax >> 16); - c00 += sstep; - c01 += sstep; - c10 += sstep; - c11 += sstep; - /* Advance destination pointer */ - dp++; - } - /* Advance source pointer */ - csay++; - csp = (SDL_TYPE *) ((Uint8 *) csp + (*csay >> 16) * src->pitch); - /* Advance destination pointers */ - dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); - } - - - } else { - csay = say; - - for (y = 0; y < dst_rect->y; y++) { - csay++; - sstep = (*csay >> 16) * src->pitch; - csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); - } - - /* Calculate sstep_jump */ - csax = sax; - sstep_jump = 0; - for (x = 0; x < dst_rect->x; x++) { - csax++; - sstep = (*csax >> 16); - sstep_jump += sstep; - } - - for (y = 0 ; y < dst_rect->h ; y++) { - sp = csp + sstep_jump; - csax = sax + dst_rect->x; - - for (x = 0; x < dst_rect->w; x++) { - - /* Draw */ - *dp = *sp; - - /* Advance source pointers */ - csax++; - sstep = (*csax >> 16); - sp += sstep; - - /* Advance destination pointer */ - dp++; - } - /* Advance source pointers */ - csay++; - sstep = (*csay >> 16) * src->pitch; - csp = (SDL_TYPE *) ((Uint8 *) csp + sstep); - - /* Advance destination pointer */ - dp = (SDL_TYPE *) ((Uint8 *) dp + d_gap); - } - } - - g_free(sax); - g_free(say); -} - -#undef SDL_TYPE - diff --git a/ui/spice-app.c b/ui/spice-app.c new file mode 100644 index 0000000000..925b27b708 --- /dev/null +++ b/ui/spice-app.c @@ -0,0 +1,202 @@ +/* + * QEMU external Spice client display driver + * + * Copyright (c) 2018 Marc-André Lureau + * + * 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 "ui/console.h" +#include "qemu/config-file.h" +#include "qemu/option.h" +#include "qemu/cutils.h" +#include "qapi/error.h" +#include "io/channel-command.h" +#include "chardev/spice.h" +#include "sysemu/sysemu.h" + +static const char *tmp_dir; +static char *app_dir; +static char *sock_path; + +typedef struct VCChardev { + SpiceChardev parent; +} VCChardev; + +#define TYPE_CHARDEV_VC "chardev-vc" +#define VC_CHARDEV(obj) OBJECT_CHECK(VCChardev, (obj), TYPE_CHARDEV_VC) + +static ChardevBackend * +chr_spice_backend_new(void) +{ + ChardevBackend *be = g_new0(ChardevBackend, 1); + + be->type = CHARDEV_BACKEND_KIND_SPICEPORT; + be->u.spiceport.data = g_new0(ChardevSpicePort, 1); + + return be; +} + +static void vc_chr_open(Chardev *chr, + ChardevBackend *backend, + bool *be_opened, + Error **errp) +{ + ChardevBackend *be; + const char *fqdn = NULL; + + if (strstart(chr->label, "serial", NULL)) { + fqdn = "org.qemu.console.serial.0"; + } else if (strstart(chr->label, "parallel", NULL)) { + fqdn = "org.qemu.console.parallel.0"; + } else if (strstart(chr->label, "compat_monitor", NULL)) { + fqdn = "org.qemu.monitor.hmp.0"; + } + + be = chr_spice_backend_new(); + be->u.spiceport.data->fqdn = fqdn ? + g_strdup(fqdn) : g_strdup_printf("org.qemu.console.%s", chr->label); + qemu_chr_open_spice_port(chr, be, be_opened, errp); + qapi_free_ChardevBackend(be); +} + +static void vc_chr_set_echo(Chardev *chr, bool echo) +{ + /* TODO: set echo for frontends QMP and qtest */ +} + +static void char_vc_class_init(ObjectClass *oc, void *data) +{ + ChardevClass *cc = CHARDEV_CLASS(oc); + + cc->parse = qemu_chr_parse_vc; + cc->open = vc_chr_open; + cc->chr_set_echo = vc_chr_set_echo; +} + +static const TypeInfo char_vc_type_info = { + .name = TYPE_CHARDEV_VC, + .parent = TYPE_CHARDEV_SPICEPORT, + .instance_size = sizeof(VCChardev), + .class_init = char_vc_class_init, +}; + +static void spice_app_atexit(void) +{ + if (sock_path) { + unlink(sock_path); + } + if (tmp_dir) { + rmdir(tmp_dir); + } + g_free(sock_path); + g_free(app_dir); +} + +static void spice_app_display_early_init(DisplayOptions *opts) +{ + QemuOpts *qopts; + ChardevBackend *be = chr_spice_backend_new(); + GError *err = NULL; + + if (opts->has_full_screen) { + error_report("spice-app full-screen isn't supported yet."); + exit(1); + } + if (opts->has_window_close) { + error_report("spice-app window-close isn't supported yet."); + exit(1); + } + + atexit(spice_app_atexit); + + if (qemu_name) { + app_dir = g_build_filename(g_get_user_runtime_dir(), + "qemu", qemu_name, NULL); + if (g_mkdir_with_parents(app_dir, S_IRWXU) < -1) { + error_report("Failed to create directory %s: %s", + app_dir, strerror(errno)); + exit(1); + } + } else { + app_dir = g_dir_make_tmp(NULL, &err); + tmp_dir = app_dir; + if (err) { + error_report("Failed to create temporary directory: %s", + err->message); + exit(1); + } + } + + type_register(&char_vc_type_info); + + sock_path = g_strjoin("", app_dir, "/", "spice.sock", NULL); + qopts = qemu_opts_create(qemu_find_opts("spice"), NULL, 0, &error_abort); + qemu_opt_set(qopts, "disable-ticketing", "on", &error_abort); + qemu_opt_set(qopts, "unix", "on", &error_abort); + qemu_opt_set(qopts, "addr", sock_path, &error_abort); + qemu_opt_set(qopts, "image-compression", "off", &error_abort); + qemu_opt_set(qopts, "streaming-video", "off", &error_abort); + qemu_opt_set(qopts, "gl", opts->has_gl ? "on" : "off", &error_abort); + display_opengl = opts->has_gl; + + be->u.spiceport.data->fqdn = g_strdup("org.qemu.monitor.qmp.0"); + qemu_chardev_new("org.qemu.monitor.qmp", TYPE_CHARDEV_SPICEPORT, + be, NULL, &error_abort); + qopts = qemu_opts_create(qemu_find_opts("mon"), + NULL, 0, &error_fatal); + qemu_opt_set(qopts, "chardev", "org.qemu.monitor.qmp", &error_abort); + qemu_opt_set(qopts, "mode", "control", &error_abort); + + qapi_free_ChardevBackend(be); +} + +static void spice_app_display_init(DisplayState *ds, DisplayOptions *opts) +{ + GError *err = NULL; + gchar *uri; + + uri = g_strjoin("", "spice+unix://", app_dir, "/", "spice.sock", NULL); + info_report("Launching display with URI: %s", uri); + g_app_info_launch_default_for_uri(uri, NULL, &err); + if (err) { + error_report("Failed to launch %s URI: %s", uri, err->message); + error_report("You need a capable Spice client, " + "such as virt-viewer 8.0"); + exit(1); + } + g_free(uri); +} + +static QemuDisplay qemu_display_spice_app = { + .type = DISPLAY_TYPE_SPICE_APP, + .early_init = spice_app_display_early_init, + .init = spice_app_display_init, +}; + +static void register_spice_app(void) +{ + qemu_display_register(&qemu_display_spice_app); +} + +type_init(register_spice_app); diff --git a/ui/spice-core.c b/ui/spice-core.c index f8c0878529..0632c74e9f 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include -#include #include "sysemu/sysemu.h" #include "ui/qemu-spice.h" @@ -35,6 +34,7 @@ #include "qemu/option.h" #include "migration/misc.h" #include "hw/hw.h" +#include "hw/pci/pci_bus.h" #include "ui/spice-display.h" /* core bits */ @@ -218,8 +218,7 @@ static void channel_event(int event, SpiceChannelEventInfo *info) switch (event) { case SPICE_CHANNEL_EVENT_CONNECTED: qapi_event_send_spice_connected(qapi_SpiceServerInfo_base(server), - qapi_SpiceChannel_base(client), - &error_abort); + qapi_SpiceChannel_base(client)); break; case SPICE_CHANNEL_EVENT_INITIALIZED: if (auth) { @@ -228,13 +227,12 @@ static void channel_event(int event, SpiceChannelEventInfo *info) } add_channel_info(client, info); channel_list_add(info); - qapi_event_send_spice_initialized(server, client, &error_abort); + qapi_event_send_spice_initialized(server, client); break; case SPICE_CHANNEL_EVENT_DISCONNECTED: channel_list_del(info); qapi_event_send_spice_disconnected(qapi_SpiceServerInfo_base(server), - qapi_SpiceChannel_base(client), - &error_abort); + qapi_SpiceChannel_base(client)); break; default: break; @@ -287,7 +285,7 @@ static void migrate_connect_complete_cb(SpiceMigrateInstance *sin) static void migrate_end_complete_cb(SpiceMigrateInstance *sin) { - qapi_event_send_spice_migrate_completed(&error_abort); + qapi_event_send_spice_migrate_completed(); spice_migration_completed = true; } @@ -400,6 +398,7 @@ static SpiceChannelList *qmp_query_spice_channels(void) static QemuOptsList qemu_spice_opts = { .name = "spice", .head = QTAILQ_HEAD_INITIALIZER(qemu_spice_opts.head), + .merge_lists = true, .desc = { { .name = "port", @@ -599,9 +598,9 @@ static int add_channel(void *opaque, const char *name, const char *value, if (strcmp(name, "tls-channel") == 0) { int *tls_port = opaque; if (!*tls_port) { - error_report("spice: tried to setup tls-channel" - " without specifying a TLS port"); - exit(1); + error_setg(errp, "spice: tried to setup tls-channel" + " without specifying a TLS port"); + return -1; } security = SPICE_CHANNEL_SECURITY_SSL; } @@ -617,8 +616,9 @@ static int add_channel(void *opaque, const char *name, const char *value, rc = spice_server_set_channel_security(spice_server, value, security); } if (rc != 0) { - error_report("spice: failed to set channel security for %s", value); - exit(1); + error_setg(errp, "spice: failed to set channel security for %s", + value); + return -1; } return 0; } @@ -628,7 +628,7 @@ static void vm_change_state_handler(void *opaque, int running, { if (running) { qemu_spice_display_start(); - } else { + } else if (state != RUN_STATE_PAUSED) { qemu_spice_display_stop(); } } @@ -746,13 +746,7 @@ void qemu_spice_init(void) } if (qemu_opt_get_bool(opts, "disable-agent-file-xfer", 0)) { -#if SPICE_SERVER_VERSION >= 0x000c04 spice_server_set_agent_file_xfer(spice_server, false); -#else - error_report("this qemu build does not support the " - "\"disable-agent-file-xfer\" option"); - exit(1); -#endif } compression = SPICE_IMAGE_COMPRESS_AUTO_GLZ; @@ -789,9 +783,9 @@ void qemu_spice_init(void) spice_server_set_playback_compression (spice_server, qemu_opt_get_bool(opts, "playback-compression", 1)); - qemu_opt_foreach(opts, add_channel, &tls_port, NULL); + qemu_opt_foreach(opts, add_channel, &tls_port, &error_fatal); - spice_server_set_name(spice_server, qemu_name); + spice_server_set_name(spice_server, qemu_name ?: "QEMU " QEMU_VERSION); spice_server_set_uuid(spice_server, (unsigned char *)&qemu_uuid); seamless_migration = qemu_opt_get_bool(opts, "seamless-migration", 0); @@ -818,9 +812,7 @@ void qemu_spice_init(void) g_free(x509_cert_file); g_free(x509_cacert_file); -#if SPICE_SERVER_VERSION >= 0x000c02 qemu_spice_register_ports(); -#endif #ifdef HAVE_SPICE_GL if (qemu_opt_get_bool(opts, "gl", 0)) { @@ -873,6 +865,56 @@ bool qemu_spice_have_display_interface(QemuConsole *con) return false; } +/* + * Recursively (in reverse order) appends addresses of PCI devices as it moves + * up in the PCI hierarchy. + * + * @returns true on success, false when the buffer wasn't large enough + */ +static bool append_pci_address(char *buf, size_t buf_size, const PCIDevice *pci) +{ + PCIBus *bus = pci_get_bus(pci); + /* + * equivalent to if (!pci_bus_is_root(bus)), but the function is not built + * with PCI_CONFIG=n, avoid using an #ifdef by checking directly + */ + if (bus->parent_dev != NULL) { + append_pci_address(buf, buf_size, bus->parent_dev); + } + + size_t len = strlen(buf); + ssize_t written = snprintf(buf + len, buf_size - len, "/%02x.%x", + PCI_SLOT(pci->devfn), PCI_FUNC(pci->devfn)); + + return written > 0 && written < buf_size - len; +} + +bool qemu_spice_fill_device_address(QemuConsole *con, + char *device_address, + size_t size) +{ + DeviceState *dev = DEVICE(object_property_get_link(OBJECT(con), + "device", + &error_abort)); + PCIDevice *pci = (PCIDevice *) object_dynamic_cast(OBJECT(dev), + TYPE_PCI_DEVICE); + + if (pci == NULL) { + warn_report("Setting device address of a display device to SPICE: " + "Not a PCI device."); + return false; + } + + strncpy(device_address, "pci/0000", size); + if (!append_pci_address(device_address, size, pci)) { + warn_report("Setting device address of a display device to SPICE: " + "Too many PCI devices in the chain."); + return false; + } + + return true; +} + int qemu_spice_add_display_interface(QXLInstance *qxlin, QemuConsole *con) { if (g_slist_find(spice_consoles, con)) { @@ -931,12 +973,20 @@ int qemu_spice_display_add_client(int csock, int skipauth, int tls) void qemu_spice_display_start(void) { + if (spice_display_is_running) { + return; + } + spice_display_is_running = true; spice_server_vm_start(spice_server); } void qemu_spice_display_stop(void) { + if (!spice_display_is_running) { + return; + } + spice_server_vm_stop(spice_server); spice_display_is_running = false; } diff --git a/ui/spice-display.c b/ui/spice-display.c index fe734821dd..a5e26479a8 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -450,27 +450,33 @@ void qemu_spice_display_switch(SimpleSpiceDisplay *ssd, qemu_mutex_unlock(&ssd->lock); } -static void qemu_spice_cursor_refresh_unlocked(SimpleSpiceDisplay *ssd) -{ - if (ssd->cursor) { - assert(ssd->dcl.con); - dpy_cursor_define(ssd->dcl.con, ssd->cursor); - } - if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { - assert(ssd->dcl.con); - dpy_mouse_set(ssd->dcl.con, ssd->mouse_x, ssd->mouse_y, 1); - ssd->mouse_x = -1; - ssd->mouse_y = -1; - } -} - void qemu_spice_cursor_refresh_bh(void *opaque) { SimpleSpiceDisplay *ssd = opaque; qemu_mutex_lock(&ssd->lock); - qemu_spice_cursor_refresh_unlocked(ssd); - qemu_mutex_unlock(&ssd->lock); + if (ssd->cursor) { + QEMUCursor *c = ssd->cursor; + assert(ssd->dcl.con); + cursor_get(c); + qemu_mutex_unlock(&ssd->lock); + dpy_cursor_define(ssd->dcl.con, c); + qemu_mutex_lock(&ssd->lock); + cursor_put(c); + } + + if (ssd->mouse_x != -1 && ssd->mouse_y != -1) { + int x, y; + assert(ssd->dcl.con); + x = ssd->mouse_x; + y = ssd->mouse_y; + ssd->mouse_x = -1; + ssd->mouse_y = -1; + qemu_mutex_unlock(&ssd->lock); + dpy_mouse_set(ssd->dcl.con, x, y, 1); + } else { + qemu_mutex_unlock(&ssd->lock); + } } void qemu_spice_display_refresh(SimpleSpiceDisplay *ssd) @@ -668,10 +674,28 @@ static int interface_client_monitors_config(QXLInstance *sin, memset(&info, 0, sizeof(info)); - head = qemu_console_get_head(ssd->dcl.con); - if (mc->num_of_monitors > head) { - info.width = mc->monitors[head].width; - info.height = mc->monitors[head].height; + if (mc->num_of_monitors == 1) { + /* + * New spice-server version which filters the list of monitors + * to only include those that belong to our display channel. + * + * single-head configuration (where filtering doesn't matter) + * takes this code path too. + */ + info.width = mc->monitors[0].width; + info.height = mc->monitors[0].height; + } else { + /* + * Old spice-server which gives us all monitors, so we have to + * figure ourself which entry we need. Array index is the + * channel_id, which is the qemu console index, see + * qemu_spice_add_display_interface(). + */ + head = qemu_console_get_index(ssd->dcl.con); + if (mc->num_of_monitors > head) { + info.width = mc->monitors[head].width; + info.height = mc->monitors[head].height; + } } trace_qemu_spice_ui_info(ssd->qxl.id, info.width, info.height); @@ -976,8 +1000,10 @@ static void qemu_spice_gl_cursor_position(DisplayChangeListener *dcl, { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + qemu_mutex_lock(&ssd->lock); ssd->ptr_x = pos_x; ssd->ptr_y = pos_y; + qemu_mutex_unlock(&ssd->lock); } static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, @@ -1048,17 +1074,23 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, /* note: spice server will close the fd, so hand over a dup */ spice_qxl_gl_scanout(&ssd->qxl, dup(dmabuf->fd), dmabuf->width, dmabuf->height, - dmabuf->stride, dmabuf->fourcc, false); + dmabuf->stride, dmabuf->fourcc, + dmabuf->y0_top); } qemu_spice_gl_monitor_config(ssd, 0, 0, dmabuf->width, dmabuf->height); ssd->guest_dmabuf_refresh = false; } if (render_cursor) { + int x, y; + qemu_mutex_lock(&ssd->lock); + x = ssd->ptr_x; + y = ssd->ptr_y; + qemu_mutex_unlock(&ssd->lock); egl_texture_blit(ssd->gls, &ssd->blit_fb, &ssd->guest_fb, !y_0_top); egl_texture_blend(ssd->gls, &ssd->blit_fb, &ssd->cursor_fb, - !y_0_top, ssd->ptr_x, ssd->ptr_y); + !y_0_top, x, y, 1.0, 1.0); glFlush(); } @@ -1115,6 +1147,17 @@ static void qemu_spice_display_init_one(QemuConsole *con) ssd->qxl.base.sif = &dpy_interface.base; qemu_spice_add_display_interface(&ssd->qxl, con); + +#if SPICE_SERVER_VERSION >= 0x000e02 /* release 0.14.2 */ + char device_address[256] = ""; + if (qemu_spice_fill_device_address(con, device_address, 256)) { + spice_qxl_set_device_info(&ssd->qxl, + device_address, + qemu_console_get_head(con), + 1); + } +#endif + qemu_spice_create_host_memslot(ssd); register_displaychangelistener(&ssd->dcl); diff --git a/ui/trace-events b/ui/trace-events index eb4bf7f00d..63de72a798 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -1,6 +1,6 @@ # See docs/devel/tracing.txt for syntax documentation. -# ui/console.c +# console.c console_gfx_new(void) "" console_gfx_reuse(int index) "%d" console_gfx_close(int index) "%d" @@ -17,7 +17,9 @@ displaychangelistener_register(void *dcl, const char *name) "%p [ %s ]" displaychangelistener_unregister(void *dcl, const char *name) "%p [ %s ]" ppm_save(const char *filename, void *display_surface) "%s surface=%p" -# ui/gtk.c +# gtk.c +# gtk-gl-area.c +# gtk-egl.c 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 qkeycode, const char *action) "tab=%s, translated GDK keycode %d to QKeyCode %d (%s)" @@ -25,7 +27,10 @@ gd_grab(const char *tab, const char *device, const char *reason) "tab=%s, dev=%s gd_ungrab(const char *tab, const char *device) "tab=%s, dev=%s" gd_keymap_windowing(const char *name) "backend=%s" -# ui/vnc.c +# vnc-auth-sasl.c +# vnc-ws.c +# vnc-auth-vencrypt.c +# vnc.c vnc_key_guest_leds(bool caps, bool num, bool scroll) "caps %d, num %d, scroll %d" vnc_key_map_init(const char *layout) "%s" vnc_key_event_ext(bool down, int sym, int keycode, const char *name) "down %d, sym 0x%x, keycode 0x%x [%s]" @@ -61,7 +66,7 @@ vnc_auth_sasl_username(void *state, const char *name) "VNC client auth SASL user vnc_auth_sasl_acl(void *state, int allow) "VNC client auth SASL ACL state=%p allow=%d" -# ui/input.c +# input.c input_event_key_number(int conidx, int number, const char *qcode, bool down) "con %d, key number 0x%x [%s], down %d" input_event_key_qcode(int conidx, const char *qcode, bool down) "con %d, key qcode %s, down %d" input_event_btn(int conidx, const char *btn, bool down) "con %d, button %s, down %d" @@ -70,7 +75,7 @@ input_event_abs(int conidx, const char *axis, int value) "con %d, axis %s, value input_event_sync(void) "" input_mouse_mode(int absolute) "absolute %d" -# ui/spice-display.c +# spice-display.c qemu_spice_add_memslot(int qid, uint32_t slot_id, unsigned long virt_start, unsigned long virt_end, int async) "%d %u: host virt 0x%lx - 0x%lx async=%d" qemu_spice_del_memslot(int qid, uint32_t gid, uint32_t slot_id) "%d gid=%u sid=%u" qemu_spice_create_primary_surface(int qid, uint32_t sid, void *surface, int async) "%d sid=%u surface=%p async=%d" @@ -90,12 +95,12 @@ qemu_spice_gl_forward_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d qemu_spice_gl_render_dmabuf(int qid, uint32_t width, uint32_t height) "%d %dx%d" qemu_spice_gl_update(int qid, uint32_t x, uint32_t y, uint32_t w, uint32_t h) "%d +%d+%d %dx%d" -# ui/keymaps.c +# keymaps.c keymap_parse(const char *file) "file %s" keymap_add(int sym, int code, const char *line) "sym=0x%04x code=0x%04x (line: %s)" keymap_unmapped(int sym) "sym=0x%04x" -# ui/x_keymap.c +# x_keymap.c xkeymap_extension(const char *name) "extension '%s'" xkeymap_vendor(const char *name) "vendor '%s'" xkeymap_keycodes(const char *name) "keycodes '%s'" diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 3751a777a4..7b2b09f242 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "authz/base.h" #include "vnc.h" #include "trace.h" @@ -146,13 +147,14 @@ size_t vnc_client_read_sasl(VncState *vs) static int vnc_auth_sasl_check_access(VncState *vs) { const void *val; - int err; - int allow; + int rv; + Error *err = NULL; + bool allow; - err = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); - if (err != SASL_OK) { + rv = sasl_getprop(vs->sasl.conn, SASL_USERNAME, &val); + if (rv != SASL_OK) { trace_vnc_auth_fail(vs, vs->auth, "Cannot fetch SASL username", - sasl_errstring(err, NULL, NULL)); + sasl_errstring(rv, NULL, NULL)); return -1; } if (val == NULL) { @@ -163,12 +165,19 @@ static int vnc_auth_sasl_check_access(VncState *vs) vs->sasl.username = g_strdup((const char*)val); trace_vnc_auth_sasl_username(vs, vs->sasl.username); - if (vs->vd->sasl.acl == NULL) { + if (vs->vd->sasl.authzid == NULL) { trace_vnc_auth_sasl_acl(vs, 1); return 0; } - allow = qemu_acl_party_is_allowed(vs->vd->sasl.acl, vs->sasl.username); + allow = qauthz_is_allowed_by_id(vs->vd->sasl.authzid, + vs->sasl.username, &err); + if (err) { + trace_vnc_auth_fail(vs, vs->auth, "Error from authz", + error_get_pretty(err)); + error_free(err); + return -1; + } trace_vnc_auth_sasl_acl(vs, allow); return allow ? 0 : -1; diff --git a/ui/vnc-auth-sasl.h b/ui/vnc-auth-sasl.h index 2ae224ee3a..fb55fe04ca 100644 --- a/ui/vnc-auth-sasl.h +++ b/ui/vnc-auth-sasl.h @@ -30,8 +30,8 @@ typedef struct VncStateSASL VncStateSASL; typedef struct VncDisplaySASL VncDisplaySASL; -#include "qemu/acl.h" #include "qemu/main-loop.h" +#include "authz/base.h" struct VncStateSASL { sasl_conn_t *conn; @@ -60,7 +60,8 @@ struct VncStateSASL { }; struct VncDisplaySASL { - qemu_acl *acl; + QAuthZ *authz; + char *authzid; }; void vnc_sasl_client_cleanup(VncState *vs); diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index d99ea362c1..f072e16ace 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -109,7 +109,7 @@ static int protocol_client_vencrypt_auth(VncState *vs, uint8_t *data, size_t len tls = qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, - vs->vd->tlsaclname, + vs->vd->tlsauthzid, &err); if (!tls) { trace_vnc_auth_fail(vs, vs->auth, "TLS setup failed", diff --git a/ui/vnc-enc-hextile-template.h b/ui/vnc-enc-hextile-template.h index d868d75720..0c56262aff 100644 --- a/ui/vnc-enc-hextile-template.h +++ b/ui/vnc-enc-hextile-template.h @@ -30,127 +30,127 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, int n_subtiles = 0; for (j = 0; j < h; j++) { - for (i = 0; i < w; i++) { - switch (n_colors) { - case 0: - bg = irow[i]; - n_colors = 1; - break; - case 1: - if (irow[i] != bg) { - fg = irow[i]; - n_colors = 2; - } - break; - case 2: - if (irow[i] != bg && irow[i] != fg) { - n_colors = 3; - } else { - if (irow[i] == bg) - bg_count++; - else if (irow[i] == fg) - fg_count++; - } - break; - default: - break; - } - } - if (n_colors > 2) - break; - irow += vnc_server_fb_stride(vd) / sizeof(pixel_t); + for (i = 0; i < w; i++) { + switch (n_colors) { + case 0: + bg = irow[i]; + n_colors = 1; + break; + case 1: + if (irow[i] != bg) { + fg = irow[i]; + n_colors = 2; + } + break; + case 2: + if (irow[i] != bg && irow[i] != fg) { + n_colors = 3; + } else { + if (irow[i] == bg) + bg_count++; + else if (irow[i] == fg) + fg_count++; + } + break; + default: + break; + } + } + if (n_colors > 2) + break; + irow += vnc_server_fb_stride(vd) / sizeof(pixel_t); } if (n_colors > 1 && fg_count > bg_count) { - pixel_t tmp = fg; - fg = bg; - bg = tmp; + pixel_t tmp = fg; + fg = bg; + bg = tmp; } if (!*has_bg || *last_bg != bg) { - flags |= 0x02; - *has_bg = 1; - *last_bg = bg; + flags |= 0x02; + *has_bg = 1; + *last_bg = bg; } if (n_colors < 3 && (!*has_fg || *last_fg != fg)) { - flags |= 0x04; - *has_fg = 1; - *last_fg = fg; + flags |= 0x04; + *has_fg = 1; + *last_fg = fg; } switch (n_colors) { case 1: - n_data = 0; - break; + n_data = 0; + break; case 2: - flags |= 0x08; + flags |= 0x08; - irow = (pixel_t *)row; + irow = (pixel_t *)row; - for (j = 0; j < h; j++) { - int min_x = -1; - for (i = 0; i < w; i++) { - if (irow[i] == fg) { - if (min_x == -1) - min_x = i; - } else if (min_x != -1) { - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - min_x = -1; - } - } - if (min_x != -1) { - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - } - irow += vnc_server_fb_stride(vd) / sizeof(pixel_t); - } - break; + for (j = 0; j < h; j++) { + int min_x = -1; + for (i = 0; i < w; i++) { + if (irow[i] == fg) { + if (min_x == -1) + min_x = i; + } else if (min_x != -1) { + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + min_x = -1; + } + } + if (min_x != -1) { + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + } + irow += vnc_server_fb_stride(vd) / sizeof(pixel_t); + } + break; case 3: - flags |= 0x18; + flags |= 0x18; - irow = (pixel_t *)row; + irow = (pixel_t *)row; - if (!*has_bg || *last_bg != bg) - flags |= 0x02; + if (!*has_bg || *last_bg != bg) + flags |= 0x02; - for (j = 0; j < h; j++) { - int has_color = 0; - int min_x = -1; - pixel_t color = 0; /* shut up gcc */ + for (j = 0; j < h; j++) { + int has_color = 0; + int min_x = -1; + pixel_t color = 0; /* shut up gcc */ - for (i = 0; i < w; i++) { - if (!has_color) { - if (irow[i] == bg) - continue; - color = irow[i]; - min_x = i; - has_color = 1; - } else if (irow[i] != color) { - has_color = 0; + for (i = 0; i < w; i++) { + if (!has_color) { + if (irow[i] == bg) + continue; + color = irow[i]; + min_x = i; + has_color = 1; + } else if (irow[i] != color) { + has_color = 0; #ifdef GENERIC vnc_convert_pixel(vs, data + n_data, color); n_data += vs->client_pf.bytes_per_pixel; #else - memcpy(data + n_data, &color, sizeof(color)); + memcpy(data + n_data, &color, sizeof(color)); n_data += sizeof(pixel_t); #endif - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; - min_x = -1; - if (irow[i] != bg) { - color = irow[i]; - min_x = i; - has_color = 1; - } - } - } - if (has_color) { + min_x = -1; + if (irow[i] != bg) { + color = irow[i]; + min_x = i; + has_color = 1; + } + } + } + if (has_color) { #ifdef GENERIC vnc_convert_pixel(vs, data + n_data, color); n_data += vs->client_pf.bytes_per_pixel; @@ -158,50 +158,50 @@ static void CONCAT(send_hextile_tile_, NAME)(VncState *vs, memcpy(data + n_data, &color, sizeof(color)); n_data += sizeof(pixel_t); #endif - hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); - n_data += 2; - n_subtiles++; - } - irow += vnc_server_fb_stride(vd) / sizeof(pixel_t); - } + hextile_enc_cord(data + n_data, min_x, j, i - min_x, 1); + n_data += 2; + n_subtiles++; + } + irow += vnc_server_fb_stride(vd) / sizeof(pixel_t); + } - /* A SubrectsColoured subtile invalidates the foreground color */ - *has_fg = 0; - if (n_data > (w * h * sizeof(pixel_t))) { - n_colors = 4; - flags = 0x01; - *has_bg = 0; + /* A SubrectsColoured subtile invalidates the foreground color */ + *has_fg = 0; + if (n_data > (w * h * sizeof(pixel_t))) { + n_colors = 4; + flags = 0x01; + *has_bg = 0; - /* we really don't have to invalidate either the bg or fg - but we've lost the old values. oh well. */ - } + /* we really don't have to invalidate either the bg or fg + but we've lost the old values. oh well. */ + } break; default: - break; + break; } if (n_colors > 3) { - flags = 0x01; - *has_fg = 0; - *has_bg = 0; - n_colors = 4; + flags = 0x01; + *has_fg = 0; + *has_bg = 0; + n_colors = 4; } vnc_write_u8(vs, flags); if (n_colors < 4) { - if (flags & 0x02) - vs->write_pixels(vs, last_bg, sizeof(pixel_t)); - if (flags & 0x04) - vs->write_pixels(vs, last_fg, sizeof(pixel_t)); - if (n_subtiles) { - vnc_write_u8(vs, n_subtiles); - vnc_write(vs, data, n_data); - } + if (flags & 0x02) + vs->write_pixels(vs, last_bg, sizeof(pixel_t)); + if (flags & 0x04) + vs->write_pixels(vs, last_fg, sizeof(pixel_t)); + if (n_subtiles) { + vnc_write_u8(vs, n_subtiles); + vnc_write(vs, data, n_data); + } } else { - for (j = 0; j < h; j++) { - vs->write_pixels(vs, row, w * 4); - row += vnc_server_fb_stride(vd); - } + for (j = 0; j < h; j++) { + vs->write_pixels(vs, row, w * 4); + row += vnc_server_fb_stride(vd); + } } } diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index f38aceb4da..d20cd1d86d 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -886,11 +886,11 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, */ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) { - uint32_t *buf32; + uint8_t *buf8; uint32_t pix; int rshift, gshift, bshift; - buf32 = (uint32_t *)buf; + buf8 = buf; if (1 /* FIXME */) { rshift = vs->client_pf.rshift; @@ -907,10 +907,11 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) } while (count--) { - pix = *buf32++; + pix = ldl_he_p(buf8); *buf++ = (char)(pix >> rshift); *buf++ = (char)(pix >> gshift); *buf++ = (char)(pix >> bshift); + buf8 += 4; } } @@ -979,7 +980,7 @@ static int send_mono_rect(VncState *vs, int x, int y, } #endif - bytes = (DIV_ROUND_UP(w, 8)) * h; + bytes = DIV_ROUND_UP(w, 8) * h; vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); vnc_write_u8(vs, VNC_TIGHT_FILTER_PALETTE); diff --git a/ui/vnc-enc-zywrle-template.c b/ui/vnc-enc-zywrle-template.c index b446380a7a..e9be55966e 100644 --- a/ui/vnc-enc-zywrle-template.c +++ b/ui/vnc-enc-zywrle-template.c @@ -44,8 +44,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* Change Log: V0.02 : 2008/02/04 : Fix mis encode/decode when width != scanline - (Thanks Johannes Schindelin, author of LibVNC - Server/Client) + (Thanks Johannes Schindelin, author of LibVNC + Server/Client) V0.01 : 2007/02/06 : Initial release */ diff --git a/ui/vnc-enc-zywrle.h b/ui/vnc-enc-zywrle.h index 610bd79d1a..9b7f698975 100644 --- a/ui/vnc-enc-zywrle.h +++ b/ui/vnc-enc-zywrle.h @@ -48,162 +48,162 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ZYWRLE_QUANTIZE /* Type A:lower bit omitting of EZW style. */ static const unsigned int zywrle_param[3][3]={ - {0x0000F000, 0x00000000, 0x00000000}, - {0x0000C000, 0x00F0F0F0, 0x00000000}, - {0x0000C000, 0x00C0C0C0, 0x00F0F0F0}, + {0x0000F000, 0x00000000, 0x00000000}, + {0x0000C000, 0x00F0F0F0, 0x00000000}, + {0x0000C000, 0x00C0C0C0, 0x00F0F0F0}, /* {0x0000FF00, 0x00000000, 0x00000000}, - {0x0000FF00, 0x00FFFFFF, 0x00000000}, - {0x0000FF00, 0x00FFFFFF, 0x00FFFFFF}, */ + {0x0000FF00, 0x00FFFFFF, 0x00000000}, + {0x0000FF00, 0x00FFFFFF, 0x00FFFFFF}, */ }; #else /* Type B:Non liner quantization filter. */ static const int8_t zywrle_conv[4][256]={ { /* bi=5, bo=5 r=0.0:PSNR=24.849 */ - 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, - 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, - 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, - 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, - 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, - 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, - 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, - 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, + 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, }, { /* bi=5, bo=5 r=2.0:PSNR=74.031 */ - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 32, 32, 32, 32, 32, 32, 32, 32, - 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 56, 56, 56, 56, 56, - 56, 56, 56, 56, 64, 64, 64, 64, - 64, 64, 64, 64, 72, 72, 72, 72, - 72, 72, 72, 72, 80, 80, 80, 80, - 80, 80, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 96, 96, - 96, 96, 96, 104, 104, 104, 104, 104, - 104, 104, 104, 104, 104, 112, 112, 112, - 112, 112, 112, 112, 112, 112, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 0, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -112, -112, -112, -112, -112, - -112, -112, -112, -112, -104, -104, -104, -104, - -104, -104, -104, -104, -104, -104, -96, -96, - -96, -96, -96, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -80, - -80, -80, -80, -80, -80, -72, -72, -72, - -72, -72, -72, -72, -72, -64, -64, -64, - -64, -64, -64, -64, -64, -56, -56, -56, - -56, -56, -56, -56, -56, -56, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, - -48, -32, -32, -32, -32, -32, -32, -32, - -32, -32, -32, -32, -32, -32, -32, -32, - -32, -32, 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, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 56, 56, 56, 56, 56, + 56, 56, 56, 56, 64, 64, 64, 64, + 64, 64, 64, 64, 72, 72, 72, 72, + 72, 72, 72, 72, 80, 80, 80, 80, + 80, 80, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 96, 96, + 96, 96, 96, 104, 104, 104, 104, 104, + 104, 104, 104, 104, 104, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -112, -112, -112, -112, -112, + -112, -112, -112, -112, -104, -104, -104, -104, + -104, -104, -104, -104, -104, -104, -96, -96, + -96, -96, -96, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -80, + -80, -80, -80, -80, -80, -72, -72, -72, + -72, -72, -72, -72, -72, -64, -64, -64, + -64, -64, -64, -64, -64, -56, -56, -56, + -56, -56, -56, -56, -56, -56, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -32, -32, -32, -32, -32, -32, -32, + -32, -32, -32, -32, -32, -32, -32, -32, + -32, -32, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, }, { /* bi=5, bo=4 r=2.0:PSNR=64.441 */ - 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, - 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, - 48, 48, 48, 48, 48, 48, 48, 48, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 80, 80, 80, 80, 80, 80, 80, 80, - 80, 80, 80, 80, 80, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 104, 104, 104, 104, 104, 104, 104, 104, - 104, 104, 104, 112, 112, 112, 112, 112, - 112, 112, 112, 112, 120, 120, 120, 120, - 120, 120, 120, 120, 120, 120, 120, 120, - 0, -120, -120, -120, -120, -120, -120, -120, - -120, -120, -120, -120, -120, -112, -112, -112, - -112, -112, -112, -112, -112, -112, -104, -104, - -104, -104, -104, -104, -104, -104, -104, -104, - -104, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -80, -80, -80, -80, - -80, -80, -80, -80, -80, -80, -80, -80, - -80, -64, -64, -64, -64, -64, -64, -64, - -64, -64, -64, -64, -64, -64, -64, -64, - -64, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, - -48, -48, -48, -48, -48, -48, -48, -48, - -48, 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, 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, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 48, 48, 48, 48, 48, 48, 48, 48, + 64, 64, 64, 64, 64, 64, 64, 64, + 64, 64, 64, 64, 64, 64, 64, 64, + 80, 80, 80, 80, 80, 80, 80, 80, + 80, 80, 80, 80, 80, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 104, 104, 104, 104, 104, 104, 104, 104, + 104, 104, 104, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 120, 120, 120, 120, + 120, 120, 120, 120, 120, 120, 120, 120, + 0, -120, -120, -120, -120, -120, -120, -120, + -120, -120, -120, -120, -120, -112, -112, -112, + -112, -112, -112, -112, -112, -112, -104, -104, + -104, -104, -104, -104, -104, -104, -104, -104, + -104, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -80, -80, -80, -80, + -80, -80, -80, -80, -80, -80, -80, -80, + -80, -64, -64, -64, -64, -64, -64, -64, + -64, -64, -64, -64, -64, -64, -64, -64, + -64, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, -48, -48, -48, -48, -48, -48, -48, + -48, 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, }, { /* bi=5, bo=2 r=2.0:PSNR=43.175 */ - 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, - 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, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 88, 88, 88, 88, 88, 88, 88, 88, - 0, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, -88, -88, -88, -88, -88, -88, -88, - -88, 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, 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, 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, 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, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, + 0, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, -88, -88, -88, -88, -88, -88, -88, + -88, 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, 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, } }; static const int8_t *zywrle_param[3][3][3]={ - {{zywrle_conv[0], zywrle_conv[2], zywrle_conv[0]}, + {{zywrle_conv[0], zywrle_conv[2], zywrle_conv[0]}, {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}, {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}}, - {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, + {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}, {zywrle_conv[0], zywrle_conv[0], zywrle_conv[0]}}, - {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, + {{zywrle_conv[0], zywrle_conv[3], zywrle_conv[0]}, {zywrle_conv[2], zywrle_conv[2], zywrle_conv[2]}, {zywrle_conv[1], zywrle_conv[1], zywrle_conv[1]}}, }; @@ -214,53 +214,53 @@ static const int8_t *zywrle_param[3][3][3]={ #define ZYWRLE_UVMASK15 0xFFFFFFF8 #define ZYWRLE_LOAD_PIXEL15(src, r, g, b) \ do { \ - r = (((uint8_t*)src)[S_1]<< 1)& 0xF8; \ - g = (((uint8_t*)src)[S_1]<< 6) | (((uint8_t*)src)[S_0]>> 2); \ + r = (((uint8_t*)src)[S_1]<< 1)& 0xF8; \ + g = (((uint8_t*)src)[S_1]<< 6) | (((uint8_t*)src)[S_0]>> 2); \ g &= 0xF8; \ - b = (((uint8_t*)src)[S_0]<< 3)& 0xF8; \ + b = (((uint8_t*)src)[S_0]<< 3)& 0xF8; \ } while (0) #define ZYWRLE_SAVE_PIXEL15(dst, r, g, b) \ do { \ - r &= 0xF8; \ - g &= 0xF8; \ - b &= 0xF8; \ - ((uint8_t*)dst)[S_1] = (uint8_t)((r >> 1)|(g >> 6)); \ - ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 2))& 0xFF); \ + r &= 0xF8; \ + g &= 0xF8; \ + b &= 0xF8; \ + ((uint8_t*)dst)[S_1] = (uint8_t)((r >> 1)|(g >> 6)); \ + ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 2))& 0xFF); \ } while (0) #define ZYWRLE_YMASK16 0xFFFFFFFC #define ZYWRLE_UVMASK16 0xFFFFFFF8 #define ZYWRLE_LOAD_PIXEL16(src, r, g, b) \ do { \ - r = ((uint8_t*)src)[S_1] & 0xF8; \ - g = (((uint8_t*)src)[S_1]<< 5) | (((uint8_t*)src)[S_0] >> 3); \ + r = ((uint8_t*)src)[S_1] & 0xF8; \ + g = (((uint8_t*)src)[S_1]<< 5) | (((uint8_t*)src)[S_0] >> 3); \ g &= 0xFC; \ - b = (((uint8_t*)src)[S_0]<< 3) & 0xF8; \ + b = (((uint8_t*)src)[S_0]<< 3) & 0xF8; \ } while (0) #define ZYWRLE_SAVE_PIXEL16(dst, r, g,b) \ do { \ - r &= 0xF8; \ - g &= 0xFC; \ - b &= 0xF8; \ - ((uint8_t*)dst)[S_1] = (uint8_t)(r | (g >> 5)); \ - ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 3)) & 0xFF); \ + r &= 0xF8; \ + g &= 0xFC; \ + b &= 0xF8; \ + ((uint8_t*)dst)[S_1] = (uint8_t)(r | (g >> 5)); \ + ((uint8_t*)dst)[S_0] = (uint8_t)(((b >> 3)|(g << 3)) & 0xFF); \ } while (0) #define ZYWRLE_YMASK32 0xFFFFFFFF #define ZYWRLE_UVMASK32 0xFFFFFFFF #define ZYWRLE_LOAD_PIXEL32(src, r, g, b) \ do { \ - r = ((uint8_t*)src)[L_2]; \ - g = ((uint8_t*)src)[L_1]; \ - b = ((uint8_t*)src)[L_0]; \ + r = ((uint8_t*)src)[L_2]; \ + g = ((uint8_t*)src)[L_1]; \ + b = ((uint8_t*)src)[L_0]; \ } while (0) #define ZYWRLE_SAVE_PIXEL32(dst, r, g, b) \ do { \ - ((uint8_t*)dst)[L_2] = (uint8_t)r; \ - ((uint8_t*)dst)[L_1] = (uint8_t)g; \ - ((uint8_t*)dst)[L_0] = (uint8_t)b; \ + ((uint8_t*)dst)[L_2] = (uint8_t)r; \ + ((uint8_t*)dst)[L_1] = (uint8_t)g; \ + ((uint8_t*)dst)[L_0] = (uint8_t)b; \ } while (0) static inline void harr(int8_t *px0, int8_t *px1) @@ -443,27 +443,27 @@ static inline void filter_wavelet_square(int *buf, int width, int height, static inline void wavelet(int *buf, int width, int height, int level) { - int l, s; - int *top; - int *end; + int l, s; + int *top; + int *end; - for (l = 0; l < level; l++) { - top = buf; - end = buf + height * width; - s = width << l; - while (top < end) { - wavelet_level(top, width, l, 1); - top += s; - } - top = buf; - end = buf + width; - s = 1<> 2; \ - u = b - g; \ - v = r - g; \ - y -= 128; \ - u >>= 1; \ - v >>= 1; \ - y &= ymask; \ - u &= uvmask; \ - v &= uvmask; \ - if (y == -128) { \ + y = (r + (g << 1) + b) >> 2; \ + u = b - g; \ + v = r - g; \ + y -= 128; \ + u >>= 1; \ + v >>= 1; \ + y &= ymask; \ + u &= uvmask; \ + v &= uvmask; \ + if (y == -128) { \ y += (0xFFFFFFFF - ymask + 1); \ } \ - if (u == -128) { \ + if (u == -128) { \ u += (0xFFFFFFFF - uvmask + 1); \ } \ - if (v == -128) { \ + if (v == -128) { \ v += (0xFFFFFFFF - uvmask + 1); \ } \ } while (0) diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index b0b15d42a8..929391f85d 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -193,6 +193,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) static void vnc_async_encoding_end(VncState *orig, VncState *local) { + buffer_free(&local->output); orig->tight = local->tight; orig->zlib = local->zlib; orig->hextile = local->hextile; @@ -278,7 +279,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) /* Copy persistent encoding data */ vnc_async_encoding_end(job->vs, &vs); - qemu_bh_schedule(job->vs->bh); + qemu_bh_schedule(job->vs->bh); } else { buffer_reset(&vs.output); /* Copy persistent encoding data */ diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 950f1cd2ac..95c9703c72 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -62,7 +62,7 @@ gboolean vncws_tls_handshake_io(QIOChannel *ioc G_GNUC_UNUSED, tls = qio_channel_tls_new_server( vs->ioc, vs->vd->tlscreds, - vs->vd->tlsaclname, + vs->vd->tlsauthzid, &err); if (!tls) { VNC_DEBUG("Failed to setup TLS %s\n", error_get_pretty(err)); diff --git a/ui/vnc.c b/ui/vnc.c index 359693238b..1871422e1d 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -33,9 +33,10 @@ #include "qemu/option.h" #include "qemu/sockets.h" #include "qemu/timer.h" -#include "qemu/acl.h" +#include "authz/list.h" #include "qemu/config-file.h" -#include "qapi/qapi-events.h" +#include "qapi/qapi-emit-events.h" +#include "qapi/qapi-events-ui.h" #include "qapi/error.h" #include "qapi/qapi-commands-ui.h" #include "ui/input.h" @@ -59,7 +60,6 @@ static QTAILQ_HEAD(, VncDisplay) vnc_displays = QTAILQ_HEAD_INITIALIZER(vnc_displays); static int vnc_cursor_define(VncState *vs); -static void vnc_release_modifiers(VncState *vs); static void vnc_update_throttle_offset(VncState *vs); static void vnc_set_share_mode(VncState *vs, VncShareMode mode) @@ -296,14 +296,13 @@ static void vnc_qmp_event(VncState *vs, QAPIEvent event) switch (event) { case QAPI_EVENT_VNC_CONNECTED: - qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info), - &error_abort); + qapi_event_send_vnc_connected(si, qapi_VncClientInfo_base(vs->info)); break; case QAPI_EVENT_VNC_INITIALIZED: - qapi_event_send_vnc_initialized(si, vs->info, &error_abort); + qapi_event_send_vnc_initialized(si, vs->info); break; case QAPI_EVENT_VNC_DISCONNECTED: - qapi_event_send_vnc_disconnected(si, vs->info, &error_abort); + qapi_event_send_vnc_disconnected(si, vs->info); break; default: break; @@ -701,6 +700,12 @@ static void vnc_abort_display_jobs(VncDisplay *vd) } QTAILQ_FOREACH(vs, &vd->clients, next) { vnc_lock_output(vs); + if (vs->update == VNC_STATE_UPDATE_NONE && + vs->job_update != VNC_STATE_UPDATE_NONE) { + /* job aborted before completion */ + vs->update = vs->job_update; + vs->job_update = VNC_STATE_UPDATE_NONE; + } vs->abort = false; vnc_unlock_output(vs); } @@ -743,6 +748,17 @@ static void vnc_update_server_surface(VncDisplay *vd) width, height); } +static bool vnc_check_pageflip(DisplaySurface *s1, + DisplaySurface *s2) +{ + return (s1 != NULL && + s2 != NULL && + surface_width(s1) == surface_width(s2) && + surface_height(s1) == surface_height(s2) && + surface_format(s1) == surface_format(s2)); + +} + static void vnc_dpy_switch(DisplayChangeListener *dcl, DisplaySurface *surface) { @@ -750,6 +766,7 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, "Display output is not active."; static DisplaySurface *placeholder; VncDisplay *vd = container_of(dcl, VncDisplay, dcl); + bool pageflip = vnc_check_pageflip(vd->ds, surface); VncState *vs; if (surface == NULL) { @@ -762,14 +779,21 @@ static void vnc_dpy_switch(DisplayChangeListener *dcl, vnc_abort_display_jobs(vd); vd->ds = surface; - /* server surface */ - vnc_update_server_surface(vd); - /* guest surface */ qemu_pixman_image_unref(vd->guest.fb); vd->guest.fb = pixman_image_ref(surface->image); vd->guest.format = surface->format; + if (pageflip) { + vnc_set_area_dirty(vd->guest.dirty, vd, 0, 0, + surface_width(surface), + surface_height(surface)); + return; + } + + /* server surface */ + vnc_update_server_surface(vd); + QTAILQ_FOREACH(vs, &vd->clients, next) { vnc_colordepth(vs); vnc_desktop_resize(vs); @@ -995,16 +1019,16 @@ static void vnc_update_throttle_offset(VncState *vs) int bps; switch (vs->as.fmt) { default: - case AUD_FMT_U8: - case AUD_FMT_S8: + case AUDIO_FORMAT_U8: + case AUDIO_FORMAT_S8: bps = 1; break; - case AUD_FMT_U16: - case AUD_FMT_S16: + case AUDIO_FORMAT_U16: + case AUDIO_FORMAT_S16: bps = 2; break; - case AUD_FMT_U32: - case AUD_FMT_S32: + case AUDIO_FORMAT_U32: + case AUDIO_FORMAT_S32: bps = 4; break; } @@ -1249,7 +1273,7 @@ void vnc_disconnect_finish(VncState *vs) vnc_sasl_client_cleanup(vs); #endif /* CONFIG_VNC_SASL */ audio_del(vs); - vnc_release_modifiers(vs); + qkbd_state_lift_all_keys(vs->vd->kbd); if (vs->mouse_mode_notifier.notify != NULL) { qemu_remove_mouse_mode_change_notifier(&vs->mouse_mode_notifier); @@ -1738,26 +1762,10 @@ static void pointer_event(VncState *vs, int button_mask, int x, int y) qemu_input_event_sync(); } -static void reset_keys(VncState *vs) +static void press_key(VncState *vs, QKeyCode qcode) { - int i; - for(i = 0; i < 256; i++) { - if (vs->modifiers_state[i]) { - qemu_input_event_send_key_number(vs->vd->dcl.con, i, false); - qemu_input_event_send_key_delay(vs->vd->key_delay_ms); - vs->modifiers_state[i] = 0; - } - } -} - -static void press_key(VncState *vs, int keysym) -{ - int keycode = keysym2scancode(vs->vd->kbd_layout, keysym, - false, false, false) & SCANCODE_KEYMASK; - qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, true); - qemu_input_event_send_key_delay(vs->vd->key_delay_ms); - qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); - qemu_input_event_send_key_delay(vs->vd->key_delay_ms); + qkbd_state_key_event(vs->vd->kbd, qcode, true); + qkbd_state_key_event(vs->vd->kbd, qcode, false); } static void vnc_led_state_change(VncState *vs) @@ -1798,32 +1806,20 @@ static void kbd_leds(void *opaque, int ledstate) static void do_key_event(VncState *vs, int down, int keycode, int sym) { + QKeyCode qcode = qemu_input_key_number_to_qcode(keycode); + /* QEMU console switch */ - switch(keycode) { - case 0x2a: /* Left Shift */ - case 0x36: /* Right Shift */ - case 0x1d: /* Left CTRL */ - case 0x9d: /* Right CTRL */ - case 0x38: /* Left ALT */ - case 0xb8: /* Right ALT */ - if (down) - vs->modifiers_state[keycode] = 1; - else - vs->modifiers_state[keycode] = 0; - break; - case 0x02 ... 0x0a: /* '1' to '9' keys */ - if (vs->vd->dcl.con == NULL && - down && vs->modifiers_state[0x1d] && vs->modifiers_state[0x38]) { + switch (qcode) { + case Q_KEY_CODE_1 ... Q_KEY_CODE_9: /* '1' to '9' keys */ + if (vs->vd->dcl.con == NULL && down && + qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL) && + qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_ALT)) { /* Reset the modifiers sent to the current console */ - reset_keys(vs); - console_select(keycode - 0x02); + qkbd_state_lift_all_keys(vs->vd->kbd); + console_select(qcode - Q_KEY_CODE_1); return; } - break; - case 0x3a: /* CapsLock */ - case 0x45: /* NumLock */ - if (down) - vs->modifiers_state[keycode] ^= 1; + default: break; } @@ -1838,16 +1834,14 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) toggles numlock away from the VNC window. */ if (keysym_is_numlock(vs->vd->kbd_layout, sym & 0xFFFF)) { - if (!vs->modifiers_state[0x45]) { + if (!qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) { trace_vnc_key_sync_numlock(true); - vs->modifiers_state[0x45] = 1; - press_key(vs, 0xff7f); + press_key(vs, Q_KEY_CODE_NUM_LOCK); } } else { - if (vs->modifiers_state[0x45]) { + if (qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK)) { trace_vnc_key_sync_numlock(false); - vs->modifiers_state[0x45] = 0; - press_key(vs, 0xff7f); + press_key(vs, Q_KEY_CODE_NUM_LOCK); } } } @@ -1860,30 +1854,25 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) toggles capslock away from the VNC window. */ int uppercase = !!(sym >= 'A' && sym <= 'Z'); - int shift = !!(vs->modifiers_state[0x2a] | vs->modifiers_state[0x36]); - int capslock = !!(vs->modifiers_state[0x3a]); + bool shift = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_SHIFT); + bool capslock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CAPSLOCK); if (capslock) { if (uppercase == shift) { trace_vnc_key_sync_capslock(false); - vs->modifiers_state[0x3a] = 0; - press_key(vs, 0xffe5); + press_key(vs, Q_KEY_CODE_CAPS_LOCK); } } else { if (uppercase != shift) { trace_vnc_key_sync_capslock(true); - vs->modifiers_state[0x3a] = 1; - press_key(vs, 0xffe5); + press_key(vs, Q_KEY_CODE_CAPS_LOCK); } } } - if (qemu_console_is_graphic(NULL)) { - qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, down); - qemu_input_event_send_key_delay(vs->vd->key_delay_ms); - } else { - bool numlock = vs->modifiers_state[0x45]; - bool control = (vs->modifiers_state[0x1d] || - vs->modifiers_state[0x9d]); + qkbd_state_key_event(vs->vd->kbd, qcode, down); + if (!qemu_console_is_graphic(NULL)) { + bool numlock = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_NUMLOCK); + bool control = qkbd_state_modifier_get(vs->vd->kbd, QKBD_MOD_CTRL); /* QEMU console emulation */ if (down) { switch (keycode) { @@ -1984,27 +1973,6 @@ static void do_key_event(VncState *vs, int down, int keycode, int sym) } } -static void vnc_release_modifiers(VncState *vs) -{ - static const int keycodes[] = { - /* shift, control, alt keys, both left & right */ - 0x2a, 0x36, 0x1d, 0x9d, 0x38, 0xb8, - }; - int i, keycode; - - if (!qemu_console_is_graphic(NULL)) { - return; - } - for (i = 0; i < ARRAY_SIZE(keycodes); i++) { - keycode = keycodes[i]; - if (!vs->modifiers_state[keycode]) { - continue; - } - qemu_input_event_send_key_number(vs->vd->dcl.con, keycode, false); - qemu_input_event_send_key_delay(vs->vd->key_delay_ms); - } -} - static const char *code2name(int keycode) { return QKeyCode_str(qemu_input_key_number_to_qcode(keycode)); @@ -2012,9 +1980,6 @@ static const char *code2name(int keycode) static void key_event(VncState *vs, int down, uint32_t sym) { - bool shift = vs->modifiers_state[0x2a] || vs->modifiers_state[0x36]; - bool altgr = vs->modifiers_state[0xb8]; - bool ctrl = vs->modifiers_state[0x1d] || vs->modifiers_state[0x9d]; int keycode; int lsym = sym; @@ -2023,7 +1988,7 @@ static void key_event(VncState *vs, int down, uint32_t sym) } keycode = keysym2scancode(vs->vd->kbd_layout, lsym & 0xFFFF, - shift, altgr, ctrl) & SCANCODE_KEYMASK; + vs->vd->kbd, down) & SCANCODE_KEYMASK; trace_vnc_key_event_map(down, sym, keycode, code2name(keycode)); do_key_event(vs, down, keycode, sym); } @@ -2410,12 +2375,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 4) return 10; switch (read_u8(data, 4)) { - case 0: vs->as.fmt = AUD_FMT_U8; break; - case 1: vs->as.fmt = AUD_FMT_S8; break; - case 2: vs->as.fmt = AUD_FMT_U16; break; - case 3: vs->as.fmt = AUD_FMT_S16; break; - case 4: vs->as.fmt = AUD_FMT_U32; break; - case 5: vs->as.fmt = AUD_FMT_S32; break; + case 0: vs->as.fmt = AUDIO_FORMAT_U8; break; + case 1: vs->as.fmt = AUDIO_FORMAT_S8; break; + case 2: vs->as.fmt = AUDIO_FORMAT_U16; break; + case 3: vs->as.fmt = AUDIO_FORMAT_S16; break; + case 4: vs->as.fmt = AUDIO_FORMAT_U32; break; + case 5: vs->as.fmt = AUDIO_FORMAT_S32; break; default: VNC_DEBUG("Invalid audio format %d\n", read_u8(data, 4)); vnc_client_error(vs); @@ -2967,7 +2932,8 @@ static int vnc_refresh_server_surface(VncDisplay *vd) PIXMAN_FORMAT_BPP(pixman_image_get_format(vd->guest.fb)); guest_row0 = (uint8_t *)pixman_image_get_data(vd->guest.fb); guest_stride = pixman_image_get_stride(vd->guest.fb); - guest_ll = pixman_image_get_width(vd->guest.fb) * (DIV_ROUND_UP(guest_bpp, 8)); + guest_ll = pixman_image_get_width(vd->guest.fb) + * DIV_ROUND_UP(guest_bpp, 8); } line_bytes = MIN(server_stride, guest_ll); @@ -3097,8 +3063,8 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, buffer_init(&vs->zrle.zlib, "vnc-zrle-zlib/%p", sioc); if (skipauth) { - vs->auth = VNC_AUTH_NONE; - vs->subauth = VNC_AUTH_INVALID; + vs->auth = VNC_AUTH_NONE; + vs->subauth = VNC_AUTH_INVALID; } else { if (websocket) { vs->auth = vd->ws_auth; @@ -3145,7 +3111,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, vs->as.freq = 44100; vs->as.nchannels = 2; - vs->as.fmt = AUD_FMT_S16; + vs->as.fmt = AUDIO_FORMAT_S16; vs->as.endianness = 0; qemu_mutex_init(&vs->output_mutex); @@ -3205,7 +3171,7 @@ static const DisplayChangeListenerOps dcl_ops = { .dpy_cursor_define = vnc_dpy_cursor_define, }; -void vnc_display_init(const char *id) +void vnc_display_init(const char *id, Error **errp) { VncDisplay *vd; @@ -3222,13 +3188,14 @@ void vnc_display_init(const char *id) if (keyboard_layout) { trace_vnc_key_map_init(keyboard_layout); - vd->kbd_layout = init_keyboard_layout(name2keysym, keyboard_layout); + vd->kbd_layout = init_keyboard_layout(name2keysym, + keyboard_layout, errp); } else { - vd->kbd_layout = init_keyboard_layout(name2keysym, "en-us"); + vd->kbd_layout = init_keyboard_layout(name2keysym, "en-us", errp); } if (!vd->kbd_layout) { - exit(1); + return; } vd->share_policy = VNC_SHARE_POLICY_ALLOW_EXCLUSIVE; @@ -3239,6 +3206,7 @@ void vnc_display_init(const char *id) vd->dcl.ops = &dcl_ops; register_displaychangelistener(&vd->dcl); + vd->kbd = qkbd_state_init(vd->dcl.con); } @@ -3267,12 +3235,24 @@ static void vnc_display_close(VncDisplay *vd) object_unparent(OBJECT(vd->tlscreds)); vd->tlscreds = NULL; } - g_free(vd->tlsaclname); - vd->tlsaclname = NULL; + if (vd->tlsauthz) { + object_unparent(OBJECT(vd->tlsauthz)); + vd->tlsauthz = NULL; + } + g_free(vd->tlsauthzid); + vd->tlsauthzid = NULL; if (vd->lock_key_sync) { qemu_remove_led_event_handler(vd->led); vd->led = NULL; } +#ifdef CONFIG_VNC_SASL + if (vd->sasl.authz) { + object_unparent(OBJECT(vd->sasl.authz)); + vd->sasl.authz = NULL; + } + g_free(vd->sasl.authzid); + vd->sasl.authzid = NULL; +#endif } int vnc_display_password(const char *id, const char *password) @@ -3344,10 +3324,6 @@ static QemuOptsList qemu_vnc_opts = { },{ .name = "tls-creds", .type = QEMU_OPT_STRING, - },{ - /* Deprecated in favour of tls-creds */ - .name = "x509", - .type = QEMU_OPT_STRING, },{ .name = "share", .type = QEMU_OPT_STRING, @@ -3384,17 +3360,15 @@ 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, },{ .name = "acl", .type = QEMU_OPT_BOOL, + },{ + .name = "tls-authz", + .type = QEMU_OPT_STRING, + },{ + .name = "sasl-authz", + .type = QEMU_OPT_STRING, },{ .name = "lossy", .type = QEMU_OPT_BOOL, @@ -3519,51 +3493,6 @@ vnc_display_setup_auth(int *auth, } -/* - * 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); -} - - static int vnc_display_get_address(const char *addrstr, bool websocket, bool reverse, @@ -3878,10 +3807,9 @@ void vnc_display_open(const char *id, Error **errp) bool reverse = false; const char *credid; bool sasl = false; -#ifdef CONFIG_VNC_SASL - int saslErr; -#endif int acl = 0; + const char *tlsauthz; + const char *saslauthz; int lock_key_sync = 1; int key_delay_ms; @@ -3930,15 +3858,6 @@ void vnc_display_open(const char *id, Error **errp) 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) { @@ -3961,33 +3880,34 @@ void vnc_display_open(const char *id, Error **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; - } - } - vd->tlscreds = vnc_display_create_creds(x509, - x509verify, - path, - vd->id, - errp); - if (!vd->tlscreds) { - goto fail; - } - } + } + if (qemu_opt_get(opts, "acl")) { + error_report("The 'acl' option to -vnc is deprecated. " + "Please use the 'tls-authz' and 'sasl-authz' " + "options instead"); } acl = qemu_opt_get_bool(opts, "acl", false); + tlsauthz = qemu_opt_get(opts, "tls-authz"); + if (acl && tlsauthz) { + error_setg(errp, "'acl' option is mutually exclusive with the " + "'tls-authz' option"); + goto fail; + } + if (tlsauthz && !vd->tlscreds) { + error_setg(errp, "'tls-authz' provided but TLS is not enabled"); + goto fail; + } + + saslauthz = qemu_opt_get(opts, "sasl-authz"); + if (acl && saslauthz) { + error_setg(errp, "'acl' option is mutually exclusive with the " + "'sasl-authz' option"); + goto fail; + } + if (saslauthz && !sasl) { + error_setg(errp, "'sasl-authz' provided but SASL auth is not enabled"); + goto fail; + } share = qemu_opt_get(opts, "share"); if (share) { @@ -4017,25 +3937,32 @@ void vnc_display_open(const char *id, Error **errp) vd->non_adaptive = true; } - if (acl) { + if (tlsauthz) { + vd->tlsauthzid = g_strdup(tlsauthz); + } else if (acl) { if (strcmp(vd->id, "default") == 0) { - vd->tlsaclname = g_strdup("vnc.x509dname"); + vd->tlsauthzid = g_strdup("vnc.x509dname"); } else { - vd->tlsaclname = g_strdup_printf("vnc.%s.x509dname", vd->id); + vd->tlsauthzid = g_strdup_printf("vnc.%s.x509dname", vd->id); } - qemu_acl_init(vd->tlsaclname); + vd->tlsauthz = QAUTHZ(qauthz_list_new(vd->tlsauthzid, + QAUTHZ_LIST_POLICY_DENY, + &error_abort)); } #ifdef CONFIG_VNC_SASL - if (acl && sasl) { - char *aclname; - - if (strcmp(vd->id, "default") == 0) { - aclname = g_strdup("vnc.username"); - } else { - aclname = g_strdup_printf("vnc.%s.username", vd->id); + if (sasl) { + if (saslauthz) { + vd->sasl.authzid = g_strdup(saslauthz); + } else if (acl) { + if (strcmp(vd->id, "default") == 0) { + vd->sasl.authzid = g_strdup("vnc.username"); + } else { + vd->sasl.authzid = g_strdup_printf("vnc.%s.username", vd->id); + } + vd->sasl.authz = QAUTHZ(qauthz_list_new(vd->sasl.authzid, + QAUTHZ_LIST_POLICY_DENY, + &error_abort)); } - vd->sasl.acl = qemu_acl_init(aclname); - g_free(aclname); } #endif @@ -4054,10 +3981,14 @@ void vnc_display_open(const char *id, Error **errp) trace_vnc_auth_init(vd, 1, vd->ws_auth, vd->ws_subauth); #ifdef CONFIG_VNC_SASL - if ((saslErr = sasl_server_init(NULL, "qemu")) != SASL_OK) { - error_setg(errp, "Failed to initialize SASL auth: %s", - sasl_errstring(saslErr, NULL, NULL)); - goto fail; + if (sasl) { + int saslErr = sasl_server_init(NULL, "qemu"); + + if (saslErr != SASL_OK) { + error_setg(errp, "Failed to initialize SASL auth: %s", + sasl_errstring(saslErr, NULL, NULL)); + goto fail; + } } #endif vd->lock_key_sync = lock_key_sync; @@ -4065,7 +3996,6 @@ void vnc_display_open(const char *id, Error **errp) vd->led = qemu_add_led_event_handler(kbd_leds, vd); } vd->ledstate = 0; - vd->key_delay_ms = key_delay_ms; device_id = qemu_opt_get(opts, "display"); if (device_id) { @@ -4082,10 +4012,13 @@ void vnc_display_open(const char *id, Error **errp) } if (con != vd->dcl.con) { + qkbd_state_free(vd->kbd); unregister_displaychangelistener(&vd->dcl); vd->dcl.con = con; register_displaychangelistener(&vd->dcl); + vd->kbd = qkbd_state_init(vd->dcl.con); } + qkbd_state_set_delay(vd->kbd, key_delay_ms); if (saddr == NULL) { goto cleanup; @@ -4169,11 +4102,15 @@ int vnc_init_func(void *opaque, QemuOpts *opts, Error **errp) char *id = (char *)qemu_opts_id(opts); assert(id); - vnc_display_init(id); + vnc_display_init(id, &local_err); + if (local_err) { + error_propagate(errp, local_err); + return -1; + } vnc_display_open(id, &local_err); if (local_err != NULL) { - error_reportf_err(local_err, "Failed to start VNC server: "); - exit(1); + error_propagate(errp, local_err); + return -1; } return 0; } diff --git a/ui/vnc.h b/ui/vnc.h index a86e0610e8..ee3da08f4a 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -39,11 +39,13 @@ #include "io/channel-socket.h" #include "io/channel-tls.h" #include "io/net-listener.h" +#include "authz/base.h" #include #include "keymaps.h" #include "vnc-palette.h" #include "vnc-enc-zrle.h" +#include "ui/kbd-state.h" // #define _VNC_DEBUG 1 @@ -155,7 +157,7 @@ struct VncDisplay int lock_key_sync; QEMUPutLEDEntry *led; int ledstate; - int key_delay_ms; + QKbdState *kbd; QemuMutex mutex; QEMUCursor *cursor; @@ -177,7 +179,8 @@ struct VncDisplay bool lossy; bool non_adaptive; QCryptoTLSCreds *tlscreds; - char *tlsaclname; + QAuthZ *tlsauthz; + char *tlsauthzid; #ifdef CONFIG_VNC_SASL VncDisplaySASL sasl; #endif @@ -326,8 +329,6 @@ struct VncState VncReadEvent *read_handler; size_t read_handler_expect; - /* input */ - uint8_t modifiers_state[256]; bool abort; QemuMutex output_mutex; diff --git a/util/Makefile.objs b/util/Makefile.objs index e1c3fed4dc..835fcd69e2 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -3,6 +3,8 @@ util-obj-y += bufferiszero.o util-obj-y += lockcnt.o util-obj-y += aiocb.o async.o aio-wait.o thread-pool.o qemu-timer.o util-obj-y += main-loop.o iohandler.o +main-loop.o-cflags := $(SLIRP_CFLAGS) +util-obj-$(call lnot,$(CONFIG_ATOMIC64)) += atomic64.o util-obj-$(CONFIG_POSIX) += aio-posix.o util-obj-$(CONFIG_POSIX) += compatfd.o util-obj-$(CONFIG_POSIX) += event_notifier-posix.o @@ -19,7 +21,6 @@ util-obj-y += envlist.o path.o module.o util-obj-y += 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 += cacheinfo.o util-obj-y += error.o qemu-error.o util-obj-y += id.o @@ -44,8 +45,13 @@ util-obj-y += log.o util-obj-y += pagesize.o util-obj-y += qdist.o util-obj-y += qht.o +util-obj-y += qsp.o util-obj-y += range.o util-obj-y += stats64.o util-obj-y += systemd.o util-obj-y += iova-tree.o +util-obj-$(CONFIG_INOTIFY1) += filemonitor-inotify.o util-obj-$(CONFIG_LINUX) += vfio-helpers.o +util-obj-$(CONFIG_OPENGL) += drm.o + +stub-obj-y += filemonitor-stub.o diff --git a/util/acl.c b/util/acl.c deleted file mode 100644 index c105addadc..0000000000 --- a/util/acl.c +++ /dev/null @@ -1,179 +0,0 @@ -/* - * QEMU access control list management - * - * 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/osdep.h" -#include "qemu-common.h" -#include "qemu/acl.h" - -#ifdef CONFIG_FNMATCH -#include -#endif - - -static unsigned int nacls = 0; -static qemu_acl **acls = NULL; - - - -qemu_acl *qemu_acl_find(const char *aclname) -{ - int i; - for (i = 0 ; i < nacls ; i++) { - if (strcmp(acls[i]->aclname, aclname) == 0) - return acls[i]; - } - - return NULL; -} - -qemu_acl *qemu_acl_init(const char *aclname) -{ - qemu_acl *acl; - - acl = qemu_acl_find(aclname); - if (acl) - return acl; - - acl = g_malloc(sizeof(*acl)); - acl->aclname = g_strdup(aclname); - /* Deny by default, so there is no window of "open - * access" between QEMU starting, and the user setting - * up ACLs in the monitor */ - acl->defaultDeny = 1; - - acl->nentries = 0; - QTAILQ_INIT(&acl->entries); - - acls = g_realloc(acls, sizeof(*acls) * (nacls +1)); - acls[nacls] = acl; - nacls++; - - return acl; -} - -int qemu_acl_party_is_allowed(qemu_acl *acl, - const char *party) -{ - qemu_acl_entry *entry; - - QTAILQ_FOREACH(entry, &acl->entries, next) { -#ifdef CONFIG_FNMATCH - if (fnmatch(entry->match, party, 0) == 0) - return entry->deny ? 0 : 1; -#else - /* No fnmatch, so fallback to exact string matching - * instead of allowing wildcards */ - if (strcmp(entry->match, party) == 0) - return entry->deny ? 0 : 1; -#endif - } - - return acl->defaultDeny ? 0 : 1; -} - - -void qemu_acl_reset(qemu_acl *acl) -{ - qemu_acl_entry *entry, *next_entry; - - /* Put back to deny by default, so there is no window - * of "open access" while the user re-initializes the - * access control list */ - acl->defaultDeny = 1; - QTAILQ_FOREACH_SAFE(entry, &acl->entries, next, next_entry) { - QTAILQ_REMOVE(&acl->entries, entry, next); - g_free(entry->match); - g_free(entry); - } - acl->nentries = 0; -} - - -int qemu_acl_append(qemu_acl *acl, - int deny, - const char *match) -{ - qemu_acl_entry *entry; - - entry = g_malloc(sizeof(*entry)); - entry->match = g_strdup(match); - entry->deny = deny; - - QTAILQ_INSERT_TAIL(&acl->entries, entry, next); - acl->nentries++; - - return acl->nentries; -} - - -int qemu_acl_insert(qemu_acl *acl, - int deny, - const char *match, - int index) -{ - qemu_acl_entry *tmp; - int i = 0; - - if (index <= 0) - return -1; - if (index > acl->nentries) { - return qemu_acl_append(acl, deny, match); - } - - QTAILQ_FOREACH(tmp, &acl->entries, next) { - i++; - if (i == index) { - qemu_acl_entry *entry; - entry = g_malloc(sizeof(*entry)); - entry->match = g_strdup(match); - entry->deny = deny; - - QTAILQ_INSERT_BEFORE(tmp, entry, next); - acl->nentries++; - break; - } - } - - return i; -} - -int qemu_acl_remove(qemu_acl *acl, - const char *match) -{ - qemu_acl_entry *entry; - int i = 0; - - QTAILQ_FOREACH(entry, &acl->entries, next) { - i++; - if (strcmp(entry->match, match) == 0) { - QTAILQ_REMOVE(&acl->entries, entry, next); - acl->nentries--; - g_free(entry->match); - g_free(entry); - return i; - } - } - return -1; -} diff --git a/util/aio-posix.c b/util/aio-posix.c index 118bf5784b..6fbfa7924f 100644 --- a/util/aio-posix.c +++ b/util/aio-posix.c @@ -40,7 +40,7 @@ struct AioHandler #ifdef CONFIG_EPOLL_CREATE1 -/* The fd number threashold to switch to epoll */ +/* The fd number threshold to switch to epoll */ #define EPOLL_ENABLE_THRESHOLD 64 static void aio_epoll_disable(AioContext *ctx) @@ -200,6 +200,31 @@ static AioHandler *find_aio_handler(AioContext *ctx, int fd) return NULL; } +static bool aio_remove_fd_handler(AioContext *ctx, AioHandler *node) +{ + /* If the GSource is in the process of being destroyed then + * g_source_remove_poll() causes an assertion failure. Skip + * removal in that case, because glib cleans up its state during + * destruction anyway. + */ + if (!g_source_is_destroyed(&ctx->source)) { + g_source_remove_poll(&ctx->source, &node->pfd); + } + + /* If a read is in progress, just mark the node as deleted */ + if (qemu_lockcnt_count(&ctx->list_lock)) { + node->deleted = 1; + node->pfd.revents = 0; + return false; + } + /* Otherwise, delete it for real. We can't just mark it as + * deleted because deleted nodes are only cleaned up while + * no one is walking the handlers list. + */ + QLIST_REMOVE(node, node); + return true; +} + void aio_set_fd_handler(AioContext *ctx, int fd, bool is_external, @@ -209,8 +234,10 @@ void aio_set_fd_handler(AioContext *ctx, void *opaque) { AioHandler *node; + AioHandler *new_node = NULL; bool is_new = false; bool deleted = false; + int poll_disable_change; qemu_lockcnt_lock(&ctx->list_lock); @@ -222,59 +249,56 @@ void aio_set_fd_handler(AioContext *ctx, qemu_lockcnt_unlock(&ctx->list_lock); return; } + /* Clean events in order to unregister fd from the ctx epoll. */ + node->pfd.events = 0; - /* If the GSource is in the process of being destroyed then - * g_source_remove_poll() causes an assertion failure. Skip - * removal in that case, because glib cleans up its state during - * destruction anyway. - */ - if (!g_source_is_destroyed(&ctx->source)) { - g_source_remove_poll(&ctx->source, &node->pfd); - } - - /* If the lock is held, just mark the node as deleted */ - if (qemu_lockcnt_count(&ctx->list_lock)) { - node->deleted = 1; - node->pfd.revents = 0; - } else { - /* Otherwise, delete it for real. We can't just mark it as - * deleted because deleted nodes are only cleaned up while - * no one is walking the handlers list. - */ - QLIST_REMOVE(node, node); - deleted = true; - } - - if (!node->io_poll) { - ctx->poll_disable_cnt--; - } + poll_disable_change = -!node->io_poll; } else { + poll_disable_change = !io_poll - (node && !node->io_poll); if (node == NULL) { - /* Alloc and insert if it's not already there */ - node = g_new0(AioHandler, 1); - node->pfd.fd = fd; - QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); - - g_source_add_poll(&ctx->source, &node->pfd); is_new = true; - - ctx->poll_disable_cnt += !io_poll; - } else { - ctx->poll_disable_cnt += !io_poll - !node->io_poll; } + /* Alloc and insert if it's not already there */ + new_node = g_new0(AioHandler, 1); /* Update handler with latest information */ - node->io_read = io_read; - node->io_write = io_write; - node->io_poll = io_poll; - node->opaque = opaque; - node->is_external = is_external; + new_node->io_read = io_read; + new_node->io_write = io_write; + new_node->io_poll = io_poll; + new_node->opaque = opaque; + new_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); + if (is_new) { + new_node->pfd.fd = fd; + } else { + new_node->pfd = node->pfd; + } + g_source_add_poll(&ctx->source, &new_node->pfd); + + new_node->pfd.events = (io_read ? G_IO_IN | G_IO_HUP | G_IO_ERR : 0); + new_node->pfd.events |= (io_write ? G_IO_OUT | G_IO_ERR : 0); + + QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, new_node, node); + } + if (node) { + deleted = aio_remove_fd_handler(ctx, node); } - aio_epoll_update(ctx, node, is_new); + /* No need to order poll_disable_cnt writes against other updates; + * the counter is only used to avoid wasting time and latency on + * iterated polling when the system call will be ultimately necessary. + * Changing handlers is a rare event, and a little wasted polling until + * the aio_notify below is not an issue. + */ + atomic_set(&ctx->poll_disable_cnt, + atomic_read(&ctx->poll_disable_cnt) + poll_disable_change); + + if (new_node) { + aio_epoll_update(ctx, new_node, is_new); + } else if (node) { + /* Unregister deleted fd_handler */ + aio_epoll_update(ctx, node, false); + } qemu_lockcnt_unlock(&ctx->list_lock); aio_notify(ctx); @@ -486,7 +510,7 @@ static void add_pollfd(AioHandler *node) npfd++; } -static bool run_poll_handlers_once(AioContext *ctx) +static bool run_poll_handlers_once(AioContext *ctx, int64_t *timeout) { bool progress = false; AioHandler *node; @@ -495,7 +519,10 @@ static bool run_poll_handlers_once(AioContext *ctx) if (!node->deleted && node->io_poll && aio_node_check(ctx, node->is_external) && node->io_poll(node->opaque)) { - progress = true; + *timeout = 0; + if (node->opaque != &ctx->notifier) { + progress = true; + } } /* Caller handles freeing deleted nodes. Don't do it here. */ @@ -517,31 +544,38 @@ static bool run_poll_handlers_once(AioContext *ctx) * * Returns: true if progress was made, false otherwise */ -static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) +static bool run_poll_handlers(AioContext *ctx, int64_t max_ns, int64_t *timeout) { bool progress; - int64_t end_time; + int64_t start_time, elapsed_time; assert(ctx->notify_me); assert(qemu_lockcnt_count(&ctx->list_lock) > 0); - assert(ctx->poll_disable_cnt == 0); - trace_run_poll_handlers_begin(ctx, max_ns); - - end_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) + max_ns; + trace_run_poll_handlers_begin(ctx, max_ns, *timeout); + start_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); do { - progress = run_poll_handlers_once(ctx); - } while (!progress && qemu_clock_get_ns(QEMU_CLOCK_REALTIME) < end_time); + progress = run_poll_handlers_once(ctx, timeout); + elapsed_time = qemu_clock_get_ns(QEMU_CLOCK_REALTIME) - start_time; + } while (!progress && elapsed_time < max_ns + && !atomic_read(&ctx->poll_disable_cnt)); - trace_run_poll_handlers_end(ctx, progress); + /* If time has passed with no successful polling, adjust *timeout to + * keep the same ending time. + */ + if (*timeout != -1) { + *timeout -= MIN(*timeout, elapsed_time); + } + trace_run_poll_handlers_end(ctx, progress, *timeout); return progress; } /* try_poll_mode: * @ctx: the AioContext - * @blocking: busy polling is only attempted when blocking is true + * @timeout: timeout for blocking wait, computed by the caller and updated if + * polling succeeds. * * ctx->notify_me must be non-zero so this function can detect aio_notify(). * @@ -549,19 +583,16 @@ static bool run_poll_handlers(AioContext *ctx, int64_t max_ns) * * Returns: true if progress was made, false otherwise */ -static bool try_poll_mode(AioContext *ctx, bool blocking) +static bool try_poll_mode(AioContext *ctx, int64_t *timeout) { - if (blocking && ctx->poll_max_ns && ctx->poll_disable_cnt == 0) { - /* See qemu_soonest_timeout() uint64_t hack */ - int64_t max_ns = MIN((uint64_t)aio_compute_timeout(ctx), - (uint64_t)ctx->poll_ns); + /* See qemu_soonest_timeout() uint64_t hack */ + int64_t max_ns = MIN((uint64_t)*timeout, (uint64_t)ctx->poll_ns); - if (max_ns) { - poll_set_started(ctx, true); + if (max_ns && !atomic_read(&ctx->poll_disable_cnt)) { + poll_set_started(ctx, true); - if (run_poll_handlers(ctx, max_ns)) { - return true; - } + if (run_poll_handlers(ctx, max_ns, timeout)) { + return true; } } @@ -570,7 +601,7 @@ static bool try_poll_mode(AioContext *ctx, bool blocking) /* Even if we don't run busy polling, try polling once in case it can make * progress and the caller will be able to avoid ppoll(2)/epoll_wait(2). */ - return run_poll_handlers_once(ctx); + return run_poll_handlers_once(ctx, timeout); } bool aio_poll(AioContext *ctx, bool blocking) @@ -582,6 +613,8 @@ bool aio_poll(AioContext *ctx, bool blocking) int64_t timeout; int64_t start = 0; + assert(in_aio_context_home_thread(ctx)); + /* aio_notify can avoid the expensive event_notifier_set if * everything (file descriptors, bottom halves, timers) will * be re-evaluated before the next blocking poll(). This is @@ -599,8 +632,14 @@ bool aio_poll(AioContext *ctx, bool blocking) start = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } - progress = try_poll_mode(ctx, blocking); - if (!progress) { + timeout = blocking ? aio_compute_timeout(ctx) : 0; + progress = try_poll_mode(ctx, &timeout); + assert(!(timeout && progress)); + + /* If polling is allowed, non-blocking aio_poll does not need the + * system call---a single round of run_poll_handlers_once suffices. + */ + if (timeout || atomic_read(&ctx->poll_disable_cnt)) { assert(npfd == 0); /* fill pollfds */ @@ -614,8 +653,6 @@ bool aio_poll(AioContext *ctx, bool blocking) } } - timeout = blocking ? aio_compute_timeout(ctx) : 0; - /* wait until next event */ if (aio_epoll_check_poll(ctx, pollfds, npfd, timeout)) { AioHandler epoll_handler; @@ -632,6 +669,7 @@ bool aio_poll(AioContext *ctx, bool blocking) if (blocking) { atomic_sub(&ctx->notify_me, 2); + aio_notify_accept(ctx); } /* Adjust polling time */ @@ -675,8 +713,6 @@ bool aio_poll(AioContext *ctx, bool blocking) } } - aio_notify_accept(ctx); - /* if we have any readable fds, dispatch event */ if (ret > 0) { for (i = 0; i < npfd; i++) { diff --git a/util/aio-wait.c b/util/aio-wait.c index b8a8f86dba..b4877493f8 100644 --- a/util/aio-wait.c +++ b/util/aio-wait.c @@ -26,21 +26,22 @@ #include "qemu/main-loop.h" #include "block/aio-wait.h" +AioWait global_aio_wait; + static void dummy_bh_cb(void *opaque) { /* The point is to make AIO_WAIT_WHILE()'s aio_poll() return */ } -void aio_wait_kick(AioWait *wait) +void aio_wait_kick(void) { /* The barrier (or an atomic op) is in the caller. */ - if (atomic_read(&wait->num_waiters)) { + if (atomic_read(&global_aio_wait.num_waiters)) { aio_bh_schedule_oneshot(qemu_get_aio_context(), dummy_bh_cb, NULL); } } typedef struct { - AioWait wait; bool done; QEMUBHFunc *cb; void *opaque; @@ -54,7 +55,7 @@ static void aio_wait_bh(void *opaque) data->cb(data->opaque); data->done = true; - aio_wait_kick(&data->wait); + aio_wait_kick(); } void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) @@ -67,5 +68,5 @@ void aio_wait_bh_oneshot(AioContext *ctx, QEMUBHFunc *cb, void *opaque) assert(qemu_get_current_aio_context() == qemu_get_aio_context()); aio_bh_schedule_oneshot(ctx, aio_wait_bh, &data); - AIO_WAIT_WHILE(&data.wait, ctx, !data.done); + AIO_WAIT_WHILE(ctx, !data.done); } diff --git a/util/aio-win32.c b/util/aio-win32.c index e676a8d9b2..a23b9c364d 100644 --- a/util/aio-win32.c +++ b/util/aio-win32.c @@ -35,6 +35,22 @@ struct AioHandler { QLIST_ENTRY(AioHandler) node; }; +static void aio_remove_fd_handler(AioContext *ctx, AioHandler *node) +{ + /* If aio_poll is in progress, just mark the node as deleted */ + if (qemu_lockcnt_count(&ctx->list_lock)) { + node->deleted = 1; + node->pfd.revents = 0; + } else { + /* Otherwise, delete it for real. We can't just mark it as + * deleted because deleted nodes are only cleaned up after + * releasing the list_lock. + */ + QLIST_REMOVE(node, node); + g_free(node); + } +} + void aio_set_fd_handler(AioContext *ctx, int fd, bool is_external, @@ -44,41 +60,23 @@ void aio_set_fd_handler(AioContext *ctx, void *opaque) { /* fd is a SOCKET in our case */ - AioHandler *node; + AioHandler *old_node; + AioHandler *node = NULL; qemu_lockcnt_lock(&ctx->list_lock); - QLIST_FOREACH(node, &ctx->aio_handlers, node) { - if (node->pfd.fd == fd && !node->deleted) { + QLIST_FOREACH(old_node, &ctx->aio_handlers, node) { + if (old_node->pfd.fd == fd && !old_node->deleted) { break; } } - /* Are we deleting the fd handler? */ - if (!io_read && !io_write) { - if (node) { - /* If aio_poll is in progress, just mark the node as deleted */ - if (qemu_lockcnt_count(&ctx->list_lock)) { - node->deleted = 1; - node->pfd.revents = 0; - } else { - /* Otherwise, delete it for real. We can't just mark it as - * deleted because deleted nodes are only cleaned up after - * releasing the list_lock. - */ - QLIST_REMOVE(node, node); - g_free(node); - } - } - } else { + if (io_read || io_write) { HANDLE event; long bitmask = 0; - if (node == NULL) { - /* Alloc and insert if it's not already there */ - node = g_new0(AioHandler, 1); - node->pfd.fd = fd; - QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); - } + /* Alloc and insert if it's not already there */ + node = g_new0(AioHandler, 1); + node->pfd.fd = fd; node->pfd.events = 0; if (node->io_read) { @@ -104,9 +102,13 @@ void aio_set_fd_handler(AioContext *ctx, bitmask |= FD_WRITE | FD_CONNECT; } + QLIST_INSERT_HEAD_RCU(&ctx->aio_handlers, node, node); event = event_notifier_get_handle(&ctx->notifier); WSAEventSelect(node->pfd.fd, event, bitmask); } + if (old_node) { + aio_remove_fd_handler(ctx, old_node); + } qemu_lockcnt_unlock(&ctx->list_lock); aio_notify(ctx); @@ -139,18 +141,7 @@ void aio_set_event_notifier(AioContext *ctx, if (node) { g_source_remove_poll(&ctx->source, &node->pfd); - /* aio_poll is in progress, just mark the node as deleted */ - if (qemu_lockcnt_count(&ctx->list_lock)) { - node->deleted = 1; - node->pfd.revents = 0; - } else { - /* Otherwise, delete it for real. We can't just mark it as - * deleted because deleted nodes are only cleaned up after - * releasing the list_lock. - */ - QLIST_REMOVE(node, node); - g_free(node); - } + aio_remove_fd_handler(ctx, node); } } else { if (node == NULL) { @@ -373,11 +364,12 @@ bool aio_poll(AioContext *ctx, bool blocking) ret = WaitForMultipleObjects(count, events, FALSE, timeout); if (blocking) { assert(first); + assert(in_aio_context_home_thread(ctx)); atomic_sub(&ctx->notify_me, 2); + aio_notify_accept(ctx); } if (first) { - aio_notify_accept(ctx); progress |= aio_bh_poll(ctx); first = false; } diff --git a/util/async.c b/util/async.c index 05979f8014..c10642a385 100644 --- a/util/async.c +++ b/util/async.c @@ -400,7 +400,7 @@ static void co_schedule_bh_cb(void *opaque) /* Protected by write barrier in qemu_aio_coroutine_enter */ atomic_set(&co->scheduled, NULL); - qemu_coroutine_enter(co); + qemu_aio_coroutine_enter(ctx, co); aio_context_release(ctx); } } diff --git a/util/atomic64.c b/util/atomic64.c new file mode 100644 index 0000000000..b198a6c9c8 --- /dev/null +++ b/util/atomic64.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ +#include "qemu/osdep.h" +#include "qemu/atomic.h" +#include "qemu/thread.h" + +#ifdef CONFIG_ATOMIC64 +#error This file must only be compiled if !CONFIG_ATOMIC64 +#endif + +/* + * When !CONFIG_ATOMIC64, we serialize both reads and writes with spinlocks. + * We use an array of spinlocks, with padding computed at run-time based on + * the host's dcache line size. + * We point to the array with a void * to simplify the padding's computation. + * Each spinlock is located every lock_size bytes. + */ +static void *lock_array; +static size_t lock_size; + +/* + * Systems without CONFIG_ATOMIC64 are unlikely to have many cores, so we use a + * small array of locks. + */ +#define NR_LOCKS 16 + +static QemuSpin *addr_to_lock(const void *addr) +{ + uintptr_t a = (uintptr_t)addr; + uintptr_t idx; + + idx = a >> qemu_dcache_linesize_log; + idx ^= (idx >> 8) ^ (idx >> 16); + idx &= NR_LOCKS - 1; + return lock_array + idx * lock_size; +} + +#define GEN_READ(name, type) \ + type name(const type *ptr) \ + { \ + QemuSpin *lock = addr_to_lock(ptr); \ + type ret; \ + \ + qemu_spin_lock(lock); \ + ret = *ptr; \ + qemu_spin_unlock(lock); \ + return ret; \ + } + +GEN_READ(atomic_read_i64, int64_t) +GEN_READ(atomic_read_u64, uint64_t) +#undef GEN_READ + +#define GEN_SET(name, type) \ + void name(type *ptr, type val) \ + { \ + QemuSpin *lock = addr_to_lock(ptr); \ + \ + qemu_spin_lock(lock); \ + *ptr = val; \ + qemu_spin_unlock(lock); \ + } + +GEN_SET(atomic_set_i64, int64_t) +GEN_SET(atomic_set_u64, uint64_t) +#undef GEN_SET + +void atomic64_init(void) +{ + int i; + + lock_size = ROUND_UP(sizeof(QemuSpin), qemu_dcache_linesize); + lock_array = qemu_memalign(qemu_dcache_linesize, lock_size * NR_LOCKS); + for (i = 0; i < NR_LOCKS; i++) { + QemuSpin *lock = lock_array + i * lock_size; + + qemu_spin_init(lock); + } +} diff --git a/util/bitops.c b/util/bitops.c index f2364015c4..3fe6b1c4f1 100644 --- a/util/bitops.c +++ b/util/bitops.c @@ -18,7 +18,7 @@ * Find the next set bit in a memory region. */ unsigned long find_next_bit(const unsigned long *addr, unsigned long size, - unsigned long offset) + unsigned long offset) { const unsigned long *p = addr + BIT_WORD(offset); unsigned long result = offset & ~(BITS_PER_LONG-1); @@ -83,7 +83,7 @@ found_middle: * Linus' asm-alpha/bitops.h. */ unsigned long find_next_zero_bit(const unsigned long *addr, unsigned long size, - unsigned long offset) + unsigned long offset) { const unsigned long *p = addr + BIT_WORD(offset); unsigned long result = offset & ~(BITS_PER_LONG-1); diff --git a/util/cacheinfo.c b/util/cacheinfo.c index db5172d07c..3cd080b83d 100644 --- a/util/cacheinfo.c +++ b/util/cacheinfo.c @@ -7,9 +7,13 @@ */ #include "qemu/osdep.h" +#include "qemu/host-utils.h" +#include "qemu/atomic.h" int qemu_icache_linesize = 0; +int qemu_icache_linesize_log; int qemu_dcache_linesize = 0; +int qemu_dcache_linesize_log; /* * Operating system specific detection mechanisms. @@ -172,6 +176,13 @@ static void __attribute__((constructor)) init_cache_info(void) arch_cache_info(&isize, &dsize); fallback_cache_info(&isize, &dsize); + assert((isize & (isize - 1)) == 0); + assert((dsize & (dsize - 1)) == 0); + qemu_icache_linesize = isize; + qemu_icache_linesize_log = ctz32(isize); qemu_dcache_linesize = dsize; + qemu_dcache_linesize_log = ctz32(dsize); + + atomic64_init(); } diff --git a/util/cutils.c b/util/cutils.c index 9205e09031..e098debdc0 100644 --- a/util/cutils.c +++ b/util/cutils.c @@ -203,23 +203,21 @@ static int64_t suffix_mul(char suffix, int64_t unit) /* * Convert string to bytes, allowing either B/b for bytes, K/k for KB, * M/m for MB, G/g for GB or T/t for TB. End pointer will be returned - * in *end, if not NULL. Return -ERANGE on overflow, Return -EINVAL on + * in *end, if not NULL. Return -ERANGE on overflow, and -EINVAL on * other error. */ -static int do_strtosz(const char *nptr, char **end, +static int do_strtosz(const char *nptr, const char **end, const char default_suffix, int64_t unit, uint64_t *result) { int retval; - char *endptr; + const char *endptr; unsigned char c; int mul_required = 0; double val, mul, integral, fraction; - errno = 0; - val = strtod(nptr, &endptr); - if (isnan(val) || endptr == nptr || errno != 0) { - retval = -EINVAL; + retval = qemu_strtod_finite(nptr, &endptr, &val); + if (retval) { goto out; } fraction = modf(val, &integral); @@ -259,17 +257,17 @@ out: return retval; } -int qemu_strtosz(const char *nptr, char **end, uint64_t *result) +int qemu_strtosz(const char *nptr, const char **end, uint64_t *result) { return do_strtosz(nptr, end, 'B', 1024, result); } -int qemu_strtosz_MiB(const char *nptr, char **end, uint64_t *result) +int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result) { return do_strtosz(nptr, end, 'M', 1024, result); } -int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result) +int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result) { return do_strtosz(nptr, end, 'B', 1000, result); } @@ -280,6 +278,7 @@ int qemu_strtosz_metric(const char *nptr, char **end, uint64_t *result) static int check_strtox_error(const char *nptr, char *ep, const char **endptr, int libc_errno) { + assert(ep >= nptr); if (endptr) { *endptr = ep; } @@ -327,6 +326,7 @@ int qemu_strtoi(const char *nptr, const char **endptr, int base, char *ep; long long lresult; + assert((unsigned) base <= 36 && base != 1); if (!nptr) { if (endptr) { *endptr = nptr; @@ -379,6 +379,7 @@ int qemu_strtoui(const char *nptr, const char **endptr, int base, char *ep; long long lresult; + assert((unsigned) base <= 36 && base != 1); if (!nptr) { if (endptr) { *endptr = nptr; @@ -435,6 +436,7 @@ int qemu_strtol(const char *nptr, const char **endptr, int base, { char *ep; + assert((unsigned) base <= 36 && base != 1); if (!nptr) { if (endptr) { *endptr = nptr; @@ -477,6 +479,7 @@ int qemu_strtoul(const char *nptr, const char **endptr, int base, { char *ep; + assert((unsigned) base <= 36 && base != 1); if (!nptr) { if (endptr) { *endptr = nptr; @@ -504,6 +507,7 @@ int qemu_strtoi64(const char *nptr, const char **endptr, int base, { char *ep; + assert((unsigned) base <= 36 && base != 1); if (!nptr) { if (endptr) { *endptr = nptr; @@ -527,6 +531,7 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, { char *ep; + assert((unsigned) base <= 36 && base != 1); if (!nptr) { if (endptr) { *endptr = nptr; @@ -544,6 +549,71 @@ int qemu_strtou64(const char *nptr, const char **endptr, int base, return check_strtox_error(nptr, ep, endptr, errno); } +/** + * Convert string @nptr to a double. + * + * This is a wrapper around strtod() that is harder to misuse. + * Semantics of @nptr and @endptr match strtod() with differences + * noted below. + * + * @nptr may be null, and no conversion is performed then. + * + * If no conversion is performed, store @nptr in *@endptr and return + * -EINVAL. + * + * If @endptr is null, and the string isn't fully converted, return + * -EINVAL. This is the case when the pointer that would be stored in + * a non-null @endptr points to a character other than '\0'. + * + * If the conversion overflows, store +/-HUGE_VAL in @result, depending + * on the sign, and return -ERANGE. + * + * If the conversion underflows, store +/-0.0 in @result, depending on the + * sign, and return -ERANGE. + * + * Else store the converted value in @result, and return zero. + */ +int qemu_strtod(const char *nptr, const char **endptr, double *result) +{ + char *ep; + + if (!nptr) { + if (endptr) { + *endptr = nptr; + } + return -EINVAL; + } + + errno = 0; + *result = strtod(nptr, &ep); + return check_strtox_error(nptr, ep, endptr, errno); +} + +/** + * Convert string @nptr to a finite double. + * + * Works like qemu_strtod(), except that "NaN" and "inf" are rejected + * with -EINVAL and no conversion is performed. + */ +int qemu_strtod_finite(const char *nptr, const char **endptr, double *result) +{ + double tmp; + int ret; + + ret = qemu_strtod(nptr, endptr, &tmp); + if (!ret && !isfinite(tmp)) { + if (endptr) { + *endptr = nptr; + } + ret = -EINVAL; + } + + if (ret != -EINVAL) { + *result = tmp; + } + return ret; +} + /** * Searches for the first occurrence of 'c' in 's', and returns a pointer * to the trailing null byte if none was found. @@ -594,6 +664,7 @@ int parse_uint(const char *s, unsigned long long *value, char **endptr, char *endp = (char *)s; unsigned long long val = 0; + assert((unsigned) base <= 36 && base != 1); if (!s) { r = -EINVAL; goto out; @@ -769,3 +840,8 @@ char *size_to_str(uint64_t val) return g_strdup_printf("%0.3g %sB", (double)val / div, suffixes[i]); } + +int qemu_pstrcmp0(const char **str1, const char **str2) +{ + return g_strcmp0(*str1, *str2); +} diff --git a/util/drm.c b/util/drm.c new file mode 100644 index 0000000000..a23ff24538 --- /dev/null +++ b/util/drm.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2015-2016 Gerd Hoffmann + * + * 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 "qemu/drm.h" + +#include +#include + +int qemu_drm_rendernode_open(const char *rendernode) +{ + DIR *dir; + struct dirent *e; + int r, fd; + char *p; + + if (rendernode) { + return open(rendernode, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + } + + 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; + } + + p = g_strdup_printf("/dev/dri/%s", e->d_name); + + r = open(p, O_RDWR | O_CLOEXEC | O_NOCTTY | O_NONBLOCK); + if (r < 0) { + g_free(p); + continue; + } + fd = r; + g_free(p); + break; + } + + closedir(dir); + if (fd < 0) { + return -1; + } + return fd; +} diff --git a/util/error.c b/util/error.c index 3efdd69162..934a78e1b1 100644 --- a/util/error.c +++ b/util/error.c @@ -103,10 +103,6 @@ void error_setg_errno_internal(Error **errp, va_list ap; int saved_errno = errno; - if (errp == NULL) { - return; - } - va_start(ap, fmt); error_setv(errp, src, line, func, ERROR_CLASS_GENERIC_ERROR, fmt, ap, os_errno != 0 ? strerror(os_errno) : NULL); @@ -292,3 +288,16 @@ void error_propagate(Error **dst_errp, Error *local_err) error_free(local_err); } } + +void error_propagate_prepend(Error **dst_errp, Error *err, + const char *fmt, ...) +{ + va_list ap; + + if (dst_errp && !*dst_errp) { + va_start(ap, fmt); + error_vprepend(&err, fmt, ap); + va_end(ap); + } /* else error is being ignored, don't bother with prepending */ + error_propagate(dst_errp, err); +} diff --git a/util/filemonitor-inotify.c b/util/filemonitor-inotify.c new file mode 100644 index 0000000000..b5f4b93f3f --- /dev/null +++ b/util/filemonitor-inotify.c @@ -0,0 +1,339 @@ +/* + * QEMU file monitor Linux inotify impl + * + * Copyright (c) 2018 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/filemonitor.h" +#include "qemu/main-loop.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "trace.h" + +#include + +struct QFileMonitor { + int fd; + QemuMutex lock; /* protects dirs & idmap */ + GHashTable *dirs; /* dirname => QFileMonitorDir */ + GHashTable *idmap; /* inotify ID => dirname */ +}; + + +typedef struct { + int64_t id; /* watch ID */ + char *filename; /* optional filter */ + QFileMonitorHandler cb; + void *opaque; +} QFileMonitorWatch; + + +typedef struct { + char *path; + int inotify_id; /* inotify ID */ + int next_file_id; /* file ID counter */ + GArray *watches; /* QFileMonitorWatch elements */ +} QFileMonitorDir; + + +static void qemu_file_monitor_watch(void *arg) +{ + QFileMonitor *mon = arg; + char buf[4096] + __attribute__ ((aligned(__alignof__(struct inotify_event)))); + int used = 0; + int len; + + qemu_mutex_lock(&mon->lock); + + if (mon->fd == -1) { + qemu_mutex_unlock(&mon->lock); + return; + } + + len = read(mon->fd, buf, sizeof(buf)); + + if (len < 0) { + if (errno != EAGAIN) { + error_report("Failure monitoring inotify FD '%s'," + "disabling events", strerror(errno)); + goto cleanup; + } + + /* no more events right now */ + goto cleanup; + } + + /* Loop over all events in the buffer */ + while (used < len) { + struct inotify_event *ev = + (struct inotify_event *)(buf + used); + const char *name = ev->len ? ev->name : ""; + QFileMonitorDir *dir = g_hash_table_lookup(mon->idmap, + GINT_TO_POINTER(ev->wd)); + uint32_t iev = ev->mask & + (IN_CREATE | IN_MODIFY | IN_DELETE | IN_IGNORED | + IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); + int qev; + gsize i; + + used += sizeof(struct inotify_event) + ev->len; + + if (!dir) { + continue; + } + + /* + * During a rename operation, the old name gets + * IN_MOVED_FROM and the new name gets IN_MOVED_TO. + * To simplify life for callers, we turn these into + * DELETED and CREATED events + */ + switch (iev) { + case IN_CREATE: + case IN_MOVED_TO: + qev = QFILE_MONITOR_EVENT_CREATED; + break; + case IN_MODIFY: + qev = QFILE_MONITOR_EVENT_MODIFIED; + break; + case IN_DELETE: + case IN_MOVED_FROM: + qev = QFILE_MONITOR_EVENT_DELETED; + break; + case IN_ATTRIB: + qev = QFILE_MONITOR_EVENT_ATTRIBUTES; + break; + case IN_IGNORED: + qev = QFILE_MONITOR_EVENT_IGNORED; + break; + default: + g_assert_not_reached(); + } + + trace_qemu_file_monitor_event(mon, dir->path, name, ev->mask, + dir->inotify_id); + for (i = 0; i < dir->watches->len; i++) { + QFileMonitorWatch *watch = &g_array_index(dir->watches, + QFileMonitorWatch, + i); + + if (watch->filename == NULL || + (name && g_str_equal(watch->filename, name))) { + trace_qemu_file_monitor_dispatch(mon, dir->path, name, + qev, watch->cb, + watch->opaque, watch->id); + watch->cb(watch->id, qev, name, watch->opaque); + } + } + } + + cleanup: + qemu_mutex_unlock(&mon->lock); +} + + +static void +qemu_file_monitor_dir_free(void *data) +{ + QFileMonitorDir *dir = data; + gsize i; + + for (i = 0; i < dir->watches->len; i++) { + QFileMonitorWatch *watch = &g_array_index(dir->watches, + QFileMonitorWatch, i); + g_free(watch->filename); + } + g_array_unref(dir->watches); + g_free(dir->path); + g_free(dir); +} + + +QFileMonitor * +qemu_file_monitor_new(Error **errp) +{ + int fd; + QFileMonitor *mon; + + fd = inotify_init1(IN_NONBLOCK); + if (fd < 0) { + error_setg_errno(errp, errno, + "Unable to initialize inotify"); + return NULL; + } + + mon = g_new0(QFileMonitor, 1); + qemu_mutex_init(&mon->lock); + mon->fd = fd; + + mon->dirs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, + qemu_file_monitor_dir_free); + mon->idmap = g_hash_table_new(g_direct_hash, g_direct_equal); + + trace_qemu_file_monitor_new(mon, mon->fd); + + return mon; +} + +static gboolean +qemu_file_monitor_free_idle(void *opaque) +{ + QFileMonitor *mon = opaque; + + if (!mon) { + return G_SOURCE_REMOVE; + } + + qemu_mutex_lock(&mon->lock); + + g_hash_table_unref(mon->idmap); + g_hash_table_unref(mon->dirs); + + qemu_mutex_unlock(&mon->lock); + + qemu_mutex_destroy(&mon->lock); + g_free(mon); + + return G_SOURCE_REMOVE; +} + +void +qemu_file_monitor_free(QFileMonitor *mon) +{ + if (!mon) { + return; + } + + qemu_mutex_lock(&mon->lock); + if (mon->fd != -1) { + qemu_set_fd_handler(mon->fd, NULL, NULL, NULL); + close(mon->fd); + mon->fd = -1; + } + qemu_mutex_unlock(&mon->lock); + + /* + * Can't free it yet, because another thread + * may be running event loop, so the inotify + * callback might be pending. Using an idle + * source ensures we'll only free after the + * pending callback is done + */ + g_idle_add((GSourceFunc)qemu_file_monitor_free_idle, mon); +} + +int64_t +qemu_file_monitor_add_watch(QFileMonitor *mon, + const char *dirpath, + const char *filename, + QFileMonitorHandler cb, + void *opaque, + Error **errp) +{ + QFileMonitorDir *dir; + QFileMonitorWatch watch; + int64_t ret = -1; + + qemu_mutex_lock(&mon->lock); + dir = g_hash_table_lookup(mon->dirs, dirpath); + if (!dir) { + int rv = inotify_add_watch(mon->fd, dirpath, + IN_CREATE | IN_DELETE | IN_MODIFY | + IN_MOVED_TO | IN_MOVED_FROM | IN_ATTRIB); + + if (rv < 0) { + error_setg_errno(errp, errno, "Unable to watch '%s'", dirpath); + goto cleanup; + } + + trace_qemu_file_monitor_enable_watch(mon, dirpath, rv); + + dir = g_new0(QFileMonitorDir, 1); + dir->path = g_strdup(dirpath); + dir->inotify_id = rv; + dir->watches = g_array_new(FALSE, TRUE, sizeof(QFileMonitorWatch)); + + g_hash_table_insert(mon->dirs, dir->path, dir); + g_hash_table_insert(mon->idmap, GINT_TO_POINTER(rv), dir); + + if (g_hash_table_size(mon->dirs) == 1) { + qemu_set_fd_handler(mon->fd, qemu_file_monitor_watch, NULL, mon); + } + } + + watch.id = (((int64_t)dir->inotify_id) << 32) | dir->next_file_id++; + watch.filename = g_strdup(filename); + watch.cb = cb; + watch.opaque = opaque; + + g_array_append_val(dir->watches, watch); + + trace_qemu_file_monitor_add_watch(mon, dirpath, + filename ? filename : "", + cb, opaque, watch.id); + + ret = watch.id; + + cleanup: + qemu_mutex_unlock(&mon->lock); + return ret; +} + + +void qemu_file_monitor_remove_watch(QFileMonitor *mon, + const char *dirpath, + int64_t id) +{ + QFileMonitorDir *dir; + gsize i; + + qemu_mutex_lock(&mon->lock); + + trace_qemu_file_monitor_remove_watch(mon, dirpath, id); + + dir = g_hash_table_lookup(mon->dirs, dirpath); + if (!dir) { + goto cleanup; + } + + for (i = 0; i < dir->watches->len; i++) { + QFileMonitorWatch *watch = &g_array_index(dir->watches, + QFileMonitorWatch, i); + if (watch->id == id) { + g_free(watch->filename); + g_array_remove_index(dir->watches, i); + break; + } + } + + if (dir->watches->len == 0) { + inotify_rm_watch(mon->fd, dir->inotify_id); + trace_qemu_file_monitor_disable_watch(mon, dir->path, dir->inotify_id); + + g_hash_table_remove(mon->idmap, GINT_TO_POINTER(dir->inotify_id)); + g_hash_table_remove(mon->dirs, dir->path); + + if (g_hash_table_size(mon->dirs) == 0) { + qemu_set_fd_handler(mon->fd, NULL, NULL, NULL); + } + } + + cleanup: + qemu_mutex_unlock(&mon->lock); +} diff --git a/util/filemonitor-stub.c b/util/filemonitor-stub.c new file mode 100644 index 0000000000..2c0e97edd8 --- /dev/null +++ b/util/filemonitor-stub.c @@ -0,0 +1,59 @@ +/* + * QEMU file monitor stub impl + * + * Copyright (c) 2018 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/filemonitor.h" +#include "qemu/error-report.h" +#include "qapi/error.h" + + +QFileMonitor * +qemu_file_monitor_new(Error **errp) +{ + error_setg(errp, "File monitoring not available on this platform"); + return NULL; +} + + +void +qemu_file_monitor_free(QFileMonitor *mon G_GNUC_UNUSED) +{ +} + + +int64_t +qemu_file_monitor_add_watch(QFileMonitor *mon G_GNUC_UNUSED, + const char *dirpath G_GNUC_UNUSED, + const char *filename G_GNUC_UNUSED, + QFileMonitorHandler cb G_GNUC_UNUSED, + void *opaque G_GNUC_UNUSED, + Error **errp) +{ + error_setg(errp, "File monitoring not available on this platform"); + return -1; +} + + +void +qemu_file_monitor_remove_watch(QFileMonitor *mon G_GNUC_UNUSED, + const char *dirpath G_GNUC_UNUSED, + int64_t id G_GNUC_UNUSED) +{ +} diff --git a/util/hbitmap.c b/util/hbitmap.c index bcd304041a..7905212a8b 100644 --- a/util/hbitmap.c +++ b/util/hbitmap.c @@ -53,6 +53,9 @@ */ struct HBitmap { + /* Size of the bitmap, as requested in hbitmap_alloc. */ + uint64_t orig_size; + /* Number of total bits in the bottom level. */ uint64_t size; @@ -141,7 +144,7 @@ unsigned long hbitmap_iter_skip_words(HBitmapIter *hbi) return cur; } -int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) +int64_t hbitmap_iter_next(HBitmapIter *hbi) { unsigned long cur = hbi->cur[HBITMAP_LEVELS - 1] & hbi->hb->levels[HBITMAP_LEVELS - 1][hbi->pos]; @@ -154,12 +157,8 @@ int64_t hbitmap_iter_next(HBitmapIter *hbi, bool advance) } } - if (advance) { - /* The next call will resume work from the next bit. */ - hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); - } else { - hbi->cur[HBITMAP_LEVELS - 1] = cur; - } + /* The next call will resume work from the next bit. */ + hbi->cur[HBITMAP_LEVELS - 1] = cur & (cur - 1); item = ((uint64_t)hbi->pos << BITS_PER_LEVEL) + ctzl(cur); return item << hbi->granularity; @@ -192,16 +191,28 @@ void hbitmap_iter_init(HBitmapIter *hbi, const HBitmap *hb, uint64_t first) } } -int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start) +int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start, uint64_t count) { size_t pos = (start >> hb->granularity) >> BITS_PER_LEVEL; unsigned long *last_lev = hb->levels[HBITMAP_LEVELS - 1]; - uint64_t sz = hb->sizes[HBITMAP_LEVELS - 1]; unsigned long cur = last_lev[pos]; - unsigned start_bit_offset = - (start >> hb->granularity) & (BITS_PER_LONG - 1); + unsigned start_bit_offset; + uint64_t end_bit, sz; int64_t res; + if (start >= hb->orig_size || count == 0) { + return -1; + } + + end_bit = count > hb->orig_size - start ? + hb->size : + ((start + count - 1) >> hb->granularity) + 1; + sz = (end_bit + BITS_PER_LONG - 1) >> BITS_PER_LEVEL; + + /* There may be some zero bits in @cur before @start. We are not interested + * in them, let's set them. + */ + start_bit_offset = (start >> hb->granularity) & (BITS_PER_LONG - 1); cur |= (1UL << start_bit_offset) - 1; assert((start >> hb->granularity) < hb->size); @@ -218,7 +229,7 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start) } res = (pos << BITS_PER_LEVEL) + ctol(cur); - if (res >= hb->size) { + if (res >= end_bit) { return -1; } @@ -231,6 +242,45 @@ int64_t hbitmap_next_zero(const HBitmap *hb, uint64_t start) return res; } +bool hbitmap_next_dirty_area(const HBitmap *hb, uint64_t *start, + uint64_t *count) +{ + HBitmapIter hbi; + int64_t firt_dirty_off, area_end; + uint32_t granularity = 1UL << hb->granularity; + uint64_t end; + + if (*start >= hb->orig_size || *count == 0) { + return false; + } + + end = *count > hb->orig_size - *start ? hb->orig_size : *start + *count; + + hbitmap_iter_init(&hbi, hb, *start); + firt_dirty_off = hbitmap_iter_next(&hbi); + + if (firt_dirty_off < 0 || firt_dirty_off >= end) { + return false; + } + + if (firt_dirty_off + granularity >= end) { + area_end = end; + } else { + area_end = hbitmap_next_zero(hb, firt_dirty_off + granularity, + end - firt_dirty_off - granularity); + if (area_end < 0) { + area_end = end; + } + } + + if (firt_dirty_off > *start) { + *start = firt_dirty_off; + } + *count = area_end - *start; + + return true; +} + bool hbitmap_empty(const HBitmap *hb) { return hb->count == 0; @@ -652,6 +702,8 @@ HBitmap *hbitmap_alloc(uint64_t size, int granularity) HBitmap *hb = g_new0(struct HBitmap, 1); unsigned i; + hb->orig_size = size; + assert(granularity >= 0 && granularity < 64); size = (size + (1ULL << granularity) - 1) >> granularity; assert(size <= ((uint64_t)1 << HBITMAP_LOG_MAX_SIZE)); @@ -723,6 +775,10 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) } } +bool hbitmap_can_merge(const HBitmap *a, const HBitmap *b) +{ + return (a->size == b->size) && (a->granularity == b->granularity); +} /** * Given HBitmaps A and B, let A := A (BITOR) B. @@ -731,14 +787,15 @@ void hbitmap_truncate(HBitmap *hb, uint64_t size) * @return true if the merge was successful, * false if it was not attempted. */ -bool hbitmap_merge(HBitmap *a, const HBitmap *b) +bool hbitmap_merge(const HBitmap *a, const HBitmap *b, HBitmap *result) { int i; uint64_t j; - if ((a->size != b->size) || (a->granularity != b->granularity)) { + if (!hbitmap_can_merge(a, b) || !hbitmap_can_merge(a, result)) { return false; } + assert(hbitmap_can_merge(b, result)); if (hbitmap_count(b) == 0) { return true; @@ -750,10 +807,13 @@ bool hbitmap_merge(HBitmap *a, const HBitmap *b) */ for (i = HBITMAP_LEVELS - 1; i >= 0; i--) { for (j = 0; j < a->sizes[i]; j++) { - a->levels[i][j] |= b->levels[i][j]; + result->levels[i][j] = a->levels[i][j] | b->levels[i][j]; } } + /* Recompute the dirty count */ + result->count = hb_count_between(result, 0, result->size - 1); + return true; } diff --git a/util/main-loop.c b/util/main-loop.c index affe0403c5..e1e349ca5c 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -26,11 +26,9 @@ #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" #include "sysemu/cpus.h" #include "sysemu/replay.h" -#include "slirp/libslirp.h" #include "qemu/main-loop.h" #include "block/aio.h" #include "qemu/error-report.h" @@ -71,7 +69,7 @@ static void sigfd_handler(void *opaque) } } -static int qemu_signal_init(void) +static int qemu_signal_init(Error **errp) { int sigfd; sigset_t set; @@ -96,7 +94,7 @@ static int qemu_signal_init(void) sigdelset(&set, SIG_IPI); sigfd = qemu_signalfd(&set); if (sigfd == -1) { - fprintf(stderr, "failed to create signalfd\n"); + error_setg_errno(errp, errno, "failed to create signalfd"); return -errno; } @@ -109,7 +107,7 @@ static int qemu_signal_init(void) #else /* _WIN32 */ -static int qemu_signal_init(void) +static int qemu_signal_init(Error **errp) { return 0; } @@ -148,7 +146,7 @@ int qemu_init_main_loop(Error **errp) init_clocks(qemu_timer_notify_cb); - ret = qemu_signal_init(); + ret = qemu_signal_init(errp); if (ret) { return ret; } @@ -469,25 +467,42 @@ static int os_host_main_loop_wait(int64_t timeout) } #endif +static NotifierList main_loop_poll_notifiers = + NOTIFIER_LIST_INITIALIZER(main_loop_poll_notifiers); + +void main_loop_poll_add_notifier(Notifier *notify) +{ + notifier_list_add(&main_loop_poll_notifiers, notify); +} + +void main_loop_poll_remove_notifier(Notifier *notify) +{ + notifier_remove(notify); +} + void main_loop_wait(int nonblocking) { + MainLoopPoll mlpoll = { + .state = MAIN_LOOP_POLL_FILL, + .timeout = UINT32_MAX, + .pollfds = gpollfds, + }; int ret; - uint32_t timeout = UINT32_MAX; int64_t timeout_ns; if (nonblocking) { - timeout = 0; + mlpoll.timeout = 0; } /* poll any events */ g_array_set_size(gpollfds, 0); /* reset for new iteration */ /* XXX: separate device handlers from system ones */ - slirp_pollfds_fill(gpollfds, &timeout); + notifier_list_notify(&main_loop_poll_notifiers, &mlpoll); - if (timeout == UINT32_MAX) { + if (mlpoll.timeout == UINT32_MAX) { timeout_ns = -1; } else { - timeout_ns = (uint64_t)timeout * (int64_t)(SCALE_MS); + timeout_ns = (uint64_t)mlpoll.timeout * (int64_t)(SCALE_MS); } timeout_ns = qemu_soonest_timeout(timeout_ns, @@ -495,7 +510,8 @@ void main_loop_wait(int nonblocking) &main_loop_tlg)); ret = os_host_main_loop_wait(timeout_ns); - slirp_pollfds_poll(gpollfds, (ret < 0)); + mlpoll.state = ret < 0 ? MAIN_LOOP_POLL_ERR : MAIN_LOOP_POLL_OK; + notifier_list_notify(&main_loop_poll_notifiers, &mlpoll); /* CPU thread can infinitely wait for event after missing the warp */ diff --git a/util/memfd.c b/util/memfd.c index d248a53c3c..00334e5b21 100644 --- a/util/memfd.c +++ b/util/memfd.c @@ -40,27 +40,12 @@ static int memfd_create(const char *name, unsigned int flags) #ifdef __NR_memfd_create return syscall(__NR_memfd_create, name, flags); #else + errno = ENOSYS; return -1; #endif } #endif -#ifndef MFD_CLOEXEC -#define MFD_CLOEXEC 0x0001U -#endif - -#ifndef MFD_ALLOW_SEALING -#define MFD_ALLOW_SEALING 0x0002U -#endif - -#ifndef MFD_HUGETLB -#define MFD_HUGETLB 0x0004U -#endif - -#ifndef MFD_HUGE_SHIFT -#define MFD_HUGE_SHIFT 26 -#endif - int qemu_memfd_create(const char *name, size_t size, bool hugetlb, uint64_t hugetlbsize, unsigned int seals, Error **errp) { @@ -86,14 +71,18 @@ int qemu_memfd_create(const char *name, size_t size, bool hugetlb, } mfd = memfd_create(name, flags); if (mfd < 0) { + error_setg_errno(errp, errno, + "failed to create memfd with flags 0x%x", flags); goto err; } if (ftruncate(mfd, size) == -1) { + error_setg_errno(errp, errno, "failed to resize memfd to %zu", size); goto err; } if (seals && fcntl(mfd, F_ADD_SEALS, seals) == -1) { + error_setg_errno(errp, errno, "failed to add seals 0x%x", seals); goto err; } @@ -103,8 +92,9 @@ err: if (mfd >= 0) { close(mfd); } +#else + error_setg_errno(errp, ENOSYS, "failed to create memfd"); #endif - error_setg_errno(errp, errno, "failed to create memfd"); return -1; } @@ -187,6 +177,7 @@ bool qemu_memfd_alloc_check(void) int fd; void *ptr; + fd = -1; ptr = qemu_memfd_alloc("test", 4096, 0, &fd, NULL); memfd_check = ptr ? MEMFD_OK : MEMFD_KO; qemu_memfd_free(ptr, 4096, fd); @@ -200,23 +191,16 @@ bool qemu_memfd_alloc_check(void) * * Check if host supports memfd. */ -bool qemu_memfd_check(void) +bool qemu_memfd_check(unsigned int flags) { #ifdef CONFIG_LINUX - static int memfd_check = MEMFD_TODO; + int mfd = memfd_create("test", flags | MFD_CLOEXEC); - if (memfd_check == MEMFD_TODO) { - int mfd = memfd_create("test", 0); - if (mfd >= 0) { - memfd_check = MEMFD_OK; - close(mfd); - } else { - memfd_check = MEMFD_KO; - } + if (mfd >= 0) { + close(mfd); + return true; } - - return memfd_check == MEMFD_OK; -#else - return false; #endif + + return false; } diff --git a/util/mmap-alloc.c b/util/mmap-alloc.c index fd329eccd8..8565885420 100644 --- a/util/mmap-alloc.c +++ b/util/mmap-alloc.c @@ -77,11 +77,20 @@ size_t qemu_mempath_getpagesize(const char *mem_path) void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) { + int flags; + int guardfd; + size_t offset; + size_t pagesize; + size_t total; + void *guardptr; + void *ptr; + /* * Note: this always allocates at least one extra page of virtual address * space, even if size is already aligned. */ - size_t total = size + align; + 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 @@ -91,36 +100,45 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) * 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); + flags = MAP_PRIVATE; + pagesize = qemu_fd_getpagesize(fd); + if (fd == -1 || pagesize == getpagesize()) { + guardfd = -1; + flags |= MAP_ANONYMOUS; + } else { + guardfd = fd; + flags |= MAP_NORESERVE; + } #else - void *ptr = mmap(0, total, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + guardfd = -1; + pagesize = getpagesize(); + flags = MAP_PRIVATE | MAP_ANONYMOUS; #endif - size_t offset; - void *ptr1; - if (ptr == MAP_FAILED) { + guardptr = mmap(0, total, PROT_NONE, flags, guardfd, 0); + + if (guardptr == MAP_FAILED) { return MAP_FAILED; } assert(is_power_of_2(align)); /* Always align to host page size */ - assert(align >= getpagesize()); + assert(align >= pagesize); - offset = QEMU_ALIGN_UP((uintptr_t)ptr, align) - (uintptr_t)ptr; - 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); + flags = MAP_FIXED; + flags |= fd == -1 ? MAP_ANONYMOUS : 0; + flags |= shared ? MAP_SHARED : MAP_PRIVATE; + offset = QEMU_ALIGN_UP((uintptr_t)guardptr, align) - (uintptr_t)guardptr; + + ptr = mmap(guardptr + offset, size, PROT_READ | PROT_WRITE, flags, fd, 0); + + if (ptr == MAP_FAILED) { + munmap(guardptr, total); return MAP_FAILED; } if (offset > 0) { - munmap(ptr, offset); + munmap(guardptr, offset); } /* @@ -128,17 +146,24 @@ void *qemu_ram_mmap(int fd, size_t size, size_t align, bool shared) * a guard page guarding against potential buffer overflows. */ total -= offset; - if (total > size + getpagesize()) { - munmap(ptr1 + size + getpagesize(), total - size - getpagesize()); + if (total > size + pagesize) { + munmap(ptr + size + pagesize, total - size - pagesize); } - return ptr1; + return ptr; } -void qemu_ram_munmap(void *ptr, size_t size) +void qemu_ram_munmap(int fd, void *ptr, size_t size) { + size_t pagesize; + if (ptr) { /* Unmap both the RAM block and the guard page */ - munmap(ptr, size + getpagesize()); +#if defined(__powerpc64__) && defined(__linux__) + pagesize = qemu_fd_getpagesize(fd); +#else + pagesize = getpagesize(); +#endif + munmap(ptr, size + pagesize); } } diff --git a/util/module.c b/util/module.c index c90973721f..1259dd3686 100644 --- a/util/module.c +++ b/util/module.c @@ -162,9 +162,10 @@ void module_load_one(const char *prefix, const char *lib_name) #ifdef CONFIG_MODULES char *fname = NULL; char *exec_dir; - char *dirs[3]; + const char *search_dir; + char *dirs[4]; char *module_name; - int i = 0; + int i = 0, n_dirs = 0; int ret; static GHashTable *loaded_modules; @@ -186,14 +187,19 @@ void module_load_one(const char *prefix, const char *lib_name) g_hash_table_insert(loaded_modules, module_name, module_name); exec_dir = qemu_get_exec_dir(); - dirs[i++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); - dirs[i++] = g_strdup_printf("%s/..", exec_dir ? : ""); - dirs[i++] = g_strdup_printf("%s", exec_dir ? : ""); - assert(i == ARRAY_SIZE(dirs)); + search_dir = getenv("QEMU_MODULE_DIR"); + if (search_dir != NULL) { + dirs[n_dirs++] = g_strdup_printf("%s", search_dir); + } + dirs[n_dirs++] = g_strdup_printf("%s", CONFIG_QEMU_MODDIR); + dirs[n_dirs++] = g_strdup_printf("%s/..", exec_dir ? : ""); + dirs[n_dirs++] = g_strdup_printf("%s", exec_dir ? : ""); + assert(n_dirs <= ARRAY_SIZE(dirs)); + g_free(exec_dir); exec_dir = NULL; - for (i = 0; i < ARRAY_SIZE(dirs); i++) { + for (i = 0; i < n_dirs; i++) { fname = g_strdup_printf("%s/%s%s", dirs[i], module_name, HOST_DSOSUF); ret = module_load_file(fname); @@ -205,7 +211,7 @@ void module_load_one(const char *prefix, const char *lib_name) } } - for (i = 0; i < ARRAY_SIZE(dirs); i++) { + for (i = 0; i < n_dirs; i++) { g_free(dirs[i]); } diff --git a/util/osdep.c b/util/osdep.c index 1c8d1e2ee0..3f04326040 100644 --- a/util/osdep.c +++ b/util/osdep.c @@ -29,7 +29,7 @@ #include /* See MySQL bug #7156 (http://bugs.mysql.com/bug.php?id=7156) for discussion about Solaris header problems */ -extern int madvise(caddr_t, size_t, int); +extern int madvise(char *, size_t, int); #endif #include "qemu-common.h" @@ -470,8 +470,8 @@ void fips_set_state(bool requested) #ifdef _FIPS_DEBUG fprintf(stderr, "FIPS mode %s (requested %s)\n", - (fips_enabled ? "enabled" : "disabled"), - (requested ? "enabled" : "disabled")); + (fips_enabled ? "enabled" : "disabled"), + (requested ? "enabled" : "disabled")); #endif } diff --git a/util/oslib-posix.c b/util/oslib-posix.c index 13b6f8d776..88dda9cd39 100644 --- a/util/oslib-posix.c +++ b/util/oslib-posix.c @@ -88,6 +88,79 @@ int qemu_daemon(int nochdir, int noclose) return daemon(nochdir, noclose); } +bool qemu_write_pidfile(const char *path, Error **errp) +{ + int fd; + char pidstr[32]; + + while (1) { + struct stat a, b; + struct flock lock = { + .l_type = F_WRLCK, + .l_whence = SEEK_SET, + .l_len = 0, + }; + + fd = qemu_open(path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); + if (fd == -1) { + error_setg_errno(errp, errno, "Cannot open pid file"); + return false; + } + + if (fstat(fd, &b) < 0) { + error_setg_errno(errp, errno, "Cannot stat file"); + goto fail_close; + } + + if (fcntl(fd, F_SETLK, &lock)) { + error_setg_errno(errp, errno, "Cannot lock pid file"); + goto fail_close; + } + + /* + * Now make sure the path we locked is the same one that now + * exists on the filesystem. + */ + if (stat(path, &a) < 0) { + /* + * PID file disappeared, someone else must be racing with + * us, so try again. + */ + close(fd); + continue; + } + + if (a.st_ino == b.st_ino) { + break; + } + + /* + * PID file was recreated, someone else must be racing with + * us, so try again. + */ + close(fd); + } + + if (ftruncate(fd, 0) < 0) { + error_setg_errno(errp, errno, "Failed to truncate pid file"); + goto fail_unlink; + } + + snprintf(pidstr, sizeof(pidstr), FMT_pid "\n", getpid()); + if (write(fd, pidstr, strlen(pidstr)) != strlen(pidstr)) { + error_setg(errp, "Failed to write pid file"); + goto fail_unlink; + } + + return true; + +fail_unlink: + unlink(path); +fail_close: + close(fd); + return false; +} + void *qemu_oom_check(void *ptr) { if (ptr == NULL) { @@ -153,21 +226,37 @@ void qemu_vfree(void *ptr) void qemu_anon_ram_free(void *ptr, size_t size) { trace_qemu_anon_ram_free(ptr, size); - qemu_ram_munmap(ptr, size); + qemu_ram_munmap(-1, ptr, size); } void qemu_set_block(int fd) { int f; f = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, f & ~O_NONBLOCK); + assert(f != -1); + f = fcntl(fd, F_SETFL, f & ~O_NONBLOCK); + assert(f != -1); } void qemu_set_nonblock(int fd) { int f; f = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, f | O_NONBLOCK); + assert(f != -1); + f = fcntl(fd, F_SETFL, f | O_NONBLOCK); +#ifdef __OpenBSD__ + if (f == -1) { + /* + * Previous to OpenBSD 6.3, fcntl(F_SETFL) is not permitted on + * memory devices and sets errno to ENODEV. + * It's OK if we fail to set O_NONBLOCK on devices like /dev/null, + * because they will never block anyway. + */ + assert(errno == ENODEV); + } +#else + assert(f != -1); +#endif } int socket_set_fast_reuse(int fd) @@ -423,6 +512,59 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, } } +uint64_t qemu_get_pmem_size(const char *filename, Error **errp) +{ + struct stat st; + + if (stat(filename, &st) < 0) { + error_setg(errp, "unable to stat pmem file \"%s\"", filename); + return 0; + } + +#if defined(__linux__) + /* Special handling for devdax character devices */ + if (S_ISCHR(st.st_mode)) { + char *subsystem_path = NULL; + char *subsystem = NULL; + char *size_path = NULL; + char *size_str = NULL; + uint64_t ret = 0; + + subsystem_path = g_strdup_printf("/sys/dev/char/%d:%d/subsystem", + major(st.st_rdev), minor(st.st_rdev)); + subsystem = g_file_read_link(subsystem_path, NULL); + if (!subsystem) { + error_setg(errp, "unable to read subsystem for pmem file \"%s\"", + filename); + goto devdax_err; + } + + if (!g_str_has_suffix(subsystem, "/dax")) { + error_setg(errp, "pmem file \"%s\" is not a dax device", filename); + goto devdax_err; + } + + size_path = g_strdup_printf("/sys/dev/char/%d:%d/size", + major(st.st_rdev), minor(st.st_rdev)); + if (!g_file_get_contents(size_path, &size_str, NULL, NULL)) { + error_setg(errp, "unable to read size for pmem file \"%s\"", + size_path); + goto devdax_err; + } + + ret = g_ascii_strtoull(size_str, NULL, 0); + +devdax_err: + g_free(size_str); + g_free(size_path); + g_free(subsystem); + g_free(subsystem_path); + return ret; + } +#endif /* defined(__linux__) */ + + return st.st_size; +} char *qemu_get_pid_name(pid_t pid) { @@ -523,6 +665,7 @@ pid_t qemu_fork(Error **errp) void *qemu_alloc_stack(size_t *sz) { void *ptr, *guardpage; + int flags; #ifdef CONFIG_DEBUG_STACK_USAGE void *ptr2; #endif @@ -537,8 +680,18 @@ void *qemu_alloc_stack(size_t *sz) /* allocate one extra page for the guard page */ *sz += pagesz; - ptr = mmap(NULL, *sz, PROT_READ | PROT_WRITE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + flags = MAP_PRIVATE | MAP_ANONYMOUS; +#if defined(MAP_STACK) && defined(__OpenBSD__) + /* Only enable MAP_STACK on OpenBSD. Other OS's such as + * Linux/FreeBSD/NetBSD have a flag with the same name + * but have differing functionality. OpenBSD will SEGV + * if it spots execution with a stack pointer pointing + * at memory that was not allocated with MAP_STACK. + */ + flags |= MAP_STACK; +#endif + + ptr = mmap(NULL, *sz, PROT_READ | PROT_WRITE, flags, -1, 0); if (ptr == MAP_FAILED) { perror("failed to allocate memory for stack"); abort(); diff --git a/util/oslib-win32.c b/util/oslib-win32.c index bb5ad28bd3..bd633afab6 100644 --- a/util/oslib-win32.c +++ b/util/oslib-win32.c @@ -67,15 +67,24 @@ void *qemu_memalign(size_t alignment, size_t size) return qemu_oom_check(qemu_try_memalign(alignment, size)); } +static int get_allocation_granularity(void) +{ + SYSTEM_INFO system_info; + + GetSystemInfo(&system_info); + return system_info.dwAllocationGranularity; +} + void *qemu_anon_ram_alloc(size_t size, uint64_t *align, bool shared) { void *ptr; - /* FIXME: this is not exactly optimal solution since VirtualAlloc - has 64Kb granularity, but at least it guarantees us that the - memory is page aligned. */ ptr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); trace_qemu_anon_ram_alloc(size, ptr); + + if (ptr && align) { + *align = MAX(get_allocation_granularity(), getpagesize()); + } return ptr; } @@ -551,6 +560,11 @@ void os_mem_prealloc(int fd, char *area, size_t memory, int smp_cpus, } } +uint64_t qemu_get_pmem_size(const char *filename, Error **errp) +{ + error_setg(errp, "pmem support not available"); + return 0; +} char *qemu_get_pid_name(pid_t pid) { @@ -767,3 +781,30 @@ ssize_t qemu_recvfrom_wrap(int sockfd, void *buf, size_t len, int flags, } return ret; } + +bool qemu_write_pidfile(const char *filename, Error **errp) +{ + char buffer[128]; + int len; + HANDLE file; + OVERLAPPED overlap; + BOOL ret; + memset(&overlap, 0, sizeof(overlap)); + + file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + + if (file == INVALID_HANDLE_VALUE) { + error_setg(errp, "Failed to create PID file"); + return false; + } + len = snprintf(buffer, sizeof(buffer), FMT_pid "\n", (pid_t)getpid()); + ret = WriteFile(file, (LPCVOID)buffer, (DWORD)len, + NULL, &overlap); + CloseHandle(file); + if (ret == 0) { + error_setg(errp, "Failed to write PID file"); + return false; + } + return true; +} diff --git a/util/qemu-coroutine-sleep.c b/util/qemu-coroutine-sleep.c index afb678fbe5..4bfdd30cbf 100644 --- a/util/qemu-coroutine-sleep.c +++ b/util/qemu-coroutine-sleep.c @@ -17,38 +17,31 @@ #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; + Coroutine *co = opaque; /* Write of schedule protected by barrier write in aio_co_schedule */ - atomic_set(&sleep_cb->co->scheduled, NULL); - aio_co_wake(sleep_cb->co); + atomic_set(&co->scheduled, NULL); + aio_co_wake(co); } void coroutine_fn qemu_co_sleep_ns(QEMUClockType type, int64_t ns) { AioContext *ctx = qemu_get_current_aio_context(); - CoSleepCB sleep_cb = { - .co = qemu_coroutine_self(), - }; + QEMUTimer *ts; + Coroutine *co = qemu_coroutine_self(); - const char *scheduled = atomic_cmpxchg(&sleep_cb.co->scheduled, NULL, - __func__); + const char *scheduled = atomic_cmpxchg(&co->scheduled, NULL, __func__); if (scheduled) { fprintf(stderr, "%s: Co-routine was already scheduled in '%s'\n", __func__, scheduled); abort(); } - 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); + ts = aio_timer_new(ctx, type, SCALE_NS, co_sleep_cb, co); + timer_mod(ts, qemu_clock_get_ns(type) + ns); qemu_coroutine_yield(); - timer_del(sleep_cb.ts); - timer_free(sleep_cb.ts); + timer_del(ts); + timer_free(ts); } diff --git a/util/qemu-coroutine.c b/util/qemu-coroutine.c index 1ba4191b84..2295928d33 100644 --- a/util/qemu-coroutine.c +++ b/util/qemu-coroutine.c @@ -198,3 +198,8 @@ bool qemu_coroutine_entered(Coroutine *co) { return co->caller; } + +AioContext *coroutine_fn qemu_coroutine_get_aio_context(Coroutine *co) +{ + return co->ctx; +} diff --git a/util/qemu-error.c b/util/qemu-error.c index a25d3b94c6..fcbe8a1f74 100644 --- a/util/qemu-error.c +++ b/util/qemu-error.c @@ -194,7 +194,6 @@ bool enable_timestamp_msg; * 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. */ static void vreport(report_type type, const char *fmt, va_list ap) { @@ -242,7 +241,6 @@ void error_vreport(const char *fmt, va_list ap) * 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. */ void warn_vreport(const char *fmt, va_list ap) { @@ -255,7 +253,6 @@ void warn_vreport(const char *fmt, va_list ap) * 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. */ void info_vreport(const char *fmt, va_list ap) { @@ -283,7 +280,6 @@ void error_report(const char *fmt, ...) * 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. */ void warn_report(const char *fmt, ...) { @@ -300,7 +296,6 @@ void warn_report(const char *fmt, ...) * 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. */ void info_report(const char *fmt, ...) { @@ -310,3 +305,43 @@ void info_report(const char *fmt, ...) vreport(REPORT_TYPE_INFO, fmt, ap); va_end(ap); } + +/* + * Like error_report(), except print just once. + * If *printed is false, print the message, and flip *printed to true. + * Return whether the message was printed. + */ +bool error_report_once_cond(bool *printed, const char *fmt, ...) +{ + va_list ap; + + assert(printed); + if (*printed) { + return false; + } + *printed = true; + va_start(ap, fmt); + vreport(REPORT_TYPE_ERROR, fmt, ap); + va_end(ap); + return true; +} + +/* + * Like warn_report(), except print just once. + * If *printed is false, print the message, and flip *printed to true. + * Return whether the message was printed. + */ +bool warn_report_once_cond(bool *printed, const char *fmt, ...) +{ + va_list ap; + + assert(printed); + if (*printed) { + return false; + } + *printed = true; + va_start(ap, fmt); + vreport(REPORT_TYPE_WARNING, fmt, ap); + va_end(ap); + return true; +} diff --git a/util/qemu-option.c b/util/qemu-option.c index 01886efe90..ef60af70fc 100644 --- a/util/qemu-option.c +++ b/util/qemu-option.c @@ -208,17 +208,71 @@ out: return result; } -void qemu_opts_print_help(QemuOptsList *list) +static const char *opt_type_to_string(enum QemuOptType type) +{ + switch (type) { + case QEMU_OPT_STRING: + return "str"; + case QEMU_OPT_BOOL: + return "bool (on/off)"; + case QEMU_OPT_NUMBER: + return "num"; + case QEMU_OPT_SIZE: + return "size"; + } + + g_assert_not_reached(); +} + +/** + * Print the list of options available in the given list. If + * @print_caption is true, a caption (including the list name, if it + * exists) is printed. The options itself will be indented, so + * @print_caption should only be set to false if the caller prints its + * own custom caption (so that the indentation makes sense). + */ +void qemu_opts_print_help(QemuOptsList *list, bool print_caption) { QemuOptDesc *desc; + int i; + GPtrArray *array = g_ptr_array_new(); assert(list); desc = list->desc; while (desc && desc->name) { - printf("%-16s %s\n", desc->name, - desc->help ? desc->help : "No description available"); + GString *str = g_string_new(NULL); + g_string_append_printf(str, " %s=<%s>", desc->name, + opt_type_to_string(desc->type)); + if (desc->help) { + if (str->len < 24) { + g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); + } + g_string_append_printf(str, " - %s", desc->help); + } + g_ptr_array_add(array, g_string_free(str, false)); desc++; } + + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); + if (print_caption && array->len > 0) { + if (list->name) { + printf("%s options:\n", list->name); + } else { + printf("Options:\n"); + } + } else if (array->len == 0) { + if (list->name) { + printf("There are no options for %s.\n", list->name); + } else { + printf("No options available.\n"); + } + } + for (i = 0; i < array->len; i++) { + printf("%s\n", (char *)array->pdata[i]); + } + g_ptr_array_set_free_func(array, g_free); + g_ptr_array_free(array, true); + } /* ------------------------------------------------------------------ */ @@ -226,7 +280,7 @@ QemuOpt *qemu_opt_find(QemuOpts *opts, const char *name) { QemuOpt *opt; - QTAILQ_FOREACH_REVERSE(opt, &opts->head, QemuOptHead, next) { + QTAILQ_FOREACH_REVERSE(opt, &opts->head, next) { if (strcmp(opt->name, name) != 0) continue; return opt; @@ -325,7 +379,7 @@ bool qemu_opt_has_help_opt(QemuOpts *opts) { QemuOpt *opt; - QTAILQ_FOREACH_REVERSE(opt, &opts->head, QemuOptHead, next) { + QTAILQ_FOREACH_REVERSE(opt, &opts->head, next) { if (is_help_option(opt->name)) { return true; } @@ -486,7 +540,7 @@ int qemu_opt_unset(QemuOpts *opts, const char *name) } static void opt_set(QemuOpts *opts, const char *name, char *value, - bool prepend, Error **errp) + bool prepend, bool *invalidp, Error **errp) { QemuOpt *opt; const QemuOptDesc *desc; @@ -496,6 +550,9 @@ static void opt_set(QemuOpts *opts, const char *name, char *value, if (!desc && !opts_accepts_any(opts)) { g_free(value); error_setg(errp, QERR_INVALID_PARAMETER, name); + if (invalidp) { + *invalidp = true; + } return; } @@ -519,7 +576,7 @@ static void opt_set(QemuOpts *opts, const char *name, char *value, void qemu_opt_set(QemuOpts *opts, const char *name, const char *value, Error **errp) { - opt_set(opts, name, g_strdup(value), false, errp); + opt_set(opts, name, g_strdup(value), false, NULL, errp); } void qemu_opt_set_bool(QemuOpts *opts, const char *name, bool val, @@ -750,7 +807,8 @@ void qemu_opts_print(QemuOpts *opts, const char *separator) } static void opts_do_parse(QemuOpts *opts, const char *params, - const char *firstname, bool prepend, Error **errp) + const char *firstname, bool prepend, + bool *invalidp, Error **errp) { char *option = NULL; char *value = NULL; @@ -785,7 +843,7 @@ static void opts_do_parse(QemuOpts *opts, const char *params, } if (strcmp(option, "id") != 0) { /* store and parse */ - opt_set(opts, option, value, prepend, &local_err); + opt_set(opts, option, value, prepend, invalidp, &local_err); value = NULL; if (local_err) { error_propagate(errp, local_err); @@ -814,11 +872,12 @@ static void opts_do_parse(QemuOpts *opts, const char *params, void qemu_opts_do_parse(QemuOpts *opts, const char *params, const char *firstname, Error **errp) { - opts_do_parse(opts, params, firstname, false, errp); + opts_do_parse(opts, params, firstname, false, NULL, errp); } static QemuOpts *opts_parse(QemuOptsList *list, const char *params, - bool permit_abbrev, bool defaults, Error **errp) + bool permit_abbrev, bool defaults, + bool *invalidp, Error **errp) { const char *firstname; char *id = NULL; @@ -850,7 +909,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, return NULL; } - opts_do_parse(opts, params, firstname, defaults, &local_err); + opts_do_parse(opts, params, firstname, defaults, invalidp, &local_err); if (local_err) { error_propagate(errp, local_err); qemu_opts_del(opts); @@ -870,7 +929,7 @@ static QemuOpts *opts_parse(QemuOptsList *list, const char *params, QemuOpts *qemu_opts_parse(QemuOptsList *list, const char *params, bool permit_abbrev, Error **errp) { - return opts_parse(list, params, permit_abbrev, false, errp); + return opts_parse(list, params, permit_abbrev, false, NULL, errp); } /** @@ -886,10 +945,16 @@ QemuOpts *qemu_opts_parse_noisily(QemuOptsList *list, const char *params, { Error *err = NULL; QemuOpts *opts; + bool invalidp = false; - opts = opts_parse(list, params, permit_abbrev, false, &err); + opts = opts_parse(list, params, permit_abbrev, false, &invalidp, &err); if (err) { - error_report_err(err); + if (invalidp && has_help_option(params)) { + qemu_opts_print_help(list, true); + error_free(err); + } else { + error_report_err(err); + } } return opts; } @@ -899,7 +964,7 @@ void qemu_opts_set_defaults(QemuOptsList *list, const char *params, { QemuOpts *opts; - opts = opts_parse(list, params, permit_abbrev, true, NULL); + opts = opts_parse(list, params, permit_abbrev, true, NULL, NULL); assert(opts); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 8bd8bb64eb..9705051690 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -270,8 +270,8 @@ static int inet_listen_saddr(InetSocketAddress *saddr, /* create socket + bind/listen */ for (e = res; e != NULL; e = e->ai_next) { getnameinfo((struct sockaddr*)e->ai_addr,e->ai_addrlen, - uaddr,INET6_ADDRSTRLEN,uport,32, - NI_NUMERICHOST | NI_NUMERICSERV); + uaddr,INET6_ADDRSTRLEN,uport,32, + NI_NUMERICHOST | NI_NUMERICSERV); port_min = inet_getport(e); port_max = saddr->has_to ? saddr->to + port_offset : port_min; diff --git a/util/qemu-thread-common.h b/util/qemu-thread-common.h index a0ea7c0d92..2af6b12085 100644 --- a/util/qemu-thread-common.h +++ b/util/qemu-thread-common.h @@ -13,7 +13,6 @@ #ifndef QEMU_THREAD_COMMON_H #define QEMU_THREAD_COMMON_H -#include "qemu/typedefs.h" #include "qemu/thread.h" #include "trace.h" diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index dfa66ff2fb..1bf5e65dea 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -443,42 +443,34 @@ void qemu_event_wait(QemuEvent *ev) } } -static pthread_key_t exit_key; - -union NotifierThreadData { - void *ptr; - NotifierList list; -}; -QEMU_BUILD_BUG_ON(sizeof(union NotifierThreadData) != sizeof(void *)); +static __thread NotifierList thread_exit; +/* + * Note that in this implementation you can register a thread-exit + * notifier for the main thread, but it will never be called. + * This is OK because main thread exit can only happen when the + * entire process is exiting, and the API allows notifiers to not + * be called on process exit. + */ void qemu_thread_atexit_add(Notifier *notifier) { - union NotifierThreadData ntd; - ntd.ptr = pthread_getspecific(exit_key); - notifier_list_add(&ntd.list, notifier); - pthread_setspecific(exit_key, ntd.ptr); + notifier_list_add(&thread_exit, notifier); } void qemu_thread_atexit_remove(Notifier *notifier) { - union NotifierThreadData ntd; - ntd.ptr = pthread_getspecific(exit_key); notifier_remove(notifier); - pthread_setspecific(exit_key, ntd.ptr); } -static void qemu_thread_atexit_run(void *arg) +static void qemu_thread_atexit_notify(void *arg) { - union NotifierThreadData ntd = { .ptr = arg }; - notifier_list_notify(&ntd.list, NULL); + /* + * Called when non-main thread exits (via qemu_thread_exit() + * or by returning from its start routine.) + */ + notifier_list_notify(&thread_exit, NULL); } -static void __attribute__((constructor)) qemu_thread_atexit_init(void) -{ - pthread_key_create(&exit_key, qemu_thread_atexit_run); -} - - typedef struct { void *(*start_routine)(void *); void *arg; @@ -490,18 +482,26 @@ static void *qemu_thread_start(void *args) QemuThreadArgs *qemu_thread_args = args; void *(*start_routine)(void *) = qemu_thread_args->start_routine; void *arg = qemu_thread_args->arg; + void *r; -#ifdef CONFIG_PTHREAD_SETNAME_NP +#ifdef CONFIG_THREAD_SETNAME_BYTHREAD /* Attempt to set the threads name; note that this is for debug, so * we're not going to fail if we can't set it. */ if (name_threads && qemu_thread_args->name) { +# if defined(CONFIG_PTHREAD_SETNAME_NP_W_TID) pthread_setname_np(pthread_self(), qemu_thread_args->name); +# elif defined(CONFIG_PTHREAD_SETNAME_NP_WO_TID) + pthread_setname_np(qemu_thread_args->name); +# endif } #endif g_free(qemu_thread_args->name); g_free(qemu_thread_args); - return start_routine(arg); + pthread_cleanup_push(qemu_thread_atexit_notify, NULL); + r = start_routine(arg); + pthread_cleanup_pop(1); + return r; } void qemu_thread_create(QemuThread *thread, const char *name, @@ -524,6 +524,11 @@ void qemu_thread_create(QemuThread *thread, const char *name, /* Leave signal handling to the iothread. */ sigfillset(&set); + /* Blocking the signals can result in undefined behaviour. */ + sigdelset(&set, SIGSEGV); + sigdelset(&set, SIGFPE); + sigdelset(&set, SIGILL); + /* TODO avoid SIGBUS loss on macOS */ pthread_sigmask(SIG_SETMASK, &set, &oldset); qemu_thread_args = g_new0(QemuThreadArgs, 1); diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index b303188a36..572f88535d 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -11,10 +11,6 @@ * */ -#ifndef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 -#endif - #include "qemu/osdep.h" #include "qemu-common.h" #include "qemu/thread.h" @@ -97,13 +93,13 @@ void qemu_rec_mutex_destroy(QemuRecMutex *mutex) DeleteCriticalSection(&mutex->lock); } -void qemu_rec_mutex_lock(QemuRecMutex *mutex) +void qemu_rec_mutex_lock_impl(QemuRecMutex *mutex, const char *file, int line) { assert(mutex->initialized); EnterCriticalSection(&mutex->lock); } -int qemu_rec_mutex_trylock(QemuRecMutex *mutex) +int qemu_rec_mutex_trylock_impl(QemuRecMutex *mutex, const char *file, int line) { assert(mutex->initialized); return !TryEnterCriticalSection(&mutex->lock); diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 86bfe84037..1cc1b2f2c3 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -339,14 +339,19 @@ int qemu_poll_ns(GPollFD *fds, guint nfds, int64_t timeout) } -void timer_init_tl(QEMUTimer *ts, - QEMUTimerList *timer_list, int scale, - QEMUTimerCB *cb, void *opaque) +void timer_init_full(QEMUTimer *ts, + QEMUTimerListGroup *timer_list_group, QEMUClockType type, + int scale, int attributes, + QEMUTimerCB *cb, void *opaque) { - ts->timer_list = timer_list; + if (!timer_list_group) { + timer_list_group = &main_loop_tlg; + } + ts->timer_list = timer_list_group->tl[type]; ts->cb = cb; ts->opaque = opaque; ts->scale = scale; + ts->attributes = attributes; ts->expire_time = -1; } @@ -484,6 +489,7 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) bool progress = false; QEMUTimerCB *cb; void *opaque; + bool need_replay_checkpoint = false; if (!atomic_read(&timer_list->active_timers)) { return false; @@ -499,8 +505,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) break; default: case QEMU_CLOCK_VIRTUAL: - if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { - goto out; + if (replay_mode != REPLAY_MODE_NONE) { + /* Checkpoint for virtual clock is redundant in cases where + * it's being triggered with only non-EXTERNAL timers, because + * these timers don't change guest state directly. + * Since it has conditional dependence on specific timers, it is + * subject to race conditions and requires special handling. + * See below. + */ + need_replay_checkpoint = true; } break; case QEMU_CLOCK_HOST: @@ -515,14 +528,39 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) break; } + /* + * Extract expired timers from active timers list and and process them. + * + * In rr mode we need "filtered" checkpointing for virtual clock. The + * checkpoint must be recorded/replayed before processing any non-EXTERNAL timer, + * and that must only be done once since the clock value stays the same. Because + * non-EXTERNAL timers may appear in the timers list while it being processed, + * the checkpoint can be issued at a time until no timers are left and we are + * done". + */ current_time = qemu_clock_get_ns(timer_list->clock->type); - for(;;) { - qemu_mutex_lock(&timer_list->active_timers_lock); - ts = timer_list->active_timers; + qemu_mutex_lock(&timer_list->active_timers_lock); + while ((ts = timer_list->active_timers)) { if (!timer_expired_ns(ts, current_time)) { - qemu_mutex_unlock(&timer_list->active_timers_lock); + /* No expired timers left. The checkpoint can be skipped + * if no timers fired or they were all external. + */ break; } + if (need_replay_checkpoint + && !(ts->attributes & QEMU_TIMER_ATTR_EXTERNAL)) { + /* once we got here, checkpoint clock only once */ + need_replay_checkpoint = false; + qemu_mutex_unlock(&timer_list->active_timers_lock); + if (!replay_checkpoint(CHECKPOINT_CLOCK_VIRTUAL)) { + goto out; + } + qemu_mutex_lock(&timer_list->active_timers_lock); + /* The lock was released; start over again in case the list was + * modified. + */ + continue; + } /* remove timer from the list before calling the callback */ timer_list->active_timers = ts->next; @@ -530,12 +568,15 @@ bool timerlist_run_timers(QEMUTimerList *timer_list) ts->expire_time = -1; cb = ts->cb; opaque = ts->opaque; - qemu_mutex_unlock(&timer_list->active_timers_lock); /* run the callback (the timer list can be modified) */ + qemu_mutex_unlock(&timer_list->active_timers_lock); cb(opaque); + qemu_mutex_lock(&timer_list->active_timers_lock); + progress = true; } + qemu_mutex_unlock(&timer_list->active_timers_lock); out: qemu_event_set(&timer_list->timers_done_ev); diff --git a/util/qht.c b/util/qht.c index c138777a9c..aa51be3c52 100644 --- a/util/qht.c +++ b/util/qht.c @@ -89,6 +89,46 @@ #define QHT_BUCKET_ENTRIES 4 #endif +enum qht_iter_type { + QHT_ITER_VOID, /* do nothing; use retvoid */ + QHT_ITER_RM, /* remove element if retbool returns true */ +}; + +struct qht_iter { + union { + qht_iter_func_t retvoid; + qht_iter_bool_func_t retbool; + } f; + enum qht_iter_type type; +}; + +/* + * Do _not_ use qemu_mutex_[try]lock directly! Use these macros, otherwise + * the profiler (QSP) will deadlock. + */ +static inline void qht_lock(struct qht *ht) +{ + if (ht->mode & QHT_MODE_RAW_MUTEXES) { + qemu_mutex_lock__raw(&ht->lock); + } else { + qemu_mutex_lock(&ht->lock); + } +} + +static inline int qht_trylock(struct qht *ht) +{ + if (ht->mode & QHT_MODE_RAW_MUTEXES) { + return qemu_mutex_trylock__raw(&(ht)->lock); + } + return qemu_mutex_trylock(&(ht)->lock); +} + +/* this inline is not really necessary, but it helps keep code consistent */ +static inline void qht_unlock(struct qht *ht) +{ + qemu_mutex_unlock(&ht->lock); +} + /* * Note: reading partially-updated pointers in @pointers could lead to * segfaults. We thus access them with atomic_read/set; this guarantees @@ -196,7 +236,7 @@ static inline void qht_head_init(struct qht_bucket *b) } static inline -struct qht_bucket *qht_map_to_bucket(struct qht_map *map, uint32_t hash) +struct qht_bucket *qht_map_to_bucket(const struct qht_map *map, uint32_t hash) { return &map->buckets[hash & (map->n_buckets - 1)]; } @@ -228,7 +268,8 @@ static void qht_map_unlock_buckets(struct qht_map *map) * Call with at least a bucket lock held. * @map should be the value read before acquiring the lock (or locks). */ -static inline bool qht_map_is_stale__locked(struct qht *ht, struct qht_map *map) +static inline bool qht_map_is_stale__locked(const struct qht *ht, + const struct qht_map *map) { return map != ht->map; } @@ -254,10 +295,10 @@ void qht_map_lock_buckets__no_stale(struct qht *ht, struct qht_map **pmap) qht_map_unlock_buckets(map); /* we raced with a resize; acquire ht->lock to see the updated ht->map */ - qemu_mutex_lock(&ht->lock); + qht_lock(ht); map = ht->map; qht_map_lock_buckets(map); - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); *pmap = map; return; } @@ -288,21 +329,21 @@ struct qht_bucket *qht_bucket_lock__no_stale(struct qht *ht, uint32_t hash, qemu_spin_unlock(&b->lock); /* we raced with a resize; acquire ht->lock to see the updated ht->map */ - qemu_mutex_lock(&ht->lock); + qht_lock(ht); map = ht->map; b = qht_map_to_bucket(map, hash); qemu_spin_lock(&b->lock); - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); *pmap = map; return b; } -static inline bool qht_map_needs_resize(struct qht_map *map) +static inline bool qht_map_needs_resize(const struct qht_map *map) { return atomic_read(&map->n_added_buckets) > map->n_added_buckets_threshold; } -static inline void qht_chain_destroy(struct qht_bucket *head) +static inline void qht_chain_destroy(const struct qht_bucket *head) { struct qht_bucket *curr = head->next; struct qht_bucket *prev; @@ -430,22 +471,22 @@ bool qht_reset_size(struct qht *ht, size_t n_elems) n_buckets = qht_elems_to_buckets(n_elems); - qemu_mutex_lock(&ht->lock); + qht_lock(ht); map = ht->map; if (n_buckets != map->n_buckets) { new = qht_map_create(n_buckets); } qht_do_resize_and_reset(ht, new); - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); return !!new; } static inline -void *qht_do_lookup(struct qht_bucket *head, qht_lookup_func_t func, +void *qht_do_lookup(const struct qht_bucket *head, qht_lookup_func_t func, const void *userp, uint32_t hash) { - struct qht_bucket *b = head; + const struct qht_bucket *b = head; int i; do { @@ -469,7 +510,7 @@ void *qht_do_lookup(struct qht_bucket *head, qht_lookup_func_t func, } static __attribute__((noinline)) -void *qht_lookup__slowpath(struct qht_bucket *b, qht_lookup_func_t func, +void *qht_lookup__slowpath(const struct qht_bucket *b, qht_lookup_func_t func, const void *userp, uint32_t hash) { unsigned int version; @@ -482,11 +523,11 @@ void *qht_lookup__slowpath(struct qht_bucket *b, qht_lookup_func_t func, return ret; } -void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, +void *qht_lookup_custom(const struct qht *ht, const void *userp, uint32_t hash, qht_lookup_func_t func) { - struct qht_bucket *b; - struct qht_map *map; + const struct qht_bucket *b; + const struct qht_map *map; unsigned int version; void *ret; @@ -505,13 +546,16 @@ void *qht_lookup_custom(struct qht *ht, const void *userp, uint32_t hash, return qht_lookup__slowpath(b, func, userp, hash); } -void *qht_lookup(struct qht *ht, const void *userp, uint32_t hash) +void *qht_lookup(const struct qht *ht, const void *userp, uint32_t hash) { return qht_lookup_custom(ht, userp, hash, ht->cmp); } -/* call with head->lock held */ -static void *qht_insert__locked(struct qht *ht, struct qht_map *map, +/* + * call with head->lock held + * @ht is const since it is only used for ht->cmp() + */ +static void *qht_insert__locked(const struct qht *ht, struct qht_map *map, struct qht_bucket *head, void *p, uint32_t hash, bool *needs_resize) { @@ -565,7 +609,7 @@ static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) * If the lock is taken it probably means there's an ongoing resize, * so bail out. */ - if (qemu_mutex_trylock(&ht->lock)) { + if (qht_trylock(ht)) { return; } map = ht->map; @@ -575,7 +619,7 @@ static __attribute__((noinline)) void qht_grow_maybe(struct qht *ht) qht_do_resize(ht, new); } - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); } bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) @@ -605,7 +649,7 @@ bool qht_insert(struct qht *ht, void *p, uint32_t hash, void **existing) return false; } -static inline bool qht_entry_is_last(struct qht_bucket *b, int pos) +static inline bool qht_entry_is_last(const struct qht_bucket *b, int pos) { if (pos == QHT_BUCKET_ENTRIES - 1) { if (b->next == NULL) { @@ -631,7 +675,7 @@ qht_entry_move(struct qht_bucket *to, int i, struct qht_bucket *from, int j) } /* - * Find the last valid entry in @head, and swap it with @orig[pos], which has + * Find the last valid entry in @orig, and swap it with @orig[pos], which has * just been invalidated. */ static inline void qht_bucket_remove_entry(struct qht_bucket *orig, int pos) @@ -665,8 +709,7 @@ static inline void qht_bucket_remove_entry(struct qht_bucket *orig, int pos) /* call with b->lock held */ static inline -bool qht_remove__locked(struct qht_map *map, struct qht_bucket *head, - const void *p, uint32_t hash) +bool qht_remove__locked(struct qht_bucket *head, const void *p, uint32_t hash) { struct qht_bucket *b = head; int i; @@ -701,15 +744,16 @@ bool qht_remove(struct qht *ht, const void *p, uint32_t hash) qht_debug_assert(p); b = qht_bucket_lock__no_stale(ht, hash, &map); - ret = qht_remove__locked(map, b, p, hash); + ret = qht_remove__locked(b, p, hash); qht_bucket_debug__locked(b); qemu_spin_unlock(&b->lock); return ret; } -static inline void qht_bucket_iter(struct qht *ht, struct qht_bucket *b, - qht_iter_func_t func, void *userp) +static inline void qht_bucket_iter(struct qht_bucket *head, + const struct qht_iter *iter, void *userp) { + struct qht_bucket *b = head; int i; do { @@ -717,37 +761,83 @@ static inline void qht_bucket_iter(struct qht *ht, struct qht_bucket *b, if (b->pointers[i] == NULL) { return; } - func(ht, b->pointers[i], b->hashes[i], userp); + switch (iter->type) { + case QHT_ITER_VOID: + iter->f.retvoid(b->pointers[i], b->hashes[i], userp); + break; + case QHT_ITER_RM: + if (iter->f.retbool(b->pointers[i], b->hashes[i], userp)) { + /* replace i with the last valid element in the bucket */ + seqlock_write_begin(&head->sequence); + qht_bucket_remove_entry(b, i); + seqlock_write_end(&head->sequence); + qht_bucket_debug__locked(b); + /* reevaluate i, since it just got replaced */ + i--; + continue; + } + break; + default: + g_assert_not_reached(); + } } b = b->next; } while (b); } /* call with all of the map's locks held */ -static inline void qht_map_iter__all_locked(struct qht *ht, struct qht_map *map, - qht_iter_func_t func, void *userp) +static inline void qht_map_iter__all_locked(struct qht_map *map, + const struct qht_iter *iter, + void *userp) { size_t i; for (i = 0; i < map->n_buckets; i++) { - qht_bucket_iter(ht, &map->buckets[i], func, userp); + qht_bucket_iter(&map->buckets[i], iter, userp); } } -void qht_iter(struct qht *ht, qht_iter_func_t func, void *userp) +static inline void +do_qht_iter(struct qht *ht, const struct qht_iter *iter, void *userp) { struct qht_map *map; map = atomic_rcu_read(&ht->map); qht_map_lock_buckets(map); - /* Note: ht here is merely for carrying ht->mode; ht->map won't be read */ - qht_map_iter__all_locked(ht, map, func, userp); + qht_map_iter__all_locked(map, iter, userp); qht_map_unlock_buckets(map); } -static void qht_map_copy(struct qht *ht, void *p, uint32_t hash, void *userp) +void qht_iter(struct qht *ht, qht_iter_func_t func, void *userp) { - struct qht_map *new = userp; + const struct qht_iter iter = { + .f.retvoid = func, + .type = QHT_ITER_VOID, + }; + + do_qht_iter(ht, &iter, userp); +} + +void qht_iter_remove(struct qht *ht, qht_iter_bool_func_t func, void *userp) +{ + const struct qht_iter iter = { + .f.retbool = func, + .type = QHT_ITER_RM, + }; + + do_qht_iter(ht, &iter, userp); +} + +struct qht_map_copy_data { + struct qht *ht; + struct qht_map *new; +}; + +static void qht_map_copy(void *p, uint32_t hash, void *userp) +{ + struct qht_map_copy_data *data = userp; + struct qht *ht = data->ht; + struct qht_map *new = data->new; struct qht_bucket *b = qht_map_to_bucket(new, hash); /* no need to acquire b->lock because no thread has seen this map yet */ @@ -761,6 +851,11 @@ static void qht_map_copy(struct qht *ht, void *p, uint32_t hash, void *userp) static void qht_do_resize_reset(struct qht *ht, struct qht_map *new, bool reset) { struct qht_map *old; + const struct qht_iter iter = { + .f.retvoid = qht_map_copy, + .type = QHT_ITER_VOID, + }; + struct qht_map_copy_data data; old = ht->map; qht_map_lock_buckets(old); @@ -775,7 +870,9 @@ static void qht_do_resize_reset(struct qht *ht, struct qht_map *new, bool reset) } g_assert(new->n_buckets != old->n_buckets); - qht_map_iter__all_locked(ht, old, qht_map_copy, new); + data.ht = ht; + data.new = new; + qht_map_iter__all_locked(old, &iter, &data); qht_map_debug__all_locked(new); atomic_rcu_set(&ht->map, new); @@ -788,7 +885,7 @@ bool qht_resize(struct qht *ht, size_t n_elems) size_t n_buckets = qht_elems_to_buckets(n_elems); size_t ret = false; - qemu_mutex_lock(&ht->lock); + qht_lock(ht); if (n_buckets != ht->map->n_buckets) { struct qht_map *new; @@ -796,15 +893,15 @@ bool qht_resize(struct qht *ht, size_t n_elems) qht_do_resize(ht, new); ret = true; } - qemu_mutex_unlock(&ht->lock); + qht_unlock(ht); return ret; } /* pass @stats to qht_statistics_destroy() when done */ -void qht_statistics_init(struct qht *ht, struct qht_stats *stats) +void qht_statistics_init(const struct qht *ht, struct qht_stats *stats) { - struct qht_map *map; + const struct qht_map *map; int i; map = atomic_rcu_read(&ht->map); @@ -821,8 +918,8 @@ void qht_statistics_init(struct qht *ht, struct qht_stats *stats) stats->head_buckets = map->n_buckets; for (i = 0; i < map->n_buckets; i++) { - struct qht_bucket *head = &map->buckets[i]; - struct qht_bucket *b; + const struct qht_bucket *head = &map->buckets[i]; + const struct qht_bucket *b; unsigned int version; size_t buckets; size_t entries; diff --git a/util/qsp.c b/util/qsp.c new file mode 100644 index 0000000000..410f1ba004 --- /dev/null +++ b/util/qsp.c @@ -0,0 +1,794 @@ +/* + * qsp.c - QEMU Synchronization Profiler + * + * Copyright (C) 2018, Emilio G. Cota + * + * License: GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + * + * QSP profiles the time spent in synchronization primitives, which can + * help diagnose performance problems, e.g. scalability issues when + * contention is high. + * + * The primitives currently supported are mutexes, recursive mutexes and + * condition variables. Note that not all related functions are intercepted; + * instead we profile only those functions that can have a performance impact, + * either due to blocking (e.g. cond_wait, mutex_lock) or cache line + * contention (e.g. mutex_lock, mutex_trylock). + * + * QSP's design focuses on speed and scalability. This is achieved + * by having threads do their profiling entirely on thread-local data. + * The appropriate thread-local data is found via a QHT, i.e. a concurrent hash + * table. To aggregate data in order to generate a report, we iterate over + * all entries in the hash table. Depending on the number of threads and + * synchronization objects this might be expensive, but note that it is + * very rarely called -- reports are generated only when requested by users. + * + * Reports are generated as a table where each row represents a call site. A + * call site is the triplet formed by the __file__ and __LINE__ of the caller + * as well as the address of the "object" (i.e. mutex, rec. mutex or condvar) + * being operated on. Optionally, call sites that operate on different objects + * of the same type can be coalesced, which can be particularly useful when + * profiling dynamically-allocated objects. + * + * Alternative designs considered: + * + * - Use an off-the-shelf profiler such as mutrace. This is not a viable option + * for us because QEMU has __malloc_hook set (by one of the libraries it + * uses); leaving this hook unset is required to avoid deadlock in mutrace. + * + * - Use a glib HT for each thread, protecting each HT with its own lock. + * This isn't simpler than the current design, and is 10% slower in the + * atomic_add-bench microbenchmark (-m option). + * + * - For reports, just use a binary tree as we aggregate data, instead of having + * an intermediate hash table. This would simplify the code only slightly, but + * would perform badly if there were many threads and objects to track. + * + * - Wrap operations on qsp entries with RCU read-side critical sections, so + * that qsp_reset() can delete entries. Unfortunately, the overhead of calling + * rcu_read_lock/unlock slows down atomic_add-bench -m by 24%. Having + * a snapshot that is updated on qsp_reset() avoids this overhead. + * + * Related Work: + * - Lennart Poettering's mutrace: http://0pointer.de/blog/projects/mutrace.html + * - Lozi, David, Thomas, Lawall and Muller. "Remote Core Locking: Migrating + * Critical-Section Execution to Improve the Performance of Multithreaded + * Applications", USENIX ATC'12. + */ +#include "qemu/osdep.h" +#include "qemu/thread.h" +#include "qemu/timer.h" +#include "qemu/qht.h" +#include "qemu/rcu.h" +#include "qemu/xxhash.h" + +enum QSPType { + QSP_MUTEX, + QSP_BQL_MUTEX, + QSP_REC_MUTEX, + QSP_CONDVAR, +}; + +struct QSPCallSite { + const void *obj; + const char *file; /* i.e. __FILE__; shortened later */ + int line; + enum QSPType type; +}; +typedef struct QSPCallSite QSPCallSite; + +struct QSPEntry { + void *thread_ptr; + const QSPCallSite *callsite; + uint64_t n_acqs; + uint64_t ns; + unsigned int n_objs; /* count of coalesced objs; only used for reporting */ +}; +typedef struct QSPEntry QSPEntry; + +struct QSPSnapshot { + struct rcu_head rcu; + struct qht ht; +}; +typedef struct QSPSnapshot QSPSnapshot; + +/* initial sizing for hash tables */ +#define QSP_INITIAL_SIZE 64 + +/* If this file is moved, QSP_REL_PATH should be updated accordingly */ +#define QSP_REL_PATH "util/qsp.c" + +/* this file's full path. Used to present all call sites with relative paths */ +static size_t qsp_qemu_path_len; + +/* the address of qsp_thread gives us a unique 'thread ID' */ +static __thread int qsp_thread; + +/* + * Call sites are the same for all threads, so we track them in a separate hash + * table to save memory. + */ +static struct qht qsp_callsite_ht; + +static struct qht qsp_ht; +static QSPSnapshot *qsp_snapshot; +static bool qsp_initialized, qsp_initializing; + +static const char * const qsp_typenames[] = { + [QSP_MUTEX] = "mutex", + [QSP_BQL_MUTEX] = "BQL mutex", + [QSP_REC_MUTEX] = "rec_mutex", + [QSP_CONDVAR] = "condvar", +}; + +QemuMutexLockFunc qemu_bql_mutex_lock_func = qemu_mutex_lock_impl; +QemuMutexLockFunc qemu_mutex_lock_func = qemu_mutex_lock_impl; +QemuMutexTrylockFunc qemu_mutex_trylock_func = qemu_mutex_trylock_impl; +QemuRecMutexLockFunc qemu_rec_mutex_lock_func = qemu_rec_mutex_lock_impl; +QemuRecMutexTrylockFunc qemu_rec_mutex_trylock_func = + qemu_rec_mutex_trylock_impl; +QemuCondWaitFunc qemu_cond_wait_func = qemu_cond_wait_impl; + +/* + * It pays off to _not_ hash callsite->file; hashing a string is slow, and + * without it we still get a pretty unique hash. + */ +static inline +uint32_t do_qsp_callsite_hash(const QSPCallSite *callsite, uint64_t ab) +{ + uint64_t cd = (uint64_t)(uintptr_t)callsite->obj; + uint32_t e = callsite->line; + uint32_t f = callsite->type; + + return qemu_xxhash6(ab, cd, e, f); +} + +static inline +uint32_t qsp_callsite_hash(const QSPCallSite *callsite) +{ + return do_qsp_callsite_hash(callsite, 0); +} + +static inline uint32_t do_qsp_entry_hash(const QSPEntry *entry, uint64_t a) +{ + return do_qsp_callsite_hash(entry->callsite, a); +} + +static uint32_t qsp_entry_hash(const QSPEntry *entry) +{ + return do_qsp_entry_hash(entry, (uint64_t)(uintptr_t)entry->thread_ptr); +} + +static uint32_t qsp_entry_no_thread_hash(const QSPEntry *entry) +{ + return do_qsp_entry_hash(entry, 0); +} + +/* without the objects we need to hash the file name to get a decent hash */ +static uint32_t qsp_entry_no_thread_obj_hash(const QSPEntry *entry) +{ + const QSPCallSite *callsite = entry->callsite; + uint64_t ab = g_str_hash(callsite->file); + uint64_t cd = callsite->line; + uint32_t e = callsite->type; + + return qemu_xxhash5(ab, cd, e); +} + +static bool qsp_callsite_cmp(const void *ap, const void *bp) +{ + const QSPCallSite *a = ap; + const QSPCallSite *b = bp; + + return a == b || + (a->obj == b->obj && + a->line == b->line && + a->type == b->type && + (a->file == b->file || !strcmp(a->file, b->file))); +} + +static bool qsp_callsite_no_obj_cmp(const void *ap, const void *bp) +{ + const QSPCallSite *a = ap; + const QSPCallSite *b = bp; + + return a == b || + (a->line == b->line && + a->type == b->type && + (a->file == b->file || !strcmp(a->file, b->file))); +} + +static bool qsp_entry_no_thread_cmp(const void *ap, const void *bp) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + + return qsp_callsite_cmp(a->callsite, b->callsite); +} + +static bool qsp_entry_no_thread_obj_cmp(const void *ap, const void *bp) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + + return qsp_callsite_no_obj_cmp(a->callsite, b->callsite); +} + +static bool qsp_entry_cmp(const void *ap, const void *bp) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + + return a->thread_ptr == b->thread_ptr && + qsp_callsite_cmp(a->callsite, b->callsite); +} + +/* + * Normally we'd call this from a constructor function, but we want it to work + * via libutil as well. + */ +static void qsp_do_init(void) +{ + /* make sure this file's path in the tree is up to date with QSP_REL_PATH */ + g_assert(strstr(__FILE__, QSP_REL_PATH)); + qsp_qemu_path_len = strlen(__FILE__) - strlen(QSP_REL_PATH); + + qht_init(&qsp_ht, qsp_entry_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_init(&qsp_callsite_ht, qsp_callsite_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); +} + +static __attribute__((noinline)) void qsp_init__slowpath(void) +{ + if (atomic_cmpxchg(&qsp_initializing, false, true) == false) { + qsp_do_init(); + atomic_set(&qsp_initialized, true); + } else { + while (!atomic_read(&qsp_initialized)) { + cpu_relax(); + } + } +} + +/* qsp_init() must be called from _all_ exported functions */ +static inline void qsp_init(void) +{ + if (likely(atomic_read(&qsp_initialized))) { + return; + } + qsp_init__slowpath(); +} + +static QSPCallSite *qsp_callsite_find(const QSPCallSite *orig) +{ + QSPCallSite *callsite; + uint32_t hash; + + hash = qsp_callsite_hash(orig); + callsite = qht_lookup(&qsp_callsite_ht, orig, hash); + if (callsite == NULL) { + void *existing = NULL; + + callsite = g_new(QSPCallSite, 1); + memcpy(callsite, orig, sizeof(*callsite)); + qht_insert(&qsp_callsite_ht, callsite, hash, &existing); + if (unlikely(existing)) { + g_free(callsite); + callsite = existing; + } + } + return callsite; +} + +static QSPEntry * +qsp_entry_create(struct qht *ht, const QSPEntry *entry, uint32_t hash) +{ + QSPEntry *e; + void *existing = NULL; + + e = g_new0(QSPEntry, 1); + e->thread_ptr = entry->thread_ptr; + e->callsite = qsp_callsite_find(entry->callsite); + + qht_insert(ht, e, hash, &existing); + if (unlikely(existing)) { + g_free(e); + e = existing; + } + return e; +} + +static QSPEntry * +qsp_entry_find(struct qht *ht, const QSPEntry *entry, uint32_t hash) +{ + QSPEntry *e; + + e = qht_lookup(ht, entry, hash); + if (e == NULL) { + e = qsp_entry_create(ht, entry, hash); + } + return e; +} + +/* + * Note: Entries are never removed, so callers do not have to be in an RCU + * read-side critical section. + */ +static QSPEntry *qsp_entry_get(const void *obj, const char *file, int line, + enum QSPType type) +{ + QSPCallSite callsite = { + .obj = obj, + .file = file, + .line = line, + .type = type, + }; + QSPEntry orig; + uint32_t hash; + + qsp_init(); + + orig.thread_ptr = &qsp_thread; + orig.callsite = &callsite; + + hash = qsp_entry_hash(&orig); + return qsp_entry_find(&qsp_ht, &orig, hash); +} + +/* + * @e is in the global hash table; it is only written to by the current thread, + * so we write to it atomically (as in "write once") to prevent torn reads. + */ +static inline void do_qsp_entry_record(QSPEntry *e, int64_t delta, bool acq) +{ + atomic_set_u64(&e->ns, e->ns + delta); + if (acq) { + atomic_set_u64(&e->n_acqs, e->n_acqs + 1); + } +} + +static inline void qsp_entry_record(QSPEntry *e, int64_t delta) +{ + do_qsp_entry_record(e, delta, true); +} + +#define QSP_GEN_VOID(type_, qsp_t_, func_, impl_) \ + static void func_(type_ *obj, const char *file, int line) \ + { \ + QSPEntry *e; \ + int64_t t0, t1; \ + \ + t0 = get_clock(); \ + impl_(obj, file, line); \ + t1 = get_clock(); \ + \ + e = qsp_entry_get(obj, file, line, qsp_t_); \ + qsp_entry_record(e, t1 - t0); \ + } + +#define QSP_GEN_RET1(type_, qsp_t_, func_, impl_) \ + static int func_(type_ *obj, const char *file, int line) \ + { \ + QSPEntry *e; \ + int64_t t0, t1; \ + int err; \ + \ + t0 = get_clock(); \ + err = impl_(obj, file, line); \ + t1 = get_clock(); \ + \ + e = qsp_entry_get(obj, file, line, qsp_t_); \ + do_qsp_entry_record(e, t1 - t0, !err); \ + return err; \ + } + +QSP_GEN_VOID(QemuMutex, QSP_BQL_MUTEX, qsp_bql_mutex_lock, qemu_mutex_lock_impl) +QSP_GEN_VOID(QemuMutex, QSP_MUTEX, qsp_mutex_lock, qemu_mutex_lock_impl) +QSP_GEN_RET1(QemuMutex, QSP_MUTEX, qsp_mutex_trylock, qemu_mutex_trylock_impl) + +QSP_GEN_VOID(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_lock, + qemu_rec_mutex_lock_impl) +QSP_GEN_RET1(QemuRecMutex, QSP_REC_MUTEX, qsp_rec_mutex_trylock, + qemu_rec_mutex_trylock_impl) + +#undef QSP_GEN_RET1 +#undef QSP_GEN_VOID + +static void +qsp_cond_wait(QemuCond *cond, QemuMutex *mutex, const char *file, int line) +{ + QSPEntry *e; + int64_t t0, t1; + + t0 = get_clock(); + qemu_cond_wait_impl(cond, mutex, file, line); + t1 = get_clock(); + + e = qsp_entry_get(cond, file, line, QSP_CONDVAR); + qsp_entry_record(e, t1 - t0); +} + +bool qsp_is_enabled(void) +{ + return atomic_read(&qemu_mutex_lock_func) == qsp_mutex_lock; +} + +void qsp_enable(void) +{ + atomic_set(&qemu_mutex_lock_func, qsp_mutex_lock); + atomic_set(&qemu_mutex_trylock_func, qsp_mutex_trylock); + atomic_set(&qemu_bql_mutex_lock_func, qsp_bql_mutex_lock); + atomic_set(&qemu_rec_mutex_lock_func, qsp_rec_mutex_lock); + atomic_set(&qemu_rec_mutex_trylock_func, qsp_rec_mutex_trylock); + atomic_set(&qemu_cond_wait_func, qsp_cond_wait); +} + +void qsp_disable(void) +{ + atomic_set(&qemu_mutex_lock_func, qemu_mutex_lock_impl); + atomic_set(&qemu_mutex_trylock_func, qemu_mutex_trylock_impl); + atomic_set(&qemu_bql_mutex_lock_func, qemu_mutex_lock_impl); + atomic_set(&qemu_rec_mutex_lock_func, qemu_rec_mutex_lock_impl); + atomic_set(&qemu_rec_mutex_trylock_func, qemu_rec_mutex_trylock_impl); + atomic_set(&qemu_cond_wait_func, qemu_cond_wait_impl); +} + +static gint qsp_tree_cmp(gconstpointer ap, gconstpointer bp, gpointer up) +{ + const QSPEntry *a = ap; + const QSPEntry *b = bp; + enum QSPSortBy sort_by = *(enum QSPSortBy *)up; + const QSPCallSite *ca; + const QSPCallSite *cb; + + switch (sort_by) { + case QSP_SORT_BY_TOTAL_WAIT_TIME: + if (a->ns > b->ns) { + return -1; + } else if (a->ns < b->ns) { + return 1; + } + break; + case QSP_SORT_BY_AVG_WAIT_TIME: + { + double avg_a = a->n_acqs ? a->ns / a->n_acqs : 0; + double avg_b = b->n_acqs ? b->ns / b->n_acqs : 0; + + if (avg_a > avg_b) { + return -1; + } else if (avg_a < avg_b) { + return 1; + } + break; + } + default: + g_assert_not_reached(); + } + + ca = a->callsite; + cb = b->callsite; + /* Break the tie with the object's address */ + if (ca->obj < cb->obj) { + return -1; + } else if (ca->obj > cb->obj) { + return 1; + } else { + int cmp; + + /* same obj. Break the tie with the callsite's file */ + cmp = strcmp(ca->file, cb->file); + if (cmp) { + return cmp; + } + /* same callsite file. Break the tie with the callsite's line */ + g_assert(ca->line != cb->line); + if (ca->line < cb->line) { + return -1; + } else if (ca->line > cb->line) { + return 1; + } else { + /* break the tie with the callsite's type */ + return cb->type - ca->type; + } + } +} + +static void qsp_sort(void *p, uint32_t h, void *userp) +{ + QSPEntry *e = p; + GTree *tree = userp; + + g_tree_insert(tree, e, NULL); +} + +static void qsp_aggregate(void *p, uint32_t h, void *up) +{ + struct qht *ht = up; + const QSPEntry *e = p; + QSPEntry *agg; + uint32_t hash; + + hash = qsp_entry_no_thread_hash(e); + agg = qsp_entry_find(ht, e, hash); + /* + * The entry is in the global hash table; read from it atomically (as in + * "read once"). + */ + agg->ns += atomic_read_u64(&e->ns); + agg->n_acqs += atomic_read_u64(&e->n_acqs); +} + +static void qsp_iter_diff(void *p, uint32_t hash, void *htp) +{ + struct qht *ht = htp; + QSPEntry *old = p; + QSPEntry *new; + + new = qht_lookup(ht, old, hash); + /* entries are never deleted, so we must have this one */ + g_assert(new != NULL); + /* our reading of the stats happened after the snapshot was taken */ + g_assert(new->n_acqs >= old->n_acqs); + g_assert(new->ns >= old->ns); + + new->n_acqs -= old->n_acqs; + new->ns -= old->ns; + + /* No point in reporting an empty entry */ + if (new->n_acqs == 0 && new->ns == 0) { + bool removed = qht_remove(ht, new, hash); + + g_assert(removed); + g_free(new); + } +} + +static void qsp_diff(struct qht *orig, struct qht *new) +{ + qht_iter(orig, qsp_iter_diff, new); +} + +static void qsp_iter_callsite_coalesce(void *p, uint32_t h, void *htp) +{ + struct qht *ht = htp; + QSPEntry *old = p; + QSPEntry *e; + uint32_t hash; + + hash = qsp_entry_no_thread_obj_hash(old); + e = qht_lookup(ht, old, hash); + if (e == NULL) { + e = qsp_entry_create(ht, old, hash); + e->n_objs = 1; + } else if (e->callsite->obj != old->callsite->obj) { + e->n_objs++; + } + e->ns += old->ns; + e->n_acqs += old->n_acqs; +} + +static void qsp_ht_delete(void *p, uint32_t h, void *htp) +{ + g_free(p); +} + +static void qsp_mktree(GTree *tree, bool callsite_coalesce) +{ + QSPSnapshot *snap; + struct qht ht, coalesce_ht; + struct qht *htp; + + /* + * First, see if there's a prior snapshot, so that we read the global hash + * table _after_ the snapshot has been created, which guarantees that + * the entries we'll read will be a superset of the snapshot's entries. + * + * We must remain in an RCU read-side critical section until we're done + * with the snapshot. + */ + rcu_read_lock(); + snap = atomic_rcu_read(&qsp_snapshot); + + /* Aggregate all results from the global hash table into a local one */ + qht_init(&ht, qsp_entry_no_thread_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_iter(&qsp_ht, qsp_aggregate, &ht); + + /* compute the difference wrt the snapshot, if any */ + if (snap) { + qsp_diff(&snap->ht, &ht); + } + /* done with the snapshot; RCU can reclaim it */ + rcu_read_unlock(); + + htp = &ht; + if (callsite_coalesce) { + qht_init(&coalesce_ht, qsp_entry_no_thread_obj_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + qht_iter(&ht, qsp_iter_callsite_coalesce, &coalesce_ht); + + /* free the previous hash table, and point htp to coalesce_ht */ + qht_iter(&ht, qsp_ht_delete, NULL); + qht_destroy(&ht); + htp = &coalesce_ht; + } + + /* sort the hash table elements by using a tree */ + qht_iter(htp, qsp_sort, tree); + + /* free the hash table, but keep the elements (those are in the tree now) */ + qht_destroy(htp); +} + +/* free string with g_free */ +static char *qsp_at(const QSPCallSite *callsite) +{ + GString *s = g_string_new(NULL); + const char *shortened; + + /* remove the absolute path to qemu */ + if (unlikely(strlen(callsite->file) < qsp_qemu_path_len)) { + shortened = callsite->file; + } else { + shortened = callsite->file + qsp_qemu_path_len; + } + g_string_append_printf(s, "%s:%u", shortened, callsite->line); + return g_string_free(s, FALSE); +} + +struct QSPReportEntry { + const void *obj; + char *callsite_at; + const char *typename; + double time_s; + double ns_avg; + uint64_t n_acqs; + unsigned int n_objs; +}; +typedef struct QSPReportEntry QSPReportEntry; + +struct QSPReport { + QSPReportEntry *entries; + size_t n_entries; + size_t max_n_entries; +}; +typedef struct QSPReport QSPReport; + +static gboolean qsp_tree_report(gpointer key, gpointer value, gpointer udata) +{ + const QSPEntry *e = key; + QSPReport *report = udata; + QSPReportEntry *entry; + + if (report->n_entries == report->max_n_entries) { + return TRUE; + } + entry = &report->entries[report->n_entries]; + report->n_entries++; + + entry->obj = e->callsite->obj; + entry->n_objs = e->n_objs; + entry->callsite_at = qsp_at(e->callsite); + entry->typename = qsp_typenames[e->callsite->type]; + entry->time_s = e->ns * 1e-9; + entry->n_acqs = e->n_acqs; + entry->ns_avg = e->n_acqs ? e->ns / e->n_acqs : 0; + return FALSE; +} + +static void +pr_report(const QSPReport *rep, FILE *f, fprintf_function pr) +{ + char *dashes; + size_t max_len = 0; + int callsite_len = 0; + int callsite_rspace; + int n_dashes; + size_t i; + + /* find out the maximum length of all 'callsite' fields */ + for (i = 0; i < rep->n_entries; i++) { + const QSPReportEntry *e = &rep->entries[i]; + size_t len = strlen(e->callsite_at); + + if (len > max_len) { + max_len = len; + } + } + + callsite_len = MAX(max_len, strlen("Call site")); + /* white space to leave to the right of "Call site" */ + callsite_rspace = callsite_len - strlen("Call site"); + + pr(f, "Type Object Call site%*s Wait Time (s) " + " Count Average (us)\n", callsite_rspace, ""); + + /* build a horizontal rule with dashes */ + n_dashes = 79 + callsite_rspace; + dashes = g_malloc(n_dashes + 1); + memset(dashes, '-', n_dashes); + dashes[n_dashes] = '\0'; + pr(f, "%s\n", dashes); + + for (i = 0; i < rep->n_entries; i++) { + const QSPReportEntry *e = &rep->entries[i]; + GString *s = g_string_new(NULL); + + g_string_append_printf(s, "%-9s ", e->typename); + if (e->n_objs > 1) { + g_string_append_printf(s, "[%12u]", e->n_objs); + } else { + g_string_append_printf(s, "%14p", e->obj); + } + g_string_append_printf(s, " %s%*s %13.5f %12" PRIu64 " %12.2f\n", + e->callsite_at, + callsite_len - (int)strlen(e->callsite_at), "", + e->time_s, e->n_acqs, e->ns_avg * 1e-3); + pr(f, "%s", s->str); + g_string_free(s, TRUE); + } + + pr(f, "%s\n", dashes); + g_free(dashes); +} + +static void report_destroy(QSPReport *rep) +{ + size_t i; + + for (i = 0; i < rep->n_entries; i++) { + QSPReportEntry *e = &rep->entries[i]; + + g_free(e->callsite_at); + } + g_free(rep->entries); +} + +void qsp_report(FILE *f, fprintf_function cpu_fprintf, size_t max, + enum QSPSortBy sort_by, bool callsite_coalesce) +{ + GTree *tree = g_tree_new_full(qsp_tree_cmp, &sort_by, g_free, NULL); + QSPReport rep; + + qsp_init(); + + rep.entries = g_new0(QSPReportEntry, max); + rep.n_entries = 0; + rep.max_n_entries = max; + + qsp_mktree(tree, callsite_coalesce); + g_tree_foreach(tree, qsp_tree_report, &rep); + g_tree_destroy(tree); + + pr_report(&rep, f, cpu_fprintf); + report_destroy(&rep); +} + +static void qsp_snapshot_destroy(QSPSnapshot *snap) +{ + qht_iter(&snap->ht, qsp_ht_delete, NULL); + qht_destroy(&snap->ht); + g_free(snap); +} + +void qsp_reset(void) +{ + QSPSnapshot *new = g_new(QSPSnapshot, 1); + QSPSnapshot *old; + + qsp_init(); + + qht_init(&new->ht, qsp_entry_cmp, QSP_INITIAL_SIZE, + QHT_MODE_AUTO_RESIZE | QHT_MODE_RAW_MUTEXES); + + /* take a snapshot of the current state */ + qht_iter(&qsp_ht, qsp_aggregate, &new->ht); + + /* replace the previous snapshot, if any */ + old = atomic_xchg(&qsp_snapshot, new); + if (old) { + call_rcu(old, qsp_snapshot_destroy, rcu); + } +} diff --git a/util/range.c b/util/range.c index 416df7cdae..098d9d2dc0 100644 --- a/util/range.c +++ b/util/range.c @@ -3,19 +3,18 @@ * * Copyright (c) 2015-2016 Red Hat, Inc. * - * This library is free software; you can redistribute it and/or + * 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 library is distributed in the hope that it will be useful, + * 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU General Public - * License along with this library; if not, see . + * 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" diff --git a/util/trace-events b/util/trace-events index 4822434c89..9dbd237dad 100644 --- a/util/trace-events +++ b/util/trace-events @@ -1,52 +1,60 @@ # See docs/devel/tracing.txt for syntax documentation. -# util/aio-posix.c -run_poll_handlers_begin(void *ctx, int64_t max_ns) "ctx %p max_ns %"PRId64 -run_poll_handlers_end(void *ctx, bool progress) "ctx %p progress %d" +# aio-posix.c +run_poll_handlers_begin(void *ctx, int64_t max_ns, int64_t timeout) "ctx %p max_ns %"PRId64 " timeout %"PRId64 +run_poll_handlers_end(void *ctx, bool progress, int64_t timeout) "ctx %p progress %d new timeout %"PRId64 poll_shrink(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 poll_grow(void *ctx, int64_t old, int64_t new) "ctx %p old %"PRId64" new %"PRId64 -# util/async.c +# async.c aio_co_schedule(void *ctx, void *co) "ctx %p co %p" aio_co_schedule_bh_cb(void *ctx, void *co) "ctx %p co %p" -# util/thread-pool.c +# 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" thread_pool_cancel(void *req, void *opaque) "req %p opaque %p" -# util/buffer.c +# 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/qemu-coroutine.c +# filemonitor-inotify.c +qemu_file_monitor_add_watch(void *mon, const char *dirpath, const char *filename, void *cb, void *opaque, int64_t id) "File monitor %p add watch dir='%s' file='%s' cb=%p opaque=%p id=%" PRId64 +qemu_file_monitor_remove_watch(void *mon, const char *dirpath, int64_t id) "File monitor %p remove watch dir='%s' id=%" PRId64 +qemu_file_monitor_new(void *mon, int fd) "File monitor %p created fd=%d" +qemu_file_monitor_enable_watch(void *mon, const char *dirpath, int id) "File monitor %p enable watch dir='%s' id=%u" +qemu_file_monitor_disable_watch(void *mon, const char *dirpath, int id) "Fle monitor %p disable watch dir='%s' id=%u" +qemu_file_monitor_event(void *mon, const char *dirpath, const char *filename, int mask, unsigned int id) "File monitor %p event dir='%s' file='%s' mask=0x%x id=%u" +qemu_file_monitor_dispatch(void *mon, const char *dirpath, const char *filename, int ev, void *cb, void *opaque, int64_t id) "File monitor %p dispatch dir='%s' file='%s' ev=%d cb=%p opaque=%p id=%" PRId64 + +# qemu-coroutine.c qemu_aio_coroutine_enter(void *ctx, void *from, void *to, void *opaque) "ctx %p from %p to %p opaque %p" qemu_coroutine_yield(void *from, void *to) "from %p to %p" qemu_coroutine_terminate(void *co) "self %p" -# util/qemu-coroutine-lock.c -qemu_co_queue_run_restart(void *co) "co %p" +# qemu-coroutine-lock.c qemu_co_mutex_lock_uncontended(void *mutex, void *self) "mutex %p self %p" qemu_co_mutex_lock_entry(void *mutex, void *self) "mutex %p self %p" qemu_co_mutex_lock_return(void *mutex, void *self) "mutex %p self %p" qemu_co_mutex_unlock_entry(void *mutex, void *self) "mutex %p self %p" qemu_co_mutex_unlock_return(void *mutex, void *self) "mutex %p self %p" -# util/oslib-win32.c -# util/oslib-posix.c +# oslib-win32.c +# oslib-posix.c qemu_memalign(size_t alignment, size_t size, void *ptr) "alignment %zu size %zu ptr %p" qemu_anon_ram_alloc(size_t size, void *ptr) "size %zu ptr %p" qemu_vfree(void *ptr) "ptr %p" qemu_anon_ram_free(void *ptr, size_t size) "ptr %p size %zu" -# util/hbitmap.c +# 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 -# util/lockcnt.c +# lockcnt.c lockcnt_fast_path_attempt(const void *lockcnt, int expected, int new) "lockcnt %p fast path %d->%d" lockcnt_fast_path_success(const void *lockcnt, int expected, int new) "lockcnt %p fast path %d->%d succeeded" lockcnt_unlock_attempt(const void *lockcnt, int expected, int new) "lockcnt %p unlock %d->%d" @@ -56,12 +64,12 @@ lockcnt_futex_wait(const void *lockcnt, int val) "lockcnt %p waiting on %d" lockcnt_futex_wait_resume(const void *lockcnt, int new) "lockcnt %p after wait: %d" lockcnt_futex_wake(const void *lockcnt) "lockcnt %p waking up one waiter" -# util/qemu-thread.c +# qemu-thread-common.h qemu_mutex_lock(void *mutex, const char *file, const int line) "waiting on mutex %p (%s:%d)" qemu_mutex_locked(void *mutex, const char *file, const int line) "taken mutex %p (%s:%d)" qemu_mutex_unlock(void *mutex, const char *file, const int line) "released mutex %p (%s:%d)" -# util/vfio-helpers.c +# vfio-helpers.c qemu_vfio_dma_reset_temporary(void *s) "s %p" qemu_vfio_ram_block_added(void *s, void *p, size_t size) "s %p host %p size 0x%zx" qemu_vfio_ram_block_removed(void *s, void *p, size_t size) "s %p host %p size 0x%zx" @@ -69,5 +77,4 @@ qemu_vfio_find_mapping(void *s, void *p) "s %p host %p" qemu_vfio_new_mapping(void *s, void *host, size_t size, int index, uint64_t iova) "s %p host %p size %zu index %d iova 0x%"PRIx64 qemu_vfio_do_mapping(void *s, void *host, size_t size, uint64_t iova) "s %p host %p size %zu iova 0x%"PRIx64 qemu_vfio_dma_map(void *s, void *host, size_t size, bool temporary, uint64_t *iova) "s %p host %p size %zu temporary %d iova %p" -qemu_vfio_dma_map_invalid(void *s, void *mapping_host, size_t mapping_size, void *host, size_t size) "s %p mapping %p %zu requested %p %zu" qemu_vfio_dma_unmap(void *s, void *host) "s %p host %p" diff --git a/util/unicode.c b/util/unicode.c index a812a35171..8580bc598b 100644 --- a/util/unicode.c +++ b/util/unicode.c @@ -13,6 +13,21 @@ #include "qemu/osdep.h" #include "qemu/unicode.h" +static bool is_valid_codepoint(int codepoint) +{ + if (codepoint > 0x10FFFFu) { + return false; /* beyond Unicode range */ + } + if ((codepoint >= 0xFDD0 && codepoint <= 0xFDEF) + || (codepoint & 0xFFFE) == 0xFFFE) { + return false; /* noncharacter */ + } + if (codepoint >= 0xD800 && codepoint <= 0xDFFF) { + return false; /* surrogate code point */ + } + return true; +} + /** * mod_utf8_codepoint: * @s: string encoded in modified UTF-8 @@ -83,13 +98,8 @@ int mod_utf8_codepoint(const char *s, size_t n, char **end) cp <<= 6; cp |= byte & 0x3F; } - if (cp > 0x10FFFF) { - cp = -1; /* beyond Unicode range */ - } else if ((cp >= 0xFDD0 && cp <= 0xFDEF) - || (cp & 0xFFFE) == 0xFFFE) { - cp = -1; /* noncharacter */ - } else if (cp >= 0xD800 && cp <= 0xDFFF) { - cp = -1; /* surrogate code point */ + if (!is_valid_codepoint(cp)) { + cp = -1; } else if (cp < min_cp[len - 2] && !(cp == 0 && len == 2)) { cp = -1; /* overlong, not \xC0\x80 */ } @@ -99,3 +109,48 @@ out: *end = (char *)p; return cp; } + +/** + * mod_utf8_encode: + * @buf: Destination buffer + * @bufsz: size of @buf, at least 5. + * @codepoint: Unicode codepoint to encode + * + * Convert Unicode codepoint @codepoint to modified UTF-8. + * + * Returns: the length of the UTF-8 sequence on success, -1 when + * @codepoint is invalid. + */ +ssize_t mod_utf8_encode(char buf[], size_t bufsz, int codepoint) +{ + assert(bufsz >= 5); + + if (!is_valid_codepoint(codepoint)) { + return -1; + } + + if (codepoint > 0 && codepoint <= 0x7F) { + buf[0] = codepoint & 0x7F; + buf[1] = 0; + return 1; + } + if (codepoint <= 0x7FF) { + buf[0] = 0xC0 | ((codepoint >> 6) & 0x1F); + buf[1] = 0x80 | (codepoint & 0x3F); + buf[2] = 0; + return 2; + } + if (codepoint <= 0xFFFF) { + buf[0] = 0xE0 | ((codepoint >> 12) & 0x0F); + buf[1] = 0x80 | ((codepoint >> 6) & 0x3F); + buf[2] = 0x80 | (codepoint & 0x3F); + buf[3] = 0; + return 3; + } + buf[0] = 0xF0 | ((codepoint >> 18) & 0x07); + buf[1] = 0x80 | ((codepoint >> 12) & 0x3F); + buf[2] = 0x80 | ((codepoint >> 6) & 0x3F); + buf[3] = 0x80 | (codepoint & 0x3F); + buf[4] = 0; + return 4; +} diff --git a/util/uuid.c b/util/uuid.c index ebf06c049a..5787f0978c 100644 --- a/util/uuid.c +++ b/util/uuid.c @@ -110,10 +110,10 @@ int qemu_uuid_parse(const char *str, QemuUUID *uuid) /* Swap from UUID format endian (BE) to the opposite or vice versa. */ -void qemu_uuid_bswap(QemuUUID *uuid) +QemuUUID qemu_uuid_bswap(QemuUUID uuid) { - assert(QEMU_PTR_IS_ALIGNED(uuid, sizeof(uint32_t))); - bswap32s(&uuid->fields.time_low); - bswap16s(&uuid->fields.time_mid); - bswap16s(&uuid->fields.time_high_and_version); + bswap32s(&uuid.fields.time_low); + bswap16s(&uuid.fields.time_mid); + bswap16s(&uuid.fields.time_high_and_version); + return uuid; } diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index 1d9272efa4..2367fe8f7f 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -348,7 +348,7 @@ static int qemu_vfio_init_pci(QEMUVFIOState *s, const char *device, goto fail; } - for (i = 0; i < 6; i++) { + for (i = 0; i < ARRAY_SIZE(s->bar_region_info); i++) { ret = qemu_vfio_pci_init_bar(s, i, errp); if (ret) { goto fail; @@ -391,10 +391,10 @@ static void qemu_vfio_ram_block_removed(RAMBlockNotifier *n, } } -static int qemu_vfio_init_ramblock(const char *block_name, void *host_addr, - ram_addr_t offset, ram_addr_t length, - void *opaque) +static int qemu_vfio_init_ramblock(RAMBlock *rb, void *opaque) { + void *host_addr = qemu_ram_get_host_addr(rb); + ram_addr_t length = qemu_ram_get_used_length(rb); int ret; QEMUVFIOState *s = opaque; @@ -411,13 +411,13 @@ static int qemu_vfio_init_ramblock(const char *block_name, void *host_addr, static void qemu_vfio_open_common(QEMUVFIOState *s) { + qemu_mutex_init(&s->lock); s->ram_notifier.ram_block_added = qemu_vfio_ram_block_added; s->ram_notifier.ram_block_removed = qemu_vfio_ram_block_removed; ram_block_notifier_add(&s->ram_notifier); s->low_water_mark = QEMU_VFIO_IOVA_MIN; s->high_water_mark = QEMU_VFIO_IOVA_MAX; qemu_ram_foreach_block(qemu_vfio_init_ramblock, s); - qemu_mutex_init(&s->lock); } /** diff --git a/vl.c b/vl.c index 16b913f9d5..c696ad2a13 100644 --- a/vl.c +++ b/vl.c @@ -61,7 +61,7 @@ int main(int argc, char **argv) #include "hw/display/vga.h" #include "hw/bt.h" #include "sysemu/watchdog.h" -#include "hw/smbios/smbios.h" +#include "hw/firmware/smbios.h" #include "hw/acpi/acpi.h" #include "hw/xen/xen.h" #include "hw/qdev.h" @@ -106,9 +106,6 @@ int main(int argc, char **argv) #include "disas/disas.h" - -#include "slirp/libslirp.h" - #include "trace-root.h" #include "trace/control.h" #include "qemu/queue.h" @@ -128,6 +125,7 @@ int main(int argc, char **argv) #include "qapi/qapi-commands-block-core.h" #include "qapi/qapi-commands-misc.h" #include "qapi/qapi-commands-run-state.h" +#include "qapi/qapi-commands-ui.h" #include "qapi/qmp/qerror.h" #include "sysemu/iothread.h" @@ -147,16 +145,21 @@ bool enable_cpu_pm = false; int nb_nics; NICInfo nd_table[MAX_NICS]; int autostart; -static int rtc_utc = 1; -static int rtc_date_offset = -1; /* -1 means no change */ +static enum { + RTC_BASE_UTC, + RTC_BASE_LOCALTIME, + RTC_BASE_DATETIME, +} rtc_base_type = RTC_BASE_UTC; +static time_t rtc_ref_start_datetime; +static int rtc_realtime_clock_offset; /* used only with QEMU_CLOCK_REALTIME */ +static int rtc_host_datetime_offset = -1; /* valid & used only with + RTC_BASE_DATETIME */ QEMUClockType rtc_clock; int vga_interface_type = VGA_NONE; static DisplayOptions dpy; -int no_frame; static int num_serial_hds; static Chardev **serial_hds; Chardev *parallel_hds[MAX_PARALLEL_PORTS]; -Chardev *virtcon_hds[MAX_VIRTIO_CONSOLES]; int win2k_install_hack = 0; int singlestep = 0; int smp_cpus; @@ -182,8 +185,8 @@ const char *prom_envs[MAX_PROM_ENVS]; int boot_menu; bool boot_strict; uint8_t *boot_splash_filedata; -size_t boot_splash_filedata_size; -uint8_t qemu_extra_params_fw[2]; +int only_migratable; /* turn it off unless user states otherwise */ +bool wakeup_suspend_enabled; int icount_align_option; @@ -207,7 +210,6 @@ bool xen_domid_restrict; static int has_defaults = 1; static int default_serial = 1; static int default_parallel = 1; -static int default_virtcon = 1; static int default_monitor = 1; static int default_floppy = 1; static int default_cdrom = 1; @@ -228,8 +230,6 @@ static struct { { .driver = "ide-drive", .flag = &default_cdrom }, { .driver = "scsi-cd", .flag = &default_cdrom }, { .driver = "scsi-hd", .flag = &default_cdrom }, - { .driver = "virtio-serial-pci", .flag = &default_virtcon }, - { .driver = "virtio-serial", .flag = &default_virtcon }, { .driver = "VGA", .flag = &default_vga }, { .driver = "isa-vga", .flag = &default_vga }, { .driver = "cirrus-vga", .flag = &default_vga }, @@ -237,11 +237,13 @@ static struct { { .driver = "vmware-svga", .flag = &default_vga }, { .driver = "qxl-vga", .flag = &default_vga }, { .driver = "virtio-vga", .flag = &default_vga }, + { .driver = "ati-vga", .flag = &default_vga }, }; static QemuOptsList qemu_rtc_opts = { .name = "rtc", .head = QTAILQ_HEAD_INITIALIZER(qemu_rtc_opts.head), + .merge_lists = true, .desc = { { .name = "base", @@ -328,10 +330,10 @@ static QemuOptsList qemu_boot_opts = { .type = QEMU_OPT_STRING, }, { .name = "splash-time", - .type = QEMU_OPT_STRING, + .type = QEMU_OPT_NUMBER, }, { .name = "reboot-timeout", - .type = QEMU_OPT_STRING, + .type = QEMU_OPT_NUMBER, }, { .name = "strict", .type = QEMU_OPT_BOOL, @@ -529,7 +531,7 @@ static QemuOptsList qemu_fw_cfg_opts = { }, { .name = "file", .type = QEMU_OPT_STRING, - .help = "Sets the name of the file from which\n" + .help = "Sets the name of the file from which " "the fw_cfg blob will be loaded", }, { .name = "string", @@ -722,6 +724,9 @@ void runstate_set(RunState new_state) { assert(new_state < RUN_STATE__MAX); + trace_runstate_set(current_run_state, RunState_str(current_run_state), + new_state, RunState_str(current_run_state)); + if (current_run_state == new_state) { return; } @@ -732,7 +737,7 @@ void runstate_set(RunState new_state) RunState_str(new_state)); abort(); } - trace_runstate_set(new_state); + current_run_state = new_state; } @@ -780,28 +785,42 @@ void qemu_system_vmstop_request(RunState state) } /***********************************************************/ -/* real time host monotonic timer */ - -static time_t qemu_time(void) +/* RTC reference time/date access */ +static time_t qemu_ref_timedate(QEMUClockType clock) { - return qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; + time_t value = qemu_clock_get_ms(clock) / 1000; + switch (clock) { + case QEMU_CLOCK_REALTIME: + value -= rtc_realtime_clock_offset; + /* no break */ + case QEMU_CLOCK_VIRTUAL: + value += rtc_ref_start_datetime; + break; + case QEMU_CLOCK_HOST: + if (rtc_base_type == RTC_BASE_DATETIME) { + value -= rtc_host_datetime_offset; + } + break; + default: + assert(0); + } + return value; } -/***********************************************************/ -/* host time/date access */ void qemu_get_timedate(struct tm *tm, int offset) { - time_t ti = qemu_time(); + time_t ti = qemu_ref_timedate(rtc_clock); ti += offset; - if (rtc_date_offset == -1) { - if (rtc_utc) - gmtime_r(&ti, tm); - else - localtime_r(&ti, tm); - } else { - ti -= rtc_date_offset; + + switch (rtc_base_type) { + case RTC_BASE_DATETIME: + case RTC_BASE_UTC: gmtime_r(&ti, tm); + break; + case RTC_BASE_LOCALTIME: + localtime_r(&ti, tm); + break; } } @@ -809,76 +828,77 @@ int qemu_timedate_diff(struct tm *tm) { time_t seconds; - if (rtc_date_offset == -1) - if (rtc_utc) - seconds = mktimegm(tm); - else { - struct tm tmp = *tm; - tmp.tm_isdst = -1; /* use timezone to figure it out */ - seconds = mktime(&tmp); - } - else - seconds = mktimegm(tm) + rtc_date_offset; + switch (rtc_base_type) { + case RTC_BASE_DATETIME: + case RTC_BASE_UTC: + seconds = mktimegm(tm); + break; + case RTC_BASE_LOCALTIME: + { + struct tm tmp = *tm; + tmp.tm_isdst = -1; /* use timezone to figure it out */ + seconds = mktime(&tmp); + break; + } + default: + abort(); + } - return seconds - qemu_time(); + return seconds - qemu_ref_timedate(QEMU_CLOCK_HOST); } -static void configure_rtc_date_offset(const char *startdate, int legacy) +static void configure_rtc_base_datetime(const char *startdate) { - time_t rtc_start_date; + time_t rtc_start_datetime; struct tm tm; - if (!strcmp(startdate, "now") && legacy) { - rtc_date_offset = -1; + if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", &tm.tm_year, &tm.tm_mon, + &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec) == 6) { + /* OK */ + } else if (sscanf(startdate, "%d-%d-%d", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday) == 3) { + tm.tm_hour = 0; + tm.tm_min = 0; + tm.tm_sec = 0; } else { - if (sscanf(startdate, "%d-%d-%dT%d:%d:%d", - &tm.tm_year, - &tm.tm_mon, - &tm.tm_mday, - &tm.tm_hour, - &tm.tm_min, - &tm.tm_sec) == 6) { - /* OK */ - } else if (sscanf(startdate, "%d-%d-%d", - &tm.tm_year, - &tm.tm_mon, - &tm.tm_mday) == 3) { - tm.tm_hour = 0; - tm.tm_min = 0; - tm.tm_sec = 0; - } else { - goto date_fail; - } - tm.tm_year -= 1900; - tm.tm_mon--; - rtc_start_date = mktimegm(&tm); - if (rtc_start_date == -1) { - date_fail: - 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; + goto date_fail; } + tm.tm_year -= 1900; + tm.tm_mon--; + rtc_start_datetime = mktimegm(&tm); + if (rtc_start_datetime == -1) { + date_fail: + error_report("invalid datetime format"); + error_printf("valid formats: " + "'2006-06-17T16:01:21' or '2006-06-17'\n"); + exit(1); + } + rtc_host_datetime_offset = rtc_ref_start_datetime - rtc_start_datetime; + rtc_ref_start_datetime = rtc_start_datetime; } static void configure_rtc(QemuOpts *opts) { const char *value; + /* Set defaults */ + rtc_clock = QEMU_CLOCK_HOST; + rtc_ref_start_datetime = qemu_clock_get_ms(QEMU_CLOCK_HOST) / 1000; + rtc_realtime_clock_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) / 1000; + value = qemu_opt_get(opts, "base"); if (value) { if (!strcmp(value, "utc")) { - rtc_utc = 1; + rtc_base_type = RTC_BASE_UTC; } else if (!strcmp(value, "localtime")) { Error *blocker = NULL; - rtc_utc = 0; + rtc_base_type = RTC_BASE_LOCALTIME; error_setg(&blocker, QERR_REPLAY_NOT_SUPPORTED, "-rtc base=localtime"); replay_add_blocker(blocker); } else { - configure_rtc_date_offset(value, 0); + rtc_base_type = RTC_BASE_DATETIME; + configure_rtc_base_datetime(value); } } value = qemu_opt_get(opts, "clock"); @@ -1070,12 +1090,12 @@ static int parse_add_fd(void *opaque, QemuOpts *opts, Error **errp) fd_opaque = qemu_opt_get(opts, "opaque"); if (fd < 0) { - error_report("fd option is required and must be non-negative"); + error_setg(errp, "fd option is required and must be non-negative"); return -1; } if (fd <= STDERR_FILENO) { - error_report("fd cannot be a standard I/O stream"); + error_setg(errp, "fd cannot be a standard I/O stream"); return -1; } @@ -1085,12 +1105,12 @@ static int parse_add_fd(void *opaque, QemuOpts *opts, Error **errp) */ flags = fcntl(fd, F_GETFD); if (flags == -1 || (flags & FD_CLOEXEC)) { - error_report("fd is not valid or already in use"); + error_setg(errp, "fd is not valid or already in use"); return -1; } if (fdset_id < 0) { - error_report("set option is required and must be non-negative"); + error_setg(errp, "set option is required and must be non-negative"); return -1; } @@ -1103,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_setg(errp, "error duplicating fd: %s", strerror(errno)); return -1; } @@ -1140,7 +1160,7 @@ static int drive_init_func(void *opaque, QemuOpts *opts, Error **errp) { BlockInterfaceType *block_default_type = opaque; - return drive_new(opts, *block_default_type) == NULL; + return drive_new(opts, *block_default_type, errp) == NULL; } static int drive_enable_snapshot(void *opaque, QemuOpts *opts, Error **errp) @@ -1166,14 +1186,60 @@ static void default_drive(int enable, int snapshot, BlockInterfaceType type, drive_enable_snapshot(NULL, opts, NULL); } - dinfo = drive_new(opts, type); - if (!dinfo) { - exit(1); - } + dinfo = drive_new(opts, type, &error_abort); dinfo->is_default = true; } +typedef struct BlockdevOptionsQueueEntry { + BlockdevOptions *bdo; + Location loc; + QSIMPLEQ_ENTRY(BlockdevOptionsQueueEntry) entry; +} BlockdevOptionsQueueEntry; + +typedef QSIMPLEQ_HEAD(, BlockdevOptionsQueueEntry) BlockdevOptionsQueue; + +static void configure_blockdev(BlockdevOptionsQueue *bdo_queue, + MachineClass *machine_class, int snapshot) +{ + /* + * If the currently selected machine wishes to override the + * units-per-bus property of its default HBA interface type, do so + * now. + */ + if (machine_class->units_per_default_bus) { + override_max_devs(machine_class->block_default_type, + machine_class->units_per_default_bus); + } + + /* open the virtual block devices */ + while (!QSIMPLEQ_EMPTY(bdo_queue)) { + BlockdevOptionsQueueEntry *bdo = QSIMPLEQ_FIRST(bdo_queue); + + QSIMPLEQ_REMOVE_HEAD(bdo_queue, entry); + loc_push_restore(&bdo->loc); + qmp_blockdev_add(bdo->bdo, &error_fatal); + loc_pop(&bdo->loc); + qapi_free_BlockdevOptions(bdo->bdo); + g_free(bdo); + } + 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, &error_fatal)) { + /* We printed help */ + exit(0); + } + + default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2, + CDROM_OPTS); + default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); + default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); + +} + static QemuOptsList qemu_smp_opts = { .name = "smp-opts", .implied_opt_name = "cpus", @@ -1210,11 +1276,14 @@ static void smp_parse(QemuOpts *opts) /* compute missing values, prefer sockets over cores over threads */ if (cpus == 0 || sockets == 0) { - sockets = sockets > 0 ? sockets : 1; cores = cores > 0 ? cores : 1; threads = threads > 0 ? threads : 1; if (cpus == 0) { + sockets = sockets > 0 ? sockets : 1; cpus = cores * threads * sockets; + } else { + max_cpus = qemu_opt_get_number(opts, "maxcpus", cpus); + sockets = max_cpus / (cores * threads); } } else if (cores == 0) { threads = threads > 0 ? threads : 1; @@ -1246,6 +1315,13 @@ static void smp_parse(QemuOpts *opts) exit(1); } + if (sockets * cores * threads != max_cpus) { + warn_report("Invalid CPU topology deprecated: " + "sockets (%u) * cores (%u) * threads (%u) " + "!= maxcpus (%u)", + sockets, cores, threads, max_cpus); + } + smp_cpus = cpus; smp_cores = cores; smp_threads = threads; @@ -1492,16 +1568,13 @@ static int machine_help_func(QemuOpts *opts, MachineState *machine) return 1; } -/***********************************************************/ -/* main execution loop */ - struct vm_change_state_entry { VMChangeStateHandler *cb; void *opaque; QLIST_ENTRY (vm_change_state_entry) entries; }; -static QLIST_HEAD(vm_change_state_head, vm_change_state_entry) vm_change_state_head; +static QLIST_HEAD(, vm_change_state_entry) vm_change_state_head; VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque) @@ -1526,7 +1599,7 @@ void vm_state_notify(int running, RunState state) { VMChangeStateEntry *e, *next; - trace_vm_state_notify(running, state); + trace_vm_state_notify(running, state, RunState_str(state)); QLIST_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { e->cb(e->opaque, running, state); @@ -1548,6 +1621,8 @@ static NotifierList suspend_notifiers = NOTIFIER_LIST_INITIALIZER(suspend_notifiers); static NotifierList wakeup_notifiers = NOTIFIER_LIST_INITIALIZER(wakeup_notifiers); +static NotifierList shutdown_notifiers = + NOTIFIER_LIST_INITIALIZER(shutdown_notifiers); static uint32_t wakeup_reason_mask = ~(1 << QEMU_WAKEUP_REASON_NONE); ShutdownCause qemu_shutdown_requested_get(void) @@ -1647,8 +1722,7 @@ void qemu_system_reset(ShutdownCause reason) qemu_devices_reset(); } if (reason != SHUTDOWN_CAUSE_SUBSYSTEM_RESET) { - qapi_event_send_reset(shutdown_caused_by_guest(reason), - &error_abort); + qapi_event_send_reset(shutdown_caused_by_guest(reason), reason); } cpu_synchronize_all_post_reset(); } @@ -1661,11 +1735,11 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) current_cpu->crash_occurred = true; } qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_PAUSE, - !!info, info, &error_abort); + !!info, info); vm_stop(RUN_STATE_GUEST_PANICKED); if (!no_shutdown) { qapi_event_send_guest_panicked(GUEST_PANIC_ACTION_POWEROFF, - !!info, info, &error_abort); + !!info, info); qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_PANIC); } @@ -1706,7 +1780,7 @@ static void qemu_system_suspend(void) pause_all_vcpus(); notifier_list_notify(&suspend_notifiers, NULL); runstate_set(RUN_STATE_SUSPENDED); - qapi_event_send_suspend(&error_abort); + qapi_event_send_suspend(); } void qemu_system_suspend_request(void) @@ -1724,11 +1798,13 @@ void qemu_register_suspend_notifier(Notifier *notifier) notifier_list_add(&suspend_notifiers, notifier); } -void qemu_system_wakeup_request(WakeupReason reason) +void qemu_system_wakeup_request(WakeupReason reason, Error **errp) { trace_system_wakeup_request(reason); if (!runstate_check(RUN_STATE_SUSPENDED)) { + error_setg(errp, + "Unable to wake up: guest is not in suspended state"); return; } if (!(wakeup_reason_mask & (1 << reason))) { @@ -1753,6 +1829,24 @@ void qemu_register_wakeup_notifier(Notifier *notifier) notifier_list_add(&wakeup_notifiers, notifier); } +void qemu_register_wakeup_support(void) +{ + wakeup_suspend_enabled = true; +} + +bool qemu_wakeup_suspend_enabled(void) +{ + return wakeup_suspend_enabled; +} + +CurrentMachineParams *qmp_query_current_machine(Error **errp) +{ + CurrentMachineParams *params = g_malloc0(sizeof(*params)); + params->wakeup_suspend_support = qemu_wakeup_suspend_enabled(); + + return params; +} + void qemu_system_killed(int signal, pid_t pid) { shutdown_signal = signal; @@ -1776,10 +1870,16 @@ void qemu_system_shutdown_request(ShutdownCause reason) static void qemu_system_powerdown(void) { - qapi_event_send_powerdown(&error_abort); + qapi_event_send_powerdown(); notifier_list_notify(&powerdown_notifiers, NULL); } +static void qemu_system_shutdown(ShutdownCause cause) +{ + qapi_event_send_shutdown(shutdown_caused_by_guest(cause), cause); + notifier_list_notify(&shutdown_notifiers, &cause); +} + void qemu_system_powerdown_request(void) { trace_qemu_system_powerdown_request(); @@ -1792,6 +1892,11 @@ void qemu_register_powerdown_notifier(Notifier *notifier) notifier_list_add(&powerdown_notifiers, notifier); } +void qemu_register_shutdown_notifier(Notifier *notifier) +{ + notifier_list_add(&shutdown_notifiers, notifier); +} + void qemu_system_debug_request(void) { debug_requested = 1; @@ -1819,8 +1924,7 @@ static bool main_loop_should_exit(void) request = qemu_shutdown_requested(); if (request) { qemu_kill_report(); - qapi_event_send_shutdown(shutdown_caused_by_guest(request), - &error_abort); + qemu_system_shutdown(request); if (no_shutdown) { vm_stop(RUN_STATE_SHUTDOWN); } else { @@ -1843,7 +1947,7 @@ static bool main_loop_should_exit(void) notifier_list_notify(&wakeup_notifiers, &wakeup_reason); wakeup_reason = QEMU_WAKEUP_REASON_NONE; resume_all_vcpus(); - qapi_event_send_wakeup(&error_abort); + qapi_event_send_wakeup(); } if (qemu_powerdown_requested()) { qemu_system_powerdown(); @@ -2015,15 +2119,10 @@ static void select_vgahw(const char *p) static void parse_display_qapi(const char *optarg) { - Error *err = NULL; DisplayOptions *opts; Visitor *v; - v = qobject_input_visitor_new_str(optarg, "type", &err); - if (!v) { - error_report_err(err); - exit(1); - } + v = qobject_input_visitor_new_str(optarg, "type", &error_fatal); visit_type_DisplayOptions(v, NULL, &opts, &error_fatal); QAPI_CLONE_MEMBERS(DisplayOptions, &dpy, opts); @@ -2032,6 +2131,11 @@ static void parse_display_qapi(const char *optarg) visit_free(v); } +DisplayOptions *qmp_query_display_options(Error **errp) +{ + return QAPI_CLONE(DisplayOptions, &dpy); +} + static void parse_display(const char *p) { const char *opts; @@ -2051,18 +2155,7 @@ static void parse_display(const char *p) while (*opts) { const char *nextopt; - if (strstart(opts, ",frame=", &nextopt)) { - g_printerr("The frame= sdl option is deprecated, and will be\n" - "removed in a future release.\n"); - opts = nextopt; - if (strstart(opts, "on", &nextopt)) { - no_frame = 0; - } else if (strstart(opts, "off", &nextopt)) { - no_frame = 1; - } else { - goto invalid_sdl_args; - } - } else if (strstart(opts, ",alt_grab=", &nextopt)) { + if (strstart(opts, ",alt_grab=", &nextopt)) { opts = nextopt; if (strstart(opts, "on", &nextopt)) { alt_grab = 1; @@ -2127,36 +2220,6 @@ static void parse_display(const char *p) } } -static int balloon_parse(const char *arg) -{ - QemuOpts *opts; - - warn_report("This option is deprecated. " - "Use '--device virtio-balloon' to enable the balloon device."); - - if (strcmp(arg, "none") == 0) { - return 0; - } - - if (!strncmp(arg, "virtio", 6)) { - if (arg[6] == ',') { - /* have params -> parse them */ - opts = qemu_opts_parse_noisily(qemu_find_opts("device"), arg + 7, - false); - if (!opts) - return -1; - } else { - /* create empty opts */ - opts = qemu_opts_create(qemu_find_opts("device"), NULL, 0, - &error_abort); - } - qemu_opt_set(opts, "driver", "virtio-balloon", &error_abort); - return 0; - } - - return -1; -} - char *qemu_find_file(int type, const char *name) { int i; @@ -2222,7 +2285,7 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) FWCfgState *fw_cfg = (FWCfgState *) opaque; if (fw_cfg == NULL) { - error_report("fw_cfg device not available"); + error_setg(errp, "fw_cfg device not available"); return -1; } name = qemu_opt_get(opts, "name"); @@ -2231,15 +2294,16 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) /* 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)"); + error_setg(errp, "invalid argument(s)"); return -1; } if (nonempty_str(file) && nonempty_str(str)) { - error_report("file and string are mutually exclusive"); + error_setg(errp, "file and string are mutually exclusive"); return -1; } if (strlen(name) > FW_CFG_MAX_FILE_PATH - 1) { - error_report("name too long (max. %d char)", FW_CFG_MAX_FILE_PATH - 1); + error_setg(errp, "name too long (max. %d char)", + FW_CFG_MAX_FILE_PATH - 1); return -1; } if (strncmp(name, "opt/", 4) != 0) { @@ -2250,8 +2314,10 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) 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); + GError *err = NULL; + if (!g_file_get_contents(file, &buf, &size, &err)) { + error_setg(errp, "can't load %s: %s", file, err->message); + g_error_free(err); return -1; } } @@ -2269,12 +2335,10 @@ static int device_help_func(void *opaque, QemuOpts *opts, Error **errp) static int device_init_func(void *opaque, QemuOpts *opts, Error **errp) { - Error *err = NULL; DeviceState *dev; - dev = qdev_device_add(opts, &err); + dev = qdev_device_add(opts, errp); if (!dev) { - error_report_err(err); return -1; } object_unref(OBJECT(dev)); @@ -2285,9 +2349,9 @@ static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp) { Error *local_err = NULL; - if (!qemu_chr_new_from_opts(opts, &local_err)) { + if (!qemu_chr_new_from_opts(opts, NULL, &local_err)) { if (local_err) { - error_report_err(local_err); + error_propagate(errp, local_err); return -1; } exit(0); @@ -2298,7 +2362,7 @@ static int chardev_init_func(void *opaque, QemuOpts *opts, Error **errp) #ifdef CONFIG_VIRTFS static int fsdev_init_func(void *opaque, QemuOpts *opts, Error **errp) { - return qemu_fsdev_add(opts); + return qemu_fsdev_add(opts, errp); } #endif @@ -2318,23 +2382,22 @@ static int mon_init_func(void *opaque, QemuOpts *opts, Error **errp) } else if (strcmp(mode, "control") == 0) { flags = MONITOR_USE_CONTROL; } else { - error_report("unknown monitor mode \"%s\"", mode); - exit(1); + error_setg(errp, "unknown monitor mode \"%s\"", mode); + return -1; } if (qemu_opt_get_bool(opts, "pretty", 0)) flags |= MONITOR_USE_PRETTY; - /* OOB is off by default */ - if (qemu_opt_get_bool(opts, "x-oob", 0)) { - flags |= MONITOR_USE_OOB; - } - chardev = qemu_opt_get(opts, "chardev"); + if (!chardev) { + error_report("chardev is required"); + exit(1); + } chr = qemu_chr_find(chardev); if (chr == NULL) { - error_report("chardev \"%s\" not found", chardev); - exit(1); + error_setg(errp, "chardev \"%s\" not found", chardev); + return -1; } monitor_init(chr, flags); @@ -2353,7 +2416,7 @@ static void monitor_parse(const char *optarg, const char *mode, bool pretty) } else { snprintf(label, sizeof(label), "compat_monitor%d", monitor_device_index); - opts = qemu_chr_parse_compat(label, optarg); + opts = qemu_chr_parse_compat(label, optarg, true); if (!opts) { error_report("parse error: %s", optarg); exit(1); @@ -2373,7 +2436,6 @@ struct device_config { DEV_BT, /* -bt */ DEV_SERIAL, /* -serial */ DEV_PARALLEL, /* -parallel */ - DEV_VIRTCON, /* -virtioconsole */ DEV_DEBUGCON, /* -debugcon */ DEV_GDB, /* -gdb, -s */ DEV_SCLP, /* s390 sclp */ @@ -2425,7 +2487,7 @@ static int serial_parse(const char *devname) snprintf(label, sizeof(label), "serial%d", index); serial_hds = g_renew(Chardev *, serial_hds, index + 1); - serial_hds[index] = qemu_chr_new(label, devname); + serial_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!serial_hds[index]) { error_report("could not connect serial device" " to character backend '%s'", devname); @@ -2461,7 +2523,7 @@ static int parallel_parse(const char *devname) exit(1); } snprintf(label, sizeof(label), "parallel%d", index); - parallel_hds[index] = qemu_chr_new(label, devname); + parallel_hds[index] = qemu_chr_new_mux_mon(label, devname, NULL); if (!parallel_hds[index]) { error_report("could not connect parallel device" " to character backend '%s'", devname); @@ -2471,44 +2533,12 @@ static int parallel_parse(const char *devname) return 0; } -static int virtcon_parse(const char *devname) -{ - QemuOptsList *device = qemu_find_opts("device"); - static int index = 0; - char label[32]; - QemuOpts *bus_opts, *dev_opts; - - if (strcmp(devname, "none") == 0) - return 0; - if (index == MAX_VIRTIO_CONSOLES) { - error_report("too many virtio consoles"); - exit(1); - } - - bus_opts = qemu_opts_create(device, NULL, 0, &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); - - snprintf(label, sizeof(label), "virtcon%d", index); - virtcon_hds[index] = qemu_chr_new(label, devname); - if (!virtcon_hds[index]) { - error_report("could not connect virtio console" - " to character backend '%s'", devname); - return -1; - } - qemu_opt_set(dev_opts, "chardev", label, &error_abort); - - index++; - return 0; -} - static int debugcon_parse(const char *devname) { QemuOpts *opts; - if (!qemu_chr_new("debugcon", devname)) { + if (!qemu_chr_new_mux_mon("debugcon", devname, NULL)) { + error_report("invalid character backend '%s'", devname); exit(1); } opts = qemu_opts_create(qemu_find_opts("device"), "debugcon", 1, NULL); @@ -2603,6 +2633,16 @@ static void qemu_run_exit_notifiers(void) notifier_list_notify(&exit_notifiers, NULL); } +static const char *pid_file; +static Notifier qemu_unlink_pidfile_notifier; + +static void qemu_unlink_pidfile(Notifier *n, void *data) +{ + if (pid_file) { + unlink(pid_file); + } +} + bool machine_init_done; void qemu_add_machine_init_done_notifier(Notifier *notify) @@ -2714,7 +2754,7 @@ static int machine_set_property(void *opaque, g_free(qom_name); if (local_err) { - error_report_err(local_err); + error_propagate(errp, local_err); return -1; } @@ -2729,8 +2769,62 @@ static int machine_set_property(void *opaque, * cannot be created here, as it depends on the chardev * already existing. */ -static bool object_create_initial(const char *type) +static bool object_create_initial(const char *type, QemuOpts *opts) { + ObjectClass *klass; + + if (is_help_option(type)) { + GSList *l, *list; + + printf("List of user creatable objects:\n"); + list = object_class_get_list_sorted(TYPE_USER_CREATABLE, false); + for (l = list; l != NULL; l = l->next) { + ObjectClass *oc = OBJECT_CLASS(l->data); + printf(" %s\n", object_class_get_name(oc)); + } + g_slist_free(list); + exit(0); + } + + klass = object_class_by_name(type); + if (klass && qemu_opt_has_help_opt(opts)) { + ObjectPropertyIterator iter; + ObjectProperty *prop; + GPtrArray *array = g_ptr_array_new(); + int i; + + object_class_property_iter_init(&iter, klass); + while ((prop = object_property_iter_next(&iter))) { + GString *str; + + if (!prop->set) { + continue; + } + + str = g_string_new(NULL); + g_string_append_printf(str, " %s=<%s>", prop->name, prop->type); + if (prop->description) { + if (str->len < 24) { + g_string_append_printf(str, "%*s", 24 - (int)str->len, ""); + } + g_string_append_printf(str, " - %s", prop->description); + } + g_ptr_array_add(array, g_string_free(str, false)); + } + g_ptr_array_sort(array, (GCompareFunc)qemu_pstrcmp0); + if (array->len > 0) { + printf("%s options:\n", type); + } else { + printf("There are no options for %s.\n", type); + } + for (i = 0; i < array->len; i++) { + printf("%s\n", (char *)array->pdata[i]); + } + g_ptr_array_set_free_func(array, g_free); + g_ptr_array_free(array, true); + exit(0); + } + if (g_str_equal(type, "rng-egd") || g_str_has_prefix(type, "pr-manager-")) { return false; @@ -2777,9 +2871,9 @@ static bool object_create_initial(const char *type) * The remainder of object creation happens after the * creation of chardev, fsdev, net clients and device data types. */ -static bool object_create_delayed(const char *type) +static bool object_create_delayed(const char *type, QemuOpts *opts) { - return !object_create_initial(type); + return !object_create_initial(type, opts); } @@ -2870,8 +2964,6 @@ static int global_init_func(void *opaque, QemuOpts *opts, Error **errp) g->driver = qemu_opt_get(opts, "driver"); g->property = qemu_opt_get(opts, "property"); g->value = qemu_opt_get(opts, "value"); - g->user_provided = true; - g->errp = &error_fatal; qdev_prop_register_global(g); return 0; } @@ -2894,19 +2986,6 @@ static void user_register_global_props(void) global_init_func, NULL, NULL); } -/* - * Note: we should see that these properties are actually having a - * priority: accel < machine < user. This means e.g. when user - * specifies something in "-global", it'll always be used with highest - * priority than either machine/accelerator compat properties. - */ -static void register_global_properties(MachineState *ms) -{ - accel_register_compat_props(ms->accelerator); - machine_register_compat_props(ms); - user_register_global_props(); -} - int main(int argc, char **argv, char **envp) { int i; @@ -2927,7 +3006,6 @@ int main(int argc, char **argv, char **envp) const char *vga_model = NULL; const char *qtest_chrdev = NULL; const char *qtest_log = NULL; - const char *pid_file = NULL; const char *incoming = NULL; bool userconfig = true; bool nographic = false; @@ -2942,13 +3020,7 @@ int main(int argc, char **argv, char **envp) Error *err = NULL; bool list_data_dirs = false; char *dir, **dirs; - typedef struct BlockdevOptions_queue { - BlockdevOptions *bdo; - Location loc; - QSIMPLEQ_ENTRY(BlockdevOptions_queue) entry; - } BlockdevOptions_queue; - QSIMPLEQ_HEAD(, BlockdevOptions_queue) bdo_queue - = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); + BlockdevOptionsQueue bdo_queue = QSIMPLEQ_HEAD_INITIALIZER(bdo_queue); module_call_init(MODULE_INIT_TRACE); @@ -2987,6 +3059,7 @@ int main(int argc, char **argv, char **envp) qemu_add_opts(&qemu_object_opts); qemu_add_opts(&qemu_tpmdev_opts); qemu_add_opts(&qemu_realtime_opts); + qemu_add_opts(&qemu_overcommit_opts); qemu_add_opts(&qemu_msg_opts); qemu_add_opts(&qemu_name_opts); qemu_add_opts(&qemu_numa_opts); @@ -2996,6 +3069,7 @@ int main(int argc, char **argv, char **envp) module_call_init(MODULE_INIT_OPTS); runstate_init(); + precopy_infrastructure_init(); postcopy_infrastructure_init(); monitor_init_globals(); @@ -3003,7 +3077,6 @@ int main(int argc, char **argv, char **envp) error_reportf_err(err, "cannot initialize crypto: "); exit(1); } - rtc_clock = QEMU_CLOCK_HOST; QLIST_INIT (&vm_change_state_head); os_setup_early_signal_handling(); @@ -3028,7 +3101,6 @@ int main(int argc, char **argv, char **envp) popt = lookup_opt(argc, argv, &optarg, &optind); switch (popt->index) { - case QEMU_OPTION_nodefconfig: case QEMU_OPTION_nouserconfig: userconfig = false; break; @@ -3048,6 +3120,7 @@ int main(int argc, char **argv, char **envp) if (optind >= argc) break; if (argv[optind][0] != '-') { + loc_set_cmdline(argv, optind, 1); drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); } else { const QEMUOption *popt; @@ -3072,15 +3145,12 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_blockdev: { Visitor *v; - BlockdevOptions_queue *bdo; + BlockdevOptionsQueueEntry *bdo; - v = qobject_input_visitor_new_str(optarg, "driver", &err); - if (!v) { - error_report_err(err); - exit(1); - } + v = qobject_input_visitor_new_str(optarg, "driver", + &error_fatal); - bdo = g_new(BlockdevOptions_queue, 1); + bdo = g_new(BlockdevOptionsQueueEntry, 1); visit_type_BlockdevOptions(v, NULL, &bdo->bdo, &error_fatal); visit_free(v); @@ -3133,7 +3203,7 @@ int main(int argc, char **argv, char **envp) #ifdef CONFIG_CURSES dpy.type = DISPLAY_TYPE_CURSES; #else - error_report("curses support is disabled"); + error_report("curses or iconv support is disabled"); exit(1); #endif break; @@ -3208,32 +3278,21 @@ int main(int argc, char **argv, char **envp) exit(1); } break; -#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; #endif case QEMU_OPTION_bt: + warn_report("The bluetooth subsystem is deprecated and will " + "be removed soon. If the bluetooth subsystem is " + "still useful for you, please send a mail to " + "qemu-devel@nongnu.org with your usecase."); add_device_config(DEV_BT, optarg); break; case QEMU_OPTION_audio_help: - AUD_help (); + audio_legacy_help(); exit (0); break; + case QEMU_OPTION_audiodev: + audio_parse_option(optarg); + break; case QEMU_OPTION_soundhw: select_soundhw (optarg); break; @@ -3299,11 +3358,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_k: keyboard_layout = optarg; break; - case QEMU_OPTION_localtime: - rtc_utc = 0; - warn_report("This option is deprecated, " - "use '-rtc base=localtime' instead."); - break; case QEMU_OPTION_vga: vga_model = optarg; default_vga = 0; @@ -3501,15 +3555,6 @@ int main(int argc, char **argv, char **envp) exit(1); } break; - case QEMU_OPTION_virtiocon: - warn_report("This option is deprecated, " - "use '-device virtconsole' instead"); - add_device_config(DEV_VIRTCON, optarg); - default_virtcon = 0; - if (strncmp(optarg, "mon:", 4) == 0) { - default_monitor = 0; - } - break; case QEMU_OPTION_parallel: add_device_config(DEV_PARALLEL, optarg); default_parallel = 0; @@ -3527,11 +3572,6 @@ int main(int argc, char **argv, char **envp) dpy.has_full_screen = true; dpy.full_screen = true; break; - case QEMU_OPTION_no_frame: - g_printerr("The -no-frame switch is deprecated, and will be\n" - "removed in a future release.\n"); - no_frame = 1; - break; case QEMU_OPTION_alt_grab: alt_grab = 1; break; @@ -3556,18 +3596,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_win2k_hack: win2k_install_hack = 1; break; - case QEMU_OPTION_rtc_td_hack: { - static GlobalProperty slew_lost_ticks = { - .driver = "mc146818rtc", - .property = "lost_tick_policy", - .value = "slew", - }; - - qdev_prop_register_global(&slew_lost_ticks); - warn_report("This option is deprecated, " - "use '-rtc driftfix=slew' instead."); - break; - } case QEMU_OPTION_acpitable: opts = qemu_opts_parse_noisily(qemu_find_opts("acpi"), optarg, true); @@ -3598,11 +3626,6 @@ int main(int argc, char **argv, char **envp) olist = qemu_find_opts("machine"); qemu_opts_parse_noisily(olist, "accel=kvm", false); break; - case QEMU_OPTION_enable_hax: - warn_report("Option is deprecated, use '-accel hax' instead"); - olist = qemu_find_opts("machine"); - qemu_opts_parse_noisily(olist, "accel=hax", false); - break; case QEMU_OPTION_M: case QEMU_OPTION_machine: olist = qemu_find_opts("machine"); @@ -3659,12 +3682,6 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_no_hpet: no_hpet = 1; break; - case QEMU_OPTION_balloon: - if (balloon_parse(optarg) < 0) { - error_report("unknown -balloon argument %s", optarg); - exit(1); - } - break; case QEMU_OPTION_no_reboot: no_reboot = 1; break; @@ -3753,23 +3770,12 @@ int main(int argc, char **argv, char **envp) case QEMU_OPTION_old_param: old_param = 1; break; - case QEMU_OPTION_clock: - /* Clock options no longer exist. Keep this option for - * backward compatibility. - */ - warn_report("This option is ignored and will be removed soon"); - break; - case QEMU_OPTION_startdate: - warn_report("This option is deprecated, use '-rtc base=' instead."); - configure_rtc_date_offset(optarg, 1); - break; case QEMU_OPTION_rtc: opts = qemu_opts_parse_noisily(qemu_find_opts("rtc"), optarg, false); if (!opts) { exit(1); } - configure_rtc(opts); break; case QEMU_OPTION_tb_size: #ifndef CONFIG_TCG @@ -3795,13 +3801,7 @@ int main(int argc, char **argv, char **envp) incoming = optarg; break; case QEMU_OPTION_only_migratable: - /* - * TODO: we can remove this option one day, and we - * should all use: - * - * "-global migration.only-migratable=true" - */ - qemu_global_option("migration.only-migratable=true"); + only_migratable = 1; break; case QEMU_OPTION_nodefaults: has_defaults = 0; @@ -3813,13 +3813,6 @@ int main(int argc, char **argv, char **envp) } xen_domid = atoi(optarg); break; - case QEMU_OPTION_xen_create: - if (!(xen_available())) { - error_report("Option not supported for this target"); - exit(1); - } - xen_mode = XEN_CREATE; - break; case QEMU_OPTION_xen_attach: if (!(xen_available())) { error_report("Option not supported for this target"); @@ -3959,7 +3952,9 @@ int main(int argc, char **argv, char **envp) exit(1); } break; - case QEMU_OPTION_nodefconfig: + case QEMU_OPTION_enable_sync_profile: + qsp_enable(); + break; case QEMU_OPTION_nouserconfig: /* Nothing to be parsed here. Especially, do not error out below. */ break; @@ -3977,6 +3972,8 @@ int main(int argc, char **argv, char **envp) */ loc_set_none(); + user_register_global_props(); + replay_configure(icount_opts); if (incoming && !preconfig_exit_requested) { @@ -3985,45 +3982,45 @@ int main(int argc, char **argv, char **envp) exit(EXIT_FAILURE); } + configure_rtc(qemu_find_opts_singleton("rtc")); + machine_class = select_machine(); + object_set_machine_compat_props(machine_class->compat_props); set_memory_options(&ram_slots, &maxram_size, machine_class); os_daemonize(); rcu_disable_atfork(); - if (pid_file && qemu_create_pidfile(pid_file) != 0) { - error_report("could not acquire pid file: %s", strerror(errno)); + if (pid_file && !qemu_write_pidfile(pid_file, &err)) { + error_reportf_err(err, "cannot create PID file: "); exit(1); } + qemu_unlink_pidfile_notifier.notify = qemu_unlink_pidfile; + qemu_add_exit_notifier(&qemu_unlink_pidfile_notifier); + if (qemu_init_main_loop(&main_loop_err)) { error_report_err(main_loop_err); exit(1); } #ifdef CONFIG_SECCOMP - if (qemu_opts_foreach(qemu_find_opts("sandbox"), - parse_sandbox, NULL, NULL)) { - exit(1); + olist = qemu_find_opts_err("sandbox", NULL); + if (olist) { + qemu_opts_foreach(olist, parse_sandbox, NULL, &error_fatal); } #endif - if (qemu_opts_foreach(qemu_find_opts("name"), - parse_name, NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("name"), + parse_name, NULL, &error_fatal); #ifndef _WIN32 - if (qemu_opts_foreach(qemu_find_opts("add-fd"), - parse_add_fd, NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("add-fd"), + parse_add_fd, NULL, &error_fatal); - if (qemu_opts_foreach(qemu_find_opts("add-fd"), - cleanup_add_fd, NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("add-fd"), + cleanup_add_fd, NULL, &error_fatal); #endif current_machine = MACHINE(object_new(object_class_get_name( @@ -4033,6 +4030,10 @@ int main(int argc, char **argv, char **envp) } object_property_add_child(object_get_root(), "machine", OBJECT(current_machine), &error_abort); + object_property_add_child(container_get(OBJECT(current_machine), + "/unattached"), + "sysbus", OBJECT(sysbus_get_default()), + NULL); if (machine_class->minimum_page_bits) { if (!set_preferred_target_page_bits(machine_class->minimum_page_bits)) { @@ -4148,9 +4149,6 @@ int main(int argc, char **argv, char **envp) if (!has_defaults || machine_class->no_parallel) { default_parallel = 0; } - if (!has_defaults || !machine_class->use_virtcon) { - default_virtcon = 0; - } if (!has_defaults || machine_class->no_floppy) { default_floppy = 0; } @@ -4183,8 +4181,7 @@ int main(int argc, char **argv, char **envp) * usage, -nographic is just a no-op in this case. */ if (nographic - && (default_parallel || default_serial - || default_monitor || default_virtcon)) { + && (default_parallel || default_serial || default_monitor)) { error_report("-nographic cannot be used with -daemonize"); exit(1); } @@ -4201,13 +4198,9 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_PARALLEL, "null"); if (default_serial && default_monitor) { add_device_config(DEV_SERIAL, "mon:stdio"); - } else if (default_virtcon && default_monitor) { - add_device_config(DEV_VIRTCON, "mon:stdio"); } else { if (default_serial) add_device_config(DEV_SERIAL, "stdio"); - if (default_virtcon) - add_device_config(DEV_VIRTCON, "stdio"); if (default_monitor) monitor_parse("stdio", "readline", false); } @@ -4218,8 +4211,6 @@ int main(int argc, char **argv, char **envp) add_device_config(DEV_PARALLEL, "vc:80Cx24C"); if (default_monitor) monitor_parse("vc:80Cx24C", "readline", false); - if (default_virtcon) - add_device_config(DEV_VIRTCON, "vc:80Cx24C"); } #if defined(CONFIG_VNC) @@ -4239,8 +4230,8 @@ int main(int argc, char **argv, char **envp) dpy.type = DISPLAY_TYPE_NONE; } - if ((no_frame || alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) { - error_report("-no-frame, -alt-grab and -ctrl-grab are only valid " + if ((alt_grab || ctrl_grab) && dpy.type != DISPLAY_TYPE_SDL) { + error_report("-alt-grab and -ctrl-grab are only valid " "for SDL, ignoring option"); } if (dpy.has_window_close && @@ -4264,22 +4255,16 @@ int main(int argc, char **argv, char **envp) page_size_init(); socket_init(); - if (qemu_opts_foreach(qemu_find_opts("object"), - user_creatable_add_opts_foreach, - object_create_initial, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("object"), + user_creatable_add_opts_foreach, + object_create_initial, &error_fatal); - if (qemu_opts_foreach(qemu_find_opts("chardev"), - chardev_init_func, NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("chardev"), + chardev_init_func, NULL, &error_fatal); #ifdef CONFIG_VIRTFS - if (qemu_opts_foreach(qemu_find_opts("fsdev"), - fsdev_init_func, NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("fsdev"), + fsdev_init_func, NULL, &error_fatal); #endif if (qemu_opts_foreach(qemu_find_opts("device"), @@ -4287,14 +4272,41 @@ int main(int argc, char **argv, char **envp) exit(0); } - machine_opts = qemu_get_machine_opts(); - if (qemu_opt_foreach(machine_opts, machine_set_property, current_machine, - NULL)) { - object_unref(OBJECT(current_machine)); - exit(1); - } + /* + * Note: we need to create block backends before + * machine_set_property(), so machine properties can refer to + * them. + */ + configure_blockdev(&bdo_queue, machine_class, snapshot); - configure_accelerator(current_machine); + machine_opts = qemu_get_machine_opts(); + qemu_opt_foreach(machine_opts, machine_set_property, current_machine, + &error_fatal); + current_machine->ram_size = ram_size; + current_machine->maxram_size = maxram_size; + current_machine->ram_slots = ram_slots; + + /* + * Note: uses machine properties such as kernel-irqchip, must run + * after machine_set_property(). + */ + configure_accelerator(current_machine, argv[0]); + + /* + * Beware, QOM objects created before this point miss global and + * compat properties. + * + * Global properties get set up by qdev_prop_register_global(), + * called from user_register_global_props(), and certain option + * desugaring. Also in CPU feature desugaring (buried in + * parse_cpu_model()), which happens below this point, but may + * only target the CPU type, which can only be created after + * parse_cpu_model() returned the type. + * + * Machine compat properties: object_set_machine_compat_props(). + * Accelerator compat props: object_set_accelerator_compat_props(), + * called from configure_accelerator(). + */ if (!qtest_enabled() && machine_class->deprecation_reason) { error_report("Machine type '%s' is deprecated: %s", @@ -4302,14 +4314,8 @@ int main(int argc, char **argv, char **envp) } /* - * Register all the global properties, including accel properties, - * machine properties, and user-specified ones. - */ - register_global_properties(current_machine); - - /* - * Migration object can only be created after global properties - * are applied correctly. + * Note: creates a QOM object, must run only after global and + * compat properties have been set up. */ migration_object_init(); @@ -4392,22 +4398,16 @@ int main(int argc, char **argv, char **envp) #endif } - colo_info_init(); - if (net_init_clients(&err) < 0) { error_report_err(err); exit(1); } - if (qemu_opts_foreach(qemu_find_opts("object"), - user_creatable_add_opts_foreach, - object_create_delayed, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("object"), + user_creatable_add_opts_foreach, + object_create_delayed, &error_fatal); - if (tpm_init() < 0) { - exit(1); - } + tpm_init(); /* init the bluetooth world */ if (foreach_device_config(DEV_BT, bt_parse)) @@ -4425,49 +4425,13 @@ int main(int argc, char **argv, char **envp) ram_mig_init(); dirty_bitmap_mig_init(); - /* If the currently selected machine wishes to override the units-per-bus - * property of its default HBA interface type, do so now. */ - if (machine_class->units_per_default_bus) { - override_max_devs(machine_class->block_default_type, - machine_class->units_per_default_bus); - } - - /* open the virtual block devices */ - while (!QSIMPLEQ_EMPTY(&bdo_queue)) { - BlockdevOptions_queue *bdo = QSIMPLEQ_FIRST(&bdo_queue); - - QSIMPLEQ_REMOVE_HEAD(&bdo_queue, entry); - loc_push_restore(&bdo->loc); - qmp_blockdev_add(bdo->bdo, &error_fatal); - loc_pop(&bdo->loc); - qapi_free_BlockdevOptions(bdo->bdo); - g_free(bdo); - } - 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); - } - - default_drive(default_cdrom, snapshot, machine_class->block_default_type, 2, - CDROM_OPTS); - default_drive(default_floppy, snapshot, IF_FLOPPY, 0, FD_OPTS); - default_drive(default_sdcard, snapshot, IF_SD, 0, SD_OPTS); - - if (qemu_opts_foreach(qemu_find_opts("mon"), - mon_init_func, NULL, NULL)) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("mon"), + mon_init_func, NULL, &error_fatal); if (foreach_device_config(DEV_SERIAL, serial_parse) < 0) exit(1); if (foreach_device_config(DEV_PARALLEL, parallel_parse) < 0) exit(1); - if (foreach_device_config(DEV_VIRTCON, virtcon_parse) < 0) - exit(1); if (foreach_device_config(DEV_DEBUGCON, debugcon_parse) < 0) exit(1); @@ -4497,9 +4461,6 @@ int main(int argc, char **argv, char **envp) replay_checkpoint(CHECKPOINT_INIT); qdev_machine_init(); - current_machine->ram_size = ram_size; - current_machine->maxram_size = maxram_size; - current_machine->ram_slots = ram_slots; current_machine->boot_order = boot_order; /* parse features once if machine provides default cpu_type */ @@ -4512,6 +4473,8 @@ int main(int argc, char **argv, char **envp) /* do monitor/qmp handling at preconfig state if requested */ main_loop(); + audio_init_audiodevs(); + /* from here on runstate is RUN_STATE_PRELAUNCH */ machine_run_board_init(current_machine); @@ -4523,10 +4486,8 @@ int main(int argc, char **argv, char **envp) hax_sync_vcpus(); } - if (qemu_opts_foreach(qemu_find_opts("fw_cfg"), - parse_fw_cfg, fw_cfg_find(), NULL) != 0) { - exit(1); - } + qemu_opts_foreach(qemu_find_opts("fw_cfg"), + parse_fw_cfg, fw_cfg_find(), &error_fatal); /* init USB devices */ if (machine_usb(current_machine)) { @@ -4539,10 +4500,8 @@ int main(int argc, char **argv, char **envp) /* 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); - } + qemu_opts_foreach(qemu_find_opts("device"), + device_init_func, NULL, &error_fatal); cpu_synchronize_all_post_init(); @@ -4559,11 +4518,10 @@ int main(int argc, char **argv, char **envp) * (2) CONFIG_SLIRP not set, in which case the implicit "-net nic" * sets up a nic that isn't connected to anything. */ - if (!default_net) { + if (!default_net && (!qtest_enabled() || has_defaults)) { net_check_clients(); } - if (boot_once) { qemu_boot_set(boot_once, &error_fatal); qemu_register_reset(restore_boot_order, g_strdup(boot_order)); @@ -4579,7 +4537,7 @@ int main(int argc, char **argv, char **envp) /* init remote displays */ #ifdef CONFIG_VNC qemu_opts_foreach(qemu_find_opts("vnc"), - vnc_init_func, NULL, NULL); + vnc_init_func, NULL, &error_fatal); #endif if (using_spice) { @@ -4610,15 +4568,17 @@ int main(int argc, char **argv, char **envp) replay_checkpoint(CHECKPOINT_RESET); qemu_system_reset(SHUTDOWN_CAUSE_NONE); register_global_state(); - if (replay_mode != REPLAY_MODE_NONE) { - replay_vmstate_init(); - } else if (loadvm) { + if (loadvm) { Error *local_err = NULL; if (load_snapshot(loadvm, &local_err) < 0) { error_report_err(local_err); autostart = 0; + exit(1); } } + if (replay_mode != REPLAY_MODE_NONE) { + replay_vmstate_init(); + } qdev_prop_check_globals(); if (vmstate_dump_file) { @@ -4645,6 +4605,12 @@ int main(int argc, char **argv, char **envp) gdbserver_cleanup(); + /* + * cleaning up the migration object cancels any existing migration + * try to do this early so that it also stops using devices. + */ + migration_shutdown(); + /* No more vcpu or device emulation activity beyond this point */ vm_shutdown(); @@ -4660,7 +4626,6 @@ int main(int argc, char **argv, char **envp) monitor_cleanup(); qemu_chr_cleanup(); user_creatable_cleanup(); - migration_object_finalize(); /* TODO: unref root container, check all devices are ok */ return 0; diff --git a/win_dump.c b/win_dump.c index b15c191ad7..e10a7831c2 100644 --- a/win_dump.c +++ b/win_dump.c @@ -30,28 +30,32 @@ static size_t write_run(WinDumpPhyMemRun64 *run, int fd, Error **errp) void *buf; uint64_t addr = run->BasePage << TARGET_PAGE_BITS; uint64_t size = run->PageCount << TARGET_PAGE_BITS; - uint64_t len = size; + uint64_t len, l; + size_t total = 0; - buf = cpu_physical_memory_map(addr, &len, false); - if (!buf) { - error_setg(errp, "win-dump: failed to map run"); - return 0; - } - if (len != size) { - error_setg(errp, "win-dump: failed to map entire run"); - len = 0; - goto out_unmap; + while (size) { + len = size; + + buf = cpu_physical_memory_map(addr, &len, false); + if (!buf) { + error_setg(errp, "win-dump: failed to map physical range" + " 0x%016" PRIx64 "-0x%016" PRIx64, addr, addr + size - 1); + return 0; + } + + l = qemu_write_full(fd, buf, len); + cpu_physical_memory_unmap(buf, addr, false, len); + if (l != len) { + error_setg(errp, QERR_IO_ERROR); + return 0; + } + + addr += l; + size -= l; + total += l; } - len = qemu_write_full(fd, buf, len); - if (len != size) { - error_setg(errp, QERR_IO_ERROR); - } - -out_unmap: - cpu_physical_memory_unmap(buf, addr, false, len); - - return len; + return total; } static void write_runs(DumpState *s, WinDumpHeader64 *h, Error **errp) diff --git a/win_dump.h b/win_dump.h index f9e1faf8eb..b8c25348f4 100644 --- a/win_dump.h +++ b/win_dump.h @@ -8,169 +8,11 @@ * */ -typedef struct WinDumpPhyMemRun64 { - uint64_t BasePage; - uint64_t PageCount; -} QEMU_PACKED WinDumpPhyMemRun64; +#ifndef WIN_DUMP_H +#define WIN_DUMP_H -typedef struct WinDumpPhyMemDesc64 { - uint32_t NumberOfRuns; - uint32_t unused; - uint64_t NumberOfPages; - WinDumpPhyMemRun64 Run[43]; -} QEMU_PACKED WinDumpPhyMemDesc64; - -typedef struct WinDumpExceptionRecord { - uint32_t ExceptionCode; - uint32_t ExceptionFlags; - uint64_t ExceptionRecord; - uint64_t ExceptionAddress; - uint32_t NumberParameters; - uint32_t unused; - uint64_t ExceptionInformation[15]; -} QEMU_PACKED WinDumpExceptionRecord; - -typedef struct WinDumpHeader64 { - char Signature[4]; - char ValidDump[4]; - uint32_t MajorVersion; - uint32_t MinorVersion; - uint64_t DirectoryTableBase; - uint64_t PfnDatabase; - uint64_t PsLoadedModuleList; - uint64_t PsActiveProcessHead; - uint32_t MachineImageType; - uint32_t NumberProcessors; - union { - struct { - uint32_t BugcheckCode; - uint32_t unused0; - uint64_t BugcheckParameter1; - uint64_t BugcheckParameter2; - uint64_t BugcheckParameter3; - uint64_t BugcheckParameter4; - }; - uint8_t BugcheckData[40]; - }; - uint8_t VersionUser[32]; - uint64_t KdDebuggerDataBlock; - union { - WinDumpPhyMemDesc64 PhysicalMemoryBlock; - uint8_t PhysicalMemoryBlockBuffer[704]; - }; - union { - uint8_t ContextBuffer[3000]; - }; - WinDumpExceptionRecord Exception; - uint32_t DumpType; - uint32_t unused1; - uint64_t RequiredDumpSpace; - uint64_t SystemTime; - char Comment[128]; - uint64_t SystemUpTime; - uint32_t MiniDumpFields; - uint32_t SecondaryDataState; - uint32_t ProductType; - uint32_t SuiteMask; - uint32_t WriterStatus; - uint8_t unused2; - uint8_t KdSecondaryVersion; - uint8_t reserved[4018]; -} QEMU_PACKED WinDumpHeader64; +#include "qemu/win_dump_defs.h" void create_win_dump(DumpState *s, Error **errp); -#define KDBG_OWNER_TAG_OFFSET64 0x10 -#define KDBG_MM_PFN_DATABASE_OFFSET64 0xC0 -#define KDBG_KI_BUGCHECK_DATA_OFFSET64 0x88 -#define KDBG_KI_PROCESSOR_BLOCK_OFFSET64 0x218 -#define KDBG_OFFSET_PRCB_CONTEXT_OFFSET64 0x338 - -#define VMCOREINFO_ELF_NOTE_HDR_SIZE 24 - -#define WIN_CTX_X64 0x00100000L - -#define WIN_CTX_CTL 0x00000001L -#define WIN_CTX_INT 0x00000002L -#define WIN_CTX_SEG 0x00000004L -#define WIN_CTX_FP 0x00000008L -#define WIN_CTX_DBG 0x00000010L - -#define WIN_CTX_FULL (WIN_CTX_X64 | WIN_CTX_CTL | WIN_CTX_INT | WIN_CTX_FP) -#define WIN_CTX_ALL (WIN_CTX_FULL | WIN_CTX_SEG | WIN_CTX_DBG) - -#define LIVE_SYSTEM_DUMP 0x00000161 - -typedef struct WinM128A { - uint64_t low; - int64_t high; -} QEMU_ALIGNED(16) WinM128A; - -typedef struct WinContext { - uint64_t PHome[6]; - - uint32_t ContextFlags; - uint32_t MxCsr; - - uint16_t SegCs; - uint16_t SegDs; - uint16_t SegEs; - uint16_t SegFs; - uint16_t SegGs; - uint16_t SegSs; - uint32_t EFlags; - - uint64_t Dr0; - uint64_t Dr1; - uint64_t Dr2; - uint64_t Dr3; - uint64_t Dr6; - uint64_t Dr7; - - uint64_t Rax; - uint64_t Rcx; - uint64_t Rdx; - uint64_t Rbx; - uint64_t Rsp; - uint64_t Rbp; - uint64_t Rsi; - uint64_t Rdi; - uint64_t R8; - uint64_t R9; - uint64_t R10; - uint64_t R11; - uint64_t R12; - uint64_t R13; - uint64_t R14; - uint64_t R15; - - uint64_t Rip; - - struct { - uint16_t ControlWord; - uint16_t StatusWord; - uint8_t TagWord; - uint8_t Reserved1; - uint16_t ErrorOpcode; - uint32_t ErrorOffset; - uint16_t ErrorSelector; - uint16_t Reserved2; - uint32_t DataOffset; - uint16_t DataSelector; - uint16_t Reserved3; - uint32_t MxCsr; - uint32_t MxCsr_Mask; - WinM128A FloatRegisters[8]; - WinM128A XmmRegisters[16]; - uint8_t Reserved4[96]; - } FltSave; - - WinM128A VectorRegister[26]; - uint64_t VectorControl; - - uint64_t DebugControl; - uint64_t LastBranchToRip; - uint64_t LastBranchFromRip; - uint64_t LastExceptionToRip; - uint64_t LastExceptionFromRip; -} QEMU_ALIGNED(16) WinContext; +#endif /* WIN_DUMP_H */